Slide 1

Slide 1 text

Rails is A Follower what we can learn from dRuby’s metaprogramming magic Masatoshi SEKI Makoto Inoue

Slide 2

Slide 2 text

Makoto Inoue Web Developer @ New Bamboo (UK) Translator of “The dRuby Book”

Slide 3

Slide 3 text

Masatoshi Seki Ruby committer Author of dRuby/Rinda Author of ERB Pokemon Master The ERB Book Embed ruby script in any document using ERB

Slide 4

Slide 4 text

Seki san @ Work Embedded RealTime Network GUI

Slide 5

Slide 5 text

Seki san @ RubyKaigi (06 - 11) 2006 dRuby as a session storage 2007 Rinda: Distributed TupleSpace 2008 RIP ERB 2009 dRuby and BigTable 2010 dRuby and Object Database 2011 Drip: A stream based database

Slide 6

Slide 6 text

History of dRuby 1999-3 ERB 1999-9 DRb 2003 committer 2010 pokemon-master 2000 Rinda 1998 started ruby 2006 - 2011 Ruby Kaigi

Slide 7

Slide 7 text

Thank you to Our Sponsors Ruby No Kai new bamboo

Slide 8

Slide 8 text

Thank you to Our Sponsors @PUPRL @ayumin @arika @mame @ vestige_ @track8 @ nagachika "ruby trunk changes" @ktou Powered by Rabbit @tsuboi @ayako119 @hisashim

Slide 9

Slide 9 text

Table Of Contents What is dRuby ? Before dRuby (BD) Designing dRuby Q&A

Slide 10

Slide 10 text

What is dRuby? Distributed Object System Can invoke methods in different processes Can send objects between processes Pure Ruby Ruby standard library

Slide 11

Slide 11 text

Demo1 Starting up

Slide 12

Slide 12 text

Demo2 Which is the server?

Slide 13

Slide 13 text

Demo3 Distributed Queue

Slide 14

Slide 14 text

dRuby usage: distributed RSpec Used at Cookpad Ask @mrkn for the detail

Slide 15

Slide 15 text

dRuby usage: prototyping

Slide 16

Slide 16 text

Before dRuby (BD)

Slide 17

Slide 17 text

Old-fashioned programmer (BD8) Used to program in old Unix style IPC (Inter-Process Communication) fork/pipe, SystemV IPC

Slide 18

Slide 18 text

Simple world of CGI (BD5) request/response, stateless Converted my IPC programs to CGI

Slide 19

Slide 19 text

Ruby & shttpsrv (BD1) 1998-9 Met Ruby & shttpsrv

Slide 20

Slide 20 text

Ruby & shttpsrv Web server written in Ruby Before WEBrick Small and extensible

Slide 21

Slide 21 text

Embedding HTTP into processes Embedding web server into small apps are fun Application communication via HTTP

Slide 22

Slide 22 text

Ruby everywhere I used to play with various languages with CGI, but now everything is Ruby

Slide 23

Slide 23 text

Not so cool because... Converting Ruby way into Web way HTTP restricts how to exchange objects Server - Client relationship Functional APIʢRPCʣ Cool URL No REST at that time

Slide 24

Slide 24 text

I just want Ruby to talk each other Converting Ruby world to Web world is restricting Too much abstraction?

Slide 25

Slide 25 text

Back to Ruby They are Ruby objects. Let’s call them in Ruby-ish way

Slide 26

Slide 26 text

Back to Ruby Distributed objects acting like Ruby RPC -> RMI Extending Ruby’s method invocation to socket programming

Slide 27

Slide 27 text

Why metaprogramming? Need a perfect proxy acting like Ruby Perfect fake that acts like normal Ruby

Slide 28

Slide 28 text

Designing dRuby method_missing Marshal ObjectSpace._id2ref & send Other Techniques

Slide 29

Slide 29 text

method_missing Respond to unknown methods Used for Proxies to catch method calls

Slide 30

Slide 30 text

class DRbObject ... def method_missing(msg_id, *a) succ, result = DRbConn.new(@uri).send_message(self, msg_id, *a) raise result if ! succ result end ... end

Slide 31

Slide 31 text

