active-fedora 9.10.0.pre2 → 9.10.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.
- checksums.yaml +4 -4
- data/.rubocop.yml +1 -0
- data/History.txt +141 -0
- data/lib/active_fedora/associations/association.rb +38 -12
- data/lib/active_fedora/associations/association_scope.rb +6 -2
- data/lib/active_fedora/associations/basic_contains_association.rb +2 -3
- data/lib/active_fedora/associations/belongs_to_association.rb +23 -4
- data/lib/active_fedora/associations/builder/association.rb +37 -56
- data/lib/active_fedora/associations/builder/belongs_to.rb +27 -1
- data/lib/active_fedora/associations/builder/collection_association.rb +33 -2
- data/lib/active_fedora/associations/builder/contains.rb +3 -3
- data/lib/active_fedora/associations/builder/directly_contains.rb +1 -7
- data/lib/active_fedora/associations/builder/directly_contains_one.rb +20 -17
- data/lib/active_fedora/associations/builder/has_and_belongs_to_many.rb +1 -1
- data/lib/active_fedora/associations/builder/has_many.rb +2 -43
- data/lib/active_fedora/associations/builder/indirectly_contains.rb +1 -7
- data/lib/active_fedora/associations/builder/property.rb +2 -13
- data/lib/active_fedora/associations/builder/singular_association.rb +2 -6
- data/lib/active_fedora/associations/builder/singular_property.rb +2 -3
- data/lib/active_fedora/associations/collection_association.rb +6 -14
- data/lib/active_fedora/associations/directly_contains_one_association.rb +1 -2
- data/lib/active_fedora/associations/has_and_belongs_to_many_association.rb +1 -1
- data/lib/active_fedora/associations/has_many_association.rb +23 -0
- data/lib/active_fedora/attached_files.rb +1 -1
- data/lib/active_fedora/core.rb +2 -1
- data/lib/active_fedora/errors.rb +13 -0
- data/lib/active_fedora/file.rb +16 -1
- data/lib/active_fedora/file/attributes.rb +6 -6
- data/lib/active_fedora/reflection.rb +200 -76
- data/lib/active_fedora/version.rb +1 -1
- data/lib/active_fedora/with_metadata/metadata_node.rb +2 -1
- data/lib/generators/active_fedora/config/fedora/fedora_generator.rb +4 -0
- data/lib/generators/active_fedora/config/fedora/templates/.fcrepo_wrapper +3 -0
- data/lib/generators/active_fedora/config/solr/solr_generator.rb +4 -0
- data/lib/generators/active_fedora/config/solr/templates/.solr_wrapper +5 -0
- data/spec/integration/attached_files_spec.rb +4 -4
- data/spec/integration/file_spec.rb +1 -1
- data/spec/integration/om_datastream_spec.rb +6 -6
- data/spec/unit/collection_proxy_spec.rb +1 -1
- data/spec/unit/file_spec.rb +19 -0
- data/spec/unit/has_and_belongs_to_many_association_spec.rb +4 -4
- data/spec/unit/has_many_association_spec.rb +2 -2
- data/spec/unit/reflection_spec.rb +2 -2
- metadata +6 -4
data/lib/active_fedora/errors.rb
CHANGED
@@ -44,6 +44,19 @@ module ActiveFedora #:nodoc:
|
|
44
44
|
class AssociationNotFoundError < ConfigurationError #:nodoc:
|
45
45
|
end
|
46
46
|
|
47
|
+
# This error is raised when trying to destroy a parent instance in N:1 or 1:1 associations
|
48
|
+
# (has_many, has_one) when there is at least 1 child associated instance.
|
49
|
+
# ex: if @project.tasks.size > 0, DeleteRestrictionError will be raised when trying to destroy @project
|
50
|
+
class DeleteRestrictionError < ActiveFedoraError #:nodoc:
|
51
|
+
def initialize(name = nil)
|
52
|
+
if name
|
53
|
+
super("Cannot delete record because of dependent #{name}")
|
54
|
+
else
|
55
|
+
super("Delete restriction error.")
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
47
60
|
# Raised when ActiveFedora cannot find the predicate corresponding to the given property
|
48
61
|
# in the predicate registy
|
49
62
|
class UnregisteredPredicateError < ActiveFedoraError
|
data/lib/active_fedora/file.rb
CHANGED
@@ -63,6 +63,12 @@ module ActiveFedora
|
|
63
63
|
end
|
64
64
|
end
|
65
65
|
|
66
|
+
def save
|
67
|
+
super.tap do
|
68
|
+
metadata.save if metadata.changed?
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
66
72
|
def described_by
|
67
73
|
raise "#{self} isn't persisted yet" if new_record?
|
68
74
|
links['describedby'].first
|
@@ -114,6 +120,7 @@ module ActiveFedora
|
|
114
120
|
def attribute_will_change!(attr)
|
115
121
|
if attr == 'content'
|
116
122
|
changed_attributes['content'] = true
|
123
|
+
elsif attr == 'type'
|
117
124
|
else
|
118
125
|
super
|
119
126
|
end
|
@@ -138,7 +145,15 @@ module ActiveFedora
|
|
138
145
|
end
|
139
146
|
|
140
147
|
def changed?
|
141
|
-
super || content_changed?
|
148
|
+
super || content_changed? || metadata_changed?
|
149
|
+
end
|
150
|
+
|
151
|
+
def metadata_changed?
|
152
|
+
if new_record? || links['describedby'].blank?
|
153
|
+
false
|
154
|
+
else
|
155
|
+
metadata.changed?
|
156
|
+
end
|
142
157
|
end
|
143
158
|
|
144
159
|
def inspect
|
@@ -1,10 +1,10 @@
|
|
1
1
|
module ActiveFedora::File::Attributes
|
2
|
-
attr_writer :mime_type
|
3
|
-
|
4
2
|
def mime_type
|
5
|
-
|
3
|
+
fetch_mime_type
|
6
4
|
end
|
7
5
|
|
6
|
+
delegate :mime_type=, to: :metadata
|
7
|
+
|
8
8
|
def original_name
|
9
9
|
@original_name ||= fetch_original_name
|
10
10
|
end
|
@@ -25,7 +25,7 @@ module ActiveFedora::File::Attributes
|
|
25
25
|
end
|
26
26
|
|
27
27
|
def dirty_size
|
28
|
-
content.size if
|
28
|
+
content.size if content_changed? && content.respond_to?(:size)
|
29
29
|
end
|
30
30
|
|
31
31
|
def size
|
@@ -68,8 +68,8 @@ module ActiveFedora::File::Attributes
|
|
68
68
|
end
|
69
69
|
|
70
70
|
def fetch_mime_type
|
71
|
-
return default_mime_type if new_record?
|
72
|
-
|
71
|
+
return default_mime_type if new_record? && metadata.mime_type.blank?
|
72
|
+
metadata.mime_type.first
|
73
73
|
end
|
74
74
|
|
75
75
|
def fetch_original_name
|
@@ -3,29 +3,35 @@ module ActiveFedora
|
|
3
3
|
extend ActiveSupport::Concern
|
4
4
|
|
5
5
|
included do
|
6
|
-
class_attribute :
|
7
|
-
self.
|
6
|
+
class_attribute :_reflections
|
7
|
+
self._reflections = {}
|
8
8
|
end
|
9
9
|
|
10
|
-
|
11
|
-
|
10
|
+
def reflections
|
11
|
+
self.class.reflections
|
12
|
+
end
|
13
|
+
|
14
|
+
class << self
|
15
|
+
def create(macro, name, scope, options, active_fedora)
|
12
16
|
klass = case macro
|
13
17
|
when :has_many, :belongs_to, :has_and_belongs_to_many, :contains, :directly_contains, :directly_contains_one, :indirectly_contains
|
14
18
|
AssociationReflection
|
15
19
|
when :rdf, :singular_rdf
|
16
20
|
RDFPropertyReflection
|
17
21
|
end
|
18
|
-
reflection = klass.new(macro, name, options, active_fedora)
|
19
|
-
add_reflection name, reflection
|
20
|
-
|
22
|
+
reflection = klass.new(macro, name, scope, options, active_fedora)
|
23
|
+
add_reflection(active_fedora, name, reflection)
|
21
24
|
reflection
|
22
25
|
end
|
23
26
|
|
24
|
-
def add_reflection(name, reflection)
|
27
|
+
def add_reflection(active_fedora, name, reflection)
|
28
|
+
active_fedora.clear_reflections_cache
|
25
29
|
# FIXME: this is where the problem with association_spec is caused (key is string now)
|
26
|
-
|
30
|
+
active_fedora._reflections = active_fedora._reflections.merge(name => reflection)
|
27
31
|
end
|
32
|
+
end
|
28
33
|
|
34
|
+
module ClassMethods
|
29
35
|
# Returns a hash containing all AssociationReflection objects for the current class.
|
30
36
|
# Example:
|
31
37
|
#
|
@@ -33,7 +39,47 @@ module ActiveFedora
|
|
33
39
|
# Account.reflections
|
34
40
|
#
|
35
41
|
def reflections
|
36
|
-
|
42
|
+
@__reflections ||= begin
|
43
|
+
ref = {}
|
44
|
+
|
45
|
+
_reflections.each do |name, reflection|
|
46
|
+
parent_reflection = reflection.parent_reflection
|
47
|
+
|
48
|
+
if parent_reflection
|
49
|
+
parent_name = parent_reflection.name
|
50
|
+
ref[parent_name.to_s] = parent_reflection
|
51
|
+
else
|
52
|
+
ref[name] = reflection
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
ref
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# Returns an array of AssociationReflection objects for all the
|
61
|
+
# associations in the class. If you only want to reflect on a certain
|
62
|
+
# association type, pass in the symbol (<tt>:has_many</tt>, <tt>:has_one</tt>,
|
63
|
+
# <tt>:belongs_to</tt>) as the first parameter.
|
64
|
+
#
|
65
|
+
# Example:
|
66
|
+
#
|
67
|
+
# Account.reflect_on_all_associations # returns an array of all associations
|
68
|
+
# Account.reflect_on_all_associations(:has_many) # returns an array of all has_many associations
|
69
|
+
#
|
70
|
+
def reflect_on_all_associations(macro = nil)
|
71
|
+
association_reflections = reflections.dup
|
72
|
+
association_reflections.select! { |_k, reflection| reflection.macro == macro } if macro
|
73
|
+
association_reflections
|
74
|
+
end
|
75
|
+
|
76
|
+
# Returns the AssociationReflection object for the +association+ (use the symbol).
|
77
|
+
#
|
78
|
+
# Account.reflect_on_association(:owner) # returns the owner AssociationReflection
|
79
|
+
# Invoice.reflect_on_association(:line_items).macro # returns :has_many
|
80
|
+
#
|
81
|
+
def reflect_on_association(association)
|
82
|
+
__reflect_on_association(association)
|
37
83
|
end
|
38
84
|
|
39
85
|
def outgoing_reflections
|
@@ -41,11 +87,11 @@ module ActiveFedora
|
|
41
87
|
end
|
42
88
|
|
43
89
|
def child_resource_reflections
|
44
|
-
|
90
|
+
reflect_on_all_associations(:contains).select { |_, reflection| reflection.klass <= ActiveFedora::File }
|
45
91
|
end
|
46
92
|
|
47
93
|
def contained_rdf_source_reflections
|
48
|
-
|
94
|
+
reflect_on_all_associations(:contains).select { |_, reflection| !(reflection.klass <= ActiveFedora::File) }
|
49
95
|
end
|
50
96
|
|
51
97
|
# Returns the AssociationReflection object for the +association+ (use the symbol).
|
@@ -66,14 +112,79 @@ module ActiveFedora
|
|
66
112
|
def reflect_on_all_autosave_associations
|
67
113
|
reflections.values.select { |reflection| reflection.options[:autosave] }
|
68
114
|
end
|
115
|
+
|
116
|
+
def clear_reflections_cache # :nodoc:
|
117
|
+
@__reflections = nil
|
118
|
+
end
|
69
119
|
end
|
70
120
|
|
71
|
-
|
121
|
+
# Holds all the methods that are shared between MacroReflection and ThroughReflection.
|
122
|
+
#
|
123
|
+
# AbstractReflection
|
124
|
+
# MacroReflection
|
125
|
+
# AggregateReflection
|
126
|
+
# AssociationReflection
|
127
|
+
# HasManyReflection
|
128
|
+
# HasOneReflection
|
129
|
+
# BelongsToReflection
|
130
|
+
# HasAndBelongsToManyReflection
|
131
|
+
# ThroughReflection
|
132
|
+
# PolymorphicReflection
|
133
|
+
# RuntimeReflection
|
134
|
+
class AbstractReflection # :nodoc:
|
135
|
+
def through_reflection?
|
136
|
+
false
|
137
|
+
end
|
138
|
+
|
139
|
+
# Returns a new, unsaved instance of the associated class. +attributes+ will
|
140
|
+
# be passed to the class's constructor.
|
141
|
+
def build_association(attributes, &block)
|
142
|
+
klass.new(attributes, &block)
|
143
|
+
end
|
144
|
+
|
145
|
+
# Returns the class name for the macro.
|
146
|
+
#
|
147
|
+
# <tt>composed_of :balance, class_name: 'Money'</tt> returns <tt>'Money'</tt>
|
148
|
+
# <tt>has_many :clients</tt> returns <tt>'Client'</tt>
|
149
|
+
def class_name
|
150
|
+
@class_name ||= (options[:class_name] || derive_class_name).to_s
|
151
|
+
end
|
152
|
+
|
153
|
+
def constraints
|
154
|
+
scope_chain.flatten
|
155
|
+
end
|
156
|
+
|
157
|
+
def inverse_of
|
158
|
+
return unless inverse_name
|
159
|
+
|
160
|
+
@inverse_of ||= klass._reflect_on_association inverse_name
|
161
|
+
end
|
162
|
+
|
163
|
+
def check_validity_of_inverse!
|
164
|
+
unless polymorphic?
|
165
|
+
if has_inverse? && inverse_of.nil?
|
166
|
+
raise InverseOfAssociationNotFoundError, self
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def alias_candidate(name)
|
172
|
+
"#{plural_name}_#{name}"
|
173
|
+
end
|
174
|
+
|
175
|
+
def chain
|
176
|
+
collect_join_chain
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
class MacroReflection < AbstractReflection
|
72
181
|
# Returns the name of the macro.
|
73
182
|
#
|
74
183
|
# <tt>has_many :clients</tt> returns <tt>:clients</tt>
|
75
184
|
attr_reader :name
|
76
185
|
|
186
|
+
attr_reader :scope
|
187
|
+
|
77
188
|
# Returns the macro type.
|
78
189
|
#
|
79
190
|
# <tt>has_many :clients</tt> returns <tt>:has_many</tt>
|
@@ -86,27 +197,13 @@ module ActiveFedora
|
|
86
197
|
|
87
198
|
attr_reader :active_fedora
|
88
199
|
|
89
|
-
|
90
|
-
#
|
91
|
-
# class Author < ActiveRecord::Base
|
92
|
-
# has_many :books
|
93
|
-
# end
|
94
|
-
#
|
95
|
-
# Author._reflect_on_association(:books).klass
|
96
|
-
# # => Book
|
97
|
-
#
|
98
|
-
# <b>Note:</b> Do not call +klass.new+ or +klass.create+ to instantiate
|
99
|
-
# a new association object. Use +build_association+ or +create_association+
|
100
|
-
# instead. This allows plugins to hook into association object creation.
|
101
|
-
def klass
|
102
|
-
@klass ||= class_name.constantize
|
103
|
-
end
|
104
|
-
|
105
|
-
def initialize(macro, name, options, active_fedora)
|
200
|
+
def initialize(macro, name, scope, options, active_fedora)
|
106
201
|
@macro = macro
|
107
202
|
@name = name
|
203
|
+
@scope = scope
|
108
204
|
@options = options
|
109
205
|
@active_fedora = active_fedora
|
206
|
+
@klass = options[:anonymous_class]
|
110
207
|
@automatic_inverse_of = nil
|
111
208
|
end
|
112
209
|
|
@@ -116,37 +213,26 @@ module ActiveFedora
|
|
116
213
|
parent_reflection.autosave = autosave if parent_reflection
|
117
214
|
end
|
118
215
|
|
119
|
-
# Returns
|
120
|
-
# be passed to the class's constructor.
|
121
|
-
def build_association(*options, &block)
|
122
|
-
klass.new(*options, &block)
|
123
|
-
end
|
124
|
-
|
125
|
-
# Returns the class name for the macro.
|
216
|
+
# Returns the class for the macro.
|
126
217
|
#
|
127
|
-
# <tt>
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
# Returns whether or not this association reflection is for a collection
|
133
|
-
# association. Returns +true+ if the +macro+ is either +has_many+ or
|
134
|
-
# +has_and_belongs_to_many+, +false+ otherwise.
|
135
|
-
def collection?
|
136
|
-
@collection
|
137
|
-
end
|
138
|
-
|
139
|
-
# Returns +true+ if +self+ is a +belongs_to+ reflection.
|
140
|
-
def belongs_to?
|
141
|
-
macro == :belongs_to
|
218
|
+
# <tt>composed_of :balance, class_name: 'Money'</tt> returns the Money class
|
219
|
+
# <tt>has_many :clients</tt> returns the Client class
|
220
|
+
def klass
|
221
|
+
@klass ||= compute_class(class_name)
|
142
222
|
end
|
143
223
|
|
144
|
-
def
|
145
|
-
|
224
|
+
def compute_class(name)
|
225
|
+
name.constantize
|
146
226
|
end
|
147
227
|
|
148
|
-
|
149
|
-
|
228
|
+
# Returns +true+ if +self+ and +other_aggregation+ have the same +name+ attribute, +active_record+ attribute,
|
229
|
+
# and +other_aggregation+ has an options hash assigned to it.
|
230
|
+
def ==(other)
|
231
|
+
super ||
|
232
|
+
other.is_a?(self.class) &&
|
233
|
+
name == other.name &&
|
234
|
+
!other.options.nil? &&
|
235
|
+
active_record == other.active_record
|
150
236
|
end
|
151
237
|
|
152
238
|
private
|
@@ -156,22 +242,6 @@ module ActiveFedora
|
|
156
242
|
class_name = class_name.singularize if collection?
|
157
243
|
class_name
|
158
244
|
end
|
159
|
-
|
160
|
-
def derive_foreign_key
|
161
|
-
if belongs_to?
|
162
|
-
"#{name}_id"
|
163
|
-
elsif has_and_belongs_to_many?
|
164
|
-
"#{name.to_s.singularize}_ids"
|
165
|
-
elsif options[:as]
|
166
|
-
"#{options[:as]}_id"
|
167
|
-
elsif inverse_of && inverse_of.collection?
|
168
|
-
# for a has_many that is the inverse of a has_and_belongs_to_many
|
169
|
-
"#{options[:inverse_of].to_s.singularize}_ids"
|
170
|
-
else
|
171
|
-
# for a has_many that is the inverse of a belongs_to
|
172
|
-
active_fedora.name.foreign_key
|
173
|
-
end
|
174
|
-
end
|
175
245
|
end
|
176
246
|
|
177
247
|
# Holds all the meta-data about an association as it was specified in the
|
@@ -179,9 +249,13 @@ module ActiveFedora
|
|
179
249
|
class AssociationReflection < MacroReflection #:nodoc:
|
180
250
|
attr_accessor :parent_reflection # Reflection
|
181
251
|
|
182
|
-
def initialize(macro, name, options, active_fedora)
|
252
|
+
def initialize(macro, name, scope, options, active_fedora)
|
183
253
|
super
|
184
|
-
@
|
254
|
+
@constructable = calculate_constructable(macro, options)
|
255
|
+
end
|
256
|
+
|
257
|
+
def constructable? # :nodoc:
|
258
|
+
@constructable
|
185
259
|
end
|
186
260
|
|
187
261
|
# Creates a new instance of the associated class, and immediately saves it
|
@@ -221,11 +295,17 @@ module ActiveFedora
|
|
221
295
|
|
222
296
|
# A chain of reflections from this one back to the owner. For more see the explanation in
|
223
297
|
# ThroughReflection.
|
224
|
-
def
|
298
|
+
def collect_join_chain
|
225
299
|
[self]
|
226
300
|
end
|
301
|
+
alias chain collect_join_chain # todo
|
227
302
|
|
228
|
-
|
303
|
+
# Returns whether or not this association reflection is for a collection
|
304
|
+
# association. Returns +true+ if the +macro+ is either +has_many+ or
|
305
|
+
# +has_and_belongs_to_many+, +false+ otherwise.
|
306
|
+
def collection?
|
307
|
+
[:has_many, :has_and_belongs_to_many, :directly_contains, :indirectly_contains].include?(macro)
|
308
|
+
end
|
229
309
|
|
230
310
|
def has_inverse?
|
231
311
|
inverse_name
|
@@ -276,8 +356,25 @@ module ActiveFedora
|
|
276
356
|
VALID_AUTOMATIC_INVERSE_MACROS = [:has_many, :has_and_belongs_to_many, :belongs_to].freeze
|
277
357
|
INVALID_AUTOMATIC_INVERSE_OPTIONS = [:conditions, :through, :polymorphic, :foreign_key].freeze
|
278
358
|
|
359
|
+
# Returns +true+ if +self+ is a +belongs_to+ reflection.
|
360
|
+
def belongs_to?
|
361
|
+
macro == :belongs_to
|
362
|
+
end
|
363
|
+
|
364
|
+
def has_many?
|
365
|
+
macro == :has_many
|
366
|
+
end
|
367
|
+
|
368
|
+
def has_and_belongs_to_many?
|
369
|
+
macro == :has_and_belongs_to_many
|
370
|
+
end
|
371
|
+
|
279
372
|
private
|
280
373
|
|
374
|
+
def calculate_constructable(_macro, _options)
|
375
|
+
true
|
376
|
+
end
|
377
|
+
|
281
378
|
def inverse_name
|
282
379
|
options.fetch(:inverse_of) do
|
283
380
|
if @automatic_inverse_of == false
|
@@ -336,9 +433,30 @@ module ActiveFedora
|
|
336
433
|
!INVALID_AUTOMATIC_INVERSE_OPTIONS.any? { |opt| reflection.options[opt] }
|
337
434
|
# && !reflection.scope
|
338
435
|
end
|
436
|
+
|
437
|
+
def derive_foreign_key
|
438
|
+
if belongs_to?
|
439
|
+
"#{name}_id"
|
440
|
+
elsif has_and_belongs_to_many?
|
441
|
+
"#{name.to_s.singularize}_ids"
|
442
|
+
elsif options[:as]
|
443
|
+
"#{options[:as]}_id"
|
444
|
+
elsif inverse_of && inverse_of.collection?
|
445
|
+
# for a has_many that is the inverse of a has_and_belongs_to_many
|
446
|
+
"#{options[:inverse_of].to_s.singularize}_ids"
|
447
|
+
else
|
448
|
+
# for a has_many that is the inverse of a belongs_to
|
449
|
+
active_fedora.name.foreign_key
|
450
|
+
end
|
451
|
+
end
|
339
452
|
end
|
340
453
|
|
341
454
|
class RDFPropertyReflection < AssociationReflection
|
455
|
+
def initialize(*args)
|
456
|
+
super
|
457
|
+
active_fedora.index_config[name] = build_index_config
|
458
|
+
end
|
459
|
+
|
342
460
|
def derive_foreign_key
|
343
461
|
name
|
344
462
|
end
|
@@ -348,6 +466,12 @@ module ActiveFedora
|
|
348
466
|
class_name = class_name.singularize if collection?
|
349
467
|
class_name
|
350
468
|
end
|
469
|
+
|
470
|
+
private
|
471
|
+
|
472
|
+
def build_index_config
|
473
|
+
ActiveFedora::Indexing::Map::IndexObject.new(predicate_for_solr) { |index| index.as :symbol }
|
474
|
+
end
|
351
475
|
end
|
352
476
|
end
|
353
477
|
end
|