saviour 0.3.1 → 0.4.0

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