Marshal Serializes objects

Slide 32

Slide 32 text

module DRbProtocol ... def dump(obj, soc) begin str = Marshal::dump(obj) rescue ro = DRbObject.new(obj) str = Marshal::dump(ro) end soc.write(str) if soc str end ... end

Slide 33

Slide 33 text

Marshal File, Thread, Proc raise exceptions If you can not serialize, then serialize its proxy Send Object references

Slide 34

Slide 34 text

module DRbProtocol ... def dump(obj, soc) begin str = Marshal::dump(obj) rescue ro = DRbObject.new(obj) str = Marshal::dump(ro) end soc.write(str) if soc str end ... end

Slide 35

Slide 35 text

ObjectSpace._id2ref & send Find a object using object id The server finds the object and dispatches the method to it

Slide 36

Slide 36 text

class DRbServer ... ! ro, msg, argv = recv_request(s) ! if ro and ro.ref ! obj = ObjectSpace._id2ref(ro.ref) ! else ! obj = DRb.front ! end ! result = obj.__send__(msg.intern, *argv) ! succ = true ... end

Slide 37

Slide 37 text

class DRbServer ... ! ro, msg, argv = recv_request(s) ! if ro and ro.ref ! obj = ObjectSpace._id2ref(ro.ref) ! else ! obj = DRb.front ! end ! result = obj.__send__(msg.intern, *argv) ! succ = true ... end

Slide 38

Slide 38 text

Other techniques How do you recover unknown objects? Multithreading Handling method call with block

Slide 39

Slide 39 text

Demo4 DRbUnknown

Slide 40

Slide 40 text

What has just happened? Who did replace Foo with DRbUnknown?

Slide 41

Slide 41 text

class DRbMessage ... def load(soc) ... begin ... Marshal.load(str) rescue NameError, ArgumentError DRbUnknown.new($!, str) ensure ... end ... end end

Slide 42

Slide 42 text

class DRbUnknown def initialize(err, buf) case err.to_s when /uninitialized constant (\S+)/ @name = $1 when /undefined class\/module (\S+)/ @name = $1 else @name = nil end @buf = buf end

Slide 43

Slide 43 text

class DRbUnknown ... def _dump(lv) @buf end ... end

Slide 44

Slide 44 text

What has just happened? How does DRbUnkown#reload work?

Slide 45

Slide 45 text

class DRbUnknown ... def self._load(s) begin Marshal::load(s) rescue NameError, ArgumentError DRbUnknown.new($!, s) end end def reload self.class._load(@buf) end end

Slide 46

Slide 46 text

Multithreading Ruby socket programming patterns from shttpsrv

Slide 47

Slide 47 text

Thread.start(@soc.accept) do |s| begin ro, msg, arg = recv_request(s) ... send_response(s, succ, result) ensure s.close end end

Slide 48

Slide 48 text

Handling method call with block kvs.each do |k,v| p v end

Slide 49

Slide 49 text

ro, msg, argv, block = recv_request(s) obj = ro_to_obj(ro) if block result = obj.__send__(msg.intern, *argv) do |*x| block.call(*x) end else result = obj.__send__(msg.intern, *argv) end

Slide 50

Slide 50 text

break? kvs.each do |k,v| break if k == "foo" p v end LocalJumpError: break from proc-closure ! from (druby://eleven.local:61018) ..... ! from (druby://eleven.local:61018) ..... ! from (druby://eleven.local:61018) .....

Slide 51

Slide 51 text

def perform_with_block @obj.__send__(@msg_id, *@argv) do |*x| jump_error = nil begin block_value = block_yield(x) rescue LocalJumpError jump_error = $! end if jump_error case jump_error.reason when :break break(jump_error.exit_value) else raise jump_error end end block_value end endɹ

Slide 52

Slide 52 text

Did you get it?

Slide 53

Slide 53 text

Summary IPC -> http RPC -> dRuby dRuby metaprogramming techniques It’s your turn now.

Slide 54

Slide 54 text

My thoughts on Metaprogramming Isn’t metaprogramming too magical? Magic should not look like magic. Your magic is complete if no one notices it

Slide 55

Slide 55 text

You have everything you need Now it’s your turn