Hi Andrew, I'm finally back. And thanks, my day was really nice
I hope you have enjoyed yours as well.
Please take a look at the comments inline.
I was not saying that it wasn't possible to write the proper routes, just that they don't seem very consistent.
I'm not getting why you think they're inconsistent - there's always a optional format parameter unless you tell it not to add one or you make it non-optional in the route itself.
Here is the main inconsistent issue I was referring to:
routes.rb:
get 'test/cached', format: false
get 'test/uncached', format: false
class TestController < ApplicationController
caches_action :cached
def cached
render json: 1
end
def uncached
render json: 1
end
end
curl -I http://localhost:3000/test/uncached 2>/dev/null | grep Content-Type
Content-Type: application/json; charset=utf-8
curl -I http://localhost:3000/test/cached 2>/dev/null | grep Content-Type
Content-Type: text/html; charset=utf-8
I would expect "render json: xxx" to render an 'application/json' content-type independent from the :format state. This is true for uncached actions but cached actions seems to only take into account the value of :format.
Because of that, we get a very verbose route for getting it to properly work with caches_action when we only want to support the :json format:
get 'test/cached', format: false, defaults: {format: :json}
That is the other inconsistency I was talking about. I'd prefer to just read the format option as the actual format instead of the default one like in:
get 'test/cached', format: :json # that should be the same as the previous example for brevity
but the above rule would allow '/test/cached.html' for instance. I find this an inconsistency with regards to API expected behavior, although it is consistent with the current implementation.
Also, there is another problem. With this last rule, I'd need to append something like ", as: :test_cached" to be able to "expire_action :test_cached" since "expire_action action: :cached" won't work and the full line would be too lengthy: "expire_action action: :cached, format: :json".
I mean, these rules were intended to simplify rules writing of REST APIs more than actual applications that don't need responds_to.
Also, I don't understand why we have "caches_page" and "caches_action" but "expire_page" and "expire_action". For consistency sake those should be named either caches/expires or cache/expire...
I would guess that most applications won't be API ones, so many of them won't opt for REST and responds_to.
There are plenty of non-API apps that utilise REST and responds_to
Could you please give me an actual example where you find responds_to to be useful for applications that don't interact with external applications?
For example, if I have a '/products' and '/products.json' where the former will render an HTML that will fill the page with the products requested by the latter, I'd probably use separate actions as they do completely different things:
get '/products' => 'products#index', format: false
get '/products.json' => 'products#index_json', format: false, defaults: {format: :json}
This also makes tests easier to be written and understood.
For example, all applications I've been working with for the past decade will only need to respond to a single format per action.
That means that if I wanted strict rules to my routes I'd have to write them as:
get '/anything', format: false
or
get '/anything.:format' => 'anything#index', constraints: {format: /json/}, defaults: {format: :json}
I could use a block for this common option except that not all of my actions will return a JSON.
Just use a scope - :format works here as well:
scope :format => false do
resources :products
end
That is exactly the block-based approach I was referring to. But the problem remains. I'd still need to write a lot for my JSON routes (defaults: {format: :json}).
You could even use a nested scope to wrap your actions that return JSON to add the constraints/defaults.
That would force me to write the rules in some unnatural ways. I like to group the actions not by the content-type of the response but by the involved resource instead.
...
There's a slight wrinkle here - read and write action caches always adds the :format so your cache file ends with .json but expire_action is essentially a wrapper around url_for so gives a path without the :format, making the expiry fail. I'll look at whether this needs addressing.
Thanks for taking a look into this. 
In the meantime it's probably best to use formatted urls - these routes work fine:
constraints :format => 'json' do
get 'test/index', :as => :index
get 'test/expire', :as => :expire
end
If I understand correctly, this would require me to request '/test/expire.json', right? But usually I'd write the expire action with "head :ok", returning a 'text/html' or 'text/plain' instead of 'application/json'.
You'll need to pass the format to the expire_action as it doesn't infer it from the current request - otherwise you could only expire JSON caches from JSON requests.
Or I could just pass the named routes to "expire_action", right? The named route would already contain the format information so 'expire_action' method wouldn't need to guess.
Please, don't get me wrong. I'm not against making API applications easy to write, nor I'm against responds_to. I just think that the commonest case may currently need too verbose routing rules.
That shortcoming could be minimized if we assumed the "format: :json" to restrict the only possible format to 'json' instead of using it as a default.
Please consider this change to routing DSL for Rails 4.
Best,
Rodrigo.