OmniAICreator/Qiita-1.07M huggingface dataset:
Next15 + prisma + supabase でSNSサービスを作って公開した

# はじめに
今回は初めてNext.jsを使ったWebサービスを公開しました。Next.jsのモダンな機能をたくさん使った実装になっていますので、それらの実装や開発した経験について紹介します。

# 自己紹介
2007年生まれの17歳です。高校一年生の途中で高校を中退してから１年半ほどアルバイトでシステム開発をしながら個人開発をしています。

# サービスの内容

<iframe width="560" height="315" src="https://www.youtube.com/embed/fStXLeCgpQ0?si=ODWlbgDsB_T5xRsn" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>

https://www.okmr07.com/

新規登録していただけると大変うれしいですが、以下のログイン情報でログインしていただけます。
email: `demo@example.com`
password: `password`

👆操作デモ動画
Xやinstagramの機能、デザインを参考に投稿やいいね、コメントなど基本的な機能のみを実装しました。

# なぜSNSを題材にしたのか
- いいねやフォローなど様々な細かいDB操作を含む機能が多く、クライアントからサーバーまでフルスタックに実装する経験を得られる
- 日常的によく利用するXやInstagramなどから設計のヒントやアイデアの着想を得やすい
- 実際に自分で使ったり友人に使ってもらいやすく、それらによって様々な改善すべき点を得られる

# 使用技術とそれらを採用した理由
**Next.js 15**
**React 19**
**Prisma (ORM)**
**TailwindCSS**
UIコンポーネントライブラリの使用を検討したが、自作でスタイルや動作を実装する経験を積みたいと思い採用した
**Supabase (DB, Auth, Storage)**
Firebaseを検討していたが、Supabaseのわかりやすいドキュメント、シンプルな認証システムを魅力に感じ採用した
**Vercel (ホスティング)**
**tailwind-variants**
**sharp**
画像処理
**zod**
フォームバリデーション
**qrcode** 
「プロフィールをシェア」機能でのQRコード生成
**linkify** 
投稿のテキストに含まれるURLをリンクに変換
**eslint**
**prettier**

https://github.com/yamaneko05/imageable

👆GitHub

# Next15 AppRouter での設計
- データの取得（投稿データの取得やプロフィール情報の取得）
    - すべてServer Componentで行いClient ComponentにPropsとして渡す
- データの操作（投稿やいいね、フォローなどの操作）
    - いいねやフォローなどUIの変化を含むインタラクティブなものはCliet Cmoponetからサーバーアクションを呼び出す形で実装し、なるべくReact19のuseActionStateを利用した
- モーダルをサーバーコンポーネントで実装する
    - Next.jsのParallel Routes, Intercepting Routesを利用し、モーダルを開くとURLが変化し、Server Componentでデータの取得をした

# 開発を通して感じたこと
Server ComponentやServer Actionsの登場により、LaravelやRailsなどでMPAを作る場合と似たような開発の流れになってきていると思いました。

また、PrismaやsupabaseなどNodejsのライブラリなどを探したりしてみて、PHPなどとくらべて非常にコミュニティが活発でNode.js界隈の盛り上がりを感じました。

Next.jsではDB操作からclient componentまで全てに型情報がつき、
prisma.schemaでのテーブル定義の型情報を末端のclient componentまで渡せるのは非常に開発体験がよく、開発スピードを速める要因になると感じました。

# おわりに
SNSサービスという特性上、通知や画像の参照などOS依存の操作がおおくなるため、やはりスマホアプリとしての開発もしたいと思いました。
お尻の[0]のお話

## お尻の[0]・・・？
Customienをいぢっていると、無意識のうちに`[0]`に遭遇する機会が結構多いのではないでしょうか？
例えばアクション番号111でレコードを取得した後、別のアクションで使おうとすると、$111[0]みたいな感じで。

他の例では、（あんまりきれいではないけど）if文を使ってチェックボックスの値を比較しようとしたときに、`if(チェックボックス[0] = "ひよこ", ...)`のような書き方をすればチェック項目による条件分岐ができるんだなぁ、、、という。

今回は、 *「その辺よくわかってないけど、なんか動いてるからそういうもんだと思ってます！」* という方に向けて、仕組みめいたものをお伝えしてみようかな、と思います。

これらのお尻の`[0]`、詳しく話しすぎるとCustomineの良さが損なわれる気がしなくもないんですが、仕組みを知っておくと色々と捗るんじゃないかなぁと思います。

## イメージは列車
*（はいはい、配列の話ですよネ～）* 、とこの手の話題に慣れている方ならもうこの時点で回れ右していただいて大丈夫ですが、kintoneをいきなりやれと言われて、ExcelのノリでCustomineに挑もうとする方々にとって、この配列という概念はあまりなじみがない、かもしれません。
*「いやいや、最近のExcelはスピルがあるから！」* という方もこの先は読まなくて大丈夫そうですね😂

まず1つ目の例で挙げた「レコードを取得する」タイプのアクションでは、あるアプリのレコードを全て、もしくは条件を絞って取得しているかと思います。
ということは・・・取得されているレコードは、必ずしも **1つだけではない** んですね。

で、たくさん持ってきたこのレコードちゃんたち、実は列車のように並べられている、というのをイメージしていただきたいです。まぁ配列というかリストというか・・・そういうアレなんですけど。
今回、配列というかリストというか、をもうとりあえず **列車** と呼ばせてもらいます。

この列車、車両番号が1からではなく **0から割り振られている** と思ってください。なんでやねん、と思うかもしれませんが、その方が都合がいいことがあります。
どうしても気になる方は **「配列 0から」** とかで調べてみてください。

冒頭の例で言うと、アクション番号111で取得したレコードの列車から、先頭車両のレコードだけ用事がある場合が多いので、`[0]`という指定を付けているわけです。

とはいえ、レコードを取得するタイプのカスタマイズ、初めのうちは取得するレコードを1つに絞ってあーだこーだすることが多い（ような気がする）ので、この`[0]`はなんか勝手についてきてるヘンなヤツにしか見えない、というわけです。

## あれもこれも実は列車の仲間
レコード同様、1つのフィールドの中に複数データを格納できるタイプのものはこの列車タイプです。

例えば冒頭2つ目の例で出した **チェックボックス** もそうですし、 **ユーザー選択** や **添付ファイル** も。

レコードとフィールドでは規模感が全然違うので、ちょーっとイメージしづらいかもしれませんが、どっちも抽象化すると複数の似たような奴が連なっていて、遠くから眺めると同じ列車タイプなんだなぁ、とぼんやり思ってください。

ちなみに先頭車両が`[0]`なので、その次は`[1]`で指定できる、という具合になってます。
1つ目が`[0]`で2つ目が`[1]`ってなんやねん！と叫びたくなるかもしれませんし、叫んでいただいて差し支えありません。（ご近所に迷惑でなければ。）

2つ目の例で挙げたチェックボックスの値を元にした条件分岐、無理やりif文を使おうとすると、`チェックボックス[0]`という書き方を要求されがちですが、これはチェックボックスの裏方の仕組みが大事です。

チェックボックスは、チェックされた項目を車両としてつけたり外したりされている、と思ってください。
例えば、チェック項目が **`たまご` `ひよこ` `こけこっこ`** の3つだったとしましょう。どんなチェックボックスやねん。
このうち、 **`こけこっこ`** 以外の2つを選択していた場合、裏方では0両目に **`たまご`** 、1両目に **`ひよこ`** が連結した2両編成となっています。

0両目が **`たまご`** かどうかを判定するのが、2つ目の例の `if(チェックボックス[0] = "たまご", ...)` という書き方になるわけです。
確かにこの書き方であれば、チェックボックスで **`たまご`** が選択されているかどうかは判定できそうです。

しかし、 **`ひよこ`** が選択されているかどうかを判定する式はどのように書けばいいのでしょうか？

## 列車の編成は良くも悪くも柔軟
先ほど同様、 **`たまご` `ひよこ` `こけこっこ`** のうち **`たまご` `ひよこ`** が選択されている場合、チェックボックスに **`ひよこ`** が含まれるかどうかを判定しようとすると、 `if(チェックボックス[1] = "ひよこ", ...)` と書けば良さそうです。

ところが、列車の編成が **`ひよこ` `こけこっこ`** だった場合はどうでしょうか・・・？
今回は **`ひよこ`** は1両目ではなく0両目に鎮座しています。
先ほどの `if(チェックボックス[1] = "ひよこ", ...)` と言う書き方は、チェックボックスの1両目が **`ひよこ`** かどうかを判定する式なのですが、今回のケースだと0両目と比較したい、ですね。

更に別の例で、列車の編成が **`ひよこ`** だけだった場合、裏方の列車は0両目しか存在しないことになるので、 `チェックボックス[1]` で指定しようとした1両目がなくてエラーになってしまうのです。

つまり何が言いたいかと言うと・・・列車タイプの判定の場合、if文は万能じゃないですよ～、というお話でございます。
ちょっとめんどくさくても、フィールドの値に関する条件で、 **含まれる** 条件を使うのが安心安全ということです。

Excelに慣れている方にとって、if文は様々な場面で救世主のような存在だったかと思いますので、ついついあちこちで頼りそうになるわけですが・・・こっちの世界ではそれだけでは太刀打ちできないヤツらがいる、ということですね。

## 補足というかなんというか

*「い～やfindif関数があるやろがい」* とツッコみをくださる方、よくここまで読んでくださいましたね。。。ありがとうございます。。。

今回の列車の例え話、じゃあそれを具体的にどう使うの？というお話は敢えてやりません。
多くの場合が関数を使ったテクニカルなお話になり、果たしてノーコード・ローコードを謳うkintoneやCustomineをこれから使ってみよう、という方に伝えるべきことか？と考えると、そこまではいいかな・・・と思ったからです。

