added the app to the container when the app is built
[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 ();
ae0690a5 7use List::Util qw(first);
d3742403 8use Devel::InnerPackage ();
b4410fc3 9use Hash::Util qw/lock_hash/;
2bb0da6d 10use MooseX::Types::LoadableClass qw/ LoadableClass /;
0dff29e2 11use Moose::Util;
6a23ae28 12use Scalar::Util qw/refaddr/;
ff0e9735 13use Catalyst::IOC::BlockInjection;
b7da37bd 14use Catalyst::IOC::ConstructorInjection;
2f32de9a 15use Module::Pluggable::Object ();
8b749525 16use namespace::autoclean;
b4a6fa62 17
18extends 'Bread::Board::Container';
19
20has config_local_suffix => (
442ab13e 21 is => 'ro',
b4a6fa62 22 isa => 'Str',
23 default => 'local',
24);
25
26has driver => (
442ab13e 27 is => 'ro',
b4a6fa62 28 isa => 'HashRef',
29 default => sub { +{} },
30);
31
32has file => (
442ab13e 33 is => 'ro',
b4a6fa62 34 isa => 'Str',
35 default => '',
36);
37
38has substitutions => (
442ab13e 39 is => 'ro',
b4a6fa62 40 isa => 'HashRef',
41 default => sub { +{} },
42);
43
d24c7cad 44has application => (
45 is => 'ro',
46 isa => 'Catalyst|Str',
47 lazy => 1,
8287cb1c 48 weak_ref => 1,
49 writer => '_set_application',
d24c7cad 50 default => sub { shift->application_name },
51);
52
6c743f02 53has application_name => (
55f417ad 54 is => 'ro',
55 isa => 'Str',
56 required => 1,
b4a6fa62 57);
58
2bb0da6d 59has sub_container_class => (
60 isa => LoadableClass,
61 is => 'ro',
62 coerce => 1,
a6c13ff4 63 default => 'Catalyst::IOC::SubContainer',
8b749525 64 handles => {
65 new_sub_container => 'new',
66 }
2bb0da6d 67);
68
b4a6fa62 69sub BUILD {
a2c0d071 70 my ( $self, $params ) = @_;
b4a6fa62 71
292277c1 72 $self->add_service(
73 $self->${\"build_${_}_service"}
74 ) for qw/
7451d1ea 75 substitutions
76 file
77 driver
d24c7cad 78 application
6c743f02 79 application_name
7451d1ea 80 prefix
81 extensions
82 path
83 config
84 raw_config
85 global_files
86 local_files
87 global_config
88 local_config
0fcf9a51 89 class_config
7451d1ea 90 config_local_suffix
91 config_path
3e4f5406 92 locate_components
7451d1ea 93 /;
f04816ce 94
0fcf9a51 95 my $config = $self->resolve( service => 'config' );
96
cd20de09 97 # don't force default_component to be undef if the config wasn't set
98 my @default_view = $config->{default_view}
99 ? ( default_component => $config->{default_view} )
100 : ( )
101 ;
102 my @default_model = $config->{default_model}
103 ? ( default_component => $config->{default_model} )
104 : ( )
105 ;
106
292277c1 107 $self->add_sub_container(
ff0e9735 108 $self->build_component_subcontainer
109 );
110
111 $self->add_sub_container(
a2c0d071 112 $self->build_controller_subcontainer
113 );
114
115 $self->add_sub_container(
cd20de09 116 $self->build_view_subcontainer( @default_view )
a2c0d071 117 );
118
119 $self->add_sub_container(
cd20de09 120 $self->build_model_subcontainer( @default_model )
a2c0d071 121 );
85ebb635 122
123 {
124 no strict 'refs';
6a23ae28 125 no warnings 'once';
85ebb635 126 my $class = ref $self;
13398eb0 127 ${ $class . '::customise_container' }->($self)
128 if ${ $class . '::customise_container' };
85ebb635 129 }
f04816ce 130}
131
132sub build_model_subcontainer {
133 my $self = shift;
134
a2c0d071 135 return $self->new_sub_container( @_,
5a53ef3d 136 name => 'model',
b06ded69 137 );
f04816ce 138}
139
140sub build_view_subcontainer {
141 my $self = shift;
142
a2c0d071 143 return $self->new_sub_container( @_,
5a53ef3d 144 name => 'view',
b06ded69 145 );
f04816ce 146}
147
148sub build_controller_subcontainer {
149 my $self = shift;
150
b06ded69 151 return $self->new_sub_container(
5a53ef3d 152 name => 'controller',
b06ded69 153 );
f04816ce 154}
155
ff0e9735 156sub build_component_subcontainer {
157 my $self = shift;
158
159 return Bread::Board::Container->new(
160 name => 'component',
161 );
162}
163
10a10c42 164sub build_home_service {
165 my $self = shift;
166
167 return Bread::Board::BlockInjection->new(
168 lifecycle => 'Singleton',
169 name => 'home',
170 block => sub {
171 my $self = shift;
172 my $class = $self->param('application_name');
1a5e7517 173 my $home;
10a10c42 174
175 if ( my $env = Catalyst::Utils::env_value( $class, 'HOME' ) ) {
176 $home = $env;
177 }
178
179 $home ||= Catalyst::Utils::home($class);
10a10c42 180 return $home;
181 },
182 dependencies => [ depends_on('application_name') ],
183 );
184}
185
186# FIXME: very ambiguous - maybe root_dir?
187sub build_root_service {
188 my $self = shift;
189
190 return Bread::Board::BlockInjection->new(
191 lifecycle => 'Singleton',
192 name => 'root',
193 block => sub {
194 my $self = shift;
195
196 return Path::Class::Dir->new( $self->param('home') )->subdir('root');
197 },
198 dependencies => [ depends_on('home') ],
199 );
200}
201
d24c7cad 202sub build_application_service {
203 my $self = shift;
204
205 return Bread::Board::Literal->new(
206 name => 'application',
207 value => $self->application,
208 );
209}
210
5b47e4a9 211sub build_application_name_service {
f04816ce 212 my $self = shift;
292277c1 213
6c743f02 214 return Bread::Board::Literal->new( name => 'application_name', value => $self->application_name );
f04816ce 215}
216
217sub build_driver_service {
218 my $self = shift;
292277c1 219
220 return Bread::Board::Literal->new( name => 'driver', value => $self->driver );
f04816ce 221}
222
223sub build_file_service {
224 my $self = shift;
292277c1 225
226 return Bread::Board::Literal->new( name => 'file', value => $self->file );
f04816ce 227}
228
229sub build_substitutions_service {
230 my $self = shift;
292277c1 231
232 return Bread::Board::Literal->new( name => 'substitutions', value => $self->substitutions );
f04816ce 233}
234
235sub build_extensions_service {
236 my $self = shift;
292277c1 237
238 return Bread::Board::BlockInjection->new(
7a267bb5 239 lifecycle => 'Singleton',
292277c1 240 name => 'extensions',
241 block => sub {
242 return \@{Config::Any->extensions};
243 },
f04816ce 244 );
245}
b4a6fa62 246
f04816ce 247sub build_prefix_service {
248 my $self = shift;
292277c1 249
250 return Bread::Board::BlockInjection->new(
7a267bb5 251 lifecycle => 'Singleton',
292277c1 252 name => 'prefix',
253 block => sub {
dca40344 254 return Catalyst::Utils::appprefix( shift->param('application_name') );
292277c1 255 },
dca40344 256 dependencies => [ depends_on('application_name') ],
f04816ce 257 );
258}
b4a6fa62 259
f04816ce 260sub build_path_service {
261 my $self = shift;
292277c1 262
263 return Bread::Board::BlockInjection->new(
7a267bb5 264 lifecycle => 'Singleton',
292277c1 265 name => 'path',
266 block => sub {
267 my $s = shift;
268
dca40344 269 return Catalyst::Utils::env_value( $s->param('application_name'), 'CONFIG' )
292277c1 270 || $s->param('file')
6c743f02 271 || $s->param('application_name')->path_to( $s->param('prefix') );
292277c1 272 },
6c743f02 273 dependencies => [ depends_on('file'), depends_on('application_name'), depends_on('prefix') ],
f04816ce 274 );
275}
b4a6fa62 276
f04816ce 277sub build_config_service {
278 my $self = shift;
292277c1 279
280 return Bread::Board::BlockInjection->new(
7a267bb5 281 lifecycle => 'Singleton',
292277c1 282 name => 'config',
283 block => sub {
284 my $s = shift;
285
286 my $v = Data::Visitor::Callback->new(
287 plain_value => sub {
288 return unless defined $_;
6c743f02 289 return $self->_config_substitutions( $s->param('application_name'), $s->param('substitutions'), $_ );
292277c1 290 }
291
292 );
293 $v->visit( $s->param('raw_config') );
294 },
6c743f02 295 dependencies => [ depends_on('application_name'), depends_on('raw_config'), depends_on('substitutions') ],
f04816ce 296 );
297}
b4a6fa62 298
f04816ce 299sub build_raw_config_service {
300 my $self = shift;
292277c1 301
302 return Bread::Board::BlockInjection->new(
7a267bb5 303 lifecycle => 'Singleton',
292277c1 304 name => 'raw_config',
305 block => sub {
306 my $s = shift;
307
308 my @global = @{$s->param('global_config')};
309 my @locals = @{$s->param('local_config')};
310
0fcf9a51 311 my $config = $s->param('class_config');
312
292277c1 313 for my $cfg (@global, @locals) {
314 for (keys %$cfg) {
315 $config = Catalyst::Utils::merge_hashes( $config, $cfg->{$_} );
b4a6fa62 316 }
292277c1 317 }
0fcf9a51 318
292277c1 319 return $config;
320 },
0fcf9a51 321 dependencies => [ depends_on('global_config'), depends_on('local_config'), depends_on('class_config') ],
f04816ce 322 );
323}
b4a6fa62 324
f04816ce 325sub build_global_files_service {
326 my $self = shift;
b4a6fa62 327
292277c1 328 return Bread::Board::BlockInjection->new(
7a267bb5 329 lifecycle => 'Singleton',
292277c1 330 name => 'global_files',
331 block => sub {
332 my $s = shift;
b4a6fa62 333
292277c1 334 my ( $path, $extension ) = @{$s->param('config_path')};
b4a6fa62 335
292277c1 336 my @extensions = @{$s->param('extensions')};
337
338 my @files;
339 if ( $extension ) {
340 die "Unable to handle files with the extension '${extension}'" unless grep { $_ eq $extension } @extensions;
341 push @files, $path;
342 } else {
343 @files = map { "$path.$_" } @extensions;
344 }
345 return \@files;
346 },
347 dependencies => [ depends_on('extensions'), depends_on('config_path') ],
f04816ce 348 );
349}
b4a6fa62 350
f04816ce 351sub build_local_files_service {
352 my $self = shift;
292277c1 353
354 return Bread::Board::BlockInjection->new(
7a267bb5 355 lifecycle => 'Singleton',
292277c1 356 name => 'local_files',
357 block => sub {
358 my $s = shift;
359
360 my ( $path, $extension ) = @{$s->param('config_path')};
361 my $suffix = $s->param('config_local_suffix');
362
363 my @extensions = @{$s->param('extensions')};
364
365 my @files;
366 if ( $extension ) {
367 die "Unable to handle files with the extension '${extension}'" unless grep { $_ eq $extension } @extensions;
368 $path =~ s{\.$extension}{_$suffix.$extension};
369 push @files, $path;
370 } else {
371 @files = map { "${path}_${suffix}.$_" } @extensions;
372 }
373 return \@files;
374 },
375 dependencies => [ depends_on('extensions'), depends_on('config_path'), depends_on('config_local_suffix') ],
f04816ce 376 );
377}
b4a6fa62 378
0fcf9a51 379sub build_class_config_service {
380 my $self = shift;
381
382 return Bread::Board::BlockInjection->new(
383 lifecycle => 'Singleton',
384 name => 'class_config',
385 block => sub {
386 my $s = shift;
387 my $app = $s->param('application_name');
388
389 # Container might be called outside Catalyst context
390 return {} unless Class::MOP::is_class_loaded($app);
391
392 # config might not have been defined
393 return $app->config || {};
394 },
395 dependencies => [ depends_on('application_name') ],
396 );
397}
398
f04816ce 399sub build_global_config_service {
400 my $self = shift;
292277c1 401
402 return Bread::Board::BlockInjection->new(
7a267bb5 403 lifecycle => 'Singleton',
292277c1 404 name => 'global_config',
405 block => sub {
406 my $s = shift;
407
408 return Config::Any->load_files({
409 files => $s->param('global_files'),
410 filter => \&_fix_syntax,
411 use_ext => 1,
412 driver_args => $s->param('driver'),
413 });
414 },
415 dependencies => [ depends_on('global_files') ],
f04816ce 416 );
417}
b4a6fa62 418
f04816ce 419sub build_local_config_service {
420 my $self = shift;
292277c1 421
422 return Bread::Board::BlockInjection->new(
7a267bb5 423 lifecycle => 'Singleton',
292277c1 424 name => 'local_config',
425 block => sub {
426 my $s = shift;
427
428 return Config::Any->load_files({
429 files => $s->param('local_files'),
430 filter => \&_fix_syntax,
431 use_ext => 1,
432 driver_args => $s->param('driver'),
433 });
434 },
435 dependencies => [ depends_on('local_files') ],
f04816ce 436 );
437}
b4a6fa62 438
f04816ce 439sub build_config_path_service {
440 my $self = shift;
b4a6fa62 441
292277c1 442 return Bread::Board::BlockInjection->new(
7a267bb5 443 lifecycle => 'Singleton',
292277c1 444 name => 'config_path',
445 block => sub {
446 my $s = shift;
b4a6fa62 447
292277c1 448 my $path = $s->param('path');
449 my $prefix = $s->param('prefix');
b4a6fa62 450
292277c1 451 my ( $extension ) = ( $path =~ m{\.(.{1,4})$} );
452
453 if ( -d $path ) {
454 $path =~ s{[\/\\]$}{};
455 $path .= "/$prefix";
456 }
b4a6fa62 457
292277c1 458 return [ $path, $extension ];
459 },
460 dependencies => [ depends_on('prefix'), depends_on('path') ],
f04816ce 461 );
462}
b4a6fa62 463
f04816ce 464sub build_config_local_suffix_service {
465 my $self = shift;
292277c1 466
467 return Bread::Board::BlockInjection->new(
7a267bb5 468 lifecycle => 'Singleton',
292277c1 469 name => 'config_local_suffix',
470 block => sub {
471 my $s = shift;
6c743f02 472 my $suffix = Catalyst::Utils::env_value( $s->param('application_name'), 'CONFIG_LOCAL_SUFFIX' ) || $self->config_local_suffix;
292277c1 473
474 return $suffix;
475 },
6c743f02 476 dependencies => [ depends_on('application_name') ],
f04816ce 477 );
b4a6fa62 478}
479
3e4f5406 480sub build_locate_components_service {
481 my $self = shift;
482
483 return Bread::Board::BlockInjection->new(
484 lifecycle => 'Singleton',
485 name => 'locate_components',
486 block => sub {
487 my $s = shift;
488 my $class = $s->param('application_name');
489 my $config = $s->param('config')->{ setup_components };
490
491 Catalyst::Exception->throw(
492 qq{You are using search_extra config option. That option is\n} .
493 qq{deprecated, please refer to the documentation for\n} .
494 qq{other ways of achieving the same results.\n}
495 ) if delete $config->{ search_extra };
496
497 my @paths = qw( ::Controller ::C ::Model ::M ::View ::V );
498
499 my $locator = Module::Pluggable::Object->new(
500 search_path => [ map { s/^(?=::)/$class/; $_; } @paths ],
501 %$config
502 );
503
b47ed80b 504 return [ $locator->plugins ];
3e4f5406 505 },
506 dependencies => [ depends_on('application_name'), depends_on('config') ],
507 );
508}
509
88d81c1b 510sub setup_components {
fa6607ec 511 my $self = shift;
88d81c1b 512 my $class = $self->resolve( service => 'application_name' );
513 my @comps = @{ $self->resolve( service => 'locate_components' ) };
514 my %comps = map { $_ => 1 } @comps;
515 my $deprecatedcatalyst_component_names = 0;
fa6607ec 516
6a23ae28 517 my $app_locate_components_addr = refaddr(
518 $class->can('locate_components')
519 );
520 my $cat_locate_components_addr = refaddr(
521 Catalyst->can('locate_components')
522 );
523
524 if ($app_locate_components_addr != $cat_locate_components_addr) {
61915b48 525 # FIXME - why not just say: @comps = $class->locate_components() ?
6a23ae28 526 $class->log->warn(qq{You have overridden locate_components. That } .
527 qq{no longer works. Please refer to the documentation to achieve } .
528 qq{similar results.\n}
529 );
530 }
531
88d81c1b 532 for my $component ( @comps ) {
fa6607ec 533
88d81c1b 534 # We pass ignore_loaded here so that overlay files for (e.g.)
535 # Model::DBI::Schema sub-classes are loaded - if it's in @comps
536 # we know M::P::O found a file on disk so this is safe
fa6607ec 537
88d81c1b 538 Catalyst::Utils::ensure_class_loaded( $component, { ignore_loaded => 1 } );
539 }
fa6607ec 540
88d81c1b 541 for my $component (@comps) {
6e2a1222 542 $self->add_component( $component );
88d81c1b 543 # FIXME - $instance->expand_modules() is broken
544 my @expanded_components = $self->expand_component_module( $component );
545
546 if (
547 !$deprecatedcatalyst_component_names &&
548 ($deprecatedcatalyst_component_names = $component =~ m/::[CMV]::/) ||
549 ($deprecatedcatalyst_component_names = grep { /::[CMV]::/ } @expanded_components)
550 ) {
551 # FIXME - should I be calling warn here?
6e2a1222 552 # Maybe it's time to remove it, or become fatal
88d81c1b 553 $class->log->warn(qq{Your application is using the deprecated ::[MVC]:: type naming scheme.\n}.
554 qq{Please switch your class names to ::Model::, ::View:: and ::Controller: as appropriate.\n}
555 );
556 }
fa6607ec 557
88d81c1b 558 for my $component (@expanded_components) {
6e2a1222 559 $self->add_component( $component )
88d81c1b 560 unless $comps{$component};
561 }
562 }
fa6607ec 563}
564
b4a6fa62 565sub _fix_syntax {
566 my $config = shift;
567 my @components = (
568 map +{
569 prefix => $_ eq 'Component' ? '' : $_ . '::',
570 values => delete $config->{ lc $_ } || delete $config->{ $_ }
571 },
572 grep { ref $config->{ lc $_ } || ref $config->{ $_ } }
573 qw( Component Model M View V Controller C Plugin )
574 );
575
576 foreach my $comp ( @components ) {
577 my $prefix = $comp->{ prefix };
578 foreach my $element ( keys %{ $comp->{ values } } ) {
579 $config->{ "$prefix$element" } = $comp->{ values }->{ $element };
580 }
581 }
582}
583
584sub _config_substitutions {
6682389c 585 my ( $self, $name, $subs, $arg ) = @_;
b4a6fa62 586
587 $subs->{ HOME } ||= sub { shift->path_to( '' ); };
588 $subs->{ ENV } ||=
589 sub {
590 my ( $c, $v ) = @_;
591 if (! defined($ENV{$v})) {
592 Catalyst::Exception->throw( message =>
593 "Missing environment variable: $v" );
594 return "";
595 } else {
596 return $ENV{ $v };
597 }
598 };
599 $subs->{ path_to } ||= sub { shift->path_to( @_ ); };
600 $subs->{ literal } ||= sub { return $_[ 1 ]; };
601 my $subsre = join( '|', keys %$subs );
602
6682389c 603 $arg =~ s{__($subsre)(?:\((.+?)\))?__}{ $subs->{ $1 }->( $name, $2 ? split( /,/, $2 ) : () ) }eg;
604 return $arg;
b4a6fa62 605}
606
a17e0ff8 607sub get_component_from_sub_container {
608 my ( $self, $sub_container_name, $name, $c, @args ) = @_;
609
610 my $sub_container = $self->get_sub_container( $sub_container_name );
611
0e747f0c 612 if (!$name) {
a2c0d071 613 my $default = $sub_container->default_component;
0e747f0c 614
615 return $sub_container->get_component( $default, $c, @args )
616 if $default && $sub_container->has_service( $default );
617
a0146296 618 # FIXME - should I be calling $c->log->warn here?
0e747f0c 619 # this is never a controller, so this is safe
620 $c->log->warn( "Calling \$c->$sub_container_name() is not supported unless you specify one of:" );
621 $c->log->warn( "* \$c->config(default_$sub_container_name => 'the name of the default $sub_container_name to use')" );
622 $c->log->warn( "* \$c->stash->{current_$sub_container_name} # the name of the view to use for this request" );
623 $c->log->warn( "* \$c->stash->{current_${sub_container_name}_instance} # the instance of the $sub_container_name to use for this request" );
a2c0d071 624
625 return;
0e747f0c 626 }
627
a17e0ff8 628 return $sub_container->get_component_regexp( $name, $c, @args )
629 if ref $name;
630
631 return $sub_container->get_component( $name, $c, @args )
632 if $sub_container->has_service( $name );
633
634 $c->log->warn(
635 "Attempted to use $sub_container_name '$name', " .
636 "but it does not exist"
637 );
638
639 return;
640}
641
c4aedec7 642sub find_component {
ec17c391 643 my ( $self, $component, @args ) = @_;
f147e6c2 644 my ( $type, $name ) = _get_component_type_name($component);
c4aedec7 645 my @result;
646
d0f954b4 647 return $self->get_component_from_sub_container(
ec17c391 648 $type, $name, @args
d0f954b4 649 ) if $type;
650
c4aedec7 651 my $query = ref $component
652 ? $component
653 : qr{^$component$}
654 ;
655
656 for my $subcontainer_name (qw/model view controller/) {
a0146296 657 my $subcontainer = $self->get_sub_container( $subcontainer_name );
c4aedec7 658 my @components = $subcontainer->get_service_list;
659 @result = grep { m{$component} } @components;
660
ec17c391 661 return map { $subcontainer->get_component( $_, @args ) } @result
c4aedec7 662 if @result;
663 }
664
d0f954b4 665 # one last search for things like $c->comp(qr/::M::/)
ae9bf2af 666 @result = $self->_find_component_regexp(
ec17c391 667 $component, @args
d0f954b4 668 ) if !@result and ref $component;
669
c4aedec7 670 # it expects an empty list on failed searches
671 return @result;
672}
673
ae9bf2af 674sub _find_component_regexp {
9084f394 675 my ( $self, $component, $ctx, @args ) = @_;
4e2b302e 676 my @result;
677
9084f394 678 my @components = grep { m{$component} } keys %{ $self->get_all_components($ctx) };
4e2b302e 679
680 for (@components) {
f147e6c2 681 my ($type, $name) = _get_component_type_name($_);
4e2b302e 682
683 push @result, $self->get_component_from_sub_container(
9084f394 684 $type, $name, $ctx, @args
4e2b302e 685 ) if $type;
686 }
687
688 return @result;
689}
690
b4410fc3 691sub get_all_components {
9084f394 692 my ($self, $class) = @_;
b4410fc3 693 my %components;
694
abefa111 695 # FIXME - if we're getting from these containers, we need to either:
696 # - pass 'ctx' and 'accept_context_args' OR
697 # - make these params optional
287df7b2 698 # big problem when setting up the dispatcher - this method is called
699 # as $container->get_all_components('MyApp'). What to do with Request
700 # life cycles?
3552850b 701 foreach my $type (qw/model view controller /) {
b32d8169 702 my $container = $self->get_sub_container($type);
3552850b 703
704 for my $component ($container->get_service_list) {
9084f394 705 my $comp_service = $container->get_service($component);
c887dd0d 706
9084f394 707 $components{$comp_service->catalyst_component_name} = $comp_service->get(ctx => $class);
3552850b 708 }
b4410fc3 709 }
710
711 return lock_hash %components;
712}
713
f147e6c2 714sub add_component {
6e2a1222 715 my ( $self, $component ) = @_;
f147e6c2 716 my ( $type, $name ) = _get_component_type_name($component);
717
718 return unless $type;
719
b844dcad 720 # The 'component' sub-container will create the object, and store it's
721 # instance, which, by default, will live throughout the application.
722 # The model/view/controller sub-containers only reference the instance
723 # held in the aforementioned sub-container, and execute the ACCEPT_CONTEXT
724 # sub every time they are called, when it exists.
725 my $instance_container = $self->get_sub_container('component');
726 my $accept_context_container = $self->get_sub_container($type);
727
25af999b 728 # Custom containers might have added the service already
729 # We don't want to override that
730 return if $accept_context_container->has_service( $name );
731
732 my $component_service_name = "${type}_${name}";
733
b844dcad 734 $instance_container->add_service(
b7da37bd 735 Catalyst::IOC::ConstructorInjection->new(
ff0e9735 736 name => $component_service_name,
28ec44a3 737 catalyst_component_name => $component,
b7da37bd 738 class => $component,
ff0e9735 739 lifecycle => 'Singleton',
bf142143 740 dependencies => [
d24c7cad 741 depends_on( '/application' ),
bf142143 742 ],
a987f40a 743 ),
25af999b 744 );
a987f40a 745 # XXX - FIXME - We have to explicitly build the service here,
746 # causing the COMPONENT method to be called early here, as otherwise
747 # if the component method defines other classes (e.g. the
748 # ACCEPT_CONTEXT injection Model::DBIC::Schema does)
749 # then they won't be found by Devel::InnerPackage
750 # see also t/aggregate/unit_core_component_loading.t
751 $instance_container->get_service($component_service_name)->get;
ff0e9735 752
b844dcad 753 $accept_context_container->add_service(
ff0e9735 754 Catalyst::IOC::BlockInjection->new(
755 name => $name,
9084f394 756 catalyst_component_name => $component,
ff0e9735 757 dependencies => [
758 depends_on( "/component/$component_service_name" ),
759 ],
b844dcad 760 block => sub { shift->param($component_service_name) },
f147e6c2 761 )
25af999b 762 );
f147e6c2 763}
764
765# FIXME: should this sub exist?
766# should it be moved to Catalyst::Utils,
767# or replaced by something already existing there?
768sub _get_component_type_name {
769 my ( $component ) = @_;
ae0690a5 770 my $result;
f147e6c2 771
ae0690a5 772 while ( !$result and (my $index = index $component, '::') > 0 ) {
773 my $type = lc substr $component, 0, $index;
774 $component = substr $component, $index + 2;
775 $result = first { $type eq $_ or $type eq substr($_, 0, 1) }
776 qw{ model view controller };
f147e6c2 777 }
778
ae0690a5 779 return ($result, $component);
f147e6c2 780}
781
d3742403 782sub expand_component_module {
783 my ( $class, $module ) = @_;
784 return Devel::InnerPackage::list_packages( $module );
785}
0dff29e2 786
b4e19c62 787__PACKAGE__->meta->make_immutable;
788
d057ddb9 7891;
790
791__END__
792
793=pod
794
795=head1 NAME
796
797Catalyst::Container - IOC for Catalyst components
798
2c2ed473 799=head1 SYNOPSIS
800
801=head1 DESCRIPTION
802
d057ddb9 803=head1 METHODS
804
61915b48 805=head1 Methods for Building Containers
a0146296 806
309caf39 807=head2 build_component_subcontainer
808
809Container that stores all components, i.e. all models, views and controllers
810together. Each service is an instance of the actual component, and by default
811it lives while the application is running. Retrieving components from this
202784d3 812sub-container will instantiate the component, if it hasn't been instantiated
309caf39 813already, but will not execute ACCEPT_CONTEXT.
814
d057ddb9 815=head2 build_model_subcontainer
816
309caf39 817Container that stores references for all models that are inside the components
202784d3 818sub-container. Retrieving a model triggers ACCEPT_CONTEXT, if it exists.
a0146296 819
d057ddb9 820=head2 build_view_subcontainer
821
309caf39 822Same as L<build_model_subcontainer>, but for views.
a0146296 823
d057ddb9 824=head2 build_controller_subcontainer
825
59221ae6 826Same as L<build_model_subcontainer>, but for controllers.
a0146296 827
61915b48 828=head1 Methods for Building Services
a0146296 829
a4541222 830=head2 build_application_name_service
d057ddb9 831
a4541222 832Name of the application (such as MyApp).
a0146296 833
d057ddb9 834=head2 build_driver_service
835
a0146296 836Config options passed directly to the driver being used.
837
d057ddb9 838=head2 build_file_service
839
a0146296 840?
841
d057ddb9 842=head2 build_substitutions_service
843
bbb306a9 844This method substitutes macros found with calls to a function. There are a
845number of default macros:
846
847=over
848
849=item * C<__HOME__> - replaced with C<$c-E<gt>path_to('')>
850
851=item * C<__ENV(foo)__> - replaced with the value of C<$ENV{foo}>
852
853=item * C<__path_to(foo/bar)__> - replaced with C<$c-E<gt>path_to('foo/bar')>
854
855=item * C<__literal(__FOO__)__> - leaves __FOO__ alone (allows you to use
856C<__DATA__> as a config value, for example)
857
858=back
859
860The parameter list is split on comma (C<,>). You can override this method to
861do your own string munging, or you can define your own macros in
5faa454d 862C<< <MyApp->config( 'Plugin::ConfigLoader' => { substitutions => { ... } } ) >>.
bbb306a9 863Example:
864
5faa454d 865 MyApp->config( 'Plugin::ConfigLoader' => {
866 substitutions => {
867 baz => sub { my $c = shift; qux( @_ ); },
868 },
869 });
bbb306a9 870
871The above will respond to C<__baz(x,y)__> in config strings.
a0146296 872
d057ddb9 873=head2 build_extensions_service
874
bbb306a9 875Config::Any's available config file extensions (e.g. xml, json, pl, etc).
876
d057ddb9 877=head2 build_prefix_service
878
202784d3 879The prefix, based on the application name, that will be used to look-up the
bbb306a9 880config files (which will be in the format $prefix.$extension). If the app is
881MyApp::Foo, the prefix will be myapp_foo.
882
d057ddb9 883=head2 build_path_service
884
bbb306a9 885The path to the config file (or environment variable, if defined).
886
d057ddb9 887=head2 build_config_service
888
bbb306a9 889The resulting configuration for the application, after it has successfully
890been loaded, and all substitutions have been made.
891
d057ddb9 892=head2 build_raw_config_service
893
bbb306a9 894The merge of local_config and global_config hashes, before substitutions.
895
d057ddb9 896=head2 build_global_files_service
897
bbb306a9 898Gets all files for config that don't have the local_suffix, such as myapp.conf.
899
d057ddb9 900=head2 build_local_files_service
901
bbb306a9 902Gets all files for config that have the local_suffix, such as myapp_local.conf.
903
d057ddb9 904=head2 build_global_config_service
905
bbb306a9 906Reads config from global_files.
907
d057ddb9 908=head2 build_local_config_service
909
bbb306a9 910Reads config from local_files.
911
800a31b0 912=head2 build_class_config_service
913
914Reads config set from the application's class attribute config,
915i.e. MyApp->config( name => 'MyApp', ... )
916
d057ddb9 917=head2 build_config_path_service
918
bbb306a9 919Splits the path to the config file, and returns on array ref containing
920the path to the config file minus the extension in the first position,
921and the extension in the second.
922
d057ddb9 923=head2 build_config_local_suffix_service
924
a0146296 925Determines the suffix of files used to override the main config. By default
926this value is C<local>, which will load C<myapp_local.conf>. The suffix can
927be specified in the following order of preference:
928
929=over
930
931=item * C<$ENV{ MYAPP_CONFIG_LOCAL_SUFFIX }>
932
933=item * C<$ENV{ CATALYST_CONFIG_LOCAL_SUFFIX }>
934
935=back
936
937The first one of these values found replaces the default of C<local> in the
938name of the local config file to be loaded.
939
940For example, if C< $ENV{ MYAPP_CONFIG_LOCAL_SUFFIX }> is set to C<testing>,
941ConfigLoader will try and load C<myapp_testing.conf> instead of
942C<myapp_local.conf>.
943
bbb306a9 944=head2 build_locate_components_service
945
946This method is meant to provide a list of component modules that should be
947setup for the application. By default, it will use L<Module::Pluggable>.
948
949Specify a C<setup_components> config option to pass additional options directly
950to L<Module::Pluggable>.
951
952=head1 Other methods
953
a0146296 954=head2 get_component_from_sub_container($sub_container, $name, $c, @args)
955
202784d3 956Looks for components in a given sub-container (such as controller, model or
164a0b2b 957view), and returns the searched component. If $name is undef, it returns the
958default component (such as default_view, if $sub_container is 'view'). If
959$name is a regexp, it returns an array of matching components. Otherwise, it
960looks for the component with name $name.
8dc2fca3 961
b4410fc3 962=head2 get_all_components
963
164a0b2b 964Fetches all the components, in each of the sub_containers model, view and
202784d3 965controller, and returns a read-only hash. The keys are the class names, and
164a0b2b 966the values are the blessed objects. This is what is returned by $c->components.
a0146296 967
f147e6c2 968=head2 add_component
969
202784d3 970Adds a component to the appropriate sub-container. The sub-container is guessed
164a0b2b 971by the component name given.
a0146296 972
c4aedec7 973=head2 find_component
974
164a0b2b 975Searches for components in all containers. If $component is the full class
202784d3 976name, the sub-container is guessed, and it gets the searched component in there.
977Otherwise, it looks for a component with that name in all sub-containers. If
ae9bf2af 978$component is a regexp it calls _find_component_regexp and matches all
979components against that regexp.
a0146296 980
6e2a1222 981=head2 expand_component_module
d3742403 982
983Components found by C<locate_components> will be passed to this method, which
984is expected to return a list of component (package) names to be set up.
985
ebf7f0a5 986=head2 setup_components
fa6607ec 987
61915b48 988Uses locate_components service to list the components, and adds them to the
202784d3 989appropriate sub-containers, using add_component().
61915b48 990
bf3c8088 991=head1 AUTHORS
992
e8ed391e 993Catalyst Contributors, see Catalyst.pm
bf3c8088 994
e8ed391e 995=head1 COPYRIGHT
bf3c8088 996
997This library is free software. You can redistribute it and/or modify it under
998the same terms as Perl itself.
999
1000=cut