2. Closures sind Codeblöcke, …
die zugewiesen und rumgereicht werden können.
die jederzeit und von jedem aufgerufen werden können.
die Zugriff auf Variablen im ursprünglich definierenden Kontext haben.
Alle antworten auf call()
5. class Bag
def each_item
@items.each do |item|
yield item
end
end
end
@my_bag.each_item { |item| puts item }
6. %w(MacBook Headphones iPhone Camera).each do |item|
item_count ||= 0
@my_bag.add item
item_count += 1
end
puts "#{item_count} item(s) have been added to my bag."
NameError: undefined local variable or method ‘item_count’
7. Blöcke
Werden an Methoden übergeben
Fangen den definierenden Kontext ein
Erweitern den definierenden Kontext nicht
Können nicht
herumgereicht oder
jederzeit aufgerufen werden
8. class Bag
def initialize(items)
@items = items
end
def each_item(&block)
@items.each(&block)
end
end
bag = Bag.new %w(MacBook Headphones Keys)
bag.each_item { |item| puts item }
9. class Bag
def initialize(items)
@items = items
end
def define_iterator(&block)
@iterator = block # Proc.new &block
end
def iterate!
@items.each(&@iterator)
end
end
bag = Bag.new(%w(MacBook Headphones Keys))
bag.define_iterator { |item| puts item }
bag.iterate!
12. Kontrollfluss
Proc.new ist abhängig von dem definierenden Kontext
lambda verhält sich wie eine Methode (“true closure”)
proc ist ein Alias auf lambda (Ruby 1.8)
proc ist ein Alias auf Proc.new (Ruby 1.9)
13. def call_closure(closure)
puts "Calling a closure"
result = closure.call
puts "The result of the call was: #{result}"
end
call_closure(Proc.new { return "All hell breaks loose!" })
LocalJumpError: unexpected return
14. def cc(closure)
puts "Calling a closure"
result = closure.call
puts "The result of the call was: '#{result}'"
end
cc(lambda { return "Everypony calm down. All is good." })
Calling a closure
The result of the call was: ‘Everypony calm down. All is good.’
18. Fun facts
In Ruby 1.8
lambda {||}.artiy != lambda {}.arity
lambda {}.arity == -1
lambda checkt nicht die Argumente, wenn Arität 1 ist WTF!?
In Ruby 1.9
lambda {}.arity == lambda {||}.arity == 0
24. class Bag
def each_item(closure)
@items.each { |item| closure.call(item) }
end
end
class Iterator
def self.print_element(element)
puts "Element: #{element}"
end
end
my_bag = Bag.new(%w(MacBook Headphones iPad Gloves))
my_bag.each_item lambda { |item| puts "Element: #{item}" }
my_bag.each_item Iterator.method(:print_element)
25. class DBLayer
decorate CacheDecorator
def find(id)
puts "Called :find with #{id}"
puts "I am: #{self}"
end
def destroy; end
def create; end
decorate CacheDecorator
def count
puts "Called :count"
return 1337
end
end
26. class CacheDecorator < BaseDecorator
def call(*args)
puts "Before closure"
result = @closure.call(*args)
puts "After closure"
return result
end
end
27. Was müssen wir machen?
Erkennen welche Methode zu dekorieren ist
Methode extrahieren
Decorator mit extrahierter Methode inititalisieren
Proxy Methode definieren
Binding vor Ausführung der “alten” Methode umsetzen
28. module FunctionDecorators
def self.apply_decorator(decorator, method_name, target)
decorated_method = target.instance_method(method_name)
target.send(:remove_method, method_name)
target.__decorators[method_name] = decorator.new(decorated_method)
params = decorated_method.parameters.collect(&:last).join(',')
class_eval <<-RUBY
def #{method_name}(#{params})
self.class.__decorators[:#{method_name}].bind_to(self)
self.class.__decorators[:#{method_name}].call(#{params})
end
RUBY
end
end
29. class BaseDecorator
def bind_to(receiver)
@closure = @closure.bind(receiver)
end
end