Apr
23

JSON with Ruby on Rails on Google AppEngine

Getting Started

Here’s a list of what you’ll need to get started with the demo:

  • a Google AppEngine account
  • Ruby 1.8.6
  • Rails 2.3.5
  • Ruby on Rails AppEngine Gem

Instructions

The first steps that you’ll want to follow are from John Woodell’s Gist on Rails 2.3.5 on App Engine with DataMapper.

As you’ll notice, the last couple of steps generate a RESTful resource called Contact and also it’s associated DataMapper Model.

Adding REST

Now in order to add JSON to the mix, we’ll need a couple more things:

We only have to make a few very small changes to the demo app that you got from the Gist to get JSON integrated. They’re listed below, and also at this Gist.

Gemfile changes: adding the json-jruby and dm-serializer to the gems for bundler
# Critical default settings:
disable_system_gems
disable_rubygems
bundle_path '.gems/bundler_gems'

# List gems to bundle here:
gem 'rails_dm_datastore'
gem 'rails', "2.3.5"
gem "json-jruby"
gem "dm-serializer"
config.ru changes: adding require ‘dm-serializer’ and ‘json’
require 'appengine-rack'
require 'cgi'
require 'json'
require 'dm-core'
require 'dm-serializer'

AppEngine::Rack.configure_app(
    :application => 'iphone-json',
    :precompilation_enabled => true,
    :sessions_enabled => true,
    :version => "1")

AppEngine::Rack.app.resource_files.exclude :rails_excludes
ENV['RAILS_ENV'] = AppEngine::Rack.environment

deferred_dispatcher = AppEngine::Rack::DeferredDispatcher.new(
     :require => File.expand_path('../config/environment', __FILE__),
     :dispatch => 'ActionController::Dispatcher', :isolate => true)

map '/admin' do
  use AppEngine::Rack::AdminRequired
  run deferred_dispatcher
end

map '/user' do
  use AppEngine::Rack::LoginRequired
  run deferred_dispatcher
end

map '/' do
  run deferred_dispatcher
end
and finally, the contacts_controller.rb needs the corresponding format.json render statements.
class ContactsController < ApplicationController
  # GET /contacts
  # GET /contacts.xml
  # GET /contacts.json
  def index
    @contacts = Contact.all

    respond_to do |format|
      format.html # index.html.erb
      format.xml  { render :x ml => @contacts }
      format.json { render :json => @contacts }
    end
  end

  # GET /contacts/1
  # GET /contacts/1.xml
  # GET /contacts/1.json
  def show
    @contact = Contact.find(params[:id])

    respond_to do |format|
      format.html # show.html.erb
      format.xml  { render :x ml => @contact }
      format.json { render :json => @contact }
    end
  end

  # GET /contacts/new
  # GET /contacts/new.xml
  # GET /contacts/new.json
  def new
    @contact = Contact.new

    respond_to do |format|
      format.html # new.html.erb
      format.xml  { render :x ml => @contact }
      format.json { render :json => @contact }
    end
  end

  # GET /contacts/1/edit
  def edit
    @contact = Contact.find(params[:id])
  end

  # POST /contacts
  # POST /contacts.xml
  # POST /contacts.json
  def create
    @contact = Contact.new(params[:contact])

    respond_to do |format|
      if @contact.save
        flash[:notice] = 'Contact was successfully created.'
        format.html { redirect_to(@contact) }
        format.xml  { render :x ml => @contact, :status => :created, :location => @contact }
        format.json { render :json => @contact, :status => :created, :location => @contact }
      else
        format.html { render :action => "new" }
        format.xml  { render :x ml => @contact.errors, :status => :unprocessable_entity }
        format.json { render :json => @contact.errors, :status => :unprocessable_entity }
      end
    end
  end

  # PUT /contacts/1
  # PUT /contacts/1.xml
  # PUT /contacts/1.json
  def update
    @contact = Contact.find(params[:id])

    respond_to do |format|
      if @contact.update_attributes(params[:contact])
        flash[:notice] = 'Contact was successfully updated.'
        format.html { redirect_to(@contact) }
        format.xml  { head :o k }
        format.json { head :o k }
      else
        format.html { render :action => "edit" }
        format.xml  { render :x ml => @contact.errors, :status => :unprocessable_entity }
        format.json { render :json => @contact.errors, :status => :unprocessable_entity }
      end
    end
  end

  # DELETE /contacts/1
  # DELETE /contacts/1.xml
  def destroy
    @contact = Contact.find(params[:id])
    @contact.destroy

    respond_to do |format|
      format.html { redirect_to(contacts_url) }
      format.xml  { head :o k }
      format.json { head :o k }
    end
  end
end

Final Notes

  1. You may have noticed that by default you get RESTful XML when you generated the resource. It doesn’t work without the needed dm-serializer. Remember, we’re using DataMapper here and not ActiveResource. If you try, you’ll get this beautiful error.
    SEVERE: [1272067889120000] RuntimeError (Not all elements respond to to_xml)
  2. If you inspect your DataMapper model, you’ll notice that every field is a required field. This is key for when you’re testing your POST method.
  3. The last thing that is currently being looked into, is that the POST requires you to pass in the Class in lowercase for it to properly work. I’m not sure if this is a case-sensitive issue, or just purely needing to change the param to be “:Contact” instead of “:contact” in the controller.

Source Code

  • The fully working demo can be found running on Google AppEngine at: iphone-json.appspot.com
  • source is available on Github.
  • And there is an accompanying iPhone REST client also available on Github. (Android client is currently in development.)

Alternatively, if you just want to quickly test your newly created JSON RESTful resource, you can use the REST Client for Firefox or REST Client for Google Chrome.

Share the Love:
  • Digg
  • del.icio.us
  • Facebook
  • StumbleUpon
  • Design Float
  • Reddit
  • DZone
  • FriendFeed
  • Twitter
  • email
  • Print

Related Posts

View Comments to “JSON with Ruby on Rails on Google AppEngine”

|
  1. [...] iPhone / iPad app with JSON interface: blog post by John Wang [...]

  2. [...] JSON with Ruby on Rails on Google AppEngine | John Wang [...]

|

Leave a Reply

blog comments powered by Disqus