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