技術メモ

プログラミングとか電子工作とか

Nuxt.jsでESLint(Prettier)が使いたいけどよくわかんなくなったのでまとめた

f:id:ysmn_deus:20190810104633p:plain

こんにちは、靖宗です。
特にチームでのJavaScript案件はやってないんですが、チームじゃなくてもESLintを導入するべきなんじゃないかと感じ、導入してみることにしました。
とはいえよくわかんないので記事にしながら学習します。
とりあえず今回はTypeScriptを対象としていません。別の記事にまとめます。

ESLint

ESLintとは?

ESLintは2013年の6月にNicholas C. Zakasさん(オライリーの「ハイパフォーマンスJavaScript」などの著者)によって作られたツールです。
実行前にJavaScriptソースコードを解析するもので、基本的な間違いを検出するだけでなく、フォーマット(セミコロンの有無など)も検出して指摘してくれます。
まさにチーム開発では重宝されるツールでしょう。

他に似たツールにJSLintなどがあったのですが、最近ではあまり聞かなくなりました。
基本的にはESLint一択なんでしょう。

導入方法

使うプロジェクトでnpmかyarnでインストールすれば良さげです。

npm install eslint --save-dev

or

yarn add -D eslint

設定ファイルを作らなければただ容量を食ってるだけの代物ですが、基本準備はこれだけです。
ただ、今回はNuxt.jsで使う想定なので、nuxtのプロジェクトを作成しながら設定していきたいと思います。

Nuxt.jsのプロジェクトを作成する

まずはコマンドでnuxtのプロジェクトを作成します。

yarn create nuxt-app eslint-test

プロジェクト名はeslint-testとしました。

...
? Choose linting tools ESLint, Prettier
? Choose test framework Jest
? Choose rendering mode Universal (SSR)

nuxtのウィザードに従ってESlintとPrettierを有効にしました。
Jestまで色々書くか微妙ですが、とりあえず導入しておきます。

.eslintrc(ESLintの設定ファイル)の作成

.eslintrcを作成します。
この設定ファイルは6種類の記述手段があるらしく

  • .eslintrc.js
  • .eslintrc.yaml
  • .eslintrc.yml
  • .eslintrc.json
  • .eslintrc
  • package.json

というファイルに記述することができるそうです。
package.jsonに記述する方法はファイルが少なくて済むんですが、煩雑になるので避けた方が良いのでは?と思います。
nuxtなどで用意すると.eslintrc.jsが勝手に生成されるので、基本これで行きます。

ちなみに一番最初に書かれている中身がこれ

module.exports = {
  root: true,
  env: {
    browser: true,
    node: true
  },
  parserOptions: {
    parser: 'babel-eslint'
  },
  extends: [
    '@nuxtjs',
    'prettier',
    'prettier/vue',
    'plugin:prettier/recommended',
    'plugin:nuxt/recommended'
  ],
  plugins: [
    'prettier'
  ],
  // add your custom rules here
  rules: {
  }
}

とりあえず詳細は後回しにするとして、設定ファイルはcreate nuxt-appで用意すれば既に生成されています。

ESLintの実行

試しにstore/index.jsを生成して適当な事を書いてみます。

function helloWorld() {
  console.log('hello!')
}

helloWorld();

ESLintをコマンドから実行してみます。
ESLintは

eslint [JavaScriptファイル名]

で実行可能です。
プロジェクトのルートフォルダで

node_modules/.bin/eslint store/index.js

を実行します。

プロジェクトのパス/eslint-test/store/index.js
  5:13  error  Delete `;`  prettier/prettier

✖ 1 problem (1 error, 0 warnings)
  1 error and 0 warnings potentially fixable with the `--fix` option.

prettier/prettierのルールでセミコロンが着いてる!しばくぞ!と怒られています。どうやらprettierの方ではデフォルトでセミコロンは悪役の様です。
こういったフォーマットの規約などをチェックするのがESLintの役割です。

--fix

ちなみにエラー分を見ると「1 error and 0 warnings potentially fixable with the --fix option.」とあります。こういう場合は

node_modules/.bin/eslint store/index.js --fix

と実行すると、該当箇所を良い感じに修正してくれます。
ただし、後述するようにこの辺は全部Prettierに任せようと思います。
(とは言っても実行されるコマンドは同じみたいですが)

ESLintの設定ファイル

ここでようやくESLintの設定ファイルに関して見ていきましょう。

ルールの追加(rules)

ESLintのルールを追加するには設定ファイルに追記していきます。
細かい設定は.eslintrc.jsrulesに記載します。

...
    "rules": {
        "semi": ["error", "always"]
    }

設定項目はESLintの公式サイトや諸先輩方の設定ファイルを参考にさせて貰うとして、基本的には
設定項目: [設定のオンオフ, オプションの値]
という構成になります。設定のオンオフはerror(あったらエラーを送出する), warn(警告だけする), off(無視)が設定できます。
オプションは設定項目で色々ですが、例えばsemiではalwaysにすると「無いと怒る」、neverにすると「あると怒る」といった感じです。

ルートの指定(root)

nuxtのウィザードでESLintの設定ファイルを生成するとrootという設定項目があります。
これは「プロジェクトルートがここなので、これ以上親の階層は気にしなくて良い」という設定のようです。
ESLintは階層ごとに配置することもできるそうなので(たぶんそんなに頻繁にすることはない)、どこまで親をたどるのか明確にするときに設定するのでしょう。
基本そのままで大丈夫そうです。

環境の設定(env)

初期設定ではbrowsernodeがtrueになっていると思います。
スクリプト実行環境を指定出来るオプションのようで、ブラウザとNode.jsで実行されることを想定しています。
基本このままでも大丈夫そうですが、念のためes6jestを追加しておきます。

...
  env: {
    browser: true,
    node: true,
    es6: true,
    jest: true
  },
...

parserOptionsの設定(parserOptions)

上記でes6を追加しましたが、それだけではimport文などのES Moduleの機能までチェックできないようです。
そこで、設定ファイルの項目parserOptionsを設定していきます。

...
  parserOptions: {
    "sourceType": "module"
  },  
...

parserOptionsの項目に"sourceType": "module"を追加します。
これでES6に対応したはずです。

また、デフォルトではbabel-eslintが設定されていますが、いろんな設定を付加してくれる反面いろいろありすぎて推奨されない見方もあるそうなので、個人的な判断ですが削除します。
ちなみに、たぶんなんですがbabel-eslintを設定しておけばes6の記述と上記のmoduleの記述は不要になると思います。お好きな方をどうぞ。

ついでにJSXもチェックできるようにしておきます。

...
  parserOptions: {
    "sourceType": "module",
    "ecmaFeatures": {
      "jsx": true
    }
  },
...

nuxtなんであんま使わないと思いますが。

拡張ルールセット(extends)

ライブラリなど既に定義されているルールを取り込みたい時があります。むしろそれだけで事足りる事が多いんじゃないでしょうか?
そういうときの設定項目としてextendsがあります。nuxtのデフォルトでは何種類か指定されていると思います。

基本的な流れとしては

  • npmかyarnでルールを追加する
  • "extends"に追記する

といった感じです。楽勝ですね。
試しにかの有名なairbnbのルールを適応してみます。パッケージはeslint-config-airbnb-baseです。

yarn add -D eslint-config-airbnb-base

お次にeslintrcに追記。

...
  extends: [
    'airbnb-base',
    '@nuxtjs',
    'prettier',
    'prettier/vue',
    'plugin:prettier/recommended',
    'plugin:nuxt/recommended'
  ],
...

一番上に追記したのは、'plugin:prettier/recommended'の設定が後ろに回さないとうまく動かない事があるという噂を聞いたことがあるからです。
この噂に従い、基本的に後から追加したものは上から順に追記していきます。(ここは曖昧なので気になる方は調べて下さい。)

プラグイン(plugins)

先ほどのextendsのなかにplugin:prettier/recommendedなどplugin:で始まるものがあります。
これらはpluginsの項目で設定したプラグインの推奨設定で、プラグインを導入すると使えるルールだそうです。
全部extendsでどうにかならんの?という気がしないでもないんですが、おそらく内部構造的にプラグインとして別途読み込まなければならないルール(たとえば、上記のES6なんかはES Moduleの為にparserOptionsが必要になったのは内部構造によるもの)の際にはpluginに追記する必要があるんでしょう。
このへんは導入するルールによるものかと思います。その都度確認しましょう。

今回はnuxtの為に導入しているので、vueのルールを追加します。
なんでデフォルトで入らないか謎ですが、追加で加えていきます。

yarn add -D eslint-loader eslint-plugin-vue

大体セットでeslint-loaderも追加してるんですが、ホットリロードの為だと思います。
設定ファイルを書き換えます。

...
  extends: [
    'airbnb-base',
    '@nuxtjs',
    'prettier',
    'prettier/vue',
    'eslint:recommended',
    'plugin:vue/recommended',
    'plugin:nuxt/recommended',
    'plugin:prettier/recommended'
  ],
  plugins: [
    'vue',
    'prettier'
  ],
...

'plugin:vue/recommended'を追加したついでに'eslint:recommended'も追加しました。

ESLintの設定はだいたいこんなもんで良さそうです。

Prettier

PrettierはESLintと併用されることの多いソースコードを整形してくれるツールです。
要するにeslint --fixのつよいバージョンといったところでしょうか。
eslintだけでもええやん!て気もするんですが、どうも整形機能としてはPrettierの方が優れているらしく、併用するひとが多いイメージです。
冗長なのが嫌いな方は別に導入しなくても良いと思います。(チェック自体はESLintがやってくれるので)

導入方法

nuxtのウィザードで導入すると設定は既に完了しています。
後から導入する場合はeslint-config-prettiereslint-plugin-prettierをyarnかnpmでインストールすれば大丈夫だと思います。
設定に関しては

...
  extends: [
...
    'prettier',
    'prettier/vue',
...
    'plugin:prettier/recommended'
  ],
  plugins: [
...
    'prettier'
  ],
...

が該当します。

Prettierのルール

Prettierはあくまで補助ツールなので、全ての構文チェックなどはしていません。
イメージとしてはESLintの苦手な整形部分"だけ"を担当しているツールという認識が正しそうです。
なので、extendsの'plugin:prettier/recommended'を一番最後に持ってこないとPrettierで整形したあとにESLintで怒られる構文になったりするので、必ずこの設定は最後に書きましょう。

また、prettierの設定はESLintのrulesに記載することができ、

...
  rules: {
    "prettier/prettier": [
      "error",
      {
        "semi": true
      }
    ]
  }
...

と書くことで、ESLintでセミコロン無しを警告するし、Prettierでコードフォーマットする際にセミコロンを追加したりできます。
基本的にESLintでもPrettierでも指定出来る設定はPrettierで設定するのが良さそうです。(どうせ上書きされるので)
Prettierで指定出来るオプションは下記の通りです。

prettier.io

その他

大体上記までで設定などは完了なのですが、ESLintやPrettierはファイルウォッチャーなどで「保存したら勝手に整形する」といった設定が望ましいです。(いちいちコマンド実行するのはめんどい)
nuxt公式のホットリロードを設定しておけばローカルでサーバーを動かしている際にチェックできるのでそれで良いと思いますが、気になる方はお使いのIDEのファイルウォッチャーにeslintのコマンドを登録するのをお勧めします。

参考

eslint.org prettier.io qiita.com qiita.com qiita.com yokotakenji.me

YouTube Data API (v3)でOAuthを使ってAPIを使う準備 その1

どうも、靖宗です。

最近YouTubeAPIを触ることがあってメモ代わりに記事にします。
OAuthあたりはよく忘れるので・・・

オープンな情報ならAPI Keyを作成するだけで簡単なのですが、ユーザーに紐付いた操作などをする場合はOAuthの認証が必要です。
今回はAPI Keyの方は言及しませんがもしかしたらそのうち書くかも。

Google Cloud Platformで下準備

まずはGoogle Cloud Platform(GCP)のダッシュボードからプロジェクトを作成し、その元にアプリケーションを登録します。

console.cloud.google.com

アクセスするとログイン画面がでるので、APIで操作したいアカウントでログインします。

f:id:ysmn_deus:20190408112851p:plain

プロジェクト作成

プロジェクト毎に認証情報は異なる方が都合が良いのでプロジェクトを作成します。
左上の「プロジェクトの選択」から作成します。

f:id:ysmn_deus:20190408212505p:plain

f:id:ysmn_deus:20190408212642p:plain

f:id:ysmn_deus:20190408213001p:plain

プロジェクト名は各自好きに設定して下さい。

「作成」というボタンを押すと、ダッシュボードに戻されて右上でなんか進みます。
暫くすると「プロジェクト「○○」を作成」と表示されればプロジェクトの作成が完了していると思います。

OAuthクライアント

お次にアプリケーションの登録を行います。
左上のメニューマークから

f:id:ysmn_deus:20190408213445p:plain

f:id:ysmn_deus:20190408213602p:plain

APIとサービス」→「認証情報」を選択します。
プロジェクトが選択されてないと「このページを表示するには、プロジェクトを選択してください。」という表示が出ると思うので、右上の「プロジェクトの選択」から先ほど作成したプロジェクトを選択します。

f:id:ysmn_deus:20190408213847p:plain

プロジェクトが選択されていれば、「認証情報」という窓が表示されていると思うので、認証情報を作成していきます。

f:id:ysmn_deus:20190408214020p:plain

今回はOAuthを利用するのでOAuthクライアントIDを選択します。

f:id:ysmn_deus:20190408214059p:plain

同意画面の設定

進むと「OAuth クライアント ID を作成するには、まず同意画面でサービス名を設定する必要があります。」と警告されていると思います。
ユーザーがOAuthを利用する際に「承認しますか?」と聞かれるページに表示されている情報を設定して下さいという意図です。確かにそれがないと始まらない。
同意画面の設定ボタンを押して設定しときます。

f:id:ysmn_deus:20190408214314p:plain

設定画面が表示されますので、必要な情報を入力します。
とりあえず動かしたいって方はおそらく

  • アプリケーション名
  • サポートメール

だけ設定しておけばローカル環境では動きそうです。
もしサービスを運用するドメイン(URL)なども決まっている場合は「承認済みドメイン」の箇所を設定しておいて下さい。
たぶんここを設定してないとなにがしかのトラブルに巻き込まれます(不正な認証情報だとか云々?しらんけど)。
実際にOAuthを利用するサービスにする場合はちゃんとポリシーなんかのアドレスも設定して下さい。

f:id:ysmn_deus:20190408214903p:plain

設定できたら「保存」を押して同意画面の設定を完了します。

f:id:ysmn_deus:20190408215016p:plain

クライアントIDの作成

同意画面の設定を完了できればようやくクライアントIDの発行ができます。

f:id:ysmn_deus:20190408215210p:plain

今回はWebアプリケーションを想定して一番上の「ウェブアプリケーション」を選択します。

承認済みのJavaScript生成元

ちょっと画像に入れ忘れてたんですが、ローカル環境で開発する際は「承認済みのJavaScript生成元」に"http://localhost"や"http://127.0.0.1:8000"などを設定しておかないとGoogleにはじかれます。

承認済みのリダイレクト URI

また、認証情報を取得してそのまま情報をアプリケーションのDBに格納したいというケースも考えられますが、そういった際にリダイレクトされるURL(URI)を「承認済みのリダイレクト URI」に設定しておきます。
例えば、承認済みのリダイレクト URIを"http://localhost/oauth/"に設定しておくと、OAuthの認証が終わった後に"http://localhost/oauth/?code=xxxxxxxxxxxxxx"へリダイレクトされます。
これを利用してアプリケーションは"アプリケーションのURL/oauth/?code=[認証トークン]"というルーティングに処理を書いておくことで認証トークンを取得することができます。

あとはYouTube Data APIを有効にするだけなんですが、別記事に記載します。

Windows 10 Home (64bit)でDockerの環境を構築する

どうも、靖宗です。
Docker for Windowsが出現してからWindowsでDockerを利用するのが簡単になりました。
とはいえ自分の利用しているWindowsがHomeでDocker for Windowsが利用できない人もいるんじゃないでしょうか?ぼくもその一人です。
(アップグレードしろよという突っ込みは受け付けません)
どうやら広大なネットの海で調べたところ、Windows 10 HomeではDocker Toolboxで一応動かせるようなので、そちらのセットアップをしていきたいと思います。

基本的に下記の記事を参考にさせていただきました。

qiita.com

インストーラーを手に入れる

なんてことないのですが、Docker公式としてはあまりToolboxの方を利用して欲しくないのか分かりにくい所にインストーラーがある気がします。
DockerToolboxのリリース一覧から取得しろとのことです。

github.com

f:id:ysmn_deus:20190805123509p:plain
ここ

今回は「18.09.3」というバージョンをダウンロードしましたが、未来にこの記事を見ている人はより新しいバージョンのインストーラーがあると思います。
(上から見てって「Assets」というところにexeファイルがあるのを選んで下さい。) ダウンロードできたら基本的にはインストーラーに従って進めて行きます。

インストール

f:id:ysmn_deus:20190805123824p:plain

自分はこんなかんじですが、Gitがインストールされてない方などはチェックボックスがついてそうです。
要らないことないのでインストールしておくことをお勧めします。

f:id:ysmn_deus:20190805123936p:plain

自分はデスクトップが汚れるのは許せないので「Create a desktop shortcut」はチェックを外しておきます。
この辺はご自由にどうぞ。

DockerToolboxを起動

最後まで順調に進めば「Docker Quickstart Terminal」というショートカットが表示されると思います。
ダブルクリックで起動!

無事くじらAAが表示されればセットアップは完了です。

