s3-publisher 0.4.4 → 0.9.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.
- checksums.yaml +4 -4
- data/.rspec +2 -0
- data/README.rdoc +42 -26
- data/lib/s3-publisher.rb +69 -55
- data/s3-publisher.gemspec +5 -5
- data/spec/s3_publisher_spec.rb +138 -0
- data/spec/spec_helper.rb +27 -0
- metadata +16 -15
- data/test/s3-publisher_test.rb +0 -127
- data/test/test_helper.rb +0 -11
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 228be6f6a0276a570e4abae98164b259ce62bfa0
|
|
4
|
+
data.tar.gz: 7c489b18c2183fa36648ddd36b2afdcb0e8f86a6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 423f3fa87a913e384771e99d2f0c6a23016f95bc6bff62a7ef6d2d54977ade31e165f8af11dbfa0dfdcdde427e0ea915214474775370b41134b9b1c5ed36f4fe
|
|
7
|
+
data.tar.gz: b4e9a958d93e70cf6273198f87a593b377296806f83bdaa71f2aff28456ebe590ea6649ef7a5649d801dc6b7095445ca8d3a5bcad87b6f6c51b51c5baa807acc
|
data/.rspec
ADDED
data/README.rdoc
CHANGED
|
@@ -1,39 +1,55 @@
|
|
|
1
|
-
|
|
1
|
+
# s3-publisher
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
files to Amazon S3 for the world to see.
|
|
3
|
+
Quickly pub your data files to S3.
|
|
5
4
|
|
|
6
|
-
|
|
5
|
+
Reasons you might want to use this instead of aws-sdk directly:
|
|
7
6
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
* parallel uploads using ruby threads. Concurrency defaults to 3 but can be increased.
|
|
8
|
+
* gzip, by default S3Publisher gzips so you don't have to.
|
|
9
|
+
* try-again technology, it will retry if a S3 request fails.
|
|
10
|
+
* no need to remember all the correct opts for content-type, acl, etc.
|
|
11
|
+
|
|
12
|
+
### Basic usage:
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
require 's3-publisher'
|
|
16
|
+
S3Publisher.publish('my-bucket') do |p|
|
|
17
|
+
p.push('test.txt', data: 'abc1234')
|
|
18
|
+
end
|
|
19
|
+
```
|
|
12
20
|
|
|
13
21
|
This will:
|
|
14
|
-
* push test.txt to my-bucket.s3.amazonaws.com
|
|
15
|
-
* set security to public-read
|
|
16
|
-
* gzip contents ('abc1234') and set a Content-Encoding: gzip header so clients know to decompress
|
|
17
|
-
* set a Cache-Control: max-age=5 header
|
|
18
22
|
|
|
19
|
-
|
|
23
|
+
* push test.txt to my-bucket.s3.amazonaws.com
|
|
24
|
+
* set security to public-read
|
|
25
|
+
* gzip contents ('abc1234') and set a Content-Encoding: gzip header so clients know to decompress
|
|
26
|
+
* set a Cache-Control: max-age=5 header
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
You can also pass file paths, rather than string data. Files aren't read until publish-time, saving memory.
|
|
20
30
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
31
|
+
```
|
|
32
|
+
require 's3-publisher'
|
|
33
|
+
S3Publisher.publish('my-bucket') do |p|
|
|
34
|
+
p.push('test.json', file: '/tmp/test.json')
|
|
35
|
+
end
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Slightly more advanced example:
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
S3Publisher.publish('my-bucket', :base_path => 'world_cup') do |p|
|
|
42
|
+
p.push('events.xml', '<xml>...', :ttl => 15)
|
|
43
|
+
end
|
|
44
|
+
```
|
|
24
45
|
|
|
25
46
|
In this example:
|
|
26
|
-
|
|
27
|
-
*
|
|
47
|
+
|
|
48
|
+
* file will be written to my-bucket.s3.amazonaws.com/world_cup/events.xml
|
|
49
|
+
* Cache-Control: max-age=15 will be set
|
|
28
50
|
|
|
29
51
|
A few miscellaneous notes:
|
|
30
|
-
* gzip compress is skipped on .jpg/gif/png/tif files
|
|
31
|
-
* uploads are multi-threaded. You can control worker thread count on instantiation.
|
|
32
|
-
* pass :redundancy => :reduced when instantiating the publisher to write to reduced
|
|
33
|
-
redundancy storage (this used to be the default, but now requires the option to be set.)
|
|
34
|
-
|
|
35
|
-
See class docs for further options.
|
|
36
52
|
|
|
37
|
-
|
|
53
|
+
* gzip compress is skipped on .jpg/gif/png/tif files
|
|
38
54
|
|
|
39
|
-
|
|
55
|
+
See class docs for more options.
|
data/lib/s3-publisher.rb
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
require 'zlib'
|
|
2
2
|
require 'thread'
|
|
3
|
+
require 'pathname'
|
|
3
4
|
|
|
4
|
-
require '
|
|
5
|
-
require '
|
|
6
|
-
|
|
7
|
-
Thread.abort_on_exception = true
|
|
5
|
+
require 'aws-sdk'
|
|
6
|
+
require 'mime-types'
|
|
8
7
|
|
|
9
8
|
# You can either use the block syntax, or:
|
|
10
9
|
# * instantiate a class
|
|
@@ -24,54 +23,71 @@ class S3Publisher
|
|
|
24
23
|
p.run
|
|
25
24
|
end
|
|
26
25
|
|
|
27
|
-
#
|
|
28
|
-
#
|
|
29
|
-
#
|
|
30
|
-
#
|
|
31
|
-
# * <tt>workers</tt> - number of threads to use when pushing to S3. Defaults to 3.
|
|
26
|
+
# @param [String] bucket_name
|
|
27
|
+
# @option opts [String] :base_path Path prepended to supplied file_name on upload
|
|
28
|
+
# @option opts [Integer] :workers Number of threads to use when pushing to S3. Defaults to 3.
|
|
29
|
+
# @option opts [Object] :logger A logger object to recieve 'uploaded' messages. Defaults to STDOUT.
|
|
32
30
|
def initialize bucket_name, opts={}
|
|
33
|
-
@s3 = RightAws::S3.new(AWSCredentials.access_key, AWSCredentials.secret_access_key, :multi_thread => true,
|
|
34
|
-
:protocol => opts[:protocol] || 'http',
|
|
35
|
-
:port => 80,
|
|
36
|
-
:logger => Logger.new(nil))
|
|
37
|
-
@bucket_name, @base_path = bucket_name, opts[:base_path]
|
|
38
|
-
raise ArgumentError, "#{bucket_name} doesn't seem to be a valid bucket on your account" if @s3.bucket(bucket_name).nil?
|
|
39
|
-
@logger = opts[:logger] || $stdout
|
|
40
|
-
@workers_to_use = opts[:workers] || 3
|
|
41
31
|
@publish_queue = Queue.new
|
|
32
|
+
@workers_to_use = opts[:workers] || 3
|
|
33
|
+
@logger = opts[:logger] || $stdout
|
|
34
|
+
|
|
35
|
+
s3_opts = {}
|
|
36
|
+
s3_opts[:access_key_id] = opts[:access_key_id] if opts.key?(:access_key_id)
|
|
37
|
+
s3_opts[:secret_access_key] = opts[:secret_access_key] if opts.key?(:secret_access_key)
|
|
38
|
+
|
|
39
|
+
@s3 = AWS::S3.new(s3_opts)
|
|
40
|
+
|
|
41
|
+
@bucket_name, @base_path = bucket_name, opts[:base_path]
|
|
42
|
+
raise ArgumentError, "#{bucket_name} doesn't seem to be a valid bucket on your account" if @s3.buckets[bucket_name].nil?
|
|
42
43
|
end
|
|
43
|
-
|
|
44
|
-
#
|
|
45
|
-
#
|
|
46
|
-
#
|
|
44
|
+
|
|
45
|
+
# Queues a file to be published.
|
|
46
|
+
# You can provide :data as a string, or a path to a file with :file.
|
|
47
|
+
# :file references won't be evaluated until publish-time, reducing memory overhead.
|
|
47
48
|
#
|
|
48
|
-
#
|
|
49
|
-
#
|
|
50
|
-
#
|
|
51
|
-
#
|
|
52
|
-
#
|
|
53
|
-
#
|
|
54
|
-
#
|
|
55
|
-
|
|
56
|
-
|
|
49
|
+
# @param [String] key_name The name of the file on S3. base_path will be prepended if supplied.
|
|
50
|
+
# @option opts [String] :data a string to be published
|
|
51
|
+
# @option opts [String] :file path to a file to publish
|
|
52
|
+
# @option opts [Boolean] :gzip gzip file contents? defaults to true.
|
|
53
|
+
# @option opts [Integer] :ttl TTL in seconds for cache-control header. defaults to 5.
|
|
54
|
+
# @option opts [String] :cache_control specify Cache-Control header directly if you don't like the default
|
|
55
|
+
# @option opts [String] :content_type no need to specify if default based on extension is okay. But if you need to force,
|
|
56
|
+
# you can provide :xml, :html, :text, or your own custom string.
|
|
57
|
+
def push key_name, opts={}
|
|
58
|
+
write_opts = { acl: 'public-read' }
|
|
57
59
|
|
|
58
|
-
|
|
60
|
+
key_name = "#{base_path}/#{key_name}" unless base_path.nil?
|
|
59
61
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
62
|
+
# Setup data.
|
|
63
|
+
if opts[:data]
|
|
64
|
+
contents = opts[:data]
|
|
65
|
+
elsif opts[:file]
|
|
66
|
+
contents = Pathname.new(opts[:file])
|
|
67
|
+
raise ArgumentError, "'#{opts[:file]}' does not exist!" if !contents.exist?
|
|
68
|
+
else
|
|
69
|
+
raise ArgumentError, "A :file or :data attr must be provided to publish to S3!"
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Then Content-Type
|
|
73
|
+
if opts[:content_type]
|
|
74
|
+
write_opts[:content_type] = opts[:content_type]
|
|
75
|
+
else
|
|
76
|
+
matching_mimes = MIME::Types.type_for(key_name)
|
|
77
|
+
raise ArgumentError, "Can't infer the content-type for '#{key_name}'! Please specify with the :content_type opt." if matching_mimes.empty?
|
|
78
|
+
write_opts[:content_type] = matching_mimes.first.to_s
|
|
63
79
|
end
|
|
64
|
-
|
|
65
|
-
headers['x-amz-storage-class'] = opts[:redundancy] == :reduced ? 'REDUCED_REDUNDANCY' : 'STANDARD'
|
|
66
|
-
headers['content-type'] = parse_content_type(opts[:content_type]) if opts[:content_type]
|
|
67
80
|
|
|
81
|
+
# And Cache-Control
|
|
68
82
|
if opts.has_key?(:cache_control)
|
|
69
|
-
|
|
83
|
+
write_opts[:cache_control] = opts[:cache_control]
|
|
70
84
|
else
|
|
71
|
-
|
|
85
|
+
write_opts[:cache_control] = "max-age=#{opts[:ttl] || 5}"
|
|
72
86
|
end
|
|
73
87
|
|
|
74
|
-
|
|
88
|
+
opts[:gzip] = true unless opts.has_key?(:gzip)
|
|
89
|
+
|
|
90
|
+
@publish_queue.push({ key_name: key_name, contents: contents, write_opts: write_opts, gzip: opts[:gzip] })
|
|
75
91
|
end
|
|
76
92
|
|
|
77
93
|
# Process queued uploads and push to S3
|
|
@@ -96,19 +112,6 @@ class S3Publisher
|
|
|
96
112
|
|
|
97
113
|
return gzipped_data.string
|
|
98
114
|
end
|
|
99
|
-
|
|
100
|
-
def parse_content_type content_type
|
|
101
|
-
case content_type
|
|
102
|
-
when :xml
|
|
103
|
-
'application/xml'
|
|
104
|
-
when :text
|
|
105
|
-
'text/plain'
|
|
106
|
-
when :html
|
|
107
|
-
'text/html'
|
|
108
|
-
else
|
|
109
|
-
content_type
|
|
110
|
-
end
|
|
111
|
-
end
|
|
112
115
|
|
|
113
116
|
def publish_from_queue
|
|
114
117
|
loop do
|
|
@@ -116,14 +119,25 @@ class S3Publisher
|
|
|
116
119
|
|
|
117
120
|
try_count = 0
|
|
118
121
|
begin
|
|
119
|
-
@s3.
|
|
122
|
+
obj = @s3.buckets[bucket_name].objects[item[:key_name]]
|
|
123
|
+
|
|
124
|
+
gzip = item[:gzip] != false && !item[:key_name].match(/\.(jpg|gif|png|tif)$/)
|
|
125
|
+
|
|
126
|
+
if gzip
|
|
127
|
+
item[:write_opts][:content_encoding] = 'gzip'
|
|
128
|
+
gzip_body = item[:contents].is_a?(Pathname) ? item[:contents].read : item[:contents]
|
|
129
|
+
item[:contents] = gzip(gzip_body)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
obj.write(item[:contents], item[:write_opts])
|
|
133
|
+
|
|
120
134
|
rescue Exception => e # backstop against transient S3 errors
|
|
121
135
|
raise e if try_count >= 1
|
|
122
136
|
try_count += 1
|
|
123
137
|
retry
|
|
124
138
|
end
|
|
125
139
|
|
|
126
|
-
logger << "Wrote http://#{bucket_name}.s3.amazonaws.com/#{item[:key_name]} with #{item[:
|
|
140
|
+
logger << "Wrote http://#{bucket_name}.s3.amazonaws.com/#{item[:key_name]} with #{item[:write_opts].inspect}\n"
|
|
127
141
|
end
|
|
128
142
|
rescue ThreadError # ThreadError hit when queue is empty. Simply jump out of loop and return to join().
|
|
129
143
|
end
|
data/s3-publisher.gemspec
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
Gem::Specification.new do |s|
|
|
4
4
|
s.name = "s3-publisher"
|
|
5
|
-
s.version = "0.
|
|
5
|
+
s.version = "0.9.0"
|
|
6
6
|
s.authors = ["Ben Koski"]
|
|
7
|
-
s.email = "
|
|
7
|
+
s.email = "bkoski@nytimes.com"
|
|
8
8
|
s.summary = "Publish data to S3 for the world to see"
|
|
9
9
|
s.description = "Publish data to S3 for the world to see"
|
|
10
10
|
s.homepage = "http://github.com/bkoski/s3-publisher"
|
|
@@ -16,7 +16,7 @@ Gem::Specification.new do |s|
|
|
|
16
16
|
|
|
17
17
|
s.require_paths = ["lib"]
|
|
18
18
|
|
|
19
|
-
s.add_development_dependency(%q<
|
|
20
|
-
s.add_runtime_dependency(%q<
|
|
21
|
-
s.add_runtime_dependency(%q<
|
|
19
|
+
s.add_development_dependency(%q<rspec>, [">= 0"])
|
|
20
|
+
s.add_runtime_dependency(%q<aws-sdk>, [">= 1.0"])
|
|
21
|
+
s.add_runtime_dependency(%q<mime-types>, [">= 0"])
|
|
22
22
|
end
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe S3Publisher do
|
|
4
|
+
describe "#push" do
|
|
5
|
+
|
|
6
|
+
describe "file_name" do
|
|
7
|
+
it "prepends base_path if provided" do
|
|
8
|
+
set_put_expectation(key_name: 'world_cup_2010/events.xml')
|
|
9
|
+
p = S3Publisher.new('test-bucket', :logger => Logger.new(nil), :base_path => 'world_cup_2010')
|
|
10
|
+
p.push('events.xml', data: '1234')
|
|
11
|
+
p.run
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it "passes name through unaltered if base_path not specified" do
|
|
15
|
+
set_put_expectation(key_name: 'events.xml')
|
|
16
|
+
p = S3Publisher.new('test-bucket', :logger => Logger.new(nil))
|
|
17
|
+
p.push('events.xml', data: '1234')
|
|
18
|
+
p.run
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
describe "gzip" do
|
|
23
|
+
it "gzips data if :gzip => true" do
|
|
24
|
+
set_put_expectation(data: gzip('1234'))
|
|
25
|
+
push_test_data('myfile.txt', data: '1234', gzip: true)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
it "does not gzip data if :gzip => false" do
|
|
29
|
+
set_put_expectation(data: '1234')
|
|
30
|
+
push_test_data('myfile.txt', data: '1234', gzip: false)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it "does not gzip data if file ends in .jpg" do
|
|
34
|
+
set_put_expectation(key_name: 'myfile.jpg', data: '1234')
|
|
35
|
+
push_test_data('myfile.jpg', data: '1234')
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
it "gzips data by default" do
|
|
39
|
+
set_put_expectation(data: gzip('1234'))
|
|
40
|
+
push_test_data('myfile.txt', data: '1234')
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
describe ":file opt" do
|
|
45
|
+
it "queues files as a pathname to be read if gzip is false" do
|
|
46
|
+
set_put_expectation(file: __FILE__)
|
|
47
|
+
push_test_data('myfile.txt', file: __FILE__, gzip: false)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it "queues gzipped contents of the file if gzip is true" do
|
|
51
|
+
set_put_expectation(data: gzip(File.read(__FILE__)))
|
|
52
|
+
push_test_data('myfile.txt', file: __FILE__, gzip: true)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
describe "content type" do
|
|
57
|
+
it "detects content type based on extension" do
|
|
58
|
+
set_put_expectation(key_name: 'myfile.xml', content_type: 'application/xml')
|
|
59
|
+
push_test_data('myfile.xml', data: '1234')
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
it "forces Content-Type to user-supplied string if provided" do
|
|
63
|
+
set_put_expectation(content_type: 'audio/vorbis')
|
|
64
|
+
push_test_data('myfile.txt', data: '1234', content_type: 'audio/vorbis')
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
it "raises an exception if the content-type cannot be parsed" do
|
|
68
|
+
expect { push_test_data('myfile', data: '1234') }.to raise_error(ArgumentError)
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
describe "cache-control" do
|
|
73
|
+
it "sets Cache-Control to user-supplied string if :cache_control provided" do
|
|
74
|
+
set_put_expectation(cache_control: 'private, max-age=0')
|
|
75
|
+
push_test_data('myfile.txt', data: '1234', cache_control: 'private, max-age=0')
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
it "sets Cache-Control with :ttl provided" do
|
|
79
|
+
set_put_expectation(cache_control: 'max-age=55')
|
|
80
|
+
push_test_data('myfile.txt', data: '1234', ttl: 55)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
it "sets Cache-Control to a 5s ttl if no :ttl or :cache_control was provided" do
|
|
84
|
+
set_put_expectation(cache_control: 'max-age=5')
|
|
85
|
+
push_test_data('myfile.txt', data: '1234')
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Based on opts, sets expecations for AWS::S3Object.write
|
|
90
|
+
# Can provide expected values for:
|
|
91
|
+
# * :key_name
|
|
92
|
+
# * :data
|
|
93
|
+
# * :content_type, :cache_control, :content_encoding
|
|
94
|
+
def set_put_expectation opts
|
|
95
|
+
s3_stub = mock()
|
|
96
|
+
bucket_stub = mock()
|
|
97
|
+
object_stub = mock()
|
|
98
|
+
|
|
99
|
+
key_name = opts[:key_name] || 'myfile.txt'
|
|
100
|
+
|
|
101
|
+
expected_entries = {}
|
|
102
|
+
[:content_type, :cache_control, :content_encoding].each do |k|
|
|
103
|
+
expected_entries[k] = opts[k] if opts.has_key?(k)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
if opts[:data]
|
|
107
|
+
expected_contents = opts[:data]
|
|
108
|
+
elsif opts[:file]
|
|
109
|
+
expected_contents = Pathname.new(opts[:file])
|
|
110
|
+
else
|
|
111
|
+
expected_contents = anything
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
object_stub.expects(:write).with(expected_contents, has_entries(expected_entries))
|
|
115
|
+
|
|
116
|
+
s3_stub.stubs(:buckets).returns({'test-bucket' => bucket_stub })
|
|
117
|
+
bucket_stub.stubs(:objects).returns({ key_name => object_stub })
|
|
118
|
+
|
|
119
|
+
AWS::S3.stubs(:new).returns(s3_stub)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def gzip data
|
|
123
|
+
gzipped_data = StringIO.open('', 'w+')
|
|
124
|
+
|
|
125
|
+
gzip_writer = Zlib::GzipWriter.new(gzipped_data)
|
|
126
|
+
gzip_writer.write(data)
|
|
127
|
+
gzip_writer.close
|
|
128
|
+
|
|
129
|
+
return gzipped_data.string
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def push_test_data file_name, opts
|
|
133
|
+
p = S3Publisher.new('test-bucket', :logger => Logger.new(nil))
|
|
134
|
+
p.push(file_name, opts)
|
|
135
|
+
p.run
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
data/spec/spec_helper.rb
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# This file was generated by the `rspec --init` command. Conventionally, all
|
|
2
|
+
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
|
3
|
+
# Require this file using `require "spec_helper"` to ensure that it is only
|
|
4
|
+
# loaded once.
|
|
5
|
+
#
|
|
6
|
+
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
|
7
|
+
|
|
8
|
+
require File.expand_path('../lib/s3-publisher.rb', File.dirname(__FILE__))
|
|
9
|
+
|
|
10
|
+
RSpec.configure do |config|
|
|
11
|
+
# Limit the spec run to only specs with the focus metadata. If no specs have
|
|
12
|
+
# the filtering metadata and `run_all_when_everything_filtered = true` then
|
|
13
|
+
# all specs will run.
|
|
14
|
+
#config.filter_run :focus
|
|
15
|
+
|
|
16
|
+
# Run all specs when none match the provided filter. This works well in
|
|
17
|
+
# conjunction with `config.filter_run :focus`, as it will run the entire
|
|
18
|
+
# suite when no specs have `:filter` metadata.
|
|
19
|
+
#config.run_all_when_everything_filtered = true
|
|
20
|
+
config.mock_framework = :mocha
|
|
21
|
+
|
|
22
|
+
# Run specs in random order to surface order dependencies. If you find an
|
|
23
|
+
# order dependency and want to debug it, you can fix the order by providing
|
|
24
|
+
# the seed, which is printed after each run.
|
|
25
|
+
# --seed 1234
|
|
26
|
+
#config.order = 'random'
|
|
27
|
+
end
|
metadata
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: s3-publisher
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.9.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ben Koski
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2013-12-
|
|
11
|
+
date: 2013-12-17 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
|
-
name:
|
|
14
|
+
name: rspec
|
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
|
16
16
|
requirements:
|
|
17
17
|
- - '>='
|
|
@@ -25,40 +25,41 @@ dependencies:
|
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
26
|
version: '0'
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
|
-
name:
|
|
28
|
+
name: aws-sdk
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
30
30
|
requirements:
|
|
31
31
|
- - '>='
|
|
32
32
|
- !ruby/object:Gem::Version
|
|
33
|
-
version: '0'
|
|
33
|
+
version: '1.0'
|
|
34
34
|
type: :runtime
|
|
35
35
|
prerelease: false
|
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
37
|
requirements:
|
|
38
38
|
- - '>='
|
|
39
39
|
- !ruby/object:Gem::Version
|
|
40
|
-
version: '0'
|
|
40
|
+
version: '1.0'
|
|
41
41
|
- !ruby/object:Gem::Dependency
|
|
42
|
-
name:
|
|
42
|
+
name: mime-types
|
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
|
44
44
|
requirements:
|
|
45
45
|
- - '>='
|
|
46
46
|
- !ruby/object:Gem::Version
|
|
47
|
-
version:
|
|
47
|
+
version: '0'
|
|
48
48
|
type: :runtime
|
|
49
49
|
prerelease: false
|
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
|
51
51
|
requirements:
|
|
52
52
|
- - '>='
|
|
53
53
|
- !ruby/object:Gem::Version
|
|
54
|
-
version:
|
|
54
|
+
version: '0'
|
|
55
55
|
description: Publish data to S3 for the world to see
|
|
56
|
-
email:
|
|
56
|
+
email: bkoski@nytimes.com
|
|
57
57
|
executables: []
|
|
58
58
|
extensions: []
|
|
59
59
|
extra_rdoc_files: []
|
|
60
60
|
files:
|
|
61
61
|
- .gitignore
|
|
62
|
+
- .rspec
|
|
62
63
|
- Gemfile
|
|
63
64
|
- Gemfile.lock
|
|
64
65
|
- LICENSE
|
|
@@ -66,8 +67,8 @@ files:
|
|
|
66
67
|
- Rakefile
|
|
67
68
|
- lib/s3-publisher.rb
|
|
68
69
|
- s3-publisher.gemspec
|
|
69
|
-
-
|
|
70
|
-
-
|
|
70
|
+
- spec/s3_publisher_spec.rb
|
|
71
|
+
- spec/spec_helper.rb
|
|
71
72
|
homepage: http://github.com/bkoski/s3-publisher
|
|
72
73
|
licenses:
|
|
73
74
|
- MIT
|
|
@@ -88,10 +89,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
88
89
|
version: '0'
|
|
89
90
|
requirements: []
|
|
90
91
|
rubyforge_project:
|
|
91
|
-
rubygems_version: 2.0.
|
|
92
|
+
rubygems_version: 2.0.3
|
|
92
93
|
signing_key:
|
|
93
94
|
specification_version: 4
|
|
94
95
|
summary: Publish data to S3 for the world to see
|
|
95
96
|
test_files:
|
|
96
|
-
-
|
|
97
|
-
-
|
|
97
|
+
- spec/s3_publisher_spec.rb
|
|
98
|
+
- spec/spec_helper.rb
|
data/test/s3-publisher_test.rb
DELETED
|
@@ -1,127 +0,0 @@
|
|
|
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 STANDARD by default" do
|
|
47
|
-
set_put_expectation(:headers => { 'x-amz-storage-class' => 'STANDARD' })
|
|
48
|
-
push_test_data('myfile.txt', '1234', {})
|
|
49
|
-
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
should "set REDUCED_REDUNDANCY if :redundancy => :reduced is passed" do
|
|
53
|
-
set_put_expectation(:headers => { 'x-amz-storage-class' => 'REDUCED_REDUNDANCY' })
|
|
54
|
-
push_test_data('myfile.txt', '1234', :redundancy => :reduced)
|
|
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
DELETED