Saturday, January 27, 2007

Ruby: Invoking a method with a ampersand-parameter

On a few ocassions recently I've been asked what the & does when it is used in the context of a method invocation parameter. For example, consider the following code, specifically line 6 where collect is called.
1. # traditional
2. [1,2,3].collect { |number| number % 2 } #=> [1,0,1]
3.
4. # collect with a previously created block
5. block = lambda { |number| number % 2 }
6. [1,2,3].collect &block #=> [1,0,1]
It's fairly easy to see what is going on from the above example; however, more often I run into code that looks like the following code.
def clone_collect(&block)
clones = collect { |item| item.dup }
clones.collect &block
end
The previous example shows a method that clones each item and then passes on the block to the collect method of the clones array. The above code seems to be a tripping point, I assume it's because the block is defined outside the method definition.

Using a ¶meter with a method invocation is explained very well within PickAxe:
Invoking a Method
[ receiver. ] name [ parameters ] [ block ] 
parameters: ( [ param, ... ] [ , hashlist ] [ *array ] [ &a_proc ] )
block: { blockbody }
do blockbody end
...

A block may be associated with a method call using either a literal block (which must start on the same source line as the last line of the method call) or a parameter containing a reference to a Proc or Method object prefixed with an ampersand character.
Another good explanation can also be found in PickAxe:
If the last argument to a method is preceded by an ampersand, Ruby assumes that it is a Proc object. It removes it from the parameter list, converts the Proc object into a block, and associates it with the method.
An understanding of the above is crucial if you want to comprehend how the following code works within a Rails codebase.
area_codes = phone_numbers.collect &:area_code
Reducing the following statement you can see that area_codes is being set equal to the result of collect being called on phone_numbers. The phone_numbers variable is an array of PhoneNumber instances. The PhoneNumber class has an area_code attribute.

So, the only mystery is what does &:area_code do? As previously stated, if the last parameter is prefixed with an &, ruby removes it from the parameter list, and converts the Proc object into a block. The conversion from a parameter into a block is done through the to_proc method of the parameter. Therefore, by defining the to_proc method it is possible to alter the behavior of any parameter passed to a method that expects a block.
class Greeter
def to_proc
lambda { "hello world" }
end
end

def greet
yield
end

greet &Greeter.new #=> "hello world"
Applying this idea to the String class you could define to_proc as below and create blocks using strings and eval.
class String
def to_proc
text = self
lambda { eval text }
end
end

instance_eval &"2 + 2" #=> 4
Along the same lines, Rails defines the Symbol.to_proc method to create a new proc which takes args. The first element of args is expected to be the item from the collection. The item from the collection is sent the symbol (self from the code below) which is the symbol that specifies which attribute to return. The full code can be seen below.
class Symbol
def to_proc
Proc.new { |*args| args.shift.__send__(self, *args) }
end
end
The result is a proc that allows you to iterate the collection and return an array of the attribute that is specified by the symbol (:area_code in our example).

The following code should be fully executable and show the concept in entirety.
class Symbol
def to_proc
Proc.new { |*args| args.shift.__send__(self, *args) }
end
end

PhoneNumber = Struct.new :area_code
*phone_numbers = PhoneNumber.new(904), PhoneNumber.new(646), PhoneNumber.new(616)
area_codes = phone_numbers.collect &:area_code #=> [904, 646, 616]

Class Reopening Hints

In my previous entry about Class Definitions Aman King left the following comment:
I've always liked Ruby's open class feature but have also wondered how do you keep track of all the changes made to the class definition during runtime? ...
There isn't an easy way (that I know of) out of the box to know what files made what changes to your objects. But, are a few things that you can do that may give you hints as to where behavior is coming from.

Use modules instead of adding behavior directly.
On my current project we needed to add a % method to symbol. You can accomplish this with the following code.
class Symbol
def %(arg)
...
end
end
The above code does what you need, but leaves no hint that you've made a change to Symbol. An alternative solution is to define a module and include that module in Symbol.
module SymbolExtension
def %(arg)
...
end
end

