OmniAICreator/Qiita-1.07M huggingface dataset:
VSCodeでLatex環境つくったらはまった

#### 2023/11/30追記
以下の方法よりも良いやり方を見つけました。latexmkは使わずに、こちらのgitsで紹介されている設定を使うというものです。
https://gist.github.com/Ikuyadeu/204d06fffd912f441b383eb02463e29b


私の環境だとpbibtexでコケたので、step2は削除して使ってます。（bibtexのコンパイルだけ、overleaf上で行っています）


一応以下は参考までに残しておきます。

-----

Latexの編集にはOverleafが便利ですが、ボリュームのある原稿を書くときには、コンパイルが重かったりsynctexが動作しなかったりと微妙なかんじです。
そこで、ローカルのVSCodeでTexを編集したくなりました。

かなり苦労しましたが、一応最低限の設定ができたのでメモ。

### できたこと、はまったこと
- VSCodeで快適に編集
  - 外部のstyファイルに定義してあるコマンドが、コード補完に認識されない問題にはまった
- ターミナルでコンパイル
  - Latex Workshopでコンパイルすると、なぜかエラーを吐く。頑張ったが解消しなかったので、Terminalでコンパイルすることにした。

### 要件
 MacTex
 VSCode
### 設定
基本的にはこの記事のとおりにやる
[VSCode で最高の LaTeX 環境を作る #VSCode - Qiita](https://qiita.com/rainbartown/items/d7718f12d71e688f3573)

### はまったこと
#### 編集
外部のstyファイルに定義してあるカスタムコマンド(xyz.sty)が、コード補完に出てこなかった。

この問題は、xyz.styをxyz.texに拡張子を変更し、`\usepackage{xyz}`のかわりに`\input{xyz}`を使うことで解消した。

#### コンパイル 
これがかなり苦戦した。Latex Workshopでコンパイルすると、こんなエラーがでてしまう
```
[05:18:18.843][Builder] Recipe step 1 The command is latexmk:["-f","-silent","-outdir=out","%WS1%/apssamp"].
[05:18:18.843][Builder] env: undefined
[05:18:18.843][Builder] root: %WS1%/apssamp.tex
[05:18:18.843][Builder] cwd: %WS1%
[05:18:18.844][Builder] LaTeX build process spawned with PID 5912.
[05:18:18.903][Builder] Recipe returns with error code 12/null on PID 5912.
[05:18:18.903][Builder] Does the executable exist? $PATH: <パスの一覧>, $Path: undefined, $SHELL: /bin/zsh
...
```

これはどうしても修正できなかったので、Terminalでコンパイルすることにした。**Terminalでコンパイルすると、VSCodeで開いているPDFは自動で更新されない**ので、 Ctrl + Shif + Pから "Latex Workshop: Refresh all Latex PDF viewers"を実行する。（あらかじめ、PDFは開いておくこと）

このコマンドをHotkeyに登録しておくと捗る。

**Terminalでコンパイルするときの注意点**
- latexmkrcを削除してからコンパイルする
	- Overleafからpullすると、きっとルートディレクトリに存在しているはず（日本語で書いている場合）
- PDFとsynctex.gzをoutディレクトリにコピーする

このコマンドでコンパイルしている
```bash
$ latexmk; cp apssamp.pdf out; cp apssamp.synctex.gz out
```

Overleafと同期を取るためにはこのコマンドでコンパイル。latexmkrcは最初の１回だけlatexmkrc.bakという名前に変更しておく。
```
$ latexmk; cp apssamp.pdf out; cp apssamp.synctex.gz out; g pull; g merge origin/master -m "merge"; cp latexmkrc.bak latexmkrc;  g add .; g ci -m "update"; g push origin; rm latexmkrc;
```

やれやれ、一応できました。（コンパイルはひと手間かかるけど）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3445203/4d7c7b13-f53c-1f4d-c746-b5f1d0166d1c.png)


(ちなみに、画像周りを編集してコンパイルするとPDFが作成されない場合があるので、そのときは生成ファイルをいったん全部削除したうえで、もう一度latexmkを実行する）
【驚愕】datatable変数からカスタムオブジェクト変数にする方法【PowerAutomateDesktop】

　PowerAutomateを使っていて、UiPathの辞書型変数のように変数を使えたらいいなと思うことありませんか？ExcelからConfigファイルを読み込んでdatatable変数からカスタムオブジェクト変数に変換できる方法を発見したので記録として残します。

# 具体的な方法

　辞書型変数に近いものとして、PowerAutomateにはカスタムオブジェクト変数というものがあります。カスタムオブジェクト変数は、JSONを変数に置き換えたものとなりJSONファイルから読み込んだ変数ならば「JSON文字列をカスタムオブジェクトに変換」アクティビティで作成可能です。

　JSONファイルを読み込んだ時のデータは、「テキスト」データとして作成されます。つまり、カスタムオブジェクト変数は文字列の合成でもJSONを作成してカスタムオブジェクトへ変換が可能です。今回の狙いとしては、datatable変数からキーと値を取ってきてJSONのような形式の文字列を作成してカスタムオブジェクト変数へ変換という流れになります。
 
 そもそも、JSONのテキストとは以下のものを作成すれば問題ないです。

```
{ "名前":"Aさん", "電話番号":"XXX-XXXX" }
```

　これをdatatable変数からキーと値を取り出して文字列合成をすればいいわけです。　

# 設計
  
 流れとしては
 １，datatable変数を作成
 ２，JSONにする文字列の初めの「{」を代入
 ３，「for each」に１の変数を取り込む。
 ４，３の中に２の文字列に１の各行の変数をJSONのように合成していく。
 ５，３と４で完成した文字列の最後に「}」を代入
 ６，５で完成した文字列を「JSON文字列をカスタムオブジェクトに変換」アクティビティに変換

　というものです。

 # 全体フロー
 完成図としては以下のようになります。

 ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3533364/b35f64d3-2fcb-8d00-32b3-6056123ae003.png)

元データは以下になります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3533364/5a0a7043-04db-2f01-6e79-532374777e04.png)

ここで最重要なのは４のアクティビティになります。それは以下の通りです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3533364/9e5d3317-5f37-5b50-df97-d1e55e75439f.png)

# 実際に動かしてみる。
　では、まず５まで動かしてみたいと思います。結果は以下のようになります。

 ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3533364/a5914bb0-eb87-6bfe-30bd-ffe35ce5e793.png)

　うまくできたので、これをカスタムオブジェクト変数に変換いたします。結果は以下のようになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3533364/b988cfe1-2577-d5d3-4b65-fbb6958e96eb.png)

　という感じで成功いたしました。試しに実際に取り出してみましょう。
 ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3533364/eef1241d-aa00-bb71-7eca-0a3d8ab9e779.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3533364/beb0f4bd-4bd4-eda8-34c8-783c6c6ded21.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3533364/7c81b252-13a1-ee5b-c0a0-bacf232ab3e8.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3533364/59750669-788c-1630-5b9f-cedf80d70720.png)

　はい、うまくいくことが確認できました。
ネットワーク上のMicrosoft Excelブックが使用中かどうかを確認して開く

　開こうとするMicrosoft Excelブックが他で使われているかどうか？
をVBAで判断する際は、速度を稼ぐため「ファイルとして追記(Append)モードで開けるか？」を試してエラーコード（または単にエラーの有無）で判断することが推奨されてますが、
　これ、ネットワーク上のファイルでやるとエラーにならずに返ってきません？
要するに使えないということ。

　なので正攻法で作りました。
引数"ブック名"には「調べるブックへのフルパス」を指定します。
開ければ、True、開けなければ、Falseが戻り値です。
開けた場合、引数"ブック名"には（フルパスから抽出した）単独のファイル名がセットされます。一度ブックを開いたら、あと、扱うのはファイル名ですから、一度にやっておきます。
開けなかった場合、ブックそのものがなければ引数"ブック名"はString型の初期値(vbNullString)がセットされます。
ブックが開かれている場合は、引数そのままです。ここでエラー内容が区別できます。
（開かれている場合もファイル名単独を返そうかとちょっと悩みましたが、エラーメッセージにフルパスを使うかもしれない、と思いとどまりました。
ブックそのものがない場合は「論外！」ということで。）

```
' [参照設定]からMicrosoft Scripting Runtimeを指定すること
Function OpenBookWritable(ByRef ブック名 As String) As Boolean

Dim fso As FileSystemObject: Set fso = New FileSystemObject

    '開こうとしたファイルが存在しない場合
    If Not fso.FileExists(ブック名) Then
        OpenBookWritable = False
        ブック名 = vbNullString
        Exit Function
    End If

    Application.DisplayAlerts = False

    'ワークブックをオープンした場合、 通知を表示しないようにする。
    Workbooks.Open Filename:=ブック名, Notify:=False

    If ActiveWorkbook.ReadOnly Then
    '開いたブックが読み取り専用となっていたら閉じる。
        ActiveWorkbook.Close
        OpenBookWritable = False
    Else
        OpenBookWritable = True
        ブック名 = fso.GetFileName(ブック名)
    End If
    
    Application.DisplayAlerts = True
    
    Set fso = Nothing

End Function
```

If ActiveWorkbook.ReadOnly
はようするに
「開いたウィンドウのタイトルに[読み取り専用]がついているかどうか、の判断をしている、ということです。
STM32G031のSPIで遊ぶ


目的
遊ぶ


![o_cop773.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/f0f5b2ae-45f6-9ebd-4c3e-a8ffbcd1ee78.jpeg)



```c++


#include <SPI.h>

#define DW    digitalWrite

//#define s60() for(ii=0;ii<60;ii++)delay(1000);
#define s60() delay(1000);


void setup() {

delay(3000); //復旧用のおまじないで絶対消すな。
  
  int ii; //ループカウンター

  //setMISO(PA11);
  //setMOSI(PA12);
  //setSCLK(PA1);
  //setSSEL(PB7);
 
  // SPIの初期化
  SPI.begin(); // MISO-PA11 MOSI-PA12 SCLK-PA1
  //pinMode(PB7, OUTPUT);
  //DW(PB7, LOW); // B7
  SPI.setDataMode(SPI_MODE0);
  SPI.setBitOrder(MSBFIRST);
  SPI.setClockDivider(SPI_CLOCK_DIV8);  // 2Mhz
  
  //0
  SPI.transfer(0x00);//DW(PB7,HIGH);DW(SS,LOW);
  SPI.transfer(0x00);//DW(PB7,HIGH);DW(SS,LOW);
  s60();
  
  //1
  SPI.transfer(0x00);//DW(PB7,HIGH);DW(SS,LOW);
  SPI.transfer(0x55);//DW(PB7,HIGH);DW(SS,LOW);
  s60();
  
  //2
  SPI.transfer(0x00);//DW(PB7,HIGH);DW(SS,LOW);
  SPI.transfer(0xaa);//DW(PB7,HIGH);DW(SS,LOW);
  s60();
  
  //3
  SPI.transfer(0x00);//DW(PB7,HIGH);DW(SS,LOW);
  SPI.transfer(0xff);//DW(PB7,HIGH);DW(SS,LOW);

} //setup


void loop() {
} //loop



```
Pythonで「文字列の中の文字が英字を表す文字かどうかを判定する(isascii, isalpha, isalnum)」の動作を確認してみた

# 概要

Pythonで「文字列の中の文字が英字を表す文字かどうかを判定する(isascii, isalpha, isalnum)」の動作を確認してみました。以下のページを参考にしました。

https://www.javadrive.jp/python/string/index15.html

# 実装

以下のファイルを作成しました。
```sample.py
print("Hello".isascii())
print("8052".isascii())
print("関東地方".isascii())

print("-8052".isascii())
print("-3.1415".isascii())
print("2,334,500".isascii())

print("".isascii())

print("Hello".isalpha())
print("3345".isalpha())

print("京都".isalpha())
print("Ａｐｐｌｅ".isalpha())
print("７６５".isalpha())
print("七五三".isalpha())

print("".isalpha())

print("100years".isalnum())
print("東京都".isalnum())
print("Ｏｒａｎｇｅ７".isalnum())

print("".isalnum())
```

以下のコマンドを実行しました。
```
$ python3 sample.py 
True
True
False
True
True
True
True
True
False
True
True
False
True
False
True
True
True
False
```

# まとめ

何かの役に立てばと。
pointerEventメモ

マウスアクションでできるPointer Eventsで使いそうなイベントをメモ

```js
const mouseEventContent = document.getElementById('mouseEventContent');
mouseEventContent.addEventListener('pointerover', (event) => {
  console.log('ポインターが要素内に入った');
});
mouseEventContent.addEventListener('pointerout', (event) => {
  console.log('ポインターが要素外に出た');
});
mouseEventContent.addEventListener('pointerdown', (event) => {
  console.log('マウスクリック');
});
mouseEventContent.addEventListener('pointermove', (event) => {
  console.log('マウスを要素内で動かした');
});
mouseEventContent.addEventListener('pointerup', (event) => {
  console.log('マウスクリックアップ');
});
```
現在はPCの他にスマホからもWEBにアクセスできるので、異なるデバイスでも同じ動作をさせようとするのは一工夫必要。
難しい場合はWebまたはスマホ専用サイトとして設計することも考慮する

#### 参考
https://developer.mozilla.org/ja/docs/Web/API/PointerEvent

https://webfrontend.ninja/js-pointer-events/
たまにはロジックを鍛えよう

