rack-params 0.0.1.pre5 → 0.0.1.pre6

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: 1007badc8d33a5465e1d7facda42c81901f4b5ad
4
- data.tar.gz: a0d6fb178dc571d748ee5bb452db6a79502c8b15
3
+ metadata.gz: 1b8f9f809d52259627ff17cd8f7ac0950d239bd0
4
+ data.tar.gz: fe7397ffc2e416cac5984e88acdd9d9266d00220
5
5
  SHA512:
6
- metadata.gz: 6f9d9e7469c1855f94b40b151c27a1d07ea1872d1c1c0dbd81addd5ead29dfae38a91df31af3378498b15a78e2a94f2cbb3809c38059bbf0a0fb3ffe8a677cf2
7
- data.tar.gz: ac8475ffbc12725fb09b60890a225baa00037d9a37ee8c77e6fb8b5e0fb110a8443d11c27c68e87b9bc324c00b759c9bc5bd4a7621965ba82c67ed6fe624f77a
6
+ metadata.gz: 7927e8dd9ea5d6e0d0c047fd62a2d9e582e32f7a072c607aee42db9a0ea5dfda2db7309dbd30f72b5831665769fb9813584cb8feb0542275f2d7dd683e9662eb
7
+ data.tar.gz: 941558bf50cfd8cb34c5cdf0d08493a1df7673f9246251ccf028998c0f0973c5cef769b40c3207f83190f67b73e326d5788c6e138d96cba6875c899eca9d97d7
data/.gitignore CHANGED
@@ -3,6 +3,7 @@
3
3
  /_yardoc/
4
4
  /coverage/
5
5
  /docs/
6
+ /doc/
6
7
  /pkg/
7
8
  /spec/reports/
8
9
  /tmp/
@@ -12,3 +13,4 @@
12
13
  spec/examples.txt
13
14
  /.byebug_history
14
15
  /Gemfile.lock
