chrono_forge 0.1.0 → 0.1.1

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: 88cee304794f2d186c2103563b8f98c08a00cc1d9a351ae206c35e34bacdcf61
4
- data.tar.gz: dee282b43b65a4846797629903141329a3f094fd009416848cb0f5b33ed3f68e
3
+ metadata.gz: 0303e4144f2e7546095c0828a02fa810138b3963603ee33810232cc4444a7ee8
4
+ data.tar.gz: d1805992395168a7873b34c7264518e0b6d740fcdef23dd5fce2ac2cf686bb3a
5
5
  SHA512:
6
- metadata.gz: ba3c5937b30c3ea0a46135a30f2cc46d59e430bb75f41bda1fc50d352a4bbe615532b1de0526eed819c824d5851cac9812b2f7de1ffb930a72a380045312692d
7
- data.tar.gz: 3db298d79b40715bc6cbcb54031b5ff37dd549c8361e70fb1584431d62010d14cffe7ca5b9e7e36e79a2513f0524882eae6ff9295fae905b342506783160a0c0
6
+ metadata.gz: 64d20d4a723c5d751c60415f1461e5e0e581a6401a084d4f6c83cc9c0813f7b7163c87dff916e4af628e7742291cecfc9786d6afc922fc046577713d2db94f13
7
+ data.tar.gz: 0bcb5a5c37f3fd0e241e0e3d60a358b4fd7d2700c1960a8148b5b12dca147ce8f0db07c7e435d314c7b6bd537e3830ad1f0fc131f33e2cee1bfbf0d60c4a7b63
data/README.md CHANGED
@@ -151,17 +151,6 @@ class MyWorkflow < ApplicationJob
151
151
  end
152
152
  ```
153
153
 
154
- ### Cleanup
155
-
156
- ChronoForge includes built-in cleanup methods for managing old workflow data:
157
-
158
- ```ruby
159
- # Clean up old workflows and logs
160
- ChronoForge::Workflow.cleanup_old_logs(retention_period: 30.days)
161
- ChronoForge::ExecutionLog.cleanup_old_logs(retention_period: 30.days)
162
- ChronoForge::ErrorLog.cleanup_old_logs(retention_period: 30.days)
163
- ```
164
-
165
154
  ## Testing
166
155
 
167
156
  ChronoForge is designed to be easily testable using [ChaoticJob](https://github.com/fractaledmind/chaotic_job), a testing framework that makes it simple to test complex job workflows. Here's how to set up your test environment:
@@ -186,7 +175,7 @@ require 'chaotic_job'
186
175
  Example test:
187
176
 
188
177
  ```ruby
189
- class WorkflowTest < Minitest::Test
178
+ class WorkflowTest < ActiveJob::TestCase
190
179
  include ChaoticJob::Helpers
191
180
 
192
181
  def test_workflow_completion
@@ -210,7 +199,6 @@ end
210
199
  ChaoticJob provides several helpful methods for testing workflows:
211
200
 
212
201
  - `perform_all_jobs`: Processes all enqueued jobs, including those enqueued during job execution
213
- - `enqueued_jobs`: Returns the current number of jobs in the queue
214
202
 
215
203
  For testing with specific job processing libraries like Sidekiq or Delayed Job, you can still use their respective testing modes, but ChaoticJob is recommended for testing ChronoForge workflows as it better handles the complexities of nested job scheduling and wait states.
216
204
 
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: ..
3
3
  specs:
4
- chrono_forge (0.0.1)
4
+ chrono_forge (0.1.0)
5
5
  activejob
6
6
  activerecord
7
7
  zeitwerk
@@ -27,10 +27,5 @@ module ChronoForge
27
27
  self.table_name = "chrono_forge_error_logs"
28
28
 
29
29
  belongs_to :workflow
30
-
31
- # Cleanup method
32
- def self.cleanup_old_logs(retention_period: 30.days)
33
- where("created_at < ?", retention_period.ago).delete_all
34
- end
35
30
  end
36
31
  end
@@ -38,10 +38,5 @@ module ChronoForge
38
38
  completed
39
39
  failed
40
40
  ]
41
-
42
- # Cleanup method
43
- def self.cleanup_old_logs(retention_period: 30.days)
44
- where("created_at < ?", retention_period.ago).delete_all
45
- end
46
41
  end
47
42
  end
@@ -6,8 +6,8 @@ module ChronoForge
6
6
  module WaitUntil
7
7
  def wait_until(condition, **options)
8
8
  # Default timeout and check interval
9
- timeout = options[:timeout] || 10.seconds
10
- check_interval = options[:check_interval] || 1.second
9
+ timeout = options[:timeout] || 1.hour
10
+ check_interval = options[:check_interval] || 15.minutes
11
11
 
12
12
  # Find or create execution log
13
13
  step_name = "wait_until$#{condition}"
@@ -89,8 +89,8 @@ module ChronoForge
89
89
  state: :failed,
90
90
  metadata: metadata.merge("result" => nil)
91
91
  )
92
- Rails.logger.warn { "Timeout reached for condition #{condition}. Condition not met within the timeout period." }
93
- raise WaitConditionNotMet, "Condition not met within timeout period"
92
+ Rails.logger.warn { "Timeout reached for condition '#{condition}'." }
93
+ raise WaitConditionNotMet, "Condition '#{condition}' not met within timeout period"
94
94
  end
95
95
 
96
96
  # Reschedule with delay
@@ -13,7 +13,7 @@ module ChronoForge
13
13
  wait_duration = BACKOFF_STRATEGY[attempt] || BACKOFF_STRATEGY.last
14
14
 
15
15
  # Schedule with exponential backoff
16
- workflow.job_klass.constantize
16
+ workflow.job_klass
17
17
  .set(wait: wait_duration)
18
18
  .perform_later(
19
19
  workflow.key,
@@ -10,7 +10,7 @@ module ChronoForge
10
10
 
11
11
  include Methods
12
12
 
13
- def perform(key, attempt: 0, **kwargs)
13
+ def perform(key, attempt: 0, options: {}, **kwargs)
14
14
  # Prevent excessive retries
15
15
  if attempt >= self.class::RetryStrategy.max_attempts
16
16
  Rails.logger.error { "Max attempts reached for job #{key}" }
@@ -18,7 +18,7 @@ module ChronoForge
18
18
  end
19
19
 
20
20
  # Find or create job with comprehensive tracking
21
- setup_workflow(key, kwargs)
21
+ setup_workflow(key, options, kwargs)
22
22
 
23
23
  begin
24
24
  # Skip if workflow cannot be executed
@@ -47,13 +47,13 @@ module ChronoForge
47
47
  nil
48
48
  rescue => e
49
49
  Rails.logger.error { "An error occurred during execution of #{key}" }
50
- self.class::ExecutionTracker.track_error(workflow, e)
50
+ error_log = self.class::ExecutionTracker.track_error(workflow, e)
51
51
 
52
52
  # Retry if applicable
53
53
  if should_retry?(e, attempt)
54
54
  self.class::RetryStrategy.schedule_retry(workflow, attempt: attempt)
55
55
  else
56
- workflow.failed!
56
+ fail_workflow! error_log
57
57
  end
58
58
  ensure
59
59
  context.save!
@@ -71,9 +71,6 @@ module ChronoForge
71
71
  step_name: "$workflow_completion$"
72
72
  ) do |log|
73
73
  log.started_at = Time.current
74
- log.metadata = {
75
- workflow_id: workflow.id
76
- }
77
74
  end
78
75
 
79
76
  begin
@@ -104,14 +101,53 @@ module ChronoForge
104
101
  end
105
102
  end
106
103
 
107
- def setup_workflow(key, kwargs)
108
- @workflow = find_workflow(key, kwargs)
104
+ def fail_workflow!(error_log)
105
+ # Create an execution log for workflow failure
106
+ execution_log = ExecutionLog.create_or_find_by!(
107
+ workflow: workflow,
108
+ step_name: "$workflow_failure$"
109
+ ) do |log|
110
+ log.started_at = Time.current
111
+ log.metadata = {
112
+ error_log_id: error_log.id
113
+ }
114
+ end
115
+
116
+ begin
117
+ execution_log.update!(
118
+ attempts: execution_log.attempts + 1,
119
+ last_executed_at: Time.current
120
+ )
121
+
122
+ workflow.failed!
123
+
124
+ # Mark execution log as completed
125
+ execution_log.update!(
126
+ state: :completed,
127
+ completed_at: Time.current
128
+ )
129
+
130
+ # Return the execution log for tracking
131
+ execution_log
132
+ rescue => e
133
+ # Log any completion errors
134
+ execution_log.update!(
135
+ state: :failed,
136
+ error_message: e.message,
137
+ error_class: e.class.name
138
+ )
139
+ raise
140
+ end
141
+ end
142
+
143
+ def setup_workflow(key, options, kwargs)
144
+ @workflow = find_workflow(key, options, kwargs)
109
145
  @context = Context.new(@workflow)
110
146
  end
111
147
 
112
- def find_workflow(key, kwargs)
113
- Workflow.create_or_find_by!(key: key) do |workflow|
114
- workflow.job_klass = self.class.to_s
148
+ def find_workflow(key, options, kwargs)
149
+ Workflow.create_or_find_by!(job_class: self.class.to_s, key: key) do |workflow|
150
+ workflow.options = options
115
151
  workflow.kwargs = kwargs
116
152
  workflow.started_at = Time.current
117
153
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ChronoForge
4
- VERSION = "0.1.0"
4
+ VERSION = "0.1.1"
5
5
  end
@@ -7,9 +7,10 @@
7
7
  # id :integer not null, primary key
8
8
  # completed_at :datetime
9
9
  # context :json not null
10
- # job_klass :string not null
10
+ # job_class :string not null
11
11
  # key :string not null
12
12
  # kwargs :json not null
13
+ # options :json not null
13
14
  # locked_at :datetime
14
15
  # started_at :datetime
15
16
  # state :integer default("idle"), not null
@@ -24,8 +25,8 @@ module ChronoForge
24
25
  class Workflow < ActiveRecord::Base
25
26
  self.table_name = "chrono_forge_workflows"
26
27
 
27
- has_many :execution_logs
28
- has_many :error_logs
28
+ has_many :execution_logs, -> { order(id: :asc) }
29
+ has_many :error_logs, -> { order(id: :asc) }
29
30
 
30
31
  enum :state, %i[
31
32
  idle
@@ -35,16 +36,15 @@ module ChronoForge
35
36
  stalled
36
37
  ]
37
38
 
38
- # Cleanup method
39
- def self.cleanup_old_logs(retention_period: 30.days)
40
- where("created_at < ?", retention_period.ago).delete_all
41
- end
42
-
43
39
  # Serialization for metadata
44
40
  serialize :metadata, coder: JSON
45
41
 
46
42
  def executable?
47
43
  idle? || running?
48
44
  end
45
+
46
+ def job_klass
47
+ job_class.constantize
48
+ end
49
49
  end
50
50
  end
data/lib/chrono_forge.rb CHANGED
@@ -4,7 +4,7 @@ require "zeitwerk"
4
4
  require "active_record"
5
5
  require "active_job"
6
6
 
7
- module Chronoforge
7
+ module ChronoForge
8
8
  Loader = Zeitwerk::Loader.for_gem.tap do |loader|
9
9
  loader.ignore("#{__dir__}/generators")
10
10
  loader.setup
@@ -3,8 +3,8 @@
3
3
  class InstallChronoForge < ActiveRecord::Migration[7.1]
4
4
  def change
5
5
  create_table :chrono_forge_workflows do |t|
6
- t.string :key, null: false, index: {unique: true}
7
- t.string :job_klass, null: false
6
+ t.string :key, null: false, index: true
7
+ t.string :job_class, null: false
8
8
 
9
9
  if t.respond_to?(:jsonb)
10
10
  t.jsonb :kwargs, null: false, default: {}
@@ -24,6 +24,7 @@ class InstallChronoForge < ActiveRecord::Migration[7.1]
24
24
  t.datetime :completed_at
25
25
 
26
26
  t.timestamps
27
+ t.index %i[job_class key], unique: true
27
28
  end
28
29
 
29
30
  create_table :chrono_forge_execution_logs do |t|
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: chrono_forge
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stefan Froelich
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-12-21 00:00:00.000000000 Z
11
+ date: 2025-04-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord