right_aws 1.4.3 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,5 +1,5 @@
1
1
  #
2
- # Copyright (c) 2007 RightScale Inc
2
+ # Copyright (c) 2007-2008 RightScale Inc
3
3
  #
4
4
  # Permission is hereby granted, free of charge, to any person obtaining
5
5
  # a copy of this software and associated documentation files (the
@@ -43,13 +43,14 @@ require 's3/right_s3_interface'
43
43
  require 's3/right_s3'
44
44
  require 'sqs/right_sqs_interface'
45
45
  require 'sqs/right_sqs'
46
+ require 'sdb/right_sdb_interface'
46
47
 
47
48
 
48
49
  module RightAws #:nodoc:
49
50
  module VERSION #:nodoc:
50
51
  MAJOR = 1
51
- MINOR = 4
52
- TINY = 3
52
+ MINOR = 5
53
+ TINY = 0
53
54
 
54
55
  STRING = [MAJOR, MINOR, TINY].join('.')
55
56
  end
@@ -34,7 +34,19 @@ module RightAws
34
34
  #
35
35
  # Error handling: all operations raise an RightAws::AwsError in case
36
36
  # of problems. Note that transient errors are automatically retried.
37
-
37
+ #
38
+ # It is a good way to use domain naming style getting a name for the buckets.
39
+ # See http://docs.amazonwebservices.com/AmazonS3/2006-03-01/UsingBucket.html
40
+ # about the naming convention for the buckets. This case they can be accessed using a virtual domains.
41
+ #
42
+ # Let assume you have 3 buckets: 'awesome-bucket', 'awesome_bucket' and 'AWEsomE-bucket'.
43
+ # The first ones objects can be accessed as: http:// awesome-bucket.s3.amazonaws.com/key/object
44
+ #
45
+ # But the rest have to be accessed as:
46
+ # http:// s3.amazonaws.com/awesome_bucket/key/object and http:// s3.amazonaws.com/AWEsomE-bucket/key/object
47
+ #
48
+ # See: http://docs.amazonwebservices.com/AmazonS3/2006-03-01/VirtualHosting.html for better explanation.
49
+ #
38
50
  class S3
39
51
  attr_reader :interface
40
52
 
@@ -60,22 +72,24 @@ module RightAws
60
72
 
61
73
  # Retrieve an individual bucket.
62
74
  # If the bucket does not exist and +create+ is set, a new bucket
63
- # is created on S3. The +create+ parameter has no effect if
64
- # the bucket alrady exists.
75
+ # is created on S3. Launching this method with +create+=+true+ may
76
+ # affect on the bucket's ACL if the bucket already exists.
65
77
  # Returns a RightAws::S3::Bucket instance or +nil+ if the bucket does not exist
66
78
  # and +create+ is not set.
67
79
  #
68
80
  # s3 = RightAws::S3.new(aws_access_key_id, aws_secret_access_key)
69
- # bucket1 = s3.bucket('my_awesome_bucket')
81
+ # bucket1 = s3.bucket('my_awesome_bucket_1')
70
82
  # bucket1.keys #=> exception here if the bucket does not exists
71
83
  # ...
72
- # bucket2 = s3.bucket('my_awesome_bucket', true)
84
+ # bucket2 = s3.bucket('my_awesome_bucket_2', true)
73
85
  # bucket2.keys #=> list of keys
74
- #
86
+ # # create a bucket at the European location with public read access
87
+ # bucket3 = s3.bucket('my-awesome-bucket-3', true, 'public-read', :location => :eu)
88
+ #
75
89
  # see http://docs.amazonwebservices.com/AmazonS3/2006-03-01/RESTAccessPolicy.html
76
90
  # (section: Canned Access Policies)
77
91
  #
78
- def bucket(name, create=true, perms=nil, headers={})
92
+ def bucket(name, create=false, perms=nil, headers={})
79
93
  headers['x-amz-acl'] = perms if perms
80
94
  @interface.create_bucket(name, headers) if create
81
95
  buckets.each { |bucket| return bucket if bucket.name == name }
