active-fedora 1.1.13 → 1.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.
@@ -19,10 +19,16 @@ class FooHistory < ActiveFedora::Base
19
19
  end
20
20
  end
21
21
 
22
+ @@last_pid = 0
23
+
22
24
  describe ActiveFedora::Base do
25
+
26
+ def increment_pid
27
+ @@last_pid += 1
28
+ end
23
29
 
24
30
  before(:each) do
25
- Fedora::Repository.instance.stubs(:nextid).returns("_nextid_")
31
+ Fedora::Repository.instance.stubs(:nextid).returns(increment_pid)
26
32
  @test_object = ActiveFedora::Base.new
27
33
  @test_object.new_object = true
28
34
  end
@@ -141,6 +147,7 @@ describe ActiveFedora::Base do
141
147
  mock_relationship = mock("relationship")
142
148
  mock_rels_ext = mock("rels-ext", :add_relationship)
143
149
  mock_rels_ext.expects(:dirty=).with(true)
150
+ @test_object.expects(:relationship_exists?).returns(false).once()
144
151
  @test_object.expects(:rels_ext).returns(mock_rels_ext).times(2)
145
152
  @test_object.add_relationship("predicate", "object")
146
153
  end
@@ -149,6 +156,7 @@ describe ActiveFedora::Base do
149
156
  mock_ds = mock("Rels-Ext")
150
157
  mock_ds.expects(:add_relationship).times(2)
151
158
  mock_ds.expects(:dirty=).with(true).times(2)
159
+ @test_object.expects(:relationship_exists?).returns(false).times(2)
152
160
  @test_object.datastreams["RELS-EXT"] = mock_ds
153
161
  test_relationships = [ActiveFedora::Relationship.new(:subject => :self, :predicate => :is_member_of, :object => "info:fedora/demo:5"),
154
162
  ActiveFedora::Relationship.new(:subject => :self, :predicate => :is_member_of, :object => "info:fedora/demo:10")]
@@ -156,7 +164,42 @@ describe ActiveFedora::Base do
156
164
  @test_object.add_relationship(rel.predicate, rel.object)
157
165
  end
158
166
  end
159
-
167
+
168
+ it 'should add a relationship to an object only if it does not exist already' do
169
+ Fedora::Repository.instance.stubs(:nextid).returns(increment_pid)
170
+ @test_object3 = ActiveFedora::Base.new
171
+ @test_object.add_relationship(:has_part,@test_object3)
172
+ r = ActiveFedora::Relationship.new(:subject=>:self, :predicate=>:dummy, :object=>@test_object3)
173
+ @test_object.relationships.should == {:self=>{:has_part=>[r.object]}}
174
+ #try adding again and make sure not there twice
175
+ @test_object.add_relationship(:has_part,@test_object3)
176
+ @test_object.relationships.should == {:self=>{:has_part=>[r.object]}}
177
+ end
178
+ end
179
+
180
+ it 'should provide #remove_relationship' do
181
+ @test_object.should respond_to(:remove_relationship)
182
+ end
183
+
184
+ describe '#remove_relationship' do
185
+ it 'should remove a relationship from the relationships hash' do
186
+ Fedora::Repository.instance.stubs(:nextid).returns(increment_pid)
187
+ @test_object3 = ActiveFedora::Base.new
188
+ Fedora::Repository.instance.stubs(:nextid).returns(increment_pid)
189
+ @test_object4 = ActiveFedora::Base.new
190
+ @test_object.add_relationship(:has_part,@test_object3)
191
+ @test_object.add_relationship(:has_part,@test_object4)
192
+ r = ActiveFedora::Relationship.new(:subject=>:self, :predicate=>:dummy, :object=>@test_object3)
193
+ r2 = ActiveFedora::Relationship.new(:subject=>:self, :predicate=>:dummy, :object=>@test_object4)
194
+ #check both are there
195
+ @test_object.relationships.should == {:self=>{:has_part=>[r.object,r2.object]}}
196
+ @test_object.remove_relationship(:has_part,@test_object3)
197
+ #check only one item removed
198
+ @test_object.relationships.should == {:self=>{:has_part=>[r2.object]}}
199
+ @test_object.remove_relationship(:has_part,@test_object4)
200
+ #check last item removed and predicate removed since now emtpy
201
+ @test_object.relationships.should == {:self=>{}}
202
+ end
160
203
  end
161
204
 
162
205
  it 'should provide #relationships' do
@@ -279,7 +322,7 @@ describe ActiveFedora::Base do
279
322
  solr_doc = @test_object.to_solr
280
323
  solr_doc[:system_create_dt].should eql("cDate")
281
324
  solr_doc[:system_modified_dt].should eql("mDate")
282
- solr_doc[:id].should eql(@test_object.pid)
325
+ solr_doc[:id].should eql("#{@test_object.pid}")
283
326
  end
284
327
 
285
328
  it "should add self.class as the :active_fedora_model" do
@@ -319,7 +362,7 @@ describe ActiveFedora::Base do
319
362
  solr_doc = @test_object.to_solr
320
363
  solr_doc[:system_create_dt].should eql("cDate")
321
364
  solr_doc[:system_modified_dt].should eql("mDate")
322
- solr_doc[:id].should eql(@test_object.pid)
365
+ solr_doc[:id].should eql("#{@test_object.pid}")
323
366
  end
324
367
 
325
368
  it "should omit base metadata and RELS-EXT if :model_only==true" do
@@ -345,7 +388,7 @@ describe ActiveFedora::Base do
345
388
  solr_doc = @test_object.to_solr
346
389
  solr_doc[:system_create_dt].should eql(cdate)
347
390
  solr_doc[:system_modified_dt].should eql(mdate)
348
- solr_doc[:id].should eql(@test_object.pid)
391
+ solr_doc[:id].should eql("#{@test_object.pid}")
349
392
  solr_doc[:active_fedora_model_s].should eql(@test_object.class.inspect)
350
393
 
351
394
  ActiveFedora::SolrService.load_mappings(File.join(File.dirname(__FILE__), "..", "..", "config", "solr_mappings_af_0.1.yml"))
@@ -355,7 +398,7 @@ describe ActiveFedora::Base do
355
398
  end
356
399
  solr_doc[:system_create_date].should eql(cdate)
357
400
  solr_doc[:system_modified_date].should eql(mdate)
