Similar to my last few posts, I’d like to explore Batman.js through building a simple CRUD app. Specifically my requirements are:
Simple form CRUD
Routing of resources - nested, with nav, pagination
Associations - has_many, belongs_to
Rails idiom and integration - shouldn’t be an impedance mismatch
Data binding - views change automatically when data does
View Composition - weak, strong…just get it done
No Framework Poison Pills - that would be bugs and things that would jeopardize a production app
HAML/Coffeescript Friendly
This first post is going to be really similar to getting ready for Ember, but has a few differences now that I’ve carried the Batman implementation all the way through and some master branches have changed (life on the edge). Here we go…
Start a new rails app (Rails 3.2.3):
1
$ rails new batman-rails-demo
And add the following to the Gemfile:
12345678910111213141516
# AMS, no longer requires a scope, woot!
gem "active_model_serializers", :git => "git://github.com/josevalim/active_model_serializers.git"
# shipping 0.8.0 (latest is 0.9.0), so just getting this for generators
gem 'batman-rails', git: 'git://github.com/Shopify/batman-rails.git'
# for sanity
gem 'thin'
gem 'quiet_assets'
gem 'bootstrap-sass', '~> 2.0.1', group: [:assets]
gem 'haml-rails'
gem 'haml_assets'
gem 'ejs'
# for faking data for dev
gem 'ffaker', group: [:development, :test]
And rebundle…
1
$ bundle update
Now let’s make our single resource and a serializer (serializer now made automatically!):
1
$ rails g resource post title:string content:string
Oddly, the serializer references some ApplicationSerializer which doesn’t exist at the time of writing, so change that to:
classPostsController<ApplicationControllerrespond_to:jsondefindexrespond_withPost.allenddefshowrespond_withPost.find(params[:id])end# for validation, can't use responders (batman expects errors to not have a root)defcreate@post=Post.new(params[:post])respond_todo|format|if@post.saveformat.json{render:json=>@post}elseformat.json{render:json=>@post.errors,:status=>:unprocessable_entity}endendend# for validation, can't use responders (batman expects errors to not have a root)defupdate@post=Post.find(params[:id])respond_todo|format|if@post.update_attributes(params[:post])format.json{render:json=>@post}elseformat.json{render:json=>@post.errors,:status=>:unprocessable_entity}endendend# batman can handle Rails 3 respond for this actiondefdestroyrespond_withPost.destroy(params[:id])endend
And get our database up to speed…
12
$ rake db:create
$ rake db:migrate
Let’s put some dummy data into seeds.rb:
db/seeds.rb
123456
foo=025.timesdosleep1# so we can have unique timestampsPost.create(:title=>"Post #{foo.to_i}",:content=>"#{Faker::Lorem.paragraph(5)}")foo=foo+1end
Seeding will take 25 seconds…
1
$ rake db:seed
Running the server and calling the API we see some serialized posts. Woot!
123
$ rails server
$ curl localhost:3000/posts.json # from another window
{"posts":[{"title":"Post 0","content":"Dolore suscipit et et consectetur necessitatibus sapiente consequatur. Ut laboriosam animi ducimus veniam delectus labore. Earum minima non architecto consequuntur minus. Delectus omnis saepe illum et quas consequatur. Consequatur vitae provident cum eaque dolor tenetur. Qui consequatur delectus odio aliquam tenetur ut."}...
Some routing changes in config/routes.rb:
1234
BatmanRailsDemo::Application.routes.draw do
resources :posts
root to: 'main#index'
end
Make a landing page action to host the app:
1
$ rails g controller main index
Remove public/index.html, empty out main/index.html and if you’re HAML crazy like most folks I know, change your application.html.haml like so (oooh, the hints of Batman creep in!):