aws-sdk 1.1.4 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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