s3io 0.0.2 → 1.0.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.
- data/README.md +16 -2
- data/lib/s3io.rb +5 -3
- data/lib/s3io/read_wrapper.rb +86 -0
- data/lib/s3io/version.rb +1 -1
- data/lib/s3io/wrapper.rb +26 -70
- data/lib/s3io/write_wrapper.rb +64 -0
- data/test/{test_s3io_wrapper.rb → test_s3io_read_wrapper.rb} +46 -29
- data/test/test_s3io_write_wrapper.rb +91 -0
- metadata +56 -41
- data/test/test_s3io.rb +0 -16
data/README.md
CHANGED
@@ -25,12 +25,13 @@ Or install it yourself as:
|
|
25
25
|
## Usage
|
26
26
|
|
27
27
|
Once wrapped, S3 objects behave the way you'd expect from an ordinary IO object.
|
28
|
+
It can read:
|
28
29
|
|
29
30
|
require 'aws-sdk'
|
30
31
|
require 's3io'
|
31
32
|
|
32
33
|
s3_object = S3.buckets['some-bucket'].objects['path/to/object']
|
33
|
-
io = S3io.
|
34
|
+
io = S3io.open(s3_object, 'r')
|
34
35
|
|
35
36
|
first_100_bytes = io.read(100) # reading first 100 bytes
|
36
37
|
|
@@ -44,9 +45,22 @@ Once wrapped, S3 objects behave the way you'd expect from an ordinary IO object.
|
|
44
45
|
|
45
46
|
puts io.read # and print everything from that byte to the end
|
46
47
|
|
48
|
+
|
49
|
+
It can write:
|
50
|
+
|
51
|
+
require 'aws-sdk'
|
52
|
+
require 's3io'
|
53
|
+
|
54
|
+
s3_object = S3.buckets['some-bucket'].objects['path/to/object']
|
55
|
+
|
56
|
+
S3io.open(s3_object, 'w') do |s3io|
|
57
|
+
io.write 'abc'
|
58
|
+
io.write 'def'
|
59
|
+
end
|
60
|
+
|
47
61
|
## To do
|
48
62
|
|
49
|
-
*
|
63
|
+
* Code documentation
|
50
64
|
|
51
65
|
## Contributing
|
52
66
|
|
data/lib/s3io.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
require "s3io/version"
|
2
2
|
require "s3io/wrapper"
|
3
|
+
require "s3io/read_wrapper"
|
4
|
+
require "s3io/write_wrapper"
|
3
5
|
|
4
6
|
require "aws-sdk"
|
5
7
|
|
@@ -11,7 +13,7 @@ module S3io
|
|
11
13
|
# @param [Hash] options options hash
|
12
14
|
# @option options [Integer] :line_buffer_size size of the buffer that is used for reading contents of S3 object when iterating over its lines
|
13
15
|
# @return [S3io::Wrapper] a wrapped S3 object
|
14
|
-
def self.new(s3object, options = {})
|
15
|
-
|
16
|
-
end
|
16
|
+
#def self.new(s3object, options = {})
|
17
|
+
# Wrapper.new(s3object, options)
|
18
|
+
#end
|
17
19
|
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
module S3io
|
2
|
+
|
3
|
+
def self.reader(s3object, options = {}, &block)
|
4
|
+
open(s3object, 'r', options, &block)
|
5
|
+
end
|
6
|
+
|
7
|
+
class ReadWrapper < Wrapper
|
8
|
+
|
9
|
+
# Default buffer size for line parser in bytes
|
10
|
+
LINE_BUFFER_SIZE = 5 * 1024 * 1024 # MiB
|
11
|
+
|
12
|
+
include Enumerable
|
13
|
+
|
14
|
+
# Current byte position in S3 object
|
15
|
+
attr_accessor :pos
|
16
|
+
|
17
|
+
def initialize(s3object, options = {})
|
18
|
+
super(s3object)
|
19
|
+
|
20
|
+
@options = {
|
21
|
+
:line_buffer_size => (options[:line_buffer_size] || LINE_BUFFER_SIZE)
|
22
|
+
}
|
23
|
+
end
|
24
|
+
|
25
|
+
# Reads data from S3 object.
|
26
|
+
#
|
27
|
+
# @param [Integer] bytes number of bytes to read
|
28
|
+
def read(bytes = nil)
|
29
|
+
content_length = @s3object.content_length
|
30
|
+
|
31
|
+
return '' if (@pos >= content_length) || (bytes == 0)
|
32
|
+
|
33
|
+
bytes ||= content_length
|
34
|
+
|
35
|
+
upper_bound = @pos + bytes - 1
|
36
|
+
upper_bound = (content_length - 1) if upper_bound >= content_length
|
37
|
+
|
38
|
+
data = @s3object.read :range => @pos..upper_bound
|
39
|
+
@pos = upper_bound + 1
|
40
|
+
|
41
|
+
return data
|
42
|
+
end
|
43
|
+
|
44
|
+
def eof?
|
45
|
+
@pos >= @s3object.content_length
|
46
|
+
end
|
47
|
+
|
48
|
+
# Rewinds position to the very beginning of S3 object.
|
49
|
+
def rewind
|
50
|
+
@pos = 0
|
51
|
+
end
|
52
|
+
|
53
|
+
# Iterates over S3 object lines.
|
54
|
+
#
|
55
|
+
# @param [String] separator line separator string
|
56
|
+
def each(separator = $/)
|
57
|
+
return enum_for(:each, separator) unless block_given?
|
58
|
+
|
59
|
+
line = ''
|
60
|
+
newline_pos = nil
|
61
|
+
|
62
|
+
# Either trying to parse the remainder or reading some more data
|
63
|
+
while newline_pos || !(buffer = read(@options[:line_buffer_size])).empty?
|
64
|
+
prev_newline_pos = newline_pos || 0
|
65
|
+
newline_pos = buffer.index(separator, prev_newline_pos)
|
66
|
+
|
67
|
+
if newline_pos
|
68
|
+
line << buffer[prev_newline_pos..newline_pos]
|
69
|
+
newline_pos += 1
|
70
|
+
yield line
|
71
|
+
line = ''
|
72
|
+
else
|
73
|
+
line << buffer[prev_newline_pos..-1]
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# Flush the remainder if body doesn't end with separator
|
78
|
+
yield line unless line.empty?
|
79
|
+
|
80
|
+
return self
|
81
|
+
end
|
82
|
+
alias lines each
|
83
|
+
alias each_line each
|
84
|
+
|
85
|
+
end
|
86
|
+
end
|
data/lib/s3io/version.rb
CHANGED
data/lib/s3io/wrapper.rb
CHANGED
@@ -1,88 +1,44 @@
|
|
1
1
|
module S3io
|
2
|
-
# This class wraps an AWS S3 object in order to provide IO-like API.
|
3
|
-
# S3 objects wrapped this way can be used in methods that would otherwise expect an instance of File, StringIO etc.
|
4
|
-
class Wrapper
|
5
2
|
|
6
|
-
|
7
|
-
|
3
|
+
def self.open(s3object, mode_string = 'r', options = {}, &block)
|
4
|
+
wrapper_class = case mode_string
|
5
|
+
when 'r'
|
6
|
+
ReadWrapper
|
7
|
+
when 'w'
|
8
|
+
WriteWrapper
|
9
|
+
else
|
10
|
+
fail "S3IO only supports 'r' or 'w' as access modes"
|
11
|
+
end
|
8
12
|
|
9
|
-
|
13
|
+
wrapper = wrapper_class.new(s3object, options)
|
10
14
|
|
11
|
-
|
12
|
-
|
15
|
+
if block_given?
|
16
|
+
result = yield wrapper if block_given?
|
17
|
+
wrapper.close
|
18
|
+
|
19
|
+
return result
|
20
|
+
else
|
21
|
+
return wrapper
|
22
|
+
end
|
23
|
+
end
|
13
24
|
|
14
|
-
|
15
|
-
|
25
|
+
# This class wraps an AWS S3 object in order to provide IO-like API.
|
26
|
+
# S3 objects wrapped this way can be used in methods that would otherwise expect an instance of File, StringIO etc.
|
27
|
+
class Wrapper
|
16
28
|
|
17
29
|
# Wraps an AWS::S3::S3Object into IO-like object.
|
18
30
|
#
|
19
31
|
# @param [AWS::S3::S3Object] s3object an object to wrap
|
20
|
-
|
21
|
-
# @option options [Integer] :line_buffer_size size of the buffer that is used for reading contents of S3 object when iterating over its lines
|
22
|
-
def initialize(s3object, options = {})
|
32
|
+
def initialize(s3object)
|
23
33
|
@s3object = s3object
|
24
|
-
@options = {
|
25
|
-
:line_buffer_size => (options[:line_buffer_size] || LINE_BUFFER_SIZE)
|
26
|
-
}
|
27
|
-
|
28
34
|
@pos = 0
|
29
35
|
end
|
30
36
|
|
31
|
-
|
32
|
-
|
33
|
-
# @param [Integer] bytes number of bytes to read
|
34
|
-
def read(bytes = nil)
|
35
|
-
content_length = @s3object.content_length
|
36
|
-
|
37
|
-
return '' if (@pos >= content_length) || (bytes == 0)
|
38
|
-
|
39
|
-
bytes ||= content_length
|
40
|
-
|
41
|
-
upper_bound = @pos + bytes - 1
|
42
|
-
upper_bound = (content_length - 1) if upper_bound >= content_length
|
43
|
-
|
44
|
-
data = @s3object.read :range => @pos..upper_bound
|
45
|
-
@pos = upper_bound + 1
|
46
|
-
|
47
|
-
return data
|
48
|
-
end
|
49
|
-
|
50
|
-
# Rewinds position to the very beginning of S3 object.
|
51
|
-
def rewind
|
37
|
+
def close
|
38
|
+
@s3object = nil
|
52
39
|
@pos = 0
|
53
|
-
end
|
54
40
|
|
55
|
-
|
56
|
-
#
|
57
|
-
# @param [String] separator line separator string
|
58
|
-
def each(separator = $/)
|
59
|
-
return enum_for(:each, separator) unless block_given?
|
60
|
-
|
61
|
-
line = ''
|
62
|
-
newline_pos = nil
|
63
|
-
|
64
|
-
# Either trying to parse the remainder or reading some more data
|
65
|
-
while newline_pos || !(buffer = read(@options[:line_buffer_size])).empty?
|
66
|
-
prev_newline_pos = newline_pos || 0
|
67
|
-
newline_pos = buffer.index(separator, prev_newline_pos)
|
68
|
-
|
69
|
-
if newline_pos
|
70
|
-
line << buffer[prev_newline_pos..newline_pos]
|
71
|
-
newline_pos += 1
|
72
|
-
yield line
|
73
|
-
line = ''
|
74
|
-
else
|
75
|
-
line << buffer[prev_newline_pos..-1]
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
# Flush the remainder if body doesn't end with separator
|
80
|
-
yield line unless line.empty?
|
81
|
-
|
82
|
-
return self
|
41
|
+
return nil
|
83
42
|
end
|
84
|
-
alias lines each
|
85
|
-
alias each_line each
|
86
|
-
|
87
43
|
end
|
88
44
|
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module S3io
|
2
|
+
|
3
|
+
def self.writer(s3object, options = {}, &block)
|
4
|
+
open(s3object, 'w', options, &block)
|
5
|
+
end
|
6
|
+
|
7
|
+
class WriteWrapper < Wrapper
|
8
|
+
|
9
|
+
# Default maximum file size
|
10
|
+
MAX_FILE_SIZE = 1 * 1024 * 1024 * 1024 * 1024 # TiB
|
11
|
+
|
12
|
+
# S3 only supports up to 10K multipart chunks
|
13
|
+
MAX_NUM_CHUNKS = 10000
|
14
|
+
|
15
|
+
# Minimum chunk size in S3 is 5MiB
|
16
|
+
MIN_CHUNK_SIZE = 5 * 1024 * 1024 # MiB
|
17
|
+
|
18
|
+
# Number of bytes written to S3
|
19
|
+
attr_reader :pos
|
20
|
+
|
21
|
+
def initialize(s3object, options = {})
|
22
|
+
super(s3object)
|
23
|
+
|
24
|
+
@options = {
|
25
|
+
:max_file_size => (options[:max_file_size] || MAX_FILE_SIZE),
|
26
|
+
:multipart_upload_options => (options[:multipart_upload_options] || {})
|
27
|
+
}
|
28
|
+
|
29
|
+
@min_chunk_size = [(@options[:max_file_size].to_f / MAX_NUM_CHUNKS).ceil, MIN_CHUNK_SIZE].max
|
30
|
+
@multipart_upload = @s3object.multipart_upload(@options[:multipart_upload_options])
|
31
|
+
@write_buffer = ''
|
32
|
+
end
|
33
|
+
|
34
|
+
def write(data)
|
35
|
+
fail "S3 Object is already closed" unless @s3object
|
36
|
+
|
37
|
+
data_str = data.to_s
|
38
|
+
data_size = data_str.size
|
39
|
+
|
40
|
+
@write_buffer << data_str
|
41
|
+
@pos += data_size
|
42
|
+
self.flush if @write_buffer.size >= @min_chunk_size
|
43
|
+
|
44
|
+
return data_size
|
45
|
+
end
|
46
|
+
|
47
|
+
def flush
|
48
|
+
return if @write_buffer.empty?
|
49
|
+
|
50
|
+
@multipart_upload.add_part(@write_buffer)
|
51
|
+
@write_buffer.replace '' # String#clear isn't available on 1.8.x
|
52
|
+
|
53
|
+
return nil
|
54
|
+
end
|
55
|
+
|
56
|
+
def close
|
57
|
+
self.flush
|
58
|
+
@multipart_upload.close
|
59
|
+
@multipart_upload = nil
|
60
|
+
|
61
|
+
super
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -3,7 +3,7 @@ require 's3io'
|
|
3
3
|
require 'stringio'
|
4
4
|
|
5
5
|
# Emulate S3Object in a way that allows us to check if its methods are being called properly
|
6
|
-
class
|
6
|
+
class S3ObjectReadMock
|
7
7
|
def initialize(body = '')
|
8
8
|
@body = body
|
9
9
|
end
|
@@ -20,35 +20,35 @@ class S3ObjectMock
|
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
|
-
class
|
23
|
+
class S3ioReadWrapperTest < Test::Unit::TestCase
|
24
24
|
S3_TEST_DATA = File.read('test/s3_test_data.csv')
|
25
25
|
|
26
26
|
def setup
|
27
|
-
@s3object =
|
27
|
+
@s3object = S3ObjectReadMock.new(S3_TEST_DATA)
|
28
28
|
end
|
29
29
|
|
30
|
-
def
|
31
|
-
wrapper = S3io::
|
30
|
+
def test_full_read
|
31
|
+
wrapper = S3io::ReadWrapper.new(@s3object)
|
32
32
|
|
33
33
|
assert_equal(S3_TEST_DATA, wrapper.read)
|
34
34
|
end
|
35
35
|
|
36
|
-
def
|
37
|
-
wrapper = S3io::
|
36
|
+
def test_zero_read
|
37
|
+
wrapper = S3io::ReadWrapper.new(@s3object)
|
38
38
|
|
39
39
|
assert_equal('', wrapper.read(0))
|
40
40
|
assert_equal(0, wrapper.pos)
|
41
41
|
end
|
42
42
|
|
43
|
-
def
|
44
|
-
wrapper = S3io::
|
43
|
+
def test_partial_read
|
44
|
+
wrapper = S3io::ReadWrapper.new(@s3object)
|
45
45
|
|
46
46
|
assert_equal(S3_TEST_DATA[0..99], wrapper.read(100))
|
47
47
|
assert_equal(S3_TEST_DATA[100..100], wrapper.read(1))
|
48
48
|
end
|
49
49
|
|
50
|
-
def
|
51
|
-
wrapper = S3io::
|
50
|
+
def test_each
|
51
|
+
wrapper = S3io::ReadWrapper.new(@s3object)
|
52
52
|
|
53
53
|
lines = []
|
54
54
|
wrapper.each do |line|
|
@@ -58,15 +58,15 @@ class S3ioWrapperTest < Test::Unit::TestCase
|
|
58
58
|
assert_equal(S3_TEST_DATA.lines.to_a, lines)
|
59
59
|
end
|
60
60
|
|
61
|
-
def
|
62
|
-
wrapper = S3io::
|
61
|
+
def test_each_enum
|
62
|
+
wrapper = S3io::ReadWrapper.new(@s3object)
|
63
63
|
|
64
64
|
assert_equal(S3_TEST_DATA.lines.to_a,
|
65
65
|
wrapper.lines.to_a)
|
66
66
|
end
|
67
67
|
|
68
|
-
def
|
69
|
-
wrapper = S3io::
|
68
|
+
def test_pos
|
69
|
+
wrapper = S3io::ReadWrapper.new(@s3object)
|
70
70
|
|
71
71
|
assert_equal(0, wrapper.pos)
|
72
72
|
|
@@ -76,8 +76,8 @@ class S3ioWrapperTest < Test::Unit::TestCase
|
|
76
76
|
assert_equal(S3_TEST_DATA[77, 32], wrapper.read(32))
|
77
77
|
end
|
78
78
|
|
79
|
-
def
|
80
|
-
wrapper = S3io::
|
79
|
+
def test_pos_beyond
|
80
|
+
wrapper = S3io::ReadWrapper.new(@s3object)
|
81
81
|
|
82
82
|
pos_beyond = @s3object.content_length + 100
|
83
83
|
wrapper.pos = pos_beyond
|
@@ -86,8 +86,8 @@ class S3ioWrapperTest < Test::Unit::TestCase
|
|
86
86
|
assert_equal(pos_beyond, wrapper.pos)
|
87
87
|
end
|
88
88
|
|
89
|
-
def
|
90
|
-
wrapper = S3io::
|
89
|
+
def test_rewind
|
90
|
+
wrapper = S3io::ReadWrapper.new(@s3object)
|
91
91
|
|
92
92
|
wrapper.lines do |line|
|
93
93
|
# iterate through all the lines
|
@@ -99,8 +99,31 @@ class S3ioWrapperTest < Test::Unit::TestCase
|
|
99
99
|
assert_equal(S3_TEST_DATA[0..100], wrapper.read(101))
|
100
100
|
end
|
101
101
|
|
102
|
-
def
|
103
|
-
wrapper = S3io::
|
102
|
+
def test_lines_custom_separator
|
103
|
+
wrapper = S3io::ReadWrapper.new(@s3object)
|
104
|
+
|
105
|
+
assert_equal(S3_TEST_DATA.lines(",").to_a, wrapper.lines(",").to_a)
|
106
|
+
end
|
107
|
+
|
108
|
+
def test_eof
|
109
|
+
wrapper = S3io::ReadWrapper.new(@s3object)
|
110
|
+
|
111
|
+
assert_equal(false, wrapper.eof?)
|
112
|
+
|
113
|
+
wrapper.read(10)
|
114
|
+
assert_equal(false, wrapper.eof?)
|
115
|
+
|
116
|
+
wrapper.read
|
117
|
+
assert_equal(true, wrapper.eof?)
|
118
|
+
|
119
|
+
wrapper.rewind
|
120
|
+
assert_equal(false, wrapper.eof?)
|
121
|
+
end
|
122
|
+
|
123
|
+
# Custom options for wrapper
|
124
|
+
|
125
|
+
def test_line_buffer_size
|
126
|
+
wrapper = S3io::ReadWrapper.new(@s3object, :line_buffer_size => 25)
|
104
127
|
|
105
128
|
wrapper.lines.each_with_index do |line, index|
|
106
129
|
break if index == 1 # skip two buffered reads
|
@@ -110,14 +133,8 @@ class S3ioWrapperTest < Test::Unit::TestCase
|
|
110
133
|
assert_equal(S3_TEST_DATA[50..-1], wrapper.read)
|
111
134
|
end
|
112
135
|
|
113
|
-
def
|
114
|
-
wrapper = S3io::
|
115
|
-
|
116
|
-
assert_equal(S3_TEST_DATA.lines(",").to_a, wrapper.lines(",").to_a)
|
117
|
-
end
|
118
|
-
|
119
|
-
def test_s3io_wrapper_empty_each
|
120
|
-
wrapper = S3io::Wrapper.new(S3ObjectMock.new(''))
|
136
|
+
def test_empty_each
|
137
|
+
wrapper = S3io::ReadWrapper.new(S3ObjectReadMock.new(''))
|
121
138
|
|
122
139
|
wrapper.each do |line|
|
123
140
|
assert_equal(false, :never_gets_called)
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 's3io'
|
3
|
+
require 'stringio'
|
4
|
+
|
5
|
+
class S3ObjectWriteMock
|
6
|
+
attr_reader :body
|
7
|
+
attr_accessor :uploaded
|
8
|
+
attr_accessor :multipart_upload_options
|
9
|
+
|
10
|
+
class MultiPartUploadMock
|
11
|
+
attr_reader :options
|
12
|
+
|
13
|
+
def initialize(body, options, mock)
|
14
|
+
@body = body
|
15
|
+
@mock = mock
|
16
|
+
@mock.multipart_upload_options = options
|
17
|
+
end
|
18
|
+
|
19
|
+
def add_part(part)
|
20
|
+
@body << part
|
21
|
+
end
|
22
|
+
|
23
|
+
def close
|
24
|
+
@mock.uploaded = @body
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def initialize
|
29
|
+
@body = ''
|
30
|
+
end
|
31
|
+
|
32
|
+
def multipart_upload(options = {})
|
33
|
+
MultiPartUploadMock.new(@body, options, self)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class S3ioWriteWrapperTest < Test::Unit::TestCase
|
38
|
+
def setup
|
39
|
+
@s3object = S3ObjectWriteMock.new
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_full_write
|
43
|
+
wrapper = S3io::WriteWrapper.new(@s3object)
|
44
|
+
|
45
|
+
test_string = 'This is a test.'
|
46
|
+
|
47
|
+
wrapper.write(test_string)
|
48
|
+
assert_equal(test_string.size, wrapper.pos)
|
49
|
+
assert_equal({}, @s3object.multipart_upload_options)
|
50
|
+
|
51
|
+
wrapper.close
|
52
|
+
assert_equal(test_string, @s3object.uploaded)
|
53
|
+
assert_equal(0, wrapper.pos)
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_multipart_write
|
57
|
+
wrapper = S3io::WriteWrapper.new(@s3object, :max_file_size => 1)
|
58
|
+
|
59
|
+
chunk1 = 'z' * (S3io::WriteWrapper::MIN_CHUNK_SIZE + 100)
|
60
|
+
chunk2 = 'y' * (S3io::WriteWrapper::MIN_CHUNK_SIZE + 300)
|
61
|
+
|
62
|
+
full_body = chunk1 + chunk2
|
63
|
+
|
64
|
+
wrapper.write(chunk1)
|
65
|
+
assert_equal(chunk1, @s3object.body)
|
66
|
+
assert_equal(nil, @s3object.uploaded)
|
67
|
+
|
68
|
+
wrapper.write(chunk2)
|
69
|
+
assert_equal(full_body, @s3object.body)
|
70
|
+
assert_equal(nil, @s3object.uploaded)
|
71
|
+
|
72
|
+
wrapper.close
|
73
|
+
assert_equal(full_body, @s3object.uploaded)
|
74
|
+
end
|
75
|
+
|
76
|
+
def test_flush
|
77
|
+
wrapper = S3io::WriteWrapper.new(@s3object, :multipart_upload_options => {:metadata => {:yes => 'no'}})
|
78
|
+
test_string = '42'
|
79
|
+
|
80
|
+
assert_equal('', @s3object.body)
|
81
|
+
assert_equal({:metadata => {:yes => 'no'}}, @s3object.multipart_upload_options)
|
82
|
+
|
83
|
+
wrapper.write(test_string)
|
84
|
+
wrapper.flush
|
85
|
+
assert_equal(test_string, @s3object.body)
|
86
|
+
|
87
|
+
wrapper.close
|
88
|
+
assert_equal(test_string, @s3object.body)
|
89
|
+
assert_equal(test_string, @s3object.uploaded)
|
90
|
+
end
|
91
|
+
end
|
metadata
CHANGED
@@ -1,39 +1,46 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: s3io
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 23
|
5
5
|
prerelease:
|
6
|
+
segments:
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
- 0
|
10
|
+
version: 1.0.0
|
6
11
|
platform: ruby
|
7
|
-
authors:
|
12
|
+
authors:
|
8
13
|
- Arthur Pirogovski
|
9
14
|
autorequire:
|
10
15
|
bindir: bin
|
11
16
|
cert_chain: []
|
12
|
-
|
13
|
-
|
14
|
-
|
17
|
+
|
18
|
+
date: 2013-01-22 00:00:00 Z
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
15
21
|
name: aws-sdk
|
16
|
-
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
|
-
requirements:
|
19
|
-
- - ! '>='
|
20
|
-
- !ruby/object:Gem::Version
|
21
|
-
version: '0'
|
22
|
-
type: :runtime
|
23
22
|
prerelease: false
|
24
|
-
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
24
|
none: false
|
26
|
-
requirements:
|
27
|
-
- -
|
28
|
-
- !ruby/object:Gem::Version
|
29
|
-
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
hash: 3
|
29
|
+
segments:
|
30
|
+
- 0
|
31
|
+
version: "0"
|
32
|
+
type: :runtime
|
33
|
+
version_requirements: *id001
|
30
34
|
description: An IO-compatible wrapper for S3
|
31
|
-
email:
|
35
|
+
email:
|
32
36
|
- arthur@flyingtealeaf.com
|
33
37
|
executables: []
|
38
|
+
|
34
39
|
extensions: []
|
40
|
+
|
35
41
|
extra_rdoc_files: []
|
36
|
-
|
42
|
+
|
43
|
+
files:
|
37
44
|
- .gitignore
|
38
45
|
- .travis.yml
|
39
46
|
- Gemfile
|
@@ -41,41 +48,49 @@ files:
|
|
41
48
|
- README.md
|
42
49
|
- Rakefile
|
43
50
|
- lib/s3io.rb
|
51
|
+
- lib/s3io/read_wrapper.rb
|
44
52
|
- lib/s3io/version.rb
|
45
53
|
- lib/s3io/wrapper.rb
|
54
|
+
- lib/s3io/write_wrapper.rb
|
46
55
|
- s3io.gemspec
|
47
56
|
- test/s3_test_data.csv
|
48
|
-
- test/
|
49
|
-
- test/
|
57
|
+
- test/test_s3io_read_wrapper.rb
|
58
|
+
- test/test_s3io_write_wrapper.rb
|
50
59
|
homepage: http://github.com/fiksu/s3io
|
51
60
|
licenses: []
|
61
|
+
|
52
62
|
post_install_message:
|
53
63
|
rdoc_options: []
|
54
|
-
|
64
|
+
|
65
|
+
require_paths:
|
55
66
|
- lib
|
56
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
67
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
57
68
|
none: false
|
58
|
-
requirements:
|
59
|
-
- -
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
|
62
|
-
|
69
|
+
requirements:
|
70
|
+
- - ">="
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
hash: 3
|
73
|
+
segments:
|
74
|
+
- 0
|
75
|
+
version: "0"
|
76
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
63
77
|
none: false
|
64
|
-
requirements:
|
65
|
-
- -
|
66
|
-
- !ruby/object:Gem::Version
|
67
|
-
|
78
|
+
requirements:
|
79
|
+
- - ">="
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
hash: 3
|
82
|
+
segments:
|
83
|
+
- 0
|
84
|
+
version: "0"
|
68
85
|
requirements: []
|
86
|
+
|
69
87
|
rubyforge_project:
|
70
88
|
rubygems_version: 1.8.24
|
71
89
|
signing_key:
|
72
90
|
specification_version: 3
|
73
|
-
summary: Amazon's official AWS SDK provides an API for S3 that isn't compatible with
|
74
|
-
|
75
|
-
AWS SDK that makes it possible to access objects stored on S3 as if they were instances
|
76
|
-
of File or StringIO classes.
|
77
|
-
test_files:
|
91
|
+
summary: Amazon's official AWS SDK provides an API for S3 that isn't compatible with Ruby's standard IO class and its derivatives. This gem provides a thin wrapper around AWS SDK that makes it possible to access objects stored on S3 as if they were instances of File or StringIO classes.
|
92
|
+
test_files:
|
78
93
|
- test/s3_test_data.csv
|
79
|
-
- test/
|
80
|
-
- test/
|
94
|
+
- test/test_s3io_read_wrapper.rb
|
95
|
+
- test/test_s3io_write_wrapper.rb
|
81
96
|
has_rdoc:
|
data/test/test_s3io.rb
DELETED
@@ -1,16 +0,0 @@
|
|
1
|
-
require 'test/unit'
|
2
|
-
require 's3io'
|
3
|
-
|
4
|
-
class S3ioTest < Test::Unit::TestCase
|
5
|
-
def test_s3io_new
|
6
|
-
s3object = Object.new
|
7
|
-
wrapper = S3io.new(s3object)
|
8
|
-
assert_equal(S3io::Wrapper, wrapper.class)
|
9
|
-
end
|
10
|
-
|
11
|
-
def test_s3io_new_with_options
|
12
|
-
s3object = Object.new
|
13
|
-
wrapper = S3io.new(s3object, :line_buffer_size => 128)
|
14
|
-
assert_equal(128, wrapper.options[:line_buffer_size])
|
15
|
-
end
|
16
|
-
end
|