saviour 0.5.11 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8506a47d7861dc92875e3dca040bc958ef442f4622464c4604545b7d2d255e36
4
- data.tar.gz: 483e8eb3fe97c38feb64cdc2e7f7cc20580070e533759fc991fddd4ecb3ea054
3
+ metadata.gz: f9031d0f7f9624571c39792d2953c5eb11f50f7edd7f4c7117bf7951166c43b8
4
+ data.tar.gz: 3ef5fac874948c7b1a330b756c25c88c357108e08237ab940f4e0cb0d71ff55e
5
5
  SHA512:
6
- metadata.gz: b751a08547b441ebebe4483d880ddd7c5a302c1f8eb0c040a65b282ab7d9a9ce0a61c7a54d677098c99a91f67f4b6bf81b58fe4e453807b02514490d85f95e09
7
- data.tar.gz: e0157df1f371809d1d8894bcfe6af390a3ce6a8ab649e16b7a6b9bd982f17ff95c7786ddfd330a9654625c6956cf2f029e68c9a6a8cc858f5950206317546bc6
6
+ metadata.gz: aa7d00ae67fe68da25c4eb9652813b9a6f0d751a0ddf9729efe43c6dcfd5af2ffa8b7db14e9bb9dcc06b8c5a4cf8bcea503c3394abf2d5e82e8213b6d0f3589f
7
+ data.tar.gz: 348f05edc2b3f0d5185412f741e5d9aedecfdff44430833aaed0ab7221a70fc6c834d4489d0ff8c1bd01c06c20fb1bd56a3b996b6d3c75cdd15471c911159636
data/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
 
7
7
  Saviour is a tool to help you manage files attached to Active Record models. It tries to be minimal about the
8
8
  use cases it covers, but with a deep and complete coverage on the ones it does. For example, it offers
9
- no support for image manipulation, but it does implement dirty tracking and transactional-aware behavior.
9
+ no support for image manipulation, but it does implement dirty tracking and transactional-aware behavior.
10
10
 
11
11
  It also tries to have a flexible design, so that additional features can be added by the user on top of it.
