reform 2.2.3 → 2.3.2

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 (97) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +5 -1
  3. data/.travis.yml +11 -6
  4. data/Appraisals +8 -0
  5. data/CHANGES.md +56 -0
  6. data/CONTRIBUTING.md +31 -0
  7. data/Gemfile +2 -16
  8. data/ISSUE_TEMPLATE.md +25 -0
  9. data/LICENSE.txt +1 -1
  10. data/README.md +5 -7
  11. data/Rakefile +16 -9
  12. data/gemfiles/0.13.0.gemfile +8 -0
  13. data/gemfiles/1.5.0.gemfile +9 -0
  14. data/lib/reform.rb +1 -0
  15. data/lib/reform/contract.rb +7 -17
  16. data/lib/reform/contract/custom_error.rb +41 -0
  17. data/lib/reform/contract/validate.rb +53 -23
  18. data/lib/reform/errors.rb +61 -0
  19. data/lib/reform/form.rb +36 -10
  20. data/lib/reform/form/call.rb +1 -1
  21. data/lib/reform/form/composition.rb +2 -2
  22. data/lib/reform/form/dry.rb +10 -58
  23. data/lib/reform/form/dry/input_hash.rb +37 -0
  24. data/lib/reform/form/dry/new_api.rb +47 -0
  25. data/lib/reform/form/dry/old_api.rb +61 -0
  26. data/lib/reform/form/populator.rb +11 -29
  27. data/lib/reform/form/prepopulate.rb +5 -4
  28. data/lib/reform/form/validate.rb +28 -13
  29. data/lib/reform/result.rb +90 -0
  30. data/lib/reform/validation.rb +19 -11
  31. data/lib/reform/validation/groups.rb +12 -27
  32. data/lib/reform/version.rb +1 -1
  33. data/reform.gemspec +13 -13
  34. data/test/benchmarking.rb +39 -6
  35. data/test/call_new_api.rb +23 -0
  36. data/test/{call_test.rb → call_old_api.rb} +4 -4
  37. data/test/changed_test.rb +8 -8
  38. data/test/coercion_test.rb +51 -19
  39. data/test/composition_new_api.rb +186 -0
  40. data/test/{composition_test.rb → composition_old_api.rb} +66 -31
  41. data/test/contract/custom_error_test.rb +55 -0
  42. data/test/contract_new_api.rb +77 -0
  43. data/test/{contract_test.rb → contract_old_api.rb} +13 -13
  44. data/test/default_test.rb +2 -2
  45. data/test/deserialize_test.rb +11 -14
  46. data/test/errors_new_api.rb +225 -0
  47. data/test/errors_old_api.rb +230 -0
  48. data/test/feature_test.rb +8 -10
  49. data/test/fixtures/dry_error_messages.yml +73 -23
  50. data/test/fixtures/dry_new_api_error_messages.yml +104 -0
  51. data/test/form_new_api.rb +57 -0
  52. data/test/{form_test.rb → form_old_api.rb} +5 -5
  53. data/test/form_option_new_api.rb +24 -0
  54. data/test/{form_option_test.rb → form_option_old_api.rb} +4 -4
  55. data/test/from_test.rb +9 -13
  56. data/test/inherit_new_api.rb +105 -0
  57. data/test/inherit_old_api.rb +105 -0
  58. data/test/{module_test.rb → module_new_api.rb} +20 -25
  59. data/test/module_old_api.rb +146 -0
  60. data/test/parse_option_test.rb +40 -0
  61. data/test/parse_pipeline_test.rb +3 -3
  62. data/test/populate_new_api.rb +304 -0
  63. data/test/{populate_test.rb → populate_old_api.rb} +83 -49
  64. data/test/populator_skip_test.rb +9 -9
  65. data/test/prepopulator_test.rb +8 -9
  66. data/test/read_only_test.rb +12 -1
  67. data/test/readable_test.rb +7 -7
  68. data/test/reform_new_api.rb +204 -0
  69. data/test/{reform_test.rb → reform_old_api.rb} +30 -51
  70. data/test/save_new_api.rb +101 -0
  71. data/test/{save_test.rb → save_old_api.rb} +32 -20
  72. data/test/setup_test.rb +8 -8
  73. data/test/{skip_if_test.rb → skip_if_new_api.rb} +23 -12
  74. data/test/skip_if_old_api.rb +92 -0
  75. data/test/skip_setter_and_getter_test.rb +3 -4
  76. data/test/test_helper.rb +25 -14
  77. data/test/validate_new_api.rb +453 -0
  78. data/test/{validate_test.rb → validate_old_api.rb} +59 -69
  79. data/test/validation/dry_validation_new_api.rb +836 -0
  80. data/test/validation/dry_validation_old_api.rb +772 -0
  81. data/test/validation/result_test.rb +77 -0
  82. data/test/validation_library_provided_test.rb +16 -0
  83. data/test/virtual_test.rb +47 -7
  84. data/test/writeable_test.rb +35 -6
  85. metadata +101 -72
  86. data/gemfiles/Gemfile.disposable-0.3 +0 -6
  87. data/lib/reform/contract/errors.rb +0 -43
  88. data/lib/reform/form/mongoid.rb +0 -37
  89. data/lib/reform/form/orm.rb +0 -26
  90. data/lib/reform/mongoid.rb +0 -4
  91. data/test/deprecation_test.rb +0 -27
  92. data/test/errors_test.rb +0 -165
  93. data/test/inherit_test.rb +0 -119
  94. data/test/readonly_test.rb +0 -14
  95. data/test/validation/dry_test.rb +0 -60
  96. data/test/validation/dry_validation_test.rb +0 -352
  97. data/test/validation/errors.yml +0 -4
@@ -3,15 +3,15 @@ require "test_helper"
3
3
  class CallTest < Minitest::Spec
4
4
  Song = Struct.new(:title)
5
5
 
6
- class SongForm < Reform::Form
6
+ class SongForm < TestForm
7
7
  property :title
8
8
 
9
9
  validation do
10
- key(:title).required
10
+ required(:title).filled
11
11
  end
12
12
  end
13
13
 
14
- let (:form) { SongForm.new(Song.new) }
14
+ let(:form) { SongForm.new(Song.new) }
15
15
 
16
16
  it { form.(title: "True North").success?.must_equal true }
17
17
  it { form.(title: "True North").failure?.must_equal false }
@@ -19,5 +19,5 @@ class CallTest < Minitest::Spec
19
19
  it { form.(title: "").failure?.must_equal true }
20
20
 
21
21
  it { form.(title: "True North").errors.messages.must_equal({}) }
22
- it { form.(title: "").errors.messages.must_equal({:title=>["must be filled"]}) }
22
+ it { form.(title: "").errors.messages.must_equal(title: ["must be filled"]) }
23
23
  end
@@ -1,12 +1,12 @@
1
- require 'test_helper'
2
- require 'reform/form/coercion'
1
+ require "test_helper"
2
+ require "reform/form/coercion"
3
3
 
4
4
  class ChangedTest < MiniTest::Spec
5
5
  Song = Struct.new(:title, :album, :composer)
6
6
  Album = Struct.new(:name, :songs, :artist)
7
7
  Artist = Struct.new(:name)
8
8
 
9
- class AlbumForm < Reform::Form
9
+ class AlbumForm < TestForm
10
10
  property :name
11
11
 
12
12
  collection :songs do
@@ -18,11 +18,11 @@ class ChangedTest < MiniTest::Spec
18
18
  end
19
19
  end
20
20
 
21
- let (:song_with_composer) { Song.new("Resist Stance", nil, composer) }
22
- let (:composer) { Artist.new("Greg Graffin") }
23
- let (:album) { Album.new("The Dissent Of Man", [song_with_composer]) }
21
+ let(:song_with_composer) { Song.new("Resist Stance", nil, composer) }
22
+ let(:composer) { Artist.new("Greg Graffin") }
23
+ let(:album) { Album.new("The Dissent Of Man", [song_with_composer]) }
24
24
 
25
- let (:form) { AlbumForm.new(album) }
25
+ let(:form) { AlbumForm.new(album) }
26
26
 
27
27
  # nothing changed after setup.
28
28
  it do
@@ -38,4 +38,4 @@ class ChangedTest < MiniTest::Spec
38
38
  form.songs[0].changed?(:title).must_equal false
39
39
  form.songs[0].composer.changed?(:name).must_equal true
40
40
  end
41
- end
41
+ end
@@ -1,21 +1,23 @@
1
1
  require "test_helper"
2
2
  require "reform/form/coercion"
3
+ require "disposable/twin/property/hash"
3
4
 
4
5
  class CoercionTest < BaseTest
5
6
  class Irreversible
6
7
  def self.call(value)
