OmniAICreator/Qiita-1.07M huggingface dataset:
Firefox で mozCaptureStream() すると音が出なくなる

WEB ページ上で Video や Audio を扱うことができる HTMLMediaElement があり、`captureStream()` メソッドを使用することでメディアをキャプチャできます。

Chrome 等では実際に音をスピーカー等から再生しながらキャプチャできるのですが、Firefox ではキャプチャすると音が出なかった (内部的には再生されていてキャプチャはできる) ため、メモしておきます。

もともと Firefox では `captureStream()` の実装が仕様に追いついておらず、`moz` ベンダー接頭辞が付いていますが、MDN にはこの音が出なくなる仕様に関しては書かれていませんでした。

参考「[HTMLMediaElement.captureStream() - Web API | MDN](https://developer.mozilla.org/ja/docs/Web/API/HTMLMediaElement/captureStream)」

# 1. 再現コード

Chrome 77 と Firefox 71 で動作確認。

(Chrome ではセキリティの制限で index.html を file スキームで開くと cross-origin data としてブロックされるため、`php -S 0.0.0.0:8080` などでローカルサーバーを立てるなどしてください。)

```html:index.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <title>mozCaptureStream() バグ</title>
    <script src="main.js"></script>
  </head>
  <body>
    <video id="video" src="video.webm" controls></video>
    <input type="button" value="mozCaptureStream()" onclick="TEST.captureStream();">
  </body>
</html>
```

```js:main.js
(()=>{

	const TEST = {};

	TEST.captureStream = () => {

		const videoElement = document.getElementById('video');

		return ( 'captureStream' in videoElement ) ? videoElement.captureStream() : videoElement.mozCaptureStream();

	};

	window.TEST = TEST;

})();
```



これはバグとして Bugzilla に投げられていました。

参考「[1178751 - Calling mozCaptureStream on an HTMLMediaElement should not destroy the AudioSink](https://bugzilla.mozilla.org/show_bug.cgi?id=1178751)」

# 2. 解決策

理想的なことを言えば Firefox のコードを修正すべきですが、ここでは JavaScript 側で対処します。

```js:main.js&nbsp;(Firefox&nbsp;対応版)
(()=>{

	const TEST = {};

	TEST.captureStream = () => {

		const videoElement = document.getElementById('video');

		let stream;

		if ( 'captureStream' in videoElement ) {
			stream = videoElement.captureStream();
		} else if ( 'mozCaptureStream' in videoElement ) {
			stream = videoElement.mozCaptureStream();
			// ★
			const audioContext = new AudioContext();
			const mediaStreamSource = audioContext.createMediaStreamSource(stream);
			mediaStreamSource.connect(audioContext.destination);
			// 
		} else {
			console.error('Unsupported: captureStream()');
		}

		return stream;

	};

	window.TEST = TEST;

})();
```

1. `mozCaptureStream()` すると `HTMLMediaElement` に接続されている `AudioContext` で音声が再生できなくなる。
2. 別途新しく `AudioContext` を作成する。
3. キャプチャーしたストリームからオーディオソースを作成し、`AudioContext.destination` [^1] に接続する。

[^1]: スピーカーなど、最終的な音声の出力先。

キャプチャーしたストリームは、既に音量 `HTMLMediaElement.volume` を反映しているので、`GainNode` に接続するような記述は不要です。

ただし、上記の対処法では他のバグ 1443511 の影響を受けます。

参考「[1443511 - A small delay occurs when the volume is changed on Soundcloud](https://bugzilla.mozilla.org/show_bug.cgi?id=1443511)」(「Soundcloud でボリュームが変更されると、わずかな遅延が発生します」[^2] )

[^2]: SoundCloud はサイト名。バグ自体はそのサイトに限らず起きる。
Streamの中からIOExceptionを外に投げたい？それなら

![](https://user-images.githubusercontent.com/743833/62367542-486f0500-b52a-11e9-815e-e9788d4c8c8d.png)[^Logo by]
[Vavr](https://www.vavr.io/)のTryかEitherを使おう。

# こういうことありませんか？

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/546864/4f87c301-ebb9-d5bc-639d-39fcc87395be.png)

Streamで値を処理していて、途中のラムダ式内で検査例外を処理しないといけないようなケース

注：`BookReader`は外部にあるなどで、変えるのが大変だと思ってください

## よくあるであろう対処

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/546864/ccb799a2-1411-62fd-cf65-22cbfda0970c.png)

実行時エラーRuntimeErrorに変えてしまうケース。
メソッド内やprivateな範囲できちんとエラーをハンドリングしているならまだしも、publicな世界に例外をぶっ飛ばしていたりしたらもう大変ですね。
また、今はそんなことをしていなくても、カジュアルにそういうことができてしまう作りになっている点が不安です。

## 無理にStreamつかわなきゃいいじゃん

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/546864/d8ad1c53-807e-573e-8c05-681515e42551.png)

ごもっとも。RuntimeError投げるよりかはよっぽどマシだとは思います。
ローカル変数をが0個から2個に増えたり、2倍以上のコーディング量になったり、ネストが2レベル深くなってますが、チーム全員がこれで満足しているなら別に良いんではないでしょうか。

## 本題

たとえばGolangではタプル（複数のインスタンスの組）を使って異常系の結果を呼び出し元へ伝搬させますが、Javaではタプルを言語仕様レベルではサポートしていません。
クラスを作れば代替できますが、関数型プログラミングに関してはScalaやHaskellを参考にすると、よりよいアイデアがあります。

Vavrは、Javaで関数型プログラミングをやりやすくするためのライブラリです。

### Either

`Right`か`Left`いずれかの値をとるクラスです。正常系は`Right`、異常系は`Left`にするのが通例です。(ダジャレ)

`Optional`の強化版なイメージを持ってもらえればいいかと思います。
`Optional`では`null`か否かという情報しかStreamの下流へ流せませんが、`Either`は例外などの値を流すことができます。

### Try

名前そのまま、try-catchを関数型に書くためのクラスです。
検査例外を投げうるラムダ式を受け取り、例外が発生したときの処理をラムダの形で表現できます。

こちらも`Either`同様に正常値・異常値のいずれかを内包している`Optional`強化版と思ってください。

雰囲気だけ見るとTry一択に感じますが、Tryでは異常系処理の入力が`Throwable`になってしまいダウンキャストが発生しがちなので、`Either`で書いたほうがもやもやしないケースもあります。

### 書いてみる

例えばこんな感じになるかと思います。

Either:
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/546864/9ef5c902-e77b-b1a6-3708-ae7d9d2fe6d9.png)

Try:
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/546864/dcda10f8-d579-5e5c-8f6c-4961b42bb2d2.png)

Tryは`throws Throwable`な点に注意。正しくハンドリングするならダウンキャストが必要になってくる。

## ちなみに

Vavrはがっつり使いはじめると、もはやJavaに見えなくなって保守しにくくなり、なぜScalaやHaskellを使わないのだろうと自問し始めてしまうので、つまみ食い程度に使うことをおすすめします。

## ほかの解決案

無理にJavaを使いつづけなくてもいいのでは？とボスに進言してもいいかもしれません。

## 雑感

これ、OSSの話かなぁ…

[^Logo by]: https://github.com/vavr-io/vavr
VSCode の Vim 拡張機能が動かなくなったのでダウングレードした

VSCode の Vim 拡張機能が動かなくなったのでダウングレードしました。VSCode の拡張機能の動作不良は初めて遭遇したので、これを機にまとめておきます。

まずは拡張機能のタブを開いて動作不良の(今回は Vim)拡張機能を探します。
<img width="469" alt="Screen Shot 2019-12-09 at 16.39.32.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244330/c68bc650-2e00-4a11-d5aa-4d2bbe9e7f91.png">
右下の歯車マークをクリックするとメニューが出てきます。
<img width="674" alt="Screen Shot 2019-12-09 at 16.39.44.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244330/8fe74eb5-a99c-ffb9-6723-8a82a36f53bf.png">
「Install Another Version...」をクリックすると VSCode の中央にバージョンの選択画面が出てきます。
<img width="597" alt="Screen Shot 2019-12-09 at 16.33.38.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244330/459c4910-0fbe-9025-ec61-4b649898cceb.png">
基本的には 1つ前のバージョンを選択すればこれまで通り使えます。今回、動作不良があった Vim は 1.12.0 だったので、1つ前の 1.11.3 を指定しました。

VSCode の拡張機能は便利な反面、動作不良があると作業効率が圧倒的に落ちてしまいます。別に開発者の方々は動作不良を起こしたくて起こしているわけではないので、そっとダウングレードして元の作業効率を保ちましょう。そして快適な VSCode ライフを送りましょう。
【AfterEffects×Web】作った動画素材をWebで使い隊！！


# はじめに

僕の暇つぶしは動画を作ることで、
日頃から「動画とプログラミングで何かできないかな・・・」
って考えてた時にとあるライブラリを発見しました。

その名は・・・・・「**Lottie**」

本日はこのライブラリを使ってAeで作った動画をブラウザに表示させる方法を紹介していきます。

