panko_serializer 0.5.10 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +3 -3
- data/Gemfile +7 -1
- data/Rakefile +1 -0
- data/benchmarks/bm_object_writer.rb +64 -0
- data/benchmarks/bm_to_object.rb +93 -0
- data/docs/attributes.md +1 -1
- data/ext/panko_serializer/attributes_writer/active_record.c +6 -10
- data/ext/panko_serializer/common.h +3 -0
- data/ext/panko_serializer/serialization_descriptor/attribute.c +1 -1
- data/lib/panko/array_serializer.rb +16 -12
- data/lib/panko/attribute.rb +1 -0
- data/lib/panko/object_writer.rb +56 -0
- data/lib/panko/serializer.rb +9 -4
- data/lib/panko/version.rb +1 -1
- data/lib/panko_serializer.rb +1 -0
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c49d12e1be46851f1fdb226b37d1c09673a586d053edb8b084f6345bf74b1637
|
4
|
+
data.tar.gz: e9895a4a74b16c83904ee77961c70259cb84a1d739b46d7203f7bdf2a06d3392
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 063cf79e3a6580a71a8b4e6f098cdb1514e7997cd22130a5189c4db7030b39ebada570f721cbfc36e4024141b30da7a40ed6c6d364c39465101f25e5a91baf61
|
7
|
+
data.tar.gz: 73987f5d252b7c2a4f0d901c941901f748536b7e16e017aede986899d1a1471246f7541b6c0aa35a1958b3c18e41bfdcb1795dfa7752296729991d5b0d1455ea
|
data/.travis.yml
CHANGED
@@ -2,9 +2,9 @@ sudo: false
|
|
2
2
|
cache: bundler
|
3
3
|
language: ruby
|
4
4
|
rvm:
|
5
|
-
- 2.3.8
|
6
5
|
- 2.5.5
|
7
6
|
- 2.6.3
|
7
|
+
- 2.7.0-preview1
|
8
8
|
|
9
9
|
env:
|
10
10
|
global:
|
@@ -26,8 +26,8 @@ env:
|
|
26
26
|
matrix:
|
27
27
|
- "RAILS_VERSION=4.2.0"
|
28
28
|
- "RAILS_VERSION=5.2.0"
|
29
|
-
- "RAILS_VERSION=6.0.0.
|
29
|
+
- "RAILS_VERSION=6.0.0.rc1"
|
30
30
|
|
31
31
|
matrix:
|
32
32
|
allow_failures:
|
33
|
-
- "RAILS_VERSION=6.0.0.
|
33
|
+
- "RAILS_VERSION=6.0.0.rc1"
|
data/Gemfile
CHANGED
@@ -10,8 +10,13 @@ gem "activesupport", rails_version
|
|
10
10
|
gem "activemodel", rails_version
|
11
11
|
gem "activerecord", rails_version, group: :test
|
12
12
|
|
13
|
+
|
13
14
|
group :benchmarks do
|
14
|
-
|
15
|
+
if raw_rails_version.include? "4.2"
|
16
|
+
gem 'sqlite3', '~> 1.3.6'
|
17
|
+
else
|
18
|
+
gem "sqlite3", "~> 1.4"
|
19
|
+
end
|
15
20
|
|
16
21
|
if raw_rails_version.include? "4.2"
|
17
22
|
gem "pg", "~> 0.15"
|
@@ -37,4 +42,5 @@ group :development do
|
|
37
42
|
gem "rake"
|
38
43
|
gem "rspec", "~> 3.0"
|
39
44
|
gem "rake-compiler"
|
45
|
+
gem "rubocop"
|
40
46
|
end
|
data/Rakefile
CHANGED
@@ -0,0 +1,64 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require_relative "./benchmarking_support"
|
3
|
+
require_relative "./app"
|
4
|
+
|
5
|
+
Benchmark.run("ObjectWriter_OneProperty_PushValue") do
|
6
|
+
writer = Panko::ObjectWriter.new
|
7
|
+
|
8
|
+
writer.push_object
|
9
|
+
writer.push_value "value1", "key1"
|
10
|
+
writer.pop
|
11
|
+
|
12
|
+
writer.output
|
13
|
+
end
|
14
|
+
|
15
|
+
Benchmark.run("ObjectWriter_TwoProperty_PushValue") do
|
16
|
+
writer = Panko::ObjectWriter.new
|
17
|
+
|
18
|
+
writer.push_object
|
19
|
+
writer.push_value "value1", "key1"
|
20
|
+
writer.push_value "value2", "key2"
|
21
|
+
writer.pop
|
22
|
+
|
23
|
+
writer.output
|
24
|
+
end
|
25
|
+
|
26
|
+
Benchmark.run("ObjectWriter_OneProperty_PushValuePushKey") do
|
27
|
+
writer = Panko::ObjectWriter.new
|
28
|
+
|
29
|
+
writer.push_object
|
30
|
+
writer.push_key "key1"
|
31
|
+
writer.push_value "value1"
|
32
|
+
writer.pop
|
33
|
+
|
34
|
+
writer.output
|
35
|
+
end
|
36
|
+
|
37
|
+
Benchmark.run("ObjectWriter_TwoProperty_PushValuePushKey") do
|
38
|
+
writer = Panko::ObjectWriter.new
|
39
|
+
|
40
|
+
writer.push_object
|
41
|
+
writer.push_key "key1"
|
42
|
+
writer.push_value "value1"
|
43
|
+
|
44
|
+
writer.push_key "key2"
|
45
|
+
writer.push_value "value2"
|
46
|
+
writer.pop
|
47
|
+
|
48
|
+
writer.output
|
49
|
+
end
|
50
|
+
|
51
|
+
Benchmark.run("ObjectWriter_NestedObject") do
|
52
|
+
writer = Panko::ObjectWriter.new
|
53
|
+
|
54
|
+
writer.push_object
|
55
|
+
writer.push_value "value1", "key1"
|
56
|
+
|
57
|
+
writer.push_object "key2"
|
58
|
+
writer.push_value "value2", "key2"
|
59
|
+
writer.pop
|
60
|
+
|
61
|
+
writer.pop
|
62
|
+
|
63
|
+
writer.output
|
64
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require_relative "./benchmarking_support"
|
3
|
+
require_relative "./app"
|
4
|
+
require_relative "./setup"
|
5
|
+
|
6
|
+
class PostWithAliasModel < ActiveRecord::Base
|
7
|
+
self.table_name = 'posts'
|
8
|
+
|
9
|
+
alias_attribute :new_id, :id
|
10
|
+
alias_attribute :new_body, :body
|
11
|
+
alias_attribute :new_title, :title
|
12
|
+
alias_attribute :new_author_id, :author_id
|
13
|
+
alias_attribute :new_created_at, :created_at
|
14
|
+
end
|
15
|
+
|
16
|
+
class PostWithAliasFastSerializer < Panko::Serializer
|
17
|
+
attributes :new_id, :new_body, :new_title, :new_author_id, :new_created_at
|
18
|
+
end
|
19
|
+
|
20
|
+
def benchmark_aliased(prefix, serializer, options = {})
|
21
|
+
posts = PostWithAliasModel.all.to_a
|
22
|
+
posts_50 = posts.first(50).to_a
|
23
|
+
data = { all: posts, small: posts_50 }
|
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
|
+
posts = PostWithAliasModel.all.to_a
|
32
|
+
posts_50 = posts.first(50).to_a
|
33
|
+
data = { all: posts, small: posts_50 }
|
34
|
+
|
35
|
+
Benchmark.run("Panko_#{prefix}_Posts_50") do
|
36
|
+
Panko::ArraySerializer.new(posts_50, merged_options).to_json
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
class AuthorFastSerializer < Panko::Serializer
|
41
|
+
attributes :id, :name
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
class PostFastSerializer < Panko::Serializer
|
46
|
+
attributes :id, :body, :title, :author_id, :created_at
|
47
|
+
end
|
48
|
+
|
49
|
+
class PostFastWithMethodCallSerializer < Panko::Serializer
|
50
|
+
attributes :id, :body, :title, :author_id, :created_at, :method_call
|
51
|
+
|
52
|
+
def method_call
|
53
|
+
object.id
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
class PostWithHasOneFastSerializer < Panko::Serializer
|
58
|
+
attributes :id, :body, :title, :author_id, :created_at
|
59
|
+
|
60
|
+
has_one :author, serializer: AuthorFastSerializer
|
61
|
+
end
|
62
|
+
|
63
|
+
class AuthorWithHasManyFastSerializer < Panko::Serializer
|
64
|
+
attributes :id, :name
|
65
|
+
|
66
|
+
has_many :posts, serializer: PostFastSerializer
|
67
|
+
end
|
68
|
+
|
69
|
+
|
70
|
+
def benchmark(prefix, serializer, options = {})
|
71
|
+
data = Benchmark.data
|
72
|
+
posts = data[:all]
|
73
|
+
posts_50 = data[:small]
|
74
|
+
|
75
|
+
merged_options = options.merge(each_serializer: serializer)
|
76
|
+
|
77
|
+
Benchmark.run("Panko_#{prefix}_Posts_#{posts.count}") do
|
78
|
+
Panko::ArraySerializer.new(posts, merged_options).to_a
|
79
|
+
end
|
80
|
+
|
81
|
+
data = Benchmark.data
|
82
|
+
posts = data[:all]
|
83
|
+
posts_50 = data[:small]
|
84
|
+
|
85
|
+
Benchmark.run("Panko_#{prefix}_Posts_50") do
|
86
|
+
Panko::ArraySerializer.new(posts_50, merged_options).to_a
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
|
91
|
+
|
92
|
+
benchmark "Simple", PostFastSerializer
|
93
|
+
benchmark "HasOne", PostWithHasOneFastSerializer
|
data/docs/attributes.md
CHANGED
@@ -86,7 +86,7 @@ Sometimes you find yourself have the same filtering logic in actions in order to
|
|
86
86
|
class UserSerializer < Panko::Serializer
|
87
87
|
attributes :id, :name, :email
|
88
88
|
|
89
|
-
def self.filters_for(context)
|
89
|
+
def self.filters_for(context, scope)
|
90
90
|
{
|
91
91
|
only: [:name]
|
92
92
|
}
|
@@ -29,7 +29,6 @@ struct attributes {
|
|
29
29
|
VALUE values;
|
30
30
|
|
31
31
|
// heuristicts
|
32
|
-
bool shouldReadFromHash;
|
33
32
|
bool tryToReadFromAdditionalTypes;
|
34
33
|
};
|
35
34
|
|
@@ -40,21 +39,18 @@ struct attributes init_context(VALUE obj) {
|
|
40
39
|
attributes_ctx.types = Qnil;
|
41
40
|
attributes_ctx.additional_types = Qnil;
|
42
41
|
|
43
|
-
attributes_ctx.shouldReadFromHash = false;
|
44
42
|
attributes_ctx.tryToReadFromAdditionalTypes = false;
|
45
43
|
|
46
44
|
volatile VALUE lazy_attributes_hash = panko_read_lazy_attributes_hash(obj);
|
47
45
|
|
48
46
|
if (RB_TYPE_P(lazy_attributes_hash, T_HASH)) {
|
49
47
|
attributes_ctx.attributes_hash = lazy_attributes_hash;
|
50
|
-
attributes_ctx.shouldReadFromHash = true;
|
51
48
|
} else {
|
52
49
|
volatile VALUE delegate_hash =
|
53
50
|
rb_ivar_get(lazy_attributes_hash, delegate_hash_id);
|
54
51
|
|
55
52
|
if (PANKO_EMPTY_HASH(delegate_hash) == false) {
|
56
53
|
attributes_ctx.attributes_hash = delegate_hash;
|
57
|
-
attributes_ctx.shouldReadFromHash = true;
|
58
54
|
}
|
59
55
|
|
60
56
|
attributes_ctx.types = rb_ivar_get(lazy_attributes_hash, types_id);
|
@@ -73,11 +69,13 @@ VALUE read_attribute(struct attributes attributes_ctx, Attribute attribute) {
|
|
73
69
|
volatile VALUE member, value;
|
74
70
|
|
75
71
|
member = attribute->name_str;
|
76
|
-
value =
|
72
|
+
value = Qnil;
|
77
73
|
|
78
|
-
|
74
|
+
|
75
|
+
if (!NIL_P(attributes_ctx.attributes_hash)) {
|
79
76
|
volatile VALUE attribute_metadata =
|
80
77
|
rb_hash_aref(attributes_ctx.attributes_hash, member);
|
78
|
+
|
81
79
|
if (attribute_metadata != Qnil) {
|
82
80
|
value = rb_ivar_get(attribute_metadata, value_before_type_cast_id);
|
83
81
|
|
@@ -87,13 +85,11 @@ VALUE read_attribute(struct attributes attributes_ctx, Attribute attribute) {
|
|
87
85
|
}
|
88
86
|
}
|
89
87
|
|
90
|
-
if (value
|
88
|
+
if (NIL_P(value) && !NIL_P(attributes_ctx.values)) {
|
91
89
|
value = rb_hash_aref(attributes_ctx.values, member);
|
92
|
-
if (NIL_P(value)) {
|
93
|
-
value = Qundef;
|
94
|
-
}
|
95
90
|
}
|
96
91
|
|
92
|
+
|
97
93
|
if (NIL_P(attribute->type) && !NIL_P(value)) {
|
98
94
|
if (attributes_ctx.tryToReadFromAdditionalTypes == true) {
|
99
95
|
attribute->type = rb_hash_aref(attributes_ctx.additional_types, member);
|
@@ -48,7 +48,7 @@ Attribute attribute_read(VALUE attribute) {
|
|
48
48
|
}
|
49
49
|
|
50
50
|
void attribute_try_invalidate(Attribute attribute, VALUE new_record_class) {
|
51
|
-
if (
|
51
|
+
if (attribute->record_class != new_record_class) {
|
52
52
|
attribute->type = Qnil;
|
53
53
|
attribute->record_class = new_record_class;
|
54
54
|
|
@@ -2,10 +2,10 @@
|
|
2
2
|
|
3
3
|
module Panko
|
4
4
|
class ArraySerializer
|
5
|
-
attr_accessor :
|
5
|
+
attr_accessor :subjects
|
6
6
|
|
7
|
-
def initialize(
|
8
|
-
@
|
7
|
+
def initialize(subjects, options = {})
|
8
|
+
@subjects = subjects
|
9
9
|
@each_serializer = options[:each_serializer]
|
10
10
|
|
11
11
|
if @each_serializer.nil?
|
@@ -27,22 +27,26 @@ Please pass valid each_serializer to ArraySerializer, for example:
|
|
27
27
|
end
|
28
28
|
|
29
29
|
def to_json
|
30
|
-
serialize_to_json @
|
30
|
+
serialize_to_json @subjects
|
31
31
|
end
|
32
32
|
|
33
|
-
def serialize(
|
34
|
-
|
33
|
+
def serialize(subjects)
|
34
|
+
serialize_with_writer(subjects, Panko::ObjectWriter.new).output
|
35
35
|
end
|
36
36
|
|
37
37
|
def to_a
|
38
|
-
|
38
|
+
serialize_with_writer(@subjects, Panko::ObjectWriter.new).output
|
39
39
|
end
|
40
40
|
|
41
|
-
def serialize_to_json(
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
41
|
+
def serialize_to_json(subjects)
|
42
|
+
serialize_with_writer(subjects, Oj::StringWriter.new(mode: :rails)).to_s
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def serialize_with_writer(subjects, writer)
|
48
|
+
Panko.serialize_objects(subjects.to_a, writer, @descriptor)
|
49
|
+
writer
|
46
50
|
end
|
47
51
|
end
|
48
52
|
end
|
data/lib/panko/attribute.rb
CHANGED
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Panko::ObjectWriter
|
4
|
+
def initialize
|
5
|
+
@values = []
|
6
|
+
@keys = []
|
7
|
+
|
8
|
+
@next_key = nil
|
9
|
+
@output = nil
|
10
|
+
end
|
11
|
+
|
12
|
+
def push_object(key = nil)
|
13
|
+
@values << {}
|
14
|
+
@keys << key
|
15
|
+
end
|
16
|
+
|
17
|
+
def push_array(key = nil)
|
18
|
+
@values << []
|
19
|
+
@keys << key
|
20
|
+
end
|
21
|
+
|
22
|
+
def push_key(key)
|
23
|
+
@next_key = key
|
24
|
+
end
|
25
|
+
|
26
|
+
def push_value(value, key = nil)
|
27
|
+
unless @next_key.nil?
|
28
|
+
raise "push_value is called with key after push_key is called" unless key.nil?
|
29
|
+
key = @next_key
|
30
|
+
@next_key = nil
|
31
|
+
end
|
32
|
+
|
33
|
+
@values.last[key] = value.as_json
|
34
|
+
end
|
35
|
+
|
36
|
+
def pop
|
37
|
+
result = @values.pop
|
38
|
+
|
39
|
+
if @values.empty?
|
40
|
+
@output = result
|
41
|
+
return
|
42
|
+
end
|
43
|
+
|
44
|
+
scope_key = @keys.pop
|
45
|
+
if scope_key.nil?
|
46
|
+
@values.last << result
|
47
|
+
else
|
48
|
+
@values.last[scope_key] = result
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def output
|
53
|
+
raise "Output is called before poping all" unless @values.empty?
|
54
|
+
@output
|
55
|
+
end
|
56
|
+
end
|
data/lib/panko/serializer.rb
CHANGED
@@ -64,6 +64,7 @@ module Panko
|
|
64
64
|
|
65
65
|
def method_added(method)
|
66
66
|
return if @_descriptor.nil?
|
67
|
+
|
67
68
|
deleted_attr = @_descriptor.attributes.delete(method)
|
68
69
|
@_descriptor.method_fields << Attribute.create(method) unless deleted_attr.nil?
|
69
70
|
end
|
@@ -115,14 +116,18 @@ module Panko
|
|
115
116
|
attr_reader :object
|
116
117
|
|
117
118
|
def serialize(object)
|
118
|
-
|
119
|
+
serialize_with_writer(object, Panko::ObjectWriter.new).output
|
119
120
|
end
|
120
121
|
|
121
122
|
def serialize_to_json(object)
|
122
|
-
|
123
|
+
serialize_with_writer(object, Oj::StringWriter.new(mode: :rails)).to_s
|
124
|
+
end
|
125
|
+
|
126
|
+
private
|
127
|
+
|
128
|
+
def serialize_with_writer(object, writer)
|
123
129
|
Panko.serialize_object(object, writer, @descriptor)
|
124
|
-
|
125
|
-
writer.to_s
|
130
|
+
writer
|
126
131
|
end
|
127
132
|
end
|
128
133
|
end
|
data/lib/panko/version.rb
CHANGED
data/lib/panko_serializer.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: panko_serializer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Yosi Attias
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-06-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: oj
|
@@ -46,11 +46,13 @@ files:
|
|
46
46
|
- benchmarks/app.rb
|
47
47
|
- benchmarks/benchmarking_support.rb
|
48
48
|
- benchmarks/bm_ams_0_10.rb
|
49
|
+
- benchmarks/bm_object_writer.rb
|
49
50
|
- benchmarks/bm_panko_json.rb
|
50
51
|
- benchmarks/bm_panko_object.rb
|
51
52
|
- benchmarks/bm_plain_object.rb
|
52
53
|
- benchmarks/bm_serialization_descriptor.rb
|
53
54
|
- benchmarks/bm_serializer_resolver.rb
|
55
|
+
- benchmarks/bm_to_object.rb
|
54
56
|
- benchmarks/profile.rb
|
55
57
|
- benchmarks/sanity.rb
|
56
58
|
- benchmarks/setup.rb
|
@@ -90,6 +92,7 @@ files:
|
|
90
92
|
- lib/panko/array_serializer.rb
|
91
93
|
- lib/panko/association.rb
|
92
94
|
- lib/panko/attribute.rb
|
95
|
+
- lib/panko/object_writer.rb
|
93
96
|
- lib/panko/response.rb
|
94
97
|
- lib/panko/serialization_descriptor.rb
|
95
98
|
- lib/panko/serializer.rb
|