Rails


6
Feb 10

Devise Rails Authentication Gem Rocks!

I’ve had a set of rather bespoke requirements for authentication on a recent project and thought I’d give Devise a go.

Devise uses Warden, which is an authentication solution build on Rack making it extremely flexible and usable across multiple frameworks e.g. Rails/Sinatra. Also, Devise is extremely modular meaning to can easily write custom “strategies” for specific behaviour.

I have used Clearance in the past which is great if you want an engine that will just work. Devise however is by far the most flexible and extensible solution that I have come across with the same ease of use as Clearance. The only thing that you don’t get with devise that you do with Clearance is the signup stage, however as this is normally custom on a per-app basis I can live with this.

One more thing to note is that Devise lets you have multiple auth systems in play e.g. one for users and one for admins.


8
Jan 10

Sexy Validation in Edge Rails (Rails 3)

I have just had my sexy validations patch accepted into Rails. Much thanks to José Valim for helping me get this applied.

The reason for the name “sexy validations” is that it gives a much more concise way of defining validation and reusing custom validator classes. Much like what sexy migrations did for defining your database schema.

Simple example of using existing Rails validations, the “sexy” way:

class Film < ActiveRecord::Base
  validates :title, :presence => true, :uniqueness => true, :length => { :maximum => 100 }
  validates :budget, :presence => true, :length => { :within => 1..10000000 }
end

The power of the “validates” method comes though, when using in conjunction with custom validators:

class IntenseFilmTitleValidator < ActiveModel::EachValidator
  def validate_each(record, attribute, value)
    record.errors[attribute] << "must start with 'The'" unless value =~ /^The/
  end
end
 
class SpendValidator < ActiveModel::EachValidator
  def validate_each(record, attribute, value)
    spend = case options[:size]
      when :big then 100000000
      when :small then 100000
    end
    record.errors[attribute] << "must not exceed #{spend}" if value > spend
  end
end
 
class Film < ActiveRecord::Base
  validates :title, :presence => true, :intense_film_title => true
  validates :budget, :spend => { :size => :big } # using custom options
end

All validations in Rails, along with other common model functionality have been extracted into ActiveModel, so you can also use validations and Validator classes without ActiveRecord e.g.

class EmailValidator < ActiveModel::EachValidator
  def validate_each(record, attribute, value)
    record.errors[attribute] << (options[:message] || "is not an email") unless
      value =~ /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i
  end
end
 
class Person
  include ActiveModel::Validations
  attr_accessor :name, :email
 
  validates :name, :presence => true, :length => { :maximum => 100 }
  validates :email, :presence => true, :email => true
end

Have fun!


16
Aug 09

Sexy Validations in Rails 3.0

Update: This patch has been applied but this information is out of date, please see: this new post.

Having had a patch accepted in Rails, I now have the bug (not a bug… the bug for writing patches… anyway).

I have just added this patch to allow for “sexy-migration-esque” validations using the new ActiveModel. Below explains the gist of it:

  class EmailValidator < ActiveRecord::Validator
    def validate
      field = options[:attr]
      record.errors[field] << "is not valid"
        unless record.send(field) =~ /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i
    end
  end
 
  # Register the custom validator in an initializer file.
  ActiveModel::Validations.register_validator :email, EmailValidator
 
  class Person < ActiveModel::Base
    attr_accessor :name, :email
 
    validates :name, :presence => true, :uniqueness => true, :length => { :maximum => 100 }
    validates :email, :presence => true, :email => true
  end

As with the previous patch I believe this adds even more flexibility which is what Rails 3 is all about. It also allows validators to be shared more easily.

Rails defaults can now also be overridden e.g.

  class RequiredValidator < ActiveRecord::Validator
    def validate
      field = options[:attr]
      record.errors[field] << "Required" if record.send(field).blank?
    end
  end
 
  ActiveModel::Validations.register_validator :email, RequiredValidator

I would appreciate a +1 on the ticket from anyone who feels this should be in Rails 3.0.


18
Dec 08

Unit testing by simplifying the problem: memoization

I see unit testing as a way to test each possible snippet of functionality and route the code in question can take. With Ruby being such a dynamic language and allowing shortcuts to common problems, sometimes it can seem somewhat of a mystery, how to test these snippets of functionality.

Using Memoization as an example:

class MyClass
  def lazy_initialized_value
    @lazy_initialized_value ||= Expensive.request
  end
end

There are actually 3 separate snippets of functionality that need testing here, however it is not immediately obvious from the example. Lets be slightly more verbose about what is actually happening:

class MyClass
  def lazy_initialized_value
    @lazy_initialized_value = Expensive.request unless @lazy_initialized_value
    @lazy_initialized_value
  end
end

Now it is much easier to see the 3 steps the code should take:

* Store result of expensive request in instance variable
* Leave instance variable alone when it is already set
* Return the value of the instance variable

Now we have this information, our tests become (using Mocha to mock external methods):

class Expensive; end
 
module Tests::MyClass
  # lazy_initialized_value
  # ----------------------
  class LazyInitializedValueTest < Test::Unit::TestCase
    def test_should_respond
      assert_respond_to MyClass.new, :lazy_initialized_value
    end
 
    def test_should_store_result_of_expensive_request_in_instance_variable
      instance = MyClass.new
      Expensive.stubs(:request).with().returns('expensive value')
      instance.lazy_initialized_value
      assert_equal 'expensive value', instance.instance_variable_get('@lazy_initialized_value')
    end
 
    def test_should_return_value_of_instance_varable
      instance = MyClass.new
      instance.instance_variable_set '@lazy_initialized_value', 'the value'
      Expensive.stubs(:request)
      assert_equal 'the value', instance.lazy_initialized_value
    end
 
    def test_should_maintain_existing_instance_variable_value_when_already_set
      instance = MyClass.new
      instance.instance_variable_set '@lazy_initialized_value', 'existing value'
      Expensive.stubs(:request)
      instance.lazy_initialized_value
      assert_equal 'existing value', instance.instance_variable_get('@lazy_initialized_value')
    end
  end
