Questions from the QueryLog: catalyst model open text file

Published · Friday, 21 August 2009 (Updated · 7 March 2010)

Like many things, this is trivial in Catalyst and a slice of the problem is already done for you. Though it wasn’t described, the problem and its solution was already shown here—Catalyst Models Intermission—MyApp source code browser.

Catalyst’s path_to method returns a Path::Class object. Path::Class has three methods for opening files: open, openw, and openr. The first, open, can be called with the equivalent arguments for reading and writing: r|w.

path_to has a limitation though. It’s specific to your application directory. It won’t let you get the path for anything outside. You can see however that it is an easy, portable way to get files and their handles within Cat so why not use it for other things too.

First, a file to use

cat ./etc/file_system/README
This is a file for your MyApp::Model::File to find.

You can see we’ve picked a nice safe spot for our file based model: etc/file_system. This isn’t necessary, it’s just a way to do it to know you’re not going to accidentally serve files that will cause security problems.

We need a little configuration next–

Add to myapp.yml

Model::File:
  root: __path_to(etc/file_system)__

Create the model: MyApp::Model::File

emacs lib/MyApp/Model/File.pm
package MyApp::Model::File;
use strict;
use warnings;
use parent "Catalyst::Model";
use Moose;
use MooseX::Types::Path::Class;
use Cwd;

has root =>
    is       => "ro",
    isa      => "Path::Class::Dir",
    required => 1,
    coerce   => 1,
    ;

sub get : method {
    my $self = shift;
    my $path = Cwd::abs_path(shift);
    my $file = Path::Class::File->new( $self->root, $path );
    -r $file or confess qq{File "$file" is unreadable or non-existent};
    return $file;
}

sub slurp : method {
    +shift->get(+shift)->slurp;
}

__PACKAGE__->meta->make_immutable;

1;

Create a controller: MyApp::Controller::File

emacs lib/MyApp/Controller/File.pm
package MyApp::Controller::File;
use strict;
use warnings;
use parent 'Catalyst::Controller';

sub index :Path :Args(0) {
    my ( $self, $c ) = @_;
    my $name = "/README";
    my $content = $c->model("File")->slurp($name);
    $c->response->body($content);
}

1;

Normally this is where the explanation of the code comes. How about that?

This code is rolled into the 10 Catalyst models in 10 days github code so grab that if you’re inclined.



digg stumbleupon del.icio.us reddit Fark Technorati Faves

« Questions from the QueryLog: access catalyst config from a model · Helper method for WWW::Selenium »
« Perl resources, modules, and sample code »