7
- value*2
8
+ value * 2
8
9
  end
9
10
  end
10
11
 
11
- class Form < Reform::Form
12
+ class Form < TestForm
12
13
  feature Coercion
14
+ include Disposable::Twin::Property::Hash
13
15
 
14
- property :released_at, type: Types::Form::DateTime
16
+ property :released_at, type: DRY_TYPES_CONSTANT::DateTime
15
17
 
16
18
  property :hit do
17
- property :length, type: Types::Form::Int
18
- property :good, type: Types::Form::Bool
19
+ property :length, type: DRY_TYPES_INT_CONSTANT
20
+ property :good, type: DRY_TYPES_CONSTANT::Bool
19
21
  end
20
22
 
21
23
  property :band do
@@ -23,34 +25,51 @@ class CoercionTest < BaseTest
23
25
  property :value, type: Irreversible
24
26
  end
25
27
  end
28
+
29
+ property :metadata, field: :hash do
30
+ property :publication_settings do
31
+ property :featured, type: DRY_TYPES_CONSTANT::Bool
32
+ end
33
+ end
26
34
  end
27
35
 
28
36
  subject do
29
37
  Form.new(album)
30
38
  end
31
39
 
32
- let (:album) {
40
+ let(:album) do
33
41
  OpenStruct.new(
34
- :released_at => "31/03/1981",
35
- :hit => OpenStruct.new(:length => "312"),
36
- :band => Band.new(OpenStruct.new(:value => "9999.99"))
42
+ released_at: "31/03/1981",
43
+ hit: OpenStruct.new(length: "312"),
44
+ band: Band.new(OpenStruct.new(value: "9999.99")),
45
+ metadata: {}
37
46
  )
38
- }
47
+ end
39
48
 
40
49
  # it { subject.released_at.must_be_kind_of DateTime }
41
50
  it { subject.released_at.must_equal "31/03/1981" } # NO coercion in setup.
42
51
  it { subject.hit.length.must_equal "312" }
43
52
  it { subject.band.label.value.must_equal "9999.99" }
44
53
 
45
-
46
- let (:params) {
54
+ let(:params) do
47
55
  {
48
- :released_at => "30/03/1981",
49
- :hit => {:length => "312"},
50
- :band => {:label => {:value => "9999.99"}}
56
+ released_at: "30/03/1981",
57
+ hit: {
58
+ length: "312",
59
+ good: "0",
60
+ },
61
+ band: {
62
+ label: {
63
+ value: "9999.99"
64
+ }
65
+ },
66
+ metadata: {
67
+ publication_settings: {
68
+ featured: "0"
69
+ }
70
+ }
51
71
  }
52
- }
53
-
72
+ end
54
73
 
55
74
  # validate
56
75
  describe "#validate" do
@@ -58,9 +77,22 @@ class CoercionTest < BaseTest
58
77
 
59
78
  it { subject.released_at.must_equal DateTime.parse("30/03/1981") }
60
79
  it { subject.hit.length.must_equal 312 }
61
- it { subject.hit.good.must_equal nil }
80
+ it { subject.hit.good.must_equal false }
62
81
  it { subject.band.label.value.must_equal "9999.999999.99" } # coercion happened once.
82
+ it { subject.metadata.publication_settings.featured.must_equal false }
63
83
  end
64
84
 
65
- # save
85
+ # sync
86
+ describe "#sync" do
87
+ before do
88
+ subject.validate(params).must_equal true
89
+ subject.sync
90
+ end
91
+
92
+ it { album.released_at.must_equal DateTime.parse("30/03/1981") }
93
+ it { album.hit.length.must_equal 312 }
94
+ it { album.hit.good.must_equal false }
95
+ it { assert_nil album.metadata[:publication_settings] }
96
+ it { album.metadata["publication_settings"]["featured"].must_equal false }
97
+ end
66
98
  end
