ruby-atmos-pure 1.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,28 @@
1
+ module Atmos
2
+ module Exceptions
3
+
4
+ class AtmosException < Exception
5
+ end
6
+
7
+ class ArgumentException < AtmosException
8
+ end
9
+
10
+ class AuthException < AtmosException
11
+ end
12
+
13
+ class InvalidStateException < AtmosException
14
+ end
15
+
16
+ class NoSuchObjectException < AtmosException
17
+ end
18
+
19
+ class ServerException < AtmosException
20
+ end
21
+
22
+ class NotImplementedException < AtmosException
23
+ end
24
+
25
+ class InternalLibraryException < AtmosException
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,321 @@
1
+ module Atmos
2
+
3
+ #
4
+ # == Object
5
+ # This class represents an object in an Atmos store.
6
+ #
7
+ # === Object Data
8
+ # When an object is instantiated, it's data is not loaded
9
+ # due to memory considerations. You can access an object's
10
+ # data as a ruby String or as a progressive download via a block.
11
+ #
12
+ # ==== all at once
13
+ #
14
+ # obj.data
15
+ #
16
+ # ==== progressive download
17
+ #
18
+ # obj.data_as_stream do |chunk|
19
+ # datafile.write(chunk)
20
+ # end
21
+ #
22
+ # ==== partial data
23
+ #
24
+ # obj.data_as_stream(0...59) do |chunk|
25
+ # readin.concat(chunk)
26
+ # end
27
+ #
28
+ #
29
+ # === System Metadata
30
+ # Each object has some information about it stored by the system.
31
+ # This information is read only, and available as a hash on the object:
32
+ #
33
+ # obj.system_metadata => Hash
34
+ #
35
+ # obj.system_metadata.each do |key,value|
36
+ # puts "#{key}=#{value}"
37
+ # end
38
+ #
39
+ # See Atmos::Metadata for more detailed information.
40
+ #
41
+ #
42
+ # === User Metadata
43
+ # There are two kinds of user metadata, listable and non-listable.
44
+ # Each of these is available as a hash
45
+ # on the object class. These can both be modified.
46
+ #
47
+ # obj.listable_metadata => Hash
48
+ #
49
+ # obj.listable_metadata.each do |key,value|
50
+ # puts "#{key}=#{value}"
51
+ # end
52
+ #
53
+ # obj.metadata => Hash
54
+ #
55
+ # obj.metadata.each do |key,value|
56
+ # puts "#{key}=#{value}"
57
+ # end
58
+ #
59
+ # See Atmos::Metadata for more detailed information.
60
+ #
61
+ #
62
+ # === Access Control Lists (ACLs)
63
+ #
64
+ # There are two hashes for access control available as properties
65
+ # on the object: +user_acl+ and +group_acl+.
66
+ # The keys are the Atmos usernames and the values are one of
67
+ # <tt>:none</tt>, <tt>:read</tt>, <tt>:write</tt>, <tt>:full</tt>.
68
+ #
69
+ # puts obj.user_acl.inspect => {user => :full}
70
+ # puts obj.group_acl.inspect => {other => :none}
71
+ #
72
+ # See Atmos::ACL for more detailed information.
73
+ #
74
+ #
75
+ class Object
76
+ attr_reader :aoid, :request, :user # :nodoc:
77
+
78
+ @request = nil
79
+ @checksum = nil
80
+ @user_acl = nil
81
+ @group_acl = nil
82
+ @metadata = nil
83
+ @system_metadata = nil
84
+ @listable_metadata = nil
85
+
86
+
87
+ #
88
+ # This constructor is only meant for internal use. Get or create an object
89
+ # with an Atmos::Store object:
90
+ #
91
+ # obj = store.create
92
+ # obj = store.get(:id => obj_id)
93
+ # obj = store.get(:namespace => obj_id)
94
+ #
95
+ def initialize(store, action, options = {})
96
+ Atmos::LOG.debug("obj.new options: #{options.inspect}")
97
+ validate_options(options)
98
+
99
+ @deleted = false
100
+ @aoid = aoid
101
+ @store = store
102
+ @request = Atmos::Request.new(:store => @store)
103
+ @user = @store.user
104
+
105
+ if (action == :create)
106
+
107
+ if (options[:id])
108
+ raise Atmos::Exceptions::ArgumentException, "You can't specify an id on object creation."
109
+ end
110
+
111
+ Atmos::LOG.debug("Object.new: creating new object")
112
+ response = @request.do(:create_object, options)
113
+ @aoid = response.id
114
+
115
+ elsif (action == :get)
116
+
117
+ Atmos::LOG.debug("Retrieving object: id: #{options[:id]}; namespace: #{options[:namespace]}")
118
+ response = @request.do(:get_object_info, options)
119
+ @aoid = Atmos::Parser::response_get_string(response.http_response, '//xmlns:objectId')
120
+ Atmos::LOG.debug("Retrieved object id for namespace: #{@aoid}")
121
+ end
122
+
123
+ end
124
+
125
+ #
126
+ # Lazy evaluation of hash-like object containing user access control properties of the object.
127
+ #
128
+ def user_acl
129
+ #Atmos::LOG.warn("user_acl: #{@user_acl.inspect}")
130
+ if (@user_acl.nil?)
131
+ @user_acl = Atmos::ACL.new(self, Atmos::ACL::USER)
132
+ #Atmos::LOG.warn("user_acl: #{@user_acl.inspect}")
133
+ end
134
+
135
+ @user_acl
136
+ end
137
+
138
+
139
+ #
140
+ # Lazy evaluation of hash-like object containing group access control properties of the object.
141
+ #
142
+ def group_acl
143
+ if (@group_acl.nil?)
144
+ @group_acl = Atmos::ACL.new(self, Atmos::ACL::GROUP)
145
+ end
146
+
147
+ @group_acl
148
+ end
149
+
150
+
151
+ #
152
+ # Lazy evaluation of hash-like object containing non-listable properties of the object.
153
+ #
154
+ def metadata
155
+ if (@metadata.nil?)
156
+ @metadata = Atmos::Metadata.new(self, Atmos::Metadata::NON_LISTABLE)
157
+ end
158
+
159
+ @metadata
160
+ end
161
+
162
+
163
+ #
164
+ # Lazy evaluation of hash-like object containing listable metadata properties of the object.
165
+ #
166
+ def listable_metadata
167
+ if (@listable_metadata.nil?)
168
+ @listable_metadata = Atmos::Metadata.new(self, Atmos::Metadata::LISTABLE)
169
+ end
170
+
171
+ @listable_metadata
172
+ end
173
+
174
+
175
+ #
176
+ # Lazy evaluation of Hash-like object containing read-only system metadata associated with the object.
177
+ #
178
+ def system_metadata
179
+ if (@system_metadata.nil?)
180
+ @system_metadata = Atmos::Metadata.new(self, Atmos::Metadata::SYSTEM)
181
+ end
182
+
183
+ @system_metadata
184
+ end
185
+
186
+
187
+ #
188
+ # Truncates the object to size 0 without changing any of the Metadata or ACLs.
189
+ #
190
+ def truncate
191
+ do_delete_check
192
+ response = @request.do(:trunc_object, :id => @aoid, :data => nil, :length => 0)
193
+ end
194
+
195
+ #
196
+ # Deletes the object from Atmos and invalidates the object.
197
+ #
198
+ # obj = store.create
199
+ # obj.delete
200
+ #
201
+ def delete
202
+ do_delete_check
203
+ response = @request.do(:delete_object, :id => @aoid)
204
+ @deleted = true
205
+ end
206
+
207
+
208
+ #
209
+ # Returns all the object data in a single string. Be judicious about
210
+ # use of this method, since it can load the entire blob into memory.
211
+ #
212
+ # Optional:
213
+ # * <tt>:range</tt> - range of bytes to retrieve (e.g. 0...10000)
214
+ #
215
+ def data(range = nil)
216
+ do_delete_check
217
+ response = @request.do(:read_object, :id => @aoid, 'Range' => range)
218
+ response.http_response.body
219
+ end
220
+
221
+
222
+ #
223
+ # Allows progressive download of the object's data. Takes a block:
224
+ #
225
+ # obj.data_as_stream do |chunk|
226
+ # datafile.write(chunk)
227
+ # end
228
+ #
229
+ # Optional:
230
+ # * <tt>:range</tt> - range of bytes to retrieve (e.g. 0...10000)
231
+ #
232
+ def data_as_stream(range = nil, &block)
233
+ do_delete_check
234
+ @request.do(:read_object, :id => @aoid, 'Range' => range) do |response|
235
+ response.read_body do |chunk|
236
+ block.call(chunk)
237
+ end
238
+ end
239
+ end
240
+
241
+
242
+ def update(data, range=nil)
243
+ response = @request.do(:update_object, :id => @aoid, :data => data, 'Range' => range)
244
+ end
245
+
246
+
247
+ #
248
+ # Checks to see if the represented object exists on Atmos
249
+ # by requesting it's system metadata.
250
+ #
251
+ # Returns boolean +true+ or +false+.
252
+ #
253
+ def exists?
254
+ rv = true
255
+ begin
256
+ @request.do(:list_system_metadata, :id => @aoid)
257
+ rescue Atmos::Exceptions::NoSuchObjectException
258
+ rv = false
259
+ end
260
+ rv
261
+ end
262
+
263
+
264
+ def copy #:nodoc:
265
+ do_delete_check
266
+ end
267
+
268
+
269
+ def headers #:nodoc:
270
+ do_delete_check
271
+ headers = {}
272
+ [@user_acl, @group_acl, @tags, @listable_tags, @metadata, @listable_metadata].each do |attr|
273
+ val = attr.header_value
274
+ next if (val.nil? || val.empty?)
275
+ headers[attr.header_name] = attr.header_value
276
+ end
277
+ headers
278
+ end
279
+
280
+
281
+ private
282
+ def do_delete_check
283
+ raise Atmos::Exceptions::InvalidStateException if (@deleted)
284
+ end
285
+
286
+ def validate_options(options)
287
+ invalid_values = []
288
+
289
+ valid_options = [:checksum, :user_acl, :group_acl, :metadata, :listable_metadata, :mimetype, :length, :data, :namespace, :id].freeze
290
+ invalid_options = options.keys - valid_options
291
+ raise Atmos::Exceptions::ArgumentException, "Unrecognized options: #{invalid_options.inspect}" if (!invalid_options.empty?)
292
+
293
+ options.each do |k,v|
294
+ case k
295
+ when :checksum
296
+ invalid_values.push(k) if (options[k].nil? || !options[k].kind_of?(Boolean))
297
+ when :user_acl
298
+ invalid_values.push(k) if (options[k].nil? || !options[k].kind_of?(Hash))
299
+ when :group_acl
300
+ invalid_values.push(k) if (options[k].nil? || !options[k].kind_of?(Hash))
301
+ when :metadata
302
+ invalid_values.push(k) if (options[k].nil? || !options[k].kind_of?(Hash))
303
+ when :listable_metadata
304
+ invalid_values.push(k) if (options[k].nil? || !options[k].kind_of?(Hash))
305
+ when :mimetype
306
+ invalid_values.push(k) if (options[k].nil? || !options[k].kind_of?(String))
307
+ when :namespace
308
+ invalid_values.push(k) if (options[k].nil? || !options[k].kind_of?(String))
309
+ when :length
310
+ invalid_values.push(k) if (options[k].nil? || !options[k].kind_of?(Integer))
311
+ when :data
312
+ invalid_values.push(k) if (options[k].nil? || (!options[k].kind_of?(String) && !options[k].class.ancestors.include?(IO)))
313
+ end
314
+ end
315
+
316
+ raise Atmos::Exceptions::ArgumentException, "Options of invalid type: #{invalid_values.inspect}" if (!invalid_values.empty?)
317
+ end
318
+
319
+ end
320
+
321
+ end
@@ -0,0 +1,110 @@
1
+ module Atmos
2
+ class Parser
3
+
4
+ NOKOGIRI = "nokogiri"
5
+ REXML = "rexml"
6
+ @@parser = nil
7
+
8
+
9
+ def self.parser
10
+ @@parser
11
+ end
12
+
13
+
14
+ def self.parser=(which)
15
+
16
+ @@parser = which
17
+ if (@@parser == NOKOGIRI)
18
+ require 'nokogiri'
19
+ elsif (@@parser == REXML)
20
+ require 'rexml/document'
21
+ else
22
+ raise Atmos::Exceptions::ArgumentException, "The XML parser has not been set to a known parser."
23
+ end
24
+ end
25
+
26
+
27
+ def self.response_get_array(response, string)
28
+ rv = nil
29
+
30
+ if (parser == NOKOGIRI)
31
+
32
+ doc = Nokogiri::XML(response.body)
33
+ rv = doc.xpath(string).map do |elt|
34
+ elt.content
35
+ end
36
+
37
+ elsif (parser == REXML)
38
+
39
+ doc = ::REXML::Document.new(response.body)
40
+ rv = doc.elements.to_a(string).map do |elt|
41
+ elt.text
42
+ end
43
+
44
+ end
45
+
46
+ rv
47
+ end
48
+
49
+
50
+ def self.response_get_string(response, string)
51
+ response_get_array(response,string)[0]
52
+ end
53
+
54
+
55
+ #
56
+ # Utility method to check the atmos server XML response for errors.
57
+ # Throws exception on error.
58
+ #
59
+ def self.response_check_action_error(action, response)
60
+ doc = nil
61
+ code = nil
62
+ msg = nil
63
+ exclass = Atmos::Exceptions::AtmosException
64
+
65
+ Atmos::LOG.info("body: #{response.body}")
66
+ if (!response.body.nil? && response.body.match(/<\?xml/))
67
+ if (parser == NOKOGIRI)
68
+
69
+ doc = Nokogiri::XML(response.body)
70
+
71
+ code = doc.xpath('//Error/Code')[0]
72
+ code = code.content if (!code.nil?)
73
+
74
+ msg = doc.xpath('//Error/Message')[0]
75
+ msg = msg.content if (!msg.nil?)
76
+
77
+ elsif (parser == REXML)
78
+
79
+ code = response_get_string(response, '//Error/Code')
80
+ msg = response_get_string(response, '//Error/Message')
81
+
82
+ else
83
+ raise Atmos::Exceptions::InvalidStateException, "The XML parser has not been set to a known parser."
84
+ end
85
+ end
86
+
87
+ if (!code.nil?)
88
+
89
+ Atmos::LOG.info("code: #{code}")
90
+ Atmos::LOG.info("msg: #{msg}")
91
+
92
+ if (!REST[action][:errors].nil? && !REST[action][:errors][code].nil?)
93
+ tmp = REST[action][:errors][code][:message]
94
+ msg = tmp if (!tmp.nil?)
95
+
96
+ tmp = REST[action][:errors][code][:class]
97
+ exclass = tmp if (!tmp.nil?)
98
+ end
99
+
100
+ raise exclass, msg
101
+
102
+ end
103
+
104
+ true
105
+ end
106
+
107
+
108
+
109
+ end
110
+ end