iostreams 1.4.0 → 1.5.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 +4 -4
- data/lib/io_streams/bzip2/reader.rb +1 -3
- data/lib/io_streams/bzip2/writer.rb +1 -3
- data/lib/io_streams/encode/reader.rb +0 -2
- data/lib/io_streams/io_streams.rb +0 -2
- data/lib/io_streams/line/reader.rb +5 -5
- data/lib/io_streams/path.rb +2 -0
- data/lib/io_streams/paths/sftp.rb +5 -2
- data/lib/io_streams/pgp.rb +5 -11
- data/lib/io_streams/tabular.rb +0 -2
- data/lib/io_streams/tabular/parser/fixed.rb +60 -30
- data/lib/io_streams/version.rb +1 -1
- data/test/tabular_test.rb +71 -40
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bef30882ae2eaebcfbc240858a6854f44cc54208465868e55fd6296addfc8015
|
4
|
+
data.tar.gz: e9984f367727934b96495a646aa271009681f9972568a0b51e9046edf451e321
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4c30cb2085ce36904551bcfce2d0ec1d4544d79af891bf27660c35daa4081e1f2edaf193332ca7f15ed8324f4aaddef991a906b896d502b1574c53531f27b4d5
|
7
|
+
data.tar.gz: 6066bb59b519568f99121d97ec0ba2637fab650889cc898a788f71d1dedcbce7167fa435206656d3ce607bb141eee5507501825afebbbd7e37e9c2c27c915f0f
|
@@ -3,9 +3,7 @@ module IOStreams
|
|
3
3
|
class Reader < IOStreams::Reader
|
4
4
|
# Read from a Bzip2 stream, decompressing the contents as it is read
|
5
5
|
def self.stream(input_stream, **args)
|
6
|
-
unless defined?(::Bzip2::FFI)
|
7
|
-
Utils.load_soft_dependency("bzip2-ffi", "Bzip2", "bzip2/ffi")
|
8
|
-
end
|
6
|
+
Utils.load_soft_dependency("bzip2-ffi", "Bzip2", "bzip2/ffi") unless defined?(::Bzip2::FFI)
|
9
7
|
|
10
8
|
begin
|
11
9
|
io = ::Bzip2::FFI::Reader.new(input_stream, args)
|
@@ -3,9 +3,7 @@ module IOStreams
|
|
3
3
|
class Writer < IOStreams::Writer
|
4
4
|
# Write to a stream, compressing with Bzip2
|
5
5
|
def self.stream(input_stream, original_file_name: nil, **args)
|
6
|
-
unless defined?(::Bzip2::FFI)
|
7
|
-
Utils.load_soft_dependency("bzip2-ffi", "Bzip2", "bzip2/ffi")
|
8
|
-
end
|
6
|
+
Utils.load_soft_dependency("bzip2-ffi", "Bzip2", "bzip2/ffi") unless defined?(::Bzip2::FFI)
|
9
7
|
|
10
8
|
begin
|
11
9
|
io = ::Bzip2::FFI::Writer.new(input_stream, args)
|
@@ -298,8 +298,6 @@ module IOStreams
|
|
298
298
|
@schemes[scheme_name.nil? ? nil : scheme_name.to_sym] || raise(ArgumentError, "Unknown Scheme type: #{scheme_name.inspect}")
|
299
299
|
end
|
300
300
|
|
301
|
-
private
|
302
|
-
|
303
301
|
Extension = Struct.new(:reader_class, :writer_class)
|
304
302
|
|
305
303
|
# Hold root paths
|
@@ -63,11 +63,11 @@ module IOStreams
|
|
63
63
|
# Auto-detect windows/linux line endings if not supplied. \n or \r\n
|
64
64
|
@delimiter ||= auto_detect_line_endings
|
65
65
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
66
|
+
return unless @buffer
|
67
|
+
|
68
|
+
# Change the delimiters encoding to match that of the input stream
|
69
|
+
@delimiter = @delimiter.encode(@buffer.encoding)
|
70
|
+
@delimiter_size = @delimiter.size
|
71
71
|
end
|
72
72
|
|
73
73
|
# Iterate over every line in the file/stream passing each line to supplied block in turn.
|
data/lib/io_streams/path.rb
CHANGED
@@ -82,6 +82,7 @@ module IOStreams
|
|
82
82
|
end
|
83
83
|
|
84
84
|
# Cleanup an incomplete write to the target "file" if the copy fails.
|
85
|
+
# rubocop:disable Lint/SuppressedException
|
85
86
|
def copy_from(source, **args)
|
86
87
|
super(source, **args)
|
87
88
|
rescue StandardError => e
|
@@ -91,6 +92,7 @@ module IOStreams
|
|
91
92
|
end
|
92
93
|
raise(e)
|
93
94
|
end
|
95
|
+
# rubocop:enable Lint/SuppressedException
|
94
96
|
|
95
97
|
# Moves the file by copying it to the new path and then deleting the current path.
|
96
98
|
# Returns [IOStreams::Path] the target path.
|
@@ -71,7 +71,9 @@ module IOStreams
|
|
71
71
|
# end
|
72
72
|
#
|
73
73
|
# # When using the sftp executable use an identity file instead of a password to authenticate:
|
74
|
-
# IOStreams.path("sftp://test.com/path/file_name.csv",
|
74
|
+
# IOStreams.path("sftp://test.com/path/file_name.csv",
|
75
|
+
# username: "jack",
|
76
|
+
# ssh_options: {IdentityFile: "~/.ssh/private_key"}).reader do |io|
|
75
77
|
# puts io.read
|
76
78
|
# end
|
77
79
|
def initialize(url, username: nil, password: nil, ssh_options: {})
|
@@ -122,7 +124,8 @@ module IOStreams
|
|
122
124
|
# end
|
123
125
|
#
|
124
126
|
# Example Output:
|
125
|
-
# sftp://sftp.example.org/a/b/c/test.txt {:type=>1, :size=>37, :owner=>"test_owner", :group=>"test_group",
|
127
|
+
# sftp://sftp.example.org/a/b/c/test.txt {:type=>1, :size=>37, :owner=>"test_owner", :group=>"test_group",
|
128
|
+
# :permissions=>420, :atime=>1572378136, :mtime=>1572378136, :link_count=>1, :extended=>{}}
|
126
129
|
def each_child(pattern = "*", case_sensitive: true, directories: false, hidden: false)
|
127
130
|
Utils.load_soft_dependency("net-sftp", "SFTP glob capability", "net/sftp") unless defined?(Net::SFTP)
|
128
131
|
|
data/lib/io_streams/pgp.rb
CHANGED
@@ -74,9 +74,10 @@ module IOStreams
|
|
74
74
|
|
75
75
|
raise(Pgp::Failure, "GPG Failed to generate key: #{err}#{out}") unless status.success?
|
76
76
|
|
77
|
-
|
78
|
-
|
79
|
-
|
77
|
+
match = err.match(/gpg: key ([0-9A-F]+)\s+/)
|
78
|
+
return unless match
|
79
|
+
|
80
|
+
match[1]
|
80
81
|
end
|
81
82
|
|
82
83
|
# Delete all private and public keys for a particular email.
|
@@ -110,11 +111,6 @@ module IOStreams
|
|
110
111
|
!list_keys(email: email, key_id: key_id, private: private).empty?
|
111
112
|
end
|
112
113
|
|
113
|
-
# Deprecated
|
114
|
-
def self.has_key?(**args)
|
115
|
-
key?(**args)
|
116
|
-
end
|
117
|
-
|
118
114
|
# Returns [Array<Hash>] the list of keys.
|
119
115
|
# Each Hash consists of:
|
120
116
|
# key_length: [Integer]
|
@@ -232,7 +228,7 @@ module IOStreams
|
|
232
228
|
err.each_line do |line|
|
233
229
|
if line =~ /secret key imported/
|
234
230
|
secret = true
|
235
|
-
elsif match = line.match(/key\s+(\w+):\s+(\w+).+\"(.*)<(.*)>\"/)
|
231
|
+
elsif (match = line.match(/key\s+(\w+):\s+(\w+).+\"(.*)<(.*)>\"/))
|
236
232
|
results << {
|
237
233
|
key_id: match[1].to_s.strip,
|
238
234
|
private: secret,
|
@@ -347,8 +343,6 @@ module IOStreams
|
|
347
343
|
end
|
348
344
|
end
|
349
345
|
|
350
|
-
private
|
351
|
-
|
352
346
|
@logger = nil
|
353
347
|
|
354
348
|
def self.logger
|
data/lib/io_streams/tabular.rb
CHANGED
@@ -37,6 +37,22 @@ module IOStreams
|
|
37
37
|
# The :size is the total size of this field including the `.` and the decimals.
|
38
38
|
# Number of :decimals
|
39
39
|
# Raises Errors::ValueTooLong when the supplied value cannot be rendered in `size` characters.
|
40
|
+
#
|
41
|
+
# In some circumstances the length of the last column is variable.
|
42
|
+
# layout: [Array<Hash>]
|
43
|
+
# [
|
44
|
+
# {size: 23, key: "name"},
|
45
|
+
# {size: :remainder, key: "rest"}
|
46
|
+
# ]
|
47
|
+
# By setting a size of `:remainder` it will take the rest of the line as the value for that column.
|
48
|
+
#
|
49
|
+
# A size of `:remainder` and no `:key` will discard the remainder of the line without validating the length.
|
50
|
+
# layout: [Array<Hash>]
|
51
|
+
# [
|
52
|
+
# {size: 23, key: "name"},
|
53
|
+
# {size: :remainder}
|
54
|
+
# ]
|
55
|
+
#
|
40
56
|
def initialize(layout:, truncate: true)
|
41
57
|
@layout = Layout.new(layout)
|
42
58
|
@truncate = truncate
|
@@ -57,12 +73,7 @@ module IOStreams
|
|
57
73
|
|
58
74
|
result = ""
|
59
75
|
layout.columns.each do |column|
|
60
|
-
|
61
|
-
if !truncate && (value.length > column.size)
|
62
|
-
raise(Errors::ValueTooLong, "Value: #{value.inspect} is too long to fit into column #{column.key} of size #{column.size}")
|
63
|
-
end
|
64
|
-
|
65
|
-
result << column.render(value)
|
76
|
+
result << column.render(hash[column.key], truncate)
|
66
77
|
end
|
67
78
|
result
|
68
79
|
end
|
@@ -74,13 +85,18 @@ module IOStreams
|
|
74
85
|
raise(Errors::TypeMismatch, "Line must be a String when format is :fixed. Actual: #{line.class.name}")
|
75
86
|
end
|
76
87
|
|
77
|
-
if line.length != layout.length
|
88
|
+
if layout.length.positive? && (line.length != layout.length)
|
78
89
|
raise(Errors::InvalidLineLength, "Expected line length: #{layout.length}, actual line length: #{line.length}")
|
79
90
|
end
|
80
91
|
|
81
92
|
hash = {}
|
82
93
|
index = 0
|
83
94
|
layout.columns.each do |column|
|
95
|
+
if column.size == -1
|
96
|
+
hash[column.key] = column.parse(line[index..-1]) if column.key
|
97
|
+
break
|
98
|
+
end
|
99
|
+
|
84
100
|
# Ignore "columns" that have no keys. E.g. Fillers
|
85
101
|
hash[column.key] = column.parse(line[index, column.size]) if column.key
|
86
102
|
index += column.size
|
@@ -93,8 +109,6 @@ module IOStreams
|
|
93
109
|
false
|
94
110
|
end
|
95
111
|
|
96
|
-
private
|
97
|
-
|
98
112
|
class Layout
|
99
113
|
attr_reader :columns, :length
|
100
114
|
|
@@ -113,7 +127,15 @@ module IOStreams
|
|
113
127
|
raise(Errors::InvalidLayout, "Missing required :size in: #{hash.inspect}") unless hash.key?(:size)
|
114
128
|
|
115
129
|
column = Column.new(**hash)
|
116
|
-
|
130
|
+
if column.size == -1
|
131
|
+
if @length == -1
|
132
|
+
raise(Errors::InvalidLayout, "Only the last :size can be '-1' or :remainder in: #{hash.inspect}")
|
133
|
+
end
|
134
|
+
|
135
|
+
@length = -1
|
136
|
+
else
|
137
|
+
@length += column.size
|
138
|
+
end
|
117
139
|
column
|
118
140
|
end
|
119
141
|
end
|
@@ -126,11 +148,13 @@ module IOStreams
|
|
126
148
|
|
127
149
|
def initialize(key: nil, size:, type: :string, decimals: 2)
|
128
150
|
@key = key
|
129
|
-
@size = size.to_i
|
151
|
+
@size = size == :remainder ? -1 : size.to_i
|
130
152
|
@type = type.to_sym
|
131
153
|
@decimals = decimals
|
132
154
|
|
133
|
-
|
155
|
+
unless @size.positive? || (@size == -1)
|
156
|
+
raise(Errors::InvalidLayout, "Size #{size.inspect} must be positive or :remainder")
|
157
|
+
end
|
134
158
|
raise(Errors::InvalidLayout, "Unknown type: #{type.inspect}") unless TYPES.include?(type)
|
135
159
|
end
|
136
160
|
|
@@ -151,27 +175,33 @@ module IOStreams
|
|
151
175
|
end
|
152
176
|
end
|
153
177
|
|
154
|
-
def render(value)
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
178
|
+
def render(value, truncate)
|
179
|
+
formatted =
|
180
|
+
case type
|
181
|
+
when :string
|
182
|
+
value = value.to_s
|
183
|
+
return value if size == -1
|
184
|
+
|
185
|
+
format(truncate ? "%-#{size}.#{size}s" : "%-#{size}s", value)
|
186
|
+
when :integer
|
187
|
+
return value.to_i.to_s if size == -1
|
188
|
+
|
189
|
+
truncate = false
|
190
|
+
format("%0#{size}d", value.to_i)
|
191
|
+
when :float
|
192
|
+
return value.to_f.to_s if size == -1
|
193
|
+
|
194
|
+
truncate = false
|
195
|
+
format("%0#{size}.#{decimals}f", value.to_f)
|
196
|
+
else
|
197
|
+
raise(Errors::InvalidLayout, "Unsupported type: #{type.inspect}")
|
162
198
|
end
|
163
199
|
|
164
|
-
|
165
|
-
|
166
|
-
formatted = format("%0#{size}.#{decimals}f", value.to_f)
|
167
|
-
if formatted.length > size
|
168
|
-
raise(Errors::ValueTooLong, "Value: #{value} is too large to fit into column:#{key} of size:#{size}")
|
169
|
-
end
|
170
|
-
|
171
|
-
formatted
|
172
|
-
else
|
173
|
-
raise(Errors::InvalidLayout, "Unsupported type: #{type.inspect}")
|
200
|
+
if !truncate && formatted.length > size
|
201
|
+
raise(Errors::ValueTooLong, "Value: #{value} is too large to fit into column:#{key} of size:#{size}")
|
174
202
|
end
|
203
|
+
|
204
|
+
formatted
|
175
205
|
end
|
176
206
|
end
|
177
207
|
end
|
data/lib/io_streams/version.rb
CHANGED
data/test/tabular_test.rb
CHANGED
@@ -10,6 +10,36 @@ class TabularTest < Minitest::Test
|
|
10
10
|
IOStreams::Tabular.new(columns: %w[first_field second third], format: format)
|
11
11
|
end
|
12
12
|
|
13
|
+
let :fixed do
|
14
|
+
layout = [
|
15
|
+
{size: 23, key: :name},
|
16
|
+
{size: 40, key: :address},
|
17
|
+
{size: 2},
|
18
|
+
{size: 5, key: :zip, type: :integer},
|
19
|
+
{size: 8, key: :age, type: :integer},
|
20
|
+
{size: 10, key: :weight, type: :float, decimals: 2}
|
21
|
+
]
|
22
|
+
IOStreams::Tabular.new(format: :fixed, format_options: {layout: layout})
|
23
|
+
end
|
24
|
+
|
25
|
+
let :fixed_with_remainder do
|
26
|
+
layout = [
|
27
|
+
{size: 23, key: :name},
|
28
|
+
{size: 40, key: :address},
|
29
|
+
{size: :remainder, key: :remainder}
|
30
|
+
]
|
31
|
+
IOStreams::Tabular.new(format: :fixed, format_options: {layout: layout})
|
32
|
+
end
|
33
|
+
|
34
|
+
let :fixed_discard_remainder do
|
35
|
+
layout = [
|
36
|
+
{size: 23, key: :name},
|
37
|
+
{size: 40, key: :address},
|
38
|
+
{size: :remainder}
|
39
|
+
]
|
40
|
+
IOStreams::Tabular.new(format: :fixed, format_options: {layout: layout})
|
41
|
+
end
|
42
|
+
|
13
43
|
describe "#parse_header" do
|
14
44
|
it "parses and sets the csv header" do
|
15
45
|
tabular = IOStreams::Tabular.new(format: :csv)
|
@@ -136,57 +166,55 @@ class TabularTest < Minitest::Test
|
|
136
166
|
end
|
137
167
|
|
138
168
|
describe ":fixed format" do
|
139
|
-
let :tabular do
|
140
|
-
layout = [
|
141
|
-
{size: 23, key: :name},
|
142
|
-
{size: 40, key: :address},
|
143
|
-
{size: 2},
|
144
|
-
{size: 5, key: :zip, type: :integer},
|
145
|
-
{size: 8, key: :age, type: :integer},
|
146
|
-
{size: 10, key: :weight, type: :float, decimals: 2}
|
147
|
-
]
|
148
|
-
IOStreams::Tabular.new(format: :fixed, format_options: {layout: layout})
|
149
|
-
end
|
150
|
-
|
151
169
|
it "parses to hash" do
|
152
|
-
assert hash =
|
170
|
+
assert hash = fixed.record_parse("Jack over there XX34618012345670012345.01")
|
153
171
|
assert_equal({name: "Jack", address: "over there", zip: 34_618, age: 1_234_567, weight: 12_345.01}, hash)
|
154
172
|
end
|
155
173
|
|
156
174
|
it "parses short string" do
|
157
175
|
assert_raises IOStreams::Errors::InvalidLineLength do
|
158
|
-
|
176
|
+
fixed.record_parse("Jack over th")
|
159
177
|
end
|
160
178
|
end
|
161
179
|
|
162
180
|
it "parses longer string" do
|
163
181
|
assert_raises IOStreams::Errors::InvalidLineLength do
|
164
|
-
|
182
|
+
fixed.record_parse("Jack over there XX34618012345670012345.01............")
|
165
183
|
end
|
166
184
|
end
|
167
185
|
|
168
186
|
it "parses zero values" do
|
169
|
-
assert hash =
|
187
|
+
assert hash = fixed.record_parse(" 00000000000000000000000")
|
170
188
|
assert_equal({name: "", address: "", zip: 0, age: 0, weight: 0.0}, hash)
|
171
189
|
end
|
172
190
|
|
173
191
|
it "parses empty values" do
|
174
|
-
assert hash =
|
192
|
+
assert hash = fixed.record_parse(" XX ")
|
175
193
|
assert_equal({name: "", address: "", zip: nil, age: nil, weight: nil}, hash)
|
176
194
|
end
|
177
195
|
|
178
196
|
it "parses blank strings" do
|
179
|
-
skip "TODO: Part of
|
180
|
-
assert hash =
|
197
|
+
skip "TODO: Part of fixed refactor to get this working"
|
198
|
+
assert hash = fixed.record_parse(" ")
|
181
199
|
assert_equal({name: "", address: "", zip: nil, age: nil, weight: nil}, hash)
|
182
200
|
end
|
183
201
|
|
184
202
|
it "parses nil data as nil" do
|
185
|
-
refute
|
203
|
+
refute fixed.record_parse(nil)
|
186
204
|
end
|
187
205
|
|
188
206
|
it "parses empty string as nil" do
|
189
|
-
refute
|
207
|
+
refute fixed.record_parse("")
|
208
|
+
end
|
209
|
+
|
210
|
+
it "parses remainder" do
|
211
|
+
hash = fixed_with_remainder.record_parse("Jack over there XX34618012345670012345.01............")
|
212
|
+
assert_equal({name: "Jack", address: "over there", remainder: "XX34618012345670012345.01............"}, hash)
|
213
|
+
end
|
214
|
+
|
215
|
+
it "discards remainder" do
|
216
|
+
hash = fixed_discard_remainder.record_parse("Jack over there XX34618012345670012345.01............")
|
217
|
+
assert_equal({name: "Jack", address: "over there"}, hash)
|
190
218
|
end
|
191
219
|
end
|
192
220
|
|
@@ -236,52 +264,55 @@ class TabularTest < Minitest::Test
|
|
236
264
|
end
|
237
265
|
|
238
266
|
describe ":fixed format" do
|
239
|
-
let :tabular do
|
240
|
-
layout = [
|
241
|
-
{size: 23, key: :name},
|
242
|
-
{size: 40, key: :address},
|
243
|
-
{size: 2},
|
244
|
-
{size: 5, key: :zip, type: :integer},
|
245
|
-
{size: 8, key: :age, type: :integer},
|
246
|
-
{size: 10, key: :weight, type: :float, decimals: 2}
|
247
|
-
]
|
248
|
-
IOStreams::Tabular.new(format: :fixed, format_options: {layout: layout})
|
249
|
-
end
|
250
|
-
|
251
267
|
it "renders fixed data" do
|
252
|
-
assert string =
|
268
|
+
assert string = fixed.render(name: "Jack", address: "over there", zip: 34_618, weight: 123_456.789123, age: 21)
|
253
269
|
assert_equal "Jack over there 34618000000210123456.79", string
|
254
270
|
end
|
255
271
|
|
256
272
|
it "truncates long strings" do
|
257
|
-
assert string =
|
273
|
+
assert string = fixed.render(name: "Jack ran up the beanstalk and when jack reached the top it was truncated", address: "over there", zip: 34_618)
|
258
274
|
assert_equal "Jack ran up the beanstaover there 34618000000000000000.00", string
|
259
275
|
end
|
260
276
|
|
261
277
|
it "when integer is too large" do
|
262
278
|
assert_raises IOStreams::Errors::ValueTooLong do
|
263
|
-
|
279
|
+
fixed.render(zip: 3_461_832_653_653_265)
|
264
280
|
end
|
265
281
|
end
|
266
282
|
|
267
283
|
it "when float is too large" do
|
268
284
|
assert_raises IOStreams::Errors::ValueTooLong do
|
269
|
-
|
285
|
+
fixed.render(weight: 3_461_832_653_653_265.234)
|
270
286
|
end
|
271
287
|
end
|
272
288
|
|
273
289
|
it "renders nil as empty string" do
|
274
|
-
assert string =
|
290
|
+
assert string = fixed.render(zip: 34_618)
|
275
291
|
assert_equal " 34618000000000000000.00", string
|
276
292
|
end
|
277
293
|
|
278
294
|
it "renders boolean" do
|
279
|
-
assert string =
|
295
|
+
assert string = fixed.render(name: true, address: false)
|
280
296
|
assert_equal "true false 00000000000000000000.00", string
|
281
297
|
end
|
282
298
|
|
283
299
|
it "renders no data as nil" do
|
284
|
-
refute
|
300
|
+
refute fixed.render({})
|
301
|
+
end
|
302
|
+
|
303
|
+
it "any size last string" do
|
304
|
+
assert string = fixed_with_remainder.render(name: "Jack", address: "over there", remainder: "XX34618012345670012345.01............")
|
305
|
+
assert_equal "Jack over there XX34618012345670012345.01............", string
|
306
|
+
end
|
307
|
+
|
308
|
+
it "nil last string" do
|
309
|
+
assert string = fixed_with_remainder.render(name: "Jack", address: "over there", remainder: nil)
|
310
|
+
assert_equal "Jack over there ", string
|
311
|
+
end
|
312
|
+
|
313
|
+
it "skips last filler" do
|
314
|
+
assert string = fixed_discard_remainder.render(name: "Jack", address: "over there")
|
315
|
+
assert_equal "Jack over there ", string
|
285
316
|
end
|
286
317
|
end
|
287
318
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: iostreams
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Reid Morrison
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-09-
|
11
|
+
date: 2020-09-10 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description:
|
14
14
|
email:
|