{"id":16669419,"url":"https://github.com/bigpresh/dancer-plugin-simplecrud","last_synced_at":"2025-03-21T17:32:40.482Z","repository":{"id":1120770,"uuid":"992867","full_name":"bigpresh/Dancer-Plugin-SimpleCRUD","owner":"bigpresh","description":"Quick and effortless CRUD (create/read/update/delete) operations based on database tables","archived":false,"fork":false,"pushed_at":"2020-02-02T21:37:43.000Z","size":439,"stargazers_count":24,"open_issues_count":9,"forks_count":31,"subscribers_count":10,"default_branch":"master","last_synced_at":"2024-10-13T11:32:28.940Z","etag":null,"topics":["crud","crud-api","crud-generator","dancer","hacktoberfest","perl"],"latest_commit_sha":null,"homepage":"","language":"Perl","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bigpresh.png","metadata":{"files":{"readme":"README","changelog":"Changes","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2010-10-16T17:05:43.000Z","updated_at":"2022-09-30T05:09:32.000Z","dependencies_parsed_at":"2022-08-16T12:05:20.236Z","dependency_job_id":null,"html_url":"https://github.com/bigpresh/Dancer-Plugin-SimpleCRUD","commit_stats":null,"previous_names":[],"tags_count":32,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bigpresh%2FDancer-Plugin-SimpleCRUD","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bigpresh%2FDancer-Plugin-SimpleCRUD/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bigpresh%2FDancer-Plugin-SimpleCRUD/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bigpresh%2FDancer-Plugin-SimpleCRUD/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bigpresh","download_url":"https://codeload.github.com/bigpresh/Dancer-Plugin-SimpleCRUD/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":221817367,"owners_count":16885522,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["crud","crud-api","crud-generator","dancer","hacktoberfest","perl"],"created_at":"2024-10-12T11:32:31.816Z","updated_at":"2024-10-28T10:32:27.521Z","avatar_url":"https://github.com/bigpresh.png","language":"Perl","funding_links":[],"categories":[],"sub_categories":[],"readme":"NAME\n    Dancer::Plugin::SimpleCRUD - very simple CRUD\n    (create/read/update/delete)\n\nDESCRIPTION\n    A plugin for Dancer web applications, to use a few lines of code to\n    create appropriate routes to support creating/editing/deleting/viewing\n    records within a database table. Uses CGI::FormBuilder to generate,\n    process and validate forms, Dancer::Plugin::Database for database\n    interaction and HTML::Table::FromDatabase to display lists of records.\n\n    Setting up forms and code to display and edit database records is a very\n    common requirement in web apps; this plugin tries to make something\n    basic trivially easy to set up and use.\n\nSYNOPSIS\n    The following assumes that you already have a working Dancer app and\n    have put your database connection details in your \"config.yml\" to be\n    read by Dancer::Plugin::Database, which this plugin uses in order to\n    obtain a database connection.\n\n        # In your Dancer app,\n        use Dancer::Plugin::SimpleCRUD;\n\n        # Simple example:\n        simple_crud(\n            record_title =\u003e 'Widget',\n            prefix =\u003e '/widgets',\n            db_table =\u003e 'widgets',\n            editable =\u003e 1,\n        );\n\n        # The above would create a route to handle C\u003c/widgets\u003e, listing all widgets,\n        # with options to add/edit entries (linking to C\u003c/widgets/add\u003e and\n        # C\u003c/widgets/edit/:id\u003e respectively) where a form to add a new entry or edit\n        # an existing entry will be created.\n        # All fields in the database table would be editable.\n        #\n        # There is also a view route, C\u003c/widgets/view/:id\u003e, which shows all the values\n        # for the fields of a single database entry.\n\n        # A more in-depth synopsis, using all options (of course, usually you'd only\n        # need to use a few of the options where you need to change the default\n        # behaviour):\n\n        simple_crud(\n            record_title =\u003e 'Team',\n            prefix =\u003e '/teams',\n            db_table =\u003e 'team',\n            labels =\u003e {     # More human-friendly labels for some columns\n                venue_id =\u003e 'Home Venue',\n                name     =\u003e 'Team Name', \n            },  \n            validation =\u003e {  # validate values entered for some columns\n                division =\u003e qr/\\d+/,\n            },\n            input_types =\u003e {  # overriding form input type for some columns\n                supersecret =\u003e 'password',\n                lotsoftext' =\u003e 'textarea',\n            },\n            key_column =\u003e 'id', # id is default anyway\n            editable_columns =\u003e [ qw( venue_id name division )    ],\n            display_columns  =\u003e [ qw( id venue_id name division ) ],\n            deleteable =\u003e 1,\n            editable =\u003e 1,\n            addable =\u003e 0,   # does not allow adding rows\n            sortable =\u003e 1,\n            paginate =\u003e 300,\n            template =\u003e 'simple_crud.tt',\n            query_auto_focus =\u003e 1,\n            downloadable =\u003e 1,\n            foreign_keys =\u003e {\n                columnname =\u003e {\n                    table =\u003e 'venues',\n                    key_column =\u003e 'id',\n                    label_column =\u003e 'name',\n                },\n            },\n            table_class =\u003e 'table table-bordered',\n            paginate_table_class =\u003e 'table table-borderless',\n            custom_columns =\u003e [\n                {\n                    name =\u003e \"division_news\",\n                    raw_column =\u003e \"division\",\n                    transform  =\u003e sub {\n                        my $division_name = shift;\n                        my $label = \"News about $division_name\";\n                        $division_name =~ s/([^-_.~A-Za-z0-9])/sprintf(\"%%%02X\", ord($1))/seg;\n                        my $search = qq{http://news.google.com/news?q=\"$division_name\"};\n                        return \"\u003ca href='$search'\u003e$label\u003c/a\u003e\";\n                    },\n                    column_class =\u003e \"column-class\",\n                },\n            ],\n            auth =\u003e {\n                view =\u003e {\n                    require_login =\u003e 1,\n                },\n                edit =\u003e {\n                    require_role =\u003e 'Admin',\n                },\n            },\n        );\n\nUSAGE\n    This plugin provides a \"simple_crud\" keyword, which takes a hash of\n    options as described below, and sets up the appropriate routes to\n    present add/edit/delete options.\n\nOPTIONS\n    The options you can pass to simple_crud are:\n\n    \"record_title\" (required)\n        What we're editing, for instance, if you're editing widgets, use\n        'Widget'. Will be used in form titles (for instance \"Add a ...\",\n        \"Edit ...\"), and button labels.\n\n    \"prefix\" (required)\n        The prefix for the routes which will be created. Given a prefix of\n        \"/widgets\", then you can go to \"/widgets/new\" to create a new\n        Widget, and \"/widgets/42\" to edit the widget with the ID (see\n        key_column) 42.\n\n        Don't confuse this with Dancer's \"prefix\" setting, which would be\n        prepended before the prefix you pass to this plugin. For example, if\n        you used:\n\n            prefix '/foo';\n            simple_crud(\n                prefix =\u003e 'bar',\n                ...\n            );\n\n        ... then you'd end up with e.g. \"/foo/bar\" as the record listing\n        page.\n\n    \"db_table\" (required)\n        The name of the database table.\n\n    \"key_column\" (optional, default: 'id')\n        Specify which column in the table is the primary key. If not given,\n        defaults to id.\n\n    \"where_filter\" (optional)\n        Specify one or more 'where' clauses to use to filter the table. For\n        example:\n\n            simple_crud(\n                prefix =\u003e 'bar',\n                where_filter =\u003e {user_id =\u003e 1000},\n                ...\n            );\n\n        This would cause only rows with an user_id of 1000 to be displayed\n        in listings and search results, viewed, edited etc.\n\n        The \"where_filter\" parameter takes a hashref describing the WHERE\n        clause, as used by Dancer::Plugin::Database's \"quick_select\"\n        convenience method for example - see the where clause documentation\n        in Dancer::Plugin::Database::Core::Handle.\n\n        Alternatively, if the filter condition needs to be calculated at\n        runtime (for example, based on the logged in user calling it), then\n        you can provide a coderef which returns the WHERE clause hashref -\n        for instance:\n\n          where_filter =\u003e sub { { customer_id =\u003e logged_in_user()-\u003e{customer_id} } },\n\n    \"db_connection_name\" (optional)\n        We use Dancer::Plugin::Database to obtain database connections. This\n        option allows you to specify the name of a connection defined in the\n        config file to use. See the documentation for\n        Dancer::Plugin::Database for how multiple database configurations\n        work. If this is not supplied or is empty, the default database\n        connection details in your config file will be used - this is often\n        what you want, so unless your app is dealing with multiple DBs, you\n        probably won't need to worry about this option.\n\n    \"labels\" (optional)\n        A hashref of field_name =\u003e 'Label', if you want to provide more\n        user-friendly labels for some or all fields. As we're using\n        CGI::FormBuilder, it will do a reasonable job of figuring these out\n        for itself usually anyway - for instance, a field named \"first_name\"\n        will be shown as \"First Name\".\n\n    \"input_types\" (optional)\n        A hashref of field_name =\u003e input type, if you want to override the\n        default type of input which would be selected by CGI::FormBuilder or\n        by our DWIMmery (by default, password fields will be used for field\n        names like 'password', 'passwd' etc, and text area inputs will be\n        used for columns with type 'TEXT').\n\n        Valid values include anything allowed by HTML, e.g. \"text\",\n        \"select\", \"textarea\", \"radio\", \"checkbox\", \"password\", \"hidden\".\n\n        Example:\n\n            input_types =\u003e {\n                first_name =\u003e 'text',\n                secret     =\u003e 'password',\n                gender     =\u003e 'radio',\n            }\n\n    \"validation\" (optional)\n        A hashref of field_name =\u003e validation criteria which should be\n        passed to CGI::FormBuilder.\n\n        Example:\n\n            validation =\u003e {\n                email_address =\u003e 'EMAIL',\n                age =\u003e '/^\\d+$/',\n            }\n\n    \"message\" (optional)\n        A hashref of field_name =\u003e messages to show if validation failed.\n\n        Default is \"Invalid entry\".\n\n        Example:\n\n            message =\u003e {\n                age   =\u003e 'Please enter your age in years',\n                email =\u003e 'That is not a valid email address',\n            },\n\n    \"jsmessage\" (optional)\n        A hashref of field_name =\u003e message to show when Javascript\n        validation fails.\n\n        Default message is \"- Invalid entry for the \"$fieldname\" field\". See\n        above for example.\n\n    \"sort_options\" (optional)\n        A hashref of field_name =\u003e optionspec indicating how select options\n        should be sorted\n\n        This is currently a passthrough to CGI::FormBuilder's sortopts.\n        There are several built-in values:\n\n            NAME            Sort option values by name\n            NUM             Sort option values numerically\n            LABELNAME       Sort option labels by name\n            LABELNUM        Sort option labels numerically\n\n        See the documentation for \"sortopts\" in CGI::FormBuilder for more.\n\n    \"acceptable_values\" (optional)\n        A hashref of arrayrefs to declare that certain fields can take only\n        a set of acceptable values.\n\n        Example:\n\n            acceptable_values =\u003e {\n                gender =\u003e ['Male', 'Female'],\n                status =\u003e [qw(Alive Dead Zombie Unknown)],\n            }\n\n        You can automatically create option groups (on a field of type\n        \"select\") by specifying the acceptable values in CGI::FormBuilder's\n        \"[value, label, category]\" format, like this:\n\n            acceptable_values =\u003e {\n                gender =\u003e ['Male', 'Female'],\n                status =\u003e [qw(Alive Dead Zombie Unknown)],\n                threat_level =\u003e [\n                    [ 'child_puke',   'Regurgitation',       'Child'],\n                    [ 'child_knee',   'Knee Biter',          'Child'],\n                    [ 'teen_eye',     'Eye Roll',            'Adolescent'],\n                    [ 'teen_lip',     'Withering Sarcasm',   'Adolescent'],\n                    [ 'adult_silent', 'Pointedly Ignore',    'Adult'],\n                    [ 'adult_freak',  'Become Very Put Out', 'Adult'],\n                ],\n            }\n\n        If you are letting FormBuilder choose the field type, you won't see\n        these categories unless you have enough options that it makes the\n        field into a select. If you want to see the categories all the time,\n        you can use the \"input_types\" option to force your field to be\n        rendered as a select.\n\n    \"default_value\" (optional)\n        A hashref of default values to have pre-selected on the add form.\n\n        Example:\n\n            default_value =\u003e {\n                gender =\u003e 'Female',\n                status =\u003e 'Unknown',\n            }\n\n    \"editable_columns\" (optional)\n        Specify an arrayref of fields which the user can edit. By default,\n        this is all columns in the database table, with the exception of the\n        key column.\n\n    \"not_editable_columns\" (optional)\n        Specify an arrayref of fields which should not be editable.\n\n    \"required\" (optional)\n        Specify an arrayref of fields which must be completed. If this is\n        not provided, DWIMmery based on whether the field is set to allow\n        null values in the database will be used - i.e. if that column can\n        contain null, then it doesn't have to be completed, otherwise, it\n        does.\n\n    \"deletable\"\n        Specify whether to support deleting records. If set to a true value,\n        a route will be created for \"/prefix/delete/:id\" to delete the\n        record with the ID given, and the edit form will have a \"Delete\n        $record_title\" button.\n\n    \"editable\"\n        Specify whether to support editing records. Defaults to true. If set\n        to a false value, it will not be possible to add or edit rows in the\n        table. See also \"addable\".\n\n    \"addable\"\n        Specify whether to support adding records. Defaults to the value of\n        \"editable\" if set, or true otherwise. If set to a false value, it\n        will not be possible to add rows in the table.\n\n    \"sortable\"\n        Specify whether to support sorting the table. Defaults to false. If\n        set to a true value, column headers will become clickable, allowing\n        the user to sort the output by each column, and with\n        ascending/descending order.\n\n    \"paginate\"\n        Specify whether to show results in pages (with next/previous\n        buttons). Defaults to undef, meaning all records are shown on one\n        page (not useful for large tables). When defined as a number, only\n        this number of results will be shown.\n\n    \"display_columns\"\n        Specify an arrayref of columns that should show up in the list.\n        Defaults to all.\n\n    \"template\"\n        Specify a template that will be applied to all output. This template\n        must have a \"simple_crud\" placeholder defined or you won't get any\n        output. This template must be located in your \"views\" directory.\n\n        Any global layout will be applied automatically because this option\n        causes the module to use the \"template\" keyword. If you don't use\n        this option, the \"template\" keyword is not used, which implies that\n        any \"before_template_render\" and \"after_template_render\" hooks won't\n        be called.\n\n    \"query_auto_focus\"\n        Specify whether to automatically set input focus to the query input\n        field. Defaults to true. If set to a false value, focus will not be\n        set. The focus is set using a simple inlined javascript.\n\n    \"downloadable\"\n        Specify whether to support downloading the results. Defaults to\n        false. If set to a true value, The results show on the HTML page can\n        be downloaded as CSV/TSV/JSON/XML. The download links will appear at\n        the top of the page.\n\n    \"foreign_keys\"\n        A hashref to specify columns in the table which are foreign keys;\n        for each one, the value should be a hashref containing the keys\n        \"table\", \"key_column\" and \"label_column\".\n\n    \"custom_columns\"\n        An arrayref of hashrefs to specify custom columns to appear in the\n        list view of an entity. (Previously, this was just a hashref of\n        column names and specs, and this style is still supported for\n        backwards compatibility, but is deprecated because it leaves the\n        order of the columns unpredictable.)\n\n        The keys of each hash are \"name\", the name to use for this custom\n        column, \"raw_column\" indicating a column from the table that should\n        be selected to build the custom column from, \"transform\", a subref\n        to be used as a HTML::Table::FromDatabase callback on the resulting\n        column, and \"column_class\", to specify a CSS class for the the\n        column. \"column_class\" is optional, and if no \"transform\" is\n        provided, sub { return shift; } will be used.\n\n        If your custom column has the same name as an existing column, your\n        customizations will be used in-place to override the display of the\n        content in that column. If sorting is enabled, the column will be\n        sorted by the underlying database content for that row, and not by\n        the output of your transform function.\n\n        For a somewhat spurious example:\n\n            ...\n            custom_columns =\u003e [\n                {\n                    name =\u003e 'email_provider',\n                    raw_column =\u003e 'email',\n                    transform =\u003e sub {\n                        my $value = shift;\n                        return (split /@/, 1)[1];\n                    },\n                    column_class =\u003e 'column-class',\n                },\n            ],\n            ...\n\n        The \"transform\" code ref is passed to HTML::Table::FromDatabase as a\n        callback for that column, so it can do anything a\n        HTML::Table::FromDatabase callback can do. In particular, the\n        coderef will receive the value of the column as the first parameter,\n        but also a reference to the whole row hashref as the second\n        parameter, so you can do a variety of cunning things.\n\n        An example of a custom column whose \"transform\" coderef uses the row\n        hashref to get other values for the same row could be:\n\n            ...\n            custom_columns =\u003e [\n                {\n                    name =\u003e 'salutation',\n                    raw_column =\u003e 'name',\n                    transform =\u003e sub {\n                        my ($name_value, $row) = @_;\n                        return \"Hi, $row-\u003e{title} $name_value!\";\n                    },\n                }\n            ],\n            ...\n\n    \"auth\"\n        You can require that users be authenticated to view/edit records\n        using the \"auth\" option to enable authentication powered by\n        Dancer::Plugin::Auth::Extensible.\n\n        You can set different requirements for viewing and editing, for\n        example:\n\n            auth =\u003e {\n                view =\u003e {\n                    require_login =\u003e 1,\n                },\n                edit =\u003e {\n                    require_role =\u003e 'Admin',\n                },\n            },\n\n        The example above means that any logged in user can view records,\n        but only users with the 'Admin' role are able to create/edit/delete\n        records.\n\n        Or, to just require login for anything (same requirements for both\n        viewing and editing), you can use the shorthand:\n\n            auth =\u003e {\n                require_login =\u003e 1,\n            },\n\n    \"table_class\"\n        This provides a CSS class for the tables.\n\n    \"paginate_table_class\"\n        This provides a CSS class for the tables paginate buttons.\n\nDWIMmery\n    This module tries to do what you'd expect it to do, so you can rock up\n    your web app with as little code and effort as possible, whilst still\n    giving you control to override its decisions wherever you need to.\n\n  Field types\n    CGI::FormBuilder is excellent at working out what kind of field to use\n    by itself, but we give it a little help where needed. For instance, if a\n    field looks like it's supposed to contain a password, we'll have it\n    rendered as a password entry box, rather than a standard text box.\n\n    If the column in the database is an ENUM, we'll limit the choices\n    available for this field to the choices defined by the ENUM list.\n    (Unless you've provided a set of acceptable values for this field using\n    the \"acceptable_values\" option to \"simple_crud\", in which case what you\n    say goes.)\n\nHooks\n    Hooks are provided, which can be used in the normal Dancer way, using\n    the \"hook\" keyword.\n\n  add_edit_row (deprecated, use add_edit_row_pre_save)\n    You can use the same code from your add_edit_row hook in an\n    add_edit_row_pre_save hook. The only modification is that the new hook\n    passes the editable params as a key of the first argument (called\n    \"params\"), rather than as the first argument itself. So, if your hook\n    had \"my $args = shift;\", it could just use \"my $args = shift-\u003e{params};\"\n    and it should work the same way.\n\n  add_edit_row_pre_save, add_edit_row_post_save\n    These fire right before and after a row is added/edited; a hashref is\n    passed with metadata such as the name of the table (in \"table_name\"),\n    the args from the original route setup (\"args\"), the table's key column\n    (\"key_column\"), and the values of the editable params (\"params\").\n\n    In the post-save hook, you are also sent \"success\" (the return value of\n    quick_insert or quick_update) telling you if the save was successful\n    (which is a little redundant because your post-save hook won't be called\n    unless the insert or update was successful). You'll also get \"dbh\"\n    giving you the instance of the handle used to save the entity (so you\n    can access last_insert_id()), and \"verb\" (currently either 'create new'\n    or 'update').\n\n    For instance, if you were dealing with a users table, you could use the\n    pre_save hook to hash the password before storing it - assuming for the\n    sake of example that you have a \"hash_pw()\" function to return a hashed\n    password:\n\n      hook add_edit_row_pre_save =\u003e sub {\n          my $args = shift;\n          if ($args-\u003e{table_name} eq 'user') {\n              $args-\u003e{params}{password} = hash_pw($args-\u003e{params}{password});\n          }\n      };\n\n  delete_row_pre_delete, delete_row_post_delete\n    These fire right before and after a row is deleted. As with the\n    add_edit_row_pre_save and add_edit_row_post_save hooks, these are passed\n    a hashref with metadata such as the name of the table (in \"table_name\"),\n    the args from the original route setup (\"args\"), the table's key column\n    (\"key_column\"), and the values of the editable params (\"params\"). As\n    with the post-save hook, delete_row_post_delete hook won't be called if\n    we weren't able to delete the row.\n\n    You could use these to clean up ancillary data associated with a\n    database row when it was deleted, for example.\n\nAUTHOR\n    David Precious, \"\u003cdavidp@preshweb.co.uk\u003e\"\n\nACKNOWLEDGEMENTS\n    Alberto Simões (ambs)\n\n    WK\n\n    Johnathan Barber\n\n    saberworks\n\n    jasonjayr\n\n    Paul Johnson (pjcj)\n\n    Rahul Kotamaraju\n\n    Michael J South (msouth)\n\n    Martijn Lievaart\n\n    Josh Rabinowitz\n\nBUGS\n    Please report any bugs or feature requests to\n    \"bug-dancer-plugin-simplecrud at rt.cpan.org\", or through the web\n    interface at\n    \u003chttp://rt.cpan.org/NoAuth/ReportBug.html?Queue=Dancer-Plugin-SimpleCRUD\n    \u003e. I will be notified, and then you'll automatically be notified of\n    progress on your bug as I make changes.\n\nCONTRIBUTING\n    This module is developed on Github:\n\n    http://github.com/bigpresh/Dancer-Plugin-SimpleCRUD\n\n    Bug reports, ideas, suggestions, patches/pull requests all welcome.\n\n    Even just a quick \"Hey, this is great, thanks\" or \"This is no good to me\n    because...\" is greatly appreciated. It's always good to know if people\n    are using your code, and what they think.\n\nSUPPORT\n    You can find documentation for this module with the perldoc command.\n\n        perldoc Dancer::Plugin::SimpleCRUD\n\n    You may find help with this module on the main Dancer IRC channel or\n    mailing list - see http://www.perldancer.org/\n\n    You can also look for information at:\n\n    *   RT: CPAN's request tracker\n\n        \u003chttp://rt.cpan.org/NoAuth/Bugs.html?Dist=Dancer-Plugin-SimpleCRUD\u003e\n\n    *   AnnoCPAN: Annotated CPAN documentation\n\n        \u003chttp://annocpan.org/dist/Dancer-Plugin-SimpleCRUD\u003e\n\n    *   CPAN Ratings\n\n        \u003chttp://cpanratings.perl.org/d/Dancer-Plugin-SimpleCRUD\u003e\n\n    *   Search CPAN\n\n        \u003chttp://search.cpan.org/dist/Dancer-Plugin-SimpleCRUD/\u003e\n\nLICENSE AND COPYRIGHT\n    Copyright 2010-16 David Precious.\n\n    This program is free software; you can redistribute it and/or modify it\n    under the terms of either: the GNU General Public License as published\n    by the Free Software Foundation; or the Artistic License.\n\n    See http://dev.perl.org/licenses/ for more information.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbigpresh%2Fdancer-plugin-simplecrud","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbigpresh%2Fdancer-plugin-simplecrud","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbigpresh%2Fdancer-plugin-simplecrud/lists"}