OmniAICreator/Qiita-1.07M huggingface dataset:
完全匿名性SNS「痰壺」がないなって悲しかったので開発者の許可を得て作ってみた。

# 作らさせていただきました。

https://anonymous-sns.com/tan-tubo/
![ogp_org.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/111655/6d13ec5a-f5b1-eedb-89ff-8d05753199c0.png)

# 1.背景
以前に[ampersand](https://qiita.com/ampersand)さんが作った痰壺というWeb1.0を彷彿とさせるネットの掃き溜めSNS（誉め言葉）がありました。
※記事[匿名SNSを覚えたてのFirebaseで3時間で作ったらWeb1.0が爆誕した](https://qiita.com/ampersand/items/968ac952ddcc8dd30b87)


根暗で陰キャで変哲もない僕は、公のSNSでは言えない**誰にも伝わらなくてもいいけど声を大にして叫びたいこと**をよく書き込ませていただいていました。

そういうことってありませんか？　あ、ありませんよね、すみませんorz

でもスパムや真偽不明の個人情報などの投稿が相次ぎ、今後の事故を防ぐため閉鎖してしまいました。

で、悲しかったので、[ampersand](https://qiita.com/ampersand)さんのご許可を頂き精神的続編として作らさせていただきました。

# 2.全景
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/111655/7aef1c3d-7f00-65c3-03f4-f88d5c396bf2.png)

# 3.プログラムとして
テーマは**軽量**です。
デザインは前回をインターネットアーカイブから抽出してそのまま使ってます。
Firebaseなどと高級なものは扱えないローテクエンジニアなものでPHPで作りました。また、DBで作るとハッキングやらアップデートやら容量やらで面倒なので投稿はJSONで管理しています。

どのみちNoSQLも中身はJSONですしｗ

PHP+JSONなら引越しも楽ですし、ついでにドメインも取っちゃいました。

プログラム的な不正投稿を防ぐために**session_start**を使い、**token**を生成。
投稿時に**token**をチェックして、php直叩きで実行を防止してます。
一応、こんな感じです。

```php
session_start();
if (empty($_SESSION['token'])) {
    $_SESSION['token'] = bin2hex(random_bytes(32));
}
$token = $_SESSION['token'];
```

そして前回は「問い合わせ」がありませんでした。どこまで対応できるかはわかりませんが面倒なのでgoogleフォームで作りました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/111655/53a67882-23c4-a59b-f708-a9a14f3ac0c4.png)

# 4.進化した部分
OGP画像を作成しました。
こちらは、Canvaの画像生成AIを使っています。
あと、コード自体もChatGPTと相談しながら作りました。
他にもセキュリティ対策はしていますが、**秘密**です。

# 5.今後の課題
[ampersand](https://qiita.com/ampersand)さんにこれまでの運営もお聞きしながら作ってやはり掲示板として**削除義務**、**発信者情報開示義務**など責任が伴うことを改めて痛感しました。

ただ好きでやれるものでもないのだと、その点においては某ひろゆきさんはすごいなぁと思います。

また、まだ全然検討の域をでませんが、良い事しか書けない掲示板もあってもいいのかなぁと思ってます。
そうした場合は、攻撃的な投稿をAPIで判別して書き込めないようにするなど考えられます。
一応、[Perspective API](https://www.perspectiveapi.com/intl/ja_jp/)なる代物があるようで、興味をそそられています。
しかしテストした感じではまだまだ日本語対応には難があり、攻撃コメントの判定が鈍かったです。

あなたも**誰にも伝わらなくてもいいけど声を大にして叫びたいこと**がありましたらXの愚痴垢ではなく、こちらへと。
少しでもここに愚痴が集まることで、気持ちの整理ができて、その他のSNSが綺麗なればと思います。

以上、よろしくお願いいたします。
Object.○○()の使い方

# Object.○○()

### 1. Object.keys(オブジェクト名)
指定したオブジェクトの各プロパティのキーを配列で取得できる

### 2. Object.values(オブジェクト名)
指定したオブジェクトの各プロパティの値を配列で取得できる

### 3. Object.entries(オブジェクト名)
指定したオブジェクトのプロパティを配列で取得できる


```js
let person = {
  name: 'Takuto',
  gender: '男性',
  age: 20,
};

console.log(Object.keys(person));
// ['name', 'gender', 'age']

console.log(Object.values(person));
// ['Takuto', '男性', 20]

console.log(Object.entries(person));
// [['name', 'Takuto'],['gender', '男性'],['age', 20]]
```
* * *
### Object.○○()を使ってfor...in文をfor...of文で記述

```js
let person = {
  name: 'Takuto',
  gender: '男性',
  age: 20,
};

//perosnの各プロパティのキーを出力
//for in の場合
for(const key in person){
  console.log(key);
}

//for of の場合
for(const value of Object.keys(person)){
  console.log(value);
}

// name
// gender
// age


//perosnの各プロパティの値を出力
//for in の場合
for(const key in person){
  console.log(person[key]);
}

//for of の場合
for(const value of Object.values(person)){
  console.log(value);
}

// Takuto
// 男性
// 20
```
onClick={handleClick}とonClick={() ⇒ handleClick()}の違い

# はじめに
基本的なことなのですが、アプリ開発をしているときに何度か混乱して間違えてしまい、解決に時間を要することがあったのでここにまとめておきたいと思います。

何かというと、よく出てくるイベントハンドラのonClickで定義した関数は以下の3つの定義方法があります。

```js
(1) <button onClick={handleClick()}>クリック</button>

(2) <button onClick={handleClick}>クリック</button>

(3) <button onClick={() => handleClick()}>クリック</button>

```

この違いを説明できますか？

# (1)について
上記3つの中で(1)は即座に実行がされてしまいます。
ですので、ページを再読み込みすると関数が実行されて戻り値がイベントハンドラに渡されてしまいます。

なぜかというと、JavaScriptのルールとして、関数名の後に括弧をつけると、それは関数を即座に「実行」するという命令になるからです。


はい、続きを書かせていただきます：

# (2)について
(2)は関数の参照を渡しているだけなので、クリックされるまで実行されることはありません。
新しい関数を作成する必要がないためメモリ効率が良く、引数を渡す必要がない場合はこちらの書き方が推奨されると思います。
デメリットとしては、引数が渡せないと言う部分になります。

# (3)について
アロー関数で関数をラップしている形になります。
この方法では、レンダリングのたびに新しい関数が作られてしまうので、(2)と比べると若干パフォーマンスが落ちてしまいます。
ただ、引数を渡す場合は基本的にこちらの書き方が必須になってくると思います。

```js
<button onClick={() => handleClick(123)}>クリック</button>
```

# おわりに
開発をしている中で(1)の書き方をしてonClickのイベントハンドラを発火させても何も起こらなかったりり、(2)と(3)の違いを忘れてしまってどのように選択すべきか迷うことが何度かありました。
この記事の内容を見返して知識を定着させていきたいと思います。
皆様の参考になれば幸いです！


# JISOUのメンバー募集中！

プログラミングコーチングJISOUでは、新たなメンバーを募集しています。
実践的なカリキュラムで、あなたのエンジニアとしてのキャリアを最短で飛躍させましょう！
興味のある方は、ぜひホームページからお気軽にカウンセリングをお申し込みください！
▼▼▼

https://projisou.jp/
東大・東京工大・筑波大などが提供している無料講座まとめ

# はじめに
個人開発をしている中で大学が無料で提供している公開講座が多くあることに気づいたので共有したいと思います。

# 東京大学
UTokyo OpenCourseWare (OCW)を通じて、豊富な講座が無償で提供されています。

https://ocw.u-tokyo.ac.jp/

## 特徴
- 1,400を超える講義資料や映像を無料公開
- 文系・理系問わず幅広い分野の講座を提供
- プログラミングからデータサイエンスまで、IT関連の充実した講座群を提供

## 人気の講座
### プログラミング入門
- Pythonを使用した基礎的なプログラミング演習
- 初学者向けの丁寧な解説

### データサイエンス・AI入門
- 機械学習の基礎概念
- 実践的なデータ分析手法

# 東京工業大学
## 特徴
- edXプラットフォーム「TokyoTechX」での体系的な学習環境を提供

https://www.edx.org/school/tokyotechx

※英語で提供されているコースになります！


# 筑波大学
- オープンコースウェアで充実した教材を提供
- プログラミング関連の実践的な講義資料が豊富
- エンジニアリングだけではなく、文系のコースなど幅広いラインナップ

https://ocw.tsukuba.ac.jp/

## 人気の講座

データサイエンス講座
https://ocw.tsukuba.ac.jp/data-science/

# 京都大学
- KyotoUx（edX）での先進的なオンライン講座
- コンピュータサイエンスや統計学の専門講座

https://ocw.kyoto-u.ac.jp/

# 東北大学
- データサイエンスと機械学習に特化した講座
- YouTubeでの分かりやすい解説動画

https://mooc.tohoku.ac.jp/

## 注目の講座
### 統計オープンデータ

https://lms.gacco.org/courses/course-v1:gacco+ga084+2025_01/about

総務省統計局が提供するデータサイエンス・オンライン講座の第3弾として、データの活用手法について分かりやすく解説する講座。

# その他のプラットフォーム

## JMOOC（日本オープンオンライン教育推進協議会）
https://www.jmooc.jp/
- 国内大学・企業による質の高いオンライン講座
- 修了証発行可能な本格的な学習プログラム
- IT・プログラミング分野の充実したコンテンツ

## 放送大学OCW

https://www.ouj.ac.jp/reasons-to-choose-us/ocw/

- 体系的な教養教育からIT専門講座まで
- 実践的なプログラミング学習が可能
- オンデマンドで自分のペースでの学習が可能

## 注目の講座
### Webのしくみと応用

https://v.ouj.ac.jp/view/ouj/#/navi/vod?caa=1570358p

少し見ていましたがかなり詳しく良質な情報が提供されているので、本を読む時間がないけどインプットしたい方にはおすすめです！


# まとめ
普段はYoutubeやUdemyを利用することが多いのですが、大学が無料で提供している講座は質が確保されているので、良質な教材を探している時には利用したいなと思います。
皆様の参考になれば幸いです！


# JISOUのメンバー募集中！

プログラミングコーチングJISOUでは、新たなメンバーを募集しています。
実践的なカリキュラムで、あなたのエンジニアとしてのキャリアを最短で飛躍させましょう！
興味のある方は、ぜひホームページからお気軽にカウンセリングをお申し込みください！
▼▼▼

https://projisou.jp/
wRPCはWITを用いたRPC

[wRPC](https://github.com/bytecodealliance/wrpc) はインターフェース定義に WIT[^0]を用いた RPC 技術で、通信プロトコルは現時点で TCP, UNIXドメインソケット, QUIC, NATS に対応しているようです。

[^0]: Wasm Interface Type

WebAssembly に特化したものではなく、今のところ次のような使い方があります。

||実装方法|実行方法|
|---|---|---|
|a|WebAssemblyコンポーネント|wRPC対応ランタイムによる実行|
|b|ネイティブwRPCアプリケーション（RustやGoで実装）|直接実行|

```a``` は通信プロトコル部分をランタイムが適用し、```b``` は通信プロトコル部分も実装するようです。

ここでは wrpc v0.14.0 を使って ```a``` の方法を試してみました。

# インストール

下記のようにインストールする事で wRPC 対応ランタイムの ```wrpc-wasmtime``` コマンドが使えるようになります。

```sh:インストール例
$ cargo install wrpc
```

# WebAssembly コンポーネント作成

wRPC のサーバー側とクライアント側の WebAssembly コンポーネントをそれぞれ作成します。

名前はとりあえず ```server``` と ```client``` にしました。

## サーバー側

まずは WIT を定義します。
```add``` 関数を wRPC で呼び出すように export します。

```js:wit/world.wit
package local:sample;

interface handler {
    add: func(a: s32, b: s32) -> s32;
}

world server {
    export handler;
}
```

この WIT に基づいた WebAssembly コンポーネントを [wit-bindgen](https://github.com/bytecodealliance/wit-bindgen) で実装しました。

```rust:src/lib.rs
mod bindings {
    use crate::Handler;

    wit_bindgen::generate!({
        with: {
            "local:sample/handler": generate,
        }
    });

    export!(Handler);
}

struct Handler;

impl bindings::exports::local::sample::handler::Guest for Handler {
    fn add(a: i32, b: i32) -> i32 {
        a + b
    }
}
```

Cargo.toml は次のような内容となっています。

```toml:Cargo.toml
[package]
name = "server"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = ["cdylib"]

[dependencies]
wit-bindgen = "0.35"
```

### ビルド

WASI は使っていないので、ここでは ```wasm32-unknown-unknown``` でビルドします。

```sh:ビルド例
$ cargo build --release --target wasm32-unknown-unknown
```

なお、```wasm32-wasip2``` でビルドしても問題はありません。

## クライアント側

クライアント側の WIT では ```add``` 関数を import します。

```js:wit/world.wit
package local:sample;

interface handler {
    add: func(a: s32, b: s32) -> s32;
}

world client {
    import handler;
}
```

```add``` 関数を呼び出して結果を出力するだけの処理を実装してみました。

```rust:/src/main.rs
mod bindings {
    wit_bindgen::generate!({
        with: {
            "local:sample/handler": generate,
        }
    });
}

fn main() {
    let a = 2;
    let b = 3;

    let r = bindings::local::sample::handler::add(a, b);

    println!("{a} + {b} = {r}");
}
```

```toml:Cargo.toml
[package]
name = "client"
version = "0.1.0"
edition = "2021"

[dependencies]
wit-bindgen = "0.35"
```

### ビルド

```println!``` 等の使用有無に関わらず、クライアント側は WASI が必要そうなので[^2]```wasm32-wasip2``` でビルドします。

[^2]: wasm32-unknown-unknown でビルドした結果を wrpc-wasmtime run で実行してみたところエラーが発生した

```sh:ビルド例
$ cargo build --release --target wasm32-wasip2
```

# 動作確認

通信プロトコルは TCP で実行してみます。

まずは、```wrpc-wasmtime tcp serve``` コマンドでサーバー側を実行しておきます。

```sh:server実行
$ wrpc-wasmtime tcp serve ./target/wasm32-unknown-unknown/release/server.wasm
 INFO wrpc_wasmtime_cli: serving instance function name="add"
```

```wrpc-wasmtime tcp run``` コマンドでクライアント側を実行するとこのような結果となりました。

```sh:client実行
$ wrpc-wasmtime tcp run ./target/wasm32-wasip2/release/client.wasm
2 + 3 = 5
```
Windows と TypeScript ではじめる Symbol 通信 10【アナウンス2】


## ノードへパーシャルトランザクションをアナウンス

パーシャルのパケットタイプ(PUSH_PARTIAL_TRANSACTIONS)は、`0x100`です。

```typescript:Catapult.ts
/** パケットタイプ */
private PacketType = {
  CHAIN_STATISTICS: 0x0_05,
  PUSH_TRANSACTIONS: 0x0_09,
  PUSH_PARTIAL_TRANSACTIONS: 0x1_00,
  NODE_DISCOVERY_PULL_PING: 0x1_11,
  TIME_SYNC_NETWORK_TIME: 0x1_20,
  FINALIZATION_STATISTICS: 0x1_32,
  UNLOCKED_ACCOUNTS: 0x3_04,
}

/**
 * アグリゲートボンデッドトランザクションアナウンス
 * API必要
 * @param payloadHex トランザクションペイロード
 * @returns
 */
async announceTxPartial(payload: Uint8Array): Promise<boolean> {
  console.info('announceTxPartial')
  try {
    await this.request(
      this.PacketType.PUSH_PARTIAL_TRANSACTIONS,
      payload,
      false
    )
  } catch {
    return false
  } finally {
    this.close()
  }
  return true
}
```

## レスポンスデータの解析

レスポンスはありません。
トランザクションのエラー確認は、WebSocket か ZeroMQ の Status を参照する必要があります。
Curlコマンドのメモ

アプリ開発をしている時、そのアプリ以外でAPIを叩くことがありましたので、メモを残す。以下はCurlでAPIを叩いた時のコマンドになる。

```
curl [URL] -H [ヘッダー情報] | jq
```

`[URL]`に接続するURLを記述し、`[ヘッダー情報]`にトークン情報などをのせる。jqコマンドを使用すれば、レスポンスのJSONが整理され、見やすくなる。

```
curl -O [URL.png] -H [ヘッダー情報]
```

画像ファイルなどアクセス先のデータをダウンロードすることがある場合は上記のように`-O`オプションを記述する。
アップロードは`-T`、認証は`-u`、ボディ情報は`-d`オプションを使用する。


#### 参考
https://qiita.com/fy406/items/6dd2de050e875e52d36a
Calendar 投稿記事の方針 Qiita(258)

Calendar投稿記事の方針

「三方よし」への三つのあり方 仮説(247)
https://qiita.com/kaizen_nagoya/items/ad4ffd6d8a4045d1117a

# 世の中の役に立つ

パスワードは同じ媒体を使わないで
https://qiita.com/kaizen_nagoya/items/c3103f5ade4da3ce5d19

git連携すればO.K.
https://qiita.com/kaizen_nagoya/items/856554693c0fa802784d

# 対象読者の役に立つ
CAN Stressとか、CAN対応オシロスコープの管理者をしていた。
実際には、あまり直接操作していない。関連資料は、特定の人たちを想定して提供していきたい。

CANを極めるには
https://qiita.com/kaizen_nagoya/items/f7738d6d0940e4732789

Day 1 Udemy 車載LAN入門講座 LIN通信編
https://qiita.com/kaizen_nagoya/items/65681d1c59ad11a6ee13

Qiitaの初投稿の記事を参照し、Qiitaに初投稿する人に伝えたいことを描き始めた。

<この項は書きかけです。順次追記します。>
This article is not completed. I will add some words and/or centences in order.

初投稿を思い出しながら
https://qiita.com/kaizen_nagoya/items/c3515b2102de7a88dba8

# 自分の役に立つ
URLが、文字で表示されていない資料は、個人的に使いづらい。
そこへ飛ぶかどうかは、そのURLによりけり。Qiitaであれば作者がわかる。
資料であれば、htmlかpdfかがわかると飛ぼうかどうしようかの決断が早くなるかも。

LLM コミュニティ参加記録
https://qiita.com/kaizen_nagoya/items/4840ae09437f1e9df74b

過去の資料の見直し。何年か前の資料のURLの更新も時々している。
あるまとまった量だと別記事にして、なにが確認できたかが見てわかるようにする。

Pytorch復習
https://qiita.com/kaizen_nagoya/items/b61dd92d5686dca37af3
Chromeの拡張機能でDebate Motionsを強化してみた話(ローカルの画像を表示できた話)

## 概要

Chromeの拡張機能で特定のページに役立つ画像を好きなレイアウトで表示する方法をご紹介します．

https://github.com/Non2d/raw-js-chrome-extension

## きっかけ

私は，[Debate Structure Visualizer](https://vps4.nkmr.io/debates)という競技ディベートの反論のグラフ構造の可視化と試合動画の探索を掛け合わせたサービスを運用しています（今んところ来訪者数＝実験参加者数ですが運用は運用ですよね...?）．

そんな中で，最近，[Debate motions](https://debatedata.io/)という大会や各ラウンドのモーション（論題），YouTube動画のリンクに，生成AIが考えた論点や主張など，ディベートに役立つ情報をふんだんにまとめたライバル（勝手に自分がそう思ってるだけ）を見つけました．

これらのサービスを組み合わせたら面白いのでは？と思い，Chrome拡張機能として合体させるようと考えました！

.....この話に共感できる人いないよな....競技ディベート広まれ....

## この記事が役立つ人
ディベートなんて知らないよ！って人もまだブラウザバックしないでください！要はこの記事，Chrome拡張機能で画像を表示させてみたってだけの話なので...! Web開発なら知ってるけど，拡張機能作るの面倒...と思ってるあなたにも役立つはずです...!

ただ，Chrome拡張機能開発の基礎中の基礎は[とほほ様の記事](https://www.tohoho-web.com/ex/chrome_extension.html)がすごく分かりやすいので，0から始めたい方は是非こちらをご参照ください．この記事はあくまで，「ローカルに保存した画像はどう表示すればいいの？」って方向けです．

## 前置き長くなりましたが本題
Chrome拡張機能はmanifest.json(設定ファイル)+jsで作れます．Reactとか使ってみようかな？とかも考えたのですが，とりあえずバニラで，まずは肌感覚掴んでみるかの精神で．

今回，使用する画像(反論グラフのスクショ)は拡張機能内に入れています．このローカルに画像があるパターンでは，普通のweb開発と同じように`<img src={"https:/~~"}>`とか，`<img src={images/test.png}>`とかって書くだけでは表示できません．インターネット経由だとほぼweb開発と同じノリなんですけどね．

ポイントはふたつあります．

１．manifest.jsonで許可すべし

２．chrome.runtime.getURLを使うべし

それぞれ見ていきましょう．

## 前提：ディレクトリ構造
```text:directory
.
├── content.js
├── images
│   ├── debate-graph-001.png
│   ├── debate-graph-002.png
|   ├── debate-graph-003.png
│   └── ...
├── manifest.json
└── popup.html //全く使っていません
```

## manifest.jsonで許可すべし
このもとで，まず[公式リファレンス](https://developer.chrome.com/docs/extensions/reference/manifest/web-accessible-resources?hl=ja)を見ながら，表示を許可する画像を画像を指定します．今回はimagesディレクトリ直下にすべての画像ファイルを置く想定です．
```json:manifest.json
"web_accessible_resources": [
    {
      "resources": ["images/*"],
      "matches": [ "<all_urls>" ]
    }
  ]
```
これがstep1です．なお，2024年11月24日現在，manifest_versionは3にしないとChromeが怒っちゃうので，3に設定している前提で説明します．

## chrome.runtime.getURLを使うべし

```js:content.js
const graphContainer = document.createElement("div");
document.body.appendChild(graphContainer);

const imageUrls = [
    "images/debate-graph-001.png",
    "images/debate-graph-002.png",
    "images/debate-graph-003.png",
    ...
]

imageUrls.forEach((url) => {
  const img = document.createElement("img");
  img.src = chrome.runtime.getURL(url); // https://~では使えないので注意
  img.style.○○ = ××; // スタイリング系
  ...
  graphContainer.appendChild(img);
}
```

このように，imageUrlを直接指定するのではなく，chrome.runtime.getURLを使ってsrcを指定しましょう．

## おわりに

かなりかいつまんで説明しましたので，気になる点があったらぜひ冒頭のGitHubリポジトリを参照してください！
Chakra UI v3のトーストの使い方

## 概要
Chakra UI v3からトーストの使い方が大きく変わり、実装するのに苦労したので備忘録として紹介します。

## 参考
公式ドキュメント
https://www.chakra-ui.com/docs/components/toast

## 手順
1. `npx @chakra-ui/cli snippet add toaster`でtoasterスニペットを追加（私の環境ではnpmでインストールを実行）
2. プロジェクトのルートコンポーネントに`import { Toaster } from "@/components/ui/toaster"`と`<Toaster />`を記述しレンダリングする

    ```react
    // <例>
    import { Box } from '@chakra-ui/react';
    import { Router } from './router/Router';
    import { BrowserRouter } from 'react-router-dom';
    import { Toaster } from "@/components/ui/toaster"; // インポート

    function App() {

      return (
        <Box bg="gray.100" color="gray.800" height="100vh">
          <BrowserRouter>
            <Toaster /> // Toasterをレンダリング
            <Router />
          </BrowserRouter>
        </Box>
      )
    }

    export default App
    ```
3. ボタンコンポーネントファイルに`import { toaster } from "@/components/ui/toaster"`を記述し、onClickに`toaster.create`の設定を記述する

    ```react
    // 例
    "use client"

    import { Button } from "@chakra-ui/react"
    import { toaster } from "@/components/ui/toaster" // インポート

    const Demo = () => {
      return (
          <Button
            size="sm"
            variant="outline"
            onClick={() =>
              toaster.create({
                title: `Toast status is success`, // トースターのメッセージ
                type: "success", // トースターの見た目のタイプ指定
              })
            }
          >
            success
          </Button>
      )
    }
    ```
![スクリーンショット 2024-11-24 18.25.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3289947/2c3ee7d8-8848-0646-39ca-047a1af98638.png)

画面右下にスクショのようなトースターが表示されればOK

## トースターの表示位置のカスタマイズ
デフォルトではトースターの表示は画面右下になります

画面の真上にカスタマイズしたい場合の例を紹介します

手順：
1. `src/components/ui/toaster.tsx`を開く
1. placementをデフォルトのbottom-endからtopに変更

    ```diff_typescript
        export const toaster = createToaster({
    -        placement: "bottom-end",
    +        placement: "top",
          pauseOnPageIdle: true,
        })
    ```

3. ヘッダーがある場合は、ヘッダーにトーストが隠れる可能性があるのでzIndexのstyleを付与する

    ``` diff_typescript
    export const Toaster = () => {
      return (
        <Portal>
    -      <ChakraToaster toaster={toaster} insetInline={{ mdDown: "4" }}>
    +      <ChakraToaster toaster={toaster} style={{ zIndex: 9999 }} insetInline={{ mdDown: "4" }}>
    ```

![スクリーンショット 2024-11-24 18.49.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3289947/b28aa658-5899-1bd3-e13d-4bdf52ff853b.png)

画面の真上にトーストの表示をカスタマイズすることが出来た

## まとめ
Chakra UI v3のトーストは以前のuseToastを使った実装法と大きく変わっており、公式ドキュメントを見ながら試行錯誤して実装しました。

しばらくChakra UI v3の変更点でつまずくシーンは多くなりそうですが、公式ドキュメントの読解力も上げていきたいです。
TDD(テスト駆動開発)の勉強会に参加してきた

# はじめに
JISOUの勉強会でテスト駆動開発(以下TDD)というのを体験したので簡単にまとめようと思います。個人やインターンではやったことがない手法だったのでかなり難しかったです。
勉強会は最初に以下のtdd boot camp 2020 onlineという動画を視聴し、その後apiからデータを取得する簡単なアプリをTDDでペアプロしながら作っていくという流れでした。

https://www.youtube.com/watch?v=Q-FJ3XmFlT8

# TDD(テスト駆動開発)とは？
Test Driven Developmentの略称で、従来の**設計→実装→テスト**ではなく**テスト→実装→リファクタリング**を繰り返していくプログラム開発手法のこと。テストから書くことで仕様を深く理解できたりバグを早い段階で見つけられるのでプロダクトの品質が高まるというメリットがあるが、慣れるまでに時間がかかることや開発コストが大きくなるなどのデメリットもある。

# TDDの基本的な流れ
以下tdd boot camp 2020 onlineで紹介されていたTDDの基本サイクルです。
1. 次の目標を考える
2. その目標を示すテストをかく
3. そのテストを実行して失敗させる(<font color="red">Red</font>)
4. その目的のコードを書く
5. 2で書いたテストを成功させる(<font color="green">Green</font>)
6. テストが通るままでリファクタリングを行う(<font color="blue">Refactor</font>)
7. 1~6を繰り返す

勉強会ではjson placeholderからpostsのデータを取得してきて表示させるという簡単目な実装をTDDで行いました。長くなるので今回はタイトル表示の部分だけを基本サイクルに沿って行なっていきます。

https://jsonplaceholder.typicode.com/posts
### 1. 次の目標を考える
ここでは実装する機能をTODO項目にしていきます。実装の順番としてはテスト容易性が高いものから進めていきます。最初はどのようにTODO項目を作ったりテスト容易性が高いものを判断するのかが難しいですが、ここは設計の技術書を読んだりとにかく経験を積むのが大事みたいです。

今回TODOは以下のようになりました(かなり省略していますが)。
- [ ] ユーザーはwebサイトのタイトルを見ることができる
- [ ]  ~~ユーザー投稿の一覧をみることができる~~
    - [ ]  ~~ユーザーは投稿のタイトルを見ることができる~~
    - [ ]  ~~ユーザーは投稿の本文を見ることができる~~

:::note info
「json placeholderの/postsから全データを取得する」ではなく「ユーザーは投稿の一覧を見ることができる」のようにユーザー視点で書かれた説明のことを**ユーザーストーリー**というみたいです。
:::

### 2. その目標を示すテストをかく
「ユーザーはwebサイトのタイトルを見ることができる」というテストをJestで書いてみます。実装はReactで行います。
```demo.spec.tsx
describe("ユーザーはwebサイトのタイトルを見ることができる",()=>{
    test("「demo app」というタイトルが表示されている",()=>{ 
        render(<App />)
        expect(screen.getByTestId("title")).toHaveTextContent('demo app');
    })
})
```

### 3. そのテストを実行して失敗させる(<font color="red">Red</font>)
当たり前ですが2で書いたテストはまだAppの中身を実装していないのでエラーになります。このようにテストが通らないことをわかっていてエラーが出た場合**ナイスレッド**といいます。(何が原因でテストが通らないか予想してからテストを実行するといいらしい)

### 4. その目的のコードを書く
Appの中身を書いていきます。リファクタリングの工程は後にあるのでこの段階ではコードの綺麗さ等は特に気にしなくて良いです。
```App.tsx
function App() {

  return (
    <>
      <h1 data-testid="title">demo app</h1>
    </>
  );
}

export default App;
```

### 5. 2で書いたテストを成功させる(<font color="green">Green</font>)
これでテストがPASSするようになったと思います。このようにテストが通ることをわかっていてPASSした場合は**ナイスグリーン**といいます。

### 6. テストが通るままでリファクタリングを行う(<font color="blue">Refactor</font>)
テストが通る状態のままで実装コード、テストコードともに必要な箇所をリファクタリングしていきます(今回は特にないですが)。リファクタリング自体は無限にできてしまうので5分~10分と時間を決めてやるといいそうです。

### 7. 1~6を繰り返す
今回は単純な実装でしたのでこれで終わりなのですが、実際は以下のように1つの機能実装を細かいTODOに分けて実装していくので1~6のステップを繰り返していくことになります。
- [x] ユーザーはwebサイトのタイトルを見ることができる
- [ ]  ユーザー投稿の一覧をみることができる
    - [ ]  ユーザーは投稿のタイトルを見ることができる
    - [ ]  ユーザーは投稿の本文を見ることができる

# 気づき
### TDDは難しい
一言で感想を言うととにかく難しかったです。普通に実装から入ればすぐ終わりそうな要件でもテストから入ることでかなり苦戦しました(結局最後まで作れなかった)。個人で小さなアプリを作る分にはTDDは必要ないかなと思いましたが、実務規模の品質が大事になってくるアプリであれば重要になってくるのかと思います。

### ペアプロ楽しい
1対1のペアプロをあまりやったことがなかったので新鮮で楽しかったです。また1人でやるより作業に集中できますし、自分の知らないことも色々学べたのでまた機会があればやりたいです。(自分の技術力不足でペアの方には迷惑かけましたが😅)
# おわりに
ここに書ききれてないこともたくさんあるのですが、TDDの概要は大体書けたと思います。また必要に応じて深く学びたいと思います。(以下の本がおすすめみたい👇)

https://amzn.asia/d/3iWcaJ6
テスト結果のレポートの重要性を改めて考えてみた

# はじめに

テスト実行管理に関わるトピックとして、今回テスト結果を取り上げてみた。

自分は2016年にプロダクトマネージャからテストエンジニアにロールチェンジしました。
当初は、JSTQBも知らず、独学なやり方でQA組織の構築を行なってきた。
その中でも、テストレポートは当初テキトーでしたw

JSTQBやテストに関する書籍を通して、レポートの書き方、重要性を徐々に学び、経験してきた。
そこで、当時の過去の自分に教えたい、テストレポートの重要性を書き綴る。


# テストレポートとは

テストレポートは、ソフトウェアテストの実施結果をまとめたドキュメントです。
テストの目的、範囲、実施期間、発見された不具合、テスト結果の評価などを客観的に記録し、関係者に報告するために作成する。

テストレポートの主な目的として、5つ挙げる

## 1. テスト結果の記録と展開
テストの実施状況、発見された不具合、テストケースの結果などを客観的に記録し、将来の閲覧や分析に用いる。
開発チーム、プロジェクトマネージャー、顧客などステークホルダーに、テスト結果を正しく、わかりやすく展開する。

## 2. 品質評価
テスト結果に基づいて、ソフトウェアの品質レベルを評価し、リリースの可否を判断するための品質評価になる。

## 3. 改善点
潜在的な問題やリスクを早期に特定し、今後の改善に繋げる。
プロダクトの品質だけではなく、テストケース、テスト環境、テスト手法などの改善点を見つけることで、今後のテストプロセスの効率化にも貢献なる。
さらに、開発プロセスにおける問題点を洗い出し、開発プロセス全体の改善に繋がる場合もある。

## 4. 責任の所在の明確化
不具合が発生した場合、誰がいつどのようなテストを行ったのかを明確にすることで、原因究明や責任の所在を明らかにできる。

## 5. コミュニケーション
テスト結果を展開して、開発チームと顧客間のコミュニケーションを円滑にし、プロジェクトの成功に貢献する


テストレポートは、単なる報告書ではありません。
ソフトウェア開発の意思決定の材料になる。
これら目的を達成するために、正確かつ客観的な情報を盛り込み、ステークホルダーが理解できるようにすることが重要である。

# 不具合レポートの必要要素

不具合レポートは単に報告書ではありません。
レポートを受け取ったエンジニアが、短時間で状況を把握し、バグ修正に必要な情報がそろっていて、そのバグを直すように働きかけることが求められる。

レポートを書くにあたって、以下のことを意識するといい。

## 1.再現（発生頻度）	
バグがどの確率で発生するかを示す。
 エンジニアがバグの再現をするときに、発生頻度が低い場合バグ出ないと誤判断してしまうためである。
また、具体的な数字（５回中５回発生するのか、１００回中１回なのか）を記述すると、エンジニアは助かる。


## 2.分離（原因特定）	
一つのバグに対しテスト条件を変えてテストする。 
そこからバグの原因につながるような情報（限られた条件で発生する等）を導き出す
例えば、chromeで発生するが、firefoxで発生しない（ブラウザ依存のバグ）。
user Aで発生するが、user Bで発生しない（ユーザー依存のバグ）。　など


## 3.一般化（影響範囲）	
一つのバグに対して類似した処理が同様のバグを含んでいないかを確認する
2に近しいかもしれない。
例えば、商品Aの購入が失敗した場合、別の商品でも失敗（購入処理の大きな機能不具合であるか、データ依存なのか）しないかを確認する

## 4.比較	
旧バージョンのシステム、別の環境（iOS、Android等）で見つかったバグの再現性を確認する。
今回のバグがデグレ、特定環境でのみ起きる、既存バグであるかを確認することで、開発の対応判断の助けになる

## 5.要約	
バグの内容を簡潔に、いろいろなステークホルダに理解できるようにまとめる。 
バグ報告、分析等を行いやすくする
また、エンジニアは、長文やテストステップを全部読むと時間がかかってしまうため、１センテンスで何が起きたのかがわかると助かる。

## 6.凝縮・明確化	
不必要な情報、あいまいな情報を削る。 
報告されたバグの内容から発散しないように、誤解を招かないようにする
余計な情報を含むと、脱線して調査する可能性がある。

## 7.中立化
中立の立場からバグを客観的に報告する。 
プログラマ、システムの実装、コーディングの非難、バグ改修内容の提案などは避ける。
自分がレポートを受け取った時に、不愉快を感じないかを考えてみると良い。

## 8.プライオリティ
バグの重要性、優先度を定義する。 
このバグにより、ほかのテスト活動に影響の有無を伝える（例えば、これがブロッカーとなりほかのテストが実行できない等） 
また、このバグによりビジネスリスクがどの程度あるかを伝える

# 伝える技術

「ＢＣＧ流経営者はこう育てる」の書籍の中で、「伝えるから共有へ」というセンテンスがある。


```
Said　≠　Heard　　
（こちらが言ったからといっても、聞いてもらえたわけではない）

Heard　≠　Listened　　
（“聞いて”もらえたからといっても、“聴いて”もらえたわけではない）

Listened　≠　Understood　
（聴いてもらえたからといっても、理解してもらえたわけではない）

Understood ≠ Agreed　
（理解してもらえたからといっても、賛成してもらえたわけではない）

Agreed　≠ Convinced　
（賛成してもらえたからといっても、腑に落ちて納得し行動しようと思ってもらえたわけではない）
```

これは何を指しているかというと、不具合レポートに置き換えてみると、「不具合レポートを言った（作った）」からといって、エンジニアが納得して不具合改修へ行動してくれるわけではない」ということである。

先に記載したように、テストエンジニアは、不具合レポートを持って、エンジニアに不具合改修させることが、真に求められる活動である。

つまり、レポートを書くだけに終わらせてはいけない。
言った後に、エンジニアに動いてもらうために、どうするか、何が必要なのかを考えた上で、レポートを作成して展開する必要があると思う。

このことは、過去の自分に何回も言わせたことだと思う。

いくつもの不具合レポートで放置されたものがあったことかw


テストエンジニアには、レポートスキルだけではなく、そのレポートで人を動かすためのスキルも必要です。
茶色コーダーによるABC381振り返り


# はじめに

東京行ってきたんですけど(ABCの翌日)色んなとこ行って、楽しかったです

<details><summary>ほぼ初東京の田舎者の思い出話　全く関係ないので見なくていいです</summary>
長野駅から新幹線で、上野まで行ってとりあえず渋谷駅に行った。(軽井沢駅で30分足止め食らった)
ハチ公の前で写真取ろうとしたら、外国人がたくさんいて、しょうがないから尻尾のあたりに立って写真撮って、その後スクランブル交差点をわたった。

その後六本木ヒルズに行って、東京の都会ぶりを見渡す
まだ自分が住んでいるとこの標高より低いとはず(六本木ヒルズが)
で秋葉原に行って昼飯食って、VimConfがやってるみたいだから、会場の前で記念撮影した。
誰かしら有名人に会うかなーと思っていたんですけど、そんなはずはなく、誰にも会いませんでした
その後、秋葉原を散策して、でっかいヨドバシカメラに行って、HHKBを探したけどなかった

その後丸ノ内線乗って、国会議事堂前で写真取った一応、議事堂一周した。
で東京駅に行って、東京駅と皇居を外側から見渡す(東京から皇居まで往復1.2キロ)
そしてまた丸ノ内線乗って(丸ノ内線4回乗りましたw)西新宿に行って　都庁を見る(結構歩いた)
そして新宿駅まで歩いて、新宿駅の小田急で鳩サブレを買い
そして中央線に乗って東京に戻ってスタバで休憩して駅弁買って、帰りの新幹線の中で食べました(新宿駅の人混みがすごかった)

皆さん都会に、疲れたら田舎に来て休息しに来てください
長野県では冬ウィンタースポーツが盛んです。寒いですがぜひ来てください(寒いときは-10度行きます)

宣伝するな

</details>

やったー3完だけで緑perf、レート上がったラッキー(楽観的)
なんかABの実装に手こずり時間ロスして、Cはいい感じの解法が思いついたので、それをしたらACした
Dは尺取り法だってわかってたけど、実装ミスって最後までACできなかったです
さて本題

使っているライブラリ　前回から変えてません 参考にどうぞ
[github](https://github.com/hidehic0/my-kyopro-code/blob/main/Performance/.template/code/main.py)

# [A問題](https://atcoder.jp/contests/abc381/tasks/abc381_a)

問題文を要約
前半が1で、真ん中の文字が/で、後半の文字が2だと判定してくださいという問題

C問題と同じ趣旨らしい
普通に実装したけど、少し時間がかかった

acコード(ライブラリ抜粋) [全文](https://atcoder.jp/contests/abc381/submissions/60040608)
クソコードです 参考にしないでください

```py
N = ii()
S = s()

c = 0

for i in range(N):
    if S[i] == "1":
        c += 1
    else:
        break

if S[c] != "/":
    print("No")
    exit()

a = 0

for i in range(c + 1, N):
    if S[i] == "2":
        a += 1
    else:
        break

if c == a and c + a + 1 == N:
    print("Yes")
else:
    print('No')
```

# [B問題](https://atcoder.jp/contests/abc381/tasks/abc381_b)

問題文を要約...するのがめんどくさい

D問題と趣旨は似ていた

普通に実装してACした

acコード(ライブラリ抜粋) [全文](https://atcoder.jp/contests/abc381/submissions/60045986)

```python
S = s()
if len(S) % 2 != 0:
    print("No")
    exit()

D = defaultdict(int)

for s in S:
    D[s] += 1

for i in D.values():
    if i == 0 or i == 2:
        continue
    else:
        print("No")
        exit()

i = 1

while i <= len(S) // 2:
    if S[(2 * i) - 2] == S[(2 * i) - 1]:
        pass
    else:
        print("No")
        exit()
    i += 1

print("Yes")
```

# [C問題](https://atcoder.jp/contests/abc381/tasks/abc381_c)

問題文を要約
$S$の中からA問題の文字列の条件を満たす。**最大**の部分文字列の長さを求めてください

普通にやると$O(N^2)$でTLEするので、**/**を見つけたら左右に条件を満たさなくなるまで、左右を広げ続ければいい
そしてansを**更新**すればいい
計算量は$O(N)$です

acコード(ライブラリ抜粋) [全文](https://atcoder.jp/contests/abc381/submissions/60055136)

```python
N = ii()
S = s()

ans = 1

for i in range(N):
    if S[i] != "/":
        continue

    a, b = i - 1, i + 1

    while 0 <= a and b < N:
        if S[a] == "1" and S[b] == "2":
            a -= 1
            b += 1
        else:
            break

    if a == i - 1 and b == i + 1:
        continue
    ans = max(ans, (i - a) * 2 - 1)

print(ans)
```

# [D問題](https://atcoder.jp/contests/abc381/tasks/abc381_d)

問題文を要約
$A$の中からB問題の条件を満たす、最大の部分列の長さを求めてください

**尺取り法**までは行けたけど、色々ミスって結局ACできなかった

後日**尺取り法**で書いたら**AC**した

after-contest caseが大量に作成されていて笑った

# 最後に

レートは30ぐらい上がった。
やっと茶色上位勢になった

4完していたらもうちょっとperfが出ていたな、と思うとDの目の付け所を間違えた
二分探索法

# 結論
ソート済みの配列限定ではあるが，少ない計算量で値の有無を確認できる探索アルゴリズム
# 内容
### 前提条件
-　sort済みの配列であること
### 計算量
- O(longN)
```python
key = \\求めたい値
dp = []\\ソート済みの配列

right = len(dp)-1
 left = 0

while(right>=left)
{
 mid = left+ (right-left)/2 \\これは原点からrightの半分大きくなった長さ
 if(dp[mid]==key)return mid
 elif(dp[mid] > key)return right = mid -1
 \\左側に求める値があると推定
 elif(dp[mid] < key)return left = mid + 1
 \\右側に求める値があると推定
}
```
このコードの中で```elif```の部分で中央の要素番目を示す値に1という最小の変化を加えることにより，２文探索を行う範囲を決定している.
# 解釈
配列の中央の値をleft, rightのどちらかに設定するかで範囲を決めて数値を求めるアルゴリズム.
git mv を使ってGit管理下のファイルをリネームする方法

# はじめに
Gitを使用している際に、ファイル名を変更したいケースがあります。
本記事ではGitのインデックスに登録されてるファイルの名前を変更したい時に、コマンドを操作（`git add git rm` と `git mv` ）でファイル名をリネームする方法について記載します。


# ゴール
`git mv` でインデックスに登録されたファイルの名前をリネームする


# テストファイルの作成（準備）

テストあるファイルを作成しコミットまでやっておきます。

```bash
# テストファイルを作成
echo "これはサンプルテキストファイルです" > old_filename.txt

# インデックスに追加
git add old_filename.txt

# コミット
git commit -m "Add test file"
```

:::note info

#### Gitでのファイル管理の基本的な流れ

Gitでファイルを管理する際、通常は以下のような流れで操作します。
1. ワークツリー（作業ディレクトリ）でファイルの編集
2. その変更を `git add` コマンドでインデックスに登録
3. インデックスに登録された変更は `git commit` でローカルリポジトリに保存
4. 最終的に `git push` でリモートリポジトリに反映

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1247619/813bdbdd-8fda-8dbd-dbfc-319761dc75c1.png)

:::

# ファイル名の変更手順

## 1. git add & git rm を使った場合
ワークツリーでファイル名を変更してもインデックスには以前のファイル名が登録されたままなので、git rmでインデックスに登録されてるファイル名を削除します。
git addで新しい名前のファイルをインデックスに登録します。

```bash
# 1. ファイル名を変更
mv old_filename.txt new_filename.txt

# 2. 古いファイルをインデックスから削除（出力：rm 'old_filename.txt'）
git rm old_filename.txt

# インデックスの状態を確認（削除された状態、出力：何もでない）
git ls-files

# 3. 新しいファイルをインデックスに追加
git add new_filename.txt

# インデックスの状態を確認（新しい名前で登録された状態、出力：new_filename.txt）
git ls-files
```

:::note info
`git ls-files`：インデックスに登録されているファイル名の一覧を表示する
:::


## 2. git mv を使った場合
`git mv`を使用すると上記 `1. git add & git rm を使った場合` でやっていた複数のステップを1度のコマンドで実行できます。
`git mv` は名前変更とインデックスの登録を一度で行える便利なコマンドです。これにより操作を簡略化できます。Git管理しているファイル名の変更には個人的にこちらを使用した方が良いと思います。

```bash
# git mv 古いファイル名 新しいファイル名
git mv old_filename.txt new_filename.txt

# インデックスの状態を確認（新しい名前で登録された状態、出力：new_filename.txt）
git ls-files
```
Ray ClusterのGlobal Control Store（GCS）コンポーネントをRedisで置き換える

::: note info
これは [ZOZO Advent Calendar 2024](https://qiita.com/advent-calendar/2024/zozo) カレンダー Vol.9 の 5日目の記事です。
:::

# はじめに
昨日は[Ray ClusterのWorker NodeでService Accountを指定する](https://qiita.com/okmtz/private/2f3f980fad8d0bb72f44)というタイトルで、Ray Clusterに関する記事を投稿しました。  
昨日に引き続き、本日もRay Cluster関連の投稿になります。Ray Clusterについては1日目の記事（[GKE上に構成したRay Clusterに外部Load Balancer経由でアクセスする](https://qiita.com/okmtz/private/09b6b7be9d9bcaf1df9b)）で簡単に説明しているため、ご参照ください。

本記事では、Ray ClusterのGlobal Control Storeコンポーネント（以下GCS）をGoogle CloudのマネージドサービスであるMemorystore for Redisに置き換える方法をご紹介します。

# Global Control Store（GCS）とは
GCSはRay Clusterにおけるmetadataサーバーの役割をしており、stateを管理します。  
デフォルトではHead Nodeのメモリ上でstateを持ちますが、Ray ClusterではHead NodeはPodであるため、単一障害点となります。
フォールトトレランスの観点から今回はGCSコンポーネントをMemorystore for Redisで置き換えます。

# GCSのRedisでの置き換え
Memorystore for Redisの構成については割愛します。[公式ドキュメント](https://cloud.google.com/memorystore/docs/redis/memorystore-for-redis-overview?hl=ja)を参考に事前構築し、Ray ClusterのHead Nodeから通信できるように、Ray Clusterを構成するVPCとPeeringしてください。

Ray Clusterでは、RayClusterオブジェクトのマニフェストで指定することにより、GCSを指定したRedisインスタンスに置き換えることができます。

RayClusterのマニフェストについて、以下のAnnotationとHead Nodeの環境変数を指定するようにしてください。

```ray-cluster.yaml
apiVersion: ray.io/v1
kind: RayCluster
metadata:
  annotations:
    ray.io/ft-enabled: "true"
spec:
  headGroupSpec:
    template:
      spec:
        containers:
        - env:
          - name: RAY_REDIS_ADDRESS
            value: <redisip>:<redis-port>
          - name: ENABLE_GCS_FT_REDIS_CLEANUP
            value: "true"
```

環境変数の`ENABLE_GCS_FT_REDIS_CLEANUP`はGCS metadataの自動削除を制御するパラメータになります。デフォルトでは`true`となっており、Jobの失敗時にベストエフォートでRedisからmetadataが自動削除されます。 

詳細は公式ドキュメントで説明されているためご参照ください。

# 参考資料
- https://docs.ray.io/en/latest/cluster/kubernetes/user-guides/kuberay-gcs-ft.html#gcs-fault-tolerance-in-kuberay
転職活動には７年くらいかけました

# はじめに

私は、現職は４社目になります。
以下のような転職歴です。

| 期間 | どんな会社 | 内容 |
|:-----------|:------------|:------------|
| 2000-2002      | 大手電気メーカー        | エンジニアとして新卒で入社         |
| 2002-2007      | インターネットカンパニー        | WEBエンジニアとして転職　       |
| 2007-2024      | インターネットカンパニー        | WEBエンジニアとして転職　当時は日本語でOKだったが、今は英語必須         |
| 2024-      | SIer        | テスト自動化スペシャリストとして転職         |


# ４社目への転職活動のきっかけ

３社目では、前半はエンジニアとして活動していました。
主に社内でアプリケーション開発・運用を独自で学習してスキルを伸ばしてきました。
社内の勉強会などもある程度揃っていたため、社内で閉じて成長してきたこともありました。

転機は、社内で異動により、ロールがアプリケーションエンジニアからテストエンジニアになった時に訪れました。
アプリケーションエンジニアとして、ある程度テストに関して知識はありましたが、とはいえこれまで経験したことのないロールなので、ゼロから学び直しを決意しました。

そのスキルを獲得するためにやったこととして、以下のことです

- 外部の勉強会に積極的に参加
- 同じロールの人たちとの繋がり
- 社内に持ち帰り、成果を出したら外で共有する

このような活動を行うことで、自分のテストエンジニアとしての知名度が上がったおかげなのか、エンジニアのハンティング会社に目がかかりました。（T社）
2017年のことでした。
先方からは、ラフでもいいので会えないかと連絡があったので、担当者と会いました。

これが、今思えば転職のきっかけとなったのかもしれません。

# T社の担当と会って

T社の担当と会って、以下のようなことを話しました

- いろんな企業を知ること
- テストエンジニア市場の現状・需要・求められるスキル
- スキルの棚卸し
- 自身のエンジニア価値、ポテンシャル

当時は全く転職を考えていませんでしたが、紹介していただいた会社にカジュアル面談（当時は直接面接です　コロナ禍前でしたので）しました。
紹介していただいた会社は、自分が知っていた会社もあれば、全く知らない会社もありました。

実際にカジュアル面談することで、自分のスキルを求めている新たな会社と会うことができました。
また、面談してくれた人とは今でもお付き合いしている人もいます。

T社からは年に１〜２社のペースで紹介していただいたので、当時の会社で働くにあたって、全く負担にならない程度で、将来の可能性を広げることができました。

また、ここをきっかけにスキルの棚卸し、レジメ作成を行い、自分のスキル、会社や業界へのアウトプットを整理することができました。

今となっては、T社を通して転職はしなかったですが、いろいろなことを学ぶことができ、本格的に転職活動する時に負担にならない経験にもなりました。

# B社のエントリー

コロナ禍が収束するころまでT社とは定期的にお話ししていました。
そして、自分のスキルの棚卸しは定期的にアップデートされてました。

2022年、何かのきっかけで、せっかくなのでとB社の会員登録を行いました。
最新版のレジメがあったので、かんたんに登録できるということも、一つのきっかけでした。

すると、毎日のようにスカウトのメールがきました。
最初は、自分のスキルが求められていると感じてましたが、T社の時に比べると、単にウザく感じてきましたw

しかも、転職エージェントも多く、本当にあったスキル・希望の会社を紹介しているとは思えませんでした。
全てのエージェントを否定するものではないですが、採用活動でエージェントの成果報酬のことも知っていたため、ほぼ疑っていました。

そのため、2023年夏頃まで、こんな企業からお誘いくるのね〜程度で見て、オファー内容を見る程度になりました。

# 友人の活躍を見る

2023年、東京ゲームショーで友人と話をする機会がありました。
友人は、異動？転籍？で、ゲーム業界の会社にも籍を持ち、ゲーム好きでもある彼らは活き活きとしていていました。

その様子を見て、改めて自分は今の会社で、活き活きしているのかを再考することになりました。

このままでは、自分の人生がつまらなくなるなと感じて、改めて、B社の活動を活性化させました。

ただ、レジメは定期的に最新化してたり、外での活動も継続していることもあったため、すぐにいい企業からのオファーがきました。
また、自分のスキルを求めている企業、オファーのランクを大体把握していたため、絞り込みがしやすかった。

# 転職の決意

現職の会社とは４回面接がありました

1st : カジュアル面談で、会社紹介と自分からの質問への回答
2nd : 執行役員とのカジュアル面談で、自分の合うロールに関して説明を受ける
3rd : 社長とカジュアル面談で、自分に求めていることを説明受ける
4th : 社長と直接面談で、オファー内容を説明受ける

自分の外での活動が知られていたためか、よくあるような採用面接は全くありませんでしたw（なぜ、この会社を選んだのか？　とか）
むしろ、先方から入社してやってほしいことの説明。

最後に社長から、「ぜひ来てください。テスト自動化で活躍してほしい」と、シンプルで自分にとって刺さる内容でしたため、決意しました。

# 転職する人たちへ

自分の経験から、転職する人たちへやっておくといいことをお伝えしたいと思います

- 定期的に自分のスキルを棚卸し、書き出す（転職サイト使うといいと思う）
- 勉強会、セミナーなどで外でも活躍する
- 知らない会社をカジュアル面談などで知る
- 妥協しない。自分にとって刺さる会社が来るまで時間をかける（短期決戦はやめた方がいい）
「型 Promise<void> を型 MouseEventHandler<HTMLButtonElement> に割り当てることはできません。」の解決方法

## はじめに
ReactとTypeScriptで学習記録アプリを作成しています。
追加機能として、表示している一覧から特定の行を削除するボタンを追加したところ、エラーが表示されたので解決方法をまとめます。

## 原因のコード
`Button`タグの`onClick`プロパティでエラーが発生しました。
ブラウザ側では、`onClickDelete`関数の`alert`関数が削除ボタンを押下しなくても実行される状態になり、無限ループに陥りました。

```tsx:App.tsx
export const App = () => {

    // 削除
    const onClickDelete = async (id: string) => {
        alert("削除を実行")
        await DeleteRecord(id);
    
        // 削除後のレコードを取得
        const updateAllRecord = await GetAllRecords();
        setRecords(updateAllRecord);
    };

    // 画面描画
    return (
        <Provider>
            {records.map((record) => (
                <Table.Row key={record.id}>
                    <Table.Cell>{record.learn_title}</Table.Cell>
                    <Table.Cell textAlign="center">
                        
                        <Button onClick={onClickDelete(record.id)}>削除</Button>
                        
                    </Table.Cell>
                </Table.Row>
            ))}
        </Provider>
    )
};
```


## 解決方法
対応としては、`onClick`にアロー関数を渡すことでエラーと無限ループが解消され、任意のレコードが削除されました。

```tsx:App.tsx
// 一部省略

{records.map((record) => (
    <Table.Row key={record.id}>
        <Table.Cell>{record.learn_title}</Table.Cell>
        <Table.Cell textAlign="center">
            
            <Button onClick={() => onClickDelete(record.id)}>削除</Button>
            
        </Table.Cell>
    </Table.Row>
))}
```

## 学んだこと

まず、エラーが発生した`onClick={関数名(引数)}`の書き方だと、
画面描画時に<strong><font color="">「関数の実行結果」</font></strong>(ここでは`Promise<void>型`)が`onClick`に渡されてしまいます。

:::note info
Reactでは、イベントハンドラに渡すべきは`MouseEventHandler<HTMLButtonElement>`型、つまり「関数そのもの」です。
戻り値ではなく、クリックイベント時に実行される関数を指定する必要があります。
:::

<details><summary>なぜ無限ループが発生したのか</font></summary>
削除ボタン押下時に関数が実行されるのではなく、画面を表示するタイミングで実行され結果が返っていたため、無限ループなどの予期せぬ事態が発生しました。
</details>

では、`onClick={() => 関数名(引数)}`はどうなのかというと、
<strong><font color="">「関数そのもの」</font></strong>が`onClick`に渡されるため型の不一致が生じず、エラーが解消されるということらしい。


## 気付き
今までアロー関数とは、コードをシンプルに書けることがメリットとしか考えていませんでした。
今回のケースを通じて、イベントハンドラ設定時に使用することで戻り値の型に違いが生じ、エラーを解消できることを学びました。

## 参考サイト

https://stackoverflow.com/questions/67367665/type-promisevoid-is-not-assignable-to-type-mouseeventhandlerhtmlbuttonelem

https://zenn.dev/kisukeyas/articles/9af27eab7122ce
Ray ClusterのWorker NodeでService Accountを指定する

::: note info
これは [ZOZO Advent Calendar 2024](https://qiita.com/advent-calendar/2024/zozo) カレンダー Vol.9 の 4日目の記事です。
:::

# はじめに
昨日は[KubernetesのNamespaceごとに構築したRay Clusterへの参照をアクセス時のパスで切り替える](https://qiita.com/okmtz/private/23e67133c05d8f6db1f5)というタイトルで、Ray Clusterに関する記事を投稿しました。  
昨日に引き続き、本日もRay Cluster関連の投稿になります。Ray Clusterについては1日目の記事（[GKE上に構成したRay Clusterに外部Load Balancer経由でアクセスする](https://qiita.com/okmtz/private/09b6b7be9d9bcaf1df9b)）で簡単に説明しているため、ご参照ください。

今回はRay ClusterのWorker NodeのPodについて、認証するKubernetesのService Accountを指定する方法を紹介します。

# Service Accountの指定
以下のように、RayClusterのオブジェクト作成時に`spec.workerGroupSpecs[].template.spec.serviceAccountName`で指定可能です。
Worker NodeのPodで認証するKubernetesのService AccountをWorker Groupごとに指定できます。

```ray-cluster.yaml
apiVersion: ray.io/v1
kind: RayCluster
metadata:
  name: raycluster-autoscaler
  labels:
    ...省略
spec:
  workerGroupSpecs:
  - groupName: small-group
    ...省略
    template:
      spec:
        serviceAccountName: raycluster-worker
```

Google CloudのService Acccountと連携することでRayのWorker Node内でもIAMで認証して、Google Cloudのリソースにアクセス可能です。

```serviceaccount.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  annotations:
    iam.gke.io/gcp-service-account: mlops-ray-worker@example-project.iam.gserviceaccount.com
  name: raycluster-worker
```

Google Kubernets EngineにおけるKubernetesのService AccountとGoogle CloudのService Accountの連携については[公式ドキュメント](https://cloud.google.com/kubernetes-engine/docs/how-to/service-accounts?hl=ja)をご参照ください。
クラスインスタンス変数

# ２種類のインスタンス変数がある
```rb
irb(main):001* class Product
irb(main):002*   @name = "Product"
irb(main):003*
irb(main):004*   def self.name
irb(main):005*     @name
irb(main):006*   end
irb(main):007*
irb(main):008*   def initialize(name)
irb(main):009*     @name = name
irb(main):010*   end
irb(main):011*
irb(main):012*   def name
irb(main):013*     @name
irb(main):014*   end
irb(main):015> end
=> :name
irb(main):016> Product.name
=> "Product"
irb(main):017> product = Product.new("A great movie")
=> #<Product:0x00000001092a97e8 @name="A great movie">
irb(main):018> product.name
=> "A great movie"
irb(main):019> Product.name
=> "Product"
```
> 実は２種類の@nameに分かれます。一つはインスタンス変数の@nameで、もう一つはクラスインスタンス変数の@nameです.

# ２つのインスタンス変数を見てみる
```rb
irb(main):034* class Product
irb(main):035*   # クラスインスタンス変数
irb(main):036*   @name = "Product"
irb(main):037*
irb(main):038*   def self.name
irb(main):039*     # クラスインスタンス変数
irb(main):040*     @name
irb(main):041*   end
irb(main):042*
irb(main):043*   def initialize(name)
irb(main):044*     # インスタンス変数
irb(main):045*     @name = name
irb(main):046*   end
irb(main):047*
irb(main):048*   def name
irb(main):049*     # インスタンス変数
irb(main):050*     @name
irb(main):051*   end
irb(main):052> end
=> :name
```

### インスタンス変数
>`クラスをインスタンス化`（クラス名.newでオブジェクトを作成）した際に、オブジェクトごとに管理される変数

### クラスインスタンス変数
>インスタンスの作成とは無関係に、クラス自身が保持しているデータ（クラス自身のインスタンス変数）です。`クラス構文の直下や、クラスメソッドの内部で@で始まる変数を操作すると、クラスインスタンス変数にアクセスしている`ことになります。

# クラス自身がインスタンス変数を保持できる
>`インスタンス変数と比べるとクラスインスタンス変数を使う機会は少ない`と思いますが、`クラス自身もインスタンス変数を保持できる`ことと、インスタンス変数とは異なりスーパークラスとサブクラスでは同じ名前でも別の変数になること覚えておいてください

```rb
irb(main):001* class Product
irb(main):002*   @name = 'Product'
irb(main):003*
irb(main):004*   def self.name
irb(main):005*     @name
irb(main):006*   end
irb(main):007*
irb(main):008*   def initialize(name)
irb(main):009*     @name = name
irb(main):010*   end
irb(main):011*   def name
irb(main):012*     @name
irb(main):013*   end
irb(main):014> end
=> :name

irb(main):015* class DVD < Product
irb(main):016*   @name = "DVD"
irb(main):017*
irb(main):018*   def self.name
irb(main):019*     # クラスインスタンス変数を参照
irb(main):020*     @name
irb(main):021*   end
irb(main):022*   def upcase_name
irb(main):023*     # インスタンス変数を参照
irb(main):024*     @name.upcase
irb(main):025*   end
irb(main):026> end
=> :upcase_name
irb(main):027>


irb(main):028> Product.name
=> "Product"
irb(main):029> DVD.name
=> "DVD"
irb(main):030> product = Product.new("A great movie")
=> #<Product:0x0000000106ed9278 @name="A great movie">
irb(main):031> product.name
=> "A great movie"
irb(main):032> dvd = DVD.new("An awesome file")
=> #<DVD:0x0000000106e772d0 @name="An awesome file">
irb(main):033> dvd.name
=> "An awesome file"
irb(main):034> dvd.name.upcase_name
(irb):34:in `<main>': undefined method `upcase_name' for "An awesome file":String (NoMethodError)

dvd.name.upcase_name
        ^^^^^^^^^^^^
Did you mean?  upcase
	from 
 ....
irb(main):035> dvd.upcase_name
=> "AN AWESOME FILE"
irb(main):036> Product.name
=> "Product"
irb(main):037> DVD.name
=> "DVD"
```
https://gihyo.jp/book/2017/978-4-7741-9397-7

# 感想
クラスインスタンス変数を今知ることができた。
JWTとは 備忘録

https://jwt.io/

こちらのサイトを見て学びます

# JSON Web Tokenとは
JSONオブジェクトとして関係者感で情報を安全に送信するための方法

# JWTをいつ利用するか
主に認証で使われる
ユーザーがログインすると、各リクエストにはJWTが含まれ、ユーザーはそのトークンで許可されているルート、サービス、リソースにアクセスすることができるようになる。また異なるドメイン間で利用することができるため、広く利用されている

# JWTの構造
1. ヘッダ
2. ペイロード
3. サイン
がある

`xxxxx.yyyyy.zzzzz` こんな感じ

### ヘッダ
トークンのタイプと使用されている署名アルゴリズムの2つの部分で構成される

```json
{
  "alg": "HS256",
  "typ": "JWT"
}
```
このJSONはBase64Urlでエンコードされる

### ペイロード
クレームというユーザーやデータに関するもの
クレームは登録済み、パブリック、プライベートの3種類がある
Base64Urlでエンコードされる

- 登録済み
必須ではないが、定義済みのクレーム
iss(発行者), exp(有効期限), sub(件名)などの3文字のもの

- パブリック
JWTを使用するユーザーが自由に定義できる


- プライベート
使用に同意する当事者感で情報を共有するために作成されたもの
以下は例

```json
{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true
}
```

### サイン
署名部分を作成するには、エンコードされたヘッダー、エンコードされたペイロード、秘密、ヘッダーで指定されたアルゴリズムを取得し、それに署名する必要がある
署名は、メッセージが途中で変更されていないことを確認するために使用され、秘密鍵で署名されたトークンの場合は、JWT の送信者がその送信者本人であることを確認することもできる

例

```json
HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret)
```

# JWTはどのように使用されるか
認証では、ユーザーが資格情報を使用して正常にログインすると、JSON Webトークンが返される。トークンは資格情報であるため、一般にトークンは必要以上に長く保持してはいけない。

ユーザーエージェントはBearerスキーマを使用してAuthorizationヘッダーでJWTを送信する

```
Authorization: Bearer <token>
```

トークンがAuthorizationヘッダーで送信される場合、Cookieを使用しないため、CORSにならない



# まとめ
出力はドットで区切られた 3 つの Base64 URL 文字列であり、HTML および HTTP 環境で簡単に渡すことができる。
DialogFragment.dismissAllowingStateLossについて

# はじめに
DialogFragmentを使用していたところ、エラーでクラッシュしていたので調べたのでそれについての記事です。

# 解決
エラー内容的には、DialogFragmentを表示し、非同期で何か通信などをした後にDialogFragmentを閉じると出るエラーみたいで、dismissではなく解決法が記事のタイトルにもある**dismissAllowingStateLoss**を使うことでした。


使い方は、dismissと同じなのでClick処理にやりたい処理と**dismissAllowingStateLoss**をつけるだけです。

```
    〜〜〜.setOnClickListener { 
    　 dismissAllowingStateLoss()
      // やりたい処理
    }
```



# 参考

https://bg1.hatenablog.com/entry/2021/05/31/210000

https://tekidroid.tokyo/android%E3%83%80%E3%82%A4%E3%82%A2%E3%83%AD%E3%82%B0%E3%82%92%E7%B0%A1%E5%8D%98%E3%81%AB%E9%96%89%E3%81%98%E3%82%8B%E6%96%B9%E6%B3%95/
.pemファイルに権限を付与することを忘れるな！！

`.pem`ファイルと`.ssh`の配下に置いたあと、`chmod`コマンドを使って`.pem`ファイルに **所有者のみが読み書き可能** できるように権限を付与します。


コードはこちら↓
```bash
chmod 600 ~/.ssh/任意の名前.pem
```

短すぎる記事となってしましましたが、以上です
【ペアワイズ法】記憶から飛ぶPICT構文のメモ【QA】

# はじめに
PICTは、二因子間網羅(ペアワイズ法)を実現するためのMicrosoft製ツールである。
PictMasterやGIHOZ等、ペアワイズをサポートするためのGUIツールはあるが、それらの殆どはPICTを利用したものだ。
「ガワ」が異なっていたとしても、裏で動いているエンジンは全部PICTみたいな感じである。

# 構文メモ
しょっちゅうPICT構文を忘れるので、備忘録として作成する。

## コメントアウト(# hogehoge)
シャープ「#」をつける

<img width=500px src = "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3680589/bb9205d2-eecc-7d3a-5187-0ab8035b521b.png">

## 水準と因子

### 基本フォーマット
- 水準が文字列型であっても""で囲う必要はない
- 末尾に「；」は不要

<img width=550px src = "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3680589/980a8d8c-b1ae-1952-2e53-d6df65b38836.png">


### エイリアス（水準 : 因子1 | 因子2, 因子A）
複数の因子を同値クラスと見なし、ペア生成時に区別しなくなる。
<img width=550px src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3680589/16e294af-3ec5-1fe9-35cb-ae1458966cd7.png">


### ペアの無効化 (~)
頭に「~」をつけたものは互いにペアを組まなくなる。
<img width=550px src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3680589/ea58eb05-9f21-78f9-2338-d2ed1fd830b3.png">

### 他の水準設定を流用(<水準>)
別の水準と同じ因子を持つ場合、<>で囲って流用することができる。
<img width=550px src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3680589/a32fc8d1-6afb-32d4-c714-c7274f09ebf1.png">

## 制約の構文
### 基本構文
- ネストは不可
- 末尾にセミコロン「;」が必要
- 因子は[]で囲い、水準は""で囲う。
<img width=450px src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3680589/15e23211-c589-bc02-1129-c0cbc8d0273d.png">

### Equalは1つの「=」、Not Equalは「<>」
おそらく、これが一番忘れがち
<img width=450px src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3680589/d7ebbf10-7691-ea93-9559-405ca4c6c1c0.png">

- 「==」はNG
- 「!=」はNG

### IF文とIN句が頻用される
<img width=400px src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3680589/a7e494af-c4d9-6c4a-fdad-888c5b6713ca.png">

あまり使わないが、IN句の前には、NOTを置くこともできる。
<img width=450px src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3680589/6862dbc0-1344-e4b7-2795-554e63160383.png">

### サブモデル
「特定の因子グループは全網羅させたい」みたいなときに使う。
以下の例では、Device、Browser，Versionは全網羅されるようになる。
正直あまり使わない。
<img width=550px src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3680589/4f3bdfaf-10d0-4d1a-b5e0-2ab4ef951f11.png">


# おわりに
PictMasterやGIHOZは、GUIベースでペアワイズを利用できる点で、非常に優れたツールである。
しかし、複雑な制約が求められる組み合わせテストであればあるほど、PICT文を直接書いてしまったほうが認知負荷が下がったりする。
決して難しい構文でもないので、書けて損することはなさそう。

ただ、基本的にペアワイズ法は「ケースを削減する他の手立てがない」ときに利用する最終手段であり、本来は、プロダクトの仕様やリスクに基づいてケース数を削減したほうが良いと思っている。
なので、実はあまり好きくない。
【Ransack】sort_linkで重複のある値をソートするときに気をつけるべきこと【Rails】

# ユースケース
railsでよく行う実装として、ransackで検索・絞り込み・ソートを行ったオブジェクトをviewのindexにレンダリングするというケースが多いと思います。
例えば商品一覧(products)をテーブル形式で表示した際、テーブルのヘッダーに`sort_link @q, :selling_price, 'Price'`という記述を加えて、価格の高い順・安い順で並べ替えるなんてことがよくあるかと思います。
今回はこのようなsort_linkを使用した場合に起きえる問題点をご紹介します。

# 問題点
上記で説明した`sort_link @q, :selling_price, 'Price'`というケースですとあまり起き得ないかもしれませんが、例えば以下のように在庫の数量で同じ実装をしたとします。
`sort_link @q, :quantity, 'Quantity'`

```products_controller.rb
class ProductsController
  before_action :set_products, only: :index

  PER = 30

  def index; end

  private

  def set_products
    @q = Product.ransack(params[:q])
    page_per = params[:page_per].present? ? params[:page_per] : PER
    @products = @q.result.page(params[:page]).per(page_per)
  end
end
```

この場合にquantityのソートを実行すると発行されるSQLは以下のようになります。

```
SELECT
	`products`.*
FROM
	`products`
ORDER BY
	`products`.`quantity` DESC
LIMIT 30 OFFSET 0;
```

一見問題なさそうに見えますが、このようにページネーションがあるかつ、ソートする値(quantity)に同数のものが多数存在する場合、1ページ目と2ページ目に同じ商品が表示されてしまうというケースが起きえます(1ページ目にあった商品Aが2ページ目にも表示されているというような状態)。

# 原因
なぜこのようなことが起きるのかというと、ORDER BY句の並び順が一意でないからです。
例えば1ページあたりの表示数を30件(`PER = 30`)としている場合にquantityが1のものが100件あるとします。
その状態でページネーションにより2ページ目以降にアクセスした場合、複数のレコードが同じquantityの値を持っており、これらのレコードの並び順はベースエンジンによって任意に決定されます。
また、OFFSETを使うとき、どのレコードが次のページに含まれるかが曖昧になるため、同じレコードが異なるページに現れることがあるようです。

# 対策
並び順が一意となるセカンダリソートキーを設定しましょう。
例えば以下のようにidで一位性を保つなどの方法があります。
`sort_link @q, :quantity, [:quantity, 'id asc'], 'Quantity'`

```
SELECT
	`products`.*
FROM
	`products`
ORDER BY
	`products`.`quantity` ASC,
	`products`.`id` ASC
LIMIT 30 OFFSET 0;
```

もしくは以下のようにransackのソート条件の後に作成時刻(ほぼ一位と思われる)などで並べ替えすればデータの並び順が安定し、ページングを移動しても問題はなくなります。
```products_controller.rb
  def set_products
    @q = Product.ransack(params[:q])
    page_per = params[:page_per].present? ? params[:page_per] : PER
    @products = @q.result.order(created_at: :desc).page(params[:page]).per(page_per)
  end
```

# さいごに
この問題に直面した際、最初は同じデータが重複して作成されていることを疑ったのですが、そうではなく並び替えの一位性が原因でした。
同じような問題に直面した方の解決の助けになれば幸いです。
ラズパイPicoで、OLEDに文字を表示する

ラズパイPicoのI2Cで、OLEDに文字を表示してみよう

## OLEDの説明

ここで使うOLED(有機LED)は、I2Cで制御される128x64ドットのグラフィックディスプレイモジュールです。モジュールではSSD1306と言う制御チップが使われています。

I2Cでは、GND、VCC、SCL、SDAと言う、4本のピンで複数の周辺機器を制御します。このひとつにOLEDを接続して、ディスプレイに文字を表示してみます。

0.96インチ 128×64ドット有機ELディスプレイ(OLED) 白色：
https://akizukidenshi.com/catalog/g/g112031/

## 準備：OLED の GND、VCC、SCL、SDA を接続

OLEDの左から右に向かって：
GND：-
VCC：+ (3.3V)
SCL：PIN 1
SDA：PIN 0

![OLED.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3665435/002fce66-5440-133a-3f54-e900fd4273a5.png)


## プログラム

まず、「パッケージを管理」で、ssd1306 をインストールしておきます。

```Python
from machine import Pin, I2C
from ssd1306 import SSD1306_I2C
import random
import time

i2c = I2C(0, scl=Pin(1), sda=Pin(0))    # SCL=1、SDA=0 でI2Cを初期化
oled = SSD1306_I2C(128, 64, i2c) 	# 幅=128、高さ=64 でSSD1306を初期化

oled.fill(0)	# 画面をクリア
oled.text("Hello, World!", 0, 0)		# 文字列、X座標、Y座標
oled.rect(10, 20, 20, 40, 1)
oled.line(60, 30, 80, 40, 1)
oled.show()	# 画面を更新
time.sleep(3)

for i in range(30):
    oled.scroll(-4, 0) 			# 20 ピクセル左にスクロール
    oled.show()
    time.sleep(0.1)

oled.fill(0)	# 画面をクリア
oled.text("Hello, World!", 0, 0)
oled.text("0123456789", 0, 8*2)
oled.text("ABCDEFGHIJKLMNO", 0, 8*3)
oled.text("PQRSTUVWXYZ", 0, 8*4)
oled.show()	# 画面を更新
time.sleep(3)

# ランダムにドットを表示する
while True:
    x = random.randint(0, 127) # 0から127までのランダムな数
    y = random.randint(0, 63) # 0から63までのランダムな数
    oled.pixel(x, y, 1) # 点をかく
    oled.show()
    time.sleep(0.1)

```
データベースの基本　#1

データベースについて学習中ですので、複数の記事に分けてまとめます。
内容は初学者向けの内容になっています。

使用するデータベース：PostgreSQL
筆者の動作環境：macOS

"PostgreSQL"のインストール方法はここでは省略します。OSに合ったインストールの方法でインストールしてください。

## PostgreSQLに接続
PstgreSQLに接続するコマンドは以下です。
* 「ホスト名/IPアドレス」はローカルホストの場合は"localhost"
* 「ポート番号」のデフォルトは、"5432"
* 「ロール名」のデフォルトは、OSのユーザ名（スーパーユーザ）
* 「データベース」には、接続したいデータベース名を入力する
```console
psql -h localhost -p 5432 -U <ロール名> -d <データベース名>
```

また、PostgreSQLを終了する場合は、以下のコマンドを実行します。
```console
\q
```

## データベースの作成
データベースを作成する場合、PostgreSQLに接続した状態で、以下のSQL文を実行します。
```console
CREATE DATABASE <データベース名>;
```

## テーブルの作成
データベースを作成できたら、次にテーブルをその中に作成します。テーブルを作成するSQL文は以下の通りです。
```console
CREATE TABLE <テーブル名>
(<列名１> <データ型> <この列の制約>,
<列名2> <データ型> <この列の制約>,
<列名3> <データ型> <この列の制約>,
<列名4> <データ型> <この列の制約>,
・・・
<このテーブルの制約1>, <このテーブルの制約2>, ・・・);
```
この構文は、＜列名1＞、＜列名2＞、・・・という列を持ち、＜テーブル名＞という名前のテーブルを作っている、と捉えると分かりやすいです。
また、列に対する「データ型」の指定は必須で、必要に応じて列に「制約」を設定します。
制約の設定は、列の定義ごとに記述することもできますし、最後にまとめて記述することもできます。

作成したテーブルの一覧は以下のSQL文を実行することで表示されます。
```console
\dt
```

次のSQL文を実行することで、テーブルの列の一覧も表示できます。
```console
\d <テーブル名>
```

## データ型の指定
`CREATE TABLE`でテーブルを作成時に、列名の右隣にあるのが、データ型の指定です。その列のデータ型を宣言するもので、全ての列に必ず指定します。

データ型にはたくさんの種類があるため、代表的な4種類を紹介します。
|データ型|説明|
|-|-|
|`INTEGER`|整数を入れる列に指定するデータ型（数値型）。少数派入れられない。|
|`CHAR`|文字列を入れる列に指定するデータ型（文字列型）。CHAR(10)やCHAR(200)のように、列の中に入れることのできる文字列の長さ（最大長）をかっこで指定する。固定長文字列。|
|`VARCHAR`|`CHAR`型と同じく、文字列を入れる列に指定するデータ型。可変長文字列。|
|`DATE`|日付（年月日）を入れる列に指定するデータ型（日付型）。|

## 制約の設定
制約とは、データ型の他に、列に入れるデータに制限や条件を追加する機能です。

制約の一つに`NOT NULL`制約があります。`NULL`とは、「無記入」状態であることを表すキーワードです。その`NULL`を否定しているため、`NOT NULL`制約は無記入ではいけない、必ずデータが入っていなければならない、という制限を列に課します。

## テーブルの削除
テーブルを削除するSQL文は以下の通りです。
```console
DROP TABLE <テーブル名>;
```

## テーブル定義の変更
テーブルを作った後に、列が足りないといったことが判明する場合があります。そうしたとき、テーブルを削除して再作成する必要はなく、テーブルの定義を`ALTER TABLE文`を使う。

* **列の追加**
```console
ALTER TABLE <テーブル名> ADD COLUMN <列の定義>;
```

* **列の削除**
```console
ALTER TABLE <テーブル名> DROP COLUMN <列名>;
```

* **テーブル名の変更**
```console
ALTER TABLE <元のテーブル名> RENAME TO <新たなテーブル名>;
```