Since Ruby on Rails 4.2 has just been released, perhaps now is a good time to review creating a shiny new Rails project. It’s not often I get to create a new project from scratch, but it’s Christmas and I’ve got a bit of downtime – and an itch I’d like to scratch! So, let’s get started.
I’m aiming to build a wee project that keeps track of OmniFocus perspectives. I’ve noticed that people are sharing their perspectives as screenshots and descriptions, in tweets and blog posts. Wouldn’t it be awesome if there was a one-stop-shop for everybody’s perspectives?
A couple of early decisions in terms of the basic starting point:
-
Chances are I’ll deploy the app onto Heroku, so it’s a no-brainer to start out by using PostgreSQL in development. (Even if I wasn’t using Heroku, personally I’d choose it over MySQL if I could!)
-
I have no design skills whatsoever, so I’ll be using Twitter Bootstrap to provide some basic styling.
-
I’ll be using RSpec for testing – both unit tests and integration tests. I’ll give Cucumber a miss, at least for now.
-
Eventually, I’d like to move away from the asset pipeline, managing client side assets separately, but I’ll save that for a future refactoring.
With that in mind, let’s get started. I’m assuming that you’ve got suitable versions of Ruby, and Git, and that you’ve got PostgreSQL running locally. If you haven’t, but you’re on a Mac, may I politely point you in the direction of homebrew, rbenv, and ruby-build? If you’re not on Mac OS X, please feel free to help out in the comments. :)
So, let’s install Rails through Rubygems:
$ gem install rails
Now we’ll create the new project, and add the generated skeleton as our initial git commit:
$ rails new perspectives -d postgresql --skip-keeps --skip-test-unit
[ ... ]
$ cd perspectives
$ git init
$ git add .
$ git commit -m 'Skeleton Rails project.'
Now let’s set up the database configuration. Edit config/database.yml
to
remove the production stanza (which is provided by our production configuration
management system!), and remove the boilerplate comments:
Let’s create the two databases, generate the initial schema, and commit the changes back to git:
$ rake db:create:all
$ rake db:migrate
$ git add .
$ git commit -m "Database configuration."
Now we’ll just tidy up the bundler dependencies in Gemfile
, just because
there’s stuff in there we’ll never use. We can always add them back in at the
point we need them after all. Gemfile
now contains just:
source 'https://rubygems.org'
gem 'rails', '~> 4.2.0'
gem 'pg'
# Asset pipeline
gem 'sass-rails', '~> 5.0'
gem 'uglifier', '>= 1.3.0'
gem 'coffee-rails', '~> 4.1.0'
gem 'jquery-rails'
group :development, :test do
gem 'byebug'
gem 'web-console', '~> 2.0'
gem 'spring'
end
Since we’ve removed the turbolinks gem, we’ll need to remove the reference to
it from app/assets/javascripts/application.js
, lest we start getting errors
on every page load. Run bundle install
again to tidy up the lock file, add
the changes to the index and commit:
$ git add .
$ git commit -m "Tidy up gem dependencies."
It’s a good idea to set the Ruby version. This tells rbenv or rvm which version of Ruby you want to use in development, and tells Heroku which one you want in production. I’m currently working with Ruby 2.1.5, so let’s set that:
$ echo '2.1.5' > .ruby-version
We’ll get bundler to pick up that same setting, just so we’re not repeating
ourselves. In Gemfile, just under the source
declaration, add:
Again, keeping with small, discrete changes, we’ll commit that:
$ git add .
$ git commit -m "Set Ruby version to 2.1.5."
We should figure out a way to run the application server. I like to use
foreman to have all the app’s components up and running in development. I
have it installed through the Heroku Toolbelt, but you can just install it
through Rubygems, with gem install foreman
. In terms of the application
server itself, unless I have a compelling reason to use something different,
I’ll use Unicorn. Create a Procfile
in the root of the project with:
web: bundle exec unicorn --port=${PORT} --config-file config/unicorn.rb
add the ‘unicorn’ gem to our bundler dependencies, and create a Unicorn
configuration file in config/unicorn.rb
:
This is pretty much cargo-culted from the Heroku documentation for using Unicorn, but it’s doing a couple of key things:
-
Translating between the TERM and QUIT signals, so that Unicorn does the right thing when Heroku’s process supervision system tells it to die.
-
Makes sure that every worker process has its own connection to the PostgreSQL database, instead of sharing one inherited from the master (which doesn’t need it at all).
Now we can run:
$ foreman start
to start up an application server, and it should allow us to visit http://localhost:5000/. Commit those changes:
$ git add .
$ git commit -m "Unicorn and foreman for the app server."
Let’s setup some testing next. My testing framework of choice is RSpec. Let’s add it, and spring support, to the Gemfile:
and run bundle install
to add them to our bundle. Now generate the rspec
binstub so it uses spring to load faster:
$ spring binstub --all
and generate the skeleton configuration for rspec:
$ rails g rspec:install
create .rspec
create spec
create spec/spec_helper.rb
create spec/rails_helper.rb
Note that two spec helpers are now generated – one that just loads and configures rspec, and one which also loads the full Rails environment. This means that, if you have code which doesn’t depend on Rails itself, you can skip loading the whole shebang, which means your tests run faster. Awesome. (Though with the help of Spring, I can’t say I’ve suffered enough from slow specs to need the separation lately.)
the spec/spec_helper.rb
file comes with a sensible default configuration
that’s commented out in an =begin
/=end
clause. They’re all useful defaults,
so just remove the =begin
and =end
, and we’re good to start testing.
Sometimes, spring gets in the way a bit, in that it needs to be manually
restarted to pick up changes. This is one of those times, so let’s restart
(well, stop – it’ll automatically start again when it needs to) it:
$ spring stop
Then run the specs:
$ rspec
Run options: include {:focus=>true}
All examples were filtered out; ignoring {:focus=>true}
No examples found.
Finished in 0.00029 seconds (files took 0.13203 seconds to load)
0 examples, 0 failures
Top 0 slowest examples (0 seconds, 0.0% of total time):
Randomized with seed 35401
That’s working nicely, so let’s commit our changes:
$ git add .
$ git commit -m "Add rspec for testing."
It’s handy to have quick feedback from tests. In particular, I like to have
tests run automatically when I make changes to a related file. Guard has my
back here, so let’s use it. Add the following to the Gemfile
:
and run bundle install
to install them. Now we can generate a skeleton
Guardfile
to configure Guard:
$ bundle exec guard init bundler rspec
The configuration in here has changed substantially since last time I generated a Guardfile! Suffice to say, all that really needs to be changed is to tell guard to use spring’s binstub to launch rspec. Change the rspec declaration to:
We can now start guard in a new shell, with:
$ bundle exec guard start
and it’ll keep an eye out for file changes, running the appropriate tests for us when a file is saved. Hit enter to run all the tests now, just to check it’s working. Tests passing? Good, let’s commit our changes:
$ git add .
$ git commit -m "Guard, for automatically running our tests."
Winning. Let’s actually start serving some content – in particular, it’s
always helpful to start with a static home page. Let’s start by specifying the
routes. Create spec/routing/pages_controller_routing_spec.rb
with:
then replace the contents of config/routes.rb
with the following to make the
specs pass:
Now we’ll specify the controller itself, in
spec/controllers/pages_controller_spec.rb
:
with the corresponding controller implementation in
app/controllers/pages_controller.rb
containing:
And, finally, a view, in app/views/pages/index.html.erb
:
Not only should our specs be green, but visiting http://localhost:5000/
should reveal the newly minted view being rendered. Just for completeness,
we’ll add a feature spec as a simple integration test to check the entire stack
is working. Create spec/features/pages_spec.rb
with:
Let’s commit our changes and move on to the final stage:
$ git add .
$ git commit -m "Add in a static home page."
The final stage is to make things look a little bit prettier, and for that
we’ll use Bootstrap. In particular, since we’re still using the asset
pipeline, in order to reduce the number of dependencies, we’ll use the SASS
port of Bootstrap. Add the following to the Gemfile
:
and run bundle install
to install it. Rename the application stylesheet so
it’s processed as sass:
git mv app/assets/stylesheets/application.css{,.scss}
and add the following to the end:
Import the Bootstrap Javascript, in app/assets/javascripts/application.js
:
Finally, create a layout suitable for bootstrap, in app/views/layouts/application.html.erb
:
It’s a bit of a mouthful, but that’s the template I wind up starting with for all my new projects. It gives me a nice title, a menu bar, flash messages, and a prettily styled body. A good start for putting together the rest of the app. Finally, commit that change.
That’s about it for getting started on a new Rails project. All in all – especially when I crib from another project – it takes about half an hour to get up and running with a full test suite, a pretty UI, and a pleasant development environment. Now it’s time to implement the first feature!