Fixed run-on sentence in COPYRIGHT and s/program/library/
[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';
11use Storable 'dclone';
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
2ef59958 64sub BUILDARGS {
7a5ed4ef 65 my $class = shift;
66 my $args = {};
67
68 if (@_ == 1) {
69 $args = $_[0] if ref($_[0]) eq 'HASH';
70 } elsif (@_ == 2) { # is it ($app, $args) or foo => 'bar' ?
71 if (blessed($_[0])) {
72 $args = $_[1] if ref($_[1]) eq 'HASH';
73 } elsif (Class::MOP::is_class_loaded($_[0]) &&
74 $_[0]->isa('Catalyst') && ref($_[1]) eq 'HASH') {
75 $args = $_[1];
76 } elsif ($_[0] == $_[1]) {
77 $args = $_[1];
78 } else {
79 $args = +{ @_ };
80 }
81 } elsif (@_ % 2 == 0) {
82 $args = +{ @_ };
83 }
43c58153 84
7a5ed4ef 85 return $class->merge_config_hashes( $class->config, $args );
2ef59958 86}
4090e3bb 87
22247e54 88sub COMPONENT {
89 my ( $self, $c ) = @_;
90
91 # Temporary fix, some components does not pass context to constructor
92 my $arguments = ( ref( $_[-1] ) eq 'HASH' ) ? $_[-1] : {};
6a7254b5 93 if( my $next = $self->next::can ){
94 my $class = blessed $self || $self;
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 }
4090e3bb 101 return $self->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')) {
7a5ed4ef 120 $self->_config( dclone $config );
46d0346d 121 }
158c88c0 122 }
7a5ed4ef 123 return $self->_config;
158c88c0 124}
125
7cd1a42b 126sub merge_config_hashes {
127 my ( $self, $lefthash, $righthash ) = @_;
158c88c0 128
7cd1a42b 129 return Catalyst::Utils::merge_hashes( $lefthash, $righthash );
130}
158c88c0 131
132sub process {
133
134 Catalyst::Exception->throw( message => ( ref $_[0] || $_[0] )
135 . " did not override Catalyst::Component::process" );
136}
137
46d0346d 138__PACKAGE__->meta->make_immutable;
7a5ed4ef 139
7cd1a42b 1401;
baf6a3db 141
7cd1a42b 142__END__
baf6a3db 143
7cd1a42b 144=head1 METHODS
baf6a3db 145
7cd1a42b 146=head2 new($c, $arguments)
baf6a3db 147
7cd1a42b 148Called by COMPONENT to instantiate the component; should return an object
149to be stored in the application's component hash.
150
7a5ed4ef 151=head2 COMPONENT
152
153C<< my $component_instance = $component->COMPONENT($app, $arguments); >>
7cd1a42b 154
155If this method is present (as it is on all Catalyst::Component subclasses,
156it is called by Catalyst during setup_components with the application class
157as $c and any config entry on the application for this component (for example,
158in the case of MyApp::Controller::Foo this would be
43c58153 159MyApp->config->{'Controller::Foo'}). The arguments are expected to be a
160hashref and are merged with the __PACKAGE__->config hashref before calling
7cd1a42b 161->new to instantiate the component.
162
7a5ed4ef 163You can override it in your components to do custom instantiation, using
164something like this:
165
166 sub COMPONENT {
167 my ($class, $app, $args) = @_;
168 $args = $self->merge_config_hashes($self->config, $args);
169 return $class->new($app, $args);
170 }
171
7cd1a42b 172=head2 $c->config
173
174=head2 $c->config($hashref)
175
176=head2 $c->config($key, $value, ...)
177
43c58153 178Accessor for this component's config hash. Config values can be set as
7cd1a42b 179key value pair, or you can specify a hashref. In either case the keys
43c58153 180will be merged with any existing config settings. Each component in
181a Catalyst application has its own config hash.
7cd1a42b 182
183=head2 $c->process()
184
185This is the default method called on a Catalyst component in the dispatcher.
43c58153 186For instance, Views implement this action to render the response body
7cd1a42b 187when you forward to them. The default is an abstract method.
188
189=head2 $c->merge_config_hashes( $hashref, $hashref )
190
191Merges two hashes together recursively, giving right-hand precedence.
192Alias for the method in L<Catalyst::Utils>.
baf6a3db 193
825dbf85 194=head1 OPTIONAL METHODS
195
196=head2 ACCEPT_CONTEXT($c, @args)
197
f9c35d6c 198Catalyst components are normally initialized during server startup, either
825dbf85 199as a Class or a Instance. However, some components require information about
200the current request. To do so, they can implement an ACCEPT_CONTEXT method.
201
202If this method is present, it is called during $c->comp/controller/model/view
203with the current $c and any additional args (e.g. $c->model('Foo', qw/bar baz/)
204would cause your MyApp::Model::Foo instance's ACCEPT_CONTEXT to be called with
205($c, 'bar', 'baz')) and the return value of this method is returned to the
206calling code in the application rather than the component itself.
207
158c88c0 208=head1 SEE ALSO
209
e7f1cf73 210L<Catalyst>, L<Catalyst::Model>, L<Catalyst::View>, L<Catalyst::Controller>.
158c88c0 211
2f381252 212=head1 AUTHORS
158c88c0 213
2f381252 214Catalyst Contributors, see Catalyst.pm
158c88c0 215
216=head1 COPYRIGHT
217
536bee89 218This library is free software. You can redistribute it and/or modify it under
158c88c0 219the same terms as Perl itself.
220
85d9fce6 221=cut