active_job_store 0.1.0 → 0.2.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: 2f576f48581d1561a8deb8c41fdbc1f89070bc7d48b5f3c6ece7e6de39c33747
4
- data.tar.gz: a7807764c2199023daf083ee48ea3c5ded230f566bc07e25664e420ed048725c
3
+ metadata.gz: 913b4d81b0dfb938f013f907696dc04354a106818140d25003ae44ab4e4ed9f9
4
+ data.tar.gz: 39874892b9ee2443311d54cfedf7589697959c5ccb4184d8d934d67960838c7d
5
5
  SHA512:
6
- metadata.gz: aa62be0ecc3e9ff53ccc8d3e1ff02202383323b0fba7075493bd5c5ca010e2393a627a7bd9e9b04edadc59c799d4fd7d630b81dbfcb3058d05601dce520777c8
7
- data.tar.gz: 9436383c18415b52c7226816d62040cd8b8846c3385bac23681fa9730aadc98deebe086dfdfd2b8b33cedcbc5bdcffa951ae6f96725a3a49e74aa45770c0810e
6
+ metadata.gz: 18b9c5696a4b4092ae9ea5d6ae8ed74ed45aaef567804f53a7b59fee306038f6ce2e63f0832b9bdc6f6ad61dbb7d69b81239c5801fe39db2b163be50f0e468ad
7
+ data.tar.gz: 0ad46829f63b6a6347812df19f0b0a3ef8b9e0c092ecf6f8f1743b08ddfeef9077735cdcb19690421222ca9557b2c6338b9e122d7e747f76a50d3357d62e3534
data/README.md CHANGED
@@ -1,11 +1,20 @@
1
1
  # ActiveJob Store
2
2
 
