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 170     170   234092 use Class::MOP;
  170         944907  
  170         1009  
4 170     170   1080487 use Class::MOP::Object;
  170         470  
  170         4651  
5 170     170   1040 use Catalyst::Utils;
  170         411  
  170         6758  
6 170     170   17616 use Class::C3::Adopt::NEXT;
  170         508  
  170         4898  
7 170     170   83652 use Devel::InnerPackage ();
  170         735219  
  170         1265  
8 170     170   5878 use MRO::Compat;
  170         438  
  170         2701  
9 170     170   852 use mro 'c3';
  170         420  
  170         3645  
10 170     170   929 use Scalar::Util 'blessed';
  170         417  
  170         1357  
11 170     170   5106 use Class::Load 'is_class_loaded';
  170         432  
  170         7811  
12 170     170   1109 use Moose::Util 'find_meta';
  170         475  
  170         7651  
13 170     170   1068 use namespace::clean -except => 'meta';
  170         484  
  170         1267  
14 170     170   45151  
  170         459  
  170         1243  
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 7270     7270 1 44134  
91 7270         13023 if (@_ == 1) {
92             $args = $_[0] if ref($_[0]) eq 'HASH';
93 7270 100       21097 } elsif (@_ == 2) { # is it ($app, $args) or foo => 'bar' ?
    100          
    50          
94 309 50       1533 if (blessed($_[0])) {
95             $args = $_[1] if ref($_[1]) eq 'HASH';
96 6954 100 66     66540 } elsif (is_class_loaded($_[0]) &&
    100 66        
97 1 50       6 $_[0]->isa('Catalyst') && ref($_[1]) eq 'HASH') {
98             $args = $_[1];
99             } else {
100 6952         16350 $args = +{ @_ };
101             }
102 1         4 } elsif (@_ % 2 == 0) {
103             $args = +{ @_ };
104             }
105 7         25  
106             return $class->merge_config_hashes( $class->config, $args );
107             }
108 7270         32757  
109             my ( $class, $c ) = @_;
110              
111             # Temporary fix, some components does not pass context to constructor
112 6874     6874 1 15747 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 6874 100       17595 warn "There is a COMPONENT method resolving after Catalyst::Component in ${next_package}.\n";
116 6874 100       22574 warn "This behavior can no longer be supported, and so your application is probably broken.\n";
117 1         55 warn "Your linearized isa hierarchy is: " . join(', ', @{ mro::get_linear_isa($class) }) . "\n";
118 1         14 warn "Please see perldoc Catalyst::Upgrading for more information about this issue.\n";
119 1         10 }
120 1         7 return $class->new($c, $arguments);
  1         16  
121 1         9 }
122              
123 6874         355706 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 35443     35443 1 17816236 if (@_) {
128             my $newconfig = { %{@_ > 1 ? {@_} : $_[0]} };
129             $self->_config(
130 35443   100     124087 $self->merge_config_hashes( $config, $newconfig )
131 35443 100       83768 );
132 943 100       2571 } else {
  943         7674  
133 943         8993 # 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 34500   66     126463 # config onto the subclass
142 34500         80939 $self->_config( Catalyst::Utils::merge_hashes($config, {}) );
143 34500 100       256163 }
  34500         111984  
144             }
145             return $self->_config;
146 6365         93906 }
147              
148             my ( $self, $lefthash, $righthash ) = @_;
149 35443         418196  
150             return Catalyst::Utils::merge_hashes( $lefthash, $righthash );
151             }
152              
153 16040     16040 1 46222  
154             Catalyst::Exception->throw( message => ( ref $_[0] || $_[0] )
155 16040         49076 . " 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 6874     6874 1 16762  
166 6874         20481  
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