Skip to content

Instantly share code, notes, and snippets.

@stilist
Last active December 10, 2015 06:18
Show Gist options
  • Save stilist/4393460 to your computer and use it in GitHub Desktop.
Save stilist/4393460 to your computer and use it in GitHub Desktop.
How I run Backbone.js applications on Sinatra.
require "sinatra/base"
module Sinatra
# = Sinatra::JsonApi
#
# <tt>Sinatra::JsonApi</tt> is an extension meant for use with Backbone-style
# apps that use the backend strictly as an API. It assumes that full page
# loads should serve only to bootstrap the frontend application.
#
# == Usage
#
# Note: depends on the `sinatra-respond_to` gem.
#
# === Classic Application
#
# To use the extension in a classic application:
#
# require "sinatra"
# register Sinatra::RespondTo
# require "sinatra/json_api"
#
# # Application code
#
# === Modular Application
#
# To use the extension in a modular application:
#
# require "sinatra/base"
# require "sinatra/json_api"
#
# class MyApp < Sinatra::Base
# register Sinatra::RespondTo
# helpers Sinatra::JsonApi
#
# # Application code
# end
#
# This will add the +page_out+ method to the application/class scope. You
# can choose not to register the extension, but instead of calling
# +page_out+, you will need to call <tt>Sinatra::JsonApi.page_out</tt>.
#
module JsonApi
# Assuming ActiveRecord is set up with a `User` table, and a HAML file
# named `layout.haml` that loads Backbone etc.
#
# enable :sessions
#
# get "/users" do
# page_out User.all
# end
#
# app.get "/users/:id" do
# user = User.find_by_id params[:id]
# page_out user, ->{ 404 unless user }
# end
#
# app.post "/" do
# if @user
# page_out user
# else
# user = User.by_email params[:email]
# session[:user] = user.id if user && user.password == params[:password]
# page_out user, ->{ 401 unless user }
# end
# end
#
# before do
# @user = User.find_by_id session[:user]
# unauthorized = !@user && request.path != "/"
# page_out(nil, 401) if unauthorized
# end
#
# If the user has not authenticated, attempting to access `/users` or
# `/users/3` will result in a 401 Unauthorized error and a blank response.
# Once the user has authenticated, `/user` will give all known users;
# `/users/3` will give back the `User` record or a 404 error.
#
# This example also demonstrates several convenient features:
# 1. `data` and `status_code` can be `nil`
# 2. Passing a lambda for the `status_code` argument--`/users/3` determines
# the HTTP status code by whether the `User` record exists.
#
# == Other notes
#
# `page_out` attempts to call `for_json` on whatever is passed to `data`.
# Simply define a `for_json` method in the ActiveRecord model and return
# a `Hash` with the desired fields. Convenient for filtering out passwords
# or embedding associations.
def page_out data={}, status_code=200
output = case
when data.respond_to?(:for_json) then data.for_json
when data.class == Array
data.map { |i| i.respond_to?(:for_json) ? i.for_json : i }
else data
end
# Process `status_code` if it's a lambda. Need `... || 200` in case
# lambda returns `nil`.
status_code = (status_code.call || 200) if status_code.respond_to? :call
# 401 Unauthorized
# 403 Forbidden
if [401, 403].include? status_code
respond_to do |format|
format.js { halt_json status_code }
format.json { halt_json status_code }
format.html { redirect "/" }
end
else
respond_to do |format|
format.js { send_json output, status_code }
format.json { send_json output, status_code }
format.html { haml :layout, :layout => false }
end
end
end
private
def halt_json status_code=400
content_type :json
halt status_code, {}.to_json
end
def send_json data={}, status_code=200
content_type :json
status status_code
body({ data: data, status: status_code }.to_json)
end
end
helpers JsonApi
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment