【问题标题】:call before methods in model on ruby在红宝石模型中的方法之前调用
【发布时间】:2015-04-22 00:16:47
【问题描述】:

这是我在模型中的所有方法之前开发代码运行方式的实现

调用“before_hook :months_used”方法需要在类的底部,ExecutionHooks 才能获取模块中加载的instance_method。我想在顶部加载实例方法

class BalanceChart < BalanceFind
 include ExecutionHooks

 attr_reader :options

 def initialize(options = {})
  @options = options
  @begin_at = @options[:begin_at]
 end

 def months_used
  range.map{|date| I18n.l date, format: :month_year}.uniq!
 end

 before_hook :months_used
end

module ExecutionHooks

def self.included(base)
 base.send :extend, ClassMethods
end

module ClassMethods
  def before
   @hooks.each do |name|
    m = instance_method(name)
    define_method(name) do |*args, &block|  

      return if @begin_at.blank? ## the code you can execute before methods

      m.bind(self).(*args, &block) ## your old code in the method of the class
    end
   end
  end

  def before_hook(*method_name)
   @hooks = method_name
   before
  end

  def hooks
   @hooks ||= []
  end
 end
end

【问题讨论】:

  • 您的意思是您想在班级顶部调用 before_hook?
  • 是的@FrederickCheung

标签: ruby model metaprogramming


【解决方案1】:

您可以使用prepend 执行此操作。 prependinclude 类似,它向类的祖先添加了一个模块,但是它不是在类之后添加它,而是在之前添加它。

这意味着如果一个方法同时存在于前置模块和类中,则首先调用模块实现(如果它想调用基类,它可以选择调用super)。

这允许你像这样编写一个钩子模块:

module Hooks
  def before(*method_names)
    to_prepend = Module.new do
      method_names.each do |name| 
        define_method(name) do |*args, &block|
          puts "before #{name}"
          super(*args,&block)
        end
      end
    end
    prepend to_prepend
  end
end


class Example
  extend Hooks
  before :foo, :bar

  def foo
    puts "in foo"
  end
  def bar
    puts "in bar"
  end
end

在实际使用中,您可能希望将该模块存储在某个地方,以便每次调用 before 都不会创建新模块,但这只是一个实现细节

【讨论】:

  • 谢谢。稍后我会尝试测试您的代码并回复反馈
  • 嗯.. 似乎对我不起作用。在irb (Ruby-2.0.0-p247) 中尝试过。呼叫 Example.new.foo 仅打印 before foo in foo。我做错了吗?
  • 这正是它应该做的。
  • 找到了你的实现,看起来不错,但是你能扩展一下最后的评论吗?据我了解,您只会加载一次Example 类,此时将调用before 方法,仅为Example 类创建一个模块。希望我解释得当
  • 没错。如果您调用 Example.new.foo,除了“before foo in foo”之外,您还期望输出什么?
【解决方案2】:

@rathrio 这是我使用你所说的方法添加的实现。谢谢

module ExecutionHooks

  def validation
    p "works1"
  end

  def self.included(base)
    base.send :extend, ClassMethods
  end
end

module ClassMethods
  attr_writer :hooked
  
  def hooked
    @hooked ||= []
  end

  def method_added(method)
    return if @hooks.nil? 
    return unless @hooks.include?(method)
    m = self.instance_method(method)
    unless hooked.include?(method)
      hooked << method
      define_method(method) do |*args, &block|  
        validation      
        m.bind(self).(*args, &block) ## your old code in the method of the class
      end
    end
  end

  def before_hook(*method_name)
    @hooks = method_name
  end

  def hooks
    @hooks ||= []
  end
 end
end

class BalanceChart < BalanceFind
 include ExecutionHooks
 before_hook :months_data, :months_used, :debits_amount, :test

  def test
    "test"
  end
end

【讨论】:

    【解决方案3】:

    您可以在调用before_hook 时覆盖method_added 钩子,以便在定义方法后立即将before 钩子添加到该方法的前面,而不是重新定义该方法。这样,您的 before_hook 调用可以(实际上,必须)放置在类定义的顶部。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-03-21
      • 1970-01-01
      • 2014-12-05
      • 2011-02-28
      • 2013-03-19
      • 2010-09-08
      • 2023-04-07
      相关资源
      最近更新 更多