We love Rails, object-oriented programming, and refactoring. We use a process to develop applications to work faster, introduce fewer bugs, and enjoy what we’re doing. We blog, Tweet, and talk at conferences on these subjects.
After every post and discussion, there are topics left unexplored. There are too many related concepts to link to, and there’s too much code we can’t share. We want to share our process with as many Rails developers as we can, and we’ve finally decided to take the next step. We’re writing a new book, called Ruby Science, that describes this process in an easy-to-digest reference format.
But this is more than just a book. As with Backbone.js on Rails, in addition to the book (in HTML, PDF, EPUB, and Kindle formats), you also get a complete example application, and the ability to get your questions about Ruby on Rails answered by the thoughtbot team using GitHub issues and live chat.
The book is written using Markdown and pandoc and distributed via GitHub. When you purchase, we give you direct access to the repository, so you can use the GitHub comment and issue features to give us feedback about what we’ve written and what you’d like to see. Give us your toughest Ruby questions with GitHub issues, and we’ll answer. Last but not least, also included is a Ruby on Rails reference application. What the book describes and explains, the example app demonstrates with real, working code. Fully up to date for Rails 3.2.
The book is a work in progress, and we currently have almost 40 pages of content. You can get access now for an early purchase price of $39. This offer is only valid for a limited time, and will increase to $49 on January 31, 2013.
You can see our working table of contents, read a free sample, and purchase access on the Ruby Science web page.
We have two online workshops scheduled for January, 2013. If you’ve always wanted to take one of our workshops, but couldn’t travel to Boston or SF, these online versions will give you the same great education in a format custom-tailored to the online experience.
Design for Developers. This workshop is for developers who want to better understand the language and practice of visual design. This workshop starts January 4th, 2013.
Test-Driven Rails. By the end of the workshop, you will know how to do Test-Driven Development using RSpec and Capybara. This workshop starts January 11th, 2013.
Each of the workshops lasts 4 weeks and is a mix of pre-recorded lessons with extended periods of hands-on practice, one-on-one support, and group online office hours. Signing up today gets you the first video lesson while you wait for the workshop to start.
After the course ends, you’ll still have access to the course videos, instructor, and thoughtbot team who will answer any ongoing questions you have about the topic. You’ll also have access to any updates we make to the course in the future.
And as with everything at learn.thoughtbot.com, if you’re not happy, just let us know within 30 days and we’ll refund your money. It’s as simple as that.
I hope we’ll see you in one of the courses!
Suppose you have the following in your Rails app:
# app/models/user.rb
class User < ActiveRecord::Base
has_secure_password
has_many :posts
end
# app/models/post.rb
class Post < ActiveRecord::Base
belongs_to :user
end
# app/controllers/posts_controller.rb
class PostsController < ApplicationController
def show
@post = Post.find(params[:id])
render json: @post.as_json(
only: [:id, :content, :created_at],
include: { user: { only: [:id, :username] } }
)
end
end
Your show action returns a JSON representation of a post, along with some limited information about the user it belongs to, using as_json. Works like a champ.
However… that nested hash you’re passing into as_json is a bit clunky. It’ll get clunkier if you add more associations, and you’ll have to repeat yourself if you add more actions that render posts (like index).
“A-ha!” you think, “I’ll just carefully override as_json in my models to include only the attributes I want by default, and everything will be rainbows and kittens!”
# app/models/user.rb
class User < ActiveRecord::Base
has_secure_password
has_many :posts
def as_json(options = nil)
super({ only: [:id, :username] }.merge(options || {}))
end
end
# app/models/post.rb
class Post < ActiveRecord::Base
belongs_to :user
def as_json(options = nil)
super({ only: [:id, :content, :created_at], include: :user }.merge(options || {}))
end
end
# app/controllers/posts_controller.rb
class PostsController < ApplicationController
respond_to :json
def show
respond_with Post.find(params[:id])
end
end
You fire up a console for a quick sanity check:
> pp Post.first.as_json
{"content"=>"test post, please ignore",
"created_at"=>Thu, 04 Oct 2012 21:05:50 UTC +00:00,
"id"=>1,
:user=>
{"created_at"=>Thu, 04 Oct 2012 17:07:29 UTC +00:00,
"id"=>1,
"password_digest"=>
"$2a$10$NKW95m6zQnPJiaOXz4u5LeMHKnRmrHjLLCUsTu8yMma/XNmJDi6qy",
"updated_at"=>Thu, 04 Oct 2012 17:07:29 UTC +00:00,
"username"=>"tester"}}
Cripes! You just exposed the user’s password digest! What’s going on here?
A bit of Rails source-diving reveals that the original as_json you’re overriding is a thin wrapper around serializable_hash, which does the actual work of serializing attributes and included associations. The latter is done by, of course, calling serializable_hash on the associated models… not as_json.
Armed with this knowledge the quick fix is obvious, and indeed, replacing “def as_json” with “def serializable_hash” in the example above will make it work as intended. But two things about this should make you slightly queasy. For one, your solution is coupled to the implementation details of an internal Rails method that could change in the future (what if serializable_hash itself becomes a wrapper like as_json?). And for two, serialization seems like it should be happening closer to the view layer — that concern doesn’t belong in our model classes.
If the JSON you’re emitting is hairy enough to merit the use of actual templates, you might want something like Jbuilder or RABL. But before diving into that world, consider the underrated active_model_serializers. It’s easy to use and doesn’t add much complexity over the old-and-busted approach — you just specify the attributes and associations to include in a different way.
Here’s our example again, now using active_model_serializers:
# app/serializers/user_serializer.rb
class UserSerializer < ActiveModel::Serializer
attributes :id, :username
end
# app/serializers/post_serializer.rb
class PostSerializer < ActiveModel::Serializer
has_one :user
attributes :id, :content, :created_at
end
# app/models/user.rb
class User < ActiveRecord::Base
has_secure_password
has_many :posts
end
# app/models/post.rb
class Post < ActiveRecord::Base
belongs_to :user
end
# app/controllers/posts_controller.rb
class PostsController < ApplicationController
respond_to :json
def show
respond_with Post.find(params[:id])
end
end
The only quirk is the use of has_one in serializers for all singular associations, regardless of whether the real association is has_one or belongs_to.
While your integration tests should hit the route and assert that the JSON response contains the expected set of attributes, let’s just revisit our sanity check from earlier:
> pp PostSerializer.new(Post.first).as_json
{:id=>1,
:content=>"test post, please ignore",
:created_at=>Thu, 04 Oct 2012 21:05:50 UTC +00:00,
:user=>{:id=>1, :username=>"tester"}}
Perfect! Now everything is rainbows and kittens… and you didn’t have to override as_json.
This is the last of the episodes we recorded at RubyConf 2012. Thank you to the conference organizers for providing us with space to record, to all our guests for taking their time away from the conference to talk to us, and to all our fans who stopped by and said “hi”. It all means the world and keeps us going.
Ben Orenstein is joined by Sarah Mei, RailsBridge co-founder, a developer at Pivotal Labs, and Diaspora core team member. In this episode, recorded at RubyConf 2012, Ben and Sarah discuss how communication patterns of your team manifest themselves in the code it writes, and how understanding those patterns can help you improve your code. They discuss RailsBridge, teaching, how teaching is an incredible learning opportunity, and how RailsBridge has helped expand the community of women developers in San Francisco and beyond. Finally, they explore how she got into Ruby, and women in technology.
Ben Orenstein is joined by Tammer Saleh and Randall Thomas, the founders of Thunderbolt Labs. In this episode, recorded at RubyConf 2012, they discuss their philosophy of running and building the company, how they differ from other consulting companies, and how they do much more than just Rails programming and how its leading to very interesting new kinds of work. Why they list their prices right on their website, and how they derived their rate of $277 per hour. They also explore what their first year in business has been like, some challenges they’ve faced, and some important lessons they’ve learned.