package Mojolicious::Che; use Mojo::Base::Che; # один патч для хазов use Mojo::Base 'Mojolicious'; use Mojo::Log::Che; use Mojo::Loader qw(load_class); #~ use Mojo::Util qw(url_unescape); #~ use Scalar::Util 'weaken'; sub new { my ($class, %args) = @_; my $config = delete($args{config}) || 'Config.pm'; my $app = $class->SUPER::new(%args); $app->plugin(Config =>{file => $config}); #~ return $app # остановка или двойной перезапуск kill -USR2 #~ if $ENV{HYPNOTOAD_PID} || $ENV{HYPNOTOAD_STOP}; my $conf = $app->config; $conf->{mojo} ||= {}; my $defaults = $conf->{'mojo_defaults'} || $conf->{'mojo'}{'defaults'} || $conf->{'mojo.defaults'}; $app->defaults($defaults) if $defaults; my $secret = $conf->{'mojo_secret'} || $conf->{'mojo_secrets'} || $conf->{'mojo'}{'secret'} || $conf->{'mojo'}{'secrets'} || $conf->{'mojo.secret'} || $conf->{'mojo.secrets'} || $conf->{'шифры'} || [rand]; $app->secrets($secret); my $mode = $conf->{'mojo_mode'} || $conf->{'mojo'}{'mode'} || $conf->{'mojo.mode'}; $app->mode($mode) # Файл лога уже не переключишь if $mode; #~ $app->log->level( $conf->{'mojo_log_level'} || $conf->{'mojo'}{'log_level'} || 'debug'); my $log = $conf->{'mojo_log'} || $conf->{'mojo.log'} || $conf->{'mojo'}{'log'}; $app->log(Mojo::Log::Che->new(%$log)) if $log; #~ warn "Mode: ", $app->mode, "; log level: ", $app->log->level; my $home = $app->home; my $statics = $conf->{'mojo_static_paths'} || $conf->{'mojo.static.paths'} || $conf->{'mojo'}{'static'}{'paths'} || []; #~ push @{$app->static->paths}, @{$paths} if $paths; push @{$app->static->paths}, $home->rel_file($_) for @$statics; my $templates_paths = $conf->{'mojo_renderer_paths'} || $conf->{'mojo.renderer.paths'} || $conf->{'mojo'}{'renderer'}{'paths'} || []; push @{$app->renderer->paths}, $home->rel_dir($_) for @$templates_paths; my $renderer_classes = $conf->{'mojo_renderer_classes'} || $conf->{'mojo.renderer.classes'} || $conf->{'mojo'}{'renderer'}{'classes'} || []; push @{$app->renderer->classes}, $_ for grep ! load_class($_), @$renderer_classes; $app->сессия(); $app->хазы(); #~ $app->базы(); #~ $app->запросы(); $app->плугины(); $app->хуки(); $app->спейсы(); $app->маршруты(); $app->задачи(); $app->типы(); #~ $app->minion_worker(); return $app; } sub хазы { # Хазы из конфига my $app = shift; my $conf = $app->config; my $h = $conf->{'mojo_has'} || $conf->{'mojo.has'} || $conf->{'mojo'}{'has'} || $conf->{'хазы'}; map { $app->log->debug("Make the app->has('$_')"); has $_ => $h->{$_}; } keys %$h; } #~ sub плугины {# Плугины из конфига has плугины => sub { my $app = shift; my $conf = $app->config; my $плугины = {}; my $plugins = $conf->{'mojo_plugins'} || $conf->{'mojo.plugins'} || $conf->{'mojo'}{'plugins'} || $conf->{'плугины'} || return; map { push @{ $плугины->{$_->[0]} ||= [] }, [ref $_->[1] eq 'CODE' ? $app->plugin($_->[0] => $app->${ \$_->[1] }) : $app->plugin(@$_)]; $app->log->debug("Enable plugin [$_->[0]]"); } @$plugins; return $плугины; }; has dbh => sub { #~ sub базы {# обрабатывает dbh конфига my $app = shift; my $conf = $app->config; my $c_dbh = $conf->{dbh} || $conf->{'базы'}; return unless $c_dbh && ref($c_dbh) eq 'HASH' && keys %$c_dbh; #~ has dbh => sub {{};} #~ unless $app->can('dbh'); my $dbh = {}; my $req_dbi; while (my ($db, $opt) = each %$c_dbh) { if ($opt->{dbh}) {# && ref $opt eq 'DBI::db' $dbh->{$db} ||= $opt->{dbh}; } else { ++$req_dbi and require DBI unless $req_dbi; $dbh->{$db} ||= DBI->connect(@{$opt->{connect}}); $app->log->debug("Соединился с базой $opt->{connect}[0] app->dbh->{'$db'}"); } map { $dbh->{$db}->do($_); } @{$opt->{do}} if $opt->{do}; } return $dbh; }; has sth => sub { #~ sub запросы {# обрабатывает sth конфига my $app = shift; my $dbh = eval { $app->dbh } or return; #~ my %arg = @_; my $conf = $app->config; my $c_dbh = $conf->{dbh} || $conf->{'базы'}; my $c_sth = $conf->{sth} || $conf->{'запросы'} || {}; #~ my $c_pos = $conf->{pos} || $conf->{'посы'} || {}; return unless ($c_sth && ref($c_sth) eq 'HASH' && keys %$c_sth); #~ || ($c_pos && ref($c_pos) eq 'HASH' && keys %$c_pos); my $sth = {}; while (my ($db, $opt) = each %$c_dbh) { while (my ($st, $sql) = each %{$opt->{sth}}) { $sth->{$db}{$st} = $dbh->{$db}->prepare($sql);# $app->sth->{main}{...} $app->log->debug("Подготовился запрос [app->sth->{$db}{$st}]"); } } while (my ($db, $h) = each %$c_sth) { while (my ($st, $sql) = each %$h) { $sth->{$db}{$st} = $dbh->{$db}->prepare($sql);# $app->sth->{main}{...} $app->log->debug("Подготовился запрос [app->sth->{$db}{$st}]"); } } $sth; }; sub хуки {# Хуки из конфига my $app = shift; my $conf = $app->config; my $hooks = $conf->{'mojo_hooks'} || $conf->{'mojo.hooks'} || $conf->{'mojo'}{'hooks'} || $conf->{'хуки'} || return; while (my ($name, $sub) = each %$hooks) { if (ref $sub eq 'ARRAY') { $app->hook($name => $_) for @$sub; } else { $app->hook($name => $sub); } $app->log->debug(sprintf("Applied hook%s [%s] from config", ref $sub eq 'ARRAY' ? "s (@{[ scalar @$sub]})" : '', $name)); } } sub сессия { my $app = shift; my $conf = $app->config; my $session = $conf->{'mojo_session'} || $conf->{'mojo_sessions'} || $conf->{'mojo.session'} || $conf->{'mojo.sessions'} || $conf->{'mojo'}{'session'} || $conf->{'mojo'}{'sessions'} || $conf->{'сессии'} || $conf->{'сессия'} || return; #~ $app->sessions->cookie_name($session->{'cookie_name'}) #~ if $session->{'cookie_name'}; #~ $app->sessions->default_expiration($session->{'default_expiration'}) # set expiry #~ if defined $session->{'default_expiration'}; while (my ($meth, $val) = each %$session) { next unless $app->sessions->can($meth); $app->sessions->$meth($val); } } sub маршруты { my $app = shift; my $conf = $app->config; my $routes = $conf->{'mojo_routes'} || $conf->{'mojo.routes'} || $conf->{'mojo'}{'routes'} || $conf->{'routes'} || $conf->{'маршруты'} or return; my $app_routes = $app->routes; my $apply_route = sub { my $r = shift || $app_routes; my ($meth, $arg) = @_; my $nr; if (my $m = $r->can($meth)) { $nr = $r->$m($arg) unless ref($arg); $nr = $r->$m(cb => $arg) if ref($arg) eq 'CODE'; $nr = $r->$m(@$arg) if ref($arg) eq 'ARRAY'; $nr = $r->$m(%$arg) if ref($arg) eq 'HASH'; } else { $app->log->warn("Can't method [$meth] for route",); } return $nr; }; for my $r (@$routes) { my $nr = $apply_route->($app_routes, @$r[0,1]) or next; $app->log->debug("Apply route [$r->[0] $r->[1]]"); for( my $i = 2; $i < @$r; $i += 2 ) { $nr = $apply_route->($nr, @$r[$i, $i+1]) or next; } } } sub спейсы { my $app = shift; my $conf = $app->config; my $ns = $conf->{'mojo_namespaces'} || $conf->{'mojo.namespaces'} || $conf->{'mojo'}{'namespaces'} || $conf->{'namespaces'} || $conf->{'ns'} || $conf->{'спейсы'} || return; push @{$app->routes->namespaces}, @$ns; } sub задачи { my $app = shift; my $conf = $app->config; my $tasks = $conf->{'jobs'} || $conf->{'tasks'} || $conf->{'задачи'} or return; die "You have jobs and first enable plugin Minion" unless $app->renderer->get_helper('minion'); while (my ($name, $sub) = each %$tasks) { $app->log->debug(sprintf("Applied task [%s] in [%s] from config", $name, $app->minion->add_task($name => $sub))); } #~ $app->minion->reset; } sub типы {#MIME my $app = shift; my $conf = $app->config; my $types = $conf->{'mojo_types'} || $conf->{'mojo.types'} || $conf->{'mojo'}{'types'} || $conf->{'types'} || $conf->{'типы'} or return; while (my ($name, $val) = each %$types) { $app->types->type($name => $val); $app->log->debug(sprintf("Applied type [%s] from config", $name)); } } # overide only on my $path = $req->url->path->to_abs_string; sub Mojolicious::dispatch { my ($self, $c) = @_; my $plugins = $self->plugins->emit_hook(before_dispatch => $c); # Try to find a static file my $tx = $c->tx; $self->static->dispatch($c) and $plugins->emit_hook(after_static => $c) unless $tx->res->code; # Start timer (ignore static files) my $stash = $c->stash; $self->log->debug(sub { my $req = $c->req; my $url = $req->url->to_abs; $c->helpers->timing->begin('mojo.timer'); return sprintf qq{[%s] %s "%s://%s%s%s"}, $req->request_id, $req->method, $url->scheme, $url->host, $url->port ? ":".$url->port : '', $url->path->to_route; }) unless $stash->{'mojo.static'}; # Routes $plugins->emit_hook(before_routes => $c); $c->helpers->reply->not_found unless $tx->res->code || $self->routes->dispatch($c) || $tx->res->code; } our $VERSION = '0.0824';# as to Mojolicious/100+0.000 =pod =encoding utf8 =head1 Mojolicious::Che Доброго всем ¡ ¡ ¡ ALL GLORY TO GLORIA ! ! ! =head1 VERSION 0.0824 =head1 NAME Mojolicious::Che - Мой базовый модуль для приложений Mojolicious. Нужен только развернутый конфиг. =head1 SYNOPSIS # app.pl use lib 'lib'; use Mojo::Base 'Mojolicious::Che'; __PACKAGE__->new(config => 'lib/Config.pm')->start(); =head1 Config file Порядок строк в этом конфиге соответствует исполнению в модуле! { 'Проект'=>'Тест-проект', # mojo => { # defaults => # secrets => # mode=> # log => {level=>...} # static => {paths => [...]}, # renderer => {paths => [...], classes => [...], }, # session[s] => # has => # plugins => # hooks => # namespaces => # routes => # jobs => # types => # }, # or with prefix mojo_ # Default values for "stash" in Mojolicious::Controller, assigned for every new request. mojo_defaults => {layout=>'default',}, # 'шифры' => [ mojo_secrets => ['true 123 my app',], mojo_mode=> 'development', mojo_log=>{level => 'error'}, mojo_static_paths => ["static"], mojo_renderer_classes => ["Mojolicious::Foo::Fun"], # 'сессия'(или сессии) => mojo_session[s] => {cookie_name => 'EXX', default_expiration => 86400}, # 'хазы' => 'Лет 500-700 назад был такой дикий степной торговый жадный народ ХАЗАРЫ. Столицей их "государства" был город Тьмутаракань, где-то на берегу моря Каспия. Потомки этих людей рассыпаны по странам России, Средней Азии, Европы. Есть мнение, что хазары присвоили себе название ЕВРЕИ, но это не те библейские кроткие евреи, а жадные потомки кроманьонцев' mojo_has => { foo => sub {my $app = shift; return 'is a bar';}, }, # 'базы' => # will be as has! dbh=>{ 'main' => { # DBI->connect(dsn, user, passwd, $attrs) connect => ["DBI:Pg:dbname=test;", "postgres", undef, { ShowErrorStatement => 1, AutoCommit => 1, RaiseError => 1, PrintError => 1, pg_enable_utf8 => 1, #mysql_enable_utf8 => 1, #mysql_auto_reconnect=>1, }], # or use Foo::Dbh; external defined dbh # dbh => Dbh->dbh, # will do on connect do => ['set datestyle to "ISO, DMY";',], # prepared sth will be as has $app->sth->{}{} sth => { foo => < # prepared sth will be as has $app->sth->{}{} sth => { main => { now => "select now();" }, }, # 'плугины'=> [ mojo_plugins=>[ ['Foo::Bar'], [Foo::Bar::Plugin => opt1 => ..., opt2 => ...], ['Foo::Plugin' => sub {<...returns config data list...>}], ], # 'хуки' => mojo_hooks=>{ #~ before_dispatch => sub {1;}, }, # 'спейсы' => [...] namespaces => ['Space::Shattle'], # 'маршруты' => [...] routes => [ [get=>'/', to=> {cb=>sub{shift->render(format=>'txt', text=>'Hello friend!');},}], ] #~ 'задачи'=> {#first enable plugin Minion jobs => { slow_log => sub { my ($job, $msg) = @_; sleep 5; $job->app->log->error(qq{slow_log "$msg"}); }, }, # или 'типы'=>{...} types => { docx => ['application/vnd.openxmlformats-officedocument.wordprocessingml.document'], ... }, }; =head1 ATTRIBUTES B inherits all attributes from L and implements the following new ones. =head2 dbh Set DBI handlers from config B (или B<базы>) my $dbh = $app->dbh->{main}; =head2 sth Set prepared stattements from config B (или B<запросы>). my $sth = $app->sth->{main}{foo}; =head2 плугины Apply the plugins. See L, L. =head1 METHODS B inherits all methods from L and implements the following new ones. =head2 сессия() Session object config apply. See L, L. =head2 хазы() Apply the has's. UTF names allow. =head2 хуки() Apply the hooks. See L. =head2 спейсы() Apply the namespaces. Push @{$app->routes->namespaces} your namespaces. See L. namespaces => ['Space::Shattle'], =head2 маршруты() Apply the routes. See L, L. #~ 'маршруты' => [ 'routes'=>[ [get=>'/', to=> {cb=>sub{shift->render(format=>'txt', text=>'Welcome!');},}], ], =head2 задачи() Apply the jobs. See L. #~ 'задачи'=> {#first enable plugin Minion 'jobs'=> { # or tasks slow_log => sub { my ($job, $msg) = @_; sleep 5; $job->app->log->error(qq{slow_log "$msg"}); }, }, =head типы() Apply the new types. See L, L. =head1 SEE ALSO L L =head1 AUTHOR Михаил Че (Mikhail Che), C<< >> =head1 BUGS / CONTRIBUTING Please report any bugs or feature requests at L. Pull requests also welcome. =head1 COPYRIGHT Copyright 2016+ Mikhail Che. This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut 1;