【问题标题】:Define Method Based on Existing Method in Ruby基于 Ruby 中现有方法定义方法
【发布时间】:2015-03-02 19:43:22
【问题描述】:

我正在尝试元编程一种基于谓词方法定义爆炸方法的方法。现在我有我想要使用method_missing的行为:

class PredicateBang
  def true?
    true
  end

  def false?
    false
  end

  def method_missing(method, *args, &block)
    if bang_match = /\A([^?]+)!\z/.match(method.to_s)
      predicate_method = :"#{bang_match[1]}?"
      if respond_to?(predicate_method)
        unless send(predicate_method, *args, &block)
          raise "#{predicate_method} is false"
        end
        return
      end
    end
    super.method_missing(method, *args, &block)
  end
end

PredicateBang.new.true!
PredicateBang.new.false! # false? is false (RuntimeError)

但是,我不想覆盖method_missing,而是想通过迭代instance_methods(false) 并使用define_method 为任何以问号结尾并带有匹配参数的方法创建一个bang 方法来动态定义这些方法,但我不确定如何反思这些方法的所有细节。

Method#parameters 似乎是一个不错的第一步,但我不确定如何将其转换为阻止参数或处理默认值。

【问题讨论】:

    标签: ruby metaprogramming


    【解决方案1】:

    无需使用Method#parameters,您可以使用*args 来捕获正在传递的参数(包括默认值)并使用&block 样式参数来传递任何块,就像使用method_missing 一样。所以一个基本的例子是

    define_method(bang_method) do |*args, &block|
      unless send(predicate_method, *args, &block)
        raise "#{predicate_method} is false"
      end
    end
    

    现在要添加一个方法add_bang_methods(clazz),它将为类上的每个谓词添加一个bang方法,您可以使用instance_methods来获取方法列表,并使用class_eval在类的上下文。所以大致的轮廓是:

    def add_bang_methods(clazz)
      clazz.instance_methods.each do |method_name|
        if predicate_match = /\A([^?]+)\?\z/.match(method_name.to_s)
          bang_method = :"#{predicate_match[1]}!"
          clazz.class_eval do
            define_method(bang_method) do |*args, &block|
              unless send(method_name, *args, &block)
                raise "#{method_name} is false"
              end
            end
          end
        end
      end
    end
    

    【讨论】:

    • 超级酷。我从不知道您可以将 splat 作为块参数传递。我猜当时没有办法反映默认值?
    • SomeClass.instance_method(:method_name).parameters 将为您提供该方法名称的参数列表,包括哪些是必需的以及哪些是可选的详细信息,但它不会为您提供默认值。
    • 我最终将此用例演变成我第一个发布的 gem,Interrobang。感谢您的帮助!
    猜你喜欢
    • 1970-01-01
    • 2012-10-21
    • 2016-06-15
    • 2013-09-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多