rocketjob 1.3.0 → 2.0.0.rc1
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 +4 -4
- data/LICENSE.txt +201 -0
- data/README.md +15 -10
- data/bin/rocketjob +3 -1
- data/bin/rocketjob_perf +92 -0
- data/lib/rocket_job/cli.rb +71 -31
- data/lib/rocket_job/config.rb +21 -23
- data/lib/rocket_job/dirmon_entry.rb +63 -45
- data/lib/rocket_job/extensions/aasm.rb +56 -0
- data/lib/rocket_job/extensions/mongo.rb +23 -0
- data/lib/rocket_job/job.rb +9 -433
- data/lib/rocket_job/jobs/dirmon_job.rb +20 -20
- data/lib/rocket_job/jobs/simple_job.rb +12 -0
- data/lib/rocket_job/plugins/document.rb +69 -0
- data/lib/rocket_job/plugins/job/callbacks.rb +92 -0
- data/lib/rocket_job/plugins/job/defaults.rb +40 -0
- data/lib/rocket_job/plugins/job/logger.rb +36 -0
- data/lib/rocket_job/plugins/job/model.rb +288 -0
- data/lib/rocket_job/plugins/job/persistence.rb +167 -0
- data/lib/rocket_job/plugins/job/state_machine.rb +166 -0
- data/lib/rocket_job/plugins/job/worker.rb +167 -0
- data/lib/rocket_job/plugins/restart.rb +54 -0
- data/lib/rocket_job/plugins/singleton.rb +26 -0
- data/lib/rocket_job/plugins/state_machine.rb +105 -0
- data/lib/rocket_job/version.rb +1 -1
- data/lib/rocket_job/worker.rb +150 -119
- data/lib/rocketjob.rb +43 -21
- data/test/config_test.rb +12 -0
- data/test/dirmon_entry_test.rb +81 -85
- data/test/dirmon_job_test.rb +40 -28
- data/test/job_test.rb +14 -257
- data/test/plugins/job/callbacks_test.rb +163 -0
- data/test/plugins/job/defaults_test.rb +52 -0
- data/test/plugins/job/logger_test.rb +58 -0
- data/test/plugins/job/model_test.rb +97 -0
- data/test/plugins/job/persistence_test.rb +81 -0
- data/test/plugins/job/state_machine_test.rb +118 -0
- data/test/plugins/job/worker_test.rb +183 -0
- data/test/plugins/restart_test.rb +185 -0
- data/test/plugins/singleton_test.rb +94 -0
- data/test/plugins/state_machine_event_callbacks_test.rb +101 -0
- data/test/plugins/state_machine_test.rb +64 -0
- data/test/test_helper.rb +3 -36
- metadata +64 -19
- data/lib/rocket_job/concerns/singleton.rb +0 -33
- data/lib/rocket_job/concerns/worker.rb +0 -214
- data/test/files/_archive/archived.txt +0 -3
- data/test/job_worker_test.rb +0 -86
- data/test/jobs/test_job.rb +0 -46
- data/test/worker_test.rb +0 -97
@@ -0,0 +1,166 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require 'active_support/concern'
|
3
|
+
|
4
|
+
module RocketJob
|
5
|
+
module Plugins
|
6
|
+
module Job
|
7
|
+
# State machine for RocketJob::Job
|
8
|
+
module StateMachine
|
9
|
+
extend ActiveSupport::Concern
|
10
|
+
|
11
|
+
included do
|
12
|
+
# State Machine events and transitions
|
13
|
+
#
|
14
|
+
# :queued -> :running -> :completed
|
15
|
+
# -> :paused -> :running (if started )
|
16
|
+
# -> :queued ( if no started )
|
17
|
+
# -> :aborted
|
18
|
+
# -> :failed -> :aborted
|
19
|
+
# -> :queued
|
20
|
+
# -> :aborted
|
21
|
+
# -> :queued (when a worker dies)
|
22
|
+
# -> :aborted
|
23
|
+
aasm column: :state do
|
24
|
+
# Job has been created and is queued for processing ( Initial state )
|
25
|
+
state :queued, initial: true
|
26
|
+
|
27
|
+
# Job is running
|
28
|
+
state :running
|
29
|
+
|
30
|
+
# Job has completed processing ( End state )
|
31
|
+
state :completed
|
32
|
+
|
33
|
+
# Job is temporarily paused and no further processing will be completed
|
34
|
+
# until this job has been resumed
|
35
|
+
state :paused
|
36
|
+
|
37
|
+
# Job failed to process and needs to be manually re-tried or aborted
|
38
|
+
state :failed
|
39
|
+
|
40
|
+
# Job was aborted and cannot be resumed ( End state )
|
41
|
+
state :aborted
|
42
|
+
|
43
|
+
event :start do
|
44
|
+
transitions from: :queued, to: :running
|
45
|
+
end
|
46
|
+
|
47
|
+
event :complete do
|
48
|
+
transitions from: :running, to: :completed
|
49
|
+
end
|
50
|
+
|
51
|
+
event :fail do
|
52
|
+
transitions from: :queued, to: :failed
|
53
|
+
transitions from: :running, to: :failed
|
54
|
+
transitions from: :paused, to: :failed
|
55
|
+
end
|
56
|
+
|
57
|
+
event :retry do
|
58
|
+
transitions from: :failed, to: :queued
|
59
|
+
end
|
60
|
+
|
61
|
+
event :pause do
|
62
|
+
transitions from: :running, to: :paused
|
63
|
+
transitions from: :queued, to: :paused
|
64
|
+
end
|
65
|
+
|
66
|
+
event :resume do
|
67
|
+
transitions from: :paused, to: :running, if: -> { started_at }
|
68
|
+
transitions from: :paused, to: :queued, unless: -> { started_at }
|
69
|
+
end
|
70
|
+
|
71
|
+
event :abort do
|
72
|
+
transitions from: :running, to: :aborted
|
73
|
+
transitions from: :queued, to: :aborted
|
74
|
+
transitions from: :failed, to: :aborted
|
75
|
+
transitions from: :paused, to: :aborted
|
76
|
+
end
|
77
|
+
|
78
|
+
event :requeue do
|
79
|
+
transitions from: :running, to: :queued,
|
80
|
+
if: -> _worker_name { worker_name == _worker_name },
|
81
|
+
after: :rocket_job_clear_started_at
|
82
|
+
end
|
83
|
+
end
|
84
|
+
# @formatter:on
|
85
|
+
|
86
|
+
# Define a before and after callback method for each event
|
87
|
+
state_machine_define_event_callbacks(*aasm.state_machine.events.keys)
|
88
|
+
|
89
|
+
before_start :rocket_job_set_started_at
|
90
|
+
before_complete :rocket_job_set_completed_at, :rocket_job_mark_complete
|
91
|
+
before_fail :rocket_job_set_completed_at, :rocket_job_increment_failure_count, :rocket_job_set_exception
|
92
|
+
before_pause :rocket_job_set_completed_at
|
93
|
+
before_abort :rocket_job_set_completed_at
|
94
|
+
before_retry :rocket_job_clear_exception
|
95
|
+
before_resume :rocket_job_clear_completed_at
|
96
|
+
after_complete :rocket_job_destroy_on_complete
|
97
|
+
|
98
|
+
# Pause all running jobs
|
99
|
+
def self.pause_all
|
100
|
+
running.each(&:pause!)
|
101
|
+
end
|
102
|
+
|
103
|
+
# Resume all paused jobs
|
104
|
+
def self.resume_all
|
105
|
+
paused.each(&:resume!)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
private
|
110
|
+
|
111
|
+
# Sets the exception child object for this job based on the
|
112
|
+
# supplied Exception instance or message
|
113
|
+
def rocket_job_set_exception(worker_name = nil, exc_or_message = nil)
|
114
|
+
if exc_or_message.is_a?(Exception)
|
115
|
+
self.exception = JobException.from_exception(exc_or_message)
|
116
|
+
exception.worker_name = worker_name
|
117
|
+
else
|
118
|
+
build_exception(
|
119
|
+
class_name: 'RocketJob::JobException',
|
120
|
+
message: exc_or_message,
|
121
|
+
backtrace: [],
|
122
|
+
worker_name: worker_name
|
123
|
+
)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def rocket_job_set_started_at
|
128
|
+
self.started_at = Time.now
|
129
|
+
end
|
130
|
+
|
131
|
+
def rocket_job_mark_complete
|
132
|
+
self.percent_complete = 100
|
133
|
+
end
|
134
|
+
|
135
|
+
def rocket_job_increment_failure_count
|
136
|
+
self.failure_count += 1
|
137
|
+
end
|
138
|
+
|
139
|
+
def rocket_job_clear_exception
|
140
|
+
self.completed_at = nil
|
141
|
+
self.exception = nil
|
142
|
+
self.worker_name = nil
|
143
|
+
end
|
144
|
+
|
145
|
+
def rocket_job_set_completed_at
|
146
|
+
self.completed_at = Time.now
|
147
|
+
self.worker_name = nil
|
148
|
+
end
|
149
|
+
|
150
|
+
def rocket_job_clear_completed_at
|
151
|
+
self.completed_at = nil
|
152
|
+
end
|
153
|
+
|
154
|
+
def rocket_job_clear_started_at
|
155
|
+
self.started_at = nil
|
156
|
+
self.worker_name = nil
|
157
|
+
end
|
158
|
+
|
159
|
+
def rocket_job_destroy_on_complete
|
160
|
+
destroy if destroy_on_complete && !new_record?
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
@@ -0,0 +1,167 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require 'active_support/concern'
|
3
|
+
|
4
|
+
# Worker behavior for a job
|
5
|
+
module RocketJob
|
6
|
+
module Plugins
|
7
|
+
module Job
|
8
|
+
module Worker
|
9
|
+
extend ActiveSupport::Concern
|
10
|
+
|
11
|
+
included do
|
12
|
+
# Run this job later
|
13
|
+
#
|
14
|
+
# Saves it to the database for processing later by workers
|
15
|
+
def self.perform_later(*args, &block)
|
16
|
+
if RocketJob::Config.inline_mode
|
17
|
+
perform_now(*args, &block)
|
18
|
+
else
|
19
|
+
job = new(arguments: args)
|
20
|
+
block.call(job) if block
|
21
|
+
job.save!
|
22
|
+
job
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Run this job now.
|
27
|
+
#
|
28
|
+
# The job is not saved to the database since it is processed entriely in memory
|
29
|
+
# As a result before_save and before_destroy callbacks will not be called.
|
30
|
+
# Validations are still called however prior to calling #perform
|
31
|
+
def self.perform_now(*args, &block)
|
32
|
+
job = new(arguments: args)
|
33
|
+
block.call(job) if block
|
34
|
+
job.perform_now
|
35
|
+
job
|
36
|
+
end
|
37
|
+
|
38
|
+
# Returns the next job to work on in priority based order
|
39
|
+
# Returns nil if there are currently no queued jobs, or processing batch jobs
|
40
|
+
# with records that require processing
|
41
|
+
#
|
42
|
+
# Parameters
|
43
|
+
# worker_name [String]
|
44
|
+
# Name of the worker that will be processing this job
|
45
|
+
#
|
46
|
+
# skip_job_ids [Array<BSON::ObjectId>]
|
47
|
+
# Job ids to exclude when looking for the next job
|
48
|
+
#
|
49
|
+
# Note:
|
50
|
+
# If a job is in queued state it will be started
|
51
|
+
def self.rocket_job_next_job(worker_name, skip_job_ids = nil)
|
52
|
+
while (job = rocket_job_retrieve(worker_name, skip_job_ids))
|
53
|
+
case
|
54
|
+
when job.running?
|
55
|
+
# Batch Job
|
56
|
+
return job
|
57
|
+
when job.expired?
|
58
|
+
job.rocket_job_fail_on_exception!(worker_name) { job.destroy }
|
59
|
+
logger.info "Destroyed expired job #{job.class.name}, id:#{job.id}"
|
60
|
+
else
|
61
|
+
job.worker_name = worker_name
|
62
|
+
job.rocket_job_fail_on_exception!(worker_name) { job.start }
|
63
|
+
return job if job.running?
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Requeues all jobs that were running on worker that died
|
69
|
+
def self.requeue_dead_worker(worker_name)
|
70
|
+
running.each { |job| job.requeue!(worker_name) if job.may_requeue?(worker_name) }
|
71
|
+
end
|
72
|
+
|
73
|
+
# Turn off embedded callbacks. Slow and not used for Jobs
|
74
|
+
embedded_callbacks_off
|
75
|
+
end
|
76
|
+
|
77
|
+
# Runs the job now in the current thread.
|
78
|
+
#
|
79
|
+
# Validations are called prior to running the job.
|
80
|
+
#
|
81
|
+
# The job is not saved and therefore the following callbacks are _not_ called:
|
82
|
+
# * before_save
|
83
|
+
# * after_save
|
84
|
+
# * before_create
|
85
|
+
# * after_create
|
86
|
+
#
|
87
|
+
# Exceptions are _not_ suppressed and should be handled by the caller.
|
88
|
+
def perform_now
|
89
|
+
# Call validations
|
90
|
+
if respond_to?(:validate!)
|
91
|
+
validate!
|
92
|
+
elsif invalid?
|
93
|
+
raise(MongoMapper::DocumentNotValid, self)
|
94
|
+
end
|
95
|
+
worker = RocketJob::Worker.new(name: 'inline')
|
96
|
+
worker.started
|
97
|
+
start if may_start?
|
98
|
+
# Raise exceptions
|
99
|
+
rocket_job_work(worker, true) if running?
|
100
|
+
result
|
101
|
+
end
|
102
|
+
|
103
|
+
def perform(*)
|
104
|
+
raise NotImplementedError
|
105
|
+
end
|
106
|
+
|
107
|
+
# Fail this job in the event of an exception.
|
108
|
+
#
|
109
|
+
# The job is automatically saved only if an exception is raised in the supplied block.
|
110
|
+
#
|
111
|
+
# worker_name: [String]
|
112
|
+
# Name of the worker on which the exception has occurred
|
113
|
+
#
|
114
|
+
# raise_exceptions: [true|false]
|
115
|
+
# Re-raise the exception after updating the job
|
116
|
+
# Default: !RocketJob::Config.inline_mode
|
117
|
+
def rocket_job_fail_on_exception!(worker_name, raise_exceptions = !RocketJob::Config.inline_mode)
|
118
|
+
yield
|
119
|
+
rescue Exception => exc
|
120
|
+
if failed? || !may_fail?
|
121
|
+
self.exception = JobException.from_exception(exc)
|
122
|
+
exception.worker_name = worker_name
|
123
|
+
save! unless new_record? || destroyed?
|
124
|
+
else
|
125
|
+
if new_record? || destroyed?
|
126
|
+
fail(worker_name, exc)
|
127
|
+
else
|
128
|
+
fail!(worker_name, exc)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
raise exc if raise_exceptions
|
132
|
+
end
|
133
|
+
|
134
|
+
# Works on this job
|
135
|
+
#
|
136
|
+
# Returns [true|false] whether this job should be excluded from the next lookup
|
137
|
+
#
|
138
|
+
# If an exception is thrown the job is marked as failed and the exception
|
139
|
+
# is set in the job itself.
|
140
|
+
#
|
141
|
+
# Thread-safe, can be called by multiple threads at the same time
|
142
|
+
def rocket_job_work(worker, raise_exceptions = !RocketJob::Config.inline_mode)
|
143
|
+
raise(ArgumentError, 'Job must be started before calling #rocket_job_work') unless running?
|
144
|
+
rocket_job_fail_on_exception!(worker.name, raise_exceptions) do
|
145
|
+
run_callbacks :perform do
|
146
|
+
# Allow callbacks to fail, complete or abort the job
|
147
|
+
if running?
|
148
|
+
ret = perform(*arguments)
|
149
|
+
if collect_output?
|
150
|
+
# Result must be a Hash, if not put it in a Hash
|
151
|
+
self.result = (ret.is_a?(Hash) || ret.is_a?(BSON::OrderedHash)) ? ret : {result: ret}
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
if new_record? || destroyed?
|
156
|
+
complete if may_complete?
|
157
|
+
else
|
158
|
+
may_complete? ? complete! : save!
|
159
|
+
end
|
160
|
+
end
|
161
|
+
false
|
162
|
+
end
|
163
|
+
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require 'active_support/concern'
|
3
|
+
|
4
|
+
module RocketJob
|
5
|
+
module Plugins
|
6
|
+
# Automatically starts a new instance of this job anytime it fails, aborts, or completes.
|
7
|
+
# Failed jobs are aborted so that they cannot be restarted.
|
8
|
+
# On destroy this job is destroyed without starting a new copy. Abort the job first to get
|
9
|
+
# it to start a new instance before destroying.
|
10
|
+
# Include RocketJob::Plugins::Singleton to prevent multiple copies of a job from running at
|
11
|
+
# the same time.
|
12
|
+
#
|
13
|
+
# Note:
|
14
|
+
# - The job will not be restarted if:
|
15
|
+
# - A validation fails after cloning this job.
|
16
|
+
# - The job has expired.
|
17
|
+
module Restart
|
18
|
+
extend ActiveSupport::Concern
|
19
|
+
|
20
|
+
# Attributes to exclude when copying across the attributes to the new instance
|
21
|
+
RESTART_EXCLUDES = %w(_id state created_at started_at completed_at failure_count worker_name percent_complete exception result run_at)
|
22
|
+
|
23
|
+
included do
|
24
|
+
after_abort :rocket_job_restart_new_instance
|
25
|
+
after_complete :rocket_job_restart_new_instance
|
26
|
+
after_fail :rocket_job_restart_abort
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
# Run again in the future, even if this run fails with an exception
|
32
|
+
def rocket_job_restart_new_instance
|
33
|
+
return if expired?
|
34
|
+
attrs = attributes.dup
|
35
|
+
RESTART_EXCLUDES.each { |attr| attrs.delete(attr) }
|
36
|
+
|
37
|
+
# Copy across run_at for future dated jobs
|
38
|
+
attrs['run_at'] = run_at if run_at && (run_at > Time.now)
|
39
|
+
# Allow Singleton to prevent the creation of a new job if one is already running
|
40
|
+
job = self.class.create(attrs)
|
41
|
+
if job.persisted?
|
42
|
+
logger.info("Started a new job instance: #{job.id}")
|
43
|
+
else
|
44
|
+
logger.info('New job instance not started since one is already active')
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def rocket_job_restart_abort
|
49
|
+
new_record? ? abort : abort!
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require 'active_support/concern'
|
3
|
+
|
4
|
+
module RocketJob
|
5
|
+
module Plugins
|
6
|
+
# Prevent more than one instance of this job class from running at a time
|
7
|
+
module Singleton
|
8
|
+
extend ActiveSupport::Concern
|
9
|
+
|
10
|
+
included do
|
11
|
+
# Validation prevents a new job from being saved while one is already running
|
12
|
+
validates_each :state do |record, attr, value|
|
13
|
+
if (record.running? || record.queued? || record.paused?) && record.rocket_job_singleton_active?
|
14
|
+
record.errors.add(attr, "Another instance of #{record.class.name} is already queued or running")
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# Returns [true|false] whether another instance of this job is already active
|
19
|
+
def rocket_job_singleton_active?
|
20
|
+
self.class.where(state: [:running, :queued], _id: {'$ne' => id}).exists?
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require 'active_support/concern'
|
3
|
+
require 'aasm'
|
4
|
+
require 'rocket_job/extensions/aasm'
|
5
|
+
|
6
|
+
module RocketJob
|
7
|
+
module Plugins
|
8
|
+
# State machine for RocketJob
|
9
|
+
#
|
10
|
+
# Define before and after callbacks for state machine events
|
11
|
+
#
|
12
|
+
# Example: Supply a method name to call
|
13
|
+
#
|
14
|
+
# class MyJob < RocketJob::Job
|
15
|
+
# before_fail :let_me_know
|
16
|
+
#
|
17
|
+
# def let_me_know
|
18
|
+
# puts "Oh no, the job has failed with and exception"
|
19
|
+
# end
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# Example: Pass a block
|
23
|
+
#
|
24
|
+
# class MyJob < RocketJob::Job
|
25
|
+
# before_fail do
|
26
|
+
# puts "Oh no, the job has failed with an exception"
|
27
|
+
# end
|
28
|
+
# end
|
29
|
+
module StateMachine
|
30
|
+
extend ActiveSupport::Concern
|
31
|
+
|
32
|
+
included do
|
33
|
+
include AASM
|
34
|
+
|
35
|
+
# Adds a :before or :after callback to an event
|
36
|
+
# state_machine_add_event_callback(:start, :before, :my_method)
|
37
|
+
def self.state_machine_add_event_callback(event_name, action, *methods, &block)
|
38
|
+
raise(ArgumentError, 'Cannot supply both a method name and a block') if (methods.size > 0) && block
|
39
|
+
raise(ArgumentError, 'Must supply either a method name or a block') unless (methods.size > 0) || block
|
40
|
+
|
41
|
+
# TODO Somehow get AASM to support options such as :if and :unless to be consistent with other callbacks
|
42
|
+
# For example:
|
43
|
+
# before_start :my_callback, unless: :encrypted?
|
44
|
+
# before_start :my_callback, if: :encrypted?
|
45
|
+
if event = aasm.state_machine.events[event_name]
|
46
|
+
values = Array(event.options[action])
|
47
|
+
code =
|
48
|
+
if block
|
49
|
+
block
|
50
|
+
else
|
51
|
+
# Validate methods are any of Symbol String Proc
|
52
|
+
methods.each do |method|
|
53
|
+
unless method.is_a?(Symbol) || method.is_a?(String)
|
54
|
+
raise(ArgumentError, "#{action}_#{event_name} currently does not support any options. Only Symbol and String method names can be supplied.")
|
55
|
+
end
|
56
|
+
end
|
57
|
+
methods
|
58
|
+
end
|
59
|
+
action == :before ? values.push(code) : values.unshift(code)
|
60
|
+
event.options[action] = values.flatten.uniq
|
61
|
+
else
|
62
|
+
raise(ArgumentError, "Unknown event: #{event_name.inspect}")
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.state_machine_define_event_callbacks(*event_names)
|
67
|
+
event_names.each do |event_name|
|
68
|
+
module_eval <<-RUBY, __FILE__, __LINE__ + 1
|
69
|
+
def self.before_#{event_name}(*methods, &block)
|
70
|
+
state_machine_add_event_callback(:#{event_name}, :before, *methods, &block)
|
71
|
+
end
|
72
|
+
|
73
|
+
def self.after_#{event_name}(*methods, &block)
|
74
|
+
state_machine_add_event_callback(:#{event_name}, :after, *methods, &block)
|
75
|
+
end
|
76
|
+
RUBY
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# Patch AASM so that save! is called instead of save
|
81
|
+
# So that validations are run before job.requeue! is completed
|
82
|
+
# Otherwise it just fails silently
|
83
|
+
def aasm_write_state(state, name=:default)
|
84
|
+
attr_name = self.class.aasm(name).attribute_name
|
85
|
+
old_value = read_attribute(attr_name)
|
86
|
+
write_attribute(attr_name, state)
|
87
|
+
|
88
|
+
begin
|
89
|
+
if aasm_skipping_validations(name)
|
90
|
+
saved = save(validate: false)
|
91
|
+
write_attribute(attr_name, old_value) unless saved
|
92
|
+
saved
|
93
|
+
else
|
94
|
+
save!
|
95
|
+
end
|
96
|
+
rescue Exception => exc
|
97
|
+
write_attribute(attr_name, old_value)
|
98
|
+
raise(exc)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
data/lib/rocket_job/version.rb
CHANGED