1 package Catalyst::Utils;
9 use FindBin qw/ $Bin /;
11 use String::RewritePrefix;
12 use List::MoreUtils qw/ any /;
18 Catalyst::Utils - The Catalyst Utils
30 =head2 appprefix($class)
32 MyApp::Foo becomes myapp_foo
43 =head2 class2appclass($class);
45 MyApp::Controller::Foo::Bar becomes MyApp
46 My::App::Controller::Foo::Bar becomes My::App
51 my $class = shift || '';
53 if ( $class =~ /^(.+?)::([MVC]|Model|View|Controller)::.+$/ ) {
59 =head2 class2classprefix($class);
61 MyApp::Controller::Foo::Bar becomes MyApp::Controller
62 My::App::Controller::Foo::Bar becomes My::App::Controller
66 sub class2classprefix {
67 my $class = shift || '';
69 if ( $class =~ /^(.+?::([MVC]|Model|View|Controller))::.+$/ ) {
75 =head2 class2classsuffix($class);
77 MyApp::Controller::Foo::Bar becomes Controller::Foo::Bar
81 sub class2classsuffix {
82 my $class = shift || '';
83 my $prefix = class2appclass($class) || '';
84 $class =~ s/$prefix\:://;
88 =head2 class2classshortsuffix($class)
90 MyApp::Controller::Foo::Bar becomes Foo::Bar
94 sub class2classshortsuffix {
95 my $class = shift || '';
96 my $prefix = class2classprefix($class) || '';
97 $class =~ s/$prefix\:://;
102 =head2 class2env($class);
104 Returns the environment name for class.
107 My::App becomes MY_APP
112 my $class = shift || '';
117 =head2 class2prefix( $class, $case );
119 Returns the uri prefix for a class. If case is false the prefix is converted to lowercase.
121 My::App::Controller::Foo::Bar becomes foo/bar
126 my $class = shift || '';
127 my $case = shift || 0;
129 if ( $class =~ /^.+?::([MVC]|Model|View|Controller)::(.+)$/ ) {
130 $prefix = $case ? $2 : lc $2;
131 $prefix =~ s{::}{/}g;
136 =head2 class2tempdir( $class [, $create ] );
138 Returns a tempdir for a class. If create is true it will try to create the path.
140 My::App becomes /tmp/my/app
141 My::App::Controller::Foo::Bar becomes /tmp/my/app/c/foo/bar
146 my $class = shift || '';
147 my $create = shift || 0;
148 my @parts = split '::', lc $class;
150 my $tmpdir = dir( File::Spec->tmpdir, @parts )->cleanup;
152 if ( $create && !-e $tmpdir ) {
154 eval { $tmpdir->mkpath };
157 # don't load Catalyst::Exception as a BEGIN in Utils,
158 # because Utils often gets loaded before MyApp.pm, and if
159 # Catalyst::Exception is loaded before MyApp.pm, it does
161 # $Catalyst::Exception::CATALYST_EXCEPTION_CLASS in
163 require Catalyst::Exception;
164 Catalyst::Exception->throw(
165 message => qq/Couldn't create tmpdir '$tmpdir', "$@"/ );
169 return $tmpdir->stringify;
172 =head2 dist_indicator_file_list
174 Returns a list of files which can be tested to check if you're inside a checkout
178 sub dist_indicator_file_list {
179 qw/ Makefile.PL Build.PL dist.ini /;
184 Returns home directory for given class.
186 Note that the class must be loaded for the home directory to be found using this function.
193 # make an $INC{ $key } style string from the class name
194 (my $file = "$class.pm") =~ s{::}{/}g;
196 if ( my $inc_entry = $INC{$file} ) {
198 # look for an uninstalled Catalyst app
200 # find the @INC entry in which $file was found
201 (my $path = $inc_entry) =~ s/$file$//;
202 my $home = find_home_unloaded_in_checkout($path);
203 return $home if $home;
207 # look for an installed Catalyst app
209 # trim the .pm off the thing ( Foo/Bar.pm -> Foo/Bar/ )
210 ( my $path = $inc_entry) =~ s/\.pm$//;
211 my $home = dir($path)->absolute->cleanup;
213 # return if if it's a valid directory
214 return $home->stringify if -d $home;
222 =head2 find_home_unloaded_in_checkout ($path)
224 Tries to determine if C<$path> (or $FindBin::Bin if not supplied)
225 looks like a checkout. Any leading lib, script or blib components
226 will be removed, then the directory produced will be checked
227 for the existence of a C<< dist_indicator_file_list() >>.
229 If one is found, the directory will be returned, otherwise false.
233 sub find_home_unloaded_in_checkout {
235 $path ||= $Bin if !defined $path || !length $path;
236 my $home = dir($path)->absolute->cleanup;
238 # pop off /lib and /blib if they're there
239 $home = $home->parent while $home =~ /b?lib$/;
240 # pop off /script if it's there.
241 $home = $home->parent while $home =~ /b?script$/;
243 # only return the dir if it has a Makefile.PL or Build.PL or dist.ini
244 if (any { $_ } map { -f $home->file($_) } dist_indicator_file_list()) {
246 # clean up relative path:
247 # MyApp/script/.. -> MyApp
250 my @dir_list = $home->dir_list();
251 while (($dir = pop(@dir_list)) && $dir eq '..') {
252 $home = dir($home)->parent->parent;
255 return $home->stringify;
260 =head2 prefix($class, $name);
262 Returns a prefixed action.
264 MyApp::Controller::Foo::Bar, yada becomes foo/bar/yada
269 my ( $class, $name ) = @_;
270 my $prefix = &class2prefix($class);
271 $name = "$prefix/$name" if $prefix;
277 Returns an L<HTTP::Request> object for a uri.
283 unless ( ref $request ) {
284 if ( $request =~ m/^http/i ) {
285 $request = URI->new($request);
288 $request = URI->new( 'http://localhost' . $request );
291 unless ( ref $request eq 'HTTP::Request' ) {
292 $request = HTTP::Request->new( 'GET', $request );
297 =head2 ensure_class_loaded($class_name, \%opts)
299 Loads the class unless it already has been loaded.
301 If $opts{ignore_loaded} is true always tries the require whether the package
302 already exists or not. Only pass this if you're either (a) sure you know the
303 file exists on disk or (b) have code to catch the file not found exception
304 that will result if it doesn't.
308 sub ensure_class_loaded {
312 croak "Malformed class Name $class"
313 if $class =~ m/(?:\b\:\b|\:{3,})/;
315 croak "Malformed class Name $class"
316 if $class =~ m/[^\w:]/;
318 croak "ensure_class_loaded should be given a classname, not a filename ($class)"
319 if $class =~ m/\.pm$/;
321 # $opts->{ignore_loaded} can be set to true, and this causes the class to be required, even
322 # if it already has symbol table entries. This is to support things like Schema::Loader, which
323 # part-generate classes in memory, but then also load some of their contents from disk.
324 return if !$opts->{ ignore_loaded }
325 && Class::MOP::is_class_loaded($class); # if a symbol entry exists we don't load again
327 # this hack is so we don't overwrite $@ if the load did not generate an error
331 my $file = $class . '.pm';
333 eval { CORE::require($file) };
337 die $error if $error;
339 warn "require $class was successful but the package is not defined."
340 unless Class::MOP::is_class_loaded($class);
345 =head2 merge_hashes($hashref, $hashref)
347 Base code to recursively merge two hashes together with right-hand precedence.
352 my ( $lefthash, $righthash ) = @_;
354 return $lefthash unless defined $righthash;
356 my %merged = %$lefthash;
357 for my $key ( keys %$righthash ) {
358 my $right_ref = ( ref $righthash->{ $key } || '' ) eq 'HASH';
359 my $left_ref = ( ( exists $lefthash->{ $key } && ref $lefthash->{ $key } ) || '' ) eq 'HASH';
360 if( $right_ref and $left_ref ) {
361 $merged{ $key } = merge_hashes(
362 $lefthash->{ $key }, $righthash->{ $key }
366 $merged{ $key } = $righthash->{ $key };
373 =head2 env_value($class, $key)
375 Checks for and returns an environment value. For instance, if $key is
376 'home', then this method will check for and return the first value it finds,
377 looking at $ENV{MYAPP_HOME} and $ENV{CATALYST_HOME}.
382 my ( $class, $key ) = @_;
385 my @prefixes = ( class2env($class), 'CATALYST' );
387 for my $prefix (@prefixes) {
388 if ( defined( my $value = $ENV{"${prefix}_${key}"} ) ) {
398 Try to guess terminal width to use with formatting of debug output
400 All you need to get this work, is:
402 1) Install Term::Size::Any, or
404 2) Export $COLUMNS from your shell.
406 (Warning to bash users: 'echo $COLUMNS' may be showing you the bash
407 variable, not $ENV{COLUMNS}. 'export COLUMNS=$COLUMNS' and you should see
408 that 'env' now lists COLUMNS.)
410 As last resort, default value of 80 chars will be used.
417 return $_term_width if $_term_width;
421 my ($columns, $rows) = Term::Size::Any::chars;
426 $width = $ENV{COLUMNS}
427 if exists($ENV{COLUMNS})
428 && $ENV{COLUMNS} =~ m/^\d+$/;
431 $width = 80 unless ($width && $width >= 80);
432 return $_term_width = $width;
436 =head2 resolve_namespace
438 Method which adds the namespace for plugins and actions.
440 __PACKAGE__->setup(qw(MyPlugin));
442 # will load Catalyst::Plugin::MyPlugin
447 sub resolve_namespace {
448 my $appnamespace = shift;
449 my $namespace = shift;
451 return String::RewritePrefix->rewrite({
452 q[] => qq[${namespace}::],
454 (defined $appnamespace
455 ? (q[~] => qq[${appnamespace}::])
464 Catalyst Contributors, see Catalyst.pm
468 This library is free software. You can redistribute it and/or modify it under
469 the same terms as Perl itself.