saviour 0.3.1 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -5,33 +5,17 @@ module Saviour
5
5
  @uploader = uploader
6
6
  end
7
7
 
8
- def candidate_store_dirs
9
- @candidate_store_dirs ||= @uploader.class.store_dirs
10
- end
11
-
12
- def versioned_store_dirs?
13
- candidate_store_dirs.any? { |x| x.versioned? && x.version == @uploader.version_name }
14
- end
15
-
16
- def versioned_store_dir
17
- candidate_store_dirs.select { |x| x.versioned? && x.version == @uploader.version_name }.last if versioned_store_dirs?
18
- end
19
-
20
- def non_versioned_store_dir
21
- candidate_store_dirs.select { |x| !x.versioned? }.last
22
- end
23
-
24
8
  def store_dir_handler
25
- @store_dir_handler ||= versioned_store_dir || non_versioned_store_dir
9
+ @store_dir_handler ||= @uploader.class.store_dirs.last
26
10
  end
27
11
 
28
12
  def store_dir
29
13
  @store_dir ||= begin
30
14
  if store_dir_handler
31
- if store_dir_handler.block?
32
- @uploader.instance_eval(&store_dir_handler.method_or_block)
15
+ if store_dir_handler.respond_to?(:call)
16
+ @uploader.instance_eval(&store_dir_handler)
33
17
  else
34
- @uploader.send(store_dir_handler.method_or_block)
18
+ @uploader.send(store_dir_handler)
35
19
  end
36
20
  end
37
21
  end
@@ -9,21 +9,29 @@ module Saviour
9
9
  @uri = wrap_uri_string(url)
10
10
  end
11
11
 
12
- def read
13
- with_retry(3) { resolve(@uri) }
12
+ def read(*args)
13
+ stringio.read(*args)
14
14
  end
15
15
 
16
- def original_filename
17
- ::File.basename(@uri.path)
16
+ def rewind
17
+ stringio.rewind
18
18
  end
19
19
 
20
- def path
21
- @uri.path
20
+ def original_filename
21
+ ::File.basename(@uri.path)
22
22
  end
23
23
 
24
24
 
25
25
  private
26
26
 
27
+ def stringio
28
+ @stringio ||= StringIO.new(raw_data)
29
+ end
30
+
31
+ def raw_data
32
+ @raw_data ||= with_retry(3) { resolve(@uri) }
33
+ end
34
+
27
35
  def resolve(uri, max_redirects = MAX_REDIRECTS)
28
36
  raise RuntimeError, "Max number of allowed redirects reached (#{MAX_REDIRECTS}) when resolving #{uri}" if max_redirects == 0
29
37
 
@@ -8,21 +8,19 @@ module Saviour
8
8
 
9
9
  def validate!
10
10
  validations.each do |column, method_or_blocks|
11
- raise(ArgumentError, "There is no attachment defined as '#{column}'") unless attached_files[column]
12
- (attached_files[column] + [nil]).each do |version|
13
- if @model.send(column, version).changed?
14
- method_or_blocks.each { |method_or_block| run_validation(column, version, method_or_block) }
15
- end
11
+ raise(ArgumentError, "There is no attachment defined as '#{column}'") unless attached_files.include?(column)
12
+ if @model.send(column).changed?
13
+ method_or_blocks.each { |method_or_block| run_validation(column, method_or_block) }
16
14
  end
17
15
  end
18
16
  end
19
17
 
20
18
  private
21
19
 
22
- def run_validation(column, version, method_or_block)
23
- data = @model.send(column, version).source_data
24
- filename = @model.send(column, version).filename_to_be_assigned
25
- opts = {attached_as: column, version: version}
20
+ def run_validation(column, method_or_block)
21
+ data = @model.send(column).source_data
22
+ filename = @model.send(column).filename_to_be_assigned
23
+ opts = { attached_as: column }
26
24
 
27
25
  if method_or_block.respond_to?(:call)
28
26
  if method_or_block.arity == 2
@@ -40,7 +38,7 @@ module Saviour
40
38
  end
41
39
 
42
40
  def attached_files
43
- @model.class.attached_files || {}
41
+ @model.class.attached_files
44
42
  end
45
43
 
46
44
  def validations
@@ -1,3 +1,3 @@
1
1
  module Saviour
2
- VERSION = "0.3.1"
2
+ VERSION = "0.4.0"
3
3
  end
data/saviour.gemspec CHANGED
@@ -15,8 +15,6 @@ Gem::Specification.new do |spec|
15
15
 
16
16
  spec.required_ruby_version = ">= 2.1.0"
17
17
 
18
- spec.add_dependency "fog-aws"
19
- spec.add_dependency "mime-types"
20
18
  spec.add_dependency "activerecord", ">= 4.0"
21
19
  spec.add_dependency "activesupport", ">= 4.0"
22
20
 
@@ -25,4 +23,6 @@ Gem::Specification.new do |spec|
25
23
  spec.add_development_dependency "rake"
26
24
  spec.add_development_dependency "sqlite3"
27
25
  spec.add_development_dependency "appraisal"
26
+ spec.add_development_dependency "fog-aws"
27
+ spec.add_development_dependency "mime-types"
28
28
  end
@@ -0,0 +1,33 @@
1
+ require 'spec_helper'
2
+
3
+ describe "method override" do
4
+ before { allow(Saviour::Config).to receive(:storage).and_return(Saviour::LocalStorage.new(local_prefix: @tmpdir, public_url_prefix: "http://domain.com")) }
5
+
6
+ it "works with user redefinition" do
7
+ uploader = Class.new(Saviour::BaseUploader) {
8
+ store_dir { "/store/dir" }
9
+ process { |contents, filename| [contents, "foo_#{filename}"] }
10
+ }
11
+ klass = Class.new(Test) { include Saviour::Model }
12
+ klass.attach_file :file, uploader
13
+ klass.class_eval do
14
+ attr_accessor :setter_bean, :getter_bean
15
+
16
+ def file=(value)
17
+ self.setter_bean = 'setted!'
18
+ super
19
+ end
20
+
21
+ def file
22
+ self.getter_bean = 'getted!'
23
+ super
24
+ end
25
+ end
26
+
27
+ a = klass.create! file: Saviour::StringSource.new("content", "houhou.txt")
28
+ expect(a.setter_bean).to eq 'setted!'
29
+ expect(a.file.filename).to eq "foo_houhou.txt"
30
+ expect(a.file.url).to eq 'http://domain.com/store/dir/foo_houhou.txt'
31
+ expect(a.getter_bean).to eq 'getted!'
32
+ end
33
+ end
@@ -0,0 +1,53 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Make one attachment follow another one" do
4
+ before { allow(Saviour::Config).to receive(:storage).and_return(Saviour::LocalStorage.new(local_prefix: @tmpdir, public_url_prefix: "http://domain.com")) }
5
+
6
+ let(:uploader) {
7
+ Class.new(Saviour::BaseUploader) do
8
+ store_dir { "/store/dir" }
9
+ end
10
+ }
11
+
12
+ let(:uploader_for_version) {
13
+ Class.new(Saviour::BaseUploader) do
14
+ store_dir { "/store/dir" }
15
+
16
+ process do |contents, filename|
17
+ [contents, "new_#{filename}"]
18
+ end
19
+ end
20
+ }
21
+
22
+ let(:klass) {
23
+ a = Class.new(Test) { include Saviour::Model }
24
+ a.attach_file :file, uploader
25
+ a.attach_file :file_thumb, uploader_for_version, follow: :file
26
+ a
27
+ }
28
+
29
+ describe "automatic assignation" do
30
+ it "works when :file is assigned if previously empty" do
31
+ a = klass.create! file: Saviour::StringSource.new("blabla", "cuca.xml")
32
+
33
+ expect(a.file.filename).to eq "cuca.xml"
34
+ expect(a.file_thumb.filename).to eq "new_cuca.xml"
35
+
36
+ expect(a.file.read.bytesize).to eq 6
37
+ expect(a.file_thumb.read.bytesize).to eq 6
38
+ end
39
+
40
+ it "does not override a previously assigned source" do
41
+ a = klass.new
42
+ a.file_thumb = StringIO.new("some contents without a filename")
43
+ a.file = Saviour::StringSource.new("blabla", "cuca.xml")
44
+ a.save!
45
+
46
+ expect(a.file.filename).to eq "cuca.xml"
47
+ expect(a.file_thumb.read).to eq "some contents without a filename"
48
+
49
+ expect(a.file.read.bytesize).to eq 6
50
+ expect(a.file_thumb.read.bytesize).to eq 32
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'source is rewinded before read' do
4
+ before { allow(Saviour::Config).to receive(:storage).and_return(Saviour::LocalStorage.new(local_prefix: @tmpdir, public_url_prefix: "http://domain.com")) }
5
+
6
+ it do
7
+ uploader = Class.new(Saviour::BaseUploader) {
8
+ store_dir { "/store/dir/#{model.id}" }
9
+ }
10
+ klass = Class.new(Test) { include Saviour::Model }
11
+ klass.attach_file :file, uploader
12
+
13
+ with_test_file("example.xml") do |file|
14
+ a = klass.create! file: file
15
+ b = klass.create! file: file
16
+
17
+ expect(a.file.read.bytesize).to eq 409
18
+ expect(b.file.read.bytesize).to eq 409
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,38 @@
1
+ require 'spec_helper'
2
+
3
+ describe "uploader declaration" do
4
+ before { allow(Saviour::Config).to receive(:storage).and_return(Saviour::LocalStorage.new(local_prefix: @tmpdir, public_url_prefix: "http://domain.com")) }
5
+
6
+ it "raises exception if not provided either way" do
7
+ expect {
8
+ klass = Class.new(Test) { include Saviour::Model }
9
+ klass.attach_file :file
10
+ }.to raise_error.with_message "you must provide either an UploaderClass or a block to define it."
11
+ end
12
+
13
+ it "lets you provide uploader as a class" do
14
+ uploader = Class.new(Saviour::BaseUploader) {
15
+ store_dir { "/store/dir" }
16
+ process { |contents, filename| [contents, "foo_#{filename}"] }
17
+ }
18
+ klass = Class.new(Test) { include Saviour::Model }
19
+ klass.attach_file :file, uploader
20
+ a = klass.create! file: Saviour::StringSource.new("content", "houhou.txt")
21
+
22
+ expect(a.file.filename).to eq "foo_houhou.txt"
23
+ expect(a.file.url).to eq 'http://domain.com/store/dir/foo_houhou.txt'
24
+ end
25
+
26
+ it "lets you provide uploader as a block" do
27
+ klass = Class.new(Test) { include Saviour::Model }
28
+
29
+ klass.attach_file(:file) do
30
+ store_dir { "/store/dir" }
31
+ process { |contents, filename| [contents, "foo_#{filename}"] }
32
+ end
33
+ a = klass.create! file: Saviour::StringSource.new("content", "houhou.txt")
34
+
35
+ expect(a.file.filename).to eq "foo_houhou.txt"
36
+ expect(a.file.url).to eq 'http://domain.com/store/dir/foo_houhou.txt'
37
+ end
38
+ end
@@ -150,29 +150,4 @@ describe "validations saving a new file" do
150
150
  expect(a.errors[:file][0]).to eq "Received error in file"
