OmniAICreator/Qiita-1.07M huggingface dataset:
Ruby silver 模擬問題解説8

Ruby silverの模擬問題を抜粋して解説をしていきます。

seekメソッドの引数whenceに指定する値の説明として正しいものを選びなさいという問題です。
```ruby
io.seek(offset, whence)
```
1.IO::SEEK_CURで、現在のファイルポインタ位置からoffsetの数値分だけ移動する
2.IO::SEEK_DATAで、offsetで指定した数値と一致するデータがある位置へ移動する
3.IO::SEEK_SETで、ファイルポインタがファイルの先頭に戻る
4.IO::SEEK_ENDで、ファイルポインタがファイルの末尾に移動する

まず、ioとはなに？というところから解説します。
ioとはioオブジェクトという入出力操作を扱うための機能を提供するオブジェクトになっております。
ちなみに、ioとはInput/Output(入力/出力)の略称になっています。

次に'.seek'メソッドはファイルやストレージ内の現在の位置を変更するために使用されるメソッドになっています。
['.seek'メソッドの公式リファレンス](https://docs.ruby-lang.org/ja/latest/method/IO/i/seek.html)

次に、引数にあるoffsetとwhenceになります。
offsetは、ファイル内の位置を表すバイト単位のオフセットを示します。
具体的には、ファイルの先頭からのバイト数を表します。正の値はファイルの先頭からのオフセットを、負の値はファイルの末尾からのオフセットを表します。

offsetを使用することで、ファイルポインタを指定した位置に移動させることができます。
例えば、offsetが10の場合、ファイルポインタは現在位置から10バイト後ろに移動します。

次に、whenceになります。
whenceの値は以下のいずれかになります。
IO::SEEK_SET: ファイルの先頭から (デフォルト)
IO::SEEK_CUR: 現在のファイルポインタから
IO::SEEK_END: ファイルの末尾から
IO::SEEK_DATA: offset 以降の次にデータがある位置へ(lseek の man ページ参照。Linux 3.1 以降のみ)
IO::SEEK_HOLE: offset 以降の次にホールがある位置へ(同上)

```
例) io.seek(10, IO::SEEK_CUR)
```
今ままでの説明を例として書くとこのようなコードになります。
意味としては、現在のファイルポインタから10バイト先に移動しますという意味になっています。

今までの解説を踏まえて、今回の問題でwhenceの正しい説明をしているのは1番になります。
Render.com無料版ではプログラムで生成したファイルなどが永続的ではない

# 結論
Render.comの無料版ではプログラムで生成したファイルやアップロードしたファイルはサーバーがリブートされると削除されるようです。特にsqlite3をデータベースに使用している場合は注意が必要です。

# 実際に起きたこと

以前、Render.comでPostgreSQLを使用してアップグレードしていないまま90日経過するとエラーが発生するという記事を投稿しました。

https://qiita.com/76r6qo698/items/79e3b075ae8afbc3b60e

この記事ではPostgreSQLをsqlite3に切り替えておくことでエラーを解決させてます。
ただ、sqlite3に切り替え後、時間をおいてからWebアプリにアクセスすると以前のDBの内容が残っていないという点に気がつきました。

# 調査
以下の記事が参考になりました。
https://www.braingate.jp/render_prosandcons/

> - プログラムで生成したファイル、ユーザーがアップロードしたファイルなどは永続的でない。
> - 15分間アクセスがないと、サーバーはリブートされる。そのために、上記項目で記したファイルは削除される。

sqlite3はデータベースをファイルにして管理しています。そのため、サーバーがリブートされるとデータベースのファイルも削除されてしまう結果になります。

# 終わりに
プログラムで生成したファイルが永続的でないことについてはRender.comの無料版の仕様であるためどうしようもなさそうです。sqlite3のデータベースの内容を削除されたくない場合、有料版を使用することを検討したほうが良いと思います。

以上です。

# 参考

https://www.braingate.jp/render_prosandcons/
rubyの二重ハッシュ

```ruby
user_data = [
 {user: {profile: {name: 'うさぎさん'}}},
 {user: {profile: {name: 'うしさん'}}},
 {user: {profile: {name: 'ぶたさん'}}},
]
```
user_dataを利用して、全てのユーザーの名前だけが出力されるように、
rubyでうさぎさん うしさん ぶたさんだけを書き出したい。

```ruby
user_data.each do |u|
  puts u[:user][:profile][:name]
end
```
となる。


二重ハッシュから特定の値を取得する場合は、取得したい値のキーまで連続して指定すると取れる。

ハッシュ[取得したい値のキー][取得したい値のキー]
取得したい値に対応するキーはname。
そのため、nameというキーまで連続して指定すると、うさぎさん, うしさん, ぶたさんという値を取れる。

ハッシュ[:user][:profile][:name]

配列の中にハッシュが格納されている。
そのため、each文でハッシュの1つ1つを取り出す必要がある。

多重階層にあるハッシュの値をまとめて取ってこれるdigメソッド使ってもいける。
user_data.each{ |u| puts u.dig(:user, :profile, :name) }
Carbonで日時の差分を計算して「〇時間〇分〇秒」のように表示する

ITのコーチをしています。
Carbonを用いて日時の差分を計算して「〇時間〇分〇秒」のように表示する方法を、メモとして記録します。

# Carbonとは
Carbonは、PHPで日付と時刻を扱うためのライブラリの一つです。日時の計算やフォーマットなどを簡単に行うことができます。
PHPには標準で日付と時刻を扱うためのDate関数がありますが、Carbonはさらに簡単に日付と時刻を扱うことができます。

Carbonを使用すると、以下のようなことができます。

- 日付や時刻の加算や減算
- 2つの日時の差分の計算
- 日付や時刻のフォーマット
- 日付や時刻の比較

また、CarbonはLaravelというPHPのWebアプリケーションフレームワークでも使用されています。
LaravelにはCarbonの機能が組み込まれています。

■Carbon公式ドキュメント
[https://carbon.nesbot.com/docs/](https://carbon.nesbot.com/docs/)
いろいろとサンプルコードが掲載されています。

■CarbonのGitHubリポジトリ
[https://github.com/briannesbitt/carbon](https://github.com/briannesbitt/carbon)
質問やバグ報告なども見れます。

# Carbonの基本的な日付と時刻の表現
Carbonでは、日付と時刻を表すクラスが用意されています。
以下のようにしてCarbonクラスのインスタンスを作成することができます。
~~~php
use Carbon\Carbon;

// 現在の日付と時刻を取得
$now = new Carbon();

// 特定の日時を指定して作成
$date = new Carbon('2023-12-25 08:30:00');
~~~

# 日時の差分を計算する方法
ここでは、Carbonを使用してある日時からある日時までの時間を〇時間〇分〇秒 の形式で計算するプログラム例を紹介します。
~~~php
use Carbon\Carbon;

// 1.開始日時を設定
$startDate = new Carbon('2023-06-06 10:30:00');

// 2.終了日時を設定
$endDate = new Carbon('2023-06-07 12:45:30');

// 3.差分の秒数を計算
$diffInSeconds = $startDate->diffInSeconds($endDate);

// 4.秒数から時間、分、秒を計算
$hours = floor($diffInSeconds / 3600);
$minutes = floor(($diffInSeconds % 3600) / 60);
$seconds = $diffInSeconds % 60;

// 5.結果を表示
echo "開始日時から終了日時までの時間は、" . $hours . "時間" . $minutes . "分" . $seconds . "秒です。";
~~~
「1.開始日時を設定」と「2.終了日時を設定」部分では、それぞれnew Carbon()の引数に日時を表す文字列を指定することで、開始日時と終了日時を設定しています。

「3.差分の秒数を計算」部分では、**diffInSeconds**を使用することによって、開始日時と終了日時の**差分の秒数**を取得しています。
このとき、変数$diffInSecondsには、開始日時と終了日時の差分である「26時間15分30秒」を秒数で表したものである、「94430」という数字が格納されます。
このままでは使用することができませんので、秒数をもとに日時の計算をする必要があります。

「4.秒数から時間、分、秒を計算」部分では、差分の秒数をもとに、時間、分、秒を計算しています。
1時間は3600秒であるため、時間は以下のように求めることができます。
~~~php
$hours = floor($diffInSeconds / 3600);
~~~
続いて、変数$diffInSeconds を3600で割った余りを求め、それを60で割ることによって分を求めています。
~~~php
$minutes = floor(($diffInSeconds % 3600) / 60);
~~~

%は、剰余を求める演算子です。例えば、10 % 3は、10を3で割った余りである1を返します。
そのため、$diffInSeconds % 3600は、3600で割った余りであり、**時間部分を除いた分と秒の合計秒数**を表します。
1分は60秒であるため、「時間部分を除いた分と秒の合計秒数」を60で割ることによって、分を求めています。

また、それぞれfloor()関数を使用することで、小数点以下を切り捨てることができます。

同様に、変数$diffInSeconds を60で割った余りを求めることによって秒数を算出しています。
~~~php
$seconds = $diffInSeconds % 60;
~~~

「5.結果を表示」部分では、開始日時から終了日時までの時間を、時間、分、秒の単位で表示しています。
このプログラムを実行すると、以下のような結果が表示されます。
~~~
開始日時から終了日時までの時間は、26時間15分30秒です。
~~~

このように、Carbonライブラリを使用することで、日時の計算やフォーマットを簡単に行うことができます。
また、今回のサンプルプログラムでは、日時の差分を秒数で計算してから時間、分、秒に変換する方法を示しています。
日時の差分を計算する際には、このような方法を使って、目的に合わせた単位に変換することができます。
Vimの基本操作について

## モード切り替え
<table>
  <thead>
    <tr>
      <th>コマンド</th> <th>内容</th>
    </tr>
  </thead>
  <tr>
    <td>  [Esc] </td> <td>ノーマルモードに変更。Vim起動時はこのモードである。</td>
  </tr>
  <tr>
    <td>  i、a、o </td> <td>入力モードに変更。テキスト編集が可能になる。</td>
  </tr>
  <tr>
    <td> v </td> <td>ビジュアルモードに変更。テキストの範囲選択等が可能になる。</td>
  </tr>
  <tr>
    <td>  :"コマンド"　 </td> <td>コマンドラインモードに変更。変更内容の保存、Vimの終了等が可能になる。
　　　　　　　　</td>
  </tr>
</table>

## カーソル移動
ノーマルモードでの操作
<table>
  <thead>
    <tr>
      <th>コマンド</th> <th>内容</th>
    </tr>
  </thead>
  <tr>
    <td> h </td> <td>カーソルを左に移動</td>
  </tr>
  <tr>
    <td> j </td> <td>カーソルを下に移動</td>
  </tr>
  <tr>
    <td> k </td> <td>カーソルを上に移動</td>
  </tr>
  <tr>
    <td> l </td> <td>カーソルを右に移動</td>
  </tr>
  <tr>
    <td> w </td> <td>カーソルを次の単語の先頭に移動</td>
  </tr>
  <tr>
    <td> e </td> <td>カーソルを次の単語の末尾に移動</td>
  </tr>
  <tr>
    <td> 0 </td> <td>カーソルを行頭に移動</td>
  </tr>
  <tr>
    <td> $ </td> <td>カーソルを行末に移動</td>
  </tr>
  <tr>
    <td> gg </td> <td>カーソルをファイル先頭に移動</td>
  </tr>
  <tr>
    <td> G </td> <td>カーソルをファイル末尾に移動</td>
  </tr>
</table>

## 検索
ノーマルモードでの操作
<table>
  <thead>
    <tr>
      <th>コマンド</th> <th>内容</th>
    </tr>
  </thead>
  <tr>
    <td> /"検索文字列" </td> <td>指定した文字列で後方検索</td>
  </tr>
  <tr>
    <td>  ?"検索する文字列" </td> <td>指定した文字列で前方検索</td>
  </tr>
  <tr>
    <td> n </td> <td>次の検索文字列に移動</td>
  </tr>
  <tr>
    <td> N </td> <td>前の検索文字列に移動</td>
  </tr>
</table>

## 編集操作
入力モードでの操作
<table>
  <thead>
    <tr>
      <th>コマンド</th> <th>内容</th>
    </tr>
  </thead>
  <tr>
    <td> yy </td> <td>現カーソル行をコピー
</td>
  </tr>
  <tr>
    <td> p </td> <td>現カーソル行の下の行にペースト</td>
  </tr>
  <tr>
    <td> P </td> <td>現カーソル行の上の行にペースト</td>
  </tr>
  <tr>
    <td> x </td> <td>現カーソル上の文字を削除</td>
  </tr>
  <tr>
    <td> dx </td> <td>現カーソル位置の１単語を削除</td>
  </tr>
  <tr>
    <td> dd </td> <td>現カーソル行を削除</td>
  </tr>
  <tr>
    <td> u </td> <td>操作取り消し</td>
  </tr>
  <tr>
    <td> R </td> <td>uで取り消した操作の実行</td>
  </tr>
</table>

## 保存
コマンドラインモードでの操作
<table>
  <thead>
    <tr>
      <th>コマンド</th> <th>内容</th>
    </tr>
  </thead>
  <tr>
    <td> :w </td> <td>保存</td>
  </tr>
  <tr>
    <td> :wq </td> <td>保存して終了</td>
  </tr>
  <tr>
    <td> :q </td> <td>終了</td>
  </tr>
  <tr>
    <td> :q! </td> <td>保存せずに終了</td>
  </tr>
</table>

## 範囲選択
ビジュアルモードでの操作
<table>
  <thead>
    <tr>
      <th>コマンド</th> <th>内容</th>
    </tr>
  </thead>
  <tr>
    <td> y </td> <td>選択範囲をコピー</td>
  </tr>
  <tr>
    <td> b </td> <td>選択範囲を削除</td>
  </tr>
  <tr>
    <td> c </td> <td>選択範囲を削除し、入力モードに移行</td>
  </tr>
</table>
ubuntu22.04上ngrok

## 目的

NAT配下に置いてあるサーバー上でreveal.js等を動かしていると、公開したくなる場合が存在する。そんな時はngrokを使う事で簡単に公開する事が出来る

## ngrok
### ngrokのインストール
以下サイトにアクセス

https://ngrok.com/

サインインを完了させる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2738475/081a83b8-72f2-afd1-1de2-7986509620f4.png)

ダウンロードリンクを取得する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2738475/7ce7ab5f-f0b1-cfff-07fd-0bbcd64637c7.png)

## ubuntu側セットアップ
### ngrokのダウンロード
```
wget https://bin.equinox.io/c/bNyj1mQVY4c/ngrok-v3-stable-linux-amd64.tgz
```

### ngrokのインストール
解凍
```
tar -xzvf ngrok-v3-stable-linux-amd64.tgz
```

Pathの確認
```
tmcit@ubuntu-17:~$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
```

```
cp ngrok /usr/local/bin
```



### ngrokの認証を通す
```
ngrok config add-authtoken XXXXXXXXXXXXTOKENXXXXXXXXXXXXXx
```

### ngrokを起動する
```
ngrok http 8080
```






#### ngrokコマンドリファレンス
##### リージョン指定
```
ngrok http 5500 --region jp
```

##### パスワードを設定
```
ngrok http 5500 --basic-auth ユーザー名:8文字以上のパスワード
```
【Azure OpenAI】プライベートエンドポイントを設定したときのメモ

# 概要
Azure OpenAIで作成したgpt3.5リソースについて、
プライベートエンドポイントを設定して、
仮想ネットワークのプライベートIPアドレスでのみ接続できるようにする。

# 設定
## プライベートエンドポイントの作成
まずプライベートエンドポイントを作成する。
Azure OpenAIサービスで対象のリソースを選択して、
**「リソース管理 > ネットワーク > プライベートエンドポイント接続」**から作成する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2420694/8898dc43-37e5-4075-ccde-f16d86ec46d7.png)

