carrierwave_backgrounder_revived 1.0.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.
Files changed (37) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +5 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +15 -0
  5. data/CHANGELOG.md +99 -0
  6. data/Gemfile +4 -0
  7. data/README.md +229 -0
  8. data/Rakefile +12 -0
  9. data/carrierwave_backgrounder.gemspec +25 -0
  10. data/lib/backgrounder/delay.rb +26 -0
  11. data/lib/backgrounder/orm/activemodel.rb +36 -0
  12. data/lib/backgrounder/orm/base.rb +120 -0
  13. data/lib/backgrounder/orm/data_mapper.rb +63 -0
  14. data/lib/backgrounder/railtie.rb +25 -0
  15. data/lib/backgrounder/support/backends.rb +94 -0
  16. data/lib/backgrounder/version.rb +5 -0
  17. data/lib/backgrounder/workers.rb +6 -0
  18. data/lib/backgrounder/workers/base.rb +42 -0
  19. data/lib/backgrounder/workers/class_methods.rb +14 -0
  20. data/lib/backgrounder/workers/process_asset.rb +10 -0
  21. data/lib/backgrounder/workers/process_asset_mixin.rb +28 -0
  22. data/lib/backgrounder/workers/store_asset.rb +10 -0
  23. data/lib/backgrounder/workers/store_asset_mixin.rb +43 -0
  24. data/lib/carrierwave_backgrounder.rb +35 -0
  25. data/lib/generators/carrierwave_backgrounder/USAGE +8 -0
  26. data/lib/generators/carrierwave_backgrounder/install_generator.rb +20 -0
  27. data/lib/generators/carrierwave_backgrounder/templates/config/initializers/carrierwave_backgrounder.rb +10 -0
  28. data/spec/backgrounder/orm/activemodel_spec.rb +97 -0
  29. data/spec/backgrounder/orm/base_spec.rb +74 -0
  30. data/spec/backgrounder/support/backends_spec.rb +234 -0
  31. data/spec/backgrounder/workers/fixtures/images/test.jpg +0 -0
  32. data/spec/backgrounder/workers/process_asset_spec.rb +70 -0
  33. data/spec/backgrounder/workers/store_asset_spec.rb +137 -0
  34. data/spec/spec_helper.rb +20 -0
  35. data/spec/support/backend_constants.rb +58 -0
  36. data/spec/support/mock_worker.rb +22 -0
  37. metadata +151 -0
