Acrux

 view release on metacpan or  search on metacpan

lib/Acme/Crux.pm  view on Meta::CPAN

package Acme::Crux;
use warnings;
use strict;
use utf8;

=encoding utf-8

=head1 NAME

Acme::Crux - The CTK::App of the next generation

=head1 SYNOPSIS

    use Acme::Crux;

=head1 DESCRIPTION

The CTK::App of the next generation

=head2 new

    my $app = Acme::Crux->new(
        project     => 'MyApp',
        moniker     => 'myapp',

        options     => {foo => 'bar'},

        plugins     => { foo => 'MyApp::Foo', bar => 'MyApp::Bar' },
        preload     => 'Config, Log',

        cachedir    => '/var/cache/myapp',
        configfile  => '/etc/myapp/myapp.conf',
        datadir     => '/var/lib/myapp',
        docdir      => '/usr/share/doc/myapp',
        lockdir     => '/var/lock/myapp',
        logdir      => '/var/log/myapp',
        logfile     => '/var/log/myapp/myapp.log',
        pidfile     => '/var/run/myapp/myapp.pid',
        root        => '/etc/myapp',
        rundir      => '/var/run/myapp',
        sharedir    => '/usr/share/myapp',
        spooldir    => '/var/spool/myapp',
        tempdir     => '/tmp/myapp',
        webdir      => '/var/www/myapp',

        debug       => 0,
        test        => 0,
        verbose     => 0,
    );

=head1 ATTRIBUTES

This class implements the following attributes

=head2 cachedir

    cachedir => '/var/cache/myapp'

Cache dir for project cache files

    $app = $app->cachedir( "/path/to/cache/dir" );
    my $cachedir = $app->cachedir;

Default: /var/cache/<MONIKER>

=head2 configfile

    configfile => '/etc/myapp/myapp.conf'

Path to the configuration file of your project

    $app = $app->configfile( "/path/to/config/file.conf" );
    my $configfile = $app->configfile;

Default: /etc/<MONIKER>/<MONIKER>.conf

=head2 datadir

    datadir => '/var/lib/myapp'

Data dir of project

    $app = $app->datadir( "/path/to/data/dir" );
    my $datadir = $app->datadir;

Default: /var/lib/<MONIKER>

=head2 debug

    debug => 1
    debug => 'on'
    debug => 'yes'

Debug mode

Default: 0

=head2 docdir

    docdir => '/usr/share/doc/myapp'

Doc dir for project documentation

    $app = $app->docdir( "/path/to/docs/dir" );
    my $docdir = $app->docdir;

Default: /usr/share/doc/<MONIKER>

=head2 lockdir

    lockdir => '/var/lock/myapp'

Lock dir for project lock files

    $app = $app->lockdir( "/path/to/lock/dir" );
    my $lockdir = $app->lockdir;

Default: /var/lock/<MONIKER>

=head2 logdir

    logdir => '/var/log/myapp'

Log dir for project logging

    $app = $app->logdir( "/path/to/log/dir" );
    my $logdir = $app->logdir;

Default: /var/log/<MONIKER>

=head2 logfile

    logfile => '/var/log/myapp/myapp.log'

Path to the log file

    $app = $app->logfile( "/path/to/file.log" );
    my $logfile = $app->logfile;

Default: /var/log/<MONIKER>/<MONIKER>.log

=head2 moniker

    moniker => 'myapp'

This attribute sets moniker of project name.

Moniker B<SHOULD> contains only symbols: a-z, 0-9, '_', '-', '.'

    $app = $app->moniker( 'myapp' );
    my $moniker = $app->moniker;

lib/Acme/Crux.pm  view on Meta::CPAN


Temp dir for project temporary files

    $app = $app->tempdir( "/path/to/temp/dir" );
    my $tempdir = $app->tempdir;

Default: /tmp/<MONIKER>

=head2 test

    test => 1
    test => 'on'
    test => 'yes'

Test mode

Default: 0

