active-fedora 3.2.0.pre1 → 3.2.0.pre2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. data/Gemfile.lock +1 -1
  2. data/History.txt +3 -1
  3. data/lib/active_fedora.rb +3 -3
  4. data/lib/active_fedora/associations.rb +0 -2
  5. data/lib/active_fedora/associations/association_collection.rb +15 -1
  6. data/lib/active_fedora/associations/belongs_to_association.rb +5 -1
  7. data/lib/active_fedora/associations/has_and_belongs_to_many_association.rb +5 -1
  8. data/lib/active_fedora/base.rb +36 -92
  9. data/lib/active_fedora/datastream.rb +1 -2
  10. data/lib/active_fedora/file_management.rb +73 -0
  11. data/lib/active_fedora/metadata_datastream_helper.rb +3 -1
  12. data/lib/active_fedora/model.rb +6 -18
  13. data/lib/active_fedora/relationships.rb +634 -0
  14. data/lib/active_fedora/samples/special_thing.rb +4 -4
  15. data/lib/active_fedora/semantic_node.rb +97 -236
  16. data/lib/active_fedora/version.rb +1 -1
  17. data/spec/integration/base_file_management_spec.rb +1 -0
  18. data/spec/integration/base_spec.rb +114 -68
  19. data/spec/integration/full_featured_model_spec.rb +0 -1
  20. data/spec/integration/mods_article_integration_spec.rb +0 -1
  21. data/spec/integration/nokogiri_datastream_spec.rb +0 -1
  22. data/spec/integration/rels_ext_datastream_spec.rb +10 -7
  23. data/spec/integration/semantic_node_spec.rb +10 -16
  24. data/spec/samples/models/hydrangea_article.rb +1 -2
  25. data/spec/samples/oral_history_sample_model.rb +1 -1
  26. data/spec/unit/base_spec.rb +26 -16
  27. data/spec/unit/metadata_datastream_spec.rb +1 -0
  28. data/spec/unit/qualified_dublin_core_datastream_spec.rb +1 -0
  29. data/spec/unit/relationship_spec.rb +1 -0
  30. data/spec/unit/relationships_spec.rb +846 -0
  31. data/spec/unit/semantic_node_spec.rb +2 -338
  32. metadata +8 -7
  33. data/lib/active_fedora/relationships_helper.rb +0 -881
  34. data/spec/unit/relationships_helper_spec.rb +0 -800
@@ -5,7 +5,7 @@ require 'xmlsimple'
5
5
  @@last_pid = 0
6
6
 
7
7
  class SpecNode2
8
- include ActiveFedora::RelationshipsHelper
8
+ include ActiveFedora::Relationships
9
9
  include ActiveFedora::SemanticNode
10
10
 
11
11
  attr_accessor :pid
@@ -21,18 +21,10 @@ describe ActiveFedora::SemanticNode do
21
21
  def increment_pid
22
22
  @@last_pid += 1
23
23
  end
24
-
25
- before(:all) do
26
- @pid = "test:sample_pid"
27
- @uri = "info:fedora/#{@pid}"
28
- @sample_solr_hits = [{"id"=>"_PID1_", "has_model_s"=>["info:fedora/afmodel:AudioRecord"]},
29
- {"id"=>"_PID2_", "has_model_s"=>["info:fedora/afmodel:AudioRecord"]},
30
- {"id"=>"_PID3_", "has_model_s"=>["info:fedora/afmodel:AudioRecord"]}]
31
- end
32
24
 
33
25
  before(:each) do
34
26
  class SpecNode
35
- include ActiveFedora::RelationshipsHelper
27
+ include ActiveFedora::Relationships
36
28
  include ActiveFedora::SemanticNode
37
29
 
38
30
  attr_accessor :pid
@@ -91,327 +83,6 @@ describe ActiveFedora::SemanticNode do
91
83
  @node.should respond_to(:internal_uri)
92
84
  end
93
85
 
