Archive for May, 2011

RSpec2, rspec-rails and #route_to

No Comments »

If you want to spec out your routes you'll need:

#console
$ mkdir ./spec/routing
$ touch ./spec/routing/users_spec.rb

Let's say you have this in `routes.rb`:

#./config/routes.rb
⋮
get "users/:id" => 'users#show'
⋮

In your routing spec you'll want to make sure you are testing numeric `:id` values as strings since that's what they really are in this instance.

#./spec/routing/users_spec.rb
require 'spec_helper'

describe "routing to users" do
  it "routes /users/:id to users#show for id" do
    { :get => "/users/42" }
      .should route_to(:controller => "users", :action => "show", :id => "42")
  end
  it "should not expose all users" do
    { :get => "/users" }.should_not be_routable
  end
end

Meaning if you did the following:

#./spec/routing/users_spec.rb
⋮
{ :get => "/users/42" }.should route_to(:controller => "users", :action => "show", :id => 42)
⋮

... its going to keep failing and you may resort to testing routes you know that work the same exact way and then you will be driven further and further from sanity.


TextMate, Haml, and inconsistent tabs

No Comments »

So each time I setup a new Mac with TextMate this always bites me:

Inconsistent indentation: 1 tab was used for indentation, but the rest of the document was indented using 2 spaces.

