reform 1.0.4 → 1.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.
@@ -1,28 +1,32 @@
1
1
  require 'test_helper'
2
2
 
3
3
  class FormCompositionTest < MiniTest::Spec
4
- Song = Struct.new(:id, :title)
4
+ Song = Struct.new(:id, :title, :band)
5
5
  Requester = Struct.new(:id, :name, :requester)
6
+ Band = Struct.new(:title)
6
7
 
7
8
  class RequestForm < Reform::Form
8
9
  include Composition
9
10
 
10
- # TODO: remove skip_accessors in 1.1.
11
- property :name, :on => :requester, :skip_accessors => true
12
- property :requester_id, :on => :requester, :as => :id, :skip_accessors => true
11
+ property :name, :on => :requester
12
+ property :requester_id, :on => :requester, :as => :id
13
13
  properties [:title, :id], :on => :song
14
14
  # property :channel # FIXME: what about the "main model"?
15
15
  property :channel, :empty => true, :on => :song
16
- property :requester, :on => :requester, :skip_accessors => true
16
+ property :requester, :on => :requester
17
17
  property :captcha, :on => :song, :empty => true
18
18
 
19
19
  validates :name, :title, :channel, :presence => true
20
- end
21
20
 
22
- let (:form) { RequestForm.new(:song => song, :requester => requester) }
23
- let (:song) { Song.new(1, "Rio") }
24
- let (:requester) { Requester.new(2, "Duran Duran", "MCP") }
21
+ property :band, :on => :song do
22
+ property :title
23
+ end
24
+ end
25
25
 
26
+ let (:form) { RequestForm.new(:song => song, :requester => requester) }
27
+ let (:song) { Song.new(1, "Rio", band) }
28
+ let (:requester) { Requester.new(2, "Duran Duran", "MCP") }
29
+ let (:band) { Band.new("Duran^2") }
26
30
 
27
31
  # delegation form -> composition works
28
32
  it { form.id.must_equal 1 }
@@ -33,11 +37,6 @@ class FormCompositionTest < MiniTest::Spec
33
37
  it { form.requester.must_equal "MCP" } # same name as composed model.
34
38
  it { form.captcha.must_equal nil }
35
39
 
36
- # [DEPRECATED] # TODO: remove in 1.2.
37
- # delegation form -> composed models (e.g. when saving this can be handy)
38
- it { form.song.must_equal song }
39
-
40
-
41
40
  # #model just returns <Composition>.
42
41
  it { form.model.must_be_kind_of Reform::Composition }
43
42
 
@@ -51,12 +50,13 @@ class FormCompositionTest < MiniTest::Spec
51
50
  end
52
51
 
53
52
  describe "#save" do
54
- it "provides data block argument" do
53
+ # #save with {}
54
+ it do
55
55
  hash = {}
56
56
 
57
- form.save do |data, map|
58
- hash[:name] = data.name
59
- hash[:title] = data.title
57
+ form.save do |map|
58
+ hash[:name] = form.name
59
+ hash[:title] = form.title
60
60
  end
61
61
 
62
62
  hash.must_equal({:name=>"Duran Duran", :title=>"Rio"})
@@ -65,21 +65,23 @@ class FormCompositionTest < MiniTest::Spec
65
65
  it "provides nested symbolized hash as second block argument" do
66
66
  form.validate("title" => "Greyhound", "name" => "Frenzal Rhomb", "channel" => "JJJ", "captcha" => "wonderful")
67
67
 
68
- hash = {}
68
+ hash = nil
69
69
 
70
- form.save do |data, map|
70
+ form.save do |map|
71
71
  hash = map
72
72
  end
73
73
 
74
74
  hash.must_equal({
75
- :song=>{:title=>"Greyhound", :id=>1, :channel => "JJJ", :captcha=>"wonderful"},
76
- :requester=>{:name=>"Frenzal Rhomb", :id=>2, :requester => "MCP"}}
75
+ :song=>{:title=>"Greyhound", :id=>1, :channel => "JJJ", :captcha=>"wonderful", :band=>{"title"=>"Duran^2"}},
76
+ :requester=>{:name=>"Frenzal Rhomb", :id=>2, :requester => "MCP"}
77
+ }
77
78
  )
