ようへいの日々精進XP

よかろうもん

2017 年 05 月 15 日(月)

通常運用に

戻していきたい。

先週は

一日の終わりに日記を綴るような精神的な余裕が無かったのが正直なところ。お通夜や葬式、それを取り巻く諸事情、諸事情の方が精神的なダメージが大きかった、大きかった。

午前中は

普通に自宅で作業、午後からは義父宅の縁側に足を伸ばして作業。福岡のベッドタウンっぽい環境で自然も近い。直ぐそばに小川が流れていて、時折吹く風はヒヤッとするくらいだった。

夕飯は

久しぶりによし本の 1000 円セット。

2017 年 05 月 08 〜 14 日、長い一週間だった

わが人生に悔いなし

出棺のタイミングで偶然に流れた義父が好きだった石原裕次郎の「わが人生に悔いなし」。

www.dailymotion.com

5 月 9 日(火) AM 2:02 に義父が急逝した。

諸事情で頻繁にお会いしてお酒を酌み交わす等の義父と婿のような関係は築くことが出来なかったのをとても後悔しているので、この曲が更に染みる。

亡くなった人を送るというプロジェクト

お通夜、葬儀を通して感じたこと。

仏教

仮通夜、お通夜、葬儀を通して、お経を上げて下さった住職の話が面白くて仏教について少し興味が湧いた。

疲れた

お通夜、葬儀だけならこんなに疲れることも無いんだろうけど、これら以外の部分で色々な事が有りすぎて本当に疲れた。人を送るというのはこんなにも大変なのかと思うと、自分が逝く時には何も要らないから、粉々にしたお骨をその辺に適当に撒いてもらって構わないと思った。

2017 年 05 月 07 日(日)

読書

Ruby スクリプティングテクニックを引き続き読み進めている。

黄砂

がやばい。終日モヤがかかった状態で自転車の黒いサドルが黄色になっていた。

ランチ

よし本で天ぷら定食。カラッと揚がっていて美味しゅうございました。

日課

  • (腕立て x 30 + 腹筋 x 30) x 3

夕飯

一昨日頂いたイカを使ってパスタを作る。美味しかった。

ゴールデンウィークスペシャル:AWS X-Ray を出来るだけローカル環境でチュートリアルする(Node.js + Express + DynamoDB Local を使って)

AWS X-Ray について

AWS X-Ray とは何ですか?

www.slideshare.net

上記の資料が簡潔に纏まっていてとても参考になった。

そして、FAQ の記述を引用すると…

開発者は、AWS X-Ray を使用して、本番環境や分散アプリケーション (マイクロサービスアーキテクチャを使用して構築されたアプリケーションなど) を分析およびデバッグできます。X-Ray を使用すると、アプリケーションやその基盤となるサービスの実行状況を把握し、パフォーマンスの問題やエラーの根本原因を特定して、トラブルシューティングを行えます。X-Ray では、アプリケーション内で転送されるリクエストがエンドツーエンドで表示され、アプリケーションの基盤となるコンポーネントのマップも表示されます。X-Ray を使用すると、開発中か本番環境かにかかわらず、シンプルな 3 層アプリケーションから数千のサービスで構成される複雑なマイクロサービスアプリケーションまで、さまざまなアプリケーションを分析できます。

ざっくり言うと…アプリケーションのモニタリングサービスで、競合は NewRelic や Datadog APM になると思う。

AWS X-Ray が出来ること

こちら より転載。

  • パフォーマンスボトルネックとエラーの特定
  • アプリケーション内の特定サービスへの問題を特定
  • アプリケーションのユーザに対する問題のインパクトを特定
  • アプリケーションのサービスコールグラフの可視化

概念

docs.aws.amazon.com

こちら より転載。

キーワード 内容
Trace サービスをまたがった単⼀リクエストに関連したエンドツーエンドのデータ
Segments 1 つのサービスに対応するトレースの⼀部
Sub-segments サービス内でのリモートコールもしくはローカルコンピュートのセクション
Annotations トレースをフィルタするのに使⽤できるビジネスデータ
Metadata トレースに対して付与できるビジネスデータ ただし、トレースのフィルタリングには使⽤されない
Errors 正規化されたエラーメッセージとスタックトレース
Sampling トレースとして取得するアプリケーションへのリエストの割合

AWS X-Ray の画面

Service Map

Service Map とはトレースデータを視覚化したもの。

f:id:inokara:20170507193403p:plain

各ノード(上図だと緑色◯)をクリックするとトレースデータを確認する事が出来る。

Traces

トレース画面ではアプリケーションへのリクエストのレイテンシやステータスコードを確認する事が出来る。

f:id:inokara:20170507193410p:plain

アプリケーションへのトレースデータには ID が付与されていて、トレース ID をクリックするとトレースの詳細を確認する事が出来る。

Trace の詳細

Trace の詳細画面ではアプリケーションのリクエスト開始から終了までの個々の処理が 1 つのセグメントとして詳細を確認する事が出来る。

f:id:inokara:20170507193420p:plain

例えば、上図だと MyAppDynamoDB はそれぞれ別のセグメントとして認識され、アクセスしたリソースや例外が発生した場合等は例外の情報まで確認する事が出来る。

AWS X-Ray を利用する方法

X-Ray SDK

  • Java、.NET、Node.js で利用可能
  • 主要なアプリケーション呼び出し対するメタデータを自動的にキャプチャするフィルタを標準で用意

X-Ray デーモン

  • SDK から UDP でデータを受信し、ローカルバッファとして振る舞う
  • データは毎秒でバックエンドに送信し、ローカルバッファが一杯になるとフラッシュされる
  • Amazon LinuxRHELUbuntuOS X 及び Windows で利用可能

X-Ray API

X-Ray SDK がサポートしていない言語からも X-Ray API を利用してトレースデータを送信することは出来、以下のような API が提供されている。こちらより抜粋。

API 用途
PutTraceSegments AWS X-Rayへセグメントのドキュメントをアップロード
BatchGetTraces IDにより指定されたトレースのリストを検索
GetServiceGraph アプリケーションとコネクション内でサービスを⽰すドキュメントを検索
GetTraceSummaries オプションのフィルタを使⽤して、指定された時間 枠で使⽤可能なトレースのIDとメタデータを取得

AWS CLI

ローカル環境でチュートリアル

環境

以下のような環境でチュートリアルする。

$ cat /etc/lsb-release
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=16.04
DISTRIB_CODENAME=xenial
DISTRIB_DESCRIPTION="Ubuntu 16.04.2 LTS"

$ xray --version
AWS X-Ray daemon version: 1.0.1

$ node --version
v7.10.0

$ java -version
openjdk version "1.8.0_121"
OpenJDK Runtime Environment (build 1.8.0_121-8u121-b13-0ubuntu1.16.04.2-b13)
OpenJDK 64-Bit Server VM (build 25.121-b13, mixed mode)

$ ps aux | grep DynamoDB
ubuntu    6193  0.4 12.2 2658392 124676 pts/1  Sl   05:31   1:03 java -Djava.library.path=./DynamoDBLocal_lib -jar DynamoDBLocal.jar -sharedDb

xray デーモンが動けば EC2 や ECS 上のコンテナである必要が無いのは嬉しい限り。

そして、Node.js アプリケーションは express を利用して、DynamoDB Local テーブルにデータを登録したり、登録データを取得する雑なアプリケーションを利用する。(見よう見真似で作ってみた)

X-Ray デーモンの用意

X-Ray デーモンのインストール

X-Ray デーモンのパッケージを取得して dpkg コマンドでインストールする。

$ wget https://s3.amazonaws.com/aws-xray-assets.us-east-1/xray-daemon/aws-xray-daemon-1.x.deb
$ sudo dpkg -i aws-xray-daemon-1.x.deb

インストール後、--help オプションでヘルプ一覧を確認。

$ xray --help
Usage: X-Ray [options]
        -m      --memory-limit  Change the amount of memory (in MB) that the daemon can use.
        -o      --local-mode    Don\'t check for EC2 instance metadata.
        -b      --bind          Overrides default UDP address (127.0.0.1:2000).
        -r      --role-arn      Assume the specified IAM role to upload segments to a different account.
        -c      --config        Load a configuration file from the specified path.
        -f      --log-file      Output logs to the specified file path.
        -l      --log-level     Log level, from most verbose to least: dev, debug, info, warn, error, prod (default).
        -v      --version       Show AWS X-Ray daemon version.
        -h      --help          Show this screen

