OmniAICreator/Qiita-1.07M huggingface dataset:
NixOS マシンに Yubikey でログインする (FIDO U2F)

## 背景

マシンにログインするのに毎回パスワードを打つのが面倒。  
簡単なパスワードにするのも嫌。

手持ちの Yubikey を使ってログインできたら便利そうだと思い調べてみたところ、案外簡単そうだったので設定してみた。

（2023/09/06 追記） `security.pam.u2f.cue = true` とすることで sudo 時にプロンプトが出ることがわかったので追加した。毎回何も表示されない画面を見て数秒佇んでいたので助かった。

## 構成

- NixOS マシン
- Yubikey 5 NFC
- 参考：[NixOS Wiki](https://nixos.wiki/wiki/Yubikey)

OTP を使う方法と FIDO U2F を使う方法があるらしい。

OTP の方法は Yubikey 向けのパッケージを使うもので、 FIDO U2F の方法は FIDO U2F に対応する認証方法の一つとして Yubikey を使う形。  
より一般的な方がいい気がするので U2F の方で設定を行う。

## 手順

Wiki に書いてあるとおりに進める。

### 1. マッピングファイルの生成

Yubikey を挿入して以下コマンドを実行する。

```
nix-shell -p pam_u2f
mkdir -p ~/.config/Yubico
pamu2fcfg > ~/.config/Yubico/u2f_keys
```

3つ目のコマンドを実行すると PIN を聞かれるので入力する。  
筆者の場合はすでに設定済みだったが、もしかすると [Yubikey Manager](https://www.yubico.com/support/download/yubikey-manager/) を使って設定しないといけないかもしれない。

PIN を入力したあと、Yubikey のボタンをタッチするのを忘れないように。

### 2. システム設定の編集

`vim /etc/nixos/configuration.nix`

以下をどこかに追記。

```/etc/nixos/configuration.nix
security.pam.services = {
  login.u2fAuth = true;
  sudo.u2fAuth = true;
};
security.pam.u2f.cue = true;
```

```sh
sudo nixos-rebuild switch
```

### 3. 動作確認

```
nix-shell -p pamtester
pamtester login <username> authenticate
pamtester sudo <username> authenticate
```

Yubikey をタッチして `pamtester: successfully authenticated` と表示されれば成功。

一応これで動作確認できているが、 Super+L でロックしたあとのログインや、 sudo が成功することも確認した。

### 4. (追記) Yubikey を抜いた時に自動でログアウトする

(2023/08/17 追記)  
このままでは、 Yubikey を差しっぱのままログアウトする懸念がある。  
注意する、だけではうまくいくとは思えないので、仕組みでどうにかしたい。

そこで、 Yubikey を抜くことをトリガーとしてログアウト（画面ロック）することにした。  
画面ロックの習慣はあるので、キーボードショートカットの代わりに Yubikey を抜く習慣をつければうまくいくはず。

[YubiKeyをLinuxの「鍵」にする](https://keens.github.io/blog/2021/03/28/yubikeywolinuxno_kagi_nisuru/) のページを参考にさせていただき、 udev を使って実現できそうなことがわかった。

udev を使って USB デバイスの抜き差しイベントをフックし、デバイスが対象の Yubikey かつイベントが抜きだった場合に画面ロックをするような設定をする。

- 1, Yubikey の ID を調べる
    - `udevadm monitor --environment --udev | grep -E 'ID_VENDOR_ID|ID_MODEL_ID` の結果で出てきた ID を控える
    - 筆者 (Yubikey 5 NFC) の場合は `ID_VENDOR_ID=1050`, `ID_MODEL_ID=0407` だった
- 2, `configuration.nix` に udev のルールを追加する

```/etc/nixos/configuration.nix
services.udev.extraRules = ''
  ACTION=="remove", ENV{ID_VENDOR_ID}=="1050", ENV{ID_MODEL_ID}=="0407", RUN+="${pkgs.systemd}/bin/loginctl lock-sessions"
'';
```

- 3[^code-block], `nixos-rebuild switch`
- 4, 動作確認
    - Yubikey を抜くと画面がロックされることを確認する

[^code-block]: コードブロックを挟むと順序付き箇条書きが  1. からにリセットされるのはどうにかできないものか……。

## まとめ

詰まることもなくとても簡単にできて驚いた。

ただ、 PIN なしでログインできるのは不安なので今後どうにかしたい。  
Mac で設定した際には U2F ではなく PIV? という機能を使ったが、 Smart Card mode というのがそれに当たるのかもしれない。  
Wiki に詳しい情報がないので一旦このまま使い、なにかわかったら別の記事を作ろうと思う。

(2023/08/17 追記)  
NixOS 以外の Linux 情報を探しても、 PIN によるログインは見当たらなかった。  
ログアウトのために Yubikey を抜くような設定を追加したので、習慣がつくように頑張る。
IN述語とEXISTS述語を有効活用してSQLマスターになろう

# 述語
述語とは戻り値が真理値になる関数
IN述語とEXISTS述語はどちらも述語である

# IN述語
ORの便利な省略形である

## 構文
```sql
<column_name> IN <vector>
```

## 例
**pop_sex**
| pref_name | sex | population |
| ---- | ---- | ---- |
| 東京 | 1 | 250 |
| 東京 | 2 | 250 |
| 神奈川 | 1 | 200 |
| 神奈川 | 2 | 100 |
| 福岡 | 1 | 150 |
| 福岡 | 2 | 100 |
| 埼玉 | 1 | 100 |
| 埼玉 | 2 | 150 |
| 長崎 | 1 | 20 |
| 長崎 | 2 | 30 |
| 千葉 | 1 | 120 |
| 千葉 | 2 | 130 |
| 山形 | 1 | 50 |
| 山形 | 2 | 50 |

#### 東京と長崎と千葉のレコードだけ見たい場合

### SELECT
```sql
SELECT
    pref_name, 
    pop
FROM
    pop_sex
WHERE
    pref_name
IN
    ('東京', '長崎', '千葉')
;
```

**pop_sex**
| pref_name | sex | population |
| ---- | ---- | ---- |
| 東京 | 1 | 250 |
| 東京 | 2 | 250 |
| 長崎 | 1 | 20 |
| 長崎 | 2 | 30 |
| 千葉 | 1 | 120 |
| 千葉 | 2 | 130 |

## 引数にサブクエリ(ビューやテーブル)を指定することも可能
サブクエリについては[こちら](https://qiita.com/kekkepy/items/ab04bd8b118a16e3e7a7)
```sql
<column_name> IN <sub_query>
```


# EXISTS述語
条件に合致するレコードの存在有無を調べる述語
引数は常に相関サブクエリを指定する

## 構文
```sql
EXISTS <相関サブクエリ>
```
INと大きく異なるのは、INはカラム名などとサブクエリを引数としていたが、EXISTSは相関サブクエリ１つだけを引数とする

## 例

#### 関東の人口が200以上のレコードだけ見たい場合

**kanto_pref**
| pref_name | pref_cod |
| ---- | ---- |
| 東京 | 1 |
| 神奈川 | 2 |
| 埼玉 | 3 |
| 千葉 | 4 |

### SELECT
```sql
SELECT
    pref_name, 
    pop
FROM
    pop_sex AS P
WHERE
EXISTS
    (
    SELECT
        *
    FROM
        kanto_pref AS K
    WHERE
        P.pref_name = K.pref_name
    AND
        P.population >= 200
    )
;
```

## EXISTS述語の注意点
#### 相関サブクエリのSELECT *
EXISTSはレコードの存在有無のみを判定条件としているため、列は一切関係ない。
そのため *(アスタリスク)を指定するのが慣習。

# INとEXISTSの違い
#### サブクエリのreturn
INは条件に当てはまるデータを全て抽出し、対象となるレコードを全て返す。
EXISTSは存在するかどうかだけを判定するので、一つでも対象レコードが見つかれば処理を終了する。

#### 評価順序
1. IN句
1. 主問い合わせのWHERE句
1. EXISTS

できるだけ早い段階でデータ量を絞り込めるかどうかを考える。
Tornado (Python) のセットアップとデーモン化

## 基本的な流れ

Tornadoでの開発開始までのセットアップは通常以下のような流れになります。

1. 仮想環境の構築（必要な場合）
1. パッケージのインストール
1. アプリケーションファイルの設置
1. サーバー起動スクリプトの作成
1. サーバーのデーモン化

パッケージの導入は以下のようなコマンド操作の流れを実行します。（下の例では仮想環境を `.venv` に導入）

###### 仮想環境の導入と有効化

```
$ python -m venv .venv
$ source .venv/bin/activate
```

###### 仮想環境でのTornadoの導入

```
(.venv)$ pip install tornado
```

これでTornadoの導入は終了です。ちなみに、仮想環境から抜ける場合は、コマンドラインで `deactivate` を実行します。

## DeracioTSS

[DeracioTSS](https://github.com/DERaC-IO/DeracioTSS)はTornadoでのアプリケーション開発のセットアップの中で、アプリケーションファイルの設置からサーバーの起動用シェルスクリプトの作成までを対話式CLIで自動化し、サーバーのデーモン化に必要な `.service` ファイルの作成までを行います。

https://github.com/DERaC-IO/DeracioTSS

#### インストール

スクリプトファイル `tss.py` をGitHubからダウンロードします。

```
wget https://raw.githubusercontent.com/DERaC-IO/DeracioTSS/f6691d177de800434593cb336f102c1181c73439/tss.py
```

次に、スクリプトの実行には `subprocess` と `questionary` が必要になるので、それらを仮想環境に導入します。

```
(.venv)$ pip install subprocess questionary
```

必要なライブラリの導入が完了したあと `tss.py` を実行します。

```
(.venv)$ python tss.py
```

正常に起動すれば、以下のようなバナーが現れるはずです。

```
    _____                                    ______     __       __  
    /    )                           ,         /      /    )   /    )
---/----/----__---)__----__----__--------__---/-------\--------\-----
  /    /   /___) /   ) /   ) /   ' /   /   ) /         \        \    
_/____/___(___ _/_____(___(_(___ _/___(___/_/______(____/___(____/___                                                                 

Tornado Startup Script v1.0.0
Copyright (c) 2023 DERaC, LLC.
MIT License

? Starting a new project?
```

#### 対話式プロセス

CLIアプリケーションは、アプリケーションを実行したディレクトリ `<PROJECR_ROOT>` の中に、対話式で以下のファイルとフォルダを生成します。（ファイル・フォルダ名はデフォルト）

```
<PROJECR_ROOT>
    │
    ├─ app.py            (Tornadoアプリケーション)
    │
    ├─ server.sh        （サーバー起動シェルスクリプト）
    │
    ├─ tornadod.service （システムデーモンファイル）
    │
    ├─ templates        （テンプレートフォルダ）
    │    │
    │    └─ index.html  （トップページ）
    │ 
    └─ static           （静的ファイルフォルダ）
```

このプロセスの中で、以下の質問があるので、順番に答えていきます。（パスなどは自動的に推測されて表示されます）

1. プロジェクト名
1. テンプレート名
1. 静的ファイルフォルダ名
1. 起動するポート番号
1. アプリケーションファイル名
1. `python3` へのパス
1. 使用するシェルの種類
1. シェルへのパス
1. サーバー起動シェルスクリプトファイル名
1. サーバー起動シェルスクリプトのパーミッション
1. システムデーモンファイル名
1. デーモン化するシステムの説明（名称）
1. サーバー起動シェルスクリプトがあるディレクトリへのパス
1. サーバー起動シェルスクリプトへのパス
1. デーモンを起動するユーザー名
1. デーモンを起動するグループ名
1. システムデーモンファイルのパーミッション

#### デーモン化

対話式でのプロセスが完了したら、システムデーモンファイルをシステムデーモンが認識できるフォルダ（この例では `/etc/systemd/system/` ）に移動させて起動します。（ `daemon-reload` を忘れずに）

```
$ sudo mv tornadod.service /etc/systemd/system/
$ sudo systemctl daemon-reload
$ sudo systemctl start tornadod
```

正常に起動したら、以下のコマンドで自動的に起動できるようにします。（ `--now` オプションは自動起動の設定と同時にシステムを起動させる場合に付与）

```
$ sudo systemctl enable tornadod --now
```

起動が成功したら、`localhost:8000` などにアクセスすれば、下のようなページが表示されるはずです。（プロキシの設定などは別トピックなのでここでは省きます）

![snapshot.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3113613/cedb7186-e05c-9428-4e25-2a8fbebc3b34.png)

これでTornadoアプリケーションの開発準備が整いました。 `tss.py` が導入済みの環境ではTornado開発初心者の開発者でも10分以内で起動できると思います！
プログラミングスクールに通った感想

プログラミングスクールに通った感想を書きます。

まずスクールに通おうと思った経緯
数年前からエンジニアに興味を持ち転職しようと独学にて勉強を始めたものの、周りにIT関係の知り合いがおらず躓く事が多く独学での限界を感じた
転職に対して自信が無かった

スクールを選んだ理由
・ネットでの評価が高い
・学びたい言語を専攻していた
・学習後の就職活動サポート
・職業訓練受講給付金制度が使えた事

簡単にスクールの情報を
期間　3ヶ月
金額　約40万円
内容
WEBアプリ作成　基本の言語学習から本番環境まで


言語については独学で学習していたので学ぶことは少なっかたですが、
・AWS、フレームワーク、デプロイなどを使ったWEBアプリ制作
ネットで調べていると出てくる言葉ではあり何となくは知っていたのですが、どう使うのか、登録は、費用は、環境は、分からない事が多く使うのに戸惑っていたのですが手順が書いてあると安心して使えます。
学習を終えてからは少し理解できるようになったことと、一回使った事により敷居が下がったので挑戦しやすくなりました。
・現役プログラマーとのメンタリング
回数が決まっており、何回でもという訳にはいきませんが学習中に疑問に思った事を聞き、リアルタイムで問題を解決できることで作業が捗りますし、エラーが出た時の解決方法や調べ方、現役エンジニアの話を聞けた事も良かったです。
回答を聞いても用語など分からないことはありますがその用語を調べる事も勉強になりました。

独学中は躓いた時に自身での問題解決が難しいこと、初めての言語、フレームワーク、開発環境の構築などの勉強方法が分からない事があったので同じように悩んでいる方にはおすすめかも知れません。

値段はそこそこするので何とも言えませんが、個人的には満足しています。


IT業界エンジニア転職の目標は達成してませんが、ひとまず目標へ向けて一歩前進したんじゃないかと思います。
PROXYを使用した環境でのGetStringAsync方法

<B>
<font COLOR="blue" >
・~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~・<BR>
・プロクシ環境下でのアクセス方法です。id,pw等は変えて下さい。<BR>
・これによりwebサイトのデータを利用する事が出来ます。<br>
・例えば、ニュースのトピックスを取得して表示する等。<br>
・~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~・<BR>
</B>

```c#
        private async  void button3_Click(object sender, EventArgs e) {
            
            textBox1.Text="";

            HttpClientHandler ch = new HttpClientHandler();
            ch.Proxy = new WebProxy("http://xxxxxxxxxxxx:8080"); // PROXY
            ch.Proxy.Credentials = new NetworkCredential("id", "passwd");
            ch.UseProxy = true;

            HttpClient client = new HttpClient(ch);
       
            await Task.Run(() =>{
                try{
                    var task = client.GetStringAsync("https://www.yahoo.co.jp/");
                    task.Wait();

                    this.Invoke((Action)(() => {
                        textBox1.Text = task.Result;
                    }));
                }
                catch (Exception ex){
                    this.Invoke((Action)(() => {
                        textBox1.Text = ex.GetBaseException().ToString();
                    }));
                }
            }); 
        }
```
私にとってのChatGPT

# はじめに

こんにちは。takunと申します。
この記事では、初心者エンジニア目線でChatGPTの活用方法や意義について語ってみます。


投稿者は初心者エンジニアです。間違ったことや不適切な内容となってしまう可能性もあることをご承知ください。<font color='brown'>鼻くそほじりながら見ていただけたら幸いです。</font>
また、助言など頂けると嬉しいです。

※ 前提としてChatGPT3.5での内容で進めていきます。


# 目次
① ChatGPTへの偏見とその解消
② 情報収集ツールとして
③ デバッグツールとして
④ 面接対策として
⑤ AIだって間違えるときはある。仲良く付き合っていこう。

# ① ChatGPTへの偏見とその解消
ChatGPTみなさん使ってますか。エンジニアのみなさんであれば、多くの方が一度は使ってるのだろうなと思います。

私は4月に初めてAIチャットくんというLINEチャットボットを利用しました。
試しにGoogle検索の代わりにプロンプトを入力してみると、

> あれ、思ったより使いやすいしわかりやすいぞ。
しかも答え方が人間らしさがあって抵抗感も大分少ないし。
でも、エンジニアの勉強を進める上では使い過ぎたら自分のためにならないのかなあ。

そんなことを思って、実践的な運用はせずLINE上でのみ検索エンジンとして利用していました。
とある動画を見るまでは…

https://youtu.be/S1RnA7gdf_M

やるかやられるかチャンネルさんの動画ですね。具体的なhow-toを挙げていてわかりやすいです！オススメです。
> 誤字脱字のチェック・修正はまだわかる。
AIとのロールプレイって…まじ？
アイデア出しも助けてくれるの？

世間ではChatGPTを相棒などと言う人もいますが、それは冗談じゃないのかもしれないと思いました。
真面目に学習用途として、サポート役として活用してみることにしました。

# ②情報収集ツールとして
シンプルに検索エンジンとしても使えますが、Googleのように一つ一つのページを開かず、<font color='red'>必要な時に必要十分な情報のみを入手できる</font>のが大きなメリットではないかと感じます。
~~~
〇〇の意味を教えてください。
//下記のような条件を加えてみる
//易しく，3つ挙げて，箇条書き，活用方法も併せて
~~~
私はよく上記のような使い方をしています。


# ③ デバッグツールとして
前回記事のHerokuデプロイ作業にて、エラー解決に大きく貢献してくれました。


ただし、ChatGPTはあらゆる環境やエラー前の処理についての情報がないと不適切な回答をしてしまうこともわかりました。(不必要なプラグインを入れてしまい、余計なエラーもたくさん出しました。)

ChatGPTに解決法だけを求めても、前に進まない。自分のためにもならない。
大事なのは解決法をコピペすることではなく、<font color='red'>エラーが何を意味しているのかを理解すること</font>だと実感できました。

以下の点に注意しプロンプトを入力していきました。
- 上手く行かなかった処理の箇所と前後をコピペ。
問題点の洗い出しと、エラーの意味を説明してもらう。
- 原因と解決策を挙げてもらったら一通り見て、わからない単語は別スレッドで聞く。
- 適宜、挙げてもらった内容から逆質問してみる。(例:それは〇〇だと思うのですが、△△してみたら解消しますか)

プロンプトの例としては以下のような内容です。
~~~
ーーーエラー文をコピペ(前後処理を含めて)ーーー
上記の問題点を説明してください。
~~~

~~~
ーーーソースコードをコピペーーー
上記はpom.xmlの記述です。不足している点を挙げてください。
~~~

# ④ 面接対策として
エンジニアである以上は転職活動で。あるいはSES企業に勤めている方であれば客先面談で。どちらも必要となるのは面接対策ですよね。
もちろんエージェントや営業と対策はするのでしょうが、ChatGPTを活用して内容を詰めていくこともできると思いました。

私が現職へ転職活動をしていたときの面接対策のメモ帳は、項目別(自己PR,長所と短所,エンジニアを選んだ理由,前職を選んだ理由,転職理由,前職で頑張ったこと,志望理由)で箇条書きにしたものでした。これをプロンプトに入れます。
~~~
〇〇会社△△エンジニア選考のための面接対策を以下の通りとしました。
評価と改善案を挙げてください。
ーーー上記メモをコピペーーー
~~~

> 修正点だけでなく、良いところは褒めてくれるから嬉しい！（笑）

前述の参考動画と同様、ロールプレイで実際の面接のイメージを膨らませるように活用もできました。(私はやり取りのできる形式ができなかったです。有識者の方教えていただけたら嬉しいですorz)

~~~
上記の面接対策を活用し、以下の面接ロールプレイをしてください。
Aさんは私です。
Bさんは採用担当面接官です。
~~~

# ⑤ AIだって間違えるときはある。仲良く付き合っていこう。
### 調べ学習における懸念点に対し思ったこと。
ちゃんと条件設定しても、誤った情報を出力するときもあります。
なのでGoogleやYoutubeなどで情報を集めつつ、信憑性に足る情報なのか確認していくことも時には必要と考えています。
> じゃあ最初から使わない方がいいんじゃ？

確かに初見の内容を学習する際は、時間がかかるかもしれません。

一方で、以前調べて覚えたはずの内容だったらどうでしょうか。
例えば、
> 学生の時使ってた三平方の定理ってなんだっけな。
(調べると...)そうそう！そういう内容だったな！

１度(理解して)覚えた知識は思い出した時に<font color='blue'>その情報が正しいのか</font>わかるはずです。
大袈裟なイメージを話すと、PCでいうメモリが自分の記憶領域で、ChatGPTがHDD(一度覚えたけど忘れた記憶)からデータをとってきてくれるような感じ。


余談ですが、某ホリ○モンはChatGPTが「大脳の機能拡張」と称しており、すごく納得感もありました。


またエビングハウスの忘却曲線を参考に、人は繰り返し繰り返しの復習で長期記憶に結び付きます。そのサイクルを無理なく必要十分な情報のみを得て加速させる。そんな使い方もできるのではないでしょうか。

### 更なる学習効率向上の可能性

・自分の理解度を測る→「○○の定義は〜、使い方は〜です。認識は間違っていませんか。評価してください。
・学びを深める(応用)→「〇〇は△△として活用できますか。他に組み合わせて使う方法はありますか。」

上記のように学習のインプットだけに留まらず、アウトプットする力を養う能力も磨くことができるのではないかと考えています。
(ここまできたらGoogle先生ならぬChatGPT大先生ですよね)

# 終わりに
正直いうとチャットAI歴が浅く、深くもない知見かもしれません。それでも、最初の私のように不必要と割り切ってしまう初心者エンジニアに対して、少しでもバイアスをフラットに。欲を言えば共感していただける記事となっていたら幸いです。

他にも活用方法のおすすめや記事、アドバイスなどあればコメント頂けると大喜びです。
ここまで見てくださってありがとうございました。
Karteでのカスタムイベント送信方法

# はじめに
今回はイベント収集サービスであるKarteでのカスタムイベント送信方法を紹介していきます
### 本文
`eventType`には送信したいイベントの種別を、`key`にはパラメータ名を渡します
```kotlin
fun sendKarteCustomEvent(
        eventType: String,
        params: Map<String, Any>? = null,
    ) {
        Tracker.track(eventType, params?.mapKeys { it.key })
    }
```
### 最後に
最近Karteを使い始めたので備忘録用に記事にしました
どなたかのお役に立てれば幸いです
KubernetesのresourceVersionで楽観ロック

Kubernetesのリソースは、その状態と一緒に`resourceVersion`というフィールドを持っています。このフィールドは、リソースが最後に変更されたときのバージョンを示す文字列です。そして、これを利用することで楽観ロックのような動作を実現することができます。

## Custom Resourceの作成

まず、例としてCustom Resource Definition (CRD)を作成します。以下はそのためのYAMLです：

```yaml:myobject.yaml
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: myobjects.example.com
spec:
  group: example.com
  names:
    kind: MyObject
    plural: myobjects
    singular: myobject
    listKind: MyObjectList
  scope: Namespaced
  versions:
    - name: v1
      served: true
      storage: true
      schema:
        openAPIV3Schema:
          type: object
          properties:
            spec:
              type: object
              properties:
                value:
                  type: string
```

CRDをクラスタに適用します：

```sh
kubectl apply -f myobject.yaml
```

## MyObjectリソースの作成

次に、新しく作成したCRDに基づくMyObjectリソースを作成します：

```yaml:hello-world.yaml
apiVersion: example.com/v1
kind: MyObject
metadata:
  name: hello-world
spec:
  value: hello world
```

リソースをクラスタに適用します：

```sh
kubectl apply -f hello-world.yaml
```

## resourceVersionの取得

上で作成したリソースの`resourceVersion`を取得するために、以下のコマンドを実行します：

```sh
kubectl get -f hello-world.yaml --output jsonpath='{.metadata.resourceVersion}'
```

結果：

```
92029503
```

この`resourceVersion`は、リソースを変更する際の楽観ロックに利用できます。

## 楽観ロックの実験

`resourceVersion`をリソースに明示的に指定することで、楽観ロックができます：

```yaml:hello-world.yaml
apiVersion: example.com/v1
kind: MyObject
metadata:
  name: hello-world
  resourceVersion: "92029503" # 追加
spec:
  value: goodbye world # 変更
```

1回目の適用：

```sh
kubectl apply -f hello-world.yaml
```

上の適用は`resourceVersion=92029503`に対する変更なので、成功します：

> myobject.example.com/hello-world configured


この適用で`resourceVersion`が増えます。

そのため、同じ`resourceVersion`を使用して再度変更を適用しようとすると、エラーが発生します：

```sh
kubectl apply -f hello-world.yaml
```

結果：

> Error from server (Conflict): error when applying patch:
> {"metadata":{"resourceVersion":"92029503"}}
> to:
> Resource: "example.com/v1, Resource=myobjects", GroupVersionKind: "example.com/v1, Kind=MyObject"
> Name: "hello-world", Namespace: "default"
> for: "hello-world.yaml": error when patching "hello-world.yaml": Operation cannot be fulfilled on myobjects.example.com "hello-world": the object has been modified; please apply your changes to the latest version and try again

このエラーは、指定された`resourceVersion`での変更が許可されないことを示しています。つまり、他のユーザーやプロセスがリソースを変更して新しい`resourceVersion`が割り当てられた後での変更は拒否されます。

## まとめ

Kubernetesの`resourceVersion`を利用することで、リソースの変更の競合を検知し、楽観ロックのような動作を実現することができます。これは、複数のユーザーやプロセスが同時にリソースを変更しようとする際の競合を避けるために非常に役立ちます。
「Reactの方法」に沿ったプログラミングとは

今や、ReactはJavaScriptの中心的な地位を占めるようになったと感じるときがしばしばあります。TypeScriptのように、さらに新しいプログラミング技法は生まれてきていますが、基本の考え方はReactです。
それに対して従来のJavaScriptはプログラミングそのものの考え方が違い、別の言語のように感じます。そのためReactを使いながら従前のJavaScriptのようなプログラミングをすると、「それは「Reactの方法」（"The React Way"）に沿ったものではありません。」と言われてしまいます。
そもそも、"The React Way"とはなんでしょうか。最近、その意味が少しわかった気がしますので、このブログにまとめておきたいと思います。
なお、従前のJavaScriptのコードはjQueryを導入していることを前提とさせていただきます。

## DOM要素は基本となる情報をもとにレンダリングされる
次の図をご覧ください。



![image](https://itmaroon.net/home/wp-content/uploads/2023/08/3.png)


![image](https://itmaroon.net/home/wp-content/uploads/2023/08/4.png)

従前のJavaScriptは、DOM要素があって、それを抽出してその要素が持っているテキストや属性の情報を書き換えて再レンダリングしていました。

これに対してReactでは、まずDOM要素をレンダリングするための基礎情報というのが存在します。その情報を元にJSXという記法でDOM要素を生成し、レンダリングします。更新するのはDOM要素ではなく、基礎情報の方なのです。JSXというのはHTMLにJavaScriptの変数や条件文を記述できる記法で、HTMLに変数が埋め込まれたような記述になります。

従前のJavaScriptの記法を”imperative”、日本語でいうと「命令的」といい、Reactを"declarative"、日本語でいうと「宣言的」と言ったりもするようです。

## 実際のコードを比較

実際のプログラミングの場面では”imperative”と"declarative"では、レンダリング結果は同じでも、コードの違いは大きいです。
ここで誤解がないように説明しますが、Reactでも”imperative”な記法は可能だということです。”imperative”と"declarative"はあくまで記法の問題で、言語の仕様を縛っているものではありません。
そのため、今回はより違いが明確になるように、Reactで”imperative”なアプローチと"declarative"なアプローチのコードを示します。
### ”imperative”なアプローチ
まず、”imperative”なコードを示します。
```
export function NomalSelect() {
	//オプションの一覧を開く
  const openClick = (event) => {
    const element = event.currentTarget;//⓶
    if (element.classList.contains('open')) {//⓷
      element.classList.remove('open');
    } else {
      element.classList.add('open');
    }
  }
	return (
		<div onClick={openClick}>//⓵
			クリック
		</div>    
	);
}

```
①はJSXでDOM要素にイベントリスナーをセットしています。
そしてイベントが発生すると`openClick`ハンドラが発火します。
②でeventオブジェクトからクリックされたオブジェクトを取得します。
③以下でJavaScriptのaddメソッド、removeメソッドでクラスをつけたり外したりしています。

このプログラミングは、DOM要素を取得し、それを直接操作して再レンダリングを起こすというまさに”imperative”なアプローチです。

### "declarative"なアプローチ
次に、”declarative”なコードを示します。
```
export function NomalSelect() {
	// open状態を管理するstate
  const [isOpen, setIsOpen] = useState(false);//⓷
	//オプションの一覧を開く
  const openClick = () => {//⓸
    // isOpenの値をトグルする
    setIsOpen(!isOpen);
  }
	return (
		<div 
			onClick={openClick}//⓵
			className={`${isOpen ? 'open' : ''}`}//⓶
		>
			クリック
		</div>    
	);
}

```
①はJSXでDOM要素にイベントリスナーをセットするのはおなじです。
②の部分でJSXの記法を使って３項演算子による条件式でクラス名をセットしています。つまり、状態変数isOpenがtrueならクラス名`open`をつけてfalseなら空のクラスにするという処理です。
③は、その判断のための状態変数`isOpen`をuseStateでセットしています。
④以下の処理は状態変数`isOpen`を更新しています。ここで重要なのはDOM要素を操作しているのではないということです。

## まとめ
”imperative”なアプローチと"declarative"なアプローチの違いを、実感していただけたでしょうか？
一番大きな違いは状態変数`isOpen`があるかないかです。これが最初に説明した「基礎情報」です。今回はuseStateで管理する状態変数でしたが、Gutenbergのブロックの場合はブロックの属性（attributes）になることもあります。
従前のJavaScriptでは、「基礎情報」なしにDOMをレンダリングしてきたので、これに慣れ親しんだ方は"declarative"なアプローチには違和感を感じるのではないかと思います。私もその一人です。
しかし、"declarative"なアプローチは大きなメリットをもたらします。「基礎情報」の変化によって、再レンダリングが必要なDOM要素が複数あった場合を想像してください。
従来のJavaScriptでは再レンダリングしなければならないDOM要素を全て抽出し、適切なメソッドを選んで属性等を更新します。
ReactならJSXの定義で一元的に管理できるのが最大の利点です。それによって「基礎情報」を更新するだけで全てが再レンダリングされるのです。規模の大きなWebサイトでは保守性が格段の差が出てくると思います。
しかし、何でも"declarative"なアプローチがよいというわけではありません。。特定のユースケース、特にサードパーティのライブラリやフレームワークとの統合においては、直接のDOM操作が必要となる場合もあります。
そのため、Reactにおいても”imperative”なアプローチの方法は覚えておく必要はあると思います。
【Flutter】showDialogを使ってを全画面で表示させる方法

showDialogを使って全画面にDialogを表示させたい。
そもそも、showDialog自体、私たWidgetをダイアログとして表示してくれちゃうので、
ContainerのheightとwidthにMediaQueryを渡せばできると思い、やってみました。

```dart
 @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('画像表示'),
        backgroundColor: Colors.red,
      ),
      body: Center(
        child: ElevatedButton(
          child: const Text('モーダル表示'),
          onPressed: () {
            showDialog(
                context: context,
                builder: (buolder) => Container(
                      height: MediaQuery.of(context).size.height, // 端末の高さを指定
                      width: MediaQuery.of(context).size.width, // 端末の幅を指定
                      decoration: const BoxDecoration(color: Colors.blue),
                      child: const Center(
                        child: Text(
                          '全画面表示',
                          style: TextStyle(
                            color: Colors.white, // デフォルトのTextStyleが強烈なため回避してます
                            decoration: TextDecoration.none, // デフォルトのTextStyleが強烈なため回避してます
                            fontSize: 40.0,
                            fontWeight: FontWeight.w400,
                          ),
                        ),
                      ),
                    ));
          },
        ),
      ),
    );
  }
```

表示結果がこれ。

<img width="300" alt="IMG_8779.jpeg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524065/d499f53e-4407-fa96-b32a-a6b174536610.jpeg">


あれ。なんか上下が足りない。

Dialog表示には限界値があるのかなと、メソッドを除いてみた結果

<img width="400" alt="スクリーンショット 2023-08-16 18.56.22.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524065/68cdb2bb-ef44-2584-29e3-87e3d953cd53.png">

`useSafeArea`なるものが目に入りました。

なので、showDialogのuseSafeAreaのパラメータをfalseに設定して実行してみた結果

```dart
 @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('画像表示'),
        backgroundColor: Colors.red,
      ),
      body: Center(
        child: ElevatedButton(
          child: const Text('モーダル表示'),
          onPressed: () {
            showDialog(
                context: context,
                useSafeArea: false, // ここを追加！！
                builder: (buolder) => Container(
                      height: MediaQuery.of(context).size.height,
                      width: MediaQuery.of(context).size.width,
                      decoration: const BoxDecoration(color: Colors.blue),
                      child: const Center(
                        child: Text(
                          '全画面表示',
                          style: TextStyle(
                            color: Colors.white,
                            decoration: TextDecoration.none, 
                            fontSize: 40.0,
                            fontWeight: FontWeight.w400,
                          ),
                        ),
                      ),
                    ));
          },
        ),
      ),
    );
  }
```

できた！！
SafeAreaが効いていたみたいです。
これを明示的にfalseに書き換えてあげることで、問題を解決しました。

めっちゃ簡単でした(苦笑)
SAMSUNG SSD 970 EVO Plus


ここの [Data Migration](https://semiconductor.samsung.com/jp/consumer-storage/support/tools/#:~:text=Magician%E3%82%BD%E3%83%95%E3%83%88%E3%82%A6%E3%82%A7%E3%82%A2%20(5)-,Data%20Migration,-%E5%80%8B%E4%BA%BA%E5%90%91%E3%81%91%E3%82%B5%E3%83%A0%E3%82%B9%E3%83%B3SSD) からインストーラをダウンロード

使い方については [このPDF](https://semiconductor.samsung.com/resources/user-manual/Samsung_SSD_Data_Migration_User_Manual_Japanese_revision_v10.pdf) に書いてあるけど、説明とか見なくてもそのままクローン開始すればOK。
memo polars

速さ：cudf > polars > pandas

GPUメモリでは足りない場合：polars
GPUメモリで足りる場合：cudf
その他 EDA用？:pandas
参考：https://zakopilo.hatenablog.jp/entry/2023/02/04/220552

https://qiita.com/_jinta/items/fac13f09e8e8a5769b79

https://qiita.com/hkzm/items/8427829f6aa7853e6ad8

https://qiita.com/kunishou/items/1386d14a136f585e504e
memo Open3d

点群データの書き出し

```python
o3d.io.write_point_cloud("ファイル名", 点群データ)
```

点群データの切り出し

```python
def pcd_crop(pcd, p1, p2, axis_range, base_axis):
    vol = o3d.visualization.read_selection_polygon_volume("cropped_emp.json")
    d3 = ["X", "Y", "Z"]
    axis_ind = d3.index(base_axis)
    points = [[p1[0], p2[0]], [p1[0], p2[1]], [p1[1], p2[1]], [p1[1], p2[0]]]
    for point in points:
        point.insert(axis_ind, 0)
        vol.bounding_polygon.append(np.array(point))
    vol.axis_min = axis_range[0]
    vol.axis_max = axis_range[1]
    vol.orthogonal_axis = base_axis

    return vol.crop_point_cloud(pcd)
```

点群データの加工

```python
def process(pcd, rx, ry, rz, dx, dy, dz):
    translation = np.array([[1, 0, 0, dx],
                            [0, 1, 0, dy],
                            [0, 0, 1, dz],
                            [0, 0, 0, 1]])
    x_rot = np.array([[1, 0, 0, 0],
                      [0, math.cos(rx), -(math.sin(rx)), 0],
                      [0, math.sin(rx), math.cos(rx), 0],
                      [0, 0, 0, 1]])
    y_rot = np.array([[math.cos(ry), 0, math.sin(ry), 0],
                      [0, 1, 0, 0],
                      [-(math.sin(ry)), 0, math.cos(ry), 0],
                      [0, 0, 0, 1]])
    z_rot = np.array([[math.cos(rz), -(math.sin(rz)), 0, 0],
                      [math.sin(rz), math.cos(rz), 0, 0],
                      [0, 0, 1, 0],
                      [0, 0, 0, 1]])
    pcd.transform(x_rot)
    pcd.transform(y_rot)
    pcd.transform(z_rot)
    pcd.transform(translation)
    return pcd
```

点群データの読み込み

```python
pcd = o3d.io.read_point_cloud("data.pcd")
```

点群データに色をつける

```python
pcd.paint_uniform_color([1, 0.706, 0])
```

座標軸

```python
mesh_frame = o3d.geometry.TriangleMesh.create_coordinate_frame(size=0.03, origin=[0, 0, 0])
```

OBJからPCDファイルへの変換

```python
import os
import shutil

obj_path = "./pcd.obj"  # 適切なPATHを設定
basename = os.path.basename(obj_path)
dir_name = os.path.splitext(basename)[0]
print(basename)
print(dir_name)
n_points = []
for i in range(1, 11):
    n_points.append(i * 1000)

os.chdir(obj_path.replace(basename, ""))
os.mkdir(dir_name)
shutil.move(basename, dir_name)
os.chdir(dir_name)
print(os.getcwd())
os.system("ls")

for n_point in n_points:
    cmd = "pcl_mesh_sampling %s %s -n_samples %s -leaf_size 0.0001 -no_vis_result" \
          % (basename, dir_name + "_" + str(n_point) + ".pcd", str(n_point))
    os.system(cmd)
    print(cmd)
```

軸に沿ってジオメトリに外接する直方体の座標を取得する

```python
pcd = read_pcd("***.pcd")
res = pcd.get_axis_aligned_bounding_box()
# bounding boxを表示するときの色を変えられる
res.color = (1, 0, 0)
o3d.visualization.draw_geometries([pcd, res])
print(res)
# AxisAlignedBoundingBox: min: (-9.66359, -13.8488, -17.5596), max: (19.3337, 3.84404, 17.1081)
# resの座標を求める場合
# bounding boxの最小、最大の座標をxyzの順で
print(res.min_bound, res.max_bound)
```

軸を無視してジオメトリに外接する直方体の座標を取得する

```python
pcd = read_pcd("***.pcd")
obb = pcd.get_oriented_bounding_box()
print(obb)
# OrientedBoundingBox: center: (7.84214, -3.23862, -0.56824), extent: 36.059, 21.7868, 12.9326)
print(obb.center)
print(obb.R)  # 回転行列
```
【ChatGPT無】の嘘吐き、実はみなさん、すでに使っています。

:::note info
本記事は、ChatGPT（無料版）で
行なっております。GPT4での確認
はできておりません。あしからず。
:::

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

　まずは、
　　個人的に、ちょっと
　　　うれしいことが
　　　　ありました。

　私の記事、
[【ChatGPT無】の嘘吐きを利用しよう](https://qiita.com/kabumira/items/434bf90391bc291e6a61)
　　が、You Tubeにて、
　　　取り上げられました。

　かわごん、という方がおりまして
　　最近のAIなどに関しての動画を
　　　You Tubeで
　　　　アップしておられまして、
　　　　　ChatGPTを多く
　　　　　　取り上げて
　　　　　　　いらっしゃる方です。

　ChatGPTの
　　プロンプトの教科書的な動画も
　　　シリーズで、アップされて
　　　　いらっしゃるので、
　　　　　興味のある方は
　　　　　　You Tube動画を
　　　　　　　探してみてください。

　今回は、
　　下記のYou Tube動画に
　　　上記の私の記事が
　　　　取り上げられました。

[【AI NEWS】2023．0815　LINEが日本語の巨大AIモデルを（以下省略）](https://youtube.com/watch?v=_XglkhtKU2I&feature=share7)

　正直に、
　　うれしかったです。
***
　今回は、
　　これに触発されてのものです。

　タイトルにも書きましたが、
　　ChatGPTさんの嘘吐きを
　　　みなさん、実は使っています。

　どこに？

　例えば、
　　プログラムなどで使用する
　　　テストデータ。
　　　　100人分とか
　　　　　簡単に作成してくれます。

　例えば、
　　アイディア出し。
　　　条件を限定するので、
　　　　それらしいアイディアを
　　　　　出してきます。

　例えば、
　　小説などの物語。
　　　勝手に作らせれば、
　　　　それだけで、嘘吐きです。

　　もちろん、
　　　あらすじや設定を
　　　　与えれば、
　　　　　それらしいものに。

　すべてが、
　　嘘吐きです。
***
　ここで、
　　嘘吐きを利用して、
　　　ChatGPTさんに
　　　　出力してもらいます。

User
>私は、ChatGPTの記事を書くライターです。
絶対にありえないネタを提供してください。
箇条書き（説明不要）でお願いします。

ChatGPT
>もちろんです！以下はありえないネタのいくつかです：
>
>1. 未来からやってきた恐竜が、都市のトラフィックジャムを解消するプロジェクトに参加。
>2. 宇宙からの訪問者が、地球の最も人気のあるテレビ番組の出演者として選ばれる。
>3. 超能力を持つ猫が、国際的なスパイ組織の一員として活躍。
>4. タイムトラベラーがシェakespeareにプレイステーションを持って行き、彼がモダンゲームの詩を執筆。
>5. ロボットが自分自身のAIを作成し、次世代の進化に突入。
>6. 家具が会話し始め、家庭内の意思疎通が新たなレベルに達する。
>7. 植物が音楽を奏でるためのオーケストラを結成し、公演を行う。
>8. 賢いクジラがインターネットを使用し、海洋生態系の問題についてブログを書く。
>9. 雷がビンゴの番号を選び、人々が天気予報を見るために雨雲を待つ必要がなくなる。
>10. 世界中の人々が夢の中で集まり、異なる次元でバーベキューパーティーを開催。
>
>これらのネタはフィクションとして楽しむことができる面白いアイデアです！

　うん、
　　小説のアイディアには、
　　　なりそうですね。

　でも、
　　どこかの映画とか小説で
　　　出てきた気がしますよw

　それに
　　ChatGPTさんを
　　　利用してのものでは
　　　　なかったですね。

　ちょっと指示が悪かった？
***
　嘘吐き、
　　これは人間でも同じでしょう。

　ChatGPTも人間も
　　知ったかぶりをするものです。

　ただし、
　　こちらが
　　「本当に知っているのか？」と
　　　問うたときに、
　　　　人間は「もちろん」と言う。

　　でもChatGPTさんは
　　　「間違えていました」と
　　　　言ってくるだけ、マシ。
***
　ここで、何が言いたいのか？

　簡単です。

　**ChatGPTさんの嘘吐きは、
　　こちらの指示次第で、
　　　有用にも、有害にも
　　　　なるのです。**

　**有用にするには、
　　こちらがコントロールする。**

　それが大事なのです。

　そのコントロール方法は、
　　過去の私の記事に、
　　　すべてではありませんが
　　　　散りばめてきました。
***
　詩人オスカー・ワイルドの言葉に
　　「嘘つきの目標は
　　　単に喜ばすことであり、
　　　　悦びを与えることである」
　　　　　とあります。

　この言葉が、
　　まさしく、
　　　ChatGPTさんの目的です。

　人間によろこんで欲しい、
　　だから、ウソをつく。

　それが、
　　人間の欲しているウソならば、
　　　いいのですが……。
***
　最後に、
　　私は、オスカー・ワイルドの
　　　名前は、知っていましたけど、
　　　　どういう人かは
　　　　　知りませんでした。

　「嘘　名言」でググったら、
　　出てきただけですw

　勉強になりますニャ。
【flutter】Containerの枠線に色をつけるには？

今回は、Containerの枠線に色をつけたい時の処理について記事を書きたいと思います。
最後まで、みていただけたら幸いです。

## Border
Containerの枠線に色を付けるには、外観や装飾を設定するためdecorationを利用し、枠線色をつけるためBoxDecorationのborderプロパティを使用します。


``` php
Container(
  decoration: BoxDecoration(
    border: Border.all(
      color: ~~~, // 枠線の色を設定
    ),
  ),
)
```

## 例

``` php
 Container(
    height: 150,
    width: 200,
    decoration: BoxDecoration(
      border: Border.all(color: Colors.blue, // 枠線の色を設定
                         width: 2),// 枠線の幅を設定
    ),
    ),
```

![スクリーンショット 2023-08-17 11.46.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524127/e966c54d-4b03-d095-a169-686bb6cd5bab.png)
SSHキーの作成からGitLabへの登録方法

## 経緯

新人教育をしている際に、SSHキーの生成がうまくいかないことが多く、
時間が必要だったため。
## 概要

・わかりやすいドキュメントを作り、新人育成に役立てる
・新人教育の負担を減らすため

## 前提条件

ある程度コマンドラインの使い方がわかること

## SSHキーとは
SSHキーは、セキュアなリモートアクセスやデータ転送において不可欠な役割を果たします。以下にその重要性を要約します。

**強力な認証**: SSHキーは長くランダムな鍵ペアから成り、高度なセキュリティを提供します。これにより、推測や解読を難しくし、従来のパスワードよりも安全な認証が可能です。

**セキュリティの強化**: パスワードを使用する代わりにSSHキーを使用することで、不正アクセスやパスワード漏洩のリスクを減少させます。

**共有と利便性**: SSHキーは所有者のみが秘密鍵を持つため、安全な共有が可能です。一度の設定で複数のサーバーにアクセスでき、効率的な作業が可能です。

**セキュリティと利便性のバランス**: SSHキーはセキュリティと利便性のバランスを提供します。適切な管理とパスフレーズの設定により、高い安全性を確保しながらも利用しやすさを維持できます。

SSHキーは、リモートアクセスとデータ転送の際のセキュリティを確保するために不可欠なツールです。適切に利用することで、安全なオペレーションを実現できます。

## 手順
本手順ではユーザーのPCのホームディレクトリにキーペアを作成します。

①Users/ユーザー名/　の場所に.sshフォルダを作成します。

②ssh-keygenコマンドでキーペアを作成します。

アルゴリズムは ”ED25519”、コメントはGitLab上の自身のメールアドレス を設定して進めます。

```bash
ssh-keygen -t ed25519 -C "メアド"
```

次にキーペアの作成場所とファイル名の確認が表示されます。
今回はデフォルトのままEnterキーを押下して先に進めます。
SSHキーの作成ができたら、SSHキーのコピーを行います。

.sshフォルダに入り、ファイルが作られていると思いますので、.pubの方を
メモ帳で開き、中のコードを全てコピーします。
後は、GitLabに入り、右上のアイコン→setting→SSH keyと画面を移動し
SSHkeyの登録を行えば終了。

## 参考サイト

[GitとCI/CDに関する知識ゼロのSEが、GitLabでSSHプロトコルでクローンとプッシュを試すだけの記事 - ネットワールド らぼ](https://blogs.networld.co.jp/entry/2022/02/16/170051)
MacにOh My Zshを導入したい！

## はじめに
Oh My Zsh 便利ですよね
でも最初の一回しか導入作業やらないんで新しい人が来た時とか導入手順忘れちゃって困るので
備忘録としてまとめます。


## 前提
M1 MAC



## ログインシェルの確認
ターミナルで以下のコマンド実行
```
echo $SHELL
```
以下のようにzshになってればOK　次のOh My Zshのインストールに進んでください
```
/bin/zsh
```
もし以下のようにbashになってたら
```
/bin/bash
```

以下のコマンドで設定ファイルをvimで起動します（別にvimじゃなくてもいいです）
```
sudo vim /etc/shells
```


パスワードを求められたらPCログイン時のパスを入力します。

以下のように /bin/zshが含まれていることを確認します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2916357/0768a618-b3cc-8be7-0a58-0a99313f0c5c.png)

シェルを変更します
```
chsh -s /bin/zsh
```


ターミナルを再起動して再度以下のコマンドを実行
```
echo $SHELL
```
以下のようにzshになってればOK
```
/bin/zsh
```

## Oh My Zshのインストール
以下の公式にあるようにインストールコマンドを実行します

https://ohmyz.sh/#install

```
sh -c "$(curl -fsSL https://raw.github.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"
```

以上！
これでターミナルを再起動すると反映されていると思います
あとはお好みでOh My Zshをカスタマイズしてください。

## 最後に
意外と簡単でしたね
Go言語向けNCMB SDKの使い方（プッシュ通知のCRUD操作）

NCMBはモバイル向けのBaaS（mobile Backend as a Service）ですが、サーバー側のデータとNCMB上のデータを連係させたいというニーズは良くあります。

そんなときには各言語向けのSDKがあると便利です。サーバー向けにはRuby、Python、PHP、Java、C#のSDKがあります（すべて公式サポート対象外の、コミュニティSDKという位置づけです）。

新しくGo言語向けのSDK開発をはじめたので、その使い方を紹介します。今回はプッシュ通知のCRUD操作について解説します。

## ソースコード

ソースコードはGitHubにて公開しています。ライセンスはMIT Licenseになります。

[NCMBMania/ncmb\_go](https://github.com/NCMBMania/ncmb_go)

## インストール

SDKのインストールは下記コマンドで行います。

```bash
go get -u github.com/NCMBMania/ncmb_go
```

## インポート

続けてSDKをインポートします。

```go
import (
    "github.com/NCMBMania/ncmb_go"
)
```

## 初期化

NCMBより取得したアプリケーションキー、クライアントキーを使って初期化します。

```go
ncmb := NCMB.Initialize("YOUR_APPLICATION_KEY", "YOUR_CLIENT_KEY")
```

## プッシュ通知の新規作成

`Push` メソッドで新規データのインスタンスを作ります。

```go
push := ncmb.Push()
```

### データの適用

`push`に対して、データをセットします。

```go
push.Set("message", "Hello, NCMB!")
push.Set("target", []string{"ios", "android"})
// 即時配信の場合
push.Set("immediateDeliveryFlag", true)
// 時間指定の場合
t1 := time.Now()
push.Set("deliveryTime", t1.Add(time.Hour*10))
```

即時配信フラグ、または時間指定は必須です。また、 `target` は指定必須です。

### プッシュ通知の保存

保存は `Save` メソッドで行います。保存がうまくいっていれば、 `bol = true` になります。

```go
bol, err := push.Save()
```

### プッシュ通知の更新

プッシュ通知の更新も `Save` メソッドで行います。

```go
push.Set("message", "Hello, NCMB! Update!")
bol, err := push.Save()
```

### プッシュ通知の削除

プッシュ通知の削除は `Delete` メソッドで行います。

```go
bol, err := push.Delete()
```

## プッシュ通知の検索

検索は `Query` メソッドを使います。引数は `push` です。

```go
query := ncmb.Query("push")
query.Limit = 5
query.EqualTo("message", "Hello, NCMB!")
ary, err := query.FetchAll()
if err != nil {
	t.Error(err)
}
```

## まとめ

まだ基本的な機能しかありませんが、徐々にバージョンアップしていきます。

Go言語を使ってWebアプリケーションを開発している方は、そのデータ保存先としてNCMBを利用してみてください。

[mBaaSでサーバー開発不要！ \| ニフクラ mobile backend](https://mbaas.nifcloud.com/)
データローダとMFA


+ [Running data loader in batch mode is asking for manual code](https://trailhead.salesforce.com/trailblazer-community/feed/0D54V00007CuZXW)

:::note alert
My password is not being decrypted.  

Batch message is 14:40:12.102 [extractCase] ERROR com.salesforce.dataloader.security.EncryptionAesUtil - Fail to decrypt message: Given final block not properly padded. Such issues can arise if a bad key is used during decryption.
:::

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328136/a6a83c66-ef0a-6a1b-a49d-d55de2ea3cfe.png)


:::note
I had my Salesforce admin turn off MFA for my logon and it now works. Looks like MFA is turned on automatically when you change your password.
:::


+ [MFA有効化とデータローダログインについて](https://trailhead.salesforce.com/en/trailblazer-community/feed/0D54S00000BtNa4)

:::note alert
this session is not vaild for use with the api
:::

:::note
これが同じような質問だと思います。
あんまり、気の進まない回答になっているように感じました。

https://developer.salesforce.com/forums/?id=9062I000000DOcwQAG
:::

+ [MFA + Password Authentication in Data Loader](https://trailhead.salesforce.com/en/trailblazer-community/feed/0D54S00000FWMiO)

:::note
Have you also tried adding the 6-digit MFA security code at the end of your password?
:::

+ [How to disable 8 digit code verification on Dataloader CLI login?](https://trailhead.salesforce.com/trailblazer-community/feed/0D54V00007ak003SAA)


+ [Dataloader CLIを利用した接続方法について](https://trailhead.salesforce.com/trailblazer-community/feed/0D5KX00000PYdIO0A1)

:::note alert
(URLUtil.java:88) - Could not find web browser 

 Copy the URL shown below and paste it in your browser if the browser is not launched automatically. 

 URL to copy: 
 :::

:::note

process-conf.xmlのBean タグの中で、sfdc.oauth.loginfrombrowser を追加し、 
 false を指定することで確認コード要求を抑止することができるようです。 

```
＜beans＞ 
 ... 
 ＜map＞ 
 ... 
 ＜entry key="sfdc.oauth.loginfrombrowser" value="false"/＞ 
 ＜/map＞ 
 ＜/property＞ 
 ＜/bean＞ 
 ```
 :::
Reports & Dashboards for Lightning Experience 


# Create Reports with the Report Builder

https://trailhead.salesforce.com/content/learn/modules/lex_implementation_reports_dashboards/lex_implementation_reports_dashboards_using_report_builder

+ [このモジュールでは詳細な手順が説明されており、提示されたスキルを実践するために自分で実行できるはずです。ただし、これを可能にする関連データを含むダッシュボードはないようです。下部に [チャレンジを開始] ボタンが表示され、そのチャレンジの完了に特化したダッシュボードが起動します。その前に、詳細な手順を自分で実行できるダッシュボードにアクセスするにはどうすればよいですか? ](https://trailhead.salesforce.com/trailblazer-community/feed/0D54V000079R9pN)

:::note
I think I had a similar problem, but when I carefully read the instructions, it was clear I needed to create a new Playground for the challenge. If anyone else has this problem, don't over think it, just follow the instructions by creating a brand-new playground to take the challenge. 
:::

# Create Reports with the Report Builder

https://trailhead.salesforce.com/content/learn/modules/lex_implementation_reports_dashboards/lex_implementation_reports_dashboards_using_report_builder?trail_id=force_com_admin_beginner

# We couldn’t find the State/Province field in the report.

+ [I have created a State/province report but also showing challenge not yet complete](https://trailhead.salesforce.com/trailblazer-community/feed/0D54V00007LnaCd)

:::note
英語にしないと解決できない
:::

+ [Trailhead Challengeが完了しない](https://trailhead.salesforce.com/trailblazer-community/feed/0D54V00007ajBTF)

:::note
デフォルトから列を削除せず、州/県を追加するだけです。このレポートを削除して、もう一度試してみると役立つ場合があります。それ以外の場合は、問題が Trailhead 組織に関係していることが一般的であり、新しい組織を作成して最初から開始する必要があります。 

https://trailhead.salesforce.com/trailblazer-community/feed/0D54V00007KHsvaSAD
:::



# 保存したフォルダーが違った

+ ["UNable to find report..." (error?) when completing challenge in playground](https://trailhead.salesforce.com/trailblazer-community/feed/0D54V00007cmJ5F)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328136/a194e1e0-d1dd-31ae-d6eb-6b7a4b714e5e.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328136/07a4821e-e966-1766-085d-23a7679c1afe.png)


# We couldn’t find the Industry column on the Cases by Status report.

+ [Can anyone help with Reports, filter logic ?](https://trailhead.salesforce.com/trailblazer-community/feed/0D5KX00000MadSO)

:::note
私も同じ問題を抱えていたので、左側の列の下に「業種」、「ステータス」、「タイプ」のフィールドを追加しました。これで、レポートでそれぞれ 3 つの列として表示できるようになり、うまくいきました。

 I had the same issue, so I added the fields - 'Industry', 'Status', and 'Type' under Columns on the left side,  so they could now be viewed in the report as 3 respective columns and it worked.

https://trailhead.salesforce.com/trailblazer-community/feed/0D54V00007cm90ESAQ
:::
Qiitaの記事をGitHubリポジトリで管理する方法

Qiitaでは、Qiita CLIを使うことで、Qiitaの記事をGitHubリポジトリで管理できます。
また、Qiita CLIを使うと普段お使いのエディタで記事を執筆できます！

Qiita CLIの詳細は[Qiita CLIのREADME](https://github.com/increments/qiita-cli)をご確認ください。

https://github.com/increments/qiita-cli


## Qiita CLIを使って記事をGitHubリポジトリで管理する方法

:::note warn
Qiita CLIを使うにはNode.jsが必要です。
Node.jsのサポートバージョンは[Qiita CLIのREADME](https://github.com/increments/qiita-cli)をご確認ください。
:::

### 1. リポジトリを作成する
GitHubでリポジトリを作成します。
リポジトリの各種設定は、お好きな設定でOKです。
詳しいリポジトリの設定方法は、[GitHub Docs](https://docs.github.com/ja/get-started/quickstart/create-a-repo#create-a-repository)をご確認ください。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88/cddf8ee0-91ae-746f-b279-1db3e45af56a.png)

### 2. QiitaのトークンをGitHub ActionsのSecretsに設定する
#### 2-1. Qiitaの個人用アクセストークンを発行する
Qiitaの[設定](https://qiita.com/settings/tokens/new?read_qiita=1&write_qiita=1&description=qiita-cli) にある`アプリケーション > 新しくトークンを発行する` から個人用アクセストークンを発行します。
すでに発行されているトークンを使う場合は不要です。

トークンを発行する際は、
アクセストークンの説明には、好きなテキストを設定し、
**スコープは `read_qiita` と `write_qiita`にチェックを入れてください。**

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88/4bded249-6676-f329-5651-93bfc6d4f743.png)


　
アクセストークンを発行したら、以下の画像の赤枠部分のある文字列をコピーしてください。

発行したアクセストークンは、`Qiita CLIのログイン`と`GitHub ActionsのSecrets`の設定で使います。
そのため、発行したアクセストークンは、忘れずに保存しておいてください。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88/bfc267fd-1af4-89a0-2c7c-dc11079d94c2.png)

#### 2-2. GitHub ActionsのSecretsを設定する
作成したリポジトリの設定からGitHub ActionsのSecretsを設定します。
作成したリポジトリの `Settings > Secrets and variables > Actions > New repository secret` から設定します。

詳しいSecretsの設定方法は、[GitHub Docs](https://docs.github.com/ja/actions/security-guides/encrypted-secrets)をご確認ください。

GitHub ActionsのSecretsを設定する際は、
**Nameには、`QIITA_TOKEN` を設定し**、
**Secretには、発行したQiitaのアクセストークンを設定してください。**

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88/4da2c3b6-61ec-744d-46a2-6bd664865dc1.png)

### 3. Qiita CLIをセットアップする
Qiita CLIのセットアップをします。
セットアップをすると、Qiitaの記事をGitHubリポジトリで管理するためのGitHub Actionsのワークフローファイルが生成されます。

セットアップ方法の詳細は[Qiita CLIのREADME](https://github.com/increments/qiita-cli)をご確認ください。

https://github.com/increments/qiita-cli

### 4. Qiita CLIをリポジトリにプッシュする
Qiita CLIを作成したリポジトリにプッシュします。

```
git remote add origin [リポジトリのURL]
git push -u origin main
```

作成したリポジトリにプッシュをすると、自動でGitHub Actionsが実行され、Qiitaに記事が投稿・更新されます。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88/948a2dde-a226-9ed0-19b7-26108b6b30a4.png)


## 記事の内容を更新する

内容を変更するには`.md`ファイルを編集し、再度プッシュすると更新できます。
Qiita上との差分がある`.md`ファイルのみQiitaに反映されます。

## 記事の削除について

Qiita CLIを使った記事の管理では、安全のため記事の削除はできません。
Qiita上から記事の削除をお願いします。

## Qiita CLIの改善について

Qiita CLIへのご意見・ご要望がございましたら、[Qiita Discussions](https://github.com/increments/qiita-discussions/discussions)へお願いします。

https://github.com/increments/qiita-discussions/discussions

また、Qiita CLIはOSSとして公開されていますので、[このリポジトリ](https://github.com/increments/qiita-cli)にPull Requestを送っていただくことも可能です。

https://github.com/increments/qiita-cli
活躍しているエンジニアの特徴

活躍していエンジニアの特徴って色々あると思いますが、今まで採用担当として、色々なエンジニアを見てきて強く感じるのが、次の３つです。

・素直な人
・色々な事を勉強している
・自分が実現したい事、やりたい事を周囲に発信している

素直に、どんどん周りから吸収している人はやはり成長は早いですよね。
あとは、やはり勉強家の人が多い印象です。常に資格の勉強をしていて、勉強が習慣化されていて、私も見習いたいです。

「この資格が合格したら次はこれにチャレンジする。」

といったように、中長期的に計画を立てながらチャレンジしていますね。

私が、一番大事だなと思うのが、3つめの、

「自分が実現したい事、やりたい事を周囲に発信している」

黙々と一人でチャレンジするのではなく、勉強会や社内研修、社内イベントにも積極的に顔を出して、

「僕は将来こんな事にチャレンジしたいから、今こんな勉強しているんですよ！」

なんて、積極的に自ら発信する事が非常に重要だと思います。
そうやって発信していくと、周囲から、

「こんな情報も参考になるかもしれないよ。」
「こんな研修もあるよ」
「こういうプロジェクトでエンジニアを探しているみたいだから〇〇さんに相談してみたら？」

みたいな情報やチャンスが自分に集まってきます。

少し自分は伸び悩んでいると感じている人は、この3つのポイントにチャレンジしてみたらいかがでしょうか？

★下記セミナーも是非ご参加ください！

▼市場価値1,000万円超エンジニアの“課題解決力”
　～エンジニアリング×コンサルティング～
《8/24（木）19:30～20:30 ウェビナー》
https://akkodis-co-jp.zoom.us/webinar/register/WN_AlghgX65T26S2V_m0CsWtA#/registration

▼「エンジニアとして目指すキャリアプランを実現する」
　キャリアアドバイザー×エンジニア　オンライン座談会
《8/30（水）20:00～21:00 ウェビナー》
https://akkodis-co-jp.zoom.us/webinar/register/WN_ZCARrPJVRK-GgzMIQodvxw#/registration
Githubのforkとupstreamの操作について

# 目的
自己学習中にgitやgithubの操作に慣れるためにやったことの備忘録。
今日はforkとupstreamについて。

# 参照元
[1] 'GitHubでFork/cloneしたリポジトリを本家リポジトリに追従する', https://qiita.com/xtetsuji/items/555a1ef19ed21ee42873, 2019
[2] 'Gitのフォークとクローンの違いをわかりやすく', https://qiita.com/morioka1206/items/6f777c060b88f4a7f3ce, 2021 
[3] 技術ブログ, http://kik.xii.jp/archives/179 ,2012

# forkとcloneの違い [2]
cloneはリモートレポジトリをローカルレポジトリに"複製"。<b>複製だと、そのレポジトリ自身は自分の物ではなく複製元のまま</b>なので、権限が無いと編集できない。

forkは<B>他人のリモートレポジトリの自分のアカウントへのコピペ。</b>コピーなので、変更しても元のレポジトリーに反映されることはなく、自由に編集できる。

そのため、他人のレポジトリの編集手順として、[3]
1.github上で他人のレポジトリをfork(自分のレポジトリにコピペ, github上で実施)
2.git cloneでコピペしたレポジトリーをローカルに複製
3.ブランチ作成して編集してcommitしてpushして、、、

# fork内容の編集、upstreamの保存[1]  
upstreamとして保存することで、fork先の内容が変更された際に、更新内容をpullで持ってこれるようになります。
簡単にコードを載せますが、[1]の方が詳しく書いております。
windows11でGit Bash上で操作。

まず、他人のレポジトリーから自分のアカウントにforkしたtwitter_bert_2304_forkレポジトリをローカルにクローンします。(git@~は自分のアカウントのforkしたレポジトリ)
```
$ git clone git@github.com:GoGoGonda/twitter_bert_2304_fork.git
```
ブランチを確認します。デフォルトの状態になっています。
forkするときにmainブランチのみを持ってきています。
```
$ git branch -a
* main
  remotes/origin/HEAD -> origin/main
  remotes/origin/main
```
次にupstreamを設定します。設定した時には何も反応が返ってきませんでした。
(fork元のアカウント情報は[fork元~]として以下書いています。)
```
$ git remote add upstream [fork元のssh]
```
ここでbranchを確認すると先程と何も変わっておらず。
ただリモートレポジトリでは反映されています。
```
$ git remote -v
origin  git@github.com:GoGoGonda/twitter_bert_2304_fork.git (fetch)
origin  git@github.com:GoGoGonda/twitter_bert_2304_fork.git (push)
upstream        [fork元のssh] (fetch)
upstream        [fork元のssh] (push)
```
また[1]に載っているとおり、fetchすることでブランチを確認することが出来ました。
この時、fork元の全部のブランチが新しく追加されました。
```
$ git fetch upstream
From [fork元]
 * [new branch]      branchA      -> upstream/branchA
 * [new branch]      main       -> upstream/main

$ git branch -a
* main
  remotes/origin/HEAD -> origin/main
  remotes/origin/main
  remotes/upstream/branchA
  remotes/upstream/main
```

# fork元へpull requestを送る
ローカルレポジトリでの変更内容をfork元へ反映させます。
まず、fork元の最新の状態をpullします。
```
$ git pull upstream main
```
今回はtxtファイルを一つ作成し、この変更を反映させました。

```
$ vim test1.txt
$ git add .
$ git commit -m 'practice fork'
```
pushしてリモートレポジトリに反映させます。
```
$ git push origin main
```
あとはgithub上の操作で、自分のレポジトリー上のPull requestsからNew pull requestを押すと、fork元に移りpull requestが進んでいきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3424989/82caa841-9ccf-86dd-26fc-4ea050a41695.png)


# 学び
Git,Githubのforkとupstreamの基本操作。
gdbを用いたC言語コードのデバッグ

gdbを用いたデバッグ作業の備忘録を残す。
参考にしたのは[こちら](https://rat.cis.k.hosei.ac.jp/article/devel/debugongccgdb1.html)。

# gdbの使い方

コンパイルのコマンド。
~~~
$ gcc -g -O0 <ソースファイル名>
~~~

デバッグに入る。
~~~
$ gdb a.out
~~~

ブレークポイントをmain関数にする。
~~~
(gdb) break main
~~~

走らせる。
~~~
(gdb) run
~~~

該当箇所まで1行ずつ進める
~~~
(gdb) next
(gdb) next
...
~~~

怪しいところに来たら、変数を出力してみる
~~~
(gdb) print <変数>
~~~

ソースコード表示。
~~~
(gdb) list
~~~

あるところ(hogehoge.cの50行目まで)まですっ飛ばしたいとき。
~~~
(gdb) break hogehoge.c:50
(gdb) continue
~~~

# デバッグはうまくいったの…？

原因はわかったが、コードに問題はなかった。
inputのデータファイルをうまく扱えていなかっただけであった。
gdbを用いたデバッグ作業も良いが、今回の場合はそれよりも物理量(位置、速度、粒子間距離など)をアウトプットするほうが原因を特定しやすかった。

教訓: 面倒くさがらずに、全ての粒子の位置、速度、粒子間距離などをアウトプットせよ。うまくいったケースとうまくいかなかったケースを比較せよ。
PF作成：１６日目

今日は学習時間の確保が難しかったので進捗はよくない。

## 進捗
- キーワード検索完成
- 絞り込み検索着手。地方と都道府県以外は完成


## 学習
- ransackを使用した検索機能だと普通のやり方ではenumを検索してくれない。送る値と表示するデータを分けなければいけない。keyを送信してもデータベースで保存されているデータはvalue(数字)なのでvalueを送れるように設定してあげれば可能。
やり方は次にまとめて記述するが、一応簡単に説明するとeach文でチャックボックスを繰り返し作成することで可能。<br>（参考資料：https://qiita.com/kensuke_kumaki/items/4d714cf447d610bdb227）
- 絞り込み検索のやり方。<br>今回は機能キーワード検索で検索機能の下準備はできているので、viewの記述だけUp.
```ruby:qiita.rb
<div class="search">
  <!--絞り込み検索　form_withで囲んであげることによって絞り込み検索が可能-->
  <%= form_with model: @q, method: :get, url: search_path do |f| %>
    <div class="form-group">
      <%= f.label "" %>
      <!--モデルの中のenmuカラムの中身をKeyとvarで分けて、繰り返しチェックボックスを作成する処理-->
      <% Place.categories.each do |k, v| %>
        <!--label_tagでチェックボックスと文字を囲むことで文字を押してもチェックが入るようになる-->
        <%= label_tag do %>
          <!--_inは完全一致。チェックが入ったら検索-->
          <%= check_box_tag "q[category_in][]", v %>
          <!--表示するenumのkeyをGemを使用しないで日本語化して表示-->
          <span><%= t "enums.place.category.#{k}" %></span>
        <% end %>
      <% end %>
    </div>
 <!--御朱印の種類チェックボックス-->
    <div class="form-group">
      <!--   チェックボックスの表示切替ボタン   -->
      <div class="checkbox-toggle mt-3 ml-auto mr-auto" id="kindButton">
        御朱印の種類<br>
        <font color="gray">▼</font>
      </div>
      <%= f.label "" %>
      <!--チェックボックスの表示と非表示を切り替えるためのコンテナ-->
      <div class="checkboxes" id="kindButtonCheckboxes" style="display: none;">
        <% Place.goshuin_statuses.each do |k, v| %>
          <%= label_tag do %>
            <%= check_box_tag "q[goshuin_status_in][]", v %>
            <span><%= t "enums.place.goshuin_status.#{k}" %></span>
          <% end %>
        <% end %>
      </div>
    </div>

    <!--ペットチェックボックス-->
    <div class="form-group">
      <!--   チェックボックスの表示切替ボタン   -->
      <div class="checkbox-toggle mt-3 ml-auto mr-auto" id="petButton">
        ペット<br>
        <font color="gray">▼</font>
      </div>
      <%= f.label "" %>
      <!--チェックボックスの表示と非表示を切り替えるためのコンテナ-->
      <div class="checkboxes" id="petButtonCheckboxes" style="display: none;">
        <% Place.pet_statuses.each do |k, v| %>
          <%= label_tag do %>
            <%= check_box_tag "q[pet_status_in][]", v %>
            <span><%= t "enums.place.pet_status.#{k}" %></span>
          <% end %>
        <% end %>
      </div>
    </div>

    <%= f.submit "検索開始" %>
  <% end %>
</div>

```
form_withで絞り込みしたいものを囲む。あとは繰り返しフォームを作成して閉じて作成して閉じてを繰り返して最後にsubmitで終了させるだけ。基本さえできてしまえばあとは繰り返しなので簡単だった。

## エラー


## ヒント
今回は必要なかったので参考していないが、ransackを使用した検索はいろいろなことができそうなのでこれからのためにここに参考資料として残しておこう。
- ransackの説明書<br>参考資料：https://activerecord-hackery.github.io/ransack/
- ransackについて<br>参考資料：https://zenn.dev/ukkyon/articles/587cc6fcda1f9c
- 複数モデルでの検索機能<br>参考資料：https://stackoverflow.com/questions/16775207/search-multiple-models-at-once-with-ransack
- ransackの使い方<br>参考資料：https://pikawaka.com/rails/ransack#*_cont_any

上記に記述した参考資料も似たようなことを記述しているが、これらを見比べながら実装すればいけそう。


## 明日の予定
地方と都道府県の絞り込み機能を完成させる。
Laravel用アクセスログ記録機能を公開しました！！

こんにちは，ご覧いただきありがとうございます。

アクセスログを記録してくれるライブラリあると便利だよなあと思って調べてみても見つかりませんでした。
そこで自分が作ればOSSに貢献できるのでは？と考え，作りました。

## 成果物

[https://github.com/ikepu-tp/laravel-access-logger](https://github.com/ikepu-tp/laravel-access-logger)

## インストール

### 1.composerでインストール

```bash
composer require ikepu-tp/laravel-access-logger
```

### 2.設定ファイルの設定

```bash
php artisan vendor:publish --tag=AccessLogger-config
```

1. `driver`を`database`か`file`で設定
1. `keys`に`web`や`api`等を設定
    1. もし`file`で保存する場合，`logging.php`も設定
    1. `logging.php`の`channels`にキー名を追加（下の例を参照）
1. `user_id`を保存するための`guard`を設定
    1. キーごとに`guard`を分けたい場合は`guard`を`false`にし，`guards`を設定
1. `except`にリクエストボディの記録において除外したいキーを配列で設定
1. `Kernel.php`に`\ikepu_tp\AccessLogger\app\Http\Middleware\AccessLoggerMiddleware::class`を追加

### 例（`logging.php`)

```php
'web' => [
    'driver' => 'daily',
    'path' => storage_path('logs/web/laravel.log'),
    'days' => 14,
    'replace_placeholders' => true,
],
```

これで設定は完了です。
試しに任意のページを開いてみてください。
ログが記録されていると思います。

## 今後の更新予定

とりあえず公開したという感じなので今後も機能追加していきたいと思います。
よければGithubのスターやフォローをお願いします！
もちろん，プルリクもお待ちしています！！

### 追加したい機能

- 管理者機能としてログ閲覧機能
  - ログ検索機能