s3-publisher 0.4.0
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/.document +5 -0
- data/.gitignore +5 -0
- data/LICENSE +20 -0
- data/README.rdoc +38 -0
- data/Rakefile +58 -0
- data/VERSION +1 -0
- data/lib/s3-publisher.rb +128 -0
- data/s3-publisher.gemspec +60 -0
- data/test/s3-publisher_test.rb +127 -0
- data/test/test_helper.rb +11 -0
- metadata +95 -0
data/.document
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Ben Koski
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
= s3-publisher
|
2
|
+
|
3
|
+
S3Publisher is meant as a clean, simple, sensible-defaults way to publish
|
4
|
+
files to Amazon S3 for the world to see.
|
5
|
+
|
6
|
+
Basic usage:
|
7
|
+
|
8
|
+
require 's3-publisher'
|
9
|
+
S3Publisher.publish('my-bucket') do |p|
|
10
|
+
p.push('test.txt', 'abc1234')
|
11
|
+
end
|
12
|
+
|
13
|
+
This will:
|
14
|
+
* push test.txt to my-bucket.s3.amazonaws.com
|
15
|
+
* use reduced redundancy storage
|
16
|
+
* set security to public-read
|
17
|
+
* gzip contents ('abc1234') and set a Content-Encoding: gzip header so clients know to decompress
|
18
|
+
* set a Cache-Control: max-age=5 header
|
19
|
+
|
20
|
+
Slightly more advanced example:
|
21
|
+
|
22
|
+
S3Publisher.publish('my-bucket', :base_path => 'world_cup') do |p|
|
23
|
+
p.push('events.xml', '<xml>...', :ttl => 15)
|
24
|
+
end
|
25
|
+
|
26
|
+
In this example:
|
27
|
+
* file will be written to my-bucket.s3.amazonaws.com/world_cup/events.xml
|
28
|
+
* Cache-Control: max-age=15 will be set
|
29
|
+
|
30
|
+
A few miscellaneous notes:
|
31
|
+
* gzip compress is skipped on .jpg/gif/png/tif files
|
32
|
+
* uploads are multi-threaded. You can control worker thread count on instantiation.
|
33
|
+
|
34
|
+
See class docs for further options.
|
35
|
+
|
36
|
+
== Copyright
|
37
|
+
|
38
|
+
Copyright (c) 2010 Ben Koski. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "s3-publisher"
|
8
|
+
gem.summary = %Q{Publish data to S3 for the world to see}
|
9
|
+
gem.description = %Q{Publish data to S3 for the world to see}
|
10
|
+
gem.email = "gems@benkoski.com"
|
11
|
+
gem.homepage = "http://github.com/bkoski/s3-publisher"
|
12
|
+
gem.authors = ["Ben Koski"]
|
13
|
+
gem.add_development_dependency "thoughtbot-shoulda"
|
14
|
+
gem.add_dependency 'aws_credentials'
|
15
|
+
gem.add_dependency 'right_aws', '=2.0.0'
|
16
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
17
|
+
end
|
18
|
+
rescue LoadError
|
19
|
+
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
20
|
+
end
|
21
|
+
|
22
|
+
require 'rake/testtask'
|
23
|
+
Rake::TestTask.new(:test) do |test|
|
24
|
+
test.libs << 'lib' << 'test'
|
25
|
+
test.pattern = 'test/**/*_test.rb'
|
26
|
+
test.verbose = true
|
27
|
+
end
|
28
|
+
|
29
|
+
begin
|
30
|
+
require 'rcov/rcovtask'
|
31
|
+
Rcov::RcovTask.new do |test|
|
32
|
+
test.libs << 'test'
|
33
|
+
test.pattern = 'test/**/*_test.rb'
|
34
|
+
test.verbose = true
|
35
|
+
end
|
36
|
+
rescue LoadError
|
37
|
+
task :rcov do
|
38
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
task :test => :check_dependencies
|
43
|
+
|
44
|
+
task :default => :test
|
45
|
+
|
46
|
+
require 'rake/rdoctask'
|
47
|
+
Rake::RDocTask.new do |rdoc|
|
48
|
+
if File.exist?('VERSION')
|
49
|
+
version = File.read('VERSION')
|
50
|
+
else
|
51
|
+
version = ""
|
52
|
+
end
|
53
|
+
|
54
|
+
rdoc.rdoc_dir = 'rdoc'
|
55
|
+
rdoc.title = "s3-publisher #{version}"
|
56
|
+
rdoc.rdoc_files.include('README*')
|
57
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
58
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.4.0
|
data/lib/s3-publisher.rb
ADDED
@@ -0,0 +1,128 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
|
3
|
+
gem 'right_aws', '=2.0.0'
|
4
|
+
require 'right_aws'
|
5
|
+
|
6
|
+
require 'aws_credentials'
|
7
|
+
require 'zlib'
|
8
|
+
require 'thread'
|
9
|
+
|
10
|
+
Thread.abort_on_exception = true
|
11
|
+
|
12
|
+
# You can either use the block syntax, or:
|
13
|
+
# * instantiate a class
|
14
|
+
# * queue data to be published with push
|
15
|
+
# * call run to actually upload the data to S3
|
16
|
+
class S3Publisher
|
17
|
+
|
18
|
+
attr_reader :bucket_name, :base_path, :logger, :workers_to_use
|
19
|
+
|
20
|
+
# Block style. run is called for you on block close.
|
21
|
+
# S3Publisher.publish('my-bucket') do |p|
|
22
|
+
# p.push('test.txt', '123abc')
|
23
|
+
# end
|
24
|
+
def self.publish bucket_name, opts={}, &block
|
25
|
+
p = self.new(bucket_name, opts)
|
26
|
+
yield(p)
|
27
|
+
p.run
|
28
|
+
end
|
29
|
+
|
30
|
+
# Pass the publisher a bucket_name along with any of the following options:
|
31
|
+
# * <tt>base_path</tt> - path prepended to supplied file_name on upload
|
32
|
+
# * <tt>logger</tt> - a logger object to recieve 'uploaded' messages. Defaults to STDOUT.
|
33
|
+
# * <tt>workers</tt> - number of threads to use when pushing to S3. Defaults to 3.
|
34
|
+
def initialize bucket_name, opts={}
|
35
|
+
@s3 = RightAws::S3.new(AWSCredentials.access_key, AWSCredentials.secret_access_key, :multi_thread => true,
|
36
|
+
:protocol => 'http',
|
37
|
+
:port => 80,
|
38
|
+
:logger => Logger.new(nil))
|
39
|
+
@bucket_name, @base_path = bucket_name, opts[:base_path]
|
40
|
+
@logger = opts[:logger] || STDOUT
|
41
|
+
@workers_to_use = opts[:workers] || 3
|
42
|
+
@publish_queue = Queue.new
|
43
|
+
end
|
44
|
+
|
45
|
+
# Pass:
|
46
|
+
# * <tt>file_name</tt> - name of file on S3. base_path will be prepended if supplied on instantiate.
|
47
|
+
# * <tt>data</tt> - data to be uploaded as a string
|
48
|
+
#
|
49
|
+
# And one or many options:
|
50
|
+
# * <tt>:gzip (true|false)</tt> - gzip file contents? defaults to true.
|
51
|
+
# * <tt>:ttl</tt> - TTL in seconds for cache-control header. defaults to 5.
|
52
|
+
# * <tt>:cache_control</tt> - specify Cache-Control header directly if you don't like the default
|
53
|
+
# * <tt>:content_type</tt> - no need to specify if default based on extension is okay. But if you need to force,
|
54
|
+
# you can provide :xml, :html, :text, or your own custom string.
|
55
|
+
# * <tt>:redundancy</tt> - by default objects are stored at reduced redundancy, pass :standard to store at full
|
56
|
+
def push file_name, data, opts={}
|
57
|
+
headers = {}
|
58
|
+
|
59
|
+
file_name = "#{base_path}/#{file_name}" unless base_path.nil?
|
60
|
+
|
61
|
+
unless opts[:gzip] == false || file_name.match(/\.(jpg|gif|png|tif)$/)
|
62
|
+
data = gzip(data)
|
63
|
+
headers['Content-Encoding'] = 'gzip'
|
64
|
+
end
|
65
|
+
|
66
|
+
headers['x-amz-storage-class'] = opts[:redundancy] == :standard ? 'STANDARD' : 'REDUCED_REDUNDANCY'
|
67
|
+
headers['Content-Type'] = parse_content_type(opts[:content_type]) if opts[:content_type]
|
68
|
+
|
69
|
+
if opts.has_key?(:cache_control)
|
70
|
+
headers['Cache-Control'] = opts[:cache_control]
|
71
|
+
else
|
72
|
+
headers['Cache-Control'] = "max-age=#{opts[:ttl] || 5}"
|
73
|
+
end
|
74
|
+
|
75
|
+
@publish_queue.push({:key_name => file_name, :data => data, :headers => headers})
|
76
|
+
end
|
77
|
+
|
78
|
+
# Process queued uploads and push to S3
|
79
|
+
def run
|
80
|
+
threads = []
|
81
|
+
workers_to_use.times { threads << Thread.new { publish_from_queue } }
|
82
|
+
threads.each { |t| t.join }
|
83
|
+
true
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
87
|
+
def gzip data
|
88
|
+
gzipped_data = StringIO.open('', 'w+')
|
89
|
+
|
90
|
+
gzip_writer = Zlib::GzipWriter.new(gzipped_data)
|
91
|
+
gzip_writer.write(data)
|
92
|
+
gzip_writer.close
|
93
|
+
|
94
|
+
return gzipped_data.string
|
95
|
+
end
|
96
|
+
|
97
|
+
def parse_content_type content_type
|
98
|
+
case content_type
|
99
|
+
when :xml
|
100
|
+
'application/xml'
|
101
|
+
when :text
|
102
|
+
'text/plain'
|
103
|
+
when :html
|
104
|
+
'text/html'
|
105
|
+
else
|
106
|
+
content_type
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def publish_from_queue
|
111
|
+
loop do
|
112
|
+
item = @publish_queue.pop(true)
|
113
|
+
|
114
|
+
try_count = 0
|
115
|
+
begin
|
116
|
+
@s3.bucket(bucket_name).put(item[:key_name], item[:data], {}, 'public-read', item[:headers])
|
117
|
+
rescue Exception => e # backstop against transient S3 errors
|
118
|
+
raise e if try_count >= 1
|
119
|
+
try_count += 1
|
120
|
+
retry
|
121
|
+
end
|
122
|
+
|
123
|
+
logger << "Wrote http://#{bucket_name}.s3.amazonaws.com/#{item[:key_name]}"
|
124
|
+
end
|
125
|
+
rescue ThreadError # ThreadError hit when queue is empty. Simply jump out of loop and return to join().
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{s3-publisher}
|
8
|
+
s.version = "0.4.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Ben Koski"]
|
12
|
+
s.date = %q{2010-06-29}
|
13
|
+
s.description = %q{Publish data to S3 for the world to see}
|
14
|
+
s.email = %q{gems@benkoski.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE",
|
17
|
+
"README.rdoc"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".document",
|
21
|
+
".gitignore",
|
22
|
+
"LICENSE",
|
23
|
+
"README.rdoc",
|
24
|
+
"Rakefile",
|
25
|
+
"VERSION",
|
26
|
+
"lib/s3-publisher.rb",
|
27
|
+
"s3-publisher.gemspec",
|
28
|
+
"test/s3-publisher_test.rb",
|
29
|
+
"test/test_helper.rb"
|
30
|
+
]
|
31
|
+
s.homepage = %q{http://github.com/bkoski/s3-publisher}
|
32
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
33
|
+
s.require_paths = ["lib"]
|
34
|
+
s.rubygems_version = %q{1.3.5}
|
35
|
+
s.summary = %q{Publish data to S3 for the world to see}
|
36
|
+
s.test_files = [
|
37
|
+
"test/s3-publisher_test.rb",
|
38
|
+
"test/test_helper.rb"
|
39
|
+
]
|
40
|
+
|
41
|
+
if s.respond_to? :specification_version then
|
42
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
43
|
+
s.specification_version = 3
|
44
|
+
|
45
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
46
|
+
s.add_development_dependency(%q<thoughtbot-shoulda>, [">= 0"])
|
47
|
+
s.add_runtime_dependency(%q<aws_credentials>, [">= 0"])
|
48
|
+
s.add_runtime_dependency(%q<right_aws>, ["= 2.0.0"])
|
49
|
+
else
|
50
|
+
s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
|
51
|
+
s.add_dependency(%q<aws_credentials>, [">= 0"])
|
52
|
+
s.add_dependency(%q<right_aws>, ["= 2.0.0"])
|
53
|
+
end
|
54
|
+
else
|
55
|
+
s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
|
56
|
+
s.add_dependency(%q<aws_credentials>, [">= 0"])
|
57
|
+
s.add_dependency(%q<right_aws>, ["= 2.0.0"])
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
@@ -0,0 +1,127 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class S3PublisherTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
context "push" do
|
6
|
+
|
7
|
+
context "file_name" do
|
8
|
+
should "prepend base_path if provided on instantiate" do
|
9
|
+
set_put_expectation(:key_name => 'world_cup_2010/events.xml')
|
10
|
+
p = S3Publisher.new('test-bucket', :logger => Logger.new(nil), :base_path => 'world_cup_2010')
|
11
|
+
p.push('events.xml', '1234')
|
12
|
+
p.run
|
13
|
+
end
|
14
|
+
|
15
|
+
should "pass through unaltered if base_path not specified" do
|
16
|
+
set_put_expectation(:key_name => 'events.xml')
|
17
|
+
p = S3Publisher.new('test-bucket', :logger => Logger.new(nil))
|
18
|
+
p.push('events.xml', '1234')
|
19
|
+
p.run
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
context "gzip" do
|
24
|
+
should "gzip data if :gzip => true" do
|
25
|
+
set_put_expectation(:data => gzip('1234'))
|
26
|
+
push_test_data('myfile.txt', '1234', :gzip => true)
|
27
|
+
end
|
28
|
+
|
29
|
+
should "not gzip data if :gzip => false" do
|
30
|
+
set_put_expectation(:data => '1234')
|
31
|
+
push_test_data('myfile.txt', '1234', :gzip => false)
|
32
|
+
end
|
33
|
+
|
34
|
+
should "not gzip data if file ends in .jpg" do
|
35
|
+
set_put_expectation(:data => '1234')
|
36
|
+
push_test_data('myfile.jpg', '1234', {})
|
37
|
+
end
|
38
|
+
|
39
|
+
should "gzip data by default" do
|
40
|
+
set_put_expectation(:data => gzip('1234'))
|
41
|
+
push_test_data('myfile.txt', '1234', {})
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context "redundancy" do
|
46
|
+
should "set REDUCED_REDUNDANCY by default" do
|
47
|
+
set_put_expectation(:headers => { 'x-amz-storage-class' => 'REDUCED_REDUNDANCY' })
|
48
|
+
push_test_data('myfile.txt', '1234', {})
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
should "set STANDARD if :redundancy => :standard is passed" do
|
53
|
+
set_put_expectation(:headers => { 'x-amz-storage-class' => 'STANDARD' })
|
54
|
+
push_test_data('myfile.txt', '1234', :redundancy => :standard)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
context "content type" do
|
59
|
+
should "force Content-Type to user-supplied string if provided" do
|
60
|
+
set_put_expectation(:headers => { 'Content-Type' => 'audio/vorbis' })
|
61
|
+
push_test_data('myfile.txt', '1234', :content_type => 'audio/vorbis')
|
62
|
+
end
|
63
|
+
|
64
|
+
should "force Content-Type to application/xml if :xml provided" do
|
65
|
+
set_put_expectation(:headers => { 'Content-Type' => 'application/xml' })
|
66
|
+
push_test_data('myfile.txt', '1234', :content_type => :xml)
|
67
|
+
end
|
68
|
+
|
69
|
+
should "force Content-Type to text/plain if :text provided" do
|
70
|
+
set_put_expectation(:headers => { 'Content-Type' => 'text/plain' })
|
71
|
+
push_test_data('myfile.txt', '1234', :content_type => :text)
|
72
|
+
end
|
73
|
+
|
74
|
+
should "force Content-Type to text/html if :html provided" do
|
75
|
+
set_put_expectation(:headers => { 'Content-Type' => 'text/html' })
|
76
|
+
push_test_data('myfile.txt', '1234', :content_type => :html)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
context "cache-control" do
|
81
|
+
should "set Cache-Control to user-supplied string if :cache_control provided" do
|
82
|
+
set_put_expectation(:headers => { 'Cache-Control' => 'private, max-age=0' })
|
83
|
+
push_test_data('myfile.txt', '1234', :cache_control => 'private, max-age=0')
|
84
|
+
end
|
85
|
+
|
86
|
+
should "set Cache-Control with :ttl provided" do
|
87
|
+
set_put_expectation(:headers => { 'Cache-Control' => 'max-age=55' })
|
88
|
+
push_test_data('myfile.txt', '1234', :ttl => 55)
|
89
|
+
end
|
90
|
+
|
91
|
+
should "set Cache-Control to a 5s ttl if no :ttl or :cache_control was provided" do
|
92
|
+
set_put_expectation(:headers => { 'Cache-Control' => 'max-age=5' })
|
93
|
+
push_test_data('myfile.txt', '1234', {})
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
|
98
|
+
|
99
|
+
|
100
|
+
end
|
101
|
+
|
102
|
+
def set_put_expectation opts
|
103
|
+
s3_stub = mock()
|
104
|
+
bucket_stub = mock()
|
105
|
+
bucket_stub.expects(:put).with(opts[:key_name] || anything, opts[:data] || anything, {}, 'public-read', opts[:headers] ? has_entries(opts[:headers]) : anything)
|
106
|
+
|
107
|
+
s3_stub.stubs(:bucket).returns(bucket_stub)
|
108
|
+
RightAws::S3.stubs(:new).returns(s3_stub)
|
109
|
+
end
|
110
|
+
|
111
|
+
def gzip data
|
112
|
+
gzipped_data = StringIO.open('', 'w+')
|
113
|
+
|
114
|
+
gzip_writer = Zlib::GzipWriter.new(gzipped_data)
|
115
|
+
gzip_writer.write(data)
|
116
|
+
gzip_writer.close
|
117
|
+
|
118
|
+
return gzipped_data.string
|
119
|
+
end
|
120
|
+
|
121
|
+
def push_test_data file_name, data, opts
|
122
|
+
p = S3Publisher.new('test-bucket', :logger => Logger.new(nil))
|
123
|
+
p.push(file_name, data, opts)
|
124
|
+
p.run
|
125
|
+
end
|
126
|
+
|
127
|
+
end
|
data/test/test_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: s3-publisher
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.4.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ben Koski
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2010-06-29 00:00:00 -04:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: thoughtbot-shoulda
|
17
|
+
type: :development
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: aws_credentials
|
27
|
+
type: :runtime
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: "0"
|
34
|
+
version:
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: right_aws
|
37
|
+
type: :runtime
|
38
|
+
version_requirement:
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - "="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 2.0.0
|
44
|
+
version:
|
45
|
+
description: Publish data to S3 for the world to see
|
46
|
+
email: gems@benkoski.com
|
47
|
+
executables: []
|
48
|
+
|
49
|
+
extensions: []
|
50
|
+
|
51
|
+
extra_rdoc_files:
|
52
|
+
- LICENSE
|
53
|
+
- README.rdoc
|
54
|
+
files:
|
55
|
+
- .document
|
56
|
+
- .gitignore
|
57
|
+
- LICENSE
|
58
|
+
- README.rdoc
|
59
|
+
- Rakefile
|
60
|
+
- VERSION
|
61
|
+
- lib/s3-publisher.rb
|
62
|
+
- s3-publisher.gemspec
|
63
|
+
- test/s3-publisher_test.rb
|
64
|
+
- test/test_helper.rb
|
65
|
+
has_rdoc: true
|
66
|
+
homepage: http://github.com/bkoski/s3-publisher
|
67
|
+
licenses: []
|
68
|
+
|
69
|
+
post_install_message:
|
70
|
+
rdoc_options:
|
71
|
+
- --charset=UTF-8
|
72
|
+
require_paths:
|
73
|
+
- lib
|
74
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
75
|
+
requirements:
|
76
|
+
- - ">="
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: "0"
|
79
|
+
version:
|
80
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
81
|
+
requirements:
|
82
|
+
- - ">="
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: "0"
|
85
|
+
version:
|
86
|
+
requirements: []
|
87
|
+
|
88
|
+
rubyforge_project:
|
89
|
+
rubygems_version: 1.3.5
|
90
|
+
signing_key:
|
91
|
+
specification_version: 3
|
92
|
+
summary: Publish data to S3 for the world to see
|
93
|
+
test_files:
|
94
|
+
- test/s3-publisher_test.rb
|
95
|
+
- test/test_helper.rb
|