Thread Modul für Typenspezifikation über attributes (0 answers)
Opened by topeg at 2011-07-26 15:20

topeg
 2011-07-26 15:20
#150767 #150767
User since
2006-07-10
2611 Artikel
BenutzerIn

user image
Ich habe hier ein Modul das ich nachträglich mit Typenspezifikationen ausstatten möchte um ein Interfache besser zu spezifizieren.

Ich habe mir gedacht dass ich Attribute dafür verwenden kann. Das Umzusetzen war nicht sonderlich schwer. Jedoch würde es mich interessieren, ob es so was nicht schon gibt, bevor ich das weiter ausbaue.

mein Ziel ist es ohne Sourcefilter aus zu kommen und die Variablentypen genau testen zu können.

So habe ich es jetzt gemacht:
more (40.1kb):
Code (perl): (dl )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
#!/usr/bin/perl
use strict;
use warnings;

########################################################################
########################################################################
########################################################################
package param_test;
use Carp;
use attributes;
use Scalar::Util;
use 5.10.0;

BEGIN
{
  my %found;
  my %allowed=(
    _      => ['TRUE',                                 sub{ 1 }],
    def    => ['Defined',                              sub{ defined($_[0]) }],
    int    => ['an Integer',                           sub{ defined($_[0]) && $_[0]!~/[^+\-\d]/ }],
    uint   => ['an Unsigned Integer',                  sub{ defined($_[0]) && $_[0]!~/\D/ }],
    number => ['a Number',                             sub{ defined($_[0]) && Scalar::Util::looks_like_number($_[0]) }],
    string => ['a String',                             sub{ defined($_[0]) && !Scalar::Util::reftype($_[0]) }],
    ref    => ['a Reference',                          sub{ Scalar::Util::reftype($_[0]) }],
    array  => ['an Anonyme Array',                     sub{ Scalar::Util::reftype($_[0]) && Scalar::Util::reftype($_[0]) eq 'ARRAY' }],
    hash   => ['an Anonyme Hash',                      sub{ Scalar::Util::reftype($_[0]) && Scalar::Util::reftype($_[0]) eq 'HASH' }],
    handle => ['a Reference to an Handle',             sub{ Scalar::Util::reftype($_[0]) && Scalar::Util::reftype($_[0]) eq 'GLOB' }],
    code   => ['an Anonyme Function',                  sub{ Scalar::Util::reftype($_[0]) && Scalar::Util::reftype($_[0]) eq 'CODE' }],
    object => ['an Object (blessed Reference)',        sub{ defined($_[0]) && Scalar::Util::blessed($_[0]) }],
    self   => ['an Object (blessed Reference of "%1")',sub{ defined($_[0]) && Scalar::Util::blessed($_[0]) && $_[0]->DOES($_[1]); }],
  );

  my $my_sprintf = sub
  {
    my $string=shift;
    while(1)
    {
      #$string=~s/%%/-!\0!-/gs;
      last unless($string=~s/((?:[^%]|^)(?:%%)*)%(\d+)/ $1.$_[$2-1] /egs);
    }
    #$string=~s/-!\0!-/%/gs;
    $string=~s/%%/%/gs;
    return $string;
  };

  sub MODIFY_CODE_ATTRIBUTES
  {
    my  $pkg = shift;
    my  $ref = shift;
    my ($param)=grep{ /^PARAM\s*\([^)]*\)\s*$/ }@_;

    next unless($param);

    my @opts;

    if($param=~/PARAM\s*\(\s*([^\)]+)\s*\)/s)
    {  @opts=split(/\s*?(?:,|;|\s+)\s*?/s,$1); }

    $found{$pkg}{$ref}=\@opts;
    return;
  }

  sub __INIT__
  {
    for my $pkg (keys(%found))
    {
      no strict 'refs';
      while(my ( $name , $symbol ) = each %{ $pkg . '::' })
      {
        no warnings 'once';
        my  $ref  = *{ $symbol }{ CODE } or next;
        next unless(exists($found{$pkg}{$ref}));
        for(@{$found{$pkg}{$ref}})
        { croak($my_sprintf->(q(for %1 Unknown PARAM "%2"),$symbol,$_)) unless(exists($allowed{$_})); }

        no warnings 'redefine';
        *{ $pkg . '::' . $name } = sub {

          croak($my_sprintf->(q(for %1::%2 @_ is to big),$pkg,$name)) if($#{$found{$pkg}{$ref}} < $#_);
          croak($my_sprintf->(q(for %1::%2 @_ is to smal),$pkg,$name)) if($#{$found{$pkg}{$ref}} > $#_);

          for(0..$#{$found{$pkg}{$ref}})
          {
            my $p=$found{$pkg}{$ref}->[$_];
            next if($allowed{$p}->[1]->($_[$_], $pkg, $name));
            croak($my_sprintf->(q(for %1::%2 $_[%3]="%4" is not %5!),$pkg,$name,$_,$_[$_],$allowed{$p}->[0]));
          }

          goto $ref;
        } # sub end
      }
    }
  }
}

INIT{ __PACKAGE__->__INIT__(); }

########################################################################
########################################################################
########################################################################

package ttt;
use base 'param_test';

sub new : PARAM(string)
{
  my $class=shift;
  bless({},$class);
}

sub test1 : PARAM(self string array hash)
{
  my ($self,$txt,$aref,$href)=@_;
  for(0..$#$aref)
  {
    my $s=$aref->[$_];
    print "$txt: $href->{$s}\n" if(exists($href->{$s}));
  }
}

sub test2 : PARAM(self string)
{
  my ($self,$txt)=@_;
  print "OUTPUT: $txt";
}

sub test3 : PARAM(self uint)
{
  my ($self,$int)=@_;
  print "TEXT($_)\n" for(1..$int);
}

#-----------------------------------------------------------------------
package main;
my $t=ttt->new();
$t->test1('TEST',[qw(a b c)],{a=>'TESTA', b=>'TetstB', d=>'TetstD'});
$t->test3(5);

#$t->new();

# stirbt mit Fehler:
ttt->test1('TEST',[qw(a b c)],{a=>'TESTA', b=>'TetstB', d=>'TetstD'});

Das ist noch etwas gebastelt.
Wenn die Attribute geparst werden dann nehme ich alle die nach "PARAM(...)" aussehen. Hole die Einzelwerte heraus und ersetze die Funktion durch eine die einen Filter für die Variablen enthält.
Last edited: 2011-07-26 15:22:46 +0200 (CEST)

View full thread Modul für Typenspezifikation über attributes