aws-sdk 1.1.4 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. data/lib/aws.rb +2 -0
  2. data/lib/aws/api_config/ELB-2011-08-15.yml +380 -0
  3. data/lib/aws/api_config/SNS-2010-03-31.yml +2 -2
  4. data/lib/aws/api_config/SimpleEmailService-2010-12-01.yml +5 -5
  5. data/lib/aws/core.rb +18 -3
  6. data/lib/aws/core/client_logging.rb +5 -6
  7. data/lib/aws/core/collection.rb +241 -0
  8. data/lib/aws/core/collection/batchable.rb +133 -0
  9. data/lib/aws/core/collection/limitable.rb +92 -0
  10. data/lib/aws/core/collection/simple.rb +89 -0
  11. data/lib/aws/core/configuration.rb +23 -0
  12. data/lib/aws/core/option_grammar.rb +2 -0
  13. data/lib/aws/core/page_result.rb +73 -0
  14. data/lib/aws/ec2/security_group.rb +154 -89
  15. data/lib/aws/ec2/security_group/egress_ip_permission_collection.rb +1 -2
  16. data/lib/aws/ec2/security_group/{ip_permission_collection.rb → ingress_ip_permission_collection.rb} +4 -1
  17. data/lib/aws/ec2/security_group/ip_permission.rb +23 -45
  18. data/lib/aws/elb.rb +65 -0
  19. data/lib/aws/elb/availability_zone_collection.rb +138 -0
  20. data/lib/aws/elb/backend_server_policy_collection.rb +150 -0
  21. data/lib/aws/elb/client.rb +35 -0
  22. data/lib/aws/elb/client/xml.rb +33 -0
  23. data/lib/aws/elb/config.rb +18 -0
  24. data/lib/aws/elb/errors.rb +30 -0
  25. data/lib/aws/elb/instance_collection.rb +174 -0
  26. data/lib/aws/elb/listener.rb +189 -0
  27. data/lib/aws/elb/listener_collection.rb +119 -0
  28. data/lib/aws/elb/listener_opts.rb +45 -0
  29. data/lib/aws/elb/listener_spec.rb +14 -0
  30. data/lib/aws/elb/load_balancer.rb +255 -0
  31. data/lib/aws/elb/load_balancer_collection.rb +113 -0
  32. data/lib/aws/elb/load_balancer_policy.rb +93 -0
  33. data/lib/aws/elb/load_balancer_policy_collection.rb +208 -0
  34. data/lib/aws/elb/request.rb +23 -0
  35. data/lib/aws/iam/collection.rb +24 -26
  36. data/lib/aws/iam/group_user_collection.rb +21 -28
  37. data/lib/aws/iam/server_certificate_collection.rb +1 -37
  38. data/lib/aws/record.rb +1 -1
  39. data/lib/aws/record/base.rb +14 -1
  40. data/lib/aws/record/finder_methods.rb +4 -1
  41. data/lib/aws/record/validations.rb +73 -32
  42. data/lib/aws/{core/api_config_transform.rb → record/validators/method.rb} +9 -12
  43. data/lib/aws/s3/bucket_collection.rb +6 -4
  44. data/lib/aws/s3/client.rb +37 -6
  45. data/lib/aws/s3/config.rb +3 -1
  46. data/lib/aws/s3/prefixed_collection.rb +1 -2
  47. data/lib/aws/s3/presigned_post.rb +37 -4
  48. data/lib/aws/s3/s3_object.rb +93 -1
  49. data/lib/aws/simple_db/domain.rb +8 -0
  50. data/lib/aws/simple_db/item.rb +15 -0
  51. data/lib/aws/simple_db/item_collection.rb +255 -201
  52. data/lib/aws/simple_db/item_data.rb +1 -1
  53. data/lib/aws/simple_email_service/client.rb +0 -1
  54. data/lib/aws/sns/client.rb +0 -1
  55. metadata +107 -55
  56. data/lib/aws/core/collections.rb +0 -229
  57. data/lib/aws/simple_email_service/client/options.rb +0 -21
  58. data/lib/aws/sns/client/options.rb +0 -21
@@ -20,5 +20,7 @@ AWS::Core::Configuration.module_eval do
20
20
  add_option :s3_multipart_min_part_size, 5 * 1024 * 1024
21
21
 
22
22
  add_option :s3_multipart_max_parts, 10000
23
-
23
+
24
+ add_option :s3_server_side_encryption, nil
25
+
24
26
  end
@@ -65,8 +65,7 @@ module AWS
65
65
  raise ArgumentError, "invalid prefix mode `#{mode}`, it must be " +
66
66
  ":replace, :append or :prepend"
67
67
  end
68
- self.class.new(bucket,
69
- :prefix => new_prefix)
68
+ self.class.new(bucket, :prefix => new_prefix)
70
69
  end
71
70
 
72
71
  # @private
@@ -79,6 +79,7 @@ module AWS
79
79
  :content_encoding,
80
80
  :expires_header,
81
81
  :acl,
82
+ :server_side_encryption,
82
83
  :success_action_redirect,
