active-fedora 9.0.0.rc3 → 9.0.0

Sign up to get free protection for your applications and to get access to all the features.
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