How to detect the end of a method chain in Ruby

梦想的初衷 提交于 2019-12-20 14:12:44

问题


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?


回答1:


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).



来源:https://stackoverflow.com/questions/1332404/how-to-detect-the-end-of-a-method-chain-in-ruby

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!