File Coverage

lib/Class/Factory.pm
Criterion Covered Total %
statement 94 109 86.2
branch 28 42 66.6
condition 13 36 36.1
subroutine 15 18 83.3
pod 0 17 0.0
total 150 222 67.5


line stmt bran cond sub pod time code
1             package Class::Factory;
2              
3             # $Id: Factory.pm 46 2007-11-07 00:06:38Z cwinters $
4              
5 1     1   989 use strict;
  1         2  
  1         2293  
6              
7             $Class::Factory::VERSION = '1.06';
8              
9             my %CLASS_BY_FACTORY_AND_TYPE = ();
10             my %FACTORY_INFO_BY_CLASS = ();
11             my %REGISTER = ();
12              
13             # Simple constructor -- override as needed
14              
15             sub new {
16 5     5 0 326 my ( $pkg, $type, @params ) = @_;
17 5         17 my $class = $pkg->get_factory_class( $type );
18 5 100       19 return undef unless ( $class );
19 2         5 my $self = bless( {}, $class );
20 2         10 return $self->init( @params );
21             }
22              
23              
24             # Subclasses should override, but if they don't they shouldn't be
25             # penalized...
26              
27 0     0 0 0 sub init { return $_[0] }
28              
29             # Find the class associated with $object_type
30              
31             sub get_factory_class {
32 7     7 0 856 my ( $item, $object_type ) = @_;
33 7   33     31 my $class = ref $item || $item;
34 7         14 my $factory_class =
35             $CLASS_BY_FACTORY_AND_TYPE{ $class }->{ $object_type };
36 7 100       20 return $factory_class if ( $factory_class );
37              
38 4         8 $factory_class = $REGISTER{ $class }->{ $object_type };
39 4 100       8 if ( $factory_class ) {
40 2         8 my $added_class =
41             $class->add_factory_type( $object_type, $factory_class );
42 2         6 return $added_class;
43             }
44 2         13 $item->factory_error( "Factory type '$object_type' is not defined ",
45             "in '$class'" );
46 2         10 return undef;
47             }
48              
49              
50             # Associate $object_type with $object_class
51              
52             sub add_factory_type {
53 6     6 0 1647 my ( $item, $object_type, $object_class ) = @_;
54 6   33     27 my $class = ref $item || $item;
55 6 50       13 unless ( $object_type ) {
56 0         0 $item->factory_error( "Cannot add factory type to '$class': no ",
57             "type defined");
58             }
59 6 50       10 unless ( $object_class ) {
60 0         0 $item->factory_error( "Cannot add factory type '$object_type' to ",
61             "'$class': no class defined" );
62             }
63              
64 6         12 my $set_object_class =
65             $CLASS_BY_FACTORY_AND_TYPE{ $class }->{ $object_type };
66 6 100       12 if ( $set_object_class ) {
67 1         9 $item->factory_log( "Attempt to add type '$object_type' to '$class' ",
68             "redundant; type already exists with class ",
69             "'$set_object_class'" );
70 1         8 return;
71             }
72              
73             # Make sure the object class looks like a perl module/script
74             # Acceptable formats:
75             # Module.pm Module.ph Module.pl Module
76 5         26 $object_class =~ m/^([\w:-]+(?:\.(?:pm|ph|pl))?)$/;
77 5         12 $object_class = $1;
78              
79 5 50       15 if ( $INC{ $object_class } ) {
80 0         0 $item->factory_log( "Looks like class '$object_class' was already ",
81             "included; no further work necessary" );
82             }
83             else {
84 5         260 eval "require $object_class";
85 5 100       223 if ( $@ ) {
86 2         19 $item->factory_error( "Cannot add factory type '$object_type' to ",
87             "class '$class': factory class '$object_class' ",
88             "cannot be required: $@" );
89 2         16 return undef;
90             }
91             }
92              
93             # keep track of what classes have been included so far...
94 3         11 $CLASS_BY_FACTORY_AND_TYPE{ $class }->{ $object_type } = $object_class;
95              
96             # keep track of what factory and type are associated with a loaded
97             # class...
98 3         9 $FACTORY_INFO_BY_CLASS{ $object_class } = [ $class, $object_type ];
99              
100 3         9 return $object_class;
101             }
102              
103             sub register_factory_type {
104 3     3 0 12 my ( $item, $object_type, $object_class ) = @_;
105 3   33     18 my $class = ref $item || $item;
106 3 50       7 unless ( $object_type ) {
107 0         0 $item->factory_error( "Cannot add factory type to '$class': no type ",
108             "defined" );
109             }
110 3 50       9 unless ( $object_class ) {
111 0         0 $item->factory_error( "Cannot add factory type '$object_type' to ",
112             "'$class': no class defined" );
113             }
114              
115 3         6 my $set_object_class = $REGISTER{ $class }->{ $object_type };
116 3 100       8 if ( $set_object_class ) {
117 1         12 $item->factory_log( "Attempt to register type '$object_type' with ",
118             "'$class' is redundant; type registered with ",
119             "class '$set_object_class'" );
120 1         6 return;
121             }
122 2         9 return $REGISTER{ $class }->{ $object_type } = $object_class;
123             }
124              
125              
126             sub remove_factory_type {
127 1     1 0 825 my ( $item, @object_types ) = @_;
128 1   33     9 my $class = ref $item || $item;
129              
130 1         3 for my $object_type (@object_types) {
131 1 50       4 unless ( $object_type ) {
132 0         0 $item->factory_error(
133             "Cannot remove factory type from '$class': no type defined"
134             );
135             }
136              
137 1         6 delete $CLASS_BY_FACTORY_AND_TYPE{ $class }->{ $object_type };
138             }
139             }
140              
141              
142             sub unregister_factory_type {
143 1     1 0 1112 my ( $item, @object_types ) = @_;
144 1   33     7 my $class = ref $item || $item;
145              
146 1         2 for my $object_type (@object_types) {
147 1 50       3 unless ( $object_type ) {
148 0         0 $item->factory_error(
149             "Cannot remove factory type from '$class': no type defined"
150             );
151             }
152              
153 1         3 delete $REGISTER{ $class }->{ $object_type };
154              
155             # Also delete from $CLASS_BY_FACTORY_AND_TYPE because if the object
156             # type has already been instantiated, then it will have been processed
157             # by add_factory_type(), thus creating an entry in
158             # $CLASS_BY_FACTORY_AND_TYPE. We can call register_factory_type()
159             # again, but when we try to instantiate an object via
160             # get_factory_class(), it will find the old entry in
161             # $CLASS_BY_FACTORY_AND_TYPE and use that.
162              
163 1         5 delete $CLASS_BY_FACTORY_AND_TYPE{ $class }->{ $object_type };
164             }
165             }
166              
167              
168             sub get_loaded_classes {
169 2     2 0 358 my ( $item ) = @_;
170 2   33     13 my $class = ref $item || $item;
171 2 50       11 return () unless ( ref $CLASS_BY_FACTORY_AND_TYPE{ $class } eq 'HASH' );
172 2         3 return sort values %{ $CLASS_BY_FACTORY_AND_TYPE{ $class } };
  2         23  
173             }
174              
175             sub get_loaded_types {
176 2     2 0 1667 my ( $item ) = @_;
177 2   33     13 my $class = ref $item || $item;
178 2 50       8 return () unless ( ref $CLASS_BY_FACTORY_AND_TYPE{ $class } eq 'HASH' );
179 2         2 return sort keys %{ $CLASS_BY_FACTORY_AND_TYPE{ $class } };
  2         10  
180             }
181              
182             sub get_registered_classes {
183 1     1 0 2333 my ( $item ) = @_;
184 1   33     7 my $class = ref $item || $item;
185 1 50       6 return () unless ( ref $REGISTER{ $class } eq 'HASH' );
186 1         2 return sort values %{ $REGISTER{ $class } };
  1         6  
187             }
188              
189             sub get_registered_class {
190 1     1 0 604 my ( $item, $type ) = @_;
191 1 50       5 unless ( $type ) {
192 0         0 warn("No factory type passed");
193 0         0 return undef;
194             }
195 1   33     8 my $class = ref $item || $item;
196 1 50       4 return undef unless ( ref $REGISTER{ $class } eq 'HASH' );
197 1         4 return $REGISTER{ $class }{ $type };
198             }
199              
200             sub get_registered_types {
201 1     1 0 553 my ( $item ) = @_;
202 1   33     7 my $class = ref $item || $item;
203 1 50       6 return () unless ( ref $REGISTER{ $class } eq 'HASH' );
204 1         2 return sort keys %{ $REGISTER{ $class } };
  1         6  
205             }
206              
207             # Return the factory class that created $item (which can be an object
208             # or class)
209              
210             sub get_my_factory {
211 2     2 0 2122 my ( $item ) = @_;
212 2   33     14 my $impl_class = ref( $item ) || $item;
213 2         4 my $impl_info = $FACTORY_INFO_BY_CLASS{ $impl_class };
214 2 50       8 if ( ref( $impl_info ) eq 'ARRAY' ) {
215 2         11 return $impl_info->[0];
216             }
217 0         0 return undef;
218             }
219              
220             # Return the type that the factory used to create $item (which can be
221             # an object or class)
222              
223             sub get_my_factory_type {
224 2     2 0 22 my ( $item ) = @_;
225 2         15 $item->get_factory_type_for($item);
226             }
227              
228              
229             # Return the type that the factory uses to create a given object or class.
230              
231             sub get_factory_type_for {
232 5     5 0 1091 my ( $self, $item ) = @_;
233 5   66     19 my $impl_class = ref( $item ) || $item;
234 5         8 my $impl_info = $FACTORY_INFO_BY_CLASS{ $impl_class };
235 5 100       16 if ( ref( $impl_info ) eq 'ARRAY' ) {
236 4         530 return $impl_info->[1];
237             }
238 1         4 return undef;
239             }
240              
241             ########################################
242             # Overridable Log / Error
243              
244 0     0 0   sub factory_log { shift; warn @_, "\n" }
  0            
245 0     0 0   sub factory_error { shift; die @_, "\n" }
  0            
246              
247             1;
248              
249             __END__