Fix and test for issues when components import or define a meta method
[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;
a7caa492 6use MooseX::Adopt::Class::Accessor::Fast;
e8b9f2a9 7use Catalyst::Utils;
cb89a296 8use Class::C3::Adopt::NEXT;
6a7254b5 9use MRO::Compat;
10use mro 'c3';
5595dd2f 11
a7caa492 12with 'MooseX::Emulate::Class::Accessor::Fast';
13with 'Catalyst::ClassData';
14
15
158c88c0 16=head1 NAME
17
18Catalyst::Component - Catalyst Component Base Class
19
20=head1 SYNOPSIS
21
22 # lib/MyApp/Model/Something.pm
23 package MyApp::Model::Something;
24
e7f1cf73 25 use base 'Catalyst::Component';
158c88c0 26
27 __PACKAGE__->config( foo => 'bar' );
28
29 sub test {
30 my $self = shift;
31 return $self->{foo};
32 }
33
34 sub forward_to_me {
35 my ( $self, $c ) = @_;
36 $c->response->output( $self->{foo} );
37 }
ac5c933b 38
158c88c0 39 1;
40
41 # Methods can be a request step
42 $c->forward(qw/MyApp::Model::Something forward_to_me/);
43
44 # Or just methods
45 print $c->comp('MyApp::Model::Something')->test;
46
47 print $c->comp('MyApp::Model::Something')->{foo};
48
49=head1 DESCRIPTION
50
ac5c933b 51This is the universal base class for Catalyst components
158c88c0 52(Model/View/Controller).
53
54It provides you with a generic new() for instantiation through Catalyst's
55component loader with config() support and a process() method placeholder.
56
7cd1a42b 57=cut
158c88c0 58
46d0346d 59__PACKAGE__->mk_classdata('_plugins');
11b256bc 60__PACKAGE__->mk_classdata('_config');
e8b9f2a9 61
2ef59958 62sub BUILDARGS {
63 my ($self) = @_;
64
158c88c0 65 # Temporary fix, some components does not pass context to constructor
66 my $arguments = ( ref( $_[-1] ) eq 'HASH' ) ? $_[-1] : {};
e8b9f2a9 67
a7caa492 68 my $args = $self->merge_config_hashes( $self->config, $arguments );
2ef59958 69
70 return $args;
71}
4090e3bb 72
22247e54 73sub COMPONENT {
74 my ( $self, $c ) = @_;
75
76 # Temporary fix, some components does not pass context to constructor
77 my $arguments = ( ref( $_[-1] ) eq 'HASH' ) ? $_[-1] : {};
6a7254b5 78 if( my $next = $self->next::can ){
79 my $class = blessed $self || $self;
80 my ($next_package) = Class::MOP::get_code_info($next);
81 warn "There is a COMPONENT method resolving after Catalyst::Component in ${next_package}. This behavior is deprecated and will stop working in future releases.";
82 return $next->($self, $arguments);
83 }
4090e3bb 84 return $self->new($c, $arguments);
22247e54 85}
86
158c88c0 87sub config {
11b256bc 88 my $self = shift;
89 my $config = $self->_config || {};
90 if (@_) {
91 my $newconfig = { %{@_ > 1 ? {@_} : $_[0]} };
92 $self->_config(
93 $self->merge_config_hashes( $config, $newconfig )
94 );
95 } else {
96 # this is a bit of a kludge, required to make
97 # __PACKAGE__->config->{foo} = 'bar';
98 # work in a subclass. If we don't have the package symbol in the
99 # current class we know we need to copy up to ours, which calling
100 # the setter will do for us.
74c89dea 101 my $meta = $self->Class::MOP::Object::meta();
102 unless ($meta->has_package_symbol('$_config')) {
11b256bc 103
104 $config = $self->merge_config_hashes( $config, {} );
105 $self->_config( $config );
46d0346d 106 }
158c88c0 107 }
11b256bc 108 return $config;
158c88c0 109}
110
7cd1a42b 111sub merge_config_hashes {
112 my ( $self, $lefthash, $righthash ) = @_;
158c88c0 113
7cd1a42b 114 return Catalyst::Utils::merge_hashes( $lefthash, $righthash );
115}
158c88c0 116
117sub process {
118
119 Catalyst::Exception->throw( message => ( ref $_[0] || $_[0] )
120 . " did not override Catalyst::Component::process" );
121}
122
213cf5bb 123no Moose;
46d0346d 124
125__PACKAGE__->meta->make_immutable;
7cd1a42b 1261;
baf6a3db 127
7cd1a42b 128__END__
baf6a3db 129
7cd1a42b 130=head1 METHODS
baf6a3db 131
7cd1a42b 132=head2 new($c, $arguments)
baf6a3db 133
7cd1a42b 134Called by COMPONENT to instantiate the component; should return an object
135to be stored in the application's component hash.
136
137=head2 COMPONENT($c, $arguments)
138
139If this method is present (as it is on all Catalyst::Component subclasses,
140it is called by Catalyst during setup_components with the application class
141as $c and any config entry on the application for this component (for example,
142in the case of MyApp::Controller::Foo this would be
ac5c933b 143MyApp->config->{'Controller::Foo'}). The arguments are expected to be a
144hashref and are merged with the __PACKAGE__->config hashref before calling
7cd1a42b 145->new to instantiate the component.
146
147=head2 $c->config
148
149=head2 $c->config($hashref)
150
151=head2 $c->config($key, $value, ...)
152
ac5c933b 153Accessor for this component's config hash. Config values can be set as
7cd1a42b 154key value pair, or you can specify a hashref. In either case the keys
ac5c933b 155will be merged with any existing config settings. Each component in
7cd1a42b 156a Catalyst application has it's own config hash.
157
158=head2 $c->process()
159
160This is the default method called on a Catalyst component in the dispatcher.
ac5c933b 161For instance, Views implement this action to render the response body
7cd1a42b 162when you forward to them. The default is an abstract method.
163
164=head2 $c->merge_config_hashes( $hashref, $hashref )
165
166Merges two hashes together recursively, giving right-hand precedence.
167Alias for the method in L<Catalyst::Utils>.
baf6a3db 168
825dbf85 169=head1 OPTIONAL METHODS
170
171=head2 ACCEPT_CONTEXT($c, @args)
172
173Catalyst components are normally initalized during server startup, either
174as a Class or a Instance. However, some components require information about
175the current request. To do so, they can implement an ACCEPT_CONTEXT method.
176
177If this method is present, it is called during $c->comp/controller/model/view
178with the current $c and any additional args (e.g. $c->model('Foo', qw/bar baz/)
179would cause your MyApp::Model::Foo instance's ACCEPT_CONTEXT to be called with
180($c, 'bar', 'baz')) and the return value of this method is returned to the
181calling code in the application rather than the component itself.
182
158c88c0 183=head1 SEE ALSO
184
e7f1cf73 185L<Catalyst>, L<Catalyst::Model>, L<Catalyst::View>, L<Catalyst::Controller>.
158c88c0 186
2f381252 187=head1 AUTHORS
158c88c0 188
2f381252 189Catalyst Contributors, see Catalyst.pm
158c88c0 190
191=head1 COPYRIGHT
192
193This program is free software, you can redistribute it and/or modify it under
194the same terms as Perl itself.
195
85d9fce6 196=cut