Используйте define_method в родительском классе с содержимым Dynami c - PullRequest
1 голос
/ 05 марта 2020

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

В двух словах, я хотел бы определить методы динамически:

Начальная точка:

class Foo < Bar
  def baz
    RecordLoader.for(Baz).load(object.baz_id)
  end

  def qux
    RecordLoader.for(Quz).load(object.qux_id)
  end
end

class Bar
end

Я бы хотел изменить его на

class Foo < Bar
  record_loader_for :baz
  record_loader_for :qux
end

class Bar
  def self.record_loader_for(attribute)
    define_method attribute.to_s do
      # What is missing here?
    end
  end
end

Я пытаюсь выяснить, как я могу использовать значение атрибута, чтобы написать что-то вроде

RecordLoader.for(attribute.to_s.classify.constantize). # <- attribute is local to the class
            .load(object.send("#{attribute.to_s}_id")) # <- object is local to the instance

1 Ответ

1 голос
/ 05 марта 2020

Вы можете go с помощью class_eval и сгенерировать свой метод в строку:

  def self.record_loader_for(attribute)
    class_eval <<~RUBY, __FILE__ , __LINE__ + 1
      def #{attribute}
        RecordLoader.for(#{attribute.to_s.classify}).load(#{attribute}_id)
      end
    RUBY
  end

, но на самом деле, define_method тоже должно работать, ruby сохранит замыкание из вызова метода:

require 'active_support'
require 'active_support/core_ext'
require 'ostruct'

class RecordLoader
  def self.for(cls)
    new(cls)
  end

  def initialize(cls)
    @cls = cls
  end

  def load(id)
    puts "loading #{@cls} id #{id}"
  end
end

class Baz; end

class Bar
  def object
    OpenStruct.new(baz_id: 123, qux_id:321)
  end

  def self.record_loader_for(attribute)
    define_method attribute.to_s do
      RecordLoader.for(attribute.to_s.classify.constantize).
        load(object.send("#{attribute.to_s}_id"))
    end
  end
end

class Foo < Bar
  record_loader_for :baz
  record_loader_for :qux
end

Foo.new.baz

class_eval медленнее для определения метода, но результирующий метод выполняется быстрее и не сохраняет ссылок на исходный контекст закрытия, define_method наоборот - определяет быстрее, но метод работает медленнее.

...