safety-pin 0.0.9
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/.rspec +1 -0
- data/.rvmrc +1 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +21 -0
- data/LICENSE +22 -0
- data/README.md +29 -0
- data/Rakefile +2 -0
- data/bin/safety-pin +45 -0
- data/console_loader.rb +7 -0
- data/lib/safety-pin.rb +2 -0
- data/lib/safety_pin/jcr.rb +42 -0
- data/lib/safety_pin/node.rb +389 -0
- data/lib/safety_pin/node_blueprint.rb +18 -0
- data/lib/safety_pin/query/where_condition.rb +25 -0
- data/lib/safety_pin/query.rb +70 -0
- data/lib/safety_pin/version.rb +3 -0
- data/lib/safety_pin.rb +11 -0
- data/safety-pin.gemspec +17 -0
- data/spec/jcr_spec.rb +31 -0
- data/spec/jcr_sql2_spec.rb +81 -0
- data/spec/node_blueprint_spec.rb +35 -0
- data/spec/node_spec.rb +712 -0
- data/spec/query_spec.rb +114 -0
- data/spec/spec_helper.rb +27 -0
- data/spec/where_condition_spec.rb +20 -0
- metadata +79 -0
data/spec/node_spec.rb
ADDED
@@ -0,0 +1,712 @@
|
|
1
|
+
require 'spec_helper.rb'
|
2
|
+
|
3
|
+
describe SafetyPin::Node do
|
4
|
+
describe ".find" do
|
5
|
+
context "given a node name" do
|
6
|
+
context "that exists" do
|
7
|
+
it "should return a node with a matching path" do
|
8
|
+
SafetyPin::Node.find("/content").path.should eql("/content")
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
context "that doesn't exist" do
|
13
|
+
it "should return nil" do
|
14
|
+
SafetyPin::Node.find("/foo/bar/baz").should be_nil
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
it "complains if the path isn't an absolute path" do
|
20
|
+
lambda { node = SafetyPin::Node.find("content") }.should raise_exception(ArgumentError)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe ".find_or_create" do
|
25
|
+
context "given a node path that exists" do
|
26
|
+
it "should return the node at that path" do
|
27
|
+
SafetyPin::Node.create(SafetyPin::NodeBlueprint.new(:path => "/content/foo"))
|
28
|
+
SafetyPin::Node.find_or_create("/content/foo").path.should eql "/content/foo"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context "when node doesn't exist" do
|
33
|
+
it "returns node created at path" do
|
34
|
+
SafetyPin::Node.find("/content/foo").should be_nil
|
35
|
+
SafetyPin::Node.find_or_create("/content/foo").path.should == "/content/foo"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe ".exists?" do
|
41
|
+
it "returns true if node exists at path" do
|
42
|
+
SafetyPin::Node.create(SafetyPin::NodeBlueprint.new(:path => "/content/foo"))
|
43
|
+
SafetyPin::Node.exists?("/content/foo").should be_true
|
44
|
+
end
|
45
|
+
|
46
|
+
it "returns false if node does not exist" do
|
47
|
+
SafetyPin::Node.exists?("/content/foo").should be_false
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe ".session" do
|
52
|
+
it "should return a session" do
|
53
|
+
SafetyPin::Node.session.should be_a(Java::JavaxJcr::Session)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe "#session" do
|
58
|
+
it "should return a session" do
|
59
|
+
SafetyPin::Node.find("/content").session.should be_a(Java::JavaxJcr::Session)
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should cache session in an instance variable" do
|
63
|
+
node = SafetyPin::Node.find("/content")
|
64
|
+
node.session
|
65
|
+
node.instance_eval { @session }.should be_a(Java::JavaxJcr::Session)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe "#children" do
|
70
|
+
it "should return an array of child nodes" do
|
71
|
+
SafetyPin::Node.find("/content").children.first.should be_a(SafetyPin::Node)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe "#child" do
|
76
|
+
context "given a node name" do
|
77
|
+
let(:node) { SafetyPin::Node.find("/") }
|
78
|
+
|
79
|
+
context "that exists" do
|
80
|
+
it "should return a child node with a matching name" do
|
81
|
+
node.child("content").name.should eql("content")
|
82
|
+
end
|
83
|
+
|
84
|
+
it "should return a grandchild node given a relative path" do
|
85
|
+
SafetyPin::Node.create(SafetyPin::NodeBlueprint.new(:path => "/content/foo"))
|
86
|
+
node.child("content/foo").name.should eql("foo")
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
it "should coerce non-string name to string and return child" do
|
91
|
+
node.child(:content).name.should == "content"
|
92
|
+
end
|
93
|
+
|
94
|
+
context "that doesn't exist" do
|
95
|
+
it "should return nil" do
|
96
|
+
node.child("foobarbaz").should be_nil
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
describe ".create_or_update" do
|
103
|
+
let(:node_blueprint) { SafetyPin::NodeBlueprint.new(:path => "/content/foo") }
|
104
|
+
|
105
|
+
it "calls Node.create with arg when nothing exists at path" do
|
106
|
+
SafetyPin::Node.should_receive(:create).with(node_blueprint)
|
107
|
+
SafetyPin::Node.create_or_update(node_blueprint)
|
108
|
+
end
|
109
|
+
|
110
|
+
it "calls Node.update with arg when node exists at path" do
|
111
|
+
SafetyPin::Node.create(node_blueprint)
|
112
|
+
SafetyPin::Node.should_receive(:update).with(node_blueprint)
|
113
|
+
SafetyPin::Node.create_or_update(node_blueprint)
|
114
|
+
end
|
115
|
+
|
116
|
+
it "takes a node blueprint" do
|
117
|
+
SafetyPin::Node.create_or_update(node_blueprint)
|
118
|
+
SafetyPin::Node.exists?(node_blueprint.path).should be_true
|
119
|
+
end
|
120
|
+
|
121
|
+
it "takes an array of node blueprints" do
|
122
|
+
node_blueprints = [node_blueprint, SafetyPin::NodeBlueprint.new(:path => "/content/foo/bar")]
|
123
|
+
SafetyPin::Node.create_or_update(node_blueprints)
|
124
|
+
node_blueprints.each {|node_blueprint| SafetyPin::Node.exists?(node_blueprint.path).should be_true }
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
describe "#find_or_create_child" do
|
129
|
+
let(:parent) { SafetyPin::Node.create(SafetyPin::NodeBlueprint.new(:path => "/content/foo")) }
|
130
|
+
|
131
|
+
context "an existing node path" do
|
132
|
+
it "should return the child node" do
|
133
|
+
parent.create("bar")
|
134
|
+
parent.find_or_create("bar").path.should eql "/content/foo/bar"
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
context "a non-existing node path" do
|
139
|
+
it "should create a node and return it" do
|
140
|
+
parent.find_or_create("bar").path.should eql "/content/foo/bar"
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
describe "#name" do
|
146
|
+
it "should return a string name" do
|
147
|
+
SafetyPin::Node.find("/content").name.should eql("content")
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
describe "#save" do
|
152
|
+
context "on an existing node with changes" do
|
153
|
+
before do
|
154
|
+
@node = SafetyPin::Node.create(SafetyPin::NodeBlueprint.new(:path => "/content/foo"))
|
155
|
+
@node["bar"] = "baz"
|
156
|
+
end
|
157
|
+
|
158
|
+
it "should save the changes to the JCR" do
|
159
|
+
@node.save
|
160
|
+
@node.reload
|
161
|
+
@node["bar"].should eql("baz")
|
162
|
+
end
|
163
|
+
|
164
|
+
it "should return true if the save was successful" do
|
165
|
+
save_successful = @node.save
|
166
|
+
(save_successful and not @node.changed?).should be_true
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
context "on a new node" do
|
171
|
+
it "should save the node" do
|
172
|
+
node = SafetyPin::Node.build(SafetyPin::NodeBlueprint.new(:path => "/content/foo"))
|
173
|
+
node.save.should be_true
|
174
|
+
end
|
175
|
+
|
176
|
+
it "should save changes in parent node" do
|
177
|
+
parent_node = SafetyPin::Node.create(SafetyPin::NodeBlueprint.new(:path => "/content/foo"))
|
178
|
+
node = SafetyPin::Node.build(SafetyPin::NodeBlueprint.new(:path => "/content/foo/bar"))
|
179
|
+
parent_node["baz"] = "qux"
|
180
|
+
parent_node.should be_changed
|
181
|
+
node.save
|
182
|
+
parent_node.should_not be_changed
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
describe "#read_attribute" do
|
188
|
+
context "on an existing node" do
|
189
|
+
let(:node) { SafetyPin::Node.create(SafetyPin::NodeBlueprint.new(:path => "/content/foo")) }
|
190
|
+
|
191
|
+
it "should return the string value of a string property" do
|
192
|
+
node["foo"] = "bar"
|
193
|
+
node.read_attribute("foo").should eql("bar")
|
194
|
+
end
|
195
|
+
|
196
|
+
it "should return the boolean value of a boolean property" do
|
197
|
+
node["foo"] = true
|
198
|
+
node.read_attribute("foo").should eql(true)
|
199
|
+
end
|
200
|
+
|
201
|
+
it "should return the double value of a double (or Ruby float) property" do
|
202
|
+
node["foo"] = 3.14
|
203
|
+
node.read_attribute("foo").should eql(3.14)
|
204
|
+
end
|
205
|
+
|
206
|
+
it "should return the long value of a long (or Ruby Fixnum) property" do
|
207
|
+
node["foo"] = 42
|
208
|
+
node.read_attribute("foo").should eql(42)
|
209
|
+
end
|
210
|
+
|
211
|
+
it "should return the time value of a date property" do
|
212
|
+
time = Time.now
|
213
|
+
node["foo"] = time
|
214
|
+
node.read_attribute("foo").to_s.should eql(time.to_s)
|
215
|
+
end
|
216
|
+
|
217
|
+
context "given a multi-value property" do
|
218
|
+
it "should return an array of values" do
|
219
|
+
node["foo"] = ["one", "two"]
|
220
|
+
node.read_attribute("foo").should eql(["one", "two"])
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
context "given a non-string name" do
|
225
|
+
it "should co-erce the name into a string and retrieve the property" do
|
226
|
+
node["foo"] = "bar"
|
227
|
+
node.read_attribute(:foo).should eql("bar")
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
it "should return the string value of a name property" do
|
233
|
+
SafetyPin::Node.find("/")["jcr:primaryType"].should eql("rep:root")
|
234
|
+
end
|
235
|
+
|
236
|
+
it "should throw an exception when accessing a non-existent (nil) property" do
|
237
|
+
lambda { SafetyPin::Node.build(SafetyPin::NodeBlueprint.new(:path => "/content/foo")).read_attribute("foo-bar-baz") }.should raise_error(SafetyPin::NilPropertyError)
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
context "#write_attribute" do
|
242
|
+
let(:node) { SafetyPin::Node.create(SafetyPin::NodeBlueprint.new(:path => "/content/foo")) }
|
243
|
+
|
244
|
+
context "given a single value" do
|
245
|
+
it "should set a string property value" do
|
246
|
+
node.write_attribute("foo", "bar")
|
247
|
+
node.save
|
248
|
+
node.reload
|
249
|
+
node["foo"].should eql("bar")
|
250
|
+
end
|
251
|
+
|
252
|
+
context "given a Time object value" do
|
253
|
+
it "should set a date property value" do
|
254
|
+
time = Time.now
|
255
|
+
node.write_attribute("foo", time)
|
256
|
+
node.save
|
257
|
+
node.reload
|
258
|
+
node["foo"].to_s.should eql(time.to_s)
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
context "given a symbol value" do
|
263
|
+
it "coerces value to string" do
|
264
|
+
node.write_attribute("foo", :bar)
|
265
|
+
node["foo"].should == "bar"
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
context "given another supported value" do
|
270
|
+
it "sets the property" do
|
271
|
+
node.write_attribute("foo", 1)
|
272
|
+
node["foo"].should == 1
|
273
|
+
node.write_attribute("foo", 1.1)
|
274
|
+
node["foo"].should == 1.1
|
275
|
+
node.write_attribute("foo", true)
|
276
|
+
node["foo"].should == true
|
277
|
+
node.write_attribute("foo", "bar")
|
278
|
+
node["foo"].should == "bar"
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
context "given an unsupported value" do
|
283
|
+
it "raises an error" do
|
284
|
+
lambda { node.write_attribute("foo", {unsupported: :value_type}) }.should raise_error(SafetyPin::PropertyTypeError)
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
context "given a non-string name" do
|
289
|
+
it "should co-erce name into string before setting property" do
|
290
|
+
node.write_attribute(:foo, "bar")
|
291
|
+
node.save
|
292
|
+
node.reload
|
293
|
+
node["foo"].should eql("bar")
|
294
|
+
end
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
context "given an array of values" do
|
299
|
+
context "of the same type" do
|
300
|
+
it "should set a multi-value string array" do
|
301
|
+
node.write_attribute("foo", ["one", "two"])
|
302
|
+
node.save
|
303
|
+
node.reload
|
304
|
+
node["foo"].should eql(["one", "two"])
|
305
|
+
end
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
context "given a null value" do
|
310
|
+
it "should remove the property" do
|
311
|
+
node["foo"] = "bar"
|
312
|
+
node.write_attribute("foo", nil)
|
313
|
+
lambda { node["foo"] }.should raise_error(SafetyPin::NilPropertyError)
|
314
|
+
end
|
315
|
+
|
316
|
+
context "given a non-existent property and a null value" do
|
317
|
+
it "should return nil" do
|
318
|
+
node.write_attribute("foo", nil).should be_nil
|
319
|
+
end
|
320
|
+
end
|
321
|
+
end
|
322
|
+
|
323
|
+
context "changing jcr:primaryType property" do
|
324
|
+
it "should raise an error" do
|
325
|
+
lambda { node.write_attribute("jcr:primaryType", "nt:folder") }.should raise_error(SafetyPin::PropertyError)
|
326
|
+
end
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
330
|
+
context "#reload" do
|
331
|
+
let(:node) { SafetyPin::Node.create(SafetyPin::NodeBlueprint.new(:path => "/content/foo")) }
|
332
|
+
|
333
|
+
it "should discard pending changes" do
|
334
|
+
node["foo"] = "bar"
|
335
|
+
node.reload
|
336
|
+
lambda { node.read_attribute("foo") }.should raise_error(SafetyPin::NilPropertyError)
|
337
|
+
end
|
338
|
+
|
339
|
+
it "should not discard changes for another node" do
|
340
|
+
node["bar"] = "baz"
|
341
|
+
another_node = SafetyPin::Node.find("/content")
|
342
|
+
another_node["bar"] = "baz"
|
343
|
+
node.reload
|
344
|
+
lambda { node["bar"] }.should raise_error(SafetyPin::NilPropertyError)
|
345
|
+
another_node["bar"].should eql("baz")
|
346
|
+
end
|
347
|
+
end
|
348
|
+
|
349
|
+
describe "#[]" do
|
350
|
+
it "should return the value of a given property name" do
|
351
|
+
node = SafetyPin::Node.create(SafetyPin::NodeBlueprint.new(:path => "/content/foo"))
|
352
|
+
node.write_attribute("bar","baz")
|
353
|
+
node.save
|
354
|
+
node["bar"].should eql("baz")
|
355
|
+
end
|
356
|
+
end
|
357
|
+
|
358
|
+
describe "#[]=" do
|
359
|
+
it "should set the value of a given property name" do
|
360
|
+
node = SafetyPin::Node.create(SafetyPin::NodeBlueprint.new(:path => "/content/foo"))
|
361
|
+
node.write_attribute("bar","baz")
|
362
|
+
node["bar"] = "qux"
|
363
|
+
node["bar"].should eql("qux")
|
364
|
+
node.destroy
|
365
|
+
end
|
366
|
+
end
|
367
|
+
|
368
|
+
context "#changed?" do
|
369
|
+
let(:node) { SafetyPin::Node.find("/content") }
|
370
|
+
|
371
|
+
it "should return false if the node does not have unsaved changes" do
|
372
|
+
node.should_not be_changed
|
373
|
+
end
|
374
|
+
|
375
|
+
it "should return true if the node has unsaved changes" do
|
376
|
+
node["foo"] = "bar"
|
377
|
+
node.should be_changed
|
378
|
+
end
|
379
|
+
end
|
380
|
+
|
381
|
+
context "#new?" do
|
382
|
+
it "should return true if node has never been saved to JCR" do
|
383
|
+
SafetyPin::Node.build(SafetyPin::NodeBlueprint.new(:path => "/content/foo")).should be_new
|
384
|
+
end
|
385
|
+
|
386
|
+
it "should return false if node has been saved to JCR" do
|
387
|
+
SafetyPin::Node.find("/content").should_not be_new
|
388
|
+
end
|
389
|
+
end
|
390
|
+
|
391
|
+
describe "#properties" do
|
392
|
+
it "should return hash of all unprotected properties" do
|
393
|
+
SafetyPin::Node.find("/").properties.should eql({"sling:target"=>"/index.html", "sling:resourceType"=>"sling:redirect"})
|
394
|
+
end
|
395
|
+
end
|
396
|
+
|
397
|
+
describe "#properties=" do
|
398
|
+
let(:node) { SafetyPin::Node.create(SafetyPin::NodeBlueprint.new(:path => "/content/foo")) }
|
399
|
+
|
400
|
+
it "should set the properties of a node" do
|
401
|
+
node.properties = {"foo" => "bar"}
|
402
|
+
node.properties.should == {"foo" => "bar"}
|
403
|
+
end
|
404
|
+
|
405
|
+
it "should set unset properties not specified in hash" do
|
406
|
+
node["foo"] = "bar"
|
407
|
+
node.properties = {"baz" => "qux"}
|
408
|
+
node.properties.should eql({"baz" => "qux"})
|
409
|
+
end
|
410
|
+
|
411
|
+
it "creates child nodes for node blueprints" do
|
412
|
+
node_blueprint = SafetyPin::NodeBlueprint.new(:path => "/this/path/gets/thrown/away",
|
413
|
+
:primary_type => "sling:OrderedFolder",
|
414
|
+
:properties => {"bar" => "baz"})
|
415
|
+
node.properties = {"foo" => node_blueprint}
|
416
|
+
node.child("foo").properties.should == {"bar" => "baz"}
|
417
|
+
node.child("foo").primary_type.should == "sling:OrderedFolder"
|
418
|
+
end
|
419
|
+
|
420
|
+
xit "updates child nodes when they already exist" do
|
421
|
+
# Create /content/foo/bar and /content/foo/bar/baz
|
422
|
+
node.create(:bar, "nt:unstructured", {"bar" => "baz"})
|
423
|
+
node.child(:bar).create(:baz)
|
424
|
+
node.child(:bar).child(:baz).path.should == "/content/foo/bar/baz"
|
425
|
+
# Update /content/foo/bar by updating /content/foo properties
|
426
|
+
node.properties = {bar: SafetyPin::NodeBlueprint.new(:path => "/this/path/gets/thrown/away", :primary_type => "sling:OrderedFolder", :properties => {"updated" => "props"})}
|
427
|
+
node.child(:bar).properties.should == {"updated" => "props"}
|
428
|
+
node.child(:bar).primary_type.should == "sling:OrderedFolder"
|
429
|
+
node.child(:bar).child(:baz).path.should == "/content/foo/bar/baz"
|
430
|
+
end
|
431
|
+
end
|
432
|
+
|
433
|
+
describe "#protected_properties" do
|
434
|
+
it "should return hash of all protected properties" do
|
435
|
+
SafetyPin::Node.find("/").protected_properties.should eql({"jcr:primaryType"=>"rep:root", "jcr:mixinTypes"=>["rep:AccessControllable", "rep:RepoAccessControllable"]})
|
436
|
+
end
|
437
|
+
end
|
438
|
+
|
439
|
+
describe "#mixin_types" do
|
440
|
+
it "should return the mixin types of a node" do
|
441
|
+
node = SafetyPin::Node.create(SafetyPin::NodeBlueprint.new(:path => "/content/foo"))
|
442
|
+
node.j_node.add_mixin("mix:created")
|
443
|
+
node.save
|
444
|
+
node.mixin_types.should eql(["mix:created"])
|
445
|
+
end
|
446
|
+
end
|
447
|
+
|
448
|
+
describe "#add_mixin" do
|
449
|
+
let(:node) { SafetyPin::Node.create(SafetyPin::NodeBlueprint.new(:path => "/content/foo")) }
|
450
|
+
|
451
|
+
it "should add a mixin type to node" do
|
452
|
+
node.add_mixin("mix:created")
|
453
|
+
node.save
|
454
|
+
node.mixin_types.should eql(["mix:created"])
|
455
|
+
end
|
456
|
+
|
457
|
+
it "should require a save before the mixin addition is detected" do
|
458
|
+
node.add_mixin("mix:created")
|
459
|
+
node.mixin_types.should eql([])
|
460
|
+
end
|
461
|
+
end
|
462
|
+
|
463
|
+
describe "#remove_mixin" do
|
464
|
+
let(:node) do
|
465
|
+
node = SafetyPin::Node.create(SafetyPin::NodeBlueprint.new(:path => "/content/foo"))
|
466
|
+
node.add_mixin("mix:created")
|
467
|
+
node.save
|
468
|
+
node
|
469
|
+
end
|
470
|
+
|
471
|
+
it "should remove a mixin type from a node" do
|
472
|
+
node.mixin_types.should eql(["mix:created"])
|
473
|
+
node.remove_mixin("mix:created")
|
474
|
+
node.save
|
475
|
+
node.mixin_types.should eql([])
|
476
|
+
end
|
477
|
+
|
478
|
+
it "should require a save before the mixin removal is detected" do
|
479
|
+
node.remove_mixin("mix:created")
|
480
|
+
node.mixin_types.should eql(["mix:created"])
|
481
|
+
node.reload
|
482
|
+
end
|
483
|
+
end
|
484
|
+
|
485
|
+
describe ".update" do
|
486
|
+
let(:node) do
|
487
|
+
node = SafetyPin::Node.create(SafetyPin::NodeBlueprint.new(:path => "/content/foo"))
|
488
|
+
node.save
|
489
|
+
node
|
490
|
+
end
|
491
|
+
|
492
|
+
it "updates a nodes properties" do
|
493
|
+
SafetyPin::Node.update(SafetyPin::NodeBlueprint.new(:path => node.path, :properties => {"foo" => "barbazbuzzzzz"}))
|
494
|
+
SafetyPin::Node.find(node.path).properties.should == {"foo" => "barbazbuzzzzz"}
|
495
|
+
end
|
496
|
+
|
497
|
+
it "preserves node children" do
|
498
|
+
node.create(:bar)
|
499
|
+
SafetyPin::Node.update(SafetyPin::NodeBlueprint.new(:path => node.path, :primary_type => "sling:OrderedFolder", :properties => {"foo" => "bar"}))
|
500
|
+
SafetyPin::Node.exists?("/content/foo/bar").should be_true
|
501
|
+
end
|
502
|
+
|
503
|
+
it "modifies the primary type" do
|
504
|
+
SafetyPin::Node.update(SafetyPin::NodeBlueprint.new(:path => node.path, :primary_type => "sling:OrderedFolder"))
|
505
|
+
SafetyPin::Node.find(node.path).primary_type.should == "sling:OrderedFolder"
|
506
|
+
end
|
507
|
+
end
|
508
|
+
|
509
|
+
describe "#primary_type=" do
|
510
|
+
let(:node) do
|
511
|
+
node = SafetyPin::Node.create(SafetyPin::NodeBlueprint.new(:path => "/content/foo"))
|
512
|
+
node.save
|
513
|
+
node
|
514
|
+
end
|
515
|
+
|
516
|
+
it "sets the primary type" do
|
517
|
+
node.primary_type.should == "nt:unstructured"
|
518
|
+
node.primary_type = "sling:OrderedFolder"
|
519
|
+
node.save
|
520
|
+
SafetyPin::Node.find(node.path).primary_type.should == "sling:OrderedFolder"
|
521
|
+
end
|
522
|
+
end
|
523
|
+
|
524
|
+
describe ".build" do
|
525
|
+
it "returns an unsaved node at path of a specified type with properties set" do
|
526
|
+
node = SafetyPin::Node.build(SafetyPin::NodeBlueprint.new(:path => "/content/foo", :primary_type => "sling:OrderedFolder", :properties => {"foo" => "bar"}))
|
527
|
+
node.should be_new
|
528
|
+
node.primary_type.should == "sling:OrderedFolder"
|
529
|
+
node.properties.should == {"foo" => "bar"}
|
530
|
+
end
|
531
|
+
|
532
|
+
it "complains when the nodes already exists" do
|
533
|
+
node_blueprint = SafetyPin::NodeBlueprint.new(:path => "/content/foo")
|
534
|
+
SafetyPin::Node.create(node_blueprint)
|
535
|
+
lambda { SafetyPin::Node.build(node_blueprint) }.should raise_error(SafetyPin::NodeError)
|
536
|
+
end
|
537
|
+
|
538
|
+
it "complains when given a path with missing parents" do
|
539
|
+
lambda { SafetyPin::Node.build(SafetyPin::NodeBlueprint.new(:path => "/content/foo/bar/baz/doesnt/exist")) }.should raise_error(SafetyPin::NodeError)
|
540
|
+
end
|
541
|
+
|
542
|
+
it "complains when given a relative path" do
|
543
|
+
lambda { SafetyPin::Node.build(SafetyPin::NodeBlueprint.new(:path => "foo/not/absolute")) }.should raise_error(SafetyPin::NodeError)
|
544
|
+
end
|
545
|
+
|
546
|
+
it "complains when given nil" do
|
547
|
+
lambda { SafetyPin::Node.build(nil) }.should raise_error(SafetyPin::NodeError)
|
548
|
+
end
|
549
|
+
end
|
550
|
+
|
551
|
+
describe ".create" do
|
552
|
+
it "creates a node" do
|
553
|
+
node = SafetyPin::Node.create(SafetyPin::NodeBlueprint.new(:path => "/content/foo"))
|
554
|
+
node.should be_a(SafetyPin::Node)
|
555
|
+
end
|
556
|
+
|
557
|
+
it "creates a node of a specific type" do
|
558
|
+
SafetyPin::Node.create(SafetyPin::NodeBlueprint.new(:path => "/content/foo", :primary_type => "sling:OrderedFolder"))
|
559
|
+
SafetyPin::Node.find("/content/foo").primary_type.should == "sling:OrderedFolder"
|
560
|
+
end
|
561
|
+
end
|
562
|
+
|
563
|
+
describe ".create_parents" do
|
564
|
+
it "creates parent nodes if they do not exist" do
|
565
|
+
SafetyPin::Node.create_parents("/content/foo/bar/baz")
|
566
|
+
SafetyPin::Node.create(SafetyPin::NodeBlueprint.new(:path => "/content/foo/bar/baz")).should_not be_nil
|
567
|
+
end
|
568
|
+
end
|
569
|
+
|
570
|
+
context "#value_factory" do
|
571
|
+
it "should return a value factory instance" do
|
572
|
+
SafetyPin::Node.find("/content").value_factory.should be_a(Java::JavaxJcr::ValueFactory)
|
573
|
+
end
|
574
|
+
end
|
575
|
+
|
576
|
+
describe "#property_is_multi_valued" do
|
577
|
+
it "should return true if property is multi-valued" do
|
578
|
+
node = SafetyPin::Node.create(SafetyPin::NodeBlueprint.new(:path => "/content/foo"))
|
579
|
+
node["bar"] = ["baz", "qux"]
|
580
|
+
node.save
|
581
|
+
property = node.j_node.get_property("bar")
|
582
|
+
node.property_is_multi_valued?(property).should be_true
|
583
|
+
end
|
584
|
+
|
585
|
+
it "should return false if property is not multi-valued" do
|
586
|
+
node = SafetyPin::Node.create(SafetyPin::NodeBlueprint.new(:path => "/content/foo"))
|
587
|
+
node["bar"] = "baz"
|
588
|
+
node.save
|
589
|
+
property = node.j_node.get_property("bar")
|
590
|
+
node.property_is_multi_valued?(property).should be_false
|
591
|
+
end
|
592
|
+
end
|
593
|
+
|
594
|
+
describe "#destroy" do
|
595
|
+
it "should remove node from JCR" do
|
596
|
+
path = "/content/foo"
|
597
|
+
node = SafetyPin::Node.create(SafetyPin::NodeBlueprint.new(:path => path))
|
598
|
+
node.destroy
|
599
|
+
SafetyPin::Node.find(path).should be_nil
|
600
|
+
end
|
601
|
+
|
602
|
+
it "should save changes in parent node" do
|
603
|
+
parent_node = SafetyPin::Node.create(SafetyPin::NodeBlueprint.new(:path => "/content/foo"))
|
604
|
+
node = SafetyPin::Node.create(SafetyPin::NodeBlueprint.new(:path => "/content/foo/bar"))
|
605
|
+
parent_node["baz"] = "qux"
|
606
|
+
parent_node.should be_changed
|
607
|
+
node.destroy
|
608
|
+
parent_node.should_not be_changed
|
609
|
+
end
|
610
|
+
|
611
|
+
context "when it fails" do
|
612
|
+
it "should raise an error" do
|
613
|
+
node = SafetyPin::Node.create(SafetyPin::NodeBlueprint.new(:path => "/content/foo"))
|
614
|
+
node.add_mixin("mix:created")
|
615
|
+
node.save
|
616
|
+
node.remove_mixin("mix:created") # make node unremoveable
|
617
|
+
lambda { node.destroy }.should raise_error(SafetyPin::NodeError)
|
618
|
+
end
|
619
|
+
end
|
620
|
+
end
|
621
|
+
|
622
|
+
describe "#primary_type" do
|
623
|
+
let(:node) { SafetyPin::Node.create(SafetyPin::NodeBlueprint.new(:path => "/content/foo")) }
|
624
|
+
|
625
|
+
it "should return the primary type of the node" do
|
626
|
+
node.primary_type.should eql("nt:unstructured")
|
627
|
+
end
|
628
|
+
end
|
629
|
+
|
630
|
+
describe "#build" do
|
631
|
+
let(:node) { SafetyPin::Node.create(SafetyPin::NodeBlueprint.new(:path => "/content/foo")) }
|
632
|
+
|
633
|
+
it "should create a child node with a given name" do
|
634
|
+
node.build("bar").path.should == "/content/foo/bar"
|
635
|
+
end
|
636
|
+
|
637
|
+
it "should create a child node with a given name and node type" do
|
638
|
+
child_node = node.build("bar", SafetyPin::NodeBlueprint.new(:primary_type => "nt:folder", :path => :no_path))
|
639
|
+
child_node.should be_a(SafetyPin::Node)
|
640
|
+
child_node.primary_type.should == "nt:folder"
|
641
|
+
end
|
642
|
+
|
643
|
+
it "should create a child node with a name, node type, and properties" do
|
644
|
+
node.build("bar", SafetyPin::NodeBlueprint.new(:path => :no_path, :properties => {foo: "bar"})).properties.should == {"foo" => "bar"}
|
645
|
+
end
|
646
|
+
end
|
647
|
+
|
648
|
+
describe "#create" do
|
649
|
+
let(:node) { SafetyPin::Node.create(SafetyPin::NodeBlueprint.new(:path => "/content/foo")) }
|
650
|
+
|
651
|
+
it "should create a child node with a given name" do
|
652
|
+
child_node = node.create("bar")
|
653
|
+
child_node.path.should == "/content/foo/bar"
|
654
|
+
child_node.should_not
|
655
|
+
end
|
656
|
+
|
657
|
+
it "should create a child node with a given name and node type" do
|
658
|
+
child_node = node.create("bar", SafetyPin::NodeBlueprint.new(:path => :no_path, :primary_type => "nt:folder"))
|
659
|
+
child_node.should_not be_new
|
660
|
+
child_node.primary_type.should eql("nt:folder")
|
661
|
+
end
|
662
|
+
|
663
|
+
it "should create a child node with a name, node type, and properties" do
|
664
|
+
child_node = node.create("bar", SafetyPin::NodeBlueprint.new(:path => :no_path, :properties => {foo: "bar"}))
|
665
|
+
child_node.should_not be_new
|
666
|
+
child_node.properties.should == {"foo" => "bar"}
|
667
|
+
end
|
668
|
+
end
|
669
|
+
|
670
|
+
describe "#==" do
|
671
|
+
it "finds two nodes with the same path to be the same" do
|
672
|
+
node1 = SafetyPin::Node.new(double(:j_node))
|
673
|
+
node1.should_receive(:path).and_return("/content/foo")
|
674
|
+
node2 = SafetyPin::Node.new(double(:j_node))
|
675
|
+
node2.should_receive(:path).and_return("/content/foo")
|
676
|
+
|
677
|
+
node1.should == node2
|
678
|
+
end
|
679
|
+
|
680
|
+
it "finds two nodes with different paths to be different" do
|
681
|
+
node1 = SafetyPin::Node.new(double(:j_node))
|
682
|
+
node1.should_receive(:path).and_return("/content/foo")
|
683
|
+
node2 = SafetyPin::Node.new(double(:j_node))
|
684
|
+
node2.should_receive(:path).and_return("/content/foo/bar")
|
685
|
+
|
686
|
+
node1.should_not == node2
|
687
|
+
end
|
688
|
+
|
689
|
+
it "returns false when passed an object that doesn't response to path" do
|
690
|
+
node1 = SafetyPin::Node.new(double(:j_node))
|
691
|
+
node1.stub(:path => "foo")
|
692
|
+
node1.should_not == Object.new
|
693
|
+
end
|
694
|
+
|
695
|
+
it "returns false when passed a nil object" do
|
696
|
+
node1 = SafetyPin::Node.new(double(:j_node))
|
697
|
+
node1.should_not == nil
|
698
|
+
end
|
699
|
+
end
|
700
|
+
|
701
|
+
describe "#parent", :focus => true do
|
702
|
+
let(:node) { SafetyPin::Node.create(SafetyPin::NodeBlueprint.new(:path => "/content/foo")) }
|
703
|
+
|
704
|
+
it "returns the parent node" do
|
705
|
+
node.parent.should == SafetyPin::Node.find("/content")
|
706
|
+
end
|
707
|
+
|
708
|
+
it "raises an error when called on the root node" do
|
709
|
+
expect { SafetyPin::Node.find("/").parent }.to raise_error(SafetyPin::NodeError)
|
710
|
+
end
|
711
|
+
end
|
712
|
+
end
|