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 +4 -4
- data/CHANGELOG.md +9 -0
- data/Gemfile +2 -2
- data/README.md +158 -5
- data/benchmark/collection.rb +49 -0
- data/benchmark/single_resource.rb +50 -0
- data/docs/migrate_from_active_model_serializers.md +359 -0
- data/docs/migrate_from_jbuilder.md +223 -0
- data/lib/alba/deprecation.rb +14 -0
- data/lib/alba/resource.rb +73 -17
- data/lib/alba/version.rb +1 -1
- data/lib/alba.rb +21 -5
- metadata +6 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 34c441cbc87f959f73c3e7a0175309f780b23ce840add4d53cf5083488390f1f
|
4
|
+
data.tar.gz: f78fb2eea40f502c6f7b3c905317616054bd397d30aa9ce979285003205c81af
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
gem 'rubocop-performance', '~> 1.
|
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
|
-
|
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,
|
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
|
-
* `
|
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
|
-
|
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).
|
data/benchmark/collection.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
134
|
-
attr = 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
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
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
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
|
-
|
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
|
+
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-
|
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.
|
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.
|