読者です 読者をやめる 読者になる 読者になる

Let's write β

趣味で書いたこととか、RustとLispが好き

AirPodsが届いた

昨年末に注文していたAirPodsが今朝方届きました。

f:id:Pocket7878_dev:20170106131807j:plain

意外と思っているよりも箱が重たかったのが印象的でした。

f:id:Pocket7878_dev:20170106131954j:plain

箱を開けてみるとなかに小さなガムの容器のようにケースが入ってました。

このケースがしっかりした感覚の重さでした。(充電器として使えることを考えると当然かもしれませんが)

そのケースを開けると2秒ほどでiPhoneにペアリング用の画面が表示されて、ペアリングをしたら無事ペアリング完了です。

イヤホンの装着感ですが、元々EarPodsをつかっていて外れたことはまったくなかったのでAirPodsも同様
非常に安定した装着感で普通に作業していて外れる気配は全くありません。

ダブルタップでSiriを起動できるのも便利ですし、発信して、着信側で受話するのもこちらで切断するのもダブルタップでデキるようだったので使い所は色々ありそうです。

音量を上げるのにSiriに話しかけるのは少し面倒ですが、そもそも音量は基本変更することもないですし、
気にするほどではないかなと思います。

もう少し使ってみて充電などを確かめてみたいと思います。

競プロ勉強進捗

競プロ

f:id:Pocket7878_dev:20170105134541p:plain

競技プログラミングをちゃんと勉強しようというのが今年の目標でも合ったので とりあえず必ず1日一歩ずつでもいいから進めようと思う。

とりあえず、一昨日あたりからはじめてABCの残っていたA&B問を全問といておいた。 A&Bはすんなり解けるのを確認した。C++を最近触っていなかったので標準ライブラリで曖昧なところが最初は合ったが 後半になるに連れて基本的に使うライブラリは固定化されてきたので頭に入ったと思う。

計算量見積もって総当りでいいならそれでざっくり解いてしまうのも大事だなと思った。

2016年振り返り & 2017年のやってみたいこと

Github

f:id:Pocket7878_dev:20170104132758j:plain

比較的よく働いたと思う。 ただしこれはプライベートリポジトリも含めたもので、会社のプロダクトをGithubで管理しているので 思いに会社のプロダクトのRails, iOSあたりを毎日なにかしら書いているので、ほぼ埋まっているのは 当たり前かもしれない。

来年はオープンソース周りにも手を出していきたい

OSS

コントリビュート

the-rust-programming-language-ja

Rustの公式ドキュメントの翻訳プロジェクトκeenさんが始めてらして宣伝なさってたのでRustに丁度 興味を持っていた時期だったので参加した。そこそこ訳したように思う

f:id:Pocket7878_dev:20170104133326p:plain

コミット数では一応2位ということになった、行数とかで言うとそうでもない気がする。 一旦の1.6の翻訳が終わってからはあまり手を付けられていない.

github.com

Plan9PortはPlan9 OSのツールをモダンOSに移植したツール群であるが、 僕はこのPlan9Portのツールが大好きで、よく使っていた(Acme, Sam, 9Term)あたりだけだが、 日本語入力をサポートしていない(キーボードのコードをそのまま入力している)ので、それを つけようともがいていて、一応それらしいものがついた。

poketo7878-dev.hatenablog.com

今のところ余りにも日本語入力という目的のために非効率なことをしている部分もあるし、UseCaseとしては特殊だと思うのでPRは送っていない。

github.com

GooglerのサイドプロジェクトとしてRustをバックエンドにつかったエディタが開発されてるぞというのをみて 興味がある時に初期のプロジェクトが有るという機会はあまりないと思って使ってみたところこちらでもMacIMEをサポートしていなかったので plan9portにつけたときと同じ要領で日本語入力をサポートするPRを送った

github.com

こちらのほうが、plan9portとは違ってプログラムが直接文字を扱っているので実装しやすかった。 一方でやはり変換まわりはバックスペースを送りまくって置き換えるという怪しげな実装なので、 そのうち範囲リプレースをバックエンドでサポートしたら置き換えるべきだと思う。 Xcode8には事情により対応しないようなので、僕のローカルで環境を整えるのが大変でそれ移行あまりテストできていないのが悔しいが 人生初のGooglerとのPRのやりとりになった。丁寧にレビューしてくださってありがたかった。

その他は仕事でつかっている幾つかのライブラリにCarthageのアップデートやら、Cocoapod対応やらのPRを送った。

個人プロジェクト

github.com

上でも触れたがPlan9Portのエディタが大好きで使っていたのだが、 どうしても日本語入力をサポートしたとは言えネイティブのエディタには追いつけないので、 個人的に良いと思った機能を模倣したテキストエディタCocoaのNSDocumentをつかって実装している。 https://pocket7878.github.io/ec/

MouseChoordsの機能はサポートしていないが、右クリックに依るジャンプや、 中ボタンによるコマンドの実行などの機能は実装済みである。 また、ネット上でAcmeやSamにほしいと言われていた幾つかの機能も追加機能としてつけている (ドキュメントにはまだ乗せていないが、設定ファイルベースでインデントの仕様やカラーリングを設定でき、 ファイルパスベースでそれらのフラグを個別対応したりするのも検討している)

2016で一番作ってよかったのはこのエディタだともう。会社でRailsアプリケーションを書いているときも、個人でプログラムを書いているときも ドッグフーディングしている。いい面でも悪い面でもAcmeとSamの両方からいいとこ取りをしようと思っていて、 つけていない機能はある意味で「AcmeにはあるけどSamにはないし、あまり僕には必要ないので」と割り切っている。

読んだ本

めぼしいところはコレくらいだった。業務のほうが諸々忙しくあまり読書に時間をかけられなかったのが心残り。

今年は色々読むべきとされてる本の読めていないものたちをを読みたいとおもう。

学んだ言語・技術

  • Rust
  • Golang
  • RxSwift
  • Kotlin
  • Clean Architecture

業務の方は今のところ主にRails, Swift, Javaあたりで済むし、今年の頭あたりはプロダクトの忙しい時期でも合ったので とりあえず作らなければという形で技術というよりは、アーキテクチャをまっとうに組むという感じでやってきたが 年の後半になってプロダクトが落ち着いてきたので、色々と長期的なことを考える時間が取れた。

その中で関数型言語としてRustを学んで勉強会に顔を出したり、 すこしまだ様子見とおもっていたGolangもある程度導入事例が増えてきたので学んでみた。

肌感としてはGolangはまだ仕様に納得行かない部分もあるが(VendoringとかAliasとか)それでも、その納得行かない部分がコミュニティでも 確かに議論の対象にはなっているようだったので雲行きは怪しくないだろうと思って今後プロダクトに導入を進めることにした。 手始めに一部のサーバーサイドの機能をgRPCで分割して導入した。

RxSwiftやKotlin, Clean Architectureについては、 モバイルアプリケーションのリファクタリングをすすめるにあたって大きな方針として検討し、導入を開始した。

今年の目標

競技プログラミングの勉強

競技プログラミングはなかなか手を出しづらく、学生時代になんどか取り組んだがそのままにしていたが 今年はちゃんと取り組みたいと持っている。

ブログをちゃんと書く

ブログをちゃんと書こうともう、と言うかそもそもブログのネタがちゃんと見つかるように色々とやりたい

WebVR

A-Frameあたりをちゃんと触ってWebVR周りに触れていきたいと思う。

RustでWebプログラミング No.4 ~ MySQLに接続する~

振り返り

前回

poketo7878-dev.hatenablog.com

は、HandlebarでHTMLページをテンプレートから生成し、表示するというところまで作りました。今回は、MySQLに接続してデータを取得し、それをテンプレートに埋め込んで表示するということをやりました。

Iron-R2D2-Mysql-MiddlewareをCargo.tomlについか

Rust界隈はどうも主要なデータベースツール周りがPostgresをメインにサポートしていることが多いようで、MySQLをサポートしているツールがあまりない印象です。

そこで、拙作の

github.com

を利用します。これはIronのMiddlewareでR2D2-MySQLというコネクションプールライブラリを利用して、リクエストハンドラ中からMySQLへのコネクションを利用することを可能にするものです。

