Teach Cat to pass a _component_name into COMPONENT method, which is what ends up...
[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;
6a7254b5 8use MRO::Compat;
9use mro 'c3';
7a5ed4ef 10use Scalar::Util 'blessed';
7a5ed4ef 11use namespace::clean -except => 'meta';
5595dd2f 12
a7caa492 13with 'MooseX::Emulate::Class::Accessor::Fast';
14with 'Catalyst::ClassData';
15
16
158c88c0 17=head1 NAME
18
19Catalyst::Component - Catalyst Component Base Class
20
21=head1 SYNOPSIS
22
23 # lib/MyApp/Model/Something.pm
24 package MyApp::Model::Something;
25
e7f1cf73 26 use base 'Catalyst::Component';
158c88c0 27
28 __PACKAGE__->config( foo => 'bar' );
29
30 sub test {
31 my $self = shift;
32 return $self->{foo};
33 }
34
35 sub forward_to_me {
36 my ( $self, $c ) = @_;
37 $c->response->output( $self->{foo} );
38 }
43c58153 39
158c88c0 40 1;
41
42 # Methods can be a request step
43 $c->forward(qw/MyApp::Model::Something forward_to_me/);
44
45 # Or just methods
46 print $c->comp('MyApp::Model::Something')->test;
47
48 print $c->comp('MyApp::Model::Something')->{foo};
49
50=head1 DESCRIPTION
51
43c58153 52This is the universal base class for Catalyst components
158c88c0 53(Model/View/Controller).
54
55It provides you with a generic new() for instantiation through Catalyst's
56component loader with config() support and a process() method placeholder.
57
7cd1a42b 58=cut
158c88c0 59
46d0346d 60__PACKAGE__->mk_classdata('_plugins');
11b256bc 61__PACKAGE__->mk_classdata('_config');
e8b9f2a9 62
1b79e199 63has _component_name => ( is => 'ro' );
64
2ef59958 65sub BUILDARGS {
7a5ed4ef 66 my $class = shift;
67 my $args = {};
68
69 if (@_ == 1) {
70 $args = $_[0] if ref($_[0]) eq 'HASH';
71 } elsif (@_ == 2) { # is it ($app, $args) or foo => 'bar' ?
72 if (blessed($_[0])) {
73 $args = $_[1] if ref($_[1]) eq 'HASH';
74 } elsif (Class::MOP::is_class_loaded($_[0]) &&
75 $_[0]->isa('Catalyst') && ref($_[1]) eq 'HASH') {
76 $args = $_[1];
77 } elsif ($_[0] == $_[1]) {
78 $args = $_[1];
79 } else {
80 $args = +{ @_ };
81 }
82 } elsif (@_ % 2 == 0) {
83 $args = +{ @_ };
84 }
43c58153 85
7a5ed4ef 86 return $class->merge_config_hashes( $class->config, $args );
2ef59958 87}
4090e3bb 88
22247e54 89sub COMPONENT {
1b79e199 90 my ( $class, $c ) = @_;
22247e54 91
92 # Temporary fix, some components does not pass context to constructor
93 my $arguments = ( ref( $_[-1] ) eq 'HASH' ) ? $_[-1] : {};
1b79e199 94 if ( my $next = $class->next::can ) {
6a7254b5 95 my ($next_package) = Class::MOP::get_code_info($next);
7e2ec16e 96 warn "There is a COMPONENT method resolving after Catalyst::Component in ${next_package}.\n";
97 warn "This behavior can no longer be supported, and so your application is probably broken.\n";
1cc8db0c 98 warn "Your linearized isa hierarchy is: " . join(', ', @{ mro::get_linear_isa($class) }) . "\n";
7e2ec16e 99 warn "Please see perldoc Catalyst::Upgrading for more information about this issue.\n";
6a7254b5 100 }
1b79e199 101 return $class->new($c, $arguments);
22247e54 102}
103
158c88c0 104sub config {
11b256bc 105 my $self = shift;
106 my $config = $self->_config || {};
107 if (@_) {
108 my $newconfig = { %{@_ > 1 ? {@_} : $_[0]} };
109 $self->_config(
110 $self->merge_config_hashes( $config, $newconfig )
111 );
112 } else {
113 # this is a bit of a kludge, required to make
114 # __PACKAGE__->config->{foo} = 'bar';
edffeb5a 115 # work in a subclass.
7a5ed4ef 116 # TODO maybe this should be a ClassData option?
e106a59f 117 my $class = blessed($self) || $self;
118 my $meta = Class::MOP::get_metaclass_by_name($class);
74c89dea 119 unless ($meta->has_package_symbol('$_config')) {
c03aaf03 120 # Call merge_hashes to ensure we deep copy the parent
121 # config onto the subclass
122 $self->_config( Catalyst::Utils::merge_hashes($config, {}) );
46d0346d 123 }
158c88c0 124 }
7a5ed4ef 125 return $self->_config;
158c88c0 126}
127
7cd1a42b 128sub merge_config_hashes {
129 my ( $self, $lefthash, $righthash ) = @_;
158c88c0 130
7cd1a42b 131 return Catalyst::Utils::merge_hashes( $lefthash, $righthash );
132}
158c88c0 133
134sub process {
135
136 Catalyst::Exception->throw( message => ( ref $_[0] || $_[0] )
137 . " did not override Catalyst::Component::process" );
138}
139
46d0346d 140__PACKAGE__->meta->make_immutable;
7a5ed4ef 141
7cd1a42b 1421;
baf6a3db 143
7cd1a42b 144__END__
baf6a3db 145
7cd1a42b 146=head1 METHODS
baf6a3db 147
7cd1a42b 148=head2 new($c, $arguments)
baf6a3db 149
7cd1a42b 150Called by COMPONENT to instantiate the component; should return an object
151to be stored in the application's component hash.
152
7a5ed4ef 153=head2 COMPONENT
154
155C<< my $component_instance = $component->COMPONENT($app, $arguments); >>
7cd1a42b 156
157If this method is present (as it is on all Catalyst::Component subclasses,
158it is called by Catalyst during setup_components with the application class
159as $c and any config entry on the application for this component (for example,
160in the case of MyApp::Controller::Foo this would be
9779c885 161C<< MyApp->config('Controller::Foo' => \%conf >>).
162The arguments are expected to be a hashref and are merged with the
163C<< __PACKAGE__->config >> hashref before calling C<< ->new >>
164to instantiate the component.
7cd1a42b 165
7a5ed4ef 166You can override it in your components to do custom instantiation, using
167something like this:
168
169 sub COMPONENT {
170 my ($class, $app, $args) = @_;
171 $args = $self->merge_config_hashes($self->config, $args);
172 return $class->new($app, $args);
173 }
174
7cd1a42b 175=head2 $c->config
176
177=head2 $c->config($hashref)
178
179=head2 $c->config($key, $value, ...)
180
43c58153 181Accessor for this component's config hash. Config values can be set as
7cd1a42b 182key value pair, or you can specify a hashref. In either case the keys
43c58153 183will be merged with any existing config settings. Each component in
184a Catalyst application has its own config hash.
7cd1a42b 185
186=head2 $c->process()
187
188This is the default method called on a Catalyst component in the dispatcher.
43c58153 189For instance, Views implement this action to render the response body
7cd1a42b 190when you forward to them. The default is an abstract method.
191
192=head2 $c->merge_config_hashes( $hashref, $hashref )
193
194Merges two hashes together recursively, giving right-hand precedence.
195Alias for the method in L<Catalyst::Utils>.
baf6a3db 196
825dbf85 197=head1 OPTIONAL METHODS
198
199=head2 ACCEPT_CONTEXT($c, @args)
200
f9c35d6c 201Catalyst components are normally initialized during server startup, either
825dbf85 202as a Class or a Instance. However, some components require information about
203the current request. To do so, they can implement an ACCEPT_CONTEXT method.
204
205If this method is present, it is called during $c->comp/controller/model/view
206with the current $c and any additional args (e.g. $c->model('Foo', qw/bar baz/)
207would cause your MyApp::Model::Foo instance's ACCEPT_CONTEXT to be called with
208($c, 'bar', 'baz')) and the return value of this method is returned to the
209calling code in the application rather than the component itself.
210
158c88c0 211=head1 SEE ALSO
212
e7f1cf73 213L<Catalyst>, L<Catalyst::Model>, L<Catalyst::View>, L<Catalyst::Controller>.
158c88c0 214
2f381252 215=head1 AUTHORS
158c88c0 216
2f381252 217Catalyst Contributors, see Catalyst.pm
158c88c0 218
219=head1 COPYRIGHT
220
536bee89 221This library is free software. You can redistribute it and/or modify it under
158c88c0 222the same terms as Perl itself.
223
85d9fce6 224=cut