Class: BetterService::Workflows::BranchDSL

Inherits:
Object
  • Object
show all
Defined in:
lib/better_service/workflows/branch_dsl.rb

Overview

DSL context for defining conditional branches

This class provides the DSL methods available inside a ‘branch do…end` block:

  • ‘on(condition, &block)` - Defines a conditional branch

  • ‘otherwise(&block)` - Defines the default branch

  • ‘step(…)` - Adds a step to the current branch

  • ‘branch(&block)` - Allows nested branch groups

Examples:

branch do
  on ->(ctx) { ctx.user.premium? } do
    step :premium_action, with: PremiumService
  end

  on ->(ctx) { ctx.user.basic? } do
    step :basic_action, with: BasicService
  end

  otherwise do
    step :default_action, with: DefaultService
  end
end

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(branch_group) ⇒ BranchDSL

Creates a new BranchDSL context

Parameters:

  • branch_group (BranchGroup)

    The branch group being configured



33
34
35
36
37
# File 'lib/better_service/workflows/branch_dsl.rb', line 33

def initialize(branch_group)
  @branch_group = branch_group
  @current_branch = nil
  @branch_index = 0
end

Instance Attribute Details

#branch_groupObject (readonly)

Returns the value of attribute branch_group.



28
29
30
# File 'lib/better_service/workflows/branch_dsl.rb', line 28

def branch_group
  @branch_group
end

Instance Method Details

#branch(&block) ⇒ void

This method returns an undefined value.

Allows nested branch groups within branches

Examples:

on ->(ctx) { ctx.type == 'contract' } do
  step :validate_contract, with: ValidateService

  branch do
    on ->(ctx) { ctx.value > 10000 } do
      step :ceo_approval, with: CEOApprovalService
    end
    otherwise do
      step :manager_approval, with: ManagerApprovalService
    end
  end
end

Parameters:

  • block (Proc)

    The block defining the nested branch group

Raises:

  • (ArgumentError)


135
136
137
138
139
140
141
142
143
144
145
146
147
148
# File 'lib/better_service/workflows/branch_dsl.rb', line 135

def branch(&block)
  raise "branch must be called within an 'on' or 'otherwise' block" unless @current_branch
  raise ArgumentError, "Block required for 'branch'" unless block_given?

  # Create nested branch group
  nested_group = BranchGroup.new(name: :"nested_branch_#{@branch_index}")
  nested_dsl = BranchDSL.new(nested_group)

  # Evaluate the block in the nested DSL context
  nested_dsl.instance_eval(&block)

  # Add the branch group as a "step" (it responds to call like a step)
  @current_branch.add_step(nested_group)
end

#on(condition, &block) ⇒ void

This method returns an undefined value.

Defines a conditional branch

Examples:

on ->(ctx) { ctx.user.premium? } do
  step :send_premium_email, with: Email::PremiumService
end

Parameters:

  • condition (Proc)

    The condition to evaluate (receives context)

  • block (Proc)

    The block defining steps for this branch

Raises:

  • (ArgumentError)


49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/better_service/workflows/branch_dsl.rb', line 49

def on(condition, &block)
  raise ArgumentError, "Condition must be a Proc" unless condition.is_a?(Proc)
  raise ArgumentError, "Block required for 'on'" unless block_given?

  @branch_index += 1
  branch_name = :"on_#{@branch_index}"

  @current_branch = @branch_group.add_branch(
    condition: condition,
    name: branch_name
  )

  # Evaluate the block in this DSL context
  instance_eval(&block)

  @current_branch = nil
end

#otherwise(&block) ⇒ void

This method returns an undefined value.

Defines the default branch (executed when no condition matches)

Examples:

otherwise do
  step :default_action, with: DefaultService
end

Parameters:

  • block (Proc)

    The block defining steps for the default branch

Raises:

  • (ArgumentError)


76
77
78
79
80
81
82
83
84
85
86
# File 'lib/better_service/workflows/branch_dsl.rb', line 76

def otherwise(&block)
  raise ArgumentError, "Block required for 'otherwise'" unless block_given?
  raise ArgumentError, "Default branch already defined" if @branch_group.default_branch

  @current_branch = @branch_group.set_default

  # Evaluate the block in this DSL context
  instance_eval(&block)

  @current_branch = nil
end

#step(name, with:, input: nil, optional: false, **options) ⇒ void

This method returns an undefined value.

Adds a step to the current branch

This method has the same signature as the workflow-level step method.

Parameters:

  • name (Symbol)

    The step name

  • with (Class)

    The service class to execute

  • input (Proc, nil) (defaults to: nil)

    Optional input mapper

  • optional (Boolean) (defaults to: false)

    Whether the step is optional

  • if (Proc, nil)

    Optional condition for step execution

  • rollback (Proc, nil)

    Optional rollback handler



99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/better_service/workflows/branch_dsl.rb', line 99

def step(name, with:, input: nil, optional: false, **options)
  raise "step must be called within an 'on' or 'otherwise' block" unless @current_branch

  # Handle Ruby keyword 'if' gracefully
  condition = options[:if] || options.dig(:binding)&.local_variable_get(:if) rescue nil

  step_obj = Workflowable::Step.new(
    name: name,
    service_class: with,
    input: input,
    optional: optional,
    condition: condition,
    rollback: options[:rollback]
  )

  @current_branch.add_step(step_obj)
end