granite-form 0.2.0 → 0.3.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 (66) hide show
  1. checksums.yaml +4 -4
  2. data/.github/CODEOWNERS +1 -2
  3. data/.github/workflows/{ci.yml → ruby.yml} +22 -4
  4. data/.rubocop.yml +1 -1
  5. data/.rubocop_todo.yml +3 -3
  6. data/Appraisals +1 -2
  7. data/CHANGELOG.md +7 -0
  8. data/README.md +0 -2
  9. data/docker-compose.yml +14 -0
  10. data/gemfiles/rails.5.0.gemfile +0 -1
  11. data/gemfiles/rails.5.1.gemfile +0 -1
  12. data/gemfiles/rails.5.2.gemfile +0 -1
  13. data/granite-form.gemspec +15 -15
  14. data/lib/granite/form/active_record/associations.rb +1 -1
  15. data/lib/granite/form/base.rb +1 -2
  16. data/lib/granite/form/errors.rb +0 -15
  17. data/lib/granite/form/model/associations/base.rb +0 -4
  18. data/lib/granite/form/model/associations/collection/embedded.rb +2 -1
  19. data/lib/granite/form/model/associations/collection/proxy.rb +1 -1
  20. data/lib/granite/form/model/associations/embeds_any.rb +7 -0
  21. data/lib/granite/form/model/associations/embeds_many.rb +9 -58
  22. data/lib/granite/form/model/associations/embeds_one.rb +7 -36
  23. data/lib/granite/form/model/associations/nested_attributes.rb +5 -5
  24. data/lib/granite/form/model/associations/persistence_adapters/active_record.rb +0 -4
  25. data/lib/granite/form/model/associations/persistence_adapters/base.rb +0 -4
  26. data/lib/granite/form/model/associations/references_many.rb +0 -32
  27. data/lib/granite/form/model/associations/references_one.rb +0 -28
  28. data/lib/granite/form/model/associations/reflections/embeds_any.rb +1 -1
  29. data/lib/granite/form/model/associations/reflections/references_any.rb +0 -4
  30. data/lib/granite/form/model/associations/reflections/references_one.rb +0 -2
  31. data/lib/granite/form/model/associations/reflections/singular.rb +0 -8
  32. data/lib/granite/form/model/associations.rb +0 -6
  33. data/lib/granite/form/model/attributes/base.rb +1 -1
  34. data/lib/granite/form/model/attributes/reflections/attribute.rb +0 -6
  35. data/lib/granite/form/model/attributes/reflections/base.rb +8 -7
  36. data/lib/granite/form/model/attributes/reflections/reference_one.rb +0 -6
  37. data/lib/granite/form/model/persistence.rb +1 -19
  38. data/lib/granite/form/model.rb +0 -2
  39. data/lib/granite/form/version.rb +1 -1
  40. data/spec/granite/form/active_record/associations_spec.rb +16 -18
  41. data/spec/granite/form/model/associations/embeds_many_spec.rb +29 -305
  42. data/spec/granite/form/model/associations/embeds_one_spec.rb +27 -212
  43. data/spec/granite/form/model/associations/nested_attributes_spec.rb +0 -95
  44. data/spec/granite/form/model/associations/references_many_spec.rb +5 -326
  45. data/spec/granite/form/model/associations/references_one_spec.rb +6 -278
  46. data/spec/granite/form/model/associations/reflections/embeds_any_spec.rb +1 -2
  47. data/spec/granite/form/model/associations/reflections/embeds_many_spec.rb +18 -26
  48. data/spec/granite/form/model/associations/reflections/embeds_one_spec.rb +16 -23
  49. data/spec/granite/form/model/associations/reflections/references_many_spec.rb +1 -1
  50. data/spec/granite/form/model/associations/reflections/references_one_spec.rb +1 -22
  51. data/spec/granite/form/model/associations/validations_spec.rb +0 -3
  52. data/spec/granite/form/model/associations_spec.rb +3 -24
  53. data/spec/granite/form/model/dirty_spec.rb +1 -1
  54. data/spec/granite/form/model/persistence_spec.rb +0 -2
  55. data/spec/granite/form/model/validations/associated_spec.rb +2 -4
  56. data/spec/granite/form/model/validations/nested_spec.rb +2 -4
  57. data/spec/spec_helper.rb +0 -15
  58. data/spec/support/active_record.rb +20 -0
  59. data/spec/support/shared/nested_attribute_examples.rb +3 -21
  60. metadata +32 -38
  61. data/.github/workflows/main.yml +0 -29
  62. data/gemfiles/rails.4.2.gemfile +0 -15
  63. data/lib/granite/form/model/callbacks.rb +0 -72
  64. data/lib/granite/form/model/lifecycle.rb +0 -309
  65. data/spec/granite/form/model/callbacks_spec.rb +0 -337
  66. data/spec/granite/form/model/lifecycle_spec.rb +0 -356
