Slide 1

Slide 1 text

Ruby Metaprogramming Tuesday, July 24, 12

Slide 2

Slide 2 text

Metaprogramming is writing code that writes code. Tuesday, July 24, 12

Slide 3

Slide 3 text

Metaprogramming is writing code that writes code in a galaxy far, far away. Tuesday, July 24, 12

Slide 4

Slide 4 text

Metaprogramming is writing code that writes code in a galaxy far, far away. Tuesday, July 24, 12

Slide 5

Slide 5 text

Tuesday, July 24, 12

Slide 6

Slide 6 text

Tuesday, July 24, 12

Slide 7

Slide 7 text

Building abstractions to simplify Create nifty Domain Specific Languages, such as ActiveRecord associations. Tuesday, July 24, 12

Slide 8

Slide 8 text

For example, has_many is an abstraction around n-to-many relationships and all the associated duct work. Building abstractions to simplify Tuesday, July 24, 12

Slide 9

Slide 9 text

class Feed < ActiveRecord::Base has_many :items belongs_to :user validates_presence_of :user_id before_create do |feed| feed.adopt_user_display_name end end Building abstractions to simplify Tuesday, July 24, 12

Slide 10

Slide 10 text

feed = Feed.last feed.items.create(text: "An item!") Building abstractions to simplify Where did that come from? Tuesday, July 24, 12

Slide 11

Slide 11 text

Ruby Object Model •classes •objects •class objects •instance methods •class methods •singleton methods •singleton classes •virtual classes? Tuesday, July 24, 12

Slide 12

Slide 12 text

Ruby Object Model •classes •objects •class objects •instance methods •class methods •singleton methods •singleton classes •virtual classes? OMG WTF BBQ Tuesday, July 24, 12

Slide 13

Slide 13 text

Ruby Object Model objects object_id: 703 obj Tuesday, July 24, 12

Slide 14

Slide 14 text

Ruby Object Model classes objects superclass class methods: Object + to_s + nil? + respond_to? object_id: 703 obj obj = Object.new() Tuesday, July 24, 12

Slide 15

Slide 15 text

Where am I? Who am I? self Tuesday, July 24, 12

Slide 16

Slide 16 text

current object Where am I? Who am I? self Tuesday, July 24, 12

Slide 17

Slide 17 text

Where am I? Who am I? class Foo self # self is Foo def self.foo #self is Foo return self end def bar # self is a Foo instance return self end end Tuesday, July 24, 12

Slide 18

Slide 18 text

Explicit vs. implicit receiver class Foo def foo "foo" end def bar return self.foo end def baz return bar.upcase end end f = Foo.new f.baz Tuesday, July 24, 12

Slide 19

Slide 19 text

Define instance methods superclass class methods: Person + say_name + say_age class Person attr_attribute :name, :age def initialize(name, age) @name, @age = name, age end def say_name "Hi, my name is #{name}." end def say_age "I'm #{@age} years old." end end Class Definition Tuesday, July 24, 12

Slide 20

Slide 20 text

A class’s class is Class. superclass class methods: Person + say_name + say_age class Person attr_attribute :name, :age def initialize(name, age) @name, @age = name, age end def say_name "Hi, my name is #{name}." end def say_age "I'm #{@age} years old." end end Class Definition superclass class methods: Class + name + ancestors - include Tuesday, July 24, 12

Slide 21

Slide 21 text

A class’s class is Class. superclass class methods: Person + say_name + say_age class Person attr_attribute :name, :age def initialize(name, age) @name, @age = name, age end def say_name "Hi, my name is #{name}." end def say_age "I'm #{@age} years old." end end Class Definition superclass class methods: Class + name + ancestors - include (Trippy.) Tuesday, July 24, 12

Slide 22

Slide 22 text

