rails_param2 0.10.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 +7 -0
- data/README.md +159 -0
- data/lib/rails_param.rb +5 -0
- data/lib/rails_param/param.rb +162 -0
- data/lib/rails_param/version.rb +3 -0
- metadata +104 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 3712d8ce55990ec233da2ae2e2167bf23a8d9ebf
|
4
|
+
data.tar.gz: f057f82af9c4da204a379171663ae54c79e5d3f1
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ec4cda59e7bdbdcd1ed5508e2cfd1536ac3db85aa5e8ebc21b253e189581e7d72eb8df813608bd3655103c2f306015f53a0a260c3c095f960473b5209f6551e3
|
7
|
+
data.tar.gz: 809d2c8687df17816e9f5a029b9330003731aced0d01d73f3cf2e73f06abb3e9b9ac15c57fb330ac9c26b18ce4546687f5f78ad1c38d717bb2b7ec1a05bc139f
|
data/README.md
ADDED
@@ -0,0 +1,159 @@
|
|
1
|
+
# rails_param2
|
2
|
+
|
3
|
+
forked from [nicolasblanco/rails_param](https://github.com/nicolasblanco/rails_param)
|
4
|
+
|
5
|
+
_Parameter Validation & Type Coercion for Rails_
|
6
|
+
|
7
|
+
[](https://travis-ci.org/nicolasblanco/rails_param)
|
8
|
+
|
9
|
+
## Introduction
|
10
|
+
|
11
|
+
This library is handy if you want to validate a few numbers of parameters directly inside your controller.
|
12
|
+
|
13
|
+
For example : you are building a search action and want to validate that the `sort` parameter is set and only set to something like `desc` or `asc`.
|
14
|
+
|
15
|
+
## Important
|
16
|
+
|
17
|
+
This library should not be used to validate a large number of parameters or parameters sent via a form or namespaced (like `params[:user][:first_name]`). There is already a great framework included in Rails (ActiveModel::Model) which can be used to create virtual classes with all the validations you already know and love from Rails. Remember to always try to stay in the “thin controller” rule.
|
18
|
+
|
19
|
+
See [this](http://blog.remarkablelabs.com/2012/12/activemodel-model-rails-4-countdown-to-2013) page to see an example on how to build a contact form using ActiveModel::Model.
|
20
|
+
|
21
|
+
But sometimes, it’s not practical to create an external class just to validate and convert a few parameters. In this case, you may use this gem. It allows you to easily do validations and conversion of the parameters directly in your controller actions using a simple method call.
|
22
|
+
|
23
|
+
## Credits
|
24
|
+
|
25
|
+
This is originally a port of the gem [sinatra-param](https://github.com/mattt/sinatra-param) for the Rails framework.
|
26
|
+
|
27
|
+
All the credits go to [@mattt](https://twitter.com/mattt).
|
28
|
+
|
29
|
+
It has all the features of the sinatra-param gem, I used bang methods (like param!) to indicate that they are destructive as they change the controller params object and may raise an exception.
|
30
|
+
|
31
|
+
## Installation
|
32
|
+
|
33
|
+
As usual, in your Gemfile...
|
34
|
+
|
35
|
+
``` ruby
|
36
|
+
gem 'rails_param2'
|
37
|
+
```
|
38
|
+
|
39
|
+
## Example
|
40
|
+
|
41
|
+
``` ruby
|
42
|
+
# GET /search?q=example
|
43
|
+
# GET /search?q=example&categories=news
|
44
|
+
# GET /search?q=example&sort=created_at&order=ASC
|
45
|
+
def search
|
46
|
+
param! :q, String, required: true
|
47
|
+
param! :categories, Array
|
48
|
+
param! :sort, String, default: "title"
|
49
|
+
param! :order, String, in: %w(asc desc), transform: :downcase, default: "asc"
|
50
|
+
param! :price, String, format: /[<\=>]\s*\$\d+/
|
51
|
+
|
52
|
+
# Access the parameters using the params object (like params[:q]) as you usually do...
|
53
|
+
end
|
54
|
+
end
|
55
|
+
```
|
56
|
+
|
57
|
+
### Parameter Types
|
58
|
+
|
59
|
+
By declaring parameter types, incoming parameters will automatically be transformed into an object of that type. For instance, if a param is `:boolean`, values of `'1'`, `'true'`, `'t'`, `'yes'`, and `'y'` will be automatically transformed into `true`. `BigDecimal` defaults to a precision of 14, but this can but changed by passing in the optional `precision:` argument. Any `$` and `,` are automatically stripped when converting to `BigDecimal`.
|
60
|
+
|
61
|
+
- `String`
|
62
|
+
- `Integer`
|
63
|
+
- `Float`
|
64
|
+
- `:boolean/TrueClass/FalseClass` _("1/0", "true/false", "t/f", "yes/no", "y/n")_
|
65
|
+
- `Array` _("1,2,3,4,5")_
|
66
|
+
- `Hash` _("key1:value1,key2:value2")_
|
67
|
+
- `Date`, `Time`, & `DateTime`
|
68
|
+
- `BigDecimal` _("$1,000,000")_
|
69
|
+
|
70
|
+
### Validations
|
71
|
+
|
72
|
+
Encapsulate business logic in a consistent way with validations. If a parameter does not satisfy a particular condition, an exception (RailsParam::Param::InvalidParameterError) is raised.
|
73
|
+
You may use the [rescue_from](http://api.rubyonrails.org/classes/ActiveSupport/Rescuable/ClassMethods.html#method-i-rescue_from) method in your controller to catch this kind of exception.
|
74
|
+
|
75
|
+
- `required`
|
76
|
+
- `blank`
|
77
|
+
- `is`
|
78
|
+
- `in`, `within`, `range`
|
79
|
+
- `min` / `max`
|
80
|
+
- `format`
|
81
|
+
|
82
|
+
Customize exception message with option `:message`
|
83
|
+
|
84
|
+
```ruby
|
85
|
+
param! :q, String, required: true, message: "Query not specified"
|
86
|
+
```
|
87
|
+
|
88
|
+
### Defaults and Transformations
|
89
|
+
|
90
|
+
Passing a `default` option will provide a default value for a parameter if none is passed. A `default` can defined as either a default or as a `Proc`:
|
91
|
+
|
92
|
+
```ruby
|
93
|
+
param! :attribution, String, default: "©"
|
94
|
+
param! :year, Integer, default: lambda { Time.now.year }
|
95
|
+
```
|
96
|
+
|
97
|
+
Use the `transform` option to take even more of the business logic of parameter I/O out of your code. Anything that responds to `to_proc` (including `Proc` and symbols) will do.
|
98
|
+
|
99
|
+
```ruby
|
100
|
+
param! :order, String, in: ["ASC", "DESC"], transform: :upcase, default: "ASC"
|
101
|
+
param! :offset, Integer, min: 0, transform: lambda {|n| n - (n % 10)}
|
102
|
+
```
|
103
|
+
|
104
|
+
### Nested Attributes
|
105
|
+
|
106
|
+
rails_param allows you to apply any of the above mentioned validations to attributes nested in hashes:
|
107
|
+
|
108
|
+
```ruby
|
109
|
+
param! :book, Hash do |b|
|
110
|
+
b.param! :title, String, blank: false
|
111
|
+
b.param! :price, BigDecimal, precision: 4, required: true
|
112
|
+
b.param! :author, Hash, required: true do |a|
|
113
|
+
a.param! :first_name, String
|
114
|
+
a.param! :last_name, String, blank: false
|
115
|
+
end
|
116
|
+
end
|
117
|
+
```
|
118
|
+
|
119
|
+
### Arrays
|
120
|
+
|
121
|
+
Validate every element of your array, including nested hashes and arrays:
|
122
|
+
|
123
|
+
```ruby
|
124
|
+
# primitive datatype syntax
|
125
|
+
param! :integer_array, Array do |array,index|
|
126
|
+
array.param! index, Integer, required: true
|
127
|
+
end
|
128
|
+
|
129
|
+
# complex array
|
130
|
+
param! :books_array, Array, required: true do |b|
|
131
|
+
b.param! :title, String, blank: false
|
132
|
+
b.param! :author, Hash, required: true do |a|
|
133
|
+
a.param! :first_name, String
|
134
|
+
a.param! :last_name, String, required: true
|
135
|
+
end
|
136
|
+
b.param! :subjects, Array do |s,i|
|
137
|
+
s.param! i, String, blank: false
|
138
|
+
end
|
139
|
+
end
|
140
|
+
```
|
141
|
+
|
142
|
+
## Thank you
|
143
|
+
|
144
|
+
Many thanks to:
|
145
|
+
|
146
|
+
* [Mattt Thompson (@mattt)](https://twitter.com/mattt)
|
147
|
+
* [Vincent Ollivier (@vinc686)](https://twitter.com/vinc686)
|
148
|
+
|
149
|
+
## Contact
|
150
|
+
|
151
|
+
Nicolas Blanco
|
152
|
+
|
153
|
+
- http://github.com/nicolasblanco
|
154
|
+
- http://twitter.com/nblanco_fr
|
155
|
+
- nicolas@nicolasblanco.fr
|
156
|
+
|
157
|
+
## License
|
158
|
+
|
159
|
+
rails-param is available under the MIT license. See the LICENSE file for more info.
|
data/lib/rails_param.rb
ADDED
@@ -0,0 +1,162 @@
|
|
1
|
+
module RailsParam
|
2
|
+
module Param
|
3
|
+
|
4
|
+
DEFAULT_PRECISION = 14
|
5
|
+
|
6
|
+
class InvalidParameterError < StandardError
|
7
|
+
attr_accessor :param, :options
|
8
|
+
|
9
|
+
def message
|
10
|
+
return options[:message] if options.is_a?(Hash) && options.key?(:message)
|
11
|
+
super
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class MockController
|
16
|
+
include RailsParam::Param
|
17
|
+
attr_accessor :params
|
18
|
+
end
|
19
|
+
|
20
|
+
def param!(name, type, options = {}, &block)
|
21
|
+
name = name.to_s unless name.is_a? Integer # keep index for validating elements
|
22
|
+
|
23
|
+
return unless params.include?(name) || check_param_presence?(options[:default]) || options[:required]
|
24
|
+
|
25
|
+
begin
|
26
|
+
params[name] = coerce(params[name], type, options)
|
27
|
+
|
28
|
+
# set default
|
29
|
+
if options[:default].respond_to?(:call)
|
30
|
+
params[name] = options[:default].call
|
31
|
+
elsif params[name].nil? && check_param_presence?(options[:default])
|
32
|
+
params[name] = options[:default]
|
33
|
+
end
|
34
|
+
|
35
|
+
# apply tranformation
|
36
|
+
if params[name] && options[:transform]
|
37
|
+
params[name] = options[:transform].to_proc.call(params[name])
|
38
|
+
end
|
39
|
+
|
40
|
+
# validate
|
41
|
+
validate!(params[name], name, options)
|
42
|
+
|
43
|
+
if block_given?
|
44
|
+
if type == Array
|
45
|
+
params[name].each_with_index do |element, i|
|
46
|
+
if element.is_a?(Hash) || element.is_a?(ActionController::Parameters)
|
47
|
+
recurse element, &block
|
48
|
+
else
|
49
|
+
params[name][i] = recurse({ i => element }, i, &block) # supply index as key unless value is hash
|
50
|
+
end
|
51
|
+
end
|
52
|
+
else
|
53
|
+
recurse params[name], &block
|
54
|
+
end
|
55
|
+
end
|
56
|
+
params[name]
|
57
|
+
|
58
|
+
rescue InvalidParameterError => exception
|
59
|
+
exception.param ||= name
|
60
|
+
exception.options ||= options
|
61
|
+
raise exception
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# TODO: should we reintegrate this method?
|
66
|
+
# def one_of!(*names)
|
67
|
+
# count = 0
|
68
|
+
# names.each do |name|
|
69
|
+
# if params[name] and params[name].present?
|
70
|
+
# count += 1
|
71
|
+
# next unless count > 1
|
72
|
+
#
|
73
|
+
# error = "Parameters #{names.join(', ')} are mutually exclusive"
|
74
|
+
# if content_type and content_type.match(mime_type(:json))
|
75
|
+
# error = {message: error}.to_json
|
76
|
+
# end
|
77
|
+
#
|
78
|
+
# # do something with error object
|
79
|
+
# end
|
80
|
+
# end
|
81
|
+
# end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
def check_param_presence? param
|
86
|
+
not param.nil?
|
87
|
+
end
|
88
|
+
|
89
|
+
def recurse(params, index = nil)
|
90
|
+
raise InvalidParameterError, 'no block given' unless block_given?
|
91
|
+
controller = RailsParam::Param::MockController.new
|
92
|
+
controller.params = params
|
93
|
+
yield(controller, index)
|
94
|
+
end
|
95
|
+
|
96
|
+
def coerce(param, type, options = {})
|
97
|
+
begin
|
98
|
+
return nil if param.nil?
|
99
|
+
return param if (param.is_a?(type) rescue false)
|
100
|
+
if (param.is_a?(Array) && type != Array) || ((param.is_a?(Hash) || param.is_a?(ActionController::Parameters)) && type != Hash)
|
101
|
+
raise InvalidParameterError, "'#{param}' is not a valid #{type}"
|
102
|
+
end
|
103
|
+
return param if (param.is_a?(ActionController::Parameters) && type == Hash rescue false)
|
104
|
+
return Integer(param) if type == Integer
|
105
|
+
return Float(param) if type == Float
|
106
|
+
return String(param) if type == String
|
107
|
+
return Date.parse(param) if type == Date
|
108
|
+
return Time.parse(param) if type == Time
|
109
|
+
return DateTime.parse(param) if type == DateTime
|
110
|
+
return Array(param.split(options[:delimiter] || ",")) if type == Array
|
111
|
+
return Hash[param.split(options[:delimiter] || ",").map { |c| c.split(options[:separator] || ":") }] if type == Hash
|
112
|
+
return (/^(false|f|no|n|0)$/i === param.to_s ? false : (/^(true|t|yes|y|1)$/i === param.to_s ? true : (raise ArgumentError))) if type == TrueClass || type == FalseClass || type == :boolean
|
113
|
+
if type == BigDecimal
|
114
|
+
param = param.delete('$,').strip.to_f if param.is_a?(String)
|
115
|
+
return BigDecimal.new(param, (options[:precision] || DEFAULT_PRECISION))
|
116
|
+
end
|
117
|
+
return nil
|
118
|
+
rescue ArgumentError
|
119
|
+
raise InvalidParameterError, "'#{param}' is not a valid #{type}"
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def validate!(param, param_name, options)
|
124
|
+
options.each do |key, value|
|
125
|
+
case key
|
126
|
+
when :required
|
127
|
+
raise InvalidParameterError, "Parameter #{param_name} is required" if value && param.nil?
|
128
|
+
when :blank
|
129
|
+
raise InvalidParameterError, "Parameter #{param_name} cannot be blank" if !value && case param
|
130
|
+
when String
|
131
|
+
!(/\S/ === param)
|
132
|
+
when Array, Hash
|
133
|
+
param.empty?
|
134
|
+
else
|
135
|
+
param.nil?
|
136
|
+
end
|
137
|
+
when :format
|
138
|
+
raise InvalidParameterError, "Parameter #{param_name} must be a string if using the format validation" unless param.kind_of?(String)
|
139
|
+
raise InvalidParameterError, "Parameter #{param_name} must match format #{value}" unless param =~ value
|
140
|
+
when :is
|
141
|
+
raise InvalidParameterError, "Parameter #{param_name} must be #{value}" unless param === value
|
142
|
+
when :in, :within, :range
|
143
|
+
raise InvalidParameterError, "Parameter #{param_name} must be within #{value}" unless param.nil? || case value
|
144
|
+
when Range
|
145
|
+
value.include?(param)
|
146
|
+
else
|
147
|
+
Array(value).include?(param)
|
148
|
+
end
|
149
|
+
when :min
|
150
|
+
raise InvalidParameterError, "Parameter #{param_name} cannot be less than #{value}" unless param.nil? || value <= param
|
151
|
+
when :max
|
152
|
+
raise InvalidParameterError, "Parameter #{param_name} cannot be greater than #{value}" unless param.nil? || value >= param
|
153
|
+
when :min_length
|
154
|
+
raise InvalidParameterError, "Parameter #{param_name} cannot have length less than #{value}" unless param.nil? || value <= param.length
|
155
|
+
when :max_length
|
156
|
+
raise InvalidParameterError, "Parameter #{param_name} cannot have length greater than #{value}" unless param.nil? || value >= param.length
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
end
|
162
|
+
end
|
metadata
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rails_param2
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.10.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Nicolas Blanco
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-04-20 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.4'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '3.4'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rspec-rails
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '3.4'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '3.4'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: actionpack
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 3.2.0
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 3.2.0
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: activesupport
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 3.2.0
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 3.2.0
|
69
|
+
description: "\n Parameter Validation and Type Coercion for Rails\n "
|
70
|
+
email: nicolas@nicolasblanco.fr
|
71
|
+
executables: []
|
72
|
+
extensions: []
|
73
|
+
extra_rdoc_files: []
|
74
|
+
files:
|
75
|
+
- README.md
|
76
|
+
- lib/rails_param.rb
|
77
|
+
- lib/rails_param/param.rb
|
78
|
+
- lib/rails_param/version.rb
|
79
|
+
homepage: http://github.com/shhavel/rails_param2
|
80
|
+
licenses:
|
81
|
+
- MIT
|
82
|
+
metadata: {}
|
83
|
+
post_install_message:
|
84
|
+
rdoc_options:
|
85
|
+
- "--charset=UTF-8"
|
86
|
+
require_paths:
|
87
|
+
- lib
|
88
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
89
|
+
requirements:
|
90
|
+
- - ">="
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
version: '0'
|
93
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
94
|
+
requirements:
|
95
|
+
- - ">="
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: 1.3.6
|
98
|
+
requirements: []
|
99
|
+
rubyforge_project:
|
100
|
+
rubygems_version: 2.6.12
|
101
|
+
signing_key:
|
102
|
+
specification_version: 4
|
103
|
+
summary: Parameter Validation and Type Coercion for Rails
|
104
|
+
test_files: []
|