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ɹ