saviour 0.6.2 → 0.6.7

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: 299270bf4c0f5d46a9fd7a57d51062a5a86a1c9216187baf3d0d3a6258295811
4
- data.tar.gz: a0ed19b72e62d0fc006698958bd057eaf0543c8595ae080e9279c07e33fddb95
3
+ metadata.gz: 43509e7e421eab9a8b45159d62c58513dbcb894b0ccecbd96a70cc0a29db74a6
4
+ data.tar.gz: 91b2eb4cf47dbac1b642c5ccc198ae0040ea0e81ef993c580e894065f1e58ecf
5
5
  SHA512:
6
- metadata.gz: 90ac6353b42b4c3422bb3154b983422658837e9c0c307bb4871e2c3c0e50d7101286524984c036fcf68abb91b830d82aa39886f88d362490ddf86d459401c9a3
7
- data.tar.gz: 82d6cdeab9db03a38a8819109c2cf24d4824a9ca1a9c77830bdb8d95efb98c50664ad1eeb8862224f795e9c334d8d36a9f071955e0f232ae14d2198b95124720
6
+ metadata.gz: e4a1fb654e3290d827236f115ebfd93e2f6d4994e57d022dd473f41436365de36182e689d20526cea2c9bb113dd6b558901da5deda945f526242ab2e78e7c633
7
+ data.tar.gz: 8f3f81305ceb56195d67eb2a6700a5f05bb3e4567c892a983fe845689c952864497e2bd29c47a4256a88a6f003527855952d2ed78e6edc7cff80d66adacca7b0
@@ -2,14 +2,14 @@ language: ruby
2
2
  sudo: false
3
3
  cache: bundler
4
4
  rvm:
5
- - 2.2.8
6
- - 2.3.5
7
- - 2.4.2
8
- - 2.5.0
5
+ - 2.5.8
6
+ - 2.6.6
7
+ - 2.7.1
9
8
 
10
9
  gemfile:
11
10
  - gemfiles/5.1.gemfile
12
11
  - gemfiles/5.2.gemfile
12
+ - gemfiles/6.0.gemfile
13
13
 
14
14
  addons:
15
15
  code_climate:
data/README.md CHANGED
@@ -593,7 +593,7 @@ Now, both attachments are independent:
593
593
 
594
594
  ```ruby
595
595
  # `image_thumb` can be changed independently
596
- a.update_attributes! image_thumb: File.open("/path/another_file.png")
596
+ a.update! image_thumb: File.open("/path/another_file.png")
597
597
 
598
598
  # or removed
599
599
  a.remove_file_thumb!
@@ -714,7 +714,7 @@ class ImageUploader < Saviour::BaseUploader
714
714
  end
715
715
 
716
716
  after_upload do |stash|
717
- model.update_attributes!(size: stash[:size], width: stash[:width], height: stash[:height])
717
+ model.update!(size: stash[:size], width: stash[:width], height: stash[:height])
718
718
  end
719
719
  end
720
720
  ```
@@ -817,11 +817,14 @@ as in the processor's case.
817
817
 
818
818
  ### Introspection
819
819
 
820
- Two methods are added to any class including `Saviour::Model` to give you information about what attachments
820
+ Three methods are added to any class including `Saviour::Model` to give you information about what attachments
821
821
  have been defined in that class.
822
822
 
823
823
  `Model.attached_files` will give an array of symbols, representing all the attachments declared in that class.
824
824
 
825
+ `Model.uploader_classes` will give you a hash with all the uploader classes being used in that model, key-ed by
826
+ the name of each attachment.
827
+
825
828
  `Model.attached_followers_per_leader` will give a hash where the keys are attachments that have versions
826
829
  assigned, and the values being an array of symbols, representing the attachments that are following that attachment.
827
830
 
@@ -837,6 +840,7 @@ end
837
840
 
838
841
  Post.attached_files # => [:image, :image_thumb, :image_thumb_2, :cover]
839
842
  Post.attached_followers_per_leader # => { image: [:image_thumb, :image_thumb_2] }
843
+ Post.uploader_classes # => { image: SomeUploader, image_thumb: SomeUploader, ... }
840
844
  ```
841
845
 
842
846
 
@@ -964,6 +968,38 @@ Any additional information the storage may require can be provided on instance c
964
968
  this is not used by Saviour.
965
969
 
966
970
 
971
+ ### Using `ReadOnlyFile`
972
+
973
+ Sometimes you may find the need to have a saviour `File`-like object but you don't have the original model available.
974
+
975
+ For example, if you're working directly fetching data from database, you have the `path` to an asset but you're not
976
+ loading an ActiveRecord object.
977
+
978
+ In such cases you can use the class `ReadOnlyFile`, like this:
979
+
980
+ ```ruby
981
+ class Product
982
+ # ...
983
+ attach_file :image
984
+ end
985
+
986
+ my_cheap_products = ActiveRecord::Base.connection.select_all "select image, ... from products"
987
+
988
+ # In this case, you have the path to the assets and want to convert it into an object so that the
989
+ # rest of the application can work normally with it, you can do:
990
+
991
+ storage = Product.uploader_classes[:image].storage
992
+ file = Saviour::ReadOnlyFile.new(path, storage)
993
+
994
+ file.read # -> contents
995
+ file.public_url # -> url
996
+ # etc ...
997
+ ```
998
+
999
+ You'll need to get the appropriate storage object from the same uploader that attachment is using,
1000
+ and create a new `ReadOnlyFile` instance only with the asset's path and the storage.
1001
+
1002
+
967
1003
  ### Bypassing Saviour
968
1004
 
969
1005
  The only reference to stored files Saviour holds and uses is the path persisted in the database. If you want to,
@@ -1096,7 +1132,7 @@ Then it can be used as:
1096
1132
  a = Post.find(42)
1097
1133
 
1098
1134
  # Params received from a form
1099
- a.update_attributes(remove_image: "t")
1135
+ a.update(remove_image: "t")
1100
1136
  ```
