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