Slide 1

Slide 1 text

Bronie User Group Cologne

Slide 2

Slide 2 text

Ruby #7 “Closures”

Slide 3

Slide 3 text

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() Closures sind Codeblöcke, …

Slide 4

Slide 4 text

7

Slide 5

Slide 5 text

Blöcke @my_bag = Bag.new %w(MacBook Headphones iPhone Camera).each do |item| @my_bag.add item end @my_bag.each_item ?

Slide 6

Slide 6 text

class Bag def each_item @items.each do |item| yield item end end end @my_bag.each_item { |item| puts item }

Slide 7

Slide 7 text

%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’

Slide 8

Slide 8 text

Werden an Methoden übergeben Fangen den definierenden Kontext ein Erweitern den definierenden Kontext nicht Können nicht herumgereicht oder jederzeit aufgerufen werden Blöcke

Slide 9

Slide 9 text

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 }

Slide 10

Slide 10 text

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!

Slide 11

Slide 11 text

“echte” Closures? &block ohne & ist wie Proc.new(&block) proc {} lambda {}

Slide 12

Slide 12 text

Kontrollfluss & Aritätsprüfung

Slide 13

Slide 13 text

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)

Slide 14

Slide 14 text

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!" })

Slide 15

Slide 15 text

LocalJumpError: unexpected return 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!" })

Slide 16

Slide 16 text

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.’

Slide 17

Slide 17 text

Aritätsprüfung arity()-Methode Instanzen von Proc.new prüfen die Artität nicht Closures durch lambda prüfen die Arität (in Ruby 1.9)

Slide 18

Slide 18 text

proc_closure = Proc.new do |arg1, arg2| puts "arg1: #{arg1}; arg2: #{arg2}" end proc_closure.call(1,2,3,4) # arg1: 1; arg2: 2 proc_closure.call(1,2) # arg1: 1; arg2: 2 proc_closure.call(1) # arg1: 1; arg2: nil Aritätsprüfung: Proc

Slide 19

Slide 19 text

lambda_closure = lambda do |arg1, arg2| puts "arg1: #{arg1}; arg2: #{arg2}" end lambda_closure.call(1,2,3,4) # ArgumentError lambda_closure.call(1,2) # arg1: 1; arg2: 2 lambda_closure.call(1) # ArgumentError Aritätsprüfung: Lambda

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

Beispiel: Lazy Collection

Slide 22

Slide 22 text

class BlogEntry class LazyLoadCollection include Enumerable def initialize(lazy_collection, after_load_callback = nil) @lazy_collection = lazy_collection @after_load_callback = after_load_callback.present? ? after_load_callback : lambda { |args| return args } @collection = @after_load_callback.call(@lazy_collection.call) end def each(&block) @collection.each(&block) end end class <

Slide 23

Slide 23 text

block (implizit übergeben) block (explizit übergeben) block (explizit übergeben und zu Proc) Proc.new proc (Alias auf lambda / Proc.new) lambda 6 Möglichkeiten

Slide 24

Slide 24 text

One More Thing …

Slide 25

Slide 25 text

No content

Slide 26

Slide 26 text

method()

Slide 27

Slide 27 text

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)

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

class CacheDecorator < BaseDecorator def call(*args) puts "Before closure" result = @closure.call(*args) puts "After closure" return result end end

Slide 30

Slide 30 text

Erkennen welche Methode zu dekorieren ist Methode extrahieren Decorator mit extrahierter Methode inititalisieren Proxy Methode definieren Binding vor Ausführung der “alten” Methode umsetzen Was müssen wir machen?

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

class BaseDecorator def bind_to(receiver) @closure = @closure.bind(receiver) end end

Slide 33

Slide 33 text

Thanks! Q & A? Dirk Breuer / @railsbros_dirk Sebastian Cohnen / @tisba Thanks to Paul Cantrell (http:/ /innig.net/software/ruby/closures-in-ruby.html) ? “My Little Pony” © Hasbro Studios and DHX Media Vancouver