1 package Catalyst::Component;
5 use Class::MOP::Object;
7 use Class::C3::Adopt::NEXT;
8 use Devel::InnerPackage ();
11 use Scalar::Util 'blessed';
12 use namespace::clean -except => 'meta';
14 with 'MooseX::Emulate::Class::Accessor::Fast';
15 with 'Catalyst::ClassData';
20 Catalyst::Component - Catalyst Component Base Class
24 # lib/MyApp/Model/Something.pm
25 package MyApp::Model::Something;
27 use base 'Catalyst::Component';
29 __PACKAGE__->config( foo => 'bar' );
41 my ( $self, $c ) = @_;
42 $c->response->output( $self->foo );
47 # Methods can be a request step
48 $c->forward(qw/MyApp::Model::Something forward_to_me/);
51 print $c->comp('MyApp::Model::Something')->test;
53 print $c->comp('MyApp::Model::Something')->foo;
57 This is the universal base class for Catalyst components
58 (Model/View/Controller).
60 It provides you with a generic new() for component construction through Catalyst's
61 component loader with config() support and a process() method placeholder.
63 B<Note> that calling C<< $self->config >> inside a component is strongly
64 not recommended - the correctly merged config should have already been
65 passed to the constructor and stored in attributes - accessing
66 the config accessor directly from an instance is likely to get the
67 wrong values (as it only holds the class wide config, not things loaded
68 from the config file!)
72 __PACKAGE__->mk_classdata('_plugins');
73 __PACKAGE__->mk_classdata('_config');
75 has catalyst_component_name => ( is => 'ro' ); # Cannot be required => 1 as context
76 # class @ISA component - HATE
77 # Naughty modules like Catalyst::View::JSON try to write to _everything_,
78 # so spit a warning, ignore that (and try to do the right thing anyway) here..
79 around catalyst_component_name => sub {
80 my ($orig, $self) = (shift, shift);
81 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 @_;
82 return $self->$orig() || blessed($self);
90 $args = $_[0] if ref($_[0]) eq 'HASH';
91 } elsif (@_ == 2) { # is it ($app, $args) or foo => 'bar' ?
93 $args = $_[1] if ref($_[1]) eq 'HASH';
94 } elsif (Class::MOP::is_class_loaded($_[0]) &&
95 $_[0]->isa('Catalyst') && ref($_[1]) eq 'HASH') {
100 } elsif (@_ % 2 == 0) {
104 return $class->merge_config_hashes( $class->config, $args );
108 my ( $class, $c ) = @_;
110 # Temporary fix, some components does not pass context to constructor
111 my $arguments = ( ref( $_[-1] ) eq 'HASH' ) ? $_[-1] : {};
112 if ( my $next = $class->next::can ) {
113 my ($next_package) = Class::MOP::get_code_info($next);
114 warn "There is a COMPONENT method resolving after Catalyst::Component in ${next_package}.\n";
115 warn "This behavior can no longer be supported, and so your application is probably broken.\n";
116 warn "Your linearized isa hierarchy is: " . join(', ', @{ mro::get_linear_isa($class) }) . "\n";
117 warn "Please see perldoc Catalyst::Upgrading for more information about this issue.\n";
119 return $class->new($c, $arguments);
124 # Uncomment once sane to do so
125 #Carp::cluck("config method called on instance") if ref $self;
126 my $config = $self->_config || {};
128 my $newconfig = { %{@_ > 1 ? {@_} : $_[0]} };
130 $self->merge_config_hashes( $config, $newconfig )
133 # this is a bit of a kludge, required to make
134 # __PACKAGE__->config->{foo} = 'bar';
135 # work in a subclass.
136 # TODO maybe this should be a ClassData option?
137 my $class = blessed($self) || $self;
138 my $meta = Class::MOP::get_metaclass_by_name($class);
139 unless (${ $meta->get_or_add_package_symbol('$_config') }) {
140 # Call merge_hashes to ensure we deep copy the parent
141 # config onto the subclass
142 $self->_config( Catalyst::Utils::merge_hashes($config, {}) );
145 return $self->_config;
148 sub merge_config_hashes {
149 my ( $self, $lefthash, $righthash ) = @_;
151 return Catalyst::Utils::merge_hashes( $lefthash, $righthash );
156 Catalyst::Exception->throw( message => ( ref $_[0] || $_[0] )
157 . " did not override Catalyst::Component::process" );
161 my ($class, $component) = @_;
162 return Devel::InnerPackage::list_packages( $component );
165 __PACKAGE__->meta->make_immutable;
173 =head2 new($app, $arguments)
175 Called by COMPONENT to instantiate the component; should return an object
176 to be stored in the application's component hash.
180 C<< my $component_instance = $component->COMPONENT($app, $arguments); >>
182 If this method is present (as it is on all Catalyst::Component subclasses),
183 it is called by Catalyst during setup_components with the application class
184 as $app and any config entry on the application for this component (for example,
185 in the case of MyApp::Controller::Foo this would be
186 C<< MyApp->config('Controller::Foo' => \%conf >>).
188 The arguments are expected to be a hashref and are merged with the
189 C<< __PACKAGE__->config >> hashref before calling C<< ->new >>
190 to instantiate the component.
192 You can override it in your components to do custom construction, using
196 my ($class, $app, $args) = @_;
197 $args = $class->merge_config_hashes($class->config, $args);
198 return $class->new($app, $args);
203 =head2 $c->config($hashref)
205 =head2 $c->config($key, $value, ...)
207 Accessor for this component's config hash. Config values can be set as
208 key value pair, or you can specify a hashref. In either case the keys
209 will be merged with any existing config settings. Each component in
210 a Catalyst application has its own config hash.
212 The component's config hash is merged with any config entry on the
213 application for this component and passed to C<new()> (as mentioned
214 above at L</COMPONENT>). The recommended practice to access the merged
215 config is to use a Moose attribute for each config entry on the
220 This is the default method called on a Catalyst component in the dispatcher.
221 For instance, Views implement this action to render the response body
222 when you forward to them. The default is an abstract method.
224 =head2 $c->merge_config_hashes( $hashref, $hashref )
226 Merges two hashes together recursively, giving right-hand precedence.
227 Alias for the method in L<Catalyst::Utils>.
229 =head2 $c->expand_modules( $setup_component_config )
231 Return a list of extra components that this component has created. By default,
232 it just looks for a list of inner packages of this component
236 =head1 OPTIONAL METHODS
238 =head2 ACCEPT_CONTEXT($c, @args)
240 Catalyst components are normally initialized during server startup, either
241 as a Class or a Instance. However, some components require information about
242 the current request. To do so, they can implement an ACCEPT_CONTEXT method.
244 If this method is present, it is called during $c->comp/controller/model/view
245 with the current $c and any additional args (e.g. $c->model('Foo', qw/bar baz/)
246 would cause your MyApp::Model::Foo instance's ACCEPT_CONTEXT to be called with
247 ($c, 'bar', 'baz')) and the return value of this method is returned to the
248 calling code in the application rather than the component itself.
252 L<Catalyst>, L<Catalyst::Model>, L<Catalyst::View>, L<Catalyst::Controller>.
256 Catalyst Contributors, see Catalyst.pm
260 This library is free software. You can redistribute it and/or modify it under
261 the same terms as Perl itself.