BerkeleyDB-Easy

 view release on metacpan or  search on metacpan

lib/BerkeleyDB/Easy/Common.pm  view on Meta::CPAN

		$log_level ||= $flag_dual if $ENV{$flag_name}
			or _unstrict(_Base . q(::) . ucfirst lc $level_name);
		constant->import($level_name, $level <= $log_level);
		push @{$EXPORT_TAGS{bool}}, $level_name;
		
		# Handler aliases, ie: _debug (export under :sub)
		my $handler_name = q(_) . lc $level_name;
		my $handler_sub  = sub {
			my $self = shift;
			unshift @_, $flag_dual;
			$self->_log(@_);
		};
		no strict 'refs';
		*$handler_name = $handler_sub;
		push @{$EXPORT_TAGS{sub}}, $handler_name;
	}
	
	# BDB_LEVEL (export under :flag)
	constant->import(BDB_LEVEL => $log_level || BDB_IGNORE());
	push @{$EXPORT_TAGS{flag}}, q(BDB_LEVEL);
	
	# BDB_VERBOSE (export under :flag)
	my $verbose = $ENV{BDB_VERBOSE} || _unstrict(_Base . q(::Verbose));
	$verbose    = $log_level >= BDB_DEBUG() if not defined $verbose;
	constant->import(BDB_VERBOSE => !!$verbose);
	push @{$EXPORT_TAGS{flag}}, q(BDB_VERBOSE);

	# Subroutine generator specification (export under :spec) -----------
	
	my %spec = (
		K => q($key),     FUNC => 0,
		V => q($value),   RECV => 1,
		F => q($flags),   SEND => 2,
		S => q($status),  SUCC => 3,
		R => q($return),  FAIL => 4,
		A => q(@_),       OPTI => 5,
		X => q($x),       FLAG => 6,
		Y => q($y),
		Z => q($z),
		T => q(1),
		N => q(''),
		U => q(undef),
	);
	while (my ($key, $val) = each %spec) {
		constant->import($key, $val);
		push @{$EXPORT_TAGS{spec}}, $key;
	}

	# Export all tag groups by default
	push @EXPORT, map @{$EXPORT_TAGS{$_}}, keys %EXPORT_TAGS;
}

#
# Install a stub closure into the calling package. When called for the 
# first time, it will compile and magic goto itself. If we get passed a
# specification, generate a BerkeleyDB.pm wrapper function. Otherwise, make
# a simple object accessor.
#
sub _install {
	my ($self, $name, $spec) = @_;
	my ($pack, $file, $line) = (caller)[0..2];

	DEBUG and $self->_debug(qq(Installing method stub: $name));

	my $stub = sub {
		my $code = $spec
			? $self->_generate($spec, $name, $pack)
			: $self->_accessor($name);

		TRACE and $self->_trace(qq(Generated code: $code));
		$self->_compile($code, $name, $pack);

		goto &{"$pack\::$name"};
	};

	no strict 'refs';
	*{"$pack\::$name"} = $stub;
}

