File Coverage

blib/lib/VS/RuleEngine/Declare.pm
Criterion Covered Total %
statement 132 135 97.7
branch 33 52 63.4
condition 6 12 50.0
subroutine 28 29 96.5
pod 16 16 100.0
total 215 244 88.1


line stmt bran cond sub pod time code
1             package VS::RuleEngine::Declare;
2              
3 14     14   323777 use strict;
  14         36  
  14         864  
4 14     14   121 use warnings;
  14         42  
  14         458  
5              
6 14     14   77 use Carp;
  14         41  
  14         1690  
7 14     14   94 use List::Util qw(first);
  14         29  
  14         2005  
8 14     14   88 use Scalar::Util qw(blessed);
  14         28  
  14         3589  
9              
10 14     14   8637 use VS::RuleEngine::Engine;
  14         44  
  14         138  
11              
12 14     14   9241 use VS::RuleEngine::Action::Perl;
  14         42  
  14         399  
13 14     14   7659 use VS::RuleEngine::Hook::Perl;
  14         36  
  14         584  
14 14     14   8449 use VS::RuleEngine::Input::Perl;
  14         43  
  14         502  
15 14     14   8263 use VS::RuleEngine::Output::Perl;
  14         39  
  14         412  
16 14     14   8787 use VS::RuleEngine::Rule::Perl;
  14         44  
  14         395  
17              
18 14     14   110 use VS::RuleEngine::Util qw(is_existing_package);
  14         28  
  14         37234  
