form_input 1.0.0 → 1.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3bf25959262e5eb0029d1185a5c7a9901a43cd16
4
- data.tar.gz: 678589bac2e0bdcf52ca76568d63ee18cde7e128
3
+ metadata.gz: d7eb22ab0a9fbdbf797fd70548c4caed7201b4e7
4
+ data.tar.gz: d8fd5dbc4f0e7a10238aedddb889c37163e419f6
5
5
  SHA512:
6
- metadata.gz: f2d7d5d8caa65943d9765cf480f706614f7e77ad67766d5dddddb9f66f0f5f33c7d547aa614e648a5c43bf855c512d9764ea00988eafd3769ca5864733b7e281
7
- data.tar.gz: fec7c3c8f5114861fefd20ed438a000bdab38f0c5bc912dd8a8a2d335e71c899b7ad22066b40598b1bf36ba3c1b0de1aeed87a5e8e528b4105655188db678f17
6
+ metadata.gz: 5d8ccd8693068596ca26d4d6b15ddfbd5434b558062de2371a154c1b5e562712792065ba4ec32bc937bcd4870cac1a4f4bab4e428409f02d75303d8707c9e9ad
7
+ data.tar.gz: 7df1a5feca9a80307e1f1fbeb6f5a1e840775743cf96136a45e723a7cd434117563b297d57264cb5169dab8534cc125ea5af3a0009a99cb6123d4e7ce4024946
data/CHANGELOG.md ADDED
@@ -0,0 +1,9 @@
1
+ = 1.1.0
2
+
3
+ * Added report! methods for late reporting of the most fundamental errors.
4
+
5
+ * Added support for multiple `check` and `test` validation callbacks.
6
+
7
+ = 1.0.0
8
+
9
+ * Initial release.
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2015-2016 Patrik Rak (patrik@raxoft.cz)
1
+ Copyright (c) 2015-2018 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
@@ -171,7 +171,7 @@ The following two declarations are therefore the same:
171
171
  ```
172
172
 
173
173
  Parameters support many more parameter options,
174
- and we will discuss each in turn as we go.
174
+ and we will discuss each one in turn as we go.
175
175
  Comprehensive summary for an avid reader is however available in [Parameter Options](#parameter-options).
176
176
 
177
177
  The value of each parameter is a string by default (or `nil` if the parameter is not set at all).
@@ -643,6 +643,10 @@ The advantage of the `:test` callback is that it works the same way regardless o
643
643
  scalar or not,
644
644
  so it is preferable to use it
645
645
  if you plan to factor this into a `COUNTRY_ARGS` helper which works with both kinds of parameters.
646
+ If you do this,
647
+ you should also know that each parameter can support multiple `:check` and `:test` callbacks,
648
+ and when the parameter options are merged together, all of them are preserved by default
649
+ (unless you set it to `nil`, which resets all previously defined ones).
646
650
 
647
651
  In either case, the `report` method is used to report any problems about the parameter,
648
652
  which marks the parameter as invalid at the same time.
@@ -951,14 +955,22 @@ and let them report any belated additional errors which might get detected durin
951
955
  for example:
952
956
 
953
957
  ``` ruby
954
- form.report( :email, "Email is already taken" ) unless unique_email?( form.email )
958
+ form.report( :email, "Email address is already taken" ) unless unique_email?( form.email )
955
959
  ```
956
960
 
957
- As we have already seen, it is common to use the `report`
961
+ If you want the error message to have priority over anything else what might have been reported before,
962
+ you can use the `report!` method instead:
963
+
964
+ ``` ruby
965
+ form.filled_params.each{ |p| p.report!( "Fill only one of these" ) } unless form.filled_params.one?
966
+ ```
967
+
968
+ As we have already seen [before](#array-and-hash-parameters), it is common to use the `report`
958
969
  method from within the `:check` or `:test` callback of the parameter itself as well:
959
970
 
960
971
  ``` ruby
961
- check: ->{ report( "%p is already taken" ) unless unique_email?( value ) }
972
+ check: ->{ report( "%p is odd number" ) unless value.to_i.even? }
973
+ test: ->( value ){ report( "%p contain odd number" ) unless value.to_i.even? }
962
974
  ```
963
975
 
964
976
  In this case the `%p` string is replaced by the `title` of the parameter.
@@ -1119,13 +1131,17 @@ so you can get back to it using the `form` method if you need to:
1119
1131
  fail unless p.form.valid?
1120
1132
  ```
1121
1133
 
1122
- As we have seen, you can report errors about the parameter using its `report` method.
1134
+ As we have seen, you can report errors about the parameter using its `report` or `report!` methods.
1123
1135
  You can ask it about all its errors or just the first error using the `errors` or `error` methods, respectively:
1124
1136
 
1125
1137
  ``` ruby
1126
1138
  p.report( "This is invalid" )
1127
1139
  p.errors # [ "This is invalid" ]
1128
1140
  p.error # "This is invalid"
1141
+
1142
+ p.report!( "Do not fill this!" )
1143
+ p.errors # [ "Do not fill this!", "This is invalid" ]
1144
+ p.error # "Do not fill this!"
1129
1145
  ```
1130
1146
 
1131
1147
  You can also simply ask whether the parameter is valid or not by using the `valid?` and `invalid?` methods.
@@ -1398,7 +1414,7 @@ you will eventually want to extend it further to better fit your project.
1398
1414
  To do this, it's common to define the `Form` class inherited from `FormInput`,
1399
1415
  put various helpers there, and base your own forms on that.
1400
1416
  This is also the place where you can include your own `FormInput` types extensions.
1401
- This chapter shows some ideas you may want to built upon to get you started.
1417
+ This chapter shows some ideas you may want to build upon to get you started.
1402
1418
 
1403
1419
  Adding custom boolean getters which you may need:
1404
1420
 
