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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d00c743e791d4a17131842b2f515d58cff0e62190c8509666aff3d0d34e2d005
4
- data.tar.gz: b6760e6c8ceefa78e77f6b7c1a5668d40930c0d80fb8f841ea0a3deda82a77dd
3
+ metadata.gz: dd3a891430b40fa7fad9d50b8f383070cee22cba06d19c0db67670324c575613
4
+ data.tar.gz: 752e27818f5781652fc91407b51a7f5c16e0a8a07baa0f1285b3e4a3c9029bb0
5
5
  SHA512:
6
- metadata.gz: 189792644f661fd6a5455921928ff94f911d972c971590ff6754bee2f0e27c3386199d0444286e279d6b658d755c3ad0e9deff2e6338620c5a1b81efe542365a
7
- data.tar.gz: c4f9cb2d28a8598627497bf3e7f8c1de1a29e2fa0a37e54e06310af438fdd5f34b6c77c533a90b121559d5e77353b5fe316a8099b4d1f8b588873363edb43c3a
6
+ metadata.gz: 7b25933ab48ec3327e7e2c781f6502f90ed7a440163e58c3255d43bb1473fc5da5414b44d85f06de7f0d90ef05d57871bd8fcb3aabd22caa19d97df3422240d2
7
+ data.tar.gz: aca4aa6e5d417d40926d9cf57045157d1eaffdae225cc051b97ad861e0dc2d19fecf1f6acdea1a9fa91982697cee231326d410a21b0c130f327fc21f967c914e
@@ -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.3.1)
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.1)
11
- activesupport (= 7.0.1)
12
- activerecord (7.0.1)
13
- activemodel (= 7.0.1)
14
- activesupport (= 7.0.1)
15
- activesupport (7.0.1)
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.9.1)
30
+ i18n (1.10.0)
24
31
  concurrent-ruby (~> 1.0)
25
- minitest (5.14.4)
26
- msgpack (1.4.4)
32
+ minitest (5.15.0)
27
33
  parallel (1.21.0)
28
- parser (3.0.2.0)
34
+ parser (3.1.1.0)
29
35
  ast (~> 2.4.1)
30
- rainbow (3.0.0)
36
+ rainbow (3.1.1)
31
37
  rake (13.0.6)
32
- regexp_parser (2.1.1)
38
+ regexp_parser (2.2.1)
33
39
  rexml (3.2.5)
34
- rubocop (1.22.1)
40
+ rubocop (1.25.1)
35
41
  parallel (~> 1.10)
36
- parser (>= 3.0.0.0)
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.12.0, < 2.0)
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.12.0)
44
- parser (>= 3.0.1.1)
45
- rubocop-shopify (2.3.0)
46
- rubocop (~> 1.22)
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.9209)
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` allows to combine two or more serializers into one.
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` compress payloads if they are over a defined size.
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 then allow to seemlessly transition from
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 unversionned payloads.
67
+ Similar to the single byte prefix, but meant to be human readable and to allow for migrating unversioned payloads.
68
68
 
69
- Payload without a version prefix are assumed to be version `0`.
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 situation where you'd rather not serialize `nil`, you can use the `Paquito.allow_nil` shorthand:
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` translate all underlying exceptions into `Paquito::Error` descendants.
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 to explicitly include the module in the `T::Struct` classes that you will be serializing.
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 not depend on `rails` nor any of its components, however it does provide some optional utilities for it.
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 to
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
@@ -8,11 +8,13 @@ module Paquito
8
8
 
9
9
  def dump(object)
10
10
  return nil if object.nil?
11
+
11
12
  @coder.dump(object)
12
13
  end
13
14
 
14
15
  def load(payload)
15
16
  return nil if payload.nil?
17
+
16
18
  @coder.load(payload)
17
19
  end
18
20
  end
@@ -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
- if types.empty? && !serializable_type
11
- MessagePack
12
- else
13
- MessagePack::Factory.new
14
- end
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
- @factory.load(payload, freeze: @freeze)
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
@@ -68,6 +68,7 @@ module Paquito
68
68
 
69
69
  def dump_coder(target)
70
70
  return unless permitted_class?(target.class)
71
+
71
72
  super
72
73
  end
73
74
 
@@ -41,6 +41,7 @@ module Paquito
41
41
  if pack_digest != digest
42
42
  raise(VersionMismatchError, "#{self} digests do not match")
43
43
  end
44
+
44
45
  new(**props.keys.zip(values).to_h)
45
46
  end
46
47
 
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: 0x00,
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: 0x01,
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: 0x02,
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: 0x03,
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: 0x04,
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: 0x6,
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: 0x7,
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: 0x8,
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: 0x9,
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
- # Object => { code: 0x7f }, reserved for serializable Object type
184
- }.freeze
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Paquito
4
- VERSION = "0.3.1"
4
+ VERSION = "0.4.0"
5
5
  end
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.3.1
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-02-02 00:00:00.000000000 Z
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