OmniAICreator/Qiita-1.07M huggingface dataset:
Syphon出力するストップウォッチほか時計系のユーティリティを作った

![IMG_0971.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588661/c1326812-50ff-3319-843d-a700aaabc23f.jpeg)
VJ用途に！openFrameworksを使って、こんな感じでSyphonで映像出力する時計系のアプリを作ったので、その作り方を記す

画面はこんな感じ
![スクリーンショット 2023-12-25 21.20.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588661/cc8fc865-aea6-cad5-ceb6-bcb9eeda1855.png)

特徴：syphon出力を利用可能。1/100秒表示および曜日表示を選択可能。任意の文字位置・フォント・色・サイズに変更できます

追記
時計アプリ(clockSyphon)の他にも、ストップウォッチ(stopwatchSyphon)とカウントダウン時計(countdownSyphon)も作ったからよろしく！下からダウンロードできるよ！

https://github.com/ndta5

# 手っ取り早く使いたい人へ

実行ファイル（アプリ）とコードはこちらからダウンロードしてくれ

https://github.com/ndta5/clockSyphon

bin.zipに実行ファイルが入ってるので解凍して使用してください。

警告が出て開けないときは、こちらを参考にして開いてね。

https://getnavi.jp/digital/858046/

# 空のプロジェクトを生成

openFrameworksでofxGuiとofxSyphonを適用してプロジェクトを生成する
プロジェクト名はclockSyphonとした
詳しくは下記参考リンクを参照
![スクリーンショット 2023-12-25 22.19.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588661/3d82740c-b845-dab4-ce77-d21d5bfe1f80.png)

すると`openFrameworks/apps/myApps`配下に`clockSyphon`プロジェクト一式が生成される
![スクリーンショッ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588661/994a7a7d-2d5f-f977-3f95-8faebf202b3a.png)

適当なフリーフォントをダウンロードして先ほど生成したプロジェクトの
`openFrameworks/apps/myApps/clockSyphon/bin/data`配下に置く

![スクリーンショット 2023-12-25 22.14.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588661/35e83d62-0136-ad7e-35b5-cdce90933a3b.png)

フリーフォントはLatoがおすすめ

https://fonts.google.com/specimen/Lato

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

# コーディング

コーディングをする。
コードは下記のGithubに置いてあるからダウンロードしてくれ。

# アプリをビルド&実行
アプリをビルド&実行する

![スクリーンショット 2023-12-31 13.39.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588661/11c736ad-bd21-475f-f3e1-a897337698f2.png)


# VDMXでSyphon映像を受信する

右クリックでAdd clips from Built-in Sources->syphon->oFアプリを選択
![スクリーンショット 2023-12-25 22.06.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588661/7087d484-945a-1c3c-0512-16e1e1f4e213.png)


これ非常に重要！
表示したSyphonレイヤーのComposition ModeをOpenGL- Overに変更する
![スクリーンショット 2023-12-25 22.07.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588661/b108ba82-b779-5b09-9247-701dc15d70fa.png)

# 参考リンク

Syphon

https://syphon.github.io/

マウスドラッグ、マウスボタンプレス

https://yoppa.org/ma2_10/1739.html

ofxGUI ColorPicker

https://forum.openframeworks.cc/t/ofxcolorpicker-missing-argument-list/35497 

文字の縁どり

https://www.ei.tohoku.ac.jp/xkozima/lab/ofTutorial4.html 

openFrameworksとSyphon

https://qiita.com/HXYoUGZ/items/8f98485ef2480d6a3f18

# MadMapperで映す
Syphon > VDXM5をクリックする
![スクリーンショット 2023-12-30 18.37.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588661/4344394c-dc35-0f42-3542-ae63125e02a7.png)

https://madmapper.com/

# Modul8で映す
Syphonをクリックする
![スクリーンショット 2024-01-02 19.03.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588661/234a4a88-9d1c-35d8-8db6-3f47035af9de.png)

http://www.garagecube.com/modul8
ERROR関数を使ったBigQueryデータ異常検知例

# はじめに

DBのデータ異常検知というと、検知したいデータを抽出して
それをDB以外の何かしらの処理で判定するイメージですが
BigQueryにはERROR関数という関数でも検知できるのでご紹介します。


# ERROR関数について

こちらに詳細があるので参照ください。
この関数は出力するとBigQueryのクエリ実行結果がエラーになります。

https://cloud.google.com/bigquery/docs/reference/standard-sql/debugging_functions?hl=ja

これがどう検知に繋がる？ってことで例をご紹介します。


# 例

## データ重複

重複を検知したい場合
例：idが重複した場合は異常

```sql
SELECT
    ERROR('id is not unique.')
FROM
    table
GROUP BY
    id
Having
    count(1) > 1
```

ERROR関数が出力されると以下のように実行結果がエラーとなります。
クエリが失敗したらアラートを投げる要領で異常検知ができるのがSQL書く人には嬉しいですね。

![スクリーンショット 2022-12-18 23.40.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/48316/83ea7636-c020-accc-05d7-4a5474137d98.png)


## 異常件数 

指定件数より少ないなら異常としたい場合

例：10,000件より少ない場合は異常

```sql
SELECT
    CASE
        WHEN count(1) < 10000 THEN ERROR('abnormal count.')
    END
FROM
    table
```

![スクリーンショット 2022-12-19 0.16.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/48316/a3509f6b-ba94-3c5d-53f6-3fa6e69a34a3.png)



特定の値の件数が指定割合より少ないなら異常としたい場合

例：statusがNewの割合が全体の10%より低い場合は異常

```sql
SELECT
    CASE
        WHEN status = 'New' and rate < 10 THEN ERROR('abnormal rate.')
    END AS check
FROM (
    SELECT
      status
      ,(count(1) * 100)/(select count(1) from table) as rate
    FROM
      table
    GROUP BY
      rate
)
```

![スクリーンショット 2022-12-19 0.16.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/48316/27c8611c-6860-f7e3-7f9a-719ed5e0c593.png)

## おまけ：ASSERT

ERROR関数に用途が似ているASSERTというステートメントがあります。

https://cloud.google.com/bigquery/docs/reference/standard-sql/debugging-statements?hl=ja

結果をboolで返す必要があり、falseの場合エラーとなるステートメントです。
これを使っても先の例と同等の検知は可能です。

ただし、ASSERTの場合はbool型の1レコードで返さないといけないので
先の"指定割合より少ないなら異常としたい場合"例だと
case文にelse true/falseつけないとboolにならないとか
group by checkしないとレコードが一意にならないとかで
正常にASSERTを通すためだけに、本来の目的よりも余分なクエリを書く必要があります。

目的が特定の異常検知だけであれば、ERROR関数の方が目的に沿った検知だけできて
お手軽だな。という使ってみての所感です。

テスト目的でデータ全体の事前評価をするとかであれば
ASSERTの方が網羅性があうケースも多々あるでしょうし
用途や書き方によってはASSERTの方がスッキリ書けることもあるかと思います。

お好みや用途に合わせてお使いいただくと良さそうです

# 最後に

BigQueryのデータ異常検知の手段として
SQL書く人にとってはお手軽に作れる粋な関数の紹介でした

特にデータの抽出内容を高頻度で変えてる場合は
仕込んでおくと盾になってくれるやもしれません。

ご参考までにの例のご紹介でした。
[Android] Bitmapのrecycle()は何をしているのか

Bitmapの処理を行なっているライブラリのコードを読むと、Bitmapの`recycle()`メソッドが呼ばれていることに気づく。そこで、Bitmapのrecycle()の挙動について調べたので、以下にまとめる。

### AndroidバージョンごとのBitmapのメモリ管理の違い

前提知識として、以下の公式ドキュメントによれば、Bitmapのメモリ管理の方法は、Androidのバージョンによって異なる。

https://developer.android.com/topic/performance/graphics/manage-memory

まず、Android 2.2 (API level 8) 以下では、GCが発生する際に、アプリのスレッドが停止される。一方、Android 2.3でconcurrent GCが追加され、Bitmapへの参照が無くなった直後に、メモリが回収されるようになった。

また、Android 2.3.3 (API level 10) 以下では、Bitmapのピクセルデータはネイティブメモリに保存される一方で、Bitmap自体はDalvik (JVM) 側のヒープ領域に保存される。これにより、ネイティブメモリに保存されたピクセルデータは自動で解放されず、OOMやクラッシュの原因となることがある。
ゆえに、Android 2.3.3 (API level 10) 以下では、`recycle()`を呼び出すことで、メモリを解放することが推奨されている。

Android 3.0 (API level 11) からAndroid 7.1 (API level 25) では、ピクセルデータはBitmapと併せて、Dalvikのヒープ領域に保存される。Android 8.0 (API level 26) 以降では、ピクセルデータはネイティブヒープに保存される。

### Bitmapのrecycle()は何をしているのか

以下の公式ドキュメントを参考に、実際にrecycle()が何をしているのかを記載する。

https://developer.android.com/reference/android/graphics/Bitmap#recycle()

> Free the native object associated with this bitmap, and clear the reference to the pixel data. 

Bitmapに関連づけられたネイティブ側のオブジェクトを解放し、ピクセルデータへの参照を除外する。

> This will not free the pixel data synchronously; it simply allows it to be garbage collected if there are no other references.

これは、ピクセルデータを同期的には解放しない。すなわち、他に参照がない場合に限り、ガーベジコレクションされることを許可するのみである。

> The bitmap is marked as "dead", meaning it will throw an exception if getPixels() or setPixels() is called, and will draw nothing.

`recycle()`が呼ばれると、そのBitmapは"dead"としてマークされる。そうなると、`getPixels()`や`setPixels()`等のメソッドが呼ばれた際に例外を投げるようになり、何も描画しない。

> This operation cannot be reversed, so it should only be called if you are sure there are no further uses for the bitmap.

`recycle()`の操作は不可逆的なものである。つまり、そのBitmapをこの先使用しない場合に限って呼び出されるべきである。

> This is an advanced call, and normally need not be called, since the normal GC process will free up this memory when there are no more references to this bitmap.

これは高度な操作で、通常呼ぶ必要はない。なぜならば、通常のGCの処理によって、Bitmapへの参照が無くなった時点で、メモリが解放されるからである。

上記のように、`recycle()`のドキュメントにおいては、Android 3.0以降を想定しているからか、通常`recycle()`を呼ぶ必要はないとしている。

### その他の参考記事

https://stackoverflow.com/questions/3823799/android-how-does-bitmap-recycle-work

https://www.united-bears.co.jp/blog/archives/382
Azure Site Recoveryの構成準備

●Azure Site Recoveryの構成準備
■Recovery Servicesコンテナーの作成
リージョン間でレプリケーションをおこない、プライマリリージョンでの障害発生時には別のリージョンへのフェールオーバーを行う。
そのため、Azure仮想マシンが存在しているリージョンとは異なるリージョンにRecovery Serivicesコンテナーを作成する必要がある。
また、そのRecovery Servicesコンテナーを格納するリソースグループも、Azure仮想マシンとは異なるリージョンのものを選択　または作成する必要がある。

※Azure Site RecoveryによってレプリケーションされるのはAzure仮想マシンのディスクのみ。したがってリソースグループや仮想ネットワークなどは別途作成する必要がある。また、可用性セットを使用する仮想マシンに対しては、可用性セットの構成なども必要。

※マルチVM整合性を有効にすると、レプリケーショングループを構成できるが、有効にした場合は、レプリケーショングループ内の単一の仮想マシンをフェールオーバーすることはできなくなり、復旧計画を使用したフェールオーバーが必要。
Wrikeを外部から更新する（Postman編）

# 本記事でやりたいこと
WrikeのAPI機能を使って外部からAPIによる更新を行います

# Wrikeとは

https://www.wrike.com/ja/

Wrikeはチケットでタスクなどを管理するWEBツールです
類似製品としてはRedmineやBacklogになります

# Wrikeの設定
## アプリを新規作成
Wrikeのマイページに入ったら右上にある自分のプロフィール画像をクリック＞アプリ＆連携


左端からAPIを選択
アプリ名を入力して新規作成

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/137987/11b1d072-be17-b226-a032-60757d83da95.png)

## トークンの生成

画面下の方にあるトークン生成ボタンを押してトークンを生成します
ログイン時に使用するパスワードを要求されるので入力してください



![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/137987/13f94da3-6d0c-75ff-480b-aafd057c47ad.png)

**注意：ここで表示されるトークンは一回ページから離れると見れなくなるのでメモして別途保存ください**

# Postmanの設定
下記のURLからPostmanのAPI Collectionとenvironmentの設定をダウンロードして自分のPostmanにインポートします

API Collection

https://cdn.wrike.com/CSO/Official_Wrike_Collection.zip

environment

https://cdn.wrike.com/CSO/Wrike_Postman_environment.zip

・WRIKE_TOKEN
先程コピーしたtokenをValueに貼り付けます

・WrikeAPI
バージョンが違うのかもしれませんがAPI Collectionと変数名が違うのでWRIKE_API_URL→WrikeAPIに変更します
ValueはそのままでOK

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/137987/98f4579c-0cb9-b082-fe61-9218864855d1.png)

## テスト

インポートしたAPI Collection＞Contacts＞Get Information about meの下にあるRequestを開きます

また右上のenvironmentから先程インポートして設定したWrikeのenvironment設定を選択します

Requestを実行して自分のアカウントのユーザー情報が取得できればOKです

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/137987/d23552d6-de0e-b6eb-b9c4-8d7a9a7d8d2d.png)

### 参考URL
https://help.wrike.com/hc/en-us/community/posts/360051771073-Ask-Me-Anything-with-Wrike-Integrate-Part-2-Intro-to-Postman-and-Webhooks

# 次回以降にやりたいこと

公式にもありますが、WrikeからのTeamsやSlackなどへの連携

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/137987/9c2b8175-338f-c2f5-132e-ba3082babbd2.png)

WEBアプリなどからの今回PostmanでテストしたAPIの実装

# 参考
## WrikeのHelpページ
https://help.wrike.com/hc/ja


## Wrikeのdeveloperページ
https://developers.wrike.com/


## WrikeのCommunityページ
https://help.wrike.com/hc/en-us/community/topics
Kubernetesで起動するnginxにconfigを頑張って注入する

nginxの公式Dockerコンテナイメージ、他のイメージよろしく`/docker-entrypoint.d/`になんかしらshell scriptなどを置くと実行する機能があります。

https://hub.docker.com/_/nginx

でも現状の実装ではファイル実体でなくsymlinkを置かれたときに無視[^find-nofollow]されます。今使ってるKubernetes実装[k0s](https://k0sproject.io/)はConfigMapをvolume mountするとsymlinkを置くので読んでもらえない。

[^find-nofollow]: `find`に`-follow`がついていないことによる。

https://github.com/nginxinc/docker-nginx/pull/731

なので本家にPRを投げたんだけど、それはそれとして現状でどうすればいいかやってみた。

## したかったこと

単純にConfigMapを`/etc/nginx/conf.d/`にmountすると`default.conf`がなくなって動かなくなる[^content-of-defaultconf]ので、`/etc/nginx/conf.d/` の中身を保持したまま設定ファイルを追加したい[^entrypoint-pettern]。

[^content-of-defaultconf]: `default.conf`は80/tcpでlistenして`/usr/share/nginx/html`の中身を返す単純なhttp serverの単純な設定が入っているだけなので、その設定も含めて書くなら上書きしてもいい。

[^entrypoint-pettern]:`/docker-entrypoint.d/`にshell script置いたConfigMapを`0744`でmountして実行してくれるなら、それでなんとかできる。

## どうしたか

- `/etc/nginx/conf.d/`の中身を保存する[emptyDir](https://kubernetes.io/ja/docs/concepts/storage/volumes/#emptydir)ボリュームを作る
- [initコンテナ](https://kubernetes.io/ja/docs/concepts/workloads/pods/init-containers/)で`/etc/nginx/conf.d/`の中身を組み立てる。
    - nginxコンテナを起動して`/etc/nginx/conf.d/`をemptyDirボリュームにコピる
    - Volume mountしたConfigMapの中身をemptyDirボリュームにコピるbusyboxコンテナを起動
- 前段で作ったemptyDirボリュームを`/etc/nginx/conf.d/`にマウントしてnginxを起動する。

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: nginx
  name: nginx
  namespace: sample
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx:1.23.3
        name: nginx
        ports:
        - containerPort: 80
          name: http
        volumeMounts:
        - name: contents
          mountPath: /usr/share/nginx/html
          readOnly: true
        - name: confd
          mountPath: /etc/nginx/conf.d
      initContainers:
      - name: copy configmap
        image: busybox:1.34.1
        command:
        - cp
        - -r # copy directories recursively
        - -L # always follow symbolic links in SOURCE
        - -T # treat DEST as a normal file
        - /config/
        - /confd/
        volumeMounts:
        - name: config
          mountPath: /config
        - name: confd
          mountPath: /confd
      - name: copy nginx conf
        image: nginx:1.23.3
        command:
        - cp
        - -r # copy directories recursively
        - -L # always follow symbolic links in SOURCE
        - -T # treat DEST as a normal file
        - /etc/nginx/conf.d/
        - /confd/
        volumeMounts:
        - name: confd
          mountPath: /confd
      volumes:
      - name: confd
        emptyDir: {}
      - name: contents
        persistentVolumeClaim:
          claimName: nginx-contents-pvc
      - name: config
        configMap:
          name: nginx-config
```

### 注意点

initコンテナで`cp`するとき、`-T`をつけないと期待しないディレクトリが生成されてはまった[^cp-t]。
[^cp-t]: [cp でコピー先ディレクトリのあり/なし - Qiita](https://qiita.com/ngyuki/items/ea4d99cf29aa3d24655c)
テストを自動化したので、目的とポイントについて考えてみる

# はじめに
最近、APIのテスト自動化のためにPostmanを使う機会がありました。
私自身、テスト自動化というのが初めてだったので、

>「自動化って必要なの？？手動でできるじゃん。」
「そもそも、目的は何？？」

といった、素朴な疑問がありました。
なので、この記事では復習もかねてテスト自動化の目的やポイントについて考えてみようと思います。

:::note 
**Postman**
PostmanはAPIを開発・テストするためのアプリケーションです。
PostmanではTokenなどを設定し、APIのエンドポイントにリクエストを送ることで、レスポンスを受信することができます。
:::

# テスト自動化の目的は何か

まずは、テストを自動化の目的について自分なりに考えてみます。

### ○ テスト実施回数を増やす
従来のウォーターフォール型の開発では、開発工程の後にテスト工程がありそもそもテストの実施回数が少ないことが前提とされていたように感じます。
しかし、アジャイル開発のように短期間で一連の開発工程を繰り返すような場合、プルリクエストやマージごとにテストを実施することが必要になります。

1日に複数回テストを実行することが前提であれば、1回の実行時間を数分から数十分に抑え、かつ並行してテストを実行できるのは大きなメリットにつながります。

### ○ 品質の向上
1つ目にあげたようにテストの実施回数が増えるため、その分早くバグに気づくことができます。
また、定義したテストを実行するので、手動テストで起こるようなヒューマンエラーを防止することができます。

ただし、逆をいえば定義したテストしか実行しないためすべてのテストを自動化することは難しく、さらに、テスト作成時に考慮漏れがあった場合、その領域はテストが行われないためバグが発生してからテストが不十分だったことに気づくというパターンがあります。

### ○ 新規バグではなく、既存バグを見つける
1つ前でも書きましたが、自動テストは定義したテストを実行します。
なので、テスト作成者が起こりうるエラーを想定してテストを作成します。

手動テストのように、テストをしていてたまたま別のエラーに気づくというのは稀なケースであり、あくまでデグレを起こさないことを担保することが自動テストでは重要な役割かと思います。

# テスト自動化には失敗が多い？
テスト自動化について調べていると、「テスト自動化」「失敗」というキーワードでたくさんの記事を目にしました。
それぞれの記事で様々な理由があげられていましたが、私自身、テストを自動化してまず認識を改めなければいけないと感じたのは**自動テストシステムの開発は継続的におこなうもの**という点です。
これは、「テスト自動化の8原則」にも記載があります。
>テスト自動化に関わる苦労を10とすると、自動テストシステムが完成するまでが3、残りの7は運用に関するコストである。

:::note 
[テスト自動化の8原則](https://sites.google.com/site/testautomationresearch/test_automation_principle)
「テスト自動化」の技術領域の定義と啓発を目的とする、日本のテストコミュニティの有志による「テスト自動化研究会(STAR: Software Testing Automation Research Group Jp)」によって議論されたテスト自動化で考慮すべきポイントをまとめたもの。
:::

テスト自動化を行う上で、この運用に関する労力を想定していない場合、システムを修正するたびに広範囲でテストスクリプトの修正が必要になったり、一度のテスト実行で大量のエラーが発生しテストの結果分析に多くの時間を奪われたり、結果的にシステムの修正に対してテストの更新が追いつかなくなるといった問題が起こります。

こういった問題を起こさないためにも、次のことを注意する必要があると思います。

### 1. 自動化する範囲を明確にする
テスト自動化は、繰り返し実行することでメリットを生みます。
手動で行っていたテストをそのまま自動化したり、すべてのテストを自動化することは、テスト実行の俊敏性を下げる要因です。
目的に対して自動化したほうがいいテストとそうでないテストを明確にすることが大切です。

### 2. テスト観点を明確にし、無駄なテストをなくす
無駄なテスト項目を増やすことは、品質と運用の効率を低下させる原因になります。
例えば、システムを修正した際にテストスクリプトの修正範囲を広げる可能性がありますし、本当に確認したいテストも埋もれさせてしまいます。

テスト観点と期待する結果がわかるようにすることで、無駄なテスト項目を減らすと同時にシステムを修正した際に削除・追加・更新するべきテスト項目がひと目で判断できるようになります。

### 3. 自動テストが失敗したら、すぐにテストスクリプトを修正する
優先したいタスクや緊急での対応では、どうしてもテストスクリプトの修正を後回しにしてしまいがちです。
しかし、1度テストの失敗を放置するとすぐにテストスクリプトの修正範囲は広がり、テスト失敗はシステムではなくテストスクリプトに原因があると考えるようになります。
自動化されたテストの信頼度が落ちてしまうと、いつしか自動テストは使われなくなり結果的に手動でテストをしているといった状態になってしまいます。

これを避けるためには、CIに自動化したテストを組み込むことです。
コミットごとに、自動化したテストを実施することでバグの早期発見にもつながりますし、テストスクリプトの修正範囲や影響箇所も抑えることができます。
何より、テストスクリプトの修正が義務化されるため自動テストの信頼度を維持することができます。

:::note 
**CI (Continuous Integration : 継続的インテグレーション)**
[継続的インテグレーションとは?](https://aws.amazon.com/jp/devops/continuous-integration/)
継続的インテグレーションは、開発者が自分のコード変更を定期的にセントラルリポジトリにマージし、その後に自動化されたビルドとテストを実行する DevOps ソフトウェア開発の手法。
:::

# さいごに
1年目の頃は、テストとはなんて地味で華がない作業なんだろうと思っていました。笑
今年で3年目になりますが、当初地味だと思っていたテストで泣くこともあれば、助けられる思いをしたこともあり、経験を重ねる中でテストの重要度と難しさを感じています。
特に、最近ではテストを行うにも技術的な知識や経験が必要となり、私の中ではテストも開発の一部のような感覚になっています。

今回、テスト自動化を導入したことにより私の中でテストに対する認識が改められ、テストにより関心を持つきっかけとなりました。自動化のツールも色々とあったので、それぞれ試してみたいなと思います。
IchigoJamのPWMをオシロスコープで覗いてみた

# 動機
3年前に使い方もよくわかっていない状態で購入したオシロスコープ。
開封すらしていなかったので使ってみたかった。

*オシロスコープ(英: oscilloscope)：電気的な振動（=oscillation オシレーション）をスクリーンに表示する装置である(Wikipedia)

# 計測するもの
IchigoJamのPWMの波形

*PWM パルス幅変調（パルスはばへんちょう、英語: pulse width modulation、PWM）：変調方法の一つであり、パルス波のデューティ比を変化させて変調すること。(Wikipedia)

サーボモーターの制御やLEDの明滅などに利用

# IchigoJam側でのコマンドとその結果

### ケース1
コマンド PWM 2,1000,2000
(=ポート2に2000=20msecの内1000=10msecに3.3Vの電圧を出力する)

計測結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/71746/741a4db1-6ece-e788-67f1-3a662bbc0712.png)
Cycl=0.019s≒20msecで惜しいですが、Duty＝10msec/20msec=50%で正確に計測できています。
1秒間=1000msec。20msecｘ50回＝1000msecなので50Hz。これも正確に計測できていますね。


### ケース2
コマンド PWM 2,500,2000
(=ポート2に2000=20msecの内500=5msecに3.3Vの電圧を出力する)

計測結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/71746/96dd55e9-4e8a-f8fd-f478-2c3746396459.png)
同じくCycl=0.019s≒20msecで惜しいですが、Duty＝5msec/20msec=25%で正確に計測できています。

# 感想
過去にPWMをサーボモーターやLEDの明滅に利用したことはありましたが、改めて波形や周期を目で見ることができ、理解が進みました。
LT会運営経験者から見た軽量LT運営について。

昨年度に「話せる人が話したいだけできる」LT会の運営を行なっていたのですが、その時の経験や感じたことなどを忘れないうちに書き留めておこうと思います。
# LTについて

## LTは好きな技術を広めるチャンスであり、技術をまとめるチャンスである。
https://docs.google.com/presentation/d/1SRsBx_YEQrrzMtALthOKouRpx9FW9CmadGJJYDDnZiI/edit?usp=sharing

自分の学んだ興味のある技術を調べたりまとめ上げることは意外と大変です。具体的には、自分の作ったアプリの技術面などを改めて洗い出す行為やどこを売り出すかこだわりポイントかを決めて話さないといけないため、その部分が大変に思えます。~~実際に周りを見ていると数を積めばできるようになる類のものにも思えますが。。~~



# LT運営サイドについて
## 筆者が運営していたLTについて
行なっていたLT会の一覧は以下のリンクから確認できます。来年度にはまたLT会を企画できればと考えております。

https://connpass.com/user/yuria_cats/open/

## LT会小規模であれば運営する「だけ」なら簡単！

LT会運営が準備すべきものは以下の4つです（オンラインの場合）
- 公開範囲（Youtube配信、Zoom、Discord等）
- 時間（休日の１３時半から１４時くらいから始めるのがおすすめです。）
- 事前に参加する人が発言できるかの確認（当日グダらないために必要）
- ファリシテートのためのオープニングクロージングの資料
- ネタのテーマ（あったほうが良い）

オフラインの場合ここに場所代がかかってきますが、オンラインであれば場所代等を気にせず気軽に開催できるのがメリットと言えるでしょう。
喋れそうな人に事前に日程調整のDMだけ募り人を確保しその日程でLT会を主催すると人を集めやすいかなと思います。

## LT会の大変さは登壇の公開範囲の調整にあり

ただし、ボトルネックになるのは「イベントの公開範囲」で「対面等のクローズなイベントならしゃべれるがYoutubeにアーカイブで残されると困る」みたいな公開範囲によってLTのネタや表現方法が変わってくる登壇者もいらっしゃいます。
そのため、事前に以下に対しては裏取りを行ってから募集をかけるといいでしょう。**アーカイブ残すかどうかはかならず事前に決めておきましょう**
- この公開方法でコンセプトを満たせるか
- 公開方法の実現が可能か（機材や自分自身の運営能力などが足りるか）

## LT会の運営も学生が有利！
学生という~~免罪符~~で新しいことにチャレンジすると学生同士で興味を持ってくれたり、「属性」というのは検索容易性と参加障壁にかなーーーりプラスの効果を持っているため、学生のうちにできることは学生でしたほうが失敗時のリスクを「学生だから」に押し付けられて便利だぞ。~~私はその言い訳に縋らないようになりたいです。~~

## 広報の大変さ

LT会で地味に大変なのが広報だと思います。広報に関しては
- 自分よりも発信力・そのコミュニティに詳しい人を頼る
- Twitterで夜に発信する
- 参加者として類似のイベントに登壇する。
がポイントとして大事になってくると思います。

応用として
- 定期的に開催する
ことでLT会のリピート率も上がるとは思います。次の日程だけは事前に押さえておきましょう。

# 最後に　オンラインLT会もっと登壇・観覧しよう！
LT会、需要はあるのですがなかなか供給が少ないような体感があります。上記の事務的な手間もあると思いますが、参加者を募るのがとにかく大変ですので推したいLT会があればみんな参加しなくてもTwitterで拡散することやできることならば試聴でもいいので参加してくださると運営者としては励みになると思います。**推しは推し活してこその推しだぞ**
【完走賞ゲット-19】パワポ（Office系のソフトで OK）を使って画像の背景除去を行って p5.js で扱う

## はじめに
こちらは、[完走賞ゲットのため小ネタを毎日投稿しようとチャレンジする Advent Calendar 2022](https://qiita.com/advent-calendar/2022/youtoy) の 19日目の記事です。

## 今回の内容
今回の内容は、タイトルの通りで、それを行った過程や結果は以下のとおりです。

最終結果となる、p5.js で背景透過がされた PNG画像を読み込んで表示させた状態は、以下の通りです。
![最終結果](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/7f808136-6e2f-cbb6-6da2-b04c42de3958.png)

また、p5.js　のキャンバス上で表示している画像ができるまでの過程（パワポで背景除去を行っている過程）は、以下の動画の通りです。

https://twitter.com/youtoy/status/1604478588954480640

この事例では、「意図的に、背景分離が行いやすそうな状態にした」、というのはあるのですが（※ こちらは後述）、活用できそうな性能ではありそうです。
簡単な画像加工をパワポで行うことがある自分としては、とてもありがたい話です。

## 背景除去機能の話
今回、パワポ上で使った背景除去について、公式の情報は以下のとおりです。

●Office で画像の背景を削除する - Microsoft サポート
　https://support.microsoft.com/ja-jp/office/office-%E3%81%A7%E7%94%BB%E5%83%8F%E3%81%AE%E8%83%8C%E6%99%AF%E3%82%92%E5%89%8A%E9%99%A4%E3%81%99%E3%82%8B-c0819a62-6844-4190-8d67-6fb1713a12bf

以下の画像をパワポ上に読み込ませて、上記の背景除去を行いました。
![背景除去を行う対象にした画像](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/23756bff-997d-e040-e12f-f5786efdc1ec.png)

## p5.js でお試し用に作ったプログラム
p5.js で PNG画像を読み込んで表示した際、p5.js Web Editor上で書いたプログラムを掲載しておきます（単純な、画像の読み込みと表示を行う p5.js のプログラム、ではありますが...）。

```javascript
let img;

function preload(){
  img = loadImage("./device.png");
}

function setup(){
  createCanvas(400, 350);
}

function draw(){
  background(0);
  image(img, 100, 0, img.width/3, img.height/3);
}
```

## 元の画像を少し切り出して使った話
上で、「意図的に、背景分離が行いやすそうな状態にした」と書いていた話の補足です。

今回の例で用いた画像は、Twitter で自分が数日前にツイートしていた画像の一部分です。
そして、元画像は以下の通りです。

https://twitter.com/youtoy/status/1603361684017491968

写真の上のほうにうつっている製品の箱が、フローリングの色に似ています。
見るからに、画像処理による分離に悪影響を与えそうな感じです...

そして、最初は背景除去機能のお試しを、この画像全体を対象にやってみたのですが、さすがに写真の上のほうの箱（フローリングの色に似ているもの）を残そうとすると、苦戦しました。
実際の利用時にそういったパターンも混じってくることがあるとはいえ、もっとシンプルな事例も多々あると思われたため、最終的なお試しは元画像の左下あたりを切り出したもので試しました。
ノートの作成・管理・ふりかえりソフトウェア　　「FrogNote」を開発しました

# はじめに
こんにちは！秋空と申します。この記事は[C# Advent Calendar 2022](https://qiita.com/advent-calendar/2022/csharplang)の記事です。
この記事を読んでFrogNoteがどんなソフトウェアかをある程度理解していただければと思います。
ですが、技術的な話をメインで書くので紹介は軽く済ませます。

# 開発の動機
以下のような問題を抱えていました。
- レイアウトを考えながらだと情報をまとめることに集中できない
    - もっと簡単にノートをとれるようにしたい！
- 昔書いたノートを探すのに苦戦した。もっと簡単に管理したい！
- 物理的に棚を占有するため、取っておくのが難しい！

# 目的
動機からこのような目的を立てました。
- ノートをとる負担を減らす。
- 管理を簡単にする。

これらをすべて解決します。

# 紹介動画
いろいろ細かく書くと長くなるので紹介動画を載せます。この動画を見ればある程度の機能は把握できます。

<iframe width="560" height="315" src="https://www.youtube.com/embed/Gq4BlQsoAnk" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

お使いくださいと言っていますが、まだ手段が整っていないのため、配布できていません。
近いうちにリリースする予定なので、興味がある方は私の[Twitter](https://twitter.com/DTB_AutumnSky)を見に行ってください。

# 技術的な話
UI層以外は気合いで実装する内容なので省略して、UI層を中心に話します。
私の感想成分が多めなので情報は正確ではないです。予めご了承ください。

## WPFを使っています
採用した理由として、学習コストが安くて強力なデータバインディングの仕組みがあるという事が挙げられます。
そして、WPFで使えるライブラリとして[Material Design In XAML Toolkit](https://github.com/MaterialDesignInXAML/MaterialDesignInXamlToolkit)というものがあり、簡単に(私の場合はなんちゃって)マテリアルデザインを再現できるという事も利点となります。

### 問題
#### `RichTextBox`がすこぶる使いづらいです。
具体的には、
- Wordのような文字を入力する前に文字色・フォントを変更する機能がない。
    - 気合いで実装しようとしましたが私の技術では厳しかったです。(やり方を知っている方は教えてほしいです……)
- `Document`プロパティは双方向データバインディングに対応していません。単方向も工夫が必要。

が挙げられます。

## アーキテクチャ
### 概要
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1208939/0bf341bd-4225-0934-103f-31e1e5309e69.png)
オニオンアーキテクチャとMVVMパターンを組み合わせたものを採用しています。250個近くクラス・インターフェイスを実装することになりましたが、これを採用しているだけでコードの治安はかなり維持されました。

### バグ修正が楽になります
どこにどの処理があるかがなんとなく予想がつくようになるのでバグを見つけやすかったです。階層化しない場合はいろいろな方向に依存して分かりづらい状態に陥り易いからです。

### 依存性逆転を前提にしているので面倒くさい部分が多いです
例えば、`View Model`側から新しく`Window`を表示したいとします。通常であれば`View Model`から`View`へ依存することになりますが、依存が下位から上位レイヤへの一方向ではなくなってしまうので、階層化したメリットがほぼなくなります。

それを回避するには`View Model`に`Window`を表示するためのインターフェイスを定義し、`View`側で実装しておく必要があります。

## 失敗した事
### `View Model`がデカくなった
大量のコマンドと画面に表示する内容で`View Model`がカオス手前になってしまいました。

### `Command`の実装
#### `Command`たちはWinUI3でも再利用できる。
しかし、`View Model`に依存した形で`Command`を実装したため、WinUI3へ移行する際に同じ`View Model`を使わなければ、`Command`をそのまま再利用できないという問題が発生しました。難しいとは思いますが可能な限り`Command`は`View Model`より上位の層として扱ったほうが良いかもしれません。

さらに、`View Model`は実装するコード的には依存していませんが、`View`の構造を意識して実装しています。そのため、`View Model`が再利用できない可能性も考慮する必要がありました。

### カスタムコントロールがFrogNoteの仕様に大きく依存している
カスタムコントロールは他のソフトウェアでも使えるものが多かったですが、使いまわしが難しい状態になってしまいました。コントロールをXAMLで作る時も上手く分割するように心がければよかったと思います。

# 感想
実用に堪えるソフトウェアが完成してとりあえず安心といったところです。しかし、スマートフォンと連携できないのは致命的なので、複数の端末・OSで利用できるようにしていきたいです。
最後まで読んでいただきありがとうございました。
ONNXモデルをC#で推論すーる（回帰分析、Mac）

# はじめに
[前回](https://qiita.com/SatoshiGachiFujimoto/private/96bef334535ce960dc7a)の記事でCreateMLで作成したCoreMLのモデルをONNXに変換しました。今回は、ONNXのモデルをC#のコンソールアプリで推論してみましょう

# 開発環境
- MacBookPro 2018
- Visual Studio for Mac

# 導入

1．[前回](https://qiita.com/SatoshiGachiFujimoto/private/96bef334535ce960dc7a)の記事を参考にONNXモデルを用意します

2．Visual Studio for Macをインストールします

https://visualstudio.microsoft.com/ja/vs/mac/

3．コンソールアプリケーションを作成します

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63863/9fa81aba-ca3b-f3ee-477d-bb0386c776ca.png)

4．ターゲットフレームワークは.NET7.0です

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63863/b93f6c9c-50c8-2612-82d8-5892ef040caa.png)

5．プロジェクト名を入力します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63863/795d1980-6391-bfcc-bc23-a2c0b70e63c6.png)

6．ツール->NuGetパッケージの管理からMicrosoft.ML.OnnxRuntimeをインストールします

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63863/5dfd7175-ae8a-c8d9-c3ca-2ba2fc8f6acd.png)

7．Program.csを編集します

~~~Program.cs
using Microsoft.ML.OnnxRuntime;
using Microsoft.ML.OnnxRuntime.Tensors;

float[] age = new float[1] { 19.0f };
float[] bmi = new float[1] { 27.0f };
float[] children = new float[1] { 1.0f };

Console.WriteLine(Directory.GetCurrentDirectory());
var session = new InferenceSession("../../../insurance.onnx");

Tensor<float> t_age = new DenseTensor<float>(age,session.InputMetadata.First().Value.Dimensions,false);
Tensor<float> t_bmi = new DenseTensor<float>(bmi, session.InputMetadata.First().Value.Dimensions, false);
Tensor<float> t_children = new DenseTensor<float>(children, session.InputMetadata.First().Value.Dimensions, false);

var inputs = new List<NamedOnnxValue>() {
    NamedOnnxValue.CreateFromTensor<float>("age",t_age),
    NamedOnnxValue.CreateFromTensor<float>("bmi",t_bmi),
    NamedOnnxValue.CreateFromTensor<float>("children",t_children)
};

var results = session.Run(inputs);
Console.WriteLine(results.First().AsTensor<float>()[0]);
~~~

8．insurance.onnxをプロジェクト直下に置きます

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63863/f148ed20-270d-48c2-bf36-171968c16b39.png)

9．実行してみましょう

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63863/c91f3f89-a6f5-cfd3-81fa-f2f4b31acbad.png)

お疲れ様でした。

# 関連記事
- [CreateMLで回帰分析すーる](https://qiita.com/SatoshiGachiFujimoto/items/8df23bb3e37ba00b5159)
- [CreateMLで作ったCoreMLのモデルをONNXに変換すーる（回帰分析）](https://qiita.com/SatoshiGachiFujimoto/private/96bef334535ce960dc7a)
任意形状のColliderとSphereColliderの接触判定を行う [Unity]

# はじめに
IKを使って歩行アニメーションを行う場合などに、任意形状の地面との距離や接している場所の法線を取得したいことがあります。
そんなときに使える、任意形状の`Collider`と`SphereCollider`の接触判定を行い、接している場合は`SphereCollider`から見て任意形状の`Collider`上の1番近い位置とその位置の法線ベクトルなどを返す静的メソッドを紹介します。

# 実装

```CS:PhysicsUtil.cs
using UnityEngine;

public static class PhysicsUtil
{
    /// <summary>
    /// sphereColliderがtargetColliderに接しているか調べ、接している場合はclosestPosとsurfaceNormalを計算する
    /// </summary>
    /// <param name="targetCollider">sphereColliderが接しているかを調べる任意のコライダー</param>
    /// <param name="sphereCollider">targetColliderに接しているかを調べる球体コライダー</param>
    /// <param name="closestPos">
    /// targetCollider表面上の、sphereColliderの中心に最も近い点。
    /// 接触していない場合はゼロベクトルを返す
    /// </param>
    /// <param name="surfaceNormal">
    /// 接触しているtargetCollider表面の法線ベクトル。
    /// 接触していない場合はゼロベクトルを返す
    /// </param>
    /// <param name="surfacePenetrationDepth">
    /// sphereColliderをtargetColliderから引き離すのに必要なsurfaceNormalに沿った距離(どれだけtargetColliderがめり込んでいるか)。
    /// 接触していない場合は0を返す
    /// </param>
    /// <param name="scale">sphereColliderで考慮するスケール</param>
    /// <returns>sphereColliderがtargetColliderに接しているか</returns>
    public static bool ComputeClosestPosition(Collider targetCollider, SphereCollider sphereCollider,
        out Vector3 closestPos, out Vector3 surfaceNormal, out float surfacePenetrationDepth,
        float scale = 1f)
    {
        closestPos = Vector3.zero;
        surfaceNormal = Vector3.zero;
        surfacePenetrationDepth = 0f;

        if (targetCollider == sphereCollider) return false;

        Transform targetTrans = targetCollider.transform;
        Transform sphereTrans = sphereCollider.transform;
        Vector3 spherePos = sphereTrans.position;


        bool isOverlap = Physics.ComputePenetration(targetCollider, targetTrans.position, targetTrans.rotation,
            sphereCollider, spherePos, Quaternion.identity, out surfaceNormal, out surfacePenetrationDepth);

        if (isOverlap)
        {
            closestPos = spherePos + surfaceNormal * (sphereCollider.radius * scale - surfacePenetrationDepth);
            surfaceNormal = -surfaceNormal;
        }

        return isOverlap;
    }

    public static bool ComputeClosestPosition(Collider targetCollider, SphereCollider sphereCollider,
        out Vector3 closestPos, out Vector3 surfaceNormal)
    {
        return ComputeClosestPosition(targetCollider, sphereCollider, out closestPos, out surfaceNormal, out _);
    }

    /// <summary>
    /// LayerMaskに指定のLayerが含まれているかを調べる
    /// </summary>
    /// <param name="layerMask"></param>
    /// <param name="layer"></param>
    /// <returns></returns>
    public static bool Contains(this LayerMask layerMask, int layer)
    {
        return layerMask == (layerMask | (1 << layer));
    }
}
```
python 関数の可変長位置引数について改めて考える

## 可変長位置引数（スター引数）
可変長位置引数を用いると関数の呼び出しコードが簡潔になり読みやすくなる。

以下のように初めての人と知り合いで発言を区別するコードを記述する。
~~~python:exam.py
def hello(message, name):
    if not name:
        print('nice to meet you')
    else:
        print(f'{message}! {name}さん')


hello('こんにちわ', 'saku')
hello('こんにちわ', []) # 第二引数を指定しないとTypeErrorが発生する

>>>
こんにちわ! sakuさん
nice to meet you
~~~
このコードに可変長位置引数を使ってみる
~~~python:exam2.py
def hello(message, *name):
    if not name:
        print('nice to meet you')
    else:
        print(f'{message}! {name}さん')


hello('こんにちわ', 'saku')
hello('こんにちわ')

>>>
こんにちわ! ('saku',)さん
nice to meet you
~~~
このようにすると、nameがわからない場合でも第二引数を指定せず、関数を呼び出すことができ、コードも読みやすく簡潔にできる。

## 可変長位置引数を使う上での問題①
<b>可変個数の引数を関数に渡す際にタプル変換されてしまう</b>
~~~python:exam3.py
# ジェネレーター関数
def generator():
    for i in range(10):
        yield i


def main(*args):
    print(args)


G = generator()
print(main(*G))

>>>
(0, 1, 2, 3, 4, 5, 6, 7, 8, 9) # タプルに変換されている
None
~~~
このようにジェネレータからのすべての値がタプルに含まれているので、データ量が多い場合メモリを大量に消費してクラッシュを引き起こす可能性がある。
⇒つまり、*argsを関数に渡す場合はあらかじめ入力要素数が少ないとわかっているのが理想的である。

## 可変長位置引数を使う上での問題②
~~~python:exam4.py
def family(grandfather, father, *kids):
    if not kids:
        return (f'祖父: {grandfather}, 父:{father}')
    else:
        return (f'祖父: {grandfather}, 父: {father}, 子供: {kids}')


print(family('おじいちゃん', 'お父さん', 'たかし', 'カツオ'))
print(family('おじいちゃん', 'お父さん'))
print(family('お父さん', 'たかし'))

>>>
祖父: おじいちゃん, 父: お父さん, 子供: ('たかし', 'カツオ')
祖父: おじいちゃん, 父:お父さん
祖父: お父さん, 父:たかし
~~~
可変長位置引数を定義しない場合は、関数がうまく機能するが、可変長位置引数の前に定義されるべき引数を欠損すると問題が発生してしまう。
⇒このバグは例外を吐き出さず実行されてしまうのでたちが悪い。
## ポイント
* <b>可変長位置引数（*args）を使って関数の引数をすっきりさせよう！</b>
* <b>ジェネレータと可変長位置引数を使うとメモリクラッシュの原因足りうる。注意しよう</b>
* <b>可変長位置引数と使った関数に、引数を追加したり、削除したりすると例外を発生させないバグのもとになってしまう。注意しよう。</b>
* <b>可変長位置引数を使った関数の引数は、キーワード引数や型ヒントを用いることで安全に拡張できる可能性がある</b>

## 参照
Effective Python
【ルール説明・safety】risky-octal

こちらの記事は[Ansible lint Advent Calendar 2022](https://qiita.com/advent-calendar/2022/ansible-lint) 18日目の記事になります。

今回はルール[risky-octal](https://ansible-lint.readthedocs.io/rules/risky-octal/)について説明します。

### risky-octal

[risky-octal](https://ansible-lint.readthedocs.io/rules/risky-octal/)はファイルやディレクトリのパーミッションが`8進数で記載された場合先頭の0を含む`もしくは`u+rwxやu=rw,g=r,o=rなどの記号モードで書かれている`かを検証します。

YAMLで整数または8進数の値を使用すると、予期しない動作になることがあります。たとえば、YAMLローダーは0644を10進数の420として解釈しますが、644と記載すると意図した結果にならない時があります。

以下はパーミッションを設定する機能があるモジュールです。（他にもあるかもしれません）

* [ansible.builtin.assemble](https://docs.ansible.com/ansible/latest/collections/ansible/builtin/assemble_module.html)
* [ansible.builtin.copy](https://docs.ansible.com/ansible/latest/collections/ansible/builtin/copy_module.html)
* [ansible.builtin.file](https://docs.ansible.com/ansible/latest/collections/ansible/builtin/file_module.html)
* [ansible.builtin.get_url](https://docs.ansible.com/ansible/latest/collections/ansible/builtin/get_url_module.html)
* [ansible.builtin.replace](https://docs.ansible.com/ansible/latest/collections/ansible/builtin/replace_module.html)
* [ansible.builtin.template](https://docs.ansible.com/ansible/latest/collections/ansible/builtin/template_module.html)
* [community.general.archive](https://docs.ansible.com/ansible/latest/collections/community/general/archive_module.html)
* [community.general.ini_file](https://docs.ansible.com/ansible/latest/collections/community/general/ini_file_module.html)

また上記のそれぞれのAnsibleモジュールのパーミッションの設定項目の説明を見る限り`8進数で指定した方が良い`みたいです。

禁止事項として記載はされてはないのですが、u+rwxやu=rw,g=r,o=rなどの`記号モード`は採用しない方が良いような雰囲気はそれぞれのモジュールの説明からは読み取れます。

#### 問題のあるコード

```yaml
---
- name: Example playbook
  hosts: localhost
  tasks:
    - name: Unsafe example of declaring Numeric file permissions
      ansible.builtin.file:
        path: /etc/foo.conf
        owner: foo
        group: foo
        mode: 644
```

#### 修正されたコードその１

```yaml
---
- name: Example playbook
  hosts: localhost
  tasks:
    - name: Safe example of declaring Numeric file permissions (1st solution)
      ansible.builtin.file:
        path: /etc/foo.conf
        owner: foo
        group: foo
        mode: 0644 # <- 先頭に「0」を付与する
```

#### 修正されたコードその２

```yaml
---
- name: Example playbook
  hosts: localhost
  tasks:
    - name: Safe example of declaring Numeric file permissions (2nd solution)
      ansible.builtin.file:
        path: /etc/foo.conf
        owner: foo
        group: foo
        mode: "0644" # <- ダブルクウォートで囲み文字列に変換する
```

### パーミッションを設定しないとどのような権限でファイルやディレクトリが作成されるのか

しっかり調べてないですがおそらくシステムのunmaskの設定によると思われます。CentOSだとファイル作成時のパーミッションは644、ディレクトリ作成時は775です。これは大きすぎる権限かもしれません。

しかしAnsible lintを採用すればこれらは解決されます。Ansible lintではファイルやディレクトリの作成時はパーミションが付与されていないとエラーになりますがこれは[risky-file-permissions](https://qiita.com/mamono210/items/9d405c09c278af841715)により検証されます。

Ansibleでファイルやディレクトリを作成する際は常にパーミッションに注意を払います。

### 参考サイト

* [risky-octal — Ansible Lint Documentation](https://ansible-lint.readthedocs.io/rules/risky-octal/)
IchigoJam でBMIを求める試み

[IchigoJam](https://ichigojam.net/) でBMIを求めようとしてみた。

# BMI

BMIは、体重[kg]を身長[m]の2乗で割った値で、肥満度のチェックに用いられる。

* [BMI | e-ヘルスネット（厚生労働省）](https://www.e-healthnet.mhlw.go.jp/information/dictionary/metabolic/ym-002.html)
* [ボディマス指数 - Wikipedia](https://ja.wikipedia.org/wiki/%E3%83%9C%E3%83%87%E3%82%A3%E3%83%9E%E3%82%B9%E6%8C%87%E6%95%B0)

# IchigoJam BASICとBMI

IchigoJam BASICの変数は、-32768～32768の整数しか扱えず、BMIをそのまま求めるのは難しい。
そこで、身長はcm単位、体重はkg単位の値を10倍した値を入力してもらうことにした。
これらの値は、通常の人間であれば10進数で2～4桁程度の値であると想定され、まあ扱えそうである。
この身長の値を`H`、体重の値を`W`とおくと、身長[m]は`H/100`、体重[kg]は`W/10`なので、BMIは以下のように表せる。

```
BMI = (W/10) / ((H/100)*(H/100))
    = (W/10) / (H*H/10000)
    = (W/(H*H)) * 1000
```

さらに、BMIを小数第1位まで表示するため、この値にさらに10を掛ける。
すると、求める値は`W`を`H`で2回割って`10`を4回掛けた値ということになる。
「10を掛ける」は、さらに「2を掛ける」と「5を掛ける」に分解できる。
そこで、

1. 「掛ける」操作をなるべく多く行って、割る際の誤差を減らす
2. 「掛ける」操作ができなくなった (行うとオーバーフローする、または行う操作が無い) 場合は、「割る」操作の回数が残っていれば「割る」操作をする
3. 「掛ける」操作も「割る」操作もできなくなったら終了

という戦略で計算を行うことにした。

# プログラム

今回は、簡単のため「掛ける」操作をなるべく多く行うための計算を省略し、適当な貪欲法を用いている。
これでは、50行目と60行目を入れ替えると結果が変わることがあり、最適な操作になっていないようである。

```
10 ' BMI ｹｲｻﾝ
20 INPUT "ｼﾝﾁｮｳ(cm):",H
30 INPUT "ﾀｲｼﾞｭｳ(kg ﾉ 10 ﾊﾞｲ):",W
40 X=4:Y=4:Z=2:B=W
50 IF X>0 AND B<6553 B=B*5:X=X-1:GOTO 50
60 IF Y>0 AND B<16384 B=B*2:Y=Y-1:GOTO 50
70 IF Z>0 B=B/H:Z=Z-1:GOTO 50
80 ?"BMI:";B/10;".";B%10
```

# 実行結果例

例として大石泉さんの情報 (157cm 41kg) を入力してみた。
[大石泉 (おおいしいずみ)とは【ピクシブ百科事典】](https://dic.pixiv.net/a/%E5%A4%A7%E7%9F%B3%E6%B3%89)

![大石泉さんのBMIを求めてみた](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/facc9377-b64a-1e32-ff4c-14459fbc64a0.png)

結果は16.5と出たが、この場合の正しいBMIは約16.63であり、誤差が出ている。

# 今後の課題

* 割り算を行う際の誤差を最小にできるような掛け算のやり方を求めるプログラムを作る
* 多倍長計算やマシン語を用いて正確な値を求める

# おわりに

※IchigoJamはjig.jpの登録商標です。
Google UXデザイン：モックアップ（ゲシュタルトの原理：類似性・近似性・共通領域）

# はじめに

わたしが[Google UX Design Certificateで得られた素晴らしい体験][google]を、特に重要だと思われるポイントを小さく分割して、わかりやすく簡潔に紹介していきます。

興味があれば、ぜひ Google UX Design Certificateを受講してみてください。

[google]: https://qiita.com/mapami/items/7a0d0a3d4661c50f8bec

https://www.coursera.org/professional-certificates/google-ux-design

<details><summary>これまでの振り返り</summary>

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358573/f2187262-b8bf-f99d-7690-daa56c7093fc.jpeg" width="700">

</details>

# ゲシュタルトの原理とは

ゲシュタルトの原理は、人間が物体を知覚する際に、類似の要素をグループ化し、パターンを認識し、複雑なイメージを単純化する方法を説明しています。つまり、人間の脳は、複雑なイメージを単純化し、整理するために、無意識のうちにパーツを配置して、全体としてまとまりを持たせようとするのです。

これまで紹介してきた複数のビジュアルデザインの原則を使用して強調するのと同様に、複数のゲシュタルトの原則を1つのモックアップデザインに同時に適用することで、視覚的なつながりを持たせることができるのです。

今回、ゲシュタルトの原則のうち、「類似性」「近接性」「共通領域」の3つについて、紹介します。

:::note info
ゲシュタルトの原則は、今回紹介するもの以外もたくさん存在します。
:::

# 類似性

***<font color="MediumVioletRed">『見た目が似ている要素は、同じ機能を持っていると認識される』</font>***

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358573/0ed2327e-1d31-4c07-caca-727e8c9e9a44.png" width="250">

- アクションをオレンジ色で表現しています。画面上部の「犬の散歩」タブ、「犬の散歩」を選択したチェックマーク、「次へ」ボタンは、すべてユーザーが選択できるアクションであり、同じ色を使っているのです。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358573/6b1c235c-a767-88e0-2216-74e165e35929.png" width="250">

- アプリの新しいページに移動しても、オレンジ色はユーザーが取るべきアクションを指し示し続けています。これは、色彩が私たちの目に見えるものをグループ化するのに使われている例です。脳は、このオレンジ色の要素を自動的にグループ化します。同じ色だから、同じ機能を持つものとして認識されるのです。

- ページの円はすべて、ユーザーのプロフィールを表しています。すべての円は、ページ上で同じ視覚的な重みをもっています。ユーザーがSarah M.のようなプロフィール写真を持っていようと、AliceやSalのような頭文字を使っていようと、あなたの脳は自動的に、これらの類似した図形が同じアイデア、つまりユーザープロフィールを表しているという関連付けを行います。アプリの中で同じ大きさの円を見たときにも、瞬時にこの結びつきができるのです。

# 近似性

***<font color="MediumVioletRed">『近くにあるもの同士はつながっていると認識する』</font>***

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358573/1e06d580-f209-7b4a-75eb-ee90b1fff2d6.png" width="450">

- 矢印にあるボタンは近いので、関連性があることが分かります。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358573/c4e0a766-0d31-5604-a286-69a6c5b49a30.png" width="450">

- 一方、２つの矢印間にある要素の間にスペースがあることで、関連性がないことがわかります。

# 共通領域

***<font color="MediumVioletRed">『同じ領域内にある要素はグループ化されていると認識される』</font>***

近接性と関連していますが、共通領域の原則は、要素の間隔とはあまり関係がなく、視覚的要素を使ってセクションを形成することに関係があるため異なります。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358573/666696fc-cf1d-6e1f-ef8f-66504c7cc3aa.png" width="300">

- 選択したコンテンツの後ろに青い背景を使い、共通領域を作っています。ユーザーは、青い背景のアイテムが選択されており、白い背景のコンテンツが選択されていないアイテムであることをすぐに認識することができます。

# さいごに

ゲシュタルトの原理から得られる重要な教訓は、私たちの心が自然に要素を整理する方法とデザインを連動させるべきだということです。

ゲシュタルトの原理をうまく利用すれば、ビジュアルバランスを生み出し、デザインに完成度を与えます。
クソコードと対峙してきた過去の経験について語る。

# 結論

[リーダブルコード](https://amzn.to/3HOMM5J)と[リファクタリング](https://amzn.to/3W97wt9)を読むべし。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/533960/40666245-4796-c2de-5328-6ff1b9403f20.png)

# 背景

私は ~~日本の伝統的な~~ 製造業でソフトウェアエンジニアを担当しています。
トラディショナルな日本企業ゆえ、**クリーンなコードを書ける人材は絶滅危惧Ⅰ類**です。
このような人種が絶滅することを防ぐべく、今回、筆を取らせていただきます。

# 私が出会ってきたクソコードたち

これまでこのようなコードを観測してきました。

- 理解不能な命名がなされた関数や変数が大量に存在しているコード
- スネークケース、パスカル、キャメルなどが混在しまくっているコード
- 一切使われていない関数や変数が大量に存在しているコード
- コメントが一切書かれていない８，０００行を超えるコード
- if文が３〜４層は当たり前のように入れ子しているコード
- ゴリゴリにパスワードをオンコードしているコード
- ORマッパー(TypeORM)を導入しているのにも関わらず、わざわざDBクライアントのライブラリを入れて、ゴリゴリSQLクエリをオンコードしているコード
- Nuxt3(Vue3フレームワーク)を導入しているのにも関わらず、Vue2で書いているコード

ああ、思い出したら腹が立ってきた・・・

# そもそもなぜクソコードを量産してしまうのか

コードの書き手が、**そもそも「クソコードを量産していることを認識をしていない。」** というのが私の仮説です。
では、なぜ認識できないのでしょうか。
それは、**「クリーンなコードを書こうという強い意思や余裕がない。」** ということに起因しているかと思います。
日本における開発はウォータフォール型が主流です。（最近は徐々にアジャイルも浸透してきたけど…）
予算とスケジュール、品質を予め決めた上での開発の場合、決定した要件から外れてくると、ユーザと開発者の双方がとても不幸になります。
1. 予算超過やスケジュール遅延によって、ユーザは失望する。
1. 開発者は予算や納期を守ろうと品質の悪いクソコードを書き続ける。
1. 品質が悪化することで、バグが発生し、納期も予算もさらに超過してユーザと開発者は絶望する。

**このような地獄を経験したユーザや開発者の方、いらっしゃいませんか。**（私とすぐ仲良くなれると思います。）
こんな地獄の中で、リファクタリングする意思や余裕が生まれるでしょうか。
なかなか難しいんじゃないかな、と思います。

# クソコードを量産しないために

さて、ここまで読んで頂いた方の多くは、クソコードによって苦しめられた経験のある方だと思います。
新たな被害者を生まないためにすべきことは **「クリーンなコードを書けるよう努力を積み重ねる」** ことだと考えています。
では、どんな努力を積み重ねればよいのでしょうか。
私からのご提案です。
 
**[リーダブルコード](https://amzn.to/3HOMM5J)と[リファクタリング](https://amzn.to/3W97wt9)という本に目を通してください。**

どちらも原書(洋書)を翻訳しているのもあって、多少、表現がわかりにくい部分もあるかと思います。
しかし、コーディング例が豊富にあるため、まずはこの本を読んで、真似をしていくことはできます。
「真似る」は「学ぶ」。ぜひ、ご一読ください。

# おわりに

たまたまアドベントカレンダーで、[このようなイベント](https://qiita.com/advent-calendar/2022/kaonavi)を見つけたのをきっかけで執筆させていただきました。
このようなきっかけをご提供いただいた、Qiita様、スポンサーのカオナビ様には多大なる感謝をもって、本稿の結びとさせていただきます。
読者の皆様も読んでいただいて誠にありがとうございました。
初めて書いて感じたテストコードのメリット

[アドベンチャーアドベントカレンダー2022](https://qiita.com/advent-calendar/2022/adventure)の19日目の記事です。

# おまえ誰やねん
株式会社アドベンチャーでサーバーサイドのエンジニアをしている者です。
新卒でエンジニアとして仕事をし始め、2022年4月から、エンジニア3年目の節目に入社しました。日々勉強をしながら、いつの日か強いエンジニアになれる日を夢見て日々仕事をしてます。

# どんな記事か
アドベンチャーに入社してからエンジニア人生で初めてテストコードを書く機会に恵まれました。前の会社ではデバッグ業務をやっていたことはあったのですが、テストコードを書いたことがなく、「どういう観点でテストをするべきなのか」「そもそもテストコードの書き方もわからん」みたいな状態でした。テストコードに関して何もわからない状態でしたが、実際に書いてみるとめちゃめちゃ便利でした。

この記事では「テストコードってこういうところが便利だよ！」　という話を、まだテストに詳しくない人にも伝わるように話して行きます。
「テストコードかくあるべし」「こういう書き方の方がベター」みたいなテクニカルな話というよりかは、「確かにそれは便利！」「同じ悩みを抱えていたけど、テストコードを書くことで解決するんだなぁ」みたいな直感的に伝わる話をしていきます。

# 便利だと感じたこと
### リファクタしやすい
ビジネスロジックに修正が必要なことが多々あると思います。テストコードがないと、どこまで影響範囲があるか分からず、ビクビクしながら修正・確認をしたことがある人も多いと思います。
もしビジネスロジックに修正があっても、テストコードがあれば予期せぬバグやデグレを事前に検知することができ、安心感を持って修正やリファクタを行うことができると感じました。

### 設計書になる
実際にコードを書く前に先にテストコードを書いておくことで、そのテストコードが設計書の役割を果たすことになります。（TDDの考え方かも）
テストコードにそのロジックの正しい振る舞い、間違った振る舞いが記載されているので、数ヶ月後の自分や他のエンジニアがそのロジックを修正することになっても、テストコードを確認すれば、そのロジックの正解・不正解を確認することができます。

### 実装に気を配るようになる
テストコードを書くことを前提にしていると、実装にも気をつけてコードを書くようになります。
テストパターンが増えないようになるべくシンプルに書いたり、if文やswitch文などの分岐をなるべく使用しないようにしてみたり。

テストコードを理解することで、より良いコードを書くことにつながるのではと感じました。

# 気をつけたいこと
### 無意識に都合のいいテストを書かないこと
ロジックを実装した本人がテストコードを書くと、そのロジックについてよく知っているため、無意識にパスしやすいテストコードを書く傾向があるかなと感じました。
実装をする前に先に最低限のテストコードを書くなどして、なるべくフラットな状態で書くことで防げるかと思います。

### テストコードを過信しすぎないこと
テストコードは結局のところ、想定できるエラーを検知するための仕組みでしかないと考えています。
動かしてみるまでは、そのコードが本当に正しいのかは分からないと思っていますし、もしかしたら想定していないバグやデグレが発生する可能性もあります。
テストコードだけではなく、システムテストや受け入れテストも必要です。
あくまでも、実装したコードを「正しいと思われる状態」に近づけるためのツールだと考えておくことも大事かなと思います。

# まとめ
自分自身、テストコードを書くまではその必要性を感じていませんでしたが実際に書いてみて多くのメリットを感じました。
この記事が読んでくださった方の参考になれば幸いです。
【GMC-4】某仮装大賞風メーター

学研の4ビットマイコンGMC-4で某仮装大賞風メーターを作ってみた。

# 今回の仕様

* 2進LEDが全て消えている状態から開始する。
* E・F 以外のキーを押すと、2進LEDの点灯数に応じた音が鳴り、2進LEDが1個追加で点灯する。既に全て点灯している場合は何も起こらない。
* E キーを押すと、2進LEDが5個以上点灯している場合はエンド音、そうでない場合はエラー音を鳴らす。(合否判定)
* F キーを押すと、2進LEDが全て消灯する。(リセット)

# プログラム

このプログラムは、[MikeAssembler](https://mikecat.github.io/mike_assembler/) でアセンブルできる.

```
target gmc4

; [0] : ゲージの位置
; [0xE],[0xF] : 消灯用

	TIA 0
	TIY 0xE
	AM
	TIY 0xF
	AM
	TIY 0
	AM
key_release_wait:
	KA
	JUMP key_wait
	JUMP key_release_wait
key_wait:
	KA
	JUMP key_wait
	; F (リセット) が押されたか判定
	CIA 0xF
	JUMP no_reset
	CAL DSPR
	TIA 0
	AM
	JUMP key_release_wait
no_reset:
	; E (判定) が押されたか判定
	CIA 0xE
	JUMP no_judge
	; 判定
	MA
	AIA 0x10 - 5
	JUMP judge_passed
	; 不合格
	CAL ERRS
	JUMP key_release_wait
judge_passed:
	; 合格
	CAL ENDS
	JUMP key_release_wait
no_judge:
	; カウントアップ指示
	MA
	CIA 7
	JUMP not_saturate
	; 既に最大なので、カウントアップしない
	JUMP key_release_wait
not_saturate:
	; カウントアップする
	CY
	CAL SETR
	CY
	AIA 1
	AM
	CAL SUND
	JUMP key_release_wait
```

以下は、このプログラムの機械語表現である。

```
  | 0 1 2 3  4 5 6 7  8 9 A B  C D E F
--+-----------------------------------
0 | 8 0 A E  4 A F 4  A 0 4 0  F 1 2 F
1 | 0 B 0 F  1 2 C F  F 2 3 E  D 8 0 4
2 | F 0 B C  E F 3 8  5 9 B F  3 3 E 8
3 | F 0 B E  7 F 0 B  5 C 7 F  4 1 F 0
4 | B 3 E 1  3 9 1 4  E B F 0  B
```

# 実行結果

<iframe width="560" height="315" src="https://www.youtube.com/embed/gEbFqQ0Fuik" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
非エンジニアがCData Sync で複数のCSV ファイルをSnowflake にレプリケートしてみた。

# はじめに

こんにちは、CData Software Japan でOperations Manager をやっている佐藤です。この記事は、Qiita CData Software Advent Calendar 2022 18日目の記事になります。

今回はCData Sync を使って手許（ローカルドライブ）にある複数のCSV ファイルをSnowflakeへレプリケーションしてみました。

# CData Syncについて
CData Sync を使用するとkintone、Salesforce をはじめとしたSaaS、各種アプリケーションのデータを単一のDB やDWH へノーコードで集約できます。

https://www.cdata.com/jp/sync/

普段は経理をはじめとした事務仕事をしている私ですが、本当にノーコードでデータを単一DBへ集約できるのでしょうか？？

# CData Sync のインストール

CData Sync のインストールについてはこちらの記事をご参照ください。
https://qiita.com/kuwazzy/items/30e501a0ac4f60a9178d


# もろもろの設定
## データソースとなるCSV ファイルの準備
まずはおもむろにExcelを開き、練習用の特に意味の無いファイル複数個を作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2954401/af56c439-3399-aace-bfd8-ca13b8d20f04.png)
.csvで保存。しかし、本当に意味が無い・・・。


## Snowflake 側
Snowflake側にDBを準備します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2954401/1d6fc94a-1126-1ad6-4c04-eca2549c0891.png)

Data → Database → 右上の +Database というところを押すと新しいDBが作れます。
必須項目はDBの名前だけですね。今回は「Sync」としておきました。

## Sync側
接続先にSnowflake を追加
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2954401/8d586d98-d7dd-0a40-ac18-71ca687ffc68.png)
接続 → 同期先 → Snowflake で追加。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2954401/603b34f2-1035-f2f8-02eb-1e6adda5d9c5.png)

|  項目  |    |
| ---- | ---- |
|  名前  |  今回は「Snowflake」としました  |
|  プロバイダークラス  |  デフォルト（変更不可）  |
|  Auth Scheme  |  Password |
|  URL  |  Web画面にログインするときのURL  |
|  Password  |  Web画面にログインするときのPW  |
|  Waregouse  |  COMPUTE_WH  |
|  Schema  |  PUBLIC  |

次はデータソースとしてCSVを追加

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2954401/7d913805-1665-1a9e-848d-19b1dec4a746.png)
URIには先ほど作成した意味の無いCSVファイルが格納されているフォルダを指定

最後にジョブを作成。データソースにCSVを指定し、同期先にSnowflakeをそれぞれ指定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2954401/1b41a1b1-9bc1-1b34-63ce-c1a5a0e3c88e.png)

特にプロパティはいじらずタスクを作成し実行してみる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2954401/7c551fba-5ddb-2c25-00f1-47bf67842b19.png)
これが出てくれば成功！（最初csvファイルをExcelで開いていたらエラーになった。編集中だとダメみたいです。）

Snowflake側でDBを見てみる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2954401/ab05537d-db78-2924-0060-9689f4fc41cf.png)
先ほどの特に意味のないcsvがレプリケートされています。


## まとめ
普段は事務仕事の経理のおじさんでも、CData SyncとSnowflakeを使用して、複数のCSVファイルを1つのDBにまとめることが出来ました！
今回は特に意味のないCSVファイルを特に意味も無くDBへ集約するという、無駄に洗練された無駄の無い無駄なデータ連携になってしまいましたが、様々な業務システムから出力されるcsvファイルをひとつのDBに集約することが出来たら日常のレポーティングに役立てたり、経営判断に役立つ分析が容易になりそうです。
Video.js完全マニュアル part3 〜初期化で何が起きているのか？〜

# はじめに
Video.js完全マニュアルとしてVideo.jsの解説をしていくシリーズのpart3です。
Video.jsの公式のガイドである[Video.js Guides](https://videojs.com/guides)を翻訳ツールを使いながら解説していきます。

前回はVideo.jsの用意する埋め込みタグ種別紹介を解説したのですが、そこでVideo.jsの初期化について触れました。
ところで初期化とは何なのか、何が起きているかを調べてみましょう。

前回
[Video.js完全マニュアル part2 〜Video.jsの埋め込みタグ種別紹介〜](https://qiita.com/manzoku_bukuro/items/ca8af6722ba647ab81f8)

part1から読む人は[こちら](https://qiita.com/manzoku_bukuro/items/10137f64810f0dc40543)

# この記事の対象者
- Video.jsを利用する人
- 動画プレイヤーのUI操作をしたい人
- 動画再生関連のサービスを作りたい人
- Video.jsの初期化により何が起きているか知りたい人

# この記事の解説する章
この記事は公式のガイドには無い初期化により何が起きているか調査した番外編のような内容です

# 前提
## Video.jsバージョン
2022年12月最新の**7.20.3**を利用しています

# 初期化で何が起きているのか？
きっかけは私がVideo.jsの用意するスタイルシートを読み込まずにVideo.jsの初期化を行った時でした。

以下のようにvideo-jsタグを利用して、video.jsのライブラリをスクリプトのみ読み込んでdata-setup属性を利用して初期化してみます。
```html
<head>
    ...
    <!-- スタイルシートは読み込まない -->
    <!-- <link href="https://vjs.zencdn.net/7.20.3/video-js.css" rel="stylesheet" /> -->
    <script src="https://vjs.zencdn.net/7.20.3/video.min.js"></script>
</head>

<body>
    <video-js id="video1" controls data-setup='{}'>
        <source src="https://vjs.zencdn.net/v/oceans.mp4">
    </video>
</body>
```

すると以下のように表示されました
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2849606/9552155c-60f4-5831-b2db-615312ff7db0.png" width="500px" />
謎のボタンや文字が表示されましたね…
下にも続いているので全体を見てみましょう。

## 追加された要素一覧全体
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2849606/b603eded-ad4b-2ee4-243e-da8e23383056.png" width="500px" />

以上が追加された要素の全体になります。

## 追加された要素の機能確認
例えばPlayというボタンをクリックしてみます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2849606/e81d54a7-74c7-c2b8-b12f-26258e79e89a.png" width="500">

予想通り、動画が再生されて`Current Time`の項目が動き出しました。
つまりPlayというボタンが再生ボタンとしての機能を持つことが分かります。

## スタイルシート読み込み有り無しの比較
スタイルシートを読み込んでいる時の再生ボタンと、読み込んでいない時の再生ボタンの要素のclassを比較してみます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2849606/87ae907e-ee44-d38b-ab85-8fed495cdc6e.png" width="100%">

みて分かるように同一のクラスを持つことが分かります。

## (結論)初期化で何が起きているのか？
以上のことから、私はVideo.jsの初期化することで何が起きているかを以下のように考えました。

**Video.jsのプラグインを読み込み、必要な要素を設置して初期化を行う**
↓
**用意した要素が動画と動画操作機能を持つ要素と置き換わる**
↓
**Video.jsの用意したスタイルシートを読み込むことで、動画操作機能を持つ要素にCSSが適用されてどの環境でも同じ見た目になる**

Video.jsを初期化することで何が起きているか。まずはこれくらいの理解でもいいと思いました。

---
[Twitter](https://twitter.com/manzoku_bukuro)もやってます
【Alpine.js】x-textとx-htmlの違い

::: note
<b>初心者向けの内容ですが、なんやかんや大事なところです。</b>
この2つの違いはJavaScriptの<b><font color="red">innerText</font></b>と<b><font color="red">innerHTML</font></b>との違いと同じ理屈なので、それが分からない方は是非読んでください。
:::
# 結論
### HTML要素を出力する時は必ずx-htmlを使う。
例えばこんなコードがあったとします。
```
<div x-data="{
    title: "<h1>こんにちは</h1>" 
}" >
    <div x-text="title"></div>
</div>
```

### x-textの場合
上のコードではtitleプロパティの出力にx-textを使っているので、実際に出力されるのは
```
<h1>こんにちは</h1>
```
こうなります。h1タグまで「こんにちは:frowning2:」されてしまいます。
不要なタグまで出力してしまって不満げな表情になっちゃうワケね。

### x-htmlの場合
逆にx-htmlを使うと
```
<div x-data="{
    title: "<h1>こんにちは</h1>" 
}" >
    <div x-html="title"></div>
</div>
```
素直にh1タグが出力されることなく「こんにちは:smile:」します。
ニッコリスマイリーですね～
```
こんにちは
```

::: note
冒頭で述べたように、これはJavaScriptのinnerTextとinnerHTMLとの違いと同じです。
必ず使い分けるようにしましょう。
:::

# まとめ
x-textとx-htmlを甘く見てはいけない（念押し）