83
84
  :success_action_status]
84
85
 
@@ -153,6 +154,15 @@ module AWS
153
154
  # * +:bucket_owner_read+
154
155
  # * +:bucket_owner_full_control+
155
156
  #
157
+ # @option options [Symbol] :server_side_encryption (nil) If this
158
+ # option is set, the object will be stored using server side
159
+ # encryption. The only valid value is +:aes256+, which
160
+ # specifies that the object should be stored using the AES
161
+ # encryption algorithm with 256 bit keys. By default, this
162
+ # option uses the value of the +:s3_server_side_encryption+
163
+ # option in the current configuration; for more information,
164
+ # see {AWS.config}.
165
+ #
156
166
  # @option opts [String] :success_action_redirect The URL to
157
167
  # which the client is redirected upon successful upload.
158
168
  #
@@ -197,6 +207,12 @@ module AWS
197
207
  @expires = opts[:expires]
198
208
 
199
209
  super
210
+
211
+ @fields[:server_side_encryption] =
212
+ config.s3_server_side_encryption unless
213
+ @fields.key?(:server_side_encryption)
214
+ @fields.delete(:server_side_encryption) if
215
+ @fields[:server_side_encryption].nil?
200
216
  end
201
217
 
202
218
  # @return [Boolean] True if {#url} generates an HTTPS url.
@@ -415,13 +431,10 @@ module AWS
415
431
  fields = (SPECIAL_FIELDS &
416
432
  @fields.keys).inject({}) do |fields, option_name|
417
433
  fields[field_name(option_name)] =
418
- @fields[option_name].to_s
434
+ field_value(option_name)
419
435
  fields
420
436
  end
421
437
 
422
- fields["acl"] = fields["acl"].tr("_", "-") if
423
- fields["acl"]
424
-
425
438
  @metadata.each do |key, value|
426
439
  fields["x-amz-meta-#{key}"] = value.to_s
427
440
  end
@@ -435,6 +448,8 @@ module AWS
435
448
  case option_name
436
449
  when :expires_header
437
450
  "Expires"
451
+ when :server_side_encryption
452
+ "x-amz-server-side-encryption"
438
453
  when :acl, :success_action_redirect, :success_action_status
439
454
  option_name.to_s
440
455
  else
@@ -446,6 +461,24 @@ module AWS
446
461
  end
447
462
  end
448
463
 
464
+ # @private
465
+ private
466
+ def field_value(option_name)
467
+ case option_name
468
+ when :acl
469
+ @fields[:acl].to_s.tr("_", "-")
470
+ when :server_side_encryption
471
+ value = @fields[:server_side_encryption]
472
+ if value.kind_of?(Symbol)
473
+ value.to_s.upcase
474
+ else
475
+ value.to_s
476
+ end
477
+ else
478
+ @fields[option_name].to_s
479
+ end
480
+ end
481
+
449
482
  # @private
450
483
  private
451
484
  def generate_conditions
@@ -82,12 +82,14 @@ module AWS
82
82
  # * content_length (integer, number of bytes)
83
83
  # * content_type (as sent to S3 when uploading the object)
84
84
  # * etag (typically the object's MD5)
85
+ # * server_side_encryption (the algorithm used to encrypt the
86
+ # object on the server side, e.g. +:aes256+)
85
87
  #
86
88
  # @param [Hash] options
87
89
  # @option options [String] :version_id Which version of this object
88
90
  # to make a HEAD request against.
89
91
  # @return A head object response with metatadata,
90
- # content_length, content_type and etag.
92
+ # content_length, content_type, etag and server_side_encryption.
91
93
  def head options = {}
92
94
  client.head_object(options.merge(
93
95
  :bucket_name => bucket.name, :key => key))
@@ -124,6 +126,19 @@ module AWS
124
126
  head.content_type
125
127
  end
126
128
 
129
+ # @return [Symbol, nil] Returns the algorithm used to encrypt
130
+ # the object on the server side, or +nil+ if SSE was not used
131
+ # when storing the object.
132
+ def server_side_encryption
133
+ head.server_side_encryption
134
+ end
135
+
136
+ # @return [true, false] Returns true if the object was stored
137
+ # using server side encryption.
138
+ def server_side_encryption?
139
+ !server_side_encryption.nil?
140
+ end
141
+
127
142
  # Deletes the object from its S3 bucket.
128
143
  #
129
144
  # @param [Hash] options
@@ -258,6 +273,15 @@ module AWS
258
273
  # @option options :content_type A standard MIME type
259
274
  # describing the format of the object data.
260
275
  #
276
+ # @option options [Symbol] :server_side_encryption (nil) If this
277
+ # option is set, the object will be stored using server side
278
+ # encryption. The only valid value is +:aes256+, which
279
+ # specifies that the object should be stored using the AES
280
+ # encryption algorithm with 256 bit keys. By default, this
281
+ # option uses the value of the +:s3_server_side_encryption+
282
+ # option in the current configuration; for more information,
283
+ # see {AWS.config}.
284
+ #
261
285
  # @return [S3Object, ObjectVersion] If the bucket has versioning
