Slide 1

Slide 1 text

CLASS’S CLASS IS CLASS OR METAPROGRAMMING IS TROLLING YOU Friday, December 28, 12

Slide 2

Slide 2 text

MY NAME IS CHRIS KELLY Friday, December 28, 12

Slide 3

Slide 3 text

ON THE INTERNET, AMATEURHUMAN Friday, December 28, 12

Slide 4

Slide 4 text

TWITTER.COM/ AMATEURHUMAN Friday, December 28, 12

Slide 5

Slide 5 text

GITHUB.COM/ AMATEURHUMAN Friday, December 28, 12

Slide 6

Slide 6 text

SPEAKERDECK.COM/ AMATEURHUMAN Friday, December 28, 12

Slide 7

Slide 7 text

I WORK AT NEW RELIC Friday, December 28, 12

Slide 8

Slide 8 text

DEVELOPER EVANGELIST COFFEE DRINKER SOAPBOX STANDER HAPPINESS ENGINEER Friday, December 28, 12

Slide 9

Slide 9 text

Code that writes code? Friday, December 28, 12

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

To understand metaprogramming is to understand Ruby’s object model. Friday, December 28, 12

Slide 19

Slide 19 text

To understand metaprogramming is to understand Ruby’s object model. Friday, December 28, 12

Slide 20

Slide 20 text

To understand metaprogramming is to understand Ruby’s object model. Friday, December 28, 12

Slide 21

Slide 21 text

OBJECT MODEL Friday, December 28, 12

Slide 22

Slide 22 text

Object Module Class Class Methods Instances Instance Methods Singleton Classes Singleton Methods Anonymous Classes Anonymous Methods Friday, December 28, 12

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

DEEP GENE POOL gandalf = Wizard.new 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

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

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 => # First look at the instance Friday, December 28, 12

Slide 39

Slide 39 text

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 => # Then, look in the class’s methods Friday, December 28, 12

Slide 40

Slide 40 text

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 => # Next comes included modules Friday, December 28, 12

Slide 41

Slide 41 text

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 => # Then onto the superclass Friday, December 28, 12

Slide 42

Slide 42 text

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 => # Continue until BasicObject Friday, December 28, 12

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

SINGLETON METHODS gandalf_the_white = gandalf.clone => # gandalf_the_white.define_singleton_method(:cast) do |spell| super(spell).upcase end => # gandalf.cast 'fireball' => "Gandalf casts fireball" gandalf_the_white.cast 'fireball' => "GANDALF CASTS FIREBALL" Friday, December 28, 12

Slide 46

Slide 46 text

SINGLETON CLASS gollum = Object.new => # def gollum.speak "My precious" end => nil gollum.class => Object metaclass = class << gollum; self; end => #> metaclass.class => Class metaclass.instance_methods(false) => [:speak] Friday, December 28, 12

Slide 47

Slide 47 text

Metaprogramming is about getting methods where they’re needed. Friday, December 28, 12

Slide 48

Slide 48 text

Metaprogramming is about getting methods where they’re needed. Friday, December 28, 12

Slide 49

Slide 49 text

TOOLS OF THE METAPROGRAMMER Friday, December 28, 12

Slide 50

Slide 50 text

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

Slide 51

Slide 51 text

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

Slide 52

Slide 52 text

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

Slide 53

Slide 53 text

THE CALLBACKS Friday, December 28, 12

Slide 54

Slide 54 text

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

Slide 55

Slide 55 text

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

Slide 56

Slide 56 text

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

Slide 57

Slide 57 text

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

Slide 58

Slide 58 text

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 => # gandalf.cast_banish_balrog => "You cast banish_balrog" Friday, December 28, 12

Slide 59

Slide 59 text

A BASIC EXAMPLE Friday, December 28, 12

Slide 60

Slide 60 text

DYNAMIC GETTERS/SETTERS class Wizard def name @name end def name=(name) @name = name end end Friday, December 28, 12

Slide 61

Slide 61 text

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

Slide 62

Slide 62 text

DYNAMIC GETTERS/SETTERS gandalf = Wizard.new => # gandalf.name = 'Gandalf' => "Gandalf" gandalf.name => "Gandalf" gandalf.magic_points = 6 => 6 gandalf.magic_points => 6 Friday, December 28, 12

Slide 63

Slide 63 text

A NEW RELIC EXAMPLE Friday, December 28, 12

Slide 64

Slide 64 text

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

Slide 65

Slide 65 text

THANKS Friday, December 28, 12

Slide 66

Slide 66 text

DRINKS ON US! Friday, December 28, 12

Slide 67

Slide 67 text

DRINKS ON US! WARNING: I reserve the right to try to recruit you. Friday, December 28, 12