File Coverage

blib/lib/Catalyst/Component.pm
Criterion Covered Total %
statement 72 73 98.6
branch 21 24 87.5
condition 8 14 57.1
subroutine 17 18 94.4
pod 6 6 100.0
total 124 135 91.8


line stmt bran cond sub pod time code
1              
2             use Moose;
3 169     169   201490 use Class::MOP;
  169         780713  
  169         886  
4 169     169   894012 use Class::MOP::Object;
  169         465  
  169         3824  
5 169     169   895 use Catalyst::Utils;
  169         362  
  169         4798  
6 169     169   16087 use Class::C3::Adopt::NEXT;
  169         416  
  169         4419  
7 169     169   71762 use Devel::InnerPackage ();
  169         586416  
  169         1136  
8 169     169   5171 use MRO::Compat;
  169         391  
  169         2283  
9 169     169   841 use mro 'c3';
  169         380  
  169         3076  
10 169     169   815 use Scalar::Util 'blessed';
  169         363  
  169         1403  
11 169     169   4368 use Class::Load 'is_class_loaded';
  169         365  
  169         6640  
12 169     169   1003 use Moose::Util 'find_meta';
  169         422  
  169         6739  
13 169     169   914 use namespace::clean -except => 'meta';
  169         394  
  169         1066  
14 169     169   37221  
  169         472  
  169         1078  
15             with 'MooseX::Emulate::Class::Accessor::Fast';
16             with 'Catalyst::ClassData';
17              
18              
19             =head1 NAME
20              
21             Catalyst::Component - Catalyst Component Base Class
22              
23             =head1 SYNOPSIS
24              
25             # lib/MyApp/Model/Something.pm
26             package MyApp::Model::Something;
27              
28             use base 'Catalyst::Component';
29              
30             __PACKAGE__->config( foo => 'bar' );
31              
32             has foo => (
33             is => 'ro',
34             );
35              
36             sub test {
37             my $self = shift;
38             return $self->foo;
39             }
40              
41             sub forward_to_me {
42             my ( $self, $c ) = @_;
43             $c->response->output( $self->foo );
44             }
45              
46             1;
47              
48             # Methods can be a request step
49             $c->forward(qw/MyApp::Model::Something forward_to_me/);
50              
51             # Or just methods
52             print $c->comp('MyApp::Model::Something')->test;
53              
54             print $c->comp('MyApp::Model::Something')->foo;
55              
56             =head1 DESCRIPTION
57              
58             This is the universal base class for Catalyst components
59             (Model/View/Controller).
60              
61             It provides you with a generic new() for component construction through Catalyst's
62             component loader with config() support and a process() method placeholder.
63              
64             B<Note> that calling C<< $self->config >> inside a component is strongly
65             not recommended - the correctly merged config should have already been
66             passed to the constructor and stored in attributes - accessing
67             the config accessor directly from an instance is likely to get the
68             wrong values (as it only holds the class wide config, not things loaded
69             from the config file!)
70              
71             =cut
72              
73             __PACKAGE__->mk_classdata('_plugins');
74             __PACKAGE__->mk_classdata('_config');
75              
76             has catalyst_component_name => ( is => 'ro' ); # Cannot be required => 1 as context
77             # class @ISA component - HATE
78             # Make accessor callable as a class method, as we need to call setup_actions
79             # on the application class, which we don't have an instance of, ewwwww
80             # Also, naughty modules like Catalyst::View::JSON try to write to _everything_,
81             # so spit a warning, ignore that (and try to do the right thing anyway) here..
82             around catalyst_component_name => sub {
83             my ($orig, $self) = (shift, shift);
84             Carp::cluck("Tried to write to the catalyst_component_name accessor - is your component broken or just mad? (Write ignored - using default value.)") if scalar @_;
85             blessed($self) ? $self->$orig() || blessed($self) : $self;
86             };
87              
88             my $class = shift;
89             my $args = {};
90 7264     7264 1 36622  
91 7264         10912 if (@_ == 1) {
92             $args = $_[0] if ref($_[0]) eq 'HASH';
93 7264 100       17474 } elsif (@_ == 2) { # is it ($app, $args) or foo => 'bar' ?
    100          
    50          
94 307 50       1081 if (blessed($_[0])) {
95             $args = $_[1] if ref($_[1]) eq 'HASH';
96 6952 100 66     55233 } elsif (is_class_loaded($_[0]) &&
    100 66        
97 1 50       10 $_[0]->isa('Catalyst') && ref($_[1]) eq 'HASH') {
98             $args = $_[1];
99             } else {
100 6950         13512 $args = +{ @_ };
101             }
102 1         4 } elsif (@_ % 2 == 0) {
103             $args = +{ @_ };
104             }
105 5         12  
106             return $class->merge_config_hashes( $class->config, $args );
107             }
108 7264         29687  
109             my ( $class, $c ) = @_;
110              
111             # Temporary fix, some components does not pass context to constructor
112 6872     6872 1 12916 my $arguments = ( ref( $_[-1] ) eq 'HASH' ) ? $_[-1] : {};
113             if ( my $next = $class->next::can ) {
114             my ($next_package) = Class::MOP::get_code_info($next);
115 6872 100       14646 warn "There is a COMPONENT method resolving after Catalyst::Component in ${next_package}.\n";
116 6872 100       20447 warn "This behavior can no longer be supported, and so your application is probably broken.\n";
117 1         36 warn "Your linearized isa hierarchy is: " . join(', ', @{ mro::get_linear_isa($class) }) . "\n";
118 1         12 warn "Please see perldoc Catalyst::Upgrading for more information about this issue.\n";
119 1         10 }
120 1         5 return $class->new($c, $arguments);
  1         20  
