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.
- data/History.txt +93 -15
- data/Manifest.txt +15 -1
- data/README.txt +0 -4
- data/Rakefile +34 -17
- data/lib/acf/right_acf_interface.rb +260 -124
- data/lib/acf/right_acf_invalidations.rb +144 -0
- data/lib/acf/right_acf_origin_access_identities.rb +230 -0
- data/lib/acf/right_acf_streaming_interface.rb +229 -0
- data/lib/acw/right_acw_interface.rb +4 -5
- data/lib/as/right_as_interface.rb +59 -51
- data/lib/awsbase/benchmark_fix.rb +0 -0
- data/lib/awsbase/right_awsbase.rb +351 -104
- data/lib/awsbase/support.rb +2 -82
- data/lib/awsbase/version.rb +9 -0
- data/lib/ec2/right_ec2.rb +97 -246
- data/lib/ec2/right_ec2_ebs.rb +88 -68
- data/lib/ec2/right_ec2_images.rb +90 -50
- data/lib/ec2/right_ec2_instances.rb +118 -89
- data/lib/ec2/right_ec2_placement_groups.rb +108 -0
- data/lib/ec2/right_ec2_reserved_instances.rb +51 -44
- data/lib/ec2/right_ec2_security_groups.rb +396 -0
- data/lib/ec2/right_ec2_spot_instances.rb +425 -0
- data/lib/ec2/right_ec2_tags.rb +139 -0
- data/lib/ec2/right_ec2_vpc.rb +152 -140
- data/lib/ec2/right_ec2_windows_mobility.rb +84 -0
- data/lib/elb/right_elb_interface.rb +205 -39
- data/lib/iam/right_iam_access_keys.rb +71 -0
- data/lib/iam/right_iam_groups.rb +195 -0
- data/lib/iam/right_iam_interface.rb +341 -0
- data/lib/iam/right_iam_mfa_devices.rb +67 -0
- data/lib/iam/right_iam_users.rb +251 -0
- data/lib/rds/right_rds_interface.rb +591 -205
- data/lib/right_aws.rb +16 -12
- data/lib/route_53/right_route_53_interface.rb +640 -0
- data/lib/s3/right_s3.rb +34 -13
- data/lib/s3/right_s3_interface.rb +17 -14
- data/lib/sdb/active_sdb.rb +215 -38
- data/lib/sdb/right_sdb_interface.rb +93 -12
- data/lib/sqs/right_sqs.rb +1 -2
- data/lib/sqs/right_sqs_gen2.rb +0 -1
- data/lib/sqs/right_sqs_gen2_interface.rb +9 -9
- data/lib/sqs/right_sqs_interface.rb +6 -7
- data/right_aws.gemspec +91 -0
- data/test/README.mdown +39 -0
- data/test/acf/test_helper.rb +0 -0
- data/test/acf/test_right_acf.rb +10 -18
- data/test/awsbase/test_helper.rb +0 -0
- data/test/awsbase/test_right_awsbase.rb +0 -1
- data/test/ec2/test_helper.rb +0 -0
- data/test/ec2/test_right_ec2.rb +0 -1
- data/test/elb/test_helper.rb +2 -0
- data/test/elb/test_right_elb.rb +43 -0
- data/test/http_connection.rb +0 -0
- data/test/route_53/fixtures/a_record.xml +18 -0
- data/test/route_53/fixtures/alias_record.xml +18 -0
- data/test/route_53/test_helper.rb +2 -0
- data/test/route_53/test_right_route_53.rb +141 -0
- data/test/s3/test_helper.rb +0 -0
- data/test/s3/test_right_s3.rb +11 -9
- data/test/s3/test_right_s3_stubbed.rb +6 -4
- data/test/sdb/test_active_sdb.rb +71 -13
- data/test/sdb/test_batch_put_attributes.rb +54 -0
- data/test/sdb/test_helper.rb +0 -0
- data/test/sdb/test_right_sdb.rb +13 -7
- data/test/sqs/test_helper.rb +0 -0
- data/test/sqs/test_right_sqs.rb +0 -6
- data/test/sqs/test_right_sqs_gen2.rb +22 -34
- data/test/test_credentials.rb +0 -0
- data/test/ts_right_aws.rb +0 -0
- metadata +146 -16
- data/VERSION +0 -1
data/lib/s3/right_s3.rb
CHANGED
@@ -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.
|
236
|
-
|
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.
|
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(
|
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.
|
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
|
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.
|
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', {:
|
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
|
-
|
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}".
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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]
|
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.
|
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.
|
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.
|
1174
|
+
value = value.first if value.is_a?(Array) && value.size<2
|
1172
1175
|
result[key] = value
|
1173
1176
|
end
|
1174
1177
|
result
|
data/lib/sdb/active_sdb.rb
CHANGED
@@ -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
|
-
|
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.
|
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].
|
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.
|
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.
|
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].
|
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.
|
644
|
+
unless order.right_blank?
|
579
645
|
sort_by, sort_order = sort_options(options[:order])
|
580
|
-
conditions << (conditions.
|
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'].
|
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
|
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] =
|
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.
|
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.
|
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.
|
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'].
|
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.
|
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'].
|
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.
|
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.
|
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.
|
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
|
-
|
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'].
|
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] =
|
924
|
-
|
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
|