Slide 1

Slide 1 text

Brony User Group Cologne hosted by 20.6.2012

Slide 2

Slide 2 text

Ruby #10 “callbacks”

Slide 3

Slide 3 text

“Don’t call us, we call you!”

Slide 4

Slide 4 text

ponies = %w(Rarity Fluttershy Applejack) ponies.each do puts "And another pony!" end > "And another pony!" "And another pony!" "And another pony!

Slide 5

Slide 5 text

Lose Kopplung Umkehrung des Kontrollfluss Information Hiding Separation of Concern … Callback Funktionen

Slide 6

Slide 6 text

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)

Slide 7

Slide 7 text

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)

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

Non-Blocking I/O

Slide 10

Slide 10 text

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!

Slide 11

Slide 11 text

Ruby Callbacks

Slide 12

Slide 12 text

• 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 }

Slide 13

Slide 13 text

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.

Slide 14

Slide 14 text

module MagicPowers def self.included(receiver) puts "'#{receiver}' have now magical powers." end end class Unicorn include MagicPowers end # 'Unicorn' have now magical powers.

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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.

Slide 17

Slide 17 text

Autoloader

Slide 18

Slide 18 text

# 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

Slide 19

Slide 19 text

Dynamic Methods

Slide 20

Slide 20 text

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 # (NoMethodError)

Slide 21

Slide 21 text

# ActiveRecord dynamic attribute-based finders User.find_by_email("[email protected]")

Slide 22

Slide 22 text

Previously on ‘Ruby is Magic’

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

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.

Slide 25

Slide 25 text

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 # (NoMethodError)

Slide 26

Slide 26 text

Use Case?!

Slide 27

Slide 27 text

♌ 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…

Slide 28

Slide 28 text

Real-World Example

Slide 29

Slide 29 text

ActiveSupport::Callbacks

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

Entkoppelt Systemkomponenten Ermöglicht andere Programmierstile (evented, async) Callbacks auf Sprachebene bieten interessante Möglichkeiten Ruby-Callbacks erweitert Meta- Programming

Slide 33

Slide 33 text

One More Thing …

Slide 34

Slide 34 text

No content

Slide 35

Slide 35 text

ActiveSupport::Notifications

Slide 36

Slide 36 text

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}

Slide 37

Slide 37 text

Thanks! Q & A? Dirk Breuer / @railsbros_dirk Sebastian Cohnen / @tisba ? “My Little Pony” © Hasbro Studios and DHX Media Vancouver rubyismagic.de