262
286
  # enabled, returns the {ObjectVersion} representing the
263
287
  # version that was uploaded. If versioning is disabled,
@@ -267,6 +291,8 @@ module AWS
267
291
  (data_options, put_options) =
268
292
  compute_put_options(options_or_data, options)
269
293
 
294
+ add_configured_write_options(put_options)
295
+
270
296
  if use_multipart?(data_options, put_options)
271
297
  put_options.delete(:multipart_threshold)
272
298
  multipart_upload(put_options) do |upload|
@@ -363,11 +389,24 @@ module AWS
363
389
  # @option options :content_type A standard MIME type
364
390
  # describing the format of the object data.
365
391
  #
392
+ # @option options [Symbol] :server_side_encryption (nil) If this
393
+ # option is set, the object will be stored using server side
394
+ # encryption. The only valid value is +:aes256+, which
395
+ # specifies that the object should be stored using the AES
396
+ # encryption algorithm with 256 bit keys. By default, this
397
+ # option uses the value of the +:s3_server_side_encryption+
398
+ # option in the current configuration; for more information,
399
+ # see {AWS.config}.
400
+ #
366
401
  # @return [S3Object, ObjectVersion] If the bucket has versioning
367
402
  # enabled, returns the {ObjectVersion} representing the
368
403
  # version that was uploaded. If versioning is disabled,
369
404
  # returns self.
370
405
  def multipart_upload(options = {})
406
+
407
+ options = options.dup
408
+ add_configured_write_options(options)
409
+
371
410
  upload = multipart_uploads.create(options)
372
411
 
373
412
  if block_given?
@@ -399,6 +438,12 @@ module AWS
399
438
  # and upload it again. You can also change the storage class and
400
439
  # metadata of the object when copying.
401
440
  #
441
+ # @note This operation does not copy the ACL, storage class
442
+ # (standard vs. reduced redundancy) or server side encryption
443
+ # setting from the source object. If you don't specify any of
444
+ # these options when copying, the object will have the default
445
+ # values as described below.
446
+ #
402
447
  # @param [Mixed] source
403
448
  #
404
449
  # @param [Hash] options
@@ -430,6 +475,15 @@ module AWS
430
475
  # * +:bucket_owner_read+
431
476
  # * +:bucket_owner_full_control+
432
477
  #
478
+ # @option options [Symbol] :server_side_encryption (nil) If this
479
+ # option is set, the object will be stored using server side
480
+ # encryption. The only valid value is +:aes256+, which
481
+ # specifies that the object should be stored using the AES
482
+ # encryption algorithm with 256 bit keys. By default, this
483
+ # option uses the value of the +:s3_server_side_encryption+
484
+ # option in the current configuration; for more information,
485
+ # see {AWS.config}.
486
+ #
433
487
  # @return [nil]
434
488
  def copy_from source, options = {}
435
489
 
@@ -458,6 +512,10 @@ module AWS
458
512
 
459
513
  copy_opts[:acl] = options[:acl] if options[:acl]
460
514
  copy_opts[:version_id] = options[:version_id] if options[:version_id]
515
+ copy_opts[:server_side_encryption] =
516
+ options[:server_side_encryption] if
517
+ options.key?(:server_side_encryption)
518
+ add_configured_write_options(copy_opts)
461
519
 
462
520
  if options[:reduced_redundancy]
463
521
  copy_opts[:storage_class] = 'REDUCED_REDUNDANCY'
@@ -477,6 +535,12 @@ module AWS
477
535
  # and upload it again. You can also change the storage class and
478
536
  # metadata of the object when copying.
479
537
  #
538
+ # @note This operation does not copy the ACL, storage class
539
+ # (standard vs. reduced redundancy) or server side encryption
540
+ # setting from this object to the new object. If you don't
541
+ # specify any of these options when copying, the new object
542
+ # will have the default values as described below.
543
+ #
480
544
  # @param [S3Object,String] target An S3Object, or a string key of
481
545
  # and object to copy to.
482
546
  #
@@ -497,6 +561,25 @@ module AWS
497
561
  # the object is stored with reduced redundancy in S3 for a
498
562
  # lower cost.
499
563
  #
564
+ # @option options [Symbol] :acl (private) A canned access
565
+ # control policy. Valid values are:
566
+ #
567
+ # * +:private+
568
+ # * +:public_read+
569
+ # * +:public_read_write+
570
+ # * +:authenticated_read+
571
+ # * +:bucket_owner_read+
572
+ # * +:bucket_owner_full_control+
573
+ #
574
+ # @option options [Symbol] :server_side_encryption (nil) If this
575
+ # option is set, the object will be stored using server side
576
+ # encryption. The only valid value is +:aes256+, which
577
+ # specifies that the object should be stored using the AES
578
+ # encryption algorithm with 256 bit keys. By default, this
579
+ # option uses the value of the +:s3_server_side_encryption+
580
+ # option in the current configuration; for more information,
581
+ # see {AWS.config}.
582
+ #
500
583
  # @return (see #copy_from)
501
584
  def copy_to target, options = {}
502
585
 
@@ -825,6 +908,15 @@ module AWS
825
908
  end
826
909
  end
827
910
 
911
+ private
912
+ def add_configured_write_options(options)
913
+ options[:server_side_encryption] =
914
+ config.s3_server_side_encryption unless
915
+ options.key?(:server_side_encryption)
916
+ options.delete(:server_side_encryption) if
917
+ options[:server_side_encryption] == nil
918
+ end
919
+
828
920
  # @private
829
921
  private
830
922
  def use_multipart?(data_options, options)
@@ -100,6 +100,14 @@ module AWS
100
100
  ItemCollection.new(self)
101
101
  end
102
102
 
103
+ # @return [Boolean] Returns true if the domains are the same.
104
+ def == other
105
+ other.is_a?(Domain) and
106
+ other.name == name and
107
+ other.config.simple_db_endpoint == config.simple_db_endpoint
108
+ end
109
+ alias_method :eql?, :==
110
+
103
111
  # An irb-friendly string representation of this object.
104
112
  #
105
113
  # @return [String]
@@ -49,6 +49,13 @@ module AWS
49
49
  end
50
50
 
51
51
  # Deletes the item and all of its attributes from SimpleDB.
52
+ # @param [Hash] options
53
+ # @option options [Hash] :if Pass a hash with a single key (attribute
54
+ # name) and a single value (the attribute value). This causes the
55
+ # delete to become conditional.
56
+ # @option options [String,Symbol] :unless Pass an attribute name. This
57
+ # causes the delete to become conditional on that attribute not
58
+ # existing.
52
59
  # @return [nil]
53
60
  def delete options = {}
54
61
  delete_opts = {}
@@ -72,6 +79,14 @@ module AWS
72
79
  ItemData.new(:name => name, :domain => domain, :response_object => r)
73
80
  end
74
81
 
82
+ def == other
83
+ other.is_a?(Item) and
84
+ other.domain == domain and
85
+ other.name == name
86
+ end
87
+
88
+ alias_method :eql?, :==
89
+
75
90
  end
76
91
 
77
92
  end
@@ -17,10 +17,27 @@ module AWS
17
17
  # Represents a collection of items in a SimpleDB domain.
18
18
  class ItemCollection
19
19
 
20
- include Core::Model
21
- include Enumerable
20
+ # Identifies quoted regions in the string, giving access to
21
+ # the regions before and after each quoted region, for example:
22
+ # "? ? `foo?``bar?` ? 'foo?' ?".scan(OUTSIDE_QUOTES_REGEX)
23
+ # # => [["? ? ", "`foo?``bar?`", " ? "], ["", "'foo?'", " ?"]]
24
+ # @private
25
+ OUTSIDE_QUOTES_REGEX = Regexp.compile(
26
+ '([^\'"`]*)(`(?:[^`]*(?:``))*[^`]*`|' +
27
+ '\'(?:[^\']*(?:\'\'))*[^\']*\'|' +
28
+ '"(?:[^"]*(?:""))*[^"]*")([^\'`"]*)'
29
+ )
30
+
22
31
  include ConsistentReadOption
23
32
 
33
+ include Core::Collection::Limitable
34
+
35
+ # @return [Domain] The domain the items belong to.
36
+ attr_reader :domain
37
+
38
+ # @private
39
+ attr_reader :output_list
40
+
24
41
  # @private
25
42
  attr_reader :conditions
26
43
 
@@ -31,17 +48,14 @@ module AWS
31
48
  # @return [ItemCollection]
32
49
  def initialize domain, options = {}
33
50
  @domain = domain
34
- @conditions = []
35
- @conditions += options[:conditions] if options[:conditions]
36
- @sort_instructions = options[:sort_instructions] if options[:sort_instructions]
51
+ @output_list = options[:output_list] || 'itemName()'
52
+ @conditions = options[:conditions] || []
53
+ @sort_instructions = options[:sort_instructions]
37
54
  @not_null_attribute = options[:not_null_attribute]
38
- @limit = options[:limit] if options[:limit]
55
+ @limit = options[:limit]
39
56
  super
40
57
  end
41
58
 
42
- # @return [Domain] The domain the items belong to.
43
- attr_reader :domain
44
-
45
59
  # Creates a new item in SimpleDB with the given attributes:
46
60
  #
47
61
  # domain.items.create('shirt', {
@@ -78,132 +92,85 @@ module AWS
78
92
  Item.new(domain, item_name.to_s)
79
93
  end
80
94
 
81
- # Yields to the block once for each item in the domain.
95
+ # Yields to the block once for each item in the collection.
96
+ # This method can yield two type of objects:
82
97
  #
83
- # @example using each to fetch every item in the domain.
98
+ # * AWS::SimpleDB::Item objects (only the item name is populated)
99
+ # * AWS::SimpleDB::ItemData objects (some or all attributes populated)
84
100
  #
101
+ # The defualt mode of an ItemCollection is to yield Item objects with
102
+ # no populated attributes.
103
+ #
104
+ # # only receives item names from SimpleDB
85
105
  # domain.items.each do |item|
