Переменные экземпляра и класса в контроллере rails

Я новичок в рельсах и рубине. Я изучал концепцию переменных класса и экземпляра. Я понял разницу, но когда я попробовал это с помощью контроллера в рельсах, это меня смутило. То, что я сделал, это объявить переменные класса и экземпляра вне методов класса:

class BooksController < ApplicationController
  # GET /books
  # GET /books.json

  @@world = "Hello World"
  @insworld = "my hobby"

  def index
    @books = Book.all
    binding.pry

    respond_to do |format|
      format.html # index.html.erb
      format.json { render json: @books }
    end
  end

end

У меня создалось впечатление, что @insworld имеет значение "мое хобби", но когда я попытался проверить значение @insworld, когда я был внутри index method, @insworld возвращал значение nil. @@world имеет значение "Hello World". Так что же произошло здесь? Разве они не определены в одном классе?

Ответ 1

Классы также являются объектами в Ruby, поэтому они могут иметь свои собственные переменные экземпляра, которые называются переменными экземпляра класса.

  • @@world - это переменная класса
  • @insworld - это переменная экземпляра класса
  • #index - это метод экземпляра

Когда вы пытаетесь получить доступ к @insworld в #index, Ruby ищет переменную экземпляра в объекте A (что означает A.new), потому что #index - это метод экземпляра.

Но вы определили @insworld как переменную экземпляра класса, которая означает, что она определена в самом объекте класса (что означает A).

Следующий код демонстрирует:

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

Имейте в виду, что все переменные экземпляра возвращают nil, если они не существуют.

Ответ 2

Когда вы объявляете @instworld, вы находитесь в классе BooksController (т.е. self вернет BooksController). В рубине странно, что классы также являются объектами (это экземпляры класса Class), поэтому вы фактически заявляете переменная экземпляра @instworld для этого конкретного экземпляра класса Class m, не для примера BooksController.

Вы можете легко проверить это, объявив метод класса:

class A
  # self here returns class A
  @variable = 'class instance variable'
  @@variable = 'class variable'

  def initalize
    # self here returns the instance
    @variable = 'instance variable'
  end

  def self.test_me
    # self here returns class A
    @variable
  end

  def test_me
    # self returns the instance
    @variable
  end

  #class variable is accessible by both class and instance 
  def test_me2
    @@variable
  end

  def self.test_me2
    @@variable
  end
end

A.test_me       #=> 'class instance variable'
A.new.test_me   #=> 'instance variable'
A.test_me2      #=> 'class variable'
A.new.test_me2  #=> 'class variable'

Ответ 3

Объем переменных экземпляра - это действие, которое они начинают инициировать, когда происходит какое-либо действие и уничтожается при завершении действия.

Ответ 4

Когда вы объявляете переменную экземпляра вне метода, вы можете не получить желаемый результат. Это переменная экземпляра, да, но она относится к самому классу (который является экземпляром класса Class).

class Foo
  @myvar = "class-level"

  def initialize
    @myvar = 'instance-level'
  end
end

f = Foo.new

f.class.instance_variable_get(:@myvar) # => "class-level"
f.instance_variable_get(:@myvar) # => "instance-level"
f.instance_variable_get(:@myvar2) # => nil 

Если вы попытаетесь получить значение неинициализированного ivar, оно будет оцениваться до nil. Вот почему вы получили nil в своих экспериментах: переменная не существует в этой области.

Чтобы получить переменные уровня экземпляра, установите их в методах/действиях.

Ответ 5

Объявление @insworld внутри класса, но не в конструкторе или любом из методов экземпляра, устанавливает @insworld в область экземпляра самого класса.

BooksController.instance_variable_get(:'@insworld')

Если вам нужен доступ к переменной внутри вашего метода индекса, рассмотрите его определение в рамках метода.