alba 1.4.0 → 1.5.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: 01d43d93e589197104d9ee166e6a9dcf727fd204524b52145d7d93b45ba79cd2
4
- data.tar.gz: 023f2e0f8ff17bc78a01e2dc1eb1f98d9c41b0cd353e311f350c2704a9ffa602
3
+ metadata.gz: 34c441cbc87f959f73c3e7a0175309f780b23ce840add4d53cf5083488390f1f
4
+ data.tar.gz: f78fb2eea40f502c6f7b3c905317616054bd397d30aa9ce979285003205c81af
5
5
  SHA512:
6
- metadata.gz: 99992932a0f6a3d589e77e1b7dc4225be2e083a15320fa00a96f07a4cd9bbe9803454b94ecae409d439809a53225fb54ce283fcca074c0aed479e7d8b808d545
7
- data.tar.gz: c77e99af9cf98781a2e961817b5c60e31fe178e6eef7bb8bd77b1af199043b8acd8395a27dd7cb10680ae82d28ec57e98106fef42f57055cb23752d63605a7b9
6
+ metadata.gz: 326ac8e731f2b4e0fe4afbb8f690059ecd578ffcb9270b9779bdcb6bb50d57d063ec0a98fc488398915c39ca2978b41e79d53bb1f5a809f9272ad885c383a549
7
+ data.tar.gz: 2b36eb5d07749505b4ab377b56cc7e564bcd6e86d872e7cb2c45f62ae2cf91a41b2c6e0b70cdcbb8d2a5d802528e4d9bfb113b723a2be7b1bdbc207c0277df45
data/CHANGELOG.md CHANGED
@@ -6,6 +6,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [1.5.0] 2021-11-28
10
+
11
+ - [Feat] Add nil handler
12
+ - [Feat] Implement layout feature
13
+ - [Improve] if option now works with Symbol
14
+ - [Improve] Add an alias for serialize
15
+ - [Improve] Deprecation warning now printed with caller
16
+
9
17
  ## [1.4.0] 2021-06-30
10
18
 
11
19
  - [Feat] Add a config method to set encoder directly
@@ -14,6 +22,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
14
22
  - [Feat] Enable setting key for collection with `root_key`
15
23
  - [Feat] Add `Resource.root_key` and `Resource.root_key!`
16
24
  - [Feat] `Alba.serialize` now infers resource class
25
+ - [Deprecated] `Resource.key` and `Resource.key!` are deprecated
17
26
 
18
27
  ## [1.3.0] 2021-05-31
19
28
 
data/Gemfile CHANGED
@@ -9,8 +9,8 @@ gem 'inch', require: false # For inline documents
9
9
  gem 'minitest', '~> 5.14' # For test
10
10
  gem 'rake', '~> 13.0' # For test and automation
11
11
  gem 'rubocop', '>= 0.79.0', require: false # For lint
12
- gem 'rubocop-minitest', '~> 0.13.0', require: false # For lint
13
- gem 'rubocop-performance', '~> 1.11.0', require: false # For lint
12
+ gem 'rubocop-minitest', '~> 0.17.0', require: false # For lint
13
+ gem 'rubocop-performance', '~> 1.12.0', require: false # For lint
14
14
  gem 'rubocop-rake', '>= 0.5.1', require: false # For lint
15
15
  gem 'rubocop-sensible', '~> 0.3.0', require: false # For lint
16
16
  gem 'simplecov', '~> 0.21.0', require: false # For test coverage
