Luc Didry d909b8
# vim:set sw=4 ts=4 sts=4 ft=perl expandtab:
Luc Didry d909b8
package Lufi::DB::Slice;
Luc Didry d909b8
use Mojo::Base -base;
Luc Didry 8d6f10
use Encode 'encode';
Luc Didry 8d6f10
use File::Spec::Functions;
Luc Didry 164698
use Mojo::Collection 'c';
Luc Didry d909b8
Luc Didry d909b8
has 'short';
Luc Didry d909b8
has 'j';
Luc Didry 164698
has 'record' => 0;
Luc Didry d909b8
has 'app';
Luc Didry d909b8
Luc Didry 8d6f10
=encoding utf8
Luc Didry 8d6f10
Luc Didry d909b8
=head1 NAME
Luc Didry d909b8
Luc Didry d909b8
Lufi::DB::Slice - DB abstraction layer for Lufi file
Luc Didry d909b8
Luc Didry d909b8
=head1 Contributing
Luc Didry d909b8
Luc Didry d909b8
When creating a new database accessor, make sure that it provides the following subroutines.
Luc Didry d909b8
After that, modify this file and modify the C<new> subroutine to allow to use your accessor.
Luc Didry d909b8
Luc Didry d909b8
Have a look at Lufi::DB::Slice::SQLite's code: it's simple and may be more understandable that this doc.
Luc Didry d909b8
Luc Didry d909b8
=head1 Attributes
Luc Didry d909b8
Luc Didry d909b8
=over 1
Luc Didry d909b8
Luc Didry d909b8
=item B<short> : string
Luc Didry d909b8
Luc Didry d909b8
=item B<j>     : integer
Luc Didry d909b8
Luc Didry d909b8
=item B<app>   : A mojolicious object
Luc Didry d909b8
Luc Didry d909b8
=back
Luc Didry d909b8
Luc Didry d909b8
=head1 Sub routines
Luc Didry d909b8
Luc Didry d909b8
=head2 new
Luc Didry d909b8
Luc Didry d909b8
=over 1
Luc Didry d909b8
Luc Didry d909b8
=item B<Usage>     : C<$c = Lufi::DB::Slice-E<gt>new(app =E<gt> $self);>
Luc Didry d909b8
Luc Didry d909b8
=item B<Arguments> : any of the attribute above
Luc Didry d909b8
Luc Didry d909b8
=item B<Purpose>   : construct a new db accessor object. If the C<short> attribute is provided, it have to load the informations from the database.
Luc Didry d909b8
Luc Didry d909b8
=item B<Returns>   : the db accessor object
Luc Didry d909b8
Luc Didry d909b8
=item B<Info>      : the app argument is used by Lufi::DB::Slice to choose which db accessor will be used, you don't need to use it in new(), but you can use it to access helpers or configuration settings in the other subroutines
Luc Didry d909b8
Luc Didry d909b8
=back
Luc Didry d909b8
Luc Didry d909b8
=cut
Luc Didry d909b8
Luc Didry d909b8
sub new {
Luc Didry d909b8
    my $c = shift;
Luc Didry d909b8
Luc Didry d909b8
    $c = $c->SUPER::new(@_);
Luc Didry d909b8
Luc Didry d909b8
    if (ref($c) eq 'Lufi::DB::Slice') {
Luc Didry d909b8
        my $dbtype = $c->app->config('dbtype');
Luc Didry d909b8
        if ($dbtype eq 'sqlite') {
Luc Didry d909b8
            use Lufi::DB::Slice::SQLite;
Luc Didry d909b8
            $c = Lufi::DB::Slice::SQLite->new(@_);
Luc Didry 18499a
        } elsif ($dbtype eq 'postgresql') {
Luc Didry 18499a
            use Lufi::DB::Slice::Pg;
Luc Didry 18499a
            $c = Lufi::DB::Slice::Pg->new(@_);
Luc Didry 44507c
        } elsif ($dbtype eq 'mysql') {
Luc Didry 44507c
            use Lufi::DB::Slice::Mysql;
Luc Didry 44507c
            $c = Lufi::DB::Slice::Mysql->new(@_);
Luc Didry d909b8
        }
Luc Didry d909b8
    }
Luc Didry d909b8
Luc Didry d909b8
    return $c;
Luc Didry d909b8
}
Luc Didry d909b8
Luc Didry d909b8
=head2 write
Luc Didry d909b8
Luc Didry d909b8
=over 1
Luc Didry d909b8
Luc Didry d909b8
=item B<Usage>     : C<$c-E<gt>write>
Luc Didry d909b8
Luc Didry d909b8
=item B<Arguments> : none
Luc Didry d909b8
Luc Didry d909b8
=item B<Purpose>   : create or update a record in the database, with the values of the object's attributes
Luc Didry d909b8
Luc Didry d909b8
=item B<Returns>   : the db accessor object
Luc Didry d909b8
Luc Didry d909b8
=back
Luc Didry d909b8
Luc Didry 164698
=cut
Luc Didry 164698
Luc Didry 164698
sub write {
Luc Didry 164698
    my $c = shift;
Luc Didry 164698
Luc Didry 164698
    if ($c->record) {
Luc Didry 8d6f10
        $c->app->dbi->db->query('UPDATE slices SET short = ?, j = ? WHERE short = ? AND j = ?', $c->short, $c->j, $c->short, $c->j);
Luc Didry 164698
    } else {
Luc Didry 8d6f10
        $c->app->dbi->db->query('INSERT INTO slices (short, j) VALUES (?, ?)', $c->short, $c->j);
Luc Didry 164698
        $c->record(1);
Luc Didry 164698
    }
Luc Didry 164698
Luc Didry 164698
    return $c;
Luc Didry 164698
}
Luc Didry 164698
Luc Didry 8d6f10
=head2 store
Luc Didry 8d6f10
Luc Didry 8d6f10
=over 1
Luc Didry 8d6f10
Luc Didry 8d6f10
=item B<Usage>     : C<$c-E<gt>store($text)>
Luc Didry 8d6f10
Luc Didry 8d6f10
=item B<Arguments> : a scalar value
Luc Didry 8d6f10
Luc Didry 8d6f10
=item B<Purpose>   : will store the content to the object's path, either on filesystem or on Swift object storage
Luc Didry 8d6f10
Luc Didry 8d6f10
=item B<Returns>   : the db accessor object
Luc Didry 8d6f10
Luc Didry 8d6f10
=back
Luc Didry 8d6f10
Luc Didry 8d6f10
=cut
Luc Didry 8d6f10
Luc Didry 8d6f10
sub store {
Luc Didry 8d6f10
    my $c    = shift;
Luc Didry 8d6f10
    my $text = shift;
Luc Didry 8d6f10
Luc Didry 8d6f10
    if ($c->app->config('swift')) {
Luc Didry 8d6f10
        $c->app->swift->put_object(
Luc Didry 8d6f10
            container_name => $c->app->config('swift')->{container},
Luc Didry 8d6f10
            object_name    => $c->get_path(),
Luc Didry 8d6f10
            content_length => length(Encode::encode_utf8($text)),
Luc Didry ed302d
            content        => Encode::encode_utf8($text)
Luc Didry 8d6f10
        );
Luc Didry 8d6f10
    } else {
Luc Didry 8d6f10
        # Create directory
Luc Didry 8d6f10
        my $dir = catfile($c->app->config('upload_dir'), $c->short);
Luc Didry 8d6f10
        mkdir($dir, 0700) unless (-d $dir);
Luc Didry 8d6f10
Luc Didry 8d6f10
        # Write file
Luc Didry 8d6f10
        my $file = catfile($c->app->config('upload_dir'), $c->get_path());
Luc Didry 8d6f10
        Mojo::File->new($file)->spurt($text);
Luc Didry 8d6f10
    }
Luc Didry 8d6f10
Luc Didry 8d6f10
    return $c;
Luc Didry 8d6f10
}
Luc Didry 8d6f10
Luc Didry 8d6f10
=head2 retrieve
Luc Didry 8d6f10
Luc Didry 8d6f10
=over 1
Luc Didry 8d6f10
Luc Didry 8d6f10
=item B<Usage>     : C<$c-E<gt>retrieve>
Luc Didry 8d6f10
Luc Didry 8d6f10
=item B<Arguments> : none
Luc Didry 8d6f10
Luc Didry 8d6f10
=item B<Purpose>   : get file from storage, either filesystem or Swift object storage
Luc Didry 8d6f10
Luc Didry 8d6f10
=item B<Returns>   : the data from the file
Luc Didry 8d6f10
Luc Didry 8d6f10
=back
Luc Didry 8d6f10
Luc Didry 8d6f10
=cut
Luc Didry 8d6f10
Luc Didry 8d6f10
sub retrieve {
Luc Didry 8d6f10
    my $c      = shift;
Luc Didry 8d6f10
    my $upload = shift;
Luc Didry 8d6f10
Luc Didry 8d6f10
    if ($c->app->config('swift')) {
Luc Didry 8d6f10
        my $file;
Luc Didry 8d6f10
        $c->app->swift->get_object(
Luc Didry 8d6f10
            container_name => $c->app->config('swift')->{container},
Luc Didry 8d6f10
            object_name    => $c->get_path(),
Luc Didry 8d6f10
            write_code => sub {
Luc Didry 8d6f10
                my ($status, $message, $headers, $chunk) = @_;
Luc Didry 8d6f10
                $file .= $chunk;
Luc Didry 8d6f10
            }
Luc Didry 8d6f10
        );
Luc Didry ed302d
        return Encode::decode_utf8($file);
Luc Didry 8d6f10
    } else {
Luc Didry 8d6f10
        my $file = catfile($c->app->config('upload_dir'), $c->get_path());
Luc Didry 8d6f10
        return Mojo::File->new($file)->slurp;
Luc Didry 8d6f10
    }
Luc Didry 8d6f10
}
Luc Didry 8d6f10
=head2 delete_file
Luc Didry 8d6f10
Luc Didry 8d6f10
=over 1
Luc Didry 8d6f10
Luc Didry 8d6f10
=item B<Usage>     : C<$c-E<gt>delete_file()>
Luc Didry 8d6f10
Luc Didry 8d6f10
=item B<Arguments> : none
Luc Didry 8d6f10
Luc Didry 8d6f10
=item B<Purpose>   : delete the file on filesystem or Swift object storage
Luc Didry 8d6f10
Luc Didry 8d6f10
=item B<Returns>   : the db accessor object
Luc Didry 8d6f10
Luc Didry 8d6f10
=back
Luc Didry 8d6f10
Luc Didry 8d6f10
=cut
Luc Didry 8d6f10
Luc Didry 8d6f10
sub delete_file {
Luc Didry 8d6f10
    my $c   = shift;
Luc Didry 8d6f10
Luc Didry 8d6f10
    if ($c->app->config('swift')) {
Luc Didry 8d6f10
        $c->app->swift->delete_object({
Luc Didry 8d6f10
            container_name => $c->app->config('swift')->{container},
Luc Didry 8d6f10
            object_name    => $c->get_path()
Luc Didry 8d6f10
        });
Luc Didry 8d6f10
    } else {
Luc Didry 8d6f10
        my $file = catfile($c->app->config('upload_dir'), $c->get_path());
Luc Didry 8d6f10
        unlink $file or warn sprintf('Could not unlink %s: %s', $file, $!);
Luc Didry 8d6f10
    }
Luc Didry 8d6f10
    return $c;
Luc Didry 8d6f10
}
Luc Didry d909b8
=head2 get_slices_of_file
Luc Didry d909b8
Luc Didry d909b8
=over 1
Luc Didry d909b8
Luc Didry d909b8
=item B<Usage>     : C<$c-E<gt>get_slices_of_file($short)>
Luc Didry d909b8
Luc Didry d909b8
=item B<Arguments> : string
Luc Didry d909b8
Luc Didry d909b8
=item B<Purpose>   : get all Lufi::DB::Slice objects related to a file
Luc Didry d909b8
Luc Didry d909b8
=item B<Returns>   : a Mojo::Collection of Lufi::DB::Slice objects
Luc Didry d909b8
Luc Didry d909b8
=back
Luc Didry d909b8
Luc Didry d909b8
=cut
Luc Didry d909b8
Luc Didry 164698
sub get_slices_of_file {
Luc Didry 164698
    my $c     = shift;
Luc Didry 164698
    my $short = shift;
Luc Didry 164698
Luc Didry 164698
    my @slices;
Luc Didry 164698
    my $records = $c->app->dbi->db->query('SELECT * FROM slices WHERE short = ? ORDER BY j ASC', $short)->hashes;
Luc Didry 164698
    $records->each(
Luc Didry 164698
        sub {
Luc Didry 164698
            my ($e, $num) = @_;
Luc Didry 164698
            my $i = Lufi::DB::Slice->new(app => $c->app);
Luc Didry 164698
Luc Didry 164698
            push @slices, $i->_slurp($e);
Luc Didry 164698
        }
Luc Didry 164698
    );
Luc Didry 164698
Luc Didry 164698
    return c(@slices);
Luc Didry 164698
}
Luc Didry 164698
Luc Didry e41b08
=head2 delete_all
Luc Didry e41b08
Luc Didry e41b08
=over 1
Luc Didry e41b08
Luc Didry e41b08
=item B<Usage>     : C<$c-E<gt>delete_all()>
Luc Didry e41b08
Luc Didry e41b08
=item B<Arguments> : none
Luc Didry e41b08
Luc Didry 8d6f10
=item B<Purpose>   : delete all slices records from database unconditionnally
Luc Didry e41b08
Luc Didry e41b08
=item B<Returns>   : nothing
Luc Didry e41b08
Luc Didry e41b08
=back
Luc Didry e41b08
Luc Didry e41b08
=cut
Luc Didry e41b08
Luc Didry e41b08
sub delete_all {
Luc Didry e41b08
    my $c = shift;
Luc Didry e41b08
Luc Didry e41b08
    $c->app->dbi->db->delete('slices');
Luc Didry e41b08
}
Luc Didry e41b08
Luc Didry 8d6f10
=head2 path
Luc Didry 8d6f10
Luc Didry 8d6f10
=over 1
Luc Didry 8d6f10
Luc Didry 8d6f10
=item B<Usage>     : C<$c-E<gt>path()>
Luc Didry 8d6f10
Luc Didry 8d6f10
=item B<Arguments> : non
Luc Didry 8d6f10
Luc Didry 8d6f10
=item B<Purpose>   : format the path of the file, relative to the directory of the Swift object storage
Luc Didry 8d6f10
Luc Didry 8d6f10
=item B<Returns>   : the path of the file
Luc Didry 8d6f10
Luc Didry 8d6f10
=back
Luc Didry 8d6f10
Luc Didry 8d6f10
=cut
Luc Didry 8d6f10
Luc Didry 8d6f10
sub get_path {
Luc Didry 8d6f10
    my $c        = shift;
Luc Didry 8d6f10
Luc Didry 8d6f10
    return catfile($c->short, sprintf('%d.part', $c->j));
Luc Didry 8d6f10
}
Luc Didry 8d6f10
Luc Didry 8d6f10
=head2 count
Luc Didry 8d6f10
Luc Didry 8d6f10
=over 1
Luc Didry 8d6f10
Luc Didry 8d6f10
=item B<Usage>     : C<$c-E<gt>count()>
Luc Didry 8d6f10
Luc Didry 8d6f10
=item B<Arguments> : none
Luc Didry 8d6f10
Luc Didry 8d6f10
=item B<Purpose>   : get count of slices records from database
Luc Didry 8d6f10
Luc Didry 8d6f10
=item B<Returns>   : integer
Luc Didry 8d6f10
Luc Didry 8d6f10
=back
Luc Didry 8d6f10
Luc Didry 8d6f10
=cut
Luc Didry 8d6f10
Luc Didry 8d6f10
sub count {
Luc Didry 8d6f10
    my $c = shift;
Luc Didry 8d6f10
Luc Didry 8d6f10
    return $c->app->dbi->db->query('SELECT count(*) AS count FROM slices')->hashes->first->{count};
Luc Didry 8d6f10
}
Luc Didry 8d6f10
Luc Didry 164698
=head2 _slurp
Luc Didry 164698
Luc Didry 164698
=over 1
Luc Didry 164698
Luc Didry 164698
=item B<Usage>     : C<$c-E<gt>_slurp>
Luc Didry 164698
Luc Didry 164698
=item B<Arguments> : none
Luc Didry 164698
Luc Didry 164698
=item B<Purpose>   : put a database record's columns into the Lufi::DB::Slice object's attributes
Luc Didry 164698
Luc Didry 164698
=item B<Returns>   : the Lufi::DB::Slice object
Luc Didry 164698
Luc Didry 164698
=back
Luc Didry 164698
Luc Didry 164698
=cut
Luc Didry 164698
Luc Didry 164698
sub _slurp {
Luc Didry 164698
    my $c = shift;
Luc Didry 164698
    my $r = shift;
Luc Didry 164698
Luc Didry 164698
    my $slice;
Luc Didry 164698
    if (defined $r) {
Luc Didry 164698
        $slice = $r;
Luc Didry 164698
    } else {
Luc Didry 164698
        my $slices = $c->app->dbi->db->query('SELECT * FROM slices WHERE short = ? AND j = ?', $c->short, $c->j)->hashes;
Luc Didry 164698
Luc Didry 164698
        if ($slices->size) {
Luc Didry 164698
            $slice = $slices->first;
Luc Didry 164698
        }
Luc Didry 164698
    }
Luc Didry 164698
Luc Didry 164698
    if ($slice) {
Luc Didry 164698
        $c->short($slice->{short});
Luc Didry 164698
        $c->j($slice->{j});
Luc Didry 164698
Luc Didry 164698
        $c->record(1);
Luc Didry 164698
    }
Luc Didry 164698
Luc Didry 164698
    return $c;
Luc Didry 164698
}
Luc Didry 164698
Luc Didry d909b8
1;