aws-ses 0.1.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.
@@ -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
+