ruby 私有 class 方法助手

ruby private class method helper

您好,我正在尝试创建一个帮助程序,用于将 ruby 方法定义为私有 class 方法。通常,可以通过使用 private_class_method 键工作将方法定义为私有 class 方法。但我想创建一个以下样式的助手:

class Person
  define_private_class_methods do 
    def method_one
    end

    def method_two
    end
  end
end

我计划动态定义它的方式如下,根本不起作用:

class Object
  def self.define_private_class_methods &block
    instance_eval do
      private
      &block
    end
  end
end

有什么我可能出错的想法吗?

$ cat /tmp/a.rb

class Object
  def self.define_private_class_methods &cb
    existing = methods(false)
    instance_eval &cb
    (methods(false) - existing).each { |m| singleton_class.send :private, m }
  end
end

class Person
  define_private_class_methods do 
    def method_one
            puts "¡Yay!"
    end
  end
end

Person.send(:method_one)
Person.public_send(:method_one)

$ ruby /tmp/a.rb

¡Yay!

/tmp/a.rb:18:in `public_send': private method `method_one' 
                 called for Person:Class (NoMethodError)
Did you mean?  method
    from /tmp/a.rb:18:in `<main>'

请注意,很难理解您要实现的目标,可能有更好、更简洁、更可靠的方法来实现此功能。

与@mudasobwa 的回答相似但不同(语义上更正确恕我直言):

class Class
  def define_private_class_methods(&definition)
    class_methods_prior = methods

    singleton_class.class_eval(&definition)

    (methods - class_methods_prior).each do |method_name|
      private_class_method method_name
    end
  end
end

class Person
  define_private_class_methods do 
    def method_one
      1
    end
  end
end

Person.method_one # !> NoMethodError: private method `method_one' called for Person:Class
Person.send :method_one # => 1

注意:它不会改变您当前覆盖的class方法的可访问性。

您可以通过将块传递给 Module.new, make each instance method in the module private and extend 您的 class 模块来定义匿名模块中的方法:

class Class
  def define_private_class_methods(&block)
    mod = Module.new(&block)
    mod.instance_methods.each { |m| mod.send(:private, m) }
    extend(mod)
  end
end

这有预期的结果:

class Person
  define_private_class_methods do 
    def method_one
      123
    end
  end
end

Person.send(:method_one)
#=> 123

Person.method_one
#=> private method `method_one' called for Person:Class (NoMethodError)

... 作为奖励,它还提供了一个 super 方法:(可能用处不大)

class Person
  def self.method_one
    super * 2
  end
end

Person.method_one
#=> 456

当然,你不必使用extend,你也可以手动定义方法:

class Class
  def define_private_class_methods(&block)
    mod = Module.new(&block)
    mod.instance_methods.each do |m|
      define_singleton_method(m, mod.instance_method(m))
      private_class_method(m)
    end
  end
end

基本组件是匿名模块,因此您有一个(临时)容器来定义其中的方法。