attributes can all be readonly
[catagits/Catalyst-Runtime.git] / lib / Catalyst / IOC / Container.pm
CommitLineData
a6c13ff4 1package Catalyst::IOC::Container;
b4a6fa62 2use Bread::Board;
3use Moose;
4use Config::Any;
5use Data::Visitor::Callback;
6use Catalyst::Utils ();
2bb0da6d 7use MooseX::Types::LoadableClass qw/ LoadableClass /;
a6c13ff4 8use Catalyst::IOC::BlockInjection;
8b749525 9use namespace::autoclean;
b4a6fa62 10
11extends 'Bread::Board::Container';
12
13has config_local_suffix => (
442ab13e 14 is => 'ro',
b4a6fa62 15 isa => 'Str',
16 default => 'local',
17);
18
19has driver => (
442ab13e 20 is => 'ro',
b4a6fa62 21 isa => 'HashRef',
22 default => sub { +{} },
23);
24
25has file => (
442ab13e 26 is => 'ro',
b4a6fa62 27 isa => 'Str',
28 default => '',
29);
30
31has substitutions => (
442ab13e 32 is => 'ro',
b4a6fa62 33 isa => 'HashRef',
34 default => sub { +{} },
35);
36
37has name => (
442ab13e 38 is => 'ro',
b4a6fa62 39 isa => 'Str',
40 default => 'TestApp',
41);
42
2bb0da6d 43has sub_container_class => (
44 isa => LoadableClass,
45 is => 'ro',
46 coerce => 1,
a6c13ff4 47 default => 'Catalyst::IOC::SubContainer',
8b749525 48 handles => {
49 new_sub_container => 'new',
50 }
2bb0da6d 51);
52
b4a6fa62 53sub BUILD {
54 my $self = shift;
55
292277c1 56 $self->add_service(
57 $self->${\"build_${_}_service"}
58 ) for qw/
7451d1ea 59 substitutions
60 file
61 driver
62 name
63 prefix
64 extensions
65 path
66 config
67 raw_config
68 global_files
69 local_files
70 global_config
71 local_config
72 config_local_suffix
73 config_path
74 /;
f04816ce 75
292277c1 76 $self->add_sub_container(
77 $self->${ \"build_${_}_subcontainer" }
78 ) for qw/ model view controller /;
f04816ce 79}
80
81sub build_model_subcontainer {
82 my $self = shift;
83
8b749525 84 return $self->new_sub_container( name => 'model' );
f04816ce 85}
86
87sub build_view_subcontainer {
88 my $self = shift;
89
8b749525 90 return $self->new_sub_container( name => 'view' );
f04816ce 91}
92
93sub build_controller_subcontainer {
94 my $self = shift;
95
8b749525 96 return $self->new_sub_container( name => 'controller' );
f04816ce 97}
98
f04816ce 99sub build_name_service {
100 my $self = shift;
292277c1 101
102 return Bread::Board::Literal->new( name => 'name', value => $self->name );
f04816ce 103}
104
105sub build_driver_service {
106 my $self = shift;
292277c1 107
108 return Bread::Board::Literal->new( name => 'driver', value => $self->driver );
f04816ce 109}
110
111sub build_file_service {
112 my $self = shift;
292277c1 113
114 return Bread::Board::Literal->new( name => 'file', value => $self->file );
f04816ce 115}
116
117sub build_substitutions_service {
118 my $self = shift;
292277c1 119
120 return Bread::Board::Literal->new( name => 'substitutions', value => $self->substitutions );
f04816ce 121}
122
123sub build_extensions_service {
124 my $self = shift;
292277c1 125
126 return Bread::Board::BlockInjection->new(
127 name => 'extensions',
128 block => sub {
129 return \@{Config::Any->extensions};
130 },
f04816ce 131 );
132}
b4a6fa62 133
f04816ce 134sub build_prefix_service {
135 my $self = shift;
292277c1 136
137 return Bread::Board::BlockInjection->new(
138 name => 'prefix',
139 block => sub {
140 return Catalyst::Utils::appprefix( shift->param('name') );
141 },
142 dependencies => [ depends_on('name') ],
f04816ce 143 );
144}
b4a6fa62 145
f04816ce 146sub build_path_service {
147 my $self = shift;
292277c1 148
149 return Bread::Board::BlockInjection->new(
150 name => 'path',
151 block => sub {
152 my $s = shift;
153
154 return Catalyst::Utils::env_value( $s->param('name'), 'CONFIG' )
155 || $s->param('file')
156 || $s->param('name')->path_to( $s->param('prefix') );
157 },
158 dependencies => [ depends_on('file'), depends_on('name'), depends_on('prefix') ],
f04816ce 159 );
160}
b4a6fa62 161
f04816ce 162sub build_config_service {
163 my $self = shift;
292277c1 164
165 return Bread::Board::BlockInjection->new(
166 name => 'config',
167 block => sub {
168 my $s = shift;
169
170 my $v = Data::Visitor::Callback->new(
171 plain_value => sub {
172 return unless defined $_;
173 return $self->_config_substitutions( $s->param('name'), $s->param('substitutions'), $_ );
174 }
175
176 );
177 $v->visit( $s->param('raw_config') );
178 },
179 dependencies => [ depends_on('name'), depends_on('raw_config'), depends_on('substitutions') ],
f04816ce 180 );
181}
b4a6fa62 182
f04816ce 183sub build_raw_config_service {
184 my $self = shift;
292277c1 185
186 return Bread::Board::BlockInjection->new(
187 name => 'raw_config',
188 block => sub {
189 my $s = shift;
190
191 my @global = @{$s->param('global_config')};
192 my @locals = @{$s->param('local_config')};
193
194 my $config = {};
195 for my $cfg (@global, @locals) {
196 for (keys %$cfg) {
197 $config = Catalyst::Utils::merge_hashes( $config, $cfg->{$_} );
b4a6fa62 198 }
292277c1 199 }
200 return $config;
201 },
202 dependencies => [ depends_on('global_config'), depends_on('local_config') ],
f04816ce 203 );
204}
b4a6fa62 205
f04816ce 206sub build_global_files_service {
207 my $self = shift;
b4a6fa62 208
292277c1 209 return Bread::Board::BlockInjection->new(
210 name => 'global_files',
211 block => sub {
212 my $s = shift;
b4a6fa62 213
292277c1 214 my ( $path, $extension ) = @{$s->param('config_path')};
b4a6fa62 215
292277c1 216 my @extensions = @{$s->param('extensions')};
217
218 my @files;
219 if ( $extension ) {
220 die "Unable to handle files with the extension '${extension}'" unless grep { $_ eq $extension } @extensions;
221 push @files, $path;
222 } else {
223 @files = map { "$path.$_" } @extensions;
224 }
225 return \@files;
226 },
227 dependencies => [ depends_on('extensions'), depends_on('config_path') ],
f04816ce 228 );
229}
b4a6fa62 230
f04816ce 231sub build_local_files_service {
232 my $self = shift;
292277c1 233
234 return Bread::Board::BlockInjection->new(
235 name => 'local_files',
236 block => sub {
237 my $s = shift;
238
239 my ( $path, $extension ) = @{$s->param('config_path')};
240 my $suffix = $s->param('config_local_suffix');
241
242 my @extensions = @{$s->param('extensions')};
243
244 my @files;
245 if ( $extension ) {
246 die "Unable to handle files with the extension '${extension}'" unless grep { $_ eq $extension } @extensions;
247 $path =~ s{\.$extension}{_$suffix.$extension};
248 push @files, $path;
249 } else {
250 @files = map { "${path}_${suffix}.$_" } @extensions;
251 }
252 return \@files;
253 },
254 dependencies => [ depends_on('extensions'), depends_on('config_path'), depends_on('config_local_suffix') ],
f04816ce 255 );
256}
b4a6fa62 257
f04816ce 258sub build_global_config_service {
259 my $self = shift;
292277c1 260
261 return Bread::Board::BlockInjection->new(
262 name => 'global_config',
263 block => sub {
264 my $s = shift;
265
266 return Config::Any->load_files({
267 files => $s->param('global_files'),
268 filter => \&_fix_syntax,
269 use_ext => 1,
270 driver_args => $s->param('driver'),
271 });
272 },
273 dependencies => [ depends_on('global_files') ],
f04816ce 274 );
275}
b4a6fa62 276
f04816ce 277sub build_local_config_service {
278 my $self = shift;
292277c1 279
280 return Bread::Board::BlockInjection->new(
281 name => 'local_config',
282 block => sub {
283 my $s = shift;
284
285 return Config::Any->load_files({
286 files => $s->param('local_files'),
287 filter => \&_fix_syntax,
288 use_ext => 1,
289 driver_args => $s->param('driver'),
290 });
291 },
292 dependencies => [ depends_on('local_files') ],
f04816ce 293 );
294}
b4a6fa62 295
f04816ce 296sub build_config_path_service {
297 my $self = shift;
b4a6fa62 298
292277c1 299 return Bread::Board::BlockInjection->new(
300 name => 'config_path',
301 block => sub {
302 my $s = shift;
b4a6fa62 303
292277c1 304 my $path = $s->param('path');
305 my $prefix = $s->param('prefix');
b4a6fa62 306
292277c1 307 my ( $extension ) = ( $path =~ m{\.(.{1,4})$} );
308
309 if ( -d $path ) {
310 $path =~ s{[\/\\]$}{};
311 $path .= "/$prefix";
312 }
b4a6fa62 313
292277c1 314 return [ $path, $extension ];
315 },
316 dependencies => [ depends_on('prefix'), depends_on('path') ],
f04816ce 317 );
318}
b4a6fa62 319
f04816ce 320sub build_config_local_suffix_service {
321 my $self = shift;
292277c1 322
323 return Bread::Board::BlockInjection->new(
324 name => 'config_local_suffix',
325 block => sub {
326 my $s = shift;
327 my $suffix = Catalyst::Utils::env_value( $s->param('name'), 'CONFIG_LOCAL_SUFFIX' ) || $self->config_local_suffix;
328
329 return $suffix;
330 },
331 dependencies => [ depends_on('name') ],
f04816ce 332 );
b4a6fa62 333}
334
335sub _fix_syntax {
336 my $config = shift;
337 my @components = (
338 map +{
339 prefix => $_ eq 'Component' ? '' : $_ . '::',
340 values => delete $config->{ lc $_ } || delete $config->{ $_ }
341 },
342 grep { ref $config->{ lc $_ } || ref $config->{ $_ } }
343 qw( Component Model M View V Controller C Plugin )
344 );
345
346 foreach my $comp ( @components ) {
347 my $prefix = $comp->{ prefix };
348 foreach my $element ( keys %{ $comp->{ values } } ) {
349 $config->{ "$prefix$element" } = $comp->{ values }->{ $element };
350 }
351 }
352}
353
354sub _config_substitutions {
6682389c 355 my ( $self, $name, $subs, $arg ) = @_;
b4a6fa62 356
357 $subs->{ HOME } ||= sub { shift->path_to( '' ); };
358 $subs->{ ENV } ||=
359 sub {
360 my ( $c, $v ) = @_;
361 if (! defined($ENV{$v})) {
362 Catalyst::Exception->throw( message =>
363 "Missing environment variable: $v" );
364 return "";
365 } else {
366 return $ENV{ $v };
367 }
368 };
369 $subs->{ path_to } ||= sub { shift->path_to( @_ ); };
370 $subs->{ literal } ||= sub { return $_[ 1 ]; };
371 my $subsre = join( '|', keys %$subs );
372
6682389c 373 $arg =~ s{__($subsre)(?:\((.+?)\))?__}{ $subs->{ $1 }->( $name, $2 ? split( /,/, $2 ) : () ) }eg;
374 return $arg;
b4a6fa62 375}
376
d057ddb9 3771;
378
379__END__
380
381=pod
382
383=head1 NAME
384
385Catalyst::Container - IOC for Catalyst components
386
387=head1 METHODS
388
389=head2 build_model_subcontainer
390
391=head2 build_view_subcontainer
392
393=head2 build_controller_subcontainer
394
395=head2 build_name_service
396
397=head2 build_driver_service
398
399=head2 build_file_service
400
401=head2 build_substitutions_service
402
403=head2 build_extensions_service
404
405=head2 build_prefix_service
406
407=head2 build_path_service
408
409=head2 build_config_service
410
411=head2 build_raw_config_service
412
413=head2 build_global_files_service
414
415=head2 build_local_files_service
416
417=head2 build_global_config_service
418
419=head2 build_local_config_service
420
421=head2 build_config_path_service
422
423=head2 build_config_local_suffix_service
424
425=head2 _fix_syntax
426
427=head2 _config_substitutions
428
bf3c8088 429=head1 AUTHORS
430
e8ed391e 431Catalyst Contributors, see Catalyst.pm
bf3c8088 432
e8ed391e 433=head1 COPYRIGHT
bf3c8088 434
435This library is free software. You can redistribute it and/or modify it under
436the same terms as Perl itself.
437
438=cut