I have a Flickr interface that I wrote a while ago and part of it bothers me and I'd like to make it nicer. The way it works is I use method missing to construct the url parameters for the flickr call from the methods called on the flickr object, eg.
@flickr.groups.pools.getPhotos(:user_id => "12656878@N06", :group_id => "99404851@N00")
These 'method calls' construct an api call that looks like this
http://api.flickr.com/services/rest/?method=groups.pools.getPhotos&user_id=1848466274& group_id= 99404851@N00
(I have left off the api key bit) It does this by remembering each 'method' until a method is called with arguments at which time it constructs the url and and makes the call to Flickr.
The reason I took this approach is so the ruby code matches the documentation on the Flickr site, you can copy and paste it and it will mostly work, and hopefully it will make it a bit more resilient to api changes because I have no hard coded methods.
What irritates about this though is that in this example the group id is being passed to the getPhotos method rather than the groups method. I would much rather it looked like this.
@flickr.groups(:group_id => "99404851@N00").pools.getPhotos(:user_id => "12656878@N06")
So my question. Is there any way in Ruby that I can detect that the last method has been called so that I can trigger the call to Flickr?
Since I can't think of any way to detect the last method call, I'm going to suggest a different approach. It is similar to what ActiveRecord does; a proxy class that builds the options and doesn't fetch the data until you call a method that operates on the data.
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 !@did_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"]
It only fetches the data when you each
or map
it or whatever (any array method).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With