MPがありません。

$liiu->mp == 0

YAPC::Okinawaにいってきました

けっこう遅くなりましたが、3/3〜3/5の間YAPC::Okinawa 2018 ONNASONに参加するため沖縄に行っていたのでその感想を書きます。
前後に予定が入っていたり、沖縄に行くのはなかなかしんどいのであまり参加に乗り気でなかったのですが、学生は宿泊代と交通費を負担していただけるということで参加することにしました。
学生交通費支援をしてくださった企業の皆様、ありがとうございました!

前夜祭

本祭の日は朝早く出発しなければなかったので前日から沖縄入りしついでに前夜祭に参加してきました。
バーでいろんな方とゆるーく食事をとりながらLTを聞くという感じでした。

本祭

見たトーク

萬國之津梁

charsbarさんによるトークで、氏が参加したPerl関係のイベントやそこでどんなことをしたかについて話されていました。

GraphQL をプロダクション導入した結果

GraphQLがどういうものなのか少し気になっていたので聞くことに。
とりあえずリクエストのパラメーターがちゃんと定義されていて型安全に扱える点がよさそうな感じでした。
GraphQLを使ったアプリの実装や運用についてかなり詳しく話されていたので、参考にして試してみようかと思います。

そろそろPerlでのHTTP/2について触れたい

HTTP2ってHTTP1.1と違って何ができるのか、どんなことが変わるのかといったことやPerlでHTTP2を利用するためのライブラリなどが紹介されていました。
(前半の内容は前にやっていて僕が見ていたかったトークの内容とかぶっていたそうですが、僕は前のトークを見ていなかったのでありがたかったです・・・)
HTTP2でコンテンツを配信するサーバーの実装は並行処理がふえたりと今までとかなり雰囲気が違ってきてきそう、
とても丁寧に解説されていたので、これを気にHTTP2を触ってみようかと思います。
しかしサーバープッシュはHTTP2も含めていろいろな方法が使えるようになっているようですが、どれを使うのがいいんでしょうかね。

High (Availability|Performance) WebSocket for Perl Real-Time Application

Webアプリのサーバープッシュの部分だけ別のサーバーでやるという発想で作られたWebSocketとHTTPの相互変換プロキシサーバkuiperbeltについてのトークでした。
これを使えばLLでもWebSocketを使ったアプリをだいぶいい感じにかけるんではないでしょうか
新しい概念から作っていてもうなんかすげえって感じでした。
ベストトーク賞もこれに投票しました。

2018年春のPerl

Perl5, Perl6の最新情報についてのトークでした。
5, 6月ごろにでるであろうPerl5.28での変更点とかPerl6がどんなふうになっているかは興味があったので見れてよかったです。
Perl5.28での変更点で気になったのはハッシュがkey/valueのスライスでdeleteできるようになることと正規表現文法が拡張されることでした。
他にもスマートマッチやgiven-when文などを実用的にするための変更が入る予定だったそうですが、一部のモジュールが壊れたので没になったそうです。。
ずっとexperimentalなままで早く外れないかなーと思っていただけに残念でした。
やはり後方互換性を維持しながら言語の開発を進めることって大変なんですね。
Perl6はぼちぼちって感じでした。

Inlineモジュールの世界

Inline系モジュールがどのように動いているのかがわかり面白かったです。
メリットやデメリットもまとめられていて、導入時の判断基準にできそうでした。
やはりランタイムエラーが起きた時にPerl側で捕捉できないというのがつらそうで、組み込む言語側でなんとかするしかないのかなあと思いました。

Perlコーディングテクニック2018

最近のPerlの便利な機能やモジュールの紹介をされているトークでした。
個人的に気になったのはModule::Functions, Importer, Module::Findあたりでしょうか。便利そうなのでどんどん使っていきたいと思います。
最近のPerl関連の情報ってあまり多くないのでありがたかったですね。

Perl6のエコシステム

最近のPerl6のエコシステムについてのお話でした。
最初はgithubで配布していましたが、最近はCPANでやるようになっているようです。
また、インストーラーもpandaからzefになっていて、いろいろ変わっているなーと感じました。
mi6というオーサリングツールもあって、簡単にモジュールを挙げられるようになっているそうです。
zefの実装がなかなかいい感じだということで(workaroudも多いそうですがw)参考にしていきたいと思います。

