fraggle-spanx 4.0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,33 @@
1
+ require 'rubygems'
2
+ require 'fraggle'
3
+
4
+ EM.run do
5
+ c = Fraggle.connect do |c, err|
6
+ if err
7
+ fail err.message
8
+ end
9
+
10
+ c.rev do |v|
11
+ # Valid
12
+ req = c.walk(v, "/ctl/node/**") do |ents, err|
13
+ if err
14
+ p [:err, err]
15
+ else
16
+ ents.each do |e|
17
+ puts File.join(req.path, e.path) + "=" + e.value
18
+ end
19
+ end
20
+ end
21
+
22
+ # Limit 0 return nothing
23
+ c.walk(v, "/ctl/node/**", 0, 0) do |ents, err|
24
+ p [:nothing, ents, err]
25
+ end
26
+
27
+ # Error
28
+ c.walk(v, "/nothere") do |ents, err|
29
+ p [:nothere, ents, err]
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,16 @@
1
+ require 'rubygems'
2
+ require 'fraggle'
3
+
4
+ EM.run do
5
+ c = Fraggle.connect do |c, err|
6
+ if err
7
+ fail err.message
8
+ end
9
+
10
+ c.rev do |v|
11
+ c.watch(v, "/ctl/node/**") do |e, err|
12
+ p [e, err]
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,28 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "fraggle/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "fraggle-spanx"
7
+ s.version = Fraggle::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Blake Mizerany"]
10
+ s.email = ["blake.mizerany@gmail.com"]
11
+ s.homepage = ""
12
+ s.summary = %q{An EventMachine Client for Doozer}
13
+ s.description = s.summary
14
+
15
+ s.rubyforge_project = "fraggle"
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
+ s.require_paths = ["lib"]
21
+
22
+ s.add_dependency "beefcake-spanx"
23
+ s.add_dependency "eventmachine"
24
+
25
+ s.add_development_dependency "turn"
26
+ s.add_development_dependency "statsample"
27
+ s.add_development_dependency "rake"
28
+ end
@@ -0,0 +1,59 @@
1
+ require 'fraggle/client'
2
+
3
+ module Fraggle
4
+ include Response::Err
5
+
6
+ Clobber = Client::MaxInt64
7
+
8
+ DEFAULT_URI = "doozer:?" + [
9
+ "ca=127.0.0.1:8046",
10
+ "ca=127.0.0.1:8041",
11
+ "ca=127.0.0.1:8042",
12
+ "ca=127.0.0.1:8043"
13
+ ].join("&")
14
+
15
+ def self.connect(uri=nil, &blk)
16
+ uri = uri || ENV["DOOZER_URI"] || DEFAULT_URI
17
+
18
+ addrs, sk = uri(uri)
19
+
20
+ if addrs.length == 0
21
+ raise ArgumentError, "there were no addrs supplied in the uri (#{uri.inspect})"
22
+ end
23
+
24
+ addr = addrs.shift
25
+ host, port = addr.split(":")
26
+
27
+ cn = EM.connect(host, port, Connection, addr)
28
+ c = Client.new(cn, addrs)
29
+ c.access(sk) do |_, err|
30
+ if err
31
+ blk.call(nil, err)
32
+ else
33
+ blk.call(c, nil)
34
+ end
35
+ end
36
+ end
37
+
38
+ def self.uri(u)
39
+ addrs, sk = [], ""
40
+
41
+ if u =~ /^doozer:\?(.*)$/
42
+ parts = $1.split("&")
43
+ parts.each do |pt|
44
+ k, v = pt.split("=")
45
+ case k
46
+ when "ca"
47
+ addrs << v
48
+ when "sk"
49
+ sk = v
50
+ end
51
+ end
52
+ else
53
+ raise ArgumentError, "invalid doozer uri"
54
+ end
55
+
56
+ [addrs, sk]
57
+ end
58
+
59
+ end
@@ -0,0 +1,211 @@
1
+ require 'eventmachine'
2
+ require 'fraggle/connection'
3
+
4
+ module Fraggle
5
+ class Client
6
+ include Request::Verb
7
+
8
+ MaxInt64 = 1<<63 - 1
9
+
10
+ attr_reader :cn, :addrs
11
+
12
+ def initialize(cn, addrs)
13
+ @cn, @addrs = cn, addrs
14
+ @attempt = Proc.new {|_| true }
15
+ end
16
+
17
+ def addr
18
+ cn.addr
19
+ end
20
+
21
+ def set(rev, path, value, &blk)
22
+ req = Request.new
23
+ req.verb = SET
24
+ req.rev = rev
25
+ req.path = path
26
+ req.value = value
27
+
28
+ idemp(req, &blk)
29
+ end
30
+
31
+ def get(rev, path, &blk)
32
+ req = Request.new
33
+ req.verb = GET
34
+ req.rev = rev
35
+ req.path = path
36
+
37
+ resend(req, &blk)
38
+ end
39
+
40
+ def del(rev, path, &blk)
41
+ req = Request.new
42
+ req.verb = DEL
43
+ req.rev = rev
44
+ req.path = path
45
+
46
+ idemp(req, &blk)
47
+ end
48
+
49
+ def _getdir(rev, path, offset, &blk)
50
+ req = Request.new
51
+ req.verb = GETDIR
52
+ req.rev = rev
53
+ req.path = path
54
+ req.offset = offset
55
+
56
+ resend(req, &blk)
57
+ end
58
+
59
+ def _walk(rev, path, offset, &blk)
60
+ req = Request.new
61
+ req.verb = WALK
62
+ req.rev = rev
63
+ req.path = path
64
+ req.offset = offset
65
+
66
+ resend(req, &blk)
67
+ end
68
+
69
+ def wait(rev, path, &blk)
70
+ req = Request.new
71
+ req.verb = WAIT
72
+ req.rev = rev
73
+ req.path = path
74
+
75
+ resend(req, &blk)
76
+ end
77
+
78
+ def rev(&blk)
79
+ req = Request.new
80
+ req.verb = REV
81
+
82
+ resend(req) do |v, _|
83
+ blk.call(v.rev)
84
+ end
85
+ end
86
+
87
+ def stat(rev, path, &blk)
88
+ req = Request.new
89
+ req.rev = rev
90
+ req.verb = STAT
91
+ req.path = path
92
+
93
+ resend(req, &blk)
94
+ end
95
+
96
+ def access(secret, &blk)
97
+ req = Request.new
98
+ req.verb = ACCESS
99
+ req.value = secret
100
+
101
+ resend(req, &blk)
102
+ end
103
+
104
+ def watch(rev, path, &blk)
105
+ wait(rev, path) do |e, err|
106
+ blk.call(e, err)
107
+ if ! err
108
+ watch(e.rev+1, path, &blk)
109
+ end
110
+ end
111
+ end
112
+
113
+ def getdir(rev, path, off=0, lim=MaxInt64, ents=[], &blk)
114
+ all(:_getdir, rev, path, off, lim, ents, &blk)
115
+ end
116
+
117
+ def walk(rev, path, off=0, lim=MaxInt64, ents=[], &blk)
118
+ all(:_walk, rev, path, off, lim, ents, &blk)
119
+ end
120
+
121
+ def all(m, rev, path, off, lim, ents=[], &blk)
122
+ # We're decrementing lim as we go, so we need to return
123
+ # the accumulated values
124
+ if lim == 0
125
+ cn.next_tick { blk.call(ents, nil) }
126
+ return
127
+ end
128
+
129
+ __send__(m, rev, path, off) do |e, err|
130
+ case err && err.code
131
+ when nil
132
+ all(m, rev, path, off+1, lim-1, ents << e, &blk)
133
+ when Fraggle::Response::Err::RANGE
134
+ blk.call(ents, nil)
135
+ else
136
+ blk.call(nil, e)
137
+ end
138
+ end
139
+ end
140
+
141
+ # Sends a request to the server. Returns the request with a new tag
142
+ # assigned.
143
+ def send(req, &blk)
144
+ cb = Proc.new do |e, err|
145
+ case err
146
+ when Connection::DisconnectedError
147
+ if cn.err?
148
+ reconnect!
149
+ end
150
+ end
151
+ blk.call(e, err)
152
+ end
153
+
154
+ cn.send_request(req, cb)
155
+ end
156
+
157
+ def resend(req, &blk)
158
+ send(req) do |e, err|
159
+ case err
160
+ when Connection::DisconnectedError
161
+ req.tag = nil
162
+ resend(req, &blk)
163
+ else
164
+ blk.call(e, err)
165
+ end
166
+ end
167
+ end
168
+
169
+ def idemp(req, &blk)
170
+ send(req) do |e, err|
171
+ case err
172
+ when Connection::DisconnectedError
173
+ # If we're trying to update a value that isn't missing or that we're
174
+ # not trying to clobber, it's safe to retry. We can't idempotently
175
+ # update missing values because there may be a race with another
176
+ # client that sets and/or deletes the key during the time between your
177
+ # read and write.
178
+ if (req.rev || 0) > 0
179
+ req.tag = nil
180
+ idemp(req, &blk)
181
+ else
182
+ blk.call(e, err)
183
+ end
184
+ else
185
+ blk.call(e, err)
186
+ end
187
+ end
188
+ end
189
+
190
+ ##
191
+ # Setting `blk` will cause a client to call it before attempting to reconnect.
192
+ # `blk` is called with one parameter `addr`, which is the address that will be
193
+ # for reconnect.
194
+ def attempt(&blk)
195
+ @attempt = blk
196
+ end
197
+
198
+ def reconnect!
199
+ addr = @addrs.slice(rand(@addrs.length))
200
+ if @attempt.call(addr)
201
+ reconnect(addr)
202
+ end
203
+ end
204
+
205
+ def reconnect(addr)
206
+ host, port = addr.split(":")
207
+ @cn = EM.connect(host, port, Fraggle::Connection, addr)
208
+ end
209
+
210
+ end
211
+ end
@@ -0,0 +1,124 @@
1
+ require 'fraggle/response'
2
+
3
+ module Fraggle
4
+
5
+ module Connection
6
+
7
+ # Raised when a request is invalid
8
+ class SendError < StandardError
9
+ def initialize(req, msg=nil)
10
+ @req = req
11
+ super(msg)
12
+ end
13
+ end
14
+
15
+ class DisconnectedError < StandardError
16
+ def ==(o)
17
+ return false if ! o.kind_of?(self.class)
18
+ message == o.message
19
+ end
20
+ end
21
+
22
+ class ResponseError < StandardError
23
+ attr_reader :code
24
+
25
+ alias :detail :message
26
+
27
+ def initialize(res)
28
+ @code = res.err_code
29
+ super("#{res.name_for(Response::Err, code)}: #{res.err_detail}")
30
+ end
31
+
32
+ def ==(o)
33
+ return false if ! o.kind_of?(self.class)
34
+ code == o.code && message == o.message
35
+ end
36
+ end
37
+
38
+
39
+ attr_reader :addr
40
+
41
+ def initialize(addr)
42
+ @addr, @cb = addr, {}
43
+ end
44
+
45
+ def receive_data(data)
46
+ (@buf ||= "") << data
47
+
48
+ while @buf.length > 0
49
+ if @len && @buf.length >= @len
50
+ bytes = @buf.slice!(0, @len)
51
+ @len = nil
52
+ res = Response.decode(bytes)
53
+ receive_response(res)
54
+ elsif @buf.length >= 4
55
+ bytes = @buf.slice!(0, 4)
56
+ @len = bytes.unpack("N")[0]
57
+ else
58
+ break
59
+ end
60
+ end
61
+ end
62
+
63
+ # The default receive_response
64
+ def receive_response(res)
65
+ return if err?
66
+ req, blk = @cb.delete(res.tag)
67
+ return if ! blk
68
+ if res.err_code
69
+ blk.call(nil, ResponseError.new(res))
70
+ else
71
+ blk.call(res, nil)
72
+ end
73
+ end
74
+
75
+ def send_request(req, blk)
76
+ if req.tag
77
+ raise SendError, "Already sent #{req.inspect}"
78
+ end
79
+
80
+ if err?
81
+ next_tick { blk.call(nil, DisconnectedError.new(self.addr)) }
82
+ return req
83
+ end
84
+
85
+ req.tag = 0
86
+ while @cb.has_key?(req.tag)
87
+ req.tag += 1
88
+
89
+ req.tag %= 2**31
90
+ end
91
+
92
+ # TODO: remove this!
93
+ @cb[req.tag] = [req, blk]
94
+
95
+ data = req.encode
96
+ head = [data.length].pack("N")
97
+
98
+ send_data("#{head}#{data}")
99
+
100
+ req
101
+ end
102
+
103
+ def unbind
104
+ @err = true
105
+ @cb.values.each do |_, blk|
106
+ blk.call(nil, DisconnectedError.new(self.addr))
107
+ end
108
+ end
109
+
110
+ def err?
111
+ !!@err
112
+ end
113
+
114
+ def timer(n, &blk)
115
+ EM.add_timer(n, &blk)
116
+ end
117
+
118
+ def next_tick(&blk)
119
+ EM.next_tick(&blk)
120
+ end
121
+
122
+ end
123
+
124
+ end