OmniAICreator/Qiita-1.07M huggingface dataset:
【データ分析】月毎に分かれたログが保存してあるテーブルを結合して集計する方法

# 目的
別記事で紹介したApacheログを月毎にDBのテーブルに保存してある状態で、月毎のアクセス数を一括して取得したい場合に、一括で取得できるクエリを実現する。


# 環境
MySQL8.0


# テーブル構成
月毎のテーブル構成は以下の作りにしている。
```
CREATE TABLE `access_log_yyyymm` (
  `index` bigint DEFAULT NULL,
  `ip` text COLLATE utf8mb3_bin,
  `time` datetime DEFAULT NULL,
  `request` text COLLATE utf8mb3_bin,
  `status` bigint DEFAULT NULL,
  `size` bigint DEFAULT NULL,
  `referer` text COLLATE utf8mb3_bin,
  `user_agent` text COLLATE utf8mb3_bin,
  `server` text COLLATE utf8mb3_bin,
  KEY `ix_access_log_202407_index` (`index`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_bin
```

# 実現方法
そのままでは共通カラムがないため結合できないため、ダミーのカラムを用意して結合すれば月毎のテーブルを結合できる。

各月のリクエストに"*****"が含まれる数をカウントしたい場合は以下の1クエリで取得できる。
```
SELECT
    sub202401.cnt AS PV_202401
    , sub202402.cnt AS PV_202402
    , sub202403.cnt AS PV_202403
    , sub202404.cnt AS PV_202404
    , sub202405.cnt AS PV_202405
    , sub202406.cnt AS PV_202406
    , sub202407.cnt AS PV_202407
FROM
    ( 
        SELECT
            'dummy' AS dummy
            , COUNT(*) AS cnt 
        FROM
            analytics.access_log_202401
        WHERE
            request LIKE '%*****%'
    ) sub202401 JOIN (
        SELECT
            'dummy' AS dummy
            , COUNT(*) AS cnt 
        FROM
            analytics.access_log_202402
        WHERE
            request LIKE '%*****%'
    ) sub202402
        ON sub202401.dummy = sub202402.dummy
     JOIN (
        SELECT
            'dummy' AS dummy
            , COUNT(*) AS cnt 
        FROM
            analytics.access_log_202403
        WHERE
            request LIKE '%*****%'
    ) sub202403
        ON sub202402.dummy = sub202403.dummy
     JOIN (
        SELECT
            'dummy' AS dummy
            , COUNT(*) AS cnt 
        FROM
            analytics.access_log_202404
        WHERE
            request LIKE '%*****%'
    ) sub202404
        ON sub202403.dummy = sub202404.dummy
     JOIN (
        SELECT
            'dummy' AS dummy
            , COUNT(*) AS cnt 
        FROM
            analytics.access_log_202405 
        WHERE
            request LIKE '%*****%'
    ) sub202405
        ON sub202404.dummy = sub202405.dummy
     JOIN ( 
        SELECT
            'dummy' AS dummy
            , COUNT(*) AS cnt 
        FROM
            analytics.access_log_202406 
        WHERE
            request LIKE '%*****%' 
    ) sub202406 
        ON sub202405.dummy = sub202406.dummy;
```


# 改善できそうなところ
Apacheログのテーブルを月毎に保存すればデータ数もそれほど多くなく扱いやすいと思ったが、１つのテーブルに保存するようにして月毎にパーティションを区切って取り扱う方が良いかもしれない。
DifyからPerplexity APIを使う

:::note info
Dify v0.7.3で、Perplexityに対応しました。
https://github.com/langgenius/dify/releases/tag/0.7.3
:::

Perplexity Searchの使い方をアップしました。

https://qiita.com/inakano_ojisan/items/abe6ad2ea89c8f31418d

## 背景
ソフトバンクユーザーは1年限定でPerplexity Proが使える様になりました。

https://www.linemo.jp/service/perplexity-ai/

私もたまたまLINEMOユーザーだったので早速申し込んで使用していますが、Perplexityは、ChatGPTにインターネットのRAGを追加した様な感じで、最近ではChatGPTの利用頻度はかなり減り、Perplexityをよく使っています。

Perplexityについては下記を参照ください。

https://qiita.com/Kei-dev-1213/items/cdf00fdb2d00255f80f5

ところで、PerplexityはAPIでも利用できます（インターネットのRAGもそのまま使えます。ただし利用できるAIモデルはSonarなどに限られます。GPT-4oとかClaude 3.5 Sonnetは使えません）。

そして、Perplexity ProユーザーはAPI利用のためのボーナスが毎月5ドルもらえます。この5ドルの範囲で、ソフトバンクユーザーは1年間無料でAPIが使用できます。