12
12
  You can see an example of such typical features on the [FAQ section at the end of this document](#faq).
@@ -24,15 +24,15 @@ that wants to be solved.
24
24
  They offer a complete out-of-the-box solution that covers many different needs:
25
25
  image management, caching of files for seamless integration with html forms, direct uploads to s3, metadata
26
26
  extraction, background jobs integration or support for different ORMs are some of the features you can find on
27
- those libraries.
27
+ those libraries.
28
28
 
29
29
  If you need those functionalities and they suit your needs, they can be perfect solutions for you.
30
30
 
31
31
  The counterpart, however, is that they have more dependencies and, as they cover a broader spectrum of
32
- use cases, they tend to impose more conventions that are expected to be followed as is. If you don't want,
32
+ use cases, they tend to impose more conventions that are expected to be followed as is. If you don't want,
33
33
  or can't follow some of those conventions then you're out of luck.
34
34
 
35
- Saviour provides a battle-tested infrastructure for storing files following an AR model
35
+ Saviour provides a battle-tested infrastructure for storing files following an AR model
36
36
  life-cycle which can be easily extended to suit your custom needs.
37
37
 
38
38
 
@@ -53,6 +53,7 @@ And then execute:
53
53
  <!-- START doctoc generated TOC please keep comment here to allow auto update -->
54
54
  <!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
55
55
 
56
+
56
57
  - [Quick start](#quick-start)
57
58
  - [General Usage](#general-usage)
58
59
  - [Api on attachment](#api-on-attachment)
@@ -62,6 +63,7 @@ And then execute:
62
63
  - [S3 Storage](#s3-storage)
63
64
  - [Uploader classes](#uploader-classes)
64
65
  - [store_dir](#store_dir)
66
+ - [with_storage](#with_storage)
65
67
  - [Processors](#processors)
66
68
  - [halt_process](#halt_process)
67
69
  - [Versions](#versions)
@@ -160,7 +162,7 @@ The filename given to the file will be obtained by following this process:
160
162
 
161
163
  If none of that works, a random filename will be assigned.
162
164
 
163
- The actual storing of the file and any possible related processing (more on this [later](#processors)) will
165
+ The actual storing of the file and any possible related processing (more on this [later](#processors)) will
164
166
  happen on after save, not on assignation. You can assign and re-assign different values to an attachment at no
165
167
  cost.
166
168
 
@@ -180,7 +182,7 @@ Given the previous example of a User with an avatar attachment, the following me
180
182
  - `user.avatar.filename`: Returns the filename of the stored file.
181
183
  - `user.avatar.persisted_path`: If persisted, returns the path of the file as stored in the storage, otherwise nil. It's the same as the db column value.
182
184
  - `user.avatar.changed?`: Returns true/false if the attachment has been assigned but not yet saved.
183
-
185
+
184
186
  Usage example:
185
187
 
186
188
  ```ruby
@@ -218,7 +220,7 @@ user.avatar.with_copy # => yields a tempfile with the image
218
220
  user.avatar.read # => bytecontents
219
221
  ```
220
222
 
221
-
223
+
222
224
  #### Additional api on the model
223
225
 
224
226
  When you declare an attachment in an AR model, the model is extended with:
@@ -331,7 +333,7 @@ Those options will be forwarded directly to aws-sdk, you can see the complete re
331
333
 
332
334
  https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/S3/Client.html#put_object-instance_method
333
335
 
334
- Currently, there's no support for different create options on a per-file basis. All stored files will be created
336
+ Currently, there's no support for different create options on a per-file basis. All stored files will be created
335
337
  using the same options. If you want a public access on those files, you can make them public with a general
336
338
  rule at the bucket level or using the `acl` create option:
337
339
 
@@ -372,7 +374,7 @@ syntax is usually more convenient if you don't have a lot of code in your upload
372
374
  ```ruby
373
375
  class Post < ApplicationRecord
374
376
  include Saviour::Model
375
-
377
+
376
378
  attach_file :image do
377
379
  store_dir { "uploads/posts/images/#{model.id}/" }
378
380
  end
@@ -392,16 +394,16 @@ At runtime the model is available as `model`, and the name of the attachment as
392
394
  ```ruby
393
395
  class PostImageUploader < Saviour::BaseUploader
394
396
  store_dir { "uploads/posts/images/#{model.id}/" }
395
-
397
+
396
398
  # or
397
399
  store_dir { "uploads/posts/#{model.id}/#{attached_as}" }
398
-
400
+
399
401
  # or more generic
400
402
  store_dir { "uploads/#{model.class.name.parameterize}/#{model.id}/#{attached_as}" }
401
-
403
+
402
404
  # or with a method
403
405
  store_dir :calculate_dir
404
-
406
+
405
407
  def calculate_dir
406
408
  "uploads/posts/images/#{model.id}/"
407
409
  end
@@ -417,9 +419,31 @@ the store dir is a common approach to ensure there will be no collisions. Other
417
419
  random token generation.
418
420
 
419
421
 
422
+ #### with_storage
423
+
424
+ This method allows you to override storage on a per-attachment basis.
425
+ It could be useful if generally all attachments in your app are using storage declared
426
+ in `Saviour::Config` but you need more control for some of them.
427
+
428
+ ```ruby
429
+ class Post < ApplicationRecord
430
+ include Saviour::Model
431
+
432
+ attach_file :user_image do
433
+ store_dir { "uploads/posts/images/#{model.id}/" }
434
+ end
435
+
436
+ attach_file :admin_image do
437
+ store_dir { "uploads/posts/images/#{model.id}/" }
438
+ with_storage Saviour::S3Storage.new(bucket: "private-bucket", ...)
439
+ end
440
+ end
441
+ ```
442
+
443
+
420
444
  #### Processors
421
445
 
422
- Processors are methods (or lambdas) that receive the contents of the file being saved and its filename,
446
+ Processors are methods (or lambdas) that receive the contents of the file being saved and its filename,
423
447
  and in turn return file contents and filename. You can use them to change both values, for example:
424
448
 
425
449
  ```ruby
@@ -428,8 +452,8 @@ class PostImageUploader < Saviour::BaseUploader
428
452
 
429
453
  process do |contents, filename|
430
454
  new_filename = "#{Digest::MD5.hexdigest(contents)}-#{filename}"
431
- new_contents = Zlib::Deflate.deflate(contents)
432
-
455
+ new_contents = Zlib::Deflate.deflate(contents)
456
+
433
457
  [new_contents, new_filename]
434
458
  end
435
459
  end
@@ -445,7 +469,7 @@ and share them via a ruby module or via inheritance. In this form, you can pass
445
469
  module ProcessorsHelpers
446
470
  def resize(contents, filename, width:, height:)
447
471
  new_contents = SomeImageManipulationImplementation.new(contents).resize_to(width, height)
448
-
472
+
449
473
  [new_contents, filename]
450
474
  end
451
475
  end
@@ -454,7 +478,7 @@ class PostImageUploader < Saviour::BaseUploader
454
478
  include ProcessorsHelpers
455
479
  store_dir { "uploads/posts/#{model.id}/#{attached_as}" }
456
480
 
457
- process :resize, width: 100, height: 100
481
+ process :resize, width: 100, height: 100
458
482
  end
459
483
  ```
460
484
 
@@ -481,8 +505,8 @@ class PostImageUploader < Saviour::BaseUploader
481
505
 
482
506
  process_with_file do |file, filename|
483
507
  `convert -thumbnail 100x100^ #{Shellwords.escape(file.path)}`
484
-
485
- [file, filename]
508
+
509
+ [file, filename]
486
510
  end
487
511
  end
488
512
  ```
@@ -495,7 +519,7 @@ return a new one. If you return a different file instance, you're expected to cl
495
519
  You can mix `process` with `process_with_file` but you should try to avoid it, as it will be a performance penalty
496
520
  having to convert between formats.
497
521
 
498
- Also, even if there's just one `process`, the whole contents of the file will be loaded into memory. Avoid that usage
522
+ Also, even if there's just one `process`, the whole contents of the file will be loaded into memory. Avoid that usage
499
523
  if you're conservative about memory usage or take care of restricting the allowed file size you can work with on
500
524
  any file upload you accept across your application.
501
525
 
@@ -529,7 +553,7 @@ end
529
553
  ### Versions
530
554
 
531
555
  Versions is a common and popular feature on other file management libraries, however, they're usually implemented
532
- in a way that makes the "versioned" attachments behave differently than normal attachments.
556
+ in a way that makes the "versioned" attachments behave differently than normal attachments.
533
557
 
534
558
  Saviour takes another approach: there's no such concept as a "versioned attachment", there're only attachments.
535
559
  The way this works with Saviour is by making one attachment "follow" another one, so that whatever is assigned on
@@ -553,8 +577,8 @@ class Post < ApplicationRecord
553
577
  end
554
578
  ```
555
579
 
556
- Using the `follow: :image` syntax you declare that the `image_thumb` attachment has to be automatically assigned
557
- to the same contents as `image` every time `image` is assigned.
580
+ Using the `follow: :image` syntax you declare that the `image_thumb` attachment has to be automatically assigned
581
+ to the same contents as `image` every time `image` is assigned.
558
582
 
559
583
  The `:dependent` part is mandatory and indicates if the `image_thumb` attachment has to be removed when the
560
584
  `image` is removed (with `dependent: :destroy`) or not (with `dependent: :ignore`).
@@ -839,12 +863,12 @@ swap storages on the fly:
839
863
 
840
864
  ```ruby
841
865
  # config/env/test.rb
842
- Saviour::Config.storage = ::LocalStorage.new(...)
866
+ Saviour::Config.storage = ::LocalStorage.new(...)
843
867
 
844
868
  # spec/support/saviour.rb
845
869
  module S3Stub
846
870
  mattr_accessor :storage
847
-
871
+
848
872
  self.storage = Saviour::S3Storage.new(...)
849
873
  end
850
874
 
@@ -859,7 +883,7 @@ RSpec.configure do |config|
859
883
  end
860
884
  end
861
885
 
862
- it "some regular test" do
886
+ it "some regular test" do
863
887
  # local storage here
864
888
  end
865
889
 
@@ -877,9 +901,9 @@ Saviour::Config.processing_enabled = false
877
901
  ```
878
902
 
879
903
  This will skip all processors, so you'll avoid image manipulations, etc. If you have a more complex application
880
- and you can't disable all processors, but still would want to skip only the ones related to image manipulation,
881
- I would recommend to delegate image manipulation to a specialized class and then stub all of their methods.
882
-
904
+ and you can't disable all processors, but still would want to skip only the ones related to image manipulation,
905
+ I would recommend to delegate image manipulation to a specialized class and then stub all of their methods.
906
+
883
907
 
884
908
  ### Sources: url and string
885
909
 
@@ -948,15 +972,15 @@ the changes and work from there.
948
972
 
949
973
  Since Saviour is by design model-based, there may be use cases when this becomes a performance issue, for example:
950
974
 
951
- ##### Bypass example: Nested Cloning
975
+ ##### Bypass example: Nested Cloning
952
976
 
953
- Say that you have a model `Post` that has many `Image`s, and you're working with S3. `Post` has 3 attachments and
954
- `Image` has 2 attachments. If you want to do a feature to "clone" a post, a simple implementation would be to
977
+ Say that you have a model `Post` that has many `Image`s, and you're working with S3. `Post` has 3 attachments and
978
+ `Image` has 2 attachments. If you want to do a feature to "clone" a post, a simple implementation would be to
955
979
  basically `dup` the instances and save them.
956
980
 
957
981
  However, for a post with many related images, this would represent many api calls and roundtrips to download
958
982
  contents and re-upload them. It would be a lot faster to work with s3 directly, issue api calls to copy the
959
- files inside s3 directly (no download/upload, and even you could issue those api calls concurrently),
983
+ files inside s3 directly (no download/upload, and even you could issue those api calls concurrently),
960
984
  and then assign manually crafted paths directly to the new instances.
961
985
 
962
986
 
@@ -969,7 +993,7 @@ extract common behaviors into a module:
969
993
 
970
994
  ```ruby
971
995
  module FileAttachmentHelpers
972
- # Shared processors
996
+ # Shared processors
973
997
  end
974
998
 
975
999
  module FileAttachment
@@ -1016,9 +1040,9 @@ end
1016
1040
 
1017
1041
  class Post < ApplicationRecord
1018
1042
  include FileAttachment
1019
-
1043
+
1020
1044
  attach_file_with_defaults :cover # Nothing extra needed
1021
-
1045
+
1022
1046
  attach_file_with_defaults :image do
1023
1047
  process_with_file :some_extra_thing
1024
1048
  end
@@ -1046,7 +1070,7 @@ module FileAttachment
1046
1070
  def attach_file_with_defaults(*args, &block)
1047
1071
  attached_as = args[0]
1048
1072
  # ...
1049
-
1073
+
1050
1074
  define_method("remove_#{attached_as}") do
1051
1075
  instance_variable_get("@remove_#{attached_as}")
1052
1076
  end
@@ -1056,7 +1080,7 @@ module FileAttachment
1056
1080
  define_method("remove_#{attached_as}=") do |value|
1057
1081
  instance_variable_set "@remove_#{attached_as}", ActiveRecord::Type::Boolean.new.cast(value)
1058
1082
  end
1059
-
1083
+
1060
1084
  before_update do
1061
1085
  send("remove_#{attached_as}!") if send("remove_#{attached_as}?")
1062
1086
  end
@@ -1078,7 +1102,7 @@ a.update_attributes(remove_image: "t")
1078
1102
 
1079
1103
  ### How to extract metadata from files
1080
1104
 
1081
- You can use processors to accomplish this. Just be aware that processors run concurrently, so if you want to
1105
+ You can use processors to accomplish this. Just be aware that processors run concurrently, so if you want to
1082
1106
  persist you extracted information in the database probably you'll want to use `stash`, see [the section
1083
1107
  about stash feature for examples](#stash).
1084
1108
 
@@ -1097,8 +1121,8 @@ enqueuing of the job when you detect a change in the attachment:
1097
1121
  class Post < ApplicationRecord
1098
1122
  include Saviour::Model
1099
1123
  attach_file :image
1100
-
1101
- before_save do
1124
+
1125
+ before_save do
1102
1126
  if image_changed?
1103
1127
  # On after commit, enqueue the job
1104
1128
  end
@@ -1112,7 +1136,7 @@ The job then should take the model and the attachment to process and run the pro
1112
1136
  a = Post.find(42)
1113
1137
  a.image.with_copy do |f|
1114
1138
  # manipulate f as desired
1115
- a.update_attributes! image: f
1139
+ a.update_attributes! image: f
1116
1140
  end
1117
1141
  ```
1118
1142
 
@@ -1162,4 +1186,3 @@ You can use a processor like this one:
1162
1186
  ## License
1163
1187
 
1164
1188
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
1165
-
@@ -64,6 +64,10 @@ module Saviour
64
64
  @store_dir ||= Uploader::StoreDirExtractor.new(self).store_dir
65
65
  end
66
66
 
67
+ def storage
68
+ self.class.storage
69
+ end
70
+
67
71
  class << self
68
72
  def store_dirs
69
73
  @store_dirs ||= []
@@ -73,6 +77,10 @@ module Saviour
73
77
  @processors ||= []
74
78
  end
75
79
 
80
+ def storage
81
+ @storage ||= Config.storage
82
+ end
83
+
76
84
  def process(name = nil, opts = {}, type = :memory, &block)
77
85
  if block_given?
78
86
  processors.push(method_or_block: name || block, type: type)
@@ -89,6 +97,10 @@ module Saviour
89
97
  store_dirs.push(name || block)
90
98
  end
91
99
 
100
+ def with_storage(storage)
101
+ @storage = storage
102
+ end
103
+
92
104
  def after_upload(&block)
93
105
  after_upload_hooks.push(block)
94
106
  end
@@ -11,17 +11,17 @@ module Saviour
11
11
  @persisted_path = persisted_path
12
12
 
13
13
  if persisted_path
14
- @model.instance_variable_set("@__uploader_#{@attached_as}_was", ReadOnlyFile.new(persisted_path))
14
+ @model.instance_variable_set("@__uploader_#{@attached_as}_was", ReadOnlyFile.new(persisted_path, @uploader_klass))
15
15
  end
16
16
  end
17
17
 
18
18
  def exists?
19
- persisted? && Config.storage.exists?(@persisted_path)
19
+ persisted? && @uploader_klass.storage.exists?(@persisted_path)
20
20
  end
21
21
 
22
22
  def read
23
23
  return nil unless persisted?
24
- Config.storage.read(@persisted_path)
24
+ @uploader_klass.storage.read(@persisted_path)
25
25
  end
26
26
 
27
27
  def delete
@@ -32,7 +32,7 @@ module Saviour
32
32
 
33
33
  def public_url
34
34
  return nil unless persisted?
35
- Config.storage.public_url(@persisted_path)
35
+ @uploader_klass.storage.public_url(@persisted_path)
36
36
  end
37
37
 
38
38
  def ==(another_file)
@@ -111,7 +111,7 @@ module Saviour
111
111
  temp_file.binmode
112
112
 
113
113
  begin
114
- Config.storage.read_to_file(@persisted_path, temp_file)
114
+ @uploader_klass.storage.read_to_file(@persisted_path, temp_file)
115
115
 
116
116
  yield(temp_file)
117
117
  ensure
@@ -154,13 +154,13 @@ module Saviour
154
154
 
155
155
  case source_type
156
156
  when :stream
157
- Config.storage.write(contents, path)
157
+ @uploader_klass.storage.write(contents, path)
158
158
  when :file
159
- Config.storage.write_from_file(contents, path)
159
+ @uploader_klass.storage.write_from_file(contents, path)
160
160
  end
161
161
 
162
162
  @persisted_path = path
163
- @model.instance_variable_set("@__uploader_#{@attached_as}_was", ReadOnlyFile.new(persisted_path))
163
+ @model.instance_variable_set("@__uploader_#{@attached_as}_was", ReadOnlyFile.new(persisted_path, @uploader_klass))
164
164
  path
165
165
  end
166
166
  end
@@ -96,7 +96,8 @@ module Saviour
96
96
  send(attach_as).delete
97
97
 
98
98
  work = proc do
99
- Config.storage.delete(deletion_path) if deletion_path && send(attach_as).persisted_path.nil?
99
+ file = send(attach_as)
100
+ file.uploader.storage.delete(deletion_path) if deletion_path && file.persisted_path.nil?
100
101
  end
101
102
 
102
103
  if ActiveRecord::Base.connection.current_transaction.open?
@@ -140,4 +141,4 @@ module Saviour
140
141
  end
141
142
  end
142
143
  end
143
- end
144
+ end
@@ -14,7 +14,7 @@ module Saviour
14
14
  return unless @new_path
15
15
 
16
16
  DbHelpers.run_after_rollback(@connection) do
17
- Config.storage.delete(@new_path)
17
+ uploader.storage.delete(@new_path)
18
18
  end
19
19
 
20
20
  [@column, @new_path]
@@ -37,14 +37,14 @@ module Saviour
37
37
  dup_temp_path = SecureRandom.hex
38
38
 
39
39
  dup_file = proc do
40
- Config.storage.cp @current_path, dup_temp_path
40
+ uploader.storage.cp @current_path, dup_temp_path
41
41
 
42
42
  DbHelpers.run_after_commit(@connection) do
43
- Config.storage.delete dup_temp_path
43
+ uploader.storage.delete dup_temp_path
44
44
  end
45
45
 
46
46
  DbHelpers.run_after_rollback(@connection) do
47
- Config.storage.mv dup_temp_path, @current_path
47
+ uploader.storage.mv dup_temp_path, @current_path
48
48
  end
49
49
  end
50
50
 
@@ -56,14 +56,14 @@ module Saviour
56
56
 
57
57
  if @current_path && @current_path != @new_path
58
58
  DbHelpers.run_after_commit(@connection) do
59
- Config.storage.delete(@current_path)
59
+ uploader.storage.delete(@current_path)
60
60
  end
61
61
  end
62
62
 
63
63
  # Delete the newly uploaded file only if it's an update in a different path
64
64
  if @current_path.nil? || @current_path != @new_path
65
65
  DbHelpers.run_after_rollback(@connection) do
66
- Config.storage.delete(@new_path)
66
+ uploader.storage.delete(@new_path)
67
67
  end
68
68
  end
69
69
 
@@ -88,9 +88,10 @@ module Saviour
88
88
 
89
89
  futures = attached_files.map do |column|
90
90
  Concurrent::Future.execute(executor: pool) {
91
- path = @model.send(column).persisted_path
92
- Config.storage.delete(path) if path
93
- @model.send(column).delete
91
+ file = @model.send(column)
92
+ path = file.persisted_path
93
+ file.uploader.storage.delete(path) if path
94
+ file.delete
94
95
  }
95
96
  end
96
97
 
@@ -167,4 +168,4 @@ module Saviour
167
168
  @model.class.attached_files
168
169
  end
169
170
  end
170
- end
171
+ end
@@ -2,8 +2,9 @@ module Saviour
2
2
  class ReadOnlyFile
3
3
  attr_reader :persisted_path
4
4
 
5
- def initialize(persisted_path)
5
+ def initialize(persisted_path, uploader_klass)
6
6
  @persisted_path = persisted_path
7
+ @uploader_klass = uploader_klass
7
8
  end
8
9
 
9
10
  def exists?
@@ -12,12 +13,12 @@ module Saviour
12
13
 
13
14
  def read
14
15
  return nil unless persisted?
15
- Config.storage.read(@persisted_path)
16
+ @uploader_klass.storage.read(@persisted_path)
16
17
  end
17
18
 
18
19
  def public_url
19
20
  return nil unless persisted?
20
- Config.storage.public_url(@persisted_path)
21
+ @uploader_klass.storage.public_url(@persisted_path)
21
22
  end
22
23
  alias_method :url, :public_url
23
24
 
@@ -32,4 +33,4 @@ module Saviour
32
33
  true
33
34
  end
34
35
  end
35
- end
36
+ end
@@ -1,3 +1,3 @@
1
1
  module Saviour
2
- VERSION = "0.5.11"
2
+ VERSION = "0.6.0"
3
3
  end
@@ -0,0 +1,44 @@
1
+ require 'spec_helper'
2
+
3
+ describe "uploader declaration" do
4
+ let!(:default_storage) do
5
+ Saviour::LocalStorage.new(
6
+ local_prefix: @tmpdir,
7
+ public_url_prefix: "http://domain.com"
8
+ )
9
+ end
10
+
11
+ let!(:custom_storage) do
12
+ Saviour::LocalStorage.new(
13
+ local_prefix: @tmpdir,
14
+ public_url_prefix: "http://custom-domain.com"
15
+ )
16
+ end
17
+
18
+ before { allow(Saviour::Config).to receive(:storage).and_return(default_storage) }
19
+
20
+ it "lets you override storage on attachment basis" do
21
+ klass = Class.new(Test) { include Saviour::Model }
22
+ kustom_storage = custom_storage
23
+
24
+ klass.attach_file(:file) do
25
+ store_dir { "/store/dir" }
26
+ end
27
+
28
+ klass.attach_file(:file_thumb) do
29
+ store_dir { "/store/dir" }
30
+ with_storage kustom_storage
31
+ end
32
+
33
+ a = klass.create!(
34
+ file: Saviour::StringSource.new("content", "houhou.txt"),
35
+ file_thumb: Saviour::StringSource.new("content", "custom_houhou.txt")
36
+ )
37
+
38
+ expect(a.file.filename).to eq "houhou.txt"
39
+ expect(a.file.url).to eq 'http://domain.com/store/dir/houhou.txt'
40
+
41
+ expect(a.file_thumb.filename).to eq "custom_houhou.txt"
42
+ expect(a.file_thumb.url).to eq 'http://custom-domain.com/store/dir/custom_houhou.txt'
43
+ end
44
+ end
@@ -44,6 +44,20 @@ describe Saviour::BaseUploader do
44
44
  expect(subject.store_dirs[1].call).to eq "/my/dir/4"
45
45
  end
46
46
 
47
+ it "uses storage from config by default" do
48
+ mocked_storage = double(:mocked_storage)
49
+ allow(Saviour::Config).to receive(:storage).and_return(mocked_storage)
50
+
51
+ expect(subject.storage).to eq mocked_storage
52
+ end
53
+
54
+ it "allows to override storage" do
55
+ custom_storage = double(:custom_storage)
56
+
57
+ subject.with_storage(custom_storage)
58
+ expect(subject.storage).to eq custom_storage
59
+ end
60
+
47
61
  it "is not accessible from subclasses, works in isolation" do
48
62
  subject.process :hola
49
63
  expect(subject.processors[0][:method_or_block]).to eq :hola
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: saviour
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.11
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Roger Campos
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-08-10 00:00:00.000000000 Z
11
+ date: 2019-04-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -216,6 +216,7 @@ files:
216
216
  - spec/feature/reopens_file_at_every_process_spec.rb
217
217
  - spec/feature/rewind_source_before_read_spec.rb
218
218
  - spec/feature/stash_spec.rb
219
+ - spec/feature/storage_overriding_spec.rb
219
220
  - spec/feature/transactional_behavior_spec.rb
220
221
  - spec/feature/uploader_declaration_spec.rb
221
222
  - spec/feature/validations_spec.rb
@@ -254,7 +255,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
254
255
  version: '0'
255
256
  requirements: []
256
257
  rubyforge_project:
257
- rubygems_version: 2.7.6
258
+ rubygems_version: 2.7.3
258
259
  signing_key:
259
260
  specification_version: 4
260
261
  summary: File storage handler following active record model lifecycle