gotime_aws 2.5.6

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,890 @@
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
+ module Aws
25
+
26
+ class SdbInterface < AwsBase
27
+
28
+ include AwsBaseInterface
29
+
30
+ DEFAULT_HOST = 'sdb.amazonaws.com'
31
+ DEFAULT_PORT = 443
32
+ DEFAULT_PROTOCOL = 'https'
33
+ DEFAULT_SERVICE = '/'
34
+ API_VERSION = '2009-04-15'
35
+ DEFAULT_NIL_REPRESENTATION = 'nil'
36
+
37
+ def self.connection_name
38
+ :s3_connection
39
+ end
40
+
41
+ @@bench = AwsBenchmarkingBlock.new
42
+
43
+ def self.bench
44
+ @@bench
45
+ end
46
+
47
+ def self.bench_xml;
48
+ @@bench.xml;
49
+ end
50
+
51
+ def self.bench_sdb;
52
+ @@bench.service;
53
+ end
54
+
55
+ attr_reader :last_query_expression
56
+
57
+ # Creates new RightSdb instance.
58
+ #
59
+ # Params:
60
+ # { :server => 'sdb.amazonaws.com' # Amazon service host: 'sdb.amazonaws.com'(default)
61
+ # :port => 443 # Amazon service port: 80(default) or 443
62
+ # :protocol => 'https' # Amazon service protocol: 'http'(default) or 'https'
63
+ # :signature_version => '2' # The signature version : '0', '1' or '2' (default)
64
+ # DEPRECATED :multi_thread => true|false # Multi-threaded (connection per each thread): true or false(default)
65
+ # :connection_mode => :default # options are :default (will use best known option, may change in the future)
66
+ # :per_request (opens and closes a connection on every request to SDB)
67
+ # :single - one connection shared across app (same as old multi_thread=>false)
68
+ # :per_thread - one connection per ruby thread (same as old multi_thread=>true)
69
+ # :pool (uses a connection pool with a maximum number of connections - NOT IMPLEMENTED YET)
70
+ # :logger => Logger Object # Logger instance: logs to STDOUT if omitted
71
+ # :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')
72
+ # :service => '/' # Set this to /mdb/request.mgwsi for usage with M/DB #
73
+ #
74
+ # Example:
75
+ #
76
+ # sdb = Aws::SdbInterface.new('1E3GDYEOGFJPIT7XXXXXX','hgTHt68JY07JKUY08ftHYtERkjgtfERn57XXXXXX', {:connection_mode => :per_request, :logger => Logger.new('/tmp/x.log')}) #=> #<RightSdb:0xa6b8c27c>
77
+ #
78
+ # see: http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/
79
+ #
80
+ def initialize(aws_access_key_id=nil, aws_secret_access_key=nil, params={})
81
+ @nil_rep = params[:nil_representation] ? params[:nil_representation] : DEFAULT_NIL_REPRESENTATION
82
+ params.delete(:nil_representation)
83
+ init({:name => 'SDB',
84
+ :default_host => ENV['SDB_URL'] ? URI.parse(ENV['SDB_URL']).host : DEFAULT_HOST,
85
+ :default_port => ENV['SDB_URL'] ? URI.parse(ENV['SDB_URL']).port : DEFAULT_PORT,
86
+ :default_protocol => ENV['SDB_URL'] ? URI.parse(ENV['SDB_URL']).scheme : DEFAULT_PROTOCOL,
87
+ :default_service => ENV['SDB_URL'] ? URI.parse(ENV['SDB_URL']).path : DEFAULT_SERVICE},
88
+ # :service_endpoint => ENV['SDB_URL'] ? URI.parse(ENV['SDB_URL']).path : DEFAULT_ENDPOINT },
89
+ aws_access_key_id || ENV['AWS_ACCESS_KEY_ID'],
90
+ aws_secret_access_key || ENV['AWS_SECRET_ACCESS_KEY'],
91
+ params)
92
+ end
93
+
94
+ #-----------------------------------------------------------------
95
+ # Requests
96
+ #-----------------------------------------------------------------
97
+ def generate_request(action, params={}, options={}) #:nodoc:
98
+ generate_request2(@aws_access_key_id, @aws_secret_access_key, action, API_VERSION, @params, params, options)
99
+ end
100
+
101
+
102
+ # Sends request to Amazon and parses the response
103
+ # Raises AwsError if any banana happened
104
+ def request_info(request, parser, options={}) #:nodoc:
105
+ # request_info2(request, parser, :sdb_connection)
106
+ request_info2(request, parser, @params, :sdb_connection, @logger, @@bench, options)
107
+
108
+ end
109
+
110
+ # Prepare attributes for putting.
111
+ # (used by put_attributes)
112
+ def pack_attributes(attributes, replace = false, key_prefix = "") #:nodoc:
113
+ result = {}
114
+ if attributes
115
+ idx = 0
116
+ skip_values = attributes.is_a?(Array)
117
+ attributes.each do |attribute, values|
118
+ # set replacement attribute
119
+ result["#{key_prefix}Attribute.#{idx}.Replace"] = 'true' if replace
120
+ # pack Name/Value
121
+ unless values.nil?
122
+ Array(values).each do |value|
123
+ result["#{key_prefix}Attribute.#{idx}.Name"] = attribute
124
+ result["#{key_prefix}Attribute.#{idx}.Value"] = ruby_to_sdb(value) unless skip_values
125
+ idx += 1
126
+ end
127
+ else
128
+ result["#{key_prefix}Attribute.#{idx}.Name"] = attribute
129
+ result["#{key_prefix}Attribute.#{idx}.Value"] = ruby_to_sdb(nil) unless skip_values
130
+ idx += 1
131
+ end
132
+ end
133
+ end
134
+ result
135
+ end
136
+
137
+
138
+ # Use this helper to manually escape the fields in the query expressions.
139
+ # To escape the single quotes and backslashes and to wrap the string into the single quotes.
140
+ #
141
+ # see: http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/SDB_API.html
142
+ #
143
+ def escape(value)
144
+ # %Q{'#{value.to_s.gsub(/(['\\])/) { "\\#{$1}" }}'} if value
145
+ %{'#{value.to_s.gsub(/(['\\])/){"#{$1}#{$1}"}}'} if value
146
+ end
147
+
148
+ # Convert a Ruby language value to a SDB value by replacing Ruby nil with the user's chosen string representation of nil.
149
+ # Non-nil values are unaffected by this filter.
150
+ def ruby_to_sdb(value)
151
+ # puts "value #{value} is frozen? #{value.frozen?}"
152
+ # value.nil? ? @nil_rep : ((value.frozen? || !value.is_a?(String)) ? value : value.force_encoding("UTF-8"))
153
+ value.nil? ? @nil_rep : value
154
+ end
155
+
156
+ # Convert a SDB value to a Ruby language value by replacing the user's chosen string representation of nil with Ruby nil.
157
+ # Values are unaffected by this filter unless they match the nil representation exactly.
158
+ def sdb_to_ruby(value)
159
+ value.eql?(@nil_rep) ? nil : value
160
+ end
161
+
162
+ # 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.
163
+ # (This method affects on a passed response value)
164
+ def select_response_to_ruby(response) #:nodoc:
165
+ response[:items].each_with_index do |item, idx|
166
+ item.each do |key, attributes|
167
+ attributes.each do |name, values|
168
+ values.collect! { |value| sdb_to_ruby(value) }
169
+ end
170
+ end
171
+ end
172
+ response
173
+ end
174
+
175
+ # Create query expression from an array.
176
+ # (similar to ActiveRecord::Base#find using :conditions => ['query', param1, .., paramN])
177
+ #
178
+ def query_expression_from_array(params) #:nodoc:
179
+ return '' if Aws::Utils.blank?(params)
180
+ query = params[0].to_s
181
+ i = 1
182
+ query.gsub(/(\\)?(\?)/) do
183
+ if $1 # if escaped '\?' is found - replace it by '?' without backslash
184
+ "?"
185
+ else # well, if no backslash precedes '?' then replace it by next param from the list
186
+ case params[i]
187
+ when Array
188
+ ret = "(#{params[i].map{|p| escape(p)}.join(",")})"
189
+ else
190
+ ret = escape(params[i])
191
+ end
192
+ i +=1
193
+ ret
194
+ end
195
+ end
196
+ end
197
+
198
+ def query_expression_from_hash(hash)
199
+ return '' if Aws::Utils.blank?(hash)
200
+ expression = []
201
+ hash.each do |key, value|
202
+ expression << "#{key}=#{escape(value)}"
203
+ end
204
+ expression.join(' AND ')
205
+ end
206
+
207
+ # Retrieve a list of SDB domains from Amazon.
208
+ #
209
+ # Returns a hash:
210
+ # { :domains => [domain1, ..., domainN],
211
+ # :next_token => string || nil,
212
+ # :box_usage => string,
213
+ # :request_id => string }
214
+ #
215
+ # Example:
216
+ #
217
+ # sdb = Aws::SdbInterface.new
218
+ # sdb.list_domains #=> { :box_usage => "0.0000071759",
219
+ # :request_id => "976709f9-0111-2345-92cb-9ce90acd0982",
220
+ # :domains => ["toys", "dolls"]}
221
+ #
222
+ # 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,
223
+ # list_domains will end.
224
+ #
225
+ # sdb.list_domains(10) do |result| # list by 10 domains per iteration
226
+ # puts result.inspect
227
+ # true
228
+ # end
229
+ #
230
+ # see: http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/SDB_API_ListDomains.html
231
+ #
232
+ def list_domains(max_number_of_domains = nil, next_token = nil)
233
+ request_params = {'MaxNumberOfDomains' => max_number_of_domains,
234
+ 'NextToken' => next_token}
235
+ link = generate_request("ListDomains", request_params)
236
+ result = request_info(link, QSdbListDomainParser.new)
237
+ # return result if no block given
238
+ return result unless block_given?
239
+ # loop if block if given
240
+ begin
241
+ # the block must return true if it wanna continue
242
+ break unless yield(result) && result[:next_token]
243
+ # make new request
244
+ request_params['NextToken'] = result[:next_token]
245
+ link = generate_request("ListDomains", request_params)
246
+ result = request_info(link, QSdbListDomainParser.new)
247
+ end while true
248
+ rescue Exception
249
+ on_exception
250
+ end
251
+
252
+ # Retrieve a list of SDB domains from Amazon.
253
+ #
254
+ # Returns a hash:
255
+ # { :domains => [domain1, ..., domainN],
256
+ # :next_token => string || nil,
257
+ # :box_usage => string,
258
+ # :request_id => string }
259
+ #
260
+ # Example:
261
+ #
262
+ # sdb = Aws::SdbInterface.new
263
+ # sdb.list_domains #=> { :box_usage => "0.0000071759",
264
+ # :request_id => "976709f9-0111-2345-92cb-9ce90acd0982",
265
+ # :domains => ["toys", "dolls"]}
266
+ #
267
+ # 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,
268
+ # list_domains will end.
269
+ #
270
+ # sdb.list_domains(10) do |result| # list by 10 domains per iteration
271
+ # puts result.inspect
272
+ # true
273
+ # end
274
+ #
275
+ # see: http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/SDB_API_ListDomains.html
276
+ #
277
+ def domain_metadata(domain_name)
278
+ link = generate_request("DomainMetadata", 'DomainName' => domain_name)
279
+ result = request_info(link, QSdbDomainMetadataParser.new)
280
+ return result
281
+ rescue Exception
282
+ on_exception
283
+ end
284
+
285
+
286
+ # Create new SDB domain at Amazon.
287
+ #
288
+ # Returns a hash: { :box_usage, :request_id } on success or an exception on error.
289
+ # (Amazon raises no errors if the domain already exists).
290
+ #
291
+ # Example:
292
+ #
293
+ # sdb = Aws::SdbInterface.new
294
+ # sdb.create_domain('toys') # => { :box_usage => "0.0000071759",
295
+ # :request_id => "976709f9-0111-2345-92cb-9ce90acd0982" }
296
+ #
297
+ # see: http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/SDB_API_CreateDomain.html
298
+ def create_domain(domain_name)
299
+ link = generate_request("CreateDomain",
300
+ 'DomainName' => domain_name)
301
+ request_info(link, QSdbSimpleParser.new)
302
+ rescue Exception
303
+ on_exception
304
+ end
305
+
306
+ # Delete SDB domain at Amazon.
307
+ #
308
+ # Returns a hash: { :box_usage, :request_id } on success or an exception on error.
309
+ # (Amazon raises no errors if the domain does not exist).
310
+ #
311
+ # Example:
312
+ #
313
+ # sdb = Aws::SdbInterface.new
314
+ # sdb.delete_domain('toys') # => { :box_usage => "0.0000071759",
315
+ # :request_id => "976709f9-0111-2345-92cb-9ce90acd0982" }
316
+ #
317
+ # see: http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/SDB_API_DeleteDomain.html
318
+ #
319
+ def delete_domain(domain_name)
320
+ link = generate_request("DeleteDomain",
321
+ 'DomainName' => domain_name)
322
+ request_info(link, QSdbSimpleParser.new)
323
+ rescue Exception
324
+ on_exception
325
+ end
326
+
327
+ # Add/Replace item attributes.
328
+ #
329
+ # Params:
330
+ # domain_name = DomainName
331
+ # item_name = ItemName
332
+ # attributes = {
333
+ # 'nameA' => [valueA1,..., valueAN],
334
+ # ...
335
+ # 'nameZ' => [valueZ1,..., valueZN]
336
+ # }
337
+ # replace = :replace | any other value to skip replacement
338
+ # options:
339
+ # :create_domain => If true and domain does not exist, it will be created. Default is false.
340
+ #
341
+ # Returns a hash: { :box_usage, :request_id } on success or an exception on error.
342
+ # (Amazon raises no errors if the attribute was not overridden, as when the :replace param is unset).
343
+ #
344
+ # Example:
345
+ #
346
+ # sdb = Aws::SdbInterface.new
347
+ # sdb.create_domain 'family'
348
+ #
349
+ # attributes = {}
350
+ # # create attributes for Jon and Silvia
351
+ # attributes['Jon'] = %w{ car beer }
352
+ # attributes['Silvia'] = %w{ beetle rolling_pin kids }
353
+ # sdb.put_attributes 'family', 'toys', attributes #=> ok
354
+ # # now: Jon=>[car, beer], Silvia=>[beetle, rolling_pin, kids]
355
+ #
356
+ # # add attributes to Jon
357
+ # attributes.delete('Silvia')
358
+ # attributes['Jon'] = %w{ girls pub }
359
+ # sdb.put_attributes 'family', 'toys', attributes #=> ok
360
+ # # now: Jon=>[car, beer, girls, pub], Silvia=>[beetle, rolling_pin, kids]
361
+ #
362
+ # # replace attributes for Jon and add to a cat (the cat had no attributes before)
363
+ # attributes['Jon'] = %w{ vacuum_cleaner hammer spade }
364
+ # attributes['cat'] = %w{ mouse clew Jons_socks }
365
+ # sdb.put_attributes 'family', 'toys', attributes, :replace #=> ok
366
+ # # now: Jon=>[vacuum_cleaner, hammer, spade], Silvia=>[beetle, rolling_pin, kids], cat=>[mouse, clew, Jons_socks]
367
+ #
368
+ # see: http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/SDB_API_PutAttributes.html
369
+ #
370
+ def put_attributes(domain_name, item_name, attributes, replace = false, options={})
371
+ params = {'DomainName' => domain_name,
372
+ 'ItemName' => item_name}.merge(pack_attributes(attributes, replace))
373
+ logger.debug 'PUT=' + params.inspect
374
+ link = generate_request("PutAttributes", params)
375
+ begin
376
+ request_info(link, QSdbSimpleParser.new, options)
377
+ rescue Aws::AwsError => ex
378
+ # puts "RESCUED in put_attributes: " + $!
379
+ if options[:create_domain] && create_domain_if_not_exist(ex, domain_name)
380
+ options.delete(:create_domain)
381
+ put_attributes(domain_name, item_name, attributes, replace, options)
382
+ else
383
+ raise ex
384
+ end
385
+ end
386
+ rescue Exception
387
+ on_exception
388
+ end
389
+
390
+ def create_domain_if_not_exist(ex, domain_name)
391
+ if ex.message().index("NoSuchDomain")
392
+ puts "Creating domain: #{domain_name}"
393
+ create_domain(domain_name)
394
+ return true
395
+ end
396
+ return false
397
+ end
398
+
399
+ #
400
+ # items is an array of Aws::SdbInterface::Item.new(o.id, o.attributes, true)
401
+ def batch_put_attributes(domain_name, items, options={})
402
+ params = {'DomainName' => domain_name}
403
+ i = 0
404
+ items.each do |item|
405
+ prefix = "Item." + i.to_s + "."
406
+ params[prefix + "ItemName"] = item.item_name
407
+ params.merge!(pack_attributes(item.attributes, item.replace, prefix))
408
+ i += 1
409
+ end
410
+ link = generate_request("BatchPutAttributes", params)
411
+ begin
412
+ request_info(link, QSdbSimpleParser.new, options)
413
+ rescue Aws::AwsError => ex
414
+ # puts "RESCUED in batch_put_attributes: " + $!
415
+ if options[:create_domain] && create_domain_if_not_exist(ex, domain_name)
416
+ options.delete(:create_domain)
417
+ batch_put_attributes(domain_name, items, options)
418
+ else
419
+ raise ex
420
+ end
421
+ end
422
+ rescue Exception
423
+ on_exception
424
+ end
425
+
426
+ #
427
+ # items is an array item_name's or Aws::SdbInterface::Item.new(o.id, o.attributes, true)
428
+ def batch_delete_attributes(domain_name, items)
429
+ params = {'DomainName' => domain_name}
430
+ i = 0
431
+ items.each do |item|
432
+ prefix = "Item." + i.to_s + "."
433
+ if item.is_a?(String)
434
+ params[prefix + "ItemName"] = item
435
+ else
436
+ params[prefix + "ItemName"] = item.item_name
437
+ params.merge!(pack_attributes(item.attributes, item.replace, prefix))
438
+ end
439
+ i += 1
440
+ end
441
+ link = generate_request("BatchDeleteAttributes", params)
442
+ request_info(link, QSdbSimpleParser.new)
443
+ rescue Exception
444
+ on_exception
445
+ end
446
+
447
+ # Retrieve SDB item's attribute(s).
448
+ #
449
+ # Returns a hash:
450
+ # { :box_usage => string,
451
+ # :request_id => string,
452
+ # :attributes => { 'nameA' => [valueA1,..., valueAN],
453
+ # ... ,
454
+ # 'nameZ' => [valueZ1,..., valueZN] } }
455
+ #
456
+ # Example:
457
+ # # request all attributes
458
+ # sdb.get_attributes('family', 'toys') # => { :attributes => {"cat" => ["clew", "Jons_socks", "mouse"] },
459
+ # "Silvia" => ["beetle", "rolling_pin", "kids"],
460
+ # "Jon" => ["vacuum_cleaner", "hammer", "spade"]},
461
+ # :box_usage => "0.0000093222",
462
+ # :request_id => "81273d21-000-1111-b3f9-512d91d29ac8" }
463
+ #
464
+ # # request cat's attributes only
465
+ # sdb.get_attributes('family', 'toys', 'cat') # => { :attributes => {"cat" => ["clew", "Jons_socks", "mouse"] },
466
+ # :box_usage => "0.0000093222",
467
+ # :request_id => "81273d21-001-1111-b3f9-512d91d29ac8" }
468
+ #
469
+ # see: http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/SDB_API_GetAttributes.html
470
+ #
471
+ def get_attributes(domain_name, item_name, attribute_name=nil, consistent_read = nil)
472
+ link = generate_request("GetAttributes", 'DomainName' => domain_name,
473
+ 'ItemName' => item_name,
474
+ 'AttributeName' => attribute_name,
475
+ 'ConsistentRead' => consistent_read)
476
+ res = request_info(link, QSdbGetAttributesParser.new)
477
+ res[:attributes].each_value do |values|
478
+ values.collect! { |e| sdb_to_ruby(e) }
479
+ end
480
+ res
481
+ rescue Exception
482
+ on_exception
483
+ end
484
+
485
+ # Delete value, attribute or item.
486
+ #
487
+ # Example:
488
+ # # delete 'vodka' and 'girls' from 'Jon' and 'mice' from 'cat'.
489
+ # sdb.delete_attributes 'family', 'toys', { 'Jon' => ['vodka', 'girls'], 'cat' => ['mice'] }
490
+ #
491
+ # # delete the all the values from attributes (i.e. delete the attributes)
492
+ # sdb.delete_attributes 'family', 'toys', { 'Jon' => [], 'cat' => [] }
493
+ # # or
494
+ # sdb.delete_attributes 'family', 'toys', [ 'Jon', 'cat' ]
495
+ #
496
+ # # delete all the attributes from item 'toys' (i.e. delete the item)
497
+ # sdb.delete_attributes 'family', 'toys'
498
+ #
499
+ # see http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/SDB_API_DeleteAttributes.html
500
+ #
501
+ def delete_attributes(domain_name, item_name, attributes = nil)
502
+ params = {'DomainName' => domain_name,
503
+ 'ItemName' => item_name}.merge(pack_attributes(attributes))
504
+ link = generate_request("DeleteAttributes", params)
505
+ request_info(link, QSdbSimpleParser.new)
506
+ rescue Exception
507
+ on_exception
508
+ end
509
+
510
+
511
+ # QUERY:
512
+
513
+ # Perform a query on SDB.
514
+ #
515
+ # Returns a hash:
516
+ # { :box_usage => string,
517
+ # :request_id => string,
518
+ # :next_token => string,
519
+ # :items => [ItemName1,..., ItemNameN] }
520
+ #
521
+ # Example:
522
+ #
523
+ # query = "['cat' = 'clew']"
524
+ # sdb.query('family', query) #=> hash of data
525
+ # sdb.query('family', query, 10) #=> hash of data with max of 10 items
526
+ #
527
+ # If a block is given, query will iteratively yield results to it as long as the block continues to return true.
528
+ #
529
+ # # List 10 items per iteration. Don't
530
+ # # forget to escape single quotes and backslashes and wrap all the items in single quotes.
531
+ # query = "['cat'='clew'] union ['dog'='Jon\\'s boot']"
532
+ # sdb.query('family', query, 10) do |result|
533
+ # puts result.inspect
534
+ # true
535
+ # end
536
+ #
537
+ # # Same query using automatic escaping...to use the auto escape, pass the query and its params as an array:
538
+ # query = [ "['cat'=?] union ['dog'=?]", "clew", "Jon's boot" ]
539
+ # sdb.query('family', query)
540
+ #
541
+ # query = [ "['cat'=?] union ['dog'=?] sort 'cat' desc", "clew", "Jon's boot" ]
542
+ # sdb.query('family', query)
543
+ #
544
+ # see: http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/SDB_API_Query.html
545
+ # http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/index.html?SortingData.html
546
+ #
547
+ def query(domain_name, query_expression = nil, max_number_of_items = nil, next_token = nil, consistent_read = nil)
548
+ query_expression = query_expression_from_array(query_expression) if query_expression.is_a?(Array)
549
+ @last_query_expression = query_expression
550
+ #
551
+ request_params = {'DomainName' => domain_name,
552
+ 'QueryExpression' => query_expression,
553
+ 'MaxNumberOfItems' => max_number_of_items,
554
+ 'NextToken' => next_token,
555
+ 'ConsistentRead' => consistent_read}
556
+ link = generate_request("Query", request_params)
557
+ result = request_info(link, QSdbQueryParser.new)
558
+ # return result if no block given
559
+ return result unless block_given?
560
+ # loop if block if given
561
+ begin
562
+ # the block must return true if it wanna continue
563
+ break unless yield(result) && result[:next_token]
564
+ # make new request
565
+ request_params['NextToken'] = result[:next_token]
566
+ link = generate_request("Query", request_params)
567
+ result = request_info(link, QSdbQueryParser.new)
568
+ end while true
569
+ rescue Exception
570
+ on_exception
571
+ end
572
+
573
+ # Perform a query and fetch specified attributes.
574
+ # If attributes are not specified then fetches the whole list of attributes.
575
+ #
576
+ #
577
+ # Returns a hash:
578
+ # { :box_usage => string,
579
+ # :request_id => string,
580
+ # :next_token => string,
581
+ # :items => [ { ItemName1 => { attribute1 => value1, ... attributeM => valueM } },
582
+ # { ItemName2 => {...}}, ... ]
583
+ #
584
+ # Example:
585
+ #
586
+ # sdb.query_with_attributes(domain, ['hobby', 'country'], "['gender'='female'] intersection ['name' starts-with ''] sort 'name'") #=>
587
+ # { :request_id => "06057228-70d0-4487-89fb-fd9c028580d3",
588
+ # :items =>
589
+ # [ { "035f1ba8-dbd8-11dd-80bd-001bfc466dd7"=>
590
+ # { "hobby" => ["cooking", "flowers", "cats"],
591
+ # "country" => ["Russia"]}},
592
+ # { "0327614a-dbd8-11dd-80bd-001bfc466dd7"=>
593
+ # { "hobby" => ["patchwork", "bundle jumping"],
594
+ # "country" => ["USA"]}}, ... ],
595
+ # :box_usage=>"0.0000504786"}
596
+ #
597
+ # sdb.query_with_attributes(domain, [], "['gender'='female'] intersection ['name' starts-with ''] sort 'name'") #=>
598
+ # { :request_id => "75bb19db-a529-4f69-b86f-5e3800f79a45",
599
+ # :items =>
600
+ # [ { "035f1ba8-dbd8-11dd-80bd-001bfc466dd7"=>
601
+ # { "hobby" => ["cooking", "flowers", "cats"],
602
+ # "name" => ["Mary"],
603
+ # "country" => ["Russia"],
604
+ # "gender" => ["female"],
605
+ # "id" => ["035f1ba8-dbd8-11dd-80bd-001bfc466dd7"]}},
606
+ # { "0327614a-dbd8-11dd-80bd-001bfc466dd7"=>
607
+ # { "hobby" => ["patchwork", "bundle jumping"],
608
+ # "name" => ["Mary"],
609
+ # "country" => ["USA"],
610
+ # "gender" => ["female"],
611
+ # "id" => ["0327614a-dbd8-11dd-80bd-001bfc466dd7"]}}, ... ],
612
+ # :box_usage=>"0.0000506668"}
613
+ #
614
+ # see: http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/index.html?SDB_API_QueryWithAttributes.html
615
+ #
616
+ def query_with_attributes(domain_name, attributes=[], query_expression = nil, max_number_of_items = nil, next_token = nil, consistent_read = nil)
617
+ attributes = attributes.to_a
618
+ query_expression = query_expression_from_array(query_expression) if query_expression.is_a?(Array)
619
+ @last_query_expression = query_expression
620
+ #
621
+ request_params = {'DomainName' => domain_name,
622
+ 'QueryExpression' => query_expression,
623
+ 'MaxNumberOfItems' => max_number_of_items,
624
+ 'NextToken' => next_token,
625
+ 'ConsistentRead' => consistent_read}
626
+ attributes.each_with_index do |attribute, idx|
627
+ request_params["AttributeName.#{idx+1}"] = attribute
628
+ end
629
+ link = generate_request("QueryWithAttributes", request_params)
630
+ result = select_response_to_ruby(request_info(link, QSdbQueryWithAttributesParser.new))
631
+ # return result if no block given
632
+ return result unless block_given?
633
+ # loop if block if given
634
+ begin
635
+ # the block must return true if it wanna continue
636
+ break unless yield(result) && result[:next_token]
637
+ # make new request
638
+ request_params['NextToken'] = result[:next_token]
639
+ link = generate_request("QueryWithAttributes", request_params)
640
+ result = select_response_to_ruby(request_info(link, QSdbQueryWithAttributesParser.new))
641
+ end while true
642
+ rescue Exception
643
+ on_exception
644
+ end
645
+
646
+ # Perform SQL-like select and fetch attributes.
647
+ # 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.
648
+ # (Use array to pass select_expression params to avoid manual escaping).
649
+ #
650
+ # sdb.select(["select * from my_domain where gender=?", 'female']) #=>
651
+ # {:request_id =>"8241b843-0fb9-4d66-9100-effae12249ec",
652
+ # :items =>
653
+ # [ { "035f1ba8-dbd8-11dd-80bd-001bfc466dd7"=>
654
+ # {"hobby" => ["cooking", "flowers", "cats"],
655
+ # "name" => ["Mary"],
656
+ # "country" => ["Russia"],
657
+ # "gender" => ["female"],
658
+ # "id" => ["035f1ba8-dbd8-11dd-80bd-001bfc466dd7"]}},
659
+ # { "0327614a-dbd8-11dd-80bd-001bfc466dd7"=>
660
+ # {"hobby" => ["patchwork", "bundle jumping"],
661
+ # "name" => ["Mary"],
662
+ # "country" => ["USA"],
663
+ # "gender" => ["female"],
664
+ # "id" => ["0327614a-dbd8-11dd-80bd-001bfc466dd7"]}}, ... ]
665
+ # :box_usage =>"0.0000506197"}
666
+ #
667
+ # sdb.select('select country, name from my_domain') #=>
668
+ # {:request_id=>"b1600198-c317-413f-a8dc-4e7f864a940a",
669
+ # :items=>
670
+ # [ { "035f1ba8-dbd8-11dd-80bd-001bfc466dd7"=> {"name"=>["Mary"], "country"=>["Russia"]} },
671
+ # { "376d2e00-75b0-11dd-9557-001bfc466dd7"=> {"name"=>["Putin"], "country"=>["Russia"]} },
672
+ # { "0327614a-dbd8-11dd-80bd-001bfc466dd7"=> {"name"=>["Mary"], "country"=>["USA"]} },
673
+ # { "372ebbd4-75b0-11dd-9557-001bfc466dd7"=> {"name"=>["Bush"], "country"=>["USA"]} },
674
+ # { "37a4e552-75b0-11dd-9557-001bfc466dd7"=> {"name"=>["Medvedev"], "country"=>["Russia"]} },
675
+ # { "38278dfe-75b0-11dd-9557-001bfc466dd7"=> {"name"=>["Mary"], "country"=>["Russia"]} },
676
+ # { "37df6c36-75b0-11dd-9557-001bfc466dd7"=> {"name"=>["Mary"], "country"=>["USA"]} } ],
677
+ # :box_usage=>"0.0000777663"}
678
+ #
679
+ # options:
680
+ # :next_token
681
+ # :consistent_read
682
+ # :retries => maximum number of times to retry this query on an error response.
683
+ #
684
+ # see: http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/index.html?SDB_API_Select.html
685
+ # http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/index.html?UsingSelect.html
686
+ # http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/index.html?SDBLimits.html
687
+ #
688
+ def select(select_expression, next_token = nil, consistent_read = nil)
689
+ select_expression = query_expression_from_array(select_expression) if select_expression.is_a?(Array)
690
+ @last_query_expression = select_expression
691
+
692
+ options = {}
693
+ if next_token.is_a?(Hash)
694
+ options = next_token
695
+ next_token = options[:next_token]
696
+ consistent_read = options[:consistent_read]
697
+ end
698
+
699
+ #
700
+ request_params = {'SelectExpression' => select_expression,
701
+ 'NextToken' => next_token,
702
+ 'ConsistentRead' => consistent_read}
703
+ link = generate_request("Select", request_params, options)
704
+ result = select_response_to_ruby(request_info(link, QSdbSelectParser.new, options))
705
+ return result unless block_given?
706
+ # loop if block if given
707
+ begin
708
+ # the block must return true if it wanna continue
709
+ break unless yield(result) && result[:next_token]
710
+ # make new request
711
+ request_params['NextToken'] = result[:next_token]
712
+ link = generate_request("Select", request_params)
713
+ result = select_response_to_ruby(request_info(link, QSdbSelectParser.new, options))
714
+ end while true
715
+ rescue Exception
716
+ on_exception
717
+ end
718
+
719
+ class Item
720
+ attr_accessor :item_name, :attributes, :replace
721
+
722
+ def initialize(item_name, attributes, replace = false)
723
+ @item_name = item_name
724
+ @attributes = attributes
725
+ @replace = replace
726
+ end
727
+ end
728
+
729
+ #-----------------------------------------------------------------
730
+ # PARSERS:
731
+ #-----------------------------------------------------------------
732
+ class QSdbListDomainParser < AwsParser #:nodoc:
733
+ def reset
734
+ @result = {:domains => []}
735
+ end
736
+
737
+ def tagend(name)
738
+ case name
739
+ when 'NextToken' then
740
+ @result[:next_token] = @text
741
+ when 'DomainName' then
742
+ @result[:domains] << @text
743
+ when 'BoxUsage' then
744
+ @result[:box_usage] = @text
745
+ when 'RequestId' then
746
+ @result[:request_id] = @text
747
+ end
748
+ end
749
+ end
750
+
751
+ class QSdbDomainMetadataParser < AwsParser #:nodoc:
752
+ def reset
753
+ @result = {}
754
+ end
755
+
756
+ def tagend(name)
757
+ case name
758
+ when 'Timestamp' then
759
+ @result[:timestamp] = @text
760
+ when 'ItemCount' then
761
+ @result[:item_count] = @text.to_i
762
+ when 'AttributeValueCount' then
763
+ @result[:attribute_value_count] = @text.to_i
764
+ when 'AttributeNameCount' then
765
+ @result[:attribute_name_acount] = @text.to_i
766
+ when 'ItemNamesSizeBytes' then
767
+ @result[:item_names_size_bytes] = @text.to_i
768
+ when 'AttributeValuesSizeBytes' then
769
+ @result[:attributes_values_size_bytes] = @text.to_i
770
+ when 'AttributeNamesSizeBytes' then
771
+ @result[:attributes_names_size_bytes] = @text.to_i
772
+
773
+ end
774
+ end
775
+ end
776
+
777
+
778
+ class QSdbSimpleParser < AwsParser #:nodoc:
779
+ def reset
780
+ @result = {}
781
+ end
782
+
783
+ def tagend(name)
784
+ case name
785
+ when 'BoxUsage' then
786
+ @result[:box_usage] = @text
787
+ when 'RequestId' then
788
+ @result[:request_id] = @text
789
+ end
790
+ end
791
+ end
792
+
793
+ class QSdbGetAttributesParser < AwsParser #:nodoc:
794
+ def reset
795
+ @last_attribute_name = nil
796
+ @result = {:attributes => {}}
797
+ end
798
+
799
+ def tagend(name)
800
+ case name
801
+ when 'Name' then
802
+ @last_attribute_name = @text
803
+ when 'Value' then
804
+ (@result[:attributes][@last_attribute_name] ||= []) << @text
805
+ when 'BoxUsage' then
806
+ @result[:box_usage] = @text
807
+ when 'RequestId' then
808
+ @result[:request_id] = @text
809
+ end
810
+ end
811
+ end
812
+
813
+ class QSdbQueryParser < AwsParser #:nodoc:
814
+ def reset
815
+ @result = {:items => []}
816
+ end
817
+
818
+ def tagend(name)
819
+ case name
820
+ when 'ItemName' then
821
+ @result[:items] << @text
822
+ when 'BoxUsage' then
823
+ @result[:box_usage] = @text
824
+ when 'RequestId' then
825
+ @result[:request_id] = @text
826
+ when 'NextToken' then
827
+ @result[:next_token] = @text
828
+ end
829
+ end
830
+ end
831
+
832
+ class QSdbQueryWithAttributesParser < AwsParser #:nodoc:
833
+ def reset
834
+ @result = {:items => []}
835
+ end
836
+
837
+ def tagend(name)
838
+ case name
839
+ when 'Name'
840
+ case @xmlpath
841
+ when 'QueryWithAttributesResponse/QueryWithAttributesResult/Item'
842
+ @item = @text
843
+ @result[:items] << {@item => {}}
844
+ when 'QueryWithAttributesResponse/QueryWithAttributesResult/Item/Attribute'
845
+ @attribute = @text
846
+ @result[:items].last[@item][@attribute] ||= []
847
+ end
848
+ when 'RequestId' then
849
+ @result[:request_id] = @text
850
+ when 'BoxUsage' then
851
+ @result[:box_usage] = @text
852
+ when 'NextToken' then
853
+ @result[:next_token] = @text
854
+ when 'Value' then
855
+ @result[:items].last[@item][@attribute] << @text
856
+ end
857
+ end
858
+ end
859
+
860
+ class QSdbSelectParser < AwsParser #:nodoc:
861
+ def reset
862
+ @result = {:items => []}
863
+ end
864
+
865
+ def tagend(name)
866
+ case name
867
+ when 'Name'
868
+ case @xmlpath
869
+ when 'SelectResponse/SelectResult/Item'
870
+ @item = @text
871
+ @result[:items] << {@item => {}}
872
+ when 'SelectResponse/SelectResult/Item/Attribute'
873
+ @attribute = @text
874
+ @result[:items].last[@item][@attribute] ||= []
875
+ end
876
+ when 'RequestId' then
877
+ @result[:request_id] = @text
878
+ when 'BoxUsage' then
879
+ @result[:box_usage] = @text
880
+ when 'NextToken' then
881
+ @result[:next_token] = @text
882
+ when 'Value' then
883
+ @result[:items].last[@item][@attribute] << @text
884
+ end
885
+ end
886
+ end
887
+
888
+ end
889
+
890
+ end