Allow models and components to specify the names of any components they generate
[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;
5d02e790 8use Devel::InnerPackage ();
6a7254b5 9use MRO::Compat;
10use mro 'c3';
7a5ed4ef 11use Scalar::Util 'blessed';
7a5ed4ef 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
8f6cebb2 64has catalyst_component_name => ( is => 'ro' ); # Cannot be required => 1 as context
d2598ac8 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
e65d000f 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..
8f6cebb2 70around catalyst_component_name => sub {
d2598ac8 71 my ($orig, $self) = (shift, shift);
8f6cebb2 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 @_;
e65d000f 73 blessed($self) ? $self->$orig() || blessed($self) : $self;
d2598ac8 74};
1b79e199 75
2ef59958 76sub BUILDARGS {
7a5ed4ef 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];
fa7a60aa 88 } elsif ($_[0] eq $_[1]) {
7a5ed4ef 89 $args = $_[1];
90 } else {
91 $args = +{ @_ };
92 }
93 } elsif (@_ % 2 == 0) {
94 $args = +{ @_ };
95 }
43c58153 96
7a5ed4ef 97 return $class->merge_config_hashes( $class->config, $args );
2ef59958 98}
4090e3bb 99
22247e54 100sub COMPONENT {
1b79e199 101 my ( $class, $c ) = @_;
22247e54 102
103 # Temporary fix, some components does not pass context to constructor
104 my $arguments = ( ref( $_[-1] ) eq 'HASH' ) ? $_[-1] : {};
1b79e199 105 if ( my $next = $class->next::can ) {
6a7254b5 106 my ($next_package) = Class::MOP::get_code_info($next);
7e2ec16e 107 warn "There is a COMPONENT method resolving after Catalyst::Component in ${next_package}.\n";
108 warn "This behavior can no longer be supported, and so your application is probably broken.\n";
1cc8db0c 109 warn "Your linearized isa hierarchy is: " . join(', ', @{ mro::get_linear_isa($class) }) . "\n";
7e2ec16e 110 warn "Please see perldoc Catalyst::Upgrading for more information about this issue.\n";
6a7254b5 111 }
1b79e199 112 return $class->new($c, $arguments);
22247e54 113}
114
158c88c0 115sub config {
11b256bc 116 my $self = shift;
df960201 117 # Uncomment once sane to do so
118 #Carp::cluck("config method called on instance") if ref $self;
11b256bc 119 my $config = $self->_config || {};
120 if (@_) {
121 my $newconfig = { %{@_ > 1 ? {@_} : $_[0]} };
122 $self->_config(
123 $self->merge_config_hashes( $config, $newconfig )
124 );
125 } else {
126 # this is a bit of a kludge, required to make
127 # __PACKAGE__->config->{foo} = 'bar';
edffeb5a 128 # work in a subclass.
7a5ed4ef 129 # TODO maybe this should be a ClassData option?
e106a59f 130 my $class = blessed($self) || $self;
131 my $meta = Class::MOP::get_metaclass_by_name($class);
74c89dea 132 unless ($meta->has_package_symbol('$_config')) {
c03aaf03 133 # Call merge_hashes to ensure we deep copy the parent
134 # config onto the subclass
135 $self->_config( Catalyst::Utils::merge_hashes($config, {}) );
46d0346d 136 }
158c88c0 137 }
7a5ed4ef 138 return $self->_config;
158c88c0 139}
140
7cd1a42b 141sub merge_config_hashes {
142 my ( $self, $lefthash, $righthash ) = @_;
158c88c0 143
7cd1a42b 144 return Catalyst::Utils::merge_hashes( $lefthash, $righthash );
145}
158c88c0 146
147sub process {
148
149 Catalyst::Exception->throw( message => ( ref $_[0] || $_[0] )
150 . " did not override Catalyst::Component::process" );
151}
152
5d02e790 153sub expand_modules {
154 my ($class, $component) = @_;
155 return Devel::InnerPackage::list_packages( $component );
156}
157
46d0346d 158__PACKAGE__->meta->make_immutable;
7a5ed4ef 159
7cd1a42b 1601;
baf6a3db 161
7cd1a42b 162__END__
baf6a3db 163
7cd1a42b 164=head1 METHODS
baf6a3db 165
58064941 166=head2 new($app, $arguments)
baf6a3db 167
7cd1a42b 168Called by COMPONENT to instantiate the component; should return an object
169to be stored in the application's component hash.
170
7a5ed4ef 171=head2 COMPONENT
172
173C<< my $component_instance = $component->COMPONENT($app, $arguments); >>
7cd1a42b 174
175If this method is present (as it is on all Catalyst::Component subclasses,
176it is called by Catalyst during setup_components with the application class
58064941 177as $app and any config entry on the application for this component (for example,
7cd1a42b 178in the case of MyApp::Controller::Foo this would be
9779c885 179C<< MyApp->config('Controller::Foo' => \%conf >>).
58064941 180
9779c885 181The arguments are expected to be a hashref and are merged with the
182C<< __PACKAGE__->config >> hashref before calling C<< ->new >>
183to instantiate the component.
7cd1a42b 184
7a5ed4ef 185You can override it in your components to do custom instantiation, using
186something like this:
187
188 sub COMPONENT {
189 my ($class, $app, $args) = @_;
190 $args = $self->merge_config_hashes($self->config, $args);
191 return $class->new($app, $args);
192 }
193
7cd1a42b 194=head2 $c->config
195
196=head2 $c->config($hashref)
197
198=head2 $c->config($key, $value, ...)
199
43c58153 200Accessor for this component's config hash. Config values can be set as
7cd1a42b 201key value pair, or you can specify a hashref. In either case the keys
43c58153 202will be merged with any existing config settings. Each component in
203a Catalyst application has its own config hash.
7cd1a42b 204
205=head2 $c->process()
206
207This is the default method called on a Catalyst component in the dispatcher.
43c58153 208For instance, Views implement this action to render the response body
7cd1a42b 209when you forward to them. The default is an abstract method.
210
211=head2 $c->merge_config_hashes( $hashref, $hashref )
212
213Merges two hashes together recursively, giving right-hand precedence.
214Alias for the method in L<Catalyst::Utils>.
baf6a3db 215
5d02e790 216=head2 $c->expand_modules( $setup_component_config )
217
218Return a list of extra components that this component has created. By default,
219it just looks for a list of inner packages of this component
220
221=cut
222
825dbf85 223=head1 OPTIONAL METHODS
224
225=head2 ACCEPT_CONTEXT($c, @args)
226
f9c35d6c 227Catalyst components are normally initialized during server startup, either
825dbf85 228as a Class or a Instance. However, some components require information about
229the current request. To do so, they can implement an ACCEPT_CONTEXT method.
230
231If this method is present, it is called during $c->comp/controller/model/view
232with the current $c and any additional args (e.g. $c->model('Foo', qw/bar baz/)
233would cause your MyApp::Model::Foo instance's ACCEPT_CONTEXT to be called with
234($c, 'bar', 'baz')) and the return value of this method is returned to the
235calling code in the application rather than the component itself.
236
158c88c0 237=head1 SEE ALSO
238
e7f1cf73 239L<Catalyst>, L<Catalyst::Model>, L<Catalyst::View>, L<Catalyst::Controller>.
158c88c0 240
2f381252 241=head1 AUTHORS
158c88c0 242
2f381252 243Catalyst Contributors, see Catalyst.pm
158c88c0 244
245=head1 COPYRIGHT
246
536bee89 247This library is free software. You can redistribute it and/or modify it under
158c88c0 248the same terms as Perl itself.
249
85d9fce6 250=cut