=head2 verbose

    verbose => 1
    verbose => 'on'
    verbose => 'yes'

Verbose mode

Default: 0

=head2 webdir

    webdir => '/var/www/myapp'

Web dir for project web files (DocumentRoot)

    $app = $app->webdir( "/path/to/webdoc/dir" );
    my $webdirr = $app->webdir;

Default: /var/www/<MONIKER>

=head1 METHODS

This class implements the following methods

=head2 startup

This is your main hook into the application, it will be called at application startup.
Meant to be overloaded in a subclass.

This method is called immediately after creating the instance and returns it

B<NOTE:> Please use only in your subclasses!

    sub startup {
        my $self = shift;

        . . .

        return $self; # REQUIRED!
    }

=head2 debugmode

    $app->debugmode;

Returns debug flag. 1 - on, 0 - off

=head2 begin

    my $timing_begin = $app->begin;

This method sets timestamp for L</elapsed>

    my $timing_begin = $app->begin;
    # ... long operations ...
    my $elapsed = $app->elapsed( $timing_begin );

=head2 elapsed

    my $elapsed = $app->elapsed;

    my $timing_begin = [gettimeofday];
    # ... long operations ...
    my $elapsed = $app->elapsed( $timing_begin );

Return fractional amount of time in seconds since unnamed timstamp has been created while start application

    my $elapsed = $app->elapsed;
    $app->log->debug("Database stuff took $elapsed seconds");

For formatted output:

    $app->log->debug(sprintf("%+.*f sec", 4, $app->elapsed));

=head2 error

    my $error = $app->error;

Returns error string if occurred any errors while working with application

    $app = $app->error( "error text" );

Sets new error message and returns object

=head2 exedir

    my $exedir = $app->exedir;

Gets exedir value

=head2 handlers

    my @names = $app->handlers;

Returns list of names of registered handlers

    my @names_and_aliases = $app->handlers(1);

Returns list of aliases and names of registered handlers

=head2 lookup_handler

    my $handler = $app->lookup_handler($name)
        or die "Handler not found";

Lookup handler by name or aliase. Returns handler or undef while error

=head2 option, opt, getopt

    my $value = $app->option("key");

Returns option value by key

    my $options = $app->option;

Returns hash-ref structure to all options

See L</options>

=head2 orig

    my $origin_args = $app->orig;

Returns hash-ref structure to all origin arguments

=head2 plugin

    $app->plugin(foo => 'MyApp::Plugin::Foo');
    $app->plugin(foo);
    $app->plugin(foo => 'MyApp::Plugin::Foo', {bar => 123, baz => 'test'});
    $app->plugin(foo => 'MyApp::Plugin::Foo', bar => 123, baz => 'test');
    $app->plugin(foo, undef, {bar => 123, baz => 'test'});

lib/Acme/Crux.pm  view on Meta::CPAN

=head1 PLUGINS

The following plugins are included in the Acrux distribution

=over 4

=item L<Acme::Crux::Plugin::Config>

    Config => Acme::Crux::Plugin::Config

L<Acrux::Config> configuration plugin

=item L<Acme::Crux::Plugin::Log>

    Log => Acme::Crux::Plugin::Log

L<Acrux::Log> logging plugin

=back

=head1 TO DO

See C<TODO> file

=head1 SEE ALSO

L<CTK>, L<CTK::App>

=head1 AUTHOR

Serż Minus (Sergey Lepenkov) L<https://www.serzik.com> E<lt>abalama@cpan.orgE<gt>

=head1 COPYRIGHT

Copyright (C) 1998-2026 D&D Corporation

=head1 LICENSE

This program is distributed under the terms of the Artistic License Version 2.0

See the C<LICENSE> file or L<https://opensource.org/license/artistic-2-0> for details

=cut

