asset_cloud 2.0.0 → 2.1.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/.gitignore +1 -0
- data/.travis.yml +2 -1
- data/Gemfile +2 -0
- data/History.md +4 -0
- data/README.rdoc +8 -0
- data/asset_cloud.gemspec +6 -1
- data/lib/asset_cloud.rb +2 -2
- data/lib/asset_cloud/buckets/bucket_chain.rb +0 -2
- data/lib/asset_cloud/buckets/file_system_bucket.rb +22 -17
- data/lib/asset_cloud/buckets/memory_bucket.rb +0 -5
- data/lib/asset_cloud/buckets/s3_bucket.rb +62 -0
- data/lib/asset_cloud/buckets/versioned_memory_bucket.rb +0 -4
- data/lib/asset_cloud/callbacks.rb +0 -2
- data/spec/base_spec.rb +14 -0
- data/spec/bucket_spec.rb +25 -0
- data/spec/callbacks_spec.rb +0 -7
- data/spec/file_system_spec.rb +1 -2
- data/spec/memory_bucket_spec.rb +0 -4
- data/spec/mock_s3_interface.rb +167 -0
- data/spec/remote_s3_bucket_spec.rb +69 -0
- data/spec/s3_bucket_spec.rb +64 -0
- data/spec/spec_helper.rb +2 -0
- metadata +54 -25
- checksums.yaml.gz.sig +0 -2
- data.tar.gz.sig +0 -3
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c73b8628c09c25c2e05eae7b097b38a55a40175a
|
4
|
+
data.tar.gz: b78f3c69d1bf5346d88bf6c3a2608e2c92cbf13a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: eaa3b2f1e39aefa77383c50e9d3248d97d8df7f02fd7b229747c346f12b56b8f36a907e570c8b80cc7364f79fedd61527e0b0f092947898073dca045a108dabd
|
7
|
+
data.tar.gz: 24eab9467aaa4243b98c82350a504ae9fc1a3b14e4ea45dc9d7002228c9f585d0b4ae1a0c0ca3efb573547277ff193d41bb92b5862a94e528245859bdcc16bf5
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
data/History.md
CHANGED
@@ -1,5 +1,9 @@
|
|
1
1
|
# Asset Cloud Version History
|
2
2
|
|
3
|
+
## Version 2.1.0, 2015-03-03
|
4
|
+
|
5
|
+
* Add support for S3 assests https://github.com/Shopify/asset_cloud/pull/7
|
6
|
+
|
3
7
|
## Version 2.0.0, 2014-09-26
|
4
8
|
|
5
9
|
* Change the way methods on asset extensions are invoke so it won't silently swallow exceptions [#3](https://github.com/Shopify/asset_cloud/pull/3).
|
data/README.rdoc
CHANGED
@@ -10,6 +10,14 @@ An abstraction layer around arbitrary and diverse asset stores.
|
|
10
10
|
|
11
11
|
gem install asset_cloud
|
12
12
|
|
13
|
+
== Testing
|
14
|
+
|
15
|
+
bundle exec rake spec
|
16
|
+
|
17
|
+
With S3 Remote test:
|
18
|
+
|
19
|
+
AWS_ACCESS_KEY_ID="<s3_key>" AWS_SECRET_ACCESS_KEY="<s3_secret_key>" S3_BUCKET_NAME="<bucket_name>" bundle exec rake spec
|
20
|
+
|
13
21
|
== Copyright
|
14
22
|
|
15
23
|
Copyright (c) 2008-2014 Tobias Lütke & Shopify, Inc. Released under the MIT license (see LICENSE for details).
|
data/asset_cloud.gemspec
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = %q{asset_cloud}
|
5
|
-
s.version = "2.
|
5
|
+
s.version = "2.1.0"
|
6
6
|
|
7
7
|
s.authors = %w(Shopify)
|
8
8
|
s.summary = %q{An abstraction layer around arbitrary and diverse asset stores.}
|
@@ -20,4 +20,9 @@ Gem::Specification.new do |s|
|
|
20
20
|
|
21
21
|
s.add_development_dependency 'rspec'
|
22
22
|
s.add_development_dependency 'rake'
|
23
|
+
s.add_development_dependency 'mocha'
|
24
|
+
if RUBY_VERSION >= "2.0.0"
|
25
|
+
s.add_development_dependency 'pry'
|
26
|
+
s.add_development_dependency 'pry-byebug'
|
27
|
+
end
|
23
28
|
end
|
data/lib/asset_cloud.rb
CHANGED
@@ -13,6 +13,8 @@ require 'asset_cloud/buckets/memory_bucket'
|
|
13
13
|
require 'asset_cloud/buckets/versioned_memory_bucket'
|
14
14
|
require 'asset_cloud/base'
|
15
15
|
|
16
|
+
#S3
|
17
|
+
require 'asset_cloud/buckets/s3_bucket'
|
16
18
|
|
17
19
|
# Extensions
|
18
20
|
require 'asset_cloud/free_key_locator'
|
@@ -21,7 +23,6 @@ require 'asset_cloud/validations'
|
|
21
23
|
|
22
24
|
require 'asset_cloud/asset_extension'
|
23
25
|
|
24
|
-
|
25
26
|
AssetCloud::Base.class_eval do
|
26
27
|
include AssetCloud::FreeKeyLocator
|
27
28
|
include AssetCloud::Callbacks
|
@@ -51,4 +52,3 @@ AssetCloud::Asset.class_eval do
|
|
51
52
|
end
|
52
53
|
end
|
53
54
|
end
|
54
|
-
|
@@ -12,7 +12,6 @@ module AssetCloud
|
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
15
|
-
|
16
15
|
def ls(key=nil)
|
17
16
|
first_possible_bucket {|b| b.ls(key)}
|
18
17
|
end
|
@@ -29,7 +28,6 @@ module AssetCloud
|
|
29
28
|
first_possible_bucket {|b| b.versions(key)}
|
30
29
|
end
|
31
30
|
|
32
|
-
|
33
31
|
def write(key, data)
|
34
32
|
every_bucket_with_transaction_on_key(key) {|b| b.write(key, data)}
|
35
33
|
end
|
@@ -25,23 +25,9 @@ module AssetCloud
|
|
25
25
|
end
|
26
26
|
|
27
27
|
def write(key, data)
|
28
|
-
|
29
|
-
|
30
|
-
retried = false
|
31
|
-
|
32
|
-
begin
|
33
|
-
File.open(full_path, "wb+") { |fp| fp << data }
|
28
|
+
execute_in_full_path(key) do |path|
|
29
|
+
File.open(path, "wb+") { |fp| fp << data }
|
34
30
|
true
|
35
|
-
rescue Errno::ENOENT => e
|
36
|
-
if retried == false
|
37
|
-
directory = File.dirname(full_path)
|
38
|
-
FileUtils.mkdir_p(File.dirname(full_path))
|
39
|
-
retried = true
|
40
|
-
retry
|
41
|
-
else
|
42
|
-
raise
|
43
|
-
end
|
44
|
-
false
|
45
31
|
end
|
46
32
|
end
|
47
33
|
|
@@ -73,7 +59,26 @@ module AssetCloud
|
|
73
59
|
def relative_path_for(f)
|
74
60
|
f.sub(remove_full_path_regexp, '')
|
75
61
|
end
|
76
|
-
end
|
77
62
|
|
63
|
+
def execute_in_full_path(key, &block)
|
64
|
+
path = path_for(key)
|
65
|
+
|
66
|
+
find_or_create_and_execute(path, &block)
|
67
|
+
end
|
68
|
+
|
69
|
+
def find_or_create_and_execute(path)
|
70
|
+
retried = false
|
71
|
+
|
72
|
+
begin
|
73
|
+
yield(path)
|
74
|
+
rescue Errno::ENOENT => e
|
75
|
+
raise if retried
|
78
76
|
|
77
|
+
directory = File.dirname(path)
|
78
|
+
FileUtils.mkdir_p(File.dirname(path))
|
79
|
+
retried = true
|
80
|
+
retry
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
79
84
|
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'aws'
|
2
|
+
|
3
|
+
module AssetCloud
|
4
|
+
class S3Bucket < Bucket
|
5
|
+
def ls(key = nil)
|
6
|
+
key = absolute_key(key)
|
7
|
+
|
8
|
+
objects = cloud.s3_bucket(key).objects
|
9
|
+
objects = objects.with_prefix(key) if key
|
10
|
+
|
11
|
+
objects.map { |o| cloud[relative_key(o.key)] }
|
12
|
+
end
|
13
|
+
|
14
|
+
def read(key)
|
15
|
+
cloud.s3_bucket(key).objects[absolute_key(key)].read
|
16
|
+
rescue ::AWS::Errors::Base
|
17
|
+
raise AssetCloud::AssetNotFoundError, key
|
18
|
+
end
|
19
|
+
|
20
|
+
def write(key, data, options = {})
|
21
|
+
object = cloud.s3_bucket(key).objects[absolute_key(key)]
|
22
|
+
|
23
|
+
object.write(data, options)
|
24
|
+
end
|
25
|
+
|
26
|
+
def delete(key)
|
27
|
+
object = cloud.s3_bucket(key).objects[absolute_key(key)]
|
28
|
+
|
29
|
+
object.delete
|
30
|
+
|
31
|
+
true
|
32
|
+
end
|
33
|
+
|
34
|
+
def stat(key)
|
35
|
+
object = cloud.s3_bucket(key).objects[absolute_key(key)]
|
36
|
+
metadata = object.head
|
37
|
+
|
38
|
+
AssetCloud::Metadata.new(true, metadata[:content_length], nil, metadata[:last_modified])
|
39
|
+
rescue AWS::S3::Errors::NoSuchKey
|
40
|
+
AssetCloud::Metadata.new(false)
|
41
|
+
end
|
42
|
+
|
43
|
+
protected
|
44
|
+
def path_prefix
|
45
|
+
@path_prefix ||= @cloud.url
|
46
|
+
end
|
47
|
+
|
48
|
+
def absolute_key(key = nil)
|
49
|
+
if key.to_s.starts_with?(path_prefix)
|
50
|
+
return key
|
51
|
+
else
|
52
|
+
args = [path_prefix]
|
53
|
+
args << key.to_s if key
|
54
|
+
args.join('/')
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def relative_key(key)
|
59
|
+
key =~ /^#{path_prefix}\/(.+)/ ? $1 : key
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -8,7 +8,6 @@ module AssetCloud
|
|
8
8
|
def callback_methods(*symbols)
|
9
9
|
symbols.each do |method|
|
10
10
|
code = <<-"end_eval"
|
11
|
-
|
12
11
|
def self.before_#{method}(*callbacks, &block)
|
13
12
|
callbacks << block if block_given?
|
14
13
|
write_inheritable_array(:before_#{method}, callbacks)
|
@@ -19,7 +18,6 @@ module AssetCloud
|
|
19
18
|
write_inheritable_array(:after_#{method}, callbacks)
|
20
19
|
end
|
21
20
|
|
22
|
-
|
23
21
|
def #{method}_with_callbacks(*args)
|
24
22
|
if execute_callbacks(:before_#{method}, args)
|
25
23
|
result = #{method}_without_callbacks(*args)
|
data/spec/base_spec.rb
CHANGED
@@ -75,6 +75,20 @@ describe BasicCloud do
|
|
75
75
|
end
|
76
76
|
end
|
77
77
|
|
78
|
+
describe "#move" do
|
79
|
+
it "should return move a resource" do
|
80
|
+
asset = @fs['products/key.txt']
|
81
|
+
asset.key.should == 'products/key.txt'
|
82
|
+
asset.value.should == 'value'
|
83
|
+
@fs.move('products/key.txt', 'products/key2.txt')
|
84
|
+
new_asset = @fs['products/key2.txt']
|
85
|
+
new_asset.key.should == 'products/key2.txt'
|
86
|
+
new_asset.value.should == 'value'
|
87
|
+
expect {@fs['products/key.txt'].value }.to raise_error(AssetCloud::AssetNotFoundError)
|
88
|
+
@fs.move('products/key2.txt', 'products/key.txt')
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
78
92
|
describe "#[]=" do
|
79
93
|
it "should write through the Asset object (and thus run any callbacks on the asset)" do
|
80
94
|
special_asset = double(:special_asset)
|
data/spec/bucket_spec.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe AssetCloud::Bucket do
|
4
|
+
before do
|
5
|
+
@bucket = AssetCloud::Bucket.new(nil, nil)
|
6
|
+
end
|
7
|
+
|
8
|
+
describe "operations not supported" do
|
9
|
+
it "#ls not supported" do
|
10
|
+
expect { @bucket.ls('foo')}.to raise_error NotImplementedError
|
11
|
+
end
|
12
|
+
|
13
|
+
it "#read(key) not supported" do
|
14
|
+
expect { @bucket.read('foo')}.to raise_error NotImplementedError
|
15
|
+
end
|
16
|
+
|
17
|
+
it "#write(key, data) not supported" do
|
18
|
+
expect { @bucket.write('foo', 'bar')}.to raise_error NotImplementedError
|
19
|
+
end
|
20
|
+
|
21
|
+
it "#delete(key) not supported" do
|
22
|
+
expect { @bucket.delete('foo')}.to raise_error NotImplementedError
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/spec/callbacks_spec.rb
CHANGED
@@ -42,7 +42,6 @@ class MethodRecordingCloud < AssetCloud::Base
|
|
42
42
|
before_write :callback_before_write
|
43
43
|
after_write :callback_before_write
|
44
44
|
|
45
|
-
|
46
45
|
def method_missing(method, *args)
|
47
46
|
@run_callbacks << method.to_sym
|
48
47
|
end
|
@@ -52,21 +51,17 @@ describe CallbackCloud do
|
|
52
51
|
before { @fs = CallbackCloud.new(File.dirname(__FILE__) + '/files', 'http://assets/') }
|
53
52
|
|
54
53
|
it "should invoke callbacks after store" do
|
55
|
-
|
56
54
|
@fs.should_receive(:callback_before_write).with('tmp/file.txt', 'text').and_return(true)
|
57
55
|
@fs.should_receive(:callback_after_write).with('tmp/file.txt', 'text').and_return(true)
|
58
56
|
|
59
57
|
|
60
58
|
@fs.write 'tmp/file.txt', 'text'
|
61
|
-
|
62
59
|
end
|
63
60
|
|
64
61
|
it "should invoke callbacks after delete" do
|
65
|
-
|
66
62
|
@fs.should_receive(:callback_before_delete).with('tmp/file.txt').and_return(true)
|
67
63
|
@fs.should_receive(:callback_after_delete).with('tmp/file.txt').and_return(true)
|
68
64
|
|
69
|
-
|
70
65
|
@fs.delete 'tmp/file.txt'
|
71
66
|
end
|
72
67
|
|
@@ -78,9 +73,7 @@ describe CallbackCloud do
|
|
78
73
|
asset = @fs.build('tmp/file.txt')
|
79
74
|
asset.value = 'hello'
|
80
75
|
asset.store
|
81
|
-
|
82
76
|
end
|
83
|
-
|
84
77
|
end
|
85
78
|
|
86
79
|
describe MethodRecordingCloud do
|
data/spec/file_system_spec.rb
CHANGED
@@ -24,7 +24,7 @@ describe FileSystemCloud do
|
|
24
24
|
@fs.bucket_for('does-not-exist/file.txt').should be_an_instance_of(AssetCloud::InvalidBucket)
|
25
25
|
end
|
26
26
|
|
27
|
-
it "should use filesystem
|
27
|
+
it "should use filesystem bucket for products/ and tmp/ directories" do
|
28
28
|
@fs.bucket_for('products/file.txt').should be_an_instance_of(AssetCloud::FileSystemBucket)
|
29
29
|
@fs.bucket_for('tmp/file.txt').should be_an_instance_of(AssetCloud::FileSystemBucket)
|
30
30
|
end
|
@@ -44,7 +44,6 @@ describe FileSystemCloud do
|
|
44
44
|
end
|
45
45
|
|
46
46
|
describe 'when modifying file system' do
|
47
|
-
|
48
47
|
it "should call write after storing an asset" do
|
49
48
|
@fs.buckets[:tmp].should_receive(:write).with('tmp/new_file.test', 'hello world').and_return(true)
|
50
49
|
|
data/spec/memory_bucket_spec.rb
CHANGED
@@ -11,7 +11,6 @@ describe AssetCloud::MemoryBucket do
|
|
11
11
|
@fs = MemoryCloud.new(directory , 'http://assets/files' )
|
12
12
|
end
|
13
13
|
|
14
|
-
|
15
14
|
describe 'modifying items in subfolder' do
|
16
15
|
|
17
16
|
it "should return nil when file does not exist" do
|
@@ -46,7 +45,4 @@ describe AssetCloud::MemoryBucket do
|
|
46
45
|
@fs.buckets[:memory].ls.size.should == 4
|
47
46
|
end
|
48
47
|
end
|
49
|
-
|
50
|
-
|
51
|
-
|
52
48
|
end
|
@@ -0,0 +1,167 @@
|
|
1
|
+
class MockS3Interface
|
2
|
+
attr_reader :bucket_storage
|
3
|
+
|
4
|
+
def initialize(aws_access_key_id=nil, aws_secret_access_key=nil, params={})
|
5
|
+
@bucket_storage = {}
|
6
|
+
end
|
7
|
+
|
8
|
+
def buckets
|
9
|
+
@bucket_collection ||= BucketCollection.new(self)
|
10
|
+
end
|
11
|
+
|
12
|
+
def client
|
13
|
+
@client ||= Client.new
|
14
|
+
end
|
15
|
+
|
16
|
+
class Client
|
17
|
+
end
|
18
|
+
|
19
|
+
class BucketCollection
|
20
|
+
def initialize(interface)
|
21
|
+
@interface = interface
|
22
|
+
end
|
23
|
+
|
24
|
+
def [](name)
|
25
|
+
@interface.bucket_storage[name] ||= Bucket.new(name)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class Bucket
|
30
|
+
attr_reader :name
|
31
|
+
def initialize(name)
|
32
|
+
@name = name
|
33
|
+
@storage = {}
|
34
|
+
@storage_options = {}
|
35
|
+
end
|
36
|
+
|
37
|
+
def with_prefix(prefix)
|
38
|
+
keys = @storage
|
39
|
+
keys = keys.select {|k,v| k.starts_with?(prefix)}
|
40
|
+
keys.map {|k,v| S3Object.new(self, k, v)}
|
41
|
+
end
|
42
|
+
|
43
|
+
def objects
|
44
|
+
Collection.new(self, @storage.keys)
|
45
|
+
end
|
46
|
+
|
47
|
+
def get(key)
|
48
|
+
if @storage.key?(key)
|
49
|
+
@storage[key]
|
50
|
+
else
|
51
|
+
raise AWS::S3::Errors::NoSuchKey.new(nil, nil)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def get_options(key)
|
56
|
+
if @storage_options.key?(key)
|
57
|
+
@storage_options[key]
|
58
|
+
else
|
59
|
+
raise AWS::S3::Errors::NoSuchKey.new(nil, nil)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def put(key, data, options={})
|
64
|
+
@storage[key] = data.dup.force_encoding(Encoding::BINARY)
|
65
|
+
@storage_options[key] = options.dup
|
66
|
+
true
|
67
|
+
end
|
68
|
+
|
69
|
+
def delete(key)
|
70
|
+
@storage.delete(key)
|
71
|
+
true
|
72
|
+
end
|
73
|
+
|
74
|
+
def clear
|
75
|
+
@storage = {}
|
76
|
+
end
|
77
|
+
|
78
|
+
def inspect
|
79
|
+
"#<MockS3Interface::Bucket @name=#{@name.inspect}, @storage.keys = #{@storage.keys.inspect}>"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
class Collection
|
84
|
+
include Enumerable
|
85
|
+
|
86
|
+
def initialize(bucket, objects)
|
87
|
+
@bucket = bucket
|
88
|
+
@objects = objects
|
89
|
+
end
|
90
|
+
|
91
|
+
def with_prefix(prefix)
|
92
|
+
self.class.new(@bucket, @objects.select {|k| k.start_with?(prefix)})
|
93
|
+
end
|
94
|
+
|
95
|
+
def [](name)
|
96
|
+
S3Object.new(@bucket, name)
|
97
|
+
end
|
98
|
+
|
99
|
+
def each
|
100
|
+
@objects.each { |e| yield S3Object.new(@bucket, e) }
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
class S3Object
|
105
|
+
attr_reader :key
|
106
|
+
|
107
|
+
def initialize(bucket, key, data=nil)
|
108
|
+
@bucket = bucket
|
109
|
+
@key = key
|
110
|
+
end
|
111
|
+
|
112
|
+
def delete
|
113
|
+
@bucket.delete(@key)
|
114
|
+
end
|
115
|
+
|
116
|
+
def read(headers={})
|
117
|
+
@bucket.get(@key)
|
118
|
+
end
|
119
|
+
|
120
|
+
def options()
|
121
|
+
@bucket.get_options(@key)
|
122
|
+
end
|
123
|
+
|
124
|
+
def write(data, options={})
|
125
|
+
@bucket.put(@key, data, options)
|
126
|
+
end
|
127
|
+
|
128
|
+
def multipart_upload(options = {})
|
129
|
+
MockMultipartUpload.new(@bucket, @key)
|
130
|
+
end
|
131
|
+
|
132
|
+
def url_for(permission, options={})
|
133
|
+
if options[:secure]
|
134
|
+
URI.parse("https://www.youtube.com/watch?v=oHg5SJYRHA0")
|
135
|
+
else
|
136
|
+
URI.parse("http://www.youtube.com/watch?v=oHg5SJYRHA0")
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def head
|
141
|
+
{
|
142
|
+
content_length: read.size,
|
143
|
+
last_modified: Time.parse("Mon Aug 27 17:37:51 UTC 2007")
|
144
|
+
}
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
class MockMultipartUpload
|
149
|
+
def initialize(bucket, key)
|
150
|
+
@bucket =bucket
|
151
|
+
@key = key
|
152
|
+
@data = ""
|
153
|
+
end
|
154
|
+
|
155
|
+
def add_part(data)
|
156
|
+
@data << data
|
157
|
+
end
|
158
|
+
|
159
|
+
def abort
|
160
|
+
@bucket.delete(@key)
|
161
|
+
end
|
162
|
+
|
163
|
+
def complete(arg)
|
164
|
+
@bucket.put(@key, @data, {})
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class RemoteS3Cloud < AssetCloud::Base
|
4
|
+
attr_accessor :s3_connection
|
5
|
+
bucket :tmp, AssetCloud::S3Bucket
|
6
|
+
|
7
|
+
def s3_bucket(key)
|
8
|
+
s3_connection.buckets[ENV['S3_BUCKET_NAME']]
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
describe 'Remote test for AssetCloud::S3Bucket', if: ENV['AWS_ACCESS_KEY_ID'] && ENV['AWS_SECRET_ACCESS_KEY'] && ENV['S3_BUCKET_NAME'] do
|
13
|
+
require 'aws-sdk'
|
14
|
+
|
15
|
+
directory = File.dirname(__FILE__) + '/files'
|
16
|
+
|
17
|
+
before(:all) do
|
18
|
+
AWS.config({
|
19
|
+
access_key_id: ENV['AWS_ACCESS_KEY_ID'],
|
20
|
+
secret_access_key: ENV['AWS_SECRET_ACCESS_KEY']
|
21
|
+
})
|
22
|
+
@cloud = RemoteS3Cloud.new(directory , 'testing/assets/files' )
|
23
|
+
@cloud.s3_connection = AWS::S3.new()
|
24
|
+
@bucket = @cloud.buckets[:tmp]
|
25
|
+
end
|
26
|
+
|
27
|
+
after(:all) do
|
28
|
+
listing = @bucket.ls('tmp')
|
29
|
+
listing.each { |object| object.delete }
|
30
|
+
end
|
31
|
+
|
32
|
+
it "#ls should return assets with proper keys" do
|
33
|
+
@cloud['tmp/test1.txt'] = 'test1'
|
34
|
+
@cloud['tmp/test2.txt'] = 'test2'
|
35
|
+
|
36
|
+
ls = @bucket.ls('tmp')
|
37
|
+
|
38
|
+
ls.first.class.should == AssetCloud::Asset
|
39
|
+
keys = ls.map(&:key)
|
40
|
+
['tmp/test1.txt', 'tmp/test2.txt'].all? {|key| keys.include? key }
|
41
|
+
end
|
42
|
+
|
43
|
+
it "#delete should ignore errors when deleting" do
|
44
|
+
@bucket.delete('tmp/a_file_that_should_not_exist.txt')
|
45
|
+
end
|
46
|
+
|
47
|
+
it "#delete should always return true" do
|
48
|
+
@cloud['tmp/test1.txt'] = 'test1'
|
49
|
+
|
50
|
+
@bucket.delete('tmp/test1.txt').should == true
|
51
|
+
end
|
52
|
+
|
53
|
+
it "#stat should get metadata from S3" do
|
54
|
+
start_time = Time.now
|
55
|
+
value = 'hello world'
|
56
|
+
@cloud.build('tmp/new_file.test', value).store
|
57
|
+
metadata = @bucket.stat('tmp/new_file.test')
|
58
|
+
metadata.size.should == value.size
|
59
|
+
metadata.updated_at.should >= start_time
|
60
|
+
end
|
61
|
+
|
62
|
+
it "#read " do
|
63
|
+
value = 'hello world'
|
64
|
+
key = 'tmp/new_file.txt'
|
65
|
+
@bucket.write(key, value)
|
66
|
+
data = @bucket.read(key)
|
67
|
+
data.should == value
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'mock_s3_interface'
|
3
|
+
|
4
|
+
class S3Cloud < AssetCloud::Base
|
5
|
+
bucket :tmp, AssetCloud::S3Bucket
|
6
|
+
attr_accessor :s3_connection, :s3_bucket_name
|
7
|
+
|
8
|
+
def s3_bucket(key)
|
9
|
+
s3_connection.buckets[s3_bucket_name]
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
describe AssetCloud::S3Bucket do
|
14
|
+
directory = File.dirname(__FILE__) + '/files'
|
15
|
+
|
16
|
+
before(:all) do
|
17
|
+
@cloud = S3Cloud.new(directory , 'http://assets/files')
|
18
|
+
@cloud.s3_connection = MockS3Interface.new('a', 'b')
|
19
|
+
@cloud.s3_bucket_name = 'asset-cloud-test'
|
20
|
+
|
21
|
+
@bucket = @cloud.buckets[:tmp]
|
22
|
+
FileUtils.mkdir_p(directory + '/tmp')
|
23
|
+
end
|
24
|
+
|
25
|
+
after(:each) do
|
26
|
+
FileUtils.rm_rf(directory + '/tmp')
|
27
|
+
end
|
28
|
+
|
29
|
+
it "#ls should return assets with proper keys" do
|
30
|
+
collection = MockS3Interface::Collection.new(nil, ["#{@cloud.url}/tmp/blah.gif", "#{@cloud.url}/tmp/add_to_cart.gif"])
|
31
|
+
expect_any_instance_of(MockS3Interface::Bucket).to receive(:objects).and_return(collection)
|
32
|
+
ls = @bucket.ls('tmp')
|
33
|
+
ls.first.class.should == AssetCloud::Asset
|
34
|
+
ls.map(&:key).should == ['tmp/blah.gif', 'tmp/add_to_cart.gif']
|
35
|
+
end
|
36
|
+
|
37
|
+
it "#delete should not ignore errors when deleting" do
|
38
|
+
expect_any_instance_of(MockS3Interface::Bucket).to receive(:delete).and_raise(StandardError)
|
39
|
+
|
40
|
+
expect { @bucket.delete('assets/fail.gif') }.to raise_error(StandardError)
|
41
|
+
end
|
42
|
+
|
43
|
+
it "#delete should always return true" do
|
44
|
+
expect_any_instance_of(MockS3Interface::Bucket).to receive(:delete).and_return(nil)
|
45
|
+
|
46
|
+
@bucket.delete('assets/fail.gif').should == true
|
47
|
+
end
|
48
|
+
|
49
|
+
it "#stat should get metadata from S3" do
|
50
|
+
value = 'hello world'
|
51
|
+
@cloud.build('tmp/new_file.test', value).store
|
52
|
+
metadata = @bucket.stat('tmp/new_file.test')
|
53
|
+
metadata.size.should == value.size
|
54
|
+
metadata.updated_at.should == Time.parse("Mon Aug 27 17:37:51 UTC 2007")
|
55
|
+
end
|
56
|
+
|
57
|
+
it "#read " do
|
58
|
+
value = 'hello world'
|
59
|
+
key = 'tmp/new_file.txt'
|
60
|
+
@bucket.write(key, value)
|
61
|
+
data = @bucket.read(key)
|
62
|
+
data.should == value
|
63
|
+
end
|
64
|
+
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,36 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: asset_cloud
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shopify
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
|
-
cert_chain:
|
11
|
-
-
|
12
|
-
-----BEGIN CERTIFICATE-----
|
13
|
-
MIIDcDCCAligAwIBAgIBATANBgkqhkiG9w0BAQUFADA/MQ8wDQYDVQQDDAZhZG1p
|
14
|
-
bnMxFzAVBgoJkiaJk/IsZAEZFgdzaG9waWZ5MRMwEQYKCZImiZPyLGQBGRYDY29t
|
15
|
-
MB4XDTE0MDUxNTIwMzM0OFoXDTE1MDUxNTIwMzM0OFowPzEPMA0GA1UEAwwGYWRt
|
16
|
-
aW5zMRcwFQYKCZImiZPyLGQBGRYHc2hvcGlmeTETMBEGCgmSJomT8ixkARkWA2Nv
|
17
|
-
bTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL0/81O3e1vh5smcwp2G
|
18
|
-
MpLQ6q0kejQLa65bPYPxdzWA1SYOKyGfw+yR9LdFzsuKpwWzKq6zX35lj1IckWS4
|
19
|
-
bNBEQzxmufUxU0XPM02haFB8fOfDJzdXsWte9Ge4IFwahwn68gpMqN+BvxL+KMYz
|
20
|
-
Iut9YmN44d4LZdsENEIO5vmybuG2vYDz7R56qB0PA+Q2P2CdhymsBad2DQs69FBo
|
21
|
-
uico9V6VMYYctL9lCYdzu9IXrOYNTt88suKIVzzAlHOKeN0Ng5qdztFoTR8sfxDr
|
22
|
-
Ydg3KHl5n47wlpgd8R0f/4b5gGxW+v9pyJCgQnLlRu7DedVSvv7+GMtj3g9r3nhJ
|
23
|
-
KqECAwEAAaN3MHUwCQYDVR0TBAIwADALBgNVHQ8EBAMCBLAwHQYDVR0OBBYEFI/o
|
24
|
-
maf34HXbUOQsdoLHacEKQgunMB0GA1UdEQQWMBSBEmFkbWluc0BzaG9waWZ5LmNv
|
25
|
-
bTAdBgNVHRIEFjAUgRJhZG1pbnNAc2hvcGlmeS5jb20wDQYJKoZIhvcNAQEFBQAD
|
26
|
-
ggEBADkK9aj5T0HPExsov4EoMWFnO+G7RQ28C30VAfKxnL2UxG6i4XMHVs6Xi94h
|
27
|
-
qXFw1ec9Y2eDUqaolT3bviOk9BB197+A8Vz/k7MC6ci2NE+yDDB7HAC8zU6LAx8Y
|
28
|
-
Iqvw7B/PSZ/pz4bUVFlTATif4mi1vO3lidRkdHRtM7UePSn2rUpOi0gtXBP3bLu5
|
29
|
-
YjHJN7wx5cugMEyroKITG5gL0Nxtu21qtOlHX4Hc4KdE2JqzCPOsS4zsZGhgwhPs
|
30
|
-
fl3hbtVFTqbOlwL9vy1fudXcolIE/ZTcxQ+er07ZFZdKCXayR9PPs64heamfn0fp
|
31
|
-
TConQSX2BnZdhIEYW+cKzEC/bLc=
|
32
|
-
-----END CERTIFICATE-----
|
33
|
-
date: 2014-09-26 00:00:00.000000000 Z
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-03-03 00:00:00.000000000 Z
|
34
12
|
dependencies:
|
35
13
|
- !ruby/object:Gem::Dependency
|
36
14
|
name: activesupport
|
@@ -88,6 +66,48 @@ dependencies:
|
|
88
66
|
- - ">="
|
89
67
|
- !ruby/object:Gem::Version
|
90
68
|
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: mocha
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: pry
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: pry-byebug
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
91
111
|
description: An abstraction layer around arbitrary and diverse asset stores.
|
92
112
|
email: developers@shopify.com
|
93
113
|
executables: []
|
@@ -113,6 +133,7 @@ files:
|
|
113
133
|
- lib/asset_cloud/buckets/file_system_bucket.rb
|
114
134
|
- lib/asset_cloud/buckets/invalid_bucket.rb
|
115
135
|
- lib/asset_cloud/buckets/memory_bucket.rb
|
136
|
+
- lib/asset_cloud/buckets/s3_bucket.rb
|
116
137
|
- lib/asset_cloud/buckets/versioned_memory_bucket.rb
|
117
138
|
- lib/asset_cloud/callbacks.rb
|
118
139
|
- lib/asset_cloud/free_key_locator.rb
|
@@ -125,12 +146,16 @@ files:
|
|
125
146
|
- spec/base_spec.rb
|
126
147
|
- spec/blackhole_bucket_spec.rb
|
127
148
|
- spec/bucket_chain_spec.rb
|
149
|
+
- spec/bucket_spec.rb
|
128
150
|
- spec/callbacks_spec.rb
|
129
151
|
- spec/file_system_spec.rb
|
130
152
|
- spec/files/products/key.txt
|
131
153
|
- spec/files/versioned_stuff/foo
|
132
154
|
- spec/find_free_key_spec.rb
|
133
155
|
- spec/memory_bucket_spec.rb
|
156
|
+
- spec/mock_s3_interface.rb
|
157
|
+
- spec/remote_s3_bucket_spec.rb
|
158
|
+
- spec/s3_bucket_spec.rb
|
134
159
|
- spec/spec_helper.rb
|
135
160
|
- spec/validations_spec.rb
|
136
161
|
- spec/versioned_memory_bucket_spec.rb
|
@@ -164,12 +189,16 @@ test_files:
|
|
164
189
|
- spec/base_spec.rb
|
165
190
|
- spec/blackhole_bucket_spec.rb
|
166
191
|
- spec/bucket_chain_spec.rb
|
192
|
+
- spec/bucket_spec.rb
|
167
193
|
- spec/callbacks_spec.rb
|
168
194
|
- spec/file_system_spec.rb
|
169
195
|
- spec/files/products/key.txt
|
170
196
|
- spec/files/versioned_stuff/foo
|
171
197
|
- spec/find_free_key_spec.rb
|
172
198
|
- spec/memory_bucket_spec.rb
|
199
|
+
- spec/mock_s3_interface.rb
|
200
|
+
- spec/remote_s3_bucket_spec.rb
|
201
|
+
- spec/s3_bucket_spec.rb
|
173
202
|
- spec/spec_helper.rb
|
174
203
|
- spec/validations_spec.rb
|
175
204
|
- spec/versioned_memory_bucket_spec.rb
|
checksums.yaml.gz.sig
DELETED
data.tar.gz.sig
DELETED
metadata.gz.sig
DELETED
Binary file
|