f:id:ysmn_deus:20190805124639p:plain

試しにコンテナを起動してみる

試しにニンジンXのコンテナを起動してみます。
Dockerの基本的な利用方法については割愛しますが、nginxというコンテナを起動します。

docker run --name ninjin -d -p 8080:80 nginx

f:id:ysmn_deus:20190805125219p:plain

基本的にインストールし終わってパスが通ってる状態であればDocker Quickstart Terminalのコンソールからコマンドを打たなくてもPowerShellやcmd.exeでdockerコマンドが通ると思います。

VirtualBox上のVMのポートフォワーディング

Docker for Windowsならたぶんここでブラウザに「http://localhost:8080」でnginxが表示されると思うのですが、今回はDockerToolboxを利用しているのでもう一手間要ります。
VirtualBoxに「default」という仮想マシンが作成されていると思いますが(環境によっては変わる?)、このVM上でDockerが動いています。

f:id:ysmn_deus:20190805131139p:plain

なので普通にコンテナを起動すると、ポートフォワーディングはdefaultという仮想マシンへ渡されます。
なので、なにも設定していないとWindows側からは見れません。

f:id:ysmn_deus:20190805131813p:plain

今回はnginxをdefaultというVMには8080に渡しているので、defaultからWindowsへ8080に渡す設定を追加します。
VirtualBox仮想マシンなのでVirtualBoxで設定します。

f:id:ysmn_deus:20190805132007p:plain

f:id:ysmn_deus:20190805132023p:plain

f:id:ysmn_deus:20190805132037p:plain

defaultのポートフォワーディングが設定できれば、Windowsのブラウザ上からもnginxへとアクセスできるようになります。

f:id:ysmn_deus:20190805132127p:plain

ブラウザ上で「localhost:8080」にアクセスすれば、nginxのデフォルト表記が確認できると思います。

f:id:ysmn_deus:20190805132202p:plain

FusionPCBで基板製作+部品実装(PCB+PCBA)を発注する一連の流れまとめ

f:id:ysmn_deus:20190612190424p:plain

どうも、靖宗です。
FusionPCBで基板製作から部品調達+部品実装までしてもらって楽しようという記事です。
防備録としての意味合いも込めて記事にしておきます。
部品を国内から送る方法は需要があれば別途記事にします。
※2019/06/12 現在のまとめです。ちょいちょい変わったりすると思うので、お気を付けて。
※2019/06/13 追記

準備

Gerberデータ作成

一応基板の設計は終わってるものとします。
Gerberデータの作成方法はFusionPCB公式サイトで詳しく記載がありますのでそちらをご覧下さい。
デザインルールなどをチェックするのをお忘れなく。

BOMデータ作成

基板のみ作りたいって人はこの項目を飛ばして貰って構いません。
実装の際に部品表が必要になりますので、そちらを作成していきます。
部品表のテンプレートがあるので、そちらをダウンロードして作成してください。
(リンク切れの場合はFusionPCBのサイトから直接ダウンロードしてください。)

Eagleを使っている方はPartlistなどを出力して部品表を作成するのが良いと思います。
一応PartlistからBOMの基礎となるデータを出力するPythonスクリプトを置いておきますので自己責任でご利用ください。

https://www.axfc.net/u/3985652&key=deus

実装情報(任意)

※この項目は現在調査中ですので参考程度でお願いします。

実装図などをアップロードできるようになったみたいなので、この辺の情報があるとFusionPCBの人達は楽かも。
連絡回数を少なくして迅速に実装してもらうなら準備しておいた方が良いかもしれません。
Eagleで回路を設計しているなら

  • ボード図(PDF, brdを印刷→PDFなどで対応、もしくはImageなど?)
  • 配置情報(Partlist)

ぐらいあると丁寧かも。

見積

Gerberデータアップロード

GerberデータとBOMデータができていればウェブ上から見積がすぐ取れます。
FusionPCBのPCBのページからGerberデータをアップロードします。

f:id:ysmn_deus:20190612184109p:plain

アップロードできれば、念のためガーバービューアで閲覧しておく事をオススメします。

f:id:ysmn_deus:20190612184350p:plain

基板の作成枚数が10以外の場合はパラメータのところで変更してください。

BOMデータアップロード

各パラメータを入力し終えたら「実装サービス」という欄をクリックし、実装情報やBOMデータなどをアップロードします。
実装枚数が10以外の場合は、「実装枚数」と書かれた箇所を変更してください。

f:id:ysmn_deus:20190612184654p:plain

BOMデータが問題無くアップロードできれば見積が完了します。

f:id:ysmn_deus:20190612185028p:plain

もしFusionPCB側でウェブ見積できない部品(実装実績がない部品)があれば、部品見積の一番上に表示が出てきますので、部品を調達するために必要な情報を「購入リンク」から入力しましょう。だいたいDigiKeyにある部品なら調達してくれるのでDigiKeyのリンクを張っておけば問題無いかと思います。

f:id:ysmn_deus:20190612185621p:plain

部品を国内から送るとき

この場合はカスタマーサポートへの連絡が必須です。
(調達価格と実装価格が変わるので、特殊なBOMを作って貰うのにメールでのやりとりが必要です。)
迅速丁寧に対応して貰えるので恐れずに fusion.jp@seeed.cc へ連絡を送りましょう。
この辺はまた別途詳細をまとめます。

カートに入れる+購入手続き

上記までが問題無く終わっていれば上部に価格が反映されているので、「カートに追加」ボタンでカートに追加します。
カートに追加した以降は、日本語で案内がでているので大丈夫だと思います。
PayPal支払とクレジットカード支払が使えたと思います。)

決済後

担当者とのやりとり

購入手続きが完了すると、担当者から連絡があると思います。
日本語で丁寧に対応してくださるので、指示に従って必要な追加データや修正点などあればメールベースでやりとりすることになります。

納期

