line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Bolts; |
2
|
|
|
|
|
|
|
$Bolts::VERSION = '0.143171'; |
3
|
|
|
|
|
|
|
# ABSTRACT: An Inversion of Control framework for Perl |
4
|
|
|
|
|
|
|
|
5
|
11
|
|
|
11
|
|
853869
|
use Moose (); |
|
11
|
|
|
|
|
2380065
|
|
|
11
|
|
|
|
|
306
|
|
6
|
11
|
|
|
11
|
|
68
|
use Moose::Exporter; |
|
11
|
|
|
|
|
45
|
|
|
11
|
|
|
|
|
71
|
|
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
# Register attribute traits |
9
|
11
|
|
|
11
|
|
5715
|
use Bolts::Meta::Attribute::Trait::Initializer; |
|
11
|
|
|
|
|
86
|
|
|
11
|
|
|
|
|
284
|
|
10
|
|
|
|
|
|
|
|
11
|
11
|
|
|
11
|
|
54
|
use Class::Load (); |
|
11
|
|
|
|
|
11
|
|
|
11
|
|
|
|
|
135
|
|
12
|
11
|
|
|
11
|
|
40
|
use Moose::Util::MetaRole (); |
|
11
|
|
|
|
|
11
|
|
|
11
|
|
|
|
|
116
|
|
13
|
11
|
|
|
11
|
|
34
|
use Moose::Util::TypeConstraints (); |
|
11
|
|
|
|
|
12
|
|
|
11
|
|
|
|
|
140
|
|
14
|
11
|
|
|
11
|
|
38
|
use Safe::Isa; |
|
11
|
|
|
|
|
21
|
|
|
11
|
|
|
|
|
959
|
|
15
|
11
|
|
|
11
|
|
52
|
use Scalar::Util (); |
|
11
|
|
|
|
|
12
|
|
|
11
|
|
|
|
|
132
|
|
16
|
11
|
|
|
11
|
|
40
|
use Carp (); |
|
11
|
|
|
|
|
14
|
|
|
11
|
|
|
|
|
163
|
|
17
|
|
|
|
|
|
|
|
18
|
11
|
|
|
11
|
|
3483
|
use Bolts::Util qw( locator_for ); |
|
11
|
|
|
|
|
25
|
|
|
11
|
|
|
|
|
52
|
|
19
|
11
|
|
|
11
|
|
9044
|
use Bolts::Blueprint::Given; |
|
11
|
|
|
|
|
2782
|
|
|
11
|
|
|
|
|
351
|
|
20
|
11
|
|
|
11
|
|
4491
|
use Bolts::Blueprint::Literal; |
|
11
|
|
|
|
|
2641
|
|
|
11
|
|
|
|
|
326
|
|
21
|
11
|
|
|
11
|
|
4136
|
use Bolts::Blueprint::Built; |
|
11
|
|
|
|
|
3000
|
|
|
11
|
|
|
|
|
386
|
|
22
|
|
|
|
|
|
|
|
23
|
11
|
|
|
11
|
|
76
|
use Safe::Isa; |
|
11
|
|
|
|
|
16
|
|
|
11
|
|
|
|
|
9626
|
|
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
our @CARP_NOT = qw( Moose::Exporter ); |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
# Ugly, but so far... necessary... |
28
|
|
|
|
|
|
|
our $GLOBAL_FALLBACK_META_LOCATOR = 'Bolts::Meta::Locator'; |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
my @BAG_META; |
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
Moose::Exporter->setup_import_methods( |
33
|
|
|
|
|
|
|
class_metaroles => { |
34
|
|
|
|
|
|
|
class => [ |
35
|
|
|
|
|
|
|
'Bolts::Meta::Class::Trait::Locator', |
36
|
|
|
|
|
|
|
'Bolts::Meta::Class::Trait::Bag', |
37
|
|
|
|
|
|
|
], |
38
|
|
|
|
|
|
|
}, |
39
|
|
|
|
|
|
|
base_class_roles => [ 'Bolts::Role::SelfLocator' ], |
40
|
|
|
|
|
|
|
with_meta => [ qw( |
41
|
|
|
|
|
|
|
artifact bag builder contains dep option self such_that_each value |
42
|
|
|
|
|
|
|
) ], |
43
|
|
|
|
|
|
|
also => 'Moose', |
44
|
|
|
|
|
|
|
); |
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
sub init_meta { |
47
|
12
|
|
|
12
|
0
|
6824
|
my $class = shift; |
48
|
12
|
|
|
|
|
57
|
my $meta = Moose->init_meta(@_); |
49
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
$meta->add_attribute(__top => ( |
51
|
|
|
|
|
|
|
reader => '__top', |
52
|
|
|
|
|
|
|
required => 1, |
53
|
22
|
|
|
22
|
|
185
|
default => sub { shift }, |
54
|
12
|
|
|
|
|
43385
|
lazy => 1, |
55
|
|
|
|
|
|
|
weak_ref => 1, |
56
|
|
|
|
|
|
|
)); |
57
|
|
|
|
|
|
|
|
58
|
12
|
|
|
|
|
25078
|
return $meta; |
59
|
|
|
|
|
|
|
} |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
sub _bag_meta { |
62
|
60
|
|
|
60
|
|
85
|
my ($meta) = @_; |
63
|
|
|
|
|
|
|
|
64
|
60
|
100
|
|
|
|
159
|
$meta = $BAG_META[-1] if @BAG_META; |
65
|
|
|
|
|
|
|
|
66
|
60
|
|
|
|
|
100
|
return $meta; |
67
|
|
|
|
|
|
|
} |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
sub artifact { |
71
|
45
|
|
|
45
|
1
|
18866
|
my $meta = _bag_meta(shift); |
72
|
45
|
|
|
|
|
173
|
my $artifact = Bolts::Util::artifact($meta, @_); |
73
|
45
|
|
|
|
|
741
|
$meta->add_artifact(%$artifact); |
74
|
45
|
|
|
|
|
2574
|
return; |
75
|
|
|
|
|
|
|
} |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
our @BAG_OF_BUILDING; |
79
|
|
|
|
|
|
|
sub bag { |
80
|
2
|
|
|
2
|
1
|
18
|
my ($meta, $name, $partial_def) = @_; |
81
|
|
|
|
|
|
|
|
82
|
2
|
|
|
|
|
10
|
$meta = _bag_meta($meta); |
83
|
|
|
|
|
|
|
|
84
|
2
|
|
|
|
|
6
|
my $def = $partial_def->($name); |
85
|
|
|
|
|
|
|
$meta->add_artifact( |
86
|
|
|
|
|
|
|
$name => Bolts::Artifact::Thunk->new( |
87
|
|
|
|
|
|
|
thunk => sub { |
88
|
15
|
|
|
15
|
|
29
|
my ($self, $bag, $name, %params) = @_; |
89
|
15
|
|
|
|
|
407
|
return $def->name->new( |
90
|
|
|
|
|
|
|
__parent => $bag, |
91
|
|
|
|
|
|
|
%params, |
92
|
|
|
|
|
|
|
); |
93
|
|
|
|
|
|
|
}, |
94
|
|
|
|
|
|
|
) |
95
|
2
|
|
|
|
|
80
|
); |
96
|
|
|
|
|
|
|
} |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
sub contains(&;$) { |
99
|
2
|
|
|
2
|
0
|
32
|
my ($parent_meta, $code, $such_that_each) = @_; |
100
|
|
|
|
|
|
|
|
101
|
2
|
|
|
|
|
6
|
my $meta = _bag_meta($parent_meta); |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
return sub { |
104
|
2
|
|
|
2
|
|
5
|
my ($name) = shift; |
105
|
|
|
|
|
|
|
|
106
|
2
|
|
|
|
|
7
|
my $parent = $meta->name; |
107
|
|
|
|
|
|
|
|
108
|
2
|
100
|
|
|
|
20
|
my $bag_meta = Bolts::Bag->start_bag( |
109
|
|
|
|
|
|
|
package => "${parent}::$name", |
110
|
|
|
|
|
|
|
($such_that_each ? (such_that_each => $such_that_each) : ()), |
111
|
|
|
|
|
|
|
); |
112
|
2
|
|
|
|
|
4
|
push @BAG_META, $bag_meta; |
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
$bag_meta->add_attribute(__parent => ( |
115
|
|
|
|
|
|
|
reader => '__parent', |
116
|
|
|
|
|
|
|
required => 1, |
117
|
0
|
|
|
|
|
0
|
default => sub { Carp::confess('why are we here?') }, |
118
|
2
|
|
|
|
|
20
|
weak_ref => 1, |
119
|
|
|
|
|
|
|
)); |
120
|
|
|
|
|
|
|
|
121
|
2
|
|
|
|
|
4649
|
$bag_meta->add_artifact( |
122
|
|
|
|
|
|
|
__top => Bolts::Artifact->new( |
123
|
|
|
|
|
|
|
meta_locator => $bag_meta, |
124
|
|
|
|
|
|
|
name => '__top', |
125
|
|
|
|
|
|
|
blueprint => $bag_meta->acquire('blueprint', 'acquired', { |
126
|
|
|
|
|
|
|
path => [ '__parent', '__top' ], |
127
|
|
|
|
|
|
|
}), |
128
|
|
|
|
|
|
|
scope => $bag_meta->acquire('scope', 'prototype'), |
129
|
|
|
|
|
|
|
) |
130
|
|
|
|
|
|
|
); |
131
|
|
|
|
|
|
|
|
132
|
2
|
|
|
|
|
81
|
$code->($bag_meta); |
133
|
|
|
|
|
|
|
|
134
|
2
|
|
|
|
|
8
|
pop @BAG_META; |
135
|
|
|
|
|
|
|
|
136
|
2
|
|
|
|
|
10
|
$bag_meta->finish_bag; |
137
|
|
|
|
|
|
|
|
138
|
2
|
|
|
|
|
6
|
return $bag_meta; |
139
|
2
|
|
|
|
|
20
|
}; |
140
|
|
|
|
|
|
|
} |
141
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
sub such_that_each($) { |
144
|
1
|
|
|
1
|
1
|
19
|
my ($meta, $params) = @_; |
145
|
1
|
|
|
|
|
5
|
return $params; |
146
|
|
|
|
|
|
|
} |
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
sub builder(&) { |
150
|
2
|
|
|
2
|
1
|
29
|
my ($meta, $code) = @_; |
151
|
2
|
|
|
|
|
7
|
$meta = _bag_meta($meta); |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
return { |
154
|
2
|
|
|
|
|
12
|
blueprint => $meta->acquire('blueprint', 'built_injector', { |
155
|
|
|
|
|
|
|
builder => $code, |
156
|
|
|
|
|
|
|
}), |
157
|
|
|
|
|
|
|
}; |
158
|
|
|
|
|
|
|
} |
159
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
sub dep($) { |
162
|
9
|
|
|
9
|
1
|
132
|
my ($meta, $path) = @_; |
163
|
9
|
|
|
|
|
24
|
$meta = _bag_meta($meta); |
164
|
|
|
|
|
|
|
|
165
|
9
|
100
|
|
|
|
31
|
$path = [ $path ] unless ref $path eq 'ARRAY'; |
166
|
|
|
|
|
|
|
|
167
|
9
|
|
|
|
|
23
|
my @path = ('__top', @$path); |
168
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
return { |
170
|
9
|
|
|
|
|
43
|
blueprint => $meta->acquire('blueprint', 'acquired', { |
171
|
|
|
|
|
|
|
path => \@path, |
172
|
|
|
|
|
|
|
}), |
173
|
|
|
|
|
|
|
}; |
174
|
|
|
|
|
|
|
} |
175
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
sub option($) { |
178
|
8
|
|
|
8
|
1
|
1391
|
my ($meta, $p) = @_; |
179
|
|
|
|
|
|
|
|
180
|
8
|
|
|
|
|
24
|
my %bp = %$p; |
181
|
8
|
|
|
|
|
11
|
my %ip; |
182
|
8
|
|
|
|
|
13
|
for my $k (qw( isa does )) { |
183
|
16
|
100
|
|
|
|
56
|
$ip{$k} = delete $bp{$k} if exists $bp{$k}; |
184
|
|
|
|
|
|
|
} |
185
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
return { |
187
|
8
|
|
|
|
|
37
|
%ip, |
188
|
|
|
|
|
|
|
blueprint => $meta->acquire('blueprint', 'given', \%bp), |
189
|
|
|
|
|
|
|
}, |
190
|
|
|
|
|
|
|
} |
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
sub value($) { |
194
|
18
|
|
|
18
|
1
|
205
|
my ($meta, $value) = @_; |
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
return { |
197
|
18
|
|
|
|
|
84
|
blueprint => $meta->acquire('blueprint', 'literal', { |
198
|
|
|
|
|
|
|
value => $value, |
199
|
|
|
|
|
|
|
}), |
200
|
|
|
|
|
|
|
}; |
201
|
|
|
|
|
|
|
} |
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
sub self() { |
205
|
1
|
|
|
1
|
1
|
64
|
my ($meta, $value) = @_; |
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
return { |
208
|
1
|
|
|
|
|
6
|
blueprint => $meta->acquire('blueprint', 'parent_bag'), |
209
|
|
|
|
|
|
|
}; |
210
|
|
|
|
|
|
|
} |
211
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
1; |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
__END__ |
216
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
=pod |
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
=encoding UTF-8 |
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
=head1 NAME |
222
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
Bolts - An Inversion of Control framework for Perl |
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
=head1 VERSION |
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
version 0.143171 |
228
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
=head1 SYNOPSIS |
230
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
package MyApp; |
232
|
|
|
|
|
|
|
use Bolts; |
233
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
artifcat log_file => 'var/messages.log'; |
235
|
|
|
|
|
|
|
artifact logger => ( |
236
|
|
|
|
|
|
|
class => 'MyApp::Logger', |
237
|
|
|
|
|
|
|
scope => 'singleton', |
238
|
|
|
|
|
|
|
infer => 'acquisition', |
239
|
|
|
|
|
|
|
); |
240
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
# Later... |
242
|
|
|
|
|
|
|
my $log = $app->acquire('logger'); |
243
|
|
|
|
|
|
|
$log->error("Bad stuff."); |
244
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
=head1 DESCRIPTION |
246
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
B<Caution:> I<< This is an B<experimental> API. Some aspects of the API may change, possibly drastically, from version to version. That probably won't happen, but please contact me via email if you plan to use this in something and want to know what might change. Pay close attention to any B<Caution> remarks in the documentation. >> |
248
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
This is yet another Inversion of Control framework for Perl. This one is based upon a combination of L<Bread::Board>, concepts from the Spring framework, and a good mix of my own ideas and modifications after spending a few years using L<Moose> and Bread::Board. |
250
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
=head2 Inversion of Control |
252
|
|
|
|
|
|
|
|
253
|
|
|
|
|
|
|
For those who might not know what Inversion of Control (IOC) is, it is a design pattern aimed at helping you decouple your code, automate parts of the configuration assembly, and manage the life cycle of your objects. |
254
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
By using an IOC framework, the objects in your program need to know less about the other objects in your application. Your objects can focus on knowing what it needs from other objects without knowing where to find objects that do that or how they are configured. |
256
|
|
|
|
|
|
|
|
257
|
|
|
|
|
|
|
For example, early in a program's lifetime, the logger might be a local object that writes directly to a file. Later, it might be an object with the same interface, but it writes to syslog. Further on, it might be some sort of logging service that is accessed over the network through a stub provided by a service locator. If your program uses an IOC framework, the configuration files for your IOC will change to pass a different object to the application during each phase, but the program itself might not change at all. |
258
|
|
|
|
|
|
|
|
259
|
|
|
|
|
|
|
An IOC framework also helps you assemble complex configuration related to your application. It can join various configurations together in interesting and complex ways automatically. |
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
An IOC framework can make sure that your objects live only as long as they should or longer than they would normally. It can manage the list of objects that should be created each time (prototypes), objects that should last as long as a user session, objects that should last as long as some request, and objects that last for the duration of the process (singletons). |
262
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
The next sections will introduce the concepts and terminology used by this framework. |
264
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
=head2 Artifacts |
266
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
The basic building block of the Bolts IOC framework is the B<artifact>. At the simplest level, an artifact is any kind of thing your program might use. It might be a value, it might be a reference to something, it might be an object, or it might be something more complex. |
268
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
For simple values and direct references to things, you can treat any thing as an artifact. However, the real power starts when you use an implementation of L<Bolts::Role::Artifact>, usually L<Bolts::Artifact> to manage that thing. These provide utilities for constructing an object or other value according to some set of instructions and directions for managing the lifecycle of the artifact in question. |
270
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
=head2 Bags |
272
|
|
|
|
|
|
|
|
273
|
|
|
|
|
|
|
Artifacts are grouped into bags. A B<bag> can be any object, hash reference, or array reference. Artifacts are associated with the bag as indexes in the array, keys on the hash, or methods on the object. Literally, any object can be used as a bag, which differs from frameworks like L<Bread::Board>, which requires that its services be put inside a special container object. Bolts just uses hashes, arrays, and objects in the usual Perl way to locate artifacts. |
274
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
=head2 Locators |
276
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
A B<locator> is an object that finds things in a bag (these are things related to L<Bolts::Role::Locator>. The finding process is called, B<acquisition>. (If you are familiar with Harry Potter, this process is similar to Harry Potter using a wand to extract dittany from Hermione's handbag by saying "Accio Dittany.") After finding the object, the locator performs B<resolution>, which checks to see if the returned artifact needs to be resolved further. (To continue the analogy, this is like unbottling the dittany and pouring it out, there may be another step before the artifact is completely ready for use.) |
278
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
=head2 Blueprints |
280
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
Attached to L<Bolts::Artifact> definitions are a set of blueprints (some object that implements L<Bolts::Blueprint>). A B<blueprint> describes how an artifact is located or constructed, which is part of resolution. The system provides standard blueprints that can cover all possible needs, but you can create your own to extend the framework as necessary. The built-in blueprints can locate an object by acquring it from a bag, the result of a subroutine, by use of a factory method or constructor on an object, by directly handing the value in to the bag when the bag is constructed, or set as a constant. |
282
|
|
|
|
|
|
|
|
283
|
|
|
|
|
|
|
=head2 Injectors |
284
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
Another step in resolution is injection. An B<injector> associates additional artifacts with the artifact being resolved. This might be values that need to be passed to the artifact during construction, methods that need to be called to configure the object following construction, or even keys on a hash that need to be set on the artifact. |
286
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
Injectors come in two flavors, injection by automatic acquisition and by given options. With acquisition, the framework will acquire or other provide addition artifacts to the artifact being resolved automatically, this is where much of the power of IOC comes from. Sometimes, however, an object just requires some configuration state to let it know how it will be used. In those cases, options can be directly passed to C<acquire> to be used for injection. |
288
|
|
|
|
|
|
|
|
289
|
|
|
|
|
|
|
=head2 Scope |
290
|
|
|
|
|
|
|
|
291
|
|
|
|
|
|
|
The B<scope> of an artifact determines during what period an artifact is valid. Bolts provides two built-in scopes, prototype and singleton. A prototype represents an artifact that must be resolved every time it is acquired. A singleton represents an artifact that is resolved only the first time it is acquired and is then reused for all following acquisitions for the lifetime of the bag. |
292
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
=head2 Infererer |
294
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
It is usually considered a bad thing in computer science if you have to configure something twice in the same program. Such duplication is tedious and leads to technical debt. This, unfortunately, is often the case when using some IOC frameworks. You configure the object once using Moose and then a second time to let the IOC framework know how to inject configuration into the artifact. This is where the inferers come in. |
296
|
|
|
|
|
|
|
|
297
|
|
|
|
|
|
|
An B<inferer> is a tool that can inspect an object and automatically decide how that object should be configured. Bolts provides an inferer for L<Moose> that can use the metadata about a L<Moose> object to determine how to inject into that object automatically. |
298
|
|
|
|
|
|
|
|
299
|
|
|
|
|
|
|
=head2 Meta Locator and Extension |
300
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
One of the goals of this system is to have the system rely on the IOC internally as much as possible and to decouple the components as much as possible. This goal has not been totally achieved, but it is something strived for. The framework itself depends on L<Bolts::Meta::Locator>, which provides all the standard definitions internally. This can be extended to provide additional or even alternate features. |
302
|
|
|
|
|
|
|
|
303
|
|
|
|
|
|
|
All the various components: artifact, bag, locator, blueprint, injector, scope, and inferer are completely extensible. You can create new versions of L<Bolts::Role::Artifact>. You can create bags from almost anything. You can create new locators via L<Bolts::Role::Locator>. You can create new blueprints via L<Bolts::Blueprint>. You can create new scopes via L<Bolts::Scope>. You can create new inferers via L<Bolts::Inferer>. You can then associate these components with the internals using L<Bolts::Meta::Locator>. |
304
|
|
|
|
|
|
|
|
305
|
|
|
|
|
|
|
=head1 THIS CLASS |
306
|
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
The purpose of the Bolts module itself is to provide some nice syntactic sugar for turning the class that uses it into a bag and locator. |
308
|
|
|
|
|
|
|
|
309
|
|
|
|
|
|
|
=head1 FUNCTIONS |
310
|
|
|
|
|
|
|
|
311
|
|
|
|
|
|
|
=head2 artifact |
312
|
|
|
|
|
|
|
|
313
|
|
|
|
|
|
|
artifact 'name'; |
314
|
|
|
|
|
|
|
artifact name => $value; |
315
|
|
|
|
|
|
|
artifact name => %options; |
316
|
|
|
|
|
|
|
artifact name => $artifact; |
317
|
|
|
|
|
|
|
|
318
|
|
|
|
|
|
|
This defines an artifact in the current class. This will create a method on the current object with the given "name". If only the name is given, then the artifact to use must be passed when the bag is constructed. |
319
|
|
|
|
|
|
|
|
320
|
|
|
|
|
|
|
# for example, if you bag is named "MyApp" |
321
|
|
|
|
|
|
|
my $bag = MyApp->new( name => 42 ); |
322
|
|
|
|
|
|
|
my $value = $bag->acquire('name'); |
323
|
|
|
|
|
|
|
say $value; # 42 |
324
|
|
|
|
|
|
|
|
325
|
|
|
|
|
|
|
If a scalar or reference is passed in as a single argument in addition to the name, the artifact will be set to that literal value unless the value is an object implementing L<Bolts::Role::Artifact>. In that latter case, the object itself becomes the artifact used. |
326
|
|
|
|
|
|
|
|
327
|
|
|
|
|
|
|
Otherwise, you may pass in a list of pairs, which will be interpreted depending on the keys present. Here is a list of keys and their meanings: |
328
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
=over |
330
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
=item path |
332
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
This is like an alias to an artifact elsewhere within this bag or in another bag (if "locator" is passed as well). It is set to a reference to an array of names, naming the path within the bag to acquire. See L<Bolts::Blueprint::Acquired> for details. |
334
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
=item value |
336
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
This sets the artifact to a literal value, similar to passing C<$value> in the example above. See L<Bolts::Blueprint::Literal> for details. |
338
|
|
|
|
|
|
|
|
339
|
|
|
|
|
|
|
=item class |
340
|
|
|
|
|
|
|
|
341
|
|
|
|
|
|
|
This should be set to a package name. This causes the artifact to construct and return the value from a factory method or constructor. See L<Bolts::Blueprint::Factory> for details. |
342
|
|
|
|
|
|
|
|
343
|
|
|
|
|
|
|
=item builder |
344
|
|
|
|
|
|
|
|
345
|
|
|
|
|
|
|
This should be set to a subroutine. The subroutine will be called to attain this artifact and the return value used as the artifact. See L<Bolts::Blueprint::Builder> for details. |
346
|
|
|
|
|
|
|
|
347
|
|
|
|
|
|
|
=item blueprint |
348
|
|
|
|
|
|
|
|
349
|
|
|
|
|
|
|
This is set to the name of a L<Bolts::Blueprint> definition and allows you to specify the blueprint you wish to use directly. |
350
|
|
|
|
|
|
|
|
351
|
|
|
|
|
|
|
=item scope |
352
|
|
|
|
|
|
|
|
353
|
|
|
|
|
|
|
In addition to the options above, you may also specify the scope. This is usually either "prototype" or "singleton" and the default is generally "prototype". |
354
|
|
|
|
|
|
|
|
355
|
|
|
|
|
|
|
=back |
356
|
|
|
|
|
|
|
|
357
|
|
|
|
|
|
|
=head2 bag |
358
|
|
|
|
|
|
|
|
359
|
|
|
|
|
|
|
bag 'name' => contains { |
360
|
|
|
|
|
|
|
artifact 'child_name' => 42; |
361
|
|
|
|
|
|
|
}; |
362
|
|
|
|
|
|
|
|
363
|
|
|
|
|
|
|
Attaches a bag at the named location. This provides tools for assembling complex IOC configurations. |
364
|
|
|
|
|
|
|
|
365
|
|
|
|
|
|
|
=head2 such_that_each |
366
|
|
|
|
|
|
|
|
367
|
|
|
|
|
|
|
bag 'name' => contains { |
368
|
|
|
|
|
|
|
artifact 'child_name' => 'value'; |
369
|
|
|
|
|
|
|
|
370
|
|
|
|
|
|
|
} such_that_each { |
371
|
|
|
|
|
|
|
isa => 'Str', |
372
|
|
|
|
|
|
|
}; |
373
|
|
|
|
|
|
|
|
374
|
|
|
|
|
|
|
Causes every artifact within the bag to have the same type constraints, which is handy in some cases. The first argument is a hash that may contain an C<isa> key and a C<does> key, which will be applid to each of the artifacts within. The second argument is the bag definition, which should be built using C<contains> as shown in the description of L</bag>. |
375
|
|
|
|
|
|
|
|
376
|
|
|
|
|
|
|
=head2 builder |
377
|
|
|
|
|
|
|
|
378
|
|
|
|
|
|
|
artifact name => ( |
379
|
|
|
|
|
|
|
... |
380
|
|
|
|
|
|
|
parameters => { |
381
|
|
|
|
|
|
|
thing => builder { |
382
|
|
|
|
|
|
|
return MyApp::Thing->new, |
383
|
|
|
|
|
|
|
}, |
384
|
|
|
|
|
|
|
}, |
385
|
|
|
|
|
|
|
); |
386
|
|
|
|
|
|
|
|
387
|
|
|
|
|
|
|
This is a helper for setting a L<Bolts::Blueprint::BuiltInjector> for use in passing in a dependency that is wired directly to the builder function given. |
388
|
|
|
|
|
|
|
|
389
|
|
|
|
|
|
|
=head2 dep |
390
|
|
|
|
|
|
|
|
391
|
|
|
|
|
|
|
artifact other => ( ... ); |
392
|
|
|
|
|
|
|
|
393
|
|
|
|
|
|
|
artifact name => ( |
394
|
|
|
|
|
|
|
... |
395
|
|
|
|
|
|
|
parameters => { |
396
|
|
|
|
|
|
|
thing => dep('other'), |
397
|
|
|
|
|
|
|
}, |
398
|
|
|
|
|
|
|
); |
399
|
|
|
|
|
|
|
|
400
|
|
|
|
|
|
|
This is a helper for laoding dependencies from a path in the current bag (or a bag within it). |
401
|
|
|
|
|
|
|
|
402
|
|
|
|
|
|
|
=head2 option |
403
|
|
|
|
|
|
|
|
404
|
|
|
|
|
|
|
artifact name => ( |
405
|
|
|
|
|
|
|
... |
406
|
|
|
|
|
|
|
parameters => { |
407
|
|
|
|
|
|
|
thing => option { |
408
|
|
|
|
|
|
|
isa => 'MyApp::Thing', |
409
|
|
|
|
|
|
|
required => 1, |
410
|
|
|
|
|
|
|
}, |
411
|
|
|
|
|
|
|
other_thing => option { |
412
|
|
|
|
|
|
|
does => 'MyApp::OtherThing', |
413
|
|
|
|
|
|
|
}, |
414
|
|
|
|
|
|
|
}, |
415
|
|
|
|
|
|
|
); |
416
|
|
|
|
|
|
|
|
417
|
|
|
|
|
|
|
Helper to allow a dependency to be passed as a given option to the call to L<Bolts::Role::Locator/acquire>. To provide validators for the values pass, you may set the C<isa> and C<does> options to a L<Moose> type constraint. To make the option required, set the C<required> option. |
418
|
|
|
|
|
|
|
|
419
|
|
|
|
|
|
|
=head2 value |
420
|
|
|
|
|
|
|
|
421
|
|
|
|
|
|
|
artifact name => ( |
422
|
|
|
|
|
|
|
... |
423
|
|
|
|
|
|
|
parameters => { |
424
|
|
|
|
|
|
|
thing => value 42, |
425
|
|
|
|
|
|
|
}, |
426
|
|
|
|
|
|
|
); |
427
|
|
|
|
|
|
|
|
428
|
|
|
|
|
|
|
Helper that passes a literal value through as a dependency to the artifact |
429
|
|
|
|
|
|
|
during injection. |
430
|
|
|
|
|
|
|
|
431
|
|
|
|
|
|
|
=head2 self |
432
|
|
|
|
|
|
|
|
433
|
|
|
|
|
|
|
artifact thing => ( |
434
|
|
|
|
|
|
|
... |
435
|
|
|
|
|
|
|
parameters => { |
436
|
|
|
|
|
|
|
parent => self, |
437
|
|
|
|
|
|
|
}, |
438
|
|
|
|
|
|
|
); |
439
|
|
|
|
|
|
|
|
440
|
|
|
|
|
|
|
Sets up a blueprint to return the artifact's parent. |
441
|
|
|
|
|
|
|
|
442
|
|
|
|
|
|
|
=head1 GLOBALS |
443
|
|
|
|
|
|
|
|
444
|
|
|
|
|
|
|
=head2 $Bolts::GLOBAL_FALLBACK_META_LOCATOR |
445
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
B<Subject to Change:> This is the name of the locator to use for locating the meta objects needed to configure within Bolts. The default is L<Bolts::Meta::Locator>, which defines the standard set of scopes, blueprints, etc. |
447
|
|
|
|
|
|
|
|
448
|
|
|
|
|
|
|
This variable likely to change or disappear in the future. |
449
|
|
|
|
|
|
|
|
450
|
|
|
|
|
|
|
=for Pod::Coverage contains |
451
|
|
|
|
|
|
|
init_meta |
452
|
|
|
|
|
|
|
|
453
|
|
|
|
|
|
|
=head1 AUTHOR |
454
|
|
|
|
|
|
|
|
455
|
|
|
|
|
|
|
Andrew Sterling Hanenkamp <hanenkamp@cpan.org> |
456
|
|
|
|
|
|
|
|
457
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
458
|
|
|
|
|
|
|
|
459
|
|
|
|
|
|
|
This software is copyright (c) 2014 by Qubling Software LLC. |
460
|
|
|
|
|
|
|
|
461
|
|
|
|
|
|
|
This is free software; you can redistribute it and/or modify it under |
462
|
|
|
|
|
|
|
the same terms as the Perl 5 programming language system itself. |
463
|
|
|
|
|
|
|
|
464
|
|
|
|
|
|
|
=cut |