surrealist 1.0.0 → 1.1.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
  SHA256:
3
- metadata.gz: 18d601273dc48757785a2356db0353254bf692deb46ed7df9e128aa329eef928
4
- data.tar.gz: 20bdde30e93d282483c88beb6de145fb198e332a3cf73379fa0f96d176a99895
3
+ metadata.gz: 2b3537308a609571f4a82a0f35792244135fd0fe249b85b408d444dc46c4ecf4
4
+ data.tar.gz: 8c50d83dbbe452bb55f70eadccc69ad024a6055e2ee1e60aa29f768c3281bfbc
5
5
  SHA512:
6
- metadata.gz: bec9aaebea9985002b24625c3d99316137344794cbab43a2e04d8fa685eda6c46a10f7dc072b37cbfef2428e6a3a0abbb5a7223823feb0ca546fd31128aab7a2
7
- data.tar.gz: 9c47eb33373325982ab09eeba0aae2dacf963bdf27f0291d79301ea3f5cf1495369d9c7d9f808288cc6dcf596e4dcab409481255909e6dcc266acdf4e43b6c2b
6
+ metadata.gz: 3b5a167801a3393667d0fae41727f3512be479bfcdfb26426615a422173ff417b186ea59d067550db2f2f44af49a789450c5056bad07de500849e8b21ae46e0e
7
+ data.tar.gz: 9c3923e37180abfaa4159dbc5f55b393f6ab88927803bf639ffa1825cbf5aa16e8a61199a7dc19e65f8586e26666b6208f4e995826d6a8ecaa03b9868ce01f57
data/.rubocop.yml CHANGED
@@ -33,9 +33,6 @@ Layout/MultilineOperationIndentation:
33
33
  Layout/SpaceInLambdaLiteral:
34
34
  EnforcedStyle: require_space
35
35
 
36
- Layout/SpaceInsideBrackets:
37
- Enabled: false
38
-
39
36
  # Lint
40
37
  Lint/AmbiguousBlockAssociation:
41
38
  Enabled: false
@@ -97,6 +94,10 @@ Performance/UnfreezeString:
97
94
 
98
95
  # Style
99
96
 
97
+ # ¯\_(ツ)_/¯
98
+ Style/AsciiComments:
99
+ Enabled: false
100
+
100
101
  Style/MixinUsage:
101
102
  Exclude:
102
103
  - spec/**/*rb
@@ -152,3 +153,7 @@ Style/TrailingCommaInLiteral:
152
153
 
153
154
  Style/MethodMissing:
154
155
  Enabled: false
156
+
157
+ Style/EvalWithLocation:
158
+ Exclude:
159
+ - spec/**/*.rb
data/.travis.yml CHANGED
@@ -10,15 +10,10 @@ matrix:
10
10
  gemfile: Gemfile
11
11
  - rvm: 2.5.0
12
12
  gemfile: Gemfile
13
- - rvm: 2.4.2
13
+ - rvm: 2.4.3
14
14
  gemfile: Gemfile
15
- - rvm: 2.4.0
15
+ - rvm: 2.3.6
16
16
  gemfile: Gemfile
17
- - rvm: 2.3.5
18
- gemfile: Gemfile
19
- - rvm: 2.3.1
20
- gemfile: Gemfile
21
- - rvm: 2.2.5
22
- gemfile: gemfiles/activerecord42.gemfile
23
- - rvm: 2.2.0
17
+ - rvm: 2.2.9
24
18
  gemfile: gemfiles/activerecord42.gemfile
