Class: Stepped::Action

Inherits:
ActiveRecord::Base
  • Object
show all
Defined in:
app/models/stepped/action.rb

Defined Under Namespace

Classes: Deadlock

Constant Summary collapse

STATUSES =
%w[
  pending
  performing
  succeeded
  superseded
  cancelled
  failed
  timed_out
  deadlocked
].freeze
KEYS_JOINER =
"/"

Instance Method Summary collapse

Instance Method Details

#accomplished(step) ⇒ Object



123
124
125
126
127
128
129
130
131
132
133
# File 'app/models/stepped/action.rb', line 123

def accomplished(step)
  if step.failed?
    complete! :failed
  elsif more_steps_to_do?
    increment :current_step_index
    save!
    perform_current_step
  elsif !outbound?
    complete!
  end
end

#achieves?(action) ⇒ Boolean

Returns:

  • (Boolean)


87
88
89
# File 'app/models/stepped/action.rb', line 87

def achieves?(action)
  checksum_key == action.checksum_key && checksum == action.checksum
end

#actor_becomes_baseObject



140
141
142
# File 'app/models/stepped/action.rb', line 140

def actor_becomes_base
  safe_actor&.becomes actor_type.constantize
end

#apply_definitionObject



203
204
205
206
207
208
209
210
# File 'app/models/stepped/action.rb', line 203

def apply_definition
  return if definition.nil?
  self.outbound = definition.outbound
  self.concurrency_key = compute_concurrency_key
  self.checksum_key = compute_checksum_key
  self.job = definition.job
  self.timeout_seconds = compute_timeout
end

#cancelObject



74
75
76
# File 'app/models/stepped/action.rb', line 74

def cancel
  self.status = :cancelled
end

#cancellable?Boolean

Returns:

  • (Boolean)


160
161
162
# File 'app/models/stepped/action.rb', line 160

def cancellable?
  pending? || performing?
end

#completeObject



78
79
80
# File 'app/models/stepped/action.rb', line 78

def complete
  self.status = :succeeded
end

#complete!(status = :succeeded) ⇒ Object



164
165
166
# File 'app/models/stepped/action.rb', line 164

def complete!(status = :succeeded)
  Stepped::Performance.complete_action self, status
end

#completed?Boolean

Returns:

  • (Boolean)


194
195
196
# File 'app/models/stepped/action.rb', line 194

def completed?
  cancelled? || succeeded? || superseded? || failed? || timed_out? || deadlocked?
end

#compute_checksum_keyObject



115
116
117
# File 'app/models/stepped/action.rb', line 115

def compute_checksum_key
  run_definition_block :checksum_key_block
end

#compute_concurrency_keyObject



111
112
113
# File 'app/models/stepped/action.rb', line 111

def compute_concurrency_key
  run_definition_block :concurrency_key_block
end

#compute_timeoutObject



152
153
154
155
156
157
158
# File 'app/models/stepped/action.rb', line 152

def compute_timeout
  if definition.timeout.is_a?(Symbol)
    actor.send(definition.timeout)
  else
    definition.timeout
  end
end

#copy_parent_steps_to(action) ⇒ Object

Raises:

  • (ArgumentError)


91
92
93
94
95
96
97
98
99
100
101
# File 'app/models/stepped/action.rb', line 91

def copy_parent_steps_to(action)
  raise ArgumentError, "Can't copy_parent_steps_to self" if action == self

  parent_steps.each do |step|
    transaction(requires_new: true) do
      action.parent_steps << step
    end
  rescue ActiveRecord::RecordNotUnique
    action.reload
  end
end

#deadlock!Object



177
178
179
180
181
182
183
184
185
# File 'app/models/stepped/action.rb', line 177

def deadlock!
  e = Deadlock.new "#{name} on #{actor.class.name}/#{actor.id}"
  handled = Stepped.handled_exception_classes.any? { e.class <= _1 }
  raise e unless handled

  Rails.error.report(e, handled:)
  self.status = :deadlocked
  propagate_completion_to_parent_steps
end

#definitionObject



