root/lang/perl/Shika/trunk/lib/Shika.pm @ 25118

Revision 25118, 7.2 kB (checked in by tokuhirom, 4 years ago)

ソースを再生成。

Line 
1package Shika;
2
3use strict;
4use warnings;
5use Carp;
6our $VERSION = '0.01';
7eval "use mro; 1;" or eval "use MRO::Compat; 1;"; ## no critic
8
9sub import {
10    my $pkg = caller(0);
11    strict->import;
12    warnings->import;
13
14    my $meta = +{
15        has      => {},
16        default  => {},
17    };
18
19    no strict 'refs';
20    *{"$pkg\::new"}     = \&_new;
21    *{"$pkg\::has"}     = \&_has;
22    *{"$pkg\::extends"} = \&_extends;
23    *{"$pkg\::with"}    = \&_with;
24    *{"$pkg\::meta"}    = sub { $meta };
25}
26
27sub _new {
28    my $class = shift;
29    my %attr = @_;
30
31    for my $klass ($class, @{mro::get_linear_isa($class)}) {
32        next unless $klass->can('meta');
33        # set default values
34        while (my ($name, $has) = each %{ $klass->meta->{has} }) {
35            next if     exists $attr{$name};
36            next unless exists $has->{default} && $has->{default};
37            next if     exists $has->{lazy} && $has->{lazy};
38            next if     exists $has->{lazy_build} && $has->{lazy_build};
39            my $code = $has->{default};
40            $attr{$name} = ref($code) eq 'CODE' ? $code->() : $code;
41        }
42        # process coerce
43        while (my ($name, $has) = each %{ $klass->meta->{has} }) {
44            next unless exists $attr{$name};
45            next unless exists $has->{coerce} && $has->{coerce};
46            $attr{$name} = $has->{coerce}->($attr{$name});
47        }
48        # process 'required'
49        while (my ($name, $has) = each %{ $klass->meta->{has} }) {
50            next unless $has->{required};
51            next if     exists $attr{$name};
52            Carp::croak "missing parameter $name for $class";
53        }
54    }
55
56    my $self = bless { %attr }, $class;
57    if ($self->can('BUILD')) {
58        $self->BUILD(\%attr);
59    }
60    $self;
61}
62
63sub _has {
64    my $pkg = caller(0);
65    my $n = shift;
66    my %attr = @_;
67    $pkg->meta->{has}->{$n} = \%attr;
68
69    if (my $handles = $attr{handles}) {
70        $handles = [$handles] unless ref $handles;
71        for my $handle (@$handles) {
72            no strict 'refs';
73            *{"$pkg\::$handle"} = sub {
74                shift->$n->$handle(@_)
75            };
76        }
77    }
78   
79    my $f1 =       $attr{lazy} ? 'lazy'
80                               :
81             $attr{lazy_build} ? 'lazy_build'
82                               : 'normal';
83    my $f2 = $attr{coerce} ? '_coerce' : '';
84    my $meth = "_has_install_${f1}${f2}";
85    __PACKAGE__->can($meth)->( $pkg, $n );
86}
87
88### START HAS_INSTALL
89
90sub _has_install_normal {
91    my ($pkg, $n, ) = @_;
92    my $has = $pkg->meta->{has}->{$n};
93    no strict "refs";
94    *{"$pkg\::$n"} = sub {
95        if (@_ == 1) {
96            return $_[0]->{$n};
97        }
98
99        if (@_==2) {
100            return $_[0]->{$n} = $_[1];
101        }
102        shift->{$n} = \@_;
103    };
104}
105sub _has_install_normal_coerce {
106    my ($pkg, $n, ) = @_;
107    my $has = $pkg->meta->{has}->{$n};
108    no strict "refs";
109    *{"$pkg\::$n"} = sub {
110        if (@_ == 1) {
111            return $_[0]->{$n};
112        }
113
114        if (@_==2) {
115            return $_[0]->{$n} = $has->{coerce}->($_[1]);
116        }
117        shift->{$n} = \@_;
118    };
119}
120sub _has_install_lazy {
121    my ($pkg, $n, ) = @_;
122    my $has = $pkg->meta->{has}->{$n};
123    no strict "refs";
124    *{"$pkg\::$n"} = sub {
125        if (@_ == 1) {
126            unless (exists $_[0]->{$n} && exists $has->{default}) {
127                my $code = $has->{default};
128                return $_[0]->{$n} = ref($code) eq 'CODE' ? $code->($_[0]) : $code;
129            }
130            return $_[0]->{$n};
131        }
132
133        if (@_==2) {
134            return $_[0]->{$n} = $_[1];
135        }
136        shift->{$n} = \@_;
137    };
138}
139sub _has_install_lazy_coerce {
140    my ($pkg, $n, ) = @_;
141    my $has = $pkg->meta->{has}->{$n};
142    no strict "refs";
143    *{"$pkg\::$n"} = sub {
144        if (@_ == 1) {
145            unless (exists $_[0]->{$n} && exists $has->{default}) {
146                my $code = $has->{default};
147                return $_[0]->{$n} = ref($code) eq 'CODE' ? $code->($_[0]) : $code;
148            }
149            return $_[0]->{$n};
150        }
151
152        if (@_==2) {
153            return $_[0]->{$n} = $has->{coerce}->($_[1]);
154        }
155        shift->{$n} = \@_;
156    };
157}
158sub _has_install_lazy_build {
159    my ($pkg, $n, ) = @_;
160    my $has = $pkg->meta->{has}->{$n};
161    no strict "refs";
162    *{"$pkg\::$n"} = sub {
163        if (@_ == 1) {
164            unless (exists $_[0]->{$n}) {
165                die "$pkg does not support builder method '_build_$n' for attribute '$n'"
166                    unless my $code = $pkg->can("_build_$n");
167                return $_[0]->{$n} = $code->();
168            }
169            return $_[0]->{$n};
170        }
171
172        if (@_==2) {
173            return $_[0]->{$n} = $_[1];
174        }
175        shift->{$n} = \@_;
176    };
177}
178sub _has_install_lazy_build_coerce {
179    my ($pkg, $n, ) = @_;
180    my $has = $pkg->meta->{has}->{$n};
181    no strict "refs";
182    *{"$pkg\::$n"} = sub {
183        if (@_ == 1) {
184            unless (exists $_[0]->{$n}) {
185                die "$pkg does not support builder method '_build_$n' for attribute '$n'"
186                    unless my $code = $pkg->can("_build_$n");
187                return $_[0]->{$n} = $code->();
188            }
189            return $_[0]->{$n};
190        }
191
192        if (@_==2) {
193            return $_[0]->{$n} = $has->{coerce}->($_[1]);
194        }
195        shift->{$n} = \@_;
196    };
197}
198
199### END HAS_INSTALL
200
201sub _extends {
202    my $pkg = caller(0);
203    my @parents = @_;
204    no strict 'refs';
205    unshift @{"$pkg\::ISA"}, @parents;
206}
207
208sub _with {
209    my $pkg = caller(0);
210    my @roles = @_;
211
212    for my $role (@roles) {
213        Shika::_load_class($role) unless $role->can('meta');
214        next unless $role->can('meta');
215
216        for my $method (@{ _get_functions($role) }) {
217            next if $method eq 'has' || $method eq 'requires' || $method eq 'meta';
218            next if $pkg->can($method);
219            no strict 'refs';
220            *{"$pkg\::$method"} = *{"$role\::$method"};
221        }
222    }
223}
224
225sub _load_class {
226    my $role = shift;
227    eval "require $role" ## no critic ### too bad
228}
229
230# copied from Class::Inspector
231sub _get_functions {
232    my $name = shift;
233
234    no strict 'refs';
235    # Get all the CODE symbol table entries
236    my @functions = sort grep { /\A[^\W\d]\w*\z/o }
237        grep { defined &{"${name}::$_"} }
238            keys %{"${name}::"};
239    \@functions;
240}
241
2421;
243__END__
244
245=head1 NAME
246
247Shika - Lightweight class builder with DSL
248
249=head1 SYNOPSIS
250
251  package Point;
252  use Shika; # automatically turns on strict and warnings
253
254  has 'x';
255  has 'y';
256
257  sub clear {
258      my $self = shift;
259      $self->x(0);
260      $self->y(0);
261  }
262
263  package Point3D;
264  use Shika;
265
266  extends 'Point';
267
268  has 'z';
269
270  after 'clear' => sub {
271      my $self = shift;
272      $self->z(0);
273  };
274
275=head1 DESCRIPTION
276
277Shika is
278
279=head1 AUTHOR
280
281tokuhirom
282
283yappo
284
285lestrrat
286
287typester
288
289charsbar
290
291miyagawa
292
293kan
294
295walf443
296
297kazuho
298
299hidek
300
301mattn
302
303=head1 TODO
304
305    - method modifiers
306    - isa?
307    - make coerce as dsl
308
309=head1 SEE ALSO
310
311=head1 REPOSITORY
312
313  svn co http://svn.coderepos.org/share/lang/perl/Shika/trunk Shika
314
315Shika is Subversion repository is hosted at L<http://coderepos.org/share/>.
316patches and collaborators are welcome.
317
318=head1 LICENSE
319
320This library is free software; you can redistribute it and/or modify
321it under the same terms as Perl itself.
322
323=cut
Note: See TracBrowser for help on using the browser.