BerkeleyDB-Easy

 view release on metacpan or  search on metacpan

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

package BerkeleyDB::Easy::Common;

use strict;
use warnings;
no warnings 'uninitialized';

use Exporter     ();
use Scalar::Util ();

# The following are "switchboard" methods for class dispatching.
# The idea is that sometimes classes need to call each other laterally
# and not through the inheritance chain. For example, Handle::cursor()
# creates a new cursor, so it finds the right class via $self->_Cursor.
# That way, if you wanted to extend that class with your own, you
# could just override Common::_Cursor here instead of tracking down
# and overriding all the various call sites.

use constant {
	_Base   => 'BerkeleyDB::Easy',
	_Handle => 'BerkeleyDB::Easy::Handle',
	_Cursor => 'BerkeleyDB::Easy::Cursor',
	_Error  => 'BerkeleyDB::Easy::Error',
	_Common => 'BerkeleyDB::Easy::Common',
};

sub _unstrict { no strict 'refs'; no warnings 'once'; ${+shift} }

our (@ISA, @EXPORT, %EXPORT_TAGS, %Levels);

#
# Set up constant functions and exports used by the other packages
# TODO: Process compile/construction-time options;
#       integrate with handle constructor
#
BEGIN {
	@ISA         = qw(Exporter);
	@EXPORT      = ();
	%EXPORT_TAGS = (
		subs  => [qw(_generate _accessor _compile _install _wrap _try 
					 _lines _log)],
		class => [],  # Class dispatching        ex: _Base, _Btree
		flag  => [],  # Error level flags, etc.  ex: BDB_TRACE, BDB_IGNORE
		spec  => [],  # Specification constants  ex: FUNC, RECV, K, V, F
		bool  => [],  # Compilation guard bools  ex: TRACE, INFO, NOTICE
	);
	
	# Class dispatching (export under :class) ---------------------------
	
	my @classes = qw(
		Handle  Cursor  Error  Common
		Btree   Hash    Queue  Recno   Heap  Unknown
	);
	
	my $base = q(BerkeleyDB::Easy);
	constant->import(_Base => $base);
	push @{$EXPORT_TAGS{class}}, q(_Base);
	
	for my $name (@classes) {
		my $const = qq(_$name);
		my $class = qq($base\::$name);
		constant->import($const => $class);
		push @{$EXPORT_TAGS{class}}, $const;
	}
	
	# Error severity / log levels ---------------------------------------
	
	my @levels = (
		'IGNORE',   # 0
		'FATAL',    # 1
		'ERROR',    # 2
		'WARN',     # 3
		'NOTICE',   # 4
		'INFO',     # 5

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

		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]};

	# What to return on failure ($status is set) or success.
	my $fail = join q(, ), @{$spec->[FAIL]};
	my $succ = join q(, ), @{$spec->[SUCC]};
	
	# Use specification to generate code from the following template.
	# Right now, the only use of $opt is to determine if we localize
	# 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 {



( run in 1.520 second using v1.01-cache-2.11-cpan-39bf76dae61 )