@@ -0,0 +1,186 @@
1
+ require "test_helper"
2
+
3
+ class FormCompositionInheritanceTest < MiniTest::Spec
4
+ module SizePrice
5
+ include Reform::Form::Module
6
+
7
+ property :price
8
+ property :size
9
+
10
+ module InstanceMethods
11
+ def price(for_size: size)
12
+ case for_size.to_sym
13
+ when :s then super() * 1
14
+ when :m then super() * 2
15
+ when :l then super() * 3
16
+ end
17
+ end
18
+ end
19
+ end
20
+
21
+ class OutfitForm < TestForm
22
+ include Reform::Form::Composition
23
+ include SizePrice
24
+
25
+ property :price, inherit: true, on: :tshirt
26
+ property :size, inherit: true, on: :measurement
27
+ end
28
+
29
+ let(:measurement) { Measurement.new(:l) }
30
+ let(:tshirt) { Tshirt.new(2, :m) }
31
+ let(:form) { OutfitForm.new(tshirt: tshirt, measurement: measurement) }
32
+
33
+ Tshirt = Struct.new(:price, :size)
34
+ Measurement = Struct.new(:size)
35
+
36
+ it { form.price.must_equal 6 }
37
+ it { form.price(for_size: :s).must_equal 2 }
38
+ end
39
+
40
+ class FormCompositionTest < MiniTest::Spec
41
+ Song = Struct.new(:id, :title, :band)
42
+ Requester = Struct.new(:id, :name, :requester)
43
+ Band = Struct.new(:title)
44
+
45
+ class RequestForm < TestForm
46
+ include Composition
47
+
48
+ property :name, on: :requester
49
+ property :requester_id, on: :requester, from: :id
50
+ properties :title, :id, on: :song
51
+ # property :channel # FIXME: what about the "main model"?
52
+ property :channel, virtual: true, on: :song
53
+ property :requester, on: :requester
54
+ property :captcha, on: :song, virtual: true
55
+
56
+ validation do
57
+ params do
58
+ required(:name).filled
59
+ required(:title).filled
60
+ end
61
+ end
62
+
63
+ property :band, on: :song do
64
+ property :title
65
+ end
66
+ end
67
+
68
+ let(:form) { RequestForm.new(song: song, requester: requester) }
69
+ let(:song) { Song.new(1, "Rio", band) }
70
+ let(:requester) { Requester.new(2, "Duran Duran", "MCP") }
71
+ let(:band) { Band.new("Duran^2") }
72
+
73
+ # delegation form -> composition works
74
+ it { form.id.must_equal 1 }
75
+ it { form.title.must_equal "Rio" }
76
+ it { form.name.must_equal "Duran Duran" }
77
+ it { form.requester_id.must_equal 2 }
78
+ it { assert_nil form.channel }
79
+ it { form.requester.must_equal "MCP" } # same name as composed model.
80
+ it { assert_nil form.captcha }
81
+
82
+ # #model just returns <Composition>.
83
+ it { form.mapper.must_be_kind_of Disposable::Composition }
84
+
85
+ # #model[] -> composed models
86
+ it { form.model[:requester].must_equal requester }
87
+ it { form.model[:song].must_equal song }
88
+
89
+ it "creates Composition for you" do
90
+ form.validate("title" => "Greyhound", "name" => "Frenzal Rhomb").must_equal true
91
+ form.validate("title" => "", "name" => "Frenzal Rhomb").must_equal false
92
+ end
93
+
94
+ describe "#save" do
95
+ # #save with {}
96
+ it do
97
+ hash = {}
98
+
99
+ form.save do |map|
100
+ hash[:name] = form.name
101
+ hash[:title] = form.title
102
+ end
103
+
104
+ hash.must_equal({name: "Duran Duran", title: "Rio"})
105
+ end
106
+
107
+ it "provides nested symbolized hash as second block argument" do
108
+ form.validate("title" => "Greyhound", "name" => "Frenzal Rhomb", "channel" => "JJJ", "captcha" => "wonderful")
109
+
110
+ hash = nil
111
+
112
+ form.save do |map|
113
+ hash = map
114
+ end
115
+
116
+ hash.must_equal({
117
+ song: {"title" => "Greyhound", "id" => 1, "channel" => "JJJ", "captcha" => "wonderful", "band" => {"title" => "Duran^2"}},
118
+ requester: {"name" => "Frenzal Rhomb", "id" => 2, "requester" => "MCP"}
119
+ }
120
+ )
121
+ end
122
+
123
+ it "xxx pushes data to models and calls #save when no block passed" do
124
+ song.extend(Saveable)
125
+ requester.extend(Saveable)
126
+ band.extend(Saveable)
127
+
128
+ form.validate("title" => "Greyhound", "name" => "Frenzal Rhomb", "captcha" => "1337")
129
+ form.captcha.must_equal "1337" # TODO: move to separate test.
130
+
131
+ form.save
132
+
133
+ requester.name.must_equal "Frenzal Rhomb"
134
+ requester.saved?.must_equal true
135
+ song.title.must_equal "Greyhound"
136
+ song.saved?.must_equal true
137
+ song.band.title.must_equal "Duran^2"
138
+ song.band.saved?.must_equal true
139
+ end
140
+
141
+ it "returns true when models all save successfully" do
142
+ song.extend(Saveable)
143
+ requester.extend(Saveable)
144
+ band.extend(Saveable)
145
+
146
+ form.save.must_equal true
147
+ end
148
+
149
+ it "returns false when one or more models don't save successfully" do
150
+ module Unsaveable
151
+ def save
152
+ false
153
+ end
154
+ end
155
+
156
+ song.extend(Unsaveable)
157
+ requester.extend(Saveable)
158
+ band.extend(Saveable)
159
+
160
+ form.save.must_equal false
161
+ end
162
+ end
163
+ end
164
+
165
+ class FormCompositionCollectionTest < MiniTest::Spec
166
+ Book = Struct.new(:id, :name)
167
+ Library = Struct.new(:id) do
168
+ def books
169
+ [Book.new(1, "My book")]
170
+ end
171
+ end
172
+
173
+ class LibraryForm < TestForm
174
+ include Reform::Form::Composition
175
+
176
+ collection :books, on: :library do
177
+ property :id
178
+ property :name
179
+ end
180
+ end
181
+
182
+ let(:form) { LibraryForm.new(library: library) }
183
+ let(:library) { Library.new(2) }
184
+
185
+ it { form.save { |hash| hash.must_equal({library: {"books" => [{"id" => 1, "name" => "My book"}]}}) } }
186
+ end
@@ -1,56 +1,92 @@
1
- require 'test_helper'
1
+ require "test_helper"
2
+
3
+ class FormCompositionInheritanceTest < MiniTest::Spec
4
+ module SizePrice
5
+ include Reform::Form::Module
6
+
7
+ property :price
8
+ property :size
9
+
10
+ module InstanceMethods
11
+ def price(for_size: size)
12
+ case for_size.to_sym
13
+ when :s then super() * 1
14
+ when :m then super() * 2
15
+ when :l then super() * 3
16
+ end
17
+ end
18
+ end
19
+ end
20
+
21
+ class OutfitForm < TestForm
22
+ include Reform::Form::Composition
23
+ include SizePrice
24
+
25
+ property :price, inherit: true, on: :tshirt
26
+ property :size, inherit: true, on: :measurement
27
+ end
28
+
29
+ let(:measurement) { Measurement.new(:l) }
30
+ let(:tshirt) { Tshirt.new(2, :m) }
31
+ let(:form) { OutfitForm.new(tshirt: tshirt, measurement: measurement) }
32
+
33
+ Tshirt = Struct.new(:price, :size)
34
+ Measurement = Struct.new(:size)
35
+
36
+ it { form.price.must_equal 6 }
37
+ it { form.price(for_size: :s).must_equal 2 }
38
+ end
2
39
 
