OmniAICreator/Qiita-1.07M huggingface dataset:
Rocky Linux 9 OS再インストール – さくらのVPS

さくらのVPS使っていますが、Rocky Linux 9 に再インストールした時の備忘録です。

OS再インストール
===
Rocky Linux 9 x86_64

スタートアップスクリプト
---
Setup and update（スタートアップスクリプト設定）の内容

- 初回起動時にパッケージ更新する。：更新する
- 日本語環境 ja_JP.UTF-8 に変更する：変更しない
- ホスト名を設定する。：www.example.com (自身のホスト名設定)
- キーボード配列のVM内部設定をUS配列に変更する。：US配列に変更する
- VM内部のファイアウォール機能(firewalld, ufw, nft のいずれか)を有効化する。：有効化しない
- ウェブ管理インタフェース Cockpit をインストールする。：インストールしない
- (対応している場合)IPv6 を有効化する。：有効化しない
- スワップ(swapfile)を作成する。：512MB
- パッケージ管理システム Snappy(snap) をインストールする。：インストールしない
- SELinux を有効化する。：有効化しない
- カーネルクラッシュダンプを有効化する。：有効化しない
- タイムゾーンを変更する。：変更しない Asia/Tokyo

以下ポイント

- 変更点
  - US配列に変更する （自 PCに合わせた）
  - スワップ(swapfile)を作成する。512M
- 最安プラン（512MB プラン）のため、余計なものはインストールしない。
  - 日本語環境 ja_JP.UTF-8
  - Cockpit
  - Snappy
- ファイアウォール機能は「パケットフィルタ機能」があるので、有効化しない。
- SELinux は使わない。（必要ない）

パケットフィルター設定
---
Webを追加しておく

ssh接続
===
```shell
ssh rocky@[ip address]
```

SSHホスト鍵が変更された時の対応
---
```shell
% ssh rocky@[ip address]
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that a host key has just been changed.
The fingerprint for the ED25519 key sent by the remote host is
SHA256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.
Please contact your system administrator.
Add correct host key in /Users/xxx/.ssh/known_hosts to get rid of this message.
Offending ECDSA key in /Users/xxx/.ssh/known_hosts:5
Host key for 111.111.111.111 has changed and you have requested strict checking.
Host key verification failed.
```

`ssh-keygen`コマンドで`known_hosts`ファイルからホスト鍵を削除する

```shell
ssh-keygen -R [ip address]
```

再度、ssh接続すると接続できます。

```shell
ssh rocky@[ip address]
```

まとめ
---
- Rocky LinuxとAlmaLinuxは似ているが、今回はRocky Linuxにした。
  - デフォルトの選択がRocky Linuxになっている。
  - 参考がRocky Linuxだった
Dockerユーザー必見！データ消失を防ぐマウント方法ガイド

Docker におけるマウントについて学んだので、自分なりにまとめてみました。

## そもそもなぜマウントが必要なのか？

**A. コンテナはホスト環境から独立したシステムを持ち、一度破棄すると内部のデータやファイルなどが失われるから**です。

コンテナの外部にマウントすることで、コンテナの一時的な破棄が問題ではなくなります。

そのため、コンテナ自体のアップデートが容易になったり、コンテナ間のデータ共有が可能になったりと様々なメリットがあります。

**コンテナでは、「実行するシステム」と「扱うデータ」は別に管理することが推奨されます。**

要するに、コンテナの外（Dockerホスト もしくは Docker Engine　※後述）でデータを保存しましょう、ということです。

## バインドマウントとボリュームマウント

マウントには大きく２つ種類があります。

### 1\. **バインドマウント**

Docker ホストにあらかじめディレクトリを作っておき、それをマウントする方法です。

**メリット**

- Docker ホスト上にあるファイルをコンテナから扱える
- 作業ディレクトリでの変更を即座に Docker コンテナから参照できるため、開発に便利

### 2\. **ボリュームマウント**

Docker ホストのディレクトリではなく、Docker Engine上で確保された領域をマウントする方法です。

**メリット**

- Docker Engine上で管理されるため、ボリュームの保存場所の物理的な位置を気にしなくていい
    - チームメンバーごとにDocker ホストのディレクトリ構成に差異があっても問題ない

- Docker コンテナが扱うデータをブラックボックスとして扱える
    - データベースのデータなど、Dcoker ホストから不用意にデータを書き換えたくない場面などに有効

:::note info
他に、tmpfs マウントも存在しますが、今回は割愛します。
気になる人は[Docker 公式ドキュメント](https://docs.docker.com/storage/#tmpfs)を参照してください。
:::


## ボリュームの作成とマウントの実行

まずボリュームを作成します。

```bash
docker volume create --name ボリューム名
```

作成したボリュームをマウントします。2種類のオプションがあります。
- -v オプション
```bash
-v マウント元:マウント先
```

- --mount オプション
```bash
--mount type=マウントの種類（bind or volume）,src=マウント元,dst=マウント先
```

## mount オプションが推奨される理由

--mount オプションは Docker バージョン17.06からサポートされました。

どちらを使っても結果は同じですが、以下の理由から --mount の方が推奨されています。

### 1\. マウントの種類（バインド or マウント）が明示されている
- -v オプションでは、マウント元が 「/」 から始まるとバインドマウント、そうでないときはボリュームマウントとしてマウントされます。これは非常に分かりにくく、間違える可能性が高いです

### 2\. ボリュームが存在しないときに新規作成されない
- -v オプションではその場でボリュームを作成することもできるが、タイプミスしたとき致命的な問題になりうる
- --mount オプションではボリュームが存在しないときは新規作成せず、エラーが発生する

## 参考

- さわって学ぶクラウドインフラ　docker基礎からのコンテナ構築

https://amzn.asia/d/1bYdP5A

- Docker&仮想サーバー完全入門　Webクリエイター＆エンジニアの作業がはかどる開発環境構築ガイド

https://amzn.asia/d/4LGMoLI

- Docker 公式ドキュメント

https://docs.docker.com/storage/
Firebase Admin SDK (Java) を使って FCM Push を Proxy 越え送信する場合の注意

# 概要

Firebase Admin SDK (Java) を使って FCM Push を Proxy サーバ越えで送信するときにドハマリしたので、注意点を記載します。

なお、Proxy 設定を行う手順は Firebase Admin SDK ドキュメントには一切記載がありません
(そのくらい書いといてよね >Google)

# FCM Push の送信手順(Proxyなし)

Proxyなしの場合の送信手順は [送信リクエストを許可する](https://firebase.google.com/docs/cloud-messaging/auth-server?lang=ja&hl=ja) に記載されています。

このうち重要なのは初期化部分で、以下のようなコードになります。

```java
// GoogleCredentials を生成
GoogleCredentials googleCredentials = GoogleCredentials
    .fromStream(new FileInputStream("service-account.json"))
    .build();

// FirebaseOptions を生成
FirebaseOptions options = FirebaseOptions.builder()
    .setCredentials(GoogleCredentials.getApplicationDefault())
    .build();

// FirebaseApp を初期化
FirebaseApp.initializeApp(options);        
```

# Proxy 越えが必要な場合

Proxy 越えが必要な場合は `HttpTransport` を Proxy 設定付きで作成する必要があります。
これを GoogleCredentials と FirebaseOptions の２箇所に設定する必要があります。

```java
// Proxy 設定付きの HttpTransport を生成
Proxy proxy = new Proxy(Proxy.Type.HTTP, 
    new InetSocketAddress("proxy.example.com", "8080"));
HttpTransport httpTransport = new NetHttpTransport.Builder()
    .setProxy(proxy)
    .build();

// GoogleCredentials を生成
GoogleCredentials googleCredentials = GoogleCredentials
    .fromStream(new FileInputStream("service-account.json"),
        () -> httpTransport) // #1
    .build();

// FirebaseOptions を生成
FirebaseOptions options = FirebaseOptions.builder()
    .setCredentials(GoogleCredentials.getApplicationDefault())
    .setHttpTransport(httpTransport) // #2
    .build();

// FirebaseApp を初期化
FirebaseApp.initializeApp(options);  
```

* GoogleCredentials
  * fromStream() の第2引数に `HttpTransport` の factory を設定する必要があります
* FirebaseOptions
  * setHttpTransport() で `HttpTransport` を指定する必要があります

これで Proxy 越えで FCM Push 送信ができるはずです。
EC2のWindowsでPythonが認識されない

# はじめに
こんにちは、ユーゴです。MacでPythonをexe化したく、Windowsの環境を用意するために、AWSのEC2でWindowsのサーバーを立てて、そこから色々とやっていました。その時に、Pythonをダウンロードしてもうまく認識されなかったので、解決策を紹介します。

# 環境
EC2側の環境は、以下の通りです。
|項目|詳細|
|:-:|:-:|
|インスタンスタイプ|t2.micro|
|プロセッサ|Intel(R) Xeon(R)|
|システムタイプ|64-bit operating system, x64-based processor|
|エディション|Windows Serer 2022 Datacenter|

# 課題
Pythonをインストールしたが、pythonコマンドが認識されない。

```bash
python -V
```

```
'python' is not recognized as an internal or external command,
operable program or batch file.
```

# 解決策
32-bit版を入れる。
システムタイプで64-bitと書いてあるが、なぜか64-bit版だと動かない。

![スクリーンショット 2024-05-17 13.46.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2607452/bdeedd6c-94ba-d39b-6185-9ac3267eef71.png)

![スクリーンショット 2024-05-17 13.46.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2607452/d0e7c2e8-9371-4a88-4c85-c887121d8070.png)

# まとめ
システムタイプで64-bitと書いてあるのに、64-bit版が動かないのは謎です。有識者の方、ぜひ教えてください。

このように、AWSに関係する知見から、UnityやGASなどについても紹介しています。
お役に立てましたら、いいね, LGTM, フォローなどよろしくお願いします！
あるリクエストがGoogle Chatから送られてきたかどうかを検証する

# リクエストがGoogle Chatから送られてきているのかを検証したい
Google Chatにbotをデプロイする場合、botのエンドポイントを設定する必要があります。HTTPエンドポイントを利用する場合、URLを知っている人は誰でもbotを利用できる可能性があります。なので、リクエストがGoogle Chatから送られてきているかを検証し、不正な利用ができないようにする必要があります。

# Bearer Tokenを使って検証を行う
Google Chatからの通知はheaderにBearer Tokenがついているため、このトークンを使って検証を行います。いかに検証を行うサンプルコードを示します。

```verify.py
from flask import Request
from google.auth.transport import requests
from google.oauth2 import id_token

# 以下2つは定数なので固定
PUBLIC_CERT_URL_PREFIX = 'https://www.googleapis.com/service_accounts/v1/metadata/x509/'
CHAT_ISSUER = 'chat@system.gserviceaccount.com'
CERT_URL = PUBLIC_CERT_URL_PREFIX + CHAT_ISSUER

# ここを自分のGCPの情報で置き換える
AUDIENCE = 'GoogleChatAPIを有効にしているプロジェクト番号'


def verify_request(request: Request) -> bool:
    """
    Bearer tokenを検証して、リクエストがGoogle Chatから送信されていることを確認する。

    Args:
        request (Request): Flask request object

    Returns:
        bool: True if the request is valid, False otherwise
    """
    auth_header = request.headers.get('Authorization')
    if not auth_header or not auth_header.startswith('Bearer '):
        return False

    bearer_token = auth_header.split(' ')[1]
    try:
        # Verify valid token, signed by CHAT_ISSUER, intended for a third party.
        token = id_token.verify_token(bearer_token, requests.Request(), AUDIENCE, CERT_URL)

        if token['iss'] != CHAT_ISSUER:
            return False
    except BaseException:
        return False

    return True
```

このコードではheaderから `Authorization` キーを取得し、その中のBearer Tokenを検証しています。検証が成功した場合は `True`、失敗した場合は `False` を返します。

# 参考
- [Google Chat からのリクエストを確認する](https://developers.google.com/workspace/chat/verify-requests-from-chat?hl=ja#python)
Python文法まとめ【for】

# はじめに
Pythonの基礎的なことをまとめています。
Google Colabで実行できます。
[Colabの使い方(Python.jp)](https://www.python.jp/train/experience/colab.html "Python.jp 入門講座")

# 繰り返し処理
forを利用した繰り返し処理です。
~~~Python:サンプル
for i in range(5):
  print('★', end='')
~~~
~~~:実行結果
★★★★★
~~~
`★`マークが5回表示されました。
※`end=''`は、改行をしないための記述です。
~~~
for 変数 in 範囲もしくはデータのまとまり:
  処理
~~~
が基本形です。

#### ifと組み合わせる
~~~Python:応用
for i in range(5):
  if i == 3:
    print('☆', end='')
  else:
    print('★', end='')
~~~
実行結果を見る前に、どういう表示になるか予想してみてください。

<details><summary>実行結果</summary>

~~~:実行結果
★★★☆★
~~~
</details>

予想通りでしたか？
次に、以下のコードを実行してみましょう。
~~~Python:サンプル
for i in range(5):
  print(i)
~~~
~~~:実行結果
0
1
2
3
4
~~~
ゼロからはじまり、5回`i`の値を表示しています。

# whileとforの違い
`while`も`for`も処理を繰り返す時に使います。
`while`は、値を条件として繰り返します。
`for`は、連続した値やデータのまとまりを範囲として、繰り返します。
記述の仕方によっては同じ結果を返すこともできます。

お菓子作りにたとえると、
粉っぽさがなくなるまでまぜる　といった命令は`while`　（終了の条件を指定する）
8個の型に生地を流し込む　といった命令は`for`　（8回繰り返すことを指定する）
といった感じでしょうか。

# おわり
お役に立てたら幸いです。
flutterのAndroidアプリでGoogleログインする時の12500エラー

# 12500エラーが止まらない
1ヶ月半程前からFlutterでAndroidアプリの作成の練習をしていました。
練習でGoogleログインを組み込もうとしたときに良くあるらしいエラー12500に遭いました。

```zsh:デバッグログ
PlatformException(sign_in_failed, com.google.android.gms.common.api.ApiException: 12500: , null)
```

それで、調べると大体出てくるFirebaseAuthのSHA-1の登録やサポートメールの登録、build.gradleの設定を何回見てもおかしいところはない・・・。
というか、今回の場合は朝方まで組み込んでて一度動作したのを確認して寝て起きて再度動かそうとしたらこのエラーが出だしたので、そもそも間違っている気がしない・・・。

# 原因
気づけば何のことはないAndroidエミュレーターが原因だったというオチです。
コールドブートすればあっさり動きました・・・。

```bash:コールドブート
./emulator -avd Pixel_8_API_VanillaIceCream -dns-server 8.8.8.8 -no-snapshot-load
```
-no-snapshot-loadがコールドブート指定です。

スナップショットの高速起動も良し悪しですね・・・。
3時間くらい無駄にしましたorz
[ECS] CPUアーキテクチャ違いでクラスターのタスクが強制停止になってしまう件について。

# 目的
CPUアーキテクチャの違いを解決し、タスクが強制停止されないようにする

# 前提
- ECRにdocker イメージプッシュ済み
- タスク定義作成済み

# タスクが強制停止になってしまう？！
タスク定義を作成後、クラスターでタスクを起動設定しました
ただ、タスクがいつまでも実行中になりません
タスクのログを確認すると以下のようにエラーが出ています

```
exec format error
```

![タスクログ___Elastic_Container_Service___ap-northeast-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2515002/a1688c9f-7c69-de1b-2e0c-d2763b2d82fc.png)

少し時間がタスクが停止してしまいました

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2515002/83876dd2-cbfb-34ca-37f9-92800a87cd34.png)

タスクが期限を損ね、いなくなっちゃいました
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2515002/625ddcc8-2630-cfa8-4837-17a11c7fdbb5.png)

# 原因は？

CPUアーキテクチャの違いが原因のようです




タスク定義の作成に利用したECRにプッシュされたdockerイメージは以下の構成になっています
- OS
    - linux
- CPUアーキテクチャ
    - arm64

```
❯ docker image inspect aaaabbbbcccc

## 結果が長いので必要なところだけ↓
        "Architecture": "arm64",
        "Os": "linux",
        
```
タスク定義を作成する際、OS/アーキテクチャの設定をデフォルト（Linux/X86_64）としていたため、食い違いが発生し、今回のエラーが起きたようです

タスク定義の設定の際、Linux/ARM64に修正しておきましょう

![Elastic_Container_Service___ap-northeast-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2515002/6c633223-05a6-e628-b272-194bfad8f4eb.png)

再び、クラスター上でタスクを実行すると正常に動作しました

![クラスタータスク___Elastic_Container_Service___ap-northeast-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2515002/087af3d0-c6e5-b370-b2ee-dee56f9b27e9.png)

コンテナのログにて起動していることが確認できました

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2515002/bee940c1-8ad4-77fc-ee27-581982dbc85a.png)



### 参考
https://www.wakuwakubank.com/posts/809-it-cpu-x86-x64-arm/

https://docs.oracle.com/cd/E19455-01/806-2720/msgs-278/index.html

https://qiita.com/masaozi3/items/801e07979696051ae93b
AIが読み解く"お客様の本音" - 「Octoparse VoC」

ネットショッピング市場の拡大に伴い、ECビジネスは競争を強いられています。かつては実店舗のみで購入できた商品も、いつでもどこからでも簡単に手に入るようになりました。その結果、消費者は膨大な商品情報とレビューにさらされ、購買行動を左右する影響力は高まる一方です。

一方のECサイト運営者は、益々複雑化するお客様のニーズを適切に把握することが困難になってきました。従来のリサーチ手法では、収集できるデータに限界があり、本当のニーズを見逃してしまう可能性がありました。それでも、口コミデータからお客様の声を読み解くことは、既存顧客の維持や新規顧客の獲得に欠かせない重要課題です。

そのため、AI自然言語処理技術を駆使したツール「Octoparse VoC」が登場しました。このツールは、大量の購入者レビューを素早く分析し、ターゲット層の潜在的なニーズを可視化します。単に文字情報を解析するだけでなく、顧客の喜びや不満、期待値など、感情の動きまでを読み解いてくれるのが最大の特長です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/262932/41e08aeb-18bf-15ee-1eaa-3eb280f2b3e1.png)


たとえば「お買い得だった」といった単なる肯定的な言葉だけでなく、そこに潜む「価格重視」というニーズを捉えることができます。また「商品より説明書の方が分かりやすかった」という一言から、ユーザーが抱える「使い勝手の悪さ」への不安を読み取ることも可能です。このように、一つひとつの言葉の裏側に潜む顧客の本音に迫ることで、これまで気づかなかった新たな潜在ニーズを発見できるのです。

さらに、感情分析により商品購入の最終的なハードルを割り出すことも可能です。ターゲット層別にポジティブ/ネガティブの感情を割り出すことで、その属性が購買に至る際の心理的障壁を事前に予測し対策を講じることができます。製品企画の段階から、このようなお客様の本音を組み込むことで、高い顧客満足度を実現できるはずです。

口コミデータは、これまでECサイト運営者の頭を悩ませる"渦"と化していました。しかし、「Octoparse VoC」のようなAIソリューションにより、その渦を解きほぐし、ビジネスチャンスに転換できる可能性が開けたのです。VoCという概念自体は目新しいものではありませんが、AIの活用でその概念をしっかりと具現化できるようになりました。膨大なVoCデータからお客様の潜在ニーズを発見する鍵を、「Octoparse VoC」は握っているのです。

もしあなたの商品に対する「お客様の本音」を知りたいのであれば、ぜひ一度「Octoparse VoC」の無料トライアルを体験してみてください。大量のレビューデータからお客様の潜在ニーズを発見し、ビジネスの次なる成長のカギを見つける手助けとなることでしょう。

