transit-ruby 0.8.467 → 0.8.539
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
- checksums.yaml.gz.sig +1 -1
- data.tar.gz.sig +0 -0
- data/README.md +9 -4
- data/lib/transit.rb +22 -0
- data/lib/transit/date_time_util.rb +2 -2
- data/lib/transit/marshaler/base.rb +179 -0
- data/lib/transit/marshaler/cruby/json.rb +94 -0
- data/lib/transit/marshaler/cruby/messagepack.rb +60 -0
- data/lib/transit/read_handlers.rb +16 -1
- data/lib/transit/reader.rb +2 -54
- data/lib/transit/unmarshaler/cruby/json.rb +55 -0
- data/lib/transit/unmarshaler/cruby/messagepack.rb +39 -0
- data/lib/transit/write_handlers.rb +36 -4
- data/lib/transit/writer.rb +19 -288
- data/spec/spec_helper.rb +14 -0
- data/spec/transit/exemplar_spec.rb +2 -1
- data/spec/transit/reader_spec.rb +12 -4
- data/spec/transit/round_trip_spec.rb +29 -36
- data/spec/transit/writer_spec.rb +32 -6
- metadata +23 -60
- metadata.gz.sig +3 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4c90ad9b7561aaa1ad18f56cc7ed90af38d7d70d
|
4
|
+
data.tar.gz: fbeacd5a32fbe9ce5030a5d0f341c0e895269d1c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c959bd7c9532b09cdae9edf20bb657ef0232b3b7220e5cc97af2ff2027f60bcce14e049e026fd23936620a5d3a150c13b482fbf52ab49f8b6959e6ed5d94f5af
|
7
|
+
data.tar.gz: 12609c53e17740223bc6a485a8de42d5f45faa67a145e741606a7bd79b24414d3597f71e9edbd2a40016860db0556c197310194a05fe31ae9fc22947346f8bf6
|
checksums.yaml.gz.sig
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
��B��骐�A3Y�o�bS�1���A�X(�\���iX�i�ʖ�`"X:�(� =�Ͼ�Ɓ=�_ ��ٝY*��,��#��"��8��nlj��[@��PҔ� Ű�`�S��4�M�Т�������𤡬���w�N�*h��Pa�ӷdt��#��J�jw�����%���t� �5�5�*�0�,������I�_j"}�7����(XXmTF����l1��(�g�É�pX �6,�Ӟf%z��`NR��
|
data.tar.gz.sig
CHANGED
Binary file
|
data/README.md
CHANGED
@@ -18,6 +18,10 @@ between applications, it should not yet be used for storing data
|
|
18
18
|
durably over time. This recommendation will change when the
|
19
19
|
specification is complete._
|
20
20
|
|
21
|
+
### Contributing
|
22
|
+
|
23
|
+
This library is open source, developed internally by Cognitect. We welcome discussions of potential problems and enhancement suggestions on the [transit-format mailing list](https://groups.google.com/forum/#!forum/transit-format). Issues can be filed using GitHub [issues](https://github.com/cognitect/transit-ruby/issues) for this project. Because transit is incorporated into products and client projects, we prefer to do development internally and are not accepting pull requests or patches.
|
24
|
+
|
21
25
|
## Releases and Dependency Information
|
22
26
|
|
23
27
|
See https://rubygems.org/gems/transit-ruby
|
@@ -140,15 +144,16 @@ for more info.
|
|
140
144
|
|bytes|Transit::ByteArray|Transit::ByteArray|Transit::ByteArray.new("base64")|base64|
|
141
145
|
|link|Transit::Link|Transit::Link|Transit::Link.new(Addressable::URI.parse("http://example.org/search"), "search")|`#<Transit::Link:0x007f81c405b7f0 @values={"href"=>#<Addressable::URI:0x3fc0e202dfb8 URI:http://example.org/search>, "rel"=>"search", "name"=>nil, "render"=>nil, "prompt"=>nil}>`|
|
142
146
|
|
147
|
+
### Additional types (not required by the [transit-format](https://github.com/cognitect/transit-format) spec)
|
148
|
+
|
149
|
+
|Semantic type|Write accepts|Read returns|Example(write)|Example(read)|
|
150
|
+
|------------|-------------|------------|--------------|-------------|
|
151
|
+
|ratio|Rational|Rational|Rational(1, 3)|Rational(1, 3)|
|
143
152
|
|
144
153
|
## Supported Rubies
|
145
154
|
|
146
155
|
* MRI 1.9.3, 2.0.0, 2.1.0, 2.1.1, 2.1.2
|
147
156
|
|
148
|
-
## Contributing
|
149
|
-
|
150
|
-
This library is open source, developed internally by Cognitect. We welcome discussions of potential problems and enhancement suggestions on the [transit-format mailing list](https://groups.google.com/forum/#!forum/transit-format). Issues can be filed using GitHub [issues](https://github.com/cognitect/transit-ruby/issues) for this project. Because transit is incorporated into products and client projects, we prefer to do development internally and are not accepting pull requests or patches.
|
151
|
-
|
152
157
|
## Copyright and License
|
153
158
|
|
154
159
|
Copyright © 2014 Cognitect
|
data/lib/transit.rb
CHANGED
@@ -66,6 +66,11 @@ module Transit
|
|
66
66
|
|
67
67
|
JSON_MAX_INT = 2**53 - 1
|
68
68
|
JSON_MIN_INT = -JSON_MAX_INT
|
69
|
+
|
70
|
+
def jruby?
|
71
|
+
defined?(RUBY_ENGINE) && RUBY_ENGINE == "jruby"
|
72
|
+
end
|
73
|
+
module_function :jruby?
|
69
74
|
end
|
70
75
|
|
71
76
|
require 'set'
|
@@ -81,6 +86,23 @@ require 'transit/transit_types'
|
|
81
86
|
require 'transit/rolling_cache'
|
82
87
|
require 'transit/write_handlers'
|
83
88
|
require 'transit/read_handlers'
|
89
|
+
require 'transit/marshaler/base'
|
84
90
|
require 'transit/writer'
|
85
91
|
require 'transit/decoder'
|
86
92
|
require 'transit/reader'
|
93
|
+
|
94
|
+
if Transit::jruby?
|
95
|
+
require 'lock_jar'
|
96
|
+
LockJar.lock(File.join(File.dirname(__FILE__), "..", "Jarfile"))
|
97
|
+
LockJar.load
|
98
|
+
require 'transit.jar'
|
99
|
+
require 'jruby'
|
100
|
+
com.cognitect.transit.ruby.TransitService.new.basicLoad(JRuby.runtime)
|
101
|
+
require 'transit/marshaler/jruby/json'
|
102
|
+
require 'transit/marshaler/jruby/messagepack'
|
103
|
+
else
|
104
|
+
require 'transit/marshaler/cruby/json'
|
105
|
+
require 'transit/marshaler/cruby/messagepack'
|
106
|
+
require 'transit/unmarshaler/cruby/json'
|
107
|
+
require 'transit/unmarshaler/cruby/messagepack'
|
108
|
+
end
|
@@ -22,11 +22,11 @@ module Transit
|
|
22
22
|
when Date
|
23
23
|
t = Time.gm(v.year, v.month, v.day)
|
24
24
|
when Time
|
25
|
-
t = v
|
25
|
+
t = v
|
26
26
|
else
|
27
27
|
raise "Don't know how to get millis from #{t.inspect}"
|
28
28
|
end
|
29
|
-
(t.to_i * 1000) + (t.usec / 1000)
|
29
|
+
(t.to_i * 1000) + (t.usec / 1000.0).round
|
30
30
|
end
|
31
31
|
|
32
32
|
def from_millis(millis)
|
@@ -0,0 +1,179 @@
|
|
1
|
+
# Copyright 2014 Cognitect. All Rights Reserved.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS-IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
module Transit
|
16
|
+
# Transit::Writer marshals Ruby objects as transit values to an output stream.
|
17
|
+
# @see https://github.com/cognitect/transit-format
|
18
|
+
module Marshaler
|
19
|
+
|
20
|
+
# @api private
|
21
|
+
module Base
|
22
|
+
def parse_options(opts)
|
23
|
+
@cache_enabled = !opts[:verbose]
|
24
|
+
@prefer_strings = opts[:prefer_strings]
|
25
|
+
@max_int = opts[:max_int]
|
26
|
+
@min_int = opts[:min_int]
|
27
|
+
|
28
|
+
handlers = WriteHandlers::DEFAULT_WRITE_HANDLERS.dup
|
29
|
+
handlers = handlers.merge!(opts[:handlers]) if opts[:handlers]
|
30
|
+
@handlers = (opts[:verbose] ? verbose_handlers(handlers) : handlers)
|
31
|
+
@handlers.values.each do |h|
|
32
|
+
if h.respond_to?(:handlers=)
|
33
|
+
h.handlers=(@handlers)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def find_handler(obj)
|
39
|
+
obj.class.ancestors.each do |a|
|
40
|
+
if handler = @handlers[a]
|
41
|
+
return handler
|
42
|
+
end
|
43
|
+
end
|
44
|
+
nil
|
45
|
+
end
|
46
|
+
|
47
|
+
def verbose_handlers(handlers)
|
48
|
+
handlers.each do |k, v|
|
49
|
+
if v.respond_to?(:verbose_handler) && vh = v.verbose_handler
|
50
|
+
handlers.store(k, vh)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
handlers
|
54
|
+
end
|
55
|
+
|
56
|
+
def escape(s)
|
57
|
+
if s.start_with?(SUB,ESC,RES) && s != "#{SUB} "
|
58
|
+
"#{ESC}#{s}"
|
59
|
+
else
|
60
|
+
s
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def emit_nil(as_map_key, cache)
|
65
|
+
as_map_key ? emit_string(ESC, "_", nil, true, cache) : emit_value(nil)
|
66
|
+
end
|
67
|
+
|
68
|
+
def emit_string(prefix, tag, value, as_map_key, cache)
|
69
|
+
encoded = "#{prefix}#{tag}#{value}"
|
70
|
+
if @cache_enabled && cache.cacheable?(encoded, as_map_key)
|
71
|
+
emit_value(cache.write(encoded), as_map_key)
|
72
|
+
else
|
73
|
+
emit_value(encoded, as_map_key)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def emit_boolean(handler, b, as_map_key, cache)
|
78
|
+
as_map_key ? emit_string(ESC, "?", handler.string_rep(b), true, cache) : emit_value(b)
|
79
|
+
end
|
80
|
+
|
81
|
+
def emit_int(tag, i, as_map_key, cache)
|
82
|
+
if as_map_key || i > @max_int || i < @min_int
|
83
|
+
emit_string(ESC, tag, i, as_map_key, cache)
|
84
|
+
else
|
85
|
+
emit_value(i, as_map_key)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def emit_double(d, as_map_key, cache)
|
90
|
+
as_map_key ? emit_string(ESC, "d", d, true, cache) : emit_value(d)
|
91
|
+
end
|
92
|
+
|
93
|
+
def emit_array(a, cache)
|
94
|
+
emit_array_start(a.size)
|
95
|
+
a.each {|e| marshal(e, false, cache)}
|
96
|
+
emit_array_end
|
97
|
+
end
|
98
|
+
|
99
|
+
def emit_map(m, cache)
|
100
|
+
emit_map_start(m.size)
|
101
|
+
m.each do |k,v|
|
102
|
+
marshal(k, true, cache)
|
103
|
+
marshal(v, false, cache)
|
104
|
+
end
|
105
|
+
emit_map_end
|
106
|
+
end
|
107
|
+
|
108
|
+
def emit_tagged_value(tag, rep, cache)
|
109
|
+
emit_array_start(2)
|
110
|
+
emit_string(ESC, "#", tag, false, cache)
|
111
|
+
marshal(rep, false, cache)
|
112
|
+
emit_array_end
|
113
|
+
end
|
114
|
+
|
115
|
+
def emit_encoded(handler, tag, obj, as_map_key, cache)
|
116
|
+
if tag.length == 1
|
117
|
+
rep = handler.rep(obj)
|
118
|
+
if String === rep
|
119
|
+
emit_string(ESC, tag, rep, as_map_key, cache)
|
120
|
+
elsif as_map_key || @prefer_strings
|
121
|
+
if str_rep = handler.string_rep(obj)
|
122
|
+
emit_string(ESC, tag, str_rep, as_map_key, cache)
|
123
|
+
else
|
124
|
+
raise "Cannot be encoded as String: " + {:tag => tag, :rep => rep, :obj => obj}.to_s
|
125
|
+
end
|
126
|
+
else
|
127
|
+
emit_tagged_value(tag, handler.rep(obj), cache)
|
128
|
+
end
|
129
|
+
elsif as_map_key
|
130
|
+
raise "Cannot be used as a map key: " + {:tag => tag, :rep => rep, :obj => obj}.to_s
|
131
|
+
else
|
132
|
+
emit_tagged_value(tag, handler.rep(obj), cache)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def marshal(obj, as_map_key, cache)
|
137
|
+
handler = find_handler(obj)
|
138
|
+
tag = handler.tag(obj)
|
139
|
+
case tag
|
140
|
+
when "_"
|
141
|
+
emit_nil(as_map_key, cache)
|
142
|
+
when "?"
|
143
|
+
emit_boolean(handler, obj, as_map_key, cache)
|
144
|
+
when "s"
|
145
|
+
emit_string(nil, nil, escape(handler.rep(obj)), as_map_key, cache)
|
146
|
+
when "i"
|
147
|
+
emit_int(tag, handler.rep(obj), as_map_key, cache)
|
148
|
+
when "d"
|
149
|
+
emit_double(handler.rep(obj), as_map_key, cache)
|
150
|
+
when "'"
|
151
|
+
emit_tagged_value(tag, handler.rep(obj), cache)
|
152
|
+
when "array"
|
153
|
+
emit_array(handler.rep(obj), cache)
|
154
|
+
when "map"
|
155
|
+
emit_map(handler.rep(obj), cache)
|
156
|
+
else
|
157
|
+
emit_encoded(handler, tag, obj, as_map_key, cache)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
def marshal_top(obj, cache=RollingCache.new)
|
162
|
+
if handler = find_handler(obj)
|
163
|
+
if tag = handler.tag(obj)
|
164
|
+
if tag.length == 1
|
165
|
+
marshal(TaggedValue.new(QUOTE, obj), false, cache)
|
166
|
+
else
|
167
|
+
marshal(obj, false, cache)
|
168
|
+
end
|
169
|
+
flush
|
170
|
+
else
|
171
|
+
raise "Handler must provide a non-nil tag: #{handler.inspect}"
|
172
|
+
end
|
173
|
+
else
|
174
|
+
raise "Can not find a Write Handler for #{obj.inspect}."
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
# Copyright 2014 Cognitect. All Rights Reserved.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS-IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
require 'oj'
|
16
|
+
|
17
|
+
module Transit
|
18
|
+
module Marshaler
|
19
|
+
class BaseJson
|
20
|
+
include Transit::Marshaler::Base
|
21
|
+
|
22
|
+
def default_opts
|
23
|
+
{:prefer_strings => true,
|
24
|
+
:max_int => JSON_MAX_INT,
|
25
|
+
:min_int => JSON_MIN_INT}
|
26
|
+
end
|
27
|
+
|
28
|
+
def initialize(io, opts)
|
29
|
+
@oj = Oj::StreamWriter.new(io)
|
30
|
+
parse_options(default_opts.merge(opts))
|
31
|
+
@state = []
|
32
|
+
end
|
33
|
+
|
34
|
+
def emit_array_start(size)
|
35
|
+
@state << :array
|
36
|
+
@oj.push_array
|
37
|
+
end
|
38
|
+
|
39
|
+
def emit_array_end
|
40
|
+
@state.pop
|
41
|
+
@oj.pop
|
42
|
+
end
|
43
|
+
|
44
|
+
def emit_map_start(size)
|
45
|
+
@state << :map
|
46
|
+
@oj.push_object
|
47
|
+
end
|
48
|
+
|
49
|
+
def emit_map_end
|
50
|
+
@state.pop
|
51
|
+
@oj.pop
|
52
|
+
end
|
53
|
+
|
54
|
+
def emit_value(obj, as_map_key=false)
|
55
|
+
if @state.last == :array
|
56
|
+
@oj.push_value(obj)
|
57
|
+
else
|
58
|
+
as_map_key ? @oj.push_key(obj) : @oj.push_value(obj)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def flush
|
63
|
+
# no-op
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# @api private
|
68
|
+
class Json < BaseJson
|
69
|
+
def emit_map(m, cache)
|
70
|
+
emit_array_start(-1)
|
71
|
+
emit_value("^ ", false)
|
72
|
+
m.each do |k,v|
|
73
|
+
marshal(k, true, cache)
|
74
|
+
marshal(v, false, cache)
|
75
|
+
end
|
76
|
+
emit_array_end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# @api private
|
81
|
+
class VerboseJson < BaseJson
|
82
|
+
def emit_string(prefix, tag, value, as_map_key, cache)
|
83
|
+
emit_value("#{prefix}#{tag}#{value}", as_map_key)
|
84
|
+
end
|
85
|
+
|
86
|
+
def emit_tagged_value(tag, rep, cache)
|
87
|
+
emit_map_start(1)
|
88
|
+
emit_string(ESC, "#", tag, true, cache)
|
89
|
+
marshal(rep, false, cache)
|
90
|
+
emit_map_end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# Copyright 2014 Cognitect. All Rights Reserved.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS-IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
require 'msgpack'
|
16
|
+
|
17
|
+
module Transit
|
18
|
+
module Marshaler
|
19
|
+
class MessagePack
|
20
|
+
include Transit::Marshaler::Base
|
21
|
+
|
22
|
+
def default_opts
|
23
|
+
{:prefer_strings => false,
|
24
|
+
:max_int => MAX_INT,
|
25
|
+
:min_int => MIN_INT}
|
26
|
+
end
|
27
|
+
|
28
|
+
def initialize(io, opts)
|
29
|
+
@io = io
|
30
|
+
@packer = ::MessagePack::Packer.new(io)
|
31
|
+
parse_options(default_opts.merge(opts))
|
32
|
+
end
|
33
|
+
|
34
|
+
def emit_array_start(size)
|
35
|
+
@packer.write_array_header(size)
|
36
|
+
end
|
37
|
+
|
38
|
+
def emit_array_end
|
39
|
+
# no-op
|
40
|
+
end
|
41
|
+
|
42
|
+
def emit_map_start(size)
|
43
|
+
@packer.write_map_header(size)
|
44
|
+
end
|
45
|
+
|
46
|
+
def emit_map_end
|
47
|
+
# no-op
|
48
|
+
end
|
49
|
+
|
50
|
+
def emit_value(obj, as_map_key=:ignore)
|
51
|
+
@packer.write(obj)
|
52
|
+
end
|
53
|
+
|
54
|
+
def flush
|
55
|
+
@packer.flush
|
56
|
+
@io.flush
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -42,6 +42,16 @@ module Transit
|
|
42
42
|
class BigDecimalHandler
|
43
43
|
def from_rep(v) BigDecimal.new(v) end
|
44
44
|
end
|
45
|
+
class SpecialNumbersHandler
|
46
|
+
def from_rep(v)
|
47
|
+
case v
|
48
|
+
when "NaN" then Float::NAN
|
49
|
+
when "INF" then Float::INFINITY
|
50
|
+
when "-INF" then -Float::INFINITY
|
51
|
+
else raise ArgumentError.new("Don't know how to handle #{v.inspect} for the \"z\" tag")
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
45
55
|
class IdentityHandler
|
46
56
|
def from_rep(v) v end
|
47
57
|
end
|
@@ -69,6 +79,9 @@ module Transit
|
|
69
79
|
class CmapHandler
|
70
80
|
def from_rep(v) Hash[*v] end
|
71
81
|
end
|
82
|
+
class RatioHandler
|
83
|
+
def from_rep(v) Rational(v[0], v[1]) end
|
84
|
+
end
|
72
85
|
|
73
86
|
DEFAULT_READ_HANDLERS = {
|
74
87
|
"_" => NilHandler.new,
|
@@ -86,10 +99,12 @@ module Transit
|
|
86
99
|
"u" => UuidHandler.new,
|
87
100
|
"r" => UriHandler.new,
|
88
101
|
"'" => IdentityHandler.new,
|
102
|
+
"z" => SpecialNumbersHandler.new,
|
89
103
|
"set" => SetHandler.new,
|
90
104
|
"link" => LinkHandler.new,
|
91
105
|
"list" => IdentityHandler.new,
|
92
|
-
"cmap" => CmapHandler.new
|
106
|
+
"cmap" => CmapHandler.new,
|
107
|
+
"ratio" => RatioHandler.new
|
93
108
|
}.freeze
|
94
109
|
|
95
110
|
DEFAULT_READ_HANDLER = Default.new
|