Class: Signoff::DSL

Inherits:
Object
  • Object
show all
Defined in:
lib/signoff/dsl.rb

Overview

The receiver for the signoff do … end block. Every method here mutates the Signoff::Definition it was built with.

signoff do
  state :draft
  state :manager_review
  state :approved
  state :rejected

  initial_state :draft

  transition :draft,           to: :manager_review
  transition :manager_review,  to: :approved

  reject_to :rejected

  allow_transition :manager_review do |user|
    user.manager?
  end

  after_transition do |record, event|
    WorkflowNotificationJob.perform_later(record.id, event.id)
  end
end

Instance Method Summary collapse

Constructor Details

#initialize(definition) ⇒ DSL

Returns a new instance of DSL.



29
30
31
# File 'lib/signoff/dsl.rb', line 29

def initialize(definition)
  @definition = definition
end

Instance Method Details

#action(name, to:, from: nil) ⇒ Object

Declare a named custom action. It generates a name! transition method and a can_name? predicate on the model, records an event whose action is the name verbatim, and is authorized by the same allow_transition guard as the source state. Like reject, the target is a side-transition (never a forward edge), so approve! stays unambiguous. This is the extension point for modelling any workflow verb beyond submit/approve/reject.

action :request_changes, from: :pending_review, to: :changes_requested
action :cancel,          to: :cancelled            # from any in-flight state

from is optional and may be a single state or an array; when omitted the action is allowed from every non-finalized state except its own target.



74
75
76
# File 'lib/signoff/dsl.rb', line 74

def action(name, to:, from: nil)
  @definition.add_action(name, to: to, from: from)
end

#after_transition(&block) ⇒ Object

Run block after the transition’s transaction commits. Receives (record, event). The created event is already persisted, so this is the right place to enqueue jobs or send mail.

Raises:



102
103
104
105
106
# File 'lib/signoff/dsl.rb', line 102

def after_transition(&block)
  raise DefinitionError, "after_transition requires a block" unless block

  @definition.after_callbacks << block
end

#allow_transition(from_state, &block) ⇒ Object

Authorize transitions out of from_state. The block receives the acting user (and optionally the record) and must return a truthy value to allow the transition.

allow_transition :finance_review do |user, record|
  user.finance_team? && record.amount <= user.approval_limit
end

Raises:



85
86
87
88
89
# File 'lib/signoff/dsl.rb', line 85

def allow_transition(from_state, &block)
  raise DefinitionError, "allow_transition requires a block" unless block

  @definition.add_authorization(from_state, block)
end

#before_transition(&block) ⇒ Object

Run block inside the transition’s transaction, before the state column is written. Receives (record, from_state, to_state).

Raises:



93
94
95
96
97
# File 'lib/signoff/dsl.rb', line 93

def before_transition(&block)
  raise DefinitionError, "before_transition requires a block" unless block

  @definition.before_callbacks << block
end

#initial_state(name) ⇒ Object

Explicitly set the starting state. Defaults to the first declared state.



46
47
48
# File 'lib/signoff/dsl.rb', line 46

def initial_state(name)
  @definition.initial_state = name
end

#reject_to(state) ⇒ Object

The state a record moves to when reject! is called.



58
59
60
# File 'lib/signoff/dsl.rb', line 58

def reject_to(state)
  @definition.reject_state = state
end

#state(name, initial: false) ⇒ Object

Declare a state. Pass initial: true to mark it as the starting state (equivalent to a separate initial_state call).



35
36
37
38
# File 'lib/signoff/dsl.rb', line 35

def state(name, initial: false)
  @definition.add_state(name)
  @definition.initial_state = name if initial
end

#states(*names) ⇒ Object

Declare several states at once: states :draft, :review, :approved.



41
42
43
# File 'lib/signoff/dsl.rb', line 41

def states(*names)
  names.flatten.each { |name| state(name) }
end

#transition(from, to:) ⇒ Object

Declare a forward transition. Both from and to accept a single state or an array, so several source states can share one forward edge (e.g. transition [:draft, :rejected], to: :review).



53
54
55
# File 'lib/signoff/dsl.rb', line 53

def transition(from, to:)
  @definition.add_transition(from, to)
end