Module: Smith::Workflow::Claim

Defined in:
lib/smith/workflow/claim.rb

Overview

ActiveRecord-aware atomic claim helper. Two strategies:

.atomic — AASM-event path. SELECT FOR UPDATE + record.public_send(transition_via)

inside transaction_owner.transaction. AASM callbacks fire.

.cas — single-statement CAS via update_all + where(status: from_statuses).

Does NOT invoke AASM events; intended for non-AASM claim sites
that already use update_all today.

ActiveRecord is loaded lazily — this file does NOT const-reference ::ActiveRecord at module load. Both methods raise AdapterUnavailable before any other work when ::ActiveRecord is not defined.

Defined Under Namespace

Classes: AdapterUnavailable, UnexpectedStatus

Class Method Summary collapse

Class Method Details

.atomic(model_class, id:, from_statuses:, transition_via:, terminal_statuses: [], on_unexpected_status: :raise, transaction_owner: nil) ⇒ Object



29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/smith/workflow/claim.rb', line 29

def self.atomic(model_class, id:, from_statuses:, transition_via:,
                terminal_statuses: [], on_unexpected_status: :raise,
                transaction_owner: nil)
  ensure_active_record!
  validate_atomic_args!(model_class, transition_via)

  owner = transaction_owner || model_class
  from = Array(from_statuses).map(&:to_s)
  terminal = Array(terminal_statuses).map(&:to_s)

  claimed = false

  owner.transaction do
    locked = model_class.lock.find(id)
    status = locked.public_send(:status).to_s

    if from.include?(status)
      locked.public_send(transition_via)
      claimed = true
    elsif terminal.include?(status)
      claimed = false
    else
      handle_unexpected_status!(model_class, id, status, on_unexpected_status)
      claimed = false
    end
  end

  claimed ? model_class.find(id) : nil
end

.cas(model_class, id:, from_statuses:, to_status:, status_column: :status, updated_at_column: :updated_at, now: -> { Time.now.utc }) ⇒ Object



59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/smith/workflow/claim.rb', line 59

def self.cas(model_class, id:, from_statuses:, to_status:,
             status_column: :status, updated_at_column: :updated_at,
             now: -> { Time.now.utc })
  ensure_active_record!
  from = Array(from_statuses).map(&:to_s)

  updates = { status_column => to_status.to_s }
  updates[updated_at_column] = now.call if updated_at_column

  rows = model_class
    .where(:id => id, status_column => from)
    .update_all(updates)

  rows.zero? ? nil : model_class.find(id)
end