358
- solr_doc[:id].should eql(@test_object.pid)
401
+ solr_doc[:id].should eql("#{@test_object.pid}")
359
402
  solr_doc[:active_fedora_model_field].should eql(@test_object.class.inspect)
360
403
  end
361
404
 
@@ -557,6 +600,54 @@ describe ActiveFedora::Base do
557
600
  FooHistory.solr_search("pid: foobar", {:ding=>:dang}).should == {:baz=>:bif}
558
601
  end
559
602
 
603
+ it 'should provide #named_relationships' do
604
+ @test_object.should respond_to(:named_relationships)
605
+ end
606
+
607
+ describe '#named_relationships' do
608
+
609
+ class MockNamedRelationships < ActiveFedora::Base
610
+ has_relationship "testing", :has_part, :type=>ActiveFedora::Base
611
+ has_relationship "testing2", :has_member, :type=>ActiveFedora::Base
612
+ has_relationship "testing_inbound", :has_part, :type=>ActiveFedora::Base, :inbound=>true
613
+ end
614
+
615
+ it 'should return current named relationships' do
616
+ Fedora::Repository.instance.stubs(:nextid).returns(increment_pid)
617
+ @test_object2 = MockNamedRelationships.new
618
+ @test_object2.add_relationship(:has_model, ActiveFedora::ContentModel.pid_from_ruby_class(MockNamedRelationships))
619
+ @test_object.add_relationship(:has_model, ActiveFedora::ContentModel.pid_from_ruby_class(ActiveFedora::Base))
620
+ #should return expected named relationships
621
+ @test_object2.named_relationships
622
+ @test_object2.named_relationships.should == {:self=>{"testing"=>[],"testing2"=>[]}}
623
+ r = ActiveFedora::Relationship.new({:subject=>:self,:predicate=>:dummy,:object=>@test_object})
624
+ @test_object2.add_named_relationship("testing",@test_object)
625
+ @test_object2.named_relationships.should == {:self=>{"testing"=>[r.object],"testing2"=>[]}}
626
+ end
627
+ end
560
628
 
561
-
629
+
630
+ describe '#create_named_relationship_methods' do
631
+ class MockCreateNamedRelationshipMethodsBase < ActiveFedora::Base
632
+ register_named_relationship :self, "testing", :is_part_of, :type=>ActiveFedora::Base
633
+ create_named_relationship_methods "testing"
634
+ end
635
+
636
+ it 'should append and remove using helper methods for each outbound relationship' do
637
+ Fedora::Repository.instance.stubs(:nextid).returns(increment_pid)
638
+ @test_object2 = MockCreateNamedRelationshipMethodsBase.new
639
+ @test_object2.should respond_to(:testing_append)
640
+ @test_object2.should respond_to(:testing_remove)
641
+ #test executing each one to make sure code added is correct
642
+ r = ActiveFedora::Relationship.new({:subject=>:self,:predicate=>:has_model,:object=>ActiveFedora::ContentModel.pid_from_ruby_class(ActiveFedora::Base)})
643
+ @test_object.add_relationship(r.predicate,r.object)
644
+ @test_object2.add_relationship(r.predicate,r.object)
645
+ @test_object2.testing_append(@test_object)
646
+ #create relationship to access generate_uri method for an object
647
+ r = ActiveFedora::Relationship.new(:subject=>:self, :predicate=>:dummy, :object=>@test_object)
648
+ @test_object2.named_relationships.should == {:self=>{"testing"=>[r.object]}}
649
+ @test_object2.testing_remove(@test_object)
650
+ @test_object2.named_relationships.should == {:self=>{"testing"=>[]}}
651
+ end
652
+ end
562
653
  end
@@ -20,6 +20,10 @@ describe ActiveFedora::NokogiriDatastream do
20
20
  after(:each) do
21
21
  end
22
22
 
23
+ it "should include the Solrizer::XML::TerminologyBasedSolrizer for .to_solr support" do
24
+ ActiveFedora::NokogiriDatastream.included_modules.should include(Solrizer::XML::TerminologyBasedSolrizer)
25
+ end
26
+
23
27
  describe '#new' do
24
28
  it 'should provide #new' do
25
29
  ActiveFedora::NokogiriDatastream.should respond_to(:new)
@@ -59,7 +63,7 @@ describe ActiveFedora::NokogiriDatastream do
59
63
  # In other words, { "fubar"=>"dork" } should have the same effect as { "fubar"=>{"0"=>"dork"} }
60
64
  result = @mods_ds.update_indexed_attributes( { [{":person"=>"0"}, "role"]=>"the role" } )
61
65
  result.should == {"person_0_role"=>{"0"=>"the role"}}
62
- @mods_ds.property_values('//oxns:name[@type="personal"][1]/oxns:role').first.should == "the role"
66
+ @mods_ds.term_values('//oxns:name[@type="personal"][1]/oxns:role').first.should == "the role"
63
67
  end
64
68
  it "should do nothing if field key is a string (must be an array or symbol). Will not accept xpath queries!" do
65
69
  xml_before = @mods_ds.to_xml
@@ -119,24 +123,36 @@ describe ActiveFedora::NokogiriDatastream do
119
123
  # end
120
124
  #
121
125
  # it "should allow deleting of values and should delete values so that to_xml does not return emtpy nodes" do
122
- # att= {"fubar"=>{"-1"=>"mork", "0"=>"york", "1"=>"mangle"}}
123
- # @test_ds.update_indexed_attributes(att)
124
- # @test_ds.fubar_values.should == ['mork', 'york', 'mangle']
126
+ # att= {[{"person"=>"0"},"description"]=>{"-1"=>"mork", "0"=>"york", "1"=>"mangle"}}
127
+ # @mods_ds.update_indexed_attributes(att)
128
+ # @mods_ds.fubar_values.should == ['mork', 'york', 'mangle']
125
129
  # rexml = REXML::Document.new(@test_ds.to_xml)
126
130
  # #puts rexml.root.elements.each {|el| el.to_s}
127
131
  # #puts rexml.root.elements.to_a.inspect
128
132
  # rexml.root.elements.to_a.length.should == 3
129
- # @test_ds.update_indexed_attributes({"fubar"=>{"1"=>""}})
130
- # @test_ds.fubar_values.should == ['mork', 'mangle']
133
+ # @mods_ds.update_indexed_attributes({[{"person"=>"0"},"description"]=>{"1"=>""}})
134
+ # @mods_ds.fubar_values.should == ['mork', 'mangle']
131
135
  # rexml = REXML::Document.new(@test_ds.to_xml)
132
136
  # rexml.root.elements.to_a.length.should == 2
133
- # @test_ds.update_indexed_attributes({"fubar"=>{"0"=>:delete}})
134
- # @test_ds.fubar_values.should == ['mangle']
137
+ # @mods_ds.update_indexed_attributes({[{"person"=>"0"},"description"]=>{"0"=>:delete}})
138
+ # @mods_ds.fubar_values.should == ['mangle']
135
139
  # rexml = REXML::Document.new(@test_ds.to_xml)
136
140
  # rexml.root.elements.to_a.length.should == 1
137
- #
141
+ # end
142
+ it "should allow deleting of values and should delete values so that to_xml does not return emtpy nodes" do
143
+ att= {[{"person"=>"0"},"description"]=>{"0"=>"york", "1"=>"mangle","2"=>"mork"}}
144
+ @mods_ds.update_indexed_attributes(att)
145
+ @mods_ds.get_values([{"person"=>"0"},"description"]).should == ['york', 'mangle', 'mork']
146
+
147
+ @mods_ds.update_indexed_attributes({[{"person"=>"0"},"description"]=>{"1"=>""}})
148
+ @mods_ds.get_values([{"person"=>"0"},"description"]).should == ['york', 'mork']
149
+
150
+ @mods_ds.update_indexed_attributes({[{"person"=>"0"},"description"]=>{"0"=>:delete}})
151
+ @mods_ds.get_values([{"person"=>"0"},"description"]).should == ['mork']
152
+ end
153
+ # it "should delete values so that to_xml does not return emtpy nodes" do
138
154
  # @test_ds.fubar_values = ["val1", nil, "val2"]
139
- # @test_ds.update_indexed_attributes({"fubar"=>{"1"=>""}})
155
+ # @test_ds.update_indexed_attributes({{[{"person"=>"0"},"description"]=>{"1"=>""}})
140
156
  # @test_ds.fubar_values.should == ["val1", "val2"]
141
157
  # end
142
158
 
@@ -154,12 +170,12 @@ describe ActiveFedora::NokogiriDatastream do
154
170
  end
155
171
 
156
172
  it "should call lookup with field_name and return the text values from each resulting node" do
157
- @mods_ds.expects(:property_values).with("--my xpath--").returns(["value1", "value2"])
173
+ @mods_ds.expects(:term_values).with("--my xpath--").returns(["value1", "value2"])
158
174
  @mods_ds.get_values("--my xpath--").should == ["value1", "value2"]
159
175
  end
160
- it "should assume that field_name that are strings are xpath queries" do
176
+ it "should assume that field_names that are strings are xpath queries" do
161
177
  ActiveFedora::NokogiriDatastream.expects(:accessor_xpath).never
162
- @mods_ds.expects(:property_values).with("--my xpath--").returns(["abstract1", "abstract2"])
178
+ @mods_ds.expects(:term_values).with("--my xpath--").returns(["abstract1", "abstract2"])
163
179
  @mods_ds.get_values("--my xpath--").should == ["abstract1", "abstract2"]
164
180
  end
165
181
  it "should assume field_names that are symbols or arrays are pointers to accessors declared in this datastreams model" do
@@ -263,136 +279,4 @@ describe ActiveFedora::NokogiriDatastream do
263
279
  end
264
280
  end
265
281
 
