icehouse-right_aws 1.11.0 → 2.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 (71) hide show
  1. data/History.txt +93 -15
  2. data/Manifest.txt +15 -1
  3. data/README.txt +0 -4
  4. data/Rakefile +34 -17
  5. data/lib/acf/right_acf_interface.rb +260 -124
  6. data/lib/acf/right_acf_invalidations.rb +144 -0
  7. data/lib/acf/right_acf_origin_access_identities.rb +230 -0
  8. data/lib/acf/right_acf_streaming_interface.rb +229 -0
  9. data/lib/acw/right_acw_interface.rb +4 -5
  10. data/lib/as/right_as_interface.rb +59 -51
  11. data/lib/awsbase/benchmark_fix.rb +0 -0
  12. data/lib/awsbase/right_awsbase.rb +351 -104
  13. data/lib/awsbase/support.rb +2 -82
  14. data/lib/awsbase/version.rb +9 -0
  15. data/lib/ec2/right_ec2.rb +97 -246
  16. data/lib/ec2/right_ec2_ebs.rb +88 -68
  17. data/lib/ec2/right_ec2_images.rb +90 -50
  18. data/lib/ec2/right_ec2_instances.rb +118 -89
  19. data/lib/ec2/right_ec2_placement_groups.rb +108 -0
  20. data/lib/ec2/right_ec2_reserved_instances.rb +51 -44
  21. data/lib/ec2/right_ec2_security_groups.rb +396 -0
  22. data/lib/ec2/right_ec2_spot_instances.rb +425 -0
  23. data/lib/ec2/right_ec2_tags.rb +139 -0
  24. data/lib/ec2/right_ec2_vpc.rb +152 -140
  25. data/lib/ec2/right_ec2_windows_mobility.rb +84 -0
  26. data/lib/elb/right_elb_interface.rb +205 -39
  27. data/lib/iam/right_iam_access_keys.rb +71 -0
  28. data/lib/iam/right_iam_groups.rb +195 -0
  29. data/lib/iam/right_iam_interface.rb +341 -0
  30. data/lib/iam/right_iam_mfa_devices.rb +67 -0
  31. data/lib/iam/right_iam_users.rb +251 -0
  32. data/lib/rds/right_rds_interface.rb +591 -205
  33. data/lib/right_aws.rb +16 -12
  34. data/lib/route_53/right_route_53_interface.rb +640 -0
  35. data/lib/s3/right_s3.rb +34 -13
  36. data/lib/s3/right_s3_interface.rb +17 -14
  37. data/lib/sdb/active_sdb.rb +215 -38
  38. data/lib/sdb/right_sdb_interface.rb +93 -12
  39. data/lib/sqs/right_sqs.rb +1 -2
  40. data/lib/sqs/right_sqs_gen2.rb +0 -1
  41. data/lib/sqs/right_sqs_gen2_interface.rb +9 -9
  42. data/lib/sqs/right_sqs_interface.rb +6 -7
  43. data/right_aws.gemspec +91 -0
  44. data/test/README.mdown +39 -0
  45. data/test/acf/test_helper.rb +0 -0
  46. data/test/acf/test_right_acf.rb +10 -18
  47. data/test/awsbase/test_helper.rb +0 -0
  48. data/test/awsbase/test_right_awsbase.rb +0 -1
  49. data/test/ec2/test_helper.rb +0 -0
  50. data/test/ec2/test_right_ec2.rb +0 -1
  51. data/test/elb/test_helper.rb +2 -0
  52. data/test/elb/test_right_elb.rb +43 -0
  53. data/test/http_connection.rb +0 -0
  54. data/test/route_53/fixtures/a_record.xml +18 -0
  55. data/test/route_53/fixtures/alias_record.xml +18 -0
  56. data/test/route_53/test_helper.rb +2 -0
  57. data/test/route_53/test_right_route_53.rb +141 -0
  58. data/test/s3/test_helper.rb +0 -0
  59. data/test/s3/test_right_s3.rb +11 -9
  60. data/test/s3/test_right_s3_stubbed.rb +6 -4
  61. data/test/sdb/test_active_sdb.rb +71 -13
  62. data/test/sdb/test_batch_put_attributes.rb +54 -0
  63. data/test/sdb/test_helper.rb +0 -0
  64. data/test/sdb/test_right_sdb.rb +13 -7
  65. data/test/sqs/test_helper.rb +0 -0
  66. data/test/sqs/test_right_sqs.rb +0 -6
  67. data/test/sqs/test_right_sqs_gen2.rb +22 -34
  68. data/test/test_credentials.rb +0 -0
  69. data/test/ts_right_aws.rb +0 -0
  70. metadata +146 -16
  71. data/VERSION +0 -1
