asset_cloud 2.0.0 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
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