aliyun-oss-ex 0.7.0.1402831795

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 (57) hide show
  1. checksums.yaml +7 -0
  2. data/COPYING +19 -0
  3. data/INSTALL +35 -0
  4. data/README +443 -0
  5. data/Rakefile +334 -0
  6. data/bin/oss +6 -0
  7. data/bin/setup.rb +11 -0
  8. data/lib/aliyun/oss.rb +55 -0
  9. data/lib/aliyun/oss/acl.rb +132 -0
  10. data/lib/aliyun/oss/authentication.rb +222 -0
  11. data/lib/aliyun/oss/base.rb +241 -0
  12. data/lib/aliyun/oss/bucket.rb +320 -0
  13. data/lib/aliyun/oss/connection.rb +279 -0
  14. data/lib/aliyun/oss/error.rb +70 -0
  15. data/lib/aliyun/oss/exceptions.rb +134 -0
  16. data/lib/aliyun/oss/extensions.rb +405 -0
  17. data/lib/aliyun/oss/logging.rb +304 -0
  18. data/lib/aliyun/oss/object.rb +612 -0
  19. data/lib/aliyun/oss/owner.rb +45 -0
  20. data/lib/aliyun/oss/parsing.rb +100 -0
  21. data/lib/aliyun/oss/response.rb +181 -0
  22. data/lib/aliyun/oss/service.rb +52 -0
  23. data/lib/aliyun/oss/version.rb +14 -0
  24. data/support/faster-xml-simple/lib/faster_xml_simple.rb +188 -0
  25. data/support/faster-xml-simple/test/regression_test.rb +48 -0
  26. data/support/faster-xml-simple/test/test_helper.rb +18 -0
  27. data/support/faster-xml-simple/test/xml_simple_comparison_test.rb +47 -0
  28. data/support/rdoc/code_info.rb +212 -0
  29. data/test/acl_test.rb +70 -0
  30. data/test/authentication_test.rb +114 -0
  31. data/test/base_test.rb +137 -0
  32. data/test/bucket_test.rb +75 -0
  33. data/test/connection_test.rb +218 -0
  34. data/test/error_test.rb +71 -0
  35. data/test/extensions_test.rb +346 -0
  36. data/test/fixtures.rb +90 -0
  37. data/test/fixtures/buckets.yml +133 -0
  38. data/test/fixtures/errors.yml +34 -0
  39. data/test/fixtures/headers.yml +3 -0
  40. data/test/fixtures/logging.yml +15 -0
  41. data/test/fixtures/loglines.yml +5 -0
  42. data/test/fixtures/logs.yml +7 -0
  43. data/test/fixtures/policies.yml +16 -0
  44. data/test/logging_test.rb +90 -0
  45. data/test/mocks/fake_response.rb +27 -0
  46. data/test/object_test.rb +221 -0
  47. data/test/parsing_test.rb +67 -0
  48. data/test/remote/acl_test.rb +28 -0
  49. data/test/remote/bucket_test.rb +147 -0
  50. data/test/remote/logging_test.rb +86 -0
  51. data/test/remote/object_test.rb +350 -0
  52. data/test/remote/test_file.data +0 -0
  53. data/test/remote/test_helper.rb +34 -0
  54. data/test/response_test.rb +69 -0
  55. data/test/service_test.rb +24 -0
  56. data/test/test_helper.rb +110 -0
  57. metadata +177 -0
