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