86
106
  # puts item.name
107
+ # puts item.class.name # => AWS::SimpleDB::Item
108
+ # end
109
+ #
110
+ # You can switch a collection into yielded {ItemData} objects by
111
+ # specifying what attributes to request:
112
+ #
113
+ # domain.items.select(:all).each do |item_data|
114
+ # puts item_data.class.name # => AWS::SimpleDB::ItemData
115
+ # puts item_data.attributes # => { 'attr-name' => 'attr-value', ... }
116
+ # end
117
+ #
118
+ # You can also pass the standard scope options to #each as well:
119
+ #
120
+ # # output the item names of the 10 most expesive items
121
+ # domain.items.each(:order => [:price, :desc], :limit => 10).each do |item|
122
+ # puts item.name
87
123
  # end
88
124
  #
89
125
  # @yield [item] Yields once for every item in the {#domain}.
90
- # @yieldparam [Item] item
91
- # @param options (see #select)
92
- # @option options (see #select)
93
- # @option options [Symbol or Array] :select Causes this method
94
- # to behave like {#select} and yield {ItemData} instead of
95
- # {Item} instances.
96
- # @option options :batch_size Specifies a maximum number of records
97
- # to fetch from SimpleDB in a single request. SimpleDB may return
98
- # fewer items than :batch_size per request, but never more.
99
- # @return [nil]
100
- def each options = {}, &block
101
-
102
- handle_query_options(options) do |c, opts|
103
- return c.each(opts, &block)
104
- end
105
-
106
- if attributes = options.delete(:select)
107
- return select(attributes, options, &block)
108
- end
109
-
110
- perform_select(options) do |response|
111
- response.items.each do |item|
112
- yield(self[item.name])
113
- end
114
- end
115
-
116
- end
117
-
118
- # Retrieves data from each item in the domain.
119
126
  #
120
- # domain.items.select('size', 'color')
127
+ # @yieldparam [Item,ItemData] item If the item collection has been
128
+ # scoped by chaining +#select+ or by passing the +:select+ option
129
+ # then {ItemData} objects (that contain a hash of attrbiutes) are
130
+ # yielded. If no list of attributes has been provided, then#
131
+ # {Item} objects (with no populated data) are yielded.
121
132
  #
122
- # You may optionally filter by a set of conditions. For example,
123
- # to retrieve the attributes of each of the top 100 items in order of
124
- # descending popularity as an array of hashes, you could do:
133
+ # @param options [Hash]
125
134
  #
126
- # items.order(:popularity, :desc).limit(100).select do |data|
127
- # puts data.to_yaml
128
- # end
135
+ # @option options [Boolean] :consistent_read (false) Causes this
136
+ # method to yield the most current data in the domain.
129
137
  #
130
- # You can select specific attributes; for example, to get
131
- # all the unique colors in the collection you could do:
138
+ # @option options [Mixed] :select If select is provided, then each
139
+ # will yield {ItemData} objects instead of empty {Item}.
140
+ # The +:select+ option may be:
132
141
  #
133
- # colors = Set.new
134
- # items.select(:color) {|data| colors += data.attributes["color"] }
142
+ # * +:all+ - Specifies that all attributes should requested.
135
143
  #
136
- # Finally, you can specify conditions, sort instructions, and
137
- # a limit in the same method call:
144
+ # * A single or array of attribute names (as strings or symbols).
145
+ # This causes the named attribute(s) to be requested.
138
146
  #
139
- # items.select(:color,
140
- # :where => "rating > 4",
141
- # :order => [:popularity, :desc],
142
- # :limit => 100) do |data|
143
- # puts "Data for #{data.name}: #{data.attributes.inspect}"
144
- # end
147
+ # @option options :where Restricts the item collection using
148
+ # {#where} before querying (see {#where}).
145
149
  #
146
- # @overload select(*attribute_names, options = {}) &block
147
- # @param *attributes [Symbol, String, or Array]
148
- # The attributes to retrieve. This can be:
150
+ # @option options :order Changes the order in which the items
151
+ # will be yielded (see {#order}).
149
152
  #
150
- # * +:all+ to retrieve all attributes (the default).
151
- # * a Symbol or String to retrieve a single attribute.
152
- # * an array of Symbols or Strings to retrieve multiple attributes.
153
+ # @option options :limit [Integer] The maximum number of
154
+ # items to fetch from SimpleDB.
153
155
  #
154
- # For single attributes or arrays of attributes, the
155
- # attribute name may contain any characters that are valid
156
- # in a SimpleDB attribute name; this method will handle
157
- # escaping them for inclusion in the query. Note that you
158
- # cannot use this method to select the number of items; use
159
- # {#count} instead.
156
+ # @option options :batch_size Specifies a maximum number of records
157
+ # to fetch from SimpleDB in a single request. SimpleDB may return
158
+ # fewer items than :batch_size per request, but never more.
159
+ # Generally you should not need to specify this option.
160
160
  #