@@ -7,7 +7,8 @@ describe Granite::Form::Model::Associations::EmbedsMany do
7
7
  end
8
8
 
9
9
  stub_model(:project) do
10
- include Granite::Form::Model::Lifecycle
10
+ include Granite::Form::Model::Persistence
11
+ include Granite::Form::Model::Associations
11
12
 
12
13
  attribute :title, String
13
14
  validates :title, presence: true
@@ -27,49 +28,6 @@ describe Granite::Form::Model::Associations::EmbedsMany do
27
28
  let(:existing_user) { User.instantiate name: 'Rick', projects: [{title: 'Genesis'}] }
28
29
  let(:existing_association) { existing_user.association(:projects) }
29
30
 
30
- context 'performers' do
31
- let(:user) { User.new(projects: [Project.new(title: 'Project 1')]) }
32
-
33
- specify do
34
- p2 = user.projects.build(title: 'Project 2')
35
- p3 = user.projects.build(title: 'Project 3')
36
- p4 = user.projects.create(title: 'Project 4')
37
- expect(user.read_attribute(:projects)).to eq([{'title' => 'Project 4'}])
38
- p2.save!
39
- expect(user.read_attribute(:projects)).to eq([{'title' => 'Project 2'}, {'title' => 'Project 4'}])
40
- p2.destroy!.destroy!
41
- expect(user.read_attribute(:projects)).to eq([{'title' => 'Project 4'}])
42
- user.projects.create(title: 'Project 5')
43
- expect(user.read_attribute(:projects)).to eq([{'title' => 'Project 4'}, {'title' => 'Project 5'}])
44
- p3.destroy!
45
- user.projects.first.destroy!
46
- expect(user.read_attribute(:projects)).to eq([{'title' => 'Project 4'}, {'title' => 'Project 5'}])
47
- p4.destroy!.save!
48
- expect(user.read_attribute(:projects)).to eq([{'title' => 'Project 4'}, {'title' => 'Project 5'}])
49
- expect(user.projects.count).to eq(5)
50
- user.projects.map(&:save!)
51
- expect(user.read_attribute(:projects)).to eq([
52
- {'title' => 'Project 1'}, {'title' => 'Project 2'}, {'title' => 'Project 3'},
53
- {'title' => 'Project 4'}, {'title' => 'Project 5'}
54
- ])
55
- user.projects.map(&:destroy!)
56
- expect(user.read_attribute(:projects)).to eq([])
57
- user.projects.first(2).map(&:save!)
58
- expect(user.read_attribute(:projects)).to eq([{'title' => 'Project 1'}, {'title' => 'Project 2'}])
59
- expect(user.projects.reload.count).to eq(2)
60
- p3 = user.projects.create!(title: 'Project 3')
61
- expect(user.read_attribute(:projects)).to eq([
62
- {'title' => 'Project 1'}, {'title' => 'Project 2'}, {'title' => 'Project 3'}
63
- ])
64
- p3.destroy!
65
- expect(user.read_attribute(:projects)).to eq([{'title' => 'Project 1'}, {'title' => 'Project 2'}])
66
- user.projects.create!(title: 'Project 4')
67
- expect(user.read_attribute(:projects)).to eq([
68
- {'title' => 'Project 1'}, {'title' => 'Project 2'}, {'title' => 'Project 4'}
69
- ])
70
- end
71
- end
72
-
73
31
  context 'callbacks' do
74
32
  before do
75
33
  User.class_eval do
@@ -101,24 +59,6 @@ describe Granite::Form::Model::Associations::EmbedsMany do
101
59
  ])
102
60
  end
103
61
 
104
- specify do
105
- expect { association.create(title: 'Project1') }
106
- .to change { user.callbacks }
107
- .to([[:before_add, project1], [:after_add, project1]])
108
- end
109
-
110
- specify do
111
- expect do
112
- association.create(title: 'Project1')
113
- association.create(title: 'Project2')
114
- end
115
- .to change { user.callbacks }
116
- .to([
117
- [:before_add, project1], [:after_add, project1],
118
- [:before_add, project2], [:after_add, project2]
119
- ])
120
- end
121
-
122
62
  specify do
123
63
  expect { association.concat(project1, project2) }
124
64
  .to change { user.callbacks }
@@ -194,7 +134,6 @@ describe Granite::Form::Model::Associations::EmbedsMany do
194
134
  let(:project) { Project.new(title: 'Project') }
195
135
 
196
136
  specify { expect(association.build.embedder).to eq(user) }
197
- specify { expect(association.create.embedder).to eq(user) }
198
137
  specify do
199
138
  expect { association.writer([project]) }
200
139
  .to change { project.embedder }.from(nil).to(user)
@@ -236,7 +175,6 @@ describe Granite::Form::Model::Associations::EmbedsMany do
236
175
  end
237
176
 
238
177
  specify { expect(association.build(title: 'Project').title).to eq('ProjectUser') }
239
- specify { expect(association.create(title: 'Project').title).to eq('ProjectUser') }
240
178
  end
241
179
  end
242
180
 
@@ -265,174 +203,6 @@ describe Granite::Form::Model::Associations::EmbedsMany do
265
203
  end
266
204
  end
267
205
 
268
- describe '#create' do
269
- specify { expect(association.create).to be_a Project }
270
- specify { expect(association.create).not_to be_persisted }
271
-
272
- specify { expect(association.create(title: 'Swordfish')).to be_a Project }
273
- specify { expect(association.create(title: 'Swordfish')).to be_persisted }
274
-
275
- specify do
276
- expect { association.create }
277
- .not_to change { user.read_attribute(:projects) }
278
- end
279
- specify do
280
- expect { association.create(title: 'Swordfish') }
281
- .to change { user.read_attribute(:projects) }
282
- .from(nil).to([{'title' => 'Swordfish'}])
283
- end
284
- specify do
285
- expect { association.create(title: 'Swordfish') }
286
- .to change { association.reader.map(&:attributes) }
287
- .from([]).to([{'title' => 'Swordfish'}])
288
- end
289
-
290
- specify do
291
- expect { existing_association.create }
292
- .not_to change { existing_user.read_attribute(:projects) }
293
- end
294
- specify do
295
- expect { existing_association.create(title: 'Swordfish') }
296
- .to change { existing_user.read_attribute(:projects) }
297
- .from([{title: 'Genesis'}]).to([{title: 'Genesis'}, {'title' => 'Swordfish'}])
298
- end
299
- specify do
300
- expect { existing_association.create(title: 'Swordfish') }
301
- .to change { existing_association.reader.map(&:attributes) }
302
- .from([{'title' => 'Genesis'}]).to([{'title' => 'Genesis'}, {'title' => 'Swordfish'}])
303
- end
304
- end
305
-
306
- describe '#create!' do
307
- specify { expect { association.create! }.to raise_error Granite::Form::ValidationError }
308
-
309
- specify { expect(association.create!(title: 'Swordfish')).to be_a Project }
310
- specify { expect(association.create!(title: 'Swordfish')).to be_persisted }
311
-
312
- specify do
313
- expect { muffle(Granite::Form::ValidationError) { association.create! } }
314
- .not_to change { user.read_attribute(:projects) }
315
- end
316
- specify do
317
- expect { muffle(Granite::Form::ValidationError) { association.create! } }
318
- .to change { association.reader.map(&:attributes) }.from([]).to([{'title' => nil}])
319
- end
320
- specify do
321
- expect { association.create!(title: 'Swordfish') }
322
- .to change { user.read_attribute(:projects) }.from(nil).to([{'title' => 'Swordfish'}])
323
- end
324
- specify do
325
- expect { association.create!(title: 'Swordfish') }
326
- .to change { association.reader.map(&:attributes) }
327
- .from([]).to([{'title' => 'Swordfish'}])
328
- end
329
-
330
- specify do
331
- expect { muffle(Granite::Form::ValidationError) { existing_association.create! } }
332
- .not_to change { existing_user.read_attribute(:projects) }
333
- end
334
- specify do
335
- expect { muffle(Granite::Form::ValidationError) { existing_association.create! } }
336
- .to change { existing_association.reader.map(&:attributes) }
337
- .from([{'title' => 'Genesis'}]).to([{'title' => 'Genesis'}, {'title' => nil}])
338
- end
339
- specify do
340
- expect { existing_association.create!(title: 'Swordfish') }
341
- .to change { existing_user.read_attribute(:projects) }
342
- .from([{title: 'Genesis'}]).to([{title: 'Genesis'}, {'title' => 'Swordfish'}])
343
- end
344
- specify do
345
- expect { existing_association.create!(title: 'Swordfish') }
346
- .to change { existing_association.reader.map(&:attributes) }
347
- .from([{'title' => 'Genesis'}]).to([{'title' => 'Genesis'}, {'title' => 'Swordfish'}])
348
- end
349
- end
350
-
351
- describe '#apply_changes' do
352
- specify do
353
- association.build
354
- expect { association.apply_changes }
355
- .not_to change { association.target.map(&:persisted?) }
356
- .from([false])
357
- end
358
- specify do
359
- association.build(title: 'Genesis')
360
- expect { association.apply_changes }
361
- .to change { association.target.map(&:persisted?) }
362
- .from([false]).to([true])
363
- end
364
- specify do
365
- existing_association.target.first.mark_for_destruction
366
- existing_association.build(title: 'Swordfish')
367
- expect { existing_association.apply_changes }
368
- .to change { existing_association.target.map(&:title) }
369
- .to(['Swordfish'])
370
- end
371
- specify do
372
- existing_association.target.first.mark_for_destruction
373
- existing_association.build(title: 'Swordfish')
374
- expect { existing_association.apply_changes }
375
- .to change { existing_association.target.map(&:persisted?) }
376
- .from([true, false]).to([true])
377
- end
378
- specify do
379
- existing_association.target.first.mark_for_destruction
380
- existing_association.build(title: 'Swordfish')
381
- expect { existing_association.apply_changes }
382
- .to change { existing_association.destroyed.map(&:title) }
383
- .from([]).to(['Genesis'])
384
- end
385
- specify do
386
- existing_association.target.first.destroy!
387
- existing_association.build(title: 'Swordfish')
388
- expect { existing_association.apply_changes }
389
- .to change { existing_association.target.map(&:title) }
390
- .to(['Swordfish'])
391
- end
392
- specify do
393
- existing_association.target.first.destroy!
394
- existing_association.build(title: 'Swordfish')
395
- expect { existing_association.apply_changes }
396
- .to change { existing_association.destroyed.map(&:title) }
397
- .from([]).to(['Genesis'])
398
- end
399
- end
400
-
401
- describe '#apply_changes!' do
402
- specify do
403
- association.build
404
- expect { association.apply_changes! }
405
- .to raise_error Granite::Form::AssociationChangesNotApplied
406
- end
407
- specify do
408
- association.build(title: 'Genesis')
409
- expect { association.apply_changes! }
410
- .to change { association.target.map(&:persisted?) }
411
- .to([true])
412
- end
413
- specify do
414
- existing_association.target.first.mark_for_destruction
415
- existing_association.build(title: 'Swordfish')
416
- expect { existing_association.apply_changes! }
417
- .to change { existing_association.target.map(&:title) }
418
- .to(['Swordfish'])
419
- end
420
- specify do
421
- existing_association.target.first.mark_for_destruction
422
- existing_association.build(title: 'Swordfish')
423
- expect { existing_association.apply_changes! }
424
- .to change { existing_association.target.map(&:persisted?) }
425
- .from([true, false]).to([true])
426
- end
427
- specify do
428
- existing_association.target.first.destroy!
429
- existing_association.build(title: 'Swordfish')
430
- expect { existing_association.apply_changes! }
431
- .to change { existing_association.target.map(&:title) }
432
- .to(['Swordfish'])
433
- end
434
- end
435
-
436
206
  describe '#target' do
