CGI-Snapp
view release on metacpan or search on metacpan
lib/CGI/Snapp.pm view on Meta::CPAN
package CGI::Snapp;
use strict;
use warnings;
use Carp;
use Class::ISA;
use Log::Handler;
use Moo;
use Try::Tiny;
has _current_run_mode =>
(
is => 'rw',
default => sub{return ''},
required => 0,
);
has _error_mode =>
(
is => 'rw',
default => sub{return ''},
required => 0,
);
has _headers =>
(
is => 'rw',
default => sub{return {} },
required => 0,
);
has _header_type =>
(
is => 'rw',
default => sub{return 'header'},
required => 0,
);
has logger =>
(
is => 'rw',
default => sub{return ''},
required => 0,
);
has _object_callbacks =>
(
is => 'rw',
default => sub{return {} },
required => 0,
);
has PARAMS =>
(
is => 'rw',
default => sub{return {} },
required => 0,
);
has _params =>
(
is => 'rw',
default => sub{return {} },
required => 0,
);
has _prerun_mode_lock =>
(
is => 'rw',
default => sub{return 1},
required => 0,
);
has _psgi =>
(
is => 'rw',
default => sub{return 0},
required => 0,
);
has QUERY =>
(
is => 'rw',
default => sub{return ''},
required => 0,
);
has _query =>
(
is => 'rw',
default => sub{return ''},
required => 0,
);
has _run_mode_source =>
(
is => 'rw',
default => sub{return 'rm'}, # I.e. the CGI form field of that name.
required => 0,,
);
has _run_modes =>
(
is => 'rw',
default => sub{return {} },
required => 0,
);
has send_output =>
(
is => 'rw',
default => sub{return 1},
required => 0,
);
has _start_mode =>
(
is => 'rw',
default => sub{return 'start'},
required => 0,
);
my(%class_callbacks) =
(
error => {},
forward_prerun => {},
init => {'CGI::Snapp' => ['cgiapp_init']},
prerun => {'CGI::Snapp' => ['cgiapp_prerun']},
postrun => {'CGI::Snapp' => ['cgiapp_postrun']},
teardown => {'CGI::Snapp' => ['teardown']},
);
my($myself);
our $VERSION = '2.01';
# --------------------------------------------------
sub add_callback
{
my($self, $hook, $option) = @_;
croak "Error: Can't use undef as a hook name\n" if (! defined $hook);
$hook = lc $hook;
$self -> log(debug => "add_callback($hook, ...)");
croak "Error: Unknown hook name '$hook'\n" if (! $class_callbacks{$hook});
if (ref $self)
{
# it's an object-level callback.
my($object_callback) = $self -> _object_callbacks;
$$object_callback{$hook} = [] if (! $$object_callback{$hook});
push @{$$object_callback{$hook} }, $option;
$self -> _object_callbacks($object_callback);
}
else
{
# It's a class-level callback.
push @{$class_callbacks{$hook}{$self} }, $option;
}
} # End of add_callback.
# --------------------------------------------------
sub add_header
{
my($self, @headers) = @_;
$self -> log(debug => 'add_header(...)');
my($old) = $self -> _headers;
if (@headers)
{
my(%new) = ref $headers[0] eq 'HASH' ? %{$headers[0]}
: ref $headers[0] eq 'ARRAY' ? @{$headers[0]}
: scalar(@headers) % 2 == 0 ? @headers
: croak "Error: Odd number of parameters passed to add_header()\n";
my($value);
for my $key (keys %new)
{
$value = $$old{$key};
if (ref $new{$key} eq 'ARRAY')
{
if (ref $value eq 'ARRAY')
{
$new{$key} = [@$value, @{$new{$key} }];
}
else
{
$new{$key} = [$value, @{$new{$key} }] if (defined $value);
}
}
else
{
if (ref $value eq 'ARRAY')
{
$new{$key} = [@$value, $new{$key}];
}
else
{
$new{$key} = [$value, $new{$key}] if (defined $value);
}
}
}
$old = {%$old, %new};
$self -> _headers($old);
}
return %$old;
} # End of add_header.
# --------------------------------------------------
sub BUILD
{
my($self, $args) = @_;
$myself = $self;
$self -> _params($$args{PARAMS}) if ($$args{PARAMS} && (ref $$args{PARAMS} eq 'HASH') );
$self -> _query($$args{QUERY}) if ($$args{QUERY});
$self -> send_output(0) if ($ENV{CGI_SNAPP_RETURN_ONLY});
$self -> _run_modes({$self -> _start_mode => 'dump_html'});
$self -> call_hook('init', %$args);
$self -> setup;
} # End of BUILD.
# --------------------------------------------------
sub call_hook
{
my($self, $hook, @args) = @_;
croak "Error: Can't use undef as a hook name\n" if (! defined $hook);
$hook = lc $hook;
$self -> log(debug => "call_hook($hook, ...)");
my($count) = {class => 0, object => 0};
my(%seen);
# Call object-level hooks.
for my $callback (@{${$self -> _object_callbacks}{$hook} })
{
next if ($seen{$callback});
try
{
$self -> $callback(@args);
}
catch
{
croak "Error executing object-level callback for hook '$hook': $@\n" if ($@);
};
$$count{object}++;
$seen{$callback} = 1;
}
# Call class-level hooks.
for my $class (Class::ISA::self_and_super_path(ref $self || $self) )
{
next if (! exists $class_callbacks{$hook}{$class});
for my $callback (@{$class_callbacks{$hook}{$class} })
{
next if ($seen{$callback});
try
{
$self -> $callback(@args);
}
catch
{
croak "Error executing class-level callback for class '$class', hook '$hook': $@\n" if ($@);
};
$$count{class}++;
$seen{$callback} = 1;
}
}
return $count;
} # End of call_hook.
# --------------------------------------------------
sub cgiapp_get_query
{
my($self) = @_;
$self -> log(debug => 'cgiapp_get_query()');
if (! $self -> _query)
{
require CGI;
$self -> _query(CGI -> new);
}
return $self -> _query;
} # End of cgiapp_get_query.
# --------------------------------------------------
sub cgiapp_init
{
my($self) = @_;
$self -> log(debug => 'cgiapp_init()');
} # End of cgiapp_init.
# --------------------------------------------------
sub cgiapp_prerun
{
my($self) = @_;
$self -> log(debug => 'cgiapp_prerun()');
} # End of cgiapp_prerun.
lib/CGI/Snapp.pm view on Meta::CPAN
sub _generate_output
{
my($self, @args) = @_;
$self -> log(debug => '_generate_output()');
my($is_autoload) = 0;
my($run_mode) = $self -> _current_run_mode;
my(%run_modes) = $self -> run_modes;
my($method_name);
if (exists $run_modes{$run_mode})
{
$method_name = $run_modes{$run_mode};
}
else
{
croak "Error: No such run mode: '$run_mode'\n" if (! exists $run_modes{'AUTOLOAD'});
$method_name = $run_modes{'AUTOLOAD'};
$is_autoload = 1;
}
my($output);
try
{
$output = $is_autoload ? $self -> $method_name($run_mode, @args) : $self -> $method_name(@args);
}
catch
{
my($error) = $_;
$self -> call_hook('error', $error);
if ($method_name = $self -> error_mode)
{
try
{
$output = $self -> $method_name($error);
}
catch
{
croak "Error executing the error mode method '$method_name': $_\n";
};
}
else
{
croak "Error executing run mode '$run_mode': $error\n";
}
};
return defined($output) ? $output : '';
} # End of _generate_output.
# --------------------------------------------------
sub get_callbacks
{
my($self, $type, $hook) = @_;
$type ||= '';
$hook ||= '';
$self -> log(debug => "get_callbacks($type, $hook)");
croak "Error: \$type parameter to get_callbacks() must be 'class' or 'object'\n" if ($type !~ /^(?:class|object)$/);
croak "Error: \$hook parameter to get_callbacks() must be a string\n" if (length($hook) == 0);
return $type eq 'class' ? $class_callbacks{$hook} : ${$self -> _object_callbacks}{$hook};
} # End of get_callbacks.
# --------------------------------------------------
sub get_current_runmode
{
my($self) = @_;
$self -> log(debug => 'get_current_runmode()');
return $self -> _current_run_mode;
} # End of get_current_runmode.
# --------------------------------------------------
sub header_add
{
my($self, @headers) = @_;
$self -> log(debug => 'header_add(...)');
my(%new) = ref $headers[0] eq 'HASH' ? %{$headers[0]}
: ref $headers[0] eq 'ARRAY' ? @{$headers[0]}
: scalar(@headers) % 2 == 0 ? @headers
: croak "Error: Odd number of parameters passed to header_add()\n";
my($old) = $self -> _headers;
if (scalar keys %new)
{
my($value);
for my $key (grep{ref $new{$_} eq 'ARRAY'} keys %new)
{
$value = $$old{$key};
next if (! defined $value);
$value = [$value] if (ref $value ne 'ARRAY');
$new{$key} = [@$value, @{$new{$key} }];
}
$old = {%$old, %new};
$self -> _headers($old);
}
return %$old;
} # End of header_add.
# --------------------------------------------------
sub header_props
{
my($self, @headers) = @_;
$self -> log(debug => 'header_props(...)');
if (@headers)
lib/CGI/Snapp.pm view on Meta::CPAN
{
my($self, @new_options) = @_;
$self -> log(debug => 'mode_param(...)');
my($mode_source);
if (@new_options)
{
my($ref) = ref $new_options[0];
if ( ($#new_options == 0) && ($ref !~ /(?:ARRAY|HASH)/) )
{
$mode_source = $new_options[0];
}
else
{
my(%new_options) = $ref eq 'HASH' ? %{$new_options[0]}
: $ref eq 'ARRAY' ? @{$new_options[0]}
: scalar(@new_options) % 2 == 0 ? @new_options
: croak "Error: Odd number of parameters passed to mode_param()\n";
# We need defined in case someone uses a run mode of 0.
$mode_source = defined($new_options{param}) ? $new_options{param} : '';
my($index) = $new_options{path_info};
my($path_info) = $self -> query -> path_info;
if ($index && $path_info)
{
$index -= 1 if ($index > 0);
$path_info =~ s!^/!!;
$path_info = (split m|/|, $path_info)[$index] || '';
$mode_source = length $index ? {run_mode => $path_info} : $mode_source;
}
}
$self -> _run_mode_source($mode_source);
}
else
{
$mode_source = $self -> _run_mode_source;
}
return $mode_source;
} # End of mode_param.
# --------------------------------------------------
sub new_hook
{
my($self, $hook) = @_;
croak "Error: Can't use undef as a hook name\n" if (! defined $hook);
$hook = lc $hook;
$self -> log(debug => "new_hook($hook)");
$class_callbacks{$hook} ||= {};
return 1;
} # End of new_hook.
# --------------------------------------------------
sub param
{
my($self, @params) = @_;
$self -> log(debug => 'param(...)');
my(%old) = %{$self -> _params};
my($returnz);
my($value);
if (@params)
{
my(%new);
if (ref $params[0] eq 'HASH')
{
%new = %{$params[0]};
}
elsif (ref $params[0] eq 'ARRAY')
{
%new = @{$params[0]};
}
elsif (scalar @params % 2 == 0)
{
%new = @params;
$value = $params[1] if ($#params == 1);
}
elsif ($#params == 0)
{
$value = $old{$params[0]};
}
else
{
croak "Error: Odd number of parameters passed to param()\n";
}
$returnz = 'scalar';
%old = (%old, %new);
$self -> _params({%old});
}
else
{
$returnz = 'array';
}
return $returnz eq 'scalar' ? $value : keys %{$self -> _params};
} # End of param.
# --------------------------------------------------
lib/CGI/Snapp.pm view on Meta::CPAN
Now, the HTTP headers are generated, and both those headers and the HTML are sent to the HTTP client. You can stop the transmission with L</send_output([$Boolean])>.
utf8::downgrade() is used to turn off any stray UTF-8 bits on the headers.
=item o 7: teardown()
Here's where you clean up, by disconnecting from the database, or whatever.
=back
=back
=head3 Note 1: Parameters passed to your run mode method
Normally, the only parameter passed is $self, which is an object of type L<CGI::Snapp> or a sub-class.
However, if the method was invoked via the AUTOLOAD mechanism (note 2), the next parameter is the run mode.
Lastly, if the method was invoked via L<CGI::Snapp::Plugin::Forward>'s forward(@args), then those parameters you pass to forward() will be passed to the run mode method (after $self).
=head3 Note 2: When the run mode is not a key in the dispatch table, this algorithm is invoked
=over 4
=item o The AUTOLOAD run mode
The code checks if you have defined a run mode named 'AUTOLOAD'. If so, it's value in the dispatch table is used as the method name.
=item o Fallback
If no run mode called 'AUTOLOAD' is found, the code calls L<Carp>'s croak($message).
=back
=head3 Note 3: When the run mode method fails to run, this algorithm is invoked
=over 4
=item o The error hook
The method, if any, attached to the 'error' hook is called. The error message generated from the run mode method's failure is passed as the parameter, for you to utilize when deciding what
action to take.
Hooks are discussed under L</A More Complex View> just below.
=item o The error_mode method
Next, L<error_mode([$method_name])> is called. If it returns a defined value, that value is used as the name of a method to call.
=item o Fallback
Finally, if L<error_mode([$method_name])> does not return a method name, or calling that method also fails, the code calls L<Carp>'s croak($message).
=back
Aren't you glad that was the I<simple> view?
=head2 A More Complex View
L<CGI::Snapp> and before it L<CGI::Application> are designed in such a way that some of those methods are actually I<callbacks> aka I<hooks>, and their names are looked up via hook names.
See the Wikipedia article L<Hooking|http://en.wikipedia.org/wiki/Hooking> for a long explanation of hooks.
It works like this: A hook name is a key in a hash, and the corresponding value is a package name, which in turn points to an arrayref of method names. So, for a given hook name and
package, we can execute a series of named methods, where those names are listed in that arrayref.
The hooked methods are not expected to return anything.
Here's the default set of hooks aka (default) dispatch table. It's just a hash with fancy values per key:
my(%class_callback) =
(
error => {},
forward_prerun => {},
init => {'CGI::Snapp' => ['cgiapp_init']},
prerun => {'CGI::Snapp' => ['cgiapp_prerun']},
postrun => {'CGI::Snapp' => ['cgiapp_postrun']},
teardown => {'CGI::Snapp' => ['teardown']},
);
An explanation:
=over 4
=item o Yes, there are class-level callbacks and object-level callbacks
See L</add_callback($hook, $option)> for details.
=item o The error hook
By default, there is no method attached to the 'error' hook. See L</error_mode([$method_name])> for details.
=item o The init hook
Instead of calling cgiapp_init() directly at the start of the run as alleged above, we call all those methods named as belonging to the 'init' hook, of which - here - there is just the
default one, CGI::Snapp::cgiapp_init().
=item o The prerun hook
Likewise.
=item o The postrun hook
Likewise.
=item o The teardown hook
Likewise, instead of calling teardown() directly at the finish of the run, we call all those methods named as belonging to the 'teardown' hook, starting with (the default) CGI::Snapp::teardown().
=back
Now, when I say 'all those methods', that's because you can add your own hooked methods, to enhance this process. What happens is that your hooks are pushed onto the stack of hooked methods
attached to a given hook name, and run in turn at the appropriate time.
Further, besides extending the stack of methods attached to a pre-existing hook name, you can create new hook names, and attach any number of methods to each.
The pre-defined hooks are called 'error', 'init', 'prerun', 'postrun' and 'teardown', so there is no need to call L</new_hook($hook)> for those.
This matter is discussed in depth under the entry for L</add_callback($hook, $option)>. Also, see L</new_hook($hook)> and L</call_hook($hook, @args)> for how hooks are named and invoked.
Sample code is in t/callback.pl, in the distro.
=head1 Distributions
This module is available as a Unix-style distro (*.tgz).
See L<http://savage.net.au/Perl-modules/html/installing-a-module.html>
for help on unpacking and installing distros.
=head1 Installation
Install L<CGI::Snapp> as you would for any C<Perl> module:
Run:
cpanm CGI::Snapp
or run:
sudo cpan CGI::Snapp
or unpack the distro, and then either:
perl Build.PL
./Build
lib/CGI/Snapp.pm view on Meta::CPAN
It returns a new object of type C<CGI::Snapp>.
Key-value pairs accepted in the parameter list (see corresponding methods for details
[e.g. L</send_output([$Boolean])>]):
=over 4
=item o logger => $aLoggerObject
Specify a logger compatible with L<Log::Handler>.
Default: '' (The empty string).
To clarify: The built-in calls to log() all use a log level of 'debug', so if your logger has 'maxlevel' set
to anything less than 'debug', nothing will get logged.
'maxlevel' and 'minlevel' are discussed in L<Log::Handler#LOG-LEVELS> and L<Log::Handler::Levels>.
Also, see L</How do I use my own logger object?> and L</Troubleshooting>.
=item o PARAMS => $hashref
Provides a set of ($key => $value) pairs as initial data available to your sub-class of L<CGI::Snapp> via the L</param([@params])> method.
Default: {}.
=item o send_output => $Boolean
Controls whether or not the HTML output is sent (printed) to the HTTP client.
This corresponds to L<CGI::Application>'s use of $ENV{CGI_APP_RETURN_ONLY}. But check the spelling in the next line.
Default: 1 (meaning yes, send). However, if $ENV{CGI_SNAPP_RETURN_ONLY} has a Perlish true value, the default is 0.
Using 0 means you have to get the output from the return value of the L</run()> method.
=item o QUERY => $q
Provides L</new()> with a pre-created L<CGI>-compatible object.
Default: ''.
However, a new L<CGI> object is created at run-time if needed. See L</query([$q])>.
=back
=head1 Methods
=head2 add_callback($hook, $option)
Adds another method to the stack of methods associated with $hook.
$hook is the name of a hook. $hook is forced to be lower-case.
Returns nothing.
That name is either pre-defined (see L</new_hook($hook)>) or one of your own, which you've previously set up with L</new_hook($hook)>.
Sample code:
# Class-level callbacks.
$class_name -> add_callback('init', \&method_1);
KillerApp -> add_callback('init', 'method_2');
# Object-level callbacks.
$app = CGI::Snapp -> new;
$app -> add_callback('init', \&method_3);
Notes:
=over 4
=item o Callback lifetimes
Class-level callbacks outlive the life of the $app object (of type L<CGI::Snapp> or your sub-class), by surviving for the duration of the Perl process, which, in a persistent
environment like L<Starman>, L<Plack>, etc, can be long enough to serve many HTTP client requests.
Object-level callbacks, however, go out of scope at the same time the $app object itself does.
=item o The class hierarchy
Callbacks can be registered by an object, or any of its parent classes, all the way up the hierarchy to L<CGI::Snapp>.
=item o Callback name resolution
Callback names are checked, and only the first with a given name is called. The type of callback, class or object, is ignored in this test, as it is in L<CGI::Application>.
This also means, that if there are 2 callbacks with the same name, in different classes, then still only the first is called.
Consider:
In Class A: $self -> add_callback('teardown', 'teardown_sub');
In Class B: $self -> add_callback('teardown', 'teardown_sub');
Here, because the names are the same, only one (1) teardown_sub() will be called. Which one called depends on the order in which those calls to add_callback() take place.
In Class A: $self -> add_callback('teardown', \&teardown_sub);
In Class B: $self -> add_callback('teardown', \&teardown_sub);
This time, both teardown_sub()s are called, because what's passed to add_callback() are 2 subrefs, which are memory addresses, and can't be the same for 2 different subs.
=item o Pre-defined hooks
Only the pre-defined hooks are called by L<CGI::Snapp>. So, if you use your own name in calling new_hook($name), you are also responsible for triggering the calls to that hook.
The pre-defined hooks are called 'error', 'init', 'prerun', 'postrun' and 'teardown', and there is no need to call L</new_hook($hook)> for those.
=item o Class-level callbacks
These belong to the class of the object calling L</add_callback($hook, $option)>.
=item o Multiple callbacks for a given hook
If multiple I<class>-level callbacks are added for the same hook by different classes, they will be executed in reverse-class-hierarchy order.
That it, the callback for the most derived class is executed first. This is the way normal class-hierarchy overrides work - nothing unexpected here.
If multiple I<class>-level callbacks are added for the same hook by the same class, they will be executed in the order added, since they are pushed onto a stack (as are object-level
callbacks).
If multiple I<object>-level callbacks are added for the same hook, they are run in the order they are registered, i.e. in the order of calls to L</add_callback($hook, $option)>.
=item o The 'init' hook
Since the 'init' hook is triggered during the call to L</new()>, even before L</setup()> is called, there is no opportunity for normal end-user code (your sub-class of L<CGI::Snapp>) to attach
a callback to this hook.
The way around this is to write a class which is I<not> a sub-class of L<CGI::Snapp>, and whose import() method is triggered when you 'use' this class in your sub-class of L<CGI::Snapp>.
There is a group of examples on how to do this. Start with t/hook.test.a.pl, which 'use's t/lib/CGI/Snapp/HookTestA.pm, which in turn 'use's t/lib/CGI/Snapp/Plugin/HookTest1.pm.
Alternately, examine the source code of L<CGI::Snapp::Plugin::Forward> for another way to do things, although it uses 'forward_prerun' rather than 'init'.
=back
To summarize, you are I<strongly> advised to examine t/hook.test.pl and all the modules it uses to gain a deeper understanding of this complex issue. In particular, the order of 'use'
statements in your sub-class of L<CGI::Snapp> will determine the order in which class-level callbacks are triggered.
=head2 add_header(@headers)
Adds headers to the list which will be sent to the HTTP client.
Returns all headers as a hash.
See also L</delete_header(@keys)>, L</header_add(@headers)>, L</header_props([@headers])>, L</header_type([$option])> and L</How does add_header() differ from header_add()?>.
=head2 call_hook($hook, @args)
Call the named hook. $hook is forced to be lower-case.
Returns a hashref of the number of callbacks actually called, where the keys are 'class' and 'object', and the values are integer counts.
@args takes various values, depending on the name of the callback:
=over 4
=item o init
Here, @args is the hash of options passed in to L</new()>.
Defaults to calling CGI::Snapp::cgiapp_init(@args).
=item o prerun
@args is the name of the run mode.
Defaults to calling CGI::Snapp::cgiapp_prerun($run_mode).
=item o postrun
@args is a scalarref, where the scalar is the output generated by the run mode method. This scalar does not yet have the HTTP headers attatched (if any).
Defaults to calling CGI::Snapp::cgiapp_postrun(\$html).
=item o teardown
@args is not used in this case.
Defauts to calling CGI::Snapp::teardown().
=back
If you call an unregistered hook, the call is just ignored.
See L</new_hook($hook)> and L</add_hook($hook, @args)> if you wish to register a new type of hook.
=head2 cgiapp_get_query()
Returns the query object.
This method only creates an object of type L<CGI> when a query object is needed.
Alternately, you can pass your own query object to the L</query([$q])> method.
You can override this method in your sub-class, if you wish to provide a L<CGI>-compatible object, such as a L<CGI::Simple> object, or similar. If not using L<CGI>, note:
=over 4
=item o The object must have a param() method
Normally, your object just needs to have a L</param([@params])> method, for it to be 'similar enough' to a L<CGI> object.
=item o The object may need a header() method
This is called if L</header_type([$option])> returns 'header'.
=item o The object may need a redirect() method
This is called if L</header_type([$option])> returns 'redirect'.
=item o If you use the 'path_info' option in the call to L</mode_param([@new_options])>
lib/CGI/Snapp.pm view on Meta::CPAN
=item o The query parameters
This is derived from the query object's Dump() method.
=item o The environment
This is derived from %ENV.
=back
See L</cgiapp_get_query()> for how to influence the type of query object used.
=head2 error_mode([$method_name])
Sets and gets the name of the error mode method.
Note: This is a method name, not a run mode as is returned from L</start_mode([$run_mode])>.
Here, the [] indicate an optional parameter.
Default: ''.
Returns the current error mode method name.
=head2 forward($run_mode[, @args])
Switches from the current run mode to the given $run_mode, passing the optional @args to the new mode's method.
For this to work, you must have previously called $self -> run_modes($run_mode => 'some_method'), so the code
knows which method it must call.
Just before the method associated with $run_mode is invoked, the current run mode is set to $run_mode, and any
methods attached to the hook 'forward_prerun' are called.
Calling this hook gives you the opportunity of making any preparations you wish before the new run mode is entered.
Finally, $run_mode's method is called, using @args as its arguments.
Returns the output of the $run_mode's method.
See t/forward.t and t/lib/CGI/Snapp/ForwardTest.pm for sample code.
If you wish to interrupt the current request, and redirect to an external url, then the
L</redirect($url[, $status])> method is probably what you want.
=head2 get_current_runmode()
Returns the name of the current run mode.
=head2 header_add(@headers)
Adds I<and sometimes deletes> headers from the list which will be sent to the HTTP client.
This strange behaviour is copied directly from L<CGI::Application>.
Returns the remaining headers as a hash.
Deprecated.
See also L</add_header(@headers)>, L</delete_header(@keys)>, L</header_props([@headers])>, L</header_type([$option])> and L</How does add_header() differ from header_add()?>.
=head2 get_callbacks($type, $hook)
Gets callback information associated with the given $type (class/object) and $hook.
$type is 'class' for class-level callbacks, and 'object' for object-level callbacks.
Values for $type:
=over 4
=item o 'class'
get_callbacks('class', $hook) returns a I<hashref>.
The keys of this hashref are the class names which have registered callbacks for $hook.
The values of this hashref are arrayrefs of method names or references.
=item o 'object'
get_callbacks('object', $hook) returns an I<arrayref>.
The values of this arrayref are arrayrefs of method names or references.
=back
See t/defaults.pl for sample code.
=head2 header_props([@headers])
Sets the headers to be sent to the HTTP client. These headers are expected to be a hash of L<CGI>-compatible HTTP header properties.
These properties will be ignored (sic) or passed directly to the header() or redirect() method of the L</query([$q])> object, depending on the value returned by L</header([$option])>.
Returns all headers as a hash.
See also L</add_header(@headers)>, L</delete_header(@keys)>, L</header_add([@headers])>, L</header_type([$option])> and L</How does add_header() differ from header_add()?>.
=head2 header_type([$option])
Sets and gets the type of HTTP headers to output.
Here, the [] indicate an optional parameter.
Returns the current header type.
Possible values for $option:
=over 4
=item o 'header'
The default. Uses the hash returned by L</header_props([@headers])> to generate a set of HTTP headers to send to the HTTP client.
=item o 'none'
Don't output any headers. See also the L</send_output([$Boolean)]> method.
In this case the HTTP status is set to 200.
=item o 'redirect'
Generates a redirection header to send to the HTTP client.
=back
=head2 log($level, $string)
If a logger object exists, then this calls the log() method of that object, passing it $level and $string.
Returns nothing.
So, the body of this method consists of this 1 line:
$self -> logger -> log($level => $string) if ($self && $self -> logger);
Up until V 1.03, this used to call $self -> logger -> $level($s), but the change was made to allow
simpler loggers, meaning they did not have to implement all the methods covered by $level().
See CHANGES for details. For more on log levels, see L<Log::Handler::Levels>.
=head2 logger([$logger_object])
lib/CGI/Snapp.pm view on Meta::CPAN
In particular, you are I<strongly> encouraged to read L<the Data::Session FAQ|https://metacpan.org/module/Data::Session#FAQ> and
L<the Data::Session Troubleshooting guidelines|https://metacpan.org/module/Data::Session#Troubleshooting> before writing your own teardown() method.
=head1 FAQ
=head2 Do I need to output a header when using Ajax?
Yes. At least, when I use jQuery I must do this in a run mode:
$self -> add_header(Status => 200, 'Content-Type' => 'text/html; charset=utf-8');
return $self -> param('view') -> search -> display($name, $row);
Here, display() returns a HTML table wrapped in 2 divs in the jQuery style, which becomes the return value
of the run mode.
The quoted code is in L<App::Office::Contacts::Controller::Exporter::Search>'s display (the run mode), and the
display() method being called above is in L<App::Office::Contacts::View::Search>, but it will be the same no
matter which Perl app you're running.
=head2 Does CGI::Snapp V 1.01 support PSGI?
Yes. See L</psgi_app()> and L<CGI::Snapp::Dispatch>.
=head2 Is there any sample code?
Yes. See t/*.pl and all the modules in t/lib/*.
See also L<CGI::Snapp::Dispatch> and its t/psi.args.t.
=head2 Why did you fork CGI::Application?
In order to study the code. I want to understand how L<CGI::Application>, L<CGI::Application::Dispatch> and L<CGI::Application::Dispatch::PSGI> work in sufficient detail that I
can put my forks of those modules into production - I<in my own code>.
Also - obviously - it allows me to implement what I think are code cleanups. And lastly, it allows me to indulge myself in a user-friendly release strategy.
Clearly, those are the same reasons which motivated me to fork L<CGI::Session> into L<Data::Session>.
As a byproduct of forking, rewriting the documentation has also allowed me to cut about 20,000 bytes from the source file Snapp.pm compared to Application.pm.
=head2 What version is the fork of CGI::Application based on?
CGI::Snapp V 1.00 is based on CGI::Application V 4.31. CGI::Snapp V 1.01 is based on CGI::Application V 4.50.
=head2 How does CGI::Snapp differ from CGI::Application?
My usage of the latter's features was always minimalistic, so - at least initially - I will only support a basic set of L<CGI::Application>'s features.
These are the major differences:
=head3 Clean up 'run_mode' 'v' runmode
Except for method calls where 'runmode' is unfortunately used, e.g L</get_current_runmode()>, 'run_mode' and 'run mode' have been adopted as the norm.
=head3 Always call croak and not a combination of croak and die
Also, every message passed to croak matches /^Error/ and ends with "\n".
=head3 No global variables (except for the inescapable dispatch table of class-level callbacks)
This means things like $$self{__CURRENT_RUNMODE} and $$self{__PRERUN_MODE_LOCKED} etc are only be available via method calls.
Here is a list of the global variables in L<CGI::Application>, and the corresponding methods in L<CGI::Snapp>, in alphabetical order:
=over 4
=item o __CALLBACK_CLASSES => %callback_classes
=item o __CURRENT_RUNMODE => L</get_current_runmode()>
=item o __CURRENT_TMPL_EXTENSION => Not implemented
=item o __ERROR_MODE => L</error_mode([$method_name])>
=item o __HEADER_PROPS => L</header_props([@headers])>
=item o __HEADER_TYPE => L</header_type([$option])>
=item o __HTML_TMPL_CLASS => Not implemented
=item o __INSTALLED_CALLBACKS => L</installed_callbacks()>
=item o __IS_PSGI => _psgi()
=item o __MODE_PARAM => L</mode_param([@new_options])>
=item o __PARAMS => L</param([@params])>
=item o __PRERUN_MODE => L</prerun_mode($string)>
=item o __PRERUN_MODE_LOCKED => _prerun_mode_lock([$Boolean])
=item o __QUERY_OBJ => L</query([$q])>
=item o __RUN_MODES => L</run_modes([$option])>
=item o __START_MODE => L</start_mode([$run_mode])>
=item o __TMPL_PATH => Not implemented
=back
The leading '_' on some CGI::Snapp method names means all such methods are for the exclusive use of the author of this module.
=head3 New methods
=over 4
=item o L</add_header(@headers)>
=item o L</get_callbacks($type, $hook)>
=item o L</log($level, $string)>
=item o L</logger($logger_object)>
=item o L</send_output([$Boolean])>
=back
=head3 Deprecated methods
=over 4
=item o L</header_add(@headers)>
See L</How does add_header() differ from header_add()?>.
=back
=head3 Unsupported methods
=over 4
=item o html_tmpl_class()
=item o load_tmpl()
=item o run_as_psgi()
=item o tmpl_path()
=back
See below for details.
=head3 Enchanced features
=over 4
=item o Use of utf8::downgrade() to turn off utf8 bit on headers
=item o Use of Try::Tiny rather than eval
Ideally, this won't be detectable, and hence won't matter.
=item o call_hook(...) returns a hashref - keys are 'class' and 'object' - of counts of hooks actually called
=item o delete_header(A list)
See L</delete_header(@keys)> for how to delete any number of HTTP headers.
=item o Calling the error_mode() method
This call is protected by Try::Tiny.
=item o Calling mode_param([...])
mode_param() can be called with an arrayref, as in $self -> mode_param([qw/path_info -2/]). See t/run.modes.pl for details.
=item o Calling param([...])
( run in 0.981 second using v1.01-cache-2.11-cpan-cdf2f3d4e48 )