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