paquito 0.8.0 → 0.9.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 +4 -4
- data/.github/workflows/ci.yml +5 -6
- data/.rubocop.yml +1 -1
- data/CHANGELOG.md +13 -0
- data/Gemfile.lock +7 -9
- data/README.md +34 -0
- data/Rakefile +1 -2
- data/benchmark/flat-entry-coder.rb +50 -0
- data/dev.yml +2 -2
- data/lib/paquito/active_record_coder.rb +1 -0
- data/lib/paquito/cache_entry_coder.rb +4 -11
- data/lib/paquito/codec_factory.rb +1 -1
- data/lib/paquito/compressor.rb +17 -0
- data/lib/paquito/flat_cache_entry_coder.rb +48 -0
- data/lib/paquito/single_byte_prefix_version_with_string_bypass.rb +14 -6
- data/lib/paquito/version.rb +1 -1
- data/lib/paquito.rb +5 -1
- data/paquito.gemspec +1 -1
- metadata +6 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e0339ed04d21077425e5dd4b2e814a63ec524ca4806bc3af33b82fc162b61205
|
4
|
+
data.tar.gz: 55b5ff0b7b2c325622307b6d6a25a978067ec69762993fc6d7f5f732fcc11301
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 264f88b37a150446c8e3d021887321fe1f47138894ac36a06e57105107f0f47add8ad82bd5e95078a50387ae4daad95154ebc5e1ce2bfde1b758a74e4559087c
|
7
|
+
data.tar.gz: 3775371cc85bd799bdb9580f878e3c5a33c1a10020e9349afe5eecfcac7d65552a96e76eb72b387bc72dbce375a9f03f85151bdcdf27de8ceb8d23a73560b792
|
data/.github/workflows/ci.yml
CHANGED
@@ -13,7 +13,7 @@ jobs:
|
|
13
13
|
- name: Set up Ruby
|
14
14
|
uses: ruby/setup-ruby@v1
|
15
15
|
with:
|
16
|
-
ruby-version: '2.
|
16
|
+
ruby-version: '2.7'
|
17
17
|
bundler-cache: true
|
18
18
|
- name: Run test
|
19
19
|
run: bundle exec rubocop
|
@@ -23,18 +23,17 @@ jobs:
|
|
23
23
|
strategy:
|
24
24
|
fail-fast: false
|
25
25
|
matrix:
|
26
|
-
ruby: [ ruby-head, '3.1', '3.0', '2.7'
|
26
|
+
ruby: [ ruby-head, '3.1', '3.0', '2.7' ]
|
27
27
|
steps:
|
28
28
|
- name: Checkout
|
29
29
|
uses: actions/checkout@v3
|
30
|
+
- name: Remove Gemfile.lock
|
31
|
+
run: rm Gemfile.lock
|
30
32
|
- name: Set up Ruby
|
31
33
|
uses: ruby/setup-ruby@v1
|
32
34
|
with:
|
33
35
|
ruby-version: ${{ matrix.ruby }}
|
34
|
-
|
35
|
-
run: |
|
36
|
-
rm Gemfile.lock
|
37
|
-
bundle install
|
36
|
+
bundler-cache: true
|
38
37
|
- name: Run test
|
39
38
|
run: bundle exec rake
|
40
39
|
- name: Install gem
|
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,18 @@
|
|
1
1
|
# Unreleased
|
2
2
|
|
3
|
+
# 0.9.1
|
4
|
+
|
5
|
+
* Fix a bug when handle `compress / decompress` coders. (#23)
|
6
|
+
* `SingleBytePrefixVersionWithStringBypass` accepts another coder for strings.
|
7
|
+
|
8
|
+
# 0.9.0
|
9
|
+
|
10
|
+
* Handle `compress / decompress` coders (#22)
|
11
|
+
* Introduce `FlatCacheEntryCoder` (#21).
|
12
|
+
* Drop the partial Ruby 2.6 support.
|
13
|
+
|
14
|
+
# 0.8.0
|
15
|
+
|
3
16
|
* Introduce `SingleBytePrefixVersionWithStringBypass` (#18, #20).
|
4
17
|
|
5
18
|
# 0.7.0
|
data/Gemfile.lock
CHANGED
@@ -1,23 +1,22 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
paquito (0.
|
4
|
+
paquito (0.9.1)
|
5
5
|
msgpack (>= 1.5.2)
|
6
6
|
|
7
7
|
GEM
|
8
8
|
remote: https://rubygems.org/
|
9
9
|
specs:
|
10
|
-
activemodel (
|
11
|
-
activesupport (=
|
12
|
-
activerecord (
|
13
|
-
activemodel (=
|
14
|
-
activesupport (=
|
15
|
-
activesupport (
|
10
|
+
activemodel (7.0.4)
|
11
|
+
activesupport (= 7.0.4)
|
12
|
+
activerecord (7.0.4)
|
13
|
+
activemodel (= 7.0.4)
|
14
|
+
activesupport (= 7.0.4)
|
15
|
+
activesupport (7.0.4)
|
16
16
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
17
17
|
i18n (>= 1.6, < 2)
|
18
18
|
minitest (>= 5.1)
|
19
19
|
tzinfo (~> 2.0)
|
20
|
-
zeitwerk (~> 2.3)
|
21
20
|
ast (2.4.2)
|
22
21
|
benchmark-ips (2.10.0)
|
23
22
|
byebug (11.1.3)
|
@@ -52,7 +51,6 @@ GEM
|
|
52
51
|
tzinfo (2.0.5)
|
53
52
|
concurrent-ruby (~> 1.0)
|
54
53
|
unicode-display_width (2.2.0)
|
55
|
-
zeitwerk (2.6.0)
|
56
54
|
|
57
55
|
PLATFORMS
|
58
56
|
ruby
|
data/README.md
CHANGED
@@ -70,6 +70,18 @@ in an optimized way.
|
|
70
70
|
When the object to serialize is an `UTF-8`, `ASCII` or `BINARY` string, rather than invoking the underlying serializer, it simply
|
71
71
|
prepends a single byte to the string which indicates the encoding.
|
72
72
|
|
73
|
+
Additionally, you can pass a distinct serializer for strings only:
|
74
|
+
|
75
|
+
Example:
|
76
|
+
|
77
|
+
```ruby
|
78
|
+
coder = Paquito::SingleBytePrefixVersion.new(
|
79
|
+
1,
|
80
|
+
{ 0 => YAML, 1 => JSON },
|
81
|
+
Paquito::ConditionalCompressor.new(Zlib, 1024), # Large strings will be compressed but not serialized in JSON.
|
82
|
+
)
|
83
|
+
```
|
84
|
+
|
73
85
|
The larger the string the larger the speed gain is, e.g. for a 1MB string, it's over 500x faster than going through `MessagePack` or `Marshal`.
|
74
86
|
|
75
87
|
### `CommentPrefixVersion`
|
@@ -161,6 +173,28 @@ Example:
|
|
161
173
|
ActiveSupport::Cache::FileStore.new("tmp/cache", coder: Paquito.chain(Paquito::CacheEntryCoder, JSON))
|
162
174
|
```
|
163
175
|
|
176
|
+
### `FlatCacheEntryCoder`
|
177
|
+
|
178
|
+
`Paquito::FlatCacheEntryCoder` is a variation of `Paquito::CacheEntryCoder`. Instead of encoding `ActiveSupport::Cache::Entry`
|
179
|
+
into an Array of three members, it serializes the entry metadata itself and adds it as a prefix to the serialized payload.
|
180
|
+
|
181
|
+
This allows to leverage `Paquito::SingleBytePrefixVersionWithStringBypass` effectively.
|
182
|
+
|
183
|
+
Example:
|
184
|
+
|
185
|
+
```ruby
|
186
|
+
ActiveSupport::Cache::FileStore.new(
|
187
|
+
"tmp/cache",
|
188
|
+
coder: Paquito::FlatCacheEntryCoder.new(
|
189
|
+
Paquito::SingleBytePrefixVersionWithStringBypass.new(
|
190
|
+
1,
|
191
|
+
0 => Marshal,
|
192
|
+
1 => JSON,
|
193
|
+
)
|
194
|
+
)
|
195
|
+
)
|
196
|
+
```
|
197
|
+
|
164
198
|
### `SerializedColumn`
|
165
199
|
|
166
200
|
`Paquito::SerializedColumn` allows you to decorate any encoder to behave like Rails's builtin `YAMLColumn`
|
data/Rakefile
CHANGED
@@ -3,8 +3,7 @@
|
|
3
3
|
require "bundler/gem_tasks"
|
4
4
|
require "rake/testtask"
|
5
5
|
|
6
|
-
suites = [:vanilla, :activesupport]
|
7
|
-
suites << :activerecord if RUBY_VERSION >= "2.7"
|
6
|
+
suites = [:vanilla, :activesupport, :activerecord]
|
8
7
|
namespace :test do
|
9
8
|
suites.each do |suite|
|
10
9
|
Rake::TestTask.new(suite) do |t|
|
@@ -0,0 +1,50 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "bundler/setup"
|
5
|
+
require "paquito"
|
6
|
+
require "active_support"
|
7
|
+
require "benchmark/ips"
|
8
|
+
|
9
|
+
CODEC = Paquito::CodecFactory.build
|
10
|
+
ORIGINAL = Paquito::SingleBytePrefixVersion.new(
|
11
|
+
0,
|
12
|
+
0 => Paquito.chain(
|
13
|
+
Paquito::CacheEntryCoder,
|
14
|
+
CODEC,
|
15
|
+
),
|
16
|
+
)
|
17
|
+
FLAT = Paquito::FlatCacheEntryCoder.new(
|
18
|
+
Paquito::SingleBytePrefixVersionWithStringBypass.new(
|
19
|
+
0,
|
20
|
+
0 => CODEC,
|
21
|
+
)
|
22
|
+
)
|
23
|
+
|
24
|
+
entries = {
|
25
|
+
small_string: "Hello World!",
|
26
|
+
bytes_1mb: Random.bytes(1_000_000),
|
27
|
+
int_array: 1000.times.to_a,
|
28
|
+
}
|
29
|
+
|
30
|
+
entries.each do |name, object|
|
31
|
+
entry = ActiveSupport::Cache::Entry.new(object, expires_at: 15.minutes.from_now.to_f)
|
32
|
+
original_payload = ORIGINAL.dump(entry).freeze
|
33
|
+
flat_payload = FLAT.dump(entry).freeze
|
34
|
+
|
35
|
+
puts " === Read #{name} ==="
|
36
|
+
Benchmark.ips do |x|
|
37
|
+
x.report("original") { ORIGINAL.load(original_payload) }
|
38
|
+
x.report("flat") { FLAT.load(flat_payload) }
|
39
|
+
x.compare!(order: :baseline)
|
40
|
+
end
|
41
|
+
|
42
|
+
puts " === Write #{name} ==="
|
43
|
+
Benchmark.ips do |x|
|
44
|
+
x.report("original") { ORIGINAL.dump(entry) }
|
45
|
+
x.report("flat") { FLAT.dump(entry) }
|
46
|
+
x.compare!(order: :baseline)
|
47
|
+
end
|
48
|
+
|
49
|
+
puts
|
50
|
+
end
|
data/dev.yml
CHANGED
@@ -1,22 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
gem "activesupport", ">= 7.0"
|
4
|
+
|
3
5
|
module Paquito
|
4
6
|
module CacheEntryCoder
|
5
7
|
def self.dump(entry)
|
6
|
-
|
7
|
-
# drop any trailing nil values to save a couple bytes
|
8
|
-
attrs.pop until !attrs.last.nil? || attrs.empty?
|
9
|
-
attrs
|
8
|
+
entry.pack
|
10
9
|
end
|
11
10
|
|
12
11
|
def self.load(payload)
|
13
|
-
|
14
|
-
value, expires_in, version = payload
|
15
|
-
entry.instance_variable_set(:@value, value)
|
16
|
-
entry.instance_variable_set(:@expires_in, expires_in)
|
17
|
-
entry.instance_variable_set(:@created_at, 0.0)
|
18
|
-
entry.instance_variable_set(:@version, version)
|
19
|
-
entry
|
12
|
+
::ActiveSupport::Cache::Entry.unpack(payload)
|
20
13
|
end
|
21
14
|
end
|
22
15
|
end
|
@@ -5,7 +5,7 @@ require "paquito/coder_chain"
|
|
5
5
|
|
6
6
|
module Paquito
|
7
7
|
class CodecFactory
|
8
|
-
def self.build(types, freeze: false, serializable_type: false, pool: 1)
|
8
|
+
def self.build(types = [], freeze: false, serializable_type: false, pool: 1)
|
9
9
|
factory = if types.empty? && !serializable_type
|
10
10
|
MessagePack::DefaultFactory
|
11
11
|
else
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Paquito
|
4
|
+
class Compressor
|
5
|
+
def initialize(compressor)
|
6
|
+
@compressor = compressor
|
7
|
+
end
|
8
|
+
|
9
|
+
def dump(serial)
|
10
|
+
@compressor.compress(serial)
|
11
|
+
end
|
12
|
+
|
13
|
+
def load(payload)
|
14
|
+
@compressor.decompress(payload)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
gem "activesupport", ">= 7.0"
|
4
|
+
|
5
|
+
module Paquito
|
6
|
+
class FlatCacheEntryCoder
|
7
|
+
METADATA_CODEC = CodecFactory.build
|
8
|
+
|
9
|
+
EXPIRES_AT_FORMAT = "E" # Float double-precision, little-endian byte order (8 bytes)
|
10
|
+
VERSION_SIZE_FORMAT = "l<" # 32-bit signed, little-endian byte order (int32_t) (4 bytes)
|
11
|
+
PREFIX_FORMAT = -(EXPIRES_AT_FORMAT + VERSION_SIZE_FORMAT)
|
12
|
+
VERSION_SIZE_OFFSET = [0.0].pack(EXPIRES_AT_FORMAT).bytesize # should be 8
|
13
|
+
VERSION_OFFSET = [0.0, 0].pack(PREFIX_FORMAT).bytesize # Should be 12
|
14
|
+
VERSION_SIZE_UNPACK = -"@#{VERSION_SIZE_OFFSET}#{VERSION_SIZE_FORMAT}"
|
15
|
+
|
16
|
+
def initialize(value_coder)
|
17
|
+
@value_coder = value_coder
|
18
|
+
end
|
19
|
+
|
20
|
+
def dump(entry)
|
21
|
+
version = entry.version
|
22
|
+
payload = [
|
23
|
+
entry.expires_at || 0.0,
|
24
|
+
version ? version.bytesize : -1,
|
25
|
+
].pack(PREFIX_FORMAT)
|
26
|
+
payload << version if version
|
27
|
+
payload << @value_coder.dump(entry.value)
|
28
|
+
end
|
29
|
+
|
30
|
+
def load(payload)
|
31
|
+
expires_at = payload.unpack1(EXPIRES_AT_FORMAT)
|
32
|
+
expires_at = nil if expires_at == 0.0
|
33
|
+
|
34
|
+
version_size = payload.unpack1(VERSION_SIZE_UNPACK)
|
35
|
+
if version_size < 0
|
36
|
+
version_size = 0
|
37
|
+
else
|
38
|
+
version = payload.byteslice(VERSION_OFFSET, version_size)
|
39
|
+
end
|
40
|
+
|
41
|
+
::ActiveSupport::Cache::Entry.new(
|
42
|
+
@value_coder.load(payload.byteslice((VERSION_OFFSET + version_size)..-1).freeze),
|
43
|
+
expires_at: expires_at,
|
44
|
+
version: version,
|
45
|
+
)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -6,15 +6,20 @@ module Paquito
|
|
6
6
|
BINARY_VERSION = 254
|
7
7
|
ASCII_VERSION = 253
|
8
8
|
|
9
|
+
def initialize(current_version, coders, string_coder = nil)
|
10
|
+
super(current_version, coders)
|
11
|
+
@string_coder = string_coder
|
12
|
+
end
|
13
|
+
|
9
14
|
def dump(object)
|
10
15
|
if object.class == String # We don't want to match subclasses
|
11
16
|
case object.encoding
|
12
17
|
when Encoding::UTF_8
|
13
|
-
UTF8_VERSION.chr(Encoding::BINARY) << object
|
18
|
+
UTF8_VERSION.chr(Encoding::BINARY) << (@string_coder ? @string_coder.dump(object) : object)
|
14
19
|
when Encoding::BINARY
|
15
|
-
BINARY_VERSION.chr(Encoding::BINARY) << object
|
20
|
+
BINARY_VERSION.chr(Encoding::BINARY) << (@string_coder ? @string_coder.dump(object) : object)
|
16
21
|
when Encoding::US_ASCII
|
17
|
-
ASCII_VERSION.chr(Encoding::BINARY) << object
|
22
|
+
ASCII_VERSION.chr(Encoding::BINARY) << (@string_coder ? @string_coder.dump(object) : object)
|
18
23
|
else
|
19
24
|
super
|
20
25
|
end
|
@@ -31,11 +36,14 @@ module Paquito
|
|
31
36
|
|
32
37
|
case payload_version
|
33
38
|
when UTF8_VERSION
|
34
|
-
payload.byteslice(1..-1).force_encoding(Encoding::UTF_8)
|
39
|
+
string = payload.byteslice(1..-1).force_encoding(Encoding::UTF_8)
|
40
|
+
@string_coder ? @string_coder.load(string) : string
|
35
41
|
when BINARY_VERSION
|
36
|
-
payload.byteslice(1..-1).force_encoding(Encoding::BINARY)
|
42
|
+
string = payload.byteslice(1..-1).force_encoding(Encoding::BINARY)
|
43
|
+
@string_coder ? @string_coder.load(string) : string
|
37
44
|
when ASCII_VERSION
|
38
|
-
payload.byteslice(1..-1).force_encoding(Encoding::US_ASCII)
|
45
|
+
string = payload.byteslice(1..-1).force_encoding(Encoding::US_ASCII)
|
46
|
+
@string_coder ? @string_coder.load(string) : string
|
39
47
|
else
|
40
48
|
coder = @coders.fetch(payload_version) do
|
41
49
|
raise UnsupportedCodec, "Unsupported packer version #{payload_version}"
|
data/lib/paquito/version.rb
CHANGED
data/lib/paquito.rb
CHANGED
@@ -10,11 +10,11 @@ require "msgpack"
|
|
10
10
|
|
11
11
|
require "paquito/version"
|
12
12
|
require "paquito/deflater"
|
13
|
+
require "paquito/compressor"
|
13
14
|
require "paquito/allow_nil"
|
14
15
|
require "paquito/translate_errors"
|
15
16
|
require "paquito/safe_yaml"
|
16
17
|
require "paquito/conditional_compressor"
|
17
|
-
require "paquito/cache_entry_coder"
|
18
18
|
require "paquito/single_byte_prefix_version"
|
19
19
|
require "paquito/single_byte_prefix_version_with_string_bypass"
|
20
20
|
require "paquito/comment_prefix_version"
|
@@ -25,6 +25,8 @@ require "paquito/typed_struct"
|
|
25
25
|
require "paquito/serialized_column"
|
26
26
|
|
27
27
|
module Paquito
|
28
|
+
autoload :CacheEntryCoder, "paquito/cache_entry_coder"
|
29
|
+
autoload :FlatCacheEntryCoder, "paquito/flat_cache_entry_coder"
|
28
30
|
autoload :ActiveRecordCoder, "paquito/active_record_coder"
|
29
31
|
|
30
32
|
class << self
|
@@ -33,6 +35,8 @@ module Paquito
|
|
33
35
|
coder
|
34
36
|
elsif coder.respond_to?(:deflate) && coder.respond_to?(:inflate)
|
35
37
|
Deflater.new(coder)
|
38
|
+
elsif coder.respond_to?(:compress) && coder.respond_to?(:decompress)
|
39
|
+
Compressor.new(coder)
|
36
40
|
else
|
37
41
|
raise TypeError, "Coders must respond to #dump and #load, #{coder.inspect} doesn't"
|
38
42
|
end
|
data/paquito.gemspec
CHANGED
@@ -12,7 +12,7 @@ Gem::Specification.new do |spec|
|
|
12
12
|
spec.description = "Framework for defining efficient and extendable serializers"
|
13
13
|
spec.homepage = "https://github.com/Shopify/paquito"
|
14
14
|
spec.license = "MIT"
|
15
|
-
spec.required_ruby_version = ">= 2.
|
15
|
+
spec.required_ruby_version = ">= 2.7.0"
|
16
16
|
|
17
17
|
spec.metadata["allowed_push_host"] = "https://rubygems.org"
|
18
18
|
|
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.9.1
|
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-12-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: msgpack
|
@@ -40,6 +40,7 @@ files:
|
|
40
40
|
- LICENSE.txt
|
41
41
|
- README.md
|
42
42
|
- Rakefile
|
43
|
+
- benchmark/flat-entry-coder.rb
|
43
44
|
- benchmark/msgpack-pooling.rb
|
44
45
|
- benchmark/string-bypass.rb
|
45
46
|
- bin/console
|
@@ -52,9 +53,11 @@ files:
|
|
52
53
|
- lib/paquito/codec_factory.rb
|
53
54
|
- lib/paquito/coder_chain.rb
|
54
55
|
- lib/paquito/comment_prefix_version.rb
|
56
|
+
- lib/paquito/compressor.rb
|
55
57
|
- lib/paquito/conditional_compressor.rb
|
56
58
|
- lib/paquito/deflater.rb
|
57
59
|
- lib/paquito/errors.rb
|
60
|
+
- lib/paquito/flat_cache_entry_coder.rb
|
58
61
|
- lib/paquito/safe_yaml.rb
|
59
62
|
- lib/paquito/serialized_column.rb
|
60
63
|
- lib/paquito/single_byte_prefix_version.rb
|
@@ -81,7 +84,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
81
84
|
requirements:
|
82
85
|
- - ">="
|
83
86
|
- !ruby/object:Gem::Version
|
84
|
-
version: 2.
|
87
|
+
version: 2.7.0
|
85
88
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
86
89
|
requirements:
|
87
90
|
- - ">="
|