Clean up branch. I'm happy with this (bar the eww, as commented) and intend to merge...
[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
88fbc3be 63has _component_name => ( is => 'ro' ); # Cannot be required => 1 as context
64 # class @ISA component - HATE
65# Make accessor callable as a class method, as we need to call setup_actions
66# on the application class, which we don't have an instance of, ewwwww
67around _component_name => sub {
68 my ($orig, $self) = (shift, shift);
69 blessed($self) ? $self->$orig(@_) : $self;
70};
1b79e199 71
2ef59958 72sub BUILDARGS {
7a5ed4ef 73 my $class = shift;
74 my $args = {};
75
76 if (@_ == 1) {
77 $args = $_[0] if ref($_[0]) eq 'HASH';
78 } elsif (@_ == 2) { # is it ($app, $args) or foo => 'bar' ?
79 if (blessed($_[0])) {
80 $args = $_[1] if ref($_[1]) eq 'HASH';
81 } elsif (Class::MOP::is_class_loaded($_[0]) &&
82 $_[0]->isa('Catalyst') && ref($_[1]) eq 'HASH') {
83 $args = $_[1];
84 } elsif ($_[0] == $_[1]) {
85 $args = $_[1];
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;
113 my $config = $self->_config || {};
114 if (@_) {
115 my $newconfig = { %{@_ > 1 ? {@_} : $_[0]} };
116 $self->_config(
117 $self->merge_config_hashes( $config, $newconfig )
118 );
119 } else {
120 # this is a bit of a kludge, required to make
121 # __PACKAGE__->config->{foo} = 'bar';
edffeb5a 122 # work in a subclass.
7a5ed4ef 123 # TODO maybe this should be a ClassData option?
e106a59f 124 my $class = blessed($self) || $self;
125 my $meta = Class::MOP::get_metaclass_by_name($class);
74c89dea 126 unless ($meta->has_package_symbol('$_config')) {
c03aaf03 127 # Call merge_hashes to ensure we deep copy the parent
128 # config onto the subclass
129 $self->_config( Catalyst::Utils::merge_hashes($config, {}) );
46d0346d 130 }
158c88c0 131 }
7a5ed4ef 132 return $self->_config;
158c88c0 133}
134
7cd1a42b 135sub merge_config_hashes {
136 my ( $self, $lefthash, $righthash ) = @_;
158c88c0 137
7cd1a42b 138 return Catalyst::Utils::merge_hashes( $lefthash, $righthash );
139}
158c88c0 140
141sub process {
142
143 Catalyst::Exception->throw( message => ( ref $_[0] || $_[0] )
144 . " did not override Catalyst::Component::process" );
145}
146
46d0346d 147__PACKAGE__->meta->make_immutable;
7a5ed4ef 148
7cd1a42b 1491;
baf6a3db 150
7cd1a42b 151__END__
baf6a3db 152
7cd1a42b 153=head1 METHODS
baf6a3db 154
7cd1a42b 155=head2 new($c, $arguments)
baf6a3db 156
7cd1a42b 157Called by COMPONENT to instantiate the component; should return an object
158to be stored in the application's component hash.
159
7a5ed4ef 160=head2 COMPONENT
161
162C<< my $component_instance = $component->COMPONENT($app, $arguments); >>
7cd1a42b 163
164If this method is present (as it is on all Catalyst::Component subclasses,
165it is called by Catalyst during setup_components with the application class
166as $c and any config entry on the application for this component (for example,
167in the case of MyApp::Controller::Foo this would be
9779c885 168C<< MyApp->config('Controller::Foo' => \%conf >>).
169The arguments are expected to be a hashref and are merged with the
170C<< __PACKAGE__->config >> hashref before calling C<< ->new >>
171to instantiate the component.
7cd1a42b 172
7a5ed4ef 173You can override it in your components to do custom instantiation, using
174something like this:
175
176 sub COMPONENT {
177 my ($class, $app, $args) = @_;
178 $args = $self->merge_config_hashes($self->config, $args);
179 return $class->new($app, $args);
180 }
181
7cd1a42b 182=head2 $c->config
183
184=head2 $c->config($hashref)
185
186=head2 $c->config($key, $value, ...)
187
43c58153 188Accessor for this component's config hash. Config values can be set as
7cd1a42b 189key value pair, or you can specify a hashref. In either case the keys
43c58153 190will be merged with any existing config settings. Each component in
191a Catalyst application has its own config hash.
7cd1a42b 192
193=head2 $c->process()
194
195This is the default method called on a Catalyst component in the dispatcher.
43c58153 196For instance, Views implement this action to render the response body
7cd1a42b 197when you forward to them. The default is an abstract method.
198
199=head2 $c->merge_config_hashes( $hashref, $hashref )
200
201Merges two hashes together recursively, giving right-hand precedence.
202Alias for the method in L<Catalyst::Utils>.
baf6a3db 203
825dbf85 204=head1 OPTIONAL METHODS
205
206=head2 ACCEPT_CONTEXT($c, @args)
207
f9c35d6c 208Catalyst components are normally initialized during server startup, either
825dbf85 209as a Class or a Instance. However, some components require information about
210the current request. To do so, they can implement an ACCEPT_CONTEXT method.
211
212If this method is present, it is called during $c->comp/controller/model/view
213with the current $c and any additional args (e.g. $c->model('Foo', qw/bar baz/)
214would cause your MyApp::Model::Foo instance's ACCEPT_CONTEXT to be called with
215($c, 'bar', 'baz')) and the return value of this method is returned to the
216calling code in the application rather than the component itself.
217
158c88c0 218=head1 SEE ALSO
219
e7f1cf73 220L<Catalyst>, L<Catalyst::Model>, L<Catalyst::View>, L<Catalyst::Controller>.
158c88c0 221
2f381252 222=head1 AUTHORS
158c88c0 223
2f381252 224Catalyst Contributors, see Catalyst.pm
158c88c0 225
226=head1 COPYRIGHT
227
536bee89 228This library is free software. You can redistribute it and/or modify it under
158c88c0 229the same terms as Perl itself.
230
85d9fce6 231=cut