公式サイト：https://voc.octoparse.com
Youtubeチュートリアル：
https://www.youtube.com/watch?v=-gVAd1TzEIQ
https://www.youtube.com/watch?v=re8VWKAsnJU&t=6s
GitHub ブランチ関係のコマンド まとめ(作成〜プッシュ）

# はじめに
よく使うブランチ関係のコマンドをアウトプットしています。
統合ブランチをdevelop、作業ブランチをfeatureとしています。

# コマンドメモ
- 初回ローカルリポジトリ作成・リモートリポジトリにプッシュ
```
$ git init                          # 現在いるディレクトリをgitで管理するため設定ファイル作成
$ git add ファイル名                   # ファイル名を.に置き換えると全てが対象になる
$ git commit -m "コメント"             # メッセージをつけて変更履歴を保存
$ git branch -M main                # デフォルトブランチをmasterからmainに変更
$ git remote add origin リポジトリURL   # originという名前でリモートリポジトリのURLを登録
$ git push origin ブランチ名           # ローカルリポジトリの変更をリモートリポジトリにアップロードする
```

- ブランチの確認
*がついている場所が今いるブランチ
```
$ git branch
*main　
```

- ブランチの作成・移動
developブランチを作成して、移動
```
$ git checkout -b develop
$ git branch
main
*develop
```

- ブランチの移動
```
$ cd アプリケーション
$ git checkout develop
```

- リモートの（統合ブランチの）最新版をローカルに反映させる
```
$ git pull origin develop
```

- 統合ブランチであるdevelopブランチから作業ブランチを作成する
```
$ git checkout develop
$ git checkout -b 作業ブランチ名
```

- 作業ブランチから内容をコミットする
```
$ git status
$ git add -A
$ git commit -m "コミット名"
```

- リモートリポジトリにpushする
```
$ git push origin 作業ブランチ名
```

# ブランチ名変更
ブランチ名の変更コマンドもメモしときます！
```
$ git branch -m 古いブランチ名 新しいブランチ名
```
// 今いるブランチの名前を変更する場合
```
$ git branch -m 新しいブランチ名
```

# GitHubのコミットにIssuesを関連付けてcloseにする
closeでマージしたときにIssuesをcloseさせる
#数字 で作成済みのIssuesに関連付ける

```
$ git commit -m "close #68"
```

あとはマージするだけでIssuesがcloseされる！

# コミットメッセージを変えたい！コミットを上書きしたい！
Issues連携するの忘れてた！
コミットを上書きしたい！ってときに便利なコマンドです↓

```
$ git commit --amend -m "新しいコミットメッセージ"
```

#  空コミットの仕方
```
$ git commit --allow-empty -m "空コミットメッセージ"
$ git push origin ブランチ名
```

# ログの確認方法
--onelineで見やすく絞り込む
qで閉じる
```
git log --oneline
```
# ブランチを削除してやり直したい時のコマンド手順

### ローカルブランチの削除方法
リモートにプッシュしていない場合、ローカルブランチを削除するだけでやり直すことができます

### 手順
現在作業ブランチにいる場合、作業ブランチを削除することができません
そのため、一旦削除対象以外のブランチに切り替えます
(mainでもdevelopでもいいです)

・ブランチの切り替え
```
$ git checkout develop
```

・作業ブランチを削除
```
$ git branch -D ブランチ名
```

作業ブランチでコミットをしていなかった場合に、
ブランチを削除したにも関わらず作業した内容が残っている場合があります
そういう時には、削除対象以外のブランチに切り替えた時にターミナルに以下が出力されているかと思います

```
ec2-user:~/environment/アプリ名 (feature/作業名) $ git checkout develop
M app/controllers/admin/confirmations_controller.rb
M app/controllers/admin/customers_controller.rb
M app/controllers/admin/genres_controller.rb
︙
Switched to branch 'develop'
```
この表示は作業ブランチで修正された箇所を引き継いでdevelopブランチで作業を続けられることを意味しています
しかし、作業をやり直したいわけですから余計なお世話ですよね

なので、まっさらな状態で新しく作業を始めたい場合は、以下のコマンドを入力します
```
$ git reset --hard HEAD
```
これで直前のコミット時点の状態に戻り、変更が全て破棄されます

:::note alert
注意
上記のコマンドで修正状態を元に戻ししても、
新しく作成したviewファイルなどのは削除されず、コードの中身もそのままです
ステータスを確認しておきましょう！
```
$ git status
```
:::
以上のコマンドでやり直すことができます！

:::note warn
最新の統合リポジトリを取得する癖をつけましょう
コンフリクトが起きる可能性があります！

・リモートの（統合ブランチの）最新版をローカルに反映させる
```
$ git pull origin develop
```
:::

# リモートブランチの削除方法
・リモートブランチを削除
```
$ git push origin --delete ブランチ名
```

すでにそのリモートブランチが削除されている場合、以下のエラーが発生する可能性があります
```
error: unable to push to unqualified destination: remoteBranchName The destination refspec neither matches an existing ref on the remote nor begins with refs/, and we are unable to guess a prefix based on the source ref. error: failed to push some refs to 'git@repository_name'
```


# さいごに
都度追加してます！
React aタグをクリックした際に新しいページタグを開いて表示する方法

お久しぶりの投稿となってしまいました！

最近は以前自作で作成したポートフォリオ(HTML/CSS,JS)をReactで書き換えているのですが、、フロントエンドをほかの言語よりも触ってきている身としては細かいことが気になってしまうことがよくあります。

## aタグの画面遷移
通常のaタグで作成すると、どうしてもそのページのリンクが更新されて表示されますよね

この画面遷移が個人的には扱いづらいなーって感じていて、ページ内リンクをクリックして新しいタブに遷移する方が扱いやすいと思っていたので、ここの問題点の解消をしたいと思い調べてみました！

## コード
````.js
<a href={{/*propsで受け取る値(直接入力でも可)*/} target="_blank" rel="noopener noreferrer">{/*propsで受け取る値*/}</a>
````

このtarget="_blank"を記述することで、リンクにクリックすることで新しいタブを開いて選択したページに遷移するようになります。rel="noopener noreferrer"はセキュリティ性向上のために書き込んでいます！！

参考になると幸いです！！
【GitHub Copilot】Copilotのナレッジファイルを追加してサポートを強化する

# やったこと
1. `.github/copilot.md`を追加
1. VSCodeで常に`copilot.md`を開いた状態で作業を行う

## `copilot.md`の内容
1. そのリポジトリ内で良く使われるコード
1. 専用のコーディング使用
1. 使用している言語のSpecificationリンク

# 理由
### 1. VSCodeで開きっぱなしのファイル内容を読む

```
オープンされている非アクティブなタブ
直近20個のタブをコンテキストとして与えられる
```

参考: https://qiita.com/yoda_naan/items/a5c53acc790586cd4fd9

### 2. Copilot用のファイルを作っておく

5/13に開催されていた[GitHub CXO来日記念イベント：ちょっと未来の開発者体験を知る](https://cto-a.connpass.com/event/313782/)にて
株式会社プレイドの日鼻氏が話していた内容をメモしたのが下記
```
prompt.mdを作り、README.mdとともに開いておく

prompt.mdファイルをプロジェクト配下におく
　→コードとしてナレッジを貯めていく

prompt.mdの内容
例えば、Specificationのリンクをそのまま貼り付ける、サンプルコードを貼り付ける
基本的にはGitHubにあるリポジトリの内容をコピーしてくる
```
REST APIのURI設計のポイント - ベストプラクティスと具体例

### **はじめに**

REST APIの設計において、URI（Uniform Resource Identifier）の設計は非常に重要です。良いURI設計は、APIの使いやすさやメンテナンスの容易さに大きく影響します。この記事では、REST APIのURI設計における9つのポイントを具体例と共に解説します。

## **URI設計のポイント**

### **1. 短く入力しやすい（冗長なパスを含まない）**

URIはできるだけ短くし、入力が簡単であることが重要です。冗長なパスを含まないようにしましょう。

### **例**

- 良い例: **`/users`**
- 悪い例: **`/api/v1/users/list/all`**

### **2. 人間が読んで理解できる（省略しない）**

URIは人間が読んで理解できるように設計する必要があります。省略を避け、明確な名前を使用しましょう。

### **例**

- 良い例: **`/users`**
- 悪い例: **`/usr`**

### **3. 大文字小文字が混雑していない（すべて小文字）**

URIはすべて

小文字で記述することで、一貫性を保ち、誤解や入力ミスを防ぎます。

### **例**

- 良い例: **`/users`**
- 悪い例: **`/Users`** または **`/UsersList`**

### **4. 単語はハイフンでつなげる**

単語をつなぐ際にはハイフン（-）を使用することで、URIが読みやすくなります。アンダースコア（_）は避けましょう。

### **例**

- 良い例: **`/user-profiles`**
- 悪い例: **`/user_profiles`** または **`/userProfiles`**

### **5. 単語は複数形を利用する**

リソースのコレクションを示す場合、単語を複数形で表現するのが一般的です。

### **例**

- 良い例: **`/users`**
- 悪い例: **`/user`**

### **6. エンコードを必要とする文字を使わない**

URIには、空白や特殊文字を避け、エンコードが必要な文字を使わないようにしましょう。

### **例**

- 良い例: **`/users`**
- 悪い例: **`/users list`**

### **7. サーバー側のアーキテクチャが反映されていない**

URIは、サーバーの内部構造やアーキテクチャを反映させず、リソースの識別に集中するべきです。

### **例**

- 良い例: **`/users`**
- 悪い例: **`/database/users`**

### **8. 改造しやすい（Hackable）**

URIは直感的に変更できる（改造しやすい）ことが理想です。例えば、ユーザーの投稿を参照する際に、ベースURIから自然に推測できるようにしましょう。

### **例**

- 良い例: **`/users/123/posts`** （ユーザーID 123の投稿）
- 悪い例: **`/posts?user_id=123`**

### **9. ルールが統一されている**

URIの設計には一貫したルールを適用することで、使いやすさとメンテナンス性が向上します。

### **例**

- 良い例: 全てのリソースURIが同じパターンに従う
    - **`/users`**
    - **`/users/123`**
    - **`/users/123/posts`**
- 悪い例: URIのパターンがバラバラ
    - **`/users`**
    - **`/user_detail/123`**
    - **`/post/list?user_id=123`**

### **具体例**

以下に、上記のポイントを踏まえた具体的なURI設計例を示します：

- **ユーザーリソースの操作**
    - ユーザーの一覧取得: **`GET /users`**
    - 新しいユーザーの作成: **`POST /users`**
    - 特定ユーザーの取得: **`GET /users/123`**
    - ユーザー情報の更新: **`PUT /users/123`**
    - ユーザーの削除: **`DELETE /users/123`**
- **ユーザーの投稿リソースの操作**
    - 特定ユーザーの投稿一覧取得: **`GET /users/123/posts`**
    - 特定ユーザーの新しい投稿作成: **`POST /users/123/posts`**
    - 特定の投稿の取得: **`GET /users/123/posts/456`**
    - 投稿の更新: **`PUT /users/123/posts/456`**
    - 投稿の削除: **`DELETE /users/123/posts/456`**

### **まとめ**

良いURI設計は、APIの使いやすさとメンテナンス性を大きく向上させます。ここで紹介した9つのポイントを参考にして、わかりやすく、一貫性のあるURIを設計しましょう。具体例を参考にしながら、自分のプロジェクトに最適なURIを考えることで、開発効率を向上させることができます。
【ChatGPT＋DALL-E3】コンポジット写真って？


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

　今回は、とある画像を解析したら、“コンポジット写真”というスタイルであることを知りました。その話です。

:::note info
本記事は、ChatGPT（有料版）
＋DALL-E3で行なっております。
画像はあります。　あしからず。
:::

　まずは、ニュース。

　OpenAI、Redditのデータを
　　AI学習に利用する契約締結。
[ITmedia NEWSポスト](https://x.com/itmedia_news/status/1791254821498155405?t=01BDG_k61jz1WyU01m0gYQ&s=03)

　Google SGEで調べると、
「Reddit（レディット）は、アメリカ発祥の掲示板型のソーシャルニュースサイトです。ユーザーがニュース記事や画像、テキストを投稿し、他のユーザーがコメントや投票を行うことで評価やランク付けを行うことができます」
　　とのこと。

　私は名前を
　　知っているだけでした😅﻿
***
　次は
　　ChatGPTの音声会話について。

　薄々気付いていましたが、
　　サム・アルトマンが
　　　正式にポストしました。

[Sam Altmanポスト](https://x.com/sama/status/1790817315069771959?t=IO9KbIjKxZ9gfeXgu1MjUA&s=03)

　DeepL翻訳
「新しい音声モードはまだ出荷されていません（GPT-4oのテキストモードは出荷されています）。

現在アプリで使用できるのは旧バージョンです！」

　やっぱね😅
***
　次。

　これは確定ではありませんが、
　　現在GPTsは、おそらくGPT-4oで
　　　動かされている、とのポストが
　　　　ありました。

　ポストを読むと、
　　処理スピードが
　　　早いことから、
　　　　そうではないか、と。

　まぁ、GPT-4oもGPTsも
　　無料ユーザーに
　　　開放されましたから、
　　　　そのためでしょうね。
***
　さて、本題です。

　まずは、やり取りをどうぞ。
***
***user***
![1715912959876.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3457238/563f2d09-2f55-5655-18a9-2a95d7ae660a.jpeg)
この画像の写し方は、なんと言いますか？

***ChatGPT***
（方向違いの説明なので、カット）

***user***
そうではなく、バストアップと立つ姿、のことを聞いています

***ChatGPT***
了解しました。画像についての説明を求めているのですね。

この画像は、左側が「バストアップ」ポートレートで、右側が「フルボディ」ポートレートです。左側のバストアップは、肩から上の部分を中心に撮影したもので、顔の表情や髪のスタイルが詳細に描かれています。右側のフルボディは、全身を通じてキャラクターのポーズや服装が表現されており、よりダイナミックな印象を与えます。

***user***
これをひと言で言えますか？

***ChatGPT***
この画像は「コンポジット写真」と呼ぶことができます。

***user***
説明してください

***ChatGPT***
「コンポジット写真」とは、複数の異なる画像や写真を組み合わせて一つの画像に仕上げる技法です。この場合、異なるポーズの写真を一つの画像ファイルにまとめて表示しています。左側がバストアップのクローズアップショットで、右側がフルボディの立ち姿を示しているため、一枚の画像に異なる視点やスタイルが含まれているのが特徴です。このようにして、同一の被写体を異なるアングルから見せることで、より多面的な表現が可能になります。

***user***
コンポジット写真に、種類などはありますか

***ChatGPT***
はい、「コンポジット写真」にはいくつかの異なる種類や使用法があります。以下はその主な種類です：

1. **合成写真**：
   - 複数の画像をデジタル的に組み合わせて、新しい画像を作成します。この技術は、現実では撮影が不可能なシーンや効果を実現するためによく使われます。

2. **コラージュ**：
   - 異なる写真や画像の断片を組み合わせて一つの芸術的な画像を作成します。これは芸術的な表現手法として利用され、感情やメッセージを伝えるために用いられます。

3. **マルチエクスポージャー**：
   - 一つのフィルムやデジタルセンサーに複数回露光して画像を重ねる方法です。この技術は写真の創造的な表現に使われ、夢幻的または抽象的な効果を生み出します。

4. **パノラマコンポジット**：
   - 複数の画像を横に並べて繋げ、広範囲をカバーする一枚の画像にします。主に風景写真に使用され、広大な景色を一つの画像で表現します。

これらの技術は、写真家がクリエイティブなビジョンを実現するために使うツールとして、または特定の効果やイメージを生み出すために広く利用されています。

***user***（実際にやってみることに）
横長で描画。
パノラマコンポジットで、ひとりの女性をポーズを変えて、いくつか出してください。
人物は、18歳金髪碧眼女性、ゆるふわロングで、風に梳かしています

***ChatGPT***
![1715912985952.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3457238/c0d2b7c7-8d91-d3cc-3a92-9bae0c7da4bf.jpeg)

***user***
彼女のポーズは三種類で描画

***ChatGPT***
![1715913003686.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3457238/35d62807-b8ad-1e35-fdb0-e481b35b37d7.jpeg)
***

　このあと、
　　三つではなく、
　　　五つが出るようになり、
　　　　途中で挫折しました😅

　それでも
　　このスタイルは
　　　面白いと思いました。

　みなさんの
　　作品制作の糧に
　　　なれば、幸いです。

　今回は、ここまで。
validatesの数値チェックについて

## 環境
Rails5.2
Ruby2.6

## はじめに

Railsの**validates**での数値チェックについて書いてみました。

## 内容
#### その１

validatesで数値チェックをする場合、基本な書き方はつぎのようになります。

```
validates :hoge, numericality: true
```

この設定は、Railsガイドによると「整数値または浮動小数点数値にマッチする。符号がある場合もマッチする。」と書かれています。つまり、次の入力はすべてOKになります。

```
0		　　# 〇
9		　　# 〇
+9		　　# 〇
-9		　　# 〇
0.999		# 〇
9.999		# 〇
+9.999		# 〇
-9.999		# 〇
```

数値の入力チェックをこれだけで済ませると、あまりにもざっくりしすぎです。ほとんどの場合において、数値の入力チェックは、もっと、他の条件との組み合わせで行うことになります。

#### その２

整数だけにマッチさせて、浮動小数点数値にはマッチさせたくない場合は、次のようになります。ちなみに±符号もマッチされます。

```
validates :hoge, numericality: { only_integer: true }
```

Railsガイドにも書かれていますが、上の書き方は、内部的にはつぎの正規表現で記述した場合と、同じ動きになっています。

```
validates :hoge, format: {with: /\A[+-]?\d+\z/}
```

つまり、どっちで記述しても同じだということです。

このようなテスト結果となります。

```
0		　　# 〇
9		　　# 〇
+9		　　# 〇
-9		　　# 〇
0.999		# ×
9.999		# ×
+9.999		# ×
-9.999		# ×
```

テーブルのカラムがInteger型の場合、+符号をつけて更新したとしても、当然ではありますが、テーブルに+符号が付いた状態で更新されるようなことはありません。更新後に再表示をすると、+符号がついていない状態で表示されます。-符号を付けて更新した場合は、当然、-符号がついて更新されます。

#### その３

符号を付けて更新したくない場合、つまり、符号を付けてsubmitボタンを押すと、入力エラーにしたい場合は、残念ながら、numericalityヘルパーは使えません。

例えば、次のような正規表現を使って、符号が入力できないように自力で制御をかけるしかありません。Railsの標準機能だけでは、こういった、ちょっとした痒い所に手が届かない場合があります。

```
validates :hoge, format: {with: /\A\d{4}\z/
```

つぎように、Railsで用意されているヘルパーを使って、入力範囲を1～9999に制限させると、-符号がついた整数は入力できなくなりますが、+100とかは入力ができる状態になります。

+100で更新しても、テーブルのカラムがInteger型であれば問題ないでしょうが、カラムがString型であれば、そのまま+100で更新されることになります。

```
validates :hoge, numericality: { only_integer: true, greater_than_or_equal_to: 1, less_than_or_equal_to: 9999}
```

**numericality: true**を使っても、**numericality: { only_integer: true }** を使っても、画面のテキスト項目が未入力の状態でsubmitを押すと、共にエラーになります。nilまたは、""で書き込まれてしまうことはありません。

入力項目に「000」で入力してsubmitを押した場合、テーブルのカラムがString型の場合は、当然、そのまま「000」で更新されます。Integer型の場合は「0」が更新されることになります。「000」でsubmitを押しても、再表示した場合は「0」が表示されます。
ArduinoUnoR4WiFi: シリアル通信　（受信）

## 仕様

'n' を受信したら、PD8 を ON
'f' を受信したら、PD8 を OFF

## プログラム

```text:serial_read.ino
// ---------------------------------------------------------------------
/*
	serial_read.ino

					May/17/2024

*/
// ---------------------------------------------------------------------
#define LED_A 8
 
// ---------------------------------------------------------------------
void setup() {
	Serial.begin(19200); 
	pinMode(LED_A,OUTPUT);

	for (int it=0; it<3; it++)
		{
		Serial.println("Hello");
		delay(500);
		Serial.println("Welcome");
		delay(500);
		}
}
 
// ---------------------------------------------------------------------
void loop() {
	char key;

	if ( Serial.available() ) {
		key = Serial.read();

		if (key == 'n')
			{
			Serial.println("PD8 ON");
			digitalWrite(LED_A,HIGH);
			}
		else if (key == 'f')
			{
			Serial.println("PD8 OFF");
			digitalWrite(LED_A,LOW);
			}
		else
			{
			Serial.println(key);
			}
	} 
 
	delay(200);
}

// ---------------------------------------------------------------------
```
Octoparseプリセットクローラーなら、ブラウザ上で簡単にデータ収集できます

ビジネスの現場で必要不可欠なのが、ウェブ上の様々な情報を効率的に収集することです。しかし一方で、そうした膨大な情報の中から自分に必要なデータを手作業で探し出すのは、多大な時間と手間がかかる現実的な課題でした。

そんなニーズに正面から応えるべく誕生したのが、Octoparseの新サービス「プリセットクローラー」です。ウェブブラウザ上で直接操作でき、あらかじめ設定されたルールに従ってデータを自動収集できるオンラインサービスなのです。

「プリセットクローラー」最大の魅力は、プログラミング知識が無くてもウェブデータを手軽に収集できる点にあります。従来のスクレイピングツールは、ある程度の技術的なスキルが必要でユーザーにハードルが高かったのが実情です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/262932/d76546f1-5082-a4da-0942-c5b97c724b6b.png)


このサービスであれば、ブラウザ上で直接利用可能。利用手順は、Octoparseのサイトにアクセスし、登録してから、提供されている収集プランの中からほしいものを選択するだけです。必要なデータさえ特定できれば、サイトからデータが自動的に抽出されていきます。

さらに出力ファイル形式も柔軟に選択でき、設定したスケジュール通りに自動収集することも可能です。ユーザーの用途に合わせて、最適な条件でデータ収集が行えるのが大きな強みです。

一体どのような場面で「プリセットクローラー」が活躍するのでしょうか。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/262932/3e760afd-2eec-4eee-4b96-fbc56692aa9e.png)


実はすでにさまざまな業界で幅広い活用が行われています。たとえば競合他社の製品価格を一括取得すれば、リアルタイムの価格動向を手軽に把握できます。また大量の製品ラインナップから仕様や関連情報を収集したり、営業リストなどのビジネス情報をスムーズに調達したりと、適用範囲は多岐にわたります。  

投資家やアナリストの方々からもご利用いただいており、市場関連の最新ニュースや株価情報、アナリストレポートなどを効率的に収集する活用例が見受けられます。

このように「プリセットクローラー」は、業種業態を問わずあらゆるビジネスシーンで役立つオンラインサービスです。ウェブ上に存在する価値あるデータから、新しいビジネスチャンスを見出すきっかけを提供してくれます。

もちろん、Octoparseサービスゆえのセキュリティやサポートの高い品質も、「プリセットクローラー」には備わっています。長年の実績を持つOctoparseが提供するからこそ、安心して利用できるのです。

公式サイト：https://www.octoparse.jp
プリセットクローラーストア：https://www.octoparse.jp/template
オブジェクト指向UI設計のすすめ


## タスク指向UI
- SIerが設計するシステムでよく見かける
- 「何をやる」に着目して設計する
「出荷登録」「出荷更新」「出荷取り消し」など

----

## タスク指向UIの利点

- 機能単位で画面を作ることになるので分担しやすい
- 似たような画面が増えるので流用しやすい
- 業務分析の結果をそのまま反映させやすい

※基本、**作る側の都合！**　←ここ重要

----

## タスク指向UIの欠点

- 操作の「対象」が明確になるまでが遠くなりがちで、本当に操作が合っているのか分かりづらい
- 操作と対象の組み合わせがメニューとなるため、メニュー項目が長大になりがち（いわゆる『組み合わせ爆発』）
- 途中で異なる操作をしたいときに、トップまで戻る必要がある
- 想定していない手順で操作をしたいときに不便、ないしは出来ない

※総じて、**ユーザの使い勝手を悪くしてしまいがち！**　←ここ重要

----

## オブジェクト指向UIとは？

大雑把に言えば・・・

- まず「もの」を選択して、そこから「操作」を選ぶ作りのこと
- 分かりやすいのはマスタメンテ

----

## なぜオブジェクト指向UIが使いやすいのか

- 人の認知の順番に合致している（特に日本語話者の思考と合致？）

----

## タスク指向UIが有効なケース

- 銀行ATM：対象が「口座」と明確なのでいきなり「操作」を選択させてよい


----

## 拡張性の観点

- タスク指向な作りは、画面遷移をいじるのが難しい（最初にモードが決まるから）
- オブジェクト指向だとこれが楽。特に、WebのRESTの考え方と相性が良い

----

## 「誰でも使えるシステム」とは？(1/2)

- こういう話をすると、人は「マニュアルを整備する」とか「ウィザードを充実する」という話になりがち

- でも、本当に必要なのは「自分に合った手順で操作できる」ことなのでは？
→　これはタスク指向UIだと実現が難しい！

----

## 「誰でも使えるシステム」とは？(2/2)

- 極端に言えば、Excel。Excelは操作の手順を限定しないから汎用的に使える

----

## オブジェクト指向UIを実装する上で留意すべき点

- オブジェクトの名前を統一する：安易に略さない／表記を統一する／命名ルールを統一する
- オブジェクトの抽出
- タスク指向もときには有効：よく使う操作へのショートカット

----

## 最後に

- オブジェクト指向UI設計は「銀の弾丸」
- マニュアルを見なくても使えるシステムを目指そう
- 観察眼：使いにくいスマホアプリvs使いやすいスマホアプリ／テレビのリモコンvsApple TVのリモコン

----

## 参考書籍（必読級！）
- [オブジェクト指向UIデザイン──使いやすいソフトウェアの原理]( https://www.amazon.co.jp/%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E6%8C%87%E5%90%91UI%E3%83%87%E3%82%B6%E3%82%A4%E3%83%B3%E2%94%80%E2%94%80%E4%BD%BF%E3%81%84%E3%82%84%E3%81%99%E3%81%84%E3%82%BD%E3%83%95%E3%83%88%E3%82%A6%E3%82%A7%E3%82%A2%E3%81%AE%E5%8E%9F%E7%90%86-WEB-DB-PRESS-plus%E3%82%B7%E3%83%AA%E3%83%BC%E3%82%BA/dp/4297113511)
Bashで「Argument list too long」が発生した時の対策

Bash のコマンドライン実行時に「Argument list too long」が発生した場合の対策です。

このエラーが発生する背景は２つあります。

1. 引数が多すぎる (例: ファイルが大量にあるディレクトリで `ls *` とした時のワイルドカード展開が多い時)
2. 引数が長すぎる (例: 一行の文字数が多い)

## 引数が多い場合の対策

先達が書かれているので、これらをご覧ください。
結論、xargs とかを使って引数を分割実行するといったことが書かれています。

* [chmod や mv すると "bash: Argument list too long" が出るようになった](https://qiita.com/KEINOS/items/41de05166425045368f7)
* [linuxのコマンドラインでargument list too longエラーが出た時の対処方法](https://mome-n.com/posts/shell-argument-list-too-long/)

## 引数が長すぎる場合の対策

引数が長すぎる場合は、[ヒアドキュメント](https://ja.wikipedia.org/wiki/%E3%83%92%E3%82%A2%E3%83%89%E3%82%AD%E3%83%A5%E3%83%A1%E3%83%B3%E3%83%88)で標準入力から引き渡し、curl 側ではファイルからの入力として扱うことで回避できます。

```bash
# ほかのオプションは省略しています
curl -d @- << _EndOfPostBody_
{
  "image_url": "${DATA_URI}"
}
_EndOfPostBody_

# もしくは
cat << _EndOfPostBody_ | curl -d @-
{
  "image_url": "${DATA_URI}"
}
_EndOfPostBody_
```

２つの方法があります。
どちらでもよいのですが、個人的にはcatの後にパイプで加工できたりするので２番目が好きです。ただ、Syntax Highlightは１番目のほうが素直なんですよね、悩む。

### 解説

例えば画像をBase64エンコーディングしてData URIにした時に、画像サイズが大きいと発生しがちです。例として curl を考えてみます。

curl の `-d` の引数に直接 Data URI(画像のBase64エンコード済み文字列) を書いた場合、大抵 `Argument list too long` が発生する事でしょう。例えば以下のようにした場合です。

```bash
# ほかのオプションは省略しています
DATA_URI='data:image/jpeg;base64,'$(base64 -w0 INPUT.jpg)
curl -d '{"image_url": "${DATA_URI}"}'
#=> -bash: /usr/bin/curl: Argument list too long
```

## あとがき

[GPT-4o モデルに curl コマンドで画像をプロンプトとして送信する方法](https://qiita.com/ma2shita/items/d4eea9835327c12b4775)を書いてたら、ほとんどシェル芸の話だったことに気が付いたので、分割しました。

_EoT_
Bashのコマンド置換＋ヒアドキュメントで「warning: command substitution: 1 unterminated here-document」が発生した時の対策

Bash でコマンド置換＋ヒアドキュメントを使った際に「warning: command substitution: 1 unterminated here-document」が発生した場合の対策です。

* [コマンド置換(Command Substitution)](https://www.gnu.org/software/bash/manual/html_node/Command-Substitution.html)
* [ヒアドキュメント](https://ja.wikipedia.org/wiki/%E3%83%92%E3%82%A2%E3%83%89%E3%82%AD%E3%83%A5%E3%83%A1%E3%83%B3%E3%83%88)

この警告は、ヒアドキュメント全体もコマンド置換内(のサブシェル内)に記述することで回避できます。

```bash
VAL1="Any Value"
RESULT=$(cat << _EndOfJson_
{
  "foobar1": "${VAL1}"
}
_EndOfJson_
)
echo "$RESULT"
#=> {
#=>   "foobar1": "Any Value"
#=> }
```

```bash:catとパイプを使う場合
VAL1="Any Value"
RESULT=$(cat << _EndOfJson_
{
  "foobar1": "${VAL1}"
}
_EndOfJson_
)
#=> {
#=>   "foobar1": "Any Value"
#=> }
```

## 解説

やりがちなのが以下のような記述です。

```bash:warningが発生する例
VAL1="Any Value"
RESULT=$(cat << _EndOfJson_)
{
  "foobar1": "${VAL1}"
}
_EndOfJson_
#=> warning: command substitution: 1 unterminated here-document が発生
# ちなみに、コマンド自体はなんとなく実行される
```

ほとんど似ていますが、コマンド置換 `$()` のカッコの位置が異なることがポイントです。

`$(cat << _EndOfJson_)` は、サブシェル(子プロセス)で実行されます。しかし、このカッコ内 `_EndOfJson_` という終端行がありません。これが warning の発生理由です。

こういったこともあり、手で１行づつ入力すると `$(cat << _EndOfJson_)` という行の時点で warning が発生します。
※ しかし、warning が出てもなんとなく動いちゃう（理由は追ってない）ので、困りもの...

対策の方は、`$RESULT` 視点では `RESULT=$(...)` となっており、`$(...)` 内にヒアドキュメントの終端が含まれているため、警告が出ないのです。

## あとがき

[GPT-4o モデルに curl コマンドで画像をプロンプトとして送信する方法](https://qiita.com/ma2shita/items/d4eea9835327c12b4775)を書いてたら、ほとんどシェル芸の話だったことに気が付いたので、分割しました。

_EoT_
SSH パスワード認証のバッチ処理

パスワード認証のSSHって殆無いが、**極たまーに** そんなサーバーあったりする。特に IBM i のデフォルトインストールのままだと...。
対話式ではパスワード入力すれば良いがバッチ処理はどうか？ IBM i の yum でも **sshpass** が用意されているのでそれで OK 🐄

## sshpass インストール

`yum install sshpass` コマンドでインストール

```sh
$ yum install sshpass
インストール処理の設定をしています
依存性の解決をしています
--> トランザクションの確認を実行しています。
---> Package sshpass.ppc64 0:1.06-1 will be インストール
--> 依存性解決を終了しました。

依存性を解決しました

================================================================================================================================================================
 パッケージ                            アーキテクチャ                      バージョン                             リポジトリー                             容量
================================================================================================================================================================
インストールしています:
 sshpass                               ppc64                               1.06-1                                 ibmi-base                                30 k

トランザクションの要約
================================================================================================================================================================
インストール        1 Packages

総ダウンロード容量: 30 k
インストール済み容量: 30 k
これでいいですか? [y/N]y
パッケージをダウンロードしています:
(1/1): sshpass-1.06-1.ibmi7.2.ppc64.rpm                                                                                                  |  30 kB  00:00:00
----------------------------------------------------------------------------------------------------------------------------------------------------------------
合計                                                                                                                             82 kB/s |  30 kB     00:01
トランザクションのチェックを実行してします。
トランザクションのテストを実行しています
トランザクションのテストを成功しました
トランザクションを実行しています
  インストールしています  : sshpass-1.06-1.ppc64                                                                                                            1/2

インストール:
sshpass.ppc64 0:1.06-1
```

## 使い方

sshpassの使い方が2パターンある。

1. 引数にパスワードを渡す（`-p` オプション）
2. 環境変数 `SSHPASS` を使う（`-e` オプション）

```引数を使う例.sh
sshpass -p "pwd" ssh "hoge_user@hoge_host"
```

```環境変数を使う例.sh
export SSHPASS="pwd"
sshpass -e ssh "hoge_user@hoge_host"
```

以上
Google拡張機能と連携できるElectronベースのアプリを作った（Electronで別アプリからアクセスできるローカルサーバーを開く）

# はじめに
色々な経緯があり、Google拡張機能でWindows標準の通知機能を使っていました。

https://developer.mozilla.org/ja/docs/Web/API/Notification

- 手軽に呼び出せる
- 色々な画面を開いていたりバックグラウンドでも通知ができる
- 履歴が残る
- うるさすぎない

と、かなりいいことづくめなのですが・・。
欠点があります。

それは、
- **メインウィンドウにしか表示させられない**
- **ゲーム中などは通知がオフになる**
    - 設定でオンには出来るが、その場合ゲームの邪魔になるかゲームをサブウィンドウに移したりしないといけない
    - ゲームが起動される画面は基本的に毎回メインウィンドウなので、開くたびに動かすのが面倒くさい（`win + shift + 矢印`でいけるとしても面倒くさい！！！！！）
    - Windows11だと、通知バーをサブウィンドウに移すことが出来ない
（[Notifications on second monitor in Windows 11 - Microsoft Community](https://answers.microsoft.com/en-us/windows/forum/all/notifications-on-second-monitor-in-windows-11/62162977-1e3a-47fd-a791-88b64869ddc0)）

ということです。

これがあまりにもしんどすぎたので、連携出来るアプリを作成しました。

# 連携方法
JavaScriptから別のアプリケーションに連携する方法が一つしか思いつかなかったので、**ローカルにサーバーを立てて、POSTリクエストを送る**という形で実装しました。

```javascript:main.js
// Electronアプリ
const http = require('http');

// メインとなるウィンドウを作成
let win;
app.whenReady().then(() => {
    win = new BrowserWindow({
        width: 600
		,height: 800
		,webPreferences: {
			preload: __dirname + '/preload.js'
		}
    });

    win.loadFile('index.html');
});

// 下記鯖立て処理
const port = 8081; // ここは空いているポート番号なら好きなもので良い

http.createServer((req, res) => {

    // うまく動かなかったときに拾ってきたヘッダーなので、もしかしたら問題あるかも。要確認
    const headers = {
        'Access-Control-Allow-Origin': '*', /* @dev First, read about security */
        'Access-Control-Allow-Methods': 'OPTIONS, POST, GET', // 動作確認用にGETも含んでいるが、今回のケースであれば正直使わない
        'Access-Control-Max-Age': 2592000, // 30 days
        'Access-Control-Allow-Headers': 'content-type' // Might be helpful
    };

    if (req.method === 'OPTIONS') {
        res.writeHead(204, headers);
        res.end();
        return;
    }

    if (['GET', 'POST'].includes(req.method)) {
        let body = '';
        req.on('data', chunk => {
            body += chunk;
        });
        req.on('end', () => {
            res.writeHead(200, headers);
            res.end('OK');

            win.webContents.send('add-alert', JSON.parse(body));
        })
        return;
    }

}).listen(port);
```

```javascript:extension.js
// Google拡張機能
function sendAlert(imgSrc, message) {
	const req = new XMLHttpRequest();
	req.open('POST', 'http://localhost:8081');
	req.setRequestHeader('Content-Type','application/json');

    // 通知の画像と文言を送信
	req.send(JSON.stringify({
		img: imgSrc
		,text: message
	}));
}
```

# 詰まった点
## Requestの受付がうまくいかなかった
Request周りの理解が浅く、「POSTで投げれば内容を受け取れる」程度の認識だったのですが、少なくとも今回のケースに関しては下記のような流れのようです。
1. 「OPTIONS」形式でRequestを受け、問題がないか確認する
（204を返すことで問題なしとしている）
1. 「POST」形式でのRequestが**複数回**送られてくる
そのため、`data`をつなぎ合わせる必要がある
1. 最後に`end`が送信されて、リクエスト処理が終了する

参考：

https://qiita.com/im36-123/items/775aa7fd4ba3b171768c

## 受け取ったdataの型が不正
きちんと`JSON.stringify`と`JSON.parse`をしていないのが悪かったです。
ｽﾐﾏｾﾝ。

## 正直Tauriで~~作りたかった~~ → 作れた
~~のですが、Tauriでローカルサーバーを立てる方法がわからず・・。~~

https://v2.tauri.app/features/localhost/

~~このプラグインを使うことで立てられるっぽいのですが、理解が浅いこともありうまく動作しませんでした。~~
~~利用しているサンプルも全く見つからなかった。~~

~~どなたか詳しい方おられましたら教えて下さい。~~

作れました。

https://qiita.com/kft12/items/185b1cd616703125d242

# おわりに
そもそも、もっと簡単な方法があったんじゃないか？という疑問が消えないのですが、とりあえず出来たので良しとします。
また一つ学びました。
【Salesforce】レコードタイプの並び順

レコードタイプはラベル順に表示されます。
デフォルトレコードタイプが設定されている場合、デフォルトが先頭に表示されます。

### 検証
レコードタイプを作成。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2728822/dbe3b8ce-3071-85aa-0d85-0d3aa9d61ccb.png)

表示ラベルを昇順、開発名を降順に設定。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2728822/3f4d4403-20f7-5993-1db5-595d2e63a788.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2728822/8674b3d8-2937-e729-5a2d-1bf4ff507c84.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2728822/86f50e1d-0015-e42b-cd9b-fe0e074b01b3.png)

表示ラベル順に表示される。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2728822/a812429e-e3c4-26ba-bdb6-a16e662f7065.png)

デフォルトレコードタイプを1→2に変更。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2728822/48364f9f-e1fe-1ad4-8ade-6928255f6082.png)

2が先頭に表示される。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2728822/8713384a-5688-8af4-f91a-6a4319834f36.png)
項目「承認者」に値が入ったら、特定の項目の変更を不可とする入力規則を作成する。

## １．システム管理者であることを確認する
プロファイルを利用する場合、名称でなく、IDを利用する（名称変更されたり言語的な影響が出るため）

```
$Profile.Id <>"システム管理者のプロファイルID"
```

カスタム権限を作成しプロファイルで割り当てる方法もある。hogehogeはカスタム権限名

```
AND(NOT($Permission.hogehoge)
```


[参考記事](https://qiita.com/Takaa/items/f0f863c7795dd975701c)


## 2.条件設定→新規行でないこと　を判定する

```
NOT(ISNEW())
```

## 3.条件設定→承認者の項目に値が入っていることを判定する

```
NOT(ISBLANK( checker__c ))
```

## 4.該当項目に変更があったかどうか判定する。

```
ISCHANGED( price )
```

これらをAND条件で結合すると、

```
AND(
  NOT($Permission.CustomerMaintenance),
  NOT(ISNEW()),
  NOT(ISBLANK( checker__c )),
  ISCHANGED( price )
)
```


[参考記事](https://qiita.com/akiblue/items/b1b36054b5f698bbf2c5)
rex0220 ちょっと便利なプラグイン テーブルフィルター

[ちょっと便利なプラグイン](https://qiita.com/rex0220/items/45883f15ccd34a68b8af)で、詳細画面のテーブルフィルター機能をご紹介します。
kintone の少し使いにくいところをちょっとだけ便利にするプラグインです。

# 概要

[ちょっと便利なプラグイン](https://qiita.com/rex0220/items/45883f15ccd34a68b8af)で、詳細画面のテーブルにフィルター表示できます。

数値も文字列として、フィルタリングされます。

![2024-05-17_10h00_32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/bf94ece7-ee40-294f-e614-475e81addea8.png)

# 操作例

![2024-05-17_10h07_21.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/99ca0582-206d-a39d-998b-2766f633f78e.gif)

# ちょっと便利なプラグイン設定

「F31 詳細画面でテーブルフィルター」をチェックします。
※フィルタに"テーブル"を入力すると、絞り込めます。

![2024-05-17_10h09_46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/a88e7aba-8b63-9351-753a-71c6d98e9918.png)
[AWS IAM] CloudWatch Log への Logging の権限を忘れずに作ろう

よく作り忘れて、Terraformで何度も作り直している気がする。

## 必要となる例

- `ECS task execution role ---> CW logs`
- `Lambda role ---> CW logs`



## 必要となる権限

以下の権限をrole内に含めてやる。

```tf
        {
          Action   = [
            "logs:CreateLogGroup"
          ]
          Effect   = "Allow"
          Resource = "arn:aws:logs:us-east-1:111111111111:*"
        },
        {
          Action = [
            "logs:CreateLogStream",
            "logs:PutLogEvents"
          ]
          Effect   = "Allow"
          Resource = "arn:aws:logs:us-east-1:111111111111:log-group:/aws/lambda/lambda_fn_name:*"
        },
```
NDRメールマスク化

```python
import email
from email import policy
from email.parser import BytesParser
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText

# NDRメールのファイルパス
ndr_email_path = 'path/to/ndr_email.eml'

def mask_message_body(ndr_email_path):
    # メッセージをパース
    with open(ndr_email_path, 'rb') as f:
        msg = BytesParser(policy=policy.default).parse(f)
    
    if msg.get_content_type() != 'multipart/report':
        raise ValueError('This is not a multipart/report email')

    # 新しいmultipart/reportメッセージを作成
    multipart_report = MIMEMultipart('report', report_type='delivery-status')
    multipart_report.set_boundary(msg.get_boundary())

    for part in msg.iter_parts():
        content_type = part.get_content_type()

        if content_type == 'text/plain':
            multipart_report.attach(part)
        elif content_type == 'message/delivery-status':
            multipart_report.attach(part)
        elif content_type == 'message/rfc822':
            original_message = part.get_payload(0)
            
            # メッセージヘッダを保持
            headers = original_message.as_string().split('\n\n', 1)[0]
            # 本文をマスク化
            original_body = original_message.get_payload()
            masked_body = 'x' * len(original_body)
            
            # 新しいmessage/rfc822パートを作成
            masked_message = headers + '\n\n' + masked_body
            masked_part = MIMEText(masked_message, 'rfc822')
            masked_part.replace_header('Content-Type', 'message/rfc822')
            
            multipart_report.attach(masked_part)
        else:
            multipart_report.attach(part)

    return multipart_report

masked_email = mask_message_body(ndr_email_path)

# マスク化されたメールをファイルに保存
with open('masked_ndr_email.eml', 'w') as f:
    f.write(masked_email.as_string())

print('Masked NDR email saved to masked_ndr_email.eml')
```
PowerShellでアクセスにあるテーブル情報を元にoutlookから予定表を取得する方法

```powershell
# Accessデータベースの接続文字列
$connectionString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\Path\To\YourDatabase.accdb;"

# Accessデータベースからユーザーリストを取得
function Get-UserList {
    param (
        [string]$connectionString
    )

    $userList = @()
    
    $connection = New-Object -ComObject ADODB.Connection
    $connection.Open($connectionString)
    
    $command = New-Object -ComObject ADODB.Command
    $command.ActiveConnection = $connection
    $command.CommandText = "SELECT Email, Name FROM EmployeeTable"

    $recordset = $command.Execute()
    
    while (-not $recordset.EOF) {
        $user = [PSCustomObject]@{
            Email = $recordset.Fields.Item("Email").Value
            Name = $recordset.Fields.Item("Name").Value
        }
        $userList += $user
        $recordset.MoveNext()
    }

    $recordset.Close()
    $connection.Close()

    return $userList
}

# ユーザーリストを取得
$users = Get-UserList -connectionString $connectionString

# PowerShellで並列処理を行うためのジョブを定義
foreach ($user in $users) {
    Start-Job -ArgumentList $user.Email, $connectionString -ScriptBlock {
        param ($email, $connectionString)
        
        # Outlookアプリケーションのインスタンスを作成
        $outlook = New-Object -ComObject Outlook.Application
        $namespace = $outlook.GetNamespace("MAPI")
        $recipient = $namespace.CreateRecipient($email)
        $calendar = $namespace.GetSharedDefaultFolder($recipient, 9) # 9は予定表のフォルダID

        # Accessデータベースに接続
        $connection = New-Object -ComObject ADODB.Connection
        $connection.Open($connectionString)
        
        $items = $calendar.Items
        $items.Sort("[Start]")

        foreach ($item in $items) {
            # クエリを準備
            $query = "INSERT INTO YourTableName (Subject, Start, [End], Location) VALUES (?,?,?,?)"
            
            # コマンドを作成
            $command = New-Object -ComObject ADODB.Command
            $command.ActiveConnection = $connection
            $command.CommandText = $query

            # パラメータを追加
            $command.Parameters.Append($command.CreateParameter("p1", 200, 1, -1, $item.Subject))
            $command.Parameters.Append($command.CreateParameter("p2", 135, 1, -1, $item.Start))
            $command.Parameters.Append($command.CreateParameter("p3", 135, 1, -1, $item.End))
            $command.Parameters.Append($command.CreateParameter("p4", 200, 1, -1, $item.Location))

            # クエリを実行
            $command.Execute()
        }

        # 接続を閉じる
        $connection.Close()
    }
}

# 全ジョブの完了を待つ
Get-Job | Wait-Job | Out-Null

# ジョブの出力を取得して表示（オプション）
$results = Get-Job | Receive-Job
$results

# ジョブをクリーンアップ
Get-Job | Remove-Job
```
Rails ルーティング  namespace / module の使い方

# はじめに
わからなかったことを教えてもらったので、
アウトプットに投稿します！

初っ端から躓いたルーティング…
namespace / module　はどう使い分けるのか😕
まとめていきます！

# ルーティングの特徴
### 何も指定しない場合(通常のルーティング)

```routes.rb
get 'home/about' => 'homes#about'
```
<table>
  <caption>通常のルーティング</caption>
  <thead>
    <tr>
      <th>Prefix</th>
      <th>Verb</th>
      <th>URI Pattern</th>
      <th>Controller#Action</th>
    </tr>
  </thead>
  <tr>
    <td>home_about</td> 
    <td>GET</td>
    <td>/home/about(.:format)</td>
    <td>homes#about</td>
  </tr>
</table>

```routes.rb
resources :users, only: [:show, :index, :edit, :update] do
```

<table>
  <caption>通常のルーティング</caption>
  <thead>
    <tr>
      <th>Prefix</th>
      <th>Verb</th>
      <th>URI Pattern</th>
      <th>Controller#Action</th>
    </tr>
  </thead>
  <tr>
    <td>user</td> 
    <td>GET</td>
    <td>/users/:id(.:format)</td>
    <td>users#show</td>
  </tr>
</table>

### namespace使用の場合
URLにもファイル構成にも、最初に /admin がつく！

```routes.rb
namespace :admin do
  resources :customers, only: [:index, :show,  :edit, :update]
  get 'orders/:id' => 'orders#show'
end
```

<table>
  <caption>namespaceのルーティング</caption>
  <thead>
    <tr>
      <th>Prefix</th>
      <th>Verb</th>
      <th>URI Pattern</th>
      <th>Controller#Action</th>
    </tr>
  </thead>
  <tr>
    <td>edit_admin_customer</td> 
    <td>GET</td>
    <td>/admin/customers/:id/edit(.:format)</td>
    <td>admin/customers#index</td>
  </tr>
  <tr>
    <td>admin</td> 
    <td>GET</td>
    <td>/admin/orders/:id(.:format)</td>
    <td>admin/orders#show</td>
  </tr>
</table>

### module使用の場合
URLには /public がつかないが
ファイル構成には最初に /public がつく！

```routes.rb
scope module: :public do
  resources :addresses, only: [:index, :edit, :create, :update, :destroy]
  get 'home/about' => 'homes#top', as: "about"
end
```

:::note alert
moduleの記入方法
```routes.rb
scope module: ~~ do
end
```
scopeと記載があるが、これがmoduleの記入方法！
scopeは適用されていないので注意！
:::

<table>
  <caption>namespaceのルーティング</caption>
  <thead>
    <tr>
      <th>Prefix</th>
      <th>Verb</th>
      <th>URI Pattern</th>
      <th>Controller#Action</th>
    </tr>
  </thead>
  <tr>
    <td>addresses</td> 
    <td>GET</td>
    <td>/addresses(.:format) </td>
    <td>public/addresses#index</td>
  </tr>
  <tr>
    <td>about</td> 
    <td>GET</td>
    <td>/home/about(.:format)</td>
    <td>public/home/td>
  </tr>
</table>

# 最後に
scopeについても後日追記予定です！