Mittwoch, 29. Mai 2013

Respond to method missing

You have to deal with dynamic method calls and somebody threw an "Implement method_missing" at you?
You google how to do it and maybe find an article like "method_missing, baby!". You code what you gotta code...
But that's only half of the truth. You probably missed to "respond_to?":
class Person
private
  def method_missing method_name, *args
    if %w(runs walks goes drives).include?(method_name.to_s)
      puts "#{self.class} #{method_name}."
    else
      super
    end
  end
end
which means, you broke the POLS (Principle Of Least Surprise):
person = Person.new
person.respond_to? :runs # => false
person.runs # => "Person runs."
Curious! An object, which claims not to respond to a receiver, but instead seem to respond? None would expect such behavior. The solution is to overwrite Person#respond_to? likewise:
class Person
  MOTIONS = %w(runs walks goes drives)
  def respond_to? method_name
    MOTIONS.include? method_name.to_s
  end

private
  def method_missing method_name, *args
    if MOTIONS.include?(method_name.to_s)
      puts "#{self.class} #{method_name}."
    else
      super
    end
  end
 end
and accordingly:
person = Person.new
person.respond_to? :runs # => true
person.runs # => "Person runs."
If it walks like a duck and if it quacks like duck, it is a duck. Ruby duck typing.
Lastly I want to point to another great article about the Principle Of Least Surprise: The Tao Of Programming. Funny.

Supported by Ruby 1.9.3

Montag, 13. Mai 2013

method_missing, baby!

Meta programming in Ruby is a pleasure. And there are a lot of reasons for. One is method_missing.
A famous example of its "magic" is ActiveRecord and the dynamic finders in Ruby On Rails (active _record/dynamic_matchers.rb).
The approach is simple. Every time an object method was called, which is not defined, (or better a receiver gets an unknown message), Delegator#method_missing cares about it. You can overwrite method_missing in your class. And that's the hook that can be used for dynamics. And it IS used widely.
I demonstrate how to, in a very simple example.
The story is: I want people to move in different ways:
class Person
private
  def method_missing method_name, *args
    if %w(runs walks goes drives).include?(method_name.to_s)
      move method_name
    else
      super
    end
  end

  def move kind
    puts "#{self.class} #{kind}."
  end
end
There is no public method defined in the class "Person". It looks like no person can move in any way. But:
Person.new.runs
returns:
Person runs.
So what happened?
A new person object was instanciated and an message ("runs") was sent to it. Simply because the message is not defined, method_missing is sent instead. Please read the overwritten Person#method_missing carefully. If the message ("runs") is one of "runs", "walks", "goes" or "drives" the private method Person#move is sent internally. That's all. And that's why:
person = Person.new
person.walks
person.drives
returns:
Person walks.
Person drives.
And that's also why:
Person.new.swims
still calls the super method_missing:
undefined method 'swims' for # (NoMethodError)
Method missing, baby!

Supported by Ruby 1.9.3

Montag, 6. Mai 2013

Boolean method parameter assignment in Ruby

At first: prevent using boolean parameters in method definitions.
But let's assume you have to call a method that expects a boolean parameter like:
class Human
  def go backward=false
    puts "#{self.class} is going " + 
      (backward ? "backward" : "forward")
  end
