created attribute for the container class that will be used for subcontainers
[catagits/Catalyst-Runtime.git] / lib / Catalyst / Container.pm
CommitLineData
b4a6fa62 1package Catalyst::Container;
2use Bread::Board;
3use Moose;
4use Config::Any;
5use Data::Visitor::Callback;
6use Catalyst::Utils ();
2bb0da6d 7use MooseX::Types::LoadableClass qw/ LoadableClass /;
b4a6fa62 8
9extends 'Bread::Board::Container';
10
11has config_local_suffix => (
12 is => 'rw',
13 isa => 'Str',
14 default => 'local',
15);
16
17has driver => (
18 is => 'rw',
19 isa => 'HashRef',
20 default => sub { +{} },
21);
22
23has file => (
24 is => 'rw',
25 isa => 'Str',
26 default => '',
27);
28
29has substitutions => (
30 is => 'rw',
31 isa => 'HashRef',
32 default => sub { +{} },
33);
34
35has name => (
36 is => 'rw',
37 isa => 'Str',
38 default => 'TestApp',
39);
40
2bb0da6d 41has sub_container_class => (
42 isa => LoadableClass,
43 is => 'ro',
44 coerce => 1,
45 default => 'Bread::Board::Container',
46);
47
b4a6fa62 48sub BUILD {
49 my $self = shift;
50
f04816ce 51 $self->build_root_container;
52
53 $self->build_model_subcontainer;
54 $self->build_view_subcontainer;
55 $self->build_controller_subcontainer;
56}
57
58sub build_model_subcontainer {
59 my $self = shift;
60
2bb0da6d 61 $self->add_sub_container(
62 $self->sub_container_class->new( name => 'model' )
63 );
f04816ce 64}
65
66sub build_view_subcontainer {
67 my $self = shift;
68
2bb0da6d 69 $self->add_sub_container(
70 $self->sub_container_class->new( name => 'view' )
71 );
f04816ce 72}
73
74sub build_controller_subcontainer {
75 my $self = shift;
76
2bb0da6d 77 $self->add_sub_container(
78 $self->sub_container_class->new( name => 'controller' )
79 );
f04816ce 80}
81
82sub build_root_container {
83 my $self = shift;
84
85 $self->build_substitutions_service();
86 $self->build_file_service();
87 $self->build_driver_service();
88 $self->build_name_service();
89 $self->build_prefix_service();
90 $self->build_extensions_service();
91 $self->build_path_service();
92 $self->build_config_service();
93 $self->build_raw_config_service();
94 $self->build_global_files_service();
95 $self->build_local_files_service();
96 $self->build_global_config_service();
97 $self->build_local_config_service();
98 $self->build_config_local_suffix_service();
99 $self->build_config_path_service();
100}
101
102sub build_name_service {
103 my $self = shift;
104 $self->add_service(
105 Bread::Board::Literal->new( name => 'name', value => $self->name )
106 );
107}
108
109sub build_driver_service {
110 my $self = shift;
111 $self->add_service(
112 Bread::Board::Literal->new( name => 'driver', value => $self->driver )
113 );
114}
115
116sub build_file_service {
117 my $self = shift;
118 $self->add_service(
119 Bread::Board::Literal->new( name => 'file', value => $self->file )
120 );
121}
122
123sub build_substitutions_service {
124 my $self = shift;
125 $self->add_service(
126 Bread::Board::Literal->new( name => 'substitutions', value => $self->substitutions )
127 );
128}
129
130sub build_extensions_service {
131 my $self = shift;
132 $self->add_service(
133 Bread::Board::BlockInjection->new(
134 name => 'extensions',
b4a6fa62 135 block => sub {
136 return \@{Config::Any->extensions};
137 },
f04816ce 138 )
139 );
140}
b4a6fa62 141
f04816ce 142sub build_prefix_service {
143 my $self = shift;
144 $self->add_service(
145 Bread::Board::BlockInjection->new(
146 name => 'prefix',
b4a6fa62 147 block => sub {
148 return Catalyst::Utils::appprefix( shift->param('name') );
149 },
150 dependencies => [ depends_on('name') ],
f04816ce 151 )
152 );
153}
b4a6fa62 154
f04816ce 155sub build_path_service {
156 my $self = shift;
157 $self->add_service(
158 Bread::Board::BlockInjection->new(
159 name => 'path',
b4a6fa62 160 block => sub {
161 my $s = shift;
162
163 return Catalyst::Utils::env_value( $s->param('name'), 'CONFIG' )
164 || $s->param('file')
165 || $s->param('name')->path_to( $s->param('prefix') );
166 },
167 dependencies => [ depends_on('file'), depends_on('name'), depends_on('prefix') ],
f04816ce 168 )
169 );
170}
b4a6fa62 171
f04816ce 172sub build_config_service {
173 my $self = shift;
174 $self->add_service(
175 Bread::Board::BlockInjection->new(
176 name => 'config',
b4a6fa62 177 block => sub {
178 my $s = shift;
179
180 my $v = Data::Visitor::Callback->new(
181 plain_value => sub {
182 return unless defined $_;
183 return $self->_config_substitutions( $s->param('name'), $s->param('substitutions'), $_ );
184 }
185
186 );
187 $v->visit( $s->param('raw_config') );
188 },
189 dependencies => [ depends_on('name'), depends_on('raw_config'), depends_on('substitutions') ],
f04816ce 190 )
191 );
192}
b4a6fa62 193
f04816ce 194sub build_raw_config_service {
195 my $self = shift;
196 $self->add_service(
197 Bread::Board::BlockInjection->new(
198 name => 'raw_config',
b4a6fa62 199 block => sub {
200 my $s = shift;
201
202 my @global = @{$s->param('global_config')};
203 my @locals = @{$s->param('local_config')};
204
205 my $config = {};
206 for my $cfg (@global, @locals) {
207 for (keys %$cfg) {
208 $config = Catalyst::Utils::merge_hashes( $config, $cfg->{$_} );
209 }
210 }
211 return $config;
212 },
213 dependencies => [ depends_on('global_config'), depends_on('local_config') ],
f04816ce 214 )
215 );
216}
b4a6fa62 217
f04816ce 218sub build_global_files_service {
219 my $self = shift;
220 $self->add_service(
221 Bread::Board::BlockInjection->new(
222 name => 'global_files',
b4a6fa62 223 block => sub {
224 my $s = shift;
225
226 my ( $path, $extension ) = @{$s->param('config_path')};
227
228 my @extensions = @{$s->param('extensions')};
229
230 my @files;
231 if ( $extension ) {
232 die "Unable to handle files with the extension '${extension}'" unless grep { $_ eq $extension } @extensions;
233 push @files, $path;
234 } else {
235 @files = map { "$path.$_" } @extensions;
236 }
237 return \@files;
238 },
239 dependencies => [ depends_on('extensions'), depends_on('config_path') ],
f04816ce 240 )
241 );
242}
b4a6fa62 243
f04816ce 244sub build_local_files_service {
245 my $self = shift;
246 $self->add_service(
247 Bread::Board::BlockInjection->new(
248 name => 'local_files',
b4a6fa62 249 block => sub {
250 my $s = shift;
251
252 my ( $path, $extension ) = @{$s->param('config_path')};
253 my $suffix = $s->param('config_local_suffix');
254
255 my @extensions = @{$s->param('extensions')};
256
257 my @files;
258 if ( $extension ) {
259 die "Unable to handle files with the extension '${extension}'" unless grep { $_ eq $extension } @extensions;
260 $path =~ s{\.$extension}{_$suffix.$extension};
261 push @files, $path;
262 } else {
263 @files = map { "${path}_${suffix}.$_" } @extensions;
264 }
265 return \@files;
266 },
267 dependencies => [ depends_on('extensions'), depends_on('config_path'), depends_on('config_local_suffix') ],
f04816ce 268 )
269 );
270}
b4a6fa62 271
f04816ce 272sub build_global_config_service {
273 my $self = shift;
274 $self->add_service(
275 Bread::Board::BlockInjection->new(
276 name => 'global_config',
b4a6fa62 277 block => sub {
278 my $s = shift;
9ea11efb 279
f228c263 280 return Config::Any->load_files({
281 files => $s->param('global_files'),
b4a6fa62 282 filter => \&_fix_syntax,
283 use_ext => 1,
284 driver_args => $s->param('driver'),
285 });
b4a6fa62 286 },
287 dependencies => [ depends_on('global_files') ],
f04816ce 288 )
289 );
290}
b4a6fa62 291
f04816ce 292sub build_local_config_service {
293 my $self = shift;
294 $self->add_service(
295 Bread::Board::BlockInjection->new(
296 name => 'local_config',
b4a6fa62 297 block => sub {
298 my $s = shift;
299
f228c263 300 return Config::Any->load_files({
301 files => $s->param('local_files'),
b4a6fa62 302 filter => \&_fix_syntax,
303 use_ext => 1,
304 driver_args => $s->param('driver'),
305 });
b4a6fa62 306 },
307 dependencies => [ depends_on('local_files') ],
f04816ce 308 )
309 );
310}
b4a6fa62 311
f04816ce 312sub build_config_path_service {
313 my $self = shift;
314 $self->add_service(
315 Bread::Board::BlockInjection->new(
316 name => 'config_path',
b4a6fa62 317 block => sub {
318 my $s = shift;
319
320 my $path = $s->param('path');
321 my $prefix = $s->param('prefix');
322
323 my ( $extension ) = ( $path =~ m{\.(.{1,4})$} );
324
325 if ( -d $path ) {
326 $path =~ s{[\/\\]$}{};
327 $path .= "/$prefix";
328 }
329
330 return [ $path, $extension ];
331 },
332 dependencies => [ depends_on('prefix'), depends_on('path') ],
f04816ce 333 )
334 );
335}
b4a6fa62 336
f04816ce 337sub build_config_local_suffix_service {
338 my $self = shift;
339 $self->add_service(
340 Bread::Board::BlockInjection->new(
341 name => 'config_local_suffix',
b4a6fa62 342 block => sub {
343 my $s = shift;
344 my $suffix = Catalyst::Utils::env_value( $s->param('name'), 'CONFIG_LOCAL_SUFFIX' ) || $self->config_local_suffix;
345
346 return $suffix;
347 },
348 dependencies => [ depends_on('name') ],
f04816ce 349 )
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
3951;