@@ -86,28 +100,33 @@ module RightAws
86
100
  class Bucket
87
101
  attr_reader :s3, :name, :owner, :creation_date
88
102
 
89
- # Create a Bucket instance.
103
+ # Create a Bucket instance.
90
104
  # If the bucket does not exist and +create+ is set, a new bucket
91
- # is created on S3. The +create+ parameter has no effect if
92
- # the bucket alrady exists.
105
+ # is created on S3. Launching this method with +create+=+true+ may
106
+ # affect on the bucket's ACL if the bucket already exists.
93
107
  # Returns Bucket instance or +nil+ if the bucket does not exist
94
108
  # and +create+ is not set.
95
- #
96
- # s3 = RightAws::S3.new(aws_access_key_id, aws_secret_access_key)
97
- # ...
98
- # bucket1 = RightAws::S3::Bucket.create(s3, 'my_awesome_bucket')
99
- # bucket1.keys #=> exception here if the bucket does not exists
100
- # ...
101
- # bucket2 = RightAws::S3::Bucket.create(s3, 'my_awesome_bucket', true)
102
- # bucket2.keys #=> list of keys
103
- #
104
- def self.create(s3, name, create=true, perms=nil, headers={})
109
+ #
110
+ # s3 = RightAws::S3.new(aws_access_key_id, aws_secret_access_key)
111
+ # ...
112
+ # bucket1 = RightAws::S3::Bucket.create(s3, 'my_awesome_bucket_1')
113
+ # bucket1.keys #=> exception here if the bucket does not exists
114
+ # ...
115
+ # bucket2 = RightAws::S3::Bucket.create(s3, 'my_awesome_bucket_2', true)
116
+ # bucket2.keys #=> list of keys
117
+ # # create a bucket at the European location with public read access
118
+ # bucket3 = RightAws::S3::Bucket.create(s3,'my-awesome-bucket-3', true, 'public-read', :location => :eu)
119
+ #
120
+ # see http://docs.amazonwebservices.com/AmazonS3/2006-03-01/RESTAccessPolicy.html
121
+ # (section: Canned Access Policies)
122
+ #
123
+ def self.create(s3, name, create=false, perms=nil, headers={})
105
124
  s3.bucket(name, create, perms, headers)
106
125
  end
107
126
 
108
127
 
109
128
  # Create a bucket instance. In normal use this method should
110
- # not be called directly.
129
+ # not be called directly.
111
130
  # Use RightAws::S3::Bucket.create or RightAws::S3.bucket instead.
112
131
  def initialize(s3, name, creation_date=nil, owner=nil)
113
132
  @s3 = s3
@@ -137,6 +156,11 @@ module RightAws
137
156
  params = @s3.interface.params
138
157
  "#{params[:protocol]}://#{params[:server]}:#{params[:port]}/#{full_name}"
139
158
  end
159
+
160
+ # Returns the bucket location
161
+ def location
162
+ @location ||= @s3.interface.bucket_location(@name)
163
+ end
140
164
 
141
165
  # Retrieve a group of keys from Amazon.
142
166
  # +options+ is a hash: { 'prefix'=>'', 'marker'=>'', 'max-keys'=>5, 'delimiter'=>'' }).
@@ -569,13 +593,15 @@ module RightAws
569
593
  # Create a new Grantee instance.
570
594
  # Grantee +id+ must exist on S3. If +action+ == :refresh, then retrieve
571
595
  # permissions from S3 and update @perms. If +action+ == :apply, then apply
572
- # perms to +thing+ at S3. The default action is :refresh.
596
+ # perms to +thing+ at S3. If +action+ == :apply_and_refresh then it performs.
597
+ # both the actions. This is used for the new grantees that had no perms to
598
+ # this thing before. The default action is :refresh.
573
599
  #
574
600
  # bucket = s3.bucket('my_awesome_bucket', true, 'public-read')
575
601
  # grantee1 = RightAws::S3::Grantee.new(bucket, 'a123b...223c', FULL_CONTROL)
576
602
  # ...
