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
@@ -4,10 +4,36 @@ module ActiveFedora::Associations::Builder
|
|
4
4
|
:belongs_to
|
5
5
|
end
|
6
6
|
|
7
|
-
def
|
7
|
+
def self.valid_options(options)
|
8
|
+
super + [:optional]
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.valid_dependent_options
|
12
|
+
[:destroy, :delete]
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.validate_options(options)
|
8
16
|
super
|
9
17
|
raise "You must specify a predicate for #{name}" unless options[:predicate]
|
10
18
|
raise ArgumentError, "Predicate must be a kind of RDF::URI" unless options[:predicate].is_a?(RDF::URI)
|
11
19
|
end
|
20
|
+
|
21
|
+
def self.define_validations(model, reflection)
|
22
|
+
if reflection.options.key?(:required)
|
23
|
+
reflection.options[:optional] = !reflection.options.delete(:required)
|
24
|
+
end
|
25
|
+
|
26
|
+
required = if reflection.options[:optional].nil?
|
27
|
+
model.belongs_to_required_by_default
|
28
|
+
else
|
29
|
+
!reflection.options[:optional]
|
30
|
+
end
|
31
|
+
|
32
|
+
super
|
33
|
+
|
34
|
+
if required
|
35
|
+
model.validates_presence_of reflection.name, message: :required
|
36
|
+
end
|
37
|
+
end
|
12
38
|
end
|
13
39
|
end
|
@@ -8,18 +8,49 @@ module ActiveFedora::Associations::Builder
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def self.define_callbacks(model, reflection)
|
11
|
+
super
|
11
12
|
name = reflection.name
|
12
13
|
options = reflection.options
|
13
|
-
super
|
14
14
|
CALLBACKS.each { |callback_name| define_callback(model, callback_name, name, options) }
|
15
15
|
end
|
16
16
|
|
17
|
+
def self.define_extensions(model, name)
|
18
|
+
if block_given?
|
19
|
+
extension_module_name = "#{model.name.demodulize}#{name.to_s.camelize}AssociationExtension"
|
20
|
+
extension = Module.new(&Proc.new)
|
21
|
+
model.parent.const_set(extension_module_name, extension)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
17
25
|
def self.define_callback(model, callback_name, name, options)
|
18
26
|
full_callback_name = "#{callback_name}_for_#{name}"
|
19
27
|
|
20
28
|
# TODO : why do i need method_defined? I think its because of the inheritance chain
|
21
29
|
model.class_attribute full_callback_name.to_sym unless model.method_defined?(full_callback_name)
|
22
|
-
|
30
|
+
|
31
|
+
callbacks = Array(options[callback_name.to_sym]).map do |callback|
|
32
|
+
case callback
|
33
|
+
when Symbol
|
34
|
+
->(_method, owner, record) { owner.send(callback, record) }
|
35
|
+
when Proc
|
36
|
+
->(_method, owner, record) { callback.call(owner, record) }
|
37
|
+
else
|
38
|
+
->(method, owner, record) { callback.send(method, owner, record) }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
model.send("#{full_callback_name}=", callbacks)
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.wrap_scope(scope, mod)
|
45
|
+
if scope
|
46
|
+
if scope.arity > 0
|
47
|
+
proc { |owner| instance_exec(owner, &scope).extending(mod) }
|
48
|
+
else
|
49
|
+
proc { instance_exec(&scope).extending(mod) }
|
50
|
+
end
|
51
|
+
else
|
52
|
+
proc { extending(mod) }
|
53
|
+
end
|
23
54
|
end
|
24
55
|
end
|
25
56
|
end
|
@@ -8,12 +8,12 @@ module ActiveFedora::Associations::Builder
|
|
8
8
|
super + [:autocreate, :block]
|
9
9
|
end
|
10
10
|
|
11
|
-
def
|
12
|
-
super
|
11
|
+
def self.create_reflection(model, name, scope, options, extension = nil)
|
13
12
|
options[:class_name] = 'ActiveFedora::File' if options[:class_name].blank?
|
13
|
+
super(model, name, scope, options, extension)
|
14
14
|
end
|
15
15
|
|
16
|
-
def validate_options
|
16
|
+
def self.validate_options(options)
|
17
17
|
super
|
18
18
|
return unless options[:class_name] && !options[:class_name].is_a?(String)
|
19
19
|
raise ArgumentError, ":class_name must be a string for contains '#{name}'"
|
@@ -8,13 +8,7 @@ module ActiveFedora::Associations::Builder
|
|
8
8
|
super + [:has_member_relation, :is_member_of_relation] - [:predicate]
|
9
9
|
end
|
10
10
|
|
11
|
-
def
|
12
|
-
reflection = super
|
13
|
-
configure_dependency
|
14
|
-
reflection
|
15
|
-
end
|
16
|
-
|
17
|
-
def validate_options
|
11
|
+
def self.validate_options(options)
|
18
12
|
super
|
19
13
|
if !options[:has_member_relation] && !options[:is_member_of_relation]
|
20
14
|
raise ArgumentError, "You must specify a :has_member_relation or :is_member_of_relation predicate for #{name}"
|
@@ -8,11 +8,18 @@ module ActiveFedora::Associations::Builder
|
|
8
8
|
super + [:has_member_relation, :is_member_of_relation, :type, :through] - [:predicate]
|
9
9
|
end
|
10
10
|
|
11
|
-
def
|
12
|
-
|
13
|
-
|
11
|
+
def self.create_reflection(model, name, scope, options, extension = nil)
|
12
|
+
if options[:through]
|
13
|
+
inherit_options_from_association(model, options, options[:through])
|
14
|
+
else
|
15
|
+
raise ArgumentError, "you must specify a :through option on #{name}. #{name} will use the container from that directly_contains association."
|
16
|
+
end
|
17
|
+
|
14
18
|
super
|
19
|
+
end
|
15
20
|
|
21
|
+
def self.validate_options(options)
|
22
|
+
super
|
16
23
|
if options[:class_name] == "ActiveFedora::File"
|
17
24
|
raise ArgumentError, "You cannot set :class_name of #{name} to ActiveFedora::File because directly_contains_one needs to assert and read RDF.type assertions, which is not supported by ActiveFedora::File. To make Files support RDF.type assertions, define a subclass of ActiveFedora::File and make it `include ActiveFedora::WithMetadata`. Otherwise, all subclasses of ActiveFedora::Base support RDF.type assertions."
|
18
25
|
elsif !options[:has_member_relation] && !options[:is_member_of_relation]
|
@@ -25,20 +32,16 @@ module ActiveFedora::Associations::Builder
|
|
25
32
|
raise ArgumentError, "You must specify a Type and it must be a kind of RDF::URI"
|
26
33
|
end
|
27
34
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
options[:class_name] = associated_through_reflection.options[:class_name] unless options[:class_name] && options[:class_name] != "ActiveFedora::File"
|
38
|
-
end
|
35
|
+
# Inherits :has_member_relation from the association corresponding to association_name
|
36
|
+
# @param [Symbol] association_name of the association to inherit from
|
37
|
+
def self.inherit_options_from_association(model, options, association_name)
|
38
|
+
associated_through_reflection = model._reflect_on_association(association_name)
|
39
|
+
raise ArgumentError, "You specified `:through => #{@reflection.options[:through]}` on the #{name} associaiton but #{model} does not actually have a #{@reflection.options[:through]}` association" if associated_through_reflection.nil? || !associated_through_reflection.name
|
40
|
+
raise ArgumentError, "You must specify a directly_contains association as the :through option on #{name}. You provided a #{associated_through_reflection.macro}" unless associated_through_reflection.macro == :directly_contains
|
41
|
+
options[:has_member_relation] = associated_through_reflection.options[:has_member_relation] unless options[:has_member_relation]
|
42
|
+
options[:class_name] = associated_through_reflection.options[:class_name] unless options[:class_name] && options[:class_name] != "ActiveFedora::File"
|
43
|
+
end
|
39
44
|
|
40
|
-
|
41
|
-
model._reflect_on_association(association_name)
|
42
|
-
end
|
45
|
+
private_class_method :inherit_options_from_association
|
43
46
|
end
|
44
47
|
end
|
@@ -9,7 +9,7 @@ module ActiveFedora::Associations::Builder
|
|
9
9
|
super + [:inverse_of, :solr_page_size]
|
10
10
|
end
|
11
11
|
|
12
|
-
def validate_options
|
12
|
+
def self.validate_options(options)
|
13
13
|
super
|
14
14
|
Deprecation.warn HasAndBelongsToMany, ":solr_page_size doesn't do anything anymore and will be removed in ActiveFedora 10" if options.key?(:solr_page_size)
|
15
15
|
raise "You must specify a predicate for #{name}" unless options[:predicate]
|
@@ -8,10 +8,8 @@ module ActiveFedora::Associations::Builder
|
|
8
8
|
super + [:as, :dependent, :inverse_of]
|
9
9
|
end
|
10
10
|
|
11
|
-
def
|
12
|
-
|
13
|
-
configure_dependency
|
14
|
-
reflection
|
11
|
+
def self.valid_dependent_options
|
12
|
+
[:destroy, :delete_all, :nullify, :restrict_with_error, :restrict_with_exception]
|
15
13
|
end
|
16
14
|
|
17
15
|
def self.define_readers(mixin, name)
|
@@ -29,44 +27,5 @@ module ActiveFedora::Associations::Builder
|
|
29
27
|
association(name).ids_writer(ids)
|
30
28
|
end
|
31
29
|
end
|
32
|
-
|
33
|
-
private
|
34
|
-
|
35
|
-
def configure_dependency
|
36
|
-
return unless options[:dependent]
|
37
|
-
unless [:destroy, :delete_all, :nullify, :restrict].include?(options[:dependent])
|
38
|
-
raise ArgumentError, "The :dependent option expects either :destroy, :delete_all, " \
|
39
|
-
":nullify or :restrict (#{options[:dependent].inspect})"
|
40
|
-
end
|
41
|
-
|
42
|
-
send("define_#{options[:dependent]}_dependency_method")
|
43
|
-
model.before_destroy dependency_method_name
|
44
|
-
end
|
45
|
-
|
46
|
-
def define_destroy_dependency_method
|
47
|
-
name = self.name
|
48
|
-
model.send(:define_method, dependency_method_name) do
|
49
|
-
send(name).delete_all
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
def define_delete_all_dependency_method
|
54
|
-
name = self.name
|
55
|
-
model.send(:define_method, dependency_method_name) do
|
56
|
-
send(name).delete_all
|
57
|
-
end
|
58
|
-
end
|
59
|
-
alias define_nullify_dependency_method define_delete_all_dependency_method
|
60
|
-
|
61
|
-
def define_restrict_dependency_method
|
62
|
-
name = self.name
|
63
|
-
model.send(:define_method, dependency_method_name) do
|
64
|
-
raise ActiveRecord::DeleteRestrictionError, name unless send(name).empty?
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
def dependency_method_name
|
69
|
-
"has_many_dependent_for_#{name}"
|
70
|
-
end
|
71
30
|
end
|
72
31
|
end
|
@@ -8,12 +8,6 @@ module ActiveFedora::Associations::Builder
|
|
8
8
|
super + [:has_member_relation, :is_member_of_relation, :inserted_content_relation, :foreign_key, :through] - [:predicate]
|
9
9
|
end
|
10
10
|
|
11
|
-
def build
|
12
|
-
reflection = super
|
13
|
-
configure_dependency
|
14
|
-
reflection
|
15
|
-
end
|
16
|
-
|
17
11
|
def self.define_readers(mixin, name)
|
18
12
|
super
|
19
13
|
|
@@ -22,7 +16,7 @@ module ActiveFedora::Associations::Builder
|
|
22
16
|
end
|
23
17
|
end
|
24
18
|
|
25
|
-
def validate_options
|
19
|
+
def self.validate_options(options)
|
26
20
|
super
|
27
21
|
if !options[:has_member_relation] && !options[:is_member_of_relation]
|
28
22
|
raise ArgumentError, "You must specify a predicate for #{name}"
|
@@ -8,19 +8,8 @@ module ActiveFedora::Associations::Builder
|
|
8
8
|
super
|
9
9
|
end
|
10
10
|
|
11
|
-
def
|
12
|
-
|
13
|
-
@name = :"#{name.to_s.singularize}_ids"
|
14
|
-
end
|
15
|
-
|
16
|
-
def build
|
17
|
-
super.tap do |reflection|
|
18
|
-
model.index_config[name] = build_index_config(reflection)
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
def build_index_config(reflection)
|
23
|
-
ActiveFedora::Indexing::Map::IndexObject.new(reflection.predicate_for_solr) { |index| index.as :symbol }
|
11
|
+
def self.better_name(name)
|
12
|
+
:"#{name.to_s.singularize}_ids"
|
24
13
|
end
|
25
14
|
end
|
26
15
|
end
|
@@ -1,16 +1,12 @@
|
|
1
1
|
module ActiveFedora::Associations::Builder
|
2
2
|
class SingularAssociation < Association #:nodoc:
|
3
3
|
def self.valid_options(options)
|
4
|
-
super + [:dependent, :inverse_of]
|
5
|
-
end
|
6
|
-
|
7
|
-
def self.constructable?
|
8
|
-
true
|
4
|
+
super + [:dependent, :inverse_of, :required]
|
9
5
|
end
|
10
6
|
|
11
7
|
def self.define_accessors(model, reflection)
|
12
8
|
super
|
13
|
-
define_constructors(model.generated_association_methods, reflection.name) if constructable?
|
9
|
+
define_constructors(model.generated_association_methods, reflection.name) if reflection.constructable?
|
14
10
|
end
|
15
11
|
|
16
12
|
def self.define_constructors(mixin, name)
|
@@ -100,7 +100,7 @@ module ActiveFedora
|
|
100
100
|
# Replace this collection with +other_array+
|
101
101
|
# This will perform a diff and delete/add only records that have changed.
|
102
102
|
def replace(other_array)
|
103
|
-
other_array.each { |val| raise_on_type_mismatch(val) }
|
103
|
+
other_array.each { |val| raise_on_type_mismatch!(val) }
|
104
104
|
|
105
105
|
load_target
|
106
106
|
other = other_array.size < 100 ? other_array : other_array.to_set
|
@@ -156,8 +156,7 @@ module ActiveFedora
|
|
156
156
|
result = true
|
157
157
|
|
158
158
|
records.flatten.each do |record|
|
159
|
-
raise_on_type_mismatch(record)
|
160
|
-
run_type_validator(record)
|
159
|
+
raise_on_type_mismatch!(record)
|
161
160
|
add_to_target(record) do |_r|
|
162
161
|
result &&= insert_record(record) unless owner.new_record?
|
163
162
|
end
|
@@ -356,7 +355,7 @@ module ActiveFedora
|
|
356
355
|
|
357
356
|
def delete_or_destroy(records, method)
|
358
357
|
records = records.flatten.select { |x| load_target.include?(x) }
|
359
|
-
records.each { |record| raise_on_type_mismatch(record) }
|
358
|
+
records.each { |record| raise_on_type_mismatch!(record) }
|
360
359
|
existing_records = records.select(&:persisted?)
|
361
360
|
|
362
361
|
records.each { |record| callback(:before_remove, record) }
|
@@ -374,20 +373,13 @@ module ActiveFedora
|
|
374
373
|
|
375
374
|
def callback(method, record)
|
376
375
|
callbacks_for(method).each do |callback|
|
377
|
-
|
378
|
-
when Symbol
|
379
|
-
@owner.send(callback, record)
|
380
|
-
when Proc
|
381
|
-
callback.call(@owner, record)
|
382
|
-
else
|
383
|
-
callback.send(method, @owner, record)
|
384
|
-
end
|
376
|
+
callback.call(method, owner, record)
|
385
377
|
end
|
386
378
|
end
|
387
379
|
|
388
380
|
def callbacks_for(callback_name)
|
389
|
-
full_callback_name = "#{callback_name}_for_#{
|
390
|
-
|
381
|
+
full_callback_name = "#{callback_name}_for_#{reflection.name}"
|
382
|
+
owner.class.send(full_callback_name)
|
391
383
|
end
|
392
384
|
|
393
385
|
def ensure_owner_is_not_new
|
@@ -33,8 +33,7 @@ module ActiveFedora
|
|
33
33
|
# Ensures that this association's +type+ is set on the record and adds the record to the association's DirectContainer
|
34
34
|
def replace(record, *)
|
35
35
|
if record
|
36
|
-
raise_on_type_mismatch(record)
|
37
|
-
run_type_validator(record)
|
36
|
+
raise_on_type_mismatch!(record)
|
38
37
|
remove_existing_target
|
39
38
|
add_type_to_record(record, options[:type])
|
40
39
|
add_to_container(record)
|
@@ -57,6 +57,29 @@ module ActiveFedora
|
|
57
57
|
end
|
58
58
|
end
|
59
59
|
|
60
|
+
def handle_dependency
|
61
|
+
case options[:dependent]
|
62
|
+
when :restrict_with_exception
|
63
|
+
raise ActiveFedora::DeleteRestrictionError, reflection.name unless empty?
|
64
|
+
|
65
|
+
when :restrict_with_error
|
66
|
+
unless empty?
|
67
|
+
record = owner.class.human_attribute_name(reflection.name).downcase
|
68
|
+
owner.errors.add(:base, message || :'restrict_dependent_destroy.has_many', record: record)
|
69
|
+
throw(:abort)
|
70
|
+
end
|
71
|
+
|
72
|
+
else
|
73
|
+
if options[:dependent] == :destroy
|
74
|
+
# No point in executing the counter update since we're going to destroy the parent anyway
|
75
|
+
load_target.each { |t| t.destroyed_by_association = reflection }
|
76
|
+
destroy_all
|
77
|
+
else
|
78
|
+
delete_all
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
60
83
|
protected
|
61
84
|
|
62
85
|
def find_polymorphic_inverse(record)
|
@@ -131,7 +131,7 @@ module ActiveFedora
|
|
131
131
|
|
132
132
|
def create_singleton_association(file_path)
|
133
133
|
undeclared_files << file_path.to_sym
|
134
|
-
association = Associations::BasicContainsAssociation.new(self, Reflection::AssociationReflection.new(:contains, file_path, { class_name: 'ActiveFedora::File' }, self.class))
|
134
|
+
association = Associations::BasicContainsAssociation.new(self, Reflection::AssociationReflection.new(:contains, file_path, nil, { class_name: 'ActiveFedora::File' }, self.class))
|
135
135
|
@association_cache[file_path.to_sym] = association
|
136
136
|
|
137
137
|
singleton_class.send :define_method, accessor_name(file_path) do
|
data/lib/active_fedora/core.rb
CHANGED
@@ -14,6 +14,7 @@ module ActiveFedora
|
|
14
14
|
# Accepts a logger conforming to the interface of Log4r which can be
|
15
15
|
# retrieved on both a class and instance level by calling +logger+.
|
16
16
|
mattr_accessor :logger, instance_writer: false
|
17
|
+
mattr_accessor :belongs_to_required_by_default, instance_accessor: false
|
17
18
|
end
|
18
19
|
|
19
20
|
# Constructor. You may supply a custom +:id+, or we call the Fedora Rest API for the
|
@@ -31,7 +32,7 @@ module ActiveFedora
|
|
31
32
|
assign_attributes(attributes) if attributes
|
32
33
|
|
33
34
|
yield self if block_given?
|
34
|
-
|
35
|
+
_run_initialize_callbacks
|
35
36
|
end
|
36
37
|
|
37
38
|
##
|