Hades
view release on metacpan or search on metacpan
lib/Hades.pm view on Meta::CPAN
$sub_code .= $self->build_type($k, $v, $new_value, $new_error_string, ($v !~ m/^(Optional|Any|Item)/ ? qq|! defined($new_value) \|\|| : q||));
}
my $code = qq|
if ((ref($value) \|\| "") ne "HASH") {
$error_string
} $sub_code|;
return $code;
},
qr/^((Optional|Maybe)\[(.*)\])$/ => sub {
my ($val, @matches) = @_;
my $sub_code = $self->build_type($name, $matches[2], $value, $error_string);
my $code = qq|
if (defined $value) { $sub_code
}|;
return $code;
};
$code .= $switch->($type);
$self->debug_step(sprintf(debug_step_26, $name), $code);
}
return $code;
}
sub extend_error_string {
my ($self, $new_error_string, $value, $new_value, $message, $type) = @_;
my $old_type = quotemeta(qq|$value = defined $value ? $value : 'undef';|);
$new_error_string =~ s/^$old_type//;
$new_error_string =~ s/\Q$value\E/$new_value/;
$new_error_string =~ s/};$/$message};/;
if ($type && $type !~ m/^(Optional|Any|Item)/) {
$new_error_string = qq|$new_value = defined $new_value ? $new_value : 'undef';| . $new_error_string;
}
return $new_error_string;
}
sub build_macro_attributes {
my ($self, $name, $token, $meta) = @_;
return (
'default' => sub {
my $value = shift;
push @{$meta->{$name}->{caught}}, $value;
},
qr/^(\:a|\:alias)/ => sub {
my $value = shift;
$value =~ s/^\:(a|alias)\(\s*(.*)\s*\)$/$2/sg;
push @{$meta->{$name}->{alias}}, split(' ', $value);
},
qr/^(\{)/ => sub {
my $value = shift;
$value =~ s/^\{|\}$//g;
$meta->{$name}->{code} = eval qq|sub { my (\$self, \$mg, \@params) = \@_; $value }|;
},
);
}
sub build_macro {
my ($self, $mg, $class) = @_;
my $meta = $self->{macros};
for my $macro (@{$class}) {
$self->debug_step(debug_step_6, $macro);
if ($macro->[-1] !~ m/^{/) {
my $include = sprintf "Hades::Macro::%s", shift @{$macro};
$self->debug_step(sprintf(debug_step_7, $include), $macro);
eval qq|require $include|;
die $@ if $@;
my $include_meta = $include->new($macro->[0] ? do {
$macro->[0] =~ s/^\[|\]$//g;
( eval qq|$macro->[0]| );
} : ())->meta;
$self->debug_step(sprintf(debug_step_8, $include), $include_meta);
$meta = {%{$meta}, %{$include_meta}};
} else {
my $name = shift @{$macro};
$self->debug_step(sprintf(debug_step_9, $name), $macro);
$meta->{$name}->{meta} = 'MACRO';
my $switch = switch(
$self->build_macro_attributes($name, $macro, $meta)
);
$switch->(shift @{$macro}) while scalar @{$macro};
$self->debug_step(sprintf(debug_step_10, $name), $meta->{$name});
if ($meta->{$name}->{alias}) {
for (@{$meta->{$name}->{alias}}) {
$meta->{$_} = $meta->{$name};
}
}
}
}
$self->debug_step(debug_step_11, $meta);
$self->{macros} = $meta;
}
sub index {
my ($self, $index) = @_;
return substr $self->{eval}, $index, 1;
}
sub build_test_data {
my ($self, $type, $name, $required) = @_;
my $switch = switch
qr/^(Any)$/ => sub {
return $self->_generate_test_string;
},
qr/^(Item)$/ => sub {
return $self->_generate_test_string;
},
qr/^(Bool)$/ => sub {
return (q|1|, q|[]|, q|{}|);
},
qr/^(Str)$/ => sub {
return ($self->_generate_test_string, q|[]|, q|\1|);
},
qr/^(Num)$/ => sub {
return (q|100.555|, q|[]|, $self->_generate_test_string);
},
qr/^(Int)$/ => sub {
return (q|10|, q|[]|, $self->_generate_test_string);
},
qr/^(Ref)$/ => sub {
return (q|{ test => 'test' }|, $self->_generate_test_string, q|1|);
},
qr/^(Ref\[(.*)\])$/ => sub {
my ($val, @matches) = @_;
lib/Hades.pm view on Meta::CPAN
dokimi :t(CodeRef)
=cut
=head3 RegexpRef
A value where ref($value) eq "Regexp"
dokimi :t(RegexpRef)
=cut
=head3 GlobRef
A value where ref($value) eq "GLOB"
dokimi :t(GlobRef)
=cut
=head3 Object
A blessed object.
dokimi :t(Object)
=cut
=head3 Map
Similar to HashRef but parameterized with type constraints for both the key and value. The constraint for keys would typically be a subtype of Str.
dokimi :t(Map[Str, Int])
=cut
=head3 Tuple
Accepting a list of type constraints for each slot in the array.
dokimi :t(Tuple[Str, Int, HashRef])
=cut
=head3 Dict
Accepting a list of type constraints for each slot in the hash.
dokimi :t(Dict[onoma => Str, id => Optional[Int], epiloges => Dict[onama => Str]])
=cut
=head3 Optional
Used in conjunction with Dict and Tuple to specify slots that are optional and may be omitted.
dokimi :t(Optional[Str])
=cut
=head2 Macros
Hades has a concept of macros that allow you to write re-usable code. see L<https://metacpan.org/source/LNATION/Hades-0.27/macro-fh.hades> for an example of how to extend via macros.
macro {
FH [ macro => [qw/read_file write_file/], alias => { read_file => [qw/rf/], write_file => [qw/wf/] } ]
str2ArrayRef :a(s2ar) {
return qq|$params[0] = [ $params[0] ];|;
}
ArrayRef2Str :a(ar2s) {
return qq|$params[0] = $params[0]\->[0];|;
}
}
MacroKosmos {
eros $eros :t(Str) :d(t/test.txt) {
â¬s2ar('$eros');
â¬ar2s('$eros');
â¬wf('$eros', q|'this is a test'|);
return $eros;
}
psyche $psyche :t(Str) :d(t/test.txt) {
â¬rf('$psyche');
return $content;
}
}
... generates ...
package MacroKosmos;
use strict;
use warnings;
our $VERSION = 0.01;
sub new {
my ( $cls, %args ) = ( shift(), scalar @_ == 1 ? %{ $_[0] } : @_ );
my $self = bless {}, $cls;
my %accessors = ();
for my $accessor ( keys %accessors ) {
my $value
= $self->$accessor(
defined $args{$accessor}
? $args{$accessor}
: $accessors{$accessor}->{default} );
unless ( !$accessors{$accessor}->{required} || defined $value ) {
die "$accessor accessor is required";
}
}
return $self;
}
sub eros {
my ( $self, $eros ) = @_;
$eros = defined $eros ? $eros : "t/test.txt";
if ( !defined($eros) || ref $eros ) {
$eros = defined $eros ? $eros : 'undef';
die qq{Str: invalid value $eros for variable \$eros in method eros};
}
$eros = [$eros];
$eros = $eros->[0];
open my $wh, ">", $eros or die "cannot open file for writing: $!";
print $wh 'this is a test';
close $wh;
return $eros;
}
sub psyche {
my ( $self, $psyche ) = @_;
$psyche = defined $psyche ? $psyche : "t/test.txt";
if ( !defined($psyche) || ref $psyche ) {
$psyche = defined $psyche ? $psyche : 'undef';
die
qq{Str: invalid value $psyche for variable \$psyche in method psyche};
}
open my $fh, "<", $psyche or die "cannot open file for reading: $!";
my $content = do { local $/; <$fh> };
close $fh;
return $content;
}
1;
__END__
=head2 Testing
Hades can auto-generate test files. If you take the following example:
( run in 1.312 second using v1.01-cache-2.11-cpan-59e3e3084b8 )