2017-04-20 87 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

如果您想用私人方法覆蓋現有方法,這將不起作用。 –