reform 2.2.4 → 2.3.3
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.
- checksums.yaml +5 -5
- data/.gitignore +5 -1
- data/.travis.yml +11 -6
- data/Appraisals +8 -0
- data/CHANGES.md +57 -4
- data/CONTRIBUTING.md +31 -0
- data/Gemfile +2 -16
- data/ISSUE_TEMPLATE.md +25 -0
- data/LICENSE.txt +1 -1
- data/README.md +5 -7
- data/Rakefile +16 -9
- data/gemfiles/0.13.0.gemfile +8 -0
- data/gemfiles/1.5.0.gemfile +9 -0
- data/lib/reform.rb +1 -0
- data/lib/reform/contract.rb +7 -17
- data/lib/reform/contract/custom_error.rb +41 -0
- data/lib/reform/contract/validate.rb +53 -23
- data/lib/reform/errors.rb +61 -0
- data/lib/reform/form.rb +36 -10
- data/lib/reform/form/call.rb +1 -1
- data/lib/reform/form/composition.rb +2 -2
- data/lib/reform/form/dry.rb +10 -58
- data/lib/reform/form/dry/input_hash.rb +37 -0
- data/lib/reform/form/dry/new_api.rb +45 -0
- data/lib/reform/form/dry/old_api.rb +61 -0
- data/lib/reform/form/populator.rb +11 -27
- data/lib/reform/form/prepopulate.rb +4 -3
- data/lib/reform/form/validate.rb +28 -13
- data/lib/reform/result.rb +90 -0
- data/lib/reform/validation.rb +19 -11
- data/lib/reform/validation/groups.rb +12 -27
- data/lib/reform/version.rb +1 -1
- data/reform.gemspec +14 -13
- data/test/benchmarking.rb +39 -6
- data/test/call_new_api.rb +23 -0
- data/test/call_old_api.rb +23 -0
- data/test/changed_test.rb +14 -14
- data/test/coercion_test.rb +57 -25
- data/test/composition_new_api.rb +186 -0
- data/test/composition_old_api.rb +184 -0
- data/test/contract/custom_error_test.rb +55 -0
- data/test/contract_new_api.rb +77 -0
- data/test/contract_old_api.rb +77 -0
- data/test/default_test.rb +4 -4
- data/test/deserialize_test.rb +17 -20
- data/test/errors_new_api.rb +225 -0
- data/test/errors_old_api.rb +230 -0
- data/test/feature_test.rb +10 -12
- data/test/fixtures/dry_error_messages.yml +73 -23
- data/test/fixtures/dry_new_api_error_messages.yml +104 -0
- data/test/form_new_api.rb +57 -0
- data/test/{form_test.rb → form_old_api.rb} +8 -8
- data/test/form_option_new_api.rb +24 -0
- data/test/{form_option_test.rb → form_option_old_api.rb} +5 -5
- data/test/from_test.rb +18 -22
- data/test/inherit_new_api.rb +105 -0
- data/test/inherit_old_api.rb +105 -0
- data/test/{module_test.rb → module_new_api.rb} +26 -31
- data/test/module_old_api.rb +146 -0
- data/test/parse_option_test.rb +40 -0
- data/test/parse_pipeline_test.rb +4 -4
- data/test/populate_new_api.rb +304 -0
- data/test/populate_old_api.rb +304 -0
- data/test/populator_skip_test.rb +11 -11
- data/test/prepopulator_test.rb +23 -24
- data/test/read_only_test.rb +12 -1
- data/test/readable_test.rb +9 -9
- data/test/reform_new_api.rb +204 -0
- data/test/{reform_test.rb → reform_old_api.rb} +44 -65
- data/test/save_new_api.rb +101 -0
- data/test/save_old_api.rb +101 -0
- data/test/setup_test.rb +17 -17
- data/test/skip_if_new_api.rb +85 -0
- data/test/skip_if_old_api.rb +92 -0
- data/test/skip_setter_and_getter_test.rb +9 -10
- data/test/test_helper.rb +25 -14
- data/test/validate_new_api.rb +453 -0
- data/test/{validate_test.rb → validate_old_api.rb} +121 -131
- data/test/validation/dry_validation_new_api.rb +835 -0
- data/test/validation/dry_validation_old_api.rb +772 -0
- data/test/validation/result_test.rb +77 -0
- data/test/validation_library_provided_test.rb +16 -0
- data/test/virtual_test.rb +47 -7
- data/test/writeable_test.rb +38 -9
- metadata +111 -56
- data/gemfiles/Gemfile.disposable-0.3 +0 -6
- data/lib/reform/contract/errors.rb +0 -43
- data/lib/reform/form/mongoid.rb +0 -37
- data/lib/reform/form/orm.rb +0 -26
- data/lib/reform/mongoid.rb +0 -4
- data/test/call_test.rb +0 -23
- data/test/composition_test.rb +0 -149
- data/test/contract_test.rb +0 -77
- data/test/deprecation_test.rb +0 -27
- data/test/errors_test.rb +0 -165
- data/test/inherit_test.rb +0 -119
- data/test/populate_test.rb +0 -270
- data/test/readonly_test.rb +0 -14
- data/test/save_test.rb +0 -89
- data/test/skip_if_test.rb +0 -74
- data/test/validation/dry_test.rb +0 -60
- data/test/validation/dry_validation_test.rb +0 -352
- data/test/validation/errors.yml +0 -4
@@ -1,43 +0,0 @@
|
|
1
|
-
class Reform::Contract::Errors
|
2
|
-
def initialize(*)
|
3
|
-
@errors = {}
|
4
|
-
end
|
5
|
-
|
6
|
-
module Merge
|
7
|
-
def merge!(errors, prefix)
|
8
|
-
errors.messages.each do |field, msgs|
|
9
|
-
unless field.to_sym == :base
|
10
|
-
field = (prefix+[field]).join(".").to_sym # TODO: why is that a symbol in Rails?
|
11
|
-
end
|
12
|
-
|
13
|
-
msgs.each do |msg|
|
14
|
-
next if messages[field] and messages[field].include?(msg)
|
15
|
-
add(field, msg)
|
16
|
-
end # Forms now contains a plain errors hash. the errors for each item are still available in item.errors.
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
def to_s
|
21
|
-
messages.inspect
|
22
|
-
end
|
23
|
-
end
|
24
|
-
include Merge
|
25
|
-
|
26
|
-
def add(field, message)
|
27
|
-
@errors[field] ||= []
|
28
|
-
@errors[field] << message
|
29
|
-
end
|
30
|
-
|
31
|
-
def messages
|
32
|
-
@errors
|
33
|
-
end
|
34
|
-
|
35
|
-
def empty?
|
36
|
-
@errors.empty?
|
37
|
-
end
|
38
|
-
|
39
|
-
# needed by Rails form builder.
|
40
|
-
def [](name)
|
41
|
-
@errors[name] || []
|
42
|
-
end
|
43
|
-
end
|
data/lib/reform/form/mongoid.rb
DELETED
@@ -1,37 +0,0 @@
|
|
1
|
-
module Reform::Form::Mongoid
|
2
|
-
def self.included(base)
|
3
|
-
base.class_eval do
|
4
|
-
register_feature Reform::Form::Mongoid
|
5
|
-
include Reform::Form::ActiveModel
|
6
|
-
include Reform::Form::ORM
|
7
|
-
extend ClassMethods
|
8
|
-
end
|
9
|
-
end
|
10
|
-
|
11
|
-
module ClassMethods
|
12
|
-
def validates_uniqueness_of(attribute, options={})
|
13
|
-
options = options.merge(:attributes => [attribute])
|
14
|
-
validates_with(UniquenessValidator, options)
|
15
|
-
end
|
16
|
-
def i18n_scope
|
17
|
-
:mongoid
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
|
22
|
-
def self.mongoid_namespace
|
23
|
-
if mongoid_is_4_or_more?
|
24
|
-
'Validatable'
|
25
|
-
else
|
26
|
-
'Validations'
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
def self.mongoid_is_4_or_more?
|
31
|
-
Mongoid::VERSION.split('.').first.to_i >= 4
|
32
|
-
end
|
33
|
-
|
34
|
-
UniquenessValidator = Class.new("::Mongoid::#{mongoid_namespace}::UniquenessValidator".constantize) do
|
35
|
-
include Reform::Form::ORM::UniquenessValidator
|
36
|
-
end
|
37
|
-
end
|
data/lib/reform/form/orm.rb
DELETED
@@ -1,26 +0,0 @@
|
|
1
|
-
module Reform::Form::ORM
|
2
|
-
def model_for_property(name)
|
3
|
-
return model unless is_a?(Reform::Form::Composition) # i am too lazy for proper inheritance. there should be a ActiveRecord::Composition that handles this.
|
4
|
-
|
5
|
-
model_name = options_for(name)[:on]
|
6
|
-
model[model_name]
|
7
|
-
end
|
8
|
-
|
9
|
-
module UniquenessValidator
|
10
|
-
# when calling validates it should create the Vali instance already and set @klass there! # TODO: fix this in AM.
|
11
|
-
def validate(form)
|
12
|
-
property = attributes.first
|
13
|
-
|
14
|
-
# here is the thing: why does AM::UniquenessValidator require a filled-out record to work properly? also, why do we need to set
|
15
|
-
# the class? it would be way easier to pass #validate a hash of attributes and get back an errors hash.
|
16
|
-
# the class for the finder could either be infered from the record or set in the validator instance itself in the call to ::validates.
|
17
|
-
record = form.model_for_property(property)
|
18
|
-
record.send("#{property}=", form.send(property))
|
19
|
-
|
20
|
-
@klass = record.class # this is usually done in the super-sucky #setup method.
|
21
|
-
super(record).tap do |res|
|
22
|
-
form.errors.add(property, record.errors.first.last) if record.errors.present?
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
data/lib/reform/mongoid.rb
DELETED
data/test/call_test.rb
DELETED
@@ -1,23 +0,0 @@
|
|
1
|
-
require "test_helper"
|
2
|
-
|
3
|
-
class CallTest < Minitest::Spec
|
4
|
-
Song = Struct.new(:title)
|
5
|
-
|
6
|
-
class SongForm < Reform::Form
|
7
|
-
property :title
|
8
|
-
|
9
|
-
validation do
|
10
|
-
key(:title).required
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
let (:form) { SongForm.new(Song.new) }
|
15
|
-
|
16
|
-
it { form.(title: "True North").success?.must_equal true }
|
17
|
-
it { form.(title: "True North").failure?.must_equal false }
|
18
|
-
it { form.(title: "").success?.must_equal false }
|
19
|
-
it { form.(title: "").failure?.must_equal true }
|
20
|
-
|
21
|
-
it { form.(title: "True North").errors.messages.must_equal({}) }
|
22
|
-
it { form.(title: "").errors.messages.must_equal({:title=>["must be filled"]}) }
|
23
|
-
end
|
data/test/composition_test.rb
DELETED
@@ -1,149 +0,0 @@
|
|
1
|
-
require 'test_helper'
|
2
|
-
|
3
|
-
class FormCompositionTest < MiniTest::Spec
|
4
|
-
Song = Struct.new(:id, :title, :band)
|
5
|
-
Requester = Struct.new(:id, :name, :requester)
|
6
|
-
Band = Struct.new(:title)
|
7
|
-
|
8
|
-
class RequestForm < Reform::Form
|
9
|
-
include Composition
|
10
|
-
|
11
|
-
property :name, :on => :requester
|
12
|
-
property :requester_id, :on => :requester, :from => :id
|
13
|
-
properties :title, :id, :on => :song
|
14
|
-
# 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
|
18
|
-
|
19
|
-
validation do
|
20
|
-
key(:name).required
|
21
|
-
key(:name).required
|
22
|
-
key(:title).required
|
23
|
-
end
|
24
|
-
|
25
|
-
property :band, :on => :song do
|
26
|
-
property :title
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
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") }
|
34
|
-
|
35
|
-
# delegation form -> composition works
|
36
|
-
it { form.id.must_equal 1 }
|
37
|
-
it { form.title.must_equal "Rio" }
|
38
|
-
it { form.name.must_equal "Duran Duran" }
|
39
|
-
it { form.requester_id.must_equal 2 }
|
40
|
-
it { form.channel.must_equal nil }
|
41
|
-
it { form.requester.must_equal "MCP" } # same name as composed model.
|
42
|
-
it { form.captcha.must_equal nil }
|
43
|
-
|
44
|
-
# #model just returns <Composition>.
|
45
|
-
it { form.mapper.must_be_kind_of Disposable::Composition }
|
46
|
-
|
47
|
-
# #model[] -> composed models
|
48
|
-
it { form.model[:requester].must_equal requester }
|
49
|
-
it { form.model[:song].must_equal song }
|
50
|
-
|
51
|
-
|
52
|
-
it "creates Composition for you" do
|
53
|
-
form.validate("title" => "Greyhound", "name" => "Frenzal Rhomb").must_equal false
|
54
|
-
end
|
55
|
-
|
56
|
-
describe "#save" do
|
57
|
-
# #save with {}
|
58
|
-
it do
|
59
|
-
hash = {}
|
60
|
-
|
61
|
-
form.save do |map|
|
62
|
-
hash[:name] = form.name
|
63
|
-
hash[:title] = form.title
|
64
|
-
end
|
65
|
-
|
66
|
-
hash.must_equal({:name=>"Duran Duran", :title=>"Rio"})
|
67
|
-
end
|
68
|
-
|
69
|
-
it "provides nested symbolized hash as second block argument" do
|
70
|
-
form.validate("title" => "Greyhound", "name" => "Frenzal Rhomb", "channel" => "JJJ", "captcha" => "wonderful")
|
71
|
-
|
72
|
-
hash = nil
|
73
|
-
|
74
|
-
form.save do |map|
|
75
|
-
hash = map
|
76
|
-
end
|
77
|
-
|
78
|
-
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
|
-
}
|
82
|
-
)
|
83
|
-
end
|
84
|
-
|
85
|
-
it "xxx pushes data to models and calls #save when no block passed" do
|
86
|
-
song.extend(Saveable)
|
87
|
-
requester.extend(Saveable)
|
88
|
-
band.extend(Saveable)
|
89
|
-
|
90
|
-
form.validate("title" => "Greyhound", "name" => "Frenzal Rhomb", "captcha" => "1337")
|
91
|
-
form.captcha.must_equal "1337" # TODO: move to separate test.
|
92
|
-
|
93
|
-
form.save
|
94
|
-
|
95
|
-
requester.name.must_equal "Frenzal Rhomb"
|
96
|
-
requester.saved?.must_equal true
|
97
|
-
song.title.must_equal "Greyhound"
|
98
|
-
song.saved?.must_equal true
|
99
|
-
song.band.title.must_equal "Duran^2"
|
100
|
-
song.band.saved?.must_equal true
|
101
|
-
end
|
102
|
-
|
103
|
-
it "returns true when models all save successfully" do
|
104
|
-
song.extend(Saveable)
|
105
|
-
requester.extend(Saveable)
|
106
|
-
band.extend(Saveable)
|
107
|
-
|
108
|
-
form.save.must_equal true
|
109
|
-
end
|
110
|
-
|
111
|
-
it "returns false when one or more models don't save successfully" do
|
112
|
-
module Unsaveable
|
113
|
-
def save
|
114
|
-
false
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
song.extend(Unsaveable)
|
119
|
-
requester.extend(Saveable)
|
120
|
-
band.extend(Saveable)
|
121
|
-
|
122
|
-
form.save.must_equal false
|
123
|
-
end
|
124
|
-
end
|
125
|
-
end
|
126
|
-
|
127
|
-
|
128
|
-
class FormCompositionCollectionTest < MiniTest::Spec
|
129
|
-
Book = Struct.new(:id, :name)
|
130
|
-
Library = Struct.new(:id) do
|
131
|
-
def books
|
132
|
-
[Book.new(1,"My book")]
|
133
|
-
end
|
134
|
-
end
|
135
|
-
|
136
|
-
class LibraryForm < Reform::Form
|
137
|
-
include Reform::Form::Composition
|
138
|
-
|
139
|
-
collection :books, on: :library do
|
140
|
-
property :id
|
141
|
-
property :name
|
142
|
-
end
|
143
|
-
end
|
144
|
-
|
145
|
-
let (:form) { LibraryForm.new(library: library) }
|
146
|
-
let (:library) { Library.new(2) }
|
147
|
-
|
148
|
-
it { form.save do |hash| hash.must_equal({:library=>{"books"=>[{"id"=>1, "name"=>"My book"}]}}) end }
|
149
|
-
end
|
data/test/contract_test.rb
DELETED
@@ -1,77 +0,0 @@
|
|
1
|
-
require 'test_helper'
|
2
|
-
|
3
|
-
class ContractTest < MiniTest::Spec
|
4
|
-
Song = Struct.new(:title, :album, :composer)
|
5
|
-
Album = Struct.new(:name, :duration, :songs, :artist)
|
6
|
-
Artist = Struct.new(:name)
|
7
|
-
|
8
|
-
class ArtistForm < Reform::Form
|
9
|
-
property :name
|
10
|
-
end
|
11
|
-
|
12
|
-
class AlbumForm < Reform::Contract
|
13
|
-
property :name
|
14
|
-
|
15
|
-
properties :duration
|
16
|
-
properties :year, :style, readable: false
|
17
|
-
|
18
|
-
validation do
|
19
|
-
key(:name).required
|
20
|
-
end
|
21
|
-
|
22
|
-
collection :songs do
|
23
|
-
property :title
|
24
|
-
validation do
|
25
|
-
key(:title).required
|
26
|
-
end
|
27
|
-
|
28
|
-
property :composer do
|
29
|
-
property :name
|
30
|
-
validation do
|
31
|
-
key(:name).required
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
property :artist, form: ArtistForm
|
37
|
-
end
|
38
|
-
|
39
|
-
let (:song) { Song.new("Broken") }
|
40
|
-
let (:song_with_composer) { Song.new("Resist Stance", nil, composer) }
|
41
|
-
let (:composer) { Artist.new("Greg Graffin") }
|
42
|
-
let (:artist) { Artist.new("Bad Religion") }
|
43
|
-
let (:album) { Album.new("The Dissent Of Man", 123, [song, song_with_composer], artist) }
|
44
|
-
|
45
|
-
let (:form) { AlbumForm.new(album) }
|
46
|
-
|
47
|
-
# accept `property form: SongForm`.
|
48
|
-
it do
|
49
|
-
form.artist.must_be_instance_of ArtistForm
|
50
|
-
end
|
51
|
-
|
52
|
-
describe ".properties" do
|
53
|
-
it "defines a property when called with one argument" do
|
54
|
-
form.must_respond_to :duration
|
55
|
-
end
|
56
|
-
|
57
|
-
it "defines several properties when called with multiple arguments" do
|
58
|
-
form.must_respond_to :year
|
59
|
-
form.must_respond_to :style
|
60
|
-
end
|
61
|
-
|
62
|
-
it "passes options to each property when options are provided" do
|
63
|
-
readable = AlbumForm.new(album).options_for(:style)[:readable]
|
64
|
-
readable.must_equal false
|
65
|
-
end
|
66
|
-
|
67
|
-
it "returns the list of defined properties" do
|
68
|
-
returned_value = AlbumForm.properties(:hello, :world, virtual: true)
|
69
|
-
returned_value.must_equal [:hello, :world]
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
describe "#options_for" do
|
74
|
-
it { AlbumForm.options_for(:name).extend(Declarative::Inspect).inspect.must_equal "#<Disposable::Twin::Definition: @options={:private_name=>:name, :name=>\"name\"}>" }
|
75
|
-
it { AlbumForm.new(album).options_for(:name).extend(Declarative::Inspect).inspect.must_equal "#<Disposable::Twin::Definition: @options={:private_name=>:name, :name=>\"name\"}>" }
|
76
|
-
end
|
77
|
-
end
|
data/test/deprecation_test.rb
DELETED
@@ -1,27 +0,0 @@
|
|
1
|
-
require "test_helper"
|
2
|
-
|
3
|
-
|
4
|
-
class DeprecationRemoveMePopulatorTest < MiniTest::Spec
|
5
|
-
Album = Struct.new(:songs)
|
6
|
-
Song = Struct.new(:title)
|
7
|
-
|
8
|
-
|
9
|
-
class AlbumForm < Reform::Form
|
10
|
-
collection :songs, populator: ->(fragment, collection, index, *) { return Representable::Pipeline::Stop if fragment[:title]=="Good"
|
11
|
-
songs[index]
|
12
|
-
} do
|
13
|
-
property :title
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
it do
|
18
|
-
form = AlbumForm.new(Album.new([Song.new, Song.new]))
|
19
|
-
hash = {songs: [{title: "Good"}, {title: "Bad"}]}
|
20
|
-
|
21
|
-
form.validate(hash)
|
22
|
-
|
23
|
-
form.songs.size.must_equal 2
|
24
|
-
form.songs[0].title.must_equal nil
|
25
|
-
form.songs[1].title.must_equal "Bad"
|
26
|
-
end
|
27
|
-
end
|
data/test/errors_test.rb
DELETED
@@ -1,165 +0,0 @@
|
|
1
|
-
require "test_helper"
|
2
|
-
|
3
|
-
class ErrorsTest < MiniTest::Spec
|
4
|
-
class AlbumForm < Reform::Form
|
5
|
-
property :title
|
6
|
-
|
7
|
-
property :hit do
|
8
|
-
property :title
|
9
|
-
validation do
|
10
|
-
required(:title).filled
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
collection :songs do
|
15
|
-
property :title
|
16
|
-
validation do
|
17
|
-
required(:title).filled
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
property :band do # yepp, people do crazy stuff like that.
|
22
|
-
property :name
|
23
|
-
property :label do
|
24
|
-
property :name
|
25
|
-
validation do
|
26
|
-
required(:name).filled
|
27
|
-
end
|
28
|
-
end
|
29
|
-
# TODO: make band a required object.
|
30
|
-
|
31
|
-
validation do
|
32
|
-
# required(:name).filled(:music_taste_ok?)
|
33
|
-
|
34
|
-
configure do
|
35
|
-
config.messages_file = "test/validation/errors.yml"
|
36
|
-
|
37
|
-
def music_taste_ok?(value)
|
38
|
-
value != "Nickelback"
|
39
|
-
# errors.add(:base, "You are a bad person") if name == "Nickelback"
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
43
|
-
# validate :music_taste_ok?
|
44
|
-
|
45
|
-
# private
|
46
|
-
# def music_taste_ok?
|
47
|
-
# errors.add(:base, "You are a bad person") if name == "Nickelback"
|
48
|
-
# end
|
49
|
-
end
|
50
|
-
|
51
|
-
validation do
|
52
|
-
required(:title).filled
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
let (:album) do
|
57
|
-
OpenStruct.new(
|
58
|
-
:title => "Blackhawks Over Los Angeles",
|
59
|
-
:hit => song,
|
60
|
-
:songs => songs, # TODO: document this requirement,
|
61
|
-
|
62
|
-
:band => Struct.new(:name, :label).new("Epitaph", OpenStruct.new),
|
63
|
-
)
|
64
|
-
end
|
65
|
-
let (:song) { OpenStruct.new(:title => "Downtown") }
|
66
|
-
let (:songs) { [song=OpenStruct.new(:title => "Calling"), song] }
|
67
|
-
let (:form) { AlbumForm.new(album) }
|
68
|
-
|
69
|
-
|
70
|
-
describe "incorrect #validate" do
|
71
|
-
before { form.validate(
|
72
|
-
"hit" =>{"title" => ""},
|
73
|
-
"title" => "",
|
74
|
-
"songs" => [{"title" => ""}, {"title" => ""}]) } # FIXME: what happens if item is missing?
|
75
|
-
|
76
|
-
it do
|
77
|
-
form.errors.messages.must_equal({
|
78
|
-
:title => ["must be filled"],
|
79
|
-
:"hit.title"=>["must be filled"],
|
80
|
-
:"songs.title"=>["must be filled"],
|
81
|
-
:"band.label.name"=>["is missing"]
|
82
|
-
})
|
83
|
-
end
|
84
|
-
|
85
|
-
it do
|
86
|
-
#form.errors.must_equal({:title => ["must be filled"]})
|
87
|
-
# TODO: this should only contain local errors?
|
88
|
-
end
|
89
|
-
|
90
|
-
# nested forms keep their own Errors:
|
91
|
-
it { form.hit.errors.messages.must_equal({:title=>["must be filled"]}) }
|
92
|
-
it { form.songs[0].errors.messages.must_equal({:title=>["must be filled"]}) }
|
93
|
-
|
94
|
-
it do
|
95
|
-
form.errors.messages.must_equal({
|
96
|
-
:title => ["must be filled"],
|
97
|
-
:"hit.title" => ["must be filled"],
|
98
|
-
:"songs.title"=> ["must be filled"],
|
99
|
-
:"band.label.name"=>["is missing"]
|
100
|
-
})
|
101
|
-
end
|
102
|
-
end
|
103
|
-
|
104
|
-
|
105
|
-
describe "#validate with main form invalid" do
|
106
|
-
it do
|
107
|
-
form.validate("title"=>"", "band"=>{"label"=>{:name => "Fat Wreck"}}).must_equal false
|
108
|
-
form.errors.messages.must_equal({:title=>["must be filled"]})
|
109
|
-
end
|
110
|
-
end
|
111
|
-
|
112
|
-
|
113
|
-
describe "#validate with middle nested form invalid" do
|
114
|
-
before { @result = form.validate("hit"=>{"title" => ""}, "band"=>{"label"=>{:name => "Fat Wreck"}}) }
|
115
|
-
|
116
|
-
it { @result.must_equal false }
|
117
|
-
it { form.errors.messages.must_equal({:"hit.title"=>["must be filled"]}) }
|
118
|
-
end
|
119
|
-
|
120
|
-
|
121
|
-
describe "#validate with collection form invalid" do
|
122
|
-
before { @result = form.validate("songs"=>[{"title" => ""}], "band"=>{"label"=>{:name => "Fat Wreck"}}) }
|
123
|
-
|
124
|
-
it { @result.must_equal false }
|
125
|
-
it { form.errors.messages.must_equal({:"songs.title"=>["must be filled"]}) }
|
126
|
-
end
|
127
|
-
|
128
|
-
|
129
|
-
describe "#validate with collection and 2-level-nested invalid" do
|
130
|
-
before { @result = form.validate("songs"=>[{"title" => ""}], "band" => {"label" => {}}) }
|
131
|
-
|
132
|
-
it { @result.must_equal false }
|
133
|
-
it { form.errors.messages.must_equal({:"songs.title"=>["must be filled"], :"band.label.name"=>["is missing"]}) }
|
134
|
-
end
|
135
|
-
|
136
|
-
describe "#validate with nested form using :base invalid" do
|
137
|
-
it do
|
138
|
-
result = form.validate("songs"=>[{"title" => "Someday"}], "band" => {"name" => "Nickelback", "label" => {"name" => "Roadrunner Records"}})
|
139
|
-
result.must_equal false
|
140
|
-
form.errors.messages.must_equal({:"band.name"=>["You are a bad person"]})
|
141
|
-
end
|
142
|
-
end
|
143
|
-
|
144
|
-
describe "correct #validate" do
|
145
|
-
before { @result = form.validate(
|
146
|
-
"hit" => {"title" => "Sacrifice"},
|
147
|
-
"title" => "Second Heat",
|
148
|
-
"songs" => [{"title"=>"Heart Of A Lion"}],
|
149
|
-
"band" => {"label"=>{:name => "Fat Wreck"}}
|
150
|
-
) }
|
151
|
-
|
152
|
-
it { @result.must_equal true }
|
153
|
-
it { form.hit.title.must_equal "Sacrifice" }
|
154
|
-
it { form.title.must_equal "Second Heat" }
|
155
|
-
it { form.songs.first.title.must_equal "Heart Of A Lion" }
|
156
|
-
end
|
157
|
-
|
158
|
-
|
159
|
-
describe "Errors#to_s" do
|
160
|
-
before { form.validate("songs"=>[{"title" => ""}], "band" => {"label" => {}}) }
|
161
|
-
|
162
|
-
# to_s is aliased to messages
|
163
|
-
it { form.errors.to_s.must_equal "{:\"songs.title\"=>[\"must be filled\"], :\"band.label.name\"=>[\"is missing\"]}" }
|
164
|
-
end
|
165
|
-
end
|