OmniAICreator/Qiita-1.07M huggingface dataset:
AIOpsについて考えてみる

# AIOpsとは

運用のお仕事をしているとAIOpsというキーワードを耳にする事が増えてきたと思います。
この”AIOps”とは **Artificial Intelligence for IT Operations** の略で、IT運用にAI・機械学習を取り込む考え方を指しています。もともとガートナーが提唱した考え方で、下図のように”Big Data”と”Machine Learning”を中心に”Monitoring” ”Service Desk” ”Automation”のサイクルが回る仕組みとなっています。

<img width="600" alt="AIOps.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/333529/cb086080-1ecc-9321-e48b-4bebe59a59a3.png">

かなり抽象的ですが、監視システムで収集した大量のログ・メトリクスをAI・機械学習で解析・分析し、アラートや障害の対応を自動処理する仕組みであると考えています。オンプレ物理マシンでシステム構築していた時は、アラートを検知しても時間をかけてログ調査する事ができてましたが、現在は監視対象が増えアラートノイズが多くてまともに確認できません。

結果、よくやるのはアラートを送信しないようにシステム側でフィルタする事ですが、障害の予兆や本当のアラートが送信されなかったという事にもなりかねません。そこで、全てのログを監視システムに送信し、AIにアラートを学習させていつもと違う場合に通知するなどの仕組みが必要になってきました。

大量のログ・メトリクスをAIに解析させるというのは分かりましたが、その後はどうすれば良いのでしょうか？
先ほどのガートナーの図では”Monitoring”の次は”Service Desk”になっています。検知したアラートをインシデント登録してITILに沿った管理に繋げます。そして最後は”Automation”でアラート処理を自動化するという流れになってます。

20年2月頃にフロリダで開催されたITExpoでAIOpsのセッションに参加していたのですが、AIOpsを実現させるには自動化が必要だとStackStateのCEOが講演していたのが印象的でした。


# AIOpsの始め方

ガートナーがAIOpsの始め方について７つの方針を提示しています。

1. 待たない
1. 最初のテストケースを賢明に選択する
1. 目指す方向を定め、提示する
1. 自由に実験する
1. IT部門の垣根を越えて人材を求め、活用する
1. 可能なら標準化を行い、合理的ならモダナイズを行う
1. 本格導入を構想する

私が一番重要だと考えているのは１つ目の**待たない**です。
ガートナーの記事と日本語訳を紹介しておきます。

https://www.gartner.com/smarterwithgartner/how-to-get-started-with-aiops

https://atmarkit.itmedia.co.jp/ait/articles/1910/04/news007.html



おわり。
AWSにてELBを削除しようとするとLoad balancer ‘...’ cannot be deleted because it is currently associated with another serviceのエラーがでて削除できない

## 背景
- EC2　>　ロードバランシング　>　ロードバランサー
- 削除したいロードバランサーを選択してアクションで削除をクリック後、赤字でタイトルのエラーが表示され削除ができない

## 結論
- 統合サービスと連携している場合はまずそちらを削除する
    - 今回は緑のチェックが入ったVPC エンドポイントサービス (AWS PrivateLink)を使用していたため、サービスIDをクリックし、サービスID先を削除することでELBの削除が可能になった

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/308965/d867eb47-8a60-ac33-ca5a-abda3442ad54.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/308965/93f3466a-9cd5-3e68-478b-0cad7c95af2b.png)


##　メモ
- 上記の前に以下を行ったため、上記で削除できない場合は以下の作業も必要
    - EC2 > ロードバランサー > リスナー > 削除
    - EC2 > ターゲットグループ > 削除
LINE Message API で作ったBOT（チャネル）に友だち登録してくれた人の User_idを取得するテスト

##概要

タイトルの通りですが、GASで、Webhookで取得したデータをスプレッドシートにappendするようなのを作ったメモです。

##スクリプト

あらかじめ、``data`` って言うシートを作っておきました。

```
function doPost(e) {
  var params = JSON.parse(e.postData.contents);
  
  var ss = SpreadsheetApp.openById(SpreadsheetApp.getActiveSpreadsheet().getId());
  var sheet = ss.getSheetByName("data");

  sheet.appendRow([params]);

  return ;
}
```

##公開

![スクリーンショット 2021-10-31 16.52.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/186659/2086c69d-373b-6670-9f79-c8cc29761e07.png)


いつものように、「ウェブアプリケーションとして公開」して、そのURLを取得します。

```
https://script.google.com/macros/s/[個別にできた文字列]/exec
```

##LINE Developer のサイトに登録

![スクリーンショット 2021-10-31 16.55.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/186659/76d79d66-42f6-a4b3-d6b2-edf70b62058d.png)

##完成

おしまい。
Linux版Zoomで画面共有できないのでワークアラウンドを試してみた

# きっかけ

Zooｍで資料共有して話をしようと思った矢先、下のようなエラーが出てしまい、共有できませんでした。

> Can not start share, we only support Wayland on GNOME with Ubuntu 17 and above, Fedora 25 and above, Debian 9 and above, CentOS 8 and above, OpenSUSE Leap 15 and above, Oracle Linux 8 and above, Arch Linux, AnterGos, Manjaro. If your OS is not on the list, please use x11 instead.

これが表示されたときは「おかしいな。`Wayland on Gnome with Debian 10`なので`Debian 9 and above`に含まれているはずだ。」と思ったのですが、打合せの最中でしたのでそれどころではありませんでした。

# 調査結果

キーワードでインターネット検索しました。このウェブページが参考になりました。
https://www.guyrutenberg.com/2020/06/22/fixing-zoom-screen-sharing-on-debian-unstable/

* どこかのアップデートでOSバージョンを認識するzoomの機能が壊れたんじゃないかということ。
* トレースすると`/etc/os-release`を見て判断している。(straceで追っている)
* なので、このファイルを書き換えてしまえば回避可能。

完全に自己責任ですね。

# ワークアラウンド

参考サイトの記載のとおりです。
しつこいようですが自己責任で実施ください。何かあったら戻せる方のみどうぞ。

```
# mv /etc/os-release{,.save}
# cp /etc/os-release{.save,}
# cat << EOL >> /etc/os-release
VERSION_ID="99"
VERSION="99 (sid)"
VERSION_CODENAME="sid"
EOL
```

これが終わってからzoomアプリを立ち上げました。

# Resources

```
$ dpkg -l | grep ^ii
ii  zoom           5.8.0.16     amd64        Zoom Cloud Meetings 
$ cat /etc/os-release
PRETTY_NAME="Debian GNU/Linux bookworm/sid"
NAME="Debian GNU/Linux"
ID=debian
HOME_URL="https://www.debian.org/"
SUPPORT_URL="https://www.debian.org/support"
BUG_REPORT_URL="https://bugs.debian.org/"
VERSION_ID="99"
VERSION="99 (sid)"
VERSION_CODENAME="sid"
```


# おわび

エラーウィンドウのスクリーンショット、動作した後のスクリーンショットを撮り忘れてしまいました。何かの折に後者だけでも撮って更新したいと思います。

〆
SSH Agent Forwarding で、秘密鍵一つで便利に多段 SSH する

## 概要
タイトルの通りです。

以下の内容はすべて筆者の理解です。間違い等あればご指摘ください。

SSH Agent とは、 SSH する際にユーザの秘密鍵をよしなに管理してくれる存在です。たとえば、 SSH Agent にパスフレーズ付きの秘密鍵を登録すると、 SSH 接続ごとにパスフレーズを求められるといったことがなくなります。 SSH Agent 自体に関する詳細な説明は、別のページ [^ssh-agent] を参照してください。

