Что такое Ruby module.included? - PullRequest
0 голосов
/ 04 мая 2020

Я пытаюсь лучше понять метапрограммирование в Ruby, и я не совсем понимаю, что такое Module.included? В настоящее время я понимаю, что это обратный вызов, вызываемый Ruby всякий раз, когда модуль включен в другой модуль или класс. Кроме этого, в каких типах (мета) программных конструкций они используются? Есть примеры?

1 Ответ

3 голосов
/ 04 мая 2020

Module#included позволяет модулям внедрять методы класса и экземпляра и выполнять связанный код из одного включения.

Документация для ActiveSupport :: Concern иллюстрирует типичное использование. Он внедряет методы класса в вызывающий класс и выполняет код. В этом случае добавление scope .

module M
  def self.included(base)
    base.extend ClassMethods
    base.class_eval do
      scope :disabled, -> { where(disabled: true) }
    end
  end

  module ClassMethods
    ...
  end
end

А вот версия ActiveSupport :: Concern, которая делает то же самое, но добавляет декларативный синтаксис sugar.

require 'active_support/concern'

module M
  extend ActiveSupport::Concern

  included do
    scope :disabled, -> { where(disabled: true) }
  end

  class_methods do
    ...
  end
end

С included класс просто включает модуль. Это позволяет модулю быть единым пакетом: методы экземпляра, методы класса и код установки.

class Thing
  # Class and instance methods are injected, and the new scope is added.
  include M
end

Без included модуль может внедрять только методы экземпляра. Методы класса должны быть добавлены отдельно, а также выполнять любой код установки.

module M
  def some_instance_method
    ...
  end

  module ClassMethods
    def setup
      scope :disabled, -> { where(disabled: true) }
    end
  end
end
class Thing
  # Inject the instance methods
  include M

  # Inject the class methods
  extend M::ClassMethods

  # Run any setup code.
  setup
end

Другими примерами может быть регистрация класса, например, в качестве доступного плагина.

class Plugin
  def self.included(base)
    base.extend ClassMethods
    bass.class_eval do
      register_as_plugin(base)
    end
  end

  module ClassMethods
    def register_as_plugin(klass)
      ...
    end
  end
end

class Thing
  include Plugin
end

Или добавление необходимых аксессоров.

class HasLogger
  def self.included(base)
    bass.class_eval do
      attr_writer :logger
    end
  end

  def logger
    @logger ||= Rails.logger
  end
end

class Thing
  include HasLogger
end
...