0

I'm using an API with Ruby. I've constructed my own model that will work with HTTParty to go up to an api and grab the data. Here's an example of what I'm doing:

tweets = Twitter::Tweets.all
first_tweet = tweets.first

This does not return any errors, but inside tweets we now have a list of all of our tweets. (We store this in a NoSQL DB for performance and rate limiting issues.) Now then, we want to know when our first tweet was created:

first_tweet.created_at
# => undefined method `created' for #<Hash:0x007ff3491062d0>

Well that sucks... OK. Its a Hash, so we try:

first_tweet['created_at']
# => "2014-04-07T08:37:47+00:00"

Awesome! But, I'm going to be doing this a lot.

Key Question: Is there a way to use methods instead of keys?

Edit:

I'm understanding that I can either use OpenStruct or a gem for this. Thanks for letting me know. I guess the question now becomes: Is there an effective (and DRY) way to implement this into a model?

Here's what our model would may look like:

# app/models/twitter/tweet.rb
module Twitter
  class Tweet < Twitter::Base
    class << self
      def all
        get '/v1/tweets.json'
      end
    end
  end
end

And Twitter::Base is, to keep it brief, our configuration:

# app/models/twitter/base.rb
module Twitter
  class Base < ActiveRecord::Base
    include HTTParty

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

Although, I could do:

def all
  tweets = get('/v1/tweets.json')
  array = []

  tweets.each do |tweet|
    array << OpenStruct.new(tweet)
  end

  return array
end

But this would be a lot of work to write an openstruct per every method. I think I'll explore it and answer my own question in the next few days if no one knows of a DRY and scalable way to implement this.

Reminder: Not actually dealing with the twitter api, but the use case stays the same.

BenMorganIO
  • 2,036
  • 17
  • 37
  • 2
    Checkout https://github.com/svenfuchs/hashr or https://github.com/intridea/hashie or http://www.ruby-doc.org/stdlib-2.0/libdoc/ostruct/rdoc/OpenStruct.html – Uri Agassi May 20 '14 at 20:35
  • use `method_missing` to do the `hash lookup` and then they become methods dynamically – bjhaid May 20 '14 at 21:11
  • No really, @Martin is right: create a real Tweet object, Ruby was designed for it – mdesantis May 20 '14 at 21:21
  • Using symbols like `first_tweet[:created_at]` will cost as much or less memory than having a method called `created_at` since method names are actually also stored as symbols. Calling `hash[:something]` is actually a bit faster than calling `object.something` (0.2sec vs 0.226sec for million times on my machine. See: https://gist.github.com/kke/4e2220c653555318960d – Kimmo Lehto May 20 '14 at 21:30
  • If someone is accessing my site a million times to the point I'm worrying about this, I wouldn't be the one writing the code... – BenMorganIO May 20 '14 at 21:58

2 Answers2

2

you can monkey patch Hash... (or make an inherited class) so that any unrecognised method is checked to see if it's a key to the hash

class Hash
  alias_method :original_method_missing, :method_missing
  def method_missing(method, *args, &block)
    if self.has_key?(method.to_s)
      self[method.to_s]
    else
      original_method_missing(method, *args, &block)
    end
  end
end

The "to_s" may not be needed in rails if it's a 'hash with indifferent access'

SteveTurczyn
  • 36,057
  • 6
  • 41
  • 53
  • Best answer so far! Am I able to just reopen the class within `Twitter::Base` that way I don't snafu the `Hash` class throughout the app – BenMorganIO May 20 '14 at 22:06
  • 1
    @BenMorganIO Yes in Ruby 2.1 you can use [refinements](http://www.ruby-doc.org/core-2.1.2/doc/syntax/refinements_rdoc.html) to reopen the class only within the Twitter module. – Mark Thomas May 20 '14 at 22:16
  • @MarkThomas That just made my day. – BenMorganIO May 20 '14 at 22:44
  • Refinements lead to a: [NOTE] You may have encountered a bug in the Ruby interpreter or extension libraries. Bug reports are welcome. For details: http://www.ruby-lang.org/bugreport.html. I'll figure this out on my own. Merci. Your technique is absolutely amazing. – BenMorganIO May 21 '14 at 22:26
1

Hashies (https://github.com/intridea/hashie) are working really well at simulating object behaviors. Now, you may want to create a real Tweet object...

Martin
  • 7,634
  • 1
  • 20
  • 23