active_job_store 0.1.0 → 0.2.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: 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: