surat-aws-s3 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (93) hide show
  1. data/COPYING +19 -0
  2. data/INSTALL +51 -0
  3. data/README +570 -0
  4. data/README.erb +64 -0
  5. data/Rakefile +334 -0
  6. data/bin/s3sh +6 -0
  7. data/bin/setup.rb +10 -0
  8. data/lib/aws/s3.rb +62 -0
  9. data/lib/aws/s3/acl.rb +636 -0
  10. data/lib/aws/s3/authentication.rb +221 -0
  11. data/lib/aws/s3/base.rb +240 -0
  12. data/lib/aws/s3/bittorrent.rb +58 -0
  13. data/lib/aws/s3/bucket.rb +341 -0
  14. data/lib/aws/s3/connection.rb +277 -0
  15. data/lib/aws/s3/content.rb +23 -0
  16. data/lib/aws/s3/error.rb +69 -0
  17. data/lib/aws/s3/exceptions.rb +133 -0
  18. data/lib/aws/s3/extensions.rb +356 -0
  19. data/lib/aws/s3/logging.rb +314 -0
  20. data/lib/aws/s3/object.rb +612 -0
  21. data/lib/aws/s3/owner.rb +44 -0
  22. data/lib/aws/s3/parsing.rb +99 -0
  23. data/lib/aws/s3/response.rb +188 -0
  24. data/lib/aws/s3/service.rb +51 -0
  25. data/lib/aws/s3/version.rb +12 -0
  26. data/lib/aws/s3/website.rb +102 -0
  27. data/site/index.erb +41 -0
  28. data/site/public/images/box-and-gem.gif +0 -0
  29. data/site/public/images/favicon.ico +0 -0
  30. data/site/public/ruby.css +18 -0
  31. data/site/public/screen.css +99 -0
  32. data/support/faster-xml-simple/COPYING +18 -0
  33. data/support/faster-xml-simple/README +8 -0
  34. data/support/faster-xml-simple/Rakefile +54 -0
  35. data/support/faster-xml-simple/lib/faster_xml_simple.rb +187 -0
  36. data/support/faster-xml-simple/test/fixtures/test-1.rails.yml +4 -0
  37. data/support/faster-xml-simple/test/fixtures/test-1.xml +3 -0
  38. data/support/faster-xml-simple/test/fixtures/test-1.yml +4 -0
  39. data/support/faster-xml-simple/test/fixtures/test-2.rails.yml +6 -0
  40. data/support/faster-xml-simple/test/fixtures/test-2.xml +3 -0
  41. data/support/faster-xml-simple/test/fixtures/test-2.yml +6 -0
  42. data/support/faster-xml-simple/test/fixtures/test-3.rails.yml +6 -0
  43. data/support/faster-xml-simple/test/fixtures/test-3.xml +5 -0
  44. data/support/faster-xml-simple/test/fixtures/test-3.yml +6 -0
  45. data/support/faster-xml-simple/test/fixtures/test-4.rails.yml +5 -0
  46. data/support/faster-xml-simple/test/fixtures/test-4.xml +7 -0
  47. data/support/faster-xml-simple/test/fixtures/test-4.yml +5 -0
  48. data/support/faster-xml-simple/test/fixtures/test-5.rails.yml +8 -0
  49. data/support/faster-xml-simple/test/fixtures/test-5.xml +7 -0
  50. data/support/faster-xml-simple/test/fixtures/test-5.yml +8 -0
  51. data/support/faster-xml-simple/test/fixtures/test-6.rails.yml +43 -0
  52. data/support/faster-xml-simple/test/fixtures/test-6.xml +29 -0
  53. data/support/faster-xml-simple/test/fixtures/test-6.yml +41 -0
  54. data/support/faster-xml-simple/test/fixtures/test-7.rails.yml +23 -0
  55. data/support/faster-xml-simple/test/fixtures/test-7.xml +22 -0
  56. data/support/faster-xml-simple/test/fixtures/test-7.yml +22 -0
  57. data/support/faster-xml-simple/test/fixtures/test-8.rails.yml +14 -0
  58. data/support/faster-xml-simple/test/fixtures/test-8.xml +8 -0
  59. data/support/faster-xml-simple/test/fixtures/test-8.yml +11 -0
  60. data/support/faster-xml-simple/test/regression_test.rb +47 -0
  61. data/support/faster-xml-simple/test/test_helper.rb +17 -0
  62. data/support/faster-xml-simple/test/xml_simple_comparison_test.rb +46 -0
  63. data/support/rdoc/code_info.rb +211 -0
  64. data/test/acl_test.rb +254 -0
  65. data/test/authentication_test.rb +114 -0
  66. data/test/base_test.rb +136 -0
  67. data/test/bucket_test.rb +74 -0
  68. data/test/connection_test.rb +215 -0
  69. data/test/error_test.rb +70 -0
  70. data/test/extensions_test.rb +340 -0
  71. data/test/fixtures.rb +89 -0
  72. data/test/fixtures/buckets.yml +133 -0
  73. data/test/fixtures/errors.yml +34 -0
  74. data/test/fixtures/headers.yml +3 -0
  75. data/test/fixtures/logging.yml +15 -0
  76. data/test/fixtures/loglines.yml +5 -0
  77. data/test/fixtures/logs.yml +7 -0
  78. data/test/fixtures/policies.yml +16 -0
  79. data/test/logging_test.rb +89 -0
  80. data/test/mocks/fake_response.rb +26 -0
  81. data/test/object_test.rb +220 -0
  82. data/test/parsing_test.rb +66 -0
  83. data/test/remote/acl_test.rb +117 -0
  84. data/test/remote/bittorrent_test.rb +45 -0
  85. data/test/remote/bucket_test.rb +146 -0
  86. data/test/remote/logging_test.rb +82 -0
  87. data/test/remote/object_test.rb +371 -0
  88. data/test/remote/test_file.data +0 -0
  89. data/test/remote/test_helper.rb +33 -0
  90. data/test/response_test.rb +68 -0
  91. data/test/service_test.rb +23 -0
  92. data/test/test_helper.rb +110 -0
  93. metadata +136 -0
