transit-ruby 0.8.552-java
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 +7 -0
- data/.yard_redcarpet_ext +1 -0
- data/.yardopts +5 -0
- data/Jarfile +10 -0
- data/LICENSE +202 -0
- data/README.md +173 -0
- data/lib/transit.jar +0 -0
- data/lib/transit.rb +108 -0
- data/lib/transit/date_time_util.rb +39 -0
- data/lib/transit/decoder.rb +115 -0
- data/lib/transit/marshaler/base.rb +179 -0
- data/lib/transit/marshaler/jruby/json.rb +46 -0
- data/lib/transit/marshaler/jruby/messagepack.rb +31 -0
- data/lib/transit/read_handlers.rb +113 -0
- data/lib/transit/reader.rb +65 -0
- data/lib/transit/rolling_cache.rb +70 -0
- data/lib/transit/transit_types.rb +251 -0
- data/lib/transit/write_handlers.rb +438 -0
- data/lib/transit/writer.rb +77 -0
- data/spec/spec_helper.rb +85 -0
- data/spec/transit/date_time_util_spec.rb +65 -0
- data/spec/transit/decoder_spec.rb +60 -0
- data/spec/transit/exemplar_spec.rb +150 -0
- data/spec/transit/reader_spec.rb +165 -0
- data/spec/transit/rolling_cache_spec.rb +94 -0
- data/spec/transit/round_trip_spec.rb +172 -0
- data/spec/transit/transit_types_spec.rb +136 -0
- data/spec/transit/writer_spec.rb +296 -0
- metadata +168 -0
@@ -0,0 +1,77 @@
|
|
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
|
+
class Writer
|
19
|
+
|
20
|
+
# @param [Symbol] format required :json, :json_verbose, or :msgpack
|
21
|
+
# @param [IO] io required
|
22
|
+
# @param [Hash] opts optional
|
23
|
+
#
|
24
|
+
# Creates a new Writer configured to write to <tt>io</tt> in
|
25
|
+
# <tt>format</tt> (<tt>:json</tt>, <tt>:json_verbose</tt>,
|
26
|
+
# <tt>:msgpack</tt>).
|
27
|
+
#
|
28
|
+
# Use opts to register custom write handlers, associating each one
|
29
|
+
# with its type.
|
30
|
+
#
|
31
|
+
# @example
|
32
|
+
# json_writer = Transit::Writer.new(:json, io)
|
33
|
+
# json_verbose_writer = Transit::Writer.new(:json_verbose, io)
|
34
|
+
# msgpack_writer = Transit::Writer.new(:msgpack, io)
|
35
|
+
# writer_with_custom_handlers = Transit::Writer.new(:json, io,
|
36
|
+
# :handlers => {Point => PointWriteHandler})
|
37
|
+
#
|
38
|
+
# @see Transit::WriteHandlers
|
39
|
+
def initialize(format, io, opts={})
|
40
|
+
@marshaler = case format
|
41
|
+
when :json
|
42
|
+
Marshaler::Json.new(io,
|
43
|
+
{:prefer_strings => true,
|
44
|
+
:verbose => false,
|
45
|
+
:handlers => {},
|
46
|
+
:oj_opts => {:indent => -1}}.merge(opts))
|
47
|
+
when :json_verbose
|
48
|
+
Marshaler::VerboseJson.new(io,
|
49
|
+
{:prefer_strings => true,
|
50
|
+
:verbose => true,
|
51
|
+
:handlers => {}}.merge(opts))
|
52
|
+
else
|
53
|
+
Marshaler::MessagePack.new(io,
|
54
|
+
{:prefer_strings => false,
|
55
|
+
:verbose => false,
|
56
|
+
:handlers => {}}.merge(opts))
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# Converts a Ruby object to a transit value and writes it to this
|
61
|
+
# Writer's output stream.
|
62
|
+
#
|
63
|
+
# @param obj the value to write
|
64
|
+
# @example
|
65
|
+
# writer = Transit::Writer.new(:json, io)
|
66
|
+
# writer.write(Date.new(2014,7,22))
|
67
|
+
if Transit::jruby?
|
68
|
+
def write(obj)
|
69
|
+
@marshaler.write(obj)
|
70
|
+
end
|
71
|
+
else
|
72
|
+
def write(obj)
|
73
|
+
@marshaler.marshal_top(obj)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,85 @@
|
|
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
|
+
unless File.exist?('../transit-format/examples/0.8/simple')
|
16
|
+
puts <<-MSG
|
17
|
+
Before you can run the rspec examples, you need to install the
|
18
|
+
the https://github.com/cognitect/transit-format repo in a sibling
|
19
|
+
directory, e.g.
|
20
|
+
|
21
|
+
cd ..
|
22
|
+
git clone https://github.com/cognitect/transit-format
|
23
|
+
|
24
|
+
That repo contains exemplars used by transit-ruby's rspec examples
|
25
|
+
(in ../transit-format/examples/0.8/simple), so then you can:
|
26
|
+
|
27
|
+
cd transit-ruby
|
28
|
+
rspec
|
29
|
+
|
30
|
+
MSG
|
31
|
+
exit
|
32
|
+
end
|
33
|
+
|
34
|
+
require 'json'
|
35
|
+
require 'rspec'
|
36
|
+
require 'wrong/adapters/rspec'
|
37
|
+
require 'transit'
|
38
|
+
require 'spec_helper-local' if File.exist?(File.expand_path('../spec_helper-local.rb', __FILE__))
|
39
|
+
|
40
|
+
RSpec.configure do |c|
|
41
|
+
c.alias_example_to :fit, :focus => true
|
42
|
+
c.filter_run_including :focus => true, :focused => true
|
43
|
+
c.run_all_when_everything_filtered = true
|
44
|
+
c.mock_with :nothing
|
45
|
+
end
|
46
|
+
|
47
|
+
ALPHA_NUM = 'abcdefghijklmnopqrstuvwxyzABCDESFHIJKLMNOPQRSTUVWXYZ_0123456789'
|
48
|
+
|
49
|
+
def random_alphanum
|
50
|
+
ALPHA_NUM[rand(ALPHA_NUM.size)]
|
51
|
+
end
|
52
|
+
|
53
|
+
def random_string(max_length=10)
|
54
|
+
l = rand(max_length) + 1
|
55
|
+
(Array.new(l).map {|x| random_alphanum}).join
|
56
|
+
end
|
57
|
+
|
58
|
+
def random_strings(max_length=10, n=100)
|
59
|
+
Array.new(n).map {random_string(max_length)}
|
60
|
+
end
|
61
|
+
|
62
|
+
def random_symbol(max_length=10)
|
63
|
+
random_string(max_length).to_sym
|
64
|
+
end
|
65
|
+
|
66
|
+
def ints_centered_on(m, n=5)
|
67
|
+
((m-n)..(m+n)).to_a
|
68
|
+
end
|
69
|
+
|
70
|
+
def array_of_symbols(m, n=m)
|
71
|
+
seeds = (0...m).map {|i| ("key%04d" % i).to_sym}
|
72
|
+
seeds.cycle.take(n)
|
73
|
+
end
|
74
|
+
|
75
|
+
def hash_of_size(n)
|
76
|
+
Hash[array_of_symbols(n).zip((0..n).to_a)]
|
77
|
+
end
|
78
|
+
|
79
|
+
Person = Struct.new("Person", :first_name, :last_name, :birthdate)
|
80
|
+
|
81
|
+
class PersonHandler
|
82
|
+
def tag(_) "person"; end
|
83
|
+
def rep(p) {:first_name => p.first_name, :last_name => p.last_name, :birthdate => p.birthdate} end
|
84
|
+
def string_rep(p) nil end
|
85
|
+
end
|
@@ -0,0 +1,65 @@
|
|
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 'spec_helper'
|
16
|
+
|
17
|
+
module Transit
|
18
|
+
describe DateTimeUtil do
|
19
|
+
describe "[to|from]_millis" do
|
20
|
+
it "round trips properly" do
|
21
|
+
100.times do
|
22
|
+
n = DateTime.now
|
23
|
+
a = Transit::DateTimeUtil.to_millis(n)
|
24
|
+
b = Transit::DateTimeUtil.from_millis(a)
|
25
|
+
c = Transit::DateTimeUtil.to_millis(b)
|
26
|
+
d = Transit::DateTimeUtil.from_millis(c)
|
27
|
+
assert { a == c }
|
28
|
+
assert { b == d }
|
29
|
+
sleep(0.0001)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe "to_millis" do
|
35
|
+
let(:time) { Time.at(1388631845) + 0.678 }
|
36
|
+
|
37
|
+
it "supports DateTime" do
|
38
|
+
assert { Transit::DateTimeUtil.to_millis(time.to_datetime) == 1388631845678 }
|
39
|
+
end
|
40
|
+
|
41
|
+
it "supports Time" do
|
42
|
+
assert { Transit::DateTimeUtil.to_millis(time) == 1388631845678 }
|
43
|
+
end
|
44
|
+
|
45
|
+
it "supports Date" do
|
46
|
+
assert { Transit::DateTimeUtil.to_millis(Date.new(2014,1,2)) == 1388620800000 }
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe "from_millis" do
|
51
|
+
it "converts to utc" do
|
52
|
+
t = DateTime.now
|
53
|
+
m = Transit::DateTimeUtil.to_millis(t)
|
54
|
+
f = Transit::DateTimeUtil.from_millis(m)
|
55
|
+
assert { f.zone == '+00:00' }
|
56
|
+
end
|
57
|
+
|
58
|
+
it "handles millis properly" do
|
59
|
+
assert { Transit::DateTimeUtil.from_millis(1388631845674) == DateTime.new(2014,1,2,3,4,5.674).new_offset(0) }
|
60
|
+
assert { Transit::DateTimeUtil.from_millis(1388631845675) == DateTime.new(2014,1,2,3,4,5.675).new_offset(0) }
|
61
|
+
assert { Transit::DateTimeUtil.from_millis(1388631845676) == DateTime.new(2014,1,2,3,4,5.676).new_offset(0) }
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
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 'spec_helper'
|
16
|
+
|
17
|
+
module Transit
|
18
|
+
describe Decoder do
|
19
|
+
def decode(o)
|
20
|
+
Decoder.new.decode(o)
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "caching" do
|
24
|
+
it "decodes cacheable map keys" do
|
25
|
+
assert { decode([{"this" => "a"},{"^0" => "b"}]) == [{"this" => "a"},{"this" => "b"}] }
|
26
|
+
end
|
27
|
+
|
28
|
+
it "does not cache non-map-keys" do
|
29
|
+
assert { decode([{"a" => "~^!"},{"b" => "~^?"}]) == [{"a" => "^!"},{"b" => "^?"}] }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe "formats" do
|
34
|
+
describe "JSON_M" do
|
35
|
+
it "converts an array starting with '^ ' to a map" do
|
36
|
+
assert { decode(["^ ", :a, :b, :c, :d]) == {:a => :b, :c => :d} }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe "unrecognized input" do
|
42
|
+
it "decodes an unrecognized string to a TaggedValue" do
|
43
|
+
assert { decode("~Unrecognized") == TaggedValue.new("U", "nrecognized") }
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe "ints" do
|
48
|
+
it "decodes n as an Integer" do
|
49
|
+
1.upto(64).each do |pow|
|
50
|
+
assert { decode("~n#{2**pow}").kind_of? Integer }
|
51
|
+
end
|
52
|
+
end
|
53
|
+
it "decodes i as an Integer" do
|
54
|
+
1.upto(63).each do |pow|
|
55
|
+
assert { decode("~i#{2**pow - 1}").kind_of? Integer }
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,150 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
# Copyright 2014 Cognitect. All Rights Reserved.
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS-IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
|
16
|
+
# -*- coding: utf-8 -*-
|
17
|
+
require 'spec_helper'
|
18
|
+
|
19
|
+
# Assumes that the examples are in the simple_examples dir at the top.
|
20
|
+
|
21
|
+
TOP_DIR=File.dirname(File.dirname(File.dirname(__FILE__)))
|
22
|
+
|
23
|
+
ARRAY_SIMPLE = [1, 2, 3]
|
24
|
+
ARRAY_MIXED = [0, 1, 2.0, true, false, 'five', :six, Transit::Symbol.new(:seven), '~eight', nil]
|
25
|
+
ARRAY_NESTED = [ARRAY_SIMPLE, ARRAY_MIXED]
|
26
|
+
SMALL_STRINGS = ["","a","ab","abc","abcd","abcde","abcdef"]
|
27
|
+
POWERS_OF_TWO = (0..65).map {|x| 2**x}
|
28
|
+
INTERESTING_INTS = (POWERS_OF_TWO.map {|x| ints_centered_on(x, 2)}).flatten
|
29
|
+
|
30
|
+
UUIDS = [
|
31
|
+
Transit::UUID.new('5a2cbea3-e8c6-428b-b525-21239370dd55'),
|
32
|
+
Transit::UUID.new('d1dc64fa-da79-444b-9fa4-d4412f427289'),
|
33
|
+
Transit::UUID.new('501a978e-3a3e-4060-b3be-1cf2bd4b1a38'),
|
34
|
+
Transit::UUID.new('b3ba141a-a776-48e4-9fae-a28ea8571f58')]
|
35
|
+
|
36
|
+
URIS = [
|
37
|
+
Addressable::URI.parse('http://example.com'),
|
38
|
+
Addressable::URI.parse('ftp://example.com'),
|
39
|
+
Addressable::URI.parse('file:///path/to/file.txt'),
|
40
|
+
Addressable::URI.parse('http://www.詹姆斯.com/')]
|
41
|
+
|
42
|
+
DATES = [-6106017600000, 0, 946728000000, 1396909037000].map {|x| Transit::DateTimeUtil.from_millis(x)}
|
43
|
+
|
44
|
+
SYMBOLS = [:a, :ab ,:abc ,:abcd, :abcde, :a1, :b2, :c3, :a_b]
|
45
|
+
TRANSIT_SYMBOLS = SYMBOLS.map {|x| Transit::Symbol.new(x)}
|
46
|
+
|
47
|
+
SET_SIMPLE = Set.new(ARRAY_SIMPLE)
|
48
|
+
SET_MIXED = Set.new(ARRAY_MIXED)
|
49
|
+
SET_NESTED= Set.new([SET_SIMPLE, SET_MIXED])
|
50
|
+
|
51
|
+
MAP_SIMPLE = {a: 1, b: 2, c: 3}
|
52
|
+
MAP_MIXED = {:a=>1, :b=>"a string", :c=>true}
|
53
|
+
MAP_NESTED = {simple: MAP_SIMPLE, mixed: MAP_MIXED}
|
54
|
+
|
55
|
+
Exemplar = Struct.new(:name, :expected_value)
|
56
|
+
|
57
|
+
EXEMPLARS = [
|
58
|
+
Exemplar.new('nil', nil),
|
59
|
+
Exemplar.new('true', true),
|
60
|
+
Exemplar.new('false', false),
|
61
|
+
Exemplar.new('zero', 0),
|
62
|
+
Exemplar.new('one', 1),
|
63
|
+
Exemplar.new('one_string', 'hello'),
|
64
|
+
Exemplar.new('one_keyword', :hello),
|
65
|
+
Exemplar.new('one_symbol', Transit::Symbol.new('hello')),
|
66
|
+
Exemplar.new('one_date', DateTime.new(2000,1,1,12)), # Transit::DateTimeUtil.from_millis(946728000000)),
|
67
|
+
Exemplar.new("vector_simple", ARRAY_SIMPLE),
|
68
|
+
Exemplar.new("vector_empty", []),
|
69
|
+
Exemplar.new("vector_mixed", ARRAY_MIXED),
|
70
|
+
Exemplar.new("vector_nested", ARRAY_NESTED),
|
71
|
+
Exemplar.new("small_strings", SMALL_STRINGS ),
|
72
|
+
Exemplar.new("strings_tilde", SMALL_STRINGS.map{|s| "~#{s}"}),
|
73
|
+
Exemplar.new("strings_hash", SMALL_STRINGS.map{|s| "##{s}"}),
|
74
|
+
Exemplar.new("strings_hat", SMALL_STRINGS.map{|s| "^#{s}"}),
|
75
|
+
Exemplar.new("small_ints", ints_centered_on(0)),
|
76
|
+
Exemplar.new("ints", (0...128).to_a),
|
77
|
+
Exemplar.new("ints_interesting", INTERESTING_INTS),
|
78
|
+
Exemplar.new("ints_interesting_neg", INTERESTING_INTS.map {|x| -1 * x}),
|
79
|
+
Exemplar.new("doubles_small", ints_centered_on(0).map {|x| Float(x)}),
|
80
|
+
Exemplar.new("doubles_interesting", [-3.14159, 3.14159, 4E11, 2.998E8, 6.626E-34]),
|
81
|
+
Exemplar.new('one_uuid', UUIDS.first),
|
82
|
+
Exemplar.new('uuids', UUIDS),
|
83
|
+
Exemplar.new('one_uri', URIS.first),
|
84
|
+
Exemplar.new('uris', URIS),
|
85
|
+
Exemplar.new('dates_interesting', DATES),
|
86
|
+
Exemplar.new('symbols', TRANSIT_SYMBOLS),
|
87
|
+
Exemplar.new('keywords', SYMBOLS),
|
88
|
+
Exemplar.new('list_simple', ARRAY_SIMPLE),
|
89
|
+
Exemplar.new('list_empty', []),
|
90
|
+
Exemplar.new('list_mixed', ARRAY_MIXED),
|
91
|
+
Exemplar.new('list_nested', [ARRAY_SIMPLE, ARRAY_MIXED]),
|
92
|
+
Exemplar.new('set_simple', SET_SIMPLE),
|
93
|
+
Exemplar.new("set_empty", Set.new),
|
94
|
+
Exemplar.new("set_mixed", SET_MIXED),
|
95
|
+
Exemplar.new("set_nested", SET_NESTED),
|
96
|
+
Exemplar.new('map_simple', MAP_SIMPLE),
|
97
|
+
Exemplar.new('map_mixed', MAP_MIXED),
|
98
|
+
Exemplar.new('map_nested', MAP_NESTED),
|
99
|
+
Exemplar.new('map_string_keys', {"first"=>1, "second"=>2, "third"=>3}),
|
100
|
+
Exemplar.new('map_numeric_keys', {1=>"one", 2=>"two"}),
|
101
|
+
Exemplar.new('map_vector_keys', {[1,1] => 'one', [2, 2] => 'two'}),
|
102
|
+
Exemplar.new('map_10_items', hash_of_size(10)),
|
103
|
+
Exemplar.new("maps_two_char_sym_keys", [{:aa=>1, :bb=>2}, {:aa=>3, :bb=>4}, {:aa=>5, :bb=>6}]),
|
104
|
+
Exemplar.new("maps_three_char_sym_keys", [{:aaa=>1, :bbb=>2}, {:aaa=>3, :bbb=>4}, {:aaa=>5, :bbb=>6}]),
|
105
|
+
Exemplar.new("maps_four_char_sym_keys", [{:aaaa=>1, :bbbb=>2}, {:aaaa=>3, :bbbb=>4}, {:aaaa=>5, :bbbb=>6}]),
|
106
|
+
Exemplar.new("maps_two_char_string_keys", [{'aa'=>1, 'bb'=>2}, {'aa'=>3, 'bb'=>4}, {'aa'=>5, 'bb'=>6}]),
|
107
|
+
Exemplar.new("maps_three_char_string_keys", [{'aaa'=>1, 'bbb'=>2}, {'aaa'=>3, 'bbb'=>4}, {'aaa'=>5, 'bbb'=>6}]),
|
108
|
+
Exemplar.new("maps_four_char_string_keys", [{'aaaa'=>1, 'bbbb'=>2}, {'aaaa'=>3, 'bbbb'=>4}, {'aaaa'=>5, 'bbbb'=>6}]),
|
109
|
+
Exemplar.new("maps_unrecognized_keys",
|
110
|
+
[Transit::TaggedValue.new("abcde", :anything), Transit::TaggedValue.new("fghij", :"anything-else")]),
|
111
|
+
Exemplar.new("map_unrecognized_vals", {key: "~Unrecognized"}),
|
112
|
+
Exemplar.new("vector_unrecognized_vals", ["~Unrecognized"]),
|
113
|
+
Exemplar.new("vector_1935_keywords_repeated_twice", array_of_symbols(1935, 3870)),
|
114
|
+
Exemplar.new("vector_1936_keywords_repeated_twice", array_of_symbols(1936, 3872)),
|
115
|
+
Exemplar.new("vector_1937_keywords_repeated_twice", array_of_symbols(1937, 3874)),
|
116
|
+
Exemplar.new("vector_special_numbers", [Float::NAN, Float::INFINITY, -Float::INFINITY])
|
117
|
+
]
|
118
|
+
|
119
|
+
[10, 1935, 1936, 1937].each do |i|
|
120
|
+
EXEMPLARS << Exemplar.new( "map_#{i}_nested", {f: hash_of_size(i), s: hash_of_size(i)})
|
121
|
+
end
|
122
|
+
|
123
|
+
def verify_exemplar(exemplar, type, suffix)
|
124
|
+
path = "../transit-format/examples/0.8/simple/#{exemplar.name}#{suffix}"
|
125
|
+
it "reads what we expect from #{path}" do
|
126
|
+
raise "Can't open #{path}" unless File.exist?(path)
|
127
|
+
File.open(path) do |io|
|
128
|
+
actual_value = Transit::Reader.new(type, io).read
|
129
|
+
assert { exemplar.expected_value == actual_value }
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
module Transit
|
135
|
+
shared_examples "exemplars" do |type, suffix|
|
136
|
+
EXEMPLARS.each {|ex| verify_exemplar(ex, type, suffix)}
|
137
|
+
end
|
138
|
+
|
139
|
+
describe "JSON exemplars" do
|
140
|
+
include_examples "exemplars", :json, '.json'
|
141
|
+
end
|
142
|
+
|
143
|
+
describe "JSON-VERBOSE exemplars" do
|
144
|
+
include_examples "exemplars", :json_verbose, '.verbose.json'
|
145
|
+
end
|
146
|
+
|
147
|
+
describe "MessagePack exemplars" do
|
148
|
+
include_examples "exemplars", :msgpack, '.mp'
|
149
|
+
end
|
150
|
+
end
|
@@ -0,0 +1,165 @@
|
|
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 'spec_helper'
|
16
|
+
|
17
|
+
module Transit
|
18
|
+
describe Reader do
|
19
|
+
shared_examples "read without a block" do |type|
|
20
|
+
it "reads a single top-level #{type} element" do
|
21
|
+
input = {:this => [1,2,3,{:that => "the other"}]}
|
22
|
+
|
23
|
+
io = StringIO.new('', 'w+')
|
24
|
+
writer = Transit::Writer.new(type, io)
|
25
|
+
writer.write(input)
|
26
|
+
|
27
|
+
reader = Transit::Reader.new(type, StringIO.new(io.string))
|
28
|
+
assert { reader.read == input }
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe "reading without a block" do
|
33
|
+
include_examples "read without a block", :json
|
34
|
+
include_examples "read without a block", :json_verbose
|
35
|
+
include_examples "read without a block", :msgpack
|
36
|
+
end
|
37
|
+
|
38
|
+
shared_examples "read with a block" do |type|
|
39
|
+
it "reads multiple top-level #{type} elements from a single IO" do
|
40
|
+
inputs = ["abc",
|
41
|
+
123456789012345678901234567890,
|
42
|
+
[:this, :that],
|
43
|
+
{:this => [1,2,3,{:that => "the other"}]}]
|
44
|
+
outputs = []
|
45
|
+
|
46
|
+
io = StringIO.new('', 'w+')
|
47
|
+
writer = Transit::Writer.new(type, io)
|
48
|
+
inputs.each {|i| writer.write(i)}
|
49
|
+
reader = Transit::Reader.new(type, StringIO.new(io.string))
|
50
|
+
reader.read {|val| outputs << val}
|
51
|
+
|
52
|
+
assert { outputs == inputs }
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
describe "reading with a block" do
|
57
|
+
include_examples "read with a block", :json
|
58
|
+
include_examples "read with a block", :json_verbose
|
59
|
+
include_examples "read with a block", :msgpack
|
60
|
+
end
|
61
|
+
|
62
|
+
describe 'handler registration' do
|
63
|
+
describe 'overrides' do
|
64
|
+
describe 'ground types' do
|
65
|
+
Decoder::GROUND_TAGS.each do |ground|
|
66
|
+
it "prevents override of #{ground} handler" do
|
67
|
+
assert {
|
68
|
+
rescuing {
|
69
|
+
Reader.new(:json, StringIO.new, :handlers => {ground => Object.new})
|
70
|
+
}.message =~ /ground types/ }
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'supports override of default string handlers' do
|
76
|
+
io = StringIO.new("[\"~rhttp://foo.com\"]","r+")
|
77
|
+
reader = Reader.new(:json, io, :handlers => {"r" => Class.new { def from_rep(v) "DECODED: #{v}" end}.new})
|
78
|
+
assert { reader.read == ["DECODED: http://foo.com"] }
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'supports override of default hash handlers' do
|
82
|
+
my_uuid_class = Class.new(String)
|
83
|
+
my_uuid = my_uuid_class.new(UUID.new.to_s)
|
84
|
+
io = StringIO.new({"~#u" => my_uuid.to_s}.to_json)
|
85
|
+
reader = Reader.new(:json, io, :handlers => {"u" => Class.new { define_method(:from_rep) {|v| my_uuid_class.new(v)}}.new})
|
86
|
+
assert { reader.read == my_uuid }
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'supports override of the default handler' do
|
90
|
+
io = StringIO.new("~Xabc".to_json)
|
91
|
+
reader = Reader.new(:json, io, :default_handler => Class.new { def from_rep(tag,val) raise "Unacceptable: #{s}" end}.new)
|
92
|
+
assert { rescuing {reader.read }.message =~ /Unacceptable/ }
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
describe 'extensions' do
|
97
|
+
it 'supports string-based extensions' do
|
98
|
+
io = StringIO.new("~D2014-03-15".to_json)
|
99
|
+
reader = Reader.new(:json, io, :handlers => {"D" => Class.new { def from_rep(v) Date.parse(v) end}.new})
|
100
|
+
assert { reader.read == Date.new(2014,3,15) }
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'supports hash based extensions' do
|
104
|
+
io = StringIO.new({"~#Times2" => 44}.to_json)
|
105
|
+
reader = Reader.new(:json, io, :handlers => {"Times2" => Class.new { def from_rep(v) v * 2 end}.new})
|
106
|
+
assert { reader.read == 88 }
|
107
|
+
end
|
108
|
+
|
109
|
+
it 'supports hash based extensions that return nil' do
|
110
|
+
io = StringIO.new({"~#Nil" => :anything}.to_json)
|
111
|
+
reader = Reader.new(:json, io, :handlers => {"Nil" => Class.new { def from_rep(_) nil end}.new})
|
112
|
+
assert { reader.read == nil }
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'supports hash based extensions that return false' do
|
116
|
+
io = StringIO.new({"~#False" => :anything}.to_json)
|
117
|
+
reader = Reader.new(:json, io, :handlers => {"False" => Class.new { def from_rep(_) false end}.new})
|
118
|
+
assert { reader.read == false }
|
119
|
+
end
|
120
|
+
|
121
|
+
it 'supports complex hash values' do
|
122
|
+
io = StringIO.new([
|
123
|
+
{"~#person"=>
|
124
|
+
{"~:first_name" => "Transit","~:last_name" => "Ruby","~:birthdate" => "~D2014-01-02"}},
|
125
|
+
{"^0"=>
|
126
|
+
{"^1" => "Transit","^2" => "Ruby","^3" => "~D2014-01-03"}}].to_json)
|
127
|
+
|
128
|
+
person_handler = Class.new do
|
129
|
+
def from_rep(v)
|
130
|
+
Person.new(v[:first_name],v[:last_name],v[:birthdate])
|
131
|
+
end
|
132
|
+
end
|
133
|
+
date_handler = Class.new do
|
134
|
+
def from_rep(v) Date.parse(v) end
|
135
|
+
end
|
136
|
+
reader = Reader.new(:json, io,
|
137
|
+
:handlers => {
|
138
|
+
"person" => person_handler.new,
|
139
|
+
"D" => date_handler.new})
|
140
|
+
expected = [Person.new("Transit", "Ruby", Date.new(2014,1,2)),
|
141
|
+
Person.new("Transit", "Ruby", Date.new(2014,1,3))]
|
142
|
+
assert { reader.read == expected }
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
describe 'Dates/Times' do
|
147
|
+
it "delivers a UTC DateTime for a non-UTC date string" do
|
148
|
+
io = StringIO.new(["~t2014-04-14T12:20:50.152-05:00"].to_json)
|
149
|
+
reader = Reader.new(:json, io)
|
150
|
+
expect(Transit::DateTimeUtil.to_millis(reader.read.first)).to eq(Transit::DateTimeUtil.to_millis(DateTime.new(2014,4,14,17,20,50.152,"Z")))
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
describe 'edge cases found in generative testing' do
|
155
|
+
it 'caches nested structures correctly' do
|
156
|
+
io = StringIO.new(["~#cmap",[["~#point",["~n10","~n11"]],"~:foobar",["^1",["~n10","~n13"]],"^2"]].to_json)
|
157
|
+
reader = Reader.new(:json, io)
|
158
|
+
expected = {TaggedValue.new("point", [10,11]) => :foobar,
|
159
|
+
TaggedValue.new("point", [10,13]) => :foobar}
|
160
|
+
assert { reader.read == expected }
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|