reform 2.1.0 → 2.2.0.rc1

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 (89) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +0 -1
  3. data/.travis.yml +4 -12
  4. data/CHANGES.md +8 -0
  5. data/README.md +36 -743
  6. data/Rakefile +1 -31
  7. data/gemfiles/{Gemfile.rails-3.1 → Gemfile.disposable-0.3} +1 -2
  8. data/lib/reform.rb +0 -9
  9. data/lib/reform/contract.rb +5 -1
  10. data/lib/reform/form.rb +1 -4
  11. data/lib/reform/form/composition.rb +1 -2
  12. data/lib/reform/form/dry.rb +29 -16
  13. data/lib/reform/form/module.rb +15 -3
  14. data/lib/reform/form/validate.rb +1 -1
  15. data/lib/reform/validation.rb +3 -3
  16. data/lib/reform/version.rb +1 -1
  17. data/reform.gemspec +3 -10
  18. data/test/coercion_test.rb +7 -7
  19. data/test/composition_test.rb +5 -1
  20. data/test/contract_test.rb +10 -4
  21. data/test/deserialize_test.rb +3 -3
  22. data/test/errors_test.rb +48 -28
  23. data/test/form_option_test.rb +3 -1
  24. data/test/form_test.rb +19 -14
  25. data/test/module_test.rb +51 -11
  26. data/test/populate_test.rb +21 -7
  27. data/test/reform_test.rb +24 -20
  28. data/test/save_test.rb +10 -4
  29. data/test/skip_if_test.rb +5 -3
  30. data/test/test_helper.rb +3 -43
  31. data/test/validate_test.rb +34 -14
  32. data/test/validation/dry_test.rb +60 -0
  33. data/test/validation/dry_validation_test.rb +65 -43
  34. data/test/validation/errors.yml +4 -0
  35. metadata +16 -192
  36. data/database.sqlite3 +0 -0
  37. data/gemfiles/Gemfile.rails-3.2 +0 -7
  38. data/gemfiles/Gemfile.rails-4.0 +0 -8
  39. data/gemfiles/Gemfile.rails-4.1 +0 -8
  40. data/gemfiles/Gemfile.rails-4.2 +0 -8
  41. data/lib/reform/active_record.rb +0 -4
  42. data/lib/reform/form/active_model.rb +0 -87
  43. data/lib/reform/form/active_model/form_builder_methods.rb +0 -48
  44. data/lib/reform/form/active_model/model_reflections.rb +0 -46
  45. data/lib/reform/form/active_model/model_validations.rb +0 -110
  46. data/lib/reform/form/active_model/validations.rb +0 -107
  47. data/lib/reform/form/active_record.rb +0 -30
  48. data/lib/reform/form/lotus.rb +0 -59
  49. data/lib/reform/form/multi_parameter_attributes.rb +0 -48
  50. data/lib/reform/form/validation/unique_validator.rb +0 -54
  51. data/lib/reform/rails.rb +0 -13
  52. data/test/active_model_custom_validation_translations_test.rb +0 -75
  53. data/test/active_model_test.rb +0 -207
  54. data/test/active_model_validation_for_property_named_format_test.rb +0 -18
  55. data/test/active_record_test.rb +0 -273
  56. data/test/builder_test.rb +0 -32
  57. data/test/custom_validation_test.rb +0 -47
  58. data/test/dummy/Rakefile +0 -7
  59. data/test/dummy/app/controllers/albums_controller.rb +0 -18
  60. data/test/dummy/app/controllers/application_controller.rb +0 -4
  61. data/test/dummy/app/controllers/musician_controller.rb +0 -5
  62. data/test/dummy/app/forms/album_form.rb +0 -18
  63. data/test/dummy/app/helpers/application_helper.rb +0 -2
  64. data/test/dummy/app/models/album.rb +0 -4
  65. data/test/dummy/app/models/song.rb +0 -3
  66. data/test/dummy/app/views/albums/new.html.erb +0 -28
  67. data/test/dummy/app/views/layouts/application.html.erb +0 -14
  68. data/test/dummy/config.ru +0 -4
  69. data/test/dummy/config/application.rb +0 -20
  70. data/test/dummy/config/boot.rb +0 -10
  71. data/test/dummy/config/database.yml +0 -22
  72. data/test/dummy/config/environment.rb +0 -5
  73. data/test/dummy/config/environments/development.rb +0 -16
  74. data/test/dummy/config/environments/production.rb +0 -46
  75. data/test/dummy/config/environments/test.rb +0 -33
  76. data/test/dummy/config/locales/en.yml +0 -14
  77. data/test/dummy/config/routes.rb +0 -4
  78. data/test/dummy/db/test.sqlite3 +0 -0
  79. data/test/form_builder_test.rb +0 -138
  80. data/test/lotus/Gemfile +0 -5
  81. data/test/lotus/lotus_test.rb +0 -31
  82. data/test/lotus_test.rb +0 -150
  83. data/test/model_reflections_test.rb +0 -138
  84. data/test/model_validations_test.rb +0 -82
  85. data/test/mongoid_test.rb +0 -313
  86. data/test/multi_parameter_attributes_test.rb +0 -50
  87. data/test/rails/integration_test.rb +0 -54
  88. data/test/unique_test.rb +0 -135
  89. data/test/validation/activemodel_validation_test.rb +0 -252
