File Coverage

blib/lib/Class/AbstractLogic.pm
Criterion Covered Total %
statement 12 12 100.0
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 16 16 100.0


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             Class::AbstractLogic - Handling Logic Abstractions
4              
5             =cut
6              
7             package Class::AbstractLogic;
8 1     1   903 use warnings;
  1         3  
  1         37  
9 1     1   5 use strict;
  1         2  
  1         37  
10              
11 1     1   760 use Carp::Clan qw/^Class::AbstractLogic::/;
  1         1662  
  1         7  
12 1     1   803 use aliased 'Class::AbstractLogic::Manager';
  1         638  
  1         5  
13             use aliased 'Class::AbstractLogic::Base';
14             use aliased 'Class::AbstractLogic::Action';
15             use Scalar::Util qw(blessed);
16              
17             our $VERSION = '0.01_01';
18              
19             sub NeedSpec () { 'Class::AbstractLogic::NeedSpec' }
20             sub VerifySpec () { 'Class::AbstractLogic::VerifySpec' }
21              
22             =head1 SYNOPSIS
23              
24             # the logic class definition
25             package My::Logic::Foo;
26             use Class::AbstractLogic-base;
27              
28             # a logic action
29             action 'add',
30             needs [qw(a b)],
31             verify { a => sub { /^\d+$/ }, b => sub { /^\d+$/ } },
32             sub { $_{a} + $_{b} };
33              
34             1;
35             ...
36              
37             # logic module manager creation
38             use Class::AbstractLogic;
39             my $calm = Class::AbstractLogic::Manager->new;
40              
41             # loading a logic class
42             $calm->load_logic(Foo => 'My::Logic::Foo');
43              
44             # requesting a result from a logic method
45             my $result = $calm->logic('Foo')->add(a => 11, b => 12);
46              
47             # $result will be false if an exception was caught
48             if ($result) {
49             print 'result was ' . $result->value . "\n";
50             }
51             else {
52             print 'exception raised: ' . $result->key . "\n";
53             print 'error message: ' . $result->error . "\n";
54             }
55              
56             =head1 DESCRIPTION
57              
58             This module provides a small framework to abstract logics. It was mostly
59             thought to have a place for isolated business-logic that does neither fit
60             in the MVC concepts controllers nor its models.
61              
62             =head2 Logic Modules
63              
64             package FooLogic;
65             use warnings;
66             use strict;
67              
68             use Class::AbstractLogic-base;
69              
70             # ... action definitions ...
71              
72             1;
73              
74             You can create a new logic module easily. By Cing C
75             with the postfix C<-base> you request the installation of action helpers as
76             well as the L class into this modules C<@ISA>.
77              
78             =head2 Action Definitions
79              
80             ...
81             action 'foo', needs [qw(field1 field2)],
82             verify { field1 => sub { ... }, ... },
83             sub { do_stuff_with($_{field1}) };
84             ...
85              
86             The installed helpers are named C, C and C. The first
87             defines the actions name. C accepts either an arrayref with a list of,
88             or a scalar with a single field name that have to be specified in the arguments
89             to this action. The C hash reference takes a code reference for each
90             key representing an argument name. The code ref gets the value passed in C<@_>
91             and as C<$_>. If it returns a false value, an error is thrown.
92              
93             C just looks for a code reference in the stream of its arguments to
94             determine which is the subroutine that actually represents the action. The
95             arguments passed with the call are available in C<@_> after the first value
96             representing the current logical module object, and also come in the global
97             hash C<%_> for easier and more readable access. Via C<&_> you can access
98             other logical classes:
99              
100             action 'foo',
101             sub { my $res = _('Bar')->bar(baz => 23); ... }
102              
103             B however, that the return values of calls to other logic methods will
104             return C objects which you have to deal with.
105             If your action returns a result object, however, it will not be rewrapped in
106             another result, but just returned itself.
107              
108             Through the logical module object you have access to the C and C
109             methods.
110              
111             =head2 Logic Modules and the Manager, General Usage
112              
113             my $calm = Class::AbstractLogic::Manager->new(
114             config => { Foo => { foobar => 23 }} );
115              
116             This creates a new logic module manager. The configuration is logic module
117             specific. In the above example, a logic module registered under C will
118             have C<{ foobar =E 23 }> as its config value.
119              
120             $calm->load_logic(Foo => 'FooLogicClass');
121              
122             This loads the class C and registers it in themanager under the
123             name C.
124              
125             my $result = $calm->logic('Foo')->foo(field1 => 12, field2 => 13);
126              
127             This calls the action C with the arguments C and C on the
128             logic module registered under the name C.
129              
130             =head2 The Result
131              
132             if ($result) { print "ok\n" } else { print "not ok\n" }
133              
134             The boolean value of the result object will be false if an exception was thrown.
135             If the call succeeded, it will evaluate to true and you can access the value via
136             the C method or its C alias.
137              
138             =head2 Logic Exceptions
139              
140             To provide a facility to handle errors and other exception like things, C
141             has a built-in exception handling facility. Inside of your actions you can just
142             throw an exception, which will propagate up to the place the current action was
143             called from.
144              
145             action 'foo',
146             sub { my $self = shift;
147             $self->throw( foobar => 'Extensive Error Message' );
148             };
149              
150             ...
151             my $result = $calm->logic('WithDyingFoo')->foo(bar => 23);
152              
153             In the above example, the C<$result> will evaluate to false. You can access its
154             error message through the C method, and its error key (the first argument
155             you specified, it's for easier flow handling with exceptions) through the C
156             method. If you need, you can also get to the original exception object through
157             C.
158              
159             =head1 METHODS
160              
161             =head2 import(@args)
162              
163             Handles helper installations in your Logic Modules.
164              
165             =cut
166              
167             sub import {
168             my ($class, @args) = @_;
169             my $type = shift @args;
170              
171             if (defined($type) and $type =~ /^-?base$/i) {
172             $class->import_helpers(scalar caller);
173             }
174             1;
175             }
176              
177             =head2 import_helpers($target)
178              
179             Internal method that installs the C, C and C helpers in
180             C<$target>.
181              
182             =cut
183              
184             sub import_helpers {
185             my ($class, $target) = @_;
186             no strict 'refs';
187              
188             unshift @{$target . '::ISA' }, Base;
189              
190             *{$target . '::' . $_} = $class->can('_handle_' . $_)
191             for qw/ action
192             needs
193             verify /;
194             }
195              
196             =head2 _handle_action(@args)
197              
198             Helper Method, creates a new action in a Logic Class.
199              
200             =cut
201              
202             sub _handle_action {
203             my ($name, @args) = @_;
204             croak 'Logic Action has no name'
205             unless defined($name) and length($name);
206              
207             my %tests = (
208             code => sub { ref shift eq 'CODE' },
209             needs => sub { (blessed(shift) || '') eq NeedSpec },
210             verify => sub { (blessed(shift) || '') eq VerifySpec },
211             );
212             my %found = ( name => $name );
213             for my $arg (@args) {
214             for my $t_key (keys %tests) {
215             if ($tests{$t_key}->($arg)) {
216             croak "Action Logic '$name' has more than one $t_key argument"
217             if exists $found{$t_key};
218             $found{$t_key} = $arg;
219             }
220             }
221             }
222              
223             croak "Logic Action '$name' has no code argument"
224             unless exists $found{code};
225              
226             my $action = Action->new(%found);
227             $action->install(scalar(caller), $name);
228             1;
229             }
230              
231             =head2 _handle_needs($spec)
232              
233             Helper Method, checks and flags the C specification.
234              
235             =cut
236              
237             sub _handle_needs ($) {
238             my $spec = shift;
239             $spec = [$spec] if not ref $spec;
240             unless (ref $spec eq 'ARRAY') {
241             croak 'Logic Action need specification expects ArrayRef or Scalar';
242             }
243             return bless $spec, NeedSpec;
244             }
245              
246             =head2 _handle_verify($spec)
247              
248             Helper Method, checks and flags the C specification.
249              
250             =cut
251              
252             sub _handle_verify ($) {
253             my $spec = shift;
254             croak 'Logic Action verify specification expects HashRef'
255             unless ref $spec eq 'HASH';
256             return bless $spec, VerifySpec;
257             }
258              
259             {
260             package Class::AbstractLogic::NeedSpec;
261             package Class::AbstractLogic::VerifySpec;
262             }
263              
264             =head1 AUTHOR
265              
266             Robert 'phaylon' Sedlacek Cphaylon@dunkelheit.atE>
267              
268             =head1 LICENSE AND COPYRIGHT
269              
270             This program is free software, you can redistribute it and/or modify it under
271             the same terms as Perl itself.
272              
273             =cut
274              
275              
276             1;