form_input 1.0.0 → 1.1.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: 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