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