おおよそ25日前後と考えておいた方が良さそうです。
これは勝手なイメージですが、おおよそ基板製造に2営業日程度、実装に3営業日程度なのですが、部品調達にどうしても時間がかかっている(税関などの影響?最近は厳しいみたいです)ようです。
なので、もし実装を急いでいる場合は事前に部品を調達しておいて、国内から部品を発送したほうが実装期間の短縮には繋がるかもしれません。その分手間は増えますが。
部品を送付する際の手順はまた気が向けば。

Phoenix入門 (第15章 Custom Errors)

f:id:ysmn_deus:20190219130922p:plain

どうも、靖宗です。
とりあえず主な項目はコレで最後です。
残るはテストの話とデプロイの話が残っていますが、残ってる話の方が重要そうな気がしますね。

今回はたぶん短めです。
PhoenixというかElixirの例外処理のお話に近いかも。

Custom Errors

Viewsの項目でErrorViewに関しては一回やりました。
基本的に400とか500のエラーはテンプレート(404.html.eexとか)を用意しておけばPhoenixのコントローラがよしなにしてくれます。
ビューのエラーはこのErrorViewで受けるとして、内部処理のエラーはPhoenixでは例外処理などが少々利用されているようです。

Custom Errors

Elixirの標準機能として、例外を定義するためのdefexceptionを利用してエラー処理を行っています。
エラーを実装したいモジュール内部にモジュールを更に定義し、内部でdefexceptionを定義するように実装するそうです。
router.exが利用しているPhoenix.Routerの内部実装が例としてドキュメントに記載されています。

defmodule Phoenix.Router do
  defmodule NoRouteError do
    @moduledoc """
    Exception raised when no route is found.
    """
    defexception plug_status: 404, message: "no route found", conn: nil, router: nil

    def exception(opts) do
      conn   = Keyword.fetch!(opts, :conn)
      router = Keyword.fetch!(opts, :router)
      path   = "/" <> Enum.join(conn.path_info, "/")

      %NoRouteError{message: "no route found for #{conn.method} #{path} (#{inspect router})",
      conn: conn, router: router}
    end
  end
...
end

Elixirの例外処理の記事では取り扱ってなかったのですが、defexceptionを宣言したモジュール内でdef exceptionとして関数を実装すると、エラー処理の関数になるようです。
(詳細はElixirのExceptionのビヘイビアを確認)

Plugを実装する際にはPlug.Exceptionというプロトコルがあるようなのでそちらを利用するのが良いかと思います。
例なども示されてますが、特殊ケースな気がするので、また利用機会があった際に追記したいと思います。

今回は短めですがこんなもんで。

Phoenix入門 (第14章 Mix Tasks その2)

f:id:ysmn_deus:20190219130922p:plain

どうも、靖宗です。
引き続きMix Tasksの項目を見ていきます。
今回はEcto関連のコマンドから。

Ecto Specific Mix Tasks

このEcto周辺のコマンドですが、当たり前ですが--no-ectoとかしてない場合が対象です。

mix ecto.create

データベースの作成を行うコマンドです。デフォルトならconfigやlib/hello/repo.exに記載したデータベースに作成するはずです。
デフォルトでいいなら引数は要らないのでシンプルです。

$ mix ecto.create
The database for Hello.Repo has been created.

別の名前で定義してたりするなら、-rオプションでモジュール名を指定します。

$ mix ecto.create -r OurCustom.Repo
The database for OurCustom.Repo has been created.

PostgreSQLのユーザー周りの説明は割愛します。
(要望があれば書きますが)

ecto.drop

純粋にecto.createの逆だと思えば良さそうで、指定したデータベースからデータを消去します。
使い方もほぼcreateと同じで、デフォルトなら引数は不要。

$ mix ecto.drop
The database for Hello.Repo has been dropped.

-rでモジュール名を指定することも可能。

$ mix ecto.drop -r OurCustom.Repo
The database for OurCustom.Repo has been dropped.

mix ecto.gen.repo

データストアが1個じゃないケースもあると思います。そういうときにこのecto.gen.repoで新しいリポジトリを作成できるそうです。
たとえば、デフォルトのリポジトリHello.Repoで、別のリポジトリOurCustom.Repoであるばあい、下記のようにして作成するようです。

$ mix ecto.gen.repo -r OurCustom.Repo
* creating lib/our_custom
* creating lib/our_custom/repo.ex
* updating config/config.exs
Don't forget to add your new repo to your supervision tree
(typically in lib/hello.ex):

worker(OurCustom.Repo, [])

* updating config/config.exsとあるので、configも修正されています。

...
config :hello, OurCustom.Repo,
database: "hello_repo",
username: "user",
password: "pass",
hostname: "localhost"
...

これは各環境で合わせて下さい。
場合によってはdev.exsprod.exsに記載してもいいかもしれません。

ファイルの生成などは自動でやってくれましたが、リポジトリのワーカーをSupervisorで管理する必要があります。
ドキュメントではlib/hello.exを編集、と書いてありますがたぶんlib/hello/application.exの間違いです。

...
children = [
  # Start the Ecto repository
  Hello.Repo,
  # Start the endpoint when the application starts
  HelloWeb.Endpoint,
  # Starts a worker by calling: Hello.Worker.start_link(arg)
  # {Hello.Worker, arg},
  # Here you could define other workers and supervisors as children
  OurCustom.Repo
]
...

これで大丈夫そうです。

mix ecto.gen.migration

マイグレーションする準備をするのがecto.gen.migrationです。こういうフレームワークにはよくある奴だと思います。
Contextの章でもいじったファイルを作成するやつで、マイグレーションも実際の所はElixirのスクリプトを実行して行っていますのでそのスクリプトを生成するコマンドということです。
基本的に何かのコマンドで自動生成されるのでそこまで出番があるように思えませんが、スキームの変更などをした場合には手動で作成する必要がありそうです。
コマンドにマイグレーションファイル名を引数として渡します。

mix ecto.gen.migration add_comments_table
* creating priv/repo/migrations
* creating priv/repo/migrations/20150318001628_add_comments_table.exs

