Class: Jidoka::Worker

Inherits:
Object
  • Object
show all
Includes:
ActiveSupport::Rescuable
Defined in:
lib/jidoka/worker.rb

Direct Known Subclasses

Supervisor

Constant Summary collapse

BASE_ERRORS =

Shared error messages available to all Workers

{
  invalid_state_transition: 'You cannot transition to this state',
  action_already_performed: 'This action has already been performed'
}.freeze
ERRORS =

Default ERRORS hash to be overridden by subclasses

{}.freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(args = nil) ⇒ Worker

– Instance Methods –



73
74
75
76
77
78
# File 'lib/jidoka/worker.rb', line 73

def initialize(args = nil)
  super
  # Handle ActiveJob vs direct instantiation args
  @opts = args || (arguments ? arguments[0] : {})
  @opts = @opts.transform_keys(&:to_sym) if @opts
end

Instance Attribute Details

#errorString (readonly)

Returns Error message to display to end users.

Returns:

  • (String)

    Error message to display to end users



6
7
8
# File 'lib/jidoka/worker.rb', line 6

def error
  @error
end

#messageString (readonly)

Returns Error message to display to end users.

Returns:

  • (String)

    Error message to display to end users



6
7
8
# File 'lib/jidoka/worker.rb', line 6

def message
  @message
end

Class Method Details

.dry_run(opts = {}) ⇒ Object



44
45
46
# File 'lib/jidoka/worker.rb', line 44

def self.dry_run(opts = {})
  initialize_and_call!(opts, :validate)
end

.dry_run!(opts = {}) ⇒ Object



40
41
42
# File 'lib/jidoka/worker.rb', line 40

def self.dry_run!(opts = {})
  initialize_and_call!(opts, :validate!)
end

.enforce_arguments!(obj) ⇒ Object

– Class Methods –



21
22
23
# File 'lib/jidoka/worker.rb', line 21

def self.enforce_arguments!(obj)
  self.argument_types = obj.freeze
end

.include_notify?(opts) ⇒ Boolean

Returns:

  • (Boolean)


58
59
60
# File 'lib/jidoka/worker.rb', line 58

def self.include_notify?(opts)
  [nil, true].include?(opts.delete(:notify)) ? %i[notify!] : []
end

.initialize_and_call!(opts, *methods_to_call) ⇒ Object



62
63
64
65
66
67
68
69
# File 'lib/jidoka/worker.rb', line 62

def self.initialize_and_call!(opts, *methods_to_call)
  instance = new(opts.transform_keys(&:to_sym))
  methods_to_call.each do |m|
    instance.send(m)
    break if instance.failure?
  end
  instance
end

.possible_errors(with_prefix = false) ⇒ Object



25
26
27
28
# File 'lib/jidoka/worker.rb', line 25

def self.possible_errors(with_prefix = false)
  @possible_errors ||= self::ERRORS
  with_prefix ? @possible_errors.transform_keys { |k| [to_s.underscore, k].join('-') } : @possible_errors
end

.run(opts = {}) ⇒ Object



34
35
36
37
38
# File 'lib/jidoka/worker.rb', line 34

def self.run(opts = {})
  initialize_and_call!(opts, :validate, :run, *include_notify?(opts)).tap do |result|
    yield(result) if block_given?
  end
end

.run!(opts = {}) ⇒ Object



30
31
32
# File 'lib/jidoka/worker.rb', line 30

def self.run!(opts = {})
  initialize_and_call!(opts, :validate!, :run!, *include_notify?(opts))
end

.undo(opts = {}) ⇒ Object



52
53
54
55
56
# File 'lib/jidoka/worker.rb', line 52

def self.undo(opts = {})
  initialize_and_call!(opts, :undo).tap do |result|
    yield(result) if block_given?
  end
end

.undo!(opts = {}) ⇒ Object



48
49
50
# File 'lib/jidoka/worker.rb', line 48

def self.undo!(opts = {})
  initialize_and_call!(opts, :undo!)
end

