1 package Catalyst::IOC::Container;
5 use Data::Visitor::Callback;
6 use Catalyst::Utils ();
7 use List::Util qw(first);
8 use Devel::InnerPackage ();
9 use Hash::Util qw/lock_hash/;
10 use MooseX::Types::LoadableClass qw/ LoadableClass /;
12 use Catalyst::IOC::BlockInjection;
13 use Catalyst::IOC::ConstructorInjection;
14 use Module::Pluggable::Object ();
15 use namespace::autoclean;
17 extends 'Bread::Board::Container';
19 has config_local_suffix => (
28 default => sub { +{} },
37 has substitutions => (
40 default => sub { +{} },
43 has application_name => (
49 has sub_container_class => (
53 default => 'Catalyst::IOC::SubContainer',
55 new_sub_container => 'new',
60 my ( $self, $params ) = @_;
63 $self->${\"build_${_}_service"}
84 my $config = $self->resolve( service => 'config' );
86 # don't force default_component to be undef if the config wasn't set
87 my @default_view = $config->{default_view}
88 ? ( default_component => $config->{default_view} )
91 my @default_model = $config->{default_model}
92 ? ( default_component => $config->{default_model} )
96 $self->add_sub_container(
97 $self->build_component_subcontainer
100 $self->add_sub_container(
101 $self->build_controller_subcontainer
104 $self->add_sub_container(
105 $self->build_view_subcontainer( @default_view )
108 $self->add_sub_container(
109 $self->build_model_subcontainer( @default_model )
114 my $class = ref $self;
115 warn("In build " . ${ $class . '::customise_container' });
116 ${ $class . '::customise_container' }->($self);
120 sub build_model_subcontainer {
123 return $self->new_sub_container( @_,
128 sub build_view_subcontainer {
131 return $self->new_sub_container( @_,
136 sub build_controller_subcontainer {
139 return $self->new_sub_container(
140 name => 'controller',
144 sub build_component_subcontainer {
147 return Bread::Board::Container->new(
152 sub build_application_name_service {
155 return Bread::Board::Literal->new( name => 'application_name', value => $self->application_name );
158 sub build_driver_service {
161 return Bread::Board::Literal->new( name => 'driver', value => $self->driver );
164 sub build_file_service {
167 return Bread::Board::Literal->new( name => 'file', value => $self->file );
170 sub build_substitutions_service {
173 return Bread::Board::Literal->new( name => 'substitutions', value => $self->substitutions );
176 sub build_extensions_service {
179 return Bread::Board::BlockInjection->new(
180 lifecycle => 'Singleton',
181 name => 'extensions',
183 return \@{Config::Any->extensions};
188 sub build_prefix_service {
191 return Bread::Board::BlockInjection->new(
192 lifecycle => 'Singleton',
195 return Catalyst::Utils::appprefix( shift->param('application_name') );
197 dependencies => [ depends_on('application_name') ],
201 sub build_path_service {
204 return Bread::Board::BlockInjection->new(
205 lifecycle => 'Singleton',
210 return Catalyst::Utils::env_value( $s->param('application_name'), 'CONFIG' )
212 || $s->param('application_name')->path_to( $s->param('prefix') );
214 dependencies => [ depends_on('file'), depends_on('application_name'), depends_on('prefix') ],
218 sub build_config_service {
221 return Bread::Board::BlockInjection->new(
222 lifecycle => 'Singleton',
227 my $v = Data::Visitor::Callback->new(
229 return unless defined $_;
230 return $self->_config_substitutions( $s->param('application_name'), $s->param('substitutions'), $_ );
234 $v->visit( $s->param('raw_config') );
236 dependencies => [ depends_on('application_name'), depends_on('raw_config'), depends_on('substitutions') ],
240 sub build_raw_config_service {
243 return Bread::Board::BlockInjection->new(
244 lifecycle => 'Singleton',
245 name => 'raw_config',
249 my @global = @{$s->param('global_config')};
250 my @locals = @{$s->param('local_config')};
252 my $config = $s->param('class_config');
254 for my $cfg (@global, @locals) {
256 $config = Catalyst::Utils::merge_hashes( $config, $cfg->{$_} );
262 dependencies => [ depends_on('global_config'), depends_on('local_config'), depends_on('class_config') ],
266 sub build_global_files_service {
269 return Bread::Board::BlockInjection->new(
270 lifecycle => 'Singleton',
271 name => 'global_files',
275 my ( $path, $extension ) = @{$s->param('config_path')};
277 my @extensions = @{$s->param('extensions')};
281 die "Unable to handle files with the extension '${extension}'" unless grep { $_ eq $extension } @extensions;
284 @files = map { "$path.$_" } @extensions;
288 dependencies => [ depends_on('extensions'), depends_on('config_path') ],
292 sub build_local_files_service {
295 return Bread::Board::BlockInjection->new(
296 lifecycle => 'Singleton',
297 name => 'local_files',
301 my ( $path, $extension ) = @{$s->param('config_path')};
302 my $suffix = $s->param('config_local_suffix');
304 my @extensions = @{$s->param('extensions')};
308 die "Unable to handle files with the extension '${extension}'" unless grep { $_ eq $extension } @extensions;
309 $path =~ s{\.$extension}{_$suffix.$extension};
312 @files = map { "${path}_${suffix}.$_" } @extensions;
316 dependencies => [ depends_on('extensions'), depends_on('config_path'), depends_on('config_local_suffix') ],
320 sub build_class_config_service {
323 return Bread::Board::BlockInjection->new(
324 lifecycle => 'Singleton',
325 name => 'class_config',
328 my $app = $s->param('application_name');
330 # Container might be called outside Catalyst context
331 return {} unless Class::MOP::is_class_loaded($app);
333 # config might not have been defined
334 return $app->config || {};
336 dependencies => [ depends_on('application_name') ],
340 sub build_global_config_service {
343 return Bread::Board::BlockInjection->new(
344 lifecycle => 'Singleton',
345 name => 'global_config',
349 return Config::Any->load_files({
350 files => $s->param('global_files'),
351 filter => \&_fix_syntax,
353 driver_args => $s->param('driver'),
356 dependencies => [ depends_on('global_files') ],
360 sub build_local_config_service {
363 return Bread::Board::BlockInjection->new(
364 lifecycle => 'Singleton',
365 name => 'local_config',
369 return Config::Any->load_files({
370 files => $s->param('local_files'),
371 filter => \&_fix_syntax,
373 driver_args => $s->param('driver'),
376 dependencies => [ depends_on('local_files') ],
380 sub build_config_path_service {
383 return Bread::Board::BlockInjection->new(
384 lifecycle => 'Singleton',
385 name => 'config_path',
389 my $path = $s->param('path');
390 my $prefix = $s->param('prefix');
392 my ( $extension ) = ( $path =~ m{\.(.{1,4})$} );
395 $path =~ s{[\/\\]$}{};
399 return [ $path, $extension ];
401 dependencies => [ depends_on('prefix'), depends_on('path') ],
405 sub build_config_local_suffix_service {
408 return Bread::Board::BlockInjection->new(
409 lifecycle => 'Singleton',
410 name => 'config_local_suffix',
413 my $suffix = Catalyst::Utils::env_value( $s->param('application_name'), 'CONFIG_LOCAL_SUFFIX' ) || $self->config_local_suffix;
417 dependencies => [ depends_on('application_name') ],
421 sub build_locate_components_service {
424 return Bread::Board::BlockInjection->new(
425 lifecycle => 'Singleton',
426 name => 'locate_components',
429 my $class = $s->param('application_name');
430 my $config = $s->param('config')->{ setup_components };
432 Catalyst::Exception->throw(
433 qq{You are using search_extra config option. That option is\n} .
434 qq{deprecated, please refer to the documentation for\n} .
435 qq{other ways of achieving the same results.\n}
436 ) if delete $config->{ search_extra };
438 my @paths = qw( ::Controller ::C ::Model ::M ::View ::V );
440 my $locator = Module::Pluggable::Object->new(
441 search_path => [ map { s/^(?=::)/$class/; $_; } @paths ],
445 return [ $locator->plugins ];
447 dependencies => [ depends_on('application_name'), depends_on('config') ],
451 sub setup_components {
453 warn("Setting up default components");
454 my $class = $self->resolve( service => 'application_name' );
455 my @comps = @{ $self->resolve( service => 'locate_components' ) };
456 my %comps = map { $_ => 1 } @comps;
457 my $deprecatedcatalyst_component_names = 0;
459 for my $component ( @comps ) {
461 # We pass ignore_loaded here so that overlay files for (e.g.)
462 # Model::DBI::Schema sub-classes are loaded - if it's in @comps
463 # we know M::P::O found a file on disk so this is safe
465 Catalyst::Utils::ensure_class_loaded( $component, { ignore_loaded => 1 } );
468 for my $component (@comps) {
469 $self->add_component( $component );
470 # FIXME - $instance->expand_modules() is broken
471 my @expanded_components = $self->expand_component_module( $component );
474 !$deprecatedcatalyst_component_names &&
475 ($deprecatedcatalyst_component_names = $component =~ m/::[CMV]::/) ||
476 ($deprecatedcatalyst_component_names = grep { /::[CMV]::/ } @expanded_components)
478 # FIXME - should I be calling warn here?
479 # Maybe it's time to remove it, or become fatal
480 $class->log->warn(qq{Your application is using the deprecated ::[MVC]:: type naming scheme.\n}.
481 qq{Please switch your class names to ::Model::, ::View:: and ::Controller: as appropriate.\n}
485 for my $component (@expanded_components) {
486 $self->add_component( $component )
487 unless $comps{$component};
496 prefix => $_ eq 'Component' ? '' : $_ . '::',
497 values => delete $config->{ lc $_ } || delete $config->{ $_ }
499 grep { ref $config->{ lc $_ } || ref $config->{ $_ } }
500 qw( Component Model M View V Controller C Plugin )
503 foreach my $comp ( @components ) {
504 my $prefix = $comp->{ prefix };
505 foreach my $element ( keys %{ $comp->{ values } } ) {
506 $config->{ "$prefix$element" } = $comp->{ values }->{ $element };
511 sub _config_substitutions {
512 my ( $self, $name, $subs, $arg ) = @_;
514 $subs->{ HOME } ||= sub { shift->path_to( '' ); };
518 if (! defined($ENV{$v})) {
519 Catalyst::Exception->throw( message =>
520 "Missing environment variable: $v" );
526 $subs->{ path_to } ||= sub { shift->path_to( @_ ); };
527 $subs->{ literal } ||= sub { return $_[ 1 ]; };
528 my $subsre = join( '|', keys %$subs );
530 $arg =~ s{__($subsre)(?:\((.+?)\))?__}{ $subs->{ $1 }->( $name, $2 ? split( /,/, $2 ) : () ) }eg;
534 sub get_component_from_sub_container {
535 my ( $self, $sub_container_name, $name, $c, @args ) = @_;
537 my $sub_container = $self->get_sub_container( $sub_container_name );
540 my $default = $sub_container->default_component;
542 return $sub_container->get_component( $default, $c, @args )
543 if $default && $sub_container->has_service( $default );
545 # FIXME - should I be calling $c->log->warn here?
546 # this is never a controller, so this is safe
547 $c->log->warn( "Calling \$c->$sub_container_name() is not supported unless you specify one of:" );
548 $c->log->warn( "* \$c->config(default_$sub_container_name => 'the name of the default $sub_container_name to use')" );
549 $c->log->warn( "* \$c->stash->{current_$sub_container_name} # the name of the view to use for this request" );
550 $c->log->warn( "* \$c->stash->{current_${sub_container_name}_instance} # the instance of the $sub_container_name to use for this request" );
555 return $sub_container->get_component_regexp( $name, $c, @args )
558 return $sub_container->get_component( $name, $c, @args )
559 if $sub_container->has_service( $name );
562 "Attempted to use $sub_container_name '$name', " .
563 "but it does not exist"
570 my ( $self, $component, @args ) = @_;
571 my ( $type, $name ) = _get_component_type_name($component);
574 return $self->get_component_from_sub_container(
578 my $query = ref $component
583 for my $subcontainer_name (qw/model view controller/) {
584 my $subcontainer = $self->get_sub_container( $subcontainer_name );
585 my @components = $subcontainer->get_service_list;
586 @result = grep { m{$component} } @components;
588 return map { $subcontainer->get_component( $_, @args ) } @result
592 # one last search for things like $c->comp(qr/::M::/)
593 @result = $self->_find_component_regexp(
595 ) if !@result and ref $component;
597 # it expects an empty list on failed searches
601 sub _find_component_regexp {
602 my ( $self, $component, $ctx, @args ) = @_;
605 my @components = grep { m{$component} } keys %{ $self->get_all_components($ctx) };
608 my ($type, $name) = _get_component_type_name($_);
610 push @result, $self->get_component_from_sub_container(
611 $type, $name, $ctx, @args
618 sub get_all_components {
619 my ($self, $class) = @_;
622 # FIXME - if we're getting from these containers, we need to either:
623 # - pass 'ctx' and 'accept_context_args' OR
624 # - make these params optional
625 foreach my $type (qw/model view controller /) {
626 my $container = $self->get_sub_container($type);
628 for my $component ($container->get_service_list) {
629 my $comp_service = $container->get_service($component);
631 $components{$comp_service->catalyst_component_name} = $comp_service->get(ctx => $class);
635 return lock_hash %components;
639 my ( $self, $component ) = @_;
640 my ( $type, $name ) = _get_component_type_name($component);
644 # The 'component' sub-container will create the object, and store it's
645 # instance, which, by default, will live throughout the application.
646 # The model/view/controller sub-containers only reference the instance
647 # held in the aforementioned sub-container, and execute the ACCEPT_CONTEXT
648 # sub every time they are called, when it exists.
649 my $instance_container = $self->get_sub_container('component');
650 my $accept_context_container = $self->get_sub_container($type);
652 # Custom containers might have added the service already
653 # We don't want to override that
654 return if $accept_context_container->has_service( $name );
656 my $component_service_name = "${type}_${name}";
658 $instance_container->add_service(
659 Catalyst::IOC::ConstructorInjection->new(
660 name => $component_service_name,
661 catalyst_component_name => $component,
663 lifecycle => 'Singleton',
665 depends_on( '/application_name' ),
670 $accept_context_container->add_service(
671 Catalyst::IOC::BlockInjection->new(
673 catalyst_component_name => $component,
675 depends_on( "/component/$component_service_name" ),
677 block => sub { shift->param($component_service_name) },
682 # FIXME: should this sub exist?
683 # should it be moved to Catalyst::Utils,
684 # or replaced by something already existing there?
685 sub _get_component_type_name {
686 my ( $component ) = @_;
689 while ( !$result and (my $index = index $component, '::') > 0 ) {
690 my $type = lc substr $component, 0, $index;
691 $component = substr $component, $index + 2;
692 $result = first { $type eq $_ or $type eq substr($_, 0, 1) }
693 qw{ model view controller };
696 return ($result, $component);
699 sub expand_component_module {
700 my ( $class, $module ) = @_;
701 return Devel::InnerPackage::list_packages( $module );
712 Catalyst::Container - IOC for Catalyst components
720 =head1 Building Containers
722 =head2 build_component_subcontainer
724 Container that stores all components, i.e. all models, views and controllers
725 together. Each service is an instance of the actual component, and by default
726 it lives while the application is running. Retrieving components from this
727 subcontainer will instantiate the component, if it hasn't been instantiated
728 already, but will not execute ACCEPT_CONTEXT.
730 =head2 build_model_subcontainer
732 Container that stores references for all models that are inside the components
733 subcontainer. Retrieving a model triggers ACCEPT_CONTEXT, if it exists.
735 =head2 build_view_subcontainer
737 Same as L<build_model_subcontainer>, but for views.
739 =head2 build_controller_subcontainer
741 Same as L<build_model_subcontainer>, but for controllers.
743 =head1 Building Services
745 =head2 build_application_name_service
747 Name of the application (such as MyApp).
749 =head2 build_driver_service
751 Config options passed directly to the driver being used.
753 =head2 build_file_service
757 =head2 build_substitutions_service
759 This method substitutes macros found with calls to a function. There are a
760 number of default macros:
764 =item * C<__HOME__> - replaced with C<$c-E<gt>path_to('')>
766 =item * C<__ENV(foo)__> - replaced with the value of C<$ENV{foo}>
768 =item * C<__path_to(foo/bar)__> - replaced with C<$c-E<gt>path_to('foo/bar')>
770 =item * C<__literal(__FOO__)__> - leaves __FOO__ alone (allows you to use
771 C<__DATA__> as a config value, for example)
775 The parameter list is split on comma (C<,>). You can override this method to
776 do your own string munging, or you can define your own macros in
777 C<< <MyApp->config( 'Plugin::ConfigLoader' => { substitutions => { ... } } ) >>.
780 MyApp->config( 'Plugin::ConfigLoader' => {
782 baz => sub { my $c = shift; qux( @_ ); },
786 The above will respond to C<__baz(x,y)__> in config strings.
788 =head2 build_extensions_service
790 Config::Any's available config file extensions (e.g. xml, json, pl, etc).
792 =head2 build_prefix_service
794 The prefix, based on the application name, that will be used to lookup the
795 config files (which will be in the format $prefix.$extension). If the app is
796 MyApp::Foo, the prefix will be myapp_foo.
798 =head2 build_path_service
800 The path to the config file (or environment variable, if defined).
802 =head2 build_config_service
804 The resulting configuration for the application, after it has successfully
805 been loaded, and all substitutions have been made.
807 =head2 build_raw_config_service
809 The merge of local_config and global_config hashes, before substitutions.
811 =head2 build_global_files_service
813 Gets all files for config that don't have the local_suffix, such as myapp.conf.
815 =head2 build_local_files_service
817 Gets all files for config that have the local_suffix, such as myapp_local.conf.
819 =head2 build_global_config_service
821 Reads config from global_files.
823 =head2 build_local_config_service
825 Reads config from local_files.
827 =head2 build_class_config_service
829 Reads config set from the application's class attribute config,
830 i.e. MyApp->config( name => 'MyApp', ... )
832 =head2 build_config_path_service
834 Splits the path to the config file, and returns on array ref containing
835 the path to the config file minus the extension in the first position,
836 and the extension in the second.
838 =head2 build_config_local_suffix_service
840 Determines the suffix of files used to override the main config. By default
841 this value is C<local>, which will load C<myapp_local.conf>. The suffix can
842 be specified in the following order of preference:
846 =item * C<$ENV{ MYAPP_CONFIG_LOCAL_SUFFIX }>
848 =item * C<$ENV{ CATALYST_CONFIG_LOCAL_SUFFIX }>
852 The first one of these values found replaces the default of C<local> in the
853 name of the local config file to be loaded.
855 For example, if C< $ENV{ MYAPP_CONFIG_LOCAL_SUFFIX }> is set to C<testing>,
856 ConfigLoader will try and load C<myapp_testing.conf> instead of
859 =head2 build_locate_components_service
861 This method is meant to provide a list of component modules that should be
862 setup for the application. By default, it will use L<Module::Pluggable>.
864 Specify a C<setup_components> config option to pass additional options directly
865 to L<Module::Pluggable>.
869 =head2 get_component_from_sub_container($sub_container, $name, $c, @args)
871 Looks for components in a given subcontainer (such as controller, model or
872 view), and returns the searched component. If $name is undef, it returns the
873 default component (such as default_view, if $sub_container is 'view'). If
874 $name is a regexp, it returns an array of matching components. Otherwise, it
875 looks for the component with name $name.
877 =head2 get_all_components
879 Fetches all the components, in each of the sub_containers model, view and
880 controller, and returns a readonly hash. The keys are the class names, and
881 the values are the blessed objects. This is what is returned by $c->components.
885 Adds a component to the appropriate subcontainer. The subcontainer is guessed
886 by the component name given.
888 =head2 find_component
890 Searches for components in all containers. If $component is the full class
891 name, the subcontainer is guessed, and it gets the searched component in there.
892 Otherwise, it looks for a component with that name in all subcontainers. If
893 $component is a regexp it calls _find_component_regexp and matches all
894 components against that regexp.
896 =head2 expand_component_module
898 Components found by C<locate_components> will be passed to this method, which
899 is expected to return a list of component (package) names to be set up.
901 =head2 setup_components
905 Catalyst Contributors, see Catalyst.pm
909 This library is free software. You can redistribute it and/or modify it under
910 the same terms as Perl itself.