1

我正在尝试创建某种模块或超类,在子类的每个方法之后包装一个方法调用。但是有一些限制:我不希望在调用 initialize() 之后运行该方法,也不希望在调用我选择的其他一些方法之后运行该方法。另一个限制是我只希望在@check_ec标志设置为 true 时执行该方法。我有超过 60 种方法的类,我对粘贴在各处的同一段代码进行了硬编码。有没有办法可以制作一个包装器,为我的类方法自动执行该方法?

所以想法是这样的:

class Abstract
  def initialize(check_ec)
    @check_ec = check_ec
  end
  def after(result) # this is the method that I'd like to be added to most methods
    puts "ERROR CODE: #{result[EC]}"
  end
  def methods(method) # below each method it would execute after
    result = method() # execute the given method normally
    after(result) if @check_ec and method != :initialize and method != :has_valid_params
  end
end

class MyClass < Abstract
  def initialize(name, some_stuff, check_error_code)
   # do some stuff...
    @name = name
    super(check_error_code)
  end
  def my_method_a() # execute after() after this method
    return {EC: 0}
  end   
  def my_method_b() # execute after() after this method
    return {EC: 7}
  end
  def has_valid_params() # don't execute after() on this method
    return true
  end

end
4

3 回答 3

2

这很容易使用method_missing, 和组合而不是继承。您可以构建一个非常简单的类来转发方法调用,然后执行after回调,除了特定的方法名称:

class Abstract
  def initialize(object)
    @object = object
  end

  def method_missing(method, *arguments)
    result = @object.send(method, *arguments)

    after() unless method == "has_valid_params"

    result
  end

  def after
    # whatever
  end
end

o = Abstract.new(MyClass.new)
于 2013-08-22T02:49:07.607 回答
0

那这个呢?它有一个主要缺点,即您的方法必须在调用之前已经定义check_error_code,但它可能适合您的需要。您可以在 Rails 回调中寻找更好的解决方案的灵感,或者推迟重新定义每个方法,直到使用method_added钩子添加该方法。

在每个要检查错误代码的类中包含ErrorCodeChecker并调用(如代码段的最后一行)。check_error_code

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

  def after(result) # this is the method that I'd like to be added to most methods
    puts "ERROR CODE: #{result[:ec]}"
  end

  module ClassMethods
    def check_error_code(options = {})
      check_on = instance_methods(false) - Array(options[:except])
      check_on &= Array(options[:only]) if options[:only]
      class_eval do
        check_on.each do |method|
          alias_method "#{ method }_without_ec", method
          define_method(method) do |*args, &block|
            send("#{ method }_without_ec", *args, &block).tap { |result| after(result) if @check_ec }

            #if you want to actually return the return value of calling after:
            #result = send("#{ method }_without_ec")
            #@check_ec ? after(result) : result
          end
        end
      end
    end
  end
 end

class Abstract
  include ErrorCodeChecker

  def initialize(check_ec)
    @check_ec = check_ec
  end
end

class MyClass < Abstract

  def initialize(name, some_stuff, check_error_code)
    # do some stuff...
    @name = name
    super(check_error_code)
  end
  def my_method_a # execute after() after this method
    {ec: 0}
  end   
  def my_method_b # execute after() after this method
    {ec: 7}
  end
  def has_valid_params # don't execute after() on this method
    true
  end

  check_error_code except: :has_valid_params
  #or whitelisting:
  #check_error_code only: [:my_method_a, :my_method_b]
  #or both:
  #check_error_code only: :my_method_a, except: [:has_valid_params, dont_check_this_one]
end 
于 2013-08-22T00:33:49.783 回答
0

使用单例类的解决方案。

class MyClass
  def initialize(name, some_stuff)
   # do some stuff...
    @name = name
  end
  def my_method_a # execute after() after this method
    return {EC: 0}
  end
  def my_method_b() # execute after() after this method
    return {EC: 7}
  end
  def has_valid_params() # don't execute after() on this method
    return true
  end
end

module ErrorCodeChecker
  def after(result) # this is the method that I'd like to be added to most methods
    puts "ERROR CODE: #{result[:EC]}"
  end

  def addErrorCodeCheck(exclude = [])
    methods = self.class.superclass.public_instance_methods(false) - exclude
    class << self
      self
    end.class_exec {
      methods.each {|method|
        define_method(method) {|*p|
          super(*p).tap {|res| after(res)}
        }
      }
    }
  end
end

class MyClassEC < MyClass
  include ErrorCodeChecker

  def initialize(name, some_stuff, check_error_code, exclude = [])
    super name, some_stuff
    addErrorCodeCheck(exclude) if check_error_code
  end
end

'addErrorCodeCheck' 打开 MyClassEC 实例的单例类,并重新定义不在排除列表中的 MyClass 实例方法。重新定义的方法隐藏了原始方法,但在调用 'after' 之前通过内部的 'super' 方法调用它们。

如果需要,您可以稍后重复应用“addErrorCodeCheck”。

执行示例:(在 Ruby 1.9.3 中测试)

my = MyClassEC.new('test', 'abc', true, [:has_valid_params])

my.my_method_a    # => ERROR CODE: 0
my.my_method_b    # => ERROR CODE: 7
my.has_valid_params    # => (nothing)
于 2013-08-22T02:35:03.847 回答