Прежде всего, небольшой совет по стилю:
self.class.send(:define_method, :say_hello, lambda { test })
Вы можете сделать это немного лучше, используя новый литерал proc в Ruby 1.9:
self.class.send(:define_method, :say_hello, -> { test })
Но тебе это не нужно. В Ruby есть нечто, называемое blocks , которое в основном представляет собой фрагмент кода, который вы можете передать в качестве аргумента методу. Фактически, вы уже использовали блоки, поскольку lambda
- это просто метод, который принимает блок в качестве аргумента и возвращает Proc
. Однако, define_method
уже все равно принимает блок, нет необходимости передавать блок в lambda
, который преобразует его в Proc
, который он передает в define_method
, который затем преобразует его обратно в блок:
self.class.send(:define_method, :say_hello) { test }
Как вы уже заметили, вы определяете метод не для того класса. Вы определяете его в классе Example
, поскольку внутри метода экземпляра, например initialize
, self
является текущим объектом (т. Е. ex1
или ex2
в примере @ mikej), что означает, что self.class
Класс ex1
, который Example
. Итак, вы перезаписываете один и тот же метод снова и снова.
Это приводит к следующему нежелательному поведению:
ex1 = Example.new('ex1')
ex2 = Example.new('ex2') # warning: method redefined; discarding old say_hello
ex1.say_hello # => ex2 # Huh?!?
Вместо этого, если вам нужен одноэлементный метод, вам нужно определить его для одноэлементного класса:
(class << self; self end).send(:define_method, :say_hello) { test }
Работает как задумано:
ex1 = Example.new('ex1')
ex2 = Example.new('ex2')
ex1.say_hello # => ex1
ex2.say_hello # => ex2
В Ruby 1.9 есть метод, который делает это:
define_singleton_method(:say_hello) { test }
Теперь, это работает так, как вы хотите, но здесь есть проблема более высокого уровня: это не код Ruby. Это Ruby синтаксис , но это не код Ruby, это схема.
Теперь, Scheme - это блестящий язык, и написание кода на Scheme в синтаксисе Ruby, безусловно, неплохая вещь. Он превосходно пишет код Java или PHP с синтаксисом Ruby или, как было вчера в вопросе StackOverflow, код Fortran-57 с синтаксисом Ruby. Но это не так хорошо, как писать Ruby код в синтаксисе Ruby.
Схема - это функциональный язык. Функциональные языки используют функции (точнее, замыкания функций) для инкапсуляции и состояния. Но Ruby не является функциональным языком, это объектно-ориентированный язык, и языки OO используют objects для инкапсуляции и состояния.
Итак, замыкания функций становятся объектами, а захваченные переменные становятся переменными экземпляра.
Мы также можем прийти к этому с совершенно другой точки зрения: то, что вы делаете, это то, что вы определяете singleton метод, который является методом, целью которого является определение поведения, характерного для один объект. Но вы определяете этот одноэлементный метод для каждого экземпляра класса, и вы определяете одинаковый одноэлементный метод для каждого экземпляра класса. У нас уже есть механизм определения поведения для каждого экземпляра класса: методы экземпляра.
Оба эти аргумента исходят из совершенно противоположных направлений, но они достигают одного и того же пункта назначения:
class Example
def initialize(test='hey')
@test = test
end
def say_hello
@test
end
end