saviour 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +20 -0
  3. data/.travis.yml +10 -0
  4. data/Gemfile +6 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +491 -0
  7. data/Rakefile +6 -0
  8. data/lib/saviour.rb +170 -0
  9. data/lib/saviour/base_uploader.rb +81 -0
  10. data/lib/saviour/config.rb +13 -0
  11. data/lib/saviour/file.rb +124 -0
  12. data/lib/saviour/local_storage.rb +72 -0
  13. data/lib/saviour/processors/digest.rb +16 -0
  14. data/lib/saviour/s3_storage.rb +77 -0
  15. data/lib/saviour/string_source.rb +15 -0
  16. data/lib/saviour/uploader/element.rb +19 -0
  17. data/lib/saviour/uploader/processors_runner.rb +88 -0
  18. data/lib/saviour/uploader/store_dir_extractor.rb +41 -0
  19. data/lib/saviour/url_source.rb +55 -0
  20. data/lib/saviour/version.rb +3 -0
  21. data/saviour.gemspec +26 -0
  22. data/spec/feature/access_to_model_and_mounted_as.rb +30 -0
  23. data/spec/feature/crud_workflows_spec.rb +138 -0
  24. data/spec/feature/persisted_path_spec.rb +35 -0
  25. data/spec/feature/reload_model_spec.rb +25 -0
  26. data/spec/feature/validations_spec.rb +172 -0
  27. data/spec/feature/versions_spec.rb +186 -0
  28. data/spec/models/base_uploader_spec.rb +396 -0
  29. data/spec/models/config_spec.rb +16 -0
  30. data/spec/models/file_spec.rb +210 -0
  31. data/spec/models/local_storage_spec.rb +154 -0
  32. data/spec/models/processors/digest_spec.rb +22 -0
  33. data/spec/models/s3_storage_spec.rb +170 -0
  34. data/spec/models/saviour_spec.rb +80 -0
  35. data/spec/models/url_source_spec.rb +76 -0
  36. data/spec/spec_helper.rb +93 -0
  37. data/spec/support/data/camaloon.jpg +0 -0
  38. data/spec/support/data/example.xml +21 -0
  39. data/spec/support/data/text.txt +1 -0
  40. data/spec/support/models.rb +3 -0
  41. data/spec/support/schema.rb +9 -0
  42. 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