Class::MOP::load_class, is_class_loaded was deprecated in Moose-2.1100
[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';
e7399d8b 12use Class::Load 'is_class_loaded';
7a5ed4ef 13use namespace::clean -except => 'meta';
5595dd2f 14
a7caa492 15with 'MooseX::Emulate::Class::Accessor::Fast';
16with 'Catalyst::ClassData';
17
18
158c88c0 19=head1 NAME
20
21Catalyst::Component - Catalyst Component Base Class
22
23=head1 SYNOPSIS
24
25 # lib/MyApp/Model/Something.pm
26 package MyApp::Model::Something;
27
e7f1cf73 28 use base 'Catalyst::Component';
158c88c0 29
30 __PACKAGE__->config( foo => 'bar' );
31
d8ccdd9d 32 has foo => (
33 is => 'ro',
34 );
35
158c88c0 36 sub test {
37 my $self = shift;
d8ccdd9d 38 return $self->foo;
158c88c0 39 }
40
41 sub forward_to_me {
42 my ( $self, $c ) = @_;
d8ccdd9d 43 $c->response->output( $self->foo );
158c88c0 44 }
43c58153 45
158c88c0 46 1;
47
48 # Methods can be a request step
49 $c->forward(qw/MyApp::Model::Something forward_to_me/);
50
51 # Or just methods
52 print $c->comp('MyApp::Model::Something')->test;
53
d8ccdd9d 54 print $c->comp('MyApp::Model::Something')->foo;
158c88c0 55
56=head1 DESCRIPTION
57
43c58153 58This is the universal base class for Catalyst components
158c88c0 59(Model/View/Controller).
60
cf8eab35 61It provides you with a generic new() for component construction through Catalyst's
158c88c0 62component loader with config() support and a process() method placeholder.
63
d8ccdd9d 64B<Note> that calling C<< $self->config >> inside a component is strongly
c80736fa 65not recommended - the correctly merged config should have already been
d8ccdd9d 66passed to the constructor and stored in attributes - accessing
67the config accessor directly from an instance is likely to get the
68wrong values (as it only holds the class wide config, not things loaded
69from the config file!)
70
7cd1a42b 71=cut
158c88c0 72
46d0346d 73__PACKAGE__->mk_classdata('_plugins');
11b256bc 74__PACKAGE__->mk_classdata('_config');
e8b9f2a9 75
8f6cebb2 76has catalyst_component_name => ( is => 'ro' ); # Cannot be required => 1 as context
d2598ac8 77 # class @ISA component - HATE
78# Make accessor callable as a class method, as we need to call setup_actions
79# on the application class, which we don't have an instance of, ewwwww
e65d000f 80# Also, naughty modules like Catalyst::View::JSON try to write to _everything_,
81# so spit a warning, ignore that (and try to do the right thing anyway) here..
8f6cebb2 82around catalyst_component_name => sub {
d2598ac8 83 my ($orig, $self) = (shift, shift);
8f6cebb2 84 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 85 blessed($self) ? $self->$orig() || blessed($self) : $self;
d2598ac8 86};
1b79e199 87
2ef59958 88sub BUILDARGS {
7a5ed4ef 89 my $class = shift;
90 my $args = {};
91
92 if (@_ == 1) {
93 $args = $_[0] if ref($_[0]) eq 'HASH';
94 } elsif (@_ == 2) { # is it ($app, $args) or foo => 'bar' ?
95 if (blessed($_[0])) {
96 $args = $_[1] if ref($_[1]) eq 'HASH';
e7399d8b 97 } elsif (is_class_loaded($_[0]) &&
7a5ed4ef 98 $_[0]->isa('Catalyst') && ref($_[1]) eq 'HASH') {
99 $args = $_[1];
7a5ed4ef 100 } else {
101 $args = +{ @_ };
102 }
103 } elsif (@_ % 2 == 0) {
104 $args = +{ @_ };
105 }
43c58153 106
7a5ed4ef 107 return $class->merge_config_hashes( $class->config, $args );
2ef59958 108}
4090e3bb 109
22247e54 110sub COMPONENT {
1b79e199 111 my ( $class, $c ) = @_;
22247e54 112
113 # Temporary fix, some components does not pass context to constructor
114 my $arguments = ( ref( $_[-1] ) eq 'HASH' ) ? $_[-1] : {};
1b79e199 115 if ( my $next = $class->next::can ) {
6a7254b5 116 my ($next_package) = Class::MOP::get_code_info($next);
7e2ec16e 117 warn "There is a COMPONENT method resolving after Catalyst::Component in ${next_package}.\n";
118 warn "This behavior can no longer be supported, and so your application is probably broken.\n";
1cc8db0c 119 warn "Your linearized isa hierarchy is: " . join(', ', @{ mro::get_linear_isa($class) }) . "\n";
7e2ec16e 120 warn "Please see perldoc Catalyst::Upgrading for more information about this issue.\n";
6a7254b5 121 }
1b79e199 122 return $class->new($c, $arguments);
22247e54 123}
124
158c88c0 125sub config {
11b256bc 126 my $self = shift;
df960201 127 # Uncomment once sane to do so
128 #Carp::cluck("config method called on instance") if ref $self;
11b256bc 129 my $config = $self->_config || {};
130 if (@_) {
131 my $newconfig = { %{@_ > 1 ? {@_} : $_[0]} };
132 $self->_config(
133 $self->merge_config_hashes( $config, $newconfig )
134 );
135 } else {
136 # this is a bit of a kludge, required to make
137 # __PACKAGE__->config->{foo} = 'bar';
edffeb5a 138 # work in a subclass.
7a5ed4ef 139 # TODO maybe this should be a ClassData option?
e106a59f 140 my $class = blessed($self) || $self;
141 my $meta = Class::MOP::get_metaclass_by_name($class);
fc9ec364 142 unless (${ $meta->get_or_add_package_symbol('$_config') }) {
c03aaf03 143 # Call merge_hashes to ensure we deep copy the parent
144 # config onto the subclass
145 $self->_config( Catalyst::Utils::merge_hashes($config, {}) );
46d0346d 146 }
158c88c0 147 }
7a5ed4ef 148 return $self->_config;
158c88c0 149}
150
7cd1a42b 151sub merge_config_hashes {
152 my ( $self, $lefthash, $righthash ) = @_;
158c88c0 153
7cd1a42b 154 return Catalyst::Utils::merge_hashes( $lefthash, $righthash );
155}
158c88c0 156
157sub process {
158
159 Catalyst::Exception->throw( message => ( ref $_[0] || $_[0] )
160 . " did not override Catalyst::Component::process" );
161}
162
5d02e790 163sub expand_modules {
164 my ($class, $component) = @_;
165 return Devel::InnerPackage::list_packages( $component );
166}
167
46d0346d 168__PACKAGE__->meta->make_immutable;
7a5ed4ef 169
7cd1a42b 1701;
baf6a3db 171
7cd1a42b 172__END__
baf6a3db 173
7cd1a42b 174=head1 METHODS
baf6a3db 175
58064941 176=head2 new($app, $arguments)
baf6a3db 177
7cd1a42b 178Called by COMPONENT to instantiate the component; should return an object
179to be stored in the application's component hash.
180
7a5ed4ef 181=head2 COMPONENT
182
183C<< my $component_instance = $component->COMPONENT($app, $arguments); >>
7cd1a42b 184
f8a54681 185If this method is present (as it is on all Catalyst::Component subclasses),
7cd1a42b 186it is called by Catalyst during setup_components with the application class
58064941 187as $app and any config entry on the application for this component (for example,
7cd1a42b 188in the case of MyApp::Controller::Foo this would be
9779c885 189C<< MyApp->config('Controller::Foo' => \%conf >>).
58064941 190
9779c885 191The arguments are expected to be a hashref and are merged with the
192C<< __PACKAGE__->config >> hashref before calling C<< ->new >>
193to instantiate the component.
7cd1a42b 194
cf8eab35 195You can override it in your components to do custom construction, using
7a5ed4ef 196something like this:
197
198 sub COMPONENT {
199 my ($class, $app, $args) = @_;
2a787673 200 $args = $class->merge_config_hashes($class->config, $args);
7a5ed4ef 201 return $class->new($app, $args);
202 }
203
7cd1a42b 204=head2 $c->config
205
206=head2 $c->config($hashref)
207
208=head2 $c->config($key, $value, ...)
209
43c58153 210Accessor for this component's config hash. Config values can be set as
7cd1a42b 211key value pair, or you can specify a hashref. In either case the keys
43c58153 212will be merged with any existing config settings. Each component in
213a Catalyst application has its own config hash.
7cd1a42b 214
f8a54681 215The component's config hash is merged with any config entry on the
216application for this component and passed to C<new()> (as mentioned
d8ccdd9d 217above at L</COMPONENT>). The recommended practice to access the merged
f8a54681 218config is to use a Moose attribute for each config entry on the
219receiving component.
220
7cd1a42b 221=head2 $c->process()
222
223This is the default method called on a Catalyst component in the dispatcher.
43c58153 224For instance, Views implement this action to render the response body
7cd1a42b 225when you forward to them. The default is an abstract method.
226
227=head2 $c->merge_config_hashes( $hashref, $hashref )
228
229Merges two hashes together recursively, giving right-hand precedence.
230Alias for the method in L<Catalyst::Utils>.
baf6a3db 231
5d02e790 232=head2 $c->expand_modules( $setup_component_config )
233
234Return a list of extra components that this component has created. By default,
235it just looks for a list of inner packages of this component
236
237=cut
238
825dbf85 239=head1 OPTIONAL METHODS
240
241=head2 ACCEPT_CONTEXT($c, @args)
242
f9c35d6c 243Catalyst components are normally initialized during server startup, either
825dbf85 244as a Class or a Instance. However, some components require information about
245the current request. To do so, they can implement an ACCEPT_CONTEXT method.
246
247If this method is present, it is called during $c->comp/controller/model/view
248with the current $c and any additional args (e.g. $c->model('Foo', qw/bar baz/)
249would cause your MyApp::Model::Foo instance's ACCEPT_CONTEXT to be called with
250($c, 'bar', 'baz')) and the return value of this method is returned to the
251calling code in the application rather than the component itself.
252
158c88c0 253=head1 SEE ALSO
254
e7f1cf73 255L<Catalyst>, L<Catalyst::Model>, L<Catalyst::View>, L<Catalyst::Controller>.
158c88c0 256
2f381252 257=head1 AUTHORS
158c88c0 258
2f381252 259Catalyst Contributors, see Catalyst.pm
158c88c0 260
261=head1 COPYRIGHT
262
536bee89 263This library is free software. You can redistribute it and/or modify it under
158c88c0 264the same terms as Perl itself.
265
85d9fce6 266=cut