panko_serializer 0.1.1
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 +7 -0
- data/.clang-format +102 -0
- data/.gitignore +12 -0
- data/.rspec +2 -0
- data/.rubocop.yml +22 -0
- data/.travis.yml +8 -0
- data/Gemfile +36 -0
- data/LICENSE.txt +21 -0
- data/README.md +8 -0
- data/Rakefile +62 -0
- data/benchmarks/BENCHMARKS.md +48 -0
- data/benchmarks/allocs.rb +23 -0
- data/benchmarks/app.rb +13 -0
- data/benchmarks/benchmarking_support.rb +43 -0
- data/benchmarks/bm_active_model_serializers.rb +45 -0
- data/benchmarks/bm_controller.rb +81 -0
- data/benchmarks/bm_panko_json.rb +60 -0
- data/benchmarks/bm_panko_object.rb +69 -0
- data/benchmarks/profile.rb +88 -0
- data/benchmarks/sanity.rb +67 -0
- data/benchmarks/setup.rb +62 -0
- data/benchmarks/type_casts/bm_active_record.rb +57 -0
- data/benchmarks/type_casts/bm_panko.rb +67 -0
- data/benchmarks/type_casts/bm_pg.rb +35 -0
- data/benchmarks/type_casts/support.rb +16 -0
- data/ext/panko_serializer/attributes_iterator.c +62 -0
- data/ext/panko_serializer/attributes_iterator.h +17 -0
- data/ext/panko_serializer/extconf.rb +8 -0
- data/ext/panko_serializer/panko_serializer.c +189 -0
- data/ext/panko_serializer/panko_serializer.h +17 -0
- data/ext/panko_serializer/serialization_descriptor.c +166 -0
- data/ext/panko_serializer/serialization_descriptor.h +30 -0
- data/ext/panko_serializer/time_conversion.c +94 -0
- data/ext/panko_serializer/time_conversion.h +6 -0
- data/ext/panko_serializer/type_cast.c +271 -0
- data/ext/panko_serializer/type_cast.h +74 -0
- data/lib/panko/array_serializer.rb +40 -0
- data/lib/panko/cache.rb +35 -0
- data/lib/panko/serialization_descriptor.rb +119 -0
- data/lib/panko/serializer.rb +57 -0
- data/lib/panko/version.rb +4 -0
- data/lib/panko.rb +8 -0
- data/panko_serializer.gemspec +31 -0
- metadata +171 -0
@@ -0,0 +1,88 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require_relative "./benchmarking_support"
|
3
|
+
require_relative "./app"
|
4
|
+
require_relative "./setup"
|
5
|
+
|
6
|
+
require "memory_profiler"
|
7
|
+
|
8
|
+
class NullLogger < Logger
|
9
|
+
def initialize(*args)
|
10
|
+
end
|
11
|
+
|
12
|
+
def add(*args, &block)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class BenchmarkApp < Rails::Application
|
17
|
+
routes.append do
|
18
|
+
get "/simple" => "main#simple"
|
19
|
+
get "/text" => "main#text"
|
20
|
+
|
21
|
+
get "/serialize_to_string" => "main#serialize_to_string"
|
22
|
+
get "/serialize_to_stream" => "streaming#serialize_to_stream"
|
23
|
+
end
|
24
|
+
|
25
|
+
config.secret_token = "s"*30
|
26
|
+
config.secret_key_base = "foo"
|
27
|
+
config.consider_all_requests_local = false
|
28
|
+
|
29
|
+
# simulate production
|
30
|
+
config.cache_classes = true
|
31
|
+
config.eager_load = true
|
32
|
+
config.action_controller.perform_caching = true
|
33
|
+
|
34
|
+
# otherwise deadlock occured
|
35
|
+
config.middleware.delete "Rack::Lock"
|
36
|
+
|
37
|
+
# to disable log files
|
38
|
+
config.logger = NullLogger.new
|
39
|
+
config.active_support.deprecation = :log
|
40
|
+
end
|
41
|
+
|
42
|
+
BenchmarkApp.initialize!
|
43
|
+
|
44
|
+
class AuthorFastSerializer < Panko::Serializer
|
45
|
+
attributes :id, :name
|
46
|
+
end
|
47
|
+
|
48
|
+
class PostWithHasOneFastSerializer < Panko::Serializer
|
49
|
+
attributes :id, :body, :title, :author_id
|
50
|
+
|
51
|
+
has_one :author, serializer: AuthorFastSerializer
|
52
|
+
end
|
53
|
+
|
54
|
+
class StreamingController < ActionController::Base
|
55
|
+
include ActionController::Live
|
56
|
+
|
57
|
+
def serialize_to_stream
|
58
|
+
headers["Content-Type".freeze] = "application/json".freeze
|
59
|
+
|
60
|
+
data = Benchmark.data[:all]
|
61
|
+
serializer = Panko::ArraySerializer.new([], each_serializer: PostWithHasOneFastSerializer)
|
62
|
+
writer = Oj::StreamWriter.new(response.stream, mode: :rails)
|
63
|
+
|
64
|
+
serializer.serialize_to_writer(data, writer)
|
65
|
+
|
66
|
+
response.stream.close
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
class RouteNotFoundError < StandardError;end
|
72
|
+
|
73
|
+
|
74
|
+
def request(method, path)
|
75
|
+
response = Rack::MockRequest.new(BenchmarkApp).send(method, path)
|
76
|
+
if response.status.in?([404, 500])
|
77
|
+
raise RouteNotFoundError.new, 'not found #{method.to_s.upcase} #{path}'
|
78
|
+
end
|
79
|
+
response
|
80
|
+
end
|
81
|
+
|
82
|
+
|
83
|
+
def memory(&block)
|
84
|
+
mem = MemoryProfiler.report(&block)
|
85
|
+
mem.pretty_print
|
86
|
+
end
|
87
|
+
|
88
|
+
memory { request(:get, "/serialize_to_stream") }
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require_relative "./benchmarking_support"
|
3
|
+
require_relative "./app"
|
4
|
+
require_relative "./setup"
|
5
|
+
|
6
|
+
class AuthorFastSerializer < Panko::Serializer
|
7
|
+
attributes :id, :name
|
8
|
+
end
|
9
|
+
|
10
|
+
|
11
|
+
class PostFastSerializer < Panko::Serializer
|
12
|
+
attributes :id, :body, :title, :author_id, :created_at
|
13
|
+
end
|
14
|
+
|
15
|
+
class PostFastWithMethodCallSerializer < Panko::Serializer
|
16
|
+
attributes :id, :body, :title, :author_id, :created_at, :method_call
|
17
|
+
|
18
|
+
def method_call
|
19
|
+
object.id
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class PostWithHasOneFastSerializer < Panko::Serializer
|
24
|
+
attributes :id, :body, :title, :author_id, :created_at
|
25
|
+
|
26
|
+
has_one :author, serializer: AuthorFastSerializer
|
27
|
+
end
|
28
|
+
|
29
|
+
class AuthorWithHasManyFastSerializer < Panko::Serializer
|
30
|
+
attributes :id, :name
|
31
|
+
|
32
|
+
has_many :posts, serializer: PostFastSerializer
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
def benchmark(prefix, serializer, options = {})
|
37
|
+
data = Benchmark.data
|
38
|
+
posts = data[:all]
|
39
|
+
posts_50 = data[:small]
|
40
|
+
|
41
|
+
merged_options = options.merge(each_serializer: serializer)
|
42
|
+
|
43
|
+
Benchmark.ams("Panko_#{prefix}_Posts_#{posts.count}") do
|
44
|
+
Panko::ArraySerializer.new(posts, merged_options).to_json
|
45
|
+
end
|
46
|
+
|
47
|
+
data = Benchmark.data
|
48
|
+
posts = data[:all]
|
49
|
+
posts_50 = data[:small]
|
50
|
+
|
51
|
+
Benchmark.ams("Panko_#{prefix}_Posts_50") do
|
52
|
+
Panko::ArraySerializer.new(posts_50, merged_options).to_json
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
#puts "Waiting .. #{Process.pid}"
|
57
|
+
#gets.chomp
|
58
|
+
|
59
|
+
#puts "Starting!"
|
60
|
+
|
61
|
+
benchmark 'SimpleWithMethodCall', PostFastWithMethodCallSerializer
|
62
|
+
benchmark "HasOne", PostWithHasOneFastSerializer
|
63
|
+
benchmark "Simple", PostFastSerializer
|
64
|
+
|
65
|
+
# benchmark 'SimpleWithMethodCall', PostFastWithMethodCallSerializer
|
66
|
+
# benchmark 'Except', PostWithHasOneFastSerializer, except: [:title]
|
67
|
+
# benchmark 'Include', PostWithHasOneFastSerializer, include: [:id, :body, :author_id, :author]
|
data/benchmarks/setup.rb
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
###########################################
|
3
|
+
# Setup active record models
|
4
|
+
##########################################
|
5
|
+
require "active_record"
|
6
|
+
require "sqlite3"
|
7
|
+
|
8
|
+
|
9
|
+
# Change the following to reflect your database settings
|
10
|
+
ActiveRecord::Base.establish_connection(
|
11
|
+
adapter: "sqlite3",
|
12
|
+
database: ":memory:"
|
13
|
+
)
|
14
|
+
|
15
|
+
# Don't show migration output when constructing fake db
|
16
|
+
ActiveRecord::Migration.verbose = false
|
17
|
+
|
18
|
+
ActiveRecord::Schema.define do
|
19
|
+
create_table :authors, force: true do |t|
|
20
|
+
t.string :name
|
21
|
+
t.timestamps(null: false)
|
22
|
+
end
|
23
|
+
|
24
|
+
create_table :posts, force: true do |t|
|
25
|
+
t.text :body
|
26
|
+
t.string :title
|
27
|
+
t.references :author
|
28
|
+
t.timestamps(null: false)
|
29
|
+
end
|
30
|
+
|
31
|
+
create_table :profiles, force: true do |t|
|
32
|
+
t.text :project_url
|
33
|
+
t.text :bio
|
34
|
+
t.date :birthday
|
35
|
+
t.references :author
|
36
|
+
t.timestamps(null: false)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
class Author < ActiveRecord::Base
|
41
|
+
has_one :profile
|
42
|
+
has_many :posts
|
43
|
+
end
|
44
|
+
|
45
|
+
class Post < ActiveRecord::Base
|
46
|
+
belongs_to :author
|
47
|
+
end
|
48
|
+
|
49
|
+
class Profile < ActiveRecord::Base
|
50
|
+
belongs_to :author
|
51
|
+
end
|
52
|
+
|
53
|
+
# Build out the data to serialize
|
54
|
+
Post.transaction do
|
55
|
+
ENV.fetch("ITEMS_COUNT", "2300").to_i.times do
|
56
|
+
Post.create(
|
57
|
+
body: "something about how password restrictions are evil, and less secure, and with the math to prove it.",
|
58
|
+
title: "Your bank is does not know how to do security",
|
59
|
+
author: Author.create(name: "Preston Sego")
|
60
|
+
)
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require_relative "./support"
|
3
|
+
|
4
|
+
def ar_type_convert(type_klass, from, to)
|
5
|
+
converter = type_klass.new
|
6
|
+
assert type_klass.name, converter.type_cast_from_database(from), to
|
7
|
+
|
8
|
+
Benchmark.ams("#{type_klass.name}_TypeCast") do
|
9
|
+
converter.type_cast_from_database(from)
|
10
|
+
end
|
11
|
+
|
12
|
+
Benchmark.ams("#{type_klass.name}_NoTypeCast") do
|
13
|
+
converter.type_cast_from_database(to)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def utc_ar_time
|
18
|
+
date = DateTime.new(2017, 3, 4, 12, 45, 23)
|
19
|
+
tz = ActiveSupport::TimeZone.new("UTC")
|
20
|
+
from = date.in_time_zone(tz).iso8601
|
21
|
+
|
22
|
+
type = ActiveRecord::ConnectionAdapters::PostgreSQL::OID::DateTime.new
|
23
|
+
converter = ActiveRecord::AttributeMethods::TimeZoneConversion::TimeZoneConverter.new(type)
|
24
|
+
|
25
|
+
Benchmark.ams("#{tz}_#{type.class.name}_TypeCast") do
|
26
|
+
converter.type_cast_from_database(from).iso8601
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
|
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.ams("ActiveRecord_Time_TypeCast_WithISO8601") do
|
39
|
+
converter.type_cast_from_database(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::Float, "Infinity", 0.0
|
48
|
+
ar_type_convert ActiveRecord::Type::Boolean, "true", true
|
49
|
+
ar_type_convert ActiveRecord::Type::Boolean, "t", true
|
50
|
+
|
51
|
+
ar_type_convert ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Integer, "1", 1
|
52
|
+
ar_type_convert ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Float, "1.23", 1.23
|
53
|
+
ar_type_convert ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Float, "Infinity", ::Float::INFINITY
|
54
|
+
ar_type_convert ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Json, '{"a":1}', {a:1}
|
55
|
+
|
56
|
+
db_ar_time
|
57
|
+
utc_ar_time
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require_relative "./support"
|
3
|
+
|
4
|
+
def panko_type_convert(type_klass, from, to)
|
5
|
+
converter = type_klass.new
|
6
|
+
assert "#{type_klass.name}", Panko::_type_cast(converter, from), to
|
7
|
+
|
8
|
+
Benchmark.ams("#{type_klass.name}_TypeCast") do
|
9
|
+
Panko::_type_cast(converter, from)
|
10
|
+
end
|
11
|
+
|
12
|
+
Benchmark.ams("#{type_klass.name}_NoTypeCast") do
|
13
|
+
Panko::_type_cast(converter, to)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
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.ams("#{tz}_#{type.class.name}_TypeCast") do
|
29
|
+
Panko::_type_cast(converter, from)
|
30
|
+
end
|
31
|
+
|
32
|
+
Benchmark.ams("#{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.ams("Panko_Time_TypeCast") do
|
44
|
+
Panko::_type_cast(converter, from)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
db_panko_time
|
49
|
+
utc_panko_time
|
50
|
+
|
51
|
+
exit
|
52
|
+
|
53
|
+
panko_type_convert ActiveRecord::Type::String, 1, "1"
|
54
|
+
panko_type_convert ActiveRecord::Type::Text, 1, "1"
|
55
|
+
panko_type_convert ActiveRecord::Type::Integer, "1", 1
|
56
|
+
panko_type_convert ActiveRecord::Type::Float, "1.23", 1.23
|
57
|
+
panko_type_convert ActiveRecord::Type::Float, "Infinity", ::Float::INFINITY
|
58
|
+
panko_type_convert ActiveRecord::Type::Boolean, "true", true
|
59
|
+
panko_type_convert ActiveRecord::Type::Boolean, "t", true
|
60
|
+
|
61
|
+
panko_type_convert ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Integer, "1", 1
|
62
|
+
panko_type_convert ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Float, "1.23", 1.23
|
63
|
+
panko_type_convert ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Float, "Infinity", ::Float::INFINITY
|
64
|
+
|
65
|
+
panko_type_convert ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Json, '{"a":1}', {a:1}
|
66
|
+
db_panko_time
|
67
|
+
utc_panko_time
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require_relative "./support"
|
3
|
+
|
4
|
+
def pg_type_convert(type_klass, from, to)
|
5
|
+
converter = type_klass.new
|
6
|
+
assert type_klass.name, converter.decode(from), to
|
7
|
+
|
8
|
+
Benchmark.ams("#{type_klass.name}_TypeCast") do
|
9
|
+
converter.decode(from)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def pg_time
|
14
|
+
decoder = PG::TextDecoder::TimestampWithoutTimeZone.new
|
15
|
+
|
16
|
+
from = "2017-07-10 09:26:40.937392"
|
17
|
+
|
18
|
+
Benchmark.ams("#{decoder.class.name}_TypeCast") do
|
19
|
+
decoder.decode(from)
|
20
|
+
end
|
21
|
+
|
22
|
+
Benchmark.ams("#{decoder.class.name}_TypeCast_InTimeZone") do
|
23
|
+
decoder.decode(from).in_time_zone
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
|
29
|
+
|
30
|
+
pg_type_convert PG::TextDecoder::Integer, "1", 1
|
31
|
+
pg_type_convert PG::TextDecoder::Float, "1.23", 1.23
|
32
|
+
pg_type_convert PG::TextDecoder::Float, "Infinity", ::Float::INFINITY
|
33
|
+
pg_type_convert PG::TextDecoder::Float, "-Infinity", ::Float::INFINITY
|
34
|
+
pg_type_convert PG::TextDecoder::Float, "NaN", ::Float::NaN
|
35
|
+
pg_time
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "active_record"
|
3
|
+
require "active_record/connection_adapters/postgresql_adapter"
|
4
|
+
require "active_model"
|
5
|
+
require "active_support/all"
|
6
|
+
require "pg"
|
7
|
+
|
8
|
+
|
9
|
+
require_relative "../benchmarking_support"
|
10
|
+
require_relative "../../lib/panko/panko"
|
11
|
+
|
12
|
+
def assert(type_name, from, to)
|
13
|
+
raise "#{type_name} - #{from.class} is not equals to #{to.class}" unless from.to_json == to.to_json
|
14
|
+
end
|
15
|
+
|
16
|
+
Time.zone = "UTC"
|
@@ -0,0 +1,62 @@
|
|
1
|
+
#include "attributes_iterator.h"
|
2
|
+
|
3
|
+
static ID attributes_id = 0;
|
4
|
+
static ID types_id = 0;
|
5
|
+
static ID values_id = 0;
|
6
|
+
|
7
|
+
VALUE read_attributes(VALUE obj) {
|
8
|
+
return rb_ivar_get(obj, attributes_id);
|
9
|
+
}
|
10
|
+
|
11
|
+
VALUE panko_read_lazy_attributes_hash(VALUE object) {
|
12
|
+
VALUE attributes_set = read_attributes(object);
|
13
|
+
if (attributes_set == Qnil) {
|
14
|
+
return Qnil;
|
15
|
+
}
|
16
|
+
|
17
|
+
VALUE attributes_hash = read_attributes(attributes_set);
|
18
|
+
if (attributes_hash == Qnil) {
|
19
|
+
return Qnil;
|
20
|
+
}
|
21
|
+
|
22
|
+
return attributes_hash;
|
23
|
+
}
|
24
|
+
|
25
|
+
void panko_read_types_and_value(VALUE attributes_hash,
|
26
|
+
VALUE* types,
|
27
|
+
VALUE* values) {
|
28
|
+
*types = rb_ivar_get(attributes_hash, types_id);
|
29
|
+
*values = rb_ivar_get(attributes_hash, values_id);
|
30
|
+
}
|
31
|
+
|
32
|
+
VALUE panko_each_attribute(VALUE obj,
|
33
|
+
SerializationDescriptor descriptor,
|
34
|
+
VALUE attributes,
|
35
|
+
EachAttributeFunc func,
|
36
|
+
VALUE context) {
|
37
|
+
VALUE attributes_hash = panko_read_lazy_attributes_hash(obj);
|
38
|
+
if(attributes_hash == Qnil) {
|
39
|
+
return Qnil;
|
40
|
+
}
|
41
|
+
|
42
|
+
VALUE types, values;
|
43
|
+
panko_read_types_and_value(attributes_hash, &types, &values);
|
44
|
+
|
45
|
+
int i;
|
46
|
+
for (i = 0; i < RARRAY_LEN(attributes); i++) {
|
47
|
+
VALUE member = rb_sym2str(RARRAY_AREF(attributes, i));
|
48
|
+
|
49
|
+
VALUE value = rb_hash_aref(values, member);
|
50
|
+
VALUE type_metadata = rb_hash_aref(types, member);
|
51
|
+
|
52
|
+
func(obj, member, value, type_metadata, context);
|
53
|
+
}
|
54
|
+
|
55
|
+
return Qnil;
|
56
|
+
}
|
57
|
+
|
58
|
+
void panko_init_attributes_iterator(VALUE mPanko) {
|
59
|
+
attributes_id = rb_intern("@attributes");
|
60
|
+
values_id = rb_intern("@values");
|
61
|
+
types_id = rb_intern("@types");
|
62
|
+
}
|
@@ -0,0 +1,17 @@
|
|
1
|
+
#include <ruby.h>
|
2
|
+
|
3
|
+
#include "serialization_descriptor.h"
|
4
|
+
|
5
|
+
typedef void (*EachAttributeFunc)(VALUE object,
|
6
|
+
VALUE name,
|
7
|
+
VALUE value,
|
8
|
+
VALUE type_metadata,
|
9
|
+
VALUE context);
|
10
|
+
|
11
|
+
extern VALUE panko_each_attribute(VALUE object,
|
12
|
+
SerializationDescriptor descriptor,
|
13
|
+
VALUE attributes,
|
14
|
+
EachAttributeFunc func,
|
15
|
+
VALUE context);
|
16
|
+
|
17
|
+
void panko_init_attributes_iterator(VALUE mPanko);
|
@@ -0,0 +1,189 @@
|
|
1
|
+
#include <ruby.h>
|
2
|
+
|
3
|
+
#include "panko_serializer.h"
|
4
|
+
|
5
|
+
static ID push_value_id = 0;
|
6
|
+
static ID push_array_id = 0;
|
7
|
+
static ID push_object_id = 0;
|
8
|
+
static ID pop_id = 0;
|
9
|
+
|
10
|
+
static ID to_a_id = 0;
|
11
|
+
|
12
|
+
void write_value(VALUE str_writer,
|
13
|
+
VALUE key,
|
14
|
+
VALUE value,
|
15
|
+
VALUE type_metadata) {
|
16
|
+
if (type_metadata != Qnil) {
|
17
|
+
value = type_cast(type_metadata, value);
|
18
|
+
}
|
19
|
+
|
20
|
+
rb_funcall(str_writer, push_value_id, 2, value, key);
|
21
|
+
}
|
22
|
+
|
23
|
+
void serialize_method_fields(VALUE subject,
|
24
|
+
VALUE str_writer,
|
25
|
+
SerializationDescriptor descriptor,
|
26
|
+
VALUE context) {
|
27
|
+
VALUE method_fields = descriptor->method_fields;
|
28
|
+
if (RARRAY_LEN(method_fields) == 0) {
|
29
|
+
return;
|
30
|
+
}
|
31
|
+
|
32
|
+
VALUE serializer = sd_build_serializer(descriptor);
|
33
|
+
sd_apply_serializer_config(serializer, subject, context);
|
34
|
+
|
35
|
+
long i;
|
36
|
+
for (i = 0; i < RARRAY_LEN(method_fields); i++) {
|
37
|
+
VALUE attribute_name = RARRAY_AREF(method_fields, i);
|
38
|
+
VALUE result = rb_funcall(serializer, rb_sym2id(attribute_name), 0);
|
39
|
+
|
40
|
+
write_value(str_writer, rb_sym2str(attribute_name), result, Qnil);
|
41
|
+
}
|
42
|
+
}
|
43
|
+
|
44
|
+
void panko_attributes_iter(VALUE object,
|
45
|
+
VALUE name,
|
46
|
+
VALUE value,
|
47
|
+
VALUE type_metadata,
|
48
|
+
VALUE context) {
|
49
|
+
write_value(context, name, value, type_metadata);
|
50
|
+
}
|
51
|
+
|
52
|
+
void serialize_fields(VALUE subject,
|
53
|
+
VALUE str_writer,
|
54
|
+
SerializationDescriptor descriptor,
|
55
|
+
VALUE context) {
|
56
|
+
panko_each_attribute(subject, descriptor, descriptor->fields,
|
57
|
+
panko_attributes_iter, str_writer);
|
58
|
+
|
59
|
+
serialize_method_fields(subject, str_writer, descriptor, context);
|
60
|
+
}
|
61
|
+
|
62
|
+
void serialize_has_one_associatoins(VALUE subject,
|
63
|
+
VALUE str_writer,
|
64
|
+
VALUE context,
|
65
|
+
SerializationDescriptor descriptor,
|
66
|
+
VALUE associations) {
|
67
|
+
long i;
|
68
|
+
for (i = 0; i < RARRAY_LEN(associations); i++) {
|
69
|
+
VALUE association = RARRAY_AREF(associations, i);
|
70
|
+
|
71
|
+
VALUE name = RARRAY_AREF(association, 0);
|
72
|
+
VALUE association_descriptor = RARRAY_AREF(association, 1);
|
73
|
+
VALUE value = rb_funcall(subject, rb_sym2id(name), 0);
|
74
|
+
|
75
|
+
if (value == Qnil) {
|
76
|
+
write_value(str_writer, rb_sym2str(name), value, Qnil);
|
77
|
+
} else {
|
78
|
+
serialize_subject(rb_sym2str(name), value, str_writer,
|
79
|
+
sd_read(association_descriptor), context);
|
80
|
+
}
|
81
|
+
}
|
82
|
+
}
|
83
|
+
|
84
|
+
void serialize_has_many_associatoins(VALUE subject,
|
85
|
+
VALUE str_writer,
|
86
|
+
VALUE context,
|
87
|
+
SerializationDescriptor descriptor,
|
88
|
+
VALUE associations) {
|
89
|
+
long i;
|
90
|
+
for (i = 0; i < RARRAY_LEN(associations); i++) {
|
91
|
+
VALUE association = RARRAY_AREF(associations, i);
|
92
|
+
|
93
|
+
VALUE name = RARRAY_AREF(association, 0);
|
94
|
+
VALUE association_descriptor = RARRAY_AREF(association, 1);
|
95
|
+
VALUE value = rb_funcall(subject, rb_sym2id(name), 0);
|
96
|
+
|
97
|
+
if (value == Qnil) {
|
98
|
+
write_value(str_writer, rb_sym2str(name), value, Qnil);
|
99
|
+
} else {
|
100
|
+
serialize_subjects(rb_sym2str(name), value, str_writer,
|
101
|
+
sd_read(association_descriptor), context);
|
102
|
+
}
|
103
|
+
}
|
104
|
+
}
|
105
|
+
|
106
|
+
VALUE serialize_subject(VALUE key,
|
107
|
+
VALUE subject,
|
108
|
+
VALUE str_writer,
|
109
|
+
SerializationDescriptor descriptor,
|
110
|
+
VALUE context) {
|
111
|
+
rb_funcall(str_writer, push_object_id, 1, key);
|
112
|
+
|
113
|
+
serialize_fields(subject, str_writer, descriptor, context);
|
114
|
+
|
115
|
+
if (RARRAY_LEN(descriptor->has_one_associations) >= 0) {
|
116
|
+
serialize_has_one_associatoins(subject, str_writer, context, descriptor,
|
117
|
+
descriptor->has_one_associations);
|
118
|
+
}
|
119
|
+
|
120
|
+
if (RARRAY_LEN(descriptor->has_many_associations) >= 0) {
|
121
|
+
serialize_has_many_associatoins(subject, str_writer, context, descriptor,
|
122
|
+
descriptor->has_many_associations);
|
123
|
+
}
|
124
|
+
|
125
|
+
rb_funcall(str_writer, pop_id, 0);
|
126
|
+
|
127
|
+
return Qnil;
|
128
|
+
}
|
129
|
+
|
130
|
+
VALUE serialize_subjects(VALUE key,
|
131
|
+
VALUE subjects,
|
132
|
+
VALUE str_writer,
|
133
|
+
SerializationDescriptor descriptor,
|
134
|
+
VALUE context) {
|
135
|
+
rb_funcall(str_writer, push_array_id, 1, key);
|
136
|
+
|
137
|
+
if (!RB_TYPE_P(subjects, T_ARRAY)) {
|
138
|
+
subjects = rb_funcall(subjects, to_a_id, 0);
|
139
|
+
}
|
140
|
+
|
141
|
+
long i;
|
142
|
+
for (i = 0; i < RARRAY_LEN(subjects); i++) {
|
143
|
+
VALUE subject = RARRAY_AREF(subjects, i);
|
144
|
+
serialize_subject(Qnil, subject, str_writer, descriptor, context);
|
145
|
+
}
|
146
|
+
|
147
|
+
rb_funcall(str_writer, pop_id, 0);
|
148
|
+
|
149
|
+
return Qnil;
|
150
|
+
}
|
151
|
+
|
152
|
+
VALUE serialize_subject_api(VALUE klass,
|
153
|
+
VALUE subject,
|
154
|
+
VALUE str_writer,
|
155
|
+
VALUE descriptor,
|
156
|
+
VALUE context) {
|
157
|
+
return serialize_subject(Qnil, subject, str_writer, sd_read(descriptor),
|
158
|
+
context);
|
159
|
+
}
|
160
|
+
|
161
|
+
VALUE serialize_subjects_api(VALUE klass,
|
162
|
+
VALUE subjects,
|
163
|
+
VALUE str_writer,
|
164
|
+
VALUE descriptor,
|
165
|
+
VALUE context) {
|
166
|
+
serialize_subjects(Qnil, subjects, str_writer, sd_read(descriptor), context);
|
167
|
+
|
168
|
+
return Qnil;
|
169
|
+
}
|
170
|
+
|
171
|
+
void Init_panko_serializer() {
|
172
|
+
CONST_ID(push_value_id, "push_value");
|
173
|
+
CONST_ID(push_array_id, "push_array");
|
174
|
+
CONST_ID(push_object_id, "push_object");
|
175
|
+
CONST_ID(pop_id, "pop");
|
176
|
+
CONST_ID(to_a_id, "to_a");
|
177
|
+
|
178
|
+
VALUE mPanko = rb_define_module("Panko");
|
179
|
+
|
180
|
+
rb_define_singleton_method(mPanko, "serialize_subject", serialize_subject_api,
|
181
|
+
4);
|
182
|
+
|
183
|
+
rb_define_singleton_method(mPanko, "serialize_subjects",
|
184
|
+
serialize_subjects_api, 4);
|
185
|
+
|
186
|
+
panko_init_serialization_descriptor(mPanko);
|
187
|
+
panko_init_attributes_iterator(mPanko);
|
188
|
+
panko_init_type_cast(mPanko);
|
189
|
+
}
|
@@ -0,0 +1,17 @@
|
|
1
|
+
#include <ruby.h>
|
2
|
+
|
3
|
+
#include "serialization_descriptor.h"
|
4
|
+
#include "attributes_iterator.h"
|
5
|
+
#include "type_cast.h"
|
6
|
+
|
7
|
+
VALUE serialize_subject(VALUE key,
|
8
|
+
VALUE subject,
|
9
|
+
VALUE str_writer,
|
10
|
+
SerializationDescriptor descriptor,
|
11
|
+
VALUE context);
|
12
|
+
|
13
|
+
VALUE serialize_subjects(VALUE key,
|
14
|
+
VALUE subjects,
|
15
|
+
VALUE str_writer,
|
16
|
+
SerializationDescriptor descriptor,
|
17
|
+
VALUE context);
|