Don't run the moose controller test if Moose isn't available
[catagits/Catalyst-Runtime.git] / lib / Catalyst / Utils.pm
CommitLineData
f05af9ba 1package Catalyst::Utils;
2
3use strict;
a2f2cde9 4use Catalyst::Exception;
37a3ac5c 5use File::Spec;
d837e1a7 6use HTTP::Request;
812a28c9 7use Path::Class;
d837e1a7 8use URI;
d9183506 9use Class::Inspector;
5e5bd6df 10use Carp qw/croak/;
19bcd236 11use Cwd;
f05af9ba 12
13=head1 NAME
14
15Catalyst::Utils - The Catalyst Utils
16
17=head1 SYNOPSIS
18
19See L<Catalyst>.
20
21=head1 DESCRIPTION
22
e73e3bad 23Catalyst Utilities.
24
f05af9ba 25=head1 METHODS
26
b5ecfcf0 27=head2 appprefix($class)
41ca9ba7 28
85d9fce6 29 MyApp::Foo becomes myapp_foo
41ca9ba7 30
31=cut
32
33sub appprefix {
34 my $class = shift;
0ef447d8 35 $class =~ s/::/_/g;
41ca9ba7 36 $class = lc($class);
37 return $class;
38}
39
b5ecfcf0 40=head2 class2appclass($class);
84cf74e7 41
0ef447d8 42 MyApp::Controller::Foo::Bar becomes MyApp
43 My::App::Controller::Foo::Bar becomes My::App
2d90477f 44
84cf74e7 45=cut
46
47sub class2appclass {
48 my $class = shift || '';
49 my $appname = '';
0ef447d8 50 if ( $class =~ /^(.+?)::([MVC]|Model|View|Controller)::.+$/ ) {
84cf74e7 51 $appname = $1;
52 }
53 return $appname;
54}
55
b5ecfcf0 56=head2 class2classprefix($class);
2930d610 57
0ef447d8 58 MyApp::Controller::Foo::Bar becomes MyApp::Controller
59 My::App::Controller::Foo::Bar becomes My::App::Controller
2d90477f 60
2930d610 61=cut
62
63sub class2classprefix {
64 my $class = shift || '';
65 my $prefix;
0ef447d8 66 if ( $class =~ /^(.+?::([MVC]|Model|View|Controller))::.+$/ ) {
2930d610 67 $prefix = $1;
68 }
69 return $prefix;
70}
71
b5ecfcf0 72=head2 class2classsuffix($class);
84cf74e7 73
0ef447d8 74 MyApp::Controller::Foo::Bar becomes Controller::Foo::Bar
2d90477f 75
84cf74e7 76=cut
77
78sub class2classsuffix {
79 my $class = shift || '';
80 my $prefix = class2appclass($class) || '';
0ef447d8 81 $class =~ s/$prefix\:://;
84cf74e7 82 return $class;
83}
84
b5ecfcf0 85=head2 class2env($class);
3ad654e0 86
26e73131 87Returns the environment name for class.
3ad654e0 88
89 MyApp becomes MYAPP
90 My::App becomes MY_APP
91
92=cut
93
94sub class2env {
95 my $class = shift || '';
0ef447d8 96 $class =~ s/::/_/g;
3ad654e0 97 return uc($class);
98}
99
b5ecfcf0 100=head2 class2prefix( $class, $case );
f05af9ba 101
e2cc89a9 102Returns the uri prefix for a class. If case is false the prefix is converted to lowercase.
f05af9ba 103
0ef447d8 104 My::App::Controller::Foo::Bar becomes foo/bar
2d90477f 105
f05af9ba 106=cut
107
108sub class2prefix {
109 my $class = shift || '';
e494bd6b 110 my $case = shift || 0;
f05af9ba 111 my $prefix;
0ef447d8 112 if ( $class =~ /^.+?::([MVC]|Model|View|Controller)::(.+)$/ ) {
e494bd6b 113 $prefix = $case ? $2 : lc $2;
0ef447d8 114 $prefix =~ s{::}{/}g;
f05af9ba 115 }
116 return $prefix;
117}
118
b5ecfcf0 119=head2 class2tempdir( $class [, $create ] );
37a3ac5c 120
e2cc89a9 121Returns a tempdir for a class. If create is true it will try to create the path.
37a3ac5c 122
123 My::App becomes /tmp/my/app
124 My::App::C::Foo::Bar becomes /tmp/my/app/c/foo/bar
125
126=cut
127
128sub class2tempdir {
129 my $class = shift || '';
130 my $create = shift || 0;
4be535b1 131 my @parts = split '::', lc $class;
37a3ac5c 132
133 my $tmpdir = dir( File::Spec->tmpdir, @parts )->cleanup;
134
4be535b1 135 if ( $create && !-e $tmpdir ) {
37a3ac5c 136
137 eval { $tmpdir->mkpath };
138
4be535b1 139 if ($@) {
37a3ac5c 140 Catalyst::Exception->throw(
4be535b1 141 message => qq/Couldn't create tmpdir '$tmpdir', "$@"/ );
37a3ac5c 142 }
143 }
144
145 return $tmpdir->stringify;
146}
147
b5ecfcf0 148=head2 home($class)
812a28c9 149
150Returns home directory for given class.
151
152=cut
153
154sub home {
51f412bd 155 my $class = shift;
156
157 # make an $INC{ $key } style string from the class name
158 (my $file = "$class.pm") =~ s{::}{/}g;
159
160 if ( my $inc_entry = $INC{$file} ) {
51452916 161 {
51f412bd 162 # look for an uninstalled Catalyst app
163
164 # find the @INC entry in which $file was found
165 (my $path = $inc_entry) =~ s/$file$//;
19bcd236 166 $path ||= cwd() if !defined $path || !length $path;
51f412bd 167 my $home = dir($path)->absolute->cleanup;
168
169 # pop off /lib and /blib if they're there
170 $home = $home->parent while $home =~ /b?lib$/;
171
172 # only return the dir if it has a Makefile.PL or Build.PL
c09c6cd7 173 if (-f $home->file("Makefile.PL") or -f $home->file("Build.PL")) {
174
175 # clean up relative path:
176 # MyApp/script/.. -> MyApp
177
e9902dae 178 my $dir;
179 my @dir_list = $home->dir_list();
180 while (($dir = pop(@dir_list)) && $dir eq '..') {
c09c6cd7 181 $home = dir($home)->parent->parent;
182 }
183
184 return $home->stringify;
185 }
51452916 186 }
4be535b1 187
51f412bd 188 {
189 # look for an installed Catalyst app
190
191 # trim the .pm off the thing ( Foo/Bar.pm -> Foo/Bar/ )
192 ( my $path = $inc_entry) =~ s/\.pm$//;
193 my $home = dir($path)->absolute->cleanup;
194
195 # return if if it's a valid directory
196 return $home->stringify if -d $home;
62459712 197 }
812a28c9 198 }
51f412bd 199
200 # we found nothing
201 return 0;
812a28c9 202}
203
b5ecfcf0 204=head2 prefix($class, $name);
812a28c9 205
206Returns a prefixed action.
207
0ef447d8 208 MyApp::Controller::Foo::Bar, yada becomes foo/bar/yada
812a28c9 209
210=cut
211
212sub prefix {
213 my ( $class, $name ) = @_;
214 my $prefix = &class2prefix($class);
215 $name = "$prefix/$name" if $prefix;
216 return $name;
217}
218
b5ecfcf0 219=head2 request($uri)
4d60aa90 220
e2cc89a9 221Returns an L<HTTP::Request> object for a uri.
4d60aa90 222
223=cut
224
225sub request {
226 my $request = shift;
227 unless ( ref $request ) {
a88c7ec8 228 if ( $request =~ m/^http/i ) {
f4c0f6f7 229 $request = URI->new($request);
4d60aa90 230 }
231 else {
f4c0f6f7 232 $request = URI->new( 'http://localhost' . $request );
4d60aa90 233 }
234 }
235 unless ( ref $request eq 'HTTP::Request' ) {
236 $request = HTTP::Request->new( 'GET', $request );
237 }
4d60aa90 238 return $request;
239}
240
dd91afb5 241=head2 ensure_class_loaded($class_name, \%opts)
d9183506 242
243Loads the class unless it already has been loaded.
244
dd91afb5 245If $opts{ignore_loaded} is true always tries the require whether the package
246already exists or not. Only pass this if you're either (a) sure you know the
247file exists on disk or (b) have code to catch the file not found exception
248that will result if it doesn't.
249
d9183506 250=cut
251
252sub ensure_class_loaded {
253 my $class = shift;
d06051f7 254 my $opts = shift;
d9183506 255
5e5bd6df 256 croak "Malformed class Name $class"
257 if $class =~ m/(?:\b\:\b|\:{3,})/;
258
59ede84e 259 croak "Malformed class Name $class"
260 if $class =~ m/[^\w:]/;
261
262 croak "ensure_class_loaded should be given a classname, not a filename ($class)"
263 if $class =~ m/\.pm$/;
264
d06051f7 265 return if !$opts->{ ignore_loaded }
266 && Class::Inspector->loaded( $class ); # if a symbol entry exists we don't load again
d9183506 267
268 # this hack is so we don't overwrite $@ if the load did not generate an error
269 my $error;
270 {
271 local $@;
7a1958eb 272 my $file = $class . '.pm';
273 $file =~ s{::}{/}g;
274 eval { CORE::require($file) };
d9183506 275 $error = $@;
276 }
6bfff75e 277
d9183506 278 die $error if $error;
6bfff75e 279 die "require $class was successful but the package is not defined"
280 unless Class::Inspector->loaded($class);
281
282 return 1;
d9183506 283}
284
358e1592 285=head2 merge_hashes($hashref, $hashref)
286
287Base code to recursively merge two hashes together with right-hand precedence.
288
289=cut
290
291sub merge_hashes {
292 my ( $lefthash, $righthash ) = @_;
293
294 return $lefthash unless defined $righthash;
295
296 my %merged = %$lefthash;
0ef447d8 297 for my $key ( keys %$righthash ) {
298 my $right_ref = ( ref $righthash->{ $key } || '' ) eq 'HASH';
299 my $left_ref = ( ( exists $lefthash->{ $key } && ref $lefthash->{ $key } ) || '' ) eq 'HASH';
300 if( $right_ref and $left_ref ) {
358e1592 301 $merged{ $key } = merge_hashes(
302 $lefthash->{ $key }, $righthash->{ $key }
0ef447d8 303 );
358e1592 304 }
305 else {
306 $merged{ $key } = $righthash->{ $key };
0ef447d8 307 }
358e1592 308 }
309
310 return \%merged;
311}
312
cb69249e 313=head2 env_value($class, $key)
314
315Checks for and returns an environment value. For instance, if $key is
316'home', then this method will check for and return the first value it finds,
317looking at $ENV{MYAPP_HOME} and $ENV{CATALYST_HOME}.
318
319=cut
320
321sub env_value {
322 my ( $class, $key ) = @_;
323
324 $key = uc($key);
325 my @prefixes = ( class2env($class), 'CATALYST' );
326
327 for my $prefix (@prefixes) {
328 if ( defined( my $value = $ENV{"${prefix}_${key}"} ) ) {
329 return $value;
330 }
331 }
332
333 return;
334}
d9183506 335
e73e3bad 336=head2 term_width
337
338Try to guess terminal width to use with formatting of debug output
339
340All you need to get this work, is:
341
3421) Install Term::Size::Any, or
343
3442) Export $COLUMNS from your shell.
345
346(Warning to bash users: 'echo $COLUMNS' may be showing you the bash
347variable, not $ENV{COLUMNS}. 'export COLUMNS=$COLUMNS' and you should see
348that 'env' now lists COLUMNS.)
349
350As last resort, default value of 80 chars will be used.
351
352=cut
353
354my $_term_width;
355
356sub term_width {
357 return $_term_width if $_term_width;
358
359 my $width = eval '
360 use Term::Size::Any;
361 my ($columns, $rows) = Term::Size::Any::chars;
362 return $columns;
363 ';
364
365 if ($@) {
366 $width = $ENV{COLUMNS}
367 if exists($ENV{COLUMNS})
368 && $ENV{COLUMNS} =~ m/^\d+$/;
369 }
370
371 $width = 80 unless ($width && $width >= 80);
372 return $_term_width = $width;
373}
374
0bf7ab71 375=head1 AUTHORS
f05af9ba 376
0bf7ab71 377Catalyst Contributors, see Catalyst.pm
f05af9ba 378
379=head1 COPYRIGHT
380
381This program is free software, you can redistribute it and/or modify it under
382the same terms as Perl itself.
383
384=cut
385
3861;