Определите одноэлементные методы при инициализации, используя переменные экземпляра - PullRequest
0 голосов
/ 16 ноября 2010

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

Я решил определить разные версии метода для каждого созданного экземпляра. Более или менее так:

class TestingSingletonMethodsWithVariable
  METHODS = %w(a b c d)

  def initialize(favorite_method)
    class << self
      METHODS.each do |method_name|
        if( favorite_method == method_name )
          define_method method_name do
            puts "#{method_name} its my favorite method"
          end
        else
          define_method method_name do
            puts "#{method_name} its not my favorite method"
          end
        end
      end
    end
  end
end

t = TestingSingletonMethodsWithVariable.new('b')
t.a
t.b
t.c
t.d

# $ ruby test/testing_singleton_methods_with_variable.rb 
# test/testing_singleton_methods_with_variable.rb:7:in `initialize': undefined local variable or method `favorite_method' for #<Class:#<TestingSingletonMethodsWithVariable:0x1001a77b8>> (NameError)
#   from test/testing_singleton_methods_with_variable.rb:6:in `each'
#   from test/testing_singleton_methods_with_variable.rb:6:in `initialize'
#   from test/testing_singleton_methods_with_variable.rb:21:in `new'
#   from test/testing_singleton_methods_with_variable.rb:21

Что происходит, так это то, что с переменными происходит что-то странное: переменные объявляют, что блок class << self снаружи не виден для переменных внутри.

Кто-нибудь может объяснить мне, как я могу вести себя, как я ищу?

Спасибо

Ответы [ 2 ]

9 голосов
/ 16 ноября 2010

В Ruby только блоки могут быть замыканиями, а тела классов (а также тела модулей и методов) не могут быть замыканиями. Или другими словами: только блоки создают новую вложенную лексическую область видимости, все остальные (тела модулей, тела классов, тела методов и тела сценариев) создают новые области верхнего уровня.

Итак, вам понадобится блок. Обычно это означает использование какой-либо формы eval, но здесь вы можете просто использовать define_singleton_method вместо:

class TestingSingletonMethodsWithVariable
  METHODS = %w(a b c d)

  def initialize(favorite_method)
    METHODS.each do |method_name|
      if favorite_method == method_name
        define_singleton_method method_name do
          puts "#{method_name} its my favorite method"
        end
      else
        define_singleton_method method_name do
          puts "#{method_name} its not my favorite method"
        end
      end
    end
  end
end

t = TestingSingletonMethodsWithVariable.new('b')
t.a
t.b
t.c
t.d
1 голос
/ 16 ноября 2010

Добавление к ответу Йорга: define_singleton_method - это Ruby 1.9+. Если вы хотите запустить его в pre 1.9, работает следующее:

class Object
  def metaclass
    class << self; self; end
  end
end
class TestingSingletonMethodsWithVariable
  METHODS = %w(a b c d)

  def initialize(favorite_method)
    METHODS.each do |method_name|
      if( favorite_method == method_name )
        metaclass.send(:define_method, method_name, Proc.new do
          puts "#{method_name} its my favorite method"
        end)
      else
        metaclass.send(:define_method, method_name, Proc.new do
          puts "#{method_name} its not my favorite method"
        end)
      end
    end
  end
end

t = TestingSingletonMethodsWithVariable.new('b')
t.a
t.b
t.c
t.d
...