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