alba 1.3.0 → 1.6.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 +4 -4
- data/.github/workflows/codeql-analysis.yml +70 -0
- data/.github/workflows/main.yml +3 -1
- data/.github/workflows/perf.yml +21 -0
- data/.rubocop.yml +9 -7
- data/CHANGELOG.md +28 -0
- data/Gemfile +4 -3
- data/README.md +469 -60
- data/alba.gemspec +7 -3
- data/benchmark/collection.rb +108 -3
- data/benchmark/single_resource.rb +82 -1
- data/docs/migrate_from_active_model_serializers.md +359 -0
- data/docs/migrate_from_jbuilder.md +223 -0
- data/gemfiles/all.gemfile +2 -1
- data/gemfiles/without_active_support.gemfile +1 -1
- data/gemfiles/without_oj.gemfile +1 -1
- data/lib/alba/association.rb +33 -24
- data/lib/alba/default_inflector.rb +22 -4
- data/lib/alba/deprecation.rb +14 -0
- data/lib/alba/errors.rb +10 -0
- data/lib/alba/resource.rb +260 -100
- data/lib/alba/typed_attribute.rb +3 -6
- data/lib/alba/version.rb +1 -1
- data/lib/alba.rb +110 -36
- data/script/perf_check.rb +174 -0
- metadata +14 -8
- data/lib/alba/key_transform_factory.rb +0 -33
- data/lib/alba/many.rb +0 -21
- data/lib/alba/one.rb +0 -21
data/benchmark/collection.rb
CHANGED
@@ -15,9 +15,14 @@ gemfile(true) do
|
|
15
15
|
gem "benchmark-ips"
|
16
16
|
gem "benchmark-memory"
|
17
17
|
gem "blueprinter"
|
18
|
+
gem "fast_serializer_ruby"
|
18
19
|
gem "jbuilder"
|
20
|
+
gem 'turbostreamer'
|
21
|
+
gem "jserializer"
|
19
22
|
gem "jsonapi-serializer" # successor of fast_jsonapi
|
20
23
|
gem "multi_json"
|
24
|
+
gem "panko_serializer"
|
25
|
+
gem "pg"
|
21
26
|
gem "primalize"
|
22
27
|
gem "oj"
|
23
28
|
gem "representable"
|
@@ -27,7 +32,9 @@ end
|
|
27
32
|
|
28
33
|
# --- Test data model setup ---
|
29
34
|
|
35
|
+
require "pg"
|
30
36
|
require "active_record"
|
37
|
+
require "active_record/connection_adapters/postgresql_adapter"
|
31
38
|
require "logger"
|
32
39
|
require "oj"
|
33
40
|
require "sqlite3"
|
@@ -133,6 +140,27 @@ class PostBlueprint < Blueprinter::Base
|
|
133
140
|
end
|
134
141
|
end
|
135
142
|
|
143
|
+
# --- Fast Serializer Ruby
|
144
|
+
|
145
|
+
require "fast_serializer"
|
146
|
+
|
147
|
+
class FastSerializerCommentResource
|
148
|
+
include ::FastSerializer::Schema::Mixin
|
149
|
+
attributes :id, :body
|
150
|
+
end
|
151
|
+
|
152
|
+
class FastSerializerPostResource
|
153
|
+
include ::FastSerializer::Schema::Mixin
|
154
|
+
|
155
|
+
attributes :id, :body
|
156
|
+
|
157
|
+
attribute :commenter_names do
|
158
|
+
object.commenters.pluck(:name)
|
159
|
+
end
|
160
|
+
|
161
|
+
has_many :comments, serializer: FastSerializerCommentResource
|
162
|
+
end
|
163
|
+
|
136
164
|
# --- JBuilder serializers ---
|
137
165
|
|
138
166
|
require "jbuilder"
|
@@ -157,6 +185,22 @@ class Comment
|
|
157
185
|
end
|
158
186
|
end
|
159
187
|
|
188
|
+
# --- Jserializer serializers ---
|
189
|
+
|
190
|
+
require 'jserializer'
|
191
|
+
|
192
|
+
class JserializerCommentSerializer < Jserializer::Base
|
193
|
+
attributes :id, :body
|
194
|
+
end
|
195
|
+
|
196
|
+
class JserializerPostSerializer < Jserializer::Base
|
197
|
+
attributes :id, :body, :commenter_names
|
198
|
+
has_many :comments, serializer: JserializerCommentSerializer
|
199
|
+
def commenter_names
|
200
|
+
object.commenters.pluck(:name)
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
160
204
|
# --- JSONAPI:Serializer serializers / (successor of fast_jsonapi) ---
|
161
205
|
|
162
206
|
class JsonApiStandardCommentSerializer
|
@@ -218,6 +262,26 @@ class JsonApiSameFormatPostSerializer < JsonApiSameFormatSerializer
|
|
218
262
|
end
|
219
263
|
end
|
220
264
|
|
265
|
+
# --- Panko serializers ---
|
266
|
+
#
|
267
|
+
|
268
|
+
require "panko_serializer"
|
269
|
+
|
270
|
+
class PankoCommentSerializer < Panko::Serializer
|
271
|
+
attributes :id, :body
|
272
|
+
end
|
273
|
+
|
274
|
+
|
275
|
+
class PankoPostSerializer < Panko::Serializer
|
276
|
+
attributes :id, :body, :commenter_names
|
277
|
+
|
278
|
+
has_many :comments, serializer: PankoCommentSerializer
|
279
|
+
|
280
|
+
def commenter_names
|
281
|
+
object.commenters.pluck(:name)
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
221
285
|
# --- Primalize serializers ---
|
222
286
|
#
|
223
287
|
class PrimalizeCommentResource < Primalize::Single
|
@@ -291,6 +355,31 @@ class SimpleAMSPostSerializer
|
|
291
355
|
end
|
292
356
|
end
|
293
357
|
|
358
|
+
require 'turbostreamer'
|
359
|
+
TurboStreamer.set_default_encoder(:json, :oj)
|
360
|
+
|
361
|
+
class TurbostreamerSerializer
|
362
|
+
def initialize(posts)
|
363
|
+
@posts = posts
|
364
|
+
end
|
365
|
+
|
366
|
+
def to_json
|
367
|
+
TurboStreamer.encode do |json|
|
368
|
+
json.array! @posts do |post|
|
369
|
+
json.object! do
|
370
|
+
json.extract! post, :id, :body, :commenter_names
|
371
|
+
|
372
|
+
json.comments post.comments do |comment|
|
373
|
+
json.object! do
|
374
|
+
json.extract! comment, :id, :body
|
375
|
+
end
|
376
|
+
end
|
377
|
+
end
|
378
|
+
end
|
379
|
+
end
|
380
|
+
end
|
381
|
+
end
|
382
|
+
|
294
383
|
# --- Test data creation ---
|
295
384
|
|
296
385
|
100.times do |i|
|
@@ -303,7 +392,7 @@ end
|
|
303
392
|
end
|
304
393
|
end
|
305
394
|
|
306
|
-
posts = Post.all.
|
395
|
+
posts = Post.all.includes(:comments, :commenters)
|
307
396
|
|
308
397
|
# --- Store the serializers in procs ---
|
309
398
|
|
@@ -319,8 +408,9 @@ alba_inline = Proc.new do
|
|
319
408
|
end
|
320
409
|
end
|
321
410
|
end
|
322
|
-
ams = Proc.new { ActiveModelSerializers::SerializableResource.new(posts, {}).
|
411
|
+
ams = Proc.new { ActiveModelSerializers::SerializableResource.new(posts, {each_serializer: AMSPostSerializer}).to_json }
|
323
412
|
blueprinter = Proc.new { PostBlueprint.render(posts) }
|
413
|
+
fast_serializer = Proc.new { FastSerializerPostResource.new(posts).to_json }
|
324
414
|
jbuilder = Proc.new do
|
325
415
|
Jbuilder.new do |json|
|
326
416
|
json.array!(posts) do |post|
|
@@ -328,30 +418,37 @@ jbuilder = Proc.new do
|
|
328
418
|
end
|
329
419
|
end.target!
|
330
420
|
end
|
421
|
+
jserializer = Proc.new { JserializerPostSerializer.new(posts, is_collection: true).to_json }
|
331
422
|
jsonapi = proc { JsonApiStandardPostSerializer.new(posts).to_json }
|
332
423
|
jsonapi_same_format = proc { JsonApiSameFormatPostSerializer.new(posts).to_json }
|
424
|
+
panko = proc { Panko::ArraySerializer.new(posts, each_serializer: PankoPostSerializer).to_json }
|
333
425
|
primalize = proc { PrimalizePostsResource.new(posts: posts).to_json }
|
334
426
|
rails = Proc.new do
|
335
427
|
ActiveSupport::JSON.encode(posts.map{ |post| post.serializable_hash(include: :comments) })
|
336
428
|
end
|
337
429
|
representable = Proc.new { PostsRepresenter.new(posts).to_json }
|
338
430
|
simple_ams = Proc.new { SimpleAMS::Renderer::Collection.new(posts, serializer: SimpleAMSPostSerializer).to_json }
|
431
|
+
turbostreamer = Proc.new { TurbostreamerSerializer.new(posts).to_json }
|
339
432
|
|
340
433
|
# --- Execute the serializers to check their output ---
|
341
|
-
|
434
|
+
GC.disable
|
342
435
|
puts "Serializer outputs ----------------------------------"
|
343
436
|
{
|
344
437
|
alba: alba,
|
345
438
|
alba_inline: alba_inline,
|
346
439
|
ams: ams,
|
347
440
|
blueprinter: blueprinter,
|
441
|
+
fast_serializer: fast_serializer,
|
348
442
|
jbuilder: jbuilder, # different order
|
443
|
+
jserializer: jserializer,
|
349
444
|
jsonapi: jsonapi, # nested JSON:API format
|
350
445
|
jsonapi_same_format: jsonapi_same_format,
|
446
|
+
panko: panko,
|
351
447
|
primalize: primalize,
|
352
448
|
rails: rails,
|
353
449
|
representable: representable,
|
354
450
|
simple_ams: simple_ams,
|
451
|
+
turbostreamer: turbostreamer
|
355
452
|
}.each { |name, serializer| puts "#{name.to_s.ljust(24, ' ')} #{serializer.call}" }
|
356
453
|
|
357
454
|
# --- Run the benchmarks ---
|
@@ -362,13 +459,17 @@ Benchmark.ips do |x|
|
|
362
459
|
x.report(:alba_inline, &alba_inline)
|
363
460
|
x.report(:ams, &ams)
|
364
461
|
x.report(:blueprinter, &blueprinter)
|
462
|
+
x.report(:fast_serializer, &fast_serializer)
|
365
463
|
x.report(:jbuilder, &jbuilder)
|
464
|
+
x.report(:jserializer, &jserializer)
|
366
465
|
x.report(:jsonapi, &jsonapi)
|
367
466
|
x.report(:jsonapi_same_format, &jsonapi_same_format)
|
467
|
+
x.report(:panko, &panko)
|
368
468
|
x.report(:primalize, &primalize)
|
369
469
|
x.report(:rails, &rails)
|
370
470
|
x.report(:representable, &representable)
|
371
471
|
x.report(:simple_ams, &simple_ams)
|
472
|
+
x.report(:turbostreamer, &turbostreamer)
|
372
473
|
|
373
474
|
x.compare!
|
374
475
|
end
|
@@ -380,13 +481,17 @@ Benchmark.memory do |x|
|
|
380
481
|
x.report(:alba_inline, &alba_inline)
|
381
482
|
x.report(:ams, &ams)
|
382
483
|
x.report(:blueprinter, &blueprinter)
|
484
|
+
x.report(:fast_serializer, &fast_serializer)
|
383
485
|
x.report(:jbuilder, &jbuilder)
|
486
|
+
x.report(:jserializer, &jserializer)
|
384
487
|
x.report(:jsonapi, &jsonapi)
|
385
488
|
x.report(:jsonapi_same_format, &jsonapi_same_format)
|
489
|
+
x.report(:panko, &panko)
|
386
490
|
x.report(:primalize, &primalize)
|
387
491
|
x.report(:rails, &rails)
|
388
492
|
x.report(:representable, &representable)
|
389
493
|
x.report(:simple_ams, &simple_ams)
|
494
|
+
x.report(:turbostreamer, &turbostreamer)
|
390
495
|
|
391
496
|
x.compare!
|
392
497
|
end
|
@@ -13,10 +13,15 @@ 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 'turbostreamer'
|
20
|
+
gem "jserializer"
|
18
21
|
gem "jsonapi-serializer" # successor of fast_jsonapi
|
19
22
|
gem "multi_json"
|
23
|
+
gem "panko_serializer"
|
24
|
+
gem "pg"
|
20
25
|
gem "primalize"
|
21
26
|
gem "oj"
|
22
27
|
gem "representable"
|
@@ -26,7 +31,9 @@ end
|
|
26
31
|
|
27
32
|
# --- Test data model setup ---
|
28
33
|
|
34
|
+
require "pg"
|
29
35
|
require "active_record"
|
36
|
+
require "active_record/connection_adapters/postgresql_adapter"
|
30
37
|
require "logger"
|
31
38
|
require "oj"
|
32
39
|
require "sqlite3"
|
@@ -154,6 +161,23 @@ class Comment
|
|
154
161
|
end
|
155
162
|
end
|
156
163
|
|
164
|
+
# --- Jserializer serializers ---
|
165
|
+
|
166
|
+
require 'jserializer'
|
167
|
+
|
168
|
+
class JserializerCommentSerializer < Jserializer::Base
|
169
|
+
attributes :id, :body
|
170
|
+
end
|
171
|
+
|
172
|
+
class JserializerPostSerializer < Jserializer::Base
|
173
|
+
attributes :id, :body, :commenter_names
|
174
|
+
has_many :comments, serializer: JserializerCommentSerializer
|
175
|
+
def commenter_names
|
176
|
+
object.commenters.pluck(:name)
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
|
157
181
|
# --- JSONAPI:Serializer serializers / (successor of fast_jsonapi) ---
|
158
182
|
|
159
183
|
class JsonApiStandardCommentSerializer
|
@@ -215,6 +239,25 @@ class JsonApiSameFormatPostSerializer < JsonApiSameFormatSerializer
|
|
215
239
|
end
|
216
240
|
end
|
217
241
|
|
242
|
+
# --- Panko serializers ---
|
243
|
+
#
|
244
|
+
require "panko_serializer"
|
245
|
+
|
246
|
+
class PankoCommentSerializer < Panko::Serializer
|
247
|
+
attributes :id, :body
|
248
|
+
end
|
249
|
+
|
250
|
+
|
251
|
+
class PankoPostSerializer < Panko::Serializer
|
252
|
+
attributes :id, :body, :commenter_names
|
253
|
+
|
254
|
+
has_many :comments, serializer: PankoCommentSerializer
|
255
|
+
|
256
|
+
def commenter_names
|
257
|
+
object.commenters.pluck(:name)
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
218
261
|
# --- Primalize serializers ---
|
219
262
|
#
|
220
263
|
class PrimalizeCommentResource < Primalize::Single
|
@@ -282,6 +325,29 @@ class SimpleAMSPostSerializer
|
|
282
325
|
end
|
283
326
|
end
|
284
327
|
|
328
|
+
require 'turbostreamer'
|
329
|
+
TurboStreamer.set_default_encoder(:json, :oj)
|
330
|
+
|
331
|
+
class TurbostreamerSerializer
|
332
|
+
def initialize(post)
|
333
|
+
@post = post
|
334
|
+
end
|
335
|
+
|
336
|
+
def to_json
|
337
|
+
TurboStreamer.encode do |json|
|
338
|
+
json.object! do
|
339
|
+
json.extract! @post, :id, :body, :commenter_names
|
340
|
+
|
341
|
+
json.comments @post.comments do |comment|
|
342
|
+
json.object! do
|
343
|
+
json.extract! comment, :id, :body
|
344
|
+
end
|
345
|
+
end
|
346
|
+
end
|
347
|
+
end
|
348
|
+
end
|
349
|
+
end
|
350
|
+
|
285
351
|
# --- Test data creation ---
|
286
352
|
|
287
353
|
post = Post.create!(body: 'post')
|
@@ -305,15 +371,19 @@ alba_inline = Proc.new do
|
|
305
371
|
end
|
306
372
|
end
|
307
373
|
end
|
374
|
+
|
308
375
|
ams = Proc.new { AMSPostSerializer.new(post, {}).to_json }
|
309
376
|
blueprinter = Proc.new { PostBlueprint.render(post) }
|
310
377
|
jbuilder = Proc.new { post.to_builder.target! }
|
378
|
+
jserializer = Proc.new { JserializerPostSerializer.new(post).to_json }
|
311
379
|
jsonapi = proc { JsonApiStandardPostSerializer.new(post).to_json }
|
312
380
|
jsonapi_same_format = proc { JsonApiSameFormatPostSerializer.new(post).to_json }
|
381
|
+
panko = proc { PankoPostSerializer.new.serialize_to_json(post) }
|
313
382
|
primalize = proc { PrimalizePostResource.new(post).to_json }
|
314
383
|
rails = Proc.new { ActiveSupport::JSON.encode(post.serializable_hash(include: :comments)) }
|
315
384
|
representable = Proc.new { PostRepresenter.new(post).to_json }
|
316
385
|
simple_ams = Proc.new { SimpleAMS::Renderer.new(post, serializer: SimpleAMSPostSerializer).to_json }
|
386
|
+
turbostreamer = Proc.new { TurbostreamerSerializer.new(post).to_json }
|
317
387
|
|
318
388
|
# --- Execute the serializers to check their output ---
|
319
389
|
|
@@ -324,13 +394,18 @@ puts "Serializer outputs ----------------------------------"
|
|
324
394
|
ams: ams,
|
325
395
|
blueprinter: blueprinter,
|
326
396
|
jbuilder: jbuilder, # different order
|
397
|
+
jserializer: jserializer,
|
327
398
|
jsonapi: jsonapi, # nested JSON:API format
|
328
399
|
jsonapi_same_format: jsonapi_same_format,
|
400
|
+
panko: panko,
|
329
401
|
primalize: primalize,
|
330
402
|
rails: rails,
|
331
403
|
representable: representable,
|
332
404
|
simple_ams: simple_ams,
|
333
|
-
|
405
|
+
turbostreamer: turbostreamer
|
406
|
+
}.each do |name, serializer|
|
407
|
+
puts "#{name.to_s.ljust(24, ' ')} #{serializer.call}"
|
408
|
+
end
|
334
409
|
|
335
410
|
# --- Run the benchmarks ---
|
336
411
|
|
@@ -341,12 +416,15 @@ Benchmark.ips do |x|
|
|
341
416
|
x.report(:ams, &ams)
|
342
417
|
x.report(:blueprinter, &blueprinter)
|
343
418
|
x.report(:jbuilder, &jbuilder)
|
419
|
+
x.report(:jserializer, &jserializer)
|
344
420
|
x.report(:jsonapi, &jsonapi)
|
345
421
|
x.report(:jsonapi_same_format, &jsonapi_same_format)
|
422
|
+
x.report(:panko, &panko)
|
346
423
|
x.report(:primalize, &primalize)
|
347
424
|
x.report(:rails, &rails)
|
348
425
|
x.report(:representable, &representable)
|
349
426
|
x.report(:simple_ams, &simple_ams)
|
427
|
+
x.report(:turbostreamer, &turbostreamer)
|
350
428
|
|
351
429
|
x.compare!
|
352
430
|
end
|
@@ -359,12 +437,15 @@ Benchmark.memory do |x|
|
|
359
437
|
x.report(:ams, &ams)
|
360
438
|
x.report(:blueprinter, &blueprinter)
|
361
439
|
x.report(:jbuilder, &jbuilder)
|
440
|
+
x.report(:jserializer, &jserializer)
|
362
441
|
x.report(:jsonapi, &jsonapi)
|
363
442
|
x.report(:jsonapi_same_format, &jsonapi_same_format)
|
443
|
+
x.report(:panko, &panko)
|
364
444
|
x.report(:primalize, &primalize)
|
365
445
|
x.report(:rails, &rails)
|
366
446
|
x.report(:representable, &representable)
|
367
447
|
x.report(:simple_ams, &simple_ams)
|
448
|
+
x.report(:turbostreamer, &turbostreamer)
|
368
449
|
|
369
450
|
x.compare!
|
370
451
|
end
|