Ответ 1
Прошло много времени с тех пор, как я спросил об этом. В ruby 1.9+ лучший способ сделать это, используя define_singleton_method
, следующим образом:
obj = SomeObject.new
obj.define_singleton_method(:new_method) do
"do some things"
end
obj = SomeObject.new
def obj.new_method
"do some things"
end
puts obj.new_method
> "do some things"
Это работает нормально. Однако мне нужно сделать то же самое внутри существующего метода:
def some_random_method
def obj.new_method
"do some things"
end
end
Работает нормально, но метод внутри метода выглядит довольно ужасно. Вопрос в том, есть ли альтернативный способ добавления такого метода?
Прошло много времени с тех пор, как я спросил об этом. В ruby 1.9+ лучший способ сделать это, используя define_singleton_method
, следующим образом:
obj = SomeObject.new
obj.define_singleton_method(:new_method) do
"do some things"
end
Используйте Mixin.
module AdditionalMethods
def new_method
"do some things"
end
end
obj = SomeObject.new
obj.extend(AdditionalMethods)
puts obj.new_method
> "do some things"
Просто интересно отметить:
если бы вы ушли:
def my_method
def my_other_method; end
end
Тогда my_other_method
будет фактически определен в КЛАССЕ объекта, не допуская, что приемник my_method
является экземпляром.
Однако, если вы идете (как и вы):
def my_method
def self.my_other_method; end
end
Затем my_other_method
определяется на собственном экземпляре экземпляра.
Не имеет прямого отношения к вашему вопросу, но, тем не менее, интересен;)
Вы можете использовать модули.
module ObjSingletonMethods
def new_method
"do some things"
end
end
obj.extend ObjSingletonMethods
puts obj.new_method # => do some things
Теперь, если вам нужно добавить дополнительные методы к этому объекту, вам просто нужно реализовать методы в модуле, и вы закончили.
class Some
end
obj = Some.new
class << obj
def hello
puts 'hello'
end
end
obj.hello
obj2 = Some.new
obj2.hello # error
class << obj
синтаксиса class << obj
означает, что мы открываем определение класса для объекта. Как вы, наверное, знаете, мы можем определить методы класса Ruby с помощью синтаксиса следующим образом:
class Math
class << self
def cos(x)
...
end
def sin(x)
...
end
end
end
Затем мы можем использовать следующие методы:
Math.cos(1)
В Ruby все объекты - даже классы. self
здесь является объектом самого Math class
(вы можете получить доступ к этому объекту с помощью Math.class
). Таким образом, class << self
синтаксиса class << self
означает, что мы открываем класс для Math class object
класса Math class object
. Да, это означает, что Math class
имеет класс (Math.class.class).
Для этого есть несколько синтаксисов, и все они связаны с классом singleton:
Вы можете использовать class <<
idiom, чтобы открыть определение класса singleton:
obj = Object.new
class << obj
def my_new_method
...
end
end
Или вы можете использовать define_singleton_method
для obj:
obj = Object.new
obj.define_sigleton_method(:my_new_method) do
...
end
Вы также можете использовать define_method
из класса singleton:
obj = Object.new
obj.singleton_class.define_method(:my_new_method) do
...
end
Или вы можете напрямую использовать def
:
obj = Object.new
def obj.my_new_method
...
end
Обратите внимание на пример 3, я думаю, что концепция одноэлементного класса становится более понятной для этого. Существует разница между этими двумя примерами:
a = Object.new
b = Object.new
# -- defining a new method in the object "class" --
a.class.define_method(:abc) do
puts "hello abc"
end
a.abc # prints "hello abc"
b.abc # also prints "hello abc"
# -- defining a new method in the object "singleton class" --
a.singleton_class.define_method(:bcd) do
puts "hello bcd"
end
a.bcd # prints "hello bcd"
b.bcd # error undefined method
Это связано с тем, что каждый объект имеет свой собственный одноэлементный класс:
a = Object.new
b = Object.new
p a.class # prints "Object"
p a.singleton_class # prints "#<Class:#<Object:0x000055ebc0b84438>>"
p b.class # also prints "Object"
p b.singleton_class # prints "#<Class:#<Object:0x000055ebc0b84410>>" (a different reference address)
Использовать instance_eval
:
obj = SomeObject.new
obj.instance_eval do
def new_method
puts 'do something new'
end
end
obj.new_method
> "do something new"