form_input 1.1.0 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/LICENSE +1 -1
- data/README.md +81 -3
- data/lib/form_input/core.rb +18 -1
- data/lib/form_input/version.rb +1 -1
- data/test/test_core.rb +36 -4
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4014e07ca32e3c7a76ee15db0dfe478a4103d123
|
4
|
+
data.tar.gz: 83224f22dc199d2a3f1bf4d46011f616195d33ca
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 82f8812fbd77215e7a058f1585bc7b2ee2a5082215d4247ae865b8ced6e2c739c39e836741482ebf4cb089fe57b6c330f8b19726d8998ed8cfca48910faf92ba
|
7
|
+
data.tar.gz: e2e6853f4eb58193d11aa22696164844130c5c653df6b872372a40bfa1d790a8b61cf45bd287046cf7c375c14326709c49d907ba51e5779f0d3a72ad9ff12152
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
= 1.2.0
|
2
|
+
|
3
|
+
* Few changes for easier import and export of JSON payloads:
|
4
|
+
* The import and the new from_data methods now support numeric, boolean, and nil values natively.
|
5
|
+
* Added to_data method which creates a hash of all non-nil parameters.
|
6
|
+
|
1
7
|
= 1.1.0
|
2
8
|
|
3
9
|
* Added report! methods for late reporting of the most fundamental errors.
|
data/LICENSE
CHANGED
data/README.md
CHANGED
@@ -86,6 +86,7 @@ Sounds cool enough? Then read on.
|
|
86
86
|
* [Errors and Validation](#errors-and-validation)
|
87
87
|
* [Using Forms](#using-forms)
|
88
88
|
* [URL Helpers](#url-helpers)
|
89
|
+
* [JSON Helpers](#json-helpers)
|
89
90
|
* [Form Helpers](#form-helpers)
|
90
91
|
* [Extending Forms](#extending-forms)
|
91
92
|
* [Parameter Options](#parameter-options)
|
@@ -798,13 +799,22 @@ to include parts of the URL as the form input:
|
|
798
799
|
end
|
799
800
|
```
|
800
801
|
|
802
|
+
Similarly, you want to use `import` when feeding form input with JSON data (see [JSON Helpers](#json-helpers) for details):
|
803
|
+
|
804
|
+
``` ruby
|
805
|
+
post '/api/v1/contact' do
|
806
|
+
form = ContactForm.new.import( json_data )
|
807
|
+
end
|
808
|
+
```
|
809
|
+
|
801
810
|
If you are worried that you might make a mistake,
|
802
|
-
you can use one of the
|
811
|
+
you can use one of the four helper shortcuts
|
803
812
|
which make it easier to remember which one to use when:
|
804
813
|
|
805
814
|
``` ruby
|
806
815
|
form = ContactForm.from_request( request ) # Like new.import, for Rack request with external values.
|
807
816
|
form = ContactForm.from_params( params ) # Like new.import, for params hash of external values.
|
817
|
+
form = ContactForm.from_data( json_data ) # Like new.import, for JSON hash of external values.
|
808
818
|
form = ContactForm.from_hash( some_hash ) # Like new.set, for hash of internal values.
|
809
819
|
```
|
810
820
|
|
@@ -1254,7 +1264,7 @@ regardless of if it comes from a form post or from the URL query string.
|
|
1254
1264
|
It is therefore quite natural that the `FormInput` provides helpers for generating
|
1255
1265
|
URL query strings as well in addition to helpers used for form creation.
|
1256
1266
|
|
1257
|
-
You can get a hash of filled parameters suitable for use in the URL query by using the `url_params` method,
|
1267
|
+
You can get a hash of filled parameters suitable for use in the URL query by using the `url_params` (AKA `to_params`) method,
|
1258
1268
|
or get them combined into the URL query string by using the `url_query` method.
|
1259
1269
|
Note that the `url_params` result differs considerably from the result of the `to_hash` method,
|
1260
1270
|
as it uses parameter code rather than name for keys and their external representation for the values:
|
@@ -1324,6 +1334,74 @@ Finally, if you do not like the idea of parameter arrays in your URLs, you can u
|
|
1324
1334
|
Just note that none of the standard array parameter validations apply in this case,
|
1325
1335
|
so make sure to apply your own validations using the `:check` callback if you need to.
|
1326
1336
|
|
1337
|
+
### JSON Helpers
|
1338
|
+
|
1339
|
+
The `Form Input` can also help a great deal with validating JSON data commonly used in REST API endpoints.
|
1340
|
+
There are two methods which are quite useful in this case, `from_data` and `to_data`.
|
1341
|
+
The former imports the data from an external input hash,
|
1342
|
+
which may contain data in either internal or external form,
|
1343
|
+
while the latter creates the hash containing the data in their canonic internal form.
|
1344
|
+
|
1345
|
+
When you create a form input from request or params, the external parameter values are always strings.
|
1346
|
+
But in case of JSON data, the values can be also numbers, booleans or `nil`.
|
1347
|
+
Fortunately, the `import` and `from_data` methods can deal with such input as well.
|
1348
|
+
The only difference is that the [input filter](#input-filter) is not run in such case.
|
1349
|
+
The [input transform](#input-transform) is run as usual,
|
1350
|
+
and so are all the [validation methods](#errors-and-validation).
|
1351
|
+
|
1352
|
+
Among other things this means that you can declare a parameter as integer using the `INTEGER_ARGS` macro,
|
1353
|
+
and pass in the value as either string or integer, and you end up with integer in both cases, which is pretty convenient.
|
1354
|
+
|
1355
|
+
``` ruby
|
1356
|
+
class NumericInput < FormInput
|
1357
|
+
param :int, INTEGER_ARGS
|
1358
|
+
param :float, FLOAT_ARGS
|
1359
|
+
end
|
1360
|
+
|
1361
|
+
NumericInput.from_data( int: '10', float: 3.0 ).to_data # { int: 10, float: 3.0 }
|
1362
|
+
NumericInput.from_data( int: 10, float: '3.0' ).to_data # { int: 10, float: 3.0 }
|
1363
|
+
```
|
1364
|
+
|
1365
|
+
Another difference when dealing with JSON data is that
|
1366
|
+
the empty strings have completely different significance than in case of web forms.
|
1367
|
+
For this reason, the result of the `to_data` method includes even empty strings, arrays, and hashes
|
1368
|
+
(unlike the `to_hash` and `to_params` methods).
|
1369
|
+
The `nil` values are still filtered out, though.
|
1370
|
+
|
1371
|
+
``` ruby
|
1372
|
+
class OptionalInput < FormInput
|
1373
|
+
param :string
|
1374
|
+
array :array
|
1375
|
+
hash :hash
|
1376
|
+
end
|
1377
|
+
|
1378
|
+
OptionalInput.from_data( string: '' ).to_data # { string: '' }
|
1379
|
+
OptionalInput.from_data( array: [] ).to_data # { array: [] }
|
1380
|
+
OptionalInput.from_data( hash: {} ).to_data # { hash: {} }
|
1381
|
+
```
|
1382
|
+
|
1383
|
+
The whole JSON processing may in the end look something like this:
|
1384
|
+
|
1385
|
+
``` ruby
|
1386
|
+
require 'oj'
|
1387
|
+
def json_data
|
1388
|
+
data = Oj.load( request.body, symbolize_keys: true )
|
1389
|
+
halt( 422, 'Invalid data' ) unless Hash === data
|
1390
|
+
data
|
1391
|
+
rescue Oj::Error
|
1392
|
+
halt( 422, 'Invalid JSON' )
|
1393
|
+
end
|
1394
|
+
|
1395
|
+
post '/api/v1/contact' do
|
1396
|
+
input = ContactForm.from_data( json_data )
|
1397
|
+
halt( 422, "Invalid data: #{input.error_messages.first}" ) unless input.valid?
|
1398
|
+
# Somehow use the input.
|
1399
|
+
Contact.create( input.to_data )
|
1400
|
+
end
|
1401
|
+
```
|
1402
|
+
|
1403
|
+
Not bad for having completely validated and converted input data, is it?
|
1404
|
+
|
1327
1405
|
### Form Helpers
|
1328
1406
|
|
1329
1407
|
It may come as a surprise, but `FormInput` provides no helpers for creating HTML tags.
|
@@ -3178,7 +3256,7 @@ Thanks for that.
|
|
3178
3256
|
|
3179
3257
|
## Credits
|
3180
3258
|
|
3181
|
-
Copyright © 2015-
|
3259
|
+
Copyright © 2015-2019 Patrik Rak
|
3182
3260
|
|
3183
3261
|
Translations contributed by
|
3184
3262
|
Maroš Rovňák (Slovak)
|
data/lib/form_input/core.rb
CHANGED
@@ -742,6 +742,7 @@ class FormInput
|
|
742
742
|
def from_params( params )
|
743
743
|
new.import( params )
|
744
744
|
end
|
745
|
+
alias from_data from_params
|
745
746
|
|
746
747
|
# Create new form from hash with internal values.
|
747
748
|
def from_hash( hash )
|
@@ -821,6 +822,10 @@ class FormInput
|
|
821
822
|
# The validation done later ensures that the keys are valid, within range,
|
822
823
|
# and that only flat hashes are allowed.
|
823
824
|
Hash[ value.map{ |k, v| [ ( Integer( k, 10 ) rescue k ), sanitize_value( v, filter ) ] } ]
|
825
|
+
when Numeric, TrueClass, FalseClass, NilClass
|
826
|
+
# For convenience of importing JSON payloads, allow each of these simple scalar types as they are.
|
827
|
+
# The validation done later will ensure that the type class matches the parameter.
|
828
|
+
value
|
824
829
|
else
|
825
830
|
fail TypeError, "unexpected parameter type"
|
826
831
|
end
|
@@ -878,7 +883,8 @@ class FormInput
|
|
878
883
|
end
|
879
884
|
|
880
885
|
# Return all non-empty parameters as a hash.
|
881
|
-
# See also
|
886
|
+
# See also #to_data, which creates a hash of non-nil parameters,
|
887
|
+
# and #url_params, which creates a hash suitable for url output.
|
882
888
|
def to_hash
|
883
889
|
result = {}
|
884
890
|
filled_params.each{ |x| result[ x.name ] = x.value }
|
@@ -886,6 +892,16 @@ class FormInput
|
|
886
892
|
end
|
887
893
|
alias to_h to_hash
|
888
894
|
|
895
|
+
# Return all non-nil parameters as a hash.
|
896
|
+
# Note that the keys are external names of the parameters (should they differ),
|
897
|
+
# so the keys created by `from_data(data).to_data` remain consistent.
|
898
|
+
# See also #to_hash, which creates a hash of non-empty parameters.
|
899
|
+
def to_data
|
900
|
+
result = {}
|
901
|
+
params.each{ |x| result[ x.code ] = x.value unless x.value.nil? }
|
902
|
+
result
|
903
|
+
end
|
904
|
+
|
889
905
|
# Convert parameters to names and fail if we encounter unknown one.
|
890
906
|
def validate_names( names )
|
891
907
|
names.flatten.map do |name|
|
@@ -1077,6 +1093,7 @@ class FormInput
|
|
1077
1093
|
result
|
1078
1094
|
end
|
1079
1095
|
alias url_parameters url_params
|
1096
|
+
alias to_params url_params
|
1080
1097
|
|
1081
1098
|
# Create string containing URL query from all current non-empty parameters.
|
1082
1099
|
def url_query
|
data/lib/form_input/version.rb
CHANGED
data/test/test_core.rb
CHANGED
@@ -459,6 +459,7 @@ describe FormInput do
|
|
459
459
|
arr: [ "a", "b" ],
|
460
460
|
hsh: { "1" => "2", "3" => "4" },
|
461
461
|
}
|
462
|
+
f.url_params.should == f.to_params
|
462
463
|
f.url_params.should == {
|
463
464
|
str: "1.5",
|
464
465
|
int: "1",
|
@@ -485,6 +486,7 @@ describe FormInput do
|
|
485
486
|
time: "e",
|
486
487
|
bool: false,
|
487
488
|
}
|
489
|
+
f.url_params.should == f.to_params
|
488
490
|
f.url_params.should == {
|
489
491
|
str: "a",
|
490
492
|
int: "0",
|
@@ -1184,14 +1186,44 @@ describe FormInput do
|
|
1184
1186
|
end
|
1185
1187
|
|
1186
1188
|
should 'reject unexpected values in request input' do
|
1187
|
-
->{ TestForm.new.import( q: "", opts: [], on: {} ) }.should.not.raise
|
1188
|
-
->{ TestForm.new.import( q: [], opts: {}, on: "" ) }.should.not.raise
|
1189
|
-
->{ TestForm.new.import( q: {}, opts: "", on: [] ) }.should.not.raise
|
1190
|
-
->{ TestForm.new.import( q: 1 ) }.should.raise
|
1189
|
+
->{ TestForm.new.import( q: "", opts: [], on: {} ).valid?(:query).should.be.false }.should.not.raise
|
1190
|
+
->{ TestForm.new.import( q: [], opts: {}, on: "" ).valid?(:query).should.be.false }.should.not.raise
|
1191
|
+
->{ TestForm.new.import( q: {}, opts: "", on: [] ).valid?(:query).should.be.false }.should.not.raise
|
1192
|
+
->{ TestForm.new.import( q: 1 ).valid?(:query).should.be.false }.should.not.raise
|
1193
|
+
->{ TestForm.new.import( q: true ).valid?(:query).should.be.false }.should.not.raise
|
1194
|
+
->{ TestForm.new.import( q: false ).valid?(:query).should.be.false }.should.not.raise
|
1195
|
+
->{ TestForm.new.import( q: nil ).valid?(:query).should.be.false }.should.not.raise
|
1191
1196
|
->{ TestForm.new.import( q: :x ) }.should.raise TypeError
|
1192
1197
|
->{ TestForm.new.import( q: TestForm ) }.should.raise TypeError
|
1193
1198
|
end
|
1194
1199
|
|
1200
|
+
should 'support importing and exporting JSON payloads without modification' do
|
1201
|
+
data = {
|
1202
|
+
q: 'x',
|
1203
|
+
email: 'foo@bar.com',
|
1204
|
+
age: 10,
|
1205
|
+
rate: 0.5,
|
1206
|
+
text: '',
|
1207
|
+
# password: nil,
|
1208
|
+
opts: [],
|
1209
|
+
on: {},
|
1210
|
+
}
|
1211
|
+
f = TestForm.from_data(data)
|
1212
|
+
f.to_data.should == data
|
1213
|
+
|
1214
|
+
f = TestForm.from_data(data.merge(password: nil))
|
1215
|
+
f.to_data.should == data
|
1216
|
+
|
1217
|
+
f.to_hash.should == {
|
1218
|
+
query: 'x',
|
1219
|
+
email: 'foo@bar.com',
|
1220
|
+
age: 10,
|
1221
|
+
rate: 0.5,
|
1222
|
+
}
|
1223
|
+
f.valid?(:age).should.be.true # has filter and correct class
|
1224
|
+
f.valid?(:rate).should.be.false # has no filter and class
|
1225
|
+
end
|
1226
|
+
|
1195
1227
|
should 'make it easy to create URLs' do
|
1196
1228
|
f = TestForm.new( query: "x", opts: [ 0, 0, 1 ], on: { 0 => 1 }, age: 10, email: nil, password: "", text: " " )
|
1197
1229
|
f.url_params.should == { q: "x", age: "10", text: " ", opts: [ "0", "0", "1" ], on: { "0" => "1" } }
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: form_input
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Patrik Rak
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2019-06-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rack
|