make sure we still properly decode the part name
[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
7cd1a42b 205=head2 $c->config
206
207=head2 $c->config($hashref)
208
209=head2 $c->config($key, $value, ...)
210
43c58153 211Accessor for this component's config hash. Config values can be set as
7cd1a42b 212key value pair, or you can specify a hashref. In either case the keys
43c58153 213will be merged with any existing config settings. Each component in
214a Catalyst application has its own config hash.
7cd1a42b 215
f8a54681 216The component's config hash is merged with any config entry on the
217application for this component and passed to C<new()> (as mentioned
d8ccdd9d 218above at L</COMPONENT>). The recommended practice to access the merged
f8a54681 219config is to use a Moose attribute for each config entry on the
220receiving component.
221
7cd1a42b 222=head2 $c->process()
223
224This is the default method called on a Catalyst component in the dispatcher.
43c58153 225For instance, Views implement this action to render the response body
7cd1a42b 226when you forward to them. The default is an abstract method.
227
228=head2 $c->merge_config_hashes( $hashref, $hashref )
229
230Merges two hashes together recursively, giving right-hand precedence.
231Alias for the method in L<Catalyst::Utils>.
baf6a3db 232
5d02e790 233=head2 $c->expand_modules( $setup_component_config )
234
235Return a list of extra components that this component has created. By default,
236it just looks for a list of inner packages of this component
237
238=cut
239
825dbf85 240=head1 OPTIONAL METHODS
241
242=head2 ACCEPT_CONTEXT($c, @args)
243
f9c35d6c 244Catalyst components are normally initialized during server startup, either
825dbf85 245as a Class or a Instance. However, some components require information about
246the current request. To do so, they can implement an ACCEPT_CONTEXT method.
247
248If this method is present, it is called during $c->comp/controller/model/view
249with the current $c and any additional args (e.g. $c->model('Foo', qw/bar baz/)
250would cause your MyApp::Model::Foo instance's ACCEPT_CONTEXT to be called with
251($c, 'bar', 'baz')) and the return value of this method is returned to the
252calling code in the application rather than the component itself.
253
158c88c0 254=head1 SEE ALSO
255
e7f1cf73 256L<Catalyst>, L<Catalyst::Model>, L<Catalyst::View>, L<Catalyst::Controller>.
158c88c0 257
2f381252 258=head1 AUTHORS
158c88c0 259
2f381252 260Catalyst Contributors, see Catalyst.pm
158c88c0 261
262=head1 COPYRIGHT
263
536bee89 264This library is free software. You can redistribute it and/or modify it under
158c88c0 265the same terms as Perl itself.
266
85d9fce6 267=cut