Created
August 29, 2011 20:30
-
-
Save nihen/1179323 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use 5.14.1; | |
use Plack::Request; | |
use Text::Xslate::Util qw/html_escape/; | |
use POSIX qw/strftime/; | |
use List::Util qw/first/; | |
use JSON::XS; | |
use IO::Handle; | |
use Encode; | |
my $header = ['content-type' => 'text/html']; | |
my $articles = []; | |
my $recent_articles = []; | |
my $article_contents = []; | |
my $sidebar = ''; | |
my $index_content = ''; | |
my $max_articleid = 0; | |
my $json_encoder = JSON::XS->new->latin1; | |
my $json_decoder = JSON::XS->new->utf8; | |
my $log_file = './logfile'; | |
my $log; | |
my $base_top =<<_EOF_; | |
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta http-equiv="Content-Type" content="text/html" charset="utf-8"> | |
<title>isucon now!</title> | |
<link type="text/css" href="/css/jquery-ui-1.8.14.custom.css" rel="stylesheet"> | |
<link type="text/css" href="/css/isucon.css" rel="stylesheet"> | |
<script type="text/javascript" src="/js/jquery-1.6.2.min.js"></script> | |
<script type="text/javascript" src="/js/jquery-ui-1.8.14.custom.min.js"></script> | |
<script type="text/javascript" src="/js/isucon.js"></script> | |
</head> | |
<body> | |
<div id="view"> | |
<div id="titleimage"><a href="/post"><img src="/images/isucon_title.jpg" style="border: none;"></a></div> | |
<div id="mainview"> | |
_EOF_ | |
my $base_bottom =<<_EOF_; | |
</div> | |
</body> | |
</html> | |
_EOF_ | |
my $post_content =<<_EOF_; | |
<div id="articleview"> | |
<form method="POST" action="/post"> | |
<table> | |
<tr><td><input type="text" name="title" size="60"/></td></tr> | |
<tr><td><textarea name="body" cols="60" rows="8"></textarea></td></tr> | |
</table> | |
<input type="submit"/> | |
</form> | |
</div> | |
_EOF_ | |
{ | |
#preload | |
if ( -e $log_file ) { | |
open $log, '<', $log_file; | |
my @lines = <$log>; | |
$log->close; | |
open $log, '>', $log_file; | |
for my $line (@lines) { | |
chomp $line; | |
my $ops = $json_decoder->decode($line); | |
for my $str ( @$ops ) { | |
$str = Encode::encode('utf-8', $str); | |
} | |
if ( $ops->[0] eq 'post' ) { | |
post(@{$ops}[1..@$ops-1]); | |
} | |
elsif ( $ops->[0] eq 'comment' ) { | |
comment(@{$ops}[1..@$ops-1]); | |
} | |
} | |
} | |
else { | |
open $log, '>', $log_file; | |
for ( 0..10 ) { | |
post($_, $_); | |
} | |
} | |
} | |
sub get_sidebar { | |
my @body = ('<div id="sidebar"><table><tr><td>新着コメントエントリ</td></tr>'); | |
for my $article ( @{$recent_articles}[0..9] ) { | |
push @body, sprintf( | |
'<tr><td><a href="/article/%s">%s</a></td></tr>', | |
$article->{id}, | |
$article->{title}, | |
); | |
} | |
push @body, '</table></div>'; | |
return join '', @body; | |
} | |
sub get_index_content { | |
my @body = ('<div id="articleview">'); | |
for my $article ( @{[reverse @{$articles}]}[0..9] ) { | |
push @body, sprintf( | |
'<div class="article"><div class="title">%s</div><div class="created">%s</div><div class="body">', | |
html_escape($article->{title}), html_escape($article->{created_at}) | |
); | |
my @lines = split "\n", $article->{body}; | |
for my $line (@lines[0..8]) { | |
push @body, html_escape($line), '<br />'; | |
} | |
push @body, '</div><div class="articlelink">'; | |
if ( @lines > 7 ) { | |
push @body, '... ...'; | |
} | |
push @body, sprintf('<a href="/article/%s">read more</a></div></div>', $article->{id}); | |
} | |
return join '', @body, '</div>'; | |
} | |
sub get_article_content { | |
my $articleid = shift; | |
my $article = $articles->[$articleid]; | |
my $abody = ''; | |
for my $body_line ( split "\n", $article->{body} ) { | |
$abody .= html_escape($body_line); | |
$abody .= '<br />'; | |
} | |
my @body = sprintf('<div id="articleview"><div class="article"> | |
<div class="title">%s</div> | |
<div class="created">%s</div> | |
<div class="body">%s</div> | |
</div> | |
<div class="comments"> | |
<div class="commenttitle">以下みなさまの反応</div> | |
', html_escape($article->{title}), html_escape($article->{created_at}), $abody); | |
for my $comment (@{$article->{comments}}) { | |
my $cbody = ''; | |
for my $body_line ( split "\n", $comment->{body} ) { | |
$cbody .= html_escape($body_line); | |
$cbody .= '<br />'; | |
} | |
push @body, sprintf(' | |
<div class="comment"> | |
<div class="name">%s</div> | |
<div class="created">%s</div> | |
<div class="body">%s</div> | |
</div> | |
', html_escape($comment->{name} eq '' ? '名無しさん' : $comment->{name}), html_escape($comment->{created_at}), $cbody); | |
} | |
push @body, sprintf(' | |
<div class="commentform"> | |
<div class="commenttitle">あなたの反応</div> | |
<form method="POST" action="/comment/%s"> | |
<table> | |
<tr><td>おなまえ: <input type="text" name="name" size="30"/></td></tr> | |
<tr><td><textarea name="body" cols="60" rows="4"></textarea></td></tr> | |
</table> | |
<input type="submit"/> | |
</form> | |
</div> | |
</div>', $articleid); | |
return join '', @body, '</div>'; | |
} | |
sub post { | |
$log->printflush($json_encoder->encode([post => @_]), "\n"); | |
my ($title, $body) = @_; | |
my $articleid = $max_articleid++; | |
my $article = { | |
id => $articleid, | |
title => $title, | |
body => $body, | |
created_at => strftime('%Y-%m-%d %H:%M:%S', localtime()), | |
comments => [], | |
}; | |
$articles->[$articleid] = $article; | |
$article_contents->[$articleid] = get_article_content($articleid); | |
$index_content = get_index_content(); | |
} | |
sub comment { | |
$log->printflush($json_encoder->encode([comment => @_]), "\n"); | |
my ($articleid, $name, $body) = @_; | |
my $article = $articles->[$articleid]; | |
push @{$article->{comments}}, { | |
name => $name, | |
body => $body, | |
created_at => strftime('%Y-%m-%d %H:%M:%S', localtime()), | |
}; | |
my $this_article = first { $_->{id} eq $articleid } @$recent_articles; | |
if ( $this_article ) { | |
$recent_articles = [$this_article, grep { $_->{id} ne $articleid } @$recent_articles]; | |
} | |
else { | |
unshift @$recent_articles, $article; | |
if ( @$recent_articles > 11 ) { | |
pop @$recent_articles; | |
} | |
} | |
$sidebar = get_sidebar(); | |
$article_contents->[$articleid] = get_article_content($articleid); | |
} | |
sub app { | |
my $path_info = $_[0]->{PATH_INFO}; | |
if ( $path_info =~ m{\A/article/(\d+)\z}o ) { | |
return ['200', $header, [ | |
\$base_top, | |
\$sidebar, | |
\$article_contents->[$1], | |
\$base_bottom, | |
]]; | |
} | |
elsif ( $path_info eq '/' ) { | |
return ['200', $header, [ | |
\$base_top, | |
\$sidebar, | |
\$index_content, | |
\$base_bottom, | |
]]; | |
} | |
elsif ( $path_info =~ m{\A/comment/(\d+)\z}o ) { | |
my $param = Plack::Request->new($_[0])->body_parameters; | |
comment($1, $param->{name}, $param->{body}); | |
return ['302', [Location => '/article/' . $1], []]; | |
} | |
elsif ( $path_info eq '/post' ) { | |
if ( $_[0]->{REQUEST_METHOD} eq 'GET' ) { | |
return ['200', $header, [ | |
\$base_top, | |
\$sidebar, | |
\$post_content, | |
\$base_bottom, | |
]]; | |
} | |
else { | |
my $param = Plack::Request->new($_[0])->body_parameters; | |
post($param->{title}, $param->{body}); | |
return ['302', [Location => '/'], []]; | |
} | |
} | |
return ['404', [], []]; | |
} | |
\&app; | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
nginxでstaticファイルを処理したら、feersumとの組み合わせで214,204req/minでた