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 { +{} },
46 isa => 'Catalyst|Str',
48 default => sub { shift->application_name },
51 has application_name => (
57 has sub_container_class => (
61 default => 'Catalyst::IOC::SubContainer',
63 new_sub_container => 'new',
68 my ( $self, $params ) = @_;
71 $self->${\"build_${_}_service"}
93 my $config = $self->resolve( service => 'config' );
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} )
100 my @default_model = $config->{default_model}
101 ? ( default_component => $config->{default_model} )
105 $self->add_sub_container(
106 $self->build_component_subcontainer
109 $self->add_sub_container(
110 $self->build_controller_subcontainer
113 $self->add_sub_container(
114 $self->build_view_subcontainer( @default_view )
117 $self->add_sub_container(
118 $self->build_model_subcontainer( @default_model )
124 my $class = ref $self;
125 ${ $class . '::customise_container' }->($self)
126 if ${ $class . '::customise_container' };
130 sub build_model_subcontainer {
133 return $self->new_sub_container( @_,
138 sub build_view_subcontainer {
141 return $self->new_sub_container( @_,
146 sub build_controller_subcontainer {
149 return $self->new_sub_container(
150 name => 'controller',
154 sub build_component_subcontainer {
157 return Bread::Board::Container->new(
162 sub build_home_service {
165 return Bread::Board::BlockInjection->new(
166 lifecycle => 'Singleton',
170 my $class = $self->param('application_name');
173 if ( my $env = Catalyst::Utils::env_value( $class, 'HOME' ) ) {
177 $home ||= Catalyst::Utils::home($class);
180 dependencies => [ depends_on('application_name') ],
184 # FIXME: very ambiguous - maybe root_dir?
185 sub build_root_service {
188 return Bread::Board::BlockInjection->new(
189 lifecycle => 'Singleton',
194 return Path::Class::Dir->new( $self->param('home') )->subdir('root');
196 dependencies => [ depends_on('home') ],
200 sub build_application_service {
203 return Bread::Board::Literal->new(
204 name => 'application',
205 value => $self->application,
209 sub build_application_name_service {
212 return Bread::Board::Literal->new( name => 'application_name', value => $self->application_name );
215 sub build_driver_service {
218 return Bread::Board::Literal->new( name => 'driver', value => $self->driver );
221 sub build_file_service {
224 return Bread::Board::Literal->new( name => 'file', value => $self->file );
227 sub build_substitutions_service {
230 return Bread::Board::Literal->new( name => 'substitutions', value => $self->substitutions );
233 sub build_extensions_service {
236 return Bread::Board::BlockInjection->new(
237 lifecycle => 'Singleton',
238 name => 'extensions',
240 return \@{Config::Any->extensions};
245 sub build_prefix_service {
248 return Bread::Board::BlockInjection->new(
249 lifecycle => 'Singleton',
252 return Catalyst::Utils::appprefix( shift->param('application_name') );
254 dependencies => [ depends_on('application_name') ],
258 sub build_path_service {
261 return Bread::Board::BlockInjection->new(
262 lifecycle => 'Singleton',
267 return Catalyst::Utils::env_value( $s->param('application_name'), 'CONFIG' )
269 || $s->param('application_name')->path_to( $s->param('prefix') );
271 dependencies => [ depends_on('file'), depends_on('application_name'), depends_on('prefix') ],
275 sub build_config_service {
278 return Bread::Board::BlockInjection->new(
279 lifecycle => 'Singleton',
284 my $v = Data::Visitor::Callback->new(
286 return unless defined $_;
287 return $self->_config_substitutions( $s->param('application_name'), $s->param('substitutions'), $_ );
291 $v->visit( $s->param('raw_config') );
293 dependencies => [ depends_on('application_name'), depends_on('raw_config'), depends_on('substitutions') ],
297 sub build_raw_config_service {
300 return Bread::Board::BlockInjection->new(
301 lifecycle => 'Singleton',
302 name => 'raw_config',
306 my @global = @{$s->param('global_config')};
307 my @locals = @{$s->param('local_config')};
309 my $config = $s->param('class_config');
311 for my $cfg (@global, @locals) {
313 $config = Catalyst::Utils::merge_hashes( $config, $cfg->{$_} );
319 dependencies => [ depends_on('global_config'), depends_on('local_config'), depends_on('class_config') ],
323 sub build_global_files_service {
326 return Bread::Board::BlockInjection->new(
327 lifecycle => 'Singleton',
328 name => 'global_files',
332 my ( $path, $extension ) = @{$s->param('config_path')};
334 my @extensions = @{$s->param('extensions')};
338 die "Unable to handle files with the extension '${extension}'" unless grep { $_ eq $extension } @extensions;
341 @files = map { "$path.$_" } @extensions;
345 dependencies => [ depends_on('extensions'), depends_on('config_path') ],
349 sub build_local_files_service {
352 return Bread::Board::BlockInjection->new(
353 lifecycle => 'Singleton',
354 name => 'local_files',
358 my ( $path, $extension ) = @{$s->param('config_path')};
359 my $suffix = $s->param('config_local_suffix');
361 my @extensions = @{$s->param('extensions')};
365 die "Unable to handle files with the extension '${extension}'" unless grep { $_ eq $extension } @extensions;
366 $path =~ s{\.$extension}{_$suffix.$extension};
369 @files = map { "${path}_${suffix}.$_" } @extensions;
373 dependencies => [ depends_on('extensions'), depends_on('config_path'), depends_on('config_local_suffix') ],
377 sub build_class_config_service {
380 return Bread::Board::BlockInjection->new(
381 lifecycle => 'Singleton',
382 name => 'class_config',
385 my $app = $s->param('application_name');
387 # Container might be called outside Catalyst context
388 return {} unless Class::MOP::is_class_loaded($app);
390 # config might not have been defined
391 return $app->config || {};
393 dependencies => [ depends_on('application_name') ],
397 sub build_global_config_service {
400 return Bread::Board::BlockInjection->new(
401 lifecycle => 'Singleton',
402 name => 'global_config',
406 return Config::Any->load_files({
407 files => $s->param('global_files'),
408 filter => \&_fix_syntax,
410 driver_args => $s->param('driver'),
413 dependencies => [ depends_on('global_files') ],
417 sub build_local_config_service {
420 return Bread::Board::BlockInjection->new(
421 lifecycle => 'Singleton',
422 name => 'local_config',
426 return Config::Any->load_files({
427 files => $s->param('local_files'),
428 filter => \&_fix_syntax,
430 driver_args => $s->param('driver'),
433 dependencies => [ depends_on('local_files') ],
437 sub build_config_path_service {
440 return Bread::Board::BlockInjection->new(
441 lifecycle => 'Singleton',
442 name => 'config_path',
446 my $path = $s->param('path');
447 my $prefix = $s->param('prefix');
449 my ( $extension ) = ( $path =~ m{\.(.{1,4})$} );
452 $path =~ s{[\/\\]$}{};
456 return [ $path, $extension ];
458 dependencies => [ depends_on('prefix'), depends_on('path') ],
462 sub build_config_local_suffix_service {
465 return Bread::Board::BlockInjection->new(
466 lifecycle => 'Singleton',
467 name => 'config_local_suffix',
470 my $suffix = Catalyst::Utils::env_value( $s->param('application_name'), 'CONFIG_LOCAL_SUFFIX' ) || $self->config_local_suffix;
474 dependencies => [ depends_on('application_name') ],
478 sub build_locate_components_service {
481 return Bread::Board::BlockInjection->new(
482 lifecycle => 'Singleton',
483 name => 'locate_components',
486 my $class = $s->param('application_name');
487 my $config = $s->param('config')->{ setup_components };
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 };
495 my @paths = qw( ::Controller ::C ::Model ::M ::View ::V );
497 my $locator = Module::Pluggable::Object->new(
498 search_path => [ map { s/^(?=::)/$class/; $_; } @paths ],
502 return [ $locator->plugins ];
504 dependencies => [ depends_on('application_name'), depends_on('config') ],
508 sub setup_components {
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;
515 my $app_locate_components_addr = refaddr(
516 $class->can('locate_components')
518 my $cat_locate_components_addr = refaddr(
519 Catalyst->can('locate_components')
522 if ($app_locate_components_addr != $cat_locate_components_addr) {
523 # FIXME - why not just say: @comps = $class->locate_components() ?
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}
530 for my $component ( @comps ) {
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
536 Catalyst::Utils::ensure_class_loaded( $component, { ignore_loaded => 1 } );
539 for my $component (@comps) {
540 $self->add_component( $component );
541 # FIXME - $instance->expand_modules() is broken
542 my @expanded_components = $self->expand_component_module( $component );
545 !$deprecatedcatalyst_component_names &&
546 ($deprecatedcatalyst_component_names = $component =~ m/::[CMV]::/) ||
547 ($deprecatedcatalyst_component_names = grep { /::[CMV]::/ } @expanded_components)
549 # FIXME - should I be calling warn here?
550 # Maybe it's time to remove it, or become fatal
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}
556 for my $component (@expanded_components) {
557 $self->add_component( $component )
558 unless $comps{$component};
567 prefix => $_ eq 'Component' ? '' : $_ . '::',
568 values => delete $config->{ lc $_ } || delete $config->{ $_ }
570 grep { ref $config->{ lc $_ } || ref $config->{ $_ } }
571 qw( Component Model M View V Controller C Plugin )
574 foreach my $comp ( @components ) {
575 my $prefix = $comp->{ prefix };
576 foreach my $element ( keys %{ $comp->{ values } } ) {
577 $config->{ "$prefix$element" } = $comp->{ values }->{ $element };
582 sub _config_substitutions {
583 my ( $self, $name, $subs, $arg ) = @_;
585 $subs->{ HOME } ||= sub { shift->path_to( '' ); };
589 if (! defined($ENV{$v})) {
590 Catalyst::Exception->throw( message =>
591 "Missing environment variable: $v" );
597 $subs->{ path_to } ||= sub { shift->path_to( @_ ); };
598 $subs->{ literal } ||= sub { return $_[ 1 ]; };
599 my $subsre = join( '|', keys %$subs );
601 $arg =~ s{__($subsre)(?:\((.+?)\))?__}{ $subs->{ $1 }->( $name, $2 ? split( /,/, $2 ) : () ) }eg;
605 sub get_component_from_sub_container {
606 my ( $self, $sub_container_name, $name, $c, @args ) = @_;
608 my $sub_container = $self->get_sub_container( $sub_container_name );
611 my $default = $sub_container->default_component;
613 return $sub_container->get_component( $default, $c, @args )
614 if $default && $sub_container->has_service( $default );
616 # FIXME - should I be calling $c->log->warn here?
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" );
626 return $sub_container->get_component_regexp( $name, $c, @args )
629 return $sub_container->get_component( $name, $c, @args )
630 if $sub_container->has_service( $name );
633 "Attempted to use $sub_container_name '$name', " .
634 "but it does not exist"
641 my ( $self, $component, @args ) = @_;
642 my ( $type, $name ) = _get_component_type_name($component);
645 return $self->get_component_from_sub_container(
649 my $query = ref $component
654 for my $subcontainer_name (qw/model view controller/) {
655 my $subcontainer = $self->get_sub_container( $subcontainer_name );
656 my @components = $subcontainer->get_service_list;
657 @result = grep { m{$component} } @components;
659 return map { $subcontainer->get_component( $_, @args ) } @result
663 # one last search for things like $c->comp(qr/::M::/)
664 @result = $self->_find_component_regexp(
666 ) if !@result and ref $component;
668 # it expects an empty list on failed searches
672 sub _find_component_regexp {
673 my ( $self, $component, $ctx, @args ) = @_;
676 my @components = grep { m{$component} } keys %{ $self->get_all_components($ctx) };
679 my ($type, $name) = _get_component_type_name($_);
681 push @result, $self->get_component_from_sub_container(
682 $type, $name, $ctx, @args
689 sub get_all_components {
690 my ($self, $class) = @_;
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
696 # big problem when setting up the dispatcher - this method is called
697 # as $container->get_all_components('MyApp'). What to do with Request
699 foreach my $type (qw/model view controller /) {
700 my $container = $self->get_sub_container($type);
702 for my $component ($container->get_service_list) {
703 my $comp_service = $container->get_service($component);
705 $components{$comp_service->catalyst_component_name} = $comp_service->get(ctx => $class);
709 return lock_hash %components;
713 my ( $self, $component ) = @_;
714 my ( $type, $name ) = _get_component_type_name($component);
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);
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 );
730 my $component_service_name = "${type}_${name}";
732 $instance_container->add_service(
733 Catalyst::IOC::ConstructorInjection->new(
734 name => $component_service_name,
735 catalyst_component_name => $component,
737 lifecycle => 'Singleton',
739 depends_on( '/application' ),
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;
751 $accept_context_container->add_service(
752 Catalyst::IOC::BlockInjection->new(
754 catalyst_component_name => $component,
756 depends_on( "/component/$component_service_name" ),
758 block => sub { shift->param($component_service_name) },
763 # FIXME: should this sub exist?
764 # should it be moved to Catalyst::Utils,
765 # or replaced by something already existing there?
766 sub _get_component_type_name {
767 my ( $component ) = @_;
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 };
777 return ($result, $component);
780 sub expand_component_module {
781 my ( $class, $module ) = @_;
782 return Devel::InnerPackage::list_packages( $module );
785 __PACKAGE__->meta->make_immutable;
795 Catalyst::Container - IOC for Catalyst components
803 =head1 Methods for Building Containers
805 =head2 build_component_subcontainer
807 Container that stores all components, i.e. all models, views and controllers
808 together. Each service is an instance of the actual component, and by default
809 it lives while the application is running. Retrieving components from this
810 sub-container will instantiate the component, if it hasn't been instantiated
811 already, but will not execute ACCEPT_CONTEXT.
813 =head2 build_model_subcontainer
815 Container that stores references for all models that are inside the components
816 sub-container. Retrieving a model triggers ACCEPT_CONTEXT, if it exists.
818 =head2 build_view_subcontainer
820 Same as L<build_model_subcontainer>, but for views.
822 =head2 build_controller_subcontainer
824 Same as L<build_model_subcontainer>, but for controllers.
826 =head1 Methods for Building Services
828 =head2 build_application_name_service
830 Name of the application (such as MyApp).
832 =head2 build_driver_service
834 Config options passed directly to the driver being used.
836 =head2 build_file_service
840 =head2 build_substitutions_service
842 This method substitutes macros found with calls to a function. There are a
843 number of default macros:
847 =item * C<__HOME__> - replaced with C<$c-E<gt>path_to('')>
849 =item * C<__ENV(foo)__> - replaced with the value of C<$ENV{foo}>
851 =item * C<__path_to(foo/bar)__> - replaced with C<$c-E<gt>path_to('foo/bar')>
853 =item * C<__literal(__FOO__)__> - leaves __FOO__ alone (allows you to use
854 C<__DATA__> as a config value, for example)
858 The parameter list is split on comma (C<,>). You can override this method to
859 do your own string munging, or you can define your own macros in
860 C<< <MyApp->config( 'Plugin::ConfigLoader' => { substitutions => { ... } } ) >>.
863 MyApp->config( 'Plugin::ConfigLoader' => {
865 baz => sub { my $c = shift; qux( @_ ); },
869 The above will respond to C<__baz(x,y)__> in config strings.
871 =head2 build_extensions_service
873 Config::Any's available config file extensions (e.g. xml, json, pl, etc).
875 =head2 build_prefix_service
877 The prefix, based on the application name, that will be used to look-up the
878 config files (which will be in the format $prefix.$extension). If the app is
879 MyApp::Foo, the prefix will be myapp_foo.
881 =head2 build_path_service
883 The path to the config file (or environment variable, if defined).
885 =head2 build_config_service
887 The resulting configuration for the application, after it has successfully
888 been loaded, and all substitutions have been made.
890 =head2 build_raw_config_service
892 The merge of local_config and global_config hashes, before substitutions.
894 =head2 build_global_files_service
896 Gets all files for config that don't have the local_suffix, such as myapp.conf.
898 =head2 build_local_files_service
900 Gets all files for config that have the local_suffix, such as myapp_local.conf.
902 =head2 build_global_config_service
904 Reads config from global_files.
906 =head2 build_local_config_service
908 Reads config from local_files.
910 =head2 build_class_config_service
912 Reads config set from the application's class attribute config,
913 i.e. MyApp->config( name => 'MyApp', ... )
915 =head2 build_config_path_service
917 Splits the path to the config file, and returns on array ref containing
918 the path to the config file minus the extension in the first position,
919 and the extension in the second.
921 =head2 build_config_local_suffix_service
923 Determines the suffix of files used to override the main config. By default
924 this value is C<local>, which will load C<myapp_local.conf>. The suffix can
925 be specified in the following order of preference:
929 =item * C<$ENV{ MYAPP_CONFIG_LOCAL_SUFFIX }>
931 =item * C<$ENV{ CATALYST_CONFIG_LOCAL_SUFFIX }>
935 The first one of these values found replaces the default of C<local> in the
936 name of the local config file to be loaded.
938 For example, if C< $ENV{ MYAPP_CONFIG_LOCAL_SUFFIX }> is set to C<testing>,
939 ConfigLoader will try and load C<myapp_testing.conf> instead of
942 =head2 build_locate_components_service
944 This method is meant to provide a list of component modules that should be
945 setup for the application. By default, it will use L<Module::Pluggable>.
947 Specify a C<setup_components> config option to pass additional options directly
948 to L<Module::Pluggable>.
952 =head2 get_component_from_sub_container($sub_container, $name, $c, @args)
954 Looks for components in a given sub-container (such as controller, model or
955 view), and returns the searched component. If $name is undef, it returns the
956 default 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
958 looks for the component with name $name.
960 =head2 get_all_components
962 Fetches all the components, in each of the sub_containers model, view and
963 controller, and returns a read-only hash. The keys are the class names, and
964 the values are the blessed objects. This is what is returned by $c->components.
968 Adds a component to the appropriate sub-container. The sub-container is guessed
969 by the component name given.
971 =head2 find_component
973 Searches for components in all containers. If $component is the full class
974 name, the sub-container is guessed, and it gets the searched component in there.
975 Otherwise, it looks for a component with that name in all sub-containers. If
976 $component is a regexp it calls _find_component_regexp and matches all
977 components against that regexp.
979 =head2 expand_component_module
981 Components found by C<locate_components> will be passed to this method, which
982 is expected to return a list of component (package) names to be set up.
984 =head2 setup_components
986 Uses locate_components service to list the components, and adds them to the
987 appropriate sub-containers, using add_component().
991 Catalyst Contributors, see Catalyst.pm
995 This library is free software. You can redistribute it and/or modify it under
996 the same terms as Perl itself.