2017-04-20 40 views
5

Merhaba Özel sınıf yöntemleri olarak yakut yöntemlerini toplu tanımlama için bir yardımcı oluşturmaya çalışıyorum. Genel olarak private_class_method anahtar çalışmasını kullanarak özel bir yöntem olarak bir yöntem tanımlayabilir. Ama şu tarzda bir yardımcı oluşturmak istiyorum:ruby ​​özel sınıf yöntem yardımcısı

class Person 
    define_private_class_methods do 
    def method_one 
    end 

    def method_two 
    end 
    end 
end 

Dinamik bu hiç çalışmıyor, hangi şu şekilde olduğunu tanımlamak için planlanan yol:

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

herhangi bir fikir Nerede yanlış gidebilirim?

+0

Bu kafa karıştırıcıdır. Bir sınıfta '# private_class_methods''ı çağırmanın, # private_instance_methods’a benzer şekilde özel sınıf yöntemlerinin bir listesini vermesini beklerdim. Ama bunu bir kenara koyup, neden bunu yapmak istiyorsun? Ruby'nin mekaniğini daha iyi anlamak için mi yoksa genel olarak mı kullanıyorsun? Özel sınıf yöntemlerinin nasıl çalıştığı ve kodumda nasıl kullanılacağının anlaşılması hakkında – ndn

+0

daha fazla bilgi edinin. Adlandırma bozuksa, belki de method_private_class_methods yöntemini yeniden adlandırabiliriz. –

cevap

4

Sen Module.new için blok ileterek anonim modülde yöntemleri tanımlamak private ve extend sınıf modülü ile modülünde her bir örneği yöntemini yapabiliriz:

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 

Bu istenen sonucu vardır:

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) 

... ve bonus olarak, aynı zamanda size super yöntemi sunar:

(muhtemelen az kullanılmaktadır)

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 

temel bileşeni anonim modül, yani bir (geçici) kapsayıcı: Elbette

class Person 
    def self.method_one 
    super * 2 
    end 
end 

Person.method_one 
#=> 456 

, az önce de elle yöntemleri tanımlayabilir, extend kullanmak zorunda değilsiniz Yöntemleri tanımlamak için:

+0

Şimdiye kadarki en iyi cevap budur: Tek dezavantaj kör genişletme (mod) 'nin potansiyel olarak zararlı olmasıdır ... Peki, bu yaklaşımda hiçbir sakınca yoktur :) – 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>' 

size ulaşmak için ne çalışıyorsunuz, bunu anlamak zor olduğunu unutmayın ve muhtemelen, daha temiz ve daha sağlam bir yol daha iyi olduğunu lütfen Bu işlevselliği elde etmek için. @ Mudasobwa cevabı

5

benzer, fakat farklı (ve anlamsal olarak daha doğru IMHO):

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 

Not: Bu, o anda üzerine yazıyorsunuz Sınıf yöntemi erişilebilirliğini değişmez.

+0

“Semantik olarak daha doğru” ifadesiyle, birkaç yerine yüzlerce elemanın geçici bir dizisini yarattığı anlamına mı geliyor? :) – mudasobwa

+0

@mudasobwa, zaten özel yöntemleri filtrelediğinizden farklı bir dizi boyutunda farkı göremiyorum. Bunu da yapabilirim, ama bunun yanında.Asıl nokta, sınıflar için bazı sihirli yöntemleri tanımlamaktır. Nesne değil, 'Class' değiştirmelisiniz. – ndn

+0

Varolan bir yöntemin üzerine özel bir yöntemle yazmak istiyorsanız bu işe yaramaz. –