Instance Method Details

#_notify(**_opts) ⇒ Object



161
# File 'lib/jidoka/worker.rb', line 161

def _notify(**_opts); end

#downObject



151
# File 'lib/jidoka/worker.rb', line 151

def down; end

#failed {|_self| ... } ⇒ Object

Yields:

  • (_self)

Yield Parameters:



173
174
175
# File 'lib/jidoka/worker.rb', line 173

def failed
  yield(self) if failure?
end

#failure?Boolean

– State Helpers –

Returns:

  • (Boolean)


165
166
167
# File 'lib/jidoka/worker.rb', line 165

def failure?
  @failure.present?
end

#notify!Object



153
154
155
156
157
158
159
# File 'lib/jidoka/worker.rb', line 153

def notify!
  _notify(**@opts)
rescue StandardError => e
  report_error(e)
  # We do not re-raise notification errors by default unless in test
  raise(e) if defined?(Rails) && Rails.env.test?
end

#perform(opts = {}) ⇒ Object



80
81
82
83
84
85
# File 'lib/jidoka/worker.rb', line 80

def perform(opts = {})
  @opts = opts.transform_keys(&:to_sym)
  validate!
  run!
  notify!
end

#prepare(opts) ⇒ Object



87
# File 'lib/jidoka/worker.rb', line 87

def prepare(opts); end

#runObject



111
112
113
114
115
# File 'lib/jidoka/worker.rb', line 111

def run
  run!
rescue StandardError => e
  notice_failure!(e)
end

#run!Object



104
105
106
107
108
109
# File 'lib/jidoka/worker.rb', line 104

def run!
  with_transaction { up(**@opts) }
rescue Jidoka::ConditionNotMet, Jidoka::Failure => e
  notice_failure!(e)
  raise(e)
end

#success {|_self| ... } ⇒ Object

Yields:

  • (_self)

Yield Parameters:



177
178
179
# File 'lib/jidoka/worker.rb', line 177

def success
  yield(self) if success?
end

#success?Boolean

Returns:

  • (Boolean)


169
170
171
# File 'lib/jidoka/worker.rb', line 169

def success?
  !failure?
end

#undoObject



125
126
127
128
129
# File 'lib/jidoka/worker.rb', line 125

def undo
  undo!
rescue StandardError => e
  notice_failure!(e)
end

#undo!Object



117
118
119
120
121
122
123
# File 'lib/jidoka/worker.rb', line 117

def undo!
  prepare_inverse(**@opts) if respond_to?(:prepare_inverse)
  with_transaction { down }
rescue Jidoka::ConditionNotMet, Jidoka::Failure => e
  notice_failure!(e)
  raise(e)
end

#up(_opts = nil) ⇒ Object

Raises:

  • (NotImplementedError)


147
148
149
# File 'lib/jidoka/worker.rb', line 147

def up(_opts = nil)
  raise NotImplementedError
end

#validateObject



98
99
100
101
102
# File 'lib/jidoka/worker.rb', line 98

def validate
  validate!
rescue StandardError => e
  notice_failure!(e)
end

#validate!Object



89
90
91
92
93
94
95
96
# File 'lib/jidoka/worker.rb', line 89

def validate!
  validate_arguments!(**@opts)
  prepare(**@opts)
  validate_conditions!(**@opts)
rescue Jidoka::ConditionNotMet, Jidoka::ArgumentClassMismatch, Jidoka::Failure => e
  notice_failure!(e)
  raise(e)
end

#with_transaction(&block) ⇒ Object

Wraps ‘up` (and `down`) in an ActiveRecord transaction. Override in a subclass to opt out — for example, when the work crosses non-transactional boundaries (external APIs, long-running processes) and holding a DB transaction open would be inappropriate:

class SyncSubscriber < Jidoka::Worker
  def with_transaction
    yield
  end

  def up(...); end
end


143
144
145
# File 'lib/jidoka/worker.rb', line 143

def with_transaction(&block)
  ActiveRecord::Base.transaction(&block)
end