compatibility for the 'components' accessor, made controllers/views/models subs get...
[catagits/Catalyst-Runtime.git] / lib / Catalyst / Container.pm
1 package Catalyst::Container;
2 use Bread::Board;
3 use Moose;
4 use Config::Any;
5 use Data::Visitor::Callback;
6 use Catalyst::Utils ();
7
8 extends 'Bread::Board::Container';
9
10 has config_local_suffix => (
11     is      => 'rw',
12     isa     => 'Str',
13     default => 'local',
14 );
15
16 has driver => (
17     is      => 'rw',
18     isa     => 'HashRef',
19     default => sub { +{} },
20 );
21
22 has file => (
23     is      => 'rw',
24     isa     => 'Str',
25     default => '',
26 );
27
28 has substitutions => (
29     is      => 'rw',
30     isa     => 'HashRef',
31     default => sub { +{} },
32 );
33
34 has name => (
35     is      => 'rw',
36     isa     => 'Str',
37     default => 'TestApp',
38 );
39
40 sub BUILD {
41     my $self = shift;
42
43     container $self => as {
44         service name => $self->name;
45         service driver => $self->driver;
46         service file => $self->file;
47         service substitutions => $self->substitutions;
48
49         service extensions => (
50             block => sub {
51                 return \@{Config::Any->extensions};
52             },
53         );
54
55         service prefix => (
56             block => sub {
57                 return Catalyst::Utils::appprefix( shift->param('name') );
58             },
59             dependencies => [ depends_on('name') ],
60          );
61
62         service path => (
63             block => sub {
64                 my $s = shift;
65
66                 return Catalyst::Utils::env_value( $s->param('name'), 'CONFIG' )
67                 || $s->param('file')
68                 || $s->param('name')->path_to( $s->param('prefix') );
69             },
70             dependencies => [ depends_on('file'), depends_on('name'), depends_on('prefix') ],
71         );
72
73         service config => (
74             block => sub {
75                 my $s = shift;
76
77                 my $v = Data::Visitor::Callback->new(
78                     plain_value => sub {
79                         return unless defined $_;
80                         return $self->_config_substitutions( $s->param('name'), $s->param('substitutions'), $_ );
81                     }
82
83                 );
84                 $v->visit( $s->param('raw_config') );
85             },
86             dependencies => [ depends_on('name'), depends_on('raw_config'), depends_on('substitutions') ],
87         );
88
89         service raw_config => (
90             block => sub {
91                 my $s = shift;
92
93                 my @global = @{$s->param('global_config')};
94                 my @locals = @{$s->param('local_config')};
95
96                 my $config = {};
97                 for my $cfg (@global, @locals) {
98                     for (keys %$cfg) {
99                         $config = Catalyst::Utils::merge_hashes( $config, $cfg->{$_} );
100                     }
101                 }
102                 return $config;
103             },
104             dependencies => [ depends_on('global_config'), depends_on('local_config') ],
105         );
106
107         service global_files => (
108             block => sub {
109                 my $s = shift;
110
111                 my ( $path, $extension ) = @{$s->param('config_path')};
112
113                 my @extensions = @{$s->param('extensions')};
114
115                 my @files;
116                 if ( $extension ) {
117                     die "Unable to handle files with the extension '${extension}'" unless grep { $_ eq $extension } @extensions;
118                     push @files, $path;
119                 } else {
120                     @files = map { "$path.$_" } @extensions;
121                 }
122                 return \@files;
123             },
124             dependencies => [ depends_on('extensions'), depends_on('config_path') ],
125         );
126
127         service local_files => (
128             block => sub {
129                 my $s = shift;
130
131                 my ( $path, $extension ) = @{$s->param('config_path')};
132                 my $suffix = $s->param('config_local_suffix');
133
134                 my @extensions = @{$s->param('extensions')};
135
136                 my @files;
137                 if ( $extension ) {
138                     die "Unable to handle files with the extension '${extension}'" unless grep { $_ eq $extension } @extensions;
139                     $path =~ s{\.$extension}{_$suffix.$extension};
140                     push @files, $path;
141                 } else {
142                     @files = map { "${path}_${suffix}.$_" } @extensions;
143                 }
144                 return \@files;
145             },
146             dependencies => [ depends_on('extensions'), depends_on('config_path'), depends_on('config_local_suffix') ],
147         );
148
149         service global_config => (
150             block => sub {
151                 my $s = shift;
152  
153                 return Config::Any->load_files({
154                     files       => $s->param('global_files'),
155                     filter      => \&_fix_syntax,
156                     use_ext     => 1,
157                     driver_args => $s->param('driver'),
158                 });
159             },
160             dependencies => [ depends_on('global_files') ],
161         );
162
163         service local_config => (
164             block => sub {
165                 my $s = shift;
166
167                 return Config::Any->load_files({
168                     files       => $s->param('local_files'),
169                     filter      => \&_fix_syntax,
170                     use_ext     => 1,
171                     driver_args => $s->param('driver'),
172                 });
173             },
174             dependencies => [ depends_on('local_files') ],
175         );
176
177         service config_path => (
178             block => sub {
179                 my $s = shift;
180
181                 my $path = $s->param('path');
182                 my $prefix = $s->param('prefix');
183
184                 my ( $extension ) = ( $path =~ m{\.(.{1,4})$} );
185
186                 if ( -d $path ) {
187                     $path =~ s{[\/\\]$}{};
188                     $path .= "/$prefix";
189                 }
190
191                 return [ $path, $extension ];
192             },
193             dependencies => [ depends_on('prefix'), depends_on('path') ],
194         );
195
196         service config_local_suffix => (
197             block => sub {
198                 my $s = shift;
199                 my $suffix = Catalyst::Utils::env_value( $s->param('name'), 'CONFIG_LOCAL_SUFFIX' ) || $self->config_local_suffix;
200
201                 return $suffix;
202             },
203             dependencies => [ depends_on('name') ],
204         );
205
206     };
207 }
208
209 sub _fix_syntax {
210     my $config     = shift;
211     my @components = (
212         map +{
213             prefix => $_ eq 'Component' ? '' : $_ . '::',
214             values => delete $config->{ lc $_ } || delete $config->{ $_ }
215         },
216         grep { ref $config->{ lc $_ } || ref $config->{ $_ } }
217             qw( Component Model M View V Controller C Plugin )
218     );
219
220     foreach my $comp ( @components ) {
221         my $prefix = $comp->{ prefix };
222         foreach my $element ( keys %{ $comp->{ values } } ) {
223             $config->{ "$prefix$element" } = $comp->{ values }->{ $element };
224         }
225     }
226 }
227
228 sub _config_substitutions {
229     my ($self, $name, $subs) = @_;
230
231     $subs->{ HOME } ||= sub { shift->path_to( '' ); };
232     $subs->{ ENV } ||=
233         sub {
234             my ( $c, $v ) = @_;
235             if (! defined($ENV{$v})) {
236                 Catalyst::Exception->throw( message =>
237                     "Missing environment variable: $v" );
238                 return "";
239             } else {
240                 return $ENV{ $v };
241             }
242         };
243     $subs->{ path_to } ||= sub { shift->path_to( @_ ); };
244     $subs->{ literal } ||= sub { return $_[ 1 ]; };
245     my $subsre = join( '|', keys %$subs );
246
247     for ( @_ ) {
248         my $arg = $_;
249         $arg =~ s{__($subsre)(?:\((.+?)\))?__}{ $subs->{ $1 }->( $name, $2 ? split( /,/, $2 ) : () ) }eg;
250         return $arg;
251     }
252 }
253
254 1;