Slide 1

Slide 1 text

Metaprogramming RUBY for Greater Good

Slide 2

Slide 2 text

Who?

Slide 3

Slide 3 text

@jarkko jlaine.net [en-us] notkea.fi [fi-fi]

Slide 4

Slide 4 text

bearmetal.eu

Slide 5

Slide 5 text

What?

Slide 6

Slide 6 text

Code that writes code

Slide 7

Slide 7 text

“Writing code that manipulates language constructs at runtime.” Paolo Perrotta

Slide 8

Slide 8 text

Why?

Slide 9

Slide 9 text

Magic

Slide 10

Slide 10 text

Not clever, Flexible

Slide 11

Slide 11 text

Why Ruby?

Slide 12

Slide 12 text

In Ruby Metaprogramming is just Programming

Slide 13

Slide 13 text

Advanced OO concepts in Ruby

Slide 14

Slide 14 text

Prototypes vs Classes

Slide 15

Slide 15 text

speaker = Object.new def speaker.talk_length @talk_length ||= 30 end def speaker.talk_length=(length) @talk_length = length end

Slide 16

Slide 16 text

gary = speaker.clone gary.talk_length # => 30 gary.talk_length = 60 gary.talk_length # => 60 scott = gary.clone scott.talk_length # => 60 scott.talk_length = 45 scott.talk_length # => 45

Slide 17

Slide 17 text

No content

Slide 18

Slide 18 text

Everything is executable

Slide 19

Slide 19 text

class Conference puts "Hello world (open)" def venue end # … end # Hello world (open) # => nil

Slide 20

Slide 20 text

class Conference < Event end > c = Conference.new > c.class => Conference > c.class.superclass

Slide 21

Slide 21 text

class Conference < Event end > c = Conference.new > c.class => Conference > c.class.superclass => ChunkyBacon

Slide 22

Slide 22 text

class ChunkyBacon end Event = ChunkyBacon class Conference < Event end > c = Conference.new > c.class => Conference > c.class.superclass => ChunkyBacon

Slide 23

Slide 23 text

def gimme_a_class [Array, Hash, String][rand(3)] end class Conference < gimme_a_class() end > c = Conference.new > c.class => Conference > c.class.superclass => ChunkyBacon

Slide 24

Slide 24 text

Open classes class String alias_method :old_reverse, :reverse def reverse old_reverse.upcase end end > speaker = "Gary" > speaker.reverse => "YRAG"

Slide 25

Slide 25 text

Everything is an object > String.instance_method(:reverse) => #

Slide 26

Slide 26 text

Even classes

Slide 27

Slide 27 text

Wait, what?

Slide 28

Slide 28 text

But, classes have class methods. They are special.

Slide 29

Slide 29 text

No.

Slide 30

Slide 30 text

class Conference def self.in_finland # return conferences in Finland end end

Slide 31

Slide 31 text

Singleton methods def speaker.talk_length @talk_length ||= 30 end Defined for a single object

Slide 32

Slide 32 text

Ruby method lookup Right, then up

Slide 33

Slide 33 text

Where are singleton methods sitting?

Slide 34

Slide 34 text

Singleton class aka ghost class, eigenclass, metaclass anonymous class below object's own class

Slide 35

Slide 35 text

No content

Slide 36

Slide 36 text

Singleton class Just a regular class, except » Hidden from the generic class hierarchy » Cannot be inherited directly » Only ever has a single instance of itself

Slide 37

Slide 37 text

So, what are class methods?

Slide 38

Slide 38 text

So, what are class methods? Singleton methods for the class object itself

Slide 39

Slide 39 text

So, what are class methods? Singleton methods for the class object itself Residing in the singleton class of the class object

Slide 40

Slide 40 text

No content

Slide 41

Slide 41 text

Singleton classes are classes and classes are objects, so…

Slide 42

Slide 42 text

…a singleton class must have its own singleton class

Slide 43

Slide 43 text

Singleton classes all the way down

Slide 44

Slide 44 text

Metaprogramming, anyone?

Slide 45

Slide 45 text

Creating code dynamically in Ruby

Slide 46

Slide 46 text

Creating code dynamically in Ruby » eval » instance_eval & class_eval » define_method & Class.new » method_missing

Slide 47