end
A human can go forward and backward.
We want a human to go backward. If you would code it this way:
human = Human.new
# human goes backward
human.go true
please go on reading, because there is something to improve. At first the output is as expected:
Human is going backward
The code works but you have to agree, that "human.go true" is not readable (that's why there is a line of documentation). But we can improve the readability:
human = Human.new
human.go :backward
also results in:
Human is going backward
In Ruby a Symbol and all objects except instances of Nil and False class are true. Besides using an expressive Symbol can save the line of documentation, because the code documents itself. Awesome! Going forward could be default or explicit:
human = Human.new
human.go # default forward
human.go not(:backward) # explicit
The "not" before the Symbol inverts the assigned parameter. You also could use a bang ("human.go !:backward"). The result:
Human is going forward
Human is going forward
Though I'd prefer having a well designed API method ;-)

Supported by Ruby 1.9.3

Freitag, 3. Mai 2013

Ruby iterators

Ruby iterators are very sweet. But sometimes some of them seem to be ignored. And there is only reason I can imagine: they are not known. Instead, "Enumerable#each" is used as the universal weapon. No it's not. It's worth to read the API of Enumerable before coding an odd iteration. This is about your reputation. Let's start.

Use Case: How to accumulate a collection of items?

Enumerable#inject and its alias Enumerable#reduce iterate over all items and applies a binary operation to a memo (which contains the accumulated temporary value). A simple accumulation:
puts [2,3,4,5].inject { |sum, i| sum += i }
If a block is passed like here, the first parameter is the memo and the second is item itself.
The result is:
14
You also can set an initial value for the memo (sum):
puts [2,3,4,5].inject(1) { |sum, i| sum += i }
and the result is:
15
There is also a shorter way of the same accumulation by passing a symbol instead of the block. Then the items are sent to that method (in this case "+") of memo. Take a look:
puts [2,3,4,5].inject(1, :+)
with the same result:
15

Use Case: How to copy a collection and manipulate its items?

Enumerable#collect and its alias Enumerable#map iterate over all items like Enumerable#inject does, but applies an operation to each item (and that's the important difference). For example I could want to increment every number:
numbers = [2,3,4,5]
puts numbers.collect { |number| number += 1 }.inspect
Therefore it returns a new collection containing the modified items:
[3, 4, 5, 6]

Please note that the original collection "numbers" still contains the original items.

Use Case: How to search a collection for the first proper item?

Enumerable#find finds the first item in a collection for which the block returns true. For example I would want to find the first even number in a collection of numbers:
puts [3,4,2,5,1].find { |number| number.even? }
and found:
4

Please note, that 2 is also even but wasn't picked, because its index in the collection is higher than the index of 2.

Use Case: How to search a collection for every proper item?

Enumerable#find_all and its alias Enumerable#select are kind of similar to Enumerable#find, but they find ALL items in a collection for which the block returns true. That's why both return an array.
The same example collection of names:
puts [3,4,2,5,1].find_all { |number| number.even? }.inspect
finds not only 2, but also 4:
[4, 2]

Use Case: How to search a collection for every improper item?

Enumerable#reject is opposite to Enumerable#find_all. It finds all items for which the block returns FALSE. To me it exists more for readability reasons. But in the context of the same numbers example, which I used before, it would look like:
puts [3,4,2,5,1].reject { |number| number.even? }.inspect
copies the original array without the even numbers:
[3, 5, 1]

Use Case: How to search a collection for the item having the minimum value?

Enumerable#min_by can be confused with Enumerable#find, since it seems to do the same job. But a closer look, highlights that Enumerable#min_by is a more concise iterator for just value comparison. The same "even number" example:
puts [3,4,2,5,1].min_by { |number| number % 2 }
This time I determine the first even number with the modulo-2-algorithm (Numeric#even? also does modulo 2 internally). The first number with result 0 (minimum number) is picked for return:
4
You definitely would go for Enumerable#find to find the first even number, but I'd like to mention another example to illustrate the concision of Enumerable#min_by:
puts %w(Bob Al Susan).min_by { |name| name.length }
finds the shortest name:
"Al"

Use Case: How to search a collection for the item having the maximum value?

Enumerable#max_by is the sibling of Enumerable#min_by. It finds the first item having the maximum value:
puts %w(Bob Al Susan).max_by { |name| name.length }
finds the longest name:
"Susan"

Use Case: How to search a collection for the items having the minimum or maximum value?

Enumerable#minmax_by is the third sibling in family. It returns an array with 2 elements. One for the item having the minimum value and one for item having the maximum value:
puts %w(Bob Al Susan).minmax_by { |name| name.length }
finds the shortest and the longest name:
["Al", "Susan"]

Use Case: How to select all items of a collection as long as they are proper?

Enumerable#take_while iterates over a copied collection and keeps every item, for which the block returns true. If the first item is not proper due to the return value of the block, the iteration stops and returns the result array.
The names example:
puts %w(Bob Al Alexander Susan).take_while { |name| name.length < 8 }.inspect
stops during the third iteration ("Alexander" consists of 9 letters) and returns:
["Bob", "Al"]
Please note, that the fourth item ("Susan") wasn't selected, though it would satisfy the condition. And that is the difference to Enumerable#find_all.

Use Case: How to remove all items in a collection as long as they are proper?

Enumerable#drop_while iterates over a copied collection and removes every item from the new array, for which the block returns true. If the first item is not proper due to the return value of the block, the iteration stops and returns the result array.
The names example:
puts %w(Bob Al Alexander Susan).drop_while { |name| name.length < 8 }.inspect
stops during the third iteration ("Alexander" consists of 9 letters) and returns:
["Alexander", "Susan"]

Supported by Ruby 1.9.3