2009-08-26 9 views
6

J'ai une interface Flickr que j'ai écrite il y a un certain temps et une partie de celle-ci me dérange et j'aimerais la rendre plus agréable. La façon dont cela fonctionne est que j'utilise la méthode manquant pour construire les paramètres url pour l'appel flickr à partir des méthodes appelées sur l'objet flickr, par exemple.Comment détecter la fin d'une chaîne de méthode dans Ruby

@flickr.groups.pools.getPhotos(:user_id => "[email protected]", :group_id => "[email protected]") 

Ces « méthode appelle » construire un appel api qui ressemble à ceci

http://api.flickr.com/services/rest/?method=groups.pools.getPhotos&user_id=1848466274& group_id= [email protected] 

(j'ai laissé hors panneton api) Il le fait en se rappelant chaque « méthode » jusqu'à ce qu'une méthode est appelé avec des arguments à ce moment, il construit l'URL et fait l'appel à Flickr. La raison pour laquelle j'ai pris cette approche est que le code ruby ​​correspond à la documentation sur le site Flickr, vous pouvez le copier et le coller et il fonctionnera surtout, et j'espère qu'il le rendra un peu plus résistant aux changements d'API parce que je n'ont pas de méthodes codées en dur. Ce qui irrite cependant, c'est que dans cet exemple, l'identifiant du groupe est passé à la méthode getPhotos plutôt qu'à la méthode groups. Je préférerais que ça ressemble à ça.

@flickr.groups(:group_id => "[email protected]").pools.getPhotos(:user_id => "[email protected]") 

Donc ma question. Y a-t-il moyen dans Ruby que je puisse détecter que la dernière méthode a été appelée pour que je puisse déclencher l'appel à Flickr?

Répondre

6

Puisque je ne peux pas trouver un moyen de détecter le dernier appel de méthode, je vais suggérer une approche différente. C'est semblable à ce que fait ActiveRecord; une classe proxy qui construit les options et ne récupère pas les données avant d'appeler une méthode qui fonctionne sur les données.

class Flickr 
    def initialize 
    @result = FlickrResult.new 
    end 

    def method_missing(method, *args, &block) 
    if @result.data.respond_to?(method) 
     @result.run(method, args, block) 
    else 
     @result.append(method, args[0]) 
     return self 
    end 
    end 

    class FlickrResult 
    attr_reader :data 

    def initialize 
     @data = [] 
     @keys = [] 
     @options = {} 
    end 

    def append(key, options) 
     @keys << key 
     @options.merge!(options) if options 
    end 

    def run(method, args, block) 
     if [email protected]_run 
     fetch_data 
     end 

     @data.send(method, *args, &block) 
    end 

    def fetch_data 
     puts "Only runs once!" 
     @url = @keys.join(".") + "?" + @options.map {|k, v| "#{k}=#{v}" }.join("&") 
     # use @url and fetch data.. 
     @data = ["foo", "bar"] 

     @did_run = true 
    end 
    end 
end 

@flickr = Flickr.new 
@flickr.groups(:group_id => "123").pools.thing.users(:user_id => "456") 

@flickr.each {|f| p f } 
# => "Only runs once!" 
# => "foo" 
# => "bar" 
p @flickr.map {|f| f.upcase } 
# => ["FOO", "BAR"] 

Il ne récupèrera que les données lorsque vous each ou map ou tout (toute méthode de tableau).

+0

Pourquoi "@flickr" dans la portée externe? Quel est l'avantage d'en faire une variable d'instance d'Objet par rapport à une variable locale? –

+0

Aucun avantage (dans ce cas). Juste un brainfart, aucune considération particulière :) –

+0

Ouais, c'était une méthode alternative que j'avais regardée. Et en utilisant memoization sur les attributs. J'espérais qu'il y avait une méthode funky dont je ne suis pas au courant. – Henry

Questions connexes