qoobaa-stree 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -16,28 +16,28 @@ environment variables and uses them by default, so if you don't want
16
16
  to pass them each time, export them (e.g. in .bashrc file).
17
17
 
18
18
  * list buckets
19
- stree bucket
19
+ stree bucket
20
20
 
21
21
  * create bucket
22
- stree bucket add name-of-bucket
22
+ stree bucket add name-of-bucket
23
23
 
24
24
  * list objects in bucket
25
- stree bucket show name-of-bucket
25
+ stree bucket show name-of-bucket
26
26
 
27
27
  * destroy bucket
28
- stree bucket remove name-of-bucket
28
+ stree bucket remove name-of-bucket
29
29
 
30
30
  * list objects (in all buckets)
31
- stree object
31
+ stree object
32
32
 
33
33
  * show the object information
34
- stree object show bucket_name/path/to/object.extension
34
+ stree object show bucket_name/path/to/object.extension
35
35
 
36
36
  * download the content of the object
37
- stree object show bucket_name/path/to/object.extension filename_to_store_the_content.extension
37
+ stree object show bucket_name/path/to/object.extension filename_to_store_the_content.extension
38
38
 
39
39
  * show the content of the object to STDOUT
40
- stree object show bucket_name/path/to/object.extension -
40
+ stree object show bucket_name/path/to/object.extension -
41
41
 
42
42
  You can also pass --help to the commmands like:
43
43
  stree bucket add --help
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.0
1
+ 0.2.0
@@ -103,7 +103,7 @@ module Technoweenie
103
103
  # :nodoc:
104
104
  def current_data
105
105
  # Object.value full_filename, bucket_name
106
- object = self.class.bucket.find(full_filename)
106
+ object = self.class.bucket.objects.find(full_filename)
107
107
  object.content
108
108
  end
109
109
 
data/lib/stree/bucket.rb CHANGED
@@ -1,25 +1,26 @@
1
1
  module Stree
2
2
  class Bucket
3
+ include Parser
3
4
  extend Roxy::Moxie
4
5
  extend Forwardable
5
6
 
6
7
  attr_reader :name, :service
7
8
 
8
9
  def_instance_delegators :service, :service_request
10
+ private_class_method :new
9
11
 
10
12
  # Retrieves the bucket information from the server. Raises an
11
13
  # Stree::Error exception if the bucket doesn't exist or you don't
12
14
  # have access to it, etc.
13
15
  def retrieve
14
- bucket_request(:get, :params => { :max_keys => 0 })
16
+ list_bucket(:max_keys => 0)
15
17
  self
16
18
  end
17
19
 
18
20
  # Returns location of the bucket, e.g. "EU"
19
21
  def location(reload = false)
20
22
  if reload or @location.nil?
21
- response = bucket_request(:get, :params => { :location => nil })
22
- @location = parse_location(response.body)
23
+ @location = location_constraint
23
24
  else
24
25
  @location
25
26
  end
@@ -45,7 +46,7 @@ module Stree
45
46
  # bucket is not empty. You can destroy non-empty bucket passing
46
47
  # true (to force destroy)
47
48
  def destroy(force = false)
48
- bucket_request(:delete)
49
+ delete_bucket
49
50
  true
50
51
  rescue Error::BucketNotEmpty
51
52
  if force
@@ -59,13 +60,7 @@ module Stree
59
60
  # Saves the newly built bucket. Optionally you can pass location
60
61
  # of the bucket (:eu or :us)
61
62
  def save(location = nil)
62
- location = location.to_s.upcase if location
63
- options = { :headers => {} }
64
- if location and location != "US"
65
- options[:body] = "<CreateBucketConfiguration><LocationConstraint>#{location}</LocationConstraint></CreateBucketConfiguration>"
66
- options[:headers][:content_type] = "application/xml"
67
- end
68
- bucket_request(:put, options)
63
+ create_bucket_configuration(location)
69
64
  true
70
65
  end
71
66
 
@@ -92,7 +87,7 @@ module Stree
92
87
  # reload).
93
88
  def objects(reload = false)
94
89
  if reload or @objects.nil?
95
- @objects = fetch_objects
90
+ @objects = list_bucket
96
91
  else
97
92
  @objects
98
93
  end
@@ -102,7 +97,7 @@ module Stree
102
97
 
