OmniAICreator/Qiita-1.07M huggingface dataset:
heroku run rails db:migrateの時にpg is not part of the bundle

##はじめに
Cloud9でRuby on Railsのアプリを作っています。
rails tutorialを参考に開発しており、herokuで動作確認をしながら開発を進めていたものの、
突然 heroku run rails db:migrate が出来なくなってしまいだいぶはまってしまいました。。。

##環境
IDE:cloud9
リポジトリ管理：bitbucket
本番環境：heroku
Ruby: 2.4.1
Rails: 5.2.1

##状況
heroku run rails db:migrateをすると

rails aborted!
Gem::LoadError: Error loading the 'postgresql' Active Record adapter. Missing a gem it depends on? pg is not part of the bundle. Add it to your Gemfile.

と言うエラーメッセージが出ました。
pgをGemfileに追加しろ？追加しとるわ!

pgが本当にheroku上のGemfile.lockに入っているのか、
pgのバージョンがおかしくないか、database.ymlはおかしくないかと色々見直しました。
heroku run rails db:migrateを何十回打ったことか。。。

##解決策
.bundle/configをherokuにアップロードしてしまったために、（もちろん.gitignoreに書いてますよ。。）

```.bundle/config
BUNDLE_WITHOUT: "production"
```
が本番環境（heroku）上でも適用され、production用のpgがherokuにダウンロードされなかったことのようです。

##原因
あるタイミングでgit管理下に.bundle/configを一度置いてしまったみたいで、
gitのキャッシュが残ったままになっていました。
そのため、[.gitignoreに記載したのに反映されない件
](https://qiita.com/fuwamaki/items/3ed021163e50beab7154)を参考にキャッシュを削除しました。

するとpgがheroku上でダウンロードされ、db:migrateまで出来ました。

##背景
heroku run rails db:migrate前のgit push heroku実行時のログをもう一度じっくり
見てみると。。。

```
You have the `.bundle/config` file checked into your repository
It contains local state like the location of the installed bundle
as well as configured git local gems, and other settings that should
not be shared between multiple checkouts of a single repo. Please
remove the `.bundle/` folder from your repo and add it to your `.gitignore` file.
https://devcenter.heroku.com/articles/bundler-configuration

```

というwarningが。。。
これよく見たら問題ありそう。。
rails tutorialではwaringいくつか出ますが無視してOK！みたいなこと書いてたので鵜呑みにして
warningスルーしてたらいつもと違うの出てました。
ちゃんとメッセージ見ないとダメですね〜〜。。

.bundle/configは.gitignoreに書いてましたが、それがherokuにアップロードされているということは
.gitignoreが反映されてない？？？
そのまま感じたことをググったところ上記の記事にたどり着きました。
gitのキャッシュを削除し、解決に至りました。

##まとめ
warningメッセージちゃんと見ないとダメですね。。。
あと、git にキャッシュという概念あるんですね。。。
Run -> Record Espresso Test から作成したテストが実行できない

## 概要
AndroidStudio に備わっている Record Espresso Test で作成したテストを実行してみたら下記のエラーが出て行き詰まったお話。

>java.lang.ClassNotFoundException: <パッケージ名>.<クラス名>
at java.lang.Class.classForName(Native Method)
at java.lang.Class.forName(Class.java:400)
at android.support.test.internal.runner.TestLoader.doCreateRunner(TestLoader.java:72)
at android.support.test.internal.runner.TestLoader.getRunnersFor(TestLoader.java:104)
at android.support.test.internal.runner.TestRequestBuilder.build(TestRequestBuilder.java:789)
at android.support.test.runner.AndroidJUnitRunner.buildRequest(AndroidJUnitRunner.java:539)
at android.support.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:382)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1932)
Caused by: java.lang.ClassNotFoundException: Didn't find class "<パッケージ名>.<クラス名>" on path: DexPathList[[zip file "/system/framework/android.test.runner.jar", zip file "/data/app/<パッケージ名>.test.test-1/base.apk", zip file "/data/app/<パッケージ名>.test-1/base.apk"],nativeLibraryDirectories=[/data/app/<パッケージ名>.sptest.gtec.test-1/lib/arm64, /system/lib64, /vendor/lib64, /system/vendor/lib64]]
at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
at java.lang.ClassLoader.loadClass(ClassLoader.java:380)
at java.lang.ClassLoader.loadClass(ClassLoader.java:312)
... 8 more
>
Tests ran to completion.

## 環境
AndroidStudio 3.1.4
Espresso 3.0.2

## 対応内容
自動作成されたテストファイルのディレクトリを変更したら実行できた。
自動作成されたファイルのディレクトリの場所が違うなんて全く疑っていなかったので、抽象的なエラーを元に試行錯誤するのにかなり時間を使ってしまった・・・。

なぜ `aidl` 配下に作成されてしまったのかはまだ調べていない。
（どこかに設定があるんだろうか・・？）

修正前：
src -> androidTest -> aidl -> <パッケージ名> -> <クラス名>
↓
修正後：
src -> androidTest -> java -> <パッケージ名> -> <クラス名>
Pythonのmultiprocessing.Poolが使いやすくなっていた件

## Pythonでのmultiprocessing
Python2.6から導入されたmultiprocessing.Poolを用いることで実現できます。

実行可能コア数の取得（指定）
↓
Poolインスタンスの生成
↓
マルチプロセス実行する関数の引数を用意
↓
実行


```python:use_multi.py
from multiprocessing import Pool
import multiprocessing as multi


def calc(n): # マルチプロセス実行したい関数
    return n * n


def main():
    n_cores = multi.cpu_count()  # 実行可能コア数の取得
    p = Pool(n_cores)
    args = list(range(10))  # fugaに与える引数のリスト
    res = p.map(calc, args)  # マルチプロセスの実行 
    print(res)  # 各プロセスで実行した関数の戻り値がリストで返ってくる


if __name__ == "__main__":
    main()
```

```
$ python use_multi.py
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
```

## 便利だけど痛い制約があった
マルチプロセス実行する関数が

- クラスのインスタンスメソッド
- 匿名関数
- 関数中で定義した関数

の場合にはpickle関連のエラーが出て使用できませんでした。


```python:use_multi.py
from multiprocessing import Pool
import multiprocessing as multi


class Calc:

    def square(self, n):
        return n * n

    def calc_square_usemulti(self, args):
        n_cores = multi.cpu_count()
        p = Pool(n_cores)
        res = p.map(self.square, args)
        print(res)


def main():
    calc = Calc()
    args = list(range(10))
    calc.calc_square_usemulti(args)


if __name__ == "__main__":
    main()
```

```python:3.2以前
$ python use_multi.py
PicklingError: Can't pickle <type 'function'>: attribute lookup __builtin__.function failed
```

## Python 3.3.7から対応された模様
どうやらmethodがpickle可能になったらしく、インスタンスメソッドでも問題なく利用できるようになりました。オブジェクト指向プログラミング等にも使用できるのは大変嬉しいですね。

```python:3.3以降
$ python use_multi.py
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
```

## 終わりに
最初、昔はインスタンスメソッド等をmultiprocessing.Poolで使うとエラーが出るということを知らずに使っていたんですよね。ある日、別なエラーで色々調べているうちにインスタンスメソッド等は使えないという記事ばかり出てきて、なんで自分は使えているんだろう？と疑問に思って調べてみて発覚しました。ネットには対応した等の記事がなかったので一応まとめました。
Solidusを利用してテストを書くために必要な初期の知識

 
## Solidusとは

SolidusはRuby on Rails製のオープンソースECパッケージです。
Solidusを使うとECサイトが爆速で作ることができますが、独自のお作法が多くとっつきにくいです。

下記Qiitaの記事が最初の取っ掛かりとしてはとても参考になりました。
[SolidusでECサイトを作る際の方法と考え方](https://qiita.com/yuskamiya/items/32f383cb8d18ceb751d7)

Solidus は責務に閉じたいくつかの gem で一つの機能を実現しています。主に利用する頻度が高そうなのは、
solidus_core （モデル等）
solidus_backend（管理画面等）
solidus_frontend （ECサイト側機能等）です。

本記事では、Solidus内ではじめから準備されているのファクトリやヘルパーを利用したテスト(Rspec)の書き方を紹介します。

## Solidusのテストデータがどこにあるのか
EDITOR環境変数が設定済みであれば下記のコマンドで「solidus_core」を開けます。

```
bundle open solidus_core
```

solidus_core内にある下記のファクトリに生成データがあります。

```
solidus_core-2.7.0/lib/spree/testing_support/factories
```

## テストデータを自分のプロジェクトで使うための準備

railsヘルパに下記のように記述します。

```ruby:rails_helper.rb
require 'spec_helper'
#
#
# 既存のコード
#
# ↓追加
require 'spree/testing_support/factories'
#
#
RSpec.configure do |config|
#
#
# ↓以下を記述しておくといちいちFactryBotを打たなくてよくなる
config.include FactoryBot::Syntax::Methods
#
end
```

## テストデータを使ってみる

 `controller-spec`、`feature-spec`などで以下のように`create`するとsolidus内の`testing_support/factories` で定義されたデータが作られる

```ruby:example.controller_spec.rb
RSpec.describe "Products_request", type: :request do
  describe "GET #show" do
    let!(:product) { create(:product) }
  end
end
```

以下がsolidusに実際に記述されているテストデータを生成するためのコードがこちら

```ruby:testing_support/factories
factory :product do
  tax_category { |r| Spree::TaxCategory.first || r.association(:tax_category) }

  factory :product_in_stock do
    after :create do |product|
    product.master.stock_items.first.adjust_count_on_hand(10)
  end

  factory :product_not_backorderable do
    after :create do |product|
      product.master.stock_items.first.update_column(:backorderable, false)
    end
  end
end
```

以下が作成されたデータになり、上の`create(:product)`の後ろにname等をつけるとカラムの値を指定することもできる。

例）let!(:product) { create(:product, name: "T-shirts")}

```ruby
(byebug)<Spree::Product id: 11, name: "Product #5 - 1937", description: "As seen on TV!", available_on: "2017-11-29 02:15:20", deleted_at: nil, slug: "product-5-1
937", meta_description: nil, meta_keywords: nil, tax_category_id: 11, shipping_category_id: 11, created_at: "2018-11-29 02:15:20", updated_at: "2018-11-29

02:15:20", promotionable: true, meta_title: nil>
```

solidusを使用してテスト(Rspec)を書くには、すでに定義されているモデルやファクトリがどう動くか理解する必要があり
理解するのにかなり時間が取られます。
なのでsolidus_coreのモデルと上のようなfactoryを深く読み解いていき値を設定するようにしましょう。


参考文献
>[[日本語訳] TESTING SPREE APPLICATIONS](https://qiita.com/yuskamiya/items/f72480ca29abd6aa363d)
JS 文字列をDateへ変換

IEで直入れ上手くいかなかったので
文字列をバラして、単品づつDateに渡す方式

```javascript:toDate
toDate: function (s) {
  if (0 < s.length) {
    var result = s.split(" ");
    var date = result[0].trim().split("/");
    var time = result[1].trim().split(":");
    return new Date(
      parseInt(date[0], 10),
      parseInt(date[1], 10) - 1,
      parseInt(date[2], 10),
      parseInt(time[0], 10),
      parseInt(time[1], 10));
  }
},
```
CSS3を使ってfooterを最下部に固定する方法

ページ内のコンテンツの量が少なくてfooterが浮いてしまっている時に、CSSを使ってfooterを最下部に固定する方法を紹介します。

まずはコードから

```html:sample.html
<body>
    <main>

      　<!--コンテンツの記述-->

    </main>

    <footer>

      　<!--フッターの記述-->

    </footer>
  </div>
</body>
```

```css:style.css
body{ 
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}

footer{
  margin-top: auto;
}
```

- bodyダグのflex-directionをcolumnにして、要素の並びを垂直の上から下方向に指定します。

- bodyタグのmin-heightを100vhにすることで、画面の高さいっぱいまで要素を広げることができます。

- footerタグのmargin-topをautoにすることで、余分な余白（footerの上にあるはずの余白）を全て指定できるので、fotterが画面の１番下に配置されます。

参考：[footerを常に最下部に固定する方法(簡単)](https://qiita.com/nozomi716/items/2b09d5bce3d08d1c41f9)
Gravioのインストール方法

[Gravio](https://www.gravio.com/jp/gpromo/1901-casual-iot)のインストールまでの流れをメモとして残しておきます。
なお、本コンテンツの内容はWindows 10 PCおよびMicrosoft Store,インターネットへの接続を利用することを前提に作成しています。

[Gravio](https://www.gravio.com/jp/gpromo/1901-casual-iot)は、IoTの実装を簡単に行うためのエッジ型ソフトウェアです。コードライティングによるプログラミングを必要とせず、GUIによりアプリケーション上にて各種設定を行うことで、気軽にセンサーデータを収集、活用できます。
Windows 10 PCがあればすぐに利用可能です:kissing_smiling_eyes:
#インストール
先にも記載しましたが、今回はWindows PCへのインストール方法を記載していきます！
Gravioのサービスは、
1. GUIベースでセンサー、データの振る舞いを設定していく **Gravio Studio**
2. Studioで設定された振る舞いにもとづき、センサーとの実質的な接続管理、データ蓄積、各種動作などを行う **Gravio Server**
上記2つのアプリケーションサービスから成り立っています。

###1.Microsoft StoreでGravioを入力して検索
![image.png](https://qiita-image-store.s3.amazonaws.com/0/247209/3d7f4e7c-fa42-8c2d-c0d8-35d3966ffc26.png)
###2.Gravio Studioをクリック
![image.png](https://qiita-image-store.s3.amazonaws.com/0/247209/56fb045c-1b6a-8dd7-5771-1abf0232070e.png)
手順としては、Studioを入れたあと

**[こちらをクリックするとMicrosoft Storeと接続します。](https://www.microsoft.com/en-us/p/gravio-studio/9n0d93kpvw3c?source=lp#activetab=pivot:overviewtab)**
###3.インストールをクリック
###4.Gravio Serverをダウンロード
こちらのサイトからインストーラーをダウンロードします。
https://www.gravio.com/jp/product#Gravio-Server
![image.png](https://qiita-image-store.s3.amazonaws.com/0/247209/fcda13fa-2d89-e7fe-8b38-d87a4567c83d.png)
###5.Gravio Serverをインストール
ダウンロードした「GravioService.msi」を実行します。
インストーラーが起動するので、表示されるメッセージに従って、インストールを実行します。
インストールが完了すると、自動的にGravio Serverが起動状態になります。
###6.サインアップ
Gravio Studioを起動し、初期画面からサインアップします。
登録したメールアドレスに認証コードが届くので、そちらを入力しログインします。
ログインすると、サーバー管理画面が表示されます。
サーバーアイコンをダブルクリックすることで、各種センサーの設定ができます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/247209/8e228d5d-4bad-6fc6-256d-a0e9dfc25efb.png)




以上で、Gravioの使える環境が整いました。
インストール方法については、こちらの動画も見ていただければと思います！

**↓↓↓動画↓↓↓**
[![インストール方法動画](http://img.youtube.com/vi/vQbOPj2RrIs/0.jpg)](http://www.youtube.com/watch?v=vQbOPj2RrIs)
#ライセンスアップグレード→センサーの貸出依頼
Gravioでは用途にあわせて複数のプランをご用意しています。有償のBasicプランにて
ご契約をいただくことで、Gravio認証センサーの無償貸出サービスが利用可能です。
プランのアップグレード方法、及び貸出依頼の方法については下記をご参照ください。
##プランのアップグレード
![image.png](https://qiita-image-store.s3.amazonaws.com/0/247209/16df8d82-054a-5907-bb63-86ffc68ba005.png)
Gravio Studioからアップグレードをクリックします。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/247209/70625fac-52f2-f892-2678-c735f65e2e92.png)
**¥500/月**がベーシックプランの金額です。こちらをクリックいただくと、自動的にMicrosoft Storeへ接続し、支払いに関するクレジットカードの登録画面が表示されます。
こちらにご登録いただくことで、自動的にプランがアップグレードされます
##センサーの貸出依頼
Basicプランを契約すると、センサーの貸出依頼ができるようになります。
弊社の[カスタマーサイト（Gravio Cloud User Website）](https://gc1.gravio.com/ui/login)にアクセスし、ログインします。
ログインは上記で設定したメールアドレスとパスワードでできます。

デバイスメニューから利用したいデバイスを選択し、必要事項を記入し、”貸出注文を実行”を押します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/247209/07048c27-ee81-0af5-5575-ad03f4475f8d.png)

こちらで入力いただきました情報をもとに、弊社よりデバイスを後日送付いたします。

![センサーイメージ_2.jpg](https://qiita-image-store.s3.amazonaws.com/0/247209/d64e027c-d89a-c96c-453f-ce89d9c78733.jpeg)


（写真はサンプルです。）

以上がセンサーの貸出までの流れとなります。

#あとがき
今後は[Gravio](https://www.gravio.com/jp/gpromo/1810-start-edgeware)でできることについては、今後も投稿していきます！
こんな使い方できたよ！などは、ぜひ投稿していただけると嬉しいです:grin:
ESLint の組み込み JSDoc サポートが終了します

この記事は公式アナウンス "[End-of-Life for Built-in JSDoc Support in ESLint](https://eslint.org/blog/2018/11/jsdoc-end-of-life)" の翻訳です。

----

ここ数年で ESLint プロジェクトのサイズとスコープは増大しました。もともとは単一の実行可能パッケージで構成されていましたが、今や複数のパーサーやユーティリティを含む多数のパッケージから構成されるプロジェクトへ成長しました。プロジェクトは成長しましたが開発チームの規模はそれほど大きくなく、全員がボランティアである開発チームがこのプロジェクトをメンテナンスしていくために、厳しい決定を下す必要がありました。

最近のレビューの一環として、ESLint プロジェクトでの JSDoc の組み込みサポートを廃止することを決定しました。 この投稿には、その決定の詳細と次に起こることが含まれています。

## 現在の JSDoc サポート

現在の ESLint の JSDoc サポートは以下のルールと API から構成されます。

- [Doctrine] コメント パーサー
- [require-jsdoc] ルール
- [valid-jsdoc] ルール
- [SourceCode#getJSDocComment()] メソッド

特に、ESLint の他の部分を維持するために必要な努力と比較して、[Doctrine] と[valid-jsdoc] ルールを維持するために多くの時間を費やしていました。本日 (訳注: 2018/11/27) より、上記 4 項のサポートを廃止します。

## サポート廃止が意味すること

今後 数週間のうちに以下のことを実施します。

- [Doctrine] コメント パーサーのリポジトリは凍結されます。2週間前にリリースされた v3.0.0 が最後のメジャーリリースになります。完全にメンテナンスを停止する前に 2,3 の小さな更新が必要かもしれませんが、機能追加やバグ修正はもう行われません。ただし、[Doctrine] は信頼できる状態にあるので、誰かが [Doctrine] をフォークして JSDoc コメントパーサーを開発したい場合は、きっとうまく行くでしょう。
- [require-jsdoc] と [valid-jsdoc] ルールは非推奨になります。私たちの[ルール非推奨ポリシー](https://eslint.org/docs/user-guide/rule-deprecation#rule-deprecation)に従い、これらのルールはすぐには削除されませんが、機能追加やバグ修正は行われません。将来のメジャーバージョンアップの際に削除される可能性があります。
- [SourceCode#getJSDocComment()] メソッドは非推奨になります。今後、このメソッドに関する機能修正やバグ修正は行われません。将来のメジャーバージョンアップの際に削除されます。

実際にいつ頃 実施されるかを知るためには、今後のリリースノートをご確認ください。

## 推奨する移行先

引き続き ESLint で JSDoc コメントをチェックしたい場合、コミュニティによって維持されている [eslint-plugin-jsdoc] プラグインを利用することを推奨します。このプラグインは非推奨になったコアルールより JSDoc に特化している複数のルールを保持しており、ほとんどのプロジェクトにフィットするはずです。

## FAQ

### [Doctrine] のために新しいメンテナを探さなかったのは何故ですか?

私たちが [Doctrine] のための新しいメンテナを探さないことにした理由はいくつかあります。

まず、パッケージが広く使用されている割に、ESLint 開発チームの外からの貢献はほとんどありませんでした。いくつかのパッケージは外部の貢献者から多くの注目を集めているので新しいメンテナを見つけるのは簡単かもしれませんが、[Doctrine] はそうではありませんでした。

第二に、[Doctrine] は [JS Foundation] の一部であるため、メンテナンスを引き継ぐということは、[JS Foundation] に関与することを意味します。単一のメンテナがこれを採用することに決めた場合、それはあまりにも大きなオーバーヘッドになる可能性があります。

毎週 700 万回近いのダウンロードが行われていたため、既存のユーザーのためには、新しいメンテナを探すことよりも [Doctrine] プロジェクトを凍結することのほうが良いと思います。私たちは、既存のメンテナンス方法を引き継ぐよりも、[Doctrine] をフォークして新しいプロジェクトを始める方が、[Doctrine] の維持に関心のある新しいメンテナにとって良い選択肢だと信じています。

(訳注: 同日に話題になった、[event-stream のメンテナンス引き継ぎに伴うセキュリティ インシデント](https://qiita.com/mysticatea/items/aac027f9183ea9f0f9b1)を念頭に置いた文章です)

### 既存の JSDoc ルールを削除せずにメンテナンスを停止するのは何故ですか?

[eslint-plugin-jsdoc] プラグインに移行するための じゅうぶん な期間を設けるためです。私たちは、ユーザーに設定ファイルの更新を要求する回数を最小限に抑えたいと考えています。既存の JSDoc ルールを使用可能にしながら非推奨にすることで、ユーザーが徐々に移行できるようにしながら、開発チームのリソースをプロジェクトの別の部分に割り振ります。

### 他の質問がある場合は?

[メーリングリスト](https://groups.google.com/group/eslint) で質問してください。
(訳注: 日本語で質問するには、[こちらのリポジトリ](https://github.com/eslint/eslint-jp)に Issue を作るか、[日本語チャットルーム](https://gitter.im/eslint/eslint-jp)をご利用ください)

[Doctrine]: https://github.com/eslint/doctrine
[require-jsdoc]: https://eslint.org/docs/rules/require-jsdoc
[valid-jsdoc]: https://eslint.org/docs/rules/valid-jsdoc
[SourceCode#getJSDocComment()]: https://eslint.org/docs/developer-guide/working-with-rules#contextgetsourcecode
[JS Foundation]: https://js.foundation/
[eslint-plugin-jsdoc]: https://github.com/gajus/eslint-plugin-jsdoc
Alpine Linux を使った docker ビルドの最中に出る WARNING 対応

# Alpine Linux を docker ビルドする際の WARNING 対応

Alpine Linux を使って docker ビルドをすると下記のような WARNING がビルド中に出ることがあります。

```
WARNING: Ignoring APKINDEX.adfa7ceb.tar.gz: No such file or directory
WARNING: Ignoring APKINDEX.efaa1f73.tar.gz: No such file or directory
```

## 解決策

apk のレポジトリ情報が古いからということで、アップデートすれば解決するようです。
下記の `RUN apk update` を ENV の後の RUN の前に入れるとよいかと思います。

```profile:Dockerfile
FROM alpine:3.8

ENV ・・・・・
ENV ・・・・・
ENV ・・・・・

RUN apk update
```

## 終わりに

普通の Linux もインストール前にレポジトリのアップデートしてから使うので、alpine も同じくというところでしょうか。

## 参照

[apk WARNING Ignoring APKINDEX No such file or directory](https://github.com/gliderlabs/docker-alpine/issues/207)
Google Play Consoleに「ユーザー権限を管理」権限が追加された。これでアカウント所有者に頼んだり、ログインし直さなくてよくなった。

以下、公式ドキュメント。

[デベロッパー アカウント ユーザーの追加と権限の管理](https://support.google.com/googleplay/android-developer/answer/2528691?hl=ja)



Google Play Consoleには、最初に登録されるアカウントであり、フルアクセス権がある「アカウント所有者」という概念があった。

ある時点までは、ユーザーの追加や削除、権限の付与などはこの「アカウント所有者」でないとできなかった。

で、これでなにがめんどくさいかというと、メンバーの追加や権限の追加をするたびに、「アカウント所有者」に依頼したり、「アカウント所有者」でログインし直す必要があった。めんどくさかった。

さて、いつの時点からかはわからないのだけれども、「ユーザー権限を管理」という権限が追加された。



これにより、ユーザーの追加や権限の付与は「アカウント所有者」でなくてもできるようになり、アカウント所有者に頼んだり、ログインし直さなくてよくなった。

やったね！

ゆるぼ : いつからこの権限が追加されたかがわかるソース
Visual Studio Code + MarkDown + PlantUML で開発ドキュメントをわかりやすくする

# きっかけ

githubなどのソース管理ツールで新規のリポジトリを作成したり、フレームワークのプロジェクトを作成したりすると、Readme.mdができる。

Markdownは流行りというか、Qiitaでもお馴染みだし使いやすい。
しかし、画像（というか図）はどこかで作ってリンクするなど、ちょっと面倒だった。

そんな中、こんな記事を見る。
https://qiita.com/kitfactory/items/31cdf80cf1d3d8b42de8

「ああ、Markdownで図が描ける」

ということで、さっそく導入しようとしたのだが、最初の？？？となったところをまとめておく。

# 導入手順

Windows環境にVSCodeは既にインストール済みであることは前提として、次の手順が必要となる。

1. VSCodeの拡張機能で、「Markdown Preview Enhanced」をインストール
2. 以下URLから、「Graphviz(dot)」をインストール（msiファイルが楽）

    http://www.graphviz.org/download/

# 使い方
正しい使い方かわからないが、Markdownファイルを開いて、「Ctrl + k（Ctrl離す）v」というショートカットキーでMPEで開く。

「Markdown Preview Enhanced」は、Markdownのビューワである。
普通に右クリックしてプレビューしても、デフォルトのビューワで開いてしまって、MPEにならない。

MPEで開いた状態になれば、右クリックで画像やPDFでエクスポートすることもできる。

# 最後に
最初は、PlantUMLのモジュールなども入れないといけないのか？など色々入れようかどうか迷っていたが、実際には先述の2つのものを入れればよいので、プロジェクトで他のメンバーにも入れてもらいやすいかなと思う。

エクスポートも使えるので、最悪画像で共有することもできるし、結構良いと思っている。
仕様の説明は、できるだけソースと一緒に管理出来てほしいと思っていて、ソースのコメントだけで全て済ましたいとすら思っていた。
mdという別ファイルになってしまうものの、ソースの近くに置いておければ直ぐ仕様が分かるはず。
Excel信者は未だたくさんいると思うが、これでまた一段ソースのみの管理に一段近づけそう。
CentOS7にGUI環境を追加する方法

# 概要

CentOS7にGUI環境(X Window System)を追加する方法です。

# 手順

まずはGNOME Desktopをインストールします。

```
# yum -y groupinstall "GNOME Desktop"
```

インストールが完了したら起動モードをGUIへ変更します。

```
# systemctl set-default graphical.target
```

起動モードが変更されていることを確認します。
`graphical.target` と表示されていればOKです。次回起動時からはGUIに切り替わります。

```
# systemctl get-default
graphical.target
```

マシンを再起動すればGUI環境が使えるようになります。

```
# shutdown -r now
```
BigQueryで最小二乗法をやってみた

BigQueryMLなるものが登場し、すこしだけ世間が賑わって(?)ますが、この記事ではそれよりも原始的な最小二乗法をBigQueryで実装する方法を紹介します。

測定値のペア($x_i$ , $y_i$)が $n$ 個数あったときに、それらを $y=ax+b$ という直線でフィッティングすることを考えます。
ある測定値と直線との間の誤差 $e_i$ を以下の式で定義します。

```math
e_i = \left\{ y_i - (ax_i + b) \right\} ^2
```

このとき全測定値に対する誤差の合計 $E$ は、以下の式で与えられます。

```math
E = \sum_{i=1}^{n} e_i = \sum_{i=1}^{n} \left\{ y_i - (ax_i + b) \right\} ^2
```
$a$ と $b$ を適切に設定することでこの $E$ を最小化することを考えます。
$E$ を $a$ と $b$ で偏微分した結果が0と等しくなるという方程式を解析的に解き、以下を得ます。

```math
a=\frac{\displaystyle n\sum_{i=1}^n x_iy_i-\sum_{i=1}^n x_i\sum_{i=1}^n y_i}{\displaystyle n\sum_{i=1}^n x^2_i-\left( \sum_{i=1}^n x_i \right)^2}
```

```math
b=\frac{\displaystyle \sum_{i=1}^n x^2_i\sum_{i=1}^n y_i-\sum_{i=1}^n x_iy_i\sum_{i=1}^n x_i}{\displaystyle n\sum_{i=1}^n x^2_i-\left( \sum_{i=1}^n x_i \right)^2}
```

この式を愚直に使っても良いのですが、BigQueryに入力するのが大変なので、シンプルな式に変形します。

```math
a = \frac{\mathrm{Cov}(x, y)}{\sigma_x^2}
```

```math
b = \mu_y - a\mu_x
```

ただし、
$\mu_x, \mu_y$ は $x_i$ と $y_i$ の平均
$\sigma_x^2$ は $x_i$ の分散
$\mathrm{Cov}(x, y)$　は $x_i$ と $y_i$ の共分散です。

あとはここまでくれば、BigQueryのSQLで簡単に表現できそうです。
BigQueryには標本分散を求める式と不偏分散を求める式が別にあるので、その点注意が必要です。

```sql
  select COVAR_POP(x, y) / VAR_POP(x) as a, AVG(y) - COVAR_POP(x, y) / VAR_POP(x) * AVG(x) as b from data
```
マストドンのAPIからトゥートの時間を取得したときに少し困惑した話


#ちゅうい
あくまで2018年8月頃の話です。
その先の仕様変更によりこの記事は一切存在価値がなくなる可能性がありますのでご注意ください
また、インスタンスによって実際に帰ってくる値は違う可能性がありますので実際に取得してみることをおすすめします
今回はjpを使っています

#時刻を取得する
JSONとかparseするの面倒なのでgemを使う

```ruby:main.rb
require 'mastodon'
MASTODON_HOST = 'https://hoge.hoge'
access_token = 'hoge'

connection = Mastodon::REST::Client.new(base_url: MASTODON_HOST, bearer_token: access_token)
#account_idのアカウントのトゥートを10件取得
account_id = '00000'

status_data = connection.statuses(account_id, {"limit": "10"})

status_data.each do |toot|
    puts "#{toot.content} : #{toot.created_at}"
    puts ''
end
```
今回の場合は自分のアカウントの最新から10件取ってくる状態
これが一番上のトゥート
<img width="668" alt="スクリーンショット 2018-11-29 13.57.31.png" src="https://qiita-image-store.s3.amazonaws.com/0/320738/68d9b1ce-3c92-2445-1092-babd1beda56a.png">

これが実行結果

```
<p>クソ良いことを知ったので感謝しかない</p> : 2018-11-28T23:47:30.053Z

<p>RT <span class="h-card"><a href="https://mstdn.jp/@walk__walkway" class="u-url mention">@<span>walk__walkway</span></a></span> 初めて知った<br />tootleの上のアカウントのとこ横にフリックするとアカウント切り替えできる</p> : 2018-11-28T23:47:20.779Z

以下略
```

取得した時間が違う。しかもなんだこのフォーマット…

#どういうこと
なんかマストドンはAPIからトゥートした時の時間とか取ってくるとUTC時間を返してくるぽい
その証明に、先程取得してきた時間+9時間するとJST時間になる

なので、実際に表示する前に9時間分こちらで加算してやる必要があります

#対策
帰ってきている値が文字列なので、とりあえず**Time.parse()**でDate型に変換した後、9時間分加算すればよい

```ruby:main.rb
status_data.each do |toot|
    jst_time = Time.parse(toot.created_at) + 60 * 60 * 9
    puts "#{toot.content} : #{jst_time}"
    puts ''
end
```

とりあえずparse投げとけばなんとかなる感ある(プライドはない)
実行結果

```
<p>クソ良いことを知ったので感謝しかない</p> : 2018-11-29 08:47:30 UTC

<p>RT <span class="h-card"><a href="https://mstdn.jp/@walk__walkway" class="u-url mention">@<span>walk__walkway</span></a></span> 初めて知った<br />tootleの上のアカウントのとこ横にフリックするとアカウント切り替えできる</p> : 2018-11-29 08:47:20 UTC
```

UTCってなっちゃってるけど、とりあえずいい感じになりました　　＜終＞

#rails使う場合はもう少し楽に変換できる
こんな面倒な計算使わなくてもJST時間に変換できるようです。それもメソッド1発で。すげえな

```ruby:main.rb
status_data.each do |toot|
    jst_time = toot.created_at.in_time_zone('Tokyo')
    puts "#{toot.content} : #{jst_time}"
end
```
rails様様でした。　＜終＞
TypeScript + Express + Jest でmockを使った簡単なユニットテストを書く

# このドキュメントについて。
TypeScriptで書かれたExpressの簡単なファイルをJestで書いたもののメモです。
タイトルの３つの技術についての解説は行いません。

# 書いたコードの解説
試したかったことは、Jestのmock機能です。
TypeScript + Expressで実装された、モジュールをimportしている関数を用意します。
そのモジュールをJestの機能でmock化し、関数のユニットテストを書くサンプルです。

### テスト対象コード
Expressのルーターから呼ばれる`getJson.ts`です。
受け取った`req`の`req.query.price`を`addYen.exec`に渡し、返り値をJSON形式に詰めて返しています。
こちらのテストを書きます。

```typescript:src/getJson.ts
import * as express from 'express'
import * as addYen from "./addYen";

export default (req: express.Request, res: express.Response, next?: express.NextFunction) => {
    const yenPrice: string = addYen.exec(req.query.price);
    res.json({ price: yenPrice });
}
```

### テスト対象がimportしているコード
`getJson.ts`がimportしている`addYen.ts`です。
引数に「円」を付けて返すだけの関数`exec`を持っています。

```typescript:src/addYen.ts
export function exec (req: string){
    return req + " 円";
}
```

これらを準備したら、テストコードを書きます。
### テストコード
```typescript:test/getJson.test.ts
import { mockReq, mockRes } from "sinon-express-mock";

// テスト対象がimportしているモジュールをモック化する
jest.mock('../src/addYen', () => ({
  exec: jest.fn(() => `100 円`),
}));

// モックを読み込み
const addYen = require('../src/addYen');

// テスト対象を読み込み
import getJson from "../src/getJson";

describe("getJson", () => {
  it("正常に処理が終了する", () => {
    // Expressのrequestオブジェクトとresponseオブジェクトをモック化
    const request = {
      query: {
        price: "100"
      }
    };
    const req = mockReq(request);
    const res = mockRes();

    // テスト対象を実行
    getJson(req, res);

    // addYen.exec()が引数request.query.priceで実行されていることを検証
    expect(addYen.exec).toBeCalledWith(request.query.price);
    // addYen.exec()が1度だけ実行されていることを検証
    expect(addYen.exec.mock.calls.length).toBe(1);
  });
});
```
ディレクトリ構成は以下のようになっています。

```zsh
├── src
│   ├── addYen.ts
│   └── getJson.ts
├── test
│   └── getJson.test.ts
```

ひとまずやりたかったことはできました。

# やってみての疑問
### Jestの機能
今回テストコードの中で、Expressのrequestオブジェクトとresponseオブジェクトをモック化するために`sinon-express-mock`をimportしましたが、Jestにそういった機能があるかは調べていませんので、もしかしたらJestだけで今回のテストコードが書けるかも、、？
supertest的なこともできるのかな？

### requireとimportの違い
テストコードでのモック読み込み部分ですが、importではうまくいかなかったのですが、requireにすることで実行できました。
requireとimportについて、もっと中身を知らないと。。

```typescript:test/getJson.test.ts
// モックを読み込み
// requireでは動いて、
const addYen = require('../src/addYen');
// importでは動かなかった。。
import * as addYen from '../src/addYen';
```

# 参考文献
 - [Mock Functions · Jest](https://jestjs.io/docs/ja/mock-functions)
 - [テスト初心者が初めに覚えた依存モジュールのモック化5パターン（Jest + TypeScript) - Qiita](https://qiita.com/vimyum/items/4ded9de3b784afd31025)
 - [sinon-express-mock - npm](https://www.npmjs.com/package/sinon-express-mock)
シングルボードコンピュータの温度をNagiosで監視する

#シングルボードコンピュータの温度をNagiosで監視する

2回目の投稿です。今回は、シングルボードコンピュータのCPU温度をNagiosで監視するためのスクリプトです。Nagiosだけではなく、監視対象側でcheck_nrpeを使ったコマンドが動いていることが必要です。

もっとキレイに書けると思いますが、シェルスクリプトは初めてなのでご容赦ください。

##そもそも、Rock64で温度を表示するには

``cat /sys/class/thermal/thermal_zone0/temp``

を使います。ただし、なぜか数字5桁で戻ってきてしまいます。これは、小数3位までの温度表示となっていて、例えば「27621」であれば「27.621度」ということになります。

参考サイト：https://forum.pine64.org/showthread.php?tid=6645

これを、シェルでNagiosのプラグインにしてみました。温度の警告範囲をシェルの引数からもらって設定するのもよいかとは思いましたが、それほどこだわる必要もないと思ったので、40度までは正常、60度まではWARNING、それより大きくなればCRITICALと設定することにしました。
（空冷式（むきだし）のシングルボードなので、今回の設定値にしましたが、今後、状況を見て見直していきます）

ということで、初めてのシェルスクリプトでのコードです。初めてなので、あちこち参考にしました。ありがとうございます。

参考サイト：
https://qiita.com/egawa_kun/items/196cd354c0d8e4e0fefc
https://pingubrog.exblog.jp/15934437/

##監視される側（監視対象ノード）の設定

###チェックスクリプト

```check_thermos.sh
#!/bin/sh

 temp1=$(cat /sys/class/thermal/thermal_zone0/temp)
 tempA=`echo "scale=3; $temp1 / 1000 " | bc `

echo "Temp is ${tempA} "

if [ "$(echo "$tempA >= 60" | bc )" -eq 1 ]; then
        exit 2;
elif [ "$(echo "tempA >= 40" | bc )" -eq 1 ]; then
        exit 1;
else
        exit 0;
fi

```

###導入方法

```
cd /usr/lib/nagios/plugins
sudo vi ./check_thermos.sh
（上記のコードをコピーして貼り付け）
sudo chmod a+x ./check_thermos.sh
```

監視される側（監視対象ノード）のnrpe.cfgに、以下の項目を追加します。

```
command[check_thermo]=/usr/lib/nagios/plugins/check_thermo.sh
```

完了したら、nrpeを再起動してください。

##監視する側（Nagiosサーバ）側の設定

command.cfgに以下の項目を追加します。

```command.cfg
define command {

    command_name    check_thermo
    command_line    $USER1$/check_nrpe -H $HOSTADDRESS$ -c check_thermo
}
```

次に、監視定義ファイルに以下の定義を追加します。

```
define service{
    use                 generic-service
    host_name           192.168.***.***  #監視対象のホスト名を入力します。
    service_description Temperature
    check_command       check_thermo
}
```

これで、NagiosのPre-Flight checkを通してから、Nagiosを再起動すると、温度が監視対象となります。

###おまけ
RockPro64の場合は、thermal_zone1もあるので、2個のセンサーから情報を取得することができます。ただ、どういう条件を設定すべきなのか、というのは判断が必要です。

- 平均値を取る、という考え方もありますが、負荷にバラツキがあると、平均値にダマされてしまいます。
- このため、高い方と低い方の温度差を監視の対象にする、という考え方もあります。一方に負荷が寄っているということになるので、監視することに意味はあります。
- 高い方で判断する、という考え方もあります。温度での警告という趣旨を踏まえれば、こちらの考え方が良いのかもしれません。
- そもそもセンサーが2個あるのであれば、両方の温度を監視対象にする、というのが正しいような気もします。

他のシングルボードでも、温度センサーがあれば同じように監視は可能です（おそらく、上記のコードのcat以下の部分を当該コマンドに変更するだけで大丈夫でしょう）。
[Lightning Network] lnd のインストール

Ubuntu 16.04 LTS に入れる。

[公式のとおりにやればOK](https://github.com/lightningnetwork/lnd/blob/master/docs/INSTALL.md)と思いきや、以下のコマンドが動かない。

```
sudo apt-get install golang-1.11-go
# E: Unable to locate package golang-1.11-go
# E: Couldn't find any package by glob 'golang-1.11-go'
# E: Couldn't find any package by regex 'golang-1.11-go'
```

そんなものは無いと言われる。そりゃないよ。。

というわけで、以下を参考にして手動で GO 1.11 を入れる。
https://medium.com/@RidhamTarpara/install-go-1-11-on-ubuntu-18-04-16-04-lts-8c098c503c5f

```
# 最新にアップデート
sudo apt update
sudo apt -y upgrade

# テンポラリフォルダに GO をダウンロード
cd /tmp
wget https://dl.google.com/go/go1.11.linux-amd64.tar.gz

# /usr/local に移動
sudo tar -xvf go1.11.linux-amd64.tar.gz
sudo mv go /usr/local

# 不要ファイルを消す
rm go1.11.linux-amd64.tar.gz

# 環境変数の追加
sudo vi ~/.bashrc
```

`.bashrc` の末尾に以下を追加する。

```
export GOROOT=/usr/local/go
export GOPATH=$HOME/go
export PATH=$GOPATH/bin:$GOROOT/bin:$PATH
```

これで完了。 `source ~/.bashrc` で再読込を行う。
然る後に、以下のコマンドでインストールできているか確認する。  

```
go version
# go version go1.11 linux/amd64
```

## lnd のインストール

今度こそ[公式](https://github.com/lightningnetwork/lnd/blob/master/docs/INSTALL.md#user-content-installing-lnd)のとおりに入れてみる。

```
go get -d github.com/lightningnetwork/lnd
cd $GOPATH/src/github.com/lightningnetwork/lnd
make && make install
```

アップデートは以下でできる模様。

```
cd $GOPATH/src/github.com/lightningnetwork/lnd
git pull
make clean && make && make install
``` 

# bitcoin に接続

公式では[btcd](https://github.com/lightningnetwork/lnd/blob/master/docs/INSTALL.md#user-content-installing-lnd)を推奨している。

今回はすでに bitcoind を入れているので、それを使う。何も入ってないなら btcd でやったほうがいいっぽい。

bitcoin.conf を以下の通り編集する。

```
[Application Options]
# ネットワークに公開するIP及びポート情報
externalip=<自分のIPを入れる>:<使うポートを指定。IPだけを記載すると9735が自動的に付与される>

# lnd が受け付けるIP及びポート情報。下記の場合、9735 ポートは全てのipv4 アドレスを受け入れ、
# 9736 ポートは全てのipv6 アドレスを受け入れる。
listen=0.0.0.0:9735
listen=[::1]:9736

# lnd の RPCサーバの設定。下記の場合、10009ポートが全てのアクセスを受け付ける
rpclisten=0.0.0.0:10009 

[Bitcoin]
bitcoin.active=1
bitcoin.mainnet=1
bitcoin.node=bitcoind

zmqpubrawblock=tcp://127.0.0.1:28332
zmqpubrawtx=tcp://127.0.0.1:28333
```

以下のコマンドで実行。rpcuser や rpcpass を設定している場合は、適宜指定する。 `lnd -h` で指定方法が見られる。

```
lnd --bitcoin.active --bitcoin.mainnet --debuglevel=debug --bitcoin.node=bitcoind
```

ウォレットを作らないと動かないので、以下のコマンドでウォレット作成

```
lncli create
```

パスワードの要求などをされるので、適切に答えてウォレットを作成する。 LND CIPHER SEED を忘れずにバックアップを取るように！

最後に以下で動作確認。

```
lncli getinfo
```

`uri` の部分が自分のノード情報。
下記は著者のノード情報。みんな繋いでね。

```
"uris": [
    "034abf8f8549216ab7d775e57ae837a0cc9f22506797da349594dffa22384f7b53@35.211.30.191:9735"
]
```
学習内容まとめ（超個人的）

## 活動目的
- Ruby, Ruby on Rails について学習開始！
- 学習に伴い学んだことはQiitaにアウトプット！

## このノートの位置づけ
- 上記作業に伴い参考にしたリンク等を整理しておくためのノート
- アウトプットとして作成したノートの整理



## リンク集
- Markdown記法 サンプル集：https://qiita.com/tbpgr/items/989c6badefff69377da7
- SublimeText3を2年以上使って、生き残ったPluginを紹介:https://qiita.com/MakoTano/items/8853caa206283df5e1f9#%E7%A9%8D%E6%A5%B5%E7%9A%84%E3%81%AB%E3%82%AA%E3%82%B9%E3%82%B9%E3%83%A1-gitgutter
- テキストエディタ、設定チェックリスト10項目:https://qiita.com/jnchito/items/0ad568263f3419775d33

## ノート一覧
- Gitの基本：https://qiita.com/SAYJOY/items/fbcf1b76a26b8220b49f
- Rubyの基本：https://qiita.com/SAYJOY/items/6b9ad8bf50b20516d478
- Railsの基本：https://qiita.com/SAYJOY/items/b1cbafcbd177cc9b6b2f
Espresso テストが BuildType release でエラーになる

## 概要
Espresso テストの BuildType を release に変えたらエラーになってしまったので、debug 以外で実行できる方法を調べた。

## 前提
Android Studio 3.1.4
Esporesso 3.0.2
BuildType : debug / release の2種類が存在

## 経緯
今さらながら既存のAndroidプロジェクトにEspressoを導入しようとしたが、
プロジェクトの都合上 `BuildType` が `debug` ではなく `release` の方で動かしたかった。
`debug` の状態で問題なくテストが動いていることを確認したあと `BuildType` を `release` に変更したら、見事にテストクラスが真っ赤っかに。

## 対応内容
テストはデフォルトで `debug` に対して実行されるらしく、それ以外のビルドタイプで実行するには下記設定が必要とのこと。

引用元：https://developer.android.com/studio/build/gradle-tips?hl=ja

>テストするビルドタイプの変更
>
>デフォルトでは、すべてのテストは、デバッグ ビルドタイプに対して実行されます。対象を別>のビルドタイプに変更するには、モジュール レベルの build.gradle ファイル内で >testBuildType プロパティを使用します。たとえば、"staging" ビルドタイプに対してテ>ストを実行する場合は、次のスニペットのようにファイルを編集します。

ということなので、ここを release で設定してやるとエラーが全て消えて release でも実行ができるようになりました。

```
android {
    ...
    testBuildType "release"
}
```

## 注意
そもそも release でテストをするようには作られていないので、`Run -> Recorder Espresso Test` のテストレコーダーも動きませんでした。
下記のように `File -> Project Structure -> Modules -> release -> Build Type` で `Debuggable true` にしてやれば動くようになりますが、これは正しい使い方ではないので別のビルドタイプを用意するなどの必要がありそうです。

<img width="813" alt="スクリーンショット 2018-11-29 14.01.50.png" src="https://qiita-image-store.s3.amazonaws.com/0/42506/653dcee0-c4ca-93df-19de-9df93fc22ec8.png">
UNCパスを利用してファイル/フォルダを削除するバッチ

こんにちは。@mashiです。

UNCパスを利用し、別サーバ(※自身が管理しているイントラ内のファイルサーバ)に存在するファイルやフォルダを削除するバッチを作成しました。

<pre>
@ECHO OFF

SET TARGET_DIR=[対象ディレクトリ]
pushd %TARGET_DIR%
SET NET_DIR=%cd%

rem ●日以上更新していない一時ファイルを削除
forfiles /P %NET_DIR% /S /D -● /c "cmd /c del @file /Q /ah"

rem ▲日以上更新していないファイルを削除
forfiles /P %NET_DIR% /S /D -▲ /c "cmd /c del @file /Q"

rem 空ディレクトリを削除
for /f "usebackq delims=" %%i in (`dir %TMP_DIR% /a:d /b /s ^| sort /r`) do rd "%%i"

exit 0
</pre>

一時ファイルを削除しないと隠しファイルが削除されず、空のディレクトリが残ることがあります。
また、「cd」コマンドではUNCパスを利用できないため、注意が必要です。
TwiMLアプリでユーザートラッキングするなら気をつけろ

# TwiMLアプリケーションのユーザートラッキング

少々細かい話ですが、IVRのサービス改善には必ず必要なトラッキングについてちょっと困ったことがあったのでメモしておきます。

IVRなどの自動応答サービスを構築する際に、顧客がどこまで通話を進めたのかをチェックするのは重要です。通常のウェブサービスならGoogle Analyticsのようなサービスを使ってサイト内のユーザーの動向を簡単に把握することができますが、TwiMLアプリケーションの場合、毎回のアクセスはステートレスなので、CallSIDを使ってユーザーのサービス内での回遊状況を追いかける方法を自前で用意しなければいけません。

一般的には、アクセス毎にデータベースなどにログを記録するような方法でこれを実現するわけですが、TwiMLアプリケーションの場合、実際にユーザーがどのタイミングで電話を切ったのか正確に把握する方法がありません。

```xml
<?xml version="1.0" encoding="UTF-8"?>
<Response>
  <Gather action="/auth" numDigits="4" timeout="10">
    <Play>https://example.com/files/greeting.mp3</Play>
    <Pause length="1"/>
    <Play>https://example.com/files/auth.mp3</Play>
  </Gather>
  <Redirect>https://example.com/auth</Redirect>
</Response>
```

例えば、ユーザーに認証用コードを入力してもらうようなケースを考えてみます。上のTwiMLは `greeting.mp3` というファイルで音声を再生し、1秒置いてから `auth.mp3` というファイルを再生しています。例えば「こちらは◯◯サービスです。ご利用ありがとうございます」という音声の後に「お客様の認証用コードを、4桁の数字で入力してください」みたいな音声が流れる感じでしょうか。 `Gather` の後にちゃんと同じ `action` に `Redirect` を置いているので、もしユーザーが何も入力しなくてもエラーにならない、大変いいサンプルコードですね。

# Twilioの謎挙動と対処方法

しかし、このTwiMLをサーバから送出した場合、実はユーザーがこのメッセージをどこまで聞いたのか知る方法はありません。 `/auth` に `Digits` パラメータを持ってアクセスしてきたなら端末のボタンを押して番号を送信してくれたことはわかりますが、メッセージをどこまで再生したのかはわかりません。しかも、それだけではありません。もっと厄介なことに、実はこの内容だと、ユーザーがこのメッセージを再生している最中に電話を切ったのか、それとも何も操作せず次のアクションに移ってから切ったのかを判定することができないのです（！）。

というのも、TwiMLの最後に `Redirect` があると、TwilioはこのTwiMLを処理した時点で電話を切ったとしても、 `Redirect` で指定されたURLに必ずアクセスしてくるという謎挙動をするため、このアクセスがユーザの行動によるものなのかTwilioが勝手にやって来ただけなのか、知る手段がないのです。

`Redirect` がなければ、もしユーザーが10秒間何もしなければアプリケーションエラーになってしまうので、なくすわけにもいきませんから、大変悩ましいところです。そこで、筆者は次の `/auth` の1つ前にリダイレクトをするだけの処理を入れておくことで、ユーザーが何も操作しなかったのかどうかを判定するといういじましい対応をしました。

```xml
<?xml version="1.0" encoding="UTF-8"?>
<Response>
  <Gather action="/auth" numDigits="4" timeout="10">
    <Play>https://example.com/files/greeting.mp3</Play>
    <Pause length="1"/>
    <Play>https://example.com/files/auth.mp3</Play>
  </Gather>
  <Redirect>https://example.com/go_to_auth</Redirect><!-- ココ！ -->
</Response>
```

そして、この `/go_to_auth` では下の単純なレスポンスを返します。

```xml
<?xml version="1.0" encoding="UTF-8"?>
<Response>
  <Redirect>https://example.com/auth</Redirect>
</Response>
```

もしユーザーが電話を切らずに操作せずリダイレクトして来た場合は、この `/go_to_auth` の `Redirect` は実行されてユーザーは次の案内に移動します。そうではなくTwilioがユーザーが電話を切ったにも関わらずアクセスして来ただけの場合は、この `Redirect` は実行されないので、ここでログを記録すればユーザーのアクションが判定できます。

# 最後に・お願い

こんなのロジックでもなんでもないとお怒りの方もいらっしゃるでしょうが、どうぞこのTwilioの挙動を直そうとTwilio社に連絡するようなことは決してなさらないでください。このマニュアルにない挙動に依拠したアプリケーションもあるのですから…
Python3.7.0のインストールに失敗

# はじめに
GCPでpythonの環境構築をしていて詰まって解決したところまで書きます。

# 試み
[こちらの記事](https://qiita.com/hasepyon/items/45ea509c64a6469ca5b6)を参考に環境を構築しようと試みました。

# 詰まったところ
記事を参考に`pyenv install --list`をみて`3.7.0`を入れようとしたところ、

```bash
$ pyenv install 3.7.0
    :
    :
Last 10 log lines:
    import pip._internal
  File "/tmp/tmpb4xjx23j/pip-10.0.1-py2.py3-none-any.whl/pip/_internal/__init__.py", line 42, in <module>
  File "/tmp/tmpb4xjx23j/pip-10.0.1-py2.py3-none-any.whl/pip/_internal/cmdoptions.py", line 16, in <module>
  File "/tmp/tmpb4xjx23j/pip-10.0.1-py2.py3-none-any.whl/pip/_internal/index.py", line 25, in <module>
  File "/tmp/tmpb4xjx23j/pip-10.0.1-py2.py3-none-any.whl/pip/_internal/download.py", line 39, in <module>
  File "/tmp/tmpb4xjx23j/pip-10.0.1-py2.py3-none-any.whl/pip/_internal/utils/glibc.py", line 3, in <module>
  File "/tmp/python-build.20181129025939.30648/Python-3.7.0/Lib/ctypes/__init__.py", line 7, in <module>
    from _ctypes import Union, Structure, Array
ModuleNotFoundError: No module named '_ctypes'
make: *** [install] Error 1
```
と出ちゃいました。
ちなみに`3.6.7`だと通りました。

# 解決
[同じ事例](https://qiita.com/hitochan777/items/941d4422c53978b275f8)を発見しました。
こちらだと`libffi-dev`をインストールしたら解決したようです。
yumで入れます。

```bash
$ sudu yum install libffi-dev
Loaded plugins: fastestmirror
Loading mirror speeds from cached hostfile
 * base: mirror.fileplanet.com
 * epel: d2lzkl7pfhq30w.cloudfront.net
 * extras: mirror.fileplanet.com
 * updates: mirrors.usc.edu
No package libffi-dev available.
Error: Nothing to do
```
そんな物は無い的なことをいわれたので、探します。

```bash
$ sudo yum search libffi-dev
ghc-libffi-devel.x86_64 : Haskell libffi library development files
libffi-devel.i686 : Development files for libffi
libffi-devel.x86_64 : Development files for libffi
```
ありました。`libffi-devel`でいいのかな？

```bash
$ yum install libffi-devel
  :
  :
Installed:
  libffi-devel.x86_64 0:3.0.13-18.el7                                                               

Complete!
```
今度は行けたようです。
このあと`$ pyenv install 3.7.0`でインストール出来ました。
docker-compose+MySQL5.7(+8.0)+初期化+永続化

## 概要
dockerは業務経験あったけどdocker-compose使ったことないし（当時）MySQLの環境構築も慣れてなかったのでやってみました。
で、どうせなら初期データもあったほうがいいよね！といった感じです。
こうすればできる！というところを目指してやった備忘録として残します。

## 環境
* Mac OSX(HighSierra〜Catalina)
* docker
    * version 18.09.0, build 4d60db4
* docker-compose
    * version 1.23.1, build b02f1306
* docker, docker-composeについてはメジャーバージョンが変わらなければ基本大丈夫かと思います。多分・・・

## 解説すること、しないこと
* すること
 * 表題のことをする手順
* しないこと
 * MySQL/docker/docker-compose環境構築の方法や仕組みなど

## 手順
* 環境構築します。以下について、事前に行ってください。
    * docker、docker-composeコマンドを使用できる状態にする
    * `mysql:5.7`のdockerイメージをpull
* docker-composeを実行するディレクトリを作成し、docker-compose.ymlを作ります

```docker-compose.yml
version: '3.7' # 修正時点で最新のversionを記載しています。1.xは動きませんのでご注意を。
services:
    db:
        image: mysql:5.7
        ports:
            - "3306:3306"
        volumes:
            # 初期データを投入するSQLが格納されているdir
            - ./db/mysql_init:/docker-entrypoint-initdb.d
            # 永続化するときにマウントするdir
            - ./db/mysql_data:/var/lib/mysql
        environment:
            MYSQL_ROOT_PASSWORD: root
            MYSQL_USER: test
            MYSQL_PASSWORD: test
```
* volumesでマウントするディレクトリを、docker-compose.ymlのレイヤー下に作成します。こんな構造のディレクトリになると思います。

```
(docker-compose実行dir)
┗ docker-compose.yml
┗ db/
  ┗ mysql_init/
  ┗ mysql_data/
```

* 作成した `mysql_init` にSQLファイルを作成します。
    * とりあえず動かしたいという方は以下ファイルを中身ごとコピペしていただければ良いと思います。

```sql:1_create.sql
CREATE DATABASE sample;
use sample;

CREATE TABLE users (
    id INT(11) AUTO_INCREMENT NOT NULL,
    name VARCHAR(64) NOT NULL,
    PRIMARY KEY (id)
);
```

* `docker-compose up -d` で起動しましょう。接続できるようになっていますので、mysqlのcliなりworkbenchなりで確認してみてください。
    * `-d`オプションをつけることで、バックグラウンドで実行してくれます。詳細は[こちらを](http://docs.docker.jp/compose/reference/up.html)

## 解説
`volumes` の役割を理解することが今回の鍵とみています。
今回はdockerコンテナ内の二つのディレクトリを、ローカルのディレクトリにマウントさせることで初期化と永続化を実現しています。

* `/docker-entrypoint-initdb.d`
 * dockerコンテナとして起動したときに、ここにあるSQL文を実行してくれるようです。
 * 今回はやりませんでしたが、shファイルの実行もしてくれます。
* `/var/lib/mysql`
 * 名前で察していただけると思いますが、コンテナのmysqlのシステムファイルがここに詰まっています。
 * これをvolumesでローカルのディレクトリからバインドすることで、同じ構成のファイルがローカルにも作成されます
    * 今回の構成では、`./db/mysql_data`が該当します。

## さいごに
* 今回は当時開発していたシステムのローカルDB環境を作る目的でやったのでMySQL5.7でやりましたがMySQL8.xにも近々向き合わねばなるまい・・・

## おまけ

### MySQL8.0で試す場合
* まず`docker-compose.yml`の`image`をMySQL8.0のものに直し、`volumes`に次の行を追加します。

```docker-compose.yml
            - ./db/mysql_conf/:/etc/mysql/conf.d
```
* `./db/mysql_conf`ディレクトリを作成し、そのディレクトリに`default_authentication.cnf`ファイルを作成します。内容は下記。

```default_authentication.cnf
[mysqld]
default_authentication_plugin=mysql_native_password
```
* MySQL8.0以降では接続時の認証方式が変更になっているため、このファイルが必要になるようです。この事実や、対策方法はいろいろなところで紹介されていますが、一応ここでも載せておきます。

### MySQLのEOLについて
* MySQL5.7は2020年にEOLを迎えており、LTSである8.0のEOLも2025年に迫りつつあります。なので、これから長期運用を考えるならば別のRDBの選択肢を取るのがベターと考えます。
    * 参考→ https://endoflife.date/mysql
* MySQLの代替としてMariaDBが選択されるケースが多いようなので、同様の記事をMariaDBで近々かけたらいいなと思ってます。
    * と思っていたらMySQL8.1というものがリリースされたではないか。RDBの明日はどっちだ。
* ともあれ、お仕事ではEOLを迎えたスタックだらけのシステムの運用保守を担当することも少なくないはず。この記事を残しておくことでそんな方々の一助になれれば幸甚です。
Swift：macOSのシステムサウンドを鳴らす

macOSプリセットのシステムサウンドを鳴らす処理は非常に簡単に実装可能です．
`Mojave`時点で14種類の音を再生可能なようです．
サウンドファイルの実体が置いてある場所
`/System/Library/Sounds/`

```swift:NSSound
NSSound(named: "Basso")?.play()
NSSound(named: "Blow")?.play()
NSSound(named: "Bottle")?.play()
NSSound(named: "Frog")?.play()
NSSound(named: "Funk")?.play()
NSSound(named: "Glass")?.play()
NSSound(named: "Hero")?.play()
NSSound(named: "Morse")?.play()
NSSound(named: "Ping")?.play()
NSSound(named: "Pop")?.play()
NSSound(named: "Purr")?.play()
NSSound(named: "Sosumi")?.play()
NSSound(named: "Submarine")?.play()
NSSound(named: "Tink")?.play()
```
なお，音量はシステムの音量に依存します．
bootstrap modalを使ったIntro.jsの紹介

#bootstrap modalを使ったIntro.jsの紹介
web画面の操作説明は、いつも**Intro.js**を使っています。
PDFでマニュアルを作って、ボタンからダウンロードしてもらうのでもいいんですが、できるかぎりweb画面で完結したいものです。

今回は、bootstrapのmodalに対してもIntro.jsで画面説明する方法をご紹介します。
まずは、DEMOをご確認ください。
※ DEMOの「画面の説明開始」ボタン押下で、画面の説明が表示されます。

### 使用したライブラリとバージョン
+ jQuery : 3.3.1
+ Bootstrap : 4.1.3
+ Intro.js : 2.9.3

#DEMO
https://wakaba-bbq.github.io/Modal_Recommend/

#github
https://github.com/wakaba-bbq/Modal_Recommend

#Intro.js
https://introjs.com/

#内容
今回の画面説明では、Intro.jsに必要な「data-step」・「data-intro」の属性をjavascriptにて動的に設定しています。
理由としては、htmlの各タグに「data-step」属性を直接書いてしまった場合、抜け漏れが発生した際にすべての「data-step」の修正が発生し、時間がかかってしまうためです。
また、親画面とmodal画面の2つの画面を別々に説明しているため、必要な説明を適時判断させる必要があります。

```index.js
step = 1;
intro = "";

// aaa
$('#aaa').attr('data-step', step);
intro = 'aaa 説明①';
intro += '<br>' + 'aaa 説明②';
intro += '<br>' + 'aaa 説明③';
$('#aaa').attr('data-intro', intro);
step++;

// bbb
$('#bbb').attr('data-step', step);
intro = 'bbb 説明①';
intro += '<br>' + 'bbb 説明②';
intro += '<br>' + 'bbb 説明③';
$('#bbb').attr('data-intro', intro);
step++;
```

このように「data-step」を加算しながら、説明したいタグに属性を追加することで、説明箇所の抜け漏れが発生してもすぐに修正できます。


```index.js
introJs().setOptions({
	nextLabel:"次へ",
	prevLabel:"前へ",
	skipLabel:"スキップ",
	doneLabel:"終了"
	}).start().onbeforeexit(function(){
		$('[data-step]').each(function(index, value){
		$(this).removeAttr('data-step data-intro');
	});
});

```
introJsのonbeforeexitを利用して、Intro.jsでの画面説明が終わった後に「data-step」属性を持っているタグに対し「data-step」・「data-intro」を削除しています。
こうすることで、親画面・modal画面の説明を分けて実装することができます。

modal画面にIntro.jsを導入して、画面説明するには、2つ注意点があります。

```index.js
introJs('#exampleModal').setOptions({
	nextLabel:"次へ",
	prevLabel:"前へ",
	skipLabel:"スキップ",
	doneLabel:"終了"
	}).start().onbeforeexit(function(){
		$('[data-step]').each(function(index, value){
		$(this).removeAttr('data-step data-intro');
	});
});
```
まずは、1行目の「introJs('#exampleModal')」のように、introJsの括弧の中に対象となるmodalの中に、modalの一番外側のdivのidを指定すること。


```introjs.css
.introjs-fixParent {
  /* z-index: auto !important; */
  opacity: 1.0 !important;
  -webkit-transform: none !important;
     -moz-transform: none !important;
      -ms-transform: none !important;
       -o-transform: none !important;
          transform: none !important;
}
```

もう一つは、introjs.cssの「.introjs-fixParent」の「z-index: auto !important;」をコメントアウトすることです。

#その他の機能
DEMOには、modalの中のタブを、親画面のボタンにてactiveにする方法や「html」のみでmodalを表示する方法と、「javascrip」を使ってmodalを表示する方法を実装しています。

よきmodalライフを！
apkからpackage, 署名, APIの向き先 を確認したい

こんにちは、[ニジボックス](https://nijibox.jp "nijibox")で働いてます、tack.saitoです。
弊社では受託開発も行っていて、APKを納品するスタイルなこともあります。

このAPKを渡す直前、思うことがあります。

- 本番のAPI向いてるよね...？
- packageは本番のモノだよな...
- 署名してる...よね

と不安になることがよくあります。

自分がビルドしていないケースもあれば、
きちんとビルドした...**(ような気がする)**
なんてこともあるわけで。

これらが守れてないと信頼もなくなるので、気をつけたいです。

てな流れがあり...

# packageと署名とAPIの向き先を確認、を1コマンドにしよう

## 今まで

### packageチェック
`/PATH/Android/sdk/build-tools/27.0.3/aapt  l -a application.apk | grep "A: package"`
で、表示されるものを確認(build-toolsのversionはよしなに)

### 署名
`keytool -list -printcert -jarfile application.apk`
で、チェック。

### 本番API向いてるかどうかのチェック
`adb install `
で、実機で動かし、「本番だね。」と、**手動チェック**


いちいち...めんどくさいですね。(やってました)

## これから

`./android-moromoro-check.sh application.apk`
で、一発OK


```sh:android-moromoro-check.sh
#!/bin/bash

AAPT_PATH="/PATH/Android/sdk/build-tools/27.0.3/aapt"
API_STG_PATH="https://stg.example.com"
API_PRO_PATH="https://example.com"

echo PACKAGE!!
${AAPT_PATH} l -a $1 | grep "A: package"
echo ================
echo STG_PATH!!
${AAPT_PATH} d strings $1  | grep ${API_STG_PATH}
echo ================
echo PRO_PATH!!
${AAPT_PATH} d strings $1  | grep ${API_PRO_PATH}
echo ================
echo "証明書"!!
keytool -list -printcert -jarfile $1
```
aaptコマンドで、packageを見にいくのと、`string.xml`見にいくスタイル。
情報多過ぎるので、grepして欲しい情報だけ引っ張ってます。
STG_PATH!!部分が出てると「あかん、STG向いてる...」っていう感じですね。

正直、APIの部分は手動しかないかなーと思ってたのですが、
APIのドメイン部分は`string.xml`に記述してあり
デバッグビルドと本番ビルドとで参照を変えていたので今回はうまく行きました。

APIのラッパークラス内に定義してある、みたいなパターンは...どうしたらいいか分かりません。


今年もアドベントカレンダー、楽しいです。
現場からは以上でーす。
繰り返し処理を利用し、九九を作成

```html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>九九</title>
</head>
<body>
    <script>
        for (let i = 1; i<= 9; i++) {
            for (let j = 1; j<= 9; j++) {
                document.write(`${i} * ${j} = ${i*j}<br />`);
            }
        }
    </script>
</body>
</html>
```

#1週間前からJavascriptを勉強しているのだが、
#どうやらES2015というものがあるらしく、親切に教えてもらった。
```
-```
+```html
<!DOCTYPE html>
<html lang="ja">
<head>
 <meta charset="UTF-8">
 <title>九九</title>
</head>
<body>
 <script>
- for (var i = 1; i<= 9; i++) {
- for (var j = 1; j<= 9; j++) {
- var mult = i*j;
- document.write(i + " * " + j + " = " + mult + "<br />");
+ for (let i = 1; i<= 9; i++) {
+ for (let j = 1; j<= 9; j++) {
+ document.write(`${i} * ${j} = ${i*j}<br />`);
 }
 }
 </script>
</body>
</html>
```
-　以下が、削除推奨
＋　以下が、添削部分
私が学んでいるのは、古いバージョンのような。
これは、勉強しなおさないと、いけないのか？　軽くショックだ。。。。
【OpenLayers4】ol.proj.transform()で任意の座標系へ変換

# はじめに

[APIリファレンス](http://openlayers.org/en/v4.6.5/apidoc/ol.proj.html#.transform)によれば、`ol.proj.transform()`では任意の座標系間で座標値を変換できる関数である。
例えば、WGS84 緯度経度（EPSG:4326）で与えられた座標をOpenLayers上で取り扱うために、Web Mercator（EPSG:3857）に変換するには、以下のように記述すればよい。

```javascript
// JR札幌駅あたりの座標
var latitude = 43.068;
var longitude = 141.350;

ol.proj.transform(
    [longitude, latitude],
    'EPSG:4326', 'EPSG:3857'
); // [15735010.02362922, 5322327.874481014]
```

ところで、上記の2つ以外の座標系への変換を行おうとすると、エラーが生じる。

```javascript
ol.proj.transform([longitude, latitude], 'EPSG:4326', 'EPSG:2454');  // error
```

ちなみに、EPSG:2454は日本の平面直角座標系で札幌市が含まれるもの。
参考：
[平面直角座標系の対応市区町村とEPSGの対応表 - SengokuLAB Note](https://note.sngklab.jp/?p=87)
[わかりやすい平面直角座標系｜国土地理院](http://www.gsi.go.jp/sokuchikijun/jpc.html)


## エラーの原因

エラーは、OpenLayers4のデフォルト状態ではEPSG:4326およびEPSG:3857以外について座標系が定義されていないことに起因する。
以下に、任意の座標系の定義を追加し、その座標系への座標変換ができるようにする方法を示す。

# 座標系定義の追加

## proj4.jsを読み込む

任意の座標系定義を追加するには、[proj4.js](http://proj4js.org/)が必要となる。これを利用可能とする。
例えばhtmlファイルからproj4.jsを読み込むには、以下のように記述する。
（このURLはOpenLayers4公式サイトの例[Raster Reprojection](http://openlayers.org/en/v4.6.5/examples/reprojection.html?q=projection)から引用した）

```html
<script src="https://cdnjs.cloudflare.com/ajax/libs/proj4js/2.4.4/proj4.js"></script>
```

## 座標系定義の追加に必要なJavaScriptコードを調べ、実行する

http://epsg.io/

↑このサイトにアクセスし、EPSGを入力する。
今回の例で言えば、「2454」と入力して[SEARCH]をクリックする。
結果が1件だけ表示されるので、それをクリックする。

すると、こんな感じの画面が表示される。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/122210/a962ec32-bb34-2510-b3ab-ecb998aef815.png)

画面を下にスクロールすると、[Export]という項目が見えるので、そこの[Proj4js]をクリックする。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/122210/58986472-174f-85be-3361-3804a625cbde.png)
[Definition: JavaScript (Proj4js)]という項目が右に表示され、その下にJavaScriptのコードスニペットが表示される。
今回の例では、以下のように表示された。

```javascript
proj4.defs("EPSG:2454","+proj=tmerc +lat_0=44 +lon_0=142.25 +k=0.9999 +x_0=0 +y_0=0 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs");
```

このコードスニペットを、`ol.proj.transform()`の利用に先立って実行する。

# 定義した座標系への変換

ここまでくれば、エラーとならずに座標変換ができる。

```javascript
ol.proj.transform([longitude, latitude], 'EPSG:4326', 'EPSG:2454');
// 出力: [-73298.76942772052, -103144.64579808652]
```

# まとめ

* OpenLayers4でデフォルトで提議されている座標系はEPSG:4326とEPSG:3857の2種である。
* 任意の座標系定義を追加するには[proj4.js](http://proj4js.org/)が必要となる。
* 座標系定義の追加に必要なJavaScriptコードは[epsg.io](http://epsg.io/)で得られる。

# 参考

[空間参照系変換ライブラリ Proj4js 使い方メモ](https://qiita.com/kozy4324/items/1bbd242bc3d9cc843185)
fish でdockerコマンドを補完(completion)する

# Docker + fish でdockerコマンドを補完(completion)する

dockerコマンドを打つときにコマンド補完したくなったので、設定を残しておきます。

## 環境
- fish, version 2.7.1
- macOS Mojave 10.14

## インストール

補完に必要なファイルをダウンロードします(今回は、 **docker** と **docker-compose** )。ダウンロード先は、fishコンフィグ内の `completions` 配下です。
fishでは、 `source .bash_profile` みたいなことをしなくても、上記ディレクトリ配下に補完用のファイルがあれば勝手に補完してくれます。

```bash
curl https://raw.githubusercontent.com/docker/docker-ce/master/components/cli/contrib/completion/fish/docker.fish -o ~/.config/fish/completions/docker.fish
curl https://raw.githubusercontent.com/docker/compose/master/contrib/completion/fish/docker-compose.fish -o ~/.config/fish/completions/docker-compose.fish
```

ダウンロードが完了し、 `docker ` とタイプしてTabをタイプするとこんな感じで補完がされます。

![fish_completion.jpg](https://qiita-image-store.s3.amazonaws.com/0/170140/e6dac41c-3e8f-6172-10e2-68f44b5b3328.jpeg)

また、すでに起動しているコンテナがある場合は、 `docker stop` で補完すると、対象のコンテナを補完してくれて便利ですね。

![fish_completion_demo.jpg](https://qiita-image-store.s3.amazonaws.com/0/170140/3353abe4-9d7c-59c7-0e9b-eb3634a7883b.jpeg)

## 補足
補完ができるのは今の所、 **bash/zsh/fish/powershell** です。

bashなどの他のシェルを利用されている方は、 [こちら](https://docs.docker.com/compose/completion/) の公式に設定方法が記載されていますので、
参考にしていただけると良いかと思います。

あと、dockerコマンドは、 `https://raw.githubusercontent.com/docker/docker-ce/master/components/cli/contrib/completion/bash/docker` からダウンロードする必要がありますので、ご注意ください。
※公式のドキュメントは、docker-composeとdocker-machineは見つけましたが、dockerは見つけられませんでした。

では、よきDockerライフを🐳🐳🐳