granite-form 0.4.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (109) hide show
  1. checksums.yaml +4 -4
  2. data/.github/CODEOWNERS +1 -1
  3. data/.github/workflows/ruby.yml +3 -5
  4. data/.rubocop.yml +26 -48
  5. data/.rubocop_todo.yml +304 -27
  6. data/Appraisals +1 -1
  7. data/CHANGELOG.md +15 -2
  8. data/gemfiles/{rails.5.0.gemfile → rails.7.1.gemfile} +3 -3
  9. data/granite-form.gemspec +6 -5
  10. data/lib/granite/form/active_record/associations.rb +4 -3
  11. data/lib/granite/form/config.rb +1 -1
  12. data/lib/granite/form/errors.rb +34 -32
  13. data/lib/granite/form/extensions.rb +2 -1
  14. data/lib/granite/form/model/associations/base.rb +6 -2
  15. data/lib/granite/form/model/associations/collection/embedded.rb +1 -1
  16. data/lib/granite/form/model/associations/collection/proxy.rb +3 -3
  17. data/lib/granite/form/model/associations/embeds_any.rb +1 -1
  18. data/lib/granite/form/model/associations/embeds_many.rb +15 -11
  19. data/lib/granite/form/model/associations/embeds_one.rb +9 -8
  20. data/lib/granite/form/model/associations/nested_attributes.rb +60 -32
  21. data/lib/granite/form/model/associations/persistence_adapters/active_record/referenced_proxy.rb +2 -1
  22. data/lib/granite/form/model/associations/persistence_adapters/active_record.rb +7 -6
  23. data/lib/granite/form/model/associations/persistence_adapters/base.rb +8 -4
  24. data/lib/granite/form/model/associations/references_any.rb +1 -1
  25. data/lib/granite/form/model/associations/references_many.rb +3 -2
  26. data/lib/granite/form/model/associations/references_one.rb +1 -1
  27. data/lib/granite/form/model/associations/reflections/base.rb +3 -2
  28. data/lib/granite/form/model/associations/reflections/embeds_any.rb +4 -4
  29. data/lib/granite/form/model/associations/reflections/embeds_many.rb +4 -1
  30. data/lib/granite/form/model/associations/reflections/embeds_one.rb +4 -1
  31. data/lib/granite/form/model/associations/reflections/references_any.rb +6 -6
  32. data/lib/granite/form/model/associations/reflections/references_many.rb +1 -1
  33. data/lib/granite/form/model/associations/reflections/references_one.rb +1 -1
  34. data/lib/granite/form/model/associations/validations.rb +6 -6
  35. data/lib/granite/form/model/associations.rb +6 -4
  36. data/lib/granite/form/model/attributes/attribute.rb +1 -0
  37. data/lib/granite/form/model/attributes/base.rb +9 -7
  38. data/lib/granite/form/model/attributes/reference_one.rb +1 -1
  39. data/lib/granite/form/model/attributes/reflections/base/build_type_definition.rb +2 -1
  40. data/lib/granite/form/model/attributes/reflections/represents/build_type_definition.rb +2 -2
  41. data/lib/granite/form/model/attributes/represents.rb +1 -1
  42. data/lib/granite/form/model/attributes.rb +21 -13
  43. data/lib/granite/form/model/conventions.rb +1 -1
  44. data/lib/granite/form/model/persistence.rb +1 -1
  45. data/lib/granite/form/model/primary.rb +1 -1
  46. data/lib/granite/form/model/representation.rb +4 -4
  47. data/lib/granite/form/model/scopes.rb +5 -5
  48. data/lib/granite/form/model/validations.rb +4 -3
  49. data/lib/granite/form/types/active_support/time_zone.rb +1 -1
  50. data/lib/granite/form/types/array.rb +1 -1
  51. data/lib/granite/form/types/big_decimal.rb +1 -1
  52. data/lib/granite/form/types/boolean.rb +1 -1
  53. data/lib/granite/form/types/date.rb +1 -1
  54. data/lib/granite/form/types/date_time.rb +1 -1
  55. data/lib/granite/form/types/dictionary.rb +1 -1
  56. data/lib/granite/form/types/float.rb +1 -1
  57. data/lib/granite/form/types/has_subtype.rb +1 -0
  58. data/lib/granite/form/types/hash_with_action_controller_parameters.rb +2 -2
  59. data/lib/granite/form/types/integer.rb +1 -1
  60. data/lib/granite/form/types/object.rb +2 -1
  61. data/lib/granite/form/types/string.rb +1 -1
  62. data/lib/granite/form/types/time.rb +1 -1
  63. data/lib/granite/form/types/uuid.rb +1 -1
  64. data/lib/granite/form/util.rb +1 -1
  65. data/lib/granite/form/version.rb +1 -1
  66. data/spec/granite/form/active_record/associations_spec.rb +35 -13
  67. data/spec/granite/form/config_spec.rb +8 -4
  68. data/spec/granite/form/model/associations/embeds_many_spec.rb +99 -51
  69. data/spec/granite/form/model/associations/embeds_one_spec.rb +48 -25
  70. data/spec/granite/form/model/associations/persistence_adapters/active_record_spec.rb +12 -7
  71. data/spec/granite/form/model/associations/references_many_spec.rb +51 -10
  72. data/spec/granite/form/model/associations/references_one_spec.rb +17 -6
  73. data/spec/granite/form/model/associations/reflections/embeds_many_spec.rb +51 -16
  74. data/spec/granite/form/model/associations/reflections/embeds_one_spec.rb +19 -9
  75. data/spec/granite/form/model/associations/reflections/references_many_spec.rb +67 -15
  76. data/spec/granite/form/model/associations/reflections/references_one_spec.rb +34 -11
  77. data/spec/granite/form/model/associations/validations_spec.rb +16 -5
  78. data/spec/granite/form/model/associations_spec.rb +28 -9
  79. data/spec/granite/form/model/attributes/attribute_spec.rb +33 -11
  80. data/spec/granite/form/model/attributes/base_spec.rb +26 -2
  81. data/spec/granite/form/model/attributes/reflections/attribute_spec.rb +1 -0
  82. data/spec/granite/form/model/attributes/reflections/base_spec.rb +1 -0
  83. data/spec/granite/form/model/attributes/reflections/represents/build_type_definition_spec.rb +3 -1
  84. data/spec/granite/form/model/attributes/reflections/represents_spec.rb +2 -2
  85. data/spec/granite/form/model/attributes/represents_spec.rb +2 -2
  86. data/spec/granite/form/model/attributes_spec.rb +76 -36
  87. data/spec/granite/form/model/dirty_spec.rb +3 -0
  88. data/spec/granite/form/model/persistence_spec.rb +15 -5
  89. data/spec/granite/form/model/primary_spec.rb +17 -2
  90. data/spec/granite/form/model/representation_spec.rb +13 -3
  91. data/spec/granite/form/model/scopes_spec.rb +8 -3
  92. data/spec/granite/form/model/validations/associated_spec.rb +20 -6
  93. data/spec/granite/form/model/validations/nested_spec.rb +30 -14
  94. data/spec/granite/form/model/validations_spec.rb +1 -1
  95. data/spec/granite/form/model_spec.rb +1 -0
  96. data/spec/granite/form/types/collection_spec.rb +2 -1
  97. data/spec/granite/form/types/date_spec.rb +1 -1
  98. data/spec/granite/form/types/date_time_spec.rb +0 -2
  99. data/spec/granite/form/types/dictionary_spec.rb +1 -0
  100. data/spec/granite/form/types/has_subtype_spec.rb +6 -1
  101. data/spec/granite/form/types/hash_with_action_controller_parameters_spec.rb +1 -1
  102. data/spec/granite/form/types/object_spec.rb +2 -0
  103. data/spec/granite/form/types/time_spec.rb +0 -2
  104. data/spec/granite/form/util_spec.rb +6 -3
  105. data/spec/support/active_record.rb +13 -0
  106. data/spec/support/shared/nested_attribute_examples.rb +110 -54
  107. metadata +28 -16
  108. data/gemfiles/rails.5.1.gemfile +0 -14
  109. data/gemfiles/rails.5.2.gemfile +0 -14
