package Mojolicious::Lite; use Mojo::Base 'Mojolicious'; # "Bender: Bite my shiny metal ass!" use File::Basename qw(basename dirname); use File::Spec::Functions 'catdir'; use Mojo::UserAgent::Server; use Mojo::Util 'monkey_patch'; sub import { # Remember executable for later $ENV{MOJO_EXE} ||= (caller)[1]; # Reuse home directory if possible local $ENV{MOJO_HOME} = catdir(split '/', dirname $ENV{MOJO_EXE}) unless $ENV{MOJO_HOME}; # Initialize application class my $caller = caller; no strict 'refs'; push @{"${caller}::ISA"}, 'Mojo'; # Generate moniker based on filename my $moniker = basename $ENV{MOJO_EXE}; $moniker =~ s/\.(?:pl|pm|t)$//i; my $app = shift->new(moniker => $moniker); # Initialize routes without namespaces my $routes = $app->routes->namespaces([]); $app->static->classes->[0] = $app->renderer->classes->[0] = $caller; # The Mojolicious::Lite DSL my $root = $routes; for my $name (qw(any get options patch post put websocket)) { monkey_patch $caller, $name, sub { $routes->$name(@_) }; } monkey_patch $caller, $_, sub {$app} for qw(new app); monkey_patch $caller, del => sub { $routes->delete(@_) }; monkey_patch $caller, group => sub (&) { (my $old, $root) = ($root, $routes); shift->(); ($routes, $root) = ($root, $old); }; monkey_patch $caller, helper => sub { $app->helper(@_) }, hook => sub { $app->hook(@_) }, plugin => sub { $app->plugin(@_) }, under => sub { $routes = $root->under(@_) }; # Make sure there's a default application for testing Mojo::UserAgent::Server->app($app) unless Mojo::UserAgent::Server->app; # Lite apps are strict! Mojo::Base->import(-strict); } 1; =encoding utf8 =head1 NAME Mojolicious::Lite - Real-time micro web framework =head1 SYNOPSIS # Automatically enables "strict", "warnings", "utf8" and Perl 5.10 features use Mojolicious::Lite; # Route with placeholder get '/:foo' => sub { my $self = shift; my $foo = $self->param('foo'); $self->render(text => "Hello from $foo."); }; # Start the Mojolicious command system app->start; =head1 DESCRIPTION L<Mojolicious::Lite> is a micro real-time web framework built around L<Mojolicious>. =head1 TUTORIAL A quick example driven introduction to the wonders of L<Mojolicious::Lite>. Most of what you'll learn here also applies to normal L<Mojolicious> applications. =head2 Hello World A simple Hello World application can look like this, L<strict>, L<warnings>, L<utf8> and Perl 5.10 features are automatically enabled and a few functions imported when you use L<Mojolicious::Lite>, turning your script into a full featured web application. #!/usr/bin/env perl use Mojolicious::Lite; get '/' => sub { my $self = shift; $self->render(text => 'Hello World!'); }; app->start; There is also a helper command to generate a small example application. $ mojo generate lite_app myapp.pl =head2 Commands All the normal L<Mojolicious::Commands> are available from the command line. Note that CGI and L<PSGI> environments can usually be auto detected and will just work without commands. $ ./myapp.pl daemon Server available at http://127.0.0.1:3000. $ ./myapp.pl daemon -l http://*:8080 Server available at http://127.0.0.1:8080. $ ./myapp.pl cgi ...CGI output... $ ./myapp.pl get / Hello World! $ ./myapp.pl ...List of available commands (or automatically detected environment)... The C<app-E<gt>start> call that starts the L<Mojolicious> command system should usually be the last expression in your application and can be customized to override normal C<@ARGV> use. app->start('cgi'); =head2 Reloading Your application will automatically reload itself if you start it with the C<morbo> development web server, so you don't have to restart the server after every change. $ morbo myapp.pl Server available at http://127.0.0.1:3000. For more information about how to deploy your application see also L<Mojolicious::Guides::Cookbook/"DEPLOYMENT">. =head2 Routes Routes are basically just fancy paths that can contain different kinds of placeholders and usually lead to an action. The first argument passed to all actions (the invocant C<$self>) is a L<Mojolicious::Controller> object containing both the HTTP request and response. use Mojolicious::Lite; # Route leading to an action get '/foo' => sub { my $self = shift; $self->render(text => 'Hello World!'); }; app->start; Response content is often generated by actions with L<Mojolicious::Controller/"render">, but more about that later. =head2 GET/POST parameters All C<GET> and C<POST> parameters sent with the request are accessible via L<Mojolicious::Controller/"param">. use Mojolicious::Lite; # /foo?user=sri get '/foo' => sub { my $self = shift; my $user = $self->param('user'); $self->render(text => "Hello $user."); }; app->start; =head2 Stash and templates The L<Mojolicious::Controller/"stash"> is used to pass data to templates, which can be inlined in the C<DATA> section. use Mojolicious::Lite; # Route leading to an action that renders a template get '/bar' => sub { my $self = shift; $self->stash(one => 23); $self->render('baz', two => 24); }; app->start; __DATA__ @@ baz.html.ep The magic numbers are <%= $one %> and <%= $two %>. For more information about templates see also L<Mojolicious::Guides::Rendering/"Embedded Perl">. =head2 HTTP L<Mojolicious::Controller/"req"> and L<Mojolicious::Controller/"res"> give you full access to all HTTP features and information. use Mojolicious::Lite; # Access request information get '/agent' => sub { my $self = shift; my $host = $self->req->url->to_abs->host; my $ua = $self->req->headers->user_agent; $self->render(text => "Request by $ua reached $host."); }; # Echo the request body and send custom header with response post '/echo' => sub { my $self = shift; $self->res->headers->header('X-Bender' => 'Bite my shiny metal ass!'); $self->render(data => $self->req->body); }; app->start; You can test the more advanced examples right from the command line with L<Mojolicious::Command::get>. $ ./myapp.pl get -v -M POST -c 'test' /echo =head2 Built-in C<exception> and C<not_found> pages During development you will encounter these pages whenever you make a mistake, they are gorgeous and contain a lot of valuable information that will aid you in debugging your application. use Mojolicious::Lite; # Not found (404) get '/missing' => sub { shift->render('does_not_exist') }; # Exception (500) get '/dies' => sub { die 'Intentional error' }; app->start; You can even use CSS selectors with L<Mojolicious::Command::get> to extract only the information you're actually interested in. $ ./myapp.pl get /dies '#error' =head2 Route names All routes can have a name associated with them, this allows automatic template detection and back referencing with L<Mojolicious::Controller/"url_for"> as well as many helpers like L<Mojolicious::Plugin::TagHelpers/"link_to">. Nameless routes get an automatically generated one assigned that is simply equal to the route itself without non-word characters. use Mojolicious::Lite; # Render the template "index.html.ep" get '/' => sub { my $self = shift; $self->render; } => 'index'; # Render the template "hello.html.ep" get '/hello'; app->start; __DATA__ @@ index.html.ep <%= link_to Hello => 'hello' %>. <%= link_to Reload => 'index' %>. @@ hello.html.ep Hello World! =head2 Layouts Templates can have layouts too, you just select one with the helper L<Mojolicious::Plugin::DefaultHelpers/"layout"> and place the result of the current template with the helper L<Mojolicious::Plugin::DefaultHelpers/"content">. use Mojolicious::Lite; get '/with_layout'; app->start; __DATA__ @@ with_layout.html.ep % title 'Green'; % layout 'green'; Hello World! @@ layouts/green.html.ep <!DOCTYPE html> <html> <head><title><%= title %></title></head> <body><%= content %></body> </html> The stash or helpers like L<Mojolicious::Plugin::DefaultHelpers/"title"> can be used to pass additional data to the layout. =head2 Blocks Template blocks can be used like normal Perl functions and are always delimited by the C<begin> and C<end> keywords, they are the foundation for many helpers. use Mojolicious::Lite; get '/with_block' => 'block'; app->start; __DATA__ @@ block.html.ep % my $link = begin % my ($url, $name) = @_; Try <%= link_to $url => begin %><%= $name %><% end %>. % end <!DOCTYPE html> <html> <head><title>Sebastians frameworks</title></head> <body> %= $link->('http://mojolicio.us', 'Mojolicious') %= $link->('http://catalystframework.org', 'Catalyst') </body> </html> =head2 Helpers You can also extend L<Mojolicious> with your own helpers, a list of all built-in ones can be found in L<Mojolicious::Plugin::DefaultHelpers> and L<Mojolicious::Plugin::TagHelpers>. use Mojolicious::Lite; # A helper to identify visitors helper whois => sub { my $self = shift; my $agent = $self->req->headers->user_agent || 'Anonymous'; my $ip = $self->tx->remote_address; return "$agent ($ip)"; }; # Use helper in action and template get '/secret' => sub { my $self = shift; my $user = $self->whois; $self->app->log->debug("Request from $user."); }; app->start; __DATA__ @@ secret.html.ep We know who you are <%= whois %>. =head2 Placeholders Route placeholders allow capturing parts of a request path until a C</> or C<.> separator occurs, results are accessible via L<Mojolicious::Controller/"stash"> and L<Mojolicious::Controller/"param">. use Mojolicious::Lite; # /foo/test # /foo/test123 get '/foo/:bar' => sub { my $self = shift; my $bar = $self->stash('bar'); $self->render(text => "Our :bar placeholder matched $bar"); }; # /testsomething/foo # /test123something/foo get '/(:bar)something/foo' => sub { my $self = shift; my $bar = $self->param('bar'); $self->render(text => "Our :bar placeholder matched $bar"); }; app->start; =head2 Relaxed Placeholders Relaxed placeholders allow matching of everything until a C</> occurs. use Mojolicious::Lite; # /test/hello # /test123/hello # /test.123/hello get '/#you/hello' => 'groovy'; app->start; __DATA__ @@ groovy.html.ep Your name is <%= $you %>. =head2 Wildcard placeholders Wildcard placeholders allow matching absolutely everything, including C</> and C<.>. use Mojolicious::Lite; # /hello/test # /hello/test123 # /hello/test.123/test/123 get '/hello/*you' => 'groovy'; app->start; __DATA__ @@ groovy.html.ep Your name is <%= $you %>. =head2 HTTP methods Routes can be restricted to specific request methods with different keywords. use Mojolicious::Lite; # GET /hello get '/hello' => sub { my $self = shift; $self->render(text => 'Hello World!'); }; # PUT /hello put '/hello' => sub { my $self = shift; my $size = length $self->req->body; $self->render(text => "You uploaded $size bytes to /hello."); }; # GET|POST|PATCH /bye any [qw(GET POST PATCH)] => '/bye' => sub { my $self = shift; $self->render(text => 'Bye World!'); }; # * /whatever any '/whatever' => sub { my $self = shift; my $method = $self->req->method; $self->render(text => "You called /whatever with $method."); }; app->start; =head2 Optional placeholders All placeholders require a value, but by assigning them default values you can make capturing optional. Default values that don't belong to a placeholder simply get merged into the stash all the time. use Mojolicious::Lite; # /hello # /hello/Sara get '/hello/:name' => {name => 'Sebastian', day => 'Monday'} => sub { my $self = shift; $self->render('groovy', format => 'txt'); }; app->start; __DATA__ @@ groovy.txt.ep My name is <%= $name %> and it is <%= $day %>. =head2 Restrictive placeholders The easiest way to make placeholders more restrictive are alternatives, you just make a list of possible values. use Mojolicious::Lite; # /test # /123 any '/:foo' => [foo => [qw(test 123)]] => sub { my $self = shift; my $foo = $self->param('foo'); $self->render(text => "Our :foo placeholder matched $foo"); }; app->start; All placeholders get compiled to a regular expression internally, this process can also be easily customized. use Mojolicious::Lite; # /1 # /123 any '/:bar' => [bar => qr/\d+/] => sub { my $self = shift; my $bar = $self->param('bar'); $self->render(text => "Our :bar placeholder matched $bar"); }; app->start; Just make sure not to use C<^> and C<$> or capturing groups C<(...)>, because placeholders become part of a larger regular expression internally, C<(?:...)> is fine though. =head2 Under Authentication and code shared between multiple routes can be realized easily with bridge routes generated by the L</"under"> statement. All following routes are only evaluated if the callback returned a true value. use Mojolicious::Lite; # Authenticate based on name parameter under sub { my $self = shift; # Authenticated my $name = $self->param('name') || ''; return 1 if $name eq 'Bender'; # Not authenticated $self->render('denied'); return undef; }; # Only reached when authenticated get '/' => 'index'; app->start; __DATA__ @@ denied.html.ep You are not Bender, permission denied. @@ index.html.ep Hi Bender. Prefixing multiple routes is another good use for L</"under">. use Mojolicious::Lite; # /foo under '/foo'; # /foo/bar get '/bar' => {text => 'foo bar'}; # /foo/baz get '/baz' => {text => 'foo baz'}; # / (reset) under '/' => {msg => 'whatever'}; # /bar get '/bar' => {inline => '<%= $msg %> works'}; app->start; You can also L</"group"> related routes, which allows nesting of multiple L</"under"> statements. use Mojolicious::Lite; # Global logic shared by all routes under sub { my $self = shift; return 1 if $self->req->headers->header('X-Bender'); $self->render(text => "You're not Bender."); return undef; }; # Admin section group { # Local logic shared only by routes in this group under '/admin' => sub { my $self = shift; return 1 if $self->req->headers->header('X-Awesome'); $self->render(text => "You're not awesome enough."); return undef; }; # GET /admin/dashboard get '/dashboard' => {text => 'Nothing to see here yet.'}; }; # GET /welcome get '/welcome' => {text => 'Hi Bender.'}; app->start; =head2 Formats Formats can be automatically detected by looking at file extensions. use Mojolicious::Lite; # /detection.html # /detection.txt get '/detection' => sub { my $self = shift; $self->render('detected'); }; app->start; __DATA__ @@ detected.html.ep <!DOCTYPE html> <html> <head><title>Detected</title></head> <body>HTML was detected.</body> </html> @@ detected.txt.ep TXT was detected. Restrictive placeholders can also be used. use Mojolicious::Lite; # /hello.json # /hello.txt get '/hello' => [format => [qw(json txt)]] => sub { my $self = shift; return $self->render(json => {hello => 'world'}) if $self->stash('format') eq 'json'; $self->render(text => 'hello world'); }; app->start; Or you can just disable format detection. use Mojolicious::Lite; # /hello get '/hello' => [format => 0] => {text => 'No format detection.'}; # Disable detection and allow the following routes selective re-enabling under [format => 0]; # /foo get '/foo' => {text => 'No format detection again.'}; # /bar.txt get '/bar' => [format => 'txt'] => {text => ' Just one format.'}; app->start; =head2 Content negotiation For resources with different representations and that require truly RESTful content negotiation you can also use L<Mojolicious::Controller/"respond_to">. use Mojolicious::Lite; # /hello (Accept: application/json) # /hello (Accept: application/xml) # /hello.json # /hello.xml # /hello?format=json # /hello?format=xml get '/hello' => sub { my $self = shift; $self->respond_to( json => {json => {hello => 'world'}}, xml => {text => '<hello>world</hello>'}, any => {data => '', status => 204} ); }; app->start; MIME type mappings can be extended or changed easily with L<Mojolicious/"types">. app->types->type(rdf => 'application/rdf+xml'); =head2 Static files Similar to templates, but with only a single file extension and optional Base64 encoding, static files can be inlined in the C<DATA> section and are served automatically. use Mojolicious::Lite; app->start; __DATA__ @@ something.js alert('hello!'); @@ test.txt (base64) dGVzdCAxMjMKbGFsYWxh External static files are not limited to a single file extension and will be served automatically from a C<public> directory if it exists. $ mkdir public $ mv something.js public/something.js $ mv mojolicious.tar.gz public/mojolicious.tar.gz Both have a higher precedence than routes. =head2 External templates External templates will be searched by the renderer in a C<templates> directory if it exists and have a higher precedence than those in the C<DATA> section. use Mojolicious::Lite; # Render template "templates/foo/bar.html.ep" any '/external' => sub { my $self = shift; $self->render('foo/bar'); }; app->start; =head2 Conditions Conditions such as C<agent> and C<host> from L<Mojolicious::Plugin::HeaderCondition> allow even more powerful route constructs. use Mojolicious::Lite; # Firefox get '/foo' => (agent => qr/Firefox/) => sub { my $self = shift; $self->render(text => 'Congratulations, you are using a cool browser.'); }; # Internet Explorer get '/foo' => (agent => qr/Internet Explorer/) => sub { my $self = shift; $self->render(text => 'Dude, you really need to upgrade to Firefox.'); }; # http://mojolicio.us/bar get '/bar' => (host => 'mojolicio.us') => sub { my $self = shift; $self->render(text => 'Hello Mojolicious.'); }; app->start; =head2 Sessions Signed cookie based sessions just work out of the box as soon as you start using them through the helper L<Mojolicious::Plugin::DefaultHelpers/"session">, just be aware that all session data gets serialized with L<Mojo::JSON>. use Mojolicious::Lite; # Access session data in action and template get '/counter' => sub { my $self = shift; $self->session->{counter}++; }; app->start; __DATA__ @@ counter.html.ep Counter: <%= session 'counter' %> Note that you should use custom L<Mojolicious/"secrets"> to make signed cookies really secure. app->secrets(['My secret passphrase here']); =head2 File uploads All files uploaded via C<multipart/form-data> request are automatically available as L<Mojo::Upload> objects. And you don't have to worry about memory usage, because all files above 250KB will be automatically streamed into a temporary file. use Mojolicious::Lite; # Upload form in DATA section get '/' => 'form'; # Multipart upload handler post '/upload' => sub { my $self = shift; # Check file size return $self->render(text => 'File is too big.', status => 200) if $self->req->is_limit_exceeded; # Process uploaded file return $self->redirect_to('form') unless my $example = $self->param('example'); my $size = $example->size; my $name = $example->filename; $self->render(text => "Thanks for uploading $size byte file $name."); }; app->start; __DATA__ @@ form.html.ep <!DOCTYPE html> <html> <head><title>Upload</title></head> <body> %= form_for upload => (enctype => 'multipart/form-data') => begin %= file_field 'example' %= submit_button 'Upload' % end </body> </html> To protect you from excessively large files there is also a limit of 10MB by default, which you can tweak with the attribute L<Mojo::Message/"max_message_size"> or C<MOJO_MAX_MESSAGE_SIZE> environment variable. # Increase limit to 1GB $ENV{MOJO_MAX_MESSAGE_SIZE} = 1073741824; =head2 User agent With L<Mojo::UserAgent>, which is available through the helper L<Mojolicious::Plugin::DefaultHelpers/"ua">, there's a full featured HTTP and WebSocket user agent built right in. Especially in combination with L<Mojo::JSON> and L<Mojo::DOM> this can be a very powerful tool. use Mojolicious::Lite; # Blocking get '/headers' => sub { my $self = shift; my $url = $self->param('url') || 'http://mojolicio.us'; my $dom = $self->ua->get($url)->res->dom; $self->render(json => [$dom->find('h1, h2, h3')->text->each]); }; # Non-blocking get '/title' => sub { my $self = shift; $self->ua->get('mojolicio.us' => sub { my ($ua, $tx) = @_; $self->render(data => $tx->res->dom->at('title')->text); }); }; # Concurrent non-blocking get '/titles' => sub { my $self = shift; my $delay = Mojo::IOLoop->delay(sub { my ($delay, @titles) = @_; $self->render(json => \@titles); }); for my $url ('http://mojolicio.us', 'https://metacpan.org') { my $end = $delay->begin(0); $self->ua->get($url => sub { my ($ua, $tx) = @_; $end->($tx->res->dom->html->head->title->text); }); } }; app->start; For more information about the user agent see also L<Mojolicious::Guides::Cookbook/"USER AGENT">. =head2 WebSockets WebSocket applications have never been this simple before. Just receive messages by subscribing to events such as L<Mojo::Transaction::WebSocket/"json"> with L<Mojolicious::Controller/"on"> and return them with L<Mojolicious::Controller/"send">. use Mojolicious::Lite; websocket '/echo' => sub { my $self = shift; $self->on(json => sub { my ($self, $hash) = @_; $hash->{msg} = "echo: $hash->{msg}"; $self->send({json => $hash}); }); }; get '/' => 'index'; app->start; __DATA__ @@ index.html.ep <!DOCTYPE html> <html> <head> <title>Echo</title> <script> var ws = new WebSocket('<%= url_for('echo')->to_abs %>'); ws.onmessage = function (event) { document.body.innerHTML += JSON.parse(event.data).msg; }; ws.onopen = function (event) { ws.send(JSON.stringify({msg: 'I ♥ Mojolicious!'})); }; </script> </head> </html> For more information about real-time web features see also L<Mojolicious::Guides::Cookbook/"REAL-TIME WEB">. =head2 Mode You can use the L<Mojo::Log> object from L<Mojo/"log"> to portably collect debug messages and automatically disable them later in a production setup by changing the L<Mojolicious> operating mode, which can also be retrieved from the attribute L<Mojolicious/"mode">. use Mojolicious::Lite; # Prepare mode specific message during startup my $msg = app->mode eq 'development' ? 'Development!' : 'Something else!'; get '/' => sub { my $self = shift; $self->app->log->debug('Rendering mode specific message.'); $self->render(text => $msg); }; app->log->debug('Starting application.'); app->start; The default operating mode will usually be C<development> and can be changed with command line options or the C<MOJO_MODE> and C<PLACK_ENV> environment variables. A mode other than C<development> will raise the log level from C<debug> to C<info>. $ ./myapp.pl daemon -m production All messages will be written to C<STDERR> or a C<log/$mode.log> file if a C<log> directory exists. $ mkdir log Mode changes also affect a few other aspects of the framework, such as mode specific C<exception> and C<not_found> templates. =head2 Testing Testing your application is as easy as creating a C<t> directory and filling it with normal Perl tests, which can be a lot of fun thanks to L<Test::Mojo>. use Test::More; use Test::Mojo; use FindBin; require "$FindBin::Bin/../myapp.pl"; my $t = Test::Mojo->new; $t->get_ok('/')->status_is(200)->content_like(qr/Funky/); done_testing(); Run all tests with the command L<Mojolicious::Command::test>. $ ./myapp.pl test $ ./myapp.pl test -v =head2 More You can continue with L<Mojolicious::Guides> now, and don't forget to have fun! =head1 FUNCTIONS L<Mojolicious::Lite> implements the following functions, which are automatically exported. =head2 any my $route = any '/:foo' => sub {...}; my $route = any '/:foo' => {foo => 'bar'} => sub {...}; my $route = any '/:foo' => [foo => qr/\w+/] => sub {...}; my $route = any [qw(GET POST)] => '/:foo' => sub {...}; Generate route with L<Mojolicious::Routes::Route/"any">, matching any of the listed HTTP request methods or all. See also the tutorial above for many more argument variations. =head2 app my $app = app; The L<Mojolicious::Lite> application. =head2 del my $route = del '/:foo' => sub {...}; my $route = del '/:foo' => {foo => 'bar'} => sub {...}; my $route = del '/:foo' => [foo => qr/\w+/] => sub {...}; Generate route with L<Mojolicious::Routes::Route/"delete">, matching only C<DELETE> requests. See also the tutorial above for many more argument variations. =head2 get my $route = get '/:foo' => sub {...}; my $route = get '/:foo' => {foo => 'bar'} => sub {...}; my $route = get '/:foo' => [foo => qr/\w+/] => sub {...}; Generate route with L<Mojolicious::Routes::Route/"get">, matching only C<GET> requests. See also the tutorial above for many more argument variations. =head2 group group {...}; Start a new route group. =head2 helper helper foo => sub {...}; Add a new helper with L<Mojolicious/"helper">. =head2 hook hook after_dispatch => sub {...}; Share code with L<Mojolicious/"hook">. =head2 options my $route = options '/:foo' => sub {...}; my $route = options '/:foo' => {foo => 'bar'} => sub {...}; my $route = options '/:foo' => [foo => qr/\w+/] => sub {...}; Generate route with L<Mojolicious::Routes::Route/"options">, matching only C<OPTIONS> requests. See also the tutorial above for many more argument variations. =head2 patch my $route = patch '/:foo' => sub {...}; my $route = patch '/:foo' => {foo => 'bar'} => sub {...}; my $route = patch '/:foo' => [foo => qr/\w+/] => sub {...}; Generate route with L<Mojolicious::Routes::Route/"patch">, matching only C<PATCH> requests. See also the tutorial above for many more argument variations. =head2 plugin plugin SomePlugin => {foo => 23}; Load a plugin with L<Mojolicious/"plugin">. =head2 post my $route = post '/:foo' => sub {...}; my $route = post '/:foo' => {foo => 'bar'} => sub {...}; my $route = post '/:foo' => [foo => qr/\w+/] => sub {...}; Generate route with L<Mojolicious::Routes::Route/"post">, matching only C<POST> requests. See also the tutorial above for many more argument variations. =head2 put my $route = put '/:foo' => sub {...}; my $route = put '/:foo' => {foo => 'bar'} => sub {...}; my $route = put '/:foo' => [foo => qr/\w+/] => sub {...}; Generate route with L<Mojolicious::Routes::Route/"put">, matching only C<PUT> requests. See also the tutorial above for many more argument variations. =head2 under my $bridge = under sub {...}; my $bridge = under '/:foo' => sub {...}; my $bridge = under '/:foo' => [foo => qr/\w+/]; my $bridge = under {format => 0}; Generate bridge route with L<Mojolicious::Routes::Route/"under">, to which all following routes are automatically appended. See also the tutorial above for more argument variations. =head2 websocket my $route = websocket '/:foo' => sub {...}; my $route = websocket '/:foo' => {foo => 'bar'} => sub {...}; my $route = websocket '/:foo' => [foo => qr/\w+/] => sub {...}; Generate route with L<Mojolicious::Routes::Route/"websocket">, matching only WebSocket handshakes. See also the tutorial above for many more argument variations. =head1 ATTRIBUTES L<Mojolicious::Lite> inherits all attributes from L<Mojolicious>. =head1 METHODS L<Mojolicious::Lite> inherits all methods from L<Mojolicious>. =head1 SEE ALSO L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicio.us>. =cut