iostreams 0.18.0 → 0.19.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/io_streams/errors.rb +3 -0
- data/lib/io_streams/http/reader.rb +71 -0
- data/lib/io_streams/io_streams.rb +2 -2
- data/lib/io_streams/path.rb +85 -0
- data/lib/io_streams/s3/reader.rb +6 -8
- data/lib/io_streams/version.rb +1 -1
- data/lib/io_streams/xlsx/reader.rb +16 -16
- data/lib/io_streams/zip/reader.rb +5 -16
- data/lib/io_streams/zip/writer.rb +4 -11
- data/lib/iostreams.rb +4 -0
- data/test/http_reader_test.rb +38 -0
- data/test/path_test.rb +74 -0
- data/test/test_helper.rb +5 -0
- metadata +8 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 161967be051ed1b82c87f30afd32b760a2e6627cf8e090e978578c7c35aab278
|
4
|
+
data.tar.gz: abb7aab7f5aca8cc0d043ce3820603c86b3227c9db41d07420f3117010bf68af
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2172e682359bfe1240669fee5da16f807b5a04b940529c5a7046166d5343e7c80bd20c52f05243b59c72f7cfe57ac288603ac6475e75f413f8f99031f3e1cbd0
|
7
|
+
data.tar.gz: 76923520dfda93c00b4209424496c5324ae7975b59f0f8653578652b540c07a9dd6866890b72c147ab5c968e48879210e3aaa85ab8c8ffbe4b4921524352eb32
|
data/lib/io_streams/errors.rb
CHANGED
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'uri'
|
3
|
+
module IOStreams
|
4
|
+
module HTTP
|
5
|
+
# Read a file using an http get.
|
6
|
+
#
|
7
|
+
# For example:
|
8
|
+
# IOStreams.reader('https://www5.fdic.gov/idasp/Offices2.zip') {|file| puts file.read}
|
9
|
+
#
|
10
|
+
# Direct example without unzipping the above file:
|
11
|
+
# IOStreams::HTTP::Reader.new('https://www5.fdic.gov/idasp/Offices2.zip') {|file| puts file.read}
|
12
|
+
#
|
13
|
+
# Parameters:
|
14
|
+
# uri: [String|URI]
|
15
|
+
# URI of the file to download.
|
16
|
+
# Example:
|
17
|
+
# https://www5.fdic.gov/idasp/Offices2.zip
|
18
|
+
#
|
19
|
+
# :username
|
20
|
+
# When supplied, basic authentication is used with the username and password.
|
21
|
+
# Default: nil
|
22
|
+
#
|
23
|
+
# :password
|
24
|
+
# Password to use use with basic authentication when the username is supplied.
|
25
|
+
#
|
26
|
+
# Notes:
|
27
|
+
# * Since Net::HTTP download only supports a push stream, the data is streamed into a tempfile first.
|
28
|
+
class Reader
|
29
|
+
def self.open(uri, username: nil, password: nil, **args, &block)
|
30
|
+
raise(ArgumentError, 'file_name must be a URI string') unless uri.is_a?(String) || uri.is_a?(URI)
|
31
|
+
handle_redirects(uri, username: username, password: password, **args, &block)
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.handle_redirects(uri, username: nil, password: nil, http_redirect_count: 10, **args, &block)
|
35
|
+
uri = URI.parse(uri) unless uri.is_a?(URI)
|
36
|
+
result = nil
|
37
|
+
raise(IOStreams::Errors::CommunicationsFailure, "Too many redirects") if http_redirect_count < 1
|
38
|
+
|
39
|
+
Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http|
|
40
|
+
request = Net::HTTP::Get.new(uri)
|
41
|
+
request.basic_auth(username, password) if username
|
42
|
+
|
43
|
+
http.request(request) do |response|
|
44
|
+
if response.is_a?(Net::HTTPNotFound)
|
45
|
+
raise(IOStreams::Errors::CommunicationsFailure, "Invalid URL: #{uri}")
|
46
|
+
end
|
47
|
+
if response.is_a?(Net::HTTPUnauthorized)
|
48
|
+
raise(IOStreams::Errors::CommunicationsFailure, "Authorization Required: Invalid :username or :password.")
|
49
|
+
end
|
50
|
+
if response.is_a?(Net::HTTPRedirection)
|
51
|
+
new_uri = response['location']
|
52
|
+
return handle_redirects(new_uri, username: username, password: password, http_redirect_count: http_redirect_count - 1, **args, &block)
|
53
|
+
end
|
54
|
+
|
55
|
+
raise(IOStreams::Errors::CommunicationsFailure, "Invalid response code: #{response.code}") unless response.is_a?(Net::HTTPSuccess)
|
56
|
+
|
57
|
+
# Since Net::HTTP download only supports a push stream, write it to a tempfile first.
|
58
|
+
IOStreams::Path.temp_file_name('iostreams_http') do |file_name|
|
59
|
+
IOStreams::File::Writer.open(file_name) do |io|
|
60
|
+
response.read_body { |chunk| io.write(chunk) }
|
61
|
+
end
|
62
|
+
# Return a read stream
|
63
|
+
result = IOStreams::File::Reader.open(file_name, &block)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
result
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -585,8 +585,8 @@ module IOStreams
|
|
585
585
|
# sftp://hostname/path/file_name
|
586
586
|
# s3://bucket/key
|
587
587
|
register_scheme(nil, IOStreams::File::Reader, IOStreams::File::Writer)
|
588
|
-
|
589
|
-
|
588
|
+
register_scheme(:http, IOStreams::HTTP::Reader, nil)
|
589
|
+
register_scheme(:https, IOStreams::HTTP::Reader, nil)
|
590
590
|
# register_scheme(:sftp, IOStreams::SFTP::Reader, IOStreams::SFTP::Writer)
|
591
591
|
register_scheme(:s3, IOStreams::S3::Reader, IOStreams::S3::Writer)
|
592
592
|
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
module IOStreams
|
5
|
+
#
|
6
|
+
# NOTE: This is a proof of concept class and will change significantly.
|
7
|
+
# I.e. Dont use it yet.
|
8
|
+
#
|
9
|
+
class Path
|
10
|
+
attr_reader :root, :relative
|
11
|
+
|
12
|
+
# Return named root path
|
13
|
+
def self.[](root)
|
14
|
+
@roots[root.to_sym] || raise(ArgumentError, "Unknown root: #{root.inspect}")
|
15
|
+
end
|
16
|
+
|
17
|
+
# Add a named root path
|
18
|
+
def self.add_root(root, path)
|
19
|
+
@roots[root.to_sym] = path.dup.freeze
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.roots
|
23
|
+
@roots.dup
|
24
|
+
end
|
25
|
+
|
26
|
+
# Yields the path to a temporary file_name.
|
27
|
+
#
|
28
|
+
# File is deleted upon completion if present.
|
29
|
+
def self.temp_file_name(basename, extension = '')
|
30
|
+
result = nil
|
31
|
+
::Dir::Tmpname.create([basename, extension]) do |tmpname|
|
32
|
+
begin
|
33
|
+
result = yield(tmpname)
|
34
|
+
ensure
|
35
|
+
::File.unlink(tmpname) if ::File.exist?(tmpname)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
result
|
39
|
+
end
|
40
|
+
|
41
|
+
def initialize(*elements, root: :default)
|
42
|
+
@root = root.to_sym
|
43
|
+
root_path = self.class[@root]
|
44
|
+
if elements.empty?
|
45
|
+
@relative = ''
|
46
|
+
@path = root_path
|
47
|
+
else
|
48
|
+
@relative = ::File.join(*elements).freeze
|
49
|
+
if @relative.start_with?(root_path)
|
50
|
+
@path = @relative
|
51
|
+
@relative = @path[root_path.size + 1..-1].freeze
|
52
|
+
else
|
53
|
+
@path = ::File.join(root_path, @relative).freeze
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def to_s
|
59
|
+
@path
|
60
|
+
end
|
61
|
+
|
62
|
+
# Creates the entire path excluding the file_name.
|
63
|
+
def mkpath
|
64
|
+
path = ::File.dirname(@path)
|
65
|
+
FileUtils.mkdir_p(path) unless ::File.exist?(path)
|
66
|
+
self
|
67
|
+
end
|
68
|
+
|
69
|
+
def exist?
|
70
|
+
::File.exist?(@path)
|
71
|
+
end
|
72
|
+
|
73
|
+
# Delete the file.
|
74
|
+
#
|
75
|
+
# Note: Only the file is removed, not any of the parent paths.
|
76
|
+
def delete
|
77
|
+
::File.unlink(@path)
|
78
|
+
self
|
79
|
+
end
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
@roots = {}
|
84
|
+
end
|
85
|
+
end
|
data/lib/io_streams/s3/reader.rb
CHANGED
@@ -13,15 +13,13 @@ module IOStreams
|
|
13
13
|
|
14
14
|
begin
|
15
15
|
# Since S3 download only supports a push stream, write it to a tempfile first.
|
16
|
-
|
17
|
-
|
16
|
+
IOStreams::Path.temp_file_name('iostreams_s3') do |file_name|
|
17
|
+
args[:response_target] = file_name
|
18
|
+
object.get(args)
|
18
19
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
block.call(temp_file)
|
23
|
-
ensure
|
24
|
-
temp_file.delete if temp_file
|
20
|
+
# Return a read stream
|
21
|
+
IOStreams::File::Reader.open(file_name, &block)
|
22
|
+
end
|
25
23
|
end
|
26
24
|
end
|
27
25
|
end
|
data/lib/io_streams/version.rb
CHANGED
@@ -4,24 +4,24 @@ module IOStreams
|
|
4
4
|
module Xlsx
|
5
5
|
class Reader
|
6
6
|
# Convert a xlsx, or xlsm file or stream into CSV format.
|
7
|
-
def self.open(file_name_or_io, _ = nil)
|
8
|
-
if file_name_or_io.is_a?(String)
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
file_name = temp_file.to_path
|
7
|
+
def self.open(file_name_or_io, _ = nil, &block)
|
8
|
+
return extract_csv(file_name_or_io, &block) if file_name_or_io.is_a?(String)
|
9
|
+
|
10
|
+
# Creek gem can only work against a file, not a stream, so create temp file.
|
11
|
+
IOStreams::Path.temp_file_name('iostreams_xlsx') do |temp_file_name|
|
12
|
+
IOStreams.copy(file_name_or_io, temp_file_name, target_options: {streams: []})
|
13
|
+
extract_csv(temp_file_name, &block)
|
15
14
|
end
|
15
|
+
end
|
16
16
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
17
|
+
# Convert the spreadsheet to csv in a tempfile
|
18
|
+
def self.extract_csv(file_name, &block)
|
19
|
+
IOStreams::Path.temp_file_name('iostreams_csv') do |temp_file_name|
|
20
|
+
IOStreams::File::Writer.open(temp_file_name) do |io|
|
21
|
+
new(file_name).each { |lines| io << lines.to_csv }
|
22
|
+
end
|
23
|
+
IOStreams::File::Reader.open(temp_file_name, &block)
|
24
|
+
end
|
25
25
|
end
|
26
26
|
|
27
27
|
def initialize(file_name)
|
@@ -12,7 +12,7 @@ module IOStreams
|
|
12
12
|
# puts data
|
13
13
|
# end
|
14
14
|
# end
|
15
|
-
def self.open(file_name_or_io,
|
15
|
+
def self.open(file_name_or_io, _ = nil, &block)
|
16
16
|
if !defined?(JRuby) && !defined?(::Zip)
|
17
17
|
# MRI needs Ruby Zip, since it only has native support for GZip
|
18
18
|
begin
|
@@ -25,21 +25,10 @@ module IOStreams
|
|
25
25
|
# File name supplied
|
26
26
|
return read_file(file_name_or_io, &block) unless IOStreams.reader_stream?(file_name_or_io)
|
27
27
|
|
28
|
-
#
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
temp_file.binmode
|
33
|
-
file_name = temp_file.to_path
|
34
|
-
|
35
|
-
# Stream zip stream into temp file
|
36
|
-
::File.open(file_name, 'wb') do |file|
|
37
|
-
IOStreams.copy(file_name_or_io, file, buffer_size: buffer_size)
|
38
|
-
end
|
39
|
-
|
40
|
-
read_file(file_name, &block)
|
41
|
-
ensure
|
42
|
-
temp_file.delete if temp_file
|
28
|
+
# ZIP can only work against a file, not a stream, so create temp file.
|
29
|
+
IOStreams::Path.temp_file_name('iostreams_zip') do |temp_file_name|
|
30
|
+
IOStreams.copy(file_name_or_io, temp_file_name, target_options: {streams: []})
|
31
|
+
read_file(temp_file_name, &block)
|
43
32
|
end
|
44
33
|
end
|
45
34
|
|
@@ -38,17 +38,10 @@ module IOStreams
|
|
38
38
|
# File name supplied
|
39
39
|
return write_file(file_name_or_io, zip_file_name, &block) unless IOStreams.writer_stream?(file_name_or_io)
|
40
40
|
|
41
|
-
#
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
temp_file.binmode
|
46
|
-
write_file(temp_file.to_path, zip_file_name, &block)
|
47
|
-
|
48
|
-
# Stream temp file into output stream
|
49
|
-
IOStreams.copy(temp_file, file_name_or_io, buffer_size: buffer_size)
|
50
|
-
ensure
|
51
|
-
temp_file.delete if temp_file
|
41
|
+
# ZIP can only work against a file, not a stream, so create temp file.
|
42
|
+
IOStreams::Path.temp_file_name('iostreams_zip') do |temp_file_name|
|
43
|
+
write_file(temp_file_name, zip_file_name, &block)
|
44
|
+
IOStreams.copy(temp_file_name, file_name_or_io, source_options: {streams: []})
|
52
45
|
end
|
53
46
|
end
|
54
47
|
|
data/lib/iostreams.rb
CHANGED
@@ -15,6 +15,10 @@ module IOStreams
|
|
15
15
|
autoload :Reader, 'io_streams/gzip/reader'
|
16
16
|
autoload :Writer, 'io_streams/gzip/writer'
|
17
17
|
end
|
18
|
+
module HTTP
|
19
|
+
autoload :Reader, 'io_streams/http/reader'
|
20
|
+
end
|
21
|
+
autoload :Path, 'io_streams/path'
|
18
22
|
autoload :Pgp, 'io_streams/pgp'
|
19
23
|
autoload :S3, 'io_streams/s3'
|
20
24
|
module SFTP
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require_relative 'test_helper'
|
2
|
+
|
3
|
+
class HTTPReaderTest < Minitest::Test
|
4
|
+
describe IOStreams::HTTP::Reader do
|
5
|
+
let :uri do
|
6
|
+
"http://example.com/index.html?count=10"
|
7
|
+
end
|
8
|
+
|
9
|
+
let :ssl_uri do
|
10
|
+
"https://example.com/index.html?count=10"
|
11
|
+
end
|
12
|
+
|
13
|
+
describe '.open' do
|
14
|
+
it 'reads http' do
|
15
|
+
result = IOStreams::HTTP::Reader.open(uri) do |io|
|
16
|
+
io.read
|
17
|
+
end
|
18
|
+
assert_includes result, "<html>"
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'reads https' do
|
22
|
+
result = IOStreams::HTTP::Reader.open(ssl_uri) do |io|
|
23
|
+
io.read
|
24
|
+
end
|
25
|
+
assert_includes result, "<html>"
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'does not support streams' do
|
29
|
+
assert_raises ArgumentError do
|
30
|
+
io = StringIO.new
|
31
|
+
IOStreams::HTTP::Reader.open(io) do |http_io|
|
32
|
+
http_io.read
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/test/path_test.rb
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
require_relative 'test_helper'
|
2
|
+
|
3
|
+
module IOStreams
|
4
|
+
class PathTest < Minitest::Test
|
5
|
+
describe IOStreams::Path do
|
6
|
+
describe '.root' do
|
7
|
+
it 'return default path' do
|
8
|
+
path = ::File.expand_path(::File.join(__dir__, '../tmp/default'))
|
9
|
+
assert_equal path, IOStreams::Path[:default]
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'return downloads path' do
|
13
|
+
path = ::File.expand_path(::File.join(__dir__, '../tmp/downloads'))
|
14
|
+
assert_equal path, IOStreams::Path[:downloads]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe '.to_s' do
|
19
|
+
it 'returns path' do
|
20
|
+
assert_equal IOStreams::Path[:default], IOStreams::Path.new.to_s
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'adds path to root' do
|
24
|
+
assert_equal ::File.join(IOStreams::Path[:default], 'test'), IOStreams::Path.new('test').to_s
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'adds paths to root' do
|
28
|
+
assert_equal ::File.join(IOStreams::Path[:default], 'test', 'second', 'third'), IOStreams::Path.new('test', 'second', 'third').to_s
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'returns path and filename' do
|
32
|
+
path = ::File.join(IOStreams::Path[:default], 'file.xls')
|
33
|
+
assert_equal path, IOStreams::Path.new('file.xls').to_s
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'adds path to root and filename' do
|
37
|
+
path = ::File.join(IOStreams::Path[:default], 'test', 'file.xls')
|
38
|
+
assert_equal path, IOStreams::Path.new('test', 'file.xls').to_s
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'adds paths to root' do
|
42
|
+
path = ::File.join(IOStreams::Path[:default], 'test', 'second', 'third', 'file.xls')
|
43
|
+
assert_equal path, IOStreams::Path.new('test', 'second', 'third', 'file.xls').to_s
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'return path as sent in when full path' do
|
47
|
+
path = ::File.join(IOStreams::Path[:default], 'file.xls')
|
48
|
+
assert_equal path, IOStreams::Path.new(path).to_s
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe '.mkpath' do
|
53
|
+
it 'makes root' do
|
54
|
+
path = IOStreams::Path.new('test.xls')
|
55
|
+
assert_equal path, path.mkpath
|
56
|
+
assert ::File.exist?(IOStreams::Path.new.to_s)
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'makes root with path' do
|
60
|
+
path = IOStreams::Path.new('test', 'test.xls')
|
61
|
+
assert_equal path, path.mkpath
|
62
|
+
assert ::File.exist?(IOStreams::Path.new('test').to_s)
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'makes root with paths' do
|
66
|
+
path = IOStreams::Path.new('test', 'second', 'third', 'test.xls')
|
67
|
+
assert_equal path, path.mkpath
|
68
|
+
assert ::File.exist?(IOStreams::Path.new('test', 'second', 'third').to_s)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
data/test/test_helper.rb
CHANGED
@@ -32,3 +32,8 @@ unless IOStreams::Pgp.has_key?(email: 'receiver@example.org')
|
|
32
32
|
puts 'Generating test PGP key: receiver@example.org'
|
33
33
|
IOStreams::Pgp.generate_key(name: 'Receiver', email: 'receiver@example.org', passphrase: 'receiver_passphrase', key_length: 2048)
|
34
34
|
end
|
35
|
+
|
36
|
+
# Test paths
|
37
|
+
root = File.expand_path(File.join(__dir__, '../tmp'))
|
38
|
+
IOStreams::Path.add_root(:default, File.join(root, 'default'))
|
39
|
+
IOStreams::Path.add_root(:downloads, File.join(root, 'downloads'))
|
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: 0.
|
4
|
+
version: 0.19.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: 2019-08-
|
11
|
+
date: 2019-08-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: concurrent-ruby
|
@@ -43,9 +43,11 @@ files:
|
|
43
43
|
- lib/io_streams/file/writer.rb
|
44
44
|
- lib/io_streams/gzip/reader.rb
|
45
45
|
- lib/io_streams/gzip/writer.rb
|
46
|
+
- lib/io_streams/http/reader.rb
|
46
47
|
- lib/io_streams/io_streams.rb
|
47
48
|
- lib/io_streams/line/reader.rb
|
48
49
|
- lib/io_streams/line/writer.rb
|
50
|
+
- lib/io_streams/path.rb
|
49
51
|
- lib/io_streams/pgp.rb
|
50
52
|
- lib/io_streams/pgp/reader.rb
|
51
53
|
- lib/io_streams/pgp/writer.rb
|
@@ -93,9 +95,11 @@ files:
|
|
93
95
|
- test/files/unclosed_quote_test.csv
|
94
96
|
- test/gzip_reader_test.rb
|
95
97
|
- test/gzip_writer_test.rb
|
98
|
+
- test/http_reader_test.rb
|
96
99
|
- test/io_streams_test.rb
|
97
100
|
- test/line_reader_test.rb
|
98
101
|
- test/line_writer_test.rb
|
102
|
+
- test/path_test.rb
|
99
103
|
- test/pgp_reader_test.rb
|
100
104
|
- test/pgp_test.rb
|
101
105
|
- test/pgp_writer_test.rb
|
@@ -145,6 +149,7 @@ test_files:
|
|
145
149
|
- test/file_reader_test.rb
|
146
150
|
- test/record_reader_test.rb
|
147
151
|
- test/s3_writer_test.rb
|
152
|
+
- test/http_reader_test.rb
|
148
153
|
- test/pgp_writer_test.rb
|
149
154
|
- test/line_writer_test.rb
|
150
155
|
- test/row_reader_test.rb
|
@@ -165,6 +170,7 @@ test_files:
|
|
165
170
|
- test/test_helper.rb
|
166
171
|
- test/file_writer_test.rb
|
167
172
|
- test/tabular_test.rb
|
173
|
+
- test/path_test.rb
|
168
174
|
- test/pgp_test.rb
|
169
175
|
- test/io_streams_test.rb
|
170
176
|
- test/record_writer_test.rb
|