X-Ray デーモン設定ファイルの作成

X-Ray 用の設定ファイルを以下のように作成する。(ファイル名は任意の名前で構わない)

Processor:
  Region: "ap-northeast-1"
Logging:
  LogLevel: "warn"
  LogPath: "/tmp/xray-daemon.log"
LocalMode: true

ローカル環境で動かす場合、LocalModetrue にしておく必要がある。尚、false の場合、EC2 メタデータから情報を取得しようとする。

アクセスキー、シークレットアクセスキーを用意

マネージドポリシーの AWSXrayWriteOnlyAccess が付与された IAM User のアクセスキー、シークレットアクセスキーを用意する。

$ cat ~/.aws/credentials
[default]
aws_access_key_id = AKxxxxxxxxxxxxxxxxxxxxx
aws_secret_access_key = xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
region = ap-northeast-1

X-Ray デーモンの起動

以下のように設定ファイルを指定して X-Ray デーモンを起動する。

$ xray --config /path/to/xray-daemon.yaml &
$ ps aux | grep xray
ubuntu    6216  0.0  1.8 230788 18848 pts/1    Sl   05:31   0:05 xray --config /vagrant/xray/xray-daemon.yaml

アプリケーションの用意

Node.js と Express の用意

こちら の記事を参考にさせて頂いて、Node.js と Express を用意した。

$ tree -L 1 sampleapp
sampleapp
├── app.js
├── bin
├── node_modules
├── package.json
├── public
├── routes
└── views

5 directories, 2 files

AWS SDK for JavaScriptX-Ray SDK の導入

以下のように AWS SDK for JavaScriptX-Ray SDK の導入。

$ cd sampleapp
$ npm install aws-sdk --save --no-bin-links
$ npm install aws-xray-sdk --save --no-bin-links

今回、Vagrant の共有ディレクトリにアプリケーションプロジェクトのディレクトリを作成した為、--no-bin-links オプションを付けてインストールした。

$ cat sampleapp/package.json
{
  "name": "sampleapp",
  "version": "0.0.0",
  "private": true,
  "scripts": {
    "start": "node ./bin/www"
  },
  "dependencies": {
    "aws-sdk": "^2.49.0",
    "aws-xray-sdk": "^1.1.0",
    "body-parser": "~1.17.1",
    "cookie-parser": "~1.4.3",
    "debug": "~2.6.3",
    "express": "~4.15.2",
    "jade": "~1.11.0",
    "morgan": "~1.8.1",
    "serve-favicon": "~2.4.2"
  }
}

app.js

以下のような修正を加えた。

$ diff -u app.js.original app.js
--- app.js.original     2017-05-07 10:01:23.000000000 +0000
+++ app.js      2017-05-07 06:15:55.000000000 +0000
@@ -7,9 +7,15 @@

 var index = require('./routes/index');
 var users = require('./routes/users');
+var ddb_set = require('./routes/set');
+var ddb_get = require('./routes/get');

 var app = express();

+// for aws-xray
+var AWSXRay = require('aws-xray-sdk');
+app.use(AWSXRay.express.openSegment('MyApp'));
+
 // view engine setup
 app.set('views', path.join(__dirname, 'views'));
 app.set('view engine', 'jade');
@@ -24,6 +30,11 @@

 app.use('/', index);
 app.use('/users', users);
