Reputation: 187
My 'new' action generates the cart object @cart out of a session. When I call the 'update' action via AJAX the @cart object doesn't exist. Why is it not shared across the controller?
cart_controller.rb
def new
@cart = Cart.new(session[:cart])
end
def update
logger.debug @cart.present? # false
end
Upvotes: 3
Views: 4870
Reputation: 2398
I would suggest using before_action
to create the instance of @cart
, in the case the @cart
instance variable will be visible to new
and update
actions.
before_action :get_cart, only: [:new, :update]
private
def get_cart
@cart = Cart.new(session[:cart])
end
If you don't want to use action callbacks, another alternative is calling get_cart
method directly to new
and update
actions. Since, get_cart
returns the instance @cart
. As reference to this you can see this link
Upvotes: 0
Reputation: 1837
TL;DR: controller instance variables are not shared across different HTTP requests as each request create a new instance of the controller.
Conceptually what you are expecting should have been correct! You are defining an instance variable and you should have access to it everywhere across the class.
The problem is that on every HTTP request, a new instance of the class is being created.
So when you hit the new
action an instance of the controller will be initiated, new
method will be called and @cart
will be created and assigned. Something like:
# HTTP request /new
controller = MyController.new # an object of your controller is created
controller.new # the requested action is called and @cart is assigned
But when you make a new HTTP request to update
a new instance of the controller will be initiated, update
method will be called and it has no @cart
!
# HTTP request /update
controller1 = MyController.new # an object of your controller is created
controller1.new # the requested action is called and @cart is not assigned 😱
As you can see controller
and controller1
are two different objects initiated from MyController
as this took place in two different HTTP requests (different contexts).
To fix your issue you need to create @cart
for each action when it's needed something like:
def new
cart
end
def update
logger.debug cart.present?
end
private
def cart
@cart ||= Cart.new(session[:cart])
end
Upvotes: 0
Reputation: 44380
Instance variables (starting with @
) are not shared between requests (or across controller actions). You can denote a method in order to get cart. Here is an example:
def new
cart
end
def update
logger.debug cart.present?
end
private
def cart
@cart ||= Cart.new(session[:cart])
end
Upvotes: 1
Reputation: 33552
The instance variables can't be shared across the controller. They are available to the actions
where they are defined. So you can't use @cart
in update
action since you didn't define it.
def new
@cart = Cart.new(session[:cart])
end
def update
@cart = Cart.new(session[:cart])
logger.debug @cart.present?
end
To DRY the code, use before_action
to set the cart
before_action :set_cart, only: [:new, :update]
def new
end
def update
logger.debug @cart.present?
end
private
def set_cart
@cart = Cart.new(session[:cart])
end
Upvotes: 0
Reputation: 337
@cart
is an instance variable and it is not persisted between requests. And session
is accessible between requests.
Basically if you have set some data into session then you can use that data between requests. As it was mentioned you can setup a before_filter
and preset @cart
instance variable before executing the update
action.
class MyController < ApplicationController
before_action :instantiate_cart, only: [:update] #is the list of actions you want to affect with this `before_action` method
...
private
def instantiate_cart
@cart = Cart.new(session[:cart])
end
end
Upvotes: 6