437
207
  specify { expect(association.target).to eq([]) }
438
208
  specify { expect(existing_association.target).to eq(existing_user.projects) }
@@ -496,41 +266,44 @@ describe Granite::Form::Model::Associations::EmbedsMany do
496
266
  end
497
267
  end
498
268
 
499
- describe '#clear' do
500
- specify { expect(association.clear).to eq(true) }
501
- specify { expect { association.clear }.not_to change { association.reader } }
269
+ describe '#sync' do
270
+ let!(:project) { association.build(title: 'Genesis') }
502
271
 
503
- specify { expect(existing_association.clear).to eq(true) }
504
272
  specify do
505
- expect { existing_association.clear }
506
- .to change { existing_association.reader.map(&:attributes) }.from([{'title' => 'Genesis'}]).to([])
507
- end
508
- specify do
509
- expect { existing_association.clear }
510
- .to change { existing_user.read_attribute(:projects) }.from([{title: 'Genesis'}]).to([])
273
+ expect { association.sync }.to change { user.read_attribute(:projects) }.from(nil).to([{'title' => 'Genesis'}])
511
274
  end
512
275
 
513
- context do
514
- let(:existing_user) { User.instantiate name: 'Rick', projects: [{title: 'Genesis'}, {title: 'Swordfish'}] }
515
- before { Project.send(:include, Granite::Form::Model::Callbacks) }
516
- if ActiveModel.version >= Gem::Version.new('5.0.0')
517
- before { Project.before_destroy { throw :abort } }
518
- else
519
- before { Project.before_destroy { false } }
520
- end
276
+ context 'when embedding is nested' do
277
+ before do
278
+ Project.class_eval do
279
+ include Granite::Form::Model::Associations
521
280
 
522
- specify { expect(existing_association.clear).to eq(false) }
523
- specify do
524
- expect { existing_association.clear }
525
- .not_to change { existing_association.reader }
281
+ embeds_one :deadline do
282
+ attribute :enabled, Boolean
283
+ end
284
+ end
285
+
286
+ project.build_deadline(enabled: true)
526
287
  end
288
+
527
289
  specify do
528
- expect { existing_association.clear }
529
- .not_to change { existing_user.read_attribute(:projects) }
290
+ expect { association.sync }.to change { user.read_attribute(:projects) }
291
+ .from(nil).to([{'title' => 'Genesis', 'deadline' => {'enabled' => true}}])
530
292
  end
531
293
  end
532
294
  end
533
295
 
296
+ describe '#clear' do
297
+ specify { expect(association.clear).to eq(true) }
298
+ specify { expect { association.clear }.not_to change { association.reader } }
299
+
300
+ specify { expect(existing_association.clear).to eq(true) }
301
+ specify do
302
+ expect { existing_association.clear }
303
+ .to change { existing_association.reader.map(&:attributes) }.from([{'title' => 'Genesis'}]).to([])
304
+ end
305
+ end
306
+
534
307
  describe '#reader' do
535
308
  specify { expect(association.reader).to eq([]) }
536
309
 
@@ -573,52 +346,23 @@ describe Granite::Form::Model::Associations::EmbedsMany do
573
346
  expect { association.writer([new_project1]) }
574
347
  .to change { association.reader.map(&:attributes) }.from([]).to([{'title' => 'Project 1'}])
575
348
  end
576
- specify do
577
- expect { association.writer([new_project1]) }
578
- .not_to change { user.read_attribute(:projects) }
579
- end
580
-
581
- specify do
582
- expect { existing_association.writer([new_project1, invalid_project]) }
583
- .to raise_error Granite::Form::AssociationChangesNotApplied
584
- end
585
- specify do
586
- expect { muffle(Granite::Form::AssociationChangesNotApplied) { existing_association.writer([new_project1, invalid_project]) } }
587
- .not_to change { existing_user.read_attribute(:projects) }
588
- end
589
- specify do
590
- expect { muffle(Granite::Form::AssociationChangesNotApplied) { existing_association.writer([new_project1, invalid_project]) } }
591
- .not_to change { existing_association.reader }
592
- end
593
349
 
594
350
  specify do
595
351
  expect { existing_association.writer([new_project1, Dummy.new, new_project2]) }
596
352
  .to raise_error Granite::Form::AssociationTypeMismatch