@@ -0,0 +1,35 @@
1
+ require 'active_support/core_ext/object'
2
+ require 'backgrounder/support/backends'
3
+ require 'backgrounder/orm/base'
4
+ require 'backgrounder/delay'
5
+
6
+ module CarrierWave
7
+ module Backgrounder
8
+ include Support::Backends
9
+
10
+ def self.configure
11
+ yield self
12
+ case @backend
13
+ when :sidekiq
14
+ require 'sidekiq'
15
+ ::CarrierWave::Workers::ProcessAsset.class_eval do
16
+ include ::Sidekiq::Worker
17
+ end
18
+ ::CarrierWave::Workers::StoreAsset.class_eval do
19
+ include ::Sidekiq::Worker
20
+ end
21
+ when :sucker_punch
22
+ require 'sucker_punch'
23
+ ::CarrierWave::Workers::ProcessAsset.class_eval do
24
+ include ::SuckerPunch::Job
25
+ end
26
+ ::CarrierWave::Workers::StoreAsset.class_eval do
27
+ include ::SuckerPunch::Job
28
+ end
29
+ end
30
+ end
31
+
32
+ end
33
+ end
34
+
35
+ require 'backgrounder/railtie' if defined?(Rails)
@@ -0,0 +1,8 @@
1
+ Description:
2
+ To copy CarrierWaveBackgrounder initializer to your Rails App, with some configuration values, run the following:
3
+
4
+ Example:
5
+ rails generate carrierwave_backgrounder:install
6
+
7
+ This will create:
8
+ config/initializer/carrierwave_backgrounder.rb
@@ -0,0 +1,20 @@
1
+ module CarrierwaveBackgrounder
2
+ module Generators
3
+ class InstallGenerator < Rails::Generators::Base
4
+ source_root File.expand_path('../templates', __FILE__)
5
+
6
+ def copy_config
7
+ template "config/initializers/carrierwave_backgrounder.rb"
8
+ end
9
+
10
+ def info_config
11
+ puts <<-EOF
12
+
13
+ \e[33mBy default :delayed_job is used as the backend for carrierwave_backgrounder with :carrierwave as the queue name.
14
+ To change this, edit config/initializers/carrierwave_backgrounder.rb.\e[0m
15
+
16
+ EOF
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,10 @@
1
+ CarrierWave::Backgrounder.configure do |c|
2
+ c.backend :delayed_job, queue: :carrierwave
3
+ # c.backend :active_job, queue: :carrierwave
4
+ # c.backend :resque, queue: :carrierwave
5
+ # c.backend :sidekiq, queue: :carrierwave
6
+ # c.backend :girl_friday, queue: :carrierwave
7
+ # c.backend :sucker_punch, queue: :carrierwave
8
+ # c.backend :qu, queue: :carrierwave
9
+ # c.backend :qc
10
+ end
@@ -0,0 +1,97 @@
1
+ require 'spec_helper'
2
+ require 'ostruct'
3
+ require 'backgrounder/orm/activemodel'
4
+
5
+ RSpec.describe CarrierWave::Backgrounder::ORM::ActiveModel do
6
+ before do
7
+ @mock_class = Class.new do
8
+ def self.before_save(method, opts); nil; end
9
+ def self.after_commit(method, opts); nil; end
10
+ def avatar_changed?; nil; end
11
+ def remote_avatar_url; OpenStruct.new(:present? => true); end
12
+ def remove_avatar?; false; end
13
+ def previous_changes; {}; end
14
+ def self.uploader_options; {}; end
15
+ end
16
+
17
+ @mock_class.extend CarrierWave::Backgrounder::ORM::ActiveModel
18
+ end
19
+
20
+ describe '.store_in_background' do
21
+ context 'setting up callbacks' do
22
+ it 'creates an after_commit hook' do
23
+ expect(@mock_class).to receive(:after_commit).with(:enqueue_avatar_background_job, :if => :enqueue_avatar_background_job?)
24
+ @mock_class.store_in_background :avatar
25
+ end
26
+ end
27
+ end
28
+
29
+ describe '.process_in_background' do
30
+ context 'setting up callbacks' do
31
+ it 'creates a before_save hook' do
32
+ expect(@mock_class).to receive(:before_save).with(:set_avatar_processing, :if => :enqueue_avatar_background_job?)
33
+ @mock_class.process_in_background :avatar
34
+ end
35
+
36
+ it 'creates an after_save hook' do
37
+ expect(@mock_class).to receive(:after_commit).with(:enqueue_avatar_background_job, :if => :enqueue_avatar_background_job?)
38
+ @mock_class.process_in_background :avatar
39
+ end
40
+ end
41
+ end
42
+
43
+ describe '#trigger_column_background_processing?' do
44
+ let(:instance) { @mock_class.new }
45
+
46
+ before do
47
+ @mock_class.process_in_background :avatar
48
+ end
49
+
50
+ context 'mount_on option is set' do
51
+ before do
52
+ options_hash = {:avatar => {:mount_on => :some_other_column}}
53
+ expect(@mock_class).to receive(:uploader_options).and_return(options_hash)
54
+ end
55
+
56
+ it "returns true if alternate column is changed" do
57
+ expect(instance).to receive(:some_other_column_changed?).and_return(true)
58
+ expect(instance.avatar_updated?).to be_truthy
59
+ end
60
+ end
61
+
62
+ it "returns true if process_avatar_upload is false" do
63
+ expect(instance).to receive(:process_avatar_upload)
64
+ expect(instance.enqueue_avatar_background_job?).to be_truthy
65
+ end
66
+
67
+ it "calls column_changed?" do
68
+ expect(instance).to receive(:process_avatar_upload).and_return(false)
69
+ expect(instance).to receive(:avatar_changed?)
70
+ expect(instance.enqueue_avatar_background_job?).to be_truthy
71
+ end
72
+
73
+ it "calls previous_changes" do
74
+ expect(instance).to receive(:process_avatar_upload).and_return(false)
75
+ expect(instance).to receive(:avatar_changed?).and_return(false)
76
+ expect(instance).to receive(:previous_changes).and_return({:avatar => true})
77
+ expect(instance.enqueue_avatar_background_job?).to be_truthy
78
+ end
79
+
80
+ it "calls avatar_remote_url" do
81
+ expect(instance).to receive(:process_avatar_upload).and_return(false)
82
+ expect(instance).to receive(:avatar_changed?).and_return(false)
83
+ expect(instance).to receive(:previous_changes).and_return({})
84
+ expect(instance).to receive(:remote_avatar_url).and_return('yup')
85
+ expect(instance.enqueue_avatar_background_job?).to be_truthy
86
+ end
87
+
88
+ it "calls avatar_cache" do
89
+ expect(instance).to receive(:process_avatar_upload).and_return(false)
90
+ expect(instance).to receive(:avatar_changed?).and_return(false)
91
+ expect(instance).to receive(:previous_changes).and_return({})
92
+ expect(instance).to receive(:remote_avatar_url).and_return(nil)
93
+ expect(instance).to receive(:avatar_cache).and_return('yup')
94
+ expect(instance.enqueue_avatar_background_job?).to be_truthy
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,74 @@
1
+ require 'spec_helper'
2
+
3
+
4
+ RSpec.describe CarrierWave::Backgrounder::ORM::Base do
5
+ before do
6
+ @mock_class = Class.new do
7
+ def self.before_save(method, opts); nil; end
8
+ def self.after_commit(method, opts); nil; end
9
+ end
10
+
11
+ @mock_class.extend CarrierWave::Backgrounder::ORM::Base
12
+ end
13
+
14
+ it 'mixes in the two DSL methods' do
15
+ expect(@mock_class).to respond_to(:process_in_background)
16
+ expect(@mock_class).to respond_to(:store_in_background)
17
+ end
18
+
19
+ describe '.process_in_background' do
20
+ context 'including new methods' do
21
+ before do
22
+ @mock_class.process_in_background :avatar
23
+ @instance = @mock_class.new
24
+ end
25
+
26
+ it 'creates a processing enabled accessor' do
27
+ expect(@instance).to respond_to(:process_avatar_upload)
28
+ expect(@instance).to respond_to(:process_avatar_upload=)
29
+ end
30
+
31
+ it 'create a setter for the processing attribute' do
32
+ expect(@instance).to respond_to(:set_avatar_processing)
33
+ end
34
+
35
+ it 'create a background job queuer' do
36
+ expect(@instance).to respond_to(:enqueue_avatar_background_job)
37
+ end
38
+
39
+ it 'create a trigger interrogator' do
40
+ expect(@instance).to respond_to(:enqueue_avatar_background_job?)
41
+ end
42
+ end
43
+ end
44
+
45
+ describe 'store in background' do
46
+ describe 'including new methods' do
47
+ before do
48
+ @mock_class.store_in_background :avatar
49
+ @instance = @mock_class.new
50
+ end
51
+
52
+ it 'creates a processing enabled accessor' do
53
+ expect(@instance).to respond_to(:process_avatar_upload)
54
+ expect(@instance).to respond_to(:process_avatar_upload=)
55
+ end
56
+
57
+ it 'overrides the write column identifier method from carrierwave' do
58
+ expect(@instance).to respond_to(:write_avatar_identifier)
59
+ end
60
+
61
+ it 'overrides the store column method from carrierwave' do
62
+ expect(@instance).to respond_to(:store_avatar!)
63
+ end
64
+
65
+ it 'create a background job queuer' do
66
+ expect(@instance).to respond_to(:enqueue_avatar_background_job)
67
+ end
68
+
69
+ it 'create a trigger interrogator' do
70
+ expect(@instance).to respond_to(:enqueue_avatar_background_job?)
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,234 @@
1
+ require 'spec_helper'
2
+ require 'support/backend_constants'
3
+ require 'support/mock_worker'
4
+
5
+ module CarrierWave::Backgrounder
6
+ RSpec.describe Support::Backends do
7
+ let(:mock_module) { Module.new }
8
+
9
+ before do
10
+ mock_module.send :include, Support::Backends
11
+ end
12
+
13
+ describe 'setting backend' do
14
+ it 'using #backend=' do
15
+ expect {
16
+ mock_module.backend = :delayed_job
17
+ }.to raise_error(NoMethodError)
18
+ end
19
+
20
+ it 'using #backend' do
21
+ mock_module.backend(:delayed_job)
22
+ expect(mock_module.backend).to eql(:delayed_job)
23
+ end
24
+
25
+ it 'allows passing of queue_options' do
26
+ mock_module.backend(:delayed_job, :queue => :awesome_queue)
27
+ expect(mock_module.queue_options).to eql({:queue => :awesome_queue})
28
+ end
29
+ end
30
+
31
+ describe '#enqueue_for_backend' do
32
+ let!(:worker) { MockWorker.new('FakeClass', 1, :image) }
33
+
34
+ context 'active_job' do
35
+ let(:args) { ['FakeClass', 1, :image] }
36
+
37
+ it 'invokes perform with string arguments' do
38
+ expect(MockWorker).to receive(:perform).with('FakeClass', '1', 'image')
39
+ mock_module.backend :active_job
40
+ mock_module.enqueue_for_backend(MockWorker, *args)
41
+ end
42
+ end
43
+
44
+ context 'delayed_job' do
45
+ before do
46
+ @mock_worker = Class.new do
47
+ def self.perform(*args); new(*args).perform; end
48
+ end
49
+
50
+ allow(MockWorker).to receive(:new).and_return(worker)
51
+ end
52
+
53
+ context 'queue column exists' do
54
+ it 'does not pass the queue name if none passed to #backend' do
55
+ mock_module.backend :delayed_job
56
+ expect(Delayed::Job).to receive(:enqueue).with(worker, {})
57
+ mock_module.enqueue_for_backend MockWorker, 'FakeClass', 1, :image
58
+ end
59
+
60
+ it 'sets the queue name to the queue name passed to #backend' do
61
+ mock_module.backend :delayed_job, :queue => :awesome_queue
62
+ expect(Delayed::Job).to receive(:enqueue).with(worker, :queue => :awesome_queue)
63
+ mock_module.enqueue_for_backend MockWorker, 'FakeClass', 1, :image
64
+ end
65
+ end
66
+
67
+ context 'priority set in config' do
68
+ it 'sets the priority which is passed to #backend' do
69
+ mock_module.backend :delayed_job, :priority => 5
70
+ expect(Delayed::Job).to receive(:enqueue).with(worker, :priority => 5)
71
+ mock_module.enqueue_for_backend MockWorker, 'FakeClass', 1, :image
72
+ end
73
+ end
74
+
75
+ context 'queue column does not exist' do
76
+ before do
77
+ column_names = Delayed::Job.column_names.tap { |cn| cn.delete('queue') }
78
+ allow(Delayed::Job).to receive(:column_names).and_return(column_names)
79
+ Delayed::Job.class_eval { remove_method(:queue) }
80
+ end
81
+
82
+ after do
83
+ Delayed::Job.class_eval { define_method(:queue) { nil } }
84
+ end
85
+
86
+ it 'does not pass a queue name if none passed to #backend' do
87
+ mock_module.backend :delayed_job
88
+ expect(Delayed::Job).to receive(:enqueue).with(worker, {})
89
+ mock_module.enqueue_for_backend MockWorker, 'FakeClass', 1, :image
90
+ end
91
+
92
+ it 'does not pass a queue name and logs a warning message if a queue name is passed to #backend' do
93
+ mock_module.backend :delayed_job, :queue => :awesome_queue
94
+ expect(Rails.logger).to receive(:warn).with(instance_of(String))
95
+ expect(Delayed::Job).to receive(:enqueue).with(worker, {})
96
+ mock_module.enqueue_for_backend MockWorker, 'FakeClass', 1, :image
97
+ end
98
+ end
99
+ end
100
+
101
+ context 'resque' do
102
+ let(:args) { [MockWorker, 'FakeClass', 1, :image] }
103
+
104
+ before do
105
+ allow(Resque).to receive(:enqueue).with(*args)
106
+ end
107
+
108
+ it 'sets a variable with the queue name, defaults to :carrierwave' do
109
+ mock_module.backend :resque
110
+ mock_module.enqueue_for_backend(*args)
111
+ expect(MockWorker.instance_variable_get '@queue').to eql(:carrierwave)
112
+ end
113
+
114
+ it 'sets a variable to the queue name passed to #backend' do
115
+ mock_module.backend :resque, :queue => :awesome_queue
116
+ mock_module.enqueue_for_backend(*args)
117
+ expect(MockWorker.instance_variable_get '@queue').to eql(:awesome_queue)
118
+ end
119
+ end
120
+
121
+ context 'sidekiq' do
122
+ let(:args) { ['FakeClass', 1, :image] }
123
+
124
+ it 'invokes client_push on the class with passed args' do
125
+ expect(MockSidekiqWorker).to receive(:client_push).with({ 'class' => MockSidekiqWorker, 'args' => args })
126
+ mock_module.backend :sidekiq
127
+ mock_module.enqueue_for_backend(MockSidekiqWorker, *args)
128
+ end
129
+
130
+ it 'invokes client_push and includes the options passed to backend' do
131
+ expect(MockSidekiqWorker).to receive(:client_push).with({ 'class' => MockSidekiqWorker,
132
+ 'retry' => false,
133
+ 'timeout' => 60,
134
+ 'queue' => :awesome_queue,
135
+ 'args' => args })
136
+ options = {:retry => false, :timeout => 60, :queue => :awesome_queue}
137
+ mock_module.backend :sidekiq, options
138
+ mock_module.enqueue_for_backend(MockSidekiqWorker, *args)
139
+ end
140
+
141
+ it 'does not override queue name if set it worker' do
142
+ expect(MockNamedSidekiqWorker).to receive(:client_push).with({ 'class' => MockNamedSidekiqWorker,
143
+ 'retry' => false,
144
+ 'timeout' => 60,
145
+ 'args' => args })
146
+ options = {:retry => false, :timeout => 60}
147
+ mock_module.backend :sidekiq, options
148
+ mock_module.enqueue_for_backend(MockNamedSidekiqWorker, *args)
149
+ end
150
+ end
151
+
152
+ context 'girl_friday' do
153
+ let(:args) { [MockWorker, 'FakeClass', 1, :image] }
154
+
155
+ it 'instantiates a GirlFriday work queue if one does not exist' do
156
+ mock_module.backend :girl_friday
157
+ expect(GirlFriday::WorkQueue).to receive(:new).with(:carrierwave, {}).and_return([])
158
+ mock_module.enqueue_for_backend(*args)
159
+ end
160
+
161
+ it 'instantiates a GirlFriday work queue passing the args to the queue' do
162
+ mock_module.backend :girl_friday, :queue => :awesome_queue, :size => 3
163
+ expect(GirlFriday::WorkQueue).to receive(:new).with(:awesome_queue, {:size => 3}).and_return([])
164
+ mock_module.enqueue_for_backend(*args)
165
+ end
166
+
167
+ it 'does not instantiate a GirlFriday work queue if one exists' do
168
+ mock_module.backend :girl_friday
169
+ mock_module.instance_variable_set('@girl_friday_queue', [])
170
+ expect(GirlFriday::WorkQueue).to receive(:new).never
171
+ mock_module.enqueue_for_backend(*args)
172
+ end
173
+
174
+ it 'add a worker to the girl_friday queue' do
175
+ expected = [{ :worker => MockWorker.new('FakeClass', 1, :image) }]
176
+ mock_module.backend :girl_friday
177
+ mock_module.instance_variable_set('@girl_friday_queue', [])
178
+ mock_module.enqueue_for_backend(*args)
179
+ expect(mock_module.instance_variable_get '@girl_friday_queue').to eql(expected)
180
+ end
181
+ end
182
+
183
+ context 'sucker_punch' do
184
+ let(:args) { [MockWorker, 'FakeClass', 1, :image] }
185
+ let(:job) { double('job') }
186
+
187
+ it 'invokes a new worker' do
188
+ expect(MockWorker).to receive(:new).and_return(worker)
189
+ expect(worker).to receive(:async).and_return(job)
190
+ expect(job).to receive(:perform).with('FakeClass', 1, :image)
191
+ mock_module.backend :sucker_punch
192
+ mock_module.enqueue_for_backend(*args)
193
+ end
194
+ end
195
+
196
+ context 'qu' do
197
+ let(:args) { [MockWorker, 'FakeClass', 1, :image] }
198
+ before do
199
+ allow(Qu).to receive(:enqueue).with(*args)
200
+ end
201
+
202
+ it 'sets a variable with the queue name, defaults to :carrierwave' do
203
+ mock_module.backend :qu
204
+ mock_module.enqueue_for_backend(*args)
205
+ expect(MockWorker.instance_variable_get '@queue').to eql(:carrierwave)
206
+ end
207
+
208
+ it 'sets a variable to the queue name passed to #backend' do
209
+ mock_module.backend :qu, :queue => :awesome_queue
210
+ mock_module.enqueue_for_backend(*args)
211
+ expect(MockWorker.instance_variable_get '@queue').to eql(:awesome_queue)
212
+ end
213
+ end
214
+
215
+ context 'qc' do
216
+ it 'calls enqueue with the passed args' do
217
+ expect(QC).to receive(:enqueue).with("MockWorker.perform", 'FakeClass', 1, 'image')
218
+ mock_module.backend :qc
219
+ mock_module.enqueue_for_backend(MockWorker, 'FakeClass', 1, :image)
220
+ end
221
+ end
222
+
223
+ context 'immediate' do
224
+ it 'instantiates a worker passing the args and calls perform' do
225
+ worker = double('Worker')
226
+ expect(MockWorker).to receive(:new).with('FakeClass', 1, :image).and_return(worker)
227
+ expect(worker).to receive(:perform)
228
+ mock_module.backend :immediate
229
+ mock_module.enqueue_for_backend(MockWorker, 'FakeClass', 1, :image)
230
+ end
231
+ end
232
+ end
233
+ end
234
+ end