プライベートエンドポイントの作成を画面の内容に沿って実施する。
DNS設定については環境毎に選択してください。
今回は/etc/hostsを使って検証するためどちらでもOK。
設定したら、プライベートエンドポイントに付与されたIPアドレスを確認しておくこと。

## Azure OpenAIに接続制限を設定
**「リソース管理 > ネットワーク > Firewalls and virtual networks」**
から接続制限の設定を行う。

許可するアクセス元を「選択したネットワークとプライベートエンドポイント」に設定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2420694/a021c891-b7bb-5482-c81e-8399ec5b9169.png)

基本的にはこの設定だけで、プライベートエンドポイント以外からの接続は不可になる。
他に制限を付けたければ、他の項目も設定する。

# 検証
## 接続制限なしで実行
まず、制限なしの場合にAzure OpenAIに接続できることを確認する。
任意のサーバーからcurlコマンドでAzure OpenAIに処理を行う。
```sh
$ curl [AzureOpenAIのURL] \
  -H "Content-Type: application/json" \
  -H "api-key: [APIキー]" \
  -d '{
  "messages": [{"role":"system","content":"You are an AI assistant that helps people find information."},{"role":"user","content":"こんにちは"}],
  "max_tokens": 800,
  "temperature": 0.7,
  "frequency_penalty": 0,
  "presence_penalty": 0,
  "top_p": 0.95,
  "stop": null
}'
```
コマンドは、Azure OpenAI Studioのプレイグラウンドで、「コードの表示」から確認可能
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2420694/928db308-586c-ca76-36d0-744f430d4ec5.png)

:::note warn
「コードの表示」からcurlコマンドを表示してそのまま実行すると何故かエラーになったので確認したところ、送信JSONの「messages」に含まれる内容が配列でないといけないのに文字列になっていた模様。ダブルクォーテーションを取ってから実行したら解消した。
:::

制限なしの場合は、実行すると通常通りの応答が帰ってくる。
```
{"id":"~~~~"... "model":"gpt-35-turbo","choices":[{"index":0,"finish_reason":"stop","message":{"role":"assistant","content":"こんにちは！どのようにお手伝いできますか？"}}],"usage":{"completion_tokens":17,"prompt_tokens":25,"total_tokens":42}}
```
## 接続制限ありで実行
接続制限を付けて再度実行する。
まず、プライベートエンドポイントではなく通常のエンドポイントに宛てて実行する。
コマンドは同じものを利用する。

実行すると、下記のように接続拒否のメッセージが帰ってきて接続ができないことがわかる。
```
{"error":{"code":"AccessDenied","message": "Access denied due to Virtual Network/Firewall rules."}}
```

次に、接続制限ありの状態でプライベートエンドポイント宛てに実行する。
今回は/etc/hostsで接続先ホストの名前解決をプライベートエンドポイントのIPアドレスに設定して検証する。
```
# cat /etc/hosts
172.20.***.***   ***.openai.azure.com
```

この状態で再度実行すると、問題なく応答が帰ってくることがわかる。
```
{"id":"~~~~"... "model":"gpt-35-turbo","choices":[{"index":0,"finish_reason":"stop","message":{"role":"assistant","content":"こんにちは！どのようにお手伝いできますか？"}}],"usage":{"completion_tokens":17,"prompt_tokens":25,"total_tokens":42}}
```