# Lottieとは

![lottie.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/484076/2d5e923a-150e-701d-fd50-2440e874c824.png)


Lottieとは、Airbnbから登場したWeb、iOS、 Android、React Native対応のアニメーションライブラリです。
After Effectsで表示できるアニメーションをリアルタイムでレンダリングし、簡単にアニメーションを作成することができます。

・公式ページ: https://airbnb.design/lottie/


# 使い方

まずは**Bodymovin**をインストールします。
（補足：BodymovinとはAeの拡張機能であり、Aeで作ったアニメーションデータをJSONファイルとして書き出してくれる優れものです）

・github: https://github.com/airbnb/lottie-web
・Creative Cloud: https://exchange.adobe.com/creativecloud.details.12557.bodymovin.html

色々インストール方法はありますが、僕はAdobeストアからインストールしました。

インストールしたらAeの拡張機能にBodymovinが追加されます。
<img width="1400" alt="Screen Shot 2019-12-09 at 15.10.27.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/484076/1f05b950-56e2-0e22-0545-195b20d7f51d.png">



Bodymovinを起動させると作った動画の名前が表示されます。
以下の画像だとComp1と表示されてます。
<img width="1179" alt="Screen Shot 2019-12-09 at 15.22.56.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/484076/700adb27-4f01-c2a1-5018-a63e1060a065.png">


次に歯車マークのSettingを押すとずらっと設定の項目が表示されます。
<img width="479" alt="Screen Shot 2019-12-09 at 15.25.27.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/484076/4a6ea57e-c648-85a6-1e33-a6a6995e4a38.png">

項目の詳細下記のような感じです。

```
Split : json ファイルを指定した秒数で分割する 
Glyphs: フォントをシェイプ化する（基本的にこれを選択でOK） 
Hidden : 非表示レイヤーも出力する 
Guides : ガイドレイヤーを出力する 
Extra Comps : エクスプレッション外部ファイル読み込み？ 
Assets : 画像ファイル出力の詳細設定を行える 
Standalone : json を含んだ js ファイルを出力する 
Demo : テスト/確認用の HTML を出力する 
AMD : XML を出力する
```

Glyphsにチェックを入れSaveを押します。
Comp1を選択しディレクトリの場所が設定できたら、
Renderを押すと・・・data.jsonというファイルが作成されます！

これで準備はOK！

# ブラウザに表示させよう

```html
  <body>
    <div class="lottie"></div>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/bodymovin/5.5.3/lottie.min.js"></script>
    <script src="js/sample.js"></script>
  </body>
```
表示させる要素にclassまたはidをつける。
LottieのCDNを記述する。
（CDNはgithubに掲載されてます）

HTMLの記述はこれだけ！


JSの記述は以下になります。

```js
bodymovin.loadAnimation({
  container: document.querySelector('.lottie'),
  renderer: 'svg', //'svg' / 'canvas' / 'html'の中から選ぶことができます。
  loop: true, //ループ再生するのかを選びます（true/false）
  autoplay: true, //自動再生するかどうかを選択します（true/false）
  path: 'json/data.json' //jsonまでのパスを記述します。出力したjsonの名前は適宜変える必要があります。
});
```
querySelectorでclassを取ってきてます。
あと今回はSVGでrenderしてほしいので設定はSVGで記述しています。
loopなど不要であればfalseにしてあげると一回だけしか再生されないでお好みでどうぞ。
この設定が出来たら完成！！

ブラウザを開くと、、あら不思議！！


![ezgif.com-video-to-gif.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/484076/b980bc70-b718-162c-42c6-79a304e91828.gif)

作った動画が表示されています。
JSとかCSSでゴリッゴリにコーディングせずとも、
動画素材さえあれば簡単にブラウザに落とし込めます！素敵！！

# メリットとデメリット

メリット
・簡単に複雑なアニメーションが作れる
・JSONデータを使用するのでGIFなどと比べるとかなり軽い
・SVGでレンダリングされるので拡大しても綺麗

デメリット
・現状実現できない動作はある
(使えないエフェクト多すぎて暴れた)
・画像をそのまま使うことが出来ないので使用する場合はAi等でパス化しないといけない
・結局色んなツールを使うことになる

# まとめ
サクッとアニメーションを作るにはめちゃくちゃいいツールではないかなと思いました。
また、現状まだサポートしていないエフェクトはありますが、使えるエフェクトがまだまだ増えていくみたいなので期待してます！
Promiseで返される型を取得する型定義

# 概要

Jestでテストケースを書く際変更漏れを起こさないよう、実際のAPIクラスの型をimplementsする形で実装しようとしました。
ところが、Promiseで返すべき型を与えないとunknownと推論されてしまうので、元の定義を引っ張ってくる型定義を作成しました。

## 環境

Typescript 3.7.2で確認

## 起こったこと

下のようなメソッドを持つAPIクラスを作成し、exportしています。

```ts

interface GetResponse {
  id: number;
}

class OriginalApiClass {
  public get(): Promise<AxiosResponse<GetResponse>> {
    return null as any; // 実際のAPI処理
  }
}

export const OriginalApi = new OriginalApiClass();
```

このファイルに対し、下のようなモックを定義しようとしたところ、TSの型チェックに引っかかりました。

```ts

type Api = {
  [P in keyof typeof OriginalApi]: typeof OriginalApi[P];
}

// OriginalApiでpublicなメソッドをすべて定義させたいのでimplementsさせる
class Mock implements Api {
  // 型 'unknown' を型 'AxiosResponse<GetResponse>' に割り当てることはできません。ts(2416)
  public get() {
    return new Promise(resolve => {
      resolve({id: 1})
    });
  }
}
```

Promiseのジェネリクスパラメータに適切な型を渡してやれば解決する問題ですが、`GetResponse`をexportしてしまうと、他でサジェストに出てくるようになり邪魔なので、Promiseに渡すべき型を推論させる定義を作成しました。

## 作成した定義

```ts

export type PromiseParameter<T extends Record<string, any>, K extends keyof T> = (
  T[K] extends (...args: any) => infer A // 1
  ? (A extends Promise<any> // 2
    ? (
      A['then'] extends (resolve: (...args: infer B) => any, reject: any) => any // 3
      ? B[0] // 4
      : never
    )
    : never
  )
  : never
)

type Result = PromiseParameter<typeof OriginalApi, 'get'>; // => AxiosResponse<GetResponse>
```

1. 最初に、メソッドの戻り型`A`を`infer A`で取得しています。
2. 後続でPromiseとして扱えるように、`A`がPromiseのか判定します。
3. Promiseだった場合、第1引数がresolve、第2引数がrejectなので、resolveの引数型`B`を`infer B`で取得しています。
4. （自分の場合）第1引数以外不要なので、`B[0]`でresolveの最初の引数の型を返しています。


これで、resolveで返されるべき型が取得できたので、あとはモックを以下のように書き直せば完成です。

```ts

class Mock implements Api {
  // エラーが出なくなり、resolveで本来入れるべきパラメータがサジェストされるようになります。
  public get() {
    return new Promise<PromiseParameter<typeof OriginalApi, 'get'>(reject => {
      resolve({
        config: {},
        data: {
          id: 1,
        },
        headers: {},
        status: 200,
        statusText: 'OK',
      }););
    });
  }
}

```
日立社員「社内からQiitaに投稿？できるのそれ」

(2019-12-18 表現を一部訂正しました。)

メタ記事。投稿するのにかかる手間に関する投稿です。

![](https://media.giphy.com/media/LScY2HbrHCICWKrUbO/giphy.gif)
[^by]
[^by]: by Giphy: https://giphy.com/gifs/tvland-tv-land-king-of-queens-kingofqueens-LScY2HbrHCICWKrUbO

# どうしてこんな記事を書こうと思ったか

* 日立製作所もガンガンOSS活動をしていることを知ってほしい。日立に興味があるが、OSSコントリビューションは社外発信にどれくらい熱心なのか気になる人向けに、とある一部署の情報をシェアしたい。
* タイトルのような感想を持つ弊社内の人に情報発信してもらいたい。
    * (残念なことに、) 社内の情報共有サイトに書くよりQiitaに書いたほうが読まれそう。
* ~~後輩がAdvent Calendarの記事集めに苦労していたので気の毒になったため~~
* :warning: 注: 全社的な審査プロセスではなく、(株)日立製作所 ITプロダクツ統括本部のやりかたです。
    * では全社的なプロセスは？他の部署は？ → 詳しい方、（申請の上）コメントください。 :bow: 

### ITプロダクツ統括本部って何？

* [Google it](https://www.google.com/search?client=firefox-b-d&q=%E6%97%A5%E7%AB%8B%E8%A3%BD%E4%BD%9C%E6%89%80+IT%E3%83%97%E3%83%AD%E3%83%80%E3%82%AF%E3%83%84%E7%B5%B1%E6%8B%AC%E6%9C%AC%E9%83%A8) (※これまでも組織名称が変わってきたので、将来、変わるかもしれません。)
* ハコものを売る会社から、サービスやソリューションを売る会社になりつつあります。

### 誰？

* 横浜市戸塚区で働いています。
* 最近はよく在宅勤務をしています(2児の親)

## やったこと

1. 投稿すると表明する
    1. Advent Calendarに登録してもらったり、以下をレクチャーしてもらったり。
1. パワポに書かれた書類を読む
1. 新規アカウントの登録
    * 会社メールアドレスが登録されたアカウントでないと使用禁止。
    * Qiitaは1つしかメールアドレスが登録できないため個人で投稿しているアカウントは自動的に使用禁止になる。(なぜ…？)
1. 記事をかく
1. 以下のハンコをもらう
    2. 課長
    3. 部長
    4. 課の情報資産管理者
    5. 知財管理部署: 特許情報の審査
1. 投稿

## かかった時間

* 投稿するまでの準備: 1日
* 記事を書いた時間: 30分
* ハンコをもらう時間: 3日

## やってみて感じたこと

* やっぱり面倒！
* 以下を1投稿ごとにやる必要があり、作業ステップ数やツールに時代錯誤な感が否めない。
    1. ExcelとWordの帳票をDLして開いて、
    1. 30個強のチェックリストをチェックして、（丸図形を挿入！）
    1. 電子職印押して、（ハンコ.PNGをコピペ！）
    1. 関係各所にメールで回す。
* 慣れれば1日で完了できるらしいです
* とはいえ、数年前より良くはなってきている感はある。昔はほぼ実現不可能だったので。
    * 「チェックリスト目視で確認！」よりも、リテラシを上げる方の研修の充実や、ライセンス制やレベル制という形に変わっていってほしいなぁ。
[ Android ] Applicationを継承したクラスを使う

以前、セッターを使って異なるクラス間で値をやり取りする方法を紹介した。
https://qiita.com/QiitaD/items/50fb0b6b7709e66a5041

今回はApplicationクラスを継承したクラスを作り、そのクラスを介して異なるクラス間で値をやり取りする方法を紹介する。

##クラスの作成と登録
以下のようにApplicationクラスを継承したクラスを作る。

~~~java
public class MyApplication extends Application{
    public static String test = "test";
}
~~~

しかしこれだけでは使用できない。以下のようにしてAndroidManifestに登録する必要がある。

~~~xml
<application>
        android:name=".MyApplication"
</application>
~~~

##使用方法
MyApplicationクラスは以下のように使用する。

~~~java
//アプリケーションを取得し、MyaApplication型変数に代入する
MyApplication myApplication = (MyApplication) this.getApplication();
//上記の変数からMyApplicationクラスのメンバ変数を呼び出す
textView.setText(myApplication.test);
~~~
Android Emulatorへのコピペがダルい問題

# Android Emulatorへのコピペが不便
久しぶりにAndroid Emulatorを触ってて、ホストマシンでコピーしたテキストを`command + V`でコピペ出来ず何かと不便だったので調べてみた。
![スクリーンショット 2019-12-09 16.03.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6380/e5f99d62-d1cb-d989-a4b7-c2caadc9505f.png)

# 環境
macOS 10.14.6
Android Studio 3.5.2

# adb経由でペースト
ググってよく出てくるのがadbコマンドを通したペースト方法。
`$ adb shell input text 'hogehoge'`

ペーストするのにいちいちコマンド打つのダルい。

# EditText長押しでペースト
さすがにもっと良い方法あるやろ、と探して見つけたのがこちら。
Emulator上でEditText長押しで「Paste」メニューが出る。
![スクリーンショット 2019-12-09 16.04.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6380/b7044acc-b599-c91a-0fac-c7e5f21287c9.png)
これで簡単にペーストできる。
![スクリーンショット 2019-12-09 16.05.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6380/1ffc91c9-5119-1fbb-b9a9-ee651975d958.png)
今日でペチペチ手打ち職人も卒業だ。

やったぜ。
dockerでgpuを使用するためのnvidia-docker2インストールガイド

# 概要
  - dockerでGPUを用いるためにはnvidia-docker2のインストールが必要．
  - nvidia-docker2のlinuxへのインストール方法が少々面倒だったので，自分用にメモ．

# nvidia-docker2のインストール方法

```shell:installガイド
# リポジトリの登録
curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | sudo apt-key add -
distribution=$(. /etc/os-release;echo $ID$VERSION_ID)
curl -s -L https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.list | \
  sudo tee /etc/apt/sources.list.d/nvidia-docker.list
sudo apt-get update

# nvidia-docker2のインストール
sudo apt-get install -y nvidia-docker2
sudo pkill -SIGHUP dockerd

# 動作テスト
docker run --runtime=nvidia --rm nvidia/cuda:9.0-base nvidia-smi
```
これで，dockerを起動する際にruntimeオプションを追加し，`--runtime=nvidia`と記述すればdocker内でGPUが認識されます．
Python のCoroutine（コルーチン）理解

```python
def consumer():
    r = ''
    while True:
        n = yield r
        if not n:
            return
        print('[CONSUMER] Consuming %s...' % n)
        r = '200 OK'

def produce(c):
    c.send(None)
    n = 0
    while n < 5:
        n = n + 1
        print('[PRODUCER] Producing %s...' % n)
        r = c.send(n)
        print('[PRODUCER] Consumer return: %s' % r)
    c.close()

c = consumer()
produce(c)

```

[PRODUCER] Producing 1...
[CONSUMER] Consuming 1...
[PRODUCER] Consumer return: 200 OK
[PRODUCER] Producing 2...
[CONSUMER] Consuming 2...
[PRODUCER] Consumer return: 200 OK
[PRODUCER] Producing 3...
[CONSUMER] Consuming 3...
[PRODUCER] Consumer return: 200 OK
[PRODUCER] Producing 4...
[CONSUMER] Consuming 4...
[PRODUCER] Consumer return: 200 OK
[PRODUCER] Producing 5...
[CONSUMER] Consuming 5...
[PRODUCER] Consumer return: 200 OK
カルネージハートとは関係のないSNSシェアしていない人のためのOGPタグの設定


カルネージハート Advent Calendar 2019 6日目の記事です。

今回はカルネージハートとは全く関係ないSNSシェアしていない人のためのOGPタグの設定の話です。

# はじめに

OGPとはSNSで利用されている、Webページの情報をSNSと連携させるためのタグです。
OGPの有無でSNSシェアされる際の印象が格段に違ってきます。
これを見てOGPを設定されてない方は、ぜひとも設定しましょう！

## 書き方の例

```html
<!DOCTYPE html>
<html lang="ja">
	<head prefix="og: http://ogp.me/ns# fb: http://ogp.me/ns/fb# website: http://ogp.me/ns/website#">
		<meta charset="UTF-8">
		<meta http-equiv="X-UA-Compatible" content="IE=edge">
		<meta name="viewport" content="width=device-width,initial-scale=1.0">
		<meta name="robots" content="index,follow">
		<meta name="description" content="descriptionです">
		<meta name="twitter:card" content="summary">
		<meta name="twitter:image" content="https://hoge.com/ogp.png">
		<meta name="twitter:url" content="https://hoge.com/">
		<meta name="twitter:description" content="twitter:descriptionです">
		<meta name="twitter:title" content="twitter:titleです">
		<meta property="og:description" content="og:descriptionです">
		<meta property="og:site_name" content="og:site_nameです">
		<meta property="og:locale" content="ja_JP">
		<meta property="og:type" content="website">
		<meta property="og:image" content="https://hoge.com/ogp.png">
		<meta property="og:url" content="https://hoge.com/">
		<meta property="og:title" content="og:titleです">
```

URLやimageは絶対パスでの記述です。

その他詳細については[ogp.me](https://ogp.me/)を要参照。

**SPAで動的にOGPを出力する際は、SSR等の工夫が必要です**

## 最後に

事実上の最新作EXAが2010年に発売以降続編の情報が皆無ですが、一部の熱狂的ファンは大会を開催してゲームを続けています。ゲームを盛り上げることで続編も出るかもしれません。カルネジスト、ネジらーの皆様のご協力をお願いします！

カルネージハートファンのプログラミング知識を共有しましょう！

アヌビアスを貼り付けてお茶を濁します。

![ezgif-5-f804bd285421.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/133718/f2c75db3-3c30-80cf-56f0-30640a300c62.gif)
google-translate.el で args-out-of-range になるときの対処法

たぶん Emacs 26.3 に変更してからだと思うのですが次のコードで

```elisp
(google-translate-translate "ja" "en" "こんにちは" 'current-buffer)
```

次のエラーがでるようになりました。

```elisp
(args-out-of-range [] 1)
```

調べたところ次のリンク先のコードでとりあえず動くようになりました。

https://github.com/atykhonov/google-translate/issues/98#issuecomment-562870854

>
```elisp
 (defun google-translate-json-suggestion (json)
  "Retrieve from JSON (which returns by the
`google-translate-request' function) suggestion. This function
does matter when translating misspelled word. So instead of
translation it is possible to get suggestion."
  (let ((info (aref json 7)))
    (if (and info (> (length info) 0))
        (aref info 1)
      nil)))    
```

そんだけです。
M5stackでFeliCa リーダーを作った話　ケース編

[先日の記事](https://qiita.com/ysogabe/items/a8d1714f6e9280b56ba5)で作った、Felicaリーダーですが[RC-S620S](https://www.switch-science.com/catalog/353/)をどうにか格納しないことには使用に耐えないことが判明しました。↓では、いかんともしがたいので。。。

<img width="544" alt="スクリーンショット 2019-12-01 21.23.02.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/29641/f2539204-7ffe-6298-6e2c-749800fcbb33.png">

（カレンダー忘れてた。。。）

## 機材について
### 3Dプリンタについて

[昔に記事](https://qiita.com/ysogabe/items/95b974d13b396cf7479e)の環境です。そもそも、このケースを作るために導入したともいえる。。。

### 3DCADについて

Autodesk様[Fusion360](https://www.autodesk.co.jp/products/fusion-360/)の体験版を利用しました。Fusion360を利用した理由は以下です。インストール手順は、公式参照です。

- 事例が一番おおいこと
- フルスペックの体験版があること
- かなり昔にAutodesk社のCADを触ったことがあったので、いけるかと思ったので（すべては忘却の彼方だった）

つかってみた感想は以下ですが、基本便利な子です。

- 図面がクラウド管理なので、ログインすればどの端末からでも作業開始出来る
- 2ファクタ認証が少し面倒
- MID2015のMacBookPro（16GB）で少し重いかなぁという感じ

## 成果物

いつもお世話になっている[thingiverse](https://www.thingiverse.com/thing:4033069)にはじめて投稿してみました。

### 形状

- 吸盤（ダイソー）でくっつけることが出来ます。
<img width="486" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/29641/9b587ded-8b1b-2c7e-4da9-7a4bc85efc87.png">
<img width="339" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/29641/ab115075-9b34-a02c-c385-3c6c5e341a95.png">

- Feclia基板に合わせたネジ穴とへこみで固定できます
<img width="438" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/29641/f6997573-5a6c-f0db-8fd2-046b4b150d5d.png">

- M5Stackのボトム（バッテリー）を外してネジ止め（M3 15mm）すると、蓋の高さが合います
<img width="314" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/29641/0f42d87b-658b-118c-dd40-27f14694ddde.png">

基本、外部電源供給を前提としているので、バッテリー外すことにしました。

## 紆余曲折（製造工程）
いか、上記の形になるまでのぐだぐだを記載します。。。

### RC-S620Sの格納

固定用のネジ位置、基板格納用の穴の平面図面は[Sony様の技術情報](https://www.sony.co.jp/Products/felica/business/products/RC-S620.html)からダウンロードできます。

- スケッチの作成：基板部分のへこみと、固定ネジ、リボンケーブルの通り道が最低限必要です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/29641/64446185-c791-58db-a359-0c68aaa0cd88.png)
- リボンケーブルはケース側に溝がないと、取り出せません
<img width="589" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/29641/1c063af7-8c91-34d1-ba6e-f5f67167bda2.png">
- ケースの厚みは1mmです。詳しく検証していませんが、数mmまでは読み取りに影響はなさそうです。

### 試作ゼロ号機

最初は、M5Stackを全体的にくるむ形で作っていました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/29641/76d6a13b-3183-5889-186c-fee788a7e352.png)

この形だと、以下の問題があったので新しく作り直しました。

1. コネクタ類にアクセスできない
1. ケースの方向が固定されて設置の自由度がない
1. ケースを設置する方法を考えていなかった（両面テープでつけていた）

両面テープの強度の問題で、何回も落下させてしまい基板接続部分が壊れてしまい、M5Stackが壊れては困るが最大の理由です。
ReactFire v2 alphaを試してみる（Firestore編）

## はじめに

[前回](https://qiita.com/daishi/items/13379bbce59f275f1d0c)の[記事](https://qiita.com/daishi/items/8b08b32185a9b050637a)では、react-firebase-hooksを試したのですが、12月6日の[React Day Berlin](https://reactday.berlin)では、ReactFireのプレゼンがありました。こちらはとても先進的で期待できます。

本記事では、Firestoreの部分を移植してみたいと思います。

## ReactFireとは

ReactFire v1は2016年5月のリリースを最後に、その後deprecateされ、Firebase JS SDKを直接使うようアナウンスされています。それが[2019年7月よりv2として](https://github.com/FirebaseExtended/reactfire/issues/135)再度開発しているようです。

<https://github.com/FirebaseExtended/reactfire>

現在、masterブランチがv2になっています。

READMEには、

> Status: Alpha. ReactFire is meant for React Concurrent Mode, which is only available in experimental React builds.

と書いてあるのですが、ざっとコードを見たところ、Suspenseを使っているだけで、Concurrent Modeでなくても動きそうです。

## コーディング

```jsx
import React, { Suspense, useState } from "react";
import ReactDOM from "react-dom";

import "firebase/firestore";
import {
  FirebaseAppProvider,
  useFirestoreCollectionData,
  useFirestore
} from "reactfire";

const firebaseConfig = {
  apiKey: "...",
  authDomain: "...",
  databaseURL: "...",
  projectId: "...",
  storageBucket: "...",
  messagingSenderId: "...",
  appId: "..."
};

const TodoList = () => {
  const firestore = useFirestore();
  const todosRef = firestore().collection("todos");
  const values = useFirestoreCollectionData(todosRef, { idField: "id" });
  return (
    <ul>
      {values.map(value => (
        <li key={value.id}>{value.title}</li>
      ))}
    </ul>
  );
};

const NewTodo = () => {
  const [title, setTitle] = useState("");
  const [pending, setPending] = useState(false);
  const firestore = useFirestore();
  const add = async () => {
    setTitle("");
    setPending(true);
    try {
      await firestore()
        .collection("todos")
        .add({ title });
    } finally {
      setPending(false);
    }
  };
  return (
    <div>
      <input value={title} onChange={e => setTitle(e.target.value)} />
      <button type="button" onClick={add}>
        Add
      </button>
      {pending && "Pending..."}
    </div>
  );
};

const App = () => {
  return (
    <FirebaseAppProvider firebaseConfig={firebaseConfig}>
      <Suspense fallback={<div>Loading...</div>}>
        <div>
          <TodoList />
          <NewTodo />
        </div>
      </Suspense>
    </FirebaseAppProvider>
  );
};

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
```

動きました。nextではだめで、canaryを使う必要がありました。

## おわりに

ドキュメントがまだ追いついていなくて、ソースコードを読まないといけなかったです。
レガシーサーバ運用しかよく知らなかった自分がHerokuを使ってみて

はじめまして、 @FlipFlop です。

Ubiregi Advent Calendar 2019 の 10日目 は、Herokuを使ってみての良さをご説明できたらなと思います。

レガシーサーバの運用しか知らないというとどういう運用をしていたか説明してからHerokuを使ってみて感じたメリットについて説明しようと思います。

#昔の話から

私がサーバの運用をしていたのは、ユビレジではなく他の会社で、今から8年くらい前で、色んなサービスを抱えていて、サーバも一つのサーバの種類で何十台もあるなどとても多かったです。ではこのサーバ達をどう運用していたか項目に分けて説明します。

###サーバのリソース、死活監視
Nagiosというオープンソースの監視ソフトを使っていました。mnodeと呼ばれる監視するサーバを立てて、snodeと呼ばれる監視されるサーバをそれぞれ全て設定して、リソース、死活監視は行っていました。

###アラート対応・負荷対応
リリースにより影響からくるアラートの対応から、季節・天候などの環境からの負荷増大によりアラートなど、多くのアラートに対して対応を行っていました。どんな時でもくるため、飲み会中・夜中寝てる時に24時間監視チームから起こされることもありました。

###BCP対策
私が運用をしていたのは、2011年東日本大震災後で、サーバの冗長化について考えられていました。
サーバを東日本と西日本に分けるというもので、新しくサーバを用意しながら古いものを移して除却するというとても時間のかかる作業をした記憶があります。とはいえユーザのために24時間稼働するサービスを提供することはとても重要だと思います。

###サーバ増設、移設、除却対応
BCP対応と対応自体は同じですが、古いスペックのサーバをいつまでもコキを使うより、最新のサーバを使ってコストパフォーマンスをあげるのもサーバ運用の仕事の1つでした。

###ハードウェア故障後の交換依頼対応
物理サーバだと切っても切れないのがこのハードウェア故障。特に読み込みだけしかほとんど使わないハードディスクでもかなりの頻度で故障した記憶があります。

###アカウント管理
新入社員など、担当者が入れ替わるたびにツールからアカウントを追加したり削除したりしていました。もちろん人がやることなので、削除し忘れて結構残っていたから一気に削除するという微妙な運用もよく起きていました。

###リリース作業
パッケージアップデートやインストールでリリースをしていました。私が運用していた頃はパッケージのバージョンがバラバラで、このサーバには入るけどこのサーバには入らない、アップデートできないということがありました。バージョンの違いには実は意味はなく、リリース漏れだったりなぜか入っていることが多く、この管理が一番ずさんだった気がします。バージョンが違うとかもはや別物のサーバ・・・。
しかしほぼ手動のリリースだとこんなことも当時はあったんでしょうね。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103993/e82bd7fd-b6b7-b95f-0911-cdb0ba4479f2.png)


*↑このリリース方法はしんどい、管理もひどくなる*

#Herokuを使い始めて
最近まで、そういったレガシーな環境で働くことが多かったり、iOSなどクライアントアプリの開発が多かったため、Herokuを使い始めたのはつい最近。ユビレジでも私が入る前からHerokuを利用していて、既に運用されている状態だったのですが、凄いなあと思って使っていました。
最近、プライベートで開発しているサービスもHerokuを使って開発しようということで、１からセットアップしていましたが、無料プランでも快適な点がいくつかあったので、ここに書いていきたいと思います。

##セットアップが容易
まず、Herokuを使えるまでにする手順が、物理ーサーバより圧倒的に簡単ということです。
Herokuがサポートしている言語であれば、登録して少し設定をいじるだけで、ステージング環境や本番環境を用意することができます。

###参考になる記事
登録からパソコンにHerokuCLIを入れるまで参考になりました
https://qiita.com/mnao305/items/71c8c9d5fd195592c1ec

##開発視点でも便利
開発をしたことがある人は、ステージング環境に反映するまで、個人環境を構築した状態で動作確認をしていましたが、Githubと連携すると、各PRごとに動作環境を自動で構築してくれるように設定することもできます。これによって、各PR毎に動作確認をしてもらうこともとても簡単にできます。

###参考になる記事
プルリク毎に動作環境を自動で立てるようにするのに参考になりました
https://qiita.com/kon_yu/items/6d2c0ae91d0176ceb167

##ほぼ自動で安全にリリース（デプロイ）ができる
テストコードを書く習慣があれば、そのテストコードが全てOKなることを自動で確認して、デプロイ作業も自動に設定することができます。運用に合わせて自動デプロイをOFFにすることもできます。これらの環境をわざわざサーバを立てて連携せずとも簡単にできるのはHerokuの魅力に感じました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103993/25b64dbd-57af-083c-db45-f0fec35b626d.png)

##物理的なハードウェアの故障を気にしなくって良い
全く気にしない点なので、予想ですがハードウェアの故障はデータセンター側でカバーしているため、こちらとしては一切ハードウェアの故障を意識しなくても良い状態となっています。（裏では故障したら交換してくれている素晴らしいエンジニアがいると思うのですが、それが気にならない）

## 【有料】自動スケーリングでアクセス集中にも継続サービス提供が可能か
これは有料プラン専用機能なので、個人的には試していないですが、物理サーバだと、アクセス集中時期を予想したり、スタンバイサーバを用意したりと多くの労力をかけないと、アクセスが増えた時に対応し切れないことが多かったです。
しかし、自動スケーリング機能を使うことで、予測のできないスケーリングも多くの工数をかけることなく、スケーリングしてくれるようです。
自動にやってくれるのは、サーバ運用観点だけでなく、ユーザにとっても優しいですね。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103993/245ab8a3-1b6f-ffbe-7e13-d35e791d0136.png)

#結論
開発途中の小規模のサービスを物理サーバで開発しているのなら、まずは体験してみるべきかと思います。
少人数の開発なら、コラボレータを登録するだけで、複数人の開発にも対応できます。
有料プランへはお財布と相談になりますが、上記のこと以外にもできることはまだありますので、まずはいじってみてはどうでしょう🤗
今まで触ったことのないサービスってワクワクしますよ！
CSSだけで動的サイトを作れるらしい！！

# はじめに
最近まで深層学習の技術ブログを書いていたが、心機一転してWeb系の勉強に戻ることにした。
今までは安定を求めてAIを数ヶ月勉強していたけど面白くなかった。
やはり楽しいほうがいいと思ったので今回Web系に戻ろうと思う。

# CSSだけで動的サイトを作る
今まで動的サイトといえばJavaScriptを使わないといけないと思っていたが、実はJavaScriptから提供された変形とアニメーションの機能がCSSにあるらしい。
聞くところによればJavaScriptよりも高速に動くらしいのでこれから是非使っていきたいと思う。

# transformプロパティ
cssでtransformプロパティを使うことで、**移動・拡大縮小・回転・傾斜**の変形を行うことができる。
これらの変形を行うには、transformプロパティで、各トランスフォーム関数をして営してあげることで行うことができる。

**transelate(x軸の移動,y軸の移動)**
translate関数を使うことで、x軸y軸の移動を行うことができる。
上の様にx軸とy軸の移動する距離を指定してあげることで要素を移動することができる。

**scale(x軸方向の倍率,y軸方向の倍率)**
scale関数を使うことで要素のx軸y軸方向の拡大縮小が可能です。

**rotate(回転角度)**
rotate関数で要素を回転させることができます。
どれだけ回転させるかは、角度を指定することで決められます。

**skew(x軸の傾斜角度,y軸の傾斜角度)**
skew関数で要素に傾斜をつける変形を行うことができます。
fs.stat を Promise 化して複数のファイルの stat を一気に取る。

書いてから思ったのですが、下から読んだ方がいいかも。

# Promise 以前の状況

例えば node.js で 'x' という名前のファイルの stat を取得したい場合

``` sample.js
const fs = require( 'fs' )

fs.stat(
	'x'
,	( er, stat ) => {
		if ( er )	console.error( er )
		else		console.log( stat )
	}
)

```

という風にやってたと思います。

### ファイルが複数の場合

複数のファイル、例えば 'x', 'y' という名前のファイルの stat を取得してから何かやりたいような場合、時間のかかる逐次処理でよければ

``` sample.js
const fs = require( 'fs' )

const
stats = []
fs.stat(
	'x'
,	( er, stat ) => {
		if ( er )	console.error( er )
		else {
			stats.push( stat )
			fs.stat(
				'y'
			,	( er, stat ) => {
					if ( er )	console.error( er )
					else {
						stats.push( stat )
						console.log( stats )
					}
				}
			)
		}
	}
)
```

並行にしたい場合はちょっと苦しいテクニックを使って

``` sample.js
const fs = require( 'fs' )

const
stats = [ null, null ]
fs.stat(
	'x'
,	( er, stat ) => {
		if ( er )	console.error( er )
		else {
			stats[ 0 ] = stat
			if ( stats[ 1 ] ) console.log( stats )
		}
	}
)
fs.stat(
	'y'
,	( er, stat ) => {
		if ( er )	console.error( er )
		else {
			stats[ 1 ] = stat
			if ( stats[ 0 ] ) console.log( stats )
		}
	}
)
```

のようにする必要がありました。ちょっと大変です。特にファイルの数が増えたりしたら。

# Promise 以降

fs.stat を Promise 化すると上のような大変さがなくなります。

### Promise 化

##### util.promisify を使う

``` sample.js
const fs = require( 'fs' )
const { promisify } = require( 'util' )

promisify( fs.stat )( 'x' ).then(
	_ => console.log( _ )
).catch(
	_ => console.error( _ )
)
```

##### 素でやる

util.promisify がある今となってはもうやることはないと思いますが、参考までに。

``` sample.js
const fs = require( 'fs' )

new Promise(
	( rs, rj ) => fs.stat(
		'x'
	,	( er, stat ) => er ? rj( er ) : rs( stat )
	)
).then(
	_ => console.log( _ )
).catch(
	_ => console.error( _ )
)
```

### ファイルが複数の場合

##### Promise.all を使う

Promise の配列を作って Promise.all に渡してやります。配列の中のすべての Promise が解決されるか、どれかがリジェクトされるまで待ちます。

``` sample.js
const fs = require( 'fs' )
const { promisify } = require( 'util' )

Promise.all(
	[ 'x', 'y' ].map(
		_ => promisify( fs.stat )( _ )
	)
).then(
	_ => console.log( _ )
).catch(
	_ => console.error( _ )
)
```

##### 関数化してみる

``` sample.js
const fs = require( 'fs' )
const { promisify } = require( 'util' )

const
Stats = files => Promise.all( files.map( _ => promisify( fs.stat )( _ ) ) )

Stats( [ 'x', 'y', 'z' ] ).then(
	_ => console.log( _ )
).catch(
	_ => console.error( _ )
)
```

# 最後に

Promise.all を使わない手はありませんね！
zfsを使うならZnapZendでバックアップしよう

zfsはスナップショットが手軽に撮れる反面、バックアップの手段は用意されておらず自分でバックアップ手段を確保する必要があります。
バックアップツールとしてznapzendが優秀のため紹介します。


## 何ができるのか
1. snapshotの定期的な取得
2. 古いsnapshotの自動削除
3. snapshotの外部書き出し(バックアップ)

ざっくり言うとzfs snapshotと zfs sendの組み合わせを自動で行ってくれるスクリプトです。

## 導入
環境はUbuntu19.10を想定してます

### インストール
```sh
wget https://github.com/oetiker/znapzend/releases/download/v0.19.0/znapzend-0.19.0.tar.gz
tar zxvf znapzend-0.19.0.tar.gz
cd znapzend-0.19.0
./configure --prefix=/opt/znapzend-0.19.0
make -j4
sudo make install
```

### パスを通す
インストールしただけではパスが通っていないため、パスを通します。

```bash

for x in /opt/znapzend-0.19.0/bin/*; do sudo ln -s $x /usr/local/bin; done
```

## 設定
tankのデータセット全てを`dam/znap`にバックアップする設定例です。

```sh
sudo znapzendzetup create --recursive \
   SRC '7d=>1h' tank \
   DST:a '7d=>1h,14d=>1d' dam/znap
```

この設定では以下の通りのバックアップが行われます

- 1時間毎にtankのスナップショットを作成
- tankのスナップショットは7日間保持
- tankのスナップショットをdam/znapにバックアップ
- dam/znapは1時間毎のスナップショットを7日間、1日毎のスナップショットを14日間保持

## デーモン化
設定しただけでは、実行が行われません。常にバックアップ処理が走るようにデーモン化します。

```/etc/systemd/system/znapzend.service
[Unit]
Description = znapzend

[Service]
ExecStart = /usr/local/bin/znapzend --logto=/var/log/znapzend
Restart = always
Type = simple

[Install]
WantedBy = multi-user.target
```

### デーモンを有効化

```bash
sudo systemctl enable znapzend
sudo systemctl start znapzend
```
pip install でSSL証明書関連？のエラー

```pip install```しようとすると、下記のエラー。

```zsh
pip is configured with locations that require TLS/SSL, however the ssl module in Python is not available.
Retrying (Retry(total=4, connect=None, read=None, redirect=None, status=None)) after connection broken by 'SSLError("Can't connect to HTTPS URL because the SSL module is not available.")': /simple/pip/
Retrying (Retry(total=3, connect=None, read=None, redirect=None, status=None)) after connection broken by 'SSLError("Can't connect to HTTPS URL because the SSL module is not available.")': /simple/pip/
Retrying (Retry(total=2, connect=None, read=None, redirect=None, status=None)) after connection broken by 'SSLError("Can't connect to HTTPS URL because the SSL module is not available.")': /simple/pip/
Retrying (Retry(total=1, connect=None, read=None, redirect=None, status=None)) after connection broken by 'SSLError("Can't connect to HTTPS URL because the SSL module is not available.")': /simple/pip/
Retrying (Retry(total=0, connect=None, read=None, redirect=None, status=None)) after connection broken by 'SSLError("Can't connect to HTTPS URL because the SSL module is not available.")': /simple/pip/
Could not fetch URL https://pypi.org/simple/pip/: There was a problem confirming the ssl certificate: HTTPSConnectionPool(host='pypi.org', port=443): Max retries exceeded with url: /simple/pip/ (Caused by SSLError("Can't connect to HTTPS URL because the SSL module is not available.")) - skipping
Requirement already up-to-date: pip in ./.anyenv/envs/pyenv/versions/3.7.3/lib/python3.7/site-packages (19.0.3)
```

SSL関連のエラーっぽいので、とりあえずOpenSSLを入れ直したりしてみる。

```
brew install openssl
```

が、ダメ。~/.pip/conf.pipにtrustedの設定を追加してもダメ。

## 解決

Pythonとpipを再インストールすると解決した。pyenvを使っていたので、

```
pyenv install 3.8.0

pyenv global 3.8.0

curl -kL https://bootstrap.pypa.io/get-pip.py | python
```

を実行後、再度```pip install```を実行すると問題なく動いた。

もやもやするけど、動いたから一旦よしとしておこう。。。
AWS Transit Gateway Network Manager さっそく触ってみた

こんにちは、あやたこです。

ちょうどTrangit Gatewayを触る機会があったので、今回のre:Inventで発表されたNetwork Managerもついでに触ってみました


## 0.前提
既にTrangit GatewayでVPC又はVPNが接続されている必要があります
また、Network Manager が参照する Transit Gateway は同じAWSアカウント内にあるものとします


## 1.Global Networkを作成する
VPCサービスから Network Manager を開き、 Create a Global Network を選択します
<img width="1079" alt="スクリーンショット 2019-12-09 9.54.39.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/448713/37cfc7a4-b077-b763-4138-2c4dbd8bae28.png">
Global Networkを識別するための名前を適宜入力する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/448713/5d06a4d1-d23c-b656-3d71-6b0a733f2d3f.png)

## 2.Transit GatewayをGlobal Networkに登録する
作成したGlobal Networkを選択した後、左メニューからTransit gatewaysを選択、Transit Gatewayを登録
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/448713/2b6fe39c-1ae5-dc7a-2d18-897d9a9781be.png)

Network Managerで管理する既存のTransit Gatewayを選択し、登録
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/448713/17250eef-6de0-6867-410e-e33f452570e9.png)

## 3.ネットワークトポロジを確認してみる
左メニューのダッシュボードを選択し、さらにトポロジタブを選択します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/448713/ec644aaa-73ca-27a0-c927-70be005947c8.png)
すると、上記のようなネットワークトポロジが自動的に生成されます
上記例は、1つのTransit Gatewayに3つのVPCと3つのVPNが接続されている状態ですが、関連付けがうまくされていないため少し歪な構成になっています
4以降でオンプレミスデータセンターやルーター情報を設定し、CGWと関連付けます

## 4.オンプレミスデータセンターの情報を定義する
左メニューのSitesを選択し、サイトを作成します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/448713/2fcaf3ad-1951-6040-8618-5af5fed5cde4.png)
データセンター等の名前を定義し、アドレスに住所を入力し作成を押下します
緯度と経度はAWSが住所から自動で検索してくれます（例では、札幌駅の住所を仮で設定しています）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/448713/867e5393-caa3-1dc3-4110-bf1abd32c86d.png)

## 5.デバイス情報を定義し、関連付けをする
左メニューのDevicesを選択し、デバイスを作成します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/448713/660580c5-d253-a6c8-ac7b-b7f982dbcee3.png)
ルーターの名前やその他パラメーターを適宜入力します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/448713/9546736f-33fa-587b-0017-af13d51369a9.png)
作成した後、デバイスを選択し概要からサイトを関連付けをします
先ほど作成したサイトをプルダウンから選択します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/448713/62d3eafb-b8f1-7e34-8885-c424c808ab27.png)
次に、デバイスの オンプレミスの関連付け タブから 関連付け を選択します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/448713/3a7589dc-ad09-b2a3-7dac-031dcfd4d613.png)
作成したルーターに相当するカスタマーゲートウェイをプルダウンから選択し関連付けを作成します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/448713/7b75d4fa-3a17-2c56-5118-fa8b30911f43.png)

## 6.ダッシュボードの地域、トポロジにきちんと関連付けが反映されていることを確認する
うまくオンプレミスルーターとCGWが関連付けられたことで下記のようなトポロジに更新されます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/448713/d9ea0ac7-f9ec-94f9-6470-15a014b8a1d7.png)
また、地域タブを選択すると、住所から座標を割り当てて下記のようなmapが表示されるかと思います
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/448713/ac62de10-ba9f-61b3-003e-08bef8267e66.png)
また、モニタリングタブでトラフィック等も確認できると思います

## まとめ
無料でこれだけ簡単にグローバルネットワークの可視化ができるとはさすがAWSといったかんじですね
簡単が故に現時点ではあまり作り込めないのが玉にきずですが、今後に期待ですね
他にもいろいろアップデートが来てるので、どんどん触ってみたいと思います
欠損値　pandas

今回は欠損値について、記事を書いていきます。

#内容
・欠損値とは
・欠損値の確認方法
・欠損値の対処法

##欠損値とは
欠損値とはデータの値がはいっていないことをいいます。
例えば、表データに空欄やNULLといった具体的な数値が入っていない状態のことです。
欠損値があるとうまくデータがとれないため、グラフに可視化しても偏りがでてしまいます。

##欠損値の確認方法
では、欠損値を見つけるためにはどういう方法があるのでしょうか。
それはpythonのpandasを使えば容易にみつけることができます。ここではcsvデータをつかっていることとします。
####isnull関数
pandasではデータが入っていないところを簡単に見つけてくれる関数があります。それは「<b>isnull()</b>」です。これはデータのないところを一つ一つの欄にFalse,Trueを表示してくれる関数です。値がない場合はTrue、ある場合はFalseを表示してくれます。
####any関数
では、データ一つ一つチェックしてくれなていいよ、欠損値があるかないかをしりたい場合は「<b>any()</b>」という関数をisnull()の後に付け足すことで各列(カラム)に欠損値があるかを確認してくれます。出力結果は、各カラムに対してTrue,Falseで表示されます。欠損値がある場合はTrue、ない場合はFalseを表示してくれます。

####sum関数
欠損値の個数をしりたい場合に使います。これもany関数と同様にisnull関数の後に付け加えることでnullの個数を出力することができます。

####value_counts関数
指定した列の数値の個数を調べてくれる関数です。例えば、0という数が10個ありますよというような出力結果を得ることができます。

##欠損値の対処法
欠損値を確認しただけでは何も意味がありません。それを具体的な数値を代入しないといけません。これを補間といいます。
####fillna関数
では、表のnullのところに数値を補間していきましょう。この時に使うのが「<b>fllna</b>」という関数です。この関数は表のnullが表示されているところをすべて数値に変えてくれます。引数に数値を指定することで好きな値を指定して保管することができます。fillna(0)とすれば、nullをすべて0に補間してくれます。
####dropna関数
具体的な数値に入れ替えるのではなく、nullがある行を削除したい場合は「<b>dropna</b>」という関数を使います。厳密には、ある列にnullがあった場合、それに対応した行を削除します。特定の列をしてする場合には引数に「subset=["列名"]」と指定します。
Kinesisストリームのサーバーサイド暗号を試してみる

サーバーサイドでは、Kinesisストリームのストレージ I/O のタイミングで暗号・復号処理を行います。


とりあえずやってみたのでメモ。


#1.Kinesis ストリームを作成し、暗号化する#

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280929/e4afd767-6661-1e4b-7dee-7b119aa49b01.png)



#2.CLI で暗号化されたストリームを確認#


```
$ aws kinesis describe-stream --stream-name test
{
    "StreamDescription": {
        "KeyId": "arn:aws:kms:ap-northeast-1:123456789012:key/c22d289b-669b-4e64-830b-30713982cccc",　 ※
        "EncryptionType": "KMS",　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　※
```



#3.Kinesis ストリームにデータを投入#


```
$ aws kinesis put-record --stream-name test --data aaa --partition-key 111
{
    "ShardId": "shardId-000000000000",
    "EncryptionType": "KMS",　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　※
    "SequenceNumber": "49602066998617934605647566514234616470937753180109275138"
}
```


#4.Kinesis ストリームからデータを取得し、暗号化されていることを確認#

```
$ aws kinesis get-shard-iterator --stream-name test --shard-id shardId-000000000000 --shard-iterator-type TRIM_HORIZON
{
    "ShardIterator": "AAAAAAAAAA"
}
```

※シャードイテレーターの有効期間は 300 秒です。

```
$ aws kinesis get-records --shard-iterator "AAAAAAAAAA"
{
    "Records": [
        {
            "Data": "Yg==",
            "PartitionKey": "b",
            "ApproximateArrivalTimestamp": 1499878938.69,
            "EncryptionType": "KMS",　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　※
            "SequenceNumber": "49575030693030991390093810531763277195232933088973553666"
        }
    ],
    "NextShardIterator": "DUMMY-NEXT",
    "MillisBehindLatest": 0
}
```
社内通貨システムでQuorumを使ってみた話

# 概要

ブロックチェーンの調査や社内の課題解決にあたって、社内で流通する通貨を作っている(社内通貨)。社内通貨システムを開発する上で [Quorum](https://www.goquorum.com/) を利用したので、Quorumを使って開発する上での知見を徒然に共有。

![lifull_coin.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/379/16ecbb3a-5f78-54b1-22c9-82c8ba415320.png)

---

# 社内通貨での構成

---

![arch_old.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/379/0fafcae5-fbd3-a3bc-a231-d3a0c0810a48.png)

こんな感じにすれば社内システムとも連携していい感じになるんじゃない？という気持ち

---

![arch_new.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/379/444d0e8b-4f78-c8e9-17e8-8617d87f0821.png)

Quorum全然自動化できない…とりあえずEC2で手動運用しとくかという気持ち

* ウォレットをWebアプリとして実装
    * Metamaskは総務のおばちゃんには使えない
    * おばちゃん「あらあら、ニーモニックってなんだい？:relaxed:」
* 秘密鍵はブラウザのローカル・ストレージに保存
    * 絶対に真似してはいけないやつ
* Quorumのコンセンサスアルゴリズムは**Raft**を選択

とはいえ、現在はQuorumをコンテナ化したり、初期クラスター構築やクラスターへのノード追加もある程度自動化できた

---

# QuorumのRaft

* BFTでなくCFT(過半数)
* RaftのLeader=Minterがブロックを生成、フォークはない
* ブロックの生成間隔が早い(初期値最低50ms間隔)
* トランザクションがきた時だけブロックを生成するのでエコ
* [Speculative minting](https://docs.goquorum.com/en/latest/Consensus/raft/#speculative-minting)
    * 投機的鋳造(直訳)
    * 新ブロックを取込み中にも無限に新ブロックを生成しキューイング(speculative chain)
    * Leader選挙などで一時的に親ブロックのハッシュ違いブロックが出来たら巻き戻す
* 早い、安い、脆い
    * Leaderを盲信
    * LeaderのRAFT_ID＋nodekeyでブロックハッシュに署名したものがブロックのextraDataに入るようになった

---

# 開発時にハマったところ

---

## Raftの時だけ `block.timestamp` がナノ秒(通常は秒単位)

```
timestamp: 1522098611952556500,
```

`web3.eth.getBlock` 呼び出し時に `block.timestamp` をNumberにキャストしようとしてエラー(`1.0.0-beta.54` までダメ)

```
Error: Number can only safely store up to 53 bits
```

`beta.55` でやっと解決(Raftの時だけStringで返ってくるので気持ち悪い)
解決したが `balanceOf` の返り値がString→BigNumberになってたり、
`web3.utils.fromWei` の引数がBigNumber不可でBN可だったり、意図が分からない
(追記：β期間の途中で開発者がTypeScript大好きっ子に変わったため、全部書き換えられ破滅的変更が加えられたそう)

---

## `permissioned-nodes.json` を動的に反映するという嘘

`--permissioned` フラグと `permissioned-nodes.json` によって参加ノードを固定できる

```
[
    "enode://abcd@127.0.0.1:30400?discport=0&raftport=50400",
    "enode://abcd@127.0.0.1:30400?discport=0&raftport=50400",
    "enode://abcd@127.0.0.1:30400?discport=0&raftport=50400"
]
```

ファイル更新により動的に反映とWikiに書いてあったが何も起こらない・・・
`static-nodes.json` と `permissioned-nodes.json` を更新して `admin.addPeer|removePeer` するしかなさそう
エンタープライズブロックチェーンのイベントで詳しそうな人に聞いてみたところ「Wikiを読んでるお前が悪い。ソースを読め。」というIT業界あるあるな知見を得た

---

## Truffle@v5のQuorumサポートがコンプリートリー・ブロークンしてた罠

![completely_broken.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/379/b8797b05-cc14-2af1-4613-4869fb2dd672.png)

どうりで何もかもうまくいかないと思った
もう古い話だが `5.0.14` で全て修正された模様
スマートコントラクトを全て書き直した模様
あと `truffle-config.js` に `type: quorum` を追加すること

---

# 今後

急に本気だしたのか `v2.3.0` あたりからおもしろそうな機能が追加されてるので試したい
https://github.com/jpmorganchase/quorum/releases

* `BETA RELEASE` Smart contract and RBAC based permissions model with support for organisations, sub-organisations, roles, account level access controls and nodes in the network.
* Quorum Raft upgrade `v3.3.13` and add support for Raft `Learner` nodes
* DNS support for static nodes
Sortable.jsを使ってスパイダーソリティアを作ってみた

# はじめに
現在SIerからWebエンジニアへの転職活動中で、ポートフォリオの一つとして
私の好きなゲームであるスパイダーソリティアをつくってみることにしました。
（スパイダーソリティアのルールは[こちら](https://www.soritia.jp/spider-solitaire-game-rules)から）


ソース：[Git Repo](https://github.com/takahashimayu/spider-solitaire)

プレイ：[こちらから](https://takahashimayu.github.io/spider-solitaire/index.html)
※レスポンシブ対応はしていません（PCのみ）

# イメージ
![スパイダーソリティア.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/500332/ab520b22-b1a6-63f1-d73c-d10c148db54d.png)

# 終わりに

- 作成期間は約１か月ほどかかりました
- アルゴリズムや、一からのモノ作りを学ぶことができました
- SortableJSは結構いろいろな動作をさせることができました

途中ほんとうに挫折しかけましたがなんとか完成させることができたのでとても良かったです！


＜使用技術＞
HTML／CSS／JavaScript／SortableJs／Bootstrap／JQuery　etc

＜参考にしたサイト＞
[【jQueryUIを使わずにドラッグ＆ドロップを実装したい】Javascriptライブラリ「Sortable」を使ってみた](https://qiita.com/AVELWP/items/536c797a63c081cf7bc0)
バカンの品質保証って？

こんにちは、[株式会社バカン](https://www.vacancorp.com)のQA（品質保証）に携わる者です。
この記事は、[バカン (Vacan) Advent Calendar 2019](https://qiita.com/advent-calendar/2019/vacan)の11日目(12/11)の投稿です。

今回バカンの魅力の1つ、QAについてご紹介いたします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540838/e07fb185-759f-bca0-a76a-c3c3c72c0a03.png)

#QAって？
「Quality Assurance」の略称。日本語だと「品質保証」

品質保証とは、製品やサービスが要求された品質を満たしているかを保証するお仕事です。
詳しくは品質保証、ソフトウェアテストで検索してみてください！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540838/c0237ee4-ff05-e99a-740a-6bb005699a2c.png)


#どういう活動？
リリースされるサービスごとにテスト設計書を作成し、日々テストをしています。
主にソフトウェアテストの工程（テスト計画〜終了作業）まで行っています。
ただテストするだけでなく、市場不具合を削減する為に対策したり、
開発チームと連携し、根本から品質を高める為にどうすれば良いかを話し合ったりします。

そういうバカンでは、現在QAチーム3人でお仕事しています。
その3人の個性もバラバラですが、得意なところを補い合って非常に良いチームです！


#バカンとして何を目指している？
バカンはいわゆるスタートアップ企業であり、
品質とスピードのバランスを取ることが重要になります。
適切な品質を適切なスピードで提供することによって、
事業として成り立つサービスに貢献することを目指しています。
エンジニアは「創る仕事」、QAは「検査する仕事」であり、距離が離れやすい両職種ですが、
バカンのエンジニアは高い技術力を持ちつつもホスピタリティを重視している為、
QAとの関係も良好で、意見交換や改善要望などをお互いに出し合って日々高めあっています。
（バカンのValueを体現！）

#やりがいって？
テストのイメージが強いQAですが、バカンQAはそれだけではありません！
QAメンバー三者三様、それぞれの知見を活かし、
サービスが魅力的であるか等の改善や意見を提案することでより良いサービス作りに貢献しています。
あるサービスでは提案を34件したところ、31件が改善されました！（嬉しい！）
更にバカンQAでは、API単位のテストをエンジニアと連携して実施したり、
ソフトウェアのみならず、ハードウェアの品質向上を目指すテストを行ったりと、
品質保証に関してのスキルアップも可能です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540838/5dc3f1a4-dbc6-fdf9-b422-98cf9291888f.png)


#最後に
バカンでは一緒に働くメンバーを募集しています！
記事を読んで、バカンって素敵なところだな、って思った方は、
ぜひ！こちら（https://www.wantedly.com/companies/vacancorp ）ものぞいて見てください！
お待ちしています。

株式会社バカン：https://www.vacancorp.com
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540838/f971ac11-7143-1e76-6052-abff99603261.png)
Pythonで複素数扱うときに役立ちそうなことをまとめてみる

普段は量子コンピュータのシミュレータをPythonで書いています。
その都合上、複素数は大変よく使います。

Python, numpyは複素数もサポートされており、あんまり困ることはないのですが、いくつか罠もあるので、そこらへんをまとめておきます。

# Pythonでの複素数の基本
このへんは、普通にリファレンスに載ってるやつです。軽くおさらい。

## 虚数単位は`1j`、型は`complex`
数学では多くの場合、虚数単位にはiが使われますが、Pythonでは`1j`が使われます。
(電気などの分野で、iは電流に使っているので虚数単位をjと置く、というのを見たことがありますが、それ以外の分野では他に知りません)

`2j`とか、`1.23j`のような書き方もできます。(普通に`1j`の2倍、1.23倍、という意味)
`1`と`1.`で型に違いがあるのに対し、`1j`と`1.j`はどちらも同じです。

a, bをfloatとして、`a + b * 1.j`は`complex(a, b)`と書くこともできます。

## `.real`, `.imag`で実部、虚部を取り出す `conjugate()`で複素共役をとる
`(1 + 2j).real` は `1`, `(1 + 2j).imag` は `2` です。メソッドではないので`()`を付けないことに注意してください。
複素共役は`(1 + 2j).conjugate()`のように、`.conjugate()`を使います。こちらはメソッドなので`()`が必要です。
ちなみに`.real`, `.imag`, `conjugate()` は、実はfloatやintに対しても使えます。

## numpyも普通に使える
numpyでも`np.array([1 + 2j, 3, 4j, 5+6.7j])`のように、普通に複素数が使えます。このとき`dtype`は、デフォルトでは'complex128'になります。
`.real`, `.imag`, `.conjugate()`もnumpyのarrayに対して使うことができます。
また、numpyでは、`.conjugate()`のかわりに`.conj()`でも構いません。

なお、行列のエルミート共役取るのが`.conj().T`とする必要があり、やや面倒です。
`np.matrix`だと`.H`でできたんですが。`np.array`にはないようです。

## `math`のかわりに`cmath`
`import math`のかわりに`import cmath`を使います。
`math`の関数が定義域や値域を実数にとっているのに対し、`cmath`は定義域や値域に複素数も含まれます。
詳細は[公式ドキュメント](https://docs.python.org/ja/3/library/cmath.html)参照。

複素数の偏角を求める`phase()`や、半径と偏角のタプルを返す`polar()`や、半径と偏角から複素数を作る`rect()`といった便利な関数も用意されてます。

# Pythonで複素数を扱うときの注意
いくつか罠やその対処を書いていきます。

## 複素数になりうるなら、numpyのarrayは予め複素数で作る

Pythonは自分で型を書かなくても暗黙のうちに型変換してくれますが、numpyでは、arrayは型を持っていて、勝手にarrayの型が変わることはありません。(intのarrayがfloatにならないのも同様です)

```py
a = np.array([1., 2.]) # dtypeがfloat64
a *= 1j
# UFuncTypeError: Cannot cast ufunc 'multiply' output from dtype('complex128') to dtype('float64') with casting rule 'same_kind'
```

こうならないために
`np.array([1., 2.], dtype=np.complex128)`のように最初から型を指定するか、
`np.array([1+0j, 2.], dtype=np.complex128)`のように最初から複素数を含めるかが必要です。

## 複素ベクトルの内積は`np.dot`ではなく`np.vdot`
これは本当に罠です。

複素ベクトルの内積は$u\cdot v = \sum_{i=1}^n u_i^* v_i$のように、$u$の複素共役と$v$との各成分の積の和を取るのが普通ですが、`np.dot`では、複素共役をとらず、そのまま各成分の積の和を取ります。どうしても`np.dot`を使う必要がある場合は、自分で複素共役を取ってつかいましょう。
一方、`np.vdot`を使うと、複素ベクトルであっても適切に内積を取れます。

## 当たり前だけど、複素数同士は大小の比較ができない
他の言語で複素数を扱っている人には当たり前ですが。複素数同士は比較ができません。

より正確には、「複素数型」の大小比較ができないので、プログラマが計算結果が実数になっていると知っていても、コンピュータにとって複素数型だったら、大小比較はできません。`.real`などで実数にする必要があります。

## 複素数配列の「絶対値の2乗」は効率がいい方法がないらしい
`np.abs`は、内部的には`sqrt(real**2 + imag**2)`のような計算をしているので、これを2乗するのは無駄が多いです。
一方、`arr.real ** 2 + arr.imag **2`のような計算をPython側で書くと、インプレースで計算されず、無駄なメモリ確保が生じます。
以前調べたことがあるんですが、[StackOverflowには、numba使え、と書いてありました](https://stackoverflow.com/questions/30437947/most-memory-efficient-way-to-compute-abs2-of-complex-numpy-ndarray)。Python, numpyだけでインプレースに無駄な開平計算をせずにやる方法は意外とないみたいです。
【React】Firebase Authで、アカウントに複数のプロバイダを紐付ける

**[Firebase Authentication](https://firebase.google.com/docs/auth?hl=ja)**、とても便利ですよね。

今回、Firebase Authenticationを使って、`Googleログイン`したアカウントに対して、`電話番号`を紐付けます。

## Firebaseの準備

Fireabse Authの設定方法は[こちら](https://qiita.com/HorieH/items/8b4b1c4807e1eb6fcb0a)。
ログイン方法として、`電話番号`と`Google`を`有効`にしておいてください。

## googleアカウントでの認証
まずはgoogleアカウントで認証させます。
`signInWithPopup`というメソッドを使います。

## 電話番号での認証
次に、電話番号での認証です。
`reCAPTCHA`の設定がちょっとめんどくさいので注意しましょう。
`signInWithPhoneNumber`というメソッドを使います。

## アカウントに複数の認証プロバイダをリンク
ここまで完了したら、
currentUserに対して、`linkAndRetrieveDataWithCredential`というメソッドを使い、アカウントに複数の認証プロバイダをリンクさせることができます。

以下のようになっていればOKです。
<img width="1372" alt="スクリーンショット 2019-07-13 16.34.45.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/270696/786c550b-a8ec-b1b3-288e-48f88bc96b6a.png">

とても簡単に実装することができました。
`2要素認証`とかも簡単に実現できますね。

今回作成したソースコードをgithutbで公開しています。[こちらから](https://github.com/katsuomi/phoneNumber-auth-for-firebase)
(めっちゃ無駄ですが、アーキテクチャとして、Reduxを採用しています。。。
ちょっと前に[リリース](https://react-redux.js.org/next/api/hooks)されたhooks APIに対応した機能を使ってみたくて...)

参考： [公式ドキュメント](https://firebase.google.com/docs/auth/web/phone-auth?hl=ja)

以上です。