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,31 +1,42 @@
1
1
  require 'deprecation'
2
2
 
3
3
  module ActiveFedora
4
-
5
- #This class represents a Fedora datastream
4
+ # An LDP NonRDFSource. The base class for a bytestream stored in the repository.
6
5
  class File
7
- include AttributeMethods # allows 'content' to be tracked
8
- include ActiveModel::Dirty
9
- extend Deprecation
6
+ extend ActiveModel::Callbacks
7
+ extend ActiveSupport::Autoload
10
8
  extend ActiveTriples::Properties
9
+ extend Deprecation
10
+ extend Querying
11
+
12
+ autoload :Streaming
13
+ autoload :Attributes
14
+
15
+ include ActiveFedora::File::Attributes
16
+ include ActiveFedora::File::Streaming
17
+ include ActiveFedora::Persistence
18
+ include ActiveFedora::Versionable
19
+ include ActiveModel::Dirty
20
+ include AttributeMethods # allows 'content' to be tracked
21
+ include Identifiable
22
+ include Scoping
23
+
11
24
  generate_method 'content'
12
25
 
13
- extend ActiveModel::Callbacks
14
26
  define_model_callbacks :save, :create, :destroy
15
27
  define_model_callbacks :initialize, only: :after
16
28
 
17
- # @param parent_or_url_or_hash [ActiveFedora::Base, String, Hash, NilClass] the parent resource or the URI of this resource
29
+ # @param parent_or_url_or_hash [ActiveFedora::Base, RDF::URI, String, Hash, NilClass] the parent resource or the URI of this resource
18
30
  # @param path [String] the path partial relative to the resource
19
31
  # @param options [Hash]
20
- def initialize(parent_or_url_or_hash = nil, path=nil, options={})
32
+ # @yield [self] Yields self
33
+ # @yieldparam [File] self the newly created file
34
+ def initialize(parent_or_url_or_hash = nil, path=nil, options={}, &block)
21
35
  case parent_or_url_or_hash
22
36
  when Hash
23
- content = ''
24
- @ldp_source = Ldp::Resource::BinarySource.new(ldp_connection, nil, content, ActiveFedora.fedora.host + ActiveFedora.fedora.base_path)
25
- when nil, String
26
- #TODO this is similar to Core#build_ldp_resource
27
- content = ''
28
- @ldp_source = Ldp::Resource::BinarySource.new(ldp_connection, parent_or_url_or_hash, content, ActiveFedora.fedora.host + ActiveFedora.fedora.base_path)
37
+ @ldp_source = build_ldp_resource_via_uri
38
+ when nil, String, ::RDF::URI
39
+ @ldp_source = build_ldp_resource_via_uri parent_or_url_or_hash
29
40
  when ActiveFedora::Base
30
41
  Deprecation.warn File, "Initializing a file by passing a container is deprecated. Initialize with a uri instead. This capability will be removed in active-fedora 10.0"
31
42
  uri = if parent_or_url_or_hash.uri.kind_of?(::RDF::URI) && parent_or_url_or_hash.uri.value.empty?
@@ -33,13 +44,23 @@ module ActiveFedora
33
44
  else
34
45
  "#{parent_or_url_or_hash.uri}/#{path}"
35
46
  end
36
- @ldp_source = Ldp::Resource::BinarySource.new(ldp_connection, uri, nil, ActiveFedora.fedora.host + ActiveFedora.fedora.base_path)
47
+ @ldp_source = build_ldp_resource_via_uri(uri, nil)
37
48
 
38
49
  else
39
- raise "The first argument to #{self} must be a String or an ActiveFedora::Base. You provided a #{parent_or_url.class}"
50
+ raise "The first argument to #{self} must be a String or an ActiveFedora::Base. You provided a #{parent_or_url_or_hash.class}"
40
51
  end
41
52
 
42
53
  @attributes = {}.with_indifferent_access
54
+ yield self if block_given?
55
+ end
56
+
57
+ # @return [true, false] true if the objects are equal or when the objects have uris
58
+ # and the uris are equal
59
+ def ==(comparison_object)
60
+ super ||
61
+ comparison_object.instance_of?(self.class) &&
62
+ uri.value.present? &&
63
+ comparison_object.uri == uri
43
64
  end
44
65
 
45
66
  def ldp_source