1101
1137
 
1102
1138
 
@@ -1136,7 +1172,7 @@ The job then should take the model and the attachment to process and run the pro
1136
1172
  a = Post.find(42)
1137
1173
  a.image.with_copy do |f|
1138
1174
  # manipulate f as desired
1139
- a.update_attributes! image: f
1175
+ a.update! image: f
1140
1176
  end
1141
1177
  ```
1142
1178
 
@@ -1,5 +1,3 @@
1
- # This file was generated by Appraisal
2
-
3
1
  source "https://rubygems.org"
4
2
 
5
3
  gem "codeclimate-test-reporter", :group => :test, :require => nil
@@ -1,5 +1,3 @@
1
- # This file was generated by Appraisal
2
-
3
1
  source "https://rubygems.org"
4
2
 
5
3
  gem "codeclimate-test-reporter", :group => :test, :require => nil
@@ -0,0 +1,7 @@
1
+ source "https://rubygems.org"
2
+
3
+ gem "codeclimate-test-reporter", :group => :test, :require => nil
4
+ gem "activesupport", "~> 6.0.0"
5
+ gem "activerecord", "~> 6.0.0"
6
+
7
+ gemspec :path => "../"
@@ -17,7 +17,8 @@ require 'saviour/db_helpers'
17
17
 
18
18
  require 'tempfile'
19
19
  require 'fileutils'
20
- require 'concurrent/future'
20
+ require 'concurrent/edge/throttle'
21
+ require 'concurrent/edge/lock_free_queue'
21
22
 
22
23
  require 'active_support/dependencies'
23
24
 
@@ -79,6 +79,8 @@ module Saviour
79
79
 
80
80
  def storage
81
81
  @storage ||= Config.storage
82
+
83
+ @storage.respond_to?(:call) ? @storage.call : @storage
82
84
  end
83
85
 
84
86
  def process(name = nil, opts = {}, type = :memory, &block)
@@ -22,6 +22,9 @@ module Saviour
22
22
  def committed!(*)
23
23
  @block.call
24
24
  end
25
+
26
+ def trigger_transactional_callbacks?(*)
27
+ end
25
28
  end
26
29
 
27
30
  class RollbackDummy
@@ -42,6 +45,9 @@ module Saviour
42
45
 
43
46
  def committed!(*)
44
47
  end
48
+
49
+ def trigger_transactional_callbacks?(*)
50
+ end
45
51
  end
46
52
 
47
53
 
@@ -2,26 +2,26 @@ require 'securerandom'
2
2
 
3
3
  module Saviour
4
4
  class File
5
- attr_reader :persisted_path
6
- attr_reader :source
5
+ attr_reader :persisted_path, :source, :storage
7
6
 
8
7
  def initialize(uploader_klass, model, attached_as, persisted_path = nil)
9
8
  @uploader_klass, @model, @attached_as = uploader_klass, model, attached_as
10
9
  @source_was = @source = nil
11
10
  @persisted_path = persisted_path
11
+ @storage = @uploader_klass.storage
12
12
 
13
13
  if persisted_path
14
- @model.instance_variable_set("@__uploader_#{@attached_as}_was", ReadOnlyFile.new(persisted_path, @uploader_klass))
14
+ @model.instance_variable_set("@__uploader_#{@attached_as}_was", ReadOnlyFile.new(persisted_path, @uploader_klass.storage))
15
15
  end
16
16
  end
17
17
 
18
18
  def exists?
19
- persisted? && @uploader_klass.storage.exists?(@persisted_path)
19
+ persisted? && @storage.exists?(@persisted_path)
20
20
  end
21
21
 
22
22
  def read
23
23
  return nil unless persisted?
24
- @uploader_klass.storage.read(@persisted_path)
24
+ @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
- @uploader_klass.storage.public_url(@persisted_path)
35
+ @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
- @uploader_klass.storage.read_to_file(@persisted_path, temp_file)
114
+ @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
- @uploader_klass.storage.write(contents, path)
157
+ @storage.write(contents, path)
158
158
  when :file
159
- @uploader_klass.storage.write_from_file(contents, path)
159
+ @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, @uploader_klass))
163
+ @model.instance_variable_set("@__uploader_#{@attached_as}_was", ReadOnlyFile.new(persisted_path, @storage))
164
164
  path
165
165
  end
166
166
  end
@@ -12,6 +12,8 @@ module Saviour
12
12
  @klass.attached_files = []
13
13
  @klass.class_attribute :followers_per_leader_config
14
14
  @klass.followers_per_leader_config = {}
15
+ @klass.class_attribute :uploader_classes
16
+ @klass.uploader_classes = {}
15
17
 
16
18
  persistence_klass = @persistence_klass
17
19
 
@@ -36,10 +38,13 @@ module Saviour
36
38
  raise ConfigurationError, "you must provide either an UploaderClass or a block to define it."
37
39
  end
38
40
 
41
+ uploader_klass = Class.new(Saviour::BaseUploader, &block) if block
42
+
43
+ self.uploader_classes[attach_as] = uploader_klass
44
+
39
45
  mod = Module.new do
40
46
  define_method(attach_as) do
41
47
  instance_variable_get("@__uploader_#{attach_as}") || begin
42
- uploader_klass = Class.new(Saviour::BaseUploader, &block) if block
43
48
  layer = persistence_klass.new(self)
44
49
  new_file = ::Saviour::File.new(uploader_klass, self, attach_as, layer.read(attach_as))
45
50
 
@@ -79,6 +84,22 @@ module Saviour
79
84
  end
80
85
  end
81
86
 
87
+ define_method(:changed) do
88
+ if ActiveRecord::VERSION::MAJOR == 6 && send("#{attach_as}_changed?")
89
+ super() + [attach_as.to_s]
90
+ else
91
+ super()
92
+ end
93
+ end
94
+
95
+ define_method(:changed?) do
96
+ if ActiveRecord::VERSION::MAJOR == 6
97
+ send("#{attach_as}_changed?") || super()
98
+ else
99
+ super()
100
+ end
101
+ end
102
+
82
103
  define_method("#{attach_as}_change") do
83
104
  [send("#{attach_as}_was"), send(attach_as)]
84
105
  end
@@ -1,5 +1,7 @@
1
1
  module Saviour
2
2
  class LifeCycle
3
+ SHOULD_USE_INTERLOCK = defined?(Rails)
4
+
3
5
  class FileCreator
4
6
  def initialize(current_path, file, column, connection)
5
7
  @file = file
@@ -49,7 +51,7 @@ module Saviour
49
51
  end
50
52
 
51
53
  @new_path = @file.write(
52
- before_write: ->(path) { dup_file.call if @current_path == path }
54
+ before_write: ->(path) { dup_file.call if @current_path == path }
53
55
  )
54
56
 
55
57
  return unless @new_path
@@ -84,26 +86,19 @@ module Saviour
84
86
 
85
87
  def delete!
86
88
  DbHelpers.run_after_commit do
87
- pool = Concurrent::FixedThreadPool.new(Saviour::Config.concurrent_workers)
89
+ pool = Concurrent::Throttle.new Saviour::Config.concurrent_workers
88
90
 
89
91
  futures = attached_files.map do |column|
90
- Concurrent::Future.execute(executor: pool) {
91
- file = @model.send(column)
92
+ pool.future(@model.send(column)) do |file|
92
93
  path = file.persisted_path
93
94
  file.uploader.storage.delete(path) if path
94
95
  file.delete
95
- }
96
+ end
96
97
  end
97
98
 
98
99
  ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
99
- futures.each do |future|
100
- future.value
101
- raise(future.reason) if future.rejected?
102
- end
100
+ futures.each(&:value!)
103
101
  end
104
-
105
- pool.shutdown
106
- pool.wait_for_termination
107
102
  end
108
103
  end
109
104
 
@@ -124,34 +119,32 @@ module Saviour
124
119
  next unless @model.send(column).changed?
125
120
 
126
121
  klass.new(
127
- persistence_layer.read(column),
128
- @model.send(column),
129
- column,
130
- ActiveRecord::Base.connection
122
+ persistence_layer.read(column),
123
+ @model.send(column),
124
+ column,
125
+ ActiveRecord::Base.connection
131
126
  )
132
127
  end.compact
133
128
 
134
- pool = Concurrent::FixedThreadPool.new(Saviour::Config.concurrent_workers)
129
+ pool = Concurrent::Throttle.new Saviour::Config.concurrent_workers
130
+
135
131
  futures = uploaders.map { |uploader|
136
- Concurrent::Future.execute(executor: pool) {
137
- if defined?(Rails)
138
- Rails.application.executor.wrap { uploader.upload }
132
+ pool.future(uploader) { |given_uploader|
133
+ if SHOULD_USE_INTERLOCK
134
+ Rails.application.executor.wrap { given_uploader.upload }
139
135
  else
140
- uploader.upload
136
+ given_uploader.upload
141
137
  end
142
138
  }
143
139
  }
144
140
 
145
- result = ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
146
- futures.map do |x|
147
- x.value.tap do
148
- raise(x.reason) if x.rejected?
149
- end
150
- end.compact
151
- end
141
+ work = -> { futures.map(&:value!).compact }
152
142
 
153
- pool.shutdown
154
- pool.wait_for_termination
143
+ result = if SHOULD_USE_INTERLOCK
144
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads(&work)
145
+ else
146
+ work.call
147
+ end
155
148
 
156
149
  attrs = result.to_h
157
150
 
@@ -1,24 +1,24 @@
1
1
  module Saviour
2
2
  class ReadOnlyFile
3
- attr_reader :persisted_path
3
+ attr_reader :persisted_path, :storage
4
4
 
5
- def initialize(persisted_path, uploader_klass)
5
+ def initialize(persisted_path, storage)
6
6
  @persisted_path = persisted_path
7
- @uploader_klass = uploader_klass
7
+ @storage = storage
8
8
  end
9
9
 
10
10
  def exists?
11
- persisted? && Config.storage.exists?(@persisted_path)
11
+ persisted? && @storage.exists?(@persisted_path)
12
12
  end
13
13
 
14
14
  def read
15
15
  return nil unless persisted?
16
- @uploader_klass.storage.read(@persisted_path)
16
+ @storage.read(@persisted_path)
17
17
  end
18
18
 
19
19
  def public_url
20
20
  return nil unless persisted?
21
- @uploader_klass.storage.public_url(@persisted_path)
21
+ @storage.public_url(@persisted_path)
22
22
  end
23
23
  alias_method :url, :public_url
24
24
 
@@ -11,6 +11,7 @@ module Saviour
11
11
  def initialize(conf = {})
12
12
  @bucket = conf.delete(:bucket)
13
13
  @public_url_prefix = conf.delete(:public_url_prefix)
14
+ @extra_aws_client_options = conf.delete(:aws_client_opts)
14
15
  @conf = conf
15
16
  @create_options = conf.delete(:create_options) { {} }
16
17
  conf.fetch(:aws_access_key_id) { raise(ArgumentError, "aws_access_key_id is required") }
@@ -119,9 +120,11 @@ module Saviour
119
120
 
120
121
  def client
121
122
  @client ||= Aws::S3::Client.new(
122
- access_key_id: @conf[:aws_access_key_id],
123
- secret_access_key: @conf[:aws_secret_access_key],
124
- region: @region
123
+ {
124
+ access_key_id: @conf[:aws_access_key_id],
125
+ secret_access_key: @conf[:aws_secret_access_key],
126
+ region: @region
127
+ }.merge(@extra_aws_client_options || {})
125
128
  )
126
129
  end
127
130
  end
@@ -58,7 +58,7 @@ module Saviour
58
58
  result = run_method_or_block(method_or_block, opts, file)
59
59
 
60
60
  self.file = result[0]
61
- file.reopen(file.path)
61
+ file.reopen(file.path, "r+")
62
62
 
63
63
  self.filename = result[1]
64
64
  end
@@ -1,3 +1,3 @@
1
1
  module Saviour
2
- VERSION = "0.6.2"
2
+ VERSION = "0.6.7"
3
3
  end
@@ -13,17 +13,17 @@ Gem::Specification.new do |spec|
13
13
  spec.files = `git ls-files`.split($/)
14
14
  spec.require_paths = ["lib"]
15
15
 
16
- spec.required_ruby_version = ">= 2.2.0"
16
+ spec.required_ruby_version = ">= 2.5.0"
17
17
 
18
18
  spec.add_dependency "activerecord", ">= 5.1"
19
19
  spec.add_dependency "activesupport", ">= 5.1"
20
20
  spec.add_dependency "concurrent-ruby", ">= 1.0.5"
21
+ spec.add_dependency "concurrent-ruby-edge", ">= 0.6.0"
21
22
 
22
23
  spec.add_development_dependency "bundler"
23
24
  spec.add_development_dependency "rspec"
24
25
  spec.add_development_dependency "rake"
25
26
  spec.add_development_dependency "sqlite3"
26
- spec.add_development_dependency "appraisal"
27
27
  spec.add_development_dependency "aws-sdk-s3"
28
28
  spec.add_development_dependency "mime-types"
29
29
  spec.add_development_dependency "get_process_mem"
@@ -52,7 +52,7 @@ describe "concurrency on operations" do
52
52
 
53
53
  Saviour::Config.concurrent_workers = 4
54
54
 
55
- a.update_attributes! file: Saviour::StringSource.new("contents", "file.txt"),
55
+ a.update! file: Saviour::StringSource.new("contents", "file.txt"),
56
56
  file_thumb: Saviour::StringSource.new("contents", "file_2.txt"),
57
57
  file_thumb_2: Saviour::StringSource.new("contents", "file_3.txt"),
58
58
  file_thumb_3: Saviour::StringSource.new("contents", "file_4.txt")
@@ -66,7 +66,7 @@ describe "concurrency on operations" do
66
66
 
67
67
  Saviour::Config.concurrent_workers = 1
68
68
 
69
- a.update_attributes! file: Saviour::StringSource.new("contents", "file.txt"),
69
+ a.update! file: Saviour::StringSource.new("contents", "file.txt"),
70
70
  file_thumb: Saviour::StringSource.new("contents", "file_2.txt"),
71
71
  file_thumb_2: Saviour::StringSource.new("contents", "file_3.txt"),
72
72
  file_thumb_3: Saviour::StringSource.new("contents", "file_4.txt")
@@ -80,7 +80,7 @@ describe "concurrency on operations" do
80
80
 
81
81
  Saviour::Config.concurrent_workers = 2
82
82
 
83
- a.update_attributes! file: Saviour::StringSource.new("contents", "file.txt"),
83
+ a.update! file: Saviour::StringSource.new("contents", "file.txt"),
84
84
  file_thumb: Saviour::StringSource.new("contents", "file_2.txt"),
85
85
  file_thumb_2: Saviour::StringSource.new("contents", "file_3.txt"),
86
86
  file_thumb_3: Saviour::StringSource.new("contents", "file_4.txt")
@@ -19,14 +19,14 @@ describe "CRUD" do
19
19
  it do
20
20
  with_test_file("example.xml") do |example|
21
21
  a = klass.create!
22
- expect(a.update_attributes(file: example)).to be_truthy
22
+ expect(a.update(file: example)).to be_truthy
23
23
  end
24
24
  end
25
25
 
26
26
  it do
27
27
  with_test_file("example.xml") do |example|
28
28
  a = klass.create!
29
- a.update_attributes(file: example)
29
+ a.update(file: example)
30
30
 
31
31
  expect(Saviour::Config.storage.exists?(a[:file])).to be_truthy
32
32
  end
@@ -35,7 +35,7 @@ describe "CRUD" do
35
35
  it do
36
36
  with_test_file("example.xml") do |example, real_filename|
37
37
  a = klass.create!
38
- a.update_attributes(file: example)
38
+ a.update(file: example)
39
39
  expect(a[:file]).to eq "/store/dir/#{real_filename}"
40
40
  end
41
41
  end
@@ -43,7 +43,7 @@ describe "CRUD" do
43
43
  it do
44
44
  with_test_file("example.xml") do |example|
45
45
  a = klass.create!
46
- a.update_attributes(file: example)
46
+ a.update(file: example)
47
47
 
48
48
  example.rewind
49
49
  expect(a.file.read).to eq example.read
@@ -53,7 +53,7 @@ describe "CRUD" do
53
53
  it do
54
54
  with_test_file("example.xml") do |example|
55
55
  a = klass.create!
56
- a.update_attributes(file: example)
56
+ a.update(file: example)
57
57
 
58
58
  expect(a.file.exists?).to be_truthy
59
59
  end
@@ -62,7 +62,7 @@ describe "CRUD" do
62
62
  it do
63
63
  with_test_file("example.xml") do |example, real_filename|
64
64
  a = klass.create!
65
- a.update_attributes(file: example)
65
+ a.update(file: example)
66
66
 
67
67
  expect(a.file.filename).to eq real_filename
68
68
  end
@@ -71,7 +71,7 @@ describe "CRUD" do
71
71
  it do
72
72
  with_test_file("example.xml") do |example, real_filename|
73
73
  a = klass.create!
74
- a.update_attributes(file: example)
74
+ a.update(file: example)
75
75
 
76
76
  expect(a.file.url).to eq "http://domain.com/store/dir/#{real_filename}"
77
77
  expect(a.file.public_url).to eq a.file.url
@@ -137,7 +137,7 @@ describe "CRUD" do
137
137
  it do
138
138
  with_test_file("example.xml") do |example|
139
139
  a = klass.create!
140
- a.update_attributes(file: example)
140
+ a.update(file: example)
141
141
  expect(a.file.exists?).to be_truthy
142
142
  expect(a.destroy).to be_truthy
143
143
 
@@ -150,13 +150,13 @@ describe "CRUD" do
150
150
  it do
151
151
  with_test_file("example.xml") do |example|
152
152
  a = klass.create!
153
- a.update_attributes(file: example)
153
+ a.update(file: example)
154
154
 
155
155
  expect(Saviour::Config.storage.exists?(a[:file])).to be_truthy
156
156
  previous_location = a[:file]
157
157
 
158
158
  with_test_file("camaloon.jpg") do |example_2|
159
- a.update_attributes(file: example_2)
159
+ a.update(file: example_2)
160
160
  expect(Saviour::Config.storage.exists?(a[:file])).to be_truthy
161
161
 
162
162
  expect(Saviour::Config.storage.exists?(previous_location)).to be_falsey
@@ -167,7 +167,7 @@ describe "CRUD" do
167
167
  it "does allow to update the same file to another contents in the same path" do
168
168
  a = klass.create! file: Saviour::StringSource.new("contents", "file.txt")
169
169
 
170
- a.update_attributes! file: Saviour::StringSource.new("foo", "file.txt")
170
+ a.update! file: Saviour::StringSource.new("foo", "file.txt")
171
171
  expect(Saviour::Config.storage.read(a[:file])).to eq "foo"
172
172
  end
173
173
 
@@ -175,7 +175,7 @@ describe "CRUD" do
175
175
  a = klass.create!
176
176
 
177
177
  expect_to_yield_queries(count: 1) do
178
- a.update_attributes! file: Saviour::StringSource.new("foo", "file.txt")
178
+ a.update! file: Saviour::StringSource.new("foo", "file.txt")
179
179
  end
180
180
  end
181
181
 
@@ -183,7 +183,7 @@ describe "CRUD" do
183
183
  a = klass.create!
184
184
 
185
185
  expect_to_yield_queries(count: 2) do
186
- a.update_attributes! name: "Text",
186
+ a.update! name: "Text",
187
187
  file: Saviour::StringSource.new("foo", "file.txt")
188
188
  end
189
189
  end
@@ -192,7 +192,7 @@ describe "CRUD" do
192
192
  it "touches updated_at if the model has it" do
193
193
  time = Time.now - 4.years
194
194
  a = klass.create! updated_at: time
195
- a.update_attributes! file: Saviour::StringSource.new("foo", "file.txt")
195
+ a.update! file: Saviour::StringSource.new("foo", "file.txt")
196
196
 
197
197
  expect(a.updated_at).to be > time + 2.years
198
198
  end
@@ -207,7 +207,7 @@ describe "CRUD" do
207
207
  it "works with models that do not have updated_at" do
208
208
  a = klass.create!
209
209
  expect(a).not_to respond_to(:updated_at)
210
- a.update_attributes! file: Saviour::StringSource.new("foo", "file.txt")
210
+ a.update! file: Saviour::StringSource.new("foo", "file.txt")
211
211
  expect(a.file.read).to eq "foo"
212
212
  end
213
213
  end
@@ -226,7 +226,7 @@ describe "CRUD" do
226
226
 
227
227
  expected_query = %Q{UPDATE "tests" SET "file" = '/store/dir/file.txt', "file_thumb" = '/store/dir/file.txt'}
228
228
  expect_to_yield_queries(count: 1, including: [expected_query]) do
229
- a.update_attributes!(
229
+ a.update!(
230
230
  file: Saviour::StringSource.new("foo", "file.txt"),
231
231
  file_thumb: Saviour::StringSource.new("foo", "file.txt")
232
232
  )
@@ -14,7 +14,7 @@ describe "dirty model" do
14
14
 
15
15
  with_test_file("example.xml") do |xml_file|
16
16
  with_test_file("camaloon.jpg") do |jpg_file|
17
- a.update_attributes! file: xml_file
17
+ a.update! file: xml_file
18
18
 
19
19
  expect(a.changed_attributes).to eq({})
20
20
 
@@ -23,7 +23,7 @@ describe "halt processor behavior" do
23
23
 
24
24
  expect(Saviour::Config.storage).to_not receive(:write)
25
25
 
26
- a.update_attributes! file: StringIO.new("contents")
26
+ a.update! file: StringIO.new("contents")
27
27
  expect(a.reload.read_attribute(:file)).to be_nil
28
28
  end
29
29
 
@@ -9,8 +9,6 @@ describe "memory usage" do
9
9
  a
10
10
  }
11
11
 
12
- CHUNK = ("A" * 1024).freeze
13
-
14
12
  let(:size_to_test) { 10 } # Test with 10Mb files
15
13
 
16
14
  def with_tempfile
@@ -18,7 +16,7 @@ describe "memory usage" do
18
16
 
19
17
  size_to_test.times do
20
18
  1024.times do
21
- f.write CHUNK
19
+ f.write SecureRandom.hex(512)
22
20
  end
23
21
  end
24
22
  f.flush
@@ -56,7 +54,7 @@ describe "memory usage" do
56
54
  with_no_gc do
57
55
  base_line = GetProcessMem.new.mb
58
56
 
59
- a.update_attributes! file: f
57
+ a.update! file: f
60
58
 
61
59
  # Expect memory usage to grow below 10% of the file size
62
60
  expect(GetProcessMem.new.mb - base_line).to be < size_to_test / 10
@@ -72,22 +70,22 @@ describe "memory usage" do
72
70
 
73
71
  process do |contents, filename|
74
72
  digest = Digest::MD5.hexdigest(contents)
73
+
75
74
  [contents, "#{digest}-#{filename}"]
76
75
  end
77
76
  }
78
77
  }
79
78
 
80
79
  it do
81
- a = base_klass.create!
82
-
83
80
  with_tempfile do |f|
84
81
  with_no_gc do
85
- base_line = GetProcessMem.new.mb
82
+ a = base_klass.create!
86
83
 
87
- a.update_attributes! file: f
84
+ base_line = GetProcessMem.new.mb
88
85
 
89
- # Expect memory usage to grow at least the size of the file
90
- expect(GetProcessMem.new.mb - base_line).to be > size_to_test
86
+ a.update! file: f
87
+ # Expect memory usage to grow at least half the file size
88
+ expect(GetProcessMem.new.mb - base_line).to be >= size_to_test / 2
91
89
  end
92
90
  end
93
91
  end
@@ -11,7 +11,7 @@ describe "persisted path" do
11
11
 
12
12
  with_test_file("example.xml") do |example|
13
13
  a = klass.create!
14
- expect(a.update_attributes(file: example)).to be_truthy
14
+ expect(a.update(file: example)).to be_truthy
15
15
  expect(Saviour::Config.storage.exists?(a[:file])).to be_truthy
16
16
  expect(File.dirname(a[:file])).to eq "/store/dir"
17
17
 
@@ -20,7 +20,7 @@ describe "persisted path" do
20
20
 
21
21
  with_test_file("camaloon.jpg") do |example_2|
22
22
  b = klass.create!
23
- expect(b.update_attributes(file: example_2)).to be_truthy
23
+ expect(b.update(file: example_2)).to be_truthy
24
24
 
25
25
  expect(Saviour::Config.storage.exists?(b[:file])).to be_truthy
26
26
  expect(Saviour::Config.storage.exists?(a[:file])).to be_truthy
@@ -88,7 +88,7 @@ describe "processor's API" do
88
88
  a = klass.create!
89
89
 
90
90
  expect {
91
- a.update_attributes! file: Saviour::StringSource.new("contents", "filename.txt")
91
+ a.update! file: Saviour::StringSource.new("contents", "filename.txt")
92
92
  }.to raise_error.with_message("custom problem!")
93
93
  end
94
94
 
@@ -0,0 +1,28 @@
1
+ require 'spec_helper'
2
+
3
+ describe "direct usage of ReadOnlyFile" 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(:klass) {
13
+ klass = Class.new(Test) { include Saviour::Model }
14
+ klass.attach_file :file, uploader
15
+ klass
16
+ }
17
+
18
+ it "can be created" do
19
+ a = klass.create! file: Saviour::StringSource.new("contents", "file.txt")
20
+
21
+ path = a[:file]
22
+
23
+ read_only_file = Saviour::ReadOnlyFile.new(path, klass.uploader_classes[:file].storage)
24
+
25
+ expect(read_only_file.read).to eq "contents"
26
+ expect(read_only_file.public_url).to eq "http://domain.com/store/dir/file.txt"
27
+ end
28
+ end
@@ -12,7 +12,7 @@ describe "reload" do
12
12
  b = klass.find(a.id)
13
13
 
14
14
  with_test_file("example.xml") do |example|
15
- a.update_attributes! file: example
15
+ a.update! file: example
16
16
  expect(a.file.exists?).to be_truthy
17
17
  expect(b.file.exists?).to be_falsey
18
18
 
@@ -116,7 +116,7 @@ describe "remove attachment" do
116
116
  expect(Saviour::Config.storage.exists?(path)).to be_truthy
117
117
  expect(Saviour::Config.storage.read(path)).to eq "Some contents"
118
118
 
119
- a.update_attributes!(file: Saviour::StringSource.new("Other contents", "filename.txt"))
119
+ a.update!(file: Saviour::StringSource.new("Other contents", "filename.txt"))
120
120
  expect(a.file.persisted?).to be_truthy
121
121
  expect(a.file.read).to eq "Other contents"
122
122
  expect(Saviour::Config.storage.exists?(path)).to be_truthy
@@ -14,7 +14,7 @@ describe "stash data on process" do
14
14
  end
15
15
 
16
16
  after_upload do |stash|
17
- model.update_attributes!(file_size: stash[:file_size])
17
+ model.update!(file_size: stash[:file_size])
18
18
  end
19
19
  }
20
20
 
@@ -23,7 +23,7 @@ describe "stash data on process" do
23
23
 
24
24
  a = klass.create!
25
25
 
26
- a.update_attributes! file: Saviour::StringSource.new("a" * 74, "file.txt")
26
+ a.update! file: Saviour::StringSource.new("a" * 74, "file.txt")
27
27
  expect(a.file_size).to eq 74
28
28
  end
29
29
 
@@ -38,7 +38,7 @@ describe "stash data on process" do
38
38
  end
39
39
 
40
40
  after_upload do |stash|
41
- model.update_attributes!(file_size: stash[:file_size])
41
+ model.update!(file_size: stash[:file_size])
42
42
  end
43
43
  }
44
44
 
@@ -61,7 +61,7 @@ describe "stash data on process" do
61
61
  end
62
62
 
63
63
  after_upload do |stash|
64
- model.update_attributes!("size_#{attached_as}" => stash[:size])
64
+ model.update!("size_#{attached_as}" => stash[:size])
65
65
  end
66
66
  }
67
67
 
@@ -74,7 +74,7 @@ describe "stash data on process" do
74
74
  # - 2 queries to update size
75
75
  # - 1 query to assign stored paths
76
76
  expect_to_yield_queries(count: 3) do
77
- a.update_attributes! file: Saviour::StringSource.new("a" * 74, "file.txt"),
77
+ a.update! file: Saviour::StringSource.new("a" * 74, "file.txt"),
78
78
  file_thumb: Saviour::StringSource.new("a" * 31, "file_2.txt")
79
79
  end
80
80
 
@@ -99,7 +99,7 @@ describe "stash data on process" do
99
99
  end
100
100
 
101
101
  after_upload do |stash|
102
- model.update_attributes!(stash[:model])
102
+ model.update!(stash[:model])
103
103
  end
104
104
  }
105
105
 
@@ -123,11 +123,11 @@ describe "stash data on process" do
123
123
  end
124
124
 
125
125
  after_upload do |stash|
126
- model.update_attributes!(file_size: stash[:file_size])
126
+ model.update!(file_size: stash[:file_size])
127
127
  end
128
128
 
129
129
  after_upload do |stash|
130
- model.update_attributes!(name: stash[:name])
130
+ model.update!(name: stash[:name])
131
131
  end
132
132
  }
133
133
 
@@ -0,0 +1,87 @@
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!(:another_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
+ custom_storage = another_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 custom_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
+
45
+ context do
46
+ it "allows for lambda storages" do
47
+ allow(Saviour::Config).to receive(:storage).and_return(-> { default_storage })
48
+
49
+ klass = Class.new(Test) { include Saviour::Model }
50
+
51
+ klass.attach_file(:file) do
52
+ store_dir { "/store/dir" }
53
+ end
54
+
55
+ a = klass.create!(file: Saviour::StringSource.new("content", "houhou.txt"))
56
+
57
+ expect(a.file.filename).to eq "houhou.txt"
58
+ expect(a.file.url).to eq 'http://domain.com/store/dir/houhou.txt'
59
+ end
60
+
61
+ it "allow to change storage on the fly" do
62
+ dynamic_storage = default_storage
63
+ allow(Saviour::Config).to receive(:storage).and_return(-> { dynamic_storage })
64
+
65
+ klass = Class.new(Test) { include Saviour::Model }
66
+
67
+ klass.attach_file(:file) do
68
+ store_dir { "/store/dir" }
69
+ end
70
+
71
+ a = klass.create!(
72
+ file: Saviour::StringSource.new("content", "houhou.txt"),
73
+ file_thumb: Saviour::StringSource.new("content", "custom_houhou.txt")
74
+ )
75
+
76
+ expect(a.file.filename).to eq "houhou.txt"
77
+ expect(a.file.url).to eq 'http://domain.com/store/dir/houhou.txt'
78
+
79
+ dynamic_storage = another_storage # Lambda will pick up the new storage.
80
+
81
+ a = klass.create!(file: Saviour::StringSource.new("content", "houhou.txt"))
82
+
83
+ expect(a.file.filename).to eq "houhou.txt"
84
+ expect(a.file.url).to eq 'http://custom-domain.com/store/dir/houhou.txt'
85
+ end
86
+ end
87
+ end
@@ -87,7 +87,7 @@ describe "transactional behavior" do
87
87
 
88
88
  with_test_file("camaloon.jpg") do |file2|
89
89
  ActiveRecord::Base.transaction do
90
- a.update_attributes! file: file2
90
+ a.update! file: file2
91
91
 
92
92
  expect(Saviour::Config.storage.exists?(path1)).to be_truthy
93
93
  expect(Saviour::Config.storage.exists?(a[:file])).to be_truthy
@@ -107,7 +107,7 @@ describe "transactional behavior" do
107
107
 
108
108
  with_test_file("camaloon.jpg") do |file2|
109
109
  ActiveRecord::Base.transaction do
110
- a.update_attributes! file: file2
110
+ a.update! file: file2
111
111
  path2 = a[:file]
112
112
 
113
113
  expect(Saviour::Config.storage.exists?(path1)).to be_truthy
@@ -135,7 +135,7 @@ describe "transactional behavior" do
135
135
  expect(Saviour::Config.storage.read(a[:file])).to eq "original content"
136
136
 
137
137
  ActiveRecord::Base.transaction do
138
- a.update_attributes! file: Saviour::StringSource.new("new content", "file.txt")
138
+ a.update! file: Saviour::StringSource.new("new content", "file.txt")
139
139
  expect(Saviour::Config.storage.read(a[:file])).to eq "new content"
140
140
  raise ActiveRecord::Rollback
141
141
  end
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.6.2
4
+ version: 0.6.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Roger Campos
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-06-16 00:00:00.000000000 Z
11
+ date: 2020-06-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -53,21 +53,21 @@ dependencies:
53
53
  - !ruby/object:Gem::Version
54
54
  version: 1.0.5
55
55
  - !ruby/object:Gem::Dependency
56
- name: bundler
56
+ name: concurrent-ruby-edge
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - ">="
60
60
  - !ruby/object:Gem::Version
61
- version: '0'
62
- type: :development
61
+ version: 0.6.0
62
+ type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - ">="
67
67
  - !ruby/object:Gem::Version
68
- version: '0'
68
+ version: 0.6.0
69
69
  - !ruby/object:Gem::Dependency
70
- name: rspec
70
+ name: bundler
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - ">="
@@ -81,7 +81,7 @@ dependencies:
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
83
  - !ruby/object:Gem::Dependency
84
- name: rake
84
+ name: rspec
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
87
  - - ">="
@@ -95,7 +95,7 @@ dependencies:
95
95
  - !ruby/object:Gem::Version
96
96
  version: '0'
97
97
  - !ruby/object:Gem::Dependency
98
- name: sqlite3
98
+ name: rake
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
101
  - - ">="
@@ -109,7 +109,7 @@ dependencies:
109
109
  - !ruby/object:Gem::Version
110
110
  version: '0'
111
111
  - !ruby/object:Gem::Dependency
112
- name: appraisal
112
+ name: sqlite3
113
113
  requirement: !ruby/object:Gem::Requirement
114
114
  requirements:
115
115
  - - ">="
@@ -173,13 +173,13 @@ extra_rdoc_files: []
173
173
  files:
174
174
  - ".gitignore"
175
175
  - ".travis.yml"
176
- - Appraisals
177
176
  - Gemfile
178
177
  - LICENSE.txt
179
178
  - README.md
180
179
  - Rakefile
181
180
  - gemfiles/5.1.gemfile
182
181
  - gemfiles/5.2.gemfile
182
+ - gemfiles/6.0.gemfile
183
183
  - lib/saviour.rb
184
184
  - lib/saviour/base_uploader.rb
185
185
  - lib/saviour/config.rb
@@ -211,12 +211,13 @@ files:
211
211
  - spec/feature/original_assigned_file_is_not_modified_spec.rb
212
212
  - spec/feature/persisted_path_spec.rb
213
213
  - spec/feature/processors_api_spec.rb
214
+ - spec/feature/read_only_file_spec.rb
214
215
  - spec/feature/reload_model_spec.rb
215
216
  - spec/feature/remove_attachment_spec.rb
216
217
  - spec/feature/reopens_file_at_every_process_spec.rb
217
218
  - spec/feature/rewind_source_before_read_spec.rb
218
219
  - spec/feature/stash_spec.rb
219
- - spec/feature/storage_overriding_spec.rb
220
+ - spec/feature/storages_spec.rb
220
221
  - spec/feature/transactional_behavior_spec.rb
221
222
  - spec/feature/uploader_declaration_spec.rb
222
223
  - spec/feature/validations_spec.rb
@@ -247,15 +248,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
247
248
  requirements:
248
249
  - - ">="
249
250
  - !ruby/object:Gem::Version
250
- version: 2.2.0
251
+ version: 2.5.0
251
252
  required_rubygems_version: !ruby/object:Gem::Requirement
252
253
  requirements:
253
254
  - - ">="
254
255
  - !ruby/object:Gem::Version
255
256
  version: '0'
256
257
  requirements: []
257
- rubyforge_project:
258
- rubygems_version: 2.7.6
258
+ rubygems_version: 3.0.3
259
259
  signing_key:
260
260
  specification_version: 4
261
261
  summary: File storage handler following active record model lifecycle
data/Appraisals DELETED
@@ -1,19 +0,0 @@
1
- appraise "4.0" do
2
- gem "activesupport", "~> 4.0.0"
3
- gem "activerecord", "~> 4.0.0"
4
- end
5
-
6
- appraise "4.1" do
7
- gem "activesupport", "~> 4.1.0"
8
- gem "activerecord", "~> 4.1.0"
9
- end
10
-
11
- appraise "4.2" do
12
- gem "activesupport", "~> 4.2.0"
13
- gem "activerecord", "~> 4.2.0"
14
- end
15
-
16
- appraise "5.0" do
17
- gem "activesupport", "~> 5.0.0"
18
- gem "activerecord", "~> 5.0.0"
19
- end
@@ -1,44 +0,0 @@
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