transit-ruby 0.8.552-java
Sign up to get free protection for your applications and to get access to all the features.
- 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
|