alba 1.3.0 → 1.6.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|