Sonntag, 25. Mai 2014

Raise a Ruby exception or better fail it!

In Ruby there are a lot method alias or synonyms doing the exactly same job. That is no mistake or arbitrary choice. That is an intended concept for writing expressive code.
It is the same with the Kernel#raise and Kernel#fail. Most Ruby developer use Kernel#raise for raising exceptions but only a few use Kernel#fail for the same goal. They miss the chance to improve the expressive power of their code. Jim Weirich was the first, who used Kernel#raise and Kernel#fail for different purposes.
Following that pattern is a stylistic decision.

1. Use fail for raising exceptions, when code fails in a true sense (means an unexpected behaviour that breaks the processing)

class Account
  def register name
    fail ArgumentError, "The assigned parameter is not a name." unless name.is_a? String
    "Hello #{name}!"
  end
end

2. Use raise for raising exceptions, when you catch the exception afterwards

class Person
  attr_accessor :salary
  def net_salary
    return 0 if salary.nil?
    begin
      salary - Calculator.tax(self)
    rescue NotImplementedError
      puts "Net is equal to pre-tax."
      salary
    end
  end
end

class Calculator
  def self.tax person
    raise NotImplementedError
  end
end
The intention behind is to use raise when the code does not fail, but explicitly raises an exception. In practice the mentioned scenario is rarely the case. Therefore indicating a failure by fail will become your natural friend.
Speaking of exception:
Do not use exceptions for flow control.
There you better go with throw/catch.

Supported by Ruby 2.1.1

Sonntag, 18. Mai 2014

Chain your Ruby methods!

Method chaining is a very helpful pattern for every API. In fact it is syntactic sugar, which eliminates the need for intermediate variables. But it requires the method to return something meaningful to chain on (read Return something meaningful!).
The benefits are:
  1. less intermediate variables (and therefore more readable)
  2. predictable results (and therefore a stable API)
Well, this example class in person.rb:
class Person
  def initialize name, gender
    @name = name
    @gender = gender
  end

  def marries person
    @marriage_partner = person
  end

  def bear_child
    return unless @gender.eql? 'f'
    @children ||= 0
    @children += 1
  end
end
can create a new person, who can marry another person and even bear children, if its gender is female. The class can be used like:
@alice = Person.new 'Alice', 'f'
@bob = Person.new 'Bob', 'm'
@alice.bear_child
@alice.marries @bob
The code can be refactored by method chaining into one single line:
Person.new('Alice', 'f').bear_child.marries(Person.new('Bob', 'm'))
=> NoMethodError: undefined method `marries' for 1:Fixnum
which fails, because the method bear_child returns a number (1) and not the expected Person object.
If the order of the chaining is changed:
Person.new('Alice', 'f').marries(Person.new('Bob', 'm')).bear_child
there is no exception, but it is still an unexpected result. It is even worse, because the bug is harder to debug. The reason is: in the example the method marries returns @bob, who obviously can not bear a child.
Well, methods can only be chained perfectly, if they return something meaningful and predictable.
The refactored Person class in person.rb:
class Person
  def initialize name, gender
    @name = name
    @gender = gender
  end

  def marries person
    @marriage_partner = person
    self
  end

  def bear_child
    return self unless @gender.eql? 'f'
    @children ||= 0
    @children += 1
    self
  end
end
Both methods marries and bear_child return self in every case. So that they can be chained together in a cascading style like:
Person.new('Alice', 'f').bear_child.marries(Person.new('Bob', 'm'))
 => #<Person:0x00000001be31f0 @name="Alice", @gender="f", @marriage_partner=#<Person:0x00000001be30b0 @name="Bob", @gender="m">, @children=1>
and it does not matter in which order:
Person.new('Alice', 'f').marries(Person.new('Bob', 'm')).bear_child
 => #<Person:0x00000001be31f0 @name="Alice", @gender="f", @marriage_partner=#<Person:0x00000001be30b0 @name="Bob", @gender="m">, @children=1>
the interim result is always the same expected person and the result state is equal.
Please note, that the meaning of nil is pretty low and even true and false are only meaningful in certain contexts.

Supported by Ruby 2.1.1

Sonntag, 11. Mai 2014

RSpec On Rails

In the previous post Do it RSpec the usage of RSpec in a Ruby project was described generally. This post explains how to code a simple RSpec test in a Ruby on Rails project.
The installation of RSpec itself is alike:
user$ gem install rspec
In a Rails project the rspec-rails gem has to be added to the Gemfile:
gem "rspec-rails", :group => [:test, :development]
Runing the Bundler:
user$ bundle
and preparing the Rails project for RSpec tests:
user$ rails g rspec:install
It generates the subfolder spec and a spec helper within. The spec helper file contains logic considering all tests. At this point only testing models is covered. That's why creating the folder:
user$ mkdir spec/models
Generating the model:
user$ rails g model Person name:string surname:string birthday:date active:boolean
The generated models/person.rb looks blank like:
class Person < ActiveRecord::Base
  attr_accessible :birthday, :name, :surname, :active
end
Some tests require test objects, called fixtures. Creating the folder for them:
user$ mkdir spec/fixtures
Fixtures are defined in a YAML notation, e.g.:
david:
id: 1 
name: David
surname: Heinemeier Hansson 
birthday: 1979-10-15
Saving this single fixture in the file spec/fixtures/people.yml. Please keep in mind, the more fixtures you create for several tests the harder to maintain changes of fixtures.
Creating the spec/models/person_spec.rb:
require 'spec_helper'

describe Person do
  fixtures :people
    
  before(:each) do
    @person = people(:david)
  end

  it "should not be valid without a name" do
    @person.name = ''
    @person.should_not be_valid
  end

  it "should be activated before validation" do
    @person.create.should be_active
  end
end
That are pretty little specifications. Before fullfilling them, the test was run:
user$ rspec spec/models/person_spec.rb
it returns:
Failures:

  1) Person should not be valid without a name
     Failure/Error: @person.should_not be_valid
       expected valid? to return false, got true
     # ./spec/models/person_spec.rb:12:in `block (2 levels) in '

  2) Person should be activated before validation
     Failure/Error: @person.should be_active
       expected active? to return true, got false
     # ./spec/models/person_spec.rb:17:in `block (2 levels) in '