マイグレーション用のファイルがpriv/repo/migrationsに作成されます。おそらくタイムスタンプ_指定した名前.exsというファイルが作成されます。
ファイルの中身は下記のようになっているそうです。

defmodule Hello.Repo.Migrations.AddCommentsTable do
  use Ecto.Migration

  def change do
  end
end

基本的にコマンドで生成した場合は特に何も書かれていないはずです。
このchange/0に記載された仕様をもとに、データベースを編集したりロールバックしたりします。
たとえば、commentsというテーブルを作成し、bodyword_countというフィールド+タイムスタンプがあるスキームを追加したとします。
このとき、マイグレーションファイルは下記のように編集します。

...
def change do
  create table(:comments) do
    add :body,       :string
    add :word_count, :integer
    timestamps()
  end
end
...

ちなみに、今はcreateを使いましたが、スキームを追加するのではなく変更する場合などはalterを利用します。

基本的な使い方は上記の通りですが、デフォルトではないリポジトリを対象としたマイグレーションのファイルは、ecto.createなどと同様-rオプションでリポジトリ名を指定してやる必要があります。

$ mix ecto.gen.migration -r OurCustom.Repo add_users
* creating priv/repo/migrations
* creating priv/repo/migrations/20150318172927_add_users.exs

mix ecto.migrate

マイグレーションファイルを作成したらすることは決まっています。マイグレーションです。
何も考えずにmix ecto.migrateすることが多いのではないでしょうか。

$ mix ecto.migrate
[info] == Running Hello.Repo.Migrations.AddCommentsTable.change/0 forward
[info] create table comments
[info] == Migrated in 0.1s

そこまで開発にかかわってこない可能性がありますが、mix ecto.migrateをするとデータベース上のschema_migrationsというテーブルにマイグレーション日時のタイムスタンプが作成されるようです。

hello_dev=# select * from schema_migrations;
version     |     inserted_at
----------------+---------------------
20150317170448 | 2015-03-17 21:07:26
20150318001628 | 2015-03-18 01:45:00
(2 rows)

あとで見てみますが、ecto.rollbackなどでロールバックするときはこの辺の情報を利用してロールバックするようです。

基本的に存在している全てのマイグレーションファイルを実行してマイグレートするコマンドですが、一応実行する個数を指定出来るようです(順番はタイムスタンプ依存?)
-n--stepのオプションを使用して実行するマイグレーションの個数を指定します。

$ mix ecto.migrate -n 2
[info] == Running Hello.Repo.Migrations.CreatePost.change/0 forward
[info] create table posts
[info] == Migrated in 0.0s
[info] == Running Hello.Repo.Migrations.AddCommentsTable.change/0 forward
[info] create table comments
[info] == Migrated in 0.0s

一応下記も同様

$ mix ecto.migrate --step 2

-vのオプションを使用すると、指定したタイムスタンプのマイグレーションファイルのみを実行できるようです。

$ mix ecto.migrate -v 20150317170448

--toも同じ働き。

$ mix ecto.migrate --to 20150317170448

mix ecto.rollback

ロールバック、詰まり先祖返りできます。スキーム変更したけどやり直したい!とかいう場合に活用できそうです。
それもコレもマイグレーションファイルにきっちり変更の仕様が記載されているおかげでしょう。

$ mix ecto.rollback
[info] == Running Hello.Repo.Migrations.AddCommentsTable.change/0 backward
[info] drop table comments
[info] == Migrated in 0.0s

何も指定しなければ全部戻ってしまいそうです。

rollbackmigrationと同様のオプションが取れます。例えば-nでn個マイグレーションファイルを遡る、などでしょうか。
こちらはオプションを大いに活用しそうです。

Creating Our Own Mix Tasks

おおよそ見ていったmixコマンドで事足りそうですが、「もうちょっとやってくれよ」「ここ毎回同じ事してる」という場合はかゆいところに手が届くmixコマンドが欲しくなると思います。
そういった需要に応えるがごとく、mixコマンドを作成できるそうです。

まずはlib/フォルダにmix/tasksディレクトリを作成していきます。

$ mkdir -p lib/mix/tasks

試しにhello.greeting.exというファイルを作成するとします。

defmodule Mix.Tasks.Hello.Greeting do
  use Mix.Task

  @shortdoc "Sends a greeting to us from Hello Phoenix"

  @moduledoc """
    This is where we would put any long form documentation or doctests.
  """

  def run(_args) do
    Mix.shell.info("Greetings from the Hello Phoenix Application!")
  end

  # We can define other functions as needed here.
end

慣習なのか知りませんが、基本的にElixirはディレクトリ名をモジュール名に適応するようです。
(そもそもコンパイル時にチェックされる?すみません、この辺は勉強不足です。)
なので、lib/ディレクトリ以下のmix/tasksディレクトリ下にあるhello.greeting.exなので、モジュール名はMix.Tasks.Hello.Greetingになります。

お次にuse Mix.Taskが宣言されています。たぶんこれでmixのコマンドとして機能するのでしょう。

@shortdocのモジュール属性ですが、これはmix help字に表示される説明文です。

@moduledocはモジュールのドキュメントです。コレに関しては割愛します。

run/1mixで呼ばれたときに実行される関数です。ここではmixで実行されたときにシェルに文字列がプリントされるだけのようです。

以上の仕様で、利用する為にはまずコンパイルします。

$ mix compile
Compiled lib/tasks/hello.greeting.ex
Generated hello.app

コンパイルすると、mix helpにも表示されるようです。

$ mix help | grep hello
mix hello.greeting # Sends a greeting to us from Hello Phoenix

実際に利用する場合は、ファイル名で実行するようです。

$ mix hello.greeting
Greetings from the Hello Phoenix Application!

run/1では今のところ文字列を出力するMix.shell.info/1しか利用していませんが、アプリケーションを実行するにはMix.Task.run/1を利用すればいいようです。

...
  def run(_args) do
    Mix.Task.run("app.start")
    Mix.shell.info("Now I have access to Repo and other goodies!")
  end
...

もうちょっといろんな機能を実装するにはMixの仕様をより深掘りする必要がありそうです・・・

