rack-param 0.1.6 → 0.2.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: 8ab70fd19b1ab8288d3730129fa084826b29074b
4
- data.tar.gz: 181638cefec8db9eedf116a5f05b9945c40c193e
3
+ metadata.gz: ab3d04d1dc5029f30b604be0b2490e16140328c8
4
+ data.tar.gz: 13b3eb4a749b321419056b59e3a3a5918d8adcd8
5
5
  SHA512:
6
- metadata.gz: 7c42afcc9052f11b231461250f5405b7bac91ea83674ad781e39369e4439751626ab9e72cdaf02e51277084192f56f06d5719bbcc1f61ec8d31228267701f8d8
7
- data.tar.gz: 030fc88a1539f90eda4238624813a4439ac0e0381a3ceb3ef1eb897d3557dbeabcfdc0f668ae066441ac70a788bffcf8610d8b248dda17d60c3a8b35cc11a326
6
+ metadata.gz: 742f43719a390fd85917e14cb3f03897744e79685e05f5b196273a07e9264896e8029b9b624e0c957f26bd3eb3a835269996cd6c4d41bb28ec38fd32942bc780
7
+ data.tar.gz: f571447e217d3fece1f2c56db92d418e1d751c31e0f113b299e4d626e7f0257f4109cf8e4292418cdd9e77f4923ce1d33fc188e792f112a58da32886d38ced05
data/README.md CHANGED
@@ -2,11 +2,12 @@
2
2
 
