1 package Catalyst::Utils;
10 use String::RewritePrefix;
11 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 class2env($class);
90 Returns the environment name for class.
93 My::App becomes MY_APP
98 my $class = shift || '';
103 =head2 class2prefix( $class, $case );
105 Returns the uri prefix for a class. If case is false the prefix is converted to lowercase.
107 My::App::Controller::Foo::Bar becomes foo/bar
112 my $class = shift || '';
113 my $case = shift || 0;
115 if ( $class =~ /^.+?::([MVC]|Model|View|Controller)::(.+)$/ ) {
116 $prefix = $case ? $2 : lc $2;
117 $prefix =~ s{::}{/}g;
122 =head2 class2tempdir( $class [, $create ] );
124 Returns a tempdir for a class. If create is true it will try to create the path.
126 My::App becomes /tmp/my/app
127 My::App::Controller::Foo::Bar becomes /tmp/my/app/c/foo/bar
132 my $class = shift || '';
133 my $create = shift || 0;
134 my @parts = split '::', lc $class;
136 my $tmpdir = dir( File::Spec->tmpdir, @parts )->cleanup;
138 if ( $create && !-e $tmpdir ) {
140 eval { $tmpdir->mkpath };
143 # don't load Catalyst::Exception as a BEGIN in Utils,
144 # because Utils often gets loaded before MyApp.pm, and if
145 # Catalyst::Exception is loaded before MyApp.pm, it does
147 # $Catalyst::Exception::CATALYST_EXCEPTION_CLASS in
149 require Catalyst::Exception;
150 Catalyst::Exception->throw(
151 message => qq/Couldn't create tmpdir '$tmpdir', "$@"/ );
155 return $tmpdir->stringify;
158 =head2 dist_indicator_file_list
160 Returns a list of files which can be tested to check if you're inside a checkout
164 sub dist_indicator_file_list {
165 qw/ Makefile.PL Build.PL dist.ini /;
170 Returns home directory for given class.
172 Note that the class must be loaded for the home directory to be found using this function.
179 # make an $INC{ $key } style string from the class name
180 (my $file = "$class.pm") =~ s{::}{/}g;
182 if ( my $inc_entry = $INC{$file} ) {
184 # look for an uninstalled Catalyst app
186 # find the @INC entry in which $file was found
187 (my $path = $inc_entry) =~ s/$file$//;
188 my $home = find_home_unloaded_in_checkout($path);
189 return $home if $home;
193 # look for an installed Catalyst app
195 # trim the .pm off the thing ( Foo/Bar.pm -> Foo/Bar/ )
196 ( my $path = $inc_entry) =~ s/\.pm$//;
197 my $home = dir($path)->absolute->cleanup;
199 # return if if it's a valid directory
200 return $home->stringify if -d $home;
208 =head2 find_home_unloaded_in_checkout ($path)
210 Tries to determine if C<$path> (or cwd if not supplied)
211 looks like a checkout. Any leading lib, script or blib components
212 will be removed, then the directory produced will be checked
213 for the existence of a C<< dist_indicator_file_list() >>.
215 If one is found, the directory will be returned, otherwise false.
219 sub find_home_unloaded_in_checkout {
221 $path ||= cwd() if !defined $path || !length $path;
222 my $home = dir($path)->absolute->cleanup;
223 # pop off /lib and /blib if they're there
224 # pop off /script if it's there.
227 # only return the dir if it has a Makefile.PL or Build.PL or dist.ini
228 if (any { $_ } map { -f $home->file($_) } dist_indicator_file_list()) {
229 # clean up relative path:
230 # MyApp/script/.. -> MyApp
233 my @dir_list = $home->dir_list();
234 while (($dir = pop(@dir_list)) && $dir eq '..') {
235 $home = dir($home)->parent->parent;
237 return $home->stringify;
239 $home = $home->parent;
241 while # pop off /lib and /blib or /script or /t/ if they're there
242 ($home =~ /b?lib$/ || $home =~ /script$/ || $home =~ /\/t(\/|$)/);
245 =head2 prefix($class, $name);
247 Returns a prefixed action.
249 MyApp::Controller::Foo::Bar, yada becomes foo/bar/yada
254 my ( $class, $name ) = @_;
255 my $prefix = &class2prefix($class);
256 $name = "$prefix/$name" if $prefix;
262 Returns an L<HTTP::Request> object for a uri.
268 unless ( ref $request ) {
269 if ( $request =~ m/^http/i ) {
270 $request = URI->new($request);
273 $request = URI->new( 'http://localhost' . $request );
276 unless ( ref $request eq 'HTTP::Request' ) {
277 $request = HTTP::Request->new( 'GET', $request );
282 =head2 ensure_class_loaded($class_name, \%opts)
284 Loads the class unless it already has been loaded.
286 If $opts{ignore_loaded} is true always tries the require whether the package
287 already exists or not. Only pass this if you're either (a) sure you know the
288 file exists on disk or (b) have code to catch the file not found exception
289 that will result if it doesn't.
293 sub ensure_class_loaded {
297 croak "Malformed class Name $class"
298 if $class =~ m/(?:\b\:\b|\:{3,})/;
300 croak "Malformed class Name $class"
301 if $class =~ m/[^\w:]/;
303 croak "ensure_class_loaded should be given a classname, not a filename ($class)"
304 if $class =~ m/\.pm$/;
306 # $opts->{ignore_loaded} can be set to true, and this causes the class to be required, even
307 # if it already has symbol table entries. This is to support things like Schema::Loader, which
308 # part-generate classes in memory, but then also load some of their contents from disk.
309 return if !$opts->{ ignore_loaded }
310 && Class::MOP::is_class_loaded($class); # if a symbol entry exists we don't load again
312 # this hack is so we don't overwrite $@ if the load did not generate an error
316 my $file = $class . '.pm';
318 eval { CORE::require($file) };
322 die $error if $error;
324 warn "require $class was successful but the package is not defined."
325 unless Class::MOP::is_class_loaded($class);
330 =head2 merge_hashes($hashref, $hashref)
332 Base code to recursively merge two hashes together with right-hand precedence.
337 my ( $lefthash, $righthash ) = @_;
339 return $lefthash unless defined $righthash;
341 my %merged = %$lefthash;
342 for my $key ( keys %$righthash ) {
343 my $right_ref = ( ref $righthash->{ $key } || '' ) eq 'HASH';
344 my $left_ref = ( ( exists $lefthash->{ $key } && ref $lefthash->{ $key } ) || '' ) eq 'HASH';
345 if( $right_ref and $left_ref ) {
346 $merged{ $key } = merge_hashes(
347 $lefthash->{ $key }, $righthash->{ $key }
351 $merged{ $key } = $righthash->{ $key };
358 =head2 env_value($class, $key)
360 Checks for and returns an environment value. For instance, if $key is
361 'home', then this method will check for and return the first value it finds,
362 looking at $ENV{MYAPP_HOME} and $ENV{CATALYST_HOME}.
367 my ( $class, $key ) = @_;
370 my @prefixes = ( class2env($class), 'CATALYST' );
372 for my $prefix (@prefixes) {
373 if ( defined( my $value = $ENV{"${prefix}_${key}"} ) ) {
383 Try to guess terminal width to use with formatting of debug output
385 All you need to get this work, is:
387 1) Install Term::Size::Any, or
389 2) Export $COLUMNS from your shell.
391 (Warning to bash users: 'echo $COLUMNS' may be showing you the bash
392 variable, not $ENV{COLUMNS}. 'export COLUMNS=$COLUMNS' and you should see
393 that 'env' now lists COLUMNS.)
395 As last resort, default value of 80 chars will be used.
402 return $_term_width if $_term_width;
406 my ($columns, $rows) = Term::Size::Any::chars;
411 $width = $ENV{COLUMNS}
412 if exists($ENV{COLUMNS})
413 && $ENV{COLUMNS} =~ m/^\d+$/;
416 $width = 80 unless ($width && $width >= 80);
417 return $_term_width = $width;
421 =head2 resolve_namespace
423 Method which adds the namespace for plugins and actions.
425 __PACKAGE__->setup(qw(MyPlugin));
427 # will load Catalyst::Plugin::MyPlugin
432 sub resolve_namespace {
433 my $appnamespace = shift;
434 my $namespace = shift;
436 return String::RewritePrefix->rewrite({
437 q[] => qq[${namespace}::],
439 (defined $appnamespace
440 ? (q[~] => qq[${appnamespace}::])
449 Catalyst Contributors, see Catalyst.pm
453 This library is free software. You can redistribute it and/or modify it under
454 the same terms as Perl itself.