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,7 @@
1
+ module ActiveFedora
2
+ class FileRelation < Relation
3
+ def load_from_fedora(id, _)
4
+ klass.new(klass.id_to_uri(id))
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,87 @@
1
+ module ActiveFedora
2
+ module Identifiable
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ ##
7
+ # :singleton-method
8
+ #
9
+ # Accepts a proc that takes an id and transforms it to a URI
10
+ mattr_reader :translate_id_to_uri
11
+
12
+ # This method is mixed into ActiveFedora::Base and ActiveFedora::File, so don't
13
+ # overwrite the value if it's already set.
14
+ @@translate_id_to_uri ||= Core::FedoraIdTranslator
15
+
16
+ def self.translate_id_to_uri=(translator)
17
+ @@translate_id_to_uri = translator || Core::FedoraIdTranslator
18
+ end
19
+
20
+ ##
21
+ # :singleton-method
22
+ #
23
+ # Accepts a proc that takes a uri and transforms it to an id
24
+ mattr_reader :translate_uri_to_id
25
+
26
+ # This method is mixed into ActiveFedora::Base and ActiveFedora::File, so don't
27
+ # overwrite the value if it's already set.
28
+ @@translate_uri_to_id ||= Core::FedoraUriTranslator
29
+
30
+ def self.translate_uri_to_id=(translator)
31
+ @@translate_uri_to_id = translator || Core::FedoraUriTranslator
32
+ end
33
+ end
34
+
35
+ def id
36
+ if uri.kind_of?(::RDF::URI) && uri.value.blank?
37
+ nil
38
+ elsif uri.present?
39
+ self.class.uri_to_id(URI.parse(uri))
40
+ end
41
+ end
42
+
43
+ def id=(id)
44
+ raise "ID has already been set to #{self.id}" if self.id
45
+ @ldp_source = build_ldp_resource(id.to_s)
46
+ end
47
+
48
+
49
+ # TODO: Remove after we no longer support #pid.
50
+ def pid
51
+ Deprecation.warn FedoraAttributes, "#{self.class}#pid is deprecated and will be removed in active-fedora 10.0. Use #{self.class}#id instead."
52
+ id
53
+ end
54
+
55
+ # @return [RDF::URI] the uri for this resource
56
+ def uri
57
+ @ldp_source.subject_uri
58
+ end
59
+
60
+
61
+ module ClassMethods
62
+ ##
63
+ # Transforms an id into a uri
64
+ # if translate_id_to_uri is set it uses that proc, otherwise just the default
65
+ def id_to_uri(id)
66
+ translate_id_to_uri.call(id)
67
+ end
68
+
69
+ ##
70
+ # Transforms a uri into an id
71
+ # if translate_uri_to_id is set it uses that proc, otherwise just the default
72
+ def uri_to_id(uri)
73
+ translate_uri_to_id.call(uri)
74
+ end
75
+
76
+ ##
77
+ # Provides the common interface for ActiveTriples::Identifiable
78
+ def from_uri(uri,_)
79
+ begin
80
+ self.find(uri_to_id(uri))
81
+ rescue ActiveFedora::ObjectNotFoundError, Ldp::Gone
82
+ ActiveTriples::Resource.new(uri)
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,21 @@
1
+ module ActiveFedora
2
+ class InboundRelationConnection < SimpleDelegator
3
+ def get(*args)
4
+ result = __getobj__.get(*args) do |req|
5
+ prefer_headers = Ldp::PreferHeaders.new(req.headers["Prefer"])
6
+ prefer_headers.include = prefer_headers.include | include_uris
7
+ req.headers["Prefer"] = prefer_headers.to_s
8
+ yield req if block_given?
9
+ end
10
+ result
11
+ end
12
+
13
+ private
14
+
15
+ def include_uris
16
+ [
17
+ RDF::Fcrepo4.InboundReferences
18
+ ]
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,10 @@
1
+ module ActiveFedora
2
+ module Indexers
3
+ extend ActiveSupport::Autoload
4
+
5
+ eager_autoload do
6
+ autoload :NullIndexer
7
+ autoload :GlobalIndexer
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,30 @@
1
+ module ActiveFedora::Indexers
2
+ ##
3
+ # Applies indexing hints to any given property, independent of what that
4
+ # property
5
+ class GlobalIndexer
6
+ # @param [Array<Symbol>] index_types The indexing hints to use.
7
+ def initialize(index_types=nil)
8
+ @index_types = Array.wrap(index_types)
9
+ end
10
+
11
+
12
+ # The global indexer acts as both an indexer factory and an indexer, since
13
+ # the property doesn't matter.
14
+ def new(property)
15
+ self
16
+ end
17
+
18
+ # @param [ActiveFedora::Indexing::Map::IndexObject, #as] index_obj The indexing
19
+ # object to call #as on.
20
+ def index(index_obj)
21
+ unless index_types.empty?
22
+ index_obj.as(*index_types)
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ attr_reader :index_types
29
+ end
30
+ end
@@ -0,0 +1,12 @@
1
+ module ActiveFedora::Indexers
2
+ ##
3
+ # An indexer which does nothing with the given index object.
4
+ class NullIndexer
5
+ include Singleton
6
+ def new(_)
7
+ self
8
+ end
9
+ def index(_)
10
+ end
11
+ end
12
+ end
@@ -17,10 +17,12 @@ module ActiveFedora::Indexing
17
17
  # this enables a cleaner API for solr integration
18
18
  class IndexObject
19
19
  attr_accessor :data_type, :behaviors
20
+ attr_reader :key
20
21
 
21
- def initialize(&block)
22
+ def initialize(name, &block)
22
23
  @behaviors = []
23
24
  @data_type = :string
25
+ @key = name
24
26
  yield self if block_given?
25
27
  end
26
28
 
@@ -33,14 +35,11 @@ module ActiveFedora::Indexing
33
35
  end
34
36
 
35
37
  def dup
36
- self.class.new do |idx|
38
+ self.class.new(@key) do |idx|
37
39
  idx.behaviors = @behaviors.dup
38
40
  end
39
41
  end
40
42
 
41
- def defaults
42
- :noop
43
- end
44
43
  end
45
44
  end
46
45
  end
@@ -1,15 +1,9 @@
1
1
  module ActiveFedora
2
2
  class IndexingService
3
3
  attr_reader :object
4
- # Object must respond to
5
- # create_date
6
- # modified_date
7
- # has_model
8
- # id
9
- # to_json
10
- # attached_files
11
- # []
12
- # and it's class must respond to
4
+
5
+ # @param [#create_date, #modified_date, #has_model, #id, #to_json, #attached_files, #[]] obj
6
+ # The class of obj must respond to these methods:
13
7
  # inspect
14
8
  # outgoing_reflections
15
9
  def initialize(obj)
@@ -42,7 +36,6 @@ module ActiveFedora
42
36
  object.attached_files.each do |name, file|
43
37
  solr_doc.merge! file.to_solr(solr_doc, name: name.to_s)
44
38
  end
45
- solr_doc = solrize_relationships(solr_doc)
46
39
  solr_doc = solrize_rdf_assertions(solr_doc)
47
40
  yield(solr_doc) if block_given?
48
41
  solr_doc
@@ -62,18 +55,6 @@ module ActiveFedora
62
55
  m_time
63
56
  end
64
57
 
65
- # Serialize the datastream's RDF relationships to solr
66
- # @param [Hash] solr_doc @deafult an empty Hash
67
- def solrize_relationships(solr_doc = Hash.new)
68
- object.class.outgoing_reflections.values.each do |reflection|
69
- solr_key = reflection.solr_key
70
- Array(object[reflection.foreign_key]).compact.each do |v|
71
- ::Solrizer::Extractor.insert_solr_field_value(solr_doc, solr_key, v )
72
- end
73
- end
74
- solr_doc
75
- end
76
-
77
58
  # Serialize the resource's RDF relationships to solr
78
59
  # @param [Hash] solr_doc @deafult an empty Hash
79
60
  def solrize_rdf_assertions(solr_doc = Hash.new)
