still failing some tests. waiting for suggestions on whether to fix old CDIretardedness
[catagits/Catalyst-Runtime.git] / lib / Catalyst / Component.pm
1 package Catalyst::Component;
2
3 use Moose;
4 use Class::MOP;
5 use MooseX::Adopt::Class::Accessor::Fast;
6 use Catalyst::Utils;
7 use MRO::Compat;
8 use mro 'c3';
9
10 with 'MooseX::Emulate::Class::Accessor::Fast';
11 with 'Catalyst::ClassData';
12
13
14 =head1 NAME
15
16 Catalyst::Component - Catalyst Component Base Class
17
18 =head1 SYNOPSIS
19
20     # lib/MyApp/Model/Something.pm
21     package MyApp::Model::Something;
22
23     use base 'Catalyst::Component';
24
25     __PACKAGE__->config( foo => 'bar' );
26
27     sub test {
28         my $self = shift;
29         return $self->{foo};
30     }
31
32     sub forward_to_me {
33         my ( $self, $c ) = @_;
34         $c->response->output( $self->{foo} );
35     }
36     
37     1;
38
39     # Methods can be a request step
40     $c->forward(qw/MyApp::Model::Something forward_to_me/);
41
42     # Or just methods
43     print $c->comp('MyApp::Model::Something')->test;
44
45     print $c->comp('MyApp::Model::Something')->{foo};
46
47 =head1 DESCRIPTION
48
49 This is the universal base class for Catalyst components 
50 (Model/View/Controller).
51
52 It provides you with a generic new() for instantiation through Catalyst's
53 component loader with config() support and a process() method placeholder.
54
55 =cut
56
57 __PACKAGE__->mk_classdata($_) for qw/_config _plugins/;
58
59 around new => sub {
60     my ( $orig, $self) = @_;
61
62     # Temporary fix, some components does not pass context to constructor
63     my $arguments = ( ref( $_[-1] ) eq 'HASH' ) ? $_[-1] : {};
64
65     my $args =  $self->merge_config_hashes( $self->config, $arguments );
66     $self->$orig( $args );
67 };
68
69 no Moose;
70
71 sub COMPONENT {
72     my ( $self, $c ) = @_;
73
74     # Temporary fix, some components does not pass context to constructor
75     my $arguments = ( ref( $_[-1] ) eq 'HASH' ) ? $_[-1] : {};
76     if( my $next = $self->next::can ){
77       my $class = blessed $self || $self;
78       my ($next_package) = Class::MOP::get_code_info($next);
79       warn "There is a COMPONENT method resolving after Catalyst::Component in ${next_package}. This behavior is deprecated and will stop working in future releases.";
80       return $next->($self, $arguments);
81     }
82     return $self->new($c, $arguments);
83 }
84
85 sub config {
86     my $self = shift;
87     my $config = $self->_config ||{};
88     if (@_) {
89         my $newconfig = { %{@_ > 1 ? {@_} : $_[0]} };
90         $self->_config(
91             $self->merge_config_hashes( $config, $newconfig )
92         );
93     }
94     return $config;
95 }
96
97 sub merge_config_hashes {
98     my ( $self, $lefthash, $righthash ) = @_;
99
100     return Catalyst::Utils::merge_hashes( $lefthash, $righthash );
101 }
102
103 sub process {
104
105     Catalyst::Exception->throw( message => ( ref $_[0] || $_[0] )
106           . " did not override Catalyst::Component::process" );
107 }
108
109 1;
110
111 __END__
112
113 =head1 METHODS
114
115 =head2 new($c, $arguments)
116
117 Called by COMPONENT to instantiate the component; should return an object
118 to be stored in the application's component hash.
119
120 =head2 COMPONENT($c, $arguments)
121
122 If this method is present (as it is on all Catalyst::Component subclasses,
123 it is called by Catalyst during setup_components with the application class
124 as $c and any config entry on the application for this component (for example,
125 in the case of MyApp::Controller::Foo this would be
126 MyApp->config->{'Controller::Foo'}). The arguments are expected to be a 
127 hashref and are merged with the __PACKAGE__->config hashref before calling 
128 ->new to instantiate the component.
129
130 =head2 $c->config
131
132 =head2 $c->config($hashref)
133
134 =head2 $c->config($key, $value, ...)
135
136 Accessor for this component's config hash. Config values can be set as 
137 key value pair, or you can specify a hashref. In either case the keys
138 will be merged with any existing config settings. Each component in 
139 a Catalyst application has it's own config hash.
140
141 =head2 $c->process()
142
143 This is the default method called on a Catalyst component in the dispatcher.
144 For instance, Views implement this action to render the response body 
145 when you forward to them. The default is an abstract method.
146
147 =head2 $c->merge_config_hashes( $hashref, $hashref )
148
149 Merges two hashes together recursively, giving right-hand precedence.
150 Alias for the method in L<Catalyst::Utils>.
151
152 =head1 OPTIONAL METHODS
153
154 =head2 ACCEPT_CONTEXT($c, @args)
155
156 Catalyst components are normally initalized during server startup, either
157 as a Class or a Instance. However, some components require information about
158 the current request. To do so, they can implement an ACCEPT_CONTEXT method.
159
160 If this method is present, it is called during $c->comp/controller/model/view
161 with the current $c and any additional args (e.g. $c->model('Foo', qw/bar baz/)
162 would cause your MyApp::Model::Foo instance's ACCEPT_CONTEXT to be called with
163 ($c, 'bar', 'baz')) and the return value of this method is returned to the
164 calling code in the application rather than the component itself.
165
166 =head1 SEE ALSO
167
168 L<Catalyst>, L<Catalyst::Model>, L<Catalyst::View>, L<Catalyst::Controller>.
169
170 =head1 AUTHOR
171
172 Sebastian Riedel, C<sri@cpan.org>
173 Marcus Ramberg, C<mramberg@cpan.org>
174 Matt S Trout, C<mst@shadowcatsystems.co.uk>
175
176 =head1 COPYRIGHT
177
178 This program is free software, you can redistribute it and/or modify it under
179 the same terms as Perl itself.
180
181 =cut