577
603
  # grantee2 = RightAws::S3::Grantee.new(bucket, 'abcde...asdf', [FULL_CONTROL, READ], :apply)
578
- #
604
+ # grantee3 = RightAws::S3::Grantee.new(bucket, 'aaaaa...aaaa', 'READ', :apply_and_refresh)
579
605
  #
580
606
  def initialize(thing, id, perms=[], action=:refresh, name=nil)
581
607
  @thing = thing
@@ -583,11 +609,20 @@ module RightAws
583
609
  @name = name
584
610
  @perms = perms.to_a
585
611
  case action
586
- when :apply; apply
587
- when :refresh; refresh
612
+ when :apply: apply
613
+ when :refresh: refresh
614
+ when :apply_and_refresh: apply; refresh
588
615
  end
589
616
  end
590
617
 
618
+ # Return +true+ if the grantee has any permissions to the thing.
619
+ def exists?
620
+ self.class.grantees(@thing).each do |grantee|
621
+ return true if @id == grantee.id
622
+ end
623
+ false
624
+ end
625
+
591
626
  # Return Grantee type (+String+): "Group" or "CanonicalUser".
592
627
  def type
593
628
  @id[/^http:/] ? "Group" : "CanonicalUser"
@@ -603,11 +638,16 @@ module RightAws
603
638
  # See http://docs.amazonwebservices.com/AmazonS3/2006-03-01/UsingPermissions.html .
604
639
  # Returns +true+.
605
640
  #
606
- # grantee.grant('FULL_CONTROL') #=> true
607
- #
608
- def grant(permission)
609
- return true if @perms.include?(permission)
610
- @perms += permission.to_a
641
+ # grantee.grant('FULL_CONTROL') #=> true
642
+ # grantee.grant('FULL_CONTROL','WRITE','READ') #=> true
643
+ # grantee.grant(['WRITE_ACP','READ','READ_ACP']) #=> true
644
+ #
645
+ def grant(*permissions)
646
+ permissions.flatten!
647
+ old_perms = @perms.dup
648
+ @perms += permissions
649
+ @perms.uniq!
650
+ return true if @perms == old_perms
611
651
  apply
612
652
  end
613
653
 
@@ -617,11 +657,16 @@ module RightAws
617
657
  # Default value is 'FULL_CONTROL'.
618
658
  # Returns +true+.
619
659
  #
620
- # grantee.revoke #=> true
660
+ # grantee.revoke('READ') #=> true
661
+ # grantee.revoke('FULL_CONTROL','WRITE') #=> true
662
+ # grantee.revoke(['READ_ACP','WRITE_ACP']) #=> true
621
663
  #
622
- def revoke(permission)
623
- return true unless @perms.include?(permission)
624
- @perms -= permission.to_a
664
+ def revoke(*permissions)
665
+ permissions.flatten!
666
+ old_perms = @perms.dup
667
+ @perms -= permissions
668
+ @perms.uniq!
669
+ return true if @perms == old_perms
625
670
  apply
626
671
  end
627
672
 
@@ -664,10 +709,13 @@ module RightAws
664
709
  # grantee.apply #=> true
665
710
  #
666
711
  def apply
712
+ @perms.uniq!
667
713
  owner, grantees = self.class.owner_and_grantees(@thing)
668
- grantees.map! do |grantee|
669
- grantee.id == @id ? self : grantee
670
- end
714
+ # walk through all the grantees and replace the data for the current one and ...
715
+ grantees.map! { |grantee| grantee.id == @id ? self : grantee }
716
+ # ... if this grantee is not known - add this bad boy to a list
717
+ grantees << self unless grantees.include?(self)
718
+ # set permissions
671
719
  self.class.put_acl(@thing, owner, grantees)
672
720
  end
673
721
 
@@ -24,6 +24,9 @@
24
24
  module RightAws
25
25
 
26
26
  class S3Interface < RightAwsBase
27
+
28
+ USE_100_CONTINUE_PUT_SIZE = 1_000_000
29
+
27
30
  include RightAwsBaseInterface
