rack-param 0.1.6 → 0.2.0

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