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
@@ -0,0 +1,17 @@
1
+ module ActiveFedora
2
+
3
+ # Attributes delegated to ActiveTriples. Allows ActiveFedora to track all attributes consistently.
4
+ #
5
+ # @example
6
+ # class Book < ActiveFedora::Base
7
+ # property :title, predicate: ::RDF::DC.title
8
+ # property :author, predicate: ::RDF::DC.creator
9
+ # end
10
+ #
11
+ # Book.attribute_names
12
+ # => ["title", "author"]
13
+
14
+ class ActiveTripleAttribute < DelegatedAttribute
15
+ end
16
+
17
+ end
@@ -0,0 +1,29 @@
1
+ module ActiveFedora
2
+
3
+ # Class for attributes that are delegated to an OmDatastream
4
+
5
+ class OmAttribute < StreamAttribute
6
+
7
+ # @param [ActiveFedora::Base] obj the object that has the attribute
8
+ # @param [Object] v value to write to the attribute
9
+ def writer(obj, v)
10
+ ds = file_for_attribute(obj, delegate_target)
11
+ obj.mark_as_changed(field) if obj.value_has_changed?(field, v)
12
+ terminology = at || [field]
13
+ ds.send(:update_indexed_attributes, {terminology => v})
14
+ end
15
+
16
+ # @param [ActiveFedora::Base] obj the object that has the attribute
17
+ # @param [Object] opts extra options that are passed to the target reader
18
+ def reader(obj, *opts)
19
+ ds = file_for_attribute(obj, delegate_target)
20
+ terminology = at || [field]
21
+ if terminology.length == 1 && opts.present?
22
+ ds.send(terminology.first, *opts)
23
+ else
24
+ ds.send(:term_values, *terminology)
25
+ end
26
+ end
27
+
28
+ end
29
+ end
@@ -0,0 +1,47 @@
1
+ module ActiveFedora
2
+
3
+ # Class for attributes that are delegated to a RDFDatastream
4
+
5
+ class RdfDatastreamAttribute < StreamAttribute
6
+
7
+ # @param [ActiveFedora::Base] obj the object that has the attribute
8
+ # @param [Object] v value to write to the attribute
9
+ def writer(obj, v)
10
+ node = file_for_attribute(obj, delegate_target)
11
+ obj.mark_as_changed(field) if obj.value_has_changed?(field, v)
12
+ term = if at
13
+ vals = at.dup
14
+ while vals.length > 1
15
+ node = node.send(vals.shift)
16
+ node = node.build if node.empty?
17
+ node = node.first
18
+ end
19
+ vals.first
20
+ else
21
+ field
22
+ end
23
+ node.send("#{term}=", v)
24
+ end
25
+
26
+ # @param [ActiveFedora::Base] obj the object that has the attribute
27
+ def reader(obj)
28
+ node = file_for_attribute(obj, delegate_target)
29
+ term = if at
30
+ vals = at.dup
31
+ while vals.length > 1
32
+ node = node.send(vals.shift)
33
+ node = if node.empty?
34
+ node.build
35
+ else
36
+ node.first
37
+ end
38
+ end
39
+ vals.first
40
+ else
41
+ field
42
+ end
43
+ node.send(term)
44
+ end
45
+
46
+ end
47
+ end
@@ -0,0 +1,46 @@
1
+ module ActiveFedora
2
+
3
+ # Abstract class for attributes that are delegated to a serialized representation such as a NonRDFSource
4
+ #
5
+ # @abstract
6
+ # @attr [String] delegate_target
7
+ # @attr [String] at
8
+ # @attr [String] target_class
9
+
10
+ class StreamAttribute < DelegatedAttribute
11
+
12
+ attr_accessor :delegate_target, :at, :target_class
13
+
14
+ # @param [Symbol] field the field to find or create
15
+ # @param [Hash] args
16
+ # @option args [String] :delegate_target the path to the delegate
17
+ # @option args [Class] :klass the class to create
18
+ # @option args [true,false] :multiple (false) true for multi-value fields
19
+ # @option args [Array<Symbol>] :at path to a deep node
20
+ def initialize(field, args={})
21
+ super
22
+ self.delegate_target = args.fetch(:delegate_target)
23
+ self.target_class = args.fetch(:klass)
24
+ self.at = args.fetch(:at, nil)
25
+ end
26
+
27
+ # Gives the primary solr name for a column. If there is more than one indexer on the field definition, it gives the first
28
+ def primary_solr_name
29
+ @datastream ||= target_class.new
30
+ raise NoMethodError, "the file '#{target_class}' doesn't respond to 'primary_solr_name'" unless @datastream.respond_to?(:primary_solr_name)
31
+ @datastream.primary_solr_name(field, delegate_target)
32
+ end
33
+
34
+ def type
35
+ raise NoMethodError, "the file '#{target_class}' doesn't respond to 'type'" unless target_class.respond_to?(:type)
36
+ target_class.type(field)
37
+ end
38
+
39
+ private
40
+
41
+ def file_for_attribute(obj, delegate_target)
42
+ obj.attached_files[delegate_target] || raise(ArgumentError, "Undefined file: `#{delegate_target}' in property #{field}")
43
+ end
44
+
45
+ end
46
+ end
@@ -75,7 +75,7 @@ module ActiveFedora
75
75
  module AutosaveAssociation
