OmniAICreator/Qiita-1.07M huggingface dataset:
「地名の最後の一文字だけで地図をつくると地形が見える :: デイリーポータルZ」 を mapbox-gl-js と地理院地図Vector で

# はじめに

前の記事 [「地名の最後の一文字だけで地図をつくると地形が見える :: デイリーポータルZ」を Leaflet と 地理院ベクトルタイルで](https://qiita.com/frogcat/items/d0579fbe1d7c375842b0) では、[地名の最後の一文字だけで地図をつくると地形が見える :: デイリーポータルZ](https://dailyportalz.jp/kiji/saigo-no-hitomoji-chizu) を Leaflet と地理院ベクトルタイルで実装してみました。

この記事では [mapbox-gl-js v1.10.0](https://github.com/mapbox/mapbox-gl-js/blob/main/CHANGELOG.md#1100) で新規に追加された [index-of](https://docs.mapbox.com/mapbox-gl-js/style-spec/expressions/#index-of) と [slice](https://docs.mapbox.com/mapbox-gl-js/style-spec/expressions/#slice) を使うことで、 [mapbox-gl-js](https://docs.mapbox.com/mapbox-gl-js/api/) と[地理院地図 Vector](https://github.com/gsi-cyberjapan/gsimaps-vector-experiment) で実装してみましょう。

# できあがり

<https://frogcat.github.io/saigo-no-hitomoji-chizu/mapbox-gl-js.html>

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82494/9ee95c97-7bfd-17d6-ed8f-4d71dde61a60.png)


- z<13 では表示されません
- 13<=z<14 では代表的な注記だけが表示されます
- 14<=z では特に間引きなどなく注記が表示されます


ソースコードはこちらに置いてますので fork して改造してみてください。

- <https://github.com/frogcat/saigo-no-hitomoji-chizu/blob/master/mapbox-gl-js.html>


# 解説

地名の最後の一文字だけを抽出して表示するにはどうするか、というのが唯一の課題です。
また、**霞が関二丁目** のような地名は地理院 Vector のデータでは **霞が関（二）** のように表現されていことにも注意が必要です。
このケースではもちろん **二** ではなくて **関** が抽出されるべきです。

ここでは冒頭述べたとおり、 `index-of` と `slice` を使って実現しています。

以下が「一文字だけ表示するレイヤー」のスタイル定義です。

```json

{
  "id": "bvmap-layer",
  "type": "symbol",
  "source": "bvmap",
  "source-layer": "label",
  "minzoom": 13,
  "maxzoom": 18,
  "filter": [
    "all",
    ["==", "ftCode", 100],
    ["==", "annoCtg", 210]
  ],
  "layout": {
    "text-size": 32,
    "text-field": [
      "let",
      "s", [
        "case", ["==", ["index-of", "（", ["get", "knj"]], -1],
        ["get", "knj"],
        ["slice", ["get", "knj"], 0, ["index-of", "（", ["get", "knj"]]]
      ],
      [
        "slice",
        ["var", "s"],
        ["-", ["length", ["var", "s"]], 1]
      ]
    ],
    "text-font": [
      "NotoSansCJKjp-Regular"
    ]
  },
  "paint": {
    "text-color": "rgba(0,0,0,1)",
    "text-halo-color": "rgba(255,255,255,1)",
    "text-halo-width": 3
  }
}
```

- `id` から `source` あたりは <https://github.com/frogcat/gsimaps-vector-experiment-simplified> で作成したものを流用しました
- `layout` では文字サイズ、フォント、実際に表示するテキストを指定しています
- `paint` では文字色、縁取りの色・幅を指定しています

今回問題になるのは `text-field` の中身です。基本的には仕様を見て根性で書いたのですが、コメントを残しておきます。

```js
"text-field": [
   // 変数定義の開始宣言
   "let",
   // 変数 s に値を設定する
   "s", [
     // knj プロパティに「（」が含まれているかどうかによって分岐
     "case", ["==", ["index-of", "（", ["get", "knj"]], -1],
     // 含まれていない場合は knj をそのまま返す
     ["get", "knj"],
     // 含まれている場合は 「（」の前の部分を返す
     ["slice", ["get", "knj"], 0, ["index-of", "（", ["get", "knj"]]]
   ],
   // 変数定義終了、以下で text-field に返すべき値を生成する
   [
     // 部分文字列生成の宣言
     "slice",
     // 変数 s に対して操作を行う
     ["var", "s"],
     // 部分文字列の先頭インデックスを指定する（この場合は変数 s の長さから 1 を引いたもの）
     ["-", ["length", ["var", "s"]], 1]
   ]
]
```

# まとめ

従来 mapbox-gl-js では文字列操作が難しく、せいぜい `concat` くらいしかできなかったのですが、 v1.10.0 から `index-of` や `slice` が使えるようになりました。
お世辞にも使いやすいとは言えませんが、表現の幅が広がるのではないかと思います。
【AWS EC2】Amazon Linux2でやっておきたい設定

# 概要
EC2を起動したらまずはやっておきたい設定周り

# 環境
- AWS EC2
 - OS: Amazon Linux 2
 - AMI ID: amzn2-ami-hvm-2.0.20200520.1-x86_64-gp2
 - セキュリティグループ: SSH 22 を開けておく

# 1. サーバーの時刻をJSTにする
- デフォルトはUTCになっている
- timezoneをAsia/Tokyoに変更してJSTにしておく
- timezoneを変更したらcrondも再起動しておく

```
$ timedatectl status
      Local time: Fri 2020-06-26 06:10:18 UTC
  Universal time: Fri 2020-06-26 06:10:18 UTC
        RTC time: Fri 2020-06-26 06:10:18
       Time zone: n/a (UTC, +0000)
     NTP enabled: yes
NTP synchronized: no
 RTC in local TZ: no
      DST active: n/a
$ sudo timedatectl set-timezone Asia/Tokyo
$ timedatectl status
      Local time: Fri 2020-06-26 15:50:10 JST
  Universal time: Fri 2020-06-26 06:50:10 UTC
        RTC time: Fri 2020-06-26 06:50:09
       Time zone: Asia/Tokyo (JST, +0900)
     NTP enabled: yes
NTP synchronized: no
 RTC in local TZ: no
      DST active: n/a
$ sudo systemctl restart crond
$ date
Fri Jun 26 16:03:47 JST 2020
```

# 2. プロンプトの変更
- デフォルトだと情報が少ないので変更する
 - 変更前：`[ec2-user@ip-XX-XX-XX-XX ~]$`
 - 変更後：`[16:17:47 ec2-user@ip-XX-XX-XX-XX ~]$`
- `.bashrc`に`export PS1`を追記する
 - 全体に反映したい場合は`/etc/bashrc`の`$PS1`を編集する
- sourceコマンドで反映するのをお忘れなく

```
$ vi .bashrc

# User specific aliases and functions
export PS1='\[\033[37m\][\t \[\033[36m\]\u\[\033[37m\]@\h \[\033[32m\]\W\[\033[37m\]]$ '

$ source .bashrc
```

# 3. ロケールの変更
- やらなくても良いが、日本語環境にしたいのであればやる

```
$ localectl status
   System Locale: LANG=en_US.UTF-8
       VC Keymap: n/a
      X11 Layout: n/a
$ sudo localectl set-locale LANG=ja_JP.UTF-8
$ sudo localectl set-keymap jp106
$ localectl status
   System Locale: LANG=ja_JP.UTF-8
       VC Keymap: jp106
      X11 Layout: jp
       X11 Model: jp106
     X11 Options: terminate:ctrl_alt_bksp
```

# 4. よく使うものをインストール
以外と何も入ってないので、使うものはインストールする。
## git
```
$ sudo yum install -y git
$ git -version
git version 2.23.3
```

# 参考
- [Amazon Linux インスタンスのホスト名を変更する](https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/set-hostname.html)
【初心者向けチュートリアル】Boltを使用してUnityで簡単なゲームを作成する-目次


閲覧ありがとうございます！

こちらは、ビジュアルスクリプティングツールの「**Bolt**」と、ゲーム開発エンジンである「**Unity**」の使い方を何となく覚えたい人のためのページです。

制限時間内に落ちてくる果物を拾ってスコアを稼ぎ、ハイスコアを目指す「フルーツを拾おう！」というゲームを作りながら、各ツールの使用方法や機能、C#を使ったプログラムの基本的な組み方を紹介していきます。
![Bolt_game.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654796/402119c1-63d4-eaab-038d-4cc9b4b38b64.gif)
↑ゲームが完成するとこんな感じです。
かなりシンプルな作りなので、ツールの使い方がわかったら色々拡張してみてください！

記事の途中で不明な点ございましたら、各ページコメント欄や質問フォームにてご質問ください。
https://forms.gle/wDiTEWy9PoG3zwEr9 (Googleフォームに移動します)

また、チュートリアルを完走または途中でやめてしまった方も下記の感想アンケートにお答えいただけますと幸いです。
https://forms.gle/PA8dXoJiiazFNyex9 (Googleフォームに移動します)
※12/2追記 アンケートへのご協力誠にありがとうございます！まだまだ募集してますので、頂いたご意見をもとによりわかりやすい記事を目指して行ければと思います。

※Unityのバージョンは2019.3.1f1を使用しています。
※筆者はC#及び各ツールに関して独学ですので、不適切な記述等ありましたらご指摘いただけますと幸いです。

#目次
- [【初心者向けチュートリアル】Boltを使用してUnityで簡単なゲームを作成する-1](https://qiita.com/mma/private/ee534955ea1999cbb976)
- [【初心者向けチュートリアル】Boltを使用してUnityで簡単なゲームを作成する-2](https://qiita.com/mma/private/2943742cb6909fb22763)
- [【初心者向けチュートリアル】Boltを使用してUnityで簡単なゲームを作成する-3](https://qiita.com/mma/private/5bea756e2a023a4ca3d9)
- [【初心者向けチュートリアル】Boltを使用してUnityで簡単なゲームを作成する-4](https://qiita.com/mma/private/8526665d0bbee14dda10)
- [【初心者向けチュートリアル】Boltを使用してUnityで簡単なゲームを作成する-5](https://qiita.com/mma/private/befdeb011cf8a43843b8)
- [【初心者向けチュートリアル】Boltを使用してUnityで簡単なゲームを作成する-6](https://qiita.com/mma/private/d66383a0d098eb6b48dc)
- [【初心者向けチュートリアル】Boltを使用してUnityで簡単なゲームを作成する-7](https://qiita.com/mma/private/2ee0bfc33ce6daf74955)
- [【初心者向けチュートリアル】Boltを使用してUnityで簡単なゲームを作成する-8](https://qiita.com/mma/private/64d9349991f49f6c96ec)
- [【初心者向けチュートリアル】Boltを使用してUnityで簡単なゲームを作成する-9](https://qiita.com/mma/private/0c20c4e03f453f677119)
- [【初心者向けチュートリアル】Boltを使用してUnityで簡単なゲームを作成する-10](https://qiita.com/mma/private/b8060dcec1743177c2a7)
- [【初心者向けチュートリアル】Boltを使用してUnityで簡単なゲームを作成する-11](https://qiita.com/mma/private/ad2063bbbab047a51e10)
- [【初心者向けチュートリアル】Boltを使用してUnityで簡単なゲームを作成する-12](https://qiita.com/mma/private/d2692330873770aa0e09)

#前提知識
Q.Unityって何？
A.ゲームをめちゃくちゃ楽に作れるツールです！

Q.ビジュアルスクリプティングってなに？
図をつなぎ合わせていくことで直感的にプログラムできるシステムです！
![無題.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654796/96470298-9e0b-bd1e-67fa-519432d77f13.png)
↑こういう風にぐにゃぐにゃ繋げていきます！

Q.Boltってなに？
A.Unityで上記のビジュアルスクリプティングができるようになってプログラムをめちゃくちゃ楽に書けるツールです！
【初心者向けチュートリアル】Boltを使用してUnityで簡単なゲームを作成する-1


こちらの記事では**Unity、Boltのインストール～画面設定**までを行います。
#Unityのインストール

まず以下のUnityダウンロードページアクセスします。
https://unity3d.com/jp/get-unity/download/archive
こちらのページでUnity2019.3.1を探して、Unityエディターをクリックすると自動でDLが始まります。
![無題.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654796/9d5a3959-f2ee-b13e-4d1b-b9074d740b49.png)
DLが完了したら「UnitySetup64-2019.3.1f1」をダブルクリックして、ひたすらNextを押せばインストール完了です。

#プロジェクトの作成

インストールが終わったらUnity.exeをダブルクリックして起動します。
以下のようなウィンドウが出たら右上の「新規作成」をクリック。
![無題.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654796/d400f15f-5468-9967-ed59-31e39472c2f7.png)
テンプレートを2Dにして、好きな名前と保存場所を選択し「作成」をクリックしましょう。
![test.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654796/d7b60183-ffa2-efed-79db-88ce6bee8fa7.png)
こんな感じの画面が出たら新しいプロジェクトが作成は完了です。
今後はこのプロジェクトを使ってゲームの制作をしていきます。
一旦この画面は放っておいて、BoltのDLに移りましょう。

#Boltのインストール
以下のページからBoltを購入し、DLしたら該当するUnitypackageをダブルクリックしてインポートします。
https://assetstore.unity.com/packages/tools/visual-scripting/bolt-163802?locale=ja-JP
※7/25追記 いつの間にか無料になってました！

インポートが完了したら、先ほど作成したUnityプロジェクトの画面に戻り、上部メニューからTools>Install Bolt>Import>Importをクリックします。
インポートが完了するとこんなかんじのウィンドウが出てきますので、Nextをクリック。
![test.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654796/62b6d037-f2ec-24b2-c9d0-6aea856cb6f6.png)
プログラムを組むうえでは「関数」という命令式のようなものを使うのですが、ここではそれの名前の設定をどういった風にするか聞かれます。特にこだわりがなければ左の「Human Naming」にしましょう。
![test.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654796/f54be6b4-bdfb-05e9-63ba-ede110f18010.png)
そのあとはひたすら下までスクロールしてNext>Generate>closeをクリックします。

これでBoltのインストールは完了です。

#画面設定

このまま作業を進めるといろいろやりにくいので、画面設定をちょっといじります。

まずプロジェクトの画面上部メニューからWindow>Layouts>Boltとクリックしましょう。
自動でBoltを使用するのに適した画面のレイアウトにしてくれます。
Boltの項目がない場合は「2by3」を、その項目もない場合は手動で以下のような画面設定に調整お願いします。各ウィンドウはドラッグ＆ドロップで位置を変えられます。
![BoltGame - Game - PC, Mac & Linux Standalone - Unity 2019.3.1f1 Education _DX11_ 2020_07_27 13_37_09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654796/a18463e7-a27c-caac-efc8-670c10f20b11.png)
また、このレイアウトにすると画面左下に「Game」ウィンドウが表示されていると思います。
このGameウィンドウは実際のゲームの実行画面を移すウィンドウです。
![BoltGame - Game - PC, Mac & Linux Standalone - Unity 2019.3.1f1 Education _DX11_ 2020_07_27 13_51_30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654796/eff34d85-ceb1-0916-f213-a3de15979f91.png)
左上の「Free Aspect」からは、実行画面を表示する際の画面比率を選択することが出来ます。
今回のゲームはPCでプレイすることを想定して開発しますので、Free Aspectをクリックし「16:9」を選択しましょう。

これで画面設定は完了です。
Ctrl+Sで保存しておきましょう！

今回のチュートリアルはここまでです。お疲れさまでした！
次回は**ゲームで使用する素材の収集～配置**までをやっていきます。
[【初心者向けチュートリアル】Boltを使用してUnityで簡単なゲームを作成する-2](https://qiita.com/mma/private/2943742cb6909fb22763)
Cloud Foundry Deploy時のlocalhostとportの設定

## Cloud Foundry Deployの際のlocalhostとportの設定について

今回はexpressのアプリを例にとります。
CFへのdeployでは、環境変数のVCAP_APP_HOSTとVCAP_APP_PORTを用いてエンドポイントを形成する必要があります。
その時、ローカル環境開発を考慮に入れ、２つのパラメータが存在しない場合も対応します。

```js:server/index.js
...
const HOST = process.env.VCAP_APP_HOST || 'localhost'
const PORT = process.env.VCAP_APP_PORT || 3000
app.listen(PORT, HOST)
...
```
 
## ちなみに
VCAP_APP_HOSTとVCAP_APP_PORTを確認する方法

```console
$ cf ssh <app name>
$ env
```

以上です。
Apache2 と Tomcat の連携

Apache2.4 と Tomcat9 を連携させる方法です。

Apache2.4 と Tomcat9 が正常に動いているとします。

確認方法

Apache2.4

>```bash
sudo systemctl status apache2
```

>ブラウザーから、http://localhost にアクセス

Tomcat9

>```bash
sudo systemctl status tomcat9
```

>ブラウザーから、http://localhost:8080 にアクセス


Apache のモジュールを有効にします。

```bash
sudo a2enmod proxy
sudo a2enmod proxy_ajp
```

Tomcat の設定変更

```text:/var/lib/tomcat9/conf/server.xml
（省略）
<Connector protocol="AJP/1.3"
               port="8009"
               secretRequired="false"
               redirectPort="8443" />
（省略）
```

Tomcat の再起動

```bash
sudo systemctl restart tomcat9
```

Apache2 の設定変更

```text:/etc/apache2/apache2.conf
ProxyPass /tomcat9/ ajp://localhost:8009/
ProxyPassReverse /tomcat9/ ajp://localhost:8009/
```

Apache2 の再起動

```bash
sudo systemctl restart apache2
```

ブラウザーで、
http://localhost/tomcat9/
にアクセスします。
>![tomcat_aug16_aa.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179446/5147e551-e277-a1e5-2bdb-6578ba444ea0.png)


http://localhost:8080/
にアクセスしたのと同じ画面が表示されます。
>![tomcat_aug16_bb.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179446/a5079dad-7960-996b-239e-2e619dae9f2d.png)



次のバージョンで確認しました。

```text
$ /usr/share/tomcat9/bin/version.sh 
Using CATALINA_BASE:   /usr/share/tomcat9
Using CATALINA_HOME:   /usr/share/tomcat9
Using CATALINA_TMPDIR: /usr/share/tomcat9/temp
Using JRE_HOME:        /usr
Using CLASSPATH:       /usr/share/tomcat9/bin/bootstrap.jar:/usr/share/tomcat9/bin/tomcat-juli.jar
NOTE: Picked up JDK_JAVA_OPTIONS:  --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.io=ALL-UNNAMED --add-opens=java.rmi/sun.rmi.transport=ALL-UNNAMED
Server version: Apache Tomcat/9.0.31 (Ubuntu)
Server built:   Feb 24 2020 22:37:00 UTC
Server number:  9.0.31.0
OS Name:        Linux
OS Version:     5.4.0-39-generic
Architecture:   amd64
JVM Version:    11.0.7+10-post-Ubuntu-3ubuntu1
JVM Vendor:     Ubuntu
```

関連ページ
[RaspberryPi に Tomcat をインストール](https://qiita.com/ekzemplaro/items/b6dd39daeb027227234d)
[日本語を表示する Servlet の簡単な例](https://qiita.com/ekzemplaro/items/003a7036218a163b58f8)
ℹ NuxtJS collects completely anonymous data about usage. 

```
ℹ NuxtJS collects completely anonymous data about usage.
  This will help us improving Nuxt developer experience over the time.
  Read more on https://git.io/nuxt-telemetry

? Are you interested in participation? (Y/n)
```

なんかこういうのが出るようになった。
ちょっと鬱陶しい。

表示されるリンクを見るだけなんだけれども。
https://github.com/nuxt/telemetry#opting-out

私は `nuxt.config.js` に設定するやり方をしました。
これで出なくなった、よかったよかった。

```js
export default {
  telemetry: false
}
```

他の方法は試してないですが同様に出なくなると思います。
VSCodeのタブがめちゃくちゃ長くなるやつを解消する

いつからだったかいつの間にかこういう仕様になってたので結構フラストレーションあった。
書いてある内容の1行目がタブに表示されちゃうやつ。
<img width="873" alt="スクリーンショット 2020-06-26 15.55.14.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23195/c01f8823-168a-b861-b68c-e05abca0f2c8.png">

複数開くともうタブがどっかへ消えてしまうので、
ちょいメモしてたやつごとうっかり閉じちゃうとかザラにあり…どうにかしないと効率が悪いままだった。
<img width="1501" alt="スクリーンショット 2020-06-26 16.02.57.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23195/3518af68-4cfe-703a-26cf-a0919c55bf0d.png">

「vscode tab」で検索かけるもインデントのタブとウインドウのタブ(?)が混在して出てくるし
（これマジで我々デベロッパーは別名を策定して世の中の当たり前にすることに心血を注いだほうがいいと思う）
そもそも「タブのタイトルが1行目のテキストになる」みたいなニュアンスをどう探したらええねんと。
どこで変更出来るのか分からなくてイライラしてたけど、落ち着いて考えたら発見出来たのでメモ。
VSCodeに限らずUI的な設定ってピンポイントで設定項目探すの難しい。
こう...言葉にしづらいけどこういう感じの設定！！みたいなの多い

ユーザ設定を開いて「Workbench > Editor > Untitled > LabelFormat」
（ショートカットなら⌘+shift+Pから「user setting」と入力、titleでフィルタ）
<img width="825" alt="スクリーンショット 2020-06-26 15.58.24.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23195/e3e91b76-0d7d-af9f-28f7-a68891dd0d95.png">

値をcontent→nameへ変更します。

<img width="653" alt="スクリーンショット 2020-06-26 16.01.13.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23195/87d80e46-3c93-5d7d-f1dd-25d10a1c69c3.png">

治りました。

**おわり。**
LPIC-1(レベル1)受験記録

## 概要
先日、LPIC-1を受験し合格しましたので、記録を残します。
この記録が、これからを受験される方にとっての目安や参考になればと思います。

## 受験資格
LPIC-1(レベル1)

## 結果(点数)
- 101 : 受験1回目で不合格(480点)。2回目で合格(620点)。
- 102 : 一発合格(680点)。

(合格ラインはおそらく500点)

## プロフィール(学習前)
エンジニアとして就業して約4ヶ月。Linuxコマンドはたまに使う程度で、コマンドがなぜ動くのかわかっていない。

## 学習時間(おおよそ)
勉強開始〜合格までで2ヶ月弱。
- 101 : 40時間(1回目まで) + 40時間(2回目まで)
- 102 : 40時間

## 学習教材
- Linux教科書 LPICレベル1 (あずき本)
- Linux教科書 LPICレベル1 スピードマスター問題集 (白本)
- ping-t : 102用の有料版サービスも利用(101用は無料)

私のオススメは、下記手順です。
1. ping-tで全問を金メダルにする。(問題を解く際は、「答えの確認」よりも「解説を読むこと」が重要)
2. あずき本を読み、全体像をつかむ。(問題を解くだけでは断片的な知識であるため)
3. 白本の問題を解く。
4. 白本の模擬試験問題を解く。
5. (余裕があれば)ping-tの模擬試験問題を解く。

おそらく、1.のステップで０．５〜1ヶ月ぐらいかかるので、時間はいくらあっても足りません。
焦らずに集中して取り組み、進捗具合によっては臨機応変に手順や計画を見直したほうが良いと思います。

あずき本は、無くてもなんとかなるかと思いますが、あったほうが良いです。(当然ですが)

## 所感
- 時間が許すなら、3ヶ月程度の学習期間を設けたほうが良いです。
    - 試験に合格するためだけでなく、Linuxの理解を深めるため。
    - Linuxの知識は基本であるため、他の技術を学ぶ際に理解しやすくなる(と思います)。
- 101で1度不合格になった時はかなり落ち込みましたが、追い込まれたことで逆に火が付きました。(悔しさをバネに ってやつです。)
- 資格を取るという目標があるため学習に熱が入り、結果として深い理解ができました。
- Linuxの理解、熱心に学習した経験 の2つの側面から、非常に良い経験になったと思います。
WebRTCで撮影した動画をFastAPIにPOSTでファイルアップロードするときハマったこと

## TL;DR 解決

下記でファイルアップロードできます。

FastAPI側（受け取る側）

```
@app.post("/uploadVideo")
async def uploadVideo(video: UploadFile = File(...)): # <=ここではまった
    video_data = await video.read()
    upload_path = pathlib.Path(f'static/received_videos/{video.filename}')
    with upload_path.open(mode="wb") as f:
        f.write(video_data)
    return {"filename": video.filename}
```

Javascript側（送る側）

```
var filename = "something.webm"
f.append("video", buffer, filename); // <=ここではまった
let xhr = new XMLHttpRequest();
xhr.open('POST', window.location.origin + '/uploadVideo', true);
xhr.send(f);
```

ちなみにbufferはblobです。

## ハマったところ

- 必要なライブラリ
- FastAPIの引数名

### 必要なライブラリ

下記が必要そう

```
pip install python-multipart
```

参考：[Uploading images in FastAPI post request causes 400 Bad Request - Stackoverflow](http://www.finbit.org/questions/62429244/uploading-images-in-fastapi-post-request-causes-400-bad-request/eAE1y2EOQDAMQOETVZdlkXCbmqHBOtpxfSLx9718i1nRHlGN4ipXOqdN7ibKjkdNaixZsfXBdz4ErGUTGjnPwDvNSYEzTPTSwlBEDc70KYhU9d3BORho_PMDdF8ngA)

### FastAPIの引数名

`async def uploadVideo(video: UploadFile = File(...)):`

ここで`video`となっているところは多くの記事で`file`とかになっています。

FastAPIのPOSTで受け取る際はその引数の名前を送る側と同じにすることで受け取れるようになっているようです。

Javascriptを見るとたしかに`video`と指定して送ってますね。（無駄に自分で変えてた。。。）

実はFlaskでやって一回成功していたのですが、それがこちら。

```
from flask import request

@app.route('/uploadVideo', methods=["POST"])
def uploadVideo():
    received = request.files
    videofile = received['video']
    videofile.save('static/received_videos/' + videofile.filename)
    return str(received), 200
```

videoと指定して受け取っていますね。

## 教訓

割とどうでもいいようなところで結構な時間を使ってしまいました。

FastAPI初めてだったのでどこが違うか色々変な方向に疑って結果回り道してました。

今後はひとつひとつ成功したところを確認すること。
Power BIでDataDirect ODBC driversを使ってダイレクトクエリを実行する

# はじめに
デフォルトのPower BIではどんなODBCドライバーでも、一部のSalesforceのようなデータソースでもダイレクトクエリを実行することはできません。データビジュアライゼーションを始める前にデータを読み込んでおく必要があります。この記事ではProgress DataDirect ODBC driversを使ってどのデータソースに対してもダイレクトクエリを実行する方法を紹介します。

ダイレクトクエリを実行するためには、Microsoftが提供するData Connector SDKを使って、ダイレクトクエリを可能にする拡張機能を構築する必要があります。ただその方法がわからない場合には少し大変かもしれません。そんなときにProgress DataDirect ODBC driversが役に立ちます。

今回はProgress DataDirect Hive ODBC driverを使ってHive向けのダイレクトクエリの実行方法をお知らせします。どのDataDirect ODBC Driversにもこれと同じ方法が使えます。またProgress社のPower BI connectorsを使ってどのデータソースに対してもデータクエリを実行できます。

# Progress DataDirect Hive ODBC driverをインストールし設定する
1.[こちら](https://www.progress.com/odbc/apache-hadoop-hive)からProgress DataDirect Hive ODBC Driverをダウンロードします。
2.Setup.exeを実行しHive driverをインストールします。既定のインストール手順であれば単純なインストールです。
3.DataDirect Hive ODBC driverをインストールしたら、接続設定のためにODBCデータソースアドミニストレーターを開きます。
4.Addをクリックし、DataDirect X.X Apache Hive Protocolドライバーを追加します。
![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/658441/b6d7159a-7a81-7151-6c7c-9af6807fe54c.png)

5.以下のようにドライバーを設定します。
![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/658441/ef47add4-d605-d9ae-3021-11af89a01d66.png)

# Power BIでHiveへのダイレクトクエリを有効にする
1.[GitHub](https://github.com/saiteja09/Direct-Query-for-ODBC-Driver-in-Power-BI)からPower BI Data Connector for DataDirect ODBC driversをダウンロードします。
2.PCのドキュメントに移動しMicrosoft Power BI Desktopという名前でフォルダを作成します。
3.Microsoft Power BI Desktopフォルダ内にCustom Connectorsという別のフォルダを作成します。
4.Custom Connectorsフォルダ内にDataDirectODBC.mezをコピーします。
5.Power BI DesktopでCustom data connectorsのプレビュー機能を有効にします。
（File -> Options and settings -> Custom data connectorsから確認できます）
![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/658441/a6237221-8540-180f-c94c-abc11da53f7d.png)

*注意：この拡張機能は正式リリース版ではありません。正式版についてはProgress社にお問い合わせください。*

# DataDirect拡張機能を使ってDataDirect ODBC Drivers用のダイレクトクエリを実行する
Power BIで作業中の場合には、今開いているファイルを保存してからPower BIを再起動し、拡張機能が表示されるようにしてください。

1.Power BIでGet Data -> All -> DataDirectODBCの順にクリックすると、以下のようにDSN名を確認するフォームが表示されるはずです。Data Connectivity modeはDirectQueryを選択します。
![4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/658441/24d764b8-5c71-c861-8b02-00bc59e71451.png)

2.Basic上で認証情報を入力してからConnectをクリックしてください。
![5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/658441/141b50c6-b11f-2e14-d1fc-08666a9b6993.png)

3.該当するデータベース、テーブルを選択してからLoadをクリックし先へ進みます。
![6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/658441/0d73904c-0bac-5a18-be46-776cd8297feb.png)

4.下のようにPower BIの画面下部にてDataDirect ODBC driversへのダイレクトクエリが有効になっていることが確認できます。
![7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/658441/37419cd5-bbcd-abae-74ca-1f9e069daa4a.png)

この記事がProgress DataDirect ODBC driversを使ったダイレクトクエリの実行方法を理解する参考になれば幸いです。Salesforce、SQL Server、Oracle、IBM DB2、PostgreSQL、MySQL、Eloquaなど、すべてのProgress DataDirect ODBC driversおよびOpenAccess SDKを使って構築するドライバーは、ODBC標準に完全に準拠しており、またPower BI上での動作が保証されています。

参照：https://www.progress.com/tutorials/odbc/directquery-datadirect-odbc-drivers-in-power-bi
僕が考えた最強の React ファイル構成

# 技術スタック

[僕が考えた最強の React 技術スタック](https://qiita.com/shinoshu/items/4d132de3abea9dfbf9c1)

# Grouping by file type

https://reactjs.org/docs/faq-structure.html

基本的にこの考え方でフォルダを分ける

# 命名規則

キャメルケース

# ファイル構成

- app.yaml
- app.storybook.yaml
- cloudbuild.yaml
- firebase.json
- json-server # 検討中
    - app.yaml
    - index.js / index.ts
    - [routes.json](https://www.npmjs.com/package/json-server#add-custom-routes)
    - db
        - foo.json
- public / static
    - favicon.ico
- src
    - App.tsx
    - index.tsx
    - api / services
        - fooApi.ts / fooService.ts
    - components
        - Foo.tsx
    - constants
    - containers
        - Foo.tsx
    - contexts
    - firebase
        - [clientApp.js](https://github.com/vercel/next.js/blob/canary/examples/with-firebase/firebase/clientApp.js)
    - functions
    - layouts # たぶん必要ない
    - models / types
    - pages
        - index.tsx
    - store
        - store.ts
        - actions
            - foo.ts
        - facades
        - reducers
            - foo.ts
        - selectors
            - foo.ts
        - states
            - foo.ts
    - stories
        - foo.stories.ts
    - test # 検討中
        - pages
            - `__snapshots__`
            - index.test.tsx / index.spec.tsx

※ 自動生成されるファイルについてはいくつか省略してます

# 参考
- https://reactjs.org/docs/faq-structure.html
- https://ja.nuxtjs.org/guide/directory-structure/
- https://github.com/vercel/next.js/tree/canary/examples
- https://github.com/ngrx/platform/tree/master/projects/example-app/src/app
- https://github.com/reduxjs/redux/tree/master/examples
seeds.rbのデータを本番環境に反映させる！！！

# はじめに
seedファイルを作成したが、本番環境に反映されなかったため、調べ以下を実行したら上手くいきました！

メモとして、記事を投稿します！

#問題
seedファイルが本番環境に反映されない

```
<seedファイル>

lady = Category.create(name: "レディース")
lady_1 = lady.children.create(name: "トップス")
lady_1.children.create([{name: "Tシャツ/カットソー(半袖/袖なし)"},{name: "Tシャツ/カットソー(七分/長袖)"},{name: "シャツ/ブラウス(半袖/袖なし)"},{name: "シャツ/ブラウス(七分/長袖)"},{name: "ポロシャツ"},{name: "キャミソール"},{name: "タンクトップ"},{name: "ホルターネック"},{name: "ニット/セーター"},{name: "チュニック"},{name: "カーディガン/ボレロ"},{name: "アンサンブル"},{name: "ベスト/ジレ"},{name: "パーカー"},{name: "トレーナー/スウェット"},{name: "ベアトップ/チューブトップ"},{name: "ジャージ"},{name: "その他"}])
lady_2 = lady.children.create(name: "ジャケット/アウター")
lady_2.children.create([{name: "テーラードジャケット"},{name: "ノーカラージャケット"},{name: "Gジャン/デニムジャケット"},{name: "レザージャケット"},{name: "ダウンジャケット"},{name: "ライダースジャケット"},{name: "ミリタリージャケット"},{name: "ダウンベスト"},{name: "ジャンパー/ブルゾン"},{name: "ポンチョ"},{name: "ロングコート"},{name: "トレンチコート"},{name: "ダッフルコート"},{name: "ピーコート"},{name: "チェスターコート"},{name: "モッズコート"},{name: "スタジャン"},{name: "毛皮/ファーコート"},{name: "スプリングコート"},{name: "スカジャン"},{name: "その他"}])

~以下省略~
```

##開発環境でDBに反映させる（ステップ①）
以下を実行します

```
rails db:seed 
```
すると、DBに反映されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/622835/5e2e10ff-697a-c82e-c044-03e7beb5104b.png)


##テスト環境で反映させる（ステップ②）
ターミナルに以下を実行させます。

```
rails db:seed RAILS_ENV=test
```
##本番環境で反映させる（ステップ③）
まず、本番環境で以下のディレクトリに移動してください。

**cd /var/www/アプリ名/current**

移動しましたら、以下を実します。

```
rake db:seed RAILS_ENV=production
```


##最後に
最後に以下を実装し、unicornを止め、自動デプロイを実行

```
 bundle exec cap production unicorn:stop
 bundle exec cap production deploy 
```

これで上手く行きました。

#終わりに

本番環境で反映させる際に、currentのディレクトリにいせずコマンドを実行したら上手く行きませんでした。
cd /var/www/アプリ名/currentのディレクトリまで移動するのは、必須の様です。
[防守视角]tomcat内存马的查杀

### 前言
  随着近几年国内关于攻防比赛、HW的重视度越来越高,攻击队的权限维持手段越来越高明.从一开始的文件落地`webshell`,到今天基于`Filter`和`Servlet`的内存马,真可谓是百花齐放.

### Filter内存马&Servlet内存马
  `Java`世界中内存马无非就两种——`Filter`和`Servlet`.`Filter`翻译为拦截器,配合内存马技术`Filter`可以让任何不存在的路径变为一个`webshell`,就算是配合流量设备也很难察觉异样.因为大部分流量设备的默认规则只会当做错误日志处理.而基于`Servlet`的内存马一般需要攻击者指定一个固定路径来访问生效.
以下展示两种内存马.

第一种`Filter`内存马
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/491410/3fdaa80d-91c2-72e6-d598-2490dbc6455b.png)

可以看出攻击者利用一个get参数控制想要变更的命令

第二种`Servlet`内存马
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/491410/8258a95c-df02-ae59-e5c5-43d6f33018d7.png)

可以看出攻击者利用一个设定的路由配合一个`header`参数控制想要变更的命令

原因在于`Filter`中提供一个`doFilter`方法,在该方法中存在一个内置类`ServletRequest`,该类派生的对象含有一个`getParameter`方法可用来作为外部传参使用.
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/491410/9279b1fe-1f9c-4ac4-efec-ccab96e1ed9c.png)

而在`Servlet`中提供的则是`service`方法,在该方法中同样存在一个内置类,只不过该类是`HttpServletRequest`.该类派生的对象含有一个`getHeader`方法可用来作为外部传参使用.
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/491410/75be3399-bc36-fe71-90ac-79ee0705821c.png)

### 查杀方法
查杀方法需要借用到一款工具——`VisualVM`.我们使用这款工具时需要在应急的tomcat上提前配置好JMX访问.
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/491410/2880a2e3-ba61-271a-46b2-e0789640f3b4.png)
在`catalina.sh`/`catalina.bat`中找到
`# ----- Execute The Requested Command -----------------------------------------`字段
在其下方加入该tomcat局域网地址或者公网地址配置示例(加入哪一种网段根据情况而定)

```
CATALINA_OPTS="$CATALINA_OPTS -Djava.rmi.server.hostname=局域网或公网IP -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=监听端口(推荐19999) -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false"
```
随后启动`VisualVM`安装一个插件并激活
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/491410/fd00ccb4-8dea-f1b9-d1f2-eb090d65f07c.png)
随后找到本地的tomcat或者远程tomcat
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/491410/88c5831e-dd3f-959a-1930-d18124bcbd9d.png)
进入Mbean选项卡即可

对于`Filter`内存马和`Servlet`内存马都可以在`Catalina-Filter`/`Servlet`节点找到一些踪迹
以下为一个正常的`Filter`/`Servlet`监控情况

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/491410/3d28673f-dec9-901d-f1e4-2192622a4a2f.png)

以下为有攻击痕迹的`Filter`/`Servlet`监控情况

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/491410/e0b263dc-9c05-39b0-fdb4-c92632a926c1.png)

### 鸣谢
感谢Ntears提供的技术支持

### Reference
tomcat结合shiro无文件webshell的技术研究以及检测方法:
https://www.t00ls.net/thread-56828-1-1.html
VisualVM:
https://visualvm.github.io/download.html
[C#/OpenCvSharp] キャリブレーション

```C#
var src = new Mat(@".\dot_Bobbin_img.png", ImreadModes.Grayscale);

var patternSize = new Size(18, 13);

var centers = new Mat();
var found = Cv2.FindCirclesGrid(src, patternSize, centers, FindCirclesGridFlags.SymmetricGrid);

var points_img = new Mat();
Cv2.CvtColor(src.Clone(), points_img, ColorConversionCodes.GRAY2BGR);

Cv2.DrawChessboardCorners(points_img, patternSize, centers, true);

Cv2.ImShow("points", points_img);
Cv2.WaitKey();

int left_margin = 26;
int top_margin = 18;
int interval = 44;

var object_points = new Mat<Point3f>();
for (int j = 0; j < patternSize.Height; j++)
{
    for (int i = 0; i < patternSize.Width; i++)
    {
        object_points.Add(new Point3f(left_margin + i * interval, top_margin + j * interval, 0));
    }
}

var imageSize = src.Size();

var cameraMatrix = new Mat<double>(3, 3);
var distCoeffs = new Mat<double>(5, 1);

Cv2.CalibrateCamera(new[] { object_points }, new[] { centers }, imageSize, cameraMatrix, distCoeffs, out Mat[] rvecs, out Mat[] tvecs);

var newImageSize = new Size();
var newCameraMatrix = Cv2.GetOptimalNewCameraMatrix(cameraMatrix, distCoeffs, imageSize, 1, newImageSize, out Rect validPixROI);

var data = new Mat(@".\dot_Bobbin_img.png", ImreadModes.Grayscale);

var temp_img = new Mat();
Cv2.Undistort(data, temp_img, cameraMatrix, distCoeffs, newCameraMatrix);

var rotation = new Mat();
Cv2.Rodrigues(rvecs[0], rotation);

var transRot = new Mat<double>(3, 3);
rotation.Col(0).CopyTo(transRot.Col(0));
rotation.Col(1).CopyTo(transRot.Col(1));

var transData = new double[3,3] { { 0, 0, tvecs[0].At<double>(0) },{ 0, 0, tvecs[0].At<double>(1) }, { 0, 0, tvecs[0].At<double>(2) } };
var translate = InputArray.Create(transData).GetMat();
translate.Col(2).CopyTo(transRot.Col(2));

var dst_img = new Mat();
var m = newCameraMatrix * transRot;
Cv2.WarpPerspective(temp_img, dst_img, m, newImageSize, InterpolationFlags.WarpInverseMap);

Cv2.ImShow("temp", temp_img);
Cv2.WaitKey();

Cv2.ImShow("dst", dst_img);
```
![33661a48b48dca01f98a042375aba368[1].png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/662099/4a6a39da-9c8f-ef9d-37d9-2d7be5a5d169.png)
![c83a5792b630f2b553904eb8b601e50a[1].png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/662099/7065801a-c74c-ad87-3227-1fd59402e4a6.png)
![fb3273acdc75324b4c4ede4e3b11bef8[1].png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/662099/27ff1b2b-07b9-9ffd-9ac0-89f11f959b59.png)
![41534924ae29ab8249ea3a1a3bf25e62[1].png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/662099/84a5702b-8c50-6d53-05d0-6179896a97c4.png)
IAM基本情報まとめ

## IAMとは
- AWSのサービスで、どのユーザーがお客様のAWSリソースを使用できるか (認証) と、それらのユーザーがどのリソースをどのような方法でしようできるか、 (認可) の設定を行うことができるサービス
- ユーザがアクセスするための認証情報、ユーザがアクセスするAWSリーソースを制御するための権限を集中管理
- EC2やLambdaから他サービスへのアクセス制御を行うことも可能

## IAMユーザーとは
- AWSを利用するアカウント。
- 認可情報（IAMポリシー)を付与することが可能
- IAMグループに所属させることが可能
- ルートユーザーを使用しないようにする
- IAMユーザー作成時はMFA(多要素認証）をするべし
- 個々のユーザーには最小限のポリシーを与えるべし

## IAMユーザーの認証方法
- パスワード
    - ユーザー名と組み合わせてManagement Consoleへログイン
- アクセスキーID/シークレットアクセスキー
    - APIを使用するときのクレデンシャル情報
    
## IAMグループとは
- IAMユーザーの集合
- 認可情報（IAMポリシー)を付与することが可能
- 同じ権限のIAMユーザーが複数存在する場合、IMAユーザーを用いて一つにまとめると便利

## IAMロールとは
- ユーザーやグループが「人」に対して割り当てられるのに対し、ロールは主に「(EC2等の)インスタンス」に割り当てられる。
- IAMユーザーやグループには付与されない
- IAMロールにIAMポリシーを紐づける
- E2Eで利用されることが多い

## IAMポリシーとは
- AWSのリソースに対する権限設定を行う
- Jsonで記述する

## IAMポリシー書き方
```json

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": [
                "ec2:Describe*",
                "ec2:StartInstances",
                "ec2:StopInstances*"
            ],
            "Effect": "Allow",
            "Resource": "arn:aws:s3:::xxx"
        }
    ]
}
```
### Action

ec2:Describe* から始まる全ての権限を許可している
ec2:StartInstances ec2:StopInstances:　EC2インスタンスの開始と停止権限を設定

### Resouce

arn:aws:s3:::xxx: 取り扱うオブジェクトのARNを表す。この場合指定したS3のバケットにActionで指定した権限を許可している。

### Effect
Effect に Allow Deny を設定することで、ユーザーがResourceにアクセスすることに対して、許可または拒否を設定できる
デフォルトでは拒否されるように設定されている。
コールバック関数&jQuery不要「JSONP」取得関数（webページで複数回JSONPを使いたい人に）

Google Apps Scriptで作った複数のAPIからのJSONP取得が面倒（コールバック関数をその分だけ用意する必要があるなど）かつjQuery拡張の[jQuery-JSONP](https://github.com/jaubourg/jquery-jsonp)を使いたくなかったので、コールバック関数無しにJSONPからJSONを取得する関数をピュアなJavaScriptで作ってみました。この関数を使えば、もう、JSONP取得のためにわざわざコールバック関数を用意する必要はありません。
#大元のコード

```javascript:JavaScript
//引数
//url: jsonpのurl
//cbName: jsonpを起動することになっているはずのコールバック関数の名前
function JSONP(url, cbName="callback") {
    return new Promise(function(_r, _e) {
        window[cbName] = function(j) {
            delete window[cbName];
            _r(j);
        }
        var s = document.createElement("script");
        s.src = url;
        document.head.appendChild(s);
        s.onerror = function(e) {
            delete window[cbName];
            _e(e);
            s.remove();
        }
    });
}
```

####minified版
```javascript:JavaScript
function JSONP(url,cbName="callback"){return new Promise(function(i,r){window[cbName]=function(n){delete window[cbName];i(n)};var u=document.createElement("script");u.src=url;document.head.appendChild(u);u.onerror=function(n){delete window[cbName];r(n);u.remove()}})}
```

#使い方
JSONP関数はPromiseオブジェクトを返すので、返り値はthenで受けます。

分からない方のために念の為補足すると、thenの中に書かれた処理は、JSONPからJSONを取得した「直後」に起動します。
####JSONPのURL
```javascript:JavaScript
var url = "https://script.google.com/macros/s/XXXXX/exec?callback=cbFunc";
//この例では、"cbFunc"がリクエストしたコールバック関数名
//サーバー側でコールバック関数名が指定されている（ヒエッ…）場合は、その名前を把握しておく
```
####使い方
```javascript:JavaScript
/*使用例その1*/
JSONP(url, "cbFunc")
.then(function(r) {//jsonp読み込み成功時の処理
    console.log(r);//返り値（r）はJSONオブジェクト
})
```
```javascript:JavaScript
/*使用例その2*/
JSONP(url, "cbFunc")
.then(function(r) {
    console.log(r);
})
.catch(function(e) {//jsonp読み込み失敗時の処理
  console.log(e);//返り値（e）はonerrorのイベントオブジェクト
})
```
```javascript:JavaScript
/*使用例その3-α*/
//コールバック関数名は被らないように
JSONP(url, "cbFunc")
.then(function(r) {
    console.log(r);

    JSONP(url, "cbFunc")
    .then(function(r) {
        console.log(r);
    })
})

/*使用例その3-β*/
//IEを気にする必要が無いなら、アロー関数を使うのもアリ
JSONP(url, "cbFunc").then(r=>console.log(r),JSONP(url, "cbFunc_2").then(r=>console.log(r)));
```
Cloud pak for Dataのデプロイメントスペースを一旦きれいにする

(2020.9.8更新) WML client初期化でのversion番号を自動取得するように変更

Cloud pak for Data (以下CP4D)でのモデル開発中に、デプロイメントスペースにモデルや関数やデプロイメントを大量に作ってしまった後、それらを一旦きれいに全消ししたい時に使ってください。

あまりそんなシーンは多発しないかもしれませんが、2020年6月時点のCP4D v3.０ではモデルやデプロイメントなどのバージョン管理ができない制約があるため、複数世代のモデルを作成していくとあっという間にどんどんモデルやデプロイメントが増えていきます。
CP4Dの画面上でポチポチ１つずつ削除できるのですが、めんどくさいので一気に消すpythonコードを作りました。

分析プロジェクトで適当なNotebookを立ち上げて実行します。

```python:準備その１。WMLclientを初期化
url = "https://cloudpackfordata.url.com"
from watson_machine_learning_client import WatsonMachineLearningAPIClient
import requests, os
token = os.environ['USER_ACCESS_TOKEN']
version = requests.get(os.environ['RUNTIME_ENV_APSX_URL'] + "/diag").text[0:5]
wml_credentials = {
    "token" : token,
    "instance_id" : "openshift",
    "url": url,
    "version": version
}
client = WatsonMachineLearningAPIClient(wml_credentials)
```

```python:準備その２。デプロイメントスペースIDをセット
# IDは client.repository.list_spaces() で調べられる
space_id = "xxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
client.set.default_space(space_id)
```

全消しの順番は依存関係から、デプロイメント → モデル → 関数 の順番に行います。

```python:全デプロイメントを削除
all_deps = client.deployments.get_details()['resources']
for dep in all_deps:
    dep_name = dep['metadata']['name']
    dep_id = dep['metadata']['guid']
    client.deployments.delete(dep_id)
    print(dep_name +"(" + dep_id + ") is deleted")
```

```python:全モデルを削除
all_models = client.repository.get_model_details()['resources'] 
for model in all_models:
    model_name = model['metadata']['name']
    model_id = model['metadata']['guid']
    client.repository.delete(model_id)
    print(model_name +" (" + model_id + ") is deleted")
```

```python:全関数を削除
all_funcs = client.repository.get_function_details()['resources'] 
for func in all_funcs:
    func_name = func['metadata']['name']
    func_id = func['metadata']['guid']
    client.repository.delete(func_id)
    print(func_name +" (" + func_id + ") is deleted")
```

削除後、CP4D画面上できれいになったデプロイメントスペースを確認して終了です。

ちなみに、デプロイメントスペース自体を削除し再作成することでも瞬時にきれいになります。その場合はデプロイメントスペースIDが変わってしまうので、各種コードの修正をお忘れなく。

あと、本番稼働開始した後はこのNotebookは削除しておいたほうが無難です。残しておくと危険です。
クロスオリジンエラーの解決

[CORSリクエストでクレデンシャル(≒クッキー)を必要とする場合の注意点](https://qiita.com/kawaz/items/1e51c374b7a13c21b7e2)
[Access-Control-Allow-Credentials](https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/Access-Control-Allow-Credentials)
PHPのパスワード暗号化（md5とSHA1）＆（password_hashとpassword_verify）

PHPでログインフォームを作成する際に、パスワードを暗号化する方法はいくつかあります。

私がudemyなどの動画教材で観た限りでは、`md5`や`SHA1`でハッシュを返す方法が多かったのですが、
現在はどちらも「非推奨」との事でしたので、コードの書き換えを行いました。

## md5の暗号化 （Before）
`md5`で暗号化した場合は、認証の際にもう一度`md5`をかけてイコール判定を行います。

```php:Account.php
class Account {

    private $con;
    private $errorArray;

    public function __construct($con) {
      $this->con = $con;
      $this->errorArray = array();
    }

    // アカウント登録：データベースへの挿入
    private function insertUserDetails($username, $email, $pass) {
        $encryptedPw = md5($pw);
        $result = mysqli_query($this->con, "INSERT INTO users VALUES (NULL, '$username', '$email', '$encryptedPw')");

        return $result;
    }

    // ログイン処理：ユーザーネームとパスワード判定
    public function login($username, $pass) {
        $pass = md5($pass);
        $query = mysqli_query($this->con, "SELECT * FROM users WHERE username='$username' AND password='$pass'");

        if (mysqli_num_rows($query) == 1) {
          return true;
        } else {
          array_push($this->errorArray, Constants::$loginFailed);
          return false;
        }
    }

    
}
```
## password_hashとpassword_verifyの暗号化  （After）

password_hashで暗号化する場合は、password_verifyで認証を行います。

```php:Account.php
<?php
  class Account {

    // 省略

    // アカウント登録：DB挿入
    private function insertUserDetails($un, $fn, $ln, $em, $pw) {
        $encryptedPw = password_hash($pw, PASSWORD_DEFAULT); // 変更
        $date = date("Y-m-d");
        $result = mysqli_query($this->con, "INSERT INTO users VALUES (NULL, '$un', '$fn', '$ln', '$em', '$encryptedPw', '$date')");

        return $result;
    }

    // ログイン処理：ユーザーネームとパスワード判定
    public function login($username, $pass) {
        $query = mysqli_query($this->con, "SELECT password FROM users WHERE username='$username'");
        $checkPass = mysqli_fetch_row($query);
   
        if (password_verify($pass, $checkPass[0])) {
            return true;
        } else {
            array_push($this->errorArray, Constants::$loginFailed);
            return false;
        }
    }

    
```

引数のパスワードに対して、データベース上のパスワードと一致するかを`password_verify`を用いて判定します。

## 参考
[password_hash](https://www.php.net/manual/ja/function.password-hash.php)
[password_verify](https://www.php.net/manual/ja/function.password-verify.php)
[安全なパスワードハッシュ](https://www.php.net/manual/ja/faq.passwords.php)
GASのトリガーで起きたエラーを通知したい

# 概要
GASで設定されているトリガーが実行された時のエラー通知について。

エラー通知の設定方法は簡単だったけど、
トリガーに設定したエラー通知がちゃんと飛ぶかの動作確認の際、少し勘違いしていた部分があったのでメモ

# 勘違いしていたこと
先に、どう勘違いしていたかを言うと
スクリプトエディタ画面にある、関数の実行を押下すれば
その関数内でエラーが発生した時点で、エラーの通知が飛ぶと思っていた。。
実際は、トリガーのエラー通知設定なので
トリガーが設定した条件・時間で動き出し、その時のエラーでないと通知はこなかった。

つまり、 スクリプトエディタからエラー通知の動作確認をすることは出来ない。

# エラー通知動くか確認
同じような、些細な勘違いをしている方が居たら、この先の流れで進めると
トリガーのエラー通知がちゃんと動いているか確認できる。はず。

## 前提条件
GASのトリガーでエラーが起きたタイミングで通知できるように
スプレッドシートが既に用意されており、スクリプトエディタからプロジェクトが作られている状態であること。
また、プロジェクト内に、トリガー設定されている関数が用意されていること。

## 手順
### エラー通知の設定
トリガーの設定から、エラー通知に関する設定が可能です。
下記の鉛筆ボタンを押下すると、設定のモーダルが表示されるので、そこで設定・・！
![スクリーンショット 2020-05-29 16.10.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496890/32851ccf-d48b-a216-4e45-15a287544d3e.png)
今回は、エラー通知の設定ではなく
通知が飛ぶかどうかのチェックを行いたいためエラー通知設定方法は割愛

エラー通知設定方法参考: [[GAS][tips]トリガーのエラー通知設定](https://etauthenonprogrammercoder.tumblr.com/post/183593892914/gastips%E3%83%88%E3%83%AA%E3%82%AC%E3%83%BC%E3%81%AE%E3%82%A8%E3%83%A9%E3%83%BC%E9%80%9A%E7%9F%A5%E8%A8%AD%E5%AE%9A)



### エラー通知の挙動確認
エラー通知設定を行なったトリガーの実行時間待つ。
トリガーの設定が時間ベースでは無い場合（スプレッドシートから、等）はトリガーに合わせ動かす。

すると、実際にトリガーが動いた時
[GoogleAppScriptの画面](https://script.google.com/home)でトリガーのエラーを確認出来ます。
<img width="1173" alt="スクリーンショット 2020-06-26 16.52.34.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496890/823c803f-b1a4-d6fe-295e-70aa11dec935.png">

また、今回使用したスプレッドシートを作成したアカウントでメールを確認すると
エラー通知メールが送られてきていることが確認できます。
IE11だとNodeListがforEachで回らない問題

# はじめに

IEの配布元であるマイクロソフトを始め、 **IEが1日も早く滅んでほしい、なぜIE拡散禁止条約がないのか、早くWTOで世界IE禁止宣言を採択してほしい** と世界中のweb系エンジニアは強く祈っていることだとは思いますが、様々な事情をお持ちの顧客のために日夜IE対応に心を砕いていることと存じ上げます。
それでもIEの最終バージョンであるIE11はまだマシな方なんだとは思いますが、あらゆるpolyfillをブチ込んでbabelをまわさないとイマドキ文法で書かれたJSは動きません。今宵もそんなフロントエンドエンジニアたちの天を貫く喜び、悲しみ、怒りを抱え、欲望の果へ向かいbabelが唸りをあげます。

BUCK-TICKの宗教ソング「BABEL」の歌詞が、babel使いの気持ちを余すところなく表現してて最高なので、ぜひご一読ください。
http://j-lyric.net/artist/a0006e7/l0438a2.html

# NodeListにforEachメソッドが居ない（ただしIEに限る）

ある時「IEでJSが死んでるぞ」と庭でマンボウが死んでるくらいのテンションで報告を受けました。
しゃーないな、と思ってテスト用のVMを立ち上げて見てみたら見事に死んでいたのですが、どうやら「NodeListにforEachなんてメソッドないで」というやつでした。
マジで？と思ったらマジでした。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/57811/c870d5f2-d4e9-6475-c578-f180f855f844.png)
https://developer.mozilla.org/ja/docs/Web/API/NodeList/forEach

孔明先生もショックです。

# NodeListって何？

`document.querySelectorAll()` の戻り値として返ってくるDOMノードの集合体です。
https://developer.mozilla.org/ja/docs/Web/API/NodeList

Arrayで返ってきてもいいんじゃないのか？とは多少思いますが、ActionScriptで言うところのArrayListっぽい作りになっています。なので、Arrayに変換自体は可能です。

jQueryでclassで引っ掛けると配列ではなくてjQueryオブジェクトで返ってきて、そいつに `each()` しないと走査できないのと感覚は同じです。

# 解決策

まあ、さっきのページにpolyfillが書いてあるので、それをそのまま入れてもいいのですが、なんか癪なのでnpmにpolyfill居るんじゃね？と思って探したらいました。

https://www.npmjs.com/package/nodelist-foreach-polyfill

なのでこいつをnpmで入れてimportすれば万事解決です。

```bash:コマンドライン
$ npm install nodelist-foreach-polyfill --save-dev
```

```javascript
import 'nodelist-foreach-polyfill'

class Hoge{
    static init() {
        const hogelist = document.querySelectorAll('.hoge');
        hogelist.forEach( e => e.classlist.add('fuga') );
    }
}

Hoge.init();
```
LINE Messaging APIを使用したChatBotの概要

## 仕組み
ラインのメッセージを送った直後に同じ内容が返信される仕組みを、
自分なりにまとめてみる。
コードとかは割愛。

対象物はこの３つ

- ユーザー（クライアント）
- Lineチャネル
- サーバー言語(今回はLaravel)


こんな感じで通信が行われる

**ユーザー ➡️ Lineチャネル ➡️ Laravel**
         　　 　　　　　　　　　　　　　　⬇️ [すべてPOST送信] 
**ユーザー ⬅️ Lineチャネル ⬅️ laravel**

ということで

` Lineチャネルを作成 `

` サーバーと通信連携できるようにアクセストークン発行 `

` サーバーサイド側でルーティング設定 `

` コントローラー側で処理を書く `

こちらを行えば良さそうです。
Ansible Night オンライン！ 2020.06 〜オンラインだからこそ実力を発揮〜 にLTで参加した

[Ansible Night オンライン！ 2020.06
〜オンラインだからこそ実力を発揮〜](https://ansible-users.connpass.com/event/176436/)にLTで参加させて頂きました

## LTまでの大まかな流れ

- 6月2日にCfPに応募
- 6月10日頃資料作り開始
- 6月23日 LT当日

## 当初の内容

最初は「 **CI/CDツールにMoleculeを使った場合のGitHub Actions workflowのチューニングとMoleculeのシナリオの最適化** 」と言うタイトルで

- GitHub ActionsのActionライブラリのセキュリティーについて
- Moleculeを実行するテスト対象のコンテナの設定
- GitHub Actionsのキャッシュ機能

この3つについての内容にしようかと考えたのですが

- Moleculeを導入している
- GitHub Actionsを導入している

参加者に対するこの条件に当てはまる比率が高くないだろうと判断し「 **GitHub ActionsでAnsible roleのCIを実行する** 」と言う入門者向けの内容に方向転換しました

## 大変だったこと

- 5分は短い
- 当日まで内容の修正が続いた

### 5分は短い

内容が多すぎたのか最初にLTの練習した時は5分のところが15分掛かりました。仕方ないので喋りたかった部分をいくつか削りました。

### 当日まで内容の修正が続いた

資料自体はLT3日程前にはだいたい完成していたのですがAnsible 2.10がTwitterで話題になっていたのでLT当日にansible-base(2.10.0b)を実際に触って動作確認し、スライドに含めました

## 終わってからの感想

体感的には好評を頂けて嬉しかった反面（LT後の質問を5つ頂いた）、自分の予想に反してMolecule、CIツールの普及がまだ進んでいなかった事がちょっとショックでした。

最初の「 **CI/CDツールにMoleculeを使った場合のGitHub Actions workflowのチューニングとMoleculeのシナリオの最適化** 」と言う内容だったら恐らく反応もほとんど無く終わっていたのだろうなと思うと自分に出来る事なら少しでも情報発信すべきなのかなとも思いました。

## LT当日に話せなかった内容

### GitHub Actionsのキャッシュは導入すべきだが運用は良く考えるべき

GitHub Actionsにあるキャッシュ機能を使うとCIの実行スピードが速くなる

ただしアップデートが頻繁なAnsible collectionsライブラリやDockerイメージ等に使う場合はキャッシュの期限をどれくらいに設定するのかよく吟味する必要がある

長過ぎるとアップデートを見逃すし反対に短いとCIの実行時間の短縮につながらないかもしれない

キャッシュの有効期限はシステムに合わせて最適化する

### CIツールのステータスバッヂは便利だ

Saas型のCIツールにはステータスバッヂと言ってCIが成功したか失敗したか表示できる機能がある。

Markdownが利用できるページに各リポジトリ毎のステータスバッヂを集約する事で現在のCIのステータスが確認できる

<img width="1280" alt="スクリーンショット 2020-06-26 14.52.48.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79373/9aa0eedf-10e8-8d73-a1b0-8faa49557c91.png">

### CIは定期実行すると超強力

Ansible roleのCIを定期実行する事でユーザーにこのRoleは確実に動くと言う証拠を提示することが出来る

<img width="1280" alt="スクリーンショット 2020-06-26 15.31.23.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79373/d8749b96-def0-5372-6857-4513012d79f8.png">


### 最終的にNoOpsになる

Ansibleのバージョン、実行対象のVMのバージョン、等自動でアップデートするようにしてCIの定期実行を導入するとほぼ管理が必要なくなる

またユーザーは集約されたステータスバッヂで現在使えるRoleが簡単に確認できるようになり管理者側で明示する必要もなくなる

手間を掛ける部分がかなり減る


## LTまでに参考にしたサイト

Molecule

- [Rapidly Build & Test Ansible Roles with Molecule + Docker ](https://www.youtube.com/watch?v=DAnMyBZ8-Qs&feature=youtu.be)
- [Ansible 101 - Episode 7 - Molecule Testing and Linting and Ansible Galaxy](https://www.youtube.com/watch?v=FaXVZ60o8L8&feature=youtu.be)

スライド作成

あまり身につかなかったが

- [How to Create an Awesome Slide Presentation (for Keynote or Powerpoint) ](https://www.youtube.com/watch?time_continue=24&v=6bSOAl1i8bw&feature=emb_title)
VirtualBoxでIPアドレスがうまく設定できない時に確認すること

ifcfg-eth0をちゃんと設定したのに、
IPアドレスが設定した通りにならない！

たいてい、VirtualBoxのMACアドレスと
ifcfg-eth0の中に記載されてるHWADDRが一致してない。

普段だったらそんな意識する必要ないのかもしれないけど、
VirtualBoxはネットワークアダプタが勝手に消えることが
稀によくあるので、覚えてないと忘れた頃に毎回やってくる。
C#のと無名関数とか


# 
```
using System;
using System.Linq;

namespace test_func
{
	public class DelegateTest{

		
		protected delegate int Calculate(int a, int b);
		public void Execute()
		{
			Calculate sum = GetSum;
			Calculate gcd = GetGcd;
			Console.WriteLine(sum(3, 5));   // => 8
			Console.WriteLine(gcd(85, 25)); // => 5
		}

		protected static int GetSum(int a, int b)
		{
			return a + b; 
		}

		protected static int GetGcd(int a, int b)
		{
			if (b == 0) return a;
			return GetGcd(b, a % b);
		}
	}

	public class TEST2 : DelegateTest
	{
		//Actionは戻り値がvoidのDelegate型、Funcはそれ以外のDelegate型です。
		//Funcはジェネリックの最後の要素が戻り値です。Actionはすべて引数です。

		protected Action<int> m_writeInfo = WriteInfo;

		new public void Execute()
		{
			Func<int, int, int> sum = GetSum;
			Func<int, int, int> gcd = GetGcd;//最大公約数
			m_writeInfo(sum(3, 5));   // => 計算結果は8です
		}

		public static void WriteInfo(int num)
		{
			Console.WriteLine("計算結果は" + num.ToString() + "です");
		}

		public void Execute2() {
			Func<int, int, int> sumFunc = delegate (int a, int b) { return a + b; };
			m_writeInfo(sumFunc( 8, 5));   // => 計算結果は8です
		}
	}

	class LamdaTest:  TEST2
	{
		new public void Execute()
		{
			Func<int, int, int> sumFunc = ((a, b) => a + b);
			WriteInfo(sumFunc( 3, 5));   // => 計算結果は8です
		}

		new public void Execute2()
		{
			Func<int, int, int> gcdFunc = null;     // 再帰のときは初期化しないとコンパイルエラー
			gcdFunc = (a, b) =>
			{
				if (b == 0) return a;
				return gcdFunc(b, a % b);
			};

			WriteInfo(gcdFunc( 24, 4)); 

		}
	}


	class LinqTest
	{
		public void Execute()
		{
			var items = new int[] { 1, 2, 3 };
			items.Select(Add10).ToArray();  // => [11, 12, 13]
		}

		private static int Add10(int a)
		{
			return a + 10;
		}
	}
	class Program
	{
		static void Main(string[] args) {

			LamdaTest test = new LamdaTest();
			test.Execute();
			test.Execute2();
			Console.WriteLine("---------------");

			var t2 = new TEST2();
			t2.Execute();
			t2.Execute2();
			Console.WriteLine("Hello World!");

			Console.WriteLine("---------------");
		}
	}
}

```
[Javascript]iframeによって読み込まれているか判別する方法

##はじめに
自分がiframeによって読み込まれているときだけ行いたい処理があったので、iframeによって読み込まれているかどうかを判別する方法を調べました。
自分用の備忘録も兼ねてまとめます。

##結論
初めに結論です。

```javascript
window != window.parent
```
で判別できます。
`true`ならiframeによって読み込まれているということになります。

##解説
簡単に解説を書きます。

まず、iframeについて[MDN](https://developer.mozilla.org/ja/docs/Web/HTML/Element/iframe)に書かれていることをまとめると次のようになります。

- iFrameは閲覧コンテキスト（browsing context)を表現する
- 閲覧コンテキストは、セッション履歴（History）と文書（Document）をもつ
- 他の閲覧コンテキストを埋め込んでる閲覧コンテキストは親閲覧コンテキストと呼ばれる
- 最上位の閲覧コンテキストは、Windowオブジェクトで表される

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/615201/c306dedb-4974-66cd-d5dc-b08b685bf3b9.png)


つまり、
iFrameによって読み込まれている時、window.parentはiframeの親閲覧コンテキストになるので、windowにはなりません。







間違いや指摘があれば、コメントにお願いします。
jenkinsをcurl経由で呼び出す方法

## こんなことがあったので

jenkinsを実行した後、コンソールログを確認したいけど、html上だと重いので、
ダウンロードしてログが見たいと思った。
jenkinsはユーザ認証がある

## 実行方法

```
curl -s -u user:pass http://<jenkins_url>/view/<jobまでのパス>/job/<job_name>/<job_num>/consoleText > <出力したいファイル名>.txt
```
Nuxt.js 2.13+から標準搭載されるらしい@nuxt/componentsを古いバージョンで使う

## 経緯
Nuxt.jsで開発していて、（別にNuxt.jsに限ったことではないけれど）
当初描いていた未来と、実際の未来は違った形をたどり、
componentsディレクトリの中がカオス状態・・・
まだまだどうなっていくのかわからないので、一回整理したい。
しかし、全てimportのパスを置き換えるのか・・？

どうしようかと思っていたとき、
コンポーネントをグローバルに使えるようにできるのでは？と思って調べたら、
なんと、そういうモジュールを発見！（[@nuxtjs/global-components](https://github.com/nuxt-community/global-components)）
さらにたどっていくと、Nuxt.js 2.13+から標準搭載されると書かれていた！

古いバージョンでもモジュールをインストールすれば使えるらしい！
しかしまだやり方をまとめている記事があまりなかったので、気づいたことなどを書きます。


## @nuxt/componentsとは
[@nuxt/components](https://github.com/nuxt/components)

> Auto import components in Nuxt.js

ということなので、自動でコンポーネントをインポートしてくれるそうだ！

これだけで、使えるようになるらしい

```nuxt.config.js
export default {
  components: true
}
```

例えばこんなコンポーネントをつくったら

```
components/
  ComponentFoo.vue
  component-bar.vue
```

これだけで読み込めるそうです。

```vuejs
<template>
  <ComponentFoo />
  <component-bar />
</template>
```

importでファイルを読み込み、componentsに記述するということをしなくて良くなります。

## 古いバージョンでの使い方

私の環境のNuxt.jsのバージョンは2.12.2です。

@nuxt/componentsのインストール

```
npm install --save-dev @nuxt/components
```

nuxt.config.jsに追記

```nuxt.config.js
{
  modules: [
    '@nuxt/components'
  ],
  components: [
    '~/components/'
  ]
}
```

これであとはコンポーネントにファイルを追加し、使えるようになります。
最低限は以上だと思います。
オプションでさらに便利に設定できます。

## 私が引っかかったこと

### nuxt.config.jsの書き方
Nuxt.js 2.9からbuildModulesが推奨されているが、開発している環境は、それに書き換えていなかった。
また、`components: ['~/components/']`の部分も記載しないと動かなかった。

### コンポーネントファイルの命名規則
特にオプションを設定しない場合は、ファイル名がそのままコンポーネント呼び出しタグになるみたいだが、
パスカルケースかケバブケースでないといけないみたい。（キャメルはダメ）
もともとのファイルが、スネークケースだったため、うまく読み込まれなかった。
パスカルかケバブかはどちらでもいいようで、ファイル名がパスカル、タグがケバブでも問題ない。
headerなどのもともとあるHTMLタグと同じ名前でも、`<header />`と書けばコンポーネントタグとして使えるよう。

### コンポーネントタグの最後のスラッシュは必要
`<ComponentFoo />`を`<ComponentFoo>`とするとエラーになる。
`<ComponentFoo></ComponentFoo>`でも問題なし。

## まとめ
以上、めっちゃ楽になりました！
これで、あーこれディレクトリ移動したいってなっても、特に気にすることなく整理し直すことができます。


## 参考
https://qiita.com/JDM/items/afb5171594f69bcd94bd
https://ja.nuxtjs.org/api/configuration-modules/