在运行中确定方法的可见性

Ale*_*x B 5 ruby

我正在编写一个方法来定义类中的实例方法; 与attr_accessor类似的东西:

class Foo
  custom_method(:foo)
end
Run Code Online (Sandbox Code Playgroud)

我已经通过将custom_method函数添加到Module模块,并使用define_method定义方法来实现,这可以正常工作.但我无法弄清楚如何从课程中考虑可见性属性.例如,在以下类中

class Foo
  custom_method(:foo)
private
  custom_method(:bar)
end
Run Code Online (Sandbox Code Playgroud)

第一个生成的方法(foo)必须是公共的,第二个(bar)必须是私有的.我怎么做?或者,如何找到调用custom_method的上下文:private,public或protected?

谢谢!

Aje*_*i32 7

经过一番实验,我完全感到困惑.最初,我认为Ruby在您调用时会考虑默认的可见性(公共,私有或受保护)Module#define_method.事实证明,在Ruby版本<= 2.0上,情况并非如此:

class Foo
  private
  define_method :foo do
    puts "Foo called!"
  end
end

Foo.new.foo # Prints "Foo called!"
Run Code Online (Sandbox Code Playgroud)

在Ruby 2.1+上,它更令人困惑.Module#define_method似乎考虑了默认方法可见性:

class Foo
  private
  define_method :foo do
    puts "Foo called!"
  end
end

Foo.new.foo # NoMethodError: private method `foo' called for #<Foo:0x8cb75ac>
Run Code Online (Sandbox Code Playgroud)

但它只适用define_method于直接在班级内部打电话的情况.调用然后调用的方法define_method不起作用:

class Foo
  def self.hello_on name
    define_method name do
      puts "Hello, #{name}!"
    end
  end

  private
  hello_on :foo
end

Foo.new.foo # Prints "Hello, foo!"
Run Code Online (Sandbox Code Playgroud)

把它当作Ruby!为什么?

好的,这需要绝望的措施......

module DefaultMethodVisibilityAccessor
  attr_reader :current_default_method_visibility

  def public(*args)
    @current_default_method_visibility = :public if args.empty?
    super
  end
  def protected(*args)
    @current_default_method_visibility = :protected if args.empty?
    super
  end
  def private(*args)
    @current_default_method_visibility = :private if args.empty?
    super
  end
end

class Module
  prepend DefaultMethodVisibilityAccessor
end

module MethodDefiner
  def hello_on name
    define_method name do
      puts "Hello, #{name}!"
    end

    case current_default_method_visibility
    when :public
      public name
    when :protected
      protected name
    when :private
      private name
    end
  end
end
Run Code Online (Sandbox Code Playgroud)

用法:

class Foo
  extend MethodDefiner
  hello_on :foo
  private
  hello_on :bar
end

Foo.new.foo # Prints "Hello, foo!"
Foo.new.bar # NoMethodError: private method `bar' called for #<Foo:0x8ec18fc>
Run Code Online (Sandbox Code Playgroud)

那里,修好了!


Leo*_*sov 2

我认为这是不可能的,因为设置的范围可见性级别Module.private是在C虚拟机级别管理的,而不是暴露给Ruby的。

编辑:它仅在调用它的相同语法范围内可用,因此当您调用它时,custom_method它会丢失类声明中设置的可见性级别。

它在set_visibility()中设置,并在vm_define_method()中使用,但我找不到任何对 Ruby 中可用的相应变量的引用。

我建议使用某种自定义参数来指定方法的可见性级别。