File Coverage

blib/lib/generics.pm
Criterion Covered Total %
statement 77 78 98.7
branch 28 28 100.0
condition 9 9 100.0
subroutine 12 13 92.3
pod 2 2 100.0
total 128 130 98.4


line stmt bran cond sub pod time code
1             package generics;
2              
3 3     3   73600 use strict;
  3         8  
  3         106  
4 3     3   14 use warnings;
  3         8  
  3         245  
5              
6             our $VERSION = '0.04';
7              
8             sub import {
9 17     17   15396 my ($self) = shift;
10             # return if they dont pass anything in (use generics;)
11 17 100       86 return unless @_;
12             # otherwise ...
13 14         32 my ($class, @params) = @_;
14             # turn off strict refs cause we are messing with stuff
15 3     3   21 no strict 'refs';
  3         10  
  3         105  
16             # turn off warnings, so we dont get
17             # the function redefinition warning
18             # or the prototype mismatch as well
19 3     3   24 no warnings qw(redefine prototype once);
  3         5  
  3         2457  
20             # find out who called us
21 14         41 my ($calling_package, $file, $line) = caller();
22             # this is for:
23             # use generics params => (*params*);
24             # it just pre-initializes the sub routines
25             # so they can be called like constants
26             # if you do not then define those params
27             # later on, the methods will just return undefined
28 14 100       293 if ($class eq "params") {
    100          
29             # create a hash for to hold the
30             # valid parameters
31 4 100       7 %{"${calling_package}::GENERIC_PARAMS"} = () unless %{"${calling_package}::GENERIC_PARAMS"};
  3         11  
  4         28  
32 7         69 map {
33             # check for duplicate parameters
34             # basically just see if the param
35             # already exists in the hash of
36             # valid params
37 4 100       9 (!exists(${"${calling_package}::GENERIC_PARAMS"}{$_})) || die "generics exception: attempted duplicate parameter creation in $calling_package in file: $file on line: $line.\n";
  7         11  
38             # this creates a subroutine that returns undef.
39             # this prevents Perl from thinking that this
40             # subroutine doesnt exist, but allows you to
41             # to catch it as an error.
42 6         9 my $name = $_;
43 6     2   23 *{"${calling_package}::$_"} = sub { die "generics exception: ${calling_package}::$name is an undefined parameter (and has no default)\n" };
  6         28  
  2         913  
44             # add the latest param as a key in the valid
45             # params hash, and increment it by one
46 6         10 ${"${calling_package}::GENERIC_PARAMS"}{$_}++;
  6         22  
47             } @params;
48 3         498 return;
49             }
50             elsif ($class eq "inherit") {
51             # if you want to inherit generic params,
52             # but still have your own, then you need
53             # to do this:
54             # use generics inherit => "My::Base::Class";
55             # and it will allow one to inherit from
56             # the base class. it can be called alone
57             # or in conjunction with other calls to generics
58             # which therby either create or overwrite the
59             # generic params alreay inherited.
60             # NOTE:
61             # we create a function in the calling package which
62             # returns the value of the function from the base
63             # packages so that we are truely inheriting from it.
64             # But keep in mind that this all happens at runtime,
65             # so if the generic parameters are changed in
66             # the calling package then it will override these
67             # parameters, because that change will happen at
68             # compile time and therefore override this function.
69             # NOTE:
70             # any changes made to the params of the base package
71             # will be reflected in the calling package since the
72             # inheritance is performed at runtime.
73 2         5 my $base_package = $params[0];
74 2         3 %{"${calling_package}::GENERIC_PARAMS"} = %{"${base_package}::GENERIC_PARAMS"};
  2         32  
  2         12  
75 2         5 foreach my $param_key (keys %{"${base_package}::GENERIC_PARAMS"}) {
  2         10  
76 4     1   28 *{"${calling_package}::${param_key}"} = sub { &{"${base_package}::${param_key}"}() };
  4         19  
  1         1874  
  1         8  
77             }
78 2         57 return;
79             }
80             # before we go any further lets make sure
81             # the parameters are even key value pairs
82 8 100 100     130 (@params && ($#params % 2) != 0) || die "generics exception: uneven parameter assigments of generics in $calling_package in file: $file on line: $line.\n";
83 6         50 my %params = @params;
84             # this is for:
85             # use generics default_params => (*params and default values*);
86             # it sets up the generic parameters and
87             # fills them with a default value.
88             # NOTE:
89             # there is no need to check for
90             # duplicate params here, because they
91             # will get swallowed up by the hash
92             # assignment.
93 6 100       18 if ($class eq "default_params") {
94             # create a hash for to hold the
95             # valid parameters, unless we already
96             # have one (meaning someone has done
97             # and "inherit" somewhere.
98 3 100       4 %{"${calling_package}::GENERIC_PARAMS"} = () unless %{"${calling_package}::GENERIC_PARAMS"};
  2         7  
  3         23  
99 3         14 while (my ($key, $value) = each %params) {
100             # add the latest param as a key in the valid
101             # params hash, and increment it by one
102 5         7 ${"${calling_package}::GENERIC_PARAMS"}{$key}++;
  5         13  
103 5 100       14 *{"${calling_package}::$key"} = $value if (ref($value) eq "CODE");
  2         10  
104 5 100 100 1   36 *{"${calling_package}::$key"} = sub { $value } if (!ref($value) || (ref($value) ne "CODE"));
  3         161  
  1         2037  
105             }
106             }
107             # this is for:
108             # use generics *package* => (*params and values*);
109             # this is when the module is loaded and
110             # before you use it in any code. It populates
111             # the generic parameters with the new
112             # values that are passed.
113             else {
114             # get the hash of valid params
115 3         5 my %valid_params = %{"${class}::GENERIC_PARAMS"};
  3         18  
116 3         24 while (my ($key, $value) = each %params) {
117             # before we assign anything, check
118             # to see that the key we are assigning
119             # is a valid param in the generic module
120 5 100       34 (exists($valid_params{$key})) || die "generics exception: $key is not a valid generic parameter for $class in $calling_package in file: $file on line: $line.\n";
121             # if we get past the exception, then all
122             # is cool and we can assign the parameter
123 4 100       15 *{"${class}::$key"} = $value if (ref($value) eq "CODE");
  1         5  
124 4 100 100 0   2312 *{"${class}::$key"} = sub () { $value } if (!ref($value) || (ref($value) ne "CODE"));
  3         1905  
  0         0  
125             }
126             }
127             }
128              
129             ## NOTE:
130             # if ever you need to change the module configuration
131             # you will need to re-import the the configuration. Here
132             # is a way to do that (without having to say import which
133             # wouldnt make as much sense semanticaly).
134             # Keep in mind though that this will not restore the default
135             # values originally assigned in the class, it will just overwrite
136             # the current ones.
137             #
138             # this will be needed very rarely. If you find yourself using it
139             # you should question the reason first, and only use it as a last
140             # resort.
141             *change_params = \&import;
142              
143             # to support module reloading
144              
145             sub has_generic_params {
146 4     4 1 906 my ($self, $package_name) = @_;
147 3     3   23 no strict 'refs';
  3         6  
  3         276  
148 4 100       25 return exists ${"${package_name}::"}{GENERIC_PARAMS} ? 1 : 0;
  4         34  
149             }
150              
151             sub dump_params {
152 1     1 1 5366 my ($self, $package_name) = @_;
153 3     3   16 no strict 'refs';
  3         6  
  3         313  
154 2         9 return map {
155 2         3 ($_ => &{"${package_name}::$_"}())
  1         8  
156 1         2 } keys %{"${package_name}::GENERIC_PARAMS"};
157             }
158              
159             1;
160             __END__