@@ -59,7 +59,6 @@ module RightAws
59
59
  # {:server => 's3.amazonaws.com' # Amazon service host: 's3.amazonaws.com'(default)
60
60
  # :port => 443 # Amazon service port: 80 or 443(default)
61
61
  # :protocol => 'https' # Amazon service protocol: 'http' or 'https'(default)
62
- # :multi_thread => true|false # Multi-threaded (connection per each thread): true or false(default)
63
62
  # :logger => Logger Object} # Logger instance: logs to STDOUT if omitted }
64
63
  def initialize(aws_access_key_id=nil, aws_secret_access_key=nil, params={})
65
64
  @interface = S3Interface.new(aws_access_key_id, aws_secret_access_key, params)
@@ -217,11 +216,10 @@ module RightAws
217
216
  #
218
217
  # keys, service = bucket.keys_and_service({'max-keys'=> 2, 'prefix' => 'logs'})
219
218
  # p keys #=> # 2 keys array
220
- # p service #=> {"max-keys"=>"2", "prefix"=>"logs", "name"=>"my_awesome_bucket", "marker"=>"", "is_truncated"=>true}
219
+ # p service #=> {"max-keys"=>"2", "prefix"=>"logs", "name"=>"my_awesome_bucket", "marker"=>"", "is_truncated"=>true, :common_prefixes=>[]}
221
220
  #
222
221
  def keys_and_service(options={}, head=false)
223
222
  opt = {}; options.each{ |key, value| opt[key.to_s] = value }
224
- service_data = {}
225
223
  thislist = {}
226
224
  list = []
227
225
  @s3.interface.incrementally_list_bucket(@name, opt) do |thislist|
@@ -232,10 +230,8 @@ module RightAws
232
230
  list << key
233
231
  end
234
232
  end
235
- thislist.each_key do |key|
236
- service_data[key] = thislist[key] unless (key == :contents || key == :common_prefixes)
237
- end
238
- [list, service_data]
233
+ thislist.delete(:contents)
234
+ [list, thislist]
239
235
  end
240
236
 
241
237
  # Retrieve key information from Amazon.
@@ -249,7 +245,7 @@ module RightAws
249
245
  # key.head
250
246
  #
251
247
  def key(key_name, head=false)
252
- raise 'Key name can not be empty.' if key_name.blank?
248
+ raise 'Key name can not be empty.' if key_name.right_blank?
253
249
  key_instance = nil
254
250
  # if this key exists - find it ....
255
251
  keys({'prefix'=>key_name}, head).each do |key|
@@ -346,9 +342,10 @@ module RightAws
346
342
  # If +force+ is set, clears and deletes the bucket.
347
343
  # Returns +true+.
348
344
  #
349
- # bucket.delete(true) #=> true
345
+ # bucket.delete(:force => true) #=> true
350
346
  #
351
- def delete(force=false)
347
+ def delete(options={})
348
+ force = options.is_a?(Hash) && options[:force]==true
352
349
  force ? @s3.interface.force_delete_bucket(@name) : @s3.interface.delete_bucket(@name)
353
350
  end
354
351
 
@@ -460,6 +457,30 @@ module RightAws
460
457
  get if !@data and exists?
461
458
  @data
462
459
  end
460
+
461
+ # Getter for the 'content-type' metadata
462
+ def content_type
463
+ @headers['content-type'] if @headers
464
+ end
465
+
466
+ # Helper to get and URI-decode a header metadata.
467
+ # Metadata have to be HTTP encoded (rfc2616) as we use the Amazon S3 REST api
468
+ # see http://docs.amazonwebservices.com/AmazonS3/latest/index.html?UsingMetadata.html
469
+ def decoded_meta_headers(key = nil)
470
+ if key
471
+ # Get one metadata value by its key
472
+ URI.decode(@meta_headers[key.to_s])
473
+ else
474
+ # Get a hash of all metadata with a decoded value
475
+ @decoded_meta_headers ||= begin
476
+ metadata = {}
477
+ @meta_headers.each do |key, value|
478
+ metadata[key.to_sym] = URI.decode(value)
479
+ end
480
+ metadata
481
+ end
482
+ end
483
+ end
463
484
 
464
485
  # Retrieve object data and attributes from Amazon.
465
486
  # Returns a +String+.
@@ -626,7 +647,7 @@ module RightAws
626
647
  # key.delete #=> true
627
648
  #
628
649
  def delete
629
- raise 'Key name must be specified.' if @name.blank?
650
+ raise 'Key name must be specified.' if @name.right_blank?
630
651
  @bucket.s3.interface.delete(@bucket, @name)
631
652
  end
632
653
 
@@ -759,7 +780,7 @@ module RightAws
759
780
  @thing = thing
760
781
  @id = id
761
782
  @name = name
762
- @perms = perms.to_a
783
+ @perms = Array(perms)
763
784
  case action
764
785
  when :apply then apply
765
786
  when :refresh then refresh
@@ -1062,7 +1083,7 @@ module RightAws
1062
1083
  @bucket = bucket
1063
1084
  @name = name.to_s
1064
1085
  @meta_headers = meta_headers
1065
- raise 'Key name can not be empty.' if @name.blank?
1086
+ raise 'Key name can not be empty.' if @name.right_blank?
1066
1087
  end
1067
1088
 
1068
1089
  # Generate link to PUT key data.
@@ -63,14 +63,13 @@ module RightAws
63
63
 
64
64
  # Creates new RightS3 instance.
65
65
  #
66
- # s3 = RightAws::S3Interface.new('1E3GDYEOGFJPIT7XXXXXX','hgTHt68JY07JKUY08ftHYtERkjgtfERn57XXXXXX', {:multi_thread => true, :logger => Logger.new('/tmp/x.log')}) #=> #<RightAws::S3Interface:0xb7b3c27c>
66
+ # s3 = RightAws::S3Interface.new('1E3GDYEOGFJPIT7XXXXXX','hgTHt68JY07JKUY08ftHYtERkjgtfERn57XXXXXX', {:logger => Logger.new('/tmp/x.log')}) #=> #<RightAws::S3Interface:0xb7b3c27c>
67
67
  #
68
68
  # Params is a hash:
69
69
  #
70
70
  # {:server => 's3.amazonaws.com' # Amazon service host: 's3.amazonaws.com'(default)
71
71
  # :port => 443 # Amazon service port: 80 or 443(default)
72
72
  # :protocol => 'https' # Amazon service protocol: 'http' or 'https'(default)
73
- # :multi_thread => true|false # Multi-threaded (connection per each thread): true or false(default)
74
73
  # :logger => Logger Object} # Logger instance: logs to STDOUT if omitted }
75
74
  #
76
75
  def initialize(aws_access_key_id=nil, aws_secret_access_key=nil, params={})
@@ -93,7 +92,11 @@ module RightAws
93
92
  s3_headers = {}
94
93
  headers.each do |key, value|
95
94
  key = key.downcase