逆に、いやいやそれで何か面白いことができるの！？とご興味が湧いた方は、 **お試しアプリを用意して** `[0]`を`[1]`にしたらどうだとか`[0]`を抜いたら？という実験をしてみてください。
実際に動いているアプリで試して何かが起こっても責任はとれませんので、そこだけはご注意を。。。

それでは皆様、楽しいkintoneライフをお過ごしくださいませ！
OpenVPN サーバーのセットアップ手順

1. Easy-RSA のインストールとセットアップ

**Easy-RSA**をGitHubからクローン
```bash
git clone https://github.com/OpenVPN/easy-rsa.git ~/easy-rsa
```

Easy-RSA のディレクトリに移動
```bash
cd ~/easy-rsa/easy-rsa3/
```
`vars.example` を `vars` にコピーし、編集
```bash
cp vars.example vars
vi vars
```
設定をロード
```bash
source vars
```
PKI の初期化
```bash
./easyrsa init-pki
```
CA の作成
```bash
./easyrsa build-ca
```

２. サーバー証明書と鍵の作成
サーバー証明書と秘密鍵の作成
```bash
./easyrsa build-server-full server nopass
```

３. クライアント証明書と鍵の作成
クライアント証明書と秘密鍵の作成
```bash
./easyrsa build-client-full client nopass
```

４. DHパラメータの生成
DHパラメータの生成
```bash
./easyrsa gen-dh
```

５. 証明書ファイルの配置
必要な証明書ファイルを `/etc/openvpn/` にコピー
```bash
sudo cp ~/easy-rsa/easyrsa3/pki/ca.crt /etc/openvpn/
sudo cp ~/easy-rsa/easyrsa3/pki/issued/server.crt /etc/openvpn/
sudo cp ~/easy-rsa/easyrsa3/pki/private/server.key /etc/openvpn/
sudo cp ~/easy-rsa/easyrsa3/pki/dh.pem /etc/openvpn/dh2048.pem
```

６. OpenVPN 設定ファイルの編集
`/etc/openvpn/server.conf` の内容を確認・編集

```bash
vi /etc/openvpn/server.conf
```
設定内容を以下のように確認・編集
```bash
port 1194
proto udp
dev tun
ca /etc/openvpn/ca.crt
cert /etc/openvpn/server.crt
key /etc/openvpn/server.key
dh /etc/openvpn/dh2048.pem
server 10.8.0.0 255.255.255.0
ifconfig-pool-persist ipp.txt
push "redirect-gateway def1 bypass-dhcp"
push "dhcp-option DNS 8.8.8.8"
push "dhcp-option DNS 8.8.4.4"
keepalive 10 120
cipher AES-256-CBC
user nobody
group nogroup
persist-key
persist-tun
status openvpn-status.log
verb 3
```

７. ファイルとディレクトリの権限設定
証明書ファイルの権限を適切に設定
```bash
sudo chmod 600 /etc/openvpn/server.key
sudo chmod 644 /etc/openvpn/server.crt
sudo chmod 644 /etc/openvpn/ca.crt
sudo chmod 644 /etc/openvpn/dh2048.pem
```

８. OpenVPN サービスの起動
`openvpn@server.service` のサービスを起動
```bash
sudo systemctl daemon-reload
sudo systemctl start openvpn@server
```

サービスの状態確認
```bash
sudo systemctl status openvpn@server.service
```

```bash
sudo journalctl -xeu openvpn@server.service
```

９. nogroup グループの確認と作成
`nogroup` グループが存在しない場合、作成

```bash
getent group nogroup
sudo groupadd nogroup
```

１０. `systemd` サービスの管理
サービスの有効化
```bash
sudo systemctl enable openvpn@server
```
サービスの再起動
```bash
sudo systemctl restart openvpn@server
```
【TryHackMe】Security Engineer Intro：Walkthrough

# 概要

TryHackMe「Security Engineer Intro」のWalkthroughです。

https://tryhackme.com/r/room/securityengineerintro

# Task2

## **Q1**.*Who ensures that an organization's cyber security risk is minimized at all times?*

**A**.**Security engineer**

# Task3

## **Q1**.*Where are details about an organization's digital assets, such as name, IP address, and owner, stored?*

**A**.**Asset inventory**

## **Q2**.*Sometimes security policies can't be followed because of business needs. What avenue does a security engineer have to fulfil business needs in these cases?*

**A**.**Exceptions**

## **Q3**.*What philosophy, if followed, provides the most Return on Investment (ROI)?*

**A**.**Secure by design**

# Task4

## **Q1**.*What is considered the weakest link in an organization's security?*

**A**.**humans**

## **Q2**.*An organization's security evolves with the organization. What helps a security engineer keep the organization secure through these changes?*

**A**.**Change management**

# Task5

## **Q1**.*What is a theoretical exercise carried out to gauge the operational readiness of an organization from a security point of view?*

**A**.**Tabletop exercise**

## **Q2**.*What is the priority of the management in case of a disaster or crisis?*

**A**.**Business Continuity**

# Task6

## **Q1**.*What is the flag shown on the completion of the static site?*

4択の問題を解いていくとフラグをゲットできます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3005822/42f3e71b-b38c-94e7-747c-8ddec59ea1d8.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3005822/921eaf92-f562-15ea-0310-6343f15aab7b.png)

**A**.**THM{S3CUR1TY_3NG1N33R5_R0CK}**
Vue.jsのナビゲーター - Vue Router を使いこなそう

# Vue.jsのナビゲーター - Vue Router を使いこなそう
こんにちは、Vue.jsファンの皆さん！株式会社アシストエンジニアリング、エンジニアのsawasatoです！前回はPiniaについてお話ししましたが、今日は Vue.js アプリに必要不可欠な道案内人、Vue Routerについてお話ししましょう。Vue Routerは、アプリにページ遷移の魔法をかけてくれるもの。これがないと、Vue アプリが一枚の永遠のキャンバスみたいになってしまいますよね。ページ遷移を魔法のように実装してくれる Vue Routerの秘密を、ちょっとユーモアを交えながら探っていきます。
## Vue Routerって何者？
簡単に言うと、Vue RouterはVue.jsアプリケーションにルーティング機能を提供する公式ライブラリです。これにより、URLに基づいて異なるコンポーネントを表示したり、クライアントサイドのシングルページアプリケーション（SPA）を構築できるようになります。つまり、あなたのアプリをシームレスに複数ページに見せてくれるナビゲーションガイドです。
## Vue Routerのインストール
Vue Routerを使うためには、まずインストールが必要です。まだインストールしていない？大丈夫、たった一行です：
bash
```
npm install vue-router
```
そして、main.jsファイルにVue Routerをインポートして、アプリにセットアップします。
javascript
```
import { createApp } from 'vue';
import { createRouter, createWebHistory } from 'vue-router';
import App from './App.vue';
import Home from './components/Home.vue';
import About from './components/About.vue';
const routes = [
{ path: '/', component: Home },
{ path: '/about', component: About },
];
const router = createRouter({
history: createWebHistory(),
routes,
});
const app = createApp(App);
app.use(router);
app.mount('#app');

```

このシンプルなコードで、アプリの道筋が決まりました。まるで地図が完成したようなものですね！
##コンポーネント間の移動
Vue Routerの基本的な使い方は簡単です。ルートリンクを作るには、<router-link>を使います。
php
```
<template>
<nav>
<router-link to="/">Home</router-link>
<router-link to="/about">About</router-link>
</nav>
<router-view></router-view>
</template>

```

<router-link>は、ナビゲーションのための Vueコンポーネントです。「普通のaタグじゃダメなの？」と疑問に思うかもしれませんが、Vue Routerは<router-link>を通じてページのリロードなしに URLを変更し、SPAの滑らかな動作を維持してくれるのです。まるでマジックですよね！
<router-view>は、指定されたルートに基づいて適切なコンポーネントを表示します。これが Vue Router の舞台装置のような役割を果たし、あなたのアプリがページのように見える魔法をかけてくれます。
## 動的ルーティング
「プロフィールページはどうすればいいの？」って思っているそこのあなた、Vue Router の動的ルートパラメータを試してみましょう。例えば、/user/:idのようなルートを定義すると、特定のユーザーに基づいてページを表示できます。
arduino
```
const routes = [
{ path: '/user/:id', component: User },
];

```
Userコンポーネント内で、このidにアクセスするのも簡単です。
php
```
<script setup>
import { useRoute } from 'vue-router';
const route = useRoute();
const userId = route.params.id;
console.log('ユーザーID:', userId);
</script>

```
動的な URLパラメータを使って、ユーザー情報や詳細ページを簡単に構築できます。
## ガード機能でナビをカスタマイズ
「ページにアクセス制限をかけたい」という場合も、Vue Router はばっちりサポートしています。beforeEnter ガードやグローバルナビゲーションガードで、ルートへのアクセスを制御することができます。
vbnet
```
const routes = [
{
path: '/protected',
component: Protected,
beforeEnter: (to, from, next) => {
if (isAuthenticated()) {
next();
} else {
next('/');
}
},
},
];

```
このbeforeEnter関数はルートに入る前に呼び出され、条件に基づいて次のページに進むかどうかを決めます。これで、ページガードマンがいつでもスタンバイ状態です笑
Vue Routerは、Vue.js を使った開発で不可欠なパートナーです。簡単なセットアップから、動的ルーティング、ナビゲーションガードまで、アプリをより使いやすく、柔軟にしてくれます。Vue.jsの旅をするなら、この道案内人と一緒に素晴らしい開発の旅を始めてみませんか？
ぜひVue Routerと一緒に、素敵なアプリを作ってみてくださいね！

## 採用拡大中！
アシストエンジニアリングでは一緒に働くフロントエンド、バックエンドのエンジニア仲間を大募集しています！

少しでも興味ある方は、カジュアル面談からでもぜひお気軽にお話ししましょう！

お問い合わせはこちらから↓
[https://official.assisteng.co.jp/contact/](https://official.assisteng.co.jp/contact/)
Trino (Presto) running UU

```sql
WITH
data_source AS (
    SELECT user_id, g -- group
    FROM source_table
)

SELECT g
     , approx_distinct(user_id) as uu
     , cardinality(
         merge(approx_set(user_id)) OVER (order by g)
       ) as uu_accumurated
     , cardinality(
         merge(approx_set(user_id)) OVER (order by g)
       ) * 1.0000
       / (cardinality(merge(approx_set(user_id)) OVER ()))
        as uu_acccumurated_rate
```

Reference
=====
* https://trino.io/docs/current/functions/hyperloglog.html
> Trino implements the approx_distinct() function using the HyperLogLog data structure.
> 
> merge(HyperLogLog) → HyperLogLog#
> > Returns the HyperLogLog of the aggregate union of the individual hll HyperLogLog structures.
Vue.js の新たな友 Pinia とストア管理のお話

# Vue.js の新たな友 Pinia とストア管理のお話
こんにちは、Vue.jsファンの皆さん！株式会社アシストエンジニアリング、エンジニアのsawasatoです！最近現場でVue.jsを使っていて新しく学ぶことも多いため、ここで共有していこうと思います！まず一発目の今日は、Vueの世界で地味に、でも確実に存在感を増しているPiniaについて語りましょう。もしVuexと何年も付き合ってきたとしたら、「また新しいストア管理ツール？」とちょっと眉をひそめているかもしれませんね。でも大丈夫、Piniaはそんなに新しい友達じゃないし、心を開いて迎え入れてみる価値がありますよ！
## Piniaってそもそも何？
Pinia（ピニア）は Vue.jsのための公式なストア管理ライブラリです。Vuexの後継的な存在で、Vue 3に最適化されています。Piniaは「簡単に」「わかりやすく」「効率的に」ストアを管理するために作られました。なんと、Vuexの煩雑な書き方を捨てて、もっとすっきりとした書き方に進化しているんです。Vuexを使って「もう少しこうだったら…」と感じたこと、Piniaが解決してくれるかもしれません。
## Pinia の魅力
1.シンプルで直感的なAPI
PiniaのAPIはVuexよりもシンプルで、初めて使う人もわかりやすいように設計されています。これにより、「Vuexの書き方が複雑すぎて頭がこんがらがる」という心配がなくなります。Vueの新しいファンも、これなら安心してストア管理を始められるでしょう。

2.型の安全性
PiniaはTypeScriptとの相性も抜群です。型の安全性を重視する開発者にとって、これはかなりのアドバンテージです。「これ、本当にこの型だっけ？」と心配する必要が減り、コーディングがスムーズになります。PiniaとTypeScriptのペアは、パンケーキとメープルシロップのようなものです。素晴らしい組み合わせです！

3.モジュール化が不要
Vuex ではモジュール化が面倒で、途中で「これは本当に必要な工程？」と疑問を持ったことがあるかもしれません。Piniaは、各ストアが独立して定義できるので、モジュールの設定に頭を悩ませる必要がなくなります。作りたいストアがあったら、ただそれを作ればいいだけ。シンプルですね！

## 実際にPiniaを使ってみよう
言葉だけだとアレなので、簡単な使い方を見てみましょう。まず、Piniaをインストールします。
bash
```
npm install pinia
```

そして、メインのmain.jsでPiniaをアプリに登録します。
javascript
```
import { createApp } from ‘vue’;
import { createPinia } from ‘pinia’;
import App from ‘./App.vue’;

const app = createApp(App);
app.use(createPinia());
app.mount(‘#app’);
```

次に、ストアを作ってみましょう。
javascript
```
// stores/counter.js
import { defineStore } from 'pinia';
export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0,
}),
actions: {
increment() {
this.count++;
},
},
});
```


このストアをコンポーネントで使うとこんな感じになります。
javascript
```
<template>
<div>
<p>Count: {{ counter.count }}</p>
<button @click="counter.increment">Increment</button>
</div>
</template>
<script setup>
import { useCounterStore } from '../stores/counter';
const counter = useCounterStore();
</script>

```

ね、簡単でしょう？VuexのmapStateやmapActionsのあの長々しいインポートとはおさらばです。Pinia のシンプルさを一度知ってしまうと、もう後戻りはできないかも。
## Vuexファンも大丈夫！
Vuexの時代を愛していた皆さん、安心してください。PiniaはVuexとは違いますが、根本的な考え方は同じです。Piniaは「ストア管理をもっと楽にしよう」という優しい思いから生まれました。まるでカフェで飲むラテが、ある日エスプレッソマシンで作られるようになって驚いたときのようなものです。違うけど、美味しさはそのままです笑
冗談はさておき、、
皆さんもPiniaを試して、新しいVue.jsライフをもっと充実させてみませんか？きっと開発が楽しくなるはずです！

## 採用拡大中！
アシストエンジニアリングでは一緒に働くフロントエンド、バックエンドのエンジニア仲間を大募集しています！

少しでも興味ある方は、カジュアル面談からでもぜひお気軽にお話ししましょう！

お問い合わせはこちらから↓
[https://official.assisteng.co.jp/contact/](https://official.assisteng.co.jp/contact/)
無料版CML2.8をHyper-V上で動かす

Cisco Modeling Labs(CML)2.8の無料版がリリースされたためHyper-V上で動作させます。CMLが公式にサポートしているのはVMwareのみですが、Hyper-Vを使用してる環境(WSL2含む)の場合、VMware Workstation上でCMLを実行できません。そのためHyper-Vで実行させたいと思います。なお予期しない不具合が発生する可能性があることにご留意ください。

# 検証環境
- CML2.8.0 Free Tier
- Windows11 Pro 24H2 (Hyper-Vの動作のためPro以上が必要です。)
- PCスペック
    - CPU：AMD Ryzen7 9700X
    - メモリ：64GB (CMLは最低8BG要求するため16GB以上推奨です。)

# CMLのダウンロード
[Ciscoのサイト](https://software.cisco.com/download/home/286193282/type/286326381/release/2.8.0%20Free%20Tier)よりCMLをダウンロードします。なおダウンロードにはCiscoアカウントが必要です。ベアメタル用の下記イメージファイルをダウンロードし展開しておきます。

- cml2_f_2.8.0-6_amd64-32-iso.zip
- refplat-20241016-freetier-iso.zip

# 仮想マシンの作成

Hyper-Vマネージャーのメニューバー＞操作＞新規＞仮想マシンから新規作成します。[公式ドキュメントの要求スペック](https://developer.cisco.com/docs/modeling-labs/system-requirements/)に従い下記を設定しました。

- 名前：CML2 (任意の名称を設定)
- 世代の指定：第2世代
- メモリ：8GB以上
- ネットワーク：好みですがセットアップ時はインターネット接続できるようにDefault-swtichを指定しました
- ハードディスクサイズ：32GB以上が必要です
- ブート用CD：ダウンロードしたcml2_2.8.0-6_amd64-32.iso

作成完了後、仮想マシンの設定から追加の設定を行います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2524454/4581b248-97ab-ea07-a2de-c1cb698157ae.png)
セキュリティからセキュリティブートを有効にした上でテンプレートからMicrosoft UEFI証明書機関を設定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2524454/9f1b972e-8acc-a789-6072-5f247ae390cc.png)
SCSIコンローラータブからDVDドライブを追加後、refplat-20241016-freetier.isoをマウントします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2524454/85a64003-5d6c-67c6-3346-d12618ab058e.png)
ネットワークアアダプター＞高度な機能＞MACアドレスからMACアドレスのスプーフィングを有効にします。
その他、プロセッサ等も適当に割り当ててください。(4コア以上必要です。）

次に仮想マシンでの仮想化機能を有効にします。PoweShellで下記コマンドを実行してください。
```Powershell
Set-VMProcessor -VMName [VM Name] -ExposeVirtualizationExtensions $true
```

# CMLのインストール
仮想マシンを起動するとインストールが開始されます。完了後再起動すると下記のような画面が表示されるため、リカバリーモードで起動します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2524454/55616548-29d3-6ae3-0324-f8033cffe606.png)
上から2番目を選択する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2524454/fc1d8c63-d4b3-88ed-dac7-c22d6ad3d38c.png)
上から2番目のリカバリーモードを選択後、画面の指示に従いCtr+Dを押すと初期設定が開始されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2524454/4e8bc80b-d2e4-5c2a-6c9f-66d237741008.png)
初期設定の完了後、画面に表示されるIPアドレスへブラウザからアクセスします。

# CMLへのアクセス

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2524454/ded3c994-71db-0b8b-c701-1c52807337dc.png)

初期設定のままの場合、adminユーザーでログインできます。なおsysadminユーザーはweb consoleの9090ポートへの接続時に使用します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2524454/64189506-bced-b444-1cb6-b1f4f9ef261f.png)
ログインできました！

# 参考：ネットワーク設定について
仮想マシンに対して複数のアダプタを割り当てた場合、外部接続用のブリッジ/インターフェースが一番目のeth0に作成されるようです。なお、初期設定後のOSのネットワーク設定は管理用のwebコンソールから行えます。
JavaScriptは通過点

