包括使用Ruby的块丢失方法

时间:2014-05-22 07:40:44

标签: ruby-on-rails ruby

当我这样做调用API时,我得到了哈希。我狡猾地将Hash转换为具有某些对象的相同属性,即方法:D。

而不是item['name']我可以item.name。真棒!

以下是Twitter作为代理的一些代码:

module Twitter
  class Base
    include HTTParty

    base_uri 'api.twitter.com'
    format :json
    default_params api_password: ENV['TWITTER_PASSWORD']
  end

  refine Base::Hash do
    def method_missing(method, *args, &block)
      self.has_key?(method.to_s)? self[method.to_s] : super
    end

    def respond_to?(method, include_private = false)
      self.has_key?(method.to_s) || super
    end
  end
end

然后我可以制作推文模型:

module Twitter
  class Tweet < Twitter::Base
    class << self
      def all
        get '/v1.1/tweet'
      end
    end
  end
end

那很酷。这个例子不起作用。它是我真正想要做的一个非常愚蠢的版本:使哈希行为像一个对象。现在你知道发生了什么,让我们告诉你问题:

什么有效:

tweet.first['text']
# => 'this is the text from a hash.'

tweet.first.text
# => 'this is the text from a hash.'

tweet.first.respond_to? text
# => true

什么行不通:

tweet.map(&:text)
# => #<NoMethodError: undefined method `text' for #<Hash:0x007fb1322a1468>>

tweet.sort_by(&:created_at)
# => #<NoMethodError: undefined method `created_at' for #<Hash:0x007fb1322a1468>>

我的元编程大脑被炸了。我已经走到了这一步。有没有人对我可以做的其他事情有任何想法,以便使用#map#sort_by等方法来处理块?

更新

我决定了解#sort_by失败的原因。仍然无法回答这个问题,但这是我的调查:

tweet = Twitter::Tweet.all
tweet.sort_by(&:created_at)
# => undefined method `created_at' for #<Hash:0x007f9ae28ccfb0>

这增加了一些背景。现在找出tweet的上下文中的#sort_by

tweet.method(:sort_by)
# => #<Method: Array(Enumerable)#sort_by>

现在我们有更多的背景。

解决

考虑到Array是瓶颈,我决定采用这种方式:

def all
  respone = get '/v1/tweet.json'
  array = []

  response.each do |res|
    array << RecursiveOpenStruct.new(res)
  end

  array
end

但是 不适合应用程序扩展。我打算使用after_action,但发现我需要一张桌子。我甚至没有按下tableless model path

因此我决定重新开启HTTParty并重做#get以满足自己的需要。第一步是更新Gemfile以确保我坐在一个版本然后我这样做了:

module HTTParty
  module ClassMethods
    def get(path, options={}, &block)
      response = perform_request Net::HTTP::Get, path, options, &block
      methodize_array response if response.is_a? Array
    end

    def methodize_array(response)
      array = []

      response.each do |res|
        array << RecursiveOpenStruct.new(res)
      end

      array
    end
  end
end

现在我可以通过一个可扩展且有效的API调用哈希方法。如果有人有任何意见,请告诉我。

2 个答案:

答案 0 :(得分:2)

使用openstruct,内置:

# require 'ostruct'

hash = {a: 1, b: 2}
object = OpenStruct.new hash
object.a # => 1

如果您有嵌套结构,use a gem like thisdo it yourself like this

然后,您就可以使用所有sort_bymap方法,并且肯定会获得更好的效果。

答案 1 :(得分:0)

也许尝试Hashie并在Twitter结果中加入MethodAccess扩展名。以下是文档中的示例:

class MyHash < Hash
  include Hashie::Extensions::MethodAccess
end
h = MyHash.new
h.abc = 'def'
h.abc  # => 'def'
h.abc? # => true