Quick and effortless CRUD (create/read/update/delete) operations based on database tables
Perl PLpgSQL Perl6
Latest commit 7769f0e Sep 28, 2016 @bigpresh committed on GitHub Merge pull request #82 from joshrabinowitz/addable-without-editable
Allow simplecrud interfaces to be addable and not editable, and vice versa


    Dancer::Plugin::SimpleCRUD - very simple CRUD

    A plugin for Dancer web applications, to use a few lines of code to
    create appropriate routes to support creating/editing/deleting/viewing
    records within a database table. Uses CGI::FormBuilder to generate,
    process and validate forms, Dancer::Plugin::Database for database
    interaction and HTML::Table::FromDatabase to display lists of records.

    Setting up forms and code to display and edit database records is a very
    common requirement in web apps; this plugin tries to make something
    basic trivially easy to set up and use.

        # In your Dancer app,
        use Dancer::Plugin::SimpleCRUD;

        # Simple example:
            record_title => 'Widget',
            prefix => '/widgets',
            db_table => 'widgets',
            editable => 1,

        # The above would create a route to handle C</widgets>, listing all widgets,
        # with options to add/edit entries (linking to C</widgets/add> and
        # C</widgets/edit/:id> respectively) where a form to add a new entry or edit
        # an existing entry will be created.
        # All fields in the database table would be editable.

        # A more in-depth synopsis, using all options (of course, usually you'd only
        # need to use a few of the options where you need to change the default
        # behaviour):

            record_title => 'Team',
            prefix => '/teams',
            db_table => 'team',
            labels => {     # More human-friendly labels for some columns
                venue_id => 'Home Venue',
                name     => 'Team Name', 
            validation => {  # validate values entered for some columns
                division => qr/\d+/,
            input_types => {  # overriding form input type for some columns
                supersecret => 'password',
                lotsoftext' => 'textarea',
            key_column => 'id', # id is default anyway
            editable_columns => [ qw( venue_id name division )    ],
            display_columns  => [ qw( id venue_id name division ) ],
            deleteable => 1,
            editable => 1,
            sortable => 1,
            paginate => 300,
            template => 'simple_crud.tt',
            query_auto_focus => 1,
            downloadable => 1,
            foreign_keys => {
                columnname => {
                    table => 'venues',
                    key_column => 'id',
                    label_column => 'name',
            custom_columns => {
                division_news => {
                    raw_column => "division",
                    transform  => sub {
                        my $division_name = shift;
                        my $label = "News about $division_name";
                        $division_name =~ s/([^-_.~A-Za-z0-9])/sprintf("%%%02X", ord($1))/seg;
                        my $search = qq{http://news.google.com/news?q="$division_name"};
                        return "<a href='$search'>$label</a>";
            auth => {
                view => {
                    require_login => 1,
                edit => {
                    require_role => 1,

    This plugin provides a `simple_crud' keyword, which takes a hash of
    options as described below, and sets up the appropriate routes to
    present add/edit/delete options.

    The options you can pass to simple_crud are:

    `record_title' (required)
        What we're editing, for instance, if you're editing widgets, use
        'Widget'. Will be used in form titles (for instance "Add a ...",
        "Edit ..."), and button labels.

    `prefix' (required)
        The prefix for the routes which will be created. Given a prefix of
        `/widgets', then you can go to `/widgets/new' to create a new
        Widget, and `/widgets/42' to edit the widget with the ID (see
        keu_column) 42.

        Don't confuse this with Dancer's `prefix' setting, which would be
        prepended before the prefix you pass to this plugin. For example, if
        you used:

            prefix '/foo';
                prefix => 'bar',

        ... then you'd end up with e.g. `/foo/bar' as the record listing

    `db_table' (required)
        The name of the database table.

    `key_column' (optional, default: 'id')
        Specify which column in the table is the primary key. If not given,
        defaults to id.

    `db_connection_name' (optional)
        We use Dancer::Plugin::Database to obtain database connections. This
        option allows you to specify the name of a connection defined in the
        config file to use. See the documentation for
        Dancer::Plugin::Database for how multiple database configurations
        work. If this is not supplied or is empty, the default database
        connection details in your config file will be used - this is often
        what you want, so unless your app is dealing with multiple DBs, you
        probably won't need to worry about this option.

    `labels' (optional)
        A hashref of field_name => 'Label', if you want to provide more
        user-friendly labels for some or all fields. As we're using
        CGI::FormBuilder, it will do a reasonable job of figuring these out
        for itself usually anyway - for instance, a field named `first_name'
        will be shown as `First Name'.

    `input_types' (optional)
        A hashref of field_name => input type, if you want to override the
        default type of input which would be selected by CGI::FormBuilder or
        by our DWIMmery (by default, password fields will be used for field
        names like 'password', 'passwd' etc, and text area inputs will be
        used for columns with type 'TEXT').

        Valid values include anything allowed by HTML, e.g. `text',
        `select', `textarea', `radio', `checkbox', `password', `hidden'.


            input_types => {
                first_name => 'text',
                secret     => 'password',
                gender     => 'radio',

    `validation' (optional)
        A hashref of field_name => validation criteria which should be
        passed to CGI::FormBuilder.


            validation => {
                email_address => 'EMAIL',
                age => '/^\d+$/',

    `message' (optional)
        A hashref of field_name => messages to show if validation failed.

        Default is "Invalid entry".


            message => {
                age   => 'Please enter your age in years',
                email => 'That is not a valid email address',

    `jsmessage' (optional)
        A hashref of field_name => message to show when Javascript
        validation fails.

        Default message is "- Invalid entry for the "$fieldname" field". See
        above for example.

    `acceptable_values' (optional)
        A hashref of arrayrefs to declare that certain fields can take only
        a set of acceptable values.


            acceptable_values => {
                gender => ['Male', 'Female'],
                status => [qw(Alive Dead Zombie Unknown)],

        You can automatically create option groups (on a field of type
        `select') by specifying the acceptable values in CGI::FormBuilder's
        `[value, label, category]' format, like this:

            acceptable_values => {
                gender => ['Male', 'Female'],
                status => [qw(Alive Dead Zombie Unknown)],
                threat_level => [
                    [ 'child_puke',   'Regurgitation',       'Child'],
                    [ 'child_knee',   'Knee Biter',          'Child'],
                    [ 'teen_eye',     'Eye Roll',            'Adolescent'],
                    [ 'teen_lip',     'Withering Sarcasm',   'Adolescent'],
                    [ 'adult_silent', 'Pointedly Ignore',    'Adult'],
                    [ 'adult_freak',  'Become Very Put Out', 'Adult'],

        If you are letting FormBuilder choose the field type, you won't see
        these categories unless you have enough options that it makes the
        field into a select. If you want to see the categories all the time,
        you can use the input_types option to force your field to be
        rendered as a select.

    `default_value' (optional)
        A hashref of default values to have pre-selected on the add form.


            default_value => {
                gender => 'Female',
                status => 'Unknown',

    `editable_columns' (optional)
        Specify an arrayref of fields which the user can edit. By default,
        this is all columns in the database table, with the exception of the
        key column.

    `not_editable_columns' (optional)
        Specify an arrayref of fields which should not be editable.

    `required' (optional)
        Specify an arrayref of fields which must be completed. If this is
        not provided, DWIMmery based on whether the field is set to allow
        null values in the database will be used - i.e. if that column can
        contain null, then it doesn't have to be completed, otherwise, it

        Specify whether to support deleting records. If set to a true value,
        a route will be created for `/prefix/delete/:id' to delete the
        record with the ID given, and the edit form will have a "Delete
        $record_title" button.

        Specify whether to support editing records. Defaults to true. If set
        to a false value, it will not be possible to add or edit rows in the

        Specify whether to support sorting the table. Defaults to false. If
        set to a true value, column headers will become clickable, allowing
        the user to sort the output by each column, and with
        ascending/descending order.

        Specify whether to show results in pages (with next/previous
        buttons). Defaults to undef, meaning all records are shown on one
        page (not useful for large tables). When defined as a number, only
        this number of results will be shown.

        Specify an arrayref of columns that should show up in the list.
        Defaults to all.

        Specify a template that will be applied to all output. This template
        must have a "simple_crud" placeholder defined or you won't get any
        output. This template must be located in your "views" directory.

        Any global layout will be applied automatically because this option
        causes the module to use the `template' keyword. If you don't use
        this option, the `template' keyword is not used, which implies that
        any `before_template_render' and `after_template_render' hooks won't
        be called.

        Specify whether to automatically set input focus to the query input
        field. Defaults to true. If set to a false value, focus will not be
        set. The focus is set using a simple inlined javascript.

        Specify whether to support downloading the results. Defaults to
        false. If set to a true value, The results show on the HTML page can
        be downloaded as CSV/TSV/JSON/XML. The download links will appear at
        the top of the page.

        A hashref to specify columns in the table which are foreign keys;
        for each one, the value should be a hashref containing the keys
        `table', `key_column' and `label_column'.

        A hashref to specify custom columns to appear in the list view of an
        entity. The keys of the hash are custom column names, the values
        hashrefs specifying a `raw_column' indicating a column from the
        table that should be selected to build the custom column from, and
        `transform', a subref to be used as a HTML::Table::FromDatabase
        callback on the resulting column. If no `transform' is provided, sub
        { return shift; } will be used.

        If `raw_column' consists of anything other than letters, numbers,
        and underscores, it is passed in raw, so you could put something
        like "NOW()" or "datetime('now')" in there and it should work as

        You can require that users be authenticated to view/edit records
        using the `auth' option to enable authentication powered by

        You can set different requirements for viewing and editing, for

            auth => {
                view => {
                    require_login => 1,
                edit => {
                    require_role => 'Admin',

        The example above means that any logged in user can view records,
        but only users with the 'Admin' role are able to create/edit/delete

        Or, to just require login for anything (same requirements for both
        viewing and editing), you can use the shorthand:

            auth => {
                require_login => 1,

    This module tries to do what you'd expect it to do, so you can rock up
    your web app with as little code and effort as possible, whilst still
    giving you control to override its decisions wherever you need to.

  Field types
    CGI::FormBuilder is excellent at working out what kind of field to use
    by itself, but we give it a little help where needed. For instance, if a
    field looks like it's supposed to contain a password, we'll have it
    rendered as a password entry box, rather than a standard text box.

    If the column in the database is an ENUM, we'll limit the choices
    available for this field to the choices defined by the ENUM list.
    (Unless you've provided a set of acceptable values for this field using
    the `acceptable_values' option to `simple_crud', in which case what you
    say goes.)

  add_edit_row (deprecated, use add_edit_row_pre_save)
    You can use the same code from your add_edit_row hook in an
    add_edit_row_pre_save hook. The only modification is that the new hook
    passes the editable params as a key of the first argument (called
    `params'), rather than as the first argument itself. So, if your hook
    had `my $args = shift;', it could just use `my $args = shift->{params};'
    and it should work the same way.

  add_edit_row_pre_save, add_edit_row_post_save
    These fire right before and after a row is added/edited; a hashref is
    passed with metadata such as the name of the table (in `table_name'),
    the args from the original route setup (`args'), the table's key column
    (`key_column'), and the values of the editable params (`params').

    In the post-save hook, you are also sent `success' (the return value of
    quick_insert or quick_update) telling you if the save was successful,
    `dbh' giving you the instance of the handle used to save the entity (so
    you can access last_insert_id()), and `verb' (currently either 'create
    new' or 'update').

    For instance, if you were dealing with a users table, you could use the
    pre_save hook to hash the password before storing it.

    David Precious, `<davidp@preshweb.co.uk>'

    Alberto Simões (ambs)


    Johnathan Barber



    Paul Johnson (pjcj)

    Rahul Kotamaraju

    Michael J South (msouth)

    Please report any bugs or feature requests to
    `bug-dancer-plugin-simplecrud at rt.cpan.org', or through the web
    interface at
    I will be notified, and then you'll automatically be notified of
    progress on your bug as I make changes.

    This module is developed on Github:


    Bug reports, ideas, suggestions, patches/pull requests all welcome.

    Even just a quick "Hey, this is great, thanks" or "This is no good to me
    because..." is greatly appreciated. It's always good to know if people
    are using your code, and what they think.

    You can find documentation for this module with the perldoc command.

        perldoc Dancer::Plugin::SimpleCRUD

    You may find help with this module on the main Dancer IRC channel or
    mailing list - see http://www.perldancer.org/

    You can also look for information at:

    * RT: CPAN's request tracker

    * AnnoCPAN: Annotated CPAN documentation

    * CPAN Ratings

    * Search CPAN

    Copyright 2010-16 David Precious.

    This program is free software; you can redistribute it and/or modify it
    under the terms of either: the GNU General Public License as published
    by the Free Software Foundation; or the Artistic License.

    See http://dev.perl.org/licenses/ for more information.