Symbol.send :include, SymbolExtension
Granted, this isn't a huge hint, but if you check Symbol.ancestors you'll find the following list.
Symbol
SymbolExtension
Object
Kernel
Many people believe this is better than nothing, but the choice is yours.

Use the stack trace.
Another option is to use the stack trace to try to track down a specific method. If you are working with some code where a method is being modified somewhere, but it isn't in a module the following code could help you out.
class Symbol
def self.method_added(method)
raise caller.to_s if method == :%
end
end

class Symbol
def %(arg)
self
end
end
The above code uses the method_added hook method and raises and exception when the method you are looking for is defined. At this point the stack trace should show you where the method was defined.

Use conventions.
In practice I've had very little trouble with finding where behavior is defined. In all the projects I've been involved with we have a few conventions that we follow that help ensure we can find behavior definitions easily.
  • Define behavior for the class PhoneNumber (for example) in the phone_number.rb file
  • If you need to open a class that you have not created, create a [Class]Extension module and include it as shown above.
  • Avoid dynamically adding behavior in unexpected places when possible. For example we don't define behavior in associations, such as:
    # not recommended
    has_many :people do
    def by_name
    find(:all)
    end
    end
As I previously stated, I'm on a 14 person team currently. Those 14 people create quite a few features at a rapid pace. Despite our size and rapid pace, I can't remember ever having trouble finding behavior.

Friday, January 26, 2007

Class Definitions

One of the things that makes Ruby very interesting to me is the ability to do things that I previously could not do in C#. For example, Ruby has open classes so I can define behavior of a class in various places.

In this post I'll need a term to describe code written in Ruby that could be written in the same way using Java or C#. Since I worked primarily with C# before Ruby I'll use R#.

I'll start with a fairly easy example where I have a class that has some class and instance methods and some of the class methods need to be protected.
# R# version
class Navigator
class << self
def standard_navigator
self.new standard_path
end

def promo_navigator
self.new promotion_path
end

protected
def standard_path
[:index, :account_creation, :terms_of_service, :check_out]
end

def promotion_path
[:promo_landing, :account_creation, :check_out]
end
end

def initialize(path)
@path = path
@current = 0
end

def next
@current += 1
@path[@current]
end
end
The above example is a fairly standard implementation that puts the class methods at the top, public above the protected, and then the instance methods below. A possible problem with the above code is that if you don't have the class << self code visible when viewing the file (because you've scrolled down) it is not immediately obvious that the methods are class methods. You could argue that the using the class << self is the problem; however, it's the only clean way I've found that allows you to define protected class methods.

An alternative way to define the class can be found below.
class Navigator
def initialize(path)
@path = path
@current = 0
end

def next
@current += 1
@path[@current]
end
end

class << Navigator
def standard_navigator
self.new standard_path
end

def promo_navigator
self.new promotion_path
end

protected
def standard_path
[:index, :account_creation, :terms_of_service, :check_out]
end

def promotion_path
[:promo_landing, :account_creation, :check_out]
end
end
Is this actually better? I'm not sure, but I'd be willing to try it out.

Here's another example. I want a class that has three attributes (properties in C#), is able to initialize these properties in the constructor or default them, and contains some behavior.
# R# version
class PhoneNumber
attr_accessor :area_code, :exchange, :station

def initialize(area_code=nil, exchange=nil, station=nil)
@area_code, @exchange, @station = area_code, exchange, station
end

def formatted_number
"(#{area_code}) #{exchange} #{station}"
end
end
The above code is easy enough to read, but Ruby already has a class that encapsulates the attribute and constructor initialization pattern: Struct. The above code can also be written like below.
PhoneNumber = Struct.new :area_code, :exchange, :station

class PhoneNumber
def formatted_number
"(#{area_code}) #{exchange} #{station}"
end
end
Less readable? Perhaps, but is it if you know what behavior Struct.new encapsulates?

