OmniAICreator/Qiita-1.07M huggingface dataset:
FLOCSS設計ってどんなんだっけ？となった時に思い出せる簡単なメモ

# 概要
Found Layout Object CSSの略で、OOCSSやBEM、SMACSSの流れを受けて考案されたCSS設計思想

# FLOCSS
## Foundation

- サイト全体のデフォルトスタイルを定義する
- `_reset.scss`, `_base.scss`, `_mixin.scss`, `_variables.scss`

## Layout
- Objectの配置を決める
- ページ単位で唯一の存在になるのでidセレクタを使う
- 構造に関わる部分でProjectごとの整列用に`flex`や`float`解除につかったり、sitewidthを定義したりする程度の記述
- `_header.scss`, `_sidebar.scss`, `_footer.scss`


## Object

- Component
    - 再利用可能なパーツ
    - プレフィックスに`c-`をつける（`.c-btn`, `.c-btn_primary`)
- Project
    - Componentにするほどでもないパーツ(個々のページの中でしか使わない、１回しか使わないなど)
    - プレフィックスに`p-`をつける（`.p-article`, `.p-article__title`)
- Utility
    - 汎用クラスで単一のスタイルを持つ
    - プレフィックスに`u-`をつける（`.u-clearfix`, `.u-block`)

命名規則ではBEMと同じ規則が用いられている

# ディレクトリ構成
FLOのそれぞれの構造に合わせ、ディレクトリ構造も分割することでメンテナンスしやすくなる

```javascript=
css
|-- foundation
|   |-- _base.scss
|   |-- _reset.scss
|-- layout
|   |-- _main.scss
|   |-- _sidebar.scss
|-- object
    |-- component
    |   |-- _button.scss
    |   |-- _grid.scss
    |-- component 
    |   |-- _articles.scss
    |   |-- _profile.scss
    |-- component 
        |-- _clearfix.scss
        |-- _margin.scss
        |-- _text.scss
```

## 例

```html:sample.html
<!-- flocss.html -->
<h1>FLOCSS</h1>
<!-- 以下、layout idセレクタを使う -->
<div id="countainer">
  <!-- 以下、component(module) c-を使う -->
  <ul class="c-menu">
  　　<!-- FLOCSSではmodifierをつける時マルチクラスパターンを基本とする -->
    <li class="c-menu__item c-menu__item--large">link</li>
    <li class="c-menu__item c-menu__item--active">active</li>
  </ul>
</div>
```

```scss:style.scss
.c-menu {
  list-style-type: none;
  &__item {
    width: 80px;
    margin-bottom: 5px;
    color: #4486F7;
    border: 1px solid #4486F7;
    text-align: center;
    &--large {
      width: 100px;
    }
    &--active {
      color: #fff;
      background-color: #4486F7;
    }
  }
}
```
Android Studioでアプリ実行時にNo Connected Devices Foundとなるのを解消する

Android StudioでRunすると以下のようなメッセージが出ることがあります。
![スクリーンショット 2020-02-09 1.21.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88483/9ecff597-21e5-b008-0584-3dbc1c89c88d.png)

# 解決策

以下は仮想デバイスを使いたい場合の解決策です。

Android Virtual Deviceが起動しているか確認します。Tools>AVD Manager>該当のデバイスをLaunch
その後再度Runすればアプリを起動できます。
ちなみにデバイスがない場合は画面に従って作成後Launchします。

しょうもないですが、PC再起動時にデバイスをLaunchし直す必要があるので意外にハマりました…
SikuliXをSurface Proにインストールしてみた

今回は環境構築のみですが、初めてで手間取ったところもあったので、
メモ代わりに書いておきます。

