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