151
151
  end
152
152
  end
153
-
154
- context "versions are validated" do
155
- let(:uploader) {
156
- Class.new(Saviour::BaseUploader) {
157
- store_dir { "/store/dir" }
158
- version(:thumb)
159
- }
160
- }
161
- let(:klass) {
162
- Class.new(base_klass) do
163
- attach_validation(:file) do |contents, _, opts|
164
- errors.add(:file, "Cannot start with X in version #{opts[:version]}") if contents[0] == 'X'
165
- end
166
- end
167
- }
168
-
169
- it do
170
- a = klass.create!
171
- a.file.assign Saviour::StringSource.new("correct contents")
172
- a.file(:thumb).assign Saviour::StringSource.new("X Incorrect contents")
173
-
174
- expect(a).not_to be_valid
175
- expect(a.errors[:file][0]).to eq "Cannot start with X in version thumb"
176
- end
177
- end
178
153
  end
@@ -16,7 +16,7 @@ describe Saviour::BaseUploader do
16
16
 
17
17
  it do
18
18
  subject.process :hola
19
- expect(subject.processors[0][:element].method_or_block).to eq :hola
19
+ expect(subject.processors[0][:method_or_block]).to eq :hola
20
20
  expect(subject.processors[0][:opts]).to eq({})
21
21
  end
22
22
 
@@ -24,77 +24,44 @@ describe Saviour::BaseUploader do
24
24
  subject.process :a
25
25
  subject.process :resize, width: 50
26
26
 
27
- expect(subject.processors[0][:element].method_or_block).to eq :a
27
+ expect(subject.processors[0][:method_or_block]).to eq :a
28
28
  expect(subject.processors[0][:opts]).to eq({})
29
29
 
30
- expect(subject.processors[1][:element].method_or_block).to eq :resize
30
+ expect(subject.processors[1][:method_or_block]).to eq :resize
31
31
  expect(subject.processors[1][:opts]).to eq({width: 50})
32
32
  end
33
33
 
34
34
  it do
35
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
36
+ expect(subject.processors[0][:method_or_block]).to respond_to :call
37
+ expect(subject.processors[0][:method_or_block].call).to eq 5
38
38
  end
39
39
 
40
40
  it do
41
41
  subject.store_dir { "/my/dir" }
42
- expect(subject.store_dirs[0].method_or_block.call).to eq "/my/dir"
42
+ expect(subject.store_dirs[0].call).to eq "/my/dir"
43
43
  end
44
44
 
45
45
  it do
46
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
47
+ expect(subject.store_dirs[0]).to eq :method_to_return_the_dir
48
48
  end
49
49
 
50
50
  it "can use store_dir twice and last prevails" do
51
51
  subject.store_dir { "/my/dir" }
52
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"
53
+ expect(subject.store_dirs[0].call).to eq "/my/dir"
54
+ expect(subject.store_dirs[1].call).to eq "/my/dir/4"
55
55
  end
56
56
 
57
57
  it "is not accessible from subclasses, works in isolation" do
58
58
  subject.process :hola
59
- expect(subject.processors[0][:element].method_or_block).to eq :hola
59
+ expect(subject.processors[0][:method_or_block]).to eq :hola
60
60
  expect(subject.processors[0][:opts]).to eq({})
61
61
 
62
62
  subclass = Class.new(subject)
63
63
  expect(subclass.processors).to eq []
64
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
65
  end
99
66
 
100
67
  describe "initialization with data" do
@@ -215,136 +182,6 @@ describe Saviour::BaseUploader do
215
182
  end
216
183
  end
217
184
 
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
185
  describe "#process_with_file" do
349
186
  subject { uploader.new(data: {model: "model", attached_as: "attached_as"}) }
350
187