78
79
  end
79
80
 
80
81
  it "pushes data to models and calls #save when no block passed" do
81
82
  song.extend(Saveable)
82
83
  requester.extend(Saveable)
84
+ band.extend(Saveable)
83
85
 
84
86
  form.validate("title" => "Greyhound", "name" => "Frenzal Rhomb", "captcha" => "1337")
85
87
  form.captcha.must_equal "1337" # TODO: move to separate test.
@@ -90,6 +92,32 @@ class FormCompositionTest < MiniTest::Spec
90
92
  requester.saved?.must_equal true
91
93
  song.title.must_equal "Greyhound"
92
94
  song.saved?.must_equal true
95
+ song.band.title.must_equal "Duran^2"
96
+ song.band.saved?.must_equal true
93
97
  end
94
98
  end
95
99
  end
100
+
101
+
102
+ class FormCompositionCollectionTest < MiniTest::Spec
103
+ Book = Struct.new(:id, :name)
104
+ Library = Struct.new(:id) do
105
+ def books
106
+ [Book.new(1,"My book")]
107
+ end
108
+ end
109
+
110
+ class LibraryForm < Reform::Form
111
+ include Reform::Form::Composition
112
+
113
+ collection :books, on: :library do
114
+ property :id
115
+ property :name
116
+ end
117
+ end
118
+
119
+ let (:form) { LibraryForm.new(library: library) }
120
+ let (:library) { Library.new(2) }
121
+
122
+ it { form.save do |hash| hash.must_equal({:library=>{:books=>[{"id"=>1, "name"=>"My book"}]}}) end }
123
+ end
@@ -0,0 +1,79 @@
1
+ require 'test_helper'
2
+ require 'representable/json'
3
+
4
+ class InheritTest < BaseTest
5
+ class CompilationForm < AlbumForm
6
+
7
+ property :hit, :inherit => true do
8
+ property :rating
9
+ validates :title, :rating, :presence => true
10
+ end
11
+
12
+ # puts representer_class.representable_attrs.
13
+ # get(:hit)[:extend].evaluate(nil).new(OpenStruct.new).rating
14
+ end
15
+
16
+ let (:album) { Album.new(nil, OpenStruct.new(:hit => OpenStruct.new()) ) }
17
+ subject { CompilationForm.new(album) }
18
+
19
+
20
+ # valid.
21
+ it {
22
+ subject.validate("hit" => {"title" => "LA Drone", "rating" => 10})
23
+ subject.hit.title.must_equal "LA Drone"
24
+ subject.hit.rating.must_equal 10
25
+ subject.errors.messages.must_equal({})
26
+ }
27
+
28
+ it do
29
+ subject.validate({})
30
+ subject.hit.title.must_equal nil
31
+ subject.hit.rating.must_equal nil
32
+ subject.errors.messages.must_equal({:"hit.title"=>["can't be blank"], :"hit.rating"=>["can't be blank"]})
33
+ end
34
+ end
35
+
36
+
37
+ class ModuleInclusionTest < MiniTest::Spec
38
+ module BandPropertyForm
39
+ include Reform::Form::Module
40
+
41
+ property :band do
42
+ property :title
43
+
44
+ validates :title, :presence => true
45
+
46
+ def id # gets mixed into Form, too.
47
+ 2
48
+ end
49
+ end
50
+
51
+ def id # gets mixed into Form, too.
52
+ 1
53
+ end
54
+
55
+ validates :band, :presence => true
56
+ end
57
+
58
+
59
+ class SongForm < Reform::Form
60
+ property :title
61
+
62
+ include BandPropertyForm
63
+ end
64
+
65
+ let (:song) { OpenStruct.new(:band => OpenStruct.new(:title => "Time Again")) }
66
+
67
+ # nested form from module is present and creates accessor.
68
+ it { SongForm.new(song).band.title.must_equal "Time Again" }
69
+
70
+ # methods from module get included.
71
+ it { SongForm.new(song).id.must_equal 1 }
72
+ it { SongForm.new(song).band.id.must_equal 2 }
73
+
74
+ it do
75
+ form = SongForm.new(OpenStruct.new())
76
+ form.validate({})
77
+ form.errors.messages.must_equal({:band=>["can't be blank"]})
78
+ end
79
+ end
@@ -0,0 +1,82 @@
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, as: :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, as: :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
@@ -39,7 +39,7 @@ class NestedFormTest < MiniTest::Spec
39
39
 
