【问题标题】:Metaprogramming: method that works like "def"元编程:类似于“def”的方法
【发布时间】:2023-03-03 19:14:02
【问题描述】:

我想为一个类对象定义一个单例方法,它的工作原理与def 类似,但也可以做一些其他的事情。继承类可以像 def 一样使用该方法,除了使用方法名称而不是 def 之外,不需要任何其他特殊语法。

一个具体的例子会有所帮助。在我正在开发的代码中,我想将某些方法标记为“alpha”或“beta”。这样,您可以查询类对象的哪些方法是正常的、哪些 alpha 和哪些 beta。这是我目前的做法:

# Person
class Person
  def self.states
    if not instance_variable_defined?(:@states)
      @states = {}
     end

     return @states
  end

  def self.set_state(method_name, state)
    states[method_name] = state
  end
end

# Guest
class Guest < Person
  set_state 'sender', 'alpha'

  def sender(param1, param2)
    # do stuff
  end
end

puts Guest.states

最后一行输出

{:sender=>"alpha"}

好的,这行得通。但我真正想要的是能够调用一个像def 一样工作的方法,但它可以将一个方法标记为 alpha 或 beta。所以 Guest 类可能看起来像这样:

class Guest < Person
  alpha sender(param1, param2)
    # do stuff
  end
end

我宁愿不使用块,如下所示。撇开范围和其他问题不谈,它只是感觉不像是在定义一种方法。

alpha('sender') do |param1, param2|
  # do stuff
end

有没有办法对这样的事情进行元编程?

【问题讨论】:

  • 我几乎可以肯定,没有块 / proc 就没有办法做到这一点

标签: ruby


【解决方案1】:

您不能完全按照您的意愿行事,因为alpha sender(...) 会尝试将 sender 的值传递给 alpha 方法。这显然行不通。

但是,方法定义确实返回已定义方法的符号名称,然后您可以将其传递给您的 alpha 方法:

class Person
  def self.alpha(method_name)
    set_state method_name, "alpha"
  end
  # ...
end

class Guest < Person
  alpha def sender(param1, param2)
    # do stuff
  end
end

这将定义sender 并调用alpha(:sender),然后适当地调用set_state

【讨论】:

    【解决方案2】:

    您可以通过模拟public/private/protected 来代替尝试使用def 执行此操作,例如:

    module MethodState
      def states
        @states ||= {}
      end
    
      def current_state
        @current_state ||= :normal
      end
      private :current_state
    
      [:alpha, :beta, :normal].each do |method_state|
        define_method method_state do |*method_names|
          if method_names.empty?
            @current_state = method_state
          else
            method_names.each { |name| states[name] = method_state }
          end
        end
      end
    
      def method_added(method_name)
        states[method_name] = current_state
      end
    
      def self.extended(base)
        # all already defined methods are 'normal' state methods
        base.normal *base.instance_methods(false)
      end
    end
    
    class Guest
      def normal_method_one
      end
    
      def normal_method_two
      end
    
      extend MethodState
      alpha
      def sender(p1, p2)
      end
    
      beta
      def a_beta_method
      end
    
      def another_alpha_method
      end
      alpha :another_alpha_method
    
      normal
      def one_last_normal
      end
    end
    
    Guest.states # => {:normal_method_two=>:normal,
    # :normal_method_one=>:normal, :sender=>:alpha,
    # :a_beta_method=>:beta, :another_alpha_method=>:alpha,
    # :one_last_normal=>:normal}
    

    【讨论】:

      猜你喜欢
      • 2013-07-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-10-04
      • 2022-01-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多