Phoenix入門 (第14章 Mix Tasks その1)

f:id:ysmn_deus:20190219130922p:plain

どうも、靖宗です。
今回はMix Tasksということでmix phx.hogehogeのコマンドあたりの説明でしょうか。

Mix Tasks

Phoenix Specific Mix Tasks

Phoenixで使いそうなコマンド一覧があります。

mix local.phx          # Updates the Phoenix project generator locally
mix phx                # Prints Phoenix help information
mix phx.digest         # Digests and compresses static files
mix phx.digest.clean   # Removes old versions of static assets.
mix phx.gen.cert       # Generates a self-signed certificate for HTTPS testing
mix phx.gen.channel    # Generates a Phoenix channel
mix phx.gen.context    # Generates a context with functions around an Ecto schema
mix phx.gen.embedded   # Generates an embedded Ecto schema file
mix phx.gen.html       # Generates controller, views, and context for an HTML resource
mix phx.gen.json       # Generates controller, views, and context for a JSON resource
mix phx.gen.presence   # Generates a Presence tracker
mix phx.gen.schema     # Generates an Ecto schema and migration file
mix phx.gen.secret     # Generates a secret
mix phx.new            # Creates a new Phoenix application
mix phx.new.ecto       # Creates a new Ecto project within an umbrella project
mix phx.new.web        # Creates a new Phoenix web project within an umbrella project
mix phx.routes         # Prints all routes
mix phx.server         # Starts applications and their servers

細かい機能はPhoenix公式ドキュメントの「MIX TASKS」を見れば良さそうです。
幾つか簡単に取り上げてくれているので、一応目を通しておきます。

mix phx.new

言わずと知れたPhoenixプロジェクトを生成する際に使うコマンドです。
--no-ecto--no-webpackというオプションをつける事でEctoの実装を無くしたり、Webpackの実装を無くしたりできるそうですが、基本的には必要なんじゃないでしょうか。
APIサーバーとして機能するときはWebpackは不要?)

実際に生成するプロジェクト名はスネークケースがいいっぽいです。

> mix phx.new task_tester

相対パスでも絶対パスでもヨシ。

> mix phx.new ../task_tester
> mix phx.new /Users/me/work/task_tester

mix phx.newでプロジェクトを生成するとアプリケーション名はプロジェクト名(上記だとtask_tester)になります。
もしアプリケーション名を変更したい場合はmix phx.new task_tester --app hello--appオプションを利用するそうです。

mix.exsでアプリケーション名が変更されています。

defmodule Hello.MixProject do
  use Mix.Project

  def project do
    [app: :hello,
     version: "0.1.0",
...

このアプリケーション名を変更するとモジュール名などの接頭辞が変わるようです。

defmodule HelloWeb.PageController do
  use HelloWeb, :controller
...

--appオプションでhelloを設定したのでモジュール名がHelloから始まってます。
lib/フォルダ以下のディレクトリやファイル名なども変わります。

基本は--appでいいような気はしますが、「モジュール名の接頭辞だけ変えたい!」という場合には--moduleオプションで指定出来るようです。

$  mix phx.new task_tester --module Hello
* creating task_tester/config/config.exs
* creating task_tester/config/dev.exs
* creating task_tester/config/prod.exs
* creating task_tester/config/prod.secret.exs
* creating task_tester/config/test.exs
* creating task_tester/lib/task_tester/application.ex
* creating task_tester/lib/task_tester.ex
* creating task_tester/lib/task_tester_web/channels/user_socket.ex
* creating task_tester/lib/task_tester_web/views/error_helpers.ex
* creating task_tester/lib/task_tester_web/views/error_view.ex
* creating task_tester/lib/task_tester_web/endpoint.ex
* creating task_tester/lib/task_tester_web/router.ex
* creating task_tester/lib/task_tester_web.ex
* creating task_tester/mix.exs
* creating task_tester/README.md
* creating task_tester/.gitignore
* creating task_tester/test/support/channel_case.ex
* creating task_tester/test/support/conn_case.ex
* creating task_tester/test/test_helper.exs
* creating task_tester/test/task_tester_web/views/error_view_test.exs
* creating task_tester/lib/task_tester_web/gettext.ex
* creating task_tester/priv/gettext/en/LC_MESSAGES/errors.po
* creating task_tester/priv/gettext/errors.pot
* creating task_tester/lib/task_tester/repo.ex

mix.exsは下のようなかんじ。

defmodule Hello.MixProject do
  use Mix.Project

  def project do
    [app: :task_tester,
...

アプリケーション名はtask_testerのままで、接頭辞のみがHelloとなっています。
ややこしいことになりそうなので、変えたいなら--appを使った方が良さそうです。

mix phx.gen.html

これはContextあたりで使いました。HTMLでのresourcerouter.exresource指定する奴)を生成する際に便利なコマンドです。
Webレイヤーの実装(コントローラ、ビュー、テンプレート)はもちろん、EctoのマイグレーションファイルやContextも生成してくれます。

このコマンドは引数が多くてややこしいですが、その中身は至ってシンプルで
mix phx.gen.html Context名 スキーマ名 リソース名 スキーマのリスト(項目名:型)
です。

$ mix phx.gen.html Blog Post posts body:string word_count:integer
* creating lib/hello_web/controllers/post_controller.ex
* creating lib/hello_web/templates/post/edit.html.eex
* creating lib/hello_web/templates/post/form.html.eex
* creating lib/hello_web/templates/post/index.html.eex
* creating lib/hello_web/templates/post/new.html.eex
* creating lib/hello_web/templates/post/show.html.eex
* creating lib/hello_web/views/post_view.ex
* creating test/hello_web/controllers/post_controller_test.exs
* creating lib/hello/blog/post.ex
* creating priv/repo/migrations/20170906150129_create_posts.exs
* creating lib/hello/blog/blog.ex
* injecting lib/hello/blog/blog.ex
* creating test/hello/blog/blog_test.exs
* injecting test/hello/blog/blog_test.exs

mix系のコマンドに一般的に言えることですが、次に何やったらいいか注釈がでてくれます。

Add the resource to your browser scope in lib/hello_web/router.ex:

    resources "/posts", PostController

Remember to update your repository by running migrations:

    $ mix ecto.migrate

ルーティングの設定とマイグレーションしろよ!と出ています。基本これだけでリソースが作成できます。

大体無いとは思うんですが、Contextいらないよ!って時(Bootstrapのテストなど、なんらかのテストに使う?)は--no-contextというオプションがあるそうです。
活用法があまり思い浮かばないので省略。 同様に--no-schemaオプションもあり。

mix phx.gen.json

こちらは使ったことはないですが、見た目からして上記のJSON版といったところでしょうか。

$ mix phx.gen.json Blog Post posts title:string content:string
* creating lib/hello_web/controllers/post_controller.ex
* creating lib/hello_web/views/post_view.ex
* creating test/hello_web/controllers/post_controller_test.exs
* creating lib/hello_web/views/changeset_view.ex
* creating lib/hello_web/controllers/fallback_controller.ex
* creating lib/hello/blog/post.ex
* creating priv/repo/migrations/20170906153323_create_posts.exs
* creating lib/hello/blog/blog.ex
* injecting lib/hello/blog/blog.ex
* creating test/hello/blog/blog_test.exs
* injecting test/hello/blog/blog_test.exs

コマンドの仕様もほぼhtmlと変わらないようです。
コマンド実行後の注釈はちょっと変わってきています。

Add the resource to your :api scope in lib/hello_web/router.ex:

    resources "/posts", PostController, except: [:new, :edit]


Remember to update your repository by running migrations:

    $ mix ecto.migrate

:new:editのアクションを除外しています。
JSONなので新規生成画面と編集画面がないためでしょう。HTTPリクエストなどで直接:create:updateを実行するということでしょう。

htmlと同様に--no-context--no-schemaオプションもあり。

mix phx.gen.context

Contextの章でAccountsにCredentialを追加するときに使いました。
追加するように使った場合はinjecting lib/hello/accounts.exというように既存のContextに追記する形で生成されましたが、Contextのみ生成したい場合にも利用できるようです。
主な使用方法はhtmljsonの時と同じ。

$ mix phx.gen.context Accounts User users name:string age:integer
* creating lib/hello/accounts/user.ex
* creating priv/repo/migrations/20170906161158_create_users.exs
* creating lib/hello/accounts/accounts.ex
* injecting lib/hello/accounts/accounts.ex
* creating test/hello/accounts/accounts_test.exs
* injecting test/hello/accounts/accounts_test.exs

mix phx.gen.schema

HTMLやJSONのリソースも作らず、Contextも不要というレアケース用?でもコマンドが用意されてるってことは必要なタイミングがあるのかも。
コマンド引数はhtmljsonからContext名を省略したようなかんじ。

$ mix phx.gen.schema Accounts.Credential credentials email:string:unique user_id:references:users
* creating lib/hello/accounts/credential.ex
* creating priv/repo/migrations/20170906162013_create_credentials.exs

mix phx.gen.channel

Channelを生成するためのコマンド。Channelの章では手動で作成しましたが、コマンドでも生成してくれるようです。
引数は純粋にチャンネル名のみ。

$ mix phx.gen.channel Room
* creating lib/hello_web/channels/room_channel.ex
* creating test/hello_web/channels/room_channel_test.exs

ちゃんとメッセージも出してくれます。

Add the channel to your `lib/hello_web/channels/user_socket.ex` handler, for example:

    channel "rooms:lobby", HelloWeb.RoomChannel

mix phx.gen.presence

これはPresenceの章で使いました。
一応モジュール名を指定できますが、省略すればPresenceで作成されます。

$ mix phx.gen.presence
* creating lib/hello_web/channels/presence.ex

こちらもきちんと注釈を付けてくれます。

Add your new module to your supervision tree,
in lib/hello/application.ex:

    children = [
      ...
      HelloWeb.Presence
    ]

You're all set! See the Phoenix.Presence docs for more details:
http://hexdocs.pm/phoenix/Phoenix.Presence.html

mix phx.routes

Routingの章で利用しました。
コマンドを実行すればルーティングルールが表示されます。

$ mix phx.routes
page_path  GET  /  TaskTester.PageController.index/2

ルーティングファイルが2個以上あるときは、ルーティングファイル名を指定するとソイツだけみれるようです。

$ mix phx.routes TaskTesterWeb.Router
page_path  GET  /  TaskTesterWeb.PageController.index/2

mix phx.server

サーバーを立ち上げるコマンド。DoesNotExistオプションとかもあるけどよくわかんない。(本来は「DoesNotExist」が必要だけど省略されてる?)
iexで立ち上げたい時はiex -S mix phx.serverを実行してやればよいそうで。

$ iex -S mix phx.server
Erlang/OTP 17 [erts-6.4] [source] [64-bit] [smp:8:8] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]

[info] Running TaskTesterWeb.Endpoint with Cowboy on port 4000 (http)
Interactive Elixir (1.0.4) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)>

これ地味に便利そうですね。

mix phx.digest

静的ファイル(static assets)の名前にダイジェスト(MD5)を付与し、圧縮するコマンドだそうです。
機能的にほぼデプロイ用(改ざん防止、転送量軽減)と考えて問題無さそうです。

assets/ディレクトリにあるファイルに処理を行い、デフォルトではpriv/staticにコピーするようです。
出力されるファイルの場所や処理するファイルの場所を指定したいときは引数で調整可能。

$ mix phx.digest priv/static -o www/public
Check your digested files at 'www/public'.

コンフィグに:を指定すると圧縮するファイルの対象を変更できるようです。

config :phoenix, :gzippable_exts, ~w(.js .css)

役割的にconfig/config.exsあたりにでも書けば良いのでしょうか。

デプロイは別の章でやるので、そのときにもう一度確認します。

飛ばし飛ばしやってるつもりが結構長くなったのでまた次回!