@@ -55,19 +76,14 @@ module ActiveFedora
55
76
  ActiveFedora.fedora.connection
56
77
  end
57
78
 
58
- # TODO this is like FedoraAttributes#uri
59
- def uri
60
- ldp_source.subject
61
- end
62
-
63
- # If this file have a parent with ldp#contains, we know it is not new.
79
+ # If this file has a parent with ldp#contains, we know it is not new.
64
80
  # By tracking exists we prevent an unnecessary HEAD request.
65
81
  def new_record?
66
82
  !@exists && ldp_source.new?
67
83
  end
68
84
 
69
85
  def uri= uri
70
- @ldp_source = Ldp::Resource::BinarySource.new(ldp_connection, uri, '', ActiveFedora.fedora.host + ActiveFedora.fedora.base_path)
86
+ @ldp_source = build_ldp_resource_via_uri(uri)
71
87
  end
72
88
 
73
89
  # If we know the record to exist (parent has LDP:contains), we can avoid unnecessary HEAD requests
@@ -78,15 +94,16 @@ module ActiveFedora
78
94
  # When restoring from previous versions, we need to reload certain attributes from Fedora
79
95
  def reload
80
96
  return if new_record?
81
- reset
97
+ refresh
82
98
  end
83
99
 
84
- def reset
85
- @ldp_source = Ldp::Resource::BinarySource.new(ldp_connection, uri)
100
+ def refresh
101
+ @ldp_source = build_ldp_resource_via_uri(uri)
86
102
  @original_name = nil
87
103
  @mime_type = nil
88
104
  @content = nil
89
105
  @metadata = nil
106
+ changed_attributes.clear
90
107
  end
91
108
 
92
109
  def check_fixity
@@ -110,45 +127,10 @@ module ActiveFedora
110
127
  @ds_content ||= retrieve_content
111
128
  end
112
129
 
113
- attr_writer :mime_type
114
- def mime_type
115
- @mime_type ||= fetch_mime_type unless new_record?
116
- @mime_type || default_mime_type
117
- end
118
-
119
130
  def metadata
120
131
  @metadata ||= ActiveFedora::WithMetadata::MetadataNode.new(self)
121
132
  end
122
133
 
123
- def original_name
124
- @original_name ||= fetch_original_name_from_headers
125
- end
126
-
127
- def digest
128
- response = metadata.ldp_source.graph.query(predicate: ActiveFedora::RDF::Fcrepo4.digest)
129
- response.map(&:object)
130
- end
131
-
132
- def persisted_size
133
- ldp_source.head.headers['Content-Length'].to_i unless new_record?
134
- end
135
-
136
- def dirty_size
137
- content.size if changed? && content.respond_to?(:size)
138
- end
139
-
140
- def size
141
- dirty_size || persisted_size
142
- end
143
-
144
- def has_content?
145
- size && size > 0
146
- end
147
-
148
- def empty?
149
- !has_content?
150
- end
151
-
152
134
  def content_changed?
153
135
  return true if new_record? and !local_or_remote_content(false).blank?
154
136
  local_or_remote_content(false) != @ds_content
@@ -158,10 +140,6 @@ module ActiveFedora
158
140
  super || content_changed?
159
141
  end
160
142
 
161
- def original_name= name
162
- @original_name = name
163
- end
164
-
165
143
  def inspect
166
144
  "#<#{self.class} uri=\"#{uri}\" >"
167
145
  end
@@ -189,75 +167,73 @@ module ActiveFedora
189
167
  solr_doc
190
168
  end
191
169
 
192
- protected
193
-
194
- def default_mime_type
195
- 'text/plain'
170
+ def content= string_or_io
171
+ content_will_change! unless @content == string_or_io
172
+ @content = string_or_io
196
173
  end
197
174
 
198
- # The string to prefix all solr fields with. Override this method if you want
199
- # a prefix other than the default
200
- def prefix(path)
201
- path ? "#{path.underscore}__" : ''
202
- end
203
-
204
- def fetch_original_name_from_headers
205
- return if new_record?
206
- m = ldp_source.head.headers['Content-Disposition'].match(/filename="(?<filename>[^"]*)";/)
207
- URI.decode(m[:filename])
175
+ def content
176
+ local_or_remote_content(true)
208
177
  end