懇親会

懇親会に参加するのは初めてで、いろんな方とお話出来てよかったです。
Mouseの不具合(?)の話をskajiさんとお話できたのは良かったです、なかなか解決は難しそうですがp-r送ろうと思います。

その後

翌日は飛行機が出るまで少し時間があったので首里城に行っていました。
独特の雰囲気や眺めがとても素晴らしかったです。

DBIx::Schema::DSLで外部キー制約を定義するときにトリガを指定する方法

こんな感じででできます

foreign_key user_id => (
  user      => 'id',
  on_delete => 'CASCADE',
):

ドキュメントでは

foreign_key($columns :(Str|ArrayRef), $foreign_table :Str, $foreign_columns :(Str|ArrayRef) )

という風に明記されていませんが、追加の引数はそのままSQL::Translator::Schema::Table::add_constraintに渡されるので上記の方法で追加できます。
そもそもトリガはあまり使うべきではないのかもしれませんが...
他にも渡したいオプションがあればSQL::Translatorのドキュメントを読むとどんなことができるかわかります。

Rustをやってみた

この記事は OIT Advent Calendar 16日目の記事です。

予定変更してだいたい2ヵ月くらい前から卒研の合間に少しずつRustを勉強し始めているのでその感想を書こうかと思います。
まだそんなにがっつり使いこんでいないので参考程度に見てください。

Rustってどんな言語?

C並みの速度で実行することができてかつメモリ管理の安全性と並行処理に関する機能を強化したり、関数型言語由来の機能を取り入れた言語です。
メモリ安全性を保証するためにほかの言語と比べてかなり独特な仕様があります。
www.rust-lang.org

やろうと思ったきっかけ

前々から低レイヤーを触れる言語をできるようになっておきたいという思いがあって、
でもCを極めようとするのはしんどいしC++もがっつりやろうとすると闇が深そうな印象があったので最近出た低レイヤー向けの言語をやろうと考え、
独自のメモリ安全性を保証するための概念が気になったのと、現代的な機能がたくさんあったのでRustをすることにしました。

2ヵ月くらい使ってみた感想

現代的な機能や関数型由来の機能が多くてミスのすくないプログラムを書きやすい

まず変数はデフォルトでimmutableです。mutableな変数を使いたい場合はlet mutと宣言する必要があって、関数に渡すときとかもmutをつけないと値を変更できません。
慣れてないと面倒ですがわかりやすいです。
また、Maybeに相当するOption、Eitherに相当するResultというのがあって、クロージャも当然使えます。
型推論もありますね

メモリ安全性を保証するための機能はすばらしいと思った

Rustでは1つの値に対して変数は1つしか対応させられないようになっています。
そのためか、他のプログラミング言語では変数に値を代入するといいますが、Rustでは束縛するといいます。
そして変数は値に対する所有権をもち、変数がスコープから外れると所有権をもっている値のリソースを解放します。
何らかの値を束縛している変数を新しい変数に束縛しなおした場合は、新しい変数に所有権が移動して前の変数からは値を参照できなくなります。
この仕組みのおかげで、メモリの解放し忘れがなくなります。

テストが気軽に書ける

同じファイル内の関数をテストしたい場合は、テスト実行用の関数を書いて関数を宣言する前に#[test]と記述してコンパイルして
rustc --test ファイル名でテストが実行できます。

所有権になれるのがなかなかつらい

所有権という考え方自体はいいと思うのですが、配列やコレクションの要素にも所有権があって、コレクションを操作するときはそのあたりも考慮しなければいけなくてかなり難しいです。
例えばイテレータを取得するとき、普通のイテレータとコレクションの要素の参照のイテレータがあって、
普通のイテレータを取得すればもとのコレクションの要素の所有権がイテレータのほうに移ってしまってコレクションを束縛していた変数が使えなくなったりとか、
コレクションの要素の参照のイテレータを使うなら要素を使うときに参照外ししないといけなかったりとか、考えることが多いです。
さらにクロージャを使う際も所有権が絡みでいろいろ覚えることがあります。

それと話はそれますが配列の値を参照するときのインデックスもunsginedでないとだめで、あまり考えずに整数型を使ってプログラミングしていると
あとあとキャストしないといけなくて面倒だったりします。

標準入力から値を受け取るのがつらい

競プロの問題解くときとか結構つらいです。
以下C++との比較です。

int num1, num2;
cin >> num1 >> num2;
let mut buffuer = String::new();
io::stdin().read_line(&mut buffer).unuwrap();
let trimed = buffer.trim().to_string();
let nums: Vec<i32> = trimed.split(" ").map(|x| x.parse().unwrap()).collect();
let (num1, num2) = (nums[0], nums[1]);

外部ライブラリを使えばだいぶましになるそうです。

まとめ

Rustはいい言語だとは思うのですが、慣れるまで動くものを作るのにだいぶ時間がかかってしまいそうだと感じています。
慣れても所有権を意識しながらプログラムを書かないといけないので適当に書いているとコンパイルが通るまですごい時間がかかりそう。
そもそも今まで自由度の高い言語をよくさわっていた人間がRustみたいな制約が厳しい言語を急にやろうとするのは厳しいのかなという気もしますが、せっかくなので頑張って使えるようになりたいと思います

Plack::Middleware::Session まとめ

Plack::Middleware::Session と関連することについてのまとめ、ほぼ個人的なメモ
簡単にまとめるつもりだったけど詳しく書くとかなり長くなりそうな気がしてきた...

使い方

  use Plack::Builder;
  builder {
      enable Session => (
          state => Plack::Session::State::*->new,
          store => Plack::Session::Store::*->new,
      );
      $app;
  };

Middlewareを使うときにPlack::Session::Stateを継承したクラスのインスタンスPlack::Session::Storeを継承したクラスのインスタンスを渡す。
何も渡さない場合のデフォルトの引数は以下のようになる

state => Plack::Session::State::Cookie->new(session_key => 'plack_session'),
store => Plack::Session::Store::File->new(
    dir          => $ENV{TMPDIR} || '/tmp',
    serializer   => sub { Storable::lock_nstore( @_ ) },
    deserializer => sub { Storable::lock_retrieve( @_ ) },
),

stateの方はPlack::Session::State::Cookieが使われていて、バックエンドは普通のクッキーだけど、クッキーの有効期限だとかが指定されていないので、
試しに動かしているみたいな時以外はPlack::Session::State::Cookieのドキュメントをちゃんとみて指定したほうがいいと思う。

storeの方はPlack::Session::Store::Fileが使われていて、バックエンドにはStorableが使われている。セッションの保管場所はちゃんと指定しておくべき
Plack::Session::Storeのバックエンドは他に標準でDBI, Cacheのがあって、好みや用途で使いわけられるし、CPANにはRedisバックエンドのモジュールがあったり、なんなら自分でPlack::Session::Storeを継承した独自のモジュールを作って使うこともできる。

Mojoliciousで使う

有名だけど、Mojoliciousのセッション機能はセキュリティ的に貧弱というかそもそもセッションといえないような感じなので、
Mojoliciousで開発しているときでもPlack::Middleware::Sessionを使いたい場面が多い。
鉄板なのはMojoliciousアプリをPSGIアプリとして起動させてPlack::Middleware::Sessionを使う方法だけど、
これだとPSGIサーバーを使わなければならなくなるので、まだどのサーバーを使うか決めていない場合や開発時にMojolicious組み込みのサーバーを使って開発を進めていきたいときなんかは、
Mojolicious::Plugin::PlackMiddlewareというプラグインがあるのでそれを使ってPlack::Middleware::Sessionを使うといい。
(もしかしたらすべてのPlack::Middlewareは動かないかもしれないが少なくともSessionは使えた)

これを使ったログイン機能のMojolicousでの実装例

ORMにTengかAnikiを使っているとする。
本格的なコードでないのであしからず...(あとでちゃんと動くのをあげる)

sub auth {
    my $self = shift;
    $self->plack_session->get('user_id') ? 1 : 0;
}

sub login {
    my $self = shift;
    my $user_id = $self->param('id');
    my $user = $self->db->select('users', {id => $user_id});
    if ( $user->password eq $self->param('password') ) {
        $self->plack_session->set(change_id => 1);
        $self->plack_session->set(user_id => $user->id);
        $self->render(text => 'success');
    } else {
        $self->render(text => 'fail');
    }
}

sub logout {
    my $self = shift;
    $self->plack_session->expire;
}

Plack::Sessionを使うモジュールのテスト

Plack::Sessionのインスタンスを生成するときは要はキーpsgix.sessionにPlack::Session::Stateのインスタンスが格納されていて、キーpsgix.session.optionsにPlack::Session::Storeのインスタンスが格納されているハッシュリファレンスを渡せばよいので、
次のような感じでPlack::Sessionのインスタンスを作ってテストすればいい。

Plack::Session->new({
      'psgix.session'         => Plack::Session::State::Cookie->new(session_key => 'myapp_sid'),
      'psgix.session.options' => Plack::Session::Store::File->new(dir => './etc/sessions'),
})

まだわかってなさそうなところがあるので、間違ってたりしているところがあれば指摘していただけると嬉しいです。

モダーンなPerlの書き方

この記事は OIT Advent Calendar 2016 - Adventar 8日目の記事です。

軽く自己紹介

  • 冷雨 (@_ybrliiu) | Twitter
  • IN科3年
  • Perl大好きなことで有名かもしれません。
  • それより遅刻が多いことで有名ですね
  • 十国志NETっていうオンラインシュミレーションゲームを運営しています!

さて、、

後期の前半にPerlを使った演習があったのですが、例のごとく教えている内容が非常に古く、クソコードを生み出しやすくなるようなことが教えられていたので
昔の汚いPerlスクリプトにひどい目に合わされた*1Perl Monger*2 としては、クソコードを生み出しにくい現代的なPerlを書く方法を周知する必要があると強く感じ、この記事を書くことにしました。
全部書こうとすると長くなりすぎるので、今回は僕が特に重要だと思うもの数点を紹介します。
では早速本題に入っていきます!

Perlのコードを書きはじめる時に必須の記述

Perlのコードを書きはじめるときは、先頭にかならず下記の2行を記述するようにしましょう。

use strict;
use warnings;

何をしているのかというと、 strict プラグマ*3と warnings プラグマというものをロードしています。

strict プラグマとは?

Perlでは、何も宣言せずに使用した変数はなんとグローバル変数になってしまいます。
strictプラグマを使うと変数を必ずmyで宣言*4しなければならないようになります。
myで宣言した変数の有効範囲はブロック内のみなので、サブルーチン、if文、for文の中などでmyで宣言した変数は外部から参照できないようになります。
具体的には以下のような感じになります。

sub hello {
  my $name = 'りーう';
  $global = 'taint';
}
# グローバル変数を宣言するサブルーチンを呼び出し
hello();
# $global はここでも見える
print "HELLO, $global !!\n";
# $name は my で宣言されているので見えない
print "HELLO, $name !!\n";

また、変数を宣言しなければエラーがでるようになるので、変数のタイプミスにも気付きやすくなります。
つまり、strictプラグマを使うことでバグが減ります!!

warnings プラグマとは?

warnings プラグマは様々な警告を出してくれるようになります。
例えば、初期化されていない変数を使用した時や、数値として解釈できない文字列で計算をした時などに警告を出してくれるようになります。
要するに、プログラムが変な動作をしていたら警告して教えてくれるようになるってことです!

というわけで、Perlでコードを書くときは必ず先頭にuse strictuse warningsを書きましょう!

open関数は3引数で呼び出し、ファイルハンドルは変数に格納する

昔の方法でファイルを開いて読み込む処理を書くと、以下のようになります。

open FH, "< log.text";
my @log = <FH>;
close FH;

これには幾つか問題があります。 1つはファイル名とファイルオープンモードを指定するところが同じ文字列であることにより、脆弱性が生まれることです。
2つめはファイルハンドルがグローバル変数のような扱いになることです。*5
このように上記のコードはヤバイので、以下のように書くようにしましょう。

open my $fh, '<', 'log.text';
my @log = <$fh>;
close $fh;

ファイル名とファイルオープンモードが別ですし、ファイルハンドルがローカル変数なので安全です!

& でサブルーチンを呼び出さない

実は&を使わずにサブルーチンを呼び出すこともできます。

sub func { print "test function" }
&func;    # サブルーチン呼び出し
func();   # こう呼び出すこともできる

むしろ、&を使って他の人が作ったサブルーチンを呼び出したりすると、意図しない挙動になったりする場合があるのでやめましょう。 *6 他言語をメインで使っていた人も、&をつけないほうがわかりやすくていいんじゃないでしょうか。

時刻処理には Time::Piece などのモジュールを使う

Perlで時刻を取得するにはlocaltime関数を使用しますが、現在ではCPAN*7に楽に時刻を扱うことができるモジュールが何種類かあるので、それを活用するようにしましょう。
また、Perl5.9.5以降の場合は時刻をオブジェクトとして扱うことができるモジュール、Time::Pieceが標準で添付されています。
つまり、 use するだけで使えるのでPerl5.9.5以降を使っている方は是非Time::Pieceを使うようにしましょう。
Time::Pieceを使うと、localtime関数で書いていた処理が以下のように簡単書くことができます。

my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime;
$year += 1900;
$mon += 1;

print "$year$mon\n";

use Time::Piece;
# localtime 関数を上書きし、 localtime関数を呼び出すとオブジェクトが返えるようになります
my $time = localtime;
print $time->year . " 年 " . $time->mon . " 月 \n";

変数をたくさん宣言しなくていいですし、年や月なども足したりせずに取得できるのでいいですね!
なお、学校にある古いサーバーの幾つかでは動いているPerlのバージョンも低いので、Time::Pieceモジュールがない場合があります。 :(

間接オブジェクト記法でメソッドを呼び出さない

例えば、以下のようなコードです。

use CGI;
my $q = new CGI;  # ここ

これは、CGIモジュールのnewというメソッド(もといコンストラクタ)を、間接オブジェクト記法という方法で呼び出しています。
JavaC++と違って、newは演算子ではありません。
ちなみに、通常のメソッド呼び出しは次のようにします。

my $q = CGI->new;

Perlではメソッド呼び出しは->でやるんですよ。
Java.->になったと考えてください。
さて、引数をとるメソッドを最初の間接オブジェクト記法で書くとどうなるでしょうか。

$object->method($arg1, $arg2);   # これが
method $object $arg1, $arg2;     # こうなる

読みにくいですね...。
更に、最新のPerlでは間接オブジェクト記法で書くと警告を出すようにしようとする流れがあるようです。
将来警告がでるかもしれないことを考えると、やっぱり間接オブジェクト記法はやめておいたほうがいいです。

番外編: use utf8 する

utf8プラグマというものがあります。
これはPerlスクリプト内でunicode文字列を扱えるようにするプラグマです。
これを使うと、length関数でunicode文字列がちゃんと文字数を返すようになったり、変数名などに日本語が使えるようになったりします。
本当はuse strict, use warnings同様先頭に書いたほうが良いのですが、
使いこなすのに少しコツがいるので今回は説明しません。

さいごに

以上ー、現代的なPerlの書き方のうち、重要だと思うものを何点か紹介しました。
大学の環境や教えていることの中には古くて今ではやばいものがたくさんあるので、自分でいろいろ情報収集しましょう :(
また、Perlシェルスクリプト的な使い方もできますし、CPANを利用することでほんとにいろいろできるので、よければ授業以外でも使ってみてください。
最後まで読んでいただいた方、ありがとうございました!

*1: 十国志NETのベースとなったゲームのコードのこと

*2: 訳するとPerl屋とか、Perlの伝道師みたいな Perlを使っている人はよくこう呼ばれます。

*3: プラグマとはロードするとロードしたコード側に何らかの影響を及ぼすモジュールのことです。

*4: 他にもour, stateなどの宣言方法があります

*5: グロブとして扱われます。

*6: 具体的にはサブルーチンプロトタイプが無効になります。

*7: Perlの便利なモジュールがたくさん置かれている、素晴らしい場所です。

Exception::Tinyを継承したクラスで例外を搬送する時、スタックバックトレースを表示させるにはどうしたらいいのか

Perlでスタックバックトレースを伴った例外を発生させるにはCarp::confessを使えばいいわけですが、 下のコードのようにException::Tinyを継承したクラスで例外を発生させる時にconfessを使ってもスタックバックトレースが表示されません。

package MyApp::Exception;

use strict;
use warnings;
use utf8;
use Carp qw/confess/;
use parent qw/Exception::Tiny/;

sub throw {
    my $class = shift;

    my %args;
    if (@_ == 1) {
        $args{message} = shift;
    } else {
        %args = @_;
    }
    $args{message} = $class unless defined $args{message} && $args{message} ne '';

    ($args{package}, $args{file}, $args{line}) = caller(0);
    $args{subroutine} = (caller(1))[3];

    # die -> confess
    confess $class->new(%args);
}

原因はわかりません!!
他の例外搬送クラスはどうなんでしょうかね。
とりあえず当分evalの中でconfessを呼び出してそれをインスタンス変数に格納する、という強引な方法で対処することにします(

sub throw {
    my $class = shift;

    my %args;
    if (@_ == 1) {
        $args{message} = shift;
    } else {
        %args = @_;
    }
    $args{message} = $class unless defined $args{message} && $args{message} ne '';

    ($args{package}, $args{file}, $args{line}) = caller(0);
    $args{subroutine} = (caller(1))[3];
    
    # confessで例外を発生させてその内容を引数に追加
    eval { confess 'start trace.' };
    $args{trace} = $@;

    die $class->new(%args);
}

追記:

perldoc.jp

ドキュメントはちゃんと読みましょう!! Carpのドキュメント、バグのところにオブジェクトは受けつかないと書いてありますね。 とりあえず上記の方法かlongmessを格納する方法しかなさそうです。 誰かいいモジュールを知っている方がいらっしゃればぜひ教えていただきたいです。

PerlからYoutube Data APIを利用して動画を検索する

PerlからYoutubeの動画検索しようとした時のメモ、Youtube Data APIの利用の仕方というよりはWeb APIの使い方って感じですが。

準備

Youtube Data API v3はGoogleのアカウントを持っている上で認証しないと使用できないようなので、ドキュメントを参考に準備をします。
https://developers.google.com/youtube/v3/getting-started?hl=ja
途中で作成するAPIキーを後で使うことになるのでメモっておきましょう。

PerlからWeb APIを叩く方法

基本的な流れは、

リクエストURI作成

Web APIをリクエスト

JSONが返ってくるのでそれをPerlのデータ構造に変換

ってかんじです。

コードはこんな感じになりました。

use v5.22;
use warnings;
use utf8;

use URI;
use LWP::UserAgent;
use JSON::PP;

my $uri = URI->new('https://www.googleapis.com/youtube/v3/search');
$uri->query_form(
  key        => 'APIキー',
  q          => 'GUMI',
  part       => 'id,snippet',
  maxResults => 10,
  type       => 'video',
);

my $ua = LWP::UserAgent->new();
$ua->timeout(10);
my $response = $ua->get($uri);
my $response_json = $response->content;

my $json = JSON::PP->new();
my $response_data = $json->decode($response_json);

my @video_list = map { $_->{id}{videoId} } @{ $response_data->{items} };
say "https://www.youtube.com/watch?v=$_" for @video_list;

これを保存して実行すると

$ perl youtube.pl 
https://www.youtube.com/watch?v=w0dgGYAQVao
https://www.youtube.com/watch?v=XgNG68swi3k
https://www.youtube.com/watch?v=TUGJLqV5hfY
https://www.youtube.com/watch?v=qn0OtcV7NRA
https://www.youtube.com/watch?v=y7p5BQqAgoE
https://www.youtube.com/watch?v=l0YdN5eY11I
https://www.youtube.com/watch?v=vUQoWRAM15Y
https://www.youtube.com/watch?v=rqg90hkSJ7s
https://www.youtube.com/watch?v=MVoZ_P45v7Y
https://www.youtube.com/watch?v=dNIg2oEhgyg

こんな感じに条件に一致する動画のURIのリストが取れます!

参考(Youtube APIで学ぶWeb APIのキホン)

Amazon CAPTCHA