serde 0.0.1 → 0.0.2
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/.gitignore +5 -1
- data/.rubocop.yml +29 -0
- data/Gemfile +5 -5
- data/README.md +5 -3
- data/Rakefile +26 -0
- data/bench/bench.rb +115 -0
- data/bin/.keep +0 -0
- data/examples/app/new.rb +34 -11
- data/lib/rubygems_plugin.rb +6 -0
- data/lib/rustc_installer.rb +96 -0
- data/lib/serde/generate.rb +12 -10
- data/lib/serde/serializer_generator.rb +26 -8
- data/lib/serde.rb +41 -7
- data/serde.gemspec +14 -12
- data/templates/c/serde_rb.c +5 -1
- data/templates/rust/lib.rs +4 -8
- data/templates/rust/mod.rs +2 -0
- metadata +46 -15
- data/Gemfile.lock +0 -63
- data/examples/app/Gemfile +0 -1
- data/examples/app/Gemfile.lock +0 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6451590c25809fc93d4d9a1bcfd3b0792078c0921b6031ec31ce65892ba41c0e
|
4
|
+
data.tar.gz: 4efaecacfadf090538b228274d4555781b04c65ee9a07cef4e6266ec7a5b9400
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: db9fc2fa1159029b2387ff37768bdde5f008d56dc7a40bf5a6e6bee90650ed01fd5185abd0438aa7fc0b155e68cdb2620db37551e448b6e9f1b1a3ef4e493023
|
7
|
+
data.tar.gz: 473f65a45fdebe1d0ed38349441d7810cb4eec559efdf47316f7ce41c4d4bae0da8c691158e9b3503f10467b8ba7ebef6ae8e4f9855d36cabc931940f03e8f61
|
data/.gitignore
CHANGED
data/.rubocop.yml
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
inherit_gem:
|
2
|
+
armitage-rubocop:
|
3
|
+
- lib/rubocop.general.yml
|
4
|
+
- lib/rubocop.rspec.yml
|
5
|
+
|
6
|
+
AllCops:
|
7
|
+
TargetRubyVersion: 2.5.3
|
8
|
+
Include:
|
9
|
+
- serde.gemspec
|
10
|
+
- lib/**/*.rb
|
11
|
+
- spec/**/*.rb
|
12
|
+
- Gemfile
|
13
|
+
- examples/**/*.rb
|
14
|
+
|
15
|
+
Style/TrailingCommaInArguments:
|
16
|
+
EnforcedStyleForMultiline: comma
|
17
|
+
|
18
|
+
Style/TrailingCommaInArrayLiteral:
|
19
|
+
EnforcedStyleForMultiline: comma
|
20
|
+
|
21
|
+
Style/TrailingCommaInHashLiteral:
|
22
|
+
EnforcedStyleForMultiline: comma
|
23
|
+
|
24
|
+
Metrics/AbcSize:
|
25
|
+
Max: 40
|
26
|
+
|
27
|
+
Naming/FileName:
|
28
|
+
Exclude:
|
29
|
+
- examples/app/Gemfile
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -5,15 +5,17 @@ WIP
|
|
5
5
|
# TODO:
|
6
6
|
|
7
7
|
- [ ] Write the README
|
8
|
-
- [
|
8
|
+
- [x] Use a temporary directory instead of `_target_`
|
9
9
|
- [ ] Write specs
|
10
|
-
- [
|
10
|
+
- [x] Set up rubocop
|
11
11
|
- [ ] Set up CI
|
12
12
|
- [ ] Reuse Rust crate, but generate sources in a temp dir
|
13
13
|
- [ ] Cover all basic types (Integer, String, Float, Boolean)
|
14
14
|
- [ ] Nullable types
|
15
15
|
- [ ] Composite types (ArrayOf, HashMap?, Enum)
|
16
16
|
- [ ] Vendor `rustc` with the gem
|
17
|
-
- [
|
17
|
+
- [x] Write benchmarks, compare with Surrealist
|
18
|
+
- [ ] Support any serde-compatible format
|
19
|
+
- [ ] Deserialization
|
18
20
|
- [ ] Allow dynamic hashes?
|
19
21
|
- [ ] Type checks and coercions?
|
data/Rakefile
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/gem_tasks'
|
4
|
+
require 'rspec/core/rake_task'
|
5
|
+
require 'rubocop'
|
6
|
+
require 'rubocop-rspec'
|
7
|
+
require 'rubocop/rake_task'
|
8
|
+
|
9
|
+
RuboCop::RakeTask.new(:rubocop) do |t|
|
10
|
+
config_path = File.expand_path(File.join('.rubocop.yml'), __dir__)
|
11
|
+
|
12
|
+
t.options = ['--config', config_path]
|
13
|
+
t.requires << 'rubocop-rspec'
|
14
|
+
end
|
15
|
+
|
16
|
+
RSpec::Core::RakeTask.new(:rspec)
|
17
|
+
|
18
|
+
task :run_example do
|
19
|
+
require_relative 'examples/app/new.rb'
|
20
|
+
end
|
21
|
+
|
22
|
+
task :bench do
|
23
|
+
require_relative 'bench/bench.rb'
|
24
|
+
end
|
25
|
+
|
26
|
+
task default: :rspec
|
data/bench/bench.rb
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
|
5
|
+
require 'benchmark/ips'
|
6
|
+
require 'serde'
|
7
|
+
require 'surrealist'
|
8
|
+
|
9
|
+
class User
|
10
|
+
attr_reader :id, :first_name, :last_name, :email, :age
|
11
|
+
|
12
|
+
def initialize(id, first_name, last_name, email, age)
|
13
|
+
@id = id
|
14
|
+
@first_name = first_name
|
15
|
+
@last_name = last_name
|
16
|
+
@email = email
|
17
|
+
@age = age
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class UserSerdeSerializer < Serde::Serializer
|
22
|
+
schema(
|
23
|
+
id: Integer,
|
24
|
+
first_name: String,
|
25
|
+
last_name: String,
|
26
|
+
email: String,
|
27
|
+
age: Integer
|
28
|
+
)
|
29
|
+
end
|
30
|
+
|
31
|
+
class UserSurrealistSerializer < Surrealist::Serializer
|
32
|
+
json_schema do
|
33
|
+
{
|
34
|
+
id: Integer,
|
35
|
+
first_name: String,
|
36
|
+
last_name: String,
|
37
|
+
email: String,
|
38
|
+
age: Integer
|
39
|
+
}
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class UserOjSerializer
|
44
|
+
def initialize(user)
|
45
|
+
@user = user
|
46
|
+
end
|
47
|
+
|
48
|
+
def to_json
|
49
|
+
Oj.dump(
|
50
|
+
id: @user.id,
|
51
|
+
first_name: @user.first_name,
|
52
|
+
last_name: @user.last_name,
|
53
|
+
email: @user.email,
|
54
|
+
age: @user.age
|
55
|
+
)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
user = User.new(1, 'Pavel', 'Yakimov', 'mrpavel@example.com', 24)
|
60
|
+
serde_serializer = UserSerdeSerializer.new(user)
|
61
|
+
surrealist_serializer = UserSurrealistSerializer.new(user)
|
62
|
+
oj_serializer = UserOjSerializer.new(user)
|
63
|
+
|
64
|
+
puts "-------\nSerialize to JSON\n-------"
|
65
|
+
Benchmark.ips do |x|
|
66
|
+
x.report('serde') { serde_serializer.to_json }
|
67
|
+
x.report('Surrealist') { surrealist_serializer.surrealize }
|
68
|
+
x.report('Oj') { oj_serializer.to_json }
|
69
|
+
|
70
|
+
x.compare!
|
71
|
+
end
|
72
|
+
|
73
|
+
puts "-------\nInitialize + serialize to JSON\n-------"
|
74
|
+
Benchmark.ips do |x|
|
75
|
+
x.report('serde') { UserSerdeSerializer.new(user).to_json }
|
76
|
+
x.report('Surrealist') { UserSurrealistSerializer.new(user).surrealize }
|
77
|
+
x.report('Oj') { UserOjSerializer.new(user).to_json }
|
78
|
+
|
79
|
+
x.compare!
|
80
|
+
end
|
81
|
+
|
82
|
+
# -------
|
83
|
+
# Serialize to JSON
|
84
|
+
# -------
|
85
|
+
# Warming up --------------------------------------
|
86
|
+
# serde 76.802k i/100ms
|
87
|
+
# Surrealist 3.064k i/100ms
|
88
|
+
# Oj 51.183k i/100ms
|
89
|
+
# Calculating -------------------------------------
|
90
|
+
# serde 898.268k (± 5.1%) i/s - 4.531M in 5.058080s
|
91
|
+
# Surrealist 36.268k (± 3.2%) i/s - 183.840k in 5.074885s
|
92
|
+
# Oj 689.594k (± 3.4%) i/s - 3.480M in 5.053357s
|
93
|
+
|
94
|
+
# Comparison:
|
95
|
+
# serde: 898267.9 i/s
|
96
|
+
# Oj: 689593.9 i/s - 1.30x slower
|
97
|
+
# Surrealist: 36268.1 i/s - 24.77x slower
|
98
|
+
|
99
|
+
# -------
|
100
|
+
# Initialize + serialize to JSON
|
101
|
+
# -------
|
102
|
+
# Warming up --------------------------------------
|
103
|
+
# serde 41.158k i/100ms
|
104
|
+
# Surrealist 3.087k i/100ms
|
105
|
+
# Oj 51.710k i/100ms
|
106
|
+
# Calculating -------------------------------------
|
107
|
+
# serde 451.324k (± 1.4%) i/s - 2.264M in 5.016647s
|
108
|
+
# Surrealist 33.478k (± 6.8%) i/s - 166.698k in 5.003821s
|
109
|
+
# Oj 635.098k (± 2.9%) i/s - 3.206M in 5.052618s
|
110
|
+
|
111
|
+
# Comparison:
|
112
|
+
# Oj: 635097.9 i/s
|
113
|
+
# serde: 451323.7 i/s - 1.41x slower
|
114
|
+
# Surrealist: 33478.1 i/s - 18.97x slower
|
115
|
+
|
data/bin/.keep
ADDED
File without changes
|
data/examples/app/new.rb
CHANGED
@@ -3,20 +3,43 @@
|
|
3
3
|
require 'bundler/setup'
|
4
4
|
require 'serde'
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
id:
|
9
|
-
|
10
|
-
|
6
|
+
module Kek
|
7
|
+
class Cow
|
8
|
+
attr_reader :id, :name
|
9
|
+
|
10
|
+
def initialize(id, name)
|
11
|
+
@id = id
|
12
|
+
@name = name
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class CowSerializer < Serde::Serializer
|
17
|
+
schema(
|
18
|
+
id: Integer,
|
19
|
+
name: String,
|
20
|
+
)
|
21
|
+
end
|
11
22
|
end
|
12
23
|
|
13
|
-
|
14
|
-
|
24
|
+
module Pek
|
25
|
+
class Car
|
26
|
+
attr_reader :id, :brand, :model
|
27
|
+
|
28
|
+
def initialize(id, brand, model)
|
29
|
+
@id = id
|
30
|
+
@brand = brand
|
31
|
+
@model = model
|
32
|
+
end
|
33
|
+
end
|
15
34
|
|
16
|
-
|
17
|
-
|
18
|
-
|
35
|
+
class CarSerializer < Serde::Serializer
|
36
|
+
schema(
|
37
|
+
id: Integer,
|
38
|
+
brand: String,
|
39
|
+
model: String,
|
40
|
+
)
|
19
41
|
end
|
20
42
|
end
|
21
43
|
|
22
|
-
puts CowSerializer.new(Cow.new(1, 'Ковыч')).to_json
|
44
|
+
puts Kek::CowSerializer.new(Kek::Cow.new(1, 'Ковыч')).to_json
|
45
|
+
puts Pek::CarSerializer.new(Pek::Car.new(1, 'Mazda', '6')).to_json
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# TODO: test on different systems and architectures
|
4
|
+
|
5
|
+
require 'tmpdir'
|
6
|
+
require 'net/http'
|
7
|
+
require 'fileutils'
|
8
|
+
require 'rubygems/package'
|
9
|
+
require 'zlib'
|
10
|
+
|
11
|
+
# https://github.com/rust-lang/rustup.rs/blob/243e3d5889a58116fb7eb910e8508b9d0d0a0b04/rustup-init.sh#L172
|
12
|
+
# not considering android and mips for now
|
13
|
+
ostype = `uname -s`.chomp
|
14
|
+
cputype = `uname -m`.chomp
|
15
|
+
|
16
|
+
if ostype == 'Darwin' && cputype == 'i386' && !`sysctl hw.optional.x86_64 | grep -q ': 1'`.nil?
|
17
|
+
cputype = 'x86_64'
|
18
|
+
end
|
19
|
+
|
20
|
+
ostype =
|
21
|
+
case ostype
|
22
|
+
when 'Linux' then 'unknown-linux-gnu'
|
23
|
+
when 'FreeBSD' then 'unknown-freebsd'
|
24
|
+
when 'NetBSD' then 'unknown-netbsd'
|
25
|
+
when 'DragonFly' then 'unknown-dragonfly'
|
26
|
+
when 'Darwin' then 'apple-darwin'
|
27
|
+
when /MINGW/, /MSYS/, /CYGWIN/ then 'pc-windows-gnu'
|
28
|
+
else raise "unrecognized OS type: #{ostype}"
|
29
|
+
end
|
30
|
+
|
31
|
+
cputype =
|
32
|
+
case cputype
|
33
|
+
when 'i386', 'i486', 'i686', 'i786', 'x86' then 'i686'
|
34
|
+
when 'xscale', 'arm' then 'arm'
|
35
|
+
when 'armv6l' then 'armeabihf'
|
36
|
+
when 'armv7l', 'armv8l' then 'armv7eabihf'
|
37
|
+
when 'aarch64' then 'aarch64'
|
38
|
+
when 'x86_64', 'x86-64', 'x64', 'amd64' then 'x86_64'
|
39
|
+
when 'ppc' then 'powerpc'
|
40
|
+
when 'ppc64' then 'powerpc64'
|
41
|
+
when 'ppc64le' then 'powerpc64le'
|
42
|
+
else raise "unknown CPU type: #{cputype}"
|
43
|
+
end
|
44
|
+
|
45
|
+
rustc_name = "rustc-beta-#{cputype}-#{ostype}"
|
46
|
+
ruststd_name = "rust-std-beta-#{cputype}-#{ostype}"
|
47
|
+
TAR_LONGLINK = '././@LongLink'
|
48
|
+
DESTINATION = File.expand_path('../.rustc', __dir__)
|
49
|
+
|
50
|
+
def download_and_extract(name) # rubocop:disable Metrics/MethodLength
|
51
|
+
File.write(
|
52
|
+
"#{name}.tar.gz",
|
53
|
+
Net::HTTP.get(URI("https://static.rust-lang.org/dist/2018-11-27/#{name}.tar.gz")),
|
54
|
+
)
|
55
|
+
|
56
|
+
# https://stackoverflow.com/a/19139114
|
57
|
+
Gem::Package::TarReader.new(Zlib::GzipReader.open("#{name}.tar.gz")) do |tar|
|
58
|
+
dest = nil
|
59
|
+
tar.each do |entry|
|
60
|
+
if entry.full_name == TAR_LONGLINK
|
61
|
+
dest = File.join DESTINATION, entry.read.strip
|
62
|
+
next
|
63
|
+
end
|
64
|
+
dest ||= File.join DESTINATION, entry.full_name
|
65
|
+
if entry.directory?
|
66
|
+
File.delete dest if File.file? dest
|
67
|
+
FileUtils.mkdir_p dest, mode: entry.header.mode, verbose: false
|
68
|
+
elsif entry.file?
|
69
|
+
FileUtils.rm_rf dest if File.directory? dest
|
70
|
+
File.open dest, 'wb' do |f|
|
71
|
+
f.print entry.read
|
72
|
+
end
|
73
|
+
FileUtils.chmod entry.header.mode, dest, verbose: false
|
74
|
+
elsif entry.header.typeflag == '2' # Symlink!
|
75
|
+
File.symlink entry.header.linkname, dest
|
76
|
+
end
|
77
|
+
dest = nil
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
Dir.mktmpdir do |dir|
|
83
|
+
Dir.chdir(dir) do
|
84
|
+
# manifest = Net::HTTP.get(URI('https://static.rust-lang.org/dist/channel-rust-beta.toml'))
|
85
|
+
# puts manifest
|
86
|
+
|
87
|
+
download_and_extract(rustc_name)
|
88
|
+
download_and_extract(ruststd_name)
|
89
|
+
|
90
|
+
# TODO: windows?
|
91
|
+
FileUtils.ln_s(
|
92
|
+
"#{DESTINATION}/#{rustc_name}/rustc/bin/rustc",
|
93
|
+
File.expand_path('../bin/rustc', __dir__),
|
94
|
+
)
|
95
|
+
end
|
96
|
+
end
|
data/lib/serde/generate.rb
CHANGED
@@ -1,14 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'erb'
|
2
4
|
|
3
|
-
def
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
end
|
5
|
+
def underscore(str)
|
6
|
+
str.tr('::', '/')
|
7
|
+
.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
|
8
|
+
.gsub(/([a-z\d])([A-Z])/, '\1_\2')
|
9
|
+
.tr('-', '_')
|
10
|
+
.downcase
|
11
|
+
end
|
11
12
|
|
13
|
+
def generate_serializers # rubocop:disable Metrics/MethodLength
|
12
14
|
modules = []
|
13
15
|
|
14
16
|
Serde.subclasses.each do |klass|
|
@@ -32,7 +34,7 @@ def generate_serializers
|
|
32
34
|
fields: fields,
|
33
35
|
joint_fields: fields.map do |field|
|
34
36
|
"#{field[:name]}: #{field[:type]}"
|
35
|
-
end.join(', ')
|
37
|
+
end.join(', '),
|
36
38
|
}
|
37
39
|
|
38
40
|
mod_template = ERB.new(File.read('./rust_template/mod.rs.erb'))
|
@@ -44,7 +46,7 @@ def generate_serializers
|
|
44
46
|
lib_template = ERB.new(File.read('./rust_template/lib.rs.erb'))
|
45
47
|
compiled_template = lib_template.result(binding)
|
46
48
|
|
47
|
-
File.open(
|
49
|
+
File.open('./src/lib.rs', 'w') { |f| f.write(compiled_template) }
|
48
50
|
|
49
51
|
`rake build`
|
50
52
|
end
|
@@ -1,11 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'fileutils'
|
3
4
|
require 'erb'
|
4
5
|
|
5
6
|
module Serde
|
6
7
|
module SerializerGenerator
|
7
8
|
class << self
|
8
|
-
|
9
|
+
# rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity
|
10
|
+
def call(dir, klass)
|
11
|
+
FileUtils.cp_r(Dir.glob(File.expand_path('../../templates/extension/*', __dir__)), '.')
|
12
|
+
|
9
13
|
schema = klass.instance_variable_get(:@schema)
|
10
14
|
|
11
15
|
name = underscore(klass.name)
|
@@ -37,6 +41,18 @@ module Serde
|
|
37
41
|
{ name: k, type: type, rctype: rctype, cdecl: cdecl, ctype: ctype }
|
38
42
|
end
|
39
43
|
|
44
|
+
rust_extras = []
|
45
|
+
|
46
|
+
schema.each do |k, v|
|
47
|
+
next unless v.to_s == 'String'
|
48
|
+
|
49
|
+
rust_extras.push(<<~RUST)
|
50
|
+
let #{k} = unsafe {
|
51
|
+
CStr::from_ptr(#{k}).to_string_lossy().into_owned()
|
52
|
+
};
|
53
|
+
RUST
|
54
|
+
end
|
55
|
+
|
40
56
|
serializer = {
|
41
57
|
class_name: klass.name,
|
42
58
|
name: name,
|
@@ -50,28 +66,30 @@ module Serde
|
|
50
66
|
joint_fields_c: fields.map do |field|
|
51
67
|
"#{field[:ctype]} #{field[:name]}"
|
52
68
|
end.join(', '),
|
69
|
+
rust_extras: rust_extras,
|
53
70
|
}
|
54
71
|
|
55
|
-
mod_template = ERB.new(File.read('
|
72
|
+
mod_template = ERB.new(File.read(File.expand_path('../../templates/rust/mod.rs', __dir__)))
|
56
73
|
compiled_template = mod_template.result(binding)
|
57
74
|
|
58
|
-
File.write("
|
75
|
+
File.write(File.expand_path("../../rust/src/#{name}.rs", __dir__), compiled_template)
|
59
76
|
|
60
|
-
c_template = ERB.new(File.read('
|
77
|
+
c_template = ERB.new(File.read(File.expand_path('../../templates/c/serde_rb.c', __dir__)))
|
61
78
|
compiled_template = c_template.result(binding)
|
62
79
|
|
63
|
-
File.write('./
|
80
|
+
File.write('./serde_rb/serde_rb.c', compiled_template)
|
64
81
|
|
65
|
-
lib_template = ERB.new(File.read('
|
82
|
+
lib_template = ERB.new(File.read(File.expand_path('../../templates/rust/lib.rs', __dir__)))
|
66
83
|
compiled_template = lib_template.result(binding)
|
67
84
|
|
68
|
-
File.write(
|
85
|
+
File.write(File.expand_path('../../rust/src/lib.rs', __dir__), compiled_template)
|
69
86
|
end
|
87
|
+
# rubocop:enable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity
|
70
88
|
|
71
89
|
private
|
72
90
|
|
73
91
|
def underscore(str)
|
74
|
-
str.tr('::', '
|
92
|
+
str.tr('::', '__')
|
75
93
|
.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
|
76
94
|
.gsub(/([a-z\d])([A-Z])/, '\1_\2')
|
77
95
|
.tr('-', '_')
|
data/lib/serde.rb
CHANGED
@@ -1,20 +1,54 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'tmpdir'
|
2
4
|
require_relative 'serde/serializer_generator.rb'
|
3
5
|
|
4
|
-
|
5
|
-
|
6
|
+
trace = TracePoint.new(:end) do |tp|
|
7
|
+
klass = tp.self
|
8
|
+
if Serde::Serializer.descendants.include?(klass)
|
9
|
+
klass.compile!
|
10
|
+
end
|
11
|
+
end
|
12
|
+
trace.enable
|
6
13
|
|
7
14
|
module Serde
|
8
15
|
class Serializer
|
16
|
+
@descendants = []
|
17
|
+
@compiled = false
|
18
|
+
|
9
19
|
class << self
|
20
|
+
attr_reader :descendants
|
21
|
+
|
22
|
+
def inherited(klass)
|
23
|
+
@descendants << klass
|
24
|
+
end
|
25
|
+
|
10
26
|
def schema(**attrs)
|
11
27
|
@schema = attrs
|
12
|
-
|
13
|
-
|
14
|
-
|
28
|
+
end
|
29
|
+
|
30
|
+
def compile!
|
31
|
+
raise 'already compiled' if @compiled
|
32
|
+
|
33
|
+
Dir.mktmpdir do |dir|
|
34
|
+
Dir.chdir(dir) do
|
35
|
+
SerializerGenerator.call(dir, self)
|
36
|
+
|
37
|
+
root_path = File.expand_path('..', __dir__)
|
38
|
+
rust_path = File.expand_path('../rust', __dir__)
|
39
|
+
bin_path = File.expand_path('../bin', __dir__)
|
40
|
+
|
41
|
+
# rubocop:disable Metrics/LineLength
|
42
|
+
`cd #{rust_path}; #{bin_path}/rustc --edition=2018 --crate-name serde_rb src/lib.rs --crate-type staticlib --crate-type cdylib --emit=dep-info,link -C opt-level=3 -C metadata=aff563bf4af79579 --out-dir /Users/cow/projects/serde/rust/target/release -L dependency=/Users/cow/projects/serde/rust/target/release/deps --extern serde=/Users/cow/projects/serde/rust/target/release/deps/libserde-6a9e4ab445963d7f.rlib --extern serde_derive=/Users/cow/projects/serde/rust/target/release/deps/libserde_derive-20596753c2ed9015.dylib --extern serde_json=/Users/cow/projects/serde/rust/target/release/deps/libserde_json-489658cb61f64325.rlib -L #{root_path}/.rustc/rust-std-beta-x86_64-apple-darwin/rust-std-x86_64-apple-darwin/lib/rustlib/x86_64-apple-darwin/lib`
|
43
|
+
# rubocop:enable Metrics/LineLength
|
44
|
+
`cp #{rust_path}/target/release/libserde_rb*.a #{dir}/serde_rb/libserde_rb.a`
|
45
|
+
`cd #{dir}/serde_rb; ruby extconf.rb; make clean; make`
|
46
|
+
|
47
|
+
require_relative "#{dir}/serde_rb/serde_rb"
|
48
|
+
end
|
15
49
|
end
|
16
50
|
|
17
|
-
|
51
|
+
@compiled = true
|
18
52
|
end
|
19
53
|
|
20
54
|
def get_schema
|
data/serde.gemspec
CHANGED
@@ -4,17 +4,17 @@ lib = File.expand_path('../lib', __dir__)
|
|
4
4
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
5
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
|
-
spec.required_ruby_version = '>= 2.5.
|
7
|
+
spec.required_ruby_version = '>= 2.5.3'
|
8
8
|
|
9
|
-
spec.name
|
10
|
-
spec.version = '0.0.
|
9
|
+
spec.name = 'serde'
|
10
|
+
spec.version = '0.0.2'
|
11
11
|
spec.authors = ['Alexander Komarov']
|
12
|
-
spec.email
|
12
|
+
spec.email = %w[ak@akxcv.com]
|
13
13
|
|
14
|
-
spec.summary
|
15
|
-
spec.description =
|
16
|
-
spec.homepage
|
17
|
-
spec.license
|
14
|
+
spec.summary = 'Fast, compiled serializers for Ruby.'
|
15
|
+
spec.description = 'Fast, compiled serializers for Ruby. Powered by serde.rs.'
|
16
|
+
spec.homepage = 'https://github.com/akxcv/serde.rb'
|
17
|
+
spec.license = 'MIT'
|
18
18
|
|
19
19
|
spec.require_paths = %w[lib]
|
20
20
|
|
@@ -22,8 +22,10 @@ Gem::Specification.new do |spec|
|
|
22
22
|
f.match(%r{^(test|spec|features)/})
|
23
23
|
end
|
24
24
|
|
25
|
-
spec.add_development_dependency '
|
26
|
-
spec.add_development_dependency '
|
27
|
-
spec.add_development_dependency 'rspec',
|
28
|
-
spec.add_development_dependency 'rubocop', '~> 0.
|
25
|
+
spec.add_development_dependency 'surrealist', '~> 1.3'
|
26
|
+
spec.add_development_dependency 'benchmark-ips', '~> 2.7'
|
27
|
+
spec.add_development_dependency 'rspec', '~> 3.8'
|
28
|
+
spec.add_development_dependency 'armitage-rubocop', '~> 0.12'
|
29
|
+
spec.add_development_dependency 'bundler', '~> 1.17'
|
30
|
+
spec.add_development_dependency 'pry', '~> 0.12'
|
29
31
|
end
|
data/templates/c/serde_rb.c
CHANGED
@@ -9,7 +9,11 @@ VALUE c_to_json(VALUE _instance, <%= serializer[:fields].map { |f| "VALUE #{f[:n
|
|
9
9
|
}
|
10
10
|
|
11
11
|
void Init_serde_rb(void) {
|
12
|
-
VALUE klass =
|
12
|
+
VALUE klass = rb_eval_string("Object.const_get('<%= serializer[:class_name] %>')");
|
13
|
+
/**
|
14
|
+
* FIXME: `rb_const_get` doesn't work with nested modules
|
15
|
+
*/
|
16
|
+
// VALUE klass = rb_const_get(rb_cObject, rb_intern("<%= serializer[:class_name] %>"));
|
13
17
|
|
14
18
|
rb_define_method(klass, "internal_to_json", c_to_json, <%= serializer[:fields].length %>);
|
15
19
|
}
|
data/templates/rust/lib.rs
CHANGED
@@ -1,7 +1,5 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
extern crate serde_derive;
|
4
|
-
extern crate serde_json;
|
1
|
+
#![allow(non_snake_case)]
|
2
|
+
|
5
3
|
use std::os::raw::c_char;
|
6
4
|
use std::ffi::CStr;
|
7
5
|
|
@@ -9,13 +7,11 @@ mod <%= serializer[:name] %>;
|
|
9
7
|
|
10
8
|
#[no_mangle]
|
11
9
|
pub extern "C" fn serde_rb_rs_<%= serializer[:name] %> (<%= serializer[:joint_fields_rctype] %>) -> *const u8 {
|
12
|
-
|
13
|
-
CStr::from_ptr(name).to_string_lossy().into_owned()
|
14
|
-
};
|
10
|
+
<%= serializer[:rust_extras].join("\n") %>
|
15
11
|
let instance = <%= serializer[:name] %>::Struct {
|
16
12
|
<% serializer[:fields].each do |field| %>
|
17
13
|
<%= field[:name] %>,
|
18
14
|
<% end %>
|
19
15
|
};
|
20
|
-
serde_json::to_string(&instance).unwrap().as_ptr()
|
16
|
+
(serde_json::to_string(&instance).unwrap() + "\0").as_ptr()
|
21
17
|
}
|
data/templates/rust/mod.rs
CHANGED
metadata
CHANGED
@@ -1,43 +1,43 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: serde
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alexander Komarov
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-11-
|
11
|
+
date: 2018-11-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: surrealist
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '1.
|
19
|
+
version: '1.3'
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '1.
|
26
|
+
version: '1.3'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: benchmark-ips
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
33
|
+
version: '2.7'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
40
|
+
version: '2.7'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: rspec
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -53,19 +53,47 @@ dependencies:
|
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '3.8'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
|
-
name: rubocop
|
56
|
+
name: armitage-rubocop
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.12'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0.12'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: bundler
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '1.17'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '1.17'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: pry
|
57
85
|
requirement: !ruby/object:Gem::Requirement
|
58
86
|
requirements:
|
59
87
|
- - "~>"
|
60
88
|
- !ruby/object:Gem::Version
|
61
|
-
version: '0.
|
89
|
+
version: '0.12'
|
62
90
|
type: :development
|
63
91
|
prerelease: false
|
64
92
|
version_requirements: !ruby/object:Gem::Requirement
|
65
93
|
requirements:
|
66
94
|
- - "~>"
|
67
95
|
- !ruby/object:Gem::Version
|
68
|
-
version: '0.
|
96
|
+
version: '0.12'
|
69
97
|
description: Fast, compiled serializers for Ruby. Powered by serde.rs.
|
70
98
|
email:
|
71
99
|
- ak@akxcv.com
|
@@ -74,13 +102,16 @@ extensions: []
|
|
74
102
|
extra_rdoc_files: []
|
75
103
|
files:
|
76
104
|
- ".gitignore"
|
105
|
+
- ".rubocop.yml"
|
77
106
|
- Gemfile
|
78
|
-
- Gemfile.lock
|
79
107
|
- LICENSE
|
80
108
|
- README.md
|
81
|
-
-
|
82
|
-
-
|
109
|
+
- Rakefile
|
110
|
+
- bench/bench.rb
|
111
|
+
- bin/.keep
|
83
112
|
- examples/app/new.rb
|
113
|
+
- lib/rubygems_plugin.rb
|
114
|
+
- lib/rustc_installer.rb
|
84
115
|
- lib/serde.rb
|
85
116
|
- lib/serde/generate.rb
|
86
117
|
- lib/serde/serializer_generator.rb
|
@@ -105,7 +136,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
105
136
|
requirements:
|
106
137
|
- - ">="
|
107
138
|
- !ruby/object:Gem::Version
|
108
|
-
version: 2.5.
|
139
|
+
version: 2.5.3
|
109
140
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
110
141
|
requirements:
|
111
142
|
- - ">="
|
data/Gemfile.lock
DELETED
@@ -1,63 +0,0 @@
|
|
1
|
-
PATH
|
2
|
-
remote: .
|
3
|
-
specs:
|
4
|
-
serde (0.1.0)
|
5
|
-
|
6
|
-
GEM
|
7
|
-
remote: https://rubygems.org/
|
8
|
-
specs:
|
9
|
-
ast (2.4.0)
|
10
|
-
benchmark-ips (2.7.2)
|
11
|
-
coderay (1.1.2)
|
12
|
-
diff-lcs (1.3)
|
13
|
-
jaro_winkler (1.5.1)
|
14
|
-
method_source (0.9.2)
|
15
|
-
oj (3.7.1)
|
16
|
-
parallel (1.12.1)
|
17
|
-
parser (2.5.3.0)
|
18
|
-
ast (~> 2.4.0)
|
19
|
-
powerpack (0.1.2)
|
20
|
-
pry (0.12.2)
|
21
|
-
coderay (~> 1.1.0)
|
22
|
-
method_source (~> 0.9.0)
|
23
|
-
rainbow (3.0.0)
|
24
|
-
rspec (3.8.0)
|
25
|
-
rspec-core (~> 3.8.0)
|
26
|
-
rspec-expectations (~> 3.8.0)
|
27
|
-
rspec-mocks (~> 3.8.0)
|
28
|
-
rspec-core (3.8.0)
|
29
|
-
rspec-support (~> 3.8.0)
|
30
|
-
rspec-expectations (3.8.2)
|
31
|
-
diff-lcs (>= 1.2.0, < 2.0)
|
32
|
-
rspec-support (~> 3.8.0)
|
33
|
-
rspec-mocks (3.8.0)
|
34
|
-
diff-lcs (>= 1.2.0, < 2.0)
|
35
|
-
rspec-support (~> 3.8.0)
|
36
|
-
rspec-support (3.8.0)
|
37
|
-
rubocop (0.60.0)
|
38
|
-
jaro_winkler (~> 1.5.1)
|
39
|
-
parallel (~> 1.10)
|
40
|
-
parser (>= 2.5, != 2.5.1.1)
|
41
|
-
powerpack (~> 0.1)
|
42
|
-
rainbow (>= 2.2.2, < 4.0)
|
43
|
-
ruby-progressbar (~> 1.7)
|
44
|
-
unicode-display_width (~> 1.4.0)
|
45
|
-
ruby-progressbar (1.10.0)
|
46
|
-
surrealist (1.3.1)
|
47
|
-
oj (~> 3.0)
|
48
|
-
unicode-display_width (1.4.0)
|
49
|
-
|
50
|
-
PLATFORMS
|
51
|
-
ruby
|
52
|
-
|
53
|
-
DEPENDENCIES
|
54
|
-
benchmark-ips
|
55
|
-
bundler (~> 1.17)
|
56
|
-
pry (~> 0.12)
|
57
|
-
rspec (~> 3.8)
|
58
|
-
rubocop (~> 0.60)
|
59
|
-
serde!
|
60
|
-
surrealist
|
61
|
-
|
62
|
-
BUNDLED WITH
|
63
|
-
1.17.1
|
data/examples/app/Gemfile
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
gem 'serde', path: '../..'
|