まず、セットアップにあたってはmasuoさんの下記ブログ記事を参考にしました。
[Sikuli 入手方法とセットアップ方法 : masuoのブログ](http://masuo.doorblog.jp/archives/51788203.html)

SikuliXって何？っていう方はmasuoさんのブログの以下のエントリーを見てみてください。
[SikuliをRPAに使う－[1]はじめに : masuoのブログ](http://masuo.doorblog.jp/archives/51800949.html?ref=popular_article&id=4390260-321735)

英語ですが現在の開発者のRaiManさんのページも分かりやすいです。
[RaiMan's SikuliX](http://sikulix.com/)

#試した環境
Windows 10 Home 64bit
Surface Pro 6 (Intel Core i5, 8GB RAM)

#手順
基本的にインストール作業は以下の3つのみで、非常にシンプルです。
##### 1. Java SDKをインストール 
##### 2. SikuliX本体をインストール 
##### 3. Jythonをインストール 

#今回使ったもの
1. Java SDK
今回はmasuoさんのご紹介でAmazon Correttoを使用。
[Amazon Corretto（﻿本番環境に対応したOpenJDKディストリビューション﻿）| AWS﻿](https://aws.amazon.com/jp/corretto/)

2. SikuliX本体
今回はSikuliX 2.0.2を使用。ダウンロードは以下ページのリンクより。
[RaiMan's SikuliX](http://sikulix.com/)

3. Jythonをダウンロード
こちらも上記のダウンロードページから落とせます。

#実際にやってみる
## 1. Java SDK（Amazon Corretto）をインストール 
上記、Amazonのページからダウンロードしてインストーラーを実行するだけ。
Windows x64用のJDKでmsi形式のファイルを落としました。
当初ver8をインストールしましたが、SurfaceですとSikuliXの表示が非常に小さくなったので、
ver11を入れなおしました。

## 2. SikuliX本体をインストール
これはインストーラー等不要でC:ドライブ直下などに適当なフォルダを入れておいて、
.jarファイルをそこに置くだけでOK。

## 3. Jythonをインストール
こちらもインストーラーはないですが、Sikulixの.jarファイルと同じ場所にJythonの.jarファイルを入れておけば、SikuliX起動時に所定のフォルダに自動で移動されます。

手順としては、以下のとおり。
・SikuliXを起動（sikulix-2.0.2.jarをダブルクリック）
・下記フォルダにJythonの.jarファイルが自動的に移動されます。
`C:\Users\ユーザー名\AppData\Roaming\Sikulix\Extensions`

これでセットアップはおしまい！！

#インストールに発生したトラブルとその対策
##SurfaceだとSikuliXがとても小さく表示されてしまう
Surfaceはいわゆる高DPIディスプレイを搭載したPCで、よくこういうトラブルに見舞われます。
普通のWindowsアプリですと、互換性の設定をいじれば対処できるのですが、Javaのアプリの場合はそうもいかないようです。
今回はJDK ver11だと高DPIデバイスに対応できると聞いたので、Corretto 11を入れてみました。
ただ、SikuliXが正式対応していない可能性があるため、ちゃんと動作するか今後試していきたいと思います。

このような表示だったものが、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490870/e73c4fd6-b895-3e2c-9f7e-a7a7aa4030ce.png)

このように表示されるようになりました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490870/242e421f-4e98-16e7-a55f-0ec9116cbab7.png)


##Corretto 8をアンインストールしてCoretto 11を入れなおしたら、SikuliXが動かなくなった
.jarファイルをどのアプリケーションで開くか、というWindowsに対しての登録が外れていました。
「アプリケーションで開く」で、javaw.exeを指定してもダメだったので、ググったところ、レジストリの修正が必要と判り、直しました。

具体的には、
`コンピューター\HKEY_CLASSES_ROOT\Applications\javaw.exe\shell\open\command`
の値が下記のようになっていましたが、
`"C:\Program Files\Amazon Corretto\jdk11.0.3_7\bin\javaw.exe" "%1"`

以下のように修正したら動くようになりました。
`"C:\Program Files\Amazon Corretto\jdk11.0.3_7\bin\javaw.exe" -jar "%1"`


#まとめ
インストール手順自体は必要なファイルも3つほどで、とてもシンプルでした。
ただ、Surface特有の問題や、それに伴ってJavaアプリあるある的なひっかかる点がありましたので、この記事にまとめてみました。
Java 11でSikuliXがちゃんと動くかどうかは、これから使ってみて確かめていきたいと思います。
【R】 滑らかなcurveを描こう

# 1. 趣旨

発端はこの記事を書いたこと。
[【R】 正規分布のスペクトルを描く](https://qiita.com/zakkiiii/items/73064b7b70bed24aeec2)

この記事を書いてグラフを画像に出力したときに、ふと思った。

<BR><BR>**平滑化したい！！！！！**

**`curve()`を使って滑らかなカーブをplotしたい！！！！！**　と。


<BR><BR>ということで、頑張って画質向上を目指しましょう。

# 2. 比較

## 2-1. before

![nd.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/551476/c643f688-94c9-7e24-04d8-ee72a4adc178.png)

カクカクすぎて見るに耐えない。
このままSVG出力しても拡大に耐えられません。

## 2-2. after

![Rplot08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/551476/26c19d61-7d87-cb72-7803-6f0528be7aa5.png)

芸術品と見紛うほどの美しさ。

# 3. 手順

## 3-1. 「点の数」を増やす

ざっくり言ってしまえば、Rで曲線グラフを描写しようとすると「沢山の点と点を直線で結ぶ」ことで曲線を表現しようとしてしまいます。なのでカクカクしてても仕方がない・・・いや待てよ。じゃあ点の数を増やせばいいんじゃないか・・・？
<BR>

> 君のような勘のいいガキは嫌いだよ　— 　ショウ・タッカー

<BR>
この点のことをノードと言いますが、デフォルトでは101個となっています。
ですが実は引数で無限に増やせます。パソコンの処理能力が追い付く限りは。

ということで、**増やしましょう。好きなだけ**。
`n`という引数に、ノード数を設定してください。

参考までに、僕のおんぼろPC環境<font color = "#CCCCCC">（6年目のオンボロPC）</font>でも一曲線当たり3万点とかでも全然余裕です。

```R
# before (ノード数 = 101)
> curve(dnorm(x, 0, 1), -4, 4, add = F, lwd = 2)

# after (ノード数 = 30,000)
> curve(dnorm(x, 0, 1), -4, 4, add = F, lwd = 2, n = 30000)
```

## 3-2. ベクター画像として保存する

点の数を増やしたら、これを一旦ベクター画像として保存しましょう。
RStudioの場合、Plot図の上のほうにある`Export > Save as Image`を選択します。

サイズは調整してもしなくても良いですが、Image Formatからベクター画像形式のものを選択しましょう。
**基本的にはSVGファイルです。**
Metafileでもいいですが、点の数を増やしすぎると正常に保存できないことがあります。

## 3-3. お好きなエディタでPNGとして保存する

必要であれば、保存したSVGファイルを**Inkscape**など、SVGファイルが扱えるソフトで開いて、そのままPNGとしてエクスポートしましょう。
画像の大きさは、ここで調整できます。少し大きめに保存しておくのがオススメ。
よっぽど大きくしない限りはカクカクしないはずですが、もしカクカクが気になったら3-1. に戻って点の数を増やしてください。

# 4. ちなみに

- 実はSVGとして出力することで、曲線部分だけでなく軸やラベル文字までも綺麗に描写できるようになります。
- 紹介した方法は、`curve()`だけでなく`plot()`にも使えることを確認しました。他にも何かあったら教えてください。

```R
> FUN <- function(x) {x^3-x^2-25*x+1}

# before (ノード数 = 5)
> plot(FUN, -7, 7, n = 5)

# after (ノード数 = 100,000)
> plot(FUN, -7, 7, n = 100000)
```



# 5. おわりに

やっぱりグラフは滑らかなのが一番！

Enjoy!

おしまい。
python仮想環境を使う【venv】

# はじめに
pythonの仮想環境について教わったメモです。

## なぜpython仮想環境が必要なの？
- プロジェクトごとに必要なライブラリを管理するため
- ライブラリ同士の競合を防ぐため

## 環境
2つの環境で試しました。

1. Windows 10 + Python 3.7.3
2. Linux(Debian 10.2) + Python 3.7.3

# venvでpython仮想環境を使う

## 仮想環境を作成する

```:Windows
>python -m venv testvenv
```

```:Linux
$ python3 -m venv testvenv
```

`testvenv`は仮想環境の名前です。任意の名前でOKです。
実行すると、カレントディレクトリに、新規ディレクトリ`testvenv`が作成されます。

## 仮想環境を起動する

```:Windows
>testvenv\Scipts\activate

(testvenv) >
```

```:Linux
$ source testenv/bin/activate

(testvenv) $
```

仮想環境を起動した状態で、通常のコマンドプロンプト・ターミナルと同様のコマンドが使えます。
`pip`コマンドでライブラリをインストールすると、仮想環境にインストールされます。仮想環境を抜けた状態でこのライブラリを使用しようとしても、使えません。
```pip freeze```すると、仮想環境にインストールされているライブラリを確認できます。

## 仮想環境を終了する
```:Windows
(testvenv) >deactivate
>
```
```:Linux
(testvenv) $ deactivate
$
```
コンソールケーブルがmacosで認識されない問題を直したかった

FT232Rを使用したUSBtoRJ45なコンソールケーブルが2018年終わりころになぜか手元の環境で使えなくなってたんですけど、その問題について今回調べたのでそのメモ。（解決はしてない）
![IMG_5090.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/478491/196e1a25-8f2d-5322-d444-4d04f6e772a6.png)
## 環境
- macos 10.15.3
- FTDIのドライバはインストール済

## 状況
システムレポートでUSBデバイスを確認すると、認識されていることは確認できるものの
`/dev`にttyのデバイスが出ず、任意のツールで利用できない。
windows10な環境だと問題なく動作する。


## 拡張一覧を確認

```bash
% kextstat | grep FTDI
%
```
読み込みはされてなさそう？

```bash
% kextfind | grep FTD
/Library/Extensions/FTDIKext.kext
/Library/Extensions/FTDIUSBSerialDriver.kext
/System/Library/DriverExtensions/DriverKit.AppleUSBFTDI.dext
```
mac内にある関連する拡張を列挙、3つあるっぽい。

## 署名を確認
kextloadを試すと1つ読み込みができなかった。

```bash
% sudo kextload /Library/Extensions/FTDIUSBSerialDriver.kext
/Library/Extensions/FTDIUSBSerialDriver.kext failed to load - (libkern/kext) not loadable (reason unspecified); check the system/kernel logs for errors or try kextutil(8).
```

コンソールを確認
![console1](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/478491/12082ab6-150b-b9f2-949c-d01f9eb9e0ab.png)
`Untrusted kexts are not allowed`


とりあえず[ここ](https://apple.stackexchange.com/questions/337594/load-untrusted-kernel-extension-in-macos-mojave)の通りに拡張を確認

```bash
% codesign --display --verbose=4 /Library/Extensions/FTDIUSBSerialDriver.kext
Executable=/Library/Extensions/FTDIUSBSerialDriver.kext/Contents/MacOS/FTDIUSBSerialDriver
Identifier=com.FTDI.driver.FTDIUSBSerialDriver
Format=bundle with Mach-O thin (x86_64)
CodeDirectory v=20200 size=739 flags=0x0(none) hashes=15+5 location=embedded
Hash type=sha256 size=32
CandidateCDHash sha1=40318687a974f4d412212a1a8cf2c83fe848ef39
CandidateCDHashFull sha1=40318687a974f4d412212a1a8cf2c83fe848ef39
CandidateCDHash sha256=4e631d06b5bb45827c96e1091d75e74d0f07acef
CandidateCDHashFull sha256=4e631d06b5bb45827c96e1091d75e74d0f07acef023383f64b236aa844bedf1e
Hash choices=sha1,sha256
CMSDigest=9d42ee4c4fc818307916cbf30c1a809928b5bf686f0f73fb281bee30cc588958
CMSDigestType=2
Page size=4096
CDHash=4e631d06b5bb45827c96e1091d75e74d0f07acef
Signature size=4791
Authority=(unavailable)
Info.plist=not bound
TeamIdentifier=REW97NLHMG
Sealed Resources version=2 rules=13 files=1
Internal requirements count=1 size=208
```
`Authority=(unavailable)`

## とりあえず再インストール

```
# 削除
% sudo rm -rf /Library/Extensions/FTDIUSBSerialDriver.kext

# インストール後確認
% codesign --verify -v /Library/Extensions/FTDIUSBSerialDriver.kext
/Library/Extensions/FTDIUSBSerialDriver.kext: CSSMERR_TP_CERT_REVOKED
In architecture: x86_64
```
だめみたい

## おわりに
特定のモジュールだけ検証を回避するようにできたらいいんだけどなぁ(全部を検証回避は流石にヤバそうだなって)
vue css-loader エラー：Module build failed: TypeError: this.getResolve is not a functionでコケる

# 症状
```
ERROR in ./node_modules/css-loader?sourceMap!./node_modules/vue-loader/lib/style-compiler?{"vue":true,"id":"data-v-39e2d1f7","scoped":true,"hasInlineConfig":false}!./node_modules/sass-loader/dist/cjs.js!./src/pages/About/About.scss
Module build failed: TypeError: this.getResolve is not a function
    at Object.loader (/Users/MyUserName/MyDirectory/node_modules/sass-loader/dist/index.js:52:26)
...
..
.
```
# 解決策
[sass-loaderで似たようなエラーが出たとき、一旦uninstall(remove)して、古いバージョンをistall(add)してみると直った、という記事](https://qiita.com/gosairei1207/items/95454ec5a396bd5f05fd)を見て、同じことをcss-loaderでも試したら直った。

#### npmの場合

```
$ npm uninstall css-loader
$ npm install css-loader@2.0.0
```

#### yarnの場合

```
$ yarn remove css-loader
$ yarn add css-loader@2.0.0
```

# 備考：css-loaderのverについて
[npmのcss-loaderのページ](https://www.npmjs.com/package/css-loader?activeTab=versions)を見て適当に@2.0.0としたが、もうちょい新しいverでもいけるのかもしれないものの、試してない。
[ニコニコ動画]一般会員でもプレミアム機能を使える理由

#はじめに
**PC版**ニコ動において
実は一般会員でもプレミアム限定の機能を使えてしまうという話です。
もちろん普通に使ってたら無理なのですが、ちょっとした裏技とか強引に使う方法とかがあるということです。

プレミアム会員になると使える機能

- 好きな位置で再生
- 反転再生
- 指定した秒数だけ動画をスキップ
- 広告なし（Adblock導入すれば一般会員でも消せる）

これらの機能は理論上（？）一般会員でも使えます。

#一般会員がプレミアム機能を使える理由
これが可能な理由は「プレミアム会員かどうかの判定とか制限はすべてブラウザ側で行われているから」です。
逆にサーバー側で制限する方法が思いつきません・・・。

動画プレイヤーはブラウザで読み込まれたJavaScriptで動作しているので、プレイヤーにかかる制限なども全部JavaScriptです。

これがわかれば、２通りの制限回避方法が思いつきます。

- ニコ動のプレイヤーを使わずに直接動画を再生する
- プレイヤーのJavaScriptを書き換えてプレミアム会員だと認識させる

「ニコニコ動画のプレイヤーが制限かけてるんだから、違うプレイヤーで再生すりゃいいじゃん！」っていうのと「制限されないようにプレイヤーのコード書き換えりゃいいじゃん！」っていうやつですね。

#実際やってみた
本当にできるかどうか、両方とも試してみました。

###まず１つ目の方法
動画のURLを探して直接アクセスするだけなのでかなり楽な方法。ただ、当然この方法ではニコニココメントは読み込まれないのでただの動画をみるだけになります。
これはChrome開発者ツール使えばすぐにできました→[動画URLを探す手順](https://tialight.com/?p=810)

###そして２つ目の方法
これはどうやってやるかというと、ニコ動のJavaScript読み込みをブロックして代わりに書き換えたものを読み込ませます。
Tampermonkeyを使えば実現できます。
**（過去に制限回避できるスクリプトを配布して公式から警告受けた人がいるので、今回配布はせずに流れだけ簡単に紹介します）**

ニコ動で読み込まれるJavaScriptの１つにwatch_appがあります。
その中の至る所にisPremiumというキーがあるので全部値をtrueにしてやればプレミアム会員と認識されることになります。
書き換えたものを自分のサーバーにアップロードします。
Tampermonkeyを使ってニコ動で読み込まれるスクリプトをブロックし、書き換えたものを代わりに読み込ませればプレミアム機能を使えます。

#さいごに
一般会員でもプレミアム機能を使える理由を説明してみました。これを防ぐにはサーバー側で制限する必要がありますが、それは無理なんじゃないかと思っています。
それか、JavaScriptを超絶読みづらくするとか・・・？
Next.jsのAMPページでfallback属性を指定するとErrorが発生してしまう

# tl;dr
空文字を指定しましょう
👍

```html
<amp-img
  fallback=""
  src="hoge"
></amp-img>
```
👎

```html
<amp-img
  fallback
  src="hoge"
></amp-img>
```

# 解説
AMP HTMLでは任意の要素にfallback属性を指定することができます。
公式サイトではvideo要素が対応していないブラウザに警告文章を出す例を示しています。
[Placeholders & fallbacks - amp.dev](https://amp.dev/documentation/guides-and-tutorials/develop/style_and_layout/placeholders/)

Next.jsではAMP Pagesを簡単に作成することができます。
しかし、fallback属性を指定するとAmp Validationが警告文を表示します。

```shell
The attribute 'fallback' in tag 'amp-img' is set to the invalid value 'true'.
```
*訳:amp-imgタグ内のfallback要素に無効な値'true'が設定されている*

該当Issueはこちらです。
[Amp-image fallback leads to validation error · Issue #10000 · zeit/next.js](https://github.com/zeit/next.js/issues/10000)

Amp ValidationにHTMLが渡るまでにtrueが自動的に設定されることからこの問題が発生します。
空文字を必ず設定しましょう。
GCP と SEIL で VPN 接続

#はじめに
IIJ の提供する SEIL と GCP を VPN 接続したので忘備録としてまとめておきます。環境としては以下のとおりです。アドレスは適時環境に合わせて読み替えてください。
設定のポイントとしては
- IKEv1 を使用(SEIL が IKEv2 サポート外のため)
- ルーティングベース IPsec を使用(ポリシーベース <-> ルーティングベースではエラーになる)

```
test server (192.168.0.2/24)
 |
 |(192.168.0.1/24)
VPC
 |VPN(10.0.0.1)
 |
 |(10.0.0.2)
SEIL
 (192.168.1.1/24)
```

#SEIL の設定
必要な部分だけ抜粋します。[GCP IKEv1 の設定](https://cloud.google.com/vpn/docs/concepts/supported-ike-ciphers?hl=ja)と合わせる必要があるため注意が必要です。
`interface ipsec1 description to_gcp
interface ipsec1 mtu 1280
interface ipsec1 tcp-mss 1228
interface ipsec1 tunnel 10.0.0.2 10.0.0.1
interface ipsec1 unnumbered
route add 192.168.0.0/24 ipsec1 distance 10
ike auto-initiation enable
ike randomize-padding-value enable
ike randomize-padding-length disable
ike maximum-padding-length 20
ike strict-padding-byte-check disable
ike exclusive-tail enable
ike preshared-key add 10.0.0.1 "pass"
ike proposal add IKEP01 encryption aes128 hash sha1 authentication preshared-key dh-group modp1024 lifetime-of-time 08h
ike peer add TUNNEL01 address 10.0.0.1 exchange-mode main proposals IKEP01 dpd enable tunnel-interface enable
ipsec security-association proposal add SAP01 pfs-group modp1024 authentication-algorithm hmac-sha1 encryption-algorithm aes128 lifetime-of-time 01h
ipsec security-association add TUNNEL01 tunnel-interface ipsec1 ipv6 block ike SAP01 esp enable
`

#GCP の設定
GCP の設定を行う時の注意点としてはインスタンス、VPC、VPN、静的アドレスすべてを同じリージョンから選択する必要があります(多少違っても大丈夫かもしれないけど、合わせるのが無難？)
## VPC ネットワーク
### グローバルアドレスの予約
実際には SEIL の設定前に取得する必要があります。
GCP の VPC ネットワークの外部 IP アドレスから「静的アドレスを予約」を選択、ネットワークサービス階層は標準、IPバージョンは v4 、タイプはリージョンで予約します。
### VPCの設定
VPCネットワークから「VPCネットワークの作成」を選択します。サブネットはカスタムでIPアドレスを設定。
### ファイアウォールの設定
「ファイアウォールルールを作成」から新しいルールを追加します。ネットワークはVPCで設定したネットワークを選択、トラフィックは上り(incoming)で一致した時のアクションは許可、ターゲット(あて先)は今回は「ネットワーク上のすべてのインスタンス」にします。
ソース IP の範囲は SEIL のローカルアドレス 192.168.1.0/24 を指定。ポートは試験用なのですべて許可。
## VPN の設定
ハイブリット接続から「VPN設定ウィザード」を選択します。今回は可用性は必要ないので Classic VPN を選択、VPCネットワークとリージョンは同じものを選択します。IP アドレスは「グローバルアドレスの予約」で作成したものを選択します。
続いて VPN トンネルを作成します。リモートピア IP アドレスは SEIL の外側を記入、IKEのバージョンは v1、ルーティングオプションはルートベースを選択します。
## test server の設定
テスト用なので Debian 、fi-micro でいいです。ネットワークインターフェースには VPC ネットワークで作成したものを選択します。インターフェースアドレスはカスタムでも自動でもどちらでも問題ないです。
今回は 192.168.0.2/24 を指定します。

#確認
確認は ping コマンドを使用して相互に確認します。
注意点としてはGCP で提供される [VPC に設定されるデフォルトゲートウェイ](https://cloud.google.com/vpc/docs/vpc?hl=ja)は icmp に反応しないので、今回の環境では 192.168.0.1/24 に ping を飛ばしても応答はありません。
laravel-debugbarを使えば様々な値をブラウザ上で簡単に確認できる

# 概要
laravel-debugbarを導入することで、変数内の値やリクエスト内容、レスポンス内容などその他様々な情報をブラウザ上で操作しながら確認することができるようになる便利なバーが出現する

# install

```shell
$ cd [適用するプロジェクト]
$ composer require barryvdh/laravel-debugbar
```

# サービスプロバイダーとファサードの追加

```php:config/app.php
'providers' => [
    Barryvdh\Debugbar\ServiceProvider::class,
],

'aliases' => [
    'Debugbar' => Barryvdh\Debugbar\Facade::class,
]
```

# envファイルの設定

```php:.env
APP_DEBUG=true
```

## 本番環境へデプロイ
- envファイルを変更するだけで良い

```php:.env
APP_DEBUG=false
```
Stackdriverのログをアラートに含めたかった

## TL;DR
- アプリケーションからStackdriverに吐き出したエラーログをアラート(メールやSlack)に含めたかった
- しかし、GCPの仕様上、ログを起点としたアラートにログ本文を埋め込むことができなかった
- 仕方がないので、妥協してエラーの種類が分かる情報をメトリクスのラベルとして埋め込むことにした
- (しかし、これはラベルの本来の使い方では無さそうなので不安は残る)


## 背景
GCP Functionsを使ったアプリケーションを開発していました。
これをリリースし、運用するにあたって、アプリケーションが吐き出すエラーログを検知して、アラートを飛ばしたいという要求が出てきました。

エラーログの中でも、通知が必要なものを絞り混むために、 `[[ALERT_ERROR]]` のようなテキストが埋め込まれたログのみ通知することにしました。
さらに、運用フローをできるだけ機械的に回せるようにするため、続いてエラーの種類を識別するテキストを埋め込んでいました。
これにエラーの詳細情報、各種変数などが加わったものがログになっています。
例: `[[ALERT_ERROR]] SOMETHING_HAPPENED Some bad things happened. foo:[1000] bar:[xxxxx]`

さて、我々はアラートのメール(Slackでも何でも良いのですが、ひとまずメールにしておきましょう)を見て、どんなエラーが起きたのかを知りたかったので、ログの本文を入れようとしたのですが…。
なぜだか分かりませんが、GCPにはこれを実現する手段が用意されていませんでした。

## 妥協案
アラートに含められる[ドキュメントテンプレートの変数](https://cloud.google.com/monitoring/alerts/doc-variables)を探したり、[ログベースの指標](https://cloud.google.com/logging/docs/logs-based-metrics)を見たりしたのですが、どうもログ本文を埋め込める方法ありません。
が、[ログベースの指標(メトリクス)ラベル](https://cloud.google.com/logging/docs/logs-based-metrics/labels)を使うと、何とかログ本文から、エラーの種類が識別できる情報は含めることができることが分かりました。

`[[ALERT_ERROR]] SOMETHING_HAPPENED Some bad things happened. foo:[1000] bar:[xxxxx]`
このログで言えば、 `SOMETHING_HAPPENED` の部分ですね。

## 設定方法
### 1. ログベースの指標の作成
[公式ドキュメント](https://cloud.google.com/logging/docs/logs-based-metrics#user-defined_metrics_interface)に従い、ユーザー定義指標を作成します。
この場合、 `[[ALERT_ERROR]]` と付くものを検知したいので、フィルタに `[[ALERT_ERROR]]` を入力します。

### 2. ログベースの指標のラベルにエラー種類を示すテキストを追加
1. 指標の設定で、ラベルの「アイテムの追加」をクリック
2. 名前を適当に決定(alert_typeとしています)
3. フィールド名にログの本文を示すtextPayloadを選択
4. 抽出の正規表現に `\[\[ALERT_ERROR\]\]\s+([a-zA-Z_]+)` を入力
    - `[[ALERT_ERROR]] SOMETHING_HAPPENED Some bad things happened. foo:[1000] bar:[xxxxx]` の `SOMETHING_HAPPENED` を取り出す正規表現

![gcp_alert_label.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28487/f1fd6be8-5751-c13d-4a1f-80c7b6983db5.png)

#### 補足
[ログベース指標のドキュメント](https://cloud.google.com/logging/docs/logs-based-metrics/labels)によると、ラベルにはかなりの制約があります。

> - 長さが 100 文字以下である。
> - 正規表現 [a-zA-Z][a-zA-Z0-9_]* と一致する。
> - 文字列「log」以外も含まれる。

という条件を満たす必要があります。また、

> 注: ラベルを作成するのは、指標データを分類または要約するために値を識別する必要がある場合のみです。データ フィールドに単に名前を付けるためにラベルを使用しないでください。

あるいは

> 注意: insertId フィールドや timestamp フィールドなど、個々のログエントリに固有のラベル値を抽出しないでください。このようなラベル値の場合、ポイントを 1 つしか含まない時系列がログエントリと同じ数だけ作成されてしまいます。

という注意事項があります。
自動生成されるIDなどは、通知に出したい場合でもラベル値を利用するのは避けた方が良さそうです。

### 3. アラートポリシーを作成
1. 作成したログベースの指標の三点リーダから「指標に基づいて通知を作成する」をクリック
  ![gcp_metric_notification.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28487/ab8105ad-9e8b-c253-87d5-eab7fe43a8ba.png)
2. 適切に条件を設定
3. Documentationに変数を使って指標のラベルを記述(`${metric.label.[ラベル名]}`)
  ![gcp_alert_mail.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28487/170e0a16-4f37-e9a4-4cef-1b2520f997ca.png)
4. 「SAVE」をクリック


以上で、設定は完了です。
アラートの発生条件を満たせば、通知メールの「Policy documentation」欄に、
`アラート種別: SOMETHING_HAPPEND`
のように表示されているはずです。

## 最後に
元々やりたかったことは、単にエラーログ本文を通知に載せる、ということです。
これが不可能なので、せめてエラーの種類が分かる情報だけでも載せる、という苦肉の策を取っています。
もっと素直にログ本文を載せる方法をご存知の方がいれば、教えていただけると幸いです。

もし無いようでしたら、Googleさん、何とかしてくると幸いです。
(ログを起点にするアラートにログを載せたいって至って普通のことだと思うんだけどなぁ)
Laravel パスワードリマインダーメールを日本語化してみる

# パスワードリマインダーカスタマイズ・日本語化

- **make:auth**コマンドとメールSMTP設定が済むとパスワードリマインダー機能が使える
    - デフォルトでは英語となっているため日本語化の設定が必要


## (追記)

@ShibuyaKosuke さんのコメントで`resources/lang/ja.json` の編集のみでメールでも日本語化が可能なことを教えて頂きました。こちらの方がいいと思います。

```json:ja.json
{
    "Hello!": "こんにちは",
    "Reset Password Notification": "パスワード・リセット通知",
    "You are receiving this email because we received a password reset request for your account.": "アカウントのパスワードリセットリクエストを受け取ったため、このメールを受信して\u200B\u200Bいます。",
    "Reset Password": "パスワードをリセット",
    "This password reset link will expire in :count minutes.": "このパスワードリセットリンクの有効期限は :count 分です。",
    "If you did not request a password reset, no further action is required.": "パスワードのリセットを要求しなかった場合、それ以上のアクションは不要です。"
}
```


# 送信されるメールの日本語化
- notificationのカスタマイズ
- `App\User`にカスタマイズしたnotificationメソッドを追加し、定義をオーバーライド



# notificationのカスタマイズ

```shell
$ php aritsan make:notification CustomResetPassword
```

`app/Notifications/CustomResetPassword.php`が生成される
中身を次のように変更する

```php:app/Notifications/CustomResetPassword.php
<?php

namespace App\Notifications;

use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;

class CustomResetPassword extends Notification
{
  use Queueable;

  /**
   * Create a new notification instance.
   *
   * @return void
   */

  public $token;

  public function __construct($token)
  {
    $this->token = $token;
  }

  /**
   * Get the notification's delivery channels.
   *
   * @param  mixed  $notifiable
   * @return array
   */
  public function via($notifiable)
  {
    return ['mail'];
  }

  /**
   * Get the mail representation of the notification.
   *
   * @param  mixed  $notifiable
   * @return \Illuminate\Notifications\Messages\MailMessage
   */
  public function toMail($notifiable)
  {
    return (new MailMessage)
      ->from('admin@example.com', config('app.name'))
      ->subject('パスワード再設定')
      ->line('下のボタンをクリックしてパスワードを再設定してください。')
      ->action('パスワード再設定', url(config('app.url') . route('password.reset', $this->token, false)))
      ->line('もし心当たりがない場合は、本メッセージは破棄してください。');
  }

  /**
   * Get the array representation of the notification.
   *
   * @param  mixed  $notifiable
   * @return array
   */
  public function toArray($notifiable)
  {
    return [
      //
    ];
  }
}

```

# App\Userのオーバーライド

`App\User`に次の内容を追加する

```php:App\User
// 追加する内容
use App\Notifications\CustomResetPassword;


class User extends Authenticatable
{
  use Notifiable;

  // 追加する内容
  public function sendPasswordResetNotification($token)
  {
      $this->notify(new CustomResetPassword($token));
  }
```

# (補足)送信された再設定用のリンクが開けない場合

- `.env`ファイルの**APP_URL**がうまく設定できていない場合がある
    - `APP_URL=http://192.168.0.1:3000`　などのようにつながるドメイン・ポート番号に設定する
VSCodeでPyrightとpylintからのimport警告を無くしたい

同一ワークスペース内の複数フォルダごとに、環境変数PYTHONPATHを切り替えたい（けれど切り替え方法がわからない）、という時に、解決できた方法です。

Pyrightからの「Import 'xxx' could not be resolved Pyright (reportMissingImports)」の警告や、
pylintからの「Unable to import 'xxx' pylint(import-error)」の警告が出る場合に、フォルダ毎にimport対象の検索フォルダを変えられます。

また、同一ワークスペース内の複数フォルダで、同名のimport対象が存在する場合でも、適切な構文チェックや、文字補完が効くようになります。

ワークスペースを分けたり、venvで仮想環境を使ったりせず、簡易にimportの警告をつぶしたいと思い、いろいろ調べたのですが、同じケースの話題が見つからず、試行錯誤することになったので、備忘録的に投稿します。

#フォルダ構成（例）
```
src/
├── project1/
│   ├── .env（これを追加した）
│   ├── .vscode/
│   │   └── settings.json（これを追加した）
│   ├── project_root/
│   │   └── python_src/
│   │       ├── file1.py
│   │       └── file2.py
│   └── pyrightconfig.json（これを追加した）
├── project2/
│   ├── .env（これを追加した）
│   ├── .vscode/
│   │   └── settings.json（これを追加した）
│   ├── project_root/
│   │   └── python_src/
│   │       ├── file1.py
│   │       └── file2.py
│   └── pyrightconfig.json（これを追加した）
└── server_library/
    └── python_src/
        └── file3.py
```

#Pyright用の設定
ワークスペース直下のPYTHONPATHを切り替えたいフォルダ毎に、「pyrightconfig.json」を置きます。

```json:src/project1/pyrightconfig.json
{
  "executionEnvironments": [
    {
      "root": "project_root",
      "extraPaths": [
        "project_root/python_src",
        "../server_library/python_src"
      ]
    }
  ]
}
```


#pylint用の設定
+ ワークスペース直下のPYTHONPATHを切り替えたいフォルダ毎に、VS Codeのフォルダ設定として、「python.envFile」で環境変数設定用ファイルを指定します。

```json:src/project1/.vscode/settings.json
{
    "python.envFile": "/（ここまでのパス）/src/project1/.env"
}
```

+ 環境変数設定用ファイルを置きます。

```sh:src/project1/.env
PYTHONPATH=/（ここまでのパス）/src/server_library/python_src:/（ここまでのパス）/src/project1/project_root/python_src
```

#想定条件
+ 同一ワークスペース内の複数フォルダに、同じ名前でimportしたいものが含まれている。（内容は似ているが別物）
+ 同一ワークスペース内の複数フォルダのソースから、共通でimportしたいものもあるが、PYTHONPATHは分けたい。
+ ワークスペースは分けたくない。
+ venv仮想環境は使っていない。
+ Mac上のVS Codeを利用。(2020/02/08現在の状況)
+ 複数の似ているが別のプロジェクトを、手元では一つのワークスペースで管理して、構文チェックは生かしたい。
機械学習にこだわってしまう落とし穴って?!

# まだ機械学習で消耗しているの？

という話をしてみる。
※あくまで、誤ったやり方でという意味で

# pythonでアルゴリズム使えるぜ！っていう人にありがちな状況

って、きっとこんな感じ。

 * scikit-learnで機械学習をやってみたけど、なんか精度でない！
 * やっぱり特徴量の抽出を変えないと！！
 * でも、どうやったら、、、そして闇雲に特徴量を変え続けて答えに行き着かない

※機械学習にこだわるあまり、はまってしまう落とし穴なのかなと。

# BIに立ち返るというお話
ま、データ特性をBIで見切るって、まぁそりゃそうか。

 * データをわかるのがまずは大事：特徴量抽出にBI
 * データがわかったら機械学習

でもどうすれば？っていうのを、
やっぱりマネージドでやるべき！

# つまりコンセプトは？

 * データがわかったら機械学習はできる！
   * できればイマドキな感じでカンタンにやる！

# やってみようと思ったこと

と、ここまで書いておいて、その手段はいつくかトライして書き溜めてみる

 * Quicksight ML Insightsを試してみる
   * 異常検知が簡単にできるとのこと
   * One-class SVMとかDBScanとかありがちな異常検知アルゴリズムをSageMakerでやるよりカンタンな感じで本当に出来るかを肌感覚で知っておきたい。
 * ユースケース別のサービスを知っておく
   * 分析基盤
     * Athena:クエリ
     * Redshift:データレイク
     * EMR:Hadoop/Sparkなど
   * 分析ツール
     * Quicksight:カンタンに分析
     * Elastic service:BIツールっぽいもの？
     * SageMaker:Pythonで色々かく

## 今回のスコープ外
 * 欠損データ系の話はしない
 * MLOpsみたいなエコシステムは別途確実にやったほうがいいけど、今回対象外

# 参考資料
AWSさんのサイトを参考

 * https://www.youtube.com/watch?v=gxFQWzA0yzo
 * https://www.youtube.com/watch?v=odUgK50FUW4
hexo + GitHub pages 独自ドメインでの404エラー

静的サイトジェネレータ hexo を使って、GitHub Pagesにブログをデプロイしたところ、<username>.github.io/blog/というパスで公開されたまでではよかったのだが、独自ドメインでの公開にてこずった。

アクセスすると真っ白けなので、chromeデバッグモードで表示したところ、
`Failed to load resource: the server responded with a status of 404` がたんまりでていた。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/529867/a7cea415-6d53-fee0-8eb8-fa6400641751.png)

独自ドメイン関連の設定はあっていたが、hexoの_config.ymlの以下エントリが原因だった模様。
アクセスするパスが想定外に `/blog` ディレクトリ配下のjs,cssファイルを参照しようとしていた。

```まちがい_config.yml
url: 'https://www.kanazw.com/'
root: /blog
```

```ただしい_config.yml
url: 'https://www.kanazw.com/'
root: /
```

凡ミスでした。気づくのに数時間かかったけど、なんでこんななってたんだろ。

無事開通。　https://www.kanazw.com/
Ansible 変数名が「vars」の場合の変数参照の仕方について

# ↓のように定義された変数を参照するとき

```yaml:varfile1.yml
vars:
  var1: hogehoge
```

### `vars.var1` → 不正解

```console
$ ansible -m debug -a "var=vars.var1" -e "@varfile1.yml" localhost
localhost | SUCCESS => {
    "vars.var1": "VARIABLE IS NOT DEFINED!"
}
```

### `vars.vars.var1` → 正解

```console
$ ansible -m debug -a "var=vars.vars.var1" -e "@varfile1.yml" localhost
localhost | SUCCESS => {
    "vars.vars.var1": "hogehoge"
}
```

# なんで？

調べてみた

## 環境
| environment  | version |
|:-------------|:--------|
| Python       | 3.8.0   |
| Ansible      | 2.9.3   |

## `vars`を見てみる

```console
$ ansible -m debug -a "var=vars" -e "@varfile1.yml" localhost
localhost | SUCCESS => {
    "vars": {
        "ansible_check_mode": false,
        "ansible_connection": "local",
        "ansible_dependent_role_names": [],
        "ansible_diff_mode": false,
        "ansible_facts": {},
        ...
        "playbook_dir": "/Users/answer_d/work/20200208_vars_test",
        "role_names": [],
        "vars": {
            "var1": "hogehoge"
        }
    }
}
```

- **`vars`にはそもそもAnsibleが内部的に使う色々な値が入ってる**
- 定義した変数も一緒に入ってる

## 変数名が`vars`でない場合はどうなるか

```yaml:varfile2.yml
var1: fugafuga
```

```console
$ ansible -m debug -a "var=vars" -e "@varfile2.yml" localhost
localhost | SUCCESS => {
    "vars": {
        "ansible_check_mode": false,
        ...
        "var1": "fugafuga"
    }
}
```

### この場合は`var1`でも`vars.var1`でも参照可能

```console
$ ansible -m debug -a "var=var1" -e "@varfile2.yml" localhost
localhost | SUCCESS => {
    "var1": "fugafuga"
}
```

```console
$ ansible -m debug -a "var=vars.var1" -e "@varfile2.yml" localhost
localhost | SUCCESS => {
    "vars.var1": "fugafuga"
}
```

## 結論(予想)

1. まず定義した変数は`vars.<定義名>`で参照できる
2. ↑に加えて、単に`<定義名>`だけでも参照できるようになってる
3. でも定義名に`vars`を含んでる場合、Ansibleが内部で使っている変数名と被るので2の方法が使えない

**だから`vars.vars.var1`と参照しなければいけなかった！**
【R】 正規分布のスペクトルを描く

# 1. 趣旨

ほぼ趣味。

改めて「正規分布とは」ということを考えるにあたって、色んな標準偏差の正規分布を図に描いてみるのも良かろう、ということで。

# 2. 実装

`{RColorBrewer}`というパッケージが必要です。

```R
nd <- function(SD = c(1), left, right, ave = 0, add = F, pallet.name = "Spectral"){
  library(RColorBrewer)
  if (missing(left)){
    left <- ave - max(SD) * 4
  }
  if (missing(right)) {
    right <- ave + max(SD) * 4
  }
  SD.length.before <- length(SD)
  SD <- unique(sort(SD))
  if (length(SD) < SD.length.before) {
    warning("SDのうち、重複する要素を削除しました。")
  }
  if (length(SD) > 11) {
    warning("カラーパレットの都合上、SDの要素数は最大で11です。超過分のうちより大きい要素を削除しました。")
    SD <- SD[1:11]
  }
  SD.length <- length(SD)
  SD.min <- SD[which.min(SD)]
  SD.min.max <- dnorm(ave, ave, SD.min)
  SD.min.else <- SD[-1]
  if (SD.length < 3) {
    SD.length <- 3
  }
  pallet <- brewer.pal(SD.length, pallet.name)
  col.number <- 2
  curve(dnorm(x, ave, SD.min), left, right, add = add, col = pallet[1], lwd = 2, n = 30000)
  if (add) {
  } else {
    plot.window(xlim = c(left, right), ylim = c(0, SD.min.max))
  }
  for (SD.data in SD.min.else) {
    curve(dnorm(x, ave, SD.data), left, right, add = T, col = pallet[col.number], lwd = 2, n = 30000)
    col.number <- col.number + 1
  }
}
```

# 3. 結果

平均0, 標準偏差が1～5（0.4刻み）の正規分布をプロットしてみました。

```R
> nd(SD = seq(1, 5, by = 0.4))
```

![Rplot08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/551476/26c19d61-7d87-cb72-7803-6f0528be7aa5.png)



# 4. おわりに

きれいじゃん（小並感）

Enjoy!

おしまい。
敢えてRubyで学ぶ「ゼロから作るDeep Learning」MNISTデータセット編

「ゼロから作るDeep Learning」の72p「3.6 手書き数字認識」では、MNISTの文字のデータセットを取ってくる。読み込ませる独自のpythonのコードがモリモリ出てきて、Rubyで簡単にコード変換できない。そこで外部Gemを頼りつつ、MNISTのデータを読み込ませてみた。

# [Red Datasets](https://github.com/red-data-tools/red-datasets) を使う

Rubyから、機械学習の定番データセットを作ってくれるGem。MNISTの手書き数字も大量に入っている。

### install
```
$ gem install red-datasets
```

ついでに画像処理用のmini_magickも入れておく

```
$ gem install mini_magick
```

### 中身を確認

```ruby
require 'numo/narray'
require 'numo/gnuplot'
require 'datasets'
require 'mini_magick'

minst = Datasets::MNIST.new

# 1つ表示
pixels = minst.first.pixels
pixels = Numo::NArray.concatenate(pixels)
pixels = pixels.reshape(28,28)
str = Numo::UInt8.cast(pixels).to_string
img = MiniMagick::Image.import_pixels(str, 28, 28, 8, 'gray', 'png')
img.write('output.png')
```

### 出力結果
![output.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/293892/3affede2-9e4d-9b50-8c42-a390f7464d0c.png)

### ついでに200個ならべて表示してみる

```ruby
x = minst.take(200)
pixels = Numo::NArray.concatenate(Array.new(200) { |i| x[i].pixels })
pixels = pixels.reshape(10, 20, 28, 28).transpose(0, 2, 1, 3)
str = Numo::UInt8.cast(pixels).to_string

img = MiniMagick::Image.import_pixels(str, 560, 280, 8, 'gray', 'png')
img.write('output.png')
```
![output.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/293892/4faf9539-682d-38ab-40f5-523b11dcf39b.png)

### 訓練データとテスト画像の生成

```ruby
mnist_train = Datasets::MNIST.new(type: :train)
mnist_test = Datasets::MNIST.new(type: :test)
```
PHP 上級試験範囲 PDFまとめ

# PDFライブラリ

`HaruPDF`は、オープンソースの外部ライブラリであるlibHaruを使用してPDFファイルを作成する。
`PDFlib`は、商用の外部ライブラリであるPDFlibを使用してPDFファイルを作成する。
`MPFPDF`と`TCPDF`はPDFを作成するクラスライブラリで、スクリプト中にインクルードするだけで使用できる。
この4つのPDFライブラリの中でTCPDFのみ、HTMLをもとにPDFを作成することができる。

# MPFPDF

## MPFPDFの簡単なPDFドキュメントの作成

**MPFPDFライブラリ**を使用して書いた簡単なPDFドキュメントのコード

```php:php
// MBFPDFライブラリの読み込み
require("../fpdf/mbfpdf.php");

// MBFPDFクラスのインスタンスを生成
$pdf = new MBFPDF();

// マルチバイト対応のフォントを追加登録
$pdf->addMBFont(GOTHIC, 'SJIS');

// ページを追加
$pdf->addPage();

// 出力するフォントを設定
$pdf->setFont(GOTHIC, 'B', 16);

// テキストをドキュメント上に配置
$pdf->cell(40, 10, "Hello!");

//　内容をブラウザに送信
$pdf->output();
```

## MBFPDFのPDFドキュメント作成に使用する座標系について

MBFPDFライブラリを使用したPDFドキュメントの作成は、原点(0,0)から作業が開始される。
PDFドキュメントの原点は、ページ中央ではなくそのページの左上の角。

`cell()`や`ln()`を使用して、指定した量だけ改行したりすることで、座標を移動することができる。
その際、座標を表す単位には、以下のいずれかを指定できる。

* インチ
* ポイント
* ミリメートル
* センチメートル

座標を表す単位は`MBFPDF()`コンストラクタメソッドのオプションに指定する。

## MBFPDFでのcell()とln()

### cell()

`cell()`は、ページ上にセル（特定の範囲を表す短形のこと）を描画することができる。
以下、できること。

* 幅や高さ、枠線を指定することができる。
* テキストを書き込むことができる。
* 描画後の改行を指定することができる。

### ln()

`ln()`は、改行を行う。

以下、できること。

* 座標位置を改行することができる。引数に高さを指定することで指定した量だけ下方向に移動することができる。

* `ln`では、改行を行うためx座標は左マージンの位置(x座標が0)になる。

## MBFPDFのコンストラクタについて

`MBFPDF()`コンストラクタメソッドで指定できるパラメータは以下の3つである。

* 用紙の方向
* 座標の単位
* ページのサイズ

## MBFPDFライブラリで使用できるフォントの属性について

`setFont()`では、フォントの種類とスタイルとサイズを指定することができる。
以下の3種類。

* 太字（B）
* 下線（U）
* 斜体（I）

## MBFPDFライブラリで使用できるフォントやテキストについて

MBFPDFライブラリでは、フォント定義ファイルがあれば、任意のフォントを含めることができる。
フォントを追加するには`addFont()`を使用する。
テキストの色を変えるには`setTextColor()`を使用する。テキストの色の指定は、赤、緑、青の3原色をそれぞれ数値で指定するが、2番目、3番目を省略することができる。

## MBFPDFライブラリを使用してPDFを作成する際のヘッダやフッタについて

ヘッダやフッタは、MBFPDFクラスを継承したクラスを作成し、`header()`や`fooder()`をオーバーライドして作成する。
なお、親クラスではheader()やfooter()は、空のメソッドとして定義されている。また、header()やfooter()は、ページ出力を行うoutput()呼び出し時に自動的に呼ばれるため、特に意識する必要はない。
現在のページ番号は`pageNow()`で参照することができる。
一方、総ページ数については、`aliasNbPages()`を使用することで、`{nb}`という文字列が総ページ数に置換される。
{nb}という文字列はaliasNbPages()メソッドを呼び出すことで総ページ数に置換されるため、いつでも総ページ数に置換さっれるわけではない。

## MBFPDFライブラリを使用してPDFを作成する際の画像の追加

MBFPDFライブラリで画像ファイルを追加するには、使いたい画像ファイルを用意し、`image()`メソッドを呼び出すことで、PDFに画像を配置できる。

## MBFPFライブラリを使用してPDFを作成する際の内部リンク

MBFPDFライブラリを使用して内部リンクを作成するには、まず`addLink()`を呼び出してリンク識別子を取得する。取得したリンク識別子は、リンクの開始位置とリンクの移動先を指定する際に使用する。
リンクの開始位置は、リンク識別子を`write()`や`image()`、`cell()`メソッドの引数のリンク部分に指定することで作成する。作成したPDFではクリックできる部分となる。
一方、リンクの移動先は`setLink()`をリンク識別子を指定して呼び出すことにより決定する。移動したいリンク先のページの出力部分でsetLink()を呼び出すことにより、内部リンク先のページが決定する。

## MBFPFライブラリを使用してPDFを作成する際の外部リンク
MBFPDFライブラリを使用して外部URLへのリンクを作成するには、`write()`や`image()`、`cell()`の引数のリンク先の外部URLを文字列として渡す。内部リンクのように識別子は必要ない。

# 参照

徹底攻略 PHP5 技術者認定 [上級] 試験問題集 [PJ0-200]対応
Githubの非アクティブユーザーからユーザー名を奪う方法

Githubのユーザー名はユニークで、既に使われているユーザー名は使用することができません
Github登録時、「uji」という名前が既に使われており、泣く泣く「ujiprog」という名前にしたという経緯があります

しかし、[GitHub Username Policy](https://help.github.com/en/github/site-policy/github-username-policy)によると、

- ユーザー名は非アクティブで保持されることは無い
- 非アクティブユーザーのユーザー名はGithubの裁量で変更、削除できる

との記載があり、既に取得されているユーザー名でもまだ取得できるチャンスがありそうです

ユーザー名「uji」のアカウントを見てみると、5年前にアカウント作成されたっきりで、一度もコントリビュートしていない状態でした

# 奪ってみる

## Github supportにメールを送ってみる
「uji」は非アクティブユーザーである可能性が高いので、Githubに報告してみます
`support@github.com` に英語で

「**ujiprogって名前で活動してるんだけどujiに変えたいよ。非アクティブユーザーが持ってるから解放してくれない？**」

といった内容のメールを送りました
すると、

「**フォームあるからそっちで投げて https://support.github.com/contact**」

と返信がありました

# フォームでリクエストを送る
メールと全く同じ内容を[フォーム](https://support.github.com/contact)に入力し、送信しました
フォームを送信すると、すぐに登録しているメールアドレスにメールが届きました

「**メッセージ受け取ったよ。チケットID xxxに内容を登録したよ。早めに連絡するね。**」

ユーザーからのリクエストごとにチケットがあって、フォームから投げることで自動で登録されるワークフローみたいですね

# 待つ
タスクとして登録されたっぽいので対応を待ちます
1日経たないうちにGithubからメールが来ました

「**ユーザー名「uji」解放したよ。他の人に取られる前に早めに変えてね**」

早い。

| チケット発行 | 2020 2/4 21:57 |
|:-----------------|------------------:|
| **ユーザー名報告** | **2020 2/5 20:24** |
(日本時間)

# ユーザー名を変更する
早速ユーザー名変更してみます。
Setting → Account → Change username

変更できました。感動。

<img width="309" alt="スクリーンショット 2020-02-09 0.00.32.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/402968/6c7e0ec3-28dc-62f1-c9ad-363c3c88f374.png">

プロフィールのURLも変わりました
https://github.com/uji

# SNS等の情報更新
プロフィールやリポジトリのURLが変わるので発信している情報で更新できるものは更新するのをお忘れなく

# まとめ
使いたいユーザー名を持っているアカウントが非アクティブであれば、Github Supportに報告して名前を解放してもらうことができました
ユーザー名はOSS活動において割と重要な要素になっていると思うので、OSSのモチベを上げたい方には是非良い名前を使ってもらいたいなと思います
使いたい名前が使われている場合は是非、本記事の手順をチャレンジしてみてください

##### 参考
> [GitHub のユーザー名を既に取得されているものに変える方法](https://qiita.com/delphinus/items/7ebba704b3bad4603e81)
@delphinus さん
JavaFXのWebViewで絵文字が出ない問題への対処

JavaFXのWebViewに文字列を出そうとした時に大抵の日本語は出るのだが、絵文字&他にも一部の文字(𠮷とか 𩸽とか)が出ない問題が起きた。

Javaの内部ではUTF-16で扱っていて、UTF-8にする時にサロゲートペアで扱われている文字がおかしくなるようだ。

HTMLには数値文字参照という文字のコードポイントを直接指定して文字を出す機能がある。
(書き方は```&#x{16進};``` または ```&#{10進};```)

例えば HTML上に```&#x1F4AF;``` とかくと💯になる。

この機能を使い、以下のコードでHTMLに出力される文字列が数値文字参照になるようにして解決した。

```java
    String toCharacterReference(String str) {
        int len = str.length();
        int[] codePointArray = new int[str.codePointCount(0, len)];

        for (int i = 0, num = 0; i < len; i = str.offsetByCodePoints(i, 1)) {
            codePointArray[num] = str.codePointAt(i);
            num += 1;
        }

        StringBuffer stringBuffer = new StringBuffer();
        for (int value : codePointArray) stringBuffer.append("&#x" + (Integer.toHexString(value)) + ";");
        return stringBuffer.toString();
    }
```
システムマージを安全に効率よく実施するには

Gitを用いて構成管理をしている、システムAから一部機能を亜種サイトであるシステムBにマージする方法です。
GitクライアントであるTortoise Gitを用いて、効率よく行います。

## やりたいこと
システムAに追加開発された機能のうち、一部機能をシステムAをベースに作られた亜種サイトシステムBにマージする。
ここで、画面や一部機能はシステムB独自のものであるため、システムAとシステムBの単純なファイル比較では、不要な機能の取り込みや必要機能の削除などの危険性があるため、不可。

以下の図でいうと、追加機能D,E,Fのうち、Eのみを適用するような場合
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/356958/be4501f4-6ae6-04dd-073c-542068d3de8c.png)



## 環境
- OS（ローカル）:Windows10
- 構成管理ツール：Git
- Gitクライアント：Tortoise Git（[コチラ](https://tortoisegit.org/download/)よりDLできます）


## 手順
システムA（マージ元）のmasterブランチを参照して実施します。
以下の手順では、test_branch2の変更のみシステム（マージ先）Bのmasterブランチに取り込むこととします。

1.システムAとシステムBをそれぞれローカル環境にcloneします。

2.システムAのmasterブランチでpullして最新の状態にします。
Tortoise Gitを用いる場合は以下の通り。

①対象フォルダ上で右クリック > Git Sync
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/356958/46277f82-e66e-f4b0-ab01-767f10e0283d.png)
②pullをクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/356958/3aa52572-3aa6-e4a5-7a19-06cbc16849d3.png)


3.Tortoise Gitで差分を比較します。

①対象フォルダ上で右クリック > Tortoise Git > Show log
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/356958/a56561a5-bd0f-6c89-c3d1-693a9f790e64.png)
②Ctrlキーを押しながら、取り込みたい機能（ブランチ）の最初のコミットの直前のコミットと、最後のコミットを選択し、Compare revisionsをクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/356958/182b843b-07b6-5ba7-aa42-2075061e7d93.png)


③変更のあったファイルが表示されるので、うちひとつをクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/356958/314c15c6-3236-d794-4871-b6e1ea93a1ec.png)
④差分が着色されて表示されるので、その部分をコピーし、システムBの対象ファイルに取り込みます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/356958/3f557fb6-17bb-9804-a32d-6b70a540b9fe.png)
⑤③で表示されたファイル全てに対し、④の作業を実施します。

## できればやっておくといいこと
主にシステムA側の開発時にやっておくとシステムBへのマージがより安全かつラクになる方法です。

### 1. コミットメッセージにチケット番号を入れる
コミットメッセージの内容や作業実施者の名前から、だいたいどの機能（ブランチ）に関するコミットか判別できるかと思います。
しかし、間違って別のブランチの内容を取り込む危険性もなきにしもあらず。
なので、コミットメッセージの冒頭などに、チケット番号を入れておくとより安全性が増してベターです。

### 2. マージ時にSquashする
Squashすることにより、複数のコミットを1つにまとめることができ、システムマージをスムーズに進められます。
コミット毎にどんな変更を行ったかがわからなくなるので、そもそもNGな現場もあるかとは思いますが、問題ないようであれば実施しておくと、システムマージをかなり楽に行えるかと。

## 終わりに
初期の頃は取り込み機能（ブランチ）に関する変更をコミット毎に取り込むという非効率な方法でやっていましたが、上記の手順にしたことで、安全性と効率がよくなり、負担も減りました。

担当外になったので、なかなか機会ないですが、今後できれば自動化の方法も考えてみたいところ。
実現できたらその内容についても書きたいと思います。
Pandas クレンジング ラベル付き学習データの分割

#やったこと
0から9のラベル付き学習データを任意のラベルで分割する関数を作成しました。
今回処理するテキストのサンプルを示します。

```sample.txt
data1	0
data2	1
data3	2
data4	3
data5	4
data6	5
data7	6
data8	7
data9	8
data10	9
```
データとそのラベルが後ろについています。特にheader等もありません。
今回は特定のラベルを複数選択し、sample.txtを分割します。
以下、ソースコードです。


```conditional_list_split.py
# -*- coding: utf-8 -*-
import glob
import os
import argparse
from PIL import Image
import cv2
import matplotlib.pyplot as plt
import numpy
import re
import pandas as pd


def conditional_list_split(lbl, path):
    lbls = lbl.split(',')
    df = pd.read_table(path, sep=' ', encoding='utf-8',header=None)

    df.columns = ['data', 'label']
    df_slct = df.where(df['label'].isin(lbls)).dropna(how='all')
    df_no_slct = df.where(~df['label'].isin(lbls)).dropna(how='all')

    df_slct['label'] = df_slct['label'].astype('int64')
    df_no_slct['label'] = df_no_slct['label'].astype('int64')
    dirname = os.path.dirname(path)
    slct_path = dirname + '\\' + os.path.splitext(os.path.basename(path))[0] + '_target.txt'
    no_slct_path = dirname + '\\' + os.path.splitext(os.path.basename(path))[0] + '_no_target.txt'
    df_slct.to_csv(slct_path, header=False, index=False, sep=' ', encoding='utf-8')
    df_no_slct.to_csv(no_slct_path, header=False, index=False, sep=' ', encoding='utf-8')


def main():
    parser = argparse.ArgumentParser(description='conditional_list_split')
    parser.add_argument('--conditions', required=True, default=None)
    parser.add_argument('--datas', help='Path to label list file')
    args = parser.parse_args()
    labels = args.conditions
    conditional_list_split(labels, args.datas)


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

#使い方
```コマンドライン.txt
python conditional_list_split.py --conditions 0,1,2 --datas sample.txt
```

#実行結果例

```sample_target.txt
data1	0
data2	1
data3	2
```

```sample_no_target.txt
data4	3
data5	4
data6	5
data7	6
data8	7
data9	8
data10	9
```
お絵かきできるSNSを作りたい！3

お久しぶりです。

前回canvasを使って線を引くことが出来ました。
今回はマウスかペンを使って線を引けるところまで作ります。

基本的にcanvasの上でマウスまたは指を押した座標と動かしている時の座標と離すまでの座標を１ストロークごと書きこめば完成です。

指の場合はtouchstart→touchmove→touchend、
PCの場合はmousedown→mousemove→onMouseUp
のイベントを追加し、それぞれの関数で処理を書きます。

```JavaScript:
var can;
can=document.getElementById("canvas");
can.addEventListener("touchstart",onDown,false);
can.addEventListener("touchmove",onMove,false);
can.addEventListener("touchend",onUp,false);
can.addEventListener("mousedown",onMouseDown,false);
can.addEventListener("mousemove",onMouseMove,false);
can.addEventListener("mouseup",onMouseUp,false);
```

それぞれの関数は以下の通りにしました。

```JavaScript:
function onDown(event){
	mf=true;
	ox=event.touches[0].pageX-event.target.getBoundingClientRect().left-scx();
	oy=event.touches[0].pageY-event.target.getBoundingClientRect().top -scy();
	event.stopPropagation();
}

function onMouseDown(event){
	mf=true;
	ox=event.clientX-event.target.getBoundingClientRect().left;
	oy=event.clientY-event.target.getBoundingClientRect().top ;
}

function onMove(event){
	if(mf){
		x=event.touches[0].pageX-event.target.getBoundingClientRect().left-scx();
		y=event.touches[0].pageY-event.target.getBoundingClientRect().top -scy();
		drawLine();
		ox=x;
		oy=y;
		event.preventDefault();
		event.stopPropagation();
	}
}

function onMouseMove(event){
	if(mf){
		x=event.clientX-event.target.getBoundingClientRect().left;
		y=event.clientY-event.target.getBoundingClientRect().top;
		drawLine();
		ox=x;
		oy=y;
	}
}

function onUp(event){
	mf=false;
	event.stopPropagation();
}

function onMouseUp(event){
	mf=false;
}

function drawLine(){
	var ct;
	ct=can.getContext("2d");
	ct.strokeStyle="#000000";
	ct.lineWidth=1;
	ct.lineJoin="round";
	ct.lineCap="round";
	ct.beginPath();
	ct.moveTo(ox,oy);
	ct.lineTo(x,y);
	ct.stroke();
}

function scx(){return document.documentElement.scrollLeft || document.body.scrollLeft;}
function scy(){return document.documentElement.scrollTop  || document.body.scrollTop ;}
```

これできっと動くはず！

次：[お絵かきできるSNSを作りたい！4](https://qiita.com/cardcapt/items/a00c4854ae8713c0c4cd)
最初：[お絵かきできるSNSを作りたい！](https://qiita.com/cardcapt/items/d15fedf80d336f6b4bb9)
Codeforces Round #617 C. Yet Another Walking Robot

# 概要
- 2次元平面があり、$L,R,U,D$に動くロボットがあり、命令が与えられる。この命令セットは200,000以下。
- この命令から目的地を変えずに、連続したlからrまでの文字x個を1か所取り除くことが可能か判定せよ。

# アプローチ
今回の問題では、唯一の連続した文字列を除去できるというのがポイント。これが、複数個所の文字列を除去できるとアプローチが異なるが、今回の場合は、すでに訪れた点にもう一度再来した時、そこの区間は削ってよい。たとえば、下の例では、青いところを削れば4文字、みどりの部分を削れば10文字削れる。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/30938/c3727d64-d7a4-88e4-4ac2-6c53ddc43330.png" width=50%>

# 実装: 履歴の管理
平面の訪問済み座標を計算する際、グラフなどではよくvisited[y][x] = True/Falseが用いられるが、今回の場合、最大で各方向に200,000の移動をおこなうためx,yは200,000となってしまい、大きすぎる。
このため、hashを用いて、

```Python
visited[y][x] = distance
を
visited[x * 200000 + y] = distance
```

として表現する。各、visitedには訪れた最後の時間を示しておくことで`1.引き算することで経過時間の算出(一番短い区間を選ぶ)`と`2.答えになるlの文字番号`を知ることができる。

```Python
q = int(input())
for _ in range(q):
    n = int(input())
    s = input()
    dat = dict()
    x, y,t = 0, 0,0
    dat[x*200000 + y] = 0
    l,r,d = -1,-1,99999999999
    for i in range(n):
        t += 1
        if s[i] == "L":
            x -= 1
        elif s[i] == "R":
            x += 1
        elif s[i] == "U":
            y += 1
        elif s[i] == "D":
            y -= 1
        #print("step t={0} x={1} y={2}".format(t, x, y))
        if (x * 200000 + y) in dat:
            cd = i - dat[x * 200000 + y]
            if cd < d:
                l = dat[x * 200000 + y]
                r = t
                d = cd
        dat[x * 200000 + y] = t
    if l == -1:
        print(-1)
    else:
        print("{0} {1}".format(l + 1,r))
```
https://codeforces.com/contest/1296/submission/70275910
フロントエンド開発時に確認すべき５つの挙動

# 0. はじめに
　フロントエンド開発をしているとユーザーの予期せぬ行動で、予期せぬ挙動やバグを生み出してしまいます。しかし、フロントエンド開発の経験が浅いと、想定できるユーザーの行動も限られてきます。そこで、本記事では、確認しておくべきユーザーの行動について記載します。[^1]
[^1]: 今後、確認すべき項目が見つかり次第、順次追加していきます。

# 1. 前のページからの遷移
　前のページからの遷移を考えないことはまずないと思いますが、実装しなければいけないページにたどり着くまでの遷移が１パターンとは限りません。複数のURLから遷移してくることもあるので、どこからきてもエラーとならないように実装する必要があります。
# 2. 次のページからの戻りの遷移（リンクまたはボタン）
　前のページからの遷移が終われば、ページ遷移後の挙動の確認です。経験が浅い開発者で意外と確認し忘れることが多い挙動だと思います。その中でも、ページ上に意図的に設置されているリンクやボタンをクリックした時の戻りの確認がこの項目です。リンクやボタンの名称は「修正する」だったり、「戻る」だったり、プロダクトオーナーやデザイナーの意向によって異なる場合もあります。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121304/6a4247de-4b35-cd33-6cec-4fce7d7780fc.png" width="250pt">
　このとき確認すべき事項としては

 1) 入力した値が保持されたままになっているべきか、入力した値は空に戻すのか
 2) 戻る挙動によって、サーバーサイドから渡されるべき値が渡ってきておらず、エラーとなっていないか
 3) 入力した値が保持されている場合は送信ボタンがdisabledに戻ってないか（保持されない場合は送信ボタンがdisabledになっているか）

などです。リンクまたはボタンでの遷移の場合は、実装方法によって、

 - getで戻る場合
 - postで戻る場合
 - JavaScriptを利用してブラウザのヒストリーバックで戻る場合

などが考えられ、どの実装になっているか、決まっていない場合は、どの実装にするかを決める必要があります。

# 3. 次のページからの戻りの遷移（ブラウザバック）
　ページ遷移後の戻りの二つ目はブラウザの戻るボタン（最近は`←`や`<`の表記の場合が多い）を利用した場合です。これは、2のヒストリーバックと同じ挙動になりますが、この場合も、1)や2)の確認を行います。

# 4. 次のページからの戻りの遷移（サーバーサイドバリデーションによるもの）
　ユーザーの意図的な遷移ではなく、サーバーサイドのバリデーションに引っかかって戻ってくる場合も考えられます。ページ遷移でサーバーサイドにリクエストを送る実装になっている場合は、バリデーションエラーで戻ってくる挙動となっている場合があります。フロントエンドのバリデーションだけで弾くことができず、サーバーサイドのバリデーションでしかチェックできない場合などです。
　この時、多くの場合、エラーとなった項目やエラーメッセージがサーバーサイドから送られてきます。この時にフロントではどのような挙動となるかを定義し、実装する必要があります。例えば、エラー対象となるフィールドに色を付けてわかりやすくしたりし、送信ボタンはdisabledにします。さらに、内容が修正された場合には色を戻して送信ボタンのdisabledを解除するといった挙動が考えられます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121304/662aca6e-1e55-ac76-0d00-50ff5e6b8844.png" width="450pt">

# 5. ブラウザのリロード
　最後に忘れがちなのがブラウザのリロードです。リロードの場合も1) 入力した値が保持されたままになっているべきかなどを考慮する必要があります。

　以上、フロントエンド開発時に確認すべきユーザーの５つの挙動についてご紹介しました。これらは、私がチームで開発している時に新規参画者のコードレビューをしている際に指摘してきたことでもあります。最初のうちは、目的とする挙動を実装するのに精一杯のことが多いですが、慣れてきたらこのような細かい点も考慮に入れるようにしていきたいという、自戒も込めて記述させていただきました。
AWS Amplify のサインイン時の項目を変更してみた

#はじめに

このページでは Amplify のサインイン画面の初期項目を変更するための方法を Document を見ながら実装しました。

日本語化対応からの修正内容は[こちら](https://github.com/rararamorio/aws-amplify-example/commit/454555c73d03246ba70c7d449745499a19de0361#diff-25d902c24283ab8cfbac54dfa101ad31)

#開発環境
* Windows 10
* nodist(nodeのバージョン管理)
    * node 12.13.0
* VSCode

# Amptify のサインイン画面を変更したい！

## 前提として

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/532289/45245cf5-3088-1e4b-6c43-39faccb7c19e.png)

デフォルトで認証機能を作成すると、上記画像の項目が用意されます。
個人的には Cognito で電話番号は管理させたくないと思ったので…この電話番号の項目を設定出来なくすることは可能か？と思ったのが事の発端でした。

カスタマイズする方法がないか調べているとリファレンスが見つかりました。

https://aws-amplify.github.io/docs/js/vue

内容を読む限り、任意の場所に用意した画面を描画できるタグがあるらしく、オプションを設定すると色々修正できるらしいです:open_mouth:

ということで、今まで日本語化まで地道に行ってきたプロジェクトに手を入れていきます。

## 認証画面のカスタマイズ用のページを作成する

``` src/views/SampleAuth.vue 
<template>
  <amplify-authenticator v-bind:authConfig="authConfig"></amplify-authenticator>
</template>
<script>
export default {
  name: "sampleAuth",
  data() {
    return {
      authConfig: {
        signUpConfig: {
          hiddenDefaults: ["phone_number"]
        }
      }
    };
  }
};
</script>
```

今回項目の変更を行いたいのでカスタマイズページを用意します。
リファレンスによると amplify-authenticator タグを書くだけで、認証機能一通りの機能を用意してくれるらしいです。
こちらを用いると、authConfig 内で各ページに対してのプロパティを設定することが出来るようです。
今回は、サインアップするときの設定値を修正したいので、signUpConfig に非表示にする設定を追加しました。

## ルートを変更する

``` （before）src/router/index.js
  {
    path: "/auth",
    name: "Authenticator",
    component: components.Authenticator
  }
```
元々のソースコードは component に 「components.Authenticator」と書くことで、デフォルトの認証画面を描画指定ました。
今回はカスタムページを用意したので、こちらを呼び出すように修正します。

``` （after）src/router/index.js
import SampleAuth from "../views/SampleAuth";
// ・・・（中略）・・・
  {
    path: "/auth",
    name: "sampleAuth",
    component: SampleAuth
  }
```

## 画面を描画してみる

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/532289/a8ca348e-37ca-9f3c-dc43-18178037f1b1.png)

サーバを立ち上げて確認してみると、電話番号の項目が消えていました。
ユーザ登録も出来ることが確認できたので hiddenDefaults で非表示にするだけで問題なさそうです。
簡単に項目が出来たので驚きました。

## 番外編（ログインしたならログアウトしたい）

``` src/components/HelloWorld.vue
<amplify-sign-out></amplify-sign-out>
```
試しに上記タグをページに追加してみました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/532289/1e25cda9-c421-8d15-e3cb-94a3df875156.png)

サインアウトボタンも表示され、機能自体も使えるようになっていました。（超簡単！）

# 終わりに

今までのソースを見返すだけでは、修正可能かどうかわからなかったけれど、調べてみるとリファレンスがすごくしっかりしていて、難なく調整が出来ました。

英語が本当に苦手なんですが、まずはリファレンスを見ることを実践していきたいと思いました。