active-fedora 9.1.2 → 9.2.0.rc1

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.
Files changed (101) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -1
  3. data/Gemfile +1 -0
  4. data/History.txt +90 -0
  5. data/active-fedora.gemspec +2 -2
  6. data/lib/active_fedora.rb +17 -3
  7. data/lib/active_fedora/associations.rb +77 -0
  8. data/lib/active_fedora/associations/association.rb +2 -2
  9. data/lib/active_fedora/associations/basic_contains_association.rb +52 -0
  10. data/lib/active_fedora/associations/builder/directly_contains.rb +23 -0
  11. data/lib/active_fedora/associations/builder/directly_contains_one.rb +44 -0
  12. data/lib/active_fedora/associations/builder/has_and_belongs_to_many.rb +2 -23
  13. data/lib/active_fedora/associations/builder/indirectly_contains.rb +26 -0
  14. data/lib/active_fedora/associations/builder/property.rb +10 -0
  15. data/lib/active_fedora/associations/collection_association.rb +42 -45
  16. data/lib/active_fedora/associations/collection_proxy.rb +6 -0
  17. data/lib/active_fedora/associations/contained_finder.rb +41 -0
  18. data/lib/active_fedora/associations/container_proxy.rb +9 -0
  19. data/lib/active_fedora/associations/contains_association.rb +20 -38
  20. data/lib/active_fedora/associations/delete_proxy.rb +28 -0
  21. data/lib/active_fedora/associations/directly_contains_association.rb +56 -0
  22. data/lib/active_fedora/associations/directly_contains_one_association.rb +113 -0
  23. data/lib/active_fedora/associations/has_and_belongs_to_many_association.rb +6 -14
  24. data/lib/active_fedora/associations/has_many_association.rb +0 -3
  25. data/lib/active_fedora/associations/id_composite.rb +30 -0
  26. data/lib/active_fedora/associations/indirectly_contains_association.rb +90 -0
  27. data/lib/active_fedora/associations/rdf.rb +8 -4
  28. data/lib/active_fedora/associations/record_composite.rb +39 -0
  29. data/lib/active_fedora/attached_files.rb +1 -1
  30. data/lib/active_fedora/attributes.rb +28 -10
  31. data/lib/active_fedora/attributes/active_triple_attribute.rb +17 -0
  32. data/lib/active_fedora/attributes/om_attribute.rb +29 -0
  33. data/lib/active_fedora/attributes/rdf_datastream_attribute.rb +47 -0
  34. data/lib/active_fedora/attributes/stream_attribute.rb +46 -0
  35. data/lib/active_fedora/autosave_association.rb +2 -2
  36. data/lib/active_fedora/base.rb +3 -0
  37. data/lib/active_fedora/containers/container.rb +38 -0
  38. data/lib/active_fedora/containers/direct_container.rb +5 -0
  39. data/lib/active_fedora/containers/indirect_container.rb +7 -0
  40. data/lib/active_fedora/core.rb +4 -48
  41. data/lib/active_fedora/delegated_attribute.rb +5 -98
  42. data/lib/active_fedora/fedora.rb +1 -1
  43. data/lib/active_fedora/fedora_attributes.rb +4 -26
  44. data/lib/active_fedora/file.rb +87 -159
  45. data/lib/active_fedora/file/attributes.rb +63 -0
  46. data/lib/active_fedora/file/streaming.rb +46 -0
  47. data/lib/active_fedora/file_relation.rb +7 -0
  48. data/lib/active_fedora/identifiable.rb +87 -0
  49. data/lib/active_fedora/inbound_relation_connection.rb +21 -0
  50. data/lib/active_fedora/indexers.rb +10 -0
  51. data/lib/active_fedora/indexers/global_indexer.rb +30 -0
  52. data/lib/active_fedora/indexers/null_indexer.rb +12 -0
  53. data/lib/active_fedora/indexing/map.rb +4 -5
  54. data/lib/active_fedora/indexing_service.rb +3 -22
  55. data/lib/active_fedora/loadable_from_json.rb +8 -0
  56. data/lib/active_fedora/pathing.rb +24 -0
  57. data/lib/active_fedora/persistence.rb +15 -8
  58. data/lib/active_fedora/rdf.rb +2 -0
  59. data/lib/active_fedora/rdf/fcrepo4.rb +1 -0
  60. data/lib/active_fedora/rdf/field_map.rb +90 -0
  61. data/lib/active_fedora/rdf/field_map_entry.rb +28 -0
  62. data/lib/active_fedora/rdf/indexing_service.rb +9 -23
  63. data/lib/active_fedora/rdf/rdf_datastream.rb +1 -2
  64. data/lib/active_fedora/reflection.rb +16 -15
  65. data/lib/active_fedora/relation/delegation.rb +15 -4
  66. data/lib/active_fedora/relation/finder_methods.rb +4 -4
  67. data/lib/active_fedora/schema.rb +26 -0
  68. data/lib/active_fedora/schema_indexing_strategy.rb +25 -0
  69. data/lib/active_fedora/simple_datastream.rb +2 -2
  70. data/lib/active_fedora/solr_query_builder.rb +3 -2
  71. data/lib/active_fedora/version.rb +1 -1
  72. data/lib/active_fedora/with_metadata.rb +1 -1
  73. data/spec/integration/associations/rdf_spec.rb +49 -0
  74. data/spec/integration/base_spec.rb +19 -0
  75. data/spec/integration/belongs_to_association_spec.rb +6 -6
  76. data/spec/integration/collection_association_spec.rb +4 -4
  77. data/spec/integration/complex_rdf_datastream_spec.rb +12 -12
  78. data/spec/integration/datastream_rdf_nested_attributes_spec.rb +1 -1
  79. data/spec/integration/direct_container_spec.rb +254 -0
  80. data/spec/integration/directly_contains_one_association_spec.rb +102 -0
  81. data/spec/integration/file_spec.rb +16 -5
  82. data/spec/integration/has_many_associations_spec.rb +93 -58
  83. data/spec/integration/indirect_container_spec.rb +251 -0
  84. data/spec/integration/rdf_nested_attributes_spec.rb +1 -1
  85. data/spec/integration/relation_spec.rb +43 -27
  86. data/spec/spec_helper.rb +1 -1
  87. data/spec/unit/attributes_spec.rb +6 -6
  88. data/spec/unit/collection_proxy_spec.rb +28 -0
  89. data/spec/unit/file_spec.rb +1 -1
  90. data/spec/unit/files_hash_spec.rb +4 -4
  91. data/spec/unit/has_and_belongs_to_many_association_spec.rb +11 -9
  92. data/spec/unit/indexers/global_indexer_spec.rb +41 -0
  93. data/spec/unit/indexing_service_spec.rb +0 -21
  94. data/spec/unit/loadable_from_json_spec.rb +31 -0
  95. data/spec/unit/pathing_spec.rb +37 -0
  96. data/spec/unit/rdf/indexing_service_spec.rb +3 -3
  97. data/spec/unit/rdf_resource_datastream_spec.rb +26 -7
  98. data/spec/unit/schema_indexing_strategy_spec.rb +68 -0
  99. data/spec/unit/solr_query_builder_spec.rb +1 -1
  100. data/spec/unit/solr_service_spec.rb +1 -1
  101. metadata +49 -8
@@ -1,11 +1,13 @@
1
1
  module ActiveFedora::Associations::Builder
2
2
  class HasAndBelongsToMany < CollectionAssociation #:nodoc:
3
+ extend Deprecation
3
4
  self.macro = :has_and_belongs_to_many
4
5
 
5
6
  self.valid_options += [:inverse_of, :solr_page_size]
6
7
 
7
8
  def validate_options
8
9
  super
10
+ Deprecation.warn HasAndBelongsToMany, ":solr_page_size doesn't do anything anymore and will be removed in ActiveFedora 10" if options.key?(:solr_page_size)
9
11
  if !options[:predicate]
10
12
  raise "You must specify a predicate for #{name}"
11
13
  elsif !options[:predicate].kind_of?(RDF::URI)
@@ -13,28 +15,5 @@ module ActiveFedora::Associations::Builder
13
15
  end
14
16
  end
15
17
 
16
- def build
17
- reflection = super
18
- define_destroy_hook
19
- reflection
20
- end
21
-
22
- private
23
-
24
- def define_destroy_hook
25
- # Don't use a before_destroy callback since users' before_destroy
26
- # callbacks will be executed after the association is wiped out.
27
- # TODO Update to destroy_associations
28
- name = self.name
29
- model.send(:include, Module.new {
30
- class_eval <<-RUBY, __FILE__, __LINE__ + 1
31
- def destroy # def destroy
32
- #{name}.clear # posts.clear
33
- super # super
34
- end # end
35
- RUBY
36
- })
37
- end
38
-
39
18
  end