3
+ [![Gem Version](https://badge.fury.io/rb/active_job_store.svg)](https://badge.fury.io/rb/active_job_store)
4
+ [![Specs Rails 7.0](https://github.com/blocknotes/active_job_store/actions/workflows/specs_70.yml/badge.svg)](https://github.com/blocknotes/active_job_store/actions/workflows/specs_70.yml)
5
+ [![Linters](https://github.com/blocknotes/active_job_store/actions/workflows/linters.yml/badge.svg)](https://github.com/blocknotes/active_job_store/actions/workflows/linters.yml)
6
+
3
7
  Persist job execution information on a support model `ActiveJobStore::Record`.
4
8
 
5
9
  It can be useful to:
6
10
  - improve jobs logging capabilities;
7
11
  - query historical data about job executions;
8
- - extract job's statistical data.
12
+ - extract job's statistical data;
13
+ - track a job's state / set progress value / add custom data to the jobs.
14
+
15
+ Support some customizations:
16
+ - set custom data attributes (via `active_job_store_custom_data` accessor);
17
+ - format the job result to store (overriding `active_job_store_format_result` method).
9
18
 
10
19
  ## Installation
11
20
 
@@ -21,7 +30,7 @@ It can be useful to:
21
30
  SomeJob.perform_now(123)
22
31
  SomeJob.perform_later(456)
23
32
  SomeJob.set(wait: 1.minute).perform_later(789)
24
- "SomeJob", "completed"]]
33
+
25
34
  SomeJob.job_executions.first
26
35
  # => #<ActiveJobStore::Record:0x00000001120f6320
27
36
  # id: 1,
@@ -59,7 +68,7 @@ SomeJob.job_executions.where(started_at: 16.minutes.ago...).pluck(:job_id, :resu
59
68
  # ["267e087e-cfa7-4c88-8d3b-9d40f912733f", "some_result", Wed, 09 Nov 2022 21:13:18.011484000 UTC +00:00]]
60
69
  ```
61
70
 
62
- Some statistics:
71
+ Some statistics on completed jobs:
63
72
 
64
73
  ```rb
65
74
  SomeJob.job_executions.completed.map { |job| { id: job.id, execution_time: job.completed_at - job.started_at, started_at: job.started_at } }
@@ -70,7 +79,28 @@ SomeJob.job_executions.completed.map { |job| { id: job.id, execution_time: job.c
70
79
 
71
80
  ## Customizations
72
81
 
73
- If you need to store custom data, use `active_job_store_custom_data` accessor:
82
+ To store the custom data (ex. a progress value):
83
+
84
+ ```rb
85
+ class AnotherJob < ApplicationJob
86
+ include ActiveJobStore
87
+
88
+ def perform
89
+ # do something...
90
+ save_job_custom_data(progress: 0.5)
91
+ # do something else...
92
+ save_job_custom_data(progress: 1.0)
93
+
94
+ 'some_result'
95
+ end
96
+ end
97
+
98
+ # Usage example:
99
+ AnotherJob.perform_later(456)
100
+ AnotherJob.job_executions.last.custom_data['progress'] # 1.0 (at the end)
101
+ ```
102
+
103
+ If you need to manipulate the custom data, there is the `active_job_store_custom_data` accessor:
74
104
 
75
105
  ```rb
76
106
  class AnotherJob < ApplicationJob
@@ -87,6 +117,7 @@ class AnotherJob < ApplicationJob
87
117
  end
88
118
  end
89
119
 
120
+ # Usage example:
90
121
  AnotherJob.perform_now(123)
91
122
  AnotherJob.job_executions.last.custom_data
92
123
  # => [{"time"=>"2022-11-09T21:20:57.580Z", "message"=>"SomeJob step 1"}, {"time"=>"2022-11-09T21:20:58.581Z", "message"=>"SomeJob step 2"}]
@@ -107,6 +138,7 @@ class AnotherJob < ApplicationJob
107
138
  end
108
139
  end
109
140
 
141
+ # Usage example:
110
142
  AnotherJob.perform_now(123)
111
143
  AnotherJob.job_executions.last.result
112
144
  # => 84
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveJobStore
4
+ class Store
5
+ DETAILS_ATTRS = %w[exception_executions executions priority queue_name scheduled_at timezone].freeze
6
+
7
+ attr_reader :record
8
+
9
+ def job_competed!(result:, custom_data:)
10
+ record.update!(state: :completed, completed_at: Time.current, result: result, custom_data: custom_data)
11
+ record
12
+ end
13
+
14
+ def job_enqueued!
15
+ record.lock! # NOTE: needed to avoid update conflicts with perform when setting the state to enqueued
16
+ yield
17
+ record.update!(state: :enqueued, enqueued_at: Time.current)
18
+ record
19
+ end
20
+
21
+ def job_failed!(exception:, custom_data:)
22
+ record.update!(state: :error, exception: exception.inspect, custom_data: custom_data)
23
+ record
24
+ end
25
+
26
+ def job_started!
27
+ record.update!(state: :started, started_at: Time.current)
28
+ record
29
+ end
30
+
31
+ def prepare_record_on_enqueue(job)
32
+ @record = ::ActiveJobStore::Record.find_or_create_by!(record_reference(job)) do |record|
33
+ record.arguments = job.arguments
34
+ record.details = DETAILS_ATTRS.zip(DETAILS_ATTRS.map { job.send(_1) }).to_h
35
+ record.state = :initialized
36
+ end
37
+ end
38
+
39
+ def prepare_record_on_perform(job)
40
+ @record = ::ActiveJobStore::Record.find_or_initialize_by(record_reference(job)) do |record|
41
+ record.arguments = job.arguments
42
+ end
43
+ record.details = DETAILS_ATTRS.zip(DETAILS_ATTRS.map { job.send(_1) }).to_h
44
+ record
45
+ end
46
+
47
+ def update_job_custom_data(custom_data)
48
+ record.update!(custom_data: custom_data)
49
+ end
50
+
51
+ private
52
+
53
+ def record_reference(job)
54
+ {
55
+ job_id: job.job_id,
56
+ job_class: job.class.to_s
57
+ }
58
+ end
59
+ end
60
+ end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :nocov:
3
4
  module ActiveJobStore
4
- VERSION = '0.1.0'
5
+ VERSION = '0.2.0'
5
6
  end
7
+ # :nocov:
@@ -1,10 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'active_job_store/engine'
4
+ require_relative 'active_job_store/store'
4
5
 
5
6
  module ActiveJobStore
6
- IGNORE_ATTRS = %w[arguments job_id successfully_enqueued].freeze
7
-
8
7
  attr_accessor :active_job_store_custom_data
9
8
 
10
9
  class << self
@@ -12,26 +11,20 @@ module ActiveJobStore
12
11
  base.extend(ClassMethods)
13
12
 
14
13
  base.around_enqueue do |job, block|
15
- store_record = ::ActiveJobStore::Record.find_or_create_by!(job.active_job_store_reference) do |record|
16
- record.arguments = job.arguments
17
- record.details = job.as_json.except(*IGNORE_ATTRS)
18
- record.state = :initialized
14
+ store.prepare_record_on_enqueue(job)
15
+ store.job_enqueued! do
16
+ block.call
19
17
  end
20
- store_record.lock! # NOTE: needed to avoid update conflicts with perform when setting the state to enqueued
21
- block.call
22
- store_record.update!(state: :enqueued, enqueued_at: Time.current)
23
18
  end
24
19
 
25
20
  base.around_perform do |job, block|
26
- store_record = ::ActiveJobStore::Record.find_or_initialize_by(job.active_job_store_reference) do |record|
27
- record.arguments = job.arguments
28
- end
29
- store_record.update!(details: job.as_json.except(*IGNORE_ATTRS), state: :started, started_at: Time.current)
21
+ store.prepare_record_on_perform(job)
22
+ store.job_started!
30
23
  result = block.call
31
- result_data = job.active_job_store_format_result(result)
32
- store_record.update!(state: :completed, completed_at: Time.current, result: result_data, custom_data: active_job_store_custom_data)
24
+ formatted_result = job.active_job_store_format_result(result)
25
+ store.job_competed!(custom_data: active_job_store_custom_data, result: formatted_result)
33
26
  rescue StandardError => e
34
- store_record.update!(state: :error, exception: e.inspect)
27
+ store.job_failed!(exception: e, custom_data: active_job_store_custom_data)
35
28
  raise
36
29
  end
37
30
  end
@@ -41,8 +34,9 @@ module ActiveJobStore
41
34
  result
42
35
  end
43
36
 
44
- def active_job_store_reference
45
- { job_id: job_id, job_class: self.class.to_s }
37
+ def save_job_custom_data(custom_data = nil)
38
+ self.active_job_store_custom_data = custom_data if custom_data
39
+ store.update_job_custom_data(active_job_store_custom_data)
46
40
  end
47
41
 
48
42
  module ClassMethods
@@ -50,4 +44,10 @@ module ActiveJobStore
50
44
  ::ActiveJobStore::Record.where(job_class: to_s)
51
45
  end
52
46
  end
47
+
48
+ private
49
+
50
+ def store
51
+ @store ||= ::ActiveJobStore::Store.new
52
+ end
53
53
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_job_store
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mattia Roccoberton
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-11-09 00:00:00.000000000 Z
11
+ date: 2022-11-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activejob
@@ -36,6 +36,7 @@ files:
36
36
  - db/migrate/20221101010101_create_active_job_store.rb
37
37
  - lib/active_job_store.rb
38
38
  - lib/active_job_store/engine.rb
39
+ - lib/active_job_store/store.rb
39
40
  - lib/active_job_store/version.rb
40
41
  homepage: https://github.com/blocknotes/active_job_store
41
42
  licenses: