Herokuで学ぶ、初めてのPerl

7a8922191c1fa60728589a3def4c179f?s=47 akiym
September 20, 2013

 Herokuで学ぶ、初めてのPerl

YAPC::Asia 2013

7a8922191c1fa60728589a3def4c179f?s=128

akiym

September 20, 2013
Tweet

Transcript

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

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

  3. TRMJLBUTVEPOPSH

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

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

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

  7. 4FSWFS

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

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

    8FC "QQMJDBUJPO 4FSWFS
  10. ࣗ෼Ͱॻ͍ͨ΢ΣϒΞϓϦΛ ެ։͍͚ͨͩ͠ͳͷʹ

  11. Ұํɺ)FSPLVͰ͸

  12. 8FC "QQMJDBUJPO )FSPLV

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

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

  15. ΢ΣϒΞϓϦΛॻ͘

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

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

  18. ؀ڥߏங

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

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

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

  22. $ cpanm Amon2 $ amon2-setup.pl MyApp $ carton install $

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

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

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

    carton exec -- plackup app.psgi ΞϓϦΛىಈ
  26. w"NPOಉࠝͷεΫϦϓτ wεέϧτϯΛ࡞੒͢Δ͜ͱ͕Ͱ͖Δ wجຊతʹ͸͜ͷεΫϦϓτΛར༻ͯ͠Ξ ϓϦΛ࡞Γ࢝ΊΔ BNPOTFUVQQM

  27. ΞϓϦͷߏ੒

  28. . ├── 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
  29. . ├── 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 σΟεύονϟʔ
  30. 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;
  31. . ├── 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 DPOpH QM 1-"$,@&/7؀ڥม਺ʹΑͬͯDPOpH͕ ੾ΓସΘΔ w खݩͰQMBDLVQͨ͠ͱ͖ʹ͸ EFWFMPQNFOU w σϓϩΠͯ͠ಈ͔͍ͯ͠Δͱ͖ʹ͸ QSPEVDUJPO
  32. 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, } ], };
  33. . ├── 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');
  34. "NPO૯͟Β͍

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

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

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

  38. 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Λฦ͢
  39. SFRVFTUΛड͚औͬͯ SFTQPOTFΛฦ͢

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

  41. None
  42. ϑΟʔϦϯάͰ ΢ΣϒΞϓϦΛॻ͘

  43. (ZB[P

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

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

  46. wεΩʔϚఆٛ wTRMTRMJUFTRM wTRMQHTRM

  47. 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
  48. CREATE TABLE IF NOT EXISTS image ( id SERIAL PRIMARY

    KEY, filename VARCHAR(64), src BYTEA, ctime INT NOT NULL, UNIQUE (filename) ); TRMQHTRM
  49. 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
  50. wVQMPBEʹΞΫηε wJNBHFEBUBͱͯ͠ը૾Λड͚औΔ wը૾ͷϑΥʔϚοτ͸1/(ͷΈରԠ wΞοϓϩʔυͨ͠ը૾ͷ63-Λฦ͢

  51. MJC(ZB[P8FC%JTQBUDIFSQN

  52. 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]); };
  53. 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(), });
  54. 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
  55. 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]); };
  56. 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ͷ಺༰ΛಡΈࠐΉ
  57. 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/(ͷγάωνϟΛ൑ఆ
  58. 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]); };
  59. 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
  60. # 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]); };
  61. # 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-Λฦ͢
  62. wpMFOBNFQOHʹΞΫηε wΞοϓϩʔυͨ͠ը૾ΛݟΒΕΔΑ͏ʹ wը૾͕ଘࡏ͠ͳ͚Ε͹

  63. 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; } };
  64. 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}Ͱड͚औΔ͜ͱ͕Ͱ͖Δ
  65. 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]
  66. wIJTUPSZʹΞΫηε wΞοϓϩʔυͨ͠ը૾Ұཡ

  67. get '/history' => sub { my ($c) = @_; my

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

    @images = $c->db->search('image', {}, { order_by => {ctime => 'DESC'}, # 降順 } ); return $c->render('history.tt' => { images => \@images, }); }; DSFOEFS ςϯϓϨʔτ໊UUaςϯϓϨʔτม਺ 
  69. [% WRAPPER 'include/layout.tt' %] <div class="row"> [%- FOR image IN

    images %] <div class="col-lg-4"> <a href="[% uri_for('/' ~ image.filename) %]"> <img src="[% uri_for('/' ~ image.filename) %]" width="360" height="360" /> </a> </div> [%- END %] </div> [% END %] UNQMIJTUPSZUU
  70. [% WRAPPER 'include/layout.tt' %] <div class="row"> [%- FOR image IN

    images %] <div class="col-lg-4"> <a href="[% uri_for('/' ~ image.filename) %]"> <img src="[% uri_for('/' ~ image.filename) %]" width="360" height="360" /> </a> </div> [%- END %] </div> [% END %] UNQMIJTUPSZUU $c->render()Ͱ౉ͨ͠ςϯϓϨʔτม਺
  71. [% WRAPPER 'include/layout.tt' %] <div class="row"> [%- FOR image IN

    images %] <div class="col-lg-4"> <a href="[% uri_for('/' ~ image.filename) %]"> <img src="[% uri_for('/' ~ image.filename) %]" width="360" height="360" /> </a> </div> [%- END %] </div> [% END %] UNQMIJTUPSZUU image.filename → $image->filename
  72. [% WRAPPER 'include/layout.tt' %] <div class="row"> [%- FOR image IN

    images %] <div class="col-lg-4"> <a href="[% uri_for('/' ~ image.filename) %]"> <img src="[% uri_for('/' ~ image.filename) %]" width="360" height="360" /> </a> </div> [%- END %] </div> [% END %] UNQMIJTUPSZUU ~จࣈ࿈݁ ςϯϓϨʔτ಺
  73. wෆཁͳίʔυΛऔΓআ͘ w׬੒ IUUQTHJUIVCDPNBLJZNQFSMIFSPLVFYBNQMFHZB[P •$ git add . $ git ci

    -m 'initial commit'
  74. Ϟδϡʔϧґଘ wґଘϞδϡʔϧΛDQBOpMFʹॻ͘ wDBSUPOJOTUBMM

  75. 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
  76. खݩͰಈ͔͢ $ sqlite3 db/development.db < sql/sqlite.sql $ carton install $

    carton exec -- plackup app.psgi
  77. None
  78. ΞϓϦΛެ։

  79. )FSPLVͰ1FSM͸ ࢖͑Δʁ

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

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

  82. εςοϓ

  83. UPPMCFMU wIUUQTUPPMCFMUIFSPLVDPN •$ heroku login

  84. εςοϓ

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

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

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

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

  89. $ 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/ | git@heroku.com: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
  90. $ heroku create gyoza --stack cedar --buildpack ↩ https://github.com/akiym/heroku-buildpack-perl.git\#carton ΞϓϦͷ໊લΛࢦఆ͢Δ৔߹

  91. $ 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/ | git@heroku.com: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
  92. $ 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/ | git@heroku.com: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
  93. $ 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/ | git@heroku.com: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
  94. )FSPLVΛ࢖͏্Ͱͷ஫ҙ఺ w42-JUFΛѻ͏͜ͱ͕Ͱ͖ͳ͍ wͲ͏͢Ε͹͍͍ʁ w)FSPLVͰ͸1PTUHSF42-Λαϙʔτ ͍ͯ͠·͢

  95. $ 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
  96. $ 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
  97. $ 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
  98. $ 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
  99. $ 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
  100. ΞϓϦଆͷઃఆ wIUUQTQPTUHSFTIFSPLVDPN EBUBCBTFT͔Β࡞੒ͨ͠σʔλϕʔεΛ ݟΔ͜ͱ͕Ͱ͖·͢ w%#ͷDPOpHΛઃఆ͠·͢

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

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

    DPOpHQSPEVDJUPOQM
  103. $ git push heroku master

  104. IUUQTMFFQZEVTLIFSPLVBQQDPN

  105. ·ͱΊ

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

  107. IUUQTHJUIVCDPNBLJZNQFSMIFSPLVFYBNQMFHZB[P

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