40
19
  end
@@ -0,0 +1,26 @@
1
+ module ActiveFedora::Associations::Builder
2
+ class IndirectlyContains < CollectionAssociation #:nodoc:
3
+ self.macro = :indirectly_contains
4
+ self.valid_options += [:has_member_relation, :is_member_of_relation, :inserted_content_relation, :foreign_key, :through]
5
+ self.valid_options -= [:predicate]
6
+
7
+ def build
8
+ reflection = super
9
+ configure_dependency
10
+ reflection
11
+ end
12
+
13
+ def validate_options
14
+ super
15
+ if !options[:has_member_relation] && !options[:is_member_of_relation]
16
+ raise ArgumentError, "You must specify a predicate for #{name}"
17
+ elsif !options[:has_member_relation].kind_of?(RDF::URI) && !options[:is_member_of_relation].kind_of?(RDF::URI)
18
+ raise ArgumentError, "Predicate must be a kind of RDF::URI"
19
+ end
20
+
21
+ raise ArgumentError, "Missing :through option" if !options[:through]
22
+ raise ArgumentError, "Missing :foreign_key option" if !options[:foreign_key]
23
+ end
24
+ end
25
+ end
26
+
@@ -9,6 +9,16 @@ module ActiveFedora::Associations::Builder
9
9
  @name = :"#{name.to_s.singularize}_ids"
10
10
  end
11
11
 
12
+ def build
13
+ super.tap do |reflection|
14
+ model.index_config[name] = build_index_config(reflection)
15
+ end
16
+ end
17
+
18
+ def build_index_config(reflection)
19
+ ActiveFedora::Indexing::Map::IndexObject.new(reflection.predicate_for_solr) { |index| index.as :symbol }
20
+ end
21
+
12
22
  end
13
23
  end
14
24
 
@@ -47,8 +47,8 @@ module ActiveFedora
47
47
  end
48
48
 
49
49
  def reset
50
- reset_target!
51
- @loaded = false
50
+ super
51
+ @target = []
52
52
  end
53
53
 
54
54
  def find(*args)
@@ -239,51 +239,13 @@ module ActiveFedora
239
239
 
240
240
  def load_target
241
241
  if find_target?
242
- targets = []
243
-
244
- begin
245
- targets = find_target
246
- rescue ObjectNotFoundError, Ldp::Gone => e
247
- ActiveFedora::Base.logger.error "Solr and Fedora may be out of sync:\n" + e.message if ActiveFedora::Base.logger
248
- reset
249
- end
250
-
251
- @target = merge_target_lists(targets, @target)
242
+ @target = merge_target_lists(find_target, @target)
252
243
  end
253
244
 
254
245
  loaded!
255
246
  target
256
247
  end
