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