alba 1.4.0 → 1.5.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: 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.