Rapi-Fs/lib/Rapi/Fs.pm
package Rapi::Fs;
use strict;
use warnings;
# ABSTRACT: Plack-compatible, instant ExtJS file browser
use RapidApp 1.04;
use Moose;
extends 'RapidApp::Builder';
use Types::Standard qw(:all);
use RapidApp::Util ':all';
use File::ShareDir qw(dist_dir);
use FindBin;
use Module::Runtime;
our $VERSION = '1.105';
has '+base_appname', default => sub { 'Rapi::Fs::App' };
has 'mounts', is => 'ro', isa => ArrayRef, required => 1;
has 'filetree_class', is => 'ro', isa => Str, default => sub {'Rapi::Fs::Module::FileTree'};
has 'filetree_params', is => 'ro', isa => HashRef, default => sub {{}};
has 'share_dir', is => 'ro', isa => Str, lazy => 1, default => sub {
my $self = shift;
$ENV{RAPIFS_SHARE_DIR} || (
try{dist_dir('Rapi-Fs')} || (
-d "$FindBin::Bin/share" ? "$FindBin::Bin/share" : "$FindBin::Bin/../share"
)
)
};
sub _build_version { $VERSION }
sub _build_plugins { ['RapidApp::TabGui'] }
sub _build_config {
my $self = shift;
Module::Runtime::require_module( $self->filetree_class );
my $tpl_dir = join('/',$self->share_dir,'templates');
-d $tpl_dir or die "template dir ($tpl_dir) not found; Rapi-Fs dist may not be installed properly.\n";
my $loc_assets_dir = join('/',$self->share_dir,'assets');
-d $loc_assets_dir or die "assets dir ($loc_assets_dir) not found; Rapi-Fs dist may not be installed properly.\n";
exists $self->filetree_params->{mounts} and die join('',
"'mounts' param is configured in its own attr",
"-- don't also suppy in 'filetree_params'"
);
return {
'RapidApp' => {
load_modules => {
files => {
class => $self->filetree_class,
params => {
%{ $self->filetree_params },
mounts => $self->mounts
}
}
},
local_assets_dir => $loc_assets_dir,
default_favicon_url => '/assets/local/misc/static/folder_explore.ico'
},
'Plugin::RapidApp::TabGui' => {
title => (ref $self),
right_footer => join('','Generated by ',(ref $self),' v',$VERSION),
nav_title_iconcls => 'icon-folder-explore',
navtrees => [{
module => '/files',
}]
},
'Controller::RapidApp::Template' => {
include_paths => [ $tpl_dir ]
},
}
}
1;
__END__
=head1 NAME
Rapi::Fs - Plack-compatible, instant ExtJS file browser
=head1 SYNOPSIS
use Rapi::Fs;
my $app = Rapi::Fs->new({
mounts => [ '/some/path', '/foo/blah' ]
});
# Plack/PSGI app:
$app->to_app
Or, using L<rapi-fs.pl> utility script:
rapi-fs.pl /some/path /foo/blah
=head1 DESCRIPTION
This is a L<Plack>-compatible file browser application written using L<RapidApp>. It generates a
nice-looking dynamic web interface to browse and view arbitrary files and directories for the
configured "mounts" using a standard, split-panel tree layout:
=begin HTML
<p><img
src="https://raw.githubusercontent.com/vanstyn/Rapi-Fs/master/share/screenshot.png"
width="600"
alt="Rapi::Fs screenshot"
/></p>
=end HTML
For a live demo, see: L<rapi.io/fs|http://rapi.io/fs>
Internally, the RapidApp module class L<Rapi::Fs::Module::FileTree> does the heavy lifting, and that
module can be configured and used directly within an existing RapidApp.
The convenience utility script L<rapi-fs.pl> is also available, which is a simple wrapper around this
module, to be able to fire up a fully working and self-contained app on-the-fly (including launching a
built-in webserver) from the command-line.
=head1 CONFIGURATION
C<Rapi::Fs> extends L<RapidApp::Builder> and supports all of its options, as well as the following
params specific to this module:
=head2 mounts
List of directory "mount" points to use/show in the application. This is the only required parameter
and supports a flexible syntax. Mounts can be supplied as simple directory string paths, HashRef configs,
or "driver" objects (consuming the L<Rapi::Fs::Role::Driver> role). The default driver class which
ships with the C<Rapi::Fs> package is L<Rapi::Fs::Driver::Filesystem>.
The following mount specifications are all equivalent:
$app = Rapi::Fs->new({
mounts => [ '/some/path' ]
});
$app = Rapi::Fs->new({
mounts => [{
driver => 'Filesystem',
name => 'path',
args => '/some/path'
}]
});
$app = Rapi::Fs->new({
mounts => [
Rapi::Fs::Driver::Filesystem->new({
name => 'path',
args => '/some/path'
})
]
});
When using the HashRef syntax, the C<'driver'> param is a class name relative to the
C<Rapi::Fs::Driver::*> namespace. To supply a full class name, prefix with C<'+'>, for instance:
$app = Rapi::Fs->new({
mounts => [{
driver => '+My::Fs::Driver',
name => 'Foobar',
args => 'something understood by My::Fs::Driver'
}]
});
Different forms can also be mix/matched:
$app = Rapi::Fs->new({
mounts => [
{
driver => '+My::Fs::Driver',
name => 'Foobar',
args => 'something understood by My::Fs::Driver'
},
'/path/to/something',
{
name => 'perl5-lib',
args => '/usr/lib/perl5'
},
Some::Other::Driver->new({
name => 'larry',
args => 'bla',
foo => 'bar'
})
]
});
L<Rapi::Fs::Driver::Filesystem> is the only driver which has been implemented so far, but this module
was written with the idea of implementing other drivers in mind, both as possible additional
core-modules as well as user-defined drivers. See L<Rapi::Fs::Role::Driver> for more info on the
driver API.
=head2 filetree_class
Defaults to L<Rapi::Fs::Module::FileTree>.
=head2 filetree_params
Optional extra params to supply to the C<filetree_class> constructor.
=head1 METHODS
=head2 to_app
PSGI C<$app> CodeRef. Derives from L<Plack::Component>
=head1 TODO
=over
=item *
Add write support (move/rename/copy/delete/edit)
=item *
Add "Mount" as a 4th Node type, to allow nesting other kinds of drivers within
a structure.
=back
=head2 Planned additional drivers
=over
=item *
SSH/SFTP
=item *
IMAP
=item *
JSON/YAML files (i.e. browse data structure)
=item *
Zip/archive
=item *
Multipart/MIME
=back
=head1 SEE ALSO
=over
=item *
L<rapi-fs.pl>
=item *
L<RapidApp>
=item *
L<RapidApp::Builder>
=item *
L<Plack>
=item *
L<http://rapi.io/fs>
=back
=head1 AUTHOR
Henry Van Styn <vanstyn@cpan.org>
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2015 by IntelliTree Solutions llc.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut