Как создать метод, который выполняет ранее заданный блок в 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"
Другие вопросы по тегам