この記事は [一分で読める小ネタのカレンダー | Advent Calendar 2023 - Qiita](https://qiita.com/advent-calendar/2023/raki) の７日目

# たまにはロジックを考えよう

- 以前はバックエンドもフロントエンドもやっていたし、iPhone ユーザなので Objective-C でアプリを書いてたこともあるんだけど、現在は SRE って職能でお仕事をさせてもらっていて、正直プログラムを書く機会は激減してる
- なので勘所を失わない程度に各言語でプログラムを書く練習をしてる
- 先日書いたように、深掘り対象にしているアプリ（パズルゲーム）のロジックを考えてみようって話
- 自分の好きなゲームでやってみるといいよ
- もちろん言語はなんでもいい
- 複数の言語で、その言語仕様に則った書き方で書いてみるとか、テストを書くとかすると楽しい

今日の本文はここまで。以下は時間外ｗ


# 今回のターゲットは

- Easybrain ってとこが出しているナンバーサムというゲームがありまして
    - [Number Sums - Numbers Game - Apps on Google Play](https://play.google.com/store/apps/details?id=com.easybrain.number.sums.puzzle&hl=en_US&pli=1)
    - [「ナンバーサム - 数字 パズル」をApp Storeで](https://apps.apple.com/jp/app/%E3%83%8A%E3%83%B3%E3%83%90%E3%83%BC%E3%82%B5%E3%83%A0-%E6%95%B0%E5%AD%97-%E3%83%91%E3%82%BA%E3%83%AB/id6449427809)
- ルール等はリンク先を見てもらうとして
- デイリーがあるので１日１問新しい問題が必要
- レベル（ってわけじゃないけど）好きにプレイできる問題がたくさんある（通信追いかけてまで中身見てないので全員同じ問題が提供されているのか、それともランダム生成なのかはわからない）
- というわけでたくさん問題を作るプログラムを考える

# 問題から逆算して仕様を考える

- 縦横のマスは４から９程度（画面サイズの制約かな。説明文は3x3-10x10ってあるけど見たことない）
- 縦横の合計の数字になるように数字がランダムに配置される
- ０は使用されない
- サンプルの画像にはマス目側に２桁の数字があるけど、実際は見たことないかも（とりあえず１から９までと仮決め）
- モバイルアプリの仕様なので、タップでとか長押しでとかは一旦放置
- ヒント処理もモバイルアプリの仕様ってことでいいはず（CLIで表現するの難しいし）
- ミス判定処理も考えない（問題を作ることだけに専念）
- 今のところ別解がある問題は見たことがない
- 手軽にランダムに何問でも作れるのが望ましい
- ということは
    - 縦横の数を決める（４から９）
        - 固定はちょっとね
        - 引数渡しがベター
        - ランダムにして何問も一度に作れる１つのプログラムにしちゃうのも悪くないけど、それは UNIX 哲学から外れるかな
    - 生成処理を実行する
    - 問題が出てくる
- という感じになりそう

# 一旦避けていいものも考える

- 実際の業務ではだいたい許されないけど、細かいこと考えすぎて手を動かせないより、諦めていいことはすぱっと諦めて作ってみたほうがいい
- 可能なら後から追加すればいいのだから（先の仮決めのように）
- なんとなくダメに見える問題は許容する（１とか２ばかりの４マス面とか）
- 別解はなるべくなくす（検算機能は別の話）
- issueとして検討課題にしておくといい

# できた

- これをこうしてこうじゃ。ってポケモンは世代じゃないので元ネタよく知らないんだけど
- 頭の体操だと思って読者のみんなもぜひ考えてみて欲しい

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/44420/4022757e-48ac-d6bd-5278-a14b0cd3ef57.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/44420/50dfa57c-fa01-60e3-690a-65d9b17bc849.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/44420/589265ed-159c-4499-ec5b-7b74f8fc87ed.png)

※画面はスプレッドシートに貼った合成です。実際解けるのでやってみて。

# おわりに

- ロジックを考える癖はあって困らないので時々がんばろう
- TTP（徹底的にパクる）ってやつ、あんまり好きじゃないんだけど、個人の頭の体操なら許されるかなって

# 改善依頼

- （別の、あなたが好きな）このゲームのロジック考えたぜ、とか
- 仕様検討における注意点とか
- パズルゲームの検算（実際検算やってない）で、別解がないことを保証する方法ってないですかね？（人力しか経験がなくて。。。ｗ）
    - 自動回答するロジックを書いたらそのロジックに依らない解法で解けるケースがぬける？
StrongSwan IKEv2 セットアップ時の躓いたところ

この記事は、 [大阪工業大学 Advent Calendar 2023](https://adventar.org/calendars/9108)14日目の記事です。
## はじめに
<br>
私はStrongswanを利用したIKEv2のVPNを構築しました。<br>
その過程で躓いたことを以下にまとめました。<br>
少しでも参考になれば幸いです。<br>

## 環境
```text
NAT:Aterm製家庭用ルーター
回線:フレッツ光VDSL
基盤サーバ:Proxmox VE 8.0.4
仮想マシンスペック以下
CPU:x86-64-v3
Memory:2.00GB
BIOS:SeaBIOS
Machine:i440fx
DISK:scsi-32GB-SSD=1
NetworkDevice:virtio
```
私の環境では、仮想マシンにもルータ配下と同じIPを振っているため、NAT下にNATがいるような状況ではないことに留意してください。
## システム構成図
一般的にVPNのみを利用する場合は以下のような構造となっています。<br>
```mermaid
flowchart 
  global -->NAT
  NAT --> VPN 
```

## 証明書関係の問題
<br>
さて、本題に入りますがここでは証明書関係のエラーについて述べます。<br>
省略しますが、let's encryptにて証明書を発行し、cloudflareでDDNSを行っている環境です<br>
例えば以下のようなシステム構成で証明書更新を行って、証明書を渡している場合、うまくいかない場合に動作がおかしくなります。<br>

```mermaid
flowchart 
  let'sencrypt --> LB[Nginx LoadBalancer]
  LB --> VPN
```
具体的には以下のような現象が発生します。<br>
* Windows環境において「ネゴシエーションが失敗する」、「ポリシーマッチエラー」等々
* Android環境において接続するが、VPN(IPアドレス接続)以外に接続できない→DNSサーバが見つけられない
<br>
以上の現象を直すためには以下のような確認を行い修正しました。<br>

* ipsec.confの設定の確認
* 証明書が正しく転送されているかの確認
* 証明書の権限がStrongSwanやipsecが読めるか確認し(644だったはず)、指定するディレクトリが正しいか確認すること。
* StrongSwanのログの出力を有効化し、ネゴシエーションのどの部分で詰まっているのかを確認すること。
<br>
特に、private.pemのみ更新を忘れたりすると、接続できるが、ローカル以外に出れないなどの現象に悩まされるため注意<br>

以下余談<br>
```text
原因は言わずもがな、認証が不正であると判断されたり、暗号化された内容が複合化できないことが原因と考えられます。
また、Windows側では一度接続できていた場合、下手に接続できることがありこれも原因究明を難しくします。
逆に、Androidは疎通ができていないなどの初歩的なミス以外の場合、大抵接続できてしまうため、現象の全容を調べるのが難しかったです。
```

## VPN特有の問題
以上の問題ではなく、接続できるが、あるサイト(github等)やサービス(github Desktop等)にのみ接続が不安定であったり、接続できなかったりする場合MTU値が関係していると考えらえれます。
MTU値とはネットワークで一度に送信できる最大のデータサイズのことで、VPNを噛ますことでVPN分のデータが増えるます。
その結果、データが最大値を超え、帰ってこない状況になることもあります。
一般的に対応を行っているサイトでは再送要求に伴いMTU値の変更通知を送るらしいのだが、返してくれないサイトやサービスも存在します。
そのため、VPNサーバ側で対応を行わなければなりません。
MTU値変更と検索をかければ情報が出てくるがUbuntu+iptableでの設定例を以下に示します。
```config
sysctl.conf
#以下の設定はパケット分割の許可する設定
net.ipv4.ip_no_pmtu_disc1
```

```iptable
iptables -t mangle -A POSTROUTING -o eth0 \
 -p tcp -m tcp --tcp-flags SYN,RST SYN \
 -s 192.168.0.0/24 \
 -m tcpmss --mss 1361:1536 \
 -j TCPMSS --set-mss 1360
```
上記の設定は、eth0のインターフェースを通じて送信されるTCPパケットの1361~1536の大きさのパケットを1360の大きさに変更する設定です。
これは、TCPのみでSYN,RSTとSYNのフラグを持つパケットに適応していて、-sオプションでVPNで振られるIPのみに限定しています。

## おわりに
上記以外にも自身でセットアップを行うと様々な問題が起こります。
bing AI や GPTを利用してログを読むことである程度の回答を得ることはできますので、あなた自身が抱えている問題が解決することを願っています。
laravel10 tinkerを使用してデータを挿入してみる

# 対象
laravel10 初学者

## 前提条件
Userテーブル作成済みであること。

## tinkerの入り方

```bash
> php artisan tinker
```

## Userテーブルの挿入方法

```bash
> $model = new User();
> $model->name = 'Admin';
> $model->email = 'admin@example.com';
> $model->password = Hash::make('admin');

$model->save();
```

※ユーザー追加ごとに以下を実行する。
$model = new User();


以上です。

## 募集
Laravelで便利比較的カスタマイズしやすいscaffoldできるコンポートネントがあれば教えてください。
Eclipse+Tomcat サーブレットのみ404エラー

Tomcatは[起動済み、同期済み]、htmlやjspは問題なく表示される状態で、サーブレットのみ404エラー発生。原因は、Tomcatのバージョンとコンテキストディレクトリー、サーバー・ランタイム環境の設定が抜けていたためと思われます。以下に解決までに実施した事項を記載します。
![スクリーンショット 2023-11-12 013419.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1462637/d36fae92-8eab-87a9-7e54-bd947170f3c0.png)


ヘルプ(H)>Eclipseマーケットプレース(M)を開き、"Tomcat"で検索。Eclipse Tomcat Plugin 9.1.4をインストール。

ウィンドウ(W)>設定(P)を開き、Tomcatの欄でTomcatバージョンとコンテキスト・ディレクトリーを現在利用しているものに変更する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1462637/04da87d1-e6a9-f118-dce4-8b2649e93ba8.png)


サーバー>サーバー・ランタイム環境の欄で利用中の環境を選択し、右側の追加(A)をクリック。
新規サーバー・ランタイム環境で"Apache Tomcat v8.5" を選択し、次へ(N)をクリック。
TomcatサーバーでTomcatのインストール・ディレクトリ(D)の参照(R)から正しいディレクトリを選択、JRE(J)で使用しているバージョンのJAVAを選択し、完了。

以上を実施した結果、正常にサーブレットが表示されるようになりました。

参考：
TomcatがサポートしているJavaのバージョンが以下のサイトに載っています。
https://tomcat.apache.org/whichversion.html
毎日使うアプリを深掘りしよう

この記事は [一分で読める小ネタのカレンダー | Advent Calendar 2023 - Qiita](https://qiita.com/advent-calendar/2023/raki) の６日目

# 深掘り

- 毎日使うアプリはありますか
- デイリーボーナスのために惰性でゲームやってたりする？
- アプリ開発者もそうじゃない人も、１つのアプリを起点に改善を考える癖を作るといい、という話
- エンジニアってだいたい頭使うのは嫌いじゃないはずだと思っているので、クロスワードや数独とか、パズル系は深掘りのネタとして楽しいんじゃないか
- Trello や my-til にネタを溜め込んでいくといつか役に立つよ

本文はここまで。以下は時間外ｗ
まずは自分で考えるの大事

# 視点（の例）

## デザイン（UIを含む）

- 見た目はやる気に影響が出る
- 流行の流れ、過去にどんなデザインがあって、今の流行りはどんなもので、次にどんなデザインが流行るか
- 自分の好み
- テーマは（ライト＆ダークはもちろん、好みの配色に）変更できるか
- ウィンドウの構成、ボトムタブバー（iPhoneだと今はないほうが珍しいね）以外のUIは見やすいか、わかりやすいか、よいとされる構成はどうか
- そのアプリはユーザーフレンドリーか、そう思わないならそれはなぜか

## 機能

- あって欲しいのにないものは何か
- なくていいものが目立つ位置、邪魔な位置にないか
- 自分はあまり使わない機能がそこにあるのはなぜか
- 変えるとしたらどこに置くか
- なぜ期待したとおりになっていないのか（提供側の思考とユーザ側の思考の差異がどこからくるか）

## バグとか不具合

- 見つけたら作者に教えてあげようねｗ
- なぜそのバグが起きるのか
- 回避方法は
- プログラムのロジックは？（リストを生成するタイプのアプリにページ遷移後に同じリストが追加されちゃうとかあるあるだからね）

## 追加するなら

- ゆるいパズルゲームをやっていると、タイムアタックしたくなるしランキングがあるといいかもと思う
- 複数回の失敗を許容するルールのパズルの場合、失敗を許容しないサドンデスでどこまでクリアできるかとか
- 定期的なイベントのルーティングはどうなっているか（一般的なスマホゲーなら３タイプくらいのイベントをルーチンで回してると思う）
- 新しいイベントを考えるなら

## 変更するなら

- ボタン等のサイズ（レギュレーション守ってるのかよってアプリはままあるよね）
- ビジネス面から変えたほうがよくない？みたいなもの
- 自分で設計するなら（ビジネスロジックは変更なしで）どうするか

## 削除するなら

- 稀によく見る設計ミスっていうか、期待された使い方をしてない時に無駄に貯まるポイント類
- 必要ない（と感じる）ものをメインビューに配置しているとか
- 画面上で全く押さないボタンはないか

# 改善提案

- こういうアプリを毎日やってるぜ、とか紹介しあうのいいかもしれないね
- このアプリのこういうところを変えて欲しいんだよ、みたいなやつは作者さんに連絡してあげるか、自分のブログ等でやったほうがいいかも
- 各視点についてこれも書けよとかあったら編集リクエストください
Ubuntu Desktopでbluetooth機器が認識されないときの対処

# Ubuntuでbluetooth機器を接続する
1. 右上の電源アイコン -> bluetooth オン(もしくはオフ) -> bluetoothの設定
![bluetooth場所.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3618505/0bb221f6-97c1-493b-2ddf-0a70c66351cb.png)
1. Bluetooth接続画面が表示される
![bluetooth読み込み.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3618505/6e2d4fdb-ee8b-d246-a5f0-7147a8d25431.png)
1. Bluetooth機器側でペアリングモードにする
1. デバイスに表示されたら接続して終了 - 完 -
1. ペアリングにしてもデバイスに対象の機器が表示されない場合は以下手順に進む

# bluetooth機器を認識できるようにする
1. `/etc/bluetooth/main.conf`をviなどで開く(要sudo)
    * 開く前にコピーを取っておくのを推奨
1. `ControllerMode = dual`を`ControllerMode = bredr`に変更
1. 一番下に以下記述を追記して保存、ファイルを閉じる
    ```conf:main.conf
    autoEnable=true
    Disable=headset
    ```
1. 以下コマンドを実行してbluetoothを再起動
    ```bash
    sudo service bluetooth restart
    ```
1. 再度bluetoothの設定画面を開いてbluetooth機器側をペアリングモードにすると今度は表示されているはず
1. ペアリングして動作したら成功。

# bluetoothイヤホンが音割れする場合
1. `/etc/bluetooth/main.conf`を再度開く
1. `ControllerMode = bredr`を`ControllerMode = dual`に戻して保存、ファイルを閉じる
1. 以下コマンドを実行してbluetoothを再起動
    ```bash
    sudo service bluetooth restart
    ```
1. bluetoothの接続設定画面を開いてイヤホンの機器名をクリック、音の設定ボタンを押す
1. 「出力」項目の「設定」から「ハイファイ設定(A2DPシンク)」を選択する
    * Handsfree Head Free(HFP)の設定は音質が悪い
1. 音割れがなくなりだいぶマシになっているはず
LaravelとMVC

Laravelは、MVCアーキテクチャを使用して、開発者が素早くアプリケーションを構築するのを助ける、人気のPHPフレームワークです。

この記事では、LaravelでのMVCの実装をする際の特徴をまとめていきます。（個人の感想を含む）

## MVCとは何か？
　MVC は、アプリケーションを3つの主要なコンポーネントに分けることで、開発を合理化する設計のパターンです。

モデル（Model）: データを扱います。
ビュー（View）: ユーザーに表示される部分です。
コントローラ（Controller）: ユーザーの入力とシステムの出力を繋ぎます。

## laravelの特徴
```
・Eloquent ORM: 
データベースの扱うシンプルで読みやすいPHPコードで実行できるようにします。

・Bladeテンプレートエンジン: 
　効率的なビューの作成を可能にし、@foreach や @if といったディレクティブを使ってコードを整理できます。

・Artisanコマンドラインツール: 
開発プロセスを自動化し、煩雑なタスクを簡単にします。
```

確かに、この辺りは扱っていて、効率的なプロセスになっていると実感しました。

Laravelを使用することで、MVCの利点を生かし、速く、効率的にアプリケーションを構築できますね。ライブラリについても随時書いていきたいと思います。
【Python】pathlib.Pathを使ってみる

# はじめに

タイトルの通りです。

今まで os モジュールの os.path を利用してファイルやディレクトリなどの操作をしていて不自由は感じていませんでした。が、今回、Windowsで動かしている方のコードを動かそうとしたとき、ファイルが "¥hooo\\aaa.txt"みたいに書かれていて、自分のlinux 環境では動かせませんでした。そこで、どちらでも使えるはずのpathlib.Path を調べてみよう、ということになりました。

# 動作確認

## ファイルやディレクトリの参照

今までstrとして扱っていたファイルやディレクトリのパスの情報

```python
p:str = "./a/b"
```

を

```python
from pathlib import Path
p = Path('.','a','b')
```

とします。

```python:sample.py
from pathlib import Path

p = Path(__file__).parent
print('p=', p, ' type(p)=', type(p))
print('p.name=', p.name)
print('p.is_dir=', p.is_dir(), ' p.is_file=', p.is_file())
print('---')

p2 = p/Path('hoge')/Path('foo')
p2 = Path('hoge','foo')
print('p2=', str(p2))
print('p2.name=', p2.name)
print('p2.parts=', p2.parts)
print('p2.exsits=', p2.exists())
```

を実行すると下記の出力を得ました。

```bash
$ python sample.py
p= /home/x77/my_tips/testdir  type(p)= <class 'pathlib.PosixPath'>
p.name= testdir
p.is_dir= True  p.is_file= False
---
p2= hoge/foo
p2.name= foo
p2.parts= ('hoge', 'foo')
p2.exsits= False
```

## ディレクトリ内のファイル

今のフォルダ構成。

```shell
$ tree .
.
├── a
│   └── b
│       ├── c
│       │   ├── 001
│       │   ├── 002
│       │   └── 003
│       └── xyz.py
└── sample.py
```

で、以下を実行してみます。

```python:sample2.py
from pathlib import Path
p = Path(__file__).parent
print('p.iterdir()')
for child in p.iterdir():
    print(child)
print('')
print('glob("**/*.py")')
for f in p.glob("**/*.py"):
    print(f)
print('')
print('glob("**/*")')
for f in p.glob("**/*"):
    print(f)
```

出力は下記の通り。itedir()では直下のディレクトリにあるファイルとディレクトリを取得できるが、再帰的に下には下りて行かない。glob を使えば、拡張子でフィルタをかけて、再帰的に取得できました。

```
$ python sample2.py
p.iterdir()
/home/x77/my_tips/testdir/a
/home/x77/my_tips/testdir/sample2.py
/home/x77/my_tips/testdir/sample.py

glob("**/*.py")
/home/x77/my_tips/testdir/sample2.py
/home/x77/my_tips/testdir/sample.py
/home/x77/my_tips/testdir/a/b/xyz.py

glob("**/*")
/home/x77/my_tips/testdir/a
/home/x77/my_tips/testdir/sample2.py
/home/x77/my_tips/testdir/sample.py
/home/x77/my_tips/testdir/a/b
/home/x77/my_tips/testdir/a/b/xyz.py
/home/x77/my_tips/testdir/a/b/c
/home/x77/my_tips/testdir/a/b/c/001
/home/x77/my_tips/testdir/a/b/c/003
/home/x77/my_tips/testdir/a/b/c/002
```

## CUIコマンドの引数として

python で引数の解析に argparse を利用することがあるそうです。このとき、ディレクトリ名を引数にするとき、type に Path を利用するのが良さそうです。

何故ファイルのパスには使うと言わないのかというと、それはargparse にすでに argparse.FileType というオブジェクトがあるからです。が、自分は使い方が分かっていないので、Path を使っていたりします。下記が解説ですが。

https://docs.python.org/3/library/argparse.html#filetype-objects

## os のモジュールとの対応付け

ドキュメントを読むと分かるのですが、WindowsとLinux/Mac(Posix準拠)の相互変換もサポートしながら、全体としてPathの参照の違いを吸収できるように設計されています。さらに、socket や mount point など、OSと関係するところもサポートしてくれています。

一方それらの機能は、os モジュールでサポートもされています。対応表は下記で参照できます。

https://docs.python.org/3/library/pathlib.html#correspondence-to-tools-in-the-os-module

# まとめ

雑感ですが、世の中os モジュールですませている人もまだまだ多く、Linux/Macで完結している場合どうしてもPath を使うというインセンティブが働かないだろうと思いました。（自分がそうだった。）いろいろな機能がありますが、必要に応じてこちらで書いていこうと思います。

とりあえず pathlib.Path について必要だったことをメモっておきました。今週も何とか生き延びることができました。神様皆様ありがとうございます。
(2023/11/11)
[Vue]vue-routerでルート定義の洗い替え

# 概要

* ルート定義が複数存在し、ユーザを切り替えた際にルート定義を入れ替えたい場合がある。（例：ログインユーザの権限に応じたルートを設定する）
* にもかかわらず、vue-router(v4)には、ルートを一括で入れ替える仕組みが存在しない。
    * 以前のバージョンでは以下の方法で入れ替えができたようだが、現在（v4以降）では、機能しなくなっていた。

```javascript

// 変更するルート定義
const createRouter = () => new Router({
  ...
  routes: [...]
})

export function resetRouter () {
  const newRouter = createRouter()
  // matcherを入れ替えることで、ルート定義の洗い替えができた。
  // →v4以降は機能しない！！
  router.matcher = newRouter.matcher
}

```

※以下の引用

https://github.com/vuejs/vue-router/issues/1234#issuecomment-357941465


# 対応方法

* 1件づつ`removeRoute`でルートを消して、1件づつ`addRouter`で追加する。
* 愚直だが、現状はこの方法しかなさそう。（`router`自体を入れ替える等しても動的にルートは変更できなかった）

```javascript
// 変更するルート定義
const createRouter = () => new Router({
  ...
  routes: [...]
})

export function resetRouter() {
  // getRoutersで定義済みのルートを全て取得し、
  // nameをキーにルートを1件づつ全て削除する。
  router.getRoutes().forEach((route) => {
    router.removeRoute(route.name)
  })
  
  const newRouter = createRouter()
  // 変更後のルート情報を1件づつ追加する
  newRouter.getRoutes().forEach((route) => router.addRoute(route))
}
```

# 補足 - ルートのname属性は必須（Symbolで定義）
* `removeRoute`は、`name`をキーに削除するため、全てのルートに`name`属性が必要となる
* `name`を使用していないルートに関しては新たに設定が必要となる。影響がないよう、一意性を担保できる`Symbol`で定義すると良い。

```javascript
export const sampleRoutes: RouteRecordRaw[] = [
  {
      path: '/login',
       // Symbolでnameを追加
      name: Symbol('login'),
      component: () => import(/* webpackChunkName: "login" */ '@/views/login/index.vue'),
      meta: {...}
  },
  ...
]
```

# 終わりに

* `matcher`のハックが使えなくなったので、1件づつの入れ替えをしたが、`name`属性を設定していないルートがあったので、消せなくて困った。
    * 幸いSymbol属性が使えたので影響は少なかったが、後からの設定は面倒だったので、`name`属性は必須にしてもいいのではないか。
* または、ルート定義の洗い替えはあると思うので、一括で消せるAPIが公式から提供されてほしい。
Font Awesomeのicon使用時の表示問題を解消する方法

### CDNのバージョンとiconのバージョンが異なることが原因。

【失敗時】
![スクリーンショット 2023-11-11 23.34.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3478895/bd1411da-f833-a3ba-17e7-ee02dc177cd6.png)

【成功時】
![スクリーンショット 2023-11-11 22.40.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3478895/77c62ba7-72cc-dbb0-c446-e1131c5cda8b.png)


【コーディング詳細】
・font awesome：ver5.11.2を使用

【原因詳細】
font awesomeには、さまざまなバージョンがあります。
その中で私は、font awesomeのver5.11.2を使用して開発しています。
(2023/11/11時点では、ver6.4.2が最新)

バージョンが異なることで、下記２つに変化が出ます。
```
1.CSS CDNリンクのバージョン
2.iconのiタグのclass名
```

### 1.CSS CDNのリンク
（私の場合は、このリンクは正しかったです。）

ちなみにCDNのリンクは、fontawesome pro（有料）でないと使えません。
リンクとしては、下記のようなリンクをheadタグに書きます。
```
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.11.2/css/all.css">
```

### 2.iconのiタグのclass名
（これを直したら、正しく表示されました）

fontawesomeは、バージョンによってclass名が異なります。

fontawesomeでは、バージョンによって使用できるiconが異なるため
ユーザは自分でバージョンを選択することができます。
![スクリーンショット 2023-11-12 0.28.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3478895/491d6ea6-14a2-6365-b9ec-f6dde96decce.png)

表示のバージョンで、barアイコンの例にすると
class名は下記のような違いがあります。
・ver6.4.2
```
<i class="fa-solid fa-bars"></i>
```
・ver5.15.4
```
<i class="fas fa-bars"></i>
```

書き方が少し異なるのがわかるかと思います。


【結論】
私の場合、CDNのバージョンに合うiconのバージョンを選択して
iタグをコピーしなかったことで、正しく表示されませんでした。

font awesaomeを使用しているのに
正しくiconが表示されず困っている方は
上記２つを確認してみてください。
[GAS] スプレッドシートで複数行を高速に削除する


# TL;DR
- 一行づつ削除すると、非常に遅い
- `deleteRows` は、隣り合う列しか削除できないので、ソートしてから一括削除する
- 単なるソートで対応できない場合は、作業用の列を一時的に追加する

# よくある方法

ググると、逆順で走査して、`deleteRow` で1行づつ削除する方法が出てきます。
ただし、この方法は非常に遅いです。[^1]
[^1]:ちょっと大きなデータだとタイムアウトするレベルで遅い。

# 特定要素の行を削除する

`deleteRows` を使えば複数行を削除できますが、
これは、削除開始行と削除行数を指定する形になっており、
並んだ行しか削除することができません。

飛び地になったものを削除するにはどうすればいいか？
飛び地でなくすれば良いのです。

1列目が Foo の物を消すなら、
1. 1列目をソート
1. 1列目の内容を取得
1. Fooの開始行と個数を確認
1. `deleteRows` で削除

とすれば良い訳です。

# 特定条件の行を削除する

1列目に Foo を含む行を削除するにはどうしたら良いでしょう？
単にソートしただけでは、対象の行が固まってくれません。

作業用の列を一時的に追加し、削除対象の行に印を付けましょう。
ソートで対象の行が固まる様にすれば良い訳です。

1. 作業用の列を追加
1. 作業用の列に、削除対象なら0を,削除対象で無いなら1 をセット
1. ソートして、0が入っている行を削除
1. 作業用の列を削除
【Flutter】え、まだfvmをCUIでやってんの？草wwwwwww

事前説明
fmvとは
Flutter SDK versionを管理するものである。

https://fvm.app/

## まだその次元にいるってわけ？
環境構築で、flutterのバージョンを云々だとか、そーゆーのもう辞めましょうよ、、
Z世代はGUIで解決させたがるんです。
gitを使うにもSourceTreeを使うし、fvmもコマンド打ってやるのなんかイヤなんですよ！！！

## モテるFlutterエンジニアはこれを使っている！
fvmの公式サイトの一番下になんかあるやろ？
![スクリーンショット 2023-11-12 0.16.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/633771/edb0f9d5-f904-e599-982d-9497d2342e53.png)

### fvmがGUIで操作できる！しかも純正！！
その名も、、「Sidekick for Flutter」

https://github.com/fluttertools/sidekick#sidekick-for-flutter

こいつは一味どころか三味くらい違うゼェ、、、、、

## Sidekick、ほんとにいいのか？
Z世代のワイは構築済みの環境を壊したくないので、セッティング済みのスクショをぺたり。
![スクリーンショット 2023-11-12 0.21.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/633771/adae058a-6923-c9a9-b8ca-ee918adc3012.png)

なんだこれは！！！
一つずつ、機能を説明しよう
### インストール
検索タブの中にリリースされたsdkバージョンがずらっと並んでいるな
![スクリーンショット 2023-11-12 0.22.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/633771/8d5f31c0-49c1-3def-2631-956806c388fa.png)

下矢印ボタンを押すだけでインストールが始まるぞ！！！
なんて簡単なんだ！！！！
![スクリーンショット 2023-11-12 0.23.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/633771/5deeebfe-cd63-a050-79da-bd8a808f13a1.png)

### globalに設定
さっきの画面からわずか3タップでグローバルに設定できてしまう、、
こんなに簡単なことがあるのか？
![スクリーンショット 2023-11-12 0.23.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/633771/204c0c74-af48-6949-0554-4c95e353ca74.png)

### プロジェクトで有効にする
プロジェクトの追加でFlutterのプロジェクトをこのアプリに紐付けよう。
![スクリーンショット 2023-11-12 0.25.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/633771/fbe6ff21-f10d-16eb-1c40-ca62957bb11f.png)

そこからわずか2タップ！！！
これでFlutter Widgetのプロジェクトで使うFlutter SDKのバージョンが変わった！！
![スクリーンショット 2023-11-12 0.25.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/633771/395e2f18-46ec-be5f-23a9-9c634efabcdb.png)

## 最後に
ただ、楽がしたいんです。業界みんなで楽しませんか？
便利な情報を主観で発信しています。今回のfvmは多くの人に知って欲しいのでキータに上げましたが、そんなに要らない情報は個人ブログで書いています。
応援よろしく！(仕事募集中！)

https://sogablog.net/
【イベント参加ログ】「次はこれでしょ！eBPF」に参加して

# 参加したイベント
【Oracle Cloud Hangout Cafe シーズン７】「次はこれでしょ！eBPF」にオンラインで参加しました。

https://ochacafe.connpass.com/event/295514/

https://www.youtube.com/watch?v=9bCNg7Qrqn4

その中で感じたこと等を記録として記したものになります。
自分用のメモみたな感じです。

# 【LTセッション】OCHa Cafe Season7をふりかえって
[中川　聡也氏](https://twitter.com/xiombatsg)のLTセッション。

https://speakerdeck.com/xiombatsg/ocha-cafe-season7wohurikaetute?slide=6

OchaCafeの良いところは「オープン／デファクトスタンダードを基準に選定している」と仰っていて、すごくそうだなと思った。

**OCHa Cafeの楽しみ方で紹介されていた参加ログを残すというのを私もやってみようと思って今この記事を書いています。**

# 【セッション】次はこれしょ！eBPF
[市川　豊氏](https://twitter.com/cyberblack28)（@cyberblack28）のセッション。

https://speakerdeck.com/oracle4engineer/ebpf-introduction

https://github.com/oracle-japan/ochacafe-s7-6

eBPFが何かというところからデモまであって興味深い内容でした。
eBPF事情では、最近の動向とこれまで主流であったIstio-envoyのサイドカー構成との比較が紹介された
eBPF利用としてCilliumが紹介された
サイドカー構成と比べて、各Podにenvoyプロキシが不要になるためリソースを効率的に利用できる
でもサイドカーにもセキュリティ境界が明確など、優位性があるのでこれからも続くのでは。


### eBPFとは
- Linuxカーネルのソースコードの変更やモジュール追加をすることなく、Linuxカーネル内部でプログラムを実行できる技術
- イメージはJavaScriptがHTMLに対して行うような外から作用させる感じらしい
- Linuxカーネルサブセットとして、Linuxカーネル拡張や制御を実行できるプログラム
- カーネルモジュールプログラミングに比べてハードルが低い
- C言語で開発してClangコンパイラを利用してbytecodeプログラムを生成する
- bytecodeをLinuxカーネルにロード
- ロードされたbytecodeをverifierで検証してJIT Compillerで書くCPUの命令に合わせてbytecodeを変換
- bytecodeの実行は、フックからイベントドリブンに呼び出されて実行
【JavaScript】cookieのkeyを指定して値を取得する方法

以下のような関数を使用してcookieのkeyを引数に渡して実行すると指定したcookieの値を取得できます

```ts
function getCookieValue(key: string) {
  const cookies = document.cookie.split(';')
  const foundCookie = cookies.find(
    (cookie) => cookie.split('=')[0].trim() === key.trim()
  )
  if (foundCookie) {
    const cookieValue = decodeURIComponent(foundCookie.split('=')[1])
    return cookieValue
  }
  return ''
}

getCookieValue('sample_cookie')
```
dotfiles リポジトリのすゝめ

この記事は [一分で読める小ネタのカレンダー | Advent Calendar 2023 - Qiita](https://qiita.com/advent-calendar/2023/raki) の５日目

# dotfiles

- linux 等では各自のアカウントでシェルが選べ、自由に設定することができる
- その設定をリポジトリ管理することで、複数の環境でも同様の操作ができるようになる
- という目的で til 同様に昔からエンジニアあるあるのリポジトリ
- 日記と違ってプライベートな内容は書かないと思うので、パブリックにしておくとよい（親切な誰かが改善を教えてくれるかもしれない
- 使用しているサーバや、シェルの種類によって使い分ける必要があるが、それらのやり方、組み合わせを考えるのもエンジニアの基礎教養として楽しもう
- というわけで github に dotfiles リポジトリを作る
- 参考（にはならないけど） [officel/dotfiles: my public dot files](https://github.com/officel/dotfiles)
- 検索してみるといい [Repository search results](https://github.com/search?q=dotfiles&type=repositories)

本文はここまで。以下は時間外ｗ

# 検討されたし

## 管理方法あれこれ

- ファイルをそのまま置いておいて、clone 後に手でコピーやシンボリックリンクをはって使う（簡単）
- `make` などのコマンドを使って自動化する（古くからある手法のひとつ。１コマンドで展開するとかいいね）
- `ansible`などで環境差も吸収する（どこまで要るかは議論の余地があるけど、Windows,Macの他に、ディストリビューションやシェル差も吸収できたりする）
- パッケージマネージャーもいろいろあるので、試してみるのもよい

## シェルの取捨選択

- Mac は今だと zsh がデフォルト
- Windows でだと WSL2 に Ubuntu を入れるとデフォルトは bash
- とはいえ今どきなら他のシェルも自由に選べると思うので、好きに選べばいい
- それに合わせて dotfiles の内容というか書き方が変わることに留意する
- （シェルコマンドには互換のないものもあるので工夫が必要になる）

# 管理対象あれこれ

## alias

- エンジニアの美徳のひとつに怠惰がある
- 業務で `terraform` を使うことが多いが、毎回毎回 `terraform plan` とか入力してられないので、alias しておく。`t` だ。
- よく使うコマンドを alias でショートカットできるようになると、作業速度があがって生産性もあがる（かもしれない）
- 初心者は既存のコマンドを別のコマンドで上書きしないように留意すること

## ツールの設定

- 人気のツールはたいてい設定ファイルを管理できるようになっている
- これらをパブリックにしておけば、設定の間違いや更新などについて教えてもらえる（かもしれない）

## ツールのインストール方法のメモなど

- 世の中は便利になったもので、一度インストールして設定してしまうと環境が変わらない限り長いこと同じものが使えたり、自動でアップデートできたりする
- で、環境を変えるタイミングの時に、これどこからインストールしたっけ？とか、どうやってインストールしたっけ？みたいなことがおきる
- そんな時に備えてメモを残しておくといい
- もちろん環境が変わるタイミングはツールを新しくするいい機会でもあるので、最新情報を探しに行くのもいいはず。それでも記録を残しておく癖を作ることはいいことのはずだ


# 改善依頼

- 既に dotfiles を管理している諸兄にはぜひリンクを貼って欲しい
GoF（Gang of Four）デザインパターン：抽象ファクトリ（Abstract Factory）

GoF（Gang of Four）デザインパターンは、ソフトウェア設計における一連の再利用可能なソリューションです。GoFデザインパターンは、エリック・ガンマ、リチャード・ヘルム、ラルフ・ジョンソン、ジョン・ブリシディスによって定義され、彼らの著書『デザインパターン要素再利用のためのソフトウェアのオブジェクト指向分析と設計』で紹介されました。以下に、GoFデザインパターンの23種類を示します。

1. デザインパターン: クリエーションパターン
   - 抽象ファクトリ（Abstract Factory）
   - ビルダ（Builder）
   - ファクトリメソッド（Factory Method）
   - プロトタイプ（Prototype）
   - シングルトン（Singleton）

2. デザインパターン: 構造パターン
   - アダプタ（Adapter）
   - ブリッジ（Bridge）
   - コンポジット（Composite）
   - デコレータ（Decorator）
   - ファサード（Facade）
   - フライウェイト（Flyweight）
   - プロキシ（Proxy）

3. デザインパターン: 振る舞いパターン
   - チェインオブレスポンシビリティ（Chain of Responsibility）
   - コマンド（Command）
   - イテレータ（Iterator）
   - メディエータ（Mediator）
   - メメント（Memento）
   - オブザーバ（Observer）
   - ステート（State）
   - ストラテジ（Strategy）
   - テンプレートメソッド（Template Method）
   - ビジター（Visitor）

これらのパターンは、異なるソフトウェア設計の問題を解決するためのガイドラインとして使用することができます。各パターンは、特定の問題領域に対して再利用可能なソリューションを提供し、コードの柔軟性、再利用性、保守性を向上させるのに役立ちます。ただし、すべての場合にこれらのパターンが最適であるわけではありません。問題の特性に合わせて適切なパターンを選択する必要があります。

# 抽象ファクトリパターンの別の例：UIコンポーネント

抽象ファクトリパターンを使って、プラットフォームに応じたスクロールバーを生成するシステムを実装する例を見てみましょう。

## ステップ 1: 抽象製品のインターフェースを定義

まず、スクロールバーの基本インターフェースを定義します。

```java
public interface ScrollBar {
    void scroll();
}
```

## ステップ 2: 具象製品を作成

次に、異なるスタイルのスクロールバーを具象クラスとして実装します。

```java
public class WindowsScrollBar implements ScrollBar {
    public void scroll() {
        System.out.println("Windowsスタイルのスクロールバーを動かす");
    }
}

public class MacOSScrollBar implements ScrollBar {
    public void scroll() {
        System.out.println("MacOSスタイルのスクロールバーを動かす");
    }
}
```

## ステップ 3: 抽象ファクトリのインターフェースを定義

スクロールバーを生成するファクトリのインターフェースを定義します。

```java
public interface GUIFactory {
    ScrollBar createScrollBar();
}
```

## ステップ 4: 具象ファクトリを作成

プラットフォームに応じた具体的なファクトリクラスを作成します。

```java
public class WindowsFactory implements GUIFactory {
    public ScrollBar createScrollBar() {
        return new WindowsScrollBar();
    }
}

public class MacOSFactory implements GUIFactory {
    public ScrollBar createScrollBar() {
        return new MacOSScrollBar();
    }
}
```

## ステップ 5: クライアントコードでファクトリを使用

クライアントコードでファクトリを利用して、プラットフォームに適したスクロールバーを生成します。

```java
public class Application {
    private ScrollBar scrollBar;

    public Application(GUIFactory factory) {
        scrollBar = factory.createScrollBar();
    }

    public void run() {
        scrollBar.scroll();
    }
}

// 実行例
public class Demo {
    public static void main(String[] args) {
        Application app = new Application(new WindowsFactory());
        app.run();
    }
}
```

この例では`Application`クラスは、具体的な`ScrollBar`の実装について何も知りません。`WindowsFactory`または`MacOSFactory`のどちらかが`Application`に渡され、適切な`ScrollBar`インスタンスが生成されます。

# まとめ

この例では、抽象ファクトリパターンを使用して異なるプラットフォーム向けのUIコンポーネントを生成する方法を示しました。クライアントコードは抽象ファクトリに依存しており、具体的なファクトリの実装には依存していません。これにより、クライアントコードの変更を行うことなく、新しいプラットフォーム向けのコンポーネントを容易に追加できます。
AtCoder初心者振り返りメモ ABC328

# ABC328の回
https://atcoder.jp/contests/abc327

ABC完。
D問題は、サンプルは解けましたがTLEを最後までACに出来ませんでした。

# A-問題
https://atcoder.jp/contests/abc328/tasks/abc328_a

**問題文**
N 問の問題が出題されるプログラミングコンテストがあります。 
i=1,2,…,N について、i 問目の配点はSiです。
配点が X 以下である問題すべての配点の合計を出力してください。

**ポイント**
難しいところはありませんでした。リストの得点のうち X以下を加算していく。

````Python:A.py
# 入力 
N,X = map(int,input().split())
S = list(map(int, input().split()))

sum = 0
for i in range(N):

    # リストに格納した得点のうち、X 以下をsum に加算する
    if S[i] <= X:
        sum += S[i]
        
print(sum)
````
# B-問題
https://atcoder.jp/contests/abc328/tasks/abc328_b

![328B.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547298/be089b34-f1e4-f1d0-4a57-0750bec34631.png)

**ポイント**
10/10はゾロ目ではありません。i月j日をループする過程で、「1/11」や「2/22」をカウントするのに工夫が必要でした。文字列にして、setに格納し要素数(len)が 1 の時にカウントして正解することができました。

````Python:B.py
# 入力 
N = int(input())
D = list(map(int, input().split()))

cnt = 0

for i in range(1, N + 1):
        R = D[i-1]
        for j in range(1, R + 1):

            # ○月○日の数字を文字列にて、setに格納する
            S = str(i) + str(j)
            ST = set()
            for c in S:
                ST.add(c)

            # setの要素数が、1ならゾロ目で　cntを1加算する
            if len(ST) == 1:
                cnt += 1

print(cnt)
````
# C-問題
https://atcoder.jp/contests/abc328/tasks/abc328_c

![328C.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547298/08108c82-0bcb-907b-7143-b6c4f4f67586.png)

**ポイント**
ループで回すだけだとTLE。高速化が必要でした。過去、C++で解いたことがあるインデックス位置の変化量を答えとする方針で、実装しました。

````Python:C.py
# 入力 
N, Q = map(int, input().split())
S = str(input())

# 例: mississippi 
# 事前に、隣り合う文字が同じ場合に　1加算する累積和を計算
sum = [0]
for i in range(1, N):
    p = (S[i - 1] == S[i])

    # 前後が一致したときにプラス１加算する　S[i - 1] == S[i]　　→　True が 1 / False は 0
    sum.append(sum[-1] + (S[i - 1] == S[i]))

# sum: [0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3]
# そのインデックスの場所とインデックスの場所の変化量が、隣合う文字の出現回数となる

# クエリ処理
for _ in range(Q):
    L, R = map(int, input().split())
    result = sum[R - 1] - sum[L - 1]
    print(result)
````
# D-問題
https://atcoder.jp/contests/abc328/tasks/abc328_d

![328D.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547298/077dcadb-40ee-e3ce-fd6c-a133132d5ab4.png)

**ポイント**
replace("ABC","") など、文字列を削除する方法では、TLEを回避することは出来ませんでした。
30分以上奮闘したのですが残念です。以下のコードだとACになります。

````Python:D.py
# 入力 
S = str(input())

ans = []
for c in S:

    # 先頭から1文字ずつ別の配列に格納する
    ans.append(c)

    # A,B,C が揃ったとき、配列から削除popする
    if ans[-3:] == ['A', 'B', 'C']:
        ans.pop()
        ans.pop()
        ans.pop()

print("".join(ans))
````
Let'sEncryptでワイルドカード証明書を発行する。

:::note warn
# この記事は読むな
[Certbot公式サイト](https://certbot.eff.org/)
インターネット上のCertbotに関する情報は古かったり誤りのあるものが多いため、公式サイトだけを読むことをおすすめする。
:::

# 環境
- Debian 11 Bullseye
- Cloudflare
- nginx

# Snapをインストールする
Snap経由でインストールすることが推奨されているため、snapをインストールする。以降の作業にはroot権限が必要となる。
[Snapインストール手順](https://snapcraft.io/docs/installing-snapd)
```bash
apt install snapd
snap install core
```

# Certbotをインストールする
```bash
snap install --classic certbot
ln -s /snap/bin/certbot /usr/bin/certbot
```

# DNSプラグインの導入
各種DNSサーバやサービスに対応したプラグインが提供されている。
[対応するDNSプロバイダ](https://eff-certbot.readthedocs.io/en/latest/using.html#dns-plugins)
```bash
snap set certbot trust-plugin-with-root=ok
snap install certbot-dns-cloudflare
```

# APIキーを取得する
CloudflareのMyProfileのAPI tokensから取得することができる。権限はゾーンDNSの編集権限があればOK
[https://dash.cloudflare.com/profile/api-tokens](https://dash.cloudflare.com/profile/api-tokens)

適当な場所に保存する。今回は、ホームディレクトリ直下に`.clodudflare`として保存した。
```bash
dns_cloudflare_api_token = 0123456789abcdef0123456789abcdef01234567
```


# 証明書を取得する
実際に証明書を取得する。ワイルドカードをつけたドメインと指定なしのドメインの両方を指定することをおすすめする。
```bash
certbot certonly --dns-cloudflare --dns-cloudflare-credentials .cloudflare -d *.example.com -d example.com -i nginx
```
UE5 C++乱数

### 経緯
ブループリントではRandomStreamというものが提供されているようですが、C++だとちょっと調べづらかったので備忘録
https://docs.unrealengine.com/5.3/ja/random-streams-in-unreal-engine/

### include
includeはこちらなのですが
```
#include "Math\RandomStream.h"
```

こちらをincludeしている場合は上記のincludeが含まれているので不要です
```
#include "UObject/NoExportTypes.h"
```

### 使い方

C++では```FRandomStream``` という構造体として存在しています
コンストラクタでSeed初期値を設定することができます
```
// seed初期値を0に設定
FRandomStream rand(0);
```
Seed値をランダムにすることもできます
```
rand.GenerateNewSeed();
```
ブループリントのドキュメントにもある通り、乱数生成はいくつかの方法があります
```
// int32型で Min <= num <= Max 範囲の乱数を生成
int32 RandRange( int32 Min, int32 Max ) const

// 0～1の範囲で乱数を生成
float GetFraction() const

// 0～MAXUINTの範囲で乱数を生成
uint32 GetUnsignedInt() const

```
お手軽に使えるので、細かいことを気にしないのであればこれを使ったほうがいいと思います
【iOS×Figma】CustomSFSymbolsをFigmaから自動生成するプラグインがえぐい

# はじめに
https://twitter.com/log5/status/1722934545329381694

HIG輪読会という勉強会で紹介されてたツールをlog5さんがツイートしてるのを見て知りました
使ってみたらめっちゃすごかったので記事にしておきます。

Figma初心者の私でも簡単に使えました！

以前、CustomSFSymbolを作ったのですが、めっちゃ大変だったのでこのツールは革命です

https://qiita.com/SNQ-2001/items/1bef9b55d449636720a2

個人開発者にとっては必須のツールだと思います。

# 使い方
### 1, 「SF Symbols Optimizer」をインストールする
https://www.figma.com/community/plugin/1265484574589944515/sf-symbols-optimizer

上記のリンクにはいります。

「Try it out」を選択して編集画面に行きます。
![スクリーンショット 2023-11-11 22.25.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/8908fe06-eb50-b311-6a29-be935e48f081.png)

### 2, SFSymbolsに変換したいものを作成する
私は適当に図形を使って人を作ってみました。
特別なことは何もしてなく、ただ図形を並べただけです。
![スクリーンショット 2023-11-11 22.22.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/3763c0f6-0d3c-6ec1-076f-692c2b2c4981.png)

並べた図形を全て選択します。
![スクリーンショット 2023-11-11 22.43.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/20bf8e2c-1a27-fd5c-6b3a-58f673295ba5.png)

「Group selection」を選択します
![スクリーンショット 2023-11-11 22.45.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/c9d20f87-bee3-360b-0f71-1b1f0d379f22.png)

「Frame」を作成してFrameを正方形に設定します
![スクリーンショット 2023-11-11 23.10.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/cf4d0632-fc59-dd4e-1a7d-84442c725286.png)

### 3, componentを作成する
「Group 1」と「Frame 1」が選択された状態で右クリックをして「Create component」を選択します
![スクリーンショット 2023-11-11 23.12.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/a4d24189-ea47-4e47-c659-5d24bd8f2a6b.png)

### 4, SFSymbolsを生成する
「Component 1」を右クリックして「Plugins」「SF Symbols Optimizer」を選択します。
![スクリーンショット 2023-11-11 23.14.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/fe77a775-afdf-2861-6e6f-59beaf6a5884.png)

「Scan」を選択します。
![スクリーンショット 2023-11-11 23.15.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/f09456bb-2c8c-3080-7b61-9d925e9d7876.png)

ダウンロードボタンを選択します。
![スクリーンショット 2023-11-11 23.16.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/6cbe6ebc-2316-9a0f-5f1c-2bbcd7c95fc7.png)

# おわり
SVGをいじることなくSFSymbolsに変換することができました！
今の所、複雑なものでも変換できてるのでめっちゃ使えます！

Xcodeに入れるとこんな感じになります！
ちゃんとFigmaのままですね！
![スクリーンショット 2023-11-11 23.19.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/12123ec5-176a-d9e5-a0ea-8ee9e992d4be.png)

# 参考記事
https://note.com/shingo2000/n/n62087f9485d4
[Swift] このコードは書かないでください！(代わりに幽霊型を使いましょう)

## 前提
https://www.youtube.com/watch?v=uAlBRGNycdw

上記Youtubeを日本語でざっくり解説します。
※あくまでも私のアウトプットなので間違っていたらご教授いただけますと幸いです。

## どのような内容か

以下は、コンパイルエラーが発生しないコードです。
```swift
import Foundation

struct Person {
    let id = UUID()
    let name: String
}

struct Location {
    let id = UUID()
    let coordinates: (Double, Double)
}

func handle(locations: [Location]) {
    let me = Person(name: "Test")
    let filtered = locations.filter { $0.id == me.id }
}
```

以下は、コンパイルエラーが発生するコードです。
```swift
import Foundation

struct ID<T>: Equatable {
    private let value = UUID()
}

struct Person {
    let id = ID<Self>()
    let name: String
}

struct Location {
    let id = ID<Self>()
    let coordinates: (Double, Double)
}

func handle(locations: [Location]) {
    let me = Person(name: "Test")
    let filtered = locations.filter { $0.id == me.id } // ここでエラー
} 
```
```
Cannot convert value of type 'ID<Person>' to expected argument type 'ID<Location>'
```

ここで伝えておきたいことは、今回の動画では __コンパイルエラーが発生しないことが問題(ダメ)だよ__ ってことだと思います。

### なぜコンパイルエラーが発生しないことに問題があるのか

以下は先程のコンパイルエラーが発生しないコードと同様
```swift
import Foundation

struct Person {
    let id = UUID()
    let name: String
}

struct Location {
    let id = UUID()
    let coordinates: (Double, Double)
}

func handle(locations: [Location]) {
    let me = Person(name: "Test")
    let filtered = locations.filter { $0.id == me.id } // ココに問題がある
}
```
 __Swiftでは、異なる型同士での比較はしてはいけません。__
 
 簡単に言うと、以下は比較できませんよね。
```swift
let int = 0
let string = "0"

if int == string { 

}
// 以下はエラー内容
// Binary operator '==' cannot be applied to operands of type 'Int' and 'String'
```

この決まりがある中で、コンパイルエラーにならなかったコードの以下の部分は、型が違うのにコンパイルエラーが出ていません。

```swift
{ $0.id == me.id } // $0は、Location型。 meは、Person型。

// なぜコンパイルエラーにならないのか
// コンパイルするときに$0とmeの型は違うけど、どちらのidもUUID型だから比較OKになってしまう。
```
これは予測できない動作を引き起こす可能性が出てきてしまうので、今回の動画ではこのコードは書かないでください！ってことになっている。と思われます！

だから、エラーが発生する方のコードのようにして、異なる型同士の比較をできないようにコンパイルエラーを出してくれるようにしよう！

といった感じです。

## 最後に

> エラーが発生する方のコードのようにして、異なる型同士の比較をできないようにコンパイルエラーを出してくれるようにしよう！

先程の引用で上記のようにコンパイルエラーを出すようにすることを幽霊型というらしい。

詳しくは以下を参考

https://qiita.com/taketo1024/items/71e3272211f08d7e0cde
開発の流れを自分なりに軽くまとめてみた

## はじめに
システム開発・新規サービス開発をする際の流れとか文書とかがごちゃごちゃになってしまったので、自分なりにこれでいいんじゃないって思ったものを頭の整理を兼ねてここにまとめておきます。
自分なりの解釈ですので悪しからず。

## 目次
[1. 要件定義](#1-要件定義)
[2. 基本設計(外部設計)](#2-基本設計外部設計)
[3. 詳細設計(内部設計)](#3-詳細設計内部設計)
[4. 開発](#4-開発)
[5. テスト](#5-テスト)
[6. まとめ](#6-まとめ)

## 1. 要件定義
ここでは顧客からヒアリングを行い、どんなものを作りたいのかを明確にして「要件定義書」としてまとめる。
社内主導で新規サービス開発をするなら「企画書」とも言えるかも。
とりあえずざっくりまとめると、こういうものを実現しますということをまとめるフェーズなので企画チーム主導で動く。

## 2. 基本設計（外部設計）
ここでは要件定義書をもとに、具体的にどのような画面でどうやってそれを実現するのかを「基本設計書」としてまとめる。
書く内容は以下のような内容。
- システム構成図
- 機能一覧
- 画面仕様
- DBテーブル定義

開発チーム主導で動くが、きちんと企画チームや顧客とここまでは整合をとることが大事。

## 3. 詳細設計（内部設計）
基本設計を受けて、内部的にどのようにコーディングをしていくかを「詳細設計書」としてまとめる。
書く内容は以下のような内容。
- コーディング規則
- バックエンドのAPI仕様
- シーケンス図
- クラス図

ここからは開発チームのみで整合をとる。

## 4. 開発
基本設計書や詳細設計をもとにコーディング。

## 5. テスト
作ったものがちゃんと動くかテストする。
テスト内容は以下の通り。
- コードレビュー：ルールに従ってプログラミングできているか
- 単体テスト：プログラム部品が単体でうまく動作するか
- 結合テスト：プログラム部品を組み合わせて問題なく動作するか
- システムテスト：全体として要件定義の要求を満たしているかどうか

## 6. まとめ
色々と文書にまとめないといけないことがあって、会社ごとにルールとかも違うと思うので大変ですね。なるべく負担になりすぎない程度でまとめるというのが大事なんじゃないかなと最近思うようになってきました。
AWS Glue Studio data previewsを試してみた

# 背景・目的
[Glue Studio Visual now supports interactive data previews](https://aws.amazon.com/jp/about-aws/whats-new/2023/11/aws-glue-studio-visual-interactive-data-previews/)が発表されました。

これにより、データソースの中身や途中経過ができるようになりましたので、試してみたいと思います。

# まとめ
「Data preview」をクリックすることにより、下記の確認ができます。
- データソースの確認
- 意図した方法で変換されているか確認


# 概要
[Using data previews in the visual job editor](https://docs.aws.amazon.com/ja_jp/glue/latest/dg/job-editor-features.html#data-previews)を元に整理します。

「Data preview」をクリックすることにより、下記の確認ができるようです。
- データソースの確認ができる。
- 意図した方法で変換されているか確認できる。

# 実践
## 事前準備
1. 下記のデータをS3にデータを用意します。
    ```
    {"id":1,"value":100}
    {"id":2,"value":200}
    {"id":3,"value":300}
    {"id":4,"value":400}
    {"id":5,"value":500}
    ```


## Data previewを試す
1. Glueの「ETL jobs」で「Visual ETL」をクリックします。

    <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/4674cff5-1243-dcef-deb9-668a703fbaa0.png"　 width=400 >

1. Data sourceにS3を指定します。
    <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/a70711a0-70cc-6883-506c-a271db0d27fd.png" width=400>

1. ①「IAMロールを指定」し、②「Start session」をクリックします。
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/4c31d6f2-cc48-1e07-7db5-a633aa0933fb.png)

1. 1分くらいすると表示されました。
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/e3500f48-f58b-d0a0-770a-1a99890cbcfe.png)

1. SQL QueryのTransformを定義します。
    <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/24edf723-0fdd-aca7-d7c1-b183146e3cf7.png" width=300>

1. SQL queryに下記を書きます。
    ```
    select count(id),sum(value) from myDataSource
    ```

1. 「Start session」をクリックします。表示されました。
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/5292b65d-e9c3-6fad-9090-5b4ea463907a.png)

1. TargetにS3を指定します。
    <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/79d06b08-b36d-8b17-5559-c6998868cb9e.png" width=300>

1. Target nodeはサポートされていないようです。
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/73ded370-35c8-2717-1663-387422a0acf9.png)



# 考察
今回、Data　Previewを試してみました。今まで動かしながらログ出力しながら、データの中身を確認していたましたが、これにより、動かさずとも途中経過がある程度確認できるようになり便利なりました。


# 参考

https://aws.amazon.com/jp/about-aws/whats-new/2023/11/aws-glue-studio-visual-interactive-data-previews/

https://docs.aws.amazon.com/ja_jp/glue/latest/dg/job-editor-features.html#schema-previews
Reactのバリデーション関数

# はじめに
とあるシステムで、予約フォームを作成することになりました。
フロントエンドはReactで作成しています。

今回はバリデーション関数についていくつか調べて実装したので、備忘も兼ねてまとめておきます。

# バリデーション関数とは
そもそもバリデーション関数ってなんぞや？
という話からです。

予約フォームを入力するとき、記入事項に漏れがあったりすると「入力必須の項目が漏れています」
などのアラート文が表示されるかと思います。
あれです。

バリデーション関数を実装することで、次のことができるようになります。
- データ整合性の統一
- 必須項目が漏れたままフォーム入力完了を防ぐ

# 具体的な実装方法
Reactでバリデーション関数を実装する場合、主に次の二つのイベントハンドラーを使って実装します。
イベントハンドラーの中でバリデーション関数を呼び出します。

- handleChange
- handleBlur

それぞれ具体的に見ていきましょう。

## handleChange
フォームの入力値が変更されたときに呼び出されるイベントハンドラーです。
ユーザが項目を入力するたびに、入力欄のバリデーションを確認してエラーハンドリングをしています。
つまり、文字を打つたびにエラーが画面に表示されます。

## handleBlur
入力フィールドからフォーカスが外れたときに呼び出されるイベントハンドラーです。
ユーザが入力を完了した後、次の項目に移った時にエラーが出力されます。

## サンプルコード
```React:nameForm
import React, { useState } from 'react';

const nameForm = () => {
  const [name, setName] = useState('');
  const [nameError, setNameError] = useState('');

  const validateName = () => {
    if (name.trim() === '') {
      setNameError('名前を入力してください');
    } else {
      setNameError('');
    }
  };

  const handleChange = (e) => {
    setName(e.target.value);
  };

  const handleBlur = () => {
    validateName();
  };

  return (
    <div>
      <label htmlFor="name">名前</label>
      <input
        type="text"
        id="name"
        value={name}
        onChange={handleChange}
        onBlur={handleBlur}
      />
      {nameError && <p style={{ color: 'red' }}>{nameError}</p>}
    </div>
  );
};

export default nameForm;
```