aws-ses 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,50 @@
1
+ #--
2
+ # AWS ERROR CODES
3
+ # AWS can throw error exceptions that contain a '.' in them.
4
+ # since we can't name an exception class with that '.' I compressed
5
+ # each class name into the non-dot version which allows us to retain
6
+ # the granularity of the exception.
7
+ #++
8
+
9
+ module AWS
10
+
11
+ # All AWS errors are superclassed by Error < RuntimeError
12
+ class Error < RuntimeError; end
13
+
14
+ # CLIENT : A client side argument error
15
+ class ArgumentError < Error; end
16
+
17
+ # Server Error Codes
18
+ ###
19
+
20
+ # Server : Internal Error.
21
+ class InternalError < Error; end
22
+
23
+ # Server : Not enough available addresses to satisfy your minimum request.
24
+ class InsufficientAddressCapacity < Error; end
25
+
26
+ # Server : There are not enough available instances to satisfy your minimum request.
27
+ class InsufficientInstanceCapacity < Error; end
28
+
29
+ # Server : There are not enough available reserved instances to satisfy your minimum request.
30
+ class InsufficientReservedInstanceCapacity < Error; end
31
+
32
+ # Server : The server is overloaded and cannot handle the request.
33
+ class Unavailable < Error; end
34
+
35
+ # API Errors
36
+ ############################
37
+
38
+ # Server : Invalid AWS Account
39
+ class InvalidClientTokenId < Error; end
40
+
41
+ # Server : The provided signature does not match.
42
+ class SignatureDoesNotMatch < Error; end
43
+
44
+ # SES Errors
45
+ ############################
46
+
47
+
48
+
49
+ end
50
+
@@ -0,0 +1,313 @@
1
+ #:stopdoc:
2
+
3
+ class Hash
4
+ def to_query_string(include_question_mark = true)
5
+ query_string = ''
6
+ unless empty?
7
+ query_string << '?' if include_question_mark
8
+ query_string << inject([]) do |params, (key, value)|
9
+ params << "#{key}=#{value}"
10
+ end.join('&')
11
+ end
12
+ query_string
13
+ end
14
+
15
+ def to_normalized_options
16
+ # Convert all option names to downcased strings, and replace underscores with hyphens
17
+ inject({}) do |normalized_options, (name, value)|
18
+ normalized_options[name.to_header] = value.to_s
19
+ normalized_options
20
+ end
21
+ end
22
+
23
+ def to_normalized_options!
24
+ replace(to_normalized_options)
25
+ end
26
+ end
27
+
28
+ class String
29
+ if RUBY_VERSION <= '1.9'
30
+ def previous!
31
+ self[-1] -= 1
32
+ self
33
+ end
34
+ else
35
+ def previous!
36
+ self[-1] = (self[-1].ord - 1).chr
37
+ self
38
+ end
39
+ end
40
+
41
+ def previous
42
+ dup.previous!
43
+ end
44
+
45
+ def to_header
46
+ downcase.tr('_', '-')
47
+ end
48
+
49
+ # ActiveSupport adds an underscore method to String so let's just use that one if
50
+ # we find that the method is already defined
51
+ def underscore
52
+ gsub(/::/, '/').
53
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
54
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
55
+ tr("-", "_").downcase
56
+ end unless public_method_defined? :underscore
57
+
58
+ if RUBY_VERSION >= '1.9'
59
+ def valid_utf8?
60
+ dup.force_encoding('UTF-8').valid_encoding?
61
+ end
62
+ else
63
+ def valid_utf8?
64
+ scan(Regexp.new('[^\x00-\xa0]', nil, 'u')) { |s| s.unpack('U') }
65
+ true
66
+ rescue ArgumentError
67
+ false
68
+ end
69
+ end
70
+
71
+ # All paths in in S3 have to be valid unicode so this takes care of
72
+ # cleaning up any strings that aren't valid utf-8 according to String#valid_utf8?
73
+ if RUBY_VERSION >= '1.9'
74
+ def remove_extended!
75
+ sanitized_string = ''
76
+ each_byte do |byte|
77
+ character = byte.chr
78
+ sanitized_string << character if character.ascii_only?
79
+ end
80
+ sanitized_string
81
+ end
82
+ else
83
+ def remove_extended!
84
+ gsub!(/[\x80-\xFF]/) { "%02X" % $&[0] }
85
+ end
86
+ end
87
+
88
+ def remove_extended
89
+ dup.remove_extended!
90
+ end
91
+ end
92
+
93
+ class CoercibleString < String
94
+ class << self
95
+ def coerce(string)
96
+ new(string).coerce
97
+ end
98
+ end
99
+
100
+ def coerce
101
+ case self
102
+ when 'true'; true
103
+ when 'false'; false
104
+ # Don't coerce numbers that start with zero
105
+ when /^[1-9]+\d*$/; Integer(self)
106
+ when datetime_format; Time.parse(self)
107
+ else
108
+ self
109
+ end
110
+ end
111
+
112
+ private
113
+ # Lame hack since Date._parse is so accepting. S3 dates are of the form: '2006-10-29T23:14:47.000Z'
114
+ # so unless the string looks like that, don't even try, otherwise it might convert an object's
115
+ # key from something like '03 1-2-3-Apple-Tree.mp3' to Sat Feb 03 00:00:00 CST 2001.
116
+ def datetime_format
117
+ /^\d{4}-\d{2}-\d{2}\w\d{2}:\d{2}:\d{2}/
118
+ end
119
+ end
120
+
121
+ class Symbol
122
+ def to_header
123
+ to_s.to_header
124
+ end
125
+ end
126
+
127
+ module Kernel
128
+ def __method__(depth = 0)
129
+ caller[depth][/`([^']+)'/, 1]
130
+ end if RUBY_VERSION <= '1.8.7'
131
+
132
+ def __called_from__
133
+ caller[1][/`([^']+)'/, 1]
134
+ end if RUBY_VERSION > '1.8.7'
135
+
136
+ def expirable_memoize(reload = false, storage = nil)
137
+ current_method = RUBY_VERSION > '1.8.7' ? __called_from__ : __method__(1)
138
+ storage = "@#{storage || current_method}"
139
+ if reload
140
+ instance_variable_set(storage, nil)
141
+ else
142
+ if cache = instance_variable_get(storage)
143
+ return cache
144
+ end
145
+ end
146
+ instance_variable_set(storage, yield)
147
+ end
148
+
149
+ def require_library_or_gem(library, gem_name = nil)
150
+ if RUBY_VERSION >= '1.9'
151
+ gem(gem_name || library, '>=0')
152
+ end
153
+ require library
154
+ rescue LoadError => library_not_installed
155
+ begin
156
+ require 'rubygems'
157
+ require library
158
+ rescue LoadError
159
+ raise library_not_installed
160
+ end
161
+ end
162
+ end
163
+
164
+ class Object
165
+ def returning(value)
166
+ yield(value)
167
+ value
168
+ end
169
+ end
170
+
171
+ class Module
172
+ def memoized(method_name)
173
+ original_method = "unmemoized_#{method_name}_#{Time.now.to_i}"
174
+ alias_method original_method, method_name
175
+ module_eval(<<-EVAL, __FILE__, __LINE__)
176
+ def #{method_name}(reload = false, *args, &block)
177
+ expirable_memoize(reload) do
178
+ send(:#{original_method}, *args, &block)
179
+ end
180
+ end
181
+ EVAL
182
+ end
183
+
184
+ def constant(name, value)
185
+ unless const_defined?(name)
186
+ const_set(name, value)
187
+ module_eval(<<-EVAL, __FILE__, __LINE__)
188
+ def self.#{name.to_s.downcase}
189
+ #{name.to_s}
190
+ end
191
+ EVAL
192
+ end
193
+ end
194
+
195
+ # Transforms MarcelBucket into
196
+ #
197
+ # class MarcelBucket < AWS::S3::Bucket
198
+ # set_current_bucket_to 'marcel'
199
+ # end
200
+ def const_missing_from_s3_library(sym)
201
+ if sym.to_s =~ /^(\w+)(Bucket|S3Object)$/
202
+ const = const_set(sym, Class.new(AWS::S3.const_get($2)))
203
+ const.current_bucket = $1.underscore
204
+ const
205
+ else
206
+ const_missing_not_from_s3_library(sym)
207
+ end
208
+ end
209
+ alias_method :const_missing_not_from_s3_library, :const_missing
210
+ alias_method :const_missing, :const_missing_from_s3_library
211
+ end
212
+
213
+
214
+ class Class # :nodoc:
215
+ def cattr_reader(*syms)
216
+ syms.flatten.each do |sym|
217
+ class_eval(<<-EOS, __FILE__, __LINE__)
218
+ unless defined? @@#{sym}
219
+ @@#{sym} = nil
220
+ end
221
+
222
+ def self.#{sym}
223
+ @@#{sym}
224
+ end
225
+
226
+ def #{sym}
227
+ @@#{sym}
228
+ end
229
+ EOS
230
+ end
231
+ end
232
+
233
+ def cattr_writer(*syms)
234
+ syms.flatten.each do |sym|
235
+ class_eval(<<-EOS, __FILE__, __LINE__)
236
+ unless defined? @@#{sym}
237
+ @@#{sym} = nil
238
+ end
239
+
240
+ def self.#{sym}=(obj)
241
+ @@#{sym} = obj
242
+ end
243
+
244
+ def #{sym}=(obj)
245
+ @@#{sym} = obj
246
+ end
247
+ EOS
248
+ end
249
+ end
250
+
251
+ def cattr_accessor(*syms)
252
+ cattr_reader(*syms)
253
+ cattr_writer(*syms)
254
+ end
255
+ end if Class.instance_methods(false).grep(/^cattr_(?:reader|writer|accessor)$/).empty?
256
+
257
+ module SelectiveAttributeProxy
258
+ def self.included(klass)
259
+ klass.extend(ClassMethods)
260
+ klass.class_eval(<<-EVAL, __FILE__, __LINE__)
261
+ cattr_accessor :attribute_proxy
262
+ cattr_accessor :attribute_proxy_options
263
+
264
+ # Default name for attribute storage
265
+ self.attribute_proxy = :attributes
266
+ self.attribute_proxy_options = {:exclusively => true}
267
+
268
+ private
269
+ # By default proxy all attributes
270
+ def proxiable_attribute?(name)
271
+ return true unless self.class.attribute_proxy_options[:exclusively]
272
+ send(self.class.attribute_proxy).has_key?(name)
273
+ end
274
+
275
+ def method_missing(method, *args, &block)
276
+ # Autovivify attribute storage
277
+ if method == self.class.attribute_proxy
278
+ ivar = "@\#{method}"
279
+ instance_variable_set(ivar, {}) unless instance_variable_get(ivar).is_a?(Hash)
280
+ instance_variable_get(ivar)
281
+ # Delegate to attribute storage
282
+ elsif method.to_s =~ /^(\\w+)(=?)$/ && proxiable_attribute?($1)
283
+ attributes_hash_name = self.class.attribute_proxy
284
+ $2.empty? ? send(attributes_hash_name)[$1] : send(attributes_hash_name)[$1] = args.first
285
+ else
286
+ super
287
+ end
288
+ end
289
+ EVAL
290
+ end
291
+
292
+ module ClassMethods
293
+ def proxy_to(attribute_name, options = {})
294
+ if attribute_name.is_a?(Hash)
295
+ options = attribute_name
296
+ else
297
+ self.attribute_proxy = attribute_name
298
+ end
299
+ self.attribute_proxy_options = options
300
+ end
301
+ end
302
+ end
303
+
304
+
305
+ class XmlGenerator < String #:nodoc:
306
+ attr_reader :xml
307
+ def initialize
308
+ @xml = Builder::XmlMarkup.new(:indent => 2, :target => self)
309
+ super()
310
+ build
311
+ end
312
+ end
313
+ #:startdoc:
@@ -0,0 +1,97 @@
1
+ module AWS
2
+ module SES
3
+ class Response < String
4
+ attr_reader :response, :body, :parsed, :action
5
+
6
+ def initialize(action, response)
7
+ @action = action
8
+ @response = response
9
+ @body = response.body.to_s
10
+ super(body)
11
+ end
12
+
13
+ def headers
14
+ headers = {}
15
+ response.each do |header, value|
16
+ headers[header] = value
17
+ end
18
+ headers
19
+ end
20
+ memoized :headers
21
+
22
+ def [](header)
23
+ headers[header]
24
+ end
25
+
26
+ def each(&block)
27
+ headers.each(&block)
28
+ end
29
+
30
+ def code
31
+ response.code.to_i
32
+ end
33
+
34
+ {:success => 200..299, :redirect => 300..399,
35
+ :client_error => 400..499, :server_error => 500..599}.each do |result, code_range|
36
+ class_eval(<<-EVAL, __FILE__, __LINE__)
37
+ def #{result}?
38
+ return false unless response
39
+ (#{code_range}).include? code
40
+ end
41
+ EVAL
42
+ end
43
+
44
+ def error?
45
+ !success? && response['content-type'] == 'application/xml' && parsed.root == 'error'
46
+ end
47
+
48
+ def error
49
+ Error.new(parsed, self)
50
+ end
51
+ memoized :error
52
+
53
+ def parsed
54
+ parse_options = { 'forcearray' => ['item', 'member'], 'suppressempty' => nil, 'keeproot' => false }
55
+ # parse_options = { 'suppressempty' => nil, 'keeproot' => false }
56
+
57
+ xml = XmlSimple.xml_in(body, parse_options)
58
+ xml["#{@action}Result"]
59
+ end
60
+ memoized :parsed
61
+
62
+ # It's expected that each subclass of Response will override this method with what part of response is relevant
63
+ def result
64
+ parsed
65
+ end
66
+
67
+ def inspect
68
+ "#<%s:0x%s %s %s>" % [self.class, object_id, response.code, response.message]
69
+ end
70
+ end # class Response
71
+
72
+ # Requests whose response code is between 300 and 599 and contain an <Error></Error> in their body
73
+ # are wrapped in an Error::Response. This Error::Response contains an Error object which raises an exception
74
+ # that corresponds to the error in the response body. The exception object contains the ErrorResponse, so
75
+ # in all cases where a request happens, you can rescue ResponseError and have access to the ErrorResponse and
76
+ # its Error object which contains information about the ResponseError.
77
+ #
78
+ # begin
79
+ # Bucket.create(..)
80
+ # rescue ResponseError => exception
81
+ # exception.response
82
+ # # => <Error::Response>
83
+ # exception.response.error
84
+ # # => <Error>
85
+ # end
86
+ class Error < Response
87
+ def error?
88
+ true
89
+ end
90
+
91
+ def inspect
92
+ "#<%s:0x%s %s %s: '%s'>" % [self.class.name, object_id, response.code, error.code, error.message]
93
+ end
94
+ end
95
+ end #module SES
96
+ end # module AWS
97
+