saviour 0.4.14 → 0.5.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: 1dae3217796105e752f538dcbb2b5eb38fdd55c15e3c428f8cc38e91e0636fec
4
- data.tar.gz: 7a44ec336be6447a20b72855e90fa72fb8adfedb4c16e83187f7c38e8cc742e2
3
+ metadata.gz: '04095eaf0410d74e00e023a53cc473e8b5f2898f49673a329127d446a87f0080'
4
+ data.tar.gz: a3cba3ccb7d59f0f0e219475f003f21f102205132d3bc2cfb9ccc2ca6b0b5c54
5
5
  SHA512:
6
- metadata.gz: 0017f5ba9358177d91dfb6b4509bbe68a25fc9fb021d8fce7d8f349eaf5cc53c06754e3f0d78a0a96d2f0c77952b7a14d8cf913cf971a43b8ce9a86228a411c1
7
- data.tar.gz: a01dc0d1e49bc3aca7b468e51b3916edd81025268183faca3744a19ce55a0a0ba4b3a78c753b9c3c89cec20b384376c281f9942457c884071d99050bc8a786aa
6
+ metadata.gz: 83428891e963c4a96bffe2865380bbd586af945029e23d50c86c8b6aacac99f2ee7b928d89383bba0e1845175396e0b6f0c972d18e2df03390579cbf197518f0
7
+ data.tar.gz: 3ba8709c6d680f74eb933c3336b3954cd284b8bd52445db2cab72e2c58e1534152667a482b5cab2b5c8cf49a95a63e22f2a0a33e4cebf1becd03f3e1dddc65a6
@@ -5,6 +5,7 @@ module Saviour
5
5
  class BaseUploader
6
6
  def initialize(opts = {})
7
7
  @data = opts.fetch(:data, {})
8
+ @stash = {}
8
9
  end
9
10
 
10
11
  def method_missing(name, *args, &block)
@@ -51,6 +52,14 @@ module Saviour
51
52
  throw(:halt_process)
52
53
  end
53
54
 
55
+ def stash(hash)
56
+ @stash.merge!(hash)
57
+ end
58
+
59
+ def stashed
60
+ @stash
61
+ end
62
+
54
63
  def store_dir
55
64
  @store_dir ||= Uploader::StoreDirExtractor.new(self).store_dir
56
65
  end
@@ -76,10 +85,17 @@ module Saviour
76
85
  process(name, opts, :file, &block)
77
86
  end
78
87
 
79
-
80
88
  def store_dir(name = nil, &block)
81
89
  store_dirs.push(name || block)
82
90
  end
91
+
92
+ def after_upload(&block)
93
+ after_upload_hooks.push(block)
94
+ end
95
+
96
+ def after_upload_hooks
97
+ @after_upload ||= []
98
+ end
83
99
  end
84
100
  end
85
101
  end
@@ -39,6 +39,14 @@ module Saviour
39
39
  end
40
40
  end
41
41
  end
42
+
43
+ def concurrent_workers
44
+ @concurrent_workers || 4
45
+ end
46
+
47
+ def concurrent_workers=(x)
48
+ @concurrent_workers = x
49
+ end
42
50
  end
43
51
  end
44
52
  end
@@ -47,22 +47,22 @@ module Saviour
47
47
 
48
48
  class << self
49
49
 
50
- def run_after_commit(&block)
51
- unless ActiveRecord::Base.connection.current_transaction.open?
50
+ def run_after_commit(connection = ActiveRecord::Base.connection, &block)
51
+ unless connection.current_transaction.open?
52
52
  raise NotInTransaction, 'Trying to use `run_after_commit` but no transaction is currently open.'
53
53
  end
54
54
 
55
55
  dummy = CommitDummy.new(block)
56
- ActiveRecord::Base.connection.add_transaction_record(dummy)
56
+ connection.add_transaction_record(dummy)
57
57
  end
58
58
 
