Browse Source

Split get_config and watch_for_changes into TPSGI::Startup

tarbaby loads TPSGI.pm at startup and holds it for the process lifetime.
Any change to TPSGI.pm requires a tarbaby restart to take effect, defeating
the purpose of the inotify-based autoreload mechanism.

The fix: extract get_config(), watch_for_changes(), and the inotify state
into TPSGI::Startup — a lightweight module with minimal deps. tarbaby now
loads only TPSGI::Startup; the full TPSGI.pm stays in the workers where it
belongs and can be hot-reloaded freely.

TPSGI.pm keeps thin delegation wrappers for get_config() and
watch_for_changes() so existing callers (bin/tpsgi, bin/tpsgi-config)
continue to work unchanged.

Closes #2

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Kōan 4 tháng trước cách đây
mục cha
commit
5c8cbd1974
3 tập tin đã thay đổi với 114 bổ sung và 82 xóa
  1. 3 3
      bin/tarbaby
  2. 6 79
      lib/TPSGI.pm
  3. 105 0
      lib/TPSGI/Startup.pm

+ 3 - 3
bin/tarbaby

@@ -8,7 +8,7 @@ use FindBin::libs;
 use Plack::Runner;
 
 use File::Basename qw{dirname};
-use TPSGI;
+use TPSGI::Startup;
 
 sub version {
     require Starman;
@@ -18,12 +18,12 @@ sub version {
 # Setup the watcher for changes
 # This has to be setup at global server start rather than app start per worker.
 # This is preferable to using -R (Plack::Loader::Restarter, see notes in orig starman)
-my %options = TPSGI::get_config();
+my %options = TPSGI::Startup::get_config();
 my @dirs = ("$options{tpsgi_dir}/lib");
 foreach my $route (@{$options{routers}}) {
     push(@dirs, dirname("$options{tpsgi_dir}/$route"));
 }
-TPSGI::watch_for_changes(@dirs);
+TPSGI::Startup::watch_for_changes(@dirs);
 
 my $preload_app;
 require Getopt::Long;

+ 6 - 79
lib/TPSGI.pm

@@ -40,9 +40,8 @@ use File::Basename qw{dirname basename};
 use Log::Dispatch;
 use Log::Dispatch::Screen;
 use Log::Dispatch::FileRotate;
-use Linux::Perl::inotify;
-
 use Config::Simple;
+use TPSGI::Startup;
 
 # We have a DEBUG var which is plus ultra for extra sensitive stuff beyond just passing verbose
 my $debug = $ENV{TPSGI_DEBUG};
@@ -1096,50 +1095,7 @@ sub static {
     return $self->forbidden( $self->{current_query} );
 }
 
-sub get_config {
-    $ENV{HOME} ||= Cwd::getcwd();
-    my %options = (
-        verbose    => 0,
-        custom_log => undef,
-        routers    => [],
-        loggers    => [],
-        auth       => undef,
-        domain     => '',
-        user       => '',
-        binds      => [],
-        basedir    => '.',
-        http_user  => '',
-		tpsgi_dir  => Cwd::getcwd(),
-        autoreload => 0,
-    );
-    my $config_file = "$ENV{HOME}/.tpsgi.ini";
-    if ( -f $config_file ) {
-        my $conf = Config::Simple->new($config_file);
-        my %config;
-        %config = %{ $conf->param( -block => 'default' ) } if $conf;
-
-        # Merge the configuration with the options
-        foreach my $opt ( keys(%options) ) {
-            if ( ref $options{$opt} eq 'ARRAY' ) {
-                next unless exists $config{$opt};
-                my @arrayed = ref $config{$opt} eq 'ARRAY' ? @{ $config{$opt} } : ( $config{$opt} );
-                push( @{ $options{$opt} }, @arrayed );
-                next;
-            }
-            $options{$opt} = $config{$opt} if exists $config{$opt};
-        }
-    }
-
-    # Build the paths to logs in case apps want to use them or their dir.
-    my $LOGNAME = "$options{tpsgi_dir}/log/tpsgi.log";
-    $LOGNAME = $options{custom_log} if $options{custom_log};
-
-    my $LOGDIR = dirname($LOGNAME);
-    $options{log_dir}  = $LOGDIR;
-    $options{log_name} = $LOGNAME;
-
-    return %options;
-}
+sub get_config { return TPSGI::Startup::get_config(@_) }
 
 # Convenience method to keep track of server-timing
 sub checkpoint {
@@ -1294,22 +1250,7 @@ sub invalidate_renders {
     "$self->{tpsgi_dir}/www/static/");
 }
 
-my @wds;
-my $inotify;
-sub watch_for_changes {
-    my (@dirs) = @_;
-
-    # No point watching again if we already are watching
-    return if @wds;
-
-    $inotify //= Linux::Perl::inotify->new(flags => [qw{NONBLOCK}]);
-    my @to_watch =_readdir( @dirs );
-
-    foreach my $f2watch (@to_watch) {
-        print "Watching $f2watch for changes\n";
-        push(@wds, $inotify->add( path => $f2watch, events => [qw{CREATE MODIFY DELETE MOVE}] ));
-    }
-}
+sub watch_for_changes { return TPSGI::Startup::watch_for_changes(@_) }
 
 =head2 restart_if_changes
 
@@ -1321,16 +1262,16 @@ Powers the autorestart feature.
 sub restart_if_changes {
     my $self = shift;
 
-    my @result = $inotify->read();
+    my @result = $TPSGI::Startup::inotify->read();
     my $had_changes = 0;
 
     foreach my $res (@result) {
-        if ($res->{name} =~ m/\.pm$/) {
+        if ( $res->{name} =~ m/\.pm$/ ) {
             $had_changes++;
             last;
         }
     }
-    return 0 unless $had_changes ;
+    return 0 unless $had_changes;
 
     # We don't have to clean up the wds, that is handled in the destructor for Inotify
     $self->INFO("Relevant Change in libdirs detected, reloading\n");
@@ -1338,18 +1279,4 @@ sub restart_if_changes {
     return 0;
 }
 
-sub _readdir {
-    my @dirs = @_;
-    File::Find::find( {
-        wanted => sub {
-            my $object = $_;
-            push(@dirs, $object) if (-f $object && $object =~ m/\.pm$/);
-        },
-        no_chdir => 1,
-        bydepth => 1,
-    },
-    @dirs);
-    return @dirs;
-}
-
 1;

+ 105 - 0
lib/TPSGI/Startup.pm

@@ -0,0 +1,105 @@
+package TPSGI::Startup;
+
+# Lightweight startup helpers used by bin/tarbaby before the full TPSGI.pm
+# is loaded into workers.  Keeping these separate means updating TPSGI.pm
+# doesn't require restarting tarbaby to pick up get_config / inotify changes.
+
+use strict;
+use warnings;
+
+use Cwd;
+use File::Basename qw{dirname};
+use File::Find;
+use Linux::Perl::inotify;
+use Config::Simple;
+
+our @wds;
+our $inotify;
+
+=head2 get_config()
+
+Read ~/.tpsgi.ini and return a merged options hash.
+
+=cut
+
+sub get_config {
+    $ENV{HOME} ||= Cwd::getcwd();
+    my %options = (
+        verbose    => 0,
+        custom_log => undef,
+        routers    => [],
+        loggers    => [],
+        auth       => undef,
+        domain     => '',
+        user       => '',
+        binds      => [],
+        basedir    => '.',
+        http_user  => '',
+        tpsgi_dir  => Cwd::getcwd(),
+        autoreload => 0,
+    );
+    my $config_file = "$ENV{HOME}/.tpsgi.ini";
+    if ( -f $config_file ) {
+        my $conf = Config::Simple->new($config_file);
+        my %config;
+        %config = %{ $conf->param( -block => 'default' ) } if $conf;
+
+        foreach my $opt ( keys(%options) ) {
+            if ( ref $options{$opt} eq 'ARRAY' ) {
+                next unless exists $config{$opt};
+                my @arrayed = ref $config{$opt} eq 'ARRAY' ? @{ $config{$opt} } : ( $config{$opt} );
+                push( @{ $options{$opt} }, @arrayed );
+                next;
+            }
+            $options{$opt} = $config{$opt} if exists $config{$opt};
+        }
+    }
+
+    my $LOGNAME = "$options{tpsgi_dir}/log/tpsgi.log";
+    $LOGNAME = $options{custom_log} if $options{custom_log};
+
+    my $LOGDIR = dirname($LOGNAME);
+    $options{log_dir}  = $LOGDIR;
+    $options{log_name} = $LOGNAME;
+
+    return %options;
+}
+
+=head2 watch_for_changes(@dirs)
+
+Set up inotify watches on the given directories (and any .pm files within them).
+Idempotent: no-op if watches are already active.
+
+=cut
+
+sub watch_for_changes {
+    my (@dirs) = @_;
+
+    return if @wds;
+
+    $inotify //= Linux::Perl::inotify->new( flags => [qw{NONBLOCK}] );
+    my @to_watch = _readdir(@dirs);
+
+    foreach my $f2watch (@to_watch) {
+        print "Watching $f2watch for changes\n";
+        push( @wds, $inotify->add( path => $f2watch, events => [qw{CREATE MODIFY DELETE MOVE}] ) );
+    }
+}
+
+sub _readdir {
+    my @dirs = @_;
+    File::Find::find(
+        {
+            wanted => sub {
+                my $object = $_;
+                push( @dirs, $object ) if ( -f $object && $object =~ m/\.pm$/ );
+            },
+            no_chdir => 1,
+            bydepth  => 1,
+        },
+        @dirs
+    );
+    return @dirs;
+}
+
+1;