aboisvert_aws 3.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.
Files changed (78) hide show
  1. data/History.txt +329 -0
  2. data/Manifest.txt +61 -0
  3. data/README.txt +163 -0
  4. data/Rakefile +130 -0
  5. data/lib/acf/right_acf_interface.rb +549 -0
  6. data/lib/acf/right_acf_invalidations.rb +144 -0
  7. data/lib/acf/right_acf_origin_access_identities.rb +230 -0
  8. data/lib/acf/right_acf_streaming_interface.rb +229 -0
  9. data/lib/acw/right_acw_interface.rb +248 -0
  10. data/lib/as/right_as_interface.rb +698 -0
  11. data/lib/awsbase/benchmark_fix.rb +39 -0
  12. data/lib/awsbase/right_awsbase.rb +1343 -0
  13. data/lib/awsbase/support.rb +35 -0
  14. data/lib/awsbase/version.rb +9 -0
  15. data/lib/ec2/right_ec2.rb +541 -0
  16. data/lib/ec2/right_ec2_ebs.rb +481 -0
  17. data/lib/ec2/right_ec2_images.rb +444 -0
  18. data/lib/ec2/right_ec2_instances.rb +788 -0
  19. data/lib/ec2/right_ec2_monitoring.rb +70 -0
  20. data/lib/ec2/right_ec2_placement_groups.rb +108 -0
  21. data/lib/ec2/right_ec2_reserved_instances.rb +184 -0
  22. data/lib/ec2/right_ec2_security_groups.rb +491 -0
  23. data/lib/ec2/right_ec2_spot_instances.rb +422 -0
  24. data/lib/ec2/right_ec2_tags.rb +139 -0
  25. data/lib/ec2/right_ec2_vpc.rb +590 -0
  26. data/lib/ec2/right_ec2_vpc2.rb +381 -0
  27. data/lib/ec2/right_ec2_windows_mobility.rb +84 -0
  28. data/lib/elb/right_elb_interface.rb +573 -0
  29. data/lib/emr/right_emr_interface.rb +727 -0
  30. data/lib/iam/right_iam_access_keys.rb +71 -0
  31. data/lib/iam/right_iam_groups.rb +195 -0
  32. data/lib/iam/right_iam_interface.rb +341 -0
  33. data/lib/iam/right_iam_mfa_devices.rb +67 -0
  34. data/lib/iam/right_iam_users.rb +251 -0
  35. data/lib/rds/right_rds_interface.rb +1384 -0
  36. data/lib/right_aws.rb +86 -0
  37. data/lib/route_53/right_route_53_interface.rb +640 -0
  38. data/lib/s3/right_s3.rb +1138 -0
  39. data/lib/s3/right_s3_interface.rb +1278 -0
  40. data/lib/sdb/active_sdb.rb +1107 -0
  41. data/lib/sdb/right_sdb_interface.rb +762 -0
  42. data/lib/sns/right_sns_interface.rb +286 -0
  43. data/lib/sqs/right_sqs.rb +387 -0
  44. data/lib/sqs/right_sqs_gen2.rb +342 -0
  45. data/lib/sqs/right_sqs_gen2_interface.rb +523 -0
  46. data/lib/sqs/right_sqs_interface.rb +593 -0
  47. data/right_aws.gemspec +90 -0
  48. data/test/README.mdown +39 -0
  49. data/test/acf/test_helper.rb +2 -0
  50. data/test/acf/test_right_acf.rb +138 -0
  51. data/test/awsbase/test_helper.rb +2 -0
  52. data/test/awsbase/test_right_awsbase.rb +11 -0
  53. data/test/ec2/test_helper.rb +2 -0
  54. data/test/ec2/test_right_ec2.rb +107 -0
  55. data/test/elb/test_helper.rb +2 -0
  56. data/test/elb/test_right_elb.rb +43 -0
  57. data/test/http_connection.rb +87 -0
  58. data/test/rds/test_helper.rb +2 -0
  59. data/test/rds/test_right_rds.rb +120 -0
  60. data/test/route_53/fixtures/a_record.xml +18 -0
  61. data/test/route_53/fixtures/alias_record.xml +18 -0
  62. data/test/route_53/test_helper.rb +2 -0
  63. data/test/route_53/test_right_route_53.rb +141 -0
  64. data/test/s3/test_helper.rb +2 -0
  65. data/test/s3/test_right_s3.rb +528 -0
  66. data/test/s3/test_right_s3_stubbed.rb +97 -0
  67. data/test/sdb/test_active_sdb.rb +357 -0
  68. data/test/sdb/test_batch_put_attributes.rb +54 -0
  69. data/test/sdb/test_helper.rb +3 -0
  70. data/test/sdb/test_right_sdb.rb +253 -0
  71. data/test/sns/test_helper.rb +2 -0
  72. data/test/sns/test_right_sns.rb +153 -0
  73. data/test/sqs/test_helper.rb +2 -0
  74. data/test/sqs/test_right_sqs.rb +285 -0
  75. data/test/sqs/test_right_sqs_gen2.rb +264 -0
  76. data/test/test_credentials.rb +37 -0
  77. data/test/ts_right_aws.rb +15 -0
  78. metadata +257 -0