59
- def run_after_rollback(&block)
60
- unless ActiveRecord::Base.connection.current_transaction.open?
59
+ def run_after_rollback(connection = ActiveRecord::Base.connection, &block)
60
+ unless connection.current_transaction.open?
61
61
  raise NotInTransaction, 'Trying to use `run_after_commit` but no transaction is currently open.'
62
62
  end
63
63
 
64
64
  dummy = RollbackDummy.new(block)
65
- ActiveRecord::Base.connection.add_transaction_record(dummy)
65
+ connection.add_transaction_record(dummy)
66
66
  end
67
67
  end
68
68
  end
data/lib/saviour/file.rb CHANGED
@@ -176,9 +176,6 @@ module Saviour
176
176
  !@source && !persisted?
177
177
  end
178
178
 
179
-
180
- private
181
-
182
179
  def uploader
183
180
  @uploader ||= @uploader_klass.new(data: { model: @model, attached_as: @attached_as })
184
181
  end
@@ -1,5 +1,80 @@
1
1
  module Saviour
2
2
  class LifeCycle
3
+ class FileCreator
4
+ def initialize(current_path, file, column, connection)
5
+ @file = file
6
+ @column = column
7
+ @current_path = current_path
8
+ @connection = connection
9
+ end
10
+
11
+ def upload
12
+ @new_path = @file.write
13
+
14
+ return unless @new_path
15
+
16
+ DbHelpers.run_after_rollback(@connection) do
17
+ Config.storage.delete(@new_path)
18
+ end
19
+
20
+ [@column, @new_path]
21
+ end
22
+
23
+ def uploader
24
+ @file.uploader
25
+ end
26
+ end
27
+
28
+ class FileUpdater
29
+ def initialize(current_path, file, column, connection)
30
+ @file = file
31
+ @column = column
32
+ @current_path = current_path
33
+ @connection = connection
34
+ end
35
+
36
+ def upload
37
+ dup_temp_path = SecureRandom.hex
38
+
39
+ dup_file = proc do
40
+ Config.storage.cp @current_path, dup_temp_path
41
+
42
+ DbHelpers.run_after_commit(@connection) do
43
+ Config.storage.delete dup_temp_path
44
+ end
45
+
46
+ DbHelpers.run_after_rollback(@connection) do
47
+ Config.storage.mv dup_temp_path, @current_path
48
+ end
49
+ end
50
+
51
+ @new_path = @file.write(
52
+ before_write: ->(path) { dup_file.call if @current_path == path }
53
+ )
54
+
55
+ return unless @new_path
56
+
57
+ if @current_path && @current_path != @new_path
58
+ DbHelpers.run_after_commit(@connection) do
59
+ Config.storage.delete(@current_path)
60
+ end
61
+ end
62
+
63
+ # Delete the newly uploaded file only if it's an update in a different path
64
+ if @current_path.nil? || @current_path != @new_path
65
+ DbHelpers.run_after_rollback(@connection) do
66
+ Config.storage.delete(@new_path)
67
+ end
68
+ end
69
+
70
+ [@column, @new_path]
71
+ end
72
+
73
+ def uploader
74
+ @file.uploader
75
+ end
76
+ end
77
+
3
78
  def initialize(model, persistence_klass)
4
79
  raise ConfigurationError, "Please provide an object compatible with Saviour." unless model.class.respond_to?(:attached_files)
5
80
 
@@ -16,71 +91,50 @@ module Saviour
16
91
  end
17
92
 
18
93
  def create!
19
- attached_files.each do |column|
20
- next unless @model.send(column).changed?
21
-
22
- persistence_layer = @persistence_klass.new(@model)
23
- new_path = @model.send(column).write
24
-
25
- if new_path
26
- persistence_layer.write(column, new_path)
27
-
28
- DbHelpers.run_after_rollback do
29
- Config.storage.delete(new_path)
30
- end
31
- end
32
- end
94
+ process_upload(FileCreator)
33
95
  end
34
96
 
35
97
  def update!
36
- attached_files.each do |column|
37
- next unless @model.send(column).changed?
38
-
39
- update_file(column)
40
- end
98
+ process_upload(FileUpdater)
41
99
  end
