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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2631f9835b20e87513aed68b469ca251815ccd9517c1b6879555ab851bdae02a
4
- data.tar.gz: b1a7d1794c962323692117181c1d5485343ed38d3fec65738af5c21076801fd4
3
+ metadata.gz: e00c4ba2a50b1aa72e48cfb31653ee740059b737d9ca6139fc4ba3bf4f1ebbbd
4
+ data.tar.gz: 643526a878056882bdaf5c840e36cfa2068912850692a53878a70c3e612727a3
5
5
  SHA512:
6
- metadata.gz: d1cbb150db80d8de4aae4caa144618c143c84572949488508ee149b7fdcb8040385c966022a242fea08caf7533a96035fa0ee7e466cc8a583faec70b3e3c48b4
7
- data.tar.gz: 42f468cd335d5087d4471b0f17fd190d0b7346611fe6f00da8a3a565c85042ce8a41f0a351cbe8bd40ef316311eaeae5456ec8fa79af71db3461621313435f6c
6
+ metadata.gz: 336b3d9df782302ea3d4391683ccddcf662da77b59a324971158b7f1404eaf08547f552394e2a055f842c02176ace16b37f62a37950cbd6fcb4eca8f0fa11b25
7
+ data.tar.gz: 752c73de778d5ef811a2874ff80b755b8172c78f07f5be7ad5ab4df1a97a6ab94643625b08ea7fcec375144083f7823d540b786efdfb5ed1c7f27e8b95f8bcec
@@ -0,0 +1,7 @@
1
+ --no-private
2
+ --protected
3
+ --readme README.md
4
+ -m markdown
5
+ -
6
+ lib/**/*.rb
7
+ *.md
data/CHANGES.md CHANGED
@@ -1,3 +1,10 @@
1
+ # 0.2.0
2
+
3
+ SFTP file reading/writing.
4
+
5
+ Documentation updates.
6
+
7
+
1
8
  # 0.1.0
2
9
 
3
10
  Initial release.
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 GZip-compatible output file simultaneously computing the MD5 hash of compressed data being written.
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 blocks nesting.
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 GZip compression is made optional and is thrown in depending on external condition.
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
- - GZip/Zlib (de)compression
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 | (digest = DigestComputer.new(MD5.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.1.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
- # @abstract
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
@@ -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 +digest+ and +openssl+ standard Ruby modules.
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
  #
@@ -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 size [Integer] total number of bytes to read; +nil+ value instructs to read until end-of-data is reached
38
- #
39
- # @param offset [Integer] offset in bytes from the stream start to seek to; +nil+ value means no seeking is performed
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
- def process!
50
- @io.seek(@offset) unless @offset.nil?
51
- data = IOP.allocate_string(@block_size)
52
- loop do
53
- read_size = @size.nil? ? @block_size : IOP.min(@left, @block_size)
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] extra keyword parameters passed to {IOReader} constructor
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
@@ -12,7 +12,7 @@ module IOP
12
12
 
13
13
 
14
14
  # @private
15
- module FTPFile
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 FTPFile
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 FTPFile
162
+ include FTPCommons
162
163
 
163
164
  # Creates class instance.
164
165
  #
@@ -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
@@ -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 {SecureRandom} generator module.
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.1.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-18 00:00:00.000000000 Z
11
+ date: 2019-04-22 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: |2
14
- Allows to construct data and file processing pipelines in a manner of UNIX shell pipes.
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
- - String splitting/merging
17
- - IO or local file reading/writing
18
- - FTP file reading/writing
19
- - Digest computing
20
- - GZip/Zlib (de)compression
21
- - Zstd (de)compression
22
- - Symmetric cipher (de,en)cryption
23
- - Random data generation
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