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