2017-04-20 99 views
5

嗨我想创建一个帮助群体定义ruby方法作为私人类方法。一般来说,可以使用private_class_method关键工作将方法定义为私有类方法。但我想在下面的样式来创建一个帮手:ruby​​私人类方法帮手

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

这很混乱。我期望在一个类上调用'#private_class_methods'将给我一个与'#private_instance_methods'类似的私有类方法列表。但是,为什么要这样做呢?为了更好地理解Ruby的机制或实际使用它? – ndn

+0

更多关于理解私有类方法如何工作的机制以及如何在我的代码中使用。如果命名不好,也许我们可以将该方法重命名为define_private_class_methods。 –

回答

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 

的重要组成部分是匿名的模块,让您拥有一个(暂时的)容器定义中的方法。

+0

This是迄今为止最好的答案,唯一的缺点是盲目扩展(mod)可能有害......那么这种方法没有缺点:) – mudasobwa

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

请注意,这是很难理解,你想实现什么,可能有更好,更清洁,更可靠的方法实现这一功能。

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 

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

+0

“语义更正确”是指它产生一个由数百个元素组成的临时数组而不是几个? :) – mudasobwa

+0

@mudasobwa,我没有看到数组大小的差异,除了你已经过滤私人方法。我也可以做到这一点,但除此之外。重点是你想为类定义一些神奇的方法。你应该修改'Class',而不是'Object'。 – ndn

+0

如果您想用私人方法覆盖现有方法,这将不起作用。 –