params_deserializers 1.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/README.md +281 -0
- data/Rakefile +52 -0
- data/lib/params_deserializers/attribute.rb +39 -0
- data/lib/params_deserializers/attribute_collection.rb +43 -0
- data/lib/params_deserializers/deserialize_params_with.rb +45 -0
- data/lib/params_deserializers/params_deserializer.rb +137 -0
- data/lib/params_deserializers/version.rb +32 -0
- data/lib/params_deserializers.rb +33 -0
- metadata +131 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 2829c56aa834160422e33c79e254b7803552c48f
|
4
|
+
data.tar.gz: 3a25a942e11b6511a2794707f201267bcfa98b69
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 16ee347201fa6f0c6d7a4827d0cdccf71d45e571168fa598fa877299f6baab25a766ee6a3c7625b2b3828eeafce6daa986ba6d5bb2963d31148a8a8721359557
|
7
|
+
data.tar.gz: b898af99a150774fc67ea2841c21ad8cecdf30fd4a51444dda181566d1002ade385b9f135d72be0b496c16781c3080f0dc8626b3457eb337f6b2ac8be71ce76a
|
data/README.md
ADDED
@@ -0,0 +1,281 @@
|
|
1
|
+
# Params Deserializers
|
2
|
+
|
3
|
+
## Purpose
|
4
|
+
|
5
|
+
Modeled after [`active_model_serializers`](https://github.com/rails-api/active_model_serializers), this gem allows you to create deserializer classes to modify incoming parameters in your Rails app.
|
6
|
+
|
7
|
+
Rails controllers often receive incoming parameters (via JSON payloads, for instance) that are not in the format Rails expects for things like saving models. With Params Deserializers, you can easily transform these incoming parameters into the format you want ([example](#an-example)).
|
8
|
+
|
9
|
+
## Getting started
|
10
|
+
|
11
|
+
Create a subclass of `ParamsDeserializer`:
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
# app/deserializers/user_params_deserializer.rb
|
15
|
+
|
16
|
+
class UserParamsDeserializer < ParamsDeserializer
|
17
|
+
end
|
18
|
+
```
|
19
|
+
|
20
|
+
To deserialize a hash with your deserializer, instantiate your deserializer with the hash, and call `deserialize` on it:
|
21
|
+
|
22
|
+
```ruby
|
23
|
+
new_hash = UserParamsDeserializer.new(old_hash).deserialize
|
24
|
+
```
|
25
|
+
|
26
|
+
## Listing params
|
27
|
+
|
28
|
+
Call the class method `attributes` to list incoming params:
|
29
|
+
|
30
|
+
```ruby
|
31
|
+
class UserParamsDeserializer < ParamsDeserializer
|
32
|
+
attributes :first_name, :last_name, :birthday
|
33
|
+
end
|
34
|
+
```
|
35
|
+
|
36
|
+
This class indicates that it expects `first_name`, `last_name`, and `birthday` keys on an incoming hash. Any other keys in the hash will be discarded.
|
37
|
+
|
38
|
+
Keys can also be listed individually using `attribute`:
|
39
|
+
|
40
|
+
```ruby
|
41
|
+
class UserParamsDeserializer < ParamsDeserializer
|
42
|
+
attribute :first_name
|
43
|
+
attribute :last_name
|
44
|
+
attribute :birthday
|
45
|
+
end
|
46
|
+
```
|
47
|
+
|
48
|
+
## Renaming params
|
49
|
+
|
50
|
+
You can pass an options hash to `attribute`. If you include the `:rename_to` key in the options hash, your deserialized params will contain the renamed key instead of the original:
|
51
|
+
|
52
|
+
```ruby
|
53
|
+
class UserParamsDeserializer < ParamsDeserializer
|
54
|
+
attribute :firstName, rename_to: :first_name
|
55
|
+
attribute :lastName, rename_to: :last_name
|
56
|
+
attribute :birthday
|
57
|
+
end
|
58
|
+
|
59
|
+
# Incoming hash: { :firstName => "Grace", :lastName => "Hopper, :birthday => "12/9/1906" }
|
60
|
+
# Deserialized params: { :first_name => "Grace", :last_name => "Hopper, :birthday => "12/9/1906" }
|
61
|
+
```
|
62
|
+
|
63
|
+
## Changing params case
|
64
|
+
|
65
|
+
You can apply snake_case, CamelCase, or lowerCamel cases to all your keys automatically using `format_keys`:
|
66
|
+
|
67
|
+
```ruby
|
68
|
+
class UserParamsDeserializer < ParamsDeserializer
|
69
|
+
attributes :firstName, :lastName, :birthday
|
70
|
+
format_keys :snake_case # (or :camel_case or :lower_camel)
|
71
|
+
end
|
72
|
+
|
73
|
+
# Incoming hash: { :firstName => "Grace", :lastName => "Hopper, :birthday => "12/9/1906" }
|
74
|
+
# Deserialized params: { :first_name => "Grace", :last_name => "Hopper, :birthday => "12/9/1906" }
|
75
|
+
```
|
76
|
+
|
77
|
+
## Dealing with a root key
|
78
|
+
|
79
|
+
If you expect a root key containing an object with all the other keys, call the `root` class method on your deserializer. Otherwise, it will look for the attributes you've listed at the top level:
|
80
|
+
|
81
|
+
```ruby
|
82
|
+
class UserParamsDeserializer < ParamsDeserializer
|
83
|
+
root: :user
|
84
|
+
attributes :first_name, :last_name
|
85
|
+
end
|
86
|
+
|
87
|
+
# Incoming hash: { :user => { :first_name => "Grace", :last_name => "Hopper" } }
|
88
|
+
# Deserialized params: { :user => { :first_name => "Grace", :last_name => "Hopper" } }
|
89
|
+
```
|
90
|
+
|
91
|
+
To discard the root key in your deserialized params, pass `{ discard: true }` as the second argument to `root`:
|
92
|
+
|
93
|
+
```ruby
|
94
|
+
class UserParamsDeserializer < ParamsDeserializer
|
95
|
+
root: :user, discard: true
|
96
|
+
attributes :first_name, :last_name
|
97
|
+
end
|
98
|
+
|
99
|
+
# Incoming hash: { :user => { :first_name => "Grace", :last_name => "Hopper" } }
|
100
|
+
# Deserialized params: { :first_name => "Grace", :last_name => "Hopper" }
|
101
|
+
```
|
102
|
+
|
103
|
+
## Deciding whether a key should be present
|
104
|
+
|
105
|
+
By default, any key listed via `attribute` or `attributes` will be included in the deserialized params if the key is present in `params`. However, the `attribute` class method takes an options hash. If the hash includes a `:present_if` key with a lambda value, it will execute that lambda to determine whether that key should be present in the deserialized params:
|
106
|
+
|
107
|
+
```ruby
|
108
|
+
class UserParamsDeserializer < ParamsDeserializer
|
109
|
+
attribute :first_name
|
110
|
+
attribute :last_name, present_if: -> { params[:last_name].length > 5 }
|
111
|
+
end
|
112
|
+
|
113
|
+
# Incoming hash: { :first_name => "Grace", :last_name => "Hop" }
|
114
|
+
# Deserialized params: { :first_name => "Grace" }
|
115
|
+
```
|
116
|
+
|
117
|
+
The lambda has access to the `params` hash, and can use it to determine whether the key should be present. In the example above, `last_name` is only included when its length is greater than 5.
|
118
|
+
|
119
|
+
## Overriding a key's value
|
120
|
+
|
121
|
+
If you define a method of the same name as a parameter, that method will be called to provide the parameter's value:
|
122
|
+
|
123
|
+
```ruby
|
124
|
+
class UserParamsDeserializer < ParamsDeserializer
|
125
|
+
attribute :birthday
|
126
|
+
|
127
|
+
def birthday
|
128
|
+
month, day, year = params[:birthday].split("/")
|
129
|
+
Time.new(year.to_i, month.to_i, day.to_i)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
# Incoming hash: { :birthday => "12/9/1906" }
|
134
|
+
# Deserialized params: { :birthday => 1906-12-09 00:00:00 -0000 }
|
135
|
+
```
|
136
|
+
|
137
|
+
Note that the method name should equal to the *renamed* parameter name, if the parameter has been renamed. For example, if you had passed `{ rename_to: :birth_date }` to `attribute` in the example above, the method you define would have to be `birth_date` instead of `birthday`.
|
138
|
+
|
139
|
+
## Integrating your deserializer with your controller
|
140
|
+
|
141
|
+
You can deserialize incoming parameters in two ways:
|
142
|
+
|
143
|
+
### Via `deserialize_params_with`
|
144
|
+
|
145
|
+
If you `include ParamsDeserializers` in your controller, your controller will have access to a class method called `deserialize_params_with`. By default, `deserialize_params_with` will create an instance variable called `deserialized_params`. Think of this as the `params` instance variable, but transformed according to your deserializer.
|
146
|
+
|
147
|
+
```ruby
|
148
|
+
# /app/controllers/users_controller.rb
|
149
|
+
|
150
|
+
class UsersController < ApplicationController
|
151
|
+
include ParamsDeserializers
|
152
|
+
deserialize_params_with UserParamsDeserializer
|
153
|
+
|
154
|
+
def create
|
155
|
+
@user = User.create(deserialized_params)
|
156
|
+
render json: @user
|
157
|
+
end
|
158
|
+
end
|
159
|
+
```
|
160
|
+
|
161
|
+
If you'd like a different instance variable name, such as `better_params`, pass an options hash to `deserialize_params_with` that contains an `:as` key:
|
162
|
+
|
163
|
+
```ruby
|
164
|
+
deserialize_params_with UserParamsDeserializer, as: :better_params
|
165
|
+
```
|
166
|
+
|
167
|
+
`better_params` will now be available to your controller action.
|
168
|
+
|
169
|
+
`deserialize_params_with` passes on any remaining keys in the options hash to `before_filter`, which means you can restrict deserialization to only certain controller actions:
|
170
|
+
|
171
|
+
```ruby
|
172
|
+
deserialize_params_with UserParamsDeserializer, as: :better_params, only: [:create, :update]
|
173
|
+
```
|
174
|
+
|
175
|
+
### Via instantiation of your deserializer
|
176
|
+
|
177
|
+
You can also simply instantiate your deserializer with `params` in your controller action, and then call `deserialize` on it to get the deserialized params:
|
178
|
+
|
179
|
+
```ruby
|
180
|
+
# /app/controllers/users_controller.rb
|
181
|
+
|
182
|
+
class UsersController < ApplicationController
|
183
|
+
def create
|
184
|
+
deserialized_params = UserParamsDeserializer.new(params).deserialize
|
185
|
+
@user = User.create(deserialized_params)
|
186
|
+
render json: @user
|
187
|
+
end
|
188
|
+
end
|
189
|
+
```
|
190
|
+
|
191
|
+
## An example
|
192
|
+
|
193
|
+
Create a subclass of `ParamsDeserializer`:
|
194
|
+
|
195
|
+
```ruby
|
196
|
+
# app/deserializers/user_params_deserializer.rb
|
197
|
+
|
198
|
+
class UserParamsDeserializer < ParamsDeserializer
|
199
|
+
root :user, discard: true
|
200
|
+
|
201
|
+
format_keys :snake_case
|
202
|
+
|
203
|
+
attributes :birthday,
|
204
|
+
:firstName,
|
205
|
+
:lastName
|
206
|
+
|
207
|
+
attribute :heightInInches,
|
208
|
+
rename_to: :height_in_feet,
|
209
|
+
present_if: -> { params[:height_in_inches].is_a? Integer }
|
210
|
+
|
211
|
+
def birthday
|
212
|
+
month, day, year = params[:birthday].split("/")
|
213
|
+
Time.new(year.to_i, month.to_i, day.to_i)
|
214
|
+
end
|
215
|
+
|
216
|
+
def height_in_feet
|
217
|
+
params[:heightInInches] / 12.0
|
218
|
+
end
|
219
|
+
end
|
220
|
+
```
|
221
|
+
|
222
|
+
Then, include it in your `UsersController`:
|
223
|
+
|
224
|
+
```ruby
|
225
|
+
# app/controllers/users_controller.rb
|
226
|
+
|
227
|
+
class UsersController < ApplicationController
|
228
|
+
include ParamsDeserializers
|
229
|
+
deserialize_params_with UserParamsDeserializer, as: :new_params, only: :create
|
230
|
+
|
231
|
+
def create
|
232
|
+
@user = User.create(new_params)
|
233
|
+
render json: @user
|
234
|
+
end
|
235
|
+
end
|
236
|
+
```
|
237
|
+
|
238
|
+
Now, when an API user hits `UsersController#create` with the following JSON payload:
|
239
|
+
|
240
|
+
```json
|
241
|
+
{
|
242
|
+
"user": {
|
243
|
+
"firstName": "Grace",
|
244
|
+
"lastName": "Hopper",
|
245
|
+
"birthday": "12/9/1906",
|
246
|
+
"heightInInches": 66
|
247
|
+
}
|
248
|
+
}
|
249
|
+
```
|
250
|
+
|
251
|
+
...`UsersController#create` will have access to an instance variable called `new_params` with the following contents:
|
252
|
+
|
253
|
+
```ruby
|
254
|
+
{
|
255
|
+
:first_name => "Grace",
|
256
|
+
:last_name => "Hopper",
|
257
|
+
:birthday => 1906-12-09 00:00:00 -0000,
|
258
|
+
:height_in_feet => 5.5
|
259
|
+
}
|
260
|
+
```
|
261
|
+
|
262
|
+
## Development
|
263
|
+
|
264
|
+
- Clone this repo.
|
265
|
+
- `bundle install`
|
266
|
+
- `bundle exec rspec` (or `bundle exec guard` to watch for changes)
|
267
|
+
|
268
|
+
## License
|
269
|
+
|
270
|
+
Copyright (c) 2015, Groupon, Inc.
|
271
|
+
All rights reserved.
|
272
|
+
|
273
|
+
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
274
|
+
|
275
|
+
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
276
|
+
|
277
|
+
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
278
|
+
|
279
|
+
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
280
|
+
|
281
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/Rakefile
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
# Copyright (c) 2015, Groupon, Inc.
|
2
|
+
# All rights reserved.
|
3
|
+
#
|
4
|
+
# Redistribution and use in source and binary forms, with or without
|
5
|
+
# modification, are permitted provided that the following conditions are met:
|
6
|
+
#
|
7
|
+
# 1. Redistributions of source code must retain the above copyright notice,
|
8
|
+
# this list of conditions and the following disclaimer.
|
9
|
+
#
|
10
|
+
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
11
|
+
# this list of conditions and the following disclaimer in the documentation
|
12
|
+
# and/or other materials provided with the distribution.
|
13
|
+
#
|
14
|
+
# 3. Neither the name of the copyright holder nor the names of its contributors
|
15
|
+
# may be used to endorse or promote products derived from this software without
|
16
|
+
# specific prior written permission.
|
17
|
+
#
|
18
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
19
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
20
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
21
|
+
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
22
|
+
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
23
|
+
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
24
|
+
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
25
|
+
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
26
|
+
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
27
|
+
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
28
|
+
# POSSIBILITY OF SUCH DAMAGE.
|
29
|
+
|
30
|
+
begin
|
31
|
+
require 'bundler/setup'
|
32
|
+
rescue LoadError
|
33
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
34
|
+
end
|
35
|
+
|
36
|
+
require 'rdoc/task'
|
37
|
+
|
38
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
39
|
+
rdoc.rdoc_dir = 'rdoc'
|
40
|
+
rdoc.title = 'ParamsDeserializers'
|
41
|
+
rdoc.options << '--line-numbers'
|
42
|
+
rdoc.rdoc_files.include('README.rdoc')
|
43
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
|
48
|
+
|
49
|
+
|
50
|
+
|
51
|
+
Bundler::GemHelper.install_tasks
|
52
|
+
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# Copyright (c) 2015, Groupon, Inc.
|
2
|
+
# All rights reserved.
|
3
|
+
#
|
4
|
+
# Redistribution and use in source and binary forms, with or without
|
5
|
+
# modification, are permitted provided that the following conditions are met:
|
6
|
+
#
|
7
|
+
# 1. Redistributions of source code must retain the above copyright notice,
|
8
|
+
# this list of conditions and the following disclaimer.
|
9
|
+
#
|
10
|
+
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
11
|
+
# this list of conditions and the following disclaimer in the documentation
|
12
|
+
# and/or other materials provided with the distribution.
|
13
|
+
#
|
14
|
+
# 3. Neither the name of the copyright holder nor the names of its contributors
|
15
|
+
# may be used to endorse or promote products derived from this software without
|
16
|
+
# specific prior written permission.
|
17
|
+
#
|
18
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
19
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
20
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
21
|
+
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
22
|
+
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
23
|
+
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
24
|
+
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
25
|
+
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
26
|
+
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
27
|
+
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
28
|
+
# POSSIBILITY OF SUCH DAMAGE.
|
29
|
+
|
30
|
+
class ParamsDeserializer
|
31
|
+
class Attribute
|
32
|
+
attr_reader :name, :present_if
|
33
|
+
|
34
|
+
def initialize(original_name, name = original_name, present_if = nil)
|
35
|
+
@original_name, @name, @present_if = original_name, name, present_if
|
36
|
+
@present_if ||= -> { params_root.has_key?(original_name) }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# Copyright (c) 2015, Groupon, Inc.
|
2
|
+
# All rights reserved.
|
3
|
+
#
|
4
|
+
# Redistribution and use in source and binary forms, with or without
|
5
|
+
# modification, are permitted provided that the following conditions are met:
|
6
|
+
#
|
7
|
+
# 1. Redistributions of source code must retain the above copyright notice,
|
8
|
+
# this list of conditions and the following disclaimer.
|
9
|
+
#
|
10
|
+
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
11
|
+
# this list of conditions and the following disclaimer in the documentation
|
12
|
+
# and/or other materials provided with the distribution.
|
13
|
+
#
|
14
|
+
# 3. Neither the name of the copyright holder nor the names of its contributors
|
15
|
+
# may be used to endorse or promote products derived from this software without
|
16
|
+
# specific prior written permission.
|
17
|
+
#
|
18
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
19
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
20
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
21
|
+
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
22
|
+
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
23
|
+
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
24
|
+
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
25
|
+
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
26
|
+
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
27
|
+
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
28
|
+
# POSSIBILITY OF SUCH DAMAGE.
|
29
|
+
|
30
|
+
class ParamsDeserializer
|
31
|
+
class AttributeCollection < Array
|
32
|
+
class NameCollisionError < StandardError; end
|
33
|
+
|
34
|
+
def <<(new_attr)
|
35
|
+
if any? { |attr| attr.name == new_attr.name }
|
36
|
+
raise NameCollisionError,
|
37
|
+
"Attribute \"#{new_attr.name}\" was defined multiple times."
|
38
|
+
end
|
39
|
+
|
40
|
+
super
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# Copyright (c) 2015, Groupon, Inc.
|
2
|
+
# All rights reserved.
|
3
|
+
#
|
4
|
+
# Redistribution and use in source and binary forms, with or without
|
5
|
+
# modification, are permitted provided that the following conditions are met:
|
6
|
+
#
|
7
|
+
# 1. Redistributions of source code must retain the above copyright notice,
|
8
|
+
# this list of conditions and the following disclaimer.
|
9
|
+
#
|
10
|
+
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
11
|
+
# this list of conditions and the following disclaimer in the documentation
|
12
|
+
# and/or other materials provided with the distribution.
|
13
|
+
#
|
14
|
+
# 3. Neither the name of the copyright holder nor the names of its contributors
|
15
|
+
# may be used to endorse or promote products derived from this software without
|
16
|
+
# specific prior written permission.
|
17
|
+
#
|
18
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
19
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
20
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
21
|
+
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
22
|
+
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
23
|
+
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
24
|
+
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
25
|
+
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
26
|
+
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
27
|
+
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
28
|
+
# POSSIBILITY OF SUCH DAMAGE.
|
29
|
+
|
30
|
+
require 'active_support/concern'
|
31
|
+
|
32
|
+
module ParamsDeserializers
|
33
|
+
extend ActiveSupport::Concern
|
34
|
+
|
35
|
+
module ClassMethods
|
36
|
+
def deserialize_params_with(deserializer, options = {})
|
37
|
+
deserialized_params_name = options.delete(:as).try(:to_sym) || :deserialized_params
|
38
|
+
attr_reader deserialized_params_name
|
39
|
+
|
40
|
+
before_filter(options) do
|
41
|
+
instance_variable_set("@#{deserialized_params_name}", deserializer.new(params).deserialize)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
# Copyright (c) 2015, Groupon, Inc.
|
2
|
+
# All rights reserved.
|
3
|
+
#
|
4
|
+
# Redistribution and use in source and binary forms, with or without
|
5
|
+
# modification, are permitted provided that the following conditions are met:
|
6
|
+
#
|
7
|
+
# 1. Redistributions of source code must retain the above copyright notice,
|
8
|
+
# this list of conditions and the following disclaimer.
|
9
|
+
#
|
10
|
+
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
11
|
+
# this list of conditions and the following disclaimer in the documentation
|
12
|
+
# and/or other materials provided with the distribution.
|
13
|
+
#
|
14
|
+
# 3. Neither the name of the copyright holder nor the names of its contributors
|
15
|
+
# may be used to endorse or promote products derived from this software without
|
16
|
+
# specific prior written permission.
|
17
|
+
#
|
18
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
19
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
20
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
21
|
+
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
22
|
+
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
23
|
+
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
24
|
+
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
25
|
+
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
26
|
+
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
27
|
+
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
28
|
+
# POSSIBILITY OF SUCH DAMAGE.
|
29
|
+
|
30
|
+
require 'active_support/core_ext/hash/indifferent_access'
|
31
|
+
require 'active_support/core_ext/string/inflections'
|
32
|
+
|
33
|
+
class ParamsDeserializer
|
34
|
+
class MissingRootKeyError < StandardError; end
|
35
|
+
|
36
|
+
def initialize(params)
|
37
|
+
@params = params
|
38
|
+
verify_root_key_exists
|
39
|
+
end
|
40
|
+
|
41
|
+
def deserialize
|
42
|
+
deserialized_params = {}
|
43
|
+
self.class.attrs.each do |attr|
|
44
|
+
next unless instance_exec(&attr.present_if)
|
45
|
+
deserialized_params[attr.name] = self.send(attr.name)
|
46
|
+
end
|
47
|
+
format_keys(deserialized_params).with_indifferent_access
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
attr_reader :params
|
53
|
+
|
54
|
+
def format_key(key)
|
55
|
+
case self.class.key_format
|
56
|
+
when :snake_case then key.to_s.underscore.to_sym
|
57
|
+
when :camel_case then key.to_s.camelize.to_sym
|
58
|
+
when :lower_camel then key.to_s.camelize(:lower).to_sym
|
59
|
+
else key
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def format_keys(hash)
|
64
|
+
hash = Hash[hash.map { |k, v| [format_key(k), v] }] if self.class.key_format
|
65
|
+
optionally_include_root_key(hash)
|
66
|
+
end
|
67
|
+
|
68
|
+
def optionally_include_root_key(hash)
|
69
|
+
return hash unless self.class.include_root_key?
|
70
|
+
{ format_key(self.class.root_key) => hash }
|
71
|
+
end
|
72
|
+
|
73
|
+
def params_root
|
74
|
+
return @params_root if @params_root
|
75
|
+
|
76
|
+
if self.class.root_key
|
77
|
+
@params_root = params[self.class.root_key]
|
78
|
+
else
|
79
|
+
@params_root = params
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def verify_root_key_exists
|
84
|
+
if self.class.root_key && !params.has_key?(self.class.root_key)
|
85
|
+
raise MissingRootKeyError, "Root key #{self.class.root_key} is missing from params."
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
class << self
|
90
|
+
attr_reader :root_key
|
91
|
+
attr_accessor :key_format
|
92
|
+
alias_method :format_keys, :key_format=
|
93
|
+
|
94
|
+
def attrs
|
95
|
+
@attrs ||= AttributeCollection.new
|
96
|
+
end
|
97
|
+
|
98
|
+
def attribute(attr, options = {})
|
99
|
+
define_getter_method(attr, options) do
|
100
|
+
params_root[attr]
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def attributes(*args)
|
105
|
+
args.each do |attr|
|
106
|
+
attribute(attr)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def has_many(attr, options = {})
|
111
|
+
define_getter_method(attr, options) do
|
112
|
+
return params_root[attr] unless options[:deserializer]
|
113
|
+
|
114
|
+
params_root[attr].map do |relation|
|
115
|
+
options[:deserializer].new(relation).deserialize
|
116
|
+
end if params_root[attr].is_a?(Array)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def root(key, options = {})
|
121
|
+
@root_key = key
|
122
|
+
@discard_root_key = options[:discard]
|
123
|
+
end
|
124
|
+
|
125
|
+
def include_root_key?
|
126
|
+
@root_key && !@discard_root_key
|
127
|
+
end
|
128
|
+
|
129
|
+
private
|
130
|
+
|
131
|
+
def define_getter_method(attr, options = {}, &block)
|
132
|
+
options[:rename_to] ||= attr
|
133
|
+
attrs << Attribute.new(attr, options[:rename_to], options[:present_if])
|
134
|
+
define_method(options[:rename_to], &block) unless method_defined?(options[:rename_to])
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# Copyright (c) 2015, Groupon, Inc.
|
2
|
+
# All rights reserved.
|
3
|
+
#
|
4
|
+
# Redistribution and use in source and binary forms, with or without
|
5
|
+
# modification, are permitted provided that the following conditions are met:
|
6
|
+
#
|
7
|
+
# 1. Redistributions of source code must retain the above copyright notice,
|
8
|
+
# this list of conditions and the following disclaimer.
|
9
|
+
#
|
10
|
+
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
11
|
+
# this list of conditions and the following disclaimer in the documentation
|
12
|
+
# and/or other materials provided with the distribution.
|
13
|
+
#
|
14
|
+
# 3. Neither the name of the copyright holder nor the names of its contributors
|
15
|
+
# may be used to endorse or promote products derived from this software without
|
16
|
+
# specific prior written permission.
|
17
|
+
#
|
18
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
19
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
20
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
21
|
+
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
22
|
+
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
23
|
+
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
24
|
+
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
25
|
+
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
26
|
+
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
27
|
+
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
28
|
+
# POSSIBILITY OF SUCH DAMAGE.
|
29
|
+
|
30
|
+
module ParamsDeserializers
|
31
|
+
VERSION = "1.0.1"
|
32
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# Copyright (c) 2015, Groupon, Inc.
|
2
|
+
# All rights reserved.
|
3
|
+
#
|
4
|
+
# Redistribution and use in source and binary forms, with or without
|
5
|
+
# modification, are permitted provided that the following conditions are met:
|
6
|
+
#
|
7
|
+
# 1. Redistributions of source code must retain the above copyright notice,
|
8
|
+
# this list of conditions and the following disclaimer.
|
9
|
+
#
|
10
|
+
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
11
|
+
# this list of conditions and the following disclaimer in the documentation
|
12
|
+
# and/or other materials provided with the distribution.
|
13
|
+
#
|
14
|
+
# 3. Neither the name of the copyright holder nor the names of its contributors
|
15
|
+
# may be used to endorse or promote products derived from this software without
|
16
|
+
# specific prior written permission.
|
17
|
+
#
|
18
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
19
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
20
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
21
|
+
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
22
|
+
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
23
|
+
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
24
|
+
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
25
|
+
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
26
|
+
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
27
|
+
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
28
|
+
# POSSIBILITY OF SUCH DAMAGE.
|
29
|
+
|
30
|
+
require_relative './params_deserializers/attribute'
|
31
|
+
require_relative './params_deserializers/attribute_collection'
|
32
|
+
require_relative './params_deserializers/params_deserializer'
|
33
|
+
require_relative './params_deserializers/deserialize_params_with'
|
metadata
ADDED
@@ -0,0 +1,131 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: params_deserializers
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jesse Pinho
|
8
|
+
- Trek Glowacki
|
9
|
+
- Jim Challenger
|
10
|
+
autorequire:
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
date: 2015-11-08 00:00:00.000000000 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: activesupport
|
17
|
+
requirement: !ruby/object:Gem::Requirement
|
18
|
+
requirements:
|
19
|
+
- - '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 3.2.16
|
22
|
+
- - <
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: '5.0'
|
25
|
+
type: :runtime
|
26
|
+
prerelease: false
|
27
|
+
version_requirements: !ruby/object:Gem::Requirement
|
28
|
+
requirements:
|
29
|
+
- - '>='
|
30
|
+
- !ruby/object:Gem::Version
|
31
|
+
version: 3.2.16
|
32
|
+
- - <
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '5.0'
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: guard-rspec
|
37
|
+
requirement: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - '>='
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '0'
|
42
|
+
type: :development
|
43
|
+
prerelease: false
|
44
|
+
version_requirements: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - '>='
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
49
|
+
- !ruby/object:Gem::Dependency
|
50
|
+
name: sqlite3
|
51
|
+
requirement: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - '>='
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
56
|
+
type: :development
|
57
|
+
prerelease: false
|
58
|
+
version_requirements: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - '>='
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
- !ruby/object:Gem::Dependency
|
64
|
+
name: rails
|
65
|
+
requirement: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '4.2'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - '>='
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '4.2'
|
77
|
+
- !ruby/object:Gem::Dependency
|
78
|
+
name: rspec-rails
|
79
|
+
requirement: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - '>='
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0'
|
84
|
+
type: :development
|
85
|
+
prerelease: false
|
86
|
+
version_requirements: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - '>='
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '0'
|
91
|
+
description: Modeled after active_model_serializers, this gem allows you to create
|
92
|
+
deserializer classes for incoming Rails parameters.
|
93
|
+
email:
|
94
|
+
- jesse@jessepinho.com
|
95
|
+
executables: []
|
96
|
+
extensions: []
|
97
|
+
extra_rdoc_files: []
|
98
|
+
files:
|
99
|
+
- README.md
|
100
|
+
- Rakefile
|
101
|
+
- lib/params_deserializers.rb
|
102
|
+
- lib/params_deserializers/attribute.rb
|
103
|
+
- lib/params_deserializers/attribute_collection.rb
|
104
|
+
- lib/params_deserializers/deserialize_params_with.rb
|
105
|
+
- lib/params_deserializers/params_deserializer.rb
|
106
|
+
- lib/params_deserializers/version.rb
|
107
|
+
homepage: https://github.com/groupon/params_deserializers
|
108
|
+
licenses:
|
109
|
+
- BSD-3-Clause
|
110
|
+
metadata: {}
|
111
|
+
post_install_message:
|
112
|
+
rdoc_options: []
|
113
|
+
require_paths:
|
114
|
+
- lib
|
115
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
116
|
+
requirements:
|
117
|
+
- - '>='
|
118
|
+
- !ruby/object:Gem::Version
|
119
|
+
version: '0'
|
120
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - '>='
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
requirements: []
|
126
|
+
rubyforge_project:
|
127
|
+
rubygems_version: 2.4.8
|
128
|
+
signing_key:
|
129
|
+
specification_version: 4
|
130
|
+
summary: Deserializers for Rails params
|
131
|
+
test_files: []
|