40
40
  it "responds to #save" do
41
41
  hsh = nil
42
- form.save do |f, nested|
42
+ form.save do |nested|
43
43
  hsh = nested
44
44
  end
45
45
  hsh.must_equal({"hit"=>{"title"=>"Downtown"}, "title" => "Blackhawks Over Los Angeles", "songs"=>[{"title"=>"Calling"}]})
@@ -103,21 +103,10 @@ class NestedFormTest < MiniTest::Spec
103
103
  data.must_equal(:title=>"Second Heat", :hit_title => "Sacrifice", :first_title => "Scarified")
104
104
  end
105
105
 
106
- it "passes form instances in first argument" do
107
- frm = nil
108
-
109
- form.save { |f, hsh| frm = f }
110
-
111
- frm.must_equal form
112
- frm.title.must_be_kind_of String
113
- frm.hit.must_be_kind_of Reform::Form
114
- frm.songs.first.must_be_kind_of Reform::Form
115
- end
116
-
117
106
  it "returns nested hash with indifferent access" do
118
107
  nested = nil
119
108
 
120
- form.save do |hash, nested_hash|
109
+ form.save do |nested_hash|
121
110
  nested = nested_hash
122
111
  end
123
112
 
@@ -204,7 +204,7 @@ class ReformTest < ReformSpec
204
204
  end
205
205
 
206
206
  describe "#save with block" do
207
- it "provides data block argument" do
207
+ it "Deprecated: provides data block argument" do # TODO: remove in 1.1.
208
208
  hash = {}
209
209
 
210
210
  form.save do |data, map|
@@ -215,7 +215,7 @@ class ReformTest < ReformSpec
215
215
  hash.must_equal({:name=>"Diesel Boy", :title=>nil})
216
216
  end
217
217
 
218
- it "provides nested symbolized hash as second block argument" do
218
+ it "Deprecated: provides nested symbolized hash as second block argument" do # TODO: remove in 1.1.
219
219
  hash = {}
220
220
 
221
221
  form.save do |data, map|
@@ -224,6 +224,16 @@ class ReformTest < ReformSpec
224
224
 
225
225
  hash.must_equal({"name"=>"Diesel Boy"})
226
226
  end
227
+
228
+ it do
229
+ hash = {}
230
+
231
+ form.save do |map|
232
+ hash = map
233
+ end
234
+
235
+ hash.must_equal({"name"=>"Diesel Boy"})
236
+ end
227
237
  end
228
238
  end
229
239
 
@@ -239,7 +249,7 @@ class ReformTest < ReformSpec
239
249
  validates :position, :presence => true
240
250
  end
241
251
 
242
- let (:form) { HitForm.new(OpenStruct.new) }
252
+ let (:form) { HitForm.new(OpenStruct.new()) }
243
253
  it do
244
254
  form.validate({"title" => "The Body"})
245
255
  form.title.must_equal "The Body"
@@ -247,8 +257,73 @@ class ReformTest < ReformSpec
247
257
  form.errors.messages.must_equal({:name=>["can't be blank"], :position=>["can't be blank"]})
248
258
  end
249
259
  end