161
- # @param [Hash] options Options for querying the domain.
162
- # @option options [Boolean] :consistent_read (false) Causes this
163
- # method to yield the most current data in the domain.
164
- # @option options :where Restricts the item collection using
165
- # {#where} before querying.
166
- # @option options :order Changes the order in which the items
167
- # will be yielded (see {#order}).
168
- # @option options :limit [Integer] The maximum number of
169
- # items to fetch from SimpleDB. More than one request may be
170
- # required to satisfy the limit.
171
- # @option options :batch_size Specifies a maximum number of records
172
- # to fetch from SimpleDB in a single request. SimpleDB may return
173
- # fewer items than :batch_size per request, but never more.
174
- # @return If no block is given, an enumerator is returned. If a block
175
- # was passed then nil is returned.
176
- def select *attributes, &block
177
-
178
- options = attributes.last.is_a?(Hash) ? attributes.pop : {}
179
-
180
- args = attributes + [options]
181
-
182
- handle_query_options(*args) do |c, *clean_args|
183
- return c.select(*clean_args, &block)
184
- end
185
-
186
- unless block_given?
187
- return Enumerator.new(self, :select, *args)
188
- end
189
-
190
- if attributes.empty?
191
- output_list = '*'
192
- #elsif attributes == ['*']
193
- # output_list = '*'
194
- else
195
- output_list = [attributes].flatten.collect do |attr|
196
- coerce_attribute(attr)
197
- end.join(', ')
198
- end
161
+ # @return [String,nil] Returns a next token that can be used with
162
+ # the exact same SimpleDB select expression to get more results.
163
+ # A next token is returned ONLY if there was a limit on the
164
+ # expression, otherwise all items will be enumerated and
165
+ # nil is returned.
166
+ #
167
+ def each options = {}, &block
199
168
 
200
- perform_select(options.merge(:output_list => output_list)) do |response|
201
- response.items.each do |item|
202
- yield(ItemData.new(:domain => domain, :response_object => item))
203
- end
169
+ handle_query_options(options) do |collection, opts|
170
+ return collection.each(opts, &block)
204
171
  end
205
172
 
206
- nil
173
+ super
207
174
 
208
175
  end
209
176
 
@@ -211,31 +178,32 @@ module AWS
211
178
  #
212
179
  # domain.items.count
213
180
  #
214
- # You can use this method to get the total number of items in
215
- # the domain, or you can use it with {#where} to count a subset
216
- # of items. For example, to count the items where the "color"
217
- # attribute is "red":
181
+ # You can specify what items to count with {#where}:
218
182
  #
219
- # domain.items.where("color" => "red").count
183
+ # domain.items.where(:color => "red").count
220
184
  #
221
- # You can also limit the number of items searched using the
222
- # {#limit} method. For example, to count the number of items up
223
- # to 500:
185
+ # You can also limit the number of items to count:
224
186
  #
187
+ # # count up to 500 items and then stop
225
188
  # domain.items.limit(500).count
226
189
  #
227
190
  # @param [Hash] options Options for counting items.
228
191
  #
229
192
  # @option options [Boolean] :consistent_read (false) Causes this
230
- # method to yield the most current data in the domain.
193
+ # method to yield the most current data in the domain when +true+.
194
+ #
231
195
  # @option options :where Restricts the item collection using
232
196
  # {#where} before querying.
197
+ #
233
198
  # @option options :limit [Integer] The maximum number of
234
- # items to fetch from SimpleDB. More than one request may be
235
- # required to satisfy the limit.
199
+ # items to count in SimpleDB.
200
+ #
201
+ # @return [Integer] The number of items counted.
202
+ #
236
203
  def count options = {}, &block
237
- handle_query_options(options) do |c, opts|
238
- return c.count(opts, &block)
204
+
205
+ handle_query_options(options) do |collection, opts|
206
+ return collection.count(opts, &block)
239
207
  end
240
208
 
241
209
  options = options.merge(:output_list => "count(*)")
@@ -243,30 +211,107 @@ module AWS
243
211
  count = 0
244
212
  next_token = nil
245
213
 
246
- while limit.nil? || count < limit and
247
- response = select_request(options, next_token)
214
+ begin
248
215
 
249
- if domain_item = response.items.first and
250
- count_attribute = domain_item.attributes.first
216
+ response = select_request(options, next_token)
217
+
218
+ if
219
+ domain_item = response.items.first and
220
+ count_attribute = domain_item.attributes.first
221
+ then
251
222
  count += count_attribute.value.to_i
252
223
  end
253
224
 
254
- next_token = response.next_token
255
- break unless next_token
225
+ break unless next_token = response.next_token
256
226
 
257
- end
227
+ end while limit.nil? || count < limit
258
228
 
259
229
  count
230
+
260
231
  end
261
232
  alias_method :size, :count
262
233
 
