dnode 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ Copyright 2010 James Halliday (mail@substack.net)
2
+
3
+ This project is free software released under the MIT/X11 license:
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,11 @@
1
+ require 'rubygems'
2
+ require 'dnode'
3
+
4
+ DNode.new({:name => 'Bertrand' }).connect(5050) do |remote|
5
+ remote.moo(proc do |cow|
6
+ puts cow.msg
7
+ cow.ooo(10) { |moo| puts moo }
8
+ end)
9
+
10
+ remote.range(5) { |n| puts "range: #{n}" }
11
+ end
@@ -0,0 +1,21 @@
1
+ require 'rubygems'
2
+ require 'dnode'
3
+
4
+ class Moo
5
+ def initialize name
6
+ @name = name
7
+ end
8
+
9
+ def moo; yield({
10
+ :msg => "Moo! Greetings #{name}.",
11
+ :ooo => proc { |n,&b| b.call('M' + 'o' * n + '!') },
12
+ }); end
13
+
14
+ def range n
15
+ 0.upto(n - 1).each { |x| yield x }
16
+ end
17
+ end
18
+
19
+ DNode.new(proc { |client|
20
+ Moo.new(client.name)
21
+ }).listen(5050)
@@ -0,0 +1,6 @@
1
+ require 'rubygems'
2
+ require 'dnode'
3
+
4
+ DNode.new({}).connect(5050) do |remote|
5
+ remote.f(30000) { |x| puts "x=<#{x}>" }
6
+ end
@@ -0,0 +1,6 @@
1
+ require 'rubygems'
2
+ require 'dnode'
3
+
4
+ DNode.new({
5
+ :f => proc { |x| yield x + 1337 }
6
+ }).listen(5050)
@@ -0,0 +1,50 @@
1
+ require 'eventmachine'
2
+ require 'dnode/conn'
3
+ require 'json'
4
+ require 'socket'
5
+
6
+ class DNode
7
+ def initialize obj={}
8
+ @instance = obj
9
+ end
10
+
11
+ def from_args *args, &block
12
+ types = args.inject({}) { |acc,x| acc.merge(x.class.to_s => x) }
13
+ kw = types['Hash'] || {}
14
+ {
15
+ :host => kw['host'] || kw[:host] || types['String'] || 'localhost',
16
+ :port => kw['port'] || kw[:port] || types['Fixnum'],
17
+ :block => block || kw['block'] || kw[:block] || types['Proc'],
18
+ }
19
+ end
20
+ private :from_args
21
+
22
+ def handle_conn c, conn
23
+ c.extend EM::P::LineText2
24
+ (class << c; self; end).send(:define_method, 'receive_line') do |line|
25
+ conn.handle(JSON(line))
26
+ end
27
+ end
28
+ private :handle_conn
29
+
30
+ def connect *args, &block
31
+ params = from_args(*args, &block).merge(:instance => @instance)
32
+ EM.run do EM.connect(params[:host], params[:port]) do |c|
33
+ conn = Conn.new(params.merge :conn => c)
34
+ handle_conn(c, conn)
35
+ end end
36
+ end
37
+
38
+ class Listener < EM::Connection
39
+ end
40
+
41
+ def listen *args, &block
42
+ params = from_args(*args, &block).merge(:instance => @instance)
43
+ EM.run do
44
+ EM.start_server(params[:host], params[:port], Listener) do |c|
45
+ conn = Conn.new(params.merge :conn => c)
46
+ handle_conn(c, conn)
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,57 @@
1
+ require 'eventmachine'
2
+ require 'events'
3
+ require 'json'
4
+ require 'dnode/scrub'
5
+ require 'dnode/jsobject'
6
+
7
+ class Conn
8
+ include Events::Emitter
9
+
10
+ def initialize params
11
+ @block = params[:block] || lambda {}
12
+ @instance = params[:instance] || {}
13
+ @conn = params[:conn]
14
+ @scrub = Scrub.new
15
+ @remote = {}
16
+
17
+ request('methods',
18
+ if @instance.is_a? Proc
19
+ then @instance.call(*[@remote,self][0..@instance.arity-1])
20
+ else @instance
21
+ end
22
+ )
23
+ end
24
+
25
+ def handle req
26
+ args = @scrub.unscrub(req) do |id|
27
+ lambda { |*argv| self.request(id, *argv) }
28
+ end
29
+
30
+ if req['method'].is_a? Integer then
31
+ id = req['method']
32
+ @scrub.callbacks[id].call(*JSObject.create(args))
33
+ elsif req['method'] == 'methods' then
34
+ @remote.update(args[0])
35
+ js = JSObject.create(@remote)
36
+
37
+ @block.call(*[ js, self ][ 0 .. @block.arity - 1 ])
38
+ self.emit('remote', js)
39
+ self.emit('ready')
40
+ end
41
+ end
42
+
43
+ def request method, *args
44
+ scrubbed = @scrub.scrub(args)
45
+ @conn.send_data(JSON(
46
+ {
47
+ :method => (
48
+ if method.respond_to? :match and method.match(/^\d+$/)
49
+ then method.to_i
50
+ else method
51
+ end
52
+ ),
53
+ :links => [],
54
+ }.merge(scrubbed)
55
+ ) + "\n")
56
+ end
57
+ end
@@ -0,0 +1,38 @@
1
+ require 'dnode/walk'
2
+
3
+ class JSObject
4
+ class JSHash < Hash
5
+ def [] key; super key.to_s; end
6
+ def []= key, value; super key.to_s, value; end
7
+
8
+ @@names = []
9
+ def self.create hash
10
+ @@names.each { |name| remove_method(name) }
11
+ @@names = hash.keys
12
+
13
+ hash.each do |name,value|
14
+ define_method(name) do |*args,&block|
15
+ if value.is_a? Proc then
16
+ if block.nil? then
17
+ value.call(*args)
18
+ else
19
+ value.call(*args) { |*iargs| block.call(*iargs) }
20
+ end
21
+ else
22
+ value
23
+ end
24
+ end
25
+ end
26
+
27
+ self.new.update(hash)
28
+ end
29
+ end
30
+
31
+ def self.create obj={}
32
+ Walk.new(obj).walk do |node|
33
+ if node.value.is_a? Hash and not node.value.is_a? JSHash then
34
+ node.value = JSHash.create(node.value)
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,35 @@
1
+ require 'dnode/walk'
2
+
3
+ class Scrub
4
+ def initialize
5
+ @callbacks = {}
6
+ @last_id = 0
7
+ end
8
+
9
+ attr_accessor :callbacks
10
+
11
+ def scrub args
12
+ callbacks = {}
13
+ walked = Walk.new(args).walk do |node|
14
+ if node.value.is_a? Proc then
15
+ id = @last_id
16
+ @last_id += 1
17
+ @callbacks[id] = node.value
18
+ callbacks[id] = node.path.clone
19
+ node.value = '[Function]'
20
+ end
21
+ end
22
+ { :arguments => walked, :callbacks => callbacks }
23
+ end
24
+
25
+ def unscrub req, &block
26
+ args = Walk.new(req['arguments']).walk do |node|
27
+ path = node.path.map(&:to_s)
28
+ pair = req['callbacks'].detect{ |_,p| p.map(&:to_s) == path }
29
+ unless pair.nil? then
30
+ id = pair.first
31
+ node.value = block.call(id)
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,63 @@
1
+ class Walk
2
+ def initialize obj
3
+ @path = []
4
+ @object = obj
5
+ end
6
+
7
+ def clone obj
8
+ _walk(obj, proc do |node|
9
+ unless @object.is_a? Array or @object.is_a? Hash then
10
+ node.value = node.value.clone
11
+ end
12
+ end)
13
+ end
14
+
15
+ def walk &block
16
+ _walk(clone(@object), block)
17
+ end
18
+
19
+ def _walk obj, cb
20
+ node = Node.new(:value => obj, :path => @path)
21
+ cb.call(node)
22
+ value = node.value
23
+
24
+ if value.is_a? Hash then
25
+ copy = value.class.new
26
+ value.each do |key,v|
27
+ @path.push key
28
+ copy[key] = _walk(v, cb)
29
+ @path.pop
30
+ end
31
+ return copy
32
+ elsif value.is_a? Array then
33
+ copy = value.class.new
34
+ value.each_with_index do |v,i|
35
+ @path.push i
36
+ copy.push(_walk(v, cb))
37
+ @path.pop
38
+ end
39
+ return copy
40
+ elsif [ Numeric, String, Proc ].select{ |x| value.is_a? x }.any?
41
+ return value
42
+ else
43
+ # only serve up the object's "own" methods
44
+ return value.methods.select { |name|
45
+ value.method(name).owner == value.class
46
+ }.inject({}) { |acc,name|
47
+ acc.merge(name => value.method(name).to_proc)
48
+ }
49
+ end
50
+ end
51
+ private :_walk
52
+ end
53
+
54
+ class Node
55
+ def initialize params
56
+ @value = params[:value]
57
+ @path = params[:path]
58
+ end
59
+
60
+ attr_accessor :value
61
+ alias :update :value=
62
+ attr_reader :path
63
+ end
metadata ADDED
@@ -0,0 +1,76 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dnode
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - James Halliday
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-11-12 00:00:00 +00:00
19
+ default_executable:
20
+ dependencies: []
21
+
22
+ description: "\n With DNode you can tie together servers written in ruby, node.js, and\n perl.\n \n DNode is similar to DRb, but is asynchronous and transforms callbacks\n embedded in deeply nested structures.\n "
23
+ email: mail@substack.net
24
+ executables: []
25
+
26
+ extensions: []
27
+
28
+ extra_rdoc_files:
29
+ - LICENSE
30
+ files:
31
+ - LICENSE
32
+ - lib/dnode.rb
33
+ - lib/dnode/conn.rb
34
+ - lib/dnode/scrub.rb
35
+ - lib/dnode/walk.rb
36
+ - lib/dnode/jsobject.rb
37
+ - examples/simple/client.rb
38
+ - examples/simple/server.rb
39
+ - examples/moo/client.rb
40
+ - examples/moo/server.rb
41
+ has_rdoc: true
42
+ homepage: http://github.com/substack/dnode-ruby
43
+ licenses: []
44
+
45
+ post_install_message:
46
+ rdoc_options: []
47
+
48
+ require_paths:
49
+ - lib
50
+ required_ruby_version: !ruby/object:Gem::Requirement
51
+ none: false
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ hash: 3
56
+ segments:
57
+ - 0
58
+ version: "0"
59
+ required_rubygems_version: !ruby/object:Gem::Requirement
60
+ none: false
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ hash: 3
65
+ segments:
66
+ - 0
67
+ version: "0"
68
+ requirements: []
69
+
70
+ rubyforge_project:
71
+ rubygems_version: 1.3.7
72
+ signing_key:
73
+ specification_version: 3
74
+ summary: Asynchronous remote method calls with transparently wrapped callbacks
75
+ test_files: []
76
+