ahoward-helene 0.0.3

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 (72) hide show
  1. data/Rakefile +274 -0
  2. data/helene.gemspec +26 -0
  3. data/lib/helene.rb +113 -0
  4. data/lib/helene/attempt.rb +46 -0
  5. data/lib/helene/aws.rb +50 -0
  6. data/lib/helene/config.rb +147 -0
  7. data/lib/helene/content_type.rb +15 -0
  8. data/lib/helene/content_type.yml +661 -0
  9. data/lib/helene/error.rb +12 -0
  10. data/lib/helene/logging.rb +55 -0
  11. data/lib/helene/objectpool.rb +220 -0
  12. data/lib/helene/rails.rb +21 -0
  13. data/lib/helene/rightscale/acf/right_acf_interface.rb +379 -0
  14. data/lib/helene/rightscale/awsbase/benchmark_fix.rb +39 -0
  15. data/lib/helene/rightscale/awsbase/right_awsbase.rb +803 -0
  16. data/lib/helene/rightscale/awsbase/support.rb +111 -0
  17. data/lib/helene/rightscale/ec2/right_ec2.rb +1737 -0
  18. data/lib/helene/rightscale/net_fix.rb +160 -0
  19. data/lib/helene/rightscale/right_aws.rb +71 -0
  20. data/lib/helene/rightscale/right_http_connection.rb +507 -0
  21. data/lib/helene/rightscale/s3/right_s3.rb +1094 -0
  22. data/lib/helene/rightscale/s3/right_s3_interface.rb +1180 -0
  23. data/lib/helene/rightscale/sdb/active_sdb.rb +930 -0
  24. data/lib/helene/rightscale/sdb/right_sdb_interface.rb +696 -0
  25. data/lib/helene/rightscale/sqs/right_sqs.rb +388 -0
  26. data/lib/helene/rightscale/sqs/right_sqs_gen2.rb +286 -0
  27. data/lib/helene/rightscale/sqs/right_sqs_gen2_interface.rb +444 -0
  28. data/lib/helene/rightscale/sqs/right_sqs_interface.rb +596 -0
  29. data/lib/helene/s3.rb +34 -0
  30. data/lib/helene/s3/bucket.rb +379 -0
  31. data/lib/helene/s3/grantee.rb +134 -0
  32. data/lib/helene/s3/key.rb +162 -0
  33. data/lib/helene/s3/owner.rb +16 -0
  34. data/lib/helene/sdb.rb +9 -0
  35. data/lib/helene/sdb/base.rb +1204 -0
  36. data/lib/helene/sdb/base/associations.rb +481 -0
  37. data/lib/helene/sdb/base/attributes.rb +90 -0
  38. data/lib/helene/sdb/base/connection.rb +20 -0
  39. data/lib/helene/sdb/base/error.rb +20 -0
  40. data/lib/helene/sdb/base/hooks.rb +82 -0
  41. data/lib/helene/sdb/base/literal.rb +52 -0
  42. data/lib/helene/sdb/base/logging.rb +23 -0
  43. data/lib/helene/sdb/base/transactions.rb +53 -0
  44. data/lib/helene/sdb/base/type.rb +137 -0
  45. data/lib/helene/sdb/base/types.rb +123 -0
  46. data/lib/helene/sdb/base/validations.rb +256 -0
  47. data/lib/helene/sdb/cast.rb +114 -0
  48. data/lib/helene/sdb/connection.rb +36 -0
  49. data/lib/helene/sdb/error.rb +5 -0
  50. data/lib/helene/sdb/interface.rb +412 -0
  51. data/lib/helene/sdb/sentinel.rb +15 -0
  52. data/lib/helene/sleepcycle.rb +29 -0
  53. data/lib/helene/superhash.rb +297 -0
  54. data/lib/helene/util.rb +132 -0
  55. data/test/auth.rb +31 -0
  56. data/test/helper.rb +98 -0
  57. data/test/integration/begin.rb +0 -0
  58. data/test/integration/ensure.rb +8 -0
  59. data/test/integration/s3/bucket.rb +106 -0
  60. data/test/integration/sdb/associations.rb +45 -0
  61. data/test/integration/sdb/creating.rb +13 -0
  62. data/test/integration/sdb/emptiness.rb +56 -0
  63. data/test/integration/sdb/hooks.rb +19 -0
  64. data/test/integration/sdb/limits.rb +27 -0
  65. data/test/integration/sdb/saving.rb +21 -0
  66. data/test/integration/sdb/selecting.rb +39 -0
  67. data/test/integration/sdb/types.rb +31 -0
  68. data/test/integration/sdb/validations.rb +60 -0
  69. data/test/integration/setup.rb +27 -0
  70. data/test/integration/teardown.rb +21 -0
  71. data/test/loader.rb +39 -0
  72. metadata +139 -0
@@ -0,0 +1,696 @@
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 RightAws
25
+
26
+ class SdbInterface < RightAwsBase
27
+
28
+ include RightAwsBaseInterface
29
+
30
+ DEFAULT_HOST = 'sdb.amazonaws.com'
31
+ DEFAULT_PORT = 443
32
+ DEFAULT_PROTOCOL = 'https'
33
+ API_VERSION = '2007-11-07'
34
+ DEFAULT_NIL_REPRESENTATION = 'nil'
35
+
36
+ @@bench = AwsBenchmarkingBlock.new
37
+ def self.bench_xml; @@bench.xml; end
38
+ def self.bench_sdb; @@bench.service; end
39
+
40
+ attr_reader :last_query_expression
41
+
42
+ # Creates new RightSdb instance.
43
+ #
44
+ # Params:
45
+ # { :server => 'sdb.amazonaws.com' # Amazon service host: 'sdb.amazonaws.com'(default)
46
+ # :port => 443 # Amazon service port: 80 or 443(default)
47
+ # :protocol => 'https' # Amazon service protocol: 'http' or 'https'(default)
48
+ # :signature_version => '0' # The signature version : '0' or '1'(default)
49
+ # :multi_thread => true|false # Multi-threaded (connection per each thread): true or false(default)
50
+ # :logger => Logger Object # Logger instance: logs to STDOUT if omitted
51
+ # :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')
52
+ #
53
+ # Example:
54
+ #
55
+ # sdb = RightAws::SdbInterface.new('1E3GDYEOGFJPIT7XXXXXX','hgTHt68JY07JKUY08ftHYtERkjgtfERn57XXXXXX', {:multi_thread => true, :logger => Logger.new('/tmp/x.log')}) #=> #<RightSdb:0xa6b8c27c>
56
+ #
57
+ # see: http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/
58
+ #
59
+ def initialize(aws_access_key_id=nil, aws_secret_access_key=nil, params={})
60
+ @nil_rep = params[:nil_representation] ? params[:nil_representation] : DEFAULT_NIL_REPRESENTATION
61
+ params.delete(:nil_representation)
62
+ init({ :name => 'SDB',
63
+ :default_host => ENV['SDB_URL'] ? URI.parse(ENV['SDB_URL']).host : DEFAULT_HOST,
64
+ :default_port => ENV['SDB_URL'] ? URI.parse(ENV['SDB_URL']).port : DEFAULT_PORT,
65
+ :default_protocol => ENV['SDB_URL'] ? URI.parse(ENV['SDB_URL']).scheme : DEFAULT_PROTOCOL },
66
+ aws_access_key_id || ENV['AWS_ACCESS_KEY_ID'],
67
+ aws_secret_access_key || ENV['AWS_SECRET_ACCESS_KEY'],
68
+ params)
69
+ end
70
+
71
+ #-----------------------------------------------------------------
72
+ # Requests
73
+ #-----------------------------------------------------------------
74
+ def generate_request(action, params={}) #:nodoc:
75
+ # remove empty params from request
76
+ params.delete_if {|key,value| value.nil? }
77
+ #params_string = params.to_a.collect{|key,val| key + "=#{CGI::escape(val.to_s)}" }.join("&")
78
+ # prepare service data
79
+ service = '/'
80
+ service_hash = {"Action" => action,
81
+ "AWSAccessKeyId" => @aws_access_key_id,
82
+ "Version" => API_VERSION }
83
+ service_hash.update(params)
84
+ service_params = signed_service_params(@aws_secret_access_key, service_hash, :get, @params[:server], service)
85
+ #
86
+ # use POST method if the length of the query string is too large
87
+ # see http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/MakingRESTRequests.html
88
+ if service_params.size > 2000
89
+ if signature_version == '2'
90
+ # resign the request because HTTP verb is included into signature
91
+ service_params = signed_service_params(@aws_secret_access_key, service_hash, :post, @params[:server], service)
92
+ end
93
+ request = Net::HTTP::Post.new(service)
94
+ request.body = service_params
95
+ request['Content-Type'] = 'application/x-www-form-urlencoded'
96
+ else
97
+ request = Net::HTTP::Get.new("#{service}?#{service_params}")
98
+ end
99
+ # prepare output hash
100
+ { :request => request,
101
+ :server => @params[:server],
102
+ :port => @params[:port],
103
+ :protocol => @params[:protocol] }
104
+ end
105
+
106
+ # Sends request to Amazon and parses the response
107
+ # Raises AwsError if any banana happened
108
+ def request_info(request, parser) #:nodoc:
109
+ thread = @params[:multi_thread] ? Thread.current : Thread.main
110
+ thread[:sdb_connection] ||= Rightscale::HttpConnection.new(:exception => AwsError, :logger => @logger)
111
+ request_info_impl(thread[:sdb_connection], @@bench, request, parser)
112
+ end
113
+
114
+ # Prepare attributes for putting.
115
+ # (used by put_attributes)
116
+ def pack_attributes(attributes, replace = false) #:nodoc:
117
+ result = {}
118
+ if attributes
119
+ idx = 0
120
+ skip_values = attributes.is_a?(Array)
121
+ attributes.each do |attribute, values|
122
+ # set replacement attribute
123
+ result["Attribute.#{idx}.Replace"] = 'true' if replace
124
+ # pack Name/Value
125
+ unless values.nil?
126
+ Array(values).each do |value|
127
+ result["Attribute.#{idx}.Name"] = attribute
128
+ result["Attribute.#{idx}.Value"] = ruby_to_sdb(value) unless skip_values
129
+ idx += 1
130
+ end
131
+ else
132
+ result["Attribute.#{idx}.Name"] = attribute
133
+ result["Attribute.#{idx}.Value"] = ruby_to_sdb(nil) unless skip_values
134
+ idx += 1
135
+ end
136
+ end
137
+ end
138
+ result
139
+ end
140
+
141
+ # Use this helper to manually escape the fields in the query expressions.
142
+ # To escape the single quotes and backslashes and to wrap the string into the single quotes.
143
+ #
144
+ # see: http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/SDB_API.html
145
+ #
146
+ def escape(value)
147
+ %Q{'#{value.to_s.gsub(/(['\\])/){ "\\#{$1}" }}'} if value
148
+ end
149
+
150
+ # Convert a Ruby language value to a SDB value by replacing Ruby nil with the user's chosen string representation of nil.
151
+ # Non-nil values are unaffected by this filter.
152
+ def ruby_to_sdb(value)
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 params.blank?
180
+ query = params.shift.to_s
181
+ query.gsub(/(\\)?(\?)/) do
182
+ if $1 # if escaped '\?' is found - replace it by '?' without backslash
183
+ "?"
184
+ else # well, if no backslash precedes '?' then replace it by next param from the list
185
+ escape(params.shift)
186
+ end
187
+ end
188
+ end
189
+
190
+ def query_expression_from_hash(hash)
191
+ return '' if hash.blank?
192
+ expression = []
193
+ hash.each do |key, value|
194
+ expression << "#{key}=#{escape(value)}"
195
+ end
196
+ expression.join(' AND ')
197
+ end
198
+
199
+ # Retrieve a list of SDB domains from Amazon.
200
+ #
201
+ # Returns a hash:
202
+ # { :domains => [domain1, ..., domainN],
203
+ # :next_token => string || nil,
204
+ # :box_usage => string,
205
+ # :request_id => string }
206
+ #
207
+ # Example:
208
+ #
209
+ # sdb = RightAws::SdbInterface.new
210
+ # sdb.list_domains #=> { :box_usage => "0.0000071759",
211
+ # :request_id => "976709f9-0111-2345-92cb-9ce90acd0982",
212
+ # :domains => ["toys", "dolls"]}
213
+ #
214
+ # 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,
215
+ # list_domains will end.
216
+ #
217
+ # sdb.list_domains(10) do |result| # list by 10 domains per iteration
218
+ # puts result.inspect
219
+ # true
220
+ # end
221
+ #
222
+ # see: http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/SDB_API_ListDomains.html
223
+ #
224
+ def list_domains(max_number_of_domains = nil, next_token = nil )
225
+ request_params = { 'MaxNumberOfDomains' => max_number_of_domains,
226
+ 'NextToken' => next_token }
227
+ link = generate_request("ListDomains", request_params)
228
+ result = request_info(link, QSdbListDomainParser.new)
229
+ # return result if no block given
230
+ return result unless block_given?
231
+ # loop if block if given
232
+ begin
233
+ # the block must return true if it wanna continue
234
+ break unless yield(result) && result[:next_token]
235
+ # make new request
236
+ request_params['NextToken'] = result[:next_token]
237
+ link = generate_request("ListDomains", request_params)
238
+ result = request_info(link, QSdbListDomainParser.new)
239
+ end while true
240
+ rescue Exception
241
+ on_exception
242
+ end
243
+
244
+ # Create new SDB domain at Amazon.
245
+ #
246
+ # Returns a hash: { :box_usage, :request_id } on success or an exception on error.
247
+ # (Amazon raises no errors if the domain already exists).
248
+ #
249
+ # Example:
250
+ #
251
+ # sdb = RightAws::SdbInterface.new
252
+ # sdb.create_domain('toys') # => { :box_usage => "0.0000071759",
253
+ # :request_id => "976709f9-0111-2345-92cb-9ce90acd0982" }
254
+ #
255
+ # see: http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/SDB_API_CreateDomain.html
256
+ def create_domain(domain_name)
257
+ link = generate_request("CreateDomain",
258
+ 'DomainName' => domain_name)
259
+ request_info(link, QSdbSimpleParser.new)
260
+ rescue Exception
261
+ on_exception
262
+ end
263
+
264
+ # Delete SDB domain at Amazon.
265
+ #
266
+ # Returns a hash: { :box_usage, :request_id } on success or an exception on error.
267
+ # (Amazon raises no errors if the domain does not exist).
268
+ #
269
+ # Example:
270
+ #
271
+ # sdb = RightAws::SdbInterface.new
272
+ # sdb.delete_domain('toys') # => { :box_usage => "0.0000071759",
273
+ # :request_id => "976709f9-0111-2345-92cb-9ce90acd0982" }
274
+ #
275
+ # see: http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/SDB_API_DeleteDomain.html
276
+ #
277
+ def delete_domain(domain_name)
278
+ link = generate_request("DeleteDomain",
279
+ 'DomainName' => domain_name)
280
+ request_info(link, QSdbSimpleParser.new)
281
+ rescue Exception
282
+ on_exception
283
+ end
284
+
285
+ # Add/Replace item attributes.
286
+ #
287
+ # Params:
288
+ # domain_name = DomainName
289
+ # item_name = ItemName
290
+ # attributes = {
291
+ # 'nameA' => [valueA1,..., valueAN],
292
+ # ...
293
+ # 'nameZ' => [valueZ1,..., valueZN]
294
+ # }
295
+ # replace = :replace | any other value to skip replacement
296
+ #
297
+ # Returns a hash: { :box_usage, :request_id } on success or an exception on error.
298
+ # (Amazon raises no errors if the attribute was not overridden, as when the :replace param is unset).
299
+ #
300
+ # Example:
301
+ #
302
+ # sdb = RightAws::SdbInterface.new
303
+ # sdb.create_domain 'family'
304
+ #
305
+ # attributes = {}
306
+ # # create attributes for Jon and Silvia
307
+ # attributes['Jon'] = %w{ car beer }
308
+ # attributes['Silvia'] = %w{ beetle rolling_pin kids }
309
+ # sdb.put_attributes 'family', 'toys', attributes #=> ok
310
+ # # now: Jon=>[car, beer], Silvia=>[beetle, rolling_pin, kids]
311
+ #
312
+ # # add attributes to Jon
313
+ # attributes.delete('Silvia')
314
+ # attributes['Jon'] = %w{ girls pub }
315
+ # sdb.put_attributes 'family', 'toys', attributes #=> ok
316
+ # # now: Jon=>[car, beer, girls, pub], Silvia=>[beetle, rolling_pin, kids]
317
+ #
318
+ # # replace attributes for Jon and add to a cat (the cat had no attributes before)
319
+ # attributes['Jon'] = %w{ vacuum_cleaner hammer spade }
320
+ # attributes['cat'] = %w{ mouse clew Jons_socks }
321
+ # sdb.put_attributes 'family', 'toys', attributes, :replace #=> ok
322
+ # # now: Jon=>[vacuum_cleaner, hammer, spade], Silvia=>[beetle, rolling_pin, kids], cat=>[mouse, clew, Jons_socks]
323
+ #
324
+ # see: http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/SDB_API_PutAttributes.html
325
+ #
326
+ def put_attributes(domain_name, item_name, attributes, replace = false)
327
+ params = { 'DomainName' => domain_name,
328
+ 'ItemName' => item_name }.merge(pack_attributes(attributes, replace))
329
+ link = generate_request("PutAttributes", params)
330
+ request_info( link, QSdbSimpleParser.new )
331
+ rescue Exception
332
+ on_exception
333
+ end
334
+
335
+ # Retrieve SDB item's attribute(s).
336
+ #
337
+ # Returns a hash:
338
+ # { :box_usage => string,
339
+ # :request_id => string,
340
+ # :attributes => { 'nameA' => [valueA1,..., valueAN],
341
+ # ... ,
342
+ # 'nameZ' => [valueZ1,..., valueZN] } }
343
+ #
344
+ # Example:
345
+ # # request all attributes
346
+ # sdb.get_attributes('family', 'toys') # => { :attributes => {"cat" => ["clew", "Jons_socks", "mouse"] },
347
+ # "Silvia" => ["beetle", "rolling_pin", "kids"],
348
+ # "Jon" => ["vacuum_cleaner", "hammer", "spade"]},
349
+ # :box_usage => "0.0000093222",
350
+ # :request_id => "81273d21-000-1111-b3f9-512d91d29ac8" }
351
+ #
352
+ # # request cat's attributes only
353
+ # sdb.get_attributes('family', 'toys', 'cat') # => { :attributes => {"cat" => ["clew", "Jons_socks", "mouse"] },
354
+ # :box_usage => "0.0000093222",
355
+ # :request_id => "81273d21-001-1111-b3f9-512d91d29ac8" }
356
+ #
357
+ # see: http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/SDB_API_GetAttributes.html
358
+ #
359
+ def get_attributes(domain_name, item_name, attribute_name=nil)
360
+ link = generate_request("GetAttributes", 'DomainName' => domain_name,
361
+ 'ItemName' => item_name,
362
+ 'AttributeName' => attribute_name )
363
+ res = request_info(link, QSdbGetAttributesParser.new)
364
+ res[:attributes].each_value do |values|
365
+ values.collect! { |e| sdb_to_ruby(e) }
366
+ end
367
+ res
368
+ rescue Exception
369
+ on_exception
370
+ end
371
+
372
+ # Delete value, attribute or item.
373
+ #
374
+ # Example:
375
+ # # delete 'vodka' and 'girls' from 'Jon' and 'mice' from 'cat'.
376
+ # sdb.delete_attributes 'family', 'toys', { 'Jon' => ['vodka', 'girls'], 'cat' => ['mice'] }
377
+ #
378
+ # # delete the all the values from attributes (i.e. delete the attributes)
379
+ # sdb.delete_attributes 'family', 'toys', { 'Jon' => [], 'cat' => [] }
380
+ # # or
381
+ # sdb.delete_attributes 'family', 'toys', [ 'Jon', 'cat' ]
382
+ #
383
+ # # delete all the attributes from item 'toys' (i.e. delete the item)
384
+ # sdb.delete_attributes 'family', 'toys'
385
+ #
386
+ # see http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/SDB_API_DeleteAttributes.html
387
+ #
388
+ def delete_attributes(domain_name, item_name, attributes = nil)
389
+ params = { 'DomainName' => domain_name,
390
+ 'ItemName' => item_name }.merge(pack_attributes(attributes))
391
+ link = generate_request("DeleteAttributes", params)
392
+ request_info( link, QSdbSimpleParser.new )
393
+ rescue Exception
394
+ on_exception
395
+ end
396
+
397
+
398
+ # QUERY:
399
+
400
+ # Perform a query on SDB.
401
+ #
402
+ # Returns a hash:
403
+ # { :box_usage => string,
404
+ # :request_id => string,
405
+ # :next_token => string,
406
+ # :items => [ItemName1,..., ItemNameN] }
407
+ #
408
+ # Example:
409
+ #
410
+ # query = "['cat' = 'clew']"
411
+ # sdb.query('family', query) #=> hash of data
412
+ # sdb.query('family', query, 10) #=> hash of data with max of 10 items
413
+ #
414
+ # If a block is given, query will iteratively yield results to it as long as the block continues to return true.
415
+ #
416
+ # # List 10 items per iteration. Don't
417
+ # # forget to escape single quotes and backslashes and wrap all the items in single quotes.
418
+ # query = "['cat'='clew'] union ['dog'='Jon\\'s boot']"
419
+ # sdb.query('family', query, 10) do |result|
420
+ # puts result.inspect
421
+ # true
422
+ # end
423
+ #
424
+ # # Same query using automatic escaping...to use the auto escape, pass the query and its params as an array:
425
+ # query = [ "['cat'=?] union ['dog'=?]", "clew", "Jon's boot" ]
426
+ # sdb.query('family', query)
427
+ #
428
+ # query = [ "['cat'=?] union ['dog'=?] sort 'cat' desc", "clew", "Jon's boot" ]
429
+ # sdb.query('family', query)
430
+ #
431
+ # see: http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/SDB_API_Query.html
432
+ # http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/index.html?SortingData.html
433
+ #
434
+ def query(domain_name, query_expression = nil, max_number_of_items = nil, next_token = nil)
435
+ query_expression = query_expression_from_array(query_expression) if query_expression.is_a?(Array)
436
+ @last_query_expression = query_expression
437
+ #
438
+ request_params = { 'DomainName' => domain_name,
439
+ 'QueryExpression' => query_expression,
440
+ 'MaxNumberOfItems' => max_number_of_items,
441
+ 'NextToken' => next_token }
442
+ link = generate_request("Query", request_params)
443
+ result = request_info( link, QSdbQueryParser.new )
444
+ # return result if no block given
445
+ return result unless block_given?
446
+ # loop if block if given
447
+ begin
448
+ # the block must return true if it wanna continue
449
+ break unless yield(result) && result[:next_token]
450
+ # make new request
451
+ request_params['NextToken'] = result[:next_token]
452
+ link = generate_request("Query", request_params)
453
+ result = request_info( link, QSdbQueryParser.new )
454
+ end while true
455
+ rescue Exception
456
+ on_exception
457
+ end
458
+
459
+ # Perform a query and fetch specified attributes.
460
+ # If attributes are not specified then fetches the whole list of attributes.
461
+ #
462
+ #
463
+ # Returns a hash:
464
+ # { :box_usage => string,
465
+ # :request_id => string,
466
+ # :next_token => string,
467
+ # :items => [ { ItemName1 => { attribute1 => value1, ... attributeM => valueM } },
468
+ # { ItemName2 => {...}}, ... ]
469
+ #
470
+ # Example:
471
+ #
472
+ # sdb.query_with_attributes(domain, ['hobby', 'country'], "['gender'='female'] intersection ['name' starts-with ''] sort 'name'") #=>
473
+ # { :request_id => "06057228-70d0-4487-89fb-fd9c028580d3",
474
+ # :items =>
475
+ # [ { "035f1ba8-dbd8-11dd-80bd-001bfc466dd7"=>
476
+ # { "hobby" => ["cooking", "flowers", "cats"],
477
+ # "country" => ["Russia"]}},
478
+ # { "0327614a-dbd8-11dd-80bd-001bfc466dd7"=>
479
+ # { "hobby" => ["patchwork", "bundle jumping"],
480
+ # "country" => ["USA"]}}, ... ],
481
+ # :box_usage=>"0.0000504786"}
482
+ #
483
+ # sdb.query_with_attributes(domain, [], "['gender'='female'] intersection ['name' starts-with ''] sort 'name'") #=>
484
+ # { :request_id => "75bb19db-a529-4f69-b86f-5e3800f79a45",
485
+ # :items =>
486
+ # [ { "035f1ba8-dbd8-11dd-80bd-001bfc466dd7"=>
487
+ # { "hobby" => ["cooking", "flowers", "cats"],
488
+ # "name" => ["Mary"],
489
+ # "country" => ["Russia"],
490
+ # "gender" => ["female"],
491
+ # "id" => ["035f1ba8-dbd8-11dd-80bd-001bfc466dd7"]}},
492
+ # { "0327614a-dbd8-11dd-80bd-001bfc466dd7"=>
493
+ # { "hobby" => ["patchwork", "bundle jumping"],
494
+ # "name" => ["Mary"],
495
+ # "country" => ["USA"],
496
+ # "gender" => ["female"],
497
+ # "id" => ["0327614a-dbd8-11dd-80bd-001bfc466dd7"]}}, ... ],
498
+ # :box_usage=>"0.0000506668"}
499
+ #
500
+ # see: http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/index.html?SDB_API_QueryWithAttributes.html
501
+ #
502
+ def query_with_attributes(domain_name, attributes=[], query_expression = nil, max_number_of_items = nil, next_token = nil)
503
+ attributes = attributes.to_a
504
+ query_expression = query_expression_from_array(query_expression) if query_expression.is_a?(Array)
505
+ @last_query_expression = query_expression
506
+ #
507
+ request_params = { 'DomainName' => domain_name,
508
+ 'QueryExpression' => query_expression,
509
+ 'MaxNumberOfItems' => max_number_of_items,
510
+ 'NextToken' => next_token }
511
+ attributes.each_with_index do |attribute, idx|
512
+ request_params["AttributeName.#{idx+1}"] = attribute
513
+ end
514
+ link = generate_request("QueryWithAttributes", request_params)
515
+ result = select_response_to_ruby(request_info( link, QSdbQueryWithAttributesParser.new ))
516
+ # return result if no block given
517
+ return result unless block_given?
518
+ # loop if block if given
519
+ begin
520
+ # the block must return true if it wanna continue
521
+ break unless yield(result) && result[:next_token]
522
+ # make new request
523
+ request_params['NextToken'] = result[:next_token]
524
+ link = generate_request("QueryWithAttributes", request_params)
525
+ result = select_response_to_ruby(request_info( link, QSdbQueryWithAttributesParser.new ))
526
+ end while true
527
+ rescue Exception
528
+ on_exception
529
+ end
530
+
531
+ # Perform SQL-like select and fetch attributes.
532
+ # 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.
533
+ # (Use array to pass select_expression params to avoid manual escaping).
534
+ #
535
+ # sdb.select(["select * from my_domain where gender=?", 'female']) #=>
536
+ # {:request_id =>"8241b843-0fb9-4d66-9100-effae12249ec",
537
+ # :items =>
538
+ # [ { "035f1ba8-dbd8-11dd-80bd-001bfc466dd7"=>
539
+ # {"hobby" => ["cooking", "flowers", "cats"],
540
+ # "name" => ["Mary"],
541
+ # "country" => ["Russia"],
542
+ # "gender" => ["female"],
543
+ # "id" => ["035f1ba8-dbd8-11dd-80bd-001bfc466dd7"]}},
544
+ # { "0327614a-dbd8-11dd-80bd-001bfc466dd7"=>
545
+ # {"hobby" => ["patchwork", "bundle jumping"],
546
+ # "name" => ["Mary"],
547
+ # "country" => ["USA"],
548
+ # "gender" => ["female"],
549
+ # "id" => ["0327614a-dbd8-11dd-80bd-001bfc466dd7"]}}, ... ]
550
+ # :box_usage =>"0.0000506197"}
551
+ #
552
+ # sdb.select('select country, name from my_domain') #=>
553
+ # {:request_id=>"b1600198-c317-413f-a8dc-4e7f864a940a",
554
+ # :items=>
555
+ # [ { "035f1ba8-dbd8-11dd-80bd-001bfc466dd7"=> {"name"=>["Mary"], "country"=>["Russia"]} },
556
+ # { "376d2e00-75b0-11dd-9557-001bfc466dd7"=> {"name"=>["Putin"], "country"=>["Russia"]} },
557
+ # { "0327614a-dbd8-11dd-80bd-001bfc466dd7"=> {"name"=>["Mary"], "country"=>["USA"]} },
558
+ # { "372ebbd4-75b0-11dd-9557-001bfc466dd7"=> {"name"=>["Bush"], "country"=>["USA"]} },
559
+ # { "37a4e552-75b0-11dd-9557-001bfc466dd7"=> {"name"=>["Medvedev"], "country"=>["Russia"]} },
560
+ # { "38278dfe-75b0-11dd-9557-001bfc466dd7"=> {"name"=>["Mary"], "country"=>["Russia"]} },
561
+ # { "37df6c36-75b0-11dd-9557-001bfc466dd7"=> {"name"=>["Mary"], "country"=>["USA"]} } ],
562
+ # :box_usage=>"0.0000777663"}
563
+ #
564
+ # see: http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/index.html?SDB_API_Select.html
565
+ # http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/index.html?UsingSelect.html
566
+ # http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/index.html?SDBLimits.html
567
+ #
568
+ def select(select_expression, next_token = nil)
569
+ select_expression = query_expression_from_array(select_expression) if select_expression.is_a?(Array)
570
+ @last_query_expression = select_expression
571
+ #
572
+ request_params = { 'SelectExpression' => select_expression,
573
+ 'NextToken' => next_token }
574
+ link = generate_request("Select", request_params)
575
+ result = select_response_to_ruby(request_info( link, QSdbSelectParser.new ))
576
+ return result unless block_given?
577
+ # loop if block if given
578
+ begin
579
+ # the block must return true if it wanna continue
580
+ break unless yield(result) && result[:next_token]
581
+ # make new request
582
+ request_params['NextToken'] = result[:next_token]
583
+ link = generate_request("Select", request_params)
584
+ result = select_response_to_ruby(request_info( link, QSdbSelectParser.new ))
585
+ end while true
586
+ rescue Exception
587
+ on_exception
588
+ end
589
+
590
+ #-----------------------------------------------------------------
591
+ # PARSERS:
592
+ #-----------------------------------------------------------------
593
+ class QSdbListDomainParser < RightAWSParser #:nodoc:
594
+ def reset
595
+ @result = { :domains => [] }
596
+ end
597
+ def tagend(name)
598
+ case name
599
+ when 'NextToken' then @result[:next_token] = @text
600
+ when 'DomainName' then @result[:domains] << @text
601
+ when 'BoxUsage' then @result[:box_usage] = @text
602
+ when 'RequestId' then @result[:request_id] = @text
603
+ end
604
+ end
605
+ end
606
+
607
+ class QSdbSimpleParser < RightAWSParser #:nodoc:
608
+ def reset
609
+ @result = {}
610
+ end
611
+ def tagend(name)
612
+ case name
613
+ when 'BoxUsage' then @result[:box_usage] = @text
614
+ when 'RequestId' then @result[:request_id] = @text
615
+ end
616
+ end
617
+ end
618
+
619
+ class QSdbGetAttributesParser < RightAWSParser #:nodoc:
620
+ def reset
621
+ @last_attribute_name = nil
622
+ @result = { :attributes => {} }
623
+ end
624
+ def tagend(name)
625
+ case name
626
+ when 'Name' then @last_attribute_name = @text
627
+ when 'Value' then (@result[:attributes][@last_attribute_name] ||= []) << @text
628
+ when 'BoxUsage' then @result[:box_usage] = @text
629
+ when 'RequestId' then @result[:request_id] = @text
630
+ end
631
+ end
632
+ end
633
+
634
+ class QSdbQueryParser < RightAWSParser #:nodoc:
635
+ def reset
636
+ @result = { :items => [] }
637
+ end
638
+ def tagend(name)
639
+ case name
640
+ when 'ItemName' then @result[:items] << @text
641
+ when 'BoxUsage' then @result[:box_usage] = @text
642
+ when 'RequestId' then @result[:request_id] = @text
643
+ when 'NextToken' then @result[:next_token] = @text
644
+ end
645
+ end
646
+ end
647
+
648
+ class QSdbQueryWithAttributesParser < RightAWSParser #:nodoc:
649
+ def reset
650
+ @result = { :items => [] }
651
+ end
652
+ def tagend(name)
653
+ case name
654
+ when 'Name'
655
+ case @xmlpath
656
+ when 'QueryWithAttributesResponse/QueryWithAttributesResult/Item'
657
+ @item = @text
658
+ @result[:items] << { @item => {} }
659
+ when 'QueryWithAttributesResponse/QueryWithAttributesResult/Item/Attribute'
660
+ @attribute = @text
661
+ @result[:items].last[@item][@attribute] ||= []
662
+ end
663
+ when 'RequestId' then @result[:request_id] = @text
664
+ when 'BoxUsage' then @result[:box_usage] = @text
665
+ when 'NextToken' then @result[:next_token] = @text
666
+ when 'Value' then @result[:items].last[@item][@attribute] << @text
667
+ end
668
+ end
669
+ end
670
+
671
+ class QSdbSelectParser < RightAWSParser #:nodoc:
672
+ def reset
673
+ @result = { :items => [] }
674
+ end
675
+ def tagend(name)
676
+ case name
677
+ when 'Name'
678
+ case @xmlpath
679
+ when 'SelectResponse/SelectResult/Item'
680
+ @item = @text
681
+ @result[:items] << { @item => {} }
682
+ when 'SelectResponse/SelectResult/Item/Attribute'
683
+ @attribute = @text
684
+ @result[:items].last[@item][@attribute] ||= []
685
+ end
686
+ when 'RequestId' then @result[:request_id] = @text
687
+ when 'BoxUsage' then @result[:box_usage] = @text
688
+ when 'NextToken' then @result[:next_token] = @text
689
+ when 'Value' then @result[:items].last[@item][@attribute] << @text
690
+ end
691
+ end
692
+ end
693
+
694
+ end
695
+
696
+ end