iop 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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