@@ -57,16 +57,19 @@ describe Granite::Form::ActiveRecord::Associations do
57
57
  describe '#projects' do
58
58
  specify do
59
59
  expect { user.projects << Project.new }
60
- .not_to change { user.read_attribute(:projects) }
60
+ .not_to(change { user.read_attribute(:projects) })
61
61
  end
62
+
62
63
  specify do
63
64
  expect { user.projects << Project.new(title: 'First') }
64
- .not_to change { user.read_attribute(:projects) }
65
+ .not_to(change { user.read_attribute(:projects) })
65
66
  end
67
+
66
68
  specify do
67
69
  expect { user.projects << Project.new(title: 'First') }
68
- .not_to change { user.projects.reload.count }
70
+ .not_to(change { user.projects.reload.count })
69
71
  end
72
+
70
73
  specify do
71
74
  user.projects << Project.new(title: 'First')
72
75
  user.save
@@ -77,12 +80,14 @@ describe Granite::Form::ActiveRecord::Associations do
77
80
  describe '#profile' do
78
81
  specify do
79
82
  expect { user.profile = Profile.new(first_name: 'google.com') }
80
- .not_to change { user.read_attribute(:profile) }
83
+ .not_to(change { user.read_attribute(:profile) })
81
84
  end
85
+
82
86
  specify do
83
87
  expect { user.profile = Profile.new(first_name: 'google.com') }
84
88
  .to change { user.profile }.from(nil).to(an_instance_of(Profile))
85
89
  end
90
+
86
91
  specify do
87
92
  user.profile = Profile.new(first_name: 'google.com')
88
93
  user.save
@@ -110,8 +115,9 @@ describe Granite::Form::ActiveRecord::Associations do
110
115
  describe '#projects' do
111
116
  specify do
112
117
  expect { user.projects << Project.new(title: 'First') }
113
- .not_to change { user.read_attribute(:projects) }
118
+ .not_to(change { user.read_attribute(:projects) })
114
119
  end
120
+
115
121
  specify do
116
122
  user.projects << Project.new(title: 'First')
117
123
  user.save
@@ -120,19 +126,21 @@ describe Granite::Form::ActiveRecord::Associations do
120
126
 
121
127
  context do
122
128
  let(:project) { Project.new(title: 'First') }
129
+
123
130
  before { project.build_author(name: 'Author') }
124
131
 
125
132
  specify do
126
133
  expect { user.projects << project }
127
- .not_to change { user.read_attribute(:projects) }
134
+ .not_to(change { user.read_attribute(:projects) })
128
135
  end
136
+
129
137
  specify do
130
138
  expect do
131
139
  user.projects << project
132
140
  user.save
133
141
  end
134
142
  .to change { user.reload.read_attribute(:projects) }.from([])
135
- .to([{'title' => 'First', 'author' => {'name' => 'Author'}}])
143
+ .to([{ 'title' => 'First', 'author' => { 'name' => 'Author' } }])
136
144
  end
137
145
  end
138
146
  end
@@ -142,22 +150,25 @@ describe Granite::Form::ActiveRecord::Associations do
142
150
  expect { user.profile = Profile.new(first_name: 'google.com') }
143
151
  .to change { user.profile }.from(nil).to(an_instance_of(Profile))
144
152
  end
153
+
145
154
  specify do
146
155
  user.profile = Profile.new(first_name: 'google.com')
147
156
  user.save
148
157
  expect(user.reload.profile.first_name).to eq('google.com')
149
158
  end
159
+
150
160
  specify do
151
161
  expect { user.profile = Profile.new(first_name: 'google.com') }
152
- .not_to change { user.read_attribute(:profile) }
162
+ .not_to(change { user.read_attribute(:profile) })
153
163
  end
164
+
154
165
  specify do
155
166
  expect do
156
167
  user.profile = Profile.new(first_name: 'google.com')
157
168
  user.save
158
169
  end
159
170
  .to change { user.reload.read_attribute(:profile) }.from(nil)
160
- .to({first_name: 'google.com', last_name: nil, admin: nil}.to_json)
171
+ .to({ first_name: 'google.com', last_name: nil, admin: nil }.to_json)
161
172
  end
162
173
  end
163
174
  end
@@ -167,7 +178,8 @@ describe Granite::Form::ActiveRecord::Associations do
167
178
  expect do
168
179
  stub_class(:book, ActiveRecord::Base) do
169
180
  embeds_one :author, class_name: 'Borogoves'
170
- end.reflect_on_association(:author).klass end.to raise_error NameError
181
+ end.reflect_on_association(:author).klass
182
+ end.to raise_error NameError
171
183
  end
172
184
 
173
185
  specify do
@@ -176,7 +188,8 @@ describe Granite::Form::ActiveRecord::Associations do
176
188
  embeds_many :projects, class_name: 'Borogoves' do
177
189
  attribute :title
178
190
  end
179
- end.reflect_on_association(:projects).klass end.to raise_error NameError
191
+ end.reflect_on_association(:projects).klass
192
+ end.to raise_error NameError
180
193
  end
181
194
  end
182
195
 
