(Fri Mar 28, 2008) [/Rails] #
Part III of my three-part Apple Developer Connection series on Rails has been
posted. It's titled Deploying
Rails Applications on Mac OS X Leopard because it picks up where we left
off in Part II by deploying the application to Leopard Server using Capistrano.
I had a lot of fun writing this piece in particular because I got to play around with the new features in Leopard Server. It comes pre-installed with lots of Rails deployment goodies to streamline the process: Apache 2.2, mod_proxy_balancer, MySQL, Mongrel, Capistrano, and so on. As well, Apple created an enhanced version of mongrel_rails that, among other things, registers your Mongrel processes with Bonjour. The upshot is you can configure your Rails-powered web site right from Server Admin.
Enjoy!
(Thu Mar 20, 2008) [/Rails] #
Part II of my three-part Apple Developer Connection series on Rails has been
posted. It's titled Customizing
Rails Applications on Mac OS X Leopard because it picks up where we left
off in Part I by working with views and web forms, adding AJAX support, and
supporting an iPhone interface.
Coincidentally, we're offering another back-to-back Studio combo in June:
Register by April 18th and save $300 on each Studio.
Enjoy!
(Tue Mar 04, 2008) [/Rails] #
My latest Apple Developer Connection article, titled Developing Rails Applications on Mac OS X Leopard, is the first in a three-part series on Rails. It takes you through how to build a basic RESTful app using Rails 2.0.2 and XCode.
Parts II and III go beyond the basics to customize the application and deploy it on Leopard Server. They've already been written, and they should be available soon.
This series has been a long time in the making, and I'm delighted that it's finally available. It's a major update to the Tiger version of the article. Leopard comes with a bunch of pre-installed goodies to help you start building (and deploying!) Rails applications without a lot of fuss. And although I still use TextMate, it's great to see Rails support making its way into XCode 3.0. Even if you're a Rails pro, you may want to follow this series to see what Leopard brings to the party.
(Sun Oct 07, 2007) [/Rails] #
Herewith, responses to my survey How Would You Test This? regarding tools and techniques folks are using to test Rails controllers, and my humble thoughts at the end.
Mike Mangino shows you how he'd write an RSpec spec for my controller, and points out the style they use on projects.
"Our goal with controller tests is to create very simple, easy to read tests that cover the entire controller. We believe strongly in one assertion per test, so we will have a larger number of small tests. Our typical pattern is to stub everything at the beginning, and then mock as necessary to validate behavior."
Pat Maddox has found "testing controllers is generally child's play, and the model is where I run into the most trouble." He offers a step-by-step tutorial for BDDing my controller from start to finish using RSpec, and concludes with
"My controllers are easy to spec because they should be easy to spec. They just take some objects and tell them to do stuff. It's what those objects do that is complex. You can use mock objects to isolate that complexity. If you don't use mock objects, then you end up with leaky abstractions that make your tests harder to understand and maintain."
Jay Fields has never been able to accept that controllers can only be functionally tested. He looks forward to the day when we can instantiate controllers anew and unit test them:
"I think part of the problem is that controllers are not Good Citizens. Controllers violate the first rule of Good Citizenship. Upon creation (Controller.new), controllers are not in a valid state. Instead, controllers depend on being initialized within the framework and having their state set post construction time. That ends up being a problem for unit testing since it requires each test to set additional state on newly created controllers."
James Herdman wrote in to say he's a "hold the onions" sort of guy who prefers real objects over mocks/stubs whenever possible.
"Avoiding the database would be nice, but sometimes it's good to hit up your models in controller testing. Sometimes the angle at which we hit our models in the controller isn't quite as we intended when writing our models, or sometimes you expose bad behavior you didn't quite realize could happen... I play by this rule of thumb when it comes to mocking and stubbing with controllers: Fake out stuff that doesn't really matter, and fake out what's directly out of you control (e.g. external resources). (The drawback is, of course, that my tests take bloody ages to run through. But I'd rather stuff go afoul on my system than in production.)"
Kevin Barnes gave a nod to the shoulda plugin and finds the nested contexts to be handy on his "large-scale Rails app (EMR w/ 120+ models)".
Jim Morris uses RSpec with mocks, and pointed out a technique he came up with to automatically test all actions within a controller using RSpec.
I appreciate the time these folks took to write up examples and opinions. Hearing how other Rails developers are going about testing controllers gave me new perspectives, and that always helps me. I hope you picked up a new idea or two along the way, or maybe even found more confidence in the approach you're using. Indeed, perhaps you realized that you're already doing what the other cool kids are doing. At the same time, I appreciate that seeing varying tools may inadvertently cause some paralysis among those folks trying to get started.
If all this left you wondering how to get started, I offer you this embarrassingly obvious, but painfully honest, experience report: Testing is 90% discipline. The hard part is actually sitting down and doing it on a regular basis, where regular basis means "as you're writing code". Whether it's before or after you write the code, with Test::Unit, RSpec, test/spec, or tomorrow's testing framework, using mocks, stubs, or neither of the above is an implementation detail. Now, the tools and techniques (the other 10%) can help you keep the discipline. So choose tools that you're comfortable using on a regular basis, regardless of what's cool, and stick with them. And then focus the energy you might otherwise be spending on the worry of whether you're doing the right thing on the thing that matters most: Making great software.
(Sat Sep 08, 2007) [/Rails] #
It's not a trick question, and there is no right or wrong answer. The state of
the art of Rails testing has evolved since the early days. Keeping up has
proven both rewarding and at the same time a bit of a struggle. I'm
continually refining the way I write tests to help break writing the code down
into small, safe steps and defer decisions with confidence. And I'm generally
curious to see how you're testing controllers these days.
Why controllers? Simply because they feel more challenging to test than
models. And folks who are perfectly fine with test-driven development at the
model level are often overcome with reluctance and frustration when they start
coding controllers. The hesitation has an upside: It prompts one to move
business logic into models, where the logic belongs and where it's easy to
test.
However, the frustration has a downside. Having fattened up the models, it's
easy to then throw in the towel when it comes to testing controllers. But
given that we're writing web applications—and controllers are central to
how the world accesses our models—it seems fairly important to test
controllers. That means being able to easily flesh out what can happen at a
specific place: Given a particular state, if you poke a controller action in a
particular way, what happens to the state (model objects, the session, visible
display, etc.)?
Assume, for example, that you have the following simple create action in a controller, or assume you want to use TDD to force the writing of a similar create action:
class MenuItemsController < ApplicationController
def create
@menu_item = MenuItem.new(params[:menu_item])
if @menu_item.save
flash[:notice] = 'MenuItem was successfully created.'
redirect_to menu_items_url
else
render :action => :new
end
end
def new
@menu_item = MenuItem.new
end
end
Nothing exciting going on here. It's basically the same create
action you've been writing since Rails 0.10.0. (I've removed the use of
respond_to in this example just to keep it as simple as possible.)
Testing this action isn't especially difficult, but as an example it allows us to focus
on style. And in this brave new REST world where most controllers will have a
create action similar to the one above, style matters. If the
mechanics of writing a test get in the way of describing behavior we aim to
implement, they'll we'll hate writing tests in exactly the way programmers are
supposed to hate testing.
So, how would you go about validating the behavior of the create
action? I'm hoping that by soliciting solutions we can all learn something. To
help get things started, I offer the following styles that I've observed
across a number of projects.
The Classic Cheeseburger
Your basic, get 'er done functional test topped with an
assert_difference custom assertion (the cheese) that checks
whether a new menu item was created in the database.
class MenuItemsControllerTest < Test::Unit::TestCase
def setup
@controller = MenuItemsController.new
@request = ActionController::TestRequest.new
@response = ActionController::TestResponse.new
end
def test_create_with_valid_menu_item
assert_difference(MenuItem, :count, 1) do
post :create, :menu_item => {:name => 'Classic',
:price => 4.99}
end
assert_not_nil assigns(:menu_item)
assert_not_nil flash[:notice]
assert_redirected_to menu_items_url
end
def test_create_with_invalid_menu_item
assert_difference(MenuItem, :count, 0) do
post :create, :menu_item => { }
end
assert_not_nil assigns(:menu_item)
assert_nil flash[:notice]
assert_response :success
assert_template 'new'
end
end
Hold The Onions
This is the classic cheeseburger, marinated with FlexMock to remove the
bitter aftertaste of the database. Tastes best with a healthy side dish of
unit tests that check model validations, so the functional test doesn't have
to.
require 'flexmock/test_unit'
class MenuItemsControllerTest < Test::Unit::TestCase
def setup
@controller = MenuItemsController.new
@request = ActionController::TestRequest.new
@response = ActionController::TestResponse.new
@attributes = { 'name' => 'No Onions', 'price' => 4.99 }
@menu_item = flexmock(MenuItem.new(@attributes))
flexmock(MenuItem).should_receive(:new).with(@attributes).once.
and_return(@menu_item)
end
def test_create_with_valid_menu_item
@menu_item.should_receive(:save).with().once.and_return(true)
post :create, :menu_item => @attributes
assert_equal @menu_item, assigns(:menu_item)
assert_not_nil flash[:notice]
assert_redirected_to menu_items_url
end
def test_create_with_invalid_menu_item
@menu_item.should_receive(:save).with().once.and_return(false)
post :create, :menu_item => @attributes
assert_equal @menu_item, assigns(:menu_item)
assert_nil flash[:notice]
assert_response :success
assert_template 'new'
end
end
The Daily Spec-ial
A light, sugary topping of test/spec on an old favorite, with a distinct flavor of BDD.
require 'test/spec'
require 'test/spec/rails'
require 'flexmock/test_unit'
context 'Creating a new menu item' do
use_controller MenuItemsController
setup do
@attributes = { 'name' => 'Daily Special', 'price' => 4.99 }
@menu_item = flexmock(MenuItem.new(@attributes))
flexmock(MenuItem).should_receive(:new).with(@attributes).once.
and_return(@menu_item)
end
specify 'should redirect to index with a notice on successful save' do
@menu_item.should_receive(:save).with().once.and_return(true)
post :create, :menu_item => @attributes
assigns(:menu_item).should.equal @menu_item
flash[:notice].should.not.be.nil
should.redirect_to menu_items_url
end
specify 'should re-render new template on failed save' do
@menu_item.should_receive(:save).with().once.and_return(false)
post :create, :menu_item => @attributes
assigns(:menu_item).should.equal @menu_item
flash[:notice].should.be.nil
status.should.be :success
template.should.be 'new'
end
end
The Whole Enchilada
This ain't no burger. It's an entirely different kind of (test-lingo-free) taste, smothered in a spicy RSpec syntax and served with a side of refried mocking.
require File.dirname(__FILE__) + '/../spec_helper'
describe MenuItemsController, 'Creating a new menu item' do
before do
@attributes = {'name' => "Enchilada", 'price' => 4.99}
@menu_item = mock_model(MenuItem)
MenuItem.should_receive(:new).with(@attributes).once.
and_return(@menu_item)
end
it 'should redirect to index with a notice on successful save' do
@menu_item.should_receive(:save).with().once.and_return(true)
post :create, :menu_item => @attributes
assigns[:menu_item].should be(@menu_item)
flash[:notice].should_not be(nil)
response.should redirect_to(menu_items_url)
end
it 'should re-render new template on failed save' do
@menu_item.should_receive(:save).with().once.and_return(false)
post :create, :menu_item => @attributes
assigns[:menu_item].should be(@menu_item)
flash[:notice].should be(nil)
response.should be_success
response.should render_template('new')
end
end
This menu of controller testing styles is far from comprehensive, and it's not
intended to be a subjective comparison. I specifically tried to introduce
subtle variations along the way, while preserving the initial goal of testing
the create action. Each style has its pros and cons. Perhaps just
seeing a few different styles will trigger new ideas.
Now it's your turn to blog up a solution. Which tools and techniques are working for you? Send me links and I'll write a follow-up post summarizing the results. As well, I'm looking forward to seeing how Jim and Joe solve this in their Test-Driven Development with Rails Studio next month. I'd enjoy the opportunity to chat with you there.
(Wed Sep 05, 2007) [/Rails] #
Just for fun, here are the Rails plugins I currently have installed:
(Yup, I'm a minimalist when it comes to plugins.)
As with my gem
survey, this plugin list may be interesting to look back on in years (and
months) to come.
Which Rails plugins are you sporting?
(Mon Sep 03, 2007) [/Rails] #
In honor of Labor Day, here's a hack that might save you some. I spend
a lot of time in the Rails console, and most of that time I'm finding
stuff. Typing find is so... laborious. But drop this
in your ~/.irbrc file, and your work is made light.
# Creates shortcut methods for finding models.
def define_model_find_shortcuts
model_files = Dir.glob("app/models/**/*.rb")
table_names = model_files.map { |f| File.basename(f).split('.')[0..-2].join }
table_names.each do |table_name|
Object.instance_eval do
define_method(table_name) do |*args|
table_name.camelize.constantize.send(:find, *args)
end
end
end
end
# Called when the irb session is ready, after
# the Rails goodies used above have been loaded.
IRB.conf[:IRB_RC] = Proc.new { define_model_find_shortcuts }
Now you can find stuff using shortcut methods that are named after your
models:
$ script/console
Loading development environment.
>> event(1)
>> person(:all)
>> tag(:all, :conditions => {:name => "it"})
You get the picture. Just a simple, naive hack that pays for itself four characters at a time.
(Tue May 22, 2007) [/Rails] #
The Rails community raised over $33,000 in charitable donations during this year's RailsConf. Your conference could make a big impact, too!
If you're planning to run or attend a conference, please consider making it a fund-raising event. It's easy to do, and the payback can make a significant difference in the lives of people around the world. I'm more than happy to answer any questions anyone might have about how fund raising was organized during RailsConf.
A great big THANK YOU to everyone who participated!
(Wed May 02, 2007) [/Rails] #
What do you have cooking on your Rails project? The community wants to know!
When the first Rails Recipes book was written, most of the Rails knowledge was concentrated in a small group of experts. These days, with new Rails applications being launched almost weekly and so many different problems being solved, the State of the Art is more evenly spread across the community. In order to capture what's happening in the Rails community, we'd like the second Rails Recipe book to incorporate advanced recipes from the community's best and brightest.
So here's your chance to share tasty dishes that others may have never imagined they could make. We (Chad and I) will help you prepare your recipe for inclusion in the upcoming Advanced Rails Recipes book, including reviewing and testing the code, and you'll get all the credit.
To propose a recipe idea, here's all we need from you:
- A paragraph or two describing the problem that the recipe solves
- A sentence or two indicating what the solution will be
- You should already have working code. You can send some along if it helps convey your idea.
Email your idea to us at railsrecipes@gmail.com by May 25th (but if you've already got an idea in mind, go ahead and send it today!). We'll review your idea and let you know by May 31st if we can fit it in the book. After that, we'll start working together to get your recipe into the book!
If your recipe is selected, your name, bio, and link to your site/blog will appear in the book. And you'll receive a complimentary copy of the book, of course.
We look forward to working with you...
(Thu Mar 29, 2007) [/Rails] #
If you're still a bit confused about all this REST stuff in Rails, Dave Thomas just posted a great overview of how we got to where we are, and perhaps where we'll end up. And don't feel bad if you're not up to speed on what it means to build RESTful Rails apps. In truth, we're all still learning what that means, and I suspect we have a long way to go.
I've enjoyed the conventions that fall out of the REST style of design. Things like the named routes and the Simply Helpful plugin tend to give an application a good consistent feel to it. Converting my traditional Rails apps to the REST style has largely been an exercise in removing code, and refactoring for the better what's left.
It's worth point out though that you don't have to use the respond_to stanza to get these benefits. I'll take it a step further: Unless you plan to support non-HTML clients in the near future, and you understand their needs, then I'd recommend not using respond_to. It works perfectly well, but it feels speculative to include respond_to stanzas such as this one in all my controller actions:
def show
@event = Event.find(params[:id])
respond_to do |format|
format.html # show.rhtml
format.xml { render :xml => @event.to_xml }
end
end
Until I need to expose events as XML, I prefer to keep things simple:
def show
@event = Event.find(params[:id])
end
But, you say, isn't the respond_to block a small price to pay for future extensibility? And the scaffold_resource generator creates it for you anyway. So why not just leave it in? Simply put, it's code I don't need right now. And if it's not carrying its weight in my application today, then I prefer to remove it rather than work around it. If tomorrow I need to create an ActiveResource client for a particular set of resources, I'll add respond_to blocks in for those resources. At that time I'll probably also have a better understanding of what goes in those blocks.
All this to say, you don't have to use all the REST features of Rails to take advantage of the design conventions. At the same time, don't ignore what's going on with REST and Rails because you'll likely miss out on lots of good stuff to come.