Persistent variables in Rails 3 - ruby-on-rails

def edit
#title ="Edit account"
#page_name = "edit"
end
def update
if #wsp.update_attributes(params[:wsp])
# it worked
flash[:success] = "Profile updated."
if (#title == "Location")
redirect_to wsp_location_path
else
redirect_to edit_wsp_path
end
else
#title = "Edit account"
render 'edit'
end
end
The variable #title is empty when in the update method. How can I make the #title persistent so I can read it?

Each controller action is executed in a separate request, so you are losing the values in between.
You probably need to use session, or better yet flash to store the title across requests.
def edit
flash[:title] = #title = "Edit account"
...
end
def update
...
if (flash[:title] == "Location")
redirect_to wsp_location_path
else
redirect_to edit_wsp_path
end
end

In the edit action, you should write the result to a form field, perhaps a hidden field. Then read that data back in from the submitted form in your update action. You really should not preserve any state server side between these 2 requests.
And while you could use the session, I would advise against this. Overuse of the session for passing tiny bits of short lived data between requests is generally bad form.

If the entire purpose of this is to display it on the Edit page, why not put it there (app/views/the_controller/edit.html.erb)? That way it'd be shown on all requests to that page and you're not putting view / presentation code into the controller where it doesn't belong.

Related

How to render an action plus GET parameter?

In my Rails 4 application I have this controller:
class ProfilesController < ApplicationController
SECTIONS = %w(time currency letterhead)
def edit
section = Profile::SECTIONS.include?(params[:section]) ? params[:section] : Profile::SECTIONS[0]
session[:section] = section
end
def update
if #profile.update_attributes(profile_params)
flash[:success] = "Profile updated."
redirect_to edit_profile_path(:section => session[:section])
else
render :edit
end
end
end
In the view, each user's profile record is spread across three different tabs using GET parameters.
E.g. when the user is in the "time" tab and successfully saves it, s/he is redirected to that same tab. Nice!
But unfortunately, when the save is unsuccessful (and Rails' validation kicks in), that doesn't work.
I tried adding a parameter to render but to no avail.
Can anybody tell me what's the best way to do this?
Thanks for any help.
I think you're checking params[:section] in the view and you're using its value to determine which tab to show. So the easiest solution is to just set params[:section] in the else part.
if #profile.update_attributes(profile_params)
flash[:success] = "Profile updated."
redirect_to edit_profile_path(:section => session[:section])
else
params[:section] = session[:section]
render :edit
end
Another easier way is to just use session[:section] in the view so you don't have to pass it in between requests.
render :edit, :locals => {:params1 => "value"}
Use locals to pass paramters in render

Good way of storing the location from which request originated rails

If I was to create some object from different places, ex:
I'm creating object from my index page (POSTing to create method of controller)
I'm creating object from my show page (POSTing to create method of controller)
In case of success/error I want to be able to return to the requestors page and display the flash message.
So far I got this working :
def store_location
session[:return_to] = request.url
end
def redirect_back_or(default)
redirect_to(session[:return_to] || default)
session.delete(:return_to)
end
But my concern is what if someone else from my team choose to use this piece of code for the similar situation. It might cause the application to redirect to entirely different location.
Is there a better way to do this?
One of the possible solutions could be adding to the form a hidden_field that will allow to distinguish those two cases.
Let's say you have Post model, consider an example:
From show page:
<%= form_for #post do |f| %>
...
<%= hidden_field_tag :return_to, posts_path %>
<% end %>
Second form (that is on index page) can be leaved untouched, so it will not pass any value for "return_to" parameter. We would check in a controller if no value passed to "return_to" then consider it as a "Submit from index page" case.
In your controller:
def create
...
redirect_to params[:return_to] || posts_path
end
Or how about using the actual request referrer?
Controller:
def create
...some create logic
flash[:success] = 'YEEEY
redirect_to request.referer
rescue
flash[:error] = 'ERROR OCCURRED'
redirect_to request.referer
end

Do I need to validate session variables in every controller action? (Rails)

Sort of a follow-on from this question.
In my application, I have a Task model. Multiple pages can link to a single Task's "edit" url. For example, /tasks/1/edit is linked to from /tasks/1 and user/1/ (User being another model).
In my controller, I want to be able to redirect back to any one of the referring pages after the "edit" is submitted via an "update" action. For example, if I go to /tasks/1/edit from user/1/, after the "update" action I want to redirect back to user/1/. Same deal if I go to /tasks/1/edit from /tasks/1.
In my GET "edit" action I am doing:
#task = Task.find(params[:id])
if request.referer and (request.referer == task_url(#task) or request.referer == user_url(#task.user))
session[:return_to] = request.referer
else
session.delete(:return_to)
end
In the corresponding PUT "update" action I do:
#task = Task.find(params[:id])
respond_to do |format|
if #task.update_attributes(params[:task])
format.html { redirect_to session.has_key?(:return_to) ? session[:return_to] : #task #return to task if no return_to specified
else
...
end
end
This works, but I am concerned that the client could spoof/fake their session[:return_to] in the "update", allowing them to redirect to whatever page they want.
Does that matter? Is this a valid concern? Do I need to validate session[:return_to] in the "update"?
In short, yes...
request.referrer stores the URL that brought a user to your site. If you did the following:
Create a static HTML page
Embed a link to the /task/1/edit
At the end of the update, it would redirect back to the static web page.
To solve this problem, what you should do is store a session variable that denotes the location (e.g. in your UsersController, do a before_filter and set session[:return_task_update_to] = :users)
And then, at the end of the update, you redirect back, and clear the :return_task_update_to variable in the session. You would obviously also handle the case if there is no session variable (in which case you would redirect to whatever made sense as a default).
This way, you don't need to validate the URL or do something crazy. Simply set up the state in the session, and redirect if it exists.

On create error, should I render `new` or redirect to `new`?

Suppose I have something like this:
def new
#user = User.new
end
def create
#user = User.create(params[:user])
if #user.save
flash[:notice] = 'User created'
redirect_to :action => 'list'
else
flash[:error] = 'Some error here!'
render 'new'
end
end
I think the code is clear.
The problem here is, when the #user object is not saved successfully, should I render new (as above) or should redirect to new?
I know if redirect to new the data input by the user is lost, but if I render new, the URL will be /users/create instead of /users/new (which is ugly!).
You are correct in not using redirect. Redirect is loading an entirely new resource.
render however will keep your session data fresh, and depending on how your form is set up, should repopulate whatever data was inputted.
You mention:
I know if redirect to new the data input by the user is lost, but if I render new, the URL will be /users/create instead of /users/new (which is ugly!).
No, this is not true. If you say render 'new', it will go to the url users/new not create. Create as an action only handles POST requests to your controller, and generally never has a view associated with it. It will instead refer to the new action to handle any errors and displaying of forms.
The create action has this in common with the update action which does the same thing by handling only PUT requests, but refers to the edit action to handle the displaying of views.

Render other view without loosing variable values

I have a helpers in Rails app that return some data based on a variable set in controller: like:
def title
base_title = "Unikernel"
if #title.nil? then
base_title
else
"#{base_title} | #{#title}"
end
end
And in controller (usually in each action) I set the value, eg:
#title = "Solutions"
now when I process some result I get from form submission, if something's wrong I say:
render action: "edit"
or
render "new"
When I'm on the rendered page the variables aren't initialized and so on.
What should I do with this problem?
Usually, when something is wrong while updating, you find yourself in the update action. When something goes wrong while creating, your error message gets shown by the create action. Did you set your variable in your create and update action, too?
def new
#title = "New page"
end
def create
#title = "Errors!"
end
When you submit your form and get errors, you will get #title from your create action and if you haven't set it in create, #title will be nil.
When you call render, you are not redirecting to another action, hence you are still in update.

Resources