ruby-atmos 0.6.0 → 1.0.1

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.
data/Rakefile CHANGED
@@ -45,7 +45,7 @@ spec = Gem::Specification.new do |s|
45
45
  s.extra_rdoc_files = %w(README COPYING)
46
46
  s.homepage = 'http://www.emc.com/atmos'
47
47
  #s.rubyforge_project = 'atmos'
48
- s.files = FileList['Rakefile', 'lib/*/*.rb']
48
+ s.files = FileList['Rakefile', 'lib/atmos.rb', 'lib/*/*.rb']
49
49
  s.test_files = Dir['test/**/*']
50
50
  s.require_path = 'lib'
51
51
  s.add_dependency('log4r', '>=1.1.9')
data/lib/atmos.rb ADDED
@@ -0,0 +1,35 @@
1
+ require 'rubygems'
2
+ require 'log4r'
3
+ require 'uri'
4
+ require 'net/http'
5
+ require 'net/https'
6
+ require 'time'
7
+ require 'base64'
8
+ require 'hmac-sha1'
9
+
10
+
11
+ Log4r::Logger.new('atmos')
12
+ Log4r::Logger.get('atmos').add(Log4r::StderrOutputter.new 'console')
13
+ Log4r::Logger.get('atmos').level = Log4r::FATAL
14
+ #Log4r::Logger.get('atmos').level = Log4r::WARN
15
+ #Log4r::Logger.get('atmos').level = Log4r::INFO
16
+ #Log4r::Logger.get('atmos').level = Log4r::DEBUG
17
+
18
+
19
+ $:.unshift(File.dirname(__FILE__))
20
+ require 'atmos/parser'
21
+ require 'atmos/version'
22
+ require 'atmos/util'
23
+ require 'atmos/exceptions'
24
+ require 'atmos/request'
25
+ require 'atmos/response'
26
+ require 'atmos/rest'
27
+ require 'atmos/store'
28
+ require 'atmos/attributes'
29
+ require 'atmos/object'
30
+
31
+ #
32
+ # This sets the default, but it can still be overridden by setting the parser elsewhere.
33
+ #
34
+ Atmos::Parser::parser = (File.exists?(File.join(File.dirname(__FILE__), 'atmos/parser/rexml.rb'))) ? Atmos::Parser::REXML : Atmos::Parser::NOKOGIRI
35
+ Atmos::LOG.warn("parser: #{Atmos::Parser::parser}")
data/lib/atmos/object.rb CHANGED
@@ -75,23 +75,13 @@ module Atmos
75
75
  class Object
76
76
  attr_reader :aoid, :request, :user # :nodoc:
77
77
 
78
- # Hash-like object containing user access control properties of the object.
79
- attr_reader :user_acl
80
-
81
- # Hash-like object containing group access control properties of the object.
82
- attr_reader :group_acl
83
-
84
- # Hash-like object containing non-listable metadata associated with the object.
85
- attr_reader :metadata
86
-
87
- # Hash-like object containing listable metadata associated with the object.
88
- attr_reader :listable_metadata
89
-
90
- # Hash-like object containing read-only system metadata associated with the object.
91
- attr_reader :system_metadata
92
-
93
78
  @request = nil
94
79
  @checksum = nil
80
+ @user_acl = nil
81
+ @group_acl = nil
82
+ @metadata = nil
83
+ @system_metadata = nil
84
+ @listable_metadata = nil
95
85
 
96
86
 
97
87
  #
@@ -99,9 +89,10 @@ module Atmos
99
89
  # with an Atmos::Store object:
100
90
  #
101
91
  # obj = store.create
102
- # obj = store.get(obj_id)
92
+ # obj = store.get(:id => obj_id)
93
+ # obj = store.get(:namespace => obj_id)
103
94
  #
104
- def initialize(store, aoid, options = {})
95
+ def initialize(store, action, options = {})
105
96
  Atmos::LOG.debug("obj.new options: #{options.inspect}")
106
97
  validate_options(options)
107
98
 
@@ -111,20 +102,85 @@ module Atmos
111
102
  @request = Atmos::Request.new(:store => @store)
112
103
  @user = @store.user
113
104
 
114
- # this means we're creating a new object,
115
- # not representing an existing one
116
- if (@aoid.nil?)
117
- response = @request.do(:create_object, options)
118
- @aoid = response.id
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}")
119
121
  end
120
122
 
121
- @system_metadata = Atmos::Metadata.new(self, Atmos::Metadata::SYSTEM)
122
-
123
- # These guys need the object to have a valid id, so we do it after the create
124
- @user_acl = Atmos::ACL.new(self, Atmos::ACL::USER)
125
- @group_acl = Atmos::ACL.new(self, Atmos::ACL::GROUP)
126
- @metadata = Atmos::Metadata.new(self, Atmos::Metadata::NON_LISTABLE)
127
- @listable_metadata = Atmos::Metadata.new(self, Atmos::Metadata::LISTABLE)
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
128
184
  end
129
185
 
130
186
 
@@ -230,7 +286,7 @@ module Atmos
230
286
  def validate_options(options)
231
287
  invalid_values = []
232
288
 
