instance var was not kept across the method - ruby-on-rails

Here is code in customers controller:
def edit
#customer = Customer.find(params[:id])
#return_to = params[:return_to]
end
def update
#customer = Customer.find(params[:id])
if #customer.update_attributes(params[:customer], :as => :roles_new_update)
redirect_to #return_to, :notice => 'Customer was updated successfaully!'
else
render 'edit', :notice => 'Customer was not updated!'
end
end
It is verified that there is value in #return_to in method edit. However there is an error saying: Cannot redirect to nil! for
redirect_to #return_to, :notice => 'Customer was updated successfaully!'
in method update.
Any thoughts about the error? Thanks.

It's because when you are on the edit action, that is one request to show the form. Then when you submit the form, that is a second request calling the update action. Any states set in an action are confined to that action alone. There's a few ways to use data across multiple actions:
The database (So save it in a model)
The session hash
Pass it in the view to go into your form.
Because it's a redirect/return to value, i would suggestion using a session variable such as:
session[:return_to] = params[:return_to]
In your edit action, then just refer to that value in your update:
session[:return_to]

I don't understand; edit doesn't call update, so there'd be no intrinsic reason for #return_to to still exist.
Are you aware that controllers are created per-request?

Related

Rails controller redirect to form in another controller then back to saved model

I need to do something kind of weird in my Rails app. Once a user creates a Product instance through the create action, I need it to save and then redirect them to a Braintree payment method form if they haven't already added one to their account, and only then redirect them to the show page for the product.
Here's the product create action:
def create
#product = Product.new(product_params)
#product.set_user!(current_user)
if #product.save
if !current_user.braintree_customer_id?
redirect_to "/customer/new"
else
redirect_to view_item_path(#product.id)
end
else
flash.now[:alert] = "Woops, looks like something went wrong."
format.html {render :action => "new"}
end
end
The confirm method for the Braintree customer controller is this:
def confirm
#result = Braintree::TransparentRedirect.confirm(request.query_string)
if #result.success?
current_user.braintree_customer_id = #result.customer.id
current_user.customer_added = true
current_user.first_name = #result.customer.first_name
current_user.last_name = #result.customer.last_name
current_user.save!
redirect_to ## not sure what to put here
elsif current_user.has_payment_info?
current_user.with_braintree_data!
_set_customer_edit_tr_data
render :action => "edit"
else
_set_customer_new_tr_data
render :action => "new"
end
end
Is what I want to do possible?
You can store product id in a session variable before redirecting to braintree form, a then after complete confirmation just read this id from session and redirect to product show action.
if !current_user.braintree_customer_id?
session[:stored_product_id] = #product.id
redirect_to "/customer/new"
else
redirect_to view_item_path(#product.id)
end
Keep in mind that user can open product view page just by entering valid url address if he knows product id, so you should also handle this kind of situation. You can put before_filter in product show action to check if user has brain tree setup. If you go this way, you don't need to have condition in create action. You can always redirect to product show page and before_filter will check if user needs to update braintree data.

Excluding nested form fields in controller action with Hash#exclude before mass_assignment error is generated?

I currently have a create action in my sales controller that looks like this:
def create
#sale = Sale.new(params[:sale].except(:vehicles_attributes))
if #sale.save
redirect_to #sale, :notice => "Successfully created sale."
else
render :action => 'new'
end
end
The intention is to exclude a couple of attributes that are used only to populate linked selects, and should not be submitted (there are no columns for them).
With the controller code above, I am finding that the parameters still includes "sale"=>{"vehicles_attributes"=>{"0"=>{"make"=>"","model"=>""}}} so it seems that I have missed something in the controller code.
EDIT: After some more digging around, I have found that the mass_assignment exception is firing before my except code gets a chance to remove the params that shouldn't be sent by the form, so I am back to square one.
How can I ensure that I remove the fields that shouldn't be sent by the form before I get the mass_assignment error?
As far as I know the mass_assignment error should occur during the new call, so your way should work. Although I never used the except method. Have you tried using the reject! method?
def create
params[:sale].reject! { |k, v| k == :vehicles_attributes }
#sale = Sale.new(params[:sale])
if #sale.save
redirect_to #sale, :notice => "Successfully created sale."
else
render :action => 'new'
end
end
If you need to keep the :vehicles_attributes you can also use the reject method (without the bang) which gives you a copy instead of removing it from the original hash.

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.

Detecting whether to show or to process a form based on GET or POST

I've got a User model and an Account controller. When a user visits the /account url, it should show a form containing a text field with the username in it and a button to submit the form.
I have match '/account' => 'account#index' in my routes.
In my controller I have this method defined:
def index
#user = User.find(session[:user_id])
end
(Checking the user authentication happens in a before_filter)
Now the form shows correctly and is even populated correctly. However, I need to know how to tell whether the form has been submitted. What is the rails way? Do I have a separate route that watches for a POST request to /account? Or do I detect the request type in the index method? At what point do I decide whether the form has been submitted or not?
You could detect if the form has been submitted inside of the index controller. I believe the params hash gets sets the key :method to the method used for the request.
An alternative is to redo the way you route. Instead of match '/account' => 'account#index' you can do:
get '/account' => 'account#index'
post '/account' => 'account#post_action'
And then inside your controller you could do:
def index
#user = User.find session[user_id]
end
def post_action
#user = User.find session[user_id]
if #user.update_attributes params[:user]
flash[:notice] = 'Update Successful'
render :action => index
else
flash[:notice] = 'Update Unsuccessful'
render :action => index
end
end

Ruby on Rails when create method fails, render loses local variables

Hey guys I have a simple create method with some validations and whenever the create method fails due to validation errors it re-renders the 'new' action.
The problem is in my new action/view I have a local variable that is established in the action and passed to a partial to render some related information to what the user is creating.
Now when my create action fails and I try to re-render the 'new' action I'm getting the always awesome
undefined method `cover' for nil:NilClass
error.
What is the best way to handle re-establishing my action's local variables on a render instead of redirecting to the action again and the user losing the data they input?
For some clarification. Here is some sample code:
#controller.rb
def new
#summary = User.find(params[:user_id])
#page = Page.new
end
def create
#page = Page.new(params[:page])
if #page.save
redirect_to #page
else
render :action => 'new'
end
end
in my new.html.erb file i have something like this
<%= #summary.cover %>
#page form etc...
When you create the object and attempt to save it, the object still holds the values and the validation errors, so pass it on into the render. Usually it is named the same in your create method as it is in your new method, so the template just works.
if #my_object.save
flash[:notice] = "Successfully created."
redirect_to ....
else
render :action => 'new' #assuming new.html.erb uses #my_object
end

Resources