panko_serializer 0.8.3 → 0.8.5
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/README.md +4 -4
- data/ext/panko_serializer/attributes_writer/active_record.c +2 -29
- data/ext/panko_serializer/attributes_writer/type_cast/type_cast.c +24 -13
- data/lib/panko/version.rb +1 -1
- metadata +3 -72
- data/.clang-format +0 -3
- data/.github/dependabot.yml +0 -6
- data/.github/workflows/docs.yml +0 -39
- data/.github/workflows/lint.yml +0 -30
- data/.github/workflows/ruby.yml +0 -51
- data/.gitignore +0 -20
- data/.rspec +0 -2
- data/.rubocop.yml +0 -37
- data/Appraisals +0 -29
- data/Gemfile +0 -36
- data/Rakefile +0 -93
- data/benchmarks/BENCHMARKS.md +0 -48
- data/benchmarks/allocs.rb +0 -23
- data/benchmarks/app.rb +0 -11
- data/benchmarks/benchmarking_support.rb +0 -44
- data/benchmarks/bm_ams_0_10.rb +0 -43
- data/benchmarks/bm_object_writer.rb +0 -65
- data/benchmarks/bm_panko_json.rb +0 -60
- data/benchmarks/bm_panko_object.rb +0 -46
- data/benchmarks/bm_plain_object.rb +0 -99
- data/benchmarks/bm_serialization_descriptor.rb +0 -76
- data/benchmarks/bm_serializer_resolver.rb +0 -22
- data/benchmarks/bm_to_object.rb +0 -81
- data/benchmarks/profile.rb +0 -86
- data/benchmarks/sanity.rb +0 -83
- data/benchmarks/setup.rb +0 -56
- data/benchmarks/type_casts/bm_active_record.rb +0 -58
- data/benchmarks/type_casts/bm_panko.rb +0 -65
- data/benchmarks/type_casts/support.rb +0 -27
- data/docs/docs/associations.md +0 -107
- data/docs/docs/attributes.md +0 -143
- data/docs/docs/design-choices.md +0 -127
- data/docs/docs/getting-started.md +0 -70
- data/docs/docs/introduction.md +0 -13
- data/docs/docs/performance.md +0 -32
- data/docs/docs/response-bag.md +0 -83
- data/docs/docusaurus.config.js +0 -86
- data/docs/package-lock.json +0 -15994
- data/docs/package.json +0 -21
- data/docs/sidebars.json +0 -15
- data/docs/src/css/customTheme.css +0 -9
- data/docs/static/.DS_Store +0 -0
- data/docs/static/CNAME +0 -1
- data/docs/static/css/custom.css +0 -51
- data/docs/static/img/favicon.ico +0 -0
- data/docs/static/img/oss_logo.png +0 -0
- data/docs/static/img/undraw_code_review.svg +0 -1
- data/docs/static/img/undraw_monitor.svg +0 -1
- data/docs/static/img/undraw_note_list.svg +0 -1
- data/docs/static/img/undraw_online.svg +0 -1
- data/docs/static/img/undraw_open_source.svg +0 -1
- data/docs/static/img/undraw_operating_system.svg +0 -1
- data/docs/static/img/undraw_react.svg +0 -1
- data/docs/static/img/undraw_tweetstorm.svg +0 -1
- data/docs/static/img/undraw_youtube_tutorial.svg +0 -1
- data/docs/static/index.html +0 -14
- data/gemfiles/7.0.0.gemfile +0 -39
- data/gemfiles/7.0.0.gemfile.lock +0 -176
- data/gemfiles/7.1.0.gemfile +0 -39
- data/gemfiles/7.1.0.gemfile.lock +0 -198
- data/gemfiles/7.2.0.gemfile +0 -39
- data/gemfiles/7.2.0.gemfile.lock +0 -198
- data/gemfiles/8.0.0.gemfile +0 -39
- data/gemfiles/8.0.0.gemfile.lock +0 -219
- data/panko_serializer.gemspec +0 -36
data/benchmarks/profile.rb
DELETED
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative "benchmarking_support"
|
|
4
|
-
require_relative "app"
|
|
5
|
-
require_relative "setup"
|
|
6
|
-
|
|
7
|
-
require "memory_profiler"
|
|
8
|
-
|
|
9
|
-
class NullLogger < Logger
|
|
10
|
-
def initialize(*args)
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
def add(*args, &block)
|
|
14
|
-
end
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
class BenchmarkApp < Rails::Application
|
|
18
|
-
routes.append do
|
|
19
|
-
get "/simple" => "main#simple"
|
|
20
|
-
get "/text" => "main#text"
|
|
21
|
-
|
|
22
|
-
get "/serialize_to_string" => "main#serialize_to_string"
|
|
23
|
-
get "/serialize_to_stream" => "streaming#serialize_to_stream"
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
config.secret_token = "s" * 30
|
|
27
|
-
config.secret_key_base = "foo"
|
|
28
|
-
config.consider_all_requests_local = false
|
|
29
|
-
|
|
30
|
-
# simulate production
|
|
31
|
-
config.cache_classes = true
|
|
32
|
-
config.eager_load = true
|
|
33
|
-
config.action_controller.perform_caching = true
|
|
34
|
-
|
|
35
|
-
# otherwise deadlock occured
|
|
36
|
-
config.middleware.delete "Rack::Lock"
|
|
37
|
-
|
|
38
|
-
# to disable log files
|
|
39
|
-
config.logger = NullLogger.new
|
|
40
|
-
config.active_support.deprecation = :log
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
BenchmarkApp.initialize!
|
|
44
|
-
|
|
45
|
-
class AuthorFastSerializer < Panko::Serializer
|
|
46
|
-
attributes :id, :name
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
class PostWithHasOneFastSerializer < Panko::Serializer
|
|
50
|
-
attributes :id, :body, :title, :author_id
|
|
51
|
-
|
|
52
|
-
has_one :author, serializer: AuthorFastSerializer
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
class StreamingController < ActionController::Base
|
|
56
|
-
include ActionController::Live
|
|
57
|
-
|
|
58
|
-
def serialize_to_stream
|
|
59
|
-
headers["Content-Type"] = "application/json"
|
|
60
|
-
|
|
61
|
-
data = Benchmark.data[:all]
|
|
62
|
-
serializer = Panko::ArraySerializer.new([], each_serializer: PostWithHasOneFastSerializer)
|
|
63
|
-
writer = Oj::StreamWriter.new(response.stream, mode: :rails)
|
|
64
|
-
|
|
65
|
-
serializer.serialize_to_writer(data, writer)
|
|
66
|
-
|
|
67
|
-
response.stream.close
|
|
68
|
-
end
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
class RouteNotFoundError < StandardError; end
|
|
72
|
-
|
|
73
|
-
def request(method, path)
|
|
74
|
-
response = Rack::MockRequest.new(BenchmarkApp).send(method, path)
|
|
75
|
-
if response.status.in?([404, 500])
|
|
76
|
-
raise RouteNotFoundError.new, "not found #{method.to_s.upcase} #{path}"
|
|
77
|
-
end
|
|
78
|
-
response
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
def memory(&)
|
|
82
|
-
mem = MemoryProfiler.report(&)
|
|
83
|
-
mem.pretty_print
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
memory { request(:get, "/serialize_to_stream") }
|
data/benchmarks/sanity.rb
DELETED
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative "benchmarking_support"
|
|
4
|
-
require_relative "app"
|
|
5
|
-
require_relative "setup"
|
|
6
|
-
|
|
7
|
-
class PostWithAliasModel < ActiveRecord::Base
|
|
8
|
-
self.table_name = "posts"
|
|
9
|
-
|
|
10
|
-
alias_attribute :new_id, :id
|
|
11
|
-
alias_attribute :new_body, :body
|
|
12
|
-
alias_attribute :new_title, :title
|
|
13
|
-
alias_attribute :new_author_id, :author_id
|
|
14
|
-
alias_attribute :new_created_at, :created_at
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
class PostWithAliasFastSerializer < Panko::Serializer
|
|
18
|
-
attributes :new_id, :new_body, :new_title, :new_author_id, :new_created_at
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
def benchmark_aliased(prefix, serializer, options = {})
|
|
22
|
-
posts = PostWithAliasModel.all.to_a
|
|
23
|
-
posts_50 = posts.first(50).to_a
|
|
24
|
-
|
|
25
|
-
merged_options = options.merge(each_serializer: serializer)
|
|
26
|
-
|
|
27
|
-
Benchmark.run("Panko_#{prefix}_PostWithAliasModels_#{posts.count}") do
|
|
28
|
-
Panko::ArraySerializer.new(posts, merged_options).to_json
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
Benchmark.run("Panko_#{prefix}_Posts_50") do
|
|
32
|
-
Panko::ArraySerializer.new(posts_50, merged_options).to_json
|
|
33
|
-
end
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
class AuthorFastSerializer < Panko::Serializer
|
|
37
|
-
attributes :id, :name
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
class PostFastSerializer < Panko::Serializer
|
|
41
|
-
attributes :id, :body, :title, :author_id, :created_at
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
class PostFastWithMethodCallSerializer < Panko::Serializer
|
|
45
|
-
attributes :id, :body, :title, :author_id, :created_at, :method_call
|
|
46
|
-
|
|
47
|
-
def method_call
|
|
48
|
-
object.id
|
|
49
|
-
end
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
class PostWithHasOneFastSerializer < Panko::Serializer
|
|
53
|
-
attributes :id, :body, :title, :author_id, :created_at
|
|
54
|
-
|
|
55
|
-
has_one :author, serializer: AuthorFastSerializer
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
class AuthorWithHasManyFastSerializer < Panko::Serializer
|
|
59
|
-
attributes :id, :name
|
|
60
|
-
|
|
61
|
-
has_many :posts, serializer: PostFastSerializer
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
def benchmark(prefix, serializer, options = {})
|
|
65
|
-
posts = Benchmark.data[:all]
|
|
66
|
-
|
|
67
|
-
merged_options = options.merge(each_serializer: serializer)
|
|
68
|
-
|
|
69
|
-
Benchmark.run("Panko_#{prefix}_Posts_#{posts.count}") do
|
|
70
|
-
Panko::ArraySerializer.new(posts, merged_options).to_json
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
posts_50 = Benchmark.data[:small]
|
|
74
|
-
|
|
75
|
-
Benchmark.run("Panko_#{prefix}_Posts_50") do
|
|
76
|
-
Panko::ArraySerializer.new(posts_50, merged_options).to_json
|
|
77
|
-
end
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
benchmark "Simple", PostFastSerializer
|
|
81
|
-
benchmark "HasOne", PostWithHasOneFastSerializer
|
|
82
|
-
benchmark "SimpleWithMethodCall", PostFastWithMethodCallSerializer
|
|
83
|
-
benchmark_aliased "Simple (aliased)", PostWithAliasFastSerializer
|
data/benchmarks/setup.rb
DELETED
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
###########################################
|
|
4
|
-
# Setup active record models
|
|
5
|
-
##########################################
|
|
6
|
-
require "active_record"
|
|
7
|
-
require "sqlite3"
|
|
8
|
-
require "securerandom"
|
|
9
|
-
|
|
10
|
-
# Change the following to reflect your database settings
|
|
11
|
-
ActiveRecord::Base.establish_connection(
|
|
12
|
-
adapter: "sqlite3",
|
|
13
|
-
database: ":memory:"
|
|
14
|
-
)
|
|
15
|
-
|
|
16
|
-
# Don't show migration output when constructing fake db
|
|
17
|
-
ActiveRecord::Migration.verbose = false
|
|
18
|
-
|
|
19
|
-
ActiveRecord::Schema.define do
|
|
20
|
-
create_table :authors, force: true do |t|
|
|
21
|
-
t.string :name
|
|
22
|
-
t.timestamps(null: false)
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
create_table :posts, force: true do |t|
|
|
26
|
-
t.text :body
|
|
27
|
-
t.string :title
|
|
28
|
-
t.references :author
|
|
29
|
-
t.json :data
|
|
30
|
-
t.timestamps(null: false)
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
class Author < ActiveRecord::Base
|
|
35
|
-
has_one :profile
|
|
36
|
-
has_many :posts
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
class Post < ActiveRecord::Base
|
|
40
|
-
belongs_to :author
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
Post.destroy_all
|
|
44
|
-
Author.destroy_all
|
|
45
|
-
|
|
46
|
-
# Build out the data to serialize
|
|
47
|
-
Post.transaction do
|
|
48
|
-
ENV.fetch("ITEMS_COUNT", "2300").to_i.times do
|
|
49
|
-
Post.create(
|
|
50
|
-
body: SecureRandom.hex(30),
|
|
51
|
-
title: SecureRandom.hex(20),
|
|
52
|
-
author: Author.create(name: SecureRandom.alphanumeric),
|
|
53
|
-
data: {a: 1, b: 2, c: 3}
|
|
54
|
-
)
|
|
55
|
-
end
|
|
56
|
-
end
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative "support"
|
|
4
|
-
|
|
5
|
-
def ar_type_convert(type_klass, from, to)
|
|
6
|
-
converter = type_klass.new
|
|
7
|
-
|
|
8
|
-
assert type_klass.name, converter.deserialize(from), to
|
|
9
|
-
|
|
10
|
-
Benchmark.run("#{type_klass.name}_TypeCast") do
|
|
11
|
-
converter.deserialize(from)
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
Benchmark.run("#{type_klass.name}_NoTypeCast") do
|
|
15
|
-
converter.deserialize(to)
|
|
16
|
-
end
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
def utc_ar_time
|
|
20
|
-
date = DateTime.new(2017, 3, 4, 12, 45, 23)
|
|
21
|
-
tz = ActiveSupport::TimeZone.new("UTC")
|
|
22
|
-
from = date.in_time_zone(tz).iso8601
|
|
23
|
-
|
|
24
|
-
type = ActiveRecord::ConnectionAdapters::PostgreSQL::OID::DateTime.new
|
|
25
|
-
converter = ActiveRecord::AttributeMethods::TimeZoneConversion::TimeZoneConverter.new(type)
|
|
26
|
-
|
|
27
|
-
Benchmark.run("#{tz}_#{type.class.name}_TypeCast") do
|
|
28
|
-
converter.deserialize(from).iso8601
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def db_ar_time
|
|
33
|
-
type = ActiveRecord::ConnectionAdapters::PostgreSQL::OID::DateTime.new
|
|
34
|
-
converter = ActiveRecord::AttributeMethods::TimeZoneConversion::TimeZoneConverter.new(type)
|
|
35
|
-
|
|
36
|
-
from = "2017-07-10 09:26:40.937392"
|
|
37
|
-
|
|
38
|
-
Benchmark.run("ActiveRecord_Time_TypeCast_WithISO8601") do
|
|
39
|
-
converter.deserialize(from).iso8601
|
|
40
|
-
end
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
ar_type_convert ActiveRecord::Type::String, 1, "1"
|
|
44
|
-
ar_type_convert ActiveRecord::Type::Text, 1, "1"
|
|
45
|
-
ar_type_convert ActiveRecord::Type::Integer, "1", 1
|
|
46
|
-
ar_type_convert ActiveRecord::Type::Float, "1.23", 1.23
|
|
47
|
-
ar_type_convert ActiveRecord::Type::Boolean, "true", true
|
|
48
|
-
ar_type_convert ActiveRecord::Type::Boolean, "t", true
|
|
49
|
-
|
|
50
|
-
if check_if_exists "ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Json"
|
|
51
|
-
ar_type_convert ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Json, '{"a":1}', {a: 1}
|
|
52
|
-
end
|
|
53
|
-
if check_if_exists "ActiveRecord::Type::Json"
|
|
54
|
-
ar_type_convert ActiveRecord::Type::Json, '{"a":1}', {a: 1}
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
db_ar_time
|
|
58
|
-
utc_ar_time
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative "support"
|
|
4
|
-
|
|
5
|
-
def panko_type_convert(type_klass, from, to)
|
|
6
|
-
converter = type_klass.new
|
|
7
|
-
assert type_klass.name.to_s, Panko._type_cast(converter, from), to
|
|
8
|
-
|
|
9
|
-
Benchmark.run("#{type_klass.name}_TypeCast") do
|
|
10
|
-
Panko._type_cast(converter, from)
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
Benchmark.run("#{type_klass.name}_NoTypeCast") do
|
|
14
|
-
Panko._type_cast(converter, to)
|
|
15
|
-
end
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
def utc_panko_time
|
|
19
|
-
date = DateTime.new(2017, 3, 4, 12, 45, 23)
|
|
20
|
-
tz = ActiveSupport::TimeZone.new("UTC")
|
|
21
|
-
from = date.in_time_zone(tz).iso8601
|
|
22
|
-
|
|
23
|
-
type = ActiveRecord::ConnectionAdapters::PostgreSQL::OID::DateTime.new
|
|
24
|
-
converter = ActiveRecord::AttributeMethods::TimeZoneConversion::TimeZoneConverter.new(type)
|
|
25
|
-
|
|
26
|
-
to = Panko._type_cast(converter, from)
|
|
27
|
-
|
|
28
|
-
Benchmark.run("#{tz}_#{type.class.name}_TypeCast") do
|
|
29
|
-
Panko._type_cast(converter, from)
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
Benchmark.run("#{tz}_#{type.class.name}_NoTypeCast") do
|
|
33
|
-
Panko._type_cast(converter, to)
|
|
34
|
-
end
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
def db_panko_time
|
|
38
|
-
type = ActiveRecord::ConnectionAdapters::PostgreSQL::OID::DateTime.new
|
|
39
|
-
converter = ActiveRecord::AttributeMethods::TimeZoneConversion::TimeZoneConverter.new(type)
|
|
40
|
-
|
|
41
|
-
from = "2017-07-10 09:26:40.937392"
|
|
42
|
-
|
|
43
|
-
Benchmark.run("Panko_Time_TypeCast") do
|
|
44
|
-
Panko._type_cast(converter, from)
|
|
45
|
-
end
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
panko_type_convert ActiveRecord::Type::String, 1, "1"
|
|
49
|
-
panko_type_convert ActiveRecord::Type::Text, 1, "1"
|
|
50
|
-
panko_type_convert ActiveRecord::Type::Integer, "1", 1
|
|
51
|
-
panko_type_convert ActiveRecord::Type::Float, "1.23", 1.23
|
|
52
|
-
panko_type_convert ActiveRecord::Type::Boolean, "true", true
|
|
53
|
-
panko_type_convert ActiveRecord::Type::Boolean, "t", true
|
|
54
|
-
|
|
55
|
-
if check_if_exists "ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Json"
|
|
56
|
-
panko_type_convert ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Json, '{"a":1}', '{"a":1}'
|
|
57
|
-
end
|
|
58
|
-
if check_if_exists "ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Jsonb"
|
|
59
|
-
panko_type_convert ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Jsonb, '{"a":1}', '{"a":1}'
|
|
60
|
-
end
|
|
61
|
-
if check_if_exists "ActiveRecord::Type::Json"
|
|
62
|
-
panko_type_convert ActiveRecord::Type::Json, '{"a":1}', '{"a":1}'
|
|
63
|
-
end
|
|
64
|
-
db_panko_time
|
|
65
|
-
utc_panko_time
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "active_record"
|
|
4
|
-
require "active_record/connection_adapters/postgresql_adapter"
|
|
5
|
-
require "active_model"
|
|
6
|
-
require "active_support/all"
|
|
7
|
-
require "pg"
|
|
8
|
-
require "oj"
|
|
9
|
-
|
|
10
|
-
require_relative "../benchmarking_support"
|
|
11
|
-
require_relative "../../lib/panko_serializer"
|
|
12
|
-
|
|
13
|
-
def assert(type_name, from, to)
|
|
14
|
-
raise "#{type_name} - #{from.class} is not equals to #{to.class}" unless from.to_json == to.to_json
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
def check_if_exists(module_name)
|
|
18
|
-
mod = begin
|
|
19
|
-
module_name.constantize
|
|
20
|
-
rescue
|
|
21
|
-
nil
|
|
22
|
-
end
|
|
23
|
-
return true if mod
|
|
24
|
-
false unless mod
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
Time.zone = "UTC"
|
data/docs/docs/associations.md
DELETED
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
id: associations
|
|
3
|
-
title: Associations
|
|
4
|
-
sidebar_label: Associations
|
|
5
|
-
---
|
|
6
|
-
A serializer can define it's own associations - both `has_many` and `has_one` to serializer under the context of the object.
|
|
7
|
-
|
|
8
|
-
For example:
|
|
9
|
-
|
|
10
|
-
```ruby
|
|
11
|
-
|
|
12
|
-
class PostSerializer < Panko::Serializer
|
|
13
|
-
attributes :title, :body
|
|
14
|
-
|
|
15
|
-
has_one :author, serializer: AuthorSerializer
|
|
16
|
-
has_many :comments, each_serializer: CommentSerializer
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
### Associations with aliases
|
|
22
|
-
|
|
23
|
-
An association key name can be aliased with the `name` option.
|
|
24
|
-
|
|
25
|
-
For example:
|
|
26
|
-
the `actual_author` property will be converted to `alias_author`.
|
|
27
|
-
|
|
28
|
-
```ruby
|
|
29
|
-
|
|
30
|
-
class PostSerializer < Panko::Serializer
|
|
31
|
-
attributes :title, :body
|
|
32
|
-
|
|
33
|
-
has_one :actual_author, serializer: AuthorSerializer, name: :alias_author
|
|
34
|
-
has_many :comments, each_serializer: CommentSerializer
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
### Inference
|
|
40
|
-
|
|
41
|
-
Panko can find the type of the serializer by looking at the realtionship name, so instead specifying
|
|
42
|
-
the serializer at the above example, we can -
|
|
43
|
-
|
|
44
|
-
```ruby
|
|
45
|
-
|
|
46
|
-
class PostSerializer < Panko::Serializer
|
|
47
|
-
attributes :title, :body
|
|
48
|
-
|
|
49
|
-
has_one :author
|
|
50
|
-
has_many :comments
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
The logic of inferencing is -
|
|
56
|
-
|
|
57
|
-
- Take the name of the relationship (for example - `:author` / `:comments`) singularize and camelize it
|
|
58
|
-
- Look for const defined with the name aboe and "Serializer" suffix (by using `Object.const_get`)
|
|
59
|
-
|
|
60
|
-
> If Panko can't find the serializer it will throw an error on startup time, for example: `Can't find serializer for PostSerializer.author has_one relationship`
|
|
61
|
-
|
|
62
|
-
## Nested Filters
|
|
63
|
-
|
|
64
|
-
As talked before, Panko allows you to filter the attributes of a serializer.
|
|
65
|
-
But Panko let you take that step further, and filters the attributes of you associations so you can re-use your serializers in your application.
|
|
66
|
-
|
|
67
|
-
For example, let's say one portion of the application needs to serializer list of posts and serializer their - `title`, `body`, author's id and comments id.
|
|
68
|
-
|
|
69
|
-
We can declare tailored serializer for this, or we can re-use the above defined serializer - `PostSerializer` and use nested filters.
|
|
70
|
-
|
|
71
|
-
```ruby
|
|
72
|
-
|
|
73
|
-
posts = Post.all
|
|
74
|
-
|
|
75
|
-
Panko::ArraySerializer.new(posts, each_serializer: PostSerializer, only: {
|
|
76
|
-
instance: [:title, :body, :author, :comments],
|
|
77
|
-
author: [:id],
|
|
78
|
-
comments: [:id],
|
|
79
|
-
})
|
|
80
|
-
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
Let's dissect `only` option we passed -
|
|
84
|
-
|
|
85
|
-
- `instance` - list of attributes (and associations) we want to serializer for current instance of the serializer, in this case - `PostSerializer`.
|
|
86
|
-
- `author`, `comments` - here we specify the list of attributes we want to serialize for each association.
|
|
87
|
-
|
|
88
|
-
It's important to note that Nested Filters, are recursive, in other words, we can filter the association's associations.
|
|
89
|
-
|
|
90
|
-
For example, `CommentSerializer` have has_one association `Author`, and for each `comments.author` we only it's name.
|
|
91
|
-
|
|
92
|
-
```ruby
|
|
93
|
-
|
|
94
|
-
posts = Post.all
|
|
95
|
-
|
|
96
|
-
Panko::ArraySerializer.new(posts, only: {
|
|
97
|
-
instance: [:title, :body, :author, :comments],
|
|
98
|
-
author: [:id],
|
|
99
|
-
comments: {
|
|
100
|
-
instance: [:id, :author],
|
|
101
|
-
author: [:name]
|
|
102
|
-
}
|
|
103
|
-
})
|
|
104
|
-
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
As you see now in `comments` the `instance` have different meaning, the `CommentSerializer`.
|
data/docs/docs/attributes.md
DELETED
|
@@ -1,143 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
id: attributes
|
|
3
|
-
title: Attributes
|
|
4
|
-
sidebar_label: Attributes
|
|
5
|
-
---
|
|
6
|
-
Attributes allow you to specify which record attributes you want to serialize,
|
|
7
|
-
There are two types of attributes:
|
|
8
|
-
|
|
9
|
-
- Field - simple columns defined on the record it self.
|
|
10
|
-
- Virtual/Method - this allows to include properties beyond simple fields.
|
|
11
|
-
|
|
12
|
-
```ruby
|
|
13
|
-
|
|
14
|
-
class UserSerializer < Panko::Serializer
|
|
15
|
-
attributes :full_name
|
|
16
|
-
|
|
17
|
-
def full_name
|
|
18
|
-
"#{object.first_name} #{object.last_name}"
|
|
19
|
-
end
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
## Field Attributes
|
|
25
|
-
|
|
26
|
-
Using field attributes you can control which columns of the given ActiveRecord object you want to serialize.
|
|
27
|
-
|
|
28
|
-
Instead of relying ActiveRecord to do it's type casting, Panko does on it's own for performance reasons (read more in [Design Choices](design-choices.md#type-casting)).
|
|
29
|
-
|
|
30
|
-
## Method Attributes
|
|
31
|
-
|
|
32
|
-
Method attributes are used when your serialized values can be derived from the object you are serializing.
|
|
33
|
-
|
|
34
|
-
The serializer's attribute methods can access the object being serialized as `object` -
|
|
35
|
-
|
|
36
|
-
```ruby
|
|
37
|
-
|
|
38
|
-
class PostSerializer < Panko::Serializer
|
|
39
|
-
attributes :author_name
|
|
40
|
-
|
|
41
|
-
def author_name
|
|
42
|
-
"#{object.author.first_name} #{object.author.last_name}"
|
|
43
|
-
end
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
Another useful, thing you can pass your serializer is `context`, a `context` is a bag of data whom your serializer may need.
|
|
49
|
-
|
|
50
|
-
For example, here we will pass feature flags:
|
|
51
|
-
|
|
52
|
-
```ruby
|
|
53
|
-
|
|
54
|
-
class UserSerializer < Panko::Serializer
|
|
55
|
-
attributes :id, :email
|
|
56
|
-
|
|
57
|
-
def feature_flags
|
|
58
|
-
context[:feature_flags]
|
|
59
|
-
end
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
serializer = UserSerializer.new(context: {
|
|
63
|
-
feature_flags: FeatureFlags.all
|
|
64
|
-
})
|
|
65
|
-
|
|
66
|
-
serializer.serialize(User.first)
|
|
67
|
-
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
## Filters
|
|
71
|
-
|
|
72
|
-
Filters allows us to reduce the amount of attributes we can serialize, therefore reduce the data usage & performance of serializing.
|
|
73
|
-
|
|
74
|
-
There are two types of filters:
|
|
75
|
-
|
|
76
|
-
- only - use those attributes **only** and nothing else
|
|
77
|
-
- except - all attributes **except** those attributes
|
|
78
|
-
|
|
79
|
-
Usage example:
|
|
80
|
-
|
|
81
|
-
```ruby
|
|
82
|
-
|
|
83
|
-
class UserSerializer < Panko::Serializer
|
|
84
|
-
attributes :id, :name, :email
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
# this line will return { 'name': '..' }
|
|
88
|
-
UserSerializer.new(only: [:name]).serialize(User.first)
|
|
89
|
-
|
|
90
|
-
# this line will return { 'id': '..', 'email': ... }
|
|
91
|
-
UserSerializer.new(except: [:name]).serialize(User.first)
|
|
92
|
-
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
> **Note** that if you want to user filter on an associations, the `:name`
|
|
96
|
-
> property is not taken into account.
|
|
97
|
-
> If you have a `has_many :state_transitions, name: :history` association
|
|
98
|
-
> defined, the key to use in filters is `:state_transitions`
|
|
99
|
-
> (e.g. `{ except: [:state_transitions] }`)
|
|
100
|
-
|
|
101
|
-
## Filters For
|
|
102
|
-
|
|
103
|
-
Sometimes you find yourself have the same filtering logic in actions in order to
|
|
104
|
-
solve this duplication, Panko allows you to write the filters in the serializer.
|
|
105
|
-
|
|
106
|
-
```ruby
|
|
107
|
-
|
|
108
|
-
class UserSerializer < Panko::Serializer
|
|
109
|
-
attributes :id, :name, :email
|
|
110
|
-
|
|
111
|
-
def self.filters_for(context, scope)
|
|
112
|
-
{
|
|
113
|
-
only: [:name]
|
|
114
|
-
}
|
|
115
|
-
end
|
|
116
|
-
end
|
|
117
|
-
|
|
118
|
-
# this line will return { 'name': '..' }
|
|
119
|
-
UserSerializer.serialize(User.first)
|
|
120
|
-
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
> See discussion in: https:
|
|
124
|
-
|
|
125
|
-
## Aliases
|
|
126
|
-
|
|
127
|
-
Let's say we have attribute name that we want to expose to client as different name, the current way of doing so is using method attribute, for example:
|
|
128
|
-
|
|
129
|
-
```ruby
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
```
|
|
134
|
-
|
|
135
|
-
The downside of this approach is that `` skips Panko's type casting, therefore we get direct hit on performance.
|
|
136
|
-
|
|
137
|
-
To fix this, we can use aliases -
|
|
138
|
-
|
|
139
|
-
```ruby
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
```
|