Ruby On Rails has a neat little feature called “respond_to”:
class WeblogController < ActionController::Base
def index
@posts = Post.find :all
respond_to do |format|
format.html
format.xml { render :xml => @posts.to_xml }
format.rss { render :action => "feed.rxml" }
end
end
end |
class WeblogController < ActionController::Base
def index
@posts = Post.find :all
respond_to do |format|
format.html
format.xml { render :xml => @posts.to_xml }
format.rss { render :action => "feed.rxml" }
end
end
end
Quelle
This feature uses the routes with the new default route since 1.2:
map.connect ':controller/:action/:id.:format'. |
map.connect ':controller/:action/:id.:format'.
as well as accept header.
With the later and certainly co-starring Internet Explorer 5.5 to 7.0 the fun starts.
In most examples developers serve html, xml and javascript like here. I cannot confirm neither negate that the following problem occurs with these types as well:
I just finished developing an “respond_to jpg” method:
This was there from the beginning: http://dailyfratze.de/michael/2007/8 and i could easily achieve this http://dailyfratze.de/michael/2007/8.jpg with the exact same method:
respond_to do |format|
if(!user)
# some stuff to see if the user exists, blah blah
redirect_to(:action => :index)
return
else
# some common stuff
format.html {}
format.jpg do
# some rmagick stuff
images = Array.new
# some more and finally
send_data image.to_blob, :type =>"image/jpeg", :disposition =>'inline'
# the return is important, without it and without render, the other block is executed as well
return
end
end
end |
respond_to do |format|
if(!user)
# some stuff to see if the user exists, blah blah
redirect_to(:action => :index)
return
else
# some common stuff
format.html {}
format.jpg do
# some rmagick stuff
images = Array.new
# some more and finally
send_data image.to_blob, :type =>"image/jpeg", :disposition =>'inline'
# the return is important, without it and without render, the other block is executed as well
return
end
end
end
I use the following routes in the following order:
map.connect ':user/:year/:month.:format',
:controller => 'daily',
:action => 'month',
:requirements => {:year => /(19|20)\d\d/,
:month => /(0?[1-9]|1[012])/}
map.connect ':user/:year/:month/',
:controller => 'daily',
:action => 'month',
:requirements => {:year => /(19|20)\d\d/,
:month => /(0?[1-9]|1[012])/} |
map.connect ':user/:year/:month.:format',
:controller => 'daily',
:action => 'month',
:requirements => {:year => /(19|20)\d\d/,
:month => /(0?[1-9]|1[012])/}
map.connect ':user/:year/:month/',
:controller => 'daily',
:action => 'month',
:requirements => {:year => /(19|20)\d\d/,
:month => /(0?[1-9]|1[012])/}
I know, this is not very DRY, but i didn’t find a way to make the dot in ‘:user/:year/:month.:format’, optional. Format isn’t a problem, but the dot was there to stay. (*)
Nevertheless, the thing worked perfect. Firefox and Safari send “*/*” as an accept header, the format argument was taken and all was well.
IE sends something like “image/jpeg”, “image/png”, “application/x-shockwave-flash”, [… some more stuff…], “*/”* the first request and all subsequent request send “*/*” as accept header. Hell yeah. After closing the window, the process repeats itself.
What does this mean? The first visit to a monthly view on Daily Fratze brought an jpeg image to IE users, the second the standard html page. Crap!
I found a post on a similar problem here: Strange behaviour of respond_to in IE.
The commentors says to put the most important block first but this isn’t an option if the accept header is used wrong. To me it seems that respond_to is somewhat broken. It should use the :format parameter or the accept header, not a mix of it.
I came up with the following solution:
respond_to do |format|
format.html {}
format.jpg do
# some rmagick stuff
images = Array.new
send_data image.to_blob, :type =>"image/jpeg", :disposition =>'inline'
return
end if params[:format] == 'jpg' # If responds_to doesn't take care of :format, i'll do!
end |
respond_to do |format|
format.html {}
format.jpg do
# some rmagick stuff
images = Array.new
send_data image.to_blob, :type =>"image/jpeg", :disposition =>'inline'
return
end if params[:format] == 'jpg' # If responds_to doesn't take care of :format, i'll do!
end
If i generate links to this method with url_for(…blah…, :format => ‘jpg’) i get the correct format and i guess this will work for js, rss and co. as well.
Edit:(*) I’ve noticed some change in behaviour in Rails 2.3.x: I now use :format => nil in the routes and the dot is optional as well then and i have dropped the more or less duplicate routes.
Filed in Rails
|