NAME

Dancer2::Plugin::QuickORM - Adds QuickORM syntactic sugar to Dancer2

VERSION

version 1.0000

SYNOPSIS

package MyApp;
use Dancer2;
use Dancer2::Plugin::QuickORM;

set plugins => {
   QuickORM => {
      default => {
         class => 'MyApp::ORM',
      },
   },
};

# 'widget' table -> singular/plural keywords widget()/widgets()
get '/widget/:id' => sub {
   return widget( route_parameters->get('id') );
};

get '/widgets' => sub {
   return [ widgets( { color => 'blue' } ) ];
};

# get at the raw ORM connection directly
get '/stats' => sub {
   return { row_count => orm()->handle('widget')->all };
};

DESCRIPTION

Dancer2::Plugin::QuickORM looks at the ORM(s) you've configured with DBIx::QuickORM, walks every table in their schema(s), and generates a Dancer2 keyword (i.e. an importable function) for each one, so routes can query it without any boilerplate.

For each configured ORM name (see "CONFIGURATION AND ENVIRONMENT"), the plugin's BUILD step:

  1. Loads the configured class and calls $class->orm($name) to obtain a connection.

  2. Iterates $orm->schema($name)->tables and, for each table, derives a singular and a plural keyword name from the table name (splitting StudlyCaps/CamelCase into words and singularising/pluralising with Lingua::EN::Inflect::Phrase).

  3. Registers those keywords, plus schema-prefixed variants (<name>_<singular> and <name>_<plural>), so a table is always reachable unambiguously even if more than one configured ORM exposes a table of the same name.

Generated keywords

If a table's singular and plural forms differ (the common case, e.g. widget/widgets), two keywords are generated:

singular($id)

Fetches the single row with that primary key. Croaks if called with no argument, or with a hashref (which is a search, not a primary key).

plural($where)

Runs a search. Called with no arguments, returns every row. Called with a hashref, runs it as search conditions against the table. Croaks if given anything other than a hashref.

If a table's singular and plural forms are identical (e.g. moose), a single "smart" keyword is generated instead, which inspects the shape of its argument to decide what you meant:

  • a hashref is treated as search conditions (like the plural form above);

  • undef (i.e. no argument) croaks, since there's no sensible default;

  • anything else (a plain scalar, or an arrayref) is treated as a primary key value and looked up like the singular form above;

  • any other kind of reference (coderef, scalarref, etc.) croaks, since it's neither a valid search nor a valid primary key.

Keyword collisions

Two different kinds of collision are handled, and handled differently:

Two configured ORMs derive the same bare keyword

The bare keyword (e.g. widget) becomes ambiguous: calling it croaks, telling you to use the schema-prefixed form (e.g. default_widget or second_widget) instead. The schema-prefixed forms always work.

A derived keyword collides with an existing keyword

If the bare keyword name is already in use (either because it's a core Dancer2 DSL keyword, e.g. session or response_header, or because this plugin has already registered it itself - its own orm keyword, see below), the bare form is silently skipped so the existing keyword keeps working. The schema-prefixed form is still registered and reachable.

SUBROUTINES/METHODS

orm

orm();          # the connection configured as 'default'
orm($name);     # the connection configured under $name

Returns the raw ORM connection object for the given configured name (defaults to 'default' when no name is given). Croaks if $name was not configured.

This is the one keyword the plugin always registers itself (via plugin_keywords), regardless of what tables are configured; use it when you need something the generated per-table keywords don't cover.

BUILD

Runs once when the plugin is instantiated. Reads the plugin configuration, loads each configured ORM class, and generates the per-table keywords described in "DESCRIPTION". Not intended to be called directly.

ATTRIBUTES

This plugin exposes no public attributes of its own. All of its behaviour is driven by the plugins->QuickORM section of your Dancer2 configuration; see "CONFIGURATION AND ENVIRONMENT".

DIAGNOSTICS

ORM '%s' is missing 'class' in the configuration

A configured ORM name has no class key. Every entry under plugins->QuickORM must specify one.

Failed to load ORM class '%s': %s

The configured class could not be required; the underlying error is appended.

ORM '%s' is not defined in the configuration

orm($name) was called with a name that wasn't configured.

'%s' requires a primary key value

A singular (or smart) keyword was called with no argument.

'%s' expects a single primary key value, not a hashref of search conditions

A singular keyword was called with a hashref; use the plural (search) keyword instead.

'%s' expects a hashref of search conditions

A plural keyword was called with a non-hashref, non-undef argument.

'%s' requires a primary key value or a hashref of search conditions

A smart keyword (identical singular/plural table) was called with no argument.

'%s' does not accept a reference of type '%s'

A smart keyword was called with a reference that's neither a hashref (search) nor usable as a primary key.

%s is ambiguous; use <schema>_%s instead!

The bare keyword is generated by more than one configured ORM; call the schema-prefixed form instead.

CONFIGURATION AND ENVIRONMENT

Configure one or more named ORMs under plugins->QuickORM in your Dancer2 config:

plugins:
  QuickORM:
    default:
      class: MyApp::ORM
      dsn: "dbi:Pg:dbname=myapp"
    reporting:
      class: MyApp::ORM
      dsn: "dbi:Pg:dbname=myapp_reporting"

Each key under QuickORM is an ORM name (default is used when orm() is called without an argument); each value is a hashref that must contain a class, plus whatever other keys your ORM class needs.

Every key/value pair in a given ORM's configuration (including class itself) is also copied into %ENV as QuickORM_<name>_<key>, and the name itself is set as QuickORM_<name>_name, so the ORM class's orm() constructor can pick up its configuration from the environment if it prefers that to being passed arguments directly.

The configured class must implement the following DBIx::QuickORM features:

$class->orm($name)                     # -> connection object
$orm->schema($name)->tables            # -> list of table objects
$table->name                           # -> table name string
$orm->handle($table_name)              # -> handle object
$handle->by_id($id)                    # -> single row (hashref) or undef
$handle->where($hashref)->all          # -> list of matching rows
$handle->all                           # -> list of every row

DEPENDENCIES

BUGS AND LIMITATIONS

Table names that singularise/pluralise to the same word as a Dancer2 core DSL keyword or another already-registered keyword are only reachable via their schema-prefixed keyword; see "Keyword collisions".

For tables whose singular and plural forms are identical, an arrayref argument is treated as a primary key value (passed straight through to the backend) rather than being rejected outright, since it isn't a hashref and isn't some other clearly-invalid reference type; whether that's a meaningful primary key value is left to the configured ORM class.

Please report any bugs or feature requests through the issue tracker for this distribution.

SEE ALSO

Dancer2, Dancer2::Plugin, DBIx::QuickORM

AUTHOR

D Ruth Holloway <ruth@hiruthie.me>

COPYRIGHT AND LICENSE

This software is copyright (c) 2026 by D Ruth Holloway.

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

1 POD Error

The following errors were encountered while parsing the POD:

Around line 404:

You forgot a '=back' before '=head1'