您好我正在尝试创建一个帮助程序,用于将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
我可能出错的任何想法?
答案 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
来定义匿名模块中的方法,使用模块在模块中private
和extend
创建每个实例方法:
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
基本组件是匿名模块,因此您有一个(临时)容器来定义方法。