# Reactを学習するためのJavaScript
今回は、Reactを習得することがゴールであるから、JavaScriptはただの通過点。であるから、Reactで活きるJavaScript最低限の知識をまとめていく。
## JavaScriptの基礎
- 変数の宣言
Reactでは、constとletが基本になってくるためこの二つは網羅しておく。
```js:sample.js
const name = "React";
let count = 0;
```
const：再宣言、再代入、繰り返し文　使用不可
let 　：再宣言、再代入、繰り返し文　　使用可
- 関数の作成 
アロー関数を用いたシンプルな関数の作成方法の理解。
```js:sample.js
const greet = (name) => `Hello, ${name}!`;
console.log(greet("React"));
```
この関数を用いることで通常の関数の表記と比べて改行しなくてよい、などの利点が多くあるため、積極的にこの関数を使っていくとよい。
その名の通り、 =>　この記号がアローに見えることからアロー関数と呼ばれている。
- 条件分岐
JSX（HTMLのようなコードを書くための拡張機能）内でもよく使うため、ifや三項演算子の基本を理解しておくとよい。
```js:sample.js
const isLoggedIn = true;
const message = isLoggedIn ? "Welcome!" : "Please log in.";
console.log(message);
```
- 配列のメソッド
Reactでは配列操作が頻繁に起きるため知っておくべき。
```js:sample.js
const numbers = [1, 2, 3];
const doubled = numbers.map(num => num * 2); // [2, 4, 6]
```
- オブジェクトとスプレッド構文
状態管理（コンポーネントやアプリ全体の「データの状態」を管理し、それを使ってUIを動的に変化させる仕組み）によく使うため知っておくべき。
```js:sample.js
const user = { name: "React", age: 5 };
const updatedUser = { ...user, age: 6 }; // { name: "React", age: 6 }
``` 
- 非同期処理
外部データを取得するのに必要となってくる。具体的には、Webアプリケーションで外部のサーバーからデータを取得したり、データを送信したりする際に使う。
役割としては、時間のかかる操作をしている間にほかの作業を進められるというもの。実現方法としては主に三つある。
1. コールバック関数
1. Promise

1. async/await
- ES6の構文
分割代入　
配列やオブジェクトの要素を簡単に取り出して、変数に割り当てる構文。コードを従来よりシンプルにできる。
```js:sample.js
const user = { name: "React", age: 5 };
const { name, age } = user;
console.log(name, age); // React 5
``` 
テンプレートリテラル
テンプレートリテラルはバッククォート（`）で囲んで記述する。
中に埋め込みたい変数や式を、${}の中に書くことで結果を文字列に挿入できる。
```js:sample.js
const name = "React";
console.log(`Hello, ${name}!`);
```
## まとめ
1. 変数の宣言（constとlet）
1. 配列やオブジェクトの基本操作
1. 非同期処理
1. ES6構文（分割代入、テンプレートリテラル）

これらの知識を踏まえたうえで、Reactを学びながら不足分を補っていくと効率よく学べると思う。
AlmaLinuxのセキュリティ修正を適用する

# 0.はじめに
RHELの場合はセキュリティ修正のアップデートが可能ですが、AlmaLinuxの場合も同様にセキュリティ修正のアップデートが可能です。AlmaLinuxのセキュリティ修正は、RHEL側で更新されたのちに、AlmaLinuxでそれをもとにリビルド＆リリースされるようです。なお、このタイミングは多少の遅延がありますが、RHEL側で発表された後、数日後には提供されるようです。

# 1.手順
## セキュリティアップデートがあるか確認する。
```bash
dnf updateinfo list security
```

### 実行例
```bash
[yoyo@AL95B1 ~]$ dnf updateinfo list security
Last metadata expiration check: 0:16:25 ago on Wed Nov 27 00:30:53 2024.
ALSA-2024:9541 Unknown/Sec. expat-2.5.0-3.el9_5.1.x86_64
ALSA-2024:9605 Unknown/Sec. kernel-5.14.0-503.14.1.el9_5.x86_64
ALSA-2024:9605 Unknown/Sec. kernel-core-5.14.0-503.14.1.el9_5.x86_64
ALSA-2024:9605 Unknown/Sec. kernel-modules-5.14.0-503.14.1.el9_5.x86_64
ALSA-2024:9605 Unknown/Sec. kernel-modules-core-5.14.0-503.14.1.el9_5.x86_64
ALSA-2024:9605 Unknown/Sec. kernel-tools-5.14.0-503.14.1.el9_5.x86_64
ALSA-2024:9605 Unknown/Sec. kernel-tools-libs-5.14.0-503.14.1.el9_5.x86_64
```

## セキュリティ修正をインストール
```bash
sudo dnf update --security
```

### 実行例
```bash
[yoyo@AL95B1 ~]$ sudo dnf update --security
[sudo] password for yoyo:
Last metadata expiration check: 0:33:04 ago on Wed Nov 27 00:20:34 2024.
Dependencies resolved.
====================================================================================================================
 Package                          Architecture        Version                             Repository           Size
====================================================================================================================
Installing:
 kernel                           x86_64              5.14.0-503.14.1.el9_5               baseos              2.0 M
Upgrading:
 expat                            x86_64              2.5.0-3.el9_5.1                     baseos              115 k
 kernel-tools                     x86_64              5.14.0-503.14.1.el9_5               baseos              2.3 M
 kernel-tools-libs                x86_64              5.14.0-503.14.1.el9_5               baseos              2.0 M
Installing dependencies:
 kernel-core                      x86_64              5.14.0-503.14.1.el9_5               baseos               18 M
 kernel-modules                   x86_64              5.14.0-503.14.1.el9_5               baseos               36 M
 kernel-modules-core              x86_64              5.14.0-503.14.1.el9_5               baseos               30 M

Transaction Summary
====================================================================================================================
Install  4 Packages
Upgrade  3 Packages

Total download size: 90 M
Is this ok [y/N]:y

～～～中略　～～～
Upgraded:
  expat-2.5.0-3.el9_5.1.x86_64                                kernel-tools-5.14.0-503.14.1.el9_5.x86_64
  kernel-tools-libs-5.14.0-503.14.1.el9_5.x86_64
Installed:
  kernel-5.14.0-503.14.1.el9_5.x86_64                    kernel-core-5.14.0-503.14.1.el9_5.x86_64
  kernel-modules-5.14.0-503.14.1.el9_5.x86_64            kernel-modules-core-5.14.0-503.14.1.el9_5.x86_64

Complete!
```

以上です
vite-plugin-vitest-cache (vCache) 導入後 CI/CD 上 (GitHub Actions) で cache を利用できるよう定義する


## はじめに

前回の記事で vite-plugin-vitest-cache (vCache) を導入し実際にテストの高速化を行いました。
手元の環境ではすぐ効果が出ましたが、vite-plugin-vitest-cache (vCache) を導入しただけでは C
/CD (GitHub Actions) 上で cache させるにはもう1段階設定が必要です。

https://qiita.com/mziyut/private/cf69b9c715cd84bdf605

:::note warn
vite-plugin-vitest-cache (vCache) 及び GitHub Actions の workflow 設定等は終わっている前提で記載します。
:::

## GitHub Actions workflow の編集

### actions/cache@v4 について

https://qiita.com/mziyut/private/cf69b9c715cd84bdf605 で vite-plugin-vitest-cache (vCache) の設定は完了しているので、 CI/CD 上で cache 処理が動くように設定します。

`.tests` 以下を cache として保存するように actions/cache@v4 を利用します。

https://github.com/actions/cache

actions/cache@v4 の詳しい設定は README を参照してください。

### GitHub Actions workflow に処理を追加する

この設定例は、 `npm run test` でテストを実行するため、 `npm run test` の実行前に `actions/cache@v4` を利用して cache の取得を試みます。

`actions/cache@v4` に定義する `key` の値は Project の状態にも左右されるため、適切な条件をしていしてください。

```yml:.github/workflows/test.yml
name: Test

on:
  push:

jobs:
  vitest:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version-file: "package.json"
          cache: "npm"
      - run: npm ci
      - uses: actions/cache@v4
        with:
          path: .tests
          key: vitest-cache-${{ github.ref }}-${{ hashFiles('./.tests/**/*') }}
          restore-keys: |
            vitest-cache-${{ github.ref }}-
            vitest-cache-
      - run: npm run test
```

また、 前回の記事で `process.env.CI` を読み込むように設定しましたが、 GitHub Actions は実行時に `CI` 等特定の環境変数が定義されるため `uses` 句に環境設定は不要です。

https://qiita.com/mziyut/private/cf69b9c715cd84bdf605

## Ref

https://github.com/raegen/vite-plugin-vitest-cache

https://vitest.dev/guide/
Pythonでスクレイピングしてホロカの画像をすべて収集

友人とプログラミング学習のために毎週通話しています。

その中で友人はホロライブカードゲームの一人回し用のツールを作ることを目標としています。

私はそのお手伝いとしてそれに使う画像ファイルをスクレイピングするためのコードを書いたので今日はまとめておこうと思います。

まずはHTMLの解析ツールをインストールします。

```
pip install requests beautifulsoup4
```

その後、以下のファイルをコピペして実行します。
すると、"hololive_card_images"というフォルダが作成され、そこに205枚の現状全てのカードが保存されます。

```
import os
import requests
from bs4 import BeautifulSoup

# 画像を保存するフォルダを作成
output_folder = "hololive_card_images"
os.makedirs(output_folder, exist_ok=True)

# ベースURL
base_url = "https://hololive-official-cardgame.com/cardlist/"
img_url = "https://hololive-official-cardgame.com"

# カードIDの範囲
start_id = 1
end_id = 205

# User-Agentヘッダー
headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36"
}

for card_id in range(start_id, end_id + 1):
    try:
        # カードリストのURLを取得
        url = f"{base_url}?id={card_id}&view=text"
        response = requests.get(url, headers=headers)
        response.raise_for_status()  # HTTPエラーをチェック

        # HTMLを解析
        soup = BeautifulSoup(response.text, "html.parser")

        # 画像URLを取得
        image_tag = soup.find("img", src=lambda x: x and "/wp-content/images/cardlist/" in x)
        if not image_tag:
            print(f"Card ID {card_id} - Image tag not found.")
            continue

        image_url = img_url + image_tag["src"]

        # 画像をダウンロード
        image_response = requests.get(image_url, stream=True)
        image_response.raise_for_status()

        # ファイル名を設定
        file_name = os.path.join(output_folder, f"card_{card_id}.jpg")
        with open(file_name, "wb") as file:
            for chunk in image_response.iter_content(1024):
                file.write(chunk)

        print(f"Card ID {card_id} - Downloaded: {file_name}")

    except Exception as e:
        print(f"Card ID {card_id} - Failed: {e}")

