aws-sdk-resources 2.0.1.pre → 2.0.2.pre

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
  SHA1:
3
- metadata.gz: 56e7beb4eb1355b479f0a15f4c1a6c39e041b6f3
4
- data.tar.gz: f84dd430391e20dba1493278bfadc5f375e2846f
3
+ metadata.gz: d8d73ea1b21204559a8fb036e60f6d380268a743
4
+ data.tar.gz: b92f963d53c46d4dc64aaab930a5acea6bcba3b3
5
5
  SHA512:
6
- metadata.gz: b6386e64a750440869b42e43da646ef0ef1d6498a1d90ab1a8c8964a1e211a6d805bf253517b67b63d2a7716cdb8bee9cb36f2351320cd0aa1abbcb266de6ad9
7
- data.tar.gz: 24f80ab77c37d0003aacc7f778a04f8ca993a5b507b1df73eef1c83359b1eafb2c490dc78654fce9a1986654a6355cd2f8ead129b92302e82d8ceec60d9e00a7
6
+ metadata.gz: cf73777ea642f80159f357fc2b0e6e1c935637c7688e650ba8949205a242517997a88e84527d02c78264caf37abf19cc92e69f3b510d42a5fca945fc9ff4dca0
7
+ data.tar.gz: ad3dd237c4c1ef3068794bfa1d2ec347f07e44880321a748f610f18037aa82cd57203984d808e431f8106ed4844ea61626cf1b78bd020eaf87da4158d1bb558d
@@ -31,6 +31,11 @@ module Aws
31
31
  else raise ArgumentError, "invalid resource definition #{definition}"
32
32
  end
33
33
  definition.apply(svc_module)
34
- end
35
34
 
35
+ begin
36
+ require "aws-sdk-resources/#{_.downcase}"
37
+ rescue LoadError
38
+ end
39
+
40
+ end
36
41
  end
@@ -4,11 +4,10 @@ module Aws
4
4
 
5
5
  extend OperationMethods
6
6
 
7
- # @overload initialize(options = {})
8
7
  # @overload initialize(*identifiers, options = {})
9
- # @param options Options except `:data` and identifier options are
10
- # used to construct a {Client} unless `:client` is given.
11
- # @option options [Client] :client
8
+ # @param [Hash] options Options except `:data` and identifier options are
9
+ # used to construct a {Client} unless `:client` is given.
10
+ # @option options [Client] :client
12
11
  def initialize(*args)
13
12
  options = args.last.is_a?(Hash) ? args.pop.dup : {}
14
13
  @identifiers = extract_identifiers(args, options)
@@ -62,7 +61,9 @@ module Aws
62
61
  if options[:client]
63
62
  options[:client]
64
63
  else
65
- self.class.client_class.new(options)
64
+ self.class.client_class.new(options.merge(
65
+ user_agent_suffix: "resources"
66
+ ))
66
67
  end
67
68
  end
68
69
 
@@ -0,0 +1,41 @@
1
+ module Aws
2
+ module S3
3
+
4
+ autoload :FilePart, 'aws-sdk-resources/s3/file_part'
5
+ autoload :FileUploader, 'aws-sdk-resources/s3/file_uploader'
6
+ autoload :MultipartFileUploader, 'aws-sdk-resources/s3/multipart_file_uploader'
7
+ autoload :MultipartUploadError, 'aws-sdk-resources/s3/multipart_upload_error'
8
+
9
+ class Object
10
+
11
+ # Uploads a file from disk to the current object in S3.
12
+ #
13
+ # @example
14
+ #
15
+ # obj.upload_file('/path/to/file')
16
+ #
17
+ # @param [String,Pathname,File,Tempfile] source A file or path to a file
18
+ # on the local file system that should be uploaded to this object.
19
+ #
20
+ # @option options [Integer] :multipart_threshold (15728640) Files larger
21
+ # than `:multipart_threshold` are uploaded using the S3 multipart APIs.
22
+ # Default threshold is 15MB.
23
+ #
24
+ # @raise [MultipartUploadError] If an object is being uploaded in
25
+ # parts, and the upload can not be completed, then the upload is
26
+ # aborted and this error is raised. The raised error has a `#errors`
27
+ # method that returns the failures that caused the upload to be
28
+ # aborted.
29
+ #
30
+ # @return [void]
31
+ #
32
+ def upload_file(source, options = {})
33
+ uploader = FileUploader.new(
34
+ multipart_threshold: options.delete(:multipart_threshold),
35
+ client: client)
36
+ uploader.upload(source, options.merge(bucket: bucket_name, key: key))
37
+ end
38
+
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,74 @@
1
+ module Aws
2
+ module S3
3
+
4
+ # A utility class that provides an IO-like interface to a portion of
5
+ # a file on disk.
6
+ # @api private
7
+ class FilePart
8
+
9
+ # @option options [required,String,Pathname,File,Tempfile] :source
10
+ # @option options [required,Integer] :offset The file part will read
11
+ # starting at this byte offset.
12
+ # @option options [required,Integer] :size The maximum number of bytes to
13
+ # read from the `:offset`.
14
+ def initialize(options = {})
15
+ @source = options[:source]
16
+ @first_byte = options[:offset]
17
+ @last_byte = @first_byte + options[:size]
18
+ @size = options[:size]
19
+ end
20
+
21
+ # @return [String,Pathname,File,Tempfile]
22
+ attr_reader :source
23
+
24
+ # @return [Integer]
25
+ attr_reader :first_byte
26
+
27
+ # @return [Integer]
28
+ attr_reader :last_byte
29
+
30
+ # @return [Integer]
31
+ attr_reader :size
32
+
33
+ def read(bytes = nil, output_buffer = nil)
34
+ open_file unless @file
35
+ read_from_file(bytes, output_buffer)
36
+ end
37
+
38
+ def rewind
39
+ if @file
40
+ @file.seek(@first_byte)
41
+ @position = @first_byte
42
+ end
43
+ 0
44
+ end
45
+
46
+ def close
47
+ @file.close if @file
48
+ end
49
+
50
+ private
51
+
52
+ def open_file
53
+ @file = File.open(@source, 'rb')
54
+ rewind
55
+ end
56
+
57
+ def read_from_file(bytes, output_buffer)
58
+ if bytes
59
+ data = @file.read([remaining_bytes, bytes].min)
60
+ data = nil if data == ''
61
+ else
62
+ data = @file.read(remaining_bytes)
63
+ end
64
+ @position += data ? data.bytesize : 0
65
+ output_buffer ? output_buffer.replace(data || '') : data
66
+ end
67
+
68
+ def remaining_bytes
69
+ @last_byte - @position
70
+ end
71
+
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,58 @@
1
+ require 'pathname'
2
+
3
+ module Aws
4
+ module S3
5
+ # @api private
6
+ class FileUploader
7
+
8
+ FIFTEEN_MEGABYTES = 15 * 1024 * 1024
9
+
10
+ # @option options [Client] :client
11
+ # @option options [Integer] :multipart_threshold Files greater than
12
+ # `:multipart_threshold` bytes are uploaded using S3 multipart APIs.
13
+ def initialize(options = {})
14
+ @options = options
15
+ @client = options[:client] || Client.new
16
+ @multipart_threshold = options[:multipart_threshold] || FIFTEEN_MEGABYTES
17
+ end
18
+
19
+ # @return [Client]
20
+ attr_reader :client
21
+
22
+ # @return [Integer] Files larger than this in bytes are uploaded
23
+ # using a {MultipartFileUploader}.
24
+ attr_reader :multipart_threshold
25
+
26
+ # @param [String,Pathname,File,Tempfile] source
27
+ # @option options [requried,String] :bucket
28
+ # @option options [requried,String] :key
29
+ # @return [void]
30
+ def upload(source, options = {})
31
+ if File.size(source) >= multipart_threshold
32
+ MultipartFileUploader.new(@options).upload(source, options)
33
+ else
34
+ put_object(source, options)
35
+ end
36
+ end
37
+
38
+ private
39
+
40
+ def put_object(source, options)
41
+ open_file(source) do |file|
42
+ @client.put_object(options.merge(body: file))
43
+ end
44
+ end
45
+
46
+ def open_file(source)
47
+ if String === source || Pathname === source
48
+ file = File.open(source, 'rb')
49
+ yield(file)
50
+ file.close
51
+ else
52
+ yield(source)
53
+ end
54
+ end
55
+
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,163 @@
1
+ require 'pathname'
2
+ require 'thread'
3
+
4
+ module Aws
5
+ module S3
6
+ # @api private
7
+ class MultipartFileUploader
8
+
9
+ MIN_PART_SIZE = 5 * 1024 * 1024 # 5MB
10
+
11
+ FILE_TOO_SMALL = "unable to multipart upload files smaller than 5MB"
12
+
13
+ MAX_PARTS = 10_000
14
+
15
+ THREAD_COUNT = 10
16
+
17
+ # @option options [Client] :client
18
+ def initialize(options = {})
19
+ @client = options[:client] || Client.new
20
+ end
21
+
22
+ # @return [Client]
23
+ attr_reader :client
24
+
25
+ # @param [String,Pathname,File,Tempfile] source
26
+ # @option options [requried,String] :bucket
27
+ # @option options [requried,String] :key
28
+ # @return [void]
29
+ def upload(source, options = {})
30
+ if File.size(source) < MIN_PART_SIZE
31
+ raise ArgumentError, FILE_TOO_SMALL
32
+ else
33
+ upload_id = initiate_upload(options)
34
+ parts = upload_parts(upload_id, source, options)
35
+ complete_upload(upload_id, parts, options)
36
+ end
37
+ end
38
+
39
+ private
40
+
41
+ def initiate_upload(options)
42
+ @client.create_multipart_upload(options).upload_id
43
+ end
44
+
45
+ def complete_upload(upload_id, parts, options)
46
+ @client.complete_multipart_upload(
47
+ bucket: options[:bucket],
48
+ key: options[:key],
49
+ upload_id: upload_id,
50
+ multipart_upload: { parts: parts })
51
+ end
52
+
53
+ def upload_parts(upload_id, source, options)
54
+ pending = PartList.new(compute_parts(upload_id, source, options))
55
+ completed = PartList.new
56
+ errors = upload_in_threads(pending, completed)
57
+ if errors.empty?
58
+ completed.to_a.sort_by { |part| part[:part_number] }
59
+ else
60
+ abort_upload(upload_id, options, errors)
61
+ end
62
+ end
63
+
64
+ def abort_upload(upload_id, options, errors)
65
+ @client.abort_multipart_upload(
66
+ bucket: options[:bucket],
67
+ key: options[:key],
68
+ upload_id: upload_id
69
+ )
70
+ msg = "multipart upload failed: #{errors.map(&:message).join("; ")}"
71
+ raise MultipartUploadError.new(msg, errors)
72
+ rescue MultipartUploadError => error
73
+ raise error
74
+ rescue => error
75
+ msg = "failed to abort multipart upload: #{error.message}"
76
+ raise MultipartUploadError.new(msg, errors + [error])
77
+ end
78
+
79
+ def compute_parts(upload_id, source, options)
80
+ size = File.size(source)
81
+ default_part_size = compute_default_part_size(size)
82
+ offset = 0
83
+ part_number = 1
84
+ parts = []
85
+ while offset < size
86
+ parts << options.merge(
87
+ upload_id: upload_id,
88
+ part_number: part_number,
89
+ body: FilePart.new(
90
+ source: source,
91
+ offset: offset,
92
+ size: part_size(size, default_part_size, offset)
93
+ )
94
+ )
95
+ part_number += 1
96
+ offset += default_part_size
97
+ end
98
+ parts
99
+ end
100
+
101
+ def upload_in_threads(pending, completed)
102
+ threads = []
103
+ THREAD_COUNT.times do
104
+ thread = Thread.new do
105
+ begin
106
+ while part = pending.shift
107
+ resp = @client.upload_part(part)
108
+ part[:body].close
109
+ completed.push(etag: resp.etag, part_number: part[:part_number])
110
+ end
111
+ nil
112
+ rescue => error
113
+ # keep other threads from uploading other parts
114
+ pending.clear!
115
+ error
116
+ end
117
+ end
118
+ thread.abort_on_exception = true
119
+ threads << thread
120
+ end
121
+ errors = threads.map(&:value).compact
122
+ end
123
+
124
+ def compute_default_part_size(source_size)
125
+ [(source_size.to_f / MAX_PARTS).ceil, MIN_PART_SIZE].max.to_i
126
+ end
127
+
128
+ def part_size(total_size, part_size, offset)
129
+ if offset + part_size > total_size
130
+ total_size - offset
131
+ else
132
+ part_size
133
+ end
134
+ end
135
+
136
+ # @api private
137
+ class PartList
138
+
139
+ def initialize(parts = [])
140
+ @parts = parts
141
+ @mutex = Mutex.new
142
+ end
143
+
144
+ def push(part)
145
+ @mutex.synchronize { @parts.push(part) }
146
+ end
147
+
148
+ def shift
149
+ @mutex.synchronize { @parts.shift }
150
+ end
151
+
152
+ def clear!
153
+ @mutex.synchronize { @parts.clear }
154
+ end
155
+
156
+ def to_a
157
+ @mutex.synchronize { @parts.dup }
158
+ end
159
+
160
+ end
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,16 @@
1
+ module Aws
2
+ module S3
3
+ class MultipartUploadError < StandardError
4
+
5
+ def initialize(message, errors)
6
+ @errors = errors
7
+ super(message)
8
+ end
9
+
10
+ # @return [Array<StandardError>] The list of errors encountered
11
+ # when uploading or aborting the upload.
12
+ attr_reader :errors
13
+
14
+ end
15
+ end
16
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: aws-sdk-resources
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.1.pre
4
+ version: 2.0.2.pre
5
5
  platform: ruby
