1 package Catalyst::IOC::Container;
5 use Data::Visitor::Callback;
6 use Catalyst::Utils ();
7 use MooseX::Types::LoadableClass qw/ LoadableClass /;
8 use Catalyst::IOC::BlockInjection;
9 use namespace::autoclean;
11 extends 'Bread::Board::Container';
13 has config_local_suffix => (
22 default => sub { +{} },
31 has substitutions => (
34 default => sub { +{} },
43 has sub_container_class => (
47 default => 'Catalyst::IOC::SubContainer',
49 new_sub_container => 'new',
54 my ( $self, $params ) = @_;
57 $self->${\"build_${_}_service"}
76 $self->add_sub_container(
77 $self->build_controller_subcontainer
80 my $config = $self->resolve( service => 'config' );
81 my $default_view = $params->{default_view} || $config->{default_view};
82 my $default_model = $params->{default_model} || $config->{default_model};
84 $self->add_sub_container(
85 $self->build_view_subcontainer(
86 default_component => $default_view,
90 $self->add_sub_container(
91 $self->build_model_subcontainer(
92 default_component => $default_model,
97 sub build_model_subcontainer {
100 return $self->new_sub_container( @_,
105 sub build_view_subcontainer {
108 return $self->new_sub_container( @_,
113 sub build_controller_subcontainer {
116 return $self->new_sub_container(
117 name => 'controller',
121 sub build_name_service {
124 return Bread::Board::Literal->new( name => 'name', value => $self->name );
127 sub build_driver_service {
130 return Bread::Board::Literal->new( name => 'driver', value => $self->driver );
133 sub build_file_service {
136 return Bread::Board::Literal->new( name => 'file', value => $self->file );
139 sub build_substitutions_service {
142 return Bread::Board::Literal->new( name => 'substitutions', value => $self->substitutions );
145 sub build_extensions_service {
148 return Bread::Board::BlockInjection->new(
149 name => 'extensions',
151 return \@{Config::Any->extensions};
156 sub build_prefix_service {
159 return Bread::Board::BlockInjection->new(
162 return Catalyst::Utils::appprefix( shift->param('name') );
164 dependencies => [ depends_on('name') ],
168 sub build_path_service {
171 return Bread::Board::BlockInjection->new(
176 return Catalyst::Utils::env_value( $s->param('name'), 'CONFIG' )
178 || $s->param('name')->path_to( $s->param('prefix') );
180 dependencies => [ depends_on('file'), depends_on('name'), depends_on('prefix') ],
184 sub build_config_service {
187 return Bread::Board::BlockInjection->new(
192 my $v = Data::Visitor::Callback->new(
194 return unless defined $_;
195 return $self->_config_substitutions( $s->param('name'), $s->param('substitutions'), $_ );
199 $v->visit( $s->param('raw_config') );
201 dependencies => [ depends_on('name'), depends_on('raw_config'), depends_on('substitutions') ],
205 sub build_raw_config_service {
208 return Bread::Board::BlockInjection->new(
209 name => 'raw_config',
213 my @global = @{$s->param('global_config')};
214 my @locals = @{$s->param('local_config')};
217 for my $cfg (@global, @locals) {
219 $config = Catalyst::Utils::merge_hashes( $config, $cfg->{$_} );
224 dependencies => [ depends_on('global_config'), depends_on('local_config') ],
228 sub build_global_files_service {
231 return Bread::Board::BlockInjection->new(
232 name => 'global_files',
236 my ( $path, $extension ) = @{$s->param('config_path')};
238 my @extensions = @{$s->param('extensions')};
242 die "Unable to handle files with the extension '${extension}'" unless grep { $_ eq $extension } @extensions;
245 @files = map { "$path.$_" } @extensions;
249 dependencies => [ depends_on('extensions'), depends_on('config_path') ],
253 sub build_local_files_service {
256 return Bread::Board::BlockInjection->new(
257 name => 'local_files',
261 my ( $path, $extension ) = @{$s->param('config_path')};
262 my $suffix = $s->param('config_local_suffix');
264 my @extensions = @{$s->param('extensions')};
268 die "Unable to handle files with the extension '${extension}'" unless grep { $_ eq $extension } @extensions;
269 $path =~ s{\.$extension}{_$suffix.$extension};
272 @files = map { "${path}_${suffix}.$_" } @extensions;
276 dependencies => [ depends_on('extensions'), depends_on('config_path'), depends_on('config_local_suffix') ],
280 sub build_global_config_service {
283 return Bread::Board::BlockInjection->new(
284 name => 'global_config',
288 return Config::Any->load_files({
289 files => $s->param('global_files'),
290 filter => \&_fix_syntax,
292 driver_args => $s->param('driver'),
295 dependencies => [ depends_on('global_files') ],
299 sub build_local_config_service {
302 return Bread::Board::BlockInjection->new(
303 name => 'local_config',
307 return Config::Any->load_files({
308 files => $s->param('local_files'),
309 filter => \&_fix_syntax,
311 driver_args => $s->param('driver'),
314 dependencies => [ depends_on('local_files') ],
318 sub build_config_path_service {
321 return Bread::Board::BlockInjection->new(
322 name => 'config_path',
326 my $path = $s->param('path');
327 my $prefix = $s->param('prefix');
329 my ( $extension ) = ( $path =~ m{\.(.{1,4})$} );
332 $path =~ s{[\/\\]$}{};
336 return [ $path, $extension ];
338 dependencies => [ depends_on('prefix'), depends_on('path') ],
342 sub build_config_local_suffix_service {
345 return Bread::Board::BlockInjection->new(
346 name => 'config_local_suffix',
349 my $suffix = Catalyst::Utils::env_value( $s->param('name'), 'CONFIG_LOCAL_SUFFIX' ) || $self->config_local_suffix;
353 dependencies => [ depends_on('name') ],
361 prefix => $_ eq 'Component' ? '' : $_ . '::',
362 values => delete $config->{ lc $_ } || delete $config->{ $_ }
364 grep { ref $config->{ lc $_ } || ref $config->{ $_ } }
365 qw( Component Model M View V Controller C Plugin )
368 foreach my $comp ( @components ) {
369 my $prefix = $comp->{ prefix };
370 foreach my $element ( keys %{ $comp->{ values } } ) {
371 $config->{ "$prefix$element" } = $comp->{ values }->{ $element };
376 sub _config_substitutions {
377 my ( $self, $name, $subs, $arg ) = @_;
379 $subs->{ HOME } ||= sub { shift->path_to( '' ); };
383 if (! defined($ENV{$v})) {
384 Catalyst::Exception->throw( message =>
385 "Missing environment variable: $v" );
391 $subs->{ path_to } ||= sub { shift->path_to( @_ ); };
392 $subs->{ literal } ||= sub { return $_[ 1 ]; };
393 my $subsre = join( '|', keys %$subs );
395 $arg =~ s{__($subsre)(?:\((.+?)\))?__}{ $subs->{ $1 }->( $name, $2 ? split( /,/, $2 ) : () ) }eg;
399 sub get_component_from_sub_container {
400 my ( $self, $sub_container_name, $name, $c, @args ) = @_;
402 my $sub_container = $self->get_sub_container( $sub_container_name );
405 my $default = $sub_container->default_component;
407 return $sub_container->get_component( $default, $c, @args )
408 if $default && $sub_container->has_service( $default );
410 # this is never a controller, so this is safe
411 $c->log->warn( "Calling \$c->$sub_container_name() is not supported unless you specify one of:" );
412 $c->log->warn( "* \$c->config(default_$sub_container_name => 'the name of the default $sub_container_name to use')" );
413 $c->log->warn( "* \$c->stash->{current_$sub_container_name} # the name of the view to use for this request" );
414 $c->log->warn( "* \$c->stash->{current_${sub_container_name}_instance} # the instance of the $sub_container_name to use for this request" );
419 return $sub_container->get_component_regexp( $name, $c, @args )
422 return $sub_container->get_component( $name, $c, @args )
423 if $sub_container->has_service( $name );
426 "Attempted to use $sub_container_name '$name', " .
427 "but it does not exist"
434 my ( $self, $component, @args ) = @_;
437 my $query = ref $component
442 for my $subcontainer_name (qw/model view controller/) {
443 my $subcontainer = $self->get_sub_container($subcontainer_name);
444 my @components = $subcontainer->get_service_list;
445 @result = grep { m{$component} } @components;
447 return map { $subcontainer->get_component( $_, @args ) } @result
451 # it expects an empty list on failed searches
464 Catalyst::Container - IOC for Catalyst components
468 =head2 build_model_subcontainer
470 =head2 build_view_subcontainer
472 =head2 build_controller_subcontainer
474 =head2 build_name_service
476 =head2 build_driver_service
478 =head2 build_file_service
480 =head2 build_substitutions_service
482 =head2 build_extensions_service
484 =head2 build_prefix_service
486 =head2 build_path_service
488 =head2 build_config_service
490 =head2 build_raw_config_service
492 =head2 build_global_files_service
494 =head2 build_local_files_service
496 =head2 build_global_config_service
498 =head2 build_local_config_service
500 =head2 build_config_path_service
502 =head2 build_config_local_suffix_service
504 =head2 get_component_from_sub_container
506 =head2 find_component
510 =head2 _config_substitutions
514 Catalyst Contributors, see Catalyst.pm
518 This library is free software. You can redistribute it and/or modify it under
519 the same terms as Perl itself.