active-fedora 9.0.0.rc3 → 9.0.0

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 (57) hide show
  1. checksums.yaml +4 -4
  2. data/History.txt +1054 -0
  3. data/active-fedora.gemspec +1 -1
  4. data/config/predicate_mappings.yml +2 -2
  5. data/lib/active_fedora.rb +3 -0
  6. data/lib/active_fedora/attached_files.rb +31 -14
  7. data/lib/active_fedora/base.rb +2 -0
  8. data/lib/active_fedora/caching_connection.rb +80 -0
  9. data/lib/active_fedora/callbacks.rb +13 -14
  10. data/lib/active_fedora/core.rb +9 -17
  11. data/lib/active_fedora/errors.rb +1 -1
  12. data/lib/active_fedora/fedora.rb +5 -1
  13. data/lib/active_fedora/file.rb +42 -39
  14. data/lib/active_fedora/ldp_cache.rb +46 -0
  15. data/lib/active_fedora/ldp_resource_service.rb +27 -0
  16. data/lib/active_fedora/loadable_from_json.rb +3 -1
  17. data/lib/active_fedora/log_subscriber.rb +38 -0
  18. data/lib/active_fedora/persistence.rb +22 -12
  19. data/lib/active_fedora/predicates.rb +3 -3
  20. data/lib/active_fedora/railtie.rb +3 -0
  21. data/lib/active_fedora/rdf.rb +0 -1
  22. data/lib/active_fedora/relation/finder_methods.rb +2 -3
  23. data/lib/active_fedora/validations.rb +1 -3
  24. data/lib/active_fedora/version.rb +1 -1
  25. data/lib/generators/active_fedora/config/solr/templates/jetty.yml +3 -2
  26. data/spec/fixtures/rails_root/config/predicate_mappings.yml +2 -2
  27. data/spec/integration/associations_spec.rb +19 -19
  28. data/spec/integration/attached_files_spec.rb +3 -3
  29. data/spec/integration/belongs_to_association_spec.rb +9 -9
  30. data/spec/integration/caching_spec.rb +59 -0
  31. data/spec/integration/collection_association_spec.rb +1 -1
  32. data/spec/integration/fedora_solr_sync_spec.rb +2 -2
  33. data/spec/integration/file_spec.rb +41 -43
  34. data/spec/integration/has_and_belongs_to_many_associations_spec.rb +8 -8
  35. data/spec/integration/has_many_associations_spec.rb +15 -15
  36. data/spec/integration/nested_attribute_spec.rb +2 -2
  37. data/spec/integration/relation_spec.rb +1 -1
  38. data/spec/integration/solr_instance_loader_spec.rb +2 -1
  39. data/spec/samples/special_thing.rb +2 -2
  40. data/spec/spec_helper.rb +1 -0
  41. data/spec/unit/attached_files_spec.rb +41 -5
  42. data/spec/unit/attributes_spec.rb +1 -1
  43. data/spec/unit/change_set_spec.rb +1 -1
  44. data/spec/unit/code_configurator_spec.rb +3 -3
  45. data/spec/unit/core_spec.rb +16 -1
  46. data/spec/unit/file_spec.rb +14 -10
  47. data/spec/unit/has_and_belongs_to_many_association_spec.rb +6 -6
  48. data/spec/unit/has_many_association_spec.rb +4 -4
  49. data/spec/unit/indexing_service_spec.rb +2 -2
  50. data/spec/unit/ldp_resource_spec.rb +17 -0
  51. data/spec/unit/logger_spec.rb +1 -1
  52. data/spec/unit/persistence_spec.rb +24 -20
  53. data/spec/unit/predicates_spec.rb +7 -7
  54. data/spec/unit/sparql_insert_spec.rb +2 -2
  55. data/spec/unit/validations_spec.rb +12 -2
  56. metadata +13 -8
  57. data/lib/active_fedora/rdf/rels_ext.rb +0 -26
@@ -22,7 +22,7 @@ Gem::Specification.new do |s|
22
22
  s.add_dependency "rdf-rdfxml", '~> 1.1.0'
23
23
  s.add_dependency "linkeddata"
24
24
  s.add_dependency "deprecation"