@@ -1488,9 +1504,9 @@ This is a brief but comprehensive summary of all parameter options:
1488
1504
  See [Output Format](#output-format).
1489
1505
  * `:class` - object type (or array thereof) which the input filter is expected to convert the input value into.
1490
1506
  See [Input Filter](#input-filter).
1491
- * `:check` - optional callback used to perform arbitrary checks when testing the parameter validity.
1507
+ * `:check` - optional callback (or array thereof) used to perform arbitrary checks when testing the parameter validity.
1492
1508
  See [Errors and Validation](#errors-and-validation).
1493
- * `:test` - optional callback used to perform arbitrary tests when testing validity of each parameter value.
1509
+ * `:test` - optional callback (or array thereof) used to perform arbitrary tests when testing validity of each parameter value.
1494
1510
  See [Errors and Validation](#errors-and-validation).
1495
1511
  * `:min_key` - minimum allowed value for keys of hash parameters. Defaults to 0.
1496
1512
  * `:max_key` - maximum allowed value for keys of hash parameters. Defaults to 2<sup>64</sup>-1.
@@ -1554,8 +1570,7 @@ For example, this is a `snippet` helper based on [Sinatra]'s partials:
1554
1570
 
1555
1571
  ``` ruby
1556
1572
  # Render partial, our style.
1557
- def snippet( name, opts = {}, locals = nil )
1558
- opts, locals = {}, opts unless locals
1573
+ def snippet( name, opts = {}, **locals )
1559
1574
  partial( "snippets/#{name}", opts.merge( locals: locals ) )
1560
1575
  end
1561
1576
  ```
@@ -2683,7 +2698,7 @@ you can override it by setting the `:plural` parameter option to `true` or `'p'`
2683
2698
  and to `false` or `'s'` for singular, respectively:
2684
2699
 
2685
2700
  ``` ruby
2686
- param :keywords, "Keywords, plural: true
2701
+ param :keywords, "Keywords", plural: true
2687
2702
  array :countries, "List of countries", plural: false
2688
2703
  ```
2689
2704
 
@@ -2959,7 +2974,7 @@ might look like:
2959
2974
 
2960
2975
  The `pt` method is usable only in the parameter context.
2961
2976
  It works similar to the `ft` method,
2962
- except that all translations are automatically looked up in the parameter's own ``forms.<form_translation_name>.<parameter_name>` namespace,
2977
+ except that all translations are automatically looked up in the parameter's own `forms.<form_translation_name>.<parameter_name>` namespace,
2963
2978
  rather than the global namespace.
2964
2979
 
2965
2980
  ``` ruby
data/form_input.gemspec CHANGED
@@ -21,7 +21,7 @@ EOT
21
21
  s.homepage = 'https://github.com/raxoft/form_input'
22
22
  s.license = 'MIT'
23
23
 
24
- s.files = %w[ LICENSE README.md Rakefile .yardopts form_input.gemspec ] + Dir[ '{lib,test,example}/**/*.{rb,yml,txt,slim}' ]
24
+ s.files = %w[ LICENSE README.md CHANGELOG.md Rakefile .yardopts form_input.gemspec ] + Dir[ '{lib,test,example}/**/*.{rb,yml,txt,slim}' ]
25
25
 
26
26
  s.required_ruby_version = '>= 2.0.0'
27
27
  s.add_runtime_dependency 'rack', '>= 1.5', '< 3.0'
@@ -49,6 +49,9 @@ class FormInput
49
49
  match_msg: "%p like this is not valid",
50
50
  }
51
51
 
52
+ # Parameter options which can be merged together into an array when multiple option hashes are merged.
53
+ MERGEABLE_OPTIONS = [ :check, :test ]
54
+
52
55
  # Form parameter.
53
56
  class Parameter
54
57
 
@@ -336,12 +339,22 @@ class FormInput
336
339
 
337
340
  # Report an error concerning this parameter.
338
341
  # String %p in the message is automatically replaced with error title.
342
+ # In case of multiple errors, the message is added to the end of the list, making it less important than the other errors.
339
343
  # Returns self for chaining.
340
344
  def report( msg, *args )
341
345
  form.report( name, format_error_message( msg, *args ) ) if form
342
346
  self
343
347
  end
344
348
 
349
+ # Report an error concerning this parameter.
350
+ # String %p in the message is automatically replaced with error title.
351
+ # In case of multiple errors, the message is added to the beginning of the list, making it more important than the other errors.
352
+ # Returns self for chaining.
353
+ def report!( msg, *args )
354
+ form.report!( name, format_error_message( msg, *args ) ) if form
355
+ self
356
+ end
357
+
345
358
  end
346
359
 
347
360
  include LocaleMethods
@@ -373,10 +386,10 @@ class FormInput
373
386
  validate_value( value )
374
387
  end
375
388
 
376
- # Finally, invoke the custom check callback if there is any.
389
+ # Finally, invoke the custom check callbacks if there are any.
377
390
 
378
- if check = opts[ :check ]
379
- instance_exec( &check )
391
+ if checks = opts[ :check ]
392
+ [ *checks ].each{ |x| instance_exec( &x ) }
380
393
  end
381
394
  end
382
395
 
@@ -512,10 +525,10 @@ class FormInput
512
525
  return
513
526
  end
514
527
 
515
- # Finally, invoke the custom callback if there is any.
528
+ # Finally, invoke the custom callbacks if there are any.
516
529
 
517
- if test = opts[ :test ]
518
- instance_exec( value, &test )
530
+ if tests = opts[ :test ]
531
+ [ *tests ].each{ |x| instance_exec( value, &x ) }
519
532
  return unless valid?
520
533
  end
521
534
 
@@ -662,7 +675,7 @@ class FormInput
662
675
  size = args.shift if args.first.is_a? Numeric
663
676
 
664
677
  opts = {}
665
- opts.merge!( args.shift ) while args.first.is_a? Hash
678
+ opts.merge!( args.shift ){ |k, o, n| ( n && MERGEABLE_OPTIONS.include?( k ) ) ? [ *o, *n ] : n } while args.first.is_a? Hash
666
679
 
667
680
  fail ArgumentError, "invalid arguments #{args}" unless args.empty?
668
681
 
@@ -1099,6 +1112,7 @@ class FormInput
1099
1112
  end
1100
1113
 
1101
1114
  # Remember error concerning given parameter.
1115
+ # In case of multiple errors, the message is added to the end of the list, making it less important than the other errors.
1102
1116
  # Returns self for chaining.
1103
1117
  def report( name, msg )
1104
1118
  validate?
@@ -1106,6 +1120,15 @@ class FormInput
1106
1120
  self
1107
1121
  end
1108
1122
 
1123
+ # Remember error concerning given parameter.
1124
+ # In case of multiple errors, the message is added to the beginning of the list, making it more important than the other errors.
1125
+ # Returns self for chaining.
1126
+ def report!( name, msg )
1127
+ validate?
1128
+ ( @errors[ name ] ||= [] ).unshift( msg.to_s.dup.freeze )
1129
+ self
1130
+ end
1131
+
1109
1132
  # Get list of errors for given parameter. Returns empty list if there were no errors.
1110
1133
  def errors_for( name )
1111
1134
  errors[ name ] || []
@@ -338,22 +338,22 @@ class FormInput
338
338
  finished_step?( step ) and incorrect_step?( step )
339
339
  end
340
340
 
341
- # Get steps which shell be displayed as correct.
341
+ # Get steps which shall be displayed as correct.
342
342
  def good_steps
343
343
  steps.select{ |step| good_step?( step ) }
344
344
  end
345
345
 
346
- # Get steps which shell be displayed as incorrect.
346
+ # Get steps which shall be displayed as incorrect.
347
347
  def bad_steps
348
348
  steps.select{ |step| bad_step?( step ) }
349
349
  end
350
350
 
351
- # Test if given/current step shell be displayed as correct.
351
+ # Test if given/current step shall be displayed as correct.
352
352
  def good_step?( step = self.step )
353
353
  complete_step?( step ) and filled_step?( step ) and regular_step?( step )
354
354
  end
355
355
 
356
- # Test if given/current step shell be displayed as incorrect.
356
+ # Test if given/current step shall be displayed as incorrect.
357
357
  def bad_step?( step = self.step )
358
358
  incomplete_step?( step )
359
359
  end
@@ -3,7 +3,7 @@
3
3
  class FormInput
4
4
  module Version
5
5
  MAJOR = 1
6
- MINOR = 0
6
+ MINOR = 1
7
7
  PATCH = 0
8
8
  STRING = [ MAJOR, MINOR, PATCH ].join( '.' ).freeze
9
9
  end
data/test/helper.rb CHANGED
@@ -6,16 +6,15 @@ def jruby?
6
6
  defined?( RUBY_ENGINE ) and RUBY_ENGINE == 'jruby'
7
7
  end
8
8
 
9
- if ENV[ 'COVERAGE' ]
10
- require 'simplecov'
11
- SimpleCov.start
12
- end
13
-
14
9
  begin
15
10
  require 'codeclimate-test-reporter'
16
- CodeClimate::TestReporter.start
17
11
  ENV[ 'COVERAGE' ] = 'on'
18
12
  rescue LoadError
19
13
  end unless jruby?
20
14
 
15
+ if ENV[ 'COVERAGE' ]
16
+ require 'simplecov'
17
+ SimpleCov.start
18
+ end
19
+
21
20
  # EOF #
data/test/test_core.rb CHANGED
@@ -380,6 +380,55 @@ describe FormInput do
380
380
  f.error_messages.should == [ "s may have at most 5 characters" ]
381
381
  end
382
382
 
383
+ should 'support reasonable option merging' do
384
+ c = Class.new( FormInput )
385
+ c.param :p, { max_size: 2 }, { min_size: 2, max_size: 3 }, { max_size: 4 }
386
+ c[ :p ][ :min_size ].should == 2
387
+ c[ :p ][ :max_size ].should == 4
388
+
389
+ c = Class.new( FormInput )
390
+ c.param :p, { tag: :foo }, { tag: :bar }, { tags: [ :x, :y ] }
391
+ c.param :q, { tag: :foo }, { tag: :bar }, { tag: nil }, { tags: [ :x, :y ] }
392
+ c.param :r, { tag: :foo }, { tag: :bar }, { tags: [ :x, :y ] }, { tags: nil }
393
+ c[ :p ].tags.should == [ :bar, :x, :y ]
394
+ c[ :q ].tags.should == [ :x, :y ]
395
+ c[ :r ].tags.should == [ :bar ]
396
+
397
+ c = Class.new( FormInput )
398
+ c.param :p, { tags: :foo }, { tags: :bar }, { tags: [ :x, :y ] }
399
+ c.param :q, { tags: :foo }, { tags: :bar }, { tags: nil }, { tags: [ :x, :y ] }
400
+ c.param :r, { tags: :foo }, { tags: :bar }, { tags: [ :x, :y ] }, { tags: nil }
401
+ c[ :p ].tags.should == [ :x, :y ]
402
+ c[ :q ].tags.should == [ :x, :y ]
403
+ c[ :r ].tags.should == []
404
+
405
+ c = Class.new( FormInput )
406
+ c.param :p, { check: ->{ report( "A" ) } }, { check: ->{ report( "B" ) } }
407
+ c.param :q, { check: ->{ report( "A" ) } }, { check: nil }, { check: ->{ report( "B" ) } }
408
+ c.param :r, { check: ->{ report( "A" ) } }, { check: ->{ report( "B" ) } }, { check: nil }
409
+ c.new( p: "x" ).errors_for( :p ).should == [ "A", "B" ]
410
+ c.new( q: "x" ).errors_for( :q ).should == [ "B" ]
411
+ c.new( r: "x" ).errors_for( :q ).should == []
412
+
413
+ c = Class.new( FormInput )
414
+ c.param :p, { test: ->( value ){ report( "A" ) } }, { test: ->( value ){ report( "B" ) } }
415
+ c.param :q, { test: ->( value ){ report( "A" ) } }, { test: nil }, { test: ->( value ){ report( "B" ) } }
416
+ c.param :r, { test: ->( value ){ report( "A" ) } }, { test: ->( value ){ report( "B" ) } }, { test: nil }
417
+ c.new( p: "x" ).errors_for( :p ).should == [ "A", "B" ]
418
+ c.new( q: "x" ).errors_for( :q ).should == [ "B" ]
419
+ c.new( r: "x" ).errors_for( :q ).should == []
420
+
421
+ for name in [:reject, :match, :match_key]
422
+ c = Class.new( FormInput )
423
+ c.param :p, { name => /\d/ }, { name => /[A-Z]/ }
424
+ c.param :q, { name => /\d/ }, { name => nil }, { name => /[A-Z]/ }
425
+ c.param :r, { name => /\d/ }, { name => /[A-Z]/ }, { name => nil }
426
+ c[ :p ][ name ].should == /[A-Z]/
427
+ c[ :q ][ name ].should == /[A-Z]/
428
+ c[ :r ][ name ].should == nil
429
+ end
430
+ end
431
+
383
432
  should 'convert to/from internal value format' do
384
433
  c = Class.new( FormInput )
385
434
  c.param :str
@@ -1224,6 +1273,19 @@ describe FormInput do
1224
1273
  f[].errors_for( :password ).should == [ "bad" ]
1225
1274
  f[].error_for( :password ).should == "bad"
1226
1275
 
1276
+ f = TestForm.new.report!( :query, "important" ).report( :query, "less important" )
1277
+ f.errors.should == { query: [ "important", "q is required", "less important" ] }
1278
+ f.error_messages.should == [ "important" ]
1279
+ f.errors_for( :query ).should == [ "important", "q is required", "less important" ]
1280
+ f.error_for( :query ).should == "important"
1281
+
1282
+ f = TestForm.new
1283
+ f.param( :query ).report( "less important" ).report!( "important" )
1284
+ f.errors.should == { query: [ "important", "q is required", "less important" ] }
1285
+ f.error_messages.should == [ "important" ]
1286
+ f.errors_for( :query ).should == [ "important", "q is required", "less important" ]
1287
+ f.error_for( :query ).should == "important"
1288
+
1227
1289
  f = TestForm.new
1228
1290
  f.should.be.invalid
1229
1291
  f.set( query: "x" ).should.be.valid
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.0.0
4
+ version: 1.1.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: 2016-08-03 00:00:00.000000000 Z
11
+ date: 2018-03-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -86,6 +86,7 @@ extensions: []
86
86
  extra_rdoc_files: []
87
87
  files:
88
88
  - ".yardopts"
89
+ - CHANGELOG.md
89
90
  - LICENSE
90
91
  - README.md
91
92
  - Rakefile