266
-
267
- describe ".to_solr" do
268
-
269
- after(:all) do
270
- # Revert to default mappings after running tests
271
- ActiveFedora::SolrService.load_mappings
272
- end
273
-
274
- it "should iterate through the class accessors, calling .solrize_accessor on each and passing in the solr doc" do
275
- mock_accessors = {:accessor1=>:accessor1_info, :accessor2=>:accessor2_info}
276
- ActiveFedora::NokogiriDatastream.stubs(:accessors).returns(mock_accessors)
277
- doc = Solr::Document.new
278
- mock_accessors.each_pair do |k,v|
279
- @test_ds.expects(:solrize_accessor).with(k, v, :solr_doc=>doc)
280
- end
281
- @test_ds.to_solr(doc)
282
- end
283
-
284
- it "should provide .to_solr and return a SolrDocument" do
285
- @test_ds.should respond_to(:to_solr)
286
- @test_ds.to_solr.should be_kind_of(Solr::Document)
287
- end
288
-
289
- it "should optionally allow you to provide the Solr::Document to add fields to and return that document when done" do
290
- doc = Solr::Document.new
291
- @test_ds.to_solr(doc).should equal(doc)
292
- end
293
-
294
- end
295
-
296
- describe ".solrize_accessor" do
297
- before(:all) do
298
- class AccessorizedDs < ActiveFedora::NokogiriDatastream
299
-
300
- root_property :mods, "mods", "http://www.loc.gov/mods/v3", :attributes=>["id", "version"], :schema=>"http://www.loc.gov/standards/mods/v3/mods-3-2.xsd"
301
-
302
- accessor :title_info, :relative_xpath=>'oxns:titleInfo', :children=>[
303
- {:main_title=>{:relative_xpath=>'oxns:title'}},
304
- {:language =>{:relative_xpath=>{:attribute=>"lang"} }}
305
- ]
306
- accessor :finnish_title_info, :relative_xpath=>'oxns:titleInfo[@lang="finnish"]', :children=>[
307
- {:main_title=>{:relative_xpath=>'oxns:title'}},
308
- {:language =>{:relative_xpath=>{:attribute=>"lang"} }}
309
- ]
310
- accessor :abstract
311
- accessor :topic_tag, :relative_xpath=>'oxns:subject/oxns:topic'
312
- accessor :person, :relative_xpath=>'oxns:name[@type="personal"]', :children=>[
313
- {:last_name=>{:relative_xpath=>'oxns:namePart[@type="family"]'}},
314
- {:first_name=>{:relative_xpath=>'oxns:namePart[@type="given"]'}},
315
- {:institution=>{:relative_xpath=>'oxns:affiliation'}},
316
- {:role=>{:children=>[
317
- {:text=>{:relative_xpath=>'oxns:roleTerm[@type="text"]'}},
318
- {:code=>{:relative_xpath=>'oxns:roleTerm[@type="code"]'}}
319
- ]}}
320
- ]
321
- end
322
- end
323
-
324
- before(:each) do
325
- file = fixture(File.join("mods_articles", "hydrangea_article1.xml"))
326
- @accessorized_ds = AccessorizedDs.new(:blob=>file)
327
- end
328
-
329
- it "should perform a lookup and iterate over nodes in the result set calling solrize_node then calling solrize_accessor on any of the children, adding accessor_name & node index to parents array" do
330
- mock_title_info_set = ["TI1", "TI2"]
331
- mock_main_title_set = ["main title"]
332
- mock_language_set = ["language"]
333
-
334
- solr_doc = Solr::Document.new
335
-
336
- AccessorizedDs.expects(:accessor_xpath).with( :title_info ).returns("title_info_xpath")
337
- @accessorized_ds.expects(:lookup).with( "title_info_xpath" ).returns(mock_title_info_set)
338
-
339
- mock_title_info_set.each do |tin|
340
- node_index = mock_title_info_set.index(tin)
341
- @accessorized_ds.expects(:solrize_node).with(tin, [:title_info], solr_doc)
342
-
343
- # Couldn't mock the recursive calls to solrize_accessor without preventing the initial one, so was forced to mock out the whole recursive stack.
344
- # @accessorized_ds.expects(:solrize_accessor).with(:main_title, AccessorizedDs.accessors[:title_info][:children][:main_title], :parents=>[{:title_info=>node_index}])
345
- # @accessorized_ds.expects(:solrize_accessor).with(:language, AccessorizedDs.accessors[:title_info][:children][:language], :parents=>[{:title_info=>node_index}])
346
- AccessorizedDs.expects(:accessor_xpath).with( {:title_info=>node_index}, :main_title ).returns("title_info_main_title_xpath")
347
- AccessorizedDs.expects(:accessor_xpath).with( {:title_info=>node_index}, :language ).returns("title_info_language_xpath")
348
- @accessorized_ds.expects(:lookup).with( "title_info_main_title_xpath" ).returns(mock_main_title_set)
349
- @accessorized_ds.expects(:lookup).with( "title_info_language_xpath" ).returns(mock_language_set)
350
- @accessorized_ds.expects(:solrize_node).with("main title", [{:title_info=>node_index}, :main_title], solr_doc)
351
- @accessorized_ds.expects(:solrize_node).with("language", [{:title_info=>node_index}, :language], solr_doc)
352
- end
353
-
354
- @accessorized_ds.solrize_accessor(:title_info, AccessorizedDs.accessors[:title_info], :solr_doc=>solr_doc)
355
-
356
- end
357
-
358
- it "should not call solrize_accessor once it reaches an accessor with no children accessors set" do
359
- pending "not sure how to test for this"
360
- @accessorized_ds.solrize_accessor(:text, AccessorizedDs.accessor_info( [{:person=>1}, :last_name] ), :parents=>[{:person=>1}])
361
- end
362
-
363
- it "should use values form parents array when requesting accessor_xpath and when generating solr field names" do
364
- parents_array = [{:person=>0}, {:role=>1}]
365
- AccessorizedDs.accessors[:person][:children][:role][:children][:text]
366
-
367
- # This should catch the "submitter" roleTerm from the second role node within the first person node and put it into a solr field called "person_0_role_2_text_0_t" and a solr field called "person_role_text_t"
368
- @accessorized_ds.solrize_accessor(:text, AccessorizedDs.accessor_info( *parents_array + [:text] ), :parents=>parents_array)
369
- end
370
-
371
- it "should use Solr mappings to generate field names" do
372
-
373
- solr_doc = @accessorized_ds.to_solr
374
- #should have these
375
-
376
- solr_doc[:abstract_t].should == "ABSTRACT"
377
- solr_doc[:title_info_1_language_t].should == "finnish"
378
- solr_doc[:person_1_role_0_text_t].should == "teacher"
379
- solr_doc[:finnish_title_info_language_t].should == "finnish"
380
- solr_doc[:finnish_title_info_main_title_t].should == "Artikkelin otsikko Hydrangea artiklan 1"
381
-
382
- # solr_doc[:mydate_date].should == "fake-date"
383
- #
384
- # solr_doc[:publisher_t].should be_nil
385
- # solr_doc[:coverage_t].should be_nil
386
- # solr_doc[:creation_date_dt].should be_nil
387
- # solr_doc.should == ""
388
-
389
- end
390
- end
391
-
392
- describe ".solrize_node" do
393
- it "should create a solr field containing node.text"
394
- it "should create hierarchical field entries if parents is not empty"
395
- it "should only create one node if parents is empty"
396
- end
397
-
398
282
  end
@@ -7,8 +7,20 @@ require 'xmlsimple'
7
7
  #include ActiveFedora::SemanticNode
8
8
  #include Mocha::Standalone
9
9
 
10
+ @@last_pid = 0
11
+
12
+ class SpecNode2
13
+ include ActiveFedora::SemanticNode
14
+
15
+ attr_accessor :pid
16
+ end
17
+
10
18
  describe ActiveFedora::SemanticNode do
11
19
 
20
+ def increment_pid
21
+ @@last_pid += 1
22
+ end
23
+
12
24
  before(:all) do
13
25
  @pid = "test:sample_pid"
14
26
  @uri = "info:fedora/#{@pid}"
@@ -18,10 +30,16 @@ describe ActiveFedora::SemanticNode do
18
30
  end
19
31
 
20
32
  before(:each) do
21
- class SpecNode
33
+ class SpecNode
22
34
  include ActiveFedora::SemanticNode
35
+
36
+ attr_accessor :pid
23
37
  end
38
+
24
39
  @node = SpecNode.new
40
+ @node.pid = increment_pid
41
+ @test_object = SpecNode2.new
42
+ @test_object.pid = increment_pid
25
43
  @stub_relationship = stub("mock_relationship", :subject => @pid, :predicate => "isMemberOf", :object => "demo:8", :class => ActiveFedora::Relationship)
26
44
  @test_relationship = ActiveFedora::Relationship.new(:subject => @pid, :predicate => "isMemberOf", :object => "demo:9")
27
45
  @test_relationship1 = ActiveFedora::Relationship.new(:subject => :self, :predicate => :is_member_of, :object => "demo:10")
@@ -31,6 +49,26 @@ describe ActiveFedora::SemanticNode do
31
49
 
32
50
  after(:each) do
33
51
  Object.send(:remove_const, :SpecNode)