257
248
 
258
- def find_target
259
- # TODO: don't reify, just store the solr results and lazily reify.
260
- # For now, we set a hard limit of 1000 results.
261
- records = ActiveFedora::QueryResultBuilder.reify_solr_results(load_from_solr(rows: 1000))
262
- records.each { |record| set_inverse_instance(record) }
263
- records
264
- end
265
-
266
- def merge_target_lists(loaded, existing)
267
- return loaded if existing.empty?
268
- return existing if loaded.empty?
269
-
270
- loaded.map do |f|
271
- i = existing.index(f)
272
- if i
273
- existing.delete_at(i).tap do |t|
274
- keys = ["id"] + t.changes.keys + (f.attribute_names - t.attribute_names)
275
- # FIXME: this call to attributes causes many NoMethodErrors
276
- attributes = f.attributes
277
- (attributes.keys - keys).each do |k|
278
- t.send("#{k}=", attributes[k])
279
- end
280
- end
281
- else
282
- f
283
- end
284
- end + existing
285
- end
286
-
287
249
  # @param opts [Hash] Options that will be passed through to ActiveFedora::SolrService.query.
288
250
  def load_from_solr(opts = Hash.new)
289
251
  finder_query = construct_query
@@ -293,6 +255,7 @@ module ActiveFedora
293
255
  SolrService.query(finder_query, { rows: rows }.merge(opts))
294
256
  end
295
257
 
258
+
296
259
  def add_to_target(record, skip_callbacks = false)
297
260
  # transaction do
298
261
  callback(:before_add, record) unless skip_callbacks
@@ -320,14 +283,11 @@ module ActiveFedora
320
283
  def null_scope?
321
284
  owner.new_record? && !foreign_key_present?
322
285
  end
286
+
323
287
  protected
324
- def reset_target!
325
- @target = Array.new
326
- end
327
288
 
328
289
  def construct_query
329
290
  @solr_query ||= begin
330
- #TODO use primary_key instead of id
331
291
  clauses = { find_reflection => @owner.id }
332
292
  clauses[:has_model] = @reflection.class_name.constantize.to_class_uri if @reflection.class_name && @reflection.class_name != 'ActiveFedora::Base'
333
293
  ActiveFedora::SolrQueryBuilder.construct_query_for_rel(clauses)
@@ -337,6 +297,39 @@ module ActiveFedora
337
297
 
338
298
  private
339
299
 
300
+ def find_target
301
+ # TODO: don't reify, just store the solr results and lazily reify.
302
+ # For now, we set a hard limit of 1000 results.
303
+ records = ActiveFedora::QueryResultBuilder.reify_solr_results(load_from_solr(rows: 1000))
304
+ records.each { |record| set_inverse_instance(record) }
305
+ records
306
+ rescue ObjectNotFoundError, Ldp::Gone => e
307
+ ActiveFedora::Base.logger.error "Solr and Fedora may be out of sync:\n" + e.message if ActiveFedora::Base.logger
308
+ reset
309
+ []
310
+ end
311
+
312
+ def merge_target_lists(loaded, existing)
313
+ return loaded if existing.empty?
314
+ return existing if loaded.empty?
315
+
316
+ loaded.map do |f|
317
+ i = existing.index(f)
318
+ if i
319
+ existing.delete_at(i).tap do |t|
320
+ keys = ["id"] + t.changes.keys + (f.attribute_names - t.attribute_names)
321
+ # FIXME: this call to attributes causes many NoMethodErrors
322
+ attributes = f.attributes
323
+ (attributes.keys - keys).each do |k|
324
+ t.send("#{k}=", attributes[k])
325
+ end
326
+ end
327
+ else
328
+ f
329
+ end
330
+ end + existing
331
+ end
332
+
340
333
  def find_reflection
341
334
  return reflection if @reflection.options[:predicate]