233
- valid_options = [:checksum, :user_acl, :group_acl, :metadata, :listable_metadata, :mimetype, :length, :data].freeze
289
+ valid_options = [:checksum, :user_acl, :group_acl, :metadata, :listable_metadata, :mimetype, :length, :data, :namespace, :id].freeze
234
290
  invalid_options = options.keys - valid_options
235
291
  raise Atmos::Exceptions::ArgumentException, "Unrecognized options: #{invalid_options.inspect}" if (!invalid_options.empty?)
236
292
 
@@ -248,6 +304,8 @@ module Atmos
248
304
  invalid_values.push(k) if (options[k].nil? || !options[k].kind_of?(Hash))
249
305
  when :mimetype
250
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))
251
309
  when :length
252
310
  invalid_values.push(k) if (options[k].nil? || !options[k].kind_of?(Integer))
253
311
  when :data
data/lib/atmos/request.rb CHANGED
@@ -14,6 +14,43 @@ module Atmos
14
14
  @http = options[:store].http
15
15
  @tag = options[:default_tag]
16
16
  end
17
+
18
+
19
+ #
20
+ # If there is no namespace option provided, we're using the object interface,
21
+ # otherwise we're using the namespace interface. Determine which it is, so we know
22
+ # which URI to use.
23
+ #
24
+ # If aoid is nil, we're creating a new object regardless.
25
+ #
26
+ def get_uri(action, options)
27
+
28
+ if (options.has_key?(:namespace) && action.has_key?(:namespace_uri))
29
+
30
+ # only two actions need namespace, so if the action doesn't have the
31
+ # :namespace_url key, we use the regular :object_url.
32
+ ns = (options[:namespace].start_with?('/')) ? options[:namespace][1..-1] : options[:namespace]
33
+ uri = action[:namespace_uri].sub(/:namespace/, ns)
34
+
35
+ else
36
+
37
+ uri = action[:object_uri]
38
+
39
+ # only need the following check/substution if the uri requires an id
40
+ if (!uri.index(':id').nil?)
41
+
42
+ if (options.has_key?(:id) && !options[:id].nil?)
43
+ uri = uri.sub(/:id/, options[:id])
44
+ else
45
+ raise Atmos::Exceptions::ArgumentException, "An id is required for this action. This is an internal error."
46
+ end
47
+ end
48
+ end
49
+
50
+ Atmos::LOG.info("uri: #{uri}")
51
+ uri
52
+ end
53
+
17
54
 
18
55
 
19
56
  def do(actionname, options = {}, &block)
@@ -22,14 +59,10 @@ module Atmos
22
59
  raise Atmos::Exceptions::InternalLibraryException,
23
60
  "Invalid REST action: #{actionname}" if (REST[actionname].nil?)
24
61
 
25
- action = REST[actionname]
26
- uri = (options[:id].nil?) ? action[:uri] : action[:uri].sub(/:id/, options[:id])
27
- url = URI::join(@baseurl.to_s, uri.to_s)
28
- verb = action[:verb]
29
-
30
- if (options[:id].nil? && !action[:uri].index(':id').nil?)
31
- raise Atmos::Exceptions::ArgumentException, "An id is required for this action (#{actionname})."
32
- end
62
+ action = REST[actionname]
63
+ uri = get_uri(action, options)
64
+ url = URI::join(@baseurl.to_s, uri.to_s)
65
+ verb = action[:verb]
33
66
 
34
67
  request = (verbs.include?(verb)) ? Net::HTTP.const_get(verb.to_s.capitalize).new(uri) : nil
35
68
  raise Atmos::Exceptions::AtmosException,