52
+ begin
53
+ @test_object.delete
54
+ rescue
55
+ end
56
+ begin
57
+ @test_object2.delete
58
+ rescue
59
+ end
60
+ begin
61
+ @test_object3.delete
62
+ rescue
63
+ end
64
+ begin
65
+ @test_object4.delete
66
+ rescue
67
+ end
68
+ begin
69
+ @test_object5.delete
70
+ rescue
71
+ end
34
72
  end
35
73
 
36
74
  it 'should provide predicate mappings for entire Fedora Relationship Ontology' do
@@ -118,6 +156,64 @@ describe ActiveFedora::SemanticNode do
118
156
  containers_result.should include("container:B")
119
157
  end
120
158
 
159
+ class MockHasRelationship < SpecNode2
160
+ has_relationship "testing", :has_part, :type=>SpecNode2
161
+ has_relationship "testing2", :has_member, :type=>SpecNode2
162
+ has_relationship "testing_inbound", :has_part, :type=>SpecNode2, :inbound=>true
163
+ end
164
+
165
+ #can only duplicate predicates if not both inbound or not both outbound
166
+ class MockHasRelationshipDuplicatePredicate < SpecNode2
167
+ has_relationship "testing", :has_member, :type=>SpecNode2
168
+ had_exception = false
169
+ begin
170
+ has_relationship "testing2", :has_member, :type=>SpecNode2
171
+ rescue
172
+ had_exception = true
173
+ end
174
+ raise "Did not raise exception if duplicate predicate used" unless had_exception
175
+ end
176
+
177
+ #can only duplicate predicates if not both inbound or not both outbound
178
+ class MockHasRelationshipDuplicatePredicate2 < SpecNode2
179
+ has_relationship "testing", :has_member, :type=>SpecNode2, :inbound=>true
180
+ had_exception = false
181
+ begin
182
+ has_relationship "testing2", :has_member, :type=>SpecNode2, :inbound=>true
183
+ rescue
184
+ had_exception = true
185
+ end
186
+ raise "Did not raise exception if duplicate predicate used" unless had_exception
187
+ end
188
+
189
+ it 'should create relationship descriptions both inbound and outbound' do
190
+ @test_object2 = MockHasRelationship.new
191
+ @test_object2.pid = increment_pid
192
+ @test_object2.stubs(:testing_inbound).returns({})
193
+ r = ActiveFedora::Relationship.new({:subject=>:self,:predicate=>:has_model,:object=>ActiveFedora::ContentModel.pid_from_ruby_class(SpecNode2)})
194
+ @test_object2.add_relationship(r)
195
+ @test_object2.should respond_to(:testing_append)
196
+ @test_object2.should respond_to(:testing_remove)
197
+ @test_object2.should respond_to(:testing2_append)
198
+ @test_object2.should respond_to(:testing2_remove)
199
+ #make sure append/remove method not created for inbound rel
200
+ @test_object2.should_not respond_to(:testing_inbound_append)
201
+ @test_object2.should_not respond_to(:testing_inbound_remove)
202
+
203
+ @test_object2.named_relationships_desc.should ==
204
+ {:inbound=>{"testing_inbound"=>{:type=>SpecNode2,
205
+ :predicate=>:has_part,
206
+ :inbound=>true,
207
+ :singular=>nil}},
208
+ :self=>{"testing"=>{:type=>SpecNode2,
209
+ :predicate=>:has_part,
210
+ :inbound=>false,
211
+ :singular=>nil},
212
+ "testing2"=>{:type=>SpecNode2,
213
+ :predicate=>:has_member,
214
+ :inbound=>false,
215
+ :singular=>nil}}}
216
+ end
121
217
  end
122
218
 
123
219
  describe '#create_inbound_relationship_finders' do
@@ -134,6 +230,7 @@ describe ActiveFedora::SemanticNode do
134
230
  SpecNode.create_inbound_relationship_finders("containers", :is_member_of, :inbound => true)
135
231
  local_node.should respond_to(:containers_ids)
136
232
  local_node.should respond_to(:containers)
233
+ local_node.should respond_to(:containers_from_solr)
137
234
  end
138
235
 
139
236
  it "resulting finder should search against solr and use Model#load_instance to build an array of objects" do
@@ -195,7 +292,8 @@ describe ActiveFedora::SemanticNode do
195
292
  local_node.should_not respond_to(:containers)
196
293
  SpecNode.create_outbound_relationship_finders("containers", :is_member_of)
197
294
  local_node.should respond_to(:containers_ids)
198
- local_node.should respond_to(:containers)
295
+ local_node.should respond_to(:containers)
296
+ local_node.should respond_to(:containers_from_solr)
199
297
  end
200
298
 
201
299
  describe " resulting finder" do
@@ -348,4 +446,383 @@ describe ActiveFedora::SemanticNode do
348
446
  @node.should respond_to(:outbound_relationships)
349
447
  end
350
448
 
449
+
450
+ it 'should provide #unregister_triple' do
451
+ @test_object.should respond_to(:unregister_triple)
452
+ end
453
+
454
+ describe '#unregister_triple' do
455
+ it 'should remove a triple from the relationships hash' do
456
+ r = ActiveFedora::Relationship.new({:subject=>:self,:predicate=>:has_part,:object=>"info:fedora/3"})
457
+ r2 = ActiveFedora::Relationship.new({:subject=>:self,:predicate=>:has_part,:object=>"info:fedora/4"})
458
+ @test_object.add_relationship(r)
459
+ @test_object.add_relationship(r2)
460
+ #check both are there
461
+ @test_object.relationships.should == {:self=>{:has_part=>[r.object,r2.object]}}
462
+ @test_object.unregister_triple(r.subject,r.predicate,r.object)
463
+ #check returns false if relationship does not exist and does nothing
464
+ @test_object.unregister_triple(:self,:has_member,r2.object).should == false
465
+ #check only one item removed
466
+ @test_object.relationships.should == {:self=>{:has_part=>[r2.object]}}
467
+ @test_object.unregister_triple(r2.subject,r2.predicate,r2.object)
468
+ #check last item removed and predicate removed since now emtpy
469
+ @test_object.relationships.should == {:self=>{}}
470
+
471
+ end
472
+ end
473
+
474
+ it 'should provide #remove_relationship' do
475
+ @test_object.should respond_to(:remove_relationship)
476
+ end
477
+
478
+ describe '#remove_relationship' do
479
+ it 'should remove a relationship from the relationships hash' do
480
+ r = ActiveFedora::Relationship.new({:subject=>:self,:predicate=>:has_part,:object=>"info:fedora/3"})
481
+ r2 = ActiveFedora::Relationship.new({:subject=>:self,:predicate=>:has_part,:object=>"info:fedora/4"})
482
+ @test_object.add_relationship(r)
483
+ @test_object.add_relationship(r2)
484
+ #check both are there
485
+ @test_object.relationships.should == {:self=>{:has_part=>[r.object,r2.object]}}
486
+ @test_object.remove_relationship(r)
487
+ #check returns false if relationship does not exist and does nothing with different predicate
488
+ rBad = ActiveFedora::Relationship.new({:subject=>:self,:predicate=>:has_member,:object=>"info:fedora/4"})
489
+ @test_object.remove_relationship(rBad).should == false
490
+ #check only one item removed
491
+ @test_object.relationships.should == {:self=>{:has_part=>[r2.object]}}
492
+ @test_object.remove_relationship(r2)
493
+ #check last item removed and predicate removed since now emtpy
494
+ @test_object.relationships.should == {:self=>{}}
495
+
496
+ end
497
+ end
498
+
499
+ it 'should provide #named_relationship_predicates' do
500
+ @test_object.should respond_to(:named_relationship_predicates)
501
+ end
502
+
503
+ describe '#named_relationship_predicates' do
504
+ class MockNamedRelationshipPredicates < SpecNode2
505
+ has_relationship "testing", :has_part, :type=>SpecNode2
506
+ has_relationship "testing2", :has_member, :type=>SpecNode2
507
+ has_relationship "testing_inbound", :has_part, :type=>SpecNode2, :inbound=>true
508
+ end
509
+
510
+ it 'should return a map of subject to relationship name to fedora ontology relationship predicate' do
511
+ @test_object2 = MockNamedRelationshipPredicates.new
512
+ @test_object2.pid = increment_pid
513
+ model_rel = ActiveFedora::Relationship.new({:subject=>:self,:predicate=>:has_model,:object=>ActiveFedora::ContentModel.pid_from_ruby_class(MockNamedRelationshipPredicates)})
514
+ @test_object2.add_relationship(model_rel)
515
+ @test_object3 = SpecNode2.new
516
+ @test_object3.pid = increment_pid
517
+ r = ActiveFedora::Relationship.new({:subject=>:self,:predicate=>:has_model,:object=>ActiveFedora::ContentModel.pid_from_ruby_class(SpecNode2)})
518
+ @test_object3.add_relationship(r)
519
+ @test_object4 = SpecNode2.new
520
+ @test_object4.pid = increment_pid
521
+ @test_object4.add_relationship(r)
522
+ @test_object.add_relationship(r)
523
+ #create relationships that mirror "testing"
524
+ r3 = ActiveFedora::Relationship.new({:subject=>:self,:predicate=>:has_part,:object=>@test_object3})
525
+ r4 = ActiveFedora::Relationship.new({:subject=>:self,:predicate=>:has_part,:object=>@test_object4})
526
+ @test_object2.add_relationship(r3)
527
+ @test_object2.add_relationship(r4)
528
+ #create relationship mirroring testing2
529
+ r5 = ActiveFedora::Relationship.new({:subject=>:self,:predicate=>:has_member,:object=>@test_object})
530
+ @test_object2.add_relationship(r5)
531
+ @test_object2.named_relationship_predicates.should == {:self=>{"testing"=>:has_part,"testing2"=>:has_member},
532
+ :inbound=>{"testing_inbound"=>:has_part}}
533
+
534
+ end
535
+ end
536
+
537
+ it 'should provide #kind_of_model?' do
538
+ @test_object.should respond_to(:kind_of_model?)
539
+ end
540
+
541
+ describe '#kind_of_model?' do
542
+ it 'should check if current object is the kind of model class supplied' do
543
+ #has_model relationship does not get created until save called
544
+ r = ActiveFedora::Relationship.new({:subject=>:self,:predicate=>:has_model,:object=>ActiveFedora::ContentModel.pid_from_ruby_class(SpecNode2)})
545
+ @test_object.add_relationship(r)
546
+ @test_object.kind_of_model?(SpecNode2).should == true
547
+ end
548
+ end
549
+
550
+ it 'should provide #assert_kind_of_model' do
551
+ @test_object.should respond_to(:assert_kind_of_model)
552
+ end
553
+
554
+ describe '#assert_kind_of_model' do
555
+ it 'should correctly assert if an object is the type of model supplied' do
556
+ @test_object3 = SpecNode2.new
557
+ @test_object3.pid = increment_pid
558
+ #has_model relationship does not get created until save called so need to add the has model rel here, is fine since not testing save
559
+ r = ActiveFedora::Relationship.new({:subject=>:self,:predicate=>:has_model,:object=>ActiveFedora::ContentModel.pid_from_ruby_class(SpecNode2)})
560
+ @test_object.add_relationship(r)
561
+ @test_object3.assert_kind_of_model('object',@test_object,SpecNode2)
562
+ end
563
+ end
564
+
565
+ it 'should provide #class_from_name' do
566
+ @test_object.should respond_to(:class_from_name)
567
+ end
568
+
569
+ describe '#class_from_name' do
570
+ it 'should return a class constant for a string passed in' do
571
+ @test_object.class_from_name("SpecNode2").should == SpecNode2
572
+ end
573
+ end
574
+
575
+ it 'should provide #relationship_exists?' do
576
+ @test_object.should respond_to(:relationship_exists?)
577
+ end
578
+
579
+ describe '#relationship_exists?' do
580
+ it 'should return true if a relationship does exist' do
581
+ @test_object3 = SpecNode2.new
582
+ @test_object3.pid = increment_pid
583
+ r = ActiveFedora::Relationship.new({:subject=>:self,:predicate=>:has_member,:object=>@test_object3})
584
+ @test_object.relationship_exists?(r.subject,r.predicate,r.object).should == false
585
+ @test_object.add_relationship(r)
586
+ @test_object.relationship_exists?(r.subject,r.predicate,r.object).should == true
587
+ end
588
+ end
589
+
590
+ it 'should provide #named_relationships' do
591
+ @test_object.should respond_to(:named_relationships)
592
+ end
593
+
594
+ describe '#named_relationships' do
595
+
596
+ class MockNamedRelationships3 < SpecNode2
597
+ has_relationship "testing", :has_part, :type=>SpecNode2
598
+ has_relationship "testing2", :has_member, :type=>SpecNode2
599
+ has_relationship "testing_inbound", :has_part, :type=>SpecNode2, :inbound=>true
600
+ end
601
+
602
+ it 'should return current named relationships' do
603
+ @test_object2 = MockNamedRelationships3.new
604
+ @test_object2.pid = increment_pid
605
+ r = ActiveFedora::Relationship.new({:subject=>:self,:predicate=>:has_model,:object=>ActiveFedora::ContentModel.pid_from_ruby_class(MockNamedRelationships3)})
606
+ @test_object2.add_relationship(r)
607
+ r2 = ActiveFedora::Relationship.new({:subject=>:self,:predicate=>:has_model,:object=>ActiveFedora::ContentModel.pid_from_ruby_class(SpecNode2)})
608
+ @test_object.add_relationship(r2)
609
+ #should return expected named relationships
610
+ @test_object2.named_relationships.should == {:self=>{"testing"=>[],"testing2"=>[]},:inbound=>{"testing_inbound"=>[]}}
611
+ r = ActiveFedora::Relationship.new({:subject=>:self,:predicate=>:has_part,:object=>@test_object})
612
+ @test_object2.add_relationship(r)
613
+ @test_object2.named_relationships.should == {:self=>{"testing"=>[r.object],"testing2"=>[]},:inbound=>{"testing_inbound"=>[]}}
614
+ end
615
+
616
+ it 'should automatically update the named_relationships if relationships has changed (no refresh of named_relationships hash unless relationships hash has changed)' do
617
+ @test_object3 = MockNamedRelationships3.new
618
+ @test_object3.pid = increment_pid
619
+ r = ActiveFedora::Relationship.new({:subject=>:self,:predicate=>:has_model,:object=>ActiveFedora::ContentModel.pid_from_ruby_class(MockNamedRelationships3)})
620
+ @test_object3.add_relationship(r)
621
+ r2 = ActiveFedora::Relationship.new({:subject=>:self,:predicate=>:has_model,:object=>ActiveFedora::ContentModel.pid_from_ruby_class(SpecNode2)})
622
+ @test_object.add_relationship(r2)
623
+ #should return expected named relationships
624
+ @test_object3.named_relationships.should == {:self=>{"testing"=>[],"testing2"=>[]},:inbound=>{"testing_inbound"=>[]}}
625
+ r3 = ActiveFedora::Relationship.new({:subject=>:self,:predicate=>:has_part,:object=>@test_object})
626
+ @test_object3.add_relationship(r3)
627
+ @test_object3.named_relationships.should == {:self=>{"testing"=>[r3.object],"testing2"=>[]},:inbound=>{"testing_inbound"=>[]}}
628
+ r4 = ActiveFedora::Relationship.new({:subject=>:self,:predicate=>:has_member,:object=>"3"})
629
+ @test_object3.add_relationship(r4)
630
+ @test_object3.named_relationships.should == {:self=>{"testing"=>[r3.object],"testing2"=>[r4.object]},:inbound=>{"testing_inbound"=>[]}}
631
+ end
632
+ end
633
+
634
+ it 'should provide #assert_kind_of' do
635
+ @test_object.should respond_to(:assert_kind_of)
636
+ end
637
+
638
+ describe '#assert_kind_of' do
639
+ it 'should raise an exception if object supplied is not the correct type' do
640
+ had_exception = false
641
+ begin
642
+ @test_object.assert_kind_of 'SpecNode2', @test_object, ActiveFedora::Base
643
+ rescue
644
+ had_exception = true
645
+ end
646
+ raise "Failed to throw exception with kind of mismatch" unless had_exception
647
+ #now should not throw any exception
648
+ @test_object.assert_kind_of 'SpecNode2', @test_object, SpecNode2
649
+ end
650
+ end
651
+
652
+ it 'should provide #relationship_names' do
653
+ @test_object.should respond_to(:relationship_names)
654
+ end
655
+
656
+ describe '#relationship_names' do
657
+ class MockRelationshipNames < SpecNode2
658
+ has_relationship "testing", :has_part, :type=>SpecNode2
659
+ has_relationship "testing2", :has_member, :type=>SpecNode2
660
+ has_relationship "testing_inbound", :has_part, :type=>SpecNode2, :inbound=>true
661
+ has_relationship "testing_inbound2", :has_member, :type=>SpecNode2, :inbound=>true
662
+ end
663
+
664
+ it 'should return an array of relationship names for this model' do
665
+ @test_object2 = MockRelationshipNames.new
666
+ @test_object2.pid = increment_pid
667
+ @test_object2.relationship_names.include?("testing").should == true
668
+ @test_object2.relationship_names.include?("testing2").should == true
669
+ @test_object2.relationship_names.include?("testing_inbound").should == true
670
+ @test_object2.relationship_names.include?("testing_inbound2").should == true
671
+ @test_object2.relationship_names.size.should == 4
672
+ end
673
+ end
674
+
675
+ it 'should provide #inbound_relationship_names' do
676
+ @test_object.should respond_to(:inbound_relationship_names)
677
+ end
678
+
679
+ describe '#inbound_relationship_names' do
680
+ it 'should return an array of inbound relationship names for this model' do
681
+ @test_object2 = MockRelationshipNames.new
682
+ @test_object2.pid = increment_pid
683
+ @test_object2.inbound_relationship_names.include?("testing_inbound").should == true
684
+ @test_object2.inbound_relationship_names.include?("testing_inbound2").should == true
685
+ @test_object2.inbound_relationship_names.size.should == 2
686
+ end
687
+ end
688
+
689
+ it 'should provide #outbound_relationship_names' do
690
+ @test_object.should respond_to(:outbound_relationship_names)
691
+ end
692
+
693
+ describe '#outbound_relationship_names' do
694
+ it 'should return an array of outbound relationship names for this model' do
695
+ @test_object2 = MockRelationshipNames.new
696
+ @test_object2.pid = increment_pid
697
+ @test_object2.outbound_relationship_names.include?("testing").should == true
698
+ @test_object2.outbound_relationship_names.include?("testing2").should == true
699
+ @test_object2.outbound_relationship_names.size.should == 2
700
+ end
701
+ end
702
+
703
+ it 'should provide #named_outbound_relationships' do
704
+ @test_object.should respond_to(:named_outbound_relationships)
705
+ end
706
+
707
+ describe '#named_outbound_relationships' do
708
+ it 'should return hash of outbound relationship names to arrays of object uri' do
709
+ @test_object2 = MockRelationshipNames.new
710
+ @test_object2.pid = increment_pid
711
+ @test_object2.named_outbound_relationships.should == {"testing"=>[],
712
+ "testing2"=>[]}
713
+ end
714
+ end
715
+
716
+ it 'should provide #named_inbound_relationships' do
717
+ #testing execution of this in integration since touches solr
718
+ @test_object.should respond_to(:named_inbound_relationships)
719
+ end
720
+
721
+ it 'should provide #named_relationship' do
722
+ @test_object.should respond_to(:named_relationship)
723
+ end
724
+
725
+ describe '#named_relationship' do
726
+ it 'should return an array of object uri for a given relationship name' do
727
+ @test_object2 = MockRelationshipNames.new
728
+ @test_object2.pid = increment_pid
729
+ r = ActiveFedora::Relationship.new(:subject=>:self, :predicate=>:has_model, :object=>ActiveFedora::ContentModel.pid_from_ruby_class(MockRelationshipNames))
730
+ @test_object2.add_relationship(r)
731
+ @test_object3 = SpecNode2.new
732
+ @test_object3.pid = increment_pid
733
+ r2 = ActiveFedora::Relationship.new(:subject=>:self, :predicate=>:has_model, :object=>ActiveFedora::ContentModel.pid_from_ruby_class(SpecNode2))
734
+ @test_object3.add_relationship(r2)
735
+ @test_object4 = SpecNode2.new
736
+ @test_object4.pid = increment_pid
737
+ @test_object4.add_relationship(r2)
738
+ @test_object.add_relationship(r2)
739
+ #add relationships that mirror 'testing' and 'testing2'
740
+ r3 = ActiveFedora::Relationship.new(:subject=>:self, :predicate=>:has_part, :object=>@test_object3)
741
+ r4 = ActiveFedora::Relationship.new(:subject=>:self, :predicate=>:has_member, :object=>@test_object4)
742
+ @test_object2.add_relationship(r3)
743
+ @test_object2.add_relationship(r4)
744
+ @test_object2.named_relationship("testing").should == [r3.object]
745
+ end
746
+ end
747
+
748
+ describe ActiveFedora::SemanticNode::ClassMethods do
749
+
750
+ after(:each) do
751
+ begin
752
+ @test_object2.delete
753
+ rescue
754
+ end
755
+ end
756
+
757
+ describe '#named_relationships_desc' do
758
+ it 'should initialize named_relationships_desc to a new hash containing self' do
759
+ @test_object2 = SpecNode2.new
760
+ @test_object2.pid = increment_pid
761
+ @test_object2.named_relationships_desc.should == {:self=>{}}
762
+ end
763
+ end
764
+
765
+ describe '#register_named_subject' do
766
+
767
+ class MockRegisterNamedSubject < SpecNode2
768
+ register_named_subject :test
769
+ end
770
+
771
+ it 'should add a new named subject to the named relationships only if it does not already exist' do
772
+ @test_object2 = MockRegisterNamedSubject.new
773
+ @test_object2.pid = increment_pid
774
+ @test_object2.named_relationships_desc.should == {:self=>{}, :test=>{}}
775
+ end
776
+ end
777
+
778
+ describe '#register_named_relationship' do
779
+
780
+ class MockRegisterNamedRelationship < SpecNode2
781
+ register_named_relationship :self, "testing", :is_part_of, :type=>SpecNode2
782
+ register_named_relationship :inbound, "testing2", :has_part, :type=>SpecNode2
783
+ end
784
+
785
+ it 'should add a new named subject to the named relationships only if it does not already exist' do
786
+ @test_object2 = MockRegisterNamedRelationship.new
787
+ @test_object2.pid = increment_pid
788
+ @test_object2.named_relationships_desc.should == {:inbound=>{"testing2"=>{:type=>SpecNode2, :predicate=>:has_part}}, :self=>{"testing"=>{:type=>SpecNode2, :predicate=>:is_part_of}}}
789
+ end
790
+ end
791
+
792
+ describe '#create_named_relationship_methods' do
793
+ class MockCreateNamedRelationshipMethods < SpecNode2
794
+ register_named_relationship :self, "testing", :is_part_of, :type=>SpecNode2
795
+ create_named_relationship_methods "testing"
796
+ end
797
+
798
+ it 'should create an append and remove method for each outbound relationship' do
799
+ @test_object2 = MockCreateNamedRelationshipMethods.new
800
+ @test_object2.pid = increment_pid
801
+ @test_object2.should respond_to(:testing_append)
802
+ @test_object2.should respond_to(:testing_remove)
803
+ #test execution in base_spec since method definitions include methods in ActiveFedora::Base
804
+ end
805
+ end
806
+
807
+ describe '#def named_predicate_exists_with_different_name?' do
808
+
809
+ it 'should return true if a predicate exists for same subject and different name but not different subject' do
810
+ class MockPredicateExists < SpecNode2
811
+ has_relationship "testing", :has_part, :type=>SpecNode2
812
+ has_relationship "testing2", :has_member, :type=>SpecNode2
813
+ has_relationship "testing_inbound", :is_part_of, :type=>SpecNode2, :inbound=>true
814
+
815
+ named_predicate_exists_with_different_name?(:self,"testing",:has_part).should == false
816
+ named_predicate_exists_with_different_name?(:self,"testing3",:has_part).should == true
817
+ named_predicate_exists_with_different_name?(:inbound,"testing",:has_part).should == false
818
+ named_predicate_exists_with_different_name?(:self,"testing2",:has_member).should == false
819
+ named_predicate_exists_with_different_name?(:self,"testing3",:has_member).should == true
820
+ named_predicate_exists_with_different_name?(:inbound,"testing2",:has_member).should == false
821
+ named_predicate_exists_with_different_name?(:self,"testing_inbound",:is_part_of).should == false
822
+ named_predicate_exists_with_different_name?(:inbound,"testing_inbound",:is_part_of).should == false
823
+ named_predicate_exists_with_different_name?(:inbound,"testing_inbound2",:is_part_of).should == true
824
+ end
825
+ end
826
+ end
827
+ end
351
828
  end