Slide 47 text

eval meth = "my_method" eval <<-END def #{meth} "foo" end END

Slide 48

Slide 48 text

eval » Just executes a line of code » Super powerful… » …and dangerous in wrong hands » Code injection hazard

Slide 49

Slide 49 text

instance_eval » Can also take a string, but also a code block… » …which it then executes in the scope of the receiver. » Methods will become class methods (because self == the class) obj.instance_eval do self # => obj @v # => obj's instance var end

Slide 50

Slide 50 text

instance_eval class Turtle def move(&block) instance_eval(&block) end def right(n); end; # … end t = Turtle.new t.move do right(3) up left(2) down end

Slide 51

Slide 51 text

class_eval » Can only be called for classes and modules » creates instance methods » can be used to do stuff inside the class definition [String, Array, Hash].each do |cls| cls.class_eval { include HelloWorld } end

Slide 52

Slide 52 text

define_method » Create a method dynamically » Can be put inside other methods class Cat < Animal [:leg, :head, :tail].each do |part| define_method(part) do # … end end end

Slide 53

Slide 53 text

method_missing The catch-all method » Called whenever a method is not found in the inheritance chain. » By default throws a NoMethodError. » Can be modified to catch any method call, thus “creating” a ghost method » The name of which you don't have to even know.

Slide 54

Slide 54 text

method_missing class Speaker def method_missing(met, *args) if met.to_s == "speak" "I might as well say something: #{args[0]}" else super end end end gary = Speaker.new gary.talk("Destroy it") # => NoMethodError gary.speak("Just destroy it!") # => "I might as well say something: Just destroy it!"

Slide 55

Slide 55 text

Example Rewrite attr_accessor in Ruby

Slide 56

Slide 56 text

class Animal attr_accessor :legs, :diet end cat = Animal.new cat.legs = 4 cat.legs # => 4 cat.diet = "Small birds" cat.diet # => "Small birds"

Slide 57

Slide 57 text

What is attr_accessor there?

Slide 58

Slide 58 text

A class method errrrr... A singleton method of the class object

Slide 59

Slide 59 text

class Animal def self.nattr_accessor(*meths) meths.each do |meth| # getter define_method(meth) do instance_variable_get("@#{meth}") end # setter define_method("#{meth}=") do |wut| instance_variable_set("@#{meth}", wut) end end end end

Slide 60

Slide 60 text

class Cat < Animal nattr_accessor :legs, :diet end c = Cat.new c.legs = 4 c.diet = "Small birds" c.legs # => 4 c.diet # => "Small birds"

Slide 61

Slide 61 text

# Making it more reusable class Object def self.nattr_accessor(*meths) meths.each do |meth| # getter define_method(meth) do instance_variable_get("@#{meth}") end # setter define_method("#{meth}=") do |wut| instance_variable_set("@#{meth}", wut) end end end end

Slide 62

Slide 62 text

module Nattr def nattr_accessor(*meths) meths.each do |meth| # getter define_method(meth) do instance_variable_get("@#{meth}") end # setter define_method("#{meth}=") do |wut| instance_variable_set("@#{meth}", wut) end end end end

Slide 63

Slide 63 text

class Animal include Nattr end Won't work (creates instance methods)

Slide 64

Slide 64 text

class Animal extend Nattr end class Cat < Animal nattr_accessor :legs, :diet end c = Cat.new c.legs = 4 c.diet = "Mice" c.legs c.diet

Slide 65

Slide 65 text

Problems with metaprogramming

Slide 66

Slide 66 text

Abstraction and Indirection Code can become harder to » Read » Debug » Search for

Slide 67

Slide 67 text

Powertool Apply with care Use when you need it, not when you can

Slide 68

Slide 68 text

Where now? Only scratched the surface

Slide 69

Slide 69 text

Dave Thomas's screencasts at Prag Prog A bit dated (1.8) but lay a solid foundation Paolo Perrotta's Metaprogramming Ruby

Slide 70

Slide 70 text

Read the source, Luke Any non-trivial Ruby application is bound to have a bunch of metaprogramming sprinkled in.

Slide 71

Slide 71 text

In Ruby Metaprogramming is just programming

Slide 72

Slide 72 text

Thank you

Slide 73

Slide 73 text

?