2015-08-26 12 views
11

Bir geliştiricinin, sınıf tanımında (normal acts_as desenini izleyerek) basit bir bildirimle bir sınıfa çeşitli özellikler eklemesine olanak tanıyan bir eklenti yapıyorum. parametresi bir yöntem olarak var specific_method_to_use, ama yol kodu genellikle: Örneğin, eklenti tüketen kod ı için sağlanan değer olmadığını kontrol hata istiyorum, çünkü benim soru ortaya çıkıyorBir Ruby sınıf tanımının sonunda kodu çalıştırmak için kanca nasıl ayarlayabilirim?

class YourClass 
    consumes_my_plugin option1: :value1, specific_method_to_use: :your_method 
end 

gibi

görünebilir organize ve yüklü, yöntem henüz mevcut değil.

benim eklenti kod geçici şuna benzer:

module MyPlugin 
    extend ActiveSupport::Concern 

    module ClassMethods 
    def consumes_my_plugin(options = {}) 
     raise ArgumentError.new("#{options[:specific_method_to_use]} is not defined") if options[:specific_method_to_use].present? && !self.respond_to?(options[:specific_method_to_use]) 
    end 
    end 
end 

Bu çalışacak:

class YourClass 
    def your_method; true; end 

    consumes_my_plugin option1: :value1, specific_method_to_use: :your_method 
end 

Ama bu insanların çoğu kod yazmak nasıl olduğunu ve olmaz:

class YourClass 
    consumes_my_plugin option1: :value1, specific_method_to_use: :your_method 

    def your_method; true; end 
end 

YourClass yükleme süresinde nasıl başarısız olabilirim? Hata yapmak istiyorum, NoMethodError ile çalışma zamanında değil. Tüm sınıf yüklenene kadar ArgumentError'u yükselten satırın yürütülmesini erteleyebilir miyim, yoksa bunu başarmak için başka bir şey yapamaz mıyım?

+1

Eğer 'specific_method_to_use' çağrılan olurdu noktada çeki yapabilir sınıf yükleme zamanında kontrol yapmak yerine? –

+0

: specific_method_to_use yalnızca çalışma zamanında çağrılır, bu yüzden NoMethodError'ı yakalayabilir ve etrafındaki geliştiriciye yapıcı mesajlar verebilsem de, hata sadece yükleme zamanında değil, çalışma zamanında yakalanırdı ... ikincisi tercih edilebilir. – LikeMaBell

+1

'self.respond_to?' Kullanarak yaptığınız kontrol, "YourClass # your_method" örnek yöntemini kontrol etmeyecektir; Tek sınıf/sınıf yöntemi 'YourClass.your_method' için arayacaktır. Cevabımda, onu "self.instance_methods.include" olarak değiştirdim. 'Response_to'ya geri dönmekten çekinmeyin?Eğer niyetini yanlış anladım. –

cevap

5

Sınıfınızın :end olayını ne zaman göndereceğini takip etmek için TracePoint numaralı telefonu kullanın. Sorunun

için


Özgül çözüm Burada önceden var olan modül doğrudan TracePoint sığabilecek nasıl. Aşağıda

require 'active_support/all' 

module MyPlugin 
    extend ActiveSupport::Concern 

    module ClassMethods 
    def consumes_my_plugin(**options) 
     m = options[:specific_method_to_use] 

     TracePoint.trace(:end) do |t| 
     break unless self == t.self 

     raise ArgumentError.new("#{m} is not defined") unless instance_methods.include?(m) 

     t.disable 
     end 
    end 
    end 
end 

örnekler belirtilen gibi çalıştığını göstermektedir:

# `def` before `consumes`: evaluates without errors 
class MethodBeforePlugin 
    include MyPlugin 
    def your_method; end 
    consumes_my_plugin option1: :value1, specific_method_to_use: :your_method 
end 

# `consumes` before `def`: evaluates without errors 
class PluginBeforeMethod 
    include MyPlugin 
    consumes_my_plugin option1: :value1, specific_method_to_use: :your_method 
    def your_method; end 
end 

# `consumes` with no `def`: throws ArgumentError at load time 
class PluginWithoutMethod 
    include MyPlugin 
    consumes_my_plugin option1: :value1, specific_method_to_use: :your_method 
end 

Genel çözüm Bu modül herhangi sınıfta bir self.finalize geri arama oluşturmanızı sağlayacak

. Şimdi

module Finalize 
    def self.extended(obj) 
    TracePoint.trace(:end) do |t| 
     if obj == t.self 
     obj.finalize 
     t.disable 
     end 
    end 
    end 
end 

Eğer sınıfını genişletmek ve sınıf tanımı biter en kısa sürede çalışır self.finalize, tanımlayabilirsiniz:

class Foo 
    puts "Top of class" 

    extend Finalize 

    def self.finalize 
    puts "Finalizing #{self}" 
    end 

    puts "Bottom of class" 
end 

puts "Outside class" 

# output: 
# Top of class 
# Bottom of class 
# Finalizing Foo 
# Outside class 
+0

Bu, tam olarak aradığım cevap gibi görünüyor. Teşekkür ederim. Size tam ödül veremediğim için özür dilerim - ödül kazanma döneminde seyahat ediyordum ve bana tam miktarı vermeme izin vermiyor gibi görünüyor. – LikeMaBell

İlgili konular