[dependencies]
iron = "0.4.0"
router = "0.4.0"
handlebars-iron = "0.18.0"
params = "0.4.1"
r2d2 = "0.7.0"
r2d2_mysql="*"
iron-r2d2-mysql-middleware = { git = "https://github.com/pocket7878/iron-r2d2-mysql-middleware.git" }


[dependencies.mysql]
version = "3.0.0"
default-features = false
features = ["socket"]

ChainへのMiddlewareの追加

extern crate iron_r2d2_mysql_middleware;

...

use iron_r2d2_mysql_middleware::{MysqlMiddleware, MysqlReqExt};

まず、iron_r2d2_mysql_middlewareを利用可能にして下さい。

そして、

let mysql_middleware = MysqlMiddleware::new("mysql://root:12345678@localhost/test").unwrap();
chain.link_before(mysql_middleware);

のように、MysqlMiddlewareを作成して前回同様, MiddlewareのChainに追加して下さい。 今回のこのMiddlewareはリクエストがメインのハンドラに落ちるに、 コネクションプールにつなぐための準備を設定するものなので、BeforeMiddlewareと呼ばれるものになっています

前回の、HandlebarのMiddlewareはハンドラが処理を行ったに、レスポンスの中身をみて、 Templateが設定されていたら処理するものなので、AfterMiddlewareになっています。

そのため、Chainへの追加の方法がlink_beforeであることに注意して下さい。

ハンドラ中でのMySQLへの接続

まず、コード全体は以下のとおりです:

    fn user_index(req: &mut Request) -> IronResult<Response> {
        let mut resp = Response::new();
        let mut data = HashMap::new();
        let mut con = req.db_conn();
        let mut users = vec![];
        for result in con.prep_exec("SELECT id, first_name, last_name FROM users", ()).unwrap() {
            let mut row = result.unwrap();
            let mut user_data: HashMap<String, String> = HashMap::new();
            let user_id: u64 = row.take("id").unwrap();
            let last_name: String = row.take("last_name").unwrap();
            let first_name: String = row.take("first_name").unwrap();
            user_data.insert(String::from("user_id"), format!("{}", user_id));
            user_data.insert(String::from("user_name"), format!("{} {}", last_name, first_name));
            users.push(user_data);
        }
        data.insert(String::from("users"), users);
        resp.set_mut(Template::new("user_index", data)).set_mut(status::Ok);
        return Ok(resp);
    }

肝となるのは

let mut con = req.db_conn();

の部分です、これで、コネクションプールからMySQLへの接続を1つ取り出してきています。 このインスタンス

outersky.github.io

インスタンスとなっています。 このインスタンスを利用して、クエリを発行し、データベースから取り出した値を利用してuser_indexテンプレートに流してやっています。

RustでWebプログラミング No.3 ~ HandlebarでHTMLテンプレート~

rust-lang

振り返り

前回 poketo7878-dev.hatenablog.com

までにRouterで複数URLに対応したHTTPサーバーを建てられるようになりました。 しかし、出力されるのは常に文字列でHTMLのページではありませんでした。

今回はHandlebarというテンプレートエンジンを利用してHTMLページをユーザーに表示できるようにしました。

handlebar-ironをCargo.tomlに追加

前回までと同様にCargo.tomlに以下のdependencyを追加します。

handlebars = "0.20.5"

また、今回はHTMLページでformを表示し、POSTしてみたいと思うので、 formやURLなどのパラメータをパース及び取得するためのcrateもdependencyに追加しておきます。

params = "0.4.1"

全体

まずはコードの全体です

extern crate iron;
extern crate router;
extern crate handlebars_iron as hbs;
extern crate params;

use std::collections::HashMap;
use std::error::Error;
use iron::prelude::*;
use iron::status;
use router::{Router, url_for};
use hbs::{Template, HandlebarsEngine, DirectorySource};

