I rediscovered something about Rails instance variables tonight after initially discovering it a few weeks ago and not writing it down. I spent a good 30 minutes trying to figure out why the following code segment would not produce my expected results in my browser.
def save
@bookmark_file = BookmarkFile.new(params[:bookmark_file])
@bookmark_file.user_id = session[:user].id
parse_file @bookmark_file.data
@bookmark_file.parsed_on = Time.now
@bookmark_file.save
redirect_to :action => 'list_parsed_bookmarks'
end
On the browser side I had:
-- unimportant lines omitted --
-- just display the bookmarks in the collection --
I had an instance variable @bookmark_file with a one_to_many relationship with bookmarks that I wanted to display on the page for a user. Unfortunately when I tried to display the page in Firefox I got the following error:
You have a nil object when you didn't expect it! The error occured while evaluating nil.bookmarks
My @bookmark_file instance variable was nil in the view when it was clearly set in the controller's save method. On the surface it would seem that the @bookmark_file instance variable became nil somewhere between the controller and the view. Examining the redirect_to method yields a slightly different answer though.
According to the Rails documentation, ActionController::Base#redirect_to redirects the browser as its name implies. As far as the Rails routing engine is concerned this is a completely new request and all instance variables are thrown out. SO it's not that my @bookmark_file variable was set to nil on the way to the view, rather it wasn't even my @bookmark_file. The new request didn't set any @bookmark_file so there was nothing for the view to operate on.
ActionController::Base#render actually displays the HTML produced by the action or view specified when it is called. By replacing the call to redirect_to with a call to render in the controller the application works as expected because Rails will find the list_parsed_bookmarks view for the controller and use the current instance variables when rendering the view. WIth @bookmark_file already set in the save method the view is then free to use the initalized object.
It may be possible to get the desired behavior out of the original way the method was written by storing @bookmark_file in a session variable but I haven't tried and I don't think it would be a good idea to do so. Using session variables implies that data is shared across requests and creating another request to display the data rather than continue with the current request seems foolish in this situation. The potentially high volume of data stored across all sessions is also a good deterrent.
Any comments are welcome.




