tainted_params 0.3.1
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 +7 -0
- data/README.md +83 -0
- data/lib/tainted_params/core_ext/hash.rb +28 -0
- data/lib/tainted_params/param_constraint.rb +29 -0
- data/lib/tainted_params/param_type_constraint.rb +56 -0
- data/lib/tainted_params/params_validator.rb +67 -0
- data/lib/tainted_params/params_validator_builder.rb +53 -0
- data/lib/tainted_params/simple_key_hash.rb +46 -0
- data/lib/tainted_params/string_key_hash.rb +49 -0
- data/lib/tainted_params/version.rb +3 -0
- data/lib/tainted_params.rb +12 -0
- data/spec/unit/coercing_param_type_constraint_spec.rb +28 -0
- data/spec/unit/param_type_constraint_spec.rb +35 -0
- data/spec/unit/params_validator_builder_spec.rb +25 -0
- data/spec/unit/params_validator_spec.rb +159 -0
- data/spec/unit/simple_key_hash_spec.rb +9 -0
- data/spec/unit/string_key_hash_spec.rb +19 -0
- data/spec/unit/tainted_params_spec.rb +3 -0
- metadata +125 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 65666fe19c54decad64747cd681f5c602abcdfec
|
4
|
+
data.tar.gz: ea37c8c5a85fdd7ded0a3d61bb9a86402d4dbd45
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 634c472cb70888cd2fa94e0dd025494ef0d261efd19af819edab4652d5aef48c4234b76e683d53928545fa39317d9fdbc39dcf30f76021845462ded011d83b7c
|
7
|
+
data.tar.gz: 7d7f16e9d52012d4a92921421cb5ae37decf85225d72216ade98c87f7f5e0cdbbbbe5dd45420a9c392a633425978a68bc41c3e113dea3db82e7d125cf4f5a462
|
data/README.md
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
# tainted_params
|
2
|
+
|
3
|
+
https://github.com/Ragmaanir/tainted_params
|
4
|
+
|
5
|
+
# DESCRIPTION
|
6
|
+
|
7
|
+
Behaves like strong_parameters, with some minor differences.
|
8
|
+
|
9
|
+
# FEATURES
|
10
|
+
|
11
|
+
- Validate required and optional parameters
|
12
|
+
- Validate their types
|
13
|
+
- Type coercions for parameters
|
14
|
+
|
15
|
+
# PROBLEMS/TODO
|
16
|
+
|
17
|
+
- made some monkey patches to hash(slice, only, except, map_pairs) to not having to depend on active support gem
|
18
|
+
- custom HashWithIndifferentAccess
|
19
|
+
|
20
|
+
# SYNOPSIS
|
21
|
+
|
22
|
+
builder = TaintedParams::ParamsValidatorBuilder.new do
|
23
|
+
required :id, :Integer
|
24
|
+
optional :options, :Hash do
|
25
|
+
optional :name, :String
|
26
|
+
optional :active, :Boolean
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
validator = builder.result
|
31
|
+
|
32
|
+
result = validator.validate(options: { name: 'bob', active: 1, admin: true })
|
33
|
+
|
34
|
+
assert{ result.valid == { options: { name: 'bob' } } }
|
35
|
+
assert{ result.invalid == { options: { active: 1 } } }
|
36
|
+
assert{ result.unpermitted == { options: { admin: true } } }
|
37
|
+
assert{ result.missing == { id: nil } }
|
38
|
+
|
39
|
+
# REQUIREMENTS
|
40
|
+
|
41
|
+
* Ruby >= 2
|
42
|
+
|
43
|
+
# INSTALL
|
44
|
+
|
45
|
+
* FIX
|
46
|
+
|
47
|
+
# DEVELOPERS
|
48
|
+
|
49
|
+
After checking out the source, run:
|
50
|
+
|
51
|
+
$ rake newb
|
52
|
+
|
53
|
+
This task will install any missing dependencies, run the tests/specs,
|
54
|
+
and generate the RDoc.
|
55
|
+
|
56
|
+
# INTERNALS
|
57
|
+
|
58
|
+
The ParamsValidatorBuilder constructs a ParamsValidator out of ParamConstraints. The ParamsValidator contains the validation logic to split a params-hash into valid, invalid, missing and unpermitted parts.
|
59
|
+
|
60
|
+
# LICENSE
|
61
|
+
|
62
|
+
(The MIT License)
|
63
|
+
|
64
|
+
Copyright (c) 2015 Ragmaanir
|
65
|
+
|
66
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
67
|
+
a copy of this software and associated documentation files (the
|
68
|
+
'Software'), to deal in the Software without restriction, including
|
69
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
70
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
71
|
+
permit persons to whom the Software is furnished to do so, subject to
|
72
|
+
the following conditions:
|
73
|
+
|
74
|
+
The above copyright notice and this permission notice shall be
|
75
|
+
included in all copies or substantial portions of the Software.
|
76
|
+
|
77
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
78
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
79
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
80
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
81
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
82
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
83
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
@@ -0,0 +1,28 @@
|
|
1
|
+
class Hash
|
2
|
+
def map_pairs
|
3
|
+
return enum_for(:map_pairs) unless block_given?
|
4
|
+
|
5
|
+
result = self.class.new
|
6
|
+
each do |key, value|
|
7
|
+
k, v = *yield(key, value)
|
8
|
+
result[k] = v
|
9
|
+
end
|
10
|
+
result
|
11
|
+
end
|
12
|
+
|
13
|
+
# def map_keys
|
14
|
+
# map_pairs do |k, v|
|
15
|
+
# [yield(k), v]
|
16
|
+
# end
|
17
|
+
# end
|
18
|
+
|
19
|
+
def slice(keys)
|
20
|
+
select{ |k,_| keys.include?(k) }
|
21
|
+
end
|
22
|
+
|
23
|
+
alias_method :only, :slice
|
24
|
+
|
25
|
+
def except(keys)
|
26
|
+
select{ |k,_| !keys.include?(k) }
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module TaintedParams
|
2
|
+
|
3
|
+
class ParamConstraint
|
4
|
+
|
5
|
+
attr_reader :name, :type, :nested
|
6
|
+
|
7
|
+
def initialize(name:, required:, type:, nested: nil)
|
8
|
+
raise ArgumentError unless type.is_a?(ParamTypeConstraint)
|
9
|
+
|
10
|
+
@name = name || raise(ArgumentError)
|
11
|
+
@required = required
|
12
|
+
@type = type
|
13
|
+
@nested = nested
|
14
|
+
end
|
15
|
+
|
16
|
+
def required?
|
17
|
+
@required
|
18
|
+
end
|
19
|
+
|
20
|
+
def ==(other)
|
21
|
+
case other
|
22
|
+
when ParamConstraint then name == other.name
|
23
|
+
else false
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module TaintedParams
|
2
|
+
|
3
|
+
class CoercionError < RuntimeError
|
4
|
+
end
|
5
|
+
|
6
|
+
class ParamTypeConstraint
|
7
|
+
|
8
|
+
attr_reader :types
|
9
|
+
|
10
|
+
def initialize(types)
|
11
|
+
raise(ArgumentError, "Invalid types: #{types.inspect}") unless types.is_a?(Array)
|
12
|
+
@types = types
|
13
|
+
end
|
14
|
+
|
15
|
+
def call(value)
|
16
|
+
if @types.any?{ |t| value.is_a?(t) }
|
17
|
+
value
|
18
|
+
else
|
19
|
+
:error
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
class CoercingParamTypeConstraint < ParamTypeConstraint
|
26
|
+
|
27
|
+
attr_reader :coercion
|
28
|
+
|
29
|
+
def initialize(type, coercion:)
|
30
|
+
super(type)
|
31
|
+
@coercion = coercion || raise(ArgumentError)
|
32
|
+
end
|
33
|
+
|
34
|
+
def call(value)
|
35
|
+
val = coercion.call(value)
|
36
|
+
|
37
|
+
super(val)
|
38
|
+
rescue CoercionError
|
39
|
+
:error
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
BOOLEAN_TYPE_CONSTRAINT = CoercingParamTypeConstraint.new([TrueClass, FalseClass], coercion: ->(val) {
|
45
|
+
case val
|
46
|
+
when true, *%w{true 1 yes} then true
|
47
|
+
when false, *%w{false 0 no} then false
|
48
|
+
else raise CoercionError
|
49
|
+
end
|
50
|
+
}).freeze
|
51
|
+
|
52
|
+
INTEGER_TYPE_CONSTRAINT = CoercingParamTypeConstraint.new([Integer], coercion: ->(val) {
|
53
|
+
Integer(val) rescue raise CoercionError
|
54
|
+
}).freeze
|
55
|
+
|
56
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module TaintedParams
|
2
|
+
|
3
|
+
class ValidationResult
|
4
|
+
|
5
|
+
attr_reader :valid, :invalid, :missing, :unpermitted
|
6
|
+
|
7
|
+
def initialize(valid:, invalid:, missing:, unpermitted:)
|
8
|
+
@valid = valid
|
9
|
+
@invalid = invalid
|
10
|
+
@missing = missing
|
11
|
+
@unpermitted = unpermitted
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
class ParamsValidator
|
17
|
+
|
18
|
+
def initialize(validations)
|
19
|
+
@validations = validations
|
20
|
+
end
|
21
|
+
|
22
|
+
def validate(params)
|
23
|
+
_validate(@validations, params)
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def _validate(validations, params)
|
29
|
+
raise ArgumentError unless validations.is_a?(Array)
|
30
|
+
raise ArgumentError unless params.is_a?(Hash)
|
31
|
+
|
32
|
+
unpermitted = params.except(validations.map(&:name))
|
33
|
+
invalid = {}
|
34
|
+
missing = {}
|
35
|
+
valid = {}
|
36
|
+
|
37
|
+
validations.each do |validation|
|
38
|
+
|
39
|
+
if v = params[validation.name]
|
40
|
+
res = validation.type.call(v)
|
41
|
+
|
42
|
+
if res == :error
|
43
|
+
invalid[validation.name] = v
|
44
|
+
elsif validation.nested
|
45
|
+
res = _validate(validation.nested, v)
|
46
|
+
|
47
|
+
valid[validation.name] = res.valid unless res.valid.empty?
|
48
|
+
invalid[validation.name] = res.invalid unless res.invalid.empty?
|
49
|
+
missing[validation.name] = res.missing unless res.missing.empty?
|
50
|
+
unpermitted[validation.name] = res.unpermitted unless res.unpermitted.empty?
|
51
|
+
else
|
52
|
+
valid[validation.name] = res
|
53
|
+
end
|
54
|
+
else
|
55
|
+
if validation.required?
|
56
|
+
missing[validation.name] = nil
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
ValidationResult.new(valid: valid, invalid: invalid, missing: missing, unpermitted: unpermitted)
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module TaintedParams
|
2
|
+
|
3
|
+
DEFAULT_TYPE_CONSTRAINTS = {
|
4
|
+
:Boolean => BOOLEAN_TYPE_CONSTRAINT,
|
5
|
+
:Integer => INTEGER_TYPE_CONSTRAINT,
|
6
|
+
:Hash => ParamTypeConstraint.new([Hash]),
|
7
|
+
:String => ParamTypeConstraint.new([String])
|
8
|
+
}.freeze
|
9
|
+
|
10
|
+
class ParamsValidatorBuilder
|
11
|
+
def initialize(type_constraints = DEFAULT_TYPE_CONSTRAINTS, &block)
|
12
|
+
@type_constraints = type_constraints
|
13
|
+
@validations = []
|
14
|
+
@stack = [@validations]
|
15
|
+
instance_eval(&block)
|
16
|
+
@validations
|
17
|
+
end
|
18
|
+
|
19
|
+
def required(name, type, &block)
|
20
|
+
validation(name, true, type, &block)
|
21
|
+
end
|
22
|
+
|
23
|
+
def optional(name, type, &block)
|
24
|
+
validation(name, false, type, &block)
|
25
|
+
end
|
26
|
+
|
27
|
+
def validation(name, required, type, &block)
|
28
|
+
raise(ArgumentError, "Key registered twice: #{name}") if @stack.last.map(&:name).include?(name)
|
29
|
+
|
30
|
+
type_constraint = @type_constraints[type] || raise(ArgumentError, "Could not find constraint for type #{type.inspect}")
|
31
|
+
args = {name: name, type: type_constraint, required: required}
|
32
|
+
|
33
|
+
if block
|
34
|
+
nested = []
|
35
|
+
@stack.last << ParamConstraint.new(args.merge(nested: nested))
|
36
|
+
@stack << nested
|
37
|
+
instance_eval(&block)
|
38
|
+
@stack.pop
|
39
|
+
else
|
40
|
+
@stack.last << ParamConstraint.new(args)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def raw_validations
|
45
|
+
@validations
|
46
|
+
end
|
47
|
+
|
48
|
+
def result
|
49
|
+
ParamsValidator.new(@validations)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'delegate'
|
2
|
+
|
3
|
+
module TaintedParams
|
4
|
+
class SimpleKeyHash < Hash#< SimpleDelegator
|
5
|
+
|
6
|
+
def self.stringify_keys(hash)
|
7
|
+
hash.map_pairs{ |k,v|
|
8
|
+
[k.to_s, v]
|
9
|
+
}
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(hash)
|
13
|
+
raise ArgumentError unless hash.is_a?(Hash)
|
14
|
+
#super(self.class.stringify_keys(hash))
|
15
|
+
super()
|
16
|
+
update(self.class.stringify_keys(hash))
|
17
|
+
end
|
18
|
+
|
19
|
+
def default(key = nil)
|
20
|
+
case key
|
21
|
+
when String, nil then super
|
22
|
+
when Symbol then self[key.to_s]
|
23
|
+
else raise ArgumentError
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# def fetch(key, *extras)
|
28
|
+
# super(convert_key(key), *extras)
|
29
|
+
# end
|
30
|
+
|
31
|
+
# def values_at(*indices)
|
32
|
+
# indices.collect { |key| self[convert_key(key)] }
|
33
|
+
# end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def convert_key(key)
|
38
|
+
case key
|
39
|
+
when Symbol then key.to_s
|
40
|
+
when String then key
|
41
|
+
else raise ArgumentError
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'delegate'
|
2
|
+
require 'forwardable'
|
3
|
+
|
4
|
+
module TaintedParams
|
5
|
+
class StringKeyHash# < SimpleDelegator
|
6
|
+
extend Forwardable
|
7
|
+
include Enumerable
|
8
|
+
|
9
|
+
def self.stringify_keys(hash)
|
10
|
+
hash.map_pairs{ |k,v|
|
11
|
+
[k.to_s, v]
|
12
|
+
}
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize(hash)
|
16
|
+
raise ArgumentError unless hash.is_a?(Hash)
|
17
|
+
@hash = self.class.stringify_keys(hash).freeze
|
18
|
+
end
|
19
|
+
|
20
|
+
def_delegators :@hash, :fetch, :each, :map_pairs, :values_at, :keys, :values
|
21
|
+
|
22
|
+
def [](key)
|
23
|
+
@hash[convert_key(key)]
|
24
|
+
end
|
25
|
+
|
26
|
+
def merge(hash, &block)
|
27
|
+
self.class.new(self.to_hash.merge(hash, &block))
|
28
|
+
end
|
29
|
+
|
30
|
+
def dup
|
31
|
+
self.class.new(self)
|
32
|
+
end
|
33
|
+
|
34
|
+
def to_hash
|
35
|
+
@hash
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def convert_key(key)
|
41
|
+
case key
|
42
|
+
when Symbol then key.to_s
|
43
|
+
when String then key
|
44
|
+
else raise ArgumentError
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'tainted_params/version'
|
2
|
+
require 'tainted_params/core_ext/hash'
|
3
|
+
require 'tainted_params/string_key_hash'
|
4
|
+
require 'tainted_params/simple_key_hash'
|
5
|
+
require 'tainted_params/param_constraint'
|
6
|
+
require 'tainted_params/param_type_constraint'
|
7
|
+
require 'tainted_params/params_validator_builder'
|
8
|
+
require 'tainted_params/params_validator'
|
9
|
+
|
10
|
+
module TaintedParams
|
11
|
+
|
12
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
describe TaintedParams::CoercingParamTypeConstraint do
|
2
|
+
|
3
|
+
describe TaintedParams::INTEGER_TYPE_CONSTRAINT do
|
4
|
+
it 'attempts to convert strings to integers' do
|
5
|
+
c = TaintedParams::INTEGER_TYPE_CONSTRAINT
|
6
|
+
|
7
|
+
assert{ c.call('') == :error }
|
8
|
+
assert{ c.call('-123') == -123 }
|
9
|
+
assert{ c.call(123) == 123 }
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
describe TaintedParams::BOOLEAN_TYPE_CONSTRAINT do
|
14
|
+
it 'attempts to convert strings to booleans' do
|
15
|
+
c = TaintedParams::BOOLEAN_TYPE_CONSTRAINT
|
16
|
+
|
17
|
+
assert{ c.call('') == :error }
|
18
|
+
assert{ c.call('-123') == :error }
|
19
|
+
assert{ c.call(123) == :error }
|
20
|
+
|
21
|
+
assert{ c.call('yes') == true }
|
22
|
+
assert{ c.call('1') == true }
|
23
|
+
assert{ c.call('true') == true }
|
24
|
+
assert{ c.call(true) == true }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
describe TaintedParams::ParamTypeConstraint do
|
2
|
+
|
3
|
+
it 'returns the value if it matches the String-type, :error otherwise' do
|
4
|
+
c = described_class.new([String])
|
5
|
+
|
6
|
+
assert{ c.call('') == '' }
|
7
|
+
assert{ c.call('some string') == 'some string' }
|
8
|
+
assert{ c.call(1) == :error }
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'returns the value if it matches the Integer-type, :error otherwise' do
|
12
|
+
c = described_class.new([Integer])
|
13
|
+
|
14
|
+
assert{ c.call('') == :error }
|
15
|
+
assert{ c.call(1337) == 1337 }
|
16
|
+
assert{ c.call(1.337) == :error }
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'returns the value if it matches Integer OF Float, :error otherwise' do
|
20
|
+
c = described_class.new([Integer, Float])
|
21
|
+
|
22
|
+
assert{ c.call('') == :error }
|
23
|
+
assert{ c.call(1337) == 1337 }
|
24
|
+
assert{ c.call(1.337) == 1.337 }
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'returns the value if it is true or false, :error otherwise' do
|
28
|
+
c = described_class.new([TrueClass, FalseClass])
|
29
|
+
|
30
|
+
assert{ c.call(true) == true }
|
31
|
+
assert{ c.call(false) == false }
|
32
|
+
assert{ c.call(1) == :error }
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
describe TaintedParams::ParamsValidatorBuilder do
|
2
|
+
|
3
|
+
TC = TaintedParams::DEFAULT_TYPE_CONSTRAINTS
|
4
|
+
|
5
|
+
it 'constructs a tree out of constraints' do
|
6
|
+
b = described_class.new do
|
7
|
+
required :id, :Integer
|
8
|
+
optional :options, :Hash do
|
9
|
+
optional :name, :String
|
10
|
+
optional :active, :Boolean
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
result = [
|
15
|
+
TaintedParams::ParamConstraint.new(name: :id, type: TC[:Integer], required: true),
|
16
|
+
TaintedParams::ParamConstraint.new(name: :options, type: TC[:Hash], required: false, nested: [
|
17
|
+
TaintedParams::ParamConstraint.new(name: :name, type: TC[:String], required: false),
|
18
|
+
TaintedParams::ParamConstraint.new(name: :active, type: TC[:Boolean], required: false)
|
19
|
+
])
|
20
|
+
]
|
21
|
+
|
22
|
+
assert{ b.raw_validations == result }
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,159 @@
|
|
1
|
+
describe TaintedParams::ParamsValidator do
|
2
|
+
|
3
|
+
it 'puts valid params in the valid-hash' do
|
4
|
+
val = TaintedParams::ParamsValidatorBuilder.new do
|
5
|
+
required :id, :Integer
|
6
|
+
end.result
|
7
|
+
|
8
|
+
res = val.validate(id: 1337)
|
9
|
+
|
10
|
+
assert{ res.valid == {id: 1337} }
|
11
|
+
assert{ res.invalid == {} }
|
12
|
+
assert{ res.missing == {} }
|
13
|
+
assert{ res.unpermitted == {} }
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'does type-coercion' do
|
17
|
+
val = TaintedParams::ParamsValidatorBuilder.new do
|
18
|
+
required :id, :Integer
|
19
|
+
end.result
|
20
|
+
|
21
|
+
res = val.validate(id: '1337')
|
22
|
+
|
23
|
+
assert{ res.valid == {id: 1337} }
|
24
|
+
assert{ res.invalid == {} }
|
25
|
+
assert{ res.missing == {} }
|
26
|
+
assert{ res.unpermitted == {} }
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'puts invalid params in the invalid-hash' do
|
30
|
+
val = TaintedParams::ParamsValidatorBuilder.new do
|
31
|
+
required :id, :Integer
|
32
|
+
end.result
|
33
|
+
|
34
|
+
res = val.validate(id: 'hundred')
|
35
|
+
|
36
|
+
assert{ res.valid == {} }
|
37
|
+
assert{ res.invalid == {id: 'hundred'} }
|
38
|
+
assert{ res.missing == {} }
|
39
|
+
assert{ res.unpermitted == {} }
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'puts missing params in the missing-hash' do
|
43
|
+
val = TaintedParams::ParamsValidatorBuilder.new do
|
44
|
+
required :id, :Integer
|
45
|
+
end.result
|
46
|
+
|
47
|
+
res = val.validate({})
|
48
|
+
|
49
|
+
assert{ res.valid == {} }
|
50
|
+
assert{ res.invalid == {} }
|
51
|
+
assert{ res.missing == {id: nil} }
|
52
|
+
assert{ res.unpermitted == {} }
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'puts unpermitted params in the unpermitted-hash' do
|
56
|
+
val = TaintedParams::ParamsValidatorBuilder.new do
|
57
|
+
end.result
|
58
|
+
|
59
|
+
res = val.validate({unpermitted: 'test'})
|
60
|
+
|
61
|
+
assert{ res.valid == {} }
|
62
|
+
assert{ res.invalid == {} }
|
63
|
+
assert{ res.missing == {} }
|
64
|
+
assert{ res.unpermitted == {unpermitted: 'test'} }
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'does not require optional params' do
|
68
|
+
val = TaintedParams::ParamsValidatorBuilder.new do
|
69
|
+
optional :id, :Integer
|
70
|
+
end.result
|
71
|
+
|
72
|
+
res = val.validate({})
|
73
|
+
|
74
|
+
assert{ res.valid == { } }
|
75
|
+
assert{ res.invalid == { } }
|
76
|
+
assert{ res.unpermitted == { } }
|
77
|
+
assert{ res.missing == { } }
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'does not validate hash-contents if not specified' do
|
81
|
+
val = TaintedParams::ParamsValidatorBuilder.new do
|
82
|
+
optional :options, :Hash
|
83
|
+
end.result
|
84
|
+
|
85
|
+
res = val.validate(options: {anything: 123})
|
86
|
+
|
87
|
+
assert{ res.valid == { options: { anything: 123 } } }
|
88
|
+
assert{ res.invalid == { } }
|
89
|
+
assert{ res.unpermitted == { } }
|
90
|
+
assert{ res.missing == { } }
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'does validate sub-hashes if specified' do
|
94
|
+
val = TaintedParams::ParamsValidatorBuilder.new do
|
95
|
+
optional :options, :Hash do
|
96
|
+
required :id, :Integer
|
97
|
+
end
|
98
|
+
end.result
|
99
|
+
|
100
|
+
res = val.validate(options: {id: 'lol'})
|
101
|
+
|
102
|
+
assert{ res.valid == { } }
|
103
|
+
assert{ res.invalid == { options: { id: 'lol' } } }
|
104
|
+
assert{ res.unpermitted == { } }
|
105
|
+
assert{ res.missing == { } }
|
106
|
+
end
|
107
|
+
|
108
|
+
it 'does not require sub-hash if its optional but contains required params' do
|
109
|
+
val = TaintedParams::ParamsValidatorBuilder.new do
|
110
|
+
optional :options, :Hash do
|
111
|
+
required :id, :Integer
|
112
|
+
end
|
113
|
+
end.result
|
114
|
+
|
115
|
+
res = val.validate({})
|
116
|
+
|
117
|
+
assert{ res.valid == { } }
|
118
|
+
assert{ res.invalid == { } }
|
119
|
+
assert{ res.unpermitted == { } }
|
120
|
+
assert{ res.missing == { } }
|
121
|
+
end
|
122
|
+
|
123
|
+
it 'it puts valid, invalid, optional and missing params in sub-hashes in the result' do
|
124
|
+
val = TaintedParams::ParamsValidatorBuilder.new do
|
125
|
+
required :options, :Hash do
|
126
|
+
required :missing, :Integer
|
127
|
+
optional :valid, :Integer
|
128
|
+
optional :invalid, :String
|
129
|
+
end
|
130
|
+
end.result
|
131
|
+
|
132
|
+
res = val.validate({options: { valid: 1, invalid: 1, unpermitted: 1 } })
|
133
|
+
|
134
|
+
assert{ res.valid == { options: { valid: 1 } } }
|
135
|
+
assert{ res.invalid == { options: { invalid: 1 } } }
|
136
|
+
assert{ res.unpermitted == { options: { unpermitted: 1 } } }
|
137
|
+
assert{ res.missing == { options: { missing: nil } } }
|
138
|
+
end
|
139
|
+
|
140
|
+
# it '' do
|
141
|
+
# val = TaintedParams::ParamsValidatorBuilder.new do
|
142
|
+
# required :id, :Integer
|
143
|
+
# required :age, :Integer
|
144
|
+
# required :secret, :String
|
145
|
+
# permitted :options, :Hash do
|
146
|
+
# permitted :name, :String
|
147
|
+
# permitted :active, :Boolean
|
148
|
+
# end
|
149
|
+
# end.result
|
150
|
+
|
151
|
+
# res = val.validate(id: 1337, age: 'lol', unpermitted: 1)
|
152
|
+
|
153
|
+
# assert{ res.valid == { id: 1337 } }
|
154
|
+
# assert{ res.invalid == { age: 'lol' } }
|
155
|
+
# assert{ res.unpermitted == { unpermitted: 1 } }
|
156
|
+
# assert{ res.missing == { secret: nil } }
|
157
|
+
# end
|
158
|
+
|
159
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
describe TaintedParams::StringKeyHash do
|
2
|
+
|
3
|
+
let(:h) { described_class.new(symbol: 1, 'string' => 2) }
|
4
|
+
|
5
|
+
it '' do
|
6
|
+
assert{ h['symbol'] == 1 }
|
7
|
+
assert{ h[:string] == 2 }
|
8
|
+
end
|
9
|
+
|
10
|
+
it '' do
|
11
|
+
h2 = h.merge(newsymbol: 3)
|
12
|
+
assert{ h2['newsymbol'] == 3 }
|
13
|
+
end
|
14
|
+
|
15
|
+
it '' do
|
16
|
+
assert{ h.keys == ['symbol', 'string'] }
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
metadata
ADDED
@@ -0,0 +1,125 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: tainted_params
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.3.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ragmaanir
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-02-17 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rspec
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '3.0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '3.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: wrong
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.7'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.7'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: pry
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0.9'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0.9'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: binding_of_caller
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.7'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0.7'
|
69
|
+
description: Similar to strong_parameters, but adds type validation and makes it possible
|
70
|
+
to retrieve the invalid or unpermitted parameters
|
71
|
+
email:
|
72
|
+
- ragmaanir@gmail.com
|
73
|
+
executables: []
|
74
|
+
extensions: []
|
75
|
+
extra_rdoc_files: []
|
76
|
+
files:
|
77
|
+
- README.md
|
78
|
+
- lib/tainted_params.rb
|
79
|
+
- lib/tainted_params/core_ext/hash.rb
|
80
|
+
- lib/tainted_params/param_constraint.rb
|
81
|
+
- lib/tainted_params/param_type_constraint.rb
|
82
|
+
- lib/tainted_params/params_validator.rb
|
83
|
+
- lib/tainted_params/params_validator_builder.rb
|
84
|
+
- lib/tainted_params/simple_key_hash.rb
|
85
|
+
- lib/tainted_params/string_key_hash.rb
|
86
|
+
- lib/tainted_params/version.rb
|
87
|
+
- spec/unit/coercing_param_type_constraint_spec.rb
|
88
|
+
- spec/unit/param_type_constraint_spec.rb
|
89
|
+
- spec/unit/params_validator_builder_spec.rb
|
90
|
+
- spec/unit/params_validator_spec.rb
|
91
|
+
- spec/unit/simple_key_hash_spec.rb
|
92
|
+
- spec/unit/string_key_hash_spec.rb
|
93
|
+
- spec/unit/tainted_params_spec.rb
|
94
|
+
homepage: http://github.com/ragmaanir/tainted_params
|
95
|
+
licenses:
|
96
|
+
- MIT
|
97
|
+
metadata: {}
|
98
|
+
post_install_message:
|
99
|
+
rdoc_options: []
|
100
|
+
require_paths:
|
101
|
+
- lib
|
102
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
103
|
+
requirements:
|
104
|
+
- - "~>"
|
105
|
+
- !ruby/object:Gem::Version
|
106
|
+
version: '2.1'
|
107
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - "~>"
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: '2.2'
|
112
|
+
requirements: []
|
113
|
+
rubyforge_project: tainted_params
|
114
|
+
rubygems_version: 2.2.2
|
115
|
+
signing_key:
|
116
|
+
specification_version: 4
|
117
|
+
summary: Similar to strong_parameters, with some enhancements
|
118
|
+
test_files:
|
119
|
+
- spec/unit/simple_key_hash_spec.rb
|
120
|
+
- spec/unit/tainted_params_spec.rb
|
121
|
+
- spec/unit/string_key_hash_spec.rb
|
122
|
+
- spec/unit/coercing_param_type_constraint_spec.rb
|
123
|
+
- spec/unit/param_type_constraint_spec.rb
|
124
|
+
- spec/unit/params_validator_spec.rb
|
125
|
+
- spec/unit/params_validator_builder_spec.rb
|