代码之家  ›  专栏  ›  技术社区  ›  Peter R

我如何在普通的ruby中实现类似rails的before-initialize/before-new?

  •  3
  • Peter R  · 技术社区  · 7 年前

    在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 拿一个block/proc/list of symbols,我不介意,只是试着理解这个概念)

    2 回复  |  直到 7 年前
        1
  •  3
  •   matthewd    7 年前

    对于全面的描述,您可以始终 check the Rails source ;毕竟,它本身是用“普通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
    

    留给读者:

    • 论据
    • 按名称调用方法
    • 继承
    • 回调中止调用并提供返回值
    • 包装原始调用的“around”回调
    • 条件回调( :if / :unless )
    • 子类有选择地重写/跳过回调
    • 在序列中的其他位置插入新回调

    …但是,消除所有这些是(希望)使这个实现更容易实现的原因。

        2
  •  2
  •   Jörg W Mittag    7 年前

    一种方法是重写 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
    
    推荐文章