Saturday, February 9, 2008

Custom Rails Validations - Keeping it DRY(2)

Last week I added a custom validation for zip/postal codes. Now we make that available to all ActiveRecord models.

The best way is to create a plugin for all your custom validations. We have a plugin for all our ActiveRecord extensions. Its easier to test and its reusable across projects.

So we have our Validations module location in 'vendor/plugins/active_record_ext/lib'

module ValidationSystem
def self.included(base) # :nodoc:
base.extend ClassMethods
end

module ClassMethods
def validates_as_zip_code(*attr_names)
configuration = {
:message => 'is an invalid zip code',
:with => /(^\d{5}$)|(^\d{5}-\d{4}$)|(^[ABCEGHJKLMNPRSTVXY]\d[A-Z] \d[A-Z]\d)/,
:allow_nil => false}
configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
validates_format_of attr_names, configuration
end
end

end


Now we just need to tell ActiveRecord to include this in Base so its available for all model classes.

'vendor/plugins/active_record_ext/init.rb'

ActiveRecord::Base.class_eval do
include ValidationSystem
end

Wednesday, February 6, 2008

Require vs Load

In ruby there are two main ways to include additional source files into working enviroment. One way is with the Kernel#load method and the other is with the Kernel#require method.

So what are the main difference and when should one be used over the other?

Some simple examples should clear up any confusion.

Say we have a very useful file called 'useful.rb' that has the following contents.

puts "This file is so useful I just want to use it everywhere!"


Now lets play in our nifty IRB and 'require' our destined to be hugely popular file.

>> require 'useful'
This file is so useful I just want to use it everywhere!
=> true


Ahh all is well in the world. Now lets try to load it.

>> load 'useful'
LoadError: no such file to load -- useful
from (irb):2:in 'load'
from (irb):2
>>


As you can see 'load' was unable to locate the file. Lets try adding the .rb to extension and see what happens.

>> load 'useful.rb'
This file is so useful I just want to use it everywhere!
=> true


There we go. So the first difference I want to point out is that 'load' requires that you explicitly add the file extension while 'require' does not. 'require' will automatically search your path for files with the given name and automatically append .rb, .so, .o and .dll extensions to try to locate the proper file.

Now lets see what happens if we 'require' and 'include' our file a second time.

>> require 'useful'
=> false
>> load 'useful.rb'
This file is so useful I just want to use it everywhere!
=> true


As you an see requiring the file a second time fails but including it passes. This is because when you 'require' a file it will not reload itself if it has already been loaded. This is in contrast to 'load' will always reload the file.

Why would you ever use 'load' then? Well the truth is most of the time you will be using 'require' but there are circumstances where 'load' is preferably. Rails for example uses 'load' while in development mode as to guarantee any changes to the source will invalidate any existing cache that the web server may have thus always displaying your most recent changes.

I hope this post clears up any confusion.

Sunday, February 3, 2008

Custom Rails Validations - Keeping it DRY

One of the validations that keeps coming up for us is zip/postal code. Sure you can use 'validates_format_of' with a regexp but it's nice to keep that regexp logic all in one place. We recently had to support Canadian Postal Codes so it was nice to update it just in one spot.

   def validates_as_zip_code(*attr_names)
configuration = {
:message => 'is an invalid zip code',
:with => /(^\d{5}$)|(^\d{5}-\d{4}$)|(^[ABCEGHJKLMNPRSTVXY]\d[A-Z] \d[A-Z]\d)/,
:allow_nil => false}
configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
validates_format_of attr_names, configuration
end