28
31
 
29
32
  DEFAULT_HOST = 's3.amazonaws.com'
@@ -88,15 +91,44 @@ module RightAws
88
91
  # ignore everything after the question mark...
89
92
  out_string << path.gsub(/\?.*$/, '')
90
93
  # ...unless there is an acl or torrent parameter
91
- out_string << '?acl' if path[/[&?]acl($|&|=)/]
92
- out_string << '?torrent'if path[/[&?]torrent($|&|=)/]
94
+ out_string << '?acl' if path[/[&?]acl($|&|=)/]
95
+ out_string << '?torrent' if path[/[&?]torrent($|&|=)/]
96
+ out_string << '?location' if path[/[&?]location($|&|=)/]
97
+ # out_string << '?logging' if path[/[&?]logging($|&|=)/] # this one is beta, no support for now
93
98
  out_string
94
99
  end
95
100
 
101
+ def is_dns_bucket?(bucket_name)
102
+ bucket_name = bucket_name.to_s
103
+ return nil unless (3..63) === bucket_name.size
104
+ bucket_name.split('.').each do |component|
105
+ return nil unless component[/^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/]
106
+ end
107
+ true
108
+ end
109
+
96
110
  # Generates request hash for REST API.
111
+ # Assumes that headers[:url] is URL encoded (use CGI::escape)
97
112
  def generate_rest_request(method, headers) # :nodoc:
