paquito 0.3.1 → 0.4.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 +4 -4
- data/.github/workflows/ci.yml +15 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +29 -21
- data/README.md +14 -13
- data/benchmark/msgpack-pooling.rb +19 -0
- data/lib/paquito/allow_nil.rb +2 -0
- data/lib/paquito/codec_factory.rb +21 -8
- data/lib/paquito/safe_yaml.rb +1 -0
- data/lib/paquito/typed_struct.rb +1 -0
- data/lib/paquito/types.rb +39 -11
- data/lib/paquito/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dd3a891430b40fa7fad9d50b8f383070cee22cba06d19c0db67670324c575613
|
4
|
+
data.tar.gz: 752e27818f5781652fc91407b51a7f5c16e0a8a07baa0f1285b3e4a3c9029bb0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7b25933ab48ec3327e7e2c781f6502f90ed7a440163e58c3255d43bb1473fc5da5414b44d85f06de7f0d90ef05d57871bd8fcb3aabd22caa19d97df3422240d2
|
7
|
+
data.tar.gz: aca4aa6e5d417d40926d9cf57045157d1eaffdae225cc051b97ad861e0dc2d19fecf1f6acdea1a9fa91982697cee231326d410a21b0c130f327fc21f967c914e
|
data/.github/workflows/ci.yml
CHANGED
@@ -3,6 +3,21 @@ name: CI
|
|
3
3
|
on: [push, pull_request]
|
4
4
|
|
5
5
|
jobs:
|
6
|
+
rubocop:
|
7
|
+
runs-on: ubuntu-latest
|
8
|
+
strategy:
|
9
|
+
fail-fast: false
|
10
|
+
steps:
|
11
|
+
- name: Checkout
|
12
|
+
uses: actions/checkout@v2
|
13
|
+
- name: Set up Ruby
|
14
|
+
uses: ruby/setup-ruby@v1
|
15
|
+
with:
|
16
|
+
ruby-version: '2.7'
|
17
|
+
bundler-cache: true
|
18
|
+
- name: Run test
|
19
|
+
run: bundle exec rubocop
|
20
|
+
|
6
21
|
rubies:
|
7
22
|
runs-on: ubuntu-latest
|
8
23
|
strategy:
|
data/Gemfile
CHANGED
@@ -5,10 +5,13 @@ source "https://rubygems.org"
|
|
5
5
|
# Specify your gem's dependencies in paquito.gemspec
|
6
6
|
gemspec
|
7
7
|
|
8
|
+
gem "msgpack", github: "msgpack/msgpack-ruby"
|
9
|
+
|
8
10
|
gem "rake", "~> 13.0"
|
9
11
|
gem "activesupport", ">= 7.0.0"
|
10
12
|
gem "activerecord", ">= 7.0.0"
|
11
13
|
gem "sqlite3"
|
14
|
+
gem "benchmark-ips"
|
12
15
|
|
13
16
|
gem "minitest", "~> 5.0"
|
14
17
|
|
data/Gemfile.lock
CHANGED
@@ -1,51 +1,57 @@
|
|
1
|
+
GIT
|
2
|
+
remote: https://github.com/msgpack/msgpack-ruby.git
|
3
|
+
revision: b69e6d6965ef46890885ddf73f02b820f3185d80
|
4
|
+
specs:
|
5
|
+
msgpack (1.4.5)
|
6
|
+
|
1
7
|
PATH
|
2
8
|
remote: .
|
3
9
|
specs:
|
4
|
-
paquito (0.
|
10
|
+
paquito (0.4.0)
|
5
11
|
msgpack
|
6
12
|
|
7
13
|
GEM
|
8
14
|
remote: https://rubygems.org/
|
9
15
|
specs:
|
10
|
-
activemodel (7.0.
|
11
|
-
activesupport (= 7.0.
|
12
|
-
activerecord (7.0.
|
13
|
-
activemodel (= 7.0.
|
14
|
-
activesupport (= 7.0.
|
15
|
-
activesupport (7.0.
|
16
|
+
activemodel (7.0.2.3)
|
17
|
+
activesupport (= 7.0.2.3)
|
18
|
+
activerecord (7.0.2.3)
|
19
|
+
activemodel (= 7.0.2.3)
|
20
|
+
activesupport (= 7.0.2.3)
|
21
|
+
activesupport (7.0.2.3)
|
16
22
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
17
23
|
i18n (>= 1.6, < 2)
|
18
24
|
minitest (>= 5.1)
|
19
25
|
tzinfo (~> 2.0)
|
20
26
|
ast (2.4.2)
|
27
|
+
benchmark-ips (2.10.0)
|
21
28
|
byebug (11.1.3)
|
22
29
|
concurrent-ruby (1.1.9)
|
23
|
-
i18n (1.
|
30
|
+
i18n (1.10.0)
|
24
31
|
concurrent-ruby (~> 1.0)
|
25
|
-
minitest (5.
|
26
|
-
msgpack (1.4.4)
|
32
|
+
minitest (5.15.0)
|
27
33
|
parallel (1.21.0)
|
28
|
-
parser (3.
|
34
|
+
parser (3.1.1.0)
|
29
35
|
ast (~> 2.4.1)
|
30
|
-
rainbow (3.
|
36
|
+
rainbow (3.1.1)
|
31
37
|
rake (13.0.6)
|
32
|
-
regexp_parser (2.
|
38
|
+
regexp_parser (2.2.1)
|
33
39
|
rexml (3.2.5)
|
34
|
-
rubocop (1.
|
40
|
+
rubocop (1.25.1)
|
35
41
|
parallel (~> 1.10)
|
36
|
-
parser (>= 3.
|
42
|
+
parser (>= 3.1.0.0)
|
37
43
|
rainbow (>= 2.2.2, < 4.0)
|
38
44
|
regexp_parser (>= 1.8, < 3.0)
|
39
45
|
rexml
|
40
|
-
rubocop-ast (>= 1.
|
46
|
+
rubocop-ast (>= 1.15.1, < 2.0)
|
41
47
|
ruby-progressbar (~> 1.7)
|
42
48
|
unicode-display_width (>= 1.4.0, < 3.0)
|
43
|
-
rubocop-ast (1.
|
44
|
-
parser (>= 3.
|
45
|
-
rubocop-shopify (2.
|
46
|
-
rubocop (~> 1.
|
49
|
+
rubocop-ast (1.16.0)
|
50
|
+
parser (>= 3.1.1.0)
|
51
|
+
rubocop-shopify (2.5.0)
|
52
|
+
rubocop (~> 1.25)
|
47
53
|
ruby-progressbar (1.11.0)
|
48
|
-
sorbet-runtime (0.5.
|
54
|
+
sorbet-runtime (0.5.9742)
|
49
55
|
sqlite3 (1.4.2)
|
50
56
|
tzinfo (2.0.4)
|
51
57
|
concurrent-ruby (~> 1.0)
|
@@ -59,8 +65,10 @@ PLATFORMS
|
|
59
65
|
DEPENDENCIES
|
60
66
|
activerecord (>= 7.0.0)
|
61
67
|
activesupport (>= 7.0.0)
|
68
|
+
benchmark-ips
|
62
69
|
byebug
|
63
70
|
minitest (~> 5.0)
|
71
|
+
msgpack!
|
64
72
|
paquito!
|
65
73
|
rake (~> 13.0)
|
66
74
|
rubocop
|
data/README.md
CHANGED
@@ -22,7 +22,7 @@ Or install it yourself as:
|
|
22
22
|
|
23
23
|
### `chain`
|
24
24
|
|
25
|
-
`Paquito::CoderChain`
|
25
|
+
`Paquito::CoderChain` combines two or more serializers into one.
|
26
26
|
|
27
27
|
Example:
|
28
28
|
|
@@ -34,7 +34,7 @@ compressed_yaml_coder.load(payload) # => { foo: 42 }
|
|
34
34
|
|
35
35
|
### `ConditionalCompressor`
|
36
36
|
|
37
|
-
`Paquito::ConditionalCompressor`
|
37
|
+
`Paquito::ConditionalCompressor` compresses payloads if they are over a defined size.
|
38
38
|
|
39
39
|
Example:
|
40
40
|
```ruby
|
@@ -45,8 +45,8 @@ coder.dump("foo" * 500) # => "\x01<compressed-data....>"
|
|
45
45
|
|
46
46
|
### `SingleBytePrefixVersion`
|
47
47
|
|
48
|
-
`Paquito::SingleBytePrefixVersion` prepends a version prefix to the payloads, which
|
49
|
-
different serialization methods.
|
48
|
+
`Paquito::SingleBytePrefixVersion` prepends a version prefix to the payloads, which allows you to seamlessly transition
|
49
|
+
between different serialization methods.
|
50
50
|
|
51
51
|
The first argument is the current version used for newly generated payloads.
|
52
52
|
|
@@ -64,9 +64,9 @@ coder.load("\x00---\n:foo: 42") # => { foo: 42 }
|
|
64
64
|
|
65
65
|
### `CommentPrefixVersion`
|
66
66
|
|
67
|
-
Similar to the single byte prefix, but meant to be human readable and to allow for migrating
|
67
|
+
Similar to the single byte prefix, but meant to be human readable and to allow for migrating unversioned payloads.
|
68
68
|
|
69
|
-
|
69
|
+
Payloads without a version prefix are assumed to be version `0`.
|
70
70
|
|
71
71
|
The first argument is the current version used for newly generated payloads.
|
72
72
|
|
@@ -84,7 +84,7 @@ coder.dump([1]) # => "#☠1☢\n[1]"
|
|
84
84
|
|
85
85
|
### `allow_nil`
|
86
86
|
|
87
|
-
In some
|
87
|
+
In some situations where you'd rather not serialize `nil`, you can use the `Paquito.allow_nil` shorthand:
|
88
88
|
|
89
89
|
```ruby
|
90
90
|
coder = Paquito.allow_nil(Marshal)
|
@@ -95,7 +95,7 @@ coder.load(nil) # => nil
|
|
95
95
|
### `TranslateErrors`
|
96
96
|
|
97
97
|
If you do need to handle serialization or deserialization errors, for instance to fallback to acting like a cache miss,
|
98
|
-
`Paquito::TranslateErrors`
|
98
|
+
`Paquito::TranslateErrors` translates all underlying exceptions into `Paquito::Error` descendants.
|
99
99
|
|
100
100
|
Example:
|
101
101
|
|
@@ -117,7 +117,8 @@ coder.load(coder.dump(%i(foo bar).to_set)) # => #<Set: {:foo, :bar}>
|
|
117
117
|
|
118
118
|
### `TypedStruct`
|
119
119
|
|
120
|
-
`Paquito::TypedStruct` is a opt-in Sorbet runtime plugin that allows `T::Struct` classes to be serializable. You need
|
120
|
+
`Paquito::TypedStruct` is a opt-in Sorbet runtime plugin that allows `T::Struct` classes to be serializable. You need
|
121
|
+
to explicitly include the module in the `T::Struct` classes that you will be serializing.
|
121
122
|
|
122
123
|
Example
|
123
124
|
|
@@ -137,12 +138,12 @@ MyStruct.from_pack([26450, "foo", 1]) # => <MyStruct bar=1, foo="foo">
|
|
137
138
|
|
138
139
|
## Rails utilities
|
139
140
|
|
140
|
-
`paquito` doesn't
|
141
|
+
`paquito` doesn't depend on `rails` or any of its components, however it does provide some optional utilities.
|
141
142
|
|
142
143
|
### `CacheEntryCoder`
|
143
144
|
|
144
|
-
`Paquito::CacheEntryCoder` turns an `ActiveSupport::Cache::Entry` instance into a simple `Array` instance. This allows
|
145
|
-
implement custom coders for `ActiveSupport::Cache`.
|
145
|
+
`Paquito::CacheEntryCoder` turns an `ActiveSupport::Cache::Entry` instance into a simple `Array` instance. This allows
|
146
|
+
you to implement custom coders for `ActiveSupport::Cache`.
|
146
147
|
|
147
148
|
Example:
|
148
149
|
|
@@ -152,7 +153,7 @@ ActiveSupport::Cache::FileStore.new("tmp/cache", coder: Paquito.chain(Paquito::C
|
|
152
153
|
|
153
154
|
### `SerializedColumn`
|
154
155
|
|
155
|
-
`Paquito::SerializedColumn` allows to decorate any encoder to behave like Rails's builtin `YAMLColumn`
|
156
|
+
`Paquito::SerializedColumn` allows you to decorate any encoder to behave like Rails's builtin `YAMLColumn`
|
156
157
|
|
157
158
|
Example:
|
158
159
|
|
@@ -0,0 +1,19 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "bundler/setup"
|
5
|
+
require "paquito"
|
6
|
+
require "benchmark/ips"
|
7
|
+
|
8
|
+
BASELINE = Paquito::CodecFactory.build([Symbol], pool: false)
|
9
|
+
POOLED = Paquito::CodecFactory.build([Symbol], pool: 1)
|
10
|
+
|
11
|
+
PAYLOAD = BASELINE.dump(:foo)
|
12
|
+
MARSHAL_PAYLOAD = Marshal.dump(:foo)
|
13
|
+
|
14
|
+
Benchmark.ips do |x|
|
15
|
+
x.report("marshal") { Marshal.load(MARSHAL_PAYLOAD) }
|
16
|
+
x.report("msgpack") { BASELINE.load(PAYLOAD) }
|
17
|
+
x.report("pooled") { POOLED.load(PAYLOAD) }
|
18
|
+
x.compare!(order: :baseline)
|
19
|
+
end
|
data/lib/paquito/allow_nil.rb
CHANGED
@@ -5,15 +5,24 @@ require "paquito/coder_chain"
|
|
5
5
|
|
6
6
|
module Paquito
|
7
7
|
class CodecFactory
|
8
|
-
def self.build(types, freeze: false, serializable_type: false)
|
9
|
-
factory =
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
8
|
+
def self.build(types, freeze: false, serializable_type: false, pool: 1)
|
9
|
+
factory = if types.empty? && !serializable_type
|
10
|
+
MessagePack::DefaultFactory
|
11
|
+
else
|
12
|
+
MessagePack::Factory.new
|
13
|
+
end
|
14
|
+
|
15
15
|
Types.register(factory, types) unless types.empty?
|
16
16
|
Types.register_serializable_type(factory) if serializable_type
|
17
|
+
|
18
|
+
if pool && pool > 0 && factory.respond_to?(:pool)
|
19
|
+
if serializable_type || types.any? { |t| Types.recursive?(t) }
|
20
|
+
pool *= 2
|
21
|
+
end
|
22
|
+
factory = factory.freeze.pool(pool, freeze: freeze)
|
23
|
+
freeze = false
|
24
|
+
end
|
25
|
+
|
17
26
|
MessagePackCodec.new(factory, freeze: freeze)
|
18
27
|
end
|
19
28
|
|
@@ -32,7 +41,11 @@ module Paquito
|
|
32
41
|
end
|
33
42
|
|
34
43
|
def load(payload)
|
35
|
-
|
44
|
+
if @freeze
|
45
|
+
@factory.load(payload, freeze: @freeze)
|
46
|
+
else
|
47
|
+
@factory.load(payload)
|
48
|
+
end
|
36
49
|
rescue MessagePack::UnpackError => error
|
37
50
|
raise UnpackError, error.message
|
38
51
|
rescue IOError => error
|
data/lib/paquito/safe_yaml.rb
CHANGED
data/lib/paquito/typed_struct.rb
CHANGED
data/lib/paquito/types.rb
CHANGED
@@ -37,10 +37,12 @@ module Paquito
|
|
37
37
|
def register(klass, packer: nil, unpacker:)
|
38
38
|
if packer
|
39
39
|
raise ArgumentError, "packer for #{klass} already defined" if packers.key?(klass)
|
40
|
+
|
40
41
|
packers[klass] = packer
|
41
42
|
end
|
42
43
|
|
43
44
|
raise ArgumentError, "unpacker for #{klass} already defined" if unpackers.key?(klass)
|
45
|
+
|
44
46
|
unpackers[klass] = unpacker
|
45
47
|
|
46
48
|
self
|
@@ -72,13 +74,13 @@ module Paquito
|
|
72
74
|
# New types can be added as long as they have unique #code.
|
73
75
|
TYPES = {
|
74
76
|
"Symbol" => {
|
75
|
-
code:
|
77
|
+
code: 0,
|
76
78
|
packer: Symbol.method_defined?(:name) ? :name : :to_s,
|
77
79
|
unpacker: :to_sym,
|
78
80
|
optimized_symbols_parsing: true,
|
79
81
|
}.freeze,
|
80
82
|
"Time" => {
|
81
|
-
code:
|
83
|
+
code: 1,
|
82
84
|
packer: ->(value) do
|
83
85
|
rational = value.utc.to_r
|
84
86
|
[rational.numerator, rational.denominator].pack(TIME_FORMAT)
|
@@ -89,7 +91,7 @@ module Paquito
|
|
89
91
|
end,
|
90
92
|
}.freeze,
|
91
93
|
"DateTime" => {
|
92
|
-
code:
|
94
|
+
code: 2,
|
93
95
|
packer: ->(value) do
|
94
96
|
sec = value.sec + value.sec_fraction
|
95
97
|
offset = value.offset
|
@@ -129,7 +131,7 @@ module Paquito
|
|
129
131
|
end,
|
130
132
|
}.freeze,
|
131
133
|
"Date" => {
|
132
|
-
code:
|
134
|
+
code: 3,
|
133
135
|
packer: ->(value) do
|
134
136
|
[value.year, value.month, value.day].pack(DATE_FORMAT)
|
135
137
|
end,
|
@@ -139,28 +141,29 @@ module Paquito
|
|
139
141
|
end,
|
140
142
|
}.freeze,
|
141
143
|
"BigDecimal" => {
|
142
|
-
code:
|
144
|
+
code: 4,
|
143
145
|
packer: :_dump,
|
144
146
|
unpacker: BigDecimal.method(:_load),
|
145
147
|
}.freeze,
|
146
148
|
# Range => { code: 0x05 }, do not recycle that code
|
147
149
|
"ActiveRecord::Base" => {
|
148
|
-
code:
|
150
|
+
code: 6,
|
149
151
|
packer: ->(value) { ActiveRecordPacker.dump(value) },
|
150
152
|
unpacker: ->(value) { ActiveRecordPacker.load(value) },
|
151
153
|
}.freeze,
|
152
154
|
"ActiveSupport::HashWithIndifferentAccess" => {
|
153
|
-
code:
|
155
|
+
code: 7,
|
154
156
|
packer: ->(factory, value) do
|
155
157
|
unless value.instance_of?(ActiveSupport::HashWithIndifferentAccess)
|
156
158
|
raise PackError.new("cannot pack HashWithIndifferentClass subclass", value)
|
157
159
|
end
|
160
|
+
|
158
161
|
factory.dump(value.to_h)
|
159
162
|
end,
|
160
163
|
unpacker: ->(factory, value) { HashWithIndifferentAccess.new(factory.load(value)) },
|
161
164
|
},
|
162
165
|
"ActiveSupport::TimeWithZone" => {
|
163
|
-
code:
|
166
|
+
code: 8,
|
164
167
|
packer: ->(value) do
|
165
168
|
[
|
166
169
|
value.utc.to_i,
|
@@ -176,12 +179,27 @@ module Paquito
|
|
176
179
|
end,
|
177
180
|
},
|
178
181
|
"Set" => {
|
179
|
-
code:
|
182
|
+
code: 9,
|
180
183
|
packer: ->(factory, value) { factory.dump(value.to_a) },
|
181
184
|
unpacker: ->(factory, value) { factory.load(value).to_set },
|
182
185
|
},
|
183
|
-
#
|
184
|
-
|
186
|
+
# Integer => { code: 10 }, reserved for oversized Integer
|
187
|
+
# Object => { code: 127 }, reserved for serializable Object type
|
188
|
+
}
|
189
|
+
begin
|
190
|
+
require "msgpack/bigint"
|
191
|
+
|
192
|
+
TYPES["Integer"] = {
|
193
|
+
code: 10,
|
194
|
+
packer: MessagePack::Bigint.method(:to_msgpack_ext),
|
195
|
+
unpacker: MessagePack::Bigint.method(:from_msgpack_ext),
|
196
|
+
oversized_integer_extension: true,
|
197
|
+
}
|
198
|
+
rescue LoadError
|
199
|
+
# expected on older msgpack
|
200
|
+
end
|
201
|
+
|
202
|
+
TYPES.freeze
|
185
203
|
|
186
204
|
class << self
|
187
205
|
def register(factory, types)
|
@@ -206,6 +224,11 @@ module Paquito
|
|
206
224
|
end
|
207
225
|
end
|
208
226
|
|
227
|
+
def recursive?(type)
|
228
|
+
type_attributes = TYPES.fetch(type.name)
|
229
|
+
recursive_callback?(type_attributes[:packer]) || recursive_callback?(type_attributes[:unpacker])
|
230
|
+
end
|
231
|
+
|
209
232
|
def register_serializable_type(factory)
|
210
233
|
factory.register_type(
|
211
234
|
0x7f,
|
@@ -236,9 +259,14 @@ module Paquito
|
|
236
259
|
|
237
260
|
private
|
238
261
|
|
262
|
+
def recursive_callback?(callback)
|
263
|
+
callback.respond_to?(:arity) && callback.arity != 1
|
264
|
+
end
|
265
|
+
|
239
266
|
def curry_callback(callback, factory)
|
240
267
|
return callback.to_proc if callback.is_a?(Symbol)
|
241
268
|
return callback if callback.arity == 1
|
269
|
+
|
242
270
|
callback.curry.call(factory)
|
243
271
|
end
|
244
272
|
end
|
data/lib/paquito/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: paquito
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jean Boussier
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-03-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: msgpack
|
@@ -39,6 +39,7 @@ files:
|
|
39
39
|
- LICENSE.txt
|
40
40
|
- README.md
|
41
41
|
- Rakefile
|
42
|
+
- benchmark/msgpack-pooling.rb
|
42
43
|
- bin/console
|
43
44
|
- bin/setup
|
44
45
|
- dev.yml
|