76
76
  extend ActiveSupport::Concern
77
77
 
78
- ASSOCIATION_TYPES = %w{ HasMany BelongsTo HasAndBelongsToMany }
78
+ ASSOCIATION_TYPES = %w{ HasMany BelongsTo HasAndBelongsToMany DirectlyContains IndirectlyContains}
79
79
 
80
80
  module AssociationBuilderExtension #:nodoc:
81
81
  def self.included(base)
@@ -305,7 +305,7 @@ module ActiveFedora
305
305
  saved = record.save(:validate => !autosave) if record.new_record? || (autosave && record.changed_for_autosave?)
306
306
 
307
307
  if association.updated?
308
- self[reflection.name.to_s + "_id"] = record.id #TODO use primary_key here
308
+ self[reflection.foreign_key] = record.id
309
309
  association.loaded!
310
310
  end
311
311
 
@@ -30,6 +30,7 @@ module ActiveFedora
30
30
  extend LdpCache::ClassMethods
31
31
 
32
32
  include Core
33
+ include Identifiable
33
34
  include Persistence
34
35
  include Indexing
35
36
  include Scoping
@@ -49,6 +50,8 @@ module ActiveFedora
49
50
  include Attributes
50
51
  include Versionable
51
52
  include LoadableFromJson
53
+ include Schema
54
+ include Pathing
52
55
  end
53
56
 
54
57
  ActiveSupport.run_load_hooks(:active_fedora, Base)
@@ -0,0 +1,38 @@
1
+ module ActiveFedora
2
+ # This is the base class for ldp containers, it is not an ldp:BasicContainer
3
+ class Container < ActiveFedora::Base
4
+
5
+ property :membership_resource, predicate: ::RDF::Vocab::LDP.membershipResource
6
+ property :has_member_relation, predicate: ::RDF::Vocab::LDP.hasMemberRelation
7
+ property :is_member_of_relation, predicate: ::RDF::Vocab::LDP.isMemberOfRelation
8
+ property :contained, predicate: ::RDF::Vocab::LDP.contains
9
+
10
+ def parent
11
+ @parent || raise("Parent hasn't been set on #{self.class}")
12
+ end
13
+
14
+ def parent=(parent)
15
+ @parent = parent
16
+ self.membership_resource = [::RDF::URI(parent.uri)]
17
+ end
18
+
19
+ def mint_id
20
+ "#{id}/#{SecureRandom.uuid}"
21
+ end
22
+
23
+ def self.find_or_initialize(id)
24
+ find(id)
25
+ rescue ActiveFedora::ObjectNotFoundError
26
+ new(id)
27
+ end
28
+
29
+ private
30
+
31
+ # Don't allow directly setting contained
32
+ def contained=(*args)
33
+ end
34
+
35
+ end
36
+ end
37
+
38
+
@@ -0,0 +1,5 @@
1
+ module ActiveFedora
2
+ class DirectContainer < Container
3
+ type ::RDF::Vocab::LDP.DirectContainer
4
+ end
5
+ end
@@ -0,0 +1,7 @@
1
+ module ActiveFedora
2
+ class IndirectContainer < Container
3
+ type ::RDF::Vocab::LDP.IndirectContainer
4
+
5
+ property :inserted_content_relation, predicate: ::RDF::Vocab::LDP.insertedContentRelation
6
+ end
7
+ end
@@ -1,7 +1,11 @@
1
1
  module ActiveFedora
