Amon2-Lite

 view release on metacpan or  search on metacpan

lib/Amon2/Lite.pm  view on Meta::CPAN

        unshift @{"$caller\::ISA"}, $base_class;
    }

    *{"$caller\::to_app"} = sub {
        my ($class, %opts) = @_;

        my $app = $class->Amon2::Web::to_app();
        if (delete $opts{handle_static}) {
            my $vpath = Data::Section::Simple->new($caller)->get_data_section();
            require Plack::App::File;
            my $orig_app = $app;
            my $app_file_1;
            my $app_file_2;
            my $root1 = File::Spec->catdir( dirname((caller(0))[1]), 'static' );
            my $root2 = File::Spec->catdir( dirname((caller(0))[1]) );
            $app = sub {
                my $env = shift;
                if ((my $content = $vpath->{$env->{PATH_INFO}}) && $env->{PATH_INFO} =~ m{^/}) {
                    my $ct = Plack::MIME->mime_type($env->{PATH_INFO});
                    return [200, ['Content-Type' => $ct, 'Content-Length' => length($content)], [$content]];
                } elsif ($env->{PATH_INFO} =~ qr{^(?:/robots\.txt|/favicon\.ico)$}) {
                    $app_file_1 ||= Plack::App::File->new({ root => $root1 });
                    return $app_file_1->call($env);
                } elsif ($env->{PATH_INFO} =~ m{^/static/}) {
                    $app_file_2 ||= Plack::App::File->new({ root => $root2 });
                    return $app_file_2->call($env);
                } else {
                    return $orig_app->($env);
                }
            };
        }
        if (my @middlewares = @{"${caller}::_MIDDLEWARES"}) {
            for my $middleware (@middlewares) {
                my ($klass, $args) = @$middleware;
                $klass = Plack::Util::load_class($klass, 'Plack::Middleware');
                $app = $klass->wrap($app, %$args);
            }
        }
        unless ($opts{no_x_content_type_options}) {
            $class->add_trigger(AFTER_DISPATCH => sub {
                my ($c, $res) = @_;
                $res->header( 'X-Content-Type-Options' => 'nosniff' );
            });
        }
        unless ($opts{no_x_frame_options}) {
            $class->add_trigger(AFTER_DISPATCH => sub {
                my ($c, $res) = @_;
                $res->header( 'X-Frame-Options' => 'DENY' );
            });
        }
        return $app;
    };

    *{"${base_class}::enable_middleware"} = sub {
        my ($class, $klass, %args) = @_;
        push @{"${caller}::_MIDDLEWARES"}, [$klass, \%args];
    };
    *{"${base_class}::enable_session"} = sub {
        my ($class, %args) = @_;
        $args{state} ||= do {
            require Plack::Session::State::Cookie;
            Plack::Session::State::Cookie->new(httponly => 1); # for security
        };
        require Plack::Middleware::Session;
        $class->enable_middleware('Plack::Middleware::Session', %args);
        $class->add_trigger(AFTER_DISPATCH => sub {
            my ($c, $res) = @_;
            $res->header('Cache-Control' => 'private');
        });
    };

    *{"$caller\::router"} = sub { $router };

    # any [qw/get post delete/] => '/bye' => sub { ... };
    # any '/bye' => sub { ... };
    *{"$caller\::any"} = sub ($$;$) {
        my $pkg = caller(0);
        if (@_==3) {
            my ($methods, $pattern, $code) = @_;
            $router->connect(
                $pattern,
                {code => $code, method => [ map { uc $_ } @$methods ]},
                {method => [map { uc $_ } @$methods]},
            );
        } else {
            my ($pattern, $code) = @_;
            $router->connect(
                $pattern,
                {code => $code},
            );
        }
    };

    *{"$caller\::get"} = sub {
        $router->connect($_[0], {code => $_[1], method => ['GET', 'HEAD']}, {method => 'GET'});
    };

    *{"$caller\::post"} = sub {
        $router->connect($_[0], {code => $_[1], method => ['POST']}, {method => ['POST']});
    };

    *{"${base_class}\::dispatch"} = sub {
        my ($c) = @_;
        if (my $p = $router->match($c->request->env)) {
            return $p->{code}->( $c, $p );
        } else {
            if ($router->method_not_allowed) {
                my $content = '405 Method Not Allowed';
                return $c->create_response(
                    405,
                    [
                        'Content-Type'   => 'text/plain; charset=utf-8',
                        'Content-Length' => length($content),
                    ],
                    [$content]
                );
            } else {
                return $c->res_404();
            }
        }
    };

lib/Amon2/Lite.pm  view on Meta::CPAN


=encoding utf8

=head1 NAME

Amon2::Lite - Sinatra-ish framework on Amon2!

=head1 SYNOPSIS

    use Amon2::Lite;

    get '/' => sub {
        my ($c) = @_;
        return $c->render('index.tt');
    };

    __PACKAGE__->to_app();

    __DATA__

    @@ index.tt
    <!doctype html>
    <html>
        <body>Hello</body>
    </html>

=head1 DESCRIPTION

This is a Sinatra-ish wrapper for Amon2.

B<THIS MODULE IS BETA STATE. API MAY CHANGE WITHOUT NOTICE>.

=head1 FUNCTIONS

=over 4

=item C<< any(\@methods, $path, \&code) >>

=item C<< any($path, \&code) >>

Register new route for router.

=item C<< get($path, $code->($c)) >>

Register new route for router.

=item C<< post($path, $code->($c)) >>

Register new route for router.

=item C<< __PACKAGE__->load_plugin($name, \%opts) >>

Load a plugin to the context object.

=item [EXPERIMENTAL] C<< __PACKAGE__->enable_session(%args) >>

This method enables L<Plack::Middleware::Session>.

C<< %args >> would be pass to enabled to C<< Plack::Middleware::Session->new >>.

The default state class is L<Plack::Session::State::Cookie>, and store class is L<Plack::Session::Store::File>.

This option enables a response filter, that adds C< Cache-Control: private > header.

=item [EXPERIMENTAL] C<< __PACKAGE__->enable_middleware($klass, %args) >>

    __PACKAGE__->enable_middleware('Plack::Middleware::XFramework', framework => 'Amon2::Lite');

Enable the Plack middlewares.

=item C<< __PACKAGE__->to_app(%args) >>

Create new PSGI application instance.

There is a options.

=over 4

=item C<< no_x_content_type_options : default false >>

    __PACKAGE__->to_app(no_x_content_type_options => 1);

Amon2::Lite puts C<< X-Content-Type-Options >> header by default for security reason.
You can disable this feature by this option.

=item C<< no_x_frame_options >>

    __PACKAGE__->to_app(no_x_frame_options => 1);

Amon2::Lite puts C<< X-Frame-Options: DENY >> header by default for security reason.
You can disable this feature by this option.

=back

=back

=head1 FAQ

=over 4

=item How can I configure the options for Xslate?

You can provide a constructor arguments by configuration.
Write following lines on your app.psgi.

    __PACKAGE__->template_options(
        syntax => 'Kolon',
    );

=item How can I use other template engines instead of Text::Xslate?

You can use any template engine with Amon2::Lite. You can overwrite create_view method same as normal Amon2.

This is a example to use L<Text::MicroTemplate::File>.

    use Tiffany::Text::MicroTemplate::File;

    sub create_view {
        Tiffany::Text::MicroTemplate::File->new(+{
            include_path => ['./tmpl/']
        })



( run in 0.491 second using v1.01-cache-2.11-cpan-6aa56a78535 )