342
335
  if @reflection.class_name && @reflection.class_name != 'ActiveFedora::Base' && @reflection.macro != :has_and_belongs_to_many
@@ -366,8 +359,12 @@ module ActiveFedora
366
359
  existing_records = records.select { |r| r.persisted? }
367
360
 
368
361
  records.each { |record| callback(:before_remove, record) }
362
+
363
+ # Delete the record from Fedora.
369
364
  delete_records(existing_records, method) if existing_records.any?
365
+
370
366
  records.each do |record|
367
+ # Remove the record from the array/collection.
371
368
  target.delete(record)
372
369
  end
373
370
 
@@ -842,6 +842,12 @@ module ActiveFedora
842
842
  @association
843
843
  end
844
844
 
845
+ # @return [Relation] object for the records in this association
846
+ def scope
847
+ @association.scope
848
+ end
849
+ alias spawn scope
850
+
845
851
  def to_ary
846
852
  load_target.dup
847
853
  end
@@ -0,0 +1,41 @@
1
+ module ActiveFedora::Associations
2
+ ##
3
+ # Finds the objects which associate with a given record and are contained
4
+ # within the given container. Uses #repository to find the objects.
5
+ class ContainedFinder
6
+ attr_reader :container, :repository
7
+ delegate :contained_ids, to: :container
8
+ # @param [#contained_ids] container a container that records are stored
9
+ # under.
10
+ # @param [#translate_uri_to_id, #find] repository a repository to build
11
+ # objects from.
12
+ def initialize(container:, repository:)
13
+ @container = container
14
+ @repository = repository
15
+ end
16
+
17
+ # @param [ActiveFedora::Base] record a record which you want to find the
18
+ # reference node for.
19
+ # @return [Array<ActiveFedora::Base>] This returns whatever type
20
+ # repository.find returns.
21
+ def find(record)
22
+ record.reload
23
+ repository.find(matching_ids(record))
24
+ end
25
+
26
+ private
27
+
28
+ def matching_ids(record)
29
+ IDComposite.new(proxy_ids(record) & contained_ids, repository.translate_uri_to_id)
30
+ end
31
+
32
+ def proxy_ids(record)
33
+ relation_subjects(record)
34
+ end
35
+
36
+ def relation_subjects(record)
37
+ record.resource.query(object: record.rdf_subject).subjects.to_a
38
+ end
39
+ end
40
+
41
+ end
@@ -0,0 +1,9 @@
1
+ module ActiveFedora
2
+ module Associations
3
+ class ContainerProxy < CollectionProxy
4
+ def initialize(association)
5
+ @association = association
6
+ end
7
+ end
8
+ end
9
+ end
@@ -1,52 +1,34 @@
1
+ # This is the parent class of DirectlyContainsAssociation and IndirectlyContainsAssociation
1
2
  module ActiveFedora
2
3
  module Associations
3
- class ContainsAssociation < SingularAssociation #:nodoc:
4
- # Implements the reader method, e.g. foo.bar for Foo.has_one :bar
5
- def reader(force_reload = false)
6
- super || build
7
- end
8
-
9
- def find_target
10
- reflection.build_association(target_uri).tap do |record|
11
- configure_datastream(record) if reflection.options[:block]
12
- end
13
- end
14
-
15
- def target_uri
16
- "#{owner.uri}/#{reflection.name}"
17
- end
18
-
19
- private
4
+ class ContainsAssociation < CollectionAssociation #:nodoc:
20
5
 
21
- def raise_on_type_mismatch(record)
22
- return if record.is_a? LoadableFromJson::SolrBackedMetadataFile
23
- super
6
+ def reader
7
+ @records ||= ContainerProxy.new(self)
24
8
  end
25
9
 
