Tuesday, November 13, 2007

Ruby: Time::is

Mocha is fantastic for unit testing, but I usually try to avoid requiring it while functional testing. In general this works, but Time.now is something that I occasionally like to fix even while functional testing. To solve the problem, within a functional test helper I load a time_extensions.rb file that defines a Time::is method. The Time::is method is useful for freezing time at a certain point and executing a block of code. When the block of code finishes the Time.now method is returned to it's original implementation.

The example below is how I usually solve the described problem.

require 'time'

class Time
def self.metaclass
class << self; self; end
end

def self.is(point_in_time)
new_time = case point_in_time
when String then Time.parse(point_in_time)
when Time then point_in_time
else raise ArgumentError.new("argument should be a string or time instance")
end
class << self
alias old_now now
end
metaclass.class_eval do
define_method :now do
new_time
end
end
yield
class << self
alias now old_now
undef old_now
end
end
end

Time.is(Time.now) do
Time.now # => Tue Nov 13 19:31:46 -0500 2007
sleep 2
Time.now # => Tue Nov 13 19:31:46 -0500 2007
end

Time.is("10/05/2006") do
Time.now # => Thu Oct 05 00:00:00 -0400 2006
sleep 2
Time.now # => Thu Oct 05 00:00:00 -0400 2006
end

Rails: Enumerable#sum

Documentation
Calculates a sum from the elements. Examples:

payments.sum { |p| p.price * p.tax_rate }
payments.sum(&:price)

This is instead of payments.inject { |sum, p| sum + p.price }

Also calculates sums without the use of a block:

[5, 15, 10].sum # => 30

The default identity (sum of an empty list) is zero. However, you can override this default:

[].sum(Payment.new(0)) { |i| i.amount } # => Payment.new(0)
Usage
The Enumerable#sum method does exactly what you would expect: Sum the elements of the array.

Test

require 'rubygems'
require 'active_support'
require 'test/unit'
require 'dust'

unit_tests do
test "sum the numbers from the array" do
grades = [50, 55, 67, 62, 71, 89, 84, 85, 99]
assert_equal 662, grades.sum
end
end

Monday, November 12, 2007

Rails: Enumerable#group_by

Documentation
Collect an enumerable into sets, grouped by the result of a block. Useful, for example, for grouping records by date.

e.g.
  latest_transcripts.group_by(&:day).each do |day, transcripts|
p "#{day} -> #{transcripts.map(&:class) * ', '}"
end
"2006-03-01 -> Transcript"
"2006-02-28 -> Transcript"
"2006-02-27 -> Transcript, Transcript"
"2006-02-26 -> Transcript, Transcript"
"2006-02-25 -> Transcript"
"2006-02-24 -> Transcript, Transcript"
"2006-02-23 -> Transcript"
Usage
The Enumerable#group_by method is helpful for grouping elements of an Enumerable by an attribute or an arbitrary grouping. The documentation provides a good example of how to group by an attribute; however, the group_by method can be used logically group by anything returned from the block given to group_by.

Test

require 'rubygems'
require 'active_support'
require 'test/unit'
require 'dust'

unit_tests do
test "group by grades" do
grades = [50, 55, 60, 62, 71, 83, 84, 85, 99]
expected = {"A"=>[99], "B"=>[83, 84, 85], "C"=>[71], "D"=>[60, 62], "F"=>[50, 55]}
actual = grades.group_by do |grade|
case
when grade < 60 then "F"
when grade < 70 then "D"
when grade < 80 then "C"
when grade < 90 then "B"
else "A"
end
end
assert_equal expected, actual
end
end

Ruby: Testing Private Methods

Jason Rudolph recently wrote about Testing Private Methods in Ruby. Before I continue, it's probably a good idea to note that I rarely test private methods. I prefer to test through the public API. However, there are times when life is easier if you write a few tests for a private method or two.

The solution highlighted by Jason (original implementation from Evan Phoenix) is nice from a maintainability perspective since it encapsulates changing the behavior of the class within the test. However, it's a bit verbose for my taste.

Reading the entry I couldn't help but think that gaining temporary access to a private method for testing seems like something that can be handled by a general solution instead.

Here's how I would have solved the problem.

require 'rubygems'
require 'dust'
require 'test/unit'

class Ninja
private
def kill(num_victims)
"#{num_victims} victims are no longer with us."
end
end

class Class
def publicize_methods
saved_private_instance_methods = self.private_instance_methods
self.class_eval { public *saved_private_instance_methods }
yield
self.class_eval { private *saved_private_instance_methods }
end
end

