iostreams 1.8.0 → 1.10.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b84445b48ac6697a255b5fdacd16783b8573e2cdb75ad395c3b4167ffdd6e01b
4
- data.tar.gz: a37969afc9635fdac60ea45717d64b9bdd8fbb7c6d853a04c0a02cd798274606
3
+ metadata.gz: 26d07bc98819ce065e8c698f00b75a6c2e9fc0a9ea6bf13c1afa8bc8c1e71e66
4
+ data.tar.gz: 2f7898b4197d3f94cbfcc30ba6c8d23e72121877833e57ec9a989e465d019ca2
5
5
  SHA512:
6
- metadata.gz: 685d6a23dfc176f3abe922fab1879c76572b73dc878864af3e38dfa5824e0eb8c115f855537dcf3c3f2d181b3381512a2c9a20c065beb62cfd91642913c84b30
7
- data.tar.gz: db6e6fcb72c07fe502e64f9ab4d18c79334cde6c1622d1b2ac2a22361ed1f38967adc19bb83959e56fc2039ee595f078c1a92d6277a5b8d39d3802e020df0a1a
6
+ metadata.gz: 2887fb5b2935d28bcf2426fd7139a6b7a27c87c1da8d7289cc69f0a644c2d98e4667f6e6da1a57133bcf8bdd93e948d7819d24ce09cb0d65f6c35d59b0b604c7
7
+ data.tar.gz: 034aa07ff80107a139dd86ffd492361887de4f4e8190385b48d12b1bedeb0a3af8c1a25720d41f731330cb46d707736972c707c3ec5576551425d29d3843da68
@@ -79,15 +79,16 @@ module IOStreams
79
79
  # with their options that will be applied when the reader or writer is invoked.
80
80
  def pipeline
81
81
  return streams.dup.freeze if streams
82
- return {}.freeze unless file_name
83
82
 
84
- built_streams = {}
85
- # Encode stream is always first
86
- built_streams[:encode] = options[:encode] if options&.key?(:encode)
83
+ build_pipeline.freeze
84
+ end
87
85
 
88
- opts = options || {}
89
- parse_extensions.each { |stream| built_streams[stream] = opts[stream] || {} }
90
- built_streams.freeze
86
+ # Removes the named stream from the current pipeline.
87
+ # If the stream pipeline has not yet been built it will be built from the file_name if present.
88
+ # Note: Any options must be set _before_ calling this method.
89
+ def remove_from_pipeline(stream_name)
90
+ @streams ||= build_pipeline
91
+ @streams.delete(stream_name.to_sym)
91
92
  end
92
93
 
93
94
  # Returns the tabular format if set, otherwise tries to autodetect the format if the file_name has been set
@@ -106,6 +107,18 @@ module IOStreams
106
107
 
107
108
  private
108
109
 
110
+ def build_pipeline
111
+ return {} unless file_name
112
+
113
+ built_streams = {}
114
+ # Encode stream is always first
115
+ built_streams[:encode] = options[:encode] if options&.key?(:encode)
116
+
117
+ opts = options || {}
118
+ parse_extensions.each { |stream| built_streams[stream] = opts[stream] || {} }
119
+ built_streams
120
+ end
121
+
109
122
  def class_for_stream(type, stream)
110
123
  ext = IOStreams.extensions[stream.nil? ? nil : stream.to_sym] ||
111
124
  raise(ArgumentError, "Unknown Stream type: #{stream.inspect}")
@@ -2,9 +2,9 @@ module IOStreams
2
2
  module Gzip
3
3
  class Reader < IOStreams::Reader
4
4
  # Read from a gzip stream, decompressing the contents as it is read
5
- def self.stream(input_stream, original_file_name: nil, &block)
5
+ def self.stream(input_stream, original_file_name: nil)
6
6
  io = ::Zlib::GzipReader.new(input_stream)
7
- block.call(io)
7
+ yield io
8
8
  ensure
9
9
  io&.close
10
10
  end
@@ -146,8 +146,8 @@ module IOStreams
146
146
  data
147
147
  end
148
148
 
149
- # Returns [Integer] the number of characters read into the internal buffer
150
- # Returns 0 on EOF
149
+ # Returns whether more data is available to read
150
+ # Returns false on EOF
151
151
  def read_block
152
152
  return false if @eof
153
153
 
@@ -157,7 +157,8 @@ module IOStreams
157
157
  @input_stream.read(@buffer_size, @read_cache_buffer)
158
158
  rescue ArgumentError
159
159
  # Handle arity of -1 when just 0..1
160
- @read_cache_buffer = nil
160
+ @read_cache_buffer = nil
161
+ @use_read_cache_buffer = false
161
162
  @input_stream.read(@buffer_size)
162
163
  end
163
164
  else
@@ -89,6 +89,11 @@ module IOStreams
89
89
  # "**.rb" "lib/song.rb" true
90
90
  # "*" "dave/.profile" true
91
91
  def each_child(pattern = "*", case_sensitive: false, directories: false, hidden: false)
92
+ unless block_given?
93
+ return to_enum(__method__, pattern,
94
+ case_sensitive: case_sensitive, directories: directories, hidden: hidden)
95
+ end
96
+
92
97
  flags = 0
93
98
  flags |= ::File::FNM_CASEFOLD unless case_sensitive
94
99
  flags |= ::File::FNM_DOTMATCH if hidden
@@ -284,6 +284,11 @@ module IOStreams
284
284
  # Notes:
285
285
  # - Currently all S3 lookups are recursive as of the pattern regardless of whether the pattern includes `**`.
286
286
  def each_child(pattern = "*", case_sensitive: false, directories: false, hidden: false)
287
+ unless block_given?
288
+ return to_enum(__method__, pattern,
289
+ case_sensitive: case_sensitive, directories: directories, hidden: hidden)
290
+ end
291
+
287
292
  matcher = Matcher.new(self, pattern, case_sensitive: case_sensitive, hidden: hidden)
288
293
 
289
294
  # When the pattern includes an exact file name without any pattern characters
@@ -142,6 +142,11 @@ module IOStreams
142
142
  # sftp://sftp.example.org/a/b/c/test.txt {:type=>1, :size=>37, :owner=>"test_owner", :group=>"test_group",
143
143
  # :permissions=>420, :atime=>1572378136, :mtime=>1572378136, :link_count=>1, :extended=>{}}
144
144
  def each_child(pattern = "*", case_sensitive: true, directories: false, hidden: false)
145
+ unless block_given?
146
+ return to_enum(__method__, pattern,
147
+ case_sensitive: case_sensitive, directories: directories, hidden: hidden)
148
+ end
149
+
145
150
  Utils.load_soft_dependency("net-sftp", "SFTP glob capability", "net/sftp") unless defined?(Net::SFTP)
146
151
 
147
152
  flags = ::File::FNM_EXTGLOB
@@ -89,10 +89,11 @@ module IOStreams
89
89
 
90
90
  IOStreams::Pgp.logger&.debug { "IOStreams::Pgp::Writer.open: #{command}" }
91
91
 
92
+ result = nil
92
93
  Open3.popen2e(command) do |stdin, out, waith_thr|
93
94
  begin
94
95
  stdin.binmode
95
- yield(stdin)
96
+ result = yield(stdin)
96
97
  stdin.close
97
98
  rescue Errno::EPIPE
98
99
  # Ignore broken pipe because gpg terminates early due to an error
@@ -104,6 +105,7 @@ module IOStreams
104
105
  raise(Pgp::Failure, "GPG Failed to create encrypted file: #{file_name}: #{out.read.chomp}")
105
106
  end
106
107
  end
108
+ result
107
109
  end
108
110
  end
109
111
  end
@@ -56,6 +56,14 @@ module IOStreams
56
56
  builder.pipeline
57
57
  end
58
58
 
59
+ # Removes the named stream from the current pipeline.
60
+ # If the stream pipeline has not yet been built it will be built from the file_name if present.
61
+ # Note: Any options must be set _before_ calling this method.
62
+ def remove_from_pipeline(stream_name)
63
+ builder.remove_from_pipeline(stream_name)
64
+ self
65
+ end
66
+
59
67
  # Iterate over a file / stream returning one line at a time.
60
68
  #
61
69
  # Example: Read a line at a time
@@ -148,14 +148,14 @@ module IOStreams
148
148
 
149
149
  def initialize(size:, key: nil, type: :string, decimals: 2)
150
150
  @key = key
151
- @size = size == :remainder ? -1 : size.to_i
151
+ @size = (size == :remainder || size == "remainder") ? -1 : size.to_i
152
152
  @type = type.to_sym
153
153
  @decimals = decimals
154
154
 
155
155
  unless @size.positive? || (@size == -1)
156
156
  raise(Errors::InvalidLayout, "Size #{size.inspect} must be positive or :remainder")
157
157
  end
158
- raise(Errors::InvalidLayout, "Unknown type: #{type.inspect}") unless TYPES.include?(type)
158
+ raise(Errors::InvalidLayout, "Unknown type: #{type.inspect}") unless TYPES.include?(@type)
159
159
  end
160
160
 
161
161
  def parse(value)
@@ -1,3 +1,3 @@
1
1
  module IOStreams
2
- VERSION = "1.8.0".freeze
2
+ VERSION = "1.10.2".freeze
3
3
  end
@@ -4,8 +4,9 @@ module IOStreams
4
4
  # and then pass that filename in for this reader.
5
5
  def self.stream(output_stream, original_file_name: nil, **args, &block)
6
6
  Utils.temp_file_name("iostreams_writer") do |file_name|
7
- file(file_name, original_file_name: original_file_name, **args, &block)
7
+ count = file(file_name, original_file_name: original_file_name, **args, &block)
8
8
  ::File.open(file_name, "rb") { |source| ::IO.copy_stream(source, output_stream) }
9
+ count
9
10
  end
10
11
  end
11
12
 
data/test/builder_test.rb CHANGED
@@ -237,6 +237,21 @@ class BuilderTest < Minitest::Test
237
237
  end
238
238
  end
239
239
 
240
+ describe "#remove_from_pipeline" do
241
+ let(:file_name) { "my/path/abc.bz2.pgp" }
242
+ it "removes a named stream from the pipeline" do
243
+ assert_equal({bz2: {}, pgp: {}}, streams.pipeline)
244
+ streams.remove_from_pipeline(:bz2)
245
+ assert_equal({pgp: {}}, streams.pipeline)
246
+ end
247
+ it "removes a named stream from the pipeline with options" do
248
+ streams.option(:pgp, passphrase: "unlock-me")
249
+ assert_equal({bz2: {}, pgp: {passphrase: "unlock-me"}}, streams.pipeline)
250
+ streams.remove_from_pipeline(:bz2)
251
+ assert_equal({pgp: {passphrase: "unlock-me"}}, streams.pipeline)
252
+ end
253
+ end
254
+
240
255
  describe "#execute" do
241
256
  it "directly calls block for an empty stream" do
242
257
  string_io = StringIO.new
Binary file
@@ -5,7 +5,7 @@ module Paths
5
5
  describe IOStreams::Paths::File do
6
6
  let(:root) { IOStreams::Paths::File.new("/tmp/iostreams").delete_all }
7
7
  let(:directory) { root.join("/some_test_dir") }
8
- let(:data) { "Hello World" }
8
+ let(:data) { "Hello World\nHow are you doing?\nOn this fine day" }
9
9
  let(:file_path) do
10
10
  path = root.join("some_test_dir/test_file.txt")
11
11
  path.writer { |io| io << data }
@@ -17,6 +17,15 @@ module Paths
17
17
  path
18
18
  end
