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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7ec2a5cbfc062d6b8e272f038638645e3289b94dc9aa730f2317082d1ff2d9c4
4
- data.tar.gz: e2d04c62d3b1fcf2f292170181e3a4f689367af7d3cc53719bbbcfc6a7eea9af
3
+ metadata.gz: c49d12e1be46851f1fdb226b37d1c09673a586d053edb8b084f6345bf74b1637
4
+ data.tar.gz: e9895a4a74b16c83904ee77961c70259cb84a1d739b46d7203f7bdf2a06d3392
5
5
  SHA512:
6
- metadata.gz: 80ec461198ad3a7fd1db51a692686b1a90874444a1f6c2c9cde22aef40cf5a79b597804d6ba6aa77b5f7f8e765e5e2dd5b95401b9817b3a8dd5136a1e849fa36
7
- data.tar.gz: 7637de2f896f9c6a6b788da226fda929ca28f944eaa1b2b0a4f00f3dd95a771350b6fe0975459502669dadbccc565008708a38263c4baa71f892c95bd8531242
6
+ metadata.gz: 063cf79e3a6580a71a8b4e6f098cdb1514e7997cd22130a5189c4db7030b39ebada570f721cbfc36e4024141b30da7a40ed6c6d364c39465101f25e5a91baf61
7
+ data.tar.gz: 73987f5d252b7c2a4f0d901c941901f748536b7e16e017aede986899d1a1471246f7541b6c0aa35a1958b3c18e41bfdcb1795dfa7752296729991d5b0d1455ea
@@ -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.beta3"
29
+ - "RAILS_VERSION=6.0.0.rc1"
30
30
 
31
31
  matrix:
32
32
  allow_failures:
33
- - "RAILS_VERSION=6.0.0.beta3"
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
- gem "sqlite3", "~> 1.3.6"
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
@@ -19,6 +19,7 @@ end
19
19
 
20
20
  RSpec::Core::RakeTask.new(:spec)
21
21
  Rake::Task[:spec].prerequisites << :compile
22
+ Rake::Task[:compile].prerequisites << :clean
22
23
 
23
24
  task default: :spec
24
25
 
@@ -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
@@ -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 = Qundef;
72
+ value = Qnil;
77
73
 
78
- if (attributes_ctx.shouldReadFromHash == true) {
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 == Qundef && !NIL_P(attributes_ctx.values)) {
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);
@@ -3,6 +3,9 @@
3
3
  #include <ruby.h>
4
4
 
5
5
 
6
+ #define PANKO_SAFE_HASH_SIZE(hash) \
7
+ (hash == Qnil || hash == Qundef) ? 0 : RHASH_SIZE(hash)
8
+
6
9
  #define PANKO_EMPTY_HASH(hash) \
7
10
  (hash == Qnil || hash == Qundef) ? 1 : (RHASH_SIZE(hash) == 0)
8
11
 
@@ -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 (rb_equal(attribute->record_class, new_record_class) == Qfalse) {
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 :objects
5
+ attr_accessor :subjects
6
6
 
7
- def initialize(objects, options = {})
8
- @objects = objects
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 @objects
30
+ serialize_to_json @subjects
31
31
  end
32
32
 
33
- def serialize(objects)
34
- Oj.load(serialize_to_json(objects))
33
+ def serialize(subjects)
34
+ serialize_with_writer(subjects, Panko::ObjectWriter.new).output
35
35
  end
36
36
 
37
37
  def to_a
38
- Oj.load(serialize_to_json(@objects))
38
+ serialize_with_writer(@subjects, Panko::ObjectWriter.new).output
39
39
  end
40
40
 
41
- def serialize_to_json(objects)
42
- writer = Oj::StringWriter.new(mode: :rails)
43
- Panko.serialize_objects(objects.to_a, writer, @descriptor)
44
- @descriptor.set_serialization_context(nil) unless @serialization_context.is_a?(EmptySerializerContext)
45
- writer.to_s
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
@@ -10,6 +10,7 @@ module Panko
10
10
  def ==(attr)
11
11
  return name.to_sym == attr if attr.is_a? Symbol
12
12
  return name == attr.name && alias_name == attr.alias_name if attr.is_a? Panko::Attribute
13
+
13
14
  super
14
15
  end
15
16
 
@@ -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
@@ -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
- Oj.load(serialize_to_json(object))
119
+ serialize_with_writer(object, Panko::ObjectWriter.new).output
119
120
  end
120
121
 
121
122
  def serialize_to_json(object)
122
- writer = Oj::StringWriter.new(mode: :rails)
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
- @descriptor.set_serialization_context(nil) unless @serialization_context.is_a?(EmptySerializerContext)
125
- writer.to_s
130
+ writer
126
131
  end
127
132
  end
128
133
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Panko
4
- VERSION = "0.5.10"
4
+ VERSION = "0.6.0"
5
5
  end
@@ -7,6 +7,7 @@ require "panko/serializer"
7
7
  require "panko/array_serializer"
8
8
  require "panko/response"
9
9
  require "panko/serializer_resolver"
10
+ require "panko/object_writer"
10
11
 
11
12
  # C Extension
12
13
  require "oj"
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.5.10
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-04-20 00:00:00.000000000 Z
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