OmniAICreator/Qiita-1.07M huggingface dataset:
Sassを触ってみた

どうも、豆腐メンタルです。

こちらの続きです
https://qiita.com/mortyie/items/05a76ec9ca656110c4e3

今回は、過去に作ったオリジナルアプリのRemake一発目。
デザイン面を一新すると共に、現在CSSで書かれているものをSassに置き換えていきたいと思います。

## CSSとSassの違いって？
HTMLが使われていると、一般的にはCSSがセットで付いてきますが
今回使おうとしているSassとは何か？CSSとは何が違うか？

どちらも共通しているのは、Webページのデザインやフォントを設定するものであるのは変わらないのですが

CSSだとコーディングをして行く上で、どうしてもコードが複雑になりやすく、後々修正する際にメンテナンスが大変になります。

それに対して、SassはCSSの機能を拡張したもので変数やネストを扱うことでコードのメンテナンスが簡単になるメリットがあります。

では、具体的に中身を見ていきましょう

## 事前準備
HTML
```html
<body>
  <!-- ヘッダー -->
    <div class="header-btns-wrapper">
      <button class="header-btn">画像</button>
      <button class="header-btn">新規登録</button>
      <button class="header-btn">ログイン</button>
    </div>
</body>
```
CSS
```css
.header-btns-wrapper{
  background-color: #ffff4c;
  width: 100%;
  height: 70px;
  display: flex;
  align-items: center;
  justify-content: space-around;
}

.header-btn{
  background-color: white;
  color: black;
  width: 100px;
  height: 50px;
  text-align: center;
  font-weight: 600;
}
```
このように表示されてます(画像はどこかへいきました)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3020655/33702f57-0681-b594-204f-748c3efb244f.png)

**sassファイルの方ですが、こちらはまだCSSとなっているので後ほどコンパイルしてCSSに変換します。**


今回やりたいこととしては、以下のコードは何度も使うことになりそうなのでメンテナンスをやりやすくするためにSassの記述をします。

```css
  background-color: white;
  color: black;
```

ファイルの上の方でSassの変数を宣言しましょう

```sass
$cWhite: White;
$cBlack: Black;
```

こうすることで、$cWhite,$cBlackという変数の中に
それぞれの色のプロパティを入れることができました。

そして、以下のように記述。

```sass
$cWhite: White;
$cBlack: Black;

.header-btns-wrapper{
  background-color: #ffff4c;
  width: 100%;
  height: 70px;
  display: flex;
  align-items: center;
  justify-content: space-around;
}

.header-btn{
  background-color: $cWhite;
  color: $cBlack;
  width: 100px;
  height: 50px;
  text-align: center;
  font-weight: 600;
}
```
以上が記述できたら、SassをCSSに変換しましょう。

自分はVScodeを使っているので、拡張機能を実施。
「Live Sass compiler」を実行。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3020655/e7d5d001-ef32-fb31-79c3-902ee7df9793.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3020655/a8aeb075-7988-d442-0f29-9e68de0017d2.png)


すると、cssが変換されるのでこちらを読み込ませるようにすると...?
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3020655/ff385547-98be-9be1-e248-e26c67383423.png)

デザインは変わらずに、実行されました。
上手くいきましたね。

## 終わりに

このように、Sassを使うことで変数内でプロパティを管理できるようになります。

今回はやらなかったのですが、Sassの記述法によって
HTML内のデザインを部位ごとに指定して、変更することもできるようですが...それはまたいつか...

閲覧、ありがとうございました
github workflowの実行履歴を全部削除する

# Github workflowsの実行履歴を全部削除

## やりたいこと
workflow runsにある実行を全て削除したいです。


<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/443312/b95e7199-cb28-0b58-c136-907c2c4f6efd.png" width="400">

削除後
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/443312/53ad813f-c464-a1ca-53ef-964ca0058e9b.png" width="400">


version更新でgh apiのEndpointが変更されたためか、CLIコマンドの変更のためか、以前の書き方では削除できなくなってました。

この記事はその備忘録です。


## 以前の書き方
バージョン
```
% gh version                
gh version 2.55.0 (2024-08-20)
https://github.com/cli/cli/releases/tag/v2.55.0
```

```
gh api "repos/${owner}/${repo}/actions/workflows/${file_name}/runs?per_page=100" \
| jq -r '.workflow_runs[].id' \
| xargs -P4 -I{} gh api repos/${owner}/${repo}/actions/runs/{} -X DELETE
```

## 2024-09-16時点

```
% gh version
gh version 2.57.0 (2024-09-16)
https://github.com/cli/cli/releases/tag/v2.57.0
```

```
gh api repos/${owner}/${repo}/actions/runs | jq '.workflow_runs[] | .id' | xargs -P4 -I{} gh api --method DELETE repos/Kynea0b/github-workflow-sample1/actions/runs/{}
```
MySQLリストア 実行エラーが出ていないのにデータが空のまま

# 概要

初歩的なミスで行き詰まってしまったので共有します。  

``` bash
mysql -u root -p restore < example.dump
```

上記のようにmysqlコマンドを使用し、ダンプファイルからデータをインポートしたが、データが入ってこない。

## 実行した手順

リストア用のデータベースを新規作成

``` bash
mysql -u root -p -e "CREATE DATABASE restore;"
```

dumpファイルを実行
``` bash
mysql -u root -p restore < example.dump
```

実行後、エラーはではなかったが、phpmyadminで確認してみるとデータが空のままである。  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3814957/3daaf4a0-a335-7741-f35d-77a7bd7bed8d.png)

## 結論

dumpファイル内で、異なるデータベースを参照するようになっていた。

``` example.dump
CREATE DATABASE /*!32312 IF NOT EXISTS*/ `another_database` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci */ /*!80016 DEFAULT ENCRYPTION='N' */;

USE `another_database`;
```

中身を見てみると、設定の中でUSEするテーブルを変更していた。  
mysqlコマンドで実行先を指定していれば絶対にそのデータベースへ入ると思っていたので、**dumpファイル内で実行先を変更できることは盲点だった**。  

以下のように書き換えれば解決である。  

``` example.dump
CREATE DATABASE /*!32312 IF NOT EXISTS*/ `restore` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci */ /*!80016 DEFAULT ENCRYPTION='N' */;

USE `restore`;
```

幸い事故には至らなかったが、実行先のファイルが稼働中だったら危なかったので、皆様も気を付けてほしい。
HoloLens2が故障？電源がつかない。

# はじめに
HoloLens2を使っていて、電源がつかなくなり、何も反応がなくなりました。
原因と解決策について記載します。

# 事の発端
HoloLensのカメラが開発作業において、使えなくなり、初期化しようとしていました。

リセットし、通常なら数分後には再起動し、「ようこそ」と画面に表示されるはずですが、
私の場合は30分程度待っても何も表示されませんでした．

# 解決方法
Microsoft Storeにある「Advanced Recovery Companion」を利用して，修復できました．
https://apps.microsoft.com/detail/9p74z35sfrs8?hl=ja-jp&gl=US

恐らく，初期化した後すぐに電源ボタン長押しを何回も行ったことが原因だと思います．

同じような状況の方に役立つと嬉しいです．

参考になるサイト（https://learn.microsoft.com/ja-jp/hololens/hololens-recovery）
Amazon EC2についてまとめ

## はじめに
Amazon EC2(Amazon Elastic Compute Cloud)はAWS上で動作する仮想サーバーを提供するサービスです。「インスタンス」という単位でサーバーが管理されます。
インスタンスの個数や性能を柔軟に変更できるため、コストの削減や時間短縮が実現できます。

## EC2インスタンス作成の流れ
### 1.AMI(Amazon Machine Image)の選択
AMIはEC2インスタンスを起動するために必要なOSやミドルウェアなどのテンプレートです。
Amazon Linux AMIやMicrosoft Windows ServerといったAWSが標準で提供しているものや、各ベンダーがサービスをプリインストールしたものがあります。
EC2 Image Builder(EC2およびオンプレミスに展開可能な仮想マシンイメージの作成・管理ができるサービス)を利用する方法もあります。EC2 Image Builderを利用することでOSのパッチ当てなどの運用作業とAMI化といった一連の作業を自動化できます。
選択したAMIを元にEC2インスタンスが作成されます。AMIに含まれないサーバー固有のOSの設定や、ミドルウェアの導入は個別に実施する必要があります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3899061/71218cde-73d4-35df-f107-1c9b116d93dc.png)

### 2.インスタンスタイプの選択
インスタンスタイプは、インスタンス作成時に選択できるCPU、メモリ、ストレージ性能、ネットワークキャパシティーの組み合わせです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3899061/d4927eb8-c4c2-9fba-4894-45807f580bb1.png)

インスタンスタイプの名前は、インスタンスファミリー、インスタンスの世代、インスタンスサイズで構成されています。

#### インスタンスファミリー
**m:** 汎用
**t:** 汎用(CPUバースト機能)
**c:** コンピューティング最適化
**r、x:** メモリ最適化
**p、g、f:** 高速コンピューティング
**i、d、h:** ストレージ最適化

### 3.キーペアの設定

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3899061/71d05c37-9689-7970-e729-76c2c67d165d.png)

### 4.ネットワークの設定