26
- def replace(record)
27
- if record
28
- raise_on_type_mismatch(record)
29
- @updated = true
10
+ def include?(other)
11
+ if loaded?
12
+ target.include?(other)
13
+ elsif container_predicate = options[:has_member_relation]
14
+ owner.resource.query(predicate: container_predicate, object: ::RDF::URI(other.uri)).present?
15
+ else #is_member_of_relation
16
+ # This will force a load, so it's slowest and the least preferable option
17
+ target.include?(other)
30
18
  end
31
-
32
- self.target = record
33
19
  end
34
20
 
35
- def new_record(method, attributes)
36
- record = super
37
- configure_datastream(record)
38
- record
39
- end
21
+ protected
40
22
 
41
- def configure_datastream(record)
42
- # If you called has_metadata with a block, pass the block into the File class
43
- if reflection.options[:block].class == Proc
44
- reflection.options[:block].call(record)
23
+ def count_records
24
+ load_target.size
45
25
  end
46
- if record.new_record? && reflection.options[:autocreate]
47
- record.datastream_will_change!
26
+
27
+ def uri
28
+ raise "Can't get uri. Owner isn't saved" if @owner.new_record?
29
+ "#{@owner.uri}/#{@reflection.name}"
48
30
  end
49
- end
50
31
  end
51
32
  end
52
33
  end
34
+
@@ -0,0 +1,28 @@
1
+ module ActiveFedora::Associations
2
+ class DeleteProxy
3
+ def self.call(proxy_ids:, proxy_class:)
4
+ new(proxy_ids: proxy_ids, proxy_class: proxy_class).run
5
+ end
6
+ attr_reader :proxy_ids, :proxy_class
7
+
8
+ def initialize(proxy_ids:, proxy_class:)
9
+ @proxy_ids = proxy_ids
10
+ @proxy_class = proxy_class
11
+ end
12
+
13
+ def run
14
+ proxies.each(&:delete)
15
+ end
16
+
17
+ private
18
+
19
+ def proxies
20
+ @proxies ||= proxy_ids.map{ |uri| uri_to_proxy(uri) }
21
+ end
22
+
23
+ def uri_to_proxy(uri)
24
+ proxy_class.find(proxy_class.uri_to_id(uri))
25
+ end
26
+
27
+ end
28
+ end
@@ -0,0 +1,56 @@
1
+ module ActiveFedora
2
+ module Associations
3
+ class DirectlyContainsAssociation < ContainsAssociation #:nodoc:
4
+
5
+ def insert_record(record, force = true, validate = true)
6
+ container.save!
7
+ if force
8
+ record.save!
9
+ else
10
+ record.save(validate: validate)
11
+ end
12
+ end
13
+
14
+ def find_target
15
+ query_node = if container_predicate = options[:has_member_relation]
16
+ owner
17
+ else
18
+ container_predicate = ::RDF::Vocab::LDP.contains
19
+ container
20
+ end
21
+
22
+ uris = query_node.resource.query(predicate: container_predicate).map { |r| r.object.to_s }
23
+
24
+ uris.map { |object_uri| klass.find(klass.uri_to_id(object_uri)) }
25
+ end
26
+
27
+ def container
28
+ @container ||= begin
29
+ DirectContainer.find_or_initialize(ActiveFedora::Base.uri_to_id(uri)).tap do |container|
30
+ container.parent = @owner
31
+ container.has_member_relation = Array(options[:has_member_relation])
32
+ container.is_member_of_relation = Array(options[:is_member_of_relation])
33
+ end
34
+ end
35
+ end
36
+
37
+ protected
38
+
39
+ def initialize_attributes(record) #:nodoc:
40
+ record.uri = ActiveFedora::Base.id_to_uri(container.mint_id)
41
+ set_inverse_instance(record)
42
+ end
43
+
44
+ private
45
+
46
+ def delete_records(records, method)
47
+ if method == :destroy
48
+ records.each { |r| r.destroy }
49
+ else
50
+ records.each { |r| r.delete }
51
+ end
52
+ end
53
+
54
+ end
55
+ end
56
+ end