data/README.md CHANGED
@@ -65,9 +65,11 @@ You can find the documentation on [RubyDoc](https://rubydoc.info/github/okuramas
65
65
  * Key transformation
66
66
  * Root key inference
67
67
  * Error handling
68
+ * Nil handling
68
69
  * Resource name inflection based on association name
69
70
  * Circular associations control
70
71
  * [Experimental] Types for validation and conversion
72
+ * Layout
71
73
  * No runtime dependencies
72
74
 
73
75
  ## Anti features
@@ -129,7 +131,7 @@ Alba.on_error :ignore
129
131
 
130
132
  For the details, see [Error handling section](#error-handling)
131
133
 
132
- ### Simple serialization with key
134
+ ### Simple serialization with root key
133
135
 
134
136
  ```ruby
135
137
  class User
@@ -146,7 +148,7 @@ end
146
148
  class UserResource
147
149
  include Alba::Resource
148
150
 
149
- key :user
151
+ root_key :user
150
152
 
151
153
  attributes :id, :name
152
154
 
@@ -243,7 +245,7 @@ end
243
245
  `Alba.serialize` method is a shortcut to define everything inline.
244
246
 
245
247
  ```ruby
246
- Alba.serialize(user, key: :foo) do
248
+ Alba.serialize(user, root_key: :foo) do
247
249
  attributes :id
248
250
  many :articles do
249
251
  attributes :title, :body
@@ -325,7 +327,7 @@ UserResourceCamel.new(user).serialize
325
327
  You can also transform root key when:
326
328
 
327
329
  * `Alba.enable_inference!` is called
328
- * `key!` is called in Resource class
330
+ * `root_key!` is called in Resource class
329
331
  * `root` option of `transform_keys` is set to true or `Alba.enable_root_key_transformation!` is called.
330
332
 
331
333
  ```ruby
@@ -342,7 +344,7 @@ end
342
344
  class BankAccountResource
343
345
  include Alba::Resource
344
346
 
345
- key!
347
+ root_key!
346
348
 
347
349
  attributes :account_number
348
350
  transform_keys :dash, root: true
@@ -570,6 +572,95 @@ Alba.on_error do |error, object, key, attribute, resource_class|
570
572
  end
571
573
  ```
572
574
 
575
+ ### Nil handling
576
+
577
+ Sometimes we want to convert `nil` to different values such as empty string. Alba provides a flexible way to handle `nil`.
578
+
579
+ ```ruby
580
+ class User
581
+ attr_reader :id, :name, :age
582
+
583
+ def initialize(id, name = nil, age = nil)
584
+ @id = id
585
+ @name = name
586
+ @age = age
587
+ end
588
+ end
589
+
590
+ class UserResource
591
+ include Alba::Resource
592
+
593
+ on_nil { '' }
594
+
595
+ root_key :user, :users
596
+
597
+ attributes :id, :name, :age
598
+ end
599
+
600
+ UserResource.new(User.new(1)).serialize
601
+ # => '{"user":{"id":1,"name":"","age":""}}'
602
+ ```
603
+
604
+ You can get various information via block parameters.
605
+
606
+ ```ruby
607
+ class UserResource
608
+ include Alba::Resource
609
+
610
+ on_nil do |object, key|
611
+ if key == age
612
+ 20
613
+ else
614
+ "User#{object.id}"
615
+ end
616
+ end
617
+
618
+ root_key :user, :users
619
+
620
+ attributes :id, :name, :age
621
+ end
622
+
623
+ UserResource.new(User.new(1)).serialize
624
+ # => '{"user":{"id":1,"name":"User1","age":20}}'
625
+ ```
626
+
627
+ You can also set global nil handler.
628
+
629
+ ```ruby
630
+ Alba.on_nil { 'default name' }
631
+
632
+ class Foo
633
+ attr_reader :name
634
+ def initialize(name)
635
+ @name = name
636
+ end
637
+ end
638
+
639
+ class FooResource
640
+ include Alba::Resource
641
+
642
+ key :foo
643
+
644
+ attributes :name
645
+ end
646
+
647
+ FooResource.new(Foo.new).serialize
648
+ # => '{"foo":{"name":"default name"}}'
649
+
650
+ class FooResource2
651
+ include Alba::Resource
652
+
653
+ key :foo
654
+
655
+ on_nil { '' } # This is applied instead of global handler
656
+
657
+ attributes :name
658
+ end
659
+
660
+ FooResource2.new(Foo.new).serialize
661
+ # => '{"foo":{"name":""}}'
662
+ ```
663
+
573
664
  ### Metadata
574
665
 
575
666
  You can set a metadata with `meta` DSL or `meta` option.
@@ -667,6 +758,68 @@ UserResource.new(user).serialize
667
758
 
668
759
  Note that this feature is experimental and interfaces are subject to change.
669
760
 
761
+ ### Layout
762
+
763
+ Sometimes we'd like to serialize JSON into a template. In other words, we need some structure OUTSIDE OF serialized JSON. IN HTML world, we call it a "layout".
764
+
765
+ Alba supports serializing JSON in a layout. You need a file for layout and then to specify file with `layout` method.
766
+
767
+ ```erb
768
+ {
769
+ "header": "my_header",
770
+ "body": <%= serialized_json %>
771
+ }
772
+ ```
773
+
774
+ ```ruby
775
+ class FooResource
776
+ include Alba::Resource
777
+ layout file: 'my_layout.json.erb'
778
+ end
779
+ ```
780
+
781
+ Note that layout files are treated as `json` and `erb` and evaluated in a context of the resource, meaning
782
+
783
+ * A layout file must be a valid JSON
784
+ * You must write `<%= serialized_json %>` in a layout to put serialized JSON string into a layout
785
+ * You can access `params` in a layout so that you can add virtually any objects to a layout
786
+ * When you access `params`, it's usually a Hash. You can use `encode` method in a layout to convert `params` Hash into a JSON with the backend you use
787
+ * You can also access `object`, the underlying object for the resource
788
+
789
+ In case you don't want to have a file for layout, Alba lets you define and apply layouts inline:
790
+
791
+ ```ruby
792
+ class FooResource
793
+ include Alba::Resource
794
+ layout inline: proc do
795
+ {
796
+ header: 'my header',
797
+ body: serializable_hash
798
+ }
799
+ end
800
+ end
801
+ ```
802
+
803
+ In the example above, we specify a Proc which returns a Hash as an inline layout. In the Proc we can use `serializable_hash` method to access a Hash right before serialization.
804
+
805
+ You can also use a Proc which returns String, not a Hash, for an inline layout.
806
+
807
+ ```ruby
808
+ class FooResource
809
+ include Alba::Resource
810
+ layout inline: proc do
811
+ %({
812
+ "header": "my header",
813
+ "body": #{serialized_json}
814
+ })
815
+ end
816
+ end
817
+ ```
818
+
819
+ It looks similar to file layout but you must use string interpolation for method calls since it's not an ERB.
820
+
821
+ Also note that we use percentage notation here to use double quotes. Using single quotes in inline string layout causes the error which might be resolved in other ways.
822
+
670
823
  ### Caching
671
824
 
672
825
  Currently, Alba doesn't support caching, primarily due to the behavior of `ActiveRecord::Relation`'s cache. See [the issue](https://github.com/rails/rails/issues/41784).
@@ -16,8 +16,11 @@ gemfile(true) do
16
16
  gem "benchmark-memory"
17
17
  gem "blueprinter"
18
18
  gem "jbuilder"
19
+ gem "jserializer"
19
20
  gem "jsonapi-serializer" # successor of fast_jsonapi
20
21
  gem "multi_json"
22
+ gem "panko_serializer"
23
+ gem "pg"
21
24
  gem "primalize"
22
25
  gem "oj"
23
26
  gem "representable"
@@ -27,7 +30,9 @@ end
27
30
 
28
31
  # --- Test data model setup ---
29
32
 
33
+ require "pg"
30
34
  require "active_record"
35
+ require "active_record/connection_adapters/postgresql_adapter"
31
36
  require "logger"
32
37
  require "oj"
33
38
  require "sqlite3"
@@ -157,6 +162,22 @@ class Comment
157
162
  end
158
163
  end
159
164
 
165
+ # --- Jserializer serializers ---
166
+
167
+ require 'jserializer'
168
+
169
+ class JserializerCommentSerializer < Jserializer::Base
170
+ attributes :id, :body
171
+ end
172
+
173
+ class JserializerPostSerializer < Jserializer::Base
174
+ attributes :id, :body, :commenter_names
175
+ has_many :comments, serializer: JserializerCommentSerializer
176
+ def commenter_names
177
+ object.commenters.pluck(:name)
178
+ end
179
+ end
180
+
160
181
  # --- JSONAPI:Serializer serializers / (successor of fast_jsonapi) ---
161
182
 
162
183
  class JsonApiStandardCommentSerializer
@@ -218,6 +239,26 @@ class JsonApiSameFormatPostSerializer < JsonApiSameFormatSerializer
218
239
  end
219
240
  end
220
241
 
242
+ # --- Panko serializers ---
243
+ #
244
+
245
+ require "panko_serializer"
246
+
247
+ class PankoCommentSerializer < Panko::Serializer
248
+ attributes :id, :body
249
+ end
250
+
251
+
252
+ class PankoPostSerializer < Panko::Serializer
253
+ attributes :id, :body, :commenter_names
254
+
255
+ has_many :comments, serializer: PankoCommentSerializer
256
+
257
+ def commenter_names
258
+ object.comments.pluck(:name)
259
+ end
260
+ end
261
+
221
262
  # --- Primalize serializers ---
222
263
  #
223
264
  class PrimalizeCommentResource < Primalize::Single
@@ -328,8 +369,10 @@ jbuilder = Proc.new do
328
369
  end
329
370
  end.target!
330
371
  end
372
+ jserializer = Proc.new { JserializerPostSerializer.new(posts, is_collection: true).to_json }
331
373
  jsonapi = proc { JsonApiStandardPostSerializer.new(posts).to_json }
332
374
  jsonapi_same_format = proc { JsonApiSameFormatPostSerializer.new(posts).to_json }
375
+ panko = proc { Panko::ArraySerializer.new(posts, each_serializer: PankoPostSerializer).to_json }
333
376
  primalize = proc { PrimalizePostsResource.new(posts: posts).to_json }
334
377
  rails = Proc.new do
335
378
  ActiveSupport::JSON.encode(posts.map{ |post| post.serializable_hash(include: :comments) })
@@ -346,8 +389,10 @@ puts "Serializer outputs ----------------------------------"
346
389
  ams: ams,
347
390
  blueprinter: blueprinter,
348
391
  jbuilder: jbuilder, # different order
392
+ jserializer: jserializer,
349
393
  jsonapi: jsonapi, # nested JSON:API format
350
394
  jsonapi_same_format: jsonapi_same_format,
395
+ panko: panko,
351
396
  primalize: primalize,
352
397
  rails: rails,
353
398
  representable: representable,
@@ -363,8 +408,10 @@ Benchmark.ips do |x|
363
408
  x.report(:ams, &ams)
364
409
  x.report(:blueprinter, &blueprinter)
365
410
  x.report(:jbuilder, &jbuilder)
411
+ x.report(:jserializer, &jserializer)
366
412
  x.report(:jsonapi, &jsonapi)
367
413
  x.report(:jsonapi_same_format, &jsonapi_same_format)
414
+ x.report(:panko, &panko)
368
415
  x.report(:primalize, &primalize)
369
416
  x.report(:rails, &rails)
370
417
  x.report(:representable, &representable)
@@ -381,8 +428,10 @@ Benchmark.memory do |x|
381
428
  x.report(:ams, &ams)
382
429
  x.report(:blueprinter, &blueprinter)
383
430
  x.report(:jbuilder, &jbuilder)
431
+ x.report(:jserializer, &jserializer)
384
432
  x.report(:jsonapi, &jsonapi)
385
433
  x.report(:jsonapi_same_format, &jsonapi_same_format)
434
+ x.report(:panko, &panko)
386
435
  x.report(:primalize, &primalize)
387
436
  x.report(:rails, &rails)
388
437
  x.report(:representable, &representable)
@@ -13,10 +13,14 @@ gemfile(true) do
13
13
  gem "activerecord", "6.1.3"
14
14
  gem "alba", path: '../'
15
15
  gem "benchmark-ips"
16
+ gem "benchmark-memory"
16
17
  gem "blueprinter"
17
18
  gem "jbuilder"
19
+ gem "jserializer"
18
20
  gem "jsonapi-serializer" # successor of fast_jsonapi
19
21
  gem "multi_json"
22
+ gem "panko_serializer"
23
+ gem "pg"
20
24
  gem "primalize"
21
25
  gem "oj"
22
26
  gem "representable"
@@ -26,7 +30,9 @@ end
26
30
 
27
31
  # --- Test data model setup ---
28
32
 
33
+ require "pg"
29
34
  require "active_record"
35
+ require "active_record/connection_adapters/postgresql_adapter"
30
36
  require "logger"
31
37
  require "oj"
32
38
  require "sqlite3"
@@ -154,6 +160,23 @@ class Comment
154
160
  end
155
161
  end
156
162
 
163
+ # --- Jserializer serializers ---
164
+
165
+ require 'jserializer'
166
+
167
+ class JserializerCommentSerializer < Jserializer::Base
168
+ attributes :id, :body
169
+ end
170
+
171
+ class JserializerPostSerializer < Jserializer::Base
172
+ attributes :id, :body, :commenter_names
173
+ has_many :comments, serializer: JserializerCommentSerializer
174
+ def commenter_names
175
+ object.commenters.pluck(:name)
176
+ end
177
+ end
178
+
179
+
157
180
  # --- JSONAPI:Serializer serializers / (successor of fast_jsonapi) ---
158
181
 
159
182
  class JsonApiStandardCommentSerializer
@@ -215,6 +238,25 @@ class JsonApiSameFormatPostSerializer < JsonApiSameFormatSerializer
215
238
  end
216
239
  end
217
240
 
241
+ # --- Panko serializers ---
242
+ #
243
+ require "panko_serializer"
244
+
245
+ class PankoCommentSerializer < Panko::Serializer
246
+ attributes :id, :body
247
+ end
248
+
249
+
250
+ class PankoPostSerializer < Panko::Serializer
251
+ attributes :id, :body, :commenter_names
252
+
253
+ has_many :comments, serializer: PankoCommentSerializer
254
+
255
+ def commenter_names
256
+ object.comments.pluck(:name)
257
+ end
258
+ end
259
+
218
260
  # --- Primalize serializers ---
219
261
  #
220
262
  class PrimalizeCommentResource < Primalize::Single
@@ -308,8 +350,10 @@ end
308
350
  ams = Proc.new { AMSPostSerializer.new(post, {}).to_json }
309
351
  blueprinter = Proc.new { PostBlueprint.render(post) }
310
352
  jbuilder = Proc.new { post.to_builder.target! }
353
+ jserializer = Proc.new { JserializerPostSerializer.new(post).to_json }
311
354
  jsonapi = proc { JsonApiStandardPostSerializer.new(post).to_json }
312
355
  jsonapi_same_format = proc { JsonApiSameFormatPostSerializer.new(post).to_json }
356
+ panko = proc { PankoPostSerializer.new.serialize_to_json(post) }
313
357
  primalize = proc { PrimalizePostResource.new(post).to_json }
314
358
  rails = Proc.new { ActiveSupport::JSON.encode(post.serializable_hash(include: :comments)) }
315
359
  representable = Proc.new { PostRepresenter.new(post).to_json }
@@ -324,8 +368,10 @@ puts "Serializer outputs ----------------------------------"
324
368
  ams: ams,
325
369
  blueprinter: blueprinter,
326
370
  jbuilder: jbuilder, # different order
371
+ jserializer: jserializer,
327
372
  jsonapi: jsonapi, # nested JSON:API format
328
373
  jsonapi_same_format: jsonapi_same_format,
374
+ panko: panko,
329
375
  primalize: primalize,
330
376
  rails: rails,
331
377
  representable: representable,
@@ -341,8 +387,10 @@ Benchmark.ips do |x|
341
387
  x.report(:ams, &ams)
342
388
  x.report(:blueprinter, &blueprinter)
343
389
  x.report(:jbuilder, &jbuilder)
390
+ x.report(:jserializer, &jserializer)
344
391
  x.report(:jsonapi, &jsonapi)
345
392
  x.report(:jsonapi_same_format, &jsonapi_same_format)
393
+ x.report(:panko, &panko)
346
394
  x.report(:primalize, &primalize)
347
395
  x.report(:rails, &rails)
348
396
  x.report(:representable, &representable)
@@ -359,8 +407,10 @@ Benchmark.memory do |x|
359
407
  x.report(:ams, &ams)
360
408
  x.report(:blueprinter, &blueprinter)
361
409
  x.report(:jbuilder, &jbuilder)
410
+ x.report(:jserializer, &jserializer)
362
411
  x.report(:jsonapi, &jsonapi)
363
412
  x.report(:jsonapi_same_format, &jsonapi_same_format)
413
+ x.report(:panko, &panko)
364
414
  x.report(:primalize, &primalize)
365
415
  x.report(:rails, &rails)
366
416
  x.report(:representable, &representable)
@@ -0,0 +1,359 @@
1
+ ---
2
+ title: Upgrading from ActiveModelSerializers
3
+ ---
4
+
5
+ <!-- @format -->
6
+
7
+ This guide is aimed at helping ActiveModelSerializers users transition to Alba, and it consists of three parts:
8
+
9
+ 1. Basic serialization
10
+ 2. Complex serialization
11
+ 3. Unsupported features
12
+
13
+ ## Example class
14
+
15
+ Example clsss is inherited `ActiveRecord::Base`, because [serializing PORO with AMS is pretty hard](https://github.com/rails-api/active_model_serializers/blob/0-10-stable/docs/howto/serialize_poro.md).
16
+
17
+ ```rb
18
+ class User < ActiveRecord::Base
19
+ # columns: id, created_at, updated_at
20
+ has_one :profile
21
+ has_many :articles
22
+ end
23
+
24
+ class Profile < ActiveRecord::Base
25
+ # columns: id, user_id, email, created_at, updated_at
26
+ belongs_to :user
27
+ end
28
+
29
+ class Article < ActiveRecord::Base
30
+ # columns: id, user_id, title, body, created_at, updated_at
31
+ belongs_to :user
32
+ end
33
+ ```
34
+
35
+ ## 1. Basic serialization
36
+
37
+ ### #serializable_hash
38
+
39
+ - or #as_json, #to_json
40
+
41
+ #### ActiveModelSerializer
42
+
43
+ ```rb
44
+ # Infer and use by "#{MODEL_NAME}Serializer" in app/serializers/user_serializer.rb
45
+ class UserSerializer < ActiveModel::Serializer
46
+ type :user
47
+ attributes :id, :created_at, :updated_at
48
+ end
49
+
50
+ # serialze
51
+ user = User.create!
52
+ ActiveModelSerializers::SerializableResource.new(
53
+ user
54
+ ).serializable_hash
55
+ # => {
56
+ # user: {
57
+ # id: id,
58
+ # created_at: created_at,
59
+ # updated_at: updated_at
60
+ # }
61
+ # }
62
+ ```
63
+
64
+ #### Alba
65
+
66
+ ```rb
67
+ # Infer and use by "#{MODEL_NAME}Resource"
68
+ # In app/resources/user_resource.rb
69
+ class UserResource
70
+ include Alba::Resource
71
+ attributes :id, :created_at, :updated_at
72
+ end
73
+
74
+ # serialze
75
+ user = User.create!
76
+ UserResource.new(user).serializable_hash
77
+ # => {
78
+ # id: id,
79
+ # created_at: created_at,
80
+ # updated_at: updated_at,
81
+ # }
82
+
83
+ # If want `user key`
84
+ class UserResource
85
+ include Alba::Resource
86
+ root_key :user # Call root_key method like ActiveModel::Serializer#type
87
+ attributes :id, :created_at, :updated_at
88
+ end
89
+
90
+ # serialze
91
+ user = User.create!
92
+ JSON.parse UserResource.new(user).serialize # !!!!serializable_hash does not support root key!!! Must use JSON.parse and serialize
93
+ # => {
94
+ # "user"=>{
95
+ # "id"=>id,
96
+ # "created_at"=>created_at,
97
+ # "updated_at"=>updated_at
98
+ # }
99
+ # }
100
+ # If want symbolize keys with #deep_symbolize_keys in Rails
101
+ user = User.create!
102
+ JSON.parse(UserResource.new(user).serialize).deep_symbolize_keys
103
+ # => {
104
+ # user: {
105
+ # id: id,
106
+ # created_at: created_at,
107
+ # updated_at: updated_at
108
+ # }
109
+ # }
110
+ ```
111
+
112
+ ## 2. Complex serialization
113
+
114
+ ### Serialize collections
115
+
116
+ #### ActiveModelSerializer
117
+
118
+ ```rb
119
+ class UserSerializer < ActiveModel::Serializer
120
+ type :user
121
+ attributes :id, :created_at, :updated_at
122
+ end
123
+ 3.times { User.create! }
124
+ users = User.limit 3
125
+ ActiveModelSerializers::SerializableResource.new(
126
+ users,
127
+ adapter: :attributes # Comment out this line if you want users key
128
+ # Want to specified key to call with root: args
129
+ ).serializable_hash
130
+ # => [{:id=>1, :created_at=>created_at, :updated_at=>updated_at},
131
+ # {:id=>2, :created_at=>created_at, :updated_at=>updated_at},
132
+ # {:id=>3, :created_at=>created_at, :updated_at=>updated_at}]
133
+ ```
134
+
135
+ #### Alba
136
+
137
+ ```rb
138
+ class UserResource
139
+ include Alba::Resource
140
+ attributes :id, :created_at, :updated_at
141
+ end
142
+ 3.times { User.create! }
143
+ users = User.limit 3
144
+ UserResource.new(users).serializable_hash
145
+ # =>[{:id=>1, :created_at=>created_at, :updated_at=>updated_at},
146
+ # {:id=>2, :created_at=>created_at, :updated_at=>updated_at},
147
+ # {:id=>3, :created_at=>created_at, :updated_at=>updated_at}]
148
+ # or
149
+ JSON.parse UserResource.new(users).serialize(root_key: :users)
150
+ # => {"users"=>
151
+ # [{"id"=>1, "created_at"=>created_at, "updated_at"=>updated_at},
152
+ # {"id"=>2, "created_at"=>created_at, "updated_at"=>updated_at},
153
+ # {"id"=>3, "created_at"=>created_at, "updated_at"=>updated_at}]}
154
+ ```
155
+
156
+ ### Nested serialization
157
+
158
+ #### ActiveModelSerializer
159
+
160
+ ```rb
161
+ class ProfileSerializer < ActiveModel::Serializer
162
+ type :profile
163
+ attributes :email
164
+ end
165
+
166
+ class ArticleSerializer < ActiveModel::Serializer
167
+ type :article
168
+ attributes :title, :body
169
+ end
170
+
171
+ class UserSerializer < ActiveModel::Serializer
172
+ type :user
173
+ attributes :id, :created_at, :updated_at
174
+ has_one :profile, serializer: ProfileSerializer # For has_one relation
175
+ has_many :articles, serializer: ArticleSerializer # For has_many relation
176
+ end
177
+ user = User.create!
178
+ user.craete_profile! email: email
179
+ user.articles.create! title: title, body: body
180
+ ActiveModelSerializers::SerializableResource.new(
181
+ user
182
+ ).serializable_hash
183
+ # => {
184
+ # :user=> {
185
+ # :id=>1,
186
+ # :created_at=>created_at,
187
+ # :updated_at=>updated_at,
188
+ # :profile=> {
189
+ # :email=>email
190
+ # },
191
+ # :articles => [
192
+ # {
193
+ # :title=>title,
194
+ # :body=>body
195
+ # }
196
+ # ]
197
+ # }
198
+ # }
199
+ ```
200
+
201
+ #### Alba
202
+
203
+ ```rb
204
+ class ProfileResource
205
+ include Alba::Resource
206
+ root_key :profile
207
+ attributes :email
208
+ end
209
+
210
+ class ArticleResource
211
+ include Alba::Resource
212
+ root_key :article
213
+ attributes :title, :body
214
+ end
215
+
216
+ class UserResource
217
+ include Alba::Resource
218
+ root_key :user
219
+ attributes :id, :created_at, :updated_at
220
+ one :profile, resource: ProfileResource # For has_one relation
221
+ many :articles, resource: ArticleResource # For has_many relation
222
+ end
223
+
224
+ user = User.create!
225
+ user.craete_profile! email: email
226
+ user.articles.create! title: title, body: body
227
+ UserResource.new(user).serializable_hash
228
+ # => {
229
+ # :id=>1,
230
+ # :created_at=>created_at,
231
+ # :updated_at=>updated_at,
232
+ # :profile=> {
233
+ # :email=>email
234
+ # },
235
+ # :articles => [
236
+ # {
237
+ # :title=>title,
238
+ # :body=>body
239
+ # }
240
+ # ]
241
+ # }
242
+ ```
243
+
244
+ ### Serialize with custom serializer
245
+
246
+ #### ActiveModelSerializer
247
+
248
+ ```rb
249
+ class CustomUserSerializer < ActiveModel::Serializer
250
+ type :user
251
+ attribute :email do
252
+ object.profile.email
253
+ end
254
+ end
255
+
256
+ # serialze
257
+ user = User.create!
258
+ user.craete_profile! email: email
259
+ ActiveModelSerializers::SerializableResource.new(
260
+ user,
261
+ serializer: ::CustomUserSerializer # Call with serializer arg
262
+ ).serializable_hash
263
+ # => {
264
+ # user: {
265
+ # email: email
266
+ # }
267
+ # }
268
+ ```
269
+
270
+ #### Alba
271
+
272
+ ```rb
273
+ class CustomUserResource
274
+ include Alba::Resource
275
+ root_key :user
276
+ attribute :email do
277
+ object.profile.email
278
+ end
279
+ end
280
+
281
+ # serialze
282
+ user = User.create!
283
+ user.craete_profile! email: email
284
+ CustomUserResource.new(user).serializable_hash
285
+ # => {
286
+ # email: email
287
+ # }
288
+ ```
289
+
290
+ ### Passing arbitrary options to a serializer
291
+
292
+ #### ActiveModelSerializer
293
+
294
+ ```rb
295
+ class UserSerializer < ApplicationSerializer
296
+ type :user
297
+ attributes :id, :created_at, :updated_at
298
+ attribute :custom_params do
299
+ pp instance_options
300
+ # => given_params: { a: :b }
301
+ instance_options # Access by instance_options method
302
+ end
303
+ end
304
+
305
+ # serialze
306
+ user = User.create!
307
+ ActiveModelSerializers::SerializableResource.new(
308
+ user,
309
+ given_params: { a: :b } # Give with your favorite keyword argument
310
+ ).serializable_hash
311
+ # => {
312
+ # :id=>1,
313
+ # :created_at=>created_at,
314
+ # :updated_at=>updated_at,
315
+ # :custom_params=>{
316
+ # :given_params=>{
317
+ # :a=>:b
318
+ # }
319
+ # }
320
+ # }
321
+ ```
322
+
323
+ #### Alba
324
+
325
+ ```rb
326
+ class UserResource
327
+ include Alba::Resource
328
+ root_key :user
329
+ attributes :id, :created_at, :updated_at
330
+ attribute :custom_params do
331
+ pp params
332
+ # => { :a=>:b }
333
+ params
334
+ end
335
+ end
336
+
337
+ # serialze
338
+ user = User.create!
339
+ UserResource.new(
340
+ user,
341
+ params: { a: :b } # Give with :params keyword argument
342
+ ).serializable_hash
343
+ # => {
344
+ # :id=>1,
345
+ # :created_at=>created_at,
346
+ # :updated_at=>updated_at,
347
+ # :custom_params=>{
348
+ # :a=>:b
349
+ # }
350
+ # }
351
+ ```
352
+
353
+ ## 3. Unsupported features
354
+
355
+ - [RelationshipLinks](https://github.com/rails-api/active_model_serializers/blob/v0.10.6/docs/howto/add_relationship_links.md)
356
+ - [PaginationLinks](https://github.com/rails-api/active_model_serializers/blob/v0.10.6/docs/howto/add_pagination_links.md)
357
+ - [Logging](https://github.com/rails-api/active_model_serializers/blob/v0.10.6/docs/general/logging.md)
358
+ - [Caching](https://github.com/rails-api/active_model_serializers/blob/v0.10.6/docs/general/caching.md)
359
+ - [Rendering](https://github.com/rails-api/active_model_serializers/blob/v0.10.6/docs/general/rendering.md)
@@ -0,0 +1,223 @@
1
+ ---
2
+ title: Upgrading from Jbuilder
3
+ ---
4
+
5
+ <!-- @format -->
6
+
7
+ This guide is aimed at helping Jbuilder users transition to Alba, and it consists of three parts:
8
+
9
+ 1. Basic serialization
10
+ 2. Complex serialization
11
+ 3. Unsupported features
12
+
13
+ ## Example class
14
+
15
+ This example will also be replaced by ActiveReord.
16
+
17
+ ```rb
18
+ class User
19
+ attr_reader :id, :created_at, :updated_at
20
+ attr_accessor :profile, :articles
21
+
22
+ def initialize(id)
23
+ @id = id
24
+ @created_at = Time.now
25
+ @updated_at = Time.now
26
+ @articles = []
27
+ end
28
+ end
29
+
30
+ class Profile
31
+ attr_reader :email
32
+
33
+ def initialize(email)
34
+ @email = email
35
+ end
36
+ end
37
+
38
+ class Article
39
+ attr_accessor :title, :body
40
+
41
+ def initialize(title, body)
42
+ @title = title
43
+ @body = body
44
+ end
45
+ end
46
+ ```
47
+
48
+ ## 1. Basic serialization
49
+
50
+ #### Jbuilder
51
+
52
+ ```rb
53
+ # show.json.jbuilder
54
+ # With block
55
+ @user = User.new(id)
56
+ json.user do |user|
57
+ user.id @user.id
58
+ user.created_at @user.created_at
59
+ user.updated_at @user.updated_at
60
+ end
61
+ # => '{"user":{"id":id, "created_at": created_at, "updated_at": updated_at}'
62
+ # or #extract!
63
+ json.extract! @user, :id, :created_at, :updated_at
64
+ # => '{"id":id, "created_at": created_at, "updated_at": updated_at}'
65
+ ```
66
+
67
+ #### Alba
68
+
69
+ ```rb
70
+ # With block
71
+ user = User.new(id)
72
+ Alba.serialize(user, root_key: :user) do
73
+ attributes :id, :created_at, :updated_at
74
+ end
75
+ # => '{"user":{"id":id, "created_at": created_at, "updated_at": updated_at}'
76
+ # or with resourceClass.
77
+ # Infer and use by "#{MODEL_NAME}Resource"
78
+ class UserResource
79
+ include Alba::Resource
80
+ root_key :user
81
+ attributes :id, :created_at, :updated_at
82
+ end
83
+ UserResource.new(user).serialize
84
+ # => '{"user":{"id":id, "created_at": created_at, "updated_at": updated_at}'
85
+ ```
86
+
87
+ ## 2. Complex serialization
88
+
89
+ ### Serialize collections
90
+
91
+ #### Jbuilder
92
+
93
+ ```rb
94
+ @users = ids.map { |id| User.new(id) }
95
+ # index.json.jbuilder
96
+ json.array! @users, :id, :created_at, :updated_at
97
+ # => '[{"id":id, "created_at": created_at, "updated_at": updated_at}, {"id":id, "created_at": created_at, "updated_at": updated_at}, {"id":id, "created_at": created_at, "updated_at": updated_at}]'
98
+ ```
99
+
100
+ #### Alba
101
+
102
+ ```rb
103
+ class UserResource
104
+ include Alba::Resource
105
+ root_key :user
106
+ attributes :id, :created_at, :updated_at
107
+ end
108
+ users = ids.map { |id| User.new(id) }
109
+ UserResource.new(users).serialize
110
+ # => '[{"id":id, "created_at": created_at, "updated_at": updated_at}, {"id":id, "created_at": created_at, "updated_at": updated_at}, {"id":id, "created_at": created_at, "updated_at": updated_at}]'
111
+
112
+ ```
113
+
114
+ ### Nested serialization
115
+
116
+ #### Jbuilder
117
+
118
+ ```rb
119
+ # show.json.jbuilder
120
+ @user = User.new(id)
121
+ @user.profile = Profile.new(email)
122
+ @user.articles = [Article.new(title, body)]
123
+ json.user do |user|
124
+ user.id @user.id
125
+ user.created_at @user.created_at
126
+ user.updated_at @user.updated_at
127
+ json.profile do
128
+ json.email @user.profile.email
129
+ end
130
+ json.articles do
131
+ json.array! @user.articles, :title, :body
132
+ end
133
+ end
134
+ # => '{"user":{"id":id, "created_at": created_at, "updated_at": updated_at, "profile": {"email": email}, articles: [{"title": title, "body": body}]}'
135
+ # or #merge!
136
+ profile_hash = { profile: { email: @user.profile.email } }
137
+ articles_hash = { articles: @user.articles.map { |article| { title: article.title, body: article.body } } }
138
+ json.user do |user|
139
+ user.id @user.id
140
+ user.created_at @user.created_at
141
+ user.updated_at @user.updated_at
142
+ json.merge! profile_hash
143
+ json.merge! articles_hash
144
+ end
145
+ # => '{"user":{"id":id, "created_at": created_at, "updated_at": updated_at, "profile": {"email": email}, articles: [{"title": title, "body": body}]}'
146
+ # or #partial!
147
+ # profiles/_profile.json.jbuilder
148
+ json.profile do
149
+ json.email @profile.email
150
+ end
151
+ # articles/_article.json.jbuilder
152
+ json.extract! article, :title, :body
153
+ # user/show.json.jbuilder
154
+ json.user do |user|
155
+ user.id @user.id
156
+ user.created_at @user.created_at
157
+ user.updated_at @user.updated_at
158
+ json.partial! @user.profile, as: :profile
159
+ json.articles @user.articles do |article|
160
+ json.partial! article, partial: 'articles/article'
161
+ end
162
+ end
163
+ ```
164
+
165
+ #### Alba
166
+
167
+ ```rb
168
+ # With ResourceClass by each resources
169
+ class ProfileResource
170
+ include Alba::Resource
171
+ root_key :profile
172
+ attributes :email
173
+ end
174
+ class ArticleResource
175
+ include Alba::Resource
176
+ root_key :article
177
+ attributes :title, :body
178
+ end
179
+ class UserResource
180
+ include Alba::Resource
181
+ root_key :user
182
+ attributes :id, :created_at, :updated_at
183
+ one :profile, resource: ProfileResource
184
+ many :articles, resource: ArticleResource
185
+ end
186
+ user = User.new(id)
187
+ user.profile = Profile.new(email)
188
+ user.articles = [Article.new(title, body)]
189
+ UserResource.new(user).serialize
190
+ # => '{"user":{"id":id, "created_at": created_at, "updated_at": updated_at, "profile": {"email": email}, articles: [{"title": title, "body": body}]}'
191
+
192
+ # or #attribute
193
+ class UserResource
194
+ include Alba::Resource
195
+ root_key :user
196
+ attributes :id, :created_at, :updated_at
197
+
198
+ attribute :profile do
199
+ {
200
+ email: object.profile.email # Can access to received resource by #object method
201
+ }
202
+ end
203
+
204
+ attribute :articles do
205
+ object.articles.map do |article|
206
+ {
207
+ title: article.title,
208
+ body: article.body,
209
+ }
210
+ end
211
+ end
212
+ end
213
+ user = User.new(id)
214
+ user.profile = Profile.new(email)
215
+ UserResource.new(user).serialize
216
+ # => '{"user":{"id":id, "created_at": created_at, "updated_at": updated_at, "profile": {"email": email}, articles: [{"title": title, "body": body}]}'
217
+ ```
218
+
219
+ ## 3. Unsupported features
220
+
221
+ - Jbuilder#ignore_nil!
222
+ - Jbuilder#cache!
223
+ - Jbuilder.key_format! and Jbuilder.deep_format_keys!
@@ -0,0 +1,14 @@
1
+ module Alba
2
+ # Module for printing deprecation warning
3
+ module Deprecation
4
+ # Similar to {Kernel.warn} but prints caller as well
5
+ #
6
+ # @param message [String] main message to print
7
+ # @return void
8
+ def warn(message)
9
+ Kernel.warn(message)
10
+ Kernel.warn(caller_locations(2..2).first) # For performance reason we use (2..2).first
11
+ end
12
+ module_function :warn
13
+ end
14
+ end
data/lib/alba/resource.rb CHANGED
@@ -2,13 +2,14 @@ require_relative 'one'
2
2
  require_relative 'many'
3
3
  require_relative 'key_transform_factory'
4
4
  require_relative 'typed_attribute'
5
+ require_relative 'deprecation'
5
6
 
6
7
  module Alba
7
8
  # This module represents what should be serialized
8
9
  module Resource
9
10
  # @!parse include InstanceMethods
10
11
  # @!parse extend ClassMethods
11
- DSLS = {_attributes: {}, _key: nil, _key_for_collection: nil, _meta: nil, _transform_key_function: nil, _transforming_root_key: false, _on_error: nil}.freeze # rubocop:disable Layout/LineLength
12
+ DSLS = {_attributes: {}, _key: nil, _key_for_collection: nil, _meta: nil, _transform_key_function: nil, _transforming_root_key: false, _on_error: nil, _on_nil: nil, _layout: nil}.freeze # rubocop:disable Layout/LineLength
12
13
  private_constant :DSLS
13
14
 
14
15
  WITHIN_DEFAULT = Object.new.freeze
@@ -48,7 +49,7 @@ module Alba
48
49
  # @param meta [Hash] metadata for this seialization
49
50
  # @return [String] serialized JSON string
50
51
  def serialize(key: nil, root_key: nil, meta: {})
51
- warn '`key` option to `serialize` method is deprecated, use `root_key` instead.' if key
52
+ Alba::Deprecation.warn '`key` option to `serialize` method is deprecated, use `root_key` instead.' if key
52
53
  key = key.nil? && root_key.nil? ? fetch_key : root_key || key
53
54
  hash = if key && key != ''
54
55
  h = {key.to_s => serializable_hash}
@@ -56,8 +57,9 @@ module Alba
56
57
  else
57
58
  serializable_hash
58
59
  end
59
- Alba.encoder.call(hash)
60
+ serialize_with(hash)
60
61
  end
62
+ alias to_json serialize
61
63
 
62
64
  # A Hash for serialization
63
65
  #
@@ -69,6 +71,25 @@ module Alba
69
71
 
70
72
  private
71
73
 
74
+ attr_reader :serialized_json # Mainly for layout
75
+
76
+ def encode(hash)
77
+ Alba.encoder.call(hash)
78
+ end
79
+
80
+ def serialize_with(hash)
81
+ @serialized_json = encode(hash)
82
+ case @_layout
83
+ when String # file
84
+ ERB.new(File.read(@_layout)).result(binding)
85
+ when Proc # inline
86
+ inline = instance_eval(&@_layout)
87
+ inline.is_a?(Hash) ? encode(inline) : inline
88
+ else # no layout
89
+ @serialized_json
90
+ end
91
+ end
92
+
72
93
  def hash_with_metadata(hash, meta)
73
94
  base = @_meta ? instance_eval(&@_meta) : {}
74
95
  metadata = base.merge(meta)
@@ -120,23 +141,38 @@ module Alba
120
141
  if attribute.is_a?(Array) # Conditional
121
142
  conditional_attribute(object, key, attribute)
122
143
  else
123
- [key, fetch_attribute(object, attribute)]
144
+ fetched_attribute = fetch_attribute(object, key, attribute)
145
+ [key, fetched_attribute]
124
146
  end
125
147
  end
126
148
 
127
149
  def conditional_attribute(object, key, attribute)
128
150
  condition = attribute.last
151
+ if condition.is_a?(Proc)
152
+ conditional_attribute_with_proc(object, key, attribute.first, condition)
153
+ else
154
+ conditional_attribute_with_symbol(object, key, attribute.first, condition)
155
+ end
156
+ end
157
+
158
+ def conditional_attribute_with_proc(object, key, attribute, condition)
129
159
  arity = condition.arity
130
160
  # We can return early to skip fetch_attribute
131
161
  return [] if arity <= 1 && !instance_exec(object, &condition)
132
162
 
133
- fetched_attribute = fetch_attribute(object, attribute.first)
134
- attr = attribute.first.is_a?(Alba::Association) ? attribute.first.object : fetched_attribute
163
+ fetched_attribute = fetch_attribute(object, key, attribute)
164
+ attr = attribute.is_a?(Alba::Association) ? attribute.object : fetched_attribute
135
165
  return [] if arity >= 2 && !instance_exec(object, attr, &condition)
136
166
 
137
167
  [key, fetched_attribute]
138
168
  end
139
169
 
170
+ def conditional_attribute_with_symbol(object, key, attribute, condition)
171
+ return [] unless __send__(condition)
172
+
173
+ [key, fetch_attribute(object, key, attribute)]
174
+ end
175
+
140
176
  def handle_error(error, object, key, attribute)
141
177
  on_error = @_on_error || Alba._on_error
142
178
  case on_error
@@ -156,15 +192,20 @@ module Alba
156
192
  @_transform_key_function.call(key.to_s)
157
193
  end
158
194
 
159
- def fetch_attribute(object, attribute)
160
- case attribute
161
- when Symbol then object.public_send attribute
162
- when Proc then instance_exec(object, &attribute)
163
- when Alba::One, Alba::Many then yield_if_within(attribute.name.to_sym) { |within| attribute.to_hash(object, params: params, within: within) }
164
- when TypedAttribute then attribute.value(object)
165
- else
166
- raise ::Alba::Error, "Unsupported type of attribute: #{attribute.class}"
167
- end
195
+ def fetch_attribute(object, key, attribute)
196
+ value = case attribute
197
+ when Symbol then object.public_send attribute
198
+ when Proc then instance_exec(object, &attribute)
199
+ when Alba::One, Alba::Many then yield_if_within(attribute.name.to_sym) { |within| attribute.to_hash(object, params: params, within: within) }
200
+ when TypedAttribute then attribute.value(object)
201
+ else
202
+ raise ::Alba::Error, "Unsupported type of attribute: #{attribute.class}"
203
+ end
204
+ value.nil? && nil_handler ? instance_exec(object, key, attribute, &nil_handler) : value
205
+ end
206
+
207
+ def nil_handler
208
+ @nil_handler ||= (@_on_nil || Alba._on_nil)
168
209
  end
169
210
 
170
211
  def yield_if_within(association_name)
@@ -289,7 +330,7 @@ module Alba
289
330
  # @param key [String, Symbol]
290
331
  # @deprecated Use {#root_key} instead
291
332
  def key(key)
292
- warn '[DEPRECATION] `key` is deprecated, use `root_key` instead.'
333
+ Alba::Deprecation.warn '[DEPRECATION] `key` is deprecated, use `root_key` instead.'
293
334
  @_key = key.respond_to?(:to_sym) ? key.to_sym : key
294
335
  end
295
336
 
@@ -307,7 +348,7 @@ module Alba
307
348
  #
308
349
  # @deprecated Use {#root_key!} instead
309
350
  def key!
310
- warn '[DEPRECATION] `key!` is deprecated, use `root_key!` instead.'
351
+ Alba::Deprecation.warn '[DEPRECATION] `key!` is deprecated, use `root_key!` instead.'
311
352
  @_key = true
312
353
  @_key_for_collection = true
313
354
  end
@@ -323,6 +364,14 @@ module Alba
323
364
  @_meta = block
324
365
  end
325
366
 
367
+ # Set layout
368
+ #
369
+ # @params file [String] name of the layout file
370
+ # @params inline [Proc] a proc returning JSON string or a Hash representing JSON
371
+ def layout(file: nil, inline: nil)
372
+ @_layout = file || inline
373
+ end
374
+
326
375
  # Delete attributes
327
376
  # Use this DSL in child class to ignore certain attributes
328
377
  #
@@ -354,6 +403,13 @@ module Alba
354
403
  @_on_error = handler || block
355
404
  end
356
405
 
406
+ # Set nil handler
407
+ #
408
+ # @param block [Block]
409
+ def on_nil(&block)
410
+ @_on_nil = block
411
+ end
412
+
357
413
  # rubocop:enable Metrics/ParameterLists
358
414
  end
359
415
  end
data/lib/alba/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Alba
2
- VERSION = '1.4.0'.freeze
2
+ VERSION = '1.5.0'.freeze
3
3
  end
data/lib/alba.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  require 'json'
2
2
  require_relative 'alba/version'
3
3
  require_relative 'alba/resource'
4
+ require_relative 'alba/deprecation'
4
5
 
5
6
  # Core module
6
7
  module Alba
@@ -14,7 +15,7 @@ module Alba
14
15
  class UnsupportedType < Error; end
15
16
 
16
17
  class << self
17
- attr_reader :backend, :encoder, :inferring, :_on_error, :transforming_root_key
18
+ attr_reader :backend, :encoder, :inferring, :_on_error, :_on_nil, :transforming_root_key
18
19
 
19
20
  # Accessor for inflector, a module responsible for incflecting strings
20
21
  attr_accessor :inflector
@@ -51,7 +52,7 @@ module Alba
51
52
  # @return [String] serialized JSON string
52
53
  # @raise [ArgumentError] if block is absent or `with` argument's type is wrong
53
54
  def serialize(object, key: nil, root_key: nil, &block)
54
- warn '`key` option to `serialize` method is deprecated, use `root_key` instead.' if key
55
+ Alba::Deprecation.warn '`key` option to `serialize` method is deprecated, use `root_key` instead.' if key
55
56
  klass = block ? resource_class(&block) : infer_resource_class(object.class.name)
56
57
 
57
58
  resource = klass.new(object)
@@ -87,6 +88,14 @@ module Alba
87
88
  @_on_error = handler || block
88
89
  end
89
90
 
91
+ # Set nil handler
92
+ #
93
+ # @param block [Block]
94
+ # @return [void]
95
+ def on_nil(&block)
96
+ @_on_nil = block
97
+ end
98
+
90
99
  # Enable root key transformation
91
100
  def enable_root_key_transformation!
92
101
  @transforming_root_key = true
@@ -115,6 +124,15 @@ module Alba
115
124
  const_parent.const_get("#{ActiveSupport::Inflector.classify(name)}Resource")
116
125
  end
117
126
 
127
+ # Reset config variables
128
+ # Useful for test cleanup
129
+ def reset!
130
+ @encoder = default_encoder
131
+ @_on_error = :raise
132
+ @_on_nil = nil
133
+ @transforming_root_key = false # TODO: This will be true since 2.0
134
+ end
135
+
118
136
  private
119
137
 
120
138
  def set_encoder_from_backend
@@ -151,7 +169,5 @@ module Alba
151
169
  end
152
170
  end
153
171
 
154
- @encoder = default_encoder
155
- @_on_error = :raise
156
- @transforming_root_key = false # TODO: This will be true since 2.0
172
+ reset!
157
173
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: alba
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.0
4
+ version: 1.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - OKURA Masafumi
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-06-30 00:00:00.000000000 Z
11
+ date: 2021-11-28 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Alba is the fastest JSON serializer for Ruby. It focuses on performance,
14
14
  flexibility and usability.
@@ -39,12 +39,15 @@ files:
39
39
  - bin/console
40
40
  - bin/setup
41
41
  - codecov.yml
42
+ - docs/migrate_from_active_model_serializers.md
43
+ - docs/migrate_from_jbuilder.md
42
44
  - gemfiles/all.gemfile
43
45
  - gemfiles/without_active_support.gemfile
44
46
  - gemfiles/without_oj.gemfile
45
47
  - lib/alba.rb
46
48
  - lib/alba/association.rb
47
49
  - lib/alba/default_inflector.rb
50
+ - lib/alba/deprecation.rb
48
51
  - lib/alba/key_transform_factory.rb
49
52
  - lib/alba/many.rb
50
53
  - lib/alba/one.rb
@@ -75,7 +78,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
75
78
  - !ruby/object:Gem::Version
76
79
  version: '0'
77
80
  requirements: []
78
- rubygems_version: 3.2.16
81
+ rubygems_version: 3.2.22
79
82
  signing_key:
80
83
  specification_version: 4
81
84
  summary: Alba is the fastest JSON serializer for Ruby.