Arturo Herrero

Closure Design Patterns. Ruby Edition

Back in 2012 I wrote an article about closure design patterns. I used Groovy as a programming language but now I have decided to use Ruby (the programming language that I mainly have been using for two years). This article was featured on issue #231 of Ruby Weekly newsletter.

I want to keep the same examples, so I have created a helper method to make assertions.

def assert(expression)
  raise "Assertion failed" unless expression
end

Execute Around Method

A pair of operations that needs to be performed before and after operations.

def operations(&block)
  puts "Open"
  block.call
  puts "Close"
end

operations { puts "Operation" }

=> Open
=> Operation
=> Close

Pluggable Behavior

Specifies the behavior of an object at runtime.

def select_values(number, &block)
  list = []
  1.upto(number) do |i|
    list << i if block.call(i)
  end
  return list
end

assert [2, 4, 6, 8, 10] == select_values(10) { |i| i % 2 == 0 }  # even
assert [1, 3, 5, 7, 9]  == select_values(10) { |i| i % 2 != 0 }  # odd

Iterator Pattern

Allows sequential access to the elements.

def list_numbers(&block)
  (0..3).each(&block)
end

list_numbers do |i|
  if i < 2
    puts "#{i} is a little number"
  else
    puts "#{i} is a big number"
  end
end

=> 0 is a little number
=> 1 is a little number
=> 2 is a big number
=> 3 is a big number

Dynamical Conditional Execution

Creates and executes a conditional operation.

def greet(user, success_block, fail_block)
  if user.is_admin?
    success_block.call
  else
    fail_block.call
  end
end

greet(user, -> { puts "Hi Admin!" }, -> { puts "Hello User" })

Template Method Pattern

Defines common algorithm steps (getting a customer) and customizations (passed as a block).

def with_customer(id, &block)
  customer = get_customer(id)
  block.call(customer)
end

with_customer(1234) do |customer|
  puts "Found customer #{customer.name}"
end

Loan Pattern

Ensures that a resource is deterministically disposed of once it goes out of scope.

def with_list_of_words_for_each_line(file, &block)
  file = File.open(file)
  file.each_line { |line| block.call(line.split(' ')) }
ensure
  file.close
end

with_list_of_words_for_each_line(file) do |word_list|
  puts word_list
end

Command Design Pattern

Encapsulates all the information needed to call a method at a later time.

count = 0
commands = []

1.upto(10) do
  commands << -> { count += 1 }
end

puts "count is initially #{count}"
commands.each do |cmd|
  cmd.call
end
puts "did all commands, count is #{count}"

=> count is initially 0
=> did all commands, count is 10

Strategy Pattern

Defines a family of interchangeable algorithms.

calc_mult = ->(n, m) { n * m }
calc_adds = lambda do |n, m|
  result = 0
  n.times { result += m }
  return result
end

calc_strategies = [calc_mult, calc_adds]
calc_strategies.each do |calc|
  assert 10 == calc.call(5, 2)
end

Factory Pattern

Abstracts the object creation process (currying as a function factory).

adder = ->(x, y) { x + y }.curry
incrementer = adder.call(1)

assert 5 == incrementer.call(4)

Method Combination

Builds a method from components.

sum = ->(collection) { collection.inject(:+) }
first2 = ->(collection) { collection.take(2) }
take2_and_add = ->(collection) { sum.call(first2.call(collection)) }

assert 3 == take2_and_add.call([1, 2, 3, 4, 5])

Closure Composition

Since Ruby doesn’t have closure composition I’d like to update my previous example with it.

class Proc
  def >>(f)
    ->(*args) { f.call(self.call(*args)) }
  end
end

sum = ->(collection) { collection.inject(:+) }
first2 = ->(collection) { collection.take(2) }
take2_and_add = first2 >> sum

assert 3 == take2_and_add.call([1, 2, 3, 4, 5])

January 26, 2015 | @ArturoHerrero