597
353
  end
598
- specify do
599
- expect { muffle(Granite::Form::AssociationTypeMismatch) { existing_association.writer([new_project1, Dummy.new, new_project2]) } }
600
- .not_to change { existing_user.read_attribute(:projects) }
601
- end
602
354
  specify do
603
355
  expect { muffle(Granite::Form::AssociationTypeMismatch) { existing_association.writer([new_project1, Dummy.new, new_project2]) } }
604
356
  .not_to change { existing_association.reader }
605
357
  end
606
358
 
607
359
  specify { expect { existing_association.writer(nil) }.to raise_error NoMethodError }
608
- specify do
609
- expect { muffle(NoMethodError) { existing_association.writer(nil) } }
610
- .not_to change { existing_user.read_attribute(:projects) }
611
- end
612
360
  specify do
613
361
  expect { muffle(NoMethodError) { existing_association.writer(nil) } }
614
362
  .not_to change { existing_association.reader }
615
363
  end
616
364
 
617
365
  specify { expect(existing_association.writer([])).to eq([]) }
618
- specify do
619
- expect { existing_association.writer([]) }
620
- .to change { existing_user.read_attribute(:projects) }.to([])
621
- end
622
366
  specify do
623
367
  expect { existing_association.writer([]) }
624
368
  .to change { existing_association.reader }.to([])
@@ -630,11 +374,6 @@ describe Granite::Form::Model::Associations::EmbedsMany do
630
374
  .to change { existing_association.reader.map(&:attributes) }
631
375
  .from([{'title' => 'Genesis'}]).to([{'title' => 'Project 1'}, {'title' => 'Project 2'}])
632
376
  end
633
- specify do
634
- expect { existing_association.writer([new_project1, new_project2]) }
635
- .to change { existing_user.read_attribute(:projects) }
636
- .from([{title: 'Genesis'}]).to([{'title' => 'Project 1'}, {'title' => 'Project 2'}])
637
- end
638
377
  end
639
378
 
640
379
  describe '#concat' do
@@ -662,12 +401,6 @@ describe Granite::Form::Model::Associations::EmbedsMany do
662
401
  .not_to change { user.read_attribute(:projects) }
663
402
  end
664
403
 
665
- specify { expect(existing_association.concat(new_project1, invalid_project)).to eq(false) }
666
- specify do
667
- expect { existing_association.concat(new_project1, invalid_project) }
668
- .to change { existing_user.read_attribute(:projects) }
669
- .from([{title: 'Genesis'}]).to([{'title' => 'Genesis'}, {'title' => 'Project 1'}])
670
- end
671
404
  specify do
672
405
  expect { existing_association.concat(new_project1, invalid_project) }
673
406
  .to change { existing_association.reader.map(&:attributes) }
@@ -678,10 +411,6 @@ describe Granite::Form::Model::Associations::EmbedsMany do
678
411
  expect { existing_association.concat(new_project1, Dummy.new, new_project2) }
679
412
  .to raise_error Granite::Form::AssociationTypeMismatch
680
413
  end
681
- specify do
682
- expect { muffle(Granite::Form::AssociationTypeMismatch) { existing_association.concat(new_project1, Dummy.new, new_project2) } }
683
- .not_to change { existing_user.read_attribute(:projects) }
684
- end
685
414
  specify do
686
415
  expect { muffle(Granite::Form::AssociationTypeMismatch) { existing_association.concat(new_project1, Dummy.new, new_project2) } }
687
416
  .to change { existing_association.reader.map(&:attributes) }
@@ -697,10 +426,5 @@ describe Granite::Form::Model::Associations::EmbedsMany do
697
426
  .to change { existing_association.reader.map(&:attributes) }
698
427
  .from([{'title' => 'Genesis'}]).to([{'title' => 'Genesis'}, {'title' => 'Project 1'}, {'title' => 'Project 2'}])
699
428
  end
700
- specify do
701
- expect { existing_association.concat([new_project1, new_project2]) }
702
- .to change { existing_user.read_attribute(:projects) }
703
- .from([{title: 'Genesis'}]).to([{'title' => 'Genesis'}, {'title' => 'Project 1'}, {'title' => 'Project 2'}])
704
- end
705
429
  end
706
430
  end