aliyun-oss-rails4 0.7.0.1448446959

Sign up to get free protection for your applications and to get access to all the features.
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 +364 -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