Question

Is there a way to have one app in dancer but with multiple appdirs.

Or could I do something like this:

My project is in dir 'foo'. And let's say that I have a dir 'bar' (not inside 'foo') which has a directory called 'public'. I what my app 'foo' to use this public as its own public and if it searches for let's say '/css/style.css' and it is not in '/bar/public/' it should search the '/foo/public/'. How can I do that?

Was it helpful?

Solution

One of the ways if to write a plugin that renders static (and replaces some functionality). You can use Dancer::Plugin::Thumbnail as an example.

Other way I see is to monkey-patch get_file_response() at Dancer::Renderer which is not really such a good idea.

Following code looks for static files in each dir from @dirs array. It's dirty, ugly and unsafe. This can be broken in future version and may cause problems with other parts of Dancer framework I'm not familiar with. You're warned.

#!/usr/bin/env perl
use Dancer;
use Dancer::Renderer;
use MyWeb::App;

my $get_file_response_original = \&Dancer::Renderer::get_file_response;
my @dirs = ('foo');

*Dancer::Renderer::get_file_response = sub {
    my $app = Dancer::App->current;

    my $result;

    # Try to find static in default dir
    if ($result = $get_file_response_original->(@_)) {
        return $result;
    }

    # Save current settings
    my $path_backup = $app->setting('public');

    # Go through additional dirs
    foreach my $dir (@dirs) {
        $app->setting(public => $dir);
        if ($result = $get_file_response_original->(@_)) {
            last;
        }
    }

    # Restore public
    $app->setting('public' => $path_backup);

    return $result
};

dance;

Third ways is to let nginx just do this work for you by writing proper nginx config for your application.

OTHER TIPS

OK, here is the good way to do it. It can of course be a plugin.

You should never do this kind of things by hacking inside Dancer's core, you should rather always consider implementing a route handler to do the job:

#!/usr/bin/env perl
use Dancer;
use File::Spec;
use Dancer::FileUtils 'read_file_content';
use Dancer::MIME;
use HTTP::Date;

# your routes here

# then the catchall route for 
# serving static files

# better in config
my @public_dirs = qw(/tmp/test/foo /tmp/test/bar /tmp/test/baz);

get '/**' => sub {
    my $path = request->path;
    my $mime = Dancer::MIME->instance;

    # security checks
    return send_error("unauthrorized request", 403) if $path =~ /\0/;
    return send_error("unauthrorized request", 403) if $path =~ /\.\./;

    # decompose the path_info into a file path
    my @path = split '/', $path;

    for my $location (@public_dirs) {
        my $file_path = File::Spec->catfile($location, @path);

        next if ! -f $file_path;

        my $content = read_file_content($file_path);
        my $content_type = $mime->for_file($file_path);
        my @stat = stat $file_path;

        header 'Content-Type', $content_type;
        header 'Content-Length', $stat[7];
        header 'Last-Modified', HTTP::Date::time2str($stat[9]);
        return $content;
    }

    pass;
};

start;

An example of this app running:

$ mkdir -p /tmp/test/foo /tmp/test/bar /tmp/test/baz
$ echo 1 > /tmp/test/foo/foo.txt
$ echo 2 > /tmp/test/bar/bar.txt
$ echo 3 > /tmp/test/baz/baz.txt
$ ./bin/app.pl
$ curl -I http://0:3000/baz.txt
HTTP/1.0 200 OK
Content-Length: 2
Content-Type: text/plain
Last-Modified: Fri, 14 Oct 2011 11:28:03 GMT
X-Powered-By: Perl Dancer 1.3051

May be this module will help to you? https://github.com/Perlover/Dancer-Plugin-Hosts You can setup virtual sites in Dancer with own appdir & other directory settings I uploaded this module today to github Soon will be in CPAN

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top