Module: Plutonium::Routing::WizardRegistration
- Defined in:
- lib/plutonium/routing/wizard_registration.rb
Overview
Adds register_wizard to the routing mapper, mirroring register_resource
(see MapperExtensions). A wizard is portal-hosted (§5.2): the routes are
drawn inside the portal engine's routes.draw block, so they inherit the
portal's scope/auth/layout, and they dispatch to a portal-namespaced wizard
controller that includes Wizard::Controller + the portal's own
controller concern.
Draws (portal-relative):
GET /onboarding(/:token)/:step → <Portal>::WizardsController#show
POST /onboarding(/:token)/:step → <Portal>::WizardsController#update
with onboarding_wizard_path / _url helpers.
Constant Summary collapse
- WIZARD_CONTROLLER_NAME =
"wizards"
Class Attribute Summary collapse
-
.appended_public_wizards ⇒ Object
Returns the value of attribute appended_public_wizards.
Instance Method Summary collapse
Class Attribute Details
.appended_public_wizards ⇒ Object
Returns the value of attribute appended_public_wizards.
33 34 35 |
# File 'lib/plutonium/routing/wizard_registration.rb', line 33 def appended_public_wizards @appended_public_wizards end |
Instance Method Details
#register_wizard(wizard_class, at:, as: nil, public: nil, layout: nil) ⇒ Object
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 |
# File 'lib/plutonium/routing/wizard_registration.rb', line 51 def register_wizard(wizard_class, at:, as: nil, public: nil, layout: nil) # The wizard subsystem is opt-in (`config.wizards.enabled`). When disabled, # draw no routes — its tables/migrations are skipped too, so a mounted route # couldn't work anyway. Warn rather than fail silently, so a # registered-but-disabled wizard is discoverable instead of a mystery 404. unless Plutonium.configuration.wizards.enabled Rails.logger.warn { "[Plutonium::Wizard] not registering routes for #{wizard_class} — config.wizards.enabled is false" } return end # A CONTEXT anchor (`anchored via: :method`) is portal-level: the anchor is # resolved by calling a controller method, needs no URL `:id`, and is # IDOR-safe (trusted context) — so it CAN mount here. Only a TYPE anchor # (`with:`-only, resolved from the URL `:id`) is rejected, because it needs # the resource controller's scoped, policy-gated `resource_record!`. if wizard_class.anchored? && !wizard_class.anchored_via? raise ArgumentError, "register_wizard #{wizard_class.name} — `with:`-anchored wizards are not " \ "mounted portal-level. Register them on the anchored resource's definition " \ "with the `wizard` macro, which auto-mounts a record action whose anchor is " \ "resolved through the resource controller's scoped, policy-gated " \ "`resource_record!`. (A `via:`-anchored wizard mounts here fine.)" end # Default the mount kind to the wizard's `anonymous?` flag, and reject # contradictions (§4.5): an `anonymous` wizard NEEDS a public route # (pre-login); a non-`anonymous` wizard MUST stay behind portal auth. is_public = public.nil? ? wizard_class.anonymous? : !!public if is_public && !wizard_class.anonymous? raise ArgumentError, "register_wizard #{wizard_class.name}, public: true — only an `anonymous` " \ "wizard may be mounted public. Add the `anonymous` macro to the wizard, or " \ "drop `public:`." end if !is_public && wizard_class.anonymous? raise ArgumentError, "register_wizard #{wizard_class.name} — an `anonymous` wizard must be mounted " \ "public (it runs pre-login). Pass `public: true` (it is the default for " \ "`anonymous` wizards)." end return register_public_wizard(wizard_class, at:, as:, layout:) if is_public ensure_wizard_controller!(wizard_class) # The helper name defaults to the mount path (`at:`), so # `register_wizard W, at: "onboarding"` yields `onboarding_wizard_path`. # `as:` overrides it; the wizard's own route name is the final fallback. helper_name = (as || at.presence || wizard_route_name(wizard_class)).to_s.tr("/", "_") defaults = wizard_route_defaults(wizard_class, layout) scope path: at do # Canonical launch: GET the bare mount → resolve/mint the run and PRG to # its first (or resumed) step, with the token already in the URL. This is # the shareable entry point; `wizard_step_url` builds the stepped URLs. get "/", to: "#{WIZARD_CONTROLLER_NAME}#launch", as: :"#{helper_name}_wizard_launch", defaults: defaults get "(/:token)/:step", to: "#{WIZARD_CONTROLLER_NAME}#show", as: :"#{helper_name}_wizard", defaults: defaults post "(/:token)/:step", to: "#{WIZARD_CONTROLLER_NAME}#update", defaults: defaults end end |