98
- path = headers[:url]
99
- path = "/#{path}" unless path[/^\//]
113
+ # default server to use
114
+ server = @params[:server]
115
+ # fix path
116
+ path_to_sign = headers[:url]
117
+ path_to_sign = "/#{path_to_sign}" unless path_to_sign[/^\//]
118
+ # extract bucket name and check it's dns compartibility
119
+ path_to_sign[%r{^/([a-z0-9._-]*)(/[^?]*)?(\?.+)?}i]
120
+ bucket_name, key_path, params_list = $1, $2, $3
121
+ # select request model
122
+ if is_dns_bucket?(bucket_name)
123
+ # add backet to a server name
124
+ server = "#{bucket_name}.#{server}"
125
+ # remove bucket from the path
126
+ path = "#{key_path || '/'}#{params_list}"
127
+ # refactor the path (add '/' before params_list if the key is empty)
128
+ path_to_sign = "/#{bucket_name}#{path}"
129
+ else
130
+ path = path_to_sign
131
+ end
100
132
  data = headers[:data]
101
133
  # remove unset(==optional) and symbolyc keys
102
134
  headers.each{ |key, value| headers.delete(key) if (value.nil? || key.is_a?(Symbol)) }
@@ -104,22 +136,22 @@ module RightAws
104
136
  headers['content-type'] ||= ''
105
137
  headers['date'] = Time.now.httpdate
106
138
  # create request
107
- request = "Net::HTTP::#{method.capitalize}".constantize.new(URI::escape(CGI::unescape(path)))
139
+ request = "Net::HTTP::#{method.capitalize}".constantize.new(path)
108
140
  request.body = data if data
109
141
  # set request headers and meta headers
110
142
  headers.each { |key, value| request[key.to_s] = value }
111
143
  #generate auth strings
112
- auth_string = canonical_string(request.method, request.path, request.to_hash)
144
+ auth_string = canonical_string(request.method, path_to_sign, request.to_hash)
113
145
  signature = AwsUtils::sign(@aws_secret_access_key, auth_string)
114
146
  # set other headers
115
147
  request['Authorization'] = "AWS #{@aws_access_key_id}:#{signature}"
116
148
  # prepare output hash
117
149
  { :request => request,
118
- :server => @params[:server],
150
+ :server => server,
119
151
  :port => @params[:port],
120
152
  :protocol => @params[:protocol] }
121
- end
122
-
153
+ end
154
+
123
155
  # Sends request to Amazon and parses the response.
124
156
  # Raises AwsError if any banana happened.
125
157
  def request_info(request, parser, &block) # :nodoc:
@@ -146,11 +178,34 @@ module RightAws
146
178
 
147
179
  # Creates new bucket. Returns +true+ or an exception.
148
180
  #
149
- # s3.create_bucket('my_awesome_bucket') #=> true
181
+ # # create a bucket at American server
182
+ # s3.create_bucket('my-awesome-bucket-us') #=> true
183
+ # # create a bucket at European server
184
+ # s3.create_bucket('my-awesome-bucket-eu', :location => :eu) #=> true
150
185
  #
151
186
  def create_bucket(bucket, headers={})
152
- req_hash = generate_rest_request('PUT', headers.merge(:url=>bucket))
187
+ data = nil
188
+ unless headers[:location].blank?
189
+ data = "<CreateBucketConfiguration><LocationConstraint>#{headers[:location].to_s.upcase}</LocationConstraint></CreateBucketConfiguration>"
190
+ end
191
+ req_hash = generate_rest_request('PUT', headers.merge(:url=>bucket, :data => data))
153
192
  request_info(req_hash, S3TrueParser.new)
193
+ rescue Exception => e
194
+ # if the bucket exists AWS returns an error for the location constraint interface. Drop it
195
+ e.is_a?(RightAws::AwsError) && e.message.include?('BucketAlreadyOwnedByYou') ? true : on_exception
196
+ end
197
+
198
+ # Retrieve bucket location
199
+ #
200
+ # s3.create_bucket('my-awesome-bucket-us') #=> true
201
+ # puts s3.bucket_location('my-awesome-bucket-us') #=> '' (Amazon's default value assumed)
202
+ #
203
+ # s3.create_bucket('my-awesome-bucket-eu', :location => :eu) #=> true
204
+ # puts s3.bucket_location('my-awesome-bucket-eu') #=> 'EU'
205
+ #
206
+ def bucket_location(bucket, headers={})
207
+ req_hash = generate_rest_request('GET', headers.merge(:url=>"#{bucket}?location"))
208
+ request_info(req_hash, S3BucketLocationParser.new)
154
209
  rescue
155
210
  on_exception
156
211
  end
@@ -220,7 +275,7 @@ module RightAws
220
275
  # ]
221
276
  # }
222
277
  def incrementally_list_bucket(bucket, options={}, headers={}, &block)
223
- internal_options = options.dup
278
+ internal_options = options.symbolize_keys
224
279
  begin
225
280
  internal_bucket = bucket.dup
226
281
  internal_bucket += '?'+internal_options.map{|k, v| "#{k.to_s}=#{CGI::escape v.to_s}"}.join('&') unless internal_options.blank?
@@ -229,11 +284,11 @@ module RightAws
229
284
  there_are_more_keys = response[:is_truncated]
230
285
  if(there_are_more_keys)
231
286
  if(response[:next_marker])
232
- internal_options['marker'] = response[:next_marker]
287
+ internal_options[:marker] = response[:next_marker]
233
288
  else
234
- internal_options['marker'] = response[:contents].last[:key]
289
+ internal_options[:marker] = response[:contents].last[:key]
235
290
  end
236
- internal_options['max-keys'] ? (internal_options['max-keys'] -= response[:contents].length) : nil
291
+ internal_options[:'max-keys'] ? (internal_options[:'max-keys'] -= response[:contents].length) : nil
237
292
  end
238
293
  yield response
239
294
  end while there_are_more_keys
@@ -242,12 +297,49 @@ module RightAws
242
297
  on_exception
243
298
  end
244
299
 
245
- # Saves object to Amazon. Returns +true+ or an exception.
246
- # Any header starting with AMAZON_METADATA_PREFIX is considered user metadata. It will be stored with the object and returned when you retrieve the object. The total size of the HTTP request, not including the body, must be less than 4 KB.
300
+ # Saves object to Amazon. Returns +true+ or an exception.
301
+ # Any header starting with AMAZON_METADATA_PREFIX is considered
302
+ # user metadata. It will be stored with the object and returned
303
+ # when you retrieve the object. The total size of the HTTP
304
+ # request, not including the body, must be less than 4 KB.
305
+ #
306
+ # s3.put('my_awesome_bucket', 'log/current/1.log', 'Ola-la!', 'x-amz-meta-family'=>'Woho556!') #=> true
307
+ #
308
+ # This method is capable of 'streaming' uploads; that is, it can upload
309
+ # data from a file or other IO object without first reading all the data
310
+ # into memory. This is most useful for large PUTs - it is difficult to read
311
+ # a 2 GB file entirely into memory before sending it to S3.
312
+ # To stream an upload, pass an object that responds to 'read' (like the read
313
+ # method of IO) and to either 'lstat' or 'size'. For files, this means
314
+ # streaming is enabled by simply making the call:
315
+ #
316
+ # s3.put(bucket_name, 'S3keyname.forthisfile', File.open('localfilename.dat'))
317
+ #
318
+ # If the IO object you wish to stream from responds to the read method but
319
+ # doesn't implement lstat or size, you can extend the object dynamically
320
+ # to implement these methods, or define your own class which defines these
321
+ # methods. Be sure that your class returns 'nil' from read() after having
322
+ # read 'size' bytes. Otherwise S3 will drop the socket after
323
+ # 'Content-Length' bytes have been uploaded, and HttpConnection will
324
+ # interpret this as an error.
247
325
  #
248
- # s3.put('my_awesome_bucket', 'log/curent/1.log', 'Ola-la!', 'x-amz-meta-family'=>'Woho556!') #=> true
326
+ # This method now supports very large PUTs, where very large
327
+ # is > 2 GB.
328
+ #
329
+ # For Win32 users: Files and IO objects should be opened in binary mode. If
330
+ # a text mode IO object is passed to PUT, it will be converted to binary
331
+ # mode.
249
332
  #
250
333
  def put(bucket, key, data=nil, headers={})
334
+ # On Windows, if someone opens a file in text mode, we must reset it so
335
+ # to binary mode for streaming to work properly
336
+ if(data.respond_to?(:binmode))
337
+ data.binmode
338
+ end
339
+ if (data.respond_to?(:lstat) && data.lstat.size >= USE_100_CONTINUE_PUT_SIZE) ||
340
+ (data.respond_to?(:size) && data.size >= USE_100_CONTINUE_PUT_SIZE)
341
+ headers['expect'] = '100-continue'
342
+ end
251
343
  req_hash = generate_rest_request('PUT', headers.merge(:url=>"#{bucket}/#{CGI::escape key}", :data=>data))
252
344
  request_info(req_hash, S3TrueParser.new)
253
345
  rescue
@@ -470,8 +562,25 @@ module RightAws
470
562
 
471
563
  # Generates link for QUERY API
472
564
  def generate_link(method, headers={}, expires=nil) #:nodoc:
473
- path = headers[:url]
474
- path = "/#{path}" unless path[/^\//]
565
+ # default server to use
566
+ server = @params[:server]
567
+ # fix path
568
+ path_to_sign = headers[:url]
569
+ path_to_sign = "/#{path_to_sign}" unless path_to_sign[/^\//]
570
+ # extract bucket name and check it's dns compartibility
571
+ path_to_sign[%r{^/([a-z0-9._-]*)(/[^?]*)?(\?.+)?}i]
572
+ bucket_name, key_path, params_list = $1, $2, $3
573
+ # select request model
574
+ if is_dns_bucket?(bucket_name)
575
+ # add backet to a server name
576
+ server = "#{bucket_name}.#{server}"
577
+ # remove bucket from the path
578
+ path = "#{key_path || '/'}#{params_list}"
579
+ # refactor the path (add '/' before params_list if the key is empty)
580
+ path_to_sign = "/#{bucket_name}#{path}"
581
+ else
582
+ path = path_to_sign
583
+ end
475
584
  # expiration time
476
585
  expires ||= DEFAULT_EXPIRES_AFTER
477
586
  expires = Time.now.utc.since(expires) if expires.is_a?(Fixnum) && (expires<1.year)
@@ -479,12 +588,12 @@ module RightAws
479
588
  # remove unset(==optional) and symbolyc keys
480
589
  headers.each{ |key, value| headers.delete(key) if (value.nil? || key.is_a?(Symbol)) }
481
590
  #generate auth strings
482
- auth_string = canonical_string(method, path, headers, expires)
591
+ auth_string = canonical_string(method, path_to_sign, headers, expires)
483
592
  signature = CGI::escape(Base64.encode64(OpenSSL::HMAC.digest(OpenSSL::Digest::Digest.new("sha1"), @aws_secret_access_key, auth_string)).strip)
484
593
  # path building
485
594
  addon = "Signature=#{signature}&Expires=#{expires}&AWSAccessKeyId=#{@aws_access_key_id}"
486
595
  path += path[/\?/] ? "&#{addon}" : "?#{addon}"
487
- "#{@params[:protocol]}://#{@params[:server]}:#{@params[:port]}#{path}"
596
+ "#{@params[:protocol]}://#{server}:#{@params[:port]}#{path}"
488
597
  rescue
489
598
  on_exception
490
599
  end
@@ -707,6 +816,48 @@ module RightAws
707
816
  end
708
817
  end
709
818
 
819
+ class S3BucketLocationParser < RightAWSParser # :nodoc:
820
+ def reset
821
+ @result = ''
822
+ end
823
+ def tagend(name)
824
+ @result = @text if name == 'LocationConstraint'
825
+ end
826
+ end
827
+
828
+ class S3AclParser < RightAWSParser # :nodoc:
829
+ def reset
830
+ @result = {:grantees=>[], :owner=>{}}
831
+ @current_grantee = {}
832
+ end
833
+ def tagstart(name, attributes)
834
+ @current_grantee = { :attributes => attributes } if name=='Grantee'
835
+ end
836
+ def tagend(name)
837
+ case name
838
+ # service info
839
+ when 'ID'
840
+ if @xmlpath == 'AccessControlPolicy/Owner'
841
+ @result[:owner][:id] = @text
842
+ else
843
+ @current_grantee[:id] = @text
844
+ end
845
+ when 'DisplayName'
846
+ if @xmlpath == 'AccessControlPolicy/Owner'
847
+ @result[:owner][:display_name] = @text
848
+ else
849
+ @current_grantee[:display_name] = @text
850
+ end
851
+ when 'URI'
852
+ @current_grantee[:uri] = @text
853
+ when 'Permission'
854
+ @current_grantee[:permissions] = @text
855
+ when 'Grant'
856
+ @result[:grantees] << @current_grantee
857
+ end
858
+ end
859
+ end
860
+
710
861
  #-----------------------------------------------------------------
711
862
  # PARSERS: Non XML
712
863
  #-----------------------------------------------------------------
@@ -746,40 +897,6 @@ module RightAws
746
897
  @result = headers_to_string(response.to_hash)
747
898
  end
748
899
  end
749
-
750
-
751
- class S3AclParser < RightAWSParser # :nodoc:
752
- def reset
753
- @result = {:grantees=>[], :owner=>{}}
754
- @current_grantee = {}
755
- end
756
- def tagstart(name, attributes)
757
- @current_grantee = { :attributes => attributes } if name=='Grantee'
758
- end
759
- def tagend(name)
760
- case name
761
- # service info
762
- when 'ID'
763
- if @xmlpath == 'AccessControlPolicy/Owner'
764
- @result[:owner][:id] = @text
765
- else
766
- @current_grantee[:id] = @text
767
- end
768
- when 'DisplayName'
769
- if @xmlpath == 'AccessControlPolicy/Owner'
770
- @result[:owner][:display_name] = @text
771
- else
772
- @current_grantee[:display_name] = @text
773
- end
774
- when 'URI'
775
- @current_grantee[:uri] = @text
776
- when 'Permission'
777
- @current_grantee[:permissions] = @text
778
- when 'Grant'
779
- @result[:grantees] << @current_grantee
780
- end
781
- end
782
- end
783
900
 
784
901
  end
785
902