tabs => spaces
[catagits/Catalyst-Runtime.git] / lib / Catalyst / Utils.pm
1 package Catalyst::Utils;
2
3 use strict;
4 use Catalyst::Exception;
5 use File::Spec;
6 use HTTP::Request;
7 use Path::Class;
8 use URI;
9 use Class::Inspector;
10 use Carp qw/croak/;
11
12 =head1 NAME
13
14 Catalyst::Utils - The Catalyst Utils
15
16 =head1 SYNOPSIS
17
18 See L<Catalyst>.
19
20 =head1 DESCRIPTION
21
22 =head1 METHODS
23
24 =head2 appprefix($class)
25
26     MyApp::Foo becomes myapp_foo
27
28 =cut
29
30 sub appprefix {
31     my $class = shift;
32     $class =~ s/::/_/g;
33     $class = lc($class);
34     return $class;
35 }
36
37 =head2 class2appclass($class);
38
39     MyApp::Controller::Foo::Bar becomes MyApp
40     My::App::Controller::Foo::Bar becomes My::App
41
42 =cut
43
44 sub class2appclass {
45     my $class = shift || '';
46     my $appname = '';
47     if ( $class =~ /^(.+?)::([MVC]|Model|View|Controller)::.+$/ ) {
48         $appname = $1;
49     }
50     return $appname;
51 }
52
53 =head2 class2classprefix($class);
54
55     MyApp::Controller::Foo::Bar becomes MyApp::Controller
56     My::App::Controller::Foo::Bar becomes My::App::Controller
57
58 =cut
59
60 sub class2classprefix {
61     my $class = shift || '';
62     my $prefix;
63     if ( $class =~ /^(.+?::([MVC]|Model|View|Controller))::.+$/ ) {
64         $prefix = $1;
65     }
66     return $prefix;
67 }
68
69 =head2 class2classsuffix($class);
70
71     MyApp::Controller::Foo::Bar becomes Controller::Foo::Bar
72
73 =cut
74
75 sub class2classsuffix {
76     my $class = shift || '';
77     my $prefix = class2appclass($class) || '';
78     $class =~ s/$prefix\:://;
79     return $class;
80 }
81
82 =head2 class2env($class);
83
84 Returns the environment name for class.
85
86     MyApp becomes MYAPP
87     My::App becomes MY_APP
88
89 =cut
90
91 sub class2env {
92     my $class = shift || '';
93     $class =~ s/::/_/g;
94     return uc($class);
95 }
96
97 =head2 class2prefix( $class, $case );
98
99 Returns the uri prefix for a class. If case is false the prefix is converted to lowercase.
100
101     My::App::Controller::Foo::Bar becomes foo/bar
102
103 =cut
104
105 sub class2prefix {
106     my $class = shift || '';
107     my $case  = shift || 0;
108     my $prefix;
109     if ( $class =~ /^.+?::([MVC]|Model|View|Controller)::(.+)$/ ) {
110         $prefix = $case ? $2 : lc $2;
111         $prefix =~ s{::}{/}g;
112     }
113     return $prefix;
114 }
115
116 =head2 class2tempdir( $class [, $create ] );
117
118 Returns a tempdir for a class. If create is true it will try to create the path.
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
125 sub class2tempdir {
126     my $class  = shift || '';
127     my $create = shift || 0;
128     my @parts = split '::', lc $class;
129
130     my $tmpdir = dir( File::Spec->tmpdir, @parts )->cleanup;
131
132     if ( $create && !-e $tmpdir ) {
133
134         eval { $tmpdir->mkpath };
135
136         if ($@) {
137             Catalyst::Exception->throw(
138                 message => qq/Couldn't create tmpdir '$tmpdir', "$@"/ );
139         }
140     }
141
142     return $tmpdir->stringify;
143 }
144
145 =head2 home($class)
146
147 Returns home directory for given class.
148
149 =cut
150
151 sub home {
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} ) {
158         {
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$//;
163             my $home = dir($path)->absolute->cleanup;
164
165             # pop off /lib and /blib if they're there
166             $home = $home->parent while $home =~ /b?lib$/;
167
168             # only return the dir if it has a Makefile.PL or Build.PL
169             if (-f $home->file("Makefile.PL") or -f $home->file("Build.PL")) {
170
171                 # clean up relative path:
172                 # MyApp/script/.. -> MyApp
173
174                 my ($lastdir) = $home->dir_list( -1, 1 );
175                 if ( $lastdir eq '..' ) {
176                     $home = dir($home)->parent->parent;
177                 }
178
179                 return $home->stringify;
180             }
181         }
182
183         {
184             # look for an installed Catalyst app
185
186             # trim the .pm off the thing ( Foo/Bar.pm -> Foo/Bar/ )
187             ( my $path = $inc_entry) =~ s/\.pm$//;
188             my $home = dir($path)->absolute->cleanup;
189
190             # return if if it's a valid directory
191             return $home->stringify if -d $home;
192         }
193     }
194
195     # we found nothing
196     return 0;
197 }
198
199 =head2 prefix($class, $name);
200
201 Returns a prefixed action.
202
203     MyApp::Controller::Foo::Bar, yada becomes foo/bar/yada
204
205 =cut
206
207 sub prefix {
208     my ( $class, $name ) = @_;
209     my $prefix = &class2prefix($class);
210     $name = "$prefix/$name" if $prefix;
211     return $name;
212 }
213
214 =head2 request($uri)
215
216 Returns an L<HTTP::Request> object for a uri.
217
218 =cut
219
220 sub request {
221     my $request = shift;
222     unless ( ref $request ) {
223         if ( $request =~ m/^http/i ) {
224             $request = URI->new($request);
225         }
226         else {
227             $request = URI->new( 'http://localhost' . $request );
228         }
229     }
230     unless ( ref $request eq 'HTTP::Request' ) {
231         $request = HTTP::Request->new( 'GET', $request );
232     }
233     return $request;
234 }
235
236 =head2 ensure_class_loaded($class_name)
237
238 Loads the class unless it already has been loaded.
239
240 =cut
241
242 sub ensure_class_loaded {
243     my $class = shift;
244     my $opts  = shift;
245
246     croak "Malformed class Name $class"
247         if $class =~ m/(?:\b\:\b|\:{3,})/;
248
249     return if !$opts->{ ignore_loaded }
250         && Class::Inspector->loaded( $class ); # if a symbol entry exists we don't load again
251
252     # this hack is so we don't overwrite $@ if the load did not generate an error
253     my $error;
254     {
255         local $@;
256         eval "require $class";
257         $error = $@;
258     }
259
260     die $error if $error;
261     die "require $class was successful but the package is not defined"
262         unless Class::Inspector->loaded($class);
263
264     return 1;
265 }
266
267 =head2 merge_hashes($hashref, $hashref)
268
269 Base code to recursively merge two hashes together with right-hand precedence.
270
271 =cut
272
273 sub merge_hashes {
274     my ( $lefthash, $righthash ) = @_;
275
276     return $lefthash unless defined $righthash;
277     
278     my %merged = %$lefthash;
279     for my $key ( keys %$righthash ) {
280         my $right_ref = ( ref $righthash->{ $key } || '' ) eq 'HASH';
281         my $left_ref  = ( ( exists $lefthash->{ $key } && ref $lefthash->{ $key } ) || '' ) eq 'HASH';
282         if( $right_ref and $left_ref ) {
283             $merged{ $key } = merge_hashes(
284                 $lefthash->{ $key }, $righthash->{ $key }
285             );
286         }
287         else {
288             $merged{ $key } = $righthash->{ $key };
289         }
290     }
291     
292     return \%merged;
293 }
294
295 =head2 env_value($class, $key)
296
297 Checks for and returns an environment value. For instance, if $key is
298 'home', then this method will check for and return the first value it finds,
299 looking at $ENV{MYAPP_HOME} and $ENV{CATALYST_HOME}.
300
301 =cut
302
303 sub env_value {
304     my ( $class, $key ) = @_;
305
306     $key = uc($key);
307     my @prefixes = ( class2env($class), 'CATALYST' );
308
309     for my $prefix (@prefixes) {
310         if ( defined( my $value = $ENV{"${prefix}_${key}"} ) ) {
311             return $value;
312         }
313     }
314
315     return;
316 }
317
318 =head1 AUTHOR
319
320 Sebastian Riedel, C<sri@cpan.org>
321 Yuval Kogman, C<nothingmuch@woobling.org>
322
323 =head1 COPYRIGHT
324
325 This program is free software, you can redistribute it and/or modify it under
326 the same terms as Perl itself.
327
328 =cut
329
330 1;