せっかくなので、これをお手軽に使える手はないかと考えたのですが、今話題のノーコードツールである[Dify](https://dify.ai/jp)から使える様にしてみました。

以下、黙示録として記録します。

## 大まかな手順

```mermaid
flowchart TD
    A[Perplexity Proへ申し込み] --> B[Perplexity APIキーを生成]
    B --> C[DifyのOpenAI-API-compatibleでモデル登録]
    C --> D[チャットボットから動作確認中]
```

## Perplexity Proへ申し込み
下記を参照ください。

https://www.softbank.jp/mobile/service/perplexity-ai/

## APIキー生成
以下からAPIキーを生成できます。
https://www.perplexity.ai/settings/api
毎月5ドル分のクレジットが無料でチャージされる設定にしておきます。

以下が参考になります。

https://note.com/robothink/n/nf90ca03c8e29

## モデル選択
サポートされるモデルは以下を参照。
https://docs.perplexity.ai/docs/model-cards
Perplexityの特徴でもあるonlineモデルがおすすめ。

モデル毎に値段が違います。
https://docs.perplexity.ai/docs/pricing
5ドルの枠を大事に使いたいので私はsmallモデルを選択しました。

## Difyへ登録
DifyのモデルプロバイダーにはPerplexityが無いので、OpenAI-API-compatibleで登録します。
Model Nameは「llama-3.1-sonar-small-128k-online」とします。
API Keyを入力します。
API endpoint URLは以下とします。
```url
https://api.perplexity.ai/
```
![スクリーンショット 2024-08-24 10.19.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202767/e220817e-542f-dadc-653f-db0de156df41.png)

Delimiter for streaming resultsのところは「\n\n」を「\n」に修正しておきます。
![スクリーンショット 2024-08-24 10.19.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202767/d48abe5e-487c-7b40-66e6-dda4ad7109d7.png)

上記以外はデフォルトのままとしました。

## 動作確認
試しに、最近の政治情勢について尋ねてみます（2024年8月24日現在）。
![スクリーンショット 2024-08-24 12.44.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202767/d2cdee1c-a014-fc90-8178-1dc8bef3f391.png)

無事、回答が戻ったら完成です！

ソフトバンクユーザーは無料で（1年間の限定ですが）Difyのワークフローとかと組み合わせることで、何か良さげなアプリが作れそうな予感です。

## 追記
別の記事も記載しましたのでご参考にしてください。

https://qiita.com/inakano_ojisan/items/1cbc4e9e677e0fca27a8
Azure Static Web Apps で次世代フロントエンドツールの Vite と swa cli について学んでみた

Azure Static Web Apps のクイックスタートを読んでいると、Vite という次世代フロントエンドツールがあるらしく、これは何だろうと試してみたくなりました。また、swa cli で Azure Static Web Apps にデプロイする方法も、実際やってみないとわからないので試してみました。

## ローカルで Vite アプリの作成

```bash:bash
prefix=mnrswadev
region=eastasia

npm create vite@latest $prefix -- --template=vanilla

cd $prefix

npm install

npm run dev

open http://localhost:5173/
```

Vite のドキュメントを読むと template で指定している `vanilla` 以外にも、下記のテンプレートがあるようです。

vanilla-ts, vue, vue-ts, react, react-ts, react-swc, react-swc-ts, preact, preact-ts, lit, lit-ts, svelte, svelte-ts, solid, solid-ts, qwik, qwik-ts

## ローカルで Vite アプリを確認

![swa-vite-01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/675885/533fec6f-ae5a-e3b1-1a72-77bc6701b37e.png)

## Azure に Static Web App を作成

```bash:bash
az group create \
  --name ${prefix}-rg \
  --location $region

az staticwebapp create \
  --name ${prefix} \
  --resource-group ${prefix}-rg \
  --location $region
```

## ローカルの Vite アプリを初回デプロイ

```bash:bash
cat <<EOF > staticwebapp.config.json
{
    "navigationFallback": {
        "rewrite": "/index.html"
    }
}
EOF

npm install -D @azure/static-web-apps-cli

npx swa init --yes

npx swa build

npx swa login --resource-group ${prefix}-rg --app-name ${prefix}

npx swa deploy --env production

open https://$(az staticwebapp show \
  --resource-group ${prefix}-rg \
  --name ${prefix} \
  --query defaultHostname \
  --output tsv)
```

## Azure で Vite アプリを確認

![swa-vite-02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/675885/736b976b-be7c-63cb-5187-e2110f3646f5.png)

## 2 回目のデプロイ準備

アプリが更新された事を確認するため、`counter.js` の `count is` を `カウンターの値：` に変更します。

```js:counter.js
export function setupCounter(element) {
  let counter = 0
  const setCounter = (count) => {
    counter = count
    element.innerHTML = `カウンターの値： ${counter}`
  }
  element.addEventListener('click', () => setCounter(counter + 1))
  setCounter(0)
}
```
## 2 回目以降のデプロイを試す

```bash:bash
npm run dev

open http://localhost:5173/

npx swa build

npx swa deploy --env production

open https://$(az staticwebapp show \
  --resource-group ${prefix}-rg \
  --name ${prefix} \
  --query defaultHostname \
  --output tsv)
```

## 2 回目のアプリ更新を確認

![swa-vite-03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/675885/4dea116e-e081-68c7-8c1c-20d1e92da3f6.png)

## 検証後の片付け

```bash:bash
az group delete \
  --name ${prefix}-rg \
  --yes
```

## 参考

https://learn.microsoft.com/ja-jp/azure/static-web-apps/deploy-web-framework?tabs=bash&pivots=vanilla-js

https://vitejs.dev/guide/

https://azure.github.io/static-web-apps-cli/docs/intro
Vue.js初学者が書籍片手にtodoアプリをつくる　前篇：環境整備

## はじめに
最近、協力会社の開発でVue.jsが使用されていることが続いたので、
すこしぐらいは知識を持っておこうと思い、書籍とVue公式を参考にアプリ開発をしてみました。

### 参考書籍
完全初学なので書籍を写経する形で開発しました。

『Vue.js 超入門 v3.4+: 最初に手にしてよかった本』

https://www.amazon.co.jp/dp/B0CW1BZ6PG?ref_=cm_sw_r_cp_ud_dp_79Q82QWRT76X0TFT1ZP6

具体的なソースに加えて、必要な解説が記載されていて非常にわかりやすかったです。

## Vue.js とは
https://ja.vuejs.org/guide/introduction

公式によると↓
>Vue（発音は /vjuː/、view と同様）は、ユーザーインターフェースの構築のための JavaScript フレームワークです。標準的な HTML、CSS、JavaScript の上に構築され、あらゆる複雑さのユーザーインターフェースを効率的に開発するのに役立つ、宣言的でコンポーネントベースのプログラミングモデルを提供します。

また、こんなことも書いてます↓
>このドキュメントの続きでは、HTML、CSS、そして JavaScript の基礎知識があることが前提となっています。もしフロントエンドの開発をまったく経験したことがなければ、最初の一歩で Vue のようなフレームワークにすぐに飛び込むのは最適な方針でないかもしれません。基本を習ってからここに戻ってくるのでも大丈夫です！　必要に応じて、JavaScript、HTML、CSS の概要を見て知識レベル確認できます。他のフレームワークを使用した経験があれば役立ちますが、必須ではありません。

Web開発から13年以上離れた人はお呼びでない感があるが、めげずに続けます。

## 開発環境の整備
環境作成は参考書籍と公式の[クイックスタート](https://ja.vuejs.org/guide/quick-start.html)を見ながら進めます。

### 開発マシン
OS:Windows 11 Home 23H2
エディタ:VSCode

### Node.js,npmのインストール
Node.js のバージョン 18.3 以上をインストールしている必要があるとのことで、node.jsのインストールから行う。

https://nodejs.org/en/download/package-manager

```winbatch:node.jsとnpmのバージョン
> node --version
v20.16.0

> npm --version
10.8.1
```

### Vueプロジェクトの作成

コマンドプロンプトでプロジェクトを作成したいフォルダまで移動してプロジェクトを作成する。
```winbatch:Vueプロジェクトの作成
> npm create vue@latest
```

さっそく公式にはない内容が・・・
```
Need to install the following packages:
create-vue@3.10.4
Ok to proceed? (y) 
```
必要なパッケージがなかったみたいで y で続行

対話形式で必要な機能を Yes/No を選択するととりあえずプロジェクトは作れたみたい。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/296985/4a023b8d-9570-8fcd-7c86-92b132772f05.png)

### ローカルサーバの起動
以下のコマンドでサーバを起動する
```winbatch
> cd <your-project-name>
> npm install
> npm run dev
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/296985/1ad51839-e316-e2da-ecd1-c55cc43afc79.png)

[VITE](https://ja.vitejs.dev/)ってなんだろ？
とか調べつつ、`http://localhost:5173/`をブラウザで接続する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/296985/ba1ed836-d024-16e5-0034-a7ce38cdf22f.png)

こんな画面が出たので、サーバ起動までは無事完了したみたいです。

今回はここまで。
つづき↓

https://qiita.com/yorocovich/items/83e4a42dea2112c07768
特定のポートを使用しているプロセスを終了するバッチ

特定のポートを使用しているプログラムを探索して終了させるWindows用のバッチです。
引数に対象となるプロトコルとポートを指定します。複数入力可能です。

以下簡単な処理の説明です。
* 引数からプロトコルとポート番号を取得する
* netstat -anoで出力されたリストと比較し、PIDを取得する
* 対象のPIDをtaskkillを使ってキルする

~~~ :使い方の例
killprocess.bat UDP:62500-62550 TCP:56000
~~~

~~~ bat:killprocess.bat
@echo off

:loop
if "%1"=="" goto end
for /f "tokens=1,2,3 delims=:- usebackq" %%i in ('%1') do (
	SET PROTOCOL=%%i
	SET STARTPORT=%%j
	SET ENDPORT=%%k
	CALL :killprocess %%i %%j %%k
)
shift
goto loop

:killprocess
setlocal enabledelayedexpansion

for /l %%n in (%2,1,%3) do (
	for /f "tokens=1,2,4,5" %%i in ('netstat -ano ^| find ":%%n"') do (
		echo %%j | find ":%%n">NUL
		if not ERRORLEVEL 1 if %1==%%i (
			SET PID=%%k
			if /i %1==TCP (
				SET PID=%%l
			)
			echo PROTOCOL:%1 Port:%%n PID:!PID!を終了します。
			taskkill /f /pid !PID!
		)
	)
)
exit /b
endlocal

:end
~~~

netstatコマンドで状態を取得しますが、TCPとUDPで出力のされ方が違う(厳密にいえば空白で出力されるので行が変わってくる)ので、以下のように読み込む位置をずらしています。
~~~
SET PID=%%k
if /i %1==TCP (
	SET PID=%%l
)
~~~

複数のポートを使うアプリの場合、複数回KILLされます。
TryHackMe WriteUp　AgentSudo

TryHackMeのAgentSudoに取り組んだので簡単に何をしたのか記載します。
詳細な手順や解答はないので悪しからず。。。

# Task1: Author note

マシンを起動するだけです

# Task2: Enumerate

1. nmapでポートスキャンしてみます
1. IPアドレスにHTTP接続してみる
1. Burp suite を使って総当たりでuser-agentを割り出します

# Task3:  Hash cracking and brute-force

1. Hydraでブルートフォースしました。パスワードリストはrockyou.txtを使用しました
1. この問題は最後まで分からなかったです。なぜかというと、stegseekというツールを使えば、Zipファイルを経由せずに次の課題ができてしまったからです。
1. Stegseekを使います
1. Stegseekの出力ファイルに記載
1. Stegseekの出力ファイルに記載

# Task4: Capture the user flag

1. SSH接続します
1. 画像をChatGPTに読み込ませて調べました（多分有名な画像と思われます）

# Task5: Privilege escalation

1. rootのVersionと`sudo -l`の出力結果からCVEを調べます。
1. root権限に昇格後にrootディレクトリにいきます（これを探すのが大変だった）
1. 上の問題の回答と同じところに記載があります（Ｒから始まらないのでは最初わからなかった、、、）

詳細な解説：
https://qiita.com/kaz_tak/items/43bc589f327013c5ac99

CVEで参考にしたWebページ
https://www.jpcert.or.jp/newsflash/2019101601.html
(UNO)8ビット機 信号処理用の1/Xの表を作って遊ぶ(固定小数点8,8)(8ビット/8ビット=8ビット)(割り算)


目的
入ってきた8ビット信号を増幅では、なく、絞るため


いろいろ
DSPとかの固定小数点用に付いていたり、いなかったりするが
精度が低く独自なため(各社工夫を凝らす)、あまり使われない方法だか
DSPとかの信号処理の場合、処理している間に次の信号が来る為
とりあえず、そこは、そこで速度重視なやり方
(約256バイトぐらい、けちるなと思うが!!!DSP系の命令を付けるなら付けろ、
設計思想に反するなら仕方ない)
(1/2,1/4,1/8して×(掛ける)と言うやり方もある　シフトは、さすがに付いている)

割られる数  0...256
割る数　    0...256
答え        0...255

範囲を超えた場合は、不定

例外　入力 256は、確認用
例外　割る数 入力 1の時は、そのまま
　　　(ただし、確認のための入力 256の時は、255を返す)
例外　割る数 0の時は、1で割る時と同じ(そのまま)


表の使い方(遊び方)

x = 割られる数
y = 割る数
z = 答え

z = (x * u[y])>>8;
(x,y,z共に16ビットに拡張(強制キャスト)(掛けた時に範囲を超える為))



結果


![o_coq341.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/e45bd596-64d2-9e72-0f99-fd3eea282474.jpeg)





プログラム


オンラインコンパイラ


```c

#include <stdio.h>
int main(void){
    // Your code here!
    int s;
    int h=2560;
    int l=10000;
    int y,u,m,p;
    
    printf("{\n");
    p = 0;
    for(int i=0;i<256;i++){
        s = i;
        if(s==0){s=1;};
        
        y = h/s;
        
        u = (y + 0)/10;
        
        m = (256 * u)>>8;
        
        //printf("<2560>/(%d)=[%d] 10000/(%d)=[%d] <<256/(%d)=[%d]>>  256/%d=%d    ",
        //                i,   y,         i,  l/s,          i,   m,        i, 256/s );
        
        if(i==0 || i==1){u=255;}
                        
        if(i != 255){printf("%d,",u);}else{printf("%d ",u);}
        p++;
        if(p >= 8){printf("\n");p=0;}
        
    }
    printf(",1\n"); // /256
    printf("}\n");
}



```



```

{
255,255,128,85,64,51,42,36,
32,28,25,23,21,19,18,17,
16,15,14,13,12,12,11,11,
10,10,9,9,9,8,8,8,
8,7,7,7,7,6,6,6,
6,6,6,5,5,5,5,5,
5,5,5,5,4,4,4,4,
4,4,4,4,4,4,4,4,
4,3,3,3,3,3,3,3,
3,3,3,3,3,3,3,3,
3,3,3,3,3,3,2,2,
2,2,2,2,2,2,2,2,
2,2,2,2,2,2,2,2,
2,2,2,2,2,2,2,2,
2,2,2,2,2,2,2,2,
2,2,2,2,2,2,2,2,
2,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1 
,1
}


```
AWS Lightsail のWordpress で使っているLet's Encryptの証明書を更新する

Wordpress を手軽に使うために、AWSのLightsailを使っています。以前の記事（[Wordpressで作ったホームページを安全に公開する方法](https://qiita.com/sawabenaota/items/6ad1da5af6caf91560c4)）にも書いた通り、Wordpress は静的コンテンツを生成する際だけ使っていて、日頃は停止状態にしていました。

# 証明書の有効期限が近づいている旨のメールが届く
あるとき、expiry@letsencrypt.org から「あと19日で証明書が失効する」旨のメールが届きました。なんとなく自動更新されると思い込んでいましたが、サーバが立ち上がっていなければ自動更新のしようもないことに気づき、手動での更新作業を始めました。

# Lightsailの設定状況によって更新手段は様々
Lightsail固有の更新方法がありそうだと思って検索してみたところ、AWSのサポートサイトに手順が書かれていました。今回はここに書かれた手順で完了できました。

[Lightsail インスタンスでホストされている Bitnami スタックの Let's Encrypt SSL 証明書を更新する方法を教えてください。](https://repost.aws/ja/knowledge-center/lightsail-bitnami-renew-ssl-certificate)

大きく分けると、証明書をインストール手段には、bncert-tool あるいは Certbot を使うケースがあるようです。私が使っているサーバでは、Certbot を使っていたので、証明書の更新自体は次のコマンドを実行するだけでできました。
```
sudo /opt/bitnami/ctlscript.sh stop
sudo certbot renew
sudo /opt/bitnami/ctlscript.sh start
```
これで90日間延長できました。通常時にサーバを止めている現在の運用を続ける限りは、また90日後までに同じ作業を実行する必要があります。
【laravel】検索機能作りたい時にwhen()メソッドがめっちゃ楽な件について

## はじめに
検索機能をlaravelで実装したい時に、if分岐を連発させたくないなぁ。めっちゃダサいなぁと個人的な謎のこだわりにより、ついに辿り着いたwhen()メソッド。めちゃくちゃ便利すぎたので、忘れないように備忘録として記録しておく。


## when()メソッドって何
第一引数が「true」を返したときに、第二引数を実行するlaravelのデフォルトメソッド。
そしてELQUENTでもクエリビルダどちらも使用可能という個人的にめちゃくちゃ便利なメソッド。

## whenメソッドを使用すると・・・
```TodoService.php
$query = Todo::query();
$name = $request->input('name');
$query->when($name, function($query) use($name) {
    $query->where("name", "LIKE", "%{$name}%");
});
return query;
```
めちゃ簡単。しかもチェーン可能なので項目が増えたらwhen()メソッドて繋いで絞り込みが可能となるので便利だぁ！！


## ちなみにif分岐だと・・・
```TodoService.php
$query = Todo::query();
$name = $request->input('name');
if ($name) {
    $query->where('name', 'LIKE',"%{$name}%");
}
```

なんかダサいし項目が多くなった時にif分岐連発の予感。
個人的にはフレームワーク使用しているのになかなか許せない。


## 終わりに
laravelって本当に簡単。
何やっても楽に早く完結に実装できるためめちゃくちゃ好きなフレームワークの一つ。
【Salesforce】Winter'25 FlowBuilder レコード作成要素の強化

![01ロゴ.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/789516/a098ef04-b08f-9e85-7e5d-a5212d728229.jpeg)

この記事では、SalesforceのWinter'25のFlow Builderのアップデートを1つピックアップして紹介します。

## レコード作成要素の強化
Flow Builderのアップデートで、レコードの作成や更新がもっと簡単になります。これまでは、新しいレコードを作成するときに、同じフィールド値がすでにデータベースにあるかどうかを確認する必要がありました。
しかし今後はその手間を減らせるかもしれません。

今回のフロービルダーでは、レコードを作成する際に、指定したフィールド値がすでに存在するかどうかを自動でチェックしてくれます。そして、存在する場合はレコードを更新し、存在しない場合は新しくレコードを作成してくれるそうです。このアップデートをうまく使えば、重複作成のミス心配が減らせそうです。

Flow Builderから、レコードの作成要素を追加します。作成するレコード数で、複数をクリックし、レコードコレクションを選択します。既存のレコードの更新を有効にします。次に、既存のレコードを識別するための残りのオプションと、レコードが失敗した場合に残りのレコードを処理するための残りのオプションを選択します。

### レコードの項目値の設定方法
「レコード変数から」を選択します。
[01Flow1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/789516/58ecd748-a245-09e5-b71d-ac12d66d2d6a.png)

### Update Existing Records
「Disabled」を有効にします。
![02Flow2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/789516/7c1e9caa-20e9-0154-7e52-5b23df6422f9.png)

※記述内容を翻訳すると下記のようになりました。

既存のレコードの更新
実行時に、Create Records要素は指定されたフィールドを使用して、コレクション内の各レコードのレコードを作成するか、既存のレコードを更新します。レコードの作成または更新に失敗した場合は、コレクション内の他のレコードを試すか、すべてをロールバックするかを選択します。
既存のレコードを識別するために使用するフィールド
- レコードID
- 外部ID
- 標準フィールド

作成または更新に失敗した場合の処理
- 成功したレコードを処理する。成功したレコードのみを作成または更新する。
- レコードを処理しない。レコードの作成または更新を行わず、トランザクションをロールバックする。

いかがでしたでしょうか？
他にも、Winter'25のリリースは、Salesforceの使い勝手を向上させるためのアップデートが含まれていそうです。
引き続き、今後も新しいリリース機能を触って紹介していきます！
フロントエンドエンジニアのためのGo入門：変数と関数をサクッと理解しよう！

# はじめに
Goはシンプルで効率的な言語ですが、TypeScript(Javascript)の経験のあるフロントエンドエンジニアが初めて学ぶ際には、いくつかの独特なポイントで戸惑ったので記事にしました。
フロントエンドエンジニアがGoを学ぶ際につまづきやすい点をわかりやすく解説し、それぞれの言語の比較コードを示します。
この記事はA Tour of GoのBasicsの一部をピックアップしました。

https://go-tour-jp.appspot.com/list

# エクスポートされた名前
Goでは、名前が大文字で始まる場合、その名前はパッケージ外から参照できる「エクスポートされた名前」になります。TypeScriptでも同様にエクスポートの概念がありますが、違いは大文字小文字によってエクスポートが決まる点です。
Goでは、大文字で始まる Name フィールドがパッケージ外から参照可能です。一方で、name のように小文字で始まるフィールドはパッケージ内でしか参照できません。

```sample.ts
export class User {
  constructor(public name: string) {}
}
```

```sample.go
package user

type User struct {
    Name string
}
```

# 引数の型省略
Goでは、複数の引数が同じ型である場合、最後の型を残して他の型を省略できます。
これはTypeScriptにはない特有の機能です。

```sample.ts
function add(x: number, y: number): number {
  return x + y;
}
```
```sample.go
func add(x, y int) int {
    return x + y
}
```

# 複数の戻り値
Goでは、関数が複数の戻り値を返すことができます。TypeScriptではタプルを使って同様のことができますが、Goはこの機能が標準的です。
Goでは、複数の戻り値を括弧でまとめて返すことができます。
```sample.ts
function divide(x: number, y: number): [number, boolean] {
  if (y === 0) {
    return [0, false];
  }
  return [x / y, true];
}
```
```sample.go
func divide(x, y int) (int, bool) {
    if y == 0 {
        return 0, false
    }
    return x / y, true
}
```

# 名前付き戻り値
Goでは、関数の戻り値に名前を付けることができます。これにより、戻り値用の変数を事前に宣言しておき、関数内でその変数に値を割り当てるだけで済むようになります。
Goでは、fullName という名前付き戻り値を使って、return 文を簡潔にすることができます。
```sample.ts
function getFullName(firstName: string, lastName: string): string {
  return `${firstName} ${lastName}`;
}
```
```sample.go
func getFullName(firstName, lastName string) (fullName string) {
    fullName = firstName + " " + lastName
    return
}
```

# 短縮記法
Goでは := を使って暗黙的に型を推論しつつ変数を宣言できます。これはTypeScriptの let や const に似ていますが、Go独自の特徴として型推論が直接行われます。
どちらも変数の型を明示的に書かなくても、自動的に推論されます。
```sample.ts
let age = 30; // TypeScriptが自動的に型を推論する
```

```sample.go
age := 30 // Goが自動的に型を推論する
```

# 条件文での簡易ステートメント
Goでは、if 文の条件の前に簡単なステートメントを書くことができます。これはTypeScriptにはないGo特有の機能です。
Goでは、if 文の中で変数を宣言し、その変数を条件に使うことができます。
```sample.ts
let result = calculate();
if (result > 0) {
  console.log("Positive");
}
```
```sample.go
if result := calculate(); result > 0 {
    fmt.Println("Positive")
}
```

# defer
deferは、Goで特定の関数が終了する直前に実行するコードを予約するために使われます。
一方、TypeScript（JavaScript）には直接的なdeferはありませんが、tryを使えば似たようなことができます。
```sample.ts
function main() {
    console.log("Start");
    try {
        console.log("Doing something...");
    } finally {
        console.log("End");
    }
    console.log("This is deferred");
}

main();
```

```実行結果.txt
Start
Doing something...
End
This is deferred
```

```sample.go
package main

import (
    "fmt"
)

func main() {
    fmt.Println("Start")
    
    // defer ステートメントで最後に実行する処理を指定
    defer fmt.Println("This is deferred")
    
    fmt.Println("Doing something...")
    
    // 関数の最後で defer された処理が実行される
    fmt.Println("End")
}
```
```実行結果.txt
Start
Doing something...
End
This is deferred
```

# 終わりに
GoとTypeScriptの違いや共通点を理解することで、新しい言語の学習がよりスムーズになるはずです。
Goのシンプルで強力な構文を活用し、より効率的に開発を進められるようになっていただければ幸いです。
最後までお読みいただきありがとうございます！
paizaラーニングレベルアップ問題集の「名刺バインダー管理」を色々な言語でやってみた

paiza×Qiita記事投稿キャンペーンということで、paizaラーニングレベルアップ問題集の[名刺バインダー管理](https://paiza.jp/works/mondai/b_rank_skillcheck_archive/name_card)を色々な言語でやってみました。対象は[コードモンスター大図鑑](https://paiza.jp/code_monster)で対応している9言語にPerl、Scala、Go、Swiftを合わせた13言語です。

2024/08/25更新
Javascriptのコードを修正いたしました。
- - -
###### 方針
番号$m$の名刺は
```math
\left\lfloor\frac{m-1}{2n}\right\rfloor+1
```
枚目のファイルにあり、裏面の番号を$m'$とすると、
```math
m+m'=4n\left\lfloor\frac{m-1}{2n}\right\rfloor+2n+1
```
となります。答えは
```math
m'=2n\times\left(\left\lfloor\frac{m-1}{2n}\right\rfloor\times2+1\right)+1-m
```
です。
- - -
###### PHP
```php
<?php
	[$n, $m] = explode(" ", fgets(STDIN));
	echo (2 * $n * (intdiv($m - 1, 2 * $n) * 2 + 1) + 1 - $m) . "\n";
?>
```
- - -
###### Ruby
```rb
n, m = gets.split.map(&:to_i)
p 2 * n * ((m - 1) / (2 * n) * 2 + 1) + 1 - m
```
- - -
###### Java	
```java
import java.util.*;

public class Main {
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		int n = sc.nextInt();
		int m = sc.nextInt();
		sc.close();
		System.out.println(2 * n * ((m - 1) / (2 * n) * 2 + 1) + 1 - m);
	}
}
```
- - -
###### Python
```py
n, m = map(int, input().split())
print(2 * n * ((m - 1) // (2 * n) * 2 + 1) + 1 - m)
```
- - -
###### C言語
```c
#include <stdio.h>
int main(void){
	int n, m;
	scanf("%d %d", &n, &m);
	printf("%d\n", 2 * n * ((m - 1) / (2 * n) * 2 + 1) + 1 - m);
	return 0;
}
```
- - -
###### C#
```cs
using System;

class Program
{
	static void Main()
	{
		int[] nm = Array.ConvertAll(Console.ReadLine().Split(), int.Parse);
		int n = nm[0];
		int m = nm[1];
		Console.WriteLine(2 * n * ((m - 1) / (2 * n) * 2 + 1) + 1 - m);
	}
}
```
- - -
###### Javascript
```js
const lines = require("fs").readFileSync("/dev/stdin", "utf8").split("\n");
// 2024-08-25 parseIntからNumberに修正
// const [n, m] = lines[0].split(" ").map(x => parseInt(x, 10));
const [n, m] = lines[0].split(" ").map(Number);
console.log(2 * n * (Math.floor((m - 1) / (2 * n)) * 2 + 1) + 1 - m);
```
- - -
###### C++
```cpp
#include <iostream>
using namespace std;

int main(void){
	int n, m;
	cin >> n >> m;
	cout << 2 * n * ((m - 1) / (2 * n) * 2 + 1) + 1 - m << endl;
	return 0;
}
```
- - -
###### Kotlin
```kotlin
import java.util.*

fun main() {
	val sc = Scanner(System.`in`)
	val n = sc.nextInt()
	val m = sc.nextInt()
	sc.close()
	println(2 * n * ((m - 1) / (2 * n) * 2 + 1) + 1 - m)
}
```
- - -
###### Perl
```pl
my ($n, $m) = split ' ', <STDIN>;
print 2 * $n * (int(($m - 1) / (2 * $n)) * 2 + 1) + 1 - $m, "\n"
```
- - - 
###### Scala
```scala
import scala.io.StdIn._

object Main extends App {
	val Array(n, m) = readLine().split(" ").map(_.toInt)
	println(2 * n * (((m - 1) / (2 * n)) * 2 + 1) + 1 - m)
}
```
- - -
###### Go
```go
package main

import (
	"fmt"
)

func main() {
	var n, m int
	fmt.Scan(&n, &m)
	fmt.Println(2*n*((m-1)/(2*n)*2+1) + 1 - m)
}
```
- - -
###### Swift
```swift
import Foundation

let nm = readLine()!.split(separator: " ").map { Int($0)! }
let n = nm[0]
let m = nm[1]
print(2 * n * ((m - 1) / (2 * n) * 2 + 1) + 1 - m)
```
Tailwind CSSのtruncateとline-clampの違い

# truncate
truncateは1行のテキストがコンテナの幅を超える場合に、テキストを省略して...を表示します。
```html
<div class="w-[100px] truncate">
  文字の長さがwidthを超えると省略されます文字の長さがwidthを超えると省略されます
</div>
```
実際の表示
```
文字の長さがwidthを超えると省略されます文字の...
```

## line-clamp-1
line-clamp-1は1行のテキストがコンテナの幅を超える場合に、テキストを省略して...を表示します。
```html
<div class="w-[100px] line-clamp-1">
  文字の長さがwidthを超えると省略されます文字の長さがwidthを超えると省略されます
</div>
```
実際の表示
```
文字の長さがwidthを超えると省略されます文字の...
```

## 注意点
似たようなスタイルの設定ですが、注意点があります。
truncateは英単語や数字の羅列の場合も幅を超える場合省略されますが、
line-clamp-1は英単語や数字の羅列の場合省略されません。
```html
<p>truncate</p>
<div class="w-[100px] truncate">
  asdfasdfasdfasdfasdfasdfasdfasdfasdfasdf
</div>
<p>line-clamp-1</p>
<div class="w-[100px] line-clamp-1">
  asdfasdfasdfasdfasdfasdfasdfasdfasdfasdf
</div>
```
実際の表示
```
truncate
asdfasdfasdfasdfasdfasdfasdf...
line-clamp-1
asdfasdfasdfasdfasdfasdfasdfasdfasdfasdf
```
ALBeast について



https://www.miggo.io/resources/uncovering-auth-vulnerability-in-aws-alb-albeast

なるほど、↑の記事が詳しいが、ALBに関連づけられた IdP で認証をした後に、Idp を変更し、
トークンがリフレッシュされると変更先の IdP を issuer としてALBトークンを生成してしまうようである。

記事を読むまでは、JWTでよくある署名の検証をしていない場合にのみ攻撃を受けるのかなと思っていたがそうではないらしい。
issuer の項目は任意のものを挿入されてしまうようだが、 signer は偽造することができないようで、
signer にはALBトークンを生成した ALB の arn が入るようだ。
なので、想定された ALB のトークンであるかを検証するようにドキュメントが修正されたようだ。↓

![img](https://cdn.prod.website-files.com/661975c8be1890b945109820/66c38749a6a97199a93aff80_66c2473caad799fc32e42706_AD_4nXfIHi1SijYVat5CqKcr-wrnQo3HhdQ4MO5Us3rHyjLWCckxCYS7ND7ExU5OSGfGmGiyRAQYbCY_uS09uJBDkc8AWvt1QiL4uXQVGGnVri1i14yey-d-Gfr_l01cE0g1CmevIvaKbMV0WI5ZPyPEozaOrZ5u.png)
(冒頭の記事より)


また、これは ALB をバイパスして直接アプリにリクエストが飛ばせてしまう場合に影響を受けるようだ。
AWS になれている人にとっては当たり前かもしれないが、アプリが public subnet に存在する場合、
Security Group で 特定のALB からのみリクエストを受け付ける設定をしていないと、ALBを経由せずにアプリへアクセスできてしまう。
ALBがバイパスできてしまうと、任意のトークンを渡されてしまい、なりすまされてしまう可能性があるよということだろう。

なので、記事にもあるが、

1. JWT(x-amzn-oidc-data)の署名の検証に加えてsignerフィールドの検証を行う
2. アプリはALBからのみ通信を受け付けるようにする

ということに気をつけていれば良さそう。

気をつけるべき構成は以下のような条件1,2を両方満たす構成である場合だ

![スクリーンショット 2024-08-25 3.24.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/632997/fd0c58fc-50ac-8200-7163-61fe32db98cd.png)


てっきり、認証後に払い出される ALB のCookieが、他のALBでも使えてしまうとかそういう系のバグだと思っていたがそうではないようだ。思ったより深刻ではない気がする。

>ALBに関連づけられた IdP で認証をした後に、Idp を変更し、トークンがリフレッシュされると変更先の IdP を issuer としてALBトークンを生成してしまう

ただ、この挙動は良くないよねとは思う。
Phoenix Projectの作成からモーダル経由でのポストの作成と一覧への反映の処理の流れ part1

こんにちは、rickyです。
今回はelixirのPhoenixでプロジェクトを作成し、新規追加機能を使って一覧への情報反映までの一連の流れを解説していきます。
普段何気なく使用しているコマンドやどの関数が実行されているのかを詳細に書くことを目指しています。

まずはPhoenixプロジェクトの作成から行っていきます

```mix phx.new hogehoge```
これでPhoenixプロジェクトの作成を実行できます
このコマンドを実行した階層にて作成されます
オプションも色々ありますが、一番使うのは--no-ectoのEctoファイルを生成しないバージョンがおおいかと思います
Ectoファイルを生成しない単純構成のPhoenixプロジェクトを作成する場合があります

https://hexdocs.pm/phoenix/Mix.Tasks.Phx.New.html

次にデータベースの作成になります
```mix ecto.create```
作成したプロジェクトへ移動してから行います
このコマンドにより既存のデータベース内にテーブルを作成することができます
作成されるテーブルの情報についてはPhoenixプロジェクトの作成を行った際に生成されたSchemaを参考にしています
中身を覗いてみると
```elixir
  schema "posts" do
  field :title, :string
  field :body, :string
```
となっておりtitleとbodyがString型でフィールドとして扱われていることがわかります

https://hexdocs.pm/ecto/Mix.Tasks.Ecto.Create.html

ここまで行った段階でプロジェクトを動かしてみます
```mix phx.server```
か
```iex -S mix phx.server```
のコマンドを実行後ブラウザを開きlocalhost:4000にアクセスすることでプロジェクトを開けます

シンプルにサーバーを起動するだけなら上のコマンドを
iexに入りながらサーバーの起動を行えるようにしたい場合は下のコマンドを実行してください
単純な動作だけならmix phx.serverで十分ですが、データの内容をチェックしたりどんな変数が入っているのかを確認したい場合はiexでの起動が向いているかと思います

ここまででコマンドのミスがなく特殊な環境でない限りは動くはずです
なおここまでの話はプロジェクトを作成する際に以下のメッセージで表現されています

> We are almost there! The following steps are missing:
    $ cd hogehoge
Then configure your database in config/dev.exs and run:
    $ mix ecto.create
Start your Phoenix app with:
    $ mix phx.server
You can also run your app inside IEx (Interactive Elixir) as:
    $ iex -S mix phx.server

和訳すると
>もうすぐです! 以下の手順が抜けています:
$ cd hogehoge
次に、config/dev.exs でデータベースを構成して実行します:
$ mix ecto.create
次のコマンドで Phoenix アプリを起動します:
$ mix phx.server
次のように IEx (Interactive Elixir) 内でアプリを実行することもできます:
$ iex -S mix phx.server
Goのマイグレーションツールであるgooseでdockerのmysqlのマイグレーションをする方法

# はじめに
今回はGo言語の学習で、github版のgooseでのマイグレーションをどうすればいいのか調べてもイマイチいい情報がなかったのでメモ感覚で書きます

# 使っている環境
- golang 1.23
    - これはmacに直接ダウンロードしました。
- docker
    - mysql 8.0.34
- goose

# やりたい事
マイグレーションファイルを作ってgooseでdocker上にあるmysqlに対してマイグレーションを実行させたい

# やったこと
※golangのインストールやdockerでmysqlのコンテナを立ち上げる方法は割愛します(mysqlはmacに直接インストールしても問題ないです)

### gooseのインストール
下記コマンドでgooseをインストールする
```
go install github.com/pressly/goose/v3/cmd/goose@latest
```
その後whichコマンドで確認する

:::note warn
この時パスが通ってない場合はパスを通す必要あり
:::


```
which goose

## 何も表示されなかった場合は
 echo 'export GOPATH=$HOME/go' >> ~/.zshrc 
 echo 'export PATH=$PATH:$GOPATH/bin' >> ~/.zshrc 
 source ~/.zshrc  
 ```

 ### マイグレーションファイルの作成
 まずは「myproject/db/migrations」ディレクトリを作成する(myprojectは自分の好きな名称にしてください)
 ```
mkdir -p myproject/db/migrations
```
その後gooseのコマンドを使ってマイグレーションファイルを作ります(今回はcustomersというテーブルをマイグレーションでつくります)
```
cd myproject
goose -dir db/migrations create create_customers_table sql
```
実行が成功するとタイムスタンプ付きの「xxxxxxxxxxxxxxx_create_customers_table.sql」というファイルが「db/migrations」に作成されます
その作成されたファイルを下記のように書き換えます

:::note alert
作成されたファイルに
SELECT 'up SQL query';
SELECT 'down SQL query';
がデフォルトで入っていますが、これ消さないとマイグレーションがうまくいかないので消しましょう！
:::

```sql
-- +goose Up
-- +goose StatementBegin
CREATE TABLE `customers` (
    `customer_id` INT AUTO_INCREMENT PRIMARY KEY,
    `name` VARCHAR(255) NOT NULL,
    `password` VARCHAR(255) NOT NULL,
    `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    `updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
-- +goose StatementEnd

-- +goose Down
-- +goose StatementBegin
DROP TABLE IF EXISTS `customers`;
-- +goose StatementEnd
```

### マイグレーションの実行
gooseを使ってマイグレーションを走らせます

:::note warn
コマンド実行は「myproject」で実行するのでカレントディレクトリは「myproject」にしといてください！
:::

今回マイグレーションを走らせたいDB(mysql)の情報は下記のようになります
- ユーザー名: root
- パスワード: root
- ホスト: localhost
- ポート: 3333
- DB名: test_dev

```
 GOOSE_DRIVER=mysql GOOSE_DBSTRING="root:root@tcp(localhost:3333)/test_dev" goose -dir db/migrations up
 ```
実行が成功したらDBに「customers」というテーブルが新たに作成されます
 ![スクリーンショット 2024-08-24 3.24.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/146391/7b7829f1-22b5-5143-689a-56c3ea753ea8.png)

 マイグレーション実行後切り戻したい場合は下記のコマンドを実行します
 ```
 GOOSE_DBSTRING="root:root@tcp(localhost:3333)/test_dev" goose -dir db/migrations down
```
###

# 最後に
今回github版のgooseを使ったマイグレーションをしてみました
コマンド実行する際に接続先の指定と使うファイルの指定をどのようにすればいいのかイマイチ分からず苦労しました

ネットだとbibucket版のgooseを使った方法が多数ありますが2015年頃から更新が止まっており個人的にはあまりオススメしません(最新のPCで動かない場合もあるみたいです)
まだGo言語初心者なのでこれから少しずつ学習していきたいです。
初心者向け！fmt.Printfで覚えておきたいフォーマット指定のやり方

## fmt.Printfでよく使われるもの一覧
### 文字列
| verb | 内容 |
|:-:|:-:|
| %s | 文字列|
| %q | エスケープされた文字列 |

#### 例
```Go
var s1 = "文字列"

fmt.Printf("%s\n", s1) // 文字列

s2 := `バッククオートを使った、改行や
    タブが入った
        文字列`

fmt.Printf("%s\n", s2)  // そのまま表示
fmt.Printf("%q\n", s2)  // バッククオートを使った、改行や\n\tタブが入った\n\t\t文字列  
```

### 整数
| verb | 内容 |
|:-:|:-:|
| %d | 整数 |
| %b | 2進数 |
| %o | 8進数 |
| %x | 16進数(小文字のa-f) |
| %X | 16進数(大文字のA-F) |

#### 例
```Go
var i = 254
fmt.Printf("%d %b %o %x %X\n", i, i, i, i, i)  // 254 11111110 376 fe FE
```

### 小数点
| verb | 内容 |
|:-:|:-:|
| %f, %F | 小数点あり、指数なし、例:123.456 |
| %e | 指数標記、-1.234456e+78 |
| %E | 指数標記、-1.234456E+78 |

#### 例
```Go
var f = 3.14159265358
fmt.Printf("%f %F %e %E\n", f, f, f, f) // 3.141593 3.141593 3.141593e+00 3.141593E+00
```

### その他
| verb | 内容 |
|:-:|:-:|
| %v | デフォルト形式の値で出力 |
| %+v | 構造体のフィールド名も出力 |
| %#v | Goのリテラル表現で出力 |
| %T | 値の型情報を出力 |
| %% | 「%」そのものを出力 |

#### 例
```Go
fmt.Printf("v: %v  %v  %v\n", s1, i, f) // 文字列 254 3.14159265358
fmt.Printf("T: %T  %T  %T\n", s1, i, f) // string int float64
fmt.Printf("「%%%%」で「%%」をひとつ出力\n") // 「%%」で「%」をひとつ出力
```

### 間違って指定した場合
%!fなのように「!」が表示される
```Go
fmt.Printf("%%fでs1を指定: %f\n", s1) // %fでs1を指定: %!f(string=文字列)
```

## 参考文献
[公式ドキュメント](https://pkg.go.dev/fmt)
[初めてのGo](https://www.oreilly.co.jp/books/9784814400041/)
TypeScriptの型操作入門 keyof typeof の活用法

## typeof 型演算子とは

`typeof` は変数から型を抽出する型演算子です。

例として以下のようなオブジェクトがあったとします。

```ts
const person = {
  name: "John",
  age: 26,
};
```

上記オブジェクトに対して `typeof` を使った型は次のような型になります。

```ts
type Person = typeof person;
```

```ts
type Person = {
  name: string;
  age: number;
};
```

## keyof 型演算子とは

`keyof` はオブジェクトの型からプロパティ名を型として返す型演算子です。

例として以下のようなオブジェクトの型があったとします。

```ts
type Person = {
  name: string;
  age: number;
};
```

上記オブジェクトの型に対して `keyof` を使った型は次のような型になります。

```ts
type PersonKey = keyof Person;
```

```ts
type PersonKey = "name" | "age";
```

## keyof typeof を同時に使う

`keyof` と `typeof` を同時に使えば、オブジェクトのプロパティ名から型を抽出できます。

```ts
const Book = {
  title: "sample book",
  price: 1000,
  author: "Mike",
};
```

上記オブジェクトに対して `keyof typeof` を使った型は次のような型になります。

```ts
type BookKey = keyof typeof Book;
```

```ts
type BookKey = "title" | "price" | "author";
```

また、オブジェクトの末尾に `as const` を記述することで、オブジェクトの Value を型にすることができます。
(`as const` 記述することで、プロパティすべてを `readonly` で指定したものと同等の扱いになります。)

```ts
const Book = {
  title: "sample book",
  price: 1000,
  author: "Mike",
} as const;
```

上記オブジェクトに対して `BookValue` の型は次にようになります。

```ts
type BookKey = keyof typeof Book;
type BookValue = (typeof Book)[BookKey];
```

```ts
type BookValue = "sample book" | 1000 | "Mike";
```
文字列収集 (paizaランク S 相当) を普通にツリー構造で解く(JavaScript)

## 問題

https://qiita.com/official-events/9ab96aa95d62fe3cbdd7

これの

https://paiza.jp/works/mondai/real_event/word_collection

これです。
標題の通り、今回は(自分的には)普通にやります。


## 説明

入力の中の「文字列と価格」の行の部分、

```
paiza 16
pizza 1
paizaio 4
paizapoh 2
pizzaio 8
```

ここをどういうデータ構造で持つかという話ですね。
1文字づつ辿っていくツリー構造にしましょう。ノードは単純です。

```
Node {
  total: 自身と配下のノードの合計額,
  index: { a: Node, b: Node, ... }
}

indexの要素は存在するパターンのみ
```

上記入力を例にすると以下のような構造になります。

```
(※「total」「index」の表記は省略しています)
(※ルール的に空文字がなく、ルートのtotalは使われないので0のままにしてあります)

                  root
               { 0 ,{..} }
                   | p 
               { 31,{..} }
           | a              | i
      { 22,{..} }       { 9,{..} }
           | i              | z
      { 22,{..} }       { 9,{..} }
           | z              | z
      { 22,{..} }       { 9,{..} }
           | a              | a
      { 22,{..} }       { 9,{..} }
      | i       | p         | i
{ 4,{..} }  { 2,{..} }  { 8,{..} }
      | o       | o         | o
{ 4,{..} }  { 2,{..} }  { 8,{..} }
                | h
            { 2,{..} }
```

こういうのを「[トライ木(Wikipedia)](https://ja.wikipedia.org/wiki/%E3%83%88%E3%83%A9%E3%82%A4_(%E3%83%87%E3%83%BC%E3%82%BF%E6%A7%8B%E9%80%A0))」というらしいです。

探索する際は、
- 「paiza」を p a i z a と辿ると、そこに書いてある22が答え
- 「pizza」を p i z z a と辿ると、そこに書いてある9が答え 
- 「p」を p と辿ると、そこに書いてある31が答え
- 「pu」等、途中でノードが見つからなくなる場合は該当なしとして0

となります。

どうやって合計値を作っていくのかというと、
ツリーの構築時、ノードを辿る度に加算していくのですが
これはコードを見ていただいた方が早いかと思います。


## コード

```JavaScript:word_collection.js
// Copyright © 2024 https://qiita.com/tanin_no_sorani
// Released under the CC0 1.0 Universal license.
'use strict';

let N = null; // 文字列＋価格データの個数 (カウンタを兼任)

class Node { total = 0; index = {}; } // index: { a: Node, b: Node, ... }
const root = new Node(); // ツリー構造のルート

require('node:readline').createInterface({ input: process.stdin })
.on('line', line => {
	// 先頭行 文字列＋価格データの数 だけ使う
	if (N === null) { N = Number(line.split(' ')[0]); return; }
	
	let node = root; // 探索は常にルートから始める
	
	if (0 < N--) {
		// 登録
		const [word, price] = line.split(' '); // 文字列と価格
		
		// 1文字ずつ索引を作成
		for (const ch of word) {
			// 次のノードへ辿る、なければノードを新規生成する
			const next = node.index[ch];
			if (next) { node = next; } // 次のノードがあればそのまま進む
			else      { node = node.index[ch] = new Node(); } // なければ作成
			
			node.total += Number(price); // 通ったノードには自身の価格を加算
		}
		
	} else {
		// 購入
		
		// 1文字ずつ辿り、ノードが見つからなかったらその時点で抜ける
		for (const ch of line) { if (!(node = node.index[ch])) { break; } }
		
		// 最後までノードがあった場合はtotalが「必要な金額」、見つからなかった場合は0
		console.log(node ? node.total : 0);
	}
});
```

## 感想

配列アクセスの方が速いかな？と思って最初はindexを配列にしていましたが、
paizaのテストケースでは差はなかったので、文字のままObjectに突っ込む形にしました。
【SQL】COALESCE関数のすゝめ

こんにちは突然ですが
<b>CULUMN_01の値を取得、NULLだったら0として処理をしてください</b>
みなさんならどのように書きますか？

僕はCASE文を使ってこのように書いてました

```
SELECT
    CASE
        WHEN COLUMN_01 IS NULL THEN 0
        ELSE COLUMN_01
    END AS RESULT
;
```
これを見てもらったところCOALESCE関数で書いたらどうかと教えてもらい1行で書けるようになりました

```
SELECT
    COALESCE(COLUMN_01, 0) AS RESULT
;
```
<h1>COALESCE関数とは？</h1>
参照するカラムがNULLだったら置き換えてくれる関数です<br>
参照するカラムは複数指定できます<br>
COLUMN_01を取得、もしNULLだったらCOLUMN_02を取得、これもNULLだったらCOLUMN_03を取得、それでもNULLだったら指定した結果を表示させるといったことができます<br>
以下のように参照するカラムがすべてnullだったら0を表示するクエリを作成するとこのようになります

```
COALESCE(COLUMN_01,COLUMN_02,COLUMN_03,0) AS RESULT
```
<b>元データ</b>
|COLUMN_01|COLUMN_02|COLUMN_03|
|:--|:--|:--|
|3|4|5|
|null|1|3|
|null|null|7|
|null|null|null|

<b>結果</b>
|RESULT|
|:--|
|3|
|1|
|7|
|0|

<h1>さいごに</h1>
先ほどの例ですが一番初めに僕がやっていたようにCASE文を使って書くこともできます<br>
COALESCEで書くメリットは

- 行数、文字数が少なくて済む
- クエリを見直すときにCOALESCEが見えた時点でNULLだったら別のもので置き換える処理だとわかりやすくなる

この2つかなと思ってます
NULLの置き換え処理をすることがあったらぜひ一度使ってみてください

読んでいただきありがとうございました！
クソ雑魚のワイ、Symfonyのコードリーディングしてみた②

前回の記事に続きまして、Symfonyのコードリーディングを続けていきます。
以下は前回の記事です。

https://qiita.com/JonyTask/items/ce96c4afafd409d2b4ef

## Requestクラスについて
本記事で主に進めていく箇所は、以下のパスです。
./src/Symfony/Component/HttpFoundation/Request.php

### クロージャについて
このクラスのコードを読んでいくと、おそらくcreateRequestFromFacoryという関数が重要であることが分かります。

複数の関数において返されるインスタンスはこの静的関数を用いて作成されているようです。
この関数は以下のコードです。

```
private static function createRequestFromFactory(array $query = [], array $request = [], array $attributes = [], array $cookies = [], array $files = [], array $server = [], $content = null): static
{
    if (self::$requestFactory) {
        $request = (self::$requestFactory)($query, $request, $attributes, $cookies, $files, $server, $content);

        if (!$request instanceof self) {
                throw new \LogicException('The Request factory must return an instance of Symfony\Component\HttpFoundation\Request.');
        }

        return $request;
    }

    return new static($query, $request, $attributes, $cookies, $files, $server, $content);
}
```

簡単にご説明すると、まず初めに以下の要素が引数として渡されてきます。
1. クエリ
1. HTTPメソッド
1. attributes 何これ？
1. クッキー
1. ファイル
1. サーバ
1. リクエスト内容

これらを元にインスタンスを作成し、返すわけですがこの関数の中で一点、不思議な点があります。
```
self::$requestFactory
```
こちらです。
静的に宣言されたrequestFactoryプロパティを呼び出しているのは分かるのですが、その宣言部分はこちらです。
```
 protected static ?\Closure $requestFactory = null;
```
__いやいや、何もしてへんやん。__
クソ雑魚のワイもこのように思うわけです。

こちらの意味は、「$requestFactoryの型はクロージャ型、もしくはnullでもいいよ。でも初期値はnullね」という意味です。
よって、クラス内でクロージャを設定することができる箇所が考えられます。
探してみると、以下のようなコードがありました。
```
 public static function setFactory(?callable $callable): void
{
    self::$requestFactory = null === $callable ? null : $callable(...);
}
```
こちらでクロージャを設定する時もあるというわけですね。
callable型というのはコールバック関数に型づけるというわけです。
処理の内容としては、「コールバック関数が引数に渡されていないならrequestFactoryプロパティはnullで、あるなら引数のコールバック関数をrequestFactoryプロパティに定義しようね」という意味です。

このクラス内にはsetFactoryを呼び出している箇所はありませんでした。
外部から呼び出し、クロージャを設定、Requestインスタンスを作る際にそのクロージャを使用する、といったイメージでしょうか。難しいって。

このクロージャやコールバック関数について正しく理解することは、ワイのようなクソ雑魚にとっては1つの壁であるような気がします。

他にも関数がたくさんあるのですが、うーん、何から読めばいいのやら。
この記事自体は短くなりましたが、次にある程度読むべき場所を精査してから記事を執筆します。
Raspberry Pi5でC言語使ってLチカ

Raspberry Pi5からGPIO接続について一部変更点があり、4で動いてたコードがそのまま使えないケースが発生しました。

<h1>動いたサンプルコード</h1>
<pre>
<code>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <gpiod.h>

#define CONSUMER "LED"

int main(int argc, char **argv)
{
    const char *chipname = "gpiochip4";
    struct gpiod_chip *chip;
    struct gpiod_line *line;
    int ret;

    // GPIOチップを開く
    chip = gpiod_chip_open_by_name(chipname);
    if (!chip) {
        perror("gpiod_chip_open");
        exit(EXIT_FAILURE);
    }

    // GPIOラインを取得する (BCM 17)
    line = gpiod_chip_get_line(chip, 17);  // ここが物理ピン11に対応
    if (!line) {
        perror("gpiod_chip_get_line");
        gpiod_chip_close(chip);
        exit(EXIT_FAILURE);
    }

    // GPIOラインを出力としてリクエストする
    ret = gpiod_line_request_output(line, CONSUMER, 0);
    if (ret < 0) {
        perror("gpiod_line_request_output");
        gpiod_chip_close(chip);
        exit(EXIT_FAILURE);
    }

    // LEDの点滅
    for (;;) {
        gpiod_line_set_value(line, 1); // LEDをオン
        printf("LED ON\n");
        usleep(100000);                // 100ms待機
        gpiod_line_set_value(line, 0); // LEDをオフ
        printf("LED OFF\n");
        usleep(100000);                // 100ms待機
    }

    // 解放処理
    gpiod_chip_close(chip);
    return EXIT_SUCCESS;
}
</code>
</pre>

<h1>確認すべき点</h1>
Raspberry Piは、複数のGPIOチップがいくつかのGPIOピンをまとめて管理しています。刺そうと思っているピンに応じて*chipname = "gpiochip?"(?には適切なチップ番号を入れる。今回使用した17番ピンはチップ番号4で動きました。)を設定する必要があります。
[ほぼC] C#で簡単に分かりやすく「Fizz Bazz」を解いてみました! [paiza]

# 目的
C#ってなんか難しそう...って人のために、こんな感じでも動くんだよ～というのを示して敷居を下げる目的で記事を書きました。

# コード
こちらが正解をもらったコードになります。
~~~C#:Program.cs
using System;

class Program
{
    static void Main()
    {
        var upper = Console.ReadLine();
        
        if(int.TryParse(upper, out var naturalNum)){
            for(int i = 1; i <= naturalNum; i++){
                if(i % 3 == 0 && i % 5 == 0){
                    Console.WriteLine("Fizz Buzz");
                }
                else if(i % 3 == 0){
                    Console.WriteLine("Fizz");
                }
                else if(i % 5 == 0){
                    Console.WriteLine("Buzz");
                }
                else{
                    Console.WriteLine(i);
                }
            }
        }
        else{
            Console.WriteLine("整数でないものが入力されました。");
        }

        
    }
}
~~~
見て頂ければわかると思いますが、TryParseメソッド(関数)以外は、Cが分かれば理解できるようになっております。(Console.WriteLine()はCでいうprintf()、Console.ReadLine()はCでいうscanf()と機能的には同じです!)

# おまじない
プログラムを見てみると、「using System」や「class Program」のような見慣れないものがありますが、1つの機能を作るならこれらは「おまじない」として無視してしまっても大丈夫です。
とりあえず、static void Main(){}の{}内にプログラムを書けばよいと思ってください。

# TryParseについて
さて、「おまじない」を無視したところで、なにやら「TryParse」という謎の文がありますね。
ここら一帯で何をやっているかということについてですが、簡単に言うと、__入力された値が正常であるかということを調べ、正常ならば型変換を、そうでなければ例外処理をしています。__
構文は以下の通りです。
~~~c#:int.Tryparse.cs
int.TryParse([string s], out [int result]);
~~~
この関数は値が正常であるかどうかを調べ、以上なら0(false)を返してくれます。if文の中身はCと同じで中身がTrueのときに{}内が実行されるため、この構文を()内に入れてあげれば、ifとelseだけで例外を簡単に弾くことができます。

(ちなみに、Parseメソッドで例外をキャッチする方法もありますが、コードを書くのも面倒かつ処理コストが重くなるため、推奨しません)

こういった例外処理を早めに習得しておくことで、後々楽になるかもしれませんよ!
# まとめ
C#、できることが結構多いので最初は混乱しがちですが、Cで実装可能な機能は基本的に大体実装できるので、良かったら触れてみてはいかがでしょうか(ポインタを除く)
以上です。

参考:

https://learn.microsoft.com/ja-jp/dotnet/standard/design-guidelines/names-of-classes-structs-and-interfaces

https://www.tairax.com/entry/study/Csharp/TryParse
6キー片手キーボード その後

https://qiita.com/nak435/items/389f62a41efc72c804a8

↑ 以前、こちらの記事で紹介した「6キー片手キーボード」だが、<b>”Macがスリープから復帰したとき”</b>は、キーを押しても反応しない現象が起きていた[^1]。

[^1]: 購入当初からの事象。原因は追及していない

> USB2.0ハブ経由でMacと有線接続している場合の話で、Bluetoothや2.4GHzドングル接続の場合は調べていない。また、WindowsPCの場合も調べていない。

このため、スリープから復帰した直後は、一度、USBケーブルの抜き差し[^3]をしていたが、先日、偶然にも 以下の事実が判明した。

[^3]: USBケーブルの抜き差しも面倒なため、USBケーブルに（押すと電源断となる）3Pスイッチを取り付けて、キーが反応しないとは スイッチを押していた

<i><b>Macがスリープから復帰したとき、キーを押しても反応しないが、その状態から、Macからキー情報を読み出してやると、その後はキーを押すとちゃんと反応するようになった。</b></i>

と言うことで、↓こちらの記事を参考に、Macに`sleepwatcher`をインストールし、スリープから復帰時に、デバイス情報を読み出す[^4]ことで、これまでの煩わしい操作（ケーブル抜き差し、電源切り入り）が不要になった。

https://qiita.com/fiftystorm36/items/5fe936a92445cbf4ad9a

[^4]: [デバイスから設定情報を読み出す（その2）](https://qiita.com/nak435/items/389f62a41efc72c804a8#%E3%83%87%E3%83%90%E3%82%A4%E3%82%B9%E3%81%8B%E3%82%89%E8%A8%AD%E5%AE%9A%E6%83%85%E5%A0%B1%E3%82%92%E8%AA%AD%E3%81%BF%E5%87%BA%E3%81%99%E3%81%9D%E3%81%AE2)
Chrome で Gemini Nano を試すためのハードウェア要件

Chrome からローカルで Gemini Nano を呼び出せるようになりましたが、利用するにはいくつかのハードウェア要件を満たす必要があります。手順通りにやっているのに `chrome://components` で `optimization guide on device model` が見つからない場合、これらの要件を満たしていない可能性があります。

以下にハードウェア要件を和訳してまとめてみたので確認してみてください。
ソース: https://docs.google.com/document/d/1VG8HIyz361zGduWgNG7R_R8Xkv0OOJ8b5C9QKeCjU0c/edit?pli=1


### 対応 OS

* Windows 10, 11
* macOS 13 (Ventura) 以上
* Linux (バージョン指定なし)


### ストレージ

* Chrome プロファイルを含むボリュームに **22 GB 以上の空き容量** が必要
    * モデル自体はもっと少ない容量で動作するが、余裕を持たせている
    * ダウンロード後に空き容量が **10 GB を下回るとモデルが削除される**


### GPU

* 内蔵GPU またはディスクリートGPU (ビデオカード) が必要
* **VRAM は 4 GB 以上** 必要


### ネットワーク接続

* 非従量課金制
[RSpec]createメソッドからブロック構文で囲って追いデータ作るやつ備忘録

# 概要
letメソッドで定義した`user_...`の変数だけ使いたい場合、関連オブジェクトの`favorite_...`の定義がちょっと無駄だったりする。
```ruby
let(:user_1) { create(:user, name: "ハム太郎") }
let(:favorite_1) { create(:favorite, user: user_1, name: "ヒマワリのタネ") }

let(:user_2) { create(:user, name: "まいどくん") }
let(:favorite_2) { create(:favorite, user: user_2, name: "おそうじ") }
let(:favorite_3) { create(:favorite, user: user_2, name: "お金もうけ") }

let(:user_3) { create(:user, name: "トラハムくん") }
let(:favorite_4) { create(:favorite, user: user_3, name: "スケボー") }
let(:favorite_5) { create(:favorite, user: user_3, name: "歌") }
let(:favorite_6) { create(:favorite, user: user_3, name: "ダンス") }
```

# 対応
## 基本
createメソッドでブロック構文を囲み関連オブジェクトのcreateを羅列することでスッキリ書く事ができる。

```ruby
let(:user_1) {
  create(:user, name: "ハム太郎") do |user|
    create(:favorite, user:, name: "ヒマワリのタネ")
  end
}

let(:user_2) {
  create(:user, name: "まいどくん") do |user|
    create(:favorite, user:, name: "おそうじ")
    create(:favorite, user:, name: "お金もうけ")
  end
}

let(:user_3) {
  create(:user, name: "トラハムくん") do |user|
    create(:favorite, user:, name: "スケボー")
    create(:favorite, user:, name: "歌")
    create(:favorite, user:, name: "ダンス")
  end
}
```

## 応用
create_listメソッドでブロック構文を囲むと関連オブジェクト毎に何かできるようになる。
```ruby
let(:user_4) {
  create(:user, name: "ヨクバリス") do |user|
    create_list(:favorite, 108, user:) do |favorite|
      # TODO: favoriteの数だけ何かできるかも？
    end
  end
}
```
curl: (7) Failed to connect to localhost port 8080 after 0 ms: Couldn't connect to serverが生じる

<h2>挨拶・経緯</h2>
こんにちは。初投稿です。<br>
すごく簡単な勘違いで15分ほど時間を費やしてしまったので、同じ問題で詰まってしまった人のためになる記事を書けたらいいな、と思い作成しました。<br>
Goを学んでいる最中、localhost:8080を起動したは良いものの、curlコマンドを使用するタイミングがあったのですが、

    curl http://localhost:8080/hello

の実行結果が、

    
    curl: (7) Failed to connect to localhost port 8080 after 0 ms: Couldn't connect to server
    
と出てしまっていました。

<h2>解決方法</h2>
まず私のPCはMacのARM64になります。<br>
GoをVSCodeで書いている時に、上記のエラーが発生しました。<br>
しかし、こちらのnetstatコマンドでポートの状況を確認しました。<br>
一度、ターミナルを開き、<br>

    netstat -tlpen | grep 8080

すると、<br>

    netstat: en: unknown or uninstrumented protocol

と返ってきました。<br>
この時に、もしかしてlocalhost:8080を立ち上げたままcurlを使用しなければならなかったのでは…？と思い、別のターミナルにて、<br>

    go run main.go

と起動。<br>

また元のターミナルに戻って

    curl http://localhost:8080/hello

実行結果が、

    Hello, world!

となりました。

<h2>最後に</h2>
本当に簡単なことで申し訳ないと思ったのですが、日本語の記事には無く、海外の掲示板を参考にしたところ解決できたので、念のため情報共有までに。


閲覧いただきありがとうございました。

<br>


大学でプログラミングを学んでいます。もし良かったら話しかけて欲しいです！☺️
includesメソッドについて

## 初めに

疑問に思ったことや上手くいかなかったことのアウトプットをしています。
自分なりの理解でアウトプットしていきます。初学者なので誤りもあると思います。
その際はご指摘いただけると幸いです。

## どういった時に使用するメソッドか
主に一覧などを作成する際、アクションに定義する。
関連するデータを一度のクエリで効率的に取得できる。

## 具体的には
```
@object = object.includes(:example)
```
objectを取得と同時にそれに関連するexampleも一緒にロードされる。
これによってN+1問題が解消されるので、効率的に関連データを取得できる。
zshからbashにあえて乗り換えた話

# きっかけ
今までbash -> zsh -> fish -> zshと使ってきて色々不満点がありました。

+ fish
    + メリット
        + カスタマイズ楽すぎる
        + 標準で十分すぎる補完が効く
            + npmコマンドの補完なども自動で効く
        + 高速
    + デメリット
        + いわずもがな文法が...
+ zsh
    + メリット
        + カスタマイズ性はかなり高い
        + POSIX準拠
    + デメリット
        + カスマイズがだるい
        + 遅い
        + ワイルドカードが動かないときがある（自分のせいの可能性もある）

という感じでbashの乗り換えも検討していました。

# 達成したいこと
+ warpなどの高機能ターミナルエミュレータの力を借りずに基本的にbashだけで機能させる。

+ Tabを補完をzshほど強く効かせることを目標にする。
+ zsh-syntaxhighlighting, zsh-autosuggestionなどの機能を使用できるようにする。

+ gitの表示

# 導入

## 使用したツール

+ Ble.sh
    + 簡単に言うとbashをzshのように使いやすくしたカスタマイズされたshellです。Tab補完もハイライトなど自動でされます。

+ bash-it
    + プラグインマネージャでcompletionなどの補完を楽に管理することができるツール。


## インストール
+ Ble.sh

https://github.com/akinomyoga/ble.sh

```bash:bash
git clone --recursive --depth 1 --shallow-submodules https://github.com/akinomyoga/ble.sh.git
make -C ble.sh install PREFIX=~/.local
echo 'source ~/.local/share/blesh/ble.sh' >> ~/.bashrc
```

+ bash-it

https://github.com/Bash-it/bash-it

```bash:bash
git clone --depth=1 https://github.com/Bash-it/bash-it.git ~/.bash_it
~/.bash_it/install.sh
```

## カスタマイズ

### bash-it
エイリアス(alias)が標準で整備されています。導入するだけでエイリアスを入れることができますが、私は自分で設定しているので導入をパスしました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2729508/0edeb7dd-f432-ca9f-d111-1b89a5ea7cf6.png)


bash-itのプラグイン導入
これが非常に便利でした。使いたいプラグインを選ぶだけです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2729508/0d49249d-df19-91d1-e6b2-059ccc33b73a.png)

```bash:bash
bash-it enable plugin git sudo ssh
```

completionの導入
使いたいツールを選んで同様に入れるだけです

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2729508/bda0b28a-f257-74bb-dee3-3d635c115f9b.png)

```:bash
bash-it enable completion docker docker-compose ssh yarn
```

これでzshに負けないくらいの補完がつきます。

### .bashrcのカスタマイズ

**特に効果が高いものをピックアップします**

+ 補完で大文字小文字を無視(大文字でも小文字でTab補完有効)

```sh:.bashrc
bind 'set completion-ignore-case on'
```

+ Tabでメニューを表示して選択する
```sh:.bashrc
bind 'TAB:menu-complete'
bind 'set show-all-if-ambiguous on'
```

### pnpm補完

https://pnpm.io/completion

ここにもありますが、bashでもバシバシ効かせます。

```sh:bash
pnpm completion bash > ~/completion-for-pnpm.bash
echo 'source ~/completion-for-pnpm.bash' >> ~/.bashrc
```
## 挙動

zshやfishのようにオプションの補完やコマンド有無によるハイライト、suggesitionなどもしっかり機能するようになりました。
また、gitブランチも表示されて非常に快適です。
warpと組み合わせればより高機能になりますが、ほとんどbashだけで動作しているので当初の目標は達成できました。
zsh, fishを使用の方も是非参考にしてください。

![bash.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2729508/30234e3d-4754-93e2-796d-5bd461263bcb.gif)


以上です。お疲れ様でした。
また何かあれば追記します。