19
19
 
20
+ describe "#each" do
21
+ it "reads lines" do
22
+ records = []
23
+ count = file_path.each { |line| records << line }
24
+ assert_equal count, data.lines.size
25
+ assert_equal data.lines.collect(&:strip), records
26
+ end
27
+ end
28
+
20
29
  describe "#each_child" do
21
30
  it "iterates an empty path" do
22
31
  none = nil
@@ -48,6 +57,12 @@ module Paths
48
57
  actual = root.children("**/Test*.TXT", case_sensitive: true).collect(&:to_s)
49
58
  refute_equal expected, actual.sort
50
59
  end
60
+
61
+ it "with no block returns enumerator" do
62
+ expected = [file_path.to_s, file_path2.to_s]
63
+ actual = root.each_child("**/*").first(100).collect(&:to_s)
64
+ assert_equal expected.sort, actual.sort
65
+ end
51
66
  end
52
67
 
53
68
  describe "#mkpath" do
@@ -10,7 +10,7 @@ class RowReaderTest < Minitest::Test
10
10
  CSV.read(file_name)
11
11
  end
12
12
 
13
- describe ".open" do
13
+ describe "#each" do
14
14
  it "file" do
15
15
  rows = []
16
16
  count = IOStreams::Row::Reader.file(file_name) do |io|
data/test/tabular_test.rb CHANGED
@@ -40,6 +40,19 @@ class TabularTest < Minitest::Test
40
40
  IOStreams::Tabular.new(format: :fixed, format_options: {layout: layout})
41
41
  end
42
42
 
43
+ let :fixed_with_strings do
44
+ layout = [
45
+ {size: "23", key: "name"},
46
+ {size: 40, key: "address"},
47
+ {size: 2},
48
+ {size: 5.0, key: "zip", type: "integer"},
49
+ {size: "8", key: "age", type: "integer"},
50
+ {size: 10, key: "weight", type: "float", decimals: 2},
51
+ {size: "remainder", key: "remainder"}
52
+ ]
53
+ IOStreams::Tabular.new(format: :fixed, format_options: {layout: layout})
54
+ end
55
+
43
56
  describe "#parse_header" do
44
57
  it "parses and sets the csv header" do
45
58
  tabular = IOStreams::Tabular.new(format: :csv)
@@ -269,6 +282,11 @@ class TabularTest < Minitest::Test
269
282
  assert_equal "Jack over there 34618000000210123456.79", string
270
283
  end
271
284
 
285
+ it "renders fixed data with string keys" do
286
+ assert string = fixed_with_strings.render("name" => "Jack", "address" => "over there", "zip" => 34_618, "weight" => 123_456.789123, "age" => 21)
287
+ assert_equal "Jack over there 34618000000210123456.79", string
288
+ end
289
+
272
290
  it "truncates long strings" do
273
291
  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)
274
292
  assert_equal "Jack ran up the beanstaover there 34618000000000000000.00", string
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.8.0
4
+ version: 1.10.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Reid Morrison
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-07-22 00:00:00.000000000 Z
11
+ date: 2021-10-25 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description:
14
14
  email:
@@ -87,6 +87,7 @@ files:
87
87
  - test/files/unclosed_quote_large_test.csv
88
88
  - test/files/unclosed_quote_test.csv
89
89
  - test/files/unclosed_quote_test2.csv
90
+ - test/files/utf16_test.csv
90
91
  - test/gzip_reader_test.rb
91
92
  - test/gzip_writer_test.rb
92
93
  - test/io_streams_test.rb
@@ -159,6 +160,7 @@ test_files:
159
160
  - test/files/unclosed_quote_large_test.csv
160
161
  - test/files/unclosed_quote_test.csv
161
162
  - test/files/unclosed_quote_test2.csv
163
+ - test/files/utf16_test.csv
162
164
  - test/gzip_reader_test.rb
163
165
  - test/gzip_writer_test.rb
164
166
  - test/io_streams_test.rb