70
71
72
# File 'app/models/stepped/action.rb', line 70

def definition
  @definition = Stepped::Registry.find_or_add actor.class, name
end

#descendant_of?(action) ⇒ Boolean

Returns:

  • (Boolean)


187
188
189
190
191
192
# File 'app/models/stepped/action.rb', line 187

def descendant_of?(action)
  parent_steps.any? do |step|
    return true if step.action_id == action.id
    step.action.descendant_of?(action)
  end
end

#finalize_complete(status) ⇒ Object



168
169
170
171
172
173
174
175
# File 'app/models/stepped/action.rb', line 168

def finalize_complete(status)
  self.status = status
  execute_after_complete_callbacks
  update!(completed_at: Time.zone.now, performance: nil)
  Stepped::Achievement.grand_to(self) if succeeded_including_callbacks? && checksum.present?

  propagate_completion_to_parent_steps
end

#obtain_lock_and_performObject



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'app/models/stepped/action.rb', line 34

def obtain_lock_and_perform
  apply_definition
  run_before_chain

  if completed?
    propagate_completion_to_parent_steps
    return self
  end

  set_checksum

  Stepped::Achievement.raise_if_exists_for?(self)
  Stepped::Performance.obtain_for(self)
rescue Stepped::Achievement::ExistsError
  self.status = :succeeded
  propagate_completion_to_parent_steps
  self
end

#outbound_complete_keyObject



119
120
121
# File 'app/models/stepped/action.rb', line 119

def outbound_complete_key
  outbound? ? tenancy_key : nil
end

#performObject



59
60
61
62
63
64
65
66
67
68
# File 'app/models/stepped/action.rb', line 59

def perform
  update! status: :performing, started_at: Time.zone.now

  Stepped::Achievement.erase_of self

  ActiveRecord.after_all_transactions_commit do
    perform_current_step
    Stepped::TimeoutJob.set(wait: timeout).perform_later(self) if timeout?
  end
end

#perform_current_stepObject



103
104
105
106
107
108
109
# File 'app/models/stepped/action.rb', line 103

def perform_current_step
  steps.create!(
    definition_index: current_step_index,
    started_at: Time.zone.now,
    status: :performing
  ).perform
end

#propagate_completion_to_parent_stepsObject



216
217
218
219
220
221
222
# File 'app/models/stepped/action.rb', line 216

def propagate_completion_to_parent_steps
  ActiveRecord.after_all_transactions_commit do
    parent_steps.each do |step|
      step.conclude_job(succeeded_including_callbacks?)
    end
  end
end

#propagated_touchObject



198
199
200
201
# File 'app/models/stepped/action.rb', line 198

def propagated_touch
  touch
  parent_steps.each { _1.action.propagated_touch }
end

#safe_actorObject



135
136
137
138
# File 'app/models/stepped/action.rb', line 135

def safe_actor
  actor
rescue ActiveRecord::SubclassNotFound
end

#short_checksumObject



144
145
146
# File 'app/models/stepped/action.rb', line 144

def short_checksum
  checksum.to_s[0..7]
end

#succeeded_including_callbacks?Boolean

Returns:

  • (Boolean)


224
225
226
# File 'app/models/stepped/action.rb', line 224

def succeeded_including_callbacks?
  succeeded? && after_callbacks_failed_count.nil?
end

#supersede_with(action) ⇒ Object



82
83
84
85
# File 'app/models/stepped/action.rb', line 82

def supersede_with(action)
  update! completed_at: Time.zone.now, status: :superseded
  copy_parent_steps_to action
end

#timeoutObject



212
213
214
# File 'app/models/stepped/action.rb', line 212

def timeout
  timeout_seconds.seconds
end

#timeout?Boolean

Returns:

  • (Boolean)


148
149
150
# File 'app/models/stepped/action.rb', line 148

def timeout?
  timeout_seconds.present?
end

#update_performance(performance) ⇒ Object



53
54
55
56
57
# File 'app/models/stepped/action.rb', line 53

def update_performance(performance)
  self.performance = performance
  perform if pending? && (performance.action == self)
  save!
end