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