Finished in 0.09145 seconds
2 examples, 2 failures
Implementing the logic to achieve the goals by enhancing the models/person.rb:
class Person < ActiveRecord::Base
  attr_accessible :birthday, :name, :surname, :active
  validates_presence_of :name
end
Again the test:
Failures:

  1) Person should be activated before validation
     Failure/Error: @person.should be_active
       expected active? to return true, got false
     # ./spec/models/person_spec.rb:17:in `block (2 levels) in '

Finished in 0.17313 seconds
2 examples, 1 failure
First requirement is satisfied. Enhancing the same model to strike the second:
class Person < ActiveRecord::Base
  attr_accessible :birthday, :name, :surname
  validates_presence_of :name
  before_validation :activate

private
  def activate
    self.active = true
  end
end
Running the tests again:
Finished in 0.11393 seconds
2 examples, 0 failures
signals: mission accomplished!

Supported by Ruby 2.1, RubyOnRails 3.2.17 and RSpec 2.12.0

Sonntag, 4. Mai 2014

Do it RSpec!

Needless to say that testing code is essential for beeing successful. When it comes to Ruby, there are a lot of helping test frameworks out there with different flaviours. In any case tests should imply specifications. On the basis of that awareness specifying first means writing tests first.
A roughly guideline for testing is:
  1. Test only your API (disclaim testing the private methods)
  2. Do not test tested code (Ruby, gems or frameworks).
  3. Write at least 1 test for every public method.
  4. Assert only 1 expectation of the tested method.
  5. Specify first, code afterwards
Installing the RSpec gem:
user$ gem install rspec
works.
Doing it the TDD way, the new feature method has to be described/ specified first. A convention is to name the spec file class-to-be-tested_spec.rb. In this example it is the example_spec.rb:
# example_spec.rb
require "./example.rb"
describe Example do
  it "should have a new awesome feature" do
    example = Example.new
    example.feature.should eq("Awesome")
  end
end
The Ruby file for testing (example.rb) has to be required on top of the test file as shown above. All tests of a class should be wrapped in a description, calling the RSpec describe. Aside the uninspired naming of the class (Example) it specifies the test in a humanized and readable manner and of course it tests the feature. The test itself is wrapped in a skeleton:
it "should describe your specification in a descriptive manner" do
  # assumption what your method should return according to your specification
end
In real life, methods may be a little bit more complex and therefore more than only one test could be reasonable.
Runing the test:
user$ rspec example_spec.rb
Since the feature was not implemented, the Example class returns:
Failures:

  1) Example should return feature
     Failure/Error: example.feature.should eq("Awesome")
       
       expected: "Awesome"
            got: nil
       
Finished in 0.00827 seconds
1 example, 1 failure
as expected.
After implementing the brand new fancy feature:
class Example
  def feature
    "Awesome"
  end
end
and running the test, it returns:
Finished in 0.00073 seconds
1 example, 0 failures
Great!
The more tests you will write using RSpec, the more you will get familiar with it. The A great resource for further reading is the documentation of RSpec itself.

Supported by Ruby 1.9.3 and RSpec 2.12.0