25
- s.add_dependency "ldp", '~> 0.2.0'
25
+ s.add_dependency "ldp", '~> 0.2.1'
26
26
  s.add_dependency "rdf-ldp"
27
27
 
28
28
  s.add_development_dependency "rdoc"
@@ -1,5 +1,5 @@
1
1
  # The default namespace maps to the default namespace for generating relationships from solr
2
- :default_namespace: http://fedora.info/definitions/v4/rels-ext#
2
+ :default_namespace: info:fedora/fedora-system:def/relations-external#
3
3
 
4
4
  # namespace mappings---
5
5
  # you can add specific mappings for your institution by providing the following:
@@ -14,7 +14,7 @@
14
14
  # :oai_item_id => ["info:fedora/oai:example.edu:changeme:500"]
15
15
  #
16
16
  :predicate_mapping:
17
- http://fedora.info/definitions/v4/rels-ext#:
17
+ info:fedora/fedora-system:def/relations-external#:
18
18
  :conforms_to: conformsTo
19
19
  :has_annotation: hasAnnotation
20
20
  :has_collection_member: hasCollectionMember
@@ -46,6 +46,7 @@ module ActiveFedora #:nodoc:
46
46
  autoload :Attributes
47
47
  autoload :AutosaveAssociation
48
48
  autoload :Base
49
+ autoload :CachingConnection
49
50
  autoload :Callbacks
50
51
  autoload :ChangeSet
51
52
  autoload :Config
@@ -62,7 +63,9 @@ module ActiveFedora #:nodoc:
62
63
  autoload :FixityService
63
64
  autoload :Indexing
64
65
  autoload :IndexingService
66
+ autoload :LdpCache
65
67
  autoload :LdpResource
68
+ autoload :LdpResourceService
66
69
  autoload :LoadableFromJson
67
70
  autoload :Model
68
71
  autoload :NestedAttributes
@@ -41,20 +41,20 @@ module ActiveFedora
41
41
  end
42
42
  deprecation_deprecate :clear_datastreams
43
43
 
44
- def datastream_assertions
44
+ def contains_assertions
45
45
  resource.query(subject: resource, predicate: Ldp.contains).objects.map(&:to_s)
46
46
  end
47
47
 
48
48
  def load_attached_files
49
- datastream_assertions.each do |ds_uri|
50
- dsid = ds_uri.to_s.sub(uri + '/', '')
51
- next if association(dsid.to_sym)
52
- create_singleton_association(dsid)
49
+ contains_assertions.each do |file_uri|
50
+ path = file_uri.to_s.sub(uri + '/', '')
51
+ next if association(path.to_sym)
52
+ create_singleton_association(path)
53
53
  end
54
54
  end
55
55
 
56
56
  # Adds datastream to the object.
57
- # @return [String] dsid of the added datastream
57
+ # @return [String] path of the added datastream
58
58
  def attach_file(file, file_path, opts={})
59
59
  create_singleton_association(file_path)
60
60
  attached_files[file_path] = file
@@ -75,19 +75,36 @@ module ActiveFedora
75
75
  # File Management
76
76
  #
77
77
 
78
+ def add_file_datastream(file, opts={})
79
+ Deprecation.warn AttachedFiles, "add_file_datastream is deprecated and will be removed in active-fedora 10.0. Use add_file instead"
80
+ add_file(file, opts)
81
+ end
82
+
78
83
  # Attach the given file to object
79
84
  #
80
85
  # @param [File] file the file to add
81
86
  # @param [Hash] opts options: :dsid, :prefix, :mime_type
82
- # @option opts [String] :dsid The datastream id
83
- # @option opts [String] :prefix The datastream prefix (for auto-generated dsid)
87
+ # @option opts [String] :path The file path
88
+ # @option opts [String] :prefix The path prefix (for auto-generated path)
84
89
  # @option opts [String] :mime_type The Mime-Type of the file
85
90
  # @option opts [String] :original_name The original name of the file (used for Content-Disposition)
