smooth_s3 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem "s3", "~> 0.3.9"
4
+ gem "jeweler"
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,3 @@
1
+ module SmoothS3
2
+ class Error < StandardError ; end
3
+ 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: []