use Carp qw/carp croak/;
use Time::HiRes qw/gettimeofday tv_interval/;
use FindBin qw/$RealBin $Script/;
use File::Spec qw//;
use Cwd qw/getcwd/;
use Sub::Util qw/set_subname/;
use Acrux::RefUtil qw/
        as_hash_ref is_hash_ref
        as_array_ref is_array_ref
        is_value is_code_ref is_true_flag
    /;
use Acrux::Const qw/:dir/;
use Acrux::Util qw/load_class trim words/;

use constant {
    WIN             => !!($^O =~ /mswin/i),
    ALOWED_MODES    => [qw/debug test verbose/],
    PRELOAD_PLUGINS => [qw/Config Log/], # Order is very important!
    DEFAULT_PLUGINS => {
        Config  => "Acme::Crux::Plugin::Config",
        Log     => "Acme::Crux::Plugin::Log",
    },
};

sub new {
    my $class = shift;
    my $args = @_ ? @_ > 1 ? {@_} : {%{$_[0]}} : {};

    # Get project name and moniker
    my $project = $args->{project} || $args->{name}
      || ($Script =~ /^(.+?)\.(pl|t|pm|cgi)$/ ? $1 : $Script)
      || $class || scalar(caller(0));
    my $moniker = $args->{moniker} || _project2moniker($project)
      || _project2moniker($class || scalar(caller(0)));

    # Current dir
    my $pwd = getcwd();

    # Create
    my $self = bless {
        # Common
        error       => "",
        script      => $Script,
        invocant    => scalar(caller(0)),
        project     => $project,
        moniker     => $moniker,
        pid         => $$,
        running     => 0,

        # General
        orig        => {%$args},
        created     => Time::HiRes::time,
        hitime      => [gettimeofday],
        options     => as_hash_ref($args->{options}), # Options of command line
        plugins     => {},
        preload_plugins => $args->{preload} || $args->{preload_plugins} || PRELOAD_PLUGINS,

        # Modes (defaults)
        debugmode   => 0,
        testmode    => 0,
        verbosemode => 0,

        # Dirs
        pwd         => $pwd,
        exedir      => $RealBin, # Script dir
        root        => $args->{root}, # Root dir of project. Default: /etc/moniker
        tempdir     => $args->{tempdir}, # Temp dir of project. Default: /tmp/moniker
        datadir     => $args->{datadir}, # Data dir of project. Defaut: /var/lib/moniker
        logdir      => $args->{logdir}, # Log dir of project. Default: /var/log/moniker
        sharedir    => $args->{sharedir}, # Share dir. Default: /usr/share/moniker
        docdir      => $args->{docdir}, # Share dir. Default: /usr/share/doc/moniker
        cachedir    => $args->{cachedir}, # Cache dir. Default: /var/cache/moniker
        spooldir    => $args->{spooldir}, # Spool dir. Default: /var/spool/moniker
        rundir      => $args->{rundir}, # Run dir. Default: /var/run/moniker
        lockdir     => $args->{lockdir}, # Lock dir. Default: /var/lock/moniker
        webdir      => $args->{webdir}, # Web dir. Default: /var/www/moniker

        # Files
        logfile     => $args->{logfile}, # Log file of project. Default: /var/log/moniker/moniker.log
        configfile  => $args->{configfile}, # Config file of project. Default: /etc/moniker/moniker.conf
        pidfile     => $args->{pidfile}, # PID file of project. Default: /var/run/moniker.pid

    }, $class;

    # Modes
    foreach my $mode ( @{(ALOWED_MODES)}) {
        $self->{$mode."mode"} = 1 if is_true_flag($args->{$mode});
    }

    # Root dir
    my $root = $self->{root};
    $root = $self->{root} = $pwd if defined($root) && $root eq '.'; # Set root to cwd if specified as '.'
    unless (defined($root) && length($root)) {
        $root = $self->{root} = File::Spec->catdir(SYSCONFDIR, $moniker);
    }

    # Temp dir
    my $temp = $self->{tempdir};
    unless (defined($temp) && length($temp)) {
        $temp = $self->{tempdir} = File::Spec->catdir(File::Spec->tmpdir(), $moniker);
    }

    # Data dir
    my $datadir = $self->{datadir};
    unless (defined($datadir) && length($datadir)) {
        $datadir = $self->{datadir} = File::Spec->catdir(SHAREDSTATEDIR, $moniker);
    }

    # Log dir
    my $logdir = $self->{logdir};
    unless (defined($logdir) && length($logdir)) {
        $logdir = $self->{logdir} = File::Spec->catdir(LOGDIR, $moniker);
    }

    # Share dir
    my $sharedir = $self->{sharedir};
    unless (defined($sharedir) && length($sharedir)) {
        $self->{sharedir} = File::Spec->catdir(DATADIR, $moniker);
    }

lib/Acme/Crux.pm  view on Meta::CPAN

}
sub spooldir {
    my $self = shift;
    if (scalar(@_) >= 1) {
        $self->{spooldir} = shift;
        return $self;
    }
    return $self->{spooldir};
}
sub rundir {
    my $self = shift;
    if (scalar(@_) >= 1) {
        $self->{rundir} = shift;
        return $self;
    }
    return $self->{rundir};
}
sub lockdir {
    my $self = shift;
    if (scalar(@_) >= 1) {
        $self->{lockdir} = shift;
        return $self;
    }
    return $self->{lockdir};
}
sub webdir {
    my $self = shift;
    if (scalar(@_) >= 1) {
        $self->{webdir} = shift;
        return $self;
    }
    return $self->{webdir};
}
sub configfile {
    my $self = shift;
    if (scalar(@_) >= 1) {
        $self->{configfile} = shift;
        return $self;
    }
    return $self->{configfile};
}
sub logfile {
    my $self = shift;
    if (scalar(@_) >= 1) {
        $self->{logfile} = shift;
        return $self;
    }
    return $self->{logfile};
}
sub pidfile {
    my $self = shift;
    if (scalar(@_) >= 1) {
        $self->{pidfile} = shift;
        return $self;
    }
    return $self->{pidfile};
}

