reform 2.2.4 → 2.3.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.
- checksums.yaml +4 -4
- data/.travis.yml +13 -7
- data/CHANGES.md +26 -4
- data/CONTRIBUTING.md +31 -0
- data/Gemfile +1 -12
- data/ISSUE_TEMPLATE.md +25 -0
- data/LICENSE.txt +1 -1
- data/README.md +3 -3
- data/lib/reform.rb +1 -0
- data/lib/reform/contract.rb +1 -11
- data/lib/reform/contract/validate.rb +49 -23
- data/lib/reform/errors.rb +49 -0
- data/lib/reform/form.rb +20 -5
- data/lib/reform/form/dry.rb +57 -29
- data/lib/reform/form/populator.rb +2 -16
- data/lib/reform/form/prepopulate.rb +1 -1
- data/lib/reform/form/validate.rb +10 -2
- data/lib/reform/result.rb +63 -0
- data/lib/reform/validation.rb +19 -13
- data/lib/reform/validation/groups.rb +11 -25
- data/lib/reform/version.rb +1 -1
- data/reform.gemspec +7 -6
- data/test/benchmarking.rb +39 -5
- data/test/call_test.rb +1 -1
- data/test/changed_test.rb +1 -1
- data/test/coercion_test.rb +2 -2
- data/test/composition_test.rb +47 -9
- data/test/contract_test.rb +5 -5
- data/test/default_test.rb +1 -1
- data/test/deserialize_test.rb +3 -3
- data/test/errors_test.rb +36 -21
- data/test/feature_test.rb +1 -1
- data/test/fixtures/dry_error_messages.yml +70 -23
- data/test/form_option_test.rb +3 -3
- data/test/form_test.rb +3 -3
- data/test/from_test.rb +2 -2
- data/test/inherit_test.rb +44 -51
- data/test/module_test.rb +12 -12
- data/test/parse_option_test.rb +40 -0
- data/test/parse_pipeline_test.rb +2 -2
- data/test/populate_test.rb +59 -19
- data/test/populator_skip_test.rb +9 -8
- data/test/prepopulator_test.rb +3 -3
- data/test/readable_test.rb +2 -2
- data/test/readonly_test.rb +1 -1
- data/test/reform_test.rb +16 -31
- data/test/save_test.rb +23 -8
- data/test/setup_test.rb +2 -2
- data/test/skip_if_test.rb +4 -4
- data/test/skip_setter_and_getter_test.rb +1 -1
- data/test/test_helper.rb +13 -10
- data/test/validate_test.rb +18 -18
- data/test/validation/dry_validation_test.rb +430 -117
- data/test/validation/result_test.rb +79 -0
- data/test/validation_library_provided_test.rb +16 -0
- data/test/virtual_test.rb +1 -1
- data/test/writeable_test.rb +31 -2
- metadata +42 -23
- 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/deprecation_test.rb +0 -27
- data/test/validation/dry_test.rb +0 -60
- data/test/validation/errors.yml +0 -4
@@ -0,0 +1,79 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class ErrorsResultTest < Minitest::Spec
|
4
|
+
MyResult = Struct.new(:success?, :errors) do
|
5
|
+
def failure?; !success? end
|
6
|
+
end
|
7
|
+
|
8
|
+
# TODO: errors(args) not tested.
|
9
|
+
|
10
|
+
describe "Contract::Result#success?" do
|
11
|
+
let (:failed) { MyResult.new(false) }
|
12
|
+
let (:succeeded) { MyResult.new(true) }
|
13
|
+
|
14
|
+
it { Reform::Contract::Result.new([failed, failed]).success?.must_equal false }
|
15
|
+
it { Reform::Contract::Result.new([succeeded, failed]).success?.must_equal false }
|
16
|
+
it { Reform::Contract::Result.new([failed, succeeded]).success?.must_equal false }
|
17
|
+
it { Reform::Contract::Result.new([succeeded, succeeded]).success?.must_equal true }
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "Contract::Result#errors" do
|
21
|
+
let (:results) {
|
22
|
+
[
|
23
|
+
MyResult.new(false, { title: ["must be filled"], nested: { something: [] } }),
|
24
|
+
MyResult.new(false, { length: ["no Int"] })
|
25
|
+
]
|
26
|
+
}
|
27
|
+
|
28
|
+
it { Reform::Contract::Result.new(results).errors.must_equal({:title=>["must be filled"], :length=>["no Int"]}) }
|
29
|
+
end
|
30
|
+
|
31
|
+
describe "Result::Pointer" do
|
32
|
+
let (:errors) do # dry result #errors format.
|
33
|
+
{
|
34
|
+
title: ["ignore"],
|
35
|
+
artist: { age: ["too old"],
|
36
|
+
bands: {
|
37
|
+
0 => { name: "too new school" },
|
38
|
+
1 => { name: "too boring" },
|
39
|
+
}
|
40
|
+
}
|
41
|
+
}
|
42
|
+
end
|
43
|
+
|
44
|
+
let (:top) { Reform::Contract::Result::Pointer.new(MyResult.new(false, errors), []) }
|
45
|
+
it { top.success?.must_equal false }
|
46
|
+
it { top.errors.must_equal errors }
|
47
|
+
|
48
|
+
let (:artist) { Reform::Contract::Result::Pointer.new(MyResult.new(false, errors), [:artist]) }
|
49
|
+
it { artist.success?.must_equal false }
|
50
|
+
it { artist.errors.must_equal({:age=>["too old"], :bands=>{0=>{:name=>"too new school"}, 1=>{:name=>"too boring"}}}) }
|
51
|
+
|
52
|
+
let (:band) { Reform::Contract::Result::Pointer.new(MyResult.new(false, errors), [:artist, :bands, 1]) }
|
53
|
+
it { band.success?.must_equal false }
|
54
|
+
it { band.errors.must_equal({:name=>"too boring"}) }
|
55
|
+
|
56
|
+
describe "advance" do
|
57
|
+
let(:advanced) { artist.advance(:bands, 1) }
|
58
|
+
|
59
|
+
it { advanced.success?.must_equal false }
|
60
|
+
it { advanced.errors.must_equal({:name=>"too boring"}) }
|
61
|
+
|
62
|
+
it { artist.advance([:absolute, :nonsense]).must_equal nil }
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
# validation group:
|
69
|
+
|
70
|
+
# form.errors/messages/hint(*args) ==> {:title: [..]}
|
71
|
+
# @call_result.errors/messages/hint(*args) }
|
72
|
+
|
73
|
+
|
74
|
+
|
75
|
+
# # result = Result(original_result => [:band, :label], my_local_result => [] )
|
76
|
+
# # result.messages(locale: :en) merges original_result and my_local_result
|
77
|
+
|
78
|
+
# form.errors => Result(fetch tree of all nested forms.messages(*args))
|
79
|
+
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'reform'
|
2
|
+
require 'minitest/autorun'
|
3
|
+
|
4
|
+
class ValidationLibraryProvidedTest < MiniTest::Spec
|
5
|
+
it 'no validation library loaded' do
|
6
|
+
assert_raises Reform::Validation::NoValidationLibraryError do
|
7
|
+
class PersonForm < Reform::Form
|
8
|
+
property :name
|
9
|
+
|
10
|
+
validation do
|
11
|
+
required(:name).maybe(:str?)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/test/virtual_test.rb
CHANGED
data/test/writeable_test.rb
CHANGED
@@ -3,7 +3,7 @@ require 'test_helper'
|
|
3
3
|
class WriteableTest < MiniTest::Spec
|
4
4
|
Location = Struct.new(:country)
|
5
5
|
|
6
|
-
class LocationForm <
|
6
|
+
class LocationForm < TestForm
|
7
7
|
property :country, writeable: false
|
8
8
|
end
|
9
9
|
|
@@ -26,4 +26,33 @@ class WriteableTest < MiniTest::Spec
|
|
26
26
|
|
27
27
|
hash.must_equal("country"=> "Germany")
|
28
28
|
end
|
29
|
-
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# writable option is alias of writeable option.
|
32
|
+
class WritableTest < MiniTest::Spec
|
33
|
+
Location = Struct.new(:country)
|
34
|
+
|
35
|
+
class LocationForm < TestForm
|
36
|
+
property :country, writable: false
|
37
|
+
end
|
38
|
+
|
39
|
+
let (:loc) { Location.new("Australia") }
|
40
|
+
let (:form) { LocationForm.new(loc) }
|
41
|
+
|
42
|
+
it do
|
43
|
+
form.country.must_equal "Australia"
|
44
|
+
|
45
|
+
form.validate("country" => "Germany") # this usually won't change when submitting.
|
46
|
+
form.country.must_equal "Germany"
|
47
|
+
|
48
|
+
form.sync
|
49
|
+
loc.country.must_equal "Australia" # the writer wasn't called.
|
50
|
+
|
51
|
+
hash = {}
|
52
|
+
form.save do |nested|
|
53
|
+
hash = nested
|
54
|
+
end
|
55
|
+
|
56
|
+
hash.must_equal("country"=> "Germany")
|
57
|
+
end
|
58
|
+
end
|
metadata
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: reform
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.3.0.rc1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nick Sutterer
|
8
|
-
-
|
8
|
+
- Fran Worley
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2017-
|
12
|
+
date: 2017-02-26 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: disposable
|
@@ -17,14 +17,34 @@ dependencies:
|
|
17
17
|
requirements:
|
18
18
|
- - ">="
|
19
19
|
- !ruby/object:Gem::Version
|
20
|
-
version: 0.4.
|
20
|
+
version: 0.4.2
|
21
|
+
- - "<"
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.5.0
|
21
24
|
type: :runtime
|
22
25
|
prerelease: false
|
23
26
|
version_requirements: !ruby/object:Gem::Requirement
|
24
27
|
requirements:
|
25
28
|
- - ">="
|
26
29
|
- !ruby/object:Gem::Version
|
27
|
-
version: 0.4.
|
30
|
+
version: 0.4.2
|
31
|
+
- - "<"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.5.0
|
34
|
+
- !ruby/object:Gem::Dependency
|
35
|
+
name: uber
|
36
|
+
requirement: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "<"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 0.2.0
|
41
|
+
type: :runtime
|
42
|
+
prerelease: false
|
43
|
+
version_requirements: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "<"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 0.2.0
|
28
48
|
- !ruby/object:Gem::Dependency
|
29
49
|
name: representable
|
30
50
|
requirement: !ruby/object:Gem::Requirement
|
@@ -121,18 +141,18 @@ dependencies:
|
|
121
141
|
requirements:
|
122
142
|
- - ">="
|
123
143
|
- !ruby/object:Gem::Version
|
124
|
-
version: 0.10.
|
144
|
+
version: 0.10.1
|
125
145
|
type: :development
|
126
146
|
prerelease: false
|
127
147
|
version_requirements: !ruby/object:Gem::Requirement
|
128
148
|
requirements:
|
129
149
|
- - ">="
|
130
150
|
- !ruby/object:Gem::Version
|
131
|
-
version: 0.10.
|
151
|
+
version: 0.10.1
|
132
152
|
description: Form object decoupled from models.
|
133
153
|
email:
|
134
154
|
- apotonick@gmail.com
|
135
|
-
-
|
155
|
+
- frances@safetytoolbox.co.uk
|
136
156
|
executables: []
|
137
157
|
extensions: []
|
138
158
|
extra_rdoc_files: []
|
@@ -140,28 +160,27 @@ files:
|
|
140
160
|
- ".gitignore"
|
141
161
|
- ".travis.yml"
|
142
162
|
- CHANGES.md
|
163
|
+
- CONTRIBUTING.md
|
143
164
|
- Gemfile
|
165
|
+
- ISSUE_TEMPLATE.md
|
144
166
|
- LICENSE.txt
|
145
167
|
- README.md
|
146
168
|
- Rakefile
|
147
169
|
- TODO.md
|
148
|
-
- gemfiles/Gemfile.disposable-0.3
|
149
170
|
- lib/reform.rb
|
150
171
|
- lib/reform/contract.rb
|
151
|
-
- lib/reform/contract/errors.rb
|
152
172
|
- lib/reform/contract/validate.rb
|
173
|
+
- lib/reform/errors.rb
|
153
174
|
- lib/reform/form.rb
|
154
175
|
- lib/reform/form/call.rb
|
155
176
|
- lib/reform/form/coercion.rb
|
156
177
|
- lib/reform/form/composition.rb
|
157
178
|
- lib/reform/form/dry.rb
|
158
179
|
- lib/reform/form/module.rb
|
159
|
-
- lib/reform/form/mongoid.rb
|
160
|
-
- lib/reform/form/orm.rb
|
161
180
|
- lib/reform/form/populator.rb
|
162
181
|
- lib/reform/form/prepopulate.rb
|
163
182
|
- lib/reform/form/validate.rb
|
164
|
-
- lib/reform/
|
183
|
+
- lib/reform/result.rb
|
165
184
|
- lib/reform/validation.rb
|
166
185
|
- lib/reform/validation/groups.rb
|
167
186
|
- lib/reform/version.rb
|
@@ -173,7 +192,6 @@ files:
|
|
173
192
|
- test/composition_test.rb
|
174
193
|
- test/contract_test.rb
|
175
194
|
- test/default_test.rb
|
176
|
-
- test/deprecation_test.rb
|
177
195
|
- test/deserialize_test.rb
|
178
196
|
- test/errors_test.rb
|
179
197
|
- test/feature_test.rb
|
@@ -183,6 +201,7 @@ files:
|
|
183
201
|
- test/from_test.rb
|
184
202
|
- test/inherit_test.rb
|
185
203
|
- test/module_test.rb
|
204
|
+
- test/parse_option_test.rb
|
186
205
|
- test/parse_pipeline_test.rb
|
187
206
|
- test/populate_test.rb
|
188
207
|
- test/populator_skip_test.rb
|
@@ -197,12 +216,12 @@ files:
|
|
197
216
|
- test/skip_setter_and_getter_test.rb
|
198
217
|
- test/test_helper.rb
|
199
218
|
- test/validate_test.rb
|
200
|
-
- test/validation/dry_test.rb
|
201
219
|
- test/validation/dry_validation_test.rb
|
202
|
-
- test/validation/
|
220
|
+
- test/validation/result_test.rb
|
221
|
+
- test/validation_library_provided_test.rb
|
203
222
|
- test/virtual_test.rb
|
204
223
|
- test/writeable_test.rb
|
205
|
-
homepage: https://github.com/
|
224
|
+
homepage: https://github.com/trailblazer/reform
|
206
225
|
licenses:
|
207
226
|
- MIT
|
208
227
|
metadata: {}
|
@@ -217,12 +236,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
217
236
|
version: '0'
|
218
237
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
219
238
|
requirements:
|
220
|
-
- - "
|
239
|
+
- - ">"
|
221
240
|
- !ruby/object:Gem::Version
|
222
|
-
version:
|
241
|
+
version: 1.3.1
|
223
242
|
requirements: []
|
224
243
|
rubyforge_project:
|
225
|
-
rubygems_version: 2.
|
244
|
+
rubygems_version: 2.6.3
|
226
245
|
signing_key:
|
227
246
|
specification_version: 4
|
228
247
|
summary: Form object decoupled from models with validation, population and presentation.
|
@@ -234,7 +253,6 @@ test_files:
|
|
234
253
|
- test/composition_test.rb
|
235
254
|
- test/contract_test.rb
|
236
255
|
- test/default_test.rb
|
237
|
-
- test/deprecation_test.rb
|
238
256
|
- test/deserialize_test.rb
|
239
257
|
- test/errors_test.rb
|
240
258
|
- test/feature_test.rb
|
@@ -244,6 +262,7 @@ test_files:
|
|
244
262
|
- test/from_test.rb
|
245
263
|
- test/inherit_test.rb
|
246
264
|
- test/module_test.rb
|
265
|
+
- test/parse_option_test.rb
|
247
266
|
- test/parse_pipeline_test.rb
|
248
267
|
- test/populate_test.rb
|
249
268
|
- test/populator_skip_test.rb
|
@@ -258,8 +277,8 @@ test_files:
|
|
258
277
|
- test/skip_setter_and_getter_test.rb
|
259
278
|
- test/test_helper.rb
|
260
279
|
- test/validate_test.rb
|
261
|
-
- test/validation/dry_test.rb
|
262
280
|
- test/validation/dry_validation_test.rb
|
263
|
-
- test/validation/
|
281
|
+
- test/validation/result_test.rb
|
282
|
+
- test/validation_library_provided_test.rb
|
264
283
|
- test/virtual_test.rb
|
265
284
|
- test/writeable_test.rb
|
@@ -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
|