+app.use('/set', ddb_set);
+app.use('/get', ddb_get);
+
+// for aws-xray
+app.use(AWSXRay.express.closeSegment());

 // catch 404 and forward to error handler
 app.use(function(req, res, next) {

以下を設定することで、Express アプリケーションが提供する受信 HTTP リクエストをトレースする事が出来るようになる。

var AWSXRay = require('aws-xray-sdk');
app.use(AWSXRay.express.openSegment('MyApp'));
...
app.use('/', index);
app.use('/users', users);
app.use('/set', ddb_set);
app.use('/get', ddb_get);
...
app.use(AWSXRay.express.closeSegment());

MyApp がアプリケーション名として X-Ray ダッシュボードの Service Map でも確認する事が出来る。

尚、/set が DynamoDB に値をセットし、/get で DynamoDB のテーブルを Scan して結果を返すようにしてみた。

/set

本当に見よう見真似の書き方でだいぶん怪しいが、/set にアクセスすると以下の処理が実行される。

var express = require('express');
var router = express.Router();

var AWSXRay = require('aws-xray-sdk');
var AWS = AWSXRay.captureAWS(require('aws-sdk'));
var dynamodb = AWSXRay.captureAWSClient(new AWS.DynamoDB({ endpoint: new AWS.Endpoint('http://localhost:8000'), region: 'ap-northeast-1' }));

router.get('/:key', function(req, res) {
  var params = {
      TableName: 'SampleTable',
      Item: {
          'Artist': {"S": req.params.key},
          'SongTitle': {"S": req.params.key}
      }
  };
  dynamodb.putItem(params, function (err, data) {
      if (err) {
          console.log(err, err.stack);
      } else {
          console.log(req.params.key);
          res.render('ddb_set', { message: "OK" });
      }
  });
});

module.exports = router;

以下のように記述して AWS SDK を利用した DynamoDB への呼び出しを AWSXRay.captureAWSClient でラップすることで、DynamoDB を呼び出す処理についてもトレースする。

var AWSXRay = require('aws-xray-sdk');
var AWS = AWSXRay.captureAWS(require('aws-sdk'));
var dynamodb = AWSXRay.captureAWSClient(new AWS.DynamoDB({ endpoint: new AWS.Endpoint('http://localhost:8000'), region: 'ap-northeast-1' }));

DynamoDB Local を利用しているので endpointhttp://localhost:8000 を指定している。

/get

/set 同様にだいぶん怪しいが、/get にアクセスすると以下の処理が実行される。

var express = require('express');
var router = express.Router();

var AWSXRay = require('aws-xray-sdk');
var AWS = AWSXRay.captureAWS(require('aws-sdk'));
var dynamodb = AWSXRay.captureAWSClient(new AWS.DynamoDB({ endpoint: new AWS.Endpoint('http://localhost:8000'), region: 'ap-northeast-1' }));

router.get('/', function(req, res) {
  dynamodb.scan({TableName: "SampleTable",
      Select: "ALL_ATTRIBUTES",
  }, function (err, datas) {
    console.log(JSON.stringify(datas));
    res.render('ddb_get', { datas: datas });
  });
});

module.exports = router;

/set と同様の設定で DynamoDB への呼び出しをトレースするようにする。

アプリケーションを実行して計測してみる

アプリケーションの起動

以下のようにアプリケーションを起動する。

node bin/www &

アプリケーションへの断続的なアクセス

以下のように実行してアプリケーションへ断続的にアクセスさせる。

while true; do curl -s localhost:3000/set/$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 16 | head -n 1 | sort | uniq); sleep 15; curl -s localhost:3000/get; sleep 15; done

暫く動かしつつ X-Ray のダッシュボードを確認すると…

Service Map

以下のように出力されている。

f:id:inokara:20170507193332p:plain

なかなかカッコイイ。

Traces

f:id:inokara:20170507193340p:plain

Trace の詳細

f:id:inokara:20170507194737p:plain

セグメントをクリックすると…

f:id:inokara:20170507194756p:plain

ということで…

フィルタについても

書きたいと思っている。

X-Ray デーモンを使えば

AWS 以外で動いているアプリケーションもトレースする事が出来るのは嬉しい。

X-Ray SDK 待ち

X-Ray SDK に対応している言語が少ないのが残念(Datadog APMPythonRuby と Go をサポートしている)だけど、Datadog APM や NewRelic のようなアプリケーションのパフォーマンスの計測が AWS だけで行えるというのも嬉しい。(※ AWS SDKAWS CLI でトレースデータを送信や取得は出来る)

ゴールデンウィークスペシャル:Elixir チュートリアル (2) 〜 JSON パース編〜

気になっていた Elixir という言語について、以下の資料を写経して引き続きチュートリアルしてみた。

www.slideshare.net

チュートリアル

チュートリアル環境

  • OS は Ubuntu Xenial でお届けします
ubuntu@ubuntu-xenial:~$ cat /etc/lsb-release
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=16.04
DISTRIB_CODENAME=xenial
DISTRIB_DESCRIPTION="Ubuntu 16.04.2 LTS"
  • Elixir は 1.4.1 でお届けします
ubuntu@ubuntu-xenial:~$ elixir --version
Erlang/OTP 19 [erts-8.3] [source-d5c06c6] [64-bit] [smp:2:2] [async-threads:10] [hipe] [kernel-poll:false]

Elixir 1.4.1

JSON パースに必要なライブラリの導入

導入するライブラリ

以下のライブラリを導入する。

ライブラリ名 Github リポジトリ 用途
HTTPoison https://github.com/edgurgel/httpoison HTTP クライアント
Poison https://github.com/devinus/poison JSON パーサー

mix.exs を修正

sample/mix.exs を以下のように修正する。

--- mix.exs.original    2017-05-05 02:36:31.000000000 +0000
+++ mix.exs     2017-05-05 02:37:13.000000000 +0000
@@ -28,6 +28,9 @@
   #
   # Type "mix help deps" for more examples and options
   defp deps do
-    []
+    [
+      { :httpoison, "~> 0.7.2" },
+      { :poison, "~> 1.5" }
+    ]
   end
 end

Ruby だと Gemfile みたいなものですな、きっと。

以下を実行して各モジュールを取得する。

ubuntu@ubuntu-xenial:~$ mix deps.get

以下のように出力される。

Running dependency resolution...
Dependency resolution completed:
  hackney 1.3.2
  httpoison 0.7.5
  idna 1.0.3
  poison 1.5.2
  ssl_verify_hostname 1.0.6
* Getting httpoison (Hex package)
  Checking package (https://repo.hex.pm/tarballs/httpoison-0.7.5.tar)
  Using locally cached package
* Getting poison (Hex package)
  Checking package (https://repo.hex.pm/tarballs/poison-1.5.2.tar)
  Using locally cached package
* Getting hackney (Hex package)
  Checking package (https://repo.hex.pm/tarballs/hackney-1.3.2.tar)
  Using locally cached package
* Getting idna (Hex package)
  Checking package (https://repo.hex.pm/tarballs/idna-1.0.3.tar)
  Using locally cached package
* Getting ssl_verify_hostname (Hex package)
  Checking package (https://repo.hex.pm/tarballs/ssl_verify_hostname-1.0.6.tar)
  Using locally cached package

Ruby だと bundle install みたいなものですが、きっと。

Qiita API を呼び出す

Qiita API を呼び出すコードを書く

以下のように get() 関数を追加する。

defmodule Sample do
  ...
  def get() do
    HTTPoison.get!("https://qiita.com/api/v2/items?query=Elixir")
  end
end

Sample 関数を recompile() した後に Sample 関数を実行すると以下のように Qiita API から Elixr 関連記事を取得する。

iex(5)> Sample.get
...

{"X-Request-Id", "850ad9f5-9b62-4a65-8ffb-d1f37ffb511d"},
{"X-Runtime", "0.432123"}, {"X-XSS-Protection", "1; mode=block"},
{"transfer-encoding", "chunked"}, {"Connection", "keep-alive"}],
status_code: 200}
iex(6)>

おお、簡単ばい。

Body だけを出力したい

以下のように get() 関数を修正して Body だけを抜き出す。

defmodule Sample do
  def get() do
    response = HTTPoison.get!("https://qiita.com/api/v2/items?query=Elixir")
    body = body( response )
    Poison.decode!( body )
  end
  def body( %{ status_code: 200, body: json_body } ), do: json_body
end

改めて recompile() を実行して Sample 関数を実行すると Qiita API レスポンスから Body のみを取得出来る。

iex(17)> Sample.get
...

"user" => %{"description" => "", "facebook_id" => "", "followees_count" => 0,
  "followers_count" => 1, "github_login_name" => nil, "id" => "mdmom",
  "items_count" => 5, "linkedin_id" => "", "location" => "", "name" => "",
  "organization" => "", "permanent_id" => 152421,
  "profile_image_url" => "https://avatars.githubusercontent.com/u/18415389?v=3",
  "twitter_screen_name" => nil, "website_url" => ""}}]

おお。いい感じ。

ちなみに

status_code200 にマッチしたレスポンスを処理しているが、もし status_code200 以外の場合を考慮した場合にはどのように書けば良いのか。

defmodule Sample do
  def get() do
    response = HTTPoison.get!("https://qiita.com/api/v3/items?query=Elixir")
    if response.status_code == 200 do
      body = body( response )
      Poison.decode!( body )
    else
      response.status_code
    end
  end
  def body( %{ status_code: 200, body: json_body } ), do: json_body
end

こんな感じかな…おお、Ruby となんか似てる。こちらによると、cond do ~ という書き方もあるらしい。

defmodule Sample do
  def get() do
    response = HTTPoison.get!("https://qiita.com/api/v3/items?query=Elixir")
    cond do
      response.status_code == 200 ->
        body = body( response )
        Poison.decode!( body )
      response.status_code != 200 ->
        response.status_code
    end
  end
  def body( %{ status_code: 200, body: json_body } ), do: json_body
end

パイプ演算子

変数の受け渡しがちょっと煩わしい時がある

現状は以下のように Qiita からデータを取得して Body を抽出し JSON をデコードするという流れを変数で受け渡しを行っている。

defmodule Sample do
  def get() do
    response = HTTPoison.get!("https://qiita.com/api/v2/items?query=Elixir")
    body = body( response )
    Poison.decode!( body )
  end
  def body( %{ status_code: 200, body: json_body } ), do: json_body
end

まあ、別にコレでもいいかなーと思うけど。

パイプ演算子が便利

Elixir ではパイプ演算子を使えばスマートに書くことが出来る。

defmodule Sample do
  def get() do
    HTTPoison.get!("https://qiita.com/api/v2/items?query=Elixir")
    |> body
    |> Poison.decode!
  end
  def body( %{ status_code: 200, body: json_body } ), do: json_body
end

|> は前の処理の出力を次の処理の第一引数として渡すことが出来る。引数が複数ある場合には、第二引数を以降を () で括って指定が可能。

タイトルのみを抽出する

Qiita API のレスポンス Body

レスポンスを解析すると以下のような内容となっている。

[
%{ "body" => "body1", ..., "title" => "titile1" },
%{ "body" => "body2", ..., "title" => "titile2" },
%{ "body" => "body3", ..., "title" => "titile3" },
]

記事のタイトルのみを取得する

以下のように (1) 〜 (6) を追加する。

defmodule Sample do
  def get() do
    HTTPoison.get!("https://qiita.com/api/v2/items?query=Elixir")
    |> body
    |> Poison.decode!
    |> title_list( [] )                          # 追加 1
  end
  def body( %{ status_code: 200, body: json_body } ), do: json_body
  def title_list( [ head | tail ], titles ) do   # 追加 2
    %{ "title" => json_title } = head            # 追加 3
    added_titles = [ json_title ] ++ titles      # 追加 4
    title_list( tail, added_titles )             # 追加 5
  end
  def title_list( [], titles ), do: titles       # 追加 6
end

以下のような処理が追加されたことになる。

  1. パイプ演算子として [] は空のリストを引数として title_list() 関数を呼び出す
  2. JSON 解析した Body リストの先頭を head それ以降を tail で処理出来るようにする
  3. head で取得した Body リストから title キーをパターンマッチで抽出して json_title に保持する
  4. json_title[] で囲むことでリスト化し、titles に連結して added_titles に保持する
  5. tail に保持しているデータに対して 2 〜 4 の処理を繰り返し呼び出す
  6. tail が空になった際に呼ばれて、titles を出力する

recompile() を実行した後に関数を実行すると以下のように出力される。

iex(45)> Sample.get
["Amazon EC2上にRed Hatインスタンスを作成してElixir Reportをコンソールインストールする手順",
 "Mastodon を Node.js で遊んでみる", "CowboyのHelloWorldまで",
...
 "Elixirで弱々しいAI#3を作る「MeCab辞書差し替え |> CaboChaモジュールの作成」"]

おお。

例のごとく、Ruby で書くと以下のように書けると思う。

irb(main):001:0> require 'net/http'
=> true
irb(main):002:0> require 'json'
=> true
irb(main):003:0> res = Net::HTTP.get(URI.parse('https://qiita.com/api/v2/items?query=Elixir'))
=> ...
...
irb(main):004:0> titles = []
=> []
irb(main):005:0> JSON.parse(res).each { |body| titles << body['title'] }
irb(main):006:0> titles.reverse
=> ["Amazon EC2上にRed Hatインスタンスを作成してElixir Reportをコンソールインストールする手順", "Mastodon を Node.js で遊んでみる", "CowboyのHelloWorldまで", "[翻訳] Elixirでスモールデータを扱う", "Elixir製のプロジェクトをTravisCIで運用するための.travis.yml", "Amazon EC2上のRed Hat Enterprise LinuxにX11転送でElixir ReportをGUIインストールする手順", "Amazon EC2上のRed Hat Enterprise Linuxにリモートデスクトップ接続してElixir ReportをGUIインストールする手順", "Ectoでバリデーション", "【elixir】slackbotをつくり定期的にコメントさせる", "Laravel5系blade中でassetファイルのlastModified追加", "Ectoでカスタムバリデーションを追加する", "Google Compute EngineのRed Hat VMインスタンス作成と、X11転送でGUIインストーラをWindowsから操作する手順 - Elixir Report", "PhoenixでPlugを使用しアクションの前にフィルターを実装する", "Goの構造体にメ タ情報を付与するタグの基本", "Google Compute EngineでのRed Hat Enterprise Linux VMインスタンス作成と、Elixir Reportのコマンドインストール手順", "ElixirでSleep Sort書いてみた", "Elixirで弱々しいAI#1を作る「MeCabで文章パース」", "Elixirのソースコードにバイナリを埋めこむ方法とそのバイナリ表現の生成方法", "Elixirで弱々しいAI#2を作る「構文解釈してオウム返し(ついでにPhoenixでWebアプリ化)」", "Elixirで弱々しいAI#3を作る「MeCab辞書差し替え |> CaboChaモジュールの作成」"]

ということで

sample.ex

これまでのチュートリアルで作成した sample.ex は以下のようになった。

defmodule Sample do
  # def sort( values ) do
  #   Enum.sort( values )
  # end
  def sort( values ), do: Enum.sort( values )
  def match( %{ Yes: "we can" } ), do: "Barack Obama"
  def match( %{ Yes: need } ), do: need
  def match(_), do: "Yes...Not EXIST"
  def match_inner( input_map ) do
    %{ Yes: need } = input_map
    need
  end
  # def nums( [ head | tail ], rows ), do: nums( tail, [ head | rows ] )
  # def nums( [], rows ), do: rows
  def nums( [ head | tail ], rows ) do #...1
    nums( tail, [ head | rows ] )      #...2
  end
  def nums( [], rows ) do              #...3
    rows                               #...4
  end

  def sum_list( [ head | tail ], total ) do
    sum_list( tail, head + total )
  end
  def sum_list( [], total ) do
    total
  end

  def get() do
    HTTPoison.get!("https://qiita.com/api/v2/items?query=Elixir")
    |> body
    |> Poison.decode!
    |> title_list( [] )
  end
  def body( %{ status_code: 200, body: json_body } ), do: json_body
  def title_list( [ head | tail ], titles ) do
    %{ "title" => json_title } = head
    added_titles = [ json_title ] ++ titles
    title_list( tail, added_titles )
  end
  def title_list( [], titles ), do: titles
end

パイプ演算子

って便利だなと思ったけど、エラーハンドリングの方法が見つけられずに悩んでいるところ。

参考

2017 年 05 月 05 日(金)

佐世保

佐世保のゆーちゃんおじさん宅に。その前に道の駅させぼっくす 99 でりょーちゃんおばさんともお茶をしばく。

おばさん、おじさんともにすごく歓迎して下さって嬉しかった。

夜はおじさん宅で呑みながらワイワイ楽しい時間を過ごすことが出来て感謝。

バカという言葉ほど…

おじさんの息子(まさひろ)が酔っ払って言っていた言葉。忘れないうちにつぶやいておいた。

なるほどなって思った。