94
- it 'should provide #has_relationship' do
95
- SpecNode.should respond_to(:has_relationship)
96
- SpecNode.should respond_to(:has_relationship)
97
- end
98
-
99
- describe '#has_relationship' do
100
- it "should create finders based on provided relationship name" do
101
- SpecNode.has_relationship("parts", :is_part_of, :inbound => true)
102
- local_node = SpecNode.new
103
- local_node.should respond_to(:parts_ids)
104
- local_node.should respond_to(:parts_query)
105
- # local_node.should respond_to(:parts)
106
- local_node.should_not respond_to(:containers)
107
- SpecNode.has_relationship("containers", :is_member_of)
108
- local_node.should respond_to(:containers_ids)
109
- local_node.should respond_to(:containers_query)
110
- end
111
-
112
- it "should add a subject and predicate to the relationships array" do
113
- SpecNode.has_relationship("parents", :is_part_of)
114
- SpecNode.relationships.should have_key(:self)
115
- SpecNode.relationships[:self].should have_key(:is_part_of)
116
- end
117
-
118
- it "should use :inbound as the subject if :inbound => true" do
119
- SpecNode.has_relationship("parents", :is_part_of, :inbound => true)
120
- SpecNode.relationships.should have_key(:inbound)
121
- SpecNode.relationships[:inbound].should have_key(:is_part_of)
122
- end
123
-
124
- it 'should create inbound relationship finders' do
125
- SpecNode.expects(:create_inbound_relationship_finders)
126
- SpecNode.has_relationship("parts", :is_part_of, :inbound => true)
127
- end
128
-
129
- it 'should create outbound relationship finders' do
130
- SpecNode.expects(:create_outbound_relationship_finders).times(2)
131
- SpecNode.has_relationship("parts", :is_part_of, :inbound => false)
132
- SpecNode.has_relationship("container", :is_member_of)
133
- end
134
-
135
- it "should create outbound relationship finders that return an array of fedora PIDs" do
136
- SpecNode.has_relationship("containers", :is_member_of, :inbound => false)
137
- local_node = SpecNode.new
138
- local_node.internal_uri = "info:fedora/#{@pid}"
139
-
140
- local_node.expects(:rels_ext).returns(stub("rels_ext", :dirty= => true, :content=>'')).at_least_once
141
- local_node.add_relationship(:is_member_of, "info:fedora/container:A")
142
- local_node.add_relationship(:is_member_of, "info:fedora/container:B")
143
-
144
- containers_result = local_node.containers_ids
145
- containers_result.should be_instance_of(Array)
146
- containers_result.should include("container:A")
147
- containers_result.should include("container:B")
148
- end
149
-
150
- class MockHasRelationship < SpecNode2
151
- has_relationship "testing", :has_part, :type=>SpecNode2
152
- has_relationship "testing2", :has_member, :type=>SpecNode2
153
- has_relationship "testing_inbound", :has_part, :type=>SpecNode2, :inbound=>true
154
- end
155
-
156
- it 'should create relationship descriptions both inbound and outbound' do
157
- @test_object2 = MockHasRelationship.new
158
- @test_object2.pid = increment_pid
159
- @test_object2.stubs(:testing_inbound).returns({})
160
- @test_object2.expects(:rels_ext).returns(stub("rels_ext", :dirty= => true, :content =>'')).at_least_once
161
- @test_object2.add_relationship(:has_model, ActiveFedora::ContentModel.pid_from_ruby_class(SpecNode2))
162
- @test_object2.should respond_to(:testing_append)
163
- @test_object2.should respond_to(:testing_remove)
164
- @test_object2.should respond_to(:testing2_append)
165
- @test_object2.should respond_to(:testing2_remove)
166
- #make sure append/remove method not created for inbound rel
167
- @test_object2.should_not respond_to(:testing_inbound_append)
168
- @test_object2.should_not respond_to(:testing_inbound_remove)
169
-
170
- @test_object2.relationships_desc.should ==
171
- {:inbound=>{"testing_inbound"=>{:type=>SpecNode2,
172
- :predicate=>:has_part,
173
- :inbound=>true,
174
- :singular=>nil}},
175
- :self=>{"testing"=>{:type=>SpecNode2,
176
- :predicate=>:has_part,
177
- :inbound=>false,
178
- :singular=>nil},
179
- "testing2"=>{:type=>SpecNode2,
180
- :predicate=>:has_member,
181
- :inbound=>false,
182
- :singular=>nil}}}
183
- end
184
- end
185
-
186
- describe '#create_inbound_relationship_finders' do
187
-
188
- it 'should respond to #create_inbound_relationship_finders' do
189
- SpecNode.should respond_to(:create_inbound_relationship_finders)
190
- end
191
-
192
- it "should create finders based on provided relationship name" do
193
- SpecNode.create_inbound_relationship_finders("parts", :is_part_of, :inbound => true)
194
- local_node = SpecNode.new
195
- local_node.should respond_to(:parts_ids)
196
- local_node.should_not respond_to(:containers)
197
- SpecNode.create_inbound_relationship_finders("containers", :is_member_of, :inbound => true)
198
- local_node.should respond_to(:containers_ids)
199
- local_node.should respond_to(:containers)
200
- local_node.should respond_to(:containers_from_solr)
201
- local_node.should respond_to(:containers_query)
202
- end
203
-
204
- it "resulting finder should search against solr and use Model#load_instance to build an array of objects" do
205
- solr_result = (mock("solr result", :is_a? => true, :hits => @sample_solr_hits))
206
- SpecNode.create_inbound_relationship_finders("parts", :is_part_of, :inbound => true)
207
- local_node = SpecNode.new()
208
- local_node.expects(:pid).returns("test:sample_pid")
209
- SpecNode.expects(:relationships_desc).returns({:inbound=>{"parts"=>{:predicate=>:is_part_of}}}).at_least_once()
210
- ActiveFedora::SolrService.instance.conn.expects(:query).with("is_part_of_s:info\\:fedora/test\\:sample_pid", :rows=>25).returns(solr_result)
211
- local_node.parts.map(&:pid).should == ["_PID1_", "_PID2_", "_PID3_"]
212
- end
213
-
214
- it "resulting finder should accept :solr as :response_format value and return the raw Solr Result" do
215
- solr_result = mock("solr result")
216
- SpecNode.create_inbound_relationship_finders("constituents", :is_constituent_of, :inbound => true)
217
- local_node = SpecNode.new
218
- mock_repo = mock("repo")
219
- mock_repo.expects(:find_model).never
220
- local_node.expects(:pid).returns("test:sample_pid")
221
- SpecNode.expects(:relationships_desc).returns({:inbound=>{"constituents"=>{:predicate=>:is_constituent_of}}}).at_least_once()
222
- ActiveFedora::SolrService.instance.conn.expects(:query).with("is_constituent_of_s:info\\:fedora/test\\:sample_pid", :rows=>101).returns(solr_result)
223
- local_node.constituents(:response_format => :solr, :rows=>101).should equal(solr_result)
224
- end
225
-
226
-
227
- it "resulting _ids finder should search against solr and return an array of fedora PIDs" do
228
- SpecNode.create_inbound_relationship_finders("parts", :is_part_of, :inbound => true)
229
- local_node = SpecNode.new
230
- local_node.expects(:pid).returns("test:sample_pid")
231
- SpecNode.expects(:relationships_desc).returns({:inbound=>{"parts"=>{:predicate=>:is_part_of}}}).at_least_once()
232
- ActiveFedora::SolrService.instance.conn.expects(:query).with("is_part_of_s:info\\:fedora/test\\:sample_pid", :rows=>25).returns(mock("solr result", :hits => [Hash["id"=>"pid1"], Hash["id"=>"pid2"]]))
233
- local_node.parts(:response_format => :id_array).should == ["pid1", "pid2"]
234
- end
235
-
236
- it "resulting _ids finder should call the basic finder with :result_format => :id_array" do
237
- SpecNode.create_inbound_relationship_finders("parts", :is_part_of, :inbound => true)
238
- local_node = SpecNode.new
239
- local_node.expects(:parts).with(:response_format => :id_array)
240
- local_node.parts_ids
241
- end
242
-
243
- it "resulting _query finder should call relationship_query" do
244
- SpecNode.create_inbound_relationship_finders("parts", :is_part_of, :inbound => true)
245
- local_node = SpecNode.new
246
- local_node.expects(:relationship_query).with("parts")
247
- local_node.parts_query
248
- end
249
- end
250
-
251
- describe '#create_outbound_relationship_finders' do
252
-
253
- it 'should respond to #create_outbound_relationship_finders' do
254
- SpecNode.should respond_to(:create_outbound_relationship_finders)
255
- end
256
-
257
- it "should create finders based on provided relationship name" do
258
- SpecNode.create_outbound_relationship_finders("parts", :is_part_of)
259
- local_node = SpecNode.new
260
- local_node.should respond_to(:parts_ids)
261
- #local_node.should respond_to(:parts) #.with(:type => "AudioRecord")
262
- local_node.should_not respond_to(:containers)
263
- SpecNode.create_outbound_relationship_finders("containers", :is_member_of)
264
- local_node.should respond_to(:containers_ids)
265
- local_node.should respond_to(:containers)
266
- local_node.should respond_to(:containers_from_solr)
267
- local_node.should respond_to(:containers_query)
268
- end
269
-
270
- describe " resulting finder" do
271
- it "should read from relationships array and use Repository.find_model to build an array of objects" do
272
- SpecNode.create_outbound_relationship_finders("containers", :is_member_of)
273
- local_node = SpecNode.new
274
- local_node.expects(:ids_for_outbound).with(:is_member_of).returns(["my:_PID1_", "my:_PID2_", "my:_PID3_"])
275
- mock_repo = mock("repo")
276
- solr_result = mock("solr result", :is_a? => true)
277
- solr_result.expects(:hits).returns(
278
- [{"id"=> "my:_PID1_", "has_model_s"=>["info:fedora/afmodel:SpecNode"]},
279
- {"id"=> "my:_PID2_", "has_model_s"=>["info:fedora/afmodel:SpecNode"]},
280
- {"id"=> "my:_PID3_", "has_model_s"=>["info:fedora/afmodel:SpecNode"]}])
281
-
282
- ActiveFedora::SolrService.instance.conn.expects(:query).with("id:my\\:_PID1_ OR id:my\\:_PID2_ OR id:my\\:_PID3_").returns(solr_result)
283
- local_node.containers.map(&:pid).should == ["my:_PID1_", "my:_PID2_", "my:_PID3_"]
284
- end
285
-
286
- it "should accept :solr as :response_format value and return the raw Solr Result" do
287
- solr_result = mock("solr result")
288
- SpecNode.create_outbound_relationship_finders("constituents", :is_constituent_of)
289
- local_node = SpecNode.new
290
- mock_repo = mock("repo")
291
- mock_repo.expects(:find_model).never
292
- local_node.expects(:rels_ext).returns(stub('rels-ext', :content=>''))
293
- ActiveFedora::SolrService.instance.conn.expects(:query).returns(solr_result)
294
- local_node.constituents(:response_format => :solr).should equal(solr_result)
295
- end
296
-
297
- it "(:response_format => :id_array) should read from relationships array" do
298
- SpecNode.create_outbound_relationship_finders("containers", :is_member_of)
299
- local_node = SpecNode.new
300
- local_node.expects(:ids_for_outbound).with(:is_member_of).returns([])
301
- local_node.containers_ids
302
- end
303
-
304
- it "(:response_format => :id_array) should return an array of fedora PIDs" do
305
- SpecNode.create_outbound_relationship_finders("containers", :is_member_of)
306
- local_node = SpecNode.new
307
- local_node.expects(:rels_ext).returns(stub("rels_ext", :dirty= => true, :content=>'')).at_least_once
308
- local_node.add_relationship(:is_member_of, "demo:10")
309
- result = local_node.containers_ids
310
- result.should be_instance_of(Array)
311
- result.should include("demo:10")
312
- end
313
-
314
- end
315
-
316
- describe " resulting _ids finder" do
317
- it "should call the basic finder with :result_format => :id_array" do
318
- SpecNode.create_outbound_relationship_finders("parts", :is_part_of)
319
- local_node = SpecNode.new
320
- local_node.expects(:parts).with(:response_format => :id_array)
321
- local_node.parts_ids
322
- end
323
- end
324
-
325
- it "resulting _query finder should call relationship_query" do
326
- SpecNode.create_outbound_relationship_finders("containers", :is_member_of)
327
- local_node = SpecNode.new
328
- local_node.expects(:relationship_query).with("containers")
329
- local_node.containers_query
330
- end
331
- end
332
-
333
- describe ".create_bidirectional_relationship_finder" do
334
- before(:each) do
335
- SpecNode.create_bidirectional_relationship_finders("all_parts", :has_part, :is_part_of)
336
- @local_node = SpecNode.new
337
- @local_node.pid = @pid
338
- @local_node.internal_uri = @uri
339
- end
340
- it "should create inbound & outbound finders" do
341
- @local_node.should respond_to(:all_parts_inbound)
342
- @local_node.should respond_to(:all_parts_outbound)
343
- end
344
- it "should rely on inbound & outbound finders" do
345
- @local_node.expects(:all_parts_inbound).with(:rows => 25).returns(["foo1"])
346
- @local_node.expects(:all_parts_outbound).with(:rows => 25).returns(["foo2"])
347
- @local_node.all_parts.should == ["foo1", "foo2"]
348
- end
349
- it "(:response_format => :id_array) should rely on inbound & outbound finders" do
350
- @local_node.expects(:all_parts_inbound).with(:response_format=>:id_array, :rows => 34).returns(["fooA"])
351
- @local_node.expects(:all_parts_outbound).with(:response_format=>:id_array, :rows => 34).returns(["fooB"])
352
- @local_node.all_parts(:response_format=>:id_array, :rows => 34).should == ["fooA", "fooB"]
353
- end
354
- it "(:response_format => :solr) should construct a solr query that combines inbound and outbound searches" do
355
- # get the id array for outbound relationships then construct solr query by combining id array with inbound relationship search
356
- @local_node.expects(:ids_for_outbound).with(:has_part).returns(["mypid:1"])
357
- id_array_query = ActiveFedora::SolrService.construct_query_for_pids(["mypid:1"])
358
- solr_result = mock("solr result")
359
- ActiveFedora::SolrService.instance.conn.expects(:query).with("#{id_array_query} OR (is_part_of_s:info\\:fedora/test\\:sample_pid)", :rows=>25).returns(solr_result)
360
- @local_node.all_parts(:response_format=>:solr)
361
- end
362
-
363
- it "should register both inbound and outbound predicate components" do
364
- @local_node.class.relationships[:inbound].has_key?(:is_part_of).should == true
365
- @local_node.class.relationships[:self].has_key?(:has_part).should == true
366
- end
367
-
368
- it "should register relationship names for inbound, outbound" do
369
- @local_node.relationship_names.include?("all_parts_inbound").should == true
370
- @local_node.relationship_names.include?("all_parts_outbound").should == true
371
- end
372
-
373
- it "should register finder methods for the bidirectional relationship name" do
374
- @local_node.should respond_to(:all_parts)
375
- @local_node.should respond_to(:all_parts_ids)
376
- @local_node.should respond_to(:all_parts_query)
377
- @local_node.should respond_to(:all_parts_from_solr)
378
- end
379
-
380
- it "resulting _query finder should call relationship_query" do
381
- SpecNode.create_bidirectional_relationship_finders("containers", :is_member_of, :has_member)
382
- local_node = SpecNode.new
383
- local_node.expects(:relationship_query).with("containers")
384
- local_node.containers_query
385
- end
386
- end
387
-
388
- describe "#has_bidirectional_relationship" do
389
- it "should ..." do
390
- SpecNode.expects(:create_bidirectional_relationship_finders).with("all_parts", :has_part, :is_part_of, {})
391
- SpecNode.has_bidirectional_relationship("all_parts", :has_part, :is_part_of)
392
- end
393
-
394
- it "should have relationships_by_name and relationships hashes contain bidirectionally related objects" do
395
- SpecNode.has_bidirectional_relationship("all_parts", :has_part, :is_part_of)
396
- @local_node = SpecNode.new
397
- @local_node.pid = "mypid1"
398
- @local_node2 = SpecNode.new
399
- @local_node2.pid = "mypid2"
400
- model_def = ActiveFedora::ContentModel.pid_from_ruby_class(SpecNode)
401
- @local_node.expects(:rels_ext).returns(stub("rels_ext", :dirty= => true, :content=>'')).at_least_once
402
- @local_node.add_relationship(:has_model, model_def)
403
- @local_node2.expects(:rels_ext).returns(stub("rels_ext", :dirty= => true, :content=>'')).at_least_once
404
- @local_node2.add_relationship(:has_model, model_def)
405
- @local_node.add_relationship(:has_part, @local_node2)
406
- @local_node2.add_relationship(:has_part, @local_node)
407
- @local_node.ids_for_outbound(:has_part).should == [@local_node2.pid]
408
- @local_node.ids_for_outbound(:has_model).should == ['afmodel:SpecNode']
409
- @local_node2.ids_for_outbound(:has_part).should == [@local_node.pid]
410
- @local_node2.ids_for_outbound(:has_model).should == ['afmodel:SpecNode']
411
- @local_node.relationships_by_name(false).should == {:self=>{"all_parts_outbound"=>[@local_node2.internal_uri]},:inbound=>{"all_parts_inbound"=>[]}}
412
- @local_node2.relationships_by_name(false).should == {:self=>{"all_parts_outbound"=>[@local_node.internal_uri]},:inbound=>{"all_parts_inbound"=>[]}}
413
- end
414
- end
415
86
 
416
87
  describe ".add_relationship" do
417
88
  it "should add relationship to the relationships graph" do
@@ -444,13 +115,6 @@ describe ActiveFedora::SemanticNode do
444
115
 
445
116
  end
446
117
 
447
- describe '#relationships' do
448
-
449
- it "should return a hash" do
450
- SpecNode.relationships.class.should == Hash
451
- end
452
-
453
- end
454
118
 
455
119
  it "should provide .outbound_relationships" do
456
120
  @node.should respond_to(:outbound_relationships)
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active-fedora
3
3
  version: !ruby/object:Gem::Version
4
- hash: 1923832043
4
+ hash: 1923832045
5
5
  prerelease: 6
6
6
  segments:
7
7
  - 3
8
8
  - 2
9
9
  - 0
10
10
  - pre
11
- - 1
12
- version: 3.2.0.pre1
11
+ - 2
12
+ version: 3.2.0.pre2
13
13
  platform: ruby
14
14
  authors:
15
15
  - Matt Zumwalt
@@ -18,7 +18,7 @@ autorequire:
18
18
  bindir: bin
19
19
  cert_chain: []
20
20
 
21
- date: 2011-12-21 00:00:00 -06:00
21
+ date: 2011-12-29 00:00:00 -06:00
22
22
  default_executable:
23
23
  dependencies:
24
24
  - !ruby/object:Gem::Dependency
@@ -569,6 +569,7 @@ files:
569
569
  - lib/active_fedora/delegating.rb
570
570
  - lib/active_fedora/digital_object.rb
571
571
  - lib/active_fedora/fedora_object.rb
572
+ - lib/active_fedora/file_management.rb
572
573
  - lib/active_fedora/fixture_loader.rb
573
574
  - lib/active_fedora/metadata_datastream.rb
574
575
  - lib/active_fedora/metadata_datastream_helper.rb
@@ -584,7 +585,7 @@ files:
584
585
  - lib/active_fedora/reflection.rb
585
586
  - lib/active_fedora/relationship.rb
586
587
  - lib/active_fedora/relationship_graph.rb
587
- - lib/active_fedora/relationships_helper.rb
588
+ - lib/active_fedora/relationships.rb
588
589
  - lib/active_fedora/rels_ext_datastream.rb
589
590
  - lib/active_fedora/rubydora_connection.rb
590
591
  - lib/active_fedora/samples.rb
@@ -707,7 +708,7 @@ files:
707
708
  - spec/unit/rdf_xml_writer.rb
708
709
  - spec/unit/relationship_graph_spec.rb
709
710
  - spec/unit/relationship_spec.rb
710
- - spec/unit/relationships_helper_spec.rb
711
+ - spec/unit/relationships_spec.rb
711
712
  - spec/unit/rels_ext_datastream_spec.rb
712
713
  - spec/unit/rubydora_connection_spec.rb
713
714
  - spec/unit/semantic_node_spec.rb
@@ -850,7 +851,7 @@ test_files:
850
851
  - spec/unit/rdf_xml_writer.rb
851
852
  - spec/unit/relationship_graph_spec.rb
852
853
  - spec/unit/relationship_spec.rb
853
- - spec/unit/relationships_helper_spec.rb
854
+ - spec/unit/relationships_spec.rb
854
855
  - spec/unit/rels_ext_datastream_spec.rb
855
856
  - spec/unit/rubydora_connection_spec.rb
856
857
  - spec/unit/semantic_node_spec.rb
@@ -1,881 +0,0 @@
1
- module ActiveFedora
2
- # This module is meant to extend semantic node to add functionality based on a relationship's name
3
- # It is meant to turn a relationship into just another attribute in a model.
4
- # The notion of a "relationship name" is used _internally_ to distinguish between the relationships you've set up using has_relationship and the implicit relationships that are based on the predicates themselves.
5
- #
6
- # @example ActiveFedora
7
- # has_relationship "parents", :is_member_of
8
- #
9
- # obj.parents is a relationship in ActiveFedora while :is_member_of is the literal RDF relationship in Fedora
10
- #
11
- # There are also several helper methods created for any relationship declared in ActiveFedora. For the above example
12
- # the following methods are created:
13
- #
14
- # obj.parents_append(object) Appends an object to the "parents" relationship
15
- # obj.parents_remove(object) Removes an object from the "parents" relationship
16
- # obj.parents_query Returns the query used against solr to retrieve objects linked via the "parents" relationship
17
- #
18
- # Note: ActiveFedora relationships can reflect filters ...
19
- # If you define the solr_fq parameter in your has_relationship call some objects will be filtered out:
20
- #
21
- # has_relationship "parents", :is_member_of, :solr_fq=>"eyes:blue"
22
- #
23
- # Then obj.parents will only return parents where their eyes are blue.
24
- module RelationshipsHelper
25
- extend ActiveSupport::Concern
26
-
27
- included do
28
- class_attribute :class_relationships_desc
29
- end
30
-
31
-
32
- # ** EXPERIMENTAL **
33
- #
34
- # Return array of objects for a given relationship name
35
- # @param [String] Name of relationship to find
36
- # @return [Array] Returns array of objects linked via the relationship name given
37
- def find_relationship_by_name(name)
38
- rels = nil
39
- if inbound_relationship_names.include?(name)
40
- rels = relationships_by_name(false)[:inbound][name]
41
- elsif outbound_relationship_names.include?(name)
42
- rels = relationships_by_name[:self][name]
43
- end
44
- rels = [] if rels.nil?
45
- return rels
46
- end
47
-
48
- # ** EXPERIMENTAL **
49
- #
50
- # Internal method that ensures a relationship subject such as :self and :inbound
51
- # exist within the relationships_desc hash tracking relationships metadata.
52
- # This method just calls the class method counterpart of this method.
53
- # @param [Symbol] Subject name to register (will probably be something like :self or :inbound)
54
- def register_relationship_desc_subject(subject)
55
- self.class.register_relationship_desc_subject(subject)
56
- end
57
-
58
- # ** EXPERIMENTAL **
59
- #
60
- # Internal method that adds a relationship description for a
61
- # relationship name and predicate pair to either an outbound (:self)
62
- # or inbound (:inbound) relationship types. This method just calls the class method counterpart of this method.
63
- # @param [Symbol] Subject name to register
64
- # @param [String] Name of relationship being registered
65
- # @param [Symbol] Fedora ontology predicate to use
66
- # @param [Hash] Any options passed to has_relationship such as :type, :solr_fq, etc.
67
- def register_relationship_desc(subject, name, predicate, opts={})
68
- self.class.register_relationship_desc(subject, name, predicate, opts)
69
- end
70
-
71
- # ** EXPERIMENTAL **
72
- #
73
- # Gets the relationships hash with subject mapped to relationship
74
- # names instead of relationship predicates (unlike the "relationships" method in SemanticNode)
75
- # It has an optional parameter of outbound_only that defaults true.
76
- # If false it will include inbound relationships in the results.
77
- # Also, it will only reload outbound relationships if the relationships hash has changed
78
- # since the last time this method was called.
79
- # @param [Boolean] if false it will include inbound relationships (defaults to true)
80
- # @return [Hash] Returns a hash of subject name (:self or :inbound) mapped to nested hashs of each relationship name mapped to an Array of objects linked via the relationship
81
- def relationships_by_name(outbound_only=true)
82
- @relationships_by_name = relationships_by_name_from_class()
83
- outbound_only ? @relationships_by_name : @relationships_by_name.merge(:inbound=>inbound_relationships_by_name)
84
- end
85
-
86
- # ** EXPERIMENTAL **
87
- #
88
- # Gets relationships by name from the class using the current relationships hash
89
- # and relationship name,predicate pairs.
90
- # @return [Hash] returns the outbound relationships with :self mapped to nested hashs of each relationship name mapped to an Array of objects linked via the relationship
91
- def relationships_by_name_from_class()
92
- rels = {}
93
- relationship_predicates.each_pair do |subj, names|
94
- case subj
95
- when :self
96
- rels[:self] = {}
97
- names.each_pair do |name, predicate|
98
- set = []
99
- res = relationships.query(:predicate => Predicates.find_graph_predicate(predicate))
100
- res.each_object do |o|
101
- set << o.to_s
102
- end
103
- rels[:self][name] = set
104
- end
105
- when :inbound
106
- #nop
107
- # inbound = inbound_relationships
108
- # names.each_pair do |name, predicate|
109
- # rels[:inbound][name] = inbound[predicate]
110
- # end
111
- end
112
- end
113
- return rels
114
- end
115
-
116
-
117
- # ** EXPERIMENTAL **
118
- #
119
- # Return hash of relationships_by_name defined within other objects' RELS-EXT
120
- # It returns a hash of relationship name to arrays of objects. It requeries
121
- # solr each time this method is called.
122
- # @return [Hash] Return hash of each relationship name mapped to an Array of objects linked to this object via inbound relationships
123
- def inbound_relationships_by_name
124
- rels = {}
125
- if relationships_desc.has_key?(:inbound)&&!relationships_desc[:inbound].empty?()
126
- inbound_rels = inbound_relationships
127
-
128
- if relationship_predicates.has_key?(:inbound)
129
- relationship_predicates[:inbound].each do |name, predicate|
130
- rels[name] = inbound_rels.has_key?(predicate) ? inbound_rels[predicate] : []
131
- end
132
- end
133
- end
134
- return rels
135
- end
136
-
137
- # ** EXPERIMENTAL **
138
- #
139
- # Return hash of outbound relationship names and predicate pairs
140
- # @return [Hash] A hash of outbound relationship names mapped to predicates used
141
- def outbound_relationship_predicates
142
- relationship_predicates.has_key?(:self) ? relationship_predicates[:self] : {}
143
- end
144
-
145
- # ** EXPERIMENTAL **
146
- #
147
- # Return hash of inbound relationship names and predicate pairs
148
- # @return [Hash] A hash of inbound relationship names mapped to predicates used
149
- def inbound_relationship_predicates
150
- relationship_predicates.has_key?(:inbound) ? relationship_predicates[:inbound] : {}
151
- end
152
-
153
- # ** EXPERIMENTAL **
154
- #
155
- # Return hash of relationship names and predicate pairs (inbound and outbound).
156
- # This method calls the class method version of this method to get the static settings
157
- # defined in the class definition.
158
- # @return [Hash] A hash of relationship names (inbound and outbound) mapped to predicates used
159
- def relationship_predicates
160
- @relationship_predicates ||= relationship_predicates_from_class
161
- end
162
-
163
- # ** EXPERIMENTAL **
164
- #
165
- # Return hash of relationship names and predicate pairs from class.
166
- # It retrieves this information via the relationships_desc hash in the class.
167
- # @return [Hash] A hash of relationship names (inbound and outbound) mapped to predicates used
168
- def relationship_predicates_from_class
169
- rels = {}
170
- relationships_desc.each_pair do |subj, names|
171
- rels[subj] = {}
172
- names.each_pair do |name, args|
173
- rels[subj][name] = args[:predicate]
174
- end
175
- end
176
- return rels
177
- end
178
-
179
- # ** EXPERIMENTAL **
180
- #
181
- # Return array all relationship names
182
- # @return [Array] of relationship names for relationships declared via has_relationship in the class
183
- def relationship_names
184
- names = []
185
- relationships_desc.each_key do |subject|
186
- names = names.concat(relationships_desc[subject].keys)
187
- end
188
- names
189
- end
190
-
191
- # ** EXPERIMENTAL **
192
- #
193
- # Return array of relationship names for all inbound relationships (coming from other objects' RELS-EXT and Solr)
194
- # @return [Array] of inbound relationship names for relationships declared via has_relationship in the class
195
- def inbound_relationship_names
196
- relationships_desc.has_key?(:inbound) ? relationships_desc[:inbound].keys : []
197
- end
198
-
199
- # ** EXPERIMENTAL **
200
- #
201
- # Return array of relationship names for all outbound relationships (coming from this object's RELS-EXT)
202
- # @return [Array] of outbound relationship names for relationships declared via has_relationship in the class
203
- def outbound_relationship_names
204
- relationships_desc.has_key?(:self) ? relationships_desc[:self].keys : []
205
- end
206
-
207
- # ** EXPERIMENTAL **
208
- #
209
- # Return hash of relationships_by_name defined within this object's RELS-EXT
210
- # It returns a hash of relationship name to arrays of objects
211
- # @return [Hash] Return hash of each relationship name mapped to an Array of objects linked to this object via outbound relationships
212
- def outbound_relationships_by_name
213
- relationships_desc.has_key?(:self) ? relationships_by_name[:self] : {}
214
- end
215
-
216
- # ** EXPERIMENTAL **
217
- #
218
- # Returns true if the given relationship name is a relationship
219
- # @param [String] Name of relationship
220
- # @param [Boolean] If false checks inbound relationships as well (defaults to true)
221
- def is_relationship_name?(name, outbound_only=true)
222
- if outbound_only
223
- outbound_relationship_names.include?(name)
224
- else
225
- (outbound_relationship_names.include?(name)||inbound_relationship_names.include?(name))
226
- end
227
- end
228
-
229
- # ** EXPERIMENTAL **
230
- #
231
- # Return hash that persists relationship metadata defined by has_relationship calls
232
- # @return [Hash] Hash of relationship subject (:self or :inbound) mapped to nested hashs of each relationship name mapped to another hash relationship options
233
- # @example For the following relationship
234
- #
235
- # has_relationship "audio_records", :has_part, :type=>AudioRecord
236
- #
237
- # Results in the following returned by relationships_desc
238
- # {:self=>{"audio_records"=>{:type=>AudioRecord, :singular=>nil, :predicate=>:has_part, :inbound=>false}}}
239
- def relationships_desc
240
- @relationships_desc ||= relationships_desc_from_class
241
- end
242
-
243
- # ** EXPERIMENTAL **
244
- #
245
- # Get class instance variable relationships_desc that holds has_relationship metadata
246
- # @return [Hash] Hash of relationship subject (:self or :inbound) mapped to nested hashs of each relationship name mapped to another hash relationship options
247
- def relationships_desc_from_class
248
- self.class.relationships_desc
249
- end
250
-
251
- # ** EXPERIMENTAL **
252
- #
253
- # Return the value of :type for the relationship for name passed in if defined
254
- # It defaults to ActiveFedora::Base.
255
- # @return [Class] the name of the class defined for a relationship by the :type option if present
256
- def relationship_model_type(name)
257
- if is_relationship_name?(name,true)
258
- subject = outbound_relationship_names.include?(name)? :self : :inbound
259
- if relationships_desc[subject][name].has_key?(:type)
260
- return class_from_name(relationships_desc[subject][name][:type])
261
- end
262
- end
263
- return nil
264
- end
265
-
266
- # ** EXPERIMENTAL **
267
- #
268
- # Add an outbound relationship for given relationship name
269
- # See ActiveFedora::SemanticNode::ClassMethods.has_relationship
270
- # @param [String] Name of relationship
271
- # @param [ActiveFedora::Base] object to add to the relationship (expects ActvieFedora::Base to be an ancestor)
272
- # @return [Boolean] returns true if add operation successful
273
- def add_relationship_by_name(name, object)
274
- if is_relationship_name?(name,true)
275
- if relationships_desc[:self][name].has_key?(:type)
276
- klass = class_from_name(relationships_desc[:self][name][:type])
277
- unless klass.nil?
278
- (assert_conforms_to 'object', object, klass)
279
- end
280
- end
281
- add_relationship(outbound_relationship_predicates[name],object)
282
- else
283
- false
284
- end
285
- end
286
-
287
- # ** EXPERIMENTAL **
288
- #
289
- # Remove an object for the given relationship name
290
- # @param [String] Relationship name
291
- # @param [ActiveFedora::Base] object to remove
292
- # @return [Boolean] return true if remove operation successful
293
- def remove_relationship_by_name(name, object)
294
- if is_relationship_name?(name,true)
295
- remove_relationship(outbound_relationship_predicates[name],object)
296
- else
297
- return false
298
- end
299
- end
300
-
301
- # ** EXPERIMENTAL **
302
- #
303
- # Throws an assertion error if conforms_to? returns false for object and model_class
304
- # @param [String] Name of object (just label for output)
305
- # @param [ActiveFedora::Base] Expects to be an object that has ActiveFedora::Base as an ancestor of its class
306
- # @param [Class] The model class used in conforms_to? check on object
307
- def assert_conforms_to(name, object, model_class)
308
- raise "Assertion failure: #{name}: #{object.pid} does not have model #{model_class}, it has model #{relationships[:self][:has_model]}" unless object.conforms_to?(model_class)
309
- end
310
-
311
- # ** EXPERIMENTAL **
312
- #
313
- # Checks that this object is matches the model class passed in.
314
- # It requires two steps to pass to return true
315
- # 1. It has a hasModel relationship of the same model
316
- # 2. kind_of? returns true for the model passed in
317
- # This method can most often be used to detect if an object from Fedora that was created
318
- # with a different model was then used to populate this object.
319
- # @param [Class] the model class name to check if an object conforms_to that model
320
- # @return [Boolean] true if this object conforms to the given model name
321
- def conforms_to?(model_class)
322
- if self.kind_of?(model_class)
323
- #check has model and class match
324
- mod = relationships.first(:predicate=>Predicates.find_graph_predicate(:has_model))
325
- if mod
326
- expected = ActiveFedora::ContentModel.pid_from_ruby_class(self.class)
327
- if mod.object.to_s == expected
328
- return true
329
- else
330
- raise "has_model relationship check failed for model #{model_class} raising exception, expected: '#{expected}' actual: '#{mod.object.to_s}'"
331
- end
332
- else
333
- raise "has_model relationship does not exist for model #{model_class} check raising exception"
334
- end
335
- else
336
- raise "kind_of? check failed for model #{model_class}, actual #{self.class} raising exception"
337
- end
338
- return false
339
- end
340
-
341
- # Returns a Class symbol for the given string for the class name
342
- # @param [String] the class name as a string
343
- # @return [Class] the class as a Class object
344
- def class_from_name(name)
345
- klass = name.to_s.split('::').inject(Kernel) {|scope, const_name|
346
- scope.const_get(const_name)}
347
- (!klass.nil? && klass.is_a?(::Class)) ? klass : nil
348
- end
349
-
350
- # Call this method to return the query used against solr to retrieve any
351
- # objects linked via the relationship name given.
352
- #
353
- # Instead of this method you can also use the helper method
354
- # [relationship_name]_query, i.e. method "parts_query" for relationship "parts" to return the same value
355
- # @param [String] The name of the relationship defined in the model
356
- # @return [String] The query used when querying solr for objects for this relationship
357
- # @example
358
- # Class SampleAFObjRelationshipFilterQuery < ActiveFedora::Base
359
- # #points to all parents linked via is_member_of
360
- # has_relationship "parents", :is_member_of
361
- # #returns only parents that have a level value set to "series"
362
- # has_relationship "series_parents", :is_member_of, :solr_fq=>level_t:series"
363
- # end
364
- # s = SampleAFObjRelationshipFilterQuery.new
365
- # obj = ActiveFedora::Base.new
366
- # s.parents_append(obj)
367
- # s.series_parents_query
368
- # #=> "(id:changeme\\:13020 AND level_t:series)"
369
- # SampleAFObjRelationshipFilterQuery.relationship_query("series_parents")
370
- # #=> "(id:changeme\\:13020 AND level_t:series)"
371
- def relationship_query(relationship_name)
372
- query = ""
373
- if self.class.is_bidirectional_relationship?(relationship_name)
374
- predicate = outbound_relationship_predicates["#{relationship_name}_outbound"]
375
- id_array = ids_for_outbound(predicate)
376
- query = self.class.bidirectional_relationship_query(pid,relationship_name,id_array)
377
- elsif outbound_relationship_names.include?(relationship_name)
378
- predicate = outbound_relationship_predicates[relationship_name]
379
- id_array = ids_for_outbound(predicate)
380
- query = self.class.outbound_relationship_query(relationship_name,id_array)
381
- elsif inbound_relationship_names.include?(relationship_name)
382
- query = self.class.inbound_relationship_query(pid,relationship_name)
383
- end
384
- query
385
- end
386
-
387
- ## Deprecated method checks for HYDRA-541 methods renamed
388
- #
389
- # Old Name New Name
390
- # named_relationship find_relationship_by_name
391
- # register_named_subject register_relationship_desc_subject
392
- # register_named_relationship register_relationship_desc
393
- # named_relationships relationships_by_name
394
- # named_relationships_from_class relationships_by_name_from_class
395
- # named_inbound_relationships inbound_relationship_names
396
- # outbound_named_relationship_predicates outbound_relationship_predicates
397
- # inbound_named_relationship_predicates inbound_relationship_predicates
398
- # named_relationship_predicates relationship_predicates
399
- # named_relationship_predicates_from_class relationship_predicates_from_class
400
- # named_outbound_relationships outbound_relationships_by_name
401
- # is_named_relationship? is_relationship_name?
402
- # named_relationships_desc relationships_desc
403
- # named_relationships_desc_from_class relationships_desc_from_class
404
- # named_relationship_type relationship_model_type
405
- # add_named_relationship add_relationship_by_name
406
- # remove_named_relationship remove_relationship_by_name
407
- # assert_kind_of_model assert_conforms_to
408
- # kind_of_model? conforms_to?
409
- # named_relationship_query relationship_query
410
- # CLASS METHODS
411
- # named_relationships_desc relationships_desc
412
- # register_named_subject register_relationship_desc_subject
413
- # register_named_relationship register_relationship_desc
414
- # create_named_relationship_method create_relationship_name_methods
415
- # create_bidirectional_named_relationship_methods create_bidirectional_relationship_name_methods
416
- # outbound_named_relationship_query outbound_relationship_query
417
- # inbound_named_relationship_query inbound_relationship_query
418
- # bidirectional_named_relationship_query bidirectional_relationship_query
419
- # named_predicate_exists_with_different_name? predicate_exists_with_different_relationship_name?
420
-
421
- # @deprecated Please use {#find_relationship_by_name} instead.
422
- def named_relationship(name)
423
- ActiveSupport::Deprecation.warn("Deprecation: named_relationship has been deprecated. Please call find_relationship_by_name instead.")
424
- find_relationship_by_name(name)
425
- end
426
-
427
- # @deprecated Please use {#register_relationship_desc_subject} instead.
428
- def register_named_subject(subject)
429
- ActiveSupport::Deprecation.warn("Deprecation: register_named_subject has been deprecated. Please call register_relationship_desc_subject instead.")
430
- register_relationship_desc_subject(subject)
431
- end
432
-
433
- # @deprecated Please use {#register_relationship_desc} instead.
434
- def register_named_relationship(subject, name, predicate, opts)
435
- ActiveSupport::Deprecation.warn("Deprecation: register_named_relationship has been deprecated. Please call register_relationship_desc instead.")
436
- register_relationship_desc(subject, name, predicate, opts)
437
- end
438
-
439
- # @deprecated Please use {#relationships_by_name} instead.
440
- def named_relationships(outbound_only=true)
441
- ActiveSupport::Deprecation.warn("Deprecation: named_relationships has been deprecated. Please call relationships_by_name instead.")
442
- relationships_by_name(outbound_only)
443
- end
444
-
445
- # @deprecated Please use {#relationships_by_name_from_class} instead.
446
- def named_relationships_from_class
447
- ActiveSupport::Deprecation.warn("Deprecation: named_relationships_from_class has been deprecated. Please call relationships_by_name_from_class instead.")
448
- relationships_by_name_from_class
449
- end
450
-
451
- # @deprecated Please use {#inbound_relationships_by_name} instead.
452
- def named_inbound_relationships
453
- ActiveSupport::Deprecation.warn("Deprecation: named_inbound_relationships has been deprecated. Please call inbound_relationships_by_name instead.")
454
- inbound_relationships_by_name
455
- end
456
-
457
- # @deprecated Please use {#outbound_relationships_by_name} instead.
458
- def named_outbound_relationships
459
- ActiveSupport::Deprecation.warn("Deprecation: named_outbound_relationships has been deprecated. Please call outbound_relationships_by_name instead.")
460
- outbound_relationships_by_name
461
- end
462
-
463
- # @deprecated Please use {#outbound_relationship_predicates} instead.
464
- def outbound_named_relationship_predicates
465
- ActiveSupport::Deprecation.warn("Deprecation: outbound_named_relationship_predicates has been deprecated. Please call outbound_relationship_predicates instead.")
466
- outbound_relationship_predicates
467
- end
468
-
469
- # @deprecated Please use {#inbound_relationship_predicates} instead.
470
- def inbound_named_relationship_predicates
471
- ActiveSupport::Deprecation.warn("Deprecation: inbound_named_relationship_predicates has been deprecated. Please call inbound_relationship_predicates instead.")
472
- inbound_relationship_predicates
473
- end
474
-
475
- # @deprecated Please use {#relationship_predicates} instead.
476
- def named_relationship_predicates
477
- ActiveSupport::Deprecation.warn("Deprecation: named_relationship_predicates has been deprecated. Please call relationship_predicates instead.")
478
- relationship_predicates
479
- end
480
-
481
- # @deprecated Please use {#relationship_predicates_from_class} instead.
482
- def named_relationship_predicates_from_class
483
- ActiveSupport::Deprecation.warn("Deprecation: named_relationship_predicates_from_class has been deprecated. Please call relationship_predicates_from_class instead.")
484
- relationship_predicates_from_class
485
- end
486
-
487
- # @deprecated Please use {#is_relationship_name?} instead.
488
- def is_named_relationship?(name, outbound_only=true)
489
- ActiveSupport::Deprecation.warn("Deprecation: is_named_relationship? has been deprecated. Please call is_relationship_name? instead.")
490
- is_relationship_name?(name,outbound_only)
491
- end
492
-
493
- # @deprecated Please use {#relationships_desc} instead.
494
- def named_relationships_desc
495
- ActiveSupport::Deprecation.warn("Deprecation: named_relationships_desc has been deprecated. Please call relationships_desc instead.")
496
- relationships_desc
497
- end
498
-
499
- # @deprecated Please use {#relationships_desc_from_class} instead.
500
- def named_relationships_desc_from_class
501
- ActiveSupport::Deprecation.warn("Deprecation: named_relationships_desc_from_class has been deprecated. Please call relationships_desc_from_class instead.")
502
- relationships_desc_from_class
503
- end
504
-
505
- # @deprecated Please use {#relationship_model_type} instead.
506
- def named_relationship_type(name)
507
- ActiveSupport::Deprecation.warn("Deprecation: named_relationship_type has been deprecated. Please call relationship_model_type instead.")
508
- relationship_model_type(name)
509
- end
510
-
511
- # @deprecated Please use {#add_relationship_by_name} instead.
512
- def add_named_relationship(name,object)
513
- ActiveSupport::Deprecation.warn("Deprecation: add_named_relationship has been deprecated. Please call add_relationship_by_name instead.")
514
- add_relationship_by_name(name,object)
515
- end
516
-
517
- # @deprecated Please use {#remove_relationship_by_name} instead.
518
- def remove_named_relationship(name,object)
519
- ActiveSupport::Deprecation.warn("Deprecation: remove_named_relationship has been deprecated. Please call remove_relationship_by_name instead.")
520
- remove_relationship_by_name(name,object)
521
- end
522
-
523
- # @deprecated Please use {#assert_conforms_to} instead.
524
- def assert_kind_of_model(name,object,model_class)
525
- ActiveSupport::Deprecation.warn("Deprecation: assert_kind_of_model has been deprecated. Please call assert_conforms_to instead.")
526
- assert_conforms_to(name,object,model_class)
527
- end
528
-
529
- # @deprecated Please use {#conforms_to?} instead.
530
- def kind_of_model?(model_class)
531
- ActiveSupport::Deprecation.warn("Deprecation: kind_of_model? has been deprecated. Please call conforms_to? instead.")
532
- conforms_to?(model_class)
533
- end
534
-
535
- # @deprecated Please use {#relationship_query} instead.
536
- def named_relationship_query(relationship_name)
537
- ActiveSupport::Deprecation.warn("Deprecation: named_relationship_query has been deprecated. Please call relationship_query instead.")
538
- relationship_query(relationship_name)
539
- end
540
-
541
- module ClassMethods
542
-
543
- # ** EXPERIMENTAL **
544
- #
545
- # Return hash that persists relationship metadata defined by has_relationship calls. If you implement a child class of ActiveFedora::Base it will inherit
546
- # the relationship descriptions defined there by merging in the class
547
- # instance variable values. It will also do this for any level of
548
- # ancestors.
549
- # @return [Hash] Hash of relationship subject (:self or :inbound) mapped to nested hashs of each relationship name mapped to another hash relationship options
550
- # @example
551
- # For the following relationship
552
- #
553
- # has_relationship "audio_records", :has_part, :type=>AudioRecord
554
- #
555
- # Results in the following returned by relationships_desc
556
- # {:self=>{"audio_records"=>{:type=>AudioRecord, :singular=>nil, :predicate=>:has_part, :inbound=>false}}}
557
- def relationships_desc
558
- #get any relationship descriptions from superclasses
559
- if @class_relationships_desc.nil?
560
- @class_relationships_desc ||= Hash[:self => {}]
561
-
562
- #get super classes
563
- super_klasses = []
564
- #insert in reverse order so the child overwrites anything in parent
565
- super_klass = self.superclass
566
- while !super_klass.nil?
567
- super_klasses.insert(0,super_klass)
568
- super_klass = super_klass.superclass
569
- end
570
-
571
- super_klasses.each do |super_klass|
572
- if super_klass.respond_to?(:relationships_desc)
573
- super_rels = super_klass.relationships_desc
574
- super_rels.each_pair do |subject,rels|
575
- @class_relationships_desc[subject] = {} unless @class_relationships_desc.has_key?(subject)
576
- @class_relationships_desc[subject].merge!(rels)
577
- end
578
- end
579
- end
580
- end
581
- @class_relationships_desc
582
- end
583
-
584
- # ** EXPERIMENTAL **
585
- #
586
- # Internal method that ensures a relationship subject such as :self and :inbound
587
- # exist within the relationships_desc hash tracking relationships metadata.
588
- # @param [Symbol] Subject name to register (will probably be something like :self or :inbound)
589
- def register_relationship_desc_subject(subject)
590
- unless relationships_desc.has_key?(subject)
591
- relationships_desc[subject] = {}
592
- end
593
- end
594
-
595
- # ** EXPERIMENTAL **
596
- #
597
- # Internal method that adds relationship name and predicate pair to either an outbound (:self)
598
- # or inbound (:inbound) relationship types. Refer to ActiveFedora::SemanticNode.has_relationship for information on what metadata will be persisted.
599
- # @param [Symbol] Subject name to register
600
- # @param [String] Name of relationship being registered
601
- # @param [Symbol] Fedora ontology predicate to use
602
- # @param [Hash] Any options passed to has_relationship such as :type, :solr_fq, etc.
603
- def register_relationship_desc(subject, name, predicate, opts={})
604
- register_relationship_desc_subject(subject)
605
- opts.merge!({:predicate=>predicate})
606
- relationships_desc[subject][name] = opts
607
- end
608
-
609
- # Tests if the relationship name passed is in bidirectional
610
- # @param [String] relationship name to test
611
- # @return [Boolean]
612
- def is_bidirectional_relationship?(relationship_name)
613
- (relationships_desc.has_key?(:self)&&relationships_desc.has_key?(:inbound)&&relationships_desc[:self].has_key?("#{relationship_name}_outbound") && relationships_desc[:inbound].has_key?("#{relationship_name}_inbound"))
614
- end
615
-
616
- # ** EXPERIMENTAL **
617
- #
618
- # Used in has_relationship call to create dynamic helper methods to
619
- # append and remove objects to and from a relationship
620
- # @param [String] relationship name to create helper methods for
621
- # @example
622
- # For the following relationship
623
- #
624
- # has_relationship "audio_records", :has_part, :type=>AudioRecord
625
- #
626
- # Methods audio_records_append and audio_records_remove are created.
627
- # Boths methods take an object that is kind_of? ActiveFedora::Base as a parameter
628
- def create_relationship_name_methods(name)
629
- append_method_name = "#{name.to_s.downcase}_append"
630
- remove_method_name = "#{name.to_s.downcase}_remove"
631
- self.send(:define_method,:"#{append_method_name}") {|object| add_relationship_by_name(name,object)}
632
- self.send(:define_method,:"#{remove_method_name}") {|object| remove_relationship_by_name(name,object)}
633
- end
634
-
635
- # ** EXPERIMENTAL **
636
- # Similar to +create_relationship_name_methods+ except it is used when an ActiveFedora::Base model class
637
- # declares has_bidirectional_relationship. we are merely creating an alias for outbound portion of bidirectional
638
- # @param [String] bidirectional relationship name
639
- # @param [String] outbound relationship method name associated with the bidirectional relationship ([bidirectional_name]_outbound)
640
- # @example
641
- # has_bidirectional_relationship "members", :has_collection_member, :is_member_of_collection
642
- #
643
- # Method members_outbound_append and members_outbound_remove added
644
- # This method will create members_append which does same thing as members_outbound_append
645
- # and will create members_remove which does same thing as members_outbound_remove
646
- def create_bidirectional_relationship_name_methods(name,outbound_name)
647
- append_method_name = "#{name.to_s.downcase}_append"
648
- remove_method_name = "#{name.to_s.downcase}_remove"
649
- self.send(:define_method,:"#{append_method_name}") {|object| add_relationship_by_name(outbound_name,object)}
650
- self.send(:define_method,:"#{remove_method_name}") {|object| remove_relationship_by_name(outbound_name,object)}
651
- end
652
-
653
- # Returns a solr query for retrieving objects specified in an outbound relationship.
654
- # This method is mostly used by internal method calls.
655
- # It utilizes any solr_fq value defined within a relationship to attach a query filter when
656
- # querying solr on top of just the predicate being used.
657
- # Because it is static it
658
- # needs the pids defined within RELS-EXT for this relationship to be passed in.
659
- # If you are calling this method directly to get the query you should use the
660
- # ActiveFedora::SemanticNode.relationship_query instead or use the helper method
661
- # [relationship_name]_query, i.e. method "parts_query" for relationship "parts". This
662
- # method would only be called directly if you had something like an array of outbound pids
663
- # already in something like a solr document for object that has these relationships.
664
- # @param [String] The name of the relationship defined in the model
665
- # @param [Array] An array of pids to include in the query
666
- # @return [String]
667
- # @example
668
- # Class SampleAFObjRelationshipFilterQuery < ActiveFedora::Base
669
- # #points to all parents linked via is_member_of
670
- # has_relationship "parents", :is_member_of
671
- # #returns only parents that have a level value set to "series"
672
- # has_relationship "series_parents", :is_member_of, :solr_fq=>"level_t:series"
673
- # end
674
- # s = SampleAFObjRelationshipFilterQuery.new
675
- # obj = ActiveFedora::Base.new
676
- # s.series_parents_append(obj)
677
- # s.series_parents_query
678
- # #=> "(id:changeme\\:13020 AND level_t:series)"
679
- # SampleAFObjRelationshipFilterQuery.outbound_relationship_query("series_parents",["id:changeme:13020"])
680
- # #=> "(id:changeme\\:13020 AND level_t:series)"
681
- def outbound_relationship_query(relationship_name,outbound_pids)
682
- query = ActiveFedora::SolrService.construct_query_for_pids(outbound_pids)
683
- subject = :self
684
- if relationships_desc.has_key?(subject) && relationships_desc[subject].has_key?(relationship_name) && relationships_desc[subject][relationship_name].has_key?(:solr_fq)
685
- solr_fq = relationships_desc[subject][relationship_name][:solr_fq]
686
- unless query.empty?
687
- #substitute in the filter query for each pid so that it is applied to each in the query
688
- query_parts = query.split(/OR/)
689
- query = ""
690
- query_parts.each_with_index do |query_part,index|
691
- query_part.strip!
692
- query << " OR " if index > 0
693
- query << "(#{query_part} AND #{solr_fq})"
694
- end
695
- else
696
- query = solr_fq
697
- end
698
- end
699
- query
700
- end
701
-
702
- # Returns a solr query for retrieving objects specified in an inbound relationship.
703
- # This method is mostly used by internal method calls.
704
- # It utilizes any solr_fq value defined within a relationship to attach a query filter
705
- # on top of just the predicate being used. Because it is static it
706
- # needs the pid of the object that has the inbound relationships passed in.
707
- # If you are calling this method directly to get the query you should use the
708
- # ActiveFedora::SemanticNode.relationship_query instead or use the helper method
709
- # [relationship_name]_query, i.e. method "parts_query" for relationship "parts". This
710
- # method would only be called directly if you were working only with Solr and already
711
- # had the pid for the object in something like a solr document.
712
- # @param [String] The pid for the object that has these inbound relationships
713
- # @param [String] The name of the relationship defined in the model
714
- # @return [String]
715
- # @example
716
- # Class SampleAFObjRelationshipFilterQuery < ActiveFedora::Base
717
- # #returns all parts
718
- # has_relationship "parts", :is_part_of, :inbound=>true
719
- # #returns only parts that have level to "series"
720
- # has_relationship "series_parts", :is_part_of, :inbound=>true, :solr_fq=>"level_t:series"
721
- # end
722
- # s = SampleAFObjRelationshipFilterQuery.new
723
- # s.pid
724
- # #=> id:changeme:13020
725
- # s.series_parts_query
726
- # #=> "is_part_of_s:info\\:fedora/changeme\\:13021 AND level_t:series"
727
- # SampleAFObjRelationshipFilterQuery.inbound_relationship_query(s.pid,"series_parts")
728
- # #=> "is_part_of_s:info\\:fedora/changeme\\:13021 AND level_t:series"
729
- def inbound_relationship_query(pid,relationship_name)
730
- query = ""
731
- subject = :inbound
732
- if relationships_desc.has_key?(subject) && relationships_desc[subject].has_key?(relationship_name)
733
- predicate = relationships_desc[subject][relationship_name][:predicate]
734
- internal_uri = "info:fedora/#{pid}"
735
- escaped_uri = internal_uri.gsub(/(:)/, '\\:')
736
- query = "#{predicate}_s:#{escaped_uri}"
737
- if relationships_desc.has_key?(subject) && relationships_desc[subject].has_key?(relationship_name) && relationships_desc[subject][relationship_name].has_key?(:solr_fq)
738
- solr_fq = relationships_desc[subject][relationship_name][:solr_fq]
739
- query << " AND " unless query.empty?
740
- query << solr_fq
741
- end
742
- end
743
- query
744
- end
745
-
746
- # Returns a solr query for retrieving objects specified in a bidirectional relationship.
747
- # This method is mostly used by internal method calls.
748
- # It usea of solr_fq value defined within a relationship to attach a query filter
749
- # on top of just the predicate being used. Because it is static it
750
- # needs the pids defined within RELS-EXT for the outbound relationship as well as the pid of the
751
- # object for the inbound portion of the relationship.
752
- # If you are calling this method directly to get the query you should use the
753
- # ActiveFedora::SemanticNode.relationship_query instead or use the helper method
754
- # [relationship_name]_query, i.e. method "bi_parts_query" for relationship "bi_parts". This
755
- # method would only be called directly if you had something like an array of outbound pids
756
- # already in something like a solr document for object that has these relationships.
757
- # @param [String] The pid for the object that has these inbound relationships
758
- # @param [String] The name of the relationship defined in the model
759
- # @param [Array] An array of pids to include in the query
760
- # @return [String]
761
- # @example
762
- # Class SampleAFObjRelationshipFilterQuery < ActiveFedora::Base
763
- # has_bidirectional_relationship "bi_series_parts", :has_part, :is_part_of, :solr_fq=>"level_t:series"
764
- # end
765
- # s = SampleAFObjRelationshipFilterQuery.new
766
- # obj = ActiveFedora::Base.new
767
- # s.bi_series_parts_append(obj)
768
- # s.pid
769
- # #=> "changeme:13025"
770
- # obj.pid
771
- # #=> id:changeme:13026
772
- # s.bi_series_parts_query
773
- # #=> "(id:changeme\\:13026 AND level_t:series) OR (is_part_of_s:info\\:fedora/changeme\\:13025 AND level_t:series)"
774
- # SampleAFObjRelationshipFilterQuery.bidirectional_relationship_query(s.pid,"series_parents",["id:changeme:13026"])
775
- # #=> "(id:changeme\\:13026 AND level_t:series) OR (is_part_of_s:info\\:fedora/changeme\\:13025 AND level_t:series)"
776
- def bidirectional_relationship_query(pid,relationship_name,outbound_pids)
777
- outbound_query = outbound_relationship_query("#{relationship_name}_outbound",outbound_pids)
778
- inbound_query = inbound_relationship_query(pid,"#{relationship_name}_inbound")
779
- query = outbound_query # use outbound_query by default
780
- if !inbound_query.empty?
781
- query << " OR (" + inbound_relationship_query(pid,"#{relationship_name}_inbound") + ")"
782
- end
783
- return query
784
- end
785
-
786
- # Check if a relationship has any solr query filters defined by has_relationship call
787
- # @param [Symbol] subject to use such as :self or :inbound
788
- # @param [String] relationship name
789
- # @return [Boolean] true if the relationship has a query filter defined
790
- def relationship_has_solr_filter_query?(subject, relationship_name)
791
- relationships_desc.has_key?(subject) && relationships_desc[subject].has_key?(relationship_name) && relationships_desc[subject][relationship_name].has_key?(:solr_fq)
792
- end
793
-
794
- # ** EXPERIMENTAL **
795
- #
796
- # Check to make sure a subject,name, and predicate triple does not already exist
797
- # with the same subject but different name.
798
- # This method is used to ensure conflicting has_relationship calls are not made because
799
- # predicates cannot be reused across relationship names. Otherwise, the mapping of relationship name
800
- # to predicate in RELS-EXT would be broken.
801
- # @param [Symbol] subject to check (:self or :inbound)
802
- # @param [String] relationship name
803
- # @param [Symbol] symbol for Fedora relationship ontology predicate
804
- def predicate_exists_with_different_relationship_name?(subject,name,predicate)
805
- if relationships_desc.has_key?(subject)
806
- relationships_desc[subject].each_pair do |existing_name, args|
807
- return true if !args[:predicate].nil? && args[:predicate] == predicate && existing_name != name
808
- end
809
- end
810
- return false
811
- end
812
-
813
- ## Deprecated class method checks for HYDRA-541 methods renamed
814
- #
815
- # Old Name New Name
816
- # named_relationships_desc relationships_desc
817
- # register_named_subject register_relationship_desc_subject
818
- # register_named_relationship register_relationship_desc
819
- # create_named_relationship_method create_relationship_name_methods
820
- # create_bidirectional_named_relationship_methods create_bidirectional_relationship_name_methods
821
- # outbound_named_relationship_query outbound_relationship_query
822
- # inbound_named_relationship_query inbound_relationship_query
823
- # bidirectional_named_relationship_query bidirectional_relationship_query
824
- # named_predicate_exists_with_different_name? predicate_exists_with_different_relationship_name?
825
-
826
- # @deprecated Please use {#relationships_desc} instead.
827
- def named_relationships_desc
828
- ActiveSupport::Deprecation.warn("Deprecation: named_relationships_desc has been deprecated. Please call relationships_desc instead.")
829
- relationships_desc
830
- end
831
-
832
- # @deprecated Please use {#register_relationship_desc_subject} instead.
833
- def register_named_subject(subject)
834
- ActiveSupport::Deprecation.warn("Deprecation: register_named_subject has been deprecated. Please call register_relationship_desc_subject instead.")
835
- register_relationship_desc_subject(subject)
836
- end
837
-
838
- # @deprecated Please use {#register_relationship_desc} instead.
839
- def register_named_relationship(subject, name, predicate, opts)
840
- ActiveSupport::Deprecation.warn("Deprecation: register_named_relationship has been deprecated. Please call register_relationship_desc instead.")
841
- register_relationship_desc(subject, name, predicate, opts)
842
- end
843
-
844
- # @deprecated Please use {#create_relationship_name_methods} instead.
845
- def create_named_relationship_methods(name)
846
- ActiveSupport::Deprecation.warn("Deprecation: create_named_relationship_methods has been deprecated. Please call create_relationship_name_methods instead.")
847
- create_relationship_name_methods(name)
848
- end
849
-
850
- # @deprecated Please use {#create_bidirectional_relationship_name_methods} instead.
851
- def create_bidirectional_named_relationship_methods(name,outbound_name)
852
- ActiveSupport::Deprecation.warn("Deprecation: create_bidirectional_named_relationship_methods has been deprecated. Please call create_bidirectional_relationship_name_methods instead.")
853
- create_bidirectional_relationship_name_methods(name,outbound_name)
854
- end
855
-
856
- # @deprecated Please use {#outbound_relationship_query} instead.
857
- def outbound_named_relationship_query(relationship_name,outbound_pids)
858
- ActiveSupport::Deprecation.warn("Deprecation: outbound_named_relationship_query has been deprecated. Please call outbound_relationship_query instead.")
859
- outbound_relationship_query(relationship_name,outbound_pids)
860
- end
861
-
862
- # @deprecated Please use {#inbound_relationship_query} instead.
863
- def inbound_named_relationship_query(pid,relationship_name)
864
- ActiveSupport::Deprecation.warn("Deprecation: inbound_named_relationship_query has been deprecated. Please call inbound_relationship_query instead.")
865
- inbound_relationship_query(pid,relationship_name)
866
- end
867
-
868
- # @deprecated Please use {#bidirectional_relationship_query} instead.
869
- def bidirectional_named_relationship_query(pid,relationship_name,outbound_pids)
870
- ActiveSupport::Deprecation.warn("Deprecation: bidirectional_named_relationship_query has been deprecated. Please call bidirectional_relationship_query instead.")
871
- bidirectional_relationship_query(pid,relationship_name,outbound_pids)
872
- end
873
-
874
- # @deprecated Please use {#predicate_exists_with_different_relationship_name?} instead.
875
- def named_predicate_exists_with_different_name?(subject,name,predicate)
876
- ActiveSupport::Deprecation.warn("Deprecation: named_predicate_exists_with_different_name? has been deprecated. Please call predicate_exists_with_different_relationship_name? instead.")
877
- predicate_exists_with_different_relationship_name?(subject,name,predicate)
878
- end
879
- end
880
- end
881
- end