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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d7eb22ab0a9fbdbf797fd70548c4caed7201b4e7
4
- data.tar.gz: d8fd5dbc4f0e7a10238aedddb889c37163e419f6
3
+ metadata.gz: 4014e07ca32e3c7a76ee15db0dfe478a4103d123
4
+ data.tar.gz: 83224f22dc199d2a3f1bf4d46011f616195d33ca
5
5
  SHA512:
6
- metadata.gz: 5d8ccd8693068596ca26d4d6b15ddfbd5434b558062de2371a154c1b5e562712792065ba4ec32bc937bcd4870cac1a4f4bab4e428409f02d75303d8707c9e9ad
7
- data.tar.gz: 7df1a5feca9a80307e1f1fbeb6f5a1e840775743cf96136a45e723a7cd434117563b297d57264cb5169dab8534cc125ea5af3a0009a99cb6123d4e7ce4024946
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
@@ -1,4 +1,4 @@
1
- Copyright (c) 2015-2018 Patrik Rak (patrik@raxoft.cz)
1
+ Copyright (c) 2015-2019 Patrik Rak (patrik@raxoft.cz)
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining a copy
4
4
  of this software and associated documentation files (the "Software"), to deal
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 three helper shortcuts
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 &copy; 2015-2016 Patrik Rak
3259
+ Copyright &copy; 2015-2019 Patrik Rak
3182
3260
 
3183
3261
  Translations contributed by
3184
3262
  Maroš Rovňák (Slovak)
@@ -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 url_params, which creates a hash suitable for url output.
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
@@ -3,7 +3,7 @@
3
3
  class FormInput
4
4
  module Version
5
5
  MAJOR = 1
6
- MINOR = 1
6
+ MINOR = 2
7
7
  PATCH = 0
8
8
  STRING = [ MAJOR, MINOR, PATCH ].join( '.' ).freeze
9
9
  end
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 TypeError
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.1.0
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: 2018-03-06 00:00:00.000000000 Z
11
+ date: 2019-06-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack