OmniAICreator/Qiita-1.07M huggingface dataset:
1時間でSpringFrameworkのDIを説明してみた（１時間シリーズ第３弾）

#1時間でSpringFrameworkのDIを５分で分かるように説明してみた#

といっても、コードとかはほかのサイトに仰山あるみたいだし。
私は備忘録的に文章だけで説明いたします。

**結論：DIとは何か**
Dependency Injectionの略で、依存性注入っていう意味。

**メリットあるんか**
インスタンスの作成や初期化処理などを設定ファイルに切り出せるので、コードの見通しが良くなる。
DIコンテナにインスタンスのスコープやライフサイクルを管理してもらえる。

**デメリットあるんか**
設定ファイルが膨大な行数になったりする

**サルにも分かりやすく説明して（独自見解）**
まず、SpringFramework社長のDI会社というものがありました。
DI会社は派遣を提供する会社で、様々な部署があります。
しかもその部署には分身ができる優秀な忍者であればだれでも入社できることができます。
あっ、さっそく太郎君が「俺はお前に登録されるために生きてきたんだから、登録せい」とマークを付けてやってきました。
SpringFramework社長が太郎君含め入社希望の方を探して、DI会社に招き入れます。
太郎君、よかったですね。
しばらくして、メイさんがやってきました。
メイさんはこの仕事ができる人が欲しいとSpringFramework社長に要求します。
SpringFramework社長はメイさんのもとへ派遣社員を提供します。
なんと、その人はあの太郎君です。
っていうのらしい。
まぁ、要するに
SpringFrameworkがとせかせかとコンテナにBean（インスタンス）を集めて、
クラスがこのインスタンスをくれと言ったときにBean（インスタンス）を投げつけること。


**サルにも分かりやすく説明して（独自見解）の詳細**

* DI会社というのはDIコンテナのこと。BeanはこのDIコンテナに入れたり取り出したりする。
* DIコンテナは使用クラスにコンテナに登録されているインスタンスを提供する
* 様々な部署、じゃなくてBeanには様々な種類がある
    * Bean 
    * Component
    * Serviceなど
* クラスそのものを注入することはできない。あくまで、インスタンスを注入する。
* マークとはアノテーションのこと。ここではわかりやすくアノテーションベースで説明したが、外部設定ファイルにBeanを設定する場合はアノテーションは不要。
* Springは起動時にBean定義の目印がついたものを探し、見つけたらDIコンテナに登録する。また、@ComponentScanがあるクラスは実行時にComponentやServiceの目印がついたものを探し、見つけたらDIコンテナに登録する。
* SpringFrameworkに要求する方法はAutowiredしたり、getBeanしたりなどがある
* SpringFrameworkはその要求にこたえ、DIコンテナからそのクラスに注入する。
 
こんな感じかな。
firewall を再起動しただけなのに

# ある日の（やらかしが起こって解消するまでの一連の）出来事

**登場人物**

* k-nishigaki：筆者、たぶんインフラエンジニア、以下私
* 開発さん：すごいプログラマ

開発さん「k-nishigaki さん、今やってる○○案件の機能テストで staging 環境のストレージに EC2 からアクセスしたいんすけど、なんか制限かかってます？」
k-nishigaki「あー、LB で IP 制限してありますな。追加しときます。」

〜〜数分後〜〜

開発さん「やっぱなんかネットワークレベルで切られちゃうっぽいんですけど。」
私「あるぇー？おっかしいなぁ……一瞬 firewall 落として疎通だけ見てみっか[^1]……。」
私「えーっと、LB に ssh して、`sudo systemctl stop firewalld` っと……うーん、つながらないなぁ。経路の問題かな？とりあえず戻さないと。`sudo systemctl start firewalld` っと……ん？なんか通信切れた？」
私「あるぇー？なんでだろ……新規ウィンドウでも ssh できない、てか ping も通らない……ってかこれ本番さーｂ

ーー鳴り響くアラート、つながらないままの本番 LB、つながらなくなる本番ストレージ。
ーーこの感じ、覚えがある。**「やらかし」**だ。俺は久しぶりに**「やらかし」**をやっている。
ーーあぁ、この血の気が引いて視界が狭くなっていく感じ、本当に懐かしい……。

なんてポエムってる場合ではなく。とにかく復旧しなければなりません。
まずは震える指先で障害発生報告を打ち込み送信、裏では ping タイムアウトが続いているコンソール画面を確認。
自分が何をやらかしたのか、コンソールを遡って確認するも、どう考えても firewall の stop/start　をしただけ。
再起動で書き換わるような変な設定はしていないはずだが、まずは事実を受け止め、対処をしないといけません。

幸い、該当サーバは稼働系と待機系の冗長構成だったため、コンソール接続を諦め、遠隔からサーバの電源リセットを強行。
数十秒後、待機系が稼働を開始し、無事にアラートが解消、サービスも正常動作していることを確認。
やらかし期間は数分で済みました・・・。


# 何が起こっていたのか？

さて、実際に起ったことは「firewall を再起動したら何故かあらゆるアクセスが遮断された」です。
一体何が起こったのか？と **今度こそ** staging のサーバで firewall を再起動したところ、同じことが起こりました。
staging なので停止しても気にせず、遠隔でサーバコンソールを確認します。
すると、firewalld 自身はきちんと再起動しているのに、ZONE へのインタフェース割当が消失していることに気づきました。

