parametric 0.0.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/.gitignore +17 -0
- data/.rspec +2 -0
- data/.travis.yml +3 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +211 -0
- data/Rakefile +6 -0
- data/lib/parametric/hash.rb +36 -0
- data/lib/parametric/params.rb +60 -0
- data/lib/parametric/policies.rb +62 -0
- data/lib/parametric/utils.rb +24 -0
- data/lib/parametric/version.rb +3 -0
- data/lib/parametric.rb +9 -0
- data/parametric.gemspec +24 -0
- data/spec/parametric_spec.rb +182 -0
- data/spec/spec_helper.rb +2 -0
- metadata +106 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 6f8f3fe8f214b883a606c2418b1aaabc5b70e5b3
|
4
|
+
data.tar.gz: 736039abdbf281c7121ff770f41fe718ff9a3e64
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1349a426eceace4895e23d2be890d82c5748b20d9628087f29d163c0aef1dc13d79796b240cb5ece5336dc325af853b6f81940d272749fcd2370b0419b45e742
|
7
|
+
data.tar.gz: f0b3cb7792f497cdf8283afc2acb3b8b67fbf8acd17be94d81034e123eb55a2b7960b5062c5156bfc1de701ca3ad53c3cef074f87af616d06ea61b8c5ff1a341
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Ismael Celis
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,211 @@
|
|
1
|
+
# Parametric
|
2
|
+
|
3
|
+
DSL for declaring allowed parameters with options, regexp patern and default values.
|
4
|
+
|
5
|
+
Useful for building self-documeting APIs, search or form objects.
|
6
|
+
|
7
|
+
## Usage
|
8
|
+
|
9
|
+
Declare your parameters
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
class OrdersSearch
|
13
|
+
include Parametric::Params
|
14
|
+
param :q, 'Full text search query'
|
15
|
+
param :page, 'Page number', default: 1
|
16
|
+
param :per_page, 'Items per page', default: 30
|
17
|
+
param :status, 'Order status', options: ['checkout', 'pending', 'closed', 'shipped'], multiple: true
|
18
|
+
end
|
19
|
+
```
|
20
|
+
|
21
|
+
Populate and use. Missing keys return defaults, if provided.
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
order_search = OrdersSearch.new(page: 2, q: 'foobar')
|
25
|
+
order_search.params[:page] # => 2
|
26
|
+
order_search.params[:per_page] # => 30
|
27
|
+
order_search.params[:q] # => 'foobar'
|
28
|
+
order_search.params[:status] # => nil
|
29
|
+
```
|
30
|
+
|
31
|
+
Undeclared keys are ignored.
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
order_search = OrdersSearch.new(page: 2, foo: 'bar')
|
35
|
+
order_params.params.has_key?(:foo) # => false
|
36
|
+
```
|
37
|
+
|
38
|
+
```ruby
|
39
|
+
order_search = OrderParams.new(status: 'checkout,closed')
|
40
|
+
order_search.params[:status] #=> ['checkout', 'closed']
|
41
|
+
```
|
42
|
+
|
43
|
+
### Search object pattern
|
44
|
+
|
45
|
+
A class that declares allowed params and defaults, and builds a query.
|
46
|
+
|
47
|
+
```ruby
|
48
|
+
class OrdersSearch
|
49
|
+
include Parametric::Params
|
50
|
+
param :q, 'Full text search query'
|
51
|
+
param :page, 'Page number', default: 1
|
52
|
+
param :per_page, 'Items per page', default: 30
|
53
|
+
param :status, 'Order status', options: ['checkout', 'pending', 'closed', 'shipped'], multiple: true
|
54
|
+
param :sort, 'Sort', options: ['updated_on-desc', 'updated_on-asc'], default: 'updated_on-desc'
|
55
|
+
|
56
|
+
def results
|
57
|
+
query = Order.sort(params[:sort])
|
58
|
+
query = query.where(["code LIKE ? OR user_name ?", params[:q]]) if params[:q]
|
59
|
+
query = query.where(status: params[:status]) if params[:status].any?
|
60
|
+
query = query.paginate(page: params[:page], per_page: params[:per_page])
|
61
|
+
end
|
62
|
+
end
|
63
|
+
```
|
64
|
+
|
65
|
+
### :match
|
66
|
+
|
67
|
+
Pass a regular expression to match parameter value. Non-matching values will be ignored or use default value, if available.
|
68
|
+
|
69
|
+
```ruby
|
70
|
+
class OrdersSearch
|
71
|
+
include Parametric::Params
|
72
|
+
param :email, 'Valid email address', match: /\w+@\w+\.\w+/
|
73
|
+
end
|
74
|
+
```
|
75
|
+
|
76
|
+
### :options array
|
77
|
+
|
78
|
+
Declare allowed values in an array. Values not in the options will be ignored or use default value.
|
79
|
+
|
80
|
+
```ruby
|
81
|
+
class OrdersSearch
|
82
|
+
include Parametric::Params
|
83
|
+
param :sort, 'Sort', options: ['updated_on-desc', 'updated_on-asc'], default: 'updated_on-desc'
|
84
|
+
end
|
85
|
+
```
|
86
|
+
|
87
|
+
### :multiple values
|
88
|
+
|
89
|
+
`:multiple` values are separated on "," and treated as arrays.
|
90
|
+
|
91
|
+
```ruby
|
92
|
+
class OrdersSearch
|
93
|
+
include Parametric::Params
|
94
|
+
param :status, 'Order status', multiple: true
|
95
|
+
end
|
96
|
+
|
97
|
+
search = OrdersSearch.new(status: 'closed,shipped,abandoned')
|
98
|
+
search.params[:status] # => ['closed', 'shipped', 'abandoned']
|
99
|
+
```
|
100
|
+
|
101
|
+
If `:options` array is declared, values outside of the options will be filtered out.
|
102
|
+
|
103
|
+
```ruby
|
104
|
+
class OrdersSearch
|
105
|
+
include Parametric::Params
|
106
|
+
param :status, 'Order status', options: ['checkout', 'pending', 'closed', 'shipped'], multiple: true
|
107
|
+
end
|
108
|
+
|
109
|
+
search = OrdersSearch.new(status: 'closed,shipped,abandoned')
|
110
|
+
search.params[:status] # => ['closed', 'shipped']
|
111
|
+
```
|
112
|
+
|
113
|
+
When using `:multiple`, results and defaults are always returned as an array, for consistency.
|
114
|
+
|
115
|
+
```ruby
|
116
|
+
class OrdersSearch
|
117
|
+
include Parametric::Params
|
118
|
+
param :status, 'Order status', multiple: true, default: 'closed'
|
119
|
+
end
|
120
|
+
|
121
|
+
search = OrdersSearch.new
|
122
|
+
search.params[:status] # => ['closed']
|
123
|
+
```
|
124
|
+
|
125
|
+
## `available_params`
|
126
|
+
|
127
|
+
`#available_params` returns the subset of keys that were populated (including defaults). Useful to build query strings.
|
128
|
+
|
129
|
+
```ruby
|
130
|
+
order_search = OrdersSearch.new(page: 2, foo: 'bar')
|
131
|
+
order_search.available_params # => {page: 2, per_page: 50}
|
132
|
+
```
|
133
|
+
|
134
|
+
## `schema`
|
135
|
+
|
136
|
+
`#schema` returns a data structure including meta-data on each parameter, such as "label" and "options". Useful for building forms or self-documented Hypermedia APIs (or maybe [json-schema](http://json-schema.org/example2.html) endpoints).
|
137
|
+
|
138
|
+
```ruby
|
139
|
+
order_search.schema # =>
|
140
|
+
|
141
|
+
{
|
142
|
+
q: {label: 'Full text search query', value: ''},
|
143
|
+
page: {label: 'Page number', value: 1},
|
144
|
+
per_page: {label: 'Items per page', value: 30},
|
145
|
+
status: {label: 'Order status', value: '', options: ['checkout', 'pending', 'closed', 'shipped'], multiple: true},
|
146
|
+
sort: {label: 'Sort', value: 'updated_on-desc', options: ['updated_on-desc', 'updated_on-asc']}
|
147
|
+
}
|
148
|
+
```
|
149
|
+
|
150
|
+
## Parametric::Hash
|
151
|
+
|
152
|
+
The alternative `Parametric::Hash` module makes your objects quack like a hash, instead of exposing the `#params` object directly.
|
153
|
+
|
154
|
+
```ruby
|
155
|
+
class OrdersParams
|
156
|
+
include Parametric::Hash
|
157
|
+
param :q, 'Full text search query'
|
158
|
+
param :page, 'Page number', default: 1
|
159
|
+
param :per_page, 'Items per page', default: 30
|
160
|
+
param :status, 'Order status', options: ['checkout', 'pending', 'closed', 'shipped'], multiple: true
|
161
|
+
end
|
162
|
+
```
|
163
|
+
|
164
|
+
```ruby
|
165
|
+
order_params = OrdersParams.new(page: 2, q: 'foobar')
|
166
|
+
order_params[:page] # => 2
|
167
|
+
order_params[:per_page] # => 30
|
168
|
+
order_params.each{|key, value| ... }
|
169
|
+
```
|
170
|
+
|
171
|
+
## Use cases
|
172
|
+
|
173
|
+
### In Rails
|
174
|
+
|
175
|
+
```ruby
|
176
|
+
def index
|
177
|
+
@search = OrdersSearch.new(params)
|
178
|
+
@results = @search.results
|
179
|
+
end
|
180
|
+
```
|
181
|
+
|
182
|
+
I use this along with [Oat](https://github.com/ismasan/oat) in API projects:
|
183
|
+
|
184
|
+
```ruby
|
185
|
+
def index
|
186
|
+
search = OrdersSearch.new(params)
|
187
|
+
render json: OrdersSerializer.new(search)
|
188
|
+
end
|
189
|
+
```
|
190
|
+
|
191
|
+
## Installation
|
192
|
+
|
193
|
+
Add this line to your application's Gemfile:
|
194
|
+
|
195
|
+
gem 'parametric'
|
196
|
+
|
197
|
+
And then execute:
|
198
|
+
|
199
|
+
$ bundle
|
200
|
+
|
201
|
+
Or install it yourself as:
|
202
|
+
|
203
|
+
$ gem install parametric
|
204
|
+
|
205
|
+
## Contributing
|
206
|
+
|
207
|
+
1. Fork it ( http://github.com/<my-github-username>/parametric/fork )
|
208
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
209
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
210
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
211
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
module Parametric
|
2
|
+
module Hash
|
3
|
+
|
4
|
+
def self.included(base)
|
5
|
+
base.send(:include, Params)
|
6
|
+
base.send(:include, Enumerable)
|
7
|
+
base.extend Forwardable
|
8
|
+
base.send(:def_delegators, :params,
|
9
|
+
:[],
|
10
|
+
:[]=,
|
11
|
+
:each,
|
12
|
+
:each_value,
|
13
|
+
:each_key,
|
14
|
+
:each_pair,
|
15
|
+
:keys,
|
16
|
+
:values,
|
17
|
+
:values_at,
|
18
|
+
:fetch,
|
19
|
+
:size,
|
20
|
+
:to_hash,
|
21
|
+
:merge,
|
22
|
+
:merge!,
|
23
|
+
:replace,
|
24
|
+
:update,
|
25
|
+
:has_key?,
|
26
|
+
:key?,
|
27
|
+
:key,
|
28
|
+
:select,
|
29
|
+
:select!,
|
30
|
+
:delete,
|
31
|
+
:store
|
32
|
+
)
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Parametric
|
2
|
+
|
3
|
+
module Params
|
4
|
+
|
5
|
+
def self.included(base)
|
6
|
+
base.send(:attr_reader, :params)
|
7
|
+
base.extend DSL
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize(raw_params = {})
|
11
|
+
@params = _reduce(raw_params)
|
12
|
+
end
|
13
|
+
|
14
|
+
def available_params
|
15
|
+
@available_params ||= params.each_with_object({}) do |(k,v),memo|
|
16
|
+
memo[k] = v if Utils.present?(v)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def schema
|
21
|
+
@schema ||= params.each_with_object({}) do |(k,v),memo|
|
22
|
+
memo[k] = {
|
23
|
+
value: Utils.value(v),
|
24
|
+
label: self.class._allowed_params[k][:label],
|
25
|
+
multiple: !!self.class._allowed_params[k][:multiple]
|
26
|
+
}
|
27
|
+
memo[k][:match] = self.class._allowed_params[k][:match].to_s if self.class._allowed_params[k].has_key?(:match)
|
28
|
+
memo[k][:options] = self.class._allowed_params[k][:options] if self.class._allowed_params[k].has_key?(:options)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
protected
|
33
|
+
|
34
|
+
def _reduce(raw_params)
|
35
|
+
self.class._allowed_params.each_with_object({}) do |(key,options),memo|
|
36
|
+
policy = Policies::Policy.new(raw_params[key], options)
|
37
|
+
policy = policy.wrap(Policies::MultiplePolicy) if options[:multiple]
|
38
|
+
policy = policy.wrap(Policies::OptionsPolicy) if options[:options]
|
39
|
+
policy = policy.wrap(Policies::MatchPolicy) if options[:match]
|
40
|
+
policy = policy.wrap(Policies::DefaultPolicy) if options.has_key?(:default)
|
41
|
+
policy = policy.wrap(Policies::SinglePolicy) unless options[:multiple]
|
42
|
+
|
43
|
+
memo[key] = policy.value
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
module DSL
|
48
|
+
def _allowed_params
|
49
|
+
@allowed_params ||= {}
|
50
|
+
end
|
51
|
+
|
52
|
+
def param(field_name, label = '', opts = {})
|
53
|
+
opts[:label] = label
|
54
|
+
_allowed_params[field_name] = opts
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Parametric
|
2
|
+
module Policies
|
3
|
+
|
4
|
+
class Policy
|
5
|
+
def initialize(value, options, decorated = nil)
|
6
|
+
@value, @options = value, options
|
7
|
+
@decorated = decorated
|
8
|
+
end
|
9
|
+
|
10
|
+
def wrap(decoratedClass)
|
11
|
+
decoratedClass.new(@value, @options, self)
|
12
|
+
end
|
13
|
+
|
14
|
+
def value
|
15
|
+
Array(@value)
|
16
|
+
end
|
17
|
+
|
18
|
+
protected
|
19
|
+
attr_reader :decorated, :options
|
20
|
+
end
|
21
|
+
|
22
|
+
class DefaultPolicy < Policy
|
23
|
+
def value
|
24
|
+
v = decorated.value
|
25
|
+
v.any? ? v : Array(options[:default])
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class MultiplePolicy < Policy
|
30
|
+
OPTION_SEPARATOR = /\s*,\s*/.freeze
|
31
|
+
|
32
|
+
def value
|
33
|
+
v = decorated.value.first
|
34
|
+
v = v.split(options.fetch(:separator, OPTION_SEPARATOR)) if v.is_a?(String)
|
35
|
+
Array(v)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class SinglePolicy < Policy
|
40
|
+
def value
|
41
|
+
decorated.value.first
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
class OptionsPolicy < Policy
|
46
|
+
def value
|
47
|
+
decorated.value.each_with_object([]){|a,arr|
|
48
|
+
arr << a if options[:options].include?(a)
|
49
|
+
}
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
class MatchPolicy < Policy
|
54
|
+
def value
|
55
|
+
decorated.value.each_with_object([]){|a,arr|
|
56
|
+
arr << a if a.to_s =~ options[:match]
|
57
|
+
}
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Parametric
|
2
|
+
module Utils
|
3
|
+
def self.value(val)
|
4
|
+
if val.nil?
|
5
|
+
''
|
6
|
+
elsif val.is_a?(Array)
|
7
|
+
val.join(',')
|
8
|
+
else
|
9
|
+
val
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.present?(value)
|
14
|
+
case value
|
15
|
+
when String
|
16
|
+
value.strip != ''
|
17
|
+
when Array, Hash
|
18
|
+
value.any?
|
19
|
+
else
|
20
|
+
!value.nil?
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/lib/parametric.rb
ADDED
data/parametric.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'parametric/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "parametric"
|
8
|
+
spec.version = Parametric::VERSION
|
9
|
+
spec.authors = ["Ismael Celis"]
|
10
|
+
spec.email = ["ismaelct@gmail.com"]
|
11
|
+
spec.summary = %q{DSL for declaring allowed parameters with options, regexp patern and default values.}
|
12
|
+
spec.description = %q{Useful for modelling search or form objects, white-listed query parameters and safe parameter defaults.}
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.5"
|
22
|
+
spec.add_development_dependency "rake"
|
23
|
+
spec.add_development_dependency "rspec"
|
24
|
+
end
|
@@ -0,0 +1,182 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Parametric do
|
4
|
+
it 'should have a version number' do
|
5
|
+
Parametric::VERSION.should_not be_nil
|
6
|
+
end
|
7
|
+
|
8
|
+
describe Parametric do
|
9
|
+
|
10
|
+
let(:klass) do
|
11
|
+
Class.new do
|
12
|
+
include Parametric::Params
|
13
|
+
param :name, 'User name'
|
14
|
+
param :page, 'page number', default: 1
|
15
|
+
param :per_page, 'items per page', default: 50
|
16
|
+
param :status, 'status', options: ['one', 'two', 'three'], multiple: true
|
17
|
+
param :piped_status, 'status with pipes', multiple: true, separator: '|'
|
18
|
+
param :country, 'country', options: ['UK', 'CL', 'JPN']
|
19
|
+
param :email, 'email', match: /\w+@\w+\.\w+/
|
20
|
+
param :emails, 'emails', match: /\w+@\w+\.\w+/, multiple: true, default: 'default@email.com'
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe '#params' do
|
25
|
+
let(:subject) { klass.new(foo: 'bar', per_page: 20, status: 'four') }
|
26
|
+
|
27
|
+
it 'ignores undeclared fields' do
|
28
|
+
subject.params.has_key?(:foo).should be_false
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'sets passed values' do
|
32
|
+
subject.params[:per_page].should == 20
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'uses defaults if no value passed' do
|
36
|
+
subject.params[:page].should == 1
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'does not set value if outside of declared options' do
|
40
|
+
subject.params[:status].should == []
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'does not set value if it does not :match' do
|
44
|
+
klass.new(email: 'my@email').params[:email].should be_nil
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'does set value if it does :match' do
|
48
|
+
klass.new(email: 'my@email.com').params[:email].should == 'my@email.com'
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'only sets value for :multiple values that :match' do
|
52
|
+
klass.new(emails: 'my@email,your,her@email.com').params[:emails].should == ['her@email.com']
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'returns :default wrapped in array if :multiple' do
|
56
|
+
klass.new().params[:emails].should == ['default@email.com']
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'turns :multiple comma-separated values into arrays' do
|
60
|
+
klass.new(status: 'one,three').params[:status].should == ['one', 'three']
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'turns :multiple separated values with custom separator into arrays' do
|
64
|
+
klass.new(piped_status: 'one|three').params[:piped_status].should == ['one', 'three']
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'does not turn non-multiple comma-separated values into arrays' do
|
68
|
+
klass.new(name: 'foo,bar').params[:name].should == 'foo,bar'
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'filters out undeclared options' do
|
72
|
+
klass.new(status: 'one,three,fourteen').params[:status].should == ['one', 'three']
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'defaults empty multiple options to empty array' do
|
76
|
+
klass.new().params[:status].should == []
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'wraps single multiple options in array' do
|
80
|
+
klass.new(status: 'one').params[:status].should == ['one']
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'does not accept comma-separated values outside of options unless :multiple == true' do
|
84
|
+
klass.new(country: 'UK,CL').params[:country].should be_nil
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'does accept single option' do
|
88
|
+
klass.new(country: 'UK').params[:country].should == 'UK'
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'does not accept single option if not in declared options' do
|
92
|
+
klass.new(country: 'USA').params[:country].should be_nil
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
describe '#available_params' do
|
97
|
+
let(:subject) { klass.new(foo: 'bar', name: 'lala', per_page: 20, status: 'four') }
|
98
|
+
|
99
|
+
it 'only includes declared params with values or defaults' do
|
100
|
+
subject.available_params.keys.sort.should == [:emails, :name, :page, :per_page]
|
101
|
+
subject.available_params[:emails].should == ["default@email.com"]
|
102
|
+
subject.available_params[:name].should == 'lala'
|
103
|
+
subject.available_params[:per_page].should == 20
|
104
|
+
subject.available_params[:page].should == 1
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
describe '#schema' do
|
109
|
+
let(:subject) { klass.new(foo: 'bar', name: 'lala', per_page: 20, status: 'four') }
|
110
|
+
|
111
|
+
it 'returns full param definitions with populated value' do
|
112
|
+
regexp = /\w+@\w+\.\w+/.to_s
|
113
|
+
|
114
|
+
subject.schema[:name][:label].should == 'User name'
|
115
|
+
subject.schema[:name][:value].should == 'lala'
|
116
|
+
|
117
|
+
subject.schema[:page][:label].should == 'page number'
|
118
|
+
subject.schema[:page][:value].should == 1
|
119
|
+
|
120
|
+
subject.schema[:per_page][:label].should == 'items per page'
|
121
|
+
subject.schema[:per_page][:value].should == 20
|
122
|
+
|
123
|
+
subject.schema[:status][:label].should == 'status'
|
124
|
+
subject.schema[:status][:value].should == ''
|
125
|
+
subject.schema[:status][:options].should == ['one', 'two', 'three']
|
126
|
+
subject.schema[:status][:multiple].should be_true
|
127
|
+
|
128
|
+
subject.schema[:piped_status][:label].should == 'status with pipes'
|
129
|
+
subject.schema[:piped_status][:value].should == ''
|
130
|
+
subject.schema[:piped_status][:multiple].should be_true
|
131
|
+
|
132
|
+
subject.schema[:country][:label].should == 'country'
|
133
|
+
subject.schema[:country][:value].should == ''
|
134
|
+
subject.schema[:country][:options].should == ['UK', 'CL', 'JPN']
|
135
|
+
|
136
|
+
subject.schema[:email][:label].should == 'email'
|
137
|
+
subject.schema[:email][:value].should == ''
|
138
|
+
subject.schema[:email][:match].should == regexp
|
139
|
+
|
140
|
+
subject.schema[:emails][:label].should == 'emails'
|
141
|
+
subject.schema[:emails][:value].should == 'default@email.com'
|
142
|
+
subject.schema[:emails][:multiple].should be_true
|
143
|
+
subject.schema[:emails][:match].should == regexp
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
describe Parametric::Hash do
|
149
|
+
let(:klass) do
|
150
|
+
Class.new do
|
151
|
+
include Parametric::Hash
|
152
|
+
param :name, 'User name'
|
153
|
+
param :page, 'page number', default: 1
|
154
|
+
param :per_page, 'items per page', default: 50
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
let(:subject) { klass.new(name: 'Ismael', page: 2) }
|
159
|
+
|
160
|
+
it 'quacks like a hash' do
|
161
|
+
subject[:name].should == 'Ismael'
|
162
|
+
subject[:page].should == 2
|
163
|
+
subject[:per_page].should == 50
|
164
|
+
subject.map{|k,v| k}.sort.should == [:name, :page, :per_page]
|
165
|
+
subject.keys.sort.should == [:name, :page, :per_page]
|
166
|
+
subject.values.map(&:to_s).sort.should == ['2', '50', 'Ismael']
|
167
|
+
subject.fetch(:page, 0).should == 2
|
168
|
+
subject.fetch(:foo, 0).should == 0
|
169
|
+
subject.merge(foo: 22).should == {name: 'Ismael', page: 2, per_page: 50, foo: 22}
|
170
|
+
subject.select{|k,v| k == :name}.should == {name: 'Ismael'}
|
171
|
+
end
|
172
|
+
|
173
|
+
it 'has #available_params' do
|
174
|
+
subject.available_params[:name].should == 'Ismael'
|
175
|
+
end
|
176
|
+
|
177
|
+
it 'has #schema' do
|
178
|
+
subject.schema[:name][:label].should == 'User name'
|
179
|
+
subject.schema[:name][:value].should == 'Ismael'
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: parametric
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ismael Celis
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-03-13 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.5'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.5'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
description: Useful for modelling search or form objects, white-listed query parameters
|
56
|
+
and safe parameter defaults.
|
57
|
+
email:
|
58
|
+
- ismaelct@gmail.com
|
59
|
+
executables: []
|
60
|
+
extensions: []
|
61
|
+
extra_rdoc_files: []
|
62
|
+
files:
|
63
|
+
- ".gitignore"
|
64
|
+
- ".rspec"
|
65
|
+
- ".travis.yml"
|
66
|
+
- Gemfile
|
67
|
+
- LICENSE.txt
|
68
|
+
- README.md
|
69
|
+
- Rakefile
|
70
|
+
- lib/parametric.rb
|
71
|
+
- lib/parametric/hash.rb
|
72
|
+
- lib/parametric/params.rb
|
73
|
+
- lib/parametric/policies.rb
|
74
|
+
- lib/parametric/utils.rb
|
75
|
+
- lib/parametric/version.rb
|
76
|
+
- parametric.gemspec
|
77
|
+
- spec/parametric_spec.rb
|
78
|
+
- spec/spec_helper.rb
|
79
|
+
homepage: ''
|
80
|
+
licenses:
|
81
|
+
- MIT
|
82
|
+
metadata: {}
|
83
|
+
post_install_message:
|
84
|
+
rdoc_options: []
|
85
|
+
require_paths:
|
86
|
+
- lib
|
87
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
88
|
+
requirements:
|
89
|
+
- - ">="
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
version: '0'
|
92
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
requirements: []
|
98
|
+
rubyforge_project:
|
99
|
+
rubygems_version: 2.1.11
|
100
|
+
signing_key:
|
101
|
+
specification_version: 4
|
102
|
+
summary: DSL for declaring allowed parameters with options, regexp patern and default
|
103
|
+
values.
|
104
|
+
test_files:
|
105
|
+
- spec/parametric_spec.rb
|
106
|
+
- spec/spec_helper.rb
|