Slide 1

Slide 1 text

)FSPLVͰֶͿɺॳΊͯͷ1FSM !BLJZN :"1$"TJB

Slide 2

Slide 2 text

NF w!BLJZN wIUUQBLJZNDPN wେֶੜ w๺ւಓ͔Βདྷ·ͨ͠ w4,:"3$༷ ͋Γ͕ͱ͏͍͟͝·͢

Slide 3

Slide 3 text

TRMJLBUTVEPOPSH

Slide 4

Slide 4 text

ର৅ wॳ৺ऀ޲͚ηογϣϯ wͰ͖Δ͚ͩஸೡʹղઆ͠·͢ w΢ΣϒΞϓϦΛॻ͖ͨͯ͘1FSMΛ࢝Ίͨ

Slide 5

Slide 5 text

໨ࢦ͢΋ͷ wϑΟʔϦϯάͰΞϓϦΛ࡞ͬͯΈΔ wखݩͰಈ͔͢ w)FSPLVʹσϓϩΠɺ֎෦΁ެ։ wྲྀΕΛ֮͑Δ

Slide 6

Slide 6 text

ݸਓͰ࡞Δ ΢ΣϒΞϓϦͷߏ੒

Slide 7

Slide 7 text

4FSWFS

Slide 8

Slide 8 text

"QQMJDBUJPO4FSWFS 4UBSNBO 4UBSMFU OHJOY w JQUBCMFT w 42-4FSWFS w 4FSWFS

Slide 9

Slide 9 text

"QQMJDBUJPO4FSWFS 4UBSNBO 4UBSMFU OHJOY w JQUBCMFT w 42-4FSWFS w 8FC "QQMJDBUJPO 4FSWFS

Slide 10

Slide 10 text

ࣗ෼Ͱॻ͍ͨ΢ΣϒΞϓϦΛ ެ։͍͚ͨͩ͠ͳͷʹ

Slide 11

Slide 11 text

Ұํɺ)FSPLVͰ͸

Slide 12

Slide 12 text

8FC "QQMJDBUJPO )FSPLV

Slide 13

Slide 13 text

΢ΣϒΞϓϦ͚ͩΛ ॻ͚ͩ͘

Slide 14

Slide 14 text

ར఺ wඞཁͳͱ͜Ζ͚ͩ wແྉͰ͋Δఔ౓ͷΞϓϦΛެ։͢Δ͜ͱ ͕Ͱ͖Δ

Slide 15

Slide 15 text

΢ΣϒΞϓϦΛॻ͘

Slide 16

Slide 16 text

8"' w8FC"QQMJDBUJPO'SBNFXPSL w$BUBMZTU .PKPMJDJPVT w͜͜Ͱ͸"NPOΛ࢖͍·͢ w֮͑Δ͜ͱ͕গͳ͍ɺίʔυ͕খ͍͞

Slide 17

Slide 17 text

"NPOͷಛ௃ wͱͯ΋ܰྔ wηΩϡϦςΟ wσϑΥϧτͰ$43'ରࡦ wσϑΥϧτͰ࠾༻͍ͯ͠ΔςϯϓϨʔτ Τϯδϯ͕5FYU9TMBUF

Slide 18

Slide 18 text

؀ڥߏங

Slide 19

Slide 19 text

νϡʔτϦΞϧ wͦΕͰ͸΢ΣϒΞϓϦΛ࡞Γ·͠ΐ͏

Slide 20

Slide 20 text

DQBONΛΠϯετʔϧ͢Δ $ curl -L http://cpanmin.us | ↩ perl - App::cpanminus ΋͘͠͸ $ curl -L http://cpanmin.us | ↩ perl - --sudo App::cpanminus

Slide 21

Slide 21 text

DBSUPOΛΠϯετʔϧ͢Δ $ cpanm Carton

Slide 22

Slide 22 text

$ cpanm Amon2 $ amon2-setup.pl MyApp $ carton install $ carton exec -- plackup app.psgi

Slide 23

Slide 23 text

$ cpanm Amon2 $ amon2-setup.pl MyApp $ carton install $ carton exec -- plackup app.psgi εέϧτϯΛ࡞੒͢Δ

Slide 24

Slide 24 text

$ cpanm Amon2 $ amon2-setup.pl MyApp $ carton install $ carton exec -- plackup app.psgi ґଘϞδϡʔϧΛΠϯετʔϧ

Slide 25

Slide 25 text

$ cpanm Amon2 $ amon2-setup.pl MyApp $ carton install $ carton exec -- plackup app.psgi ΞϓϦΛىಈ

Slide 26

Slide 26 text

w"NPOಉࠝͷεΫϦϓτ wεέϧτϯΛ࡞੒͢Δ͜ͱ͕Ͱ͖Δ wجຊతʹ͸͜ͷεΫϦϓτΛར༻ͯ͠Ξ ϓϦΛ࡞Γ࢝ΊΔ BNPOTFUVQQM

Slide 27

Slide 27 text

ΞϓϦͷߏ੒

Slide 28

Slide 28 text

. ├── Build.PL ├── app.psgi ├── config │ ├── development.pl │ ├── production.pl │ └── test.pl ├── cpanfile ├── db ├── lib │ ├── MyApp │ │ ├── DB │ │ │ ├── Row.pm │ │ │ └── Schema.pm │ │ ├── DB.pm │ │ ├── Web │ │ │ ├── Dispatcher.pm │ │ │ ├── View.pm │ │ │ └── ViewFunctions.pm │ │ └── Web.pm │ └── MyApp.pm ├── sql │ ├── mysql.sql │ └── sqlite.sql ├── static │ ├── 404.html │ ├── 500.html │ ├── 502.html │ ├── 503.html │ ├── 504.html │ ├── bootstrap │ ├── css │ │ └── main.css │ ├── img │ ├── js │ │ ├── es5-shim.min.js │ │ ├── jquery-1.10.0.min.js │ │ ├── main.js │ │ ├── micro-location.js │ │ ├── micro_dispatcher.js │ │ ├── micro_template.js │ │ ├── sprintf-0.7-beta1.js │ │ └── strftime.js │ └── robots.txt ├── t │ ├── 00_compile.t │ ├── 01_root.t │ ├── 02_mech.t │ ├── 03_assets.t │ ├── 06_jslint.t │ └── Util.pm ├── tmpl │ ├── include │ │ ├── layout.tt │ │ └── pager.tt │ └── index.tt └── xt 19 directories, 52 files

Slide 29

Slide 29 text

. ├── Build.PL ├── app.psgi ├── config │ ├── development.pl │ ├── production.pl │ └── test.pl ├── cpanfile ├── db ├── lib │ ├── MyApp │ │ ├── DB │ │ │ ├── Row.pm │ │ │ └── Schema.pm │ │ ├── DB.pm │ │ ├── Web │ │ │ ├── Dispatcher.pm │ │ │ ├── View.pm │ │ │ └── ViewFunctions.pm │ │ └── Web.pm │ └── MyApp.pm ├── sql │ ├── mysql.sql │ └── sqlite.sql ├── static │ ├── 404.html │ ├── 500.html │ ├── 502.html │ ├── 503.html │ ├── 504.html │ ├── bootstrap │ ├── css │ │ └── main.css │ ├── img │ ├── js │ │ ├── es5-shim.min.js │ │ ├── jquery-1.10.0.min.js │ │ ├── main.js │ │ ├── micro-location.js │ │ ├── micro_dispatcher.js │ │ ├── micro_template.js │ │ ├── sprintf-0.7-beta1.js │ │ └── strftime.js │ └── robots.txt ├── t │ ├── 00_compile.t │ ├── 01_root.t │ ├── 02_mech.t │ ├── 03_assets.t │ ├── 06_jslint.t │ └── Util.pm ├── tmpl │ ├── include │ │ ├── layout.tt │ │ └── pager.tt │ └── index.tt └── xt 19 directories, 52 files MJC.Z"QQ8FC%JTQBUDIFSQN σΟεύονϟʔ

Slide 30

Slide 30 text

package MyApp::Web::Dispatcher; use strict; use warnings; use utf8; use Amon2::Web::Dispatcher::Lite; any '/' => sub { my ($c) = @_; return $c->render('index.tt'); }; post '/account/logout' => sub { my ($c) = @_; $c->session->expire(); return $c->redirect('/'); }; 1;

Slide 31

Slide 31 text

. ├── Build.PL ├── app.psgi ├── config │ ├── development.pl │ ├── production.pl │ └── test.pl ├── cpanfile ├── db ├── lib │ ├── MyApp │ │ ├── DB │ │ │ ├── Row.pm │ │ │ └── Schema.pm │ │ ├── DB.pm │ │ ├── Web │ │ │ ├── Dispatcher.pm │ │ │ ├── View.pm │ │ │ └── ViewFunctions.pm │ │ └── Web.pm │ └── MyApp.pm ├── sql │ ├── mysql.sql │ └── sqlite.sql ├── static │ ├── 404.html │ ├── 500.html │ ├── 502.html │ ├── 503.html │ ├── 504.html │ ├── bootstrap │ ├── css │ │ └── main.css │ ├── img │ ├── js │ │ ├── es5-shim.min.js │ │ ├── jquery-1.10.0.min.js │ │ ├── main.js │ │ ├── micro-location.js │ │ ├── micro_dispatcher.js │ │ ├── micro_template.js │ │ ├── sprintf-0.7-beta1.js │ │ └── strftime.js │ └── robots.txt ├── t │ ├── 00_compile.t │ ├── 01_root.t │ ├── 02_mech.t │ ├── 03_assets.t │ ├── 06_jslint.t │ └── Util.pm ├── tmpl │ ├── include │ │ ├── layout.tt │ │ └── pager.tt │ └── index.tt └── xt 19 directories, 52 files DPOpHQM 1-"$,@&/7؀ڥม਺ʹΑͬͯDPOpH͕ ੾ΓସΘΔ w खݩͰQMBDLVQͨ͠ͱ͖ʹ͸ EFWFMPQNFOU w σϓϩΠͯ͠ಈ͔͍ͯ͠Δͱ͖ʹ͸ QSPEVDUJPO

Slide 32

Slide 32 text

use File::Spec; use File::Basename qw(dirname); my $basedir = File::Spec->rel2abs(File::Spec->catdir(dirname(__FILE__), '..')); my $dbpath = File::Spec->catfile($basedir, 'db', 'development.db'); +{ 'DBI' => [ "dbi:SQLite:dbname=$dbpath", '', '', +{ sqlite_unicode => 1, } ], };

Slide 33

Slide 33 text

. ├── Build.PL ├── app.psgi ├── config │ ├── development.pl │ ├── production.pl │ └── test.pl ├── cpanfile ├── db ├── lib │ ├── MyApp │ │ ├── DB │ │ │ ├── Row.pm │ │ │ └── Schema.pm │ │ ├── DB.pm │ │ ├── Web │ │ │ ├── Dispatcher.pm │ │ │ ├── View.pm │ │ │ └── ViewFunctions.pm │ │ └── Web.pm │ └── MyApp.pm ├── sql │ ├── mysql.sql │ └── sqlite.sql ├── static │ ├── 404.html │ ├── 500.html │ ├── 502.html │ ├── 503.html │ ├── 504.html │ ├── bootstrap │ ├── css │ │ └── main.css │ ├── img │ ├── js │ │ ├── es5-shim.min.js │ │ ├── jquery-1.10.0.min.js │ │ ├── main.js │ │ ├── micro-location.js │ │ ├── micro_dispatcher.js │ │ ├── micro_template.js │ │ ├── sprintf-0.7-beta1.js │ │ └── strftime.js │ └── robots.txt ├── t │ ├── 00_compile.t │ ├── 01_root.t │ ├── 02_mech.t │ ├── 03_assets.t │ ├── 06_jslint.t │ └── Util.pm ├── tmpl │ ├── include │ │ ├── layout.tt │ │ └── pager.tt │ └── index.tt └── xt 19 directories, 52 files UNQM ςϯϓϨʔτ $c->render('index.tt');

Slide 34

Slide 34 text

"NPO૯͟Β͍

Slide 35

Slide 35 text

஌͓ͬͯ͘ͱྑ͍΋ͷ wίϯςΩετ •$c •use Amon2::Declare; •c()

Slide 36

Slide 36 text

SFRVFTU •$c->req w"NPO8FC3FRVFTU •$c->req->param('foo')

Slide 37

Slide 37 text

SFTQPOTF •$c->render() •$c->create_response() •$c->res_404() •$c->redirect()

Slide 38

Slide 38 text

package MyApp::Web::Dispatcher; use strict; use warnings; use utf8; use Amon2::Web::Dispatcher::Lite; any '/' => sub { my ($c) = @_; return $c->render('index.tt'); }; post '/account/logout' => sub { my ($c) = @_; $c->session->expire(); return $c->redirect('/'); }; 1; SFTQPOTFΛฦ͢

Slide 39

Slide 39 text

SFRVFTUΛड͚औͬͯ SFTQPOTFΛฦ͢

Slide 40

Slide 40 text

͜Ε͚͓͚֮ͩ͑ͯ͹ ͳΜͱ͔ͳΓ·͢

Slide 41

Slide 41 text

No content

Slide 42

Slide 42 text

ϑΟʔϦϯάͰ ΢ΣϒΞϓϦΛॻ͘

Slide 43

Slide 43 text

(ZB[P

Slide 44

Slide 44 text

(ZB[P wHZB[PDPN wը૾ڞ༗αʔϏε wʮॳΊͯʯʹ͸ͱͯ΋ྑ͍୊ࡐ

Slide 45

Slide 45 text

$ amon2-setup.pl Gyazo εέϧτϯΛ࡞੒͢Δ

Slide 46

Slide 46 text

wεΩʔϚఆٛ wTRMTRMJUFTRM wTRMQHTRM

Slide 47

Slide 47 text

CREATE TABLE IF NOT EXISTS image ( id INTEGER NOT NULL PRIMARY KEY, filename VARCHAR(64), src TEXT, ctime INT UNSIGNED NOT NULL, UNIQUE (filename) ); TRMTRMJUFTRM

Slide 48

Slide 48 text

CREATE TABLE IF NOT EXISTS image ( id SERIAL PRIMARY KEY, filename VARCHAR(64), src BYTEA, ctime INT NOT NULL, UNIQUE (filename) ); TRMQHTRM

Slide 49

Slide 49 text

package Gyazo::DB::Schema; use strict; use warnings; use utf8; use Teng::Schema::Declare; base_row_class 'Gyazo::DB::Row'; table { name 'image'; pk 'id'; columns qw(id filename src ctime); }; 1; MJC(ZB[P%#4DIFNBQN

Slide 50

Slide 50 text

wVQMPBEʹΞΫηε wJNBHFEBUBͱͯ͠ը૾Λड͚औΔ wը૾ͷϑΥʔϚοτ͸1/(ͷΈରԠ wΞοϓϩʔυͨ͠ը૾ͷ63-Λฦ͢

Slide 51

Slide 51 text

MJC(ZB[P8FC%JTQBUDIFSQN

Slide 52

Slide 52 text

use DBD::Pg; use Digest::MD5 qw/md5_hex/; post '/upload' => sub { my ($c) = @_; my $imagedata = $c->req->upload('imagedata') or die; # アップロードできる画像は1MB未満とする die unless $imagedata->size < 1024 * 1024; # 画像データを読み込む my $src = do { open my $fh, '<:raw', $imagedata->path or die $!; local $/; <$fh>; }; # PNGであることをシグネチャで判定する die unless $src =~ /^\x89PNG\x0d\x0a\x1a\x0a/; my $filename = md5_hex($src) . '.png'; $c->db->fast_insert('image', { filename => $filename, src => [$src, {pg_type => DBD::Pg::PG_BYTEA}], ctime => time(), }); my $url = $c->req->base . $filename; return $c->create_response(200, [], [$url]); };

Slide 53

Slide 53 text

use DBD::Pg; use Digest::MD5 qw/md5_hex/; post '/upload' => sub { my ($c) = @_; my $imagedata = $c->req->upload('imagedata') or die; # アップロードできる画像は1MB未満とする die unless $imagedata->size < 1024 * 1024; # 画像データを読み込む my $src = do { open my $fh, '<:raw', $imagedata->path or die $!; local $/; <$fh>; }; # PNGであることをシグネチャで判定する die unless $src =~ /^\x89PNG\x0d\x0a\x1a\x0a/; my $filename = md5_hex($src) . '.png'; $c->db->fast_insert('image', { filename => $filename, src => [$src, {pg_type => DBD::Pg::PG_BYTEA}], ctime => time(), });

Slide 54

Slide 54 text

use DBD::Pg; use Digest::MD5 qw/md5_hex/; post '/upload' => sub { my ($c) = @_; my $imagedata = $c->req->upload('imagedata') or die; # アップロードできる画像は1MB未満とする die unless $imagedata->size < 1024 * 1024; # 画像データを読み込む my $src = do { open my $fh, '<:raw', $imagedata->path or die $!; local $/; <$fh>; }; # PNGであることをシグネチャで判定する die unless $src =~ /^\x89PNG\x0d\x0a\x1a\x0a/; my $filename = md5_hex($src) . '.png'; $c->db->fast_insert('image', { filename => $filename, src => [$src, {pg_type => DBD::Pg::PG_BYTEA}], ctime => time(), }); perldoc Plack::Request::Upload

Slide 55

Slide 55 text

post '/upload' => sub { my ($c) = @_; my $imagedata = $c->req->upload('imagedata') or die; # アップロードできる画像は1MB未満とする die unless $imagedata->size < 1024 * 1024; # 画像データを読み込む my $src = do { open my $fh, '<:raw', $imagedata->path or die $!; local $/; <$fh>; }; # PNGであることをシグネチャで判定する die unless $src =~ /^\x89PNG\x0d\x0a\x1a\x0a/; my $filename = md5_hex($src) . '.png'; $c->db->fast_insert('image', { filename => $filename, src => [$src, {pg_type => DBD::Pg::PG_BYTEA}], ctime => time(), }); my $url = $c->req->base . $filename; return $c->create_response(200, [], [$url]); };

Slide 56

Slide 56 text

post '/upload' => sub { my ($c) = @_; my $imagedata = $c->req->upload('imagedata') or die; # アップロードできる画像は1MB未満とする die unless $imagedata->size < 1024 * 1024; # 画像データを読み込む my $src = do { open my $fh, '<:raw', $imagedata->path or die $!; local $/; <$fh>; }; # PNGであることをシグネチャで判定する die unless $src =~ /^\x89PNG\x0d\x0a\x1a\x0a/; my $filename = md5_hex($src) . '.png'; $c->db->fast_insert('image', { filename => $filename, src => [$src, {pg_type => DBD::Pg::PG_BYTEA}], ctime => time(), }); my $url = $c->req->base . $filename; return $c->create_response(200, [], [$url]); }; ΠσΟΦϜ$imagedata->pathͷ಺༰ΛಡΈࠐΉ

Slide 57

Slide 57 text

post '/upload' => sub { my ($c) = @_; my $imagedata = $c->req->upload('imagedata') or die; # アップロードできる画像は1MB未満とする die unless $imagedata->size < 1024 * 1024; # 画像データを読み込む my $src = do { open my $fh, '<:raw', $imagedata->path or die $!; local $/; <$fh>; }; # PNGであることをシグネチャで判定する die unless $src =~ /^\x89PNG\x0d\x0a\x1a\x0a/; my $filename = md5_hex($src) . '.png'; $c->db->fast_insert('image', { filename => $filename, src => [$src, {pg_type => DBD::Pg::PG_BYTEA}], ctime => time(), }); my $url = $c->req->base . $filename; return $c->create_response(200, [], [$url]); }; ਖ਼نදݱͰ1/(ͷγάωνϟΛ൑ఆ

Slide 58

Slide 58 text

die unless $imagedata->size < 1024 * 1024; # 画像データを読み込む my $src = do { open my $fh, '<:raw', $imagedata->path or die $!; local $/; <$fh>; }; # PNGであることをシグネチャで判定する die unless $src =~ /^\x89PNG\x0d\x0a\x1a\x0a/; my $filename = md5_hex($src) . '.png'; $c->db->fast_insert('image', { filename => $filename, src => [$src, {pg_type => DBD::Pg::PG_BYTEA}], ctime => time(), }); my $url = $c->req->base . $filename; return $c->create_response(200, [], [$url]); };

Slide 59

Slide 59 text

die unless $imagedata->size < 1024 * 1024; # 画像データを読み込む my $src = do { open my $fh, '<:raw', $imagedata->path or die $!; local $/; <$fh>; }; # PNGであることをシグネチャで判定する die unless $src =~ /^\x89PNG\x0d\x0a\x1a\x0a/; my $filename = md5_hex($src) . '.png'; $c->db->fast_insert('image', { filename => $filename, src => [$src, {pg_type => DBD::Pg::PG_BYTEA}], ctime => time(), }); my $url = $c->req->base . $filename; return $c->create_response(200, [], [$url]); }; perldoc Teng

Slide 60

Slide 60 text

# PNGであることをシグネチャで判定する die unless $src =~ /^\x89PNG\x0d\x0a\x1a\x0a/; my $filename = md5_hex($src) . '.png'; $c->db->fast_insert('image', { filename => $filename, src => [$src, {pg_type => DBD::Pg::PG_BYTEA}], ctime => time(), }); my $url = $c->req->base . $filename; return $c->create_response(200, [], [$url]); };

Slide 61

Slide 61 text

# PNGであることをシグネチャで判定する die unless $src =~ /^\x89PNG\x0d\x0a\x1a\x0a/; my $filename = md5_hex($src) . '.png'; $c->db->fast_insert('image', { filename => $filename, src => [$src, {pg_type => DBD::Pg::PG_BYTEA}], ctime => time(), }); my $url = $c->req->base . $filename; return $c->create_response(200, [], [$url]); }; $c->create_reponse()ͰϨεϙϯεΛ࡞੒͠ ը૾ͷ63-Λฦ͢

Slide 62

Slide 62 text

wpMFOBNFQOHʹΞΫηε wΞοϓϩʔυͨ͠ը૾ΛݟΒΕΔΑ͏ʹ wը૾͕ଘࡏ͠ͳ͚Ε͹

Slide 63

Slide 63 text

get '/{filename:.+\.png}' => sub { my ($c, $args) = @_; my $image = $c->db->single('image', { filename => $args->{filename}, }); if ($image) { return $c->create_response( 200, ['Content-Type' => 'image/png'], [$image->src] ); } else { return $c->res_404; } };

Slide 64

Slide 64 text

get '/{filename:.+\.png}' => sub { my ($c, $args) = @_; my $image = $c->db->single('image', { filename => $args->{filename}, }); if ($image) { return $c->create_response( 200, ['Content-Type' => 'image/png'], [$image->src] ); } else { return $c->res_404; } }; ਖ਼نදݱ͕࢖͑Δ(perldoc Router::Simple) $args->{filename}Ͱड͚औΔ͜ͱ͕Ͱ͖Δ

Slide 65

Slide 65 text

get '/{filename:.+\.png}' => sub { my ($c, $args) = @_; my $image = $c->db->single('image', { filename => $args->{filename}, }); if ($image) { return $c->create_response( 200, ['Content-Type' => 'image/png'], [$image->src] ); } else { return $c->res_404; } }; status, [headers], [body]

Slide 66

Slide 66 text

wIJTUPSZʹΞΫηε wΞοϓϩʔυͨ͠ը૾Ұཡ

Slide 67

Slide 67 text

get '/history' => sub { my ($c) = @_; my @images = $c->db->search('image', {}, { order_by => {ctime => 'DESC'}, # 降順 } ); return $c->render('history.tt' => { images => \@images, }); };

Slide 68

Slide 68 text

get '/history' => sub { my ($c) = @_; my @images = $c->db->search('image', {}, { order_by => {ctime => 'DESC'}, # 降順 } ); return $c->render('history.tt' => { images => \@images, }); }; DSFOEFS ςϯϓϨʔτ໊UUaςϯϓϨʔτม਺

Slide 69

Slide 69 text

[% WRAPPER 'include/layout.tt' %]
[%- FOR image IN images %] [%- END %]
[% END %] UNQMIJTUPSZUU

Slide 70

Slide 70 text

[% WRAPPER 'include/layout.tt' %]
[%- FOR image IN images %] [%- END %]
[% END %] UNQMIJTUPSZUU $c->render()Ͱ౉ͨ͠ςϯϓϨʔτม਺

Slide 71

Slide 71 text

[% WRAPPER 'include/layout.tt' %]
[%- FOR image IN images %] [%- END %]
[% END %] UNQMIJTUPSZUU image.filename → $image->filename

Slide 72

Slide 72 text

[% WRAPPER 'include/layout.tt' %]
[%- FOR image IN images %] [%- END %]
[% END %] UNQMIJTUPSZUU ~จࣈ࿈݁ ςϯϓϨʔτ಺

Slide 73

Slide 73 text

wෆཁͳίʔυΛऔΓআ͘ w׬੒ IUUQTHJUIVCDPNBLJZNQFSMIFSPLVFYBNQMFHZB[P •$ git add . $ git ci -m 'initial commit'

Slide 74

Slide 74 text

Ϟδϡʔϧґଘ wґଘϞδϡʔϧΛDQBOpMFʹॻ͘ wDBSUPOJOTUBMM

Slide 75

Slide 75 text

requires 'perl', '5.008001'; requires 'Amon2', '4.03'; requires 'Text::Xslate', '1.6001'; requires 'DBD::Pg' , '2.19.3'; requires 'JSON' , '2.50'; requires 'Module::Functions' , '2'; requires 'Plack::Middleware::ReverseProxy', '0.09'; requires 'Teng' , '0.18'; requires 'Test::WWW::Mechanize::PSGI' , '0'; requires 'Time::Piece' , '1.20'; on 'configure' => sub { requires 'Module::Build', '0.38'; requires 'Module::CPANfile', '0.9010'; }; on 'test' => sub { requires 'Test::More', '0.98'; }; DQBOpMF

Slide 76

Slide 76 text

खݩͰಈ͔͢ $ sqlite3 db/development.db < sql/sqlite.sql $ carton install $ carton exec -- plackup app.psgi

Slide 77

Slide 77 text

No content

Slide 78

Slide 78 text

ΞϓϦΛެ։

Slide 79

Slide 79 text

)FSPLVͰ1FSM͸ ࢖͑Δʁ

Slide 80

Slide 80 text

࠷ۙͷ1FSMʹ଍Γͳ͍Ϟϊc)BDIJPKJQN೔Ί͘ΓςοΫτʔΫ IUUQIBDIJPKJQNHJUIVCJPFOUSZQFSM@TIPSUBHFIUNM

Slide 81

Slide 81 text

ެࣜαϙʔτ͸͞Ε͍ͯ·ͤΜ͕ ࣮͸࢖͑·͢

Slide 82

Slide 82 text

εςοϓ

Slide 83

Slide 83 text

UPPMCFMU wIUUQTUPPMCFMUIFSPLVDPN •$ heroku login

Slide 84

Slide 84 text

εςοϓ

Slide 85

Slide 85 text

CVJMEQBDL wCVJMEQBDLͱ͍͏࢓૊ΈͰެࣜͰ αϙʔτ͞Ε͍ͯͳ͍ݴޠ΋ѻ͏͜ͱ͕ Ͱ͖Δ wNJZBHBXBIFSPLVCVJMEQBDLQFSM w҆৺ͷNJZBHBXB͞Μ

Slide 86

Slide 86 text

wࠓճ͸ΑΓεϜʔζʹਐΊΔΑ͏ʹ͢Δ ͨΊʹ wDBSUPOରԠ wBLJZNIFSPLVCVJMEQBDLQFSMUSFF DBSUPO

Slide 87

Slide 87 text

UJQT w1FSMͷόʔδϣϯࢦఆ wQFSMWFSTJPOʹॻ͘ wFDIPQFSMWFSTJPO

Slide 88

Slide 88 text

w)FSPLVʹσϓϩΠ͢Δલʹ wDBSUPOJOTUBMM HJUBEEDQBOpMFTOBQTIPU HJUDPNNJUNEFQT

Slide 89

Slide 89 text

$ heroku create --stack cedar --buildpack ↩ https://github.com/akiym/heroku-buildpack-perl.git\#carton Creating sleepy-dusk-4854... done, stack is cedar BUILDPACK_URL=https://github.com/akiym/heroku-buildpack-perl.git#carton http://sleepy-dusk-4854.herokuapp.com/ | [email protected]:sleepy-dusk-4854.git Git remote heroku added $ git push heroku master -----> Fetching custom git buildpack... done -----> Perl/PSGI app detected -----> Vendoring Perl Using perl 5.16.3 -----> Bootstrapping cpanm/Carton -----> Installing dependencies with Carton -----> Installing Starman -----> Discovering process types Procfile declares types -> (none) Default types for Perl/PSGI -> web -----> Compiled slug size: 20.3MB -----> Launching... done, v5 http://sleepy-dusk-4854.herokuapp.com deployed to Heroku

Slide 90

Slide 90 text

$ heroku create gyoza --stack cedar --buildpack ↩ https://github.com/akiym/heroku-buildpack-perl.git\#carton ΞϓϦͷ໊લΛࢦఆ͢Δ৔߹

Slide 91

Slide 91 text

$ heroku create --stack cedar --buildpack ↩ https://github.com/akiym/heroku-buildpack-perl.git\#carton Creating sleepy-dusk-4854... done, stack is cedar BUILDPACK_URL=https://github.com/akiym/heroku-buildpack-perl.git#carton http://sleepy-dusk-4854.herokuapp.com/ | [email protected]:sleepy-dusk-4854.git Git remote heroku added $ git push heroku master -----> Fetching custom git buildpack... done -----> Perl/PSGI app detected -----> Vendoring Perl Using perl 5.16.3 -----> Bootstrapping cpanm/Carton -----> Installing dependencies with Carton -----> Installing Starman -----> Discovering process types Procfile declares types -> (none) Default types for Perl/PSGI -> web -----> Compiled slug size: 20.3MB -----> Launching... done, v5 http://sleepy-dusk-4854.herokuapp.com deployed to Heroku

Slide 92

Slide 92 text

$ heroku create --stack cedar --buildpack ↩ https://github.com/akiym/heroku-buildpack-perl.git\#carton Creating sleepy-dusk-4854... done, stack is cedar BUILDPACK_URL=https://github.com/akiym/heroku-buildpack-perl.git#carton http://sleepy-dusk-4854.herokuapp.com/ | [email protected]:sleepy-dusk-4854.git Git remote heroku added $ git push heroku master -----> Fetching custom git buildpack... done -----> Perl/PSGI app detected -----> Vendoring Perl Using perl 5.16.3 -----> Bootstrapping cpanm/Carton -----> Installing dependencies with Carton -----> Installing Starman -----> Discovering process types Procfile declares types -> (none) Default types for Perl/PSGI -> web -----> Compiled slug size: 20.3MB -----> Launching... done, v5 http://sleepy-dusk-4854.herokuapp.com deployed to Heroku

Slide 93

Slide 93 text

$ heroku create --stack cedar --buildpack ↩ https://github.com/akiym/heroku-buildpack-perl.git\#carton Creating sleepy-dusk-4854... done, stack is cedar BUILDPACK_URL=https://github.com/akiym/heroku-buildpack-perl.git#carton http://sleepy-dusk-4854.herokuapp.com/ | [email protected]:sleepy-dusk-4854.git Git remote heroku added $ git push heroku master -----> Fetching custom git buildpack... done -----> Perl/PSGI app detected -----> Vendoring Perl Using perl 5.16.3 -----> Bootstrapping cpanm/Carton -----> Installing dependencies with Carton -----> Installing Starman -----> Discovering process types Procfile declares types -> (none) Default types for Perl/PSGI -> web -----> Compiled slug size: 20.3MB -----> Launching... done, v5 http://sleepy-dusk-4854.herokuapp.com deployed to Heroku

Slide 94

Slide 94 text

)FSPLVΛ࢖͏্Ͱͷ஫ҙ఺ w42-JUFΛѻ͏͜ͱ͕Ͱ͖ͳ͍ wͲ͏͢Ε͹͍͍ʁ w)FSPLVͰ͸1PTUHSF42-Λαϙʔτ ͍ͯ͠·͢

Slide 95

Slide 95 text

$ heroku addons:add heroku-postgresql Adding heroku-postgresql on sheltered-earth-3211... done, v6 (free) Attached as HEROKU_POSTGRESQL_CYAN_URL Database has been created and is available ! This database is empty. If upgrading, you can transfer ! data from another database with pgbackups:restore. Use `heroku addons:docs heroku-postgresql` to view documentation. $ heroku pg:psql HEROKU_POSTGRESQL_CYAN_URL < sql/pg.sql NOTICE: CREATE TABLE will create implicit sequence "image_image_id_seq" for serial column "image.image_id" NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "image_pkey" for table "image" NOTICE: CREATE TABLE / UNIQUE will create implicit index "image_filename_key" for table "image" CREATE TABLE

Slide 96

Slide 96 text

$ heroku addons:add heroku-postgresql Adding heroku-postgresql on sheltered-earth-3211... done, v6 (free) Attached as HEROKU_POSTGRESQL_CYAN_URL Database has been created and is available ! This database is empty. If upgrading, you can transfer ! data from another database with pgbackups:restore. Use `heroku addons:docs heroku-postgresql` to view documentation. $ heroku pg:psql HEROKU_POSTGRESQL_CYAN_URL < sql/pg.sql NOTICE: CREATE TABLE will create implicit sequence "image_image_id_seq" for serial column "image.image_id" NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "image_pkey" for table "image" NOTICE: CREATE TABLE / UNIQUE will create implicit index "image_filename_key" for table "image" CREATE TABLE

Slide 97

Slide 97 text

$ heroku addons:add heroku-postgresql Adding heroku-postgresql on sheltered-earth-3211... done, v6 (free) Attached as HEROKU_POSTGRESQL_CYAN_URL Database has been created and is available ! This database is empty. If upgrading, you can transfer ! data from another database with pgbackups:restore. Use `heroku addons:docs heroku-postgresql` to view documentation. $ heroku pg:psql HEROKU_POSTGRESQL_CYAN_URL < sql/pg.sql NOTICE: CREATE TABLE will create implicit sequence "image_image_id_seq" for serial column "image.image_id" NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "image_pkey" for table "image" NOTICE: CREATE TABLE / UNIQUE will create implicit index "image_filename_key" for table "image" CREATE TABLE

Slide 98

Slide 98 text

$ heroku addons:add heroku-postgresql Adding heroku-postgresql on sheltered-earth-3211... done, v6 (free) Attached as HEROKU_POSTGRESQL_CYAN_URL Database has been created and is available ! This database is empty. If upgrading, you can transfer ! data from another database with pgbackups:restore. Use `heroku addons:docs heroku-postgresql` to view documentation. $ heroku pg:psql HEROKU_POSTGRESQL_CYAN_URL < sql/pg.sql NOTICE: CREATE TABLE will create implicit sequence "image_image_id_seq" for serial column "image.image_id" NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "image_pkey" for table "image" NOTICE: CREATE TABLE / UNIQUE will create implicit index "image_filename_key" for table "image" CREATE TABLE

Slide 99

Slide 99 text

$ heroku addons:add heroku-postgresql Adding heroku-postgresql on sheltered-earth-3211... done, v6 (free) Attached as HEROKU_POSTGRESQL_CYAN_URL Database has been created and is available ! This database is empty. If upgrading, you can transfer ! data from another database with pgbackups:restore. Use `heroku addons:docs heroku-postgresql` to view documentation. $ heroku pg:psql HEROKU_POSTGRESQL_CYAN_URL < sql/pg.sql NOTICE: CREATE TABLE will create implicit sequence "image_image_id_seq" for serial column "image.image_id" NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "image_pkey" for table "image" NOTICE: CREATE TABLE / UNIQUE will create implicit index "image_filename_key" for table "image" CREATE TABLE

Slide 100

Slide 100 text

ΞϓϦଆͷઃఆ wIUUQTQPTUHSFTIFSPLVDPN EBUBCBTFT͔Β࡞੒ͨ͠σʔλϕʔεΛ ݟΔ͜ͱ͕Ͱ͖·͢ w%#ͷDPOpHΛઃఆ͠·͢

Slide 101

Slide 101 text

+{ 'DBI' => [ "dbi:Pg:dbname=DATABASE;host=HOST;port=PORT;sslmode=require", 'USER', 'PASSWORD', +{} ], }; DPOpHQSPEVDJUPOQM

Slide 102

Slide 102 text

+{ 'DBI' => [ "dbi:Pg:dbname=DATABASE;host=HOST;port=PORT;sslmode=require", 'USER', 'PASSWORD', +{} ], }; DPOpHQSPEVDJUPOQM

Slide 103

Slide 103 text

$ git push heroku master

Slide 104

Slide 104 text

IUUQTMFFQZEVTLIFSPLVBQQDPN

Slide 105

Slide 105 text

·ͱΊ

Slide 106

Slide 106 text

wॳΊͷҰาͱͯ͠ͷ(ZB[PΞϓϦ w)FSPLVͰ1FSMΛಈ͔͢ͷ͸؆୯ wखܰʹެ։ wࠓճ͸"NPOΛ࢖͍·͕ͨ͠ ΋ͪΖΜ.PKPMJDJPVTͰ΋ಈ͖·͢

Slide 107

Slide 107 text

IUUQTHJUIVCDPNBLJZNQFSMIFSPLVFYBNQMFHZB[P

Slide 108

Slide 108 text

͋Γ͕ͱ͏͍͟͝·ͨ͠ ΞϓϦΛಈ͔ͯ͠ΈͯΘ͔Βͳ͍ͱ͜Ζ͕͋Ε͹!BLJZN·ͰͲ͏ͧ