fn main() {

    fn top_handler(req: &mut Request) -> IronResult<Response> {
        let mut resp = Response::new();
        let mut data = HashMap::new();
        data.insert(String::from("greeting_path"),
                    format!("{}", url_for(req, "greeting", HashMap::new())));
        resp.set_mut(Template::new("index", data)).set_mut(status::Ok);
        return Ok(resp);
    }

    fn greet_handler(req: &mut Request) -> IronResult<Response> {
        use params::{Params, Value};
        let map = req.get_ref::<Params>().unwrap();
        return match map.find(&["name"]) {
            Some(&Value::String(ref name)) => {
                Ok(Response::with(
                    (status::Ok,
                     format!("Hello {}", name).as_str())))
            },
            _ => Ok(Response::with((status::Ok, "Hello world")))
        }
    }

    //Create Router
    let mut router = Router::new();
    router.get("/", top_handler, "index");
    router.post("/greet", greet_handler, "greeting");
    
    //Create Chain
    let mut chain = Chain::new(router);
    // Add HandlerbarsEngine to middleware Chain
    let mut hbse = HandlebarsEngine::new();
    hbse.add(Box::new(
        DirectorySource::new("./src/templates/", ".hbs")));
    if let Err(r) = hbse.reload() {
        panic!("{}", r.description());
    }
    chain.link_after(hbse);
    
    println!("Listen on localhost:3000");
    Iron::new(chain).http("localhost:3000").unwrap();
}

細かく見ていきましょう

Router

    //Create Router
    let mut router = Router::new();
    router.get("/", top_handler, "index");
    router.post("/greet", greet_handler, "greeting");

前回同様Routerを作成しています。今回はgreetの名前はurlの一部ではなくpostのパラメータとして送信することにしました。

Chain

let mut chain = Chain::new(router);

前回は作成したRouterを直接HandlerとしてIronに渡していましたが、今回はChainというオブジェクトを作成しています。 これはMiddlewareの連鎖を表現したオブジェクトで、ここにミドルウェアをつなげていくことができます。

HandlebarEngine

    // Add HandlerbarsEngine to middleware Chain
    let mut hbse = HandlebarsEngine::new();
    hbse.add(Box::new(
        DirectorySource::new("./src/templates/", ".hbs")));
    if let Err(r) = hbse.reload() {
        panic!("{}", r.description());
    }
    chain.link_after(hbse);

ここではHandlebarsEngineを作成し、 Handlebarのテンプレートファイルを設置するディレクトリのルートディレクトリ、 Handlebarのファイルとして扱うファイルの拡張子を指定しています

今回の場合は./src/templates/をルートディレクトリとして指定しています、また拡張子は.hbsとしています。

このようにディレクトリを指定したら

    if let Err(r) = hbse.reload() {
        panic!("{}", r.description());
    }

ディレクトリ中のファイルをロードしています。

そして最後に先ほどのChainの末尾にHandlebarsEngineを追加しています。

chain.link_after(hbse);

テンプレートの描画

    fn top_handler(req: &mut Request) -> IronResult<Response> {
        let mut resp = Response::new();
        let mut data = HashMap::new();
        data.insert(String::from("greeting_path"),
                    format!("{}", url_for(req, "greeting", HashMap::new())));
        resp.set_mut(Template::new("index", data)).set_mut(status::Ok);
        return Ok(resp);
    }

indexページのハンドラ中では、レスポンスのボディとしてTemplate::new("index", data)を指定しています。 このようにすることで、さきほどしたルートディレクトリからの相対パスで指定したhandlebarファイルを指定できます。

ファイル中で利用する変数のデータはJSONに変換可能なデータ構造、今回の場合はHashMap等で渡すことができます。 参考URL

トップページのテンプレートファイルは

<h1>Hello, world</h1>

<form action={{greeting_path}} method="POST">
      <input type="text" name="name"/>
      <input type="submit" value="Hello!"/>
</form>

このようにformのaction URLをgreetingのページのURLに設定しています。

このようにすると

https://gyazo.com/894654c0137504b7a682cfe47449c0a6

のようにformの表示されたHTMLページが描画されます。

POSTのパラメータの処理

さて、先ほどのページのformに値をいれてsubmitするとその値はnameというパラメータ名でPOSTされてきます。

