X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?p=catagits%2FCatalyst-Runtime.git;a=blobdiff_plain;f=lib%2FCatalyst.pm;h=545ec839e8c1243a1a3fab02e52a2a7f455c60f3;hp=27b421ebf3fb01622e787b5a963717f3a91db90a;hb=067a21ea868757ad0d3ea1d4e80f74d61e0698fc;hpb=ec4d72594fb7a701c2f36e85ecf9a680ca1abba2 diff --git a/lib/Catalyst.pm b/lib/Catalyst.pm index 27b421e..545ec83 100644 --- a/lib/Catalyst.pm +++ b/lib/Catalyst.pm @@ -710,13 +710,24 @@ sub _comp_names { } # Filter a component before returning by calling ACCEPT_CONTEXT if available + +#our %tracker = (); sub _filter_component { my ( $c, $comp, @args ) = @_; + # die "Circular Dependencies Detected." if $tracker{$comp}; + # $tracker{$comp}++; + if(ref $comp eq 'CODE') { + $comp = $comp->($c); + } + #$tracker{$comp}++; + if ( eval { $comp->can('ACCEPT_CONTEXT'); } ) { return $comp->ACCEPT_CONTEXT( $c, @args ); } + $c->log->warn("You called component '${\$comp->catalyst_component_name}' with arguments [@args], but this component does not ACCEPT_CONTEXT, so args are ignored.") if scalar(@args) && $c->debug; + return $comp; } @@ -763,7 +774,8 @@ Gets a L instance by name. $c->model('Foo')->do_stuff; -Any extra arguments are directly passed to ACCEPT_CONTEXT. +Any extra arguments are directly passed to ACCEPT_CONTEXT, if the model +defines ACCEPT_CONTEXT. If it does not, the args are discarded. If the name is omitted, it will look for - a model object in $c->stash->{current_model_instance}, then @@ -2831,24 +2843,92 @@ sub setup_components { } for my $component (@comps) { - my $instance = $class->components->{ $component } = $class->setup_component($component); - my @expanded_components = $instance->can('expand_modules') - ? $instance->expand_modules( $component, $config ) - : $class->expand_component_module( $component, $config ); - for my $component (@expanded_components) { - next if $comps{$component}; - $class->components->{ $component } = $class->setup_component($component); - } + my $instance = $class->components->{ $component } = $class->delayed_setup_component($component); } - # Inject a component or wrap a stand alone class in an adaptor - #my @configured_comps = grep { not($class->component($_)||'') } - # grep { /^(Model)::|(View)::|(Controller::)/ } - # keys %{$class->config ||+{}}; + # Inject a component or wrap a stand alone class in an adaptor. This makes a list + # of named components in the configuration that are not actually existing (not a + # real file). + + my @injected_components = keys %{$class->config->{inject_components} ||+{}}; + foreach my $injected_comp_name(@injected_components) { + my $component_class = $class->config->{inject_components}->{$injected_comp_name}->{from_component} || ''; + if($component_class) { + my @roles = @{$class->config->{inject_components}->{$injected_comp_name}->{roles} ||[]}; + my %args = %{ $class->config->{$injected_comp_name} || +{} }; + + Catalyst::Utils::inject_component( + into => $class, + component => $component_class, + (scalar(@roles) ? (traits => \@roles) : ()), + as => $injected_comp_name); + } + } + + # All components are registered, now we need to 'init' them. + foreach my $component_name (keys %{$class->components||{}}) { + $class->components->{$component_name} = $class->components->{$component_name}->() if + (ref($class->components->{$component_name}) || '') eq 'CODE'; + } +} + +=head2 $app->inject_components($MyApp_Component_name => \%args); + +Add a component that is injected at setup: + + MyApp->inject_component( 'Model::Foo' => { from_component => 'Common::Foo' } ); + +Must be called before ->setup. Expects a component name for your +current application and \%args where + +=over 4 + +=item from_component + +The target component being injected into your application + +=item roles + +An arrayref of Ls that are applied to your component. + +=back + +Example + + MyApp->inject_component( + 'Model::Foo' => { + from_component => 'Common::Model::Foo', + roles => ['Role1', 'Role2'], + }); - #foreach my $configured_comp(@configured_comps) { - #warn $configured_comp; - #} +=head2 $app->inject_components + +Inject a list of components: + + MyApp->inject_components( + 'Model::FooOne' => { + from_component => 'Common::Model::Foo', + roles => ['Role1', 'Role2'], + }, + 'Model::FooTwo' => { + from_component => 'Common::Model::Foo', + roles => ['Role1', 'Role2'], + }); + +=cut + +sub inject_component { + my ($app, $name, $args) = @_; + die "Component $name exists" if + $app->config->{inject_components}->{$name}; + $app->config->{inject_components}->{$name} = $args; +} + +sub inject_components { + my $app = shift; + while(@_) { + $app->inject_component(shift, shift); + } } =head2 $c->locate_components( $setup_component_config ) @@ -2892,6 +2972,21 @@ sub expand_component_module { return Devel::InnerPackage::list_packages( $module ); } +=head2 $app->delayed_setup_component + +Returns a coderef that points to a setup_component instance. Used +internally for when you want to delay setup until the first time +the component is called. + +=cut + +sub delayed_setup_component { + my($class, $component, @more) = @_; + return sub { + return my $instance = $class->setup_component($component, @more); + }; +} + =head2 $c->setup_component =cut @@ -2910,14 +3005,15 @@ sub setup_component { # for the debug screen, as $component is already the key name. local $config->{catalyst_component_name} = $component; - my $instance = eval { $component->COMPONENT( $class, $config ); }; - - if ( my $error = $@ ) { - chomp $error; - Catalyst::Exception->throw( - message => qq/Couldn't instantiate component "$component", "$error"/ - ); - } + my $instance = eval { + $component->COMPONENT( $class, $config ); + } || do { + my $error = $@; + chomp $error; + Catalyst::Exception->throw( + message => qq/Couldn't instantiate component "$component", "$error"/ + ); + }; unless (blessed $instance) { my $metaclass = Moose::Util::find_meta($component); @@ -2929,7 +3025,16 @@ sub setup_component { qq/Couldn't instantiate component "$component", COMPONENT() method (from $component_method_from) didn't return an object-like value (value was $value)./ ); } - return $instance; + + my @expanded_components = $instance->can('expand_modules') + ? $instance->expand_modules( $component, $config ) + : $class->expand_component_module( $component, $config ); + for my $component (@expanded_components) { + next if $class->components->{ $component }; + $class->components->{ $component } = $class->setup_component($component); + } + + return $instance; } =head2 $c->setup_dispatcher