Как я могу реализовать что-то вроде Rails 'before_initialize / before_new в простом Ruby? - PullRequest
0 голосов
/ 01 июля 2018

В Rails мы можем определить класс как:

class Test < ActiveRecord::Base
  before_initialize :method
end

и при вызове Test.new на экземпляр будет вызываться method(). Я пытаюсь узнать больше о Ruby и методах классов, подобных этому, но у меня возникают проблемы при попытке реализовать это на простом Ruby.

Вот что у меня есть:

class LameAR
  def self.before_initialize(*args, &block)
    # somehow store the symbols or block to be called on init
  end

  def new(*args)
    ## Call methods/blocks here
    super(*args)
  end
end

class Tester < LameAR
  before_initialize :do_stuff

  def do_stuff
    puts "DOING STUFF!!"
  end
end

Я пытаюсь выяснить, где хранить блоки в self.before_initialize. Первоначально я попробовал переменную экземпляра, такую ​​как @before_init_methods, но в тот момент эта переменная экземпляра не существовала в памяти, поэтому я не мог сохранить или извлечь ее. Я не уверен, как / где я могу хранить эти блоки / символы / символы во время определения класса, чтобы потом вызывать их внутри new.

Как я мог это реализовать? (Либо имея before_initialize взять блок / proc / список символов, на данный момент я не против, просто пытаюсь понять концепцию)

Ответы [ 2 ]

0 голосов
/ 01 июля 2018

Одним из способов будет переопределение Class#new:

class LameAR
  def self.before_initialize(*symbols_or_callables, &block)
    @before_init_methods ||= []
    @before_init_methods.concat(symbols_or_callables)
    @before_init_methods << block if block
    nil
  end

  def self.new(*args, &block)
    obj = allocate

    @before_init_methods.each do |symbol_or_callable|
      if symbol_or_callable.is_a?(Symbol)
        obj.public_send(symbol_or_callable)
      else
        symbol_or_callable.(obj)
      end
    end

    obj.__send__(:initialize, *args, &block)
  end
end

class Tester < LameAR
  before_initialize :do_stuff

  def do_stuff
    puts "DOING STUFF!!"
  end
end
0 голосов
/ 01 июля 2018

Для подробного описания вы всегда можете проверить источник Rails ; в конце концов, он сам реализован в «обычном Ruby». (Но он обрабатывает множество крайних случаев, поэтому он не очень хорош для получения краткого обзора.)

Быстрая версия:

module MyCallbacks
  def self.included(klass)
    klass.extend(ClassMethods) # we don't have ActiveSupport::Concern either
  end

  module ClassMethods
    def initialize_callbacks
      @callbacks ||= []
    end

    def before_initialize(&block)
      initialize_callbacks << block
    end
  end

  def initialize(*)
    self.class.initialize_callbacks.each do |callback|
      instance_eval(&callback)
    end

    super
  end
end

class Tester
  include MyCallbacks
  before_initialize { puts "hello world" }
end

Tester.new

Оставлено читателю:

  • аргументы
  • вызов методов по имени
  • Наследование
  • обратные вызовы, прерывающие вызов и возвращающие значение
  • "вокруг" обратных вызовов, которые оборачивают исходный вызов
  • условные обратные вызовы (:if / :unless)
  • подклассы, выборочно перекрывающие / пропускающие обратные вызовы
  • вставка новых обратных вызовов в другом месте последовательности

... но исключение всего этого - то, что [надеюсь] делает эту реализацию более доступной.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...