[^ssh-agent]: [ssh-agent - How to configure, forwarding, protocol.](https://www.ssh.com/academy/ssh/agent) や [【SSH】ssh-agent の使い方](https://laboradian.com/how-to-use-ssh-agent/)

本記事では、この SSH Agent のフォワーディング機能を利用し、秘密鍵ひとつで便利に多段 SSH する手段を説明します。

## 想定する環境
色々な開発をしていると、多段 SSH を行う場面にちょいちょい遭遇します。ここで多段 SSH とは、自分のパソコン A からホスト B に SSH し、そこからさらにホスト C に対して SSH するようなことを指します。コマンドにすると、以下のようになるかと思います。

```sh
# hostA ===SSH==> hostB ===SSH==> hostC
user1@hostA$ ssh user2@hostB
user2@hostB$ ssh user3@hostC
user3@hostC$ 目的の作業
```

使う場面は

- インターネットに公開されていないホストに対して、踏み台サーバを経由してアクセスする場合
- SSH 先のホストで、 SSH 公開鍵認証を利用して GitHub にアクセスする場合

などがあると思います。

そのような場合に、ここでいう **`hostB` に新しい秘密鍵を置くことなく、自分のパソコンの秘密鍵を使って `hostC` にアクセスする** というのが、今回の目的です。これを実現するために *エージェントのフォワーディング* という機能を利用します。

なお、既に自分のパソコン (`hostA`) の公開鍵が `hostB` と `hostC` のそれぞれに設置されているものとします。

## 手順
### 1. 自分のパソコンで SSH Agent を起動する
実際に秘密鍵を管理してくれる存在である、 SSH Agent を起動します。

Mac の場合は、自動で起動されるためスキップして構いません。

```sh
# Linux
user1@hostA$ eval "$(ssh-agent)"
```

### 2. 鍵を登録する
SSH Agent に対して、管理してもらう秘密鍵を登録します。

```sh
user1@hostA$ ssh-add 秘密鍵ファイル # ~/.ssh/id_rsa など
```

ファイル名を省略すると、自動で `~/.ssh/id_rsa`, `~/.ssh/id_dsa`, `~/.ssh/id_ecdsa`, `~/.ssh/id_ed25519` が登録されます。 [^ssh-version]

[^ssh-version]: バージョンによります。

Mac の場合は、以下のようにすると mac OS のキーチェインに保存されて、次回以降自動で登録してくれます。

```sh
user1@hostA$ ssh-add -K 秘密鍵ファイル
```

`ssh-add -l` を実行してフィンガープリントが表示されたら、正常に登録できています。

```sh
# 出力例
$ ssh-add -l
256 SHA256:abcdefghijklmnopqrstuvwxyz0123456789abcdefg user1@hostA (ED25519)
```

### 3. `hostB` に SSH する
中継役となる `hostB` に SSH を行います。

```sh
user1@hostA$ ssh -A user2@hostB
```

この `-A` オプションによって、登録された秘密鍵を `hostB` で使うことができるようになります (フォワーディングされる)。ここでも、 `ssh-add -l` を実行することで、 `hostB` で鍵を使えるようになっているか確認することができます。


### 4. `hostC` に SSH する
`ssh -A` で `hostB` に接続したら、あとは `hostC` に SSH して目的の作業を行うだけです。

```sh
user2@hostB$ ssh user3@hostC
# ... ログイン時のメッセージとか ...
user3@hostC$ # 目的の作業
```

GitHub へのアクセスが目的なら、自由にアクセスできるようになっているはずです。 [^github-key]

[^github-key]: もちろん秘密鍵に対応する公開鍵が GitHub に登録されている必要があります。

```
user2@hostB$ ssh git@github.com
PTY allocation request failed on channel 0
Hi ユーザ名! You've successfully authenticated, but GitHub does not provide shell access.
Connection to github.com closed.
```

本来はこのステップで `hostB` に新しく秘密鍵を作る必要がありましたが、SSH Agent をフォワーディングしているおかげで `hostA` (自分のパソコン) の秘密鍵を使って接続することができます。

### 5. (optional) 自動でフォワーディングする
`~/.ssh/config` で以下のように記述すると、そのホストへの SSH では毎回自動でフォワーディングが行われます。オフにしたい場合は、 `yes` の部分を `no` に変更します。

```ssh
Host hostB
    ForwardAgent yes
```

また、全ての接続先に対してフォワーディングを行うように設定することもできますが、十分セキュリティに配慮して使う必要があります。

```ssh
Host *
    ForwardAgent yes
```

手順は以上です。

## まとめ
SSH Agent とそのフォワーディング機能を使うことで、 SSH する先々で秘密鍵を作る必要がなくなります。ただ、使い方によってはセキュリティ的な懸念が生まれると見ることもできますので、充分注意しましょう。[^disclaimer]

[^disclaimer]: 何か事故が起こった場合、この記事の筆者は責任を負いません。
inputのradioボタンをカスタマイズしようとして、borderが効かない時・・・

#はじめに
radioボタンのカスタマイズ、**超めんどくせぇ・・・**

そう思いませんか？擬似要素を使ったり、何か色々とコネコネせんとダメっぽい・・

と言うのを、思い知った記念の記事となります！

##環境
・Nuxt.js
・bootstrap



#あれ？radioボタンの色が変わらんのやけど？？
普通にinputタグの`background`と`border`を変えたらいいのかな〜？とか思ってると、全然ダメでした。。。

```vue:index.vue
<template>
  <div>
        <input
          type="radio"
          v-for="j in question.num"
          :key="j"
          :value="Number(j)"
        />
  </div>
</template>

<style scoped>
input {
  background: greenyellow;
  border: red;
  width: 20px;
  height: 20px;
}
</style>
```

![スクリーンショット 2021-10-31 15.44.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/167220/55476570-8598-2701-b5f1-9e2119fcb2fc.png)
**なんでやねん！！:weary:**

#結論、擬似要素を使わないと無理です！！
「みんなやたらと擬似要素使ってるな〜」とか思ってると、、**そもそも擬似要素を使わないとinputをカスタマイズすることは無理っぽいようでした・・:sweat_smile:**



```vue:index.vue
<template>
  <div>
        <input
          type="radio"
          v-for="j in question.num"
          :key="j"
          :value="Number(j)"
        />
  </div>
</template>

<style scoped>
input[type="radio"] {
  -webkit-appearance: none;  /* フォーム要素のスタイルを初期化 */
  width: 20px;
  height: 20px;
  border: 1px solid red;
  border-radius: 50%;
}
/* 内側のマル */
input[type="radio"]:before {
  content: "";
  display: block;
  width: 60%;
  height: 60%;
  margin: 20% auto;
  border-radius: 50%;
}
/* 選択された時の内側のマルの色 */
input[type="radio"]:checked:before {
  background: greenyellow;
}
</style>
```

![スクリーンショット 2021-10-31 16.00.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/167220/fcd7864e-92ce-df83-09a4-ac459a22138f.png)

##重要なポイント
いくつか重要なポイントをご紹介！
###まずはinputを初期化しないといけません！！

```css
-webkit-appearance: none;  /* フォーム要素のスタイルを初期化 */
```

👆これ超重要！
これがないと`border`が効きませんでした:disappointed:
これを追加することによって、自由にinputをカスタマイズすることが可能になります。


###checked attributeを追加させるような処理は、特に何も書いていません！！
checked attributeとは👇こーゆーの

```html
<input type="radio" checked>
```





もしchecked attributeが無いと、こんな感じになってしまいます！

```css
input[type="radio"] {
  -webkit-appearance: none;
  width: 20px;
  height: 20px;
  border: 1px solid red;
  border-radius: 50%;
}

input[type="radio"]:before {
  content: "";
  display: block;
  width: 60%;
  height: 60%;
  margin: 20% auto;
  border-radius: 50%;
  background: greenyellow;
}
```

![スクリーンショット 2021-10-31 16.10.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/167220/8f46cbc7-2df8-1319-ea33-739cdb48a163.png)

#参考

https://newbedev.com/javascript-change-radio-button-border-color-css-code-example
M1 mac + laradockのapache2とMySQLエラー回避(おまけXdebug3設定)

# M1 macでのlaradockの問題点

仕事でlaravelを使用しており、ローカル開発環境としてlaradockをIntel macで使用していましたが、最近M1 macに移行したところ、laradockでいくつか問題点があったので回避策を調べました。

# 問題1 apache2が起動しない

まず、M1ではapache2のコンテナが起動しません。

## 方法

`/apache2/Dockerfile`を修正

```
// 1行目を修正
FROM --platform=linux/x86_64 webdevops/apache:ubuntu-18.04

// 最後の行に下記を追加
RUN wget -O "/usr/local/bin/go-replace" "https://github.com/webdevops/goreplace/releases/download/1.1.2/gr-arm64-linux" \
    && chmod +x "/usr/local/bin/go-replace" \
    && "/usr/local/bin/go-replace" --version
```

ちなみにnginxはM1でも普通に動きます

# 問題2 MySQLが起動しない

同様にMySQLも起動しません。
ネットで調べるといろいろ情報がありますが、自分の場合は下記の変更だけで起動しました。

## 方法

`/mysql/Dockerfile`を修正

```
// 2行目を修正
FROM --platform=linux/x86_64 mysql:${MYSQL_VERSION}
```

これで起動しない場合は、`.env`の`DATA_PATH_HOST`で指定しているディレクトリを削除してください。

# おまけ

### Xdebug3の設定

PHPのバージョンによっては（PHP8.0など）Xdebugのバージョン3がインストールされますが、設定のパラメータ名がバージョン2から変わっており、うまくPhpStormでステップ実行ができませんでした。

## 方法

`/php-fpm/xdebug.ini`と`/workspace/xdebug.ini`の最後の行に下記を追加

```
xdebug.client_host="host.docker.internal"
xdebug.discover_client_host=0
xdebug.client_port=9000
xdebug.mode=debug
xdebug.start_with_request=yes
xdebug.output_dir="~/xdebug/phpstorm/tmp/profiling"
```

.envの`WORKSPACE_INSTALL_XDEBUG`と`PHP_FPM_INSTALL_XDEBUG`を`true`に変更してください。

PhpStormでステップ実行すると、ポート周りのワーニングが出ますが、一応ステップ実行はできます。

# おまけ2

### 環境構築のコマンド一覧

#### コンテナビルド

LAMPとredis、phpmyadmin、redis-webuiを構築

```
docker-compose build --no-cache apache2 php-fpm mysql workspace redis phpmyadmin redis-webui
```

#### コンテナ起動

```
docker-compose up -d apache2 mysql workspace redis phpmyadmin redis-webui
```

#### サーバーの中からlaravelのコマンドやマイグレーションをしたい時

```
docker-compose exec workspace bash
```

サーバーにsshで入ったような時のような感じで操作できます。

#### コンテナ停止

```
docker-compose stop
```

#### コンテナ削除

```
docker-compose down
```

#### phpmyadminへのアクセス（デフォルト設定）

```
localhost:8081

サーバー:mysql
ユーザー:root
パスワード:root
```

#### redis-webuiへのアクセス（デフォルト設定）

```
localhost:9987

認証ID:laradock
認証PW:laradock
```

#### コンテナ再構築

エラーやPHPバージョン変更などでコンテナを再構築するときは、自分は下記のように一旦コンテナを削除してから再構築してます。
もっと簡略化できる場合もあります。

```
docker-compose down
docker-compose build --no-cache apache2 php-fpm mysql workspace redis phpmyadmin redis-webui
docker-compose up -d apache2 mysql workspace redis phpmyadmin redis-webui
```

# うまくいかない時

上記でも書きましたが、うまくいかない時は`.env`の`DATA_PATH_HOST`で指定しているディレクトリを一旦削除してから、`build`すると直る場合があります。
pyenv + pipenv環境構築(python)から、pythonファイル実行まで

<h1>python環境構築</h1>

pythonの環境を構築していく。今回のpythonの環境はpyenv + pipenvを使用する。


homebrewは入っている状態のパソコンで作業する。
https://qiita.com/zaburo/items/29fe23c1ceb6056109fd

<h1>pyenvとは</h1>
pyenvとは、システム上に複数バージョンのPythonを同居させて使い分けられるようにするものであり、pyenvを用いることでpythonのさまざまな環境を使い分けることができる。

pyenvインストール

```php
#　brewが入っているコマンドラインで
brew install pyenv
```

<h1>pipenvとは</h1>
pipenvとはpip と venv の両方の機能を兼ね備えたサードパーティ製のパッケージ管理ツールである(下記urlより)
https://rinatz.github.io/python-book/ch04-05-pipenv/

pipはpythonのパッケージを管理するためのツールであり
venvとは、Pythonが動作する仮想的な環境（virtual environment）を作り出すことができるツールである。

要するに、pipenvはpythonのパッケージを管理できる要素とpython環境を構築できる要素を兼ね備えたツールである。

pipenvインストール

```php
brew install pipenv
```



<h1>環境構築</h1>

```php
// pyenvに入っているversion確認
% pyenv versions
#  system
#  3.6.3
#  3.6.5
#  3.6.7
#  3.6.8
#* 3.7.4 (set by /Users//.pyenv/version)

// 使いたいversion指定
% pyenv local 3.6.7

//もう一回version確認。versionが変わっている
% pyenv versions   
#  system
#  3.6.3
#  3.6.5
#* 3.6.7 (set by /Users//Self-development/qiita/pipenv/.python-version)
#  3.6.8
#  3.7.4

//pipenvの環境を作る
% pipenv --python 3.6.7
#・
#・
#・
#Creating a Pipfile for this project...

// pipfile生成された
% ls
# Pipfile

// 作ったpipenv環境に入る
pipenv shell
# Launching subshell in virtual environment...
# ・・・
```

パッケージインストール

```php
// パッケージインストール
(pipenv) pipenv % pipenv install pandas
#Installing pandas...
# Adding pandas to Pipfile's [packages]...
# ✔ Installation Succeeded 
# Pipfile.lock not found, creating...
# Locking [dev-packages] dependencies...
# Locking [packages] dependencies...
# Building requirements...
# Resolving dependencies...
# ✔ Success! 
# Updated Pipfile.lock (8943a4)!
# Installing dependencies from Pipfile.lock (8943a4)...
#  🎃   ▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉ 0/0 — 00:00:00

```

python実行

```php
// panda.pyの中身確認
pipenv % cat panda.py   
# import pandas as pd
# d = {'col1': [1, 2], 'col2': [3, 4]}
# df = pd.DataFrame(data=d)
# print(df)

pipenv % python panda.py
#   col1  col2
#0     1     3
#1     2     4
```
OpenAPI GeneratorでSlim3向けのソースコードを生成

訳があって未だにPHP 5.6を使用している環境があり、Slim3でWeb APIを実装することになった。
ドキュメンテーションが面倒なので、[Swagger Editor](https://editor.swagger.io/)でOpenAPIを記述し、そこで生成したyamlをOpenAPI Generatorを使用してWeb APIのスタブコードに変換することにした。
その際、最初に行ったことを以下に記録して置く。

参考先：https://qiita.com/amuyikam/items/e8a45daae59c68be0fc8

# 作業環境

| 項目   | 内容                    |
|--------|-------------------------|
| OS     | LinuxMint 18.3 (Sylvia) |
| Docker | 18.09.7                 |
| Apache | 2.4.25 (Debian)         |
| PHP    | 5.6.40                  |

# OpenAPI GeneratorのDockerコンテナの利用

作業は参考先の内容にしたがってOpenAPI GeneratorのDockerコンテナを使用したが、最新のコンテナではSlim3が未対応になっているため、以前の[バージョン3.3.4](https://hub.docker.com/layers/openapitools/openapi-generator-cli/v3.3.4/images/sha256-3bba4c05932d0d0ad6180f3c1997a831d7e32320044c135c461e51fac3748755?context=explore)を使用した。

# Web APIスタブへの変換

参考先に記載の（[Swagger Petstore](https://petstore.swagger.io/)の簡易版）petstore.yamlを、以下のコマンドでWeb APIのスタブコードに変換することができた。

```
docker run --rm -v ${pwd}:/local openapitools/openapi-generator-cli:v3.3.4 \
generate -g php-slim -i /local/petstore.yaml -o /local/petstore
```

カレントディレクトリ下のpetstoreの内容は以下の通り。

```
.
├── README.md
├── composer.json
├── index.php
├── lib
│   ├── AbstractApiController.php
│   ├── Api
│   │   └── PetsApi.php
│   ├── Model
│   │   ├── Error.php
│   │   └── Pet.php
│   └── SlimRouter.php
├── phpunit.xml.dist
└── test
    ├── Api
    │   └── PetsApiTest.php
    └── Model
        ├── ErrorTest.php
        └── PetTest.php
```

- index.php で 実体化する SlimRouter のソースコードは lib/SlimRouter.php であり、ここに /v1/pets へのルートが記述されている
- GET /v1/pets の実装は lib/Api/PetsApi.php に listPets メソッドとして定義されている
- listPets メソッド名は petstore.yaml に operationId として定義されている
- PetsApi.php ファイル名の Pets は petstore.yaml に tags として定義されている
- petstore.yaml の components/schemas に定義されている Pet と Error に対応するソースコ
ードは lib/Model 下に作成される（Pets のは作成されない）

# Web APIを呼び出す

以下の手順でWeb APIを呼び出すことができた。

- Apacheの .htaccess が有効なドキュメントルート以下に、作成された petstore ディレクトリをコピー
- コピー先の petstore ディレクトリに移動して、 `composer install` を実行（いくつかのパッケージを諦めたログが出力されたが無視）
- `curl http://＜ホスト名＞/v1/pets` を実行すると、 `How about implementing listPets as a GET method ?` と表示されることを確認
rm -rf / をやってみて残ったもの

# はじめに 

[Bashの邪悪なコマンド9選](https://qiita.com/rana_kualu/items/be32f8302017b7aa2763)に「`rm -rf /`やったことある人。」と書かれているのを見て、大昔に試したのを思い出しました。`rm -rf /`をやってしまったのでは無く**`rm -rf /`をやってみた** のです。当時の記憶を呼び起こしてここに書き留めてみます。


# どういうシチュエーション？

話は1995年頃に遡ります。ある日、展示会か何かで短期のレンタルで借りた [SPARCstation 5](https://ja.wikipedia.org/wiki/SPARCstation) あたり(古い話なのでさすがにうろ覚え)を返却することになりました。OS は [SunOS](https://ja.wikipedia.org/wiki/SunOS) の 4.1.3 か 4.1.4 あたりでしょう(当時 Solaris 2系も有ったけど、手元では使っていない)。

レンタル品であれば、次に貸し出す場合にOSはインストールし直すはずなので、データが残っている必要性はありません。いい機会なので `rm -rf /` をやってみようということになりました。

`rm -rf /`は危険な操作の一つとして古くから知られていました。もちろんファイルシステムの全てのファイルが無くなるのは誰もが理解するところです。と、頭では理解できていますが、実際に実行すると果たしてどうなるのかについては、自分を含め周りの人も知りません。なので是非とも一度確認してみたかったのです。

当時の記録はさすがに残っておらず、以下は雰囲気を出す為に画面イメージを適当に作っているため、実際のSunOSの表示とは少し違うかもしれません。

# rm -rf / の実行

まずは root でログインしました。

```
SunOS 4.1.4
login: root
password:

# pwd
/
# 
```

今時の多くのUNIX系OSではrootアカウントのホームディレクリエーションは`/root`ですが、当時のSunOSでは`/`でした。

それでは、おもむろに`rm -rf /`を実行します。

```
# rm -rf /
```

今時のLinuxの`rm`コマンドであれば、これを実行しようとすると警告が出て実行させてもらえませんが[^rm]、当時のUNIX環境の`rm`コマンドにそんな芸はなく、なんの警告も無くさくっと処理が進みます。

[^rm]: 実行するためには`--no-preserve-root`オプションを指定する必要があります。

実際に入力したコマンドは `rm -rf /` ではなく

```
# rm -rf *
```

だったかもしれません[^star]。

[^star]: この方法だと`/.profile`などの`/`直下の「.」で始まるファイルは残ります。

# rm -rf / の実行結果

しばらくディスクのアクセス音が続きます。おそらく10分以上待ったと思います。無事(?)プロンプトが戻ってきました。

さて、どうなった？と思い`ls`を実行してみます。

```
# ls
ls: not found
#
```
自分を含めて周囲からも「お〜、lsが無い！」などと言う声が上がります(笑)。`ls`コマンドの実体も削除されてしまったので当然の結果ですね。状況を確認するためにはどうすれば？ということで少し考えて`echo *`でいけることに気づきました。

```
# echo *
export tmp usr var
# 
```

これらはファイルでは無くディレクトリで、4個のディレクトリが残っていることが確認できました。`cd`コマンドなどと組みあわせて配下のファイルやディレクトリを確認しましたが、他は綺麗さっぱり無くなっていました。

# なぜこれらのディレクトリが残ったのか

さて、なぜ一部のディレクトリが残り、残ったものは消えてしまったディレクトリとは何が違うのでしょうか。

当時のSunOSでは単純にインストールした場合、1台のディスクが複数のパーティションで構成されて、`/usr, /var, /export`はそれぞれマウントポイントとなります。また`/tmp`にはtmpfsがマウントされています。マウントポイントはディスクがマウントされた状態では削除できません。rm コマンドではマウントしたディスクのアンマウントは行わないため、これらが残ったというお話でした。

# その後

haltやshutdown等のサーバーを停止するために必要なコマンドも消えてしまったため、最後はいきなり電源を切って終了した気がします。
Webサイト・Webサービスを３０秒でスマホアプリ化する方法

# はじめに
ネタバレ：スマホアプリのブラウザで対象のWebサイト・Webサービスを表示するだけです。

PWA化すればいいじゃんって事かもしれないですが、ストアに掲載できないので。
「PWA2APK」というPWAからAPKを生成してくれる便利なサービスがあるものの、うまく動作せず、
力技を使いました。

#### メリット
・３０秒でスマホアプリ化出来る
・ウェブアプリとスマホアプリの二重管理が無くなる
・知識が浅い人に軽い気持ちで「これスマホアプリ化出来る？」って言われた時の対処方法

#### デメリット
・ストアに掲載できる以外、スマホアプリであるメリットが一切ない
　（でも、ブラウザ表示以外のウィジェットを別途用意すれば機能追加できるかも）

# 実装
Flutterを使います。
ウェブブラウザのライブラリさえあれば、別の言語やフレームワークでも出来ると思います。

[webview_flutter](https://pub.dev/packages/webview_flutter)というライブラリを使います
pubspec.yamlに「webview_flutter: ^2.1.2」を追加してください

```web_view_screen.dart
import 'dart:async';
import 'dart:io';

import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';

class WebViewScreen extends StatefulWidget {
  @override
  _WebViewScreenState createState() => _WebViewScreenState();
}

class _WebViewScreenState extends State<WebViewScreen> {
  final Completer<WebViewController> _controller =
      Completer<WebViewController>();

  @override
  void initState() {
    super.initState();
    if (Platform.isAndroid) {
      WebView.platform = SurfaceAndroidWebView();
    }
  }

  @override
  Widget build(BuildContext context) {
    
    return Scaffold(
          body: SafeArea(child: _buildBody()),
        );
  }

  Widget _buildBody() {
    return Column(
      children: [
        Expanded(
          child: _buildWebView(),
        ),
      ],
    );
  }

  Widget _buildWebView() {
    return WebView(
      initialUrl: 'https://modern-board-vm.web.app',
      javascriptMode: JavascriptMode.unrestricted,
      onWebViewCreated: _controller.complete,
    );
  }
}

```

「initialUrl」に対象のウェブサービスのURLを書くだけです
おわり。

Flutterで作っているので、AndroidアプリにもiPhoneアプリにも出来ます。
どっちのストアにも掲載できると思います。


# 実際の画像

<img width="400" alt="mojikyo45_640-2.gif" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/911989/64d05832-c659-78bd-0968-2c43afa87e1a.png">

<img width="400" alt="mojikyo45_640-2.gif" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/911989/aa4f8cd6-1e66-13a5-0670-e59e06c215d9.png">

<img width="400" alt="mojikyo45_640-2.gif" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/911989/565819dc-2269-457a-b574-29ba17723029.png">
ghを導入したらDXが爆上がりした話

GitHubの公式CUI [ghコマンド](https://github.com/cli/cli)を使うようになってから、開発してく中でのストレスが減ったなーと気づきがあり、これ結構DX（開発体験）上がったんじゃね？と思ったので記事にしてみます。

## 想定読者

* GitHubのPull Request（以下、PR）をよく使う方
  * GitHubのCUIなのでGithub使ってない方には興味なさげな話です
  * その中でもPRにフォーカスした話をしようと思ってます
* ghコマンドを使ったことがない方、使い始めの方
  * ghコマンドの基本的な話をするので、バリバリ使いこなしてる方にはそんなの知ってるわって話かもしれません
* zsh使ってる方に特にオススメします
  * 詳細は後述ですがzshとの相性が抜群です
  * zsh使ってない方はラッパー用意するとか一手間加えることになるかもです
* エンジニアの方
  * 違う方のDX(Digital Transformation)の話は一切出てきません🙇‍♂️

## TL; DR

* gh使うとPRに毎回設定する項目を指定するのが楽になるよ
* PR項目の設定漏れも無くなるよ
* コーディング中のアイデアとか疑問とかレビュアーに伝えたいこととかをメモする場所が定まるよ
  * 個人的にはこれが1番のPR（ぴーあーる :innocent: ）ポイントです

## ghとは

ghとはGitHubが公式に提供しているCUI（コマンド）です。
PRを発行したり、Issueを立てたりといった、GitHubの基本的な機能は網羅してるんじゃないですかね（投げやり）

機能が膨大なのでいきなり全部使いこなそうと思うと逆に取っ付きづらいので、PR発行機能に話を絞ります。

## ghのPR発行機能

ghコマンドを使うことでCUIからPRを発行することができるようになります。
文法は以下です。

```
gh pr create [options]
```

PRを発行したいbranchをcheck outしている状態でコマンド入力すると、GitHub上にPRが発行されます。

ここで大事なのがoptionが豊富なことです。
PRに設定できる項目は大体ここで指定することができます。
よく使うoptionを紹介します。

|option|説明|
|---|---|
|-F file|本文をファイルから読み取る。|
|-r user|レビュアーの設定。|
|-a user|担当者のアサイン。 `@me` で自分をアサインできる。|
|-t title|タイトルの設定。|
|-m milestone|マイルストーンの設定。|
|-l label|ラベルの設定。|
|-B branch|ベースブランチ（PRのマージ先）の設定。<br>設定しなければデフォルトブランチ（developなど）向けになる。|

特にオススメしたいのが `-F` optionです。
こちらを使うことにより、PRの本文をいつでも作成することができるようになります。私はテンプレートになるファイルを作っておいて、それをコピーして使い回してます。PR発行直前に作ることもあるし、コーディング中にこれ特にレビュアーに伝えとかないといけないなーと思った時点でメモして後で編集したり。作業中のコーディングに関することをここに集約することができるようになります。

また発行したPRをWEBブラウザで開く機能もあります。こちらは `gh pr view --web` です。これをPR発行のあとに `&&` で繋げてあげるとPR発行後にブラウザで微調整するみたいなことができるようになります。

コマンドの全体像はこんな感じになります。

```
gh pr create -F ~/somePath/xxx.md -m 'someMilestone' -a '@me' -r 'someReviewer' -r 'anotherReviewer' -t 'タイトル' -l 'someLabel' && gh pr view --web
```

これで面倒なレビュアーの指定やマイルストーンの指定がコマンドからできるようになりました👏 Hooray！🎉

…

でも、きっとあなたはこう思うでしょう…

コマンドが長すぎる…

私もそう思いました…思っていた時期がありました！でもzshのコマンド履歴からの入力補完が使えると、そんな心配はまったく不要でした！

zshのプラグインである[zsh-autosuggestions](https://github.com/zsh-users/zsh-autosuggestions)を導入することで、コマンド履歴から入力候補を補完してくれるようになります。
（導入手順は[こちら](https://qiita.com/hinatades/items/d38be4830191f251935d)などが参考になると思います）
`gh` と入力するだけでオプション指定を復元することができます。
後はタイトルを修正したり微調整すれば大抵は事足ります。
これがなければghコマンドが自分の中でここまで定着することがなかっただろうなと思うくらい相性がよく、程よい柔軟性で運用を続けることができています。

## まとめ

ghコマンドによって、PR発行時の煩わしさから解放されました。
正直使い始める前まではそれほど煩わしさを感じてはいなかったのですが、慣れてしまうともう後戻りできないと感じています。
参考になれば幸いです🙏
Laravel Tinkerの使い方

# はじめに
LaravelでクエリビルダやEloquentを用いて、実際にデータの取得ができるか確かめたい時に便利です。
コマンドにその処理を書く事で、実際にどういったデータが取得できるか確認できます。

# 目次
<!-- タイトルとアンカー名を編集 -->
1. [tinkerを起動する](#tinkerを起動する)
2. [試したい処理を書く](#試したい処理を書く)
3. [上記の処理で取得したデータを表示](#上記の処理で取得したデータを表示)
4. [tinkerを終了する](#tinkerを終了する)

<!-- 各チャプター -->
<a id="#Chapter1"></a>
# Tinkerを起動する
tinkerを使用するには下記コマンドを入力

```
$ php artisan tinker
```

<a id="#Chapter2"></a>
# 試したい処理を書く
ユーザーidが1であるユーザーの情報が取得できるか確かめたい。

```
User::where('id', 1)->get();
```

<a id="#Chapter3"></a>
# 上記の処理で取得したデータを表示

```
=> Illuminate\Database\Eloquent\Collection {#4753
     all: [
       App\Models\User {#4741
         id: 1,
         name: "山田　太郎",
         email: "test@exampe.co.jp",
         created_at: "2021-10-23 16:59:02",
         updated_at: "2021-10-23 16:59:02",
         deleted_at: null,
       },
     ],
   }
```

<a id="#Chapter4"></a>
# Tinkerを終了する
q を入力してEnterでTinkerを閉じる。

```
>>> q
```
CloudWatchのダッシュボードをCLIで設定する

最近、仕事でCloudWatchのダッシュボードを設定する機会がありました。
しかし、Webコンソールで一つずつ設定するのはとても面倒に感じたので、`aws cli`を使用して設定していきます！

## 前提
今回は、事前にElastic Beanstalkで作成した環境(下記2点)をダッシュボードに設定する。
（こちらの監視対象環境はEC2でもRDSでも何でも良い）

**Elastic Beanstalk**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/558865/637afb72-67fc-201f-aa7c-2cb8a9e2a0ce.png)

**EC2 (Elastic Beanstalkで作成されたEC2)**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/558865/df81bd4e-beb6-c737-0a1c-159723500cef.png)


## json作成
widgetsという配列変数に、モニタリングしたい分だけ設定。
今回はメモリとディスクの統計情報をモニタリングできるようにする。

~~~json:test.json
{
  "widgets": [
    {
      "type": "metric",
      "x": 0,
      "y": 0,
      "width": 6,
      "height": 6,
      "properties": {
        "view": "timeSeries",
        "stacked": false,
        "metrics": [
          [
            "AWS/EC2",
            "MemoryUtilization",
            "InstanceId",
            "i-0accd6fcf9ecc2d61"
          ]
        ],
        "region": "us-east-2"
      }
    },
    {
      "type": "metric",
      "x": 6,
      "y": 0,
      "width": 6,
      "height": 6,
      "properties": {
        "view": "timeSeries",
        "stacked": false,
        "metrics": [
          [
            "AWS/EC2",
            "DiskUsage",
            "InstanceId",
            "i-0accd6fcf9ecc2d61"
          ]
        ],
        "region": "us-east-2"
      }
    }
  ]
}
~~~

## コマンド実行
サブコマンドput-dashboardでダッシュボードを登録し、--dashboard-nameオプションでダッシュボード名指定、--dashboard-bodyオプションで先ほど作成したjsonファイルを指定。

jsonファイルを指定方法は「file://ファイルパス」という形式。
そうじゃないと以下のエラーが出る。
`An error occurred (InvalidParameterInput) when calling the PutDashboard operation: The field DashboardBody must be a valid JSON object`

~~~bash:ターミナル
$ aws cloudwatch put-dashboard --dashboard-name test --dashboard-body file://test.json
{
    "DashboardValidationMessages": []
}
~~~

## ダッシュボードを確認
testダッシュボードが作成されている。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/558865/cd75a7ba-ff80-7799-e6ce-3d3591a8a8f3.png)

testダッシュボードの中に先ほど設定したメモリとディスクの統計情報のモニタリングができるようになっている。
※インスタンスに何もしてないのでデータは無いが・・

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/558865/3e0f2cd2-3df9-9095-19d2-a839f867e745.png)

## 終わりに
とりあえず、CLIでCloudWatchのダッシュボードを作成できました。
しかし、そもそもCloudWatchに関しての理解が乏しいので、そこをしっかり勉強して使いこなせるようになりたいです。

## 参考
https://dev.classmethod.jp/articles/cloudwatch-dashboard-api-using-awscli/
https://aws.amazon.com/jp/blogs/news/new-api-cloudformation-support-for-amazon-cloudwatch-dashboards/
最近webpackで環境構築したので利用したプラグインまとめてみた

#はじめに
こんにちは〜
最近フロントエンドの環境構築の勉強でwebpackを利用しました。
その際、プラグイン多いな！！！
と感じたので、自分なりにプラグインをまとめてみました！

#そもそもwebpackって？
コンポーネント化が進む時代・・・build時に各種ファイルを1つにバンドルしてくれるプリプロセッサがwebpackです。

公式の画像がわかりやすいので添付します。

![スクリーンショット 2021-10-31 15.12.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542241/bef46596-72e0-bd50-b078-8e1548d8ecf5.png)

このように、webpackが依存関係も込でいい感じにファイルを1つにまとめてくれます。
あとは、バンドルしてくれたファイルを読み込めば解決になります。

素晴らしいですね！

このバンドル時に数々あるプラグインを利用することでより便利になりますので、今回はそちらを紹介いたします。

#プラグイン一覧

| プラグイン名 | プラグインの特徴 |
|:----------|:-------------:|
| webpack   |webpackを使えるようにするためにとりあえずInstallする|
| webpack-cli|開発者がカスタムwebpackプロジェクトをセットアップする際の速度を上げるための柔軟なコマンドセットを提供|
|html-loader|webpack用のhtmlローダーモジュール|
|css-loader|webpack用のcssローダーモジュール|
|file-loader|webpack用のfileローダーモジュール|
|html-webpack-plugin|バンドルを読み込んだ HTML を出力するプラグイン|
|mini-css-extract-plugin|バンドルされる CSS を別の CSS ファイルに抽出する|
|optimize-css-assets-webpack-plugin|CSS を最適化するプラグイン|
|webpack-merge|設定をマージする関数|
|terser-webpack-plugin|JavaScritpt を圧縮するプラグイン|
|@babel/core|Babelを利用できるようにする|
|@babel/preset-env|対応ブウラウザなどの大雑把な情報を元に、必要なプラグインを自動で選択して、最新の ES6+ を動く状態にしてくれる|
|autoprefixer|バンドルする際にベンダープレフィックスを設定する|
|babel-loader|babelをwebpackで利用できるようにする|
|clean-webpack-plugin|build時に利用していないファイルを削除してくれる|


ざっとこんな感じです。
本当に便利なプラグインが多いですね。。。
他にも使えそうなプラグインがあったり、便利な書き方があったりしたので、次回の機会にアウトプットしようと思います。

ありがとうございました。
python: 内包表記で組合せ(コンビネーション)を列挙する

異なるn個のものからr個のものを選び出す組合せ(コンビネーション)を列挙する。

```python:5個から2個を選ぶ
[[a, b] for a in range(5) for b in range(a)]
```
```python:
 [[1, 0],
 [2, 0],
 [2, 1],
 [3, 0],
 [3, 1],
 [3, 2],
 [4, 0],
 [4, 1],
 [4, 2],
 [4, 3]]
```

```python:5個から2個を選ぶ(ビットパターン)
[1<<a | 1<<b for a in range(5) for b in range(a)]
```
```python:
[3, 5, 6, 9, 10, 12, 17, 18, 20, 24]
```

```python:6個から3個を選ぶ
[[a, b, c] for a in range(6) for b in range(a) for c in range(b)]
```
```python:
[[2, 1, 0],
 [3, 1, 0],
 [3, 2, 0],
 [3, 2, 1],
 [4, 1, 0],
 [4, 2, 0],
 [4, 2, 1],
 [4, 3, 0],
 [4, 3, 1],
 [4, 3, 2],
 [5, 1, 0],
 [5, 2, 0],
 [5, 2, 1],
 [5, 3, 0],
 [5, 3, 1],
 [5, 3, 2],
 [5, 4, 0],
 [5, 4, 1],
 [5, 4, 2],
 [5, 4, 3]]
```
php-fpmが起動しまくっている

top コマンドやったらphp-fpmが起動しまくっている。
よく理由はわからんが。

php-fpm の設定ファイルを探す


`find / -name "*www.conf*" -print`

以下に変更

``` /etc/opt/remi/php71/php-fpm.d/www.conf

pm.max_requests = 50

```



>各子プロセスが、再起動するまでに実行するリクエスト数。 サードパーティのライブラリにおけるメモリリークの回避策として便利です。 再起動せずにずっとリクエストを処理させる場合は ‘0’ を指定


再起動

```

systemctl restart php-fpm.service

```


`free -h` で確認

```

処理前

              total        used        free      shared  buff/cache   available
Mem:           1.8G        653M        172M        186M        1.0G        779M
Swap:          2.0G        587M        1.4G

```


```

処理後

              total        used        free      shared  buff/cache   available
Mem:           1.8G        133M        787M         76M        918M        1.4G
Swap:          2.0G        237M        1.8G

```



とりあえず、これで様子見ますか

参考
https://blog.oyasu.info/2018/02/17/6259/

さらなるチューニングはこちら
https://blog.adachin.me/archives/2317
eslint doc

# ESLintとは
Find and fix problems in your JavaScript code.
以下の３つの特徴を持つ。

[ESLint Officail Document](https://eslint.org/docs/user-guide/configuring/)

## Find Problems
JavaScriptコードを静的に解析し問題を見つける。

## Fix Automatically
ESLintが見つけたコードの問題を自動修正する。

## Customize
パーサーや独自のルールを用いてESLintに組み込まれたルールをカスタマイズできる。

# 設定ファイル
設定ファイルはJavaScript,JSON,YAMLファイルで定義できる。

- .eslintrc.js
- .eslintrc.yaml　
- .eslintrc.yml
- .eslintrc.json


## 各プロパティ内容
ESLintは初期状態では何もチェックをしてくれない。そこで以下のようなプロパティを追加していくことによって、チェックルールを追加する。

- root
- env
- globals
- extends
- plugins
- Rules
- parser
- parserOptions

## root
ESLintによる検証ルールには優先順位がある。

1. Lint対象ファイル （inline configuration)
2. Lint対象ファイルと同階層にある設定ファイルのrules
3. Lint対象ファイルの同階層にある設定ファイルのextends
4. Lint対象ファイルの親階層にある設定ファイルのrules
5. Lint対象ファイルの親階層にある設定ファイルのextends

4,5のように親階層の設定ファイルを参照しないようにするためには`root: true`を指定すれば良い。

## env
scriptがどの環境で実行されるかを指定する。設定することで各環境で事前に定義されているグローバル変数を設定する。

利用可能な主な環境:
`browser`, `node`, `es2021`, `worker`, `mocha`

## global
ESLintは実行環境にグローバル変数が存在するかどうかは想定していない。グローバル変数が必要なルールを使用する場合に設定ファイルや、Lint対象ファイルにコメントすることでグローバル変数を追加できる。
`true`は`writable`に等しく、`false`や`reeadable`は`readonly`に等しい。

```global.json
"globals": {
  "alert": false,  
  "document": false,
  "$": false,
  "console": false
}
```

## extends
ESLintには多くのルールがあるが、それを１つ１つ指定していくのは効率的ではない。
このプロパティを使うことで、グループ化されたルールを読み込むことができる。

### eslint:recommended
ESLintがおすすめするルール
https://eslint.org/docs/rules/

## rules
ESLintにはビルトインされたたくさんのルールがあり、プラグインを通してルールを追加することもできる。このプロパティによって、プロジェクトで使用するルールを変更できる。
ルールを変更するには、以下の数値を設定する必要がある。

| 設定 | 省略系 | 意味 |
|:-:|:-:|:-:|
| off | 0  |  ルールをチェックしない |
| warn  | 1  | ルールに違反すると警告を出す  |
|  error | 2  |  ルールに違反するとエラーが出る  |


オプションがあるルールもあるのでその場合は配列を用いる。

```js
/* eslint quotes: ["error", "double"], curly: 2 */
```


## plugins
ESLingはpluginsの使用をサポートしており、これを使用するためにはnpmでインストールしている必要がある。

pluginsの特徴

- Plugins can expose additional rules for use in ESLint.
- Plugins can expose additional environments for use in ESLint.

pluginsを指定する時`eslint-plugin-`prefixは取り除くことができる。
pluginsのrules,environmentsなどを使うときは以下のように指定する。


```json
{
    // ...
    "plugins": [
        "jquery",   // eslint-plugin-jquery
        "@foo/foo", // @foo/eslint-plugin-foo
        "@bar"      // @bar/eslint-plugin
    ],
    "extends": [
        "plugin:@foo/foo/recommended",
        "plugin:@bar/recommended"
    ],
    "rules": {
        "jquery/a-rule": "error",
        "@foo/foo/some-rule": "error",
        "@bar/another-rule": "error"
    },
    "env": {
        "jquery/jquery": true,
        "@foo/foo/env-foo": true,
        "@bar/env-bar": true,
    }
    // ...
}

### ExtendsとPluginsの違い
https://blog.ojisan.io/eslint-plugin-and-extend/

## parser
ESLintはデフォルトで[Espree](https://github.com/eslint/espree)というパーサーを用いている。このプロパティで違うパーサーを指定できる。

```js
parser: "@typescript-eslint/parser" // typescript parser
```
Excel VBA ファイルを別のフォルダにコピーする

#設定

 - A列にファイルのフルパス。
 - M列にネットワークフォルダがある。ただし、末尾に円マークはない。 \\Server\name\folder のような感じ。

#やりたいこと

 - M列にフォルダが入っている場合だけ、A列のファイルをコピーしたい。
 - <b>【重要】上書きしても構わない。が一応サンプルでは上書きを止めている</B>
 - Scripting.FileSystemObjectを事前に参照設定すること

```VB
Sub DeliverFile()
' For Excel VBA
' Reference Setting Microsoft Scripting Runtime
Dim FSO As New Scripting.FileSystemObject
Dim oFile As File
Dim oFolder As Scripting.Folder
Dim r As Range
Dim LastRow As Long, iCol As Long, iRow As Long
Dim wb As Workbook: Set wb = ThisWorkbook
Dim ws As Worksheet: Set ws = wb.Worksheets(1)
lastrow = ws.Range("A" & Rows.Count).End(xlUp).Row
For iRow = LastRow To 2 Step -1 '  1行目に列見出しが入っているため、最後は2行目になる。こういう時は最後から最初に向かうのがセオリー。
If FSO.FileExists(Cells(iRow, 1)) Then
Set oFile = FSO.GetFile(Cells(iRow, 1))
Set r = ws.Range("M" & iRow)
If r.Value <> "" And FSO.FolderExists(r.Value & "\") Then
Set oFolder = FSO.GetFolder(r.Value & "\")
If Not FSO.FileExists(oFolder.Path & "\" & oFile.Name) Then
FileCopy oFile.Path, oFolder.Path & "\" & oFile.Name
End if
End If
End If
Next
End Sub
```

#ポイント
ネットワークフォルダはN:\などとせずURN表記\\Server\name\を使用する
これに対してA列のソースファイルはN:\といった割りてられたドライブレターでもよい。
最後は末尾の円マークというかスラッシュがないので追加する。
FolderExistsは最後に円マークがないとエラーになる。
FileCopyはVBAならではの命令。これは、コピー先のフォルダ名だけではなく、ファイル名まで続ける。
`Filecopy "E:\hoge\text.txt`, "E:\hogehoge\text.txt"`
という表記になる。
ここでoFile.Pathでフルパス、oFile.NameでBasename+Extentionを使ってそれを実現している。
oFolder.Path と oFile.path と oFile.name は混乱しがちだが、このサンプルでわかると思う。
次に、Folder名の修飾子はScriptingになる。単純に`As Folder`だとエラーになった。しかし、これはほかにも参照設定していたからかもしれない。
ただ、正確にはScriptingになるというのはちょっと思いつかなかった。これはこれからも使えると思う。わかりにくいバグを回避できる。
単純にFolderだけだといろいろなアプリケーションがオブジェクトに含んでいるので、たくさんの参照設定をしていればVBAも混乱するのかもしれない。
こういうエラーもあるということがわかった。またこのようなエラーが起きる場合、明示的に修飾子を使い、エラーを防ぐほうが良いことも分かった。

##課題
これとFSOのコピーを比較してどちらが早いかを調べる。
体感的にはこのFileCopyの方が早いという感じがした。

#留意点
https://www.moug.net/tech/exvba/0060075.html
このFileCopyは上書きを強制するので注意。
上書きを回避するには、この記事の方法のほか、Dir命令などを用いて、ファイルの有無を判定してからコピーする。
PHP

##おねがい
この記事は自分用のメモで書いています。未熟なためウソが交じることがあると思います。
閲覧の際はふーん程度に留めておいていただければと思います。

##変数の作成
・型宣言不要。
・変数名の先頭に数字は不可。
・配列の宣言も同じ書式で先頭に$をつける。

```php5
//変数作成
$moji="こんにちは"; //文字列
$number=1234;      //整数
```

##変数の参照方法
・echo()かprint()で可能。
・双方とも同じっぽい。
・' 'か" "で括るかで参照結果が変わる。

```php5
$number="こんにちは";

echo '<p>$number</p>'; //出力結果：$number
echo "<p>$number</p>"; //出力結果：こんにちは

print '<p>$number</p>'; //出力結果：$number
print "<p>$number</p>";  //出力結果：こんにちは
```

##参考文献
・PHPによるWebアプリケーションスーパーサンプル第２版
・速攻!図解プログラミングPHP+MySQL
Swiftのハーフモーダル

swiftハーフモーダル
--

ハーフモーダル実装したことがなかったのでしてみた。とても簡単でした。
![スクリーンショット 2021-10-31 15.01.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1474285/4d0c6192-7b6c-7928-784d-07ffd69217e7.png)

ハーフモーダルを発火させるボタンと遷移先の画面をそれぞれ用意

```swift
//遷移前画面　prepareをoverrideしてあげる。sheet.detentsを複数指定することによってハーフモーダルと通常モーダルをどちらも使える。
 override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        let next = segue.destination
        if let sheet = next.sheetPresentationController {
            sheet.detents = [.medium(),.large()]
        }
    }

//遷移先画面　IBActionで下記のように設定。　ボタンを押すと、ハーフだったらラージへ、ラージだったらメディアムになるように設定する
animateChangesにすることによって、うまくアニメーションしてうごいていくれる。
@IBAction func change(_ sender: Any) {
        if let sheet = self.sheetPresentationController {
            if sheet.selectedDetentIdentifier == .medium {
            sheet.animateChanges {
                sheet.selectedDetentIdentifier = .large
            }
            } else {
                sheet.animateChanges {
                    sheet.selectedDetentIdentifier = .medium
                }
            }
        }
    }
```

とても簡単に実装できました。
Webサーバを起動した時にJakartaのクラスでエラーが出てしまう

# はじめに
Udemyで購入した講座を見ながら実装していたら以下のエラーが出てしまい先に進めなくなってしまった。

```
java.lang.ClassNotFoundException: jakarta.servlet.http.HttpServletRequest
```
# 環境
MacBookPro MacOS 11.6
Tomcat 9.0.46
Java 1.8.0_292 AdoptOpenJDK
Eclipse 2021-09(4.21.0)

# 解決した方法
Tomcatの10系も入っていてそちらがビルドに使われてしまっていたのでProjectのPropertiesでJava Build Pathの内容を修正した
1. ビルド対象のProjectを右クリックしてPropertiesを選ぶ
![スクリーンショット 2021-10-30 16.42.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/227794/82f6ba49-476e-f358-ebfb-c06bfaf5c434.png)
2. Java Build PathをクリックしてLibrariesタブでServer RuntimeをクリックするとEdit...が表示されるのでそれをクリックする
![スクリーンショット 2021-10-30 16.42.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/227794/05ee4f80-1644-0943-0797-244842d0e56a.png)
3. 今設定されているRuntime以外に使用可能なServerのRuntimeがあれば表示されるので適切なものを選んでFinishをクリックする
表示されなければDownloadしておけば選べるようになるかと思います
![スクリーンショット 2021-10-30 16.43.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/227794/91543736-dd00-cda7-938f-2cda95743f01.png)

# 終わりに
めっちゃ悩んで調べまくり1時間くらい潰したので、今後同じ轍を踏まないようにセットアップの際に要らないものをDownloadしたり残しておいたりしないようにしたいですね。
JavaScript メモ　：定数、変数

##<font color="red">定数</font>

プログラムの中で値に付けられる名前のこと

・定数を作るには、<font color="blue">const</font>とというキーワードの後に定数名を書いて、定数を使いますよと<font color="red">宣言</font>する必要がある。
例）const price = 200;
・上記の＝（イコール）は右の値に左の値を当てはめるという意味。これを<font color="red">代入</font>という。
・再代入できない。

##<font color="red">変数</font>
・変数を作るには、<font color="blue">let</font>もしくはvarとというキーワードの後に変数名を書いて、変数を使いますよと<font color="red">宣言</font>する必要がある。
・再代入できる。
例）
let prece =100;
console.log(price * 10);
console.log(price * 20);

price = 150;(再代入）
console.log(price * 10);
console.log(price * 20);
【Unity】Cinemachine で即座にバーチャルカメラの位置にリセットする

Unity が所持する強力なカメラシステム [**Cinemachine**](https://docs.unity3d.com/Packages/com.unity.cinemachine@2.8/manual/index.html) ですが、位置の初期化でつまづいてしまったので備忘録です。

# 確認バージョン

Unity 2020.3.13f1
Cinemachine 2.6.10

# はじめに

Cinemachine はカメラ本体とは別にバーチャルカメラという仮想の概念があり、このバーチャルカメラ間をカメラ本体が渡り歩いていくイメージで、効率よく滑らかなカメラワークを実現できます。

ですのでバーチャルカメラをワープ[^1]させたとしても、**カメラ本体はそれに遅れる形で滑らかに**バーチャルカメラの位置まで移動していきます。
ですが、配置、リスポーン、テレポートなど、補間などせず**すぐにバーチャルカメラの位置に合致してほしい**シチュエーションはありますよね。
たとえカメラ本体（CinemachineBrain）の transform を直接設定しても、内部の補間計算は続いているため、結局補間途中の位置に戻されてしまいうまくいきません。
今回は**カメラ本体をすぐにバーチャルカメラの位置に飛ばす**やり方のメモになります。

[^1]: CinemachineVirtualCamera の Follow や LookAt のターゲットを変える、もしくは transform を直接設定するなど。

# 方法

要するに補間処理を打ち切ってやれば OK でして、やり方はバーチャルカメラの位置を変える処理の直後で **[CinemachineVirtualCameraBase](https://docs.unity3d.com/Packages/com.unity.cinemachine@2.6/api/Cinemachine.CinemachineVirtualCameraBase.html).PreviousStateIsValid を false** に設定するだけです。

```cs
    _cinemachineVirtualCameraBase.PreviousStateIsValid = false;
```

私は CinemachineBrain 側からアプローチしたかったので、次のように使いました。

```cs
    (_cinemachineBrain.ActiveVirtualCamera as CinemachineVirtualCameraBase).PreviousStateIsValid = false;
```


~~ちょっと名前が直感的でないため意図が読みにくいコードですが、~~これでやりたいことが達成できましたので、同じようなことでつまづいている方の参考になれば幸いです。

# 参考
- [Unity Forum: Proper way to 'reset' follow camera?](https://forum.unity.com/threads/proper-way-to-reset-follow-camera.747101/)
【Python】2次元リストの行と列を入れ替える（転置する）

#概要
競技プログラミング中に必要なり、調べてみました。
2次元リストの行と列を入れ替える（転置する）には、リストのアンパックと組み込み関数のzip()をうまく使います。
zipは組み込み関数なので何かをimportする必要はありません。
実際のコードは以下のようになります。

```python:zipの使用例
#元のリスト
list_org = [[0, 1, 2], [3, 4, 5], [6, 7, 8]]
print(list_org)

#転置
list_transposed = [list(x) for x in zip(*list_org)]
print(list_transposed)
```

出典：
nkmk note様

https://note.nkmk.me/python-list-transpose/

#解説
```python
list_transposed = [list(x) for x in zip(*list_org)]
```
の行が転置を行っています。
*list_orgのようにリストの前にアスタリスクをつけると「アンパック」という処理が行われます。
これは、リストの一番上の次元の要素を取り出して引数として渡す処理になります。
試しに以下のコードを実行してみます。

```python:アンパックの例
list_org = [[0, 1, 2], [3, 4, 5], [6, 7, 8]]
print(list_org)
```
結果は
[0, 1, 2] [3, 4, 5] [6, 7, 8]
となります。
printにlist_org[0]とlist_org[1]とlist_org[2]が渡されています。
（printに複数の引数を渡すとどうなるかは末尾の「補足」に軽く記載しました）
つまり今回の場合、

```python
zip(*list_org)
```
は、

```python
zip(list_org[0], list_org[1], list_org[2])
```

と同じ意味になります。

続いてzipですが、これは渡された引数それぞれのi番目の要素を取り出してタプルにする関数となっています。
[公式リファレンス](https://docs.python.org/ja/3/library/functions.html#zip)にわかりやすい例があったので転載します。

```python:zip()のわかりやすい例
for item in zip([1, 2, 3], ['sugar', 'spice', 'everything nice']):
    print(item)
```

```python:出力結果
(1, 'sugar')
(2, 'spice')
(3, 'everything nice')
```
この例ではzip()の一番目の引数に連番の数字のリスト、二番目の引数に文字列のリストを渡しています。
そしてzip()の結果、二つのリストで添え字が同じになる要素がタプルとしてまとめられています。
そしてそれぞれのタプルはiterableなクラス（for inが使えるクラス）としてひとまとめにされて返却されます。
（実際にtype()で型を見てみるとzipというクラスのようです）

今回の転置の

```python
zip(list_org[0], list_org[1], list_org[2])
```
は、
list_org = [[0, 1, 2], [3, 4, 5], [6, 7, 8]]なので、
zip([0, 1, 2],  [3, 4, 5], [6, 7, 8])となり、
それぞれの引数のリストで同じ位置の要素がタプルでまとめられ、

```python
(0, 3, 6)
(1, 4, 7)
(2, 5, 8)
```
となります。
もう実質転置された状態になっていますね。
しかし、2次元リストではなくタプルとそれをまとめたiterableなので最後にこれを変換します。
それを行うのが以下のコードの[list(x) for x in～]の部分です。

```python
list_transposed = [list(x) for x in zip(*list_org)]
```
この構文でのリストの作成ですが実際の処理順はともかく、意味的には以下の画像のようになっています。
![list_desc.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2049468/c656b862-f8f6-2c5e-ee6d-ba84a32250ba.png)
この構文によって、
タプルの(0, 3, 6)、(1, 4, 7)、(2, 5, 8)はlist(x)の部分で
それぞれリスト[0, 3, 6]、[1, 4, 7]、[2, 5, 8]になり、
さらにそれを[]で囲むことによって2次元リスト
[[0, 3, 6],[1, 4, 7],[2, 5, 8]]になります。
これで転置した2次元リストが出来上がりました。


#補足
補足ですがprintには複数の引数を渡すことが可能であり、例えば

```python
print('a','b','c')
```
とすると出力は
a b c
と、引数全てがスペースでつながれて出力されます。
JupyterLab　Desktop Appのインストールで困ったこと

#1 はじめに
　JupyterLab Desktop App([紹介ページ](https://www.publickey1.jp/blog/21/jupyterlabjupyterlab_apppython.html))をインストール後、アプリを実行すると以下のエラーメッセージが出た。この解決策(自分の場合)を備忘録として残す。
 
**[エラーメッセージ]**
<img width="500" alt="qiita-square" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/378325/b23b2bac-16a3-f5ed-c8e2-95e0140415a1.png">


#2 現象の説明

* JupyterLab Desktop App　を以下のサイトを参考にインストールした。

https://gammasoft.jp/blog/jupyterlab-desktop-app-install-to-windows/

* このときのインストール環境は以下のとおり

| 項目 | 内容 | 備考 |
|:-:|:-:|:-:|
| OS  | Windows10  |   |
| Winユーザ  | 管理者権限あり  |   |
| IPアドレス  | 固定IP  | 意味のない情報かもしれませんが念のため  |
| DNSサーバ設定  | 認証なし | 意味のない情報かもしれませんが念のため  |

* インストール後、「Jupyer Server Not Found」とエラーメッセージがでてアプリが起動されない。



#3 対策

* 上記の[エラーメッセージ]が出た後に確認したことを記す。<font color="Red">一番最後の方法が(自分の環境では)効果がありました。</font>

##3.1 実施したが不発した対策
* ”CHOOSE PATH”から、JupyterLabAppServerの中にあるPythonJupyterLabPython.exeを選択　⇒　効果なし
* "INSTALL JUPYTER"を押すと、インストール説明画面に飛ばされるだけで、意味が分からなかった。　⇒　効果なし
* ([別サイトでの解決策](https://issueexplorer.com/issue/jupyterlab/jupyterlab_app/241))で、アプリを"管理者権限で実行"するとよいと記載されていた。解決した人もいればしない人もいるらしいが、実行してみた。　⇒　効果なし
 
##3.2 効果があった対策
* JupyterLab Appをアンインストールした後、以下の手順で再インストールを試みた。
* 再インストールで"All users"を選択した。（これまではJust Me）
 
<img width="400" alt="xxx" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/378325/153c7d13-534b-c7d9-e96d-3f4dae5d45b4.jpeg">


* 保存先は以下のとおりです。　※先程、"Just Me"を選択すると、個人フォルダが保存先に割り当てられる。この辺がアクセス制限の問題を引き起こしているのか？
<img width="400" alt="xxx" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/378325/1f8e3013-c769-5559-33d4-17f80e722d78.jpeg">

* 以下２か所(Recommendedの個所)にチェックをつけてインストール開始
<img width="400" alt="xxx" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/378325/4a65e276-2930-efdf-8314-c4998504bdd9.jpeg">

* インストール後、JypyterLabが立ち上がった。　涙

#4 まとめ
　Jupyterlab Appをインストールすると"Jupyter Server Not Found"とエラーが出てしまった。理由は分からないが、All user でインストールすることで、起動するようになった。
　アカウントのフォルダアクセス権限とかが問題になっているんじゃないかと推測するも良くわからない。とりあえず使えればいいやの精神で完。
入門Power Automate Desktop：目次

# 目次
[入門Power Automate Desktop：第零話「業務で使える？Power Automate Desktop」](https://qiita.com/A_T_B/items/908c2c02586a59e8de87)
[入門Power Automate Desktop：第一話「まずは環境構築」](https://qiita.com/A_T_B/items/0306d4ee0f4756259f26)
[入門Power Automate Desktop：第二話「基本操作とノウハウを身に着けよう」](https://qiita.com/A_T_B/items/bd83f75b6b69dd52eec9)
[入門Power Automate Desktop：第三話「ブラウザ操作を自動化しよう」](https://qiita.com/A_T_B/items/d47e6d83c6cb189926fc)
[入門Power Automate Desktop：第四話「ファイル操作を自動化しよう」](https://qiita.com/A_T_B/items/13d375497268330dc874)
[入門Power Automate Desktop：第五話「アプリ操作を自動化しよう」](https://qiita.com/A_T_B/items/107a6e09eac4deca43dd)
[入門Power Automate Desktop：第六話「フローを繋げて複雑な操作を実現しよう」](https://qiita.com/A_T_B/items/9e7a9c63c04d123d32a7)
[入門Power Automate Desktop：第七話「画像認識でゲームを自動化しよう」](https://qiita.com/A_T_B/items/940e1f95845eab0ef09d)
[入門Power Automate Desktop：第八話「REST APIを叩いてWebシステムを利用しよう」](https://qiita.com/A_T_B/items/4cf8e08c6f2b4e475092)
Progate Ruby on Rails5 VI ~ VII 個人的ざっくりまとめ

# emailの重複がないかチェック

バリデーションで`uniqueness: true`を指定

```rb:models/user.rb
class User < ApplicationRecord
  validates :email, {uniqueness: true}
end
```

# マイグレーションファイルのみ作成

データベースに変更を加えるが、モデルは必要ないとき

`rails g migration ファイル名`をターミナルに入力

```terminal:ターミナル
$ rails g migration add_image_name_to_users
```

# マイグレーションファイルの仕組み

テーブルの変更を加えるには、マイグレーションファイルのchangeメソッドの中に処理をかく

```rb:20170427053118_add_image_name_to_users.rb
class AddImageNameToUsers < ActiveRecord::Migration[5.0]
  def change
    # 変更内容
  end
end
```

# カラムを追加するマイグレーションファイル

`add_column テーブル名, カラム名, データ型`をchangeメソッドの中にかく

```rb:20170427053118_add_image_name_to_users.rb
class AddImageNameToUsers < ActiveRecord::Migration[5.0]
  def change
    add_column :users, :image_name, :string
  end
end
```

※`rails db:migrate`はこのchangeメソッドの中身を実行するためのコマンド

```terminal:ターミナル
$ rails db:migrate  # changeメソッドの中身を実行
```

# 画像の送信

画像の送信の場合は`form_tag("URL", {multipart: true})`と指定する必要がある

```erb:users/edit.html.erb
<%= form_tag("/users/#{@user.id}/update"), {multipart: true}) do %>
  <p>画像</p>
  <input name="image" type="file">
<% end %>
```

# Rubyのコードでファイルを作成

`File.write(ファイルの場所, ファイルの中身)`と指定する

```terminal:ターミナル
$ rails console

> File.write("public/sample.txt", "Hello World")
```

# 画像の保存

画像を保存するためには、画像データを元に画像ファイルを作成する必要がある
↓
ファイルを作成するにはFileクラスを使う
↓
`File.binwrite(ファイルの場所, ファイルの中身(画像データ))`と指定する

※readメソッドを用いることで、その画像データを取得することができる

```rb:users_controller.rb
def update
  :
  :
  @user.image_name = "#{@user.id}.jpg"
  image = params[:image]
  File.binwrite("public/user_images/#{@user.image_name}", image.read)
end
```
入門Power Automate Desktop：第六話「フローを繋げて複雑な操作を実現しよう」

# 自動化する作業
1. Webから小説のデータを取得する。
1. 取得したデータをドキュメント内に移動する。
1. 移動したデータをVOICEROID2で読み上げさせる。

今回はWebからの小説データの取得からVOICEROID2への読み上げまでを纏めて実行します。
※他のフローは過去の記事で作成している前提とします。

# 実際にフローを作る
## 0. 事前準備
目的が一目でわかる名前でフローを作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/587410/38c607d5-7a73-eb34-eed0-33a0541ddd17.png)

## 1. Webから小説のデータを取得する。
アクションから「Desktopフローを実行」をドラッグしてフローにドロップします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/587410/7d1b488b-9fb7-aad1-0e4f-1867569eb099.png)
アクションの設定画面が表示されるので、スクレイピングのフローを指定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/587410/9b3efc1c-bb41-b4c5-5e34-8047a77d5bfb.png)
追加されるとこんな感じです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/587410/4b197408-7031-8770-3b1f-0ddb5f8eb8bd.png)

## 2. 取得したデータをドキュメント内に移動する。
1と同じ手順でファイル整理のフローを指定します。
追加されるとこんな感じです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/587410/57a28097-2622-7aad-57d2-7e814e56c691.png)

## 3. 移動したデータをVOICEROID2で読み上げさせる。
1と同じ手順でVOICEROID2読み上げのフローを指定します。
追加されるとこんな感じです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/587410/d5bfec2d-66fe-cffd-2d22-e51746a32319.png)

# 作成したフローを実行してみる
作成したフローを実行する。
Webから小説をダウンロードし、VOICEROID2が起動して小説データを1行毎に読み上げれば成功です。