### 5.ストレージの設定
* Amazon EBS(Elastic Block Store)
EC2インスタンスに割り当てられるブロックストレージで、物理ハードディスクドライブと同じように利用できます。
* インスタンスストア
EC2インスタンスの内部ストレージとして利用できるブロックストレージです。インスタンスを停止するとデータが消失します。高い費用対効果、IOPSがあります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3899061/9c788848-c420-2411-fee4-6fd17e777cc8.png)

### 6.高度な設定
プレイスメントグループやメタデータなどを設定できます。


## 起動できました！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3899061/84d29a3f-267c-3a8c-2e83-28ac0406337c.png)
Next.js の Client Components で metadata を動的に生成する

## やりたいこと

Next.js の `metadata` は、通常 `layout.tsx` や `page.tsx` 内にて、以下のように生成する。

```tsx
import { Metadata } from 'next'

// 静的
export const metadata: Metadata = {
  title: '...',
}

// 動的
export async function generateMetadata({ params }) {
  return {
    title: '...',
  }
}
```

via. [Metadata Object and generateMetadata Options](https://langxlang.com/ja/javascript/next.js/v14/02-app/02-api-reference/04-functions/generate-metadata)

ただし、

> `metadata` の object と `generateMetadata` 関数の export は `Server Components` でのみサポートされています。

とあるように、`Client Components` では使えない。困る。なんとかしたい。

解決策は、悩むような内容ではないが、何度も悩んでいるような気がするので備忘のため。

## あっさり解決策

title タグを return すれば良い。おしまい。

```page.tsx
'use client';

export function page({ params }: Props) {
  const dynamicTitle = xxxx();
  return (
    <>
      <title>{dynamicTitle}</title>
      <main>
        your main contents
      </main>
    </>
  );
}
```

## 具体例

### 元となる Server Component なコード

```tsx:src/app/something/[id]/page.tsx
import { metadata } from 'next';
import { getSomethingById } from '@/server/actions'

type Props = {
  params: {
    id: string;
  };
};

export async function generateMetadata({ params }: Props): Promise<Metadata> {
  const something = await getSomethingById(params.id);
  return {
    title: something.title,
    ...
  }
}

export default async function page({ params }: Props ) {
  const something = await getSomethingById(params.id);

  return (
    <main>
      your main contents
    </main>
  )
}
```

例えば、こんなものがあったとする。これは `Server Component` なので何の問題もないのだが、クリックしたらモダルを表示したいとか、何らかの理由で、`Client Component` にする必要がある場合もよくある。

### Client Component 化する

```tsx:src/app/something/[id]/page.tsx
'use client';
import { useEffect, useState } from 'react';
import { getSomethingById } from '@/server/actions'

type Props = {
  params: {
    id: string;
  };
};

export default function page({ params }: Props ) {
  const [something, setSomething] = useState<DataType | null>(null);

  useEffect(() => {
    async function fetchData() {
      const data = await getSomethingById(params.id);
      setSomething(data);
    }
    fetchData();
  }, [params.id]);

  // other client logic here.

  if (something) {
    return (
      <>
        <title>{something.title}</title>
        <main>
          your main contents
        </main>
      </>
    );
  } else {
    return <div>Loading...</div>;
  }
}
```

すごく大雑把だが、データを取得する際の `async/await` は、`useEffect` 内で行い、state にぶち込む。この「あれ？await できないじゃん」と合わせて、よく悩んでるような気がする。

おわり。
【小ネタ】簡単なClipboardの機能を実装する

# 1. 概要
ちょっとした小ネタなので忘備録も兼ねて短めに内容をまとめます。

今回実装しているプロジェクトで、クリップボードを実装する要件があったので実装してみました。あまりNext.jsにおいてクリップボードを組み込む記事がないように感じたので、今回はUI面も含めてクリップボードの作り方を紹介しようと思います。

# 2. Clipboard

早速コードを紹介しようと思います。
UIライブラリーはchakra-uiを使用していて、アイコンはreact iconsを使用しています。

```tsx
"use client";

import { Center, Tooltip } from "@chakra-ui/react";
import { useState } from "react";
import { FaCheck } from "react-icons/fa6";
import { MdErrorOutline, MdOutlineCopyAll } from "react-icons/md";

export default function ClipBoard({ value }:{ value:string }){
  const [isCopied, setIsCopied] = useState(false);
  const [err, setErr] = useState(false);

  async function handleCopy(){
    try{
      setErr(false);
      await navigator.clipboard.writeText(value);
      setIsCopied(true);

      await new Promise(resolve => setTimeout(resolve, 5000));
      setIsCopied(false);
    }catch(err){
      setErr(true);
    }
  }
  return(
    <Tooltip hasArrow label={err ? "error" : isCopied ? "copied" : "copy"} color={err ? "red" : "black"} fontSize="sm" bgColor="gray.200" >
      <Center mx={2} _hover={{ cursor: "pointer" }} onClick={handleCopy}>
        { err ? <MdErrorOutline color="red" />  : isCopied ? <FaCheck color="#39aaaa"  /> : <MdOutlineCopyAll color="#39aaaa" />}
      </Center>
    </Tooltip>
  );
}
```
まずUI面について説明します。
基本的にはコピーのアイコンが表示されるようになっており、そのアイコンをホバーするとTooltipによって状態を表すテキストが表示されます。TooltipはUIを設定する複数のプロパティを持っていて、hasArrowで吹き出しに矢印を付けるかどうかや、placementで吹き出しの位置を調整することもできます。細かい内容は[公式ドキュメント](https://v2.chakra-ui.com/docs/components/tooltip)を参照してもらえればと思います。
デフォルトではコピーのアイコンを表示しておいて、コピーできたかやエラーが発生したかに基づいてアイコンを出し分けるようにしています。
実際にどういうアイコンを使うかはその時々の要件に合わせるとよいでしょう。

次にロジック面について説明します。
クリップボードにテキストを張り付けるコード自体は`await navigator.clipboard.writeText(value)`ですが、その前後で少々工夫しています。
isCopiedの状態によってチェックアイコンとコピーアイコンを使い分けているのですが、`await new Promise(resolve => setTimeout(resolve, 5000))`のあとに`setIsCopied(false)`とすることで時間経過でもとのコピーアイコンに戻し、再びコピーができるようになることを視覚的に表現しています。

一つ注意したいのが、navigatorオブジェクトはJavaScriptの組み込みのWebAPIであるのでクライアントサイドでしか使えません。コンポーネントを記述したファイルのトップに`"use client"`を付けるのを忘れないようにしましょう。

# 3. おわりに
一つのコンポーネントについてだけなのでかなり短い記事になりましたが、お役に立てれば幸いです。
VBA基本

# はじめに
学習記録・復習のためまとめています。IT業界2年目のひよっこです。

## 基本

### 作成したマクロの呼び出し方法

・マクロ上で、他のマクロ（プロシージャ）を呼び出す方法

:::note info
Callステートメントをしようしてマクロを記述する
:::

例　※マクロ名の後にかっこは不要！
```vba
Call test1
Call test2
```

・シート上で、ボタンから呼び出す場合
![{840D8879-FDE1-48F5-951C-61D85EA0D7A9}.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2655769/67885a1b-b217-90fa-e4e9-8f6dd6a5862f.png)

:::note info
１）開発タブ＞挿入＞ボタン（フォームコントロール）
２）該当のマクロを選択
３）右クリック＞テキストの編集から表示名を入力する
:::


***

## シート操作
### シートの指定方法
＜こんなときに使える＞
・複数シートがあり、明確に指定したいとき

```
'シート名で指定したいとき
Worksheets("シート名").対象セルなど

'シート番号で指定したいとき
Worksheets(シート番号).対象セルなど
```
:::note warn
・シート名が頻繁に変わる場合は、シート番号で指定した方が修正が少ない
・シート番号はブック内で左から順に附番される（非表示シートも同様に附番される）
:::

***
### シート数を数える
＜こんなときに使える＞
・シート数を数えたいとき
・シート数の増減があっても、修正不要で汎用的に使いたいとき

```
Worksheets.Count
```
:::note warn
ブック内の全てのシート数を数えるため、引数（シートの指定）はない
:::
例　全てのシートに対して繰り返しなにかの処理をしたい場合
```VB
Dim i

For i = 1 To Worksheets.count
    処理
Next
```

***
### シートを削除
＜こんなときに使える＞
・シートを削除したいとき

```
Worksheets(対象シート).Delete
```
:::note warn
セルに値が入っているシートを削除する場合、下記のように「本当に削除してよいか」という警告ウィンドウが表示される。
![{2CCCE061-116A-4D69-8016-7AFD54DF1D86}.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2655769/bd8a133a-625a-8e9d-6d2a-564f6e1765ed.png)
→ここでマクロが止まってしまうため、警告ウィンドウを表示しない設定も可能。
:::

例　値が入っていても、警告ウィンドウを表示せずシートを削除する方法
```VB
'警告ウィンドウを非表示
'※Alertsは複数形の「s」になる
Application.displayAlerts = False

Worksheets(対象シート).Delete

'警告ウィンドウを表示　※必要な場合のみ非表示設定した方がよいため、削除後は表示する設定に戻すことが推奨されている
Application.DisplayAlerts = True
```

***
### シートをコピー
＜こんなときに使える＞
・シートをコピーしたいとき
・全シート または、コピーする場所（ブック内）を指定したいとき

```VB
'新規のブックにコピーしたい場合
Worksheets(コピーしたい対象シート).Copy

'現在のブックにコピーしたい場合
 '対象シート※よりも「前（左）」に追加したい場合
  Worksheets(コピーしたい対象シート).Copy before:= Worksheets(対象シート※) 
  
 '対象シート※よりも「後ろ（右）」に追加したい場合 
  Worksheets(コピーしたい対象シート).Copy after:= Worksheets(対象シート※) 
```
:::note warn
・「コピーしたい対象シート」の記載がない場合（Worksheets.Copy）は、全シートがコピーされる
・コピー後のブックやシートが「アクティブ（ブック/シート）」になる
・基本的な文法は、移動（Move）メソッドと同じ
:::

例　テンプレートシートを12ヵ月分コピーして、シート名を「Y月」にしたい場合
```VB
    Dim i

    '1月から12月分を繰り返しコピーする
    For i = 1 To 12
        'シートの最後に追加していく
        Worksheets("テンプレート").Copy after:=Worksheets(Worksheets.count)
        
        '変数を使って、最後に追加したシートの名前を変更する
        Worksheets(Worksheets.count).name = i & "月"
    Next
```


***
### シートを移動
＜こんなときに使える＞
・シートを移動したいとき
・移動する場所（ブック内）を指定したいとき

```VB
'新規のブックに移動したい場合
'※対象シートのみ、新規ブックに移動する
Worksheets(移動したい対象シート).Move

'現在のブックに移動したい場合
 '対象シート※よりも「前（左）」に移動したい場合
  Worksheets(移動したい対象シート).Move before:= Worksheets(対象シート※) 
  
 '対象シート※よりも「後ろ（右）」に移動したい場合 
  Worksheets(移動したい対象シート).Move after:= Worksheets(対象シート※) 
```
:::note warn
・基本的な文法は、コピー（Copy）メソッドと同じ
・移動後のブックやシートが「アクティブ（ブック/シート）」になる
:::

***

### シートを追加
＜こんなときに使える＞
・シートを追加したいとき
・追加する場所（ブック内）やシート数を指定したいとき

```VB
'追加したい場合
Worksheets.Add

'対象シート※よりも「前（左）」に、2シート追加したい場合
  Worksheets.Add before:= Worksheets(対象シート※),Count := 2 
  
 '対象シート※よりも「後ろ（右）」に、4シート追加したい場合 
  Worksheets.Move after:= Worksheets(対象シート※) ,Count := 4
```
:::note warn
・新規シートが追加される
・「Worksheets.Add」のみの場合は、アクティブシートの前（左）に追加される
（※忘れやすい場合は、新規シートが「ちょいと前（左）失礼～！」と言いながら割り込んでくるイメージで覚える）
:::
Python3: GATT で String データを送信

送信先のアドレスと Characteristic UUID　を与えて、String を送る方法です。

## プログラム

```py:send_data.py
#! /usr/bin/python
#
#	send_data.py
#
#					Oct/02/2024
# ------------------------------------------------------------------
import asyncio
from bleak import BleakClient

DEVICE_ADDRESS = "08:3A:F2:66:04:4A"
CHARACTERISTIC_UUID = "7DEF8317-7302-4EE6-8849-46FACE74CA2A"


# ------------------------------------------------------------------
async def send_data(address, char_uuid, data):
	async with BleakClient(address) as client:
		if client.is_connected:
			print(f"Connected to {address}")

			data_bytes = data.encode('utf-8')

			await client.write_gatt_char(char_uuid, data_bytes)
			print(f"Data '{data}' sent to characteristic {char_uuid}")
		else:
			print(f"Failed to connect to {address}")
#
# ------------------------------------------------------------------
if __name__ == "__main__":
	data_to_send = "SU\r"
	asyncio.run(send_data(DEVICE_ADDRESS, CHARACTERISTIC_UUID, data_to_send))
	data_to_send = "ST\r"
	asyncio.run(send_data(DEVICE_ADDRESS, CHARACTERISTIC_UUID, data_to_send))
	data_to_send = "SV\r"
	asyncio.run(send_data(DEVICE_ADDRESS, CHARACTERISTIC_UUID, data_to_send))
# ------------------------------------------------------------------
```

## 実行結果

```text
$ ./send_data.py 
Connected to 08:3A:F2:66:04:4A
' sent to characteristic 7DEF8317-7302-4EE6-8849-46FACE74CA2A
Connected to 08:3A:F2:66:04:4A
' sent to characteristic 7DEF8317-7302-4EE6-8849-46FACE74CA2A
Connected to 08:3A:F2:66:04:4A
' sent to characteristic 7DEF8317-7302-4EE6-8849-46FACE74CA2A
```
grep memo

すごい単純なメモだが、好きな、実際に役に立っているオプションをリストアップする

## -w
This has the same effect as「Match whole word」in VS Code search:
![スクリーンショット 2024-10-02 17.24.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1680534/1dcb3d16-4bac-827f-ee11-ad96a91fed23.png)

## -l
Only show matching **filenames**. Also shows the uniquely

## -n
line numbers

## -A {number}
show {number} lines after each line in the results

## -B {number}
show {number} lines before each line in the results

## -C {number}
show {number lines before AND after each line in the results!

## -o
only show the matching part of the line
VBA【ステートメント】

# はじめに
学習記録・復習のためまとめています。IT業界2年目のひよっこです。

## ステートメントとは
マクロの中で動きの命令をするもの。条件分岐や繰り返し文など、マクロの動作を制御したり、変数を宣言したりできる。
***


### Callステートメント
＜こんなときに使える＞
・作成済みのマクロ（プロシージャ）を呼び出したい
・記述量を最小限にしたい

```
Call マクロ名
```
:::note warn
Call マクロ名( )　←かっこは不要！
:::

***
### Withステートメント
＜こんなときに使える＞
・繰り返し同じ対象について記述する
・記述量を最小限にしたい
・可視性を高めたい

```
With 対象   'シートやセルなど指定可能
    .処理
    .select
    .values など
End With
```
:::note warn
処理の前に「.」をつけることで「対象を（の）」という意味になる
例　「.select」→「対象を選択する」
　　「.values」→「対象の値」
:::
※with内で別な対象（別なセルなど）を書いた場合は、明記された対象が実行される！



***

### ifステートメント
＜こんなときに使える＞
・条件分岐させたいとき
・条件を比較したいとき

```
If 条件 then
   　　　　処理
   ElseIf 処理
   Else   処理
End If
```
:::note warn
thenを次の行に書くと「コンパイルエラー」が発生する。
もし次の行に跨ぎたい場合は「行継続文字（_）」を使用する。
※1行分の中で最大24回までは使用可能
:::


Ifステートメントで使用可能な比較演算子
| 演算子 | 説明 |
|:-:|:-:|
|  = | 等しい  |  
| <  |  より大きい |   
| <=  | 以上  |   
| > | より小さい | 
| >= | 以下 | 
| <> | 等しくない | 
| And | 全ての条件に合致する | 
| Or | いずれか一つの条件に合致する | 

※以上、以下は「=」が「<」や「>」の後につくため注意！

***

### Select Caseステートメント
＜こんなときに使える＞
・複数の条件を同時に判定したいとき

```
Select Case 値
    Case 条件1
         処理

    Case 条件2
         処理
         
    Case Else     '※同じ行に処理を記載するとコンパイルエラー
         処理
End Select    
```
:::note warn
Elseも同様に「Case」がつく！
分岐指定の際は必ず「Case」が先頭につくと覚えておく！
:::

条件の指定方法
|値 | 意味 |
|:-:|:-:|
| 1,2,3 | 1または2または3の場合 |  
| 1 To 10 | 1から10の場合 |  

例　「,」と「To」は同時に使用可能！

```
Dim month
    month = Range("B10")
    
    Select Case month
        Case 3 To 5
            MsgBox "春"
        Case 6 To 8
            MsgBox "夏"
        Case 9 To 11
            MsgBox "秋"
        Case 12, 1 To 2
            MsgBox "冬"
        Case Else
            MsgBox "1～12の数字を入力ください"
    End Select
```
ファーストパーティcookie,サードパーティcookie,パラサイトcookie 3種類のHTTP Cookieについて

# はじめに
cookieとは簡単に言えば、ウェブブラウザに保存される小さなデータです。
主に以下の3つの用途で使われます。

- セッション管理: ユーザーのログイン状態、ショッピングカート、ゲームのスコア、またはその他のユーザーセッションに関するサーバーが覚えておくべきその他のものの保持
- パーソナライズ: 表示言語や UI テーマのようなユーザー設定
- トラッキング: ユーザーの行動の記録および分析

また、cookieは基本的に`ファーストパーティcookie`と`サードパーティcookie`の2種類に分けられます。
この記事ではファーストパーティcookieとサードパーティcookieに加え、`パラサイトcookie`の3種類のcookieの違いについて解説します。

# cookieの設定
ウェブブラウザにcookieを設定する(別名:焼く)方法には以下の2種類があります。
2種類の方法について知ることで3種のcookieの違いが分かりやすくなると思いますのでこの記事で紹介します。

### 1.JavaScriptの[document.cookie](https://developer.mozilla.org/en-US/docs/Web/API/Document/cookie)

以下のようなコードでcookieを設定可能です。

```js
document.cookie = "favorite_food=tripe; SameSite=None; Secure";
```

### 2.HTTPヘッダーの[Set-Cookie](https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/Set-Cookie)

以下のようなヘッダーを付与する事で、cookieを設定可能です。

```
Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT
```

# ファーストパーティcookieとは

ユーザーが訪問したウェブサイトのドメインで焼かれるcookieです。
主にウェブサイト側がdocument.cookieやSet-Cookieヘッダーでcookieを焼き、セッション管理などを行います。

ここで重要なのが、cookieを焼く側に関係なく`焼かれる側のドメインがファーストパーティの時にファーストパーティcookieになる`点です。
例えば広告事業者などのサードパーティがdocument.cookieでcookieを焼く場合もファーストパーティcookieになります。

# サードパーティcookieとは

ユーザーが訪問したウェブサイトのドメイン`以外のドメイン`で焼かれるcookieです。
主に広告事業者が複数のドメインでユーザーに合わせて広告をターゲティングするのに使用されます。

外部ドメインにcookieを焼く必要があるのでSet-Cookieヘッダーを使用する必要があります。
document.cookieではサードパーティcookieを焼けません。

また、サードパーティcookieは外部にユーザーの情報が送信されるのでプライバシーの侵害が問題になっています。
ですので、各所でサードパーティcookieの規制が行われています。
[こちらの記事](https://cloud.google.com/looker/docs/best-practices/chrome-third-party-cookie-deprecation?hl=ja)にあるように、Chromeがサードパーティcookieを廃止しようとした時期もありました。
しかし、主に広告業界で必要不可欠な技術であるのも事実であり、後にGoogleはサードパーティcookieの廃止を撤回しました。
利便性と危険性を併せ持つ諸刃の剣みたいな技術ですね。

# パラサイトcookieとは

パラサイトcookieは[こちらの記事](https://www.techscore.com/blog/2016/06/24/3rd-party-cookie/)にあるように、アドテク界隈で使われている造語です。
主に広告事業者が特定のサイトにおける広告表示回数を制限するのに使われます。

前述の通り、サードパーティがdocument.cookieで焼くcookieはファーストパーティcookieです。
しかし、初見ではサードパーティcookieと勘違いしやすいcookieですのでアドテク界隈では`サードパーティが焼くファーストパーティcookieをパラサイトcookie`と名付けたようです。
ファーストパーティの領域にcookieを焼くことでサードパーティcookieの規制を回避する、まるでファーストパーティに寄生しているかのようなcookieという意味合いです。
しかし、サードパーティcookieと、サードパーティが焼くファーストパーティcookieの違いを明確にする上で非常に有用な単語だと考えています。

# まとめ

3種類のHTTP Cookieについて簡潔に述べると以下のようになります。

- ファーストパーティcookie: ユーザーが訪問したウェブサイトのドメインに焼かれるcookie。`ウェブサイト側(ファーストパーティ)が焼く。`
- サードパーティcookie: ユーザーが訪問したウェブサイトのドメイン`以外のドメイン`に焼かれるcookie。`サードパーティがSet-Cookieヘッダーで焼く。`
- パラサイトcookie: ユーザーが訪問したウェブサイトのドメインに焼かれるcookie。ファーストパーティcookieに含まれる。`サードパーティがdocument.cookieプロパティで焼く。`

表にまとめると以下のようになります。

|cookieを焼く事業者→<br>cookieを焼く技術↓ |ファーストパーティ |サードパーティ |
|---|---|---|
|**document.cookie** |ファーストパーティcookie |パラサイトcookie |
|**Set-Cookieヘッダー[^1]** |ファーストパーティcookie |サードパーティcookie |

[^1]: ファーストパーティはファーストパーティのドメイン、サードパーティはサードパーティのドメインに付与する前提です。

# おわりに

以前、cookieについて色々と調べていましたが、ファーストパーティcookieとサードパーティcookieの違いがややこしくて理解に苦労しました。
ですので、cookieの理解に苦しむ人に届けという想いで記事にしてみました。
皆さんのcookieについての理解が深まれば幸いです。
mindの研究 その87

# 概要
mindが、謎なので調べてみた。
練習問題やってみた。

# 練習問題
開いたメモ帳を操作して、「hello」をセーブせよ。

# 環境
windows10

# 写真
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18104/b1ccef48-17dd-d77d-684a-12e5939cf460.png)

# サンプルコード

```

右クリックとは
　結果は　変数
　数１は　変数
　数２は　変数
　数１に　入れ
　数２に　入れ
　数２をつみ
　数１をつみ
　"user32"と　"SetCursorPos"で　ＡＰＩ呼出し２　結果に　入れ
　エラー？
　ならば
　　エラー文字列を　一行表示し　終り
　つぎに
　一秒待つ
　２をつみ
　０をつみ
　０をつみ
　０をつみ
　０をつみ
　"user32"と　"mouse_event"で　ＡＰＩ呼出し５　結果に　入れ
　エラー？
　ならば
　　エラー文字列を　一行表示し　終り
　つぎに
　４をつみ
　０をつみ
　０をつみ
　０をつみ
　０をつみ
　"user32"と　"mouse_event"で　ＡＰＩ呼出し５　結果に　入れ
　エラー？
　ならば
　　エラー文字列を　一行表示し　終り
　つぎに
　。



送信とは
　数１は　変数
　結果は　変数
　数１に　入れ
　数１をつみ
　０をつみ
　０をつみ
　０をつみ
　"user32"と　"keybd_event"で　ＡＰＩ呼出し４　結果に　入れ
　エラー？
　ならば
　　エラー文字列を　一行表示し　終り
　つぎに
　数１をつみ
　０をつみ
　２をつみ
　０をつみ
　"user32"と　"keybd_event"で　ＡＰＩ呼出し４　結果に　入れ
　エラー？
　ならば
　　エラー文字列を　一行表示し　終り
　つぎに
。

保存を開くとは
　結果は　変数
　0a0hをつみ
　０をつみ
　０をつみ
　０をつみ
　"user32"と　"keybd_event"で　ＡＰＩ呼出し４　結果に　入れ
　エラー？
　ならば
　　エラー文字列を　一行表示し　終り
　つぎに
　0a2hをつみ
　０をつみ
　０をつみ
　０をつみ
　"user32"と　"keybd_event"で　ＡＰＩ呼出し４　結果に　入れ
　エラー？
　ならば
　　エラー文字列を　一行表示し　終り
　つぎに
　053hをつみ
　０をつみ
　０をつみ
　０をつみ
　"user32"と　"keybd_event"で　ＡＰＩ呼出し４　結果に　入れ
　エラー？
　ならば
　　エラー文字列を　一行表示し　終り
　つぎに
　053hをつみ
　０をつみ
　２をつみ
　０をつみ
　"user32"と　"keybd_event"で　ＡＰＩ呼出し４　結果に　入れ
　エラー？
　ならば
　　エラー文字列を　一行表示し　終り
　つぎに
　0a2hをつみ
　０をつみ
　２をつみ
　０をつみ
　"user32"と　"keybd_event"で　ＡＰＩ呼出し４　結果に　入れ
　エラー？
　ならば
　　エラー文字列を　一行表示し　終り
　つぎに
　0a0hをつみ
　０をつみ
　２をつみ
　０をつみ
　"user32"と　"keybd_event"で　ＡＰＩ呼出し４　結果に　入れ
　エラー？
　ならば
　　エラー文字列を　一行表示し　終り
　つぎに
。

 
メインとは
※　「notepad.exe」を　プログラム実行
※　二秒待つ
　１００をつみ　１００をつみ　右クリックし
　048hをつみ　送信し
　045hをつみ　送信し
　04chをつみ　送信し
　04chをつみ　送信し
　04fhをつみ　送信し
　保存を開く
　048hをつみ　送信し
　045hをつみ　送信し
　04chをつみ　送信し
　04chをつみ　送信し
　04fhをつみ　送信し
　0dhをつみ　送信し
。




```

以上
uvを使ってCrypto環境の構築

<!-- uvを使ってCrypto環境の構築 -->

## はじめに

今回はuvを用いてCrypto環境を構築する方法を紹介します。

Crypto環境を構築しておくと、CTFでCryptoカテゴリの問題を解くときに便利です。

## uvとは

uvはPythonのパッケージ管理システムで、pipと比較して高速であるということが特徴となっています。

Windowsでは、以下のようにインストールすることができます。

```bash
powershell -c "irm https://astral.sh/uv/install.ps1 | iex"
```

Linuxでは、以下のようにインストールすることができます。

```bash
curl -sL https://astral.sh/uv/install.sh | bash
```

以下のコマンドで、インストールしたかどうかを確認できます。

```bash
uv --version
```

## Crypto環境の構築

ここからは実際にCrypto環境を構築していきます。

まず、以下のコマンドで作業ディレクトリを作成します。名前は任意ですがここでは`crypto`とします。

```bash
uv init crypto
```

これによって、以下のような構成のディレクトリができます。

```
crypto/
    .git
    .gitignore
    .python-version
    hello.py
    pyproject.toml
    README.md
```

ここでは割愛しますが、オプションをつけるとREADMEを省略するなどの設定ができます。gitで公開する必要がなかったりしたら、git関連のディレクトリやファイルを消去するのも手です。

次に、以下のコマンドでPythonのバージョンを指定します。

```bash
uv python pin 3.9.6
```

これによって、Pythonのバージョンが3.9.6になります。特に指定しない場合は最新のバージョンがインストールされます。

次に、以下のコマンドでPythonの仮想環境を作成します。

```bash
uv sync
```

これによって、`crypto`ディレクトリ内に`.venv`ディレクトリが作成され、実際にPythonの仮想環境が構築されることとなります。

次に、`requirements.txt`に必要なパッケージを記述します。例えば、Cryptoモジュールを使いたい場合は以下のように記述します。

```txt
pycryptodome == 3.20.0
```

`requirements.txt`に記述したパッケージをインストールするには以下のコマンドを実行します。

```bash
uv add -r requirements.txt
```

これによって、`pycryptodome`がインストールされます。`uv add`コマンドによって個別にパッケージをインストールすることもできますが、`requirements.txt`を使うことで管理が楽になります。必要に応じてパッケージのリストを更新するとよいでしょう。

これによって、Crypto環境の構築が完了しました。実際にCrypto問題を解いてみましょう。例えば、WaniCTF 2024の`beginners_rsa`という問題を解いてみます。

```python
from Crypto.Util.number import *

#暗号文
#enc = pow(m, e, n)
enc = 127075137729897107295787718796341877071536678034322988535029776806418266591167534816788125330265
#公開鍵
e = 65537
#これを素因数分解すると鍵が求まる
n = 317903423385943473062528814030345176720578295695512495346444822768171649361480819163749494400347
#msieveで素因数分解した結果
p = 9953162929836910171
q=11771834931016130837
r=12109985960354612149
s=13079524394617385153
a=17129880600534041513

#秘密鍵を求める
phi = (p-1)*(q-1)*(r-1)*(s-1)*(a-1)
d = inverse(e, phi)
m = pow(enc, d, n)
flag=long_to_bytes(m).decode()
print(flag)
```

このプログラムを`solve.py`として保存し、以下のコマンドを実行します。

```bash
uv run solve.py
```

結果は以下のようになりました。

```bash
FLAG{S0_3a5y_1254!!}
```

## おわりに

uvを用いて、Crypto環境を構築する方法を紹介しました。CTFをやっていると環境が汚れやすいので、このように専用の仮想環境を立ててその中で問題を解くとよいと思います。

## 参考文献

[uvを使ってPython実行環境を整理してみた | DevelopersIO](https://dev.classmethod.jp/articles/i-like-uv/)
RailsでHotwire(websocket)使ったら、HerokuのRedisで接続エラーになった

## 起こったこと
RailsアプリでHotwireのWebSocketを導入時に、Redisが必要になった。
HerokuそれをHerokuにデプロイしたら、Redisを使う場面で以下のエラーになった。

```
redis::CannotConnectError (SSL_connect returned=1 errno=0 peeraddr=34.232.151.103:20700 state=error: certificate verify failed (self-signed certificate in certificate chain) (rediss://ec2-XXXXXXXXXX.amazonaws.com:000000)):
```

Herokuの[公式ドキュメント](https://devcenter.heroku.com/ja/articles/connecting-heroku-redis#connecting-in-rails)にある、RailsでRedisに接続するためのコードは実装していた。

```rb:config/initializers/redis.rb
$redis = Redis.new(url: ENV["REDIS_URL"], ssl_params: { verify_mode: OpenSSL::SSL::VERIFY_NONE })
```

環境変数、`REDIS_URL`や`REDIS_TLS_URL`などの設定も問題なさそう。

## 結論

WebSocketを使う場合、以下の設定も必要でした。

```yaml: config/cable.yml
production:
  adapter: redis
  url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %>
  channel_prefix: name_production
  ssl_params:
    verify_mode: <%= OpenSSL::SSL::VERIFY_NONE %>
```
単純にHotwireを使うだけではRedisは必要ない。

▼実装の参考にした記事
https://zenn.dev/shita1112/books/cat-hotwire-turbo/viewer/turbo-streams-websocket

HotwireでWebSocketを使っていたからRedisが必要だった。
Hotwireだから設定が必要なのではなく、WebSocketを使っていたから設定が必要だと気付ければ、もっと早く解決できたかもしれない。
迫真画像加工部 ～フラクタルと化した先輩～


## 先駆者様

https://zenn.dev/baroqueengine/articles/ccf3ac7a2c9332

## 結論

https://mogamoga1024.github.io/image-to-fractal/

<img width=600 src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/649673/458d7aa2-1615-fcd8-bd3a-b3d321b0fa94.png>

<img width=600 src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/649673/62818579-9ea3-8bb3-8af1-c340810d43e0.png>

## 解説

と言っても、アルゴリズムは先駆者様とほぼ同じ。
そのため、差分を説明する。

```js:複雑スコアの計算：先駆者様
const error = (hist.reduce((prev, cur, i) => prev + cur * (i - avg) ** 2, 0) / num) ** 0.5;
```

先駆者様は最後に0.5乗しているが、ただ単に大小を比較するだけならする必要がないため私はしなかった。
とはいえ、後々係数をかけたりして弄りまわすので好みの問題。

```js:複雑スコアの計算：自分
let roughnessSum = 0;
for (const {r, g, b} of colorList) {
    // マンハッタンとユークリッド どっちがよいのだろうか
    // roughnessSum += Math.abs(block.r - r) + Math.abs(block.g - g) + Math.abs(block.b - b);
    roughnessSum += (block.r - r) ** 2 + (block.g - g) ** 2 + (block.b - b) ** 2;
}
if (block.width < 2 || block.height < 2) {
    block.roughness = Number.NEGATIVE_INFINITY;
}
else {
    block.roughness = (roughnessSum / colorList.length) * (pixelCount / originalPixelCount);
}
```

四分割ができないほど小さい領域はマイナス無限大をセットしている。（今考えると0でもよかった。）

また、先駆者様の「領域のサイズ」「ある領域が分割されすぎる場合があるのでは？」の問題は
```js
block.roughness = (roughnessSum / colorList.length) * (pixelCount / originalPixelCount);
// block.roughness = 分散 * 面積の割合
```
とすることで回避を試みた。
小さいエリアは面積の割合が0に近づくため選ばれにくくなるという訳だ。

その他、丸で描いてみても面白そうなので実装した。

また私は画像を10000回分割しても大して重くならなかったため、ヒープうんぬんは利用しなかった。
処理が重くなるとしたら、最大スコア探しよりも画像の加工、描画処理のほうが大きな要因だと思うが、どのくらい速くなるのか少し気になる。

以上。

全体のソースはこ↑こ↓

https://github.com/mogamoga1024/image-to-fractal

## おまけ（Bad Apple But Quadtree）

<img width=600 src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/649673/e9ddd880-b190-b5cb-19d6-e045ab76f665.gif>

まさかな…と思ってググったら既存だった。。。

https://www.youtube.com/watch?v=mZZ6-3x6lZQ

## チラ裏

* 四分割ではなくて、二分割でもよいのでは？
* 複雑度のスコアが0のもの（単色しかないブロック）が選択されたら分割せずに処理を終了すべきでは？→対応した。
年間数十件の案件を担当するエンジニア向けの業務効率化

# はじめに
私は年間数十件の案件を担当するWEBエンジニアです。
そんな私が日常業務で実践している効率化の方法を共有します。
少しでもお役に立てば幸いです。

# Gmail
同時並行で複数件の案件が走るので、案件毎のメールの振り分けは必須です。
振り分けの方法については[こちら](https://qiita.com/kentosity/items/8cca2924de98cf4e5974)を参考にしてください。

ラベルが増えると視認性が悪くなるので、完了した案件のラベルは未読のみ表示されるように設定しています。
該当ラベルの三点リーダーを選択し、「未読の場合は表示」を選択してください。
この設定により、通常は非表示になりますが、「もっと見る」を選択すると表示されます。
<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3659381/5aa25b05-4d56-585c-c018-d2442ab1811d.png">

# Chrome
案件毎にブックマークフォルダーを作成し、その中に必要なURLを登録しています。
ブックマークフォルダーはブックマークバーに表示しています。
ブックマークとブックマークバーの表示方法は[こちら](https://support.biglobe.ne.jp/settei/browser/chrome/chrome-004.html)を参照してください。

私の場合、以下のリンクを登録しています。
・Backlog
・クライアント資料
・スケジュール
・デザイン資料
・製作WEBサイト
　「WEB」フォルダを作成し、その下に環境毎のURLを登録(例、dev、stg、prod）
 ・Googleアナリティクス

以下は私のブックマークバーです。現在進行中の案件を常にブックマークバーに表示させ、終了した案件は「old」フォルダーへ移動しています。
<img width="800" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3659381/b818657c-dd02-2448-2cb9-e205d7ad9527.png">

# Slack
・スターを付ける
私は常に100チャンネル以上のチャンネルに参加しているため、重要なチャンネルには[スター](https://slack.com/intl/ja-jp/help/articles/201331016-%E3%83%81%E3%83%A3%E3%83%B3%E3%83%8D%E3%83%AB%E3%82%84%E3%83%80%E3%82%A4%E3%83%AC%E3%82%AF%E3%83%88%E3%83%A1%E3%83%83%E3%82%BB%E3%83%BC%E3%82%B8%E3%81%AB%E3%82%B9%E3%82%BF%E3%83%BC%E3%82%92%E4%BB%98%E3%81%91%E3%82%8B
)を付けて上部に表示させています。

・リマインド
　すぐに対応できないメッセージにはリマインド設定をしています。メッセージを右クリックして「後でリマインドする」を選択してください。
<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3659381/0bc25bf1-38a2-716f-6b08-4ea6f99d2b25.png">
<br>
指定した時間になると「後で」アイコンにマークが付きます。
対応が完了したら「完了にする」ボタンを選択してマークを消すことができます。
<img width="800" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3659381/834fed04-f885-cbb7-2aa4-79016ec9013e.png">

・自分専用のプライベートチャンネルの有効活用
　自分専用のプライベートチャンネルを使い、以下の内容を投稿しています。
　１．自分用のメモ
 　２．アクションアイテムを投稿し「後でリマインドする」設定を適用
　３．スマホで撮影したSnapShotを投稿し、PCで取り込む

# WEB全体のSnapShot
最後にWEB全体のSnapShotを撮るのに便利なツールを紹介します。
・Windows/MAC Chrome
　デベロッパーツールででWEB全体のSnapShotを撮る方法は[こちら](https://www.netassist.ne.jp/techblog/26734/)を参照してください。
　なお、画像の遅延読み込みされている場合は、うまく撮影できないので、私は　[FireShot](https://chromewebstore.google.com/detail/take-webpage-screenshots/mcbpblocgmgfnpjjppndjkmgjaogfceg?hl=ja)というChrome拡張機能を使用しています。
・iOS Safari
　safariでWEB全体のSnapShotを撮る方法は[こちら](https://dekiru.net/article/18900/)を参照してください。
　なお、画像の遅延読み込みされている場合は、うまく撮影できないので、私は　「[Picsew](https://apps.apple.com/jp/app/picsew-screenshot-stitching/id1208145167)」というアプリを使用しています。
  このアプリではLIFFアプリ全体のSnapShotも撮影できます。
 ・Android Chrome
　Android ChromeでWEB全体のSnapShotを撮る方法は[こちら](https://pixeler.condb.link/chrome-long-screenshot/)を参照してください。

# 最後に
今回ご紹介した業務効率化の方法は、私が日々の業務で実践しているものです。Gmailのメール振り分けやChromeのブックマーク整理、Slackのリマインド設定など、各ツールの特徴を活かしながら、案件管理や情報整理の効率を上げることができます。特に、複数の案件を同時進行するエンジニアにとって、こうした小さな工夫が大きな効果を生み出します。

ぜひ、皆さんの業務にも取り入れてみてください。少しでも参考になれば幸いです。
Keycloak server to be placed behind the HTTPS-enabled ALB

# 0. はじめに
KeycloakをApplication Load Balancerの背後に配置して使おうとしたときにはまったのでメモ。

# 1. 前提
InternetとALBの間はHTTPS化します。
ALBとKeycloakの間はHTTPです。
ですので，適切なドメインをRoute 53のホストゾーンで管理していることが前提です。

このとき，セキュリティグループは以下の通り。
- ALBのセキュリティグループ:
    - インバウンド 443
- EC2のセキュリティグループ:
    - インバウンド 8080
    - ソース ALBのセキュリティグループ

リスナーは，
    - Protocol:Port HTTPS:443

ターゲットグループは，
    - Protocol:Port HTTP:8080

としておきます。これで，外部からは443で来たリクエストが，Keycloakには8080で渡されることになります。

# 2. docker-compose.yml

```docker-compose.yml
services:
  keycloak:
    image: quay.io/keycloak/keycloak:16.1.1
    environment:
      DB_VENDOR: POSTGRES
      DB_ADDR: postgres
      DB_DATABASE: keycloak
      DB_USER: keycloak
      DB_PASSWORD: password
      KEYCLOAK_USER: test
      KEYCLOAK_PASSWORD: test
      KC_HOSTNAME: www.example.com
      PROXY_ADDRESS_FORWARDING: "true"
    ports:
      - "8080:8080"
      - "8443:8443"
    depends_on:
      - postgres
    restart: always

  postgres:
    image: postgres:15.7
    environment:
      POSTGRES_DB: keycloak
      POSTGRES_USER: keycloak
      POSTGRES_PASSWORD: password
    ports:
      - "5432:5432"
    restart: always
```
KC_HOSTNAMEはALBに設定する証明書と対応したドメイン名です。PROXY_ADDRESS_FORWARDINGは"true"を設定しておきましょう。この設定がないと，Keycloakが追加のリソースを取得できなくなります。その場合，ホームページは表示されるのですが，Administrator consoleを押すと真っ白な画面になってしまいます。
Railsバージョンアップで `URI.encode` と `URI.escape` が使えなくなった問題とその解決策


Railsアプリをバージョンアップした際に、`URI.encode` や `URI.escape` が非推奨・廃止されるため、エンコードの方法を変更する必要が出てきます。この問題とその解決策について、コード例を交えて解説します。



---

## 問題の発生

Railsをバージョンアップした後、次のようなコードで問題が発生することがあります。

```ruby
address = "#{params[:address_line1]}#{params[:city]}#{params[:address_line2]}"
target_uri = URI.escape("https://api.example.com/location?address=#{address}&key=#{API_KEY}")
```

このコードは `URI.escape` の廃止によりエラーが発生します。

---

## 解決策: `URI.encode_www_form_component` を使用

すべてのURLをエンコードするのではなく、パラメータ部分のみをエンコードします。

### 変更後

```ruby
address = URI.encode_www_form_component("#{params[:address_line1]}#{params[:city]}#{params[:address_line2]}")
target_uri = "https://api.example.com/location?address=#{address}&key=#{API_KEY}"
```

これにより、正しいリクエストを生成できます。

---

上記のように、エンコード対象を変更することで、Railsバージョンアップによるエラーを回避できます。
---

## まとめ

Railsのバージョンアップに伴い、`URI.encode` や `URI.escape` が使えなくなった場合は、`URI.encode_www_form_component` を使うことで、パラメータのみをエンコードして問題を解消することができます。バージョンアップ時には、これらの変更に注意して、コードの互換性を保つようにしましょう。

---
古くて役に立たなくなった記事は削除していきます

# SDKのバージョンアップにより
煮ても焼いても食えなくなった記事は検索する側としてもノイズにしかならないため、頃合いを見て削除していく方針です。
また、今後の新規記事はこのアカウントではなく、こちらで掲載します。
https://qiita.com/Xenoma_Goto
TortoiseGitのLog確認画面で『Too many files to display』によってコミット詳細が見れない時の対応

# 現象

TortoiseGitのLog確認画面で、下記のようにコミットしたファイル達が確認できない。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/706116/89267712-e1c7-e464-2c93-ec37b1091f33.png)
『Too many files to display』とあるように、一度にコミットしたファイルが多すぎるようです。

# 対応

TortoiseGitの『Setting』。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/706116/0dde5ff9-0dc6-eeba-41f6-c7031763387d.png)

『Advanced』。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/706116/c0e1ec5e-5f2e-b5dd-2094-f165f41492a9.png)

『LongTooManyItemsThreshold』の値について、たぶんデフォルトは1000になっています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/706116/6495cfcb-d784-8d7f-0771-6e0db0c143f7.png)

これを下記のように増やしてあげます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/706116/df9c35a2-ea4b-f0a4-4378-3f05a3f1436d.png)

# 蛇足

本来、大量ファイルを一度にコミットするべきではないのですが…。
どうしても、致し方なく、やむをえず、やらねばならぬ、な時もあるので…。

# 参考サイトさん

https://gitlab.com/tortoisegit/tortoisegit/-/issues/3878

# バージョン

Microsoft Windows [Version 10.0.22631.4249]
Git 2.44.0.windows.1
TortoiseGit 2.16.0.0
VSCode の textlint 拡張機能が TextLintCore の利用に対して警告を表示していたので ESLint に乗り換えた

# eslintに乗り換えた話

## 概要

2024年現在、VSCode で開発をしていると、出力タブで textlint に関する警告を目にすることがあります。

```zsh
(node:3302) DeprecationWarning: textlint: TextLintCore is deprecated. Please use new APIs https://github.com/textlint/textlint/issues/1310
```

この問題は [拡張機能のvscode-textlint](https://marketplace.visualstudio.com/items?itemName=taichi.vscode-textlint) が内部でTextLintCoreを利用していることが原因だと思われます。

本件に関するissueが上がっていますが、現状では未対応です。

https://github.com/taichi/vscode-textlint/issues/70

textlint本体をアップデートしても解消されませんでした。このことからも、拡張機能側の問題であると考えられるでしょう。

## 乗り換え手順

私の環境ではeslintを使っても期待する機能を実現できそうでした。従って表題の通り乗り換えることにしました。

まずはtextlintが必要なくなるのでアンインストールします。

**※筆者は[ni](https://github.com/antfu-collective/ni)を使っています。**

```zsh
nun textlint
```

.textlintrcがあれば、それも削除します。

次にeslintをインストールしていきます。

```zsh
ni eslint -D
```

eslintの初期設定を開始します。初期設定の中で尋ねられる質問には、あなたの環境に応じて回答します。

```zsh
npx eslint --init
```

初期設定が完了したら、プロジェクトの `.vscode/settings.json` へ次のように設定を追加します。

```json
{
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": "always"
  }
}
```

以上の設定が完了すれば、コードを書きながらリアルタイムにlinterが走り、可能であれば修正まで自動で実行してくれるようになります。

もちろん、textlintの警告も表示しなくなります。
Railsのバージョンアップで `add_template_helper(ApplicationHelper)` エラー発生！ `helper ApplicationHelper` に変更する理由と対処法



Railsアプリをバージョンアップした際に、`add_template_helper(ApplicationHelper)` を使用しているとエラーが発生することがあります。このエラーを解消するために、 `helper ApplicationHelper` へ変更する方法について解説します。

---

## エラーの概要

Railsのバージョンをアップデートした後、コントローラやビューでヘルパーを使おうとしたときに、以下のようなエラーが発生する場合があります。

```
NoMethodError: undefined method `add_template_helper' for ApplicationController:Class
```

このエラーは、Railsのバージョン変更に伴い、`add_template_helper` の使用方法が非推奨、または変更されたことが原因です。特に、`ApplicationHelper` をコントローラで使いたいときにこのエラーが見られます。

---

## 解決策: `helper ApplicationHelper` を使う

エラーを解決するためには、従来の `add_template_helper` の代わりに、`helper` メソッドを使います。以下のようにコードを修正します。

### 変更前

```ruby
class ApplicationController < ActionController::Base
  add_template_helper(ApplicationHelper)
end
```

### 変更後

```ruby
class ApplicationController < ActionController::Base
  helper ApplicationHelper
end
```

これにより、`ApplicationHelper` が正しくコントローラ内で使えるようになり、エラーも解消されます。

---

## なぜ `add_template_helper` ではなく `helper` を使うべきか？

Railsのヘルパーをコントローラで使う方法として、`add_template_helper` は古い記法であり、現在のRailsではほとんどの場合 `helper` メソッドを使うことが推奨されています。

- **`add_template_helper` は内部的に使われるメソッド**: かつて `add_template_helper` は、ヘルパーをコントローラに追加するために使われていましたが、通常のアプリ開発では `helper` を使うほうがシンプルで直感的です。
- **`helper` メソッドはよりRailsらしい記法**: Railsではヘルパーを使うために `helper` メソッドが用意されており、これを使うことでビューやコントローラにヘルパーを簡単に読み込むことができます。これにより、コードがより一貫性を持ち、他の開発者にもわかりやすい形になります。

---

## まとめ

Railsのバージョンアップ後に `add_template_helper(ApplicationHelper)` のエラーが出た場合、`helper ApplicationHelper` へ書き換えることで解決します。Railsの新しいバージョンでは `helper` メソッドを使うことが推奨されており、これによってエラーを回避しつつ、よりシンプルなコードを書けるようになります。

バージョンアップ時には、こうした細かい変更に気を配りながら、コードの互換性を保つようにしましょう。
レビューアイで 星野リゾート 界 松本 のレビュー分析をしてみた

弊社リテジーリンクではレビュー分析AIレビューアイの無料デモを公開中です。
レビューアイ (英語表記 ReviewAI) は、商品・サービスのレビューを分析し、
観点ごとに強みと改善点を明らかにする、レビュー分析AIです。

本記事では、星野リゾート 界 松本のレビューを例に
レビューアイで明らかになった強みと改善点を1つずつ紹介します。

# 強み
レビューアイでレビュー分析すると、レビューでよく出てくる観点が一覧できます。
強み順に並べると、部屋が一番上にきます。
![スクリーンショット 2024-09-13 1.07.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3776808/07ba3186-f46f-2353-2768-af0fc129db9f.png)

**部屋** の詳細を確認すると、部屋に関してどのようなレビューがあるのか可視化されます。
こちらを強み順に並べると、**風呂**、**露天風呂**、**広い** が上位にきます。
![スクリーンショット 2024-09-13 1.09.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3776808/28d3c561-2897-5373-09e8-afa8ea6d45e7.png)

**風呂**のレビューを見ると、感情分析した結果で整理してレビューの抜粋文が確認できます。
部屋のお風呂が広くとられており、リラックス出来たという評価をされている様子がわかります。
![スクリーンショット 2024-09-13 1.11.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3776808/b809da99-303a-dadd-437d-2f775375b407.png)

# 改善点
レビュー分析で見つかる観点を改善順に並べると、**時間** が上がってきます。
![スクリーンショット 2024-09-13 1.14.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3776808/c2341217-d846-3fc5-09b1-a2cdc9e49444.png)

 **時間** の詳細を確認すると、どのような点で改善の余地があるか一覧できます。
![スクリーンショット 2024-09-13 1.52.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3776808/8d29dd14-6159-3aed-5e8c-f607cf2a206d.png)

**朝食**　のレビュー文を見ると希望の時間を取れないという意見があります。
一方ではスタッフの方と楽しく会話して過ごすことが出来たとの声もあります。
![スクリーンショット 2024-09-14 0.52.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3776808/a95026be-7836-edd5-de58-12cd9f1ef622.png)

## まとめ
レビューアイで 星野リゾート 界 松本のレビュー分析をしました。
結果としては、

・部屋や風呂が広くゆっくりできる。
・リラックスして過ごせことができる。
ということが判明しました。

今回は紹介できませんでしたが、
・スタッフの対応
・おもてなし
などについての意見もありました。

[レビューアイ](https://reviewai.next-seed.work/)の無料デモの公開中です。
ぜひ[こちら](https://reviewai.next-seed.work/)からお申し込みください。
ISP

# ISPとは
>インターネットを利用するためには、`回線業者が提供しているインターネット回線を利用する必要`があります。代表的な回線業者には、NTT東日本・西日本（フレッツ光）やその他事業者のほか、ケーブルテレビ会社系や電力会社系の企業などがあります。
>
>しかし、実際にインターネットを利用するためには、`回線業者だけでなく、プロバイダーと呼ばれるインターネット接続事業者と契約する`必要があります。プロバイダー（Provider）という言葉には、「何らかの商品やサービスを提供している人や会社」という意味があります。それで`「インターネット接続というサービスを提供している事業者」`のことを、Internet Service Provider（インターネットサービスプロバイダー）といい、略してISPと呼ぶこともあります。
>
>なお、携帯電話事業者やモバイルWi-Fiルーターの提供企業の中には、`回線業者とプロバイダーが一体化している事業者もあります`。
...
>インターネットを利用するためには、アクセスポイント（基地局）を介してインターネット回線に接続する必要があります。その`アクセスポイントを提供しているのがプロバイダー`です。

# 各社ISPの違い
>プロバイダーの最大の役割は、ユーザーのデバイスとインターネットをつなぐことです。そのために、`インターネットに接続するという点で各社に大きな差はありません。インターネット接続料金にしても、大きな違いはない`のです。では、どこに違いを見いだせるのかというと、`インターネット接続以外の付加サービス`です。

https://www.ntt.com/business/services/network/internet-connect/ocn-business/bocn/knowledge/archive_15.html

# 感想
ISPの意味が今更知るこtができた。
例外処理

今回はJavaの例外処理について記事にしたいと思います。

例外とは、プログラムが予想外の問題に遭遇したときに発生するエラーのことです。たとえば、次のような状況が考えられます：

数字を入力する場面で、文字を入力してしまった。
ファイルを読み込もうとしたけど、そのファイルが存在しなかった。
0で割ろうとした（これは数学的に禁止されています）。
こうした状況が起きたとき、プログラムは通常止まってしまいますが、例外処理を使うことで、エラーに対処しながらプログラムを続けることができます。

3. 例外処理（try ~ catch）
Javaでは、エラーが発生する可能性がある部分に対して、tryブロックと**catchブロック**を使って、エラーが起きたときにどう対処するかを指定します。

tryブロックには、エラーが起こるかもしれない処理を書きます。
catchブロックには、エラーが起きたときにどうするかを書きます。
```Java
コードをコピーする
try {
    int result = 10 / 0;  // ここでエラーが発生
} catch (ArithmeticException e) {
    System.out.println("エラー：0で割ることはできません");
}
```
このように、エラー（例外）に備えて事前に対処法を決めておくことができます。

4. 例外の継承関係
継承は、プログラムのクラスが他のクラスの特性を受け継ぐことです。例外もクラスとして定義されており、例外の種類ごとにクラスがあります。たとえば、次のような継承関係があります：

Exceptionクラスが全ての例外の親クラスです。
ArithmeticException（数学的なエラー）
IOException（ファイル入出力エラー）
これによって、複数の種類のエラーをまとめて処理することができるのです。

5. 非チェック例外
非チェック例外は、コンパイル時にはチェックされない例外のことです。つまり、プログラムを書く段階では注意を払わず、実行して初めてエラーに気づくものです。

例：ArithmeticExceptionやNullPointerExceptionなどは非チェック例外で、プログラムを書いている段階でチェックされません。
例：
```Java
int a = 0;
int result = 10 / a;  // ここで実行時に例外が発生（非チェック例外）
```
6. 例外を投げる
Javaでは、プログラムの中で自分で意図的に例外を発生（投げる）させることができます。これを例外を投げる（throwする）と言います。
```Java
if (age < 0) {
    throw new IllegalArgumentException("年齢は0以上でなければなりません");
}
```
この例では、年齢がマイナスになっている場合に、プログラムの中で例外を意図的に投げています。これにより、間違った入力があったときに適切にエラーを処理することができます。

まとめ
例外とは：プログラムがエラーに遭遇したときに発生する問題。
例外処理（try ~ catch）：エラーが起きてもプログラムが止まらないように対処する仕組み。
例外の継承関係：例外もクラスとして定義され、親子関係がある。親クラスを使って、複数の種類の例外をまとめて処理できる。
非チェック例外：コンパイル時にチェックされない例外。プログラム実行中に発生する。
例外を投げる：自分で意図的にエラーを発生させることで、プログラムの流れをコントロールする。

今回は例外について記事にしてみました。
Java基礎文法Part5

今回はJavaのfor文について記事にまとめました。

構文
```Java
for(初期化;条件式;後処理){
   　繰り返す処理

　}
```
. 反復処理の制御構文
反復処理（はんぷくしょり）とは、同じことを何度も繰り返して実行することです。たとえば、10回ジャンプするとか、毎朝同じ時間に起きるのも反復処理です。プログラミングでは、同じ動作を何度も繰り返すために、ループと呼ばれる仕組みを使います。

制御構文は、プログラムがどう動くかを決めるルールです。例えば、for文やwhile文というものを使って、何度も繰り返すことができます。

例:
```Java
for (int i = 0; i < 5; i++) {
    System.out.println("こんにちは");
}
```
このプログラムは、「こんにちは」を5回表示します。

2. インクリメント
インクリメントは、数を1ずつ増やすことです。たとえば、「1, 2, 3, 4」と数を増やすときに使います。

例:
```Java
int x = 0;
x++; // xは1になる
x++; // xは2になる
x++と書くことで、xの値を1増やすことができます。
```

3. for文のネスト
ネストとは、プログラムの中にさらに別のプログラムを入れることです。for文のネストは、for文の中にさらにfor文を入れて、二重に繰り返し処理を行うことです。たとえば、外側のループで行の数を数え、内側のループで列の数を数えることができます。

例
```Java
for (int i = 0; i < 3; i++) { // 外側のループ
    for (int j = 0; j < 2; j++) { // 内側のループ
        System.out.println("ループ");
    }
}
```
このプログラムは、計6回「ループ」と表示します。

4. 変数の有効範囲（スコープ）
スコープとは、変数が使える範囲のことです。プログラムのどこで変数が使えるかを決めるルールです。変数は、作られた場所（関数やループの中）だけで使うことができ、その外では使えません。

例:
```Java
{
    int a = 5; // この中だけでaは使える
    System.out.println(a); // ここではaが使える
}
System.out.println(a); // ここではaは使えない
上の例では、aという変数は中括弧の中だけで使えます。外に出ると使えなくなります。
```

5. ループ制御（break, continue）
ループの中で、途中で処理を止めたり、次の繰り返しにスキップしたりする方法があります。

break：ループを途中でやめて、次の処理に移ります。
continue：今の繰り返しをスキップして、次の繰り返しに進みます。
例:
```Java
for (int i = 0; i < 5; i++) {
    if (i == 2) {
        break; // iが2のときにループを止める
    }
    System.out.println(i);
}
```
このプログラムは、「0」「1」と表示した後、iが2になるとループが止まります。

6. ループ処理と配列の相性
配列とは、複数のデータをまとめて一つの変数に入れておくものです。たとえば、5つの数字を持っている配列があったとします。ループを使うと、配列の中のデータを1つずつ取り出して使うことができます。配列とループは相性が良く、よく一緒に使われます。

例:
```Java
int[] numbers = {10, 20, 30, 40, 50};
for (int i = 0; i < numbers.length; i++) {
    System.out.println(numbers[i]);
}
```
このプログラムは、配列の中の数字を1つずつ取り出して表示します。

7. 拡張for文
拡張for文は、配列やリストの中のデータを簡単に1つずつ取り出すためのループです。普通のfor文よりも簡単に書けます。

例:
```Java
int[] numbers = {10, 20, 30};
for (int num : numbers) {
    System.out.println(num);
}
```
このプログラムは、配列の中の数字を1つずつ取り出して表示します。

for文について記事にしてみました。
PythonでLINEチャットボットを作る

過去に作ったLINEチャットボットを今風にリメイクしたものを投稿します。

### 環境：
　ローカル、Linux(Ubuntu)、python3.12.6

### １．LINE Developersアカウントの作成:
https://developers.line.biz/ja/ にアクセスしてアカウントを作成する。
（今回はビジネスアカウントを作成）

### ２．プロバイダーの作成
コンソールにログインし、「新規プロバイダー作成」を選択。

### ３．チャットボット（LINEチャネル）の作成
チャネル設定から MessageAPI を選択して各種設定する
※執筆時点でLINE公式アカウントの作成が必要になっているため、画面の指示に従う。

### ４．Message API設定
LINE公式アカウントを作成して、MessageAPIを有効にする。詳しくは以下を参照。
https://developers.line.biz/ja/docs/messaging-api/getting-started/
有効にしたら、URLとアクセストークンを取得する。
また、Webhookを使うのでそちらも有効にする。

### ５．Pythonプロジェクトのセットアップ
必要なライブラリをインストールする。LINEのSDK(line-bot-sdk)を使用する。
```
pip install line-bot-sdk
```

### ６．Pythonコードの記述
```python
from flask import Flask, request, abort
from linebot import (
    LineBotApi, WebhookHandler
)
from linebot.exceptions import (
    InvalidSignatureError
)
from linebot.models import (
    MessageEvent, TextMessage, TextSendMessage,
)

app = Flask(__name__)

line_bot_api = LineBotApi('YOUR_CHANNEL_ACCESS_TOKEN')
handler = WebhookHandler('YOUR_CHANNEL_SECRET')

@app.route("/callback", methods=['POST'])
def callback():
    signature = request.headers['X-Line-Signature']
    body = request.get_data(as_text=True)
    app.logger.info("Request body: " + body)

    try:
        handler.handle(body, signature)
    except InvalidSignatureError:
        abort(400)
    return 'OK'

@handler.add(MessageEvent, message=TextMessage)
def handle_message(event):
    line_bot_api.reply_message(
        event.reply_token,
        TextSendMessage(text=event.message.text)
    )

if __name__ == "__main__":
    app.run()
```

### ７．Webhook URLの設定とボットのテスト
Flaskを実行する。ローカル実行ではngrokを使うと良いと思う。
公開URLを取得してLINE Developer コンソールでWebhook URLとして設定する。
※ngrokについては今回は割愛

LINEアプリで友だち追加したチャットボットにメッセージを送信して動作確認を行う。

以上で基本的なLINEチャットボットの作成は完了。
カスタマイズしたい場合、例えば以下の機能があるが詳細はドキュメントを参照願いたい。

リッチメニュー、プッシュメッセージの送信、データベースの導入など…



実務的には、AWSの場合、APIGateway、Lambda、S3を使うのが現実出来ではあると思うので、Lambdaのコードを以下に追記しておく。
```python
import json
from linebot import (
    LineBotApi, WebhookHandler
)
from linebot.exceptions import (
    InvalidSignatureError
)
from linebot.models import (
    MessageEvent, TextMessage, TextSendMessage,
)

line_bot_api = LineBotApi('YOUR_CHANNEL_ACCESS_TOKEN')
handler = WebhookHandler('YOUR_CHANNEL_SECRET')

def lambda_handler(event, context):
    # eventオブジェクトからリクエストされた本文と署名を取得
    body = event['body']  # リクエストの本文
    signature = event['headers']['x-line-signature']  # LINEからの署名

    # Webhookからのイベントを処理
    try:
        handler.handle(body, signature)
    except InvalidSignatureError:
        return {
            'statusCode': 403,
            'body': json.dumps('Invalid signature. Please check your channel access token/channel secret.')
        }
    
    return {
        'statusCode': 200,
        'body': json.dumps('OK')
    }

@handler.add(MessageEvent, message=TextMessage)
def handle_message(event):
    line_bot_api.reply_message(
        event.reply_token,
        TextSendMessage(text=event.message.text)
```
Cato SASE CloudのSocketとvSocketの冗長方式の違い

## Socket/vSocketとは
Cato SASE Cloudにおけるコンポーネントで、オンプレミスやパブリッククラウドとCato SASE Cloudを接続する際に利用します。

オンプレミスにはSocketという小さなルータのようなものを設置してCatoと接続、
AWSやESXi環境等にはvSocketという仮想マシンをデプロイしてCatoと接続します。

## LAN側の筐体冗長の方式
Socket/vSocketともに可用性を高めるために冗長構成を組むことができるのですが、
自社検証環境で機能確認のため設定をしてみたところ、それぞれ方式に違いがあることがわかったため共有します。
**※文字ばっかりですみません。**

### Socketの場合
2台で冗長構成を組んでみたのですが、使われている機能としてはVRRPが動いています。
ただし細かい設定はできず、タイマー値の変更やPreempt設定はできません。(2024/9月現在)

### vSocketの場合
まず、ESXi版のvSocketではHA構成がサポートされていません。
とはいえvSphere HAがあるので他ホストに仮想マシンが移行された後は通信を継続することが可能かと思います。

vSocketではVRRPを使用しておらず、独自の機能で冗長構成を実現しているようです。
例えばAWS環境の場合、ルートテーブルを書き換えるために両方のvSocketにAPIでルートテーブルを変更するためのIAM権限が必要になります。


## 最後に
実際に検証を行うことでこういった細かな違いがわかり、実際の利用時にもためになると思い記録として残させていただきました。

特に日本のお客様はオンプレミス環境でPreempt設定を好む傾向があるかと思いますので注意が必要です。
ちなみに私はどっちがマスターで動いていてもいい派です:v_tone2: