root/lang/perl/Yacafi/trunk/lib/Yacafi.pm @ 23465

Revision 23465, 5.8 kB (checked in by yappo, 5 years ago)

oops

Line 
1### NO PACK
2package Yacafi;
3use strict;
4use warnings;
5our $VERSION = '0.01';
6
7our $MAX_POST_BODY_SIZE = 1000000;
8our $DEBUG              = 0;
9our $NOT_FOUND_CODE     = \&_not_found;
10our $CURRENT_CLASS      = '';
11### NO PACK END
12
13my $QUERY = undef;
14### NO PACK
15sub import {
16    my($class, %args) = @_;
17    $QUERY = undef;
18    my $caller = caller;
19    $CURRENT_CLASS = $args{current_class} || $caller;
20
21    # create a pack file
22    _pack() if $ARGV[0] eq '--pack';
23
24    # functions export
25    for my $name (qw/ dispatch query controller model view redirect filter /) {
26        no strict 'refs';
27        *{"$caller\::$name"} = \&{$name};
28    }
29    strict->import;
30    warnings->import;
31}
32
33sub _pack {
34    my $yacafi = _read_file($INC{'Yacafi.pm'});
35    $yacafi =~ s/### NO PACK\n.+?### NO PACK END\n//sg;
36    $yacafi =~ s/\n__END__\n.+$//s;
37    $yacafi =~ s/\$CURRENT_CLASS/$CURRENT_CLASS/g;
38
39    my $cgi = _read_file((caller(1))[1]);
40    $cgi =~ s/use (?:Yacafi|strict|warnings);//g;
41    $cgi =~ s/\$Yacafi::/\$/g;
42    my $shebang;
43    if ($cgi =~ s/(\#\![^\n]+)//s) {
44        $shebang = $1;
45    }
46   
47    my $pl = qq{$shebang
48use strict;
49use warnings;
50package $CURRENT_CLASS;
51my \$MAX_POST_BODY_SIZE = 1000000;
52my \$DEBUG              = 0;
53my \$NOT_FOUND_CODE     = \&_not_found;
54{\n$yacafi\n}
55{\n$cgi\n}\n
56};
57    print $pl;
58    exit;
59}
60
61sub _read_file {
62    my $file = shift;
63    open my $fh, '<', $file or die "$file: $!";
64    do { local $/; <$fh> };
65}
66### NO PACK END
67
68sub dispatch {
69    my $response;
70    eval {
71        my $action = query('action') || 'index';
72        my $func = 'do_' . $action;
73        if (my $code = $CURRENT_CLASS->can($func)) {
74            $response = $code->();
75        } else {
76            $response = $NOT_FOUND_CODE->();
77        }
78    };
79
80    if ($@) {
81        die $@ unless $DEBUG;
82        $response = +{
83            headers => +{},
84            body    => 'Error: ' . $@,
85        };
86    }
87
88    $response ||= +{ headers => +{}, body => '' };
89    $response->{body} ||= '';
90    $response->{headers}->{'Content-Length'} ||= length($response->{body});
91    $response->{headers}->{'Content-Type'} ||= 'text/html';
92
93    # build headers
94    while (my($name, $values) = each %{ $response->{headers} }) {
95        next unless defined $values;
96        for my $value (ref($values) eq 'ARRAY' ? @{ $values } : ( $values )) {
97            printf STDOUT "%s: %s\r\n", $name, $value;
98        }
99    }
100    print STDOUT "\r\n" . $response->{body};
101}
102
103### NO PACK
104sub _parse_query {
105    my $query = +{};
106    my $input = '';
107    if ($ENV{'REQUEST_METHOD'} eq "POST") {
108        if ($ENV{CONTENT_LENGTH} > $MAX_POST_BODY_SIZE) {
109            die "too long Content-Length";
110        } else {
111            read(STDIN, $input, $ENV{'CONTENT_LENGTH'});
112        }
113    } else {
114        $input = $ENV{QUERY_STRING} || '';
115    }
116   
117    for (split /&/, $input) {
118        my ($key, $val) = split /=/, $_;
119        $val =~ tr/+/ /;
120        $val =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("H2", $1)/eg;
121        $query->{$key} = $val;
122    }
123    $query;
124}
125### NO PACK END
126
127sub query {
128    my $name = shift;
129#    $QUERY ||= _parse_query;
130    unless ($QUERY) {
131        my $input = '';
132        if ($ENV{'REQUEST_METHOD'} eq "POST") {
133            if ($ENV{CONTENT_LENGTH} > $MAX_POST_BODY_SIZE) {
134                die "too long Content-Length";
135            } else {
136                read(STDIN, $input, $ENV{'CONTENT_LENGTH'});
137            }
138        } else {
139            $input = $ENV{QUERY_STRING} || '';
140        }
141
142        for (split /&/, $input) {
143            my ($key, $val) = split /=/, $_;
144            $val =~ tr/+/ /;
145            $val =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("H2", $1)/eg;
146            $QUERY->{$key} = $val;
147        }
148    }
149    $QUERY->{$name};
150}
151
152sub redirect {
153    my($uri, $status) = @_;
154    $status ||= 302;
155    +{
156        headers => +{ Status => $status, Location => $uri },
157        body    => 'redirect to ' . $uri,
158    }
159}
160
161sub controller {
162    my $name = shift;
163    my $func = 'do_' . $name;
164    if (my $code = $CURRENT_CLASS->can($func)) {
165        return $code->(@_);
166    } else {
167        die "controller: $CURRENT_CLASS\::do_$name function is missing...";
168    }
169}
170
171sub model {
172    my $name = shift;
173    my $func = 'model_' . $name;
174    if (my $code = $CURRENT_CLASS->can($func)) {
175        return $code->(@_);
176    } else {
177        die "model: $CURRENT_CLASS\::model_$name function is missing...";
178    }
179}
180
181sub view {
182    my $name = shift;
183    my $func = 'view_' . $name;
184    if (my $code = $CURRENT_CLASS->can($func)) {
185        my $ret = $code->(@_);
186        return $ret if ref($ret);
187        return +{
188            headers => +{},
189            body    => $ret,
190        };
191    } else {
192        die "view: $CURRENT_CLASS\::view_$name function is missing...";
193    }
194    my $ret = _goto_mvc( view => @_ );
195}
196
197
198sub _not_found {
199    +{
200        headers => +{ Status => 404 },
201        body    => 'Not Found',
202    };
203}
204
205my $FILTERS = +{
206    html => sub {
207        my $text = shift;
208        $text =~ s/&/&amp;/g;
209        $text =~ s/</&lt;/g;
210        $text =~ s/>/&gt;/g;
211        $text =~ s/\"/&quot;/g;
212        $text =~ s/'/&#39;/g;
213        $text;
214    },
215};
216
217sub filter {
218    return $FILTERS if @_ == 0;
219    my($text, @filters) = @_;
220    for my $filter (@filters) {
221        next unless exists $FILTERS->{$filter} && ref($FILTERS->{$filter}) eq 'CODE';
222        $text = $FILTERS->{$filter}->($text);
223    }
224    $text;
225}
226
2271;
228__END__
229
230=head1 NAME
231
232Yacafi - Yet another CGI application framework interface
233
234=head1 SYNOPSIS
235
236  use Yacafi;
237
238=head1 DESCRIPTION
239
240Yacafi is
241
242=head1 AUTHOR
243
244Kazuhiro Osawa E<lt>yappo <at> shibuya <döt> plE<gt>
245
246=head1 SEE ALSO
247
248=head1 REPOSITORY
249
250  svn co http://svn.coderepos.org/share/lang/perl/Yacafi/trunk Yacafi
251
252Yacafi is Subversion repository is hosted at L<http://coderepos.org/share/>.
253patches and collaborators are welcome.
254
255=head1 LICENSE
256
257This library is free software; you can redistribute it and/or modify
258it under the same terms as Perl itself.
259
260=cut
Note: See TracBrowser for help on using the browser.