Also, Class’s class is Class. superclass class methods: Person + say_name + say_age class Person attr_attribute :name, :age def initialize(name, age) @name, @age = name, age end def say_name "Hi, my name is #{name}." end def say_age "I'm #{@age} years old." end end Class Definition superclass class methods: Class + name + ancestors - include Tuesday, July 24, 12

Slide 23

Slide 23 text

Also, Class’s class is Class. superclass class methods: Person + say_name + say_age class Person attr_attribute :name, :age def initialize(name, age) @name, @age = name, age end def say_name "Hi, my name is #{name}." end def say_age "I'm #{@age} years old." end end Class Definition superclass class methods: Class + name + ancestors - include (Ruby is an asshole.) Tuesday, July 24, 12

Slide 24

Slide 24 text

superclass class methods: Person + say_name + say_age jeff = Person.new('Jeff', 31) jeff.say_age #=> "I'm 31 years old." jeff.say_name #=> "Hi, my name is Jeff." Class Definition name: “Jeff” age: 31 jeff Tuesday, July 24, 12

Slide 25

Slide 25 text

superclass class methods: Person + say_name + say_age jeff = Person.new('Jeff', 31) jeff.say_age #=> "I'm 31 years old." jeff.say_name #=> "Hi, my name is Jeff." jeff.to_s #=> "#" Class Definition name: “Jeff” age: 31 jeff Tuesday, July 24, 12

Slide 26

Slide 26 text

superclass class methods: Person + say_name + say_age name: “Jeff” age: 31 jeff Where did that come from? jeff = Person.new('Jeff', 31) jeff.say_age #=> "I'm 31 years old." jeff.say_name #=> "Hi, my name is Jeff." jeff.to_s #=> "#" Class Definition Tuesday, July 24, 12

Slide 27

Slide 27 text

superclass class methods: Person + say_name + say_age name: “Jeff” age: 31 jeff superclass class methods: Object + clone + to_s ... Instance Method Lookup jeff.to_s Tuesday, July 24, 12

Slide 28

Slide 28 text

superclass class methods: Person + say_name + say_age name: “Jeff” age: 31 jeff superclass class methods: Object + clone + to_s ... Instance Method Lookup jeff.to_s Nope! Tuesday, July 24, 12

Slide 29

Slide 29 text

superclass class methods: Person + say_name + say_age name: “Jeff” age: 31 jeff superclass class methods: Object + clone + to_s ... Instance Method Lookup jeff.to_s Yep! Tuesday, July 24, 12

Slide 30

Slide 30 text

superclass class methods: Person + say_name + say_age name: “Jeff” age: 31 jeff superclass class methods: Object + clone + to_s ... Instance Method Lookup jeff.to_s #=> "#" Yep! Tuesday, July 24, 12

Slide 31

Slide 31 text

Override the #to_s method superclass class methods: Instructor + to_s class Instructor < Person def to_s "Instructor #{name}." end end jeff = Instructor.new('Jeff', 31) jeff.to_s #=> "Instructor Jeff" Tuesday, July 24, 12

Slide 32

Slide 32 text

name: “Jeff” age: 31 jeff Instance Method Lookup jeff = Instructor.new('Jeff', 31) jeff.to_s superclass class methods: Person + say_name superclass class methods: Object ... superclass class methods: Instructor + to_s Tuesday, July 24, 12

Slide 33

Slide 33 text

name: “Jeff” age: 31 jeff Instance Method Lookup jeff = Instructor.new('Jeff', 31) jeff.to_s #=> "Instructor Jeff" superclass class methods: Person + say_name superclass class methods: Object ... superclass class methods: Instructor + to_s Yep! Tuesday, July 24, 12

Slide 34

Slide 34 text

name: “Jeff” age: 31 jeff Instance Method Lookup jeff = Instructor.new('Jeff', 31) jeff.say_name superclass class methods: Person + say_name superclass class methods: Object ... superclass class methods: Instructor + to_s Tuesday, July 24, 12

Slide 35

Slide 35 text

