{"id":19597923,"url":"https://github.com/khrt/raisin","last_synced_at":"2025-05-07T13:36:14.775Z","repository":{"id":13581821,"uuid":"16274446","full_name":"khrt/Raisin","owner":"khrt","description":"Raisin - a REST API micro framework for Perl 🐫 🐪","archived":false,"fork":false,"pushed_at":"2024-09-07T09:39:20.000Z","size":753,"stargazers_count":62,"open_issues_count":20,"forks_count":31,"subscribers_count":9,"default_branch":"master","last_synced_at":"2025-03-31T10:38:39.648Z","etag":null,"topics":["openapi","perl","plack","raisin","rest","swagger"],"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/khrt.png","metadata":{"files":{"readme":"README.md","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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2014-01-27T09:21:35.000Z","updated_at":"2024-10-14T15:48:03.000Z","dependencies_parsed_at":"2025-01-20T05:37:13.311Z","dependency_job_id":null,"html_url":"https://github.com/khrt/Raisin","commit_stats":{"total_commits":637,"total_committers":22,"mean_commits":"28.954545454545453","dds":0.06907378335949765,"last_synced_commit":"5d843b0700bfaf1cf09241917848db5c6d5a4386"},"previous_names":[],"tags_count":52,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/khrt%2FRaisin","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/khrt%2FRaisin/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/khrt%2FRaisin/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/khrt%2FRaisin/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/khrt","download_url":"https://codeload.github.com/khrt/Raisin/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252887938,"owners_count":21819939,"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":["openapi","perl","plack","raisin","rest","swagger"],"created_at":"2024-11-11T09:03:35.022Z","updated_at":"2025-05-07T13:36:14.732Z","avatar_url":"https://github.com/khrt.png","language":"Perl","funding_links":[],"categories":[],"sub_categories":[],"readme":"# NAME\n\nRaisin - A REST API microframework for Perl.\n\n# VERSION\n\nversion 0.94\n\n# SYNOPSIS\n\n    use HTTP::Status qw(:constants);\n    use List::Util qw(max);\n    use Raisin::API;\n    use Types::Standard qw(HashRef Any Int Str);\n\n    my %USERS = (\n        1 =\u003e {\n            first_name =\u003e 'Darth',\n            last_name =\u003e 'Wader',\n            password =\u003e 'deathstar',\n            email =\u003e 'darth@deathstar.com',\n        },\n        2 =\u003e {\n            first_name =\u003e 'Luke',\n            last_name =\u003e 'Skywalker',\n            password =\u003e 'qwerty',\n            email =\u003e 'l.skywalker@jedi.com',\n        },\n    );\n\n    plugin 'Logger', fallback =\u003e 1;\n    app-\u003elog( debug =\u003e 'Starting Raisin...' );\n\n    middleware 'CrossOrigin',\n        origins =\u003e '*',\n        methods =\u003e [qw/DELETE GET HEAD OPTIONS PATCH POST PUT/],\n        headers =\u003e [qw/accept authorization content-type api_key_token/];\n\n    plugin 'Swagger';\n\n    swagger_setup(\n        title =\u003e 'A POD synopsis API',\n        description =\u003e 'An example of API documentation.',\n        #terms_of_service =\u003e '',\n\n        contact =\u003e {\n            name =\u003e 'Artur Khabibullin',\n            url =\u003e 'http://github.com/khrt',\n            email =\u003e 'rtkh@cpan.org',\n        },\n\n        license =\u003e {\n            name =\u003e 'Perl license',\n            url =\u003e 'http://dev.perl.org/licenses/',\n        },\n    );\n\n    desc 'Users API';\n    resource users =\u003e sub {\n        summary 'List users';\n        params(\n            optional('start', type =\u003e Int, default =\u003e 0, desc =\u003e 'Pager (start)'),\n            optional('count', type =\u003e Int, default =\u003e 10, desc =\u003e 'Pager (count)'),\n        );\n        get sub {\n            my $params = shift;\n\n            my @users\n                = map { { id =\u003e $_, %{ $USERS{$_} } } }\n                  sort { $a \u003c=\u003e $b } keys %USERS;\n\n            my $max_count = scalar(@users) - 1;\n            my $start = $params-\u003e{start} \u003e $max_count ? $max_count : $params-\u003e{start};\n            my $end = $params-\u003e{count} \u003e $max_count ? $max_count : $params-\u003e{count};\n\n            my @slice = @users[$start .. $end];\n            { data =\u003e \\@slice }\n        };\n\n        summary 'List all users at once';\n        get 'all' =\u003e sub {\n            my @users\n                = map { { id =\u003e $_, %{ $USERS{$_} } } }\n                  sort { $a \u003c=\u003e $b } keys %USERS;\n            { data =\u003e \\@users }\n        };\n\n        summary 'Create new user';\n        params(\n            requires('user', type =\u003e HashRef, desc =\u003e 'User object', group {\n                requires('first_name', type =\u003e Str, desc =\u003e 'First name'),\n                requires('last_name', type =\u003e Str, desc =\u003e 'Last name'),\n                requires('password', type =\u003e Str, desc =\u003e 'User password'),\n                optional('email', type =\u003e Str, default =\u003e undef, regex =\u003e qr/.+\\@.+/, desc =\u003e 'User email'),\n            }),\n        );\n        post sub {\n            my $params = shift;\n\n            my $id = max(keys %USERS) + 1;\n            $USERS{$id} = $params-\u003e{user};\n\n            res-\u003estatus(HTTP_CREATED);\n            { success =\u003e 1 }\n        };\n\n        desc 'Actions on the user';\n        params requires('id', type =\u003e Int, desc =\u003e 'User ID');\n        route_param 'id' =\u003e sub {\n            summary 'Show user';\n            get sub {\n                my $params = shift;\n                $USERS{ $params-\u003e{id} };\n            };\n\n            summary 'Delete user';\n            del sub {\n                my $params = shift;\n                delete $USERS{ $params-\u003e{id} };\n                res-\u003estatus(HTTP_NO_CONTENT);\n                undef;\n            };\n        };\n    };\n\n    run;\n\n# DESCRIPTION\n\nRaisin is a REST API microframework for Perl.\nIt's designed to run on Plack, providing a simple DSL to develop RESTful APIs easily.\nIt was inspired by [Grape](https://github.com/intridea/grape).\n\n# FUNCTIONS\n\n## API DESCRIPTION\n\n### app\n\nReturns the `Raisin` app. Seldom needed, because most `Raisin::API` methods\ninvoke the app directly.\n\n### resource\n\nAdds a route to an application. `namespace` is a synonym for `resource`.\n\n    resource user =\u003e sub { ... };\n\n### route\\_param\n\nDefines a route parameter as a resource `id` which can be anything if type\nisn't specified for it.\n\n    route_param id =\u003e sub { ... };\n\nRaisin allows you to nest `route_param`:\n\n    params requires =\u003e { name =\u003e 'id', type =\u003e Int };\n    route_param id =\u003e sub {\n        get sub { ... };\n\n        params requires =\u003e { name =\u003e 'sub_id', type =\u003e Int };\n        route_param sub_id =\u003e sub {\n            ...\n        };\n    };\n\n### produces\n\nSpecifies the content types produced by `resource`.\n\n    produces ['text', 'json'];\n\nThe argument is an array reference of strings corresponding to the\nkeys used by `register_encoder`. This array is compared with the\nAccept header of the request to decide what content-type will\nactually be returned from a given invocation of `resource`.\n\n### del, get, patch, post, put, head, options\n\nShortcuts to add a `route` restricted to the corresponding HTTP method.\n\n    get sub { 'GET' };\n\n    del 'all' =\u003e sub { 'OK' };\n\n    params(\n        requires('id', type =\u003e Int),\n        optional('key', type =\u003e Str),\n    );\n    get sub { 'GET' };\n\n    desc 'Put data';\n    params(\n        required('id', type =\u003e Int),\n        optional('name', type =\u003e Str),\n    );\n    put 'all' =\u003e sub {\n        'PUT'\n    };\n\n### desc\n\nAdds a description to `resource` or any of the HTTP methods.\nUseful for OpenAPI as it's shown there as a description of an action.\n\n    desc 'Some long explanation about an action';\n    put sub { ... };\n\n    desc 'Some exaplanation about a group of actions',\n    resource =\u003e 'user' =\u003e sub { ... }\n\n### summary\n\nSame as [\"desc\"](#desc) but shorter.\n\n    summary 'Some summary';\n    put sub { ... };\n\n### tags\n\nTags can be used for logical grouping of operations by resources\nor any other qualifier. Using in API description.\n\n    tags 'delete', 'user';\n    delete sub { ... };\n\nBy default tags are added automatically based on it's namespace but you always\ncan overwrite it using the function.\n\n### entity\n\nDescribes response object which will be used to generate OpenAPI description.\n\n    entity 'MusicApp::Entity::Album';\n    get {\n        my $albums = $schema-\u003eresultset('Album');\n        present data =\u003e $albums, with =\u003e 'MusicApp::Entity::Album';\n    };\n\n### params\n\nDefines validations and coercion options for your parameters.\nCan be applied to any HTTP method and/or [\"route\\_param\"](#route_param) to describe parameters.\n\n    params(\n        requires('name', type =\u003e Str),\n        optional('start', type =\u003e Int, default =\u003e 0),\n        optional('count', type =\u003e Int, default =\u003e 10),\n    );\n    get sub { ... };\n\n    params(\n        requires('id', type =\u003e Int, desc =\u003e 'User ID'),\n    );\n    route_param 'id' =\u003e sub { ... };\n\nFor more see [\"Validation-and-coercion\" in Raisin](https://metacpan.org/pod/Raisin#Validation-and-coercion).\n\n### api\\_default\\_format\n\nSpecifies default API format mode when formatter isn't specified by API user.\nE.g. if URI is asked without an extension (`json`, `yaml`) or `Accept` header\nisn't specified the default format will be used.\n\nDefault value: `YAML`.\n\n    api_default_format 'json';\n\nSee also [\"API-FORMATS\" in Raisin](https://metacpan.org/pod/Raisin#API-FORMATS).\n\n### api\\_format\n\nRestricts API to use only specified formatter to serialize and deserialize data.\n\nAlready exists [Raisin::Encoder::JSON](https://metacpan.org/pod/Raisin%3A%3AEncoder%3A%3AJSON), [Raisin::Encoder::YAML](https://metacpan.org/pod/Raisin%3A%3AEncoder%3A%3AYAML),\nand [Raisin::Encoder::Text](https://metacpan.org/pod/Raisin%3A%3AEncoder%3A%3AText), but you can always register your own\nusing [\"register\\_encoder\"](#register_encoder).\n\n    api_format 'json';\n\nSee also [\"API-FORMATS\" in Raisin](https://metacpan.org/pod/Raisin#API-FORMATS).\n\n### api\\_version\n\nSets up an API version header.\n\n    api_version 1.23;\n\n### plugin\n\nLoads a Raisin module. A module options may be specified after the module name.\nCompatible with [Kelp](https://metacpan.org/pod/Kelp) modules.\n\n    plugin 'Swagger';\n\n### middleware\n\nAdds a middleware to your application.\n\n    middleware '+Plack::Middleware::Session' =\u003e { store =\u003e 'File' };\n    middleware '+Plack::Middleware::ContentLength';\n    middleware 'Runtime'; # will be loaded Plack::Middleware::Runtime\n\n### mount\n\nMounts multiple API implementations inside another one.\nThese don't have to be different versions, but may be components of the same API.\n\nIn `RaisinApp.pm`:\n\n    package RaisinApp;\n\n    use Raisin::API;\n\n    api_format 'json';\n\n    mount 'RaisinApp::User';\n    mount 'RaisinApp::Host';\n\n    1;\n\n### register\\_decoder\n\nRegisters a third-party parser (decoder).\n\n    register_decoder(xml =\u003e 'My::Parser::XML');\n\nSee also [Raisin::Decoder](https://metacpan.org/pod/Raisin%3A%3ADecoder).\n\n### register\\_encoder\n\nRegisters a third-party formatter (encoder).\n\n    register_encoder(xml =\u003e 'My::Formatter::XML');\n\nSee also [Raisin::Encoder](https://metacpan.org/pod/Raisin%3A%3AEncoder).\n\n### run\n\nReturns the `PSGI` application.\n\n## CONTROLLER\n\n### req\n\nProvides quick access to the [Raisin::Request](https://metacpan.org/pod/Raisin%3A%3ARequest) object for the current route.\n\nUse `req` to get access to request headers, params, env, etc.\n\n    use DDP;\n    p req-\u003eheaders;\n    p req-\u003eparams;\n    p req-\u003eenv;\n\n    say req-\u003eheader('X-Header');\n\nSee also [Plack::Request](https://metacpan.org/pod/Plack%3A%3ARequest).\n\n### res\n\nProvides quick access to the [Raisin::Response](https://metacpan.org/pod/Raisin%3A%3AResponse) object for the current route.\n\nUse `res` to set up response parameters.\n\n    res-\u003estatus(403);\n    res-\u003eheaders(['X-Application' =\u003e 'Raisin Application']);\n\nSee also [Plack::Response](https://metacpan.org/pod/Plack%3A%3AResponse).\n\n### param\n\nReturns request parameters.\nWithout an argument will return an array of all input parameters.\nOtherwise it will return the value of the requested parameter.\n\nReturns [Hash::MultiValue](https://metacpan.org/pod/Hash%3A%3AMultiValue) object.\n\n    say param('key'); # -\u003e value\n    say param(); # -\u003e { key =\u003e 'value', foo =\u003e 'bar' }\n\n### include\\_missing\n\nReturns all declared parameters even if there is no value for a param.\n\nSee [\"Declared-parameters\" in Raisin](https://metacpan.org/pod/Raisin#Declared-parameters).\n\n### session\n\nReturns `psgix.session` hash. When it exists, you can retrieve and store\nper-session data.\n\n    # store param\n    session-\u003e{hello} = 'World!';\n\n    # read param\n    say session-\u003e{name};\n\n### present\n\nRaisin hash a built-in `present` method, which accepts two arguments: an\nobject to be presented and an options associated with it. The options hash may\ninclude `with` key, which is defined the entity to expose. See [Raisin::Entity](https://metacpan.org/pod/Raisin%3A%3AEntity).\n\n    my $artists = $schema-\u003eresultset('Artist');\n\n    present data =\u003e $artists, with =\u003e 'MusicApp::Entity::Artist';\n    present count =\u003e $artists-\u003ecount;\n\n[Raisin::Entity](https://metacpan.org/pod/Raisin%3A%3AEntity) supports [DBIx::Class](https://metacpan.org/pod/DBIx%3A%3AClass) and [Rose::DB::Object](https://metacpan.org/pod/Rose%3A%3ADB%3A%3AObject).\n\nFor details see examples in _examples/music-app_ and [Raisin::Entity](https://metacpan.org/pod/Raisin%3A%3AEntity).\n\n# ALLOWED METHODS\n\nWhen you add a route for a resource, a route for the OPTIONS method will also be\nadded. The response to an OPTIONS request will include an \"Allow\" header listing\nthe supported methods.\n\n    get 'count' =\u003e sub {\n        { count =\u003e $count };\n    };\n\n    params(\n        requires('num', type =\u003e Int, desc =\u003e 'Value to add to the count.'),\n    );\n    put 'count' =\u003e sub {\n        my $params = shift;\n        $count += $params-\u003e{num};\n        { count: $count };\n    };\n\n\n    curl -v -X OPTIONS http://localhost:5000/count\n\n    \u003e OPTIONS /count HTTP/1.1\n    \u003e Host: localhost:5000\n    \u003e\n    * HTTP 1.0, assume close after body\n    \u003c HTTP/1.1 204 No Content\n    \u003c Allow: GET, OPTIONS, PUT\n\nIf a request for a resource is made with an unsupported HTTP method, an HTTP 405\n(Method Not Allowed) response will be returned.\n\n    curl -X DELETE -v http://localhost:3000/count\n\n    \u003e DELETE /count HTTP/1.1\n    \u003e Host: localhost:5000\n    \u003e\n    * HTTP 1.0, assume close after body\n    \u003c HTTP/1.1 405 Method Not Allowed\n    \u003c Allow: OPTIONS, GET, PUT\n\n# PARAMETERS\n\nRequest parameters are available through the `params` `HASH`. This includes\nGET, POST and PUT parameters, along with any named parameters you specify in\nyour route strings.\n\nParameters are automatically populated from the request body\non `POST` and `PUT` for form input, `JSON` and `YAML` content-types.\n\nThe request:\n\n    curl localhost:5000/data -H Content-Type:application/json -d '{\"id\": \"14\"}'\n\nThe Raisin endpoint:\n\n    post data =\u003e sub { param('id') };\n\nMultipart `POST`s and `PUT`s are supported as well.\n\nIn the case of conflict between either of:\n\n- path parameters;\n- GET, POST and PUT parameters;\n- contents of request body on POST and PUT;\n\nPath parameters have precedence.\n\nQuery string and body parameters will be merged (see [\"parameters\" in Plack::Request](https://metacpan.org/pod/Plack%3A%3ARequest#parameters))\n\n## Declared parameters\n\nRaisin allows you to access only the parameters that have been declared by you in\n[\"params\" in Raisin](https://metacpan.org/pod/Raisin#params) block.\n\nBy default you can get all declared parameter as a first argument passed to your\nroute subroutine.\n\nApplication:\n\n    api_format 'json';\n\n    post data =\u003e sub {\n        my $params = shift;\n        { data =\u003e $params };\n    };\n\nRequest:\n\n    curl -X POST -H \"Content-Type: application/json\" localhost:5000/signup -d '{\"id\": 42}'\n\nResponse:\n\n    { \"data\": nil }\n\nOnce we add parameters block, Raisin will start return only the declared parameters.\n\nApplication:\n\n    api_format 'json';\n\n    params(\n        requires('id', type =\u003e Int),\n        optional('email', type =\u003e Str)\n    );\n    post data =\u003e sub {\n        my $params = shift;\n        { data =\u003e $params };\n    };\n\nRequest:\n\n    curl -X POST -H \"Content-Type: application/json\" localhost:5000/signup -d '{\"id\": 42, \"key\": \"value\"}'\n\nResponse:\n\n    { \"data\": { \"id\": 42 } }\n\nBy default declared parameters don't contain parameters which have no value.\nIf you want to return all parameters you can use the `include_missing` function.\n\nApplication:\n\n    api_format 'json';\n\n    params(\n        requires('id', type =\u003e Int),\n        optional('email', type =\u003e Str)\n    );\n    post data =\u003e sub {\n        my $params = shift;\n        { data =\u003e include_missing($params) };\n    };\n\nRequest:\n\n    curl -X POST -H \"Content-Type: application/json\" localhost:5000/signup -d '{\"id\": 42, \"key\": \"value\"}'\n\nResponse:\n\n    { \"data\": { \"id\": 42, \"email\": null } }\n\n## Validation and coercion\n\nYou can define validations and coercion options for your parameters using a\n[\"params\" in Raisin](https://metacpan.org/pod/Raisin#params) block.\n\nParameters can `requires` value or can be `optional`.\n`optional` parameters can have default value.\n\n    params(\n        requires('name', type =\u003e Str),\n        optional('count', type =\u003e Int, default =\u003e 10),\n    );\n    get sub {\n        my $params = shift;\n        \"$params-\u003e{count}: $params-\u003e{name}\";\n    };\n\nNote that default values will NOT be passed through to any validation options\nspecified.\n\nAvailable arguments:\n\n- name\n- type\n- default\n- desc\n- regex\n- in\n\n## Nested Parameters\n\n### Hash\n\nUse a keyword `group` to define a group of parameters which is enclosed to\nthe parent `HashRef` parameter.\n\n    params(\n        requires('name', type =\u003e HashRef, group {\n            requires('first_name', type =\u003e Str),\n            requires('last_name', type =\u003e Str),\n        })\n    )\n\n### Array\n\nUse `ArrayRef[*]` types from your compatible type library to define arrays.\n\n    requires('list', type =\u003e ArrayRef[Int], desc =\u003e 'List of integers')\n\n## Types\n\nRaisin supports Moo(se)-compatible type constraint so you can use any of the\n[Moose](https://metacpan.org/pod/Moose), [Moo](https://metacpan.org/pod/Moo) or [Type::Tiny](https://metacpan.org/pod/Type%3A%3ATiny) type constraints.\n\nBy default [Raisin](https://metacpan.org/pod/Raisin) depends on [Type::Tiny](https://metacpan.org/pod/Type%3A%3ATiny) and it's [Types::Standard](https://metacpan.org/pod/Types%3A%3AStandard) type\ncontraint library.\n\nYou can create your own types as well.\nSee [Type::Tiny::Manual](https://metacpan.org/pod/Type%3A%3ATiny%3A%3AManual) and [Moose::Manual::Types](https://metacpan.org/pod/Moose%3A%3AManual%3A%3ATypes).\n\n# HOOKS\n\nThose blocks can be executed before or/and after every API call, using\n`before`, `after`, `before_validation` and `after_validation`.\n\nCallbacks execute in the following order:\n\n- before\n- before\\_validation\n- after\\_validation\n- after\n\nThe block applies to every API call\n\n    before sub {\n        my $self = shift;\n        say $self-\u003ereq-\u003emethod . \"\\t\" . $self-\u003ereq-\u003epath;\n    };\n\n    after_validation sub {\n        my $self = shift;\n        say $self-\u003eres-\u003ebody;\n    };\n\nSteps `after_validation` and `after` are executed only if validation succeeds.\n\nEvery callback has only one argument as an input parameter which is [Raisin](https://metacpan.org/pod/Raisin)\nobject. For more information of available methods see [\"CONTROLLER\" in Raisin](https://metacpan.org/pod/Raisin#CONTROLLER).\n\n# API FORMATS\n\nBy default, Raisin supports `YAML`, `JSON`, and `TEXT` content types.\nDefault format is `YAML`.\n\nResponse format can be determined by `Accept header` or `route extension`.\n\nSerialization takes place automatically. So, you do not have to call\n`encode_json` in each `JSON` API implementation.\n\nThe response format (and thus the automatic serialization) is determined in the following order:\n\n- Use the file extension, if specified. If the file is .json, choose the JSON format.\n- Attempt to find an acceptable format from the Accept header.\n- Use the default format, if specified by the `default_format` option.\n- Default to `YAML`.\n\nYour API can declare to support only one serializator by using [\"api\\_format\" in Raisin](https://metacpan.org/pod/Raisin#api_format).\n\nCustom formatters for existing and additional types can be defined with a\n[Raisin::Encoder](https://metacpan.org/pod/Raisin%3A%3AEncoder)/[Raisin::Decoder](https://metacpan.org/pod/Raisin%3A%3ADecoder).\n\n- JSON\n\n    Call `JSON::encode_json` and `JSON::decode_json`.\n\n- YAML\n\n    Call `YAML::Dump` and `YAML::Load`.\n\n- Text\n\n    Call `Data::Dumper-\u003eDump` if output data is not a string.\n\nThe order for choosing the format is the following.\n\n- Use the route extension.\n- Use the value of the `Accept` header.\n- Fallback to default.\n\n# LOGGING\n\nRaisin has a built-in logger and supports for `Log::Dispatch`.\nYou can enable it by:\n\n    plugin 'Logger', outputs =\u003e [['Screen', min_level =\u003e 'debug']];\n\nOr use [Raisin::Logger](https://metacpan.org/pod/Raisin%3A%3ALogger) with a `fallback` option:\n\n    plugin 'Logger', fallback =\u003e 1;\n\nThe plugin registers a `log` subroutine to [Raisin](https://metacpan.org/pod/Raisin). Below are examples of\nhow to use it.\n\n    app-\u003elog(debug =\u003e 'Debug!');\n    app-\u003elog(warn =\u003e 'Warn!');\n    app-\u003elog(error =\u003e 'Error!');\n\n`app` is a [Raisin](https://metacpan.org/pod/Raisin) instance, so you can use `$self` instead of `app` where\nit is possible.\n\nSee [Raisin::Plugin::Logger](https://metacpan.org/pod/Raisin%3A%3APlugin%3A%3ALogger).\n\n# API DOCUMENTATION\n\n## Raisin script\n\nYou can see application routes with the following command:\n\n    $ raisin examples/pod-synopsis-app/darth.pl\n    GET     /user\n    GET     /user/all\n    POST    /user\n    GET     /user/:id\n    DELETE  /user/:id\n    PUT     /user/:id\n    GET     /echo\n\nIncluding parameters:\n\n    $ raisin --params examples/pod-synopsis-app/darth.pl\n    GET     /user\n       start Int{0}\n       count Int{10}\n    GET     /user/all\n    POST    /user\n      *name     Str\n      *password Str\n    email    Str\n    GET     /user/:id\n      *id Int\n    DELETE  /user/:id\n      *id Int\n    PUT     /user/:id\n      *id Int\n    GET     /echo\n      *data Any{ёй}\n\n## OpenAPI/Swagger\n\n[Swagger](http://swagger.io) compatible API documentations.\n\n    plugin 'Swagger';\n\nDocumentation will be available on `http://\u003curl\u003e/swagger.json` URL.\nSo you can use this URL in Swagger UI.\n\nSee [Raisin::Plugin::Swagger](https://metacpan.org/pod/Raisin%3A%3APlugin%3A%3ASwagger).\n\n# MIDDLEWARE\n\nYou can easily add any [Plack](https://metacpan.org/pod/Plack) middleware to your application using\n`middleware` keyword. See [\"middleware\" in Raisin](https://metacpan.org/pod/Raisin#middleware).\n\n# PLUGINS\n\nRaisin can be extended using custom _modules_. Each new module must be a subclass\nof the `Raisin::Plugin` namespace. Modules' job is to initialize and register new\nmethods into the web application class.\n\nFor more see [\"plugin\" in Raisin](https://metacpan.org/pod/Raisin#plugin) and [Raisin::Plugin](https://metacpan.org/pod/Raisin%3A%3APlugin).\n\n# TESTING\n\nSee [Plack::Test](https://metacpan.org/pod/Plack%3A%3ATest), [Test::More](https://metacpan.org/pod/Test%3A%3AMore) and etc.\n\n    my $app = Plack::Util::load_psgi(\"$Bin/../script/raisinapp.pl\");\n\n    test_psgi $app, sub {\n        my $cb  = shift;\n        my $res = $cb-\u003e(GET '/user');\n\n        subtest 'GET /user' =\u003e sub {\n            if (!is $res-\u003ecode, 200) {\n                diag $res-\u003econtent;\n                BAIL_OUT 'FAILED!';\n            }\n            my $got = Load($res-\u003econtent);\n            isdeeply $got, $expected, 'Data!';\n        };\n    };\n\n# DEPLOYING\n\nDeploying a Raisin application is done the same way any other Plack\napplication is deployed:\n\n    $ plackup -E deployment -s Starman app.psgi\n\n## Kelp\n\n    use Plack::Builder;\n    use RaisinApp;\n    use KelpApp;\n\n    builder {\n        mount '/' =\u003e KelpApp-\u003enew-\u003erun;\n        mount '/api/rest' =\u003e RaisinApp-\u003enew;\n    };\n\n## Dancer\n\n    use Plack::Builder;\n    use Dancer ':syntax';\n    use Dancer::Handler;\n    use RaisinApp;\n\n    my $dancer = sub {\n        setting appdir =\u003e '/home/dotcloud/current';\n        load_app 'My::App';\n        Dancer::App-\u003eset_running_app('My::App');\n        my $env = shift;\n        Dancer::Handler-\u003einit_request_headers($env);\n        my $req = Dancer::Request-\u003enew(env =\u003e $env);\n        Dancer-\u003edance($req);\n    };\n\n    builder {\n        mount '/' =\u003e $dancer;\n        mount '/api/rest' =\u003e RaisinApp-\u003enew;\n    };\n\n## Mojolicious::Lite\n\n    use Plack::Builder;\n    use RaisinApp;\n\n    builder {\n        mount '/' =\u003e builder {\n            enable 'Deflater';\n            require 'my_mojolicious-lite_app.pl';\n        };\n\n        mount '/api/rest' =\u003e RaisinApp-\u003enew;\n    };\n\nSee also [Plack::Builder](https://metacpan.org/pod/Plack%3A%3ABuilder), [Plack::App::URLMap](https://metacpan.org/pod/Plack%3A%3AApp%3A%3AURLMap).\n\n# EXAMPLES\n\nRaisin comes with three instance in _example_ directory:\n\n- pod-synopsis-app\n\n    Basic example.\n\n- music-app\n\n    Shows the possibility of using [\"present\" in Raisin](https://metacpan.org/pod/Raisin#present) with [DBIx::Class](https://metacpan.org/pod/DBIx%3A%3AClass)\n    and [Rose::DB::Object](https://metacpan.org/pod/Rose%3A%3ADB%3A%3AObject).\n\n- sample-app\n\n    Shows an example of complex application.\n\n# ROADMAP\n\n- Versioning support;\n- Mount API's in any place of `resource` block;\n\n# GITHUB\n\n[https://github.com/khrt/Raisin](https://github.com/khrt/Raisin)\n\n# ACKNOWLEDGEMENTS\n\nThis module was inspired both by Grape and [Kelp](https://metacpan.org/pod/Kelp),\nwhich was inspired by [Dancer](https://metacpan.org/pod/Dancer), which in its turn was inspired by Sinatra.\n\n# AUTHOR\n\nArtur Khabibullin\n\n# COPYRIGHT AND LICENSE\n\nThis software is copyright (c) 2019 by Artur Khabibullin.\n\nThis is free software; you can redistribute it and/or modify it under\nthe same terms as the Perl 5 programming language system itself.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkhrt%2Fraisin","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkhrt%2Fraisin","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkhrt%2Fraisin/lists"}