This guide continues on from the Rails guide, and probably isn't applicable to day to day development, but will help you with some of the more complicated aspects down the line.

Stitch

An alternative to using Sprockets for Spine integration is Stitch. Stitch has the advantage that it outputs CommonJS modules, rather than use just simple concatenation. Using Stitch is beyond the scope of this guide, please see its README for more information.

JSON Prefixing

When sending JSON Ajax requests, Spine doesn't prefix any of it's data with the model name. For example, once a record has been created, Spine will will send a POST request with a payload looking like this:

{"name": "Sam Seaborn"}

Instead of something most Rails programmers are familiar with, prefixed data:

{"user": {name: "Sam Seaborn"}}

The reason for this design decision is that the prefix is often redundant data; the application already knows the format and indented destination of the data by the context of the HTTP request.

Fortunately Rails 3.1 has added default support for un-prefixed parameters, via a feature called wrap parameters. You should find a file under config/initializers/wrap_parameters.rb that looks like this:

ActionController::Base.wrap_parameters format: [:json]

# Disable root element in JSON by default.
if defined?(ActiveRecord)
  ActiveRecord::Base.include_root_in_json = false
end

Spine also requires responses from the server to be un-prefixed. As in the example above, ActiveRecord::Base.include_root_in_json should be false.

Both these settings are the default in Rails 3.1, and you shouldn't need to alter them.

Ajax & REST

Spine's Ajax module is fully REST compatible, and will work with Rails out of the box. One thing to look out for, is that Spine expects create and update actions to return the record object.

respond_to :html, :json

def create
  @page = Page.new(params[:page])
  if @page.save
    respond_with(@page, status: :created, location: @page)
  else
    respond_with(@page.errors, status: :unprocessable_entity) 
  end
end

def update
  @page = Page.find(params[:id])
  if @page.update_attributes(params[:page])
    respond_with(@page)
  else
    respond_with(@page.errors, status: :unprocessable_entity)
  end
end

For a demonstration of Spine communicating with a Rails controller, see the example application.

To find out more information about Ajax & Spine, please see its guide.

ID changes

Although Spine generates a ID for records client-side, most Rails apps will use server-side generated IDs. The latter approach has a few advantages, as server-side generated IDs are guaranteed to be unique, and usually automatically generated by the database. If you want to use a server-side generated ID, simply return it as part of the record response to the create action:

# POST /pages returns:
{"id": 1, "name": "POTUS"}

Spine will use the server generated ID from then on, and does some clever stuff to merge its internal ID so old ID references (such as in routes) still work.

Cross Domain Requests

A limitation of Ajax is the same-origin policy which restricts Ajax requests to the same domain and port as the page was loaded from. This is for security reasons, and prevents CSRF attacks.

However, while the same origin policy is integral to the security of the Web, it’s also somewhat inconvenient for developers trying to access legitimate remote resources. Cross Origin Requests, or CORS, lets you break out of the same origin policy, giving you access to authorized remote servers.

Using CORS is trivially easy, and is just the matter of adding a few authorization headers to request responses. The specification is well supported by the major browsers, although IE ignored the spec and created it's own object, XDomainRequest, which has a number of arbitrary restrictions and limitations. Luckily, it's easily shimed.

CORS support by browser:

External hosts in Spine

You can specify an external host for Spine to use by setting the Spine.Model.host option, like so:

Spine.Model.host = "http://api.myservice.com"

Once set, all of Spine's subsequent Ajax requests will be made on that endpoint.

CORs Rails integration

Let's create a cor method, which will add some of the request access control headers to the request's response.

Add the following to app/application_controller.rb:

before_filter :cor

def cor
  headers["Access-Control-Allow-Origin"]  = "js-app-origin.com"
  headers["Access-Control-Allow-Methods"] = %w{GET POST PUT DELETE}.join(",")
  headers["Access-Control-Allow-Headers"] = %w{Origin Accept Content-Type X-Requested-With X-CSRF-Token}.join(",")
  head(:ok) if request.request_method == "OPTIONS"
end

Although Access-Control-Allow-Origin takes a wildcard, I highly recommend not using it as it opens up your app to all sorts of CSRF attacks. Using a whitelist is much better and more secure.

The Access-Control-Allow-Headers section is important, especially the X-Requested-With header. Rails doesn't like it if you send Ajax requests to it without this header, and ignores the request's Accept header, returning HTML when it should in fact return JSON.

It's worth noting that jQuery doesn't add this header to cross domain requests by default. This is an issue that Spine solves internally, but if you're using plain jQuery for CORs, you'll need to specify the header manually.

jQuery.ajaxSetup({
  headers: {"X-Requested-With": "XMLHttpRequest"}
});

Some browsers send an options request to the server first, to make sure the correct access headers are set. You'll need to catch this in Rails, returning a 200 status with the correct headers. To do this, add the following to your application's config/routes.rb file:

match '*all' => 'application#cor', :constraints => {:method => 'OPTIONS'}

That's it, you're all set up for Cross Origin Requests with Spine!