unit_tests do
test "kill returns a murder string" do
Ninja.publicize_methods do
assert_equal '3 victims are no longer with us.', Ninja.new.kill(3)
end
end
end

Sunday, November 11, 2007

Rails: Enumerable#index_by

Documentation
Convert an enumerable to a hash. Examples:

people.index_by(&:login)
=> { "nextangle" => , "chade-" => , ...}
people.index_by { |person| "#{person.first_name} #{person.last_name}" }
=> { "Chade- Fowlersburg-e" => , "David Heinemeier Hansson" => , ...}
Usage
I've used Enumerable#index_by for 2 different reasons recently. In one instance I needed faster access to find an element in an array. We found that pulling something out of a hash was faster than using the find method of array. We did a few benchmarks similar to the contrived examples below.
array = [1..100].to_a
hash = array.index_by { |element| element }
array_bm = Benchmark.measure do
array.find { |element| element = 99 }
end
hash_bm = Benchmark.measure do
hash.include? 99
end

p array_bm.real # => 1.382
p hash_bm.real # => 1.001
note: we couldn't use the include? method for our particular instance, so I left it out of the contrived example.

The benchmarks for our project revealed even larger gains, thus it made sense to convert our array to a hash and work with that instead of the array.

The other usage we found for index_by was to utilize the fact that index_by overwrites the value instead of appending (which group_by does). We had a list of scores and wanted to group them together. The group_by method could handle that case; however, we only wanted to keep track of the highest score per group. The test below shows an example where a list of grades can be grouped by grade letter and the highest score per grade letter.

Test

require 'rubygems'
require 'active_support'
require 'test/unit'
require 'dust'

unit_tests do
test "index by highest numeric per grade level" do
grades = [50, 55, 67, 62, 71, 89, 84, 85, 99]
expected = {"A"=>99, "B"=>89, "C"=>71, "D"=>67, "F"=>55}
actual = grades.sort.index_by do |grade|
case
when grade < 60 then "F"
when grade < 70 then "D"
when grade < 80 then "C"
when grade < 90 then "B"
else "A"
end
end
assert_equal expected, actual
end
end

Saturday, November 10, 2007

Rails: Integer#even?

Documentation
None

Usage
The Integer#even? method is helpful when adding zebra striping to views.

For example:
  <% @customers.each_with_index do |customer, index| %>
<div style="color:<%= index.even? ? "white" : "gray" %>">
<%= customer.name %>
</div>
<% end %>

Test

require 'rubygems'
require 'active_support'
require 'test/unit'
require 'dust'

unit_tests do
test "10 is even?" do
assert_equal true, 10.even?
end
end

Friday, November 09, 2007

Rails: String#each_char

Documentation
Yields a single-character string for each character in the string. When $KCODE = ‘UTF8’, multi-byte characters are yielded appropriately.
Usage
The String#each_char method is nice for iterating through a string, one character at a time. I generally use regex for string manipulation; however, when context within the string matters, each_char is helpful.

Test

require 'rubygems'
require 'active_support'
require 'test/unit'
require 'dust'

unit_tests do
test "each_char can be used to strip every other charater" do
strip, result = true, ""
"hello world".each_char do |char|
result << char if strip
strip = !strip
end
assert_equal "hlowrd", result
end
end

Thursday, November 08, 2007

Rails: Hash#diff

Documentation
None

Usage
The Hash#diff method is helpful for determining the difference between two hashes.

