Class: Plutonium::Wizard::Base

Inherits:
Object
  • Object
show all
Includes:
ActiveModel::Model, Definition::Presentable, DSL
Defined in:
lib/plutonium/wizard/base.rb

Overview

Author-facing base class for wizards (§2). A wizard declares ordered steps (with their own field surface, branching condition:, and optional per-step on_submit/on_rollback), an optional terminal review step, and commits at the end via execute.

This class is pure object behaviour — declaring the DSL, exposing the ordered steps, the union data snapshot, and the anchor/fail! accessors. HTTP/runner/store wiring lives elsewhere.

Examples:

class CompanyOnboardingWizard < Plutonium::Wizard::Base
  step :company do
    attribute :name, :string
    input :name
    validates :name, presence: true
  end
  review label: "Review"

  def execute
    succeed(Company.create!(name: data.name))
  end
end

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Definition::Presentable

#description, #icon, #label

Constructor Details

#initialize(view_context: nil) ⇒ Base

Returns a new instance of Base.



58
59
60
61
62
# File 'lib/plutonium/wizard/base.rb', line 58

def initialize(view_context: nil, **)
  @view_context = view_context
  @data_attributes = {}
  super()
end

Instance Attribute Details

#anchorObject

The record this wizard was launched against (§3). Raises when the wizard was not declared anchored — never returns nil.



141
142
143
144
145
146
# File 'lib/plutonium/wizard/base.rb', line 141

def anchor
  unless self.class.anchored?
    raise NotAnchoredError, "#{self.class} is not declared `anchored`"
  end
  @anchor
end

#current_scoped_entityObject

Identity/concurrency context (§4.5), supplied by the runner/driving layer so concurrency_key resolvers and the tenancy fold can reach them. wizard_token is the per-run id (the identity for guest/repeatable runs, available inside concurrency_key) — NOT a pre-auth principal that survives login.



43
44
45
# File 'lib/plutonium/wizard/base.rb', line 43

def current_scoped_entity
  @current_scoped_entity
end

#current_userObject

Identity/concurrency context (§4.5), supplied by the runner/driving layer so concurrency_key resolvers and the tenancy fold can reach them. wizard_token is the per-run id (the identity for guest/repeatable runs, available inside concurrency_key) — NOT a pre-auth principal that survives login.



43
44
45
# File 'lib/plutonium/wizard/base.rb', line 43

def current_user
  @current_user
end

#data_attributesObject

Returns the value of attribute data_attributes.



34
35
36
# File 'lib/plutonium/wizard/base.rb', line 34

def data_attributes
  @data_attributes
end

#scope=(value) ⇒ Object (writeonly)

Sets the attribute scope

Parameters:

  • value

    the value to set the attribute scope to.



36
37
38
# File 'lib/plutonium/wizard/base.rb', line 36

def scope=(value)
  @scope = value
end

#token=(value) ⇒ Object (writeonly)

Sets the attribute token

Parameters:

  • value

    the value to set the attribute token to.



36
37
38
# File 'lib/plutonium/wizard/base.rb', line 36

def token=(value)
  @token = value
end

#view_contextObject

Returns the value of attribute view_context.



35
36
37
# File 'lib/plutonium/wizard/base.rb', line 35

def view_context
  @view_context
end

#wizard_tokenObject

Identity/concurrency context (§4.5), supplied by the runner/driving layer so concurrency_key resolvers and the tenancy fold can reach them. wizard_token is the per-run id (the identity for guest/repeatable runs, available inside concurrency_key) — NOT a pre-auth principal that survives login.



43
44
45
# File 'lib/plutonium/wizard/base.rb', line 43

def wizard_token
  @wizard_token
end

Class Method Details

.data_step_classesObject