Then I go digging into ⌘, (TextMate's preferences) trying to figure out where the hell the "use spaces instead of tabs" is buried and scratch my head.

If you look at the bottom of an editor window in TextMate you will see

Tab Size: 2

If you click on that you can check the option Soft Tabs (Spaces) and we are again at peace with the TextMate.


Recaptcha, Devise, and Rails 3

No Comments »

My friend and I are working on a custom system where it is very important there is a low barrier to registration. I'm not keen on OpenID as I've read some of the headaches people have with reliability and I think for this particular system our own registration would be fine. I've always been a fan of Devise and we figured that Devise plus a Captcha would do the trick. We don't want our users to have to click on a verification link via email to activate their account, or at least not right away.

To that end we've settled on the following gems for our Rails 3 application:

Devise
Recaptcha

We went through some difficulty at first trying to monkey patch Devise's controllers because we didn't want to have copies of views floating around. That didn't work. Then I tried forking Devise and I learned that adding `self` methods to controllers makes big problems (or at least it did with Devise's controllers, I assume this is a Rails thing I'm just not familiar with). Then I came across this on the Devise github wiki which is essentially what I am regurgitating here, step by step, with my observations for Rails 3.

Without further ado:

#Gemfile
⋮
gem 'recaptcha', :require => 'recaptcha/rails'
gem 'devise'
⋮
#console
$ bundle install

You will need to sign up for an API key for Recaptcha - assuming you are using that service and the gem I am. (With a bit of effort other Captcha gems could be used instead.) You'll also need to configure the Recaptcha gem:

#./config/initializes/recaptcha.rb
# from github.com/ambethia/recaptcha README.rdoc
Recaptcha.configure do |config|
    config.public_key  = '6Lc6BAAAAAAAAChqRbQZcn_yyyyyyyyyyyyyyyyy'
    config.private_key = '6Lc6BAAAAAAAAKN3DRm6VA_xxxxxxxxxxxxxxxxx'
  end

Be aware that if you host your project on a public repos you will want to add "./config/initializers/recaptcha.rb" to your `.gitignore` file and share the file amongst team members manually. (Or you can set environment variables as per Recaptcha's README.rdoc.)

The Recaptcha gem relies on `Net::HTTP` to perform its verification of the generated Captcha. I didn't see a `require "net/http"` anywhere except the tests and `init.rb` so I assume its common that this is included by default in Rails - probably 2.x. In my particular Rails 3 project, however, I had to add it myself as I don't think `init.rb` was included along any execution path:

#./config/application.rb
⋮
require 'net/http'
⋮

Now you'll have to go about setting up Devise. In this case I'm using the vanilla install, so:

#console
$ rails g devise:install
$ rake db:migrate

Then let's create our custom `RegistrationsController` which is necessary to integrate the Recaptcha gem's verification: (I created a new empty file and did not use any Rails generators for this part.)

#./app/controllers/registrations_controller.rb
class RegistrationsController < Devise::RegistrationsController
  def create
      if verify_recaptcha
        super
      else
        build_resource
        clean_up_passwords(resource)
        flash[:alert] = "There was an error with the recaptcha code below. Please re-enter the code and click submit."
        render_with_scope :new
      end
    end
end

But that's not all! We have to instruct Devise to use our custom controller and fortunately Devise will still use all its default controllers for anything we do not explicitly specify:

#./config/routes.rb
⋮
devise_for :users, :controllers => { :registrations => "registrations" }
⋮

And as we discovered when you specify your own controller there is no easy way to do a view path redirection - Rails controllers appear to be very inflexible in this way. We're going to have to generate Devise's views anyway, but then we're also going to have to copy the generated `registrations` new and edit views to the path Rails expects them to exist at.

#console
$ rails g devise:views
$ mkdir ./app/views/registrations
$ cp -r ./app/views/devise/registrations/*.html.erb ./app/views/registrations/

Let's add the code necessary to integrate the Recaptcha UI elements with our new registration view:

#./app/views/registrations/new.html.erb
⋮
<%= recaptcha_tags %>
<p><%= f.submit "Sign up" %></p>
⋮

And that should be it! Fire up your Rails server and go to http://localhost:3000/users/sign_up and your form should contain the Recaptcha UI elements. Fill the form out, roll face on keyboard for the Captcha verification, click submit and you should get an error message. Try again by this time entering the actual value from the Captcha and your account should be created.

You may be wondering about automated testing, especially with a browser controlled by Selenium. Browsing through the Recaptcha source code for verify.rb I discovered that the validation of the Recaptcha code is ignored when certain environment variables are set. In fact an array is defined at `Recaptcha::SKIP_VERIFY_ENV` which contains "test" and "cucumber" by default. So as long as your rake tasks are setting one of those environment variables OR you launch:

#console
$ rails s RAILS_ENV=test

... then Captcha validation should not occur.


Guard, RSpec 2, and Growl

No Comments »

Guard is like autotest (part of ZenTest) except its a bit more of a multipurpose tool. You can use it to fire off CSS creation for Sass/Scss, compile *.coffee files into *.js, and of course watch for changes to *_spec.rb and their associated files among other things. Growl is a visual notification tool popular on OS X. When you combine these two you will receive visual notifications that popup as your specs fail or pass without having to switch to the command line - and yes it only runs those that have changed. (You can configure where and how Growl displays its notifications in the Growl system prefs pane located under the "Other" category.)

In order to see the Growl notifications you have to do several things. Installing Growl is one of them, but you will also need a couple more gems in your Gemfile (`rb-fsevent` for performance friendly polling of file system updates and `growl` which is the gem that interfaces with the CLI `growlnotify`). Finally in the Growl disk image you had to download to install Growl find `Extras/growlnotify/growlnotify.pkg` and run that installer - this is the command line tool. You can verify the installation by running `growlnotify` from Terminal, entering a message when prompted, and then hitting Ctrl+D to post the message to Growl.

So my Gemfile ended up looking like this:

# Gemfile
⋮
group :development do
  ⋮
  gem 'guard'
  gem 'guard-rspec'
  gem 'guard-bundler'

  if RUBY_PLATFORM.downcase.include?("darwin")
    gem 'rb-fsevent'
    gem 'growl' # also install growlnotify from the Extras/growlnotify/growlnotify.pkg in Growl disk image
  end
end
⋮

Since Growl is Mac OS X the `if` test only installs those gems for the development profile on OS X. (There are Windows and Linux equivalents that I obviously have not had to install.)

After you have ran `bundle install` you can then run:

#console
$ guard init rspec

...which will generate a sample RSpec-themed Guardfile in the current directory. This Guardfile has some examples that may work for you out of the box - here's mine with some modification and support for the other guarded files:

# Guardfile

guard 'bundler' do
	watch('Gemfile')
end

guard 'rspec', :version => 2, :cli => '--color --format doc' do
  watch(%r{^spec/.+_spec\.rb})
  watch(%r{^lib/(.+)\.rb}) { |m| "spec/lib/#{m[1]}_spec.rb" }
  watch('spec/spec_helper.rb') { "spec" }

  # rails
  watch (%r{^spec/models/.+_spec\.rb}) { "spec/models" }
  watch('spec/spec_helper.rb') { "spec" }
  watch('config/routes.rb') { "spec/routing" }
  watch(%r{^app/controllers/.+_controller\.rb}) { "spec/controllers" }
  watch(%r{^spec/.+_spec\.rb})
  watch(%r{^app/(.+)\.rb}) { |m| "spec/#{m[1]}_spec.rb" }
  watch(%r{^lib/(.+)\.rb}) { |m| "spec/lib/#{m[1]}_spec.rb" }
  #watch(%r{^app/controllers/(.+)_(controller)\.rb}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] }
end

The 'bundler' block does what you might guess - if your Gemfile is saved (yes `touch`/mtime change is enough, a file CRC change is not required) `bundle install` is executed.

In order to start Guard, simply execute `guard` in the directory with your `Guardfile`. All path patterns in the Guardfile are relative to the file itself. You should see immediate output as Guard attempts to get a clean run of your specs.

If you see nothing - I had this problem - run `rspec spec/controllers` .. through each folder under spec and see where your problem lies. In my case it was because one of the controller specs generated before I switched to `factory_girl` had `fixtures :all` in it; removing that line seemed to fix the problem.

As files or their associated specs are changed your specs will execute and you will receive Growl notifications. Failure messages should include a red "X" icon so you can easily tell red from green.


factory_girl, RSpec 2 and Rails 3

No Comments »

I've done my fair share of TDD and unit tests with the caveat that those activities were written using C# on the .NET platform.  A lot of static typing, interfaces, and dependency injection.  There is the golden rule you must never, ever for any reason violate and that is of course trampling your separation of concerns while writing tests.  I was appalled to say the least when I started working with Rails that this golden rule is broken regularly.  So regularly in fact that it seems almost difficult to not break it.  And why not?  For a lot of these web page/form/database applications the database is really part of the full stack and using SQLite isn't such a test performance killer.  (You should be testing against whatever your production database of choice happens to be, but that is another discussion entirely.  Suffice it to say you can do this while developing a Rails application with little penalty.)

So let's cave into peer pressure, step all over the separation of concerns rule for a little bit, and get a grasp on how you might approach this problem in a way that may make you feel not so dirty.

In a C# unit test I usually tend to instantiate an instance of a domain model class and populate it with values necessary to satisfy the test. Then I dependency inject that in and I'm ready to go. A lot of ceremony to say the least. We have a number of options available to us in Rails to provide us with test data. Let's take a look:

seeds.rb

./db/seeds.rb

... is typically where you might store nonvolatile system data, like a list of states.  Test data does not belong here.

fixtures

./test/fixtures
./spec/fixtures

... is a somewhat convenient way of achieving what we want to do but using this approach will create a lot of Internet grumbling.  You can even use ERb templating to provide dynamic values for some of your attributes.  I'm not going to into anymore detail because, well, Internet grumbling.

factory_girl

... is according to its Github project page is a "[f]ixture replacement for focused and readable tests."  If you are new to Rails, like me, it might sound a little confusing.  "Fixture replacement?  Why can't I just use the defaults and just get to coding?"  Certainly you could do that, but then if you were, you wouldn't be reading this.  Also we already covered fixtures, above.  We don't use those, it makes the Internet grumble, remember?

Here's a trivial example based on a subset of some prototyping we were doing at work.  Maybe you don't need to TDD your scopes, but maybe you are new to Rails and Ruby and you're thinking "scopes?" or "I am told all of this works but... well let me see for myself."

Create a new Rails 3 application named "fgdemo":

# console
$ cd ~/Projects
$ rails new fgdemo

Don't worry - its okay to have tons of Rails apps all up in your ~.

# console
$ cd fgdemo
$ mate .

... or vim or whatever you use to scratch text files into your hard disk.

Open your Gemfile - this is a file Rails creates for you and its part of bundler and add these lines:

# ./Gemfile
⋮
group :development, :test do
  gem 'rspec-rails'
  gem 'factory_girl_rails'
  gem 'rspec_multi_matchers'
end
group :development do
  # required to generate factory_girl fixtures under rails 3 as of 20110518
  gem 'rails3-generators'
end
⋮

The nice thing about Bundler is that it will install prerequisites for you. Ergo `rspec-rails` will install `rspec`.

We've got a couple groups defined here; these groups correspond to your RAILS_ENV environment variable. This is important during deployment on production, during testing where ever, and of course during development. We're working in the default environment which is (you guessed it) development so we don't need to do anything like explicitly pass a `RAILS_ENV=development` on the command line. And when we move onto executing our specs as part of our testing procedure you won't have to RAILS_ENV=test either. (But maybe you are wondering what passing RAILS_ENV on the command line looks like? Guess what: `rake db:migrate RAILS_ENV=test`.)

`rspec_multi_matchers` isn't strictly necessary but I find it lets me write more expressive `should` statements in my specs when dealing with enumerables. `rails3-generators` will hook into your Rails 3 generators to create factory_girl factories for each model class you create... but not just yet. This is more a convenience than anything else.

Now we execute Bundler which reads our Gemfile and installs the necessary gems onto our system:

# console
$ bundle

And let's install RSpec into our project - this just creates some files and modifies our application configuration.

# console
$ rails g rspec:install

(`g` is shorthand for `generate` - you may use either or.)

Edit your Application's configuration:

# ./config/application.rb
⋮
# these lines go within the Application class definition
config.generators do |g|
  g.test_framework :rspec, :fixture => true, :views => false, :fixture_replacement => :factory_girl, :view_specs => false
  g.fixture_replacement :factory_girl, :dir => 'spec/factories'
end
⋮

(Without the :dir hash key above rails3-generators will infuriatingly create factories at test/factories.)
Then let's make some changes to our `spec_helper.rb` file. This file should be required at the top of every `_spec.rb` file you generate or author but there's no magic that autoloads it behind the scenes. (The Rails generator will hook into the rspec-rails stuff and actually add a `require 'spec_helper'` line at the top of each `_spec.rb` file it generates for you.)

# ./spec/spec_helper.rb
⋮
require 'factory_girl'
# then look for a line like the one below and comment it out
#config.fixture_path = "#{::Rails.root}/spec/fixtures"
⋮

Now we will make a model!

# console
rails g model AdTargeting ad_id:integer start_date:datetime end_date:datetime

A column named `id` will be generated automatically for you (among other things). `ad_id` is a key value referring out to another domain class in our prototype and the date fields are what we'll be scoping here as part of our spec/factory_girl exercise. The end result of this command will be a number of things that I'm not going to go into detail here.

Next let's update our development database schema:

# console
$ rake db:migrate

Since we're practicing good TDD let's go write a spec:

# ./spec/models/ad_targeting_spec.rb
require 'spec_helper'

describe AdTargeting do
  describe "date_between scope" do
    it "returns only the :valid_ad_with_date_range_for_today" do
      Factory.create(:invalid_ad_for_today)
      Factory.create(:valid_ad_with_date_range_for_today)
      AdTargeting.date_between(DateTime.current).should have(1).things
    end
  end
end

That may not make a whole lot of sense to you if you have no RSpec background but you should be able to get the gist if you're coming to Rails from another framework. So when we run this at the console:

# console
$ rake spec

You're going to be red because we haven't yet updated the `.\spec\factories\ad_targeting.rb` file. Let's do that now:

# ./spec/factories/ad_targeting.rb
Factory.define :empty_ad, :class => AdTargeting do |a|
  a.ad_id        0
  a.start_date   DateTime.current.beginning_of_day
  a.end_date     DateTime.current.end_of_day
  # other attribute defaults ...
end

Factory.define :invalid_ad_for_today, :parent => :empty_ad do |a|
  a.ad_id        2
  a.start_date   DateTime.current.beginning_of_day - 2
  a.end_date     DateTime.current.end_of_day - 2
end

Factory.define :valid_ad_with_date_range_for_today, :parent => :empty_ad do |a|
  a.ad_id        3
  a.start_date   DateTime.current.beginning_of_day - 2
  a.end_date     DateTime.current.end_of_day + 2
end

So the code above is a bit more lengthy than it absolutely has to be, but my goal here is to help you feel not-so-dirty for violating separation of concerns. And how do we do that? Misdirection! See, our factory definitions are inheriting from a factory definition!

Let's run our specs again:

# console
$ rake spec

Red again as we have not yet defined our scope.

# ./app/models/ad_targeting.rb
class AdTargeting < ActiveRecord::Base
  scope :date_between, lambda { |date| where('start_date <= ? and end_date >= ?', date, date) }
end

While still trivial I wanted to get you thinking about what you can do with scopes beyond just the regular "field = value" approach. (Before you start trying to use scopes to OR conditions together its better to know that as of Rails 3 you cannot OR. You'll need to look at squeel in order to do that.)

And run the specs yet again...

# console
$ rake spec

Green! Awesome. So... what is this factory_girl doing behind the scenes? Are we really violating separation of concerns? Is the full stack being exercised? Let's let the log file speak for itself. Open a new terminal and...

# console
$ cd ~/Projects/fgdemo
$ tail -f ./log/test.log

Then run those specs again (in your original terminal)...

# console
$ rake spec

Watching the tail of the log file you will see a test database is created and your factory_girl definitions will be inserted into the appropriate tables when `Factory.create(:symbol)` is invoked. Yes the appropriate tables will be cleaned up at the end of each test so you don't need to worry about the order in which your tests execute.