6
6
  authors:
7
7
  - Amazon Web Services
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-09-29 00:00:00.000000000 Z
11
+ date: 2014-10-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: aws-sdk-core
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - '='
18
18
  - !ruby/object:Gem::Version
19
- version: 2.0.1
19
+ version: 2.0.2
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - '='
25
25
  - !ruby/object:Gem::Version
26
- version: 2.0.1
26
+ version: 2.0.2
27
27
  description: Provides resource-oriented abstractions for AWS.
28
28
  email:
29
29
  executables: []
@@ -51,6 +51,11 @@ files:
51
51
  - lib/aws-sdk-resources/request.rb
52
52
  - lib/aws-sdk-resources/request_params.rb
53
53
  - lib/aws-sdk-resources/resource.rb
54
+ - lib/aws-sdk-resources/s3/file_part.rb
55
+ - lib/aws-sdk-resources/s3/file_uploader.rb
56
+ - lib/aws-sdk-resources/s3/multipart_file_uploader.rb
57
+ - lib/aws-sdk-resources/s3/multipart_upload_error.rb
58
+ - lib/aws-sdk-resources/s3.rb
54
59
  - lib/aws-sdk-resources/source.rb
55
60
  - lib/aws-sdk-resources/validator/context.rb
56
61
  - lib/aws-sdk-resources/validator/identifier_validator.rb
@@ -84,4 +89,3 @@ signing_key:
84
89
  specification_version: 4
85
90
  summary: AWS SDK for Ruby - Resources
86
91
  test_files: []
87
- has_rdoc: