NAME

Mojolicious::Plugin::Fondation::Setup::Controller::Setup - Setup wizard controller — plugin selection, workflow wizard, and .conf generation

VERSION

version 0.03

SYNOPSIS

# Routes are registered automatically by Fondation::Setup:
#   GET  /setup          → wizard()
#   GET  /setup/plugins  → plugins()
#   GET  /setup/discover → discover()
#   POST /setup/start    → start()
#   POST /setup/execute  → execute()
#   GET  /setup/reset    → reset()

DESCRIPTION

This controller implements the Fondation setup wizard — a step-by-step interface for discovering, selecting, and configuring Fondation plugins. It fetches available plugins from MetaCPAN, lets the user pick which ones to install, walks them through configuration parameters via a Workflow state machine, and generates the application's .conf file.

The wizard is designed to be the first thing a user sees after installing a Fondation-based application. It can also be re-run later to add or upgrade plugins.

NAME

Mojolicious::Plugin::Fondation::Setup::Controller::Setup - Setup wizard controller — plugin selection, workflow wizard, and .conf generation

ACTIONS

plugins

GET /setup/plugins

Renders the plugin selection page. Reads the existing .conf file to pre-select plugins that are already configured, fetches the full list of Fondation plugins from MetaCPAN (async), and passes both to the setup/plugins template.

Stash keys:

  • plugins — arrayref of plugin metadata from MetaCPAN

  • already_selected — hashref of fully-qualified class names already present in the config

discover

GET /setup/discover

Async JSON endpoint that returns the full list of Fondation plugins discovered on MetaCPAN. Used by the plugin selection UI to populate the list dynamically.

Response: { plugins = [...] }> on success, { error = "..." }> with status 500 on failure.

start

POST /setup/start

Builds a Workflow state machine from the selected plugins' configuration parameters. Expects selected_plugins (array of fully-qualified class names) in the POST body.

Flow:

1. Reads each selected plugin's fondation_meta → setup → parameters
2. Resolves current values from the existing .conf file if present
3. Builds a YAML workflow data structure with one state per plugin
4. Registers the workflow with Workflow::Factory (in-memory)
5. Checks which selected plugins are not yet installed
7. Redirects to /setup (the wizard)

wizard

GET /setup

Renders the setup wizard page. Loads the workflow identified by the setup_wizard_id cookie. If no workflow exists yet, redirects to /setup/plugins.

Also performs an async MetaCPAN check for upgradable plugins — compares installed versions against CPAN versions and flags any that are out of date.

Stash keys:

  • wf — the workflow object

  • context — workflow context hashref (user-provided values)

  • is_done — true when state is setup_done

  • is_review — true when state is setup_review

  • conf_path — path to the generated .conf file (when done)

  • config_loaded — true if Mojolicious::Plugin::Config is loaded

  • selected_plugins — arrayref of selected plugin class names

  • not_installed — arrayref of plugins not yet installed

  • upgradable — arrayref of plugins with newer versions on CPAN

execute

POST /setup/execute

Processes a workflow action (next, back, save). Reads form parameters matching the current state's parameter keys and stores them in the workflow context. On save (transition to setup_done), generates the .conf file via _generate_conf.

Expects:

  • action — the workflow action name to execute

  • Parameter values matching the current state's parameters keys

reset

GET /setup/reset

Clears the setup_wizard_id cookie and redirects back to the plugin selection page. Use this to start the wizard over from scratch.

INTERNAL METHODS

These methods are not exposed as routes but implement the core logic.

_persister_file_dir

my $dir = $self->_persister_file_dir;

Returns the directory path for the Workflow::Persister::File storage. Reads persister_file_dir from Fondation::Workflow's merged config in the registry, defaulting to data/setup.

_resolve_config_value

my $value = $self->_resolve_config_value($config, $key_path);

Resolves a dotted key path within a config hash. Handles flattened array notation (e.g. backends.main.dsn where backends is an arrayref of name => { dsn => '...' } pairs). Keys prefixed with + are stripped before resolution.

_parse_conf_for_plugins

my $configs = $self->_parse_conf_for_plugins(@classes);

Parses the existing .conf file and extracts per-plugin configuration hashes for the given class names. Returns a hashref keyed by fully-qualified class name.

_collect_setup_params

my $params = $self->_collect_setup_params($conf_config, @classes);

Loads each plugin's fondation_meta → setup → parameters and resolves current values from the config file (if available). Returns an arrayref of parameter hashes ready for the workflow builder.

Each parameter hash contains: plugin_short, plugin_label, plugin_desc, key, label, type, default, current, required, and optionally min, max, placeholder, options.

_build_workflow_data

my $yaml_data = $self->_build_workflow_data($params);

Builds a complete Workflow data structure from collected parameters. Creates one state per plugin group, a setup_review state, and a terminal setup_done state. Includes next, back, and save actions with Fondation metadata for UI rendering (labels, colors, icons).

_plugin_state_name

my $state = $self->_plugin_state_name($plugin_short);

Derives a workflow state name from a plugin short name — lowercased, double-colons replaced with underscores, non-alphanumeric chars stripped, prefixed with setup_. Example: Fondation::Model::DBIx::Async becomes setup_model_dbix_async.

_generate_conf

my $conf_path = $self->_generate_conf($wf);

Generates the application .conf file from all values collected in the workflow context. Builds a hash with:

  • Fondation → dependencies — array of plugin short names

  • Root-level keys per plugin with their configured values

  • Dotted keys expanded into nested hashes; + prefix keys use flattened array notation

Writes the file using Data::Dumper and returns its path.

_installed_version_for

my $version = $self->_installed_version_for($class);

Returns the installed version of a plugin class, or undef if not installed. Used to compare against MetaCPAN versions for upgrade detection.

AUTHOR

Daniel Brosseau <dab@cpan.org>

COPYRIGHT AND LICENSE

This software is copyright (c) 2026 by Daniel Brosseau.

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.

AUTHOR

Daniel Brosseau <dab@cpan.org>

COPYRIGHT AND LICENSE

This software is copyright (c) 2026 by Daniel Brosseau.

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.