1 package Catalyst::IOC::Container;
5 use Data::Visitor::Callback;
6 use Catalyst::Utils ();
7 use Devel::InnerPackage ();
8 use Hash::Util qw/lock_hash/;
9 use MooseX::Types::LoadableClass qw/ LoadableClass /;
11 use Catalyst::IOC::BlockInjection;
12 use Catalyst::IOC::ConstructorInjection;
13 use Module::Pluggable::Object ();
14 use namespace::autoclean;
16 extends 'Bread::Board::Container';
18 has config_local_suffix => (
27 default => sub { +{} },
36 has substitutions => (
39 default => sub { +{} },
42 has application_name => (
48 has sub_container_class => (
52 default => 'Catalyst::IOC::SubContainer',
54 new_sub_container => 'new',
59 my ( $self, $params ) = @_;
62 $self->${\"build_${_}_service"}
83 my $config = $self->resolve( service => 'config' );
85 # don't force default_component to be undef if the config wasn't set
86 my @default_view = $config->{default_view}
87 ? ( default_component => $config->{default_view} )
90 my @default_model = $config->{default_model}
91 ? ( default_component => $config->{default_model} )
95 $self->add_sub_container(
96 $self->build_component_subcontainer
99 $self->add_sub_container(
100 $self->build_controller_subcontainer
103 $self->add_sub_container(
104 $self->build_view_subcontainer( @default_view )
107 $self->add_sub_container(
108 $self->build_model_subcontainer( @default_model )
112 sub build_model_subcontainer {
115 return $self->new_sub_container( @_,
120 sub build_view_subcontainer {
123 return $self->new_sub_container( @_,
128 sub build_controller_subcontainer {
131 return $self->new_sub_container(
132 name => 'controller',
136 sub build_component_subcontainer {
139 return Bread::Board::Container->new(
144 sub build_application_name_service {
147 return Bread::Board::Literal->new( name => 'application_name', value => $self->application_name );
150 sub build_driver_service {
153 return Bread::Board::Literal->new( name => 'driver', value => $self->driver );
156 sub build_file_service {
159 return Bread::Board::Literal->new( name => 'file', value => $self->file );
162 sub build_substitutions_service {
165 return Bread::Board::Literal->new( name => 'substitutions', value => $self->substitutions );
168 sub build_extensions_service {
171 return Bread::Board::BlockInjection->new(
172 lifecycle => 'Singleton',
173 name => 'extensions',
175 return \@{Config::Any->extensions};
180 sub build_prefix_service {
183 return Bread::Board::BlockInjection->new(
184 lifecycle => 'Singleton',
187 return Catalyst::Utils::appprefix( shift->param('application_name') );
189 dependencies => [ depends_on('application_name') ],
193 sub build_path_service {
196 return Bread::Board::BlockInjection->new(
197 lifecycle => 'Singleton',
202 return Catalyst::Utils::env_value( $s->param('application_name'), 'CONFIG' )
204 || $s->param('application_name')->path_to( $s->param('prefix') );
206 dependencies => [ depends_on('file'), depends_on('application_name'), depends_on('prefix') ],
210 sub build_config_service {
213 return Bread::Board::BlockInjection->new(
214 lifecycle => 'Singleton',
219 my $v = Data::Visitor::Callback->new(
221 return unless defined $_;
222 return $self->_config_substitutions( $s->param('application_name'), $s->param('substitutions'), $_ );
226 $v->visit( $s->param('raw_config') );
228 dependencies => [ depends_on('application_name'), depends_on('raw_config'), depends_on('substitutions') ],
232 sub build_raw_config_service {
235 return Bread::Board::BlockInjection->new(
236 lifecycle => 'Singleton',
237 name => 'raw_config',
241 my @global = @{$s->param('global_config')};
242 my @locals = @{$s->param('local_config')};
244 my $config = $s->param('class_config');
246 for my $cfg (@global, @locals) {
248 $config = Catalyst::Utils::merge_hashes( $config, $cfg->{$_} );
254 dependencies => [ depends_on('global_config'), depends_on('local_config'), depends_on('class_config') ],
258 sub build_global_files_service {
261 return Bread::Board::BlockInjection->new(
262 lifecycle => 'Singleton',
263 name => 'global_files',
267 my ( $path, $extension ) = @{$s->param('config_path')};
269 my @extensions = @{$s->param('extensions')};
273 die "Unable to handle files with the extension '${extension}'" unless grep { $_ eq $extension } @extensions;
276 @files = map { "$path.$_" } @extensions;
280 dependencies => [ depends_on('extensions'), depends_on('config_path') ],
284 sub build_local_files_service {
287 return Bread::Board::BlockInjection->new(
288 lifecycle => 'Singleton',
289 name => 'local_files',
293 my ( $path, $extension ) = @{$s->param('config_path')};
294 my $suffix = $s->param('config_local_suffix');
296 my @extensions = @{$s->param('extensions')};
300 die "Unable to handle files with the extension '${extension}'" unless grep { $_ eq $extension } @extensions;
301 $path =~ s{\.$extension}{_$suffix.$extension};
304 @files = map { "${path}_${suffix}.$_" } @extensions;
308 dependencies => [ depends_on('extensions'), depends_on('config_path'), depends_on('config_local_suffix') ],
312 sub build_class_config_service {
315 return Bread::Board::BlockInjection->new(
316 lifecycle => 'Singleton',
317 name => 'class_config',
320 my $app = $s->param('application_name');
322 # Container might be called outside Catalyst context
323 return {} unless Class::MOP::is_class_loaded($app);
325 # config might not have been defined
326 return $app->config || {};
328 dependencies => [ depends_on('application_name') ],
332 sub build_global_config_service {
335 return Bread::Board::BlockInjection->new(
336 lifecycle => 'Singleton',
337 name => 'global_config',
341 return Config::Any->load_files({
342 files => $s->param('global_files'),
343 filter => \&_fix_syntax,
345 driver_args => $s->param('driver'),
348 dependencies => [ depends_on('global_files') ],
352 sub build_local_config_service {
355 return Bread::Board::BlockInjection->new(
356 lifecycle => 'Singleton',
357 name => 'local_config',
361 return Config::Any->load_files({
362 files => $s->param('local_files'),
363 filter => \&_fix_syntax,
365 driver_args => $s->param('driver'),
368 dependencies => [ depends_on('local_files') ],
372 sub build_config_path_service {
375 return Bread::Board::BlockInjection->new(
376 lifecycle => 'Singleton',
377 name => 'config_path',
381 my $path = $s->param('path');
382 my $prefix = $s->param('prefix');
384 my ( $extension ) = ( $path =~ m{\.(.{1,4})$} );
387 $path =~ s{[\/\\]$}{};
391 return [ $path, $extension ];
393 dependencies => [ depends_on('prefix'), depends_on('path') ],
397 sub build_config_local_suffix_service {
400 return Bread::Board::BlockInjection->new(
401 lifecycle => 'Singleton',
402 name => 'config_local_suffix',
405 my $suffix = Catalyst::Utils::env_value( $s->param('application_name'), 'CONFIG_LOCAL_SUFFIX' ) || $self->config_local_suffix;
409 dependencies => [ depends_on('application_name') ],
413 sub build_locate_components_service {
416 return Bread::Board::BlockInjection->new(
417 lifecycle => 'Singleton',
418 name => 'locate_components',
421 my $class = $s->param('application_name');
422 my $config = $s->param('config')->{ setup_components };
424 Catalyst::Exception->throw(
425 qq{You are using search_extra config option. That option is\n} .
426 qq{deprecated, please refer to the documentation for\n} .
427 qq{other ways of achieving the same results.\n}
428 ) if delete $config->{ search_extra };
430 my @paths = qw( ::Controller ::C ::Model ::M ::View ::V );
432 my $locator = Module::Pluggable::Object->new(
433 search_path => [ map { s/^(?=::)/$class/; $_; } @paths ],
437 return [ $locator->plugins ];
439 dependencies => [ depends_on('application_name'), depends_on('config') ],
443 sub setup_components {
445 my $class = $self->resolve( service => 'application_name' );
446 my @comps = @{ $self->resolve( service => 'locate_components' ) };
447 my %comps = map { $_ => 1 } @comps;
448 my $deprecatedcatalyst_component_names = 0;
450 for my $component ( @comps ) {
452 # We pass ignore_loaded here so that overlay files for (e.g.)
453 # Model::DBI::Schema sub-classes are loaded - if it's in @comps
454 # we know M::P::O found a file on disk so this is safe
456 Catalyst::Utils::ensure_class_loaded( $component, { ignore_loaded => 1 } );
459 for my $component (@comps) {
460 $self->add_component( $component );
461 # FIXME - $instance->expand_modules() is broken
462 my @expanded_components = $self->expand_component_module( $component );
465 !$deprecatedcatalyst_component_names &&
466 ($deprecatedcatalyst_component_names = $component =~ m/::[CMV]::/) ||
467 ($deprecatedcatalyst_component_names = grep { /::[CMV]::/ } @expanded_components)
469 # FIXME - should I be calling warn here?
470 # Maybe it's time to remove it, or become fatal
471 $class->log->warn(qq{Your application is using the deprecated ::[MVC]:: type naming scheme.\n}.
472 qq{Please switch your class names to ::Model::, ::View:: and ::Controller: as appropriate.\n}
476 for my $component (@expanded_components) {
477 $self->add_component( $component )
478 unless $comps{$component};
487 prefix => $_ eq 'Component' ? '' : $_ . '::',
488 values => delete $config->{ lc $_ } || delete $config->{ $_ }
490 grep { ref $config->{ lc $_ } || ref $config->{ $_ } }
491 qw( Component Model M View V Controller C Plugin )
494 foreach my $comp ( @components ) {
495 my $prefix = $comp->{ prefix };
496 foreach my $element ( keys %{ $comp->{ values } } ) {
497 $config->{ "$prefix$element" } = $comp->{ values }->{ $element };
502 sub _config_substitutions {
503 my ( $self, $name, $subs, $arg ) = @_;
505 $subs->{ HOME } ||= sub { shift->path_to( '' ); };
509 if (! defined($ENV{$v})) {
510 Catalyst::Exception->throw( message =>
511 "Missing environment variable: $v" );
517 $subs->{ path_to } ||= sub { shift->path_to( @_ ); };
518 $subs->{ literal } ||= sub { return $_[ 1 ]; };
519 my $subsre = join( '|', keys %$subs );
521 $arg =~ s{__($subsre)(?:\((.+?)\))?__}{ $subs->{ $1 }->( $name, $2 ? split( /,/, $2 ) : () ) }eg;
525 sub get_component_from_sub_container {
526 my ( $self, $sub_container_name, $name, $c, @args ) = @_;
528 my $sub_container = $self->get_sub_container( $sub_container_name );
531 my $default = $sub_container->default_component;
533 return $sub_container->get_component( $default, $c, @args )
534 if $default && $sub_container->has_service( $default );
536 # FIXME - should I be calling $c->log->warn here?
537 # this is never a controller, so this is safe
538 $c->log->warn( "Calling \$c->$sub_container_name() is not supported unless you specify one of:" );
539 $c->log->warn( "* \$c->config(default_$sub_container_name => 'the name of the default $sub_container_name to use')" );
540 $c->log->warn( "* \$c->stash->{current_$sub_container_name} # the name of the view to use for this request" );
541 $c->log->warn( "* \$c->stash->{current_${sub_container_name}_instance} # the instance of the $sub_container_name to use for this request" );
546 return $sub_container->get_component_regexp( $name, $c, @args )
549 return $sub_container->get_component( $name, $c, @args )
550 if $sub_container->has_service( $name );
553 "Attempted to use $sub_container_name '$name', " .
554 "but it does not exist"
561 my ( $self, $component, @args ) = @_;
562 my ( $type, $name ) = _get_component_type_name($component);
565 return $self->get_component_from_sub_container(
569 my $query = ref $component
574 for my $subcontainer_name (qw/model view controller/) {
575 my $subcontainer = $self->get_sub_container( $subcontainer_name );
576 my @components = $subcontainer->get_service_list;
577 @result = grep { m{$component} } @components;
579 return map { $subcontainer->get_component( $_, @args ) } @result
583 # one last search for things like $c->comp(qr/::M::/)
584 @result = $self->find_component_regexp(
586 ) if !@result and ref $component;
588 # it expects an empty list on failed searches
592 sub find_component_regexp {
593 my ( $self, $component, @args ) = @_;
596 my @components = grep { m{$component} } keys %{ $self->get_all_components };
599 my ($type, $name) = _get_component_type_name($_);
601 push @result, $self->get_component_from_sub_container(
609 sub get_all_components {
613 my $container = $self->get_sub_container('component');
615 for my $component ($container->get_service_list) {
616 my $comp = $container->resolve(
617 service => $component
619 my $comp_name = ref $comp || $comp;
620 $components{$comp_name} = $comp;
623 return lock_hash %components;
627 my ( $self, $component ) = @_;
628 my ( $type, $name ) = _get_component_type_name($component);
632 my $component_service_name = "${type}_${name}";
634 # The 'component' sub-container will create the object, and store it's
635 # instance, which, by default, will live throughout the application.
636 # The model/view/controller sub-containers only reference the instance
637 # held in the aforementioned sub-container, and execute the ACCEPT_CONTEXT
638 # sub every time they are called, when it exists.
639 my $instance_container = $self->get_sub_container('component');
640 my $accept_context_container = $self->get_sub_container($type);
642 $instance_container->add_service(
643 Catalyst::IOC::ConstructorInjection->new(
644 name => $component_service_name,
646 lifecycle => 'Singleton',
648 depends_on( '/application_name' ),
649 depends_on( '/config' ),
652 ) unless $instance_container->has_service( $component_service_name );
653 # ^ custom containers might have added the service already.
654 # we don't want to override that.
656 $accept_context_container->add_service(
657 Catalyst::IOC::BlockInjection->new(
660 depends_on( "/component/$component_service_name" ),
662 block => sub { shift->param($component_service_name) },
664 ) unless $accept_context_container->has_service( $name );
668 # FIXME: should this sub exist?
669 # should it be moved to Catalyst::Utils,
670 # or replaced by something already existing there?
671 sub _get_component_type_name {
672 my ( $component ) = @_;
674 my @parts = split /::/, $component;
676 while (scalar @parts > 1) {
677 my $type = shift @parts;
679 return ('controller', join '::', @parts)
680 if $type =~ /^(c|controller)$/i;
682 return ('model', join '::', @parts)
683 if $type =~ /^(m|model)$/i;
685 return ('view', join '::', @parts)
686 if $type =~ /^(v|view)$/i;
689 return (undef, $component);
692 sub expand_component_module {
693 my ( $class, $module ) = @_;
694 return Devel::InnerPackage::list_packages( $module );
697 # copied from stevan's OX
698 sub flush_request_services {
700 my @services = $self->get_service_list;
702 foreach my $service (@services) {
703 my $injection = $self->get_service($service);
704 if ($injection->does('Catalyst::IOC::LifeCycle::Request')) {
705 $injection->flush_instance;
718 Catalyst::Container - IOC for Catalyst components
726 =head1 Building Containers
728 =head2 build_component_subcontainer
730 Container that stores all components, i.e. all models, views and controllers
731 together. Each service is an instance of the actual component, and by default
732 it lives while the application is running. Retrieving components from this
733 subcontainer will instantiate the component, if it hasn't been instantiated
734 already, but will not execute ACCEPT_CONTEXT.
736 =head2 build_model_subcontainer
738 Container that stores references for all models that are inside the components
739 subcontainer. Retrieving a model triggers ACCEPT_CONTEXT, if it exists.
741 =head2 build_view_subcontainer
743 Same as L<build_model_subcontainer>, but for views.
745 =head2 build_controller_subcontainer
747 Same as L<build_model_subcontainer>, but for controllers.
749 =head1 Building Services
751 =head2 build_application_name_service
753 Name of the application (such as MyApp).
755 =head2 build_driver_service
757 Config options passed directly to the driver being used.
759 =head2 build_file_service
763 =head2 build_substitutions_service
765 This method substitutes macros found with calls to a function. There are a
766 number of default macros:
770 =item * C<__HOME__> - replaced with C<$c-E<gt>path_to('')>
772 =item * C<__ENV(foo)__> - replaced with the value of C<$ENV{foo}>
774 =item * C<__path_to(foo/bar)__> - replaced with C<$c-E<gt>path_to('foo/bar')>
776 =item * C<__literal(__FOO__)__> - leaves __FOO__ alone (allows you to use
777 C<__DATA__> as a config value, for example)
781 The parameter list is split on comma (C<,>). You can override this method to
782 do your own string munging, or you can define your own macros in
783 C<< <MyApp->config( 'Plugin::ConfigLoader' => { substitutions => { ... } } ) >>.
786 MyApp->config( 'Plugin::ConfigLoader' => {
788 baz => sub { my $c = shift; qux( @_ ); },
792 The above will respond to C<__baz(x,y)__> in config strings.
794 =head2 build_extensions_service
796 Config::Any's available config file extensions (e.g. xml, json, pl, etc).
798 =head2 build_prefix_service
800 The prefix, based on the application name, that will be used to lookup the
801 config files (which will be in the format $prefix.$extension). If the app is
802 MyApp::Foo, the prefix will be myapp_foo.
804 =head2 build_path_service
806 The path to the config file (or environment variable, if defined).
808 =head2 build_config_service
810 The resulting configuration for the application, after it has successfully
811 been loaded, and all substitutions have been made.
813 =head2 build_raw_config_service
815 The merge of local_config and global_config hashes, before substitutions.
817 =head2 build_global_files_service
819 Gets all files for config that don't have the local_suffix, such as myapp.conf.
821 =head2 build_local_files_service
823 Gets all files for config that have the local_suffix, such as myapp_local.conf.
825 =head2 build_global_config_service
827 Reads config from global_files.
829 =head2 build_local_config_service
831 Reads config from local_files.
833 =head2 build_class_config_service
835 Reads config set from the application's class attribute config,
836 i.e. MyApp->config( name => 'MyApp', ... )
838 =head2 build_config_path_service
840 Splits the path to the config file, and returns on array ref containing
841 the path to the config file minus the extension in the first position,
842 and the extension in the second.
844 =head2 build_config_local_suffix_service
846 Determines the suffix of files used to override the main config. By default
847 this value is C<local>, which will load C<myapp_local.conf>. The suffix can
848 be specified in the following order of preference:
852 =item * C<$ENV{ MYAPP_CONFIG_LOCAL_SUFFIX }>
854 =item * C<$ENV{ CATALYST_CONFIG_LOCAL_SUFFIX }>
858 The first one of these values found replaces the default of C<local> in the
859 name of the local config file to be loaded.
861 For example, if C< $ENV{ MYAPP_CONFIG_LOCAL_SUFFIX }> is set to C<testing>,
862 ConfigLoader will try and load C<myapp_testing.conf> instead of
865 =head2 build_locate_components_service
867 This method is meant to provide a list of component modules that should be
868 setup for the application. By default, it will use L<Module::Pluggable>.
870 Specify a C<setup_components> config option to pass additional options directly
871 to L<Module::Pluggable>.
875 =head2 get_component_from_sub_container($sub_container, $name, $c, @args)
877 Looks for components in a given subcontainer (such as controller, model or
878 view), and returns the searched component. If $name is undef, it returns the
879 default component (such as default_view, if $sub_container is 'view'). If
880 $name is a regexp, it returns an array of matching components. Otherwise, it
881 looks for the component with name $name.
883 =head2 get_components_names_types
885 Gets all components from all containers and returns them as an array of
886 arrayrefs containing the component name and the component type (i.e., whether
887 it's an instance or a class).
889 =head2 get_all_components
891 Fetches all the components, in each of the sub_containers model, view and
892 controller, and returns a readonly hash. The keys are the class names, and
893 the values are the blessed objects. This is what is returned by $c->components.
897 Adds a component to the appropriate subcontainer. The subcontainer is guessed
898 by the component name given.
900 =head2 find_component
902 Searches for components in all containers. If $component is the full class
903 name, the subcontainer is guessed, and it gets the searched component in there.
904 Otherwise, it looks for a component with that name in all subcontainers. If
905 $component is a regexp, it calls the method below, find_component_regexp,
906 and matches all components against that regexp.
908 =head2 find_component_regexp
910 Finds components that match a given regexp. Used internally, by find_component.
912 =head2 expand_component_module
914 Components found by C<locate_components> will be passed to this method, which
915 is expected to return a list of component (package) names to be set up.
917 =head2 flush_request_services
919 =head2 setup_components
923 Catalyst Contributors, see Catalyst.pm
927 This library is free software. You can redistribute it and/or modify it under
928 the same terms as Perl itself.