@@ -0,0 +1,44 @@
1
+ module AWS
2
+ module S3
3
+ # Entities in S3 have an associated owner (the person who created them). The owner is a canonical representation of an
4
+ # entity in the S3 system. It has an <tt>id</tt> and a <tt>display_name</tt>.
5
+ #
6
+ # These attributes can be used when specifying a ACL::Grantee for an ACL::Grant.
7
+ #
8
+ # You can retrieve the owner of the current account by calling Owner.current.
9
+ class Owner
10
+ undef_method :id if method_defined?(:id) # Get rid of Object#id
11
+ include SelectiveAttributeProxy
12
+
13
+ class << self
14
+ # The owner of the current account.
15
+ def current
16
+ response = Service.get('/')
17
+ new(response.parsed['owner']) if response.parsed['owner']
18
+ end
19
+ memoized :current
20
+ end
21
+
22
+ def initialize(attributes = {}) #:nodoc:
23
+ @attributes = attributes
24
+ end
25
+
26
+ def ==(other_owner) #:nodoc:
27
+ hash == other_owner.hash
28
+ end
29
+
30
+ def hash #:nodoc
31
+ [id, display_name].join.hash
32
+ end
33
+
34
+ private
35
+ def proxiable_attribute?(name)
36
+ valid_attributes.include?(name)
37
+ end
38
+
39
+ def valid_attributes
40
+ %w(id display_name)
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,99 @@
1
+ #:stopdoc:
2
+ module AWS
3
+ module S3
4
+ module Parsing
5
+ class << self
6
+ def parser=(parsing_library)
7
+ XmlParser.parsing_library = parsing_library
8
+ end
9
+
10
+ def parser
11
+ XmlParser.parsing_library
12
+ end
13
+ end
14
+
15
+ module Typecasting
16
+ def typecast(object)
17
+ case object
18
+ when Hash
19
+ typecast_hash(object)
20
+ when Array
21
+ object.map {|element| typecast(element)}
22
+ when String
23
+ CoercibleString.coerce(object)
24
+ else
25
+ object
26
+ end
27
+ end
28
+
29
+ def typecast_hash(hash)
30
+ if content = hash['__content__']
31
+ typecast(content)
32
+ else
33
+ keys = hash.keys.map {|key| key.underscore}
34
+ values = hash.values.map {|value| typecast(value)}
35
+ keys.inject({}) do |new_hash, key|
36
+ new_hash[key] = values.slice!(0)
37
+ new_hash
38
+ end
39
+ end
40
+ end
41
+ end
42
+
43
+ class XmlParser < Hash
44
+ include Typecasting
45
+
46
+ class << self
47
+ attr_accessor :parsing_library
48
+ end
49
+
50
+ attr_reader :body, :xml_in, :root
51
+
52
+ def initialize(body)
53
+ @body = body
54
+ unless body.strip.empty?
55
+ parse
56
+ set_root
57
+ typecast_xml_in
58
+ end
59
+ end
60
+
61
+ private
62
+
63
+ def parse
64
+ @xml_in = self.class.parsing_library.xml_in(body, parsing_options)
65
+ end
66
+
67
+ def parsing_options
68
+ {
69
+ # Includes the enclosing tag as the top level key
70
+ 'keeproot' => true,
71
+ # Makes tag value available via the '__content__' key
72
+ 'contentkey' => '__content__',
73
+ # Always parse tags into a hash, even when there are no attributes
74
+ # (unless there is also no value, in which case it is nil)
75
+ 'forcecontent' => true,
76
+ # If a tag is empty, makes its content nil
77
+ 'suppressempty' => nil,
78
+ # Force nested elements to be put into an array, even if there is only one of them
79
+ 'forcearray' => ['Contents', 'Bucket', 'Grant']
80
+ }
81
+ end
82
+
83
+ def set_root
84
+ @root = @xml_in.keys.first.underscore
85
+ end
86
+
87
+ def typecast_xml_in
88
+ typecast_xml = {}
89
+ @xml_in.dup.each do |key, value| # Some typecasting is destructive so we dup
90
+ typecast_xml[key.underscore] = typecast(value)
91
+ end
92
+ # An empty body will try to update with a string so only update if the result is a hash
93
+ update(typecast_xml[root]) if typecast_xml[root].is_a?(Hash)
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
99
+ #:startdoc:
@@ -0,0 +1,188 @@
1
+ #:stopdoc:
2
+ module AWS
3
+ module S3
4
+ class Base
5
+ class Response < String
6
+ attr_reader :response, :body, :parsed
7
+ def initialize(response)
8
+ @response = response
9
+ @body = response.body.to_s
10
+ super(body)
11
+ end
12
+
13
+ def headers
14
+ headers = {}
15
+ response.each do |header, value|
16
+ headers[header] = value
17
+ end
18
+ headers
19
+ end
20
+ memoized :headers
21
+
22
+ def [](header)
23
+ headers[header]
24
+ end
25
+
26
+ def each(&block)
27
+ headers.each(&block)
28
+ end
29
+
30
+ def code
31
+ response.code.to_i
32
+ end
33
+
34
+ {:success => 200..299, :redirect => 300..399,
35
+ :client_error => 400..499, :server_error => 500..599}.each do |result, code_range|
36
+ class_eval(<<-EVAL, __FILE__, __LINE__)
37
+ def #{result}?
38
+ return false unless response
39
+ (#{code_range}).include? code
40
+ end
41
+ EVAL
42
+ end
43
+
44
+ def error?
45
+ !success? && response['content-type'] == 'application/xml' && parsed.root == 'error'
46
+ end
47
+
48
+ def error
49
+ Error.new(parsed, self)
50
+ end
51
+ memoized :error
52
+
53
+ def parsed
54
+ # XmlSimple is picky about what kind of object it parses, so we pass in body rather than self
55
+ Parsing::XmlParser.new(body)
56
+ end
57
+ memoized :parsed
58
+
59
+ def inspect
60
+ "#<%s:0x%s %s %s>" % [self.class, object_id, response.code, response.message]
61
+ end
62
+ end
63
+ end
64
+
65
+ class Bucket
66
+ class Response < Base::Response
67
+ def bucket
68
+ parsed
69
+ end
70
+ end
71
+ end
72
+
73
+ class Website
74
+ class Response < Base::Response
75
+ def website
76
+ parsed
77
+ end
78
+ end
79
+ end
80
+
81
+ class S3Object
82
+ class Response < Base::Response
83
+ def etag
84
+ headers['etag'][1...-1]
85
+ end
86
+ end
87
+ end
88
+
89
+ class Service
90
+ class Response < Base::Response
91
+ def empty?
92
+ parsed['buckets'].nil?
93
+ end
94
+
95
+ def buckets
96
+ parsed['buckets']['bucket'] || []
97
+ end
98
+ end
99
+ end
100
+
101
+ module ACL
102
+ class Policy
103
+ class Response < Base::Response
104
+ alias_method :policy, :parsed
105
+ end
106
+ end
107
+ end
108
+
109
+ # Requests whose response code is between 300 and 599 and contain an <Error></Error> in their body
110
+ # are wrapped in an Error::Response. This Error::Response contains an Error object which raises an exception
111
+ # that corresponds to the error in the response body. The exception object contains the ErrorResponse, so
112
+ # in all cases where a request happens, you can rescue ResponseError and have access to the ErrorResponse and
113
+ # its Error object which contains information about the ResponseError.
114
+ #
115
+ # begin
116
+ # Bucket.create(..)
117
+ # rescue ResponseError => exception
118
+ # exception.response
119
+ # # => <Error::Response>
120
+ # exception.response.error
121
+ # # => <Error>
122
+ # end
123
+ class Error
124
+ class Response < Base::Response
125
+ def error?
126
+ true
127
+ end
128
+
129
+ def inspect
130
+ "#<%s:0x%s %s %s: '%s'>" % [self.class.name, object_id, response.code, error.code, error.message]
131
+ end
132
+ end
133
+ end
134
+
135
+ # Guess response class name from current class name. If the guessed response class doesn't exist
136
+ # do the same thing to the current class's parent class, up the inheritance heirarchy until either
137
+ # a response class is found or until we get to the top of the heirarchy in which case we just use
138
+ # the the Base response class.
139
+ #
140
+ # Important: This implemantation assumes that the Base class has a corresponding Base::Response.
141
+ class FindResponseClass #:nodoc:
142
+ class << self
143
+ def for(start)
144
+ new(start).find
145
+ end
146
+ end
147
+
148
+ def initialize(start)
149
+ @container = AWS::S3
150
+ @current_class = start
151
+ end
152
+
153
+ def find
154
+ self.current_class = current_class.superclass until response_class_found?
155
+ target.const_get(class_to_find)
156
+ end
157
+
158
+ private
159
+ attr_reader :container
160
+ attr_accessor :current_class
161
+
162
+ def target
163
+ container.const_get(current_name)
164
+ end
165
+
166
+ def target?
167
+ container.const_defined?(current_name)
168
+ end
169
+
170
+ def response_class_found?
171
+ target? && target.const_defined?(class_to_find)
172
+ end
173
+
174
+ def class_to_find
175
+ :Response
176
+ end
177
+
178
+ def current_name
179
+ truncate(current_class)
180
+ end
181
+
182
+ def truncate(klass)
183
+ klass.name[/[^:]+$/]
184
+ end
185
+ end
186
+ end
187
+ end
188
+ #:startdoc:
@@ -0,0 +1,51 @@
1
+ module AWS
2
+ module S3
3
+ # The service lets you find out general information about your account, like what buckets you have.
4
+ #
5
+ # Service.buckets
6
+ # # => []
7
+ class Service < Base
8
+ @@response = nil #:nodoc:
9
+
10
+ class << self
11
+ # List all your buckets.
12
+ #
13
+ # Service.buckets
14
+ # # => []
15
+ #
16
+ # For performance reasons, the bucket list will be cached. If you want avoid all caching, pass the <tt>:reload</tt>
17
+ # as an argument:
18
+ #
19
+ # Service.buckets(:reload)
20
+ def buckets
21
+ response = get('/')
22
+ if response.empty?
23
+ []
24
+ else
25
+ response.buckets.map {|attributes| Bucket.new(attributes)}
26
+ end
27
+ end
28
+ memoized :buckets
29
+
30
+ # Sometimes methods that make requests to the S3 servers return some object, like a Bucket or an S3Object.
31
+ # Othertimes they return just <tt>true</tt>. Other times they raise an exception that you may want to rescue. Despite all these
32
+ # possible outcomes, every method that makes a request stores its response object for you in Service.response. You can always
33
+ # get to the last request's response via Service.response.
34
+ #
35
+ # objects = Bucket.objects('jukebox')
36
+ # Service.response.success?
37
+ # # => true
38
+ #
39
+ # This is also useful when an error exception is raised in the console which you weren't expecting. You can
40
+ # root around in the response to get more details of what might have gone wrong.
41
+ def response
42
+ @@response
43
+ end
44
+
45
+ def response=(response) #:nodoc:
46
+ @@response = response
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,12 @@
1
+ module AWS
2
+ module S3
3
+ module VERSION #:nodoc:
4
+ MAJOR = '0'
5
+ MINOR = '6'
6
+ TINY = '2'
7
+ BETA = nil #Time.now.to_i.to_s
8
+ end
9
+
10
+ Version = [VERSION::MAJOR, VERSION::MINOR, VERSION::TINY, VERSION::BETA].compact * '.'
11
+ end
12
+ end
@@ -0,0 +1,102 @@
1
+ require File.dirname(__FILE__) + "/content"
2
+ module AWS
3
+ module S3
4
+ # To enable a bucket as a website you just specify its name.
5
+ #
6
+ # # Pick a existing bucket name, or else you'll get an error
7
+ # Website.create('jukebox')
8
+ # By default index document is "index.html" and error document is "error.html"
9
+ #
10
+ # If Its different you can do
11
+ # Website.create('jukebox', "about.html", "404.html")
12
+ #
13
+ #
14
+ # Once you have succesfully enabled as website you can you can fetch it by name using Website.find.
15
+ #
16
+ # music_website = Website.find('jukebox')
17
+ #
18
+ # The bucket that is not website enabled will will throw an error.
19
+ #
20
+ # You can remove website from bucket using Website.delete.
21
+ #
22
+ # Website.delete('jukebox')
23
+
24
+ class Website < Base
25
+
26
+ class Builder < XmlGenerator #:nodoc:
27
+ attr_reader :index_page, :error_page
28
+ def initialize(index_page, error_page)
29
+ @index_page = index_page
30
+ @error_page = error_page
31
+ super()
32
+ end
33
+
34
+ def build
35
+ xml.tag!('WebsiteConfiguration', 'xmlns' => 'http://s3.amazonaws.com/doc/2006-03-01/') do
36
+ xml.IndexDocument do
37
+ xml.Suffix index_page
38
+ end
39
+ xml.ErrorDocument do
40
+ xml.Key error_page
41
+ end
42
+ end
43
+ end
44
+ end
45
+
46
+ class << self
47
+ # To enable a bucket as a website you just specify its name.
48
+ #
49
+ # # Pick a existing bucket name, or else you'll get an error
50
+ # Website.create('jukebox')
51
+ # By default index document is "index.html" and error document is "error.html"
52
+ #
53
+ # If Its different you can do
54
+ # Website.create('jukebox', "about.html", "404.html")
55
+
56
+ def create(name=nil, index_page="index.html", error_page="error.html")
57
+ put(path(name), {}, Builder.new(index_page, error_page).to_s).success?
58
+ end
59
+
60
+ # Fetches if a bucket is website enabled.
61
+ #
62
+ # website=Website.find('jukebox')
63
+ #
64
+ # website.index_doc
65
+ # => 'index.html'
66
+ # website.error_doc
67
+ # => 'error.html'
68
+ def find(name = nil)
69
+ new(get(path(name)).website)
70
+ end
71
+
72
+ # disables a bucket aswebsite.
73
+ # Website.delete('photos')
74
+ def delete(name = nil, options = {})
75
+ Base.delete(path(name)).success?
76
+ end
77
+
78
+ private
79
+
80
+ def path(name, options = {})
81
+ if name.is_a?(Hash)
82
+ options = name
83
+ name = nil
84
+ end
85
+ # "/#{website_name(name)}#{RequestOptions.process(options).to_query_string}"
86
+ "/#{name}/?website"
87
+ end
88
+
89
+ include Content
90
+ end
91
+
92
+ def index_doc
93
+ self.index_document["suffix"]
94
+ end
95
+
96
+ def error_doc
97
+ self.error_document["key"]
98
+ end
99
+
100
+ end
101
+ end
102
+ end