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

Class's Class is Class

Chris Kelly
December 06, 2012

Class's Class is Class

Introduction to Ruby metaprogramming given at SD Ruby. We start with the Ruby Object Model and work through the most common metaprogramming callbacks.

Chris Kelly

December 06, 2012
Tweet

More Decks by Chris Kelly

Other Decks in Programming

Transcript

  1. Code intended to make your application more flexible, easier to

    extend, and more maintainable. Friday, December 28, 12
  2. Code intended to make your application more flexible, easier to

    extend, and more maintainable. Friday, December 28, 12
  3. Code intended to make your application more flexible, easier to

    extend, and more maintainable. Friday, December 28, 12
  4. Code intended to make your application more flexible, easier to

    extend, and more maintainable. Friday, December 28, 12
  5. Monkeypatching is a pejorative term for when you're doing something

    potentially dangerous. Friday, December 28, 12
  6. Monkeypatching is a pejorative term for when you're doing something

    potentially dangerous. Friday, December 28, 12
  7. Metaprogramming is excellent for building DSLs and for DRYing up

    code. It’s not excellent for being clever. Friday, December 28, 12
  8. Metaprogramming is excellent for building DSLs and for DRYing up

    code. It’s not excellent for being clever. Friday, December 28, 12
  9. Object Module Class Class Methods Instances Instance Methods Singleton Classes

    Singleton Methods Anonymous Classes Anonymous Methods Friday, December 28, 12
  10. GET READY FOR A RABBIT HOLE module Magic attr_accessor :magic_points

    def cast(spell) "You cast #{spell}" end end class Character attr_accessor :hit_points end class Wizard < Character include Magic attr_accessor :name def initialize(options = {}) @name = options.fetch :name, 'NPC' @hit_points = rand(1..4) end end Friday, December 28, 12
  11. DEEP GENE POOL gandalf = Wizard.new name: 'Gandalf' => #<Wizard:0x007fbea4a3cea0

    @name="Gandalf"> gandalf.class => Wizard gandalf.class.superclass => Character gandalf.class.superclass.superclass => Object gandalf.class.superclass.superclass.superclass => BasicObject gandalf.class.ancestors => [Wizard, Magic, Character, Object, Kernel, BasicObject] Where did these come from? Friday, December 28, 12
  12. YEP, EVERYTHING IS AN OBJECT Magic.class => Module Module.class =>

    Class Class.class => Class Module.superclass => Object Friday, December 28, 12
  13. YEP, EVERYTHING IS AN OBJECT Magic.class => Module Module.class =>

    Class Class.class => Class Module.superclass => Object Friday, December 28, 12
  14. Wizard Class: Class Super: Character Class Class: Class Super: Module

    Module Class: Class Super: Object Object Class: Class Super: BasicObject BasicObject Class: Class Super: nil Character Class: Class Super: Object Magic Class: Module Super: – gandalf Class: Wizard Super: – Friday, December 28, 12
  15. CALLING METHODS class Wizard < Character include Magic attr_accessor :name

    def initialize(options = {}) @name = options.fetch :name, 'NPC' @hit_points = rand(1..4) end end gandalf.name => "Gandalf" gandalf.hit_points => 3 gandalf.cast 'fireball' => "Gandalf casts fireball" Friday, December 28, 12
  16. CALLING METHODS class Wizard < Character include Magic attr_accessor :name

    def initialize(options = {}) @name = options.fetch :name, 'NPC' @hit_points = rand(1..4) end end gandalf.name => "Gandalf" gandalf.hit_points => 3 gandalf.cast 'fireball' => "Gandalf casts fireball" Neither are defined in Wizard class Friday, December 28, 12
  17. Wizard Class: Class Super: Character Class Class: Class Super: Module

    Module Class: Class Super: Object Object Class: Class Super: BasicObject BasicObject Class: Class Super: nil Character Class: Class Super: Object Magic Class: Module Super: – gandalf Class: Wizard Super: – Where is #cast? Friday, December 28, 12
  18. Wizard Class: Class Super: Character Class Class: Class Super: Module

    Module Class: Class Super: Object Object Class: Class Super: BasicObject BasicObject Class: Class Super: nil Character Class: Class Super: Object Magic Class: Module Super: – gandalf Class: Wizard Super: – Nope Where is #cast? Friday, December 28, 12
  19. Wizard Class: Class Super: Character Class Class: Class Super: Module

    Module Class: Class Super: Object Object Class: Class Super: BasicObject BasicObject Class: Class Super: nil Character Class: Class Super: Object Magic Class: Module Super: – gandalf Class: Wizard Super: – Nope Where is #cast? Friday, December 28, 12
  20. Wizard Class: Class Super: Character Class Class: Class Super: Module

    Module Class: Class Super: Object Object Class: Class Super: BasicObject BasicObject Class: Class Super: nil Character Class: Class Super: Object Magic Class: Module Super: – gandalf Class: Wizard Super: – Eureka! Where is #cast? Friday, December 28, 12
  21. Wizard Class: Class Super: Character Class Class: Class Super: Module

    Module Class: Class Super: Object Object Class: Class Super: BasicObject BasicObject Class: Class Super: nil Character Class: Class Super: Object Magic Class: Module Super: – gandalf Class: Wizard Super: – Not here Where is #hit_points? Friday, December 28, 12
  22. Wizard Class: Class Super: Character Class Class: Class Super: Module

    Module Class: Class Super: Object Object Class: Class Super: BasicObject BasicObject Class: Class Super: nil Character Class: Class Super: Object Magic Class: Module Super: – gandalf Class: Wizard Super: – Nor here Where is #hit_points? Friday, December 28, 12
  23. Wizard Class: Class Super: Character Class Class: Class Super: Module

    Module Class: Class Super: Object Object Class: Class Super: BasicObject BasicObject Class: Class Super: nil Character Class: Class Super: Object Magic Class: Module Super: – gandalf Class: Wizard Super: – Not here either! Where is #hit_points? Friday, December 28, 12
  24. Wizard Class: Class Super: Character Class Class: Class Super: Module

    Module Class: Class Super: Object Object Class: Class Super: BasicObject BasicObject Class: Class Super: nil Character Class: Class Super: Object Magic Class: Module Super: – gandalf Class: Wizard Super: – Found it! Where is #hit_points? Friday, December 28, 12
  25. LAST IN, FIRST OUT class Wizard < Character include Magic

    attr_accessor :name def initialize(options = {}) @name = options.fetch :name, 'NPC' @hit_points = rand(1..4) end end gandalf => #<Wizard:0x007fbea4b2f3d0 @name="Gandalf", @hit_points=1> First look at the instance Friday, December 28, 12
  26. LAST IN, FIRST OUT class Wizard < Character include Magic

    attr_accessor :name def initialize(options = {}) @name = options.fetch :name, 'NPC' @hit_points = rand(1..4) end end gandalf => #<Wizard:0x007fbea4b2f3d0 @name="Gandalf", @hit_points=1> Then, look in the class’s methods Friday, December 28, 12
  27. LAST IN, FIRST OUT class Wizard < Character include Magic

    attr_accessor :name def initialize(options = {}) @name = options.fetch :name, 'NPC' @hit_points = rand(1..4) end end gandalf => #<Wizard:0x007fbea4b2f3d0 @name="Gandalf", @hit_points=1> Next comes included modules Friday, December 28, 12
  28. LAST IN, FIRST OUT class Wizard < Character include Magic

    attr_accessor :name def initialize(options = {}) @name = options.fetch :name, 'NPC' @hit_points = rand(1..4) end end gandalf => #<Wizard:0x007fbea4b2f3d0 @name="Gandalf", @hit_points=1> Then onto the superclass Friday, December 28, 12
  29. LAST IN, FIRST OUT class Wizard < Character < Object

    < BasicObject include Magic attr_accessor :name def initialize(options = {}) @name = options.fetch :name, 'NPC' @hit_points = rand(1..4) end end gandalf => #<Wizard:0x007fbea4b2f3d0 @name="Gandalf", @hit_points=1> Continue until BasicObject Friday, December 28, 12
  30. LAST IN, FIRST OUT gandalf.class.ancestors => [Wizard, Magic, Character, Object,

    Kernel, BasicObject] gandalf.methods => [:name, :name=, :magic_points, :magic_points=, :cast, :hit_points, :hit_points=, :hit_points, :hit_points=, :nil?, :===, :=~, :~, :eql?, :hash, :<=>, :class, :singleton_class, :clone, :dup, :initialize_dup, :initialize_clone, :taint, :tainted?, :untaint, :untrust, :untrusted?, :trust, :freeze, :frozen?, :to_s, :inspect, :methods, :singleton_methods, :protected_methods, :private_methods, :public_methods, :instance_variables, :instance_variable_get, :instance_variable_set, :instance_variable_defined?, :instance_of?, :kind_of?, :is_a?, :tap, :send, :public_send, :respond_to?, :respond_to_missing?, :extend, :display, :method, :public_method, :define_singleton_method, :object_id, :to_enum, :enum_for, :==, :equal?, :!, :! =, :instance_eval, :instance_exec, :__send__, :__id__] BasicObject.instance_methods => [:==, :equal?, :!, :!=, :instance_eval, :instance_exec, :__send__, :__id__] Friday, December 28, 12
  31. LAST IN, FIRST OUT gandalf.class.ancestors => [Wizard, Magic, Character, Object,

    Kernel, BasicObject] gandalf.methods => [:name, :name=, :magic_points, :magic_points=, :cast, :hit_points, :hit_points=, :hit_points, :hit_points=, :nil?, :===, :=~, :~, :eql?, :hash, :<=>, :class, :singleton_class, :clone, :dup, :initialize_dup, :initialize_clone, :taint, :tainted?, :untaint, :untrust, :untrusted?, :trust, :freeze, :frozen?, :to_s, :inspect, :methods, :singleton_methods, :protected_methods, :private_methods, :public_methods, :instance_variables, :instance_variable_get, :instance_variable_set, :instance_variable_defined?, :instance_of?, :kind_of?, :is_a?, :tap, :send, :public_send, :respond_to?, :respond_to_missing?, :extend, :display, :method, :public_method, :define_singleton_method, :object_id, :to_enum, :enum_for, :==, :equal?, :!, :! =, :instance_eval, :instance_exec, :__send__, :__id__] BasicObject.instance_methods => [:==, :equal?, :!, :!=, :instance_eval, :instance_exec, :__send__, :__id__] Friday, December 28, 12
  32. SINGLETON METHODS gandalf_the_white = gandalf.clone => #<Wizard:0x007ffa44195e38 @name="Gandalf", @hit_points=2> gandalf_the_white.define_singleton_method(:cast)

    do |spell| super(spell).upcase end => #<Proc:0x007ffa44186050@(irb):4 (lambda)> gandalf.cast 'fireball' => "Gandalf casts fireball" gandalf_the_white.cast 'fireball' => "GANDALF CASTS FIREBALL" Friday, December 28, 12
  33. SINGLETON CLASS gollum = Object.new => #<Object:0x007ffa440c2560> def gollum.speak "My

    precious" end => nil gollum.class => Object metaclass = class << gollum; self; end => #<Class:#<Object:0x007ffa440c2560>> metaclass.class => Class metaclass.instance_methods(false) => [:speak] Friday, December 28, 12
  34. OPEN CLASSES, REWRITE METHODS module Canine def bark "Arff" end

    end class Dog include Canine def bark "Woof" end end class Dog def bark "Yip" end end Friday, December 28, 12
  35. DEFINE_METHOD() class Dog define_method :bark, ->(sound) { "#{sound} #{sound}" }

    def bark(sound) "#{sound} #{sound}" end end fido = Dog.new fido.bark 'woof' => "woof woof" These are the same Friday, December 28, 12
  36. CLASS_EVAL() Canine = Class.new Canine.class_eval do define_method :bark!, ->(sound) {

    sound.upcase } end dingo = Canine.new dingo.bark! 'woof' => "WOOF" Friday, December 28, 12
  37. INHERITED() # Invoked whenever a subclass is created class Animal

    def self.inherited(subclass) puts "#{subclass} has evolved from #{self}" end end class Dog < Animal; end Dog has evolved from Animal => true Friday, December 28, 12
  38. INCLUDED() # Invoked whenever the module is included within another

    class or module module Magic def self.included(klass) puts "#{klass} can now use #{self}" end end class Wizard include Magic end Wizard can now use Magic => true Friday, December 28, 12
  39. EXTENDED() # Invoked whenever the module is extended within another

    class or module module Magic def self.extended(klass) puts "#{klass} can now use #{self}" end end class Wizard extend Magic end Wizard can now use Magic => true Friday, December 28, 12
  40. METHOD_{ADDED,REMOVED}() # Invoked whenever a method is {added,removed} in class

    or module module Magic def self.method_added(method_name) puts "You can now cast #{method_name}" end def self.method_removed(method_name) puts "You forgot how to cast #{method_name}" end def fireball; end remove_method :fireball end You can now cast fireball You forgot how to cast fireball => true Friday, December 28, 12
  41. METHOD_MISSING() # Invoked whenever an object receives a message it

    cannot handle module Magic def method_missing(name, *args, &block) if /cast_/.match name cast name.to_s.gsub('cast_', '') else super end end def cast(name) "You cast #{name}" end end class Wizard; include Magic; end gandalf = Wizard.new => #<Wizard:0x007fa2db173ad8> gandalf.cast_banish_balrog => "You cast banish_balrog" Friday, December 28, 12
  42. DYNAMIC GETTERS/SETTERS module GetterSetters def getter_setter(*attributes) attributes.each do |attribute| define_method(attribute)

    do instance_variable_get "@#{attribute}" end define_method("#{attribute}=") do |value| instance_variable_set "@#{attribute}", value end end end end class Wizard extend GetterSetters getter_setter :name, :magic_points end Friday, December 28, 12
  43. DYNAMIC GETTERS/SETTERS gandalf = Wizard.new => #<Wizard:0x007fe17aa14d08> gandalf.name = 'Gandalf'

    => "Gandalf" gandalf.name => "Gandalf" gandalf.magic_points = 6 => 6 gandalf.magic_points => 6 Friday, December 28, 12
  44. INSTRUMENTING UNICORN # rpm/lib/new_relic/agent/instrumentation/unicorn_instrumentation.rb DependencyDetection.defer do @name = :unicorn depends_on

    do defined?(::Unicorn) && defined?(::Unicorn::HttpServer) end executes do NewRelic::Agent.logger.debug 'Installing Unicorn instrumentation' NewRelic::Agent.logger.info 'Detected Unicorn, ...' end executes do Unicorn::HttpServer.class_eval do old_worker_loop = instance_method(:worker_loop) define_method(:worker_loop) do |worker| NewRelic::Agent.after_fork(:force_reconnect => true) old_worker_loop.bind(self).call(worker) end end end end Friday, December 28, 12
  45. DRINKS ON US! WARNING: I reserve the right to try

    to recruit you. Friday, December 28, 12