granite-form 0.5.0 → 0.6.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +26 -48
- data/.rubocop_todo.yml +304 -27
- data/CHANGELOG.md +14 -2
- data/granite-form.gemspec +2 -1
- data/lib/granite/form/active_record/associations.rb +4 -3
- data/lib/granite/form/config.rb +1 -1
- data/lib/granite/form/errors.rb +34 -32
- data/lib/granite/form/extensions.rb +2 -1
- data/lib/granite/form/model/associations/base.rb +6 -2
- data/lib/granite/form/model/associations/collection/embedded.rb +1 -1
- data/lib/granite/form/model/associations/collection/proxy.rb +3 -3
- data/lib/granite/form/model/associations/embeds_any.rb +1 -1
- data/lib/granite/form/model/associations/embeds_many.rb +15 -11
- data/lib/granite/form/model/associations/embeds_one.rb +9 -8
- data/lib/granite/form/model/associations/nested_attributes.rb +60 -32
- data/lib/granite/form/model/associations/persistence_adapters/active_record/referenced_proxy.rb +2 -1
- data/lib/granite/form/model/associations/persistence_adapters/active_record.rb +7 -6
- data/lib/granite/form/model/associations/persistence_adapters/base.rb +8 -4
- data/lib/granite/form/model/associations/references_any.rb +1 -1
- data/lib/granite/form/model/associations/references_many.rb +3 -2
- data/lib/granite/form/model/associations/references_one.rb +1 -1
- data/lib/granite/form/model/associations/reflections/base.rb +3 -2
- data/lib/granite/form/model/associations/reflections/embeds_any.rb +4 -4
- data/lib/granite/form/model/associations/reflections/embeds_many.rb +4 -1
- data/lib/granite/form/model/associations/reflections/embeds_one.rb +4 -1
- data/lib/granite/form/model/associations/reflections/references_any.rb +6 -6
- data/lib/granite/form/model/associations/reflections/references_many.rb +1 -1
- data/lib/granite/form/model/associations/reflections/references_one.rb +1 -1
- data/lib/granite/form/model/associations/validations.rb +6 -6
- data/lib/granite/form/model/associations.rb +6 -4
- data/lib/granite/form/model/attributes/attribute.rb +1 -0
- data/lib/granite/form/model/attributes/base.rb +9 -7
- data/lib/granite/form/model/attributes/reference_one.rb +1 -1
- data/lib/granite/form/model/attributes/reflections/base/build_type_definition.rb +2 -1
- data/lib/granite/form/model/attributes/reflections/represents/build_type_definition.rb +2 -2
- data/lib/granite/form/model/attributes/represents.rb +1 -1
- data/lib/granite/form/model/attributes.rb +31 -14
- data/lib/granite/form/model/conventions.rb +1 -1
- data/lib/granite/form/model/persistence.rb +1 -1
- data/lib/granite/form/model/primary.rb +1 -1
- data/lib/granite/form/model/representation.rb +4 -4
- data/lib/granite/form/model/scopes.rb +5 -5
- data/lib/granite/form/model/validations.rb +4 -3
- data/lib/granite/form/types/active_support/time_zone.rb +1 -1
- data/lib/granite/form/types/array.rb +1 -1
- data/lib/granite/form/types/big_decimal.rb +1 -1
- data/lib/granite/form/types/boolean.rb +1 -1
- data/lib/granite/form/types/date.rb +1 -1
- data/lib/granite/form/types/date_time.rb +1 -1
- data/lib/granite/form/types/dictionary.rb +1 -1
- data/lib/granite/form/types/float.rb +1 -1
- data/lib/granite/form/types/has_subtype.rb +1 -0
- data/lib/granite/form/types/hash_with_action_controller_parameters.rb +2 -2
- data/lib/granite/form/types/integer.rb +1 -1
- data/lib/granite/form/types/object.rb +2 -1
- data/lib/granite/form/types/string.rb +1 -1
- data/lib/granite/form/types/time.rb +1 -1
- data/lib/granite/form/types/uuid.rb +1 -1
- data/lib/granite/form/util.rb +1 -1
- data/lib/granite/form/version.rb +1 -1
- data/spec/granite/form/active_record/associations_spec.rb +35 -13
- data/spec/granite/form/config_spec.rb +8 -4
- data/spec/granite/form/model/associations/embeds_many_spec.rb +99 -51
- data/spec/granite/form/model/associations/embeds_one_spec.rb +48 -25
- data/spec/granite/form/model/associations/persistence_adapters/active_record_spec.rb +12 -7
- data/spec/granite/form/model/associations/references_many_spec.rb +51 -10
- data/spec/granite/form/model/associations/references_one_spec.rb +17 -6
- data/spec/granite/form/model/associations/reflections/embeds_many_spec.rb +51 -16
- data/spec/granite/form/model/associations/reflections/embeds_one_spec.rb +19 -9
- data/spec/granite/form/model/associations/reflections/references_many_spec.rb +67 -15
- data/spec/granite/form/model/associations/reflections/references_one_spec.rb +34 -11
- data/spec/granite/form/model/associations/validations_spec.rb +16 -5
- data/spec/granite/form/model/associations_spec.rb +28 -9
- data/spec/granite/form/model/attributes/attribute_spec.rb +33 -11
- data/spec/granite/form/model/attributes/base_spec.rb +9 -3
- data/spec/granite/form/model/attributes/reflections/attribute_spec.rb +1 -0
- data/spec/granite/form/model/attributes/reflections/base_spec.rb +1 -0
- data/spec/granite/form/model/attributes/reflections/represents/build_type_definition_spec.rb +3 -1
- data/spec/granite/form/model/attributes/reflections/represents_spec.rb +2 -2
- data/spec/granite/form/model/attributes/represents_spec.rb +2 -2
- data/spec/granite/form/model/attributes_spec.rb +97 -36
- data/spec/granite/form/model/dirty_spec.rb +3 -0
- data/spec/granite/form/model/persistence_spec.rb +15 -5
- data/spec/granite/form/model/primary_spec.rb +17 -2
- data/spec/granite/form/model/representation_spec.rb +13 -3
- data/spec/granite/form/model/scopes_spec.rb +8 -3
- data/spec/granite/form/model/validations/associated_spec.rb +20 -6
- data/spec/granite/form/model/validations/nested_spec.rb +30 -14
- data/spec/granite/form/model/validations_spec.rb +1 -1
- data/spec/granite/form/model_spec.rb +1 -0
- data/spec/granite/form/types/collection_spec.rb +2 -1
- data/spec/granite/form/types/date_spec.rb +1 -1
- data/spec/granite/form/types/date_time_spec.rb +0 -2
- data/spec/granite/form/types/dictionary_spec.rb +1 -0
- data/spec/granite/form/types/has_subtype_spec.rb +6 -1
- data/spec/granite/form/types/hash_with_action_controller_parameters_spec.rb +1 -1
- data/spec/granite/form/types/object_spec.rb +2 -0
- data/spec/granite/form/types/time_spec.rb +0 -2
- data/spec/granite/form/util_spec.rb +6 -3
- data/spec/support/active_record.rb +13 -0
- data/spec/support/shared/nested_attribute_examples.rb +110 -54
- metadata +19 -6
- data/.github/CODEOWNERS +0 -1
@@ -4,6 +4,7 @@ module Granite
|
|
4
4
|
module Associations
|
5
5
|
class Base
|
6
6
|
attr_accessor :owner, :reflection
|
7
|
+
|
7
8
|
delegate :macro, :collection?, to: :reflection
|
8
9
|
|
9
10
|
def initialize(owner, reflection)
|
@@ -33,6 +34,7 @@ module Granite
|
|
33
34
|
|
34
35
|
def target
|
35
36
|
return @target if loaded?
|
37
|
+
|
36
38
|
self.target = load_target
|
37
39
|
end
|
38
40
|
|
@@ -66,10 +68,12 @@ module Granite
|
|
66
68
|
end
|
67
69
|
|
68
70
|
def inspect
|
69
|
-
|
71
|
+
macro = reflection.macro.to_s.camelize
|
72
|
+
target_for_inspect = target.inspect.truncate(50, omission: collection? ? '...]' : '...')
|
73
|
+
"#<#{macro} #{target_for_inspect}>"
|
70
74
|
end
|
71
75
|
|
72
|
-
|
76
|
+
private
|
73
77
|
|
74
78
|
def read_source
|
75
79
|
reflection.read_source owner
|
@@ -8,8 +8,8 @@ module Granite
|
|
8
8
|
|
9
9
|
delegate :target, :loaded?, :reload, :clear, :concat, to: :@association
|
10
10
|
delegate :each, :size, :length, :first, :last, :empty?, :many?, :==, :dup, to: :target
|
11
|
-
|
12
|
-
|
11
|
+
alias << concat
|
12
|
+
alias push concat
|
13
13
|
|
14
14
|
def initialize(association)
|
15
15
|
@association = association
|
@@ -19,7 +19,7 @@ module Granite
|
|
19
19
|
dup
|
20
20
|
end
|
21
21
|
|
22
|
-
|
22
|
+
alias to_a to_ary
|
23
23
|
|
24
24
|
def inspect
|
25
25
|
entries = target.take(10).map!(&:inspect)
|
@@ -23,14 +23,14 @@ module Granite
|
|
23
23
|
default = Array.wrap(reflection.default(owner))
|
24
24
|
if default.present?
|
25
25
|
collection = if default.all? { |object| object.is_a?(reflection.klass) }
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
26
|
+
default
|
27
|
+
else
|
28
|
+
default.map do |attributes|
|
29
|
+
reflection.klass.with_sanitize(false) do
|
30
|
+
build_object(attributes)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
34
|
collection.map { |object| object.send(:clear_changes_information) } if reflection.klass.dirty?
|
35
35
|
collection
|
36
36
|
end
|
@@ -64,13 +64,13 @@ module Granite
|
|
64
64
|
end
|
65
65
|
end
|
66
66
|
|
67
|
-
|
67
|
+
alias writer replace
|
68
68
|
|
69
69
|
def concat(*objects)
|
70
70
|
append objects.flatten
|
71
71
|
end
|
72
72
|
|
73
|
-
|
73
|
+
private
|
74
74
|
|
75
75
|
def read_source
|
76
76
|
super || []
|
@@ -78,7 +78,11 @@ module Granite
|
|
78
78
|
|
79
79
|
def append(objects)
|
80
80
|
objects.each do |object|
|
81
|
-
|
81
|
+
unless object.is_a?(reflection.klass)
|
82
|
+
raise AssociationTypeMismatch.new(reflection.klass,
|
83
|
+
object.class)
|
84
|
+
end
|
85
|
+
|
82
86
|
push_object object
|
83
87
|
end
|
84
88
|
target
|
@@ -30,12 +30,12 @@ module Granite
|
|
30
30
|
return unless default
|
31
31
|
|
32
32
|
object = if default.is_a?(reflection.klass)
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
33
|
+
default
|
34
|
+
else
|
35
|
+
reflection.klass.with_sanitize(false) do
|
36
|
+
build_object(default)
|
37
|
+
end
|
38
|
+
end
|
39
39
|
object.send(:clear_changes_information) if reflection.klass.dirty?
|
40
40
|
object
|
41
41
|
end
|
@@ -58,6 +58,7 @@ module Granite
|
|
58
58
|
def replace(object)
|
59
59
|
if object
|
60
60
|
raise AssociationTypeMismatch.new(reflection.klass, object.class) unless object.is_a?(reflection.klass)
|
61
|
+
|
61
62
|
transaction do
|
62
63
|
clear
|
63
64
|
self.target = object
|
@@ -69,9 +70,9 @@ module Granite
|
|
69
70
|
target
|
70
71
|
end
|
71
72
|
|
72
|
-
|
73
|
+
alias writer replace
|
73
74
|
|
74
|
-
|
75
|
+
private
|
75
76
|
|
76
77
|
def setup_performers!(object)
|
77
78
|
embed_object(object)
|
@@ -22,7 +22,7 @@ module Granite
|
|
22
22
|
attrs = attrs.stringify_keys
|
23
23
|
|
24
24
|
nested_attrs = self.class.nested_attributes_options.keys
|
25
|
-
|
25
|
+
.each_with_object({}) do |association_name, result|
|
26
26
|
name = "#{association_name}_attributes"
|
27
27
|
result[name] = attrs.delete(name) if attrs.key?(name)
|
28
28
|
end
|
@@ -33,14 +33,18 @@ module Granite
|
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
36
|
-
|
36
|
+
alias attributes= assign_attributes
|
37
37
|
end
|
38
38
|
|
39
|
-
class NestedAttributesMethods
|
40
|
-
REJECT_ALL_BLANK_PROC = proc { |attributes|
|
39
|
+
class NestedAttributesMethods # rubocop:disable Metrics/ClassLength
|
40
|
+
REJECT_ALL_BLANK_PROC = proc { |attributes|
|
41
|
+
attributes.all? do |key, value|
|
42
|
+
key == DESTROY_ATTRIBUTE || value.blank?
|
43
|
+
end
|
44
|
+
}
|
41
45
|
|
42
46
|
def self.accepts_nested_attributes_for(klass, *attr_names)
|
43
|
-
options = {allow_destroy: false, update_only: false}
|
47
|
+
options = { allow_destroy: false, update_only: false }
|
44
48
|
options.update(attr_names.extract_options!)
|
45
49
|
options.assert_valid_keys(:allow_destroy, :reject_if, :limit, :update_only)
|
46
50
|
options[:reject_if] = REJECT_ALL_BLANK_PROC if options[:reject_if] == :all_blank
|
@@ -49,10 +53,18 @@ module Granite
|
|
49
53
|
|
50
54
|
attr_names.each do |association_name|
|
51
55
|
reflection = klass.reflect_on_association(association_name)
|
52
|
-
|
53
|
-
|
56
|
+
unless reflection
|
57
|
+
raise ArgumentError,
|
58
|
+
"No association found for name `#{association_name}'. Has it been defined yet?"
|
59
|
+
end
|
60
|
+
|
61
|
+
klass.nested_attributes_options =
|
62
|
+
klass
|
63
|
+
.nested_attributes_options
|
64
|
+
.merge(association_name.to_sym => options)
|
54
65
|
|
55
|
-
should_validate_nested = klass.respond_to?(:validates_nested) &&
|
66
|
+
should_validate_nested = klass.respond_to?(:validates_nested) &&
|
67
|
+
!klass.validates_nested?(association_name)
|
56
68
|
klass.validates_nested(association_name) if should_validate_nested
|
57
69
|
|
58
70
|
type = (reflection.collection? ? :collection : :one_to_one)
|
@@ -74,10 +86,17 @@ module Granite
|
|
74
86
|
primary_attribute_name = primary_name_for(association.reflection.klass)
|
75
87
|
if existing_record
|
76
88
|
primary_attribute = existing_record.attribute(primary_attribute_name)
|
77
|
-
|
89
|
+
if primary_attribute
|
90
|
+
primary_attribute_value = primary_attribute.type_definition
|
91
|
+
.prepare(attributes[primary_attribute_name])
|
92
|
+
end
|
78
93
|
end
|
79
94
|
|
80
|
-
|
95
|
+
should_assign = !primary_attribute ||
|
96
|
+
options[:update_only] ||
|
97
|
+
existing_record.primary_attribute == primary_attribute_value
|
98
|
+
|
99
|
+
if existing_record && should_assign
|
81
100
|
assign_to(existing_record, attributes) unless call_reject_if(object, association_name, attributes)
|
82
101
|
association.clear if destroy_flag?(attributes) && options[:allow_destroy]
|
83
102
|
elsif attributes[primary_attribute_name].present?
|
@@ -93,11 +112,12 @@ module Granite
|
|
93
112
|
end
|
94
113
|
end
|
95
114
|
|
96
|
-
def self.assign_nested_attributes_for_collection_association(object, association_name, attributes_collection)
|
115
|
+
def self.assign_nested_attributes_for_collection_association(object, association_name, attributes_collection) # rubocop:disable Layout/LineLength,Metrics/MethodLength
|
97
116
|
options = object.nested_attributes_options[association_name]
|
98
117
|
|
99
118
|
unless attributes_collection.is_a?(Hash) || attributes_collection.is_a?(Array)
|
100
|
-
raise ArgumentError,
|
119
|
+
raise ArgumentError,
|
120
|
+
"Hash or Array expected, got #{attributes_collection.class.name} (#{attributes_collection.inspect})" # rubocop:disable Layout/LineLength
|
101
121
|
end
|
102
122
|
|
103
123
|
check_record_limit!(options[:limit], attributes_collection)
|
@@ -105,26 +125,32 @@ module Granite
|
|
105
125
|
association = object.association(association_name)
|
106
126
|
primary_attribute_name = primary_name_for(association.reflection.klass)
|
107
127
|
|
108
|
-
|
128
|
+
unless primary_attribute_name
|
129
|
+
raise Granite::Form::UndefinedPrimaryAttribute.new(object.class,
|
130
|
+
association_name)
|
131
|
+
end
|
109
132
|
|
110
133
|
if attributes_collection.is_a? Hash
|
111
134
|
keys = attributes_collection.keys
|
112
|
-
attributes_collection =
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
135
|
+
attributes_collection =
|
136
|
+
if keys.include?(primary_attribute_name) || keys.include?(primary_attribute_name.to_sym)
|
137
|
+
[attributes_collection]
|
138
|
+
else
|
139
|
+
attributes_collection.values
|
140
|
+
end
|
117
141
|
end
|
118
142
|
|
119
143
|
attributes_collection.each do |attributes|
|
120
144
|
attributes = attributes.with_indifferent_access
|
121
145
|
|
122
146
|
if attributes[primary_attribute_name].blank?
|
123
|
-
association.build(attributes.except(*unassignable_keys(object))) unless reject_new_object?(
|
147
|
+
association.build(attributes.except(*unassignable_keys(object))) unless reject_new_object?(
|
148
|
+
object, association_name, attributes, options
|
149
|
+
)
|
124
150
|
else
|
125
151
|
existing_record = association.target.detect do |record|
|
126
152
|
primary_attribute_value = record.attribute(primary_attribute_name)
|
127
|
-
|
153
|
+
.type_definition.prepare(attributes[primary_attribute_name])
|
128
154
|
record.primary_attribute == primary_attribute_value
|
129
155
|
end
|
130
156
|
if existing_record
|
@@ -137,7 +163,8 @@ module Granite
|
|
137
163
|
end
|
138
164
|
end
|
139
165
|
else
|
140
|
-
raise Granite::Form::ObjectNotFound.new(object, association_name,
|
166
|
+
raise Granite::Form::ObjectNotFound.new(object, association_name,
|
167
|
+
attributes[primary_attribute_name])
|
141
168
|
end
|
142
169
|
end
|
143
170
|
end
|
@@ -145,13 +172,13 @@ module Granite
|
|
145
172
|
|
146
173
|
def self.check_record_limit!(limit, attributes_collection)
|
147
174
|
limit = case limit
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
175
|
+
when Symbol
|
176
|
+
send(limit)
|
177
|
+
when Proc
|
178
|
+
limit.call
|
179
|
+
else
|
180
|
+
limit
|
181
|
+
end
|
155
182
|
|
156
183
|
return unless limit && attributes_collection.size > limit
|
157
184
|
|
@@ -197,15 +224,16 @@ module Granite
|
|
197
224
|
module NestedAttributesMethodsExtension
|
198
225
|
def self.ensure_extended!(klass)
|
199
226
|
return if klass.singleton_class.ancestors.include?(self)
|
227
|
+
|
200
228
|
klass.extend(self)
|
201
229
|
end
|
202
230
|
|
203
231
|
def nested_attributes_methods_module
|
204
232
|
@nested_attributes_methods_module ||= begin
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
233
|
+
mod = const_set(:NestedAttributesMethods, Module.new)
|
234
|
+
include(mod)
|
235
|
+
mod
|
236
|
+
end
|
209
237
|
end
|
210
238
|
end
|
211
239
|
end
|
data/lib/granite/form/model/associations/persistence_adapters/active_record/referenced_proxy.rb
CHANGED
@@ -9,6 +9,7 @@ module Granite
|
|
9
9
|
METHODS_EXCLUDED_FROM_DELEGATION = %w[build create create!].map(&:to_sym).freeze
|
10
10
|
|
11
11
|
attr_reader :association
|
12
|
+
|
12
13
|
delegate :scope, to: :@association
|
13
14
|
|
14
15
|
def method_missing(method, *args, &block)
|
@@ -19,7 +20,7 @@ module Granite
|
|
19
20
|
delegate_to_scope?(method) || super
|
20
21
|
end
|
21
22
|
|
22
|
-
|
23
|
+
private
|
23
24
|
|
24
25
|
def delegate_to_scope?(method)
|
25
26
|
METHODS_EXCLUDED_FROM_DELEGATION.exclude?(method) && scope.respond_to?(method)
|
@@ -17,10 +17,11 @@ module Granite
|
|
17
17
|
text: String,
|
18
18
|
string: String,
|
19
19
|
binary: String,
|
20
|
-
boolean: Boolean
|
20
|
+
boolean: Boolean,
|
21
|
+
enum: String
|
21
22
|
}.freeze
|
22
23
|
|
23
|
-
|
24
|
+
alias data_type data_source
|
24
25
|
|
25
26
|
def build(attributes)
|
26
27
|
data_source.new(attributes)
|
@@ -31,10 +32,10 @@ module Granite
|
|
31
32
|
|
32
33
|
if scope_proc
|
33
34
|
scope = if scope_proc.arity.zero?
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
35
|
+
scope.instance_exec(&scope_proc)
|
36
|
+
else
|
37
|
+
scope.instance_exec(owner, &scope_proc)
|
38
|
+
end
|
38
39
|
end
|
39
40
|
|
40
41
|
scope.where(primary_key => source)
|
@@ -13,7 +13,8 @@ module Granite
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def build(_attributes)
|
16
|
-
raise NotImplementedError,
|
16
|
+
raise NotImplementedError,
|
17
|
+
'Should be implemented in inhereted adapter. Build new instance of data object by attributes'
|
17
18
|
end
|
18
19
|
|
19
20
|
def scope(_owner, _source)
|
@@ -29,11 +30,13 @@ module Granite
|
|
29
30
|
end
|
30
31
|
|
31
32
|
def identify(_object)
|
32
|
-
raise NotImplementedError,
|
33
|
+
raise NotImplementedError,
|
34
|
+
'Should be implemented in inhereted adapter. Field to be used as primary_key for object'
|
33
35
|
end
|
34
36
|
|
35
37
|
def data_type
|
36
|
-
raise NotImplementedError,
|
38
|
+
raise NotImplementedError,
|
39
|
+
'Should be implemented in inhereted adapter. Type of data object for type_check'
|
37
40
|
end
|
38
41
|
|
39
42
|
def primary_key_type
|
@@ -41,7 +44,8 @@ module Granite
|
|
41
44
|
end
|
42
45
|
|
43
46
|
def referenced_proxy
|
44
|
-
raise NotImplementedError,
|
47
|
+
raise NotImplementedError,
|
48
|
+
'Should be implemented in inhereted adapter. Object to manage proxying of methods to scope.'
|
45
49
|
end
|
46
50
|
end
|
47
51
|
end
|
@@ -42,7 +42,7 @@ module Granite
|
|
42
42
|
end
|
43
43
|
end
|
44
44
|
|
45
|
-
|
45
|
+
alias writer replace
|
46
46
|
|
47
47
|
def concat(*objects)
|
48
48
|
append objects.flatten
|
@@ -60,12 +60,13 @@ module Granite
|
|
60
60
|
target.map { |obj| reflection.persistence_adapter.identify(obj) }
|
61
61
|
end
|
62
62
|
|
63
|
-
|
63
|
+
private
|
64
64
|
|
65
65
|
def append(objects)
|
66
66
|
attribute.pollute do
|
67
67
|
objects.each do |object|
|
68
68
|
next if target.include?(object)
|
69
|
+
|
69
70
|
raise_type_mismatch(object) unless matches_type?(object)
|
70
71
|
|
71
72
|
target.push(object)
|
@@ -10,13 +10,14 @@ module Granite
|
|
10
10
|
attr_reader :name, :options
|
11
11
|
# AR compatibility
|
12
12
|
attr_accessor :parent_reflection
|
13
|
+
|
13
14
|
delegate :association_class, to: 'self.class'
|
14
15
|
|
15
16
|
def self.build(target, generated_methods, name, options = {}, &_block)
|
16
17
|
generate_methods name, generated_methods
|
17
18
|
if options.delete(:validate) &&
|
18
|
-
|
19
|
-
|
19
|
+
target.respond_to?(:validates_nested) &&
|
20
|
+
!target.validates_nested?(name)
|
20
21
|
target.validates_nested name
|
21
22
|
end
|
22
23
|
new(name, options)
|
@@ -5,7 +5,10 @@ module Granite
|
|
5
5
|
module Reflections
|
6
6
|
class EmbedsMany < EmbedsAny
|
7
7
|
def self.build(target, generated_methods, name, options = {}, &block)
|
8
|
-
|
8
|
+
if target < Granite::Form::Model::Attributes
|
9
|
+
target.add_attribute(Granite::Form::Model::Attributes::Reflections::Base, name,
|
10
|
+
type: Object)
|
11
|
+
end
|
9
12
|
options[:validate] = true unless options.key?(:validate)
|
10
13
|
super
|
11
14
|
end
|
@@ -7,7 +7,10 @@ module Granite
|
|
7
7
|
include Singular
|
8
8
|
|
9
9
|
def self.build(target, generated_methods, name, options = {}, &block)
|
10
|
-
|
10
|
+
if target < Granite::Form::Model::Attributes
|
11
|
+
target.add_attribute(Granite::Form::Model::Attributes::Reflections::Base, name,
|
12
|
+
type: Object)
|
13
|
+
end
|
11
14
|
options[:validate] = true unless options.key?(:validate)
|
12
15
|
super
|
13
16
|
end
|
@@ -25,17 +25,17 @@ module Granite
|
|
25
25
|
|
26
26
|
def klass
|
27
27
|
@klass ||= if options[:data_source].present?
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
28
|
+
options[:data_source].to_s.constantize
|
29
|
+
else
|
30
|
+
super
|
31
|
+
end
|
32
32
|
end
|
33
33
|
|
34
|
-
|
34
|
+
alias data_source klass
|
35
35
|
|
36
36
|
def persistence_adapter
|
37
37
|
@persistence_adapter ||= self.class.persistence_adapter(klass)
|
38
|
-
|
38
|
+
.new(data_source, options[:primary_key], @scope_proc)
|
39
39
|
end
|
40
40
|
|
41
41
|
def read_source(object)
|
@@ -9,7 +9,7 @@ module Granite
|
|
9
9
|
run_validations!
|
10
10
|
end
|
11
11
|
|
12
|
-
|
12
|
+
alias validate_ancestry valid_ancestry?
|
13
13
|
|
14
14
|
def invalid_ancestry?
|
15
15
|
!valid_ancestry?
|
@@ -19,16 +19,16 @@ module Granite
|
|
19
19
|
valid_ancestry? || raise_validation_error
|
20
20
|
end
|
21
21
|
|
22
|
-
|
22
|
+
private
|
23
23
|
|
24
24
|
def validate_nested!
|
25
25
|
association_names.each do |name|
|
26
26
|
association = association(name)
|
27
27
|
invalid_block = if association.reflection.klass.method_defined?(:invalid_ansestry?)
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
28
|
+
->(object) { object.invalid_ansestry? }
|
29
|
+
else
|
30
|
+
->(object) { object.invalid? }
|
31
|
+
end
|
32
32
|
|
33
33
|
Granite::Form::Model::Validations::NestedValidator
|
34
34
|
.validate_nested(self, name, association.target, &invalid_block)
|
@@ -58,6 +58,7 @@ module Granite
|
|
58
58
|
def alias_association(alias_name, association_name)
|
59
59
|
reflection = reflect_on_association(association_name)
|
60
60
|
raise ArgumentError, "Can't alias undefined association `#{attribute_name}` on #{self}" unless reflection
|
61
|
+
|
61
62
|
reflection.class.generate_methods alias_name, generated_associations_methods
|
62
63
|
self._association_aliases = _association_aliases.merge(alias_name.to_sym => reflection.name)
|
63
64
|
reflection
|
@@ -72,7 +73,7 @@ module Granite
|
|
72
73
|
_associations.keys
|
73
74
|
end
|
74
75
|
|
75
|
-
|
76
|
+
private
|
76
77
|
|
77
78
|
def attributes_for_inspect
|
78
79
|
(_associations.map do |name, reflection|
|
@@ -82,7 +83,7 @@ module Granite
|
|
82
83
|
|
83
84
|
def generated_associations_methods
|
84
85
|
@generated_associations_methods ||= const_set(:GeneratedAssociationsMethods, Module.new)
|
85
|
-
|
86
|
+
.tap { |proxy| include proxy }
|
86
87
|
end
|
87
88
|
end
|
88
89
|
|
@@ -92,15 +93,16 @@ module Granite
|
|
92
93
|
end
|
93
94
|
end
|
94
95
|
|
95
|
-
|
96
|
+
alias eql? ==
|
96
97
|
|
97
98
|
def association(name)
|
98
99
|
reflection = self.class.reflect_on_association(name)
|
99
100
|
return unless reflection
|
101
|
+
|
100
102
|
(@_associations ||= {})[reflection.name] ||= reflection.build_association(self)
|
101
103
|
end
|
102
104
|
|
103
|
-
|
105
|
+
private
|
104
106
|
|
105
107
|
def attributes_for_inspect
|
106
108
|
(association_names.map do |name|
|