name: “Jeff” age: 31 jeff Instance Method Lookup jeff = Instructor.new('Jeff', 31) jeff.say_name superclass class methods: Person + say_name superclass class methods: Object ... superclass class methods: Instructor + to_s Nope! Tuesday, July 24, 12

Slide 36

Slide 36 text

name: “Jeff” age: 31 jeff Instance Method Lookup jeff = Instructor.new('Jeff', 31) jeff.say_name #=> "Hi, my name is Jeff." superclass class methods: Person + say_name superclass class methods: Object ... superclass class methods: Instructor + to_s Yep! Tuesday, July 24, 12

Slide 37

Slide 37 text

Define a module superclass class methods: Rubyist + write_code module Rubyist def write_code "Ruby!" end end class Person include Rubyist end Module Instance Methods Tuesday, July 24, 12

Slide 38

Slide 38 text

A module’s class is Module superclass class methods: Rubyist + write_code module Rubyist def write_code "Ruby!" end end class Person include Rubyist end Module Instance Methods superclass class methods: Module + name + ancestors - include Tuesday, July 24, 12

Slide 39

Slide 39 text

name: “chad” age: 33 chad Module Method Lookup class Person include Rubyist end chad = Person.new chad.write_code superclass class methods: Person + say_name superclass class methods: Object ... Tuesday, July 24, 12

Slide 40

Slide 40 text

name: “chad” age: 33 chad Module Method Lookup class Person include Rubyist end chad = Person.new chad.write_code superclass class methods: Person + say_name superclass class methods: Object ... superclass class methods: Rubyist + write_code Including a module inserts it into the classe’s superclass chain. Tuesday, July 24, 12

Slide 41

Slide 41 text

name: “chad” age: 33 chad Module Method Lookup class Person include Rubyist end chad = Person.new chad.write_code superclass class methods: Person + say_name superclass class methods: Object ... superclass class methods: Rubyist + write_code Nope! Tuesday, July 24, 12

Slide 42

Slide 42 text

name: “chad” age: 33 chad Module Method Lookup class Person include Rubyist end chad = Person.new chad.write_code #=> "Ruby!" superclass class methods: Person + say_name superclass class methods: Object ... superclass class methods: Rubyist + write_code Yep! Tuesday, July 24, 12

Slide 43

Slide 43 text

name: “chad” age: 33 chad Module Method Lookup class Person include Rubyist include Lisper end chad = Person.new chad.write_code superclass class methods: Person + say_name superclass class methods: Object ... superclass class methods: Rubyist + write_code superclass class methods: Lisper + write_code Tuesday, July 24, 12

Slide 44

Slide 44 text

name: “chad” age: 33 chad Module Method Lookup class Person include Rubyist include Lisper end chad = Person.new chad.write_code superclass class methods: Person + say_name superclass class methods: Object ... superclass class methods: Rubyist + write_code Tuesday, July 24, 12

Slide 45

Slide 45 text

name: “chad” age: 33 chad Module Method Lookup class Person include Rubyist include Lisper end chad = Person.new chad.write_code superclass class methods: Person + say_name superclass class methods: Rubyist + write_code superclass class methods: Lisper + write_code Tuesday, July 24, 12

Slide 46

Slide 46 text

name: “chad” age: 33 chad Module Method Lookup class Person include Rubyist include Lisper end chad = Person.new chad.write_code superclass class methods: Person + say_name superclass class methods: Rubyist + write_code superclass class methods: Lisper + write_code Nope! Tuesday, July 24, 12

Slide 47

Slide 47 text

name: “chad” age: 33 chad Module Method Lookup class Person include Rubyist include Lisper end chad = Person.new chad.write_code #=> "Lithp!" superclass class methods: Person + say_name superclass class methods: Rubyist + write_code superclass class methods: Lisper + write_code Yep! Tuesday, July 24, 12

Slide 48

Slide 48 text

name: “chad” age: 33 chad Precedence superclass class methods: Person + write_code superclass class methods: Object ... class Person def write_code "Type, type, type." end end chad = Person.new chad.write_code #=> "Type, type, type." Tuesday, July 24, 12

Slide 49

Slide 49 text

name: “chad” age: 33 chad superclass class methods: Person + write_code superclass class methods: Object ... superclass class methods: Rubyist + write_code class Person def write_code "Type, type, type." end end Person.send(:include, Rubyist) chad = Person.new chad.write_code #=> Which? Precedence Tuesday, July 24, 12

Slide 50

Slide 50 text

name: “chad” age: 33 chad superclass class methods: Person + write_code superclass class methods: Object ... superclass class methods: Rubyist + write_code class Person def write_code "Type, type, type." end end Person.send(:include, Rubyist) chad = Person.new chad.write_code #=> "Type, type, type." Precedence Yep! Tuesday, July 24, 12

Slide 51

Slide 51 text

How can one ensure a class provides a method but allow it to be overridden by an included module? Tuesday, July 24, 12

Slide 52

Slide 52 text

name: “chad” age: 33 chad class Person module Coder def write_code "Type, type, type." end end include Person::Coder end chad = Person.new chad.write_code superclass class methods: Person + say_name superclass class methods: Object ... superclass class methods: P::Coder + write_code Precedence Tuesday, July 24, 12

Slide 53

Slide 53 text

name: “chad” age: 33 chad class Person module Coder def write_code "Type, type, type." end end include Person::Coder end Person.send(:include, Rubyist) chad = Person.new chad.write_code superclass class methods: Person + say_name Precedence superclass class methods: Rubyist + write_code superclass class methods: P::Coder + write_code Tuesday, July 24, 12

Slide 54

Slide 54 text

Override a method on matt matt = Person.new('Matt', 29) def matt.say_name "I'm Batman." end matt.say_age #=> "I’m 29 years old." matt.say_name #=> "I'm Batman." name: “Matt” age: 29 matt Singleton Methods Tuesday, July 24, 12

Slide 55

Slide 55 text

superclass class methods: Person + say_name name: “Matt” age: 29 matt superclass class methods: Object ... Singleton Classes def matt.say_name "I'm Batman." end matt.say_name Tuesday, July 24, 12

Slide 56

Slide 56 text

superclass class methods: Person + say_name name: “Matt” age: 29 matt superclass class methods: Object ... Singleton Classes def matt.say_name "I'm Batman." end matt.say_name superclass class methods: Person’ + say_name Tuesday, July 24, 12

Slide 57

Slide 57 text

superclass class methods: Person + say_name name: “Matt” age: 29 matt superclass class methods: Object ... Singleton Classes def matt.say_name "I'm Batman." end matt.say_name #=> "I'm Batman." superclass class methods: Person’ + say_name Yep! Tuesday, July 24, 12

Slide 58

Slide 58 text

Accessing the singleton class class Foo class << self # self is Class' for Foo end end f = Foo.new class << f # self is Foo' for f # and is equal to f.singleton_class end class << foo Tuesday, July 24, 12

Slide 59

Slide 59 text

Class Methods class Person def self.fess_up "There are no class methods." end end Person.fess_up #=> "There are no class methods." superclass class methods: Person + say_name + say_age Tuesday, July 24, 12

Slide 60

Slide 60 text

superclass class methods: Object ... superclass class methods: Object ... superclass class methods: Class ... superclass class methods: Person + say_name Class Methods class Person def self.fess_up "There are no class methods." end end Person.fess_up #=> "There are no class methods." Tuesday, July 24, 12

Slide 61

Slide 61 text

superclass class methods: Object ... superclass class methods: Object ... superclass class methods: Class ... superclass class methods: Class’ + fess_up superclass class methods: Person + say_name Class Methods class Person def self.fess_up "There are no class methods." end end Person.fess_up #=> "There are no class methods." “Class” methods are methods defined on the class’s singleton class. name: “chad” age: 33 chad Tuesday, July 24, 12