@@ -0,0 +1,45 @@
1
+ # -*- encoding : utf-8 -*-
2
+ module Aliyun
3
+ module OSS
4
+ # Entities in OSS have an associated owner (the person who created them). The owner is a canonical representation of an
5
+ # entity in the OSS system. It has an <tt>id</tt> and a <tt>display_name</tt>.
6
+ #
7
+ # These attributes can be used when specifying a ACL::Grantee for an ACL::Grant.
8
+ #
9
+ # You can retrieve the owner of the current account by calling Owner.current.
10
+ class Owner
11
+ undef_method :id if method_defined?(:id) # Get rid of Object#id
12
+ include SelectiveAttributeProxy
13
+
14
+ class << self
15
+ # The owner of the current account.
16
+ def current
17
+ response = Service.get('/')
18
+ new(response.parsed['owner']) if response.parsed['owner']
19
+ end
20
+ memoized :current
21
+ end
22
+
23
+ def initialize(attributes = {}) #:nodoc:
24
+ @attributes = attributes
25
+ end
26
+
27
+ def ==(other_owner) #:nodoc:
28
+ hash == other_owner.hash
29
+ end
30
+
31
+ def hash #:nodoc
32
+ [id, display_name].join.hash
33
+ end
34
+
35
+ private
36
+ def proxiable_attribute?(name)
37
+ valid_attributes.include?(name)
38
+ end
39
+
40
+ def valid_attributes
41
+ %w(id display_name)
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,100 @@
1
+ # -*- encoding : utf-8 -*-
2
+ #:stopdoc:
3
+ module Aliyun
4
+ module OSS
5
+ module Parsing
6
+ class << self
7
+ def parser=(parsing_library)
8
+ XmlParser.parsing_library = parsing_library
9
+ end
10
+
11
+ def parser
12
+ XmlParser.parsing_library
13
+ end
14
+ end
15
+
16
+ module Typecasting
17
+ def typecast(object)
18
+ case object
19
+ when Hash
20
+ typecast_hash(object)
21
+ when Array
22
+ object.map {|element| typecast(element)}
23
+ when String
24
+ CoercibleString.coerce(object)
25
+ else
26
+ object
27
+ end
28
+ end
29
+
30
+ def typecast_hash(hash)
31
+ if content = hash['__content__']
32
+ typecast(content)
33
+ else
34
+ keys = hash.keys.map {|key| key.underscore}
35
+ values = hash.values.map {|value| typecast(value)}
36
+ keys.inject({}) do |new_hash, key|
37
+ new_hash[key] = values.slice!(0)
38
+ new_hash
39
+ end
40
+ end
41
+ end
42
+ end
43
+
44
+ class XmlParser < Hash
45
+ include Typecasting
46
+
47
+ class << self
48
+ attr_accessor :parsing_library
49
+ end
50
+
51
+ attr_reader :body, :xml_in, :root
52
+
53
+ def initialize(body)
54
+ @body = body
55
+ unless body.strip.empty?
56
+ parse
57
+ set_root
58
+ typecast_xml_in
59
+ end
60
+ end
61
+
62
+ private
63
+
64
+ def parse
65
+ @xml_in = self.class.parsing_library.xml_in(body, parsing_options)
66
+ end
67
+
68
+ def parsing_options
69
+ {
70
+ # Includes the enclosing tag as the top level key
71
+ 'keeproot' => true,
72
+ # Makes tag value available via the '__content__' key
73
+ 'contentkey' => '__content__',
74
+ # Always parse tags into a hash, even when there are no attributes
75
+ # (unless there is also no value, in which case it is nil)
76
+ 'forcecontent' => true,
77
+ # If a tag is empty, makes its content nil
78
+ 'suppressempty' => nil,
79
+ # Force nested elements to be put into an array, even if there is only one of them
80
+ 'forcearray' => ['Contents', 'Bucket', 'Grant']
81
+ }
82
+ end
83
+
84
+ def set_root
85
+ @root = @xml_in.keys.first.underscore
86
+ end
87
+
88
+ def typecast_xml_in
89
+ typecast_xml = {}
90
+ @xml_in.dup.each do |key, value| # Some typecasting is destructive so we dup
91
+ typecast_xml[key.underscore] = typecast(value)
92
+ end
93
+ # An empty body will try to update with a string so only update if the result is a hash
94
+ update(typecast_xml[root]) if typecast_xml[root].is_a?(Hash)
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
100
+ #:startdoc:
@@ -0,0 +1,181 @@
1
+ # -*- encoding : utf-8 -*-
2
+ #:stopdoc:
3
+ module Aliyun
4
+ module OSS
5
+ class Base
6
+ class Response < String
7
+ attr_reader :response, :body, :parsed
8
+ def initialize(response)
9
+ @response = response
10
+ @body = response.body.to_s
11
+ super(body)
12
+ end
13
+
14
+ def headers
15
+ headers = {}
16
+ response.each do |header, value|
17
+ headers[header] = value
18
+ end
19
+ headers
20
+ end
21
+ memoized :headers
22
+
23
+ def [](header)
24
+ headers[header]
25
+ end
26
+
27
+ def each(&block)
28
+ headers.each(&block)
29
+ end
30
+
31
+ def code
32
+ response.code.to_i
33
+ end
34
+
35
+ {:success => 200..299, :redirect => 300..399,
36
+ :client_error => 400..499, :server_error => 500..599}.each do |result, code_range|
37
+ class_eval(<<-EVAL, __FILE__, __LINE__)
38
+ def #{result}?
39
+ return false unless response
40
+ (#{code_range}).include? code
41
+ end
42
+ EVAL
43
+ end
44
+
45
+ def error?
46
+ !success? && response['content-type'] == 'application/xml' && parsed.root == 'error'
47
+ end
48
+
49
+ def error
50
+ Error.new(parsed, self)
51
+ end
52
+ memoized :error
53
+
54
+ def parsed
55
+ # XmlSimple is picky about what kind of object it parses, so we pass in body rather than self
56
+ Parsing::XmlParser.new(body)
57
+ end
58
+ memoized :parsed
59
+
60
+ def inspect
61
+ "#<%s:0x%s %s %s>" % [self.class, object_id, response.code, response.message]
62
+ end
63
+ end
64
+ end
65
+
66
+ class Bucket
67
+ class Response < Base::Response
68
+ def bucket
69
+ parsed
70
+ end
71
+ end
72
+ end
73
+
74
+ class OSSObject
75
+ class Response < Base::Response
76
+ def etag
77
+ headers['etag'][1...-1]
78
+ end
79
+ end
80
+ end
81
+
82
+ class Service
83
+ class Response < Base::Response
84
+ def empty?
85
+ parsed['buckets'].nil?
86
+ end
87
+
88
+ def buckets
89
+ parsed['buckets']['bucket'] || []
90
+ end
91
+ end
92
+ end
93
+
94
+ module ACL
95
+ class Policy
96
+ class Response < Base::Response
97
+ alias_method :policy, :parsed
98
+ end
99
+ end
100
+ end
101
+
102
+ # Requests whose response code is between 300 and 599 and contain an <Error></Error> in their body
103
+ # are wrapped in an Error::Response. This Error::Response contains an Error object which raises an exception
104
+ # that corresponds to the error in the response body. The exception object contains the ErrorResponse, so
105
+ # in all cases where a request happens, you can rescue ResponseError and have access to the ErrorResponse and
106
+ # its Error object which contains information about the ResponseError.
107
+ #
108
+ # begin
109
+ # Bucket.create(..)
110
+ # rescue ResponseError => exception
111
+ # exception.response
112
+ # # => <Error::Response>
113
+ # exception.response.error
114
+ # # => <Error>
115
+ # end
116
+ class Error
117
+ class Response < Base::Response
118
+ def error?
119
+ true
120
+ end
121
+
122
+ def inspect
123
+ "#<%s:0x%s %s %s: '%s'>" % [self.class.name, object_id, response.code, error.code, error.message]
124
+ end
125
+ end
126
+ end
127
+
128
+ # Guess response class name from current class name. If the guessed response class doesn't exist
129
+ # do the same thing to the current class's parent class, up the inheritance heirarchy until either
130
+ # a response class is found or until we get to the top of the heirarchy in which case we just use
131
+ # the the Base response class.
132
+ #
133
+ # Important: This implemantation assumes that the Base class has a corresponding Base::Response.
134
+ class FindResponseClass #:nodoc:
135
+ class << self
136
+ def for(start)
137
+ new(start).find
138
+ end
139
+ end
140
+
141
+ def initialize(start)
142
+ @container = Aliyun::OSS
143
+ @current_class = start
144
+ end
145
+
146
+ def find
147
+ self.current_class = current_class.superclass until response_class_found?
148
+ target.const_get(class_to_find)
149
+ end
150
+
151
+ private
152
+ attr_reader :container
153
+ attr_accessor :current_class
154
+
155
+ def target
156
+ container.const_get(current_name)
157
+ end
158
+
159
+ def target?
160
+ container.const_defined?(current_name)
161
+ end
162
+
163
+ def response_class_found?
164
+ target? && target.const_defined?(class_to_find)
165
+ end
166
+
167
+ def class_to_find
168
+ :Response
169
+ end
170
+
171
+ def current_name
172
+ truncate(current_class)
173
+ end
174
+
175
+ def truncate(klass)
176
+ klass.name[/[^:]+$/]
177
+ end
178
+ end
179
+ end
180
+ end
181
+ #:startdoc:
@@ -0,0 +1,52 @@
1
+ # -*- encoding : utf-8 -*-
2
+ module Aliyun
3
+ module OSS
4
+ # The service lets you find out general information about your account, like what buckets you have.
5
+ #
6
+ # Service.buckets
7
+ # # => []
8
+ class Service < Base
9
+ @@response = nil #:nodoc:
10
+
11
+ class << self
12
+ # List all your buckets.
13
+ #
14
+ # Service.buckets
15
+ # # => []
16
+ #
17
+ # For performance reasons, the bucket list will be cached. If you want avoid all caching, pass the <tt>:reload</tt>
18
+ # as an argument:
19
+ #
20
+ # Service.buckets(:reload)
21
+ def buckets
22
+ response = get('/')
23
+ if response.empty?
24
+ []
25
+ else
26
+ response.buckets.map {|attributes| Bucket.new(attributes)}
27
+ end
28
+ end
29
+ memoized :buckets
30
+
31
+ # Sometimes methods that make requests to the OSS servers return some object, like a Bucket or an OSSObject.
32
+ # Other times they return just <tt>true</tt>. Other times they raise an exception that you may want to rescue. Despite all these
33
+ # possible outcomes, every method that makes a request stores its response object for you in Service.response. You can always
34
+ # get to the last request's response via Service.response.
35
+ #
36
+ # objects = Bucket.objects('jukebox')
37
+ # Service.response.success?
38
+ # # => true
39
+ #
40
+ # This is also useful when an error exception is raised in the console which you weren't expecting. You can
41
+ # root around in the response to get more details of what might have gone wrong.
42
+ def response
43
+ @@response
44
+ end
45
+
46
+ def response=(response) #:nodoc:
47
+ @@response = response
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,14 @@
1
+ # -*- encoding : utf-8 -*-
2
+ module Aliyun
3
+ module OSS
4
+ module VERSION #:nodoc:
5
+ MAJOR = '0'
6
+ MINOR = '7'
7
+ TINY = '0'
8
+ #BETA = nil # Time.now.to_i.to_s
9
+ BETA = Time.now.to_i.to_s
10
+ end
11
+
12
+ Version = [VERSION::MAJOR, VERSION::MINOR, VERSION::TINY, VERSION::BETA].compact * '.'
13
+ end
14
+ end
@@ -0,0 +1,188 @@
1
+ # -*- encoding : utf-8 -*-
2
+ #
3
+ # Copyright (c) 2006 Michael Koziarski
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ # this software and associated documentation files (the "Software"), to deal in the
7
+ # Software without restriction, including without limitation the rights to use,
8
+ # copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
9
+ # Software, and to permit persons to whom the Software is furnished to do so,
10
+ # subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in all
13
+ # copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
+ # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
19
+ # AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
22
+ require 'rubygems'
23
+ require 'xml/libxml'
24
+
25
+ class FasterXmlSimple
26
+ Version = '0.5.0'
27
+ class << self
28
+ # Take an string containing XML, and returns a hash representing that
29
+ # XML document. For example:
30
+ #
31
+ # FasterXmlSimple.xml_in("<root><something>1</something></root>")
32
+ # {"root"=>{"something"=>{"__content__"=>"1"}}}
33
+ #
34
+ # Faster XML Simple is designed to be a drop in replacement for the xml_in
35
+ # functionality of http://xml-simple.rubyforge.org
36
+ #
37
+ # The following options are supported:
38
+ #
39
+ # * <tt>contentkey</tt>: The key to use for the content of text elements,
40
+ # defaults to '\_\_content__'
41
+ # * <tt>forcearray</tt>: The list of elements which should always be returned
42
+ # as arrays. Under normal circumstances single element arrays are inlined.
43
+ # * <tt>suppressempty</tt>: The value to return for empty elements, pass +true+
44
+ # to remove empty elements entirely.
45
+ # * <tt>keeproot</tt>: By default the hash returned has a single key with the
46
+ # name of the root element. If the name of the root element isn't
47
+ # interesting to you, pass +false+.
48
+ # * <tt>forcecontent</tt>: By default a text element with no attributes, will
49
+ # be collapsed to just a string instead of a hash with a single key.
50
+ # Pass +true+ to prevent this.
51
+ #
52
+ #
53
+ def xml_in(string, options={})
54
+ new(string, options).out
55
+ end
56
+ end
57
+
58
+ def initialize(string, options) #:nodoc:
59
+ @doc = parse(string)
60
+ @options = default_options.merge options
61
+ end
62
+
63
+ def out #:nodoc:
64
+ if @options['keeproot']
65
+ {@doc.root.name => collapse(@doc.root)}
66
+ else
67
+ collapse(@doc.root)
68
+ end
69
+ end
70
+
71
+ private
72
+ def default_options
73
+ {'contentkey' => '__content__', 'forcearray' => [], 'keeproot'=>true}
74
+ end
75
+
76
+ def collapse(element)
77
+ result = hash_of_attributes(element)
78
+ if text_node? element
79
+ text = collapse_text(element)
80
+ result[content_key] = text if text =~ /\S/
81
+ elsif element.children?
82
+ element.inject(result) do |hash, child|
83
+ unless child.text?
84
+ child_result = collapse(child)
85
+ (hash[child.name] ||= []) << child_result
86
+ end
87
+ hash
88
+ end
89
+ end
90
+ if result.empty?
91
+ return empty_element
92
+ end
93
+ # Compact them to ensure it complies with the user's requests
94
+ inline_single_element_arrays(result)
95
+ remove_empty_elements(result) if suppress_empty?
96
+ if content_only?(result) && !force_content?
97
+ result[content_key]
98
+ else
99
+ result
100
+ end
101
+ end
102
+
103
+ def content_only?(result)
104
+ result.keys == [content_key]
105
+ end
106
+
107
+ def content_key
108
+ @options['contentkey']
109
+ end
110
+
111
+ def force_array?(key_name)
112
+ Array(@options['forcearray']).include?(key_name)
113
+ end
114
+
115
+ def inline_single_element_arrays(result)
116
+ result.each do |key, value|
117
+ if value.size == 1 && value.is_a?(Array) && !force_array?(key)
118
+ result[key] = value.first
119
+ end
120
+ end
121
+ end
122
+
123
+ def remove_empty_elements(result)
124
+ result.each do |key, value|
125
+ if value == empty_element
126
+ result.delete key
127
+ end
128
+ end
129
+ end
130
+
131
+ def suppress_empty?
132
+ @options['suppressempty'] == true
133
+ end
134
+
135
+ def empty_element
136
+ if !@options.has_key? 'suppressempty'
137
+ {}
138
+ else
139
+ @options['suppressempty']
140
+ end
141
+ end
142
+
143
+ # removes the content if it's nothing but blanks, prevents
144
+ # the hash being polluted with lots of content like "\n\t\t\t"
145
+ def suppress_empty_content(result)
146
+ result.delete content_key if result[content_key] !~ /\S/
147
+ end
148
+
149
+ def force_content?
150
+ @options['forcecontent']
151
+ end
152
+
153
+ # a text node is one with 1 or more child nodes which are
154
+ # text nodes, and no non-text children, there's no sensible
155
+ # way to support nodes which are text and markup like:
156
+ # <p>Something <b>Bold</b> </p>
157
+ def text_node?(element)
158
+ !element.text? && element.all? {|c| c.text?}
159
+ end
160
+
161
+ # takes a text node, and collapses it into a string
162
+ def collapse_text(element)
163
+ element.map {|c| c.content } * ''
164
+ end
165
+
166
+ def hash_of_attributes(element)
167
+ result = {}
168
+ element.each_attr do |attribute|
169
+ name = attribute.name
170
+ name = [attribute.ns, attribute.name].join(':') if attribute.ns?
171
+ result[name] = attribute.value
172
+ end
173
+ result
174
+ end
175
+
176
+ def parse(string)
177
+ if string == ''
178
+ string = ' '
179
+ end
180
+ XML::Parser.string(string).parse
181
+ end
182
+ end
183
+
184
+ class XmlSimple # :nodoc:
185
+ def self.xml_in(*args)
186
+ FasterXmlSimple.xml_in *args
187
+ end
188
+ end