260
+
261
+ describe "#column_for_attribute" do
262
+ let (:model) { Artist.new }
263
+ let (:klass) do
264
+ require 'reform/active_record'
265
+
266
+ Class.new Reform::Form do
267
+ include Reform::Form::ActiveRecord
268
+
269
+ model :artist
270
+
271
+ property :name
272
+ end
273
+ end
274
+ let (:form) { klass.new(model) }
275
+
276
+ it 'should delegate to the model' do
277
+ Calls = []
278
+
279
+ def model.column_for_attribute(*args)
280
+ Calls << [:column_for_attribute, *args]
281
+ end
282
+
283
+ form.column_for_attribute(:name)
284
+ Calls.must_include [:column_for_attribute, :name]
285
+ end
286
+ end
287
+
288
+ describe "#column_for_attribute with composition" do
289
+ let (:artist_model) { Artist.new }
290
+ let (:song_model) { Song.new }
291
+ let (:form_klass) do
292
+ require 'reform/active_record'
293
+
294
+ Class.new Reform::Form do
295
+ include Reform::Form::ActiveRecord
296
+ include Reform::Form::Composition
297
+
298
+ model :artist
299
+
300
+ property :name, on: :artist
301
+ property :title, on: :song
302
+ end
303
+ end
304
+ let (:form) { form_klass.new(artist: artist_model, song: song_model) }
305
+
306
+ it 'should delegate to the model' do
307
+ ArtistCalls, SongCalls = [], []
308
+
309
+ def artist_model.column_for_attribute(*args)
310
+ ArtistCalls << [:column_for_attribute, *args]
311
+ end
312
+
313
+ def song_model.column_for_attribute(*args)
314
+ SongCalls << [:column_for_attribute, *args]
315
+ end
316
+
317
+ form.column_for_attribute(:name)
318
+ ArtistCalls.must_include [:column_for_attribute, :name]
319
+
320
+ form.column_for_attribute(:title)
321
+ SongCalls.must_include [:column_for_attribute, :title]
322
+ end
323
+ end
250
324
  end
251
325
 
326
+
252
327
  class EmptyAttributesTest < MiniTest::Spec
253
328
  Credentials = Struct.new(:password)
254
329
 
@@ -270,7 +345,7 @@ class EmptyAttributesTest < MiniTest::Spec
270
345
  cred.password.must_equal "123"
271
346
 
272
347
  hash = {}
273
- form.save do |f, nested|
348
+ form.save do |nested|
274
349
  hash = nested
275
350
  end
276
351
 
@@ -298,7 +373,7 @@ class ReadonlyAttributesTest < MiniTest::Spec
298
373
  loc.country.must_equal "Australia" # the writer wasn't called.
299
374
 
300
375
  hash = {}
301
- form.save do |f, nested|
376
+ form.save do |nested|
302
377
  hash = nested
303
378
  end
304
379
 
@@ -335,7 +410,7 @@ class OverridingAccessorsTest < BaseTest
335
410
 
336
411
  # the reader is not used when saving/syncing.
337
412
  it do
338
- subject.save do |f, hash|
413
+ subject.save do |hash|
339
414
  hash["title"].must_equal "Hey Little WorldHey Little World"
340
415
  end
341
416
  end
@@ -347,4 +422,28 @@ class OverridingAccessorsTest < BaseTest
347
422
  song.title.must_equal "Hey Little WorldHey Little World"
348
423
  end
349
424
  end
350
- end
425
+ end
426
+
427
+
428
+ class MethodInFormTest < MiniTest::Spec
429
+ class AlbumForm < Reform::Form
430
+ property :title
431
+
432
+ def title
433
+ "The Suffer And The Witness"
434
+ end
435
+
436
+ property :hit do
437
+ property :title
438
+
439
+ def title
440
+ "Drones"
441
+ end
442
+ end
443
+ end
444
+
445
+ # methods can be used instead of created accessors.
446
+ subject { AlbumForm.new(OpenStruct.new(:hit => OpenStruct.new)) }
447
+ it { subject.title.must_equal "The Suffer And The Witness" }
448
+ it { subject.hit.title.must_equal "Drones" }
449
+ end