postburner 0.3.2 → 0.6.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: ba4b7b0b6ccdc3f0ce25453614c73e683dcac10fde47c6382337e9d0a8eecc42
4
- data.tar.gz: 20899869073130b8fb856009c1a69dfa5b0bc4594c79477ff7593d17775cf35f
3
+ metadata.gz: f0c2a65b18596dafa8809d07924e2274f7adc05ce971fea1b82143c7132f2929
4
+ data.tar.gz: e3000b8fa490aec4fb1c8b49c54fe7ee78613b3e7e3849c2270d7a7dd01adecd
5
5
  SHA512:
6
- metadata.gz: 32d316908465caab8c6276ba6db4be43bfdcab5185146cbff59a708057ef331dac90374b817f65a25c623530cfbcb8bfbded3d152aaabd7607fd8df4e7288a1a
7
- data.tar.gz: b7ba8bce12c6ddd9e2e353b8dd11fb6e87cd7e7ecbde3a410bc73e8c06464cfb6142172200528e66a2cdd3e02f903366f27ebd7d63e0b2ca14fb6a83335269bd
6
+ metadata.gz: 87cb7aec3924346ab43b93f5886aebfb1b138a36b199eb3e10032404a23b07774c287cb96ed8c4889feabde1106aba63d23d6429cead43f3eec8211f257d6baa
7
+ data.tar.gz: 94e32291c4f00db869a630adeea7e44abb92c4938e2314fc5e46ea487b874acc4a74b80771bd8db8944f3a7c98e06101ef90a36f8da5bf72d7fecc82fbd14c3c
data/CHANGELOG.md CHANGED
@@ -1,5 +1,36 @@
1
1
  # Changelog
2
2
 
3
+ ## v0.6.0 - 2021-11-01
4
+
5
+ ### Added
6
+ - update exception handling to better show where the error was raised.
7
+ - update logging to match errata format.
8
+ - update documentation.
9
+
10
+ ## v0.5.0 - 2021-10-27
11
+
12
+ ### Added
13
+ - add mailer, action, params accessors to Mailer for easy access in subclassed jobs.
14
+ - add logging to Mailer perform.
15
+ - add save to #queue! job if unsaved
16
+ - add block with logging if queued_at isn't set or isn't prior to run time.
17
+
18
+ ## v0.4.0 - 2021-10-12
19
+
20
+ ### Added
21
+ - Change Job logging to persisted in perform.
22
+ - Update mailer job to use #with and support serializations.
23
+
24
+ ## v0.3.3 - 2021-06-15
25
+
26
+ ### Fixed
27
+ - re-raise exception after logging during perform.
28
+ - log but swallow post perform data update exceptions.
29
+ - only allow remove! if job still present.
30
+
31
+ ### Added
32
+ - Job#log! and Job#log_exception! that save the data immediately.
33
+
3
34
  ## v0.3.2 - 2021-05-07
4
35
 
5
36
  ### Fixed
data/README.md CHANGED
@@ -1,29 +1,15 @@
1
1
  # Postburner