@@ -0,0 +1,762 @@
1
+ #
2
+ # Copyright (c) 2008 RightScale Inc
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ #
23
+
24
+ require "right_aws"
25
+
26
+ module RightAws
27
+
28
+ class SdbInterface < RightAwsBase
29
+
30
+ include RightAwsBaseInterface
31
+
32
+ DEFAULT_HOST = 'sdb.amazonaws.com'
33
+ DEFAULT_PORT = 443
34
+ DEFAULT_PROTOCOL = 'https'
35
+ DEFAULT_PATH = '/'
36
+ API_VERSION = '2009-04-15'
37
+ DEFAULT_NIL_REPRESENTATION = 'nil'
38
+
39
+ @@bench = AwsBenchmarkingBlock.new
40
+ def self.bench_xml; @@bench.xml; end
41
+ def self.bench_sdb; @@bench.service; end
42
+
43
+ attr_reader :last_query_expression
44
+
45
+ # Creates new RightSdb instance.
46
+ #
47
+ # Params:
48
+ # { :server => 'sdb.amazonaws.com' # Amazon service host: 'sdb.amazonaws.com'(default)
49
+ # :port => 443 # Amazon service port: 80 or 443(default)
50
+ # :protocol => 'https' # Amazon service protocol: 'http' or 'https'(default)
51
+ # :signature_version => '0' # The signature version : '0','1 or '2'(default)
52
+ # :logger => Logger Object # Logger instance: logs to STDOUT if omitted
53
+ # :nil_representation => 'mynil'} # interpret Ruby nil as this string value; i.e. use this string in SDB to represent Ruby nils (default is the string 'nil')
54
+ #
55
+ # Example:
56
+ #
57
+ # sdb = RightAws::SdbInterface.new('1E3GDYEOGFJPIT7XXXXXX','hgTHt68JY07JKUY08ftHYtERkjgtfERn57XXXXXX', {:logger => Logger.new('/tmp/x.log')}) #=> #<RightSdb:0xa6b8c27c>
58
+ #
59
+ # see: http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/
60
+ #
61
+ def initialize(aws_access_key_id=nil, aws_secret_access_key=nil, params={})
62
+ @nil_rep = params[:nil_representation] ? params[:nil_representation] : DEFAULT_NIL_REPRESENTATION
63
+ params.delete(:nil_representation)
64
+ init({ :name => 'SDB',
65
+ :default_host => ENV['SDB_URL'] ? URI.parse(ENV['SDB_URL']).host : DEFAULT_HOST,
66
+ :default_port => ENV['SDB_URL'] ? URI.parse(ENV['SDB_URL']).port : DEFAULT_PORT,
67
+ :default_service => ENV['SDB_URL'] ? URI.parse(ENV['SDB_URL']).path : DEFAULT_PATH,
68
+ :default_protocol => ENV['SDB_URL'] ? URI.parse(ENV['SDB_URL']).scheme : DEFAULT_PROTOCOL,
69
+ :default_api_version => ENV['SDB_API_VERSION'] || API_VERSION },
70
+ aws_access_key_id || ENV['AWS_ACCESS_KEY_ID'],
71
+ aws_secret_access_key || ENV['AWS_SECRET_ACCESS_KEY'],
72
+ params)
73
+ end
74
+
75
+ #-----------------------------------------------------------------
76
+ # Requests
77
+ #-----------------------------------------------------------------
78
+
79
+ def generate_request(action, params={}) #:nodoc:
80
+ generate_request_impl(:get, action, params )
81
+ end
82
+
83
+ # Sends request to Amazon and parses the response
84
+ # Raises AwsError if any banana happened
85
+ def request_info(request, parser) #:nodoc:
86
+ request_info_impl(:sdb_connection, @@bench, request, parser)
87
+ end
88
+
89
+ # Prepare attributes for putting.
90
+ # (used by put_attributes)
91
+ def pack_attributes(items_or_attributes, replace = false, batch = false) #:nodoc:
92
+ if batch
93
+ index = 0
94
+ items_or_attributes.inject({}){|result, (item_name, attributes)|
95
+ item_prefix = "Item.#{index}."
96
+ result["#{item_prefix}ItemName"] = item_name.to_s
97
+ result.merge!(
98
+ pack_single_item_attributes(attributes, replace, item_prefix))
99
+ index += 1
100
+ result
101
+ }
102
+ else
103
+ pack_single_item_attributes(items_or_attributes, replace)
104
+ end
105
+ end
106
+
107
+ def pack_single_item_attributes(attributes, replace, prefix = "")
108
+ result = {}
109
+ if attributes
110
+ idx = 0
111
+ skip_values = attributes.is_a?(Array)
112
+ attributes.each do |attribute, values|
113
+ # set replacement attribute
114
+ result["#{prefix}Attribute.#{idx}.Replace"] = 'true' if replace
115
+ # pack Name/Value
116
+ unless values.nil?
117
+ # Array(values) does not work here:
118
+ # - Array('') => [] but we wanna get here ['']
119
+ [values].flatten.each do |value|
120
+ result["#{prefix}Attribute.#{idx}.Name"] = attribute
121
+ result["#{prefix}Attribute.#{idx}.Value"] = ruby_to_sdb(value) unless skip_values
122
+ idx += 1
123
+ end
124
+ else
125
+ result["#{prefix}Attribute.#{idx}.Name"] = attribute
126
+ result["#{prefix}Attribute.#{idx}.Value"] = ruby_to_sdb(nil) unless skip_values
127
+ idx += 1
128
+ end
129
+ end
130
+ end
131
+ result
132
+ end
133
+
134
+ # Use this helper to manually escape the fields in the query expressions.
135
+ # To escape the single quotes and backslashes and to wrap the string into the single quotes.
136
+ #
137
+ # see: http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/SDB_API.html
138
+ #
139
+ def escape(value)
140
+ %Q{'#{value.to_s.gsub(/(['\\])/){ "\\#{$1}" }}'} if value
141
+ end
142
+
143
+ # Convert a Ruby language value to a SDB value by replacing Ruby nil with the user's chosen string representation of nil.
144
+ # Non-nil values are unaffected by this filter.
145
+ def ruby_to_sdb(value)
146
+ value.nil? ? @nil_rep : value
147
+ end
148
+
149
+ # Convert a SDB value to a Ruby language value by replacing the user's chosen string representation of nil with Ruby nil.
150
+ # Values are unaffected by this filter unless they match the nil representation exactly.
151
+ def sdb_to_ruby(value)
152
+ value.eql?(@nil_rep) ? nil : value
153
+ end
154
+
155
+ # Convert select and query_with_attributes responses to a Ruby language values by replacing the user's chosen string representation of nil with Ruby nil.
156
+ # (This method affects on a passed response value)
157
+ def select_response_to_ruby(response) #:nodoc:
158
+ response[:items].each_with_index do |item, idx|
159
+ item.each do |key, attributes|
160
+ attributes.each do |name, values|
161
+ values.collect! { |value| sdb_to_ruby(value) }
162
+ end
163
+ end
164
+ end
165
+ response
166
+ end
167
+
168
+ # Create query expression from an array.
169
+ # (similar to ActiveRecord::Base#find using :conditions => ['query', param1, .., paramN])
170
+ #
171
+ def query_expression_from_array(params) #:nodoc:
172
+ return '' if params.right_blank?
173
+ query = params.shift.to_s
174
+ query.gsub(/(\\)?(\?)/) do
175
+ if $1 # if escaped '\?' is found - replace it by '?' without backslash
176
+ "?"
177
+ else # well, if no backslash precedes '?' then replace it by next param from the list
178
+ escape(params.shift)
179
+ end
180
+ end
181
+ end
182
+
183
+ def query_expression_from_hash(hash)
184
+ return '' if hash.right_blank?
185
+ expression = []
186
+ hash.each do |key, value|
187
+ expression << "#{key}=#{escape(value)}"
188
+ end
189
+ expression.join(' AND ')
190
+ end
191
+
192
+ # Retrieve a list of SDB domains from Amazon.
193
+ #
194
+ # Returns a hash:
195
+ # { :domains => [domain1, ..., domainN],
196
+ # :next_token => string || nil,
197
+ # :box_usage => string,
198
+ # :request_id => string }
199
+ #
200
+ # Example:
201
+ #
202
+ # sdb = RightAws::SdbInterface.new
203
+ # sdb.list_domains #=> { :box_usage => "0.0000071759",
204
+ # :request_id => "976709f9-0111-2345-92cb-9ce90acd0982",
205
+ # :domains => ["toys", "dolls"]}
206
+ #
207
+ # If a block is given, this method yields to it. If the block returns true, list_domains will continue looping the request. If the block returns false,
208
+ # list_domains will end.
209
+ #
210
+ # sdb.list_domains(10) do |result| # list by 10 domains per iteration
211
+ # puts result.inspect
212
+ # true
213
+ # end
214
+ #
215
+ # see: http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/SDB_API_ListDomains.html
216
+ #
217
+ def list_domains(max_number_of_domains = nil, next_token = nil )
218
+ request_params = { 'MaxNumberOfDomains' => max_number_of_domains,
219
+ 'NextToken' => next_token }
220
+ link = generate_request("ListDomains", request_params)
221
+ result = request_info(link, QSdbListDomainParser.new)
222
+ # return result if no block given
223
+ return result unless block_given?
224
+ # loop if block if given
225
+ begin
226
+ # the block must return true if it wanna continue
227
+ break unless yield(result) && result[:next_token]
228
+ # make new request
229
+ request_params['NextToken'] = result[:next_token]
230
+ link = generate_request("ListDomains", request_params)
231
+ result = request_info(link, QSdbListDomainParser.new)
232
+ end while true
233
+ rescue Exception
234
+ on_exception
235
+ end
236
+
237
+ # Create new SDB domain at Amazon.
238
+ #
239
+ # Returns a hash: { :box_usage, :request_id } on success or an exception on error.
240
+ # (Amazon raises no errors if the domain already exists).
241
+ #
242
+ # Example:
243
+ #
244
+ # sdb = RightAws::SdbInterface.new
245
+ # sdb.create_domain('toys') # => { :box_usage => "0.0000071759",
246
+ # :request_id => "976709f9-0111-2345-92cb-9ce90acd0982" }
247
+ #
248
+ # see: http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/SDB_API_CreateDomain.html
249
+ def create_domain(domain_name)
250
+ link = generate_request("CreateDomain",
251
+ 'DomainName' => domain_name)
252
+ request_info(link, QSdbSimpleParser.new)
253
+ rescue Exception
254
+ on_exception
255
+ end
256
+
257
+ # Query Metadata for Domain
258
+ #
259
+ # Returns a hash on success or an exception on error.
260
+ #
261
+ # example:
262
+ # sdb = RightAWS:::SdbInterface.new
263
+ # sdb.domain_metadata('toys') # => {:attribute_values_size_bytes=>"2754",
264
+ # :item_count=>"25",
265
+ # :item_names_size_bytes=>"900",
266
+ # :timestamp=>"1291890409",
267
+ # :attribute_name_count=>"7",
268
+ # :box_usage=>"0.0000071759",
269
+ # :attribute_names_size_bytes=>"48",
270
+ # :attribute_value_count=>"154",
271
+ # :request_id=>"79bbfe8f-f0c9-59a2-0963-16d5fc6c3c52"}
272
+ # see http://docs.amazonwebservices.com/AmazonSimpleDB/latest/DeveloperGuide/index.html?SDB_API_DomainMetadata.html
273
+ def domain_metadata(domain)
274
+ link = generate_request("DomainMetadata","DomainName"=>domain)
275
+ request_info(link,QSdbGenericParser.new)
276
+ end
277
+
278
+ # Delete SDB domain at Amazon.
279
+ #
280
+ # Returns a hash: { :box_usage, :request_id } on success or an exception on error.
281
+ # (Amazon raises no errors if the domain does not exist).
282
+ #
283
+ # Example:
284
+ #
285
+ # sdb = RightAws::SdbInterface.new
286
+ # sdb.delete_domain('toys') # => { :box_usage => "0.0000071759",
287
+ # :request_id => "976709f9-0111-2345-92cb-9ce90acd0982" }
288
+ #
289
+ # see: http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/SDB_API_DeleteDomain.html
290
+ #
291
+ def delete_domain(domain_name)
292
+ link = generate_request("DeleteDomain",
293
+ 'DomainName' => domain_name)
294
+ request_info(link, QSdbSimpleParser.new)
295
+ rescue Exception
296
+ on_exception
297
+ end
298
+
299
+ # Add/Replace item attributes.
300
+ #
301
+ # Params:
302
+ # domain_name = DomainName
303
+ # item_name = ItemName
304
+ # attributes = {
305
+ # 'nameA' => [valueA1,..., valueAN],
306
+ # ...
307
+ # 'nameZ' => [valueZ1,..., valueZN]
308
+ # }
309
+ # replace = :replace | any other value to skip replacement
310
+ #
311
+ # Returns a hash: { :box_usage, :request_id } on success or an exception on error.
312
+ # (Amazon raises no errors if the attribute was not overridden, as when the :replace param is unset).
313
+ #
314
+ # Example:
315
+ #
316
+ # sdb = RightAws::SdbInterface.new
317
+ # sdb.create_domain 'family'
318
+ #
319
+ # attributes = {}
320
+ # # create attributes for Jon and Silvia
321
+ # attributes['Jon'] = %w{ car beer }
322
+ # attributes['Silvia'] = %w{ beetle rolling_pin kids }
323
+ # sdb.put_attributes 'family', 'toys', attributes #=> ok
324
+ # # now: Jon=>[car, beer], Silvia=>[beetle, rolling_pin, kids]
325
+ #
326
+ # # add attributes to Jon
327
+ # attributes.delete('Silvia')
328
+ # attributes['Jon'] = %w{ girls pub }
329
+ # sdb.put_attributes 'family', 'toys', attributes #=> ok
330
+ # # now: Jon=>[car, beer, girls, pub], Silvia=>[beetle, rolling_pin, kids]
331
+ #
332
+ # # replace attributes for Jon and add to a cat (the cat had no attributes before)
333
+ # attributes['Jon'] = %w{ vacuum_cleaner hammer spade }
334
+ # attributes['cat'] = %w{ mouse clew Jons_socks }
335
+ # sdb.put_attributes 'family', 'toys', attributes, :replace #=> ok
336
+ # # now: Jon=>[vacuum_cleaner, hammer, spade], Silvia=>[beetle, rolling_pin, kids], cat=>[mouse, clew, Jons_socks]
337
+ #
338
+ # see: http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/SDB_API_PutAttributes.html
339
+ #
340
+ def put_attributes(domain_name, item_name, attributes, replace = false)
341
+ params = { 'DomainName' => domain_name,
342
+ 'ItemName' => item_name }.merge(pack_attributes(attributes, replace))
343
+ link = generate_request("PutAttributes", params)
344
+ request_info( link, QSdbSimpleParser.new )
345
+ rescue Exception
346
+ on_exception
347
+ end
348
+
349
+ # Add/Replace attributes for multiple items at a time.
350
+ #
351
+ # Params:
352
+ # domain_name = DomainName
353
+ # items = {
354
+ # 'Item1' => {
355
+ # 'nameA' => [valueA1, valueA2,..., valueAN],
356
+ # ...
357
+ # 'nameB' => [valueB1, valueB2,..., valueBN]
358
+ # },
359
+ # 'Item2' => {
360
+ # 'nameC' => [valueC1, valueC2,..., valueCN],
361
+ # ...
362
+ # 'nameD' => [valueD1, valueD2,..., valueDN]
363
+ # }
364
+ # }
365
+ # replace = :replace | any other value to skip replacement
366
+ #
367
+ # Usage of batch_put_attributes is similar to put_attributes except that
368
+ # instead of supplying an item_name and a hash of attributes, you supply a
369
+ # hash of item names to attributes.
370
+ #
371
+ # See: http://docs.amazonwebservices.com/AmazonSimpleDB/latest/DeveloperGuide/index.html?SDB_API_BatchPutAttributes.html
372
+ def batch_put_attributes(domain_name, items, replace = false)
373
+ params = { 'DomainName' => domain_name }.merge(pack_attributes(items, replace, true))
374
+ link = generate_request("BatchPutAttributes", params)
375
+ request_info( link, QSdbSimpleParser.new)
376
+ rescue Exception
377
+ on_exception
378
+ end
379
+
380
+ # Retrieve SDB item's attribute(s).
381
+ #
382
+ # Returns a hash:
383
+ # { :box_usage => string,
384
+ # :request_id => string,
385
+ # :attributes => { 'nameA' => [valueA1,..., valueAN],
386
+ # ... ,
387
+ # 'nameZ' => [valueZ1,..., valueZN] } }
388
+ #
389
+ # Example:
390
+ # # request all attributes
391
+ # sdb.get_attributes('family', 'toys') # => { :attributes => {"cat" => ["clew", "Jons_socks", "mouse"] },
392
+ # "Silvia" => ["beetle", "rolling_pin", "kids"],
393
+ # "Jon" => ["vacuum_cleaner", "hammer", "spade"]},
394
+ # :box_usage => "0.0000093222",
395
+ # :request_id => "81273d21-000-1111-b3f9-512d91d29ac8" }
396
+ #
397
+ # # request cat's attributes only
398
+ # sdb.get_attributes('family', 'toys', 'cat') # => { :attributes => {"cat" => ["clew", "Jons_socks", "mouse"] },
399
+ # :box_usage => "0.0000093222",
400
+ # :request_id => "81273d21-001-1111-b3f9-512d91d29ac8" }
401
+ #
402
+ # # request all attributes using a consistent read
403
+ # # see: http://docs.amazonwebservices.com/AmazonSimpleDB/latest/DeveloperGuide/index.html?ConsistencySummary.html
404
+ # sdb.get_attributes('family', 'toys', nil, true) # => { :attributes => {"cat" => ["clew", "Jons_socks", "mouse"] },
405
+ # "Silvia" => ["beetle", "rolling_pin", "kids"],
406
+ # "Jon" => ["vacuum_cleaner", "hammer", "spade"]},
407
+ # :box_usage => "0.0000093222",
408
+ # :request_id => "81273d21-000-1111-b3f9-512d91d29ac8" }
409
+ #
410
+ # see: http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/SDB_API_GetAttributes.html
411
+ #
412
+ def get_attributes(domain_name, item_name, attribute_name=nil, consistent_read=nil)
413
+ link = generate_request("GetAttributes", 'DomainName' => domain_name,
414
+ 'ItemName' => item_name,
415
+ 'AttributeName' => attribute_name,
416
+ 'ConsistentRead' => consistent_read )
417
+ res = request_info(link, QSdbGetAttributesParser.new)
418
+ res[:attributes].each_value do |values|
419
+ values.collect! { |e| sdb_to_ruby(e) }
420
+ end
421
+ res
422
+ rescue Exception
423
+ on_exception
424
+ end
425
+
426
+ # Delete value, attribute or item.
427
+ #
428
+ # Example:
429
+ # # delete 'vodka' and 'girls' from 'Jon' and 'mice' from 'cat'.
430
+ # sdb.delete_attributes 'family', 'toys', { 'Jon' => ['vodka', 'girls'], 'cat' => ['mice'] }
431
+ #
432
+ # # delete the all the values from attributes (i.e. delete the attributes)
433
+ # sdb.delete_attributes 'family', 'toys', { 'Jon' => [], 'cat' => [] }
434
+ # # or
435
+ # sdb.delete_attributes 'family', 'toys', [ 'Jon', 'cat' ]
436
+ #
437
+ # # delete all the attributes from item 'toys' (i.e. delete the item)
438
+ # sdb.delete_attributes 'family', 'toys'
439
+ #
440
+ # see http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/SDB_API_DeleteAttributes.html
441
+ #
442
+ def delete_attributes(domain_name, item_name, attributes = nil)
443
+ params = { 'DomainName' => domain_name,
444
+ 'ItemName' => item_name }.merge(pack_attributes(attributes))
445
+ link = generate_request("DeleteAttributes", params)
446
+ request_info( link, QSdbSimpleParser.new )
447
+ rescue Exception
448
+ on_exception
449
+ end
450
+
451
+
452
+ # QUERY:
453
+
454
+ # Perform a query on SDB.
455
+ #
456
+ # Returns a hash:
457
+ # { :box_usage => string,
458
+ # :request_id => string,
459
+ # :next_token => string,
460
+ # :items => [ItemName1,..., ItemNameN] }
461
+ #
462
+ # Example:
463
+ #
464
+ # query = "['cat' = 'clew']"
465
+ # sdb.query('family', query) #=> hash of data
466
+ # sdb.query('family', query, 10) #=> hash of data with max of 10 items
467
+ #
468
+ # If a block is given, query will iteratively yield results to it as long as the block continues to return true.
469
+ #
470
+ # # List 10 items per iteration. Don't
471
+ # # forget to escape single quotes and backslashes and wrap all the items in single quotes.
472
+ # query = "['cat'='clew'] union ['dog'='Jon\\'s boot']"
473
+ # sdb.query('family', query, 10) do |result|
474
+ # puts result.inspect
475
+ # true
476
+ # end
477
+ #
478
+ # # Same query using automatic escaping...to use the auto escape, pass the query and its params as an array:
479
+ # query = [ "['cat'=?] union ['dog'=?]", "clew", "Jon's boot" ]
480
+ # sdb.query('family', query)
481
+ #
482
+ # query = [ "['cat'=?] union ['dog'=?] sort 'cat' desc", "clew", "Jon's boot" ]
483
+ # sdb.query('family', query)
484
+ #
485
+ # see: http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/SDB_API_Query.html
486
+ # http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/index.html?SortingData.html
487
+ #
488
+ def query(domain_name, query_expression = nil, max_number_of_items = nil, next_token = nil)
489
+ query_expression = query_expression_from_array(query_expression) if query_expression.is_a?(Array)
490
+ @last_query_expression = query_expression
491
+ #
492
+ request_params = { 'DomainName' => domain_name,
493
+ 'QueryExpression' => query_expression,
494
+ 'MaxNumberOfItems' => max_number_of_items,
495
+ 'NextToken' => next_token }
496
+ link = generate_request("Query", request_params)
497
+ result = request_info( link, QSdbQueryParser.new )
498
+ # return result if no block given
499
+ return result unless block_given?
500
+ # loop if block if given
501
+ begin
502
+ # the block must return true if it wanna continue
503
+ break unless yield(result) && result[:next_token]
504
+ # make new request
505
+ request_params['NextToken'] = result[:next_token]
506
+ link = generate_request("Query", request_params)
507
+ result = request_info( link, QSdbQueryParser.new )
508
+ end while true
509
+ rescue Exception
510
+ on_exception
511
+ end
512
+
513
+ # Perform a query and fetch specified attributes.
514
+ # If attributes are not specified then fetches the whole list of attributes.
515
+ #
516
+ #
517
+ # Returns a hash:
518
+ # { :box_usage => string,
519
+ # :request_id => string,
520
+ # :next_token => string,
521
+ # :items => [ { ItemName1 => { attribute1 => value1, ... attributeM => valueM } },
522
+ # { ItemName2 => {...}}, ... ]
523
+ #
524
+ # Example:
525
+ #
526
+ # sdb.query_with_attributes(domain, ['hobby', 'country'], "['gender'='female'] intersection ['name' starts-with ''] sort 'name'") #=>
527
+ # { :request_id => "06057228-70d0-4487-89fb-fd9c028580d3",
528
+ # :items =>
529
+ # [ { "035f1ba8-dbd8-11dd-80bd-001bfc466dd7"=>
530
+ # { "hobby" => ["cooking", "flowers", "cats"],
531
+ # "country" => ["Russia"]}},
532
+ # { "0327614a-dbd8-11dd-80bd-001bfc466dd7"=>
533
+ # { "hobby" => ["patchwork", "bundle jumping"],
534
+ # "country" => ["USA"]}}, ... ],
535
+ # :box_usage=>"0.0000504786"}
536
+ #
537
+ # sdb.query_with_attributes(domain, [], "['gender'='female'] intersection ['name' starts-with ''] sort 'name'") #=>
538
+ # { :request_id => "75bb19db-a529-4f69-b86f-5e3800f79a45",
539
+ # :items =>
540
+ # [ { "035f1ba8-dbd8-11dd-80bd-001bfc466dd7"=>
541
+ # { "hobby" => ["cooking", "flowers", "cats"],
542
+ # "name" => ["Mary"],
543
+ # "country" => ["Russia"],
544
+ # "gender" => ["female"],
545
+ # "id" => ["035f1ba8-dbd8-11dd-80bd-001bfc466dd7"]}},
546
+ # { "0327614a-dbd8-11dd-80bd-001bfc466dd7"=>
547
+ # { "hobby" => ["patchwork", "bundle jumping"],
548
+ # "name" => ["Mary"],
549
+ # "country" => ["USA"],
550
+ # "gender" => ["female"],
551
+ # "id" => ["0327614a-dbd8-11dd-80bd-001bfc466dd7"]}}, ... ],
552
+ # :box_usage=>"0.0000506668"}
553
+ #
554
+ # see: http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/index.html?SDB_API_QueryWithAttributes.html
555
+ #
556
+ def query_with_attributes(domain_name, attributes=[], query_expression = nil, max_number_of_items = nil, next_token = nil)
557
+ attributes = Array(attributes)
558
+ query_expression = query_expression_from_array(query_expression) if query_expression.is_a?(Array)
559
+ @last_query_expression = query_expression
560
+ #
561
+ request_params = { 'DomainName' => domain_name,
562
+ 'QueryExpression' => query_expression,
563
+ 'MaxNumberOfItems' => max_number_of_items,
564
+ 'NextToken' => next_token }
565
+ attributes.each_with_index do |attribute, idx|
566
+ request_params["AttributeName.#{idx+1}"] = attribute
567
+ end
568
+ link = generate_request("QueryWithAttributes", request_params)
569
+ result = select_response_to_ruby(request_info( link, QSdbQueryWithAttributesParser.new ))
570
+ # return result if no block given
571
+ return result unless block_given?
572
+ # loop if block if given
573
+ begin
574
+ # the block must return true if it wanna continue
575
+ break unless yield(result) && result[:next_token]
576
+ # make new request
577
+ request_params['NextToken'] = result[:next_token]
578
+ link = generate_request("QueryWithAttributes", request_params)
579
+ result = select_response_to_ruby(request_info( link, QSdbQueryWithAttributesParser.new ))
580
+ end while true
581
+ rescue Exception
582
+ on_exception
583
+ end
584
+
585
+ # Perform SQL-like select and fetch attributes.
586
+ # Attribute values must be quoted with a single or double quote. If a quote appears within the attribute value, it must be escaped with the same quote symbol as shown in the following example.
587
+ # (Use array to pass select_expression params to avoid manual escaping).
588
+ #
589
+ # sdb.select(["select * from my_domain where gender=?", 'female']) #=>
590
+ # {:request_id =>"8241b843-0fb9-4d66-9100-effae12249ec",
591
+ # :items =>
592
+ # [ { "035f1ba8-dbd8-11dd-80bd-001bfc466dd7"=>
593
+ # {"hobby" => ["cooking", "flowers", "cats"],
594
+ # "name" => ["Mary"],
595
+ # "country" => ["Russia"],
596
+ # "gender" => ["female"],
597
+ # "id" => ["035f1ba8-dbd8-11dd-80bd-001bfc466dd7"]}},
598
+ # { "0327614a-dbd8-11dd-80bd-001bfc466dd7"=>
599
+ # {"hobby" => ["patchwork", "bundle jumping"],
600
+ # "name" => ["Mary"],
601
+ # "country" => ["USA"],
602
+ # "gender" => ["female"],
603
+ # "id" => ["0327614a-dbd8-11dd-80bd-001bfc466dd7"]}}, ... ]
604
+ # :box_usage =>"0.0000506197"}
605
+ #
606
+ # sdb.select('select country, name from my_domain') #=>
607
+ # {:request_id=>"b1600198-c317-413f-a8dc-4e7f864a940a",
608
+ # :items=>
609
+ # [ { "035f1ba8-dbd8-11dd-80bd-001bfc466dd7"=> {"name"=>["Mary"], "country"=>["Russia"]} },
610
+ # { "376d2e00-75b0-11dd-9557-001bfc466dd7"=> {"name"=>["Putin"], "country"=>["Russia"]} },
611
+ # { "0327614a-dbd8-11dd-80bd-001bfc466dd7"=> {"name"=>["Mary"], "country"=>["USA"]} },
612
+ # { "372ebbd4-75b0-11dd-9557-001bfc466dd7"=> {"name"=>["Bush"], "country"=>["USA"]} },
613
+ # { "37a4e552-75b0-11dd-9557-001bfc466dd7"=> {"name"=>["Medvedev"], "country"=>["Russia"]} },
614
+ # { "38278dfe-75b0-11dd-9557-001bfc466dd7"=> {"name"=>["Mary"], "country"=>["Russia"]} },
615
+ # { "37df6c36-75b0-11dd-9557-001bfc466dd7"=> {"name"=>["Mary"], "country"=>["USA"]} } ],
616
+ # :box_usage=>"0.0000777663"}
617
+ #
618
+ # see: http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/index.html?SDB_API_Select.html
619
+ # http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/index.html?UsingSelect.html
620
+ # http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/index.html?SDBLimits.html
621
+ #
622
+ def select(select_expression, next_token = nil)
623
+ select_expression = query_expression_from_array(select_expression) if select_expression.is_a?(Array)
624
+ @last_query_expression = select_expression
625
+ #
626
+ request_params = { 'SelectExpression' => select_expression,
627
+ 'NextToken' => next_token }
628
+ link = generate_request("Select", request_params)
629
+ result = select_response_to_ruby(request_info( link, QSdbSelectParser.new ))
630
+ return result unless block_given?
631
+ # loop if block if given
632
+ begin
633
+ # the block must return true if it wanna continue
634
+ break unless yield(result) && result[:next_token]
635
+ # make new request
636
+ request_params['NextToken'] = result[:next_token]
637
+ link = generate_request("Select", request_params)
638
+ result = select_response_to_ruby(request_info( link, QSdbSelectParser.new ))
639
+ end while true
640
+ rescue Exception
641
+ on_exception
642
+ end
643
+
644
+ #-----------------------------------------------------------------
645
+ # PARSERS:
646
+ #-----------------------------------------------------------------
647
+ class QSdbGenericParser < RightAWSParser #:nodoc:
648
+ def reset
649
+ @result = {}
650
+ end
651
+ def tagend(name)
652
+ case full_tag_name
653
+ when %r{/(DomainMetadataResult|ResponseMetadata)/}
654
+ @result[name.right_underscore.to_sym] = @text
655
+ end
656
+ end
657
+ end
658
+
659
+ class QSdbListDomainParser < RightAWSParser #:nodoc:
660
+ def reset
661
+ @result = { :domains => [] }
662
+ end
663
+ def tagend(name)
664
+ case name
665
+ when 'NextToken' then @result[:next_token] = @text
666
+ when 'DomainName' then @result[:domains] << @text
667
+ when 'BoxUsage' then @result[:box_usage] = @text
668
+ when 'RequestId' then @result[:request_id] = @text
669
+ end
670
+ end
671
+ end
672
+
673
+ class QSdbSimpleParser < RightAWSParser #:nodoc:
674
+ def reset
675
+ @result = {}
676
+ end
677
+ def tagend(name)
678
+ case name
679
+ when 'BoxUsage' then @result[:box_usage] = @text
680
+ when 'RequestId' then @result[:request_id] = @text
681
+ end
682
+ end
683
+ end
684
+
685
+ class QSdbGetAttributesParser < RightAWSParser #:nodoc:
686
+ def reset
687
+ @last_attribute_name = nil
688
+ @result = { :attributes => {} }
689
+ end
690
+ def tagend(name)
691
+ case name
692
+ when 'Name' then @last_attribute_name = @text
693
+ when 'Value' then (@result[:attributes][@last_attribute_name] ||= []) << @text
694
+ when 'BoxUsage' then @result[:box_usage] = @text
695
+ when 'RequestId' then @result[:request_id] = @text
696
+ end
697
+ end
698
+ end
699
+
700
+ class QSdbQueryParser < RightAWSParser #:nodoc:
701
+ def reset
702
+ @result = { :items => [] }
703
+ end
704
+ def tagend(name)
705
+ case name
706
+ when 'ItemName' then @result[:items] << @text
707
+ when 'BoxUsage' then @result[:box_usage] = @text
708
+ when 'RequestId' then @result[:request_id] = @text
709
+ when 'NextToken' then @result[:next_token] = @text
710
+ end
711
+ end
712
+ end
713
+
714
+ class QSdbQueryWithAttributesParser < RightAWSParser #:nodoc:
715
+ def reset
716
+ @result = { :items => [] }
717
+ end
718
+ def tagend(name)
719
+ case name
720
+ when 'Name'
721
+ case @xmlpath
722
+ when 'QueryWithAttributesResponse/QueryWithAttributesResult/Item'
723
+ @item = @text
724
+ @result[:items] << { @item => {} }
725
+ when 'QueryWithAttributesResponse/QueryWithAttributesResult/Item/Attribute'
726
+ @attribute = @text
727
+ @result[:items].last[@item][@attribute] ||= []
728
+ end
729
+ when 'RequestId' then @result[:request_id] = @text
730
+ when 'BoxUsage' then @result[:box_usage] = @text
731
+ when 'NextToken' then @result[:next_token] = @text
732
+ when 'Value' then @result[:items].last[@item][@attribute] << @text
733
+ end
734
+ end
735
+ end
736
+
737
+ class QSdbSelectParser < RightAWSParser #:nodoc:
738
+ def reset
739
+ @result = { :items => [] }
740
+ end
741
+ def tagend(name)
742
+ case name
743
+ when 'Name'
744
+ case @xmlpath
745
+ when 'SelectResponse/SelectResult/Item'
746
+ @item = @text
747
+ @result[:items] << { @item => {} }
748
+ when 'SelectResponse/SelectResult/Item/Attribute'
749
+ @attribute = @text
750
+ @result[:items].last[@item][@attribute] ||= []
751
+ end
752
+ when 'RequestId' then @result[:request_id] = @text
753
+ when 'BoxUsage' then @result[:box_usage] = @text
754
+ when 'NextToken' then @result[:next_token] = @text
755
+ when 'Value' then @result[:items].last[@item][@attribute] << @text
756
+ end
757
+ end
758
+ end
759
+
760
+ end
761
+
762
+ end