ruby私有类方法助手

时间:2017-04-20 11:26:35

标签: ruby metaprogramming

您好我正在尝试创建一个帮助程序,用于将ruby方法定义为私有类方法。通常,可以使用private_class_method键工作将方法定义为私有类方法。但我想以下列风格创建一个帮助器:

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

我可能出错的任何想法?

3 个答案:

答案 0 :(得分:5)

<强> $ 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>'

请注意,它很难理解,您正在尝试实现的目标,并且可能有更好,更清晰,更强大的方法来实现此功能。

答案 1 :(得分:5)

与@ 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

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

答案 2 :(得分:4)

您可以通过将块传递给Module.new来定义匿名模块中的方法,使用模块在模块中privateextend创建每个实例方法:

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

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