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
@@ -34,9 +34,18 @@ describe Granite::Form::Model::Associations do
|
|
34
34
|
|
35
35
|
describe '#reflect_on_association' do
|
36
36
|
specify { expect(Nobody.reflect_on_association(:blabla)).to be_nil }
|
37
|
-
|
37
|
+
|
38
|
+
specify do
|
39
|
+
expect(Admin.reflect_on_association('projects'))
|
40
|
+
.to be_a Granite::Form::Model::Associations::Reflections::EmbedsMany
|
41
|
+
end
|
42
|
+
|
38
43
|
specify { expect(Admin.reflect_on_association('own_projects').name).to eq(:admin_projects) }
|
39
|
-
|
44
|
+
|
45
|
+
specify do
|
46
|
+
expect(Manager.reflect_on_association(:managed_project))
|
47
|
+
.to be_a Granite::Form::Model::Associations::Reflections::EmbedsOne
|
48
|
+
end
|
40
49
|
end
|
41
50
|
end
|
42
51
|
|
@@ -47,7 +56,8 @@ describe Granite::Form::Model::Associations do
|
|
47
56
|
include Granite::Form::Model::Associations
|
48
57
|
|
49
58
|
embeds_one :author, class_name: 'Borogoves'
|
50
|
-
end.reflect_on_association(:author).data_source
|
59
|
+
end.reflect_on_association(:author).data_source
|
60
|
+
end.to raise_error NameError
|
51
61
|
end
|
52
62
|
|
53
63
|
specify do
|
@@ -58,7 +68,8 @@ describe Granite::Form::Model::Associations do
|
|
58
68
|
embeds_many :projects, class_name: 'Borogoves' do
|
59
69
|
attribute :title
|
60
70
|
end
|
61
|
-
end.reflect_on_association(:projects).data_source
|
71
|
+
end.reflect_on_association(:projects).data_source
|
72
|
+
end.to raise_error NameError
|
62
73
|
end
|
63
74
|
end
|
64
75
|
|
@@ -108,7 +119,9 @@ describe Granite::Form::Model::Associations do
|
|
108
119
|
specify { expect(user.profile).to be_nil }
|
109
120
|
|
110
121
|
describe '.inspect' do
|
111
|
-
specify
|
122
|
+
specify do
|
123
|
+
expect(User.inspect).to eq('User(profile: EmbedsOne(Profile), projects: EmbedsMany(Project), login: Object)')
|
124
|
+
end
|
112
125
|
end
|
113
126
|
|
114
127
|
describe '.association_names' do
|
@@ -118,9 +131,10 @@ describe Granite::Form::Model::Associations do
|
|
118
131
|
describe '#inspect' do
|
119
132
|
let(:profile) { Profile.new first_name: 'Name' }
|
120
133
|
let(:project) { Project.new title: 'Project' }
|
134
|
+
|
121
135
|
specify do
|
122
136
|
expect(User.new(login: 'Login', profile: profile, projects: [project]).inspect)
|
123
|
-
.to eq('#<User profile: #<EmbedsOne #<Profile first_name: "Name", last_name: nil>>, projects: #<EmbedsMany [#<Project author: #<EmbedsOne nil>, title: "P...]>, login: "Login">')
|
137
|
+
.to eq('#<User profile: #<EmbedsOne #<Profile first_name: "Name", last_name: nil>>, projects: #<EmbedsMany [#<Project author: #<EmbedsOne nil>, title: "P...]>, login: "Login">') # rubocop:disable Layout/LineLength
|
124
138
|
end
|
125
139
|
end
|
126
140
|
|
@@ -137,7 +151,8 @@ describe Granite::Form::Model::Associations do
|
|
137
151
|
specify { expect(User.new(projects: [project])).not_to eql(User.new) }
|
138
152
|
|
139
153
|
context do
|
140
|
-
before { User.
|
154
|
+
before { User.include Granite::Form::Model::Primary }
|
155
|
+
|
141
156
|
let(:user) { User.new(projects: [project]) }
|
142
157
|
|
143
158
|
specify { expect(user).to eq(user.clone.tap { |b| b.projects(author: project) }) }
|
@@ -161,13 +176,17 @@ describe Granite::Form::Model::Associations do
|
|
161
176
|
end
|
162
177
|
|
163
178
|
describe '#instantiate' do
|
164
|
-
before
|
179
|
+
before do
|
180
|
+
User.include Granite::Form::Model::Persistence
|
181
|
+
project.build_author(name: 'Author')
|
182
|
+
end
|
183
|
+
|
165
184
|
let(:profile) { Profile.new first_name: 'Name' }
|
166
185
|
let(:project) { Project.new title: 'Project' }
|
167
186
|
let(:user) { User.new(profile: profile, projects: [project]) }
|
168
|
-
before { project.build_author(name: 'Author') }
|
169
187
|
|
170
188
|
specify { expect(User.instantiate(JSON.parse(user.to_json))).to eq(user) }
|
189
|
+
|
171
190
|
specify do
|
172
191
|
expect(User.instantiate(JSON.parse(user.to_json))
|
173
192
|
.tap { |u| u.projects.first.author.name = 'Other' }).not_to eq(user)
|
@@ -5,12 +5,16 @@ describe Granite::Form::Model::Attributes::Attribute do
|
|
5
5
|
|
6
6
|
def attribute(*args)
|
7
7
|
options = args.extract_options!
|
8
|
-
Dummy.add_attribute(Granite::Form::Model::Attributes::Reflections::Attribute, :field,
|
8
|
+
Dummy.add_attribute(Granite::Form::Model::Attributes::Reflections::Attribute, :field,
|
9
|
+
{ type: Object }.merge(options))
|
9
10
|
Dummy.new.attribute(:field)
|
10
11
|
end
|
11
12
|
|
12
13
|
describe '#read' do
|
13
|
-
let(:field)
|
14
|
+
let(:field) do
|
15
|
+
normalizer = ->(v) { v ? v.strip : v }
|
16
|
+
attribute(type: String, normalizer: normalizer, default: :world, enum: %w[hello 42 world])
|
17
|
+
end
|
14
18
|
|
15
19
|
specify { expect(field.tap { |r| r.write(nil) }.read).to eq('world') }
|
16
20
|
specify { expect(field.tap { |r| r.write(:world) }.read).to eq('world') }
|
@@ -36,7 +40,11 @@ describe Granite::Form::Model::Attributes::Attribute do
|
|
36
40
|
specify { expect(field.tap { |r| r.write('') }.read_before_type_cast).to eq('') }
|
37
41
|
|
38
42
|
context ':readonly' do
|
39
|
-
specify
|
43
|
+
specify do
|
44
|
+
attr = attribute(readonly: true, default: :world)
|
45
|
+
attr.write('string')
|
46
|
+
expect(attr.read_before_type_cast).to eq(:world)
|
47
|
+
end
|
40
48
|
end
|
41
49
|
end
|
42
50
|
|
@@ -58,11 +66,22 @@ describe Granite::Form::Model::Attributes::Attribute do
|
|
58
66
|
describe '#normalize' do
|
59
67
|
specify { expect(attribute.normalize(' hello ')).to eq(' hello ') }
|
60
68
|
specify { expect(attribute(normalizer: ->(v) { v.strip }).normalize(' hello ')).to eq('hello') }
|
61
|
-
|
62
|
-
specify
|
69
|
+
|
70
|
+
specify do
|
71
|
+
normalizers = [->(v) { v.strip }, ->(v) { v.first(4) }]
|
72
|
+
attr = attribute(normalizer: normalizers)
|
73
|
+
expect(attr.normalize(' hello ')).to eq('hell')
|
74
|
+
end
|
75
|
+
|
76
|
+
specify do
|
77
|
+
normalizers = [->(v) { v.first(4) }, ->(v) { v.strip }]
|
78
|
+
attr = attribute(normalizer: normalizers)
|
79
|
+
expect(attr.normalize(' hello ')).to eq('hel')
|
80
|
+
end
|
63
81
|
|
64
82
|
context do
|
65
83
|
before { allow_any_instance_of(Dummy).to receive_messages(value: 'value') }
|
84
|
+
|
66
85
|
let(:other) { 'other' }
|
67
86
|
|
68
87
|
specify { expect(attribute(normalizer: ->(_v) { value }).normalize(' hello ')).to eq('value') }
|
@@ -89,12 +108,14 @@ describe Granite::Form::Model::Attributes::Attribute do
|
|
89
108
|
specify { expect(attribute(normalizer: :strip).normalize(' hello ')).to eq('hello') }
|
90
109
|
specify { expect(attribute(normalizer: %i[strip trim]).normalize(' hello ')).to eq('he') }
|
91
110
|
specify { expect(attribute(normalizer: %i[trim strip]).normalize(' hello ')).to eq('h') }
|
92
|
-
specify { expect(attribute(normalizer: [:strip, {trim: {length: 4}}]).normalize(' hello ')).to eq('hell') }
|
93
|
-
specify { expect(attribute(normalizer: {strip: {}, trim: {length: 4}}).normalize(' hello ')).to eq('hell') }
|
111
|
+
specify { expect(attribute(normalizer: [:strip, { trim: { length: 4 } }]).normalize(' hello ')).to eq('hell') }
|
112
|
+
specify { expect(attribute(normalizer: { strip: {}, trim: { length: 4 } }).normalize(' hello ')).to eq('hell') }
|
113
|
+
|
94
114
|
specify do
|
95
|
-
expect(attribute(normalizer: [:strip, {trim: {length: 4}}, ->(v) { v.last(2) }])
|
115
|
+
expect(attribute(normalizer: [:strip, { trim: { length: 4 } }, ->(v) { v.last(2) }])
|
96
116
|
.normalize(' hello ')).to eq('ll')
|
97
117
|
end
|
118
|
+
|
98
119
|
specify { expect(attribute(normalizer: :reset).normalize('')).to eq(nil) }
|
99
120
|
specify { expect(attribute(normalizer: %i[strip reset]).normalize(' ')).to eq(nil) }
|
100
121
|
specify { expect(attribute(normalizer: :reset, default: '!!!').normalize(nil)).to eq('!!!') }
|
@@ -103,10 +124,11 @@ describe Granite::Form::Model::Attributes::Attribute do
|
|
103
124
|
context do
|
104
125
|
let(:length) { 3 }
|
105
126
|
|
106
|
-
specify { expect(attribute(normalizer: [:strip, {trim: {length: 4}}]).normalize(' hello ')).to eq('hel') }
|
107
|
-
specify { expect(attribute(normalizer: {strip: {}, trim: {length: 4}}).normalize(' hello ')).to eq('hel') }
|
127
|
+
specify { expect(attribute(normalizer: [:strip, { trim: { length: 4 } }]).normalize(' hello ')).to eq('hel') }
|
128
|
+
specify { expect(attribute(normalizer: { strip: {}, trim: { length: 4 } }).normalize(' hello ')).to eq('hel') }
|
129
|
+
|
108
130
|
specify do
|
109
|
-
expect(attribute(normalizer: [:strip, {trim: {length: 4}}, ->(v) { v.last(2) }])
|
131
|
+
expect(attribute(normalizer: [:strip, { trim: { length: 4 } }, ->(v) { v.last(2) }])
|
110
132
|
.normalize(' hello ')).to eq('el')
|
111
133
|
end
|
112
134
|
end
|
@@ -7,12 +7,16 @@ describe Granite::Form::Model::Attributes::Base do
|
|
7
7
|
|
8
8
|
def attribute(*args)
|
9
9
|
options = args.extract_options!
|
10
|
-
Dummy.add_attribute(Granite::Form::Model::Attributes::Reflections::Base, :field,
|
10
|
+
Dummy.add_attribute(Granite::Form::Model::Attributes::Reflections::Base, :field,
|
11
|
+
options.reverse_merge(type: Object))
|
11
12
|
model.attribute(:field)
|
12
13
|
end
|
13
14
|
|
14
15
|
describe '#read' do
|
15
|
-
let(:field)
|
16
|
+
let(:field) do
|
17
|
+
normalizer = ->(v) { v ? v.strip : v }
|
18
|
+
attribute(type: String, normalizer: normalizer, default: :world, enum: %w[hello 42 world])
|
19
|
+
end
|
16
20
|
let(:object) { Object.new }
|
17
21
|
|
18
22
|
specify { expect(field.tap { |r| r.write(nil) }.read).to be_nil }
|
@@ -99,6 +103,7 @@ describe Granite::Form::Model::Attributes::Base do
|
|
99
103
|
|
100
104
|
describe '#type_definition' do
|
101
105
|
subject { attr.type_definition }
|
106
|
+
|
102
107
|
let(:attr) { attribute(type: String) }
|
103
108
|
|
104
109
|
it { is_expected.to have_attributes(type: String, reflection: subject.reflection, owner: model) }
|
@@ -112,11 +117,12 @@ describe Granite::Form::Model::Attributes::Base do
|
|
112
117
|
'hello' => 'field: "hello"',
|
113
118
|
123 => 'field: 123',
|
114
119
|
Date.new(2023, 6, 20) => 'field: "2023-06-20"',
|
115
|
-
DateTime.new(2023, 6, 20, 12, 30) => 'field: "2023-06-20 12:30:00"',
|
120
|
+
DateTime.new(2023, 6, 20, 12, 30) => 'field: "2023-06-20 12:30:00"',
|
116
121
|
Time.new(2023, 6, 20, 12, 30) => 'field: "2023-06-20 12:30:00"'
|
117
122
|
}.each do |input, expected_output|
|
118
123
|
context "attribute type is #{input.class}" do
|
119
124
|
let(:type) { input.class }
|
125
|
+
|
120
126
|
specify { expect(field.tap { |r| r.write(input) }.inspect_attribute).to eq(expected_output) }
|
121
127
|
end
|
122
128
|
end
|
@@ -10,6 +10,7 @@ describe Granite::Form::Model::Attributes::Reflections::Attribute do
|
|
10
10
|
|
11
11
|
specify { expect(described_class.build(Class.new, Target, :field, String).type).to eq(String) }
|
12
12
|
specify { expect(described_class.build(Class.new, Target, :field) {}.defaultizer).to be_a(Proc) }
|
13
|
+
|
13
14
|
specify do
|
14
15
|
described_class.build(Class.new, Target, :field)
|
15
16
|
|
@@ -12,6 +12,7 @@ describe Granite::Form::Model::Attributes::Reflections::Base do
|
|
12
12
|
described_class.build(Class.new, Target, :field)
|
13
13
|
expect(Target).not_to be_method_defined(:field)
|
14
14
|
end
|
15
|
+
|
15
16
|
specify { expect(described_class.build(Class.new, Target, :field).name).to eq('field') }
|
16
17
|
end
|
17
18
|
|
data/spec/granite/form/model/attributes/reflections/represents/build_type_definition_spec.rb
CHANGED
@@ -4,7 +4,8 @@ require 'spec_helper'
|
|
4
4
|
|
5
5
|
describe Granite::Form::Model::Attributes::Reflections::Represents::BuildTypeDefinition do
|
6
6
|
def build_type_definition(name = :name, **options)
|
7
|
-
@reflection = Granite::Form::Model::Attributes::Reflections::Represents.new(name,
|
7
|
+
@reflection = Granite::Form::Model::Attributes::Reflections::Represents.new(name,
|
8
|
+
options.reverse_merge(of: :author))
|
8
9
|
described_class.new(owner, @reflection).call
|
9
10
|
end
|
10
11
|
|
@@ -102,6 +103,7 @@ describe Granite::Form::Model::Attributes::Reflections::Represents::BuildTypeDef
|
|
102
103
|
it { expect(build_type_definition(:status)).to have_type(Integer) }
|
103
104
|
it { expect(build_type_definition(:full_name)).to have_type(String) }
|
104
105
|
it { expect(build_type_definition(:unknown_attribute)).to have_type(Object) }
|
106
|
+
|
105
107
|
it do
|
106
108
|
attribute = build_type_definition(:related_ids)
|
107
109
|
expect(attribute).to be_a(Granite::Form::Types::Collection)
|
@@ -23,7 +23,7 @@ describe Granite::Form::Model::Attributes::Reflections::Represents do
|
|
23
23
|
end
|
24
24
|
|
25
25
|
let(:instance) { Target.new attributes }
|
26
|
-
let(:attributes) { {subject: Author.new} }
|
26
|
+
let(:attributes) { { subject: Author.new } }
|
27
27
|
let!(:reflection) { build_reflection }
|
28
28
|
|
29
29
|
it { expect(reflection.reference).to eq('author') }
|
@@ -53,7 +53,7 @@ describe Granite::Form::Model::Attributes::Reflections::Represents do
|
|
53
53
|
context 'when validate_reference is false' do
|
54
54
|
let(:reflection) { build_reflection(validate_reference: false) }
|
55
55
|
|
56
|
-
it { expect { instance.validate }.not_to
|
56
|
+
it { expect { instance.validate }.not_to(change { instance.errors.messages }) }
|
57
57
|
end
|
58
58
|
end
|
59
59
|
end
|
@@ -23,9 +23,9 @@ describe Granite::Form::Model::Attributes::Represents do
|
|
23
23
|
describe '#initialize' do
|
24
24
|
before { Author.attribute :name, String, default: 'Default Name' }
|
25
25
|
|
26
|
-
let(:attributes) { {foo: 'bar'} }
|
26
|
+
let(:attributes) { { foo: 'bar' } }
|
27
27
|
|
28
|
-
it { expect { Model.new(attributes) }.
|
28
|
+
it { expect { Model.new(attributes) }.not_to(change { attributes }) }
|
29
29
|
|
30
30
|
it { expect(attribute.read).to eq('Default Name') }
|
31
31
|
it { expect(attribute.read_before_type_cast).to eq('Default Name') }
|
@@ -37,28 +37,33 @@ describe Granite::Form::Model::Attributes do
|
|
37
37
|
describe '.inspect' do
|
38
38
|
specify { expect(stub_model.inspect).to match(/#<Class:0x\w+>\(no attributes\)/) }
|
39
39
|
specify { expect(stub_model(:user).inspect).to eq('User(no attributes)') }
|
40
|
+
|
40
41
|
specify do
|
41
42
|
expect(stub_model do
|
42
43
|
include Granite::Form::Model::Primary
|
43
44
|
primary :count, Integer
|
44
45
|
attribute :object, Object
|
45
|
-
end.inspect).to match(/#<Class:0x\w+>\(\*count: Integer, object: Object\)/)
|
46
|
+
end.inspect).to match(/#<Class:0x\w+>\(\*count: Integer, object: Object\)/)
|
47
|
+
end
|
48
|
+
|
46
49
|
specify do
|
47
50
|
expect(stub_model(:user) do
|
48
51
|
include Granite::Form::Model::Primary
|
49
52
|
primary :count, Integer
|
50
53
|
attribute :object, Object
|
51
|
-
end.inspect).to match('User(*count: Integer, object: Object)')
|
54
|
+
end.inspect).to match('User(*count: Integer, object: Object)')
|
55
|
+
end
|
52
56
|
end
|
53
57
|
|
54
58
|
describe '#==' do
|
59
|
+
subject { model.new name: 'hello', count: 42 }
|
60
|
+
|
55
61
|
let(:model) do
|
56
62
|
stub_model do
|
57
63
|
attribute :name, String
|
58
64
|
attribute :count, Float, default: 0
|
59
65
|
end
|
60
66
|
end
|
61
|
-
subject { model.new name: 'hello', count: 42 }
|
62
67
|
|
63
68
|
it { is_expected.not_to eq(nil) }
|
64
69
|
it { is_expected.not_to eq('hello') }
|
@@ -79,6 +84,7 @@ describe Granite::Form::Model::Attributes do
|
|
79
84
|
|
80
85
|
describe '#attribute' do
|
81
86
|
let(:instance) { model.new }
|
87
|
+
|
82
88
|
specify { expect(instance.attribute(:full_name).reflection.name).to eq('full_name') }
|
83
89
|
specify { expect(instance.attribute('full_name').reflection.name).to eq('full_name') }
|
84
90
|
specify { expect(instance.attribute(:name).reflection.name).to eq('full_name') }
|
@@ -108,10 +114,12 @@ describe Granite::Form::Model::Attributes do
|
|
108
114
|
|
109
115
|
describe '#attributes' do
|
110
116
|
specify { expect(stub_model.new.attributes).to eq({}) }
|
117
|
+
|
111
118
|
specify do
|
112
119
|
expect(model.new(name: 'Name').attributes)
|
113
120
|
.to match('id' => nil, 'full_name' => 'Name', 'author' => nil, 'projects' => nil)
|
114
121
|
end
|
122
|
+
|
115
123
|
specify do
|
116
124
|
expect(model.new(name: 'Name').attributes(false))
|
117
125
|
.to match('id' => nil, 'full_name' => 'Name')
|
@@ -119,13 +127,16 @@ describe Granite::Form::Model::Attributes do
|
|
119
127
|
end
|
120
128
|
|
121
129
|
describe '#assign_attributes' do
|
122
|
-
let(:attributes) { {id: 42, full_name: 'Name', missed: 'value'} }
|
123
130
|
subject { model.new }
|
124
131
|
|
132
|
+
let(:attributes) { { id: 42, full_name: 'Name', missed: 'value' } }
|
133
|
+
|
125
134
|
specify { expect { subject.assign_attributes(attributes) }.to change { subject.id }.to(42) }
|
126
135
|
specify { expect { subject.assign_attributes(attributes) }.to change { subject.full_name }.to('Name') }
|
127
136
|
|
128
137
|
context 'features stack and assign order' do
|
138
|
+
subject { model.new }
|
139
|
+
|
129
140
|
let(:model) do
|
130
141
|
stub_model do
|
131
142
|
attr_reader :logger
|
@@ -147,7 +158,6 @@ describe Granite::Form::Model::Attributes do
|
|
147
158
|
log(:plain2)
|
148
159
|
end
|
149
160
|
end
|
150
|
-
subject { model.new }
|
151
161
|
|
152
162
|
specify do
|
153
163
|
expect { subject.assign_attributes(plain1: 'value', plain2: 'value') }
|
@@ -176,22 +186,47 @@ describe Granite::Form::Model::Attributes do
|
|
176
186
|
log(:assoc_plain)
|
177
187
|
|
178
188
|
def assign_attributes(attrs)
|
179
|
-
super
|
189
|
+
super(attrs.merge(attrs.extract!('plain2')))
|
180
190
|
end
|
181
191
|
end
|
182
192
|
end
|
183
193
|
|
184
194
|
specify do
|
185
|
-
expect
|
195
|
+
expect do
|
196
|
+
subject.assign_attributes(assoc_plain: 'value', assoc_attributes: {}, plain1: 'value', plain2: 'value')
|
197
|
+
end
|
186
198
|
.to change { subject.logger }.to(%i[plain1 assoc_attributes assoc_plain plain2])
|
187
199
|
end
|
188
200
|
|
189
201
|
specify do
|
190
|
-
expect
|
202
|
+
expect do
|
203
|
+
subject.assign_attributes(plain1: 'value', plain2: 'value', assoc_plain: 'value', assoc_attributes: {})
|
204
|
+
end
|
191
205
|
.to change { subject.logger }.to(%i[plain1 assoc_attributes assoc_plain plain2])
|
192
206
|
end
|
193
207
|
end
|
194
208
|
end
|
209
|
+
|
210
|
+
context 'with mass_assignment_strict_mode' do
|
211
|
+
let(:model) do
|
212
|
+
stub_model do
|
213
|
+
self.mass_assignment_strict_mode = true
|
214
|
+
attribute :full_name, String
|
215
|
+
alias_attribute :name, :full_name
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
specify do
|
220
|
+
expect do
|
221
|
+
subject.assign_attributes(name: 'name', unexisting: 'value')
|
222
|
+
end.to raise_error(ActiveModel::UnknownAttributeError, /unknown attribute 'unexisting' for/)
|
223
|
+
end
|
224
|
+
|
225
|
+
specify do
|
226
|
+
subject.assign_attributes(name: 'name', full_name: 'full_name')
|
227
|
+
expect(subject.name).to eq('full_name')
|
228
|
+
end
|
229
|
+
end
|
195
230
|
end
|
196
231
|
|
197
232
|
describe '#sync_attributes' do
|
@@ -212,7 +247,7 @@ describe Granite::Form::Model::Attributes do
|
|
212
247
|
|
213
248
|
let(:author) { Author.new }
|
214
249
|
let(:model) { Model.new(attributes) }
|
215
|
-
let(:attributes) { {author: author, name: 'Author Name', full_name: nil, age: 25} }
|
250
|
+
let(:attributes) { { author: author, name: 'Author Name', full_name: nil, age: 25 } }
|
216
251
|
|
217
252
|
it { expect { model.sync_attributes }.to change(author, :name).to('Author Name') }
|
218
253
|
|
@@ -226,21 +261,27 @@ describe Granite::Form::Model::Attributes do
|
|
226
261
|
describe '#inspect' do
|
227
262
|
specify { expect(stub_model.new.inspect).to match(/#<#<Class:0x\w+> \(no attributes\)>/) }
|
228
263
|
specify { expect(stub_model(:user).new.inspect).to match(/#<User \(no attributes\)>/) }
|
264
|
+
|
229
265
|
specify do
|
230
266
|
expect(stub_model do
|
231
267
|
include Granite::Form::Model::Primary
|
232
268
|
primary :count, Integer
|
233
269
|
attribute :object, Object
|
234
|
-
end.new(object: 'String').inspect).to match(/#<#<Class:0x\w+> \*count: nil, object: "String">/)
|
270
|
+
end.new(object: 'String').inspect).to match(/#<#<Class:0x\w+> \*count: nil, object: "String">/)
|
271
|
+
end
|
272
|
+
|
235
273
|
specify do
|
236
274
|
expect(stub_model(:user) do
|
237
275
|
include Granite::Form::Model::Primary
|
238
276
|
primary :count, Integer
|
239
277
|
attribute :object, Object
|
240
|
-
end.new.inspect).to match(/#<User \*count: nil, object: nil>/)
|
278
|
+
end.new.inspect).to match(/#<User \*count: nil, object: nil>/)
|
279
|
+
end
|
241
280
|
end
|
242
281
|
|
243
282
|
context 'attributes integration' do
|
283
|
+
subject { model.new('world') }
|
284
|
+
|
244
285
|
let(:model) do
|
245
286
|
stub_class do
|
246
287
|
include Granite::Form::Util
|
@@ -257,9 +298,10 @@ describe Granite::Form::Model::Attributes do
|
|
257
298
|
attribute :enum_with_default, Integer, enum: [1, 2, 3], default: '2'
|
258
299
|
attribute :foo, Boolean, default: false
|
259
300
|
collection :array, Integer, enum: [1, 2, 3], default: [2], normalizer: ->(v) { v.uniq }
|
260
|
-
dictionary :dict, Integer, keys: %w[from to], enum: [1, 2, 3], default: {from: 1}, normalizer: proc { |v|
|
301
|
+
dictionary :dict, Integer, keys: %w[from to], enum: [1, 2, 3], default: { from: 1 }, normalizer: proc { |v|
|
261
302
|
next v if v[:from].nil? || v[:to].nil? || v[:from] <= v[:to]
|
262
|
-
|
303
|
+
|
304
|
+
{ from: v[:to], to: v[:from] }.with_indifferent_access
|
263
305
|
}
|
264
306
|
|
265
307
|
def initialize(name = nil)
|
@@ -269,21 +311,19 @@ describe Granite::Form::Model::Attributes do
|
|
269
311
|
end
|
270
312
|
end
|
271
313
|
|
272
|
-
|
273
|
-
|
274
|
-
its(:
|
275
|
-
its(:
|
276
|
-
its(:
|
277
|
-
its(:
|
278
|
-
its(:
|
279
|
-
its(:
|
280
|
-
its(:
|
281
|
-
its(:
|
282
|
-
its(:
|
283
|
-
its(:
|
284
|
-
its(:
|
285
|
-
its(:enum?) { should eq(false) }
|
286
|
-
its(:enum_with_default?) { should eq(true) }
|
314
|
+
its(:enum_values) { is_expected.to eq [1, 2, 3] }
|
315
|
+
its(:string_default) { is_expected.to eq 'world' }
|
316
|
+
its(:count_default) { is_expected.to eq '10' }
|
317
|
+
its(:name) { is_expected.to eq 'world' }
|
318
|
+
its(:hello) { is_expected.to eq(nil) }
|
319
|
+
its(:hello?) { is_expected.to eq(false) }
|
320
|
+
its(:count) { is_expected.to eq 10 }
|
321
|
+
its(:count_before_type_cast) { is_expected.to eq '10' }
|
322
|
+
its(:count_came_from_user?) { is_expected.to eq(false) }
|
323
|
+
its(:count?) { is_expected.to eq(true) }
|
324
|
+
its(:calc) { is_expected.to eq 5 }
|
325
|
+
its(:enum?) { is_expected.to eq(false) }
|
326
|
+
its(:enum_with_default?) { is_expected.to eq(true) }
|
287
327
|
specify { expect { subject.hello = 'worlds' }.to change { subject.hello }.from(nil).to('worlds') }
|
288
328
|
specify { expect { subject.count = 20 }.to change { subject.count }.from(10).to(20) }
|
289
329
|
specify { expect { subject.calc = 15 }.to change { subject.calc }.from(5).to(15) }
|
@@ -294,22 +334,27 @@ describe Granite::Form::Model::Attributes do
|
|
294
334
|
subject.enum = 3
|
295
335
|
expect(subject.enum).to eq(3)
|
296
336
|
end
|
337
|
+
|
297
338
|
specify do
|
298
339
|
subject.enum = '3'
|
299
340
|
expect(subject.enum).to eq(3)
|
300
341
|
end
|
342
|
+
|
301
343
|
specify do
|
302
344
|
subject.enum = 10
|
303
345
|
expect(subject.enum).to eq(nil)
|
304
346
|
end
|
347
|
+
|
305
348
|
specify do
|
306
349
|
subject.enum = 'hello'
|
307
350
|
expect(subject.enum).to eq(nil)
|
308
351
|
end
|
352
|
+
|
309
353
|
specify do
|
310
354
|
subject.enum_with_default = 3
|
311
355
|
expect(subject.enum_with_default).to eq(3)
|
312
356
|
end
|
357
|
+
|
313
358
|
specify do
|
314
359
|
subject.enum_with_default = 10
|
315
360
|
expect(subject.enum_with_default).to be_nil
|
@@ -349,21 +394,37 @@ describe Granite::Form::Model::Attributes do
|
|
349
394
|
|
350
395
|
specify do
|
351
396
|
expect(subject).to have_attributes(
|
352
|
-
dict: {from: 1},
|
353
|
-
dict_before_type_cast: {from: 1},
|
397
|
+
dict: { from: 1 },
|
398
|
+
dict_before_type_cast: { from: 1 },
|
354
399
|
dict?: true,
|
355
|
-
dict_default: {from: 1},
|
400
|
+
dict_default: { from: 1 },
|
356
401
|
dict_values: [1, 2, 3]
|
357
402
|
)
|
358
403
|
end
|
359
404
|
|
360
|
-
specify { with_assigned_value(nil).to have_attributes(dict: {'from' => 1}, dict_before_type_cast: {from: 1}) }
|
405
|
+
specify { with_assigned_value(nil).to have_attributes(dict: { 'from' => 1 }, dict_before_type_cast: { from: 1 }) }
|
361
406
|
specify { with_assigned_value([nil]).to have_attributes(dict: {}, dict_before_type_cast: [nil]) }
|
362
407
|
specify { with_assigned_value(1).to have_attributes(dict: {}, dict_before_type_cast: 1) }
|
363
|
-
|
364
|
-
specify
|
365
|
-
|
366
|
-
|
408
|
+
|
409
|
+
specify do
|
410
|
+
with_assigned_value(from: 1, to: 2)
|
411
|
+
.to have_attributes(dict: { 'from' => 1, 'to' => 2 }, dict_before_type_cast: { from: 1, to: 2 })
|
412
|
+
end
|
413
|
+
|
414
|
+
specify do
|
415
|
+
with_assigned_value(from: 2, to: 4)
|
416
|
+
.to have_attributes(dict: { 'from' => 2, 'to' => nil }, dict_before_type_cast: { from: 2, to: 4 })
|
417
|
+
end
|
418
|
+
|
419
|
+
specify do
|
420
|
+
with_assigned_value(from: '1', to: '2')
|
421
|
+
.to have_attributes(dict: { 'from' => 1, 'to' => 2 }, dict_before_type_cast: { from: '1', to: '2' })
|
422
|
+
end
|
423
|
+
|
424
|
+
specify do
|
425
|
+
with_assigned_value(from: 3, to: 1)
|
426
|
+
.to have_attributes(dict: { 'from' => 1, 'to' => 3 }, dict_before_type_cast: { from: 3, to: 1 })
|
427
|
+
end
|
367
428
|
end
|
368
429
|
|
369
430
|
context 'attribute caching' do
|
@@ -45,16 +45,19 @@ describe Granite::Form::Model::Dirty do
|
|
45
45
|
.tap { |m| m.update(author_id: author.id) }.changes)
|
46
46
|
.to eq('author_id' => [other_author.id, author.id])
|
47
47
|
end
|
48
|
+
|
48
49
|
specify do
|
49
50
|
expect(Model.instantiate(author_id: other_author.id)
|
50
51
|
.tap { |m| m.update(author: author) }.changes)
|
51
52
|
.to eq('author_id' => [other_author.id, author.id])
|
52
53
|
end
|
54
|
+
|
53
55
|
specify do
|
54
56
|
expect(Model.instantiate(author_ids: [other_author.id])
|
55
57
|
.tap { |m| m.update(author_ids: [author.id]) }.changes)
|
56
58
|
.to eq('author_ids' => [[other_author.id], [author.id]])
|
57
59
|
end
|
60
|
+
|
58
61
|
specify do
|
59
62
|
expect(Model.instantiate(author_ids: [other_author.id])
|
60
63
|
.tap { |m| m.update(authors: [author]) }.changes)
|
@@ -19,7 +19,7 @@ describe Granite::Form::Model::Persistence do
|
|
19
19
|
context do
|
20
20
|
subject(:instance) { model.instantiate(name: 'Hello', foo: 'Bar') }
|
21
21
|
|
22
|
-
specify { expect(subject.instance_variable_get(:@initial_attributes)).to eq({name: 'Hello'}.stringify_keys) }
|
22
|
+
specify { expect(subject.instance_variable_get(:@initial_attributes)).to eq({ name: 'Hello' }.stringify_keys) }
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
@@ -28,17 +28,27 @@ describe Granite::Form::Model::Persistence do
|
|
28
28
|
subject(:instances) { model.instantiate_collection(name: 'Hello', foo: 'Bar') }
|
29
29
|
|
30
30
|
specify { expect(subject).to be_a Array }
|
31
|
-
|
31
|
+
|
32
|
+
specify do
|
33
|
+
expect(subject.first.instance_variable_get(:@initial_attributes)).to eq({ name: 'Hello' }.stringify_keys)
|
34
|
+
end
|
32
35
|
end
|
33
36
|
|
34
37
|
context do
|
38
|
+
subject(:instances) { model.instantiate_collection([{ name: 'Hello', foo: 'Bar' }, { name: 'World' }]) }
|
39
|
+
|
35
40
|
before { model.send(:include, Granite::Form::Model::Scopes) }
|
36
|
-
subject(:instances) { model.instantiate_collection([{name: 'Hello', foo: 'Bar'}, {name: 'World'}]) }
|
37
41
|
|
38
42
|
specify { expect(subject).to be_a Granite::Form::Model::Scopes::ScopeProxy }
|
39
43
|
specify { expect(subject.count).to eq(2) }
|
40
|
-
|
41
|
-
specify
|
44
|
+
|
45
|
+
specify do
|
46
|
+
expect(subject.first.instance_variable_get(:@initial_attributes)).to eq({ name: 'Hello' }.stringify_keys)
|
47
|
+
end
|
48
|
+
|
49
|
+
specify do
|
50
|
+
expect(subject.second.instance_variable_get(:@initial_attributes)).to eq({ name: 'World' }.stringify_keys)
|
51
|
+
end
|
42
52
|
end
|
43
53
|
end
|
44
54
|
end
|