use 5.010001;
use strict;
use warnings;
package Mite::Package;
use Mite::Miteception -all;
our $AUTHORITY = 'cpan:TOBYINK';
our $VERSION = '0.012000';
has name =>
is => ro,
isa => ValidClassName,
required => true;
has shim_name =>
is => rw,
isa => ValidClassName,
lazy => true,
builder => sub {
my $self = shift;
eval { $self->project->config->data->{shim} } // 'Mite::Shim'
};
has source =>
is => rw,
isa => MiteSource,
# avoid a circular dep with Mite::Source
weak_ref => true;
has imported_functions =>
is => ro,
isa => Map[ MethodName, Str ],
builder => sub { {} };
has imported_keywords =>
is => ro,
isa => Map[ MethodName, Str ],
builder => sub { {} };
has arg =>
is => rw,
default => {};
sub kind { 'package' }
sub BUILD {
my $self = shift;
require Type::Registry;
my $reg = 'Type::Registry'->for_class( $self->name );
$reg->add_types( 'Types::Standard' );
$reg->add_types( 'Types::Common::Numeric' );
$reg->add_types( 'Types::Common::String' );
my $library = eval { $self->project->config->data->{types} };
$reg->add_types( $library ) if $library;
}
sub project {
my $self = shift;
return $self->source->project;
}
sub inject_mite_functions {
my ( $self, $file, $arg ) = ( shift, @_ );
my $requested = sub { $arg->{$_[0]} ? 1 : $arg->{'!'.$_[0]} ? 0 : $arg->{'-all'} ? 1 : $_[1]; };
my $shim = $self->shim_name;
my $package = $self->name;
my $ctxt = $shim->can( '_definition_context' );
no strict 'refs';
${ $package .'::USES_MITE' } = ref( $self );
${ $package .'::MITE_SHIM' } = $shim;
my $want_bool = $requested->( '-bool', 0 );
my $want_is = $requested->( '-is', 0 );
for my $f ( qw/ true false / ) {
next unless $requested->( $f, $want_bool );
*{"$package\::$f"} = \&{"$shim\::$f"};
$self->imported_functions->{$f} = "$shim\::$f";
}
for my $f ( qw/ ro rw rwp lazy bare / ) {
next unless $requested->( $f, $want_is );
*{"$package\::$f"} = \&{"$shim\::$f"};
$self->imported_functions->{$f} = "$shim\::$f";
}
for my $f ( qw/ carp croak confess guard STRICT lock unlock / ) {
next unless $requested->( $f, false );
*{"$package\::$f"} = \&{"$shim\::$f"};
$self->imported_functions->{$f} = "$shim\::$f";
}
if ( $requested->( blessed => false ) ) {
require Scalar::Util;
*{"$package\::blessed"} = \&Scalar::Util::blessed;
$self->imported_functions->{blessed} = "Scalar::Util::blessed";
}
}
sub autolax {
my $self = shift;
return undef
if not eval { $self->project->config->data->{autolax} };
return $self->imported_functions->{STRICT}
? 'STRICT'
: sprintf( '%s::STRICT', $self->project->config->data->{shim} );
}
for my $function ( qw/ carp croak confess / ) {
no strict 'refs';
*{"_function_for_$function"} = sub {
my $self = shift;
return $function
if $self->imported_functions->{$function};
return sprintf '%s::%s', $self->shim_name, $function
if $self->shim_name;
$function eq 'carp' ? 'warn sprintf' : 'die sprintf';
};
}
sub compile {
my $self = shift;
my $code = join "\n",
'{',
map( $self->$_, $self->compilation_stages ),
'1;',
'}';
#::diag $code if main->can('diag');
return $code;
}
sub _compile_meta {
my ( $self, $classvar, $selfvar, $argvar, $metavar ) = @_;
return sprintf '( $Mite::META{%s} ||= %s->__META__ )',
$classvar, $classvar;
}
sub compilation_stages {
return qw(
_compile_package
_compile_pragmas
_compile_uses_mite
_compile_imported_keywords
_compile_imported_functions
_compile_meta_method
);
}
sub _compile_package {
my $self = shift;
return "package @{[ $self->name ]};";
}
sub _compile_pragmas {
my $self = shift;
return <<'CODE';
use strict;
use warnings;
no warnings qw( once void );
CODE
}
sub _compile_uses_mite {
my $self = shift;
my @code = sprintf 'our $USES_MITE = %s;', B::perlstring( ref($self) );
if ( $self->shim_name ) {
push @code, sprintf 'our $MITE_SHIM = %s;', B::perlstring( $self->shim_name );
}
push @code, sprintf 'our $MITE_VERSION = %s;', B::perlstring( $self->VERSION );
join "\n", @code;
}
sub _compile_imported_keywords {
my $self = shift;
my %func = %{ $self->imported_keywords or {} } or return;
my @keywords = sort keys %func;
my $keyword_slots = join q{, }, map "*$_", @keywords;
my $coderefs = join "\n", map " $func{$_},", @keywords;
return sprintf <<'CODE', B::perlstring( $self->shim_name ), B::perlstring( $self->name ), $keyword_slots, $self->shim_name, $coderefs;
# Mite keywords
BEGIN {
my ( $SHIM, $CALLER ) = ( %s, %s );
( %s ) = do {
package %s;
no warnings 'redefine';
(
%s
);
};
};
CODE
}
sub _compile_imported_functions {
my $self = shift;
my %func = %{ $self->imported_functions } or return;
return join "\n",
'# Mite imports',
'BEGIN {',
( $func{blessed} ? ' require Scalar::Util;' : () ),
map(
sprintf( ' *%s = \&%s;', $_, $func{$_} ),
sort keys %func
),
'};',
'';
}
sub _compile_meta_method {
my $self = shift;
my $code = <<'CODE';
# Gather metadata for constructor and destructor
sub __META__ {
no strict 'refs';
my $class = shift; $class = ref($class) || $class;
my $linear_isa = mro::get_linear_isa( $class );
return {
BUILD => [
map { ( *{$_}{CODE} ) ? ( *{$_}{CODE} ) : () }
map { "$_\::BUILD" } reverse @$linear_isa
],
DEMOLISH => [
map { ( *{$_}{CODE} ) ? ( *{$_}{CODE} ) : () }
map { "$_\::DEMOLISH" } @$linear_isa
],
HAS_BUILDARGS => $class->can('BUILDARGS'),
HAS_FOREIGNBUILDARGS => $class->can('FOREIGNBUILDARGS'),
};
}
CODE
if ( eval { $self->project->config->data->{mop} } ) {
$code .= sprintf <<'CODE', $self->project->config->data->{mop};
# Moose-compatibility method
sub meta {
require %s;
Moose::Util::find_meta( ref $_[0] or $_[0] );
}
CODE
}
return $code;
}
1;