如何检测Ruby中方法链的结束

时间:2021-06-11 00:07:30

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界面,我刚才写过,其中一部分困扰我,我想让它变得更好。它的工作方式是我使用缺少的方法从flickr对象上调用的方法构造flickr调用的url参数,例如。

@flickr.groups.pools.getPhotos(:user_id => "12656878@N06", :group_id => "99404851@N00")

These 'method calls' construct an api call that looks like this

这些'方法调用'构造一个看起来像这样的api调用

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.

(我已经离开了api密钥位)它通过记住每个'方法'来做到这一点,直到用参数调用方法,此时它构造url并调用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.

我采用这种方法的原因是ruby代码与Flickr网站上的文档匹配,你可以复制并粘贴它,它将主要工作,并希望它会使api更改更有弹性,因为我没有硬编码方法。

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.

令人恼火的是,在这个例子中,组ID被传递给getPhotos方法而不是groups方法。我宁愿它看起来像这样。

@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?

所以我的问题。在Ruby中是否有任何方法可以检测到最后一个方法已被调用,以便我可以触发对Flickr的调用?

1 个解决方案

#1


6  

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.

由于我想不出任何方法来检测最后一个方法调用,我将建议采用不同的方法。它类似于ActiveRecord的功能;一个代理类,它构建选项,并且在您调用对数据进行操作的方法之前不会获取数据。

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

它只会在您获取数据或映射数据时提取数据(任何数组方法)。

#1


6  

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.

由于我想不出任何方法来检测最后一个方法调用,我将建议采用不同的方法。它类似于ActiveRecord的功能;一个代理类,它构建选项,并且在您调用对数据进行操作的方法之前不会获取数据。

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

它只会在您获取数据或映射数据时提取数据(任何数组方法)。