castiel
castiel

Reputation: 2783

A problem with a operator(||=) in rails controller

def index
  @hash ||= Hash.new
  puts @hash       #the result is {} every time I reload the action
  @hash['key'] = value
end

I thought by doing this the variable @hash will be signed just once.

But turns out, if I am right, the @hash will be a new empty hash every time I reload the index action.

Am I right or there is another reason for this odd phenomenon?

Upvotes: 2

Views: 214

Answers (3)

tokland
tokland

Reputation: 67900

Controller instances are not shared for different requests (that would be a real mess!), every single request creates a new instance.

Just use the hash-like object session, it's the right way to have stateful requests (data is usually -but not always, there are other ways- stored in a cookie)

session[:my_key] = my_value

Upvotes: 4

Arsen7
Arsen7

Reputation: 12830

Tokland gave you the right suggestion of using session object, and I can only serve you with a little more detailed explanation.

The operator ||= works as you expect, but you assumed that the same object handles all your requests. In Rails a new instance of the controller is created for handling every request. The class is cached (at least in production and test environment), but the instances are not.

This way of handling requests ensures that your action does not need to worry about instance variables from other actions, and even from previous calls of the same action. Thus you may be sure that if your action did not set a variable, then it is nil.

If you are curious, you can test how this would behave if you define a class-method and handle the instance variable there. (Just remember that in development the class is also recreated for each request)

class SomeController
  def index
    self.class.check_variable("Some value")
  end

  def self.check_variable(value)
    @hash ||= Hash.new
    logger.info "Before: @hash = #{@hash.inspect}"
    @hash['key'] = value
    logger.info "After:  @hash = #{@hash.inspect}"
  end
end

Upvotes: 3

Mischa
Mischa

Reputation: 43298

Memoization (||=) is useful when you use a variable more than once in a single request. For example:

def current_user
  @current_user ||= User.find(session[:user_id])
end

And then, for example, in your view:

<%= current_user.name %> (<%= current_user.email %>)

In this example, because of using memoization User.find will only be executed once, whereas if you didn't use memoization, it would execute twice.

I agree with tokland, just wanted to add an answer that shows when it is useful to use memoization.

Upvotes: 1

Related Questions