@@ -0,0 +1,116 @@
1
+ module Atmos
2
+ class Response # :nodoc:
3
+ attr_reader :http_response
4
+
5
+ def initialize(response, action)
6
+ @http_response = response
7
+ @action = action
8
+
9
+ if (response.kind_of?(Net::HTTPServerError))
10
+ raise Atmos::Exceptions::ServerException, response.message
11
+ elsif (response.kind_of?(Net::HTTPClientError))
12
+
13
+ raise Atmos::Exceptions::AtmosException, "Atmos got a bad request. This is probably a problem in this library." if (response.kind_of?(Net::HTTPBadRequest))
14
+
15
+ Atmos::LOG.debug("#{response.class}")
16
+ Atmos::LOG.debug("#{response.body}")
17
+
18
+ Atmos::Parser::response_check_action_error(@action, response)
19
+
20
+ elsif (!response.kind_of?(Net::HTTPSuccess))
21
+ raise Atmos::Exceptions::NotImplementedException, "This library doesn't handle these kinds of responses: #{response}"
22
+ end
23
+
24
+ end
25
+
26
+ def each(header)
27
+ if (!self[header].nil?)
28
+ self[header].split(',').each do |elt|
29
+ if (!elt.index('=').nil?)
30
+ key,val = elt.split('=')
31
+ yield key.strip, val.strip
32
+ else
33
+ yield elt.strip
34
+ end
35
+ end
36
+ end
37
+ end
38
+
39
+ def body
40
+ @http_response.body
41
+ end
42
+
43
+ def method_missing(sym, *args)
44
+ Atmos::LOG.info("method missing: #{sym}")
45
+ header = "x-emc-#{sym.id2name.sub(/_/, '-')}"
46
+ if (REST[@action][:return_headers].include?(header))
47
+ Atmos::LOG.info("header: #{header}")
48
+ rv = Atmos::Util.header2hash(header, @http_response[header])
49
+ Atmos::LOG.debug("rv: #{rv.inspect}")
50
+ return rv
51
+ else
52
+ return super.method_missing(sym, *args)
53
+ end
54
+ end
55
+
56
+ def delta
57
+ if (REST[@action][:return_headers].include?('x-emc-delta'))
58
+ @http_response['x-emc-delta']
59
+ else
60
+ raise "A #{@action} request doesn't return a delta."
61
+ end
62
+ end
63
+
64
+ def policy
65
+ if (REST[@action][:return_headers].include?('x-emc-policy'))
66
+ @http_response['x-emc-policy']
67
+ else
68
+ raise "A #{@action} request doesn't return a policy description."
69
+ end
70
+ end
71
+
72
+ def id
73
+ if (REST[@action][:return_headers].include?('location'))
74
+ @http_response['location'][@http_response['location'].rindex('/')+1..-1]
75
+ else
76
+ raise "A #{@action} request doesn't return an id."
77
+ end
78
+ end
79
+
80
+ def headers
81
+ headers = {}
82
+ @http_response.each do |header, value|
83
+ headers[header] = value
84
+ end
85
+ headers
86
+ end
87
+
88
+ def [](header)
89
+ @http_response[header]
90
+ end
91
+
92
+ end
93
+
94
+
95
+
96
+
97
+
98
+
99
+ # while (response.kind_of?(Net::HTTPRedirection))
100
+
101
+ #From http://railstips.org/blog/archives/2009/03/04/following-redirects-with-nethttp/
102
+ # rurl = (response['location'].nil?) ? response.body.match(/<a href=\"([^>]+)\">/i)[1] : response['location']
103
+
104
+ # puts("Got a redirect \nfrom: #{options[:url]}\n to: #{rurl}")
105
+
106
+ # if rurl.start_with?('/')
107
+ # puts("Got a relative redirect url: #{options[:url]}")
108
+ # options[:url] = URI.parse("#{url.scheme}://#{url.host}#{redirect_url}")
109
+ # end
110
+
111
+ # options[:redirects] = (options[:redirects].nil?) ? 0 : options[:redirects] += 1
112
+ # response = self.generic_request(options)
113
+ # end
114
+ # raise "Too many redirects (#{options[:redirects]}): #{url}" if (!options[:redirects].nil? && (options[:redirects] > 3))
115
+
116
+ end
data/lib/atmos/rest.rb CHANGED
@@ -1,62 +1,62 @@
1
1
  module Atmos
2
2
  REST = {
3
3
  :listable_tags => { :verb => :get,
4
- :uri => '/rest/objects?listabletags',
4
+ :object_uri => '/rest/objects?listabletags',
5
5
  :required_headers => ['x-emc-tags'],
6
6
  :optional_headers => ['x-emc-token'],
7
7
  :http_response => 200,
8
8
  :return_headers => ['x-emc-listable-tags'],
9
9
  },
10
10
  :set_group_acl => { :verb => :post,
11
- :uri => '/rest/objects/:id?acl',
11
+ :object_uri => '/rest/objects/:id?acl',
12
12
  :required_headers => ['x-emc-groupacl'],
13
13
  :http_response => 200,
14
14
  },
15
15
  :set_user_acl => { :verb => :post,
16
- :uri => '/rest/objects/:id?acl',
16
+ :object_uri => '/rest/objects/:id?acl',
17
17
  :required_headers => ['x-emc-useracl'],
18
18
  :http_response => 200,
19
19
  },
20
20
  :set_metadata => { :verb => :post,
21
- :uri => '/rest/objects/:id?metadata/user',
21
+ :object_uri => '/rest/objects/:id?metadata/user',
22
22
  :http_response => 200,
23
23
  :required_headers => ['x-emc-meta'],
24
24
  },
25
- :set_listable_metadata => { :verb => :post,
26
- :uri => '/rest/objects/:id?metadata/user',
25
+ :set_listable_metadata => { :verb => :post,
26
+ :object_uri => '/rest/objects/:id?metadata/user',
27
27
  :http_response => 200,
28
28
  :required_headers => ['x-emc-listable-meta'],
29
29
  },
30
30
  :delete_metadata => { :verb => :delete,
31
- :uri => '/rest/objects/:id?metadata/user',
31
+ :object_uri => '/rest/objects/:id?metadata/user',
32
32
  :http_response => 204,
33
33
  :required_headers => ['x-emc-tags'],
34
34
  },
35
35
  :read_object => { :verb => :get,
36
- :uri => '/rest/objects/:id',
36
+ :object_uri => '/rest/objects/:id',
37
37
  :http_response => 200,
38
38
  :optional_headers => ['Range'],
39
39
  },
40
40
  :update_object => { :verb => :put,
41
- :uri => '/rest/objects/:id',
41
+ :object_uri => '/rest/objects/:id',
42
42
  :http_response => 200,
43
43
  :required_headers => ['Content-Type', 'Content-Length'],
44
44
  :optional_headers => ['x-emc-useracl', 'x-emc-groupacl', 'Range'],
45
45
  },
46
46
  :trunc_object => { :verb => :put,
47
- :uri => '/rest/objects/:id',
47
+ :object_uri => '/rest/objects/:id',
48
48
  :http_response => 200,
49
49
  :required_headers => ['Content-Length'],
50
50
  :optional_headers => ['x-emc-useracl', 'x-emc-groupacl', 'Range'],
51
51
  },
52
52
  :get_service_info => { :verb => :get,
53
- :uri => '/rest/service',
53
+ :object_uri => '/rest/service',
54
54
  :response => :xml,
55
55
  :http_response => 200,
56
56
  },
57
57
 
58
58
  :list_objects => { :verb => :get,
59
- :uri => '/rest/objects',
59
+ :object_uri => '/rest/objects',
60
60
  :response => :xml,
61
61
  :http_response => 200,
62
62
  :required_headers => ['x-emc-tags', 'Content-Type'],
@@ -66,7 +66,8 @@ module Atmos
66
66
  },
67
67
 
68
68
  :create_object => { :verb => :post,
69
- :uri => '/rest/objects',
69
+ :namespace_uri => '/rest/namespace/:namespace',
70
+ :object_uri => '/rest/objects',
70
71
  :http_response => 201,
71
72
  :optional_headers => ['x-emc-wschecksum', 'x-emc-useracl', 'x-emc-groupacl', 'x-emc-meta', 'x-emc-listable-meta'],
72
73
  :required_headers => ['Content-Type', 'Content-Length'],
@@ -78,8 +79,14 @@ module Atmos
78
79
  'x-emc-listable-meta' => :listable_metadata},
79
80
  },
80
81
 
82
+ :get_object_info => { :verb => :get,
83
+ :namespace_uri => '/rest/namespace/:namespace?info',
84
+ :object_uri => '/rest/objects/:id?info',
85
+ :http_response => 200,
86
+ :return_headers => ['x-emc-policy'],
87
+ },
81
88
  :delete_object => { :verb => :delete,
82
- :uri => '/rest/objects/:id',
89
+ :object_uri => '/rest/objects/:id',
83
90
  :http_response => 204,
84
91
  :required_headers => ['Content-Type'],
85
92
  :return_headers => ['x-emc-delta', 'x-emc-policy'],
@@ -88,27 +95,27 @@ module Atmos
88
95
  },
89
96
 
90
97
  :list_system_metadata=> { :verb => :get,
91
- :uri => '/rest/objects/:id?metadata/system',
98
+ :object_uri => '/rest/objects/:id?metadata/system',
92
99
  :http_response => 200,
93
100
  :return_headers => ['x-emc-meta'],
94
101
  :errors => { '1003' => { :class => Atmos::Exceptions::NoSuchObjectException ,
95
102
  :message => "Object not found to delete." } },
96
103
  },
97
104
  :list_metadata => { :verb => :get,
98
- :uri => '/rest/objects/:id?metadata/user',
105
+ :object_uri => '/rest/objects/:id?metadata/user',
99
106
  :http_response => 200,
100
107
  :required_headers => ['Content-Type'],
101
108
  :return_headers => ['x-emc-meta', 'x-emc-listable-meta'],
102
109
  },
103
110
 
104
111
  :list_tags => { :verb => :get,
105
- :uri => '/rest/objects/:id?metadata/tags',
112
+ :object_uri => '/rest/objects/:id?metadata/tags',
106
113
  :http_response => 200,
107
114
  :return_headers => ['x-emc-tags', 'x-emc-listable-tags'],
108
115
  },
109
116
 
110
117
  :list_acl => { :verb => :get,
111
- :uri => '/rest/objects/:id?acl',
118
+ :object_uri => '/rest/objects/:id?acl',
112
119
  :http_response => 200,
113
120
  :return_headers => ['x-emc-useracl', 'x-emc-groupacl'],
114
121
  },
@@ -119,136 +126,20 @@ module Atmos
119
126
 
120
127
 
121
128
 
122
- :delete_version => { :verb => :delete,
123
- :uri => '/rest/objects/:id?versions',
124
- :http_response => 204,
125
- },
126
- :get_object_info => { :verb => :get,
127
- :uri => '/rest/objects/:id?info',
128
- :http_response => 200,
129
+ :delete_version => { :verb => :delete,
130
+ :object_uri => '/rest/objects/:id?versions',
131
+ :http_response => 204,
129
132
  },
130
- :create_version => { :verb => :post,
131
- :uri => '/rest/objects/:id?versions' ,
132
- :http_response => 201,
133
+ :create_version => { :verb => :post,
134
+ :object_uri => '/rest/objects/:id?versions' ,
135
+ :http_response => 201,
133
136
  },
134
- :list_versions => { :verb => :get, :uri => '/rest/objects/:id?versions', :http_response => 200},
135
- :restore_version => { :verb => :put, :uri => '/rest/objects/:id?versions', :http_response => 200},
137
+ :list_versions => { :verb => :get,
138
+ :object_uri => '/rest/objects/:id?versions',
139
+ :http_response => 200},
140
+ :restore_version => { :verb => :put,
141
+ :object_uri => '/rest/objects/:id?versions',
142
+ :http_response => 200},
136
143
  } # :nodoc:
137
-
138
-
139
-
140
- class Response # :nodoc:
141
- attr_reader :http_response
142
-
143
- def initialize(response, action)
144
- @http_response = response
145
- @action = action
146
-
147
- if (response.kind_of?(Net::HTTPServerError))
148
- raise Atmos::Exceptions::ServerException, response.message
149
- elsif (response.kind_of?(Net::HTTPClientError))
150
-
151
- raise Atmos::Exceptions::AtmosException, "Atmos got a bad request. This is probably a problem in this library." if (response.kind_of?(Net::HTTPBadRequest))
152
-
153
- Atmos::LOG.debug("#{response.class}")
154
- Atmos::LOG.debug("#{response.body}")
155
-
156
- Atmos::Parser::response_check_action_error(@action, response)
157
-
158
- elsif (!response.kind_of?(Net::HTTPSuccess))
159
- raise Atmos::Exceptions::NotImplementedException, "This library doesn't handle these kinds of responses: #{response}"
160
- end
161
-
162
- end
163
-
164
- def each(header)
165
- if (!self[header].nil?)
166
- self[header].split(',').each do |elt|
167
- if (!elt.index('=').nil?)
168
- key,val = elt.split('=')
169
- yield key.strip, val.strip
170
- else
171
- yield elt.strip
172
- end
173
- end
174
- end
175
- end
176
-
177
- def body
178
- @http_response.body
179
- end
180
-
181
- def method_missing(sym, *args)
182
- Atmos::LOG.info("method missing: #{sym}")
183
- header = "x-emc-#{sym.id2name.sub(/_/, '-')}"
184
- if (REST[@action][:return_headers].include?(header))
185
- Atmos::LOG.info("header: #{header}")
186
- rv = Atmos::Util.header2hash(header, @http_response[header])
187
- Atmos::LOG.debug("rv: #{rv.inspect}")
188
- return rv
189
- else
190
- return super.method_missing(sym, *args)
191
- end
192
- end
193
-
194
- def delta
195
- if (REST[@action][:return_headers].include?('x-emc-delta'))
196
- @http_response['x-emc-delta']
197
- else
198
- raise "A #{@action} request doesn't return a delta."
199
- end
200
- end
201
-
202
- def policy
203
- if (REST[@action][:return_headers].include?('x-emc-policy'))
204
- @http_response['x-emc-policy']
205
- else
206
- raise "A #{@action} request doesn't return a policy description."
207
- end
208
- end
209
-
210
- def id
211
- if (REST[@action][:return_headers].include?('location'))
212
- @http_response['location'][@http_response['location'].rindex('/')+1..-1]
213
- else
214
- raise "A #{@action} request doesn't return an id."
215
- end
216
- end
217
-
218
- def headers
219
- headers = {}
220
- @http_response.each do |header, value|
221
- headers[header] = value
222
- end
223
- headers
224
- end
225
-
226
- def [](header)
227
- @http_response[header]
228
- end
229
-
230
- end
231
-
232
-
233
-
234
-
235
-
236
-
237
- # while (response.kind_of?(Net::HTTPRedirection))
238
-
239
- #From http://railstips.org/blog/archives/2009/03/04/following-redirects-with-nethttp/
240
- # rurl = (response['location'].nil?) ? response.body.match(/<a href=\"([^>]+)\">/i)[1] : response['location']
241
-
242
- # puts("Got a redirect \nfrom: #{options[:url]}\n to: #{rurl}")
243
-
244
- # if rurl.start_with?('/')
245
- # puts("Got a relative redirect url: #{options[:url]}")
246
- # options[:url] = URI.parse("#{url.scheme}://#{url.host}#{redirect_url}")
247
- # end
248
-
249
- # options[:redirects] = (options[:redirects].nil?) ? 0 : options[:redirects] += 1
250
- # response = self.generic_request(options)
251
- # end
252
- # raise "Too many redirects (#{options[:redirects]}): #{url}" if (!options[:redirects].nil? && (options[:redirects] > 3))
253
144
 
254
145
  end
data/lib/atmos/store.rb CHANGED
@@ -5,9 +5,8 @@ module Atmos
5
5
 
6
6
  #
7
7
  # Create and validate a connection to an Atmos server.
8
- # This API currently uses only the Atmos object interface,
9
- # not the namespace interface. Proxy support has been tested
10
- # against squid 2.7.STABLE9 on ubuntu 10.10.
8
+ # This API supports both the object interface and the namespace interface.
9
+ # Proxy support has been tested against squid 2.7.STABLE9 on ubuntu 10.10.
11
10
  #
12
11
  # If you have used Atmos Online this example will look familiar:
13
12
  #
@@ -130,9 +129,10 @@ module Atmos
130
129
  # * <tt>:mimetype</tt> - defaults to application/octet-stream
131
130
  # * <tt>:data</tt> - a String or an IO stream
132
131
  # * <tt>:length</tt> - a number (requires <tt>:data</tt>)
132
+ # * <tt>:namespace</tt> - name for namespace interface instead of object interface
133
133
  #
134
134
  def create(options = {})
135
- Atmos::Object.new(self, nil, options)
135
+ Atmos::Object.new(self, :create, options)
136
136
  end
137
137
 
138
138
 
@@ -143,8 +143,19 @@ module Atmos
143
143
  # The blob data is not loaded until
144
144
  # accessed, so it can be progressively downloaded.
145
145
  #
146
- def get(id)
147
- Atmos::Object.new(self, id)
146
+ # Mutually exclusive, one required:
147
+ # * <tt>:id</tt> - the id of the object to retrieve
148
+ # * <tt>:namespace</tt> - the namespace key of the object to retrieve
149
+ def get(options)
150
+
151
+ # can't both be set, can't both be empty
152
+ if (!options[:id].nil? && !options[:namespace].nil?)
153
+ raise Atmos::Exceptions::ArgumentException, "One of :id, :namespace is required, but not both."
154
+ elsif (options[:id].nil? && options[:namespace].nil?)
155
+ raise Atmos::Exceptions::ArgumentException, "One of :id, :namespace is required."
156
+ end
157
+
158
+ Atmos::Object.new(self, :get, options)
148
159
  end
149
160
 
150
161
 
data/lib/atmos/version.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  module Atmos
2
2
 
3
3
  # Version number of this library.
4
- VERSION = "0.6.0"
4
+ VERSION = "1.0.1"
5
5
 
6
6
  # Array of version strings this library has been tested against.
7
7
  SUPPORTED_VERSIONS = ["1.2.7", "1.3.4", "1.4.0", "1.4.1", "2.0.1"].freeze
data/test/suite.rb CHANGED
@@ -4,6 +4,8 @@ require File.dirname(__FILE__) + '/test_util'
4
4
  require File.dirname(__FILE__) + '/test_acl'
5
5
  require File.dirname(__FILE__) + '/test_metadata'
6
6
  require File.dirname(__FILE__) + '/test_object_create'
7
+ require File.dirname(__FILE__) + '/test_object_get'
8
+ require File.dirname(__FILE__) + '/test_object_read'
7
9
  require File.dirname(__FILE__) + '/test_object_update'
8
10
  require File.dirname(__FILE__) + '/test_object_misc'
9
11
  require File.dirname(__FILE__) + '/test_store'
@@ -22,7 +22,7 @@ class AtmosObjectCreateTest < Test::Unit::TestCase
22
22
  $stdout.flush
23
23
  end
24
24
 
25
- def test_create_empty_object
25
+ def test_create_empty_object_via_object_interface
26
26
  obj = @s.create
27
27
  @cleanup.push(obj)
28
28
 
@@ -33,6 +33,21 @@ class AtmosObjectCreateTest < Test::Unit::TestCase
33
33
  assert_equal({Atmos::Test::Util.group => :none}, obj.group_acl)
34
34
  end
35
35
 
36
+ def test_create_empty_object_via_namespace_interface
37
+ time = Time.now()
38
+ time = time.to_i
39
+ ns = "/foo/bar#{time}"
40
+
41
+ obj = @s.create(:namespace => ns)
42
+ @cleanup.push(obj)
43
+
44
+ assert_not_nil(obj)
45
+ assert_equal({}, obj.metadata)
46
+ assert_equal({}, obj.listable_metadata)
47
+ assert_equal({Atmos::Test::Util.user => :full}, obj.user_acl)
48
+ assert_equal({Atmos::Test::Util.group => :none}, obj.group_acl)
49
+ end
50
+
36
51
  def test_create_object_with_bad_option
37
52
  assert_raise Atmos::Exceptions::ArgumentException do
38
53
  @cleanup.push(@s.create(:tag => ['testtag']))
@@ -0,0 +1,55 @@
1
+ require 'credentials'
2
+ require 'test/unit'
3
+ require 'atmos'
4
+
5
+
6
+ class AtmosObjectGetTest < Test::Unit::TestCase
7
+
8
+ def initialize(id)
9
+ super(id)
10
+ @cleanup = []
11
+
12
+ end
13
+
14
+ def setup
15
+ @s = Atmos::Test::Util.static_store
16
+ end
17
+
18
+
19
+ def teardown
20
+ @cleanup.each do |obj|
21
+ obj.delete
22
+ end
23
+ $stdout.flush
24
+ end
25
+
26
+ def test_get_object_via_object_interface
27
+ obj = @s.create
28
+ @cleanup.push(obj)
29
+
30
+ obj = @s.get(:id => obj.aoid)
31
+ assert_not_nil(obj)
32
+ assert_equal({}, obj.metadata)
33
+ assert_equal({}, obj.listable_metadata)
34
+ assert_equal({Atmos::Test::Util.user => :full}, obj.user_acl)
35
+ assert_equal({Atmos::Test::Util.group => :none}, obj.group_acl)
36
+ end
37
+
38
+ def test_get_object_via_namespace_interface
39
+ time = Time.now()
40
+ time = time.to_i#{}"%.6f" % a.to_f #=> "1195480202.282373"
41
+
42
+ ns = "/foo/bar#{time}"
43
+ obj = @s.create(:namespace => ns)
44
+ obj = @s.get(:namespace => ns)
45
+
46
+ assert_not_nil(obj)
47
+ assert_equal({}, obj.metadata)
48
+ assert_equal({}, obj.listable_metadata)
49
+ assert_equal({Atmos::Test::Util.user => :full}, obj.user_acl)
50
+ assert_equal({Atmos::Test::Util.group => :none}, obj.group_acl)
51
+
52
+ obj.delete()
53
+ end
54
+
55
+ end
@@ -40,7 +40,7 @@ class AtmosObjectMiscTest < Test::Unit::TestCase
40
40
  def test_delete_nonexisting_object
41
41
  first = @s.create
42
42
  assert_not_nil(first)
43
- second = @s.get(first.aoid)
43
+ second = @s.get(:id => first.aoid)
44
44
  assert_not_nil(second)
45
45
  first.delete
46
46
  assert_raise Atmos::Exceptions::NoSuchObjectException do
@@ -3,24 +3,24 @@ require 'test/unit'
3
3
  require 'atmos'
4
4
 
5
5
 
6
- class AtmosObjectCreateTest < Test::Unit::TestCase
6
+ class AtmosObjectReadTest < Test::Unit::TestCase
7
7
 
8
- def initialize(id)
9
- super(id)
10
- @cleanup = []
11
- end
8
+ def initialize(id)
9
+ super(id)
10
+ @cleanup = []
11
+ end
12
12
 
13
- def setup
14
- @s = Atmos::Test::Util.static_store
15
- @md = {'non' => 'listable', 'user' => 'metadata', 'a' => 'b'}
16
- @lmd = {'x' => 'a', 'y' => 'b', 'z' => 'c'}
17
- data = open(Atmos::Test::Util.small_binary_filename)
18
- obj = @s.create(:metadata => @md,
19
- :listable_metadata => @lmd,
20
- :data => File.new(Atmos::Test::Util.small_binary_filename, "rb"))
21
- @cleanup.push(obj)
22
- @aoid = obj.aoid
23
- end
13
+ def setup
14
+ @s = Atmos::Test::Util.static_store
15
+ @md = {'non' => 'listable', 'user' => 'metadata', 'a' => 'b'}
16
+ @lmd = {'x' => 'a', 'y' => 'b', 'z' => 'c'}
17
+ data = open(Atmos::Test::Util.small_binary_filename)
18
+ obj = @s.create(:metadata => @md,
19
+ :listable_metadata => @lmd,
20
+ :data => File.new(Atmos::Test::Util.small_binary_filename, "rb"))
21
+ @cleanup.push(obj)
22
+ @aoid = obj.aoid
23
+ end
24
24
 
25
25
 
26
26
  def teardown
@@ -31,14 +31,10 @@ class AtmosObjectCreateTest < Test::Unit::TestCase
31
31
 
32
32
 
33
33
  def test_read_partial_data_as_stream
34
- # something is very strange here
35
- # the object shouldn't end up empty, but it does
36
- # it doesn't end up empty when this file is run
37
- # as a standalone test
34
+ obj = @s.get(:id => @aoid)
35
+
38
36
  file = open(Atmos::Test::Util.small_binary_filename)
39
37
  contents = file.read
40
- obj = @s.get(@aoid)
41
- obj.update(contents)
42
38
  assert_equal(contents, obj.data)
43
39
 
44
40
  f = File.new(Atmos::Test::Util.small_binary_filename, "rb")
@@ -57,13 +53,9 @@ class AtmosObjectCreateTest < Test::Unit::TestCase
57
53
 
58
54
  def test_read_data_as_stream
59
55
 
60
- # something is very strange here
61
- # the object shouldn't end up empty, but it does
62
- # it doesn't end up empty when this file is run
63
- # as a standalone test
64
56
  file = open(Atmos::Test::Util.small_binary_filename)
65
57
  contents = file.read
66
- obj = @s.get(@aoid)
58
+ obj = @s.get(:id => @aoid)
67
59
  obj.update(contents)
68
60
  assert_equal(contents, obj.data)
69
61
 
@@ -77,13 +69,9 @@ class AtmosObjectCreateTest < Test::Unit::TestCase
77
69
 
78
70
  def test_read_partial_data_as_string
79
71
 
80
- # something is very strange here
81
- # the object shouldn't end up empty, but it does
82
- # it doesn't end up empty when this file is run
83
- # as a standalone test
84
72
  file = open(Atmos::Test::Util.small_binary_filename)
85
73
  contents = file.read
86
- obj = @s.get(@aoid)
74
+ obj = @s.get(:id => @aoid)
87
75
  obj.update(contents)
88
76
  assert_equal(contents, obj.data)
89
77
 
@@ -97,13 +85,9 @@ class AtmosObjectCreateTest < Test::Unit::TestCase
97
85
  end
98
86
 
99
87
  def test_read_data_as_string
100
- # something is very strange here
101
- # the object shouldn't end up empty, but it does
102
- # it doesn't end up empty when this file is run
103
- # as a standalone test
104
88
  file = open(Atmos::Test::Util.small_binary_filename)
105
89
  contents = file.read
106
- obj = @s.get(@aoid)
90
+ obj = @s.get(:id => @aoid)
107
91
  obj.update(contents)
108
92
  assert_equal(contents, obj.data)
109
93
 
@@ -3,7 +3,7 @@ require 'test/unit'
3
3
  require 'atmos'
4
4
 
5
5
 
6
- class AtmosObjectCreateTest < Test::Unit::TestCase
6
+ class AtmosObjectUpdateTest < Test::Unit::TestCase
7
7
 
8
8
  def initialize(id)
9
9
  super(id)
@@ -14,6 +14,7 @@ class AtmosObjectCreateTest < Test::Unit::TestCase
14
14
  @s = Atmos::Test::Util.static_store
15
15
  @obj = @s.create
16
16
  @cleanup.push(@obj)
17
+ #print "test_object_update.setup(): obj: #{@obj}; objid: #{@obj.aoid}\n"
17
18
  end
18
19
 
19
20
 
@@ -3,7 +3,7 @@ require 'test/unit'
3
3
  require File.dirname(__FILE__) + '/../lib/atmos'
4
4
 
5
5
 
6
- class AtmosUtilTest < Test::Unit::TestCase
6
+ class AtmosRequestTest < Test::Unit::TestCase
7
7
 
8
8
  def dont_test_signature_calculation
9
9
  headers = {}
@@ -44,7 +44,6 @@ class AtmosUtilTest < Test::Unit::TestCase
44
44
  :secret => 'p97mki4AkiAyVi+/7QpdxFAU/jY=',
45
45
  :unsupported => true
46
46
  )
47
- print "#{store.server_version}\n"
48
47
 
49
48
  end
50
49
  end
data/test/test_store.rb CHANGED
@@ -112,11 +112,11 @@ class AtmosStoreTest < Test::Unit::TestCase
112
112
 
113
113
  for i in (1...10)
114
114
  obj = @s.create(:listable_metadata => {tag => true})
115
+ @cleanup.push(obj)
115
116
  end
116
117
 
117
118
  i = 0
118
119
  @s.each_object_with_listable_tag(tag) do |obj|
119
- @cleanup.push(obj)
120
120
  i += 1
121
121
  end
122
122
  assert_equal(9, i)
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-atmos
3
3
  version: !ruby/object:Gem::Version
4
- hash: 7
4
+ hash: 21
5
5
  prerelease: false
6
6
  segments:
7
+ - 1
7
8
  - 0
8
- - 6
9
- - 0
10
- version: 0.6.0
9
+ - 1
10
+ version: 1.0.1
11
11
  platform: ruby
12
12
  authors:
13
13
  - Fleur Dragan
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-12-14 00:00:00 -08:00
18
+ date: 2012-01-03 00:00:00 -08:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -77,11 +77,13 @@ extra_rdoc_files:
77
77
  - COPYING
78
78
  files:
79
79
  - Rakefile
80
+ - lib/atmos.rb
80
81
  - lib/atmos/attributes.rb
81
82
  - lib/atmos/exceptions.rb
82
83
  - lib/atmos/object.rb
83
84
  - lib/atmos/parser.rb
84
85
  - lib/atmos/request.rb
86
+ - lib/atmos/response.rb
85
87
  - lib/atmos/rest.rb
86
88
  - lib/atmos/store.rb
87
89
  - lib/atmos/util.rb
@@ -91,16 +93,17 @@ files:
91
93
  - test/files/dragaf-tiny-from-vsphere.ova
92
94
  - test/files/SmallImageForTest.iso
93
95
  - test/files/something.txt
94
- - test/request_test.rb
95
96
  - test/suite.rb
96
97
  - test/suite_noproxy.rb
97
98
  - test/suite_proxy.rb
98
99
  - test/test_acl.rb
99
100
  - test/test_metadata.rb
100
101
  - test/test_object_create.rb
102
+ - test/test_object_get.rb
101
103
  - test/test_object_misc.rb
102
104
  - test/test_object_read.rb
103
105
  - test/test_object_update.rb
106
+ - test/test_request.rb
104
107
  - test/test_store.rb
105
108
  - test/test_util.rb
106
109
  - README
@@ -150,15 +153,16 @@ test_files:
150
153
  - test/files/dragaf-tiny-from-vsphere.ova
151
154
  - test/files/SmallImageForTest.iso
152
155
  - test/files/something.txt
153
- - test/request_test.rb
154
156
  - test/suite.rb
155
157
  - test/suite_noproxy.rb
156
158
  - test/suite_proxy.rb
157
159
  - test/test_acl.rb
158
160
  - test/test_metadata.rb
159
161
  - test/test_object_create.rb
162
+ - test/test_object_get.rb
160
163
  - test/test_object_misc.rb
161
164
  - test/test_object_read.rb
162
165
  - test/test_object_update.rb
166
+ - test/test_request.rb
163
167
  - test/test_store.rb
164
168
  - test/test_util.rb