#
# Expand function specification into code via a dynamic template.
# (Internal method, used by _install)
# 
sub _generate {
	my ($self, $spec, $name, $pack) = @_;
	
	# Optimization level. The higher this is, the less we do.
	my $opt = $spec->[OPTI] || 0;

	# The parameters to our function and the vars we will unroll @_ into.
	# Generally some combination of K ($key), V ($value), and F ($flags).
	my $recv = join q(, ), q($self), @{$spec->[RECV]};
	
	# Need to declare any other variables we're going to need that didn't
	# get declared when we unrolled @_.
	my $decl = do {
		my %r =     map  { $_ => 1             } @{$spec->[RECV]};
		join q(, ), grep { $_ ne A and !$r{$_} } @{$spec->[SEND]};
	};

	# What BerkeleyDB.pm class are we wrapping?
	# Either ::Common (for all handle types) or ::Cursor.
	my $isa = do { no strict 'refs'; ${qq($pack\::ISA)}[0] };

	# Does the function return something we need to keep? (db_cursor)
	#   Yes (R): keep it and get $status from SUPER::status.
	#    No (S): return value is $status.
	my $keep = ( grep { $_ eq R } @{$spec->[SUCC]} ) ? R : S;
	
	# What function are we wrapping?
	my $func = $spec->[FUNC];

	# Does it require a default flag?
	my $flag = $spec->[FLAG];

	# Arguments that we send to the function. If the function has a default
	# flag, we need to OR it together with any flags provided by the user.
	my $send = join q(, ), $flag
		? map { $_ eq F ? qq($flag | ${\F}) : $_ } @{$spec->[SEND]}
		: @{$spec->[SEND]};

lib/BerkeleyDB/Easy/Common.pm  view on Meta::CPAN

	# error variables and signal handlers, which is expensive.
	# Various other logic is done is to create the trimmest possible
	# wrapper depending on the needs of the function.
	
	# $opt = 1;
	my ($D, $W) = (BDB_FATAL, BDB_WARN);

	$self->_lines(
		(                  qq|sub $name {                                 |),
		(!$opt          && qq|    my \@err;                               |),
		(!$opt          && qq|    local (\$!, \$^E);                      |),
		(!$opt          && qq|    local \$SIG{__DIE__} =                  |),
		(!$opt          && qq|        sub { \@err = ($D, \$_) };          |),
		(!$opt          && qq|    local \$SIG{__WARN__} =                 |),
		(!$opt          && qq|        sub { \@err = ($W, \$_) };          |),
		( $opt <= 1     && qq|    undef \$BerkeleyDB::Error;              |),
		(                  qq|    my ($recv) = \@_;                       |),
		($decl          && qq|    my ($decl);                             |),
		(TRACE          && qq|    \$self->_trace('$name', \@_);           |),
		($send ne A     && qq|    my $keep = $isa\::$func(\$self, $send); |),
		($send eq A     && qq|    my $keep = &$isa\::$func;               |),
		($keep eq R     && qq|    my ${\S} = $isa\::status(\$self);       |),
		(!$opt          && qq|    \$self->_log(\@err) if \@err;           |),
		(                  qq|    if (${\S}) {                            |),
		(!$opt          && qq|        \$self->_throw(${\S});              |),
		( $opt          && qq|        \$self->_throw(${\S}, undef, $opt); |),
		($fail ne $succ && qq|        return($fail);                      |),
		(                  qq|    }                                       |),
		(                  qq|    return($succ);                          |),
		(                  qq|}                                           |),
	);
}

#
# Make a getter-setter for managing our own state.
# (Internal method, used by _install)
#
sub _accessor {
	my ($self, $name) = @_;

	$self->_lines(
		(qq|sub $name {                      |),
		(qq|    my \$self = shift;           |),
		(qq|    if (\@_) {                   |),
		(qq|        \$self->{$name} = shift; |),
		(qq|        return(\$self);          |),
		(qq|    }                            |),
		(qq|    return(\$self->{$name});     |),
		(qq|}                                |),
	);
}

#
# Prior to compilation, interleave code with line directives so that 
# stack traces will be still be somewhat useful. They'll point to the
# file and line number of our caller, the site of the template definition.
# (Internal method, used by _generate and _accessor)
#
sub _lines {
	my $self = shift;
	my ($file, $line) = (caller)[1..2];

	join qq(# line $line $file(EVAL)\n), 
		map { (my $ln = $_) =~ s/\s*$/\n/; $ln }
		grep $_, @_;
}

#
# Compile some code into the requested package (or caller).
# (Internal method, used by _install)
#
sub _compile {
	my ($self, $code, $name, $pack) = @_;
	$name ||= q(__ANON__);
	$pack ||= caller;

	INFO and $self->_info(qq(Compiling method: $name));

	my ($sub, $err);
	{
		local $@;
		no warnings 'redefine';
		$sub = eval(qq(package $pack; $code));
		($err = $@) =~ s/, at EOF\n$//;
	};

	$self->_fatal(qq(Error compiling method "$name": $err)) if $err;
	
	$sub;
}

#
# BerkeleyDB.pm doesn't throw many exceptions -- only during initialization,
# really -- but whenever it might, we wrap it and localize its global error
# variable as well as the operating system's, so we can return everything
# back to the user in a pristine state.
#
sub _wrap {
	my ($self, $func) = (shift, shift);
	local ($BerkeleyDB::Error, $!, $^E);
	$func->(@_);
}

#
# An even tinier Try::Tiny because lol no dependencies.
#
sub _try {
	my ($self, $try, $catch) = @_;
	my ($ok, $ret, $err);

	my $prev = $@;
	{
		local $@;
		$ok = eval {
			local $@ = $prev;
			$ret = $try->();
			1;
		};
		$err = $@;
	};



( run in 1.667 second using v1.01-cache-2.11-cpan-995e09ba956 )