fraggel 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2011 Blake Mizerany, Keith Rarick, Chris Moos
2
+
3
+ Permission is hereby granted, free of charge, to any person
4
+ obtaining a copy of this software and associated documentation
5
+ files (the "Software"), to deal in the Software without
6
+ restriction, including without limitation the rights to use,
7
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the
9
+ Software is furnished to do so, subject to the following
10
+ conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,61 @@
1
+ # Fraggel
2
+ **An EventMachine based Doozer client**
3
+
4
+ ## Install
5
+
6
+ $ gem install fraggel
7
+
8
+ ## Use
9
+
10
+ require 'rubygems'
11
+ require 'eventmachine'
12
+ require 'fraggel'
13
+
14
+ EM.start do
15
+ client = Fraggel.connect "127.0.0.1", 8046
16
+
17
+ ## Setting a key
18
+ client.set "/foo", "bar", :missing do |cas, err|
19
+ if err != nil
20
+ cas # => "123"
21
+ cas.dir? # => false
22
+ end
23
+ end
24
+
25
+ client.get "/foo" do |body, cas, err|
26
+ if err != nil
27
+ body # => "bar"
28
+ cas # => "123"
29
+ cas.dir? # => false
30
+ end
31
+ end
32
+
33
+ watch = client.watch "/foo" do |path, body, cas, err|
34
+ # The event has:
35
+ # ------------------------
36
+ # NOTE: `err` will be set iff the glob is bad
37
+ # err # => nil
38
+ # path # => "/foo"
39
+ # body # => "bar"
40
+ # cas # => "123"
41
+ # cas.set? # => true
42
+ # cas.del? # => false
43
+ # ------------------------
44
+
45
+ # Phoney check for example
46
+ if can_stop_watching?(path)
47
+ client.close(watch)
48
+ end
49
+ end
50
+
51
+ end
52
+
53
+
54
+ ## Develop
55
+
56
+ **Clone**
57
+ $ git clone http://github.com/bmizerany/fraggel.git
58
+
59
+ **Test**
60
+ $ gem install turn
61
+ $ turn
@@ -0,0 +1,9 @@
1
+ require 'fraggel'
2
+
3
+ EM.run do
4
+ client = Fraggel.connect 8046
5
+
6
+ client.get "/ping" do |body, cas, err|
7
+ p [:got, body, cas, err]
8
+ end
9
+ end
@@ -0,0 +1,13 @@
1
+ require 'fraggel'
2
+
3
+ Glob = "/letters/*"
4
+
5
+ EM.run do
6
+ client = Fraggel.connect 8046
7
+
8
+ puts "Watching #{Glob}"
9
+
10
+ client.watch Glob do |path, body, cas, err|
11
+ p [:noticed, path, body, cas, err]
12
+ end
13
+ end
@@ -0,0 +1,238 @@
1
+ require 'eventmachine'
2
+ require 'fraggel/decoder'
3
+ require 'fraggel/encoder'
4
+ require 'fraggel/responder'
5
+
6
+ module Fraggel
7
+ include Encoder
8
+
9
+ # Flags
10
+ Valid = 1
11
+ Done = 2
12
+
13
+ # Cas
14
+ Dir = "dir"
15
+ Missing = "0"
16
+ Clobber = ""
17
+
18
+ # Create an unique object to test for
19
+ # set or not-set
20
+ None = Object.new
21
+
22
+ module Cas
23
+ def dir?
24
+ self == Dir
25
+ end
26
+
27
+ def del?
28
+ self == "0"
29
+ end
30
+
31
+ def dummy?
32
+ empty?
33
+ end
34
+
35
+ def set?
36
+ ! del? && ! dummy?
37
+ end
38
+
39
+ end
40
+
41
+ def self.connect(port, host="127.0.0.1")
42
+ EM.connect(host, port, self)
43
+ end
44
+
45
+ def post_init
46
+ @callbacks = {}
47
+ @opid = 0
48
+
49
+ @responder = Responder.new do |value|
50
+ receive_response(value)
51
+ end
52
+
53
+ @decoder = Decoder.new do |name, value|
54
+ @responder.receive_event(name, value)
55
+ end
56
+ end
57
+
58
+ def receive_data(data)
59
+ @decoder.receive_data(data)
60
+ end
61
+
62
+ def receive_response(response)
63
+ opid, flags, value = response
64
+
65
+ if blk = @callbacks[opid]
66
+ if (flags & Valid) > 0
67
+ blk.call(value)
68
+ end
69
+
70
+ if (flags & Done) > 0
71
+ blk.call(:done)
72
+ @callbacks.delete(opid)
73
+ end
74
+ else
75
+ # TODO: Log something? Raise error?
76
+ end
77
+ end
78
+
79
+ def call(verb, args=None, &blk)
80
+ @opid += 1
81
+ @callbacks[@opid] = blk
82
+
83
+ request = [@opid, verb.to_s]
84
+ if args != None
85
+ request << args
86
+ end
87
+
88
+ encoded = encode(request)
89
+
90
+ # TODO: Chunk with next_tick
91
+ send_data(encoded)
92
+
93
+ @opid
94
+ end
95
+
96
+ def get(path, snap_id=0, &blk)
97
+ call :GET, [path, snap_id] do |res|
98
+ case res
99
+ when StandardError
100
+ blk.call(nil, nil, res)
101
+ when :done
102
+ # Do nothing
103
+ else
104
+ # Add sugar to the CAS token
105
+ res[1].extend Cas
106
+ blk.call(*res)
107
+ end
108
+ end
109
+ end
110
+
111
+ def set(path, body, cas, &blk)
112
+ call :SET, [path, body, casify(cas)] do |res|
113
+ case res
114
+ when StandardError
115
+ blk.call(nil, res)
116
+ when :done
117
+ # Do nothing
118
+ else
119
+ res.extend Cas
120
+ blk.call(res)
121
+ end
122
+ end
123
+ end
124
+
125
+ def sett(path, i, cas, &blk)
126
+ call :SETT, [path, i, casify(cas)] do |res|
127
+ case res
128
+ when StandardError
129
+ blk.call(nil, nil, res)
130
+ when :done
131
+ # Do nothing
132
+ else
133
+ res[1].extend Cas
134
+ blk.call(*res)
135
+ end
136
+ end
137
+ end
138
+
139
+ def close(opid, &blk)
140
+ call :CLOSE, opid do |res|
141
+ case res
142
+ when StandardError
143
+ blk.call(res)
144
+ when :done
145
+ # Do nothing
146
+ else
147
+ blk.call(nil)
148
+ end
149
+ end
150
+ end
151
+
152
+ def del(path, cas, &blk)
153
+ call :DEL, [path, cas] do |res|
154
+ case res
155
+ when StandardError
156
+ blk.call(res)
157
+ when :done
158
+ # Do nothing
159
+ else
160
+ blk.call(nil)
161
+ end
162
+ end
163
+ end
164
+
165
+ def noop(&blk)
166
+ call :NOOP do |res|
167
+ case res
168
+ when StandardError
169
+ blk.call(res)
170
+ when :done
171
+ # Do nothing
172
+ else
173
+ blk.call(nil)
174
+ end
175
+ end
176
+ end
177
+
178
+ def snap(&blk)
179
+ call :SNAP do |res|
180
+ case res
181
+ when StandardError
182
+ blk.call(nil, res)
183
+ when :done
184
+ # Do nothing
185
+ else
186
+ blk.call(res, nil)
187
+ end
188
+ end
189
+ end
190
+
191
+ def delsnap(&blk)
192
+ call :DELSNAP do |res|
193
+ case res
194
+ when StandardError
195
+ blk.call(nil, res)
196
+ when :done
197
+ # Do nothing
198
+ else
199
+ blk.call(res, nil)
200
+ end
201
+ end
202
+ end
203
+
204
+ def walk(glob, sid=0, &blk)
205
+ call :WALK, [glob, sid] do |res|
206
+ case res
207
+ when StandardError, :done
208
+ blk.call(nil, nil, nil, res)
209
+ else
210
+ res[2].extend Cas
211
+ blk.call(*res)
212
+ end
213
+ end
214
+ end
215
+
216
+ def watch(glob, &blk)
217
+ call :WATCH, glob do |res|
218
+ case res
219
+ when StandardError, :done
220
+ blk.call(nil, nil, nil, res)
221
+ else
222
+ res[2].extend Cas
223
+ blk.call(*res)
224
+ end
225
+ end
226
+ end
227
+
228
+ private
229
+
230
+ def casify(cas)
231
+ case cas
232
+ when :missing: "0"
233
+ when :clobber: ""
234
+ else cas
235
+ end
236
+ end
237
+
238
+ end
@@ -0,0 +1,133 @@
1
+ module Fraggel
2
+
3
+ class Decoder
4
+
5
+ class Poisioned < StandardError ; end
6
+
7
+ def initialize(&blk)
8
+ @receiver = blk
9
+ end
10
+
11
+ def receive_data(data)
12
+ @buf ||= ""
13
+ @buf << data
14
+
15
+ if ! @dcs
16
+ read_type
17
+ else
18
+ @dcs.call
19
+ end
20
+ end
21
+
22
+ def dcs(&blk)
23
+ @dcs = blk
24
+ @dcs.call
25
+ end
26
+
27
+ def read_type
28
+ dcs do
29
+ case @buf.slice!(0)
30
+ when nil
31
+ # Wait for next byte
32
+ when ?\r
33
+ finish do
34
+ read_type
35
+ end
36
+ when ?:
37
+ read_integer do |i|
38
+ @receiver.call(:value, i)
39
+ read_type
40
+ end
41
+ when ?$
42
+ read_string do |s|
43
+ @receiver.call(:value, s)
44
+ read_type
45
+ end
46
+ when ?+
47
+ read_line do |msg|
48
+ @receiver.call(:status, msg)
49
+ read_type
50
+ end
51
+ when ?-
52
+ read_line do |msg|
53
+ @receiver.call(:error, msg)
54
+ read_type
55
+ end
56
+ when ?*
57
+ read_integer do |count|
58
+ @receiver.call(:array, count)
59
+ read_type
60
+ end
61
+ else
62
+ raise Poisioned
63
+ end
64
+ end
65
+ end
66
+
67
+ def finish(&blk)
68
+ dcs do
69
+ c = @buf.slice!(0)
70
+ case c
71
+ when nil
72
+ # Wait for next byte
73
+ when ?\n
74
+ blk.call
75
+ else
76
+ raise Poisioned
77
+ end
78
+ end
79
+ end
80
+
81
+ def read_integer(&blk)
82
+ @int = ""
83
+ dcs do
84
+ while c = @buf.slice!(0)
85
+ case c
86
+ when ?0..?9
87
+ @int << c.chr
88
+ when ?\r
89
+ finish do
90
+ blk.call(Integer(@int))
91
+ end
92
+ else
93
+ raise Poisioned
94
+ end
95
+ end
96
+ end
97
+ end
98
+
99
+ def read_string(&blk)
100
+ read_integer do |count|
101
+ dcs do
102
+ if @buf.length >= count
103
+ string = @buf.slice!(0, count)
104
+ dcs do
105
+ case @buf.slice!(0)
106
+ when nil
107
+ # Wait for next byte
108
+ when ?\r
109
+ finish do
110
+ blk.call(string)
111
+ end
112
+ else
113
+ raise Poisioned
114
+ end
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
120
+
121
+ def read_line(&blk)
122
+ dcs do
123
+ if line = @buf.slice!(/.*\r/)
124
+ finish do
125
+ blk.call(line.chomp)
126
+ end
127
+ end
128
+ end
129
+ end
130
+
131
+ end
132
+
133
+ end