42
100
 
43
-
44
101
  private
45
102
 
46
-
47
- def update_file(column)
103
+ def process_upload(klass)
48
104
  persistence_layer = @persistence_klass.new(@model)
49
- current_path = persistence_layer.read(column)
50
- dup_temp_path = SecureRandom.hex
51
105
 
52
- dup_file = proc do
53
- Config.storage.cp current_path, dup_temp_path
54
-
55
- DbHelpers.run_after_commit do
56
- Config.storage.delete dup_temp_path
57
- end
106
+ uploaders = attached_files.map do |column|
107
+ next unless @model.send(column).changed?
58
108
 
59
- DbHelpers.run_after_rollback do
60
- Config.storage.mv dup_temp_path, current_path
61
- end
62
- end
109
+ klass.new(
110
+ persistence_layer.read(column),
111
+ @model.send(column),
112
+ column,
113
+ ActiveRecord::Base.connection
114
+ )
115
+ end.compact
63
116
 
64
- new_path = @model.send(column).write(
65
- before_write: ->(path) { dup_file.call if current_path == path }
66
- )
117
+ pool = Concurrent::FixedThreadPool.new(Saviour::Config.concurrent_workers)
118
+ futures = uploaders.map { |uploader| Concurrent::Future.execute(executor: pool) { uploader.upload } }
67
119
 
68
- if new_path
69
- persistence_layer.write(column, new_path)
120
+ pool.shutdown
121
+ pool.wait_for_termination
70
122
 
71
- if current_path && current_path != new_path
72
- DbHelpers.run_after_commit do
73
- Config.storage.delete(current_path)
74
- end
123
+ result = futures.map do |x|
124
+ x.value.tap do
125
+ raise(x.reason) if x.rejected?
75
126
  end
127
+ end.compact
76
128
 
77
- # Delete the newly uploaded file only if it's an update in a different path
78
- if current_path.nil? || current_path != new_path
79
- DbHelpers.run_after_rollback do
80
- Config.storage.delete(new_path)
81
- end
129
+ attrs = result.to_h
130
+
131
+ uploaders.map(&:uploader).select { |x| x.class.after_upload_hooks.any? }.each do |uploader|
132
+ uploader.class.after_upload_hooks.each do |hook|
133
+ uploader.instance_exec(uploader.stashed, &hook)
82
134
  end
83
135
  end
136
+
137
+ persistence_layer.write_attrs(attrs) if attrs.length > 0
84
138
  end
85
139
 
86
140
  def attached_files
@@ -12,6 +12,10 @@ module Saviour
12
12
  @model.update_columns(attr => value)
13
13
  end
14
14
 
15
+ def write_attrs(attributes)
16
+ @model.update_columns(attributes)
17
+ end
18
+
15
19
  def persisted?
16
20
  @model.persisted? || @model.destroyed?
17
21
  end
@@ -1,3 +1,3 @@
1
1
  module Saviour
2
- VERSION = "0.4.14"
2
+ VERSION = "0.5.0"
3
3
  end
data/lib/saviour.rb CHANGED
@@ -16,6 +16,7 @@ require 'saviour/db_helpers'
16
16
 
17
17
  require 'tempfile'
18
18
  require 'fileutils'
19
+ require 'concurrent/future'
19
20
 
20
21
  module Saviour
21
22
  NoActiveRecordDetected = Class.new(StandardError)
data/saviour.gemspec CHANGED
@@ -17,6 +17,7 @@ Gem::Specification.new do |spec|
17
17
 
18
18
  spec.add_dependency "activerecord", ">= 5.0"
19
19
  spec.add_dependency "activesupport", ">= 5.0"
20
+ spec.add_dependency "concurrent-ruby", ">= 1.0.5"
20
21
 
21
22
  spec.add_development_dependency "bundler"
22
23
  spec.add_development_dependency "rspec"
@@ -0,0 +1,118 @@
1
+ require 'spec_helper'
2
+
3
+ describe "concurrent processors" 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
+ WAIT_TIME = 0.5
7
+
8
+ let(:uploader) {
9
+ Class.new(Saviour::BaseUploader) {
10
+ store_dir { "/store/dir" }
11
+
12
+ process_with_file do |file, filename|
13
+ sleep WAIT_TIME
14
+
15
+ [file, filename]
16
+ end
17
+ }
18
+ }
19
+
20
+ let(:klass) {
21
+ klass = Class.new(Test) { include Saviour::Model }
22
+ klass.attach_file :file, uploader
23
+ klass.attach_file :file_thumb, uploader
24
+ klass.attach_file :file_thumb_2, uploader
25
+ klass.attach_file :file_thumb_3, uploader
26
+ klass
27
+ }
28
+
29
+ context 'on update' do
30
+ it 'works concurrently with 4 workers' do
31
+ a = klass.create!
32
+
33
+ Saviour::Config.concurrent_workers = 4
34
+
35
+ t0 = Time.now
36
+ a.update_attributes! file: Saviour::StringSource.new("contents", "file.txt"),
37
+ file_thumb: Saviour::StringSource.new("contents", "file_2.txt"),
38
+ file_thumb_2: Saviour::StringSource.new("contents", "file_3.txt"),
39
+ file_thumb_3: Saviour::StringSource.new("contents", "file_4.txt")
40
+
41
+ diff = Time.now - t0
42
+ expect(diff).to be_within(0.05).of(WAIT_TIME)
43
+ expect(diff).to be < WAIT_TIME * 4
44
+ end
45
+
46
+ it 'works in serial with 1 worker' do
47
+ a = klass.create!
48
+
49
+ Saviour::Config.concurrent_workers = 1
50
+
51
+ t0 = Time.now
52
+ a.update_attributes! file: Saviour::StringSource.new("contents", "file.txt"),
53
+ file_thumb: Saviour::StringSource.new("contents", "file_2.txt"),
54
+ file_thumb_2: Saviour::StringSource.new("contents", "file_3.txt"),
55
+ file_thumb_3: Saviour::StringSource.new("contents", "file_4.txt")
56
+
57
+ expect(Time.now - t0).to be >= WAIT_TIME * 4
58
+ end
59
+
60
+ it 'concurrency can be adjusted' do
61
+ a = klass.create!
62
+
63
+ Saviour::Config.concurrent_workers = 2
64
+
65
+ t0 = Time.now
66
+ a.update_attributes! file: Saviour::StringSource.new("contents", "file.txt"),
67
+ file_thumb: Saviour::StringSource.new("contents", "file_2.txt"),
68
+ file_thumb_2: Saviour::StringSource.new("contents", "file_3.txt"),
69
+ file_thumb_3: Saviour::StringSource.new("contents", "file_4.txt")
70
+
71
+ diff = Time.now - t0
72
+ expect(diff).to be_within(0.05).of(WAIT_TIME * 2)
73
+ expect(diff).to be < WAIT_TIME * 4
74
+ end
75
+ end
76
+
77
+ context 'on create' do
78
+ it 'works concurrently with 4 workers' do
79
+ Saviour::Config.concurrent_workers = 4
80
+
81
+ t0 = Time.now
82
+ klass.create! file: Saviour::StringSource.new("contents", "file.txt"),
83
+ file_thumb: Saviour::StringSource.new("contents", "file_2.txt"),
84
+ file_thumb_2: Saviour::StringSource.new("contents", "file_3.txt"),
85
+ file_thumb_3: Saviour::StringSource.new("contents", "file_4.txt")
86
+
87
+ diff = Time.now - t0
88
+ expect(diff).to be_within(0.05).of(WAIT_TIME)
89
+ expect(diff).to be < WAIT_TIME * 4
90
+ end
91
+
92
+ it 'works in serial with 1 worker' do
93
+ Saviour::Config.concurrent_workers = 1
94
+
95
+ t0 = Time.now
96
+ klass.create! file: Saviour::StringSource.new("contents", "file.txt"),
97
+ file_thumb: Saviour::StringSource.new("contents", "file_2.txt"),
98
+ file_thumb_2: Saviour::StringSource.new("contents", "file_3.txt"),
99
+ file_thumb_3: Saviour::StringSource.new("contents", "file_4.txt")
100
+
101
+ expect(Time.now - t0).to be >= WAIT_TIME * 4
102
+ end
103
+
104
+ it 'concurrency can be adjusted' do
105
+ Saviour::Config.concurrent_workers = 2
106
+
107
+ t0 = Time.now
108
+ klass.create! file: Saviour::StringSource.new("contents", "file.txt"),
109
+ file_thumb: Saviour::StringSource.new("contents", "file_2.txt"),
110
+ file_thumb_2: Saviour::StringSource.new("contents", "file_3.txt"),
111
+ file_thumb_3: Saviour::StringSource.new("contents", "file_4.txt")
112
+
113
+ diff = Time.now - t0
114
+ expect(diff).to be_within(0.05).of(WAIT_TIME * 2)
115
+ expect(diff).to be < WAIT_TIME * 4
116
+ end
117
+ end
118
+ end
@@ -103,6 +103,26 @@ describe "saving a new file" do
103
103
  a.save