209
178
 
210
- def fetch_mime_type
211
- ldp_source.head.headers['Content-Type']
179
+ def readonly?
180
+ false
212
181
  end
213
182
 
214
- private
183
+ protected
215
184
 
216
- def links
217
- @links ||= Ldp::Response.links(ldp_source.head)
218
- end
185
+ # The string to prefix all solr fields with. Override this method if you want
186
+ # a prefix other than the default
187
+ def prefix(path)
188
+ path ? "#{path.underscore}__" : ''
189
+ end
219
190
 
191
+ private
220
192
 
221
- # Rack::Test::UploadedFile is often set via content=, however it's not an IO, though it wraps an io object.
222
- def behaves_like_io?(obj)
223
- [IO, Tempfile, StringIO].any? { |klass| obj.kind_of? klass } || (defined?(Rack) && obj.is_a?(Rack::Test::UploadedFile))
224
- end
193
+ def self.relation
194
+ FileRelation.new(self)
195
+ end
225
196
 
226
- # Persistence is an included module, so that we can include other modules which override these methods
227
- module Persistence
228
- def content= string_or_io
229
- content_will_change! unless @content == string_or_io
230
- @content = string_or_io
197
+ # Rack::Test::UploadedFile is often set via content=, however it's not an IO, though it wraps an io object.
198
+ def behaves_like_io?(obj)
199
+ [IO, Tempfile, StringIO].any? { |klass| obj.kind_of? klass } || (defined?(Rack) && obj.is_a?(Rack::Test::UploadedFile))
231
200
  end
232
201
 
233
- def content
234
- local_or_remote_content(true)
202
+ def retrieve_content
203
+ ldp_source.get.body
235
204
  end
236
205
 
237
- def save(*)
238
- return true unless content_changed?
206
+ def ldp_headers
239
207
  headers = { 'Content-Type'.freeze => mime_type, 'Content-Length'.freeze => content.size.to_s }
240
208
  headers['Content-Disposition'.freeze] = "attachment; filename=\"#{URI.encode(@original_name)}\"" if @original_name
209
+ headers
210
+ end
241
211
 
212
+ def create_record(options = {})
213
+ return false if content.nil?
242
214
  ldp_source.content = content
243
- if new_record?
244
- ldp_source.create do |req|
245
- req.headers.merge!(headers)
246
- end
247
- else
248
- ldp_source.update do |req|
249
- req.headers.merge!(headers)
250
- end
215
+ ldp_source.create do |req|
216
+ req.headers.merge!(ldp_headers)
251
217
  end
252
- reset
253
- changed_attributes.clear
218
+ refresh
254
219
  end
255
220
 
256
- def retrieve_content
257
- ldp_source.get.body
221
+ def update_record(options = {})
222
+ return true unless content_changed?
223
+ ldp_source.content = content
224
+ ldp_source.update do |req|
225
+ req.headers.merge!(ldp_headers)
226
+ end
227
+ refresh
258
228
  end
259
229
 
260
- private
230
+ def build_ldp_resource(id)
231
+ build_ldp_resource_via_uri self.class.id_to_uri(id)
232
+ end
233
+
234
+ def build_ldp_resource_via_uri(uri=nil, content='')
235
+ Ldp::Resource::BinarySource.new(ldp_connection, uri, content, ActiveFedora.fedora.host + ActiveFedora.fedora.base_path)
236
+ end
261
237
 
262
238
  def uploaded_file?(payload)
263
239
  defined?(ActionDispatch::Http::UploadedFile) and payload.instance_of?(ActionDispatch::Http::UploadedFile)
@@ -280,55 +256,7 @@ module ActiveFedora
280
256
  end
281
257
  @content
282
258
  end
283
- end
284
-
285
- module Streaming
286
- # @param range [String] the Range HTTP header
287
- # @return [Stream] an object that responds to each
288
- def stream(range = nil)
289
- uri = URI.parse(self.uri)
290
- FileBody.new(uri, headers(range, authorization_key))
291
- end
292
-
293
- # @return [String] current authorization token from Ldp::Client
294
- def authorization_key
295
- self.ldp_source.client.http.headers.fetch("Authorization", nil)
296
- end
297
-
298
- # @param range [String] from #stream
299
- # @param key [String] from #authorization_key
300
- # @return [Hash]
301
- def headers(range, key, result = Hash.new)
302
- result["Range"] = range if range
303
- result["Authorization"] = key if key
304
- result
305
- end
306
259
 