19              
20             require Exporter;
21              
22             our @ISA = qw(Exporter);
23              
24             our @EXPORT = qw(
25             action
26             as
27             defaults
28             does
29             engine
30             input
31             instanceof
32             load_module
33             output
34             posthook
35             prehook
36             rule
37             run
38             when
39             with_args
40             with_defaults
41             );
42              
43             our $current_engine;
44              
45             sub engine(&) {
46 29     29 1 801 my ($sub, $name) = @_;
47              
48 29         229 my $engine = VS::RuleEngine::Engine->new();
49              
50 29         54 local $current_engine = $engine;
51 29         97 $sub->();
52            
53 29 50       114 if (defined $name) {
54 0         0 VS::RuleEngine::Engine->register_engine($name => $engine);
55             }
56            
57 29         85 return $engine;
58             }
59              
60             sub as($) {
61 0     0 1 0 return $_[0];
62             }
63              
64             sub does(&) {
65 141     141 1 337 my $cv = shift;
66 141         388 my $does = bless [$cv], "_Does";
67 141         493 return $does;
68             }
69              
70             {
71             my %Classes;
72             sub load_module($) {
73 1     1 1 13 my $class = shift;
74 1 50       5 if (!exists $Classes{$class}) {
75 1         121 eval "require $class;";
76 1 50       352 croak $@ if $@;
77 1         4 $Classes{$class} = 1;
78             }
79            
80 1         3 1;
81             }
82             }
83              
84             sub instanceof($) {
85 34     34 1 77 my $class = shift;
86 34 100       115 load_module($class) if !is_existing_package($class);
87 34         184 my $instanceof = bless [$class], "_InstanceOf";
88 34         164 return $instanceof;
89             }
90              
91             sub with_args($) {
92 26     26 1 42 my $args = shift;
93 26 50       87 croak "Arguments must be a hash reference" if ref $args ne 'HASH';
94 26         92 my $with_args = bless $args, "_WithArgs";
95 26         93 return $with_args;
96             }
97              
98             sub with_defaults($) {
99 2     2 1 4 my $defaults = shift;
100 2 50 66     11 croak "Arguments must be a single string or an array reference" if ref $defaults && ref $defaults ne 'ARRAY';
101 2 100       8 $defaults = [$defaults] if ref $defaults ne 'ARRAY';
102            
103 2         8 my $with_defaults = bless [@$defaults], "_WithDefaults";
104 2         8 return $with_defaults;
105             }
106              
107             sub when(@) {
108 42     42 1 251 for (@_) {
109 43 50       467 croak "Rule '$_' does not exist" if !$current_engine->has_rule($_);
110             }
111 42         173 my $rules = bless [@_], "_When";
112 42         112 return $rules;
113             }
114              
115             sub run(@) {
116 42 100   42 1 73 my @when = grep { blessed $_ && $_->isa('_When') } @_;
  84         521  
117 42   66     60 my @actions = grep { !(blessed $_ && $_->isa('_When')) } @_;
  84         454  
118            
119 42 50       124 croak "Unkown input for 'run'" if @_ > @when + @actions;
120            
121 42         77 for (@actions) {
122 42 50       130 croak "Action '$_' does not exist" if !$current_engine->has_action($_);
123             }
124            
125             # Add all actions to each rule
126 42         213 for my $rule (map { @$_ } @when) {
  42         119  
127 43         81 for my $action (@actions) {
128 43         130 $current_engine->add_rule_action($rule => $action);
129             }
130             }
131             }
132              
133             sub _get_command {
134 181     181   226 my $kind = shift;
135 181         199 my $base_class = shift;
136 181         188 my $does_class = shift;
137            
138 181 50       461 croak "Can't use keyword '${kind}' outside an engine declaration" if !$current_engine;
139            
140 181 50       249 my @isa = grep { blessed $_ && $_->isa('_InstanceOf') } @_;
  227         2317  
141 181 50       422 croak "Multiple 'instanceof' declared" if @isa > 1;
142            
143 181 50       257 my @args = grep { blessed $_ && $_->isa('_WithArgs') } @_;
  227         1746  
144 181 50       381 croak "Multiple 'with_args' declared" if @args > 1;
145              
146 181 50       247 my @defaults = grep { blessed $_ && $_->isa('_WithDefaults') } @_;
  227         1640  
147 181 50       390 croak "Multiple 'with_defaults' declared" if @defaults > 1;
148            
149 181 50       233 my @does = grep { blessed $_ && $_->isa('_Does') } @_;
  227         1557  
150 181 50       463 croak "Multiple 'does' declared" if @does > 1;
151            
152 181         238 my $instance = shift;
153 181         284 my $cmd;
154 181         245 my $defaults = [];
155            
156 181 100 33     553 if (@isa) {
    100 33        
    50          
157 34 100       114 $defaults = [@{shift @defaults}] if @defaults;
  2         5  
158 34 100       81 @args = @args ? %{shift @args} : ();
  26         107  
159 34         75 $cmd = (shift @isa)->[0];
160             }
161             elsif (@does) {
162 141         288 @args = (shift @does)->[0];
163 141         202 $cmd = $does_class;
164             }
165             elsif ($instance && blessed $instance && $instance->isa($base_class)) {
166 6         12 $cmd = $instance;
167             }
168             else {
169 0         0 croak "Can't fingure out how to create ${kind} because we have neither 'instanceof', 'does' nor an instance";
170             }
171            
172 181         721 return ($cmd, $defaults, @args);
173             }
174              
175             sub action ($@) {
176 48     48 1 69 my $name = shift;
177 48         127 my ($action, $defaults, @args) = _get_command("action", "VS::RuleEngine::Action", "VS::RuleEngine::Action::Perl", @_);
178 48         187 $current_engine->add_action($name => $action, $defaults, @args);
179             }
180              
181             sub defaults ($$) {
182 2     2 1 15 my $name = shift;
183 2         3 my $defaults = shift;
184 2 50       9 croak "Defaults is not a hash reference" if ref $defaults ne 'HASH';
185 2         11 $current_engine->add_defaults($name => $defaults);
186             }
187              
188             sub input ($@) {
189 54     54 1 77 my $name = shift;
190 54         123 my ($input, $defaults, @args) = _get_command("input", "VS::RuleEngine::Input", "VS::RuleEngine::Input::Perl", @_);
191 54         202 $current_engine->add_input($name => $input, $defaults, @args);
192             }
193              
194             sub output ($@) {
195 5     5 1 8 my $name = shift;
196 5         12 my ($output, $defaults, @args) = _get_command("output", "VS::RuleEngine::Output", "VS::RuleEngine::Output::Perl", @_);
197 5         28 $current_engine->add_output($name => $output, $defaults, @args);
198             }
199              
200             sub prehook ($@) {
201 4     4 1 7 my $name = shift;
202 4         11 my ($hook, $defaults, @args) = _get_command("prehook", "VS::RuleEngine::Hook", "VS::RuleEngine::Hook::Perl", @_);
203 4         17 $current_engine->add_hook($name => $hook, $defaults, @args);
204 4         14 $current_engine->add_pre_hook($name);
205             }
206              
207             sub posthook ($@) {
208 25     25 1 43 my $name = shift;
209 25         286 my ($hook, $defaults, @args) = _get_command("posthook", "VS::RuleEngine::Hook", "VS::RuleEngine::Hook::Perl", @_);
210 25         145 $current_engine->add_hook($name => $hook, $defaults, @args);
211 25         96 $current_engine->add_post_hook($name);
212             }
213              
214             sub rule ($@) {
215 45     45 1 61 my $name = shift;
216 45         102 my ($rule, $defaults, @args) = _get_command("rule", "VS::RuleEngine::Rule", "VS::RuleEngine::Rule::Perl", @_);
217 45         182 $current_engine->add_rule($name => $rule, $defaults, @args);
218             }
219              
220             1;
221             __END__