@@ -1,138 +0,0 @@
1
- require 'test_helper'
2
-
3
- # Reform::ModelReflections will be the interface between the form object and form builders like simple_form.
4
- class ModelReflectionTest < MiniTest::Spec
5
- class SongForm < Reform::Form
6
- include Reform::Form::ActiveRecord
7
- include Reform::Form::ActiveModel::ModelReflections
8
-
9
- model :song
10
-
11
- property :title
12
- property :artist do
13
- property :name
14
- end
15
- end
16
-
17
- module ColumnForAttribute
18
- def column_for_attribute(*args)
19
- "#{self.class}: #{args.inspect}"
20
- end
21
- end
22
-
23
- module HasAttribute
24
- def has_attribute?(*args)
25
- "#{self.class}: has #{args.inspect}"
26
- end
27
- end
28
-
29
- module DefinedEnums
30
- def defined_enums
31
- {self.class => []}
32
- end
33
- end
34
-
35
- describe "#column_for_attribute" do
36
- let (:artist) { Artist.new }
37
- let (:song) { Song.new(artist: artist) }
38
- let (:form) { SongForm.new(song) }
39
-
40
- # delegate to model.
41
- it do
42
- song.extend(ColumnForAttribute)
43
- artist.extend(ColumnForAttribute)
44
-
45
- form.column_for_attribute(:title).must_equal "Song: [:title]"
46
- form.artist.column_for_attribute(:name).must_equal "Artist: [:name]"
47
- end
48
- end
49
-
50
- describe "#has_attribute?" do
51
- let (:artist) { Artist.new }
52
- let (:song) { Song.new(artist: artist) }
53
- let (:form) { SongForm.new(song) }
54
-
55
- # delegate to model.
56
- it do
57
- song.extend(HasAttribute)
58
- artist.extend(HasAttribute)
59
-
60
- form.has_attribute?(:title).must_equal "Song: has [:title]"
61
- form.artist.has_attribute?(:name).must_equal "Artist: has [:name]"
62
- end
63
- end
64
-
65
- describe "#defined_enums" do
66
- let (:artist) { Artist.new }
67
- let (:song) { Song.new(artist: artist) }
68
- let (:form) { SongForm.new(song) }
69
-
70
- # delegate to model.
71
- it do
72
- song.extend(DefinedEnums)
73
- artist.extend(DefinedEnums)
74
-
75
- form.defined_enums.must_include Song
76
- form.artist.defined_enums.must_include Artist
77
- end
78
- end
79
-
80
- describe ".reflect_on_association" do
81
- let (:artist) { Artist.new }
82
- let (:song) { Song.new(artist: artist) }
83
- let (:form) { SongForm.new(song) }
84
-
85
- # delegate to model class.
86
- it do
87
- reflection = form.class.reflect_on_association(:artist)
88
- reflection.must_be_kind_of ActiveRecord::Reflection::AssociationReflection
89
- end
90
- end
91
-
92
- class SongWithArtistForm < Reform::Form
93
- include Reform::Form::ActiveRecord
94
- include Reform::Form::ModelReflections
95
- include Reform::Form::Composition
96
-
97
- model :artist
98
-
99
- property :name, on: :artist
100
- property :title, on: :song
101
- end
102
-
103
- describe "#column_for_attribute with composition" do
104
- let (:artist) { Artist.new }
105
- let (:song) { Song.new }
106
- let (:form) { SongWithArtistForm.new(artist: artist, song: song) }
107
-
108
- # delegates to respective model.
109
- it do
110
- song.extend(ColumnForAttribute)
111
- artist.extend(ColumnForAttribute)
112
-
113
-
114
- form.column_for_attribute(:name).must_equal "Artist: [:name]"
115
- form.column_for_attribute(:title).must_equal "Song: [:title]"
116
- end
117
- end
118
-
119
- describe "#defined_enums with composition" do
120
- let (:artist) { Artist.new }
121
- let (:song) { Song.new }
122
- let (:form) { SongWithArtistForm.new(artist: artist, song: song) }
123
-
124
- # delegates to respective model.
125
- it do
126
- song.extend(DefinedEnums)
127
- artist.extend(DefinedEnums)
128
-
129
-
130
- form.defined_enums.must_include Song
131
- form.defined_enums.must_include Artist
132
- end
133
- end
134
-
135
- describe "::validators_on" do
136
- it { assert SongWithArtistForm.validators_on }
137
- end
138
- end
@@ -1,82 +0,0 @@
1
- require 'test_helper'
2
-
3
- class ModelValidationsTest < MiniTest::Spec
4
-
5
- class Album
6
- include ActiveModel::Validations
7
- attr_accessor :title, :artist, :other_attribute
8
-
9
- validates :title, :artist, presence: true
10
- validates :other_attribute, presence: true
11
- end
12
-
13
- class AlbumRating
14
- include ActiveModel::Validations
15
-
16
- attr_accessor :rating
17
-
18
- validates :rating, numericality: { greater_than_or_equal_to: 0 }
19
-
20
- end
21
-
22
- class AlbumForm < Reform::Form
23
- extend ActiveModel::ModelValidations
24
-
25
- property :title
26
- property :artist_name, from: :artist
27
- copy_validations_from Album
28
- end
29
-
30
- class CompositeForm < Reform::Form
31
- include Composition
32
- extend ActiveModel::ModelValidations
33
-
34
- model :album
35
-
36
- property :title, on: :album
37
- property :artist_name, from: :artist, on: :album
38
- property :rating, on: :album_rating
39
-
40
- copy_validations_from album: Album, album_rating: AlbumRating
41
- end
42
-
43
- let(:album) { Album.new }
44
-
45
- describe 'non-composite form' do
46
-
47
- let(:album_form) { AlbumForm.new(album) }
48
-
49
- it 'is not valid when title is not present' do
50
- album_form.validate(artist_name: 'test', title: nil).must_equal false
51
- end
52
-
53
- it 'is not valid when artist_name is not present' do
54
- album_form.validate(artist_name: nil, title: 'test').must_equal false
55
- end
56
-
57
- it 'is valid when title and artist_name is present' do
58
- album_form.validate(artist_name: 'test', title: 'test').must_equal true
59
- end
60
-
61
- end
62
-
63
- describe 'composite form' do
64
-
65
- let(:album_rating) { AlbumRating.new }
66
- let(:composite_form) { CompositeForm.new(album: album, album_rating: album_rating) }
67
-
68
- it 'is valid when all attributes are correct' do
69
- composite_form.validate(artist_name: 'test', title: 'test', rating: 1).must_equal true
70
- end
71
-
72
- it 'is invalid when rating is below 0' do
73
- composite_form.validate(artist_name: 'test', title: 'test', rating: -1).must_equal false
74
- end
75
-
76
- it 'is invalid when artist_name is missing' do
77
- composite_form.validate(artist_name: nil, title: 'test', rating: 1).must_equal false
78
- end
79
-
80
- end
81
-
82
- end
@@ -1,313 +0,0 @@
1
- require 'test_helper'
2
- def mongoid_present?
3
- require 'mongoid'
4
- Mongoid.configure do |config|
5
- config.connect_to("reform-mongoid-test")
6
- end
7
- true
8
- rescue
9
- false
10
- end
11
-
12
- if mongoid_present?
13
- require 'reform/mongoid'
14
-
15
- class Disc
16
- include Mongoid::Document
17
- field :title, type: String
18
- has_many :tunes
19
- has_and_belongs_to_many :musicians
20
- end
21
-
22
- class Musician
23
- include Mongoid::Document
24
- field :name, type: String
25
- end
26
-
27
- class Tune
28
- include Mongoid::Document
29
- include Mongoid::Timestamps
30
- field :title, type: String
31
- belongs_to :disc
32
- belongs_to :musician
33
- end
34
-
35
- class MongoidTest < MiniTest::Spec
36
- class TuneForm < Reform::Form
37
- include Reform::Form::Mongoid
38
- model :tune
39
-
40
- property :title
41
- property :created_at
42
-
43
- validates_uniqueness_of :title, scope: [:disc_id, :musician_id]
44
- validates :created_at, :presence => true # have another property to test if we mix up.
45
-
46
- property :musician do
47
- property :name
48
- validates_uniqueness_of :name # this currently also tests if Form::AR is included as a feature.
49
- end
50
- end
51
-
52
- let(:disc) { Disc.create(:title => "Damnation") }
53
- let(:musician) { Musician.create(:name => "Opeth") }
54
- let(:form) { TuneForm.new(Tune.new(:musician => Musician.new)) }
55
-
56
- it { form.class.i18n_scope.must_equal :mongoid }
57
-
58
- it "allows accessing the database" do
59
- end
60
-
61
- # uniqueness
62
- it "has no errors on title when title is unique for the same musician and disc" do
63
- form.validate("title" => "The Gargoyle", "musician_id" => musician.id, "disc" => disc.id, "created_at" => "November 6, 1966")
64
- assert_empty form.errors[:title]
65
- end
66
-
67
- it "has errors on title when title is taken for the same musician and disc" do
68
- skip "replace ActiveModel::Validations with our own, working and reusable gem."
69
- Tune.create(title: "Windowpane", musician_id: musician.id, disc_id: disc.id)
70
- form.validate("title" => "Windowpane", "musician_id" => musician.id, "disc" => disc)
71
- refute_empty form.errors[:title]
72
- end
73
-
74
- # nested object taken.
75
- it "is valid when musician name is unique" do
76
- form.validate("musician" => {"name" => "Paul Gilbert"}, "title" => "The Gargoyle", "created_at" => "November 6, 1966").must_equal true
77
- end
78
-
79
- it "is invalid and shows error when taken" do
80
- Tune.delete_all
81
- Musician.create(:name => "Racer X")
82
-
83
- form.validate("musician" => {"name" => "Racer X"}, "title" => "Ghost Inside My Skin").must_equal false
84
- form.errors.messages.must_equal({:"musician.name"=>["is already taken"], :created_at => ["can't be blank"]})
85
- end
86
-
87
- it "works with Composition" do
88
- form = Class.new(Reform::Form) do
89
- include Reform::Form::Mongoid
90
- include Reform::Form::Composition
91
-
92
- property :name, :on => :musician
93
- validates_uniqueness_of :name
94
- end.new(:musician => Musician.new)
95
-
96
- Musician.create(:name => "Bad Religion")
97
- form.validate("name" => "Bad Religion").must_equal false
98
- end
99
-
100
- describe "#save" do
101
- # TODO: test 1-n?
102
- it "calls model.save" do
103
- Musician.delete_all
104
- form.validate("musician" => {"name" => "Bad Religion"}, "title" => "Ghost Inside My Skin")
105
- form.save
106
- Musician.where(:name => "Bad Religion").size.must_equal 1
107
- end
108
-
109
- it "doesn't call model.save when block is given" do
110
- Musician.delete_all
111
- form.validate("name" => "Bad Religion")
112
- form.save {}
113
- Musician.where(:name => "Bad Religion").size.must_equal 0
114
- end
115
- end
116
- end
117
-
118
-
119
- class PopulateWithActiveRecordTest < MiniTest::Spec
120
- class DiscForm < Reform::Form
121
-
122
- property :title
123
-
124
- collection :tunes, :populate_if_empty => Tune do
125
- property :title
126
- end
127
- end
128
-
129
- let (:disc) { Disc.new(:tunes => []) }
130
- it do
131
- form = DiscForm.new(disc)
132
-
133
- form.validate("tunes" => [{"title" => "Straight From The Jacket"}])
134
-
135
- # form populated.
136
- form.tunes.size.must_equal 1
137
- form.tunes[0].model.must_be_kind_of Tune
138
-
139
- # model NOT populated.
140
- disc.tunes.must_equal []
141
-
142
-
143
- form.sync
144
-
145
- # form populated.
146
- form.tunes.size.must_equal 1
147
- form.tunes[0].model.must_be_kind_of Tune
148
-
149
- # model also populated.
150
- tune = disc.tunes[0]
151
- disc.tunes.must_equal [tune]
152
- tune.title.must_equal "Straight From The Jacket"
153
-
154
-
155
- # if ActiveRecord::VERSION::STRING !~ /^3.0/
156
- # # saving saves association.
157
- # form.save
158
- #
159
- # disc.reload
160
- # tune = disc.tunes[0]
161
- # disc.tunes.must_equal [tune]
162
- # tune.title.must_equal "Straight From The Jacket"
163
- # end
164
- end
165
-
166
-
167
- describe "modifying 1., adding 2." do
168
- let (:tune) { Tune.new(:title => "Part 2") }
169
- let (:disc) { Disc.create.tap { |a| a.tunes << tune } }
170
-
171
- it do
172
- form = DiscForm.new(disc)
173
-
174
- id = disc.tunes[0].id
175
- disc.tunes[0].persisted?.must_equal true
176
- assert id.to_s.size > 0
177
-
178
- form.validate("tunes" => [{"title" => "Part Two"}, {"title" => "Check For A Pulse"}])
179
-
180
- # form populated.
181
- form.tunes.size.must_equal 2
182
- form.tunes[0].model.must_be_kind_of Tune
183
- form.tunes[1].model.must_be_kind_of Tune
184
-
185
- # model NOT populated.
186
- disc.tunes.must_equal [tune]
187
-
188
-
189
- form.sync
190
-
191
- # form populated.
192
- form.tunes.size.must_equal 2
193
-
194
- # model also populated.
195
- disc.tunes.size.must_equal 2
196
-
197
- # corrected title
198
- disc.tunes[0].title.must_equal "Part Two"
199
- # ..but same tune.
200
- disc.tunes[0].id.must_equal id
201
-
202
- # and a new tune.
203
- disc.tunes[1].title.must_equal "Check For A Pulse"
204
- disc.tunes[1].persisted?.must_equal true # TODO: with << strategy, this shouldn't be saved.
205
- end
206
-
207
- describe 'using nested_models_attributes to modify nested collection' do
208
- class ActiveModelDiscForm < Reform::Form
209
- include Reform::Form::ActiveModel
210
- include Reform::Form::ActiveModel::FormBuilderMethods
211
-
212
- property :title
213
-
214
- collection :tunes, :populate_if_empty => Tune do
215
- property :title
216
- end
217
- end
218
-
219
- let (:disc) { Disc.create(:title => 'Greatest Hits') }
220
- let (:form) { ActiveModelDiscForm.new(disc) }
221
-
222
- it do
223
- form.validate('tunes_attributes' => {'0' => {'title' => 'Tango'}})
224
-
225
- # form populated.
226
- form.tunes.size.must_equal 1
227
- form.tunes[0].model.must_be_kind_of Tune
228
- form.tunes[0].title.must_equal 'Tango'
229
-
230
- # model NOT populated.
231
- disc.tunes.must_equal []
232
-
233
- form.save
234
-
235
- # nested model persisted.
236
- first_tune = disc.tunes[0]
237
- first_tune.persisted?.must_equal true
238
- assert first_tune.id.to_s.size > 0
239
-
240
- # form populated.
241
- form.tunes.size.must_equal 1
242
-
243
- # model also populated.
244
- disc.tunes.size.must_equal 1
245
- disc.tunes[0].title.must_equal 'Tango'
246
-
247
- form = ActiveModelDiscForm.new(disc)
248
- form.validate('tunes_attributes' => {'0' => {'id' => first_tune.id, 'title' => 'Tango nuevo'}, '1' => {'title' => 'Waltz'}})
249
-
250
- # form populated.
251
- form.tunes.size.must_equal 2
252
- form.tunes[0].model.must_be_kind_of Tune
253
- form.tunes[1].model.must_be_kind_of Tune
254
- form.tunes[0].title.must_equal 'Tango nuevo'
255
- form.tunes[1].title.must_equal 'Waltz'
256
-
257
- # model NOT populated.
258
- disc.tunes.size.must_equal 1
259
- disc.tunes[0].title.must_equal 'Tango'
260
-
261
- form.save
262
-
263
- # form populated.
264
- form.tunes.size.must_equal 2
265
-
266
- # model also populated.
267
- disc.tunes.size.must_equal 2
268
- disc.tunes[0].id.must_equal first_tune.id
269
- disc.tunes[0].persisted?.must_equal true
270
- disc.tunes[1].persisted?.must_equal true
271
- disc.tunes[0].title.must_equal 'Tango nuevo'
272
- disc.tunes[1].title.must_equal 'Waltz'
273
- end
274
- end
275
- end
276
-
277
- # it do
278
- # a=Disc.new
279
- # a.tunes << Tune.new(title: "Old What's His Name") # Tune does not get persisted.
280
-
281
- # a.tunes[1] = Tune.new(title: "Permanent Rust")
282
-
283
- # puts "@@@"
284
- # puts a.tunes.inspect
285
-
286
- # puts "---"
287
- # a.save
288
- # puts a.tunes.inspect
289
-
290
- # b = a.tunes.first
291
-
292
- # a.tunes = [Tune.new(title:"Biomag")]
293
- # puts "\\\\"
294
- # a.save
295
- # a.reload
296
- # puts a.tunes.inspect
297
-
298
- # b.reload
299
- # puts "#{b.inspect}, #{b.persisted?}"
300
-
301
-
302
- # a.tunes = [a.tunes.first, Tune.new(title: "Count Down")]
303
- # b = a.tunes.first
304
- # puts ":::::"
305
- # a.save
306
- # a.reload
307
- # puts a.tunes.inspect
308
-
309
- # b.reload
310
- # puts "#{b.inspect}, #{b.persisted?}"
311
- # end
312
- end
313
- end