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