@@ -54,10 +54,16 @@ module ActiveFedora
54
54
 
55
55
  # FakeQuery exists to adapt the hash to the RDF interface used by RDF associations in ActiveFedora
56
56
  class FakeQuery
57
+ include ::Enumerable
58
+
57
59
  def initialize(values)
58
60
  @values = values || []
59
61
  end
60
62
 
63
+ def each(&block)
64
+ enum_statement.each(&block)
65
+ end
66
+
61
67
  def enum_statement
62
68
  @values.map {|v| FakeStatement.new(v) }
63
69
  end
@@ -87,6 +93,8 @@ module ActiveFedora
87
93
  ::RDF::URI.new(nil)
88
94
  end
89
95
 
96
+ # Called by Associations::RDF#replace to add data to this resource represenation
97
+ # @param [Array] vals an array of 3 elements (subject, predicate, object) to insert
90
98
  def insert(vals)
91
99
  _, pred, val = vals
92
100
  set_value(reflection(pred), [val])
@@ -0,0 +1,24 @@
1
+ module ActiveFedora
2
+ module Pathing
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ def uri_prefix
7
+ nil
8
+ end
9
+
10
+ def has_uri_prefix?
11
+ !uri_prefix.nil?
12
+ end
13
+
14
+ def root_resource_path
15
+ if has_uri_prefix?
16
+ ActiveFedora.fedora.base_path + "/" + self.uri_prefix
17
+ else
18
+ ActiveFedora.fedora.base_path
19
+ end
20
+ end
21
+ end
22
+
23
+ end
24
+ end
@@ -47,11 +47,6 @@ module ActiveFedora
47
47
  return self if new_record?
48
48
 
49
49
  @destroyed = true
50
- reflections.each_pair do |name, reflection|
51
- if reflection.macro == :has_many
52
- association(name.to_sym).delete_all
53
- end
54
- end
55
50
 
56
51
  id = self.id ## cache so it's still available after delete
57
52
  # Clear out the ETag
@@ -141,7 +136,8 @@ module ActiveFedora
141
136
 
142
137
  def create_or_update(*args)
143
138
  raise ReadOnlyRecord if readonly?
144
- new_record? ? create_record(*args) : update_record(*args)
139
+ result = new_record? ? create_record(*args) : update_record(*args)
140
+ result != false
145
141
  end
146
142
 
147
143
  # Deals with preparing new object to be saved to Fedora, then pushes it and its attached files into Fedora.
@@ -149,7 +145,6 @@ module ActiveFedora
149
145
  assign_rdf_subject
150
146
  serialize_attached_files
151
147
  @ldp_source = @ldp_source.create
152
- @resource = nil
153
148
  assign_uri_to_attached_files
154
149
  save_attached_files
155
150
  refresh
@@ -184,10 +179,22 @@ module ActiveFedora
184
179
  @ldp_source = if !id && new_id = assign_id
185
180
  LdpResource.new(ActiveFedora.fedora.connection, self.class.id_to_uri(new_id), @resource)
186
181
  else
187
- LdpResource.new(ActiveFedora.fedora.connection, @ldp_source.subject, @resource, ActiveFedora.fedora.host + ActiveFedora.fedora.base_path)
182
+ LdpResource.new(ActiveFedora.fedora.connection, @ldp_source.subject, @resource, ActiveFedora.fedora.host + base_path_for_resource)
188
183
  end
189
184
  end
190
185
 
