2

実行時に属性アクセサーを動的に追加するクラスがあります。このクラスは DSL の一部を形成し、ブロックが構成メソッドに渡され、instance_eval を使用して呼び出されます。これにより、クラスのメソッドを参照するときに、DSL で「self」への参照を削除できます。

ただし、属性を参照してその値を取得することはできますが、次のコード サンプルに示すように、明示的に自分自身を参照しない限り、属性を割り当てることができないことがわかりました。

class Bar

  def add_dynamic_attribute_to_class(name)
    Bar.add_dynamic_attribute(name)
  end

  def invoke_block(&block)
    instance_eval &block
  end

  def self.add_dynamic_attribute(name)
    attr_accessor name
  end

end

b = Bar.new

b.add_dynamic_attribute_to_class 'dyn_attr'

b.dyn_attr = 'Hello World!'

# dyn_attr behaves like a local variable in this case
b.invoke_block do
  dyn_attr = 'Goodbye!'
end

# unchanged!
puts "#{b.dyn_attr} but should be 'Goodbye!'"

# works if explicitly reference self
b.invoke_block do
  self.dyn_attr = 'Goodbye!'
end

# changed...
puts "#{b.dyn_attr} = 'Goodbye!"

# using send works
b.invoke_block do
  send 'dyn_attr=', 'Hello Again'
end

# changed...
puts "#{b.dyn_attr} = 'Hello Again!"

# explain this... local variable or instance method?
b.invoke_block do

  puts "Retrieving... '#{dyn_attr}'"

  # doesn't fail... but no effect
  dyn_attr = 'Cheers'

end

# unchanged
puts "#{b.dyn_attr} should be 'Cheers'"

これが期待どおりに動作しない理由を誰か説明できますか?

4

1 に答える 1

4

この問題は、Rubyがインスタンス変数とローカル変数を処理する方法に起因します。何が起こっているのかというと、rubyアクセサーを使用するのではなく、instance_evalブロックにローカル変数を設定しているということです。

これはそれを説明するのに役立つかもしれません:

class Foo
  attr_accessor :bar

  def input_local
    bar = "local"
    [bar, self.bar, @bar, bar()]
  end

  def input_instance
    self.bar = "instance"
    [bar, self.bar, @bar, bar()]
  end

  def input_both
    bar = "local"
    self.bar = "instance"
    [bar, self.bar, @bar, bar()]
  end
end

foo = Foo.new
foo.input_local #["local", nil, nil, nil]
foo.input_instance #["instance", "instance", "instance", "instance"]
foo.input_both #["local", "instance", "instance", "instance"]

bocksが機能する方法は、ローカル変数とインスタンス変数を区別することですが、リーダーが呼び出されたときにローカル変数が定義されていない場合、クラスはデフォルトでインスタンス変数になります(私の例のinput_instanceの呼び出しの場合のように)。

希望する動作を得るには3つの方法があります。

インスタンス変数を使用します。

    クラスフー
      attr_accessor:bar

      def Evaluation(&block)
        instance_eval&block
      終わり
    終わり

    foo = Foo.new
    foo.evaluate do
      @bar="インスタンス"
    終わり
    foo.bar#"インスタンス"

自己変数を使用します。

    クラスフー
      attr_accessor:bar

      def Evaluation(&block)
        block.call(self)
      終わり
    終わり

    foo = Foo.new
    foo.evaluate do | c |
      c.bar="インスタンス"
    終わり
    foo.bar#"インスタンス"

セッター関数を使用します。

    クラスフー
      attr_reader:bar
      defset_bar値
        @bar=値
      終わり

      def Evaluation(&block)
        instance_eval&block
      終わり
    終わり

    foo = Foo.new
    foo.evaluate do
      set_bar "instance"
    終わり
    foo.bar#"インスタンス"

これらの例はすべて、foo.barを「インスタンス」に設定します。

于 2010-12-09T23:37:41.460 に答える