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

Ruby is Magic: Callbacks

Ruby is Magic: Callbacks

Talk (in German) with @railsbros_dirk about callbacks in Ruby at the Ruby user group Cologne (06/2012)

Sebastian Cohnen

June 20, 2012
Tweet

More Decks by Sebastian Cohnen

Other Decks in Technology

Transcript

  1. ponies = %w(Rarity Fluttershy Applejack) ponies.each do puts "And another

    pony!" end > "And another pony!" "And another pony!" "And another pony!
  2. class Movie def self.cast(pony) if pony.talents.include?(required_talent) message = "Congratulations! Welcome

    on the show!" else message = "Sorry pal. You're out." end pony.give_a_message(message) end end Movie.cast(pony)
  3. class Movie def self.cast(pony, callback_method) if pony.talents.include?(required_talent) message = "Congratulations!

    Welcome on the show!" else message = "Sorry pal. You're out." end pony.send(callback_method, message) end end Movie.cast(pony, :give_a_message)
  4. class Movie def self.cast(pony, callback) if pony.talents.include?(required_talent) message = "Congratulations!

    Welcome on the show!" else message = "Sorry pal. You're out." end callback.call(message) end end Movie.cast(pony) { |message| pony.give_message(message) }
  5. require 'eventmachine' require 'em-http' EventMachine.run do puts "Setting up HTTP

    request ..." url = 'http://www.equestriadaily.com/' http = EventMachine::HttpRequest.new(url).get http.callback { puts "Response Status: #{http.response_header.status}" EM.stop } puts "Fetched latest pony news!" end # Setting up HTTP request #1 # Fetched page #1 # Response Status: 200 Callback!
  6. • inherited • included • extended • append_features • const_missing

    • method_missing • method_added • method_removed • method_undefined • singleton_method_added • singleton_method_removed • singleton_method_undefined • ObjectSpace.define_finalizer(obj, aProc=proc()) • at_exit { block }
  7. class Pony def self.inherited(clazz) puts "'#{clazz}' is a special type

    of Pony." end end class Unicorn < Pony end # 'Unicorn' is a special type of Pony.
  8. module MagicPowers def self.included(receiver) puts "'#{receiver}' have now magical powers."

    end end class Unicorn include MagicPowers end # 'Unicorn' have now magical powers.
  9. module PonyExtensions module ClassMethods def homeland puts "Equestria" end end

    module InstanceMethods def name puts @name end end def self.included(receiver) receiver.extend ClassMethods receiver.send :include, InstanceMethods end end class Pony include PonyExtensions def initialize(name) @name = name end end Pony.homeland fluttershy = Pony.new("Fluttershy") fluttershy.name # Equestria # Fluttershy
  10. module Cloudsdale def self.extended(receiver) puts "'#{receiver}' has now access to

    Cloudsdale." end end class Pegasus extend Cloudsdale end # 'Pegasus' has now access to Cloudsdale.
  11. # pony.rb class Pony attr_reader :name def initialize(name) @name =

    name end end # autoloader.rb def Object.const_missing(const) puts "Could not find '#{const}'. Trying to load ..." require "./#{const.downcase}" puts "Success!" const_get(const) end p = Pony.new("Rainbow Dash") puts p.name # Could not find 'Pony'. Trying to load ... # Success! # Rainbow Dash
  12. class Pony def method_missing(meth, *args, &blk) if meth.to_s =~ /sing_/

    song = meth.to_s.split('_')[1..-1].collect(&:capitalize).join(' ') puts "Let's sing the song '#{song}'" else super end end end pinkie_pie = Pony.new pinkie_pie.sing_american_idiot pinkie_pie.dance # Let's sing the song 'American Idiot' # `method_missing': undefined method `dance' for #<Pony> (NoMethodError)
  13. module FunctionDecorators def decorate(decorator) @decorate_next_with = decorator end def method_added(name)

    if decorator_class = @decorate_next_with @decorate_next_with = nil DecoratorHelper.apply_decorator(decorator_class, name, self) end end end class DatabaseConnectorWithDecorator extend FunctionDecorators decorate CacheDecorator def count Logger.info "Called :count" return 1337 end end
  14. Fun Fact undef_method(symbol) → self Prevents the current class from

    responding to calls to the named method. Contrast this with remove_method, which deletes the method from the particular class; Ruby will still search superclasses and mixed-in modules for a possible receiver.
  15. class Pony def hello; "In Pony"; end end class Dragon

    < Pony def hello; "In Dragon"; end end d = Dragon.new p = Pony.new puts d.hello puts p.hello Dragon.send(:undef_method, :hello) puts d.hello # In Dragon # In Pony # undefined method `hello' for #<Dragon> (NoMethodError)
  16. ♌ gambit:rails[v3.2.3] $ git grep undef_method actionpack/lib/abstract_controller/base.rb: undef_method :not_implemented actionpack/lib/action_controller/metal/responder.rb:

    undef_method(:to_json) if method_defined?(:to_json) actionpack/lib/action_controller/metal/responder.rb: undef_method(:to_yaml) if method_defined?(:to_yaml) actionpack/test/lib/controller/fake_models.rb: undef_method :to_json activemodel/lib/active_model/attribute_methods.rb: instance_methods.each { |m| undef_method(m) } activemodel/test/cases/observing_test.rb: undef_method :observed_classes activerecord/lib/active_record/associations/collection_proxy.rb: instance_methods.each { |m| undef_method m unless m.to_s =~ … activerecord/lib/active_record/attribute_methods/read.rb: instance_methods.each { |m| undef_method(m) } activerecord/lib/active_record/attribute_methods/read.rb: undef_method :__temp__ activerecord/lib/active_record/attribute_methods/read.rb: undef_method :__temp__ activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb: undef_method :select_rows activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb: undef_method :execute activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb: undef_method :select activerecord/test/cases/attribute_methods_test.rb: Object.send(:undef_method, :title) # remove test method from object activerecord/test/cases/attribute_methods_test.rb: topic.method(:id).owner.send(:undef_method, :id) activerecord/test/cases/finder_test.rb: undef_method(:find_or_create_by_name) if … activesupport/lib/active_support/basic_object.rb: undef_method :== activesupport/lib/active_support/basic_object.rb: undef_method :equal? activesupport/lib/active_support/basic_object.rb: undef_method(m) if m.to_s !~ /(?:^__|^nil\?$|^send$|^object_id$)/ activesupport/lib/active_support/callbacks.rb: undef_method(name) if method_defined?(name) activesupport/lib/active_support/core_ext/rexml.rb: undef_method :unnormalized activesupport/lib/active_support/deprecation/proxy_wrappers.rb: instance_methods.each { |m| undef_method m unless m =~ /^__|^object_id$/ } activesupport/lib/active_support/option_merger.rb: undef_method(method) if method !~ /^(__|instance_eval|class|object_id)/ activesupport/test/core_ext/duration_test.rb: class << k; undef_method :== end Rails…
  17. class Pony include Callbacks before :throw_party, lambda { PartyCanon.fire! }

    after :throw_party, :cleanup def throw_party run_with_callbacks :throw_party do puts "We're having a great partey!" end end def cleanup puts "Cleaning up all this mess ..." end end applejack = Pony.new applejack.throw_party # I never leave without my party canon! # We're having a great partey! # Cleaning up all this mess ...
  18. module ClassMethods def before(method, callback) before_callbacks[method] ||= [] before_callbacks[method] <<

    callback end def before_callbacks @__before_callbacks ||= {} end def after(method, callback) after_callbacks[method] ||= [] after_callbacks[method] << callback end def after_callbacks @__after_callbacks ||= {} end def [](callback_type) send("#{callback_type}_callbacks") end end module InstanceMethods def run_with_callbacks(method, &impl) run_callbacks(:before, method) yield run_callbacks(:after, method) end def run_callbacks(type, meth) self.class[type][meth].each do |cb| case cb when Symbol self.send(cb) when Proc cb.call end end end end
  19. Entkoppelt Systemkomponenten Ermöglicht andere Programmierstile (evented, async) Callbacks auf Sprachebene

    bieten interessante Möglichkeiten Ruby-Callbacks erweitert Meta- Programming
  20. require "active_support" ActiveSupport::Notifications.subscribe("pony:created") do |name, payload| puts "Pony #{payload[:id]} has

    been created!" end ActiveSupport::Notifications.subscribe(%r{pony}) do |name, payload| puts "A pony event happend: #{name}, #{payload.inspect}" end ActiveSupport::Notifications.publish("pony:created", :id => 42) ActiveSupport::Notifications.publish("pony:updated", :id => 42) # Pony 42 has been created! # A pony event happend: pony:created, {:id => 42} # A pony event happend: pony:updated, {:id => 42}
  21. Thanks! Q & A? Dirk Breuer / @railsbros_dirk Sebastian Cohnen

    / @tisba ? “My Little Pony” © Hasbro Studios and DHX Media Vancouver rubyismagic.de