Thanks to all of you for your enlightening replies.
With your replies I realized that I have somehow neglected "tie" so far. Shame on me :)
My aim is to write a module which simply helps testing reading or writing to files, without actually touching the file system.
With that in my mind, it looks like in-memory file is the answer. Besides, when I play with it I concluded that the in-memory file mechanism is something like a predefined tied filehandle.
I modified my module and wanna share with you: (As you can see, since the in-memory file mechanism is so powerful, I can also mock files for writing; thus I changed the name of the module)
package Test::MockFile::Light;
use strict;
use Carp;
# ---
my %file;
sub define_file_mocker {
my ($name, $content) = @_;
$content ||= '';
$file{$name} = $content;
}
sub undefine_file_mocker {
my ($name, $content) = @_;
delete $file{$name};
}
# ---
sub import {
my ($class, %arg) = @_;
my $package = caller;
_export_functions_to($package);
my $module = defined $arg{module} ? $arg{module} : $package;
_override_open_builtin_for($module);
}
# ---
sub _export_functions_to {
my ($package) = @_;
no strict 'refs';
*{"$package\::define_file_mocker"} = \&define_file_mocker;
*{"$package\::undefine_file_mocker"} = \&undefine_file_mocker;
}
# ---
sub _override_open_builtin_for {
my ($module) = @_;
my $mode_and_name_parser = qr/
^
\s*
(|<|>|>>|\+<|\+>|\+>>) # the mode
\s*
(\w.*?) # the file name
\s*
$
/x;
no strict 'refs';
*{"$module\::open"} = sub (\[*$]$;$) {
my ($fh, $mode, $name) = @_;
$name ||= '';
my $compound = "$mode $name";
if ($compound =~ $mode_and_name_parser) {
$mode = $1 || '<';
$name = $2;
}
else {
croak 'Unexpected open() parameters for file mocking';
}
if ($mode eq '<' && ! defined $file{$name}) {
$! = 2;
return 0;
}
return open $$fh, $mode, \$file{$name};
};
}
# ---
1;
A sample usage of the module can be as follows:
package Foo::Bar;
sub process {
my ($class, $file_name) = @_;
my $sum = 0;
open my $fh, $file_name;
while (<$fh>) {
chomp;
$sum += $_;
}
close $fh;
return $sum;
}
1;
... and then,
use Test::More tests => 3;
use Test::MockFile::Light module => 'Foo::Bar';
use_ok('Foo::Bar');
my $file_name = 'data.txt';
my $content = join("\n", 1..5);
define_file_mocker($file_name, $content);
can_ok(Foo::Bar, 'process');
is(Foo::Bar->process($file_name), 15, 'unexpected sum');
I plan to put it on CPAN. Any comments are welcome.
Thanks again,
Oguz
---------------------------------
life is ...
$mutation = sub { $_[0] =~ s/(.)/rand()<0.1?1-$1:$1/ge };