# おわり
Python・GPT・Azureが分かる人が社内に居なさすぎてつらいぞ。
みんな新しいことは学んでいこうぜ！
ssh-agentでsshのパスワード入力を省略

## やりたいこと

**ssh接続をするたびに、passphrase を入力するのはもう終わりにする**

```sh
$ git pull
Enter passphrase for key '/home/user/.ssh/id_rsa': # passphraseを入力
```

:frowning2: sshでのやりとりで毎回passphrase の入力を要求されるのはストレス

## やること

passphrase を一度だけ登録

```sh
# ssh-agentを起動
$ ssh-agent bash

# 秘密鍵を登録
$ ssh-add /home/user/.ssh/id_rsa # ssh-add [秘密鍵へのファイルパス]
Enter passphrase for /home/user/.ssh/id_rsa: # passphraseを入力
Identity added: /home/user/.ssh/id_rsa (test@example.com)
```

:blush: ssh-agent を起動して秘密鍵を登録しておけば、sshで実行した際に秘密鍵を自動的に適用して認証処理を行ってくれる
:blush:  ssh-agent は一度起動すると、バックグラウンドで稼働し続ける
udemyのセール情報

udemyのセールは月1回〜4回行われます。
一講座最安1220円で購入できます。

通時価格の90%OFFなどお得に購入できるセールを狙いましょう！

詳しくはスケジュールは、ここ数年の動きを参考にしてください。下記のブログに記載しています。

Udemy（ユーデミー）のセールはいつ？どれくらい安くなる？
https://blolabo.com/udemysale/

また、udemyについては下記の記事もあわせてご覧ください。

Udemy（ユーデミー）のオンライン学習を勧める理由
https://blolabo.com/udemy/
git push -u origin mainがうまくいかない時

# `git push -u origin mai`がうまくいかない

現在[progate path](https://path.progate.com/roadmap)をやっているのですが、その課題で、プッシュがうまくいかず、
`error: src refspec main does not match any`というメッセージが出ました。
結論から言うと`main`ではなく`master`にしたらうまく行ったのですが、それを調べていたら、Qiitaの記事で、解説されている方がいたので、参考に記載します。

https://qiita.com/wa-ma/items/094b75a482e4a1bf0a01

調べた方法としては、
1. そのままエラーメッセージをググって、出てきたqiita記事の内容から、`git branch`を実行すればいいことがわかったので、
1. `git branch`実行すると`* master`と出てきたので、`main`ではなく`master`なのだとわかり
1. `git push -u origin master`を実行したら、うまく行ったと言う感じです。

参考になれば嬉しいです。

参考の記事にも記載されていますが、masterとmainを関連付けすると良いそうです。

`progate path`で質問して返ってきた回答も載せておきます。

> 現在のGitでは、既定のブランチ名がmasterからmainに変更されています。したがって、あなたのリポジトリでmasterを使っている場合、git branchで表示されるブランチ名が* masterになります。
mainとmasterの違いはブランチ名だけです。どちらもデフォルトブランチとして機能します。最近のGitバージョンではmainがデフォルトで作成されますが、古いバージョンではmasterが使われていました。どちらのブランチ名でも問題ありません。
リモートリポジトリにプッシュする際に関連付けを行うことで、今後同じブランチへのプッシュが簡単になります。-u（--set-upstream）オプションを使用することで、ローカルブランチとリモートブランチが関連付けられ、今後git pushやgit pullを実行する際にブランチ名を指定する必要がなくなります。
【備忘録】DB内のテーブルを再作成したときの後処理について

## 何が発生？
Adminerにて、とあるデータベース内にあるテーブルすべてを
```
TRUNCATE TABLE テーブル名;
```
でトランケート（テーブル内のレコードのみを削除）し、
Seederでデータを一括登録し直し、
migrateした際、

:::note alert
SQLSTATE[42S01]: Base table or view already exists: 1050 Table 'users' already exists (Connection: mysql, SQL: create table `users` (`id` bigint unsigned not null auto_increment primary key, `name` varchar(255) not null, `email` varchar(255) not null, `email_verified_at` timestamp null, `password` varchar(255) not null, `remember_token` varchar(100) null, `created_at` timestamp null, `updated_at` timestamp null) default character set utf8mb4 collate 'utf8mb4_unicode_ci')
:::
と出た件。
`users`という名前のテーブルが既に存在しているのに、再度作成しようとしているとのこと。

## いつ発生？
Laravelにて開発中案件のデータに不備があり、Gitでリモートのdevelopリポジトリから最新の情報をpullした際

## なぜ発生？
上司曰く、
> migrationsテーブルもトランケートした場合、最初からマイグレーションファイルを動かすので、その影響だと思う

とのこと。
というわけで、`migrations`テーブルの中身を（Seederで再登録してから）確認してみた。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3375549/631b163b-193d-9f2c-ea05-eafcae7054db.png)

なるほど、
#### `migrations` テーブル内のデータが、マイグレーションのログ、バージョン管理になっているので、このデータを消すとテーブルごと全部消さないと、再実行できない
みたいですね。

## 解決策
なので、今後、データを消す場合などは、
#### `migrations`テーブルを残して消す
か、
#### テーブルごと、ごっそり消すか
という対応を取ると良いそう。

ちなみに、トランケートする場合は、すべてのテーブルを一括してというコマンドが無いので、いちいちテーブル名を指定して、トランケートしたいテーブル名の数だけ
```
TRUNCATE TABLE テーブル名;
```
と書く必要があるみたいなので、実行したいテーブルが多いと・・・これはやめといたほうが良さげかな🤔
というわけで、テーブルごとごっそり消した上で、Seederの実行と一緒にmigrationまでを行なってくれる、
```
php artisan migrate:fresh --seed
```
を実行し、無事にエラーが出ずに通りました！

ちなみに、
php artisan migrate:freshとは別に、
`php artisan migrate:refresh`
というコマンドもあるみたいで、その違いをざっと調べてみたところ、

#### php artisan migrate:fresh
1. すべてのマイグレーションをロールバック（逆の手順で実行）して、テーブルを削除します。
2. 全てのマイグレーションを再実行して、新しいテーブルとカラムを作成します。

#### php artisan migrate:refresh
1. 一度実行されたマイグレーションをロールバックし、テーブルを削除します。
2. ロールバックされたマイグレーションを再実行し、テーブルとカラムを再作成します。
3. データベース内のデータは、ロールバック時に一時的に保存され、再実行後に再挿入されます。

うん、わからん😅

もうちょい詳しく調べてみると・・・

「refresh」のほうは、
1. マイグレーションテーブルのデータ: migrations テーブルに格納されているマイグレーションの実行状態は保持されます。これにより、マイグレーションの実行履歴が保持されます。
2. Seeder の実行結果: DatabaseSeeder クラスなどで定義されたシーディング処理によって挿入されたデータは保持されます。つまり、シーダーで作成されたデータはリセット後も存在し、再実行されます。
ということらしい。

なら、今回は`migrations`テーブルの中身を消しちゃってたので、「fresh」のほうでよかったということですかね💡
【Kotlin】Manifest.permission.WRITE_EXTERNAL_STORAGEがエラーになる【AndroidStudio】

参考書を使用して、AndroidStudioでの開発を勉強している際、
ストレージ関連の権限の許可をする記述が必要でした。
テキストに書いてあるとおり、AndoroidManifest.xmlに
```
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
```
と記述しても動かず困りましたが、解決したので備忘録として置いておきます。

## 結論
`android.permission.WRITE_EXTERNAL_STORAGE`の部分が間違っていた。

## 現象

テキスト通り、AndoroidManifest.xmlに
```kotlin
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
```
と記述しているのに、
MainActivityで
```kotlin
val permission = ContextCompat.checkSelfPermission(  
    this,  
    Manifest.permission.WRITE_EXTERNAL_STORAGE  
)
```
とする(これもテキスト通り)と
`Unresolved reference: WRITE_EXTERNAL_STORAGE`
というエラーを吐く。

参考書通りに書いているのに何でじゃ‥

## 解決
AndoroidManifest.xml内の記述を
```kotlin
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
```
↓
```kotlin
<uses-permission android:name="android.Manifest.permission.WRITE_EXTERNAL_STORAGE" />
```
に修正することでエラーは解決された。


以上です。
どこかのどなたかの役に立てば幸いです。
【PHP】number_format(カンマ)で小数点も表示で余分な小数点の0出さない(.0)出さない

# コード1
```PHP
<?php
$num = '1000';
$num = '1000.5';
if ((string)(int)$num === (string)floatval($num)) {
    $result = number_format($num); 
} else {
    $result = number_format($num, 1); 
}
echo $result;
```

# コード2
```PHP
<?php
$num = '1000.5';
echo str_replace('.0','',number_format($num,1)); 
```

# 結果
```javascript
1,000
1,000.5
```
MacでSelenium

## はじめに
Mac で Selenium環境構築しようと下記を参考にしたけど少し古かった様子

https://qiita.com/yaju/items/4a7c300670d69037f5bb

urlib3
find_element_by_name
の対応をしたのでメモ


## pip3 インストール
```
pip3 --version
```
でバージョンが表示される人はスキップ。
されない人はＭａcがインストーラーを立ち上げてくれるので暫し待機。

#### pip3のアップグレード
```
python3 -m pip install --upgrade pip
```
#### バージョン確認
```
pip3 --version
pip 23.1.2 from /Users/<User Name>/Library/Python/3.8/lib/python/site-packages/pip (python 3.8)
```

## Selenium インストール
```
pip3 install selenium
```

## chromedriverインストール
```
pip3 install chromedriver-binary-auto
```

