granite-form 0.1.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.
Files changed (134) hide show
  1. checksums.yaml +7 -0
  2. data/.codeclimate.yml +13 -0
  3. data/.github/workflows/ci.yml +35 -0
  4. data/.github/workflows/main.yml +29 -0
  5. data/.gitignore +21 -0
  6. data/.rspec +2 -0
  7. data/.rubocop.yml +64 -0
  8. data/.rubocop_todo.yml +48 -0
  9. data/Appraisals +8 -0
  10. data/CHANGELOG.md +73 -0
  11. data/Gemfile +8 -0
  12. data/Guardfile +77 -0
  13. data/LICENSE +22 -0
  14. data/README.md +429 -0
  15. data/Rakefile +6 -0
  16. data/gemfiles/rails.4.2.gemfile +15 -0
  17. data/gemfiles/rails.5.0.gemfile +15 -0
  18. data/gemfiles/rails.5.1.gemfile +15 -0
  19. data/gemfiles/rails.5.2.gemfile +15 -0
  20. data/gemfiles/rails.6.0.gemfile +14 -0
  21. data/gemfiles/rails.6.1.gemfile +14 -0
  22. data/gemfiles/rails.7.0.gemfile +14 -0
  23. data/granite-form.gemspec +31 -0
  24. data/lib/granite/form/active_record/associations.rb +57 -0
  25. data/lib/granite/form/active_record/nested_attributes.rb +20 -0
  26. data/lib/granite/form/base.rb +15 -0
  27. data/lib/granite/form/config.rb +42 -0
  28. data/lib/granite/form/errors.rb +111 -0
  29. data/lib/granite/form/extensions.rb +36 -0
  30. data/lib/granite/form/model/associations/base.rb +97 -0
  31. data/lib/granite/form/model/associations/collection/embedded.rb +14 -0
  32. data/lib/granite/form/model/associations/collection/proxy.rb +35 -0
  33. data/lib/granite/form/model/associations/embeds_any.rb +19 -0
  34. data/lib/granite/form/model/associations/embeds_many.rb +152 -0
  35. data/lib/granite/form/model/associations/embeds_one.rb +112 -0
  36. data/lib/granite/form/model/associations/nested_attributes.rb +215 -0
  37. data/lib/granite/form/model/associations/persistence_adapters/active_record/referenced_proxy.rb +33 -0
  38. data/lib/granite/form/model/associations/persistence_adapters/active_record.rb +68 -0
  39. data/lib/granite/form/model/associations/persistence_adapters/base.rb +55 -0
  40. data/lib/granite/form/model/associations/references_any.rb +43 -0
  41. data/lib/granite/form/model/associations/references_many.rb +113 -0
  42. data/lib/granite/form/model/associations/references_one.rb +88 -0
  43. data/lib/granite/form/model/associations/reflections/base.rb +92 -0
  44. data/lib/granite/form/model/associations/reflections/embeds_any.rb +52 -0
  45. data/lib/granite/form/model/associations/reflections/embeds_many.rb +17 -0
  46. data/lib/granite/form/model/associations/reflections/embeds_one.rb +19 -0
  47. data/lib/granite/form/model/associations/reflections/references_any.rb +65 -0
  48. data/lib/granite/form/model/associations/reflections/references_many.rb +30 -0
  49. data/lib/granite/form/model/associations/reflections/references_one.rb +32 -0
  50. data/lib/granite/form/model/associations/reflections/singular.rb +37 -0
  51. data/lib/granite/form/model/associations/validations.rb +41 -0
  52. data/lib/granite/form/model/associations.rb +120 -0
  53. data/lib/granite/form/model/attributes/attribute.rb +75 -0
  54. data/lib/granite/form/model/attributes/base.rb +134 -0
  55. data/lib/granite/form/model/attributes/collection.rb +19 -0
  56. data/lib/granite/form/model/attributes/dictionary.rb +28 -0
  57. data/lib/granite/form/model/attributes/localized.rb +44 -0
  58. data/lib/granite/form/model/attributes/reference_many.rb +21 -0
  59. data/lib/granite/form/model/attributes/reference_one.rb +52 -0
  60. data/lib/granite/form/model/attributes/reflections/attribute.rb +61 -0
  61. data/lib/granite/form/model/attributes/reflections/base.rb +62 -0
  62. data/lib/granite/form/model/attributes/reflections/collection.rb +12 -0
  63. data/lib/granite/form/model/attributes/reflections/dictionary.rb +15 -0
  64. data/lib/granite/form/model/attributes/reflections/localized.rb +45 -0
  65. data/lib/granite/form/model/attributes/reflections/reference_many.rb +12 -0
  66. data/lib/granite/form/model/attributes/reflections/reference_one.rb +49 -0
  67. data/lib/granite/form/model/attributes/reflections/represents.rb +56 -0
  68. data/lib/granite/form/model/attributes/represents.rb +67 -0
  69. data/lib/granite/form/model/attributes.rb +204 -0
  70. data/lib/granite/form/model/callbacks.rb +72 -0
  71. data/lib/granite/form/model/conventions.rb +40 -0
  72. data/lib/granite/form/model/dirty.rb +84 -0
  73. data/lib/granite/form/model/lifecycle.rb +309 -0
  74. data/lib/granite/form/model/localization.rb +26 -0
  75. data/lib/granite/form/model/persistence.rb +59 -0
  76. data/lib/granite/form/model/primary.rb +59 -0
  77. data/lib/granite/form/model/representation.rb +101 -0
  78. data/lib/granite/form/model/scopes.rb +118 -0
  79. data/lib/granite/form/model/validations/associated.rb +22 -0
  80. data/lib/granite/form/model/validations/nested.rb +56 -0
  81. data/lib/granite/form/model/validations.rb +29 -0
  82. data/lib/granite/form/model.rb +33 -0
  83. data/lib/granite/form/railtie.rb +9 -0
  84. data/lib/granite/form/undefined_class.rb +11 -0
  85. data/lib/granite/form/version.rb +5 -0
  86. data/lib/granite/form.rb +163 -0
  87. data/spec/lib/granite/form/active_record/associations_spec.rb +211 -0
  88. data/spec/lib/granite/form/active_record/nested_attributes_spec.rb +15 -0
  89. data/spec/lib/granite/form/config_spec.rb +66 -0
  90. data/spec/lib/granite/form/model/associations/embeds_many_spec.rb +706 -0
  91. data/spec/lib/granite/form/model/associations/embeds_one_spec.rb +533 -0
  92. data/spec/lib/granite/form/model/associations/nested_attributes_spec.rb +119 -0
  93. data/spec/lib/granite/form/model/associations/persistence_adapters/active_record_spec.rb +58 -0
  94. data/spec/lib/granite/form/model/associations/references_many_spec.rb +572 -0
  95. data/spec/lib/granite/form/model/associations/references_one_spec.rb +445 -0
  96. data/spec/lib/granite/form/model/associations/reflections/embeds_any_spec.rb +42 -0
  97. data/spec/lib/granite/form/model/associations/reflections/embeds_many_spec.rb +145 -0
  98. data/spec/lib/granite/form/model/associations/reflections/embeds_one_spec.rb +117 -0
  99. data/spec/lib/granite/form/model/associations/reflections/references_many_spec.rb +303 -0
  100. data/spec/lib/granite/form/model/associations/reflections/references_one_spec.rb +287 -0
  101. data/spec/lib/granite/form/model/associations/validations_spec.rb +137 -0
  102. data/spec/lib/granite/form/model/associations_spec.rb +198 -0
  103. data/spec/lib/granite/form/model/attributes/attribute_spec.rb +186 -0
  104. data/spec/lib/granite/form/model/attributes/base_spec.rb +97 -0
  105. data/spec/lib/granite/form/model/attributes/collection_spec.rb +72 -0
  106. data/spec/lib/granite/form/model/attributes/dictionary_spec.rb +100 -0
  107. data/spec/lib/granite/form/model/attributes/localized_spec.rb +103 -0
  108. data/spec/lib/granite/form/model/attributes/reflections/attribute_spec.rb +72 -0
  109. data/spec/lib/granite/form/model/attributes/reflections/base_spec.rb +56 -0
  110. data/spec/lib/granite/form/model/attributes/reflections/collection_spec.rb +37 -0
  111. data/spec/lib/granite/form/model/attributes/reflections/dictionary_spec.rb +43 -0
  112. data/spec/lib/granite/form/model/attributes/reflections/localized_spec.rb +37 -0
  113. data/spec/lib/granite/form/model/attributes/reflections/represents_spec.rb +70 -0
  114. data/spec/lib/granite/form/model/attributes/represents_spec.rb +85 -0
  115. data/spec/lib/granite/form/model/attributes_spec.rb +350 -0
  116. data/spec/lib/granite/form/model/callbacks_spec.rb +337 -0
  117. data/spec/lib/granite/form/model/conventions_spec.rb +11 -0
  118. data/spec/lib/granite/form/model/dirty_spec.rb +84 -0
  119. data/spec/lib/granite/form/model/lifecycle_spec.rb +356 -0
  120. data/spec/lib/granite/form/model/persistence_spec.rb +46 -0
  121. data/spec/lib/granite/form/model/primary_spec.rb +84 -0
  122. data/spec/lib/granite/form/model/representation_spec.rb +139 -0
  123. data/spec/lib/granite/form/model/scopes_spec.rb +86 -0
  124. data/spec/lib/granite/form/model/typecasting_spec.rb +193 -0
  125. data/spec/lib/granite/form/model/validations/associated_spec.rb +102 -0
  126. data/spec/lib/granite/form/model/validations/nested_spec.rb +164 -0
  127. data/spec/lib/granite/form/model/validations_spec.rb +31 -0
  128. data/spec/lib/granite/form/model_spec.rb +10 -0
  129. data/spec/lib/granite/form_spec.rb +11 -0
  130. data/spec/shared/nested_attribute_examples.rb +332 -0
  131. data/spec/spec_helper.rb +50 -0
  132. data/spec/support/model_helpers.rb +10 -0
  133. data/spec/support/muffle_helper.rb +7 -0
  134. metadata +403 -0
