Where does Rails' respond_to block parameter get xml, html, json members? - ruby-on-rails-3

Generating a scaffold like this:
rails generate scaffold User name:string email:string
creates bits of code that look like this in the controller class:
respond_to do |format|
format.html # show.html.erb
format.json { render json: #user }
end
At first I didn't know what this meant, so I went looking and found this,
which I think adequately explains most of it. But format is supposed to end up being a Responder class. Well, there doesn't seem to be any xml or html or json members declared in the Responder class. Where do they come from?
PS:
It seems cruel that the scaffold developer would name the block parameter "format" when it ends up being a Responder (and the format information comes from the .html, .xml, .json, or whatever) to make the code sound a bit like English but hinder understanding of what's actually going on. In fact, Responder::respond is defined like so, which seems too coincidental:
def respond
method = "to_#{format}"
respond_to?(method) ? send(method) : to_format
end
But I don't see how the format here (in "to_#{format}") can be related to the block parameter named format. And to be clear, it's NOT an instance variable because it's lacking an # sign, right?

As the blog explains later, basically from here the Responder instance handles .html and .xml via method_missing, depending on mime types your rails app can process.
One of the nice blogs that describe method_missing is here.
Basically, the responder's method_missing will get called when js and json methods are invoked. The method_missing of responder class will get the "missing method_name" (that is, :js or :json symbols) as its first parameter, which will drive its rendering logic.

Related

How to give option for multiple templates for one view?

I have an application in rails3 where i want to give user an option to choose a template from defaults (4 or 5 templates) to view his records.
the approach i am working on is to sent user on setting page from where he will select the template he want to use and on the basis of that setting the template will be rendered.
This looks simple but i am not sure this will work for me, please suggest me any alternative.
Please note i am talking about PDF formats.
Let's say you have a Setting model with a string attribute template.
You would let the user save the setting through a normal controller action.
Then on the controller where you want your pdf template to show, you could do something like this.
class MyController < ApplicationController
def show
#setting = Setting.find(params[:setting_id]) # Retrieve the setting instance
respond_to do |format|
format.pdf { render setting.template }
end
end
end
This will render the template named after the template the user has selected and is stored on setting as a string.

Is there any issue with using respond_to blocks in controller actions like this?

I'm AJAXifying an existing Rails 3.2 application, and some of the requests that are made from the client are better done asynchronously. To facilitate this, and to cut down on rendering time, I'm giving feedback to the user via alerts. I have a blank div in my application.html.erb file that I add alerts to as needed from certain controller actions like so:
def my_async_controller_action
#user = User.find(params[:id])
begin
##user.import
flash[:notice] = "Your data is being downloaded."
rescue Exception => bang
flash[:alert] = "There was an error downloading your data: #{bang.message}"
end
respond_to do |format|
format.js { render 'common/flashes' }
end
end
And my common/flashes file just uses jQuery to append the alerts to the blank div. While it works fine, I have never seen alerts delivered like this, only via redirects. Is there any unwritten (or written) Rails convention or rule that I'm breaking by taking this approach? Also, is there a way to instead do this as a respond_with? I can't see how I do two different types of rendering from a single line.
If the js file you are rendering has the same name as the action of your controller, you don't need a respond_to block.
However, since you are placing your js file in a common folder, to reuse among your controllers, you have to explicitly tell Rails to render that file. This is totally fine.
If your action only responds to js, you can use brackets instead of a do ... end block to make it a one liner:
respond_to { |format| format.js }
However, since you have to specify the name of the js file, this can get quite ugly, so you can omit the respond_to block and just do the following:
render 'common/flashes'
This, however, has the inconvenient that if your controller receives an html request, it will search for a file named flashes.html inside the common folder. This will most likely return a Template is missing error. If you want to fix that, you have to add some constraints to the format.
However, if your action also responds to html, then you should declare the block like this:
respond_to do |format|
format.html
format.js { render 'common/flashes' }
end
You don't need the format.html, but when you look at the code, you can immediately tell that it responds to both html and js requests.
Using a respond_with block to render different files isn't applicable in your case, since you are only rendering some javascript. respond_with is used to return some resource in various formats (html, json, xml). This article goes into a little more detail.
respond_to do |format|
format.js
end
I see no problems from the side of your controller.
Just be sure to use escape_javascript when rending in your .js.erb

Rails3 not rendering page correctly

I am writing a simple Rails3 application running in a sub URL that works well except for one problem. If I do not add a ".html" extension at the end of a URL for the "index" method of any of the controllers, the request returns a blank page. It does not matter which controller I request, the "index" method always returns completely empty, including if I curl the URL. I have an "index.html.erb" file in each of the controllers, with simple but complete HTML, and if I do include the ".html" extension in the URL, everything works fine. Here are some example URLs and their results:
http://my.application.url/appname/pages -- returns a blank page.
http://my.application.url/appname/pages.html -- returns the correct HTML page
http://my.application.url/appname/pages/new -- returns the correct HTML form
http://my.application.url/appname/pages/1 -- returns the correct HTML page
http://my.application.url/appname/pages/1/edit -- returns the correct HTML form
My routes file looks similar to this:
My::Application.routes.draw do
scope "/appname" do
resources :posts
resources :pages
root :to => 'home#index'
end
end
The applicable part of my controller looks similar to this:
class PagesController < ApplicationController
def index
#pages = Page.all
respond_to do |format|
format.html
end
end
...
end
As I said, the index method is the only one that is having this problem. I have tried everything I can think of, including adding My::Application.default_url_options = {:format => "html"} in application.rb (which works except when I need to do a redirect_to from the controller), and I am at a loss. The app is using Thin as an application server proxied behind Apache 1.3 (which I unfortunately cannot change, and this doesn't seem to be an issue anyway because hitting the Thin server directly results in the same problem). Any ideas would be much appreciated.
So, the answer seems to be that if the URL does not specify the .html extension, the application will serve out assets (from the asset pipeline) of the same name as the controller, at least while running in the development environment. Once I removed the [controllername].css.scss and [controllername].js.coffee files (both of which were unused anyway) that were auto-generated when I created the controllers from the assets folder, the application worked correctly. Just for kicks, I tried leaving them in and running rake assets:precompile, but the behavior persisted until the files were actually removed. This still seems counterintuitive, and I am contemplating filing this as a bug.

rspec render_views ignores layouts? want to test static cached page does not display flash message

I'm trying to test that "static" pages (they're ERB, but get cached), generated through rails, don't render any stray flash notices left over by the authentication system (Devise) or wherever else.
I've tried writing this controller spec, but it appears that response.body only renders the template, not its layouts?
describe "so that static caching can be used" do
render_views
specify "flash notices are not rendered" do
# edit: the following flash lines don't do anything
# it's not the right flash object, this one is intended for
# inspecting after request not setting before request
flash[:notice] = "flash boo"
flash[:error] = "flash boo"
flash[:alert] = "flash boo"
get :show, :page => 'privacy_policy'
response.body.should have_content('flash boo')
end
end
class StaticPagesController < ApplicationController
layout 'master'
def show
response.headers['Cache-Control'] = "public, max-age=#{6.hours}"
render "static_pages/#{params[:page]}"
end
end
I've tried changing to a layout which does render flash notices, and even inserting the text into the layout template, but can't make the spec fail.
Is there a way to ask rspec to render the template with the appropriate layouts as well?
Is a controller spec the wrong way to try and do this?
It seems out of place, as it's more to do with which layouts are being used, and their contents, but the rendering process starts at the controller, it receives the result, and I can manipulate the flash hash contents before rendering.
Versions:
rails (3.0.10),
rspec-rails (2.6.1),
rspec-core (2.6.4)
Thanks, Nick
Turns out this isn't the right approach. It should be an integration test (cucumber, request spec, etc) as it's testing several layers. That and rails doesn't seem to render the templates inside their layouts at this level.
So in integration test:
Set up a flash message by making a request to a controller which does nothing else (create a dummy controller & routing in your test code), then hit the page of concern and make sure the flash notice isn't rendered.
e.g. https://gist.github.com/1178383
It seems like a long way round but it will cover it.

Rails: Render actions with a specific path

In my Rails3 application I add a specific match for edit my Post model as:
match '/edit' => 'posts#edit', :as => 'post_edit'
So, I overwrite the default path of the edit action. - Now if any errors occurs in the update action, it will render the edit action with settting the URL path with the default edit path /posts/1.
How can I overwrite that to render the edit action with setting the URL path as /edit instead of posts/1.
You are actually seeing the URL for "update", the default path for edit would be /posts/1/edit. I don't think you will be able to change what the URL displays using render :action. An alternate, although somewhat sloppy, method would be to redirect and save the #post object in the session or flash. If you do not save the #post object, you will lose the error messages from the update.
if #post.update_attributes(params[:post])
#business as usual
else
session[:post] = #post
redirect_to post_edit(#post)
end
Note that it is bad practice to save the whole object in the session (especially large objects), so you may instead want to only send the error message string with the flash and render that in the view. These are both rather hackish methods, but I don't really see an elegant way to do this.
Out of curiosity, why do you want to change the default URL? Is it necessary?