## urlib3のOpenSSL1.1.1サポート終了対応 （暫定
#### 参考先のコードだと実行エラーが発生する
>raise ImportError(ImportError: urllib3 v2.0 only supports OpenSSL 1.1.1+, currently the >'ssl' module is compiled with LibreSSL 2.8.3. See: >https://github.com/urllib3/urllib3/issues/2168

#### ダウングレード方法確認
https://stackoverflow.com/questions/76187256/importerror-urllib3-v2-0-only-supports-openssl-1-1-1-currently-the-ssl-modu

#### urlib3ダウングレード
```
pip install urllib3==1.26.6
```





## インストールしたものの確認
```
pip3 list

Package                  Version
------------------------ --------
chromedriver-binary-auto 0.2.6
pip                      23.1.2
selenium                 4.9.1
urllib3                  1.26.6
```

## テスト
```
python3 test.py
```
ブラウザが開いてGoogleでSeleniumが検索できていたら成功！

#### インストール先に合わせて修正する箇所
/Users/\<User Name\>/Library/Python/3.8/lib/python/site-packages/chromedriver_binary

#### test.py
```python
# coding:utf-8
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from time import sleep
from selenium.webdriver.chrome import service as fs

# ブラウザを開く
chrome_service = fs.Service(executable_path='/Users/<User Name>/Library/Python/3.8/lib/python/site-packages/chromedriver_binary')
driver = webdriver.Chrome(service=chrome_service)
# Googleの検索TOP画面を開く
driver.get("https://www.google.co.jp/")

# 検索語として「selenium」と入力し、Enterキーを押す。
# search = driver.find_element_by_name('q')  #old
search = driver.find_element("name", "q") #Selenium v4.3.0
search.send_keys("selenium")
search.send_keys(Keys.ENTER)

# 5秒間待機してみる。
sleep(5)
# ブラウザを終了する。
driver.close()
```


#### find_elements_by_がなくなったので記述変更対応済み

https://stackoverflow.com/questions/72773206/selenium-python-attributeerror-webdriver-object-has-no-attribute-find-el

今までの記述
driver.find_element_by_name('q')

Selenium 4.3.0以降の記述
driver.find_element("name", "q") #new
CKA(Certified Kubernetes Administrator),CKAD(Certified Kubernetes Application Developer)を更新しました

# はじめに
CKA,CKADを取得してから3年経ちましたので更新しました。
日本語版のCKA-JP,CKAD-JPを受けました。

# 教材
## CKA
1. KodeKloud:[CKA Certification Course – Certified Kubernetes Administrator](https://kodekloud.com/courses/certified-kubernetes-administrator-cka/)
1. KodeKloud:[Ultimate Certified Kubernetes Administrator (CKA) Mock Exam Series](https://kodekloud.com/courses/ultimate-certified-kubernetes-administrator-cka-mock-exam/)
1. KodeKloud:[Kubernetes Challenges](https://kodekloud.com/courses/kubernetes-challenges/)
    - いつものKodeKloudです。「Mock Exam Series」という模擬試験(20問)が新しく追加され、やりがいがある内容となっています。ただ、不具合(環境が構築されない等)が多々あり、そこは改善して欲しいです。
1. KLLR SHLL:[Kubernetes Exam Simulator](https://killer.sh/dashboard)
    - 試験を購入するとおまけで付いてくるシミュレータです。本番試験を同等の環境のため、操作感をここで慣れます。コピー操作に慣れるために、1回目は本番試験の1週間前、2回目は本番前日に行うと良いと思います。
## CKAD
1. KodeKloud:[Certified Kubernetes Application Developer (CKAD)](https://kodekloud.com/courses/certified-kubernetes-application-developer-ckad/)
1. KodeKloud:[Ultimate Certified Kubernetes Application Developer (CKAD) Mock Exam Series](https://kodekloud.com/courses/ultimate-certified-kubernetes-application-developer-ckad-mock-exam-series/)
1. KodeKloud:[Kubernetes Challenges](https://kodekloud.com/courses/kubernetes-challenges/)
    - 内容は割愛。
1. KLLR SHLL:[Kubernetes Exam Simulator](https://killer.sh/dashboard)
    - 内容は割愛。
上記の模擬試験を100%正答できる状態になってから本番に挑みました。
# 当日試験について
- 試験内容は日本語ですが、試験ツール、手続き、試験官とのチャットは全て英語となります。
- それ以外は、以前受けた[CKS](https://qiita.com/ttuser/items/ef514fb579b9e65c0d18)と同じです。
- 試験はリモートデスクトップで行いますが、CKSの時と異なる点がありました。CKSでは、複数の仮想デスクトップがあり、操作用(ターミナル、テキストエディタ)とドキュメント参照用(ブラウザ)で使い分けをしていましたが、今回は仮想デスクトップが「×」表示となっており使用できませんでした。このため、一つの画面で操作・参照をしました。
- CKA
    - 問題数は17問
- CKAD
    - 問題数は16問
    - Helm,CRDは出題されず

# 感想
- CKSと比べ難易度は低く、解けない問題は無く、時間に余裕を持った状態で終えることができました(どこかでケアレスミスがあったようで満点ではありませんでした。。。)。
JavaScriptのfilter、mapメソッドについて

JavaScriptのmapメソッドは、与えられた配列の要素一つ一つを取り出し、そのそれぞれに対して指定した処理を行った結果を新しい配列として返します。

以下はmapメソッドの使用例です。
```.js
const array1 = [1,2,3,4];
const map1 = array1.map(x => x * 3);

console.log(map);//> Array [3, 6, 9, 12]
```

この例では、array1という名前の配列を定義しています。次に、mapメソッドを使用してarray1の各要素に対してアロー関数(x => x * 3)を適用し、新しい配列map1を作成しています。このアロー関数は、与えられた数値を3倍する処理を表しています。

最終的に、map1配列にはarray1の各要素を3倍した値が格納されています。

ちなみに、mapメソッドをconsole.logで出力すると、配列から一つづつ値を取り出していることがわかります。

```.js
const array1 = [1,2,3,4];
const map1 = array1.map((x, i, array1) =>
    console.log(x);
);
/* xにはarray1の配列内のそれぞれの値が取り出されて格納されるため、
console.log(x)は下記のようになります。
> 1
> 2
> 3
> 4
*/
```
filterメソッドでは、配列のなかで指定した条件を満たす要素のみを新しい配列の要素として返します。

例えば、以下のような処理を考えてみます。
```.js
array2 = [10, 20, 30, 40, 50];

filterdArray = array2.filter(x => x > 20);
console.log(filterdArray);
//>[30, 40, 50]と表示されます。
```
この例ではfilterメソッドを適用した際、array2の要素[10, 20, 30, 40, 50]を一つづつ変数xとして取り出し、そのxが20より大きければfilterArray配列の要素として追加、小さければ次の要素の比較という処理を行います。

またmapメソッドとfilterメソッドは、メソッドチェーンで繋げて使用することができ、ある配列の要素を２倍したもののうち、値が30より大きいものだけを抽出して配列を作りたいと言った時に便利です。


```.js
const array = [10 ,20 ,30 ,40];
const mapFilter = array.map(x => x * 2).filter(x => x > 30);

console.log(mapFilter);//> [40, 60, 80]
```
【WWDC23】SwiftUIのPreviewで@Stateを使用する方法

# はじめに
2023年6月6日、今年もWWDCが開催されました。
SwiftUIにもいくつか変更があり、その中でもまずPreviewの変更が目につきました。

# Preview
以前までは、SwiftUIでBindingを使用した時のプレビューはこのように書いていました。

```swift
import SwiftUI

struct ContentView: View {
    @Binding var text: String
    
    var body: some View {
        VStack {
            TextField("", text: $text)
        }
        .padding()
    }
}

struct ContentView_Previews: PreviewProvider {
    @State static var text: String = ""
    static var previews: some View {
        ContentView(text: $text)
    }
}
```
---
ところが、今回からPreviewの書き方が大きく変わり、このように書くことでPreviewにStateを持たせられるようになりました。

```swift
import SwiftUI

struct ContentView: View {
    @Binding var text: String
    
    var body: some View {
        VStack {
            TextField("", text: $text)
        }
        .padding()
    }
}

#Preview {
    @State var text = ""
    
    return ContentView(text: $text)
}
```

# おわりに
だいぶ簡潔に書けるようになりました😆

# Twitter
https://twitter.com/shota_appdev/status/1665917556547538945?s=61&t=xtcewpXbSQDZlJyYFYBAOg

# 参考
https://developer.apple.com/videos/play/wwdc2023/102/
汎用プログラミング言語におけるアルゴリズムの知識をRDBMSにおける知識として再利用しよう

　こんにちは。ディマージシェアの技術担当です。今回は、とても簡単なアルゴリズムの課題を題材に、いくつかの回答の紹介、加えてRDBMSのインデックスの仕組みまで一気通貫の知識として紹介したいと思います。

# 課題
　いわゆるkey value形式で保存されているデータがsrc, tgtの2つあり、f_keyの値が同じものを関連付けてsrc_idとtgt_idを同じレコードにしたい、という課題です。

```
src = [{src_id=1, f_key=f_key_1}, {src_id=2, f_key=f_key_2}, {src_id=3, f_key=f_key_3}]
tgt = [{tgt_id=111, f_key=f_key_1}, {tgt_id=222, f_key=f_key_2}, {tgt_id=333, f_key=f_key_3}]
```

上記の2つの配列に分かれたデータを、
```
[{src_id=1, tgt_id=111, f_key=f_key_1}, {src_id=2, tgt_id=222, f_key=f_key_2}, {src_id=3, tgt_id=333, f_key=f_key_3}]
```
のように纏めることができればOKです。

# いくつかの回答例
**2重ループを書く**

　2重ループで上記の課題を実現しようとすると、例えば次のようになります。言語はJavaを採用しました。

```java
for (Map<String,Object> p : src){
    String src_f_key = (String)p.get("f_key");
    for (Map<String,Object> q : tgt){
        String tgt_f_key = (String)q.get("f_key");
        if (src_f_key.equals(tgt_f_key)) {
            p.put("tgt_id", q.get("tgt_id"));
        }
    }
}
```

**HashMapを使う**

　HashMapを使うと次のように書くこともできます。
```java
Map<String, Map<String,Object>> tgt_map = new HashMap<>();

for (Map<String,Object> p : tgt){
    tgt_map.put((String)p.get("f_key"), o);
}

for (Map<String,Object> p : src){
    p.put("tgt_id", tgt_map.get((String)p.get("f_key")).get("tgt_id"));
}
```

**TreeMapを使う**

　JavaのMapの実装はHashMapの他にTreeMapもあります。
```java
Map<String, Map<String,Object>> tgt_map = new TreeMap<>();

for (Map<String,Object> p : tgt){
    tgt_map.put((String)p.get("f_key"), o);
}

for (Map<String,Object> p : src){
    p.put("tgt_id", tgt_map.get((String)p.get("f_key")).get("tgt_id"));
}
```
# どの方法が正解？
　さて、簡単なお題ではありますが、3通りもの回答例を挙げることができました。どの方法を選ぶのが最適でしょう。実は、入力するデータの性質によって最適な方法が異なります。

# 2重ループは悪？
　ある程度アルゴリズムの性質に明るい人は**2重ループ**に難色を示す方が一定数いると思います。**データの件数が多いとき**に2重ループを書くと、確かに他に挙げたMapを使った方法の方が早いのですが、実は、**データの件数が極めて少ないときに限り**、2重ループはとても高速に動作します。
　ただ、2重ループは処理するデータ件数がちょっと増えるだけでパフォーマンスが悪くなり事故に繋がることもあるので、とりあえずMapで書けることを2重ループで書いている箇所を見つけたらMapに書き直すように指示するのは安全とも言えます。

# Mapはどのタイプを使う？
　Mapの実装は主にHashとTreeの2通りが選べます。今回の課題ではHash方式を選ぶ方が良いでしょう。Treeは保持しているデータをソートや範囲で絞り込みたいときに威力を発揮します。
　（Hashはソートや範囲絞り込みができない代わりに、keyでデータを1つ指定する処理がとても高速です）

# RDBMSとの関連
　これまでの文脈からRDBMSの話が出てくるのが意外に思う方もいらっしゃるかもしれません。実は、RDBMSのインデックスの仕組みに強い関連があります。
　今回の課題は、RDBMSの2テーブルをf_keyが一致する条件でJOINしてSELECTする処理に他なりません。このとき、2つのテーブルのf_keyカラムにINDEXが貼られていないと、レコード数が多いときにクエリの実行に時間がかかることをご存じの方は多いと思います。これは、先のJavaコードで2重ループを採用した場合とほぼ同じなのです。f_keyカラムにHashインデックスを貼ればHash方式に、Treeインデックスを貼ればTree方式に等しくなります。
※インデックスのタイプが選べないRDBMSではTree方式が標準で採用されています
　RDBMSのインデックスは、レコードを検索しやすい形に予め持ち替えておき、クエリの実行を高速化する仕組みです。Javaコードで、Mapを用いて特定の列で検索を早くできるようにデータを持ち替えたこととほぼ同じです。
RDBMSでインデックスとしてストレージに保存されている部分をJavaコードでオンタイムで生成したようなものです。
 　逆に言えば、RDBMSでインデックスを貼らない、インデックスを貼る、のようなパフォーマンスチューニングの選択は、Javaコードでは2重ループを採用する、Mapに持ち替える方法を採用する、に言い換えることができます。実行するエンジンが違っても本質的には大差ないのです。

# まとめ
　簡単なアルゴリズムの課題でも、回答例は多岐に渡ることを紹介しました。また、似たようなことをRDBMSで実現する際、インデックスの有無がアルゴリズムの選択にとても似ていることを紹介しました。
　アルゴリズムに関する知識は本質を上手に捉えることで、Javaのような汎用プログラミング言語から、RDBMSまで共通して役に立てることができます。
プログラミングにおける「文字列」に関する理解を深めよう

　こんにちは。ディマージシェアの技術担当です。今回はプログラミングをする際、ソースコード上で扱う「文字列」に関して少し掘り下げてみます。
　プログラミング言語における文字列の扱いは、最近の言語ではコーディングに都合が良いように上手く言語設計がされています。JavaScriptやPHPなどは文字列をあたかも数値型のように扱うことができます。Javaでは文字列であることを少し意識していないと初歩的なバグを埋め込んでしまうことがあります。C言語あたりになると、文字列は型としては定義すらされていません。
　これらの違いは、1つの言語を使い続けているうちは大きなトラブルになりませんが、「便利に実装された言語 => 原始的な言語」の方向に使う言語が変わったとき、正しい知識を持っていないとトラブルが発生することがあります。
　本記事ではいくつかのプログラミング言語における文字列の扱いと、言語ごとの扱いの違いにまつわるトラブルを紹介したいと思います。

# 文字列の定義はプログラミング言語内での取り決めに過ぎない
　例えばC言語における文字列は次のような取り決めです。

```
与えられたアドレスを起点に\0以外の文字が連続するバイト列
\0の出現をもって終端とみなす
```

※「\0」は「null文字」を表すときによく使われる記法です

なので、"abc\0"というような並びでメモリ上に配置され、変数の値としてはaの場所（アドレス）という扱いになります。型は、メモリ上のアドレスなので一般の計算機ではCPUのレジスタ長の整数（ptr_t）になります。C言語の世界では型としての文字列型は定義されておらず、整数値で始まる場所を伝えたら、そこを起点に\0が出現するまでを文字列として扱います。

　Javaではどうでしょうか。おおまかに次のようになっています。

```
文字列開始位置、文字列長をペアで型として管理
型の値としてはメモリ上の位置
```

という感じです。C言語との違いは、"abc\0def"のように、文字列内にnull文字が許容されるようになり、終端の判定は文字列の長さを型に内包する形で実現しています。この構造がString型という文字列専用の型として用意されています。

　PHPでは更に便利になり、

```
文字列開始位置、文字列長、その他メタ情報を型として管理
型の値としては文字列
```

という感じになっています。Javaとの違いはこれだけではわかりにくいですが、

```php
<?php
$a = "abc";
$b = "abc";
if ($a == $b) {
}
```

のように演算子で直接比較ができます。Javaでは

```java
String a = "abc";
String b = "abc";
if (a.equals(b)) {
}
```

と書く必要があり、Javaで文字列型同士を==で比較すると不具合の原因になります。Javaの場合、文字列型同士を==で比較した場合は、「文字列の場所が同じかどうか」という処理になり、コンパイルエラーにはならず、実行時のバグとして開発者を悩ませることになります。

# よくあるトラブルと解決するための知識
**Javaあるある**

　先ほど、Javaでは
```java
String a = "abc";
String b = "abc";
if (a == b) {
}
```
　のようにコーディングするとバグの原因となる旨を書きました。ところが。上記のソースはa == bの結果trueになり、書いた人の思った通りの結果になってしまいます。ちょっと知識のある開発者ならば、a == bがfalseになってほしいところです。このコードを、
```java
String a = new String("abc");
String b = new String("abc");
if (a == b) {
}
```

と直すと、a == bがfalseになり、予定通りバグるはずです。

　Javaでは、文字列リテラルをソースコード内で使いまわすと文字列リテラルの位置を使いまわして変数に格納するため、コード内で"abc"と2か所に書いても変数には同じ値が入るようにコンパイルされます。なので、文字列リテラルを直に変数に代入する書き方だと、予期しない予想通りの挙動、という難解な挙動に悩まされる可能性があります。多少冗長ではありますが、new String("abc"); の書き方を積極的に選んだ方が、不思議なトラブルに遭遇しにくくなります。

# 処理系をまたぐ文字列に注意
　例えばPHPやJavaの文字列をC言語で作られた処理系に渡す際、null文字に十分に注意する必要があります。nullバイトインジェクションという有名な攻撃もあります。次のコード片はphp.netから引用しました。
```php
<?php
$file = $_GET['file']; // ここで "../../etc/passwd\0" が渡されたとします
if (file_exists('/home/wwwrun/'.$file.'.php')) {
    // file_exists は true を返します。これは、ファイル /home/wwwrun/../../etc/passwd が存在するからです
    include '/home/wwwrun/'.$file.'.php';
    // ファイル /etc/passwd がインクルードされてしまいます
}
?>
```
https://www.php.net/manual/ja/security.filesystem.nullbytes.php

nullバイトを含む文字列をC言語で作られた処理系にそのまま渡してしまうと意図しない動作が起こることがあります。

# まとめ
　今回は、いくつかのプログラミング言語における文字列の扱いと、それにまつわるトラブルを紹介しました。文字列に関する知識は正しく保有していないと、単なるバグを引き起こすだけでなく、場合によっては致命的な脆弱性を埋め込んでしまう可能性があります。
　ソースコードを書くエンジニアが複数のプログラミング言語それぞれに精通するのはとてもコストがかかるので、言語を横断したときにトラブルになりうるポイントをピックアップしてまとめてみると面白いかもしれません。
あまり知られていなそうな、すごく便利なショートカットなどまとめ

## はじめに
「あまり知られていなそうな」は、IT学生2年目の僕視点のものであります
Windows使ってます
ほかにもあったら教えてください

## Windows + Shift + S
切り取り範囲を選択してスクリーンショット

## Ctrl + Shift + T【Chrome】
直前に削除したタブを復元する
FPSやってるわりにエイムが悪く、誤って×を押してしまうことがよくあるので

## Ctrl + Y
Ctrl + Z の逆。直前の取り消しを取り消す

## Ctrl + クリック【Chrome】
表示しているタブを保存したまま、新しいタブで表示する

## マウスホイール長押し + マウス上下
すごい勢いで上下にスクロールできる
特に、Twitterで古い投稿にさかのぼるときに凄く便利

## F9 + F8
「python」って入力したいが半角全角の切り替えを忘れて「ｐｙてょｎ」となってしまったとき、
F9、F8を指をスライドするように押すだけで半角英語に変換できる

## デベロッパーツール -> Ctrl + Shift + p -> 「full」と入力【Chrome】
Web画面全体をスクリーンショットして保存できる

---
ほかにも思い出したら追加します。
ModuleNotFoundError: No module named 'werkzeug.contrib'

Profilerを使いたかったが上のエラーに遭遇。
https://dev.classmethod.jp/articles/taking_the_profiler_of_flask/
をやりたかった。

```
from werkzeug.contrib.profiler import ProfilerMiddleware
ModuleNotFoundError: No module named 'werkzeug.contrib'
```

最新werkzeugでは、middlewareにこのprofilerが入っているので、
contribの部分をprofilerで置き換えれば動いた。

https://werkzeug.palletsprojects.com/en/1.0.x/middleware/profiler/
【ChatGPT無】嘘吐きを減らそう（その２）


　どうも、カーブミラーです。

　今回は、
　　嘘吐き減らしその２です。


　以前に
[【ChatGPT】嘘吐きを減らそう](https://qiita.com/kabumira/items/2da48c6728b0c1f9a76d "【ChatGPT】嘘吐きを減らそう")

　で、「**わからない・知らない場合は、そう言ってください**」と書きました。

　次の嘘吐き減らしは、
[【ChatGPT】アプリ開発したい、と言ったら助長すぎる](https://qiita.com/kabumira/items/8dd989e655528e2ccfb5 "【ChatGPT】アプリ開発したい、と言ったら助長すぎる")
　にも書きましたが。

　ふつうは、ことわざの説明が、
　　約200文字くらいで
　　　返ってきます。

　それを
　　「**ひと言で教えてください**」
　　　　とすると、40文字程度に
　　　　　なりました。

　また、
　　箇条書きなどで、
　　　説明が長いと思ったら、

　　「**手順をひと言ずつで教えてください。**」
　　　とすると、
　　　　かなり、わかりやすいと
　　　　　思います。

　　それで、
　　　わからなければ、
　　　　その部分だけを
　　　　　尋ねるだけです。

　「**ひと言で**」というのは、
　　意外と、
　　　嘘が吐きにくいもの
　　　　のようですね。

　　これは、おそらく、
　　　表現方法が、
　　　　抑えられてしまい、
　　　説明を
　　　　たくさんしたい（？）
　　　　　ChatGPTさんが、
　　　　　　限られた文字数で、
　　　　なんとかしようと
　　　　　もがいた結果なのでしょう。

　　文字数ということを考えれば、
　　　よく言われる
　　　　「文字数を指定する」
　　　　　というのと、
　　　　　　同じですね。


　最後に、
　　「**ひと言**」のオマケです。

　NewChatを開き、
>ここからは、すべて、ひと言で返答してください。

　と入力。
```Text:チャット内容
ChatGPT
了解です。どのような質問に対してもひと言で返答いたします。

User
ありがとうございます。
では、お互いにやり取りをして、物語を作りましょう。
まずは、私から。
むかしむかし

ChatGPT
あるところに、小さな村がありました。

User
その村には

ChatGPT
陽気な若者が住んでいました。

User
若者は、村人たちに好かれていて、幸せです。

ChatGPT
ある日、村に旅人がやってきました。旅人は疲れ切っていて、若者のもとに助けを求めました。

User
若者は、その求めに応じて、旅人を自宅に連れ帰り、ベッドに休めました。

ChatGPT
旅人は深い眠りにつき、次の朝目覚めると、若者が作った美味しい朝食が待っていました。

User
旅人は、よろこび、礼を言いながら、朝食を食べました。

ChatGPT
朝食を楽しみながら、旅人は自分の冒険の話を若者に語り始めました。

User
若者は黙って聞きます。

ChatGPT
旅人が冒険の中で出会った驚くべき出来事や美しい風景を描写するにつれ、若者の目には冒険の興奮が宿りました。彼は自分も新たな世界を探検したくなりました。

User
陽気な彼でしたが、以前から、村から出たいと思っていたのです。

ChatGPT
旅人の話を聞いて、若者は自分の心の声に気づきました。彼は村の人々に別れを告げ、旅立つことを決意しました。

User
旅人が癒えるのを待ち、ともに旅に出た若者ですが、初めての旅で注意散漫です。
旅人が注意します。

ChatGPT
旅人は若者に対して優しく微笑みながら、「旅は楽しいけれど、危険も潜んでいるものだよ。常に注意を怠らず、周りを見て行動しよう」と忠告しました。若者は反省し、旅の大切さと重要性を再認識しました。
```

　まぁ、
　　お互いに、
　　　ひと言ですまなくなるのは、
　　　　仕方ないんですかねぇ。

　で、最後に
>ChatGPTさん、ここまでをまとめてもらえますか？

　と言って、
　　まとめてもらったのが、
　　　こちら
```Text:まとめ
もちろんです。物語をまとめます。

むかしむかし、陽気な若者が住む小さな村がありました。若者は村人たちに好かれ、幸せな日々を送っていました。ある日、村に疲れ切った旅人がやってきました。若者は旅人を助け、自宅に連れ帰り、休ませました。旅人は感謝の気持ちを込めて朝食を食べ、話を始めました。

若者は静かに聞き入りました。旅人が語る冒険の物語に興奮し、自分も新たな世界を探検したいという思いが湧き上がりました。彼は村を離れる決意をし、旅人とともに旅に出ることにしました。

しかし、初めての旅において、若者は注意散漫な行動が目立ちました。旅人は若者に注意を促し、危険に備えるよう忠告しました。若者は反省し、旅の重要性と注意の必要性を再認識しました。

以上が物語のまとめです。
```

　まぁまぁでは？
***
　ほかにも書きました。
[【ChatGPT無】嘘吐きを減らそう（その３）](https://qiita.com/kabumira/items/c04419e9dd6a72ab70ec)

[【ChatGPT無】嘘吐きを減らそう（その４）](https://qiita.com/kabumira/items/ea9d668d665c69478ce2)


　参考にしてくださいね。
ChatGPTに普通に負けた話【穴掘り法】

# 自分で迷路を作る
ある日ふと思い立ち、迷路を作るスクリプトを作成してみた。
アルゴリズムが複雑でなく、出来る迷路もそこそこ複雑性のある、
軽く実装するにはうってつけの穴掘り法で実装した。
しかしながら、アルゴリズムは簡単なものの、実装が煩雑で、
言ってしまえば普通にめんどくさく、
境界値の検査やtypescript特有の配列操作のやりづらさもあり、
半日もかかってしまった。
しかも実装がめちゃくちゃ、もっといい方法がないかと考えていたが、
思いつかない。しかもめんどくさい。
勝手に実装してくれたらいいのにと思っていた。

# ChatGPT大先生
ということで登場してもらったChatGPT先生。
噂には聞いていたが使うのは初めてである。
所詮AIごときに私様の書いたコードより優秀なコードが書けるわけ無いという思い半分、
普通に負けるだろうという思い半分で、使わなければその答えは出ない、
ということで使わないようにしてたChatGPTである。
このシュレーディンガーの実験装置に押し込まれた私様のプライドを観測してみようじゃないか。

# 普通に負けた
普通に負けました。5分で作りました。
しかも大体指示が悪かっただけという結果。
実験装置の中でプライドがズタズタにされて死んでいた。

# 大先生、その優秀なる頭脳
ということで、前置きが長くなったが、大先生の優秀なる頭脳と
ポンコツプログラマとのやりとりの一部始終を記載していこうと思う。


```
> please japanese
```

> はい、どのようなお手伝いをいたしましょうか？
> 日本語での質問や翻訳のお手伝いなど、なんでもお伺いいたします。

```
> あなたはプログラムの専門家です
```

> はい、私はプログラムの専門家です。
> プログラミングに関する質問や助言が必要な場合は、お気軽にどんな内容でもお尋ねください。
? どのようなプログラミング言語や技術についての情報をお求めですか？

```
> 以降の会話では、コードを書くときはtype scriptで書いてください
```

> 了解です。以降の会話では、TypeScriptでコードを記述します。ご了承ください。

```
> 穴掘り法で迷路を作るスクリプトを書いてください
```


> 以下は、TypeScriptで穴掘り法を使用してランダムな迷路を生成するスクリプトの例です。

<details><summary>コード</summary>

```typescript
class MazeGenerator {
  private rows: number;
  private cols: number;
  private maze: number[][];

  constructor(rows: number, cols: number) {
    this.rows = rows;
    this.cols = cols;
    this.maze = [];

    // Initialize maze grid
    for (let i = 0; i < this.rows; i++) {
      this.maze[i] = [];
      for (let j = 0; j < this.cols; j++) {
        this.maze[i][j] = 1; // 1 represents walls
      }
    }
  }

  generateMaze(): number[][] {
    this.digRecursive(0, 0);
    return this.maze;
  }

  private digRecursive(row: number, col: number): void {
    const directions = this.shuffleDirections();

    for (const direction of directions) {
      const [dx, dy] = direction;
      const nextRow = row + dx * 2;
      const nextCol = col + dy * 2;

      if (this.isValidCell(nextRow, nextCol)) {
        this.maze[nextRow][nextCol] = 0; // 0 represents paths
        this.maze[row + dx][col + dy] = 0;
        this.digRecursive(nextRow, nextCol);
      }
    }
  }

  private shuffleDirections(): number[][] {
    const directions = [[-1, 0], [1, 0], [0, -1], [0, 1]];
    for (let i = directions.length - 1; i > 0; i--) {
      const j = Math.floor(Math.random() * (i + 1));
      [directions[i], directions[j]] = [directions[j], directions[i]];
    }
    return directions;
  }

  private isValidCell(row: number, col: number): boolean {
    return row >= 0 && row < this.rows && col >= 0 && col < this.cols && this.maze[row][col] === 1;
  }
}

// Example usage
const rows = 10;
const cols = 10;
const generator = new MazeGenerator(rows, cols);
const maze = generator.generateMaze();

// Print the generated maze
for (const row of maze) {
  console.log(row.join(" "));
}
```

> このスクリプトでは、指定した行数と列数で初期化された迷路グリッドを作成し、
穴掘り法アルゴリズムを使用してランダムな迷路を生成します。
迷路は2次元の配列として表され、1は壁を、0は通路を示します。
最後に、生成された迷路をコンソールに表示します。

</details>

# あとがき
実に<b>30倍の速度</b>で実装してくれたChatGPT大先生の次回作にご期待ください。
私は死んだプライドを抱えながら大先生に実装コードをすべて丸投げして寝ます。
サポーターズ1on1面談に参加しました

# はじめに
はじめまして、[546](https://twitter.com/8pGCJmg4nVU2o37)と申します。2023年5月に開催された、サポーターズの1on1面談に参加しましたので、そのレポートを書こうと思います。

# サポーターズ1on1面談とは
- 人気企業最大8社のエンジニアや人事担当と個別で話せる逆求人イベントです。
- 参加した学生は最大10,000円+αの就活応援支援金をプレゼントされます。

# 自己紹介
- 情報科学科に通う大学3年生。
- サポーターズの技育CAMPに3度参加し、入賞した経験あり。
- 普段は競技プログラミングをしています。

# 事前面談について
自己紹介プレゼンを添削したり、当日の流れを確認したりします。


競技プログラミングのことや、大学の実習で作成したパズル、ハッカソンのことなどについて書きました。

# 当日の流れ
<table>
  <tbody>
    <tr>
      <td>企業プレゼンテーション</td>
      <td>企業の5分ほどの説明を聞き、アンケートに記入します。</td>
    </tr>
    <tr>
      <td>昼休憩</td>
      <td>記入したアンケートをもとに、どの企業と面談するか発表されます。</td>
    </tr>
    <tr>
      <td>個別面談</td>
      <td>5分の自己紹介の後、20分の面談に移ります。</td>
    </tr>
  </tbody>
</table>

# 企業プレゼンを聞いた感想
- ゲーム系やweb系の企業が多かったです。
- 1on1面談に参加するまで知らなかった企業でも、魅力的な企業がありました。
- 競プロやKaggleのSlackがあったり、勉強会を行っている企業もありました。

# マッチングについて
- もともと面談を希望していた企業とは面談できませんでしたが、企業プレゼンを聞いて興味を持った企業とマッチングできました。
- サポーターズの1on1面談は最大8社と面談できると謳っていますが、私は4社としか面談できませんでした。
- 企業の数が15で学生の数が30であること、8ターン面談の機会が与えられていることを考えると、ほとんどの学生が15x8÷30=4社としかマッチングできないと言えるでしょう。

# 面談について
- どの企業も和やかな雰囲気で面談が進みました。
- 基本的には、人事の方とエンジニアの方と自分の3人で面談しました。
- 将来像や価値観などについてよく聞かれました。
- こちらから質問する機会も設けてくださり、エンジニアの方が普段どのような業務をされているのかを聞くことができました。

# フィードバックについて
- 面談が終了すると、各企業からフィードバックを受けることができます。
- フィードバックは基本的には褒めてくれます。

# 参加してみて
- エンジニアの方が普段どのような業務をしているか知ることができ、将来のイメージを掴むことができました。
- もともと希望していた企業とマッチングできなかったのは残念です。（アンケートきちんと書きましょう。）
Reactを使用してログイン中のGoogleアカウントを確認するための簡単な実装例

```react
import React, { useState } from 'react';
import GoogleLogin, { GoogleLogout } from 'react-google-login';

const GoogleAuth = () => {
  const [loggedIn, setLoggedIn] = useState(false);
  const [userProfile, setUserProfile] = useState(null);

  const handleLoginSuccess = (response) => {
    setLoggedIn(true);
    setUserProfile(response.profileObj);
  };

  const handleLogoutSuccess = () => {
    setLoggedIn(false);
    setUserProfile(null);
  };

  const handleLoginFailure = (error) => {
    console.log('Google login failed:', error);
  };

  return (
    <div>
      {loggedIn ? (
        <div>
          <p>ログイン中のGoogleアカウント:</p>
          <p>名前: {userProfile.name}</p>
          <p>メールアドレス: {userProfile.email}</p>
          <GoogleLogout
            clientId="YOUR_GOOGLE_CLIENT_ID"
            buttonText="ログアウト"
            onLogoutSuccess={handleLogoutSuccess}
          />
        </div>
      ) : (
        <GoogleLogin
          clientId="YOUR_GOOGLE_CLIENT_ID"
          buttonText="Googleアカウントでログイン"
          onSuccess={handleLoginSuccess}
          onFailure={handleLoginFailure}
          cookiePolicy={'single_host_origin'}
        />
      )}
    </div>
  );
};

export default GoogleAuth;
```


上記のコードでは、GoogleAuthコンポーネントがログイン状態を管理し、ログイン成功時にはユーザープロファイル情報を表示します。ログアウト時にはログアウト処理が行われます。

以下の手順で実装を行います。

Google Developer Console（https://console.developers.google.com/）にアクセスして、プロジェクトを作成し、OAuth 2.0 クライアント IDを取得します。
YOUR_GOOGLE_CLIENT_IDを取得したクライアント IDに置き換えます。
GoogleAuthコンポーネントを他のコンポーネントで使用すると、ログイン中のGoogleアカウント情報が表示されます。

この実装は、react-google-loginパッケージを使用しており、Google Sign-In APIとの連携を簡素化しています。必要に応じてスタイリングやエラーハンドリングなどを追加してください。
【エラーメモ】cloudhubにデプロイエラーの対処法

# エラー内容
cloudhubにデプロイした際に"java.io.IOError: java.io.IOException: Wrong index checksum, store was not closed properly and could be corrupted."とエラーが発生した。

# 原因分析
Anypoint Studioが正常にシャットダウンされず、キャッシュファイルが破損していることが原因である可能性がある。

# 解決案
**”clear application data”** でAPIのキャッシュをクリアすることで解決した。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3343120/9bc54ce4-b088-b35c-d6ca-43bc99180504.png)

ちなみに、毎回デプロイする場合、Object storeとVM dataを削除したい場合は、run configurationで設定できます。
AnyPoint Studio - go to run as configuration -> clear application data -> Always

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3343120/fe5d63f5-e680-78ac-64bf-fdc33e1a07c0.png)
サーバレススクレイピング環境をCDK, Lambda, Puppeteerで作る

# Serverless Puppeteer on AWS Lambda

サーバレス環境でスクレイピングがしたい人のために、CDKのサンプルを作成しました.

CDKを実行すると、スクレイピングに必要なレイヤーを作成し、Lambdaを実行することでスクレイピングが行えます.

Github: https://github.com/rv-rescala/jp-serverless-puppeteer

- 動作確認済み環境: MacOS Monterey
- 言語: Typescript

# 事始め

あらかじめAWS CDK, typescript, nodeをインストールしてください.

## インストール

```bash
git clone https://github.com/rv-rescala/jp-serverless-puppeteer
npx @puppeteer/browsers install chromium@latest --path /tmp/localChromium
npm install @sparticuz/chromium@109 puppeteer-core@19.4
make init
```

## 環境変数の設定

```bash
export IS_LOCAL=true    
export BROWSER_PATH=/tmp/localChromium/chromium/mac_arm-1153064/chrome-mac/Chromium.app/Contents/MacOS/Chromium # chack your path
```

## ローカルでのテスト実行

```bash
npx ts-node test/test-browser.ts
```

## AWSへのデプロイ

```bash
cdk synth --all
cdk deploy --all
```

## Lambdaの実行

"jp-serverless-puppeteer-test-puppeteer-handler"をLambda上で実行してください.

# Reference
- [puppeteer-aws-lambda](https://www.cloudtechsimplified.com/puppeteer-aws-lambda/)
- [@sparticuz/chromium](https://github.com/Sparticuz/chromium)
おすすめのChrome拡張機能

# はじめに
ここではおすすめのChrome拡張機能を紹介します。
基本的には有名どころを使っていますが、そうでないものもありますのでセキュリティには考慮して入れるかの判断をしてください。

# 画面録画ツール | Loom
https://chrome.google.com/webstore/detail/loom-%E2%80%93-screen-recorder-sc/liecbddmkiiihnedobmlmillhodjkdmb?hl=ja&gl=DE

:::note info
画面の録画をしてくれます。
画面全体、ウィンドウ、Chromeタブで録画が可能です。
:::


# 翻訳ツール | DeepL
https://chrome.google.com/webstore/detail/deepl-translate-reading-w/cofdbpoegempjloogbagkncekinflcnj?hl=ja

:::note info
選択した範囲の翻訳が可能です。
DeepLの翻訳なので翻訳精度が高いです。
:::
![Animation.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264944/de93ab54-f646-2586-44c7-62566970e1d2.gif)

# 翻訳ツール | Google 翻訳 - 簡単翻訳
https://chrome.google.com/webstore/detail/translator-pro-quick-tran/bebmphofpgkhclocdbgomhnjcpelbenh

:::note info
ダブルクリックで選択するだけで翻訳してくれるのでわからない単語がすぐに調べられます
:::
![Animation2.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264944/370e928e-ab8a-3f67-4e11-e338ac252e19.gif)

# 文法チェック/スペルチェックツール | Grammarly

https://chrome.google.com/webstore/detail/grammarly-grammar-checker/kbfnbcaeplbcioakkpcpgfkobkghlhen?hl=ja

:::note info
記述した英文の文法やスペルミスをチェックしてくれます。
特に有用なのが文法のミスもチェックしてくれるところになります。
:::

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264944/665622b9-38c9-36dd-2328-e9cc421a6558.png)

# 価格遷移チェックツール | Keepa
https://chrome.google.com/webstore/detail/keepa-amazon-price-tracke/neebplgakaahbhdphmkckjjcegoiijjo?hl=ja

:::note info
Amazonでの価格の遷移をチェックできます。
セールのはずが、価格が変わっていないなどのケースをキャッチできるようになります。
:::

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264944/f4986811-aff4-9f7a-7cd4-8b8dcfe06e83.png)


# 検索ツール | Selection Search
https://chrome.google.com/webstore/detail/selection-search/gipnlpdeieaidmmeaichnddnmjmcakoe

:::note info
選択した単語を任意のエンジンで検索ができます。
新しいエンジンを追加できるので社内のWikiなどで検索するようにすることもできます。
右クリックコンテクストメニューで検索できるようにしたい場合はオプション -> Toolbar Popup を 「Enabled」に変更してください。
:::

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264944/9250bf28-d56f-73e3-ac57-2ebc7aa231ff.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264944/e946353e-ff8e-a598-ad9d-06e19a24175b.png)


# コピーツール | Auto Copy

https://chrome.google.com/webstore/detail/auto-copy/bijpdibkloghppkbmhcklkogpjaenfkg

:::note info
選択したテキストが自動でコピーされます。
Ctrl+Cでコピーをすることすら面倒という方に。
勝手にコピーされると困るという方はオプションからCtrlを押してる時だけ自動コピーするようにする、なども可能です。
:::
![Animation3.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264944/dc27d071-fd44-d66d-eceb-fed8a5e2d759.gif)

# スクリーンショットツール | FireShot

https://chrome.google.com/webstore/detail/take-webpage-screenshots/mcbpblocgmgfnpjjppndjkmgjaogfceg?hl=ja

:::note info
画面のスクリーンショットを撮影できます。
選択範囲、表示領域、ページ全体で撮影できます。
またファイルの形式もPDFと画像ファイル（PNG,JPG）を選べます。
:::
![Animation4.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264944/0fec703f-a234-c52f-e5de-68a8eb737290.gif)

# スクリーンショットツール | Awesome screen shot

https://chrome.google.com/webstore/detail/awesome-screen-reorder-sc/nlipoenfbbikpbjkfpfillcgkoblgpmj?utm_source=ext_sidebar&hl=ja

:::note info
画面のスクリーンショットを撮影できます。
選択範囲、表示領域、ページ全体で撮影できます。
URLでの共有も可能です。
:::
Go 言語での PRG (Post/Redirect/Get ) パターンを学ぶ

以下、9 割方 ChatGPT の回答です。

---
ウェブ開発におけるGo言語を使用した PRG パターンの詳細な説明をします。

PRG パターンは、フォームの送信を処理し、ユーザーがページをリフレッシュした際に重複したフォームの送信を防ぐために一般的に使用される設計パターンです。

以下にPRGパターンの詳細な説明を示します。

## Post（送信)
ユーザーがウェブページ上のフォームを送信すると、通常は POST リクエストを使用して、サーバーにフォームデータが送られます。Go では、このデータはリクエストオブジェクトを介してアクセスできます。サーバーはフォームデータを処理し、データの検証、データベース操作、ビジネスロジックなどの必要な処理を行い、レスポンスを準備します。

## Redirect(リダイレクト)
フォームデータを処理した後、サーバーはレスポンスを直接ユーザーに返すのではなく、リダイレクトレスポンスをクライアント (例: ユーザーのブラウザ) に送信します。通常、これは 302 ステータスコードと、クライアントをリダイレクトするURLを示す Location ヘッダーとともに行われます。このURLは、後続のGETリクエストを処理する別のルートまたはエンドポイントであることが一般的です。

## Get(取得)
リダイレクトレスポンスを受け取ったクライアントのブラウザは、指定された URL に対して GET リクエストを行います。このGETリクエストは通常、フォームの送信を処理したものとは異なるハンドラやルートによって処理されます。サーバーはこのGETリクエストに対するレスポンスを準備し、成功メッセージの表示、更新されたデータの表示、または別のページへのリダイレクトなどを行います。

## サンプル

```go
func formHandler(w http.ResponseWriter, r *http.Request) {
    if r.Method == "POST" {
        // Process the form data
        // ...

        // Redirect to a different URL
        http.Redirect(w, r, "/success", http.StatusSeeOther)
        return
    }

    // Render the form template
    // ...
}

func successHandler(w http.ResponseWriter, r *http.Request) {
    // Prepare the response for the GET request after redirection
    // ...
}

func main() {
    http.HandleFunc("/form", formHandler)
    http.HandleFunc("/success", successHandler)
    http.ListenAndServe(":8080", nil)
}
```

- `formHandler` function にて、"/form" URL リクエストを GET か POST かで処理を変えています。
- POST でリクエストされた場合、何かしらの処理 (例: DB への登録) をし、 "/success" URL へリダイレクトしています。
- `successHandler` 関数にて、リダイレクトされた際の表示をします。
    - `StatusSeeOther` の他にも `StatusMovedPermanently, StatusFound` などがあります。
    - 詳しくはこちらを公式ページを参照 (https://pkg.go.dev/net/http#Redirect)

## エラー: "superfluous response.WriteHeader call from main.formHandler" が出る場合

リダイレクトの前に `w` http.ResponseWriter に書き込みをすると、エラーが出てリダイレクトされなくなります。なので、`http.Redirect` の前に書き込みはしないよう注意しましょう。

## まとめ
PRG パターンは、ページをリフレッシュした際に重複したフォームの送信が発生する可能性がある問題を防ぐのに役立ちます。リダイレクトのステップがない場合、ページのリフレッシュは同じフォームデータを再送し、重複したフォームの送信を引き起こす可能性があります。
cscの作法 その346

# 概要
cscの作法、調べてみた。
練習問題、やってみた。

# 練習問題
closedxmlで数式を設定した、xlsxを作れ。

# 写真

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18104/9abed4c6-a1e2-0058-afd3-5d7896050f2c.png)

# サンプルコード

```

using System;
using System.IO;
using ClosedXML.Excel;

namespace App
{
	class Program {
		static void Main(string[] args) {
			var filePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "Book11.xlsx");
			var wb = new XLWorkbook();
			var ws = wb.Worksheets.Add("Sheet1");
			int i;
			int j;
			for (i = 1; i < 11; i++)
			{
				for (j = 1; j < 11; j++)
				{
					ws.Cell(i, j).Value = i * 10 + j - 10;
				}
			}
			ws.Cell(11, 1).FormulaA1 = "SUM(A1:A10)";
			ws.Cell(11, 2).FormulaA1 = "SUM(B1:B10)";
			ws.Cell(11, 3).FormulaA1 = "SUM(C1:C10)";
			ws.Cell(11, 4).FormulaA1 = "SUM(D1:D10)";
			ws.Cell(11, 5).FormulaA1 = "SUM(E1:E10)";
			ws.Cell(11, 6).FormulaA1 = "SUM(F1:F10)";
			ws.Cell(11, 7).FormulaA1 = "SUM(G1:G10)";
			ws.Cell(11, 8).FormulaA1 = "SUM(H1:H10)";
			ws.Cell(11, 9).FormulaA1 = "SUM(I1:I10)";
			ws.Cell(11, 10).FormulaA1 = "SUM(J1:J10)";
			wb.SaveAs(filePath);
			Console.WriteLine("ok");
		}
	}
}





```

以上。
気づいたら、React Testing Libraryのscreen.debug()が何も表示しなくなっていた。

## 背景

screen.debug()でレンダリングされるコンポーネントをチェックしようとしたら、何も表示されなくなっていました。

以下のような簡単なコンポーネントも表示されないので、`DEBUG_PRINT_LIMIT`は関係なさそうです。

```
describe('Test suit', () => {
    test('First test', () => {
        render(<div>Component</div>)
        screen.debug()
    })
})
```

<br/>

## 環境

- Reactフレームワーク
    Create React App(TypeScript)

- Jest + React Testing Library

<br/>

## 原因

`console.log`をsetUpTests.tsに移行したことが原因でした。以下を削除し、各テストファイルにモック宣言を戻すことで、screen.debug()が正常に動作することを確認できました。

setUpTests.ts
```
console.log = jest.fn()
```

## 終わりに

Globalレベルの変数や関数をモックする際は要注意です。テストターゲットのコンポーネントだけでなく、テストフレームワーク・ライブラリにも影響が出る可能性も考慮しなければなりません。