1 package Catalyst::Utils;
4 use Catalyst::Exception;
11 use String::RewritePrefix;
12 use Moose::Util qw/find_meta/;
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 # Special move to deal with components which are anon classes.
54 # Specifically, CX::Component::Traits c072fb2
55 my $meta = find_meta($class);
57 while ($meta->is_anon_class) {
58 my @superclasses = $meta->superclasses;
59 return if scalar(@superclasses) > 1; # Fail silently, MI, can't deal..
60 $class = $superclasses[0];
61 $meta = find_meta($class);
66 if ( $class =~ /^(.+?)::([MVC]|Model|View|Controller)::.+$/ ) {
72 =head2 class2classprefix($class);
74 MyApp::Controller::Foo::Bar becomes MyApp::Controller
75 My::App::Controller::Foo::Bar becomes My::App::Controller
79 sub class2classprefix {
80 my $class = shift || '';
82 if ( $class =~ /^(.+?::([MVC]|Model|View|Controller))::.+$/ ) {
88 =head2 class2classsuffix($class);
90 MyApp::Controller::Foo::Bar becomes Controller::Foo::Bar
94 sub class2classsuffix {
95 my $class = shift || '';
96 my $prefix = class2appclass($class) || '';
97 $class =~ s/$prefix\:://;
101 =head2 class2env($class);
103 Returns the environment name for class.
106 My::App becomes MY_APP
111 my $class = shift || '';
116 =head2 class2prefix( $class, $case );
118 Returns the uri prefix for a class. If case is false the prefix is converted to lowercase.
120 My::App::Controller::Foo::Bar becomes foo/bar
125 my $class = shift || '';
126 my $case = shift || 0;
128 if ( $class =~ /^.+?::([MVC]|Model|View|Controller)::(.+)$/ ) {
129 $prefix = $case ? $2 : lc $2;
130 $prefix =~ s{::}{/}g;
135 =head2 class2tempdir( $class [, $create ] );
137 Returns a tempdir for a class. If create is true it will try to create the path.
139 My::App becomes /tmp/my/app
140 My::App::C::Foo::Bar becomes /tmp/my/app/c/foo/bar
145 my $class = shift || '';
146 my $create = shift || 0;
147 my @parts = split '::', lc $class;
149 my $tmpdir = dir( File::Spec->tmpdir, @parts )->cleanup;
151 if ( $create && !-e $tmpdir ) {
153 eval { $tmpdir->mkpath };
156 Catalyst::Exception->throw(
157 message => qq/Couldn't create tmpdir '$tmpdir', "$@"/ );
161 return $tmpdir->stringify;
166 Returns home directory for given class.
173 # make an $INC{ $key } style string from the class name
174 (my $file = "$class.pm") =~ s{::}{/}g;
176 if ( my $inc_entry = $INC{$file} ) {
178 # look for an uninstalled Catalyst app
180 # find the @INC entry in which $file was found
181 (my $path = $inc_entry) =~ s/$file$//;
182 $path ||= cwd() if !defined $path || !length $path;
183 my $home = dir($path)->absolute->cleanup;
185 # pop off /lib and /blib if they're there
186 $home = $home->parent while $home =~ /b?lib$/;
188 # only return the dir if it has a Makefile.PL or Build.PL
189 if (-f $home->file("Makefile.PL") or -f $home->file("Build.PL")) {
191 # clean up relative path:
192 # MyApp/script/.. -> MyApp
195 my @dir_list = $home->dir_list();
196 while (($dir = pop(@dir_list)) && $dir eq '..') {
197 $home = dir($home)->parent->parent;
200 return $home->stringify;
205 # look for an installed Catalyst app
207 # trim the .pm off the thing ( Foo/Bar.pm -> Foo/Bar/ )
208 ( my $path = $inc_entry) =~ s/\.pm$//;
209 my $home = dir($path)->absolute->cleanup;
211 # return if if it's a valid directory
212 return $home->stringify if -d $home;
220 =head2 prefix($class, $name);
222 Returns a prefixed action.
224 MyApp::Controller::Foo::Bar, yada becomes foo/bar/yada
229 my ( $class, $name ) = @_;
230 my $prefix = &class2prefix($class);
231 $name = "$prefix/$name" if $prefix;
237 Returns an L<HTTP::Request> object for a uri.
243 unless ( ref $request ) {
244 if ( $request =~ m/^http/i ) {
245 $request = URI->new($request);
248 $request = URI->new( 'http://localhost' . $request );
251 unless ( ref $request eq 'HTTP::Request' ) {
252 $request = HTTP::Request->new( 'GET', $request );
257 =head2 ensure_class_loaded($class_name, \%opts)
259 Loads the class unless it already has been loaded.
261 If $opts{ignore_loaded} is true always tries the require whether the package
262 already exists or not. Only pass this if you're either (a) sure you know the
263 file exists on disk or (b) have code to catch the file not found exception
264 that will result if it doesn't.
268 sub ensure_class_loaded {
272 croak "Malformed class Name $class"
273 if $class =~ m/(?:\b\:\b|\:{3,})/;
275 croak "Malformed class Name $class"
276 if $class =~ m/[^\w:]/;
278 croak "ensure_class_loaded should be given a classname, not a filename ($class)"
279 if $class =~ m/\.pm$/;
281 # $opts->{ignore_loaded} can be set to true, and this causes the class to be required, even
282 # if it already has symbol table entries. This is to support things like Schema::Loader, which
283 # part-generate classes in memory, but then also load some of their contents from disk.
284 return if !$opts->{ ignore_loaded }
285 && Class::MOP::is_class_loaded($class); # if a symbol entry exists we don't load again
287 # this hack is so we don't overwrite $@ if the load did not generate an error
291 my $file = $class . '.pm';
293 eval { CORE::require($file) };
297 die $error if $error;
299 warn "require $class was successful but the package is not defined."
300 unless Class::MOP::is_class_loaded($class);
305 =head2 merge_hashes($hashref, $hashref)
307 Base code to recursively merge two hashes together with right-hand precedence.
312 my ( $lefthash, $righthash ) = @_;
314 return $lefthash unless defined $righthash;
316 my %merged = %$lefthash;
317 for my $key ( keys %$righthash ) {
318 my $right_ref = ( ref $righthash->{ $key } || '' ) eq 'HASH';
319 my $left_ref = ( ( exists $lefthash->{ $key } && ref $lefthash->{ $key } ) || '' ) eq 'HASH';
320 if( $right_ref and $left_ref ) {
321 $merged{ $key } = merge_hashes(
322 $lefthash->{ $key }, $righthash->{ $key }
326 $merged{ $key } = $righthash->{ $key };
333 =head2 env_value($class, $key)
335 Checks for and returns an environment value. For instance, if $key is
336 'home', then this method will check for and return the first value it finds,
337 looking at $ENV{MYAPP_HOME} and $ENV{CATALYST_HOME}.
342 my ( $class, $key ) = @_;
345 my @prefixes = ( class2env($class), 'CATALYST' );
347 for my $prefix (@prefixes) {
348 if ( defined( my $value = $ENV{"${prefix}_${key}"} ) ) {
358 Try to guess terminal width to use with formatting of debug output
360 All you need to get this work, is:
362 1) Install Term::Size::Any, or
364 2) Export $COLUMNS from your shell.
366 (Warning to bash users: 'echo $COLUMNS' may be showing you the bash
367 variable, not $ENV{COLUMNS}. 'export COLUMNS=$COLUMNS' and you should see
368 that 'env' now lists COLUMNS.)
370 As last resort, default value of 80 chars will be used.
377 return $_term_width if $_term_width;
381 my ($columns, $rows) = Term::Size::Any::chars;
386 $width = $ENV{COLUMNS}
387 if exists($ENV{COLUMNS})
388 && $ENV{COLUMNS} =~ m/^\d+$/;
391 $width = 80 unless ($width && $width >= 80);
392 return $_term_width = $width;
396 =head2 resolve_namespace
398 Method which adds the namespace for plugins and actions.
400 __PACKAGE__->setup(qw(MyPlugin));
402 # will load Catalyst::Plugin::MyPlugin
407 sub resolve_namespace {
408 my $appnamespace = shift;
409 my $namespace = shift;
411 return String::RewritePrefix->rewrite({
412 q[] => qq[${namespace}::],
414 (defined $appnamespace
415 ? (q[~] => qq[${appnamespace}::])
424 Catalyst Contributors, see Catalyst.pm
428 This library is free software. You can redistribute it and/or modify it under
429 the same terms as Perl itself.