16
+ /CHANGELOG.html
@@ -0,0 +1,26 @@
1
+ # Changelog
2
+ All notable changes to `Rack::Params` will be documented in this file.
3
+
4
+ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
5
+ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
6
+
7
+ ## [Unreleased]
8
+
9
+ This is a pretty major refactor, but since we're not really released
10
+ at all yet, I'm just including highlights in the changelog - check the
11
+ diff for explicit changes, but so much got moved around, I'm not sure
12
+ that'll be easy. Such are the perogotives of personal projects :)
13
+
14
+ * added a changelog!
15
+ * `#param` and `#every` now take a block for any type, which is passed
16
+ the value for transformation (think #map), which makes the
17
+ Hash/Array handling less special (though still special, since
18
+ they're recursed, not transformed given the block).
19
+ * extracted the `Validator` module from `Context`; easier to test,
20
+ makes more sense architecturally (context is only concerned with
21
+ being `self` for the block), and allows the use of the validation
22
+ helpers elsewhere.
23
+ * much more robust specs.
24
+
25
+ ### [0.0.1.pre5]
26
+ - all basic functionality
data/README.md CHANGED
@@ -3,6 +3,17 @@
3
3
 
4
4
  `Rack::Request.params` validation and type coercion, on Rack.
5
5
 
6
+ ## Usage
7
+
8
+ **[Documentation](https://lygaret.github.io/rack-params/)**
9
+
10
+ 1. Include `Rack::Params` to get the `.validator`, `#validate` and `#validate!` methods.
11
+ 2. Call `.validator(name, options = {}, &code)` to register a named validator for use later.
12
+ 3. Call `#validate(name = nil, params = request.params, options = {}, &code)` to build a new result, with the results of validation and coercion.
13
+ 4. The blocks passed to the validation methods run in the context of `HashContext` and `ArrayContext`, which is where the coercion methods are defined.
14
+
15
+ ## Example
16
+
6
17
  ```ruby
7
18
  # NOTE (to self) - if this changes, update `readme_spec.rb`
8
19
 
@@ -16,8 +27,8 @@ class SomeExampleApp
16
27
  param :title, String, required: true
17
28
  param :created, DateTime
18
29
 
19
- param :tags, Array, sep: " " do
20
- every Symbol
30
+ param :tags, Array do
31
+ every :symbol
21
32
  end
22
33
 
23
34
  param :content, Hash, required: true do
@@ -106,15 +117,6 @@ Or install it yourself as:
106
117
 
107
118
  $ gem install rack-params
108
119
 
109
- ## Usage
110
-
111
- **[RDoc @ master - Rack::Params](http://www.rubydoc.info/github/lygaret/rack-params/master)**
112
-
113
- 1. Include `Rack::Params` to get the `.validator`, `#validate` and `#validate!` methods.
114
- 2. Call `.validator(name, options = {}, &code)` to register a named validator for use later.
115
- 3. Call `#validate(name = nil, params = request.params, options = {}, &code)` to build a new result, with the results of validation and coercion.
116
- 4. The blocks passed to the validation methods run in the context of `HashContext` and `ArrayContext`, which is where the coercion methods are defined.
117
-
118
120
  ## Development
119
121
 
120
122
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
data/Rakefile CHANGED
@@ -8,7 +8,7 @@ require "coveralls/rake/task"
8
8
 
9
9
  import(*Dir.glob("./lib/rack/params/**/*.rake"))
10
10
 
11
- CLOBBER << "docs"
11
+ CLOBBER << "doc"
12
12
  CLOBBER << "coverage"
13
13
 
14
14
  RSpec::Core::RakeTask.new(:spec)
@@ -1,4 +1,8 @@
1
1
  require 'rack/params/context'
2
+ require 'rack/params/context/hash_context'
3
+ require 'rack/params/context/array_context'
4
+
5
+ require 'rack/params/connector'
2
6
  require 'rack/params/errors'
3
7
  require 'rack/params/result'
4
8
  require 'rack/params/version'
@@ -6,8 +10,8 @@ require 'rack/params/version'
6
10
  module Rack
7
11
 
8
12
  # Rack::Params provides a lightweight DSL for type coercion and validation of request parameters.
9
- # @!parse extend Rack::Params::ClassMethods
10
13
  module Params
14
+ # @!parse extend Rack::Params::ClassMethods
11
15
 
12
16
  # @private
13
17
  def self.included(base)
@@ -15,15 +19,13 @@ module Rack
15
19
  end
16
20
 
17
21
  module ClassMethods
18
- # holds options and a block when registering a validator.
19
22
  # @private
20
23
  Validator = Struct.new(:options, :code) do
21
- def exec(values)
22
- HashContext.new(values, options).exec(&code)
24
+ def exec(values, **options)
25
+ Rack::Params::Context.exec(values, options, &code)
23
26
  end
24
27
  end
25
28
 
26
- # get the set of validators, keyed by name
27
29
  # @private
28
30
  def validators
29
31
  @_rp_validators ||= {}
@@ -56,12 +58,12 @@ module Rack
56
58
  name = nil
57
59
  end
58
60
 
59
- fail ArgumentError, "no parameters provided!" if params.nil?
61
+ fail "no parameters provided!" if params.nil?
60
62
  if name.nil?
61
- fail ArgumentError, "no validation block was provided!" unless block_given?
62
- HashContext.new(params, options).exec(&block)
63
+ fail "no validation block was provided!" unless block_given?
64
+ Rack::Params::Context.exec(params, **options, &block)
63
65
  else
64
- fail ArgumentError, "no validation is registered under #{name}" unless self.class.validators.key? name
66
+ fail "no validation is registered under #{name}" unless self.class.validators.key? name
65
67
  self.class.validators[name].exec(params)
66
68
  end
67
69
  end
@@ -85,49 +87,5 @@ module Rack
85
87
  fail ParameterValidationError, res.errors if res.invalid?
86
88
  end
87
89
  end
88
-
89
- # mixin for frameworks that have a {#request} method in scope
90
- # make sure you `include Rack::Params` before including
91
- # @!attribute [r] params
92
- # @return [Result] the validated params, including errors
93
- module Connector
94
- def self.included(base)
95
- base.class_eval do
96
- attr_reader :params
97
- end
98
- end
99
-
100
- # validates {Rack::Request#params} against a validator.
101
- # @overload validate(name, options = {})
102
- # @param [Symbol] name the name of a registered validator.
103
- # @param [Hash] options
104
- # @return [Result] a result hash containing the extracted keys, and any errors.
105
- # @raise [ParameterValidationError] if the parameters are invalid after validation and coercion
106
- # @overload validate(options = {}, &block)
107
- # validates the given parameters against the given block
108
- # @param [Hash] options
109
- # @yield a code block that will run in the context of a {Rack::Params::HashContext} to validate the params
110
- # @return [Result] a result hash containing the extracted keys, and any errors.
111
- # @raise [ParameterValidationError] if the parameters are invalid after validation and coercion
112
- def validate(name = nil, params = nil, options = {}, &block)
113
- super(name, params || request.params, options, &block)
114
- end
115
-
116
- # validates {Rack::Request#params} against a validator, raising on errors.
117
- # @overload validate!(name, options = {})
118
- # @param [Symbol] name the name of a registered validator.
119
- # @param [Hash] options
120
- # @return [Result] a valid result hash containing the extracted keys, and no errors.
121
- # @raise [ParameterValidationError] if the parameters are invalid after validation and coercion
122
- # @overload validate!(options = {}, &block)
123
- # validates the given parameters against the given block
124
- # @param [Hash] options
125
- # @yield a code block that will run in the context of a {Rack::Params::HashContext} to validate the params
126
- # @return [Result] a valid result hash containing the extracted keys, and no errors.
127
- # @raise [ParameterValidationError] if the parameters are invalid after validation and coercion
128
- def validate!(name = nil, params = nil, options = {}, &block)
129
- super(name, params || request.params, options, &block)
130
- end
131
- end
132
90
  end
133
91
  end
@@ -0,0 +1,49 @@
1
+ module Rack
2
+ module Params
3
+
4
+ # mixin for frameworks that have a {#request} method in scope
5
+ # make sure you `include Rack::Params` before including
6
+ # @!attribute [r] params
7
+ # @return [Result] the validated params, including errors
8
+ module Connector
9
+ def self.included(base)
10
+ base.class_eval do
11
+ attr_reader :params
12
+ end
13
+ end
14
+
15
+ # validates {Rack::Request#params} against a validator.
16
+ # @overload validate(name, options = {})
17
+ # @param [Symbol] name the name of a registered validator.
18
+ # @param [Hash] options
19
+ # @return [Result] a result hash containing the extracted keys, and any errors.
20
+ # @raise [ParameterValidationError] if the parameters are invalid after validation and coercion
21
+ # @overload validate(options = {}, &block)
22
+ # validates the given parameters against the given block
23
+ # @param [Hash] options
24
+ # @yield a code block that will run in the context of a {Rack::Params::HashContext} to validate the params
25
+ # @return [Result] a result hash containing the extracted keys, and any errors.
26
+ # @raise [ParameterValidationError] if the parameters are invalid after validation and coercion
27
+ def validate(name = nil, params = nil, options = {}, &block)
28
+ super(name, params || request.params, options, &block)
29
+ end
30
+
31
+ # validates {Rack::Request#params} against a validator, raising on errors.
32
+ # @overload validate!(name, options = {})
33
+ # @param [Symbol] name the name of a registered validator.
34
+ # @param [Hash] options
35
+ # @return [Result] a valid result hash containing the extracted keys, and no errors.
36
+ # @raise [ParameterValidationError] if the parameters are invalid after validation and coercion
37
+ # @overload validate!(options = {}, &block)
38
+ # validates the given parameters against the given block
39
+ # @param [Hash] options
40
+ # @yield a code block that will run in the context of a {Rack::Params::HashContext} to validate the params
41
+ # @return [Result] a valid result hash containing the extracted keys, and no errors.
42
+ # @raise [ParameterValidationError] if the parameters are invalid after validation and coercion
43
+ def validate!(name = nil, params = nil, options = {}, &block)
44
+ super(name, params || request.params, options, &block)
45
+ end
46
+ end
47
+
48
+ end
49
+ end
@@ -1,12 +1,14 @@
1
- require 'date'
1
+ require 'rack/params/validator'
2
2
 
3
3
  module Rack
4
4
  module Params
5
5
 
6
- # the context in which to run validation.
6
+ # the validator to run.
7
7
  # it contains the {#params} and {#result}, and provides methods to use in coercion.
8
8
  # @abstract To subclass, provide a {Result} constant, and methods to do coercion/validation.
9
- class Context
9
+ class Context
10
+ include Rack::Params::Validator
11
+
10
12
  attr_reader :options
11
13
  attr_reader :params
12
14
  attr_reader :result
@@ -15,7 +17,11 @@ module Rack
15
17
  # the actual {#result} will be this type extended by {Rack::Params::Result}
16
18
  Result = nil
17
19
 
18
- # create a context with a parameter hash and options.
20
+ def self.exec(params, **options, &block)
21
+ HashContext.new(params, options).exec(&block)
22
+ end
23
+
24
+ # create a validator with a parameter hash and options.
19
25
  def initialize(params, options = {})
20
26
  @options = options
21
27
  @path = options[:path]
@@ -24,193 +30,55 @@ module Rack
24
30
 
25
31
  @result = self.class::Result.new
26
32
  @result.extend ::Rack::Params::Result
27
- @result.errors = Hash.new { |h, k| h[k] = [] }
33
+ @result.errors = ::Hash.new { |h, k| h[k] = [] }
28
34
  end
29
35
 
30
- # execute the given block, in this context
36
+ # execute the given block, in this validator
31
37
  # @yield in the block, {self}'s methods are available.
32
- # @return [Result] the result of this context's validation.
38
+ # @return [Result] the result of validation
33
39
  def exec(&block)
34
40
  instance_exec(&block)
35
41
  @result
36
42
  end
37
43
 
38
- protected
39
-
40
- # return a correctly typed value, given a string and a type.
41
- #
42
- # == valid types
43
- # :symbol :: convert value as a symbol
44
- # :boolean :: parse the following strings: "0, 1, false, f, true, t, no, n, yes, y"
45
- # Symbol ::
46
- # Int ::
47
- # Float ::
48
- # Date ::
49
- # Time ::
50
- # DateTime :: parse as the given type.
51
- # Array :: parse value as an array. default options { sep: ' ' }
52
- # Hash :: parse value as a Hash, default options { esep: ',', fsep: ':' }
53
- #
54
- # @param value the value to coerce, likely a string, hash or array
55
- # @param type the type to coerce into
56
- # @param options [Hash]
57
- # @return value the coerced value
58
- # @raise [ArgumentError] if coercion fails
59
- def _coerce(value, type, options)
60
- return nil if value.nil?
61
- return value if value.is_a?(type) rescue false
62
-
63
- return value.to_sym if type == :symbol || type == Symbol
64
-
65
- return Integer(value, options[:base] || 0) if type == ::Integer
66
- return Float(value) if type == ::Float
67
-
68
- [::Date, ::Time, ::DateTime].each do |klass|
69
- return klass.parse(value) if type == klass
70
- end
71
-
72
- if type == ::Array
73
- sep = options.fetch(:sep, ',')
74
-
75
- values = value.split(sep)
76
- return Array(values)
77
- end
78
-
79
- if type == ::Hash
80
- esep = options.fetch(:esep, ',')
81
- fsep = options.fetch(:fsep, ':')
82
-
83
- values = value.split(esep).map { |p| p.split(fsep) }
84
- return ::Hash[values]
85
- end
86
-
87
- if type == ::TrueClass || type == ::FalseClass || type == :boolean
88
- return false if /^(false|f|no|n|0)$/i === value
89
- return true if /^(true|t|yes|y|1)$/i === value
90
- raise ArgumentError # otherwise
91
- end
92
-
93
- # default failure
94
- raise ArgumentError, "unknown type #{type}"
95
- end
96
-
97
- # todo: figure this out
98
- # def _validate(key, value, validation, options)
99
- # options = {} if options == true
100
- # end
101
-
102
- # recursively process parameters, so we can support validating nested
103
- # parameter hashes and arrays.
44
+ # yields the value to the block, according to the type.
45
+ # @param [String] key current params path segment
46
+ # @param value to yield to the block
47
+ # @param type of value, special handling for recursible types (Hash | Array)
104
48
  #
105
- # @param path [String] the current path to the recursing object, used to provide error keys
106
- # @param type must be {Array} or {Hash}
107
- # @return [Result] with validation results and errors
108
- def _recurse(path, type, value, &block)
109
- path = [@path, path].reject(&:nil?).join(".")
110
-
111
- if type == Array
112
- ArrayContext.new(value, path: path).exec(&block)
113
- elsif type == Hash
114
- HashContext.new(value, path: path).exec(&block)
115
- else
116
- fail "can not recurse into #{type}"
117
- end
118
- end
119
- end
120
-
121
- # the DSL for validating a hash parameter (including the top-level params hash)
122
- class HashContext < Context
123
- Result = Hash
124
-
125
- # do type coercion and validation for a parameter with the given key.
126
- # adds the coerced value to the result hash, or push an error.
49
+ # @overload _yield(key, value, type, options = {}, &block)
50
+ # returns the result of yielding the value to the block
51
+ # @param [Hash] options, same as {#_fetch}
52
+ # @return [Array] tuple of result of yielding the value to the block, and errors
127
53
  #
128
- # @see #_coerce #_coerce defines valid types for coercion.
129
- # @param key the key in the hash of the parameter to validate
130
- # @param type the type to coerce the value into
131
- # @param options [Hash]
132
- # @yield
133
- # if type is {Hash} or {Array}, passing a block will recursively
134
- # validate the coerced value, assuming the block is a new validation
135
- # context.
136
- # @return the coerced value
137
- def param(key, type, options = {}, &block)
138
- key = key.to_s
139
-
140
- # default and required
141
- value = params[key] || options[:default]
142
- raise ArgumentError, "is required" if options[:required] && value.nil?
143
-
144
- # type cast
145
- value = _coerce(value, type, options)
146
-
147
- # validate against rules
148
- # options.each { |v, vopts| _validate(key, value, v, vopts) }
149
-
150
- # recurse if we've got a block
151
- if block_given?
152
- value = _recurse(key, type, value, &block)
153
- result.errors.merge! value.errors
54
+ # @overload _yield(key, value, type = Hash | Array, options = {}, &block)
55
+ # recursively validates the value through the block given, which is run
56
+ # inside a suitable validation context; also merges result errors.
57
+ # @param [Hash] options, same as {#initialize}
58
+ # @return [Array] tuple of validated results
59
+ def _yield(key, value, type, **options, &block)
60
+ # simple types
61
+ unless type == ::Array || type == ::Hash
62
+ return super(value, **options, &block)
154
63
  end
155
64
 
156
- # return - we're good
157
- result[key] = value
158
- rescue ArgumentError => ex
159
- path = [@path, key].reject(&:nil?).join(".")
160
- result.errors[path] << ex.message
161
- nil
162
- end
163
-
164
- # collect uncoerced keys from the parameter hash into the results hash.
165
- # collects keys not _yet_ coerced, so it should be last in the validation block.
166
- #
167
- # @param key the result key under which to place the collected parameters
168
- # @param options [Hash]
169
- # @return the collected keys as a hash
170
- def splat(key, options = {})
171
- key = key.to_s
65
+ # recursive types
66
+ fail "no block provided" if block.nil?
67
+ path = [@path, key].reject(&:nil?).join(".")
68
+ values =
69
+ if type == ::Array
70
+ Rack::Params::Context::ArrayContext.new(value, path: path).exec(&block)
71
+ elsif type == ::Hash
72
+ Rack::Params::Context::HashContext.new(value, path: path).exec(&block)
73
+ else
74
+ fail "cannot recurse into #{type}"
75
+ end
172
76
 
173
- # every key in params that's not already in results
174
- value = params.reject { |k, _| result.keys.include? k }
175
- result[key] = value
77
+ # this is special, we merge errors
78
+ result.errors.merge! values.errors
79
+ return values
176
80
  end
177
81
  end
178
82
 
179
- # the DSL for validating an array parameter
180
- class ArrayContext < Context
181
- Result = Array
182
-
183
- # validate and coerce every element in the array, using the same values.
184
- # equivalent to {HashContext#param} over every element.
185
- #
186
- # @see HashContext#param
187
- # @see #_coerce #_coerce defines valid types for coercion.
188
- # @param type the type to use for coercion
189
- # @param options [Hash]
190
- # @yield
191
- # if type is Hash or Array, passing a block will recursively
192
- # validate the coerced value, assuming the block is a new validation
193
- # context.
194
- # @return the coerced value
195
- def every(type, options = {}, &block)
196
- params.each_with_index do |value, i|
197
- begin
198
- value = _coerce(value, type, options)
199
- # options.each { |v, vopts| _validate(i.to_s, value, v, vopts) }
200
- if block_given?
201
- value = _recurse(i.to_s, type, value, &block)
202
- result.errors.merge! value.errors
203
- end
204
-
205
- result[i] = value
206
- rescue ArgumentError => ex
207
- path = [@path, i].reject(&:nil?).join(".")
208
- result.errors[path] << ex.message
209
- nil
210
- end
211
- end
212
- end
213
-
214
- end
215
83
  end
216
84
  end
@@ -0,0 +1,44 @@
1
+ require 'rack/params/validator'
2
+
3
+ module Rack
4
+ module Params
5
+ class Context
6
+
7
+ # the DSL for validating an array parameter
8
+ class ArrayContext < Context
9
+ Result = ::Array
10
+
11
+ # validate and coerce every element in the array, using the same values.
12
+ # equivalent to {HashContext#param} over every element.
13
+ #
14
+ # @see HashContext#param
15
+ # @see #_coerce #_coerce defines valid types for coercion.
16
+ # @param type the type to use for coercion
17
+ # @param options [Hash]
18
+ # @yield
19
+ # if type is Hash or Array, passing a block will recursively
20
+ # validate the coerced value, assuming the block is a new validation
21
+ # context.
22
+ # @return the coerced value
23
+ def every(type, **options, &block)
24
+ options[:required] = true unless options.key?(:required)
25
+
26
+ params.each_with_index do |value, i|
27
+ begin
28
+ value = _coerce(value, type, options)
29
+ value = _yield(i.to_s, value, type, options, &block) unless block.nil?
30
+
31
+ # options.each { |v, vopts| _validate(i.to_s, value, v, vopts) }
32
+
33
+ result[i] = value
34
+ rescue ArgumentError => ex
35
+ path = [@path, i].reject(&:nil?).join(".")
36
+ result.errors[path] << ex.message
37
+ nil
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,89 @@
1
+ require 'rack/params/context'
2
+
3
+ module Rack
4
+ module Params
5
+ class Context
6
+
7
+ # the DSL for validating a hash parameter (including the top-level params hash)
8
+ class HashContext < Context
9
+ Result = ::Hash
10
+
11
+ # return the value from the params hash, validating it's presence from options
12
+ # @param key the name of the param to fetch
13
+ # @param [Hash] options
14
+ # @option options [Any] :default the value to return if the param is missing
15
+ # @option options [Boolean] :required will fail if key is missing from params
16
+ # @option options [Boolean] :allow_nil
17
+ # is nil considered present? for :required?,
18
+ # defaults to false, considered missing.
19
+ # @option options [Boolean] :allow_blank
20
+ # is blank considered present? for :required?,
21
+ # defaults to false, considered missing.
22
+ # @see #_blank?
23
+ def _fetch(key, options)
24
+ value = params.key?(key) ? params[key] : options[:default]
25
+ options[:required] ? _ensure(value, options) : value
26
+ end
27
+
28
+ # do type coercion and validation for a parameter with the given key.
29
+ # adds the coerced value to the result hash, or push an error.
30
+ #
31
+ # @param [String] key the key in the parameter hash
32
+ # @param [Hash] options
33
+ # @option options [Boolean] :required will fail if key is missing from params
34
+ # @option options [Any] :default the value to return if the param is missing
35
+ # @option options [Boolean] :allow_nil is nil considered present?, defaults to false, considered missing.
36
+ # @option options [Boolean] :allow_blank is blank (0-length string, or nil) considered present?, defaults to false, considered missing.
37
+ # @return the coerced value
38
+ #
39
+ # @overload param(key, type = String, options = {}, &block)
40
+ # coerces the value through the block given, marking invalid on raised errors.
41
+ # if yielding to the block, the :allow_nil and :allow_blank options apply equally to the result of the block
42
+ # (use the `allow_nil` and `allow_falsey` options to change the validity behavoir)
43
+ # @option options [Boolean] :allow_falsey is falsey considered present?, defaults to false, considered missing.
44
+ # @yield to the transformation function, failure on nil, blank or ArgumentError (unless `:allow_nil` or `:allow_blank`)
45
+ # @yieldparam [String|nil] value from the param hash to validate/coerce
46
+ # @yieldreturn the value that's been validated/coerced
47
+ # @see #_coerce #_coerce defines valid types for coercion.
48
+ #
49
+ # @overload param(key, type = Hash | Array, options = {}, &block)
50
+ # recursively validates the value through the block given, which
51
+ # is run in the context of a suitable validation context.
52
+ # @yield
53
+ # if type is {Hash} or {Array}, passing a block will recursively
54
+ # validate the coerced value, assuming the block is a new validation
55
+ # context. if type is not {Hash} or {Array}, raises `ArgumentError`
56
+ def param(key, type = String, **options, &block)
57
+ key = key.to_s
58
+ value = _fetch(key, options)
59
+ value = _coerce(value, type, options)
60
+ value = _yield(key, value, type, options, &block) unless block.nil?
61
+
62
+ # validate against rules
63
+ # options.each { |v, vopts| _validate(key, value, v, vopts) }
64
+
65
+ result[key] = value
66
+ rescue ArgumentError => ex
67
+ path = [@path, key].reject(&:nil?).join(".")
68
+ result.errors[path] << ex.message
69
+ nil
70
+ end
71
+
72
+ # collect uncoerced keys from the parameter hash into the results hash.
73
+ # collects keys not _yet_ coerced, so it should be last in the validation block.
74
+ #
75
+ # @param key the result key under which to place the collected parameters
76
+ # @param options [Hash]
77
+ # @return the collected keys as a hash
78
+ def splat(key, options = {})
79
+ key = key.to_s
80
+
81
+ # every key in params that's not already in results
82
+ value = params.reject { |k, _| result.keys.include? k }
83
+ result[key] = value
84
+ end
85
+ end
86
+
87
+ end
88
+ end
89
+ end
@@ -4,7 +4,7 @@ require 'yard/rake/yardoc_task'
4
4
 
5
5
  namespace :docs do
6
6
  ROOT_DIR = `git rev-parse --show-toplevel`.strip
7
- DOC_DIR = File.join(ROOT_DIR, 'docs')
7
+ DOC_DIR = File.join(ROOT_DIR, 'doc')
8
8
 
9
9
  YARD::Rake::YardocTask.new(:generate) do |yard|
10
10
  yard.options = ["--out", DOC_DIR]
@@ -0,0 +1,103 @@
1
+ require 'date'
2
+
3
+ module Rack
4
+ module Params
5
+
6
+ # a bunch of helpers for coercion and validation of parameter values
7
+ module Validator
8
+
9
+ # An object is blank if it's false, empty, or a whitespace string.
10
+ # For example, false, '', ' ', nil, [], and {} are all blank.
11
+ # #hattip rails' Object#blank?
12
+ # @param [Any] value to check
13
+ # @return true if blank, false otherwise
14
+ def _blank?(value)
15
+ value.respond_to?(:empty?) ? !!value.empty? : !value
16
+ end
17
+
18
+ # return a correctly typed value, given a string and a type.
19
+ #
20
+ # == valid types
21
+ # :boolean :: parse the following strings: "0, 1, false, f, true, t, no, n, yes, y"
22
+ # :symbol :: parse as a symbol, (#to_sym)
23
+ # String :: parse as a String, (#to_s)
24
+ # Int :: parse an an int, with an optional base (from options)
25
+ # Float :: parse as the given type
26
+ # #parse :: parse with a parse method on type (Date, Time, etc.)
27
+ # Array, Hash :: Rack::QueryParser already converts these, so return them directly
28
+ #
29
+ # @param value to coerce, likely a string, hash or array
30
+ # @param type to coerce into
31
+ # @param options to control coercion
32
+ # @option options :base [Number] the base to parse into for Integer
33
+ # @return value the coerced value
34
+ # @raise [ArgumentError] if coercion fails
35
+ # @raise [RuntimeError] if unsupported type given
36
+ def _coerce(value, type, options = {})
37
+ return nil if value.nil?
38
+ return value if value.is_a?(type) rescue false
39
+
40
+ return value.to_s if type == ::String
41
+ return value.to_sym if type == :symbol
42
+
43
+ return Integer(value, options[:base] || 0) if type == ::Integer
44
+ return Float(value) if type == ::Float
45
+
46
+ if type.respond_to?(:parse)
47
+ return type.parse(value)
48
+ end
49
+
50
+ if type == ::TrueClass || type == ::FalseClass || type == :boolean
51
+ return false if /^(false|f|no|n|0)$/i === value
52
+ return true if /^(true|t|yes|y|1)$/i === value
53
+ raise ArgumentError, "couldn't parse as boolean" # otherwise
54
+ end
55
+
56
+ # default failure
57
+ fail "unknown type #{type}"
58
+ end
59
+
60
+ # todo: figure this out
61
+ # def _validate(key, value, validation, options)
62
+ # options = {} if options == true
63
+ # end
64
+
65
+ # ensures value, checking for nils and blank values
66
+ # @see #_blank?
67
+ # @param value to check
68
+ # @param [Hash] options
69
+ # @option options :allow_nil [Boolean] if value is nil, return it?
70
+ # @option options :allow_blank [Boolean] if value is blank, return it?
71
+ # @raise [ArgumentError] when a value is required but not present
72
+ # @return the value, conforming to the options given
73
+ def _ensure(value, options = {})
74
+ # allow nil has to precede allow blank, as nil is blank
75
+ if options[:allow_nil] && value.nil?
76
+ return value
77
+ end
78
+
79
+ if !options[:allow_blank] && _blank?(value)
80
+ fail ArgumentError, "is required."
81
+ end
82
+
83
+ value
84
+ end
85
+
86
+ # yields the value to the block, with required semantics
87
+ # @param value to yield to the block
88
+ # @option options :required [Boolean] ensure value returned from block exists
89
+ # @option options :allow_nil [Boolean] if :required, allow nils
90
+ # @option options :allow_blank [Boolean] if :required, allow blank
91
+ # @return value after transformation
92
+ # @see _ensure
93
+ def _yield(value, **options, &block)
94
+ fail "no block provided" if block.nil?
95
+
96
+ value = block.call(value)
97
+ return options[:required] ? _ensure(value, options) : value
98
+ end
99
+
100
+ end
101
+
102
+ end
103
+ end
@@ -1,5 +1,5 @@
1
1
  module Rack
2
2
  module Params
3
- VERSION = "0.0.1.pre5"
3
+ VERSION = "0.0.1.pre6"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rack-params
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1.pre5
4
+ version: 0.0.1.pre6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jon Raphaelson
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-01-26 00:00:00.000000000 Z
11
+ date: 2018-02-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -147,6 +147,7 @@ files:
147
147
  - ".rspec"
148
148
  - ".travis.yml"
149
149
  - ".yardopts"
150
+ - CHANGELOG.md
150
151
  - CODE_OF_CONDUCT.md
151
152
  - Gemfile
152
153
  - LICENSE.txt
@@ -156,10 +157,14 @@ files:
156
157
  - bin/console
157
158
  - bin/setup
158
159
  - lib/rack/params.rb
160
+ - lib/rack/params/connector.rb
159
161
  - lib/rack/params/context.rb
162
+ - lib/rack/params/context/array_context.rb
163
+ - lib/rack/params/context/hash_context.rb
160
164
  - lib/rack/params/errors.rb
161
165
  - lib/rack/params/result.rb
162
166
  - lib/rack/params/tasks.rake
167
+ - lib/rack/params/validator.rb
163
168
  - lib/rack/params/version.rb
164
169
  - rack-params.gemspec
165
170
  homepage: https://github.com/lygaret/rack-params