panko_serializer 0.5.10 → 0.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 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