Posts Tagged ‘Ruby’

ActiveRecord new(&block) vs. new({ })

No Comments »

Assume the following ActiveRecord::Base class:

#./app/models/army_list.rb
class ArmyList < ActiveRecord::Base
  belongs_to :game_system

  validates :name, :presence => true
  validates :points, :numericality => true
  validates :game_system, :presence => true
end

I was just pulling my hair out over this spec I was writing to make sure all my validations were working properly:

#./spec/models/army_list_spec.rb
describe ArmyList do
  it "should be valid" do
    ArmyList.new(:name => "Name", :points => 1500, :game_system => Factory(:game_system)).should be_valid
  end
end

Changing it from the constructor method to the block method works however:

#army_list_spec.rb
describe ArmyList do
  it "should be valid" do
    ArmyList.new do |al|
      al.name = "Name"
      al.points = 1500
      al.game_system = Factory(:game_system)
    end
    .should be_valid
  end
end

Now that I've solved this problem it makes sense. The ActiveRecord::Base documentation for #new states

[...] valid attribute keys are determined by the column names of the associated table – hence you can’t have attributes that aren’t part of the table columns.

Because the column name is actually `game_system_id` it doesn't make sense that I'd be assigning `game_system` - an attribute that doesn't exist on the table - by using this form of the constructor.


View Specs and RSpec 2

No Comments »

I get this feeling that speccing views in Rails may be pooh-poohed but with this being my first substantive Rails project I wanted to run the full gamut. As I'm using RSpec 2 I discovered that what I thought would be a readily available tag matching assertion is just gone; this is functionality that was available in RSpec 1 but removed due to its rigidness/difficulty to maintain and the fact that Webrat has `have_selector` for performing a similar task.

But I really wanted the simplistic and easily readable `have_tag` syntax so I looked around to see what else was out there. I stumbled across rspec2-rails-view-matchers which was exactly what I was looking for.

I ended up with the following code for testing that one of my show views has the required links for navigation (among other things):

#spec/views/army_lists/show.html.haml_spec.rb
require 'spec_helper'

describe "army_lists/show.html.haml" do
  subject do
    assign(:army_list,
      stub_model(ArmyList,
        :name => "Name",
        :user => User.new do |u|
          u.display_name = "John Smith"
          u.id = 1
        end,
        :game_system => GameSystem.new do |gs|
          gs.id = 1
          gs.name = "Generic Game System"
        end
      )
    )
    render
    rendered
  end

  it { should have_tag("a", :text => "John Smith") }
  it { should have_tag("p", :text => /Name/m) }
  it { should have_tag("a", :text => /Generic Game System/m) }
end

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.


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.