3
3
  Parameter checking and validation for `Rack::Request`. Originally designed to be used with [`Sansom`](http://github.com/fhsjaagshs/fhsjaagshs), but it also works with [`Rack`](http://github.com/rack/rack).
4
4
 
5
- ## Installation
5
+ Installation
6
+ -
6
7
 
7
8
  Add this line to your application's Gemfile:
8
9
 
9
- gem 'rack-param'
10
+ gem "rack-param"
10
11
 
11
12
  And then execute:
12
13
 
@@ -16,39 +17,159 @@ Or install it through `gem`:
16
17
 
17
18
  $ gem install rack-param
18
19
 
19
- ## Usage
20
+ Usage
21
+ -
22
+
23
+ For example:
20
24
 
21
25
  require "rack/param"
22
26
 
23
27
  r = Rack::Request env # pass your env
24
- r.param :param_name, Integer, :required => true ...
28
+ r.param :positive, Integer, :min => 0, :required => true
25
29
 
26
- Now, `r.params` should contain a single entry:
30
+ Now, `r.params` should contain a single entry, regardless of the original request's parameters.
27
31
 
28
- { :param_name => <some Integer> }
32
+ { "positive" => <some Integer> }
29
33
 
30
- `Rack::Param` patches `Rack::Request#params` to contain only the validated parameters, in coerced form.
31
-
32
- Here's a list of options:
33
-
34
- `:required` => `true`/`false`<br />
35
- `:blank` => `true`/`false`<br />
36
- `:greater_than` => Any `Numeric`<br />
37
- `:less_than` => Any `Numeric`<br />
38
- `:min` => Any `Numeric`<br />
39
- `:max` => Any `Numeric`<br />
40
- `:length` => Any `Numeric`<br />
41
- `:min_length` => Any `Numeric`<br />
42
- `:max_length` => Any `Numeric`<br />
43
- `:in` => Something that responds to `include?`<br />
44
- `:regex` => A `Regexp`<br />
45
- `:validator` => A block: `{ |param| true }`<br />
46
- `:error_message` => A `String`. `$` is the parameter and `#` is the validator object. (`"Invalid password: $"`)
47
-
48
- ## Contributing
34
+ `Rack::Param` patches `Rack::Request#params` to contain only the validated parameters, in coerced form. If you want the original parameters hash, use the `Rack::Request#raw_params` method.
35
+
36
+ Rules
37
+ -
38
+
39
+ Rules are a way of making sure parameters are valid. Rather than type them yourself, use the rules below to
40
+
41
+ <table border="0" style="width:100%">
42
+ <tr>
43
+ <td><b>Rule</b></td>
44
+ <td><b>Argument</b></td>
45
+ <td><b>Default</b></td>
46
+ <td><b>Description</b></td>
47
+ </tr>
48
+ <tr>
49
+ <td><code>:greater_than</code></td>
50
+ <td><code>Numeric</code></td>
51
+ <td>none</td>
52
+ <td>Self explanatory.</td>
53
+ </tr>
54
+ <tr>
55
+ <td><code>:less_than</code></td>
56
+ <td><code>Numeric</code></td>
57
+ <td>none</td>
58
+ <td>Self explanatory.</td>
59
+ </tr>
60
+ <tr>
61
+ <td><code>:min</code></td>
62
+ <td><code>Numeric</code></td>
63
+ <td>none</td>
64
+ <td>Greater than or equal to.</td>
65
+ </tr>
66
+ <tr>
67
+ <td><code>:max</code></td>
68
+ <td><code>Numeric</code></td>
69
+ <td>none</td>
70
+ <td>Less than or equal to.</td>
71
+ </tr>
72
+ <tr>
73
+ <td><code>:length</code></td>
74
+ <td><code>Numeric</code></td>
75
+ <td>none</td>
76
+ <td>Self explanatory.</td>
77
+ </tr>
78
+ <tr>
79
+ <td><code>:min_length</code></td>
80
+ <td><code>Numeric</code></td>
81
+ <td>none</td>
82
+ <td>Greater than or equal to, using the parameter's length.</td>
83
+ </tr>
84
+ <tr>
85
+ <td><code>:max_length</code></td>
86
+ <td><code>Numeric</code></td>
87
+ <td>none</td>
88
+ <td>Less than or equal to, using the parameter's length.</td>
89
+ </tr>
90
+ <tr>
91
+ <td><code>:in</code></td>
92
+ <td>Responds to <code>include?</code></td>
93
+ <td>none</td>
94
+ <td>Less than or equal to, using the parameter's length.</td>
95
+ </tr>
96
+ <tr>
97
+ <!--TODO: This should be :matches-->
98
+ <td><code>:regex</code></td>
99
+ <td>Responds to <code>match</code></td>
100
+ <td>none</td>
101
+ <td>Self explanatory.</td>
102
+ </tr>
103
+ <tr>
104
+ <td><code>:validator</code></td>
105
+ <td>Responds to <code>call</code> with a <i>single</i> argument. Returns <code>true</code> or <code>false</code<</td>
106
+ <td>none</td>
107
+ <td>If the argument returns something truthy or <code>true</code>, this rule is true.</td>
108
+ </tr>
109
+ </table>
110
+
111
+ There are also a couple options used to control parameter checking.
112
+
113
+ <table border="0" style="width:100%">
114
+ <tr>
115
+ <td><b>Option</b></td>
116
+ <td><b>Argument</b></td>
117
+ <td><b>Default</b></td>
118
+ <td><b>Description</b></td>
119
+ </tr>
120
+ <tr>
121
+ <td><code>:error_message</code></td>
122
+ <td><code>String</code></td>
123
+ <td>depends on rule</td>
124
+ <td>The message to be used when a <code>Rack::ParameterError</code> is raised. It has a specific format: <code>$</code> is the parameter and <code>#</code> is the argument. (ex "Invalid token: $")</td>
125
+ </tr>
126
+ <tr>
127
+ <td><code>:coerce</code></td>
128
+ <td><code>true</code>/<code>false</code></td>
129
+ <td><code>true</code></td>
130
+ <td>Whether or not rack-param coerces the parameter.</td>
131
+ </tr>
132
+ <tr>
133
+ <td><code>:required</code></td>
134
+ <td><code>true</code>/<code>false</code></td>
135
+ <td><code>false</code></td>
136
+ <td>Whether or not the parameter is required.</td>
137
+ </tr>
138
+ <tr>
139
+ <td><code>:default</code></td>
140
+ <td><code>Object</code></td>
141
+ <td><code>none</code></td>
142
+ <td>If the parameter doesn't exist, this value takes its place. It should be a fully coerced value.</td>
143
+ </tr>
144
+ <tr>
145
+ <td><code>:transform</code></td>
146
+ <td>Responds to <code>call</code> with a <i>single</i> argument. Returns a new value.</td>
147
+ <td><code>none</code></td>
148
+ <td>Called to transform a coerced value.</td>
149
+ </tr>
150
+ </table>
151
+
152
+ Custom Types & Coercion
153
+ -
154
+
155
+ Have a custom type that you want to be able to coerce a value? Write a function whose name is the same as your class. It should take one argument, a `String`. In fact, most classes already have this (like `Integer()`)
156
+
157
+ class Money
158
+ ...
159
+ end
160
+
161
+ # somewhere in the global scope (or in `Kernel`)
162
+ def Money str
163
+ # turn str into a `Money` and return it
164
+ end
165
+
166
+ Contributing
167
+ -
49
168
 
50
169
  1. Fork it ( https://github.com/sansomrb/rack-param/fork )
51
170
  2. Create your feature branch (`git checkout -b my-new-feature`)
52
171
  3. Commit your changes (`git commit -am 'Add some feature'`)
53
172
  4. Push to the branch (`git push origin my-new-feature`)
54
173
  5. Create a new Pull Request
174
+
175
+ Your changes must pass the rspec test suite.
data/lib/rack/param.rb CHANGED
@@ -4,136 +4,88 @@ require "rack"
4
4
  require "time"
5
5
  require "date"
6
6
 
7
- Boolean = "Boolean" # A boldfaced hack
7
+ Boolean = Class.new TrueClass # giant hack
8
8
 
9
9
  module Rack
10
10
  ParameterError = Class.new StandardError
11
11
 
12
12
  class ::String
13
- def truthy?
14
- (/^(true|t|yes|y|1|on)$/ =~ downcase) != nil
15
- end
16
-
17
- def falsey?
18
- (/^(false|f|no|n|0|off)$/ =~ downcase) != nil
19
- end
13
+ def truthy?; (/^(true|t|yes|y|1|on)$/ =~ downcase) != nil; end
14
+ def falsey?; (/^(false|f|no|n|0|off)$/ =~ downcase) != nil; end
20
15
  end
21
16
 
22
- class Request
23
- alias_method :params_original, :params
24
- def params
25
- @processed_parameters ||= {}
17
+ class Rule
18
+ def self.rule message, &block; new message, &block; end
19
+
20
+ def initialize message, &block
21
+ @blk = block
22
+ @msg = message
26
23
  end
27
24
 
28
- def param name, type, opts={}
29
- _name = name.to_s
30
-
31
- if params_original.member? _name
32
- p = Rack::Parameter.new(
33
- :name => _name,
34
- :value => params_original[_name] || params[_name],
35
- :type => type,
36
- :conditions => opts
37
- )
38
-
39
- raise ParameterError, p.error unless p.valid?
40
- params[_name] = p.value
41
- end
25
+ def validate! param, val, emsg=nil
26
+ raise ParameterError, (emsg || @msg).sub('$',"`#{param}`").sub('#',"`#{val}`") unless @blk.call(param, val)
42
27
  end
43
28
  end
44
29
 
45
- class Parameter
46
- attr_reader :error, :value
30
+ class Request
31
+ alias_method :raw_params, :params
32
+ def params; @processed_parameters ||= {}; end
47
33
 
48
- class Rule
49
- def initialize message, &block
50
- @block = block
51
- @message = message
52
- end
53
-
54
- def self.rule message, &block
55
- new message, &block
56
- end
34
+ def param name, type, opts={}
35
+ name = name.to_s
36
+ opts = Hash[opts.map { |k,v| [k.to_sym,v] }]
57
37
 
58
- def validate param, value, error_msg=nil
59
- return (error_msg || @message).sub('$',"`#{param.to_s}`").sub('#',"`#{value.to_s}`") unless @block.call(param,value)
38
+ if raw_params.member? name
39
+ v = raw_params[name] || params[name]
40
+
41
+ if !v.is_a?(type) && ((opts.member?(:coerce) && opts[:coerce]) || !opts.member?(:coerce))
42
+ begin
43
+ v = self.class.coercers[type].call type, v
44
+ rescue StandardError
45
+ raise ParameterError, "Parameter `#{name}` is not a valid #{type}."
46
+ end
47
+ end
48
+
49
+ # will raise an error on an invalid param
50
+ opts.select { |k,_| self.class.rules.member? k }
51
+ .each { |k,arg| self.class.rules[k].validate! v, arg, opts[:error_message] }
52
+
53
+ v = opts[:transform].to_proc.call v if opts.member?(:transform)
54
+ params[name] = v
55
+ else
56
+ raise ParameterError, "Parameter #{name} is required." if opts.member?(:required) && opts[:required] && !opts.member?(:default)
57
+ params[name] = opts[:default]
60
58
  end
61
59
  end
62
60
 
63
- def initialize(opts={})
64
- opts[:value].inspect
65
- _opts = Hash[opts.dup.map { |k,v| [k.to_sym,v] }]
66
- _opts.merge! _opts.delete(:conditions)
67
- @error_message = _opts.delete :error_message
68
- @default = _opts.delete :default
69
- @required = _opts.delete :required
70
- @transform = _opts.delete :transform
71
- @delimiter = _opts.delete(:delimiter) || ","
72
- @separator = _opts.delete(:separator) || ":"
73
- @name = _opts.delete :name
74
- @type = _opts.delete :type
75
- @value = _opts.delete(:value) || @default
76
- @error = process(_opts) unless default?
77
- end
78
-
79
- def valid?
80
- @error.nil?
61
+ def self.coercers
62
+ @coercers ||= {
63
+ Date => lambda { |t,v| Date.parse v },
64
+ Time => lambda { |t,v| Time.parse v },
65
+ DateTime => lambda { |t,v| DateTime.parse v },
66
+ Array => lambda { |t,v| v.split(',') },
67
+ Hash => lambda { |t,v| Hash[v.split(',').map { |c| c.split ':', 2 }] },
68
+ Boolean => lambda { |t,v| v.falsey? ? false : (v.truthy? ? true : raise(StandardError)) }
69
+ }
70
+ @coercers.default_proc = lambda { |h, k| h[k] = lambda { |t,v| method(t.to_s.to_sym).call v } }
71
+ @coercers
81
72
  end
82
73
 
83
- def default?
84
- @default == @value && !@value.nil?
85
- end
86
-
87
- def required?
88
- @required
89
- end
90
-
91
- def nil?
92
- @value.nil?
93
- end
94
-
95
- def process opts
96
- return "Parameter #{@name} is required." if @value.nil? && required?
97
-
98
- unless @value.class == @type || @value.nil?
99
- begin
100
- @value = case @type.to_s.downcase.to_sym
101
- when :date then Date.parse @value
102
- when :time then Time.parse @value
103
- when :datetime then DateTime.parse @value
104
- when :array then @value.split(@delimiter)
105
- when :hash then Hash[@value.split(@delimiter).map { |c| c.split @separator, 2 }]
106
- when :boolean then (@value.falsey? ? false : @value.truthy? ? true : raise(StandardError))
107
- else method(@type.to_s.to_sym).call @value end
108
- rescue StandardError => e
109
- return "Parameter `#{@name}` is not a valid #{@type.to_s}."
110
- end
111
- end
112
-
113
- validate_error = opts.detect { |k,v| break rules[k].validate @value, v, @error_message }
114
- return validate_error unless validate_error.nil?
115
-
116
- @value = @transform.to_proc.call @value if @transform
117
- nil
118
- end
119
-
120
- def rules
121
- @rules ||= {}
122
- if @rules.empty?
123
- @rules[:blank] = Rule.rule("$ cannot be blank.") { |p,v| v == (p.empty? rescue false) }
124
- @rules[:greater_than] = Rule.rule("$ can't be less than #.") { |p,v| p > v }
125
- @rules[:less_than] = Rule.rule("$ can't be greater than #.") { |p,v| p < v }
126
- @rules[:min] = Rule.rule("$ can't be less than #.") { |p,v| p >= v }
127
- @rules[:max] = Rule.rule("$ can't be greater than #.") { |p,v| p <= v }
128
- @rules[:length] = Rule.rule("$ can't be longer or shorter than #.") { |p,v| p.length == v }
129
- @rules[:min_length] = Rule.rule("$ must be longer than #.") { |p,v| p.length >= v }
130
- @rules[:max_length] = Rule.rule("$ must be shorter than #.") { |p,v| p.length <= v }
131
- @rules[:in] = Rule.rule("$ must be included in #.") { |p,v| v.include? p }
132
- @rules[:contains] = Rule.rule("$ must include #") { |p,v| p.include? v }
133
- @rules[:regex] = Rule.rule("$ failed validation.") { |p,v| v.match(p) rescue false }
134
- @rules[:validator] = Rule.rule("$ failed validation.") { |p,v| v.call p }
135
- end
136
- @rules
74
+ def self.rules
75
+ @rules ||= {
76
+ :blank => Rule.rule("$ cannot be blank.") { |p,v| v == (p.empty? rescue false) },
77
+ :greater_than => Rule.rule("$ can't be less than #.") { |p,v| p > v },
78
+ :less_than => Rule.rule("$ can't be greater than #.") { |p,v| p < v },
79
+ :min => Rule.rule("$ can't be less than #.") { |p,v| p >= v },
80
+ :max => Rule.rule("$ can't be greater than #.") { |p,v| p <= v },
81
+ :length => Rule.rule("$ can't be longer or shorter than #.") { |p,v| p.length == v },
82
+ :min_length => Rule.rule("$ must be longer than #.") { |p,v| p.length >= v },
83
+ :max_length => Rule.rule("$ must be shorter than #.") { |p,v| p.length <= v },
84
+ :in => Rule.rule("$ must be included in #.") { |p,v| v.include? p },
85
+ :contains => Rule.rule("$ must include #") { |p,v| p.include? v },
86
+ :regex => Rule.rule("$ failed validation.") { |p,v| v.match(p) rescue false },
87
+ :validator => Rule.rule("$ failed validation.") { |p,v| v.call p }
88
+ }
137
89
  end
138
90
  end
139
91
  end
data/rack-param.gemspec CHANGED
@@ -4,7 +4,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
 
5
5
  Gem::Specification.new do |spec|
6
6
  spec.name = "rack-param"
7
- spec.version = "0.1.6"
7
+ spec.version = "0.2.0"
8
8
  spec.authors = ["Nathaniel Symer"]
9
9
  spec.email = ["nate@natesymer.com"]
10
10
  spec.summary = "Sane parameter validation for Rack::Request."
@@ -18,5 +18,6 @@ Gem::Specification.new do |spec|
18
18
  spec.require_paths = ["lib"]
19
19
 
20
20
  spec.add_development_dependency "bundler", "~> 1.6"
21
+ spec.add_development_dependency "rspec", "~> 3.0"
21
22
  spec.add_dependency "rack", "~> 1.0"
22
23
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  require_relative "./spec_helper.rb"
4
4
 
5
- describe Rack::Parameter do
5
+ describe Rack::Request do
6
6
  before :all do
7
7
  @p = {
8
8
  :date => "2014/8/4",
@@ -18,38 +18,34 @@ describe Rack::Parameter do
18
18
  :integer => "42",
19
19
  :float => "420.42",
20
20
  :string => "foo bar baz",
21
- :should_fail => "asdfasdf"
21
+ :should_fail => "asdfasdf",
22
+ :dont_coerce => "1"
22
23
  }
23
24
  end
24
25
 
25
26
  context "when coercing types" do
26
27
  it "parses a Date" do
27
28
  @r.param :date, Date
28
-
29
29
  expect(@r.params["date"]).to eq(Date.new(2014, 8, 4))
30
30
  end
31
31
 
32
32
  it "parses a Time" do
33
33
  @r.param :time, Time
34
-
35
34
  expect(@r.params["time"].to_i).to eq(1007044400)
36
35
  end
37
36
 
38
37
  it "parses a DateTime" do
39
38
  @r.param :datetime, DateTime
40
-
41
39
  expect(@r.params["datetime"].to_s).to eq("2012-02-09T20:05:33+00:00")
42
40
  end
43
41
 
44
42
  it "parses an Array" do
45
43
  @r.param :array, Array
46
-
47
44
  expect(@r.params["array"]).to eq(["asdf", "asdf", "asdf"])
48
45
  end
49
46
 
50
47
  it "parses a Hash" do
51
48
  @r.param :hash, Hash
52
-
53
49
  expect(@r.params["hash"]).to eq({"key" => "value", "this" => "that", "foo" => "bar"})
54
50
  end
55
51
 
@@ -69,24 +65,27 @@ describe Rack::Parameter do
69
65
 
70
66
  it "coerces an Integer" do
71
67
  @r.param :integer, Integer
72
-
73
68
  expect(@r.params["integer"]).to eq(42)
74
69
  end
75
70
 
76
71
  it "coerces a Float" do
77
72
  @r.param :float, Float
78
-
79
73
  expect(@r.params["float"]).to eq(420.42)
80
74
  end
81
75
 
82
76
  it "\"coerces\" a String" do
83
77
  @r.param :string, String
84
-
85
78
  expect(@r.params["string"]).to eq("foo bar baz")
86
79
  end
87
80
 
88
81
  it "errors out on unrecognized types" do
89
82
  expect { @r.param :should_fail, Date }.to raise_error(Rack::ParameterError)
90
83
  end
84
+
85
+ it "doesn't coerce when the coerce option is set to false" do
86
+ @r.param :dont_coerce, Integer, :coerce => false
87
+ expect(@r.params["dont_coerce"]).to eq(@p[:dont_coerce])
88
+ expect(@r.params["dont_coerce"].class).to eq(String)
89
+ end
91
90
  end
92
91
  end
data/spec/rules_spec.rb CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  require_relative "./spec_helper.rb"
4
4
 
5
- describe Rack::Parameter do
5
+ describe Rack::Rule do
6
6
  before :all do
7
7
  @p = {
8
8
  :int_three => "3",
@@ -17,7 +17,7 @@ describe Rack::Parameter do
17
17
 
18
18
  context "when given rules" do
19
19
  it "checks a parameter based on :required" do
20
- expect { @r.param :not_a_param, String, :required => true }.to raise_error(Rack::ParameterError)
20
+ expect { @r.param :not_a_param_asdfasdfasdfasdf, String, :required => true }.to raise_error(Rack::ParameterError)
21
21
  expect { @r.param :required, String, :required => true }.to_not raise_error
22
22
  end
23
23
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rack-param
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.6
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nathaniel Symer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-02-05 00:00:00.000000000 Z
11
+ date: 2015-02-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '1.6'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '3.0'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: rack
29
43
  requirement: !ruby/object:Gem::Requirement