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