adrift 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,78 @@
1
+ require 'spec_helper'
2
+
3
+ module Adrift
4
+ describe FileToAttach do
5
+ context "when initialized with an unknown file representation" do
6
+ it "raises an error" do
7
+ expect {
8
+ FileToAttach.new(Object.new)
9
+ }.to raise_error(UnknownFileRepresentationError)
10
+ end
11
+ end
12
+
13
+ context "within a rails application" do
14
+ let(:file_to_attach) { FileToAttach.new(file_representation) }
15
+ let(:file_representation) do
16
+ double('uploaded file', {
17
+ :original_filename => 'me.png',
18
+ :tempfile => double('tempfile', :path => '/tmp/123')
19
+ })
20
+ end
21
+
22
+ describe "#original_filename" do
23
+ it "returns the original filename of the uploaded file" do
24
+ file_to_attach.original_filename.should ==
25
+ file_representation.original_filename
26
+ end
27
+ end
28
+
29
+ describe "#path" do
30
+ it "returns the uploaded tempfile's path" do
31
+ file_to_attach.path.should == file_representation.tempfile.path
32
+ end
33
+ end
34
+ end
35
+
36
+ context "within a rack (non rails) application" do
37
+ let(:file_to_attach) { FileToAttach.new(file_representation) }
38
+ let(:file_representation) do
39
+ {
40
+ :filename => 'me.png',
41
+ :tempfile => double('tempfile', :path => '/tmp/123')
42
+ }
43
+ end
44
+
45
+ describe "#original_filename" do
46
+ it "returns the original filename of the uploaded file" do
47
+ file_to_attach.original_filename.should ==
48
+ file_representation[:filename]
49
+ end
50
+ end
51
+
52
+ describe "#path" do
53
+ it "returns the uploaded tempfile's path" do
54
+ file_to_attach.path.should == file_representation[:tempfile].path
55
+ end
56
+ end
57
+ end
58
+
59
+ context "when initialized with a local file" do
60
+ let(:file_to_attach) { FileToAttach.new(file_representation) }
61
+ let(:file_representation) do
62
+ double('file', :to_path => '/avatars/me.png')
63
+ end
64
+
65
+ describe "#original_filename" do
66
+ it "returns the local file name" do
67
+ file_to_attach.original_filename.should == 'me.png'
68
+ end
69
+ end
70
+
71
+ describe "#path" do
72
+ it "returns the local file's path" do
73
+ file_to_attach.path.should == file_representation.to_path
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+
3
+ module Adrift::Integration
4
+ describe ActiveRecord do
5
+ it_behaves_like Adrift::Integration::Base do
6
+ before { klass.stub(after_save: nil, before_destroy: nil) }
7
+
8
+ describe ".attachment" do
9
+ it "registers an after save callback to save the attachments" do
10
+ klass.should_receive(:after_save).with(:save_attachments)
11
+ klass.attachment :avatar
12
+ end
13
+
14
+ it "registers a before destroy callback to destroy the attachments" do
15
+ klass.should_receive(:before_destroy).with(:destroy_attachments)
16
+ klass.attachment :avatar
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,7 @@
1
+ require 'spec_helper'
2
+
3
+ module Adrift::Integration
4
+ describe Base do
5
+ it_behaves_like Adrift::Integration::Base
6
+ end
7
+ end
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+
3
+ module Adrift::Integration
4
+ describe DataMapper do
5
+ it_behaves_like Adrift::Integration::Base do
6
+ before { klass.stub(after: nil, before: nil) }
7
+
8
+ describe ".attachment" do
9
+ it "registers an after save callback to save the attachments" do
10
+ klass.should_receive(:after).with(:save, :save_attachments)
11
+ klass.attachment :avatar
12
+ end
13
+
14
+ it "registers a before destroy callback to destroy the attachments" do
15
+ klass.should_receive(:before).with(:destroy, :destroy_attachments)
16
+ klass.attachment :avatar
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,98 @@
1
+ require 'spec_helper'
2
+
3
+ module Adrift
4
+ describe Pattern do
5
+ describe "#specialize" do
6
+ let(:attachment) { double('attachment') }
7
+
8
+ it "replaces :attachment with the pluralized attachment's name" do
9
+ attachment.stub(name: 'avatar')
10
+ Pattern.new(':attachment').specialize(attachment: attachment).should == 'avatars'
11
+ end
12
+
13
+ it "replaces :style with the given style" do
14
+ Pattern.new(':style').specialize(style: :normal).should == 'normal'
15
+ end
16
+
17
+ it "replaces :url with the attachment's url for the given style" do
18
+ attachment.stub(:url) do |style|
19
+ case style
20
+ when :original then '/attachment/url'
21
+ when :small then '/attachment/small/url'
22
+ end
23
+ end
24
+ pattern = Pattern.new(':url')
25
+ pattern.specialize(attachment: attachment, style: :original).should == '/attachment/url'
26
+ pattern.specialize(attachment: attachment, style: :small).should == '/attachment/small/url'
27
+ end
28
+
29
+ context "when the model's class is a top-level one" do
30
+ it "replaces :class with the pluralized model's class name" do
31
+ attachment.stub_chain(:model, :class, :name).and_return('User')
32
+ Pattern.new(':class').specialize(attachment: attachment).should == 'users'
33
+ end
34
+
35
+ it "replaces :class_name with the pluralized model's class name" do
36
+ attachment.stub_chain(:model, :class, :name).and_return('User')
37
+ Pattern.new(':class_name').specialize(attachment: attachment).should == 'users'
38
+ end
39
+ end
40
+
41
+ context "when the model's class is not a top-level one" do
42
+ it "replaces :class with the pluralized model's class name namespaced" do
43
+ attachment.stub_chain(:model, :class, :name).and_return('App::Models::User')
44
+ Pattern.new(':class').specialize(attachment: attachment).should == 'app/models/users'
45
+ end
46
+
47
+ it "replaces :class_name with the pluralized model's class name" do
48
+ attachment.stub_chain(:model, :class, :name).and_return('App::Models::User')
49
+ Pattern.new(':class_name').specialize(attachment: attachment).should == 'users'
50
+ end
51
+ end
52
+
53
+ it "replaces :id with the model's id" do
54
+ attachment.stub_chain(:model, :id).and_return(1)
55
+ Pattern.new(':id').specialize(attachment: attachment).should == '1'
56
+ end
57
+
58
+ context "when Pattern::Tags::Root.path has been assigned" do
59
+ after { Pattern::Tags::Root.path = nil }
60
+
61
+ it "returns Root.path" do
62
+ Pattern::Tags::Root.path = '/root/path'
63
+ Pattern.new(':root').specialize.should == '/root/path'
64
+ end
65
+ end
66
+
67
+ context "when Root.path hasn't been assigned" do
68
+ it "returns '.' by default" do
69
+ Pattern.new(':root').specialize.should == '.'
70
+ end
71
+ end
72
+
73
+ it "replaces :filename with the attachment's file name" do
74
+ attachment.stub(filename: 'me.png')
75
+ Pattern.new(':filename').specialize(attachment: attachment).should == 'me.png'
76
+ end
77
+
78
+ it "replaces :basename with the attachment's file basename" do
79
+ attachment.stub(filename: 'me.png')
80
+ Pattern.new(':basename').specialize(attachment: attachment).should == 'me'
81
+ end
82
+
83
+ it "replaces :extension with the attachment's file extension" do
84
+ attachment.stub(filename: 'me.png')
85
+ Pattern.new(':extension').specialize(attachment: attachment).should == 'png'
86
+ end
87
+
88
+ it "replaces a tag every time it appears" do
89
+ attachment.stub(name: 'avatar')
90
+ Pattern.new(':attachment/:attachment/:attachment').specialize(attachment: attachment).should == 'avatars/avatars/avatars'
91
+ end
92
+
93
+ it "doesn't replace unknown tags" do
94
+ Pattern.new(':unknown_tag').specialize.should == ':unknown_tag'
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,61 @@
1
+ require 'spec_helper'
2
+
3
+ module Adrift
4
+ module Processor
5
+ describe Thumbnail::Cli do
6
+ describe "#run" do
7
+ it "calls convert with the proper syntax" do
8
+ cli = Thumbnail::Cli.new
9
+ cli.should_receive('`').with('convert /tmp/123 -resize "50x50^" -gravity "center" -extent "50x50" /tmp/small-123')
10
+ cli.run('/tmp/123', '/tmp/small-123', :resize => '50x50^', :gravity => 'center', :extent => '50x50')
11
+ end
12
+ end
13
+ end
14
+
15
+ describe Thumbnail do
16
+ let(:cli) { double('cli').as_null_object }
17
+ let(:processor) { Thumbnail.new(cli) }
18
+
19
+ describe "#processed_files" do
20
+ context "for a newly instantiated processor" do
21
+ it "returns an empty hash" do
22
+ processor.processed_files.should == {}
23
+ end
24
+ end
25
+
26
+ context "immediately after a proccess" do
27
+ it "returns the generated files in the last process" do
28
+ processor.process('/tmp/123', :small => '50x50', :normal => '100x100')
29
+ processor.processed_files.should == { :small => '/tmp/small-123', :normal => '/tmp/normal-123' }
30
+ end
31
+ end
32
+
33
+ context "after two proccess" do
34
+ it "returns the generated files in the last process" do
35
+ processor.process('/tmp/123', :small => '50x50')
36
+ processor.process('/tmp/456', :normal => '100x100')
37
+ processor.processed_files.should == { :normal => '/tmp/normal-456' }
38
+ end
39
+ end
40
+ end
41
+
42
+ describe "#process" do
43
+ it "doesn't do anything if no styles are given" do
44
+ processor.process('/tmp/123', {})
45
+ processor.processed_files.should be_empty
46
+ end
47
+
48
+ it "creates the thumbnails for the given styles" do
49
+ cli.should_receive(:run).with('/tmp/123', '/tmp/small-123', :resize => '50x50!')
50
+ cli.should_receive(:run).with('/tmp/123', '/tmp/normal-123', :resize => '100x100')
51
+ processor.process('/tmp/123', :small => '50x50!', :normal => '100x100')
52
+ end
53
+
54
+ it "crops the image when the style definition ends with '#'" do
55
+ cli.should_receive(:run).with('/tmp/123', '/tmp/small-123', :resize => '50x50^', :gravity => 'center', :background => 'None', :extent => '50x50')
56
+ processor.process('/tmp/123', :small => '50x50#')
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,181 @@
1
+ require 'spec_helper'
2
+
3
+ module Adrift::Storage
4
+ describe Filesystem do
5
+ let(:storage) { Filesystem.new }
6
+
7
+ describe "#dirty?" do
8
+ context "for a newly instantiated storage" do
9
+ it "returns false" do
10
+ storage.should_not be_dirty
11
+ end
12
+ end
13
+
14
+ context "when it hasn't been flushed" do
15
+ context "and there's a file that needs to be stored" do
16
+ it "reutrns false" do
17
+ storage.store('/tmp/1', '/path/to/storage/1/file')
18
+ storage.should be_dirty
19
+ end
20
+ end
21
+
22
+ context "and there's a file that needs to be removed" do
23
+ it "returns false" do
24
+ storage.remove('/path/to/storage/1/file')
25
+ storage.should be_dirty
26
+ end
27
+ end
28
+ end
29
+
30
+ context "immediately after a flush" do
31
+ it "returns true" do
32
+ storage.store('/tmp/1', '/path/to/storage/1/file')
33
+ storage.remove('/path/to/storage/2/file')
34
+ storage.flush
35
+ storage.should_not be_dirty
36
+ end
37
+ end
38
+ end
39
+
40
+ describe "#removed" do
41
+ context "for a newly instantiated storage" do
42
+ it "returns an empty array" do
43
+ storage.removed.should == []
44
+ end
45
+ end
46
+
47
+ context "immediately after a flush" do
48
+ before do
49
+ storage.remove('/path/to/storage/1/file')
50
+ storage.remove('/path/to/storage/2/file')
51
+ storage.flush
52
+ end
53
+
54
+ it "returns the files removed in the last flush" do
55
+ storage.removed.should include('/path/to/storage/1/file')
56
+ storage.removed.should include('/path/to/storage/2/file')
57
+ storage.removed.size.should == 2
58
+ end
59
+
60
+ context "when a file has been enqueued for removal" do
61
+ before { storage.remove('/path/to/storage/3/file') }
62
+
63
+ it "doesn't include it before a flush" do
64
+ storage.removed.should_not include('/path/to/storage/3/file')
65
+ end
66
+
67
+ it "only contains it after a flush" do
68
+ storage.flush
69
+ storage.removed.should == ['/path/to/storage/3/file']
70
+ end
71
+ end
72
+ end
73
+ end
74
+
75
+ describe "#stored" do
76
+ context "for a newly instantiated storage" do
77
+ it "returns an empty array" do
78
+ storage.stored.should == []
79
+ end
80
+ end
81
+
82
+ context "immediately after a flush" do
83
+ before do
84
+ storage.store('/tmp/1', '/path/to/storage/1/file')
85
+ storage.store('/tmp/2', '/path/to/storage/2/file')
86
+ storage.flush
87
+ end
88
+
89
+ it "returns the files stored in the last flush" do
90
+ storage.stored.should include(['/tmp/1', '/path/to/storage/1/file'])
91
+ storage.stored.should include(['/tmp/2', '/path/to/storage/2/file'])
92
+ storage.stored.size.should == 2
93
+ end
94
+
95
+ context "when a file has been enqueued for storage" do
96
+ before { storage.store('/tmp/3', '/path/to/storage/3/file') }
97
+
98
+ it "doesn't include it before a flush" do
99
+ storage.stored.should_not include(['/tmp/3', '/path/to/storage/3/file'])
100
+ end
101
+
102
+ it "only contains it after a flush" do
103
+ storage.flush
104
+ storage.stored.should == [['/tmp/3', '/path/to/storage/3/file']]
105
+ end
106
+ end
107
+ end
108
+ end
109
+
110
+ describe "#remove!" do
111
+ before { File.stub(:exist? => true) }
112
+
113
+ context "when there're files that need to be removed" do
114
+ before do
115
+ storage.remove('/path/to/storage/1/file')
116
+ storage.remove('/path/to/storage/2/file')
117
+ end
118
+
119
+ it "removes the specified files" do
120
+ FileUtils.should_receive(:rm).with('/path/to/storage/1/file')
121
+ FileUtils.should_receive(:rm).with('/path/to/storage/2/file')
122
+ storage.remove!
123
+ end
124
+
125
+ it "doesn't try to remove inexistent files" do
126
+ File.stub(:exist?) { |path| path != '/path/to/storage/1/file' }
127
+ FileUtils.should_not_receive(:rm).with('/path/to/storage/1/file')
128
+ FileUtils.should_receive(:rm).with('/path/to/storage/2/file')
129
+ storage.remove!
130
+ end
131
+ end
132
+ end
133
+
134
+ describe "#store!" do
135
+ before { File.stub(:exist? => true) }
136
+
137
+ context "when there're files that need to be stored" do
138
+ before do
139
+ storage.store('/tmp/1', '/path/to/storage/1/file')
140
+ storage.store('/tmp/2', '/path/to/storage/2/file')
141
+ end
142
+
143
+ it "tries to create the directories for the files to store" do
144
+ FileUtils.should_receive(:mkdir_p).with('/path/to/storage/1')
145
+ FileUtils.should_receive(:mkdir_p).with('/path/to/storage/2')
146
+ storage.store!
147
+ end
148
+
149
+ it "moves the specified files to their destination path" do
150
+ FileUtils.should_receive(:cp).with('/tmp/1', '/path/to/storage/1/file')
151
+ FileUtils.should_receive(:cp).with('/tmp/2', '/path/to/storage/2/file')
152
+ storage.store!
153
+ end
154
+
155
+ it "changes the permissions of the stored files" do
156
+ FileUtils.should_receive(:chmod).with(0644, '/path/to/storage/1/file')
157
+ FileUtils.should_receive(:chmod).with(0644, '/path/to/storage/2/file')
158
+ storage.store!
159
+ end
160
+ end
161
+
162
+ describe "for each file" do
163
+ it "creates its directory, then moves it there and then sets its permissions" do
164
+ storage.store('/tmp/1', '/path/to/storage/1/file')
165
+ FileUtils.should_receive(:mkdir_p).ordered
166
+ FileUtils.should_receive(:cp).ordered
167
+ FileUtils.should_receive(:chmod).ordered
168
+ storage.store!
169
+ end
170
+ end
171
+ end
172
+
173
+ describe "#flush" do
174
+ it "removes the enqueued files for removal and then stores the enqueued for storage" do
175
+ storage.should_receive(:remove!).ordered
176
+ storage.should_receive(:store!).ordered
177
+ storage.flush
178
+ end
179
+ end
180
+ end
181
+ end