@@ -194,13 +207,22 @@ describe Granite::Form::ActiveRecord::Associations do
194
207
 
195
208
  specify { expect(User.reflect_on_association(:projects).klass).to eq(User::Project) }
196
209
  specify { expect(User.new.projects).to eq([]) }
197
- specify { expect(User.new.tap { |u| u.projects.build(title: 'Project') }.projects).to be_a(Granite::Form::Model::Associations::Collection::Embedded) }
198
- specify { expect(User.new.tap { |u| u.projects.build(title: 'Project') }.projects).to match([have_attributes(title: 'Project')]) }
210
+
211
+ specify do
212
+ expect(User.new.tap { |u| u.projects.build(title: 'Project') }.projects)
213
+ .to be_a(Granite::Form::Model::Associations::Collection::Embedded)
214
+ end
215
+
216
+ specify do
217
+ expect(User.new.tap { |u| u.projects.build(title: 'Project') }.projects)
218
+ .to match([have_attributes(title: 'Project')])
219
+ end
199
220
 
200
221
  specify { expect(User.reflect_on_association(:profile).klass).to eq(User::Profile) }
201
222
  specify { expect(User.reflect_on_association(:profile).klass).to be < Profile }
202
223
  specify { expect(User.new.profile).to be_nil }
203
224
  specify { expect(User.new.tap { |u| u.build_profile(first_name: 'Profile') }.profile).to be_a(User::Profile) }
225
+
204
226
  specify do
205
227
  expect(User.new.tap { |u| u.build_profile(first_name: 'Profile') }.profile)
206
228
  .to have_attributes(first_name: 'Profile', last_name: nil, admin: nil, age: nil)
@@ -4,7 +4,8 @@ describe Granite::Form::Config do
4
4
  subject { Granite::Form::Config.send :new }
5
5
 
6
6
  describe '#include_root_in_json' do
7
- its(:include_root_in_json) { should == false }
7
+ its(:include_root_in_json) { is_expected.to eq false }
8
+
8
9
  specify do
9
10
  expect { subject.include_root_in_json = true }
10
11
  .to change { subject.include_root_in_json }.from(false).to(true)
@@ -12,7 +13,8 @@ describe Granite::Form::Config do
12
13
  end
13
14
 
14
15
  describe '#i18n_scope' do
15
- its(:i18n_scope) { should == :granite }
16
+ its(:i18n_scope) { is_expected.to eq :granite }
17
+
16
18
  specify do
17
19
  expect { subject.i18n_scope = :data_model }
18
20
  .to change { subject.i18n_scope }.from(:granite).to(:data_model)
@@ -20,11 +22,12 @@ describe Granite::Form::Config do
20
22
  end
21
23
 
22
24
  describe '#logger' do
23
- its(:logger) { should be_a Logger }
25
+ its(:logger) { is_expected.to be_a Logger }
24
26
  end
25
27
 
26
28
  describe '#primary_attribute' do
27
- its(:primary_attribute) { should == :id }
29
+ its(:primary_attribute) { is_expected.to eq :id }
30
+
28
31
  specify do
29
32
  expect { subject.primary_attribute = :identified }
30
33
  .to change { subject.primary_attribute }.from(:id).to(:identified)
@@ -42,6 +45,7 @@ describe Granite::Form::Config do
42
45
  end
43
46
  }.from(nil).to(an_instance_of(Proc))
44
47
  end
48
+
45
49
  specify { expect { subject.normalizer(:wrong) }.to raise_error Granite::Form::NormalizerMissing }
46
50
  end
47
51
 
@@ -25,19 +25,20 @@ describe Granite::Form::Model::Associations::EmbedsMany do
25
25
  let(:user) { User.new(name: 'User') }
26
26
  let(:association) { user.association(:projects) }
27
27
 
28
- let(:existing_user) { User.instantiate name: 'Rick', projects: [{title: 'Genesis'}] }
28
+ let(:existing_user) { User.instantiate name: 'Rick', projects: [{ title: 'Genesis' }] }
29
29
  let(:existing_association) { existing_user.association(:projects) }
30
30
 
31
31
  context 'callbacks' do
32
32
  before do
33
33
  User.class_eval do
34
34
  embeds_many :projects,
35
- before_add: ->(object) { callbacks.push([:before_add, object]) },
36
- after_add: ->(object) { callbacks.push([:after_add, object]) }
35
+ before_add: ->(object) { callbacks.push([:before_add, object]) },
36
+ after_add: ->(object) { callbacks.push([:after_add, object]) }
37
37
 
38
38
  collection :callbacks, Array
39
39
  end
40
40
  end
41
+
41
42
  let(:project1) { Project.new(title: 'Project1') }
42
43
  let(:project2) { Project.new(title: 'Project2') }
43
44
 
@@ -54,18 +55,18 @@ describe Granite::Form::Model::Associations::EmbedsMany do
54
55
  end
55
56
  .to change { user.callbacks }
56
57
  .to([
57
- [:before_add, project1], [:after_add, project1],
58
- [:before_add, project2], [:after_add, project2]
59
- ])
58
+ [:before_add, project1], [:after_add, project1],
59
+ [:before_add, project2], [:after_add, project2]
60
+ ])
60
61
  end
61
62
 
62
63
  specify do
63
64
  expect { association.concat(project1, project2) }
64
65
  .to change { user.callbacks }
65
66
  .to([
66
- [:before_add, project1], [:after_add, project1],
67
- [:before_add, project2], [:after_add, project2]
68
- ])
67
+ [:before_add, project1], [:after_add, project1],
68
+ [:before_add, project2], [:after_add, project2]
69
+ ])
69
70
  end
70
71
 
71
72
  specify do
@@ -75,18 +76,18 @@ describe Granite::Form::Model::Associations::EmbedsMany do
75
76
  end
76
77
  .to change { user.callbacks }
77
78
  .to([
78
- [:before_add, project1], [:after_add, project1],
79
- [:before_add, project2], [:after_add, project2]
80
- ])
79
+ [:before_add, project1], [:after_add, project1],
80
+ [:before_add, project2], [:after_add, project2]
81
+ ])
81
82
  end
82
83
 
83
84
  specify do
84
85
  expect { association.writer([project2, project1]) }
85
86
  .to change { user.callbacks }
86
87
  .to([
87
- [:before_add, project2], [:after_add, project2],
88
- [:before_add, project1], [:after_add, project1]
89
- ])
88
+ [:before_add, project2], [:after_add, project2],
89
+ [:before_add, project1], [:after_add, project1]
90
+ ])
90
91
  end
91
92
 
92
93
  specify do
@@ -97,18 +98,18 @@ describe Granite::Form::Model::Associations::EmbedsMany do
97
98
  end
98
99
  .to change { user.callbacks }
99
100
  .to([
100
- [:before_add, project1], [:after_add, project1],
101
- [:before_add, project2], [:after_add, project2]
102
- ])
101
+ [:before_add, project1], [:after_add, project1],
102
+ [:before_add, project2], [:after_add, project2]
103
+ ])
103
104
  end
104
105
 
105
106
  context 'default' do
106
107
  before do
107
108
  User.class_eval do
108
109
  embeds_many :projects,
109
- before_add: ->(owner, object) { owner.callbacks.push([:before_add, object]) },
110
- after_add: ->(owner, object) { owner.callbacks.push([:after_add, object]) },
111
- default: -> { {title: 'Project1'} }
110
+ before_add: ->(owner, object) { owner.callbacks.push([:before_add, object]) },
111
+ after_add: ->(owner, object) { owner.callbacks.push([:after_add, object]) },
112
+ default: -> { { title: 'Project1' } }
112
113
 
113
114
  collection :callbacks, Array
114
115
  end
@@ -118,9 +119,9 @@ describe Granite::Form::Model::Associations::EmbedsMany do
118
119
  expect { association.concat(project2) }
119
120
  .to change { user.callbacks }
120
121
  .to([
121
- [:before_add, project2], [:after_add, project2],
122
- [:before_add, project1], [:after_add, project1]
123
- ])
122
+ [:before_add, project2], [:after_add, project2],
123
+ [:before_add, project1], [:after_add, project1]
124
+ ])
124
125
  end
125
126
  end
126
127
  end
@@ -134,14 +135,17 @@ describe Granite::Form::Model::Associations::EmbedsMany do
134
135
  let(:project) { Project.new(title: 'Project') }
135
136
 
136
137
  specify { expect(association.build.embedder).to eq(user) }
138
+
137
139
  specify do
138
140
  expect { association.writer([project]) }
139
141
  .to change { project.embedder }.from(nil).to(user)
140
142
  end
143
+
141
144
  specify do
142
145
  expect { association.concat(project) }
143
146
  .to change { project.embedder }.from(nil).to(user)
144
147
  end
148
+
145
149
  specify do
146
150
  expect { association.target = [project] }
147
151
  .to change { project.embedder }.from(nil).to(user)
@@ -150,7 +154,7 @@ describe Granite::Form::Model::Associations::EmbedsMany do
150
154
  context 'default' do
151
155
  before do
152
156
  User.class_eval do
153
- embeds_many :projects, default: -> { {title: 'Project1'} }
157
+ embeds_many :projects, default: -> { { title: 'Project1' } }
154
158
  end
155
159
  end
156
160
 
@@ -184,22 +188,24 @@ describe Granite::Form::Model::Associations::EmbedsMany do
184
188
 
185
189
  specify do
186
190
  expect { association.build(title: 'Swordfish') }
187
- .not_to change { user.read_attribute(:projects) }
191
+ .not_to(change { user.read_attribute(:projects) })
188
192
  end
193
+
189
194
  specify do
190
195
  expect { association.build(title: 'Swordfish') }
191
196
  .to change { association.reader.map(&:attributes) }
192
- .from([]).to([{'title' => 'Swordfish'}])
197
+ .from([]).to([{ 'title' => 'Swordfish' }])
193
198
  end
194
199
 
195
200
  specify do
196
201
  expect { existing_association.build(title: 'Swordfish') }
197
- .not_to change { existing_user.read_attribute(:projects) }
202
+ .not_to(change { existing_user.read_attribute(:projects) })
198
203
  end
204
+
199
205
  specify do
200
206
  expect { existing_association.build(title: 'Swordfish') }
201
207
  .to change { existing_association.reader.map(&:attributes) }
202
- .from([{'title' => 'Genesis'}]).to([{'title' => 'Genesis'}, {'title' => 'Swordfish'}])
208
+ .from([{ 'title' => 'Genesis' }]).to([{ 'title' => 'Genesis' }, { 'title' => 'Swordfish' }])
203
209
  end
204
210
  end
205
211
 
@@ -210,27 +216,41 @@ describe Granite::Form::Model::Associations::EmbedsMany do
210
216
  end
211
217
 
212
218
  describe '#default' do
213
- before { User.embeds_many :projects, default: -> { {title: 'Default'} } }
214
219
  before do
220
+ User.embeds_many :projects, default: -> { { title: 'Default' } }
215
221
  Project.class_eval do
216
222
  include Granite::Form::Model::Primary
217
223
  primary :title
218
224
  end
219
225
  end
226
+
220
227
  let(:new_project) { Project.new.tap { |p| p.title = 'Project' } }
221
228
  let(:existing_user) { User.instantiate name: 'Rick' }
222
229
 
223
230
  specify { expect(association.target.map(&:title)).to eq(['Default']) }
224
231
  specify { expect(association.target.map(&:new_record?)).to eq([true]) }
225
- specify { expect { association.replace([new_project]) }.to change { association.target.map(&:title) }.to eq(['Project']) }
232
+
233
+ specify do
234
+ expect { association.replace([new_project]) }
235
+ .to change { association.target.map(&:title) }
236
+ .to eq(['Project'])
237
+ end
238
+
226
239
  specify { expect { association.replace([]) }.to change { association.target }.to([]) }
227
240
 
228
241
  specify { expect(existing_association.target).to eq([]) }
229
- specify { expect { existing_association.replace([new_project]) }.to change { existing_association.target.map(&:title) }.to(['Project']) }
230
- specify { expect { existing_association.replace([]) }.not_to change { existing_association.target } }
242
+
243
+ specify do
244
+ expect { existing_association.replace([new_project]) }
245
+ .to change { existing_association.target.map(&:title) }
246
+ .to(['Project'])
247
+ end
248
+
249
+ specify { expect { existing_association.replace([]) }.not_to(change { existing_association.target }) }
231
250
 
232
251
  context do
233
- before { Project.send(:include, Granite::Form::Model::Dirty) }
252
+ before { Project.include Granite::Form::Model::Dirty }
253
+
234
254
  specify { expect(association.target.any?(&:changed?)).to eq(false) }
235
255
  end
236
256
  end
@@ -250,18 +270,20 @@ describe Granite::Form::Model::Associations::EmbedsMany do
250
270
 
251
271
  context do
252
272
  before { association.build(title: 'Swordfish') }
273
+
253
274
  specify do
254
275
  expect { association.reload }
255
- .to change { association.reader.map(&:attributes) }.from([{'title' => 'Swordfish'}]).to([])
276
+ .to change { association.reader.map(&:attributes) }.from([{ 'title' => 'Swordfish' }]).to([])
256
277
  end
