The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

PEF::Front::Guide::QuickStart - a quick-start guide to the PEF::Front web framework.

DESCRIPTION

A quick-start guide with examples to get you up and running with the PEF::Front web framework.

PREPARATION

There're many possible ways to start your PSGI application. You can choose any that you like. Here is one of the most effective possible way.

  nginx -> uwsgi -> PEF::Front(application)

Nginx accepts incoming requests and serves static content. Some defined request it passes to uwsgi. Which works as a very effective PSGI-server and passes semi-prepared requests to PEF::Front framework. Which runs your application on.

Approximate Nginx configuration

This is usually in /etc/nginx/sites-enabled/ directory. Alternatively you can put this (say, app-nginx.conf) configuration file in any directory you like and add include /$FULL_PATH_TO/app-nginx.conf to your nginx.conf file in http section.

  server {
    listen 80 default_server;

    root /$PROJECT_DIR/www-static;
    access_log /$PROJECT_DIR/log/nginx.access.log;
    error_log /$PROJECT_DIR/log/nginx.error.log;
    client_max_body_size 100m;
    server_name $PROJECT.DOMAIN;
    # Static content
    location =/favicon.ico {}
    location /jss/ {}
    location /fonts/ {}
    location /images/ {}
    location /styles/ {}
    location /captchas/ {}
    # Dynamic content
    location / {
      include uwsgi_params;
      uwsgi_pass unix:///run/uwsgi/app/$UWSGI_APP/socket;
      uwsgi_modifier1 5;
    }
    location ~ /\. {
      deny all;
    }
  }

Approximate uwsgi configuration

  /etc/uwsgi/apps-available/$UWSGI_APP.ini
  /etc/uwsgi/apps-enabled/$UWSGI_APP.ini is symbolic link 
   to /etc/uwsgi/apps-available/$UWSGI_APP.ini
   
  [uwsgi]
  plugins = psgi,logfile
  chdir = /$PROJECT_DIR
  logger = file:log/application.log
  psgi = bin/startup.pl
  master = true
  processes = 10
  stats = 127.0.0.1:5000
  perl-no-plack = true
  #cheaper-algo = spare
  #cheaper = 3
  #cheaper-initial = 6
  #cheaper-step = 1
  harakiri = 0
  uid = $PROJECT_USER
  gid = www-data
  chmod-socket = 664

Typical project structure

  cd $PROJECT_DIR
  mkdir  app bin log model templates var www-static
  mkdir app/$MyAPP
  cd app/$MyAPP
  mkdir InFilter Local OutFilter
  cd var
  mkdir cache captcha-db tt_cache upload
  cd ../ www-static
  mkdir captchas images jss styles
  

Now you are ready to start coding your application.

CONFIGURING

AppFrontConfig

Edit /$PROJECT_DIR/app/$MyAPP/AppFrontConfig.pm. Usually you need only something like this:

  package $MyAPP::AppFrontConfig;
  sub cfg_db_user      { $DBUSER }
  sub cfg_db_password  { $DBPASSWORD }
  sub cfg_db_name      { $DBNAME }
  1;

When you don't need multilanguage support, then add

  sub cfg_no_nls { 1 }
  sub cfg_no_multilang_support { 1 }

More info about configuration is here.

startup.pl

Typical startup file

  #!/usr/bin/perl

  use lib qw'/$PROJECT_DIR/app';

  use $MyAPP::AppFrontConfig;
  use PEF::Front::Preload;

  use PEF::Front::Route (
    '/'               => ['/index' 'R'],
    qr'/index(.*)'    => '/appIndex$1',
  );

  PEF::Front::Route->to_app();

PEF::Front::Preload makes database connect and builds validation subroutines for model method descriptions. It's very often needed to make route for '/' path but after that it's up to whether to use default routing scheme or amend it. See PEF::Front::Route for more info about routing.

APPLICATION

"Local" model handlers are located in /$PROJECT_DIR/app/$MyAPP/Local. For example, /$PROJECT_DIR/app/$MyAPP/Local/Article.pm:

  package MyApp::Local::Article;
  use DBIx::Struct qw(connector hash_ref_slice);
  use strict;
  use warnings;

  sub get_articles {
    my ($req, $context) = @_;
    my $articles = all_rows(
        [   "article a" => -join => "author w",
            -columns => ['a.*', 'w.name author']
        ],
        -order_by => {-desc => 'id_article'},
        -limit    => $req->{limit},
        -offset   => $req->{offset},
        sub { $_->filter_timestamp->data }
    );
    for my $article (@$articles) {
        $article->{comment_count} =
          one_row([comment => -columns => 'count(*)'], {hash_ref_slice $article, 'id_article'})->count;
    }
    return {
        result   => "OK",
        articles => $articles,
        count    => one_row([article => -columns => 'count(*)'])->count
    };
  }

Model description for this handler /$PROJECT_DIR/model/GetArticles.yaml:

  ---
  params:
    limit:
      regex: ^\d+$        
      max-size: 3
    offset:
      regex: ^\d+$
      max-size: 10
  model: Article::get_articles

Then this handler can be called from AJAX with request like:

  GET /ajaxGetArticles?limit=5&offset=0

or from template:

  [% articles="get articles".model(limit => 5, offset => 0) %]

AUTHOR

This module was written and is maintained by Anton Petrusevich.

Copyright and License

Copyright (c) 2016 Anton Petrusevich. Some Rights Reserved.

This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself.