simplified Catalyst.pm
[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
b06ded69 84 return $self->new_sub_container(
5a53ef3d 85 name => 'model',
b06ded69 86 );
f04816ce 87}
88
89sub build_view_subcontainer {
90 my $self = shift;
91
b06ded69 92 return $self->new_sub_container(
5a53ef3d 93 name => 'view',
b06ded69 94 );
f04816ce 95}
96
97sub build_controller_subcontainer {
98 my $self = shift;
99
b06ded69 100 return $self->new_sub_container(
5a53ef3d 101 name => 'controller',
b06ded69 102 );
f04816ce 103}
104
0e747f0c 105sub build_default_model {
106 Bread::Board::BlockInjection->new(
107 block => sub {
108 shift->param('config')->{default_model};
109 },
110 dependencies => [ depends_on('config') ],
111 );
112}
113
114sub build_default_view {
115 Bread::Board::BlockInjection->new(
116 name => 'default_view',
117 block => sub {
118 shift->param('config')->{default_view};
119 },
120 dependencies => [ depends_on('config') ],
121 );
122}
123
f04816ce 124sub build_name_service {
125 my $self = shift;
292277c1 126
127 return Bread::Board::Literal->new( name => 'name', value => $self->name );
f04816ce 128}
129
130sub build_driver_service {
131 my $self = shift;
292277c1 132
133 return Bread::Board::Literal->new( name => 'driver', value => $self->driver );
f04816ce 134}
135
136sub build_file_service {
137 my $self = shift;
292277c1 138
139 return Bread::Board::Literal->new( name => 'file', value => $self->file );
f04816ce 140}
141
142sub build_substitutions_service {
143 my $self = shift;
292277c1 144
145 return Bread::Board::Literal->new( name => 'substitutions', value => $self->substitutions );
f04816ce 146}
147
148sub build_extensions_service {
149 my $self = shift;
292277c1 150
151 return Bread::Board::BlockInjection->new(
152 name => 'extensions',
153 block => sub {
154 return \@{Config::Any->extensions};
155 },
f04816ce 156 );
157}
b4a6fa62 158
f04816ce 159sub build_prefix_service {
160 my $self = shift;
292277c1 161
162 return Bread::Board::BlockInjection->new(
163 name => 'prefix',
164 block => sub {
165 return Catalyst::Utils::appprefix( shift->param('name') );
166 },
167 dependencies => [ depends_on('name') ],
f04816ce 168 );
169}
b4a6fa62 170
f04816ce 171sub build_path_service {
172 my $self = shift;
292277c1 173
174 return Bread::Board::BlockInjection->new(
175 name => 'path',
176 block => sub {
177 my $s = shift;
178
179 return Catalyst::Utils::env_value( $s->param('name'), 'CONFIG' )
180 || $s->param('file')
181 || $s->param('name')->path_to( $s->param('prefix') );
182 },
183 dependencies => [ depends_on('file'), depends_on('name'), depends_on('prefix') ],
f04816ce 184 );
185}
b4a6fa62 186
f04816ce 187sub build_config_service {
188 my $self = shift;
292277c1 189
190 return Bread::Board::BlockInjection->new(
191 name => 'config',
192 block => sub {
193 my $s = shift;
194
195 my $v = Data::Visitor::Callback->new(
196 plain_value => sub {
197 return unless defined $_;
198 return $self->_config_substitutions( $s->param('name'), $s->param('substitutions'), $_ );
199 }
200
201 );
202 $v->visit( $s->param('raw_config') );
203 },
204 dependencies => [ depends_on('name'), depends_on('raw_config'), depends_on('substitutions') ],
f04816ce 205 );
206}
b4a6fa62 207
f04816ce 208sub build_raw_config_service {
209 my $self = shift;
292277c1 210
211 return Bread::Board::BlockInjection->new(
212 name => 'raw_config',
213 block => sub {
214 my $s = shift;
215
216 my @global = @{$s->param('global_config')};
217 my @locals = @{$s->param('local_config')};
218
219 my $config = {};
220 for my $cfg (@global, @locals) {
221 for (keys %$cfg) {
222 $config = Catalyst::Utils::merge_hashes( $config, $cfg->{$_} );
b4a6fa62 223 }
292277c1 224 }
225 return $config;
226 },
227 dependencies => [ depends_on('global_config'), depends_on('local_config') ],
f04816ce 228 );
229}
b4a6fa62 230
f04816ce 231sub build_global_files_service {
232 my $self = shift;
b4a6fa62 233
292277c1 234 return Bread::Board::BlockInjection->new(
235 name => 'global_files',
236 block => sub {
237 my $s = shift;
b4a6fa62 238
292277c1 239 my ( $path, $extension ) = @{$s->param('config_path')};
b4a6fa62 240
292277c1 241 my @extensions = @{$s->param('extensions')};
242
243 my @files;
244 if ( $extension ) {
245 die "Unable to handle files with the extension '${extension}'" unless grep { $_ eq $extension } @extensions;
246 push @files, $path;
247 } else {
248 @files = map { "$path.$_" } @extensions;
249 }
250 return \@files;
251 },
252 dependencies => [ depends_on('extensions'), depends_on('config_path') ],
f04816ce 253 );
254}
b4a6fa62 255
f04816ce 256sub build_local_files_service {
257 my $self = shift;
292277c1 258
259 return Bread::Board::BlockInjection->new(
260 name => 'local_files',
261 block => sub {
262 my $s = shift;
263
264 my ( $path, $extension ) = @{$s->param('config_path')};
265 my $suffix = $s->param('config_local_suffix');
266
267 my @extensions = @{$s->param('extensions')};
268
269 my @files;
270 if ( $extension ) {
271 die "Unable to handle files with the extension '${extension}'" unless grep { $_ eq $extension } @extensions;
272 $path =~ s{\.$extension}{_$suffix.$extension};
273 push @files, $path;
274 } else {
275 @files = map { "${path}_${suffix}.$_" } @extensions;
276 }
277 return \@files;
278 },
279 dependencies => [ depends_on('extensions'), depends_on('config_path'), depends_on('config_local_suffix') ],
f04816ce 280 );
281}
b4a6fa62 282
f04816ce 283sub build_global_config_service {
284 my $self = shift;
292277c1 285
286 return Bread::Board::BlockInjection->new(
287 name => 'global_config',
288 block => sub {
289 my $s = shift;
290
291 return Config::Any->load_files({
292 files => $s->param('global_files'),
293 filter => \&_fix_syntax,
294 use_ext => 1,
295 driver_args => $s->param('driver'),
296 });
297 },
298 dependencies => [ depends_on('global_files') ],
f04816ce 299 );
300}
b4a6fa62 301
f04816ce 302sub build_local_config_service {
303 my $self = shift;
292277c1 304
305 return Bread::Board::BlockInjection->new(
306 name => 'local_config',
307 block => sub {
308 my $s = shift;
309
310 return Config::Any->load_files({
311 files => $s->param('local_files'),
312 filter => \&_fix_syntax,
313 use_ext => 1,
314 driver_args => $s->param('driver'),
315 });
316 },
317 dependencies => [ depends_on('local_files') ],
f04816ce 318 );
319}
b4a6fa62 320
f04816ce 321sub build_config_path_service {
322 my $self = shift;
b4a6fa62 323
292277c1 324 return Bread::Board::BlockInjection->new(
325 name => 'config_path',
326 block => sub {
327 my $s = shift;
b4a6fa62 328
292277c1 329 my $path = $s->param('path');
330 my $prefix = $s->param('prefix');
b4a6fa62 331
292277c1 332 my ( $extension ) = ( $path =~ m{\.(.{1,4})$} );
333
334 if ( -d $path ) {
335 $path =~ s{[\/\\]$}{};
336 $path .= "/$prefix";
337 }
b4a6fa62 338
292277c1 339 return [ $path, $extension ];
340 },
341 dependencies => [ depends_on('prefix'), depends_on('path') ],
f04816ce 342 );
343}
b4a6fa62 344
f04816ce 345sub build_config_local_suffix_service {
346 my $self = shift;
292277c1 347
348 return Bread::Board::BlockInjection->new(
349 name => 'config_local_suffix',
350 block => sub {
351 my $s = shift;
352 my $suffix = Catalyst::Utils::env_value( $s->param('name'), 'CONFIG_LOCAL_SUFFIX' ) || $self->config_local_suffix;
353
354 return $suffix;
355 },
356 dependencies => [ depends_on('name') ],
f04816ce 357 );
b4a6fa62 358}
359
360sub _fix_syntax {
361 my $config = shift;
362 my @components = (
363 map +{
364 prefix => $_ eq 'Component' ? '' : $_ . '::',
365 values => delete $config->{ lc $_ } || delete $config->{ $_ }
366 },
367 grep { ref $config->{ lc $_ } || ref $config->{ $_ } }
368 qw( Component Model M View V Controller C Plugin )
369 );
370
371 foreach my $comp ( @components ) {
372 my $prefix = $comp->{ prefix };
373 foreach my $element ( keys %{ $comp->{ values } } ) {
374 $config->{ "$prefix$element" } = $comp->{ values }->{ $element };
375 }
376 }
377}
378
379sub _config_substitutions {
6682389c 380 my ( $self, $name, $subs, $arg ) = @_;
b4a6fa62 381
382 $subs->{ HOME } ||= sub { shift->path_to( '' ); };
383 $subs->{ ENV } ||=
384 sub {
385 my ( $c, $v ) = @_;
386 if (! defined($ENV{$v})) {
387 Catalyst::Exception->throw( message =>
388 "Missing environment variable: $v" );
389 return "";
390 } else {
391 return $ENV{ $v };
392 }
393 };
394 $subs->{ path_to } ||= sub { shift->path_to( @_ ); };
395 $subs->{ literal } ||= sub { return $_[ 1 ]; };
396 my $subsre = join( '|', keys %$subs );
397
6682389c 398 $arg =~ s{__($subsre)(?:\((.+?)\))?__}{ $subs->{ $1 }->( $name, $2 ? split( /,/, $2 ) : () ) }eg;
399 return $arg;
b4a6fa62 400}
401
a17e0ff8 402sub get_component_from_sub_container {
403 my ( $self, $sub_container_name, $name, $c, @args ) = @_;
404
405 my $sub_container = $self->get_sub_container( $sub_container_name );
406
0e747f0c 407 if (!$name) {
408 my $default_name = 'default_' . $sub_container_name;
409 my $default = $self->resolve( service => $default_name )
410 if $self->has_service($default_name);
411
412 return $sub_container->get_component( $default, $c, @args )
413 if $default && $sub_container->has_service( $default );
414
415 # this is never a controller, so this is safe
416 $c->log->warn( "Calling \$c->$sub_container_name() is not supported unless you specify one of:" );
417 $c->log->warn( "* \$c->config(default_$sub_container_name => 'the name of the default $sub_container_name to use')" );
418 $c->log->warn( "* \$c->stash->{current_$sub_container_name} # the name of the view to use for this request" );
419 $c->log->warn( "* \$c->stash->{current_${sub_container_name}_instance} # the instance of the $sub_container_name to use for this request" );
420 }
421
a17e0ff8 422 return $sub_container->get_component_regexp( $name, $c, @args )
423 if ref $name;
424
425 return $sub_container->get_component( $name, $c, @args )
426 if $sub_container->has_service( $name );
427
428 $c->log->warn(
429 "Attempted to use $sub_container_name '$name', " .
430 "but it does not exist"
431 );
432
433 return;
434}
435
d057ddb9 4361;
437
438__END__
439
440=pod
441
442=head1 NAME
443
444Catalyst::Container - IOC for Catalyst components
445
446=head1 METHODS
447
448=head2 build_model_subcontainer
449
450=head2 build_view_subcontainer
451
452=head2 build_controller_subcontainer
453
454=head2 build_name_service
455
456=head2 build_driver_service
457
458=head2 build_file_service
459
460=head2 build_substitutions_service
461
462=head2 build_extensions_service
463
464=head2 build_prefix_service
465
466=head2 build_path_service
467
468=head2 build_config_service
469
470=head2 build_raw_config_service
471
472=head2 build_global_files_service
473
474=head2 build_local_files_service
475
476=head2 build_global_config_service
477
478=head2 build_local_config_service
479
480=head2 build_config_path_service
481
482=head2 build_config_local_suffix_service
483
484=head2 _fix_syntax
485
486=head2 _config_substitutions
487
bf3c8088 488=head1 AUTHORS
489
e8ed391e 490Catalyst Contributors, see Catalyst.pm
bf3c8088 491
e8ed391e 492=head1 COPYRIGHT
bf3c8088 493
494This library is free software. You can redistribute it and/or modify it under
495the same terms as Perl itself.
496
497=cut