note: The receiver matches on both keys and values. If the pair is not matched it be returned. (for example: {:a => 1, :b => 3}.diff({:a=>2}) # => {:a=>1, :b=>3})

Test

require 'rubygems'
require 'active_support'
require 'test/unit'
require 'dust'

unit_tests do
test "find the difference between two hashes" do
assert_equal({:b=>3}, {:a => 1, :b => 3}.diff({:a=>1}))
end
end

Despised Software Luminaries

This morning I spent some time thinking about how software luminaries are generally loved or hated.

I've always been confused by people who really dislike Martin Fowler. Personally, I find Martin to be quite nice, but even if I didn't I would find it hard to disregard his passion for improving software development.

Obie Fernandez is another luminary who generally evokes love or hate. I'm friends with Obie as well as people who dislike Obie. Obie detractors often ask: How can you put up with him? The question is mostly targeted at Obie's passionate communication style. I've always thought that what Obie has to say is what's important, so I don't really care how he chooses to communicate it.

As I've gotten to know an increasing number of luminaries I noticed that things don't change at the top either: several of the luminaries seem to love and hate each other.

Why is it that software luminaries elicit such passionate feelings? I believe it's because software development (at an advanced level) is art. It's hard to find developers who can agree on architecture and it's hard to find artists who can agree on the best painting of all time. There is no right answer, it's contextual and subjective.

I also believe that luminaries (unconsciously) perpetuate the disdain with their passionate personalities. For example, a Martin Fowler averse developer once told me that Martin lives in a Java only world. If you read Martin's material you know that isn't the case; however, Martin does use Java examples very often. I don't think Martin uses Java because he prefers Java, I believe that Martin is passionate about reaching as many people as possible and therefore must use a widely known language. Unfortunately, the developer didn't see it from that perspective (I believe) because he was so focused on Ruby being better than Java that he was ready to dismiss anything pertaining to Java.

Software is an art and luminaries are passionate, I don't expect either of these things to change in the near future. Thus, I expect the disdain to remain as well, but perhaps being aware of the origin of the dislike can lead to a bit more tolerance within our passionate, artistic communities.

Wednesday, November 07, 2007

Rails: String#constantize

Documentation
Constantize tries to find a declared constant with the name specified in the string. It raises a NameError when the name is not in CamelCase or is not initialized.

Examples
  • "Module".constantize #=> Module
  • "Class".constantize #=> Class
Usage
Constantize is definitely the most used String method that I utilize while metaprogramming.

Test

require 'rubygems'
require 'active_support'
require 'test/unit'
require 'dust'

unit_tests do
class Klass
end

test "constantize a string representation of a class" do
assert_equal Klass, "Klass".constantize
end
end

Tuesday, November 06, 2007

Rails: String#classify

Documentation
Create a class name from a table name like Rails does for table names to models. Note that this returns a string and not a Class. (To convert to an actual class follow classify with constantize.)

Examples
  • "egg_and_hams".classify #=> "EggAndHam"
  • "post".classify #=> "Post"
Usage
The classify method is helpful when metaprogramming. I generally replace "strings".singularize.camelize with "strings".classify (and the classify implementation calls singularize and camelize for me).

Test

require 'rubygems'
require 'active_support'
require 'test/unit'
require 'dust'

unit_tests do
test "singularize and camelize a string via classify" do
assert_equal "Name", "names".classify
end
end

Monday, November 05, 2007

Rails: String#singularize

Documentation
The reverse of pluralize, returns the singular form of a word in a string.

Examples
  • "posts".singularize #=> "post"
  • "octopi".singularize #=> "octopus"
  • "sheep".singluarize #=> "sheep"
  • "word".singluarize #=> "word"
  • "the blue mailmen".singularize #=> "the blue mailman"
  • "CamelOctopi".singularize #=> "CamelOctopus"
Usage
I generally use singularize when metaprogrammming to convert from a potentially plural version of a class name to something that I know can be constantized.

Test

require 'rubygems'
require 'active_support'
require 'test/unit'
require 'dust'

unit_tests do
test "change plural word to singular word" do
assert_equal "class_name", "class_names".singularize
end
end

Sunday, November 04, 2007

Validatable 1.6.6 released

The 1.6.6 version of Validatable was released this morning.

This was a minor release that removed custom validation assertion syntax. (http://blog.jayfields.com/2007/09/ruby-creating-custom-assertions.html)

You can replace custom validation assertions with the newly introduced validate_only instance method. The validate_only method is my latest attempt to make it easy to test model validations one at a time.

The following code should provide a decent example.

require 'rubygems'
require 'test/unit'
require 'dust'
require 'validatable'

class SampleModel
include Validatable
validates_presence_of :name, :address
attr_accessor :name, :address
end

unit_tests do
test "individual validation for name is executed" do
instance = SampleModel.new
instance.validate_only("presence_of/name")
assert_equal "can't be empty", instance.errors.on(:name)
end

test "individual validation for address is not executed" do
instance = SampleModel.new
instance.validate_only("presence_of/name")
assert_equal nil, instance.errors.on(:address)
end
end


The validate_only method accepts an argument that is a DSL for running a single validation. The format of the DSL is "[validation type]/[key or attribute]". If the validation has a key set you should provide the key, otherwise the attribute will suffice. The documentation provides the following examples.
  • validates_presence_of :name can be run with obj.validate_only("presence_of/name")
  • validates_presence_of :birthday, :key => "a key" can be run with obj.validate_only("presence_of/a key")