surrealist 0.4.0 → 1.0.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 936bf35d7c1f0511db6d2f9458ade02330ec2246d444f9c0c7f9069ed7f24d73
4
- data.tar.gz: 1b91381b04081fb2e4cd6af4167cd41907f982c9a1f713077e76d78ec51a82c4
3
+ metadata.gz: 18d601273dc48757785a2356db0353254bf692deb46ed7df9e128aa329eef928
4
+ data.tar.gz: 20bdde30e93d282483c88beb6de145fb198e332a3cf73379fa0f96d176a99895
5
5
  SHA512:
6
- metadata.gz: d058ddf6a4314ee51c09fc2bd7866302971d9a8f9171430189d95df37871331883237519579c9044a4e720435bdf99702e134e9af992cddb4b5a241db528a92e
7
- data.tar.gz: ccee664c6cadf85ec84e5584a8ac1c9490f43a5dfb7308ecf7d8d883daa5f4bbc6c6f1a2836c58f30677bb1ca6a5cfb1c5111f03db9ca6e0b9fade5420a11998
6
+ metadata.gz: bec9aaebea9985002b24625c3d99316137344794cbab43a2e04d8fa685eda6c46a10f7dc072b37cbfef2428e6a3a0abbb5a7223823feb0ca546fd31128aab7a2
7
+ data.tar.gz: 9c47eb33373325982ab09eeba0aae2dacf963bdf27f0291d79301ea3f5cf1495369d9c7d9f808288cc6dcf596e4dcab409481255909e6dcc266acdf4e43b6c2b
data/.rubocop.yml CHANGED
@@ -2,6 +2,12 @@ AllCops:
2
2
  TargetRubyVersion: 2.2
3
3
  Exclude:
4
4
  - './gemfiles/*gemfile'
5
+ - './tmp/*'
6
+
7
+ Documentation:
8
+ Exclude:
9
+ - benchmarks/*rb
10
+ - spec/**/*rb
5
11
 
6
12
  # Layout
7
13
 
@@ -51,6 +57,7 @@ Lint/RescueType:
51
57
  Metrics/BlockLength:
52
58
  Exclude:
53
59
  - spec/**/*rb
60
+ - benchmarks
54
61
 
55
62
  Metrics/CyclomaticComplexity:
56
63
  Enabled: false
@@ -59,6 +66,7 @@ Metrics/MethodLength:
59
66
  Max: 15
60
67
 
61
68
  Metrics/LineLength:
