deserializer 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d2d925b16aaf70730f68ed799b22236a2b4a9c1b
4
- data.tar.gz: 291d5ba90c44ddd31eeab94c314f16f414ecf7df
3
+ metadata.gz: 3e444e500d391065ce2aed5b2b41cb605fa443df
4
+ data.tar.gz: ba9db864ddb2d65dce89ddcb494939eda0d05428
5
5
  SHA512:
6
- metadata.gz: df1caed559319d4c42f6770408172d600c8511260126a8eec742d645586d14861e8468999f094d195f0a9462c0bf09efe2afc3f289166f3c42f330e750e96e1d
7
- data.tar.gz: 2a87db6f696784599033d89f4b41abad6bb2094890f7a403c83182afab53094c0714a7ff8d276a0a113c00a9e9b9990498f5b723721c5b23dc317dee305e36c5
6
+ metadata.gz: c84a34731a8b4e26b93b8ac96116bf39116a9617676330b32465e1ff8ab743491a33c54d8eb321cad436d5ab8a1fc6954be981405066e9b48d62459cb6d47911
7
+ data.tar.gz: 302c940e2185dfc3eaf5471cf9ea34ca276a5b8a5327f7d94a216d55dc27e4a527ec39a2b35a832885ea6c7d826d286d7725d4e06f0957ebe1bae08e20c5932c
@@ -1,12 +1,21 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- deserializer (0.2.0)
4
+ deserializer (0.3.0)
5
+ activesupport
5
6
 
6
7
  GEM
7
8
  remote: https://rubygems.org/
8
9
  specs:
10
+ activesupport (4.2.5)
11
+ i18n (~> 0.7)
12
+ json (~> 1.7, >= 1.7.7)
13
+ minitest (~> 5.1)
14
+ thread_safe (~> 0.3, >= 0.3.4)
15
+ tzinfo (~> 1.1)
9
16
  docile (1.1.5)
17
+ i18n (0.7.0)
18
+ json (1.8.3)
10
19
  minitest (5.7.0)
11
20
  multi_json (1.11.1)
12
21
  rake (10.4.2)
@@ -15,6 +24,9 @@ GEM
15
24
  multi_json (~> 1.0)
16
25
  simplecov-html (~> 0.9.0)
17
26
  simplecov-html (0.9.0)
27
+ thread_safe (0.3.5)
28
+ tzinfo (1.2.2)
29
+ thread_safe (~> 0.1)
18
30
 
19
31
  PLATFORMS
20
32
  ruby
data/README.md CHANGED
@@ -1,12 +1,13 @@
1
1
  # Deserializer
