I think the most significant strength of the Ruby language is its impressive power and flexibility in metaprogramming. Affectionately dubbed "Monkey Patching" by Ruby developers, metaprogramming makes it easy to 'hack' existing code and frameworks like Rails for infinite customization, while making it easy to keep these hacks legible, organized and maintainable.
A powerful programming concept that Ruby makes easy to implement is the use of Proxy Objects. Since Ruby implements duck typing, a proxy object can easily step in to take the place of a regular object so long as the two share a similar object interface.
The following tutorial is the extraction of a "real world" problem that was solved with the simple implementation of a few proxy objects.
The Problem
Let's say that we are displaying information about a collection of people in our system, and we want to display min, max and averages for the attributes of the members of that collection. An immediate solution would be to clutter up my template with code to find the statistic values, but this gets messy and puts too much logic in my view. Using some kind of helper methods would help extract the logic from the view, which would be better.
That's still not quite what I want, though. What I would really like is to have an object that acts just like a Person, but that gives me values based on a collection of Person objects rather than just one. A nice implementation would be:
@john.age #=> 22 @jim.age #=> 48 @susan.age #=> 20 avg = AveragePerson.new([@john, @jim, @susan]) avg.age #=> 30
This approach is very strong if, for example, you're creating a table by iterating through a collection of people. Thanks to duck typing, an AveragePerson could be slipped into a collection of Person objects and the table would be none the wiser -- the average data would be displayed just the same as a single person's data.
Creating Some Models
First, let's whip up some quick model classes. I will simply use new Ruby classes, but the following techniques can be used with ActiveRecord classes in Rails just as easily.
class Person attr_accessor :name, :age, :inches, :weight def initialize(n, a, i, w) @name = n @age = a @inches = i @weight = w end end class AveragePerson def initialize(col) @collection = col end end
Writing Some Tests
Let's write some RSpec tests to set some goals for our AveragePerson class:
require 'rubygems' require 'spec' describe "People Statistics" do before(:each) do @james = Person.new("James", 23, 74, 210) @cheryl = Person.new("Cheryl", 47, 63, 115) @timmy = Person.new("Timmy", 12, 55, 87) @people = [@james, @cheryl, @timmy] end describe "AveragePerson" do before(:each) do @average = AveragePerson.new(@people) end it "should give the average age" do @average.age.should eql((@james.age + @timmy.age + @cheryl.age) / 3) end it "should give the average weight" do @average.weight.should eql((@james.weight + @timmy.weight + @cheryl.weight) / 3) end it "should give the average inches" do @average.inches.should eql((@james.inches + @timmy.inches + @cheryl.inches) / 3) end it "should be named 'Average'" do @average.name.should eql("Average") end end end
Now let's get these to pass.
A Little Proxy Magic
Per our specs, we need to add #age, #weight, #inches and #name methods to AveragePerson. This would do the trick, but it's not DRY:
class AveragePerson def name "Average" end def age total = @collection.inject(0){ |sum, person| sum += person.age } total / @collection.length end def weight total = @collection.inject(0){ |sum, person| sum += person.weight } total / @collection.length end def inches total = @collection.inject(0){ |sum, person| sum += person.inches } total / @collection.length end end
The tests should pass now, but we have some serious repetition and need to DRY this up. It's also not extensible -- any time a method is added to Person, another would need to be added to AveragePerson.
All we're really doing here is proxying the method that AveragePerson receives to each member of the collection, so let's use method_missing to do this more concisely and extensibly.
class AveragePerson def name "Average" end # proxy methods to collection, return the average of results def method_missing(method_name, *args, &block) total = @collection.inject(0){ |sum, person| sum += person.send(method_name, *args, &block) } total / @collection.length end end
Now AveragePerson will proxy any methods it doesn't have to its collection, add up the results and return the average. The tests still pass and we have much cleaner code.
Adding in Height
It isn't very helpful to just tell a user how many inches tall a person is, it would be much more useful to return a string like "5ft 8in". Let's integrate that into the Person model and write a test for AveragePerson:
class Person def height inches_to_height(self.inches) end private def inches_to_height(_inches) ft = _inches / 12 ins = _inches % 12 "#{ft}ft #{ins}in" end end ### describe "AveragePerson" do it "should give the average height" do @average.height.should eql("5ft 4in") end end
Though our AveragePerson object is proxying the #height method to the collection, this test crashes and burns because our #method_missing assumes that each member will return a number, not a string like "6ft 0in". To fix this, let's "override" AveragePerson#height.
class AveragePerson def height ft = self.inches / 12 ins = self.inches % 12 "#{ft}ft #{ins}in" end end
The tests pass, but we still have some refactoring to do. I've duplicated the height display logic from Person into AveragePerson, so let's extract this out into a module and include it in both classes:
module PersonHelper def height inches_to_height(self.inches) end def inches_to_height(_inches) ft = _inches / 12 ins = _inches % 12 "#{ft}ft #{ins}in" end end class Person include PersonHelper end class AveragePerson include PersonHelper end
I can also include PersonHelper into my RSpec tests so that I can make them more legible using the #inches_to_height helper method:
describe "AveragePerson" do include PersonHelper it "should give the average height" do # @average.height.should eql("5ft 4in") @average.height.should eql(inches_to_height((@james.inches + @timmy.inches + @cheryl.inches) / 3)) end end
Creating Max and Min Classes
Using the same proxy techniques, we can easily build out MaxPerson and MinPerson classes. First the tests:
describe "MaxPerson" do before(:each) do @max = MaxPerson.new(@people) end it "should give the max age" do @max.age.should eql(@cheryl.age) end it "should give the max weight" do @max.weight.should eql(@james.weight) end it "should give the max inches" do @max.inches.should eql(@james.inches) end it "should be named 'Max'" do @max.name.should eql("Max") end it "should give the max height" do @max.height.should eql(inches_to_height(@james.inches)) end end describe "MinPerson" do before(:each) do @min = MinPerson.new(@people) end it "should give the min age" do @min.age.should eql(@timmy.age) end it "should give the min weight" do @min.weight.should eql(@timmy.weight) end it "should give the min inches" do @min.inches.should eql(@timmy.inches) end it "should be named 'Min'" do pending("Needs to be implemented") do @min.name.should eql("Min") end end it "should give the min height" do @min.height.should eql(inches_to_height(@timmy.inches)) end end
And here are the classes:
class MaxPerson include PersonHelper def initialize(col) @collection = col end def name "Max" end def method_missing(method_name, *args, &block) @collection.inject(0) do |highest, person| val = person.send(method_name, *args, &block) highest = (val > highest) ? val : highest end end end class MinPerson include PersonHelper def initialize(col) @collection = col end def name "Min" end def method_missing(method_name, *args, &block) # assumes at least one item in the collection will return a value less than 1000000 @collection.inject(1000000) do |lowest, person| val = person.send(method_name, *args, &block) lowest = (val < lowest) ? val : lowest end end end
More Options with Extend
Our proxy objects are working nicely, but what if we want to access our objects a different way? Maybe it would be slicker if the collection itself had methods to instantiate these objects rather than simply instantiating them explicitly. Let's do something like this:
@people = [@john, @jim, @susan] @people.min_person #=> MinPerson object @people.max_person #=> MaxPerson object @people.avg_person #=> AveragePerson object
Here is a new describe block of tests:
describe "PeopleCollection" do it "should return a MinPerson" do @people.min_person.class.should eql(MinPerson) end it "should return a MaxPerson" do @people.max_person.class.should eql(MaxPerson) end it "should return an AveragePerson" do @people.avg_person.class.should eql(AveragePerson) end end
Let's create a module that will define these collection methods:
module PeopleCollection def min_person MinPerson.new(self) end def max_person MaxPerson.new(self) end def avg_person AveragePerson.new(self) end end
One way to apply this module so that our collection will have access to these methods is to open up the Array class and include the module:
class Array include PeopleCollection end # or you can use the send hack: # Array.send :include, PeopleCollection
I'm not sure this is what I want, though. The tests passs, but I don't really need (or want) every single array in my app to have these methods -- at best it's sloppy, at worst I could run into conflicts. I really only want my @people collection to have this functionality, so instead I will extend that instance with my module only when I need it. Here's the updated describe test block:
describe "PeopleCollection" do before(:each) do @people.extend PeopleCollection end it "should return a MinPerson" do @people.min_person.class.should eql(MinPerson) end it "should return a MaxPerson" do @people.max_person.class.should eql(MaxPerson) end it "should return an AveragePerson" do @people.avg_person.class.should eql(AveragePerson) end end
I have found this to be a helpful technique when you just need to add a little extra functionality to a single object and want to keep it lightweight.
What is BDD and TDD?
When it comes to testing our specs and code, Rails developer have two mainstream choices. We can use Test::Unit, which is supplied by the Ruby Standard Library and is immediately supported by Rails, or we can use the RSpec gems and plugins. Although either one can get the job done, they vary significantly in how they approach testing.
Test::Unit supports a Test Driven Development (TDD) approach, while RSpec implies a Behavior Driven Development (BDD) approach. In TDD, we are usually writing one test per function, leading to many small tests. In TDD, since there is one test per function, we end up writing tests at a core level that end up following the same structure as our code. With a BDD approach, we are using specs as a guide for testing. We are telling a story by describing how a set of functionality should behave, and focus less on the return value of a single method. Think of BDD as a humanized TDD; it is TDD written to use a more English like vocabulary for expressing functionality. BDD makes it easier for other developers to read and understand the code, becoming an additional form of documentation. BDD changes the way we think about testing, since it is more focused on implementing high level specs.
Turning our attention back to Rails, we have access to both Test::Unit and RSpec testing frameworks. Test::Unit is more focused on TDD, while RSpec is more focused on BDD. It is possible to use a BDD approach with Test::Unit, though doing so wouldn't be as clear and concise as using RSpec. Each has their own pros and cons; for example, future updates may break our RSpec tests, causing developers to go back and update their tests into a working state. Test Unit tests won't be affected since it is part of the Ruby Standard Library and built into Rails. Which ever framework you do choose to use will be sufficient for testing, so it's more a question of your preferred testing ideology.
At Killswitch, we have chosen to use the BDD testing paradigm with RSpec. The following is a brief tutorial in getting up and running with RSpec and adopting the BDD approach.
Getting Started:
1. Create a new Rails app:
rails rspec_demo -d mysql
2. Set up your test database and development databases.
3. Install the RSpec Gem:
sudo gem install rspec
4. Install Plugins (git needed):
script/plugin install git://github.com/dchelimsky/rspec.git script/plugin install git://github.com/dchelimsky/rspec-rails.git
5. Generate RSpec directories in root of application:
script/generate rspec
Using RSpec
First, let's create some models. RSpec gives us a new rspec_model command; it will create the model similar to using the Rails model generator, except it will create RSpec files instead of Test::Unit files:
script/generate rspec_model salesman first_name:string last_name:string total_products_sold:integer script/generate rspec_model category title:string script/generate rspec_model product title:string quantity:integer category_id:integer script/generate rspec_model saleman_product saleman_id:integer product_id:integer amount_sold:integer
Before migrating, add 0 as a default value in the migrations for the following columns: total_products_sold in salesman, quantity in product, amount_sold in salesman_products.
Run our migrations with:
rake db:migrate rake db:migrate RAILS_ENV=test
Next, we need to set up relationships and validations in our models:
# app/models/salesman.rb class Salesman < ActiveRecord::Base has_many :salesman_products has_many :products, :through => :salesman_products validates_presence_of :first_name validates_presence_of :last_name end # app/models/product.rb class Product < ActiveRecord::Base has_many :salesman_products has_many :salesmen, :through => :salesman_products belongs_to :category end # app/models/category.rb class Category < ActiveRecord::Base has_many :products validates_presence_of :title end # app/models/salesman_product.rb class SalesmanProduct < ActiveRecord::Base belongs_to :salesman belongs_to :product end
Open up salesman_spec.rb in spec/models. It should contain the following:
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper') describe Salesman do before(:each) do @valid_attributes = { :first_name => "value for first_name", :last_name => "value for last_name", :total_products_sold => "1" } end it "should create a new instance given valid attributes" do Salesman.create!(@valid_attributes) end end
The first line includes the spec_helper which is inside of the spec folder. This file will hold helper methods that all specs will use, I will not be going into detail on creating spec helpers, if you would like to learn more about spec helpers check out the official rspec site.
After the spec helper we have a describe block which will describe a given situation. One can put all their tests in one description block, but I usually like to create multiple description blocks that contains logic for a new record, existing records and relationships. Inside the description block there is a before block that sets up each test with needed data.
There is also an it block inside of the describe block, which is an individual test. One describe block can contain many it blocks. Each it takes a string argument that should describe the test logic it contains. With it being Salesman, the example test would read as "Salesman should create a new instance given valid attributes"
Before moving on, lets make it clear the type of tests this describe block will contain. Each describe block can take in a optional string argument. Lets test a new Salesman object, add "that is new," as the second argument in the describe block. Now the test would read as "Salesman that is new, should create a new instance given valid attributes"
describe Salesman, "that is new," do before(:each) do @valid_attributes = { :first_name => "value for first_name", :last_name => "value for last_name", :total_products_sold => "1" } end it "should create a new instance given valid attributes" do Salesman.create!(@valid_attributes) end end
Try running the spec with:
ruby spec/models/user_spec.rb
You should see:
1 examples, 0 failures
RSpec does not print successful tests, only ones that failed. If there was a error, for example a NoMethodError, RSpec would tell us like so:
NoMethodError in 'Salesman that is new, should create a new instance given valid attributes'
Now since we are creating a new salesman, lets remove the total_products_sold set from the hash. Total products sold is not required, so let's write a new test that will check if a new salesman starts with the value of 0 for total_products_sold. While we're at it, lets change the "value for ...." default strings in the before block to have more realistic data. Currently our spec should look similar to the following:
describe Salesman, "that is new" do before(:each) do @valid_attributes = { :first_name => "Tony", :last_name => "Little" } end it "should create a new instance given valid attributes" do Salesman.create!(@valid_attributes) end it "should create a new salesman with zero total products sold" do end end
Usually in Ruby when we want to make sure an attribute equals a certain value we usually use:
salesman.total_products_sold == 0
Using such syntax would make our tests look similar to the actual code. However, since RSpec has its own syntax similar to English, why not use the RSpec equivalent for more readable tests:
salesman.total_products_sold.should eql(0)
Lets test for zero:
it "should create a new salesman with zero total products sold" do salesman = Salesman.create!(@valid_attributes) salesman.total_products_sold.should eql(0) end
Now when we run our specs, both tests should be a success.
So far we have made sure when valid data is used, our tests will pass. What if we wanted to test for errors? RSpec has syntax for testing errors. In our code we would check if an object is valid with the following:
salesman.valid?
The RSpec equivalent:
salesman.should be_valid
It's pretty straight forward, we are stating the salesman should be valid. We can also test to make sure it is not valid with salesman.should_not be_valid. Lets try it out by adding the following test:
it "should not be valid without a first name" do @valid_attributes[:first_name] = nil salesman = Salesman.new(@valid_attributes) salesman.should_not be_valid end
In the above example we first changed the value of first_name to nil, and then we pass those attributes into a new Salesman object. Then when it reaches salesman.should_not be_valid, it will run through the validations, check to see if it was valid, if not, it will return true and our test will pass!
We can be more detailed and thorough in the previous example by checking to make sure the first_name attribute returns a error. To do this, we simply add the following to the end of the test:
salesman.should have(1).errors_on(:first_name)
We did a few tests for a brand new Salesman, so now let's do some model testing with existing data. Before starting, we will need some data that will be used throughout all our tests. We can use fixtures to create test objects which will add data to our test database. The test database will be reset before each test so changes to data from the previous test won't break the next test. Open up salesmen.yml inside of spec/fixtures and enter the following data:
mays: first_name: Billie last_name: Mays total_products_sold: 356 norris: first_name: Chuck last_name: Norris total_products_sold: 155 simmons: first_name: Richard last_name: Simmons total_products_sold: 186
So far we have created a describe block for a new salesman, next we will create another describe block for existing salesmen. I always separate existing from new for not only organizational purposes, but also for speed purposes. If we were to have many tests in a single describe block and load unnecessary fixtures for every test in a spec, it would lead to really slow tests since the database is reset every time with fixture data. So let's add a new describe block that will contain only the tests requiring fixtures. I have added comments in the code:
describe Salesman, "that exists" do # loads spec/fixtures/salemen.yml fixtures :salesmen before(:each) do # Using fixtures, not needed end it "should have a collection of salesmen" do # !Salesman.find(:all).empty? Salesman.find(:all).should_not be_empty end it "should have three records" do # Salesman.count == 3 Salesman.should have(3).records end it "should find an existing salesman" do salesman = Salesman.find(salesmen(:mays).id) salesman.should eql(salesmen(:mays)) end end
For the last test we are using salesmen(:mays), this will grab the attributes with the title 'mays' from the salesmen.yml file. Since mays has an id of 1, it will tell ActiveRecord to grab the salesman with the id of 1. Next we check to make sure that the salesman object has the same attributes found in the .yml file.
Let's quickly add one more describe block that will test relationships between salesmen and products. Once again we will need fixtures, so open up products.yml and saleman_products.yml and add the following:
# products.yml
oxiclean:
id: 1
title: OxiClean
quantity: 35
category_id: 1 # Cleaning
orangeglow:
id: 2
title: OrangeGlow
quantity: 42
category_id: 1
discosweat:
id: 3
title: Disco Sweat
quantity: 22
category_id: 2 # Video
platinumsweat:
id: 4
title: Platinum Sweat
quantity: 34
category_id: 2
totalgym:
id: 5
title: Total Gym Workout Equipment
quantity: 11
category_id: 3 # Exercise Equipment
handyswitch:
id: 6
title: Handy Switch
quantity: 23
category_id: 4 # Home
fixit:
id: 7
title: Fix It
quantity: 14
category_id: 5
liquid_diamond:
id: 8
title: Liquid Diamond
quantity: 7
category_id: 5 # Auto
# saleman_products.yml
one:
saleman_id: 1
product_id: 1
amount_sold: 75
two:
saleman_id: 1
product_id: 2
amount_sold: 89
three:
saleman_id: 2
product_id: 3
amount_sold: 87
four:
saleman_id: 2
product_id: 4
amount_sold: 99
five:
saleman_id: 3
product_id: 5
amount_sold: 45
six:
saleman_id: 1
product_id: 6
amount_sold: 81
seven:
saleman_id: 1
product_id: 7
amount_sold: 76
eight:
saleman_id: 1
product_id: 8
amount_sold: 45
Finally add in a new describe block that will load only fixtures needed for testing salesmen and products relationships.
describe Salesman, "with Products" do # Will load 3 fixtures need for testing Salesman Product relationships fixtures :salesmen, :salesman_products, :products before(:each) do # Using fixtures end it "should have products" do salesman = Salesman.find(salesmen(:mays).id) salesman.products.should_not be_empty end it "should have five products" do salesman = Salesman.find(salesmen(:mays).id) salesman.products.should have(5).records # should include will check if product belongs to salesman salesman.products.should include(products(:oxiclean)) salesman.products.should include(products(:orange_glow)) salesman.products.should include(products(:handy_switch)) salesman.products.should include(products(:fix_it)) salesman.products.should include(products(:liquid_diamond)) end it "should not have products from other salesmen" do salesman = Salesman.find(salesmen(:norris).id) # should_not include is the opposite of should include # we are making sure this salesman, doesn't have products that # other salesmen are selling salesman.products.should_not include(products(:disco_sweat)) salesman.products.should_not include(products(:platinum_sweat)) salesman.products.should_not include(products(:orange_glow)) salesman.products.should_not include(products(:liquid_diamond)) end end
Now with your feet soaked into the world of BDD, it is time for you to start writing tests for your own applications. If you would like to continue building over the example above, more validations can be tested and specs can to be written for the other models as well. Be sure to check out the official RSpec site for more info.
Many Web Applications these days consist of two main parts: the public facing front-end, and the private, secure administration area. When developing with Ruby on Rails, the convention is to try to write code as DRY (don't repeat yourself) as possible and use one single controller for both the public side and the administration side of the site. For certain circumstances this is fine, but I think there is some flexibility here, and recently I've noticed that many times the Admin logic is quite a bit different that the public facing functionality — enough so to warrant its own set of admin controllers and views.
With Rails 2.0+, you can use namespacing to keep the admin controller logic and views separate from the public controller logic and views. I've come to really like the way this helps to keep both sections of the site separate and organized.
I'll demonstrate a very simple example of a web app that provides a way for an Administrator to create, edit, and delete articles with one controller, and for a visitor to view those articles with a separate controller.
Create Your Controllers
After you've created your new rails app, we can proceed by generating our two controllers to access our articles:
# our private admin controller uses namespacing. Notice the Admin:: prefix ./script/generate controller Admin::Articles
# our public controller is created normally ./script/generate controller Articles
We now have two controllers named articles_controller.rb. The public controller lives in app/controllers/ and the private controller lives in app/controllers/admin/ with its respective views in app/views/admin/articles. Once you've created the necessary views, you should have a file structure similar to the image below. Our public controller only needs two methods at this point: 'index' and 'show', while our admin controller will require all 7 standard REST methods.

Create Your Routes
Before either of these controllers can function, we need to setup our routes in the config/routes.rb file.
map.namespace :admin do |admin| admin.resources :articles end map.resources :articles
Take a second and run rake routes on the command line to view all of your current routes. You'll notice you now have 2 sets of articles routes, one of which targets your admin controller, and one which targets your public controller.
Handling Links and Forms
So how does this change how links are handled in the views? It turns out it's not all that complicated thanks to Rails creating a set of admin specific URL generation methods:
# Create a link to the public article page '/articles/:id' <%= link_to 'View Article', article_url(@article) %> # Create a link to view the article in the Admin area '/admin/articles/:id' <%= link_to 'View Article', admin_article_url(@article) %> # Create a link to edit the article in the Admin area '/admin/articles/:id/edit' <%= link_to 'Edit Article', edit_admin_article_url(@article) %>
The same goes for using forms in the Admin area. Instead of using <% form_for(@article) do |f| %> you simply add in the admin namespace, and voila, your forms submit to the /admin/articles controller:
# Create a form that submits to the /admin/articles controller <% form_for([:admin, @article]) do |f| %> .... <% end %>
Securing and Styling Your Admin Controllers
You will no doubt want to secure all of your Admin controllers and require an Admin user to be logged in. If you use a plugin like the popular RESTUL Authentication, you can simply apply the before_filter :login_required to the top of each Admin controller.
Most of the time the Administration area is also styled differently than the public facing area of the site. Here we can simply put something like layout 'admin' at the top of our Admin controllers to use a separate admin.html.erb layout file instead of the public application.html.erb layout file.
class Admin::ArticlesController < ApplicationController before_filter :login_required layout 'admin' ... REST methods here ... end
Wrapping Things Up...
You should now have the beginnings of a web app with a set of self-contained, password-protected Admin CMS controllers prefixed with /admin, and a set publicly viewable front-end controllers. If you're feeling adventurous, you could even extract the controllers, views, routes, and stylesheets into a plugin or a generator if many of the CMS features will be used throughout a variety of different applications.

