55

I would like to write a Ruby on Rails application that consumes a RESTful web service API performs some logic on the result and then displays that data on my view. For example, let's say I wanted to write a program that did a search on search.twitter.com. Using pure ruby I might create the following method:

def run(search_term='', last_id=0)
  @results = []
  url = URI.parse("http://search.twitter.com")
  res = Net::HTTP.start(url.host, url.port) do |http|
    http.get("/search.json?q=#{search_term}&since_id=#{last_id.to_s}")
  end
  @results = JSON.parse res.body
end

I'm tempted to just drop that method into my Rails controller as a private method, but part of me thinks that there is a better, more "Rails" way to do this. Is there a best practice approach or is this really the best way?

Mike Farmer
  • 2,992
  • 4
  • 28
  • 32

5 Answers5

54

There is a plugin/gem called HTTParty that I've used for several projects.

http://johnnunemaker.com/httparty/

HTTParty lets you easily consume any web service and parses results into a hash for you. Then you can use the hash itself or instantiate one or more model instances with the results. I've done it both ways.

For the twitter example, your code would look like this:

class Twitter
  include HTTParty
  base_uri 'twitter.com'

  def initialize(u, p)
    @auth = {:username => u, :password => p}
  end

  # which can be :friends, :user or :public
  # options[:query] can be things like since, since_id, count, etc.
  def timeline(which=:friends, options={})
    options.merge!({:basic_auth => @auth})
    self.class.get("/statuses/#{which}_timeline.json", options)
  end

  def post(text)
    options = { :query => {:status => text}, :basic_auth => @auth }
    self.class.post('/statuses/update.json', options)
  end
end

# usage examples.
twitter = Twitter.new('username', 'password')
twitter.post("It's an HTTParty and everyone is invited!")
twitter.timeline(:friends, :query => {:since_id => 868482746})
twitter.timeline(:friends, :query => 'since_id=868482746')

As a last point, you could use your code above also, but definitely include the code in a model as opposed to a controller.

rmiesen
  • 2,470
  • 1
  • 21
  • 15
Kyle Boon
  • 5,213
  • 6
  • 39
  • 50
  • I love this gem. This makes consuming web services really slick. A question I still have though is whether Rails has something built in already to do this? Seems like this is a common enough thing to try to do that they would have a way to do it. – Mike Farmer Apr 14 '09 at 18:44
  • Also, forgive my Rails noobieness :) , how would I set that up in as a model. I have only used models for database access using ActiveRecord. – Mike Farmer Apr 14 '09 at 18:47
  • 7
    There isn't anything built in to rails core. A model is a just a class - just create a class in the models directory and don't inherit from the ActiveRecord:Base class. You won't have any of the AR goodness included, but this is a pretty common pattern for consuming web services within a rails app – Kyle Boon Apr 14 '09 at 18:51
  • Mike, did you settle on HttpParty and are you using it to do POST's to non-rest HTTP web services? I am using rest-client, not sure if it's working right though so wanted you take? – Satchel May 30 '10 at 07:08
  • 5
    I've started using restclient for this kind of stuff now because HTTParty is getting stale and restclient is much easier to use. – Mike Farmer Apr 15 '11 at 20:27
  • @MikeFarmer - I posted your comment about restclient as an answer since it was hard to find as a comment. Feel free to edit my answer. http://stackoverflow.com/a/12645037/74359 – Ethan Heilman Sep 28 '12 at 17:58
27

Restclient is a really nice solution to this problem.

require 'rest_client'
RestClient.get 'http://example.com/resource'
RestClient.get 'http://example.com/resource', {:params => {:id => 50, 'foo' => 'bar'}}

From the readme.

Bruno Peres
  • 2,980
  • 1
  • 21
  • 19
Ethan Heilman
  • 16,347
  • 11
  • 61
  • 88
11

if the remote RESTful web service was also created with Ruby on Rails, ActiveResource is the way to go.

mustaccio
  • 18,234
  • 16
  • 48
  • 57
Adam Alexander
  • 15,132
  • 5
  • 42
  • 41
  • I can't make that assumption as I would like to pull data from many different web sites wherein I would have no idea what the underlying framework would be. But I'll look at ActiveResource for the things I know are Rails. Thanks! – Mike Farmer Apr 14 '09 at 17:42
  • 4
    Actually, the remote RESTful service does not have to be created using Rails for ActiveResource to work. The underlying implementation does not matter. – WattsInABox Jan 17 '12 at 20:09
  • This is definitely the Rails preferred way to consume RESTful resources. Keeps the code clean and conforms to MVC. – Michael Choi Apr 11 '16 at 21:13
3

In response to your Twitter example, there is a Twitter Gem that would help to automate this for you.

Adam Alexander
  • 15,132
  • 5
  • 42
  • 41
2

I think Faraday deserves a mention here. Really nice interface, and powerful concept of middleware.

https://github.com/lostisland/faraday

Duke
  • 7,070
  • 3
  • 38
  • 28