Tuesday, November 13, 2007

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

Labels: ,




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

Labels: ,




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

Labels: ,




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

Labels: ,




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

Labels: ,




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

Labels: ,




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

Labels: ,




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

Labels: ,




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

Labels: ,




Sunday, November 04, 2007

Rails: String#pluralize

Documentation
Returns the plural form of the word in the string.

Examples
  • "post".pluralize #=> "posts"
  • "octopus".pluralize #=> "octopi"
  • "sheep".pluralize #=> "sheep"
  • "words".pluralize #=> "words"
  • "the blue mailman".pluralize #=> "the blue mailmen"
  • "CamelOctopus".pluralize #=> "CamelOctopi"
Usage
I generally use pluralize for creating nice messages for users. The pluralize method can also be helpful for metaprogramming when converting from a class name to a table name.

Test

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

unit_tests do
test "change singular word to plural word" do
assert_equal "names", "name".pluralize
end
end

Labels: ,




Saturday, November 03, 2007

Rails: String#camelize

Documentation
By default, camelize converts strings to UpperCamelCase. If the argument to camelize is set to ":lower" then camelize produces lowerCamelCase.

camelize will also convert ’/’ to ’::’ which is useful for converting paths to namespaces

Examples
  • "active_record".camelize #=> "ActiveRecord"
  • "active_record".camelize(:lower) #=> "activeRecord"
  • "active_record/errors".camelize #=> "ActiveRecord::Errors"
  • "active_record/errors".camelize(:lower) #=> "activeRecord::Errors"
Usage
I generally use camelize when metaprogrammming to convert from a underscored version of a class name.

Test

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

unit_tests do
test "change underscored word to camelized word" do
assert_equal "AClassName", "a_class_name".camelize
end
end

Labels: ,




Friday, November 02, 2007

Rails: Hash#assert_valid_keys

Documentation
None

Usage
Hash#assert_valid_keys gives you the ability to verify that each of the keys in the Hash are expected. For example, if you take an options Hash as an argument to a method you may want to validate that only expected options are passed as part of the hash; assert_valid_keys gives you the ability to list the keys you expect.

Test

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

unit_tests do
test