Ver Fonte

Merge pull request #355 from Troglodyne-Internet-Widgets/unmigrated-sites-stay-flatfile

Keep serving a FlatFile site from FlatFile until it is migrated
George S. Baugh há 1 semana atrás
pai
commit
0a586725e4
3 ficheiros alterados com 102 adições e 2 exclusões
  1. 2 1
      config/default.cfg
  2. 42 1
      lib/Trog/Data.pm
  3. 58 0
      t/Trog-Data.t

+ 2 - 1
config/default.cfg

@@ -1,6 +1,7 @@
 [general]
     # FlatFile or SQLite.  See the Readme, and bin/migrate.pl
-    # for moving an existing FlatFile site across.
+    # for moving an existing FlatFile site across.  Until it has
+    # been moved, such a site keeps serving from FlatFile.
     data_model=SQLite
     # What the site calls itself in page titles and feeds.
     title=tCMS

+ 42 - 1
lib/Trog/Data.pm

@@ -3,6 +3,8 @@ package Trog::Data;
 use v5.36;
 use re '/aa';
 
+use Trog::Log qw{:all};
+
 #It's just a factory
 
 =head1 Trog::Data
@@ -13,13 +15,15 @@ This is a data model factory.
 
 Returns a new Trog::Data::* class appropriate to what is configured in the Trog::Config object passed.
 
+Memoized: every call after the first returns the first handle, whatever it is passed.
+
 =cut
 
 sub new ( $class, $config ) {
     state $datamodule;
     return $datamodule if $datamodule;
 
-    my $module = "Trog::Data::" . $config->param('general.data_model');
+    my $module = "Trog::Data::" . model($config);
     my $req    = $module;
     $req =~ s/::/\//g;
     require "$req.pm";
@@ -27,4 +31,41 @@ sub new ( $class, $config ) {
     return $datamodule;
 }
 
+=head2 model(Trog::Config) = STRING
+
+The data model to serve from: general.data_model, except on a site which never
+moved its posts across with bin/migrate.pl.
+
+SQLite is the shipped default, so a FlatFile site which never saved a
+data_model of its own, or which saved the default back, is configured for a
+database that does not exist.  While data/posts.sqlite is missing and data/files
+holds posts, this answers FlatFile and warns, so the site keeps serving the
+posts it has.  Once the migration has written the database, the configuration
+applies as written.
+
+=cut
+
+# FlatFile's datastore, named here because loading that model creates its tag
+# index, and this has to look before choosing.  Trog::Data::FlatFile has its own.
+our $flatfile_store = 'data/files';
+
+sub model ($config) {
+    my $model = $config->param('general.data_model');
+    return $model unless $model eq 'SQLite';
+
+    # Which that model only creates on first use.
+    require Trog::Data::SQLite;
+    no warnings 'once';
+    my $sqlite_db = $Trog::Data::SQLite::dbname;
+    return $model if -e $sqlite_db;
+
+    opendir( my $dh, $flatfile_store ) or return $model;
+    my $posts = grep { -f "$flatfile_store/$_" } readdir $dh;    ## no critic (ProhibitFiletest_f) -- counting names, nothing is opened
+    closedir $dh;
+    return $model unless $posts;
+
+    WARN("general.data_model is SQLite, but $sqlite_db does not exist and $flatfile_store holds $posts post(s).  Serving them from FlatFile.  Run bin/migrate.pl to move them across.");
+    return 'FlatFile';
+}
+
 1;

+ 58 - 0
t/Trog-Data.t

@@ -0,0 +1,58 @@
+use strict;
+use warnings;
+
+use Test::More;
+use Test::MockModule;
+use Config::Simple ();
+use File::Temp     ();
+use Path::Tiny     ();
+use FindBin;
+
+use lib "$FindBin::Bin/../lib";
+
+use_ok('Trog::Data') or BAIL_OUT("Can't find SUT");
+
+# Ahead of the tests, which point its database somewhere else: model() requires
+# it, and a first load would put the real path back.
+use Trog::Data::SQLite ();
+
+no warnings qw{once};
+
+sub config_for {
+    my ($model) = @_;
+    my $conf = Config::Simple->new( syntax => 'ini' );
+    $conf->param( 'general.data_model', $model );
+    return $conf;
+}
+
+subtest 'model' => sub {
+    my $dir = Path::Tiny->new( File::Temp::tempdir( CLEANUP => 1 ) );
+    local $Trog::Data::flatfile_store = "$dir/files";
+    local $Trog::Data::SQLite::dbname = "$dir/posts.sqlite";
+
+    my @warned;
+    my $mock = Test::MockModule->new('Trog::Data');
+    $mock->redefine( WARN => sub { push( @warned, @_ ) } );
+
+    is( Trog::Data::model( config_for('FlatFile') ), 'FlatFile', 'FlatFile is served as configured' );
+    is( Trog::Data::model( config_for('SQLite') ),   'SQLite',   'a new site with no flat file store gets SQLite' );
+
+    $dir->child('files')->mkpath;
+    is( Trog::Data::model( config_for('SQLite') ), 'SQLite', 'and so does one whose store is empty' );
+    is( scalar(@warned),                           0,        'neither of which is worth a warning' );
+
+    $dir->child( 'files', 'a-post' )->spew('{}');
+    $dir->child( 'files', 'another' )->spew('{}');
+    is( Trog::Data::model( config_for('SQLite') ), 'FlatFile', 'a site with flat file posts and no database keeps serving its posts' );
+    is( scalar(@warned),                           1,          'with a warning' );
+    like( $warned[0] // '', qr{holds 2 post\(s\).*bin/migrate\.pl}a, 'saying how many, and how to move them' );
+
+    $dir->child('posts.sqlite')->touch;
+    @warned = ();
+    is( Trog::Data::model( config_for('SQLite') ), 'SQLite', 'once the migration has written the database, it is served' );
+    is( scalar(@warned),                           0,        'without a warning' );
+
+    is( Trog::Data::model( config_for('FlatFile') ), 'FlatFile', 'and a site configured for FlatFile is never moved' );
+};
+
+done_testing();