Stupid Slash Tricks: Auto-Encapsulation of Tasks

pudge on 2008-06-05T19:54:43

The slashd program in slash runs "tasks" which are separate files. It performs a require(). The problem is that sometimes symbols in each task can conflict with another. We have many tasks so rather than edit each one, we decided to handle the encapsulation in slashd itself. It creates a package name, and has code to import symbols from main into the package, and then sets the line number correctly.

		(my $tmppackage = $file) =~ s/\.pl$//;
		$tmppackage =~ s/\W/_/g;
		$tmppackage =~ s/^([^a-zA-Z])/a_$1/g;
		$tmppackage = "Slash::Task::$tmppackage";

		# replace tmppackage in string where appropriate
		(my $addtxt = <<'EOT') =~ s/\${?tmppackage}?/$tmppackage/g;

package $tmppackage;

{ no strict 'refs';
	my @scalar = qw(me task_exit_flag);
	my @hash   = qw(task);
	my @code   = (qw(
			slashdLog slashdErrnote slashdLogDie
			tagboxLog verbosity db_time init_cron
		),
		grep { *{"main::$_"}{CODE} }
			@Slash::EXPORT, @Slash::Display::EXPORT,
			@Slash::Utility::EXPORT,
			@File::Spec::Functions::EXPORT,
			@LWP::UserAgent::EXPORT, @URI::Escape::EXPORT,
			@File::Basename::EXPORT, @File::Path::EXPORT,
			@Time::Local::EXPORT, @Time::HiRes::EXPORT
	);

	*{"${tmppackage}::$_"} = *{"main::$_"}{HASH}   for @hash;
	*{"${tmppackage}::$_"} = *{"main::$_"}{SCALAR} for @scalar;
	*{"${tmppackage}::$_"} = *{"main::$_"}{CODE}   for @code;
}

#line 1
EOT

		my($tmpfh, $tmpfile) = tempfile();
		my $tmptxt = do { open my $fh, '<', $fullname; local $/; <$fh> };
		print $tmpfh $addtxt, $tmptxt;
		$tmpfh->flush; # can't close fh until we require the file,
		               # else the file might disappear

		my $ok = 0;
		eval { local $me = $file; $ok = require $tmpfile };

Cross-posted on <pudge/*>.