Parcourir la source

Fix #79 - flat file data model

George S. Baugh il y a 5 ans
Parent
commit
1b8816d59b
4 fichiers modifiés avec 75 ajouts et 28 suppressions
  1. 10 3
      lib/Trog/Data/DUMMY.pm
  2. 43 11
      lib/Trog/Data/FlatFile.pm
  3. 15 11
      lib/Trog/DataModule.pm
  4. 7 3
      lib/Trog/Routes/HTML.pm

+ 10 - 3
lib/Trog/Data/DUMMY.pm

@@ -35,9 +35,16 @@ sub count ($self) {
     return scalar(@$posts);
 }
 
-sub write($self,$data) {
+sub write($self,$data,$overwrite=0) {
+    my $orig = [];
+    if ($overwrite) {
+        $orig = $data;
+    } else {
+        $orig = $self->read();
+        push(@$orig,@$data);
+    }
     open(my $fh, '>', $datastore) or confess;
-    print $fh JSON::MaybeXS::encode_json($data);
+    print $fh JSON::MaybeXS::encode_json($orig);
     close $fh;
 }
 
@@ -46,7 +53,7 @@ sub delete($self, @posts) {
     foreach my $update (@posts) {
         @$example_posts = grep { $_->{id} ne $update->{id} } @$example_posts;
     }
-    $self->write($example_posts);
+    $self->write($example_posts,1);
     return 0;
 }
 

+ 43 - 11
lib/Trog/Data/FlatFile.pm

@@ -17,7 +17,6 @@ use parent qw{Trog::DataModule};
 our $datastore = 'data/files';
 sub lang { 'Perl Regex in Quotemeta' }
 sub help { 'https://perldoc.perl.org/functions/quotemeta.html' }
-our @index;
 
 =head1 Trog::Data::FlatFile
 
@@ -29,41 +28,74 @@ You can only post once per second due to it storing each post as a file named af
 our $parser = JSON::MaybeXS->new();
 
 sub read ($self, $query={}) {
-    @index = $self->_index() unless @index;
+    #Optimize direct ID
+    my @index;
+    if ($query->{id}) {
+        @index = ("$datastore/$query->{id}");
+    } else {
+        @index = $self->_index();
+    }
+    $query->{limit} //= 25;
+
     my @items;
     foreach my $item (@index) {
+        next unless -f $item;
         my $slurped = File::Slurper::read_text($item);
         my $parsed  = $parser->decode($slurped);
-        push(@items,$parsed) if $self->filter($query,$parsed);
+
+        #XXX this imposes an inefficiency in itself, get() will filter uselessly again here
+        my @filtered = $self->filter($query,@$parsed);
+
+        push(@items,@filtered) if @filtered;
         last if scalar(@items) == $query->{limit};
     }
+
     return \@items;
 }
 
 sub _index ($self) {
-    return @index if @index;
     confess "Can't find datastore!" unless -d $datastore;
     opendir(my $dh, $datastore) or confess;
-    @index = grep { -f } map { "$datastore/$_" } readdir $dh;
+    my @index = grep { -f } map { "$datastore/$_" } readdir $dh;
     closedir $dh;
     return sort { $b cmp $a } @index;
 }
 
 sub write($self,$data) {
-    my $file = "$datastore/$data->{created}";
-    open(my $fh, '>', $file) or confess;
-    print $fh $parser->encode($data);
-    close $fh;
+    foreach my $post (@$data) {
+        my $file = "$datastore/$post->{id}";
+        my $update = [$post];
+        if (-f $file) {
+            my $slurped = File::Slurper::read_text($file);
+            my $parsed  = $parser->decode($slurped);
+
+            $update = [(@$parsed, $post)];
+        }
+
+        open(my $fh, '>', $file) or confess;
+        print $fh $parser->encode($update);
+        close $fh;
+    }
 }
 
 sub count ($self) {
-    @index = $self->_index() unless @index;
+    my @index = $self->_index();
     return scalar(@index);
 }
 
+sub add ($self,@posts) {
+    my $ctime = time();
+    @posts = map {
+        $_->{id} //= $ctime;
+        $_->{created} = $ctime;
+        $_
+    } @posts;
+    return $self->SUPER::add(@posts);
+}
+
 sub delete($self, @posts) {
     foreach my $update (@posts) {
-        unlink "$datastore/$update->{created}" or confess;
+        unlink "$datastore/$update->{id}" or confess;
     }
     return 0;
 }

+ 15 - 11
lib/Trog/DataModule.pm

@@ -101,9 +101,8 @@ sub filter ($self, $query, @filtered) {
 
     # If an ID is passed, just get that (and all it's prior versions)
     if ($request{id}) {
-
-        @filtered = grep { $_->{id} eq $request{id} } @filtered if $request{id};
-        @filtered = _dedup_versions($request{version}, @filtered);
+        @filtered = grep { $_->{id} eq $request{id} } @filtered   if $request{id};
+        @filtered = _dedup_versions($request{version}, @filtered) if defined $request{version};
         return @filtered;
     }
 
@@ -134,10 +133,13 @@ sub paginate ($self, $query, @filtered) {
 }
 
 sub _dedup_versions ($version=-1, @posts) {
+
+    #ASSUMPTION made here - if we pass version this is direct ID query
     if (defined $version) {
-        my $version_max = List::Util::max(map { $_->{version } } @posts);
+        my $version_max = List::Util::max(map { $_->{version} } @posts);
+
         return map {
-            $_->{version_max} = $version_max;
+            $_->{version_max} //= $version_max;
             $_
         } grep { $_->{version} eq $version } @posts;
     }
@@ -202,28 +204,30 @@ Used to determine paginator parameters.
 Add the provided posts to the datastore.
 If any post already exists with the same id, a new post with a version higher than it will be added.
 
+Passes an array of new posts to add to the data store module's write() function.
+
 You probably won't want to override this.
 
 =cut
 
 sub add ($self, @posts) {
     require UUID::Tiny;
-    my $example_posts = $self->read();
+    my @to_write;
     foreach my $post (@posts) {
         $post->{id} //= UUID::Tiny::create_uuid_as_string(UUID::Tiny::UUID_V1, UUID::Tiny::UUID_NS_DNS);
         $post->{created} = time();
-        my (undef, $existing_posts) = $self->get( id => $post->{id} );
-        if (@$existing_posts) {
-            my $existing_post = $existing_posts->[0];
+        my @existing_posts = $self->get( id => $post->{id} );
+        if (@existing_posts) {
+            my $existing_post = $existing_posts[0];
             $post->{version}  = $existing_post->{version};
             $post->{version}++;
         }
         $post->{version} //= 0;
 
         $post = _process($post);
-        push @$example_posts, $post;
+        push @to_write, $post;
     }
-    $self->write($example_posts);
+    $self->write(\@to_write);
     return 0;
 }
 

+ 7 - 3
lib/Trog/Routes/HTML.pm

@@ -524,6 +524,7 @@ sub post ($query, $render_cb) {
         title       => 'New Post',
         to          => $query->{to},
         failure     => $query->{failure} // -1,
+        message     => $query->{message},
         post_visibilities => \@visibuddies,
         stylesheets => $css,
         scripts     => $js,
@@ -533,7 +534,7 @@ sub post ($query, $render_cb) {
         category    => '/posts',
         limit       => $limit,
         pages       => scalar(@posts) == $limit,
-        older       => $posts[-1]->{created},
+        older       => @posts ? $posts[-1]->{created} : '',
         sizes       => [25,50,100],
         id          => $query->{id},
         acls        => \@acls,
@@ -556,10 +557,11 @@ sub post_save ($query, $render_cb) {
     my $acls = $query->{acls};
     state $data = Trog::Data->new($conf);
     $query->{tags}  = _coerce_array($query->{tags});
-    $data->add($query);
-    $query->{failure} = 0;
+    $query->{failure} = $data->add($query);
     $query->{to} = $to;
     $query->{acls} = $acls;
+    $query->{message} = $query->{failure} ? "Failed to add post!" : "Successfully added Post as $query->{id}";
+    delete $query->{id};
     return post($query, $render_cb);
 }
 
@@ -592,6 +594,8 @@ sub post_delete ($query, $render_cb) {
     state $data = Trog::Data->new($conf);
     $query->{failure} = $data->delete($query);
     $query->{to} = $query->{to};
+    $query->{message} = $query->{failure} ? "Failed to delete post $query->{id}!" : "Successfully deleted Post $query->{id}";
+    delete $query->{id};
     return post($query, $render_cb);
 }