Sonntag, 21. September 2014

5 Ruby shots!

1. Extract regular expression matches quickly!

String#match is the typical message receiver for examining String patterns. With the help of regular expressions the Stings patterns can be matched/ found like:
email = "chris@ruby.de"
email.match(/@((?:[-a-z0-9]+\.)+[a-z]{2,})/)[1]
can be refactored to:
email[/@((?:[-a-z0-9]+\.)+[a-z]{2,})/, 1]
which looks much cleaner. With Strings being a sequence (array) of characters in mind, the logic is even more compelling.
The API String#[] documentation reveals even more examples.

2. Use the method block shortcut!

Methods and especially enumerations and their blocks can look way too extensive in simple use cases, for example:
[1, 2, 3 ].map { |number| number.to_s } #=> ["1", "2", "3"]
[1, 2, 3 ].select { |number| number.odd? } #=> [1, 3]
Inside the block there is only one message sent to the iterated Fixnum object, without any parameters. The logic is super simple and that is why Ruby offers a shortcut for such. Please compare the examples with:
[1, 2, 3 ].map(&:to_s) # => ["1", "2", "3"]
[1, 2, 3 ].select(&:odd?) # => [1, 3]
Use the shortcut for all iterators, with the receiver (iterated object) only receiving one message (method call) inside the block and the message (method call) expecting any parameters.
It is implemented by passing the message (method call) as a symbol and an unary ampersand (&).
Please note that the shortcut is not an enumerator thing only. It can be used for every method expecting a block.

3. Explode enumerables!

There are methods having a variable length of parameters. Array#values_at is such a candidate. For example getting objects at specific indexes:
[1, 2, 3, 4, 5].values_at(0, 4) # => [1, 5]
works fine as long as the indexes are known. But if the number of indexes or the indexes themselves are calculated, there is no other option than dealing with an array and exploding it to a parameter list:
# indexes = [0, 4]
[1, 2, 3, 4, 5].values_at(*indexes) # => [1, 5]
In the case the sequence of numbers also is calculated, it can be achieved by exploding a range like:
first = 1
last = 5
numbers = *(first..last) # => [1, 2, 3, 4, 5]
indexes = [0, 4]
numbers.values_at(*indexes) # => [1, 5]
... so powerful.

4. Rescue without verbosity!

The verbose way of exception handling looks like:
def slice_it
  begin
    "Ruby".slice
  rescue ArgumentError => e
    $stderr.puts "Read the API documention of String#slice.
      Message: '#{e}'"
  end
end
but it can be less verbose without the explicit begin/ end block style, because method definitions are implicitly also exception blocks:
def slice_it
  "Ruby".slice
rescue ArgumentError => e
  $stderr.puts "Read the API documention of String#slice.
    Message: '#{e}'"
end

5. Identify the Hash values with non Strings/ Symbols!

Associative arrays (aka Hash) are key/ value stores in their basic meaning. The keys usually are symbols or strings. For example:
{ language: 'Ruby', 'version' => 2.1 }
Sometimes it is a better idea to have meaningful keys. Imagine a cache store for all people grouped by their birthday:
people = Person.all.group_by(&:birthday)
# => { Sat, 21 Sep 1996 => [#<Person 1>, #<Person 2>], 
Fri, 19 Apr 1996 => [#<Person 3>] }
which can be accessed easily:
people[18.years.ago]
# => [#<Person 1>, #<Person 2>]
In this case the keys are Date objects, but Hash keys can be any kind of objects.

Supported by Ruby 2.1.1