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 Scalar::Util qw/refaddr/;
13 use Catalyst::IOC::BlockInjection;
14 use Catalyst::IOC::ConstructorInjection;
15 use Module::Pluggable::Object ();
16 use namespace::autoclean;
18 extends 'Bread::Board::Container';
20 has config_local_suffix => (
29 default => sub { +{} },
38 has substitutions => (
41 default => sub { +{} },
44 has application_name => (
50 has sub_container_class => (
54 default => 'Catalyst::IOC::SubContainer',
56 new_sub_container => 'new',
61 my ( $self, $params ) = @_;
64 $self->${\"build_${_}_service"}
85 my $config = $self->resolve( service => 'config' );
87 # don't force default_component to be undef if the config wasn't set
88 my @default_view = $config->{default_view}
89 ? ( default_component => $config->{default_view} )
92 my @default_model = $config->{default_model}
93 ? ( default_component => $config->{default_model} )
97 $self->add_sub_container(
98 $self->build_component_subcontainer
101 $self->add_sub_container(
102 $self->build_controller_subcontainer
105 $self->add_sub_container(
106 $self->build_view_subcontainer( @default_view )
109 $self->add_sub_container(
110 $self->build_model_subcontainer( @default_model )
116 my $class = ref $self;
117 warn("In build $class");
118 ${ $class . '::customise_container' }->($self)
119 if ${ $class . '::customise_container' };
123 sub build_model_subcontainer {
126 return $self->new_sub_container( @_,
131 sub build_view_subcontainer {
134 return $self->new_sub_container( @_,
139 sub build_controller_subcontainer {
142 return $self->new_sub_container(
143 name => 'controller',
147 sub build_component_subcontainer {
150 return Bread::Board::Container->new(
155 sub build_application_name_service {
158 return Bread::Board::Literal->new( name => 'application_name', value => $self->application_name );
161 sub build_driver_service {
164 return Bread::Board::Literal->new( name => 'driver', value => $self->driver );
167 sub build_file_service {
170 return Bread::Board::Literal->new( name => 'file', value => $self->file );
173 sub build_substitutions_service {
176 return Bread::Board::Literal->new( name => 'substitutions', value => $self->substitutions );
179 sub build_extensions_service {
182 return Bread::Board::BlockInjection->new(
183 lifecycle => 'Singleton',
184 name => 'extensions',
186 return \@{Config::Any->extensions};
191 sub build_prefix_service {
194 return Bread::Board::BlockInjection->new(
195 lifecycle => 'Singleton',
198 return Catalyst::Utils::appprefix( shift->param('application_name') );
200 dependencies => [ depends_on('application_name') ],
204 sub build_path_service {
207 return Bread::Board::BlockInjection->new(
208 lifecycle => 'Singleton',
213 return Catalyst::Utils::env_value( $s->param('application_name'), 'CONFIG' )
215 || $s->param('application_name')->path_to( $s->param('prefix') );
217 dependencies => [ depends_on('file'), depends_on('application_name'), depends_on('prefix') ],
221 sub build_config_service {
224 return Bread::Board::BlockInjection->new(
225 lifecycle => 'Singleton',
230 my $v = Data::Visitor::Callback->new(
232 return unless defined $_;
233 return $self->_config_substitutions( $s->param('application_name'), $s->param('substitutions'), $_ );
237 $v->visit( $s->param('raw_config') );
239 dependencies => [ depends_on('application_name'), depends_on('raw_config'), depends_on('substitutions') ],
243 sub build_raw_config_service {
246 return Bread::Board::BlockInjection->new(
247 lifecycle => 'Singleton',
248 name => 'raw_config',
252 my @global = @{$s->param('global_config')};
253 my @locals = @{$s->param('local_config')};
255 my $config = $s->param('class_config');
257 for my $cfg (@global, @locals) {
259 $config = Catalyst::Utils::merge_hashes( $config, $cfg->{$_} );
265 dependencies => [ depends_on('global_config'), depends_on('local_config'), depends_on('class_config') ],
269 sub build_global_files_service {
272 return Bread::Board::BlockInjection->new(
273 lifecycle => 'Singleton',
274 name => 'global_files',
278 my ( $path, $extension ) = @{$s->param('config_path')};
280 my @extensions = @{$s->param('extensions')};
284 die "Unable to handle files with the extension '${extension}'" unless grep { $_ eq $extension } @extensions;
287 @files = map { "$path.$_" } @extensions;
291 dependencies => [ depends_on('extensions'), depends_on('config_path') ],
295 sub build_local_files_service {
298 return Bread::Board::BlockInjection->new(
299 lifecycle => 'Singleton',
300 name => 'local_files',
304 my ( $path, $extension ) = @{$s->param('config_path')};
305 my $suffix = $s->param('config_local_suffix');
307 my @extensions = @{$s->param('extensions')};
311 die "Unable to handle files with the extension '${extension}'" unless grep { $_ eq $extension } @extensions;
312 $path =~ s{\.$extension}{_$suffix.$extension};
315 @files = map { "${path}_${suffix}.$_" } @extensions;
319 dependencies => [ depends_on('extensions'), depends_on('config_path'), depends_on('config_local_suffix') ],
323 sub build_class_config_service {
326 return Bread::Board::BlockInjection->new(
327 lifecycle => 'Singleton',
328 name => 'class_config',
331 my $app = $s->param('application_name');
333 # Container might be called outside Catalyst context
334 return {} unless Class::MOP::is_class_loaded($app);
336 # config might not have been defined
337 return $app->config || {};
339 dependencies => [ depends_on('application_name') ],
343 sub build_global_config_service {
346 return Bread::Board::BlockInjection->new(
347 lifecycle => 'Singleton',
348 name => 'global_config',
352 return Config::Any->load_files({
353 files => $s->param('global_files'),
354 filter => \&_fix_syntax,
356 driver_args => $s->param('driver'),
359 dependencies => [ depends_on('global_files') ],
363 sub build_local_config_service {
366 return Bread::Board::BlockInjection->new(
367 lifecycle => 'Singleton',
368 name => 'local_config',
372 return Config::Any->load_files({
373 files => $s->param('local_files'),
374 filter => \&_fix_syntax,
376 driver_args => $s->param('driver'),
379 dependencies => [ depends_on('local_files') ],
383 sub build_config_path_service {
386 return Bread::Board::BlockInjection->new(
387 lifecycle => 'Singleton',
388 name => 'config_path',
392 my $path = $s->param('path');
393 my $prefix = $s->param('prefix');
395 my ( $extension ) = ( $path =~ m{\.(.{1,4})$} );
398 $path =~ s{[\/\\]$}{};
402 return [ $path, $extension ];
404 dependencies => [ depends_on('prefix'), depends_on('path') ],
408 sub build_config_local_suffix_service {
411 return Bread::Board::BlockInjection->new(
412 lifecycle => 'Singleton',
413 name => 'config_local_suffix',
416 my $suffix = Catalyst::Utils::env_value( $s->param('application_name'), 'CONFIG_LOCAL_SUFFIX' ) || $self->config_local_suffix;
420 dependencies => [ depends_on('application_name') ],
424 sub build_locate_components_service {
427 return Bread::Board::BlockInjection->new(
428 lifecycle => 'Singleton',
429 name => 'locate_components',
432 my $class = $s->param('application_name');
433 my $config = $s->param('config')->{ setup_components };
435 Catalyst::Exception->throw(
436 qq{You are using search_extra config option. That option is\n} .
437 qq{deprecated, please refer to the documentation for\n} .
438 qq{other ways of achieving the same results.\n}
439 ) if delete $config->{ search_extra };
441 my @paths = qw( ::Controller ::C ::Model ::M ::View ::V );
443 my $locator = Module::Pluggable::Object->new(
444 search_path => [ map { s/^(?=::)/$class/; $_; } @paths ],
448 return [ $locator->plugins ];
450 dependencies => [ depends_on('application_name'), depends_on('config') ],
454 sub setup_components {
456 warn("Setting up default components");
457 my $class = $self->resolve( service => 'application_name' );
458 my @comps = @{ $self->resolve( service => 'locate_components' ) };
459 my %comps = map { $_ => 1 } @comps;
460 my $deprecatedcatalyst_component_names = 0;
462 my $app_locate_components_addr = refaddr(
463 $class->can('locate_components')
465 my $cat_locate_components_addr = refaddr(
466 Catalyst->can('locate_components')
469 if ($app_locate_components_addr != $cat_locate_components_addr) {
470 $class->log->warn(qq{You have overridden locate_components. That } .
471 qq{no longer works. Please refer to the documentation to achieve } .
472 qq{similar results.\n}
476 for my $component ( @comps ) {
478 # We pass ignore_loaded here so that overlay files for (e.g.)
479 # Model::DBI::Schema sub-classes are loaded - if it's in @comps
480 # we know M::P::O found a file on disk so this is safe
482 Catalyst::Utils::ensure_class_loaded( $component, { ignore_loaded => 1 } );
485 for my $component (@comps) {
486 $self->add_component( $component );
487 # FIXME - $instance->expand_modules() is broken
488 my @expanded_components = $self->expand_component_module( $component );
491 !$deprecatedcatalyst_component_names &&
492 ($deprecatedcatalyst_component_names = $component =~ m/::[CMV]::/) ||
493 ($deprecatedcatalyst_component_names = grep { /::[CMV]::/ } @expanded_components)
495 # FIXME - should I be calling warn here?
496 # Maybe it's time to remove it, or become fatal
497 $class->log->warn(qq{Your application is using the deprecated ::[MVC]:: type naming scheme.\n}.
498 qq{Please switch your class names to ::Model::, ::View:: and ::Controller: as appropriate.\n}
502 for my $component (@expanded_components) {
503 $self->add_component( $component )
504 unless $comps{$component};
513 prefix => $_ eq 'Component' ? '' : $_ . '::',
514 values => delete $config->{ lc $_ } || delete $config->{ $_ }
516 grep { ref $config->{ lc $_ } || ref $config->{ $_ } }
517 qw( Component Model M View V Controller C Plugin )
520 foreach my $comp ( @components ) {
521 my $prefix = $comp->{ prefix };
522 foreach my $element ( keys %{ $comp->{ values } } ) {
523 $config->{ "$prefix$element" } = $comp->{ values }->{ $element };
528 sub _config_substitutions {
529 my ( $self, $name, $subs, $arg ) = @_;
531 $subs->{ HOME } ||= sub { shift->path_to( '' ); };
535 if (! defined($ENV{$v})) {
536 Catalyst::Exception->throw( message =>
537 "Missing environment variable: $v" );
543 $subs->{ path_to } ||= sub { shift->path_to( @_ ); };
544 $subs->{ literal } ||= sub { return $_[ 1 ]; };
545 my $subsre = join( '|', keys %$subs );
547 $arg =~ s{__($subsre)(?:\((.+?)\))?__}{ $subs->{ $1 }->( $name, $2 ? split( /,/, $2 ) : () ) }eg;
551 sub get_component_from_sub_container {
552 my ( $self, $sub_container_name, $name, $c, @args ) = @_;
554 my $sub_container = $self->get_sub_container( $sub_container_name );
557 my $default = $sub_container->default_component;
559 return $sub_container->get_component( $default, $c, @args )
560 if $default && $sub_container->has_service( $default );
562 # FIXME - should I be calling $c->log->warn here?
563 # this is never a controller, so this is safe
564 $c->log->warn( "Calling \$c->$sub_container_name() is not supported unless you specify one of:" );
565 $c->log->warn( "* \$c->config(default_$sub_container_name => 'the name of the default $sub_container_name to use')" );
566 $c->log->warn( "* \$c->stash->{current_$sub_container_name} # the name of the view to use for this request" );
567 $c->log->warn( "* \$c->stash->{current_${sub_container_name}_instance} # the instance of the $sub_container_name to use for this request" );
572 return $sub_container->get_component_regexp( $name, $c, @args )
575 return $sub_container->get_component( $name, $c, @args )
576 if $sub_container->has_service( $name );
579 "Attempted to use $sub_container_name '$name', " .
580 "but it does not exist"
587 my ( $self, $component, @args ) = @_;
588 my ( $type, $name ) = _get_component_type_name($component);
591 return $self->get_component_from_sub_container(
595 my $query = ref $component
600 for my $subcontainer_name (qw/model view controller/) {
601 my $subcontainer = $self->get_sub_container( $subcontainer_name );
602 my @components = $subcontainer->get_service_list;
603 @result = grep { m{$component} } @components;
605 return map { $subcontainer->get_component( $_, @args ) } @result
609 # one last search for things like $c->comp(qr/::M::/)
610 @result = $self->_find_component_regexp(
612 ) if !@result and ref $component;
614 # it expects an empty list on failed searches
618 sub _find_component_regexp {
619 my ( $self, $component, $ctx, @args ) = @_;
622 my @components = grep { m{$component} } keys %{ $self->get_all_components($ctx) };
625 my ($type, $name) = _get_component_type_name($_);
627 push @result, $self->get_component_from_sub_container(
628 $type, $name, $ctx, @args
635 sub get_all_components {
636 my ($self, $class) = @_;
639 # FIXME - if we're getting from these containers, we need to either:
640 # - pass 'ctx' and 'accept_context_args' OR
641 # - make these params optional
642 # big problem when setting up the dispatcher - this method is called
643 # as $container->get_all_components('MyApp'). What to do with Request
645 foreach my $type (qw/model view controller /) {
646 my $container = $self->get_sub_container($type);
648 for my $component ($container->get_service_list) {
649 my $comp_service = $container->get_service($component);
651 $components{$comp_service->catalyst_component_name} = $comp_service->get(ctx => $class);
655 return lock_hash %components;
659 my ( $self, $component ) = @_;
660 my ( $type, $name ) = _get_component_type_name($component);
664 # The 'component' sub-container will create the object, and store it's
665 # instance, which, by default, will live throughout the application.
666 # The model/view/controller sub-containers only reference the instance
667 # held in the aforementioned sub-container, and execute the ACCEPT_CONTEXT
668 # sub every time they are called, when it exists.
669 my $instance_container = $self->get_sub_container('component');
670 my $accept_context_container = $self->get_sub_container($type);
672 # Custom containers might have added the service already
673 # We don't want to override that
674 return if $accept_context_container->has_service( $name );
676 my $component_service_name = "${type}_${name}";
678 $instance_container->add_service(
679 Catalyst::IOC::ConstructorInjection->new(
680 name => $component_service_name,
681 catalyst_component_name => $component,
683 lifecycle => 'Singleton',
685 depends_on( '/application_name' ),
690 $accept_context_container->add_service(
691 Catalyst::IOC::BlockInjection->new(
693 catalyst_component_name => $component,
695 depends_on( "/component/$component_service_name" ),
697 block => sub { shift->param($component_service_name) },
702 # FIXME: should this sub exist?
703 # should it be moved to Catalyst::Utils,
704 # or replaced by something already existing there?
705 sub _get_component_type_name {
706 my ( $component ) = @_;
709 while ( !$result and (my $index = index $component, '::') > 0 ) {
710 my $type = lc substr $component, 0, $index;
711 $component = substr $component, $index + 2;
712 $result = first { $type eq $_ or $type eq substr($_, 0, 1) }
713 qw{ model view controller };
716 return ($result, $component);
719 sub expand_component_module {
720 my ( $class, $module ) = @_;
721 return Devel::InnerPackage::list_packages( $module );
732 Catalyst::Container - IOC for Catalyst components
740 =head1 Building Containers
742 =head2 build_component_subcontainer
744 Container that stores all components, i.e. all models, views and controllers
745 together. Each service is an instance of the actual component, and by default
746 it lives while the application is running. Retrieving components from this
747 subcontainer will instantiate the component, if it hasn't been instantiated
748 already, but will not execute ACCEPT_CONTEXT.
750 =head2 build_model_subcontainer
752 Container that stores references for all models that are inside the components
753 subcontainer. Retrieving a model triggers ACCEPT_CONTEXT, if it exists.
755 =head2 build_view_subcontainer
757 Same as L<build_model_subcontainer>, but for views.
759 =head2 build_controller_subcontainer
761 Same as L<build_model_subcontainer>, but for controllers.
763 =head1 Building Services
765 =head2 build_application_name_service
767 Name of the application (such as MyApp).
769 =head2 build_driver_service
771 Config options passed directly to the driver being used.
773 =head2 build_file_service
777 =head2 build_substitutions_service
779 This method substitutes macros found with calls to a function. There are a
780 number of default macros:
784 =item * C<__HOME__> - replaced with C<$c-E<gt>path_to('')>
786 =item * C<__ENV(foo)__> - replaced with the value of C<$ENV{foo}>
788 =item * C<__path_to(foo/bar)__> - replaced with C<$c-E<gt>path_to('foo/bar')>
790 =item * C<__literal(__FOO__)__> - leaves __FOO__ alone (allows you to use
791 C<__DATA__> as a config value, for example)
795 The parameter list is split on comma (C<,>). You can override this method to
796 do your own string munging, or you can define your own macros in
797 C<< <MyApp->config( 'Plugin::ConfigLoader' => { substitutions => { ... } } ) >>.
800 MyApp->config( 'Plugin::ConfigLoader' => {
802 baz => sub { my $c = shift; qux( @_ ); },
806 The above will respond to C<__baz(x,y)__> in config strings.
808 =head2 build_extensions_service
810 Config::Any's available config file extensions (e.g. xml, json, pl, etc).
812 =head2 build_prefix_service
814 The prefix, based on the application name, that will be used to lookup the
815 config files (which will be in the format $prefix.$extension). If the app is
816 MyApp::Foo, the prefix will be myapp_foo.
818 =head2 build_path_service
820 The path to the config file (or environment variable, if defined).
822 =head2 build_config_service
824 The resulting configuration for the application, after it has successfully
825 been loaded, and all substitutions have been made.
827 =head2 build_raw_config_service
829 The merge of local_config and global_config hashes, before substitutions.
831 =head2 build_global_files_service
833 Gets all files for config that don't have the local_suffix, such as myapp.conf.
835 =head2 build_local_files_service
837 Gets all files for config that have the local_suffix, such as myapp_local.conf.
839 =head2 build_global_config_service
841 Reads config from global_files.
843 =head2 build_local_config_service
845 Reads config from local_files.
847 =head2 build_class_config_service
849 Reads config set from the application's class attribute config,
850 i.e. MyApp->config( name => 'MyApp', ... )
852 =head2 build_config_path_service
854 Splits the path to the config file, and returns on array ref containing
855 the path to the config file minus the extension in the first position,
856 and the extension in the second.
858 =head2 build_config_local_suffix_service
860 Determines the suffix of files used to override the main config. By default
861 this value is C<local>, which will load C<myapp_local.conf>. The suffix can
862 be specified in the following order of preference:
866 =item * C<$ENV{ MYAPP_CONFIG_LOCAL_SUFFIX }>
868 =item * C<$ENV{ CATALYST_CONFIG_LOCAL_SUFFIX }>
872 The first one of these values found replaces the default of C<local> in the
873 name of the local config file to be loaded.
875 For example, if C< $ENV{ MYAPP_CONFIG_LOCAL_SUFFIX }> is set to C<testing>,
876 ConfigLoader will try and load C<myapp_testing.conf> instead of
879 =head2 build_locate_components_service
881 This method is meant to provide a list of component modules that should be
882 setup for the application. By default, it will use L<Module::Pluggable>.
884 Specify a C<setup_components> config option to pass additional options directly
885 to L<Module::Pluggable>.
889 =head2 get_component_from_sub_container($sub_container, $name, $c, @args)
891 Looks for components in a given subcontainer (such as controller, model or
892 view), and returns the searched component. If $name is undef, it returns the
893 default component (such as default_view, if $sub_container is 'view'). If
894 $name is a regexp, it returns an array of matching components. Otherwise, it
895 looks for the component with name $name.
897 =head2 get_all_components
899 Fetches all the components, in each of the sub_containers model, view and
900 controller, and returns a readonly hash. The keys are the class names, and
901 the values are the blessed objects. This is what is returned by $c->components.
905 Adds a component to the appropriate subcontainer. The subcontainer is guessed
906 by the component name given.
908 =head2 find_component
910 Searches for components in all containers. If $component is the full class
911 name, the subcontainer is guessed, and it gets the searched component in there.
912 Otherwise, it looks for a component with that name in all subcontainers. If
913 $component is a regexp it calls _find_component_regexp and matches all
914 components against that regexp.
916 =head2 expand_component_module
918 Components found by C<locate_components> will be passed to this method, which
919 is expected to return a list of component (package) names to be set up.
921 =head2 setup_components
925 Catalyst Contributors, see Catalyst.pm
929 This library is free software. You can redistribute it and/or modify it under
930 the same terms as Perl itself.