Slide 62

Slide 62 text

name: “chad” age: 33 chad Instance extends Module superclass class methods: Person + say_name superclass class methods: Object ... class Person end module Rubyist def write_code "Ruby!" end end chad = Person.new chad.extend Rubyist chad.write_code Tuesday, July 24, 12

Slide 63

Slide 63 text

name: “chad” age: 33 chad superclass class methods: Person + say_name superclass class methods: Object ... superclass class methods: Rubyist + write_code Extending a module inserts it into your class’s superclass chain. Instance extends Module class Person end module Rubyist def write_code "Ruby!" end end chad = Person.new chad.extend Rubyist chad.write_code Tuesday, July 24, 12

Slide 64

Slide 64 text

superclass class methods: Object ... superclass class methods: Object ... superclass class methods: Class ... superclass class methods: Person + say_name Class extends Module module Rubyist def write_code "Ruby!" end end class Person extend Rubyist end Person.write_code Tuesday, July 24, 12

Slide 65

Slide 65 text

superclass class methods: Object ... superclass class methods: Object ... superclass class methods: Class ... superclass class methods: Person + say_name Class extends Module module Rubyist def write_code "Ruby!" end end class Person extend Rubyist end Person.write_code superclass class methods: Class’ ... Tuesday, July 24, 12

Slide 66

Slide 66 text

superclass class methods: Object ... superclass class methods: Class ... superclass class methods: Person + say_name Class extends Module module Rubyist def write_code "Ruby!" end end class Person extend Rubyist end Person.write_code superclass class methods: Class’ ... superclass class methods: Rubyist + write_code Tuesday, July 24, 12

Slide 67

Slide 67 text

To extend a module is equivalent to an include in your singleton class. Tuesday, July 24, 12

Slide 68

Slide 68 text

class Shape attr_reader :dimensions def initialize(dimensions={}) @dimensions = dimensions end [:width, :height, :length].each do |dim| # Fetch value from the dimensions hash define_method(dim) do return dimensions[dim] end end end define_method Tuesday, July 24, 12

Slide 69

Slide 69 text

class Instructor < Person def self.start_pomodoro puts "Ready, set, go" end def self.end_pomodoro puts "RINNGGGGG" end def give_lecture "Pomodoro, pomodoro, pomodoro." end end instance_eval and class_eval superclass class methods: Object ... superclass class methods: Object ... superclass class methods: Class ... superclass class methods: Class’ + start_pomo + end_pomo superclass class methods: Instructor + to_s Tuesday, July 24, 12

Slide 70

Slide 70 text

class Instructor < Person def self.start_pomodoro puts "Ready, set, go" end def self.end_pomodoro puts "RINNGGGGG" end def give_lecture "Pomodoro, pomodoro, pomodoro." end end Instructor.class_eval do def give_advice "Bonnie will take care of it!" end end superclass class methods: Object ... superclass class methods: Object ... superclass class methods: Class ... superclass class methods: Class’ + start_pomo + end_pomo instance_eval and class_eval superclass class methods: Instructor + give_lecture + give_advice Tuesday, July 24, 12

Slide 71

Slide 71 text

class Instructor < Person def self.start_pomodoro puts "Ready, set, go" end def self.end_pomodoro puts "RINNGGGGG" end def give_lecture "Pomodoro, pomodoro, pomodoro." end end Instructor.instance_eval do def restart_pomodoro end_pomodoro start_pomodoro end end superclass class methods: Object ... superclass class methods: Instructor + to_s superclass class methods: Object ... superclass class methods: Class ... superclass class methods: Class’ + start_pomo + end_pomo + restart_pomo instance_eval and class_eval Tuesday, July 24, 12

Slide 72

Slide 72 text

String interpolated class Shape attr_reader :dimensions def initialize(dimensions={}) @dimensions = dimensions end [:width, :height, :length].each do |dim| class_eval <<-MET def #{dim}=(val) @#{dim} = val end MET end end Tuesday, July 24, 12

Slide 73

Slide 73 text

class Instructor < Person def self.start_pomodoro puts "Ready, set, go" end def self.end_pomodoro puts "RINNGGGGG" end def give_lecture "Pomodoro, pomodoro, pomodoro." end end Instructor.class_eval <<-MET, __FILE__, __LINE__ def advise_student "Bonnie will take care of it!" end MET superclass class methods: Object ... superclass class methods: Object ... superclass class methods: Class ... superclass class methods: Class’ + start_pomo + end_pomo instance_eval and class_eval superclass class methods: Instructor + give_lecture + give_advice Tuesday, July 24, 12

Slide 74

Slide 74 text

instance_exec Tuesday, July 24, 12

Slide 75

Slide 75 text

Class “macro” methods module AccessibleAttributes def accessible_attribute(name) define_method(name) do instance_variable_get("@#{name}") end define_method("#{name}=") do |value| instance_variable_set("@#{name}", value) end end end Creating a module that generates methods allows it to be extended into an existing class to provide macro abilities. class Feed extend AccessibleAttributes end Tuesday, July 24, 12

Slide 76

Slide 76 text

Testing “macro” methods describe AccessibleAttributes do let(:klass) { Class.new } before(:each) do klass.extend AccessibleAttributes klass.accessible_attribute :foo end describe ".accessible_attribute" do it "allows writing and reading" do k = klass.new.tap{|k| k.foo = "bar" } k.foo.should == "bar" end end end Extending into a new class for each spec prevents specs from interacting with one another. Tuesday, July 24, 12

Slide 77

Slide 77 text

Rewrite this class to use a single call to define_method instead of the normal defs. Rewrite it again using a form of eval and a string with interpolation. For either version, add the corresponding reader methods. class Player def name=(name) @name = name end def score=(score) @score = score end def paddle=(paddle) @paddle = paddle end end Task: Define methods dynamically Tuesday, July 24, 12

Slide 78

Slide 78 text

Hooks Tuesday, July 24, 12

Slide 79

Slide 79 text

Hash.class_eval do def method_missing(name, *args, &block) if has_key?(name) fetch(name) else super end end end method_missing Tuesday, July 24, 12

Slide 80

Slide 80 text

Hash.class_eval do def method_missing(name, *args, &block) if has_key?(name) fetch(name) else super end end def respond_to?(name) has_key?(name) || super end end respond_to? Tuesday, July 24, 12

Slide 81

Slide 81 text

class Instructor < Person def give_lecture "Pomodoro, pomodoro, pomodoro." end end jeff = Instructor.new jeff.hand_out_contracts Method Missing name: “Jeff” age: 31 jeff superclass class methods: Person + say_name superclass class methods: Object ... superclass class methods: Instructor + to_s Tuesday, July 24, 12

Slide 82

Slide 82 text

class Instructor < Person def give_lecture "Pomodoro, pomodoro, pomodoro." end end jeff = Instructor.new jeff.hand_out_contracts Method Missing name: “Jeff” age: 31 jeff superclass class methods: Person + say_name superclass class methods: Object ... superclass class methods: Instructor + to_s Nope! #hand_out_contracts Tuesday, July 24, 12

Slide 83

Slide 83 text

class Instructor < Person def give_lecture "Pomodoro, pomodoro, pomodoro." end end jeff = Instructor.new jeff.hand_out_contracts Method Missing name: “Jeff” age: 31 jeff superclass class methods: Person + say_name superclass class methods: Object ... superclass class methods: Instructor + to_s Nope! #hand_out_contracts Tuesday, July 24, 12

Slide 84

Slide 84 text

class Instructor < Person def give_lecture "Pomodoro, pomodoro, pomodoro." end end jeff = Instructor.new jeff.hand_out_contracts Method Missing name: “Jeff” age: 31 jeff superclass class methods: Person + say_name superclass class methods: Object ... superclass class methods: Instructor + to_s Nope! #hand_out_contracts Tuesday, July 24, 12

Slide 85

Slide 85 text

class Instructor < Person def give_lecture "Pomodoro, pomodoro, pomodoro." end end jeff = Instructor.new jeff.hand_out_contracts Method Missing name: “Jeff” age: 31 jeff superclass class methods: Person + say_name superclass class methods: Object ... superclass class methods: Instructor + to_s Nope! #method_missing Tuesday, July 24, 12

Slide 86

Slide 86 text

class Instructor < Person def give_lecture "Pomodoro, pomodoro, pomodoro." end end jeff = Instructor.new jeff.hand_out_contracts Method Missing name: “Jeff” age: 31 jeff superclass class methods: Person + say_name superclass class methods: Object ... superclass class methods: Instructor + to_s Nope! #method_missing Tuesday, July 24, 12

Slide 87

Slide 87 text

class Instructor < Person def give_lecture "Pomodoro, pomodoro, pomodoro." end end jeff = Instructor.new jeff.hand_out_contracts Method Missing name: “Jeff” age: 31 jeff superclass class methods: Person + say_name superclass class methods: Object ... superclass class methods: Instructor + to_s #method_missing Yep! Tuesday, July 24, 12

Slide 88

Slide 88 text

class Instructor < Person def give_lecture "Pomodoro, pomodoro, pomodoro." end end jeff = Instructor.new jeff.hand_out_contracts #NoMethodError: undefined method `hand_out_contracts' for Instructor Jeff Method Missing name: “Jeff” age: 31 jeff superclass class methods: Person + say_name superclass class methods: Object ... superclass class methods: Instructor + to_s #method_missing Yep! Tuesday, July 24, 12

Slide 89

Slide 89 text

included is invoked by the Ruby runtime whenever a module is included into another class. It can be used to implement a pattern that gives the appearance that inclusion also imports “class” methods. module AccessibleAttributes def self.included(base) base.extend(ClassMethods) end module ClassMethods def accessible_attribute(name) define_method(name) do instance_variable_get("@#{name}") end define_method("#{name}=") do |value| instance_variable_set("@#{name}", value) end end end end Hooks Tuesday, July 24, 12

Slide 90

Slide 90 text

included is invoked by the Ruby runtime whenever a module is included into another class. It can be used to implement a pattern that gives the appearance that inclusion also imports “class” methods. module AccessibleAttributes def self.included(base) base.extend(ClassMethods) end module ClassMethods def accessible_attribute(name) define_method(name) do instance_variable_get("@#{name}") end define_method("#{name}=") do |value| instance_variable_set("@#{name}", value) end end end end Hooks Other hooks methods include: • extended • method_defined • method_undefined • inherited Tuesday, July 24, 12

Slide 91

Slide 91 text

Define a module Settings that provides a macro method called .setting that takes a name, “foo” and creates #foo_on, #foo_off, and also #foo_on?, #foo_off? instance methods. Next, let .setting take a list of setting names. class Configurator include Settings setting :power end c = Configurator.new c.power_on? #=> false c.power_off? #=> true c.power_on c.power_off? #=> false Task: Define a macro method Tuesday, July 24, 12

Slide 92

Slide 92 text

Define a let macro that takes a name and a block. It creates an instance method of that name, the body of which is the block, but that block should be dynamically evaluated in the context of the instance. Super duper bonus: define a let! that memoizes the result of the block after the first call. Bonus: Macro with instance scope class Computor let(:quotient) { dividend / divisor } def dividend; 1; end def divisor; 0; end end c = Computor.new c.quotient # ZeroDivisionError: divided by 0 Tuesday, July 24, 12

Slide 93

Slide 93 text

• class Foo < class_expr() • closure currying • caller • binding • yield (self) • set_trace_function • defined? / const_defined? Misc. Tuesday, July 24, 12

Slide 94

Slide 94 text

fin Tuesday, July 24, 12