2
- An ActiveRecord layer on top of Backburner for inspecting and auditing the
3
- queue, especially for delayed jobs. It isn't meant to be fast, but safe.
2
+ An ActiveRecord layer on top of [Backburner](https://github.com/nesquena/backburner) for inspecting and auditing the
3
+ queue, especially for delayed jobs. It isn't meant to be outperform other queues, but be safe (and inspectable).
4
4
 
5
- It is meant to be complementary to Backburner. Use Backburner as the default
5
+ It is meant to be complementary to [Backburner](https://github.com/nesquena/backburner). Use Backburner as the default
6
6
  ActiveJob processor for mailers, active storage, and the like. Use a
7
- `Postburner::Job` for things that you want to track.
7
+ `Postburner::Job` for things that you want to track. See [Comparison to Backburner](#comparison-to-backburner) for more.
8
8
 
9
- Comes with a mountable interface that can be password protected with whatever
10
- authentication you use in your project.
11
-
12
- Also meant to be a replacement/upgrade for [Que](https://github.com/que-rb/que).
13
- If you need something faster, but backed with ACID compliance, check out Que.
14
- There are some additional features in Postburner such as retained jobs
15
- after processing, stats, per job logging, etc.
16
-
17
- Compared to plain [Backburner](https://github.com/nesquena/backburner),
18
- Postburner adds:
19
- - Database Jobs for inspection, linking, auditing, removal (and deletion)
20
- - Direct access to associated [Beanstalk](https://beanstalkd.github.io/) (via [beaneater](https://github.com/beanstalkd/beaneater))
21
- - Job Statistics (lag, attempts, logs, tracked errors)
22
- - Convenience methods to clear tubes, stats, and connections for Beanstalk.
23
-
24
- Otherwise, Postburner tries to be a super simple layer on `Backburner::Queue`
25
- and `ActiveRecord`. Every tool with either of those are avilabel in
26
- `Postburner::Job`s.
9
+ Postburner meant to be a replacement/upgrade for [Que](https://github.com/que-rb/que).
10
+ If you need something faster, check out Que - we love it! Postburner is built for a slightly different purpose: to be
11
+ simple, safe, and inspectable. All of which Que has (or can be added), but are included by default with some architecture
12
+ differences. See [Comparison to Que](#comparison-to-que) for more.
27
13
 
28
14
  ## Usage
29
15
 
@@ -33,51 +19,251 @@ class RunDonation < Postburner::Job
33
19
  queue_priority 0 # 0 is highest priority
34
20
  queue_max_job_retries 0 # don't retry
35
21
 
36
- def process(args)
22
+ def perform(args)
37
23
  # do long tasks here
38
24
  # also have access to self.args
39
25
  end
40
26
  end
41
27
 
42
- RunDonation.create!(args: {donation_id: 123}).queue!
28
+ # RunDonation#create! is the `ActiveRecord` method, so it returns a model,
29
+ # you can manipulate, add columns to the table to, reference with foreign
30
+ # keys, etc.
31
+ job = RunDonation.create!(args: {donation_id: 123})
32
+ # NOTE Make sure use use an after_commit or after_save_commit, etc to avoid
33
+ # any race conditions of Postburner trying to process before a required
34
+ # database mutation is commited.
35
+ job.queue!
43
36
  => {:status=>"INSERTED", :id=>"1139"}
37
+
38
+ # queue for later with `:at`
44
39
  RunDonation.create!(args: {donation_id: 123}).queue! at: Time.zone.now + 2.days
45
40
  => {:status=>"INSERTED", :id=>"1140"}
46
41
 
47
- # `delay` takes priority over `at`
42
+ # queue for later with `:delay`
43
+ #
44
+ # `:delay` takes priority over `:at`takes priority over `:at` because the
45
+ # beanstalkd protocol uses uses `delay`
48
46
  RunDonation.create!(args: {donation_id: 123}).queue! delay: 1.hour
49
47
  => {:status=>"INSERTED", :id=>"1141"}
50
48
  ```
51
49
 
50
+ ### Mailers
51
+
52
+ ```ruby
53
+ j = Postburner::Mailer.
54
+ delivery(UserMailer, :welcome)
55
+ .with(name: 'Freddy')
56
+
57
+ j.queue!
58
+ => {:status=>"INSERTED", :id=>"1139"}
59
+ ```
60
+
61
+ ### [Beaneater](https://github.com/beanstalkd/beaneater) and [beanstalkd](https://raw.githubusercontent.com/beanstalkd/beanstalkd/master/doc/protocol.txt) attributes and methods
62
+ ```ruby
63
+ # get the beanstalkd job id
64
+ job.bkid
65
+ => 1104
66
+
67
+ # get the beaneater job, call any beaneater methods on this object
68
+ job.beanstalk_job
69
+
70
+ # get the beanstald stats
71
+ job.beanstalk_job.stats
72
+
73
+ # kick the beanstalk job
74
+ job.kick!
75
+
76
+ # delete beankstalkd job, retain the job model
77
+ job.delete!
78
+
79
+ # delete beankstalkd job, retain the job model, but set `removed_at` on model.
80
+ job.remove!
81
+
82
+ # or simply remove the model, which will clean up the beanstalkd job in a before_destroy hook
83
+ job.destroy # OR job.destroy!
84
+
85
+ # get a cached Backburner connection and inspect it (or even use it directly)
86
+ c = Postburner.connection
87
+ c.beanstalk.tubes.to_a
88
+ c.beanstalk.tubes.to_a.map{|t| c.tubes[t.name].peek(:buried)}
89
+ c.beanstalk.tubes['ss.development.caching'].stats
90
+ c.beanstalk.tubes['ss.development.caching'].peek(:buried).kick
91
+ c.beanstalk.tubes['ss.development.caching'].kick(3)
92
+ c.close
93
+
94
+ # automatically close
95
+ Postburner.connected do |connection|
96
+ # do stuff with connection
97
+ end
98
+ ```
99
+
100
+ Read about the [beanstalkd protocol](https://raw.githubusercontent.com/beanstalkd/beanstalkd/master/doc/protocol.txt).
101
+
102
+ ### Basic model fields
103
+ ```ruby
104
+ # ActiveRecord primary key
105
+ job.id
106
+
107
+ # int id of the beankstalkd job
108
+ job.bkid
109
+
110
+ # string uuid
111
+ job.sid
112
+
113
+ # ActiveRecord STI job subtype
114
+ job.type
115
+
116
+ # jsonb arguments for use in job
117
+ job.args
118
+
119
+ # time job should run - not intended to be changed
120
+ # TODO run_at should be readonly after create
121
+ job.run_at
122
+ ```
123
+
124
+ ### Job statistics
125
+ ```ruby
126
+ # when job was inserted into beankstalkd
127
+ job.queued_at
128
+
129
+ # last time attempted
130
+ job.attempting_at
131
+
132
+ # last time processing started
133
+ job.processing_at
134
+
135
+ # when completed
136
+ job.processed_at
137
+
138
+ # when removed, may be nil
139
+ job.removed_at
140
+
141
+ # lag in ms from run_at/queued_at to attempting_at
142
+ job.lag
143
+
144
+ # duration of processing in ms
145
+ job.duration
146
+
147
+ # number of attempts
148
+ job.attempt_count
149
+
150
+ # number of errors (length of errata)
151
+ job.error_count
152
+
153
+ # number of log entries (length of logs)
154
+ job.log_count
155
+
156
+ # array of attempting_at times
157
+ job.attempts
158
+
159
+ # array of errors
160
+ job.errata
161
+
162
+ # array of log messages
163
+ job.logs
164
+ ```
165
+
166
+ ### Job logging and exceptions
167
+
168
+ Optionally, you can:
169
+ 1. Add log messages to the job during processing to `logs`
170
+ 1. Add log your own exceptions to `errata`
171
+
172
+ ```ruby
173
+ class RunDonation < Postburner::Job
174
+ queue 'critical'
175
+ queue_priority 0 # 0 is highest priority
176
+ queue_max_job_retries 0 # don't retry
177
+
178
+ def perform(args)
179
+ # log at task, defaults to `:info`, but `:debug`, `:warning`, `:error`
180
+ log "Log bad condition...", level: :error
181
+
182
+ begin
183
+ # danger
184
+ rescue Exception => e
185
+ log_exception e
186
+ end
187
+ end
188
+ end
189
+ ```
190
+
191
+ ### Optionally, mount the engine
192
+
193
+ ```ruby
194
+ mount Postburner::Engine => "/postburner"
195
+
196
+ # mount only for development inspection
197
+ mount Postburner::Engine => "/postburner" if Rails.env.development?
198
+ ```
199
+
200
+ [Open the controller](https://guides.rubyonrails.org/engines.html#overriding-models-and-controllers)
201
+ to add your own authentication or changes - or just create your own routes, controllers, and views.
202
+
203
+ [Override the views](https://guides.rubyonrails.org/engines.html#overriding-views) to make them
204
+ prettier - or follow the suggestion above and use your own.
205
+
52
206
  ## Installation
53
207
 
208
+ First [install beanstalkd](https://beanstalkd.github.io/download.html). On Debian-based systems, that goes like this:
209
+ ```bash
210
+ sudo apt-get install beanstalkd
211
+ ```
212
+
213
+ Then add to your Gemfile.
54
214
  ```ruby
55
- # in Gemfile
56
215
  gem 'postburner'
57
216
  ```
58
217
 
218
+ Install...
59
219
  ```bash
60
220
  $ bundle
221
+ ```
61
222
 
223
+ After bundling, install the migration.
224
+ ```
62
225
  # install migration, possible to edit and add attributes or indexes as needed.
63
226
  $ bundle exec rails g postburner:install
64
227
  ```
65
228
 
66
- Add a `config/initializers/backburner.rb` with option found [here](https://github.com/nesquena/backburner#configuration).
229
+ Inspect `XXX_create_postburner_jobs.rb`. (The template is [here](-/blob/master/lib/generators/postburner/install/templates/migrations/create_postburner_jobs.rb.erb)).The required attributes are in there, but add more if you would like to use them,
230
+ or do it in a separate migration. By default, several indexes are added
67
231
 
68
- Set `Backburner` for `ActiveJob`
232
+ Because `Postburner` is built on `Backburner`, add a `config/initializers/backburner.rb` with option found [here](https://github.com/nesquena/backburner#configuration).
233
+ ```ruby
234
+ Backburner.configure do |config|
235
+ config.beanstalk_url = "beanstalk://127.0.0.1"
236
+ config.tube_namespace = "some.app.production"
237
+ config.namespace_separator = "."
238
+ config.on_error = lambda { |e| puts e }
239
+ config.max_job_retries = 3 # default 0 retries
240
+ config.retry_delay = 2 # default 5 seconds
241
+ config.retry_delay_proc = lambda { |min_retry_delay, num_retries| min_retry_delay + (num_retries ** 3) }
242
+ config.default_priority = 65536
243
+ config.respond_timeout = 120
244
+ config.default_worker = Backburner::Workers::Simple
245
+ config.logger = Logger.new(STDOUT)
246
+ config.primary_queue = "backburner-jobs"
247
+ config.priority_labels = { :custom => 50, :useless => 1000 }
248
+ config.reserve_timeout = nil
249
+ config.job_serializer_proc = lambda { |body| JSON.dump(body) }
250
+ config.job_parser_proc = lambda { |body| JSON.parse(body) }
251
+ end
252
+ ```
253
+
254
+ Finally, set `Backburner` for `ActiveJob`
69
255
  ```
70
256
  # config/application.rb
71
257
  config.active_job.queue_adapter = :backburner
72
258
  ```
73
259
 
74
260
  Postburner may later provide an adapter, but we recommend using `Postburner::Job` classes
75
- directyly.
261
+ directly.
76
262
 
77
263
  Add jobs to `app/jobs/`. There currently is no generator.
264
+
78
265
  ```ruby
79
266
  # app/jobs/run_donation.rb
80
-
81
267
  class RunDonation < Postburner::Job
82
268
  queue 'critical'
83
269
  queue_priority 0 # 0 is highest priority
@@ -88,6 +274,34 @@ class RunDonation < Postburner::Job
88
274
  # also have access to self.args
89
275
  end
90
276
  end
277
+ ```
278
+
279
+ ### Comparison to Backburner
280
+
281
+ Compared to plain [Backburner](https://github.com/nesquena/backburner),
282
+ Postburner adds:
283
+ - Database Jobs for inspection, linking, auditing, removal (and deletion)
284
+ - Direct access to associated [Beanstalk](https://beanstalkd.github.io/) (via [beaneater](https://github.com/beanstalkd/beaneater))
285
+ - Job Statistics (lag, attempts, logs, tracked errors)
286
+ - Convenience methods to clear tubes, stats, and connections for Beanstalk.
287
+
288
+ Otherwise, Postburner tries to be a super simple layer on `Backburner::Queue`
289
+ and `ActiveRecord`. Every tool with either of those are available in
290
+ `Postburner::Job`s.
291
+
292
+ Comes with a mountable interface that can be password protected with whatever
293
+ authentication you use in your project.
294
+
295
+ ### Comparison to Que
296
+
297
+ Postburner meant to be a replacement/upgrade for [Que](https://github.com/que-rb/que).
298
+ However, if you need something faster and backed with ACID compliance, check out Que.
299
+
300
+ Postburner has some additional features such as retained jobs after processing,
301
+ stats, per job logging, etc.
302
+
303
+ Postburner is meant to be simpler than `Que`. Que is incredible, but jobs should
304
+ be simple so the logic and history can be transparent.
91
305
 
92
306
  ## Contributing
93
307
  Submit a pull request. Follow conventions of the project. Be nice.
@@ -102,7 +316,6 @@ Submit a pull request. Follow conventions of the project. Be nice.
102
316
  - Job generator
103
317
  - Build file in app/jobs
104
318
  - Inherit from Postburner::Job
105
- - Job generator
106
319
  - Add before/after/around hooks
107
320
  - Add destroy, and remove actions on show page
108
321
  - Clear tubes.
@@ -112,5 +325,23 @@ Submit a pull request. Follow conventions of the project. Be nice.
112
325
  - Add logging with Job.args in backburner logs
113
326
  - MAYBE - ActiveJob adapter
114
327
 
328
+
329
+ ### Running in Development
330
+
331
+ ```
332
+ cd test/dummy
333
+ bundle exec backburner
334
+ ```
335
+
336
+ ### Releasing
337
+
338
+ 1. `gem bump -v minor -t`
339
+ Where <minor> can be: major|minor|patch|pre|release or a version number
340
+ 2. Edit `CHANGELOG.md` with details from `git log --oneline`
341
+ 3. `git commit --amend`
342
+ 4. `gem release -k nac -p`
343
+ Where <nac> is an authorized key with push capabilities.
344
+
345
+
115
346
  ## License
116
347
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -1,4 +1,17 @@
1
1
  module Postburner
2
+ # Must implement a perform method, if an exception is raised the job
3
+ # doesn't complete.
4
+ #
5
+ # Job won't run unless queued_at is set, and is set to a time prior to
6
+ # the time the job runs.
7
+ #
8
+ # TODO Mailer uses ActiveJob::Arguments... probably should use that here
9
+ # as well. Decided how to migrate existing jobs or allow both - Opt to
10
+ # allow both: rescue from ActiveJob::DeserializationError and use the
11
+ # plain hash, probably log it too.
12
+ #
13
+ # Add `cancelled_at` that blocks jobs performing if present.
14
+ #
2
15
  class Job < ApplicationRecord
3
16
  include Backburner::Queue
4
17
 
@@ -17,6 +30,8 @@ module Postburner
17
30
  def queue!(options={})
18
31
  return if self.queued_at.present? && self.bkid.present?
19
32
 
33
+ self.save!
34
+
20
35
  case
21
36
  when options[:at].present?
22
37
  # this is rudimentary, add error handling
@@ -33,14 +48,15 @@ module Postburner
33
48
  # tube: backburner.worker.queue.backburner-jobs
34
49
  #
35
50
  def self.perform(id, _={})
51
+ job = nil
36
52
  begin
37
53
  job = self.find(id)
38
- job.perform!(job.args)
39
54
  rescue ActiveRecord::RecordNotFound => e
40
55
  Rails.logger.warn <<-MSG
41
56
  [Postburner::Job] [#{id}] Not Found.
42
57
  MSG
43
58
  end
59
+ job&.perform!(job.args)
44
60
  end
45
61
 
46
62
  def perform!(args={})
@@ -55,52 +71,63 @@ module Postburner
55
71
  )
56
72
 
57
73
  begin
74
+ if self.queued_at.nil?
75
+ self.log! "Not Queued", level: :error
76
+ return
77
+ end
78
+
79
+ if self.queued_at > Time.zone.now
80
+ self.log! "Future Queued", level: :error
81
+ return
82
+ end
83
+
58
84
  if self.processed_at.present?
59
- self.log "Already Processed", level: :error
60
- update_column :logs, self.logs
85
+ self.log! "Already Processed", level: :error
61
86
  self.delete!
62
87
  return
63
88
  end
64
89
 
65
90
  if self.removed_at.present?
66
- self.log "Removed", level: :error
67
- update_column :logs, self.logs
91
+ self.log! "Removed", level: :error
68
92
  return
69
93
  end
70
94
 
71
95
  if self.run_at && self.run_at > Time.zone.now
72
96
  response = self.insert! delay: self.run_at - Time.zone.now
73
- self.log "PREMATURE; RE-INSERTED: #{response}"
74
- update_column :logs, self.logs
97
+ self.log! "PREMATURE; RE-INSERTED: #{response}"
75
98
  return
76
99
  end
77
100
 
78
- self.log('START')
101
+ self.log!('START')
79
102
 
80
- self.perform(args)
103
+ begin
104
+ self.perform(args)
105
+ rescue Exception => exception
106
+ self.persist_metadata!
107
+ self.log! '[Postburner] Exception raised during perform prevented completion.'
108
+ raise exception
109
+ end
81
110
 
82
- self.log('DONE')
111
+ self.log!('DONE')
112
+
113
+ begin
114
+ now = Time.zone.now
115
+ _duration = (now - self.processing_at) * 1000 rescue nil
116
+ persist_metadata!(
117
+ processed_at: now,
118
+ duration: _duration,
119
+ )
120
+ rescue Exception => e
121
+ self.log_exception!(e)
122
+ self.log! '[Postburner] Could not set data after processing.'
123
+ # TODO README doesn't retry if Postburner is to blame
124
+ end
83
125
 
84
- rescue Exception => e
85
- self.log_exception(e)
126
+ rescue Exception => exception
127
+ self.log_exception!(exception)
128
+ raise exception
86
129
  end
87
130
 
88
- begin
89
- now = Time.zone.now
90
- _duration = (now - self.processing_at) * 1000 rescue nil
91
- self.update_columns(
92
- processed_at: now,
93
- duration: _duration,
94
- errata: self.errata,
95
- error_count: self.errata.length,
96
- logs: self.logs,
97
- log_count: self.logs.length,
98
- )
99
- rescue Exception => e
100
- raise e if Rails.env.development? || Rails.env.production?
101
- self.log_exception(e)
102
- update_column :errata, self.errata
103
- end
104
131
  end
105
132
 
106
133
  def delete!
@@ -122,9 +149,12 @@ module Postburner
122
149
  end
123
150
 
124
151
  def remove!
125
- return false if self.attempts.any?
126
- self.delete!
127
- self.update_column(removed_at: Time.zone.now)
152
+ if self.beanstalk_job
153
+ self.delete!
154
+ self.update_column(:removed_at, Time.zone.now)
155
+ else
156
+ false
157
+ end
128
158
  end
129
159
 
130
160
  def beanstalk_job
@@ -152,28 +182,61 @@ module Postburner
152
182
  ]
153
183
  end
154
184
 
185
+ def log_exception!(exception)
186
+ self.log_exception(exception)
187
+ self.update_column :errata, self.errata
188
+ end
189
+
155
190
  def log(message, options={})
156
191
  options[:level] ||= :info
157
192
  options[:level] = :error unless LOG_LEVELS.member?(options[:level])
158
193
 
159
194
  self.logs << [
160
- Time.zone.now,
161
- options[:level],
162
- message,
195
+ Time.zone.now, # time
196
+ {
197
+ level: options[:level], # level
198
+ message: message, # message
199
+ elapsed: self.elapsed_ms, # ms from start
200
+ }
163
201
  ]
164
202
  end
165
203
 
204
+ # ms from attempting_at
205
+ #
206
+ def elapsed_ms
207
+ return unless self.attempting_at
208
+ (Time.zone.now - self.attempting_at) * 1000
209
+ end
210
+
211
+ def log!(message, options={})
212
+ self.log(message, options)
213
+ self.update_column :logs, self.logs
214
+ end
215
+
166
216
  private
167
217
 
218
+ def persist_metadata!(data={})
219
+ self.update_columns({
220
+ errata: self.errata,
221
+ error_count: self.errata.length,
222
+ logs: self.logs,
223
+ log_count: self.logs.length,
224
+ }.merge(data))
225
+ end
226
+
227
+
168
228
  def insert!(options={})
169
- response = Backburner::Worker.enqueue(Postburner::Job, self.id, options)
229
+ response = Backburner::Worker.enqueue(
230
+ Postburner::Job,
231
+ self.id,
232
+ options
233
+ )
170
234
 
171
235
  self.log("QUEUED: #{response}")
172
236
 
173
- update_columns(
237
+ persist_metadata!(
174
238
  queued_at: Time.zone.now,
175
239
  bkid: response[:id],
176
- logs: self.logs,
177
240
  )
178
241
 
179
242
  response
@@ -1,14 +1,14 @@
1
1
  module Postburner
2
2
  # Send a mailer, tracked.
3
3
  #
4
- # Postburner::Mailer.deliver(UserMailer, :welcome, user_id: 1)
5
- # Postburner::Mailer.deliver(UserMailer, :welcome, user_id: 1).queue! at: Time.zone.now + 1.day
6
- # Postburner::Mailer.deliver(UserMailer, :welcome, user_id: 1).queue! delay: 5.minutes
4
+ # Postburner::Mailer.deliver(UserMailer, :welcome).with(user_id: 1)
5
+ # Postburner::Mailer.deliver(UserMailer, :welcome).with(user_id: 1).queue! at: Time.zone.now + 1.day
6
+ # Postburner::Mailer.deliver(UserMailer, :welcome).with(user_id: 1).queue! delay: 5.minutes
7
7
  class Mailer < Job
8
8
  #queue 'mailers'
9
9
 
10
- def self.deliver!(mailer, action)
11
- job = self.create!(
10
+ def self.delivery(mailer, action)
11
+ job = self.new(
12
12
  args: {
13
13
  mailer: mailer.to_s,
14
14
  action: action.to_s,
@@ -17,9 +17,62 @@ module Postburner
17
17
  job
18
18
  end
19
19
 
20
- def process(args)
21
- cls = args['mailer'].constantize
22
- cls.send(args['action']).deliver_now
20
+ def self.delivery!(mailer, action)
21
+ job = self.delivery(mailer, action)
22
+ job.save!
23
+ job
24
+ end
25
+
26
+ # Similar to ActionMailer #with - set the parameters
27
+ #
28
+ def with(params={})
29
+ self.args.merge!(
30
+ 'params' => ActiveJob::Arguments.serialize(params)
31
+ )
32
+ self
33
+ end
34
+
35
+ def with!(params={})
36
+ self.with(params)
37
+ self.save!
38
+ self
39
+ end
40
+
41
+ # Build the mail but don't send.
42
+ #
43
+ # Optional `args` argument for testing convenience.
44
+ #
45
+ def assemble
46
+ mail = self.mailer.with(self.params).send(self.action)
47
+ mail
48
+ end
49
+
50
+ # Get the mailer class.
51
+ #
52
+ def mailer
53
+ self.args['mailer'].constantize
54
+ end
55
+
56
+ # Get the mailer action as a symbol.
57
+ #
58
+ def action
59
+ self.args['action']&.to_sym
60
+ end
61
+
62
+ # Get the deserialized params.
63
+ #
64
+ def params
65
+ ActiveJob::Arguments.deserialize(self.args['params']).to_h
66
+ end
67
+
68
+ def perform(args)
69
+ self.log! "Building"
70
+ mail = self.assemble
71
+
72
+ self.log! "Delivering"
73
+ mail.deliver_now
74
+
75
+ self.log! "Delivered"
23
76
  end
24
77
  end
25
78
  end
@@ -1,3 +1,3 @@
1
1
  module Postburner
2
- VERSION = '0.3.2'
2
+ VERSION = '0.6.0'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: postburner
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.2
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matt Smith
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-05-07 00:00:00.000000000 Z
11
+ date: 2021-11-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -128,7 +128,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
128
128
  - !ruby/object:Gem::Version
129
129
  version: '0'
130
130
  requirements: []
131
- rubygems_version: 3.1.4
131
+ rubygems_version: 3.1.6
132
132
  signing_key:
133
133
  specification_version: 4
134
134
  summary: Postgres backed beanstalkd queue via backburner