Here's another example in the context of a test.
# R# version
class ValidatableTest < Test::Unit::TestCase
class StubClass
include Validatable
attr_accessor :name
validates_presence_of :name
end

test "when a name is empty, then the instance is invalid" do
assert_equal false, StubClass.new.valid?
end
end
While the above code works, it begins to break down when you need multiple stub classes. You can use names such as StubClass2, StubClass3 or look for more descriptive names but when you are testing similar but slightly different situations it can be very hard to come up with good names. It is also less desirable to have the class defined outside the scope of the test that uses the class.

An alternative solution allows you to use an unnamed class within the test that needs it.
class ValidatableTest < Test::Unit::TestCase
test "when a name is empty, then the instance is invalid" do
klass = Class.new
klass.class_eval do
include Validatable
attr_accessor :name
validates_presence_of :name
end
assert_equal false, klass.new.valid?
end
end
The above test is very explicit about what behavior can be expected from the class under test. The above test also ensures that if the test fails you wont need to go elsewhere to see any set up code. This example is the one where I have an opinion, and I much prefer the 2nd version.

Ruby allows you to add behavior to classes in many ways. Using a combination of the various ways we may be able to find more descriptive class definitions, or prove that the traditional ways are superior.

Saturday, January 20, 2007

Adding Validations to any Class

On my current project we have data that needs to be collected and then sent to a service. This data needs to be validated, but since it is never put into a model it cannot be validated by using ActiveRecord's validations. To handle this scenario we designed something similar to the module below that can add validations to any class that includes it, which are generally Presenters on my project.
module Validatable
module ClassMethods
def validates_format_of(*args)
validate_all(args) do |attribute, options|
self.validations << ValidatesFormatOf.new(attribute, options[:with], options[:message] || "is invalid")
end
end

def validates_presence_of(*args)
validate_all(args) do |attribute, options|
self.validations << ValidatesPresenceOf.new(attribute, options[:message] || "can't be empty")
end
end

def validate_all(args, &block)
options = args.last.is_a?(Hash) ? args.pop : {}
args.each do |attribute|
yield attribute, options
end
end
protected :validate_all

def validations
@validations ||= []
end

def validate(instance)
self.validations.each do |validation|
instance.errors.add(validation.attribute, validation.message) unless validation.valid?(instance)
end
instance.errors.empty?
end
end

def self.included(klass)
klass.extend Validatable::ClassMethods
end

def valid?
errors.clear
self.class.validate(self)
end

def errors
@errors ||= Validatable::Errors.new
end
end
As you can see I only included the code to expose the validates_presence_of and validates_format_of validations. We actually have quite a bit more, but I think those two are all that's necessary to follow the example. I like the way we designed the validation classes because they can easily be tested and are all treated the same way when it comes time to verify if they are valid or not (the validate method). The code below shows all that is necessary for both the ValidatesPresenceOf and ValidatesFormatOf classes.
module Validatable
class ValidationBase
attr_accessor :message
def initialize(message)
self.message = message
end
end

class ValidatesPresenceOf < ValidationBase
attr_accessor :attribute
def initialize(attribute, message)
self.attribute = attribute
super message
end

def valid?(instance)
(!instance.send(self.attribute).nil? && instance.send(self.attribute).strip.length != 0)
end

end

class ValidatesFormatOf < ValidationBase
attr_accessor :attribute, :regex, :message
def initialize(attribute, regex, message)
self.attribute = attribute
self.regex = regex
super message
end

def valid?(instance)
instance.send(self.attribute) =~ self.regex
end
end
end
The only other piece to the puzzle is the errors collection. The code for this is also very straightforward.
module Validatable
class Errors
extend Forwardable

def_delegators :@errors, :empty?, :clear

def on(attribute)
@errors[attribute.to_sym]
end

def add(attribute, message)
@errors[attribute.to_sym] = message
end

def initialize
@errors = {}
end
end
end
Another important thing to note is that these validations integrate directly with the ActionView::Helpers::ActiveRecordHelper.error_message_on method. This means the same code in the view that displays ActiveRecord errors can also display errors from any object that includes Validatable.
<%= error_message_on :presenter, :name %>
And, here's a few tests if you want to ensure that it works as expected.
class ValidatableTest < Test::Unit::TestCase
test "given no presence when object is validated then valid returns false" do
klass = Class.new
klass.class_eval do
include Validatable
attr_accessor :name
validates_presence_of :name
end

assert_equal false, klass.new.valid?
end

test "given no presence when object is validated then it contains errors" do
klass = Class.new
klass.class_eval do
include Validatable
attr_accessor :name
validates_presence_of :name
end
instance = klass.new
instance.valid?
assert_equal "can't be empty", instance.errors.on(:name)
end

test "given invalid format when object is validated then valid returns false" do
klass = Class.new
klass.class_eval do
include Validatable
attr_accessor :name
validates_format_of :name, :with=>/.+/
end

assert_equal false, klass.new.valid?
end

test "given invalid format when object is validated then it contain errors" do
klass = Class.new
klass.class_eval do
include Validatable
attr_accessor :name
validates_format_of :name, :with=>/.+/
end
instance = klass.new
instance.valid?
assert_equal "is invalid", instance.errors.on(:name)
end

test "given valid data after it is previously invalid when object is validated then it is valid" do
klass = Class.new
klass.class_eval do
include Validatable
attr_accessor :name
validates_format_of :name, :with=>/.+/
end
instance = klass.new
assert_equal false, instance.valid?
instance.name = "Jay"
assert_equal true, instance.valid?
end
end

Wednesday, January 17, 2007

Appending to ERB output from a block declaration

I've recently been writing up some material and using ERB to handle the majority of my formatting needs. For example, the code below shows what my template may start with.
<%= title "Appending to ERB output" %>
I use ERB to generate both .doc and .html files. If I generate an html file the above template will produce the html found below.
<title>Appending to ERB output</title>
This is very straightforward stuff easily handled by ERB. I use classes and bindings for my needs, but the example below is a simplified version that should get the point across.
require 'erb'

template = <<eos
<%= title "Appending to ERB output" %>
eos

def title(text)
"<title>#{text}</title>"
end

puts ERB.new(template).result
I recently ran into a different situation that took a bit more effort. In the material I was writing up I needed a sidebar. I thought that the best way to represent the sidebar in my template was to use a block. Using a block ensures that the sidebar, when generating html, will be properly closed by requiring an "end".
<%= title "Appending to ERB output" %>

<% sidebar do %>
sidebar content
<% end %>
Adding the content from the sidebar to the output is easily achieved by calling "yield".
require 'erb'

template = <<eos
<%= title "Appending to ERB output" %>

<% sidebar do %>
sidebar content
<% end %>
eos

def sidebar
yield
end

def title(text)
"<title>#{text}</title>"
end

puts ERB.new(template).result
However, the above implementation hasn't appended any tags to mark the beginning or end of the sidebar. Based on the way that the title method works you might assume that the following would work.
require 'erb'

template = <<eos
<%= title "Appending to ERB output" %>

<% sidebar do %>
sidebar content
<% end %>
eos

def sidebar
"<div border=1>#{yield}</div>"
end

def title(text)
"<title>#{text}</title>"
end
But, the above code does not produce any div tags. This is because <%= .. %> mark ruby expressions that are replaced with the result of the expression, and we used <% .. %> which is used to evaluate ruby code but not append.

A way to solve this issue is to tell ERB where to store the output. ERB allows you to specify, as a constructor argument, a variable to store the output in. This allows you to append to the output from within a block declaration.
require 'erb'

template = <<eos
<%= title "Appending to ERB output" %>

<% sidebar do %>
sidebar content
<% end %>
eos

def sidebar
@output << "<div border=1>"
yield
@output << "</div>"
end

def title(text)
"<title>#{text}</title>"
end

ERB.new(template, nil, nil, "@output").result

puts @output
The above code produces the following result.
  <title>Appending to ERB output</title>

<div border=1>
sidebar content
</div>