103
98
  # Builds the object in the bucket with given key
104
99
  def build(key)
105
- Object.new(proxy_owner, key)
100
+ Object.send(:new, proxy_owner, :key => key)
106
101
  end
107
102
 
108
103
  # Finds first object with given name or raises the exception if
@@ -120,7 +115,7 @@ module Stree
120
115
  # +max_keys+:: The maximum number of keys you'd like to see
121
116
  # +delimiter+:: Causes keys that contain the same string between the prefix and the first occurrence of the delimiter to be rolled up into a single result element
122
117
  def find_all(options = {})
123
- proxy_owner.send(:fetch_objects, options)
118
+ proxy_owner.send(:list_bucket, options)
124
119
  end
125
120
 
126
121
  # Reloads the object list (clears the cache)
@@ -140,15 +135,40 @@ module Stree
140
135
  "#<#{self.class}:#{name}>"
141
136
  end
142
137
 
138
+ private
139
+
140
+ attr_writer :service
141
+
142
+ def location_constraint
143
+ response = bucket_request(:get, :params => { :location => nil })
144
+ parse_location_constraint(response.body)
145
+ end
146
+
147
+ def list_bucket(options = {})
148
+ response = bucket_request(:get, :params => options)
149
+ objects_attributes = parse_list_bucket_result(response.body)
150
+ objects_attributes.map { |object_attributes| Object.send(:new, self, object_attributes) }
151
+ end
152
+
153
+ def create_bucket_configuration(location = nil)
154
+ location = location.to_s.upcase if location
155
+ options = { :headers => {} }
156
+ if location and location != "US"
157
+ options[:body] = "<CreateBucketConfiguration><LocationConstraint>#{location}</LocationConstraint></CreateBucketConfiguration>"
158
+ options[:headers][:content_type] = "application/xml"
159
+ end
160
+ bucket_request(:put, options)
161
+ end
162
+
163
+ def delete_bucket
164
+ bucket_request(:delete)
165
+ end
166
+
143
167
  def initialize(service, name) #:nodoc:
144
168
  self.service = service
145
169
  self.name = name
146
170
  end
147
171
 
148
- private
149
-
150
- attr_writer :service
151
-
152
172
  def name=(name)
153
173
  raise ArgumentError.new("Invalid bucket name: #{name}") unless name_valid?(name)
154
174
  @name = name
@@ -159,32 +179,6 @@ module Stree
159
179
  service_request(method, options.merge(:host => host, :path => path))
160
180
  end
161
181
 
162
- def fetch_objects(options = {})
163
- response = bucket_request(:get, options)
164
- parse_objects(response.body)
165
- end
166
-
167
- def parse_objects(xml_body)
168
- xml = XmlSimple.xml_in(xml_body)
169
- objects_attributes = xml["Contents"]
170
- if objects_attributes
171
- objects_attributes.map do |object_attributes|
172
- Object.new(self,
173
- object_attributes["Key"].first,
174
- :etag => object_attributes["ETag"].first,
175
- :last_modified => object_attributes["LastModified"].first,
176
- :size => object_attributes["Size"].first)
177
- end
178
- else
179
- []
180
- end
181
- end
182
-
183
- def parse_location(xml_body)
184
- xml = XmlSimple.xml_in(xml_body)
185
- xml["content"]
186
- end
187
-
188
182
  def name_valid?(name)
189
183
  name =~ /\A[a-z0-9][a-z0-9\._-]{2,254}\Z/ and name !~ /\A#{URI::REGEXP::PATTERN::IPV4ADDR}\Z/
190
184
  end
@@ -2,6 +2,8 @@ module Stree
2
2
 
3
3
  # Class responsible for handling connections to amazon hosts
4
4
  class Connection
5
+ include Parser
6
+
5
7
  attr_accessor :access_key_id, :secret_access_key, :use_ssl, :timeout, :debug
6
8
  alias :use_ssl? :use_ssl
7
9
 
@@ -185,9 +187,7 @@ module Stree
185
187
  if response.body.nil? || response.body.empty?
186
188
  raise Error::ResponseError.new(nil, response)
187
189
  else
188
- xml = XmlSimple.xml_in(response.body)
189
- message = xml["Message"].first
190
- code = xml["Code"].first
190
+ code, message = parse_error(response.body)
191
191
  raise Error::ResponseError.exception(code).new(message, response)
