chawk 0.2.0 → 0.3.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.
- checksums.yaml +4 -4
- data/.pryrc +15 -3
- data/CHANGES +8 -1
- data/README.md +86 -61
- data/chawk.gemspec +3 -1
- data/lib/chawk/migration.rb +21 -0
- data/lib/chawk/version.rb +1 -1
- data/lib/chawk.rb +39 -19
- data/lib/models.rb +10 -150
- data/lib/node.rb +313 -0
- data/lib/range.rb +70 -0
- data/test/lib/addr_test.rb +63 -33
- data/test/lib/failed_node_test.rb +2 -2
- data/test/lib/paddr_test.rb +162 -186
- data/test/lib/range_test.rb +61 -0
- data/test/lib/vaddr_test.rb +130 -160
- data/test/schema.rb +23 -0
- data/test/test_helper.rb +9 -2
- metadata +6 -16
data/lib/node.rb
ADDED
@@ -0,0 +1,313 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
module Chawk
|
3
|
+
# Models used in Chawk. ActiveRecord classes.
|
4
|
+
module Models
|
5
|
+
class NodeInvalidator
|
6
|
+
extend Forwardable
|
7
|
+
def_delegators :sweeps, :size, :map
|
8
|
+
|
9
|
+
def initialize(node)
|
10
|
+
@node = node
|
11
|
+
@sweeps = []
|
12
|
+
@ranges = []
|
13
|
+
end
|
14
|
+
|
15
|
+
def <<(time)
|
16
|
+
@node.ranges.where("start_ts <= ? AND stop_ts >= ?",time,time).each do |range|
|
17
|
+
@ranges << range.id unless @ranges.include?(range.id)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def invalidate!()
|
22
|
+
@ranges.each{|r|Chawk::Models::Range.find(r).populate!}
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class NodeAggregator
|
27
|
+
|
28
|
+
attr_reader :dataset
|
29
|
+
|
30
|
+
def initialize(node)
|
31
|
+
node.check_read_access
|
32
|
+
if node.points.length > 0
|
33
|
+
@dataset = node.points.to_a.reduce([]) {|ary,p| ary << p.value}
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def max()
|
38
|
+
@dataset.max
|
39
|
+
end
|
40
|
+
|
41
|
+
def min()
|
42
|
+
@dataset.min
|
43
|
+
end
|
44
|
+
|
45
|
+
def mean
|
46
|
+
sum.to_f / @dataset.length
|
47
|
+
end
|
48
|
+
|
49
|
+
def sum
|
50
|
+
@dataset.reduce(0) {|sum,p| sum+=p}
|
51
|
+
end
|
52
|
+
|
53
|
+
def count
|
54
|
+
@dataset.length
|
55
|
+
end
|
56
|
+
|
57
|
+
def sumsqr
|
58
|
+
@dataset.map {|x| x * x}.reduce(&:+)
|
59
|
+
end
|
60
|
+
|
61
|
+
def stdev
|
62
|
+
m = mean
|
63
|
+
Math.sqrt((sumsqr - count * m * m)/(count-1))
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# The Node, where most Chawk:Node information is persisted..
|
68
|
+
class Node < ActiveRecord::Base
|
69
|
+
attr_accessor :agent
|
70
|
+
after_initialize :init
|
71
|
+
self.table_name_prefix = "chawk_"
|
72
|
+
belongs_to :agent
|
73
|
+
has_many :points
|
74
|
+
has_many :values
|
75
|
+
has_many :relations
|
76
|
+
has_many :ranges, foreign_key: :parent_node_id
|
77
|
+
|
78
|
+
attr_accessor :access
|
79
|
+
|
80
|
+
def init
|
81
|
+
@agent = nil
|
82
|
+
end
|
83
|
+
|
84
|
+
def clear_values!
|
85
|
+
check_admin_access
|
86
|
+
values.destroy_all
|
87
|
+
end
|
88
|
+
|
89
|
+
def clear_points!
|
90
|
+
check_admin_access
|
91
|
+
points.destroy_all
|
92
|
+
end
|
93
|
+
|
94
|
+
def _prepare_insert(val, ts, options)
|
95
|
+
values = {value:val,observed_at:ts.to_f}
|
96
|
+
if options[:meta]
|
97
|
+
if options[:meta].is_a?(Hash)
|
98
|
+
values[:meta] = options[:meta].to_json
|
99
|
+
else
|
100
|
+
raise ArgumentError, "Meta must be a JSON-representable Hash. #{options[:meta].inspect}"
|
101
|
+
end
|
102
|
+
end
|
103
|
+
values
|
104
|
+
end
|
105
|
+
|
106
|
+
def _insert_value(val,ts,options={})
|
107
|
+
self.values.create(_prepare_insert(val, ts, options))
|
108
|
+
ts
|
109
|
+
end
|
110
|
+
|
111
|
+
def value_recognizer(item, dt, options={})
|
112
|
+
case
|
113
|
+
when item.is_a?(String)
|
114
|
+
_insert_value item,dt, options
|
115
|
+
else
|
116
|
+
raise ArgumentError, "Can't recognize format of data item. #{item.inspect}"
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def _unravel(items)
|
121
|
+
if items.is_a?(Array)
|
122
|
+
items.each do |item|
|
123
|
+
yield item
|
124
|
+
end
|
125
|
+
else
|
126
|
+
yield items
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def _add(args, type, options={})
|
131
|
+
check_write_access
|
132
|
+
ni = NodeInvalidator.new(self)
|
133
|
+
options[:observed_at] ? dt = options[:observed_at] : dt = Time.now
|
134
|
+
_unravel(args) do |arg|
|
135
|
+
case type
|
136
|
+
when :point
|
137
|
+
ni << point_recognizer(arg, dt, options)
|
138
|
+
when :value
|
139
|
+
ni << value_recognizer(arg, dt, options)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
ni.invalidate!
|
143
|
+
end
|
144
|
+
|
145
|
+
# @param args [Object, Array of Objects]
|
146
|
+
# @param options [Hash] You can also pass in :meta and :timestamp
|
147
|
+
# Add an item or an array of items (one at a time) to the datastore.
|
148
|
+
def add_values(args,options={})
|
149
|
+
_add(args,:value, options)
|
150
|
+
end
|
151
|
+
|
152
|
+
# @param args [Object, Array of Objects]
|
153
|
+
# @param options [Hash] You can also pass in :meta and :timestamp
|
154
|
+
# Add an item or an array of items (one at a time) to the datastore.
|
155
|
+
def add_points(args,options={})
|
156
|
+
_add(args,:point,options)
|
157
|
+
end
|
158
|
+
|
159
|
+
def _insert_point(val,ts,options={})
|
160
|
+
self.points.create(_prepare_insert(val, ts, options))
|
161
|
+
ts
|
162
|
+
end
|
163
|
+
|
164
|
+
def _insert_point_hash(item,ts,options)
|
165
|
+
if item['v'] && item['v'].is_a?(Integer)
|
166
|
+
if item['t']
|
167
|
+
_insert_point item['v'],item['t'], options
|
168
|
+
else
|
169
|
+
_insert_point item['v'],ts, options
|
170
|
+
end
|
171
|
+
else
|
172
|
+
raise ArgumentError, "Hash must have 'v' key set to proper type.. #{item.inspect}"
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
def _insert_point_array(item,options)
|
177
|
+
if item.length == 2 && item[0].is_a?(Integer)
|
178
|
+
_insert_point item[0],item[1], options
|
179
|
+
else
|
180
|
+
raise ArgumentError, "Array Items must be in [value,timestamp] format. #{item.inspect}"
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
def _insert_point_string(item,ts,options)
|
185
|
+
if item.length > 0 && item =~ /\A[-+]?[0-9]+/
|
186
|
+
_insert_point item.to_i,ts, options
|
187
|
+
else
|
188
|
+
raise ArgumentError, "String Items must represent Integer. #{item.inspect}"
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
def point_recognizer(item, dt, options={})
|
193
|
+
case
|
194
|
+
when item.is_a?(Integer)
|
195
|
+
_insert_point item,dt, options
|
196
|
+
when item.is_a?(Array)
|
197
|
+
_insert_point_array(item, options)
|
198
|
+
when item.is_a?(Hash)
|
199
|
+
_insert_point_hash(item,dt,options)
|
200
|
+
when item.is_a?(String)
|
201
|
+
_insert_point_string(item,dt,options)
|
202
|
+
else
|
203
|
+
raise ArgumentError, "Can't recognize format of data item. #{item.inspect}"
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
def check_write_access
|
208
|
+
unless [:full,:admin,:write].include? @access
|
209
|
+
raise SecurityError,"You do not have write access to this node."
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
def check_read_access
|
214
|
+
unless [:full,:admin,:read].include? @access
|
215
|
+
raise SecurityError,"You do not have read access to this node."
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
def check_admin_access
|
220
|
+
unless [:full,:admin,:read].include? @access
|
221
|
+
raise SecurityError,"You do not have admin access to this node."
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
def increment(value=1, options={})
|
226
|
+
check_write_access
|
227
|
+
if value.is_a?(Integer)
|
228
|
+
last = self.points.last
|
229
|
+
add_points last.value + value,options
|
230
|
+
else
|
231
|
+
raise ArgumentError, "Value must be an Integer"
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
def decrement(value=1, options={})
|
236
|
+
check_write_access
|
237
|
+
if value.is_a?(Integer)
|
238
|
+
increment (-1) * value, options
|
239
|
+
else
|
240
|
+
raise ArgumentError, "Value must be an Integer"
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
def _range(dt_from, dt_to, coll, options={})
|
245
|
+
check_read_access
|
246
|
+
ret = coll.where("observed_at >= :dt_from AND observed_at <= :dt_to",{dt_from:dt_from.to_f,dt_to:dt_to.to_f}, limit:1000,order:"observed_at asc, id asc")
|
247
|
+
end
|
248
|
+
|
249
|
+
# Returns items whose observed_at times fit within from a range.
|
250
|
+
# @param dt_from [Time::Time] The start time.
|
251
|
+
# @param dt_to [Time::Time] The end time.
|
252
|
+
# @return [Array of Objects]
|
253
|
+
def values_range(dt_from, dt_to,options={})
|
254
|
+
_range(dt_from, dt_to, values, options)
|
255
|
+
end
|
256
|
+
|
257
|
+
# Returns items whose observed_at times fit within from a range ending now.
|
258
|
+
# @param dt_from [Time::Time] The start time.
|
259
|
+
# @return [Array of Objects]
|
260
|
+
def values_since(dt_from)
|
261
|
+
self.values_range(dt_from,Time.now)
|
262
|
+
end
|
263
|
+
|
264
|
+
# Returns items whose observed_at times fit within from a range.
|
265
|
+
# @param dt_from [Time::Time] The start time.
|
266
|
+
# @param dt_to [Time::Time] The end time.
|
267
|
+
# @return [Array of Objects]
|
268
|
+
def points_range(dt_from, dt_to,options={})
|
269
|
+
_range(dt_from, dt_to, points, options)
|
270
|
+
end
|
271
|
+
|
272
|
+
# Returns items whose observed_at times fit within from a range ending now.
|
273
|
+
# @param dt_from [Time::Time] The start time.
|
274
|
+
# @return [Array of Objects]
|
275
|
+
def points_since(dt_from)
|
276
|
+
self.points_range(dt_from,Time.now)
|
277
|
+
end
|
278
|
+
|
279
|
+
# Sets public read flag for this address
|
280
|
+
# @param value [Boolean] true if public reading is allowed, false if it is not.
|
281
|
+
def set_public_read(value)
|
282
|
+
value = value ? true : false
|
283
|
+
self.update_attributes :public_read => value
|
284
|
+
#save
|
285
|
+
end
|
286
|
+
|
287
|
+
# Sets public write flag for this address
|
288
|
+
# @param value [Boolean] true if public writing is allowed, false if it is not.
|
289
|
+
def set_public_write(value)
|
290
|
+
value = value ? true : false
|
291
|
+
self.update_attributes :public_write => value
|
292
|
+
#save
|
293
|
+
end
|
294
|
+
|
295
|
+
# Sets permissions flag for this address, for a specific agent. The existing Chawk::Relationship will be destroyed and
|
296
|
+
# a new one created as specified. Write access is not yet checked.
|
297
|
+
# @param agent [Chawk::Agent] the agent to give permission.
|
298
|
+
# @param read [Boolean] true/false can the agent read this address.
|
299
|
+
# @param write [Boolean] true/false can the agent write this address. (Read acces is required to write.)
|
300
|
+
# @param admin [Boolean] does the agent have ownership/adnim rights for this address. (Read and write are granted if admin is as well.)
|
301
|
+
def set_permissions(agent,read=false,write=false,admin=false)
|
302
|
+
rels = relations.where(:agent_id => agent.id)
|
303
|
+
rels.delete_all()
|
304
|
+
rels = relations.where(:agent_id => agent.id)
|
305
|
+
if read || write || admin
|
306
|
+
vals = {agent:agent,read:(read ? true : false),write:(write ? true : false),admin:(admin ? true : false)}
|
307
|
+
relations.create(vals)
|
308
|
+
end
|
309
|
+
nil
|
310
|
+
end
|
311
|
+
end
|
312
|
+
end
|
313
|
+
end
|
data/lib/range.rb
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
module Chawk
|
3
|
+
module Models
|
4
|
+
class Range < ActiveRecord::Base
|
5
|
+
self.table_name_prefix = "chawk_"
|
6
|
+
validates :start_ts, :stop_ts, :beats, :parent_node, presence:true
|
7
|
+
validates :subkey, :data_node, absence: true
|
8
|
+
validate :order_is_correct
|
9
|
+
|
10
|
+
before_create :build_subnode
|
11
|
+
|
12
|
+
after_create :build_dataset
|
13
|
+
after_find :grant_node_access
|
14
|
+
|
15
|
+
belongs_to :parent_node, class_name:"Chawk::Models::Node"
|
16
|
+
belongs_to :data_node, class_name:"Chawk::Models::Node"
|
17
|
+
|
18
|
+
def reload
|
19
|
+
super
|
20
|
+
grant_node_access
|
21
|
+
end
|
22
|
+
|
23
|
+
def grant_node_access
|
24
|
+
# TODO: vet this very carefully.
|
25
|
+
# The only way to get here should be through an authorized source.
|
26
|
+
self.data_node.access=:full
|
27
|
+
end
|
28
|
+
|
29
|
+
def build_subnode
|
30
|
+
if subkey.to_s == ''
|
31
|
+
self.subkey = parent_node.key + "/" + SecureRandom.hex.to_s
|
32
|
+
end
|
33
|
+
self.data_node = Chawk::Models::Node.create(key:subkey)
|
34
|
+
grant_node_access
|
35
|
+
end
|
36
|
+
|
37
|
+
def order_is_correct
|
38
|
+
if self.start_ts >= self.stop_ts
|
39
|
+
errors.add(:stop_ts, "must be after start_ts.")
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def build_dataset
|
44
|
+
populate!
|
45
|
+
end
|
46
|
+
|
47
|
+
def point_from_parent_point(now)
|
48
|
+
point = parent_node.points.where("observed_at <= :dt_to",{dt_to:now}).order(observed_at: :desc, id: :desc).first
|
49
|
+
if point
|
50
|
+
value = point.value
|
51
|
+
else
|
52
|
+
value = default || 0
|
53
|
+
end
|
54
|
+
data_node.points.create(observed_at:now, recorded_at:Time.now, value:value)
|
55
|
+
end
|
56
|
+
|
57
|
+
def populate!
|
58
|
+
# TODO: Accounting hook
|
59
|
+
# TODO: perform in callback (celluloid?)
|
60
|
+
self.data_node.points.destroy_all
|
61
|
+
step = 0.25 * self.beats
|
62
|
+
now = (self.start_ts*4).round/4.to_f
|
63
|
+
while now < self.stop_ts
|
64
|
+
point = point_from_parent_point now
|
65
|
+
now += step
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
data/test/lib/addr_test.rb
CHANGED
@@ -5,58 +5,88 @@ describe Chawk do
|
|
5
5
|
before do
|
6
6
|
Chawk.clear_all_data!
|
7
7
|
@agent = Chawk::Models::Agent.first || Chawk::Models::Agent.create(:name=>"Test User")
|
8
|
-
@
|
8
|
+
@node = Chawk.node(@agent,'a:b')
|
9
9
|
end
|
10
10
|
|
11
11
|
it "has a good agent" do
|
12
|
-
lambda {
|
13
|
-
lambda {
|
14
|
-
lambda {
|
12
|
+
lambda {Chawk.node(nil,'a:b')}.must_raise(ArgumentError)
|
13
|
+
lambda {Chawk.node(Object.new,'a:b')}.must_raise(ArgumentError)
|
14
|
+
lambda {Chawk.node(Chawk::Models::Agent,'a:b')}.must_raise(ArgumentError)
|
15
|
+
node = Chawk.node(@agent,'a:b').key.must_equal("a:b")
|
16
|
+
agent2 = Chawk::Models::Agent.first || Chawk::Models::Agent.create(:name=>"Test User")
|
17
|
+
node = Chawk.node(agent2,'a:b').key.must_equal("a:b")
|
18
|
+
agent3 = Chawk::Models::Agent.create(:name=>"Test Failer")
|
19
|
+
lambda{Chawk.node(agent3,'a:b').key.must_equal("a:b")}.must_raise(SecurityError)
|
15
20
|
end
|
16
21
|
|
17
22
|
it "has key" do
|
18
|
-
@
|
23
|
+
@node.must_respond_to(:key)
|
19
24
|
end
|
20
25
|
|
21
26
|
it "key is valid" do
|
22
|
-
@
|
23
|
-
Chawk.
|
24
|
-
Chawk.
|
27
|
+
@node.key.must_equal("a:b")
|
28
|
+
Chawk.node(@agent,'a:b').key.must_equal("a:b")
|
29
|
+
Chawk.node(@agent,'0:x:z').key.must_equal("0:x:z")
|
25
30
|
path = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz012345689_:$!@*[]~()"
|
26
|
-
Chawk.
|
31
|
+
Chawk.node(@agent,path).key.must_equal(path)
|
27
32
|
end
|
28
33
|
|
29
34
|
it "rejects invalid paths" do
|
30
|
-
lambda {Chawk.
|
31
|
-
lambda {Chawk.
|
32
|
-
lambda {Chawk.
|
33
|
-
lambda {Chawk.
|
34
|
-
lambda {Chawk.
|
35
|
-
lambda {Chawk.
|
36
|
-
lambda {Chawk.
|
37
|
-
lambda {Chawk.
|
38
|
-
lambda {Chawk.
|
39
|
-
lambda {Chawk.
|
35
|
+
lambda {Chawk.node(@agent,['A'])}.must_raise(ArgumentError)
|
36
|
+
lambda {Chawk.node(@agent,0)}.must_raise(ArgumentError)
|
37
|
+
lambda {Chawk.node(@agent,:a)}.must_raise(ArgumentError)
|
38
|
+
lambda {Chawk.node(@agent,Object.new)}.must_raise(ArgumentError)
|
39
|
+
lambda {Chawk.node(@agent,String.new)}.must_raise(ArgumentError)
|
40
|
+
lambda {Chawk.node(@agent,"")}.must_raise(ArgumentError)
|
41
|
+
lambda {Chawk.node(@agent,"/")}.must_raise(ArgumentError)
|
42
|
+
lambda {Chawk.node(@agent,"\\")}.must_raise(ArgumentError)
|
43
|
+
lambda {Chawk.node(@agent,"&")}.must_raise(ArgumentError)
|
44
|
+
lambda {Chawk.node(@agent,"?")}.must_raise(ArgumentError)
|
40
45
|
end
|
41
46
|
|
42
47
|
it "sets permissions" do
|
43
|
-
@
|
44
|
-
@
|
48
|
+
@node.must_respond_to(:set_public_read)
|
49
|
+
@node.must_respond_to(:set_permissions)
|
45
50
|
end
|
46
51
|
|
47
52
|
it "stops unauthorized access" do
|
48
|
-
|
49
|
-
|
50
|
-
@
|
51
|
-
|
52
|
-
@
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
@
|
57
|
-
lambda{
|
58
|
-
@
|
59
|
-
Chawk.
|
53
|
+
@node.set_permissions(@agent,true,false,false)
|
54
|
+
@node = Chawk::Models::Node.find(@node.id)
|
55
|
+
lambda{Chawk.node(@agent,'a:b')}.must_raise SecurityError
|
56
|
+
@node.set_permissions(@agent,false,false,false)
|
57
|
+
lambda{Chawk.node(@agent,'a:b')}.must_raise SecurityError
|
58
|
+
end
|
59
|
+
|
60
|
+
it "stops unauthorized reads" do
|
61
|
+
@node.set_permissions(@agent,false,false,false)
|
62
|
+
lambda{Chawk.node(@agent,'a:b', :write)}.must_raise SecurityError
|
63
|
+
@node.set_public_read true
|
64
|
+
Chawk.node(@agent,'a:b', :read).key.must_equal "a:b"
|
65
|
+
@node.set_public_read false
|
66
|
+
lambda{Chawk.node(@agent,'a:b', :read)}.must_raise SecurityError
|
67
|
+
@node.set_permissions(@agent,true,true,false)
|
68
|
+
w_node = Chawk.node(@agent,'a:b', :write)
|
69
|
+
lambda{w_node.values_range(0,0)}.must_raise SecurityError
|
70
|
+
end
|
71
|
+
|
72
|
+
it "stops unauthorized writes" do
|
73
|
+
@node.set_permissions(@agent,true,false,false)
|
74
|
+
lambda{Chawk.node(@agent,'a:b', :write)}.must_raise SecurityError
|
75
|
+
@node.set_public_write true
|
76
|
+
Chawk.node(@agent,'a:b', :write).key.must_equal "a:b"
|
77
|
+
@node.set_public_write false
|
78
|
+
lambda{Chawk.node(@agent,'a:b', :write)}.must_raise SecurityError
|
79
|
+
r_node = Chawk.node(@agent,'a:b', :read)
|
80
|
+
lambda{r_node.add_points([1,2,3,4])}.must_raise SecurityError
|
81
|
+
end
|
82
|
+
|
83
|
+
it "stops unauthorized admin" do
|
84
|
+
@node.set_permissions(@agent,false,false,false)
|
85
|
+
lambda{Chawk.node(@agent,'a:b', :admin)}.must_raise SecurityError
|
86
|
+
@node.set_permissions(@agent,false,true,true)
|
87
|
+
Chawk.node(@agent,'a:b', :admin).key.must_equal "a:b"
|
88
|
+
w_node = Chawk.node(@agent,'a:b', :write)
|
89
|
+
lambda{w_node.clear_points!}.must_raise SecurityError
|
60
90
|
end
|
61
91
|
|
62
92
|
end
|
@@ -2,7 +2,7 @@ require 'test_helper'
|
|
2
2
|
require 'json'
|
3
3
|
|
4
4
|
Jiggy = Chawk.clone
|
5
|
-
def Jiggy.find_or_create_node(agent,key) return nil end
|
5
|
+
def Jiggy.find_or_create_node(agent,key,access=:full) return nil end
|
6
6
|
|
7
7
|
describe Jiggy do
|
8
8
|
before do
|
@@ -12,7 +12,7 @@ describe Jiggy do
|
|
12
12
|
|
13
13
|
it "needs a valid node" do
|
14
14
|
|
15
|
-
lambda {@
|
15
|
+
lambda {@node = Jiggy.node(@agent,'a:b')}.must_raise(ArgumentError)
|
16
16
|
end
|
17
17
|
|
18
18
|
end
|