2010-05-25 6 views
0

Essentiellement, je me demande comment placer callbacks sur des objets en rubis, de sorte que lorsqu'un objet est changé en tout cas je peux déclencher automatiquement d'autres changements:Ruby: ayant callbacks sur « attr » objets

(EDIT: Je comme @proxy est un objet URI il a ses propres méthodes, changer l'objet URI en utilisant ses propres méthodes n'appelle pas ma propre méthode proxy= et met à jour l'objet @http)

class MyClass 
    attr_reader :proxy 
    def proxy=(string_proxy = "") 
    begin 
     @proxy = URI.parse("http://"+((string_proxy.empty?) ? ENV['HTTP_PROXY'] : string_proxy)) 
     @http = Net::HTTP::Proxy.new(@proxy.host,@proxy.port) 
    rescue 
     @http = Net::HTTP 
    end 
    end 
end 

m = MyClass.new 
m.proxy = "myproxy.com:8080" 
p m.proxy 
# => <URI: @host="myproxy.com" @port=8080> 

m.proxy.host = 'otherproxy.com' 
p m.proxy 
# => <URI: @host="otherproxy.com" @port=8080> 
# But accessing a website with @http.get('http://google.com') will still travel through myproxy.com as the @http object hasn't been changed when m.proxy.host was. 
+2

Que voulez-vous dire « comme proxy = est pas appelé »? Bien sûr, ça s'appelle. – sepp2k

+0

Je ne comprends pas, vous obtenez 'Net :: HTTP' ...? Vous pouvez le vérifier si vous ajoutez un 'attr_reader: http' et inspectez' p m.http'. Que voulez-vous dire 'proxy =' n'est pas appelé quand 'm.proxy' est changé? Cet appel de fonction est la seule chose qui arrive - vous ne pouvez pas directement modifier les variables d'instance dans Ruby. – Amadan

+0

Ahh pantalons, je me suis confus dans mon exemple! La méthode 'proxy =' ne sera pas appelée si les objets fille de l'objet @proxy URI sont modifiés (voir les 4 dernières lignes de mon exemple de code) - espérons que cela a plus de sens? –

Répondre

0

J'ai réussi à comprendre celui-ci pour moi-même!

# Unobtrusive modifications to the Class class. 
class Class 
    # Pass a block to attr_reader and the block will be evaluated in the context of the class instance before 
    # the instance variable is returned. 
    def attr_reader(*params,&block) 
    if block_given? 
     params.each do |sym| 
     # Create the reader method 
     define_method(sym) do 
      # Force the block to execute before we… 
     self.instance_eval(&block) 
      # … return the instance variable 
      self.instance_variable_get("@#{sym}") 
     end 
     end 
    else # Keep the original function of attr_reader 
     params.each do |sym| 
     attr sym 
     end 
    end 
    end 
end 

Si vous ajoutez ce code quelque part, il va étendre la méthode attr_reader de sorte que si vous faites maintenant les suivantes:

attr_reader :special_attr { p "This happens before I give you @special_attr" } 

Il va déclencher le bloc avant qu'il vous donne la @special_attr. Il est exécuté dans la portée de l'instance de sorte que vous puissiez l'utiliser, par exemple, dans les classes où les attributs sont téléchargés depuis Internet. Si vous définissez une méthode comme get_details qui fait tout récupération et met @details_retrieved à true vous pouvez alors définir la attr comme ceci:

attr_reader :name, :address, :blah { get_details if @details_retrieved.nil? } 
0

Votre ligne m.proxy = nil déclenche une exception NoMethodError, puisque nil ne fait pas répondre à empty?. Ainsi, @http est défini sur Net::HTTP, comme dans la clause de sauvegarde.

Ceci n'a rien à voir avec les callbacks/setters. Vous devez modifier votre code pour faire ce que vous voulez (par exemple, appeler string_proxy.blank? si vous utilisez ActiveSupport).

+0

Désolé pour ça, j'ai réussi à me tromper et à poser la mauvaise question (j'ai simplifié mon code pour l'amener sur stackoverflow) - merci pour votre aide! –