104
104
  end
105
105
  end
106
+
107
+ context do
108
+ let(:klass) {
109
+ a = Class.new(Test) { include Saviour::Model }
110
+ a.attach_file :file, uploader
111
+ a.attach_file :file_thumb, uploader
112
+ a
113
+ }
114
+
115
+ it "saves to db only once with multiple file attachments" do
116
+ # 1 create + 1 update with two attributes
117
+ expected_query = %Q{UPDATE "tests" SET "file" = '/store/dir/file.txt', "file_thumb" = '/store/dir/file.txt'}
118
+ expect_to_yield_queries(count: 2, including: [expected_query]) do
119
+ klass.create!(
120
+ file: Saviour::StringSource.new("foo", "file.txt"),
121
+ file_thumb: Saviour::StringSource.new("foo", "file.txt")
122
+ )
123
+ end
124
+ end
125
+ end
106
126
  end
107
127
 
108
128
  describe "deletion" do
@@ -142,6 +162,28 @@ describe "saving a new file" do
142
162
  a.update_attributes! file: Saviour::StringSource.new("foo", "file.txt")
143
163
  expect(Saviour::Config.storage.read(a[:file])).to eq "foo"
144
164
  end
165
+
166
+ context do
167
+ let(:klass) {
168
+ a = Class.new(Test) { include Saviour::Model }
169
+ a.attach_file :file, uploader
170
+ a.attach_file :file_thumb, uploader
171
+ a
172
+ }
173
+
174
+ it "saves to db only once with multiple file attachments" do
175
+ a = klass.create!
176
+
177
+ # 2 update's, first empty and then 1 with the two attributes
178
+ expected_query = %Q{UPDATE "tests" SET "file" = '/store/dir/file.txt', "file_thumb" = '/store/dir/file.txt'}
179
+ expect_to_yield_queries(count: 2, including: [expected_query]) do
180
+ a.update_attributes!(
181
+ file: Saviour::StringSource.new("foo", "file.txt"),
182
+ file_thumb: Saviour::StringSource.new("foo", "file.txt")
183
+ )
184
+ end
185
+ end
186
+ end
145
187
  end
146
188
 
147
189
  describe "dupping" do
@@ -72,4 +72,30 @@ describe "processor's API" do
72
72
  end
73
73
  end
74
74
  end
