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.
Using a ¶meter with a method invocation is explained very well within PickAxe:
So, the only mystery is what does
The following code should be fully executable and show the concept in entirety.
1. # traditionalIt'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.
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]
def clone_collect(&block)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.
clones = collect { |item| item.dup }
clones.collect &block
end
Using a ¶meter with a method invocation is explained very well within PickAxe:
Invoking a MethodAnother good explanation can also be found in PickAxe:[ 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.
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_codeReducing 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 GreeterApplying this idea to the String class you could define to_proc as below and create blocks using strings and eval.
def to_proc
lambda { "hello world" }
end
end
def greet
yield
end
greet &Greeter.new #=> "hello world"
class StringAlong 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.
def to_proc
text = self
lambda { eval text }
end
end
instance_eval &"2 + 2" #=> 4
class SymbolThe 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).
def to_proc
Proc.new { |*args| args.shift.__send__(self, *args) }
end
end
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]
Labels: ampersand, method, parameter, ruby, Symbol.to_proc, to_proc
Class Reopening Hints
In my previous entry about Class Definitions Aman King left the following comment:
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.
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.
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.
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 SymbolThe 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.
def %(arg)
...
end
end
module SymbolExtensionGranted, this isn't a huge hint, but if you check
def %(arg)
...
end
end
Symbol.send :include, SymbolExtension
Symbol.ancestors you'll find the following list.SymbolMany people believe this is better than nothing, but the choice is yours.
SymbolExtension
Object
Kernel
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 SymbolThe 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.
def self.method_added(method)
raise caller.to_s if method == :%
end
end
class Symbol
def %(arg)
self
end
end
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
Labels: caller, class, extension, include, module, open, ruby, stack trace
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.
An alternative way to define the class can be found below.
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.
Here's another example in the context of a test.
An alternative solution allows you to use an unnamed class within the test that needs it.
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.
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# versionThe 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 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
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 NavigatorIs this actually better? I'm not sure, but I'd be willing to try it out.
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
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# versionThe 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.
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
PhoneNumber = Struct.new :area_code, :exchange, :stationLess readable? Perhaps, but is it if you know what behavior Struct.new encapsulates?
class PhoneNumber
def formatted_number
"(#{area_code}) #{exchange} #{station}"
end
end
Here's another example in the context of a test.
# R# versionWhile 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.
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
An alternative solution allows you to use an unnamed class within the test that needs it.
class ValidatableTest < Test::Unit::TestCaseThe 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.
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
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 ValidatableAs 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 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
module ValidatableThe only other piece to the puzzle is the errors collection. The code for this is also very straightforward.
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
module ValidatableAnother 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.
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
<%= 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
Labels: presenter, rails, ruby, validatable
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.
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.
<%= 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'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".
template = <<eos
<%= title "Appending to ERB output" %>
eos
def title(text)
"<title>#{text}</title>"
end
puts ERB.new(template).result
<%= title "Appending to ERB output" %>Adding the content from the sidebar to the output is easily achieved by calling "yield".
<% sidebar do %>
sidebar content
<% end %>
require 'erb'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.
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
require 'erb'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.
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
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'The above code produces the following result.
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
<title>Appending to ERB output</title>
<div border=1>
sidebar content
</div>
Sunday, January 14, 2007
Another Rails Presenter Example
Jamis Buck recently posted an entry about Moving associated creations to the model. His solution is cool and probably sufficient for the needs of most; however, if you don't fall into that group you may be interested in an alternative solution using a Presenter.
In the example the view contains fields that collect person, email, and phone number information. Jamis shows what the html would actually look like, but I'm going to show what I would put in the view (rhtml) for my example.
Using Presenters has additional advantages such as their ability to easily integrate with ActiveRecord validations, separation of view behavior from models (such as formatting a phone number), and allowing you to put validation error messages somewhere other than in a model. I'll address each of these scenarios in upcoming blog entries.
In the example the view contains fields that collect person, email, and phone number information. Jamis shows what the html would actually look like, but I'm going to show what I would put in the view (rhtml) for my example.
<% form_for :presenter do |form| %>Given the above view, the controller could contain the following code.
...
<%= form.text_field :name %>
...
<%= form.text_field :email %>
...
<%= form.text_field :phone %>
<% end %>
def createUsing a Presenter limits the responsibilities of the controller without requiring that the model take on those responsibilities. For our example we'll assume the same Person class that Jamis already defined, except ours only needs the associations defined.
@person = UsersPresenter.new(params[:presenter]).create_person(current_account)
redirect_to person_url(@person)
end
class Person < ActiveRecord::BaseFinally, the Presenter brings all of this together.
has_one :email_address
has_one :phone_number
end
class UsersPresenterWhile the UsersPresenter class is fairly straightforward it does require the effort to create the additional class. I don't believe that it is worth the trouble for strictly academic reasons (e.g. a separation of concerns debate). However, I do believe that the resulting presenter class may be more easily testable. For example, testing the presenter can be done using a mocking framework such as Mocha.
attr_accessor :name, :email, :phone
def create_person(account)
person = account.people.create(:name => name)
person.create_email_address(:address => email) unless email.nil?
person.create_phone_number(:number => phone) unless phone.nil?
end
def initialize(hash={})
hash.each_pair { |key, value| self.send :"#{key}=", value }
end
end
class UsersPresnterTest < Test::Unit::TestCaseUsing Jamis' (admittedly simpler) solution would require saving data to the database and verifying it's existence.
test "a person is successfully initialized from create_person" do
account=mock
account.expects(:people).returns(people=mock)
people.expects(:create).with(:name => "Jay").returns(person=mock)
person.expects(:create_email_address).with(:address => "j@j.com")
person.expects(:create_phone_number).with(:number => "2125551212")
presenter = UsersPresenter.new(:name => "Jay", :email => "j@j.com", :phone => "2125551212")
presenter.create_person(account)
end
end
Using Presenters has additional advantages such as their ability to easily integrate with ActiveRecord validations, separation of view behavior from models (such as formatting a phone number), and allowing you to put validation error messages somewhere other than in a model. I'll address each of these scenarios in upcoming blog entries.
Labels: presenter, rails, ruby
Thursday, January 11, 2007
Ruby: Refactoring - a First Example
Chapter 1. Refactoring, a First Example
How do I begin to write about refactoring? The traditional way to begin talking about something is to outline the history, broad principles, and the like. When someone does that at a conference, I get slightly sleepy. My mind starts wandering with a low-priority background process that polls the speaker until he or she gives an example. The examples wake me up because it is with examples that I can see what is going on. With principles it is too easy to make generalizations, too hard to figure out how to apply things. An example helps make things clear.
So I'm going to start this book with an example of refactoring. During the process I'll tell you a lot about how refactoring works and give you a sense of the process of refactoring. I can then provide the usual principles-style introduction.
With an introductory example, however, I run into a big problem. If I pick a large program, describing it and how it is refactored is too complicated for any reader to work through. (I tried and even a slightly complicated example runs to more than a hundred pages.) However, if I pick a program that is small enough to be comprehensible, refactoring does not look like it is worthwhile.
Thus I'm in the classic bind of anyone who wants to describe techniques that are useful for real-world programs. Frankly it is not worth the effort to do the refactoring that I'm going to show you on a small program like the one I'm going to use. But if the code I'm showing you is part of a larger system, then the refactoring soon becomes important. So I have to ask you to look at this and imagine it in the context of a much larger system.
The Starting Point
The sample program is very simple. It is a program to calculate and print a statement of a customer's charges at a video store. The program is told which movies a customer rented and for how long. It then calculates the charges, which depend on how long the movie is rented, and identifies the type movie. There are three kinds of movies: regular, children's, and new releases. In addition to calculating charges, the statement also computes frequent renter points, which vary depending on whether the film is a new release.
Several classes represent various video elements. Here's a class diagram to show them (Figure 1.1).
I'll show the code for each of these classes in turn.
Movie
Movie is just a simple data class.
Figure 1.1. Class diagram of the starting-point classes. Only the most important features are shown. The notation is Unified Modeling Language UML [Fowler, UML].

