jamesmacaulay-asset_cloud 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +3 -0
- data/.gitignore +3 -0
- data/CHANGELOG +7 -0
- data/LICENSE +20 -0
- data/README.rdoc +17 -0
- data/Rakefile +37 -0
- data/VERSION +1 -0
- data/asset_cloud.gemspec +79 -0
- data/init.rb +1 -0
- data/install.rb +1 -0
- data/lib/asset_cloud/asset.rb +119 -0
- data/lib/asset_cloud/base.rb +178 -0
- data/lib/asset_cloud/blackhole_bucket.rb +24 -0
- data/lib/asset_cloud/bucket.rb +57 -0
- data/lib/asset_cloud/callbacks.rb +62 -0
- data/lib/asset_cloud/file_system_bucket.rb +79 -0
- data/lib/asset_cloud/free_key_locator.rb +39 -0
- data/lib/asset_cloud/invalid_bucket.rb +28 -0
- data/lib/asset_cloud/memory_bucket.rb +40 -0
- data/lib/asset_cloud/metadata.rb +30 -0
- data/lib/asset_cloud.rb +23 -0
- data/spec/asset_spec.rb +135 -0
- data/spec/base_spec.rb +77 -0
- data/spec/blackhole_bucket_spec.rb +41 -0
- data/spec/bucket_spec.rb +41 -0
- data/spec/callbacks_spec.rb +78 -0
- data/spec/file_system_spec.rb +73 -0
- data/spec/files/products/key.txt +1 -0
- data/spec/find_free_key_spec.rb +66 -0
- data/spec/memory_bucket_spec.rb +30 -0
- data/spec/regexp_spec.rb +20 -0
- data/spec/spec.opts +6 -0
- data/spec/spec_helper.rb +4 -0
- metadata +95 -0
@@ -0,0 +1,79 @@
|
|
1
|
+
module AssetCloud
|
2
|
+
|
3
|
+
class FileSystemBucket < Bucket
|
4
|
+
|
5
|
+
def ls(key = nil)
|
6
|
+
objects = []
|
7
|
+
base_path = File.join(path_for(key), '*')
|
8
|
+
|
9
|
+
Dir.glob(base_path).each do |f|
|
10
|
+
next unless File.file?(f)
|
11
|
+
objects.push Asset.at(cloud, relative_path_for(f) )
|
12
|
+
end
|
13
|
+
objects
|
14
|
+
end
|
15
|
+
|
16
|
+
def read(key)
|
17
|
+
File.read(path_for(key))
|
18
|
+
rescue Errno::ENOENT => e
|
19
|
+
raise AssetCloud::AssetNotFoundError, key
|
20
|
+
end
|
21
|
+
|
22
|
+
def delete(key)
|
23
|
+
File.delete(path_for(key))
|
24
|
+
rescue Errno::ENOENT
|
25
|
+
end
|
26
|
+
|
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 }
|
34
|
+
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
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def stat(key)
|
49
|
+
begin
|
50
|
+
stat = File.stat(path_for(key))
|
51
|
+
Metadata.new(true, stat.size, stat.ctime, stat.mtime)
|
52
|
+
rescue Errno::ENOENT => e
|
53
|
+
Metadata.new(false)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
protected
|
58
|
+
|
59
|
+
def path_for(key)
|
60
|
+
cloud.path_for(key)
|
61
|
+
end
|
62
|
+
|
63
|
+
def path
|
64
|
+
cloud.path
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
def remove_full_path_regexp
|
70
|
+
@regexp ||= /^#{path}\//
|
71
|
+
end
|
72
|
+
|
73
|
+
def relative_path_for(f)
|
74
|
+
f.sub(remove_full_path_regexp, '')
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
|
79
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module AssetCloud
|
2
|
+
|
3
|
+
module FreeKeyLocator
|
4
|
+
|
5
|
+
def find_free_key_like(key, options = {})
|
6
|
+
# Check weather the suggested key name is free. If so we
|
7
|
+
# simply return it.
|
8
|
+
|
9
|
+
if not exist?(key)
|
10
|
+
key
|
11
|
+
else
|
12
|
+
|
13
|
+
ext = File.extname(key)
|
14
|
+
dirname = File.dirname(key)
|
15
|
+
base = dirname == '.' ? File.basename(key, ext) : File.join(File.dirname(key), File.basename(key, ext))
|
16
|
+
count = base.scan(/\d+$/).flatten.first.to_i
|
17
|
+
base = base.gsub(/([\-\_]?)\d+$/,'')
|
18
|
+
separator = $1 || '_'
|
19
|
+
|
20
|
+
|
21
|
+
# increase the count until you find a unused key
|
22
|
+
10.times do
|
23
|
+
count += 1
|
24
|
+
key = "#{base}#{separator}#{count}#{ext}"
|
25
|
+
return key unless exist?(key)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Ok we have to go random here...
|
29
|
+
100.times do
|
30
|
+
count += rand(9999999)
|
31
|
+
key = "#{base}#{separator}#{count}#{ext}"
|
32
|
+
return key unless exist?(key)
|
33
|
+
end
|
34
|
+
|
35
|
+
raise StandardError, 'Filesystem out of free filenames'
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module AssetCloud
|
2
|
+
class InvalidBucketError < StandardError
|
3
|
+
end
|
4
|
+
|
5
|
+
class InvalidBucket < Bucket
|
6
|
+
Error = "No such namespace: %s".freeze
|
7
|
+
|
8
|
+
def ls(namespace)
|
9
|
+
raise InvalidBucketError, Error % key
|
10
|
+
end
|
11
|
+
|
12
|
+
def read(key)
|
13
|
+
raise InvalidBucketError, Error % key
|
14
|
+
end
|
15
|
+
|
16
|
+
def write(key, data)
|
17
|
+
raise InvalidBucketError, Error % key
|
18
|
+
end
|
19
|
+
|
20
|
+
def delete(key)
|
21
|
+
raise InvalidBucketError, Error % key
|
22
|
+
end
|
23
|
+
|
24
|
+
def stat(key)
|
25
|
+
raise InvalidBucketError, Error % key
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module AssetCloud
|
2
|
+
|
3
|
+
class MemoryBucket < Bucket
|
4
|
+
|
5
|
+
def initialize(*args)
|
6
|
+
super
|
7
|
+
@memory = {}
|
8
|
+
end
|
9
|
+
|
10
|
+
def ls(key = nil)
|
11
|
+
@memory.find_all do |key, value|
|
12
|
+
key.left(key.size) == namespace
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def read(key)
|
17
|
+
raise AssetCloud::AssetNotFoundError, key unless @memory.has_key?(key)
|
18
|
+
@memory[key]
|
19
|
+
end
|
20
|
+
|
21
|
+
def delete(key)
|
22
|
+
@memory.delete(key)
|
23
|
+
end
|
24
|
+
|
25
|
+
def write(key, data)
|
26
|
+
@memory[key] = data
|
27
|
+
|
28
|
+
true
|
29
|
+
end
|
30
|
+
|
31
|
+
def stat(key)
|
32
|
+
return Metadata.non_existing unless @memory.has_key?(key)
|
33
|
+
|
34
|
+
Metadata.new(true, @memory[key].size)
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module AssetCloud
|
2
|
+
|
3
|
+
class Metadata
|
4
|
+
attr_accessor :exist, :size, :created_at, :updated_at
|
5
|
+
|
6
|
+
def new?
|
7
|
+
!self.exist
|
8
|
+
end
|
9
|
+
|
10
|
+
def exist?
|
11
|
+
self.exist
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(exist, size = nil, created_at = nil, updated_at = nil)
|
15
|
+
self.exist, self.size, self.created_at, self.updated_at = exist, size, created_at, updated_at
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.existing
|
19
|
+
self.new(true)
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.non_existing
|
23
|
+
self.new false
|
24
|
+
end
|
25
|
+
|
26
|
+
def inspect
|
27
|
+
"#<#{self.class.name}: exist:#{exist} size:#{size.inspect} bytes>"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/lib/asset_cloud.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'active_support'
|
2
|
+
|
3
|
+
# Core
|
4
|
+
require File.dirname(__FILE__) + '/asset_cloud/asset'
|
5
|
+
require File.dirname(__FILE__) + '/asset_cloud/metadata'
|
6
|
+
require File.dirname(__FILE__) + '/asset_cloud/bucket'
|
7
|
+
require File.dirname(__FILE__) + '/asset_cloud/invalid_bucket'
|
8
|
+
require File.dirname(__FILE__) + '/asset_cloud/blackhole_bucket'
|
9
|
+
require File.dirname(__FILE__) + '/asset_cloud/memory_bucket'
|
10
|
+
require File.dirname(__FILE__) + '/asset_cloud/file_system_bucket'
|
11
|
+
require File.dirname(__FILE__) + '/asset_cloud/base'
|
12
|
+
|
13
|
+
|
14
|
+
# Extensions
|
15
|
+
require File.dirname(__FILE__) + '/asset_cloud/free_key_locator'
|
16
|
+
require File.dirname(__FILE__) + '/asset_cloud/callbacks'
|
17
|
+
|
18
|
+
|
19
|
+
AssetCloud::Base.class_eval do
|
20
|
+
include AssetCloud::FreeKeyLocator
|
21
|
+
include AssetCloud::Callbacks
|
22
|
+
end
|
23
|
+
|
data/spec/asset_spec.rb
ADDED
@@ -0,0 +1,135 @@
|
|
1
|
+
|
2
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
3
|
+
|
4
|
+
describe "Asset" do
|
5
|
+
include AssetCloud
|
6
|
+
|
7
|
+
before do
|
8
|
+
@cloud = mock('Bucket')
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "when first created (without a value)" do
|
12
|
+
before do
|
13
|
+
@asset = AssetCloud::Asset.new(@cloud, "products/key.txt")
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should be return new_asset? => true" do
|
17
|
+
@asset.new_asset?.should == true
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should have a key" do
|
21
|
+
@asset.key.should == 'products/key.txt'
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should have a value of nil" do
|
25
|
+
|
26
|
+
@asset.value.should == nil
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should have a basename" do
|
30
|
+
@asset.basename.should == 'key.txt'
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should have a basename without ext (if required)" do
|
34
|
+
@asset.basename_without_ext.should == 'key'
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should have an ext" do
|
38
|
+
@asset.extname.should == '.txt'
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
it "should store data to the bucket" do
|
43
|
+
@cloud.should_receive(:write).with("products/key.txt", 'value')
|
44
|
+
|
45
|
+
@asset.value = 'value'
|
46
|
+
@asset.store
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should not try to store data when it's value is nil" do
|
50
|
+
@cloud.should_receive(:write).never
|
51
|
+
|
52
|
+
@asset.store
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should not try to read data from bucket if its a new_asset" do
|
56
|
+
@cloud.should_receive(:read).never
|
57
|
+
|
58
|
+
@asset.value.should == nil
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should simply ignore calls to delete" do
|
62
|
+
@cloud.should_receive(:delete).never
|
63
|
+
|
64
|
+
@asset.delete
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
describe "when first created with value" do
|
70
|
+
before do
|
71
|
+
@asset = AssetCloud::Asset.new(@cloud, "products/key.txt", 'value')
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should be return new_asset? => true" do
|
75
|
+
@asset.new_asset?.should == true
|
76
|
+
end
|
77
|
+
|
78
|
+
|
79
|
+
it "should have a value of 'value'" do
|
80
|
+
@asset.value.should == 'value'
|
81
|
+
end
|
82
|
+
|
83
|
+
it "should return false when asked if it exists because its still a new_asset" do
|
84
|
+
@asset.exist?.should == false
|
85
|
+
end
|
86
|
+
|
87
|
+
|
88
|
+
it "should not try to read data from bucket if its a new_asset" do
|
89
|
+
@cloud.should_receive(:read).never
|
90
|
+
|
91
|
+
@asset.value.should == 'value'
|
92
|
+
end
|
93
|
+
|
94
|
+
it "should write data to the bucket" do
|
95
|
+
@cloud.should_receive(:write).with("products/key.txt", 'value')
|
96
|
+
@asset.store
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
|
101
|
+
describe "when fetched from the bucket" do
|
102
|
+
before do
|
103
|
+
@asset = AssetCloud::Asset.at(@cloud, "products/key.txt", 'value', AssetCloud::Metadata.new(true, 'value'.size, Time.now, Time.now))
|
104
|
+
end
|
105
|
+
|
106
|
+
it "should be return new_asset? => false" do
|
107
|
+
@asset.new_asset?.should == false
|
108
|
+
end
|
109
|
+
|
110
|
+
it "should indicate that it exists" do
|
111
|
+
|
112
|
+
@asset.exist?.should == true
|
113
|
+
end
|
114
|
+
|
115
|
+
|
116
|
+
it "should read the value from the bucket" do
|
117
|
+
@asset.value.should == 'value'
|
118
|
+
end
|
119
|
+
|
120
|
+
|
121
|
+
it "should simply ignore calls to delete" do
|
122
|
+
@cloud.should_receive(:delete).and_return(true)
|
123
|
+
|
124
|
+
@asset.delete
|
125
|
+
end
|
126
|
+
|
127
|
+
it "should ask the bucket to create a full url" do
|
128
|
+
@cloud.should_receive(:url_for).with('products/key.txt').and_return('http://assets/products/key.txt')
|
129
|
+
|
130
|
+
@asset.url.should == 'http://assets/products/key.txt'
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
|
135
|
+
end
|
data/spec/base_spec.rb
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
class SpecialAsset < AssetCloud::Asset
|
4
|
+
end
|
5
|
+
|
6
|
+
class BasicCloud < AssetCloud::Base
|
7
|
+
bucket :special, AssetCloud::MemoryBucket, :asset_class => SpecialAsset
|
8
|
+
end
|
9
|
+
|
10
|
+
|
11
|
+
describe BasicCloud do
|
12
|
+
directory = File.dirname(__FILE__) + '/files'
|
13
|
+
|
14
|
+
before do
|
15
|
+
@fs = BasicCloud.new(directory , 'http://assets/files' )
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should raise invalid bucket if none is given" do
|
19
|
+
@fs['image.jpg'].exist?.should == false
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
it "should be backed by a file system bucket" do
|
24
|
+
@fs['products/key.txt'].exist?.should == true
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should raise when listing non existing buckets" do
|
28
|
+
@fs.ls('products').should == [AssetCloud::Asset.new(@fs, 'products/key.txt')]
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
it "should allow you to create new assets" do
|
33
|
+
obj = @fs.build('new_file.test')
|
34
|
+
obj.should be_an_instance_of(AssetCloud::Asset)
|
35
|
+
obj.cloud.should be_an_instance_of(BasicCloud)
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should raise error when using with minus relative or absolute paths" do
|
39
|
+
lambda { @fs['../test'] }.should raise_error(AssetCloud::IllegalPath)
|
40
|
+
lambda { @fs['/test'] }.should raise_error(AssetCloud::IllegalPath)
|
41
|
+
lambda { @fs['.../test'] }.should raise_error(AssetCloud::IllegalPath)
|
42
|
+
lambda { @fs['./test'] }.should raise_error(AssetCloud::IllegalPath)
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should allow sensible relative filenames" do
|
46
|
+
@fs['assets/rails_logo.gif']
|
47
|
+
@fs['assets/rails-2.gif']
|
48
|
+
@fs['assets/223434.gif']
|
49
|
+
@fs['files/1.JPG']
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should compute complete urls to assets" do
|
53
|
+
@fs.url_for('products/key.txt').should == 'http://assets/files/products/key.txt'
|
54
|
+
end
|
55
|
+
|
56
|
+
describe "#find" do
|
57
|
+
it "should return the appropriate asset when one exists" do
|
58
|
+
asset = @fs.find('products/key.txt')
|
59
|
+
asset.key.should == 'products/key.txt'
|
60
|
+
asset.value.should == 'value'
|
61
|
+
end
|
62
|
+
it "should raise AssetNotFoundError when the asset doesn't exist" do
|
63
|
+
lambda { @fs.find('products/not-there.txt') }.should raise_error(AssetCloud::AssetNotFoundError)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe "#bucket" do
|
68
|
+
it "should allow specifying a class to use for assets in this bucket" do
|
69
|
+
@fs['assets/rails_logo.gif'].should be_instance_of(AssetCloud::Asset)
|
70
|
+
@fs['special/fancy.txt'].should be_instance_of(SpecialAsset)
|
71
|
+
|
72
|
+
@fs.build('assets/foo').should be_instance_of(AssetCloud::Asset)
|
73
|
+
@fs.build('special/foo').should be_instance_of(SpecialAsset)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
class BlackholeCloud < AssetCloud::Base
|
4
|
+
bucket AssetCloud::BlackholeBucket
|
5
|
+
end
|
6
|
+
|
7
|
+
describe BlackholeCloud do
|
8
|
+
directory = File.dirname(__FILE__) + '/files'
|
9
|
+
|
10
|
+
before do
|
11
|
+
@fs = BlackholeCloud.new(directory , 'http://assets/files' )
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should allow access to files using the [] operator" do
|
15
|
+
@fs['tmp/image.jpg']
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should return nil for non existent files" do
|
19
|
+
@fs['tmp/image.jpg'].exist?.should == false
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should still return nil, even if you wrote something there" do
|
23
|
+
@fs['tmp/image.jpg'] = 'test'
|
24
|
+
@fs['tmp/image.jpg'].exist?.should == false
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "when using a sub path" do
|
28
|
+
it "should allow access to files using the [] operator" do
|
29
|
+
@fs['tmp/image.jpg']
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should return nil for non existent files" do
|
33
|
+
@fs['tmp/image.jpg'].exist?.should == false
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should still return nil, even if you wrote something there" do
|
37
|
+
@fs['tmp/image.jpg'] = 'test'
|
38
|
+
@fs['tmp/image.jpg'].exist?.should == false
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
data/spec/bucket_spec.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
class ChainedCloud < AssetCloud::Base
|
4
|
+
bucket :stuff, AssetCloud::Bucket.chain(AssetCloud::MemoryBucket, AssetCloud::BlackholeBucket)
|
5
|
+
end
|
6
|
+
|
7
|
+
describe AssetCloud::Bucket do
|
8
|
+
directory = File.dirname(__FILE__) + '/files'
|
9
|
+
|
10
|
+
before(:each) do
|
11
|
+
@cloud = ChainedCloud.new(directory , 'http://assets/files' )
|
12
|
+
@bucket_chain = @cloud.buckets[:stuff]
|
13
|
+
@memory_bucket, @blackhole_bucket = @bucket_chain.chained_buckets
|
14
|
+
end
|
15
|
+
|
16
|
+
describe "#chain" do
|
17
|
+
it 'should take multiple Bucket classes and return a new Bucket class' do
|
18
|
+
@bucket_chain.should be_a_kind_of(AssetCloud::Bucket)
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'should return a Bucket which writes to each sub-bucket' do
|
22
|
+
@bucket_chain.chained_buckets.each do |bucket|
|
23
|
+
bucket.should_receive(:write).with('stuff/foo', 'bar').and_return(true)
|
24
|
+
bucket.should_receive(:delete).with('stuff/foo').and_return(true)
|
25
|
+
end
|
26
|
+
|
27
|
+
@bucket_chain.write('stuff/foo', 'bar')
|
28
|
+
@bucket_chain.delete('stuff/foo')
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'should return a Bucket which reads from only the first sub-bucket' do
|
32
|
+
@memory_bucket.should_receive(:read).with('stuff/foo').and_return('bar')
|
33
|
+
@memory_bucket.should_receive(:ls).with(nil).and_return(:some_assets)
|
34
|
+
@blackhole_bucket.should_not_receive(:read)
|
35
|
+
@blackhole_bucket.should_not_receive(:ls)
|
36
|
+
|
37
|
+
@bucket_chain.read('stuff/foo').should == 'bar'
|
38
|
+
@bucket_chain.ls.should == :some_assets
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
class CallbackCloud < AssetCloud::Base
|
4
|
+
bucket :tmp, AssetCloud::MemoryBucket
|
5
|
+
|
6
|
+
|
7
|
+
after_delete :callback_after_delete
|
8
|
+
before_delete :callback_before_delete
|
9
|
+
|
10
|
+
after_write :callback_after_write
|
11
|
+
before_write :callback_before_write
|
12
|
+
end
|
13
|
+
|
14
|
+
class MethodRecordingCloud < AssetCloud::Base
|
15
|
+
attr_accessor :run_callbacks
|
16
|
+
|
17
|
+
bucket :tmp, AssetCloud::MemoryBucket
|
18
|
+
|
19
|
+
before_write :callback_before_write
|
20
|
+
after_write :callback_before_write
|
21
|
+
|
22
|
+
|
23
|
+
def method_missing(method, *args)
|
24
|
+
@run_callbacks << method.to_sym
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe CallbackCloud do
|
29
|
+
before { @fs = CallbackCloud.new(File.dirname(__FILE__) + '/files', 'http://assets/') }
|
30
|
+
|
31
|
+
it "should invoke callbacks after store" do
|
32
|
+
|
33
|
+
@fs.should_receive(:callback_before_write).with('tmp/file.txt', 'text').and_return(true)
|
34
|
+
@fs.should_receive(:callback_after_write).with('tmp/file.txt', 'text').and_return(true)
|
35
|
+
|
36
|
+
|
37
|
+
@fs.write 'tmp/file.txt', 'text'
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should invoke callbacks after delete" do
|
42
|
+
|
43
|
+
@fs.should_receive(:callback_before_delete).with('tmp/file.txt').and_return(true)
|
44
|
+
@fs.should_receive(:callback_after_delete).with('tmp/file.txt').and_return(true)
|
45
|
+
|
46
|
+
|
47
|
+
@fs.delete 'tmp/file.txt'
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should invoke callbacks even when constructing a new asset" do
|
51
|
+
@fs.should_receive(:callback_before_write).with('tmp/file.txt', 'hello').and_return(true)
|
52
|
+
@fs.should_receive(:callback_after_write).with('tmp/file.txt', 'hello').and_return(true)
|
53
|
+
|
54
|
+
|
55
|
+
asset = @fs.build('tmp/file.txt')
|
56
|
+
asset.value = 'hello'
|
57
|
+
asset.store
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
describe MethodRecordingCloud do
|
64
|
+
before do
|
65
|
+
@fs = MethodRecordingCloud.new(File.dirname(__FILE__) + '/files', 'http://assets/')
|
66
|
+
@fs.run_callbacks = []
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'should record event when invoked' do
|
70
|
+
@fs.write('tmp/file.txt', 'random data')
|
71
|
+
@fs.run_callbacks.should == [:callback_before_write, :callback_before_write]
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'should record event when assignment operator is used' do
|
75
|
+
@fs['tmp/file.txt'] = 'random data'
|
76
|
+
@fs.run_callbacks.should == [:callback_before_write, :callback_before_write]
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
class FileSystemCloud < AssetCloud::Base
|
4
|
+
bucket AssetCloud::InvalidBucket
|
5
|
+
bucket :products, AssetCloud::FileSystemBucket
|
6
|
+
bucket :tmp, AssetCloud::FileSystemBucket
|
7
|
+
end
|
8
|
+
|
9
|
+
|
10
|
+
describe FileSystemCloud do
|
11
|
+
directory = File.dirname(__FILE__) + '/files'
|
12
|
+
|
13
|
+
before do
|
14
|
+
@fs = FileSystemCloud.new(directory , 'http://assets/files' )
|
15
|
+
FileUtils.mkdir_p(directory + '/tmp')
|
16
|
+
end
|
17
|
+
|
18
|
+
after do
|
19
|
+
FileUtils.rm_rf(directory + '/tmp')
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should use invalid bucket for random directories" do
|
23
|
+
@fs.bucket_for('does-not-exist/file.txt').should be_an_instance_of(AssetCloud::InvalidBucket)
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should use filesystem bucekt for products/ and tmp/ directories" do
|
27
|
+
@fs.bucket_for('products/file.txt').should be_an_instance_of(AssetCloud::FileSystemBucket)
|
28
|
+
@fs.bucket_for('tmp/file.txt').should be_an_instance_of(AssetCloud::FileSystemBucket)
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should return Asset for existing files" do
|
32
|
+
@fs['products/key.txt'].exist?.should == true
|
33
|
+
@fs['products/key.txt'].should be_an_instance_of(AssetCloud::Asset)
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should be able to test if a file exists or not" do
|
37
|
+
@fs.stat('products/key.txt').exist?.should == true
|
38
|
+
@fs.stat('products/key2.txt').exist?.should == false
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should be able to list files" do
|
42
|
+
@fs.ls('products').collect(&:key).should == ['products/key.txt']
|
43
|
+
end
|
44
|
+
|
45
|
+
describe 'when modifying file system' do
|
46
|
+
|
47
|
+
it "should call write after storing an asset" do
|
48
|
+
@fs.buckets[:tmp].should_receive(:write).with('tmp/new_file.test', 'hello world').and_return(true)
|
49
|
+
|
50
|
+
@fs.build('tmp/new_file.test', 'hello world').store
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should be able to create new files" do
|
54
|
+
@fs.build('tmp/new_file.test', 'hello world').store
|
55
|
+
|
56
|
+
@fs.stat('tmp/new_file.test').exist.should == true
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should be able to create new files with simple assignment" do
|
60
|
+
@fs['tmp/new_file.test'] = 'hello world'
|
61
|
+
|
62
|
+
@fs.stat('tmp/new_file.test').exist.should == true
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should create directories as needed" do
|
66
|
+
@fs.build('tmp/new_file.test', 'hello world').store
|
67
|
+
|
68
|
+
@fs['tmp/new_file.test'].exist?.should == true
|
69
|
+
@fs['tmp/new_file.test'].value.should == 'hello world'
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
value
|