Skip to content

Instantly share code, notes, and snippets.

@mix3
Created May 3, 2011 15:43
Show Gist options
  • Save mix3/953572 to your computer and use it in GitHub Desktop.
Save mix3/953572 to your computer and use it in GitHub Desktop.
Mojolicious::Lite + Teng + SNBinder + Xslate (+ SQLite/OnMemory) でCRUDアプリ
package Message;
use parent 'Teng';
__PACKAGE__->load_plugin('Pager');
use Class::Method::Modifiers;
around delete => sub {
my ($orig, $self, $table_name, $delete_condition) = @_;
my $update_row_data = {
deleted_at => DateTime->now->set_time_zone('Asia/Tokyo'),
};
$self->update($table_name, $update_row_data, $delete_condition);
};
before update => sub {
my ($self, $table_name, $update_row_data, $update_condition) = @_;
if (!$update_row_data->{deleted_at}) {
$update_row_data->{updated_at} = DateTime->now->set_time_zone('Asia/Tokyo');
}
};
before insert => sub {
my ($self, $table_name, $row_data) = @_;
$row_data->{created_at} = DateTime->now->set_time_zone('Asia/Tokyo');
$row_data->{updated_at} = DateTime->now->set_time_zone('Asia/Tokyo');
};
before fast_insert => sub {
my ($self, $table_name, $row_data) = @_;
$row_data->{created_at} = DateTime->now->set_time_zone('Asia/Tokyo');
$row_data->{updated_at} = DateTime->now->set_time_zone('Asia/Tokyo');
};
before search => sub {
my ($self, $table_name, $search_condition, $search_attr) = @_;
$search_condition->{deleted_at} = \'IS NULL';
};
before search_with_pager => sub {
my ($self, $table_name, $where, $opts) = @_;
$where->{deleted_at} = \'IS NULL';
};
1;
package Message::Schema;
use Teng::Schema::Declare;
use DateTime::Format::MySQL;
table {
name 'message',
pk 'id',
columns qw(
id
message
created_at
updated_at
deleted_at
);
deflate qr/_at$/ => sub {
DateTime::Format::MySQL->format_datetime(shift);
};
inflate qr/_at$/ => sub {
DateTime::Format::MySQL->parse_datetime(shift);
};
};
1;
package main;
use utf8;
use Mojolicious::Lite;
plugin 'xslate_renderer';
use Devel::KYTProf;
my $model = Message->new(
dbh => DBI->connect(
'dbi:SQLite:dbname=:memory:', '', '',
{
RaiseError => 1,
PrintError => 0,
AutoCommit => 1,
sqlite_unicode => 1,
}
)
);
$model->do(q{
CREATE TABLE IF NOT EXISTS message (
id INTEGER PRIMARY KEY AUTOINCREMENT,
message TEXT NOT NULL,
created_at DATETIME NOT NULL,
updated_at DATETIME NOT NULL,
deleted_at DATETIME
)
});
get '/' => sub{
my $self = shift;
$self->render(
handler => 'tx',
);
} => 'index';
get '/edit/:id' => sub {
my $self = shift;
my $result = $model->single('message', {
id => $self->param('id')
});
return $self->render(
handler => 'tx',
template => 'error',
message => '該当メッセージが見つからないか、既に削除されています'
) if !$result;
$self->render(
handler => 'tx',
id => $result->get_column('id'),
message => $result->get_column('message')
);
} => 'edit';
post '/create' => sub {
my $self = shift;
return $self->render(
handler => 'tx',
template => 'error',
message => 'メッセージを入力してください'
) if !$self->param('message');
$model->fast_insert('message', {
message => $self->param('message'),
});
$self->redirect_to('index');
};
post '/update' => sub {
my $self = shift;
return $self->render(
handler => 'tx',
template => 'error',
message => 'メッセージを入力してください'
) if !$self->param('message');
$model->update('message',
{
message => $self->param('message'),
},
{
id => $self->param('id'),
}
);
$self->redirect_to('index');
};
get '/delete/:id' => sub {
my $self = shift;
$model->delete('message', {
id => $self->param('id'),
});
$self->redirect_to('index');
};
get '/show/:id' => sub {
my $self = shift;
my $row = $model->single('message', {
id => $self->param('id'),
});
my $result = !$row ? {} : $row->get_columns;
$self->render('json' => $result);
};
get '/list' => sub {
my $self = shift;
my $page = $self->param('page') || 1;
my ($rows, $pager) = $model->search_with_pager(
'message',
{
},
{
order_by => 'id DESC',
page => $page,
rows => 3,
}
);
my $messages = [];
foreach my $row (@$rows) {
push @$messages, $row->get_columns;
}
my $result = {
messages => $messages,
entries_per_page => $pager->entries_per_page(),
current_page => $pager->current_page(),
has_next => $pager->has_next(),
entries_on_this_page => $pager->entries_on_this_page(),
next_page => $pager->next_page(),
prev_page => $pager->prev_page(),
};
$self->render('json' => $result);
};
get '/templates' => sub {
my $self = shift;
$self->render(
template => 'templates'
);
} => 'templates';
app->start;
__DATA__
@@ base.tx
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<script src="http://www.google.com/jsapi" type="text/javascript"></script>
<script type="text/javascript">//<![CDATA[
google.load('jquery', '1.4.4');
//]]></script>
<script src="https://github.com/snakajima/SNBinder/raw/master/snbinder-0.5.3.js" type="text/javascript"></script>
: include main_js
<title>Mojolicious::Lite + Teng + SNBinder + Xslate (+ SQLite/OnMemory)</title>
</head>
<body>
: block content -> { }
</body>
</html>
@@ main_js.tx
<script type="text/javascript">//<![CDATA[
var page = 1;
var templates = null;
var main = (function(){
return {
init: function(){
SNBinder.flush_all();
SNBinder.get_named_sections("/templates", null, function(sections) {
templates = sections;
$('div#main').show().html(templates['main']);
main.reload();
$('#prev').click(function(){
page--;
main.reload();
});
$('#next').click(function(){
page++;
main.reload();
});
});
},
reload: function() {
$.get('/list', {"page": page}, function(json){
main.list(json);
main.pager(json);
});
},
list: function(json){
$('#list').fadeOut('fast', function(){
$(this).html(SNBinder.bind_rowset(templates['content'], json.messages));
$(this).show('fast');
});
},
pager: function (json){
$('#pager').fadeOut('fast', function(){
if(json.prev_page != null){
$('#prev').attr('href', '#');
}else{
$('#prev').removeAttr('href');
}
if(json.next_page != null){
$('#next').attr('href', '#');
}else{
$('#next').removeAttr('href');
}
$(this).show('fast');
});
},
};
})();
$(document).ready(function(){
SNBinder.init();
main.init();
});
//]]></script>
@@ templates.html.tx
{%}main{%}
<div id="list" style="display:none;" />
<div id="pager" style="display:none;"><a id="prev">&lt;</a>|<a id="next">&gt;</a></div>
{%}content{%}
<hr /><p>$(.message)</p><p>[<a href=\"/edit/$(.id)\">編集</a>][<a href=\"/delete/$(.id)\">削除</a>]</p>
@@ form.tx
<form method="post" action="<: $path :>">
: if ($id) {
<input type="hidden" name="id" value="<: $id :>">
: }
<p><textarea name="message" cols="50" rows="10" ><: $message :></textarea></p>
<p><input type="submit" /></p>
</form>
@@ index.html.tx
: cascade base
: override content -> {
: include form { path => "/create" }
<div id="main" style="display:none;">
<p>Accessing server ...</p>
</div>
: }
@@ edit.html.tx
: cascade base
: override content -> {
: include form { path => "/update", message => $message }
: }
@@ error.html.tx
: cascade base
: override content -> {
ERROR: <: $message :>
: }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment