snusnu-dm-accepts_nested_attributes 0.0.6 → 0.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.
- data/Manifest.txt +13 -8
- data/Rakefile +2 -3
- data/TODO +5 -0
- data/lib/dm-accepts_nested_attributes.rb +7 -14
- data/lib/dm-accepts_nested_attributes/error_collecting.rb +35 -0
- data/lib/dm-accepts_nested_attributes/model.rb +132 -0
- data/lib/dm-accepts_nested_attributes/resource.rb +218 -29
- data/lib/dm-accepts_nested_attributes/save.rb +13 -0
- data/lib/dm-accepts_nested_attributes/transactional_save.rb +15 -0
- data/lib/dm-accepts_nested_attributes/version.rb +1 -1
- data/spec/fixtures/person.rb +8 -0
- data/spec/fixtures/profile.rb +9 -0
- data/spec/fixtures/project.rb +8 -0
- data/spec/fixtures/project_membership.rb +8 -0
- data/spec/fixtures/task.rb +9 -0
- data/spec/integration/belongs_to_spec.rb +10 -134
- data/spec/integration/has_1_spec.rb +9 -121
- data/spec/integration/has_n_spec.rb +10 -149
- data/spec/integration/has_n_through_spec.rb +10 -162
- data/spec/{shared → lib}/rspec_tmbundle_support.rb +1 -1
- data/spec/shared/belongs_to_spec.rb +127 -0
- data/spec/shared/has_1_spec.rb +103 -0
- data/spec/shared/has_n_spec.rb +114 -0
- data/spec/shared/has_n_through_spec.rb +139 -0
- data/spec/spec_helper.rb +12 -9
- data/spec/unit/accepts_nested_attributes_for_spec.rb +39 -118
- data/tasks/changelog.rb +18 -0
- data/tasks/spec.rb +0 -1
- metadata +19 -14
- data/lib/dm-accepts_nested_attributes/association_proxies.rb +0 -55
- data/lib/dm-accepts_nested_attributes/association_validation.rb +0 -49
- data/lib/dm-accepts_nested_attributes/nested_attributes.rb +0 -350
- data/spec/unit/resource_spec.rb +0 -174
data/Manifest.txt
CHANGED
@@ -7,25 +7,30 @@ Rakefile
|
|
7
7
|
TODO
|
8
8
|
CHANGELOG
|
9
9
|
lib/dm-accepts_nested_attributes.rb
|
10
|
-
lib/dm-accepts_nested_attributes/
|
11
|
-
lib/dm-accepts_nested_attributes/
|
12
|
-
lib/dm-accepts_nested_attributes/nested_attributes.rb
|
10
|
+
lib/dm-accepts_nested_attributes/error_collecting.rb
|
11
|
+
lib/dm-accepts_nested_attributes/model.rb
|
13
12
|
lib/dm-accepts_nested_attributes/resource.rb
|
13
|
+
lib/dm-accepts_nested_attributes/save.rb
|
14
|
+
lib/dm-accepts_nested_attributes/transactional_save.rb
|
14
15
|
lib/dm-accepts_nested_attributes/version.rb
|
16
|
+
spec/spec.opts
|
17
|
+
spec/spec_helper.rb
|
18
|
+
spec/lib/rspec_tmbundle_support.rb
|
15
19
|
spec/fixtures/person.rb
|
16
20
|
spec/fixtures/profile.rb
|
17
21
|
spec/fixtures/project.rb
|
18
22
|
spec/fixtures/project_membership.rb
|
19
23
|
spec/fixtures/task.rb
|
24
|
+
spec/shared/belongs_to_spec.rb
|
25
|
+
spec/shared/has_1_spec.rb
|
26
|
+
spec/shared/has_n_spec.rb
|
27
|
+
spec/shared/has_n_through_spec.rb
|
28
|
+
spec/unit/accepts_nested_attributes_for_spec.rb
|
20
29
|
spec/integration/belongs_to_spec.rb
|
21
30
|
spec/integration/has_1_spec.rb
|
22
31
|
spec/integration/has_n_spec.rb
|
23
32
|
spec/integration/has_n_through_spec.rb
|
24
|
-
|
25
|
-
spec/spec.opts
|
26
|
-
spec/spec_helper.rb
|
27
|
-
spec/unit/accepts_nested_attributes_for_spec.rb
|
28
|
-
spec/unit/resource_spec.rb
|
33
|
+
tasks/changelog.rb
|
29
34
|
tasks/gemspec.rb
|
30
35
|
tasks/hoe.rb
|
31
36
|
tasks/install.rb
|
data/Rakefile
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
require 'pathname'
|
2
|
-
require 'rubygems'
|
3
2
|
require 'rake'
|
4
3
|
require 'rake/rdoctask'
|
5
4
|
|
@@ -16,8 +15,8 @@ GEM_NAME = "dm-accepts_nested_attributes"
|
|
16
15
|
GEM_VERSION = DataMapper::NestedAttributes::VERSION
|
17
16
|
|
18
17
|
GEM_DEPENDENCIES = [
|
19
|
-
["dm-core",
|
20
|
-
[
|
18
|
+
["dm-core", '>=0.10.0'],
|
19
|
+
["dm-validations", '>=0.10.0']
|
21
20
|
]
|
22
21
|
|
23
22
|
GEM_CLEAN = %w[ log pkg coverage ]
|
data/TODO
CHANGED
@@ -1,3 +1,8 @@
|
|
1
1
|
TODO
|
2
2
|
====
|
3
3
|
|
4
|
+
* add specs for :reject_if => :foo option, but think about _where_ first!
|
5
|
+
* think about supporting :reject_unless in addition to :reject_if
|
6
|
+
* think about generalizing :reject_if to not only work for new? resources
|
7
|
+
* think about :allow_destroy accepting the same parameters like :reject_if
|
8
|
+
(Symbol, String, #call)
|
@@ -1,20 +1,13 @@
|
|
1
|
-
# Needed to import datamapper and other gems
|
2
|
-
require 'rubygems'
|
3
1
|
require 'pathname'
|
4
|
-
|
5
|
-
# Add all external dependencies for the plugin here
|
6
|
-
gem 'dm-core', '>=0.9.11'
|
7
|
-
gem 'dm-validations', '>=0.9.11'
|
8
|
-
|
9
2
|
require 'dm-core'
|
10
|
-
require 'dm-validations'
|
11
3
|
|
12
|
-
# Require plugin-files
|
13
4
|
dir = Pathname(__FILE__).dirname.expand_path / 'dm-accepts_nested_attributes'
|
5
|
+
|
6
|
+
require dir / 'model'
|
7
|
+
#require dir / 'save'
|
14
8
|
require dir / 'resource'
|
15
|
-
require dir / 'association_proxies'
|
16
|
-
require dir / 'nested_attributes'
|
17
9
|
|
18
|
-
#
|
19
|
-
DataMapper::Model.append_extensions
|
20
|
-
DataMapper::
|
10
|
+
# Activate the plugin
|
11
|
+
DataMapper::Model.append_extensions(DataMapper::NestedAttributes::Model)
|
12
|
+
#DataMapper::Model.append_inclusions(DataMapper::NestedAttributes::Save)
|
13
|
+
DataMapper::Model.append_inclusions(DataMapper::NestedAttributes::CommonResourceSupport)
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module DataMapper
|
2
|
+
module NestedAttributes
|
3
|
+
|
4
|
+
module ValidationErrorCollecting
|
5
|
+
|
6
|
+
# collect errors on parent associations
|
7
|
+
def before_save_parent_association(association, context)
|
8
|
+
if association.respond_to?(:each)
|
9
|
+
association.each do |r|
|
10
|
+
unless r.valid?(context)
|
11
|
+
r.errors.each { |e| self.errors.add(:general, e) }
|
12
|
+
end
|
13
|
+
end
|
14
|
+
else
|
15
|
+
unless association.valid?(context)
|
16
|
+
association.errors.each { |e| self.errors.add(:general, e) }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# collect errors on child associations
|
22
|
+
def before_save_child_association(association, context)
|
23
|
+
if association.respond_to?(:valid?)
|
24
|
+
unless association.valid?(context)
|
25
|
+
association.errors.each { |e| self.errors.add(:general, e) }
|
26
|
+
end
|
27
|
+
else
|
28
|
+
self.errors.add(:general, "child association is missing")
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
module DataMapper
|
2
|
+
module NestedAttributes
|
3
|
+
|
4
|
+
##
|
5
|
+
# raised by accepts_nested_attributes_for
|
6
|
+
# if the passed options don't make sense
|
7
|
+
class InvalidOptions < ArgumentError; end
|
8
|
+
|
9
|
+
module Model
|
10
|
+
|
11
|
+
##
|
12
|
+
# Allows any association to accept nested attributes.
|
13
|
+
#
|
14
|
+
# @param association_name [Symbol, String]
|
15
|
+
# The name of the association that should accept nested attributes
|
16
|
+
#
|
17
|
+
# @param options [Hash, nil]
|
18
|
+
# List of resources to initialize the Collection with
|
19
|
+
#
|
20
|
+
# @option :reject_if [Symbol, String, #call]
|
21
|
+
# An instance method name or an object that respond_to?(:call), which
|
22
|
+
# stops a new record from being created, if it evaluates to true.
|
23
|
+
#
|
24
|
+
# @option :allow_destroy [true, false]
|
25
|
+
# If true, allow destroying the association via the generated writer
|
26
|
+
# If false, prevent destroying the association via the generated writer
|
27
|
+
# defaults to false
|
28
|
+
#
|
29
|
+
# @return nil
|
30
|
+
def accepts_nested_attributes_for(association_name, options = {})
|
31
|
+
|
32
|
+
# ----------------------------------------------------------------------------------
|
33
|
+
# try to fail as early as possible
|
34
|
+
# ----------------------------------------------------------------------------------
|
35
|
+
|
36
|
+
unless relationship = relationships(repository_name)[association_name]
|
37
|
+
raise(ArgumentError, "No relationship #{name.inspect} for #{self.name} in #{repository_name}")
|
38
|
+
end
|
39
|
+
|
40
|
+
# raise InvalidOptions if the given options don't make sense
|
41
|
+
assert_valid_options_for_nested_attributes(options)
|
42
|
+
|
43
|
+
# by default, nested attributes can't be destroyed
|
44
|
+
options = { :allow_destroy => false }.update(options)
|
45
|
+
|
46
|
+
# ----------------------------------------------------------------------------------
|
47
|
+
# should be safe to go from here
|
48
|
+
# ----------------------------------------------------------------------------------
|
49
|
+
|
50
|
+
options_for_nested_attributes[relationship] = options
|
51
|
+
|
52
|
+
include ::DataMapper::NestedAttributes::Resource
|
53
|
+
|
54
|
+
add_save_behavior
|
55
|
+
|
56
|
+
# TODO i wonder if this is the best place here?
|
57
|
+
# the transactional save behavior is definitely not needed for all resources,
|
58
|
+
# but it's necessary for resources that accept nested attributes
|
59
|
+
# FIXME this leads to weird "no such table" errors when specs are run
|
60
|
+
add_transactional_save_behavior # TODO if repository.adapter.supports_transactions?
|
61
|
+
|
62
|
+
# TODO make this do something
|
63
|
+
# it's only here now to remind me that this is probably the best place to put it
|
64
|
+
add_error_collection_behavior if DataMapper.const_defined?('Validate')
|
65
|
+
|
66
|
+
type = relationship.max > 1 ? :collection : :resource
|
67
|
+
|
68
|
+
define_method "#{association_name}_attributes" do
|
69
|
+
instance_variable_get("@#{association_name}_attributes")
|
70
|
+
end
|
71
|
+
|
72
|
+
define_method "#{association_name}_attributes=" do |attributes|
|
73
|
+
attributes = sanitize_nested_attributes(attributes)
|
74
|
+
instance_variable_set("@#{association_name}_attributes", attributes)
|
75
|
+
send("assign_nested_attributes_for_related_#{type}", relationship, attributes)
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
|
80
|
+
##
|
81
|
+
# The options given to the accepts_nested_attributes method
|
82
|
+
# They are guaranteed to be valid if they made it this far.
|
83
|
+
#
|
84
|
+
# @return [Hash] The options given to the accepts_nested_attributes method
|
85
|
+
# @see accepts_nested_attributes
|
86
|
+
def options_for_nested_attributes
|
87
|
+
@options_for_nested_attributes ||= {}
|
88
|
+
end
|
89
|
+
|
90
|
+
private
|
91
|
+
|
92
|
+
def add_save_behavior
|
93
|
+
require Pathname(__FILE__).dirname.expand_path + 'save'
|
94
|
+
include ::DataMapper::NestedAttributes::Save
|
95
|
+
end
|
96
|
+
|
97
|
+
def add_transactional_save_behavior
|
98
|
+
require Pathname(__FILE__).dirname.expand_path + 'transactional_save'
|
99
|
+
include ::DataMapper::NestedAttributes::TransactionalSave
|
100
|
+
end
|
101
|
+
|
102
|
+
def add_error_collection_behavior
|
103
|
+
require Pathname(__FILE__).dirname.expand_path + 'error_collecting'
|
104
|
+
include ::DataMapper::NestedAttributes::ValidationErrorCollecting
|
105
|
+
end
|
106
|
+
|
107
|
+
|
108
|
+
def assert_valid_options_for_nested_attributes(options)
|
109
|
+
|
110
|
+
assert_kind_of 'options', options, Hash
|
111
|
+
|
112
|
+
valid_options = [ :allow_destroy, :reject_if ]
|
113
|
+
|
114
|
+
unless options.all? { |k,v| valid_options.include?(k) }
|
115
|
+
raise InvalidOptions, 'options must be one of :allow_destroy or :reject_if'
|
116
|
+
end
|
117
|
+
|
118
|
+
guard = options[:reject_if]
|
119
|
+
if guard.is_a?(Symbol) || guard.is_a?(String)
|
120
|
+
msg = ":reject_if => #{guard.inspect}, but there is no instance method #{guard.inspect} in #{self.name}"
|
121
|
+
raise InvalidOptions, msg unless instance_methods.include?(options[:reject_if].to_s)
|
122
|
+
else
|
123
|
+
msg = ":reject_if must be a Symbol|String or respond_to?(:call) "
|
124
|
+
raise InvalidOptions, msg unless guard.nil? || guard.respond_to?(:call)
|
125
|
+
end
|
126
|
+
|
127
|
+
end
|
128
|
+
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|
132
|
+
end
|
@@ -1,42 +1,231 @@
|
|
1
1
|
module DataMapper
|
2
|
-
module
|
3
|
-
|
4
|
-
# basic extract method refactorings to work around a bug in extlib
|
5
|
-
# see http://sick.snusnu.info/2009/04/29/extlibhook-breaks-if-hooked-method-is-redefined/
|
6
|
-
# maybe they are worth considering even when the bug in extlib (hopefully) gets fixed
|
2
|
+
module NestedAttributes
|
7
3
|
|
8
|
-
|
4
|
+
module Resource
|
9
5
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
6
|
+
##
|
7
|
+
# Can be used to remove ambiguities from the passed attributes.
|
8
|
+
# Consider a situation with a belongs_to association where both a valid value
|
9
|
+
# for the foreign_key attribute *and* nested_attributes for a new record are
|
10
|
+
# present (i.e. item_type_id and item_type_attributes are present).
|
11
|
+
# Also see http://is.gd/sz2d on the rails-core ml for a discussion on this.
|
12
|
+
# The basic idea is, that there should be a well defined behavior for what
|
13
|
+
# exactly happens when such a situation occurs. I'm currently in favor for
|
14
|
+
# using the foreign_key if it is present, but this probably needs more thinking.
|
15
|
+
# For now, this method basically is a no-op, but at least it provides a hook where
|
16
|
+
# everyone can perform it's own sanitization by overwriting this method.
|
17
|
+
#
|
18
|
+
# @return [Hash] The sanitized attributes
|
19
|
+
def sanitize_nested_attributes(attributes)
|
20
|
+
attributes # noop
|
17
21
|
end
|
18
22
|
|
19
|
-
|
23
|
+
private
|
24
|
+
|
25
|
+
# Attribute hash keys that should not be assigned as normal attributes.
|
26
|
+
# These hash keys are nested attributes implementation details.
|
27
|
+
UNASSIGNABLE_KEYS = [ :id, :_delete ]
|
28
|
+
|
29
|
+
|
30
|
+
##
|
31
|
+
# Assigns the given attributes to the resource association.
|
32
|
+
#
|
33
|
+
# If the given attributes include an <tt>:id</tt> that matches the existing
|
34
|
+
# record’s id, then the existing record will be modified. Otherwise a new
|
35
|
+
# record will be built.
|
36
|
+
#
|
37
|
+
# If the given attributes include a matching <tt>:id</tt> attribute _and_ a
|
38
|
+
# <tt>:_delete</tt> key set to a truthy value, then the existing record
|
39
|
+
# will be marked for destruction.
|
40
|
+
#
|
41
|
+
# @param relationship [DataMapper::Associations::Relationship]
|
42
|
+
# The relationship backing the association.
|
43
|
+
# Assignment will happen on the target end of the relationship
|
44
|
+
#
|
45
|
+
# @param attributes [Hash]
|
46
|
+
# The attributes to assign to the relationship's target end
|
47
|
+
# All attributes except @see UNASSIGNABLE_KEYS will be assigned
|
48
|
+
#
|
49
|
+
# @return nil
|
50
|
+
def assign_nested_attributes_for_related_resource(relationship, attributes)
|
51
|
+
if attributes[:id].blank?
|
52
|
+
return if reject_new_record?(relationship, attributes)
|
53
|
+
new_record = relationship.target_model.new(attributes.except(*UNASSIGNABLE_KEYS))
|
54
|
+
relationship.set(self, new_record)
|
55
|
+
else
|
56
|
+
existing_record = relationship.get(self)
|
57
|
+
if existing_record && existing_record.id.to_s == attributes[:id].to_s
|
58
|
+
assign_to_or_mark_for_destruction(relationship, existing_record, attributes)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
##
|
64
|
+
# Assigns the given attributes to the collection association.
|
65
|
+
#
|
66
|
+
# Hashes with an <tt>:id</tt> value matching an existing associated record
|
67
|
+
# will update that record. Hashes without an <tt>:id</tt> value will build
|
68
|
+
# a new record for the association. Hashes with a matching <tt>:id</tt>
|
69
|
+
# value and a <tt>:_delete</tt> key set to a truthy value will mark the
|
70
|
+
# matched record for destruction.
|
71
|
+
#
|
72
|
+
# For example:
|
73
|
+
#
|
74
|
+
# assign_nested_attributes_for_collection_association(:people, {
|
75
|
+
# '1' => { :id => '1', :name => 'Peter' },
|
76
|
+
# '2' => { :name => 'John' },
|
77
|
+
# '3' => { :id => '2', :_delete => true }
|
78
|
+
# })
|
79
|
+
#
|
80
|
+
# Will update the name of the Person with ID 1, build a new associated
|
81
|
+
# person with the name `John', and mark the associatied Person with ID 2
|
82
|
+
# for destruction.
|
83
|
+
#
|
84
|
+
# Also accepts an Array of attribute hashes:
|
85
|
+
#
|
86
|
+
# assign_nested_attributes_for_collection_association(:people, [
|
87
|
+
# { :id => '1', :name => 'Peter' },
|
88
|
+
# { :name => 'John' },
|
89
|
+
# { :id => '2', :_delete => true }
|
90
|
+
# ])
|
91
|
+
#
|
92
|
+
# @param relationship [DataMapper::Associations::Relationship]
|
93
|
+
# The relationship backing the association.
|
94
|
+
# Assignment will happen on the target end of the relationship
|
95
|
+
#
|
96
|
+
# @param attributes [Hash]
|
97
|
+
# The attributes to assign to the relationship's target end
|
98
|
+
# All attributes except @see UNASSIGNABLE_KEYS will be assigned
|
99
|
+
#
|
100
|
+
# @return nil
|
101
|
+
def assign_nested_attributes_for_related_collection(relationship, attributes_collection)
|
20
102
|
|
21
|
-
|
22
|
-
|
103
|
+
normalize_attributes_collection(attributes_collection).each do |attributes|
|
104
|
+
|
105
|
+
if attributes[:id].blank?
|
106
|
+
next if reject_new_record?(relationship, attributes)
|
107
|
+
relationship.get(self).new(attributes.except(*UNASSIGNABLE_KEYS))
|
108
|
+
else
|
109
|
+
collection = relationship.get(self)
|
110
|
+
if existing_record = collection.detect { |record| record.id.to_s == attributes[:id].to_s }
|
111
|
+
assign_to_or_mark_for_destruction(relationship, existing_record, attributes)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
23
116
|
|
24
|
-
|
25
|
-
|
117
|
+
end
|
26
118
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
119
|
+
##
|
120
|
+
# Updates a record with the +attributes+ or marks it for destruction if
|
121
|
+
# +allow_destroy+ is +true+ and has_delete_flag? returns +true+.
|
122
|
+
#
|
123
|
+
# @param relationship [DataMapper::Associations::Relationship]
|
124
|
+
# The relationship backing the association.
|
125
|
+
# Assignment will happen on the target end of the relationship
|
126
|
+
#
|
127
|
+
# @param attributes [Hash]
|
128
|
+
# The attributes to assign to the relationship's target end
|
129
|
+
# All attributes except @see UNASSIGNABLE_KEYS will be assigned
|
130
|
+
#
|
131
|
+
# @return nil
|
132
|
+
def assign_to_or_mark_for_destruction(relationship, resource, attributes)
|
133
|
+
allow_destroy = self.class.options_for_nested_attributes[relationship][:allow_destroy]
|
134
|
+
if has_delete_flag?(attributes) && allow_destroy
|
135
|
+
resource.mark_for_destruction
|
136
|
+
else
|
137
|
+
resource.update(attributes.except(*UNASSIGNABLE_KEYS))
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
##
|
142
|
+
# Determines if a hash contains a truthy _delete key.
|
143
|
+
#
|
144
|
+
# @param hash [Hash] The hash to test
|
145
|
+
#
|
146
|
+
# @return [Boolean]
|
147
|
+
# true, if hash containts a truthy _delete key
|
148
|
+
# false, otherwise
|
149
|
+
def has_delete_flag?(hash)
|
150
|
+
# TODO find out if this activerecord code needs to be ported
|
151
|
+
# ConnectionAdapters::Column.value_to_boolean hash['_delete']
|
152
|
+
hash[:_delete]
|
153
|
+
end
|
31
154
|
|
32
|
-
|
33
|
-
|
155
|
+
##
|
156
|
+
# Determines if a new record should be build by checking for
|
157
|
+
# has_delete_flag? or if a <tt>:reject_if</tt> proc exists for this
|
158
|
+
# association and evaluates to +true+.
|
159
|
+
#
|
160
|
+
# @param relationship [DataMapper::Associations::Relationship]
|
161
|
+
# The relationship backing the association.
|
162
|
+
# Assignment will happen on the target end of the relationship
|
163
|
+
#
|
164
|
+
# @param attributes [Hash]
|
165
|
+
# The attributes to assign to the relationship's target end
|
166
|
+
# All attributes except @see UNASSIGNABLE_KEYS will be assigned
|
167
|
+
#
|
168
|
+
# @return [Boolean]
|
169
|
+
# true, if the given attributes won't be rejected
|
170
|
+
# false, otherwise
|
171
|
+
def reject_new_record?(relationship, attributes)
|
172
|
+
guard = self.class.options_for_nested_attributes[relationship][:reject_if]
|
173
|
+
return false if guard.nil? # if relationship guard is nil, nothing will be rejected
|
174
|
+
has_delete_flag?(attributes) || evaluate_reject_new_record_guard(guard, attributes)
|
175
|
+
end
|
176
|
+
|
177
|
+
def evaluate_reject_new_record_guard(guard, attributes)
|
178
|
+
if guard.is_a?(Symbol) || guard.is_a?(String)
|
179
|
+
send(guard)
|
180
|
+
elsif guard.respond_to?(:call)
|
181
|
+
guard.call(attributes)
|
182
|
+
else
|
183
|
+
# never reached when called from inside the plugin
|
184
|
+
raise ArgumentError, "guard must be a Symbol, a String, or respond_to?(:call)"
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
def normalize_attributes_collection(attributes_collection)
|
189
|
+
if attributes_collection.is_a?(Hash)
|
190
|
+
attributes_collection.sort_by { |index, _| index.to_i }.map { |_, attributes| attributes }
|
191
|
+
else
|
192
|
+
attributes_collection
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
34
196
|
end
|
35
197
|
|
36
|
-
|
37
|
-
|
38
|
-
|
198
|
+
module CommonResourceSupport
|
199
|
+
|
200
|
+
##
|
201
|
+
# remove mark for destruction if present
|
202
|
+
# before delegating reload behavior to super
|
203
|
+
#
|
204
|
+
# @return The same value that super returns
|
205
|
+
def reload
|
206
|
+
@marked_for_destruction = false
|
207
|
+
super
|
208
|
+
end
|
209
|
+
|
210
|
+
##
|
211
|
+
# Test if this resource is marked for destruction
|
212
|
+
#
|
213
|
+
# @return [Boolean]
|
214
|
+
# true, if this resource is marked for destruction
|
215
|
+
# false, otherwise
|
216
|
+
def marked_for_destruction?
|
217
|
+
@marked_for_destruction
|
218
|
+
end
|
219
|
+
|
220
|
+
##
|
221
|
+
# Mark this resource for destruction
|
222
|
+
#
|
223
|
+
# @return true
|
224
|
+
def mark_for_destruction
|
225
|
+
@marked_for_destruction = true
|
226
|
+
end
|
227
|
+
|
39
228
|
end
|
40
|
-
|
229
|
+
|
41
230
|
end
|
42
|
-
end
|
231
|
+
end
|