smooth_s3 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +4 -0
- data/Gemfile.lock +19 -0
- data/README.rdoc +19 -0
- data/README.textile +111 -0
- data/VERSION +1 -0
- data/lib/smooth_s3/bucket.rb +58 -0
- data/lib/smooth_s3/error.rb +3 -0
- data/lib/smooth_s3/service.rb +63 -0
- data/lib/smooth_s3/uploader.rb +129 -0
- data/lib/smooth_s3.rb +15 -0
- metadata +86 -0
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
git (1.2.5)
|
5
|
+
jeweler (1.6.4)
|
6
|
+
bundler (~> 1.0)
|
7
|
+
git (>= 1.2.5)
|
8
|
+
rake
|
9
|
+
proxies (0.2.1)
|
10
|
+
rake (0.9.2.2)
|
11
|
+
s3 (0.3.9)
|
12
|
+
proxies (~> 0.2.0)
|
13
|
+
|
14
|
+
PLATFORMS
|
15
|
+
ruby
|
16
|
+
|
17
|
+
DEPENDENCIES
|
18
|
+
jeweler
|
19
|
+
s3 (~> 0.3.9)
|
data/README.rdoc
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
= smooth_s3
|
2
|
+
|
3
|
+
Description goes here.
|
4
|
+
|
5
|
+
== Contributing to smooth_s3
|
6
|
+
|
7
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
|
8
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
|
9
|
+
* Fork the project
|
10
|
+
* Start a feature/bugfix branch
|
11
|
+
* Commit and push until you are happy with your contribution
|
12
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
13
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
14
|
+
|
15
|
+
== Copyright
|
16
|
+
|
17
|
+
Copyright (c) 2011 Nicholas Brochu. See LICENSE.txt for
|
18
|
+
further details.
|
19
|
+
|
data/README.textile
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
h1. Smooth S3
|
2
|
+
|
3
|
+
"http://github.com/nbrochu/smooth_s3":http://github.com/nbrochu/smooth_s3
|
4
|
+
|
5
|
+
h2. Summary
|
6
|
+
|
7
|
+
A user-friendly superset of the S3 gem geared towards file system backup operations.
|
8
|
+
|
9
|
+
h2. Description
|
10
|
+
|
11
|
+
Smooth S3 is a user-friendly superset of the S3 gem geared towards file system backup operations. It greatly simplifies regular file uploads to S3 by using Convention over Configuration™. The library also adds new features such as directory syncronization and timestamped uploads, which should come in real handy to anyone doing backup scripts on a regular basis. A decent amount of control is left to the developer: You can specify a prefix path to use with any upload and provide your own timestamp formats if desired.
|
12
|
+
|
13
|
+
The goal with Smooth S3 is to facilitate and simplify your S3 uploads. It is a library focused on the file system, so no integration with MySQL, third-party services or anything like that. Nothing prevents you from doing a _mysqldump_ and uploading the results in the same script using Smooth S3 though ;)
|
14
|
+
|
15
|
+
h2. Installation
|
16
|
+
|
17
|
+
This library has been developed against and designed for MRI 1.9.2 in a UNIX environment. It should be compatible with 1.8.6+, as well as with other Ruby implementations, but no guarantees. Probably not compatible with Windows environments.
|
18
|
+
|
19
|
+
<pre>
|
20
|
+
Using RubyGems - gem install smooth_s3
|
21
|
+
Using Bundler - gem "smooth_s3" in Gemfile, then bundle install
|
22
|
+
</pre>
|
23
|
+
|
24
|
+
h2. Overview
|
25
|
+
|
26
|
+
Before running upload operations using Smooth S3, you will need to initialize the service using your AWS credentials.
|
27
|
+
|
28
|
+
Once that is done you can use the following methods:
|
29
|
+
|
30
|
+
* upload() - Regular file upload. Can specify multiple files at once. Directory structure is not preserved, only the file name.
|
31
|
+
|
32
|
+
* sync_directory() - Uploads the entire content of a directory and its subdirectories. Preserves folder directory structure inside S3.
|
33
|
+
|
34
|
+
* timestamped_upload() - Like a regular upload. Files uploaded this way have a timestamp added in front of their names. Default timestamp format: YYYYmmddHHMMSS
|
35
|
+
|
36
|
+
* timestamped_directory_sync() Like a regular directory sync. Provided directory has a timestamp added in front of its name. Default timestamp format: YYYYmmddHHMMS
|
37
|
+
|
38
|
+
Each of the 4 methods above also has a bang(!) version available. The difference between the regular and bang version is that the former won't overwrite existing files in the selected bucket, while the latter will.
|
39
|
+
|
40
|
+
You can also specify a prefix as an option to the above methods. This will be inserted in front of what would have been the normal path on S3. For example, you upload _'test.rb'_ with the prefix _'path/to'_. It will show up on S3 as _'path/to/test.rb'_
|
41
|
+
|
42
|
+
That's it! Code examples in the next section.
|
43
|
+
|
44
|
+
h2. How to use
|
45
|
+
|
46
|
+
<pre>
|
47
|
+
require 'rubygems' # if using 1.8
|
48
|
+
require 'smooth_s3'
|
49
|
+
|
50
|
+
# Initialize the service
|
51
|
+
# Params: :aws_key, :aws_secret(, :ssl => false)
|
52
|
+
|
53
|
+
@service = SmoothS3::Service.new(:aws_key => "MY_AWS_ACCESS_KEY", :aws_secret => "MY_AWS_SECRET_KEY")
|
54
|
+
|
55
|
+
|
56
|
+
# upload()
|
57
|
+
# Params: bucket_name, files(, :prefix)
|
58
|
+
|
59
|
+
@service.upload("my_test_bucket", "test.rb")
|
60
|
+
@service.upload!("my_test_bucket", ["here.rb", "../parent.rb"], :prefix => "prefix/to/files")
|
61
|
+
|
62
|
+
|
63
|
+
# sync_directory()
|
64
|
+
# Params: bucket_name, directory(, :prefix)
|
65
|
+
|
66
|
+
@service.sync_directory("my_test_bucket", "../reports")
|
67
|
+
@service.sync_directory!("my_test_bucket", "data", :prefix => "prefix/to/dir")
|
68
|
+
|
69
|
+
|
70
|
+
# timestamped_upload()
|
71
|
+
# Params: bucket_name, files(, :prefix, :timestamp_type => :epoch/:strftime(default), :timestamp_format => "%Y%m%d%H%M%S" )
|
72
|
+
|
73
|
+
@service.timestamped_upload("my_test_bucket", "test.rb")
|
74
|
+
@service.timestamped_upload!("my_test_bucket", "test.rb", :timestamp_type => :epoch)
|
75
|
+
@service.timestamped_upload!("my_test_bucket", "test.rb", :timestamp_type => :strftime, :timestamp_format => "%Y%m%d")
|
76
|
+
|
77
|
+
|
78
|
+
# timestamped_directory_sync()
|
79
|
+
# Params: bucket_name, directory(, :prefix, :timestamp_type => :epoch/:strftime(default), :timestamp_format => "%Y%m%d%H%M%S" )
|
80
|
+
|
81
|
+
@service.timestamped_directory_sync("my_test_bucket", "data")
|
82
|
+
@service.timestamped_directory_sync!("my_test_bucket", "data", :timestamp_type => :epoch)
|
83
|
+
@service.timestamped_directory_sync!("my_test_bucket", "data", :timestamp_type => :strftime, :timestamp_format => "%Y%m%d")
|
84
|
+
</pre>
|
85
|
+
|
86
|
+
h2. Credits
|
87
|
+
|
88
|
+
* Jakub KuĹşma for his great S3 library, often overshadowed by the popular but old AWS-S3.
|
89
|
+
|
90
|
+
h2. LICENSE
|
91
|
+
|
92
|
+
Copyright (c) 2011 - Nicholas Brochu
|
93
|
+
|
94
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
95
|
+
a copy of this software and associated documentation files (the
|
96
|
+
'Software'), to deal in the Software without restriction, including
|
97
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
98
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
99
|
+
permit persons to whom the Software is furnished to do so, subject to
|
100
|
+
the following conditions:
|
101
|
+
|
102
|
+
The above copyright notice and this permission notice shall be
|
103
|
+
included in all copies or substantial portions of the Software.
|
104
|
+
|
105
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
106
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
107
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
108
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
109
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
110
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
111
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module SmoothS3
|
2
|
+
class Bucket
|
3
|
+
|
4
|
+
def self.exists?(bucket, service)
|
5
|
+
service.buckets.include? bucket
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.file_exists?(file, bucket, service)
|
9
|
+
b = service.proxy_service.buckets.find_first(bucket)
|
10
|
+
|
11
|
+
begin
|
12
|
+
b.objects.find_first(file)
|
13
|
+
return true
|
14
|
+
rescue S3::Error::NoSuchKey
|
15
|
+
return false
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.select(bucket, service)
|
20
|
+
Bucket.create(bucket, service) unless Bucket.exists?(bucket, service)
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.create(bucket_name, service)
|
24
|
+
begin
|
25
|
+
new_bucket = service.proxy_service.buckets.build(bucket_name)
|
26
|
+
new_bucket.save
|
27
|
+
rescue S3::Error::BucketAlreadyExists
|
28
|
+
raise SmoothS3::Error, "A bucket named '#{bucket_name}' already exists in the Global S3 Namespace. Please select one of you existing buckets or try a new name."
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.store_file(file, remote_file, bucket, service, prefix, overwrite)
|
33
|
+
b = service.proxy_service.buckets.find_first(bucket)
|
34
|
+
|
35
|
+
if prefix
|
36
|
+
remote_file = prefix + remote_file if prefix =~ /\/$/
|
37
|
+
remote_file = prefix + "/" + remote_file unless prefix =~ /\/$/
|
38
|
+
end
|
39
|
+
|
40
|
+
unless overwrite == true
|
41
|
+
if Bucket.file_exists?(remote_file, bucket, service)
|
42
|
+
puts "'#{remote_file}' already exists on S3 bucket named '#{bucket}'. Use the bang(!) version of the method to overwrite."
|
43
|
+
return
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
bo = b.objects.build(remote_file)
|
48
|
+
bo.content = open(file)
|
49
|
+
|
50
|
+
if bo.save
|
51
|
+
puts "'#{file}' was uploaded to S3 bucket '#{bucket}' under the name '#{remote_file}'."
|
52
|
+
else
|
53
|
+
puts "There was a problem trying to upload '#{file}' to S3"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module SmoothS3
|
2
|
+
class Service
|
3
|
+
attr_reader :aws_key, :aws_secret, :ssl, :proxy_service
|
4
|
+
|
5
|
+
def initialize(opts={})
|
6
|
+
@aws_key = opts.delete(:aws_key)
|
7
|
+
@aws_secret = opts.delete(:aws_secret)
|
8
|
+
|
9
|
+
@ssl = opts.delete(:ssl) == true ? true : false
|
10
|
+
@proxy_service = S3::Service.new(:access_key_id => @aws_key, :secret_access_key => @aws_secret, :use_ssl => @ssl)
|
11
|
+
|
12
|
+
test_connection
|
13
|
+
end
|
14
|
+
|
15
|
+
def upload(bucket, files, options={})
|
16
|
+
Uploader.upload(self, bucket, files, options)
|
17
|
+
end
|
18
|
+
|
19
|
+
def upload!(bucket, files, options={})
|
20
|
+
Uploader.upload!(self, bucket, files, options)
|
21
|
+
end
|
22
|
+
|
23
|
+
def sync_directory(bucket, directory, options={})
|
24
|
+
Uploader.sync_directory(self, bucket, directory, options)
|
25
|
+
end
|
26
|
+
|
27
|
+
def sync_directory!(bucket, directory, options={})
|
28
|
+
Uploader.sync_directory!(self, bucket, directory, options)
|
29
|
+
end
|
30
|
+
|
31
|
+
def timestamped_upload(bucket, files, options={})
|
32
|
+
Uploader.timestamped_upload(self, bucket, files, options)
|
33
|
+
end
|
34
|
+
|
35
|
+
def timestamped_upload!(bucket, files, options={})
|
36
|
+
Uploader.timestamped_upload!(self, bucket, files, options)
|
37
|
+
end
|
38
|
+
|
39
|
+
def timestamped_directory_sync(bucket, directory, options={})
|
40
|
+
Uploader.timestamped_directory_sync(self, bucket, directory, options)
|
41
|
+
end
|
42
|
+
|
43
|
+
def timestamped_directory_sync!(bucket, directory, options={})
|
44
|
+
Uploader.timestamped_directory_sync!(self, bucket, directory, options)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Utility Methods
|
48
|
+
def buckets
|
49
|
+
@proxy_service.buckets.map { |b| b.name }
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def test_connection
|
55
|
+
begin
|
56
|
+
@proxy_service.send(:service_request, :get)
|
57
|
+
rescue S3::Error::SignatureDoesNotMatch => e
|
58
|
+
raise Error, "Invalid AWS Key and/or Secret provided."
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
module SmoothS3
|
2
|
+
class Uploader
|
3
|
+
|
4
|
+
def self.upload(service, bucket, files, options={})
|
5
|
+
options[:overwrite] = false unless options[:overwrite]
|
6
|
+
Bucket.select(bucket, service)
|
7
|
+
|
8
|
+
valid_files = Uploader.validate_files(files)
|
9
|
+
valid_files.each do |vf|
|
10
|
+
Bucket.store_file(vf, vf.split("/")[-1], bucket, service, options[:prefix], options[:overwrite])
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.upload!(service, bucket, files, options={})
|
15
|
+
options.merge!(:overwrite => true)
|
16
|
+
Uploader.upload(service, bucket, files, options)
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.sync_directory(service, bucket, directory, options={})
|
20
|
+
options[:overwrite] = false unless options[:overwrite]
|
21
|
+
Bucket.select(bucket, service)
|
22
|
+
|
23
|
+
valid_files = Uploader.validate_files_in_directory(directory)
|
24
|
+
valid_files.each do |vf|
|
25
|
+
Bucket.store_file(vf[0], vf[1], bucket, service, options[:prefix], options[:overwrite])
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.sync_directory!(service, bucket, directory, options={})
|
30
|
+
options.merge!(:overwrite => true)
|
31
|
+
Uploader.sync_directory(service, bucket, directory, options)
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.timestamped_upload(service, bucket, files, options={})
|
35
|
+
options[:overwrite] = false unless options[:overwrite]
|
36
|
+
Bucket.select(bucket, service)
|
37
|
+
|
38
|
+
if options[:timestamp_type] == :epoch
|
39
|
+
timestamp = Time.now.strftime("%s")
|
40
|
+
elsif options[:timestamp_type] == :strftime
|
41
|
+
if options[:timestamp_format]
|
42
|
+
timestamp = Time.now.strftime(options[:timestamp_format])
|
43
|
+
else
|
44
|
+
timestamp = Uploader.default_timestamp
|
45
|
+
end
|
46
|
+
else
|
47
|
+
timestamp = Uploader.default_timestamp
|
48
|
+
end
|
49
|
+
|
50
|
+
valid_files = Uploader.validate_files(files)
|
51
|
+
valid_files.each do |vf|
|
52
|
+
Bucket.store_file(vf, timestamp + "_" + vf.split("/")[-1], bucket, service, options[:prefix], options[:overwrite])
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.timestamped_upload!(service, bucket, files, options={})
|
57
|
+
options.merge!(:overwrite => true)
|
58
|
+
Uploader.timestamped_upload(service, bucket, files, options)
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.timestamped_directory_sync(service, bucket, directory, options={})
|
62
|
+
options[:overwrite] = false unless options[:overwrite]
|
63
|
+
Bucket.select(bucket, service)
|
64
|
+
|
65
|
+
if options[:timestamp_type] == :epoch
|
66
|
+
timestamp = Time.now.strftime("%s")
|
67
|
+
elsif options[:timestamp_type] == :strftime
|
68
|
+
if options[:timestamp_format]
|
69
|
+
timestamp = Time.now.strftime(options[:timestamp_format])
|
70
|
+
else
|
71
|
+
timestamp = Uploader.default_timestamp
|
72
|
+
end
|
73
|
+
else
|
74
|
+
timestamp = Uploader.default_timestamp
|
75
|
+
end
|
76
|
+
|
77
|
+
valid_files = Uploader.validate_files_in_directory(directory)
|
78
|
+
valid_files.each do |vf|
|
79
|
+
Bucket.store_file(vf[0], timestamp + "_" + vf[1], bucket, service, options[:prefix], options[:overwrite])
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def self.timestamped_directory_sync!(service, bucket, directory, options={})
|
84
|
+
options.merge!(:overwrite => true)
|
85
|
+
Uploader.timestamped_directory_sync(service, bucket, directory, options)
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
def self.validate_files(files)
|
91
|
+
valid_files = []
|
92
|
+
|
93
|
+
files = [files] if files.class == String
|
94
|
+
files.each do |f|
|
95
|
+
begin
|
96
|
+
file = File.open(f, "r")
|
97
|
+
valid_files << f
|
98
|
+
file.close
|
99
|
+
rescue Errno::ENOENT
|
100
|
+
puts "'#{f}' is an invalid file. Skipping."
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
valid_files
|
105
|
+
end
|
106
|
+
|
107
|
+
def self.validate_files_in_directory(directory)
|
108
|
+
valid_files = []
|
109
|
+
|
110
|
+
begin
|
111
|
+
Find.find(directory) do |f|
|
112
|
+
next if File.directory?(f)
|
113
|
+
|
114
|
+
file_name = directory.split("/")[-1] + "/" + f.gsub(directory + "/", "")
|
115
|
+
valid_files << [f, file_name]
|
116
|
+
end
|
117
|
+
rescue Errno::ENOENT
|
118
|
+
raise SmoothS3::Error, "'#{directory}' is not a valid directory."
|
119
|
+
end
|
120
|
+
|
121
|
+
valid_files
|
122
|
+
end
|
123
|
+
|
124
|
+
def self.default_timestamp
|
125
|
+
Time.now.strftime("%Y%m%d%H%M%S")
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
129
|
+
end
|
data/lib/smooth_s3.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__)) unless $LOAD_PATH.include?(File.dirname(__FILE__))
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 's3'
|
5
|
+
|
6
|
+
require 'find'
|
7
|
+
|
8
|
+
require 'smooth_s3/service.rb'
|
9
|
+
require 'smooth_s3/bucket.rb'
|
10
|
+
require 'smooth_s3/uploader.rb'
|
11
|
+
require 'smooth_s3/error.rb'
|
12
|
+
|
13
|
+
module SmoothS3
|
14
|
+
VERSION = "0.1.0"
|
15
|
+
end
|
metadata
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: smooth_s3
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Nicholas Brochu
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-12-08 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: s3
|
16
|
+
requirement: &2165830360 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 0.3.9
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *2165830360
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: jeweler
|
27
|
+
requirement: &2165829340 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *2165829340
|
36
|
+
description: A user-friendly superset of the S3 gem geared towards file system backup
|
37
|
+
operations. Simplifies standard actions such as basic uploads, for example allowing
|
38
|
+
multiple files to be uploaded in one operation and adds new functionality such as
|
39
|
+
directory syncs and timestamped uploads.
|
40
|
+
email: info@nicholasbrochu.com
|
41
|
+
executables: []
|
42
|
+
extensions: []
|
43
|
+
extra_rdoc_files:
|
44
|
+
- README.rdoc
|
45
|
+
- README.textile
|
46
|
+
files:
|
47
|
+
- Gemfile
|
48
|
+
- Gemfile.lock
|
49
|
+
- README.textile
|
50
|
+
- VERSION
|
51
|
+
- lib/smooth_s3.rb
|
52
|
+
- lib/smooth_s3/bucket.rb
|
53
|
+
- lib/smooth_s3/error.rb
|
54
|
+
- lib/smooth_s3/service.rb
|
55
|
+
- lib/smooth_s3/uploader.rb
|
56
|
+
- README.rdoc
|
57
|
+
homepage: http://github.com/nbrochu/smooth_s3
|
58
|
+
licenses:
|
59
|
+
- MIT
|
60
|
+
post_install_message:
|
61
|
+
rdoc_options: []
|
62
|
+
require_paths:
|
63
|
+
- lib
|
64
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
segments:
|
71
|
+
- 0
|
72
|
+
hash: 3856766489077473240
|
73
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
74
|
+
none: false
|
75
|
+
requirements:
|
76
|
+
- - ! '>='
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: '0'
|
79
|
+
requirements: []
|
80
|
+
rubyforge_project:
|
81
|
+
rubygems_version: 1.8.10
|
82
|
+
signing_key:
|
83
|
+
specification_version: 3
|
84
|
+
summary: A user-friendly superset of the S3 gem geared towards file system backup
|
85
|
+
operations.
|
86
|
+
test_files: []
|