fraggle 0.1.1 → 0.2.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/README.md +32 -16
- data/lib/fraggle.rb +6 -316
- data/lib/fraggle/client.rb +325 -0
- data/lib/fraggle/emitter.rb +26 -0
- data/lib/fraggle/logger.rb +26 -0
- data/lib/fraggle/meta.rb +9 -0
- data/lib/fraggle/{proto.rb → msg.rb} +10 -2
- data/lib/fraggle/protocol.rb +47 -0
- data/lib/fraggle/request.rb +15 -0
- data/lib/fraggle/response.rb +33 -0
- data/lib/fraggle/snap.rb +43 -0
- data/lib/fraggle/test.rb +72 -0
- data/test/fraggle_client_test.rb +261 -0
- data/test/fraggle_protocol_test.rb +100 -0
- data/test/fraggle_snap_test.rb +67 -0
- metadata +20 -11
- data/test/core_test.rb +0 -215
- data/test/live_test.rb +0 -197
- data/test/reconnect_test.rb +0 -175
@@ -0,0 +1,26 @@
|
|
1
|
+
module Fraggle
|
2
|
+
|
3
|
+
module Emitter
|
4
|
+
|
5
|
+
def callbacks
|
6
|
+
@callbacks ||= Hash.new(lambda {})
|
7
|
+
end
|
8
|
+
|
9
|
+
def emit(name, *args)
|
10
|
+
callbacks[name].call(*args)
|
11
|
+
end
|
12
|
+
|
13
|
+
def valid(&blk) ; must_callback!(:valid, blk) ; end
|
14
|
+
def done(&blk) ; must_callback!(:done, blk) ; end
|
15
|
+
def error(&blk) ; must_callback!(:error, blk) ; end
|
16
|
+
|
17
|
+
def must_callback!(name, blk)
|
18
|
+
if ! blk
|
19
|
+
raise ArgumentError, "no block given to `#{name}`"
|
20
|
+
end
|
21
|
+
callbacks[name] = blk
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Fraggle
|
2
|
+
module Logger
|
3
|
+
|
4
|
+
DEBUG = 0
|
5
|
+
INFO = 1
|
6
|
+
ERROR = 2
|
7
|
+
|
8
|
+
attr_accessor :writer, :level
|
9
|
+
|
10
|
+
def log(lv, msg)
|
11
|
+
label = case lv
|
12
|
+
when DEBUG then "debug "
|
13
|
+
when INFO then "info "
|
14
|
+
when ERROR then "error "
|
15
|
+
end
|
16
|
+
|
17
|
+
if lv >= level
|
18
|
+
writer.puts "#{label}: #{msg}"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def debug(msg) ; log(DEBUG, msg) ; end
|
23
|
+
def info(msg) ; log(INFO, msg) ; end
|
24
|
+
def error(msg) ; log(ERROR, msg) ; end
|
25
|
+
end
|
26
|
+
end
|
data/lib/fraggle/meta.rb
ADDED
@@ -18,6 +18,7 @@ module Fraggle
|
|
18
18
|
NOOP = 7; # {} => {}
|
19
19
|
WATCH = 8; # path => {cas, path, value}+
|
20
20
|
CANCEL = 10; # id => {}
|
21
|
+
STAT = 16; # path, id => cas, len
|
21
22
|
|
22
23
|
# future
|
23
24
|
GETDIR = 14; # path => {cas, value}+
|
@@ -45,14 +46,20 @@ module Fraggle
|
|
45
46
|
class Response
|
46
47
|
include Beefcake::Message
|
47
48
|
|
48
|
-
required :tag, :
|
49
|
+
required :tag, :uint32, 1
|
49
50
|
required :flags, :int32, 2
|
50
51
|
|
51
|
-
optional :
|
52
|
+
optional :rev, :int64, 3
|
52
53
|
optional :cas, :int64, 4
|
53
54
|
optional :path, :string, 5
|
54
55
|
optional :value, :bytes, 6
|
55
56
|
optional :id, :int32, 7
|
57
|
+
optional :len, :int32, 8
|
58
|
+
|
59
|
+
module Flag
|
60
|
+
VALID = 1
|
61
|
+
DONE = 2
|
62
|
+
end
|
56
63
|
|
57
64
|
module Err
|
58
65
|
# don't use value 0
|
@@ -66,6 +73,7 @@ module Fraggle
|
|
66
73
|
# match unix errno
|
67
74
|
NOTDIR = 20
|
68
75
|
ISDIR = 21
|
76
|
+
NOINT = 22
|
69
77
|
end
|
70
78
|
|
71
79
|
optional :err_code, Err, 100
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'fraggle/response'
|
2
|
+
|
3
|
+
module Fraggle
|
4
|
+
|
5
|
+
module Protocol
|
6
|
+
|
7
|
+
attr_reader :last_received
|
8
|
+
|
9
|
+
def receive_data(data)
|
10
|
+
@last_received = Time.now
|
11
|
+
|
12
|
+
(@buf ||= "") << data
|
13
|
+
|
14
|
+
while @buf.length > 0
|
15
|
+
if @len && @buf.length >= @len
|
16
|
+
bytes = @buf.slice!(0, @len)
|
17
|
+
@len = nil
|
18
|
+
res = Response.decode(bytes)
|
19
|
+
receive_response(res)
|
20
|
+
elsif @buf.length >= 4
|
21
|
+
bytes = @buf.slice!(0, 4)
|
22
|
+
@len = bytes.unpack("N")[0]
|
23
|
+
else
|
24
|
+
break
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# The default receive_response
|
30
|
+
def receive_response(res)
|
31
|
+
p res
|
32
|
+
end
|
33
|
+
|
34
|
+
def send_request(req)
|
35
|
+
data = req.encode
|
36
|
+
head = [data.length].pack("N")
|
37
|
+
|
38
|
+
send_data("#{head}#{data}")
|
39
|
+
end
|
40
|
+
|
41
|
+
def send_data(data)
|
42
|
+
super(data)
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'fraggle/msg'
|
2
|
+
require 'fraggle/emitter'
|
3
|
+
|
4
|
+
##
|
5
|
+
# An extension to Request in msg.rb. I want to keep these seperated so when
|
6
|
+
# future versions of Beefcake can generate code, we don't have to manually add
|
7
|
+
# this back in for each generation.
|
8
|
+
|
9
|
+
module Fraggle
|
10
|
+
|
11
|
+
class Request
|
12
|
+
include Emitter
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'fraggle/msg'
|
2
|
+
|
3
|
+
##
|
4
|
+
# An extension to Response in msg.rb. I want to keep these seperated so when
|
5
|
+
# future versions of Beefcake can generate code, we don't have to manually add
|
6
|
+
# this back in for each generation.
|
7
|
+
|
8
|
+
module Fraggle
|
9
|
+
|
10
|
+
class Response
|
11
|
+
|
12
|
+
Missing = 0
|
13
|
+
Clobber = -1
|
14
|
+
Dir = -2
|
15
|
+
Dummy = -3
|
16
|
+
|
17
|
+
# CAS
|
18
|
+
def missing? ; cas == Missing ; end
|
19
|
+
def dir? ; cas == Dir ; end
|
20
|
+
def dummy? ; cas == Dummy ; end
|
21
|
+
|
22
|
+
# ERR
|
23
|
+
def ok? ; error_code != 0 ; end
|
24
|
+
def other? ; error_code == Err::OTHER ; end
|
25
|
+
def unknown_verb? ; error_code == Err::UNKNOWN_VERB ; end
|
26
|
+
def redirect? ; error_code == Err::REDIRECT ; end
|
27
|
+
def invalid_snap? ; error_code == Err::INVALID_SNAP ; end
|
28
|
+
def mismatch? ; error_code == Err::CAS_MISMATCH ; end
|
29
|
+
def not_dir? ; error_code == Err::NOT_DIR ; end
|
30
|
+
def is_dir? ; error_code == Err::ISDIR ; end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
data/lib/fraggle/snap.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'fraggle/client'
|
2
|
+
|
3
|
+
module Fraggle
|
4
|
+
|
5
|
+
class Snap
|
6
|
+
|
7
|
+
attr_reader :id
|
8
|
+
|
9
|
+
def initialize(id, c)
|
10
|
+
@id = id
|
11
|
+
@c = c
|
12
|
+
end
|
13
|
+
|
14
|
+
def get(path, &blk)
|
15
|
+
@c.get(@id, path, &blk)
|
16
|
+
end
|
17
|
+
|
18
|
+
def walk(glob, &blk)
|
19
|
+
@c.walk(@id, glob, &blk)
|
20
|
+
end
|
21
|
+
|
22
|
+
def stat(path, &blk)
|
23
|
+
@c.stat(@id, path, &blk)
|
24
|
+
end
|
25
|
+
|
26
|
+
def getdir(path, offset=0, limit=0, &blk)
|
27
|
+
@c.getdir(@id, path, offset, limit, &blk)
|
28
|
+
end
|
29
|
+
|
30
|
+
def snap(&blk)
|
31
|
+
@c.snap do |res|
|
32
|
+
sn = Snap.new(res.id, @c)
|
33
|
+
blk.call(sn)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def method_missing(*args, &blk)
|
38
|
+
@c.__send__(*args, &blk)
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
data/lib/fraggle/test.rb
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'fraggle/request'
|
2
|
+
require 'fraggle/response'
|
3
|
+
|
4
|
+
module Fraggle
|
5
|
+
|
6
|
+
##
|
7
|
+
# I want this to be a great starting point for testing fraggle applications.
|
8
|
+
# It's currently a work in progress. Think Rack::Test for fraggle.
|
9
|
+
#
|
10
|
+
module Test
|
11
|
+
|
12
|
+
V = Fraggle::Request::Verb
|
13
|
+
F = Fraggle::Response::Flag
|
14
|
+
E = Fraggle::Response::Err
|
15
|
+
|
16
|
+
|
17
|
+
class TestClient < Array
|
18
|
+
include Fraggle::Client
|
19
|
+
alias :send_request :<<
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
# This is handy for testing callbacks
|
24
|
+
class Blk < Array
|
25
|
+
def to_proc
|
26
|
+
Proc.new {|res| self << res }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
def assert_sent(tag, attrs={})
|
32
|
+
req = Fraggle::Request.new(attrs)
|
33
|
+
req.tag = tag
|
34
|
+
|
35
|
+
msg = "This was not sent:\n"
|
36
|
+
msg << " #{req.inspect}\n"
|
37
|
+
msg << "Sent:\n "
|
38
|
+
msg << c.map {|r| r.inspect }.join("\n ")
|
39
|
+
msg << "\n"
|
40
|
+
|
41
|
+
assert_block(msg) { c.include?(req) }
|
42
|
+
end
|
43
|
+
|
44
|
+
def assert_recv(attrs)
|
45
|
+
req = Fraggle::Response.new(attrs)
|
46
|
+
msg = "This was not recieved:\n"
|
47
|
+
msg << " #{req.inspect}\n"
|
48
|
+
msg << "Received:\n "
|
49
|
+
msg << blk.map {|r| r.inspect }.join("\n ")
|
50
|
+
msg << "\n"
|
51
|
+
|
52
|
+
assert_block(msg) { blk.include?(attrs) }
|
53
|
+
end
|
54
|
+
|
55
|
+
# Replies with a valid response
|
56
|
+
def reply(tag, attrs={})
|
57
|
+
res = Fraggle::Response.new(attrs)
|
58
|
+
res.tag = tag
|
59
|
+
res.flags ||= 0
|
60
|
+
res.flags |= Fraggle::Response::Flag::VALID
|
61
|
+
c.receive_response(res)
|
62
|
+
res
|
63
|
+
end
|
64
|
+
|
65
|
+
# Replies with a valid + done response
|
66
|
+
def reply!(tag, attrs={})
|
67
|
+
reply(tag, attrs.merge(:flags => Fraggle::Response::Flag::DONE))
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
@@ -0,0 +1,261 @@
|
|
1
|
+
require 'fraggle/client'
|
2
|
+
require 'fraggle/response'
|
3
|
+
require 'fraggle/test'
|
4
|
+
|
5
|
+
class FraggleClientTest < Test::Unit::TestCase
|
6
|
+
include Fraggle::Test
|
7
|
+
|
8
|
+
attr_reader :c, :blk
|
9
|
+
|
10
|
+
def setup
|
11
|
+
@c = TestClient.new("doozer://127.0.0.1:8046")
|
12
|
+
@blk = Blk.new
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_send_recv
|
16
|
+
req = c.send(Fraggle::Request.new, &blk)
|
17
|
+
|
18
|
+
assert_sent req.tag
|
19
|
+
assert_recv reply(req.tag)
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_valid
|
23
|
+
req = c.send(Fraggle::Request.new, &blk)
|
24
|
+
|
25
|
+
reply(req.tag)
|
26
|
+
reply(req.tag)
|
27
|
+
reply(req.tag)
|
28
|
+
|
29
|
+
assert_equal 3, blk.length
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_done
|
33
|
+
req = c.send(Fraggle::Request.new)
|
34
|
+
req.done(&blk)
|
35
|
+
|
36
|
+
reply!(req.tag)
|
37
|
+
|
38
|
+
assert_equal 1, blk.length
|
39
|
+
|
40
|
+
req.valid(&blk)
|
41
|
+
|
42
|
+
reply(req.tag)
|
43
|
+
reply(req.tag)
|
44
|
+
reply(req.tag)
|
45
|
+
|
46
|
+
assert_equal 1, blk.length
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_default_error
|
50
|
+
req = c.send(Fraggle::Request.new)
|
51
|
+
|
52
|
+
assert_raises Fraggle::Client::Error do
|
53
|
+
reply(req.tag, :err_code => E::OTHER, :err_detail => "boom!")
|
54
|
+
end
|
55
|
+
|
56
|
+
assert_nothing_raised do
|
57
|
+
reply(req.tag, :err_code => E::OTHER, :err_detail => "boom!")
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def test_error
|
62
|
+
req = c.send(Fraggle::Request.new)
|
63
|
+
req.error(&blk)
|
64
|
+
|
65
|
+
reply(req.tag, :err_code => E::OTHER, :err_detail => "boom!")
|
66
|
+
reply(req.tag, :err_code => E::OTHER, :err_detail => "boom!")
|
67
|
+
|
68
|
+
assert_equal 1, blk.length
|
69
|
+
end
|
70
|
+
|
71
|
+
def test_tagging
|
72
|
+
t = Fraggle::Client::MinTag
|
73
|
+
|
74
|
+
assert_equal t+0, c.noop.tag
|
75
|
+
assert_equal t+1, c.noop.tag
|
76
|
+
assert_equal t+2, c.noop.tag
|
77
|
+
assert_equal t+3, c.noop.tag
|
78
|
+
assert_equal t+4, c.noop.tag
|
79
|
+
end
|
80
|
+
|
81
|
+
# CHECKIN cas, path => cas
|
82
|
+
def test_checkin
|
83
|
+
req = c.checkin("abc123", 123, &blk)
|
84
|
+
|
85
|
+
assert_sent(req.tag, :verb => V::CHECKIN, :path => "abc123", :cas => 123)
|
86
|
+
assert_recv(reply(req.tag, :cas => 123))
|
87
|
+
end
|
88
|
+
|
89
|
+
# GET path, id => cas, value
|
90
|
+
def test_get
|
91
|
+
req = c.get(0, "/ping", &blk)
|
92
|
+
|
93
|
+
assert_sent req.tag, :verb => V::GET, :path => "/ping"
|
94
|
+
assert_recv reply(req.tag, :cas => 123, :value => "pong")
|
95
|
+
end
|
96
|
+
|
97
|
+
# STAT path, id => cas, len
|
98
|
+
def test_stat
|
99
|
+
req = c.stat(0, "/ping", &blk)
|
100
|
+
|
101
|
+
assert_sent req.tag, :verb => V::STAT, :path => "/ping"
|
102
|
+
assert_recv reply(req.tag, :cas => 123, :len => 4)
|
103
|
+
end
|
104
|
+
|
105
|
+
# GETDIR id, path, offset, limit => {cas, value}+
|
106
|
+
def test_getdir
|
107
|
+
req = c.getdir(0, "/test", 0, 0, &blk)
|
108
|
+
|
109
|
+
assert_sent req.tag, :verb => V::GETDIR, :path => "/test"
|
110
|
+
assert_recv reply(req.tag, :cas => 123, :value => "a")
|
111
|
+
|
112
|
+
req = c.getdir(0, "/test", 1, 2, &blk)
|
113
|
+
|
114
|
+
assert_sent req.tag, :verb => V::GETDIR, :path => "/test", :offset => 1, :limit => 2
|
115
|
+
assert_recv reply(req.tag, :cas => 123, :value => "b")
|
116
|
+
end
|
117
|
+
|
118
|
+
# SET cas, path, value => cas
|
119
|
+
def test_set
|
120
|
+
req = c.set("/foo", "bar", 123, &blk)
|
121
|
+
|
122
|
+
assert_sent(req.tag, :verb => V::SET, :cas => 123, :path => "/foo", :value => "bar")
|
123
|
+
assert_recv(reply(req.tag, :cas => 123))
|
124
|
+
end
|
125
|
+
|
126
|
+
# DEL cas, path => {}
|
127
|
+
def test_del
|
128
|
+
req = c.del("/foo", 123, &blk)
|
129
|
+
|
130
|
+
assert_sent(req.tag, :verb => V::DEL, :cas => 123, :path => "/foo")
|
131
|
+
assert_recv(reply(req.tag))
|
132
|
+
end
|
133
|
+
|
134
|
+
# WALK path, id => {cas, path, value}+
|
135
|
+
def test_walk
|
136
|
+
req = c.walk(0, "/foo/*", &blk)
|
137
|
+
|
138
|
+
assert_respond_to req, :cancel
|
139
|
+
|
140
|
+
assert_sent(req.tag, :verb => V::WALK, :path => "/foo/*")
|
141
|
+
assert_recv(reply(req.tag, :cas => 123, :path => "/foo/a", :value => "1"))
|
142
|
+
assert_recv(reply(req.tag, :cas => 456, :path => "/foo/b", :value => "2"))
|
143
|
+
assert_recv(reply(req.tag, :cas => 789, :path => "/foo/c", :value => "3"))
|
144
|
+
end
|
145
|
+
|
146
|
+
# WATCH path => {cas, path, value}+
|
147
|
+
def test_watch
|
148
|
+
req = c.watch("/foo/*", &blk)
|
149
|
+
|
150
|
+
assert_respond_to req, :cancel
|
151
|
+
|
152
|
+
assert_sent(req.tag, :verb => V::WATCH, :path => "/foo/*")
|
153
|
+
assert_recv(reply(req.tag, :cas => 123, :path => "/foo/a", :value => "1"))
|
154
|
+
assert_recv(reply(req.tag, :cas => 456, :path => "/foo/b", :value => "2"))
|
155
|
+
assert_recv(reply(req.tag, :cas => 789, :path => "/foo/c", :value => "3"))
|
156
|
+
end
|
157
|
+
|
158
|
+
# SNAP {} => id
|
159
|
+
def test_snap
|
160
|
+
req = c.snap(&blk)
|
161
|
+
|
162
|
+
assert_sent(req.tag, :verb => V::SNAP)
|
163
|
+
assert_recv(reply(req.tag, :id => 1))
|
164
|
+
end
|
165
|
+
|
166
|
+
# DELSNAP id => {}
|
167
|
+
def test_delsnap
|
168
|
+
req = c.delsnap(1, &blk)
|
169
|
+
|
170
|
+
assert_sent(req.tag, :verb => V::DELSNAP, :id => 1)
|
171
|
+
assert_recv(reply(req.tag))
|
172
|
+
end
|
173
|
+
|
174
|
+
# NOOP {} => {}
|
175
|
+
def test_noop
|
176
|
+
req = c.noop(&blk)
|
177
|
+
|
178
|
+
assert_sent(req.tag, :verb => V::NOOP)
|
179
|
+
assert_recv(reply(req.tag))
|
180
|
+
end
|
181
|
+
|
182
|
+
# CANCEL id => {}
|
183
|
+
def test_cancel
|
184
|
+
nop = c.noop(&blk)
|
185
|
+
req = c.__cancel__(nop, &blk)
|
186
|
+
|
187
|
+
assert_sent(req.tag, :verb => V::CANCEL, :id => nop.tag)
|
188
|
+
assert_recv(reply(req.tag))
|
189
|
+
end
|
190
|
+
|
191
|
+
def test_cancelable
|
192
|
+
can = c.cancelable(Fraggle::Request.new(:tag => 123))
|
193
|
+
|
194
|
+
assert ! can.canceled?
|
195
|
+
|
196
|
+
req = can.cancel
|
197
|
+
|
198
|
+
assert can.canceled?
|
199
|
+
assert_equal 1, c.length
|
200
|
+
assert_sent req.tag, :verb => V::CANCEL, :id => can.tag
|
201
|
+
|
202
|
+
# A few more for good measure
|
203
|
+
can.cancel
|
204
|
+
can.cancel
|
205
|
+
|
206
|
+
# Ensure we haven't called cancel on the same tag more than once.
|
207
|
+
assert_equal 1, c.length
|
208
|
+
end
|
209
|
+
|
210
|
+
def test_cancel_does_not_prematurely_remove_callback
|
211
|
+
x = c.watch("/foo/*", &blk)
|
212
|
+
y = x.cancel
|
213
|
+
|
214
|
+
assert_not_equal x.object_id, y.object_id
|
215
|
+
assert_not_equal x.tag, y.tag
|
216
|
+
end
|
217
|
+
|
218
|
+
def test_cancel_discards_further_replies
|
219
|
+
x = c.watch("/foo/*", &blk)
|
220
|
+
x.cancel
|
221
|
+
|
222
|
+
reply!(x.tag)
|
223
|
+
|
224
|
+
# The cancel happened before the reply was received by the reactor. Any
|
225
|
+
# remaining data from the server should be discarded.
|
226
|
+
assert_equal 0, blk.length
|
227
|
+
end
|
228
|
+
|
229
|
+
def test_tag_pending_cancel_is_not_useable
|
230
|
+
x = c.watch("/foo/*", &blk)
|
231
|
+
y = x.cancel
|
232
|
+
|
233
|
+
# Force a reset of tag so that `send` will attempt
|
234
|
+
# to reuse the pending cancels tag.
|
235
|
+
c.instance_eval { @tag = x.tag }
|
236
|
+
|
237
|
+
z = c.noop
|
238
|
+
|
239
|
+
assert_not_equal x.tag, z.tag
|
240
|
+
assert_not_equal y.tag, z.tag
|
241
|
+
end
|
242
|
+
|
243
|
+
def test_reuse_canceled_tag
|
244
|
+
x = c.watch("/foo/*", &blk)
|
245
|
+
y = x.cancel
|
246
|
+
|
247
|
+
reply!(y.tag)
|
248
|
+
|
249
|
+
z = c.noop
|
250
|
+
|
251
|
+
assert_equal x.tag, z.tag
|
252
|
+
end
|
253
|
+
|
254
|
+
# These are planned for future doozer versions
|
255
|
+
#
|
256
|
+
# ESET cas, path => {}
|
257
|
+
# GETDIR path => {cas, value}+
|
258
|
+
# MONITOR path => {cas, path, value}+
|
259
|
+
# SYNCPATH path => cas, value
|
260
|
+
|
261
|
+
end
|