2
2
  module Core
3
+ extend ActiveSupport::Autoload
3
4
  extend ActiveSupport::Concern
4
5
 
6
+ autoload :FedoraIdTranslator
7
+ autoload :FedoraUriTranslator
8
+
5
9
  included do
6
10
  ##
7
11
  # :singleton-method:
@@ -9,30 +13,6 @@ module ActiveFedora
9
13
  # Accepts a logger conforming to the interface of Log4r which can be
10
14
  # retrieved on both a class and instance level by calling +logger+.
11
15
  mattr_accessor :logger, instance_writer: false
12
-
13
- ##
14
- # :singleton-method
15
- #
16
- # Accepts a proc that takes an id and transforms it to a URI
17
- mattr_reader :translate_id_to_uri do
18
- FedoraIdTranslator
19
- end
20
-
21
- def self.translate_id_to_uri=(translator)
22
- @@translate_id_to_uri = translator || FedoraIdTranslator
23
- end
24
-
25
- ##
26
- # :singleton-method
27
- #
28
- # Accepts a proc that takes a uri and transforms it to an id
29
- mattr_reader :translate_uri_to_id do
30
- FedoraUriTranslator
31
- end
32
-
33
- def self.translate_uri_to_id=(translator)
34
- @@translate_uri_to_id = translator || FedoraUriTranslator
35
- end
36
16
  end
37
17
 
38
18
  def ldp_source
@@ -141,30 +121,6 @@ module ActiveFedora
141
121
  name
142
122
  end
143
123
 
144
- ##
145
- # Transforms an id into a uri
146
- # if translate_id_to_uri is set it uses that proc, otherwise just the default
147
- def id_to_uri(id)
148
- translate_id_to_uri.call(id)
149
- end
150
-
151
- ##
152
- # Transforms a uri into an id
153
- # if translate_uri_to_id is set it uses that proc, otherwise just the default
154
- def uri_to_id(uri)
155
- translate_uri_to_id.call(uri)
156
- end
157
-
158
- ##
159
- # Provides the common interface for ActiveTriples::Identifiable
160
- def from_uri(uri,_)
161
- begin
162
- self.find(uri_to_id(uri))
163
- rescue ActiveFedora::ObjectNotFoundError, Ldp::Gone
164
- ActiveTriples::Resource.new(uri)
165
- end
166
- end
167
-
168
124
  private
169
125
 
170
126
  def relation
@@ -1,106 +1,13 @@
1
1
  module ActiveFedora
2
- # Represents the mapping between a model attribute and a field in a datastream
2
+ # Represents the mapping between a model attribute and a property
3
3
  class DelegatedAttribute
4
4
 
5
- attr_accessor :dsid, :field, :datastream_class, :at, :multiple
5
+ attr_accessor :field, :multiple
6
6
 
7
- def initialize(field, dsid, datastream_class, args={})
7
+ def initialize(field, args={})
8
8
  self.field = field
9
- self.dsid = dsid
10
- self.datastream_class = datastream_class
11
- self.multiple = args[:multiple].nil? ? false : args[:multiple]
12
- self.at = args[:at]
9
+ self.multiple = args.fetch(:multiple, false)
13
10
  end
14
-
15
- # Gives the primary solr name for a column. If there is more than one indexer on the field definition, it gives the first
16
- def primary_solr_name
17
- @datastream ||= datastream_class.new
18
- if @datastream.respond_to?(:primary_solr_name)
19
- @datastream.primary_solr_name(field, dsid)
20
- else
21
- raise NoMethodError, "the datastream '#{datastream_class}' doesn't respond to 'primary_solr_name'"
22
- end
23
- end
24
-
25
- def type
26
- if datastream_class.respond_to?(:type)
27
- datastream_class.type(field)
28
- else
29
- raise NoMethodError, "the datastream '#{datastream_class}' doesn't respond to 'type'"
30
- end
31
- end
32
-
33
- def writer(obj, v)
34
- ds = datastream_for_attribute(obj, dsid)
35
- obj.mark_as_changed(field) if obj.value_has_changed?(field, v)
36
- if ds.kind_of?(ActiveFedora::RDFDatastream)
37
- write_rdf(ds, v)
38
- else
39
- write_om(ds, v)
40
- end
41
- end
42
-
43
- def reader(obj, *opts)
44
- ds = datastream_for_attribute(obj, dsid)
45
- if ds.kind_of?(ActiveFedora::RDFDatastream)
46
- read_rdf(ds)
47
- else
48
- read_om(ds, *opts)
49
- end
50
- end
51
-
52
- private
53
-
54
- def write_om(ds, v)
55
- terminology = at || [field]
56
- ds.send(:update_indexed_attributes, {terminology => v})
57
- end
58
-
59
- def read_om(ds, *opts)
60
- terminology = at || [field]
61
- if terminology.length == 1 && opts.present?
62
- ds.send(terminology.first, *opts)
63
- else
64
- ds.send(:term_values, *terminology)
65
- end
66
- end
67
-
68
- def write_rdf(node, v)
69
- term = if at
70
- vals = at.dup
71
- while vals.length > 1
72
- node = node.send(vals.shift)
73
- node = node.build if node.empty?
74
- node = node.first
75
- end
76
- vals.first
77
- else
78
- field
79
- end
80
- node.send("#{term}=", v)
81
- end
82
-
83
- def read_rdf(node)
84
- term = if at
85
- vals = at.dup
86
- while vals.length > 1
87
- node = node.send(vals.shift)
88
- node = if node.empty?
89
- node.build
90
- else
91
- node.first
92
- end
93
- end
94
- vals.first
95
- else
96
- field
97
- end
98
- node.send(term)
99
- end
100
-
101
- def datastream_for_attribute(obj, dsid)
102
- obj.attached_files[dsid] || raise(ArgumentError, "Undefined datastream id: `#{dsid}' in has_attributes")
103
- end
104
-
11
+
105
12
  end
106
13
  end
@@ -22,7 +22,7 @@ module ActiveFedora
22
22
  end
23
23
 
24
24
  def connection
25
- @connection ||= CachingConnection.new(authorized_connection)
25
+ @connection ||= InboundRelationConnection.new(CachingConnection.new(authorized_connection))
26
26
  end
27
27
 
28
28
  def clean_connection
@@ -29,31 +29,6 @@ module ActiveFedora
29
29
  resource.set_value(*args)
30
30
  end
31
31
 
32
- def id
33
- if uri.kind_of?(::RDF::URI) && uri.value.blank?
34
- nil
35
- elsif uri.present?
36
- self.class.uri_to_id(URI.parse(uri))
37
- end
38
- end
39
-
40
- def id=(id)
41
- raise "ID has already been set to #{self.id}" if self.id
42
- @ldp_source = build_ldp_resource(id.to_s)
43
- end
44
-
45
-
46
- # TODO: Remove after we no longer support #pid.
47
- def pid
48
- Deprecation.warn FedoraAttributes, "#{self.class}#pid is deprecated and will be removed in active-fedora 10.0. Use #{self.class}#id instead."
49
- id
50
- end
51
-
52
- def uri
53
- # TODO could we return a RDF::URI instead?
54
- uri = @ldp_source.try(:subject_uri)
55
- uri.value == '' ? uri : uri.to_s
56
- end
57
32
 
58
33
  ##
59
34
  # The resource is the RdfResource object that stores the graph for
@@ -62,7 +37,10 @@ module ActiveFedora
62
37
  #
63
38
  # set_value, get_value, and property accessors are delegated to this object.
64
39
  def resource
65
- @resource ||= self.class.resource_class.new(@ldp_source.graph.rdf_subject, @ldp_source.graph)
40
+ # Appending the graph at the end is necessary because adding it as the
41
+ # parent leaves behind triples not related to the ldp_source's rdf
42
+ # subject.
43
+ @resource ||= self.class.resource_class.new(@ldp_source.graph.rdf_subject, @ldp_source.graph) << @ldp_source.graph
66
44
  end
67
45
 
68
46
  # You can set the URI to use for the rdf_label on ClassMethods.rdf_label, then on