この値をハンドラ中で利用するには

        let map = req.get_ref::<Params>().unwrap();
        return match map.find(&["name"]) {
            Some(&Value::String(ref name)) => {
                Ok(Response::with(
                    (status::Ok,
                     format!("Hello {}", name).as_str())))
            },
            _ => Ok(Response::with((status::Ok, "Hello world")))
        }

このように、まずリクエスト中からParams型の値を取り出し、 Paramsに対してfindメソッドで値を検索した結果のOptionalをmatchすることで利用できます。

今回の場合は取り出してきたnameを使ってHello 名前という形で表示しています。 https://gyazo.com/7464d8053c1c13b7262281b9c23c44fd

まとめ

今回は前回までテキストのみの表示だったページをHandlebarのテンプレートエンジンを用いることでHTMLの表示を可能にしました。 また、POST等のパラメータを取得するためのparams crateを利用しパラメータをハンドラ中で取得できるようになりました。

次回はHTMLだけでなくJSONなどのデータ型をユーザーからのリクエストに応じて返せるようにしたいと思います。

RustでWebプログラミング No2 ~ Routerをつかって複数ルート~

rust-lang

前回の記事では、非常にシンプルなHelloWorldを表示するだけのサーバーを作成しIronの基礎を紹介しました。

今回はRouterと呼ばれるMiddlewareを利用して複数のURLに対応したサーバーを作成しました。

routerをCargo.tomlに追加

routerを利用するためにCargo.tomlに以下のようrouterをに追加します

[dependencies]

...other dependencies...

router = "*"

Router

コードの完成形は以下の様です:

extern crate iron;
extern crate router;

use iron::prelude::*;
use iron::status;
use iron::modifiers::{Redirect};
use router::{Router, url_for};

fn main() {

    fn top_handler(_: &mut Request) -> IronResult<Response> {
        Ok(Response::with((status::Ok, "Welcome!")))
    }

    fn redirect_handler(req: &mut Request) -> IronResult<Response> {
        let ref top_url = url_for(req, "index", ::std::collections::HashMap::new());
        return Ok(Response::with((status::Found, Redirect(top_url.clone()))))
    }

    fn greet_handler(req: &mut Request) -> IronResult<Response> {
        let ref router = req.extensions.get::<Router>();
        let ref name = router
            .unwrap()
            .find("name")
            .unwrap();
        return Ok(Response::with(
            (status::Ok,
             format!("Hello {}", name).as_str())
        ));
    }
    
    let mut router = Router::new();
    router.get("/", top_handler, "index");
    router.get("/greet/:name", greet_handler, "greeting");
    router.get("/to-top", redirect_handler, "to-top");

    Iron::new(router).http("localhost:3000").unwrap();
    println!("Listen on localhost:3000");
}

細かく見ていきましょう

Router

let mut router = Router::new();
router.get("/", top_handler, "index");
router.get("/greet/:name", greet_handler, "greeting");
router.get("/to-top", redirect_handler, "to-top");

この部分ではRouterを作成し、いくつかのルーティングを登録しています。

.getの部分はHTTPメソッドを意味していて、.post,.put, .deleteのようにその他各種HTTPメソッドに対応しています。 引数はそれぞれ、マッチするURL, その時に呼び出されるハンドラ、ルーティングIDを定義しています。

ルーティングIDはそのルーティングを後から参照する際に利用できる識別子となっています。

URL

URL中では:paramのようにすることで、後からURLの一部を取得することができます。 また、*を使うことでその部分は任意にマッチするようになります。

URLパラメータの取得

    fn greet_handler(req: &mut Request) -> IronResult<Response> {
        let ref router = req.extensions.get::<Router>();
        let ref name = router
            .unwrap()
            .find("name")
            .unwrap();
        return Ok(Response::with(
            (status::Ok,
             format!("Hello {}", name).as_str())
        ));
    }

このハンドラは/greet/:nameというURLにマッチしたときに呼び出されるようになっているハンドラです。 ハンドラ中で

let ref router = req.extensions.get::<Router>();

とすることでRouterを取得しています。extensionsはMiddlewareがリクエストを拡張するのに利用するもので、 今回の場合はRouterは自身をextensionsに保存しています。