end

Now we have these tests in place, we can go back and refractor the code ’til our heart’s content using all the tricks in the book but by simplifying the problem in the first place, it gives us a solid test suite and the confidence to make changes without breaking functionality.

If you were solving this problem test-first then you wouldn’t (but more likely, shouldn’t) have written the first example until re-factoring stage anyway, however when these shortcuts become engrained in your brain, it’s all too easy to forget what they are _actually_ doing.

So there we go, simplify the initial implementation, get a solid test suite in order, _then_ re-factor.


5
Sep 07

Rails: Using Autotest with UnitRecord

Myself and a colleague have just managed to waste away a good couple of hours trying to figure out Autotests strange ‘style’ mechanism to add the ability to “test in the way Jay Fields explains”:http://blog.jayfields.com/2007/09/rails-how-we-test.html using “UnitRecord”:http://unit-test-ar.rubyforge.org/.

You can grab our plugin to enable “UnitRecord”:http://unit-test-ar.rubyforge.org/ when using “Autotest”:http://rubyforge.org/projects/zentest below:

“http://svn.soniciq.com/public/rails/plugins/iq_autotest”:http://svn.soniciq.com/public/rails/plugins/iq_autotest

By default, running autotest in the Rails directory will run the unit tests. To run the functional tests, do: AUTOTEST='functional' autotest

I hope this saves some people some time!!


28
Aug 07

SonicIQ Hiring! – UK, Ruby on Rails Developer Required

We are looking for a Ruby on Rails, XHTML & CSS Developer to join our team at “SonicIQ”:http://soniciq.com. Head over to “43folders job board to view our ad”:http://jobs.43folders.com/job/6a5255713e8351a5eb2efef7805b7629/?d=1.

These are exiting times with projects like “Propel’r”:http://propelr.com in the pipeline, along with the ever-growing opportunities for new and interesting client projects.

If you are a highly motivated developer and can see yourself in a Ruby on Rails position in sunny (sometimes) Bournemouth, UK then “apply at 43folders”:http://jobs.43folders.com/job/6a5255713e8351a5eb2efef7805b7629/?d=1.


24
Aug 07

Using Rcov to measure the test coverage of Rails plugins

To view the coverage of your plugins using Rcov, first install the rcov gem with sudo gem install rcov, then copy and paste the following onto the end of the Rakefile inside your plugin directory:

require 'rcov/rcovtask'
 
desc 'Measures test coverage using rcov'
namespace :rcov do
  desc 'Output unit test coverage of plugin.'
  Rcov::RcovTask.new(:unit) do |rcov|
    rcov.pattern    = 'test/unit/**/*_test.rb'
    rcov.output_dir = 'rcov'
    rcov.verbose    = true
  end
 
  desc 'Output functional test coverage of plugin.'
  Rcov::RcovTask.new(:functional) do |rcov|
    rcov.pattern    = 'test/functional/**/*_test.rb'
    rcov.output_dir = 'rcov'
    rcov.verbose    = true
  end
end

You can now simply run rake rcov from inside your plugin directory which will generate an rcov directory with the results. Open rcov/index.html (if you are on OSX this will open automatically) in a browser to view the results.

Thanks to “Mike Clark”:http://clarkware.com/cgi/blosxom for his “Rcov rake task for Rails”:http://clarkware.com/cgi/blosxom/2007/01/05#RcovRakeTask which this task is based on.

*Update (11-11-08):* Changed code to use the RcovTask class.


17
Aug 07

Typo 4.1 slow comment posting

Is anyone else having problems with Typo 4.1 taking ages to process new comments. If anyone has any suggestions I would love to hear them (If you can be bothered to wait for the comment to go through!)


30
Jul 07

Autotest Growl Fail/Pass Smilies

John Nunemaker posted a handy tip on “setting up autotest to work with Growl”:http://railstips.org/2007/7/23/autotest-growl-pass-fail-notifications

I use this all the time now however I didn’t like the ugly smilies (call me shallow if you like). I used “Wolfgang Bartelme’s”:http://bartelme.at “Smily Devkit”:http://bartelme.at/journal/archive/smiley_devkit to make a couple of PNG’s slightly more pleasing to the eye.

p=. !http://thelucid.com/files/fail.png(Autotest Fail image)!
!http://thelucid.com/files/pending.png(Autotest Pending image)!
!http://thelucid.com/files/pass.png(Autotest Pass image)!

The zip file can be downloaded here: “autotest_images.zip”:http://thelucid.com/files/autotest_images.zip

*Update* 17-08-07: Added ‘pending’ image for RSpec as requested by Aslak Hellesoy


24
Jul 07

TheLucid Typo theme now works with version 4.1

TheLucid Typo theme finally works with version 4.1 of Typo.

Please bear in mind that this is quick-fix and and there will still be a Version 2 release sometime in the near future.

The new release can be downloaded below:

“lucid-typo-theme-1-1.zip”:http://thelucid.com/files/lucid-typo-theme-1-1.zip

There are a couple of improvements such as cookies remembering which colour scheme and layout have been selected along with some IE fixes.

Thanks to everyone who notified me of browser issues etc. and I hope to release v2.0 as soon as possible (for both Typo and Mephisto).