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';
23 default => sub { [] },
26 has config_local_suffix => (
35 default => sub { +{} },
44 has substitutions => (
47 default => sub { +{} },
50 has sub_container_class => (
54 default => 'Catalyst::IOC::SubContainer',
56 new_sub_container => 'new',
61 my ( $self, $params ) = @_;
64 $self->${\"build_${_}_service"}
88 my $config = $self->resolve( service => 'config' );
90 # don't force default_component to be undef if the config wasn't set
91 my @default_view = $config->{default_view}
92 ? ( default_component => $config->{default_view} )
95 my @default_model = $config->{default_model}
96 ? ( default_component => $config->{default_model} )
100 $self->add_sub_container(
101 $self->build_component_subcontainer
104 $self->add_sub_container(
105 $self->build_controller_subcontainer
108 $self->add_sub_container(
109 $self->build_view_subcontainer( @default_view )
112 $self->add_sub_container(
113 $self->build_model_subcontainer( @default_model )
119 my $class = ref $self;
120 ${ $class . '::customise_container' }->($self)
121 if ${ $class . '::customise_container' };
125 sub build_model_subcontainer {
128 return $self->new_sub_container( @_,
133 sub build_view_subcontainer {
136 return $self->new_sub_container( @_,
141 sub build_controller_subcontainer {
144 return $self->new_sub_container(
145 name => 'controller',
149 sub build_component_subcontainer {
152 return Bread::Board::Container->new(
157 sub build_flags_service {
160 return Bread::Board::Literal->new(
161 lifecycle => 'Singleton',
163 value => $self->get_flags,
171 foreach (@{ $self->flags }) {
174 ( $flags->{log} ) ? 'debug,' . $flags->{log} : 'debug';
176 elsif (/^-(\w+)=?(.*)$/) {
177 $flags->{ lc $1 } = $2;
180 push @{ $flags->{plugins} }, $_;
187 sub build_home_service {
190 return Bread::Board::BlockInjection->new(
191 lifecycle => 'Singleton',
195 my $class = $self->param('catalyst_application');
197 if ( my $env = Catalyst::Utils::env_value( $class, 'HOME' ) ) {
201 if ( my $home = $self->param('flags')->{home} ) {
205 return Catalyst::Utils::home($class);
207 dependencies => [ depends_on('flags'), depends_on('catalyst_application') ],
211 sub build_root_dir_service {
214 return Bread::Board::BlockInjection->new(
215 lifecycle => 'Singleton',
220 return Path::Class::Dir->new( $self->param('home') )->subdir('root');
222 dependencies => [ depends_on('home') ],
226 sub build_catalyst_application_service {
229 return Bread::Board::Literal->new( name => 'catalyst_application', value => $self->name );
232 sub build_driver_service {
235 return Bread::Board::Literal->new( name => 'driver', value => $self->driver );
238 sub build_file_service {
241 return Bread::Board::Literal->new( name => 'file', value => $self->file );
244 sub build_substitutions_service {
247 return Bread::Board::Literal->new( name => 'substitutions', value => $self->substitutions );
250 sub build_extensions_service {
253 return Bread::Board::BlockInjection->new(
254 lifecycle => 'Singleton',
255 name => 'extensions',
257 return \@{Config::Any->extensions};
262 sub build_prefix_service {
265 return Bread::Board::BlockInjection->new(
266 lifecycle => 'Singleton',
269 return Catalyst::Utils::appprefix( shift->param('catalyst_application') );
271 dependencies => [ depends_on('catalyst_application') ],
275 sub build_path_service {
278 return Bread::Board::BlockInjection->new(
279 lifecycle => 'Singleton',
284 return Catalyst::Utils::env_value( $s->param('catalyst_application'), 'CONFIG' )
286 || $s->param('catalyst_application')->path_to( $s->param('prefix') );
288 dependencies => [ depends_on('file'), depends_on('catalyst_application'), depends_on('prefix') ],
292 sub build_config_service {
295 return Bread::Board::BlockInjection->new(
296 lifecycle => 'Singleton',
301 my $v = Data::Visitor::Callback->new(
303 return unless defined $_;
304 return $self->_config_substitutions( $s->param('catalyst_application'), $s->param('substitutions'), $_ );
308 $v->visit( $s->param('raw_config') );
310 dependencies => [ depends_on('catalyst_application'), depends_on('raw_config'), depends_on('substitutions') ],
314 sub build_raw_config_service {
317 return Bread::Board::BlockInjection->new(
318 lifecycle => 'Singleton',
319 name => 'raw_config',
323 my @global = @{$s->param('global_config')};
324 my @locals = @{$s->param('local_config')};
326 my $config = $s->param('class_config');
328 for my $cfg (@global, @locals) {
330 $config = Catalyst::Utils::merge_hashes( $config, $cfg->{$_} );
336 dependencies => [ depends_on('global_config'), depends_on('local_config'), depends_on('class_config') ],
340 sub build_global_files_service {
343 return Bread::Board::BlockInjection->new(
344 lifecycle => 'Singleton',
345 name => 'global_files',
349 my ( $path, $extension ) = @{$s->param('config_path')};
351 my @extensions = @{$s->param('extensions')};
355 die "Unable to handle files with the extension '${extension}'" unless grep { $_ eq $extension } @extensions;
358 @files = map { "$path.$_" } @extensions;
362 dependencies => [ depends_on('extensions'), depends_on('config_path') ],
366 sub build_local_files_service {
369 return Bread::Board::BlockInjection->new(
370 lifecycle => 'Singleton',
371 name => 'local_files',
375 my ( $path, $extension ) = @{$s->param('config_path')};
376 my $suffix = $s->param('config_local_suffix');
378 my @extensions = @{$s->param('extensions')};
382 die "Unable to handle files with the extension '${extension}'" unless grep { $_ eq $extension } @extensions;
383 $path =~ s{\.$extension}{_$suffix.$extension};
386 @files = map { "${path}_${suffix}.$_" } @extensions;
390 dependencies => [ depends_on('extensions'), depends_on('config_path'), depends_on('config_local_suffix') ],
394 sub build_class_config_service {
397 return Bread::Board::BlockInjection->new(
398 lifecycle => 'Singleton',
399 name => 'class_config',
402 my $app = $s->param('catalyst_application');
404 # Container might be called outside Catalyst context
405 return {} unless Class::MOP::is_class_loaded($app);
407 # config might not have been defined
408 return $app->config || {};
410 dependencies => [ depends_on('catalyst_application') ],
414 sub build_global_config_service {
417 return Bread::Board::BlockInjection->new(
418 lifecycle => 'Singleton',
419 name => 'global_config',
423 return Config::Any->load_files({
424 files => $s->param('global_files'),
425 filter => \&_fix_syntax,
427 driver_args => $s->param('driver'),
430 dependencies => [ depends_on('global_files') ],
434 sub build_local_config_service {
437 return Bread::Board::BlockInjection->new(
438 lifecycle => 'Singleton',
439 name => 'local_config',
443 return Config::Any->load_files({
444 files => $s->param('local_files'),
445 filter => \&_fix_syntax,
447 driver_args => $s->param('driver'),
450 dependencies => [ depends_on('local_files') ],
454 sub build_config_path_service {
457 return Bread::Board::BlockInjection->new(
458 lifecycle => 'Singleton',
459 name => 'config_path',
463 my $path = $s->param('path');
464 my $prefix = $s->param('prefix');
466 my ( $extension ) = ( $path =~ m{\.(.{1,4})$} );
469 $path =~ s{[\/\\]$}{};
473 return [ $path, $extension ];
475 dependencies => [ depends_on('prefix'), depends_on('path') ],
479 sub build_config_local_suffix_service {
482 return Bread::Board::BlockInjection->new(
483 lifecycle => 'Singleton',
484 name => 'config_local_suffix',
487 my $suffix = Catalyst::Utils::env_value( $s->param('catalyst_application'), 'CONFIG_LOCAL_SUFFIX' ) || $self->config_local_suffix;
491 dependencies => [ depends_on('catalyst_application') ],
495 sub build_locate_components_service {
498 return Bread::Board::BlockInjection->new(
499 lifecycle => 'Singleton',
500 name => 'locate_components',
503 my $class = $s->param('catalyst_application');
504 my $config = $s->param('config')->{ setup_components };
506 Catalyst::Exception->throw(
507 qq{You are using search_extra config option. That option is\n} .
508 qq{deprecated, please refer to the documentation for\n} .
509 qq{other ways of achieving the same results.\n}
510 ) if delete $config->{ search_extra };
512 my @paths = qw( ::Controller ::C ::Model ::M ::View ::V );
514 my $locator = Module::Pluggable::Object->new(
515 search_path => [ map { s/^(?=::)/$class/; $_; } @paths ],
519 return [ $locator->plugins ];
521 dependencies => [ depends_on('catalyst_application'), depends_on('config') ],
525 sub setup_components {
527 my $class = $self->resolve( service => 'catalyst_application' );
528 my @comps = @{ $self->resolve( service => 'locate_components' ) };
529 my %comps = map { $_ => 1 } @comps;
530 my $deprecatedcatalyst_component_names = 0;
532 my $app_locate_components_addr = refaddr(
533 $class->can('locate_components')
535 my $cat_locate_components_addr = refaddr(
536 Catalyst->can('locate_components')
539 if ($app_locate_components_addr != $cat_locate_components_addr) {
540 # FIXME - why not just say: @comps = $class->locate_components() ?
541 $class->log->warn(qq{You have overridden locate_components. That } .
542 qq{no longer works. Please refer to the documentation to achieve } .
543 qq{similar results.\n}
547 for my $component ( @comps ) {
549 # We pass ignore_loaded here so that overlay files for (e.g.)
550 # Model::DBI::Schema sub-classes are loaded - if it's in @comps
551 # we know M::P::O found a file on disk so this is safe
553 Catalyst::Utils::ensure_class_loaded( $component, { ignore_loaded => 1 } );
556 for my $component (@comps) {
557 $self->add_component( $component );
558 # FIXME - $instance->expand_modules() is broken
559 my @expanded_components = $self->expand_component_module( $component );
562 !$deprecatedcatalyst_component_names &&
563 ($deprecatedcatalyst_component_names = $component =~ m/::[CMV]::/) ||
564 ($deprecatedcatalyst_component_names = grep { /::[CMV]::/ } @expanded_components)
566 # FIXME - should I be calling warn here?
567 # Maybe it's time to remove it, or become fatal
568 $class->log->warn(qq{Your application is using the deprecated ::[MVC]:: type naming scheme.\n}.
569 qq{Please switch your class names to ::Model::, ::View:: and ::Controller: as appropriate.\n}
573 for my $component (@expanded_components) {
574 $self->add_component( $component )
575 unless $comps{$component};
584 prefix => $_ eq 'Component' ? '' : $_ . '::',
585 values => delete $config->{ lc $_ } || delete $config->{ $_ }
587 grep { ref $config->{ lc $_ } || ref $config->{ $_ } }
588 qw( Component Model M View V Controller C Plugin )
591 foreach my $comp ( @components ) {
592 my $prefix = $comp->{ prefix };
593 foreach my $element ( keys %{ $comp->{ values } } ) {
594 $config->{ "$prefix$element" } = $comp->{ values }->{ $element };
599 sub _config_substitutions {
600 my ( $self, $name, $subs, $arg ) = @_;
602 $subs->{ HOME } ||= sub { shift->path_to( '' ); };
606 if (! defined($ENV{$v})) {
607 Catalyst::Exception->throw( message =>
608 "Missing environment variable: $v" );
614 $subs->{ path_to } ||= sub { shift->path_to( @_ ); };
615 $subs->{ literal } ||= sub { return $_[ 1 ]; };
616 my $subsre = join( '|', keys %$subs );
618 $arg =~ s{__($subsre)(?:\((.+?)\))?__}{ $subs->{ $1 }->( $name, $2 ? split( /,/, $2 ) : () ) }eg;
622 sub get_component_from_sub_container {
623 my ( $self, $sub_container_name, $name, $c, @args ) = @_;
625 my $sub_container = $self->get_sub_container( $sub_container_name );
628 my $default = $sub_container->default_component;
630 return $sub_container->get_component( $default, $c, @args )
631 if $default && $sub_container->has_service( $default );
633 # FIXME - should I be calling $c->log->warn here?
634 # this is never a controller, so this is safe
635 $c->log->warn( "Calling \$c->$sub_container_name() is not supported unless you specify one of:" );
636 $c->log->warn( "* \$c->config(default_$sub_container_name => 'the name of the default $sub_container_name to use')" );
637 $c->log->warn( "* \$c->stash->{current_$sub_container_name} # the name of the view to use for this request" );
638 $c->log->warn( "* \$c->stash->{current_${sub_container_name}_instance} # the instance of the $sub_container_name to use for this request" );
643 return $sub_container->get_component_regexp( $name, $c, @args )
646 return $sub_container->get_component( $name, $c, @args )
647 if $sub_container->has_service( $name );
650 "Attempted to use $sub_container_name '$name', " .
651 "but it does not exist"
658 my ( $self, $component, @args ) = @_;
659 my ( $type, $name ) = _get_component_type_name($component);
662 return $self->get_component_from_sub_container(
666 my $query = ref $component
671 for my $subcontainer_name (qw/model view controller/) {
672 my $subcontainer = $self->get_sub_container( $subcontainer_name );
673 my @components = $subcontainer->get_service_list;
674 @result = grep { m{$component} } @components;
676 return map { $subcontainer->get_component( $_, @args ) } @result
680 # one last search for things like $c->comp(qr/::M::/)
681 @result = $self->_find_component_regexp(
683 ) if !@result and ref $component;
685 # it expects an empty list on failed searches
689 sub _find_component_regexp {
690 my ( $self, $component, $ctx, @args ) = @_;
693 my @components = grep { m{$component} } keys %{ $self->get_all_components($ctx) };
696 my ($type, $name) = _get_component_type_name($_);
698 push @result, $self->get_component_from_sub_container(
699 $type, $name, $ctx, @args
706 sub get_all_components {
707 my ($self, $class) = @_;
710 # FIXME - if we're getting from these containers, we need to either:
711 # - pass 'ctx' and 'accept_context_args' OR
712 # - make these params optional
713 # big problem when setting up the dispatcher - this method is called
714 # as $container->get_all_components('MyApp'). What to do with Request
716 foreach my $type (qw/model view controller /) {
717 my $container = $self->get_sub_container($type);
719 for my $component ($container->get_service_list) {
720 my $comp_service = $container->get_service($component);
722 $components{$comp_service->catalyst_component_name} = $comp_service->get(ctx => $class);
726 return lock_hash %components;
730 my ( $self, $component ) = @_;
731 my ( $type, $name ) = _get_component_type_name($component);
735 # The 'component' sub-container will create the object, and store it's
736 # instance, which, by default, will live throughout the application.
737 # The model/view/controller sub-containers only reference the instance
738 # held in the aforementioned sub-container, and execute the ACCEPT_CONTEXT
739 # sub every time they are called, when it exists.
740 my $instance_container = $self->get_sub_container('component');
741 my $accept_context_container = $self->get_sub_container($type);
743 # Custom containers might have added the service already
744 # We don't want to override that
745 return if $accept_context_container->has_service( $name );
747 my $component_service_name = "${type}_${name}";
749 $instance_container->add_service(
750 Catalyst::IOC::ConstructorInjection->new(
751 name => $component_service_name,
752 catalyst_component_name => $component,
754 lifecycle => 'Singleton',
756 depends_on( '/catalyst_application' ),
760 # XXX - FIXME - We have to explicitly build the service here,
761 # causing the COMPONENT method to be called early here, as otherwise
762 # if the component method defines other classes (e.g. the
763 # ACCEPT_CONTEXT injection Model::DBIC::Schema does)
764 # then they won't be found by Devel::InnerPackage
765 # see also t/aggregate/unit_core_component_loading.t
766 $instance_container->get_service($component_service_name)->get;
768 $accept_context_container->add_service(
769 Catalyst::IOC::BlockInjection->new(
771 catalyst_component_name => $component,
773 depends_on( "/component/$component_service_name" ),
775 block => sub { shift->param($component_service_name) },
780 # FIXME: should this sub exist?
781 # should it be moved to Catalyst::Utils,
782 # or replaced by something already existing there?
783 sub _get_component_type_name {
784 my ( $component ) = @_;
787 while ( !$result and (my $index = index $component, '::') > 0 ) {
788 my $type = lc substr $component, 0, $index;
789 $component = substr $component, $index + 2;
790 $result = first { $type eq $_ or $type eq substr($_, 0, 1) }
791 qw{ model view controller };
794 return ($result, $component);
797 sub expand_component_module {
798 my ( $class, $module ) = @_;
799 return Devel::InnerPackage::list_packages( $module );
802 __PACKAGE__->meta->make_immutable;
812 Catalyst::Container - IOC for Catalyst components
820 =head1 Methods for Building Containers
822 =head2 build_component_subcontainer
824 Container that stores all components, i.e. all models, views and controllers
825 together. Each service is an instance of the actual component, and by default
826 it lives while the application is running. Retrieving components from this
827 sub-container will instantiate the component, if it hasn't been instantiated
828 already, but will not execute ACCEPT_CONTEXT.
830 =head2 build_model_subcontainer
832 Container that stores references for all models that are inside the components
833 sub-container. Retrieving a model triggers ACCEPT_CONTEXT, if it exists.
835 =head2 build_view_subcontainer
837 Same as L<build_model_subcontainer>, but for views.
839 =head2 build_controller_subcontainer
841 Same as L<build_model_subcontainer>, but for controllers.
843 =head1 Methods for Building Services
845 =head2 build_catalyst_application_service
847 Name of the application (such as MyApp).
849 =head2 build_home_service
851 The application home directory. All the files (including classes, scripts, etc)
852 created for this application are in this directory, or in a sub-directory below
855 =head2 build_root_dir_service
857 Inside the application home (as explained in L</build_home_service>), there is
858 a root directory. This is where all templates and static files are.
860 =head2 build_driver_service
862 Config options passed directly to the driver being used.
864 =head2 build_file_service
868 =head2 build_substitutions_service
870 This method substitutes macros found with calls to a function. There are a
871 number of default macros:
875 =item * C<__HOME__> - replaced with C<$c-E<gt>path_to('')>
877 =item * C<__ENV(foo)__> - replaced with the value of C<$ENV{foo}>
879 =item * C<__path_to(foo/bar)__> - replaced with C<$c-E<gt>path_to('foo/bar')>
881 =item * C<__literal(__FOO__)__> - leaves __FOO__ alone (allows you to use
882 C<__DATA__> as a config value, for example)
886 The parameter list is split on comma (C<,>). You can override this method to
887 do your own string munging, or you can define your own macros in
888 C<< <MyApp->config( 'Plugin::ConfigLoader' => { substitutions => { ... } } ) >>.
891 MyApp->config( 'Plugin::ConfigLoader' => {
893 baz => sub { my $c = shift; qux( @_ ); },
897 The above will respond to C<__baz(x,y)__> in config strings.
899 =head2 build_extensions_service
901 Config::Any's available config file extensions (e.g. xml, json, pl, etc).
903 =head2 build_prefix_service
905 The prefix, based on the application name, that will be used to look-up the
906 config files (which will be in the format $prefix.$extension). If the app is
907 MyApp::Foo, the prefix will be myapp_foo.
909 =head2 build_path_service
911 The path to the config file (or environment variable, if defined).
913 =head2 build_config_service
915 The resulting configuration for the application, after it has successfully
916 been loaded, and all substitutions have been made.
918 =head2 build_raw_config_service
920 The merge of local_config and global_config hashes, before substitutions.
922 =head2 build_global_files_service
924 Gets all files for config that don't have the local_suffix, such as myapp.conf.
926 =head2 build_local_files_service
928 Gets all files for config that have the local_suffix, such as myapp_local.conf.
930 =head2 build_global_config_service
932 Reads config from global_files.
934 =head2 build_local_config_service
936 Reads config from local_files.
938 =head2 build_class_config_service
940 Reads config set from the application's class attribute config,
941 i.e. MyApp->config( name => 'MyApp', ... )
943 =head2 build_config_path_service
945 Splits the path to the config file, and returns on array ref containing
946 the path to the config file minus the extension in the first position,
947 and the extension in the second.
949 =head2 build_config_local_suffix_service
951 Determines the suffix of files used to override the main config. By default
952 this value is C<local>, which will load C<myapp_local.conf>. The suffix can
953 be specified in the following order of preference:
957 =item * C<$ENV{ MYAPP_CONFIG_LOCAL_SUFFIX }>
959 =item * C<$ENV{ CATALYST_CONFIG_LOCAL_SUFFIX }>
963 The first one of these values found replaces the default of C<local> in the
964 name of the local config file to be loaded.
966 For example, if C< $ENV{ MYAPP_CONFIG_LOCAL_SUFFIX }> is set to C<testing>,
967 ConfigLoader will try and load C<myapp_testing.conf> instead of
970 =head2 build_locate_components_service
972 This method is meant to provide a list of component modules that should be
973 setup for the application. By default, it will use L<Module::Pluggable>.
975 Specify a C<setup_components> config option to pass additional options directly
976 to L<Module::Pluggable>.
980 =head2 get_component_from_sub_container($sub_container, $name, $c, @args)
982 Looks for components in a given sub-container (such as controller, model or
983 view), and returns the searched component. If $name is undef, it returns the
984 default component (such as default_view, if $sub_container is 'view'). If
985 $name is a regexp, it returns an array of matching components. Otherwise, it
986 looks for the component with name $name.
988 =head2 get_all_components
990 Fetches all the components, in each of the sub_containers model, view and
991 controller, and returns a read-only hash. The keys are the class names, and
992 the values are the blessed objects. This is what is returned by $c->components.
996 Adds a component to the appropriate sub-container. The sub-container is guessed
997 by the component name given.
999 =head2 find_component
1001 Searches for components in all containers. If $component is the full class
1002 name, the sub-container is guessed, and it gets the searched component in there.
1003 Otherwise, it looks for a component with that name in all sub-containers. If
1004 $component is a regexp it calls _find_component_regexp and matches all
1005 components against that regexp.
1007 =head2 expand_component_module
1009 Components found by C<locate_components> will be passed to this method, which
1010 is expected to return a list of component (package) names to be set up.
1012 =head2 setup_components
1014 Uses locate_components service to list the components, and adds them to the
1015 appropriate sub-containers, using add_component().
1019 Catalyst Contributors, see Catalyst.pm
1023 This library is free software. You can redistribute it and/or modify it under
1024 the same terms as Perl itself.