Instance and Class Variables in Rails Controller

instance and class variables in rails controller

Classes are also objects in Ruby, so they can have their own instance variables which are called class instance variables.

  • @@world is a class variable
  • @insworld is a class instance variable
  • #index is an instance method

When you try to access @insworld in #index, Ruby searches for the instance variable in the A object (meaning A.new) because #index is an instance method.

But you defined @insworld as a class instance variable which means it is defined in the class object itself (meaning A).

The following code demonstrates:

class Hi
@@a = 1 # class variable
@b = 2 # class instance variable

def initialize
@c = 3 # instance variable
end

def test # instance method, works on objects of class Hi
puts @@a # => 1
puts @b # => nil, there is no instance variable @b
puts @c # => 3 # we defined this instance variable in the initializer
end
end

Hi.class_variables # => @@a
Hi.instance_variables # => @b
Hi.new.instance_variables # => @c
# Hi is an object of class Class
# Hi.new is an object of class Hi

Keep in mind that all instance variables return nil if they don't exist.

Ruby on Rails Controller Instance Variable not shared

@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

Defining instance variables in rails controllers

In this case, defining instance variable in users#new, for example, is necessary for it to be accessible in view. Rails uses this specific mechanism to copy all the instance variables defined in controller context to view.

On the other hand, in sessions#create you only want to redirect the user if sign in is successful and display sign in form otherwise, which - as you see in sessions#new - doesn't require any instance variables defined. So local variable in this case is sufficient.

Rails can a class variable be assigned within a controller's instance method?

Your best bet here is to set up a before_action (or before_filter prior to Rails 5.)

class Api::VideoEpisodesController < Api::ApiController
before_action :set_video_episode, only: [:index, :show]

def index
end

def show
end

private
def set_video_episode
@video_episode = VideoEpisode.find(params[:id])
end

end

Now you have access to @video_episode in both the index and show actions.

Choosing a controller for a layout class instance variable in Rails

You could add a set_year action in your ApplicationController, something like:

class ApplicationController < ActionController::Base 

private

def set_year
@current_year = DateTime.now.year
end

end

And then call it in a before_action in your relevant controller actions. Something like:

class FooController < ApplicationController
before_action :set_year, only: [:some_action, :another_action]
end

Alternatively and riffing off MrYoshiji's comment, you could create a current_year action in ApplicationController, something like:

class ApplicationController < ActionController::Base

def current_year
@current_year ||= DateTime.now.year
end

end

In which case, you could use current_year in your partial instead of @current_year. And then, as MrYoshiji says, you could move that into a helper if that sort of things floats your boat. Something like:

module ApplicationHelper

def current_year
@current_year ||= DateTime.now.year
end

end

The upside, I suppose, of moving current_year into a helper is that it de-clutters your ApplicationController. The downside, I further suppose, is that it obsfucates (to some degree), the source of the current_year action. Again, you'll have to take your boat floating coefficient into consideration.

BTW, @current_year isn't a DateTime. It's a Fixnum.

Rails controllers and instance variables

  1. Correct; it belongs to the instance of FoosController that's processing the current request.
  2. Yes, each request creates a new instance of the controller–that's why instance variables can be used to hold state for a single request.
  3. Yes, but: if index isn't called, @foos won't be initialized, There is no @foos instance variable when the show action is hit in the code you show1.

1 If you called index from show, then @foos would be initialized and available on the page. Not that you should do this, because that's confusing concerns.

Copy a controller class instance variables

This problem may be solved with viewcomponent, which allows for standard ruby code for the view controller. Also solves the problem of dividing the view code to smaller reusable components in reasonable speed.

To use the viewcomponent gem first include it to your Gemfile.

gem 'view_component', require: 'view_component/engine'

After updating your gems with bundle install, you will also need to restart your server if it's running, to apply the new gem.

Then generating the component is similar in usage to other rails generators. The first argument is a component name and the second is a component argument.

rails generate component Header site_id

Now I focus on files generated in app/component directory, view and controller code. This will simply be the controller to create the header snippet of the view.

Inside of app/component/header_component.rb can be encapsulated all the code from WallController related to the header view.

class HeaderComponent < ViewComponent::Base
def initialize(site_id:)
puts "Rendering header component for site: #{site_id}"
# Load site elements
@site = Site.find site_id
@menu_items = []
Site.all.each.with_index do |site, index|
@menu_items.push site.title => site.link
end
end
end

Similarly, put all the header view erb code to the app/component/header.html.erb.

The finished component can be generated from the view using rails render:

  <%= render HeaderComponent.new(site_id: 1) %>

Moving out instance variables from Controller to Service Class

If you want to do some calculations in a separate method and assign some instance variables there, then use them in your view, you can move those methods to a mixin and include that mixin inside your controller.

# mixins/user_helper.rb
module UserHelper
def set_user_detail_variables(user_id)
@user = User.includes(:comments, :addresses, :posts).find(user_id)

@user_comments = @user.comments
@user_addresses = @user.addresses
@user_posts = @user.posts
end
end

# controllers/users_controller.rb
class UsersController < ApplicationController
include UserHelper

def show
set_user_detail_variables(params[:id])

respond_to do |format|
format.html
format.json { render json: @user }
end
end
end

This way your controller would not get lengthy and also you will be able to use your variables.

What is the scope of an instance variable in a Rails controller?

An instance variable in a Rails controller is used in two ways:

  • It is available within instance methods of that controller (including superclass instance methods), just like any other Ruby instance variable.
  • When a Rails controller action renders a view, the controller's instance variables are copied (shallowly) to the view instance. (The view instance is the value of self within a template.) Controller instance variables defined at the time an action renders are therefore effectively available within that action's view. View helpers are just modules which are extended by the view instance, so controller instance variables are effectively available within view helper methods too.

    Note that although controller instance variables are copied to the view, instance variables defined in the view are not copied back to the controller. That's not something you'd normally need to have happen because rendering the view is normally the last thing done in a controller action. And local variables defined in a view are available throughout the view, so there's no reason at all to define an instance variable in a view.

An instance variable in a given controller is not available within other controllers, within other controllers' views, or within any models at all.



Related Topics



Leave a reply



Submit