rails_param 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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 79d1fa666d9f249c4e2758fff4fe24be7856eeb8
4
+ data.tar.gz: c65e6e4d12a12a57667c659157229da75b9ce6c9
5
+ SHA512:
6
+ metadata.gz: a3c7b0fbafd0072dd049879f47b3c0d892411712dac4ca3c2a0d34d0221b42c5a40c90b732005998c3798920ec964b68961accfccabd3fb6996a88b77a331172
7
+ data.tar.gz: e0901f1e7eb5bb94f91480925f6b4876b63e3d85168159edf34c3fa4aec6fc98c137b89b7932825e2e323981b185351a17db70a87e56483464d964c26f9a9ab2
data/README.md ADDED
@@ -0,0 +1,92 @@
1
+ # rails-param
2
+ _Parameter Validation & Type Coercion for Rails_
3
+
4
+ This is a port of the gem [sinatra-param](https://github.com/mattt/sinatra-param) for the Rails framework.
5
+ All the credits go to [@mattt](https://twitter.com/mattt).
6
+ It has all the features of the sinatra-param gem, I used bang methods (param! and one_of!) to indicate that they are destructive as they change the controller params object and may raise an exception.
7
+
8
+ REST conventions take the guesswork out of designing and consuming web APIs. Simply `GET`, `POST`, `PATCH`, or `DELETE` resource endpoints, and you get what you'd expect.
9
+
10
+ However, when it comes to figuring out what parameters are expected... well, all bets are off.
11
+
12
+ This Rails extension takes a first step to solving this problem on the developer side
13
+
14
+ **`rails-param` allows you to declare, validate, and transform endpoint parameters as you would in frameworks like [ActiveModel](http://rubydoc.info/gems/activemodel/3.2.3/frames) or [DataMapper](http://datamapper.org/).**
15
+
16
+ > Use `rails-param` in combination with [`Rack::PostBodyContentTypeParser` and `Rack::NestedParams`](https://github.com/rack/rack-contrib) to automatically parameterize JSON `POST` bodies and nested parameters.
17
+
18
+ ## Example
19
+
20
+ ``` ruby
21
+ # GET /search?q=example
22
+ # GET /search?q=example&categories=news
23
+ # GET /search?q=example&sort=created_at&order=ASC
24
+ def search
25
+ param! :q, String, required: true
26
+ param! :categories, Array
27
+ param! :sort, String, default: "title"
28
+ param! :order, String, in: ["ASC", "DESC"], transform: :upcase, default: "ASC"
29
+ param! :price, String, format: "[<\=>]\s*\$\d+"
30
+
31
+ {...}
32
+ end
33
+ end
34
+ ```
35
+
36
+ ### Parameter Types
37
+
38
+ 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`.
39
+
40
+ - `String`
41
+ - `Integer`
42
+ - `Float`
43
+ - `Boolean` _("1/0", "true/false", "t/f", "yes/no", "y/n")_
44
+ - `Array` _("1,2,3,4,5")_
45
+ - `Hash` _(key1:value1,key2:value2)_
46
+ - `Date`, `Time`, & `DateTime`
47
+
48
+ ### Validations
49
+
50
+ Encapsulate business logic in a consistent way with validations. If a parameter does not satisfy a particular condition, a `400` error is returned with a message explaining the failure.
51
+
52
+ - `required`
53
+ - `blank`
54
+ - `is`
55
+ - `in`, `within`, `range`
56
+ - `min` / `max`
57
+ - `format`
58
+
59
+ ### Defaults and Transformations
60
+
61
+ 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`:
62
+
63
+ ```ruby
64
+ param! :attribution, String, default: "©"
65
+ param! :year, Integer, default: lambda { Time.now.year }
66
+ ```
67
+
68
+ 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.
69
+
70
+ ```ruby
71
+ param! :order, String, in: ["ASC", "DESC"], transform: :upcase, default: "ASC"
72
+ param! :offset, Integer, min: 0, transform: lambda {|n| n - (n % 10)}
73
+ ```
74
+
75
+ ## Thank you
76
+
77
+ Many thanks to:
78
+
79
+ * [Mattt Thompson (@mattt)](https://twitter.com/mattt)
80
+ * [Vincent Ollivier (@vinc686)](https://twitter.com/vinc686)
81
+
82
+ ## Contact
83
+
84
+ Nicolas Blanco
85
+
86
+ - http://github.com/nicolasblanco
87
+ - http://twitter.com/nblanco_fr
88
+ - nicolas@nicolasblanco.fr
89
+
90
+ ## License
91
+
92
+ rails-param is available under the MIT license. See the LICENSE file for more info.
@@ -0,0 +1,5 @@
1
+ require 'rails_param/param'
2
+
3
+ ActiveSupport.on_load(:action_controller) do
4
+ include RailsParam::Param
5
+ end
@@ -0,0 +1,110 @@
1
+ module RailsParam
2
+ module Param
3
+
4
+ class InvalidParameterError < StandardError
5
+ attr_accessor :param, :options
6
+ end
7
+
8
+ def param!(name, type, options = {})
9
+ name = name.to_s
10
+
11
+ return unless params.member?(name) || options[:default].present? || options[:required]
12
+
13
+ begin
14
+ params[name] = coerce(params[name], type, options)
15
+ params[name] = (options[:default].call if options[:default].respond_to?(:call)) || options[:default] if params[name].nil? and options[:default]
16
+ params[name] = options[:transform].to_proc.call(params[name]) if params[name] and options[:transform]
17
+ validate!(params[name], options)
18
+ # rescue InvalidParameterError => exception
19
+ # if options[:raise]
20
+ # exception.param, exception.options = name, options
21
+ # raise exception
22
+ # end
23
+
24
+ # error = "Invalid Parameter: #{name}"
25
+ # if content_type and content_type.match(mime_type(:json))
26
+ # error = {message: error, errors: {name => exception.message}}.to_json
27
+ # end
28
+
29
+ # # do something with error object
30
+ end
31
+ end
32
+
33
+ # def one_of!(*names)
34
+ # count = 0
35
+ # names.each do |name|
36
+ # if params[name] and params[name].present?
37
+ # count += 1
38
+ # next unless count > 1
39
+ #
40
+ # error = "Parameters #{names.join(', ')} are mutually exclusive"
41
+ # if content_type and content_type.match(mime_type(:json))
42
+ # error = {message: error}.to_json
43
+ # end
44
+ #
45
+ # # do something with error object
46
+ # end
47
+ # end
48
+ # end
49
+
50
+ private
51
+
52
+ def coerce(param, type, options = {})
53
+ begin
54
+ return nil if param.nil?
55
+ return param if (param.is_a?(type) rescue false)
56
+ return Integer(param) if type == Integer
57
+ return Float(param) if type == Float
58
+ return String(param) if type == String
59
+ return Date.parse(param) if type == Date
60
+ return Time.parse(param) if type == Time
61
+ return DateTime.parse(param) if type == DateTime
62
+ return Array(param.split(options[:delimiter] || ",")) if type == Array
63
+ return Hash[param.split(options[:delimiter] || ",").map{|c| c.split(options[:separator] || ":")}] if type == Hash
64
+ return (/(false|f|no|n|0)$/i === param.to_s ? false : (/(true|t|yes|y|1)$/i === param.to_s ? true : nil)) if type == TrueClass || type == FalseClass || type == Boolean
65
+ return nil
66
+ rescue ArgumentError
67
+ raise InvalidParameterError, "'#{param}' is not a valid #{type}"
68
+ end
69
+ end
70
+
71
+ def validate!(param, options)
72
+ options.each do |key, value|
73
+ case key
74
+ when :required
75
+ raise InvalidParameterError, "Parameter is required" if value && param.nil?
76
+ when :blank
77
+ raise InvalidParameterError, "Parameter cannot be blank" if !value && case param
78
+ when String
79
+ !(/\S/ === param)
80
+ when Array, Hash
81
+ param.empty?
82
+ else
83
+ param.nil?
84
+ end
85
+ when :format
86
+ raise InvalidParameterError, "Parameter must be a string if using the format validation" unless param.kind_of?(String)
87
+ raise InvalidParameterError, "Parameter must match format #{value}" unless param =~ value
88
+ when :is
89
+ raise InvalidParameterError, "Parameter must be #{value}" unless param === value
90
+ when :in, :within, :range
91
+ raise InvalidParameterError, "Parameter must be within #{value}" unless param.nil? || case value
92
+ when Range
93
+ value.include?(param)
94
+ else
95
+ Array(value).include?(param)
96
+ end
97
+ when :min
98
+ raise InvalidParameterError, "Parameter cannot be less than #{value}" unless param.nil? || value <= param
99
+ when :max
100
+ raise InvalidParameterError, "Parameter cannot be greater than #{value}" unless param.nil? || value >= param
101
+ when :min_length
102
+ raise InvalidParameterError, "Parameter cannot have length less than #{value}" unless param.nil? || value <= param.length
103
+ when :max_length
104
+ raise InvalidParameterError, "Parameter cannot have length greater than #{value}" unless param.nil? || value >= param.length
105
+ end
106
+ end
107
+ end
108
+
109
+ end
110
+ end
@@ -0,0 +1,3 @@
1
+ module RailsParam #:nodoc
2
+ VERSION = "0.0.1"
3
+ end
metadata ADDED
@@ -0,0 +1,104 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rails_param
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Nicolas Blanco
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-08-06 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: '2.11'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.11'
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: '2.11'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2.11'
41
+ - !ruby/object:Gem::Dependency
42
+ name: actionpack
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: 4.1.0
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: 4.1.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: 4.1.0
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: 4.1.0
69
+ description: "\n Parameter Validation & 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/nicolasblanco/rails_param
80
+ licenses:
81
+ - WTFPL
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.2.2
101
+ signing_key:
102
+ specification_version: 4
103
+ summary: Parameter Validation & Type Coercion for Rails
104
+ test_files: []