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"}
79 my $config = $self->resolve( service => 'config' );
81 # don't force default_component to be undef if the config wasn't set
82 my @default_view = $config->{default_view}
83 ? ( default_component => $config->{default_view} )
86 my @default_model = $config->{default_model}
87 ? ( default_component => $config->{default_model} )
91 $self->add_sub_container(
92 $self->build_component_subcontainer
95 $self->add_sub_container(
96 $self->build_controller_subcontainer
99 $self->add_sub_container(
100 $self->build_view_subcontainer( @default_view )
103 $self->add_sub_container(
104 $self->build_model_subcontainer( @default_model )
110 my $class = ref $self;
111 ${ $class . '::customise_container' }->($self)
112 if ${ $class . '::customise_container' };
116 sub build_model_subcontainer {
119 return $self->new_sub_container( @_,
124 sub build_view_subcontainer {
127 return $self->new_sub_container( @_,
132 sub build_controller_subcontainer {
135 return $self->new_sub_container(
136 name => 'controller',
140 sub build_component_subcontainer {
143 return Bread::Board::Container->new(
148 sub build_home_service {
151 return Bread::Board::BlockInjection->new(
152 lifecycle => 'Singleton',
156 my $class = $self->param('catalyst_application');
159 if ( my $env = Catalyst::Utils::env_value( $class, 'HOME' ) ) {
163 $home ||= Catalyst::Utils::home($class);
166 dependencies => [ depends_on('catalyst_application') ],
170 sub build_root_dir_service {
173 return Bread::Board::BlockInjection->new(
174 lifecycle => 'Singleton',
179 return Path::Class::Dir->new( $self->param('home') )->subdir('root');
181 dependencies => [ depends_on('home') ],
185 sub build_catalyst_application_service {
188 return Bread::Board::Literal->new( name => 'catalyst_application', value => $self->name );
191 sub build_driver_service {
194 return Bread::Board::Literal->new( name => 'driver', value => $self->driver );
197 sub build_file_service {
200 return Bread::Board::Literal->new( name => 'file', value => $self->file );
203 sub build_substitutions_service {
206 return Bread::Board::Literal->new( name => 'substitutions', value => $self->substitutions );
209 sub build_extensions_service {
212 return Bread::Board::BlockInjection->new(
213 lifecycle => 'Singleton',
214 name => 'extensions',
216 return \@{Config::Any->extensions};
221 sub build_prefix_service {
224 return Bread::Board::BlockInjection->new(
225 lifecycle => 'Singleton',
228 return Catalyst::Utils::appprefix( shift->param('catalyst_application') );
230 dependencies => [ depends_on('catalyst_application') ],
234 sub build_path_service {
237 return Bread::Board::BlockInjection->new(
238 lifecycle => 'Singleton',
243 return Catalyst::Utils::env_value( $s->param('catalyst_application'), 'CONFIG' )
245 || $s->param('catalyst_application')->path_to( $s->param('prefix') );
247 dependencies => [ depends_on('file'), depends_on('catalyst_application'), depends_on('prefix') ],
251 sub build_config_service {
254 return Bread::Board::BlockInjection->new(
255 lifecycle => 'Singleton',
260 my $v = Data::Visitor::Callback->new(
262 return unless defined $_;
263 return $self->_config_substitutions( $s->param('catalyst_application'), $s->param('substitutions'), $_ );
267 $v->visit( $s->param('raw_config') );
269 dependencies => [ depends_on('catalyst_application'), depends_on('raw_config'), depends_on('substitutions') ],
273 sub build_raw_config_service {
276 return Bread::Board::BlockInjection->new(
277 lifecycle => 'Singleton',
278 name => 'raw_config',
282 my @global = @{$s->param('global_config')};
283 my @locals = @{$s->param('local_config')};
285 my $config = $s->param('class_config');
287 for my $cfg (@global, @locals) {
289 $config = Catalyst::Utils::merge_hashes( $config, $cfg->{$_} );
295 dependencies => [ depends_on('global_config'), depends_on('local_config'), depends_on('class_config') ],
299 sub build_global_files_service {
302 return Bread::Board::BlockInjection->new(
303 lifecycle => 'Singleton',
304 name => 'global_files',
308 my ( $path, $extension ) = @{$s->param('config_path')};
310 my @extensions = @{$s->param('extensions')};
314 die "Unable to handle files with the extension '${extension}'" unless grep { $_ eq $extension } @extensions;
317 @files = map { "$path.$_" } @extensions;
321 dependencies => [ depends_on('extensions'), depends_on('config_path') ],
325 sub build_local_files_service {
328 return Bread::Board::BlockInjection->new(
329 lifecycle => 'Singleton',
330 name => 'local_files',
334 my ( $path, $extension ) = @{$s->param('config_path')};
335 my $suffix = $s->param('config_local_suffix');
337 my @extensions = @{$s->param('extensions')};
341 die "Unable to handle files with the extension '${extension}'" unless grep { $_ eq $extension } @extensions;
342 $path =~ s{\.$extension}{_$suffix.$extension};
345 @files = map { "${path}_${suffix}.$_" } @extensions;
349 dependencies => [ depends_on('extensions'), depends_on('config_path'), depends_on('config_local_suffix') ],
353 sub build_class_config_service {
356 return Bread::Board::BlockInjection->new(
357 lifecycle => 'Singleton',
358 name => 'class_config',
361 my $app = $s->param('catalyst_application');
363 # Container might be called outside Catalyst context
364 return {} unless Class::MOP::is_class_loaded($app);
366 # config might not have been defined
367 return $app->config || {};
369 dependencies => [ depends_on('catalyst_application') ],
373 sub build_global_config_service {
376 return Bread::Board::BlockInjection->new(
377 lifecycle => 'Singleton',
378 name => 'global_config',
382 return Config::Any->load_files({
383 files => $s->param('global_files'),
384 filter => \&_fix_syntax,
386 driver_args => $s->param('driver'),
389 dependencies => [ depends_on('global_files') ],
393 sub build_local_config_service {
396 return Bread::Board::BlockInjection->new(
397 lifecycle => 'Singleton',
398 name => 'local_config',
402 return Config::Any->load_files({
403 files => $s->param('local_files'),
404 filter => \&_fix_syntax,
406 driver_args => $s->param('driver'),
409 dependencies => [ depends_on('local_files') ],
413 sub build_config_path_service {
416 return Bread::Board::BlockInjection->new(
417 lifecycle => 'Singleton',
418 name => 'config_path',
422 my $path = $s->param('path');
423 my $prefix = $s->param('prefix');
425 my ( $extension ) = ( $path =~ m{\.(.{1,4})$} );
428 $path =~ s{[\/\\]$}{};
432 return [ $path, $extension ];
434 dependencies => [ depends_on('prefix'), depends_on('path') ],
438 sub build_config_local_suffix_service {
441 return Bread::Board::BlockInjection->new(
442 lifecycle => 'Singleton',
443 name => 'config_local_suffix',
446 my $suffix = Catalyst::Utils::env_value( $s->param('catalyst_application'), 'CONFIG_LOCAL_SUFFIX' ) || $self->config_local_suffix;
450 dependencies => [ depends_on('catalyst_application') ],
454 sub build_locate_components_service {
457 return Bread::Board::BlockInjection->new(
458 lifecycle => 'Singleton',
459 name => 'locate_components',
462 my $class = $s->param('catalyst_application');
463 my $config = $s->param('config')->{ setup_components };
465 Catalyst::Exception->throw(
466 qq{You are using search_extra config option. That option is\n} .
467 qq{deprecated, please refer to the documentation for\n} .
468 qq{other ways of achieving the same results.\n}
469 ) if delete $config->{ search_extra };
471 my @paths = qw( ::Controller ::C ::Model ::M ::View ::V );
473 my $locator = Module::Pluggable::Object->new(
474 search_path => [ map { s/^(?=::)/$class/; $_; } @paths ],
478 return [ $locator->plugins ];
480 dependencies => [ depends_on('catalyst_application'), depends_on('config') ],
484 sub setup_components {
486 my $class = $self->resolve( service => 'catalyst_application' );
487 my @comps = @{ $self->resolve( service => 'locate_components' ) };
488 my %comps = map { $_ => 1 } @comps;
489 my $deprecatedcatalyst_component_names = 0;
491 my $app_locate_components_addr = refaddr(
492 $class->can('locate_components')
494 my $cat_locate_components_addr = refaddr(
495 Catalyst->can('locate_components')
498 if ($app_locate_components_addr != $cat_locate_components_addr) {
499 # FIXME - why not just say: @comps = $class->locate_components() ?
500 $class->log->warn(qq{You have overridden locate_components. That } .
501 qq{no longer works. Please refer to the documentation to achieve } .
502 qq{similar results.\n}
506 for my $component ( @comps ) {
508 # We pass ignore_loaded here so that overlay files for (e.g.)
509 # Model::DBI::Schema sub-classes are loaded - if it's in @comps
510 # we know M::P::O found a file on disk so this is safe
512 Catalyst::Utils::ensure_class_loaded( $component, { ignore_loaded => 1 } );
515 for my $component (@comps) {
516 $self->add_component( $component );
517 # FIXME - $instance->expand_modules() is broken
518 my @expanded_components = $self->expand_component_module( $component );
521 !$deprecatedcatalyst_component_names &&
522 ($deprecatedcatalyst_component_names = $component =~ m/::[CMV]::/) ||
523 ($deprecatedcatalyst_component_names = grep { /::[CMV]::/ } @expanded_components)
525 # FIXME - should I be calling warn here?
526 # Maybe it's time to remove it, or become fatal
527 $class->log->warn(qq{Your application is using the deprecated ::[MVC]:: type naming scheme.\n}.
528 qq{Please switch your class names to ::Model::, ::View:: and ::Controller: as appropriate.\n}
532 for my $component (@expanded_components) {
533 $self->add_component( $component )
534 unless $comps{$component};
543 prefix => $_ eq 'Component' ? '' : $_ . '::',
544 values => delete $config->{ lc $_ } || delete $config->{ $_ }
546 grep { ref $config->{ lc $_ } || ref $config->{ $_ } }
547 qw( Component Model M View V Controller C Plugin )
550 foreach my $comp ( @components ) {
551 my $prefix = $comp->{ prefix };
552 foreach my $element ( keys %{ $comp->{ values } } ) {
553 $config->{ "$prefix$element" } = $comp->{ values }->{ $element };
558 sub _config_substitutions {
559 my ( $self, $name, $subs, $arg ) = @_;
561 $subs->{ HOME } ||= sub { shift->path_to( '' ); };
565 if (! defined($ENV{$v})) {
566 Catalyst::Exception->throw( message =>
567 "Missing environment variable: $v" );
573 $subs->{ path_to } ||= sub { shift->path_to( @_ ); };
574 $subs->{ literal } ||= sub { return $_[ 1 ]; };
575 my $subsre = join( '|', keys %$subs );
577 $arg =~ s{__($subsre)(?:\((.+?)\))?__}{ $subs->{ $1 }->( $name, $2 ? split( /,/, $2 ) : () ) }eg;
581 sub get_component_from_sub_container {
582 my ( $self, $sub_container_name, $name, $c, @args ) = @_;
584 my $sub_container = $self->get_sub_container( $sub_container_name );
587 my $default = $sub_container->default_component;
589 return $sub_container->get_component( $default, $c, @args )
590 if $default && $sub_container->has_service( $default );
592 # FIXME - should I be calling $c->log->warn here?
593 # this is never a controller, so this is safe
594 $c->log->warn( "Calling \$c->$sub_container_name() is not supported unless you specify one of:" );
595 $c->log->warn( "* \$c->config(default_$sub_container_name => 'the name of the default $sub_container_name to use')" );
596 $c->log->warn( "* \$c->stash->{current_$sub_container_name} # the name of the view to use for this request" );
597 $c->log->warn( "* \$c->stash->{current_${sub_container_name}_instance} # the instance of the $sub_container_name to use for this request" );
602 return $sub_container->get_component_regexp( $name, $c, @args )
605 return $sub_container->get_component( $name, $c, @args )
606 if $sub_container->has_service( $name );
609 "Attempted to use $sub_container_name '$name', " .
610 "but it does not exist"
617 my ( $self, $component, @args ) = @_;
618 my ( $type, $name ) = _get_component_type_name($component);
621 return $self->get_component_from_sub_container(
625 my $query = ref $component
630 for my $subcontainer_name (qw/model view controller/) {
631 my $subcontainer = $self->get_sub_container( $subcontainer_name );
632 my @components = $subcontainer->get_service_list;
633 @result = grep { m{$component} } @components;
635 return map { $subcontainer->get_component( $_, @args ) } @result
639 # one last search for things like $c->comp(qr/::M::/)
640 @result = $self->_find_component_regexp(
642 ) if !@result and ref $component;
644 # it expects an empty list on failed searches
648 sub _find_component_regexp {
649 my ( $self, $component, $ctx, @args ) = @_;
652 my @components = grep { m{$component} } keys %{ $self->get_all_components($ctx) };
655 my ($type, $name) = _get_component_type_name($_);
657 push @result, $self->get_component_from_sub_container(
658 $type, $name, $ctx, @args
665 sub get_all_components {
666 my ($self, $class) = @_;
669 # FIXME - if we're getting from these containers, we need to either:
670 # - pass 'ctx' and 'accept_context_args' OR
671 # - make these params optional
672 # big problem when setting up the dispatcher - this method is called
673 # as $container->get_all_components('MyApp'). What to do with Request
675 foreach my $type (qw/model view controller /) {
676 my $container = $self->get_sub_container($type);
678 for my $component ($container->get_service_list) {
679 my $comp_service = $container->get_service($component);
681 $components{$comp_service->catalyst_component_name} = $comp_service->get(ctx => $class);
685 return lock_hash %components;
689 my ( $self, $component ) = @_;
690 my ( $type, $name ) = _get_component_type_name($component);
694 # The 'component' sub-container will create the object, and store it's
695 # instance, which, by default, will live throughout the application.
696 # The model/view/controller sub-containers only reference the instance
697 # held in the aforementioned sub-container, and execute the ACCEPT_CONTEXT
698 # sub every time they are called, when it exists.
699 my $instance_container = $self->get_sub_container('component');
700 my $accept_context_container = $self->get_sub_container($type);
702 # Custom containers might have added the service already
703 # We don't want to override that
704 return if $accept_context_container->has_service( $name );
706 my $component_service_name = "${type}_${name}";
708 $instance_container->add_service(
709 Catalyst::IOC::ConstructorInjection->new(
710 name => $component_service_name,
711 catalyst_component_name => $component,
713 lifecycle => 'Singleton',
715 depends_on( '/catalyst_application' ),
719 # XXX - FIXME - We have to explicitly build the service here,
720 # causing the COMPONENT method to be called early here, as otherwise
721 # if the component method defines other classes (e.g. the
722 # ACCEPT_CONTEXT injection Model::DBIC::Schema does)
723 # then they won't be found by Devel::InnerPackage
724 # see also t/aggregate/unit_core_component_loading.t
725 $instance_container->get_service($component_service_name)->get;
727 $accept_context_container->add_service(
728 Catalyst::IOC::BlockInjection->new(
730 catalyst_component_name => $component,
732 depends_on( "/component/$component_service_name" ),
734 block => sub { shift->param($component_service_name) },
739 # FIXME: should this sub exist?
740 # should it be moved to Catalyst::Utils,
741 # or replaced by something already existing there?
742 sub _get_component_type_name {
743 my ( $component ) = @_;
746 while ( !$result and (my $index = index $component, '::') > 0 ) {
747 my $type = lc substr $component, 0, $index;
748 $component = substr $component, $index + 2;
749 $result = first { $type eq $_ or $type eq substr($_, 0, 1) }
750 qw{ model view controller };
753 return ($result, $component);
756 sub expand_component_module {
757 my ( $class, $module ) = @_;
758 return Devel::InnerPackage::list_packages( $module );
761 __PACKAGE__->meta->make_immutable;
771 Catalyst::Container - IOC for Catalyst components
779 =head1 Methods for Building Containers
781 =head2 build_component_subcontainer
783 Container that stores all components, i.e. all models, views and controllers
784 together. Each service is an instance of the actual component, and by default
785 it lives while the application is running. Retrieving components from this
786 sub-container will instantiate the component, if it hasn't been instantiated
787 already, but will not execute ACCEPT_CONTEXT.
789 =head2 build_model_subcontainer
791 Container that stores references for all models that are inside the components
792 sub-container. Retrieving a model triggers ACCEPT_CONTEXT, if it exists.
794 =head2 build_view_subcontainer
796 Same as L<build_model_subcontainer>, but for views.
798 =head2 build_controller_subcontainer
800 Same as L<build_model_subcontainer>, but for controllers.
802 =head1 Methods for Building Services
804 =head2 build_catalyst_application_service
806 Name of the application (such as MyApp).
808 =head2 build_home_service
810 The application home directory. All the files (including classes, scripts, etc)
811 created for this application are in this directory, or in a sub-directory below
814 =head2 build_root_dir_service
816 Inside the application home (as explained in L</build_home_service>), there is
817 a root directory. This is where all templates and static files are.
819 =head2 build_driver_service
821 Config options passed directly to the driver being used.
823 =head2 build_file_service
827 =head2 build_substitutions_service
829 This method substitutes macros found with calls to a function. There are a
830 number of default macros:
834 =item * C<__HOME__> - replaced with C<$c-E<gt>path_to('')>
836 =item * C<__ENV(foo)__> - replaced with the value of C<$ENV{foo}>
838 =item * C<__path_to(foo/bar)__> - replaced with C<$c-E<gt>path_to('foo/bar')>
840 =item * C<__literal(__FOO__)__> - leaves __FOO__ alone (allows you to use
841 C<__DATA__> as a config value, for example)
845 The parameter list is split on comma (C<,>). You can override this method to
846 do your own string munging, or you can define your own macros in
847 C<< <MyApp->config( 'Plugin::ConfigLoader' => { substitutions => { ... } } ) >>.
850 MyApp->config( 'Plugin::ConfigLoader' => {
852 baz => sub { my $c = shift; qux( @_ ); },
856 The above will respond to C<__baz(x,y)__> in config strings.
858 =head2 build_extensions_service
860 Config::Any's available config file extensions (e.g. xml, json, pl, etc).
862 =head2 build_prefix_service
864 The prefix, based on the application name, that will be used to look-up the
865 config files (which will be in the format $prefix.$extension). If the app is
866 MyApp::Foo, the prefix will be myapp_foo.
868 =head2 build_path_service
870 The path to the config file (or environment variable, if defined).
872 =head2 build_config_service
874 The resulting configuration for the application, after it has successfully
875 been loaded, and all substitutions have been made.
877 =head2 build_raw_config_service
879 The merge of local_config and global_config hashes, before substitutions.
881 =head2 build_global_files_service
883 Gets all files for config that don't have the local_suffix, such as myapp.conf.
885 =head2 build_local_files_service
887 Gets all files for config that have the local_suffix, such as myapp_local.conf.
889 =head2 build_global_config_service
891 Reads config from global_files.
893 =head2 build_local_config_service
895 Reads config from local_files.
897 =head2 build_class_config_service
899 Reads config set from the application's class attribute config,
900 i.e. MyApp->config( name => 'MyApp', ... )
902 =head2 build_config_path_service
904 Splits the path to the config file, and returns on array ref containing
905 the path to the config file minus the extension in the first position,
906 and the extension in the second.
908 =head2 build_config_local_suffix_service
910 Determines the suffix of files used to override the main config. By default
911 this value is C<local>, which will load C<myapp_local.conf>. The suffix can
912 be specified in the following order of preference:
916 =item * C<$ENV{ MYAPP_CONFIG_LOCAL_SUFFIX }>
918 =item * C<$ENV{ CATALYST_CONFIG_LOCAL_SUFFIX }>
922 The first one of these values found replaces the default of C<local> in the
923 name of the local config file to be loaded.
925 For example, if C< $ENV{ MYAPP_CONFIG_LOCAL_SUFFIX }> is set to C<testing>,
926 ConfigLoader will try and load C<myapp_testing.conf> instead of
929 =head2 build_locate_components_service
931 This method is meant to provide a list of component modules that should be
932 setup for the application. By default, it will use L<Module::Pluggable>.
934 Specify a C<setup_components> config option to pass additional options directly
935 to L<Module::Pluggable>.
939 =head2 get_component_from_sub_container($sub_container, $name, $c, @args)
941 Looks for components in a given sub-container (such as controller, model or
942 view), and returns the searched component. If $name is undef, it returns the
943 default component (such as default_view, if $sub_container is 'view'). If
944 $name is a regexp, it returns an array of matching components. Otherwise, it
945 looks for the component with name $name.
947 =head2 get_all_components
949 Fetches all the components, in each of the sub_containers model, view and
950 controller, and returns a read-only hash. The keys are the class names, and
951 the values are the blessed objects. This is what is returned by $c->components.
955 Adds a component to the appropriate sub-container. The sub-container is guessed
956 by the component name given.
958 =head2 find_component
960 Searches for components in all containers. If $component is the full class
961 name, the sub-container is guessed, and it gets the searched component in there.
962 Otherwise, it looks for a component with that name in all sub-containers. If
963 $component is a regexp it calls _find_component_regexp and matches all
964 components against that regexp.
966 =head2 expand_component_module
968 Components found by C<locate_components> will be passed to this method, which
969 is expected to return a list of component (package) names to be set up.
971 =head2 setup_components
973 Uses locate_components service to list the components, and adds them to the
974 appropriate sub-containers, using add_component().
978 Catalyst Contributors, see Catalyst.pm
982 This library is free software. You can redistribute it and/or modify it under
983 the same terms as Perl itself.