実はこのサーバ、とある理由で NetworkManager を停止していたサーバだったのです。
どうやら firewalld は NetworkManager と連動してインタフェースを ZONE へ割り当てる挙動をするようです。[（参考）](https://access.redhat.com/documentation/ja-jp/red_hat_enterprise_linux/7/html/security_guide/sec-using_firewalls)
インタフェースには ZONE の設定を書いていましたが、NetworkManager を停止していたことが原因で「firewall を再起動すると ZONE 設定が行われずあらゆる通信が遮断される」という事象が引き起こされたのでした。

firewalld のみで ZONE とインタフェースを紐付けるには、[change-interface オプション](https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html/security_guide/sec-working_with_zones)を利用します。
これを使ってインタフェースを明示しておけば、firewalld を再起動してもきちんと ZONE 設定が残ります。


# 惨劇は何故起こってしまったのか？

NetworkManager を停止した状態では ZONE を記憶するために firewall 自身に設定を入れる必要があった、というのは上記にある通りです。
しかし、惨劇の原因自体は他にあります。
それは**「うっかり本番へ接続」**し、**「それと気づかず軽率に sudo した」**ことです。

もしちゃんと staging で操作をしていたら、これはやらかしではなくヒヤリハットで留められた問題でした。
もし本番で軽率に sudo を実行しなければ、というのも同様です。
運用（緊急時対応）の利便性を追求するがあまり、staging と本番で接続・コマンド実行に対するハードルが同じになっていたのが最大の原因でしょう。


# どうすれば惨劇は回避できたのか？

命名規則やチェックシートなど色々考えられますが、一番はやはり「不要な権限は与えない」ことだと思います。
緊急時の対応として該当サーバへ ssh しやすい権限を持っていた、というのは仕方ないことかも知れません。
しかし、該当サーバは「普段これと言ってつなぐ必要がない」かつ「別に root の必要なコマンドも全然実行しない」ようなサーバでした。

こんなサーバに sudo nopassword は不要ですよね。
「権限は必要最低限にする」のが鉄則です。
さらば `NOPASSWD: ALL`。
せめて一回 root になるとかそういうクッション挟ませようね。




[^1]: 普通に考えたら分かると思いますが、普通はやってはいけません。k-nishigaki はとくしゅなくんれんを受けています。
GiuHubのリリース機能を使ったCI/CDをGitHub Actionsで構築する

# はじめに

昨今、CI/CDでテストやリリース作業を自動化されているプロダクトやチームは多いと思います。
今回、GitHubのリリース機能を活用したGitHub ActionsでのCI/CDフローを作成しましたので、その設定内容について紹介します。

:::note warn
CDには継続的デリバリー（Continuous Delivery）と継続的デプロイメント（Continuous Deployment）の2つの意味がありますが、本記事では2つを区別するためにCD（Continuous Delivery）とCD（Continuous Deployment）と表現しています。
:::

# 実現したいCI/CDフロー

今回実現したいCI/CDフローの概要図はこちらになります。

![cicd-flow.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/146076/ac21f941-de63-29d8-17bd-b580003ddcf5.png)

CI/CDとしては3つのポイントでワークフローが自動実行されるように設定を行いました。

1. CI（Continuous Integration）: GitHub上でのPull Requestの作成・更新をトリガーに実行する
1. CD（Continuous Delivery）: mainブランチへのPull Requestマージをトリガーに実行する
1. CD（Continuous Deployment）: GitHub上でのRelease作成をトリガーに実行する


# 実際の設定内容

それでは実際に設定したGitHub Actionsの設定内容をサンプルを踏まえてご紹介します。

## CI（Continuous Integration）

CIはmainブランチへのPull Requestの作成・更新をトリガーに実行するため、以下のような設定にしています。


```yaml
name: 'Continuous Integration Flow.'

on:
  pull_request:
    branches:
      - 'main'
    types:
      - 'opened'
      - 'synchronize'
      - 'reopened'

jobs:
  continuous-integration:
    name: 'Continuous Integration.'
    runs-on: ubuntu-latest
    steps:
      - run: echo Continuous Integration!

```

設定値のポイントは以下になります。

1. mainブランチへのPull Requestのみ実行したいため、`on.pull_request.branches`指定に`main`ブランチを指定する
1. Pull Requestのクローズ時はCIを実行させないため、`on.pull_request.types`に`opened`、`synchronize`、`reopened`の3つを指定する

## CD（Continuous Delivery）

CD（Continuous Delivery）はmainブランチへのPull Requestマージをトリガーに実行するために、以下のように設定しています。

```yaml
name: 'Continuous Delivery Flow.'

on:
  pull_request:
    branches:
      - 'main'
    types:
      - 'closed'

jobs:
  continuous-delivery:
    name: 'Continuous Delivery.'
    runs-on: ubuntu-latest
    if: github.event.pull_request.merged == true
    steps:
      - run: echo Continuous Delivery!

```

設定値のポイントは以下になります。

1. mainブランチへのPull Requestのみ実行したいため、`on.pull_request.branches`指定に`main`ブランチを指定する
1. Pull Requestのマージのみを実行させることは現時点で不可能なため、`on.pull_request.types`に`closed`を指定する
1. CloseだけだとPull RequestはMergeされたかMergeせずにCloseされたか判断できないため、jobのif文でPull Requestがマージされたとき（`github.event.pull_request.merged == true`） だけ実行するようにする

## CD（Continuous Deployment）

CD（Continuous Deployment）はGitHub上でのRelease作成をトリガーに実行するために、以下のように設定しています。

```yaml
name: 'Continuous Deployment Flow.'

on:
  release:
    types:
      - 'released'

jobs:
  continuous-deployment:
    name: 'Continuous Deployment.'
    runs-on: ubuntu-latest
    steps:
      - run: echo Continuous Deployment!
```

設定値のポイントは以下になります。

1. Pre Release作成時ではなくRelease作成時のみ実行したいため、`on.release.types`指定に`released`を指定する

# おわりに

今回、GitHubのリリース機能を使ったCI/CDの設定例をご紹介しました。
実際のプロダクトでは上記の設定に加えて、[Release Drafter](https://github.com/release-drafter/release-drafter)によるDraft Releaseの自動作成もCI/CDで実現しています。
これからCI/CDをGitHub Actionsで実装される方の参考になれば幸いです。
Androidで写真にテキストを合成する


###Androidで写真にテキストを合成する

（注意）
Bitmapに変換して画像を合成すると元の写真が持つExif情報は失われます。
写真の向きなどを表すExifInterface.TAG_ORIENTATIONなど必要な情報は引き継ぐように実装した方がいいかもしれません。

##### 1.合成したい元の写真をBitmapに変換して写真と同じ大きさのCanvasに貼り付けます。
以下のsourcePathに元写真のパスが入ってます。

```
val baseBitmap = BitmapFactory.decodeFile(sourcePath)
val width = baseBitmap.width
val height = baseBitmap.height
val newBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
val canvas = Canvas(baseBitmap)
canvas.drawBitmap(baseBitmap, 0f, 0f, null)
```

##### 2.テキストを描くためのPaintオブジェクトを作成します。

```
val dataText = "おはようございます。こんにちは。こんばんは。"
val paintText = Paint()
// 色を指定する
paintText.color = Color.Red

// フォントサイズを指定する
// ここでは最初は大きめのフォントを指定しておいて、
// テキストの幅がCanvasの幅を超えないように
// フォントサイズを調整しています。 
paintText.textSize = 300f
while (paintText.measureText(dateText) > width) {
   paintText.textSize -= 2
}
```

##### 3.Canvasに文字列を描きます

```
// 日付文字列を描く
canvas.drawText(dateText, 0f, 0f, paintText)

```

画面の右下などに配置したい場合はPaintオブジェクトのfontMetricsから
テキストの高さを、measureTextメソッドから幅を取得し良い感じに配置されるよう調整しましょう。

##### 4.JPEGファイルに出力する
以下のdistFileは出力先です。

```
try {
    val fos = FileOutputStream(distFile)
    newBitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos)
    fos.close()
} catch (e: IOException) {

}
```


以上。
Firebase Management APIを使ってFirebaseプロジェクトの登録をする方法

現職で自動的にFirebaseの設定が追加できないかを調査していたところ一部はbeta版ではありますが方法があったのでメモ。

<h2>なぜ調べたのか？</h2>

普通のアプリであれば、コンソールでぽちぽちやってjsonファイルを取得すれば事足りるのですが新規で作成するアプリの数が多いことからその部分自動化したいねというところからスタートしました。

<h2>Firebase Management APIとは？</h2>
Firebaseプロジェクトの新規作成だったり、プロジェクトの情報取得や変更ができるAPIです。

https://firebase.google.com/docs/projects/api/reference/rest

<h2>利用したAPIの簡単な説明</h2>

<h3>projects.list</h3>
現在登録されているプロジェクトの一覧を取得します。

![スクリーンショット 2021-11-27 12.13.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1893524/5c672e25-6b4c-9aec-f810-631ca35238a0.png)

該当のレスポンス情報

``` json
{
  "results": [
  {
      "projectId": "test-52060",
      "projectNumber": "206091014636",
      "displayName": "test",
      "name": "projects/test-52060",
      "resources": {
        "hostingSite": "test-52060",
        "realtimeDatabaseInstance": "test-52060"
      },
      "state": "ACTIVE"
    }
  ]
}

```

<h3>projects.androidApps.list</h3>
対象のプロジェクトに登録されているAndroidアプリの設定一覧を取得できます。

<b>ReqeustParameterの指定</b>
parent：projects/test-52060　

![スクリーンショット 2021-11-27 12.20.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1893524/ab41d3e0-f5a8-e018-605f-e16c2f7fba45.png)


該当のレスポンス情報

``` json
{
  "apps": [
    {
      "name": "プロジェクト名/androidApps/appIDが入ります",
      "appId": "appID",
      "projectId": "test-52060",
      "packageName": "jp.test.co.jp"
    },
    {
      "name": "プロジェクト名/androidApps/appIDが入ります",
      "appId": "appIDが入ります",
      "displayName": "test",
      "projectId": "test-52060",
      "packageName": "jp.co.k.f.pd.test"
    }
  ]
}
```

<h3>projects.androidApps.create</h3>
対象のプロジェクトにAndroidアプリの設定を追加します。

<b>ReqeustParameterの指定例</b>
parent：projects/test-52060　

<b>Requestbodyの指定例</b>
"packageName:"パッケージ名"
"displayName": "表示名"


<h3>projects.androidApps.getConfig</h3>
こちらで該当のアプリのjsonファイルをbase64形式で取得できます。

<b>RequestParameterの指定例</b>
parent :projects/test-52060/androidApps/appID/config

該当のレスポンス情報

``` json
{
  "configFilename": "google-services.json",
  "configFileContents": "base64でエンコードされた文字列"
}
```

<h2>まとめ</h2>

こちらを利用することでFirebaseの該当プロジェクトにアプリを追加し、jsonファイルが取得できるところまでを自動化することができました。
最初に貼ったFirebase Management APIを使って比較的簡単に自分のプロジェクトで試せるので興味がある方は一度お試ししてみるといいかも知れないです！
BigQuery + Googleスプレッドシート + GASでほぼノーコード開発でデータマート作成

BigQueryの外部テーブル機能 + Googleスプレッドシートでデータマートを作成しましょう

↓BQの外部テーブル設定画面
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2374914/458b7d54-4e35-0121-f46d-b8ef5909e09a.png)

スプシやExcelでデータ管理している会社は多いと思うので、スプシの外部テーブル化は有効です。
ZIPやCSV形式のファイルの場合、GASでスプレッドシートに出力するようにしてみます。

CSVを直接BQに取り込めば良いのではと思われるかもしれませんが、
データの整形、フォーマットをスプシの関数でできるので選択肢として有効です。スクラッチでPython等の整形プログラムを組まなくて良いのもメリットです。

```actionscript:Gドライブ上でzip解凍、CSVをスプシ出力するスクリプト
//zipファイルのフォルダ、ファイル名(例：yyyy-MM-dd_XXXX.zip)
var folder_id = "XXXXX";
var date = new Date();
var file_name = Utilities.formatDate(date, 'Asia/Tokyo', 'yyyy-MM-dd') + "_XXXX.zip"

//zipファイルの解凍先フォルダ、ファイル名
var unzip_folder_id = "XXXXX"
var unzip_file_name = Utilities.formatDate(date, 'Asia/Tokyo', 'yyyy-MM-dd') + "_XXXX.csv"

//出力するスプレッドシート、シート
var ss = SpreadsheetApp.openById('XXXXX');
var sheet = ss.getSheetByName('XXX');

//ZIP解凍
function unzip() {
 //解凍する対象のファイルを取得
 var zipblob = DriveApp.getFolderById(folder_id).getFilesByName(file_name).next();

 //ファイル解凍の準備
 var unzipfile = Utilities.unzip(zipblob);

 //解凍先フォルダにunzipしたファイル群を入れる
 var unzipfolder = DriveApp.getFolderById(unzip_folder_id)
 for (i = 0; i < unzipfile.length; i++) {
   unzipfolder.createFile(unzipfile[i].setName(unzip_file_name));
 }

//解凍したCSVファイルをパース
 var file = DriveApp.getFilesByName(unzip_file_name).next();
 var csv_data = Utilities.parseCsv(file.getBlob().getDataAsString('shift-jis'));

//シートを一旦クリアして、CSVデータを貼り付け
 sheet.clearContents();
 sheet.getRange(1, 1, csv_data.length, csv_data[0].length).setValues(csv_data);
}
```
CSVファイルの場合、ZIP解凍云々...がいらなくなるので、//解凍したCSVファイルをパース　以降を参考にしてください。

上記RPAで定期的にCSV DLするような業務フローと相性がいいです。
実際にRPAで業務データ取得、GASとスプシでデータマート作成を行なっていますが、メンテの手間が短縮できて良かったです。
【styled-components, React】styled-componentsを用いて、CSSの記述を行う。

#概要
こんにちは！今日はstyled-componentsの使い方について説明します！
これを用いることでjavascript内にCSSを記述することができるようになります。
余計にCSSファイルを作る必要がなくて楽なのと、CSSを作用させる対象が記述されているファイルに書くので、どのCSSがどこに適用されているかが分かりやすくなります。

#使い方
##CSSの記述
（例）

```typescript
const TitleStyle = styled.h1`
  color: #fff;
  border-radius: 100vh;
  background-color: black;
  font-size: 100px;
  position: absolute;
  height: 150px;
  width: 800px;
  left: 0;
  right: 0;
  top: 0;
  bottom: 40%;
  margin: auto;
`;
```

このようにして記述を行います。```styled.使いたいタグの名前```として上げることで新たなタグを生成することができます。
好きなCSSを自由に記述できるようになります。

##HTML部分の記述
（例）

```react
return(
・・・
<TitleStyle>タイトル名をここに書く！</TitleStyle>
・・・
)
```

このように先ほど宣言した定数名をタグにすることで、指定したCSSが適用されているh1タグとして利用できます。

#余談
これを用いるとCSSの記述が簡単になります。またstyled.sectionを用いてタグを新しく作ってあげると
GitHubにあるコードをDockerのubuntu環境で試す

# やりたいこと
Dockerをつかって、ubuntuでプログラムを実行したい！というあなた向け。
GitHubからコードをクローンして、ubuntu環境で試せます。コードを変更したりしても残せるようにします。

この記事の方法はかなりいけてないので、できれば、[最後に](#最後に)初めにを読んでほしい。

### 環境
MacOS 12.0.1
Docker Engine v20.10.11

# 準備
### ソースコード

```yml:docker-compose.yml
version: '3'
services:
  ubuntu:
    container_name: "ssh-keygen"
    hostname: ubuntu
    build: ./
    tty: true
```

```Dockerfile:Dockerfile
FROM ubuntu:20.04

ENV TZ=Asia/Tokyo
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && \
        echo $TZ > /etc/timezone
RUN apt-get update
RUN apt-get install -y openssh-server
```

### SSHキーを作成するコンテナの作成

まずDockerでSSHキー作成用のコンテナを作成して、SSHの鍵を作成していきます。
ワーキングディレクトリの下に上記のファイルを作成します。

次に以下のコマンドを実行してコンテナを作成し、コンテナ内のシェルを起動します。

```bash:ホストマシン
$ docker-compose up -d # コンテナ作成
$ docker-compose exec ubuntu bash # シェルを起動
```

### SSHキー作成

コマンド一発叩くだけで鍵の作成ができます。

```bash:仮想マシン
＃ ssh-keygen # キー作成（デフォルトの設定で作成するなら3回Enterを押す）
＃ cat /root/.ssh/id_rsa.pub # 公開鍵を確認
＃ exit
```

`cat /root/.ssh/id_rsa.pub` でSSHの公開鍵を確認できる。これをコピペして、GitHubに登録する。
また、このとき.sshディレクトリをディレクトリごと残しておく。
残す方法としては、ホストマシンで以下のコマンドを実行すると、.sshが指定した場所にコピーされる。{container_name}はDockerfileで決めたcontainer_nameが入る。今回はssh_keygen。

```bash:ホストマシン
$ docker cp {container_name}:/root/.ssh .ssh  
$ # docker cp ssh-keygen:/root/.ssh .ssh <-今回はこれ
```

# いざ！
### ソースコード

```yml:docker-compose.yml
version: '3'
services:
  ubuntu:
    container_name: "ubuntu20.04"
    hostname: ubuntu

    build: ./  
    tty: true

    # ボリューム(ローカルのディレクトリ)を設定しデータを永続化(コンテナを潰してもデータは残る)（必要に応じて）
    volumes:
      - ./codes:/codes

    # ポート解放(必要に応じて)
    ports:
      - "8002:8002"
```

```Dockerfile:Dockerfile
FROM ubuntu:20.04

# インストールでConfiguring tzdataをスキップするためタイムゾーンを設定（必要に応じて）
ENV TZ=Asia/Tokyo
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && \
        echo $TZ > /etc/timezone

RUN apt-get update
RUN apt-get install git -y
# SSHの設定
ADD .ssh /root/.ssh
RUN chmod 600 /root/.ssh/id_rsa
# Git cloneするディレクトリへ移動
WORKDIR /codes/

# 必要パッケージのインストール（必要に応じて）
RUN apt-get install python3 pip -y
RUN apt-get install python3-virtualenv -y
RUN apt-get install postgresql postgresql-contrib -y
RUN apt-get install mongodb mongodb-server -y
RUN apt-get install python-dev libpq-dev -y
```


docker-compose.ymlとDockerfileを作成する（上書き）。
先ほどと同じように仮想マシンを立ち上げます。

```bash:ホストマシン
$ docker-compose up -d # コンテナ作成
$ docker-compose exec ubuntu bash # シェルを起動
```

あとはクローンすればいいだけ！！

```bash:仮想マシン
＃ git clone git@github.com:{username}/{repository}.git
＃ cd {repository}/
```

あとは好きなようにできます。

# 最後に

思ったんやけど、ホストマシンで`git clone`して、そのクローンしたディレクトリをボリュームすれば、.sshの作成など必要ないことに気がつきました。
トリガーループ

Automation Anywhere A360 に、トリガーとは別にトリガーループがあったので、試してみた。（build 9664で検証）
トリガーは、一度動くと処理が終わってしまうが、トリガーループは待機してくれる。

短いサンプルを作成。フローだと縦に長いので一覧しにくいので、リスト表記だとこんな感じ。
![pic1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/417890/675f573f-e472-4820-8619-47f66dfcdb6e.png)
上記の設定では、E:\work_trigger\ 及びその配下のフォルダにファイルがcreatedされた時に、その後の処理を実行し、再び待機するという処理。
変数trigger_valueはレコード型。(X)+ をクリックすると、勝手にレコード型で作成してくれる。
レコード型の変数は、インデックス0～4の配列になってるっぽい。

| idx | 名前 | タイプ |
| :-- | :-- | :-- |
| 0 | triggerType | 文字列 |
| 1 | eventType | 文字列 |
| 2 | timeStamp | 日時 |
| 3 | fromFolder | ファイル |
| 4 | folder | ファイル |

3行目。上記からfolderを取得して、一旦ファイル型の変数dirpathに保存。

以降、ファイル名を文字変数に取得。後はこれを基に、読み込む、移動するなど処理をすればよい。
なお、例では、ファイルができた途端にトリガーが動くので、例えば書き込みやコピーの完了までに時間がかかることを想定して、遅延を追加している。（5行目）

複数ファイルをほぼ同時に置いた時も、順番にひとつづつ（もれなく）処理された。
また、フォルダが多くても、遅延は見られなかった。（フォルダは以下の雑なvbsで作成した。6つの支社にそれぞれ100人の利用者がフォルダにファイルを保存する想定）

```vb:フォルダ作成
const root_dir = "E:\work_trigger\"
dim branch
dim person
dim obj
dim lpb
dim lpp
Set obj = CreateObject("Scripting.FileSystemObject")
for lpb = 1 to 6
	branch = root_dir & "branch" & lpb
	obj.CreateFolder(branch)
	for lpp = 1 to 100
		person = branch & "\person" & "-" & lpb & "-" & lpp
		obj.CreateFolder(person)
	next
next
```

![pic2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/417890/66619326-8299-febb-71ef-95e55be49340.png)

どのフォルダにファイルを放り込んでも、トリガーが起動した。（全部のフォルダを試したわけではないけれど）
Wikipediaで赤いリンクを表示しない

[Linkclump](https://chrome.google.com/webstore/detail/linkclump/lfpjkncokllnfokkgpkobnkbkmelfefj?hl=ja)で青いリンクだけ一括で開きたいときに便利

``` css
a.new {
    display: none;
}
```
旅行プラン作成サイトTrunkをリリースしました

## 概要
旅行プランを作成、共有、共同編集できるwebサイトをリリースしました
![スクリーンショット 2021-12-07 21.18.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1707048/2ca65d28-2b6a-f56d-b30d-e7be6cef086d.png)


https://trunk-plan.com/

僕は旅行が好きで、長期休暇の時は家族で旅行に行っています。
旅行計画を立てるときはPCのエディタを使ったり表ソフトを使ったりして、メールやLINEで共有していました。
毎回ソフトを起動して作成、保存してファイルやテキストなどを送付する・・・というのは面倒でもう少し効率よくできないかと考えこのサイトを作りました

## 技術
* サーバサイド
  * Rails

* フロントエンド
  * React
  * react material ui

* マップ表示
  * mapbox API

* ホスティング
  * Heroku

* ssl
  * cloud flare

## 簡単すぎる構成図
こんな感じで運用しています
![export_img (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1707048/9cbecabc-fc8f-a84b-da58-af193de76b1a.png)



## 各ページでできること
### トップページ
* プラン作成ボタン
 * 新しいプランを作成、プラン編集ページへ遷移
* プランリスト
 * 作成したプランリストを表示

### プラン編集ページ
* 日程追加ボタン
  * 旅行日程を追加する。出発日から順に追加していく
* マップ表示
  * マップを表示。スポットをクリックすることで対象のタイムラインの最後にスポット追加できる
* 日程のタイムライン
  * 出発時間、タスクを管理する
* タスク
  * タイトル、所要時間、費用、メモの編集ができる

## 重視したところ
僕は基本的に怠けたい人なので、プラン作成でいかに手間を減らせられるかを考えました

### ログイン機能無し
**会員登録？**
>
![04c03656.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1707048/7c66a92d-d862-c073-b72b-944ff3e1fdfc.jpeg)
出典：ジョジョの奇妙な冒険 33巻

調整さん好きですか！？僕は調整さんをよく使っています。

https://chouseisan.com/

あのサイトはログイン無しで手軽に日程調整でき、共有しやすいところがすごい所だと思っています。調整さんを~~真似~~オマージュして**ログイン無し**で使えるようにしました。

また、アカウント登録は最初にサイトを使うか否か判断基準の一因になると考えているのでそういったハードルはなるべく取り払いたいというのもありました

### 登録ボタン無し
**つまり「我々は登録ボタンを押さずにプランを保存できる・・・」**
>![da-bi.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1707048/3b472d18-7015-3d48-229a-49c46326d0de.jpeg)
出典：ジョジョの奇妙な冒険 25巻

このサイトには編集系サイトにつきものの登録ボタン、submitするボタンはありません。日程追加やタスクの追加、編集作業をするたびに**バックグラウンドで自動データ登録、更新**するようになっています。

登録ボタンを押す1アクション省ける、また、保存前の不慮の事故で編集内容が無くなることを抑止できるのでこの機能をつけました。

### 共同編集
**ほ、他の人の編集内容を上書きしてしまいますよォォ**
>
![unnamed.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1707048/a7d6de7c-3345-3082-13a6-323083517bc4.jpeg)
出典：ジョジョの奇妙な冒険 27巻

プラン編集ページのURLを知っている人は誰でも同時に編集が可能です。複数ユーザが同時に編集していたとしてもそれぞれの更新状況は**リアルタイムに反映**していくので、「削除されたタスクを編集していた」「プランを再読み込みしたら実は全て上書きされていた」などということを抑止します。

こんな感じ。画質よくないですが・・

![ezgif.com-gif-maker.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1707048/123a86a2-bac5-74de-170b-02815ba00778.gif)

機能実現にはRailsのActioncableを使っています。



## まとめ
最小限でリリースを！と最初は思っていたものの実装していくうちに
**「合計金額を表示したい」**
**「このアクションではメッセージを入れたい」**
など頭の中のエンドユーザー:sunglasses:が囁き続け、気がつけば予定リリース時期より月日が経っていました。個人開発は自由勝手にできるとはいえ要件定義から実装まで計画的にしっかりやるべきだったなと反省しています。

今後の追加機能では、時間や費用に固定数値ボタン(+1時間とか+1000円とか)を入れたり、閲覧履歴機能を入れたいと思っています。

もし旅行に行く機会があったらご活用ください。
NoSQLドキュメント指向 組み込みデータベースCouchbase Lite 3.0.0 ベータにおけるC/C++サポート

## はじめに

この投稿では、Couchbase Lite 3.0.0 ベータにおけるC/C++サポートについて紹介します。

CouchbaseをC/C++アプリケーション用の組み込みデータベースとして利用する意義については、以下の記事をご参考ください。

https://qiita.com/yoshiyuki_kono/items/3abd5e32801060a449d2


## Couchbase Lite C/C++サポート概要

Couchbase Lite 3.0.0ベータでは、利用環境としてC/C++がサポートされるようになりました。
これにより、これまで以上に活用可能な範囲が広がり、AndroidやiPhoneのようなモバイルアプリケーションや、Javaが利用可能な環境のみでなく、IoT/エッジコンピューティングでの利用要件に応えるものになっています。

### サポートプラットフォーム

Couchbase Lite for C 3.0.0ベータは、OSとしてUnix系では、MacOS、Ubuntu 20.04(x86-64、arm64、adm64、armhf)、Debian 9/10(x86-64、arm64、adm64、armhf)の他、Raspberry Pi OS 9/10(arm64、armhf)をサポートしています。

また、Windows, Android, iOSもサポートしており、これらの環境用のアプリケーションをC/C++でプログラミングする際にCouchbase Liteを利用することが可能です。

## 背景/アーキテクチャー

Couchbase Liteは、2018年にリリースされたバージョン2.0にて、C++を用いて再実装されました。
これにより、Couchbase Liteは、プラットフォームに依存しない共通のコアコンポーネントを持つことになりました。Java、C＃、Swift、Objective-C等、各プラットフォームは、それぞれ独自のAPIを提供しますが、それらはC APIを内部で利用しています。

Couchbase Lite C APIは、この2.xアーキテクチャーの直接的な拡張として成立しています。
つまり、クロスプラットフォームに対応するために、Couchbase Lite Coreに付け加えられたアダプターとして実現されています。このアダプターはコアライブラリと静的にリンクされたバイナリとして提供されます。

以下がそのイメージです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176567/d98d8bb1-73e1-44f5-84aa-716547222764.png)

## Couchbase LiteをC/C++で利用する価値

### リソースフットプリントと実行速度の最適化

開発者は、C/C++を用いて開発することにより、IoT/エッジコンピューティングに求められる、低いリソースフットプリントと実行速度の最大化が図れます。

### 柔軟性

Cはプログラミング言語の共通語であるため、C APIを他のプログラミング言語と組み合わせて利用することによって、開発プラットフォームや開発手法に幅広い選択肢をもたらすことが考えられます。例えば、開発者は、Python、JavaScript、Rust、Goなどのプログラミング言語を使用しながら、それらの言語のネイティブサポートを活用し、Couchbase Liteを用いたアプリケーションを構築することができます。

### クライアントサイドのフィールドレベルの暗号化
Couchbase Lite C APIは、クライアントサイドのフィールドレベルの暗号化という新しい機能をサポートします(
これは、現時点では、他のCouchbase Liteプラットフォームでは利用できず、将来導入される予定の機能です)。

クライアント側の暗号化機能を使用すると、データをCouchbase Serverと同期するクライアントアプリケーションとしてCouchbase Liteを利用する際に、ネットワーク経由で複製するドキュメント内のフィールドを暗号化することができます。そして、正しい暗号化キーにアクセスできるクライアントのみが、データを復号化して読み取ることができます。

このCouchbase Liteのクライアントサイド暗号化は、Couchbase Server SDKのフィールドレベルの暗号化形式と互換性があります。


これによって、バックエンドサーバーへの機密データの安全な転送を有効にし、データにアクセスするための資格情報または特権を持たないアクターに機密データが漏洩しないことを保証することができます。


## 最後に

本稿では、Couchbase Lite 3.0.0 ベータにおけるC/C++サポートについて紹介しました。

最後に、さらなるステップに進むための情報を示して、本稿の締め括りとしたいと思います。

C/C++による実際の環境設定・コーディング・実行の例として、以下の記事を参照ください。

https://qiita.com/yoshiyuki_kono/items/5606020caffaa88ce678

Couchbase Liteについての記事を以下の投稿で整理していますので、ご関心に応じて、参照してみてください。

https://qiita.com/yoshiyuki_kono/items/6a5bbad1b02196a25417

Couchbase LiteをC/C++アプリケーション用の組み込みデータベースとして使う際のメリットのひとつとして、Couchbase Serverとの間での双方向のデータ同期があります。Couchbase Serverについては、日本語で読むことができるまとまった情報として、次の拙著を紹介させていただきます。

https://www.amazon.co.jp/-/en/%E6%B2%B3%E9%87%8E-%E6%B3%B0%E5%B9%B8/dp/429560030X/ref=tmm_pap_swatch_0?_encoding=UTF8&qid=1636588872&sr=1-1

## 関連情報
https://docs.couchbase.com/couchbase-lite/3.0/c/

https://blog.couchbase.com/couchbase-lite-in-c/

https://blog.couchbase.com/beta-release-mobile-edge-computing/

https://ja.wikipedia.org/wiki/Foreign_function_interface

https://monoist.itmedia.co.jp/mn/articles/0710/17/news140.html
"開発者による開発者のための Stripe ガイド"で書けそうなトピックをいろいろと考えてみた

Stripeで2022年1月31日まで、「[Stripeを使った開発者のためのガイド](https://jpdevguides.splashthat.com/)」のコンテストが開催されています。

中の人として、「こういうネタで応募してみませんか？」と声をかけることがあるのですが、どうせなら記事として公開したほうが「やってみようかな」となる方が多いのではと思いましたので、ここにまとめて紹介したいと思います。

# 応募方法について

コンテストには、「Stripe を活用する企業が直面しがちな課題を解決するためのガイド」を英語または日本語で公開する必要があります。

公開フォーマットは記事に限らず、映像やサンプルコードなどもOKと、それぞれがやりやすい形でガイドを作れるようになっています。

「ガイドを公開してから、応募する」必要がありますので、2022年1月31日までに公開する必要があることに注意しましょう。

# 審査基準
募集サイトには、以下の５点が審査基準として紹介されています。

- 正確さ
- 明確さ
- クリエイティビティ
- オーディエンスエクスペリエンス
- 有用性

「５指標の重み付けは同等」ですので、重厚長大なコンテンツやコードをふんだんに使ったガイドが必ず評価されるとは限らず、「いかに再現可能な形で、Stripeを使ったビジネスの課題解決方法を紹介できるか」が求められているのではないかと思います。

# 募集サイトに掲載されているガイドの例

自分の案を紹介する前に、サイトに掲載されている例を転記します。

-　SaaS ビジネスが継続支払いを受け付ける
-　多面的なマーケットプレイスの出入金管理
-　E-コマースビジネスのコンバージョン率向上
-　グローバル展開を拡大する企業に向けた各地域の決済手段サポート
-　B2B プラットフォームの新たな収入源となる決済機能の提供

# 「自分ならこういうガイドを作ったかな」と思ったネタリスト

ここからは、思いついてメモだけしてあるガイドの案をただ紹介します。
「**できるかどうかの検証もしていない**」ネタがいくつかありますので、「**やってみたら無理だった**」がありうることについては、あらかじめご了承ください。
「これ面白そうかも」と思うものがあれば、ぜひお試しくださいm(_ _)m

## 従量課金サブスクリプションを使った、作業量・時間ベースの請求システム

「時間単価XXX円、月の稼働時間をまとめて翌月請求」や「成約した件数に応じて請求を出す」ような形態で仕事を受注している方・会社向けのネタです。

Stripeの従量課金サブスクリプションに、請求する時間や案件の数を投入することで、月毎の請求作業を自動化できるのではないかと思ってメモしていました。

タスク管理や時間管理ツールがWebhookに対応していれば、それをIFTTTやAWS/GCPなどで受けてStripeの
APIを叩くようにできるかなと思います。

あとは、Stripe Connectを使ってプラットフォームを作ってしまうのも面白いかもしれません。

## マルチプラットフォームな決済UIの提供

Stripeを使うことで、さまざまな国に対して、さまざまな通貨・サービスで決済を受け付けることができます。
が、そんなStripeでもどうしても１手間必要になる部分があります。それはプラットフォーム対応です。

Stripeでの決済UIの提供は、iOS向けアプリではiOS SDKを、Android向けアプリではAndroid SDKを利用します。そしてWebではStripe ElementやCheckoutを利用します。

このように、どうしてもプラットフォームに応じた言語での実装が必要になる部分があります。

この部分を、例えば[React Native SDK](https://github.com/stripe/stripe-react-native)やCapacitor / Cordova / Flutterなどを使う方法やそのワークフローなどについて紹介するガイドもユニークではないかと思います。

さらに踏み込むと、Twilio PayでVUI分野・Unityでゲーム分野などにも進出できるとよりユニークさが増すかもしれません。

## LINEやWordPressなどのプラットフォームと深く連携したアーキテクチャ・サンプル

LINEのLIFFやWordPressプラグイン・カスタムブロックなど、決済機能をユーザーに提供するフロントエンド部分のサービス・OSSとの連携については、多くの人が興味を持っていると思います。

これらの連携方法を、徹底的にA to Zで紹介しきるガイドは、無難かもしれませんが、王道的なコンテンツになるのではないでしょうか。

## No Codeを突き詰める

StripeはWebhookとPayment Links / Invoicingなどを活用することで、ほぼコードを書かずに決済機能を手に入れることができます。
ですが、APIが使える分、ある程度の規模になると何かしらのコードを書いて動かす形になっているシステムばかりなのではないかと思います。

「本当に１行もコードを書かずに、どこまでStripeの機能をビジネスに活かし切れるか」をチャレンジして、それをガイドとして紹介するのはとても面白い取り組みではないでしょうか。

## 日本の商習慣を徹底的に再現する

月末締め翌月払いなど、Stripeのコア機能だけではまだ対応しきれていない日本の商習慣はいくつか存在します。
そんな商習慣を現在のStripe APIでどこまで再現しきれるかは、ユーザーとしてもとても興味を持つテーマではないかと思います。

## 税務や法律など、バックオフィス系業務の知識をStripeの機能にあわせて紹介する

個人情報保護法やEUのGDPRから税務に関する各法律など、「知らなかった」で終わることができない部分が決済では少なくありません。
そんな法律や条例に関するTipsを、Stripeの機能やコードに合わせて「この実装ではここに気をつけよう」のように紹介する記事は、長く重宝されるのではないかと思います。

# ガイド作成の参考になるリソース集

ここまで、思いついたものをひたすら紹介してまいりました。
以下に「実際にできるかどうか」や「実装方法の参考」などになるリソース・URLをまとめていますので、あわせてぜひご覧ください。

## 日本語
- https://stripe.com
- https://stripe.com/docs
- https://qiita.com/advent-calendar/2021/stripe
- https://adventar.org/calendars/5626
- https://adventar.org/calendars/4333
- https://adventar.org/calendars/3096
- https://adventar.org/calendars/2339

## 英語
- https://stripe.dev/
- https://github.com/stripe-samples
- https://www.youtube.com/c/StripeDevelopers/videos
Laravel-enumの使い方

#初めに
LaravelでEnumの使い方をまとめました。

#環境
・PHP 8.0.1
・Laravel 8.40.0

#公式
https://github.com/BenSampo/laravel-enum

#インストール

```
composer require bensampo/laravel-enum
```

#ファイルの作成
ファイルの作成をコマンドで実行します。

```
php artisan make:enum Type
```

コマンドを実行するとファイルが作成されます。
apps/Enums/Type.php

```
<?php
namespace App\Enums;

use BenSampo\Enum\Enum;

/**
 * @method static static OptionOne()
 * @method static static OptionTwo()
 * @method static static OptionThree()
 */
final class Type extends Enum
{
    const OptionOne =   0;
    const OptionTwo =   1;
    const OptionThree = 2;
}

```

#enumを定義

typeを1:食べ物, 2:旅行 3:音楽 4:映画と列挙型で定義します。
また日本語を使用するために「implements LocalizedEnum」を追加する。

```
<?php
namespace App\Enums;

use BenSampo\Enum\Contracts\LocalizedEnum;
use BenSampo\Enum\Enum;

/**
 * @method static static Food()
 * @method static static Trip()
 * @method static static Music()
 * @method static static Movie()
 */
final class Type extends Enum implements LocalizedEnum
{
    /** 食べ物 */
    const Food =  1;
    /** 旅行 */
    const Trip =  2;
    /** 音楽 */
    const Music = 3;
    /** 映画 */
    const Movie = 4;
}
```

テーブルのフィールドにenum型で定義する。マイグレーションファイルにenumで定義する。

```
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('article_types', function (Blueprint $table): void {
            $table->id();
            $table->foreignId('article_id')->constrained();
            $table->enum('type', Type::getValues());
            $table->timestamps();
        });
    }
```

データベースを確認するとテーブルは以下のようにenum型で定義されます。

```
mysql> desc article_types;
+------------+-----------------------+------+-----+---------+----------------+
| Field      | Type                  | Null | Key | Default | Extra          |
+------------+-----------------------+------+-----+---------+----------------+
| id         | bigint unsigned       | NO   | PRI | NULL    | auto_increment |
| article_id | bigint unsigned       | NO   | MUL | NULL    |                |
| type       | enum('1','2','3','4') | NO   |     | NULL    |                |
| created_at | timestamp             | YES  |     | NULL    |                |
| updated_at | timestamp             | YES  |     | NULL    |                |
+------------+-----------------------+------+-----+---------+----------------+
5 rows in set (0.00 sec)
```

#日本語表記を使う

表記を日本語にするために以下のように「en」から「ja」変更する。
・config/app.php

```
    'locale' => 'ja',
```

列挙型の日本語表記を設定するためにファイルを作成し定義する。

・resources/lang/ja/enums.php

```
<?php

use App\Enums\Type;

return [
    Type::class => [
        Type::Food => '食べ物',
        Type::Trip => '旅行',
        Type::Music => '音楽',
        Type::Movie => '映画',
    ],
];
```

staticのgetDescriptionで値を取得できる。

```
dd(Type::getDescription(4));
 ⇨　"映画"
```
M1 Mac上でRailsプロジェクトのyarn installができない問題を修正する

## 現象
M1 Macにて、Railsプロジェクトでyarn installしようとすると、下記のようなエラーが出る。

```
1 error generated.
make: *** [Release/obj.target/binding/src/binding.o] Error 1
gyp ERR! build error
gyp ERR! stack Error: `make` failed with exit code: 2
gyp ERR! stack     at ChildProcess.onExit (/Users/tpowell/projects/blog-post-debug/self-journal/node_modules/node-gyp/lib/build.js:262:23)
gyp ERR! stack     at ChildProcess.emit (node:events:390:28)
gyp ERR! stack     at Process.ChildProcess._handle.onexit (node:internal/child_process:290:12)
gyp ERR! System Darwin 20.6.0
gyp ERR! command "/opt/homebrew/Cellar/node/16.10.0_1/bin/node" "/Users/tpowell/projects/blog-post-debug/self-journal/node_modules/node-gyp/bin/node-gyp.js" "rebuild" "--verbose" "--libsass_ext=" "--libsass_cflags=" "--libsass_ldflags=" "--libsass_library="
gyp ERR! cwd /Users/tpowell/projects/blog-post-debug/self-journal/node_modules/node-sass
gyp ERR! node -v v16.10.0
gyp ERR! node-gyp -v v3.8.0
gyp ERR! not ok
Build failed with error code: 1
```

## 解決方法

node-sassがArmに対応していないことが原因のようです。
`@rails/webpacker`をアップグレードすると解決するようです。

```json:package.json
"@rails/webpacker": "5.4.3"
```

あとは`yarn install`すれば無事通るはずです。
【C#】null免除演算子（ビックリマーク）で警告を無視する

#概要
業務でC#を触っていたら、たまたま変数に`!`をつける方法を見つけたが、調べてみてもなかなかヒットせず…
なんと、通常の検索エンジンでは`!`という記号は調べられないとのこと！知らなかった！

ということで、C#8.0で登場した`null免除演算子`について紹介したいと思います！
本記事は、C#リファレンスの内容を簡単にまとめたものになりますので、詳しく知りたい方は下記リンクへどうぞ
↓
https://docs.microsoft.com/ja-jp/dotnet/csharp/language-reference/operators/null-forgiving

#null免除演算子(!)とは
変数`x`にnull免除演算子(`!`)をつけると、`xはnullでないことを保証する`といった意味になります。
例えば、Resharperなどのコードフォーマッタがエディタ上で警告を出している場合、null免除演算子を用いることで警告を生成しないようにすることができます。

これを用いることで、例外処理の記述を省略することができ、**コードの量を削減する**ことができます。
一方で、例外処理の記述が省いてしまうため、多用してしまうと**デバッグが困難になる**可能性があります。

具体例とともに見ていきましょう。

```C#:Program.cs
using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleApp1
{
    internal class Program
    {
        public static List<Person> PersonList = new()
        {
            new Person { Name = "桜井" },
            new Person { Name = "坂崎" },
            new Person { Name = "高見沢" },
        };

        private static void Main()
        {
            var person = PersonList.FirstOrDefault(p => p.Name == "高見沢");
            Console.WriteLine(person.Name);
        }
    }

    internal class Person
    {
        public string Name;
    }
}
```

このようなコードがあったとき、Resharperなどのコードフォーマッタを使用していると、画像のような警告文が表示されます。
![warning.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/534232/7632edca-8f5a-5e0e-05f5-a9e3cb698203.png)
この警告文を出さないようにする方法として、「変数がnullの場合は例外を投げるようにする」といった方法があります。
しかし、これを複数重ねてしまうとコードの量がどんどん増えてしまいます。

```C#:Program.cs
        // 警告文を出さないために、nullの場合例外を投げる
        private static void Main()
        {
            var person = PersonList.FirstOrDefault(p => p.Name == "高見沢");
            if (person == null)
            {
                throw new KeyNotFoundException();
            }
            Console.WriteLine(person.Name);
        }
```

そこで、null免除演算子を用いて、コードの量を抑えつつ警告文をでないようにすることができます。

```C#:Program.cs
        // null免除演算子(!)をつけると、警告文が出なくなる
        private static void Main()
        {
            var person = PersonList.FirstOrDefault(p => p.Name == "高見沢");
            Console.WriteLine(person!.Name);
        }
```

#まとめ
総合的にみるとnull演算子によるメリットに対して、**デメリットの方が多い**ように感じます。
そのため、**確実にnullでないことを保証できる場合**のみに限定するのがよいかもしれません…
Open Practice Library: 狩野モデル


# Open Practice Libraryについて  


## Practice1：狩野モデル

[Open Practice Library](https://openpracticelibrary-ja.netlify.app/)は、Red HatのサービスであるOpen Innovation Labsの顧客との関わりの中で誕生したプラクティスを、文書化して集めたものです。  
デジタルプロダクトを迅速に開発するための反復的プロセスモデル：[Mobius Loop](https://mobiusloop.com/)に基づき、各フェーズで活用できるプラクティスを検索できます。アジャイルの取り組みで活用する選択肢に活用できるため、特に日本では馴染みのないプラクティスを日本語で紹介しようと思います。

第一回目として、[狩野モデル](https://openpracticelibrary-ja.netlify.app/practice/kano-model/)を取り上げます。いきなり馴染みのないものではなく、有名なものですが、完全な日本語訳の公開許可を確認中なので、自分の言葉で書けそうなものを第一弾として取り上げます。また、デジタルプロダクトに限ったプラクティスでもないです。

### 狩野モデルとは？
狩野モデルは、[狩野紀昭教授](https://ja.wikipedia.org/wiki/%E7%8B%A9%E9%87%8E%E7%B4%80%E6%98%AD)により開発された顧客満足度のモデル。プロダクトのユーザーニーズをどれくらい満たせているかを測る尺度として品質を分類したもので以下の5つの品質領域がある。

- 当たり前品質（基本品質）
    - 備わっていて当たり前、ないとユーザの不満はとても大きくなる。ただし、ここをある一定以上のことを実装してもユーザの満足にはつながらず、過剰実装となる。
- 一元的品質（性能品質）
    - あるなしではなく、良いか悪いかで感じる品質。
- 魅力品質
    - 差別化要因となりうる品質。なくても本来はいいが、実装があるととても満足してもらえる品質。
- 逆品質
    - シンプルさを求める人にとって、機能の実装が不満足につながる
- ニュートラル
    - 満足度に影響しない、良くも悪くもない製品/機能の期待

![kanou_model.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/566760/7c776b10-4e9f-e44a-05b5-192125031929.png)

必ずしも外向けのお金を取るプロダクトだけに当てはめるモデルではなく、例えば社内のシステムで例で挙げるとすると、以下のようになる（実際に必要かは別として）

- 勤怠システム  
    - 当たり前品質： 勤務実績が入力機能。時間が計算機能。上長の承認機能。
    - 一元的品質： ユーザ入力後のレスポンスを早くする。一度に多くのデータを処理できる。
    - 魅力的品質： 連続した有給申請に対して福利厚生の活用提案。入退館システムとの連携。

### どこで使うか？
これから作ろうとしている機能、もしくは作業について、このモデルのどこにプロットされるかを可視化して、優先度付けなど議論に使用する。特にスクラムであれば、プロダクトオーナーがプロダクトバックログの並び替えを考える時や、ステークホルダーあるいはユーザと認識合わせなどに活用する。  
また、DXの文脈において、魅力的品質にプロットされる機能は、自社/自部門のプロダクトもしくはサービスが、差別化要因になる。ただ、魅力的品質は何が満足してもらえるか不確実な状況が通常であるため、仮説検証とフィードバックを繰り返して探っていくアプローチが必要となる。
npm パッケージを TypeScript で作って GitHub packeges に公開する

LIGHTz アドベントカレンダーの 10日目でございます :clap:
私は :snake: エンジニアですが、TypeScript に挑戦するチャンスをもらいました。
LIGHTz フロントを TypeScript で実装する事が多いので、 TypeScript エンジニアとして駆け出していきます :runner:

※ こちらの記事の前半(ローカルにパッケージを作成する過程まで)はこちらの記事を改変した写経です。(感謝)
https://zenn.dev/sprout2000/books/9325fe6c9c1ba9/viewer/14358
GitHub packeges に公開する過程は公式ドキュメントに倣いました。

こちらの記事で実際に作成したパッケージは[GitHub](https://github.com/nnashiki/lib_ts_npm_helloworld/packages/1131638)に置いてあります。

# npm パッケージ の作成
## npm パッケージ 最初の準備
- GitHub リポジトリを作成する
- npm init で必要事項を埋める
- `npm install -D typescript ts-node @types/node`

## ts config の作成
- `./node_modules/typescript/bin/tsc --init`
- `target` には ESNext を指定
- ES モジュールを作成したいので `module` には ESNext を指定
- `outDir` を dist に指定
- `"declaration": true` を指定
    - 型定義ファイルが欲しいので
- `"declarationMap": true` を指定

## npm script を組み立てる
- `npm install -D rimraf`
- 2 つの script を追加
    - `"prebuild": "rimraf dist"`
    - `"build" : "tsc"`

## ソースコードの用意
- `src/index.ts` に文字列 `hello` を返す関数を export 付きで定義する
- `src/use`で `hello` を返す関数を使用する
- ts-node で実行してみる

```
$ ./node_modules/.bin/ts-node src/use.ts 
hello
```

## build
- packege.json の module を記載
    - `dist/index.js` を記載
- packege.json の files を記載
    - dist を記載
- package.json の types を記載
    - `dist/index.d.ts`
- `npm run build` を実行する
    - dist に  `index.d.ts` `index.d.ts.map` `index.js` が作成されている事を確認する

## npm pack する
- `npm pack --dry-run` dry run をして確認をする
- `npm pack` する

# GitHub packeges に pubulish 編
- 参考
    - https://docs.github.com/ja/packages/working-with-a-github-packages-registry/working-with-the-npm-registry

## GitHub packeges に pubulish する
- GitHub の PAT を用意する
    - 権限はこちらを指定します 
        - ![スクリーンショット 2021-12-04 23.41.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110482/8df49873-aaf6-9002-6c78-5471b96d61b3.png)
- GitHub の npm packages に login する
    - `npm login --scope=@OWNER --registry=https://npm.pkg.github.com`
    - > OWNERを、プロジェクトを含むリポジトリを所有するユーザもしくはOrganizationアカウント名で置き換えてください。
- package.json のpackege の名前を変更する
    - > デフォルトでは、GitHub Packages は package.json ファイルの name フィールドで指定した GitHub リポジトリにパッケージを公開します。たとえば、@my-org/test という名前のパッケージを my-org/test GitHub リポジトリに公開します。
- .npmrc を追加する
    - `@OWNER:registry=https://npm.pkg.github.com` を記載
    - > OWNERを、プロジェクトを含むリポジトリを所有するユーザもしくはOrganizationアカウント名で置き換えてください。
- npm publish する
- どうやら上手く publish できたみたいです
    - ![スクリーンショット 2021-12-05 0.04.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110482/8bc2e57f-7319-8efb-e4fc-ed50bdcdada5.png)

## npm packege を install してみる
- "GitHub packeges に pubulish する" で作成した .npmrc をコピーしてくる
- npm install してみる


```
$ npm install @nnashiki/lib_ts_npm_helloworld
+ @nnashiki/lib_ts_npm_helloworld@1.0.0
added 1 package from 1 contributor and audited 2 packages in 0.753s
found 0 vulnerabilities
```

無事 registory から install することができました :beers:
フォルダにあるExcelファイルを全てpdfへ変換するVBS

フォルダにエクセルとvbsファイルを置いてvbsを実行するとフォルダ内のエクセルを全てpdfへ変換(ブック全体)

```vb:ExcelToPDF.vbs
' エクセルを開くためのオブジェクト
Set ExApp = CreateObject("Excel.Application")
' ファイル操作のためのオブジェクト
Set FSO = CreateObject("Scripting.FileSystemObject")

' vbsが配置されているフォルダ
Set CurrentFolder = FSO.GetFile(WScript.ScriptFullName).ParentFolder

' フォルダ内のファイルについて
For Each ObjFile In CurrentFolder.Files
    ' 拡張子を含む名前
    FullName=objFile.Name
        ' 拡張子
        extention=FSO.GetExtensionName(FullName)
        ' ファイル名
        FileName=FSO.GetBaseName(FullName)
        ' 拡張子がエクセルなら
        if extention="xls" or extention="xlsx" or extention="xlsm" Then
            ' 一時ファイルでなければ
            if Instr(FileName,"~$") = 0 Then
                ' エクセルを開く
                set Book = ExApp.workbooks.open(currentFolder & "/" & FullName)
                ' pdfへ変換
                Call Book.ExportAsFixedFormat(0,currentFolder & "/" & FileName)
                ' エクセルを終了
                ExApp.Quit
            End if
        End if 
Next
Databricks notebookを JobとしてRestAPI経由で実行するには



# はじめに


データブリックスのノートブックをリモートから実行出来るようになると、運用フローに取り込んだり、Webアプリなどと連携出来たりと、データやAIの活用の幅が更に広がるかと思います。データブリックスでは、jobとしてノートブックを登録することで、[RestAPI](https://docs.databricks.com/dev-tools/api/latest/jobs.html#operation/JobsRunNow)経由で実行が可能になります。

今回は、ノートブックに対して、パラーメータを引き渡して実行するまでの流れを見ていきたいと思います。



# ノートブックの内容

今回実行するノートブックには、シンプルにcsvを読み取って、左上のフィルターに年度を入力すると、その年度の情報を表示してくれるものになります。Jobでパラメーターを引き渡す際には、このように `dbutils.widgets` を使って、パラーメーターを引き渡せるようにしておく必要があります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2206424/c0c770f8-1a3c-18bf-d6e9-60532f921aa0.png)

# Job の作成

ノートブックを RestAPI経由で実行できるように Jobに登録しておきます。今回はJobの作成方法については割愛させていただきます。([@taka_yayoi さんが書かれた記事](https://qiita.com/taka_yayoi/items/b3275a1983c51a8bbe1a)などご覧ください）

Jobが作成されると、右上に `Job ID` が表示されます。こちらは後ほど RestAPI実行時に利用いたします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2206424/afac97dd-3ad4-ebdf-d387-8d201eb9f023.png)



(*) RestAPI実行前に、Job実行が成功するか、右上の`Run Now`ボタンから試しておくのをお勧めします。

# Token の取得

RestAPIの実行の際には、認証として Tokenを利用いたします。
Databricksの左下にあるペインから `setting` - `User Settings` - `Access Tokenタブ`　と移動して、`Generate New Token` をクリックして新規Tokenを取得してください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2206424/2a49618e-8475-fbfb-5392-8e26d4120e10.png)

# RestAPI実行してみる

準備は完了しました。それでは早速実行してみたいと思います。

今回は、[Talend API Tester Free Edition](https://chrome.google.com/webstore/detail/talend-api-tester-free-ed/aejoelaoggembcahagimdiliamlcdmfm?hl=ja)を利用して実行したいと思います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2206424/0fda6d5f-3d6a-54dc-4d38-245723b35859.png)

**Method**: Post
**Schema**: https://\<databricks-instance>/api/2.1/jobs/run-now
**Authorization**: Bearer \<token>
**Content-Type**: application/json

**Body**

```
{
  "job_id": 9843,
  "notebook_params": {
    "year": "2015"
  }
}
```

パラメーターは、`notebook_params`にて引き渡すことが可能です。
入力項目の[詳細はこちらのマニュアル](https://docs.databricks.com/dev-tools/api/latest/jobs.html#operation/JobsRunNow)をご覧ください。

Responseも　２００で返ってきたので Rest実行は成功したようです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2206424/0c855251-1c65-4f92-6df5-1eb50e909ec8.png)


# 結果の確認

Jobの実行結果は、Job UIの Run タブから確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2206424/54294a0a-f564-b931-71d5-2971018d1d21.png)


`View Details`をクリックするとノートブックの実行結果も確認できます。きちんと `2015` 年でフィルターされているため、パラメーターも引き渡せたことがわかります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2206424/9d849a17-31a0-c693-1650-d60a7353fe92.png)


如何でしょうか？　結構簡単に設定ができました。
RestAPIを実行できるアプリであれば、実行できますので様々なユースケースが広がるのではないでしょうか？　是非お試しください。
【Unity】ML-AgentsでIDiscreteActionMaskを使って五目並べのアクションを制限してみる

# 実行環境
- macOS Big Sur 11.6
- Unity 2019.4
- ML-Agents 0.27.0
- Python 3.6.12
- pip 21.2.4


# IDiscreteActionMaskとは？
強化学習時に、次の環境では特定のアクションを制限(以降マスキングと呼ぶ)
したい場合がある。
（例）○xゲームでいうと、既に○かxが置かれている場所には置けないようにする。など

そんな時にアクションを制限してくれるのが`IDiscreteActionMask`だ。

# 五目並べのルール
- 10x10のマスで先に5つ揃えた方の勝ちとする
![スクリーンショット 2021-12-10 16.13.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1789548/c4a95281-5166-a68d-2590-5e2ca58fcde7.png)

# マスキングの実装

``` SetMask.cs
public override void WriteDiscreteActionMask(IDiscreteActionMask actionMask)
    {
        for(int i=0;i < masking_array.Count;i++)
        {
            actionMask.SetActionEnabled(0, masking_array[i], false);
        }
    }
```

ここで一点注意。
公式ドキュメントによると、
`When using Discrete Actions, it is possible to specify that some actions are impossible for the next decision.`
と記載されており、ここでマスキングしたアクションは次の環境でのみ適応される。

以降もマスキングしたい場合は、エピソード毎にマスキングをする必要があるようだ。
よって、マスを上書きしないようゲーム終了時までマスキングし続けるために、配列を作って1個ずつマスキングを行った。

# 実行結果
実行してみると、マスに上書きがされていないことが分かる。
![画面収録_2021-12-10_16_32_59_SparkVideo.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1789548/12d7cbc9-5019-9aa4-d8dd-c01c6cacd76b.gif)

# 参考
- [Installation](https://github.com/Unity-Technologies/ml-agents/blob/release_18_docs/docs/Installation.md)
【備忘録】MacのターミナルからEC2にSSH接続する

#背景

MacでEC2に接続する際に、苦労したので備忘録として残しておきます。

#できること
Mac標準のターミナルをつかって、EC2にSSH接続できます。
SSH接続して、MYSQLのデータを確認できます。

#秘密鍵(pem)のファイル移動
EC2作成時に、秘密鍵（***.pem）をダウンロードできます。

まずは、秘密鍵を置くためのsshディレクトリを作成します。
ターミナルを開き、ホームディレクトリで下記コマンドを実行します。
※すでに作成済みであれば不要です。
`$ mkdir .ssh`

次に、作成したディレクトリに秘密鍵を移動します。
※○○○○/○○○○は、現在秘密鍵があるディレクトリを記載
`$ mv ○○○○/○○○○/***.pem .ssh`

これで秘密鍵(pem)のファイル移動は完了です。

#権限変更

秘密鍵を使用できる状態にするため、権限を与えてあげる必要があります。
`$ chmod 600 .ssh/○○○.pem`

これで秘密鍵の使用が可能な状態になります。

#ターミナルからEC2にSSH接続する

.sshディレクトリに移動し、下記を実行します。
※ユーザー名、IPはEC2作成時に設定したものを使用します。
`ssh [ユーザー名]@[パブリックIP] -i [秘密鍵名]`

下記のように表示されたらEC2にアクセス成功です。
`[ec2-user@khk-dev ~]$`

#MYSQLに接続

EC2にアクセスしている状態で、mysqlコマンドを実行する。
実行すると、テーブル等が確認可能です。
ebitenでのガベージコレクタ処理のライブラリ化

　星はじめさんによる [任天堂switchでのガベージコレクタの処理](https://docs.google.com/presentation/d/e/2PACX-1vQ7NcPdBmoVu83KqhPNNZ3X5BqDdemxgDS8lwpuqYOZNOozodmzL76GnEh8prXp96EKkBpWS5DfJOMa/pub?start=false&loop=false&delayms=3000&slide=id.p) をライブラリ化してみました

```go
package rtsGarbageCollection

import (
	"runtime"
	"runtime/debug"
)

// 強制ガベージコレクタの実行
func GC() {
	runtime.GC()
}

// golangのガベージコレクタを動かさない
func GcOff() {
	GC()
	debug.SetGCPercent(-1)
}

// golangのガベージコレクタを動かす
func GcOn() {
	GC()
	debug.SetGCPercent(100)
}

// 削除されたデータが500MBならガベージコレクタを
func GcTimer(){
	var mem runtime.MemStats
	
	runtime.ReadMemStats(&mem)
	
	if mem.HeapAlloc > 500 << 20 { // 500MB
		GC()
	}
}
```

　rtsGarbageCollection.GcOff() でgolang本体のガベージコレクタを動かさなくして、60fpsのループで60カウントしてタイマー処理を行って、 rtsGarbageCollection.GcTimer() を実行

　削除したデータが500MB以上なら強制ガベージコレクタが動きます

　golangのガベージコレクタが削除されたメモリをため込んでからガベージコレクタを動かすのでガベージコレクタが遅くなる。なので、少しずつガベージコレクタを発動して、ガベージコレクタの処理時間を減らすって事なのだろうな

　とりあえずの実装で、実際のプログラムで動かしてますが、間違っていたら教えてください

参考URL:
[https://qiita.com/ueshita/items/e6264508a4dc9738a7cc](https://qiita.com/ueshita/items/e6264508a4dc9738a7cc)

[https://making.pusher.com/golangs-real-time-gc-in-theory-and-practice/](https://making.pusher.com/golangs-real-time-gc-in-theory-and-practice/)
[AWS] CloudFront + S3 構成の環境を構築する | CloudFront FunctionsでBasic認証をかける

## CloudFrontディストリビューションを作成する

1. https://console.aws.amazon.com/cloudfront/v3/home で CloudFront コンソールを開きます。

2. 「ディストリビューションを作成」をクリックする

3. オリジンドメイン名にS3バケットを設定する(オリジンパス、名前は自動で入力される)

4. S3 バケットアクセスを選択する

> はい、OAI を使用します (バケットは CloudFront のみへのアクセスとなるように制限できます)

5.オリジンアクセスアイデンティティを作成する
>「新しいOAIを作成」をクリックする

6.「ディストリビューションを作成」をクリックする

## CloudFrontからS3へのアクセス権限を設定する

1. Amazon S3 > [プロジェクト名]へアクセス
1. 「アクセス許可」タブを選択
1. 「バケットポリシー」へ下記のポリシーを追加する


```json
{
    "Version": "2008-10-17",
    "Id": "PolicyForCloudFrontPrivateContent",
    "Statement": [
        {
            "Sid": "1",
            "Effect": "Allow",
            "Principal": {
                "AWS": "${CloudFront Origin Access Identity Key}"
            },
            "Action": [
                "s3:GetObject",
                "s3:PutObject"
            ],
            "Resource": "${S3のリソース}"
        }
    ]
}
```

## CloudFront FunctionsでBasic認証のパスワードをかける

**CloudFront Functionsとは**


>CloudFront Functions(CF2)はLambda@Edgeより手前で、シンプルな処理をより高速に、素早く、安価に実行できるサービスです。
CloudFront Functionsを使うことでこれまでLambda@Edgeで実行していたシンプルな処理をよりユーザーに近いEdge Locationで実行しつつ、高速に処理を行う事ができます


**最大実行時間1ms未満**というところが特徴です。
また現時点で JavaScript runtime 環境は ECMAScript (ES) バージョン 5.1 に準拠している。（また、ES バージョン 6 ～ 9 の一部の機能をサポートしており、ES 仕様に含まれない非標準メソッドも提供している）
Lambda@Edgeとの比較など下記参考：
https://dev.classmethod.jp/articles/amazon-cloudfront-functions-release/

JavaScript runtime 環境について参考: https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/functions-javascript-runtime-features.html





1. AWS CloudShellで`echo -n "${ID}:${PASS}" | base64` を実行する
※先にBasic認証のID、PASSをbase64でエンコードしておく
**※CloudFront Functionsには1ms 以内に処理を返さないといけない誓約があるため**

2.  https://console.aws.amazon.com/cloudfront/v3/home で CloudFront コンソールを開きます。

3. 左メニューの「関数」をクリックする

4. 「関数を作成」をクリックする

5. [関数を作成] - 名前を入力する

6. [関数を作成] - 説明を入力する

7. [関数コード] - 関数タブを選択する

8. [関数コード] - Basic認証判定の関数を実装する

```
function handler(event) {
  var request = event.request;
  var headers = request.headers;

  // echo -n user:pass | base64
  var authString = "Basic ${base64でエンコードされたIDとPASS}";

  if (
    typeof headers.authorization === "undefined" ||
    headers.authorization.value !== authString
  ) {
    return {
      statusCode: 401,
      statusDescription: "Unauthorized",
      headers: { "www-authenticate": { value: "Basic" } }
    };
  }

  return request;
}
```

参考： https://dev.classmethod.jp/articles/apply-basic-authentication-password-with-cloudfront-functions/

9.[関数コード]  -「変更を保存」をクリックする
最近のubuntuのdockerイメージのサイズ感

なるべく軽量なOSイメージを利用したくて、調べるとUbuntuが比較的軽量(glibc動かないalpineとかは除いて)。
ubuntuのlatestは20.04だが、それより当たらしいverもあり、新しいものはもっと軽量なのか??気になる。
dockerhub上や、docker searchで各イメージのサイズも一緒に確認できると便利なのだが、そういう楽な方法は無さそうだったので、地道に1個1個pullしてimagesで比べてみた

```
$ docker images
REPOSITORY   TAG       IMAGE ID       CREATED        SIZE
ubuntu       22.04     9d28ccdc1fc7   6 days ago     76.3MB
ubuntu       21.04     d662230a2592   6 days ago     80MB
ubuntu       21.10     2dc51e04d744   5 weeks ago    77.4MB
ubuntu       20.04     ba6acccedd29   7 weeks ago    72.8MB
ubuntu       18.04     5a214d77f5d7   2 months ago   63.1MB
ubuntu       16.04     b6f507652425   3 months ago   135MB
ubuntu       14.04     13b66b487594   8 months ago   197MB
```

14.04->18.04でサイズ減少頑張っていて、18.04で限界に達したのか、18.04以降は大きく変わらない結果となった。
あと以外に18.04(現行latest)が一番小さかった。

じゃあ18.04でいいか。すっきり。

# 追記

適当なWebサーバ入れて使おうとしてたけど、nginx入れたら結局apt-get updateでサイズが増えて(以下のnginx)、nginxの公式イメージと同じくらいのサイズなのでそっちでいいじゃんってなった..

```
REPOSITORY   TAG       IMAGE ID       CREATED          SIZE
nginx        latest    882c9b07e79f   23 seconds ago   162MB
ubuntu       18.04     5a214d77f5d7   2 months ago     63.1MB
```
Laravel mix で React.js と Eslint の設定メモ

Laravel mix で 一部 React.jsを使いたくなったのでその時の追加設定手順をメモ

# 前提条件
- eslintが入ってる状態


## yarn

```
$ yarn add react react-dom
```



## lint
```
$ yarn add --dev eslint@7 eslint-plugin-react
```

eslint

```
	extends: ['eslint:recommended', 'plugin:react/recommended', 'prettier'],
```


## webpack.mix.js のjs() に追加

```
.react()
```

をつける


## eslint のルール追記

```
	rules: {
		'react/prop-types': 'off'
	},
```

参考: https://cpoint-lab.co.jp/article/202107/20531/