257
278
  end
258
279
 
259
280
  context do
260
281
  before { existing_association.build(title: 'Swordfish') }
282
+
261
283
  specify do
262
284
  expect { existing_association.reload }
263
285
  .to change { existing_association.reader.map(&:attributes) }
264
- .from([{'title' => 'Genesis'}, {'title' => 'Swordfish'}]).to([{'title' => 'Genesis'}])
286
+ .from([{ 'title' => 'Genesis' }, { 'title' => 'Swordfish' }]).to([{ 'title' => 'Genesis' }])
265
287
  end
266
288
  end
267
289
  end
@@ -270,7 +292,7 @@ describe Granite::Form::Model::Associations::EmbedsMany do
270
292
  let!(:project) { association.build(title: 'Genesis') }
271
293
 
272
294
  specify do
273
- expect { association.sync }.to change { user.read_attribute(:projects) }.from(nil).to([{'title' => 'Genesis'}])
295
+ expect { association.sync }.to change { user.read_attribute(:projects) }.from(nil).to([{ 'title' => 'Genesis' }])
274
296
  end
275
297
 
276
298
  context 'when embedding is nested' do
@@ -288,19 +310,20 @@ describe Granite::Form::Model::Associations::EmbedsMany do
288
310
 
289
311
  specify do
290
312
  expect { association.sync }.to change { user.read_attribute(:projects) }
291
- .from(nil).to([{'title' => 'Genesis', 'deadline' => {'enabled' => true}}])
313
+ .from(nil).to([{ 'title' => 'Genesis', 'deadline' => { 'enabled' => true } }])
292
314
  end
293
315
  end
294
316
  end
295
317
 
296
318
  describe '#clear' do
297
319
  specify { expect(association.clear).to eq(true) }
298
- specify { expect { association.clear }.not_to change { association.reader } }
320
+ specify { expect { association.clear }.not_to(change { association.reader }) }
299
321
 
300
322
  specify { expect(existing_association.clear).to eq(true) }
323
+
301
324
  specify do
302
325
  expect { existing_association.clear }
303
- .to change { existing_association.reader.map(&:attributes) }.from([{'title' => 'Genesis'}]).to([])
326
+ .to change { existing_association.reader.map(&:attributes) }.from([{ 'title' => 'Genesis' }]).to([])
304
327
  end
305
328
  end
306
329
 
@@ -312,6 +335,7 @@ describe Granite::Form::Model::Associations::EmbedsMany do
312
335
 
313
336
  context do
314
337
  before { association.build }
338
+
315
339
  specify { expect(association.reader.last).to be_a Project }
316
340
  specify { expect(association.reader.last).not_to be_persisted }
317
341
  specify { expect(association.reader.size).to eq(1) }
@@ -320,6 +344,7 @@ describe Granite::Form::Model::Associations::EmbedsMany do
320
344
 
321
345
  context do
322
346
  before { existing_association.build(title: 'Swordfish') }
347
+
323
348
  specify { expect(existing_association.reader.size).to eq(2) }
324
349
  specify { expect(existing_association.reader.last.title).to eq('Swordfish') }
325
350
  specify { expect(existing_association.reader(true).size).to eq(1) }
@@ -342,37 +367,45 @@ describe Granite::Form::Model::Associations::EmbedsMany do
342
367
  specify { expect(association.writer([])).to eq([]) }
343
368
 
344
369
  specify { expect(association.writer([new_project1])).to eq([new_project1]) }
370
+
345
371
  specify do
346
372
  expect { association.writer([new_project1]) }
347
- .to change { association.reader.map(&:attributes) }.from([]).to([{'title' => 'Project 1'}])
373
+ .to change { association.reader.map(&:attributes) }.from([]).to([{ 'title' => 'Project 1' }])
348
374
  end
349
375
 
350
376
  specify do
351
377
  expect { existing_association.writer([new_project1, Dummy.new, new_project2]) }
352
378
  .to raise_error Granite::Form::AssociationTypeMismatch
353
379
  end
380
+
354
381
  specify do
355
- expect { muffle(Granite::Form::AssociationTypeMismatch) { existing_association.writer([new_project1, Dummy.new, new_project2]) } }
356
- .not_to change { existing_association.reader }
382
+ expect do
383
+ muffle(Granite::Form::AssociationTypeMismatch) do
384
+ existing_association.writer([new_project1, Dummy.new, new_project2])
385
+ end
386
+ end.not_to(change { existing_association.reader })
357
387
  end
358
388
 
359
389
  specify { expect { existing_association.writer(nil) }.to raise_error NoMethodError }
390
+
360
391
  specify do
361
392
  expect { muffle(NoMethodError) { existing_association.writer(nil) } }
362
- .not_to change { existing_association.reader }
393
+ .not_to(change { existing_association.reader })
363
394
  end
364
395
 
365
396
  specify { expect(existing_association.writer([])).to eq([]) }
397
+
366
398
  specify do
367
399
  expect { existing_association.writer([]) }
368
400
  .to change { existing_association.reader }.to([])
369
401
  end
370
402
 
371
403
  specify { expect(existing_association.writer([new_project1, new_project2])).to eq([new_project1, new_project2]) }
404
+
372
405
  specify do
373
406
  expect { existing_association.writer([new_project1, new_project2]) }
374
407
  .to change { existing_association.reader.map(&:attributes) }
375
- .from([{'title' => 'Genesis'}]).to([{'title' => 'Project 1'}, {'title' => 'Project 2'}])
408
+ .from([{ 'title' => 'Genesis' }]).to([{ 'title' => 'Project 1' }, { 'title' => 'Project 2' }])
376
409
  end
377
410
  end
378
411
 
@@ -392,39 +425,54 @@ describe Granite::Form::Model::Associations::EmbedsMany do
392
425
  specify { expect(existing_association.concat).to eq(existing_user.projects) }
393
426
 
394
427
  specify { expect(association.concat(new_project1)).to eq([new_project1]) }
428
+
395
429
  specify do
396
430
  expect { association.concat(new_project1) }
397
- .to change { association.reader.map(&:attributes) }.from([]).to([{'title' => 'Project 1'}])
431
+ .to change { association.reader.map(&:attributes) }.from([]).to([{ 'title' => 'Project 1' }])
398
432
  end
433
+
399
434
  specify do
400
435
  expect { association.concat(new_project1) }
401
- .not_to change { user.read_attribute(:projects) }
436
+ .not_to(change { user.read_attribute(:projects) })
402
437
  end
403
438
 
404
439
  specify do
405
440
  expect { existing_association.concat(new_project1, invalid_project) }
406
441
  .to change { existing_association.reader.map(&:attributes) }
407
- .from([{'title' => 'Genesis'}]).to([{'title' => 'Genesis'}, {'title' => 'Project 1'}, {'title' => nil}])
442
+ .from([{ 'title' => 'Genesis' }])
443
+ .to([
444
+ { 'title' => 'Genesis' },
445
+ { 'title' => 'Project 1' },
446
+ { 'title' => nil }
447
+ ])
408
448
  end
409
449
 
410
450
  specify do
411
451
  expect { existing_association.concat(new_project1, Dummy.new, new_project2) }
412
452
  .to raise_error Granite::Form::AssociationTypeMismatch
413
453
  end
454
+
414
455
  specify do
415
- expect { muffle(Granite::Form::AssociationTypeMismatch) { existing_association.concat(new_project1, Dummy.new, new_project2) } }
456
+ expect do
457
+ muffle(Granite::Form::AssociationTypeMismatch) do
458
+ existing_association.concat(new_project1, Dummy.new, new_project2)
459
+ end
460
+ end
416
461
  .to change { existing_association.reader.map(&:attributes) }
417
- .from([{'title' => 'Genesis'}]).to([{'title' => 'Genesis'}, {'title' => 'Project 1'}])
462
+ .from([{ 'title' => 'Genesis' }]).to([{ 'title' => 'Genesis' }, { 'title' => 'Project 1' }])
418
463
  end
419
464
 
420
465
  specify do
421
466
  expect(existing_association.concat(new_project1, new_project2))
422
467
  .to eq([existing_user.projects.first, new_project1, new_project2])
423
468
  end
469
+
424
470
  specify do
425
471
  expect { existing_association.concat([new_project1, new_project2]) }
426
472
  .to change { existing_association.reader.map(&:attributes) }
427
- .from([{'title' => 'Genesis'}]).to([{'title' => 'Genesis'}, {'title' => 'Project 1'}, {'title' => 'Project 2'}])
473
+ .from([{ 'title' => 'Genesis' }]).to([{ 'title' => 'Genesis' },
474
+ { 'title' => 'Project 1' },
475
+ { 'title' => 'Project 2' }])
428
476
  end
429
477
  end
430
478
  end