form_input 1.1.0 → 1.2.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.
- 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
|