James Wilding's Weblog

Category: Web Development

How To Use RSpec with Rails 3

Because Rails 3 is still pretty new (less than a week old as I write this), there’s relatively little documentation about how to use RSpec with this latest version of Rails [update: what I should have said is that docs for Rspec and Rails 3 are good, but perhaps less well-organised than for older versions of Rails. See comments below.].What I want to do here is bring together the instructions I’ve found on using RSpec with Rails 3, and share some tips on how to get your BDD cycle running smoothly.

[Note: from this point on, I'll use "Rails" to mean "Rails 3"]

Installing Rspec for Rails 3

The first new thing to be aware of is the rspec-rails gem; because Rails 3 is much more modular and extensible than previous versions of the framework, this one gem can do all the work of plugging RSpec into your Rails apps.

Install the gem thus:

gem install rspec-rails --pre

The --pre tells Rubygems to install the beta version of the gem, which you’ll need for Rails 3 development.

The next step is to tell your Rails app to use the rspec-rails gem; this is done quite simply in your app’s Bundle file. Because you don’t need RSpec in production, add the gem to the development and test groups only:

# in path/to/your/app/Bundle
group :development, :test do
  # the version number may be different for you.
  # Use gem list rspec-rails --local on your command line
  # to get the exact version number.
  gem 'rspec-rails', '2.0.0.beta.20'
end

The final step is to run the following in the root of your Rails app:

script/rails generate rspec:install

This sets up a spec/ folder and some helper files.

Using RSpec

Now you’ve installed the rspec-rails gem, using RSpec is actually really simple. The gem tells Rails to use rspec to generate test files, which means that generators will create spec files without you having to pass in any extra options (if you check the options for rails generate model, you’ll see that there’s a --test-framework option. You don’t need to use this to specify RSpec; after installing rspec-rails, RSpec will be used by default).

So, to generate a Person model with corresponding person_spec.rb, all you need to do is this (the same applies to controllers, helpers, etc):

rails generate model Person

Check out spec/models/person_spec.rb to see the stub spec for your Person model. That’s really all you need to know to start using RSpec on Rails 3, but do checkout the rspec-rails repository on Github: the README gives more in-depth explanations of a lot of the points I’ve made here, and also has extra tips on writing specs for controllers, views, responses, and routes.

Pro Tips

A few cools things I’ve found using RSpec in my new Rails apps:

Helper Specs

Helpers specs have access to a special object in the helper object, which includes your helper. For example, in person_helper_spec.rb the helper object has access to all the methods in PersonHelper. This makes it really easy to test your helper methods:

# Assumes a PersonHelper with a #speak method.
describe PersonHelper do
  describe '#speak' do
    # roughly equivalent to:
    #
    # helper = Object.new
    # helper.send :include, PersonHelper
    # helper.speak.should == 'Hello'
    it 'says Hello' do
      helper.speak.should == 'Hello'
    end
  end
end

Spec Support Files

Although it’s not there by default, if you create a folder spec/support, then every file under that folder will be required when you run specs. This is really useful if you want to keep custom RSpec matchers, mocks, or other supporting code, in separate files (for example, you might want to keep custom matcher code in spec/support/my_matcher.rb and mocks in spec/support/mocks.rb).

Hopefully this post has helped you can a handle on how to use RSpec with the latest version of Rails. If you spot any other “pro tips”, post them in the comments! Thanks for reading.

Reasons To Start Using Rails 3 Now

Tempted to wait for Rails 3.1, for security updates, more features, or just to give yourself time to learn new APIs? Don’t be. Rails 3 is ready to use now; here are three reasons to start using it to build your web apps.

Framework Agnosticism

This is huge. In previous versions of Rails, we were locked into ActiveRecord for database abstraction, and Prototype for Javascript. Both are fine choices, but Rails 3 gives you the option to use other libraries if you want to (you can stick with the defaults as before too).

I love jQuery, so I’ll be using that in all my Rails 3 projects instead of Prototype. You could also use DataMapper or Sequel in place of ActiveRecord, if you so desire. The important thing is that Rails 3 is more of a framework in the truest sense: a codebase into which we can plug our own favourite libraries, rather than a monolithic API without much flexibility when it comes to libraries.

Improved Plugin API

What better evidence do you need than this: Rails 3 itself is built on its own plugin API (internals like ActiveRecord and ActionMailer use the same code as third-party plugins to do their stuff — you can read more about this on the Rails blog). This makes things about 100% easier for plugin developers, and stops us from feeling like we sometimes have to dig into the dark depths of Rails, where we shouldn’t be, to make our plugins work.

More Thoughtful Code

As with any update, a huge amount of thought has gone into Rails 3 and I’ve been particularly impressed by the clarity of code turned out on this release. It feels like Rails has got leaner, cleaner, and better structured; this can only be good for us Rails developers in the long run: cleaner framework code means better performance and makes it easier to troubleshoot bugs (in the Rails code and in your own). Check out the new ActionMailer API for an example: it’s a great improvement over Rails 2.x — no longer the model/controller mongrel that it used to be!

Those are my top three reasons to use Rails 3 now. If you’re wavering, jump in and give it a try!

Ruby’s OpenStruct: An Introduction

OpenStructs are magic — they’re like hashes, but far less fussy about what methods you throw at them. The key feature of OpenStructs is this: they allow you to arbitrarily set and access attributes for your models, on the fly.

Here’s an example:

require 'ostruct'

s = OpenStruct.new
s.name # => nil
s.name = 'James'
s.name # => 'James'

Note that we don’t need to initialise the ‘name’ attribute: we just set the value and Ruby takes care of the rest. Want to add a new attribute? Easy.

s.age # => nil
s.age = 29
s.age # => 29

We can add any attribute we want to our objects, on the fly, and different instances of the OpenStruct class can have different attributes. Importantly, we don’t have to worry about what methods we send to our OpenStructs, because anything that doesn’t have a value just returns nil 1:

s.location # => nil
s.hobbies # => nil

Clearing Values

To unset a value, we can do one of two things: in most cases, it’s the obvious…

2.age # => 29
s.age = nil
s.age # => nil

…but to grab the value before setting the attribute back to nil, we can use delete_field 2:

# Returns 29, and sets 'age' to nil
age = s.delete_field('age')
age # => 29
s.age # => nil

Initialising With Values

We can create new OpenStruct objects with attribute/value pairs by providing a hash:

s = OpenStruct.new(:name => 'James', :occupation => 'Rubyist')
s.name # => 'James'
s.occupation # => 'Rubyist'

How It Works

Behind the scenes, Ruby uses a hash to implement OpenStruct’s easy access so we get all the flexibility of a hash with a nice, clean, method-style access laid on top. Don’t worry about the details: just throw whatever you want into your OpenStructs, and let Ruby take care of your data.

Advanced Initialization

If you want object with the flexibility of OpenStructs but with default values for some attributes then something like the following would work:

require 'ostruct'

class MyStruct < OpenStruct
  # Hard-code age and status
  def initialize(hash = {})
    super hash.merge(:age => 29, :role => 'Manager')
  end
end

s = MyStruct.new(:name => 'Bob')
s.name # => 'Bob'
s.age # => 29
s.role # => 'Manager'

# We can override the default values
s = MyStruct.new(:age => 45)
s.age # => 45

OpenStruct vs Struct

Ruby’s Struct is subtly, but importantly, different to OpenStruct. How to use Struct is another blog post, but put simply Structs are less flexible. Check the Struct docs for more info :)

Notes:

  1. This can be a curse as well as a blessing, because you’ll often get nil when you’d expect Ruby to raise a NoMethodError. Checkout OpenStruct’s method_missing to see what’s going on behind the scenes.
  2. This is one of OpenStruct’s few built-in instance methods which don’t act as attribute accessors. Check the docs for details.

Paperclip on Rails 3

You want to run Rails 3. You want to use the excellent Paperclip plugin to mess with your attached files. Not a problem.

It Just Works

Paperclip on Rails 3 now just works.

Back in February 2010, I wrote about how to use Paperclip on an early beta release of Rails 3 — back then, a few small hacks were required to get Paperclip running. Now Rails 3 is at beta 4, and it’s safe to assume that we’re as close as we can be to an official Rails release — minus a few last minute tweaks, of course — and both the framework itself and the Paperclip plugin are more stable and more compatible. The good news, then, is that it’s very easy to use the Rails 3 and Paperclip together.

How To Use Paperclip With Rails 3

These instructions are good for Rails 3 beta 4, and Paperclip 9223a917. They’ll probably work on other versions of Rails and Paperclip, too.

  1. Install Rails 3
  2. Create a new Rails app
  3. Install Paperclip
  4. Create a model
  5. Create a controller and views
  6. Define your routes

You can find the important files (controller, model, views, and routes) in this gist, and the full application code is available on Github.

Install Rails 3

Install the latest prerelease of Rails with the following:

$ gem install rails --pre

Create a new Rails app

The command for creating Rails apps has changed in Rails 3. In the bright new future, everything is done using “rails” 1:

$ rails new paperclip_example

Install Paperclip

Paperclip’s master branch is now good to go with Rails 3. Install from Github thus:

$ rails plugin install git://github.com/thoughtbot/paperclip.git

Create a model

In order to work with Paperclip, your model needs a few special database columns. In this case, I’m creating a User class which will have an attached avatar image.

$ rails generate model User \
avatar_file_name:string \
avatar_content_type:string \
avatar_file_size:integer \
avatar_updated_at:datetime
$ rake db:migrate

I use Paperclip’s has_attached_file method to define my avatar attachment:

class User < ActiveRecord::Base
  has_attached_file :avatar, :styles => {
    :thumb => '50x'
  }
end

Create a controller and views

I’m setting up a simple UsersController with index, new, and create actions (and corresponding routes). You’ll probably want to go further, but if all you’re interested in is proof-of-concept then this is enough.

$ rails generate controller Users

See this gist for the code in my UsersController, and the HTML in my views.

Define your routes

In config/routes.rb:

PaperclipExample::Application.routes.draw do |map|
  resources :users, :only => [:index, :new, :create]
  root :to => 'users#index'
end

Lastly, if you’re mapping your app’s root to a controller, remember to delete public/index.html.

And You’re Done

That’s really all there is to it. Use rails server to start your app and view the results. Did it work for you?

Notes:

  1. I’ve found that, on Mac OSX 10.6, I have to use “/usr/bin/rails” (that’s where my Rails binary lives) instead of “rails”. No doubt that will be fixed soon.

Using Ruby’s OpenStruct

Update: read “OpenStruct: An Introduction” for a detailed explanation of how to use OpenStruct in your Ruby code.

I want to share a little piece of code that I wrote today which (I think) shows how Ruby’s OpenStruct helps you write more elegant, more efficient code.

OpenStruct lets me define objects with attributes without having to write full class definitions. Pass in your attributes as a hash of values, and you get an object with accessors for those values. Here’s an example:

And here’s my code:

The aim here is to create simple models for three currencies: US Dollars, British Pounds, and Euros. I could have defined a currency class, but that seemed like overkill: OpenStruct lets me set and access attributes more easily with less code, so when I come back to this project in a year it will be clearer what I’m doing.

Once I’ve defined my currencies, I can use them in my code as I would use any other Ruby object:

You can see how OpenStructs are created like hashes but behave like objects that have attribute accessors (because they do!). Nice clean, clear, simple code.

(And if you want all your hashes to have this magic attribute accessor power, check out superhash. It’s super.)

How To Get The Contents Of A CKEditor Instance

Normally CKEditor works seamlessly with HTML forms, and you don’t need to worry about interacting with the editor directly. But sometimes you need to get the value from an editor directly, maybe to use in a Javascript function on in an AJAX call. This is how I do it:

I call this function with the element ID of the textarea which CKEditor is working with. So, for example:

To write the contents of a CKEditor to its associated textarea, you can do this:

Hello HTML5

HTML has just reached a major milestone with the publication of six working drafts of the markup language’s specification (via).

Despite being technical documents, these specs also make for interesting reading if you’re at all curious about the evolution of the web’s mother tongue. A great place to start is the history of HTML in the main HTML5 spec: there’s some detail here about how HTML5 came to be, as well as some brief information about the aborted XHTML2 effort. This section helps you understand why HTML5 exists at all.

Next, you’ll want to know what elements are available for HTML5 authors: for this, start with “The elements of HTML” (again, in the main spec). This section lists HTML5′s tags and explains how to use them; new tags such as section, nav, article, and aside are covered here.

As a web developer, you’re going to be writing HTML5, so also read “The HTML syntax” for more detail on how HTML5 documents should be structured. The section called “Writing HTML documents” is especially useful, and includes information on the doctype (previously important but now “a mostly useless, but required, header”).

If you’re put off by technical language, be reassured that the spec makes an effort to be readable by humans too: after the spec for doctype syntax, for example, we get this summary:

In other words, <!DOCTYPE HTML>, case-insensitively

Despite all the HTML5 websites out there, I think it’s really important for developers to go to the source and read the original spec: it’s surprisingly accessible, and even glancing at it will help you understand where HTML5 is coming from.

User-led Usability

I just read “Progress in Usability: Fast or Slow?” by Jakob Nielsen, and absolutely loved how he focused on the purpose of usability studies: finding out how easy it is for users to do what they want to do.

I’ve actually taken part in a few usability studies — as a participant, rather than a researcher — and what struck me looking back is how the tests focused on finding out whether users could easily do what the researchers wanted them to do. Basically, the researchers would make a list of the things they wanted a user to accomplish — making a booking, finding a contact form, performing a search — and would then test how easy it was for the participants in the test to perform those actions.

This is completely wrong.

What those researchers should have been doing is asking test participants what they wanted to do: usability tests should be framed in terms of “what do you want to do here”, not “how easily can you do what we want you to do here”. This makes tests much more open ended, but in the businesses in the real world don’t decide how people use their websites, and that sort of decision shouldn’t be made in usability tests either. Let the users decide; let the design follow.