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