86
- def add_file_datastream(file, opts={})
87
- find_or_create_child_resource(opts).tap do |node|
91
+ def add_file(file, *args)
92
+ opts = if args.size == 1
93
+ args.first
94
+ else
95
+ Deprecation.warn AttachedFiles, "The second option to add_file should be a hash. Passing the file path is deprecated and will be removed in active-fedora 10.0.", caller
96
+ { path: args[0], original_name: args[1], mime_type: args[2] }
97
+ end
98
+
99
+ if opts[:dsid]
100
+ Deprecation.warn AttachedFiles, "The :dsid option to add_file is deprecated and will be removed in active-fedora 10.0. Use :path instead", caller
101
+ opts[:path] = opts[:dsid]
102
+ end
103
+
104
+ find_or_create_child_resource(opts[:path], opts[:prefix]).tap do |node|
88
105
  node.content = file
89
106
  node.mime_type = if opts[:mimeType]
90
- Deprecation.warn AttachedFiles, "The :mimeType option to add_file_datastream is deprecated and will be removed in active-fedora 10.0. Use :mime_type instead", caller
107
+ Deprecation.warn AttachedFiles, "The :mimeType option to add_file is deprecated and will be removed in active-fedora 10.0. Use :mime_type instead", caller
91
108
  opts[:mimeType]
92
109
  else
93
110
  opts[:mime_type]
@@ -113,10 +130,10 @@ module ActiveFedora
113
130
  association
114
131
  end
115
132
 
116
- def find_or_create_child_resource(opts)
117
- association = association(opts[:dsid].to_sym) if opts[:dsid]
133
+ def find_or_create_child_resource(path, prefix)
134
+ association = association(path.to_sym) if path
118
135
  association ||= begin
119
- file_path = FilePathBuilder.build(self, opts[:dsid], opts[:prefix])
136
+ file_path = FilePathBuilder.build(self, path, prefix)
120
137
  create_singleton_association(file_path)
121
138
  end
122
139
  association.reader