69
+ Exclude: [benchmarks/*rb]
62
70
  Max: 110
63
71
 
64
72
  Metrics/PerceivedComplexity:
data/CHANGELOG.md CHANGED
@@ -1,3 +1,14 @@
1
+ # 1.0.0
2
+
3
+ ## Added
4
+ * `#build_schema` for collections from `Surrealist::Serializer` (@nesaulov) #74
5
+ * Oj dependency
6
+ * Multiple serializers API (@nulldef) #66
7
+
8
+ ## Miscellaneous
9
+ * Benchmarks for Surrealist vs AMS
10
+ * A lot of memory & performance optimizations (@nesaulov) #64
11
+
1
12
  # 0.4.0
2
13
 
3
14
  ## Added
data/Gemfile CHANGED
@@ -4,7 +4,9 @@ source 'https://rubygems.org'
4
4
  gemspec
5
5
 
6
6
  group :development, :test do
7
+ gem 'active_model_serializers', '~> 0.10.0'
7
8
  gem 'activerecord'
9
+ gem 'benchmark-ips'
8
10
  gem 'coveralls', require: false
9
11
  gem 'dry-struct'
10
12
  gem 'dry-types'
data/README.md CHANGED
@@ -3,6 +3,7 @@
3
3
  [![Coverage Status](https://coveralls.io/repos/github/nesaulov/surrealist/badge.svg?branch=master)](https://coveralls.io/github/nesaulov/surrealist?branch=master)
4
4
  [![Inline docs](http://inch-ci.org/github/nesaulov/surrealist.svg?branch=master)](http://inch-ci.org/github/nesaulov/surrealist)
5
5
  [![Gem Version](https://badge.fury.io/rb/surrealist.svg)](https://rubygems.org/gems/surrealist)
6
+ [![Open Source Helpers](https://www.codetriage.com/nesaulov/surrealist/badges/users.svg)](https://www.codetriage.com/nesaulov/surrealist)
6
7
 
7
8
  ![Surrealist](surrealist-icon.png)
8
9
 
@@ -22,15 +23,21 @@ to serialize nested objects and structures. [Introductory blogpost.](https://med
22
23
  * [Simple example](#simple-example)
23
24
  * [Nested structures](#nested-structures)
24
25
  * [Nested objects](#nested-objects)
25
- * [Delegating Surrealization](#delegating-surrealization)
26
- * [Usage with Dry::Types](#usage-with-drytypes)
26
+ * [Collection Surrealization](#collection-surrealization)
27
27
  * [Defining custom serializers](#defining-custom-serializers)
28
+ * [Multiple serializers](#multiple-serializers)
28
29
  * [Build schema](#build-schema)
29
- * [Camelization](#camelization)
30
- * [Include root](#include-root)
31
- * [Include namespaces](#include-namespaces)
32
- * [Collection Surrealization](#collection-surrealization)
33
- * [Root](#root)
30
+ * [Working with ORMs](#working-with-orms)
31
+ * [ActiveRecord](#activerecord)
32
+ * [ROM](#rom)
33
+ * [Sequel](#sequel)
34
+ * [Usage with Dry::Types](#usage-with-drytypes)
35
+ * [Delegating Surrealization](#delegating-surrealization)
36
+ * [Optional arguments](#optional-arguments)
37
+ * [Camelization](#camelization)
38
+ * [Include root](#include-root)
39
+ * [Root](#root)
40
+ * [Include namespaces](#include-namespaces)
34
41
  * [Bool and Any](#bool-and-any)
35
42
  * [Type errors](#type-errors)
36
43
  * [Undefined methods in schema](#undefined-methods-in-schema)
@@ -38,6 +45,7 @@ to serialize nested objects and structures. [Introductory blogpost.](https://med
38
45
  * [Roadmap](#roadmap)
39
46
  * [Contributing](#contributing)
40
47
  * [Credits](#credits)
48
+ * [Authors](#authors)
41
49
  * [License](#license)
42
50
 
43
51
 
@@ -59,7 +67,7 @@ Or install it yourself as:
59
67
 
60
68
  ## Usage
61
69
  Schema should be defined with a block that contains a hash. Every key of the schema should be
62
- either a name of a method of the surrealizable object (or it's parents/mixins),
70
+ either a name of a method of the surrealizable object (or it's ancestors/mixins),
63
71
  or - in case you want to build json structure independently from object's structure - a symbol.
64
72
  Every value of the hash should be a constant that represents a Ruby class,
65
73
  that will be used for type-checks.
@@ -151,101 +159,33 @@ User.new.surrealize
151
159
 
152
160
  ```
153
161
 
154
- ### Delegating surrealization
155
- You can share the `json_schema` between classes:
162
+ ### Collection Surrealization
163
+ Since 0.2.0 Surrealist has API for collection serialization. Example for ActiveRecord:
156
164
  ``` ruby
157
- class Host
165
+ class User < ActiveRecord::Base
158
166
  include Surrealist
159
167
 
160
168
  json_schema do
161
- { name: String }
162
- end
163
-
164
- def name
165
- 'Host'
169
+ { name: String, age: Integer }
166
170
  end
167
171
  end
168
-
169
- class Guest
170
- delegate_surrealization_to Host
171
172
 
172
- def name
173
- 'Guest'
174
- end
175
- end
176
-
177
- Host.new.surrealize
178
- # => '{ "name": "Host" }'
179
- Guest.new.surrealize
180
- # => '{ "name": "Guest" }'
181
- ```
182
- Schema delegation works without inheritance as well, so if you wish you can
183
- delegate surrealization not only to parent classes, but to any class. Please note that
184
- in this case you have to `include Surrealist` in class that delegates schema as well.
185
- ``` ruby
186
- class Potato
187
- include Surrealist
188
- delegate_surrealization_to Host
173
+ users = User.all
174
+ # => [#<User:0x007fa1485de878 id: 1, name: "Nikita", age: 23>, #<User:0x007fa1485de5f8 id: 2, name: "Alessandro", age: 24>]
189
175
 
190
- def name
191
- 'Potato'
192
- end
193
- end
194
-
195
- Potato.new.surrealize
196
- # => '{ "name": "Potato" }'
176
+ Surrealist.surrealize_collection(users)
177
+ # => '[{ "name": "Nikita", "age": 23 }, { "name": "Alessandro", "age": 24 }]'
197
178
  ```
179
+ You can find motivation behind introducing new API versus monkey-patching [here](https://alessandrominali.github.io/monkey_patching_real_example).
180
+ `#surrealize_collection` works for all data structures that respond to `#each`. All ActiveRecord
181
+ features (like associations, inheritance etc) are supported and covered. Further reading: [working with ORMs](#working-with-orms).
182
+ All optional arguments (`camelize`, `include_root` etc) are also supported.
198
183
 
199
-
200
- ### Usage with Dry::Types
201
- You can use `Dry::Types` for type checking. Note that Surrealist does not ship
202
- with dry-types by default, so you should do the [installation and configuration](http://dry-rb.org/gems/dry-types/)
203
- by yourself. All built-in features of dry-types work, so if you use, say, `Types::Coercible::String`,
204
- your data will be coerced if it is able to, otherwise you will get a TypeError.
205
- Assuming that you have defined module called `Types`:
206
-
207
- ``` ruby
208
- require 'dry-types'
209
-
210
- class Car
211
- include Surrealist
212
-
213
- json_schema do
214
- {
215
- age: Types::Coercible::Int,
216
- brand: Types::Coercible::String,
217
- doors: Types::Int.optional,
218
- horsepower: Types::Strict::Int.constrained(gteq: 20),
219
- fuel_system: Types::Any,
220
- previous_owner: Types::String,
221
- }
222
- end
223
-
224
- def age;
225
- '7'
226
- end
227
-
228
- def previous_owner;
229
- 'John Doe'
230
- end
231
-
232
- def horsepower;
233
- 140
234
- end
235
-
236
- def brand;
237
- 'Toyota'
238
- end
239
-
240
- def doors; end
241
-
242
- def fuel_system;
243
- 'Direct injection'
244
- end
245
- end
246
-
247
- Car.new.surrealize
248
- # => '{ "age": 7, "brand": "Toyota", "doors": null, "horsepower": 140, "fuel_system": "Direct injection", "previous_owner": "John Doe" }'
184
+ An additional and unique argument for `#surrealize_collection` is `raw` which is evaluated as a Boolean.
185
+ If this option is 'truthy' then the results will be an array of surrealized hashes (i.e. NOT a JSON string).
186
+ ```
187
+ Surrealist.surrealize_collection(users, raw: true)
188
+ # => [{ "name": "Nikita", "age": 23 }, { "name": "Alessandro", "age": 24 }]
249
189
  ```
250
190
 
251
191
  ### Defining custom serializers
@@ -314,6 +254,43 @@ IncomeSerializer.new(income, current_user: User.find(3)).surrealize
314
254
  # => '{ "amount": 200 }'
315
255
  ```
316
256
 
257
+ ### Multiple serializers
258
+
259
+ You can define several custom serializers for one object and use it in different cases. Just mark it with a tag:
260
+
261
+ ``` ruby
262
+ class PostSerializer < Surrealist::Serializer
263
+ json_schema { { id: Integer, title: String, author: { name: String } } }
264
+ end
265
+
266
+ class PreviewSerializer < Surrealist::Serializer
267
+ json_schema { { id: Integer, title: String } }
268
+ end
269
+
270
+ class Post
271
+ include Surrealist
272
+
273
+ surrealize_with PostSerializer
274
+ surrealize_with PreviewSerializer, tag: :preview
275
+
276
+ attr_reader :id, :title, :author
277
+ end
278
+ ```
279
+
280
+ And then specify serializer's tag with `for` argument:
281
+ ``` ruby
282
+ author = Struct.new(:name).new("John")
283
+ post = Post.new(1, "Ruby is awesome", author)
284
+ post.surrealize # => '{ "id": 1, "title": "Ruby is awesome", author: { name: "John" } }'
285
+
286
+ post.surrealize(for: :preview) # => '{ "id": 1, "title": "Ruby is awesome" }'
287
+ ```
288
+ Or specify serializer explicitly with `serializer` argument:
289
+
290
+ ``` ruby
291
+ post.surrealize(serializer: PreviewSerializer) # => '{ "id": 1, "title": "Ruby is awesome" }'
292
+ ```
293
+
317
294
  ### Build schema
318
295
  If you don't need to dump the hash to json, you can use `#build_schema`
319
296
  method on the instance. It calculates values and checks types, but returns
@@ -324,7 +301,256 @@ Car.new.build_schema
324
301
  # => { age: 7, brand: "Toyota", doors: nil, horsepower: 140, fuel_system: "Direct injection", previous_owner: "John Doe" }
325
302
  ```
326
303
 
327
- ### Camelization
304
+ ### Working with ORMs
305
+
306
+ There are two kinds of return values of ORM methods: some return collections of objects, while others return instances.
307
+ For the first ones one should use `instance#surrealize`, whereas for the second ones `Surrealist.surrealize_collection(collection)`
308
+ Please keep in mind that if your serialization logic is [kept in a separate class](#defining-custom-serializers) which is inherited from
309
+ `Surrealist::Serializer`, than usage boils down to `YourSerializer.new(instance || collection).surrealize`.
310
+
311
+ #### ActiveRecord
312
+ All associations work as expected: `.has_many`, `.has_and_belongs_to_many` return collections,
313
+ `.has_one`, `.belongs_to` return instances.
314
+
315
+ Methods that return instances:
316
+ ``` ruby
317
+ .find
318
+ .find_by
319
+ .find_by!
320
+ .take!
321
+ .first
322
+ .first!
323
+ .second
324
+ .second!
325
+ .third
326
+ .third!
327
+ .fourth
328
+ .fourth!
329
+ .fifth
330
+ .fifth!
331
+ .forty_two
332
+ .forty_two!
333
+ .last
334
+ .last!
335
+ .third_to_last
336
+ .third_to_last!
337
+ .second_to_last
338
+ .second_to_last!
339
+ ```
340
+ Methods that return collections:
341
+ ``` ruby
342
+ .all
343
+ .where
344
+ .where_not
345
+ .order
346
+ .take
347
+ .limit
348
+ .offset
349
+ .lock
350
+ .readonly
351
+ .reorder
352
+ .distinct
353
+ .find_each
354
+ .select
355
+ .group
356
+ .order
357
+ .except
358
+ .extending
359
+ .having
360
+ .references
361
+ .includes
362
+ .joins
363
+ ```
364
+
365
+ #### ROM
366
+
367
+ For detailed usage example (covering ROM 3.x and ROM 4.x) please see `spec/orms/rom/`.
368
+ Under the hood ROM uses Sequel, and Sequel returns instances only on `.first`, `.last`, `.[]` and `.with_pk!`.
369
+ Collections are returned for all other methods.
370
+ ``` ruby
371
+ container = ROM.container(:sql, ['sqlite::memory']) do |conf|
372
+ conf.default.create_table(:users) do
373
+ primary_key :id
374
+ column :name, String, null: false
375
+ column :email, String, null: false
376
+ end
377
+ # ...
378
+ end
379
+
380
+ users = UserRepo.new(container).users
381
+ # => #<ROM::Relation[Users] name=ROM::Relation::Name(users) dataset=#<Sequel::SQLite::Dataset: "SELECT `users`.`id`, `users`.`name`, `users`.`email` FROM `users` ORDER BY `users`.`id`">>
382
+ ```
383
+ Basically, there are several ways to fetch/represent data in ROM:
384
+ ``` ruby
385
+ # With json_schema defined in ROM::Struct::User
386
+ class ROM::Struct::User < ROM::Struct
387
+ include Surrealist
388
+
389
+ json_schema { { name: String } }
390
+ end
391
+
392
+ users.to_a.first # => #<ROM::Struct::User id=1 name="Jane Struct" email="jane@struct.rom">
393
+ users.to_a.first.surrealize # => "{\"name\":\"Jane Struct\"}"
394
+
395
+ users.where(id: 1).first # => #<ROM::Struct::User id=1 name="Jane Struct" email="jane@struct.rom">
396
+ users.where(id: 1).first.surrealize # => "{\"name\":\"Jane Struct\"}"
397
+
398
+ Surrealist.surrealize_collection(users.to_a) # => "[{\"name\":\"Jane Struct\"},{\"name\":\"Dane As\"},{\"name\":\"Jack Mapper\"}]"
399
+
400
+ # using ROM::Struct::Model#as(Representative) with json_schema defined in representative
401
+ class RomUser < Dry::Struct
402
+ include Surrealist
403
+
404
+ attribute :name, String
405
+ attribute :email, String
406
+
407
+ json_schema { { email: String } }
408
+ end
409
+
410
+ # ROM 3.x
411
+ rom_users = users.as(RomUser).to_a
412
+
413
+ # ROM 4.x
414
+ rom_users = users.map_to(RomUser).to_a
415
+
416
+ rom_users[1].surrealize # => "{\"email\":\"dane@as.rom\"}"
417
+ Surrealist.surrealize_collection(rom_users) # => "[{\"email\":\"jane@struct.rom\"},{\"email\":\"dane@as.rom\"},{\"email\":\"jack@mapper.rom\"}]"
418
+
419
+ # using Mappers
420
+ class UserModel
421
+ include Surrealist
422
+
423
+ json_schema { { id: Integer, email: String } }
424
+
425
+ attr_reader :id, :name, :email
426
+
427
+ def initialize(attributes)
428
+ @id, @name, @email = attributes.values_at(:id, :name, :email)
429
+ end
430
+ end
431
+
432
+ class UsersMapper < ROM::Mapper
433
+ register_as :user_obj
434
+ relation :users
435
+ model UserModel
436
+ end
437
+
438
+ # ROM 3.x
439
+ mapped = users.as(:user_obj)
440
+ # ROM 4.x
441
+ mapped = users.map_with(:user_obj)
442
+
443
+ mapped.to_a[2] # => #<UserModel:0x00007f8ec19fb3c8 @email="jack@mapper.rom", @id=3, @name="Jack Mapper">
444
+ mapped.where(id: 3).first # => #<UserModel:0x00007f8ec19fb3c8 @email="jack@mapper.rom", @id=3, @name="Jack Mapper">
445
+ mapped.to_a[2].surrealize # => "{\"id\":3,\"email\":\"jack@mapper.rom\"}"
446
+ Surrealist.surrealize_collection(mapped.to_a) # => "[{\"email\":\"jane@struct.rom\"},{\"email\":\"dane@as.rom\"},{\"email\":\"jack@mapper.rom\"}]"
447
+ Surrealist.surrealize_collection(mapped.where { id < 4 }.to_a) # => "[{\"email\":\"jane@struct.rom\"},{\"email\":\"dane@as.rom\"},{\"email\":\"jack@mapper.rom\"}]"
448
+ ```
449
+
450
+ #### Sequel
451
+ Basically, Sequel returns instances only on `.first`, `.last`, `.[]` and `.with_pk!`. Collections are returned for all other methods.
452
+ Most of them are covered in `spec/orms/sequel` specs, please refer to them for code examples.
453
+ Associations serialization works the same way as it does with ActiveRecord.
454
+
455
+ ### Usage with Dry::Types
456
+ You can use `Dry::Types` for type checking. Note that Surrealist does not ship
457
+ with dry-types by default, so you should do the [installation and configuration](http://dry-rb.org/gems/dry-types/)
458
+ by yourself. All built-in features of dry-types work, so if you use, say, `Types::Coercible::String`,
459
+ your data will be coerced if it is able to, otherwise you will get a TypeError.
460
+ Assuming that you have defined module called `Types`:
461
+
462
+ ``` ruby
463
+ require 'dry-types'
464
+
465
+ class Car
466
+ include Surrealist
467
+
468
+ json_schema do
469
+ {
470
+ age: Types::Coercible::Int,
471
+ brand: Types::Coercible::String,
472
+ doors: Types::Int.optional,
473
+ horsepower: Types::Strict::Int.constrained(gteq: 20),
474
+ fuel_system: Types::Any,
475
+ previous_owner: Types::String,
476
+ }
477
+ end
478
+
479
+ def age;
480
+ '7'
481
+ end
482
+
483
+ def previous_owner;
484
+ 'John Doe'
485
+ end
486
+
487
+ def horsepower;
488
+ 140
489
+ end
490
+
491
+ def brand;
492
+ 'Toyota'
493
+ end
494
+
495
+ def doors; end
496
+
497
+ def fuel_system;
498
+ 'Direct injection'
499
+ end
500
+ end
501
+
502
+ Car.new.surrealize
503
+ # => '{ "age": 7, "brand": "Toyota", "doors": null, "horsepower": 140, "fuel_system": "Direct injection", "previous_owner": "John Doe" }'
504
+ ```
505
+
506
+ ### Delegating surrealization
507
+ You can share the `json_schema` between classes:
508
+ ``` ruby
509
+ class Host
510
+ include Surrealist
511
+
512
+ json_schema do
513
+ { name: String }
514
+ end
515
+
516
+ def name
517
+ 'Host'
518
+ end
519
+ end
520
+
521
+ class Guest
522
+ delegate_surrealization_to Host
523
+
524
+ def name
525
+ 'Guest'
526
+ end
527
+ end
528
+
529
+ Host.new.surrealize
530
+ # => '{ "name": "Host" }'
531
+ Guest.new.surrealize
532
+ # => '{ "name": "Guest" }'
533
+ ```
534
+ Schema delegation works without inheritance as well, so if you wish you can
535
+ delegate surrealization not only to parent classes, but to any class. Please note that
536
+ in this case you have to `include Surrealist` in class that delegates schema as well.
537
+ ``` ruby
538
+ class Potato
539
+ include Surrealist
540
+ delegate_surrealization_to Host
541
+
542
+ def name
543
+ 'Potato'
544
+ end
545
+ end
546
+
547
+ Potato.new.surrealize
548
+ # => '{ "name": "Potato" }'
549
+ ```
550
+
551
+ ### Optional arguments
552
+
553
+ #### Camelization
328
554
  If you need to have keys in camelBack, you can pass optional `camelize` argument
329
555
  to `#surrealize or #build_schema`. From the previous example:
330
556
 
@@ -333,7 +559,7 @@ Car.new.surrealize(camelize: true)
333
559
  # => '{ "age": 7, "brand": "Toyota", "doors": null, "horsepower": 140, "fuelSystem": "Direct injection", "previousOwner": "John Doe" }'
334
560
  ```
335
561
 
336
- ### Include root
562
+ #### Include root
337
563
  If you want to wrap the resulting JSON into a root key, you can pass optional `include_root` argument
338
564
  to `#surrealize` or `#build_schema`. The root key in this case will be taken from the class name of the
339
565
  surrealizable object.
@@ -373,62 +599,7 @@ Animal::Dog.new.surrealize(include_root: true)
373
599
  # => '{ "dog": { "breed": "Collie" } }'
374
600
  ```
375
601
 
376
- ### Include namespaces
377
- You can build wrap schema into a nested hash from namespaces of the object's class.
378
- ``` ruby
379
- class BusinessSystem::Cashout::ReportSystem::Withdraws
380
- include Surrealist
381
-
382
- json_schema do
383
- { withdraws_amount: Integer }
384
- end
385
-
386
- def withdraws_amount
387
- 34
388
- end
389
- end
390
-
391
- withdraws = BusinessSystem::Cashout::ReportSystem::Withdraws.new
392
-
393
- withdraws.surrealize(include_namespaces: true)
394
- # => '{ "business_system": { "cashout": { "report_system": { "withdraws": { "withdraws_amount": 34 } } } } }'
395
- ```
396
- By default all namespaces will be taken. If you want you can explicitly specify the level of nesting:
397
- ``` ruby
398
- withdraws.surrealize(include_namespaces: true, namespaces_nesting_level: 2)
399
- # => '{ "report_system": { "withdraws": { "withdraws_amount": 34 } } }'
400
- ```
401
-
402
- ### Collection Surrealization
403
- Since 0.2.0 Surrealist has API for collection serialization. Example for ActiveRecord:
404
- ``` ruby
405
- class User < ActiveRecord::Base
406
- include Surrealist
407
-
408
- json_schema do
409
- { name: String, age: Integer }
410
- end
411
- end
412
-
413
- users = User.all
414
- # => [#<User:0x007fa1485de878 id: 1, name: "Nikita", age: 23>, #<User:0x007fa1485de5f8 id: 2, name: "Alessandro", age: 24>]
415
-
416
- Surrealist.surrealize_collection(users)
417
- # => '[{ "name": "Nikita", "age": 23 }, { "name": "Alessandro", "age": 24 }]'
418
- ```
419
- You can find motivation behind introducing new API versus monkey-patching [here](https://alessandrominali.github.io/monkey_patching_real_example).
420
- `#surrealize_collection` works for all data structures that respond to `#each`. All ActiveRecord
421
- features (like associations, inheritance etc) are supported and covered. Other ORMs should work without
422
- issues as well, tests are in progress. All optional arguments (`camelize`, `include_root` etc) are also supported.
423
-
424
- An additional and unique arguement for `#surrealize_collection` is `raw` which is evalauted as a Boolean. If this option is 'truthy' then the results will be an array of surrealized hashes (ie. NOT a JSON string).
425
- ```
426
- Surrealist.surrealize_collection(users, raw: true)
427
- # => [{ "name": "Nikita", "age": 23 }, { "name": "Alessandro", "age": 24 }]
428
- ```
429
- Guides on where to use `#surrealize_collection` vs `#surrealize` for all ORMs are coming.
430
-
431
- ### Root
602
+ #### Root
432
603
  If you want to wrap the resulting JSON into a specified root key, you can pass optional `root` argument
433
604
  to `#surrealize` or `#build_schema`. The `root` argument will be stripped of whitespaces.
434
605
  ``` ruby
@@ -457,6 +628,32 @@ Animal::Cat.new.surrealize(include_namespaces: true, root: 'kitten')
457
628
  # => '{ "kitten": { "weight": "3 kilos" } }'
458
629
  ```
459
630
 
631
+ #### Include namespaces
632
+ You can build wrap schema into a nested hash from namespaces of the object's class.
633
+ ``` ruby
634
+ class BusinessSystem::Cashout::ReportSystem::Withdraws
635
+ include Surrealist
636
+
637
+ json_schema do
638
+ { withdraws_amount: Integer }
639
+ end
640
+
641
+ def withdraws_amount
642
+ 34
643
+ end
644
+ end
645
+
646
+ withdraws = BusinessSystem::Cashout::ReportSystem::Withdraws.new
647
+
648
+ withdraws.surrealize(include_namespaces: true)
649
+ # => '{ "business_system": { "cashout": { "report_system": { "withdraws": { "withdraws_amount": 34 } } } } }'
650
+ ```
651
+ By default all namespaces will be taken. If you want you can explicitly specify the level of nesting:
652
+ ``` ruby
653
+ withdraws.surrealize(include_namespaces: true, namespaces_nesting_level: 2)
654
+ # => '{ "report_system": { "withdraws": { "withdraws_amount": 34 } } }'
655
+ ```
656
+
460
657
  ### Bool and Any
461
658
  If you have a parameter that is of boolean type, or if you don't care about the type, you
462
659
  can use `Bool` and `Any` respectively.
@@ -518,7 +715,9 @@ type check will be passed. If you want to be strict about `nil`s consider using
518
715
 
519
716
  ## Roadmap
520
717
  Here is a list of features that are not implemented yet (contributions are welcome):
521
- * [Benchmarks](https://github.com/nesaulov/surrealist/issues/40)
718
+ * [Having a config that would keep serialization parameters](https://github.com/nesaulov/surrealist/issues/76)
719
+ * [DSL for serializer contexts](https://github.com/nesaulov/surrealist/issues/67)
720
+ * Memoization/caching
522
721
 
523
722
  ## Contributing
524
723
  Bug reports and pull requests are welcome on GitHub at https://github.com/nesaulov/surrealist.
@@ -528,5 +727,12 @@ to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of
528
727
  ## Credits
529
728
  The icon was created by [Simon Child from Noun Project](https://thenounproject.com/term/salvador-dali/124566/) and is published under [Creative Commons License](https://creativecommons.org/licenses/by/3.0/us/)
530
729
 
730
+ ## Authors
731
+ Created by [Nikita Esaulov](https://github.com/nesaulov) with help from [Alessandro Minali](https://github.com/AlessandroMinali) and [Alexey Bespalov](https://github.com/nulldef).
732
+
733
+ <a href="https://github.com/umbrellio/">
734
+ <img style="float: left;" src="https://umbrellio.github.io/Umbrellio/supported_by_umbrellio.svg" alt="Supported by Umbrellio" width="439" height="72">
735
+ </a>
736
+
531
737
  ## License
532
738
  The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).