192
192
  end
193
193
  else
data/lib/stree/object.rb CHANGED
@@ -2,6 +2,7 @@ module Stree
2
2
 
3
3
  # Class responsible for handling objects stored in S3 buckets
4
4
  class Object
5
+ include Parser
5
6
  extend Forwardable
6
7
 
7
8
  attr_accessor :content_type, :content_disposition, :content_encoding
@@ -10,6 +11,7 @@ module Stree
10
11
 
11
12
  def_instance_delegators :bucket, :name, :service, :bucket_request, :vhost?, :host, :path_prefix
12
13
  def_instance_delegators :service, :protocol, :port
14
+ private_class_method :new
13
15
 
14
16
  # Compares the object with other object. Returns true if the key
15
17
  # of the objects are the same, and both have the same buckets (see
@@ -43,8 +45,7 @@ module Stree
43
45
  # NOT download the content of the object (use the content method
44
46
  # to do it).
45
47
  def retrieve
46
- response = object_request(:get, :headers => { :range => 0..0 })
47
- parse_headers(response)
48
+ get_object(:headers => { :range => 0..0 })
48
49
  self
49
50
  end
50
51
 
@@ -62,18 +63,14 @@ module Stree
62
63
  # to clear the cache and download the object again.
63
64
  def content(reload = false)
64
65
  if reload or @content.nil?
65
- response = object_request(:get)
66
- parse_headers(response)
67
- self.content = response.body
66
+ get_object
68
67
  end
69
68
  @content
70
69
  end
71
70
 
72
71
  # Saves the object, returns true if successfull.
73
72
  def save
74
- body = content.is_a?(IO) ? content.read : content
75
- response = object_request(:put, :body => body, :headers => dump_headers)
76
- parse_headers(response)
73
+ put_object
77
74
  true
78
75
  end
79
76
 
@@ -84,28 +81,12 @@ module Stree
84
81
  # +acl+:: acl of the copied object (default: "public-read")
85
82
  # +content_type+:: content type of the copied object (default: "application/octet-stream")
86
83
  def copy(options = {})
87
- key = options[:key] || self.key
88
- bucket = options[:bucket] || self.bucket
89
-
90
- headers = {}
91
- headers[:x_amz_acl] = options[:acl] || acl || "public-read"
92
- headers[:content_type] = options[:content_type] || content_type || "application/octet-stream"
93
- headers[:content_encoding] = options[:content_encoding] if options[:content_encoding]
94
- headers[:content_disposition] = options[:content_disposition] if options[:content_disposition]
95
- headers[:x_amz_copy_source] = full_key
96
- headers[:x_amz_metadata_directive] = "REPLACE"
97
- headers[:x_amz_copy_source_if_match] = options[:if_match] if options[:if_match]
98
- headers[:x_amz_copy_source_if_none_match] = options[:if_none_match] if options[:if_none_match]
99
- headers[:x_amz_copy_source_if_unmodified_since] = options[:if_modified_since] if options[:if_modified_since]
100
- headers[:x_amz_copy_source_if_modified_since] = options[:if_unmodified_since] if options[:if_unmodified_since]
101
-
102
- response = bucket.send(:bucket_request, :put, :path => key, :headers => headers)
103
- self.class.parse_copied(:object => self, :bucket => bucket, :key => key, :body => response.body, :headers => headers)
84
+ copy_object(options)
104
85
  end
105
86
 
106
87
  # Destroys the file on the server
107
88
  def destroy
108
- object_request(:delete)
89
+ delete_object
109
90
  true
110
91
  end
111
92
 
@@ -127,18 +108,62 @@ module Stree
127
108
  "#<#{self.class}:/#{name}/#{key}>"
128
109
  end
129
110
 
130
- def initialize(bucket, key, options = {}) #:nodoc:
111
+ private
112
+
113
+ attr_writer :last_modified, :etag, :size, :original_key, :bucket
114
+
115
+ def copy_object(options = {})
116
+ key = options[:key] or raise ArgumentError, "No key given"
117
+ raise ArgumentError.new("Invalid key name: #{key}") unless key_valid?(key)
118
+ bucket = options[:bucket] || self.bucket
119
+
120
+ headers = {}
121
+
122
+ headers[:x_amz_acl] = options[:acl] || acl || "public-read"
123
+ headers[:content_type] = options[:content_type] || content_type || "application/octet-stream"
124
+ headers[:content_encoding] = options[:content_encoding] if options[:content_encoding]
125
+ headers[:content_disposition] = options[:content_disposition] if options[:content_disposition]
126
+ headers[:x_amz_copy_source] = full_key
127
+ headers[:x_amz_metadata_directive] = "REPLACE"
128
+ headers[:x_amz_copy_source_if_match] = options[:if_match] if options[:if_match]
129
+ headers[:x_amz_copy_source_if_none_match] = options[:if_none_match] if options[:if_none_match]
130
+ headers[:x_amz_copy_source_if_unmodified_since] = options[:if_modified_since] if options[:if_modified_since]
131
+ headers[:x_amz_copy_source_if_modified_since] = options[:if_unmodified_since] if options[:if_unmodified_since]
132
+
133
+ response = bucket.send(:bucket_request, :put, :path => key, :headers => headers)
134
+ object_attributes = parse_copy_object_result(response.body)
135
+
136
+ object = Object.send(:new, bucket, object_attributes.merge(:key => key, :size => size))
137
+ object.acl = response[:x_amz_acl]
138
+ object.content_type = response[:content_type]
139
+ object.content_encoding = response[:content_encoding]
140
+ object.content_disposition = response[:content_disposition]
141
+ object
142
+ end
143
+
144
+ def get_object(options = {})
145
+ response = object_request(:get, options)
146
+ parse_headers(response)
147
+ end
148
+
149
+ def put_object
150
+ body = content.is_a?(IO) ? content.read : content
151
+ response = object_request(:put, :body => body, :headers => dump_headers)
152
+ parse_headers(response)
153
+ end
154
+
155
+ def delete_object(options = {})
156
+ object_request(:delete)
157
+ end
158
+
159
+ def initialize(bucket, options = {})
131
160
  self.bucket = bucket
132
- self.key = key
161
+ self.key = options[:key]
133
162
  self.last_modified = options[:last_modified]
134
163
  self.etag = options[:etag]
135
164
  self.size = options[:size]
136
165
  end
137
166
 
138
- private
139
-
140
- attr_writer :last_modified, :etag, :size, :original_key, :bucket
141
-
142
167
  def object_request(method, options = {})
143
168
  bucket_request(method, options.merge(:path => key))
144
169
  end
@@ -151,6 +176,14 @@ module Stree
151
176
  @etag = etag[1..-2] if etag
152
177
  end
153
178
 
