enumerator_io_reader 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +100 -0
  3. data/lib/enumerator/io/reader.rb +67 -0
  4. metadata +74 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 149f1dbecf68d4f4ed8d55d0297e18355a007374
4
+ data.tar.gz: f376ce3567eb87380eb787e715c8eaaf24fcefbb
5
+ SHA512:
6
+ metadata.gz: 6d348c423efc4e36404b7e6eec298b32e44b223419fa506a8454f8c851d88de1055cfa50e010f21f9b7bb676e769502fb36af2160f1186000e19418e74f7a6a5
7
+ data.tar.gz: d7e7af727e3de28a6c3bc7fe938481a1067772c3397eb89184e560385e683b4d18416b31196b15f8f77d71756980f3e8b3d6376618c00a4ae4e8a5e380c47f3d
@@ -0,0 +1,100 @@
1
+ Enumerator::IO::Reader
2
+ =========================
3
+
4
+ ## Description
5
+
6
+ Ever had a IO object you wanted to enumerate while not reading the whole stream into memory? no problems just use http://ruby-doc.org/core-2.2.2/IO.html#method-i-each_line and specify separator or any of it's siblings `#each_byte`, `#each_char` etc.
7
+
8
+ But what if the situation was the opposite, you had your enumerable, in this case a database response that was to big for your wallet to fit in memory and the interface required a IO object to read from.
9
+
10
+ Like `#put_object` in the legacy aws sdk
11
+ http://www.rubydoc.info/gems/aws-sdk-euca/1.8.5/AWS/S3/Client:put_object
12
+
13
+
14
+ ## Install
15
+
16
+ `gem install enumerator_io_reader`
17
+
18
+ ## Usage
19
+
20
+ A simple case first
21
+
22
+ ```RUBY
23
+ require 'enumerator/io/reader'
24
+ range = "abc".."abz"
25
+ io = Enumerator::IO::Reader.new(range.to_enum)
26
+ io.read(20) # => "abcabdabeabfabgabhab"
27
+ io.read # => "iabjabkablabmabnaboabpabqabrabsabtabuabvabwabxabyabz"
28
+ io.eof? # => true
29
+
30
+ ```
31
+
32
+ `#to_s` is called for every entry in the enumerable but pass a block to the constructor and you can serialize the object in a lazy manner after it has been yielded. The same lazy mapping can of course be accomplished by something like `Enumerator::Lazy`.
33
+
34
+ ```RUBY
35
+ require 'enumerator/io/reader'
36
+ require 'csv'
37
+
38
+ io = Enumerator::IO::Reader.new(Post.each.to_enum) do |post|
39
+ post.attributes.values.to_csv
40
+ end
41
+
42
+ s3.put_object(
43
+ bucket_name: 'foo',
44
+ key: 'key',
45
+ data: io)
46
+ ```
47
+
48
+ If you use this gem alot through out your project you might consider the following monkey patches
49
+
50
+ ```RUBY
51
+ module Enumerable
52
+ def to_io(method = :each)
53
+ Enumerator::IO::Reader.new(enum_for(method))
54
+ end
55
+ end
56
+
57
+ range = "abc".."abz"
58
+ io = range.to_io
59
+ io.read(20) # => "abcabdabeabfabgabhab"
60
+ ```
61
+
62
+ ```RUBY
63
+ class Enumerator
64
+ def to_io
65
+ Enumerator::IO::Reader.new(self)
66
+ end
67
+ end
68
+
69
+ enumerator = Enumerator.new do |y|
70
+ y.yield(1)
71
+ sleep 1
72
+ y.yield(2)
73
+ end
74
+ enumerator.to_io.read # => "12"
75
+ ```
76
+
77
+ ### License
78
+
79
+ (The MIT License)
80
+
81
+ Copyright (c) 2015 Erik Fonselius
82
+
83
+ Permission is hereby granted, free of charge, to any person obtaining
84
+ a copy of this software and associated documentation files (the
85
+ "Software"), to deal in the Software without restriction, including
86
+ without limitation the rights to use, copy, modify, merge, publish,
87
+ distribute, sublicense, and/or sell copies of the Software, and to
88
+ permit persons to whom the Software is furnished to do so, subject to
89
+ the following conditions:
90
+
91
+ The above copyright notice and this permission notice shall be
92
+ included in all copies or substantial portions of the Software.
93
+
94
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
95
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
96
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
97
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
98
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
99
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
100
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,67 @@
1
+ class Enumerator
2
+ module IO
3
+ class Reader
4
+ def initialize(enumerator, &block)
5
+ @enumerator = enumerator
6
+ @reader, @writer = ::IO.pipe
7
+ @end_of_read_window = 0
8
+ @read_bytes = 0
9
+ @block = block
10
+ end
11
+
12
+ def read(*args)
13
+ length = args.first
14
+ if length
15
+ required_read_window = @read_bytes + length
16
+ if required_read_window > @end_of_read_window
17
+ increase_read_window!(required_read_window - @end_of_read_window)
18
+ end
19
+ else
20
+ slurp!
21
+ end
22
+ result = @reader.read(*args)
23
+ @read_bytes += result.size if result
24
+ result
25
+ end
26
+
27
+ def eof?
28
+ moar_data if @read_bytes == 0
29
+ @reader.eof?
30
+ end
31
+
32
+ private
33
+
34
+ def increase_read_window!(bytes_left)
35
+ while bytes_left >= 0 && (written = moar_data) > 0
36
+ bytes_left -= written
37
+ end
38
+ ensure
39
+ @writer.flush unless @writer.closed?
40
+ end
41
+
42
+ def slurp!
43
+ return if @writer.closed?
44
+ while moar_data > 0
45
+ end
46
+ end
47
+
48
+ def moar_data
49
+ entry = @enumerator.next
50
+ chunck = if @block
51
+ @block.call(entry)
52
+ else
53
+ entry.to_s
54
+ end
55
+ @writer.write(chunck)
56
+ @end_of_read_window += chunck.size
57
+ chunck.size
58
+ rescue StopIteration
59
+ unless @writer.closed?
60
+ @writer.flush
61
+ @writer.close
62
+ end
63
+ 0
64
+ end
65
+ end
66
+ end
67
+ end
metadata ADDED
@@ -0,0 +1,74 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: enumerator_io_reader
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Fonsan
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-07-14 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: jeweler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ description: Read a byte stream from your enumerable
42
+ email: fonsan@gmail.com
43
+ executables: []
44
+ extensions: []
45
+ extra_rdoc_files:
46
+ - README.md
47
+ files:
48
+ - README.md
49
+ - lib/enumerator/io/reader.rb
50
+ homepage: http://github.com/Fonsan/enumerator_io_reader
51
+ licenses:
52
+ - MIT
53
+ metadata: {}
54
+ post_install_message:
55
+ rdoc_options: []
56
+ require_paths:
57
+ - lib
58
+ required_ruby_version: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ required_rubygems_version: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ requirements: []
69
+ rubyforge_project:
70
+ rubygems_version: 2.4.6
71
+ signing_key:
72
+ specification_version: 4
73
+ summary: Treat your enumerators as readable IO streams
74
+ test_files: []