96
- s3_headers[key] = value.to_s.strip if key[/^#{AMAZON_HEADER_PREFIX}|^content-md5$|^content-type$|^date$/o]
95
+ value = case
96
+ when value.is_a?(Array) then value.join('')
97
+ else value.to_s
98
+ end
99
+ s3_headers[key] = value.strip if key[/^#{AMAZON_HEADER_PREFIX}|^content-md5$|^content-type$|^date$/o]
97
100
  end
98
101
  s3_headers['content-type'] ||= ''
99
102
  s3_headers['content-md5'] ||= ''
@@ -158,7 +161,7 @@ module RightAws
158
161
  headers['content-type'] ||= ''
159
162
  headers['date'] = Time.now.httpdate
160
163
  # create request
161
- request = "Net::HTTP::#{method.capitalize}".constantize.new(path)
164
+ request = "Net::HTTP::#{method.capitalize}".right_constantize.new(path)
162
165
  request.body = data if data
163
166
  # set request headers and meta headers
164
167
  headers.each { |key, value| request[key.to_s] = value }
@@ -210,7 +213,7 @@ module RightAws
210
213
  else headers[:location].to_s
211
214
  end
212
215
 
213
- unless location.blank?
216
+ unless location.right_blank?
214
217
  data = "<CreateBucketConfiguration><LocationConstraint>#{location}</LocationConstraint></CreateBucketConfiguration>"
215
218
  end
216
219
  req_hash = generate_rest_request('PUT', headers.merge(:url=>bucket, :data => data))
@@ -296,7 +299,7 @@ module RightAws
296
299
  # 'max-keys' => "5"}, ..., {...}]
297
300
  #
298
301
  def list_bucket(bucket, options={}, headers={})
299
- bucket += '?'+options.map{|k, v| "#{k.to_s}=#{CGI::escape v.to_s}"}.join('&') unless options.blank?
302
+ bucket += '?'+options.map{|k, v| "#{k.to_s}=#{CGI::escape v.to_s}"}.join('&') unless options.right_blank?
300
303
  req_hash = generate_rest_request('GET', headers.merge(:url=>bucket))
301
304
  request_info(req_hash, S3ListBucketParser.new(:logger => @logger))
302
305
  rescue
@@ -331,10 +334,10 @@ module RightAws
331
334
  # ]
332
335
  # }
333
336
  def incrementally_list_bucket(bucket, options={}, headers={}, &block)
334
- internal_options = options.symbolize_keys
337
+ internal_options = options.right_symbolize_keys
335
338
  begin
336
339
  internal_bucket = bucket.dup
337
- internal_bucket += '?'+internal_options.map{|k, v| "#{k.to_s}=#{CGI::escape v.to_s}"}.join('&') unless internal_options.blank?
340
+ internal_bucket += '?'+internal_options.map{|k, v| "#{k.to_s}=#{CGI::escape v.to_s}"}.join('&') unless internal_options.right_blank?
338
341
  req_hash = generate_rest_request('GET', headers.merge(:url=>internal_bucket))
339
342
  response = request_info(req_hash, S3ImprovedListBucketParser.new(:logger => @logger))
340
343
  there_are_more_keys = response[:is_truncated]
@@ -697,7 +700,7 @@ module RightAws
697
700
  # <Permission>FULL_CONTROL</Permission></Grant></AccessControlList></AccessControlPolicy>" }
698
701
  #
699
702
  def get_acl(bucket, key='', headers={})
700
- key = key.blank? ? '' : "/#{CGI::escape key}"
703
+ key = key.right_blank? ? '' : "/#{CGI::escape key}"
701
704
  req_hash = generate_rest_request('GET', headers.merge(:url=>"#{bucket}#{key}?acl"))
702
705
  request_info(req_hash, S3HttpResponseBodyParser.new)
703
706
  rescue
@@ -727,7 +730,7 @@ module RightAws
727
730
  # :display_name=>"root"}}
728
731
  #
729
732
  def get_acl_parse(bucket, key='', headers={})
730
- key = key.blank? ? '' : "/#{CGI::escape key}"
733
+ key = key.right_blank? ? '' : "/#{CGI::escape key}"
731
734
  req_hash = generate_rest_request('GET', headers.merge(:url=>"#{bucket}#{key}?acl"))
732
735
  acl = request_info(req_hash, S3AclParser.new(:logger => @logger))
733
736
  result = {}
@@ -740,7 +743,7 @@ module RightAws
740
743
  else
741
744
  result[:grantees][key] =
742
745
  { :display_name => grantee[:display_name] || grantee[:uri].to_s[/[^\/]*$/],
743
- :permissions => grantee[:permissions].to_a,
746
+ :permissions => Array(grantee[:permissions]),
744
747
  :attributes => grantee[:attributes] }
745
748
  end
746
749
  end
@@ -751,7 +754,7 @@ module RightAws
751
754
 
752
755
  # Sets the ACL on a bucket or object.
753
756
  def put_acl(bucket, key, acl_xml_doc, headers={})
754
- key = key.blank? ? '' : "/#{CGI::escape key}"
757
+ key = key.right_blank? ? '' : "/#{CGI::escape key}"
755
758
  req_hash = generate_rest_request('PUT', headers.merge(:url=>"#{bucket}#{key}?acl", :data=>acl_xml_doc))
756
759
  request_info(req_hash, S3HttpResponseBodyParser.new)
757
760
  rescue
@@ -885,7 +888,7 @@ module RightAws
885
888
  # s3.list_bucket_link('my_awesome_bucket') #=> url string
886
889
  #
887
890
  def list_bucket_link(bucket, options=nil, expires=nil, headers={})
888
- bucket += '?' + options.map{|k, v| "#{k.to_s}=#{CGI::escape v.to_s}"}.join('&') unless options.blank?
891
+ bucket += '?' + options.map{|k, v| "#{k.to_s}=#{CGI::escape v.to_s}"}.join('&') unless options.right_blank?
889
892
  generate_link('GET', headers.merge(:url=>bucket), expires)
890
893
  rescue
891
894
  on_exception
@@ -1168,7 +1171,7 @@ module RightAws
1168
1171
  def headers_to_string(headers)
1169
1172
  result = {}
1170
1173
  headers.each do |key, value|
1171
- value = value.to_s if value.is_a?(Array) && value.size<2
1174
+ value = value.first if value.is_a?(Array) && value.size<2
1172
1175
  result[key] = value
1173
1176
  end
1174
1177
  result
@@ -21,13 +21,6 @@
21
21
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
22
  #
23
23
 
24
- begin
25
- require 'uuidtools'
26
- rescue LoadError => e
27
- STDERR.puts("RightSDB requires the uuidtools gem. Run \'gem install uuidtools\' and try again.")
28
- exit
29
- end
30
-
31
24
  module RightAws
32
25
 
33
26
  # = RightAws::ActiveSdb -- RightScale SDB interface (alpha release)
@@ -39,9 +32,6 @@ module RightAws
39
32
  # require 'right_aws'
40
33
  # require 'sdb/active_sdb'
41
34
  #
42
- # Additionally, the ActiveSdb class requires the 'uuidtools' gem; this gem is not normally required by RightAws and is not installed as a
43
- # dependency of RightAws.
44
- #
45
35
  # Simple ActiveSdb usage example:
46
36
  #
47
37
  # class Client < RightAws::ActiveSdb::Base
@@ -92,6 +82,59 @@ module RightAws
92
82
  # # remove domain
93
83
  # Client.delete_domain
94
84
  #
85
+ # # Dynamic attribute accessors
86
+ #
87
+ # class KdClient < RightAws::ActiveSdb::Base
88
+ # end
89
+ #
90
+ # client = KdClient.select(:all, :order => 'expiration').first
91
+ # pp client.attributes #=>
92
+ # {"name"=>["Putin"],
93
+ # "post"=>["president"],
94
+ # "country"=>["Russia"],
95
+ # "expiration"=>["2008"],
96
+ # "id"=>"376d2e00-75b0-11dd-9557-001bfc466dd7",
97
+ # "gender"=>["male"]}
98
+ #
99
+ # pp client.name #=> ["Putin"]
100
+ # pp client.country #=> ["Russia"]
101
+ # pp client.post #=> ["president"]
102
+ #
103
+ # # Columns and simple typecasting
104
+ #
105
+ # class Person < RightAws::ActiveSdb::Base
106
+ # columns do
107
+ # name
108
+ # email
109
+ # score :Integer
110
+ # is_active :Boolean
111
+ # registered_at :DateTime
112
+ # created_at :DateTime, :default => lambda{ Time.now }
113
+ # end
114
+ # end
115
+ # Person::create( :name => 'Yetta E. Andrews', :email => 'nulla.facilisis@metus.com', :score => 100, :is_active => true, :registered_at => Time.local(2000, 1, 1) )
116
+ #
117
+ # person = Person.find_by_email 'nulla.facilisis@metus.com'
118
+ # person.reload
119
+ #
120
+ # pp person.attributes #=>
121
+ # {"name"=>["Yetta E. Andrews"],
122
+ # "created_at"=>["2010-04-02T20:51:58+0400"],
123
+ # "id"=>"0ee24946-3e60-11df-9d4c-0025b37efad0",
124
+ # "registered_at"=>["2000-01-01T00:00:00+0300"],
125
+ # "is_active"=>["T"],
126
+ # "score"=>["100"],
127
+ # "email"=>["nulla.facilisis@metus.com"]}
128
+ # pp person.name #=> "Yetta E. Andrews"
129
+ # pp person.name.class #=> String
130
+ # pp person.registered_at.to_s #=> "2000-01-01T00:00:00+03:00"
131
+ # pp person.registered_at.class #=> DateTime
132
+ # pp person.is_active #=> true
133
+ # pp person.is_active.class #=> TrueClass
134
+ # pp person.score #=> 100
135
+ # pp person.score.class #=> Fixnum
136
+ # pp person.created_at.to_s #=> "2010-04-02T20:51:58+04:00"
137
+ #
95
138
  class ActiveSdb
96
139
 
97
140
  module ActiveSdbConnect
@@ -106,7 +149,6 @@ module RightAws
106
149
  # :port => 443 # Amazon service port: 80 or 443(default)
107
150
  # :protocol => 'https' # Amazon service protocol: 'http' or 'https'(default)
108
151
  # :signature_version => '0' # The signature version : '0' or '1'(default)
109
- # :multi_thread => true|false # Multi-threaded (connection per each thread): true or false(default)
110
152
  # :logger => Logger Object # Logger instance: logs to STDOUT if omitted
111
153
  # :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')
112
154
 
@@ -242,7 +284,31 @@ module RightAws
242
284
  def delete_domain
243
285
  connection.delete_domain(domain)
244
286
  end
245
-
287
+
288
+ def columns(&block)
289
+ @columns ||= ColumnSet.new
290
+ @columns.instance_eval(&block) if block
291
+ @columns
292
+ end
293
+
294
+ def column?(col_name)
295
+ columns.include?(col_name)
296
+ end
297
+
298
+ def type_of(col_name)
299
+ columns.type_of(col_name)
300
+ end
301
+
302
+ def serialize(attribute, value)
303
+ s = serialization_for_type(type_of(attribute))
304
+ s ? s.serialize(value) : value.to_s
305
+ end
306
+
307
+ def deserialize(attribute, value)
308
+ s = serialization_for_type(type_of(attribute))
309
+ s ? s.deserialize(value) : value
310
+ end
311
+
246
312
  # Perform a find request.
247
313
  #
248
314
  # Single record:
@@ -364,7 +430,7 @@ module RightAws
364
430
  end
365
431
 
366
432
  def generate_id # :nodoc:
367
- UUIDTools::UUID.timestamp_create().to_s
433
+ AwsUtils::generate_unique_token
368
434
  end
369
435
 
370
436
  protected
@@ -376,13 +442,13 @@ module RightAws
376
442
  # detect amount of records requested
377
443
  bunch_of_records_requested = args.size > 1 || args.first.is_a?(Array)
378
444
  # flatten ids
379
- args = args.to_a.flatten
445
+ args = Array(args).flatten
380
446
  args.each { |id| cond << "id=#{self.connection.escape(id)}" }
381
447
  ids_cond = "(#{cond.join(' OR ')})"
382
448
  # user defined :conditions to string (if it was defined)
383
449
  options[:conditions] = build_conditions(options[:conditions])
384
450
  # join ids condition and user defined conditions
385
- options[:conditions] = options[:conditions].blank? ? ids_cond : "(#{options[:conditions]}) AND #{ids_cond}"
451
+ options[:conditions] = options[:conditions].right_blank? ? ids_cond : "(#{options[:conditions]}) AND #{ids_cond}"
386
452
  result = sql_select(options)
387
453
  # if one record was requested then return it
388
454
  unless bunch_of_records_requested
@@ -471,7 +537,7 @@ module RightAws
471
537
  query_expression = query_expression.to_s
472
538
  # quote from Amazon:
473
539
  # The sort attribute must be present in at least one of the predicates of the query expression.
474
- if query_expression.blank?
540
+ if query_expression.right_blank?
475
541
  query_expression = sort_query_expression
476
542
  elsif !query_attributes(query_expression).include?(sort_by)
477
543
  query_expression += " intersection #{sort_query_expression}"
@@ -518,13 +584,13 @@ module RightAws
518
584
  # detect amount of records requested
519
585
  bunch_of_records_requested = args.size > 1 || args.first.is_a?(Array)
520
586
  # flatten ids
521
- args = args.to_a.flatten
587
+ args = Array(args).flatten
522
588
  args.each { |id| cond << "'id'=#{self.connection.escape(id)}" }
523
589
  ids_cond = "[#{cond.join(' OR ')}]"
524
590
  # user defined :conditions to string (if it was defined)
525
591
  options[:conditions] = build_conditions(options[:conditions])
526
592
  # join ids condition and user defined conditions
527
- options[:conditions] = options[:conditions].blank? ? ids_cond : "#{options[:conditions]} intersection #{ids_cond}"
593
+ options[:conditions] = options[:conditions].right_blank? ? ids_cond : "#{options[:conditions]} intersection #{ids_cond}"
528
594
  result = find_every(options)
529
595
  # if one record was requested then return it
530
596
  unless bunch_of_records_requested
@@ -575,9 +641,9 @@ module RightAws
575
641
  order = options[:order] ? " ORDER BY #{options[:order]}" : ''
576
642
  limit = options[:limit] ? " LIMIT #{options[:limit]}" : ''
577
643
  # mix sort by argument (it must present in response)
578
- unless order.blank?
644
+ unless order.right_blank?
579
645
  sort_by, sort_order = sort_options(options[:order])
580
- conditions << (conditions.blank? ? " WHERE " : " AND ") << "(#{sort_by} IS NOT NULL)"
646
+ conditions << (conditions.right_blank? ? " WHERE " : " AND ") << "(#{sort_by} IS NOT NULL)"
581
647
  end
582
648
  "SELECT #{select} FROM #{from}#{conditions}#{order}#{limit}"
583
649
  end
@@ -590,6 +656,13 @@ module RightAws
590
656
  end
591
657
  end
592
658
 
659
+ def serialization_for_type(type)
660
+ @serializations ||= {}
661
+ unless @serializations.has_key? type
662
+ @serializations[type] = ::RightAws::ActiveSdb.const_get("#{type}Serialization") rescue false
663
+ end
664
+ @serializations[type]
665
+ end
593
666
  end
594
667
 
595
668
  public
@@ -657,11 +730,15 @@ module RightAws
657
730
  def attributes=(attrs)
658
731
  old_id = @attributes['id']
659
732
  @attributes = uniq_values(attrs)
660
- @attributes['id'] = old_id if @attributes['id'].blank? && !old_id.blank?
733
+ @attributes['id'] = old_id if @attributes['id'].right_blank? && !old_id.right_blank?
661
734
  self.attributes
662
735
  end
663
736
 
664
- def connection
737
+ def columns
738
+ self.class.columns
739
+ end
740
+
741
+ def connection
665
742
  self.class.connection
666
743
  end
667
744
 
@@ -675,7 +752,8 @@ module RightAws
675
752
  # puts item['Cat'].inspect #=> ["Jons socks", "clew", "mice"]
676
753
  #
677
754
  def [](attribute)
678
- @attributes[attribute.to_s]
755
+ raw = @attributes[attribute.to_s]
756
+ self.class.column?(attribute) && raw ? self.class.deserialize(attribute, raw.first) : raw
679
757
  end
680
758
 
681
759
  # Updates the attribute identified by +attribute+ with the specified +values+.
@@ -686,7 +764,14 @@ module RightAws
686
764
  #
687
765
  def []=(attribute, values)
688
766
  attribute = attribute.to_s
689
- @attributes[attribute] = attribute == 'id' ? values.to_s : values.to_a.uniq
767
+ @attributes[attribute] = case
768
+ when attribute == 'id'
769
+ values.to_s
770
+ when self.class.column?(attribute)
771
+ self.class.serialize(attribute, values)
772
+ else
773
+ Array(values).uniq
774
+ end
690
775
  end
691
776
 
692
777
  # Reload attributes from SDB. Replaces in-memory attributes.
@@ -699,7 +784,7 @@ module RightAws
699
784
  old_id = id
700
785
  attrs = connection.get_attributes(domain, id)[:attributes]
701
786
  @attributes = {}
702
- unless attrs.blank?
787
+ unless attrs.right_blank?
703
788
  attrs.each { |attribute, values| @attributes[attribute] = values }
704
789
  @attributes['id'] = old_id
705
790
  end
@@ -725,7 +810,7 @@ module RightAws
725
810
  attrs_list.flatten.uniq.each do |attribute|
726
811
  attribute = attribute.to_s
727
812
  values = connection.get_attributes(domain, id, attribute)[:attributes][attribute]
728
- unless values.blank?
813
+ unless values.right_blank?
729
814
  @attributes[attribute] = result[attribute] = values
730
815
  else
731
816
  @attributes.delete(attribute)
@@ -755,7 +840,7 @@ module RightAws
755
840
  prepare_for_update
756
841
  attrs = @attributes.dup
757
842
  attrs.delete('id')
758
- connection.put_attributes(domain, id, attrs) unless attrs.blank?
843
+ connection.put_attributes(domain, id, attrs) unless attrs.right_blank?
759
844
  connection.put_attributes(domain, id, { 'id' => id }, :replace)
760
845
  mark_as_old
761
846
  @attributes
@@ -771,10 +856,10 @@ module RightAws
771
856
  prepare_for_update
772
857
  # if 'id' is present in attrs hash:
773
858
  # replace internal 'id' attribute and remove it from the attributes to be sent
774
- @attributes['id'] = attrs['id'] unless attrs['id'].blank?
859
+ @attributes['id'] = attrs['id'] unless attrs['id'].right_blank?
775
860
  attrs.delete('id')
776
861
  # add new values to all attributes from list
777
- connection.put_attributes(domain, id, attrs) unless attrs.blank?
862
+ connection.put_attributes(domain, id, attrs) unless attrs.right_blank?
778
863
  connection.put_attributes(domain, id, { 'id' => id }, :replace)
779
864
  attrs.each do |attribute, values|
780
865
  @attributes[attribute] ||= []
@@ -818,12 +903,12 @@ module RightAws
818
903
  prepare_for_update
819
904
  attrs = uniq_values(attrs)
820
905
  # if 'id' is present in attrs hash then replace internal 'id' attribute
821
- unless attrs['id'].blank?
906
+ unless attrs['id'].right_blank?
822
907
  @attributes['id'] = attrs['id']
823
908
  else
824
909
  attrs['id'] = id
825
910
  end
826
- connection.put_attributes(domain, id, attrs, :replace) unless attrs.blank?
911
+ connection.put_attributes(domain, id, attrs, :replace) unless attrs.right_blank?
827
912
  attrs.each { |attribute, values| attrs[attribute] = values }
828
913
  mark_as_old
829
914
  attrs
@@ -842,7 +927,7 @@ module RightAws
842
927
  raise_on_id_absence
843
928
  attrs = uniq_values(attrs)
844
929
  attrs.delete('id')
845
- unless attrs.blank?
930
+ unless attrs.right_blank?
846
931
  connection.delete_attributes(domain, id, attrs)
847
932
  attrs.each do |attribute, values|
848
933
  # remove the values from the attribute
@@ -871,7 +956,7 @@ module RightAws
871
956
  raise_on_id_absence
872
957
  attrs_list = attrs_list.flatten.map{ |attribute| attribute.to_s }
873
958
  attrs_list.delete('id')
874
- unless attrs_list.blank?
959
+ unless attrs_list.right_blank?
875
960
  connection.delete_attributes(domain, id, attrs_list)
876
961
  attrs_list.each { |attribute| @attributes.delete(attribute) }
877
962
  end
@@ -906,25 +991,117 @@ module RightAws
906
991
  @new_record = false
907
992
  end
908
993
 
909
- private
910
-
994
+ # support accessing attribute values via method call
995
+ def method_missing(method_sym, *args)
996
+ method_name = method_sym.to_s
997
+ setter = method_name[-1,1] == '='
998
+ method_name.chop! if setter
999
+
1000
+ if @attributes.has_key?(method_name) || self.class.column?(method_name)
1001
+ setter ? self[method_name] = args.first : self[method_name]
1002
+ else
1003
+ super
1004
+ end
1005
+ end
1006
+
1007
+ private
1008
+
911
1009
  def raise_on_id_absence
912
1010
  raise ActiveSdbError.new('Unknown record id') unless id
913
1011
  end
914
1012
 
915
1013
  def prepare_for_update
916
- @attributes['id'] = self.class.generate_id if @attributes['id'].blank?
1014
+ @attributes['id'] = self.class.generate_id if @attributes['id'].right_blank?
1015
+ columns.all.each do |col_name|
1016
+ self[col_name] ||= columns.default(col_name)
1017
+ end
917
1018
  end
918
1019
 
919
1020
  def uniq_values(attributes=nil) # :nodoc:
920
1021
  attrs = {}
921
1022
  attributes.each do |attribute, values|
922
1023
  attribute = attribute.to_s
923
- attrs[attribute] = attribute == 'id' ? values.to_s : values.to_a.uniq
924
- attrs.delete(attribute) if values.blank?
1024
+ attrs[attribute] = case
1025
+ when attribute == 'id'
1026
+ values.to_s
1027
+ when self.class.column?(attribute)
1028
+ values.is_a?(String) ? values : self.class.serialize(attribute, values)
1029
+ else
1030
+ Array(values).uniq
1031
+ end
1032
+ attrs.delete(attribute) if values.right_blank?
925
1033
  end
926
1034
  attrs
927
1035
  end
928
1036
  end
1037
+
1038
+ class ColumnSet
1039
+ attr_accessor :columns
1040
+ def initialize
1041
+ @columns = {}
1042
+ end
1043
+
1044
+ def all
1045
+ @columns.keys
1046
+ end
1047
+
1048
+ def column(col_name)
1049
+ @columns[col_name.to_s]
1050
+ end
1051
+ alias_method :include?, :column
1052
+
1053
+ def type_of(col_name)
1054
+ column(col_name) && column(col_name)[:type]
1055
+ end
1056
+
1057
+ def default(col_name)
1058
+ return nil unless include?(col_name)
1059
+ default = column(col_name)[:default]
1060
+ default.respond_to?(:call) ? default.call : default
1061
+ end
1062
+
1063
+ def method_missing(method_sym, *args)
1064
+ data_type = args.shift || :String
1065
+ options = args.shift || {}
1066
+ @columns[method_sym.to_s] = options.merge( :type => data_type )
1067
+ end
1068
+ end
1069
+
1070
+ class DateTimeSerialization
1071
+ class << self
1072
+ def serialize(date)
1073
+ date.strftime('%Y-%m-%dT%H:%M:%S%z')
1074
+ end
1075
+
1076
+ def deserialize(string)
1077
+ r = DateTime.parse(string) rescue nil
1078
+ end
1079
+ end
1080
+ end
1081
+
1082
+ class BooleanSerialization
1083
+ class << self
1084
+ def serialize(boolean)
1085
+ boolean ? 'T' : 'F'
1086
+ end
1087
+
1088
+ def deserialize(string)
1089
+ string == 'T'
1090
+ end
1091
+ end
1092
+ end
1093
+
1094
+ class IntegerSerialization
1095
+ class << self
1096
+ def serialize(int)
1097
+ int.to_s
1098
+ end
1099
+
1100
+ def deserialize(string)
1101
+ string.to_i
1102
+ end
1103
+ end
1104
+ end
1105
+
929
1106
  end
930
1107
  end