active-fedora 9.1.2 → 9.2.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
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