adrift 0.0.1

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.
@@ -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