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

Practicing Code with the DevChix


ApprenticeshipRailsRuby

In February I finally decided to do something I always wanted to do with DevChix. I wanted to lead a group project intended for those who wanted to learn and could commit to 2 hours a week. I had 3 volunteers.. then two.. and then one! But thats ok, I know people get busy and other things going on, so no big deal. But cool thing is, even those that left said they learned something. So, my goal was still reached!

The Project

I had this idea in my head for awhile. One thing with lists is, you need to write down something if you want to do it today. We all have todo lists, and there are somethings we want to do every day. You may try to remember them without a list, or maybe you write them out. I thought it would be cool to have a todo list app that will give your list, allow you to check them off, then next day you have the same list. Bonus to have a graph of how many you did each day of the week. I think its motivating to see a graph of my progress.

Getting Started

Its my experience, when I start an app, I add user login and I get hung up on the authentication, tests and stuff and never get to the meat of the app. So I wrote a simple login system with just a username (no password), threw it in the session and wrote some simple methods to check to see if logged in. Later we replaced it with Devise with not much trouble at all.

What we learned

  • git, making branches, push and pull
  • erb and haml
  • grid 960
  • HighCharts a javascript graphing library
    devise, we added a whitelist so we can have a beta with only certain email addresses allowed to sign up.

  • publishing with heroku
  • rails of course

Challenges

Summer got busy, including a cross country move for me and new job! We still managed to meet about every 2 weeks. We used github for our code. We put our tasks in Pivotal Tracker and that worked fine. We used Github wiki to record meeting notes, what happened since last meeting and who is doing what for the next week. We’ve been able to meet weekly the past month or so as the summer is winding down.

Whats next?

We are deploying the app to DevChix for beta testing. From there, not sure.

RailsBridge


RailsRubyThoughts

Even though I am a Python developer, this made me happy:

http://railsbridge.org/

We’ve been fortunate to have an outstanding, welcoming Python community driving the tone and the quality of events from PyCon, down to the statewide and local user groups. We don’t yet have a need for such a bridge group, and I hope we never need one. But it’s great to see one form quickly where it’s needed, and to see familiar names associated with it. More power to you.

Gloria

Using YUI DataTable with Rails


Javascript/AJAXRailsTips and Tricks

I am currently working on an Rails app that integrates the YUI dataTable, and in going through the tutorials I noticed they are all assume a PHP back-end. I also saw a number of people asking how to get this to work with a Rails controller, so I thought I’d write up my experience in the hopes that it helps someone else. For basic info about setting up the dataTable, refer to the YUI site, linked above. I’m also going to try to clarify a few things that I found a bit obscure.

To create a dataTable you have to define a few basic ingredients:

  1. A dataSource. This defines where the info in the table comes from, and what format it is returned in. I’m using JSON.
  2. A schema that you define. This is a part of the dataSource and is essentially a map to your data. The schema tells the table where to find the field values.
  3. An array of column definitions. You supply the name and “key” of each column and any additional information, such as whether it is editable (and if so, which editor to use) and how to format the data inside the column if you don’t want to just spit it out directly.

Let’s start with a dataSource. In this example, we’re making a table of tasks, so I want to hit my tasks controller to return the data. Standard Rails controller action. In the code snippet below,

  1. in line 1, I supply a URL (note that the url ends in .json, and in my controller I have a responds_to block which constructs my json response).
  2. In line 2, I’m telling the dataSource — hey, you’re gonna get some JSON.
  3. And line 3, is where we define the responseSchema, which has two critical parts: the resultsList and the fields.
  var dataSource = new YAHOO.util.XHRDataSource("/projects/13/elements/21/tasks.json");
  dataSource.responseType = YAHOO.util.XHRDataSource.TYPE_JSON;
  dataSource.responseSchema = {
   resultsList: "Resources.data",
   fields: [
      {key:"task.id"},
      {key:"task.status"},
      {key:"task.percent_complete"},
      {key:"task.description"},
      {key:"task.due_date", parser:date}
      ]
  }

Let me be clear since I got hung up on this a bit. You as the application developer are responsible for two things. One, constructing your response — in other words, you can build your JSON response any way you want, with any keys, values and hierarchical structure that make sense (though it will help you to think it through and standardize it, Satyam has some good info on that here). And two, telling your dataSource how to navigate your response: i.e. — where do find what the data you’re looking for (the needle) in the response (the haystack of JSON)? This is the schema, the map of your data.
Controller code that creates my custom JSON:

# tasks_controller.rb
def index
 respond_to do |format|
      format.json{
         tasks = []
         @tasks.each do |task|
           task_container = {}
           task_container  = task
           task_container['editable_by_user'] = permission.edit? # some metadata I use
           task_container['deletable_by_user'] = permission.delete?
           task_container['resource_name'] = "task"
           tasks << task_container
        end
        data = {"Resources" => {"data" => tasks}}
        render :json => data.to_json
      }
  end