@@ -2,6 +2,7 @@ SOLR_DOCUMENT_ID = "id" unless (defined?(SOLR_DOCUMENT_ID) && !SOLR_DOCUMENT_ID.
2
2
  ENABLE_SOLR_UPDATES = true unless defined?(ENABLE_SOLR_UPDATES)
3
3
  require 'active_support/descendants_tracker'
4
4
  require 'active_fedora/errors'
5
+ require 'active_fedora/log_subscriber'
5
6
 
6
7
  module ActiveFedora
7
8
 
@@ -26,6 +27,7 @@ module ActiveFedora
26
27
  class Base
27
28
  extend ActiveModel::Naming
28
29
  extend ActiveSupport::DescendantsTracker
30
+ extend LdpCache::ClassMethods
29
31
 
30
32
  include Core
31
33
  include Persistence
@@ -0,0 +1,80 @@
1
+ module ActiveFedora
2
+ class CachingConnection < Ldp::Client
3
+ def initialize(host)
4
+ super
5
+ @cache = {}
6
+ @cache_enabled = false
7
+ end
8
+
9
+ def get(url, options = {})
10
+ if @cache_enabled
11
+ cache_resource(url) { super }
12
+ else
13
+ super
14
+ end
15
+ end
16
+
17
+ def post(*)
18
+ clear_cache if @cache_enabled
19
+ super
20
+ end
21
+
22
+ def put(*)
23
+ clear_cache if @cache_enabled
24
+ super
25
+ end
26
+
27
+ def patch(*)
28
+ clear_cache if @cache_enabled
29
+ super
30
+ end
31
+
32
+ # Enable the cache within the block.
33
+ def cache
34
+ old, @cache_enabled = @cache_enabled, true
35
+ yield
36
+ ensure
37
+ @cache_enabled = old
38
+ clear_cache unless @cache_enabled
39
+ end
40
+
41
+ def enable_cache!
42
+ @cache_enabled = true
43
+ end
44
+
45
+ def disable_cache!
46
+ @cache_enabled = false
47
+ end
48
+
49
+ # Disable the query cache within the block.
50
+ def uncached
51
+ old, @cache_enabled = @cache_enabled, false
52
+ yield
53
+ ensure
54
+ @cache_enabled = old
55
+ end
56
+
57
+ def clear_cache
58
+ @cache.clear
59
+ end
60
+
61
+ private
62
+
63
+ def log(url)
64
+ ActiveSupport::Notifications.instrument("ldp.active_fedora",
65
+ id: url, name: "Load LDP", ldp_service: object_id) { yield }
66
+ end
67
+
68
+ def cache_resource(url, &block)
69
+ result =
70
+ if @cache.key?(url)
71
+ ActiveSupport::Notifications.instrument("ldp.active_fedora",
72
+ id: url, name: "CACHE", ldp_service: object_id)
73
+ @cache[url]
74
+ else
75
+ @cache[url] = log(url) { yield }
76
+ end
77
+ result.dup
78
+ end
79
+ end
80
+ end
@@ -18,7 +18,7 @@ module ActiveFedora
18
18
  # * (5) <tt>after_create</tt>
19
19
  # * (6) <tt>after_save</tt>
20
20
  #
21
- # Lastly an <tt>after_find</tt> and <tt>after_initialize</tt> callback is triggered for each object that
21
+ # Lastly an <tt>after_find</tt> and <tt>after_initialize</tt> callback is triggered for each object that
22
22
  # is found and instantiated by a finder, with <tt>after_initialize</tt> being triggered after new objects
23
23
  # are instantiated as well.
24
24
  #
@@ -191,23 +191,23 @@ module ActiveFedora
191
191
  # methods on the model, which are called last.
192
192
  #
193
193
  # == Debugging callbacks
194
- #
195
- # The callback chain is accessible via the <tt>_*_callbacks</tt> method on an object. ActiveModel Callbacks support
194
+ #
195
+ # The callback chain is accessible via the <tt>_*_callbacks</tt> method on an object. ActiveModel Callbacks support
196
196
  # <tt>:before</tt>, <tt>:after</tt> and <tt>:around</tt> as values for the <tt>kind</tt> property. The <tt>kind</tt> property
197
197
  # defines what part of the chain the callback runs in.
198
- #
199
- # To find all callbacks in the before_save callback chain:
200
- #
198
+ #
199
+ # To find all callbacks in the before_save callback chain:
200
+ #
201
201
  # Topic._save_callbacks.select { |cb| cb.kind.eql?(:before) }
202
- #
202
+ #
203
203
  # Returns an array of callback objects that form the before_save chain.
204
- #
204
+ #
205
205
  # To further check if the before_save chain contains a proc defined as <tt>rest_when_dead</tt> use the <tt>filter</tt> property of the callback object:
206
- #
206
+ #
207
207
  # Topic._save_callbacks.select { |cb| cb.kind.eql?(:before) }.collect(&:filter).include?(:rest_when_dead)
208
- #
208
+ #
209
209
  # Returns true or false depending on whether the proc is contained in the before_save callback chain on a Topic model.
210
- #
210
+ #
211
211
  module Callbacks
212
212
  extend ActiveSupport::Concern
213
213
 
@@ -226,7 +226,7 @@ module ActiveFedora
226
226
  define_model_callbacks :save, :create, :update, :destroy
227
227
  end
228
228
 
229
- def destroy #:nodoc:
229
+ def destroy(*) #:nodoc:
230
230
  run_callbacks(:destroy) { super }
231
231
  end
232
232
 
@@ -239,10 +239,9 @@ module ActiveFedora
239
239
  end
240
240
 
241
241
  def update_record(*) #:nodoc:
242
- run_callbacks(:update) {
242
+ run_callbacks(:update) {
243
243
  run_callbacks(:save) { super }
244
244
  }
245
245
  end
246
246
  end
247
247
  end
248
-
@@ -50,8 +50,7 @@ module ActiveFedora
50
50
  check_persistence unless persisted?
51
51
  clear_association_cache
52
52
  clear_attached_files
53
- @ldp_source = LdpResource.new(conn, uri)
54
- @resource = nil
53
+ refresh
55
54
  load_attached_files
56
55
  self
57
56
  end
@@ -103,10 +102,6 @@ module ActiveFedora
103
102
  @readonly = true
104
103
  end
105
104
 
106
- def to_uri(id)
107
- self.class.id_to_uri(id)
108
- end
109
-
110
105
  protected
111
106
 
112
107
  # This can be overriden to assert a different model
@@ -116,6 +111,9 @@ module ActiveFedora
116
111
  end
117
112
 
118
113
  module ClassMethods
114
+
115
+ SLASH = '/'.freeze
116
+
119
117
  def generated_association_methods
120
118
  @generated_association_methods ||= begin
121
119
  mod = const_set(:GeneratedAssociationMethods, Module.new)
@@ -138,8 +136,10 @@ module ActiveFedora
138
136
  if translate_id_to_uri
139
137
  translate_id_to_uri.call(id)
140
138
  else
141
- id = "/#{id}" unless id.start_with? '/'
142
- id = ActiveFedora.fedora.base_path + id unless id.start_with? "#{ActiveFedora.fedora.base_path}/"
139
+ id = "/#{id}" unless id.start_with? SLASH
140
+ unless ActiveFedora.fedora.base_path == SLASH || id.start_with?("#{ActiveFedora.fedora.base_path}/")
141
+ id = ActiveFedora.fedora.base_path + id
142
+ end
143
143
  ActiveFedora.fedora.host + id
144
144
  end
145
145
  end
@@ -181,16 +181,8 @@ module ActiveFedora
181
181
  @association_cache = {}
182
182
  end
183
183
 
184
- def conn
185
- ActiveFedora.fedora.connection
186
- end
187
-
188
184
  def build_ldp_resource(id=nil)
189
- if id
190
- LdpResource.new(conn, to_uri(id))
191
- else
192
- LdpResource.new(conn, nil, nil, ActiveFedora.fedora.host + ActiveFedora.fedora.base_path)
193
- end
185
+ ActiveFedora.fedora.ldp_resource_service.build(self.class, id)
194
186
  end
195
187
 
196
188
  def check_persistence
@@ -27,7 +27,7 @@ module ActiveFedora #:nodoc:
27
27
  # end
28
28
  #
29
29
  # class Patch < ActiveFedora::Base
30
- # belongs_to :ticket, predicate: ActiveFedora::RDF::RelsExt.isComponentOf
30
+ # belongs_to :ticket, predicate: ActiveFedora::RDF::Fcrepo::RelsExt.isComponentOf
31
31
  # end
32
32
  #
33
33
  # # Comments are not patches, this assignment raises AssociationTypeMismatch.
@@ -14,7 +14,11 @@ module ActiveFedora
14
14
  end
15
15
 
16
16
  def connection
17
- @connection ||= Ldp::Client.new(host)
17
+ @connection ||= CachingConnection.new(host)
18
+ end
19
+
20
+ def ldp_resource_service
21
+ @service ||= LdpResourceService.new(connection)
18
22
  end
19
23
 
20
24
  SLASH = '/'.freeze
@@ -12,12 +12,10 @@ module ActiveFedora
12
12
  define_model_callbacks :save, :create, :destroy
13
13
  define_model_callbacks :initialize, only: :after
14
14
 
15
- attr_accessor :last_modified
16
-
17
15
  # @param parent_or_url [ActiveFedora::Base, String, Hash, NilClass] the parent resource or the URI of this resource
18
16
  # @param path_name [String] the path partial relative to the resource
19
17
  # @param options [Hash]
20
- def initialize(parent_or_url_or_hash = nil, dsid=nil, options={})
18
+ def initialize(parent_or_url_or_hash = nil, path=nil, options={})
21
19
  case parent_or_url_or_hash
22
20
  when Hash
23
21
  content = ''
@@ -31,7 +29,7 @@ module ActiveFedora
31
29
  uri = if parent_or_url_or_hash.uri.kind_of?(::RDF::URI) && parent_or_url_or_hash.uri.value.empty?
32
30
  nil
33
31
  else
34
- "#{parent_or_url_or_hash.uri}/#{dsid}"
32
+ "#{parent_or_url_or_hash.uri}/#{path}"
35
33
  end
36
34
  @ldp_source = Ldp::Resource::BinarySource.new(ldp_connection, uri, nil, ActiveFedora.fedora.host + ActiveFedora.fedora.base_path)
37
35
 
@@ -174,13 +172,6 @@ module ActiveFedora
174
172
  "#<#{self.class} uri=\"#{uri}\" >"
175
173
  end
176
174
 
177
- #compatibility method for rails' url generators. This method will
178
- #urlescape escape dots, which are apparently
179
- #invalid characters in a dsid.
180
- def to_param
181
- dsid.gsub(/\./, '%2e')
182
- end
183
-
184
175
  # @abstract Override this in your concrete datastream class.
185
176
  # @return [boolean] does this datastream contain metadata (not file data)
186
177
  def metadata?
@@ -212,14 +203,14 @@ module ActiveFedora
212
203
 
213
204
  # The string to prefix all solr fields with. Override this method if you want
214
205
  # a prefix other than the default
215
- def prefix(dsid)
216
- dsid ? "#{dsid.underscore}__" : ''
206
+ def prefix(path)
207
+ path ? "#{path.underscore}__" : ''
217
208
  end
218
209
 
219
210
  def fetch_original_name_from_headers
220
211
  return if new_record?
221
212
  m = ldp_source.head.headers['Content-Disposition'].match(/filename="(?<filename>[^"]*)";/)
222
- m[:filename]
213
+ URI.decode(m[:filename])
223
214
  end
224
215
 
225
216
  def fetch_mime_type
@@ -251,13 +242,10 @@ module ActiveFedora
251
242
 
252
243
  def save(*)
253
244
  return unless content_changed?
254
- payload = behaves_like_io?(content) ? content.read : content
255
- headers = { 'Content-Type' => mime_type }
256
- headers['Content-Disposition'] = "attachment; filename=\"#{@original_name}\"" if @original_name
257
- # Setting the content-length is required until we figure out why Faraday
258
- # is not doing this automatically for files uploaded via ActionDispatch.
259
- headers['Content-Length'] = payload.size.to_s if uploaded_file?(payload)
260
- ldp_source.content = payload
245
+ headers = { 'Content-Type'.freeze => mime_type, 'Content-Length'.freeze => content.size.to_s }
246
+ headers['Content-Disposition'.freeze] = "attachment; filename=\"#{URI.encode(@original_name)}\"" if @original_name
247
+
248
+ ldp_source.content = content
261
249
  if new_record?
262
250
  ldp_source.create do |req|
263
251
  req.headers = headers
@@ -275,24 +263,6 @@ module ActiveFedora
275
263
  ldp_source.get.body
276
264
  end
277
265
 
278
- # @param range [String] the Range HTTP header
279
- # @yield [chunk] a block that receives chunked content
280
- def stream(range = nil, &block)
281
- uri = URI.parse(self.uri)
282
-
283
- headers = {}
284
- headers['Range'] = range if range
285
- Net::HTTP.start(uri.host, uri.port) do |http|
286
- request = Net::HTTP::Get.new uri, headers
287
- http.request request do |response|
288
- raise "Couldn't get data from Fedora (#{uri}). Response: #{response.code}" unless response.is_a?(Net::HTTPSuccess)
289
- response.read_body do |chunk|
290
- block.call(chunk)
291
- end
292
- end
293
- end
294
- end
295
-
296
266
  private
297
267
 
298
268
  def uploaded_file?(payload)
@@ -318,7 +288,40 @@ module ActiveFedora
318
288
  end
319
289
  end
320
290
 
291
+ module Streaming
292
+ # @param range [String] the Range HTTP header
293
+ # @returns [Stream] an object that responds to each
294
+ def stream(range = nil)
295
+ uri = URI.parse(self.uri)
296
+ headers = {}
297
+ headers['Range'] = range if range
298
+ FileBody.new(uri, headers)
299
+ end
300
+
301
+ class FileBody
302
+ attr_reader :uri, :headers
303
+ def initialize(uri, headers)
304
+ @uri = uri
305
+ @headers = headers
306
+ end
307
+
308
+ def each
309
+ Net::HTTP.start(uri.host, uri.port) do |http|
310
+ request = Net::HTTP::Get.new uri, headers
311
+ http.request request do |response|
312
+
313
+ raise "Couldn't get data from Fedora (#{uri}). Response: #{response.code}" unless response.is_a?(Net::HTTPSuccess)
314
+ response.read_body do |chunk|
315
+ yield chunk
316
+ end
317
+ end
318
+ end
319
+ end
320
+ end
321
+ end
322
+
321
323
  include ActiveFedora::File::Persistence
324
+ include ActiveFedora::File::Streaming
322
325
  include ActiveFedora::Versionable
323
326
  end
324
327