iop 0.1.0 → 0.2.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/.yardopts +7 -0
- data/CHANGES.md +7 -0
- data/README.md +6 -6
- data/lib/iop.rb +67 -6
- data/lib/iop/digest.rb +1 -1
- data/lib/iop/file.rb +10 -35
- data/lib/iop/net/ftp.rb +5 -4
- data/lib/iop/net/sftp.rb +176 -0
- data/lib/iop/securerandom.rb +1 -1
- metadata +14 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e00c4ba2a50b1aa72e48cfb31653ee740059b737d9ca6139fc4ba3bf4f1ebbbd
|
4
|
+
data.tar.gz: 643526a878056882bdaf5c840e36cfa2068912850692a53878a70c3e612727a3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 336b3d9df782302ea3d4391683ccddcf662da77b59a324971158b7f1404eaf08547f552394e2a055f842c02176ace16b37f62a37950cbd6fcb4eca8f0fa11b25
|
7
|
+
data.tar.gz: 752c73de778d5ef811a2874ff80b755b8172c78f07f5be7ad5ab4df1a97a6ab94643625b08ea7fcec375144083f7823d540b786efdfb5ed1c7f27e8b95f8bcec
|
data/.yardopts
ADDED
data/CHANGES.md
CHANGED
data/README.md
CHANGED
@@ -13,9 +13,9 @@ Consider the example:
|
|
13
13
|
(FileReader.new('input.dat') | GzipCompressor.new | DigestComputer.new(MD5.new) | FileWriter.new('output.dat.gz')).process!
|
14
14
|
```
|
15
15
|
|
16
|
-
The above snippet reads input file and compresses it into the
|
16
|
+
The above snippet reads input file and compresses it into the Gzip-compatible output file simultaneously computing the MD5 hash of compressed data being written.
|
17
17
|
|
18
|
-
The next snippet presents the incremental pipeline construction capability - a feature not easily implementable with the standard Ruby I/O
|
18
|
+
The next snippet presents the incremental pipeline construction capability - a feature not easily implementable with the standard Ruby I/O block nesting.
|
19
19
|
|
20
20
|
```ruby
|
21
21
|
# Incremental pipeline construction example
|
@@ -25,7 +25,7 @@ pipe |= FileWriter.new('output')
|
|
25
25
|
pipe.process!
|
26
26
|
```
|
27
27
|
|
28
|
-
Here the
|
28
|
+
Here the Gzip compression is made optional and is thrown in depending on external condition.
|
29
29
|
|
30
30
|
## Features
|
31
31
|
|
@@ -35,7 +35,7 @@ The following capabilities are currently implemented:
|
|
35
35
|
- IO or local file reading/writing
|
36
36
|
- FTP file reading/writing
|
37
37
|
- Digest computing
|
38
|
-
-
|
38
|
+
- Gzip/Zlib (de)compression
|
39
39
|
- Zstd (de)compression
|
40
40
|
- Symmetric cipher (de,en)cryption
|
41
41
|
- Random data generation
|
@@ -61,7 +61,7 @@ include IOP
|
|
61
61
|
|
62
62
|
```ruby
|
63
63
|
pipe = StringSplitter.new('Greetings from IOP', 10)
|
64
|
-
pipe |= GzipCompressor.new | (
|
64
|
+
pipe |= GzipCompressor.new | (d = DigestComputer.new(MD5.new))
|
65
65
|
pipe |= FileWriter.new('output.gz')
|
66
66
|
```
|
67
67
|
|
@@ -78,7 +78,7 @@ The IOP instances do normally perform self-cleanup operations, such as closing f
|
|
78
78
|
- The variable-bound instances can be then examined.
|
79
79
|
|
80
80
|
```ruby
|
81
|
-
puts digest.hexdigest
|
81
|
+
puts d.digest.hexdigest
|
82
82
|
```
|
83
83
|
|
84
84
|
For further information refer to IOP documentation.
|
data/lib/iop.rb
CHANGED
@@ -74,7 +74,7 @@
|
|
74
74
|
module IOP
|
75
75
|
|
76
76
|
|
77
|
-
VERSION = '0.
|
77
|
+
VERSION = '0.2.0'
|
78
78
|
|
79
79
|
|
80
80
|
# Default read block size in bytes for adapters which don't have this parameter externally imposed.
|
@@ -191,9 +191,71 @@ module IOP
|
|
191
191
|
end
|
192
192
|
|
193
193
|
|
194
|
+
#
|
195
|
+
# Abstract reader class for seekable streams which can read with blocks of specified size.
|
196
|
+
#
|
197
|
+
# Sequentially reads specified number of bytes starting at specified byte offset.
|
198
|
+
#
|
199
|
+
# @since 0.2
|
200
|
+
#
|
201
|
+
class RandomAccessReader
|
202
|
+
|
203
|
+
include Feed
|
204
|
+
|
205
|
+
# Sets up the reader parameters.
|
206
|
+
#
|
207
|
+
# @param size [Integer] total number of bytes to read; +nil+ value instructs to read until end-of-data is reached
|
208
|
+
#
|
209
|
+
# @param offset [Integer] offset in bytes from the stream start to seek to; +nil+ value means no seeking is performed
|
210
|
+
#
|
211
|
+
# @param block_size [Integer] size of blocks to read data with
|
212
|
+
def initialize(size: nil, offset: nil, block_size: DEFAULT_BLOCK_SIZE)
|
213
|
+
@block_size = size.nil? ? block_size : IOP.min(size, block_size)
|
214
|
+
@left = @size = size
|
215
|
+
@offset = offset
|
216
|
+
end
|
217
|
+
|
218
|
+
def process!
|
219
|
+
seek! unless @offset.nil?
|
220
|
+
buffer = IOP.allocate_string(@block_size)
|
221
|
+
loop do
|
222
|
+
read_size = @size.nil? ? @block_size : IOP.min(@left, @block_size)
|
223
|
+
break if read_size.zero?
|
224
|
+
if (data = read!(read_size, buffer)).nil?
|
225
|
+
if @size.nil?
|
226
|
+
break
|
227
|
+
else
|
228
|
+
raise EOFError, INSUFFICIENT_DATA
|
229
|
+
end
|
230
|
+
else
|
231
|
+
unless @left.nil?
|
232
|
+
@left -= data.size
|
233
|
+
raise IOError, EXTRA_DATA if @left < 0
|
234
|
+
end
|
235
|
+
end
|
236
|
+
process(data) unless data.size.zero?
|
237
|
+
end
|
238
|
+
process
|
239
|
+
end
|
240
|
+
|
241
|
+
protected
|
242
|
+
|
243
|
+
# @abstract
|
244
|
+
def seek!() end
|
245
|
+
remove_method :seek!
|
246
|
+
|
247
|
+
# @abstract
|
248
|
+
def read!(read_size, buffer) nil end
|
249
|
+
remove_method :read!
|
250
|
+
|
251
|
+
end
|
252
|
+
|
253
|
+
|
194
254
|
#
|
195
255
|
# @private
|
196
256
|
#
|
257
|
+
# Feed implementation for sequential streams which can not seek or request for the block size to read.
|
258
|
+
#
|
197
259
|
# @note a class including this module must implement the {#next_data} method.
|
198
260
|
#
|
199
261
|
# @since 0.1
|
@@ -234,14 +296,13 @@ module IOP
|
|
234
296
|
process
|
235
297
|
end
|
236
298
|
|
237
|
-
|
238
|
-
|
299
|
+
protected
|
300
|
+
|
239
301
|
# Returns the data portion of non-zero size or +nil+ on EOF.
|
240
302
|
#
|
303
|
+
# @abstract
|
241
304
|
# @return [String] data chunk recently read or +nil+
|
242
|
-
def next_data
|
243
|
-
raise
|
244
|
-
end
|
305
|
+
def next_data; nil end
|
245
306
|
remove_method :next_data
|
246
307
|
|
247
308
|
end
|
data/lib/iop/digest.rb
CHANGED
@@ -7,7 +7,7 @@ module IOP
|
|
7
7
|
|
8
8
|
#
|
9
9
|
# Filter class to compute digest of the data being passed through.
|
10
|
-
# It can be used with digest computing classes from +
|
10
|
+
# It can be used with digest computing classes from the standard Ruby +Digest::+ and +OpenSSL::Digest::+ modules.
|
11
11
|
#
|
12
12
|
# ### Use case: generate 1024 bytes of random data and compute and print MD5 hash sum of it.
|
13
13
|
#
|
data/lib/iop/file.rb
CHANGED
@@ -26,48 +26,23 @@ module IOP
|
|
26
26
|
#
|
27
27
|
# @since 0.1
|
28
28
|
#
|
29
|
-
class IOReader
|
30
|
-
|
31
|
-
include Feed
|
29
|
+
class IOReader < RandomAccessReader
|
32
30
|
|
33
31
|
# Creates class instance.
|
34
32
|
#
|
35
33
|
# @param io [IO] +IO+ instance to read data from
|
36
34
|
#
|
37
|
-
# @param
|
38
|
-
|
39
|
-
|
40
|
-
#
|
41
|
-
# @param block_size [Integer] size of blocks to read data with
|
42
|
-
def initialize(io, size: nil, offset: nil, block_size: DEFAULT_BLOCK_SIZE)
|
43
|
-
@block_size = size.nil? ? block_size : IOP.min(size, block_size)
|
44
|
-
@left = @size = size
|
45
|
-
@offset = offset
|
35
|
+
# @param options [Hash] keyword arguments passed to {RandomAccessReader} constructor
|
36
|
+
def initialize(io, **options)
|
37
|
+
super(**options)
|
46
38
|
@io = io
|
47
39
|
end
|
48
40
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
break if read_size.zero?
|
55
|
-
if @io.read(read_size, data).nil?
|
56
|
-
if @size.nil?
|
57
|
-
break
|
58
|
-
else
|
59
|
-
raise EOFError, INSUFFICIENT_DATA
|
60
|
-
end
|
61
|
-
else
|
62
|
-
unless @left.nil?
|
63
|
-
@left -= data.size
|
64
|
-
raise IOError, EXTRA_DATA if @left < 0
|
65
|
-
end
|
66
|
-
end
|
67
|
-
process(data) unless data.size.zero?
|
68
|
-
end
|
69
|
-
process
|
70
|
-
end
|
41
|
+
private
|
42
|
+
|
43
|
+
def seek!() @io.seek(@offset) end
|
44
|
+
|
45
|
+
def read!(read_size, data) @io.read(read_size, data) end
|
71
46
|
|
72
47
|
end
|
73
48
|
|
@@ -94,7 +69,7 @@ module IOP
|
|
94
69
|
#
|
95
70
|
# @param mode [String] open mode for the file; refer to {File} for details
|
96
71
|
#
|
97
|
-
# @param options [Hash]
|
72
|
+
# @param options [Hash] keyword parameters passed to {IOReader} constructor
|
98
73
|
def initialize(file, mode: 'rb', **options)
|
99
74
|
super(nil, **options)
|
100
75
|
@file = file
|
data/lib/iop/net/ftp.rb
CHANGED
@@ -12,7 +12,7 @@ module IOP
|
|
12
12
|
|
13
13
|
|
14
14
|
# @private
|
15
|
-
module
|
15
|
+
module FTPCommons
|
16
16
|
|
17
17
|
private
|
18
18
|
|
@@ -45,6 +45,7 @@ module IOP
|
|
45
45
|
|
46
46
|
end
|
47
47
|
|
48
|
+
|
48
49
|
#
|
49
50
|
# Feed class to read file from FTP server.
|
50
51
|
#
|
@@ -52,6 +53,7 @@ module IOP
|
|
52
53
|
#
|
53
54
|
# ### Use case: retrieve file from FTP server and store it locally.
|
54
55
|
#
|
56
|
+
# require 'iop/file'
|
55
57
|
# require 'iop/net/ftp'
|
56
58
|
# ( IOP::FTPFileReader.new('ftp.gnu.org', '/pub/README') | IOP::FileWriter.new('README') ).process!
|
57
59
|
#
|
@@ -60,7 +62,7 @@ module IOP
|
|
60
62
|
class FTPFileReader
|
61
63
|
|
62
64
|
include Feed
|
63
|
-
include
|
65
|
+
include FTPCommons
|
64
66
|
|
65
67
|
# Creates class instance.
|
66
68
|
#
|
@@ -133,7 +135,6 @@ module IOP
|
|
133
135
|
end
|
134
136
|
|
135
137
|
|
136
|
-
|
137
138
|
#
|
138
139
|
# Sink class to write file to FTP server.
|
139
140
|
#
|
@@ -158,7 +159,7 @@ module IOP
|
|
158
159
|
class FTPFileWriter
|
159
160
|
|
160
161
|
include Sink
|
161
|
-
include
|
162
|
+
include FTPCommons
|
162
163
|
|
163
164
|
# Creates class instance.
|
164
165
|
#
|
data/lib/iop/net/sftp.rb
ADDED
@@ -0,0 +1,176 @@
|
|
1
|
+
require 'iop'
|
2
|
+
require 'net/sftp'
|
3
|
+
|
4
|
+
|
5
|
+
module IOP
|
6
|
+
|
7
|
+
|
8
|
+
# @private
|
9
|
+
module SFTPCommons
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def setup
|
14
|
+
if @sftp.is_a?(String)
|
15
|
+
@sftp = Net::SFTP.start(@sftp, @options.delete(:username), **@options)
|
16
|
+
@managed = true
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def cleanup
|
21
|
+
if @managed
|
22
|
+
ssh = @sftp.session
|
23
|
+
@sftp.close_channel
|
24
|
+
ssh.close
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
#
|
32
|
+
# Feed class to read file from SFTP server.
|
33
|
+
#
|
34
|
+
# This class an adapter for +Net::SFTP::Session+ class.
|
35
|
+
#
|
36
|
+
# ### Use case: retrieve current user's _~/.profile_ file from SFTP server running on local machine and and compute its MD5 hash sum.
|
37
|
+
#
|
38
|
+
# require 'iop/digest'
|
39
|
+
# require 'iop/net/sftp'
|
40
|
+
# ( IOP::SFTPFileReader.new('localhost', '.profile') | (d = IOP::DigestComputer.new(Digest::MD5.new)) ).process!
|
41
|
+
# puts d.digest.hexdigest
|
42
|
+
#
|
43
|
+
# @note this class depends on external +net-sftp+ gem.
|
44
|
+
# @since 0.2
|
45
|
+
#
|
46
|
+
class SFTPFileReader < RandomAccessReader
|
47
|
+
|
48
|
+
include Feed
|
49
|
+
include SFTPCommons
|
50
|
+
|
51
|
+
# Creates class instance.
|
52
|
+
#
|
53
|
+
# @param sftp [String, Net::SFTP::Session] SFTP server to connect to
|
54
|
+
#
|
55
|
+
# @param file [String] file name to process
|
56
|
+
#
|
57
|
+
# @param size [Integer] total number of bytes to read; +nil+ value instructs to read until end-of-data is reached
|
58
|
+
#
|
59
|
+
# @param offset [Integer] offset in bytes from the stream start to seek to; +nil+ value means no seeking is performed
|
60
|
+
#
|
61
|
+
# @param block_size [Integer] size of blocks to process data with
|
62
|
+
#
|
63
|
+
# @param options [Hash] extra keyword parameters passed to +Net::SFTP::Session+ constructor, such as username, password etc.
|
64
|
+
#
|
65
|
+
# _sftp_ can be either a +String+ of +Net::SFTP::Session+ instance.
|
66
|
+
# If it is a string a corresponding +Net::SFTP::Session+ instance will be created with _options_ passed to its constructor.
|
67
|
+
#
|
68
|
+
# If _sftp_ is a string, a created SFTP session is managed, e.g. it is closed after the process is complete,
|
69
|
+
# otherwise supplied object is left as is and no closing is performed.
|
70
|
+
# This allows to reuse SFTP session for a sequence of operations.
|
71
|
+
#
|
72
|
+
# Refer to +Net::SFTP+ documentation for available options.
|
73
|
+
def initialize(sftp, file, size: nil, offset: nil, block_size: DEFAULT_BLOCK_SIZE, **options)
|
74
|
+
super(size: size, offset: offset, block_size: block_size)
|
75
|
+
@options = options
|
76
|
+
@file = file
|
77
|
+
@sftp = sftp
|
78
|
+
end
|
79
|
+
|
80
|
+
def process!
|
81
|
+
setup
|
82
|
+
begin
|
83
|
+
@io = @sftp.open!(@file, 'r')
|
84
|
+
begin
|
85
|
+
super
|
86
|
+
ensure
|
87
|
+
@sftp.close(@io)
|
88
|
+
end
|
89
|
+
ensure
|
90
|
+
cleanup
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
private
|
95
|
+
|
96
|
+
def read!(read_size, buffer)
|
97
|
+
data = @sftp.read!(@io, @offset ||= 0, read_size)
|
98
|
+
@offset += data.size unless data.nil?
|
99
|
+
data
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
|
104
|
+
|
105
|
+
#
|
106
|
+
# Sink class to write file to SFTP server.
|
107
|
+
#
|
108
|
+
# This class an adapter for +Net::SFTP::Session+ class.
|
109
|
+
#
|
110
|
+
# ### Use case: store a number of files filled with random data to remote SFTP server reusing session.
|
111
|
+
#
|
112
|
+
# require 'iop/net/sftp'
|
113
|
+
# require 'iop/securerandom'
|
114
|
+
# sftp = Net::SFTP.start('sftp.server', username: 'user', password: '123')
|
115
|
+
# begin
|
116
|
+
# (1..3).each do |i|
|
117
|
+
# ( IOP::SecureRandomGenerator.new(1024) | IOP::SFTPFileWriter.new(sftp, "random#{i}.dat") ).process!
|
118
|
+
# end
|
119
|
+
# ensure
|
120
|
+
# sftp.session.close
|
121
|
+
# end
|
122
|
+
#
|
123
|
+
# @note this class depends on external +net-sftp+ gem.
|
124
|
+
# @since 0.2
|
125
|
+
#
|
126
|
+
class SFTPFileWriter
|
127
|
+
|
128
|
+
include Sink
|
129
|
+
include SFTPCommons
|
130
|
+
|
131
|
+
# Creates class instance.
|
132
|
+
#
|
133
|
+
# @param sftp [String, Net::SFTP::Session] SFTP server to connect to
|
134
|
+
#
|
135
|
+
# @param file [String] file name to process
|
136
|
+
#
|
137
|
+
# @param options [Hash] extra keyword parameters passed to +Net::SFTP::Session+ constructor
|
138
|
+
#
|
139
|
+
# _sftp_ can be either a +String+ of +Net::SFTP::Session+ instance.
|
140
|
+
# If it is a string a corresponding +Net::SFTP::Session+ instance will be created with _options_ passed to its constructor.
|
141
|
+
#
|
142
|
+
# If _sftp_ is a string, a created SFTP session is managed, e.g. it is closed after the process is complete,
|
143
|
+
# otherwise supplied object is left as is and no closing is performed.
|
144
|
+
# This allows to reuse SFTP session for a sequence of operations.
|
145
|
+
def initialize(sftp, file, **options)
|
146
|
+
@options = options
|
147
|
+
@file = file
|
148
|
+
@sftp = sftp
|
149
|
+
end
|
150
|
+
|
151
|
+
def process!
|
152
|
+
setup
|
153
|
+
begin
|
154
|
+
@io = @sftp.open!(@file, 'w')
|
155
|
+
@offset = 0
|
156
|
+
begin
|
157
|
+
super
|
158
|
+
ensure
|
159
|
+
@sftp.close(@io)
|
160
|
+
end
|
161
|
+
ensure
|
162
|
+
cleanup
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
def process(data = nil)
|
167
|
+
unless data.nil?
|
168
|
+
@sftp.write!(@io, @offset, data)
|
169
|
+
@offset += data.size
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
end
|
174
|
+
|
175
|
+
|
176
|
+
end
|
data/lib/iop/securerandom.rb
CHANGED
@@ -8,7 +8,7 @@ module IOP
|
|
8
8
|
#
|
9
9
|
# Feed class to generate and send a random sequence of bytes of specified size.
|
10
10
|
#
|
11
|
-
# This is the adapter for standard
|
11
|
+
# This is the adapter for the standard Ruby +SecureRandom+ module.
|
12
12
|
#
|
13
13
|
# ### Use case: generate 1024 bytes of random data and compute MD5 hash sum of it.
|
14
14
|
#
|
metadata
CHANGED
@@ -1,38 +1,41 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: iop
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Oleg A. Khlybov
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-04-
|
11
|
+
date: 2019-04-22 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: |2
|
14
|
-
|
14
|
+
I/O pipeline construction framework.
|
15
|
+
Allows to construct data processing pipelines in a manner of UNIX shell pipes.
|
15
16
|
Implemented features:
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
17
|
+
string splitting/merging,
|
18
|
+
IO or local file reading/writing,
|
19
|
+
FTP/SFTP file reading/writing,
|
20
|
+
digest computing,
|
21
|
+
Gzip/Zlib (de)compression,
|
22
|
+
Zstd (de)compression,
|
23
|
+
symmetric cipher (de,en)cryption,
|
24
|
+
random data generation.
|
24
25
|
email:
|
25
26
|
- fougas@mail.ru
|
26
27
|
executables: []
|
27
28
|
extensions: []
|
28
29
|
extra_rdoc_files: []
|
29
30
|
files:
|
31
|
+
- ".yardopts"
|
30
32
|
- CHANGES.md
|
31
33
|
- README.md
|
32
34
|
- lib/iop.rb
|
33
35
|
- lib/iop/digest.rb
|
34
36
|
- lib/iop/file.rb
|
35
37
|
- lib/iop/net/ftp.rb
|
38
|
+
- lib/iop/net/sftp.rb
|
36
39
|
- lib/iop/openssl.rb
|
37
40
|
- lib/iop/securerandom.rb
|
38
41
|
- lib/iop/string.rb
|