75
+
76
+ describe "errors raised from processors are propagated" do
77
+ let(:uploader) {
78
+ Class.new(Saviour::BaseUploader) do
79
+ store_dir { "/store/dir/#{model.id}" }
80
+
81
+ process do |contents, filename|
82
+ raise "custom problem!"
83
+ end
84
+ end
85
+ }
86
+
87
+ it "on update" do
88
+ a = klass.create!
89
+
90
+ expect {
91
+ a.update_attributes! file: Saviour::StringSource.new("contents", "filename.txt")
92
+ }.to raise_error.with_message("custom problem!")
93
+ end
94
+
95
+ it "on create" do
96
+ expect {
97
+ klass.create! file: Saviour::StringSource.new("contents", "filename.txt")
98
+ }.to raise_error.with_message("custom problem!")
99
+ end
100
+ end
75
101
  end
@@ -0,0 +1,86 @@
1
+ require 'spec_helper'
2
+
3
+ describe "stash data on process" 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 'stores data on after upload on update' do
7
+ uploader = Class.new(Saviour::BaseUploader) {
8
+ store_dir { "/store/dir" }
9
+
10
+ process_with_file do |file, filename|
11
+ stash(file_size: File.size(file.path))
12
+
13
+ [file, filename]
14
+ end
15
+
16
+ after_upload do |stash|
17
+ model.update_attributes!(file_size: stash[:file_size])
18
+ end
19
+ }
20
+
21
+ klass = Class.new(Test) { include Saviour::Model }
22
+ klass.attach_file :file, uploader
23
+
24
+ a = klass.create!
25
+
26
+ a.update_attributes! file: Saviour::StringSource.new("a" * 74, "file.txt")
27
+ expect(a.file_size).to eq 74
28
+ end
29
+
30
+ it 'stores data on after upload on create' do
31
+ uploader = Class.new(Saviour::BaseUploader) {
32
+ store_dir { "/store/dir" }
33
+
34
+ process_with_file do |file, filename|
35
+ stash(file_size: File.size(file.path))
36
+
37
+ [file, filename]
38
+ end
39
+
40
+ after_upload do |stash|
41
+ model.update_attributes!(file_size: stash[:file_size])
42
+ end
43
+ }
44
+
45
+ klass = Class.new(Test) { include Saviour::Model }
46
+ klass.attach_file :file, uploader
47
+
48
+ a = klass.create! file: Saviour::StringSource.new("a" * 74, "file.txt")
49
+
50
+ expect(a.file_size).to eq 74
51
+ end
52
+
53
+ it 'stashes are independent per uploader' do
54
+ uploader = Class.new(Saviour::BaseUploader) {
55
+ store_dir { "/store/dir" }
56
+
57
+ process_with_file do |file, filename|
58
+ # same ':size' key in the stash
59
+ stash(size: File.size(file.path))
60
+
61
+ [file, filename]
62
+ end
63
+
64
+ after_upload do |stash|
65
+ model.update_attributes!("size_#{attached_as}" => stash[:size])
66
+ end
67
+ }
68
+
69
+ klass = Class.new(Test) { include Saviour::Model }
70
+ klass.attach_file :file, uploader
71
+ klass.attach_file :file_thumb, uploader
72
+
73
+ a = klass.create!
74
+
75
+ # - 1 initial empty query
76
+ # - 2 queries to update size
77
+ # - 1 query to assign stored paths
78
+ expect_to_yield_queries(count: 4) do
79
+ a.update_attributes! file: Saviour::StringSource.new("a" * 74, "file.txt"),
80
+ file_thumb: Saviour::StringSource.new("a" * 31, "file_2.txt")
81
+ end
82
+
83
+ expect(a.size_file).to eq 74
84
+ expect(a.size_file_thumb).to eq 31
85
+ end
86
+ end
data/spec/spec_helper.rb CHANGED
@@ -7,15 +7,17 @@ require 'active_record'
7
7
  require 'sqlite3'
8
8
  require 'get_process_mem'
9
9
 
10
+ require 'support/active_record_asserts'
11
+
10
12
  require File.expand_path("../../lib/saviour", __FILE__)
11
13
 
12
14
  connection_opts = case ENV.fetch('DB', "sqlite")
13
15
  when "sqlite"
14
- {adapter: "sqlite3", database: ":memory:"}
16
+ { adapter: "sqlite3", database: ":memory:" }
15
17
  when "mysql"
