Ruby 检查时,我可以在类中获得传递给attr_访问器的符号列表吗?

Ruby 检查时,我可以在类中获得传递给attr_访问器的符号列表吗?,ruby,class,metaprogramming,Ruby,Class,Metaprogramming,假设我定义了一个类,该类具有使用attr\u accessor定义的访问器: 是否有一个内置方法提供传递给attr_访问器调用的方法名列表?或者我必须用符号定义一个常量,并将其传递给attr\u accessor吗?Grep Setter方法的实例 一种方法是为setter grep类的实例。例如: A.new.methods.grep(/\p{alnum}+=\z/) #=> [:alpha=, :beta=, :gamma=] Grep Setter方法的实例 一种方法是为sette

假设我定义了一个类,该类具有使用attr\u accessor定义的访问器:

是否有一个内置方法提供传递给attr_访问器调用的方法名列表?或者我必须用符号定义一个常量,并将其传递给attr\u accessor吗?

Grep Setter方法的实例 一种方法是为setter grep类的实例。例如:

A.new.methods.grep(/\p{alnum}+=\z/)
#=> [:alpha=, :beta=, :gamma=]
Grep Setter方法的实例 一种方法是为setter grep类的实例。例如:

A.new.methods.grep(/\p{alnum}+=\z/)
#=> [:alpha=, :beta=, :gamma=]

不,那是不可能的。由attr_访问器、attr_读取器和attr_编写器生成的方法与手工编写的方法无法区分。事实上,他们一定是无法区分的手写

比如说,您有一个简单的attr_访问器,但稍后您希望对其进行重构以实现更智能的功能,例如缓存。这是一个纯粹的内部更改,客户端不能观察到差异,否则将违反封装

如果您只是想要一个setter列表,这很简单:setter是名称以=符号结尾的方法:

对于getter来说,这更为棘手:任何不接受参数的方法都可能是getter,但也可能是副作用方法,例如:


不,那是不可能的。由attr_访问器、attr_读取器和attr_编写器生成的方法与手工编写的方法无法区分。事实上,他们一定是无法区分的手写

比如说,您有一个简单的attr_访问器,但稍后您希望对其进行重构以实现更智能的功能,例如缓存。这是一个纯粹的内部更改,客户端不能观察到差异,否则将违反封装

如果您只是想要一个setter列表,这很简单:setter是名称以=符号结尾的方法:

对于getter来说,这更为棘手:任何不接受参数的方法都可能是getter,但也可能是副作用方法,例如:


没有内置的方法。只要您事先知道并能够控制方法名称,在创建时存储方法名称的解决方案就可以工作

在对的回答中,我演示了如何在事后使用跟踪点动态获取方法的名称。我在下面更新了它,包括:attr\u reader和:attr\u writer

我已经将方法名存储在Methods常量中,但是很明显,您可以在最方便的地方存储它们


定义/删除MethodTracer上添加的方法确保不会破坏您自己定义的任何添加的Foo.method。但是,这种方法确实要求,如果在调用attr_*之前定义Foo.method_,则需要在其中调用super。否则,您将跳过MethodTracer定义的添加的临时方法。

没有内置方法。只要您事先知道并能够控制方法名称,在创建时存储方法名称的解决方案就可以工作

在对的回答中,我演示了如何在事后使用跟踪点动态获取方法的名称。我在下面更新了它,包括:attr\u reader和:attr\u writer

我已经将方法名存储在Methods常量中,但是很明显,您可以在最方便的地方存储它们


定义/删除MethodTracer上添加的方法确保不会破坏您自己定义的任何添加的Foo.method。但是,这种方法确实要求,如果在调用attr_*之前定义Foo.method_,则需要在其中调用super。否则,您将跳过MethodTracer定义的临时方法。

您的意思是@alpha吗?@zishe否,OP可能表示self.alpha=1。用符号定义常量并将其传递给attr\u访问器是什么意思?请澄清,最好是用一个包含期望结果的例子。我建议重新打开这个,因为提供的答案是正确的。我想做的是不可能的,@JörgWMittag在回答中解释得很好。你是说@alpha吗?@zishe不,OP可能意味着self.alpha=1。你用符号定义常量并将其传递给attr_访问器是什么意思?请澄清,最好是用一个包含期望结果的例子。我建议重新打开这个,因为提供的答案是正确的。我想做的是不可能的,@JörgWMittag在回答中很好地解释了这一点。这不一定会产生正确的名称,因为可以手动定义setter。谁知道这是否会对op产生影响?这不一定会产生正确的名称,因为可以手动定义setter。这是否会对op产生影响,谁知道呢?这确实是一个令人信服的解决方案。非常感谢你发布它。我把你的答案改为正确答案。这真是一个令人信服的解决方案。非常感谢你的来信 把它弄得乱七八糟。我把你的答案改成正确的。
A.public_instance_methods(false).grep(/=$/)
# => [:alpha=, :beta=, :gamma=]
A.public_instance_methods(false).select {|m| 
  A.public_instance_method(m).arity.zero? 
}
# => [:alpha, :beta, :gamma]
module MethodTracer
  TracePoint.trace(:c_call) do |t|
    if %i[attr_accessor attr_writer attr_reader].include?(t.method_id)
      t.self.extend(MethodTracer)

      methods = t.self::Methods ||= []
      MethodTracer.send(:define_method, :method_added) {|m| methods << m }
    end
  end

  TracePoint.trace(:c_return) do |t|
    if %i[attr_accessor attr_writer attr_reader].include?(t.method_id)
      MethodTracer.send(:remove_method, :method_added)
    end
  end
end

class Foo
  attr_accessor :a
  attr_reader :b
  attr_writer :c

  def foo; end
end

Foo::Methods # => [:a, :a=, :b, :c=]