2
-
3
- Hash transformation and sanitization. Deserialization of complex parameters into a hash that an AR model can take.
4
-
5
- Lets you have a reverse ActiveModel::Sereializer-like interface that allows for easy create and update without having to write heavy controllers.
2
+ ## Features
3
+ - Hash transformation and sanitization
4
+ - Deserialization of complex parameters into a hash that an AR model can take
5
+ - Avoid having multiple definitions in fragile arrays when using strong params
6
+ - Easy create and update from JSON without writing heavy controllers
7
+ - [ActiveModel::Serializer](https://github.com/rails-api/active_model_serializers)-like interface and conventions
6
8
 
7
9
  ## Problem
8
-
9
- Let's say we have a API create endpoint that takes json that looks something like
10
+ Let's say we have an API with an endpoint that takes this JSON:
10
11
 
11
12
  ```json
12
13
  {
@@ -23,7 +24,7 @@ Let's say we have a API create endpoint that takes json that looks something lik
23
24
  }
24
25
  ```
25
26
 
26
- that goes into a flat DishReview model that looks like
27
+ But this goes into a flat DishReview model:
27
28
 
28
29
  ```ruby
29
30
  t.belongs_to :restaurant
@@ -37,11 +38,10 @@ t.string :texture
37
38
  t.string :smell
38
39
  ```
39
40
 
40
- what do we do?
41
+ ### Solution (No `Deserializer`)
42
+ Permit some params, do some parsing and feed that into `DishReview.new`:
41
43
 
42
- Normally, we'd have some params we permit, do some parsing and feed those into `DishReview.new`, like
43
-
44
- ``` ruby
44
+ ```ruby
45
45
  class DishReviewController < BaseController
46
46
 
47
47
  def create
@@ -93,30 +93,78 @@ class DishReviewController < BaseController
93
93
  end
94
94
  ```
95
95
 
96
- and that's fine, but kind of annoying, and you have to do this for every action. It makes the controllers heavy, hard to parse, fragile, and really do things that are no longer controller-y.
96
+ #### What's up with that?
97
+ - You have to do this for every action
98
+ - Controllers are obese, hard to parse and fragile
99
+ - Controllers are doing non-controller-y things
100
+
101
+ ### Solution (With `Deserializer`)
102
+ `DishReviewDeserializer`:
103
+
104
+ ```ruby
105
+ module MyApi
106
+ module V1
107
+ class DishReviewDeserializer < Deserializer::Base
108
+ attributes :restaurant_id
109
+ :user_id
110
+ :description
111
+
112
+ attribute :name, key: :dish_name
113
+
114
+ has_one :ratings, :deserializer => RatingsDeserializer
115
+
116
+ def ratings
117
+ object
118
+ end
119
+
120
+ end
121
+ end
122
+ end
123
+ ```
97
124
 
98
- So what we have here is a wrapper that lets us get away from polluting the controller with all of this parsing and lets us build deserializers that look very much like our serializers.
125
+ `RatingsDeserializer`:
99
126
 
100
- ## Usage
127
+ ```ruby
128
+ module MyApi
129
+ module V1
130
+ class RatingsDeserializer < Deserializer::Base
101
131
 
102
- Deserializer acts and looks pretty mich identical to ActiveModel::Serializer. It has attributes, attribute, and the has_one association. It does not currently support has_many, as that's an odd thing for a write endpoint to support, but can easily be added.
132
+ attributes :taste,
133
+ :color,
134
+ :texture,
135
+ :smell
136
+ end
137
+ end
138
+ end
139
+ ```
103
140
 
104
- ### Deserializer functions
141
+ All of this allows your controller to be so very small:
105
142
 
106
- #### from_params
107
- `MyDeserializer.from_params(params)` created the json that your AR model will then consume.
108
143
  ```ruby
109
- @review = DishReview.new( MyApi::V1::DishReviewDeserailzer.from_params(params) )
144
+ class DishReviewsController < YourApiController::Base
145
+ def create
146
+ @review = DishReview.new( MyApi::V1::DishReviewDeserailzer.from_params(params) )
147
+
148
+ if @review.save
149
+ # return review
150
+ else
151
+ # return sad errors splody
152
+ end
153
+ end
154
+
155
+ # RUD
156
+ end
110
157
  ```
111
158
 
112
- #### permitted_params
113
- If you're using strong params, this lets you avoid having multiple definitions in fragile arrays. Just call ` MyDeserailzer.permitted_params` and you'll have the full array of keys you expect params to have.
159
+ #### What's up with that?
160
+ - Un-pollutes controllers from all the parsing
161
+ - Builds deserializers that look like our serializers
114
162
 
115
- ### Deserializer Definition
116
- To define a deserializer, you inherit from `Deserializer::Base` and define it in much the same way you would an `ActiveModel::Serializer`.
163
+ ## Definition
164
+ Inherit from `Deserializer::Base` and define it in much the same way you would an [ActiveModel::Serializer](https://github.com/rails-api/active_model_serializers).
117
165
 
118
- #### attributes
119
- This is straight 1:1 mapping from params to the model, so
166
+ ### attributes
167
+ Use `attributes` for straight mapping from params to the model:
120
168
 
121
169
  ```ruby
122
170
  class PostDeserializer < Deserializer::Base
@@ -124,31 +172,70 @@ class PostDeserializer < Deserializer::Base
124
172
  :body
125
173
  end
126
174
  ```
127
- with params `{"title" => "lorem", "body" => "ipsum"}`, will give you a hash of `{title: "lorem", body: "ipsum"}`.
128
175
 
129
- #### attribute
130
- `attribute` is the singular version of `attributes`, but like `ActiveModel::Serializer` it can take a `:key`
176
+ ```ruby
177
+ # Example params
178
+ {
179
+ "title" => "lorem",
180
+ "body" => "ipsum"
181
+ }
182
+ # Resulting hash
183
+ {
184
+ title: "lorem",
185
+ body: "ipsum"
186
+ }
187
+ ```
188
+
189
+ ### attribute
190
+ Allows the following customizations for each `attribute`
191
+ #### :key
192
+
131
193
  ```ruby
132
194
  class PostDeserializer < Deserializer::Base
133
195
  attribute :title, ignore_empty: true
134
- attribute :body, key: :text
196
+ attribute :body, key: :content
135
197
  end
136
198
  ```
137
- It is symmetric with `ActiveModel::Serializer`, so that :text is what it will get in params, but :body is what it will insert into the result.
138
199
 
139
- For example with params of `{"title" => "lorem", "text" => "ipsum"}` this desrerializer will produce `{title: "lorem", body: "ipsum"}`.
200
+ `:content` here is what it will get in params while `:body` is what it will be inserted into the result.
201
+
202
+ ```ruby
203
+ # Example params
204
+ {
205
+ "title" => "lorem",
206
+ "content" => "ipsum"
207
+ }
208
+ # Resulting hash
209
+ {
210
+ title: "lorem",
211
+ body: "ipsum"
212
+ }
213
+ ```
214
+
215
+ #### :ignore_empty
216
+ While `Deserializer`'s default is to pass all values through, this option will drop any key with `false`/`nil`/`""`/`[]`/`{}` values from the result.
140
217
 
141
- `ignore_empty` is an option to ignore `false`/`nil`/`""`/`[]`/`{}` values that may come into the deserializer. By defualt it will pass the value through. With this option, it will drop the key from the result, turning `{"title" => "", "text" => nil}` into `{}`
218
+ ```ruby
219
+ # Example params
220
+ {
221
+ "title" => "",
222
+ "text" => nil
223
+ }
224
+ # Resulting hash
225
+ {}
226
+ ```
142
227
 
143
- `convert_with` allows the deserializer to deserialize and convert a value at the same time. For example, if we have a `Post` model that looks like
228
+ #### :convert_with
229
+ Allows deserializing and converting a value at the same time. For example:
144
230
 
145
- ```ruby
231
+ ```ruby
146
232
  class Post < ActiveRecord::Base
147
233
  belongs_to :post_type # this is a domain table
148
234
  end
149
235
  ```
150
236
 
151
- and we serialize with
237
+ If we serialize with
238
+
152
239
  ```ruby
153
240
  class PostSerializer < ActiveModel::Serializer
154
241
  attribute :type
@@ -159,13 +246,13 @@ class PostSerializer < ActiveModel::Serializer
159
246
  end
160
247
  ```
161
248
 
162
- Then, when we if we get a symbolic name from the controller, but want to work with an id in the backend, we can do something like:
249
+ Then, when we get a symbolic name from the controller but want to work with an id in the backend, we can:
163
250
 
164
251
  ```ruby
165
252
  class PostDeserializer < Deserializer::Base
166
253
  attribute :title, ignore_empty: true
167
254
  attribute :body
168
- attribute :post_type_id, key: type, convert_with: to_type_id
255
+ attribute :post_type_id, key: :type, convert_with: to_type_id
169
256
 
170
257
  def to_type_id(value)
171
258
  Type.find_by_symbolic_name.id
@@ -173,13 +260,25 @@ class PostDeserializer < Deserializer::Base
173
260
  end
174
261
  ```
175
262
 
176
- which would take the params `{"title" => "lorem", "body" => "ipsum", "type" => "BLAGABLAG"}` and produce `{title: "lorem", body: "ipsum", post_type_id: 1}`
263
+ ```ruby
264
+ # Example params
265
+ {
266
+ "title" => "lorem",
267
+ "body" => "ipsum",
268
+ "type" => "BLAGABLAG"
269
+ }
270
+ # Resulting hash
271
+ {
272
+ title: "lorem",
273
+ body: "ipsum",
274
+ post_type_id: 1
275
+ }
276
+ ```
177
277
 
278
+ ### has_one
279
+ `has_one` association expects a param and its deserializer:
178
280
 
179
- #### has_one
180
- NOTE: This is the only association currently supported by `Deserializer`.
181
- `has_one` expects the param and its deserializer.
182
- ```ruby
281
+ ```ruby
183
282
  class DishDeserializer < Deserializer::Base
184
283
  # probably other stuff
185
284
  has_one :ratings, deserializer: RatingsDeserializer
@@ -190,10 +289,27 @@ class RatingsDeserializer < Deserializer::Base
190
289
  :smell
191
290
  end
192
291
  ```
193
- So for params `{"ratings" => {"taste" => "bad", "smell" => "good"}}` you would get `{ratings: {taste: "bad", smell: "good"}}`
194
292
 
195
- #### Overriding Attribute Methods
196
- So let's say in the example above, your internal representation of ratings inside `Dish` is actually called `scores`, you can do
293
+ ```ruby
294
+ # Example params
295
+ {
296
+ "ratings" => {
297
+ "taste" => "bad",
298
+ "smell" => "good"
299
+ }
300
+ }
301
+ # Resulting hash
302
+ {
303
+ ratings: {
304
+ taste: "bad",
305
+ smell: "good"
306
+ }
307
+ }
308
+ ```
309
+
310
+ #### Deserialize into a Different Name
311
+ In the example above, if `ratings` inside `Dish` is called `scores` in your ActiveRecord, you can:
312
+
197
313
  ```ruby
198
314
  class DishDeserializer < Deserializer::Base
199
315
  has_one :ratings, deserializer: RatingsDeserializer
@@ -203,9 +319,26 @@ class DishDeserializer < Deserializer::Base
203
319
  end
204
320
  end
205
321
  ```
206
- which will give you `{scores: {taste: "bad", smell: "good"}}` for params `{"ratings" => {"taste" => "bad", "smell" => "good"}}`
207
322
 
208
- or, if you want to deserialize `ratings` into your `dish` object, you can use `object`
323
+ ```ruby
324
+ # Example params
325
+ {
326
+ "ratings" => {
327
+ "taste" => "bad",
328
+ "smell" => "good"
329
+ }
330
+ }
331
+ # Resulting hash
332
+ {
333
+ scores: {
334
+ taste: "bad",
335
+ smell: "good"
336
+ }
337
+ }
338
+ ```
339
+
340
+ #### Deserialize into Parent Object
341
+ To deserialize `ratings` into the `dish` object, you can use `object`:
209
342
 
210
343
  ```ruby
211
344
  class DishDeserializer < Deserializer::Base
@@ -216,9 +349,17 @@ class DishDeserializer < Deserializer::Base
216
349
  end
217
350
  end
218
351
  ```
219
- which will give you `{taste: "bad", smell: "good"}` for params `{"ratings" => {"taste" => "bad", "smell" => "good"}}`
220
352
 
221
- or you can deserialize into another subobject by doing
353
+ ```ruby
354
+ # Resulting hash
355
+ {
356
+ taste: "bad",
357
+ smell: "good"
358
+ }
359
+ ```
360
+
361
+ #### Deserialize into a Different Sub-object
362
+
222
363
  ```ruby
223
364
  class DishDeserializer < Deserializer::Base
224
365
  has_one :colors, deserializer: ColorsDeserializer
@@ -229,99 +370,126 @@ class DishDeserializer < Deserializer::Base
229
370
  end
230
371
  end
231
372
  ```
232
- which, given params
233
- ```
234
- {
235
- "ratings" =>
236
- {
373
+
374
+ Given params:
375
+
376
+ ```ruby
377
+ # Example params
378
+ {
379
+ "ratings" =>
380
+ {
237
381
  "taste" => "bad",
238
382
  "smell" => "good"
239
- },
240
- "colors" =>
241
- {
383
+ },
384
+ "colors" =>
385
+ {
242
386
  "color" => "red"
243
387
  }
244
388
  }
389
+ # Resulting hash
390
+ {
391
+ ratings: {
392
+ taste: "bad",
393
+ smell: "good",
394
+ color: "red"
395
+ }
396
+ }
245
397
  ```
246
- , will give you `{ratings: {taste: "bad", smell: "good", color: "red"}}`
247
398
 
248
- ### Example
399
+ ### has_many
400
+ Not supported as it's an odd thing for a write endpoint to support, but can easily be added.
401
+
402
+ ### nests
403
+ Sometimes you get a flat param list, but want it to be nested for `updated_nested_attributes`
249
404
 
250
- So the example above will combine all of those to look like
405
+ If you have 2 models that look like
251
406
 
252
407
  ```ruby
253
- module MyApi
254
- module V1
255
- class DishReviewDeserializer < Deserializer::Base
256
- attributes :restaurant_id
257
- :user_id
258
- :description
408
+ class RestaurantLocation
409
+ belongs_to :address
410
+ # t.string :name
411
+ end
259
412
 
260
- attribute :name, key: :dish_name
413
+ # where Address is something like
414
+ t.string :line_1
415
+ t.string :line_2
416
+ t.string :city
417
+ t.string :state
418
+ ```
261
419
 
262
- has_one :ratings, :deserializer => RatingsDeserializer
420
+ And you want to update them at the same time, as they're closely tied, `nests` lets you define
263
421
 
264
- def ratings
265
- object
266
- end
422
+ ```ruby
423
+ class ResaturantLocationDeserializer < Deserializer::Base
424
+ attribute :name
267
425
 
268
- end
269
- end
426
+ nests :address, deserializer: AddressDesrializer
270
427
  end
271
- ```
272
428
 
273
- where RatingsDeserializer looks like
429
+ class AddressDeserializer
430
+ attributes :line_1,
431
+ :line_2,
432
+ :city,
433
+ :state
434
+ end
435
+ ```
436
+ And now you can take a single block of json
274
437
 
275
438
  ```ruby
276
- module MyApi
277
- module V1
278
- class RatingsDeserializer < Deserializer::Base
439
+ # Example params into restaurant_location endpoint
440
+ {
441
+ "name" => "Little Caesars: Et Two Brute",
442
+ "line_1" => "2 Brute St.",
443
+ "city" => "Seattle",
444
+ "state" => "WA"
445
+ }
446
+
447
+ # Resulting hash
448
+ {
449
+ name: "Little Caesars: Et Two Brute",
450
+ address: {
451
+ line_1: "2 Brute St",
452
+ city: "Seattle",
453
+ state: "WA"
454
+ }
455
+ }
279
456
 
280
- attributes :taste,
281
- :color,
282
- :texture,
283
- :smell
284
- end
285
- end
286
- end
287
457
  ```
288
458
 
289
- All of this allows your controller to be so very small, like
290
459
 
291
- ```ruby
292
- class DishReviewsController < YourApiController::Base
293
- def create
294
- @review = DishReview.new( MyApi::V1::DishReviewDeserailzer.from_params(params) )
295
460
 
296
- if @review.save
297
- # return review
298
- else
299
- # return sad errors splody
300
- end
301
- end
461
+ ## Functions
462
+ ### from_params
463
+ `MyDeserializer.from_params(params)` creates the JSON that your AR model will then consume.
302
464
 
303
- # RUD
304
- end
465
+ ```ruby
466
+ @review = DishReview.new( MyApi::V1::DishReviewDeserailzer.from_params(params) )
305
467
  ```
306
468
 
307
- ## Installation
469
+ ### permitted_params
470
+ Just call `MyDeserailzer.permitted_params` and you'll have the full array of keys you expect params to have.
308
471
 
472
+ ## Installation
309
473
  Add this line to your application's Gemfile:
310
474
 
311
- gem 'deserializer'
475
+ ```
476
+ gem 'deserializer'
477
+ ```
312
478
 
313
479
  And then execute:
314
480
 
315
- $ bundle
481
+ ```
482
+ $ bundle
483
+ ```
316
484
 
317
485
  Or install it yourself as:
318
486
 
319
- $ gem install deserializer
320
-
487
+ ```
488
+ $ gem install deserializer
489
+ ```
321
490
 
322
491
  ## Contributing
323
-
324
- 1. Fork it ( https://github.com/[my-github-username]/deserializer/fork )
492
+ 1. Fork it ( [https://github.com/[my-github-username]/deserializer/fork](https://github.com/[my-github-username]/deserializer/fork) )
325
493
  2. Create your feature branch (`git checkout -b my-new-feature`)
326
494
  3. Commit your changes (`git commit -am 'Add some feature'`)
327
495
  4. Push to the branch (`git push origin my-new-feature`)
@@ -18,6 +18,8 @@ Gem::Specification.new do |s|
18
18
  s.test_files = s.files.grep(%r{^(test|s|features)/})
19
19
  s.require_paths = ["lib"]
20
20
 
21
+ s.add_dependency "activesupport"
22
+
21
23
  s.add_development_dependency "bundler", "~> 1.6"
22
24
  s.add_development_dependency "rake"
23
25
  end
@@ -1,6 +1,9 @@
1
1
  require "deserializer/version"
2
+ require 'active_support'
3
+ require 'active_support/concern'
2
4
 
3
5
  module Deserializer
6
+ autoload :Associatable, 'deserializer/associatable'
4
7
  autoload :Base, 'deserializer/base'
5
8
  autoload :DeserializerError, 'deserializer/deserializer_error'
6
9
  end
@@ -0,0 +1,38 @@
1
+ module Deserializer
2
+ module Associatable
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ class << self
7
+ def has_one( target, opts = {})
8
+ deserializer = opts[:deserializer]
9
+
10
+ unless deserializer
11
+ raise DeserializerError, class: self, message: "has_one associations need a deserilaizer"
12
+ end
13
+
14
+ self.attrs[target] = { attr: nil, deserializer: deserializer }
15
+ end
16
+
17
+ def has_many(*args)
18
+ raise DeserializerError, class: self, message: "has_many is intentionally unsupported."
19
+ end
20
+
21
+ def belongs_to(*args)
22
+ raise DeserializerError, class: self, message: "belongs_to is unsupported."
23
+ end
24
+
25
+ def nests(target, opts = {})
26
+ deserializer = opts[:deserializer]
27
+
28
+ unless deserializer
29
+ raise DeserializerError, class: self, message: "nested associations need a deserilaizer"
30
+ end
31
+
32
+ self.nested_attrs ||= {}
33
+ self.nested_attrs[target] = { deserializer: deserializer }
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -1,7 +1,11 @@
1
1
  module Deserializer
2
2
  class Base
3
+
4
+ ## has_one, nested, etc associations
5
+ include Deserializer::Associatable
6
+
3
7
  class << self
4
- attr_accessor :attrs
8
+ attr_accessor :attrs, :nested_attrs
5
9
 
6
10
  # deserializer interface functions
7
11
 
@@ -18,24 +22,6 @@ module Deserializer
18
22
  self.attrs[key] = {attr: attr, options: options}
19
23
  end
20
24
 
21
- def has_one( target, opts = {})
22
- deserializer = opts[:deserializer]
23
-
24
- unless deserializer
25
- raise DeserializerError, class: self, message: "has_one associations need a deserilaizer"
26
- end
27
-
28
- self.attrs[target] = {attr: nil, deserializer: deserializer}
29
- end
30
-
31
- def has_many(*args)
32
- raise DeserializerError, class: self, message: "has_many is currently unsupported."
33
- end
34
-
35
- def belongs_to(*args)
36
- raise DeserializerError, class: self, message: "belongs_to is currently unsupported."
37
- end
38
-
39
25
  # deserializer usage functions
40
26
 
41
27
  def from_params( params = {} )
@@ -57,7 +43,7 @@ module Deserializer
57
43
 
58
44
  # this checks if the object_key is a class that inherits from Deserializer
59
45
  if object_key[:deserializer]
60
- deseralize_nested(param_key, object_key[:deserializer])
46
+ deseralize_association(param_key, object_key[:deserializer])
61
47
  else
62
48
  attribute = object_key[:attr]
63
49
  options = object_key[:options]
@@ -65,6 +51,11 @@ module Deserializer
65
51
  assign_value attribute, params[param_key], options
66
52
  end
67
53
  end
54
+
55
+ self.class.nested_attrs ||= {}
56
+ self.class.nested_attrs.each do |target, options|
57
+ deserialize_nested target, options[:deserializer]
58
+ end
68
59
  object
69
60
  end
70
61
 
@@ -73,7 +64,6 @@ module Deserializer
73
64
  attr_accessor :params
74
65
  attr_writer :object
75
66
 
76
-
77
67
  def initialize( object = {}, params = {})
78
68
  unless params
79
69
  raise DeserializerError, class: self.class, message: "params cannot be nil"
@@ -83,7 +73,17 @@ module Deserializer
83
73
  self.object = object
84
74
  end
85
75
 
86
- def deseralize_nested(association, deserializer)
76
+ def deseralize_association(association, deserializer)
77
+ # check for method defining the target object (something, in the example below)
78
+ #
79
+ # class ExampleDeserializer < Deserializer::Base
80
+ # has_one :something, deserializer: SomethingDeserializer
81
+ #
82
+ # def something
83
+ # object
84
+ # end
85
+ # end
86
+
87
87
  if self.respond_to? association
88
88
 
89
89
  target = self.send( association )
@@ -98,6 +98,11 @@ module Deserializer
98
98
  deserializer.new( target, params[association] ).deserialize
99
99
  end
100
100
 
101
+ def deserialize_nested( target, deserializer )
102
+ target = object[target] ||= {}
103
+ deserializer.new( target, params ).deserialize
104
+ end
105
+
101
106
  def assign_value( attribute, value, options = {} )
102
107
  if options[:ignore_empty] && empty?(value)
103
108
  return
@@ -1,3 +1,3 @@
1
1
  module Deserializer
2
- VERSION = "0.3.0"
2
+ VERSION = "0.4.0"
3
3
  end
@@ -110,4 +110,16 @@ class NillableConversionDeserializer < Deserializer::Base
110
110
  range_array = Array(value)
111
111
  (range_array[0]..range_array[-1])
112
112
  end
113
+ end
114
+
115
+ class NestedDeserializer < Deserializer::Base
116
+ attribute :name, key: :attr_1
117
+ attribute :attr_2
118
+ end
119
+
120
+ class NestableDeserializer < Deserializer::Base
121
+ attributes :id,
122
+ :attr_1
123
+
124
+ nests :nested_object, deserializer: ::NestedDeserializer
113
125
  end
@@ -142,4 +142,17 @@ class DeserializerTest < Minitest::Test
142
142
 
143
143
  assert_equal expected, NillableConversionDeserializer.from_params( params )
144
144
  end
145
+
146
+ def test_using_requires_deserializer
147
+ assert_raises Deserializer::DeserializerError do
148
+ BasicDeserializer.nests :splosion
149
+ end
150
+ end
151
+
152
+ def test_supports_using
153
+ params = { id: 1, attr_1: "blah", attr_2: "something" }
154
+ expected = { id: 1, attr_1: "blah", nested_object: { name: "blah", attr_2: "something" } }
155
+
156
+ assert_equal expected, NestableDeserializer.from_params( params )
157
+ end
145
158
  end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: deserializer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Greg Orlov
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-09-21 00:00:00.000000000 Z
11
+ date: 2015-12-03 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: bundler
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -54,6 +68,7 @@ files:
54
68
  - Rakefile
55
69
  - deserializer.gemspec
56
70
  - lib/deserializer.rb
71
+ - lib/deserializer/associatable.rb
57
72
  - lib/deserializer/base.rb
58
73
  - lib/deserializer/deserializer_error.rb
59
74
  - lib/deserializer/version.rb
@@ -90,4 +105,3 @@ test_files:
90
105
  - test/minitest_helper.rb
91
106
  - test/unit/deserializer_error_test.rb
92
107
  - test/unit/deserializer_test.rb
93
- has_rdoc: