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
@@ -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