rack-params 0.0.1.pre5 → 0.0.1.pre6

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: 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