186
+ def base_path_for_resource
187
+ init_root_path if self.has_uri_prefix?
188
+ self.root_resource_path
189
+ end
190
+
191
+ def init_root_path
192
+ path = self.root_resource_path.gsub(/^\//,"")
193
+ ActiveFedora.fedora.connection.head(path)
194
+ rescue Ldp::NotFound
195
+ ActiveFedora.fedora.connection.put(path, "")
196
+ end
197
+
191
198
  def assign_uri_to_attached_files
192
199
  attached_files.each do |name, ds|
193
200
  ds.uri= "#{uri}/#{name}"
@@ -9,5 +9,7 @@ module ActiveFedora
9
9
  autoload :Identifiable
10
10
  autoload :Persistence
11
11
  autoload :ProjectHydra
12
+ autoload :FieldMap
13
+ autoload :FieldMapEntry
12
14
  end
13
15
  end
@@ -8,5 +8,6 @@ module ActiveFedora::RDF
8
8
  property :lastModified
9
9
  property :status
10
10
  property :ServerManaged
11
+ property :InboundReferences
11
12
  end
12
13
  end
@@ -0,0 +1,90 @@
1
+ module ActiveFedora::RDF
2
+ # Transient class that maps solr field names, without their suffixes, to the values and behaviors that
3
+ # are used to transforming them for insertion into the solr document.
4
+ # It partially extends Ruby's Hash class, similar to the way ActiveFedora::Indexing::Map does,
5
+ # but only with selected methods as outlined in def_delegators.
6
+ class FieldMap
7
+ extend Forwardable
8
+
9
+ def_delegators :@hash, :[], :[]=, :each, :keys
10
+
11
+ def initialize(hash = {}, &block)
12
+ @hash = hash
13
+ yield self if block_given?
14
+ end
15
+
16
+ # Inserts each solr field map configuration into the FieldMap class
17
+ # @param [Symbol] name the name of the property on the object that we're indexing
18
+ # @param [Object] index_field_config an instance of ActiveFedora::Indexing::Map::IndexObject
19
+ # @param [Object] object the instance of ActiveFedora::Base which is being indexed into Solr
20
+ def insert(name, index_field_config, object)
21
+ self[index_field_config.key.to_s] ||= FieldMapEntry.new
22
+ PolymorphicBuilder.new(self[index_field_config.key.to_s], index_field_config, object, name).build
23
+ end
24
+
25
+ # Supports assigning the delegate class that calls .build to insert the fields into the solr document.
26
+ # @attr [Object] entry instance of ActiveFedora::RDF::FieldMapEntry which will contain the values of the solr field
27
+ # @attr [Object] index_field_config an instance of ActiveFedora::Indexing::Map::IndexObject
28
+ # @attr [Object] object the instance of ActiveFedora::Base which is being indexed into Solr
29
+ # @attr [Symbol] name the name of the property on the object that we're indexing
30
+ class PolymorphicBuilder
31
+
32
+ attr_accessor :entry, :index_field_config, :object, :name
33
+
34
+ def initialize(entry, index_field_config, object, name)
35
+ @entry = entry
36
+ @index_field_config = index_field_config
37
+ @object = object
38
+ @name = name
39
+ self
40
+ end
41
+
42
+ def build
43
+ delegate_class.new(entry, index_field_config, object, name).build
44
+ end
45
+
46
+ private
47
+
48
+ def delegate_class
49
+ kind_of_af_base? ? ResourceBuilder : PropertyBuilder
50
+ end
51
+
52
+ def kind_of_af_base?
53
+ config = properties[name.to_s]
54
+ config && config[:class_name] && config[:class_name] < ActiveFedora::Base
55
+ end
56
+
57
+ def properties
58
+ object.class.properties
59
+ end
60
+
61
+ end
62
+
63
+ # Abstract class that implements the PolymorphicBuilder interface and is used for
64
+ # for building FieldMap entries. You can extend this object to create your own
65
+ # builder for creating the values in your solr fields.
66
+ class Builder < PolymorphicBuilder
67
+ def build
68
+ type = index_field_config.data_type
69
+ behaviors = index_field_config.behaviors
70
+ return unless type and behaviors
71
+ entry.merge!(type, behaviors, find_values)
72
+ end
73
+ end
74
+
75
+ # Builds a FieldMap entry for a resource such as an ActiveFedora::Base object and returns the uri as the value.
76
+ class ResourceBuilder < Builder
77
+ def find_values
78
+ object.send(name).map(&:uri)
79
+ end
80
+ end
81
+
82
+ # Builds a FieldMap entry for a rdf property and returns the values
83
+ class PropertyBuilder < Builder
84
+ def find_values
85
+ Array(object.send(name))
86
+ end
87
+ end
88
+
89
+ end
90
+ end