1 package Catalyst::IOC::Container;
2 use Bread::Board qw/depends_on/;
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 sub_container_class => (
48 default => 'Catalyst::IOC::SubContainer',
50 new_sub_container => 'new',
55 my ( $self, $params ) = @_;
58 $self->${\"build_${_}_service"}
81 my $config = $self->resolve( service => 'config' );
83 # don't force default_component to be undef if the config wasn't set
84 my @default_view = $config->{default_view}
85 ? ( default_component => $config->{default_view} )
88 my @default_model = $config->{default_model}
89 ? ( default_component => $config->{default_model} )
93 $self->add_sub_container(
94 $self->build_component_subcontainer
97 $self->add_sub_container(
98 $self->build_controller_subcontainer
101 $self->add_sub_container(
102 $self->build_view_subcontainer( @default_view )
105 $self->add_sub_container(
106 $self->build_model_subcontainer( @default_model )
112 my $class = ref $self;
113 ${ $class . '::customise_container' }->($self)
114 if ${ $class . '::customise_container' };
118 sub build_model_subcontainer {
121 return $self->new_sub_container( @_,
126 sub build_view_subcontainer {
129 return $self->new_sub_container( @_,
134 sub build_controller_subcontainer {
137 return $self->new_sub_container(
138 name => 'controller',
142 sub build_component_subcontainer {
145 return Bread::Board::Container->new(
150 sub build_home_service {
153 return Bread::Board::BlockInjection->new(
154 lifecycle => 'Singleton',
158 my $class = $self->param('catalyst_application');
160 if ( my $env = Catalyst::Utils::env_value( $class, 'HOME' ) ) {
164 if ( my $home = $self->param('home_flag') ) {
168 return Catalyst::Utils::home($class);
171 home_flag => { is => 'ro', isa => 'Str|Undef', required => 0 }
173 dependencies => [ depends_on('catalyst_application') ],
177 sub build_root_dir_service {
180 return Bread::Board::BlockInjection->new(
181 lifecycle => 'Singleton',
186 return Path::Class::Dir->new( $self->param('home') )->subdir('root');
188 dependencies => [ depends_on('home') ],
192 sub build_catalyst_application_service {
195 return Bread::Board::Literal->new( name => 'catalyst_application', value => $self->name );
198 sub build_driver_service {
201 return Bread::Board::Literal->new( name => 'driver', value => $self->driver );
204 sub build_file_service {
207 return Bread::Board::Literal->new( name => 'file', value => $self->file );
210 sub build_substitutions_service {
213 return Bread::Board::Literal->new( name => 'substitutions', value => $self->substitutions );
216 sub build_extensions_service {
219 return Bread::Board::BlockInjection->new(
220 lifecycle => 'Singleton',
221 name => 'extensions',
223 return \@{Config::Any->extensions};
228 sub build_prefix_service {
231 return Bread::Board::BlockInjection->new(
232 lifecycle => 'Singleton',
235 return Catalyst::Utils::appprefix( shift->param('catalyst_application') );
237 dependencies => [ depends_on('catalyst_application') ],
241 sub build_path_service {
244 return Bread::Board::BlockInjection->new(
245 lifecycle => 'Singleton',
250 return Catalyst::Utils::env_value( $s->param('catalyst_application'), 'CONFIG' )
252 || $s->param('catalyst_application')->path_to( $s->param('prefix') );
254 dependencies => [ depends_on('file'), depends_on('catalyst_application'), depends_on('prefix') ],
258 sub build_config_service {
261 return Bread::Board::BlockInjection->new(
262 lifecycle => 'Singleton',
267 my $v = Data::Visitor::Callback->new(
269 return unless defined $_;
270 return $self->_config_substitutions( $s->param('catalyst_application'), $s->param('substitutions'), $_ );
274 $v->visit( $s->param('raw_config') );
276 dependencies => [ depends_on('catalyst_application'), depends_on('raw_config'), depends_on('substitutions') ],
280 sub build_raw_config_service {
283 return Bread::Board::BlockInjection->new(
284 lifecycle => 'Singleton',
285 name => 'raw_config',
289 my @global = @{$s->param('global_config')};
290 my @locals = @{$s->param('local_config')};
292 my $config = $s->param('class_config');
294 for my $cfg (@global, @locals) {
296 $config = Catalyst::Utils::merge_hashes( $config, $cfg->{$_} );
302 dependencies => [ depends_on('global_config'), depends_on('local_config'), depends_on('class_config') ],
306 sub build_global_files_service {
309 return Bread::Board::BlockInjection->new(
310 lifecycle => 'Singleton',
311 name => 'global_files',
315 my ( $path, $extension ) = @{$s->param('config_path')};
317 my @extensions = @{$s->param('extensions')};
321 die "Unable to handle files with the extension '${extension}'" unless grep { $_ eq $extension } @extensions;
324 @files = map { "$path.$_" } @extensions;
328 dependencies => [ depends_on('extensions'), depends_on('config_path') ],
332 sub build_local_files_service {
335 return Bread::Board::BlockInjection->new(
336 lifecycle => 'Singleton',
337 name => 'local_files',
341 my ( $path, $extension ) = @{$s->param('config_path')};
342 my $suffix = $s->param('config_local_suffix');
344 my @extensions = @{$s->param('extensions')};
348 die "Unable to handle files with the extension '${extension}'" unless grep { $_ eq $extension } @extensions;
349 $path =~ s{\.$extension}{_$suffix.$extension};
352 @files = map { "${path}_${suffix}.$_" } @extensions;
356 dependencies => [ depends_on('extensions'), depends_on('config_path'), depends_on('config_local_suffix') ],
360 sub build_class_config_service {
363 return Bread::Board::BlockInjection->new(
364 lifecycle => 'Singleton',
365 name => 'class_config',
368 my $app = $s->param('catalyst_application');
370 # Container might be called outside Catalyst context
371 return {} unless Class::MOP::is_class_loaded($app);
373 # config might not have been defined
374 return $app->config || {};
376 dependencies => [ depends_on('catalyst_application') ],
380 sub build_global_config_service {
383 return Bread::Board::BlockInjection->new(
384 lifecycle => 'Singleton',
385 name => 'global_config',
389 return Config::Any->load_files({
390 files => $s->param('global_files'),
391 filter => \&_fix_syntax,
393 driver_args => $s->param('driver'),
396 dependencies => [ depends_on('global_files') ],
400 sub build_local_config_service {
403 return Bread::Board::BlockInjection->new(
404 lifecycle => 'Singleton',
405 name => 'local_config',
409 return Config::Any->load_files({
410 files => $s->param('local_files'),
411 filter => \&_fix_syntax,
413 driver_args => $s->param('driver'),
416 dependencies => [ depends_on('local_files') ],
420 sub build_config_path_service {
423 return Bread::Board::BlockInjection->new(
424 lifecycle => 'Singleton',
425 name => 'config_path',
429 my $path = $s->param('path');
430 my $prefix = $s->param('prefix');
432 my ( $extension ) = ( $path =~ m{\.(.{1,4})$} );
435 $path =~ s{[\/\\]$}{};
439 return [ $path, $extension ];
441 dependencies => [ depends_on('prefix'), depends_on('path') ],
445 sub build_config_local_suffix_service {
448 return Bread::Board::BlockInjection->new(
449 lifecycle => 'Singleton',
450 name => 'config_local_suffix',
453 my $suffix = Catalyst::Utils::env_value( $s->param('catalyst_application'), 'CONFIG_LOCAL_SUFFIX' ) || $self->config_local_suffix;
457 dependencies => [ depends_on('catalyst_application') ],
461 sub build_locate_components_service {
464 return Bread::Board::BlockInjection->new(
465 lifecycle => 'Singleton',
466 name => 'locate_components',
469 my $class = $s->param('catalyst_application');
470 my $config = $s->param('config')->{ setup_components };
472 Catalyst::Exception->throw(
473 qq{You are using search_extra config option. That option is\n} .
474 qq{deprecated, please refer to the documentation for\n} .
475 qq{other ways of achieving the same results.\n}
476 ) if delete $config->{ search_extra };
478 my @paths = qw( ::Controller ::C ::Model ::M ::View ::V );
480 my $locator = Module::Pluggable::Object->new(
481 search_path => [ map { s/^(?=::)/$class/; $_; } @paths ],
485 return [ $locator->plugins ];
487 dependencies => [ depends_on('catalyst_application'), depends_on('config') ],
491 sub setup_components {
493 my $class = $self->resolve( service => 'catalyst_application' );
494 my @comps = @{ $self->resolve( service => 'locate_components' ) };
495 my %comps = map { $_ => 1 } @comps;
496 my $deprecatedcatalyst_component_names = 0;
498 my $app_locate_components_addr = refaddr(
499 $class->can('locate_components')
501 my $cat_locate_components_addr = refaddr(
502 Catalyst->can('locate_components')
505 if ($app_locate_components_addr != $cat_locate_components_addr) {
506 # FIXME - why not just say: @comps = $class->locate_components() ?
507 $class->log->warn(qq{You have overridden locate_components. That } .
508 qq{no longer works. Please refer to the documentation to achieve } .
509 qq{similar results.\n}
513 for my $component ( @comps ) {
515 # We pass ignore_loaded here so that overlay files for (e.g.)
516 # Model::DBI::Schema sub-classes are loaded - if it's in @comps
517 # we know M::P::O found a file on disk so this is safe
519 Catalyst::Utils::ensure_class_loaded( $component, { ignore_loaded => 1 } );
522 for my $component (@comps) {
523 $self->add_component( $component );
524 # FIXME - $instance->expand_modules() is broken
525 my @expanded_components = $self->expand_component_module( $component );
528 !$deprecatedcatalyst_component_names &&
529 ($deprecatedcatalyst_component_names = $component =~ m/::[CMV]::/) ||
530 ($deprecatedcatalyst_component_names = grep { /::[CMV]::/ } @expanded_components)
532 # FIXME - should I be calling warn here?
533 # Maybe it's time to remove it, or become fatal
534 $class->log->warn(qq{Your application is using the deprecated ::[MVC]:: type naming scheme.\n}.
535 qq{Please switch your class names to ::Model::, ::View:: and ::Controller: as appropriate.\n}
539 for my $component (@expanded_components) {
540 $self->add_component( $component )
541 unless $comps{$component};
550 prefix => $_ eq 'Component' ? '' : $_ . '::',
551 values => delete $config->{ lc $_ } || delete $config->{ $_ }
553 grep { ref $config->{ lc $_ } || ref $config->{ $_ } }
554 qw( Component Model M View V Controller C Plugin )
557 foreach my $comp ( @components ) {
558 my $prefix = $comp->{ prefix };
559 foreach my $element ( keys %{ $comp->{ values } } ) {
560 $config->{ "$prefix$element" } = $comp->{ values }->{ $element };
565 sub _config_substitutions {
566 my ( $self, $name, $subs, $arg ) = @_;
568 $subs->{ HOME } ||= sub { shift->path_to( '' ); };
572 if (! defined($ENV{$v})) {
573 Catalyst::Exception->throw( message =>
574 "Missing environment variable: $v" );
580 $subs->{ path_to } ||= sub { shift->path_to( @_ ); };
581 $subs->{ literal } ||= sub { return $_[ 1 ]; };
582 my $subsre = join( '|', keys %$subs );
584 $arg =~ s{__($subsre)(?:\((.+?)\))?__}{ $subs->{ $1 }->( $name, $2 ? split( /,/, $2 ) : () ) }eg;
588 sub get_component_from_sub_container {
589 my ( $self, $sub_container_name, $name, $c, @args ) = @_;
591 my $sub_container = $self->get_sub_container( $sub_container_name );
594 my $default = $sub_container->default_component;
596 return $sub_container->get_component( $default, $c, @args )
597 if $default && $sub_container->has_service( $default );
599 # FIXME - should I be calling $c->log->warn here?
600 # this is never a controller, so this is safe
601 $c->log->warn( "Calling \$c->$sub_container_name() is not supported unless you specify one of:" );
602 $c->log->warn( "* \$c->config(default_$sub_container_name => 'the name of the default $sub_container_name to use')" );
603 $c->log->warn( "* \$c->stash->{current_$sub_container_name} # the name of the view to use for this request" );
604 $c->log->warn( "* \$c->stash->{current_${sub_container_name}_instance} # the instance of the $sub_container_name to use for this request" );
609 return $sub_container->get_component_regexp( $name, $c, @args )
612 return $sub_container->get_component( $name, $c, @args )
613 if $sub_container->has_service( $name );
616 "Attempted to use $sub_container_name '$name', " .
617 "but it does not exist"
624 my ( $self, $component, @args ) = @_;
625 my ( $type, $name ) = _get_component_type_name($component);
628 return $self->get_component_from_sub_container(
632 my $query = ref $component
637 for my $subcontainer_name (qw/model view controller/) {
638 my $subcontainer = $self->get_sub_container( $subcontainer_name );
639 my @components = $subcontainer->get_service_list;
640 @result = grep { m{$component} } @components;
642 return map { $subcontainer->get_component( $_, @args ) } @result
646 # one last search for things like $c->comp(qr/::M::/)
647 @result = $self->_find_component_regexp(
649 ) if !@result and ref $component;
651 # it expects an empty list on failed searches
655 sub _find_component_regexp {
656 my ( $self, $component, $ctx, @args ) = @_;
659 my @components = grep { m{$component} } keys %{ $self->get_all_components($ctx) };
662 my ($type, $name) = _get_component_type_name($_);
664 push @result, $self->get_component_from_sub_container(
665 $type, $name, $ctx, @args
673 # what exactly should this method return?
674 # By default, for back-compatibility, we have two services for each component:
675 # - one in the model|view|controller sub-container
676 # - and another in the component sub-container
677 # the latter is a Singleton, it executes the COMPONENT method, and the first
678 # is executed at every call, executing ACCEPT_CONTEXT. It's just a layer.
680 # So the one in $type sub-container is pretty useless by default. But when the
681 # user overrides the container (which is what we want), the sub-container they
682 # will use is precisely $type, not 'component'. So for now, I'm returning both
683 # services, to decide later what to do.
684 sub get_all_component_services {
688 my $components_container = $self->get_sub_container('component');
690 foreach my $type (qw/model view controller /) {
691 my $container = $self->get_sub_container($type);
693 for my $component ($container->get_service_list) {
694 my $comp_service = $container->get_service($component);
696 my $key = $comp_service->catalyst_component_name;
699 service => $comp_service,
702 my $comp_name = "${type}_${component}";
703 if ($components_container->has_service($comp_name)) {
704 $values{backcompat_service} = $components_container->get_service($comp_name);
707 $components{$key} = \%values;
711 return lock_hash %components;
714 sub get_all_singleton_lifecycle_components {
718 my $components_container = $self->get_sub_container('component');
720 foreach my $type (qw/model view controller /) {
721 my $container = $self->get_sub_container($type);
723 for my $component ($container->get_service_list) {
724 my $comp_service = $container->get_service($component);
726 my $key = $comp_service->catalyst_component_name;
727 my $lifecycle = $comp_service->lifecycle;
728 my $comp_name = "${type}_${component}";
730 if (defined $lifecycle && $lifecycle eq 'Singleton') {
731 $components{$key} = $comp_service->get;
733 elsif ($components_container->has_service($comp_name)) {
734 $components{$key} = $components_container->get_service($comp_name)->get;
739 return lock_hash %components;
742 sub get_all_components {
743 my ($self, $class) = @_;
746 foreach my $type (qw/model view controller /) {
747 my $container = $self->get_sub_container($type);
749 for my $component ($container->get_service_list) {
750 my $comp_service = $container->get_service($component);
752 $components{$comp_service->catalyst_component_name} = $comp_service->get(ctx => $class);
756 return lock_hash %components;
760 my ( $self, $component ) = @_;
761 my ( $type, $name ) = _get_component_type_name($component);
765 # The 'component' sub-container will create the object, and store it's
766 # instance, which, by default, will live throughout the application.
767 # The model/view/controller sub-containers only reference the instance
768 # held in the aforementioned sub-container, and execute the ACCEPT_CONTEXT
769 # sub every time they are called, when it exists.
770 my $instance_container = $self->get_sub_container('component');
771 my $accept_context_container = $self->get_sub_container($type);
773 # Custom containers might have added the service already
774 # We don't want to override that
775 return if $accept_context_container->has_service( $name );
777 my $component_service_name = "${type}_${name}";
779 $instance_container->add_service(
780 Catalyst::IOC::ConstructorInjection->new(
781 name => $component_service_name,
782 catalyst_component_name => $component,
784 lifecycle => 'Singleton',
786 depends_on( '/catalyst_application' ),
790 # XXX - FIXME - We have to explicitly build the service here,
791 # causing the COMPONENT method to be called early here, as otherwise
792 # if the component method defines other classes (e.g. the
793 # ACCEPT_CONTEXT injection Model::DBIC::Schema does)
794 # then they won't be found by Devel::InnerPackage
795 # see also t/aggregate/unit_core_component_loading.t
796 $instance_container->get_service($component_service_name)->get;
798 $accept_context_container->add_service(
799 Catalyst::IOC::BlockInjection->new(
801 catalyst_component_name => $component,
803 depends_on( "/component/$component_service_name" ),
805 block => sub { shift->param($component_service_name) },
810 # FIXME: should this sub exist?
811 # should it be moved to Catalyst::Utils,
812 # or replaced by something already existing there?
813 sub _get_component_type_name {
814 my ( $component ) = @_;
817 while ( !$result and (my $index = index $component, '::') > 0 ) {
818 my $type = lc substr $component, 0, $index;
819 $component = substr $component, $index + 2;
820 $result = first { $type eq $_ or $type eq substr($_, 0, 1) }
821 qw{ model view controller };
824 return ($result, $component);
827 sub expand_component_module {
828 my ( $class, $module ) = @_;
829 return Devel::InnerPackage::list_packages( $module );
832 __PACKAGE__->meta->make_immutable;
842 Catalyst::Container - IOC for Catalyst components
850 =head1 Methods for Building Containers
852 =head2 build_component_subcontainer
854 Container that stores all components, i.e. all models, views and controllers
855 together. Each service is an instance of the actual component, and by default
856 it lives while the application is running. Retrieving components from this
857 sub-container will instantiate the component, if it hasn't been instantiated
858 already, but will not execute ACCEPT_CONTEXT.
860 =head2 build_model_subcontainer
862 Container that stores references for all models that are inside the components
863 sub-container. Retrieving a model triggers ACCEPT_CONTEXT, if it exists.
865 =head2 build_view_subcontainer
867 Same as L<build_model_subcontainer>, but for views.
869 =head2 build_controller_subcontainer
871 Same as L<build_model_subcontainer>, but for controllers.
873 =head1 Methods for Building Services
875 =head2 build_catalyst_application_service
877 Name of the application (such as MyApp).
879 =head2 build_home_service
881 The application home directory. All the files (including classes, scripts, etc)
882 created for this application are in this directory, or in a sub-directory below
885 =head2 build_root_dir_service
887 Inside the application home (as explained in L</build_home_service>), there is
888 a root directory. This is where all templates and static files are.
890 =head2 build_driver_service
892 Config options passed directly to the driver being used.
894 =head2 build_file_service
898 =head2 build_substitutions_service
900 This method substitutes macros found with calls to a function. There are a
901 number of default macros:
905 =item * C<__HOME__> - replaced with C<$c-E<gt>path_to('')>
907 =item * C<__ENV(foo)__> - replaced with the value of C<$ENV{foo}>
909 =item * C<__path_to(foo/bar)__> - replaced with C<$c-E<gt>path_to('foo/bar')>
911 =item * C<__literal(__FOO__)__> - leaves __FOO__ alone (allows you to use
912 C<__DATA__> as a config value, for example)
916 The parameter list is split on comma (C<,>). You can override this method to
917 do your own string munging, or you can define your own macros in
918 C<< <MyApp->config( 'Plugin::ConfigLoader' => { substitutions => { ... } } ) >>.
921 MyApp->config( 'Plugin::ConfigLoader' => {
923 baz => sub { my $c = shift; qux( @_ ); },
927 The above will respond to C<__baz(x,y)__> in config strings.
929 =head2 build_extensions_service
931 Config::Any's available config file extensions (e.g. xml, json, pl, etc).
933 =head2 build_prefix_service
935 The prefix, based on the application name, that will be used to look-up the
936 config files (which will be in the format $prefix.$extension). If the app is
937 MyApp::Foo, the prefix will be myapp_foo.
939 =head2 build_path_service
941 The path to the config file (or environment variable, if defined).
943 =head2 build_config_service
945 The resulting configuration for the application, after it has successfully
946 been loaded, and all substitutions have been made.
948 =head2 build_raw_config_service
950 The merge of local_config and global_config hashes, before substitutions.
952 =head2 build_global_files_service
954 Gets all files for config that don't have the local_suffix, such as myapp.conf.
956 =head2 build_local_files_service
958 Gets all files for config that have the local_suffix, such as myapp_local.conf.
960 =head2 build_global_config_service
962 Reads config from global_files.
964 =head2 build_local_config_service
966 Reads config from local_files.
968 =head2 build_class_config_service
970 Reads config set from the application's class attribute config,
971 i.e. MyApp->config( name => 'MyApp', ... )
973 =head2 build_config_path_service
975 Splits the path to the config file, and returns on array ref containing
976 the path to the config file minus the extension in the first position,
977 and the extension in the second.
979 =head2 build_config_local_suffix_service
981 Determines the suffix of files used to override the main config. By default
982 this value is C<local>, which will load C<myapp_local.conf>. The suffix can
983 be specified in the following order of preference:
987 =item * C<$ENV{ MYAPP_CONFIG_LOCAL_SUFFIX }>
989 =item * C<$ENV{ CATALYST_CONFIG_LOCAL_SUFFIX }>
993 The first one of these values found replaces the default of C<local> in the
994 name of the local config file to be loaded.
996 For example, if C< $ENV{ MYAPP_CONFIG_LOCAL_SUFFIX }> is set to C<testing>,
997 ConfigLoader will try and load C<myapp_testing.conf> instead of
1000 =head2 build_locate_components_service
1002 This method is meant to provide a list of component modules that should be
1003 setup for the application. By default, it will use L<Module::Pluggable>.
1005 Specify a C<setup_components> config option to pass additional options directly
1006 to L<Module::Pluggable>.
1008 =head1 Other methods
1010 =head2 get_component_from_sub_container($sub_container, $name, $c, @args)
1012 Looks for components in a given sub-container (such as controller, model or
1013 view), and returns the searched component. If $name is undef, it returns the
1014 default component (such as default_view, if $sub_container is 'view'). If
1015 $name is a regexp, it returns an array of matching components. Otherwise, it
1016 looks for the component with name $name.
1018 =head2 get_all_components
1020 Fetches all the components, in each of the sub_containers model, view and
1021 controller, and returns a read-only hash. The keys are the class names, and
1022 the values are the blessed objects. This is what is returned by $c->components.
1024 =head2 add_component
1026 Adds a component to the appropriate sub-container. The sub-container is guessed
1027 by the component name given.
1029 =head2 find_component
1031 Searches for components in all containers. If $component is the full class
1032 name, the sub-container is guessed, and it gets the searched component in there.
1033 Otherwise, it looks for a component with that name in all sub-containers. If
1034 $component is a regexp it calls _find_component_regexp and matches all
1035 components against that regexp.
1037 =head2 expand_component_module
1039 Components found by C<locate_components> will be passed to this method, which
1040 is expected to return a list of component (package) names to be set up.
1042 =head2 setup_components
1044 Uses locate_components service to list the components, and adds them to the
1045 appropriate sub-containers, using add_component().
1049 Catalyst Contributors, see Catalyst.pm
1053 This library is free software. You can redistribute it and/or modify it under
1054 the same terms as Perl itself.