X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?p=catagits%2FCatalyst-Runtime.git;a=blobdiff_plain;f=lib%2FCatalyst.pm;h=e0dac5d30e1fdfe493bf8e857217ff343b56205a;hp=0e501807e9c08208e1e9bb2b3d5f63b0e5b113e4;hb=1592e3ff1d864030c9ea35a05b139f1af82c5660;hpb=3e5607485bfedb02a06193f653a2f05202db7a4e diff --git a/lib/Catalyst.pm b/lib/Catalyst.pm index 0e50180..e0dac5d 100644 --- a/lib/Catalyst.pm +++ b/lib/Catalyst.pm @@ -81,8 +81,9 @@ sub _build_request_constructor_args { sub composed_request_class { my $class = shift; + my @traits = (@{$class->request_class_traits||[]}, @{$class->config->{request_class_traits}||[]}); return $class->_composed_request_class || - $class->_composed_request_class(Moose::Util::with_traits($class->request_class, @{$class->request_class_traits||[]})); + $class->_composed_request_class(Moose::Util::with_traits($class->request_class, @traits)); } has response => ( @@ -104,8 +105,9 @@ sub _build_response_constructor_args { sub composed_response_class { my $class = shift; + my @traits = (@{$class->response_class_traits||[]}, @{$class->config->{response_class_traits}||[]}); return $class->_composed_response_class || - $class->_composed_response_class(Moose::Util::with_traits($class->response_class, @{$class->response_class_traits||[]})); + $class->_composed_response_class(Moose::Util::with_traits($class->response_class, @traits)); } has namespace => (is => 'rw'); @@ -147,14 +149,15 @@ __PACKAGE__->stats_class('Catalyst::Stats'); sub composed_stats_class { my $class = shift; + my @traits = (@{$class->stats_class_traits||[]}, @{$class->config->{stats_class_traits}||[]}); return $class->_composed_stats_class || - $class->_composed_stats_class(Moose::Util::with_traits($class->stats_class, @{$class->stats_class_traits||[]})); + $class->_composed_stats_class(Moose::Util::with_traits($class->stats_class, @traits)); } __PACKAGE__->_encode_check(Encode::FB_CROAK | Encode::LEAVE_SRC); # Remember to update this in Catalyst::Runtime as well! -our $VERSION = '5.90089_001'; +our $VERSION = '5.90089_002'; $VERSION = eval $VERSION if $VERSION =~ /_/; # numify for warning-free dev releases sub import { @@ -710,12 +713,17 @@ 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->(); + $comp = $comp->($c); } + #$tracker{$comp}++; if ( eval { $comp->can('ACCEPT_CONTEXT'); } ) { return $comp->ACCEPT_CONTEXT( $c, @args ); @@ -2838,41 +2846,92 @@ sub setup_components { } for my $component (@comps) { - my $instance = $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. This makes a list # of named components in the configuration that are not actually existing (not a # real file). - my @configured_comps = grep { not($class->components->{$_}||'') } - grep { /^(Model)::|(View)::|(Controller::)/ } - keys %{$class->config ||+{}}; - - foreach my $configured_comp(@configured_comps) { - my $component_class = exists $class->config->{$configured_comp}->{from_component} ? - delete $class->config->{$configured_comp}->{from_component} : ''; + 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 = @{ exists $class->config->{$configured_comp}->{roles} ? - delete $class->config->{$configured_comp}->{roles} : [] }; + my @roles = @{$class->config->{inject_components}->{$injected_comp_name}->{roles} ||[]}; + my %args = %{ $class->config->{$injected_comp_name} || +{} }; - my %args = %{ exists $class->config->{$configured_comp}->{args} ? - delete $class->config->{$configured_comp}->{args} : +{} }; - - $class->config->{$configured_comp} = \%args; Catalyst::Utils::inject_component( into => $class, component => $component_class, (scalar(@roles) ? (traits => \@roles) : ()), - as => $configured_comp); + 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}->(); + $class->components->{$component_name} = $class->components->{$component_name}->() if + (ref($class->components->{$component_name}) || '') eq 'CODE'; } +} + +=head2 $app->inject_component($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'], + }); + +=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 ) @@ -2916,6 +2975,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 @@ -2923,7 +2997,6 @@ sub expand_component_module { sub setup_component { my( $class, $component ) = @_; -return sub { unless ( $component->can( 'COMPONENT' ) ) { return $component; } @@ -2956,19 +3029,17 @@ return sub { ); } -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); -} + 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 Sets up dispatcher. @@ -4060,6 +4131,55 @@ C - See L. C - See L. +=item * + +C + +An arrayref of Ls that get componsed into your stats class. + +=item * + +C + +An arrayref of Ls that get componsed into your request class. + +=item * + +C + +An arrayref of Ls that get componsed into your response class. + +=item * + +C + +A Hashref of L subclasses that are 'injected' into configuration. +For example: + + MyApp->config({ + inject_components => { + 'Controller::Err' => { from_component => 'Local::Controller::Errors' }, + 'Model::Zoo' => { from_component => 'Local::Model::Foo' }, + 'Model::Foo' => { from_component => 'Local::Model::Foo', roles => ['TestRole'] }, + }, + 'Controller::Err' => { a => 100, b=>200, namespace=>'error' }, + 'Model::Zoo' => { a => 2 }, + 'Model::Foo' => { a => 100 }, + }); + +Generally L looks for components in your Model/View or Controller directories. +However for cases when you which to use an existing component and you don't need any +customization (where for when you can apply a role to customize it) you may inject those +components into your application. Please note any configuration should be done 'in the +normal way', with a key under configuration named after the component affix, as in the +above example. + +Using this type of injection allows you to construct significant amounts of your application +with only configuration!. This may or may not lead to increased code understanding. + +Please not you may also call the ->inject_components application method as well, although +you must do so BEFORE setup. + =back =head1 EXCEPTIONS