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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: fa28322db86635a35c0824e9629d1d65b1cb7e56
4
- data.tar.gz: 5d493e09e7037f6bbc830c8f2cc7730c6ea05213
3
+ metadata.gz: c73b8628c09c25c2e05eae7b097b38a55a40175a
4
+ data.tar.gz: b78f3c69d1bf5346d88bf6c3a2608e2c92cbf13a
5
5
  SHA512:
6
- metadata.gz: 0423e9ef826f7e230aec6ada6ddf050460ffbb0bf2027feeaabbcdd645897274aa890478ad0d522910fbd704531d98f2f54352114f2f17b21dd05df7c44bd41b
7
- data.tar.gz: fe6bc175f198b741011d0ab6d6280813e611f8e19da376ed0e6245116a80acb324049954882f6a0fb16631a528be011621714fe072b1575423c15a95a0b6c753
6
+ metadata.gz: eaa3b2f1e39aefa77383c50e9d3248d97d8df7f02fd7b229747c346f12b56b8f36a907e570c8b80cc7364f79fedd61527e0b0f092947898073dca045a108dabd
7
+ data.tar.gz: 24eab9467aaa4243b98c82350a504ae9fc1a3b14e4ea45dc9d7002228c9f585d0b4ae1a0c0ca3efb573547277ff193d41bb92b5862a94e528245859bdcc16bf5
data/.gitignore CHANGED
@@ -4,3 +4,4 @@ Gemfile.lock
4
4
  .dotest
5
5
  .rvmrc
6
6
  *.gem
7
+ tags
data/.travis.yml CHANGED
@@ -1,5 +1,6 @@
1
1
  language: ruby
2
2
  rvm:
3
- - '1.9'
3
+ - '1.9.3'
4
4
  - '2.0'
5
5
  - '2.1'
6
+ - 2.1.2
data/Gemfile CHANGED
@@ -1,3 +1,5 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
+ gem 'aws-sdk', '~> 1.34.0', :require => nil
4
+
3
5
  gemspec
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.0.0"
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
- full_path = path_for(key)
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
@@ -1,7 +1,5 @@
1
1
  module AssetCloud
2
-
3
2
  class MemoryBucket < Bucket
4
-
5
3
  def initialize(*args)
6
4
  super
7
5
  @memory = {}
@@ -35,8 +33,5 @@ module AssetCloud
35
33
 
36
34
  Metadata.new(true, read(key).size)
37
35
  end
38
-
39
36
  end
40
-
41
-
42
37
  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
@@ -1,5 +1,4 @@
1
1
  module AssetCloud
2
-
3
2
  class VersionedMemoryBucket < MemoryBucket
4
3
 
5
4
  def read(key)
@@ -26,8 +25,5 @@ module AssetCloud
26
25
  def latest_version(key)
27
26
  @memory[key].size
28
27
  end
29
-
30
28
  end
31
-
32
-
33
29
  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)
@@ -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
@@ -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
@@ -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 bucekt for products/ and tmp/ directories" do
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
 
@@ -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
@@ -1,5 +1,7 @@
1
1
  require 'rubygems'
2
2
  require 'rspec'
3
+ require 'pry-byebug' if RUBY_VERSION >= '2.0.0'
3
4
  require 'active_support/all'
4
5
  $:<< File.dirname(__FILE__) + "/../lib"
5
6
  require 'asset_cloud'
7
+ require 'asset_cloud/buckets/s3_bucket'
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.0.0
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
@@ -1,2 +0,0 @@
1
- Y�^G͙�{ �&s�a:V��Q`)�ɝO�M��^/6��(��YS�j�|�$���J
2
- Ȍ�!�+��v`�Ĥ*�"v��+���r�Ƒzz��kK��o�R�ή_��� &Y�Fޯ�'�-���E(C�Z|�
data.tar.gz.sig DELETED
@@ -1,3 +0,0 @@
1
- -�A���D����{TG�n�k����0�܈�rk-3���r\8š��� ��u&�F'䐣����٥�I�z/��׬U�|o�$��%���:���q~q�%1i�i�
2
- 5�Kd @��5��n��G�����qH�,�_o!�l|���e���ĘR�d�p����S�|�<P[���T���V��,� �6�h�GM��8)�$#ȗG�Y ��o�C0� �AŅ�
3
- q�d�!#�/��K+���$(�7g�F��
metadata.gz.sig DELETED
Binary file