```
【Day6】これからどんなゲーム作るんですか...！？【QAC24】


# 結局作るゲームって何なんですか？

前回まで Canvas を使いましたが、これからは Canvas を使ってゲーム画面を描画していきます。

で、その作るゲームなのですが、今回は「RPG」を作っていきます。

有名どころだとツクールとか Wolf とかがありますが、今回はそのようなエンジンを使わずに...というよりもそのようなエンジンに近いところから作っていきたいと思います。

# RPG をまず理解してみよう

と、プログラムを作るためには、まずはその対象について理解し、どうやったら再現できるかを考えることが大切です。

まずは「マップ」に重点を置いて RPG を理解していきましょう。

マップとは、プレイヤーが自由に動き回れる場所のこと...まぁフィールドとか呼ばれるやつですかね？
歩き回れる場所だけではなく、タイトルスクリーンとかもマップとして扱われることがあります。
ただ、今回はマップとスクリーンは別のものとして考えましょう。 (Day 20)
ただし、スクリーンを描画するためにはマップが必要ということにもしておきましょう。

## マップを描画する？

マップは、一般にタイルと呼ばれる小さな画像を並べて描画されます。 (Day 7, 8)

https://rpgmakerofficial.com/product/act/manual/04_01.html

今回もそのような形で描画していきます。
で、プレイヤーがいて、矢印キーでも押したらプレイヤーが動いて...

が、プレイヤーの動きは処理が少し大変そうです。
コリジョン (壁) に衝突している間は動けないし... (Day 19)
なので、少し荒業を使って、楽にプレイヤーを動かしていきます。 (Day 12)

## データを管理しなければ

RPG だけではなく、多くのゲームはセーブ機能があります。
いくら何でもそれを削っては RPG としての楽しさが苦行に変わります。

今回は、わざわざ Tauri を使っているので Tauri API を使用してデータを保存していきます。(Day 16)

## RPG といえば！

戦闘はやっぱり RPG の醍醐味ですよね！
ちなみにこの記事を書いてる時点で、戦闘については 1mm も書けてないので、ゆっくり考えていきます。 (Day 21)
が、基本は変わらず、戦闘のスクリーンを表示して、敵さんの情報を読み込んで...という感じでしょうか。

## 情報を読み込むと言えば

RPG にはいわゆる「イベント」があります。
プレイヤーが特定の場所に行くと、何かしらのイベントが発生する...マップ遷移なんかもそうですね。
人に話しかけたら会話が発生する...とかもイベントです。

これらはイベントテーブルというもので管理されることが多いです。
マップ上にトリガーを設置して、イベントを呼び出すような機構も必要ですね。(Day 22)

# ...と、こんなもんか？

このように、まず作りたいものがあるなら、それを細分化して理解してみましょう。
その上で、それをどうやって実装するかを考えていくと、プログラムを書く際に迷いが減るかもしれません。

まぁ今のところ迷いに迷いを重ねて作っていますが。
残り数十日で私はこれを完成させなければならない...！？

ここからの記事は、これらの RPG の要素について実装を進めていきます。
次回からは、マップについてです。

私の記事ストックが尽きるのが先か、25 日までに記事を完成させきるのが先か...！？
これも一種のゲームですのでお楽しみに...（過労）
【Day5】Canvas上でアニメーションをさせるには？【QAC24】


# 動かしたいよね。

前回は Canvas を使って画面描画をしてみましたが、今回はその Canvas 上でアニメーションをさせてみます。

アニメーションとは、画面上の要素を時間経過とともに動かすことです。
が、そんな機能は Canvas にはありません。

ではどうするか？
そんなの簡単です。
描画して消して、描画して消して、を繰り返すだけです。

# まずは消すところから

まず、現在の Canvas 上の描画を消す処理を書いてみましょう。

```typescript:main.ts
window.addEventListener("DOMContentLoaded", () => {
  const canvas = document.getElementById(
    "test-canvas"
  ) as HTMLCanvasElement | null;
  if (!canvas) return;
  const ctx = canvas.getContext("2d");
  if (!ctx) return;

  ctx.fillStyle = "red";
  ctx.fillRect(10, 10, 100, 100);

  const img = new Image();
  img.src = "https://baconmockup.com/200/200/";
  img.onload = () => {
    ctx.drawImage(img, 50, 50);
    ctx.clearRect(0, 0, canvas.width, canvas.height);
  };
});
```

`ctx.clearRect(0, 0, canvas.width, canvas.height);`が描画を消す処理です。
上から 0px、左から 0px に始まり、Canvas の幅と高さ分を消します。

これを実行すると、四角形が描画された後に画像が描画され、その後に画面が消えます。
（もちろん早いので目に見えないかも。）

# 次に描画する

今回はベーコンではなく四角形を動かしてみましょう。
そのために、描画する座標を受け取って、描画を行う関数を作ります。

```typescript:main.ts
const drawRect = (x: number, y: number) => {
  const canvas = document.getElementById(
    "test-canvas"
  ) as HTMLCanvasElement | null;
  if (!canvas) return;
  const ctx = canvas.getContext("2d");
  if (!ctx) return;
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  ctx.fillStyle = "red";
  ctx.fillRect(x, y, 100, 100);
};

window.addEventListener("DOMContentLoaded", () => {
  drawRect(10, 10);
});
```

`drawRect`関数は、引数として x 座標と y 座標を受け取り、まずキャンバスをクリアしてから、四角形を描画します。
さっきまでの四角形描画のコードをほとんど移しただけです。

ちなみに、TypeScript では引数に対して型を指定しなければ、暗黙的に`any`型として扱われます。
`any`型はどんな型でも受け付けるため、型安全が保証されません。そのため、引数には型を指定しましょう。

# さて、動かす

動かすためには、描画する座標を変更する必要があります。
そこで、`setInterval`関数を使って、一定時間ごとに座標を変更するようにします。

```typescript:main.ts
window.addEventListener("DOMContentLoaded", () => {
  let x = 10;
  let y = 10;
  setInterval(() => {
    x += 1;
    y += 1;
    drawRect(x, y);
  }, 1000 / 60);
});
```

まず、x 座標と y 座標を管理する変数`x`と`y`を定義します。
初期値として 10 を代入しています。

`setInterval`関数は、第一引数に実行する関数、第二引数に実行する間隔をミリ秒で指定します。
今回の`1000 / 60`は、1 秒間に 60 回実行することを意味します。つまり 60FPS ってこったな。

60FPS の間隔で、x 座標と y 座標を 1 ずつ増やして、四角形を描画する関数`drawRect`を増やされた`x`と`y`を渡して呼び出しています。

なんとこれで、Canvas 上でアニメーションを実装することが出来ました。
理論上は、この方法を使えばあらゆる描画が行えると思います。

もっと詳しく Canvas について知りたい人は以下の記事が参考になるかと思います。

https://developer.mozilla.org/ja/docs/Web/API/Canvas_API/Tutorial

これは、MDN というサイトで公開されている Canvas API のチュートリアルです。
このサイトは、初心者にはもしかしたらとっつきにくいかもしれませんが、優秀どころか最強サイトなので、ぜひ一度覗いてみてください。

それでは、次回以降はもう Canvas は出来てるものだと思って進めていきますぜ？兄貴...
AItuberと向き合った一年、振り返り

# 2024年。AItuberStudio。

去年までは趣味としてゲーム配信をさせてみたり、AI同士で会話させてみたりといったことをしていましたが、今年はAItuberStudioをtoB向けの製品として強化する一年でした。

toBに向けて製品として紹介する以上、保守管理のしやすさや、システムの安定性、安全性。さまざまな機能要件にスムーズに答えられる拡張性などを備えている必要があるため、そういった部分の改修作業。作り直しを一年を通して行っていました。

# AItuberStudioとは

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/609493/a6268da5-027b-7e69-af49-e118b5f583e9.png)

Unityベースで制作している、Nyagsic LLCからリリースしている製品です。
いなりくこちゃんのシステムにも使われています。
https://www.youtube.com/@%E3%81%84%E3%81%AA%E3%82%8A%E3%81%8F%E3%81%93

### 機能概要
##### AItuberとの会話機能（Youtube経由、Tiktok経由、ニコニコ動画経由、text入力、VOICE入力、映像入力）
##### 複数のLLM、TTSに対応
##### 完全記憶能力（すべてのことを覚えているが人間のようにどわすれする。必要な記憶を思い出したり、思い出そうとしても思い出せなかったりする。）
##### NGワード（事前に登録したワードについて回答することがありません。その内容についてはお答えできませんなどのお返事をします。）
##### 感情表現（複数の感情を数値化してアニメーションや口調などの表現に反映します。）
##### フィラー表現
##### 人格形成（一問一答に答えることで人格を形成する要素を収集し、その人の人格を形成します）

# 目標

全体的な管理のしやすさ、拡張性
Youtubeで安定して配信が行えるようになる。
インフラ環境での実装。
インタラクティブな体験ができるシステムを追加する。

### 機能
記憶保持
人格形成
感情機能
多数のアニメーションをその場の感情に応じて変化

# 達成率

#### 全体的な管理のしやすさ、拡張性（70％）
役割に応じてモジュール化することで拡張性があがりました。
現在リリースされているLLM、LMstudioでAPIが使用できるものに関してはサポートされています。TTSに関してはVoicevox、sbv2、ElevenlabなどAPIが使用できるものに関しては対応しています。

新しくリリースされたものも随時対応しています。

#### Youtubeで安定して配信が行えるようになる。（70％）
3ヵ月以上の連続配信が可能。
さすがに3日程度配信を続けるとメモリーリークや、APIの取得の失敗によるキャッシュなどが原因で応答がなくなってしまうことがある。

これからも安定性については向上できるのであればしていきたい。Youtubeリクエスト以外にもinputtextやvoiceによるinputもあるので合わせて安定性の向上をしていきたい。

#### インフラ環境での実装。（40％）
GCPを使用した実装テストをしている。TTSに関してもrunpodなどを利用して実装。NoSQL、かGCPのCloudSQLがいいのか検討中。

インフラについては自身ですべて管理する予定はないため、おそらく外注することにはなるだろうと予想はしているが、インフラエンジニアにPassできるように整理できる場所は整頓しておきたい。

#### インタラクティブな体験ができるシステムを追加する。（40％）
歌ってみたをリクエストすると歌ってくれる機能や、曲をリクエストするとリアルタイムで制作してくれる機能をテスト的に実装してみた。

インタラクティブな体験についてはいろいろ考えていく必要があると思っている。現在実装した機能も面白くはあるが視聴者がもっと楽しめるアイディアを考えたいところ。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/609493/ecce827c-0f42-abf4-f82a-e7c145f58273.png)

NT東京でお賽銭システムを導入した

#### 記憶保持（70％）
ユーザーの情報。常にプロフィールを制作、更新している。過去のuserとの会話も覚えている。話してくれた人の好みを話せば話すほど記憶してくれるようになっている。

ユーザーデータの蓄積についてですが、過去のコメント220000行、ユーザープロフィール6900行、程度の情報を蓄積しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/609493/a1251d7a-4537-19e7-1a68-e30f970e3b09.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/609493/cc084a44-dbda-2600-d5c1-39591943e2de.png)

#### 人格形成（60％）
一問一答形式で全500項目以上の質問に答えることで人格形成を可能としている。その人の歴史などや、他人から見た自分等を総合して人格を形成する。一問一答に関してはまだ質問数を増やしている。
#### 感情機能と多数のアニメーションをその場の感情に応じて変化（40％）
感情を10のパラメータで表現できるようにしている。表情に応じたアニメーションの再生。お話し中に変わった少しの感情変化も表現できる。アニメーションはすべてで50以上登録できる。

# Youtubeに要望を書き込めるフォームの設置
今年に入ってYoutubeの概要欄に要望を送れるフォームを設置することでリスナーと一緒に開発ができる環境を整えました。
実際にリスナーの方の意見も取り入れつつ、開発を進めることでより満足のできるサービスが提供できるように心がけていました。

# リファクタリング
10月の後半から配信のお休みをいただいて、約2年間だらだらと書いて保守管理がしづらくなっていたコードをリファクタリングしました。
要件定義があるわけでもないので、設計書も書かず走り続けていたのでなかなか大変な作業になりましたが11月時点でかなり保守管理しやすい状態になりました。

# 課題
今年の課題は来年の課題でもありますが、より面白い配信や使い方ができるように機能追加や拡張性を高め、みんなに楽しんでもらえるサービスを提供できればと考えています。
来年から色々大きく動いていく予定なので、わたしも楽しみです。
わくわくできるサービスを提供できるようNyagsic LLCのメンバーとともに頑張っていこうと思います。
【超基礎】TypeScript例外処理まとめ

## 前提
この記事は、初学者の私が、サバイバルTypeScriptで学んだこと+調べたことをまとめたものになります。

従って、経験則に基づいた発言ではないため、一部慣習とは違っていたり、認知にずれがあったりする可能性がありますので、ぜひコメントで指摘していただけると幸いです。

https://typescriptbook.jp/

## 例外処理とは？
- コード実行時に例外、つまり「エラー」が発生した時の処理
- 基本的にJSに組み込まれた`Errorオブジェクト`が使われる
- `throw`で投げ、`try-catch`で捉える

## throw
- エラーを投げる構文
- **JS,TSでは`Error型`以外のあらゆる型の値を投げることができる(非推奨)**
```typescript:throw
//コンストラクタにメッセージを渡したエラーオブジェクトを新たに作成し、throwする
throw new Error("エラーが発生しました");

//プリミティブ型もthrowできる(非推奨)
throw "エラーだよ"
throw 1
```

## try-catch
- エラーが発生し得る処理を`try`内に書き、`catch`で捉える
- あらゆる型を`throw`できるため、例外は**any型**として受け取る
- `Error`型のサブクラスを参照し、エラーハンドリングを行うことができる
- `JAVA`と違い、一つの`try`に一つの`catch`
:::note
TypeScript5.0以降、`ts-config`の`useUnknownInCatchVariables`を設定することで、より型安全な`unknown`型で`catch`できる。
:::

https://typescriptbook.jp/reference/tsconfig/useunknownincatchvariables

```typescript:try-catch
try{
    throw new Error("エラーが発生しました");
}catch(e){ //eはany型
    console.log(
}
```

## finally
- 例外の有無に関係なく実行される
- try-catchの後に実行される
```typescript:try-catch
try{
    throw new Error("エラーが発生しました");
}catch(e){ 
    console.log(
}finally{
    console.log("処理が終了しました")
}
```
## throw、try-catchは非推奨？
**エラーが型のチェックをすり抜けて返ってきたり**(`import`先で`try-catch`を使うべきか否か配慮する必要がある)、**エラーの場所を特定しにくかったり**するため、非推奨とする声もある

これらを防ぐために新たに**Result型作成**し、この値を**throwではなくreturnで返す**と良いらしい。

Result型は**正常な値とError型の両方に対応し、エラーハンドリングもできる**。

https://qiita.com/frozenbonito/items/e708dfb3ab7c1fd3824d

https://zenn.dev/ken7253/articles/reasons-not-to-use-throw


## まとめ
以上になります。例外処理への解像度が少し高くなりました！


当方初学者なため、認知のずれや、モダンな開発現場ではもう使わない表現などがありましたらコメントで指摘していただけると幸いです。😸
【高専ロボコン】設計班の視点から考えるロボット製作の進め方

## はじめに
この記事では、設計班として高専ロボコンに参加した筆者が「こーしたらよかったかも」と考えるロボット製作の進め方を書いていく。

そのポエムだけではあまりにも文章量が少なすぎるので、経験をもとに設計班がSolidWorksを使うときに役に立ちそうなことも書いていく。

(あくまで「鳥羽商船高専のロボコン部員」目線の話なので、弊校ロボコン部以外はあまり参考ならないかもしれないのは悪しからず)


## 軽い自己紹介
鳥羽商船高専　情報機械システム工学科
4年次以降の選択科目では情報・電気・機械のうち情報系の科目を選択

ロボコンでの担当は、加工(1年生)→制御(2年生)→設計(3~4年生)
(↑ただし諸事情により3年次はあまりメインで関わってはいない)


## 結論
### 試作を作れ！早く！


## 理由
#### 1. 期限
まずこの高専ロボコンとかいうコンテスト、タスクの量に対して期限が短い。
4月中旬のルール発表～10月初頭の大会本番までの約半年弱の間に、アイデア決めから操縦練習まで行わなければならない。

#### 2. アイデア決め
特に鳥羽商船高専ではアイデアを決めるときに、ひたすらアイデアを出し合い、強さや面白さからアイデアを決めていく。
このアイデアを決めるフェーズで機構の試作が少なくともCAD上にあると、違うアイデアを推しているメンバーをより早く納得させることができる。

#### 3. 設計班の立ち位置
設計が終わらなければ加工・回路・制御のすべてが進まない。(経験談)
当然、ロボットがないので練習もできない。

逆に、試作ができていればある程度は回路や制御も進めることが可能になる。
ロボットもある程度は形になっているので、選手がそれなりに感覚を掴むこともできるようになる。


## 結局…
### 早く作って練習しろ！


## ＋αのSolidWorks Tips
ここまで「**早く設計しろ！**」と重ねて書いてきたので、ここからは鳥羽商船高専が使用している3DCADソフトであるSolidWorksの中でも、筆者が使用してきた機能を設計で使う順に書いていく。
基本的なことばかりだけど知っておくと楽だと思う。
(ここからも弊校ロボコン部以外あまり参考にならないかもしれないのは悪しからず)

#### 部品-1. スケッチ
「スケッチ」から選択できる。
平面的に線を描く。

#### 部品-2. スマート寸法
「スケッチ」から選択できる。
必須。
長さだけでなく角度も指定することができる。
(スマート寸法以外でも可能だが)数値を入力するときには三角関数とかを使用できる。

#### 部品-3. 幾何拘束の追加
「スケッチ」「幾何拘束の表示/削除」の▽から選択できる。
一致や正接のように、2本の線を寸法ではなく位置関係で拘束できる。
一見いらなそうだが、既存の部品に対して寸法の変更などがあったときでも形を崩れさせないようにできる。

#### 部品-4. エンティティのミラー/円形パターンコピー
「スケッチ」から選択できる。
その名の通りの操作ができる。時短テクのひとつ。

#### 部品-5. 押し出し(回転)ボス/ベース
「フィーチャー」から選択できる。
これでようやく立体ができあがる。
押し出しか回転かは自分の使いやすいほうでいいと思う。

#### 部品-6. 押し出しカット
「フィーチャー」から選択できる。
いらない部分を切り取れる。
穴をあけたり溝を掘ったりとか。

#### 部品-7. 平面
「フィーチャー」「参照ジオメトリ」の▽から選択できる。
個人的にはあまり使わなかったけど、正面・平面・右側面以外に任意の面を追加できるので痒い所に手が届くといった感じ。

#### アセンブリ-1. 構成部品の挿入
「アセンブリ」から選択できる。
部品だけでなくアセンブリも挿入することができる。
複数使用するユニットがあるなら、ユニットのアセンブリで挿入すると楽。

挿入した部品に対して、Ctrlキーを押しながらドラッグアンドドロップをするとコピーができる(時短テク)。

#### アセンブリ-2. 合致
「アセンブリ」から選択できる。
立体版の幾何拘束みたいなもん。
めちゃくちゃ合致エラー吐いてくるやつはマジで何？

#### アセンブリ-3. 測定
「評価」から選択できる。
距離やら直径やらを調べられるので便利。

#### アセンブリ-4. 非表示/抑制
部品を右クリックして選択できる。
どちらも、「奥にある部品が見えないから一旦透過させる」というような用途以外に、「動作を軽くしたいから使う」ことがある。
非表示は部品をただ見えなくする。
抑制は非表示にする＋合致も一時的になかったことにする。
抑制は合致単位でも行うことができる。

#### アセンブリ-5. 構成部品置き換え
部品を右クリックして選択できる。
「穴を増やしただけの別部品に置き換えたい！でも元の部品をいじりたくない。。。」みたいなときに使う。時短テクのひとつ。

#### 図面-1. モデルビュー
「図面」から選択できる。
部品やアセンブリを図面におとせる。

↓「シート1」の右のやつを左クリックするとシートを増やせる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3925424/5070ef8e-727b-19a5-0ba7-579023b7ca23.png)

#### 図面-2. 部品表
「アノテートアイテム」「テーブル」の▽から選択できる。
モデルビューで挿入したアセンブリを選択することで、そのアセンブリに使用されている部品の名前と個数を表にしてくれる。
いらない行はDeleteでポイしよう。

#### 図面-3. スマート寸法
「アノテートアイテム」から選択できる。
部品の寸法を図面上に表示する。
「部品-2」のやつとは全くの別物。

スマート寸法選択後に自動寸法というものが選べる。
穴径があらぬ位置に表示される以外は概ね満足なのでオススメ。

#### 図面-4. 注記
「アノテートアイテム」から選択できる。
テキストボックスを図面上に作成する。

#### 図面-5. バルーン
「アノテートアイテム」から選択できる。
バルーン選択後に部品を左クリックすることで、その部品についての情報を表示することができる。特に、個数と部品名が便利。


## おわりに
以上、退役ロボコニストのポエム記事。
それでは、良きロボコンライフを。
vite-plugin-vitest-cache (vCache) を導入して Vite のテストを高速化する


## はじめに

Vitest の実行時間が長くなり困ることは往々にして発生します。
Vite の plugin vite-plugin-vitest-cache (vCache) を設定することで簡単にテストの実行時間の短縮を行うことができます。

## vite-plugin-vitest-cache (vCache) の設定

### パッケージのインストール

`npm install` や `yarn install` 等で `@raegen/vite-plugin-vitest-cache` を追加します。

```sh
npm install --save-dev @raegen/vite-plugin-vitest-cache
```

### 設定の追加

README に記載されている通り `vitest.config.ts` (`vitest.config.js`) 等に vite-plugin-vitest-cache (vCache) を読み込むように設定します。

```ts:vitest.config.ts
import { defineConfig } from "vitest";
import vCache from '@raegen/vite-plugin-vitest-cache';

export default defineConfig({
  plugins: [vCache()],
});
```

もし、 vite-plugin-vitest-cache (vCache) を CI/CD 上のみで動作させたい場合は、 `process.env.CI` の値が設定されている場合だけ Plugin が読み込まれるように定義すれば OKです。

```ts:vitest.config.ts
import { defineConfig } from "vitest";
import vCache from '@raegen/vite-plugin-vitest-cache';

export default defineConfig({
  plugins: process.env.CI
    ? [vitePluginVitestCache()]
    : [],
});
```

また、必要に応じて `.tests` を `.gitignore` 等に追加します

```config:.gitignore
.tests
```

## 実際に動かす

https://vitest.dev/guide/ の Getting Started の Project を用意 vite-plugin-vitest-cache (vCache) を「利用した場合」「利用しなかった場合」それぞれを用意しました。

用意したテストは簡単な内容のため実行時間に大きな差は生まれていませんが、ファイル数やテスト内容しだいでは大きな効果が出る場合もあります。

### 利用した場合

```sh
> npm run test

> vitest@1.0.0 test
> vitest


 DEV  v2.1.6 /Users/mziyut/Workspace/github.com/mziyut/playground/tools/vitest

[vCache] 0.3.2
[vCache] built hashes in 0.11s
 ✓ sum.test.js (1)
   ✓ adds 1 + 2 to equal 3

 Test Files  1 passed (1)
      Tests  1 passed (1)
   Start at  00:31:25
   Duration  404ms (transform 11ms, setup 0ms, collect 21ms, tests 1ms, environment 0ms, prepare 65ms)

 PASS  Waiting for file changes...
       press h to show help, press q to quit
```

### 利用しなかった場合

```sh
> npm run test

> vitest@1.0.0 test
> vitest


 DEV  v2.1.6 /Users/mziyut/Workspace/github.com/mziyut/playground/tools/vitest

 ✓ sum.test.js (1)
   ✓ adds 1 + 2 to equal 3

 Test Files  1 passed (1)
      Tests  1 passed (1)
   Start at  00:34:40
   Duration  197ms (transform 11ms, setup 0ms, collect 14ms, tests 1ms, environment 0ms, prepare 57ms)

 PASS  Waiting for file changes...
       press h to show help, press q to quit
```

## さいごに

README にも記載されている通り、 vite-plugin-vitest-cache (vCache) は簡単に設定できますが cache だけに頼らず定期的にテスト内容を確認するなど健全な状態を保つことを忘れないようにしたいですね。

## Ref

https://github.com/raegen/vite-plugin-vitest-cache
有向木を重ねたようなグラフの名前は？

仕事でとある処理を実装する際、下図のような階層構造をしたデータを扱うことになった。

![multi-directory.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/127130/f148af8f-eb31-7e2d-3a1a-112e10ec9cba.png)

最上位のどこを起点にしても配下は木構造（有向木）になっているので、例えば各ディレクトリに属するファイルを数えたい場合などは、普通の木構造に対するアルゴリズムで概ね上手くいく。

困ったのはアルゴリズムの選定ではなく、その考えをドキュメントに残すときだった。このグラフは部分的に見れば有向木だが全体を見れば有向木ではない。かといって有向非巡回グラフ（DAG）と言ってしまうと木構造に対するアルゴリズムで済むことを説明できない。「〇〇な DAG」と毎回特徴を添えるのは面倒。このグラフを簡潔に表す名前は無いだろうか？

## グラフの特徴

思いつく特徴を挙げていくと、

* 有向非巡回グラフ（DAG）の一種
* 有向木と異なり、頂点には親が2つ以上ありうる
* **任意の2頂点を結ぶ有向パスは高々1つ**
	* ダイヤモンド状 ♦️ の繋がり（やショートカット）は存在しない
	* 一方でバタフライ状 🦋 の繋がりはOKで、無向辺に置き換えるとパスは複数ありうる
* 任意の頂点から到達可能な頂点を抽出した部分グラフは有向木
* 任意の頂点へ到達可能な頂点を抽出した部分グラフは、有向木の逆向き
	* 有向木でいう「根」がひとつとは限らない
	* 親子関係を逆転させても同じ性質のグラフになる

DAG は前提として、太字にした事項が最も特徴的な気がする。これらの特徴が書いてあるグラフの説明を探した。

## 検索結果

調べたところ、 **multitree** という語句が見つかった。別名は strongly unambiguous graph や mangrove 。（マングローブという名前は根が複雑な木という感じで面白い）

https://en.wikipedia.org/wiki/Directed_acyclic_graph#Related_families_of_graphs の説明が簡潔かつ図入りでわかりやすい。

> **Related families of graphs**
>
> A [multitree](https://en.wikipedia.org/wiki/Multitree) (also called a strongly unambiguous graph or a mangrove) is a DAG in which there is at most one directed path between any two vertices. Equivalently, it is a DAG in which the subgraph reachable from any vertex induces an undirected tree.
>
> A polytree (also called a directed tree) is a multitree formed by orienting the edges of an undirected tree.
>
> An arborescence is a polytree formed by orienting the edges of an undirected tree away from a particular vertex, called the root of the arborescence.

multitree という名前がどれほど一般的なのか、和訳はあるのか、など細かいところまでは分からず。
（あと、日本語で「有向木」と呼ぶものは英語では "directed tree" でなく "arborescence" や "directed rooted tree" らしい）

## 利用例
multitree は今回初めて見たので、普段の例は多くない気がする。通常のディレクトリのような単純な親子関係なら有向木で表せるし、複雑な依存関係なら DAG になるはず。

身近な例のひとつは家系図。血縁関係にある結婚（いとこ同士など）が無ければ、たしかに祖先から子孫までの経路はひとつに決まる。
プログラミング寄りで考えれば、多重継承の類を使ったクラスの継承関係は、[菱形継承](https://ja.wikipedia.org/wiki/菱形継承)が無ければ multitree になる。

有向木を重ねるという方面から考えると、バージョン管理されたディレクトリが例として挙げられる（バージョン管理システム各種の内部的な仕組みの話ではなく、単に階層構造の話）。各バージョンにおけるディレクトリ構成はもちろん木構造だが、複数のバージョンを並べた場合は、**更新の無かったファイルやディレクトリは同じものを参照して構わない**。こうすればバージョン毎にコピーができてグラフが膨れ上がることを防げる。
例えば GitHub で適当なリポジトリを表示してみると、最終更新日時の異なるディレクトリやファイルが並んでいる。そのうち複数のブランチで同じ表示なものを開くと、ブランチに関わらず同じ内容が表示される。

![github-trees.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/127130/f86dd8d6-9804-6606-fa9c-490a39f73cc7.png)

<!--
※ Git 内部では SHA-1 の一致するオブジェクトを共有しているため、そもそも同じバージョン内であっても木構造になっていない。 multitree とみなせるのは、あくまでディレクトリとして表示した場合。
https://git-scm.com/book/ja/v2/Gitの内側-Gitオブジェクト
-->

3階層以上ある multitree を構成する際はダイヤモンド状の繋がりが含まれないよう注意を要するが、2階層ならその心配が無く必ず multitree になる。
【ZABBIX7.0LTS】zabbix_agentd.confのパラメータ設定

## はじめに
Zabbix Agent を監視対象サーバー（Linux・Windows）に導入する際に調査したパラメータについて、それぞれの概要や役割を記載したいと思います。

## 設定パラメータ

#### 【 Server（パラメータ） 】
「Server」は、パッシブチェックを行うZabbixサーバー（またはプロキシ）のIPアドレスやホスト名を指定するパラメータです。パッシブチェックでは、Zabbixサーバーがエージェントにリクエストを送り、その応答としてエージェントが情報を返します。

:::note info
設定例：
Server=192.168.0.1
Server=zabbix.example.com
:::

#### 【 StartAgents（パラメータ） 】
「StartAgents」は、Zabbixサーバからのパッシブチェックリクエストに対応するために、Zabbixエージェントが起動するワーカープロセスの数を指定するパラメータです。ワーカープロセスの数が多いほどリクエスト処理が迅速になりますが、その分サーバーへの負荷も増加します。値を「0」に設定すると、Zabbixサーバーからのパッシブチェックは無効化されます。

:::note info
設定例：
StartAgents=5
:::

#### 【 ServerActive（パラメータ） 】
「ServerActive」は、Zabbixエージェントがアクティブチェックを行うZabbixサーバー（またはプロキシ）のIPアドレスやホスト名を指定するパラメータです。エージェントがサーバーに接続し、チェックすべき項目を取得するために使用されます。

:::note info
設定例：
ServerActive=192.168.0.1
ServerActive=zabbix.example.com
:::

#### 【 HostMetadata（パラメータ） 】
「HostMetadata」は、Zabbixサーバー（またはプロキシ）にホストを自動登録する際に使用するメタデータを指定するパラメータです。このメタデータは、ホスト自動登録処理（アクティブエージェント）でのみ使用され、ホストが自動登録ルールに一致するかどうかを判断するために利用されます。例えば、「Linux」や「Windows」など、ホストOSを分類する情報を文字列で設定します。定義されていない場合は、HostMetadataItem に設定された値が使用されます。

:::note info
設定例：
HostMetadata=Linux
HostMetadata=Windows
:::

#### 【 HostMetadataItem（パラメータ） 】
「HostMetadataItem」は、自動登録時に送信されるホストのメタデータを自動的に収集するアイテムを指定するパラメータです。この設定を使用することで、特定のシステム情報（例：OSの種類やインターフェース名など）をメタデータとして送信できます。このオプションは、ホストの自動登録処理（アクティブエージェント）でのみ使用され、HostMetadata が未定義の場合に限り有効です。

:::note info
設定例：
HostMetadataItem=system.uname
:::

#### 【 Hostname（パラメータ） 】
「Hostname」は、Zabbixエージェントが自身を一意に識別するために使用するホスト名を指定するパラメータです。この値はアクティブチェックに必須であり、Zabbixサーバー（またはプロキシ）で設定されたホスト名と一致する必要があります。一致しない場合、Zabbixサーバー（またはプロキシ）はエージェントから送信されたデータを処理できません。未定義の場合は、HostnameItem から値が取得されます。

:::note info
設定例：
Hostname=testsver
:::

#### 【 HostnameItem（パラメータ） 】
「HostnameItem」は、Zabbixエージェントのホスト名を自動検出するためのアイテムを指定するパラメータです。この設定を使用すると、システムの情報（例：FQDNやIPアドレス）を基にホスト名を自動的に取得できます。このオプションは、Hostname が未定義の場合にのみ有効です。

:::note info
設定例：
HostnameItem=system.hostname
:::

#### 【 BufferSend（パラメータ） 】
「BufferSend」は、Zabbixエージェントが収集したデータをサーバー（またはプロキシ）に送信する間隔を指定するパラメータです。値を短く設定すると、データは即座に送信されますが、ネットワークトラフィックが増加する可能性があります。逆に、値を大きく設定するとトラフィックは減少しますが、データ送信が遅延する可能性があります。単位は秒です。

:::note info
設定例：
BufferSend=5
:::

#### 【 BufferSize（パラメータ） 】
「BufferSize」は、Zabbixエージェントがサーバー（またはプロキシ）に送信するデータを一時的に保持するバッファサイズを指定するパラメータです。バッファがいっぱいになると、エージェントは収集したすべてのデータをZabbixサーバー（またはプロキシ）に送信します。この値を増やすことで、大量のデータ収集やネットワーク遅延が発生する環境にも対応可能です。単位はアイテム数です。

:::note info
設定例：
BufferSize=100
:::

## 参考資料
・[【ZABBIX 7.0】3. Zabbix agent (UNIX)](https://www.zabbix.com/documentation/current/en/manual/appendix/config/zabbix_agentd)
・[【ZABBIX 7.0】5. Zabbix agent (Windows)](https://www.zabbix.com/documentation/current/en/manual/appendix/config/zabbix_agentd_win)
【Rails】1回きりのスクリプトファイル実行にはrails runnerを使うのが良い

## はじめに

先日、本番環境で一度きりのデータ更新が必要になりました。
<br>

具体的には「あるレコードの特定のカラムを`false`から`true`に更新したい」というケースでした。
<br>

これを実現する方法として以下のような選択肢がパッと思い浮かびました。
<br>

- Railsコンソールで手動実行
- Rakeタスクの作成
- 新しいコマンドの作成
<br>

しかし、1回きりの実行のために新しいタスクやコマンドを作るのは大げさです。
<br>

かといってコンソールでの手動実行はミスが怖くてやりたくありません。
<br>

そうした中で`rails runner`コマンドの存在を知ったため、備忘録としてまとめます。

## `rails runner`コマンドとは

`rails runner`はRailsの環境を読み込んだ状態でRubyのスクリプトを実行できるコマンドです。
<br>

以下のシンプルなコマンドさえ叩けば、追加の設定や定義はせずにスクリプトを実行できます。
<br>

```bash
$ rails runner path/to/your_script.rb
```

### 実際の使用例

まず、以下のようなRubyのスクリプトを作成しました。
<br>

```ruby:scripts/update_user_verified.rb
# 更新対象のユーザーを取得
target_user = User.find_by!(name: '佐藤二郎')

puts "#{target_user.name} (認証: #{target_user.verified})"

if ENV['DRY_RUN']
  puts "確認モードのため、実際の更新は行いません"
else
  target_user.update!(verified: true)
end

puts "#{target_user.name} (認証: #{target_user.verified})"
```
<br>

そして次のように実行します。
<br>

```bash
# 確認実行（どのレコードが更新対象かを確認）
$ DRY_RUN=1 rails runner scripts/update_user_verified.rb
佐藤二郎 (認証: false)
確認モードのため、実際の更新は行いません
佐藤二郎 (認証: false)

# 本番実行（実際にデータを更新）
$ rails runner scripts/update_user_verified.rb
佐藤二郎 (認証: false)
更新を行います
佐藤二郎 (認証: true)
```

## なぜ`rails runner`コマンドが良かったか？

`rails runner`はRakeタスクと比べて次の点で良いと思いました。

### 1. ファイル配置が自由

- Rakeタスク: `lib/tasks`ディレクトリに`.rake`ファイルを作成する必要がある
- `rails runner`: 任意のディレクトリに`.rb`ファイルを配置してOK

### 2. コードの書き方が自由

- Rakeタスク: `namespace`, `task`, `desc`などの定型文が必要
- `rails runner`: 純粋なRubyプログラムとして書いてOK

### 3. 一時的なスクリプトだと明示できる

- Rakeタスク: 恒久的なタスクとして`lib/tasks`に残り続ける
- `rails runner`: `scripts`ディレクトリに配置するなどして、一時的な用途であることを明確にできる

## おわりに

1回きりのデータ更新には`rails runner`がとても便利だと分かりました。
<br>

Rakeタスクほどの本格的な実装は必要なく、かつコンソールで手動実行するよりも安全に作業を進められます。
<br>

今後も同じような状況があれば、`rails runner`を活用していきたいと思います。

### 参考

https://railsguides.jp/command_line.html#bin-rails-runner
IPアドレスの仕組みを学ぼう：CIDRとサブネットマスク入門

CIDR（Classless Inter-Domain Routing）は、ネットワークのIPアドレス範囲を効率的に表現するための方法です。

---

### **1. CIDRの基本構造**
CIDRは次の形式で表されます：

```
<ネットワークアドレス>/<サブネットマスクのビット数>
```

例：`192.168.0.0/24`  
- `192.168.0.0`：ネットワークの開始アドレス  
- `/24`：サブネットマスクの長さ（最初の24ビットがネットワーク部分）

---

### **2. サブネットマスクとIPアドレスの範囲**
- サブネットマスクのビット数が増えるほど、ネットワーク部分が増え、使用可能なホスト数（IPアドレス）が減少します。
  
| CIDR表記 | 使用可能なIPアドレス数 | 説明                 |
|----------|------------------------|----------------------|
| `/16`    | 65,536個               | 大規模なネットワーク |
| `/24`    | 256個                  | 一般的なサブネット   |
| `/28`    | 16個                   | 小規模なネットワーク |

使用可能なIPアドレス数 = \( 2^{32-サブネットマスク長} - 2 \)  
（ネットワークアドレスとブロードキャストアドレスを除くため-2）

https://qiita.com/SMZPP/items/fc53ccd0262623797c86

---

### **3. 計算例**
#### **例1：CIDR `192.168.1.0/24`**
- サブネットマスク長：24ビット  
  → 使用可能なIPアドレス数：\( 2^{32-24} - 2 = 256 - 2 = 254 \)  
- IP範囲：`192.168.1.0` ～ `192.168.1.255`  
  - `192.168.1.0`: ネットワークアドレス  
  - `192.168.1.255`: ブロードキャストアドレス  

#### **例2：CIDR `10.0.0.0/16`**
- サブネットマスク長：16ビット  
  → 使用可能なIPアドレス数：\( 2^{32-16} - 2 = 65,536 - 2 = 65,534 \)  
- IP範囲：`10.0.0.0` ～ `10.0.255.255`

---

### **4. CIDRをシンプルに理解するポイント**
1. **ビット数が大きいほど、範囲が小さい**（例：`/28`は`/24`よりも範囲が狭い）。
2. **範囲の計算は2のべき乗**を使う。

https://qiita.com/SMZPP/items/ac80c197081f05fe0254