# Modes (methods)
sub testmode    { !! shift->{testmode} }
sub debugmode   { !! shift->{debugmode} }
sub verbosemode { !! shift->{verbosemode} }
sub silentmode  { ! shift->{verbosemode} }

# Methods
sub error {
    my $self = shift;
    if (scalar(@_) >= 1) {
        $self->{error} = shift;
        return $self;
    }
    return $self->{error};
}
sub begin {
    my $self = shift;
    $self->{hitime} = [gettimeofday];
    return $self->{hitime}
}
sub elapsed {
    my $self = shift;
    my $timing_begin = shift;
    return undef unless my $started = $timing_begin || $self->{hitime};
    return tv_interval($started, [gettimeofday]);
}
sub exedir { shift->{exedir} }
sub orig { shift->{orig} }
sub option {
    my $self = shift;
    my $key  = shift;
    my $opts = $self->{options};
    return undef unless $opts;
    return $opts unless defined $key;
    return $opts->{$key};
}
sub opt { goto &option }
sub getopt { goto &option }

# Register method. See Mojo::Util::monkey_patch
sub register_method {
    my $self = shift;
    my $code = pop || sub { 1 }; # last param
    my $method = pop;
    my $namespace = pop || ref($self) || $self || __PACKAGE__;
    croak qq{Can't register method: method name is missing} unless $method;
    croak qq{Can't register method "$method": subroutine code is not defined}
        unless is_code_ref($code);
    my $ent = sprintf("%s::%s", $namespace, $method);

    # Create new method
    no strict 'refs';
    no warnings 'redefine';
    *{$ent} = set_subname($ent, $code);

    ### Old version from CTK::Plugin::register_method
    ### Check
    ##return if do { no strict 'refs'; defined &{$ff} };
    ### Create method!
    ##do {
    ##    no strict 'refs';
    ##    *{$ff} = \&$callback;
    ##};



( run in 0.514 second using v1.01-cache-2.11-cpan-39bf76dae61 )