serde 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- 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: '../..'
|