Как создать метод, который выполняет ранее заданный блок в Ruby?
У меня есть класс, который был построен для создания подклассов.
class A
def initialize(name)
end
def some
# to define in subclass
end
end
# usage
p A.new('foo').some
#=> nil
В моем случае я не хочу создавать подкласс, так как мне нужен только один экземпляр. Поэтому я поменяю initialize
метод для поддержки следующего использования.
p A.new('foo') { 'YEAH' }.some
#=> YEAH
Как я могу поддержать использование выше?
Кстати, я нашел следующие решения для проекта Ruby 1.8.7, но они кажутся мне неловкими.
class A
def singleton_class
class << self; self; end
end
def initialize(name, &block)
@name = name
self.singleton_class.send(:define_method, :some) { block.call } if block_given?
end
def some
# to define in subclass
end
end
1 ответ
Решение
Вы можете сохранить аргумент блока в переменной экземпляра и call
это позже:
class A
def initialize(name, &block)
@name = name
@block = block
end
def some
@block.call
end
end
A.new('foo') { 'YEAH' }.some
#=> "YEAH"
Вы также можете передать аргументы в блок:
class A
# ...
def some
@block.call(@name)
end
end
A.new('foo') { |s| s.upcase }.some
#=> "FOO"
Или же instance_exec
блок в разрезе получателя:
class A
# ...
def some
instance_exec(&@block)
end
end
Что позволяет обойти инкапсуляцию:
A.new('foo') { @name.upcase }.some
#=> "FOO"