Ruby: расширенные переменные экземпляра

#ruby

#ruby

Вопрос:

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

В этом примере переменная, установленная при расширении, «потеряна»:

 module Mod
  def self.extended(base)
    @from_module = "Hello from Mod"
    puts "Setting variable to: #{@from_module}"
  end

  def hello_from_module
    return @from_module
  end
end

class Klass
  def initialize
    @from_class = "Hello from Klass"
  end

  def hello_from_class
    return @from_class
  end
end

klass = Klass.new       #=> #<Klass:0x00000000ed8618 @from_class="Hello from Klass">
klass.extend(Mod)       #=> #<Klass:0x00000000ed8618 @from_class="Hello from Klass">
"Setting variable to: Hello from Mod"

klass.hello_from_class  #=> "Hello from Klass"
klass.hello_from_module #=> nil (warning: instance variable @from_module not initialized)
  

Комментарии:

1. Переменная не потеряна. Переменные экземпляра принадлежат объектам (instances), вот почему они называются переменными экземпляра. Для какого экземпляра вы устанавливаете переменную? Ну, метод является одноэлементным методом Mod , поэтому self является Mod и переменная экземпляра является переменной экземпляра Mod .

2. Стоит отметить, что return неявно в Ruby, последнее, что остается в стеке, — это возвращаемое значение по умолчанию, поэтому его обычно можно опустить, если последнее, что вы хотите получить в методе в качестве возвращаемого значения.

Ответ №1:

Существует несколько способов сделать то, что вы описываете.

Наиболее распространенным из них было бы использование instance_variable_get и instance_variable_set :

 module ModA
  def self.extended(base)
    base.instance_variable_set(:@from_module, "Hello from Mod A")
    puts "Setting variable to: #{base.instance_variable_get(:@from_module)}"
  end

  def hello_from_module
    return @from_module
  end
end
  

Другой распространенный метод заключается в использовании любого из методов eval или exec . В этом случае instance_exec :

 module ModB
  def self.extended(base)
    base.instance_exec { @from_module = "Hello from Mod B" }
    puts "Setting variable to: #{base.instance_exec { @from_module }}"
  end

  def hello_from_module
    return @from_module
  end
end