307
- class FileBody
308
- attr_reader :uri, :headers
309
- def initialize(uri, headers)
310
- @uri = uri
311
- @headers = headers
312
- end
313
-
314
- def each
315
- Net::HTTP.start(uri.host, uri.port) do |http|
316
- request = Net::HTTP::Get.new uri, headers
317
- http.request request do |response|
318
-
319
- raise "Couldn't get data from Fedora (#{uri}). Response: #{response.code}" unless response.is_a?(Net::HTTPSuccess)
320
- response.read_body do |chunk|
321
- yield chunk
322
- end
323
- end
324
- end
325
- end
326
- end
327
- end
328
-
329
- include ActiveFedora::File::Persistence
330
- include ActiveFedora::File::Streaming
331
- include ActiveFedora::Versionable
332
260
  end
333
261
 
334
262
  end
@@ -0,0 +1,63 @@
1
+ module ActiveFedora::File::Attributes
2
+
3
+ attr_writer :mime_type
4
+
5
+ def mime_type
6
+ @mime_type ||= fetch_mime_type
7
+ end
8
+
9
+ def original_name
10
+ @original_name ||= fetch_original_name
11
+ end
12
+
13
+ def original_name= name
14
+ @original_name = name
15
+ end
16
+
17
+ def digest
18
+ response = metadata.ldp_source.graph.query(predicate: ActiveFedora::RDF::Fcrepo4.digest)
19
+ response.map(&:object)
20
+ end
21
+
22
+ def persisted_size
23
+ ldp_source.head.headers['Content-Length'].to_i unless new_record?
24
+ end
25
+
26
+ def dirty_size
27
+ content.size if changed? && content.respond_to?(:size)
28
+ end
29
+
30
+ def size
31
+ dirty_size || persisted_size
32
+ end
33
+
34
+ def has_content?
35
+ size && size > 0
36
+ end
37
+
38
+ def empty?
39
+ !has_content?
40
+ end
41
+
42
+ private
43
+
44
+ def links
45
+ @links ||= Ldp::Response.links(ldp_source.head)
46
+ end
47
+
48
+ def default_mime_type
49
+ 'text/plain'
50
+ end
51
+
52
+ def fetch_mime_type
53
+ return default_mime_type if new_record?
54
+ ldp_source.head.headers['Content-Type']
55
+ end
56
+
57
+ def fetch_original_name
58
+ return if new_record?
59
+ m = ldp_source.head.headers['Content-Disposition'].match(/filename="(?<filename>[^"]*)";/)
60
+ URI.decode(m[:filename])
61
+ end
62
+
63
+ end
@@ -0,0 +1,46 @@
1
+ module ActiveFedora::File::Streaming
2
+ # @param range [String] the Range HTTP header
3
+ # @return [Stream] an object that responds to each
4
+ def stream(range = nil)
5
+ uri = URI.parse(self.uri)
6
+ FileBody.new(uri, headers(range, authorization_key))
7
+ end
8
+
9
+ # @param range [String] from #stream
10
+ # @param key [String] from #authorization_key
11
+ # @return [Hash]
12
+ def headers(range, key, result = Hash.new)
13
+ result["Range"] = range if range
14
+ result["Authorization"] = key if key
15
+ result
16
+ end
17
+
18
+ class FileBody
19
+ attr_reader :uri, :headers
20
+ def initialize(uri, headers)
21
+ @uri = uri
22
+ @headers = headers
23
+ end
24
+
25
+ def each
26
+ Net::HTTP.start(uri.host, uri.port) do |http|
27
+ request = Net::HTTP::Get.new uri, headers
28
+ http.request request do |response|
29
+
30
+ raise "Couldn't get data from Fedora (#{uri}). Response: #{response.code}" unless response.is_a?(Net::HTTPSuccess)
31
+ response.read_body do |chunk|
32
+ yield chunk
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+
39
+ private
40
+
41
+ # @return [String] current authorization token from Ldp::Client
42
+ def authorization_key
43
+ self.ldp_source.client.http.headers.fetch("Authorization", nil)
44
+ end
45
+
46
+ end