saviour 0.4.14 → 0.5.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.
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