Add docs for changes
[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
fb0c5b21 175=head2 _component_class
176
177The class name of the component within an application. This is used to
178pass the component's class name to actions generated (becoming
179C<< $action->class >>). This is needed so that the L</COMPONENT> method can
180return an instance of a different class (e.g. a L<Class::MOP> anonymous class),
181and should be used to retrieve a component instead of C<< ref($self) >>.
182
7cd1a42b 183=head2 $c->config
184
185=head2 $c->config($hashref)
186
187=head2 $c->config($key, $value, ...)
188
43c58153 189Accessor for this component's config hash. Config values can be set as
7cd1a42b 190key value pair, or you can specify a hashref. In either case the keys
43c58153 191will be merged with any existing config settings. Each component in
192a Catalyst application has its own config hash.
7cd1a42b 193
194=head2 $c->process()
195
196This is the default method called on a Catalyst component in the dispatcher.
43c58153 197For instance, Views implement this action to render the response body
7cd1a42b 198when you forward to them. The default is an abstract method.
199
200=head2 $c->merge_config_hashes( $hashref, $hashref )
201
202Merges two hashes together recursively, giving right-hand precedence.
203Alias for the method in L<Catalyst::Utils>.
baf6a3db 204
825dbf85 205=head1 OPTIONAL METHODS
206
207=head2 ACCEPT_CONTEXT($c, @args)
208
f9c35d6c 209Catalyst components are normally initialized during server startup, either
825dbf85 210as a Class or a Instance. However, some components require information about
211the current request. To do so, they can implement an ACCEPT_CONTEXT method.
212
213If this method is present, it is called during $c->comp/controller/model/view
214with the current $c and any additional args (e.g. $c->model('Foo', qw/bar baz/)
215would cause your MyApp::Model::Foo instance's ACCEPT_CONTEXT to be called with
216($c, 'bar', 'baz')) and the return value of this method is returned to the
217calling code in the application rather than the component itself.
218
158c88c0 219=head1 SEE ALSO
220
e7f1cf73 221L<Catalyst>, L<Catalyst::Model>, L<Catalyst::View>, L<Catalyst::Controller>.
158c88c0 222
2f381252 223=head1 AUTHORS
158c88c0 224
2f381252 225Catalyst Contributors, see Catalyst.pm
158c88c0 226
227=head1 COPYRIGHT
228
536bee89 229This library is free software. You can redistribute it and/or modify it under
158c88c0 230the same terms as Perl itself.
231
85d9fce6 232=cut