263
- # Identifies quoted regions in the string, giving access to
264
- # the regions before and after each quoted region, for example:
265
- # "? ? `foo?``bar?` ? 'foo?' ?".scan(OUTSIDE_QUOTES_REGEX)
266
- # # => [["? ? ", "`foo?``bar?`", " ? "], ["", "'foo?'", " ?"]]
267
- OUTSIDE_QUOTES_REGEX = Regexp.compile('([^\'"`]*)(`(?:[^`]*(?:``))*[^`]*`|'+
268
- '\'(?:[^\']*(?:\'\'))*[^\']*\'|'+
269
- '"(?:[^"]*(?:""))*[^"]*")([^\'`"]*)')
234
+ # # @return [PageResult] Returns an array-based object with results.
235
+ # # Results are either {Item} or {ItemData} objects depending on
236
+ # # the selection mode (item names only or with attributes).
237
+ # #
238
+ # def page options = {}
239
+ #
240
+ # handle_query_options(options) do |collection, opts|
241
+ # return collection.page(opts)
242
+ # end
243
+ #
244
+ # super(options)
245
+ #
246
+ # end
247
+
248
+ # Specifies a list of attributes select from SimpleDB.
249
+ #
250
+ # domain.items.select('size', 'color').each do |item_data|
251
+ # puts item_data.attributes # => { 'size' => ..., :color => ... }
252
+ # end
253
+ #
254
+ # You can select all attributes by passing +:all+ or '*':
255
+ #
256
+ # domain.items.select('*').each {|item_data| ... }
257
+ #
258
+ # domain.items.select(:all).each {|item_data| ... }
259
+ #
260
+ # Calling #select causes #each to yield {ItemData} objects
261
+ # with #attribute hashes, instead of {Item} objects with
262
+ # an item name.
263
+ #
264
+ # @param *attributes [Symbol, String, or Array] The attributes to
265
+ # retrieve. This can be:
266
+ #
267
+ # * +:all+ or '*' to request all attributes for each item
268
+ #
269
+ # * A list or array of attribute names as strinsg or symbols
270
+ #
271
+ # Attribute names may contain any characters that are valid
272
+ # in a SimpleDB attribute name; this method will handle
273
+ # escaping them for inclusion in the query. Note that you
274
+ # cannot use this method to select the number of items; use
275
+ # {#count} instead.
276
+ #
277
+ # @return [ItemCollection] Returns a new item collection with the
278
+ # specified list of attributes to select.
279
+ #
280
+ def select *attributes, &block
281
+
282
+ # Before select was morphed into a chainable method, it accepted
283
+ # a hash of options (e.g. :where, :order, :limit) that no longer
284
+ # make sense, but to maintain backwards compatability we still
285
+ # consume those.
286
+ #
287
+ # TODO : it would be a good idea to add a deprecation warning for
288
+ # passing options to #select
289
+ #
290
+ handle_query_options(*attributes) do |collection, *args|
291
+ return collection.select(*args, &block)
292
+ end
293
+
294
+ options = attributes.last.is_a?(Hash) ? attributes.pop : {}
295
+
296
+ output_list = case attributes.flatten
297
+ when [] then '*'
298
+ when ['*'] then '*'
299
+ when [:all] then '*'
300
+ else attributes.flatten.map{|attr| coerce_attribute(attr) }.join(', ')
301
+ end
302
+
303
+ collection = collection_with(:output_list => output_list)
304
+
305
+ if block_given?
306
+ # previously select accepted a block and it would enumerate items
307
+ # this is for backwards compatability
308
+ collection.each(options, &block)
309
+ nil
310
+ else
311
+ collection
312
+ end
313
+
314
+ end
270
315
 
271
316
  # Returns an item collection defined by the given conditions
272
317
  # in addition to any conditions defined on this collection.
@@ -333,7 +378,8 @@ module AWS
333
378
  #
334
379
  # @return [ItemCollection] Returns a new item collection with the
335
380
  # additional conditions.
336
- def where(conditions, *substitutions)
381
+ #
382
+ def where conditions, *substitutions
337
383
  case conditions
338
384
  when String
339
385
  conditions = [replace_placeholders(conditions, *substitutions)]
@@ -391,64 +437,72 @@ module AWS
391
437
  #
392
438
  # @overload limit
393
439
  # @return [Integer] Returns the current limit for the collection.
440
+ #
394
441
  # @overload limit(value)
395
442
  # @return [ItemCollection] Returns a collection with the given limit.
396
- def limit(*args)
443
+ #
444
+ def limit *args
397
445
  return @limit if args.empty?
398
446
  collection_with(:limit => Integer(args.first))
399
447
  end
448
+ alias_method :_limit, :limit # for Collection::Limitable
400
449
 
401
- # turns e.g. each(:where => 'foo', ...) into where('foo').each(...)
450
+ # Applies standard scope options (e.g. :where => 'foo') and removes them from
451
+ # the options hash by calling their method (e.g. by calling #where('foo')).
452
+ # Yields only if there were scope options to apply.
402
453
  # @private
403
454
  protected
404
455
  def handle_query_options(*args)
405
- options = args.pop if args.last.kind_of?(Hash)
406
- if query_options = options.keys & [:where, :order, :limit] and
407
- !query_options.empty?
408
- c = self
456
+
457
+ options = args.last.is_a?(Hash) ? args.pop : {}
458
+
459
+ if
460
+ query_options = options.keys & [:select, :where, :order, :limit] and
461
+ !query_options.empty?
462
+ then
463
+ collection = self
409
464
  query_options.each do |query_option|
410
465
  option_args = options[query_option]
411
466
  option_args = [option_args] unless option_args.kind_of?(Array)
412
467
  options.delete(query_option)
413
- c = send(query_option, *option_args)
468
+ collection = collection.send(query_option, *option_args)
414
469
  end
415
- yield(c, *(args + [options]))
416
- end
417
- end
418
470
 
419
- # @private
420
- protected
421
- def perform_select(options = {})
471
+ args << options
422
472
 
423
- next_token = options[:next_token]
424
- batch_size = options[:batch_size] ? Integer(options[:batch_size]) : nil
425
- total = 0
426
-
427
- begin
473
+ yield(collection, *args)
428
474
 
429
- # if the user provided a batch size we need to rewrite the
430
- # select expression's LIMIT clause.
431
- if batch_size
432
- max = limit ? [limit - total, batch_size].min : batch_size
433
- else
434
- max = nil
435
- end
475
+ end
476
+ end
436
477
 
437
- response = select_request(options, next_token, max)
478
+ protected
479
+ def _each_item next_token, max, options = {}, &block
438
480
 
439
- yield(response)
481
+ handle_query_options(options) do |collection, opts|
482
+ return collection._each_item(next_token, max, opts, &block)
483
+ end
440
484
 
441
- next_token = response.next_token
485
+ response = select_request(options, next_token, max)
442
486
 
443
- total += response.items.size
487
+ if output_list == 'itemName()'
488
+ response.items.each do |item|
489
+ yield(self[item.name])
490
+ end
491
+ else
492
+ response.items.each do |item|
493
+ yield(ItemData.new(:domain => domain, :response_object => item))
494
+ end
495
+ end
444
496
 
445
- end while next_token && (limit.nil? || total < limit)
497
+ response.next_token
498
+
446
499
  end
447
500
 
448
501
  protected
449
502
  def select_request(options, next_token = nil, limit = nil)
503
+
450
504
  opts = {}
451
- opts[:select_expression] = select_expression(options[:output_list])
505
+ opts[:select_expression] = select_expression(options)
452
506
  opts[:consistent_read] = consistent_read(options)
453
507
  opts[:next_token] = next_token if next_token
454
508
 
@@ -459,57 +513,57 @@ module AWS
459
513
  end
460
514
 
461
515
  client.select(opts)
462
- end
463
516
 
464
- # @private
465
- protected
466
- def select_expression(output_list = nil)
467
- output_list ||= "itemName()"
468
- "SELECT #{output_list} FROM `#{domain.name}`" +
469
- where_clause + order_by_clause + limit_clause
470
517
  end
471
518
 
472
519
  # @private
473
520
  protected
474
- def limit_clause
475
- if limit
476
- " LIMIT #{limit}"
477
- else
478
- ""
479
- end
521
+ def select_expression options = {}
522
+ expression = []
523
+ expression << "SELECT #{options[:output_list] || self.output_list}"
524
+ expression << "FROM `#{domain.name}`"
525
+ expression << where_clause
526
+ expression << order_by_clause
527
+ expression << limit_clause
528
+ expression.compact.join(' ')
480
529
  end
481
530
 
482
531
  # @private
483
532
  protected
484
533
  def where_clause
485
- all_conditions = conditions.dup
534
+
535
+ conditions = self.conditions.dup
536
+
486
537
  if @not_null_attribute
487
- all_conditions << coerce_attribute(@not_null_attribute) + " IS NOT NULL"
488
- end
489
- if all_conditions.empty?
490
- ""
491
- else
492
- " WHERE " + all_conditions.join(" AND ")
538
+ conditions << coerce_attribute(@not_null_attribute) + " IS NOT NULL"
493
539
  end
540
+
541
+ conditions.empty? ? nil : "WHERE #{conditions.join(" AND ")}"
542
+
494
543
  end
495
544
 
496
545
  # @private
497
546
  protected
498
547
  def order_by_clause
499
- if sort_instructions
500
- " ORDER BY " + sort_instructions
501
- else
502
- ""
503
- end
548
+ sort_instructions ? "ORDER BY #{sort_instructions}" : nil
504
549
  end
505
550
 
506
551
  # @private
507
552
  protected
508
- def collection_with(opts)
553
+ def limit_clause
554
+ limit ? "LIMIT #{limit}" : nil
555
+ end
556
+
557
+ # @private
558
+ protected
559
+ def collection_with options
509
560
  ItemCollection.new(domain, {
510
- :limit => limit,
561
+ :output_list => output_list,
511
562
  :conditions => conditions,
512
- :sort_instructions => sort_instructions }.merge(opts))
563
+ :sort_instructions => sort_instructions,
564
+ :not_null_attribute => @not_null_attribute,
565
+ :limit => limit,
566
+ }.merge(options))
513
567
  end
514
568
 
515
569
  # @private