Slide 1

Slide 1 text

No content

Slide 2

Slide 2 text

Francesco Rodríguez @frodsan

Slide 3

Slide 3 text

Metaprogramming: From Zero to Hero

Slide 4

Slide 4 text

Use case

Slide 5

Slide 5 text

on post, param("gem") do |params| if RubyGems.exists?(params["name"]) # add gem to the list. else # show error message. end end

Slide 6

Slide 6 text

require "net/http" ! module RubyGems # ... ! def self.exists?(name) uri = URI(sprintf(URL, name)) res = Net::HTTP.get_response(uri) ! res.code == "200" end end

Slide 7

Slide 7 text

test "creates if gem exists" do params = { "name" => "ricojson", "status" => "ok" } ! post("/gems", gem: params) ! assert_response(201) end

Slide 8

Slide 8 text

module RubyGems def self.exists?(name) true end end ! test "creates if gem exists" do # ... end

Slide 9

Slide 9 text

$ make test .

Slide 10

Slide 10 text

test "errors if gem not exists" do post("/gems", gem: { name: "ruby" }) ! assert_response(422) end

Slide 11

Slide 11 text

$ make test .F

Slide 12

Slide 12 text

No content

Slide 13

Slide 13 text

ఠ_ఠ ! • Stub. • Mock. • Spy.

Slide 14

Slide 14 text

github.com/freerange/mocha >3000 LOC => ಥ_ಥ


Slide 15

Slide 15 text

ʕ•ᴥ•ʔ ! • Minimalist. • Travel back in time. • No integration code. • No monkey patching.

Slide 16

Slide 16 text

def stub(object, method, result) object.send(:define_method, method) do result end end

Slide 17

Slide 17 text

test "creates if gem exists" do stub(RubyGems, :exists?, true) # ... end ! test "errors if gem not exists" do stub(RubyGems, :exists?, false) # ... end

Slide 18

Slide 18 text

$ make test FF

Slide 19

Slide 19 text

“Everything is an object”

Slide 20

Slide 20 text

“Classes are also objects”

Slide 21

Slide 21 text

>> Array.object_id # => 70214494875960 >> [].object_id # => 70214503361980

Slide 22

Slide 22 text

struct RObject { struct RBasic basic; /* ... */ }; ! struct RClass { struct RBasic basic; /* ... */ struct st_table *m_tbl; VALUE super; };

Slide 23

Slide 23 text

def stub(object, method, result) object.send(:define_method, method) do result end end

Slide 24

Slide 24 text

Method Lookup Path

Slide 25

Slide 25 text

foo = Foo.new foo.foo() foo Foo Object foo() foo()

Slide 26

Slide 26 text

⊙﹏⊙ ! • Eigenclass. • Singleton class. • Object-specific class. • Virtual class. • Ghost class. • Metaclass. • Anonymous class.

Slide 27

Slide 27 text

object = Object.new # => # ! ! object.singleton_class # => #> ! object.singleton_class.class # => Class

Slide 28

Slide 28 text

bar = Foo.new ! def bar.foo "bar" end ! # or ! class << bar def foo "bar" end end

Slide 29

Slide 29 text

bar.foo # => "bar" ! foo.foo # => "foo"

Slide 30

Slide 30 text

bar Singleton Class Foo foo()

Slide 31

Slide 31 text

$ make test FF

Slide 32

Slide 32 text

def stub(object, method, result) metaclass = object.singleton_class ! metaclass.send(:define_method, method) do |*args| result end end

Slide 33

Slide 33 text

$ make test ..

Slide 34

Slide 34 text

test "errors if gem not exists" do stub(RubyGems, :exists?, false) # ... end ! RubyGems.exists?("false") # => false

Slide 35

Slide 35 text

def stub(object, method, result) metaclass = object.singleton_class original = object.method(method) ! metaclass.send(:define_method, method) do |*args| result end ! yield ensure metaclass.send(:undef_method, method) metaclass.send(:define_method, method, original) end

Slide 36

Slide 36 text

$ make test ..

Slide 37

Slide 37 text

test "errors if gem not exists" do stub(RubyGems, :exists?, false) # ... end ! RubyGems.exists?("really-exists") # => ...

Slide 38

Slide 38 text

github.com/frodsan/mocoso (‛ƅ㱼ƅ)‛ 22 LOC / 100 comment lines.


Slide 39

Slide 39 text

Gracias! <3