3
40
  class FormCompositionTest < MiniTest::Spec
4
41
  Song = Struct.new(:id, :title, :band)
5
42
  Requester = Struct.new(:id, :name, :requester)
6
43
  Band = Struct.new(:title)
7
44
 
8
- class RequestForm < Reform::Form
45
+ class RequestForm < TestForm
9
46
  include Composition
10
47
 
11
- property :name, :on => :requester
12
- property :requester_id, :on => :requester, :from => :id
13
- properties :title, :id, :on => :song
48
+ property :name, on: :requester
49
+ property :requester_id, on: :requester, from: :id
50
+ properties :title, :id, on: :song
14
51
  # property :channel # FIXME: what about the "main model"?
15
- property :channel, :virtual => true, :on => :song
16
- property :requester, :on => :requester
17
- property :captcha, :on => :song, :virtual => true
52
+ property :channel, virtual: true, on: :song
53
+ property :requester, on: :requester
54
+ property :captcha, on: :song, virtual: true
18
55
 
19
56
  validation do
20
- key(:name).required
21
- key(:name).required
22
- key(:title).required
57
+ required(:name).filled
58
+ required(:title).filled
23
59
  end
24
60
 
25
- property :band, :on => :song do
61
+ property :band, on: :song do
26
62
  property :title
27
63
  end
28
64
  end
29
65
 