19
+
data/CHANGELOG.md CHANGED
@@ -1,36 +1,48 @@
1
+ # 1.1.0
2
+
3
+ ## Added
4
+ * Configuration of default serialization params ([@nesaulov][]) [#77](https://github.com/nesaulov/surrealist/pull/77)
5
+ * DSL for custom serializers context ([@nesaulov][]) [#80](https://github.com/nesaulov/surrealist/pull/80)
6
+
7
+ ## Fixed
8
+ * Fix failing serialization with sequel & custom serializers ([@azhi][]) [#84](https://github.com/nesaulov/surrealist/pull/84)
9
+
10
+ ## Miscellaneous
11
+ * Pin Oj version to 3.4.0 [#79](https://github.com/nesaulov/surrealist/pull/79)
12
+
1
13
  # 1.0.0
2
14
 
3
15
  ## Added
4
- * `#build_schema` for collections from `Surrealist::Serializer` (@nesaulov) #74
16
+ * `#build_schema` for collections from `Surrealist::Serializer` ([@nesaulov][]) [#74](https://github.com/nesaulov/surrealist/pull/74)
5
17
  * Oj dependency
6
- * Multiple serializers API (@nulldef) #66
18
+ * Multiple serializers API ([@nulldef][]) [#66](https://github.com/nesaulov/surrealist/pull/66)
7
19
 
8
20
  ## Miscellaneous
9
21
  * Benchmarks for Surrealist vs AMS
10
- * A lot of memory & performance optimizations (@nesaulov) #64
22
+ * A lot of memory & performance optimizations ([@nesaulov][]) [#64](https://github.com/nesaulov/surrealist/pull/64)
11
23
 
12
24
  # 0.4.0
13
25
 
14
26
  ## Added
15
- * Introduce an abstract serializer class (@nesaulov) #61
16
- * Full integration for Sequel (@nesaulov) #47
17
- * Integration for ROM 4.x (@nesaulov) #56
18
- * Ruby 2.5 support (@nesaulov) #57
27
+ * Introduce an abstract serializer class ([@nesaulov][]) [#61](https://github.com/nesaulov/surrealist/pull/61)
28
+ * Full integration for Sequel ([@nesaulov][]) [#47](https://github.com/nesaulov/surrealist/pull/47)
29
+ * Integration for ROM 4.x ([@nesaulov][]) [#56](https://github.com/nesaulov/surrealist/pull/56)
30
+ * Ruby 2.5 support ([@nesaulov][]) [#57](https://github.com/nesaulov/surrealist/pull/57)
19
31
 
20
32
  ## Miscellaneous
21
- * Memory & performance optimizations (@nesaulov) #51
22
- * Refactorings (@nulldef) #55
33
+ * Memory & performance optimizations ([@nesaulov][]) [#51](https://github.com/nesaulov/surrealist/pull/51)
34
+ * Refactorings ([@nulldef][]) [#55](https://github.com/nesaulov/surrealist/pull/55)
23
35
 
24
36
  # 0.3.0
25
37
 
26
38
  ## Added
27
- * Full integration for ActiveRecord (@nesaulov, @AlessandroMinali) #37
28
- * Full integration for ROM <= 3 (@nesaulov, @AlessandroMinali) #37
29
- * `root` optional argument (@chrisatanasian) #32
30
- * Nested records surrealization (@AlessandroMinali) #34
39
+ * Full integration for ActiveRecord ([@nesaulov][], [@AlessandroMinali][]) [#37](https://github.com/nesaulov/surrealist/pull/37)
40
+ * Full integration for ROM <= 3 ([@nesaulov][], [@AlessandroMinali][]) [#37](https://github.com/nesaulov/surrealist/pull/37)
41
+ * `root` optional argument ([@chrisatanasian][]) [#32](https://github.com/nesaulov/surrealist/pull/32)
42
+ * Nested records surrealization ([@AlessandroMinali][]) [#34](https://github.com/nesaulov/surrealist/pull/34)
31
43
 
32
44
  ## Fixed
33
- * Dependencies update (@nesaulov) #48
45
+ * Dependencies update ([@nesaulov][]) [#48](https://github.com/nesaulov/surrealist/pull/48)
34
46
 
35
47
  # 0.2.0
36
48
  ## Added
@@ -65,5 +77,10 @@
65
77
  * Allow nil values by default.
66
78
  * Allow nested objects.
67
79
 
80
+ [@nesaulov]: https://github.com/nesaulov
81
+ [@AlessandroMinali]: https://github.com/AlessandroMinali
82
+ [@nulldef]: https://github.com/nulldef
83
+ [@azhi]: https://github.com/azhi
84
+ [@chrisatanasian]: https://github.com/chrisatanasian
68
85
 
69
86
 
data/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
  [![Open Source Helpers](https://www.codetriage.com/nesaulov/surrealist/badges/users.svg)](https://www.codetriage.com/nesaulov/surrealist)
7
7
 
8
8
  ![Surrealist](surrealist-icon.png)
9
-
9
+
10
10
  A gem that provides DSL for serialization of plain old Ruby objects to JSON in a declarative style
11
11
  by defining a `json_schema`. It also provides a trivial type checking in the runtime before serialization.
12
12
  [Yard documentation](http://www.rubydoc.info/github/nesaulov/surrealist/master)
@@ -31,18 +31,19 @@ to serialize nested objects and structures. [Introductory blogpost.](https://med
31
31
  * [ActiveRecord](#activerecord)
32
32
  * [ROM](#rom)
33
33
  * [Sequel](#sequel)
34
- * [Usage with Dry::Types](#usage-with-drytypes)
34
+ * [Usage with Dry::Types](#usage-with-drytypes)
35
35
  * [Delegating Surrealization](#delegating-surrealization)
36
36
  * [Optional arguments](#optional-arguments)
37
37
  * [Camelization](#camelization)
38
38
  * [Include root](#include-root)
39
39
  * [Root](#root)
40
40
  * [Include namespaces](#include-namespaces)
41
+ * [Configuration](#configuration)
41
42
  * [Bool and Any](#bool-and-any)
42
43
  * [Type errors](#type-errors)
43
44
  * [Undefined methods in schema](#undefined-methods-in-schema)
44
45
  * [Other notes](#other-notes)
45
- * [Roadmap](#roadmap)
46
+ * [Roadmap](#roadmap)
46
47
  * [Contributing](#contributing)
47
48
  * [Credits](#credits)
48
49
  * [Authors](#authors)
@@ -79,15 +80,15 @@ that will be used for type-checks.
79
80
  ``` ruby
80
81
  class Person
81
82
  include Surrealist
82
-
83
+
83
84
  json_schema do
84
85
  { name: String, age: Integer }
85
86
  end
86
-
87
+
87
88
  def name
88
89
  'John Doe'
89
90
  end
90
-
91
+
91
92
  def age
92
93
  42
93
94
  end
@@ -106,7 +107,7 @@ Person.new.surrealize
106
107
  ``` ruby
107
108
  class Person
108
109
  include Surrealist
109
-
110
+
110
111
  json_schema do
111
112
  {
112
113
  foo: String,
@@ -121,7 +122,7 @@ class Person
121
122
  end
122
123
  # ... method definitions
123
124
  end
124
-
125
+
125
126
  Person.find_by(email: 'example@email.com').surrealize
126
127
  # => '{ "foo": "Some string", "name": "John Doe", "nested": { "at": { "any": 42, "level": true } } }'
127
128
  ```
@@ -133,7 +134,7 @@ define a method that calls nested object:
133
134
  ``` ruby
134
135
  class User
135
136
  include Surrealist
136
-
137
+
137
138
  json_schema do
138
139
  {
139
140
  name: String,
@@ -143,11 +144,11 @@ class User
143
144
  },
144
145
  }
145
146
  end
146
-
147
+
147
148
  def name
148
149
  'John Doe'
149
150
  end
150
-
151
+
151
152
  def credit_card
152
153
  # Assuming that instance of a CreditCard has methods #number and #cvv defined
153
154
  CreditCard.find_by(holder: name)
@@ -164,20 +165,20 @@ Since 0.2.0 Surrealist has API for collection serialization. Example for ActiveR
164
165
  ``` ruby
165
166
  class User < ActiveRecord::Base
166
167
  include Surrealist
167
-
168
+
168
169
  json_schema do
169
170
  { name: String, age: Integer }
170
171
  end
171
172
  end
172
-
173
+
173
174
  users = User.all
174
175
  # => [#<User:0x007fa1485de878 id: 1, name: "Nikita", age: 23>, #<User:0x007fa1485de5f8 id: 2, name: "Alessandro", age: 24>]
175
-
176
+
176
177
  Surrealist.surrealize_collection(users)
177
178
  # => '[{ "name": "Nikita", "age": 23 }, { "name": "Alessandro", "age": 24 }]'
178
179
  ```
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
180
+ You can find motivation behind introducing new API versus monkey-patching [here](https://alessandrominali.github.io/monkey_patching_real_example).
181
+ `#surrealize_collection` works for all data structures that behave like `Enumerable`. All ActiveRecord
181
182
  features (like associations, inheritance etc) are supported and covered. Further reading: [working with ORMs](#working-with-orms).
182
183
  All optional arguments (`camelize`, `include_root` etc) are also supported.
183
184
 
@@ -195,65 +196,84 @@ will inherit from `Surrealist::Serializer`. To point to that class from the mode
195
196
  ``` ruby
196
197
  class CatSerializer < Surrealist::Serializer
197
198
  json_schema { { age: Integer, age_group: String } }
198
-
199
+
199
200
  def age_group
200
201
  age <= 5 ? 'kitten' : 'cat'
201
202
  end
202
203
  end
203
-
204
+
204
205
  class Cat
205
206
  include Surrealist
206
207
  attr_reader :age
207
-
208
+
208
209
  surrealize_with CatSerializer
209
-
210
+
210
211
  def initialize(age)
211
212
  @age = age
212
213
  end
213
214
  end
214
-
215
+
215
216
  Cat.new(12).surrealize # Implicit usage through .surrealize_with
216
217
  # => '{ "age": 12, "age_group": "cat" }'
217
-
218
+
218
219
  CatSerializer.new(Cat.new(3)).surrealize # explicit usage of CatSerializer
219
220
  # => '{ "age": 3, "age_group": "kitten" }'
220
221
  ```
221
- The constructor of `Surrealist::Serializer` takes two arguments: serializable model (or collection) and
222
+ The constructor of `Surrealist::Serializer` takes two arguments: serializable model (or collection) and
222
223
  a context hash. So if there is an object that is not coupled to serializable model
223
- but it is still necessary for constructing JSON, you can pass it to constructor as a hash. It will
224
- be available in the serializer in the `context` hash.
224
+ but it is still necessary for constructing JSON, you can pass it to constructor as a hash. It will
225
+ be available in the serializer in the `context` hash.
225
226
  ``` ruby
226
227
  class IncomeSerializer < Surrealist::Serializer
227
228
  json_schema { { amount: Integer } }
228
-
229
+
229
230
  def amount
230
231
  current_user.guest? ? 100000000 : object.amount
231
232
  end
232
-
233
+
233
234
  def current_user
234
235
  context[:current_user]
235
236
  end
236
237
  end
237
-
238
+
238
239
  class Income
239
240
  include Surrealist
240
241
  surrealize_with IncomeSerializer
241
-
242
+
242
243
  attr_reader :amount
243
-
244
+
244
245
  def initialize(amount)
245
246
  @amount = amount
246
- end
247
+ end
247
248
  end
248
-
249
+
249
250
  income = Income.new(200)
250
251
  IncomeSerializer.new(income, current_user: GuestUser.new).surrealize
251
252
  # => '{ "amount": 100000000 }'
252
-
253
+
253
254
  IncomeSerializer.new(income, current_user: User.find(3)).surrealize
254
255
  # => '{ "amount": 200 }'
255
256
  ```
256
-
257
+ If you happen to pass a context to a serializer, there is a handy DSL to reduce the number of methods
258
+ you have to define yourself. DSL looks as follows
259
+ ``` ruby
260
+ class IncomeSerializer < Surrealist::Serializer
261
+ serializer_context :current_user
262
+ json_schema { { amount: Integer } }
263
+
264
+ def amount
265
+ current_user.guest? ? 100000000 : object.amount
266
+ end
267
+ end
268
+ ```
269
+ `.serializer_context` takes an array of symbols and dynamically defines instance methods
270
+ that read values from the context hash. So `.serializer_context :current_user` will become
271
+ ``` ruby
272
+ def current_user
273
+ context[:current_user]
274
+ end
275
+ ```
276
+ There is also an alias in the plural form: `.serializer_contexts`.
257
277
  ### Multiple serializers
258
278
 
259
279
  You can define several custom serializers for one object and use it in different cases. Just mark it with a tag:
@@ -269,10 +289,10 @@ end
269
289
 
270
290
  class Post
271
291
  include Surrealist
272
-
292
+
273
293
  surrealize_with PostSerializer
274
294
  surrealize_with PreviewSerializer, tag: :preview
275
-
295
+
276
296
  attr_reader :id, :title, :author
277
297
  end
278
298
  ```
@@ -282,7 +302,7 @@ And then specify serializer's tag with `for` argument:
282
302
  author = Struct.new(:name).new("John")
283
303
  post = Post.new(1, "Ruby is awesome", author)
284
304
  post.surrealize # => '{ "id": 1, "title": "Ruby is awesome", author: { name: "John" } }'
285
-
305
+
286
306
  post.surrealize(for: :preview) # => '{ "id": 1, "title": "Ruby is awesome" }'
287
307
  ```
288
308
  Or specify serializer explicitly with `serializer` argument:
@@ -318,45 +338,45 @@ Methods that return instances:
318
338
  .find_by
319
339
  .find_by!
320
340
  .take!
321
- .first
341
+ .first
322
342
  .first!
323
- .second
343
+ .second
324
344
  .second!
325
- .third
345
+ .third
326
346
  .third!
327
- .fourth
347
+ .fourth
328
348
  .fourth!
329
- .fifth
349
+ .fifth
330
350
  .fifth!
331
- .forty_two
351
+ .forty_two
332
352
  .forty_two!
333
- .last
353
+ .last
334
354
  .last!
335
- .third_to_last
355
+ .third_to_last
336
356
  .third_to_last!
337
- .second_to_last
357
+ .second_to_last
338
358
  .second_to_last!
339
359
  ```
340
360
  Methods that return collections:
341
361
  ``` ruby
342
362
  .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
363
+ .where
364
+ .where_not
365
+ .order
366
+ .take
367
+ .limit
368
+ .offset
369
+ .lock
370
+ .readonly
371
+ .reorder
372
+ .distinct
373
+ .find_each
374
+ .select
375
+ .group
376
+ .order
377
+ .except
378
+ .extending
379
+ .having
360
380
  .references
361
381
  .includes
362
382
  .joins
@@ -376,7 +396,7 @@ container = ROM.container(:sql, ['sqlite::memory']) do |conf|
376
396
  end
377
397
  # ...
378
398
  end
379
-
399
+
380
400
  users = UserRepo.new(container).users
381
401
  # => #<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
402
  ```
@@ -385,67 +405,67 @@ Basically, there are several ways to fetch/represent data in ROM:
385
405
  # With json_schema defined in ROM::Struct::User
386
406
  class ROM::Struct::User < ROM::Struct
387
407
  include Surrealist
388
-
408
+
389
409
  json_schema { { name: String } }
390
- end
391
-
410
+ end
411
+
392
412
  users.to_a.first # => #<ROM::Struct::User id=1 name="Jane Struct" email="jane@struct.rom">
393
413
  users.to_a.first.surrealize # => "{\"name\":\"Jane Struct\"}"
394
-
414
+
395
415
  users.where(id: 1).first # => #<ROM::Struct::User id=1 name="Jane Struct" email="jane@struct.rom">
396
416
  users.where(id: 1).first.surrealize # => "{\"name\":\"Jane Struct\"}"
397
-
417
+
398
418
  Surrealist.surrealize_collection(users.to_a) # => "[{\"name\":\"Jane Struct\"},{\"name\":\"Dane As\"},{\"name\":\"Jack Mapper\"}]"
399
-
419
+
400
420
  # using ROM::Struct::Model#as(Representative) with json_schema defined in representative
401
421
  class RomUser < Dry::Struct
402
422
  include Surrealist
403
-
423
+
404
424
  attribute :name, String
405
425
  attribute :email, String
406
-
426
+
407
427
  json_schema { { email: String } }
408
428
  end
409
-
429
+
410
430
  # ROM 3.x
411
431
  rom_users = users.as(RomUser).to_a
412
-
432
+
413
433
  # ROM 4.x
414
434
  rom_users = users.map_to(RomUser).to_a
415
-
435
+
416
436
  rom_users[1].surrealize # => "{\"email\":\"dane@as.rom\"}"
417
437
  Surrealist.surrealize_collection(rom_users) # => "[{\"email\":\"jane@struct.rom\"},{\"email\":\"dane@as.rom\"},{\"email\":\"jack@mapper.rom\"}]"
418
-
438
+
419
439
  # using Mappers
420
440
  class UserModel
421
441
  include Surrealist
422
-
442
+
423
443
  json_schema { { id: Integer, email: String } }
424
-
444
+
425
445
  attr_reader :id, :name, :email
426
-
446
+
427
447
  def initialize(attributes)
428
448
  @id, @name, @email = attributes.values_at(:id, :name, :email)
429
449
  end
430
450
  end
431
-
451
+
432
452
  class UsersMapper < ROM::Mapper
433
453
  register_as :user_obj
434
454
  relation :users
435
455
  model UserModel
436
456
  end
437
-
457
+
438
458
  # ROM 3.x
439
459
  mapped = users.as(:user_obj)
440
460
  # ROM 4.x
441
461
  mapped = users.map_with(:user_obj)
442
-
462
+
443
463
  mapped.to_a[2] # => #<UserModel:0x00007f8ec19fb3c8 @email="jack@mapper.rom", @id=3, @name="Jack Mapper">
444
464
  mapped.where(id: 3).first # => #<UserModel:0x00007f8ec19fb3c8 @email="jack@mapper.rom", @id=3, @name="Jack Mapper">
445
465
  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
- ```
466
+ Surrealist.surrealize_collection(mapped.to_a) # => "[{\"email\":\"jane@struct.rom\"},{\"email\":\"dane@as.rom\"},{\"email\":\"jack@mapper.rom\"}]"
467
+ Surrealist.surrealize_collection(mapped.where { id < 4 }.to_a) # => "[{\"email\":\"jane@struct.rom\"},{\"email\":\"dane@as.rom\"},{\"email\":\"jack@mapper.rom\"}]"
468
+ ```
449
469
 
450
470
  #### Sequel
451
471
  Basically, Sequel returns instances only on `.first`, `.last`, `.[]` and `.with_pk!`. Collections are returned for all other methods.
@@ -464,7 +484,7 @@ require 'dry-types'
464
484
 
465
485
  class Car
466
486
  include Surrealist
467
-
487
+
468
488
  json_schema do
469
489
  {
470
490
  age: Types::Coercible::Int,
@@ -475,25 +495,25 @@ class Car
475
495
  previous_owner: Types::String,
476
496
  }
477
497
  end
478
-
498
+
479
499
  def age;
480
500
  '7'
481
501
  end
482
-
502
+
483
503
  def previous_owner;
484
504
  'John Doe'
485
505
  end
486
-
506
+
487
507
  def horsepower;
488
508
  140
489
509
  end
490
-
510
+
491
511
  def brand;
492
512
  'Toyota'
493
513
  end
494
-
514
+
495
515
  def doors; end
496
-
516
+
497
517
  def fuel_system;
498
518
  'Direct injection'
499
519
  end
@@ -508,11 +528,11 @@ You can share the `json_schema` between classes:
508
528
  ``` ruby
509
529
  class Host
510
530
  include Surrealist
511
-
531
+
512
532
  json_schema do
513
533
  { name: String }
514
534
  end
515
-
535
+
516
536
  def name
517
537
  'Host'
518
538
  end
@@ -520,7 +540,7 @@ end
520
540
 
521
541
  class Guest
522
542
  delegate_surrealization_to Host
523
-
543
+
524
544
  def name
525
545
  'Guest'
526
546
  end
@@ -538,7 +558,7 @@ in this case you have to `include Surrealist` in class that delegates schema as
538
558
  class Potato
539
559
  include Surrealist
540
560
  delegate_surrealization_to Host
541
-
561
+
542
562
  def name
543
563
  'Potato'
544
564
  end
@@ -566,16 +586,16 @@ surrealizable object.
566
586
  ``` ruby
567
587
  class Cat
568
588
  include Surrealist
569
-
589
+
570
590
  json_schema do
571
591
  { weight: String }
572
592
  end
573
-
593
+
574
594
  def weight
575
595
  '3 kilos'
576
596
  end
577
597
  end
578
-
598
+
579
599
  Cat.new.surrealize(include_root: true)
580
600
  # => '{ "cat": { "weight": "3 kilos" } }'
581
601
  ```
@@ -584,11 +604,11 @@ With nested classes the last namespace will be taken as root key:
584
604
  class Animal
585
605
  class Dog
586
606
  include Surrealist
587
-
607
+
588
608
  json_schema do
589
609
  { breed: String }
590
610
  end
591
-
611
+
592
612
  def breed
593
613
  'Collie'
594
614
  end
@@ -605,11 +625,11 @@ to `#surrealize` or `#build_schema`. The `root` argument will be stripped of whi
605
625
  ``` ruby
606
626
  class Cat
607
627
  include Surrealist
608
-
628
+
609
629
  json_schema do
610
630
  { weight: String }
611
631
  end
612
-
632
+
613
633
  def weight
614
634
  '3 kilos'
615
635
  end
@@ -633,20 +653,20 @@ You can build wrap schema into a nested hash from namespaces of the object's cla
633
653
  ``` ruby
634
654
  class BusinessSystem::Cashout::ReportSystem::Withdraws
635
655
  include Surrealist
636
-
656
+
637
657
  json_schema do
638
658
  { withdraws_amount: Integer }
639
659
  end
640
-
660
+
641
661
  def withdraws_amount
642
662
  34
643
663
  end
644
664
  end
645
-
665
+
646
666
  withdraws = BusinessSystem::Cashout::ReportSystem::Withdraws.new
647
-
667
+
648
668
  withdraws.surrealize(include_namespaces: true)
649
- # => '{ "business_system": { "cashout": { "report_system": { "withdraws": { "withdraws_amount": 34 } } } } }'
669
+ # => '{ "business_system": { "cashout": { "report_system": { "withdraws": { "withdraws_amount": 34 } } } } }'
650
670
  ```
651
671
  By default all namespaces will be taken. If you want you can explicitly specify the level of nesting:
652
672
  ``` ruby
@@ -654,6 +674,31 @@ withdraws.surrealize(include_namespaces: true, namespaces_nesting_level: 2)
654
674
  # => '{ "report_system": { "withdraws": { "withdraws_amount": 34 } } }'
655
675
  ```
656
676
 
677
+ ### Configuration
678
+
679
+ There are two ways of setting default arguments for serialization,
680
+ by passing a block to `Surrealist.configure`:
681
+ ```ruby
682
+ Surrealist.configure do |config|
683
+ config.camelize = true
684
+ config.namespaces_nesting_level = 2
685
+ end
686
+ ```
687
+ And by passing a hash:
688
+
689
+ `Surrealist.configure(camelize: true, include_root: true)`
690
+
691
+ These arguments will be applied to all calls of `#build_schema` and `#surrealize`.
692
+ If these methods will be called with arguments, they will be merged with respect to explicitly passed ones:
693
+
694
+ ```ruby
695
+ Surrealist.configure(camelize: true, include_root: true)
696
+
697
+ Something.new.surrealize(camelize: false)
698
+ # will result in Something.new.surrealize(camelize: false, include_root: true)
699
+ ```
700
+
701
+
657
702
  ### Bool and Any
658
703
  If you have a parameter that is of boolean type, or if you don't care about the type, you
659
704
  can use `Bool` and `Any` respectively.
@@ -661,7 +706,7 @@ can use `Bool` and `Any` respectively.
661
706
  ``` ruby
662
707
  class User
663
708
  include Surrealist
664
-
709
+
665
710
  json_schema do
666
711
  {
667
712
  age: Any,
@@ -677,13 +722,13 @@ end
677
722
  ``` ruby
678
723
  class CreditCard
679
724
  include Surrealist
680
-
725
+
681
726
  json_schema do
682
727
  { number: Integer }
683
728
  end
684
-
729
+
685
730
  def number
686
- 'string'
731
+ 'string'
687
732
  end
688
733
  end
689
734
 
@@ -698,7 +743,7 @@ a corresponding method defined in the object.
698
743
  ``` ruby
699
744
  class Car
700
745
  include Surrealist
701
-
746
+
702
747
  json_schema do
703
748
  { weight: Integer }
704
749
  end
@@ -716,7 +761,7 @@ type check will be passed. If you want to be strict about `nil`s consider using
716
761
  ## Roadmap
717
762
  Here is a list of features that are not implemented yet (contributions are welcome):
718
763
  * [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)
764
+ * [DSL for serializer contexts](https://github.com/nesaulov/surrealist/issues/67)
720
765
  * Memoization/caching
721
766
 
722
767
  ## Contributing
@@ -2,10 +2,11 @@
2
2
 
3
3
  module Surrealist
4
4
  # A data structure to carry arguments across methods.
5
+ # @api private
5
6
  class Carrier
6
7
  BOOLEANS = [true, false].freeze
7
8
 
8
- attr_reader :camelize, :include_root, :include_namespaces, :root, :namespaces_nesting_level
9
+ attr_accessor :camelize, :include_root, :include_namespaces, :root, :namespaces_nesting_level
9
10
 
10
11
  # Public wrapper for Carrier.
11
12
  #
@@ -43,10 +44,19 @@ module Surrealist
43
44
  self
44
45
  end
45
46
 
47
+ # Checks if all arguments are set to default
46
48
  def no_args_provided?
47
49
  @no_args ||= no_args
48
50
  end
49
51
 
52
+ # Returns all arguments
53
+ #
54
+ # @return [Hash]
55
+ def parameters
56
+ { camelize: camelize, include_root: include_root, include_namespaces: include_namespaces,
57
+ root: root, namespaces_nesting_level: namespaces_nesting_level }
58
+ end
59
+
50
60
  private
51
61
 
52
62
  # Checks all boolean arguments
@@ -85,6 +95,7 @@ module Surrealist
85
95
  root.is_a?(String) && @root = root.strip
86
96
  end
87
97
 
98
+ # Checks if all arguments are set to default
88
99
  def no_args
89
100
  !camelize && !include_root && !include_namespaces && root.nil? &&
90
101
  namespaces_nesting_level == DEFAULT_NESTING_LEVEL
@@ -31,7 +31,7 @@ module Surrealist
31
31
  # A class that raises all Surrealist exceptions
32
32
  module ExceptionRaiser
33
33
  CLASS_NAME_NOT_PASSED = "Can't wrap schema in root key - class name was not passed".freeze
34
- MUST_RESPOND_TO_EACH = "Can't serialize collection - must respond to :each".freeze
34
+ MUST_BEHAVE_LIKE_ENUMERABLE = "Can't serialize collection - must behave like enumerable".freeze
35
35
  CLASS_DOESNT_INCLUDE_SURREALIST = 'Class does not include Surrealist'.freeze
36
36
 
37
37
  class << self
@@ -64,7 +64,7 @@ module Surrealist
64
64
  #
65
65
  # @raise Surrealist::InvalidCollectionError
66
66
  def raise_invalid_collection!
67
- raise Surrealist::InvalidCollectionError, MUST_RESPOND_TO_EACH
67
+ raise Surrealist::InvalidCollectionError, MUST_BEHAVE_LIKE_ENUMERABLE
68
68
  end
69
69
 
70
70
  # Raises ArgumentError if namespaces_nesting_level is not an integer.
@@ -11,5 +11,17 @@ module Surrealist
11
11
  def self.surrealist?(klass)
12
12
  klass < Surrealist || klass < Surrealist::Serializer
13
13
  end
14
+
15
+ def self.collection?(object)
16
+ # 4.2 AR relation object did not include Enumerable (it defined
17
+ # all necessary method through ActiveRecord::Delegation module),
18
+ # so we need to explicitly check for this
19
+ object.is_a?(Enumerable) || ar_relation?(object)
20
+ end
21
+
22
+ def self.ar_relation?(object)
23
+ defined?(ActiveRecord) && object.is_a?(ActiveRecord::Relation)
24
+ end
25
+ private_class_method :ar_relation?
14
26
  end
15
27
  end
@@ -31,6 +31,25 @@ module Surrealist
31
31
  class Serializer
32
32
  extend Surrealist::ClassMethods
33
33
 
34
+ class << self
35
+ # Defines instance methods that read values from the context hash.
36
+ #
37
+ # @param [Array<Symbol>] array
38
+ # an array of symbols which represent method names
39
+ #
40
+ # @raise ArgumentError if type of argument is not an array of symbols
41
+ def serializer_context(*array)
42
+ unless array.all? { |i| i.is_a? Symbol }
43
+ raise ArgumentError, 'Please provide an array of symbols to `.serializer_context`'
44
+ end
45
+
46
+ array.each { |method| define_method(method) { context[method] } }
47
+ end
48
+
49
+ # Plural form ¯\_(ツ)_/¯
50
+ alias serializer_contexts serializer_context
51
+ end
52
+
34
53
  # NOTE: #context will work only when using serializer explicitly,
35
54
  # e.g `CatSerializer.new(Cat.new(3), food: CatFood.new)`
36
55
  # And then food will be available inside serializer via `context[:food]`
@@ -41,7 +60,7 @@ module Surrealist
41
60
 
42
61
  # Checks whether object is a collection or an instance and serializes it
43
62
  def surrealize(**args)
44
- if object.respond_to?(:each)
63
+ if Helper.collection?(object)
45
64
  Surrealist.surrealize_collection(object, args.merge(context: context))
46
65
  else
47
66
  Surrealist.surrealize(instance: self, **args)
@@ -50,7 +69,7 @@ module Surrealist
50
69
 
51
70
  # Passes build_schema to Surrealist
52
71
  def build_schema(**args)
53
- if object.respond_to?(:each)
72
+ if Helper.collection?(object)
54
73
  build_collection_schema(args)
55
74
  else
56
75
  Surrealist.build_schema(instance: self, **args)
@@ -18,7 +18,7 @@ module Surrealist
18
18
 
19
19
  if value.respond_to?(:build_schema)
20
20
  yield assign_nested_record(instance, value)
21
- elsif value.respond_to?(:each) && !value.empty? && value.all? { |v| Helper.surrealist?(v.class) }
21
+ elsif Helper.collection?(value) && !value.empty? && value.all? { |v| Helper.surrealist?(v.class) }
22
22
  yield assign_nested_collection(instance, value)
23
23
  else
24
24
  yield value
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Surrealist
4
4
  # Defines the version of Surrealist
5
- VERSION = '1.0.0'.freeze
5
+ VERSION = '1.1.0'.freeze
6
6
  end
data/lib/surrealist.rb CHANGED
@@ -54,7 +54,7 @@ module Surrealist
54
54
  # # => "[{\"name\":\"Nikita\",\"age\":23}, {\"name\":\"Alessandro\",\"age\":24}]"
55
55
  # # For more examples see README
56
56
  def surrealize_collection(collection, **args)
57
- Surrealist::ExceptionRaiser.raise_invalid_collection! unless collection.respond_to?(:each)
57
+ Surrealist::ExceptionRaiser.raise_invalid_collection! unless Helper.collection?(collection)
58
58
 
59
59
  result = collection.map do |object|
60
60
  Helper.surrealist?(object.class) ? __build_schema(object, args) : object
@@ -81,6 +81,8 @@ module Surrealist
81
81
  Oj.dump(build_schema(instance: instance, **args), mode: :compat)
82
82
  end
83
83
 
84
+ # rubocop:disable Metrics/AbcSize
85
+
84
86
  # Builds hash from schema provided in the object's class and type-checks the values.
85
87
  #
86
88
  # @param [Object] instance of a class that has +Surrealist+ included.
@@ -121,15 +123,43 @@ module Surrealist
121
123
  # # => { name: 'Nikita', age: 23 }
122
124
  # # For more examples see README
123
125
  def build_schema(instance:, **args)
124
- carrier = Surrealist::Carrier.call(args)
125
126
  schema = Surrealist::VarsHelper.find_schema(instance.class)
126
-
127
127
  Surrealist::ExceptionRaiser.raise_unknown_schema!(instance) if schema.nil?
128
128
 
129
+ parameters = config ? config.merge(args) : args
130
+ carrier = Surrealist::Carrier.call(parameters)
129
131
  normalized_schema = Surrealist::Copier.deep_copy(schema, carrier, instance.class.name)
130
132
  hash = Builder.new(carrier, normalized_schema, instance).call
131
133
  carrier.camelize ? Surrealist::HashUtils.camelize_hash(hash) : hash
132
134
  end
135
+ # rubocop:enable Metrics/AbcSize
136
+
137
+ # Reads current default serialization arguments.
138
+ #
139
+ # @return [Hash] default arguments (@see Surrealist::Carrier)
140
+ def config
141
+ @default_args || Surrealist::Copier::EMPTY_HASH
142
+ end
143
+
144
+ # Sets default serialization arguments with a block
145
+ #
146
+ # @param [Hash] hash arguments to be set (@see Surrealist::Carrier)
147
+ # @param [Proc] _block a block which will be yielded to Surrealist::Carrier instance
148
+ #
149
+ # @example set config
150
+ # Surrealist.configure do |config|
151
+ # config.camelize = true
152
+ # config.include_root = true
153
+ # end
154
+ def configure(hash = nil, &_block)
155
+ if block_given?
156
+ carrier = Surrealist::Carrier.new
157
+ yield(carrier)
158
+ @default_args = carrier.parameters
159
+ else
160
+ @default_args = hash.nil? ? Surrealist::Copier::EMPTY_HASH : hash
161
+ end
162
+ end
133
163
 
134
164
  private
135
165
 
data/surrealist.gemspec CHANGED
@@ -24,11 +24,11 @@ Gem::Specification.new do |spec|
24
24
  spec.require_paths = ['lib']
25
25
  spec.required_ruby_version = '>= 2.2.0'
26
26
 
27
- spec.add_runtime_dependency 'oj', '~> 3'
27
+ spec.add_runtime_dependency 'oj', '3.4.0'
28
28
 
29
29
  spec.add_development_dependency 'bundler', '~> 1.16'
30
30
  spec.add_development_dependency 'pry', '~> 0.11'
31
31
  spec.add_development_dependency 'rake', '~> 12.3'
32
32
  spec.add_development_dependency 'rspec', '~> 3.7'
33
- spec.add_development_dependency 'rubocop', '0.51.0'
33
+ spec.add_development_dependency 'rubocop', '~> 0.52'
34
34
  end
metadata CHANGED
@@ -1,29 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: surrealist
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nikita Esaulov
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-02-11 00:00:00.000000000 Z
11
+ date: 2018-02-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: oj
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - '='
18
18
  - !ruby/object:Gem::Version
19
- version: '3'
19
+ version: 3.4.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
24
+ - - '='
25
25
  - !ruby/object:Gem::Version
26
- version: '3'
26
+ version: 3.4.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: bundler
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -84,16 +84,16 @@ dependencies:
84
84
  name: rubocop
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
- - - '='
87
+ - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: 0.51.0
89
+ version: '0.52'
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
- - - '='
94
+ - - "~>"
95
95
  - !ruby/object:Gem::Version
96
- version: 0.51.0
96
+ version: '0.52'
97
97
  description: A gem that provides DSL for serialization of plain old Ruby objects to
98
98
  JSON in a declarative style by defining a `schema`. It also provides a trivial type
99
99
  checking in the runtime before serialization.
@@ -157,7 +157,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
157
157
  version: '0'
158
158
  requirements: []
159
159
  rubyforge_project:
160
- rubygems_version: 2.7.3
160
+ rubygems_version: 2.7.6
161
161
  signing_key:
162
162
  specification_version: 4
163
163
  summary: A gem that provides DSL for serialization of plain old Ruby objects to JSON