Rental
The rental class represents a customer renting a movie.
Customer
The customer class represents the customer of the store. Like the other classes it has data and accessors:
Customer also has the method that produces a statement. Figure 1.2 shows the interactions for this method. The body for this method is on the facing page.
Figure 1.2. Interactions for the statement method

Comments on the Starting Program
What are your impressions about the design of this program? I would describe it as not well designed and certainly not object oriented. For a simple program like this, that does not really matter. There's nothing wrong with a quick and dirty simple program. But if this is a representative fragment of a more complex system, then I have some real problems with this program. That long statement routine in the Customer class does far too much. Many of the things that it does should really be done by the other classes.
Even so the program works. Is this not just an aesthetic judgment, a dislike of ugly code? It is until we want to change the system. The compiler doesn't care whether the code is ugly or clean. But when we change the system, there is a human involved, and humans do care. A poorly designed system is hard to change. Hard because it is hard to figure out where the changes are needed. If it is hard to figure out what to change, there is a strong chance that the programmer will make a mistake and introduce bugs.
In this case we have a change that the users would like to make. First they want a statement printed in HTML so that the statement can be Web enabled and fully buzzword compliant. Consider the impact this change would have. As you look at the code you can see that it is impossible to reuse any of the behavior of the current statement method for an HTML statement. Your only recourse is to write a whole new method that duplicates much of the behavior of statement. Now, of course, this is not too onerous. You can just copy the statement method and make whatever changes you need.
But what happens when the charging rules change? You have to fix both statement and html_statement and ensure the fixes are consistent. The problem with copying and pasting code comes when you have to change it later. If you are writing a program that you don't expect to change, then cut and paste is fine. If the program is long lived and likely to change, then cut and paste is a menace.
This brings me to a second change. The users want to make changes to the way they classify movies, but they haven't yet decided on the change they are going to make. They have a number of changes in mind. These changes will affect both the way renters are charged for movies and the way that frequent renter points are calculated. As an experienced developer you are sure that whatever scheme users come up with, the only guarantee you're going to have is that they will change it again within six months.
The statement method is where the changes have to be made to deal with changes in classification and charging rules. If, however, we copy the statement to an HTML statement, we need to ensure that any changes are completely consistent. Furthermore, as the rules grow in complexity it's going to be harder to figure out where to make the changes and harder to make them without making a mistake.
You may be tempted to make the fewest possible changes to the program; after all, it works fine. Remember the old engineering adage: "if it ain't broke, don't fix it." The program may not be broken, but it does hurt. It is making your life more difficult because you find it hard to make the changes your users want. This is where refactoring comes in.
Tip
When you find you have to add a feature to a program, and the program's code is not structured in a convenient way to add the feature, first refactor the program to make it easy to add the feature, then add the feature.
How do I begin to write about refactoring? The traditional way to begin talking about something is to outline the history, broad principles, and the like. When someone does that at a conference, I get slightly sleepy. My mind starts wandering with a low-priority background process that polls the speaker until he or she gives an example. The examples wake me up because it is with examples that I can see what is going on. With principles it is too easy to make generalizations, too hard to figure out how to apply things. An example helps make things clear.
So I'm going to start this book with an example of refactoring. During the process I'll tell you a lot about how refactoring works and give you a sense of the process of refactoring. I can then provide the usual principles-style introduction.
With an introductory example, however, I run into a big problem. If I pick a large program, describing it and how it is refactored is too complicated for any reader to work through. (I tried and even a slightly complicated example runs to more than a hundred pages.) However, if I pick a program that is small enough to be comprehensible, refactoring does not look like it is worthwhile.
Thus I'm in the classic bind of anyone who wants to describe techniques that are useful for real-world programs. Frankly it is not worth the effort to do the refactoring that I'm going to show you on a small program like the one I'm going to use. But if the code I'm showing you is part of a larger system, then the refactoring soon becomes important. So I have to ask you to look at this and imagine it in the context of a much larger system.
The Starting Point
The sample program is very simple. It is a program to calculate and print a statement of a customer's charges at a video store. The program is told which movies a customer rented and for how long. It then calculates the charges, which depend on how long the movie is rented, and identifies the type movie. There are three kinds of movies: regular, children's, and new releases. In addition to calculating charges, the statement also computes frequent renter points, which vary depending on whether the film is a new release.
Several classes represent various video elements. Here's a class diagram to show them (Figure 1.1).
I'll show the code for each of these classes in turn.
Movie
Movie is just a simple data class.
class Movie
CHILDRENS = 2
REGULAR = 0
NEW_RELEASE = 1
attr_reader :title
attr_accessor :price_code
def initialize(title, price_code)
@title, @price_code = title, price_code
end
endFigure 1.1. Class diagram of the starting-point classes. Only the most important features are shown. The notation is Unified Modeling Language UML [Fowler, UML].

Rental
The rental class represents a customer renting a movie.
class Rental
attr_reader :movie, :days_rented
def initialize(movie, days_rented)
@movie, @days_rented = movie, days_rented
end
endCustomer
The customer class represents the customer of the store. Like the other classes it has data and accessors:
class Customer
attr_reader :name
def initialize(name)
@name = name
@rentals = []
end
def add_rental(arg)
@rentals << arg
end
def statement
total_amount, frequent_renter_points = 0, 0
result = "Rental Record for #{@name}\n"
@rentals.each do |element|
this_amount = 0
# determine amounts for each line
case element.movie.price_code
when Movie::REGULAR
this_amount += 2
this_amount += (element.days_rented - 2) * 1.5 if element.days_rented > 2
when Movie::NEW_RELEASE
this_amount += element.days_rented * 3
when Movie::CHILDRENS
this_amount += 1.5
this_amount += (element.days_rented - 3) * 1.5 if element.days_rented > 3
end
# add frequent renter points
frequent_renter_points += 1
# add bonus for a two day new release rental
frequent_renter_points += 1 if element.movie.price_code == Movie.NEW_RELEASE && element.days_rented > 1
# show figures for this rental
result += "\t" + element.movie.title + "\t" + this_amount.to_s + "\n"
total_amount += this_amount
end
# add footer lines
result += "Amount owed is #{total_amount.to_s}\n"
result += "You earned #{frequent_renter_points.to_s} frequent renter points"
result
end
endCustomer also has the method that produces a statement. Figure 1.2 shows the interactions for this method. The body for this method is on the facing page.
Figure 1.2. Interactions for the statement method

Comments on the Starting Program
What are your impressions about the design of this program? I would describe it as not well designed and certainly not object oriented. For a simple program like this, that does not really matter. There's nothing wrong with a quick and dirty simple program. But if this is a representative fragment of a more complex system, then I have some real problems with this program. That long statement routine in the Customer class does far too much. Many of the things that it does should really be done by the other classes.
Even so the program works. Is this not just an aesthetic judgment, a dislike of ugly code? It is until we want to change the system. The compiler doesn't care whether the code is ugly or clean. But when we change the system, there is a human involved, and humans do care. A poorly designed system is hard to change. Hard because it is hard to figure out where the changes are needed. If it is hard to figure out what to change, there is a strong chance that the programmer will make a mistake and introduce bugs.
In this case we have a change that the users would like to make. First they want a statement printed in HTML so that the statement can be Web enabled and fully buzzword compliant. Consider the impact this change would have. As you look at the code you can see that it is impossible to reuse any of the behavior of the current statement method for an HTML statement. Your only recourse is to write a whole new method that duplicates much of the behavior of statement. Now, of course, this is not too onerous. You can just copy the statement method and make whatever changes you need.
But what happens when the charging rules change? You have to fix both statement and html_statement and ensure the fixes are consistent. The problem with copying and pasting code comes when you have to change it later. If you are writing a program that you don't expect to change, then cut and paste is fine. If the program is long lived and likely to change, then cut and paste is a menace.
This brings me to a second change. The users want to make changes to the way they classify movies, but they haven't yet decided on the change they are going to make. They have a number of changes in mind. These changes will affect both the way renters are charged for movies and the way that frequent renter points are calculated. As an experienced developer you are sure that whatever scheme users come up with, the only guarantee you're going to have is that they will change it again within six months.
The statement method is where the changes have to be made to deal with changes in classification and charging rules. If, however, we copy the statement to an HTML statement, we need to ensure that any changes are completely consistent. Furthermore, as the rules grow in complexity it's going to be harder to figure out where to make the changes and harder to make them without making a mistake.
You may be tempted to make the fewest possible changes to the program; after all, it works fine. Remember the old engineering adage: "if it ain't broke, don't fix it." The program may not be broken, but it does hurt. It is making your life more difficult because you find it hard to make the changes your users want. This is where refactoring comes in.
Tip
When you find you have to add a feature to a program, and the program's code is not structured in a convenient way to add the feature, first refactor the program to make it easy to add the feature, then add the feature.
Ruby: Refactoring, Ruby Edition
A few of my colleagues and I have decided to write, with Martin's permission, a Ruby version of Refactoring.
To begin with this will be a port of the existing Refactoring text. After we finish the port, we are going to look at Ruby specific refactorings. As I go through porting the text I'm going to post what I work on to my blog.
To begin with this will be a port of the existing Refactoring text. After we finish the port, we are going to look at Ruby specific refactorings. As I go through porting the text I'm going to post what I work on to my blog.
Wednesday, January 03, 2007
Rails: Enterprise Ready
"Is Rails Enterprise Ready?"
I've avoided the question since I started working with Rails. In fact, if pressed, I previously would have stated that Rails wasn't enterprise ready. Claiming that it isn't ready is much easier than proving that it is.
But, now things are different. I'm working on a development team with 14 coders. Our application has it's own database and also integrates with 2 other legacy databases. Our application also integrates with external services via SOAP and RESTful web services. The application currently has a unit test suite that consistently runs in less than 1 second. It also has a domain model that is separated from the UI by inserting a Presenter layer.
Did Rails handle this type of environment out of the box? No. However, Ruby + Rails made it easy to extend Rails where it was necessary. Since I was able to easily extend the framework to my needs, I'm now completely comfortable stating that Rails is "Enterprise Ready". In fact, out of the box Rails solves about 80% of the problems I have, which is better than most "Enterprise" software I'm usually working with.
Out of the box, Rails expects you to be working with one database that you have full control over. Sometimes, life isn't that easy. Luckily, back in January of 2006 Dave Thomas wrote a blog entry on how to create and share database connections to additional databases. Using this article as a reference, we were able to fairly easily integrate our application with our database and the 2 legacy databases we needed to both read and write to.
Connecting to external services was also an easy task. We wrote a few Gateways that took advantage of Net::Http. These Gateway classes took our domain objects, mapped them to valid request objects for the external services, and formatted the result to our needs.
We've spent a lot of time ensuring that our test suite is quite solid, yet ran as fast as possible. The first step was removing access to the database from the unit tests. After that, we also looked at allowing ActiveRecord::Base subclasses to be tested without needing access to the database. The result is a sub 1-second unit test run that provides greater than 80% code coverage.
We also spent a fair amount of time on creating Presenter classes that sit between the UI and the Models. In our application several of the pages contain more than one domain model. For example, a page may collect a billing address and credit card information. Instead of creating several models in the controller we create one presenter instance that collects and validates input. The presenter instances also contain methods that can create model objects from the validated user input. The result is a clean domain model and an easily unit testable presentation layer.
Above are, in my opinion, the significant changes we made to mold Rails to our needs on this project. There were a few other changes we made that were not as major but also contributed to the success we've had so far. For example, we stopped using migrations because of some pain we were feeling due to our team size. Instead, we drop and recreate our database with every build using ActiveRecord::Schema.define, a tip we picked up from Dave Thomas. We also automated our check in process to ensure everyone follows the same steps before checking in. This has lead us to very few broken builds.
Rails is great for building web applications. For building enterprise web applications, Rails is often good enough. And, for building complicated enterprise web applications, Rails is easily extendable. Because of this, Rails is Enterprise Ready.
I've avoided the question since I started working with Rails. In fact, if pressed, I previously would have stated that Rails wasn't enterprise ready. Claiming that it isn't ready is much easier than proving that it is.
But, now things are different. I'm working on a development team with 14 coders. Our application has it's own database and also integrates with 2 other legacy databases. Our application also integrates with external services via SOAP and RESTful web services. The application currently has a unit test suite that consistently runs in less than 1 second. It also has a domain model that is separated from the UI by inserting a Presenter layer.
Did Rails handle this type of environment out of the box? No. However, Ruby + Rails made it easy to extend Rails where it was necessary. Since I was able to easily extend the framework to my needs, I'm now completely comfortable stating that Rails is "Enterprise Ready". In fact, out of the box Rails solves about 80% of the problems I have, which is better than most "Enterprise" software I'm usually working with.
Out of the box, Rails expects you to be working with one database that you have full control over. Sometimes, life isn't that easy. Luckily, back in January of 2006 Dave Thomas wrote a blog entry on how to create and share database connections to additional databases. Using this article as a reference, we were able to fairly easily integrate our application with our database and the 2 legacy databases we needed to both read and write to.
Connecting to external services was also an easy task. We wrote a few Gateways that took advantage of Net::Http. These Gateway classes took our domain objects, mapped them to valid request objects for the external services, and formatted the result to our needs.
We've spent a lot of time ensuring that our test suite is quite solid, yet ran as fast as possible. The first step was removing access to the database from the unit tests. After that, we also looked at allowing ActiveRecord::Base subclasses to be tested without needing access to the database. The result is a sub 1-second unit test run that provides greater than 80% code coverage.
We also spent a fair amount of time on creating Presenter classes that sit between the UI and the Models. In our application several of the pages contain more than one domain model. For example, a page may collect a billing address and credit card information. Instead of creating several models in the controller we create one presenter instance that collects and validates input. The presenter instances also contain methods that can create model objects from the validated user input. The result is a clean domain model and an easily unit testable presentation layer.
Above are, in my opinion, the significant changes we made to mold Rails to our needs on this project. There were a few other changes we made that were not as major but also contributed to the success we've had so far. For example, we stopped using migrations because of some pain we were feeling due to our team size. Instead, we drop and recreate our database with every build using ActiveRecord::Schema.define, a tip we picked up from Dave Thomas. We also automated our check in process to ensure everyone follows the same steps before checking in. This has lead us to very few broken builds.
Rails is great for building web applications. For building enterprise web applications, Rails is often good enough. And, for building complicated enterprise web applications, Rails is easily extendable. Because of this, Rails is Enterprise Ready.




