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',
49 writer => '_set_application',
50 default => sub { shift->application_name },
53 has application_name => (
59 has sub_container_class => (
63 default => 'Catalyst::IOC::SubContainer',
65 new_sub_container => 'new',
70 my ( $self, $params ) = @_;
73 $self->${\"build_${_}_service"}
95 my $config = $self->resolve( service => 'config' );
97 # don't force default_component to be undef if the config wasn't set
98 my @default_view = $config->{default_view}
99 ? ( default_component => $config->{default_view} )
102 my @default_model = $config->{default_model}
103 ? ( default_component => $config->{default_model} )
107 $self->add_sub_container(
108 $self->build_component_subcontainer
111 $self->add_sub_container(
112 $self->build_controller_subcontainer
115 $self->add_sub_container(
116 $self->build_view_subcontainer( @default_view )
119 $self->add_sub_container(
120 $self->build_model_subcontainer( @default_model )
126 my $class = ref $self;
127 ${ $class . '::customise_container' }->($self)
128 if ${ $class . '::customise_container' };
132 sub build_model_subcontainer {
135 return $self->new_sub_container( @_,
140 sub build_view_subcontainer {
143 return $self->new_sub_container( @_,
148 sub build_controller_subcontainer {
151 return $self->new_sub_container(
152 name => 'controller',
156 sub build_component_subcontainer {
159 return Bread::Board::Container->new(
164 sub build_home_service {
167 return Bread::Board::BlockInjection->new(
168 lifecycle => 'Singleton',
172 my $class = $self->param('application_name');
175 if ( my $env = Catalyst::Utils::env_value( $class, 'HOME' ) ) {
179 $home ||= Catalyst::Utils::home($class);
182 dependencies => [ depends_on('application_name') ],
186 # FIXME: very ambiguous - maybe root_dir?
187 sub build_root_service {
190 return Bread::Board::BlockInjection->new(
191 lifecycle => 'Singleton',
196 return Path::Class::Dir->new( $self->param('home') )->subdir('root');
198 dependencies => [ depends_on('home') ],
202 sub build_application_service {
205 return Bread::Board::Literal->new(
206 name => 'application',
207 value => $self->application,
211 sub build_application_name_service {
214 return Bread::Board::Literal->new( name => 'application_name', value => $self->application_name );
217 sub build_driver_service {
220 return Bread::Board::Literal->new( name => 'driver', value => $self->driver );
223 sub build_file_service {
226 return Bread::Board::Literal->new( name => 'file', value => $self->file );
229 sub build_substitutions_service {
232 return Bread::Board::Literal->new( name => 'substitutions', value => $self->substitutions );
235 sub build_extensions_service {
238 return Bread::Board::BlockInjection->new(
239 lifecycle => 'Singleton',
240 name => 'extensions',
242 return \@{Config::Any->extensions};
247 sub build_prefix_service {
250 return Bread::Board::BlockInjection->new(
251 lifecycle => 'Singleton',
254 return Catalyst::Utils::appprefix( shift->param('application_name') );
256 dependencies => [ depends_on('application_name') ],
260 sub build_path_service {
263 return Bread::Board::BlockInjection->new(
264 lifecycle => 'Singleton',
269 return Catalyst::Utils::env_value( $s->param('application_name'), 'CONFIG' )
271 || $s->param('application_name')->path_to( $s->param('prefix') );
273 dependencies => [ depends_on('file'), depends_on('application_name'), depends_on('prefix') ],
277 sub build_config_service {
280 return Bread::Board::BlockInjection->new(
281 lifecycle => 'Singleton',
286 my $v = Data::Visitor::Callback->new(
288 return unless defined $_;
289 return $self->_config_substitutions( $s->param('application_name'), $s->param('substitutions'), $_ );
293 $v->visit( $s->param('raw_config') );
295 dependencies => [ depends_on('application_name'), depends_on('raw_config'), depends_on('substitutions') ],
299 sub build_raw_config_service {
302 return Bread::Board::BlockInjection->new(
303 lifecycle => 'Singleton',
304 name => 'raw_config',
308 my @global = @{$s->param('global_config')};
309 my @locals = @{$s->param('local_config')};
311 my $config = $s->param('class_config');
313 for my $cfg (@global, @locals) {
315 $config = Catalyst::Utils::merge_hashes( $config, $cfg->{$_} );
321 dependencies => [ depends_on('global_config'), depends_on('local_config'), depends_on('class_config') ],
325 sub build_global_files_service {
328 return Bread::Board::BlockInjection->new(
329 lifecycle => 'Singleton',
330 name => 'global_files',
334 my ( $path, $extension ) = @{$s->param('config_path')};
336 my @extensions = @{$s->param('extensions')};
340 die "Unable to handle files with the extension '${extension}'" unless grep { $_ eq $extension } @extensions;
343 @files = map { "$path.$_" } @extensions;
347 dependencies => [ depends_on('extensions'), depends_on('config_path') ],
351 sub build_local_files_service {
354 return Bread::Board::BlockInjection->new(
355 lifecycle => 'Singleton',
356 name => 'local_files',
360 my ( $path, $extension ) = @{$s->param('config_path')};
361 my $suffix = $s->param('config_local_suffix');
363 my @extensions = @{$s->param('extensions')};
367 die "Unable to handle files with the extension '${extension}'" unless grep { $_ eq $extension } @extensions;
368 $path =~ s{\.$extension}{_$suffix.$extension};
371 @files = map { "${path}_${suffix}.$_" } @extensions;
375 dependencies => [ depends_on('extensions'), depends_on('config_path'), depends_on('config_local_suffix') ],
379 sub build_class_config_service {
382 return Bread::Board::BlockInjection->new(
383 lifecycle => 'Singleton',
384 name => 'class_config',
387 my $app = $s->param('application_name');
389 # Container might be called outside Catalyst context
390 return {} unless Class::MOP::is_class_loaded($app);
392 # config might not have been defined
393 return $app->config || {};
395 dependencies => [ depends_on('application_name') ],
399 sub build_global_config_service {
402 return Bread::Board::BlockInjection->new(
403 lifecycle => 'Singleton',
404 name => 'global_config',
408 return Config::Any->load_files({
409 files => $s->param('global_files'),
410 filter => \&_fix_syntax,
412 driver_args => $s->param('driver'),
415 dependencies => [ depends_on('global_files') ],
419 sub build_local_config_service {
422 return Bread::Board::BlockInjection->new(
423 lifecycle => 'Singleton',
424 name => 'local_config',
428 return Config::Any->load_files({
429 files => $s->param('local_files'),
430 filter => \&_fix_syntax,
432 driver_args => $s->param('driver'),
435 dependencies => [ depends_on('local_files') ],
439 sub build_config_path_service {
442 return Bread::Board::BlockInjection->new(
443 lifecycle => 'Singleton',
444 name => 'config_path',
448 my $path = $s->param('path');
449 my $prefix = $s->param('prefix');
451 my ( $extension ) = ( $path =~ m{\.(.{1,4})$} );
454 $path =~ s{[\/\\]$}{};
458 return [ $path, $extension ];
460 dependencies => [ depends_on('prefix'), depends_on('path') ],
464 sub build_config_local_suffix_service {
467 return Bread::Board::BlockInjection->new(
468 lifecycle => 'Singleton',
469 name => 'config_local_suffix',
472 my $suffix = Catalyst::Utils::env_value( $s->param('application_name'), 'CONFIG_LOCAL_SUFFIX' ) || $self->config_local_suffix;
476 dependencies => [ depends_on('application_name') ],
480 sub build_locate_components_service {
483 return Bread::Board::BlockInjection->new(
484 lifecycle => 'Singleton',
485 name => 'locate_components',
488 my $class = $s->param('application_name');
489 my $config = $s->param('config')->{ setup_components };
491 Catalyst::Exception->throw(
492 qq{You are using search_extra config option. That option is\n} .
493 qq{deprecated, please refer to the documentation for\n} .
494 qq{other ways of achieving the same results.\n}
495 ) if delete $config->{ search_extra };
497 my @paths = qw( ::Controller ::C ::Model ::M ::View ::V );
499 my $locator = Module::Pluggable::Object->new(
500 search_path => [ map { s/^(?=::)/$class/; $_; } @paths ],
504 return [ $locator->plugins ];
506 dependencies => [ depends_on('application_name'), depends_on('config') ],
510 sub setup_components {
512 my $class = $self->resolve( service => 'application_name' );
513 my @comps = @{ $self->resolve( service => 'locate_components' ) };
514 my %comps = map { $_ => 1 } @comps;
515 my $deprecatedcatalyst_component_names = 0;
517 my $app_locate_components_addr = refaddr(
518 $class->can('locate_components')
520 my $cat_locate_components_addr = refaddr(
521 Catalyst->can('locate_components')
524 if ($app_locate_components_addr != $cat_locate_components_addr) {
525 # FIXME - why not just say: @comps = $class->locate_components() ?
526 $class->log->warn(qq{You have overridden locate_components. That } .
527 qq{no longer works. Please refer to the documentation to achieve } .
528 qq{similar results.\n}
532 for my $component ( @comps ) {
534 # We pass ignore_loaded here so that overlay files for (e.g.)
535 # Model::DBI::Schema sub-classes are loaded - if it's in @comps
536 # we know M::P::O found a file on disk so this is safe
538 Catalyst::Utils::ensure_class_loaded( $component, { ignore_loaded => 1 } );
541 for my $component (@comps) {
542 $self->add_component( $component );
543 # FIXME - $instance->expand_modules() is broken
544 my @expanded_components = $self->expand_component_module( $component );
547 !$deprecatedcatalyst_component_names &&
548 ($deprecatedcatalyst_component_names = $component =~ m/::[CMV]::/) ||
549 ($deprecatedcatalyst_component_names = grep { /::[CMV]::/ } @expanded_components)
551 # FIXME - should I be calling warn here?
552 # Maybe it's time to remove it, or become fatal
553 $class->log->warn(qq{Your application is using the deprecated ::[MVC]:: type naming scheme.\n}.
554 qq{Please switch your class names to ::Model::, ::View:: and ::Controller: as appropriate.\n}
558 for my $component (@expanded_components) {
559 $self->add_component( $component )
560 unless $comps{$component};
569 prefix => $_ eq 'Component' ? '' : $_ . '::',
570 values => delete $config->{ lc $_ } || delete $config->{ $_ }
572 grep { ref $config->{ lc $_ } || ref $config->{ $_ } }
573 qw( Component Model M View V Controller C Plugin )
576 foreach my $comp ( @components ) {
577 my $prefix = $comp->{ prefix };
578 foreach my $element ( keys %{ $comp->{ values } } ) {
579 $config->{ "$prefix$element" } = $comp->{ values }->{ $element };
584 sub _config_substitutions {
585 my ( $self, $name, $subs, $arg ) = @_;
587 $subs->{ HOME } ||= sub { shift->path_to( '' ); };
591 if (! defined($ENV{$v})) {
592 Catalyst::Exception->throw( message =>
593 "Missing environment variable: $v" );
599 $subs->{ path_to } ||= sub { shift->path_to( @_ ); };
600 $subs->{ literal } ||= sub { return $_[ 1 ]; };
601 my $subsre = join( '|', keys %$subs );
603 $arg =~ s{__($subsre)(?:\((.+?)\))?__}{ $subs->{ $1 }->( $name, $2 ? split( /,/, $2 ) : () ) }eg;
607 sub get_component_from_sub_container {
608 my ( $self, $sub_container_name, $name, $c, @args ) = @_;
610 my $sub_container = $self->get_sub_container( $sub_container_name );
613 my $default = $sub_container->default_component;
615 return $sub_container->get_component( $default, $c, @args )
616 if $default && $sub_container->has_service( $default );
618 # FIXME - should I be calling $c->log->warn here?
619 # this is never a controller, so this is safe
620 $c->log->warn( "Calling \$c->$sub_container_name() is not supported unless you specify one of:" );
621 $c->log->warn( "* \$c->config(default_$sub_container_name => 'the name of the default $sub_container_name to use')" );
622 $c->log->warn( "* \$c->stash->{current_$sub_container_name} # the name of the view to use for this request" );
623 $c->log->warn( "* \$c->stash->{current_${sub_container_name}_instance} # the instance of the $sub_container_name to use for this request" );
628 return $sub_container->get_component_regexp( $name, $c, @args )
631 return $sub_container->get_component( $name, $c, @args )
632 if $sub_container->has_service( $name );
635 "Attempted to use $sub_container_name '$name', " .
636 "but it does not exist"
643 my ( $self, $component, @args ) = @_;
644 my ( $type, $name ) = _get_component_type_name($component);
647 return $self->get_component_from_sub_container(
651 my $query = ref $component
656 for my $subcontainer_name (qw/model view controller/) {
657 my $subcontainer = $self->get_sub_container( $subcontainer_name );
658 my @components = $subcontainer->get_service_list;
659 @result = grep { m{$component} } @components;
661 return map { $subcontainer->get_component( $_, @args ) } @result
665 # one last search for things like $c->comp(qr/::M::/)
666 @result = $self->_find_component_regexp(
668 ) if !@result and ref $component;
670 # it expects an empty list on failed searches
674 sub _find_component_regexp {
675 my ( $self, $component, $ctx, @args ) = @_;
678 my @components = grep { m{$component} } keys %{ $self->get_all_components($ctx) };
681 my ($type, $name) = _get_component_type_name($_);
683 push @result, $self->get_component_from_sub_container(
684 $type, $name, $ctx, @args
691 sub get_all_components {
692 my ($self, $class) = @_;
695 # FIXME - if we're getting from these containers, we need to either:
696 # - pass 'ctx' and 'accept_context_args' OR
697 # - make these params optional
698 # big problem when setting up the dispatcher - this method is called
699 # as $container->get_all_components('MyApp'). What to do with Request
701 foreach my $type (qw/model view controller /) {
702 my $container = $self->get_sub_container($type);
704 for my $component ($container->get_service_list) {
705 my $comp_service = $container->get_service($component);
707 $components{$comp_service->catalyst_component_name} = $comp_service->get(ctx => $class);
711 return lock_hash %components;
715 my ( $self, $component ) = @_;
716 my ( $type, $name ) = _get_component_type_name($component);
720 # The 'component' sub-container will create the object, and store it's
721 # instance, which, by default, will live throughout the application.
722 # The model/view/controller sub-containers only reference the instance
723 # held in the aforementioned sub-container, and execute the ACCEPT_CONTEXT
724 # sub every time they are called, when it exists.
725 my $instance_container = $self->get_sub_container('component');
726 my $accept_context_container = $self->get_sub_container($type);
728 # Custom containers might have added the service already
729 # We don't want to override that
730 return if $accept_context_container->has_service( $name );
732 my $component_service_name = "${type}_${name}";
734 $instance_container->add_service(
735 Catalyst::IOC::ConstructorInjection->new(
736 name => $component_service_name,
737 catalyst_component_name => $component,
739 lifecycle => 'Singleton',
741 depends_on( '/application' ),
745 # XXX - FIXME - We have to explicitly build the service here,
746 # causing the COMPONENT method to be called early here, as otherwise
747 # if the component method defines other classes (e.g. the
748 # ACCEPT_CONTEXT injection Model::DBIC::Schema does)
749 # then they won't be found by Devel::InnerPackage
750 # see also t/aggregate/unit_core_component_loading.t
751 $instance_container->get_service($component_service_name)->get;
753 $accept_context_container->add_service(
754 Catalyst::IOC::BlockInjection->new(
756 catalyst_component_name => $component,
758 depends_on( "/component/$component_service_name" ),
760 block => sub { shift->param($component_service_name) },
765 # FIXME: should this sub exist?
766 # should it be moved to Catalyst::Utils,
767 # or replaced by something already existing there?
768 sub _get_component_type_name {
769 my ( $component ) = @_;
772 while ( !$result and (my $index = index $component, '::') > 0 ) {
773 my $type = lc substr $component, 0, $index;
774 $component = substr $component, $index + 2;
775 $result = first { $type eq $_ or $type eq substr($_, 0, 1) }
776 qw{ model view controller };
779 return ($result, $component);
782 sub expand_component_module {
783 my ( $class, $module ) = @_;
784 return Devel::InnerPackage::list_packages( $module );
787 __PACKAGE__->meta->make_immutable;
797 Catalyst::Container - IOC for Catalyst components
805 =head1 Methods for Building Containers
807 =head2 build_component_subcontainer
809 Container that stores all components, i.e. all models, views and controllers
810 together. Each service is an instance of the actual component, and by default
811 it lives while the application is running. Retrieving components from this
812 sub-container will instantiate the component, if it hasn't been instantiated
813 already, but will not execute ACCEPT_CONTEXT.
815 =head2 build_model_subcontainer
817 Container that stores references for all models that are inside the components
818 sub-container. Retrieving a model triggers ACCEPT_CONTEXT, if it exists.
820 =head2 build_view_subcontainer
822 Same as L<build_model_subcontainer>, but for views.
824 =head2 build_controller_subcontainer
826 Same as L<build_model_subcontainer>, but for controllers.
828 =head1 Methods for Building Services
830 =head2 build_application_name_service
832 Name of the application (such as MyApp).
834 =head2 build_driver_service
836 Config options passed directly to the driver being used.
838 =head2 build_file_service
842 =head2 build_substitutions_service
844 This method substitutes macros found with calls to a function. There are a
845 number of default macros:
849 =item * C<__HOME__> - replaced with C<$c-E<gt>path_to('')>
851 =item * C<__ENV(foo)__> - replaced with the value of C<$ENV{foo}>
853 =item * C<__path_to(foo/bar)__> - replaced with C<$c-E<gt>path_to('foo/bar')>
855 =item * C<__literal(__FOO__)__> - leaves __FOO__ alone (allows you to use
856 C<__DATA__> as a config value, for example)
860 The parameter list is split on comma (C<,>). You can override this method to
861 do your own string munging, or you can define your own macros in
862 C<< <MyApp->config( 'Plugin::ConfigLoader' => { substitutions => { ... } } ) >>.
865 MyApp->config( 'Plugin::ConfigLoader' => {
867 baz => sub { my $c = shift; qux( @_ ); },
871 The above will respond to C<__baz(x,y)__> in config strings.
873 =head2 build_extensions_service
875 Config::Any's available config file extensions (e.g. xml, json, pl, etc).
877 =head2 build_prefix_service
879 The prefix, based on the application name, that will be used to look-up the
880 config files (which will be in the format $prefix.$extension). If the app is
881 MyApp::Foo, the prefix will be myapp_foo.
883 =head2 build_path_service
885 The path to the config file (or environment variable, if defined).
887 =head2 build_config_service
889 The resulting configuration for the application, after it has successfully
890 been loaded, and all substitutions have been made.
892 =head2 build_raw_config_service
894 The merge of local_config and global_config hashes, before substitutions.
896 =head2 build_global_files_service
898 Gets all files for config that don't have the local_suffix, such as myapp.conf.
900 =head2 build_local_files_service
902 Gets all files for config that have the local_suffix, such as myapp_local.conf.
904 =head2 build_global_config_service
906 Reads config from global_files.
908 =head2 build_local_config_service
910 Reads config from local_files.
912 =head2 build_class_config_service
914 Reads config set from the application's class attribute config,
915 i.e. MyApp->config( name => 'MyApp', ... )
917 =head2 build_config_path_service
919 Splits the path to the config file, and returns on array ref containing
920 the path to the config file minus the extension in the first position,
921 and the extension in the second.
923 =head2 build_config_local_suffix_service
925 Determines the suffix of files used to override the main config. By default
926 this value is C<local>, which will load C<myapp_local.conf>. The suffix can
927 be specified in the following order of preference:
931 =item * C<$ENV{ MYAPP_CONFIG_LOCAL_SUFFIX }>
933 =item * C<$ENV{ CATALYST_CONFIG_LOCAL_SUFFIX }>
937 The first one of these values found replaces the default of C<local> in the
938 name of the local config file to be loaded.
940 For example, if C< $ENV{ MYAPP_CONFIG_LOCAL_SUFFIX }> is set to C<testing>,
941 ConfigLoader will try and load C<myapp_testing.conf> instead of
944 =head2 build_locate_components_service
946 This method is meant to provide a list of component modules that should be
947 setup for the application. By default, it will use L<Module::Pluggable>.
949 Specify a C<setup_components> config option to pass additional options directly
950 to L<Module::Pluggable>.
954 =head2 get_component_from_sub_container($sub_container, $name, $c, @args)
956 Looks for components in a given sub-container (such as controller, model or
957 view), and returns the searched component. If $name is undef, it returns the
958 default component (such as default_view, if $sub_container is 'view'). If
959 $name is a regexp, it returns an array of matching components. Otherwise, it
960 looks for the component with name $name.
962 =head2 get_all_components
964 Fetches all the components, in each of the sub_containers model, view and
965 controller, and returns a read-only hash. The keys are the class names, and
966 the values are the blessed objects. This is what is returned by $c->components.
970 Adds a component to the appropriate sub-container. The sub-container is guessed
971 by the component name given.
973 =head2 find_component
975 Searches for components in all containers. If $component is the full class
976 name, the sub-container is guessed, and it gets the searched component in there.
977 Otherwise, it looks for a component with that name in all sub-containers. If
978 $component is a regexp it calls _find_component_regexp and matches all
979 components against that regexp.
981 =head2 expand_component_module
983 Components found by C<locate_components> will be passed to this method, which
984 is expected to return a list of component (package) names to be set up.
986 =head2 setup_components
988 Uses locate_components service to list the components, and adds them to the
989 appropriate sub-containers, using add_component().
993 Catalyst Contributors, see Catalyst.pm
997 This library is free software. You can redistribute it and/or modify it under
998 the same terms as Perl itself.