Displaying another app’s data using HTTParty and Kaminari


RailsRuby

I needed to access some data from another app and display it to users. The api side was a Rails 2.3.11 app and used will_paginate. It returned the json of a model collection and an association along with paginator information. The client side was a Rails 3.1 app using HTTParty and Kaminari.

API Controller

Rails 2.3.11 doesn’t include associations in the json, which is a known bug. So I returned a collection with the two models’ information. I used an API key to authenticate, which I won’t describe in detail since it’s outside the scope of this post.

class Api::FoosController < ApplicationController
  before_filter :authenticate_api_key!

  def foos_index
    @foos = Foo.paginate(:order => "created_at desc", :per_page => 40, :page => params[:page], 
        :include => :bar)
    @records = @foos.collect { |foo| {:foo => foo, :bar => bar} }
    render :json =>  {:records => @records, :total_entries => @foos.total_entries, 
        :per_page => @foo.per_page }
  end
end

API Route

The route requires the page number.

  map.connect '/api/foos/foos_index/:page', :controller =&gt; "api/foos", :action =&gt; :foos_index

Client Model

FooIndex.foos_index returns itself, along with accessors for the response, records, total_count, current_page, and limit_value. The last three are important for Kaminari. When I parsed the JSON, I symbolized the keys. Much prettier than strings.

class FooIndex
  include HTTParty
  base_uri ENV['FOO_APP_URL']
  default_params :api_key =&gt; ENV['FOO_API_KEY']
  format :json
  attr_accessor :response, :records, :total_count, :current_page, :limit_value

  def initialize(response, records, total_count, current_page, limit_value)
    self.response = response
    self.records = records
    self.total_count = total_count
    self.current_page = current_page
    self.limit_value = limit_value
  end

  def self.foos_index(page)
    @current_page = page || 1
    response = get("/api/foos/foos_index/#{@current_page}")
    if response.success?
      json = JSON.parse(response.body, symbolize_names: true) 
      self.new(response, json[:records], json[:total_entries], page, json[:per_page])
    else
      raise response.response
    end
  end
end

Client Controller

I had to be creative with Kaminari using the paginate_array method. The options total_count and limit are used because the api doesn’t return the whole set of data, just the data for one page.

class FoosController &lt; ApplicationController
  def index
    params[:page] ||= 1
    @foos_index = FooIndex.foos_index(params[:page])
    @paginated_array = Kaminari.paginate_array(@foos_index.records, 
        total_count: @foos_index.total_count, 
        limit: @foos_index.limit_value).page(@foos_index.current_page)
  end
end

Client Route

Page number is optional in the route. The controller defaults it to page 1.

  match '/foos(/:page)', :controller =&gt; :foos, :action =&gt; :index

Client View in HAML

The json returned by the api doubles up the first part of the hash. I’m not sure of an elegant way to handle that.

= paginate @paginated_array

%table
  %thead
    %th Foo Name
    %th Bar Name
    - @foos_index.records.each do |record|
      %tr
        %td= record[:foo][:foo][:name]
        %td= record[:bar][:bar][:name]

= paginate @paginated_array

And that’s all it takes. Please comment with any comments or questions.

About Angel N. Sciortino

Ruby programmer. Sysadmin. Queer and polyamorous. Welder. Crocheter and darner. Chaotic good.

More Posts by Angel N. Sciortino - Author Website

Hpricot and woot!


RubyTips and Tricks

I know there are several sites for tracking wootoffs on Woot, but I wanted to write my own small program in Ruby. To do this, I used Hpricot by why the lucky stiff.

First, I need my require statements. I’ll need rubygems, since hpricot is a gem, and open-uri to access woot.

require 'rubygems'
require 'hpricot'
require 'open-uri'

Woot has an API that returns XML, so I used Hpricot::XML to parse what comes back.

doc = Hpricot::XML(open("http://www.woot.com/salerss.aspx"))

Now I want to know if it’s a wootoff. If it is, I’ll want to check more frequently. I use the at method to find the element I want, and inner_html to get the text inside that element. The element I’m interested in is woot:wootoff. I’m going to start putting the output into the text variable.

wootoff = doc.at("woot:wootoff").inner_html =~ /true/i
text = wootoff ? "wootoff! ^_^" : "no wootoff v_v"
text << "\n"

Next I want to know what the item is and the price and shipping.

text << doc.at("item > title").inner_html << "\n"
text << doc.at("woot:price").inner_html << " + "
text << doc.at("woot:shipping").inner_html << "\n"

I'll also want to know how much of the item is left, so I know if I need to rush to buy something I want.

percent_gone = doc.at("woot:soldoutpercentage").inner_html
percent_gone = percent_gone.to_f * 100
percent_left = (100 - percent_gone).round
text << "#{percent_left}% Left"

I took all that code and put it into a method, to be looped over every two minutes. I wanted it to print to the screen only if the text changed. This is what the completed program looks like.

#!/usr/bin/ruby
require 'rubygems'
require 'hpricot'
require 'open-uri'

def woot_item
  doc = Hpricot::XML(open("http://www.woot.com/salerss.aspx"))
  wootoff = doc.at("woot:wootoff").inner_html =~ /true/i
  text = wootoff ? "wootoff! ^_^" : "no wootoff v_v"
  text << "\n"
  text << doc.at("item > title").inner_html << "\n"
  text << doc.at("woot:price").inner_html << " + "
  text << doc.at("woot:shipping").inner_html << "\n"
  percent_gone = doc.at("woot:soldoutpercentage").inner_html
  percent_gone = percent_gone.to_f * 100
  percent_left = (100 - percent_gone).round
  text << "#{percent_left}% Left"
end

woot_text = ''
while true
  new_woot_text = woot_item
  if woot_text != new_woot_text
    woot_text = new_woot_text
    puts "*****************************************************"
    puts woot_text
  end
  sleep 120
end

Here is some output from the program.

*****************************************************
no wootoff v_v
Lockjaw Self-Adjusting Locking Pliers - 2 Pack
$9.99 + $5 shipping
100% Left

Of course, this is only a beginning to what you can do. You can have it send a twitter, SMS, email, or any number of things if an item you want is available or a wootoff starts.

Angel

About Angel N. Sciortino

Ruby programmer. Sysadmin. Queer and polyamorous. Welder. Crocheter and darner. Chaotic good.

More Posts by Angel N. Sciortino - Author Website

Coworking: a cure for the lonely telecommuter


Thoughts

There are two types of telecommuters. Those that enjoy spending all of their time alone at home or at coffee shops, and those that really miss interacting with other programmers and geeks in general. I fall into the second category. So what is a lonely telecommuter to do? Enter Coworking. Coworking is a shared space for computer programmers, web designers, graphic designers, etc. to work alongside one another. There is frequently cafe culture involved, and coworking spaces can be in coffee shops or in offices.

A coworking space helps developers network and learn new things. They can be introduced to new hardware, development tools, or methodologies. They might be able to find some work through people they meet there. They can also ask someone a question if they are stuck on a particularly tough problem.
There are advantages to both coworking setups. A coffee shop is cheaper, and it is easier to move around from location to location. It usually costs just the price of a cup of your favorite beverage. The downsides of a coffee shop are that you need to either have a room reserved or have to try and secure a large enough space for the coworkers. It can also be more distracting with all the conversations taking place around the group, unless you’re able to reserve a room.

An office space is more expensive, and you usually have setup costs like furniture, hardware, refrigerator, etc. The upside is that you are surrounded by other people working on something. Sometimes you can also have your own desk to leave a desktop, a second monitor for your laptop, paperwork, pictures of loved ones, or whatever else you might want to have at work. You also don’t have to buy coffee if it isn’t something you want, and sometimes plain black coffee is provided. Plus the cost of sharing the office space is tax deductible.

Even if you fall into the category of the telecommuter that likes working alone, you might consider working in a coworking space. It really can help you meet new people and grow professionally. There may already be a coworking space in your city. If not, you should consider starting one. User groups are a good place to find people interested in working together. There are plenty of coffee shops. So make a post to the user group about meeting together to do work. Who knows, you might end up with coworkers.

About Angel N. Sciortino

Ruby programmer. Sysadmin. Queer and polyamorous. Welder. Crocheter and darner. Chaotic good.

More Posts by Angel N. Sciortino - Author Website

New Book: Mongrel by Angel Dobbs-Sciortino


BookLanguagesRuby

My book on Mongrel published with O’Reilly is now available for purchase. Mongrel is a web application for Ruby on Rails, more stable than WEBrick, and easier to set up than FCGI to Rails through Apache. You can find it at http://www.oreilly.com/catalog/mongrelpdf/.

About Angel N. Sciortino

Ruby programmer. Sysadmin. Queer and polyamorous. Welder. Crocheter and darner. Chaotic good.

More Posts by Angel N. Sciortino - Author Website