Fix $self vs $class, davewood++
[catagits/Catalyst-Runtime.git] / lib / Catalyst / Component.pm
1 package Catalyst::Component;
2
3 use Moose;
4 use Class::MOP;
5 use Class::MOP::Object;
6 use Catalyst::Utils;
7 use Class::C3::Adopt::NEXT;
8 use Devel::InnerPackage ();
9 use MRO::Compat;
10 use mro 'c3';
11 use Scalar::Util 'blessed';
12 use namespace::clean -except => 'meta';
13
14 with 'MooseX::Emulate::Class::Accessor::Fast';
15 with 'Catalyst::ClassData';
16
17
18 =head1 NAME
19
20 Catalyst::Component - Catalyst Component Base Class
21
22 =head1 SYNOPSIS
23
24     # lib/MyApp/Model/Something.pm
25     package MyApp::Model::Something;
26
27     use base 'Catalyst::Component';
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     }
40
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
53 This is the universal base class for Catalyst components
54 (Model/View/Controller).
55
56 It provides you with a generic new() for instantiation through Catalyst's
57 component loader with config() support and a process() method placeholder.
58
59 =cut
60
61 __PACKAGE__->mk_classdata('_plugins');
62 __PACKAGE__->mk_classdata('_config');
63
64 has catalyst_component_name => ( is => 'ro' ); # Cannot be required => 1 as context
65                                        # class @ISA component - HATE
66 # Make accessor callable as a class method, as we need to call setup_actions
67 # on the application class, which we don't have an instance of, ewwwww
68 # Also, naughty modules like Catalyst::View::JSON try to write to _everything_,
69 # so spit a warning, ignore that (and try to do the right thing anyway) here..
70 around catalyst_component_name => sub {
71     my ($orig, $self) = (shift, shift);
72     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 @_;
73     blessed($self) ? $self->$orig() || blessed($self) : $self;
74 };
75
76 sub BUILDARGS {
77     my $class = shift;
78     my $args = {};
79
80     if (@_ == 1) {
81         $args = $_[0] if ref($_[0]) eq 'HASH';
82     } elsif (@_ == 2) { # is it ($app, $args) or foo => 'bar' ?
83         if (blessed($_[0])) {
84             $args = $_[1] if ref($_[1]) eq 'HASH';
85         } elsif (Class::MOP::is_class_loaded($_[0]) &&
86                 $_[0]->isa('Catalyst') && ref($_[1]) eq 'HASH') {
87             $args = $_[1];
88         } else {
89             $args = +{ @_ };
90         }
91     } elsif (@_ % 2 == 0) {
92         $args = +{ @_ };
93     }
94
95     return $class->merge_config_hashes( $class->config, $args );
96 }
97
98 sub COMPONENT {
99     my ( $class, $c ) = @_;
100
101     # Temporary fix, some components does not pass context to constructor
102     my $arguments = ( ref( $_[-1] ) eq 'HASH' ) ? $_[-1] : {};
103     if ( my $next = $class->next::can ) {
104       my ($next_package) = Class::MOP::get_code_info($next);
105       warn "There is a COMPONENT method resolving after Catalyst::Component in ${next_package}.\n";
106       warn "This behavior can no longer be supported, and so your application is probably broken.\n";
107       warn "Your linearized isa hierarchy is: " . join(', ', @{ mro::get_linear_isa($class) }) . "\n";
108       warn "Please see perldoc Catalyst::Upgrading for more information about this issue.\n";
109     }
110     return $class->new($c, $arguments);
111 }
112
113 sub config {
114     my $self = shift;
115     # Uncomment once sane to do so
116     #Carp::cluck("config method called on instance") if ref $self;
117     my $config = $self->_config || {};
118     if (@_) {
119         my $newconfig = { %{@_ > 1 ? {@_} : $_[0]} };
120         $self->_config(
121             $self->merge_config_hashes( $config, $newconfig )
122         );
123     } else {
124         # this is a bit of a kludge, required to make
125         # __PACKAGE__->config->{foo} = 'bar';
126         # work in a subclass.
127         # TODO maybe this should be a ClassData option?
128         my $class = blessed($self) || $self;
129         my $meta = Class::MOP::get_metaclass_by_name($class);
130         unless ($meta->has_package_symbol('$_config')) {
131             # Call merge_hashes to ensure we deep copy the parent
132             # config onto the subclass
133             $self->_config( Catalyst::Utils::merge_hashes($config, {}) );
134         }
135     }
136     return $self->_config;
137 }
138
139 sub merge_config_hashes {
140     my ( $self, $lefthash, $righthash ) = @_;
141
142     return Catalyst::Utils::merge_hashes( $lefthash, $righthash );
143 }
144
145 sub process {
146
147     Catalyst::Exception->throw( message => ( ref $_[0] || $_[0] )
148           . " did not override Catalyst::Component::process" );
149 }
150
151 sub expand_modules {
152     my ($class, $component) = @_;
153     return Devel::InnerPackage::list_packages( $component );
154 }
155
156 __PACKAGE__->meta->make_immutable;
157
158 1;
159
160 __END__
161
162 =head1 METHODS
163
164 =head2 new($app, $arguments)
165
166 Called by COMPONENT to instantiate the component; should return an object
167 to be stored in the application's component hash.
168
169 =head2 COMPONENT
170
171 C<< my $component_instance = $component->COMPONENT($app, $arguments); >>
172
173 If this method is present (as it is on all Catalyst::Component subclasses,
174 it is called by Catalyst during setup_components with the application class
175 as $app and any config entry on the application for this component (for example,
176 in the case of MyApp::Controller::Foo this would be
177 C<< MyApp->config('Controller::Foo' => \%conf >>).
178
179 The arguments are expected to be a hashref and are merged with the
180 C<< __PACKAGE__->config >> hashref before calling C<< ->new >>
181 to instantiate the component.
182
183 You can override it in your components to do custom instantiation, using
184 something like this:
185
186   sub COMPONENT {
187       my ($class, $app, $args) = @_;
188       $args = $class->merge_config_hashes($class->config, $args);
189       return $class->new($app, $args);
190   }
191
192 =head2 $c->config
193
194 =head2 $c->config($hashref)
195
196 =head2 $c->config($key, $value, ...)
197
198 Accessor for this component's config hash. Config values can be set as
199 key value pair, or you can specify a hashref. In either case the keys
200 will be merged with any existing config settings. Each component in
201 a Catalyst application has its own config hash.
202
203 =head2 $c->process()
204
205 This is the default method called on a Catalyst component in the dispatcher.
206 For instance, Views implement this action to render the response body
207 when you forward to them. The default is an abstract method.
208
209 =head2 $c->merge_config_hashes( $hashref, $hashref )
210
211 Merges two hashes together recursively, giving right-hand precedence.
212 Alias for the method in L<Catalyst::Utils>.
213
214 =head2 $c->expand_modules( $setup_component_config )
215
216 Return a list of extra components that this component has created. By default,
217 it just looks for a list of inner packages of this component
218
219 =cut
220
221 =head1 OPTIONAL METHODS
222
223 =head2 ACCEPT_CONTEXT($c, @args)
224
225 Catalyst components are normally initialized during server startup, either
226 as a Class or a Instance. However, some components require information about
227 the current request. To do so, they can implement an ACCEPT_CONTEXT method.
228
229 If this method is present, it is called during $c->comp/controller/model/view
230 with the current $c and any additional args (e.g. $c->model('Foo', qw/bar baz/)
231 would cause your MyApp::Model::Foo instance's ACCEPT_CONTEXT to be called with
232 ($c, 'bar', 'baz')) and the return value of this method is returned to the
233 calling code in the application rather than the component itself.
234
235 =head1 SEE ALSO
236
237 L<Catalyst>, L<Catalyst::Model>, L<Catalyst::View>, L<Catalyst::Controller>.
238
239 =head1 AUTHORS
240
241 Catalyst Contributors, see Catalyst.pm
242
243 =head1 COPYRIGHT
244
245 This library is free software. You can redistribute it and/or modify it under
246 the same terms as Perl itself.
247
248 =cut