30
- let (:form) { RequestForm.new(:song => song, :requester => requester) }
31
- let (:song) { Song.new(1, "Rio", band) }
32
- let (:requester) { Requester.new(2, "Duran Duran", "MCP") }
33
- let (:band) { Band.new("Duran^2") }
66
+ let(:form) { RequestForm.new(song: song, requester: requester) }
67
+ let(:song) { Song.new(1, "Rio", band) }
68
+ let(:requester) { Requester.new(2, "Duran Duran", "MCP") }
69
+ let(:band) { Band.new("Duran^2") }
34
70
 
35
71
  # delegation form -> composition works
36
72
  it { form.id.must_equal 1 }
37
73
  it { form.title.must_equal "Rio" }
38
74
  it { form.name.must_equal "Duran Duran" }
39
75
  it { form.requester_id.must_equal 2 }
40
- it { form.channel.must_equal nil }
76
+ it { assert_nil form.channel }
41
77
  it { form.requester.must_equal "MCP" } # same name as composed model.
42
- it { form.captcha.must_equal nil }
78
+ it { assert_nil form.captcha }
43
79
 
44
80
  # #model just returns <Composition>.
45
81
  it { form.mapper.must_be_kind_of Disposable::Composition }
46
82
 
47
83
  # #model[] -> composed models
48
84
  it { form.model[:requester].must_equal requester }
49
- it { form.model[:song].must_equal song }
50
-
85
+ it { form.model[:song].must_equal song }
51
86
 
52
87
  it "creates Composition for you" do
53
- form.validate("title" => "Greyhound", "name" => "Frenzal Rhomb").must_equal false
88
+ form.validate("title" => "Greyhound", "name" => "Frenzal Rhomb").must_equal true
89
+ form.validate("title" => "", "name" => "Frenzal Rhomb").must_equal false
54
90
  end
55
91
 
56
92
  describe "#save" do
@@ -63,7 +99,7 @@ class FormCompositionTest < MiniTest::Spec
63
99
  hash[:title] = form.title
64
100
  end
65
101
 
66
- hash.must_equal({:name=>"Duran Duran", :title=>"Rio"})
102
+ hash.must_equal({name: "Duran Duran", title: "Rio"})
67
103
  end
68
104
 
69
105
  it "provides nested symbolized hash as second block argument" do
@@ -76,9 +112,9 @@ class FormCompositionTest < MiniTest::Spec
76
112
  end
77
113
 
78
114
  hash.must_equal({
79
- :song=>{"title"=>"Greyhound", "id"=>1, "channel" => "JJJ", "captcha"=>"wonderful", "band"=>{"title"=>"Duran^2"}},
80
- :requester=>{"name"=>"Frenzal Rhomb", "id"=>2, "requester" => "MCP"}
81
- }
115
+ song: {"title" => "Greyhound", "id" => 1, "channel" => "JJJ", "captcha" => "wonderful", "band" => {"title" => "Duran^2"}},
116
+ requester: {"name" => "Frenzal Rhomb", "id" => 2, "requester" => "MCP"}
117
+ }
82
118
  )
83
119
  end
84
120
 
@@ -124,16 +160,15 @@ class FormCompositionTest < MiniTest::Spec
124
160
  end
125
161
  end
126
162
 
127
-
128
163
  class FormCompositionCollectionTest < MiniTest::Spec
129
164
  Book = Struct.new(:id, :name)
130
165
  Library = Struct.new(:id) do
131
166
  def books
132
- [Book.new(1,"My book")]
167
+ [Book.new(1, "My book")]
133
168
  end
134
169
  end
135
170
 
136
- class LibraryForm < Reform::Form
171
+ class LibraryForm < TestForm
137
172
  include Reform::Form::Composition
138
173
 
139
174
  collection :books, on: :library do
@@ -142,8 +177,8 @@ class FormCompositionCollectionTest < MiniTest::Spec
142
177
  end
143
178
  end
144
179
 
145
- let (:form) { LibraryForm.new(library: library) }
146
- let (:library) { Library.new(2) }
180
+ let(:form) { LibraryForm.new(library: library) }
181
+ let(:library) { Library.new(2) }
147
182
 
148
- it { form.save do |hash| hash.must_equal({:library=>{"books"=>[{"id"=>1, "name"=>"My book"}]}}) end }
183
+ it { form.save { |hash| hash.must_equal({library: {"books" => [{"id" => 1, "name" => "My book"}]}}) } }
149
184
  end