Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Ruby is Magic: Closures

Ruby is Magic: Closures

Welcome to 'Ruby is Magic'. This is the first installment of a semi-regular talk series @tisba and me gonna do on the Cologne Ruby user group (cologne.rb). If you're curious about the episode number, there are not previous episodes ^^ Anyways, the first episode is about closures. The slides are in German, but maybe you find the code examples helpful.

Copyright Notice: My Little Pony - Friendship is Magic is Property of Hasbro and The Hub.

Dirk Breuer

January 22, 2012
Tweet

More Decks by Dirk Breuer

Other Decks in Programming

Transcript

  1. 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, …
  2. 7

  3. class Bag def each_item @items.each do |item| yield item end

    end end @my_bag.each_item { |item| puts item }
  4. %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’
  5. Werden an Methoden übergeben Fangen den definierenden Kontext ein Erweitern

    den definierenden Kontext nicht Können nicht herumgereicht oder jederzeit aufgerufen werden Blöcke
  6. 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 }
  7. 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!
  8. 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)
  9. 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!" })
  10. 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!" })
  11. 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.’
  12. 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
  13. 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
  14. 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
  15. 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 <<self def find_all(language) lazy_feed = lambda { Nokogiri::XML(open(Rails.config.blog_feed_url)) } create_blog_entries = lambda do |feed| feed.xpath("//item").collect { |item| BlogEntry.new(xml_item) } end LazyLoadCollection.new lazy_feed, create_blog_entries end end def initialize(xml) self.attributes = (item.xpath("*/text()").inject({}) do |attributes, text| attributes[attribute_name] = text.content if text.parent.name.present? attributes end) end end
  16. block (implizit übergeben) block (explizit übergeben) block (explizit übergeben und

    zu Proc) Proc.new proc (Alias auf lambda / Proc.new) lambda 6 Möglichkeiten
  17. 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)
  18. 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
  19. class CacheDecorator < BaseDecorator def call(*args) puts "Before closure" result

    = @closure.call(*args) puts "After closure" return result end end
  20. 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?
  21. 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
  22. 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