179
+ def key_valid?(key)
180
+ if (key.nil? or key.empty? or key =~ %r#//#)
181
+ false
182
+ else
183
+ true
184
+ end
185
+ end
186
+
154
187
  def dump_headers
155
188
  headers = {}
156
189
  headers[:x_amz_acl] = @acl || "public-read"
@@ -160,37 +193,18 @@ module Stree
160
193
  headers
161
194
  end
162
195
 
163
- def key_valid?(key)
164
- if (key.nil? or key.empty? or key =~ %r#//#)
165
- false
166
- else
167
- true
168
- end
169
- end
170
-
171
196
  def parse_headers(response)
172
197
  self.etag = response["etag"]
173
198
  self.content_type = response["content-type"]
174
199
  self.content_disposition = response["content-disposition"]
175
200
  self.content_encoding = response["content-encoding"]
176
201
  self.last_modified = response["last-modified"]
177
- self.size = response["content-length"]
178
202
  if response["content-range"]
179
203
  self.size = response["content-range"].sub(/[^\/]+\//, "").to_i
204
+ else
205
+ self.size = response["content-length"]
206
+ self.content = response.body
180
207
  end
181
208
  end
182
-
183
- def self.parse_copied(options)
184
- xml = XmlSimple.xml_in(options[:body])
185
- etag = xml["ETag"].first
186
- last_modified = xml["LastModified"].first
187
- size = options[:object].size
188
- object = Object.new(options[:bucket], options[:key], :etag => etag, :last_modified => last_modified, :size => size)
189
- object.acl = options[:headers][:x_amz_acl]
190
- object.content_type = options[:headers][:content_type]
191
- object.content_encoding = options[:headers][:content_encoding]
192
- object.content_disposition = options[:headers][:content_disposition]
193
- object
194
- end
195
209
  end
196
210
  end
@@ -0,0 +1,48 @@
1
+ module Stree
2
+ module Parser
3
+ include REXML
4
+
5
+ def rexml_document(xml)
6
+ xml.force_encoding(Encoding::UTF_8) if xml.respond_to? :force_encoding
7
+ Document.new(xml)
8
+ end
9
+
10
+ def parse_list_all_my_buckets_result(xml)
11
+ names = []
12
+ rexml_document(xml).elements.each("ListAllMyBucketsResult/Buckets/Bucket/Name") { |e| names << e.text }
13
+ names
14
+ end
15
+
16
+ def parse_location_constraint(xml)
17
+ rexml_document(xml).elements["LocationConstraint"].text
18
+ end
19
+
20
+ def parse_list_bucket_result(xml)
21
+ objects_attributes = []
22
+ rexml_document(xml).elements.each("ListBucketResult/Contents") do |e|
23
+ object_attributes = {}
24
+ object_attributes[:key] = e.elements["Key"].text
25
+ object_attributes[:etag] = e.elements["ETag"].text
26
+ object_attributes[:last_modified] = e.elements["LastModified"].text
27
+ object_attributes[:size] = e.elements["Size"].text
28
+ objects_attributes << object_attributes
29
+ end
30
+ objects_attributes
31
+ end
32
+
33
+ def parse_copy_object_result(xml)
34
+ object_attributes = {}
35
+ document = rexml_document(xml)
36
+ object_attributes[:etag] = document.elements["CopyObjectResult/ETag"].text
37
+ object_attributes[:last_modified] = document.elements["CopyObjectResult/LastModified"].text
38
+ object_attributes
39
+ end
40
+
41
+ def parse_error(xml)
42
+ document = rexml_document(xml)
43
+ code = document.elements["Error/Code"].text
44
+ message = document.elements["Error/Message"].text
45
+ [code, message]
46
+ end
47
+ end
48
+ end
data/lib/stree/service.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  module Stree
2
2
  class Service
3
+ include Parser
3
4
  extend Roxy::Moxie
4
5
 
5
6
  attr_reader :access_key_id, :secret_access_key, :use_ssl
@@ -19,8 +20,8 @@ module Stree
19
20
  # +timeout+:: parameter for Net::HTTP module
20
21
  # +debug+:: prints the raw requests to STDOUT
21
22
  def initialize(options)
22
- @access_key_id = options[:access_key_id] or raise ArgumentError.new("No access key id given")
23
- @secret_access_key = options[:secret_access_key] or raise ArgumentError.new("No secret access key given")
23
+ @access_key_id = options[:access_key_id] or raise ArgumentError, "No access key id given"
24
+ @secret_access_key = options[:secret_access_key] or raise ArgumentError, "No secret access key given"
24
25
  @use_ssl = options[:use_ssl]
25
26
  @timeout = options[:timeout]
26
27
  @debug = options[:debug]
@@ -29,8 +30,7 @@ module Stree
29
30
  # Returns all buckets in the service and caches the result (see reload)
30
31
  def buckets(reload = false)
31
32
  if reload or @buckets.nil?
32
- response = service_request(:get)
33
- @buckets = parse_buckets(response.body)
33
+ @buckets = list_all_my_buckets
34
34
  else
35
35
  @buckets
36
36
  end
@@ -49,7 +49,7 @@ module Stree
49
49
  proxy :buckets do
50
50
  # Builds new bucket with given name
51
51
  def build(name)
52
- Bucket.new(proxy_owner, name)
52
+ Bucket.send(:new, proxy_owner, name)
53
53
  end
54
54
 
55
55
  # Finds the bucket with given name
@@ -85,6 +85,12 @@ module Stree
85
85
 
86
86
  private
87
87
 
88
+ def list_all_my_buckets
89
+ response = service_request(:get)
90
+ names = parse_list_all_my_buckets_result(response.body)
91
+ names.map { |name| Bucket.send(:new, self, name) }
92
+ end
93
+
88
94
  def service_request(method, options = {})
89
95
  connection.request(method, options.merge(:path => "/#{options[:path]}"))
90
96
  end
@@ -100,18 +106,5 @@ module Stree
100
106
  end
101
107
  @connection
102
108
  end
103
-
104
- def parse_buckets(xml_body)
105
- xml = XmlSimple.xml_in(xml_body)
106
- buckets = xml["Buckets"].first["Bucket"]
107
- if buckets
108
- buckets_names = buckets.map { |bucket| bucket["Name"].first }
109
- buckets_names.map do |bucket_name|
110
- Bucket.new(self, bucket_name)
111
- end
112
- else
113
- []
114
- end
115
- end
116
109
  end
117
110
  end
data/lib/stree.rb CHANGED
@@ -1,16 +1,16 @@
1
- require "time"
2
- require "openssl"
3
- require "net/http"
4
- require "net/https"
5
1
  require "base64"
6
- require "forwardable"
7
2
  require "digest/md5"
3
+ require "forwardable"
4
+ require "net/http"
5
+ require "net/https"
6
+ require "openssl"
7
+ require "rexml/document"
8
+ require "time"
8
9
 
9
- require "xmlsimple"
10
-
11
- require "stree/roxy/proxy"
12
10
  require "stree/roxy/moxie"
11
+ require "stree/roxy/proxy"
13
12
 
13
+ require "stree/parser"
14
14
  require "stree/bucket"
15
15
  require "stree/connection"
16
16
  require "stree/exceptions"
data/stree.gemspec CHANGED
@@ -1,12 +1,15 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
1
4
  # -*- encoding: utf-8 -*-
2
5
 
3
6
  Gem::Specification.new do |s|
4
7
  s.name = %q{stree}
5
- s.version = "0.1.0"
8
+ s.version = "0.2.0"
6
9
 
7
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
11
  s.authors = ["Jakub Kuźma", "Mirosław Boruta"]
9
- s.date = %q{2009-07-13}
12
+ s.date = %q{2009-08-19}
10
13
  s.default_executable = %q{stree}
11
14
  s.email = %q{qoobaa@gmail.com}
12
15
  s.executables = ["stree"]
@@ -28,6 +31,7 @@ Gem::Specification.new do |s|
28
31
  "lib/stree/connection.rb",
29
32
  "lib/stree/exceptions.rb",
30
33
  "lib/stree/object.rb",
34
+ "lib/stree/parser.rb",
31
35
  "lib/stree/roxy/moxie.rb",
32
36
  "lib/stree/roxy/proxy.rb",
33
37
  "lib/stree/service.rb",
@@ -43,7 +47,7 @@ Gem::Specification.new do |s|
43
47
  s.homepage = %q{http://github.com/qoobaa/stree}
44
48
  s.rdoc_options = ["--charset=UTF-8"]
45
49
  s.require_paths = ["lib"]
46
- s.rubygems_version = %q{1.3.4}
50
+ s.rubygems_version = %q{1.3.5}
47
51
  s.summary = %q{Library for accessing S3 objects and buckets, with command line tool}
48
52
  s.test_files = [
49
53
  "test/bucket_test.rb",
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: qoobaa-stree
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - "Jakub Ku\xC5\xBAma"
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2009-07-13 00:00:00 -07:00
13
+ date: 2009-08-19 00:00:00 -07:00
14
14
  default_executable: stree
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
@@ -46,6 +46,7 @@ files:
46
46
  - lib/stree/connection.rb
47
47
  - lib/stree/exceptions.rb
48
48
  - lib/stree/object.rb
49
+ - lib/stree/parser.rb
49
50
  - lib/stree/roxy/moxie.rb
50
51
  - lib/stree/roxy/proxy.rb
51
52
  - lib/stree/service.rb
@@ -59,6 +60,7 @@ files:
59
60
  - test/test_helper.rb
60
61
  has_rdoc: false
61
62
  homepage: http://github.com/qoobaa/stree
63
+ licenses:
62
64
  post_install_message:
63
65
  rdoc_options:
64
66
  - --charset=UTF-8
@@ -79,7 +81,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
79
81
  requirements: []
80
82
 
81
83
  rubyforge_project:
82
- rubygems_version: 1.2.0
84
+ rubygems_version: 1.3.5
83
85
  signing_key:
84
86
  specification_version: 3
85
87
  summary: Library for accessing S3 objects and buckets, with command line tool