saviour 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +20 -0
- data/.travis.yml +10 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +22 -0
- data/README.md +491 -0
- data/Rakefile +6 -0
- data/lib/saviour.rb +170 -0
- data/lib/saviour/base_uploader.rb +81 -0
- data/lib/saviour/config.rb +13 -0
- data/lib/saviour/file.rb +124 -0
- data/lib/saviour/local_storage.rb +72 -0
- data/lib/saviour/processors/digest.rb +16 -0
- data/lib/saviour/s3_storage.rb +77 -0
- data/lib/saviour/string_source.rb +15 -0
- data/lib/saviour/uploader/element.rb +19 -0
- data/lib/saviour/uploader/processors_runner.rb +88 -0
- data/lib/saviour/uploader/store_dir_extractor.rb +41 -0
- data/lib/saviour/url_source.rb +55 -0
- data/lib/saviour/version.rb +3 -0
- data/saviour.gemspec +26 -0
- data/spec/feature/access_to_model_and_mounted_as.rb +30 -0
- data/spec/feature/crud_workflows_spec.rb +138 -0
- data/spec/feature/persisted_path_spec.rb +35 -0
- data/spec/feature/reload_model_spec.rb +25 -0
- data/spec/feature/validations_spec.rb +172 -0
- data/spec/feature/versions_spec.rb +186 -0
- data/spec/models/base_uploader_spec.rb +396 -0
- data/spec/models/config_spec.rb +16 -0
- data/spec/models/file_spec.rb +210 -0
- data/spec/models/local_storage_spec.rb +154 -0
- data/spec/models/processors/digest_spec.rb +22 -0
- data/spec/models/s3_storage_spec.rb +170 -0
- data/spec/models/saviour_spec.rb +80 -0
- data/spec/models/url_source_spec.rb +76 -0
- data/spec/spec_helper.rb +93 -0
- data/spec/support/data/camaloon.jpg +0 -0
- data/spec/support/data/example.xml +21 -0
- data/spec/support/data/text.txt +1 -0
- data/spec/support/models.rb +3 -0
- data/spec/support/schema.rb +9 -0
- metadata +196 -0
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "persisted path" do
|
4
|
+
before { Saviour::Config.storage = Saviour::LocalStorage.new(local_prefix: @tmpdir, public_url_prefix: "http://domain.com") }
|
5
|
+
after { Saviour::Config.storage = nil }
|
6
|
+
|
7
|
+
context "can change the default_path on the uploader and previous instances are not affected" do
|
8
|
+
it do
|
9
|
+
uploader = Class.new(Saviour::BaseUploader) { store_dir { "/store/dir" } }
|
10
|
+
klass = Class.new(Test) { include Saviour }
|
11
|
+
klass.attach_file :file, uploader
|
12
|
+
|
13
|
+
with_test_file("example.xml") do |example|
|
14
|
+
a = klass.create!
|
15
|
+
expect(a.update_attributes(file: example)).to be_truthy
|
16
|
+
expect(Saviour::Config.storage.exists?(a[:file])).to be_truthy
|
17
|
+
expect(File.dirname(a[:file])).to eq "/store/dir"
|
18
|
+
|
19
|
+
|
20
|
+
uploader.class_eval { store_dir { "/another/dir" } }
|
21
|
+
|
22
|
+
with_test_file("camaloon.jpg") do |example_2|
|
23
|
+
b = klass.create!
|
24
|
+
expect(b.update_attributes(file: example_2)).to be_truthy
|
25
|
+
|
26
|
+
expect(Saviour::Config.storage.exists?(b[:file])).to be_truthy
|
27
|
+
expect(Saviour::Config.storage.exists?(a[:file])).to be_truthy
|
28
|
+
|
29
|
+
expect(File.dirname(b[:file])).to eq "/another/dir"
|
30
|
+
expect(File.dirname(a[:file])).to eq "/store/dir"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "reload model" do
|
4
|
+
before { Saviour::Config.storage = Saviour::LocalStorage.new(local_prefix: @tmpdir, public_url_prefix: "http://domain.com") }
|
5
|
+
after { Saviour::Config.storage = nil }
|
6
|
+
|
7
|
+
context "updates the Saviour::File instance" do
|
8
|
+
it do
|
9
|
+
uploader = Class.new(Saviour::BaseUploader) { store_dir { "/store/dir" } }
|
10
|
+
klass = Class.new(Test) { include Saviour }
|
11
|
+
klass.attach_file :file, uploader
|
12
|
+
a = klass.create!
|
13
|
+
b = klass.find(a.id)
|
14
|
+
|
15
|
+
with_test_file("example.xml") do |example|
|
16
|
+
a.update_attributes! file: example
|
17
|
+
expect(a.file.exists?).to be_truthy
|
18
|
+
expect(b.file.exists?).to be_falsey
|
19
|
+
|
20
|
+
b.reload
|
21
|
+
expect(b.file.exists?).to be_truthy
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,172 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "validations saving a new file" do
|
4
|
+
before { Saviour::Config.storage = Saviour::LocalStorage.new(local_prefix: @tmpdir, public_url_prefix: "http://domain.com") }
|
5
|
+
after { Saviour::Config.storage = nil }
|
6
|
+
|
7
|
+
let(:uploader) {
|
8
|
+
Class.new(Saviour::BaseUploader) {
|
9
|
+
store_dir { "/store/dir" }
|
10
|
+
}
|
11
|
+
}
|
12
|
+
|
13
|
+
let(:base_klass) {
|
14
|
+
a = Class.new(Test) { include Saviour }
|
15
|
+
a.attach_file :file, uploader
|
16
|
+
a
|
17
|
+
}
|
18
|
+
|
19
|
+
it "fails at block validation" do
|
20
|
+
klass = Class.new(base_klass) do
|
21
|
+
attach_validation(:file) do |contents, _|
|
22
|
+
errors.add(:file, "Cannot start with X") if contents[0] == 'X'
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
with_test_file("example.xml") do |example|
|
27
|
+
allow(example).to receive(:read).and_return("X-Extra contents for the file")
|
28
|
+
a = klass.new
|
29
|
+
a.file = example
|
30
|
+
expect(a).not_to be_valid
|
31
|
+
expect(a.errors[:file][0]).to eq "Cannot start with X"
|
32
|
+
end
|
33
|
+
|
34
|
+
with_test_file("example.xml") do |example|
|
35
|
+
a = klass.new
|
36
|
+
a.file = example
|
37
|
+
expect(a).to be_valid
|
38
|
+
expect(a.save).to be_truthy
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
it "fails at method validation" do
|
44
|
+
klass = Class.new(base_klass) do
|
45
|
+
attach_validation :file, :check_filesize
|
46
|
+
|
47
|
+
def check_filesize(contents, _)
|
48
|
+
errors.add(:file, "Filesize must be less than 10 bytes") if contents.bytesize >= 10
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
with_test_file("example.xml") do |example|
|
53
|
+
allow(example).to receive(:read).and_return("1234567890")
|
54
|
+
a = klass.new
|
55
|
+
a.file = example
|
56
|
+
expect(a).not_to be_valid
|
57
|
+
expect(a.errors[:file][0]).to eq "Filesize must be less than 10 bytes"
|
58
|
+
end
|
59
|
+
|
60
|
+
with_test_file("example.xml") do |example|
|
61
|
+
allow(example).to receive(:read).and_return("123456789")
|
62
|
+
a = klass.new
|
63
|
+
a.file = example
|
64
|
+
expect(a).to be_valid
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
it "combined validatinos" do
|
69
|
+
klass = Class.new(base_klass) do
|
70
|
+
attach_validation :file, :check_filesize
|
71
|
+
attach_validation(:file) do |contents, _|
|
72
|
+
errors.add(:file, "Cannot start with X") if contents[0] == 'X'
|
73
|
+
end
|
74
|
+
|
75
|
+
def check_filesize(contents, _)
|
76
|
+
errors.add(:file, "Filesize must be less than 10 bytes") if contents.bytesize >= 10
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
with_test_file("example.xml") do |example|
|
81
|
+
allow(example).to receive(:read).and_return("X-Ex")
|
82
|
+
a = klass.new
|
83
|
+
a.file = example
|
84
|
+
expect(a).not_to be_valid
|
85
|
+
expect(a.errors[:file][0]).to eq "Cannot start with X"
|
86
|
+
end
|
87
|
+
|
88
|
+
with_test_file("example.xml") do |example|
|
89
|
+
allow(example).to receive(:read).and_return("Ex too long content")
|
90
|
+
a = klass.new
|
91
|
+
a.file = example
|
92
|
+
expect(a).not_to be_valid
|
93
|
+
expect(a.errors[:file][0]).to eq "Filesize must be less than 10 bytes"
|
94
|
+
end
|
95
|
+
|
96
|
+
# Consistent order
|
97
|
+
with_test_file("example.xml") do |example|
|
98
|
+
allow(example).to receive(:read).and_return("X-Ex too long content")
|
99
|
+
a = klass.new
|
100
|
+
a.file = example
|
101
|
+
expect(a).not_to be_valid
|
102
|
+
expect(a.errors[:file][0]).to eq "Filesize must be less than 10 bytes"
|
103
|
+
expect(a.errors[:file][1]).to eq "Cannot start with X"
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
it "validates by filename" do
|
108
|
+
klass = Class.new(base_klass) do
|
109
|
+
attach_validation :file, :check_filename
|
110
|
+
|
111
|
+
def check_filename(_, filename)
|
112
|
+
errors.add(:file, "Only .jpg files") unless filename =~ /\.jpg/
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
with_test_file("example.xml") do |example|
|
117
|
+
allow(example).to receive(:read).and_return("X-Ex")
|
118
|
+
a = klass.new
|
119
|
+
a.file = example
|
120
|
+
expect(a).not_to be_valid
|
121
|
+
expect(a.errors[:file][0]).to eq "Only .jpg files"
|
122
|
+
end
|
123
|
+
|
124
|
+
with_test_file("camaloon.jpg") do |example|
|
125
|
+
allow(example).to receive(:read).and_return("X-Ex")
|
126
|
+
a = klass.new
|
127
|
+
a.file = example
|
128
|
+
expect(a).to be_valid
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
it "receives the attached_as information" do
|
133
|
+
klass = Class.new(base_klass) do
|
134
|
+
attach_validation :file, :check_filename
|
135
|
+
|
136
|
+
def check_filename(_, _, opts)
|
137
|
+
errors.add(:file, "Received error in #{opts[:attached_as]}")
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
with_test_file("example.xml") do |example|
|
142
|
+
a = klass.new file: example
|
143
|
+
expect(a).not_to be_valid
|
144
|
+
expect(a.errors[:file][0]).to eq "Received error in file"
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
context "versions are validated" do
|
149
|
+
let(:uploader) {
|
150
|
+
Class.new(Saviour::BaseUploader) {
|
151
|
+
store_dir { "/store/dir" }
|
152
|
+
version(:thumb)
|
153
|
+
}
|
154
|
+
}
|
155
|
+
let(:klass) {
|
156
|
+
Class.new(base_klass) do
|
157
|
+
attach_validation(:file) do |contents, _, opts|
|
158
|
+
errors.add(:file, "Cannot start with X in version #{opts[:version]}") if contents[0] == 'X'
|
159
|
+
end
|
160
|
+
end
|
161
|
+
}
|
162
|
+
|
163
|
+
it do
|
164
|
+
a = klass.create!
|
165
|
+
a.file.assign Saviour::StringSource.new("correct contents")
|
166
|
+
a.file(:thumb).assign Saviour::StringSource.new("X Incorrect contents")
|
167
|
+
|
168
|
+
expect(a).not_to be_valid
|
169
|
+
expect(a.errors[:file][0]).to eq "Cannot start with X in version thumb"
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
@@ -0,0 +1,186 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "saving a new file" do
|
4
|
+
before { Saviour::Config.storage = Saviour::LocalStorage.new(local_prefix: @tmpdir, public_url_prefix: "http://domain.com") }
|
5
|
+
after { Saviour::Config.storage = nil }
|
6
|
+
|
7
|
+
let(:uploader) {
|
8
|
+
Class.new(Saviour::BaseUploader) do
|
9
|
+
store_dir { "/store/dir" }
|
10
|
+
|
11
|
+
version(:thumb) do
|
12
|
+
store_dir { "/versions/store/dir" }
|
13
|
+
end
|
14
|
+
end
|
15
|
+
}
|
16
|
+
|
17
|
+
let(:klass) {
|
18
|
+
a = Class.new(Test) { include Saviour }
|
19
|
+
a.attach_file :file, uploader
|
20
|
+
a
|
21
|
+
}
|
22
|
+
|
23
|
+
describe "creation following main file" do
|
24
|
+
it do
|
25
|
+
with_test_file("example.xml") do |example|
|
26
|
+
a = klass.create!
|
27
|
+
expect(a.update_attributes(file: example)).to be_truthy
|
28
|
+
expect(Saviour::Config.storage.exists?(a[:file_thumb])).to be_truthy
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe "deletion" do
|
34
|
+
it do
|
35
|
+
with_test_file("example.xml") do |example|
|
36
|
+
a = klass.create!
|
37
|
+
a.update_attributes(file: example)
|
38
|
+
expect(Saviour::Config.storage.exists?(a[:file_thumb])).to be_truthy
|
39
|
+
expect(Saviour::Config.storage.exists?(a[:file])).to be_truthy
|
40
|
+
|
41
|
+
a.destroy
|
42
|
+
expect(Saviour::Config.storage.exists?(a[:file_thumb])).to be_falsey
|
43
|
+
expect(Saviour::Config.storage.exists?(a[:file])).to be_falsey
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe "changes following main file" do
|
49
|
+
it do
|
50
|
+
with_test_file("example.xml") do |example|
|
51
|
+
a = klass.create!
|
52
|
+
expect(a.update_attributes(file: example)).to be_truthy
|
53
|
+
expect(Saviour::Config.storage.exists?(a[:file_thumb])).to be_truthy
|
54
|
+
|
55
|
+
with_test_file("camaloon.jpg") do |file|
|
56
|
+
a.update_attributes(file: file)
|
57
|
+
expect(Saviour::Config.storage.exists?(a[:file_thumb])).to be_truthy
|
58
|
+
file.rewind
|
59
|
+
expect(a.file(:thumb).read).to eq file.read
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe "accessing file features directly" do
|
66
|
+
let(:uploader) {
|
67
|
+
Class.new(Saviour::BaseUploader) do
|
68
|
+
store_dir { "/store/dir" }
|
69
|
+
|
70
|
+
version(:thumb) do
|
71
|
+
store_dir { "/versions/store/dir" }
|
72
|
+
process { |contents, name| ["#{contents}_for_version_thumb", name] }
|
73
|
+
end
|
74
|
+
end
|
75
|
+
}
|
76
|
+
|
77
|
+
it "#url" do
|
78
|
+
with_test_file("example.xml") do |example, name|
|
79
|
+
a = klass.create!
|
80
|
+
expect(a.update_attributes(file: example)).to be_truthy
|
81
|
+
|
82
|
+
versioned_name = "#{File.basename(name, ".*")}_thumb#{File.extname(name)}"
|
83
|
+
expect(a.file(:thumb).url).to eq "http://domain.com/versions/store/dir/#{versioned_name}"
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
it "#read" do
|
88
|
+
with_test_file("text.txt") do |example|
|
89
|
+
a = klass.create!
|
90
|
+
a.update_attributes(file: example)
|
91
|
+
|
92
|
+
expect(a.file(:thumb).read).to eq "Hello world\n_for_version_thumb"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
it "#delete" do
|
97
|
+
with_test_file("example.xml") do |example|
|
98
|
+
a = klass.create!
|
99
|
+
expect(a.update_attributes(file: example)).to be_truthy
|
100
|
+
expect(Saviour::Config.storage.exists?(a[:file_thumb])).to be_truthy
|
101
|
+
expect(Saviour::Config.storage.exists?(a[:file])).to be_truthy
|
102
|
+
|
103
|
+
a.file(:thumb).delete
|
104
|
+
expect(Saviour::Config.storage.exists?(a[:file_thumb])).to be_falsey
|
105
|
+
expect(Saviour::Config.storage.exists?(a[:file])).to be_truthy
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
it "#exists?" do
|
110
|
+
with_test_file("example.xml") do |example|
|
111
|
+
a = klass.create!
|
112
|
+
expect(a.update_attributes(file: example)).to be_truthy
|
113
|
+
expect(a.file(:thumb).exists?).to be_truthy
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
describe "assign specific version after first creation" do
|
119
|
+
it do
|
120
|
+
with_test_file("example.xml") do |example|
|
121
|
+
a = klass.create!
|
122
|
+
expect(a.update_attributes(file: example)).to be_truthy
|
123
|
+
expect(Saviour::Config.storage.exists?(a[:file_thumb])).to be_truthy
|
124
|
+
expect(a[:file_thumb]).to eq "/versions/store/dir/#{File.basename(example, ".*")}_thumb.xml"
|
125
|
+
|
126
|
+
with_test_file("camaloon.jpg") do |ex2, filename|
|
127
|
+
a.file(:thumb).assign(ex2)
|
128
|
+
|
129
|
+
expect(a.save!).to be_truthy
|
130
|
+
|
131
|
+
expect(Saviour::Config.storage.exists?(a[:file_thumb])).to be_truthy
|
132
|
+
expect(a[:file_thumb]).to eq "/versions/store/dir/#{File.basename(filename, ".*")}.jpg"
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
context do
|
138
|
+
let(:uploader) {
|
139
|
+
Class.new(Saviour::BaseUploader) do
|
140
|
+
store_dir { "/store/dir" }
|
141
|
+
|
142
|
+
version(:thumb) do
|
143
|
+
store_dir { "/versions/store/dir" }
|
144
|
+
process { |_, filename| ["modified_content", filename] }
|
145
|
+
end
|
146
|
+
end
|
147
|
+
}
|
148
|
+
|
149
|
+
it "runs the processors for that version only" do
|
150
|
+
with_test_file("example.xml") do |example|
|
151
|
+
a = klass.create!
|
152
|
+
expect(a.update_attributes(file: example)).to be_truthy
|
153
|
+
expect(Saviour::Config.storage.exists?(a[:file_thumb])).to be_truthy
|
154
|
+
expect(a[:file_thumb]).to eq "/versions/store/dir/#{File.basename(example, ".*")}_thumb.xml"
|
155
|
+
|
156
|
+
with_test_file("camaloon.jpg") do |ex2, filename|
|
157
|
+
a.file(:thumb).assign(ex2)
|
158
|
+
|
159
|
+
expect(a.save!).to be_truthy
|
160
|
+
|
161
|
+
expect(Saviour::Config.storage.exists?(a[:file_thumb])).to be_truthy
|
162
|
+
expect(a[:file_thumb]).to eq "/versions/store/dir/#{File.basename(filename, ".*")}.jpg"
|
163
|
+
expect(Saviour::Config.storage.read(a[:file_thumb])).to eq "modified_content"
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
describe "respects version assignation vs main file assignation on conflict" do
|
171
|
+
it do
|
172
|
+
a = klass.create!
|
173
|
+
|
174
|
+
with_test_file("example.xml") do |file1, fname1|
|
175
|
+
with_test_file("camaloon.jpg") do |file2, fname2|
|
176
|
+
a.file.assign(file1)
|
177
|
+
a.file(:thumb).assign(file2)
|
178
|
+
a.save!
|
179
|
+
|
180
|
+
expect(a[:file]).to eq "/store/dir/#{fname1}"
|
181
|
+
expect(a[:file_thumb]).to eq "/versions/store/dir/#{fname2}"
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
@@ -0,0 +1,396 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Saviour::BaseUploader do
|
4
|
+
let(:mocked_storage) {
|
5
|
+
Class.new {
|
6
|
+
def write(content, filename)
|
7
|
+
# pass
|
8
|
+
end
|
9
|
+
}.new
|
10
|
+
}
|
11
|
+
before { allow(Saviour::Config).to receive(:storage).and_return(mocked_storage) }
|
12
|
+
|
13
|
+
|
14
|
+
describe "DSL" do
|
15
|
+
subject { Class.new(Saviour::BaseUploader) }
|
16
|
+
|
17
|
+
it do
|
18
|
+
subject.process :hola
|
19
|
+
expect(subject.processors[0][:element].method_or_block).to eq :hola
|
20
|
+
expect(subject.processors[0][:opts]).to eq({})
|
21
|
+
end
|
22
|
+
|
23
|
+
it do
|
24
|
+
subject.process :a
|
25
|
+
subject.process :resize, width: 50
|
26
|
+
|
27
|
+
expect(subject.processors[0][:element].method_or_block).to eq :a
|
28
|
+
expect(subject.processors[0][:opts]).to eq({})
|
29
|
+
|
30
|
+
expect(subject.processors[1][:element].method_or_block).to eq :resize
|
31
|
+
expect(subject.processors[1][:opts]).to eq({width: 50})
|
32
|
+
end
|
33
|
+
|
34
|
+
it do
|
35
|
+
subject.process { 5 }
|
36
|
+
expect(subject.processors[0][:element].method_or_block).to respond_to :call
|
37
|
+
expect(subject.processors[0][:element].method_or_block.call).to eq 5
|
38
|
+
end
|
39
|
+
|
40
|
+
it do
|
41
|
+
subject.store_dir { "/my/dir" }
|
42
|
+
expect(subject.store_dirs[0].method_or_block.call).to eq "/my/dir"
|
43
|
+
end
|
44
|
+
|
45
|
+
it do
|
46
|
+
subject.store_dir :method_to_return_the_dir
|
47
|
+
expect(subject.store_dirs[0].method_or_block).to eq :method_to_return_the_dir
|
48
|
+
end
|
49
|
+
|
50
|
+
it "can use store_dir twice and last prevails" do
|
51
|
+
subject.store_dir { "/my/dir" }
|
52
|
+
subject.store_dir { "/my/dir/4" }
|
53
|
+
expect(subject.store_dirs[0].method_or_block.call).to eq "/my/dir"
|
54
|
+
expect(subject.store_dirs[1].method_or_block.call).to eq "/my/dir/4"
|
55
|
+
end
|
56
|
+
|
57
|
+
it "is not accessible from subclasses, works in isolation" do
|
58
|
+
subject.process :hola
|
59
|
+
expect(subject.processors[0][:element].method_or_block).to eq :hola
|
60
|
+
expect(subject.processors[0][:opts]).to eq({})
|
61
|
+
|
62
|
+
subclass = Class.new(subject)
|
63
|
+
expect(subclass.processors).to eq []
|
64
|
+
end
|
65
|
+
|
66
|
+
describe "version" do
|
67
|
+
it "stores as elements with the given version" do
|
68
|
+
subject.process :hola
|
69
|
+
subject.version(:thumb) do
|
70
|
+
process :resize_to_thumb
|
71
|
+
end
|
72
|
+
|
73
|
+
expect(subject.processors[0][:element].method_or_block).to eq :hola
|
74
|
+
expect(subject.processors[0][:element].version).to eq nil
|
75
|
+
expect(subject.processors[0][:opts]).to eq({})
|
76
|
+
|
77
|
+
expect(subject.processors[1][:element].method_or_block).to eq :resize_to_thumb
|
78
|
+
expect(subject.processors[1][:element].version).to eq :thumb
|
79
|
+
expect(subject.processors[1][:opts]).to eq({})
|
80
|
+
end
|
81
|
+
|
82
|
+
it "respects ordering" do
|
83
|
+
subject.process :hola
|
84
|
+
subject.version(:thumb) { process :resize_to_thumb }
|
85
|
+
subject.process :top_level
|
86
|
+
subject.version(:another) { process(:foo) }
|
87
|
+
|
88
|
+
expect(subject.processors[0][:element].method_or_block).to eq :hola
|
89
|
+
expect(subject.processors[0][:element].version).to eq nil
|
90
|
+
expect(subject.processors[1][:element].method_or_block).to eq :resize_to_thumb
|
91
|
+
expect(subject.processors[1][:element].version).to eq :thumb
|
92
|
+
expect(subject.processors[2][:element].method_or_block).to eq :top_level
|
93
|
+
expect(subject.processors[2][:element].version).to eq nil
|
94
|
+
expect(subject.processors[3][:element].method_or_block).to eq :foo
|
95
|
+
expect(subject.processors[3][:element].version).to eq :another
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
describe "initialization with data" do
|
101
|
+
it "can declare wathever" do
|
102
|
+
uploader = Class.new(Saviour::BaseUploader).new(data: {a: "2", data: "my file"})
|
103
|
+
expect(uploader).to respond_to :a
|
104
|
+
expect(uploader).to respond_to :data
|
105
|
+
expect(uploader.a).to eq "2"
|
106
|
+
end
|
107
|
+
|
108
|
+
it do
|
109
|
+
uploader = Class.new(Saviour::BaseUploader).new(data: {a: "2", data: "my file"})
|
110
|
+
expect(uploader).to respond_to :a
|
111
|
+
|
112
|
+
uploader = Class.new(Saviour::BaseUploader).new(data: {name: "johny"})
|
113
|
+
expect(uploader).not_to respond_to :a
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
describe "#write" do
|
118
|
+
subject { uploader.new(data: {model: "model", attached_as: "attached_as"}) }
|
119
|
+
|
120
|
+
context do
|
121
|
+
let(:uploader) { Class.new(Saviour::BaseUploader) }
|
122
|
+
|
123
|
+
it "error if no store_dir" do
|
124
|
+
expect { subject.write("contents", "filename.jpg") }.to raise_error(RuntimeError)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
context do
|
129
|
+
let(:uploader) { Class.new(Saviour::BaseUploader) {
|
130
|
+
store_dir { "/store/dir" }
|
131
|
+
} }
|
132
|
+
|
133
|
+
it "calls storage write" do
|
134
|
+
expect(Saviour::Config.storage).to receive(:write).with("contents", "/store/dir/file.jpg")
|
135
|
+
subject.write("contents", "file.jpg")
|
136
|
+
end
|
137
|
+
|
138
|
+
it "returns the fullpath" do
|
139
|
+
expect(subject.write("contents", "file.jpg")).to eq '/store/dir/file.jpg'
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
context do
|
144
|
+
let(:uploader) { Class.new(Saviour::BaseUploader) {
|
145
|
+
store_dir { "/store/dir" }
|
146
|
+
|
147
|
+
def resize(contents, filename)
|
148
|
+
["#{contents}-x2", filename]
|
149
|
+
end
|
150
|
+
|
151
|
+
process :resize
|
152
|
+
} }
|
153
|
+
|
154
|
+
it "calls the processors" do
|
155
|
+
expect(subject).to receive(:resize).with("content", "output.png").and_call_original
|
156
|
+
expect(Saviour::Config.storage).to receive(:write).with("content-x2", "/store/dir/output.png")
|
157
|
+
subject.write("content", "output.png")
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
context do
|
162
|
+
let(:uploader) { Class.new(Saviour::BaseUploader) {
|
163
|
+
store_dir { "/store/dir" }
|
164
|
+
|
165
|
+
def resize(contents, filename)
|
166
|
+
["#{contents}-x2", filename]
|
167
|
+
end
|
168
|
+
|
169
|
+
process :resize
|
170
|
+
process { |content, filename| ["#{content}_x9", "prefix-#{filename}"] }
|
171
|
+
} }
|
172
|
+
|
173
|
+
it "respects ordering on processor calling" do
|
174
|
+
expect(Saviour::Config.storage).to receive(:write).with("content-x2_x9", "/store/dir/prefix-output.png")
|
175
|
+
subject.write("content", "output.png")
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
context do
|
180
|
+
let(:uploader) { Class.new(Saviour::BaseUploader) {
|
181
|
+
store_dir { "/store/dir" }
|
182
|
+
|
183
|
+
def resize(contents, filename, opts = {})
|
184
|
+
["#{contents}-#{opts[:width]}-#{opts[:height]}", filename]
|
185
|
+
end
|
186
|
+
|
187
|
+
process :resize, width: 50, height: 10
|
188
|
+
} }
|
189
|
+
|
190
|
+
it "calls the method using the stored arguments" do
|
191
|
+
expect(Saviour::Config.storage).to receive(:write).with("content-50-10", "/store/dir/output.png")
|
192
|
+
subject.write("content", "output.png")
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
context do
|
197
|
+
let(:uploader) { Class.new(Saviour::BaseUploader) {
|
198
|
+
store_dir { "/store/dir" }
|
199
|
+
|
200
|
+
def rename(contents, filename)
|
201
|
+
[contents, "#{model.id}_#{filename}"]
|
202
|
+
end
|
203
|
+
|
204
|
+
process :rename
|
205
|
+
process { |content, filename| [content, "#{model.name}_#{filename}"] }
|
206
|
+
} }
|
207
|
+
|
208
|
+
let(:model) { double(id: 8, name: "Robert") }
|
209
|
+
subject { uploader.new(data: {model: model, attached_as: "attached_as"}) }
|
210
|
+
|
211
|
+
it "can access model from processors" do
|
212
|
+
expect(Saviour::Config.storage).to receive(:write).with("content", "/store/dir/Robert_8_output.png")
|
213
|
+
subject.write("content", "output.png")
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
describe "version" do
|
219
|
+
subject { uploader.new(version: :thumb) }
|
220
|
+
|
221
|
+
describe "store_dir" do
|
222
|
+
context "is the last one defined for the given version" do
|
223
|
+
let(:uploader) { Class.new(Saviour::BaseUploader) {
|
224
|
+
store_dir { "/store/dir" }
|
225
|
+
version(:thumb) do
|
226
|
+
store_dir { "/thumb/store/dir" }
|
227
|
+
end
|
228
|
+
store_dir { "/store/dir/second" }
|
229
|
+
} }
|
230
|
+
|
231
|
+
it do
|
232
|
+
expect(Saviour::Uploader::StoreDirExtractor.new(subject).store_dir).to eq "/thumb/store/dir"
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
context "is the last one defined without version if not specified per version" do
|
237
|
+
let(:uploader) { Class.new(Saviour::BaseUploader) {
|
238
|
+
store_dir { "/store/dir" }
|
239
|
+
version(:thumb) { process(:whatever) }
|
240
|
+
store_dir { "/store/dir/second" }
|
241
|
+
} }
|
242
|
+
|
243
|
+
it do
|
244
|
+
expect(Saviour::Uploader::StoreDirExtractor.new(subject).store_dir).to eq "/store/dir/second"
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
end
|
249
|
+
|
250
|
+
describe "processing behaviour on write" do
|
251
|
+
context "fails if no store_dir defined for root version" do
|
252
|
+
let(:uploader) { Class.new(Saviour::BaseUploader) {
|
253
|
+
version(:thumb) { store_dir { "/store/dir" } }
|
254
|
+
} }
|
255
|
+
|
256
|
+
it do
|
257
|
+
a = uploader.new(data: {model: "model", attached_as: "attached_as"})
|
258
|
+
expect { a.write('1', '2') }.to raise_error(RuntimeError)
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
context "with only one version" do
|
263
|
+
let(:uploader) { Class.new(Saviour::BaseUploader) {
|
264
|
+
store_dir { "/store/dir" }
|
265
|
+
|
266
|
+
version(:thumb) do
|
267
|
+
store_dir { "/versions/store/dir" }
|
268
|
+
process { |contents, name| [contents, "2_#{name}"] }
|
269
|
+
end
|
270
|
+
} }
|
271
|
+
|
272
|
+
it do
|
273
|
+
a = uploader.new(version: :thumb)
|
274
|
+
expect(Saviour::Config.storage).to receive(:write).with("content", "/versions/store/dir/2_output.png")
|
275
|
+
a.write("content", "output.png")
|
276
|
+
end
|
277
|
+
|
278
|
+
it do
|
279
|
+
a = uploader.new
|
280
|
+
expect(Saviour::Config.storage).to receive(:write).with("content", "/store/dir/output.png")
|
281
|
+
a.write("content", "output.png")
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
context "multiple definitions" do
|
286
|
+
let(:uploader) { Class.new(Saviour::BaseUploader) {
|
287
|
+
store_dir { "/store/dir" }
|
288
|
+
process { |contents, name| ["#{contents}_altered", name] }
|
289
|
+
|
290
|
+
version(:thumb) do
|
291
|
+
store_dir { "/versions/store/dir" }
|
292
|
+
process { |contents, name| [contents, "2_#{name}"] }
|
293
|
+
end
|
294
|
+
} }
|
295
|
+
|
296
|
+
it do
|
297
|
+
a = uploader.new(version: :thumb)
|
298
|
+
expect(Saviour::Config.storage).to receive(:write).with("content_altered", "/versions/store/dir/2_output.png")
|
299
|
+
a.write("content", "output.png")
|
300
|
+
end
|
301
|
+
|
302
|
+
it do
|
303
|
+
a = uploader.new
|
304
|
+
expect(Saviour::Config.storage).to receive(:write).with("content_altered", "/store/dir/output.png")
|
305
|
+
a.write("content", "output.png")
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
context "consecutive versions" do
|
310
|
+
let(:uploader) { Class.new(Saviour::BaseUploader) {
|
311
|
+
store_dir { "/store/dir" }
|
312
|
+
process { |contents, name| ["#{contents}_altered", name] }
|
313
|
+
|
314
|
+
version(:thumb) do
|
315
|
+
store_dir { "/versions/store/dir" }
|
316
|
+
process { |contents, name| ["thumb_#{contents}", "2_#{name}"] }
|
317
|
+
end
|
318
|
+
|
319
|
+
version(:thumb_2) do
|
320
|
+
store_dir { "/versions/store/dir" }
|
321
|
+
process { |contents, name| ["thumb_2_#{contents}", "3_#{name}"] }
|
322
|
+
end
|
323
|
+
|
324
|
+
process { |contents, name| ["last_transform_#{contents}", name] }
|
325
|
+
} }
|
326
|
+
|
327
|
+
it do
|
328
|
+
a = uploader.new
|
329
|
+
expect(Saviour::Config.storage).to receive(:write).with("last_transform_content_altered", "/store/dir/output.png")
|
330
|
+
a.write("content", "output.png")
|
331
|
+
end
|
332
|
+
|
333
|
+
it do
|
334
|
+
a = uploader.new(version: :thumb)
|
335
|
+
expect(Saviour::Config.storage).to receive(:write).with("last_transform_thumb_content_altered", "/versions/store/dir/2_output.png")
|
336
|
+
a.write("content", "output.png")
|
337
|
+
end
|
338
|
+
|
339
|
+
it do
|
340
|
+
a = uploader.new(version: :thumb_2)
|
341
|
+
expect(Saviour::Config.storage).to receive(:write).with("last_transform_thumb_2_content_altered", "/versions/store/dir/3_output.png")
|
342
|
+
a.write("content", "output.png")
|
343
|
+
end
|
344
|
+
end
|
345
|
+
end
|
346
|
+
end
|
347
|
+
|
348
|
+
describe "#process_with_file" do
|
349
|
+
subject { uploader.new(data: {model: "model", attached_as: "attached_as"}) }
|
350
|
+
|
351
|
+
context do
|
352
|
+
let(:uploader) { Class.new(Saviour::BaseUploader) {
|
353
|
+
store_dir { "/store/dir" }
|
354
|
+
|
355
|
+
def foo(file, filename)
|
356
|
+
::File.write(file.path, "modified-contents")
|
357
|
+
[file, filename]
|
358
|
+
end
|
359
|
+
|
360
|
+
process_with_file :foo
|
361
|
+
} }
|
362
|
+
|
363
|
+
it "calls the processors" do
|
364
|
+
expect(subject).to receive(:foo).with(an_instance_of(Tempfile), "output.png").and_call_original
|
365
|
+
expect(Saviour::Config.storage).to receive(:write).with("modified-contents", "/store/dir/output.png")
|
366
|
+
subject.write("contents", "output.png")
|
367
|
+
end
|
368
|
+
end
|
369
|
+
|
370
|
+
context do
|
371
|
+
let(:uploader) { Class.new(Saviour::BaseUploader) {
|
372
|
+
store_dir { "/store/dir" }
|
373
|
+
|
374
|
+
process do |contents, filename|
|
375
|
+
["#{contents}_first_run", filename]
|
376
|
+
end
|
377
|
+
|
378
|
+
process_with_file do |file, filename|
|
379
|
+
::File.write(file.path, "#{::File.read(file.path)}-modified-contents")
|
380
|
+
[file, filename]
|
381
|
+
end
|
382
|
+
|
383
|
+
process :last_run
|
384
|
+
|
385
|
+
def last_run(contents, filename)
|
386
|
+
["pre-#{contents}", "pre-#{filename}"]
|
387
|
+
end
|
388
|
+
} }
|
389
|
+
|
390
|
+
it "can mix types of runs between file and contents" do
|
391
|
+
expect(Saviour::Config.storage).to receive(:write).with("pre-contents_first_run-modified-contents", "/store/dir/pre-aaa.png")
|
392
|
+
subject.write("contents", "aaa.png")
|
393
|
+
end
|
394
|
+
end
|
395
|
+
end
|
396
|
+
end
|