121 1         8 }
122              
123 6872         307188 my $self = shift;
124             # Uncomment once sane to do so
125             #Carp::cluck("config method called on instance") if ref $self;
126             my $config = $self->_config || {};
127 35385     35385 1 14852409 if (@_) {
128             my $newconfig = { %{@_ > 1 ? {@_} : $_[0]} };
129             $self->_config(
130 35385   100     101488 $self->merge_config_hashes( $config, $newconfig )
131 35385 100       66692 );
132 943 100       2317 } else {
  943         6133  
133 943         7124 # this is a bit of a kludge, required to make
134             # __PACKAGE__->config->{foo} = 'bar';
135             # work in a subclass.
136             # TODO maybe this should be a ClassData option?
137             my $class = blessed($self) || $self;
138             my $meta = find_meta($class);
139             unless (${ $meta->get_or_add_package_symbol('$_config') }) {
140             # Call merge_hashes to ensure we deep copy the parent
141 34442   66     102271 # config onto the subclass
142 34442         66476 $self->_config( Catalyst::Utils::merge_hashes($config, {}) );
143 34442 100       212187 }
  34442         91335  
144             }
145             return $self->_config;
146 6362         86304 }
147              
148             my ( $self, $lefthash, $righthash ) = @_;
149 35385         342385  
150             return Catalyst::Utils::merge_hashes( $lefthash, $righthash );
151             }
152              
153 16026     16026 1 38320  
154             Catalyst::Exception->throw( message => ( ref $_[0] || $_[0] )
155 16026         40747 . " did not override Catalyst::Component::process" );
156             }
157              
158             my ($class, $component) = @_;
159             return Devel::InnerPackage::list_packages( $component );
160 0   0 0 1 0 }
161              
162             __PACKAGE__->meta->make_immutable;
163              
164             1;
165 6872     6872 1 13933  
166 6872         19780  
167             =head1 METHODS
168              
169             =head2 new($app, $arguments)
170              
171             Called by COMPONENT to instantiate the component; should return an object
172             to be stored in the application's component hash.
173              
174             =head2 COMPONENT
175              
176             C<< my $component_instance = $component->COMPONENT($app, $arguments); >>
177              
178             If this method is present (as it is on all Catalyst::Component subclasses),
179             it is called by Catalyst during setup_components with the application class
180             as $app and any config entry on the application for this component (for example,
181             in the case of MyApp::Controller::Foo this would be
182             C<< MyApp->config('Controller::Foo' => \%conf >>).
183              
184             The arguments are expected to be a hashref and are merged with the
185             C<< __PACKAGE__->config >> hashref before calling C<< ->new >>
186             to instantiate the component.
187              
188             You can override it in your components to do custom construction, using
189             something like this:
190              
191             sub COMPONENT {
192             my ($class, $app, $args) = @_;
193             $args = $class->merge_config_hashes($class->config, $args);
194             return $class->new($app, $args);
195             }
196              
197             B<NOTE:> Generally when L<Catalyst> starts, it initializes all the components
198             and passes the hashref present in any configuration information to the
199             COMPONENT method. For example
200              
201             MyApp->config(
202             'Model::Foo' => {
203             bar => 'baz',
204             });
205              
206             You would expect COMPONENT to be called like this ->COMPONENT( 'MyApp', +{ bar=>'baz'});
207              
208             This would happen ONCE during setup.
209              
210             =head2 $c->config
211              
212             =head2 $c->config($hashref)
213              
214             =head2 $c->config($key, $value, ...)
215              
216             Accessor for this component's config hash. Config values can be set as
217             key value pair, or you can specify a hashref. In either case the keys
218             will be merged with any existing config settings. Each component in
219             a Catalyst application has its own config hash.
220              
221             The component's config hash is merged with any config entry on the
222             application for this component and passed to C<new()> (as mentioned
223             above at L</COMPONENT>). The recommended practice to access the merged
224             config is to use a Moose attribute for each config entry on the
225             receiving component.
226              
227             =head2 $c->process()
228              
229             This is the default method called on a Catalyst component in the dispatcher.
230             For instance, Views implement this action to render the response body
231             when you forward to them. The default is an abstract method.
232              
233             =head2 $c->merge_config_hashes( $hashref, $hashref )
234              
235             Merges two hashes together recursively, giving right-hand precedence.
236             Alias for the method in L<Catalyst::Utils>.
237              
238             =head2 $c->expand_modules( $setup_component_config )
239              
240             Return a list of extra components that this component has created. By default,
241             it just looks for a list of inner packages of this component
242              
243             =cut
244              
245             =head1 OPTIONAL METHODS
246              
247             =head2 ACCEPT_CONTEXT($c, @args)
248              
249             Catalyst components are normally initialized during server startup, either
250             as a Class or a Instance. However, some components require information about
251             the current request. To do so, they can implement an ACCEPT_CONTEXT method.
252              
253             If this method is present, it is called during $c->comp/controller/model/view
254             with the current $c and any additional args (e.g. $c->model('Foo', qw/bar baz/)
255             would cause your MyApp::Model::Foo instance's ACCEPT_CONTEXT to be called with
256             ($c, 'bar', 'baz')) and the return value of this method is returned to the
257             calling code in the application rather than the component itself.
258              
259             B<NOTE:> All classes that are L<Catalyst::Component>s will have a COMPONENT
260             method, but classes that are intended to be factories or generators will
261             have ACCEPT_CONTEXT. If you have initialization arguments (such as from
262             configuration) that you wish to expose to the ACCEPT_CONTEXT you should
263             proxy them in the factory instance. For example:
264              
265             MyApp::Model::FooFactory;
266              
267             use Moose;
268             extends 'Catalyst::Model';
269              
270             has type => (is=>'ro', required=>1);
271              
272             sub ACCEPT_CONTEXT {
273             my ($self, $c, @args) = @_;
274             return bless { args=>\@args }, $self->type;
275             }
276              
277             MyApp::Model::Foo->meta->make_immutable;
278             MyApp::Model::Foo->config( type => 'Type1' );
279              
280             And in a controller:
281              
282             my $type = $c->model('FooFactory', 1,2,3,4): # $type->isa('Type1')
283              
284             B<NOTE:> If you define a ACCEPT_CONTEXT method it MUST check to see if the
285             second argument is blessed (is a context) or not (is an application class name) and
286             it MUST return something valid for the case when the scope is application. This is
287             required because a component maybe be called from the application scope even if it
288             requires a context and you must prevent errors from being issued if this happens.
289             Remember not all components that ACCEPT_CONTEXT actually need or use context information
290             (and there is a school of thought that suggestions doing so is a design error anyway...)
291              
292             =head1 SEE ALSO
293              
294             L<Catalyst>, L<Catalyst::Model>, L<Catalyst::View>, L<Catalyst::Controller>.
295              
296             =head1 AUTHORS
297              
298             Catalyst Contributors, see Catalyst.pm
299              
300             =head1 COPYRIGHT
301              
302             This library is free software. You can redistribute it and/or modify it under
303             the same terms as Perl itself.
304              
305             =cut