fraggel 0.1.0

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.
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