@@ -0,0 +1,356 @@
1
+ require 'spec_helper'
2
+
3
+ describe Granite::Form::Model::Lifecycle do
4
+ context do
5
+ before do
6
+ stub_model(:user) do
7
+ include Granite::Form::Model::Lifecycle
8
+ end
9
+ end
10
+
11
+ subject { User.new }
12
+
13
+ %i[save create update destroy].each do |action|
14
+ specify { expect { subject.public_send "_#{action}_performer=", '' }.to raise_error NoMethodError }
15
+ end
16
+
17
+ context 'performer execution' do
18
+ let(:foo) { true }
19
+
20
+ specify { expect { subject.save { foo } }.to raise_error NameError }
21
+ specify { expect { subject.save { |_| attributes } }.to raise_error NameError }
22
+ specify { expect(subject.save { attributes }).to eq(true) }
23
+ specify { expect(subject.save { |_| foo }).to eq(true) }
24
+ end
25
+
26
+ context 'save' do
27
+ specify { expect { subject.save }.to raise_error Granite::Form::UnsavableObject }
28
+ specify { expect { subject.save! }.to raise_error Granite::Form::UnsavableObject }
29
+
30
+ specify { expect(subject.save { true }).to eq(true) }
31
+ specify { expect(subject.save! { true }).to eq(true) }
32
+
33
+ context 'instance performer' do
34
+ before { subject.define_save { false } }
35
+
36
+ specify { expect(subject.save).to eq(false) }
37
+ specify { expect { subject.save! }.to raise_error Granite::Form::ObjectNotSaved }
38
+ end
39
+
40
+ context 'create performer' do
41
+ before { User.define_create { true } }
42
+
43
+ specify { expect(subject.save).to eq(true) }
44
+ specify { expect(subject.save!).to eq(true) }
45
+
46
+ context do
47
+ subject { User.create }
48
+
49
+ specify { expect { subject.save }.to raise_error Granite::Form::UnsavableObject }
50
+ specify { expect { subject.save! }.to raise_error Granite::Form::UnsavableObject }
51
+ end
52
+
53
+ context 'instance performer' do
54
+ before { subject.define_create { false } }
55
+
56
+ specify { expect(subject.save).to eq(false) }
57
+ specify { expect { subject.save! }.to raise_error Granite::Form::ObjectNotSaved }
58
+ end
59
+ end
60
+
61
+ context 'update performer' do
62
+ before { User.define_update { true } }
63
+
64
+ specify { expect { subject.save }.to raise_error Granite::Form::UnsavableObject }
65
+ specify { expect { subject.save! }.to raise_error Granite::Form::UnsavableObject }
66
+
67
+ context do
68
+ subject { User.new.tap { |u| u.save { true } } }
69
+
70
+ specify { expect(subject.save).to eq(true) }
71
+ specify { expect(subject.save!).to eq(true) }
72
+
73
+ context 'instance performer' do
74
+ before { subject.define_update { false } }
75
+
76
+ specify { expect(subject.save).to eq(false) }
77
+ specify { expect { subject.save! }.to raise_error Granite::Form::ObjectNotSaved }
78
+ end
79
+ end
80
+ end
81
+
82
+ context 'performers execution' do
83
+ before do
84
+ stub_model(:user) do
85
+ include Granite::Form::Model::Lifecycle
86
+
87
+ attribute :actions, Array, default: []
88
+
89
+ def append(action)
90
+ self.actions = actions + [action]
91
+ end
92
+
93
+ define_create { append :create }
94
+ define_update { append :update }
95
+ define_destroy { append :destroy }
96
+ end
97
+ end
98
+
99
+ subject { User.new }
100
+
101
+ specify do
102
+ subject.destroy
103
+ subject.save
104
+ subject.save
105
+ subject.destroy
106
+ subject.destroy
107
+ subject.save
108
+ expect(subject.actions).to eq(%i[destroy create update destroy destroy create])
109
+ end
110
+ end
111
+ end
112
+
113
+ context 'destroy' do
114
+ specify { expect { subject.destroy }.to raise_error Granite::Form::UndestroyableObject }
115
+ specify { expect { subject.destroy! }.to raise_error Granite::Form::UndestroyableObject }
116
+
117
+ specify { expect(subject.destroy { true }).to be_a User }
118
+ specify { expect(subject.destroy! { true }).to be_a User }
119
+
120
+ context 'instance performer' do
121
+ before { subject.define_save { false } }
122
+
123
+ specify { expect(subject.save).to eq(false) }
124
+ specify { expect { subject.save! }.to raise_error Granite::Form::ObjectNotSaved }
125
+ end
126
+ end
127
+ end
128
+
129
+ context do
130
+ before do
131
+ stub_class(:storage) do
132
+ def self.storage
133
+ @storage ||= {}
134
+ end
135
+ end
136
+ end
137
+
138
+ before do
139
+ stub_model(:user) do
140
+ include Granite::Form::Model::Lifecycle
141
+ delegate :generate_id, to: 'self.class'
142
+
143
+ attribute :id, Integer, &:generate_id
144
+ attribute :name, String
145
+ validates :id, :name, presence: true
146
+
147
+ def self.generate_id
148
+ @id = @id.to_i.next
149
+ end
150
+
151
+ define_save do
152
+ Storage.storage.merge!(id => attributes.symbolize_keys)
153
+ end
154
+
155
+ define_destroy do
156
+ Storage.storage.delete(id)
157
+ end
158
+ end
159
+ end
160
+
161
+ describe '.create' do
162
+ subject { User.create(name: 'Jonny') }
163
+
164
+ it { is_expected.to be_a User }
165
+ it { is_expected.to be_valid }
166
+ it { is_expected.to be_persisted }
167
+
168
+ context 'invalid' do
169
+ subject { User.create }
170
+
171
+ it { is_expected.to be_a User }
172
+ it { is_expected.to be_invalid }
173
+ it { is_expected.not_to be_persisted }
174
+ end
175
+ end
176
+
177
+ describe '.create!' do
178
+ subject { User.create!(name: 'Jonny') }
179
+
180
+ it { is_expected.to be_a User }
181
+ it { is_expected.to be_valid }
182
+ it { is_expected.to be_persisted }
183
+
184
+ context 'invalid' do
185
+ subject { User.create! }
186
+
187
+ specify { expect { subject }.to raise_error Granite::Form::ValidationError }
188
+ end
189
+ end
190
+
191
+ describe '#update, #update!' do
192
+ subject { User.new }
193
+
194
+ specify { expect { subject.update(name: 'Jonny') }.to change { subject.persisted? }.from(false).to(true) }
195
+ specify { expect { subject.update!(name: 'Jonny') }.to change { subject.persisted? }.from(false).to(true) }
196
+
197
+ specify { expect { subject.update({}) }.not_to change { subject.persisted? } }
198
+ specify { expect { subject.update!({}) }.to raise_error Granite::Form::ValidationError }
199
+
200
+ specify do
201
+ expect { subject.update(name: 'Jonny') }
202
+ .to change { Storage.storage.keys }.from([]).to([subject.id])
203
+ end
204
+ specify do
205
+ expect { subject.update!(name: 'Jonny') }
206
+ .to change { Storage.storage.keys }.from([]).to([subject.id])
207
+ end
208
+ end
209
+
210
+ describe '#update_attributes, #update_attributes!' do
211
+ subject { User.new }
212
+
213
+ specify { expect { subject.update_attributes(name: 'Jonny') }.to change { subject.persisted? }.from(false).to(true) }
214
+ specify { expect { subject.update_attributes!(name: 'Jonny') }.to change { subject.persisted? }.from(false).to(true) }
215
+
216
+ specify { expect { subject.update_attributes({}) }.not_to change { subject.persisted? } }
217
+ specify { expect { subject.update_attributes!({}) }.to raise_error Granite::Form::ValidationError }
218
+
219
+ specify do
220
+ expect { subject.update_attributes(name: 'Jonny') }
221
+ .to change { Storage.storage.keys }.from([]).to([subject.id])
222
+ end
223
+ specify do
224
+ expect { subject.update_attributes!(name: 'Jonny') }
225
+ .to change { Storage.storage.keys }.from([]).to([subject.id])
226
+ end
227
+ end
228
+
229
+ describe '#save, #save!' do
230
+ context 'invalid' do
231
+ subject { User.new }
232
+
233
+ it { is_expected.to be_invalid }
234
+ it { is_expected.not_to be_persisted }
235
+
236
+ specify { expect(subject.save).to eq(false) }
237
+ specify { expect { subject.save! }.to raise_error Granite::Form::ValidationError }
238
+
239
+ specify { expect { subject.save }.not_to change { subject.persisted? } }
240
+ specify do
241
+ expect { muffle(Granite::Form::ValidationError) { subject.save! } }
242
+ .not_to change { subject.persisted? }
243
+ end
244
+ end
245
+
246
+ context 'create' do
247
+ subject { User.new(name: 'Jonny') }
248
+
249
+ it { is_expected.to be_valid }
250
+ it { is_expected.not_to be_persisted }
251
+
252
+ specify { expect(subject.save).to eq(true) }
253
+ specify { expect(subject.save!).to eq(true) }
254
+
255
+ specify { expect { subject.save }.to change { subject.persisted? }.from(false).to(true) }
256
+ specify { expect { subject.save! }.to change { subject.persisted? }.from(false).to(true) }
257
+
258
+ specify { expect { subject.save }.to change { Storage.storage.keys }.from([]).to([subject.id]) }
259
+ specify { expect { subject.save! }.to change { Storage.storage.keys }.from([]).to([subject.id]) }
260
+
261
+ context 'save failed' do
262
+ before { User.define_save { false } }
263
+
264
+ it { is_expected.not_to be_persisted }
265
+
266
+ specify { expect(subject.save).to eq(false) }
267
+ specify { expect { subject.save! }.to raise_error Granite::Form::ObjectNotSaved }
268
+
269
+ specify { expect { subject.save }.not_to change { subject.persisted? } }
270
+ specify do
271
+ expect { muffle(Granite::Form::ObjectNotSaved) { subject.save! } }
272
+ .not_to change { subject.persisted? }
273
+ end
274
+ end
275
+ end
276
+
277
+ context 'update' do
278
+ subject! { User.new(name: 'Jonny').tap(&:save).tap { |u| u.name = 'Jimmy' } }
279
+
280
+ it { is_expected.to be_valid }
281
+ it { is_expected.to be_persisted }
282
+
283
+ specify { expect(subject.save).to eq(true) }
284
+ specify { expect(subject.save!).to eq(true) }
285
+
286
+ specify { expect { subject.save }.not_to change { subject.persisted? } }
287
+ specify { expect { subject.save! }.not_to change { subject.persisted? } }
288
+
289
+ specify do
290
+ expect { subject.save }.to change { Storage.storage[subject.id] }
291
+ .from(hash_including(name: 'Jonny')).to(hash_including(name: 'Jimmy'))
292
+ end
293
+ specify do
294
+ expect { subject.save! }.to change { Storage.storage[subject.id] }
295
+ .from(hash_including(name: 'Jonny')).to(hash_including(name: 'Jimmy'))
296
+ end
297
+
298
+ context 'save failed' do
299
+ before { User.define_save { false } }
300
+
301
+ it { is_expected.to be_persisted }
302
+
303
+ specify { expect(subject.save).to eq(false) }
304
+ specify { expect { subject.save! }.to raise_error Granite::Form::ObjectNotSaved }
305
+
306
+ specify { expect { subject.save }.not_to change { subject.persisted? } }
307
+ specify do
308
+ expect { muffle(Granite::Form::ObjectNotSaved) { subject.save! } }
309
+ .not_to change { subject.persisted? }
310
+ end
311
+ end
312
+ end
313
+ end
314
+
315
+ describe '#destroy, #destroy!' do
316
+ subject { User.create(name: 'Jonny') }
317
+
318
+ it { is_expected.to be_valid }
319
+ it { is_expected.to be_persisted }
320
+ it { is_expected.not_to be_destroyed }
321
+
322
+ specify { expect(subject.destroy).to eq(subject) }
323
+ specify { expect(subject.destroy!).to eq(subject) }
324
+
325
+ specify { expect { subject.destroy }.to change { subject.persisted? }.from(true).to(false) }
326
+ specify { expect { subject.destroy! }.to change { subject.persisted? }.from(true).to(false) }
327
+
328
+ specify { expect { subject.destroy }.to change { subject.destroyed? }.from(false).to(true) }
329
+ specify { expect { subject.destroy! }.to change { subject.destroyed? }.from(false).to(true) }
330
+
331
+ specify { expect { subject.destroy }.to change { Storage.storage.keys }.from([subject.id]).to([]) }
332
+ specify { expect { subject.destroy! }.to change { Storage.storage.keys }.from([subject.id]).to([]) }
333
+
334
+ context 'save failed' do
335
+ before { User.define_destroy { false } }
336
+
337
+ it { is_expected.to be_persisted }
338
+
339
+ specify { expect(subject.destroy).to eq(subject) }
340
+ specify { expect { subject.destroy! }.to raise_error Granite::Form::ObjectNotDestroyed }
341
+
342
+ specify { expect { subject.destroy }.not_to change { subject.persisted? } }
343
+ specify do
344
+ expect { muffle(Granite::Form::ObjectNotDestroyed) { subject.destroy! } }
345
+ .not_to change { subject.persisted? }
346
+ end
347
+
348
+ specify { expect { subject.destroy }.not_to change { subject.destroyed? } }
349
+ specify do
350
+ expect { muffle(Granite::Form::ObjectNotDestroyed) { subject.destroy! } }
351
+ .not_to change { subject.destroyed? }
352
+ end
353
+ end
354
+ end
355
+ end
356
+ end
@@ -0,0 +1,46 @@
1
+ require 'spec_helper'
2
+
3
+ describe Granite::Form::Model::Persistence do
4
+ let(:model) do
5
+ stub_model do
6
+ include Granite::Form::Model::Persistence
7
+
8
+ attribute :name
9
+ attribute :count, default: 0
10
+ end
11
+ end
12
+
13
+ specify { expect(model.new).not_to be_persisted }
14
+ specify { expect(model.new).not_to be_destroyed }
15
+
16
+ describe '#instantiate' do
17
+ specify { expect(model.instantiate({})).to be_an_instance_of model }
18
+ specify { expect(model.instantiate({})).to be_persisted }
19
+ specify { expect(model.instantiate({})).not_to be_destroyed }
20
+
21
+ context do
22
+ subject(:instance) { model.instantiate(name: 'Hello', foo: 'Bar') }
23
+
24
+ specify { expect(subject.instance_variable_get(:@initial_attributes)).to eq({name: 'Hello'}.stringify_keys) }
25
+ end
26
+ end
27
+
28
+ describe '#instantiate_collection' do
29
+ context do
30
+ subject(:instances) { model.instantiate_collection(name: 'Hello', foo: 'Bar') }
31
+
32
+ specify { expect(subject).to be_a Array }
33
+ specify { expect(subject.first.instance_variable_get(:@initial_attributes)).to eq({name: 'Hello'}.stringify_keys) }
34
+ end
35
+
36
+ context do
37
+ before { model.send(:include, Granite::Form::Model::Scopes) }
38
+ subject(:instances) { model.instantiate_collection([{name: 'Hello', foo: 'Bar'}, {name: 'World'}]) }
39
+
40
+ specify { expect(subject).to be_a Granite::Form::Model::Scopes::ScopeProxy }
41
+ specify { expect(subject.count).to eq(2) }
42
+ specify { expect(subject.first.instance_variable_get(:@initial_attributes)).to eq({name: 'Hello'}.stringify_keys) }
43
+ specify { expect(subject.second.instance_variable_get(:@initial_attributes)).to eq({name: 'World'}.stringify_keys) }
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,84 @@
1
+ require 'spec_helper'
2
+
3
+ describe Granite::Form::Model::Primary do
4
+ context 'undefined' do
5
+ let(:model) do
6
+ stub_model do
7
+ include Granite::Form::Model::Primary
8
+
9
+ attribute :name, String
10
+ end
11
+ end
12
+
13
+ specify { expect(model.has_primary_attribute?).to eq(false) }
14
+ specify { expect(model.new.has_primary_attribute?).to eq(false) }
15
+ specify { expect { model.new.primary_attribute }.to raise_error NoMethodError }
16
+ specify { expect(model.new(name: 'Hello')).to eq(model.new(name: 'Hello')) }
17
+ specify { expect(model.new(name: 'Hello')).to eql(model.new(name: 'Hello')) }
18
+
19
+ context do
20
+ let(:object) { model.new(name: 'Hello') }
21
+ specify { expect(object).not_to eq(object.clone.tap { |o| o.update(name: 'World') }) }
22
+ specify { expect(object).not_to eql(object.clone.tap { |o| o.update(name: 'World') }) }
23
+ end
24
+ end
25
+
26
+ context 'defined' do
27
+ let(:model) do
28
+ stub_model do
29
+ include Granite::Form::Model::Primary
30
+
31
+ primary_attribute
32
+ attribute :name, String
33
+ end
34
+ end
35
+
36
+ specify { expect(model.has_primary_attribute?).to eq(true) }
37
+ specify { expect(model.new.has_primary_attribute?).to eq(true) }
38
+ specify { expect(model.new.primary_attribute).to be_a(Granite::Form::UUID) }
39
+ specify { expect(model.new(name: 'Hello')).not_to eq(model.new(name: 'Hello')) }
40
+ specify { expect(model.new(name: 'Hello')).not_to eql(model.new(name: 'Hello')) }
41
+ specify { expect(model.new(id: 0).id).not_to eq(Granite::Form::UUID.parse_int(0)) }
42
+ specify { expect(model.new.tap { |o| o.id = 0 }.id).to eq(Granite::Form::UUID.parse_int(0)) }
43
+
44
+ context do
45
+ let(:object) { model.new(name: 'Hello') }
46
+ specify { expect(object).to eq(object.clone.tap { |o| o.update(name: 'World') }) }
47
+ specify { expect(object).to eql(object.clone.tap { |o| o.update(name: 'World') }) }
48
+ end
49
+ end
50
+
51
+ context 'defined' do
52
+ let(:model) do
53
+ stub_model do
54
+ include Granite::Form::Model::Primary
55
+
56
+ primary_attribute type: Integer
57
+ attribute :name
58
+ end
59
+ end
60
+
61
+ specify { expect(model.has_primary_attribute?).to eq(true) }
62
+ specify { expect(model.new.has_primary_attribute?).to eq(true) }
63
+ specify { expect(model.new.primary_attribute).to be_nil }
64
+ specify { expect(model.new(name: 'Hello')).not_to eq(model.new(name: 'Hello')) }
65
+ specify { expect(model.new(name: 'Hello')).not_to eql(model.new(name: 'Hello')) }
66
+ specify { expect(model.new(name: 'Hello').tap { |o| o.id = 1 }).not_to eq(model.new(name: 'Hello').tap { |o| o.id = 2 }) }
67
+ specify { expect(model.new(name: 'Hello').tap { |o| o.id = 1 }).not_to eql(model.new(name: 'Hello').tap { |o| o.id = 2 }) }
68
+ specify { expect(model.new(id: 1).id).to be_nil }
69
+ specify { expect(model.new.tap { |o| o.assign_attributes(id: 1) }.id).to be_nil }
70
+ specify { expect(model.new.tap { |o| o.id = 1 }.id).to eq(1) }
71
+
72
+ context do
73
+ let(:object) { model.new(name: 'Hello').tap { |o| o.id = 1 } }
74
+ specify { expect(object).to eq(object.clone.tap { |o| o.update(name: 'World') }) }
75
+ specify { expect(object).to eql(object.clone.tap { |o| o.update(name: 'World') }) }
76
+ end
77
+
78
+ context do
79
+ let(:object) { model.new(name: 'Hello') }
80
+ specify { expect(object).to eq(object) }
81
+ specify { expect(object).to eql(object) }
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,139 @@
1
+ require 'spec_helper'
2
+
3
+ describe Granite::Form::Model::Representation do
4
+ context 'integration' do
5
+ before do
6
+ stub_model(:author) do
7
+ attribute :rate, Integer
8
+ end
9
+
10
+ stub_model(:post) do
11
+ include Granite::Form::Model::Representation
12
+
13
+ attribute :author, Object
14
+ alias_attribute :a, :author
15
+ represents :rate, of: :a
16
+ alias_attribute :r, :rate
17
+ end
18
+ end
19
+ let(:author) { Author.new(rate: '42') }
20
+
21
+ specify { expect(Post.reflect_on_attribute(:rate).reference).to eq('author') }
22
+
23
+ specify { expect(Post.new(author: author).rate).to eq(42) }
24
+ specify { expect(Post.new(author: author).rate_before_type_cast).to eq('42') }
25
+ specify { expect(Post.new(rate: '33', author: author).rate).to eq(33) }
26
+ specify { expect(Post.new(rate: '33', author: author).author.rate).to eq(33) }
27
+ specify { expect(Post.new(r: '33', author: author).rate).to eq(33) }
28
+ specify { expect(Post.new(r: '33', author: author).author.rate).to eq(33) }
29
+ specify { expect(Post.new(author: author).rate?).to eq(true) }
30
+ specify { expect(Post.new(rate: nil, author: author).rate?).to eq(false) }
31
+
32
+ specify { expect(Post.new.rate).to be_nil }
33
+ specify { expect(Post.new.rate_before_type_cast).to be_nil }
34
+ specify { expect { Post.new(rate: '33') }.to raise_error(NoMethodError) }
35
+
36
+ context 'ActionController::Parameters' do
37
+ let(:params) { instance_double('ActionController::Parameters', to_unsafe_hash: {rate: '33', author: author}) }
38
+ specify { expect { Post.new(params) }.not_to raise_error }
39
+ end
40
+
41
+ context 'dirty' do
42
+ before do
43
+ Author.include Granite::Form::Model::Dirty
44
+ Post.include Granite::Form::Model::Dirty
45
+ end
46
+
47
+ specify do
48
+ expect(Post.new(author: author, rate: '33').changes)
49
+ .to eq('author' => [nil, author], 'rate' => [42, 33])
50
+
51
+ expect(Post.new(author: author, rate: '33').changes)
52
+ .to eq('author' => [nil, author])
53
+ end
54
+ end
55
+
56
+ context do
57
+ before do
58
+ stub_class(:author, ActiveRecord::Base)
59
+
60
+ stub_model(:post) do
61
+ include Granite::Form::Model::Associations
62
+ include Granite::Form::Model::Representation
63
+
64
+ references_one :author
65
+ alias_association :a, :author
66
+ represents :name, of: :a
67
+ end
68
+ end
69
+ let!(:author) { Author.create!(name: 42) }
70
+
71
+ specify { expect(Post.reflect_on_attribute(:name).reference).to eq('author') }
72
+
73
+ specify { expect(Post.new(name: '33', author: author).name).to eq('33') }
74
+ specify { expect(Post.new(name: '33', author: author).author.name).to eq('33') }
75
+ end
76
+
77
+ context 'multiple attributes in a single represents definition' do
78
+ before do
79
+ stub_model(:author) do
80
+ attribute :first_name, String
81
+ attribute :last_name, String
82
+ end
83
+
84
+ stub_model(:post) do
85
+ include Granite::Form::Model::Representation
86
+
87
+ attribute :author, Object
88
+ represents :first_name, :last_name, of: :author
89
+ end
90
+ end
91
+
92
+ let(:author) { Author.new(first_name: 'John', last_name: 'Doe') }
93
+ let(:post) { Post.new }
94
+
95
+ specify do
96
+ expect { post.update(author: author) }
97
+ .to change { post.first_name }.to('John')
98
+ .and change { post.last_name }.to('Doe')
99
+ end
100
+ end
101
+ end
102
+
103
+ describe '#validate' do
104
+ before do
105
+ stub_class(:author, ActiveRecord::Base) do
106
+ validates :name, presence: true
107
+
108
+ # Emulate Active Record association auto save error.
109
+ def errors
110
+ super.tap do |errors|
111
+ errors.add(:'user.email', 'is invalid') if errors[:'user.email'].empty?
112
+ end
113
+ end
114
+ end
115
+
116
+ stub_model(:post) do
117
+ include Granite::Form::Model::Associations
118
+ include Granite::Form::Model::Representation
119
+
120
+ references_one :author
121
+ represents :name, :email, of: :author
122
+ end
123
+ end
124
+
125
+ let(:post) { Post.new(author: Author.new) }
126
+
127
+ specify do
128
+ expect { post.validate }.to change { post.errors.messages }
129
+ .to(hash_including('author.user.email': ['is invalid'], name: ["can't be blank"]))
130
+ end
131
+
132
+ if ActiveModel.version >= Gem::Version.new('6.1.0')
133
+ specify do
134
+ expect { post.validate }.to change { post.errors.details }
135
+ .to(hash_including('author.user.email': [{error: 'is invalid'}], name: [{error: :blank}]))
136
+ end
137
+ end
138
+ end
139
+ end