default_view / default_model
[catagits/Catalyst-Runtime.git] / lib / Catalyst / IOC / Container.pm
CommitLineData
a6c13ff4 1package Catalyst::IOC::Container;
b4a6fa62 2use Bread::Board;
3use Moose;
4use Config::Any;
5use Data::Visitor::Callback;
6use Catalyst::Utils ();
2bb0da6d 7use MooseX::Types::LoadableClass qw/ LoadableClass /;
a6c13ff4 8use Catalyst::IOC::BlockInjection;
8b749525 9use namespace::autoclean;
b4a6fa62 10
11extends 'Bread::Board::Container';
12
13has config_local_suffix => (
442ab13e 14 is => 'ro',
b4a6fa62 15 isa => 'Str',
16 default => 'local',
17);
18
19has driver => (
442ab13e 20 is => 'ro',
b4a6fa62 21 isa => 'HashRef',
22 default => sub { +{} },
23);
24
25has file => (
442ab13e 26 is => 'ro',
b4a6fa62 27 isa => 'Str',
28 default => '',
29);
30
31has substitutions => (
442ab13e 32 is => 'ro',
b4a6fa62 33 isa => 'HashRef',
34 default => sub { +{} },
35);
36
37has name => (
442ab13e 38 is => 'ro',
b4a6fa62 39 isa => 'Str',
40 default => 'TestApp',
41);
42
2bb0da6d 43has sub_container_class => (
44 isa => LoadableClass,
45 is => 'ro',
46 coerce => 1,
a6c13ff4 47 default => 'Catalyst::IOC::SubContainer',
8b749525 48 handles => {
49 new_sub_container => 'new',
50 }
2bb0da6d 51);
52
b4a6fa62 53sub BUILD {
a2c0d071 54 my ( $self, $params ) = @_;
b4a6fa62 55
292277c1 56 $self->add_service(
57 $self->${\"build_${_}_service"}
58 ) for qw/
7451d1ea 59 substitutions
60 file
61 driver
62 name
63 prefix
64 extensions
65 path
66 config
67 raw_config
68 global_files
69 local_files
70 global_config
71 local_config
72 config_local_suffix
73 config_path
74 /;
f04816ce 75
292277c1 76 $self->add_sub_container(
a2c0d071 77 $self->build_controller_subcontainer
78 );
79
80 $self->add_sub_container(
81 $self->build_view_subcontainer(
82 default_component => $params->{default_view},
83 )
84 );
85
86 $self->add_sub_container(
87 $self->build_model_subcontainer(
88 default_component => $params->{default_model},
89 )
90 );
f04816ce 91}
92
93sub build_model_subcontainer {
94 my $self = shift;
95
a2c0d071 96 return $self->new_sub_container( @_,
5a53ef3d 97 name => 'model',
b06ded69 98 );
f04816ce 99}
100
101sub build_view_subcontainer {
102 my $self = shift;
103
a2c0d071 104 return $self->new_sub_container( @_,
5a53ef3d 105 name => 'view',
b06ded69 106 );
f04816ce 107}
108
109sub build_controller_subcontainer {
110 my $self = shift;
111
b06ded69 112 return $self->new_sub_container(
5a53ef3d 113 name => 'controller',
b06ded69 114 );
f04816ce 115}
116
f04816ce 117sub build_name_service {
118 my $self = shift;
292277c1 119
120 return Bread::Board::Literal->new( name => 'name', value => $self->name );
f04816ce 121}
122
123sub build_driver_service {
124 my $self = shift;
292277c1 125
126 return Bread::Board::Literal->new( name => 'driver', value => $self->driver );
f04816ce 127}
128
129sub build_file_service {
130 my $self = shift;
292277c1 131
132 return Bread::Board::Literal->new( name => 'file', value => $self->file );
f04816ce 133}
134
135sub build_substitutions_service {
136 my $self = shift;
292277c1 137
138 return Bread::Board::Literal->new( name => 'substitutions', value => $self->substitutions );
f04816ce 139}
140
141sub build_extensions_service {
142 my $self = shift;
292277c1 143
144 return Bread::Board::BlockInjection->new(
145 name => 'extensions',
146 block => sub {
147 return \@{Config::Any->extensions};
148 },
f04816ce 149 );
150}
b4a6fa62 151
f04816ce 152sub build_prefix_service {
153 my $self = shift;
292277c1 154
155 return Bread::Board::BlockInjection->new(
156 name => 'prefix',
157 block => sub {
158 return Catalyst::Utils::appprefix( shift->param('name') );
159 },
160 dependencies => [ depends_on('name') ],
f04816ce 161 );
162}
b4a6fa62 163
f04816ce 164sub build_path_service {
165 my $self = shift;
292277c1 166
167 return Bread::Board::BlockInjection->new(
168 name => 'path',
169 block => sub {
170 my $s = shift;
171
172 return Catalyst::Utils::env_value( $s->param('name'), 'CONFIG' )
173 || $s->param('file')
174 || $s->param('name')->path_to( $s->param('prefix') );
175 },
176 dependencies => [ depends_on('file'), depends_on('name'), depends_on('prefix') ],
f04816ce 177 );
178}
b4a6fa62 179
f04816ce 180sub build_config_service {
181 my $self = shift;
292277c1 182
183 return Bread::Board::BlockInjection->new(
184 name => 'config',
185 block => sub {
186 my $s = shift;
187
188 my $v = Data::Visitor::Callback->new(
189 plain_value => sub {
190 return unless defined $_;
191 return $self->_config_substitutions( $s->param('name'), $s->param('substitutions'), $_ );
192 }
193
194 );
195 $v->visit( $s->param('raw_config') );
196 },
197 dependencies => [ depends_on('name'), depends_on('raw_config'), depends_on('substitutions') ],
f04816ce 198 );
199}
b4a6fa62 200
f04816ce 201sub build_raw_config_service {
202 my $self = shift;
292277c1 203
204 return Bread::Board::BlockInjection->new(
205 name => 'raw_config',
206 block => sub {
207 my $s = shift;
208
209 my @global = @{$s->param('global_config')};
210 my @locals = @{$s->param('local_config')};
211
212 my $config = {};
213 for my $cfg (@global, @locals) {
214 for (keys %$cfg) {
215 $config = Catalyst::Utils::merge_hashes( $config, $cfg->{$_} );
b4a6fa62 216 }
292277c1 217 }
218 return $config;
219 },
220 dependencies => [ depends_on('global_config'), depends_on('local_config') ],
f04816ce 221 );
222}
b4a6fa62 223
f04816ce 224sub build_global_files_service {
225 my $self = shift;
b4a6fa62 226
292277c1 227 return Bread::Board::BlockInjection->new(
228 name => 'global_files',
229 block => sub {
230 my $s = shift;
b4a6fa62 231
292277c1 232 my ( $path, $extension ) = @{$s->param('config_path')};
b4a6fa62 233
292277c1 234 my @extensions = @{$s->param('extensions')};
235
236 my @files;
237 if ( $extension ) {
238 die "Unable to handle files with the extension '${extension}'" unless grep { $_ eq $extension } @extensions;
239 push @files, $path;
240 } else {
241 @files = map { "$path.$_" } @extensions;
242 }
243 return \@files;
244 },
245 dependencies => [ depends_on('extensions'), depends_on('config_path') ],
f04816ce 246 );
247}
b4a6fa62 248
f04816ce 249sub build_local_files_service {
250 my $self = shift;
292277c1 251
252 return Bread::Board::BlockInjection->new(
253 name => 'local_files',
254 block => sub {
255 my $s = shift;
256
257 my ( $path, $extension ) = @{$s->param('config_path')};
258 my $suffix = $s->param('config_local_suffix');
259
260 my @extensions = @{$s->param('extensions')};
261
262 my @files;
263 if ( $extension ) {
264 die "Unable to handle files with the extension '${extension}'" unless grep { $_ eq $extension } @extensions;
265 $path =~ s{\.$extension}{_$suffix.$extension};
266 push @files, $path;
267 } else {
268 @files = map { "${path}_${suffix}.$_" } @extensions;
269 }
270 return \@files;
271 },
272 dependencies => [ depends_on('extensions'), depends_on('config_path'), depends_on('config_local_suffix') ],
f04816ce 273 );
274}
b4a6fa62 275
f04816ce 276sub build_global_config_service {
277 my $self = shift;
292277c1 278
279 return Bread::Board::BlockInjection->new(
280 name => 'global_config',
281 block => sub {
282 my $s = shift;
283
284 return Config::Any->load_files({
285 files => $s->param('global_files'),
286 filter => \&_fix_syntax,
287 use_ext => 1,
288 driver_args => $s->param('driver'),
289 });
290 },
291 dependencies => [ depends_on('global_files') ],
f04816ce 292 );
293}
b4a6fa62 294
f04816ce 295sub build_local_config_service {
296 my $self = shift;
292277c1 297
298 return Bread::Board::BlockInjection->new(
299 name => 'local_config',
300 block => sub {
301 my $s = shift;
302
303 return Config::Any->load_files({
304 files => $s->param('local_files'),
305 filter => \&_fix_syntax,
306 use_ext => 1,
307 driver_args => $s->param('driver'),
308 });
309 },
310 dependencies => [ depends_on('local_files') ],
f04816ce 311 );
312}
b4a6fa62 313
f04816ce 314sub build_config_path_service {
315 my $self = shift;
b4a6fa62 316
292277c1 317 return Bread::Board::BlockInjection->new(
318 name => 'config_path',
319 block => sub {
320 my $s = shift;
b4a6fa62 321
292277c1 322 my $path = $s->param('path');
323 my $prefix = $s->param('prefix');
b4a6fa62 324
292277c1 325 my ( $extension ) = ( $path =~ m{\.(.{1,4})$} );
326
327 if ( -d $path ) {
328 $path =~ s{[\/\\]$}{};
329 $path .= "/$prefix";
330 }
b4a6fa62 331
292277c1 332 return [ $path, $extension ];
333 },
334 dependencies => [ depends_on('prefix'), depends_on('path') ],
f04816ce 335 );
336}
b4a6fa62 337
f04816ce 338sub build_config_local_suffix_service {
339 my $self = shift;
292277c1 340
341 return Bread::Board::BlockInjection->new(
342 name => 'config_local_suffix',
343 block => sub {
344 my $s = shift;
345 my $suffix = Catalyst::Utils::env_value( $s->param('name'), 'CONFIG_LOCAL_SUFFIX' ) || $self->config_local_suffix;
346
347 return $suffix;
348 },
349 dependencies => [ depends_on('name') ],
f04816ce 350 );
b4a6fa62 351}
352
353sub _fix_syntax {
354 my $config = shift;
355 my @components = (
356 map +{
357 prefix => $_ eq 'Component' ? '' : $_ . '::',
358 values => delete $config->{ lc $_ } || delete $config->{ $_ }
359 },
360 grep { ref $config->{ lc $_ } || ref $config->{ $_ } }
361 qw( Component Model M View V Controller C Plugin )
362 );
363
364 foreach my $comp ( @components ) {
365 my $prefix = $comp->{ prefix };
366 foreach my $element ( keys %{ $comp->{ values } } ) {
367 $config->{ "$prefix$element" } = $comp->{ values }->{ $element };
368 }
369 }
370}
371
372sub _config_substitutions {
6682389c 373 my ( $self, $name, $subs, $arg ) = @_;
b4a6fa62 374
375 $subs->{ HOME } ||= sub { shift->path_to( '' ); };
376 $subs->{ ENV } ||=
377 sub {
378 my ( $c, $v ) = @_;
379 if (! defined($ENV{$v})) {
380 Catalyst::Exception->throw( message =>
381 "Missing environment variable: $v" );
382 return "";
383 } else {
384 return $ENV{ $v };
385 }
386 };
387 $subs->{ path_to } ||= sub { shift->path_to( @_ ); };
388 $subs->{ literal } ||= sub { return $_[ 1 ]; };
389 my $subsre = join( '|', keys %$subs );
390
6682389c 391 $arg =~ s{__($subsre)(?:\((.+?)\))?__}{ $subs->{ $1 }->( $name, $2 ? split( /,/, $2 ) : () ) }eg;
392 return $arg;
b4a6fa62 393}
394
a17e0ff8 395sub get_component_from_sub_container {
396 my ( $self, $sub_container_name, $name, $c, @args ) = @_;
397
398 my $sub_container = $self->get_sub_container( $sub_container_name );
399
0e747f0c 400 if (!$name) {
a2c0d071 401 my $default = $sub_container->default_component;
0e747f0c 402
403 return $sub_container->get_component( $default, $c, @args )
404 if $default && $sub_container->has_service( $default );
405
406 # this is never a controller, so this is safe
407 $c->log->warn( "Calling \$c->$sub_container_name() is not supported unless you specify one of:" );
408 $c->log->warn( "* \$c->config(default_$sub_container_name => 'the name of the default $sub_container_name to use')" );
409 $c->log->warn( "* \$c->stash->{current_$sub_container_name} # the name of the view to use for this request" );
410 $c->log->warn( "* \$c->stash->{current_${sub_container_name}_instance} # the instance of the $sub_container_name to use for this request" );
a2c0d071 411
412 return;
0e747f0c 413 }
414
a17e0ff8 415 return $sub_container->get_component_regexp( $name, $c, @args )
416 if ref $name;
417
418 return $sub_container->get_component( $name, $c, @args )
419 if $sub_container->has_service( $name );
420
421 $c->log->warn(
422 "Attempted to use $sub_container_name '$name', " .
423 "but it does not exist"
424 );
425
426 return;
427}
428
d057ddb9 4291;
430
431__END__
432
433=pod
434
435=head1 NAME
436
437Catalyst::Container - IOC for Catalyst components
438
439=head1 METHODS
440
441=head2 build_model_subcontainer
442
443=head2 build_view_subcontainer
444
445=head2 build_controller_subcontainer
446
f97a8d0d 447=head2 build_default_model_service
448
449=head2 build_default_view_service
450
d057ddb9 451=head2 build_name_service
452
453=head2 build_driver_service
454
455=head2 build_file_service
456
457=head2 build_substitutions_service
458
459=head2 build_extensions_service
460
461=head2 build_prefix_service
462
463=head2 build_path_service
464
465=head2 build_config_service
466
467=head2 build_raw_config_service
468
469=head2 build_global_files_service
470
471=head2 build_local_files_service
472
473=head2 build_global_config_service
474
475=head2 build_local_config_service
476
477=head2 build_config_path_service
478
479=head2 build_config_local_suffix_service
480
481=head2 _fix_syntax
482
483=head2 _config_substitutions
484
bf3c8088 485=head1 AUTHORS
486
e8ed391e 487Catalyst Contributors, see Catalyst.pm
bf3c8088 488
e8ed391e 489=head1 COPYRIGHT
bf3c8088 490
491This library is free software. You can redistribute it and/or modify it under
492the same terms as Perl itself.
493
494=cut