ルータを通してURLのパラメータを取得することができます。findメソッドにURLパラメータ名(この場合は"name")を指定すると パラメータがOptionで手に入るので今回の場合は単純にunwrapしています。

url_for

URL to-topは、アクセスするとトップページにリダイレクトされるURLです。

ハンドラは以下のとおりです:

    fn redirect_handler(req: &mut Request) -> IronResult<Response> {
        let ref top_url = url_for(req, "index", ::std::collections::HashMap::new());
        return Ok(Response::with((status::Found, Redirect(top_url.clone()))))
    }

url_forを使うことでルーティングIDに対応するURLを取得することができます。

レスポンスにRedirectを利用することでユーザーをリダイレクトすることができます。

まとめ

今回はRouter Middlewareを利用して複数のルーティングに対応したHTTPサーバーを実装しました。 また、url_forを利用してルーティング登録時に設定したルーティングIDからURLを取得することができます。

次回はテンプレートエンジンを利用してHTMLページの表示をやってみたいと思います。

RustでWebプログラミング No.1 ~ IronのインストールとHello World~

rust-lang

Hello World

hello_worldバイナリプロジェクトを作ろう

$ cargo new hello_world --bin

サーバーはmain関数をもったプログラムに成るので、--binオプションを忘れに付けましょう。

Cargo.tomlにIronを追加する

[dependencies.iron]
version = "*"

まずCargo.tomlにironのdependenciesを追加しましょう。 これで、このアプリケーションでiron createを利用できるようになりました。

main関数を書く

次に、以下のように書くことでIronのサーバーをlocalhost:3000で立ち上げることができます。

extern crate iron;

use iron::prelude::*;
use iron::status;

fn main() {
    fn hello_world(_: &mut Request) -> IronResult<Response> {
        Ok(Response::with((status::Ok, "Hello, world")))
    }

    let _server = Iron::new(hello_world).http("localhost:3000").unwrap();
    println!("Lisning on port 3000");
}

こまかく見ていきましょう。

extern crate iron;

use iron::prelude::*;
use iron::status;

については通常のcrateのようにironをロードしているようです。

Handler

    fn hello_world(_: &mut Request) -> IronResult<Response> {
        Ok(Response::with((status::Ok, "Hello, world")))
    }

この関数は、なにやらRequestのミュータブルな参照をうけとって、 IronResultにくるんだResponseを返しているようです。

この部分は、Ironのドキュメントに依るとHandlerと呼ばれるようです。

この関数がリクエスト時に呼び出され、Requestに対応するResponseを返すことで、 ユーザーにデータが返されるようです。

pub trait Handler: Send + Sync + 'static {
    fn handle(&self, &mut Request) -> IronResult<Response>;
}

という定義で、

impl<F> Handler for F where F: Send + Sync + 'static + Fn(&mut Request) -> IronResult<Response>

が定義されているようなので、関数をHandlerとして利用できるようです。

参考

Trait iron::middleware::Handler

Response

サーバーからのリクエストに対する結果を返すときに利用するResponseですが

pub struct Response {
    pub status: Option<Status>,
    pub headers: Headers,
    pub extensions: TypeMap,
    pub body: Option<Box<WriteBody>>,
}

のような定義になっています。

Responseの作成のところでResponse::with((...))のようにして、statusbodyを設定しているようです。 これらは、Modifierという仕組みになっており、タプルのそれぞれの要素をつかってどのようにデータを変更するかを 別途定義しているようです。

Server

let _server = Iron::new(hello_world).http("localhost:3000").unwrap();

HTTPサーバーを起動するには、このようにIron::newHandlerを渡してやれば良いようです。 今回は、先ほど定義したhello_world関数をHandlerとして利用しています。

httpメソッドに渡した"localhost:3000"という文字列がパースされて、localhostの3000番ポートでHTTPサーバーが起動するようです。

起動

このようにして書いたプロジェクトをcargo runしてやれば無事http://localhost:3000でサーバーが起動します。

まとめ

今回はIronをつかって非常に単純なHTTPサーバーを起動する手順を確認しました。

僕が働いているAzit.incでは一緒に働けるエンジニアを募集しています!
採用情報 — 株式会社アジット|Azit Inc.