The per-step typed sub-object classes (=> Class), built once per wizard class from data_steps_spec and reused for every data snapshot (the container is cheap to instantiate; the classes aren't).



84
85
86
87
88
# File 'lib/plutonium/wizard/base.rb', line 84

def data_step_classes
  @data_step_classes ||= data_steps_spec.transform_values do |spec|
    Data.class_for(spec[:schema], options: spec[:options], structured: spec[:structured], validations: spec[:validations])
  end
end

.data_steps_specObject

Per-step data spec (=> {schema:, options:, structured:}), used to build the step-keyed data container (§2.6). Each step contributes its OWN schema/options/structured — no cross-step union, so two steps may share a field name without colliding. using: imports are already composed into each step's attribute_schema.



70
71
72
73
74
75
76
77
78
79
# File 'lib/plutonium/wizard/base.rb', line 70

def data_steps_spec
  steps.reject(&:review?).each_with_object({}) do |step, acc|
    acc[step.key.to_sym] = {
      schema: step.attribute_schema,
      options: step.attribute_options,
      structured: step_structured_schema(step),
      validations: step.validations + step.imported_form_validators
    }
  end
end

.inline_validation_classesObject

Per-step typed classes carrying ONLY the step's INLINE validates (no imported form validators — those are validated separately via the transient model, so folding them in here would double-report). Built once per wizard class and reused by the runner's inline validation, which otherwise recompiles an equivalent anonymous class on every validate call (and validate runs many times per render). {step_key => Class}, omitting steps with no inline validations.



97
98
99
100
101
102
# File 'lib/plutonium/wizard/base.rb', line 97

def inline_validation_classes
  @inline_validation_classes ||= steps.reject(&:review?).each_with_object({}) do |step, acc|
    next if step.validations.blank?
    acc[step.key.to_sym] = Data.class_for(step.attribute_schema, validations: step.validations)
  end
end

Instance Method Details

#authorize?Boolean

Entry authorization (§5.2/§6.5). Authors override it; false → 403. Default allow — resource-mounted surfaces additionally gate via the action policy.

Returns:

  • (Boolean)


182
# File 'lib/plutonium/wizard/base.rb', line 182

def authorize? = true

#concurrency_key_valueObject

Resolve this wizard's concurrency_key VALUE(S) in the wizard context (§4.2), with the tenant ALWAYS folded in (§4.4). Returns nil when no concurrency_key is declared (→ tokened identity). The returned value is an array [*key_values, tenant_gid]; InstanceKey.concurrency serializes it. The tenant is appended even when nil so the digest is stable.



153
154
155
156
157
158
159
# File 'lib/plutonium/wizard/base.rb', line 153

def concurrency_key_value
  resolver = self.class.concurrency_key_resolver
  return nil unless resolver

  key = instance_exec(&resolver)
  [*Array.wrap(key), current_scoped_entity]
end

#dataObject

The step-keyed data snapshot (§2.6), reconstituted from the nested staged data_attributes (=> {field => value}). Addressed as data.<step>.<field>; data[:step] for dynamic access.



135
136
137
# File 'lib/plutonium/wizard/base.rb', line 135

def data
  @data ||= Data::Container.new(self.class.data_step_classes, data_attributes)
end

#executeObject

The at-end commit hook (§2.3). Authors override it.

Raises:

  • (NotImplementedError)


176
177
178
# File 'lib/plutonium/wizard/base.rb', line 176

def execute
  raise NotImplementedError, "#{self.class} must implement #execute"
end

#persistedObject

Records the per-step on_submit/persist macro registers (§2.2), as a LAZY view over the stored GIDs: a key is located on first read and memoized, so a request that never reads persisted issues zero locates (§4.5). Records set this request (the persist macro) are live already.



171
172
173
# File 'lib/plutonium/wizard/base.rb', line 171

def persisted
  @persisted ||= LazyPersisted.new
end

#persisted_gid_source=(source) ⇒ Object

The { "step_key" => [gids] } source the runner injects from the stored state, backing the lazy persisted view. Reassigning it resets the memo.



163
164
165
# File 'lib/plutonium/wizard/base.rb', line 163

def persisted_gid_source=(source)
  @persisted = LazyPersisted.new(source)
end