end

And just for fun, here’s a look at the JSON my controller gives me back when I hit this url ‘/projects/13/elements/21/tasks.json’.
The JSON:

{"Resources": {"data": [{"task": {"status": "not_started", "started_on": null, "updated_at": "2009-02-07T22
:03:18Z", "project_id": 13, "percent_complete": null, "high_priority": null, "element_id": 32, "deletable_by_user"
: true, "completed_on": null, "editable_by_user": true, "element_title": "looking good", "id": 50, "created_by_id"
: 7, "resource_name": "task", "description": "Pass the stimulus bill", "assignments": [], "due_date"
: "", "users": [], "resource_url": "/projects/13/elements/32/tasks/50", "due": null, "created_at": "2009-02-07T22
:03:18Z"}}, {"task": {"status": "not_started", "started_on": null, "updated_at": "2009-02-07T22:03:39Z"
, "project_id": 13, "percent_complete": null, "high_priority": null, "element_id": 32, "deletable_by_user"
: true, "completed_on": null, "editable_by_user": true, "element_title": "looking good", "id": 51, "created_by_id"
: 7, "resource_name": "task", "description": "Negotiate without pre-conditions", "assignments": [], "due_date"
: "", "users": [], "resource_url": "/projects/13/elements/32/tasks/51", "due": null, "created_at": "2009-02-07T22
:03:39Z"}}]}}

So that’s it! I’ve defined this JSON array and built it, then returned it from the controller when I get a .json request.

Now that you’ve seen the response, take another look at the responseSchema (you created above) and the two properties that you set:

  1. ResultsList. Notice it is set to “Resources.data”, which are the JSON keys I used. It uses dot-syntax to point to array of tasks in my json.
  2. Fields. Again using dot syntax, and having the ResultsList array to navigate through, it can pull the specific values it wants from the list; so ‘task.status’, ‘task.started_on’, etc., will retrieve those values from the response.

Make sense?

You are now one step away from being able to see your table. So, create your column definitions, an array of data about each column in the table. This is also where you can specify formatting and editing information (not covered in this article).

var columnDefinitions = [
  {key:"task.status",formatter:"formatPriority", sortable:true},
  {key:"task.percent_complete", label:"Percent Complete", sortable:true},
  {key:"task.description", label:"Description"},
  {key:"task.due_date", label:"Due Date", editable:true,sortable: "true",formatter:YAHOO.widget.DataTable.formatDate, editor: new YAHOO.widget.DateCellEditor({resource:'task', updateParams:"task[due]"})}
  ];

Notice that each column definition has a key: this key must be accessible in your JSON, AND it must be defined as a field.

And then you create your table. The first argument is the id of a div on the page to which the table will be attached, the last is an optional configuration hash (pagination, anyone?).

var dataTable = new YAHOO.widget.DataTable("project_tasks", columnDefinitions, dataSource, optionalConfigurationHash);

I hope this is helpful to get you started with the YUI dataTable on Rails. I may do further posts on XHR editing of individual cells in a dataTable using Rails.

How to see exception_notification plugin work in development mode.


RailsRuby

I use HopToad by the Thoughtbot Guys (I say guys because I know they don’t have any girls on the team *wink*) to handle exceptions from my rails apps these days but today I found myself in a situation where I needed to use the exception_notification plugin instead. I haven’t used the plugin for quite sometime so I wanted to make sure I had everything all setup correctly before pushing out to staging and production. I remembered that I had done this before in development but I couldn’t remember everything I needed to do so I, of course, asked uncle Google. After reading the readme and a little googling I figured out what I needed to do in order to see it work in development. It took me far longer than I wanted and I don’t want to go through that again in the future so I figured I would just write a quick blog post to remind me next time I want to do it.

So here goes:

First get Exception notification all setup (this is all from the readme file)

script/plugin install git://github.com/rails/exception_notification.git

then in application.rb put
include ExceptionNotifiable

then in environment.rb put
ExceptionNotifier.exception_recipients = %w(joe@schmoe.com bill@schmoe.com)

Once you have it setup you can do all the other stuff that lets you see it work in your development environment.

put the following two lines in your application.rb file
alias :rescue_action_locally :rescue_action_in_public
local_addresses.clear

then in your development.rb file change
config.action_controller.consider_all_requests_local = true
to be
config.action_controller.consider_all_requests_local = false

Exception Notifier doesn’t send email notification on ActiveRecord::RecordNotFound and ActionController::UnknownAction errors. So you will need to create 500 error to see the notification going out in your log. You can just add an action to a controller that throws a divide by zero error, restart your server and hit that action and you should see the notification trigger in your development log.

Once you have seen it work make sure to undo everything in the second section.

Cheers