line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
#======================================================================== |
2
|
|
|
|
|
|
|
# |
3
|
|
|
|
|
|
|
# Badger::Factory |
4
|
|
|
|
|
|
|
# |
5
|
|
|
|
|
|
|
# DESCRIPTION |
6
|
|
|
|
|
|
|
# Factory module for loading and instantiating other modules. |
7
|
|
|
|
|
|
|
# |
8
|
|
|
|
|
|
|
# NOTE |
9
|
|
|
|
|
|
|
# This module has grown organically to fit a number of (possibly |
10
|
|
|
|
|
|
|
# conflicting) needs. It needs to be completely refactored, and |
11
|
|
|
|
|
|
|
# probably split into a number of different factory modules. The |
12
|
|
|
|
|
|
|
# TT3 code on which this was originally based had separate base class |
13
|
|
|
|
|
|
|
# factory modules for modules (that just got loaded), objects (that |
14
|
|
|
|
|
|
|
# got loaded and instantiated) and single objects (that got loaded, |
15
|
|
|
|
|
|
|
# created and cached). With hindsight, it was a mistake to try and |
16
|
|
|
|
|
|
|
# cram all that functionality into one module. It should be separated |
17
|
|
|
|
|
|
|
# into a base class module/API and a number of specialised subclasses. |
18
|
|
|
|
|
|
|
# |
19
|
|
|
|
|
|
|
# AUTHOR |
20
|
|
|
|
|
|
|
# Andy Wardley |
21
|
|
|
|
|
|
|
# |
22
|
|
|
|
|
|
|
#======================================================================== |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
package Badger::Factory; |
25
|
|
|
|
|
|
|
|
26
|
24
|
|
|
24
|
|
541
|
use Badger::Debug ':dump'; |
|
24
|
|
|
|
|
34
|
|
|
24
|
|
|
|
|
146
|
|
27
|
|
|
|
|
|
|
use Badger::Class |
28
|
24
|
|
|
|
|
322
|
version => 0.01, |
29
|
|
|
|
|
|
|
debug => 0, |
30
|
|
|
|
|
|
|
base => 'Badger::Prototype Badger::Exporter', |
31
|
|
|
|
|
|
|
import => 'class', |
32
|
|
|
|
|
|
|
utils => 'plural blessed textlike dotid camel_case', |
33
|
|
|
|
|
|
|
words => 'ITEM ITEMS ISA', |
34
|
|
|
|
|
|
|
constants => 'PKG ARRAY HASH REFS ONCE DEFAULT LOADED', |
35
|
|
|
|
|
|
|
constant => { |
36
|
|
|
|
|
|
|
OBJECT => 'object', |
37
|
|
|
|
|
|
|
FOUND => 'found', |
38
|
|
|
|
|
|
|
FOUND_REF => 'found_ref', |
39
|
|
|
|
|
|
|
PATH_SUFFIX => '_PATH', |
40
|
|
|
|
|
|
|
NAMES_SUFFIX => '_NAMES', |
41
|
|
|
|
|
|
|
DEFAULT_SUFFIX => '_DEFAULT', |
42
|
|
|
|
|
|
|
}, |
43
|
|
|
|
|
|
|
messages => { |
44
|
|
|
|
|
|
|
no_item => 'No item(s) specified for factory to manage', |
45
|
|
|
|
|
|
|
no_default => 'No default defined for %s factory', |
46
|
|
|
|
|
|
|
bad_ref => 'Invalid reference for %s factory item %s: %s', |
47
|
|
|
|
|
|
|
bad_method => qq{Can't locate object method "%s" via package "%s" at %s line %s}, |
48
|
24
|
|
|
24
|
|
157
|
}; |
|
24
|
|
|
|
|
49
|
|
49
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
our $RUNAWAY = 0; |
51
|
|
|
|
|
|
|
our $AUTOLOAD; |
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
*init = \&init_factory; |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
sub init_factory { |
57
|
25
|
|
|
25
|
0
|
61
|
my ($self, $config) = @_; |
58
|
25
|
|
|
|
|
87
|
my $class = $self->class; |
59
|
25
|
|
|
|
|
51
|
my ($item, $items, $path, $map, $default); |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
# 'item' and 'items' can be specified as config params or we look for |
62
|
|
|
|
|
|
|
# $ITEM and $ITEMS variables in the current package or those of any |
63
|
|
|
|
|
|
|
# base classes. NOTE: $ITEM and $ITEMS must be in the same package |
64
|
25
|
100
|
|
|
|
77
|
unless ($item = $config->{ item }) { |
65
|
24
|
|
|
|
|
175
|
foreach my $pkg ($class->heritage) { |
66
|
24
|
|
|
24
|
|
160
|
no strict REFS; |
|
24
|
|
|
|
|
41
|
|
|
24
|
|
|
|
|
837
|
|
67
|
24
|
|
|
24
|
|
116
|
no warnings ONCE; |
|
24
|
|
|
|
|
39
|
|
|
24
|
|
|
|
|
22525
|
|
68
|
|
|
|
|
|
|
|
69
|
24
|
50
|
|
|
|
43
|
if (defined ($item = ${ $pkg.PKG.ITEM })) { |
|
24
|
|
|
|
|
56
|
|
70
|
24
|
|
|
|
|
44
|
$items = ${ $pkg.PKG.ITEMS }; |
|
24
|
|
|
|
|
46
|
|
71
|
24
|
|
|
|
|
54
|
last; |
72
|
|
|
|
|
|
|
} |
73
|
|
|
|
|
|
|
} |
74
|
|
|
|
|
|
|
} |
75
|
25
|
50
|
|
|
|
64
|
return $self->error_msg('no_item') |
76
|
|
|
|
|
|
|
unless $item; |
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
# use 'items' in config, or grokked from $ITEMS, or guess plural |
79
|
25
|
|
33
|
|
|
205
|
$items = $config->{ items } || $items || plural($item); |
80
|
|
|
|
|
|
|
|
81
|
25
|
|
|
|
|
56
|
my $ipath = $item.PATH_SUFFIX; |
82
|
25
|
|
|
|
|
45
|
my $inames = $item.NAMES_SUFFIX; |
83
|
25
|
|
|
|
|
55
|
my $idefault = $item.DEFAULT_SUFFIX; |
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
# Merge all XXXX_PATH package vars with any 'xxxx_path' or 'path' config |
86
|
|
|
|
|
|
|
# items. Ditto for XXXX_NAME / 'xxxx_name' / 'aka' and XXXXS/ 'xxxxs' |
87
|
|
|
|
|
|
|
|
88
|
25
|
|
|
|
|
105
|
my @path = @$config{ path => lc $ipath }; |
89
|
25
|
|
|
|
|
60
|
my @names = @$config{ names => lc $inames }; |
90
|
|
|
|
|
|
|
|
91
|
25
|
|
|
|
|
135
|
$self->{ path } = $class->list_vars(uc $ipath, @path); |
92
|
25
|
|
|
|
|
146
|
$self->{ names } = $class->hash_vars(uc $inames, @names); |
93
|
25
|
|
|
|
|
115
|
$self->{ $items } = $class->hash_vars(uc $items, $config->{ $items }); |
94
|
25
|
|
|
|
|
80
|
$self->{ items } = $items; |
95
|
25
|
|
|
|
|
41
|
$self->{ item } = $item; |
96
|
25
|
|
|
|
|
62
|
$self->{ loaded } = { }; |
97
|
|
|
|
|
|
|
$self->{ no_cache } = defined $config->{ no_cache } # quick hack |
98
|
|
|
|
|
|
|
? $config->{ no_cache } |
99
|
25
|
50
|
50
|
|
|
185
|
: $class->any_var('NO_CACHE') || 0; |
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
# see if a 'xxxx_default' or 'default' configuration option is specified |
102
|
|
|
|
|
|
|
# or look for the first XXXX_DEFAULT or DEFAULT package variable. |
103
|
|
|
|
|
|
|
$default = $config->{ $idefault } |
104
|
|
|
|
|
|
|
|| $config->{ default } |
105
|
25
|
|
33
|
|
|
317
|
|| $class->any_var_in( uc $idefault, uc DEFAULT ); |
106
|
25
|
50
|
|
|
|
92
|
if ($default) { |
107
|
0
|
|
|
|
|
0
|
$self->debug("Setting default to $default") if DEBUG; |
108
|
0
|
|
|
|
|
0
|
$self->{ default } = $self->{ names }->{ default } = $default; |
109
|
|
|
|
|
|
|
} |
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
$self->debug( |
112
|
|
|
|
|
|
|
"Initialised $item/$items factory\n", |
113
|
|
|
|
|
|
|
" Path: ", $self->dump_data($self->{ path }), "\n", |
114
|
|
|
|
|
|
|
"Names: ", $self->dump_data($self->{ names }) |
115
|
25
|
|
|
|
|
42
|
) if DEBUG; |
116
|
|
|
|
|
|
|
|
117
|
25
|
|
|
|
|
98
|
return $self; |
118
|
|
|
|
|
|
|
} |
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
sub path { |
121
|
46
|
|
|
46
|
1
|
114
|
my $self = shift->prototype; |
122
|
|
|
|
|
|
|
return @_ |
123
|
|
|
|
|
|
|
? ($self->{ path } = ref $_[0] eq ARRAY ? shift : [ @_ ]) |
124
|
46
|
0
|
|
|
|
110
|
: $self->{ path }; |
|
|
50
|
|
|
|
|
|
125
|
|
|
|
|
|
|
} |
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
sub default { |
128
|
1
|
|
|
1
|
1
|
3
|
my $self = shift->prototype; |
129
|
|
|
|
|
|
|
return @_ |
130
|
|
|
|
|
|
|
? ($self->{ default } = $self->{ names }->{ default } = shift) |
131
|
1
|
50
|
|
|
|
5
|
: $self->{ default }; |
132
|
|
|
|
|
|
|
} |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
sub items { |
135
|
0
|
|
|
0
|
1
|
0
|
my $self = shift->prototype; |
136
|
0
|
|
|
|
|
0
|
my $items = $self->{ $self->{ items } }; |
137
|
0
|
0
|
|
|
|
0
|
if (@_) { |
138
|
0
|
0
|
|
|
|
0
|
my $args = ref $_[0] eq HASH ? shift : { @_ }; |
139
|
0
|
|
|
|
|
0
|
@$items{ keys %$args } = values %$args; |
140
|
|
|
|
|
|
|
} |
141
|
0
|
|
|
|
|
0
|
return $items; |
142
|
|
|
|
|
|
|
} |
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
sub item { |
145
|
85
|
100
|
|
85
|
1
|
156
|
my $self = shift; $self = $self->prototype unless ref $self; |
|
85
|
|
|
|
|
213
|
|
146
|
85
|
|
|
|
|
233
|
my ($type, @args) = $self->type_args(@_); |
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
# In most cases we're expecting $type to be a name (e.g. Table) which we |
149
|
|
|
|
|
|
|
# lookup in the items hash, or tack onto one of the module bases in the |
150
|
|
|
|
|
|
|
# path (e.g. Template::Plugin) to create a full module name which we load |
151
|
|
|
|
|
|
|
# and instantiate (e.g. Template::Plugin::Table). However, the name might |
152
|
|
|
|
|
|
|
# be explicitly mapped to a reference of some kind, or the $type passed |
153
|
|
|
|
|
|
|
# in could already be a reference (e.g. Template::TT2::Filters allow the |
154
|
|
|
|
|
|
|
# first argument to be a code ref or object which implements the required |
155
|
|
|
|
|
|
|
# filtering behaviour). In which case, we bypass any name-based lookup |
156
|
|
|
|
|
|
|
# and skip straight onto the "look what I found!" phase |
157
|
|
|
|
|
|
|
|
158
|
85
|
100
|
|
|
|
231
|
return $self->found($type, $type, \@args) |
159
|
|
|
|
|
|
|
unless textlike $type; |
160
|
|
|
|
|
|
|
|
161
|
83
|
|
|
|
|
137
|
$type = $type . ''; # auto-stringify any textlike objects |
162
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
# OK, so $type is a string. We'll also create a canonical version of the |
164
|
|
|
|
|
|
|
# name (lower case dotted) to provide a case/syntax insensitve fallback |
165
|
|
|
|
|
|
|
# (e.g. so "foo.bar" can match against "Foo.Bar", "Foo::Bar" and so on) |
166
|
|
|
|
|
|
|
|
167
|
83
|
|
|
|
|
175
|
my $items = $self->{ $self->{ items } }; |
168
|
83
|
|
|
|
|
174
|
my $canon = dotid $type; |
169
|
|
|
|
|
|
|
|
170
|
83
|
|
|
|
|
120
|
$self->debug("Looking for '$type' or '$canon' in $self->{ items }") if DEBUG; |
171
|
|
|
|
|
|
|
# $self->debug("types: ", $self->dump_data($self->{ types })) if DEBUG; |
172
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
# false but defined entry indicates the item is not found |
174
|
|
|
|
|
|
|
return $self->not_found($type, \@args) |
175
|
|
|
|
|
|
|
if exists $items->{ $type } |
176
|
83
|
50
|
66
|
|
|
347
|
&& not $items->{ $type }; |
177
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
my $item = $items->{ $type } |
179
|
83
|
|
100
|
|
|
344
|
|| $items->{ $canon } |
180
|
|
|
|
|
|
|
# TODO: this needs to be defined-or, like // |
181
|
|
|
|
|
|
|
# Plugins can return an empty string to indicate that they |
182
|
|
|
|
|
|
|
# do nothing. |
183
|
|
|
|
|
|
|
# HMMM.... or does it? |
184
|
|
|
|
|
|
|
|| $self->find($type, \@args) |
185
|
|
|
|
|
|
|
# || $self->default($type, \@args) |
186
|
|
|
|
|
|
|
|| return $self->not_found($type, \@args); |
187
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
$items->{ $type } = $item |
189
|
81
|
50
|
|
|
|
234
|
unless $self->{ no_cache }; |
190
|
|
|
|
|
|
|
|
191
|
81
|
|
|
|
|
255
|
return $self->found($type, $item, \@args); |
192
|
|
|
|
|
|
|
} |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
sub type_args { |
195
|
|
|
|
|
|
|
# Simple method to grok $type and @args from argument list. The only |
196
|
|
|
|
|
|
|
# processing it does is to set $type to 'default' if it is undefined or |
197
|
|
|
|
|
|
|
# false. Subclasses can re-define this to insert their own type mapping or |
198
|
|
|
|
|
|
|
# argument munging, e.g. to inject values into the configuration params |
199
|
|
|
|
|
|
|
# for an object |
200
|
65
|
|
|
65
|
1
|
71
|
shift; |
201
|
65
|
|
100
|
|
|
166
|
my $type = shift || DEFAULT; |
202
|
65
|
|
|
|
|
78
|
my @args; |
203
|
|
|
|
|
|
|
|
204
|
65
|
50
|
|
|
|
116
|
if (ref $type eq HASH) { |
205
|
0
|
|
|
|
|
0
|
@args = ($type, @_); |
206
|
0
|
|
0
|
|
|
0
|
$type = $type->{ type } || DEFAULT; |
207
|
|
|
|
|
|
|
} |
208
|
|
|
|
|
|
|
else { |
209
|
65
|
|
|
|
|
102
|
@args = @_; |
210
|
|
|
|
|
|
|
} |
211
|
|
|
|
|
|
|
|
212
|
65
|
|
|
|
|
127
|
return ($type, @args); |
213
|
|
|
|
|
|
|
} |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
sub find { |
216
|
46
|
|
|
46
|
1
|
65
|
my $self = shift; |
217
|
46
|
|
|
|
|
59
|
my $type = shift; |
218
|
46
|
|
|
|
|
95
|
my $bases = $self->path; |
219
|
46
|
|
|
|
|
52
|
my $module; |
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
# run the type through the type map to handle any unusual capitalisation, |
222
|
|
|
|
|
|
|
# spelling, aliases, etc. |
223
|
46
|
|
66
|
|
|
141
|
$type = $self->{ names }->{ $type } || $type; |
224
|
|
|
|
|
|
|
|
225
|
46
|
|
|
|
|
73
|
foreach my $base (@$bases) { |
226
|
54
|
100
|
|
|
|
139
|
return $module |
227
|
|
|
|
|
|
|
if $module = $self->load( $self->module_names($base, $type) ); |
228
|
|
|
|
|
|
|
} |
229
|
|
|
|
|
|
|
|
230
|
2
|
|
|
|
|
11
|
return undef; |
231
|
|
|
|
|
|
|
} |
232
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
sub load { |
234
|
54
|
|
|
54
|
1
|
70
|
my $self = shift; |
235
|
54
|
|
|
|
|
76
|
my $loaded = $self->{ loaded }; |
236
|
|
|
|
|
|
|
|
237
|
54
|
|
|
|
|
101
|
foreach my $module (@_) { |
238
|
|
|
|
|
|
|
# see if we've previously loaded a module with this name (true |
239
|
|
|
|
|
|
|
# value) or failed to load a module (defined but false value) |
240
|
|
|
|
|
|
|
|
241
|
64
|
100
|
|
|
|
182
|
if ($loaded->{ $module }) { |
|
|
50
|
|
|
|
|
|
242
|
12
|
|
|
|
|
15
|
$self->debug("$module has been previously loaded") if DEBUG; |
243
|
12
|
|
|
|
|
52
|
return $module; |
244
|
|
|
|
|
|
|
} |
245
|
|
|
|
|
|
|
elsif (defined $loaded->{ $module }) { |
246
|
0
|
|
|
|
|
0
|
next; |
247
|
|
|
|
|
|
|
} |
248
|
|
|
|
|
|
|
|
249
|
24
|
|
|
24
|
|
188
|
no strict REFS; |
|
24
|
|
|
|
|
43
|
|
|
24
|
|
|
|
|
22721
|
|
250
|
52
|
|
|
|
|
50
|
$self->debug("attempting to load $module") if DEBUG; |
251
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
# Some filesystems are case-insensitive (like Apple's HFS), so an |
253
|
|
|
|
|
|
|
# attempt to load Badger::Example::foo may succeed, when the correct |
254
|
|
|
|
|
|
|
# package name is actually Badger::Example::Foo. We double-check |
255
|
|
|
|
|
|
|
# by looking for $VERSION or @ISA. This is a bit dodgy because we might be |
256
|
|
|
|
|
|
|
# loading something that has no ISA. Need to cross-check with |
257
|
|
|
|
|
|
|
# what's going on in Badger::Class _autoload() |
258
|
|
|
|
|
|
|
|
259
|
52
|
|
|
|
|
56
|
my $loadname; |
260
|
52
|
100
|
|
|
|
120
|
if ( ($loadname = class($module)->maybe_load) ) { |
261
|
32
|
|
|
|
|
35
|
$self->debug("loaded $module") if DEBUG; |
262
|
32
|
|
|
|
|
52
|
$loaded->{ $module } = $loadname; |
263
|
32
|
|
|
|
|
229
|
return $module |
264
|
|
|
|
|
|
|
} |
265
|
|
|
|
|
|
|
|
266
|
20
|
|
|
|
|
35
|
$self->debug("failed to load $module") if DEBUG; |
267
|
|
|
|
|
|
|
} |
268
|
|
|
|
|
|
|
|
269
|
10
|
|
|
|
|
24
|
return undef; |
270
|
|
|
|
|
|
|
} |
271
|
|
|
|
|
|
|
|
272
|
|
|
|
|
|
|
|
273
|
|
|
|
|
|
|
sub found { |
274
|
83
|
|
|
83
|
1
|
1584
|
my ($self, $type, $item, $args) = @_; |
275
|
|
|
|
|
|
|
|
276
|
83
|
100
|
|
|
|
190
|
if (ref $item) { |
277
|
|
|
|
|
|
|
# if it's a reference we found then forward it onto the appropriate |
278
|
|
|
|
|
|
|
# method, e.g found_array(), found_hash(), found_code(). Fall back |
279
|
|
|
|
|
|
|
# on found_ref() |
280
|
29
|
100
|
|
|
|
105
|
my $iref = blessed($item) |
281
|
|
|
|
|
|
|
? OBJECT |
282
|
|
|
|
|
|
|
: lc ref $item; |
283
|
|
|
|
|
|
|
|
284
|
29
|
|
|
|
|
104
|
$self->debug( |
285
|
|
|
|
|
|
|
"Looking for handler methods: ", |
286
|
|
|
|
|
|
|
FOUND,'_'.$iref, "() or ", |
287
|
|
|
|
|
|
|
FOUND_REF, "()" |
288
|
|
|
|
|
|
|
) if DEBUG; |
289
|
|
|
|
|
|
|
|
290
|
|
|
|
|
|
|
my $method |
291
|
|
|
|
|
|
|
= $self->can(FOUND . '_' . $iref) |
292
|
|
|
|
|
|
|
|| $self->can(FOUND_REF) |
293
|
29
|
|
50
|
|
|
121
|
|| return $self->error_msg( bad_ref => $self->{ item }, $type, $iref ); |
294
|
|
|
|
|
|
|
|
295
|
29
|
|
|
|
|
80
|
$item = $method->($self, $type, $item, $args); |
296
|
|
|
|
|
|
|
} |
297
|
|
|
|
|
|
|
else { |
298
|
|
|
|
|
|
|
# otherwise it's the name of a module |
299
|
54
|
|
|
|
|
179
|
$item = $self->found_module($type, $item, $args); |
300
|
|
|
|
|
|
|
} |
301
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
# NOTE: an item can be defined but false, e.g. a Template::Plugin which |
303
|
|
|
|
|
|
|
# return '' from its new() method to indicate it does nothing objecty |
304
|
83
|
50
|
|
|
|
154
|
return unless defined $item; |
305
|
|
|
|
|
|
|
|
306
|
83
|
|
|
|
|
77
|
$self->debug("Found result: $type => $item") if DEBUG; |
307
|
|
|
|
|
|
|
|
308
|
|
|
|
|
|
|
# TODO: what about caching result? Do we always leave that to subclasses? |
309
|
83
|
|
|
|
|
214
|
return $self->result($type, $item, $args); |
310
|
|
|
|
|
|
|
} |
311
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
sub found_module { |
313
|
|
|
|
|
|
|
# This method is called when a module name is found, either by being |
314
|
|
|
|
|
|
|
# predefined in the factory entry table, or loaded on demand from disk. |
315
|
|
|
|
|
|
|
# It ensures the module is loaded and and instantiates an object from the |
316
|
|
|
|
|
|
|
# class name |
317
|
54
|
|
|
54
|
1
|
89
|
my ($self, $type, $module, $args) = @_; |
318
|
54
|
|
|
|
|
58
|
$self->debug("Found module: $type => $module") if DEBUG; |
319
|
54
|
|
66
|
|
|
178
|
$self->{ loaded }->{ $module } ||= class($module)->load; |
320
|
54
|
|
|
|
|
162
|
return $self->construct($type, $module, $args); |
321
|
|
|
|
|
|
|
} |
322
|
|
|
|
|
|
|
|
323
|
|
|
|
|
|
|
sub found_array { |
324
|
|
|
|
|
|
|
# This method is called when an ARRAY reference is found. We assume that |
325
|
|
|
|
|
|
|
# the first item is the module name (which needs to be loaded) and the |
326
|
|
|
|
|
|
|
# second item is the class name (which needs to be instantiated). |
327
|
4
|
|
|
4
|
1
|
10
|
my ($self, $type, $item, $args) = @_; |
328
|
4
|
|
|
|
|
10
|
my ($module, $class) = @$item; |
329
|
4
|
|
66
|
|
|
30
|
$self->{ loaded }->{ $module } ||= class($module)->load; |
330
|
4
|
|
|
|
|
27
|
return $self->construct($type, $class, $args); |
331
|
|
|
|
|
|
|
} |
332
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
sub not_found { |
334
|
2
|
|
|
2
|
1
|
6
|
my ($self, $type, @args) = @_; |
335
|
|
|
|
|
|
|
|
336
|
|
|
|
|
|
|
return $type eq DEFAULT |
337
|
|
|
|
|
|
|
? $self->error_msg( no_default => $self->{ item } ) |
338
|
2
|
100
|
|
|
|
13
|
: $self->error_msg( not_found => $self->{ item }, $type ); |
339
|
|
|
|
|
|
|
} |
340
|
|
|
|
|
|
|
|
341
|
|
|
|
|
|
|
sub construct { |
342
|
59
|
|
|
59
|
1
|
115
|
my ($self, $type, $class, $args) = @_; |
343
|
59
|
|
|
|
|
53
|
$self->debug("constructing class: $type => $class") if DEBUG; |
344
|
59
|
|
|
|
|
244
|
return $class->new(@$args); |
345
|
|
|
|
|
|
|
} |
346
|
|
|
|
|
|
|
|
347
|
|
|
|
|
|
|
sub module_names { |
348
|
54
|
|
|
54
|
1
|
58
|
my $self = shift; |
349
|
|
|
|
|
|
|
my @bits = |
350
|
119
|
|
|
|
|
210
|
map { camel_case($_) } |
351
|
54
|
|
|
|
|
88
|
map { split /[\.]+/ } @_; |
|
108
|
|
|
|
|
244
|
|
352
|
|
|
|
|
|
|
|
353
|
|
|
|
|
|
|
return ( |
354
|
54
|
|
|
|
|
90
|
join( PKG, map { ucfirst $_ } @bits ), |
|
119
|
|
|
|
|
350
|
|
355
|
|
|
|
|
|
|
join( PKG, @bits ) |
356
|
|
|
|
|
|
|
); |
357
|
|
|
|
|
|
|
} |
358
|
|
|
|
|
|
|
|
359
|
|
|
|
|
|
|
|
360
|
|
|
|
|
|
|
sub can { |
361
|
35
|
|
|
35
|
1
|
71
|
my ($self, $name) = @_; |
362
|
|
|
|
|
|
|
|
363
|
|
|
|
|
|
|
# upgrade class methods to calls on prototype |
364
|
35
|
50
|
|
|
|
78
|
$self = $self->prototype unless ref $self; |
365
|
|
|
|
|
|
|
|
366
|
|
|
|
|
|
|
# NOTE: this method can get called before we've called init_factory() |
367
|
|
|
|
|
|
|
# to define the item/items members, so we tread carefully. |
368
|
35
|
50
|
33
|
|
|
237
|
if ($self->{ item } && $self->{ item } eq $name) { |
|
|
50
|
33
|
|
|
|
|
369
|
0
|
|
|
|
|
0
|
return $self->SUPER::can('item'); |
370
|
|
|
|
|
|
|
} |
371
|
|
|
|
|
|
|
elsif ($self->{ items } && $self->{ items } eq $name) { |
372
|
0
|
|
|
|
|
0
|
return $self->SUPER::can('items'); |
373
|
|
|
|
|
|
|
} |
374
|
|
|
|
|
|
|
else { |
375
|
35
|
|
|
|
|
237
|
return $self->SUPER::can($name); |
376
|
|
|
|
|
|
|
} |
377
|
|
|
|
|
|
|
} |
378
|
|
|
|
|
|
|
|
379
|
|
|
|
|
|
|
sub result { |
380
|
33
|
|
|
33
|
1
|
113
|
$_[2]; |
381
|
|
|
|
|
|
|
} |
382
|
|
|
|
|
|
|
|
383
|
|
|
|
|
|
|
sub AUTOLOAD { |
384
|
12
|
|
|
12
|
|
82
|
my ($self, @args) = @_; |
385
|
12
|
|
|
|
|
76
|
my ($name) = ($AUTOLOAD =~ /([^:]+)$/ ); |
386
|
12
|
100
|
|
|
|
255
|
return if $name eq 'DESTROY'; |
387
|
|
|
|
|
|
|
|
388
|
7
|
|
|
|
|
9
|
$self->debug("AUTOLOAD $name") if DEBUG; |
389
|
|
|
|
|
|
|
|
390
|
7
|
|
|
|
|
19
|
local $RUNAWAY = $RUNAWAY; |
391
|
7
|
50
|
|
|
|
23
|
$self->error("AUTOLOAD went runaway on $name") |
392
|
|
|
|
|
|
|
if ++$RUNAWAY > 10; |
393
|
|
|
|
|
|
|
|
394
|
|
|
|
|
|
|
# upgrade class methods to calls on prototype |
395
|
7
|
100
|
|
|
|
29
|
$self = $self->prototype unless ref $self; |
396
|
|
|
|
|
|
|
|
397
|
7
|
|
|
|
|
10
|
$self->debug("factory item: $self->{ item }") if DEBUG; |
398
|
|
|
|
|
|
|
|
399
|
7
|
100
|
|
|
|
23
|
if ($name eq $self->{ item }) { |
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
400
|
6
|
|
|
|
|
18
|
$self->class->method( $name => $self->can('item') ); |
401
|
|
|
|
|
|
|
} |
402
|
|
|
|
|
|
|
elsif ($name eq $self->{ items }) { |
403
|
0
|
|
|
|
|
0
|
$self->class->method( $name => $self->can('items') ) |
404
|
|
|
|
|
|
|
} |
405
|
|
|
|
|
|
|
elsif (my $item = $self->try( item => $name, @args )) { |
406
|
0
|
|
|
|
|
0
|
return $item; |
407
|
|
|
|
|
|
|
} |
408
|
|
|
|
|
|
|
else { |
409
|
1
|
|
|
|
|
3
|
my ($pkg, $file, $line) = caller; |
410
|
1
|
|
33
|
|
|
4
|
my $class = ref $self || $self; |
411
|
1
|
|
|
|
|
4
|
die $self->message( bad_method => $name, $class, $file, $line ), "\n"; |
412
|
|
|
|
|
|
|
} |
413
|
|
|
|
|
|
|
|
414
|
|
|
|
|
|
|
# should be installed now |
415
|
6
|
|
|
|
|
20
|
$self->$name(@args); |
416
|
|
|
|
|
|
|
} |
417
|
|
|
|
|
|
|
|
418
|
|
|
|
|
|
|
|
419
|
|
|
|
|
|
|
1; |
420
|
|
|
|
|
|
|
|
421
|
|
|
|
|
|
|
__END__ |