16
- {adapter: "mysql2", database: "saviour", username: "root", encoding: "utf8"}
18
+ { adapter: "mysql2", database: "saviour", username: "root", encoding: "utf8" }
17
19
  when "postgres"
18
- {adapter: "postgresql", database: "saviour", username: "postgres"}
20
+ { adapter: "postgresql", database: "saviour", username: "postgres" }
19
21
  end
20
22
 
21
23
  ActiveRecord::Base.establish_connection(connection_opts)
@@ -44,6 +46,7 @@ RSpec.configure do |config|
44
46
 
45
47
  config.before do
46
48
  Test.delete_all
49
+ Saviour::Config.concurrent_workers = 4
47
50
  end
48
51
  end
49
52
 
@@ -0,0 +1,35 @@
1
+ module ActiveRecordAssertions
2
+ def expect_to_yield_queries(count: nil, including: [])
3
+ AssertionsTracker.clear!
4
+ yield
5
+
6
+ expect(AssertionsTracker.data.size).to eq(count) if count
7
+
8
+ including.each do |query|
9
+ expect(AssertionsTracker.data).to include a_string_matching(Regexp.new(query))
10
+ end
11
+ end
12
+ end
13
+
14
+ module AssertionsTracker
15
+ def self.data
16
+ @data ||= []
17
+ end
18
+
19
+ def self.clear!
20
+ @data = []
21
+ end
22
+ end
23
+
24
+ RSpec.configure do |config|
25
+ config.before do
26
+ AssertionsTracker.clear!
27
+ end
28
+
29
+ config.include ActiveRecordAssertions
30
+ end
31
+
32
+ ActiveSupport::Notifications.subscribe "sql.active_record" do |name, started, finished, unique_id, data|
33
+ AssertionsTracker.data.push(data[:sql]) if data[:name] == "SQL"
34
+ end
35
+
@@ -3,7 +3,11 @@ ActiveRecord::Schema.define do
3
3
  t.string :file
4
4
  t.string :file_thumb
5
5
  t.string :file_thumb_2
6
+ t.string :file_thumb_3
6
7
  t.string :name
8
+ t.integer :file_size
9
+ t.integer :size_file
10
+ t.integer :size_file_thumb
7
11
  t.timestamps null: false
8
12
  end
9
13
  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.4.14
4
+ version: 0.5.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-02-06 00:00:00.000000000 Z
11
+ date: 2018-03-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -38,6 +38,20 @@ dependencies:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '5.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: concurrent-ruby
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: 1.0.5
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: 1.0.5
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: bundler
43
57
  requirement: !ruby/object:Gem::Requirement
@@ -186,6 +200,7 @@ files:
186
200
  - lib/saviour/version.rb
187
201
  - saviour.gemspec
188
202
  - spec/feature/allow_overriding_attached_as_method_spec.rb
203
+ - spec/feature/concurrent_processors_spec.rb
189
204
  - spec/feature/crud_workflows_spec.rb
190
205
  - spec/feature/dirty_spec.rb
191
206
  - spec/feature/follow_file_spec.rb
@@ -199,6 +214,7 @@ files:
199
214
  - spec/feature/remove_attachment_spec.rb
200
215
  - spec/feature/reopens_file_at_every_process_spec.rb
201
216
  - spec/feature/rewind_source_before_read_spec.rb
217
+ - spec/feature/stash_spec.rb
202
218
  - spec/feature/transactional_behavior_spec.rb
203
219
  - spec/feature/uploader_declaration_spec.rb
204
220
  - spec/feature/validations_spec.rb
@@ -211,6 +227,7 @@ files:
211
227
  - spec/models/s3_storage_spec.rb
212
228
  - spec/models/url_source_spec.rb
213
229
  - spec/spec_helper.rb
230
+ - spec/support/active_record_asserts.rb
214
231
  - spec/support/data/camaloon.jpg
215
232
  - spec/support/data/example.xml
216
233
  - spec/support/data/text.txt