OmniAICreator/Qiita-1.07M huggingface dataset:
回転した長方形に内接する長方形

他の人の記事：

https://qiita.com/yukipy22/items/82493e59907f3e0256e7#comment-06e68508b848cef1460d

に馬鹿げた質問をしてしまった．
正方形の話なのに「それ長方形だとダメじゃね？」とか述べた．今は反省している．

↓

で，「長方形だった場合には DOすればE ？」というのを考えてみた，っていうだけの話です．

……こんなだろうか？

```C++
//サイズ(W,H)の矩形を（矩形の中心を回転中心として）Theta_Deg[degree]だけ回転させたとき，
//その回転結果に内接する，（回転前の矩形と同じ縦横比の）矩形を求める．
//
//※ここでは結果は返したりせずに画像に描画して可視化するだけ．
//
//引数：
// - Canvas : これは描画用の画像バッファ
// - W, H : 回転される矩形のサイズ
// ^ Theta_Deg : 矩形を回転させる角度[degree]
void Test( cv::Mat &Canvas, double W, double H, double Theta_Deg )
{
    //回転した矩形に内接する，回転前の矩形と同じ縦横比の矩形　の頂点（のうちの１つ）(x,y) を求む．
    //（座標原点は矩形の中心．なので他の３頂点は x や y の符号を変えれば作れる）
    static const double PI = acos(-1.0);
    const double theta = (W>=H ? Theta_Deg+90 : Theta_Deg) * PI / 180.0;

    double Denominator = abs(cos(theta)) + abs(sin(theta)*H/W);
    double x = 0.5 * std::min(W,H) / Denominator;
    double y = x * H / W;

    //-------------------------------
    //以下は可視化
    
    {//回転した矩形を描画
        cv::Mat Tmp = cv::Mat::zeros( Canvas.size(), CV_8UC3 );
        cv::Rect SrcRect( Tmp.cols/2+int(-W/2), Tmp.rows/2+int(-H/2), int(W),int(H) );
        cv::rectangle( Tmp, SrcRect, cv::Scalar(255,64,64), -1 );
        auto M = cv::getRotationMatrix2D( cv::Point2f{Canvas.cols*0.5f, Canvas.rows*0.5f}, Theta_Deg, 1.0 );
        cv::warpAffine( Tmp, Canvas, M, Canvas.size() );
    }

    {//求めた内接矩形を描画
        int ix = cvRound(x);
        int iy = cvRound(y);
        cv::Point P[4] = {  { ix, iy }, { -ix, iy }, { -ix, -iy }, { ix, -iy }  };
        for( int i=0; i<4; ++i ){   P[i] += cv::Point( Canvas.cols/2, Canvas.rows/2 );  }
        for( int i=0; i<4; ++i ){   cv::line( Canvas, P[i], P[(i+1)&0x03], cv::Scalar(255,255,255) );   }
    }
}

//てすと
int main()
{
    //矩形のサイズ
    const int W=100;
    const int H=145;
    
    cv::Mat Canvas( 200,200, CV_8UC3 );
    int Theta_Deg = 0;  //回転角度
    while( true )
    {
        Test( Canvas, W,H, Theta_Deg );
        cv::imshow( "Test", Canvas );
        if( cv::waitKey(16) == 'q' )break;

        //角度変更してみる
        Theta_Deg += 1;
        if( Theta_Deg >= 360 )Theta_Deg-=360;
    }

    return 0;
}
```

↑のコードの描画結果を見る限りでは，多分合ってる気がする．
（青いのが回転した矩形で，白いのが求めた矩形．）

![Fig.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3574818/a5b482d8-8f49-bf05-bc88-8534d7c76347.png)

![Fig2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3574818/026848b2-31f9-0f4e-c31b-d06456bf97a5.png)
【Git】自分のリポジトリに他者がpushできるようにする

## 発生した問題
Githubのアカウントを既存のものとは別に新しく作った際に、既存アカウント→新規アカウントのリポジトリへのpushがうまくいかなかった

## 前提
[Githubのリファレンス](https://docs.github.com/ja/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-user-account-settings/permission-levels-for-a-personal-account-repository)から抜粋

>個人アカウントが所有するリポジトリの所有者は 1 人です。 所有権のアクセス許可を別の個人アカウントと共有することはできません。

リポジトリを作った段階だとそのリポジトリに対して操作できるのは**リポジトリを作った人のみ**ということですね。

そのためその状態で別のアカウントからそのリポジトリにpushしようとしてもパーミッションエラーになります。（cloneはできる）

publicでリポジトリを作成しても他者にめちゃくちゃにされないのは安心ですね。

## 解決方法
pushしたアカウントを**コラボレーター**としてリポジトリに招待します。
githubのsetting>Collaboratorsから招待したいアカウントを検索して招待してあげます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3577451/16fa579f-a62b-1ced-d343-26fa24921d8e.png)

そしたらそのアカウントのメールアドレスに対して招待メールが届くので認証してあげれば、そのアカウントはそのリポジトリに対して操作ができるようになります。

## おわりに
今回は（自分のアカウント間で招待しただけなので）コラボレーターの権限は制限していませんが、コラボレーターにどの程度の権限を渡すかはちゃんと検討した方がよいと思います。

PS：記事の更新をさぼっていたのでこれからは小さな記事でもちょこちょこ投稿するようにしたい、、、、！（決意）
【2024年04月版】Proxmox で Cluster構築【初心者向け】

# はじめに

Proxmox　で HAや分散ストレージを構築するためのCluster構築メモ

# 環境

- Proxmox8.1 x 3台

:::note info
Proxmoxのインストールは、[【2024年04月版】Apple silicon (M1/M2/M3) de Proxmox【Proxmox on UTM】](https://qiita.com/nouernet/items/c7b116fe332fc9ed93aa)等を参考にしてください。
:::

# Cluster 構築

1つのノードの管理コンソールで、「Datacenter」から「Cluster」を選択し「Create Cluster」を押下する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/143127/7ed224c4-81e1-529d-704c-b89bef3790b9.png)

「Cluster Name」を適当に入力し「Create」を押下する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/143127/bc65412e-4dba-2141-50b0-2b872e299494.png)

「TASK OK」が表示されれば完了。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/143127/8d7cb01a-a11a-5d69-8bd9-e8116b8741d2.png)

「Datacenter」から「Cluster」を選択し「Join Information」を押下する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/143127/eefcb05c-bad6-873c-9873-1380ff3aaa14.png)

「Copy Information」を押下する。クリップボードにClusterの情報がコピーされる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/143127/1f453e04-8f23-9065-fd29-b6d8c2625f2c.png)

別のノードの管理コンソールで、「Datacenter」から「Cluster」を選択し「Join Cluster」を押下する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/143127/ed274124-40af-3682-d67b-7077938c45fa.png)

「Information」にコピーされたClusterの情報をペーストする。「Password」を入力して、「Join」ボタンを押下する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/143127/585fa657-8fcc-235c-f7ab-8a12f1ff5315.png)

エラーが表示されるが、リロードして、元のノードと追加したノードが表示されていれば成功。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/143127/2dbfa06d-f102-789b-2210-d5ef8b4bc2f6.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/143127/e173d09d-2d79-179d-e082-e3bed73ff2fb.png)

同じ手順で他のノードも追加できる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/143127/058bd010-2ee1-e67b-f8f1-1a150665ee72.png)

# さいごに

かんたんでしたね。

# 関連リンク

- [UTM](https://mac.getutm.app/)
- [Proxmox](https://www.proxmox.com/en/)
- [Proxmox-Port](https://github.com/jiangcuo/Proxmox-Port)
- [Proxmox-Port Wiki](https://github.com/jiangcuo/Proxmox-Port/wiki)
- [【2024年04月版】Apple silicon (M1/M2/M3) de Proxmox【Proxmox on UTM】](https://qiita.com/nouernet/items/c7b116fe332fc9ed93aa)
- [【2024年04月版】MacでProxmoxのインストールUSBを作成【diskutil, dd】](https://qiita.com/nouernet/items/273fdce0e77f0b2528bd)
- [【2024年04月版】Proxmoxのストレージタイプ【iSCSI, LVM, NFS, Ceph, GlusterFS】](https://qiita.com/nouernet/items/9115d497fd62930bbfcb)
- [【2024年04月版】Proxmox で Cluster構築【初心者向け】](https://qiita.com/nouernet/items/33e35409527b1c83df97)
- [【2024年04月版】Proxmox で Ceph/CephFS構築【初心者向け】](https://qiita.com/nouernet/items/120c5d8c8b96f881f077)
ファインマン・テクニックの要約の要約　アプトプットの方法

## 元の記事

https://qiita.com/KanNishida/items/e2e926bb194fd280c6d9

冗長的な部分があったためさらに要約する。

##　格言
あなたの知識の究極テストは他の人にうまく伝えることができるかどうかだ。
The ultimate test of your knowledge is your capacity to convey it to another.

## 伝えたいこと
理解、は簡単な言葉で誰かに伝えられることだ。

また、理解していないことは伝えることでわかる。

## 4ステップ
- 難しいことばを使わず、子供にもわかるように説明する。まとめる。
- 説明し、まとめるとうまく、説明できない箇所がわかる。そこが理解していない箇所。それについて学習する。
- もう一度まとめ直す。情報を構造化する。ミクロの理解をしていくとマクロの視点がなくなっていく。
- 他人に伝える。伝えることで足りない視点がわかる。フィードバックが得られる。

## 最後に
みんなもせっかくなら理解したことをアウトプットしてみよう。
フィードバックもまってます。
参照リクエスト

# はじめに
更新系APIにおける参照リクエストに必要な機能をメモする。

# 参照リクエストの目的の定義
```plantuml
サーバA-> サーバB : 参照リクエストRequestID=001
サーバB-->サーバA:状態=更新済みor取消or業務エラーorデータなし
```
シーケンス図は上記のように単純ではあるが、参照リクエスト単独で考えるのではなく、具体的な利用するシナリオを定義すると、多くの必要な機能があることがわかる。
ここでは、次の2つのシナリオに対して参照リクエストに必要な機能を検討する。
* [更新系APIの状態不整合の解決策　参照リクエスト＆リトライ (A03)](https://qiita.com/drafts/e488d49a56183ed21774/edit)
* 取消リクエストのタイムアウトによる状態不整合

:::note info
目的外利用の例
* 不整合が発生していないかを確認するために、定期的にサーバAの保持するすべてのRequestIDの参照リクエストを送信する。
→一気に大量の参照リクエストを送信されるとサーバBの性能問題の原因になる。
:::

# 必要な機能
#### 必要なパラメータ
参照リクエストにRequestIDを設定すると、サーバBの状態を返却する。

:::note info
RequestID vs TransactionID
以下の例のように、更新リクエストのレスポンスにTransactionIDが設定されるとしても、レスポンスが通知されないシナリオ（ [更新系APIの状態不整合の解決策　参照リクエスト＆リトライ (A03)](https://qiita.com/drafts/e488d49a56183ed21774/edit)）考慮すると、参照リクエストをRequestIDキーでリクエストできるようにするのはマストである。

```plantuml
サーバA-> サーバB : 更新リクエストRequestID=001
サーバB-->サーバA:処理済み　TransactionID=ABC
```


:::

:::note alert
記載中
#### 状態コード

状態があいまいにしない
特にデータなし、処理中の定義があいまい
検索期間の指定
サーバが対応できないとき
必要最小限のパラメータにしたほうがいい
サーバビジーを返却する。
流量制限を考えたほうがいい
:::

#### 状態「処理中」が必要な理由
更新リクエストをサーバBで処理中に、参照リクエストを送信した際のレスポンス値について、定義しておくこと。
ここでは、更新リクエストが、「処理済み」or「業務エラー」に処理が完了するまでは、「更新処理中」で返却すると定義する。

「処理中」の実装の難易度が高い場合、「更新リクエスト送信後、X秒間隔をあけてから参照リクエストを送信してください。」とAPI仕様書に明記することで、実装しないことも考えられるが、何秒開ければ確実に処理されるかわからないことが課題となる。
例えば、サーバBが更新リクエストを3秒で確実に処理できるとしても、ネットワークが混雑していて更新リクエストがサーバBに届くのに時間を要することを考えると、x秒を定義できない。


```plantuml
title 更新リクエスト
サーバA-> サーバB : 更新リクエストRequestID=001
activate サーバB
group 例[更新リクエストの処理中に参照リクエストを送信]
サーバA-> サーバB : 参照リクエストRequestID=001
サーバB-->サーバA:状態=更新処理中
note left:状態遷移:初期状態→更新処理中
end
サーバB-->サーバA:更新リクエスト　処理済み
deactivate サーバB
note left:状態遷移　更新処理中→処理済み
```

```plantuml
title 取消リクエスト
サーバA-> サーバB : 取消リクエストRequestID=001
activate サーバB
group 例[取消リクエストの処理中に参照リクエストを送信]
サーバA-> サーバB : 参照リクエストRequestID=001
サーバB-->サーバA:状態=取消処理中
note left:状態遷移:処理済み→取消処理中
end
サーバB-->サーバA:更新リクエスト　取消
deactivate サーバB
note left:状態遷移　取消処理中→取消
```
footprintに異型padを作りたい

円や長方形、台形ではないpadを作りたい
kicad5 世代では、パッドのプロパティにポリゴン編集があったようですが、8では（７も？）なくなりました。
その代わりの手順です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36775/0f9b9a83-2b34-c7aa-3dcc-35f4ada05baa.png)
パッドを一つ作って右クリック、グラフィック形状としてパッドを編集
F.Cu レイヤに好きにポリゴンで描いたり、他のお絵かきソフトで書いたものを取り込み、線や円弧で電気的につなげる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36775/0d3b697d-d588-87fb-3218-ee273cf2cd7d.png)
右クリック→「パッド編集を終了」
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36775/ba64a2ac-6b03-b655-3d20-437fa79840c8.png)
完成！
依存性注入のメモ

# 依存性注入とは何かChatGPTに聞いてみた
依存性注入（Dependency Injection）は、ソフトウェアエンジニアリングにおける設計原則の一つであり、特定のコンポーネントが他のコンポーネントに依存しないようにするための手法。具体的には、あるオブジェクトが他のオブジェクトを直接生成せずに、外部から提供されることでそのオブジェクトの依存関係を明示的にすること。

### 目的
1\. 結合度の低減
クラス間の依存関係を減らし、各クラスを独立して変更やテストができるようにする。
2\. 柔軟性の向上
依存性注入を使用することで、異なる実装のオブジェクトを簡単に交換することができ、システム全体の柔軟性が向上する。
3\. テスト容易性の向上
外部から依存オブジェクトを注入することで、ユニットテストやモックテストが容易になる。

# ChatGPTの回答を見て
結局コードで書くとどうなるのかが分からなく、言葉の意味も理解することが難しい
主に保守やテストで作りやすくなるといった目的はなんとなく把握することはできた

# 実装してみよう
CarクラスとEngineクラスを用意
車はエンジンがないと動かないので、CarクラスはEngineクラスに依存しているという想定で実装

#### 依存性注入を使った実装
```diff_java
public class Engine() {
    public void start() {
        System.out.println("Engine started");
    }
}

public class Car() {
    private Engine engine;

    public Car(Engine engine) {
        this.engine = engine;
    }

    public void start() {
        engine.start();
        System.out.println("Car started");
    }
}

public static void main(String[] args) {
    Engine engine = new Engine();

    Car car = new Car(engine);

    car.start();
}
```

#### 依存性注入を使わない実装
```diff_java
public class Engine() {
    public void start() {
        System.out.println("Engine started");
    }
}

public class Car() {

    // 違うポイントはコンストラクタ
    public Car() {
        this.engine = new Engine();
    }

    public void start() {
        engine.start();
        System.out.println("Car started");
    }
}

public static void main(String[] args) {
    Car car = new Car(engine);

    car.start();
}
```

# 依存性注入（DI）の実装とそうでない実装の差分
依存性注入（DI）の実装はCarクラスのコンストラクタの引数にEngineクラスを定義して、Carクラスを呼び出す際には、Engineクラスを設定する必要がある

依存性注入を使わない実装はCarクラスのコンストラクタでEnginクラスを生成している

これがChatGPTが言っていた
>あるオブジェクトが他のオブジェクトを直接生成せずに、外部から提供されることでそのオブジェクトの依存関係を明示的にすること。

ということだと思う

# 思ったこと
依存性注入は外部からコンストラクタでの引数や他メソッドでの引数での設定でも良さそう

外部から提供されるというのは、使うクラス側でインスタンス生成されているかどうかということ

# 今後の課題
依存性注入（DI）を使ってテストコードの実装し、使うメリットとかを試してみる
Gitで別ブランチの特定のコミットをCherry-pickする方法

Gitの強力な機能の一つに、別のブランチから特定のコミットを選択して現在のブランチに適用する「Cherry-pick」があります。この機能は、特定の修正や機能のみを他のブランチに追加したいときに非常に便利です。

## 1: Cherry-pickしたいコミットのハッシュ値を特定する

Cherry-pickを行う前に、適用したいコミットの正確なハッシュ値を特定する必要があります。コミットのハッシュ値は、Gitのログから確認できます。

```bash
git log
```

上記のコマンドを実行すると、コミット履歴が表示されます。履歴から、Cherry-pickしたいコミットを探し、そのコミットのハッシュ値をメモします。ハッシュ値は、コミットごとに表示される一意の識別子です。

## 2: Cherry-pickを実行するブランチに切り替える

Cherry-pickしたいコミットを現在のブランチに適用する前に、適切なブランチに切り替える必要があります。Cherry-pickを適用したいブランチにチェックアウトします。

```bash
git checkout ブランチ名
```

## 3: Cherry-pickの実行

Cherry-pickを実行するブランチに切り替えたら、以下のコマンドを使用して、特定のコミットを現在のブランチに適用します。

```bash
git cherry-pick コミットハッシュ値
```

これにより、指定したハッシュ値のコミットが現在のブランチに適用されます。

## 注意点

- Cherry-pickを行うと、新しいコミットが現在のブランチに作成されます。このコミットは、元のコミットの内容を持ちますが、新しいハッシュ値が割り当てられます。
- コンフリクトが発生する可能性があります。Cherry-pick中にコンフリクトが発生した場合は、それを解決し、コミットを完了させる必要があります。

## マージコミットのCherry-pickと`-m`オプション

`git cherry-pick`コマンドにおける`-m`オプションは、マージコミットをCherry-pickする際に使用します。通常のコミットをCherry-pickする場合にはこのオプションは必要ありませんが、マージコミットを処理する際には、どの親ブランチの変更を取り込むかを指定する必要があるため、`-m`オプションが必要になります。

### マージコミットをCherry-pickする際のステップ

1. **マージコミットのハッシュ値を確認**: マージコミットをCherry-pickする前に、そのハッシュ値を`git log`などで確認します。
2. **適切なブランチに切り替え**: Cherry-pickを適用したいブランチに切り替えます。
3. **`-m`オプションを使用してCherry-pick実行**: マージコミットには複数の親が存在するため、`-m`オプションに続けて親の番号(通常は`1`または`2`)を指定してCherry-pickを実行します。

```bash
git cherry-pick -m 親の番号 マージコミットのハッシュ値
```

### 注意点

- **親の選択**: マージコミットが持つ親は通常2つあります。`-m`オプションに続けて、どの親の変更を適用するかを指定する必要があります。
- **コンフリクトの可能性**: マージコミットをCherry-pickすると、コンフリクトが発生する可能性が高くなります。コンフリクトが発生した場合は、手動で解決し、Cherry-pickを完了させる必要があります。

### マージコミットのCherry-pickの使用例

`master`ブランチに`feature`ブランチがマージされたマージコミットを現在のブランチに適用したい場合、`master`が第1親、`feature`が第2親となります。この場合、以下のようにコマンドを実行します。

```bash
git cherry-pick -m 1 マージコミットのハッシュ値
```

これにより、マージコミットの第1親(`master`ブランチ)の変更が現在のブランチに適用されます。

## まとめ

GitのCherry-pick機能を使用すると、別のブランチから特定のコミットを現在のブランチに簡単に適用できます。この方法は、特定の修正や機能のみを選択的に取り込みたい場合に特に便利です。しかし、コンフリクトの可能性に注意しながら慎重に操作することが重要です。
firebaseとの接続方法

# 初めに
この記事は初心者視点での説明になります。正確性に欠ける可能性がございますが、ご了承ください。明らな誤りがありましたら、ご指摘くださると幸いです。

## きっかけ
Next.jsでアプリを作成したいと思い、バックエンド機能の実装でお手軽に使えそうなfirebaseを選択しました。その際の初期設定の忘備録になります。

## firebaseとは？
> Firebase は、ユーザーに愛されるアプリやゲームの構築と拡大を支援するアプリ開発プラットフォームです。
-firebase公式HPより-

「SDK」（ソフトウェア開発キット）のひとつであり、少ない労力でアプリケーションを開発できるよう、プログラム、API、サンプルコードなどをパッケージ化したものです。firebaseを活用することで認証機能やDBをフロントエンドのみの知識で簡単に開発できるようになります。

## 初期設定
1. 会員登録が完了したら、任意のプロジェクトを立ち上げてください
![スクリーンショット 2024-04-03 14.54.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2918094/af3cbe41-60f0-b680-f696-794193e103f8.png)

1. プロジェクトを立ち上げたら、アプリの追加を行なってください
![スクリーンショット 2024-04-03 14.55.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2918094/a8a982f8-f61c-15ff-cbf1-54bb0c6eba0e.png)

1. アプリを追加したら、`npm install firebase`をプロジェクト下で実行してください

1. プロジェクトの設定からマイアプリを確認し、`firebaseConfig`を見つけましょう
1. 環境変数として以下の内容を保存してください
```
NEXT_PUBLIC_APIKEY=xxxxxxxxxxxxx_xxxxxxxxx_xxxxxxxxxxxxx
NEXT_PUBLIC_AUTHDOMAIN=xxxxxxxxxxxxx_xxxxxxxxx_xxxxxxxxxxxxx
NEXT_PUBLIC_PROJECTID=xxxxxxxxxxxxx_xxxxxxxxx_xxxxxxxxxxxxx
NEXT_PUBLIC_STORAGEBUCKET=xxxxxxxxxxxxx_xxxxxxxxx_xxxxxxxxxxxxx
NEXT_PUBLIC_MESSAGINGSENDERID=xxxxxxxxxxxxx_xxxxxxxxx_xxxxxxxxxxxxx
NEXT_PUBLIC_APPID=xxxxxxxxxxxxx_xxxxxxxxx_xxxxxxxxxxxxx
```

以上でfirebaseとの接続準備が完了です！
`lib/FirebaseConfig.js`を作成してマイアプリのコードをそのまま記載してもいいですし、環境変数として`.env`ファイルに別途保管しても大丈夫です。

`.env`ファイルに記載する場合は`lib/FirebaseConfig.js`で変数を呼び出すことを忘れないでください。


## 参考
https://techtarget.itmedia.co.jp/tt/news/1911/07/news06.html

https://firebase.google.com/?hl=ja
CF7で選択した内容によって、メールの送信先を自動で振り分ける方法

# はじめに

WordPressではCF7などを利用することで、簡単にお問い合わせフォームの作成やお問い合わせ時の自動返信メールの作成などを行うことができます。

メールの文章には、フォームで入力された答えをタグとして埋め込むことができますが、選択された値によって自動返信メールの送り先を変えたいと思うことがあります。

本記事では、お問い合わせフォームの選択肢ごとにメールの送信先を振り分ける方法について記載します。

<a id="#Chapter1"></a>
# Contact Form 7とは

https://ja.wordpress.org/plugins/contact-form-7/

Contact Form 7は、お問い合わせフォームなどのフォームの作成や管理を簡単に行うことができる、WordPressの拡張プラグインです。

非常に多く利用されており、Contact Form 7のための拡張プラグイン(Flamingoなど)も充実しています。

<a id="#Chapter2"></a>
# 手順

まず簡単なCF7のフォームを作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2472147/c56a36c7-eaa9-4012-3b57-19983896e1ba.png)

選んだ食べ物によってメールの送り先を分けるために、パイプ( | )を利用します。
パイプの左側はフォームに表示され、メールタブでタグを指定したときにパイプの右側が代入されます。

詳しくは以下のページに記載されています。
https://contactform7.com/ja/selectable-recipient-with-pipes/

appleを選択時「apple@example.com」、bananaを選択時「banana@example.com」、candyを選択時「candy@example.com」宛に送信するようにしたい場合、フォームには以下のように書きます。

フォーム部分
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2472147/9ed0da2b-8132-1cd4-4027-f775ed4ebb31.png)

メール部分
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2472147/5226d56c-b5a0-4b82-6e42-46bd7b414e35.png)

実際の画面(メールアドレスは表示されていない)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2472147/f03c984e-7606-fca3-b233-8fc27d5fb722.png)

上記の方法で、選択項目ごとに送信先メールアドレスを指定することができます。
ブラウザでURLを開くとどうなるか？？



# ブラウザでURLを開いてからブラウザが反映されるまでの一覧の流れについて

## 前提知識 (ICP/IP)

| 層   | 名称               | 規格（プロトコル）                      | 主な利用例                        |
|------|--------------------|----------------------------------------|-----------------------------------|
| 4層  | アプリケーション層 | HTTP, HTTPS, SMTP, POP3, IMAP4, DHCP, DNSなど | Webサイト閲覧、メール、ファイル転送など |
| 3層  | トランスポート層   | TCP, UDP, NetWare/IPなど               | TCP/UDP（データを適切なアプリケーションへ振り分け） |
| 2層  | インターネット層   | IP, ARP, RARP, ICMPなど                | ルーティング、エンドツーエンド通信 |
| 1層  | ネットワークインタフェース層 | Ethernet                            | LAN                               |




1. **URL解析**: ブラウザが入力されたURLを解析し、使用するプロトコル（通常はHTTPまたはHTTPS）を特定します。また、ホスト名やパスなどの構成要素も解析します。
   
2. **DNS解決**: ブラウザはホスト名（例: www.example.com）をIPアドレスに変換するために、DNS（Domain Name System）を使用します。DNSは、与えられたドメイン名から対応するIPアドレスを見つけるための分散型データベースです。
   
3. **TCP接続の確立**: ブラウザは目的のサーバーに対してTCP（Transmission Control Protocol）接続を確立します。これにより、ブラウザとサーバー間での信頼性のあるデータ転送が可能になります。
   
4. **TLSハンドシェイクと暗号化**: もしHTTPSプロトコルを使用している場合、ブラウザとサーバーはTLS（Transport Layer Security）ハンドシェイクを行い、安全な通信を確立します。TLSによってデータは暗号化され、第三者による盗聴や改ざんから保護されます。
   
5. **HTTPリクエストの送信**: ブラウザはHTTP（Hypertext Transfer Protocol）リクエストをサーバーに送信します。このリクエストには、ブラウザが要求するページやリソースの情報が含まれます。
   
6. **サーバーの処理とレスポンスの送信**: サーバーは受け取ったリクエストを処理し、適切なレスポンスを生成します。これには、HTMLページや画像、スタイルシート、JavaScriptファイルなどが含まれます。サーバーはこれらのレスポンスをHTTPレスポンスとしてブラウザに送信します。
   
7. **ブラウザでのレンダリング**: ブラウザは受け取ったレスポンスを解釈し、HTMLを解析してWebページを構築します。画像やスタイルシートなどの他のリソースも必要に応じてダウンロードされ、ページの表示が完了します。
   
8. **接続の終了**: ブラウザとサーバーの間の通信が終了し、TCP接続が閉じられます。これにより、ブラウザは必要に応じて他のページやリソースを取得するために新しい接続を確立する準備が整います。

この一連の流れによって、ブラウザはURLを開いてからユーザーに反映されるまでの全体のプロセスが実行されます。

## 手順ごとの使用する層のテーブル

| ステップ | 使用する層 |
|---------|-------------|
| 1. URL解析 | アプリケーション層 |
| 2. DNS解決 | アプリケーション層 |
| 3. TCP接続の確立 | トランスポート層 |
| 4. TLSハンドシェイクと暗号化 | トランスポート層 |
| 5. HTTPリクエストの送信 | アプリケーション層 |
| 6. サーバーの処理とレスポンスの送信 | アプリケーション層、トランスポート層 |
| 7. ブラウザでのレンダリング | アプリケーション層 |
| 8. 接続の終了 | トランスポート層 |
長いテキストを展開できる「続きを読む」を持ったViewを作る


# はじめに
SwiftUIを使用して、コンテンツの一部のみを初めに表示し、ユーザーが「続きを見る」を選択することでメッセージ全体を表示するViewを考えていたときのコードになります。
ユーザーが「続きを見る」をタップすると、メッセージが展開されると共に「続きを見る」表示もサッと消えます。

# 利用環境
Xcode 15.3
Swift 5.10

# 「続きを見る」ボタンを持ったViewの作成
```swift
import SwiftUI

struct MessageView: View {
  // 表示するメッセージを定義します。
  let message: String
  
  // メッセージの表示状態を制御するための状態変数です。
  @State private var isExpanded = false

  var body: some View {
    VStack(alignment: .leading) {
      // メッセージを表示します。`isExpanded`の値に応じて行数の制限を変更します。
      Text(message)
        // 行数制限を設定します。
        // ここでは初期表示2行で設定しています。
        .lineLimit(isExpanded ? nil : 2)
        // アニメーションを設定します。
        .animation(.easeInOut, value: isExpanded)
        // テキストが適切に折り返されるようにします。
        .fixedSize(horizontal: false, vertical: true)
        // テキストを左揃えにします。
        .frame(maxWidth: .infinity, alignment: .leading)

      // メッセージが展開されていない場合、「続きを見る」ボタンを表示します。
      if !isExpanded {
        Button(action: {
          // ボタンが押されると、メッセージの表示状態を切り替えます。
          isExpanded.toggle()
        }) {
          Text("続きを見る")
            .foregroundColor(.secondary)
        }
      }
    }
  }
}
```

## MessageViewの呼び出し
```swift
import SwiftUI

struct ContentView: View {
  var body: some View {
    // サンプルテキストです。このテキストを`MessageView`に渡して表示します。
    let sampleText = "ここに長いテキストを入れます。ここに長いテキストを入れます。ここに長いテキストを入れます。ここに長いテキストを入れます。ここに長いテキストを入れます。ここに長いテキストを入れます。ここに長いテキストを入れます。ここに長いテキストを入れます。"
    MessageView(message: sampleText)
      .padding()
  }
}
```

## スクリーンショット
![Simulator Screenshot.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251940/e5262096-9019-00fa-b07b-f31d3b036a44.jpeg)

![Simulator Screenshot3.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251940/2db8edef-8cf7-f768-fff8-77e07c25c185.jpeg)

# 「続きを読む」を行末に置くパターン
```swift
import SwiftUI

struct MessageView: View {
  // 表示するメッセージを定義します。
  let message: String
  
  // メッセージの表示状態を制御するための状態変数です。
  @State private var isExpanded = false

  var body: some View {
    HStack(spacing: 0) {
      Text(message)
        // 行数制限を設定します。
        // ここでは初期表示1行で設定しています。
        .lineLimit(isExpanded ? nil : 1)
        // アニメーションを設定します。
        .animation(.easeInOut, value: isExpanded)
        // テキストがオーバーフローした場合、末尾を省略します。
        .truncationMode(.tail)
        // テキストが適切に折り返されるようにします。
        .fixedSize(horizontal: false, vertical: true)
        // テキストを左揃えに設定し、展開時に横幅を広げます。
        .frame(maxWidth: isExpanded ? .infinity : nil, alignment: .leading)

      // メッセージが展開されていない場合、「続きを見る」ボタンを表示します。
      if !isExpanded {
        Button(action: {
          // ボタンが押されると、メッセージの表示状態を切り替えます。
          isExpanded.toggle()
        }) {
          Text("続きを読む")
            .font(.subheadline)
            .foregroundColor(.secondary)
        }
      }
    }
    .frame(maxWidth: .infinity, alignment: .leading)
  }
}
```

## スクリーンショット
![Simulator Screenshot - iPhone 15 - 2024-04-03 at 08.43.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251940/79c1e766-b1dc-4606-ffe9-9abefb7c3804.png)

# 最後に
行末に「続きを見る」を設ける方法は、初期表示2行の際に期待通りの表示とならないなど、まだまだ改善が必要そうです。
また、新たな発見がありましたら書き留めたいと思います。
【Swift】#availableと#unavailable 

# はじめに
現在、SwiftUIを使ったiOSアプリを開発しているのですが、その中で「#unavailable」ディレクティブに出会いました。へーこんなのあるんだね。という気持ちになりましたので、少しメモしておきたいと思います。
このディレクティブは、特定の機能が使えないプラットフォームやバージョンを指定する際に使えます。

# 利用環境
Xcode 15.3
Swift 5.10

# #availableでの書き方
特定のiOSバージョン以上でのみ利用可能な機能や実装を安全に使用するためには、#availableディレクティブを使ってチェックを行います。
例えば、iOS15以上の機能を使いたい場合は次のように書けます。
```
if #available(iOS 15, *) {
    // iOS15以上で実行したいコード
} else {
    // iOS15未満で実行したいコード
}
```

# #unavailableを使った書き方
一方、#unavailableディレクティブを使用すると、特定のバージョン未満の場合にのみコードを実行させることができます。
```
if #unavailable(iOS 15) {
    // iOS15未満で実行したいコード
} else {
    // iOS15以上で実行したいコード
}
```

今までは、特定のiOSバージョン未満のみ実行したいコードがあった場合も、#availableディレクティブにelseを付けて利用していたのですが、それもシンプルに書き直す事が出来ました。

# 最後に
この記事が、Swiftに興味のある方にとって少しの気付きに繋がれば幸いです。
Pythonを使用してExcelの表を画像に変換する方法

データ処理と情報共有の過程で、Excelシートは強力なデータ管理ツールとして広く使用されており、大量のデータや複雑な計算結果を明瞭かつ整然と表示することができます。しかし、Excelシートの内容を画像形式で表示や共有する必要がある場合もあります。例えば、レポートのプレゼンテーション作成、オンラインコミュニケーション、ソーシャルメディア共有などです。Pythonを使用することで、Excelシートを簡単に画像に変換し、効率を向上させ、多様な情報表示の要求に応えることができます。本記事では、Pythonを使用して**Excelシートを画像（PNG、JPEG、BMPなどの形式）に変換する方法**を紹介します。

- **[Excelシート全体を画像に変換する方法](#excelシート全体を画像に変換する方法)**
- **[指定したセル範囲のシートを画像に変換する方法](#指定したセル範囲のシートを画像に変換する方法)**

---

この記事では、[Spire.XLS for Python](https://www.e-iceblue.com/Download/Spire-XLS-Python.html)を使用します。PyPIからインストールできます：`pip install Spire.XLS`。

[無料ライセンスの申請](https://www.e-iceblue.com/TemLicense.html)

## Excelシート全体を画像に変換する方法
指定したシートを直接画像に変換するために、**Worksheet.ToImage()** メソッドを使用することができます。その後、**Image.Save()** メソッドを使用して画像をファイルに保存することができます。さらに、シートに余白がある場合は、上下左右の余白を0に設定してから画像に変換するために、**Worksheet.PageSetup** プロパティを使用することもできます。これにより、空白部分を削除することができます。
以下は手順です：
1. 必要なモジュールをインポートします。
2. **Workbook** オブジェクトを作成します。
3. **Workbook.LoadFromFile()** メソッドを使用してExcelファイルを読み込みます。
4. **Worksheet.PageSetup** プロパティを使用して余白を削除します。
5. **Worksheet.ToImage()** メソッドを使用してシートを画像に変換します。
6. **Image.Save()** メソッドを使用して画像を指定した形式で保存します。
7. リソースを解放します。

**コード例：**
```python
from spire.xls import *
from spire.common import *

# Workbookオブジェクトの作成
workbook = Workbook()

# Excelファイルの読み込み
workbook.LoadFromFile("サンプル.xlsx")

# ワークシートの取得
sheet = workbook.Worksheets.get_Item(0)

# ページの余白を削除
pageSetup = sheet.PageSetup
pageSetup.TopMargin = 0
pageSetup.BottomMargin = 0
pageSetup.LeftMargin = 0
pageSetup.RightMargin = 0

# ワークシートを画像に変換
image = sheet.ToImage(sheet.FirstRow, sheet.FirstColumn, sheet.LastRow, sheet.LastColumn)

# 画像の保存
image.Save("output/ワークシートから画像への変換.png", ImageFormat.get_Png())

workbook.Dispose()
```

**変換結果：**
![Excelシート全体を画像に変換する方法](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/278784/8c02e86b-3eb5-94fa-470d-132a652e69ca.png)

## 指定したセル範囲のシートを画像に変換する方法
**Worksheet.ToImage()** メソッドを使用して指定したシートを直接画像に変換する場合、開始行列と終了行列を指定して、指定した範囲のセルを画像に変換することができます。
以下は手順です：
1. 必要なモジュールをインポートします。
2. **Workbook** オブジェクトを作成します。
3. **Workbook.LoadFromFile()** メソッドを使用してExcelファイルを読み込みます。
4. **Worksheet.PageSetup** プロパティを使用して余白を削除します。
5. **Worksheet.ToImage()** メソッドを使用して、開始行、開始列、終了行、終了列を指定して、指定した範囲のセルを画像に変換します。
6. **Image.Save()** メソッドを使用して画像を指定した形式で保存します。
7. リソースを解放します。

**コード例：**
```python
from spire.xls import *
from spire.common import *

# Workbookオブジェクトの作成
workbook = Workbook()

# Excelファイルの読み込み
workbook.LoadFromFile("サンプル.xlsx")

# ワークシートの取得
sheet = workbook.Worksheets.get_Item(0)

# 指定範囲のセルを画像に変換
image = sheet.ToImage(3, 2, 12, 6)

# 画像の保存
image.Save("output/セル範囲から画像への変換.png", ImageFormat.get_Png())

workbook.Dispose()
```

**変換結果：**
![指定したセル範囲のシートを画像に変換する方法](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/278784/54efcc74-4e1c-3e02-ced9-1b47e75de4ba.png)

本記事では、Excelシート全体または指定したセル範囲を画像に変換する方法について説明しました。
さらなるExcel操作のテクニックについては、[Spire.XLS for Pythonのチュートリアル](https://www.e-iceblue.com/Tutorials/Python/Spire.XLS-for-Python/Program-Guide/Spire.XLS-for-Python-Program-Guide-Content.html)を参照してください。
もし問題が発生した場合は、[Spire.XLSフォーラム](https://www.e-iceblue.com/forum/spire-xls-f4.html)にアクセスして技術サポートを受けることができます。
UnityでAndroidのアプリをビルドする際にアイコンを漏れなく設定しておくコード


Androidアプリのアイコンは通常のアイコン、AdaptiveIcon,Roundアイコンと何故か３種類もあります。

それなりの規模のPJだと基本的にBatchModeでビルド関数叩いて、その際に引数で環境情報とか何某を入れるみたいな実装をすると思います。
そんなときにコード側でアイコンを変えるのが普通に面倒＆忘れがちだったので備忘も兼ねて残しておきます。

### 環境
- Unity2021.3.36f1


### コード

``` cs:build.cs
//まずはAdaptiveとRound以外を設定
var icons = PlayerSettings.GetIconsForTargetGroup(BuildTargetGroup.Android);
for (var i = 0; i < icons.Length; i++)
{
    icons[i] = 標準のアイコンTexture;
}
//標準のアイコンを反映
PlayerSettings.SetIconsForTargetGroup(BuildTargetGroup.Android, icons);

//続いてAdaptive
var adaptive = PlayerSettings.GetPlatformIcons(BuildTargetGroup.Android,
    UnityEditor.Android.AndroidPlatformIconKind.Adaptive);

for (var i = 0; i < adaptive.Length; i++)
{
    adaptive[i].SetTexture(AdaptiveIconの背景レイヤー画像, 0);
    adaptive[i].SetTexture(AdaptiveIconの前面レイヤー画像, 1);
}
//Adaptiveを判定
PlayerSettings.SetPlatformIcons(BuildTargetGroup.Android, UnityEditor.Android.AndroidPlatformIconKind.Adaptive, adaptive);

//Roundアイコン
var round = PlayerSettings.GetPlatformIcons(BuildTargetGroup.Android,
    UnityEditor.Android.AndroidPlatformIconKind.Round);
for (int i = 0; i < round.Length; i++)
{
    round[i].SetTexture(Round用のアイコン画像);
}
PlayerSettings.SetPlatformIcons(BuildTargetGroup.Android, UnityEditor.Android.AndroidPlatformIconKind.Round, round);

```

これできっと大丈夫！
M3チップMacでdocker-compose upを行った際にエラーが出て環境構築できない問題の解決方法

# 概要
新しく買ったM3 Macbook Airでdockerを使ってMySQLの環境を構築しようとした際にエラーが出てしまい、少し躓いたので備忘録として残しておきます。

# 内容
## 起動させたいコンテナ
今回は、下記yamlファイルのコンテナを起動させようとしました。
```yaml:docker-compose.yaml
version: '3.3'
services:

  mysql:
    image: mysql:5.7
    container_name: db-for-go
    command: 
        - --character-set-server=utf8mb4
        - --collation-server=utf8mb4_unicode_ci
        - --sql-mode=ONLY_FULL_GROUP_BY,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
    environment:
        MYSQL_ROOT_USER: ${ROOTUSER}
        MYSQL_ROOT_PASSWORD: ${ROOTPASS}
        MYSQL_DATABASE: ${DATABASE}
        MYSQL_USER: ${USERNAME}
        MYSQL_PASSWORD: ${USERPASS}
        TZ: 'Asia/Tokyo'
    ports: 
        - "3306:3306"
    volumes:
        - db-volume:/var/lib/mysql

volumes:
  db-volume:
```
## エラー内容
```zsh:実行したコマンド
docker-compose up
```
上記コマンドを実行した際に下記のエラーが発生しました。
```zsh:表示されたエラー
no matching manifest for linux/arm64/v8 in the manifest list entries
```
調べてみた感じ、どうやらデフォルトではIntelやAMDといったx86アーキテクチャ向け提供されているDockerイメージを取得しようとしているっぽい。発生したエラーは、「M3チップはarm64アーキテクチャであるため、そのイメージは使えないよ」という内容です。

## 解決方法
### 方法①
"docker-compose up"コマンドを実行する前に、事前に必要なイメージをarm64用として引っ張ってくれば解決しそうです。今回は、MySQlのイメージが必要なので
```zsh:事前に実行するコマンド
docker pull --platform linux/amd64  mysql:5.7
```
を先に実行し、その後"docker-compose up"を行ったら解決できました。

### 方法②
yamlファイルに一行追記すればOKでした。
```yaml:docker-compose.yaml
version: '3.3'
services:

  mysql:
    image: mysql:5.7
    container_name: db-for-go
    platform: linux/amd64 # この行を追記
    command: 
        - --character-set-server=utf8mb4
        - --collation-server=utf8mb4_unicode_ci
        - --sql-mode=ONLY_FULL_GROUP_BY,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
    environment:
        MYSQL_ROOT_USER: ${ROOTUSER}
        MYSQL_ROOT_PASSWORD: ${ROOTPASS}
        MYSQL_DATABASE: ${DATABASE}
        MYSQL_USER: ${USERNAME}
        MYSQL_PASSWORD: ${USERPASS}
        TZ: 'Asia/Tokyo'
    ports: 
        - "3306:3306"
    volumes:
        - db-volume:/var/lib/mysql

volumes:
  db-volume:
```
この状態で"docker-compose up"を実行すると解決しました。

# さいごに
今回は、yamlファイルで定義した設定でコンテナを作成しようとした際に発生したエラーの対処方法について紹介しました。
2つの方法があったが、使い分けとしては
- 方法①：IntelやAMDなどのチップを利用したPCでも同じ環境を構築したい場合
- 方法②：基本的にAppleシリコンチップのようにarm64アーキテクチャのPCのみで環境を構築する場合

といった形になると個人的に予想しています。


# 備考
Zennにも同じ記事を作成しています。
[Zenn](https://zenn.dev/uz_jp/articles/58f19fc5c72213)
laravelのassetヘルパ関数

## laraveのassetヘルパ関数について
参考URL：https://readouble.com/laravel/9.x/ja/helpers.html#method-asset

### 概要
- asset()関数はlaravelのヘルパ関数
- asset()は、publicフォルダのディレクトリパスを返す

### 使い方
```
asset('storage/' . $post->image)
```
これでpublicディレクトリ内のstorageという
ディレクトリの中のファイルにアクセスする。
つまりassetの中にpublicの中のファイルパスを書けばOK


```
$path = $request->file('image')->store('photos','public')
```
例えばフォームから画像を取得した際にstoreメソッドを使って、storage/app/public/
というローカルないのディレクトリに保存をする。
このstorageディレクトリは外部からのアクセスができないため、
リンクを貼ることで公開できる。
```
php artisan storage:link
```

public/storage にリンクが貼られpublicディレクトリは外部アクセスできるので、公開ができる。
ドメインを[お名前.com]のメールサービスに使いながら、Webサービスにも使う場合のDNS設定


結論： [ここの](https://www.onamae.com/guide/om/p/26)説明通り、メールサービス内のDNS設定を入りらないといけない。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3762055/4d035392-1fe6-458b-bd75-279e9789c012.png)


---
ちなみに、いつもの「DNS設定/転送設定－ドメイン一覧」ページ(下図)で設定しても効かないようです
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3762055/f5d2a748-30ac-7e81-ab20-6962cbb696de.png)
Java Silver向け 例題 staticイニシャライザとインスタンス初期化子の問題

次のコードの出力結果を答えてください。

```java
public class AbcdExample {
    static {
        System.out.println("ABCD");
    }

    public static void main(String[] args) {
        System.out.println("EFGH");
        AB member = new AB();
        AB number = new AB();
    }
}


class AB {
    AB() {
        System.out.println("AB");
    }
    {
        System.out.println("CD");
    }
    static {
        System.out.println("EF");
    }
}
```
正解

↓
↓
↓
↓
↓
↓
↓

```
ABCD
EFGH
EF
CD
AB
CD
AB
```

1. abcdExample クラスの静的初期化子が実行されます。静的初期化子はクラスがロードされる際に一度だけ実行されます。そのため、最初に "ABCD" が出力されます。

2. main メソッドが実行されます。"EFGH" が出力されます。

3. AB クラスの静的初期化子が実行されます。同様に、これもクラスがロードされる際に一度だけ実行されます。そのため、"EF" が出力されます。

4. AB クラスのインスタンス member が生成されます。この時点で、インスタンス初期化子（インスタンスブロック）が実行され、"CD" が出力されます。次に、コンストラクタが実行され、"AB" が出力されます。

5. 同様に、AB クラスのインスタンス number が生成されます。これも同じく、インスタンス初期化子が実行され、"CD" が出力され、その後コンストラクタが実行され、"AB" が出力されます。
Ruby On Railsにおける基本コマンド

# Ruby On Railsにおける基本コマンド

## 背景説明
プログラミングスクールで学んだ**Ruby on Rails**の基本操作方法をまとめる。
メモとして使う予定。
## アプリ作成。
webアプリケーションを作成するコマンド。アプリを作成したいフォルダーで実行。[^1]

[^1]:アプリの名前は**appName**にしています。
### 基本
```
rails new appName
```
### よく使うオプション
バージョン7.0.0、データベースにmysqlを指定
```
rails _7.0.0_ new appName -d mysql
```
## コントローラー作成

コントローラーを作成するコマンド。[^2]

[^2]:コントローラーの名前は**mails**にしています。名前を必ず**複数形**にすること
```
rails g controller mails
```

## モデル作成
モデル作成するコマンド。[^3]

[^3]:モデルの名前は**mail**にしています。
```
rails g model mail
```
## マイグレーション反映
マイグレーションを実行し、データベースに反映
```
rails db:migrate
```
RHEL8 期限切れになったrootパスワードの再設定

## 背景
rootを含む各ユーザーにパスワードの有効期限を設定しているRHEL8システムに長期間アクセスしていなかったところ、パスワードが期限切れとなり、rootを含むすべてのユーザーでログインできなくなってしまいました。
具体的には、ログインを試みると、`Your account has expired; please contact your system administrator` となってしまう状態でした。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2164968/659335f1-3715-81e0-3fbe-cbfe5b657c1c.png)

参考：[root account expired](https://access.redhat.com/solutions/7000186)

## root パスワードのリセット
こちらの記事を参照して、rootのパスワードを再設定した記録です。

https://access.redhat.com/solutions/1192

RHEL8で用意されている2つのオプションのうち、recommended となっている 1) で実施します。
> 1) With rescue media (recommended - least risky)
> 2) Without rescue media (risky)

記事に書かれている手順は次のとおりです：

Using Rescue Media (boot.iso)

  1.  Boot from boot.iso, choose Troubleshooting > Rescue a system
  2.  Mount the system normally (not read-only), i.e. default, OK
  3.  At shell prompt, chroot /mnt/sysimage
  4.  passwd root
  5.  reboot

### 1. boot.iso のダウンロード

[こちらのページ](https://access.redhat.com/documentation/ja-jp/red_hat_enterprise_linux/8/html/system_design_guide/downloading-beta-installation-images_preparing-for-your-installation)に掲載されている[リンク先](https://access.redhat.com/downloads/content/rhel)から、`Red Hat Enterprise Linux 8.9 Boot ISO` (ファイル名：rhel-8.9-x86_64-boot.iso、サイズ：979,369,984 bytes) をダウンロードします。

### 2. レスキュー・メディア(boot.iso)からのレスキューモード起動
当RHELシステムはVMwareのVMとして稼働しています。以下、その環境での手順例です。

1. VMwareのデータストアにダウンロードした boot.iso をアップロード
2. VMの設定の編集
   - パワーオン時に当boot.isoファイルをマウントするよう設定
   - 当boot.isoイメージからbootするよう、「次回起動時に、強制的に EFI セットアップ画面に入る」にチェック

3. VMパワーオン

4. `Troubleshooting` を選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2164968/e6e6ef55-919e-20da-fcb7-17344fe75dc9.png)

5. `Rescue a Red Hat Enterprise Linux system` を選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2164968/a9b09bd4-0cb9-3bd3-5d9f-336f23608313.png)

6. `1`を入力して`Enter`
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2164968/cb1c4144-335f-6531-a00b-ba056658dfbc.png)

7. `Enter` でshell起動
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2164968/15d906e8-09f7-5201-0716-9a4ae2993601.png)

### 3. パスワード変更
1. （参照している記事とは違いますが）画面の指示にしたがい、`chroot /mnt/sysroot` を実行
2. `passwd root` でrootのパスワードを再設定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2164968/408d9233-6102-2def-9dcb-685c7c5287ad.png)

3. shellからexitすることにより、自動的にreboot
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2164968/751bb590-c513-fe25-388b-5d5cd21edf11.png)

reboot後、新しく設定したパスワードでログイン可能になりました。
HTML + CSS - 基本縦書きにしたいが半角英数字は回転させたくない

# 概要

CSSで以下プロパティを指定すると文章を縦書きにできる

```
writing-mode: vertical-rl;
-ms-writing-mode: tb-rl
```

だが縦書きにした場合、半角アルファベット・半角数字が右に90度回転してしまう

これは「日本語の中に英語が現れた場合、回転して表記する」という意味では正しいのだが、そうしたくない場合もある

この場合、個々の文字を左に90度回転 ( `transform: rotate` ) させることで、位置を保持できるようだ


# 例

<img width="398" alt="image" src="https://github.com/YumaInaura/YumaInaura/assets/13635059/592bf5c3-6657-44da-b0c8-d30627f8c841">

この例ではかなり文字が詰まった感じがするが、余白は適宜調整する

```html
  <div style="writing-mode: vertical-rl; -ms-writing-mode: tb-rl">
    ハロー ワ〜ルド ABC012

    <span style="display: inline-block; transform: rotate(-90deg)">X</span>
    <span style="display: inline-block; transform: rotate(-90deg)">Y</span>
    <span style="display: inline-block; transform: rotate(-90deg)">Z</span>
    <span style="display: inline-block; transform: rotate(-90deg)">0</span>
    <span style="display: inline-block; transform: rotate(-90deg)">1</span>
    <span style="display: inline-block; transform: rotate(-90deg)">2</span>
  </div>
```


# チャットメンバー募集


何か質問、悩み事、相談などあればLINEオープンチャットもご利用ください。

https://line.me/ti/g2/eEPltQ6Tzh3pYAZV8JXKZqc7PJ6L0rpm573dcQ


# プロフィール・経歴

https://github.com/YumaInaura/YumaInaura
XML、JSON、YAML変換などに便利なツールまとめ

データの交換フォーマットとして、XML、JSON、そしてYAMLは今日のソフトウェア開発において不可欠な要素です。特にWeb開発において、これらのフォーマットはデータの構造化、保存、および転送を容易にします。

異なるフォーマット間でデータを変換する必要がある場面や、より読みやすい形式に整形する必要が生じたときによく使われている[ツール](https://toolpro.dev/)をまとめました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35605/07c3afa3-63b8-8b52-9766-e95142045e42.png)

### コード形式に関するツール
JSON形式フォーマット：https://toolpro.dev/json-formatter
JSON圧縮：https://toolpro.dev/json-minifier
XML形式フォーマット：https://toolpro.dev/xml-formatter
XML圧縮：https://toolpro.dev/xml-minifier
YAML形式フォーマット：https://toolpro.dev/yaml-beautifier

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35605/dc428e5f-9f64-78ca-b0fd-ffbbdc4ddb5d.png)

### 変換に関するツール
JSONからXMLへの変換：https://toolpro.dev/json-to-xml
JSONからYAMLへの変換：https://toolpro.dev/json-to-yaml
XMLからJSONへの変換：https://toolpro.dev/xml-to-json
XMLからYAMLへの変換：https://toolpro.dev/xml-to-yaml
YAMLからJSONへの変換：https://toolpro.dev/yaml-to-json
YAMLからXMLへの変換：https://toolpro.dev/yaml-to-xml

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35605/55fd72bd-3dbf-5b65-509e-316052fd522a.png)
型 '{ children: Element; }' には型 'IntrinsicAttributes' と共通のプロパティがありません。 / Type '{ children: Element; }' has no properties in common with type 'IntrinsicAttributes'

## はじめに
`react-router-dom`の`Private`実装時等に、以下のエラーが起きた場合の対処について

![スクリーンショット 2022-04-25 17.59.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2641679/47d47a8d-35cc-7025-dc07-d68d9bc014dc.png)


日本語 : 型 '{ children: Element; }' には型 'IntrinsicAttributes' と共通のプロパティがありません。
英語　： `Type '{ children: Element; }' has no properties in common with type 'IntrinsicAttributes'`

## 結論

propを受け取らないコンポーネントに対し、上の画像のように`<Route path='/' element={<Home/>}/>`という`children`を渡そうとすると発生する様子。
よって下記のように修正した。

```react

<Private>
  <Route path="/" element={<Home />} />
</Private>
// ↓変更
<Route path='/' element={<Private />}>
  <Route path='/' element={<Home/>}/>
</Route>
```



```react
const Private = () => {
  if (isSignedIn) {
    // return children
    // ↓変更
    return <Outlet />
  } else {
    return <Navigate to="/signin" />
  }
}

```

親コンポーネント内で`<Outlet />`を記述することで、実際にはchildren要素が呼び出される。


### 詳細
今回のは、`react-router-dom`のv5からv6にアップデートする際に直面した問題でした。
ちなみに、v6では`Switch`の代わりに`Routes`を使用します。

## おわりに
なんとなく`react-router-dom`は把握できているつもりであったがv6になり思っていたより変更箇所があった。型エラーの学習にもなりラッキーだった。
GitHub上で特定のパッケージを利用しているリポジトリやコードを探す

よく「このパッケージってみんなどんな風に使ってるんだろう」と思うこととかがあるのですが、それを見つける方法がわからず、ググってもコードはあまり出てこないことが多かったです。
GitHub上で検索してもあまり結果が出てこなくて、なんとなく「GitHubの検索使いづらいなぁ」と思っていたのですが、Code Searchという機能を使ってコードの検索ができるようです。

https://github.com/search?type=code&auto_enroll=true

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47327/4cd54e30-694e-1574-058b-e7b8f1d042e9.png)

例えばここで、「npmでprismaを使っているリポジトリを探そう」というときには

```
prisma path:package.json
```

と検索すると出てきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47327/76d29b58-1f7c-a2d1-f271-71ef1cb37a23.png)

早くて便利です。

Composerで "nuwave/lighthouse" を使っているパッケージはこうです。

`path:**/composer.json "nuwave/lighthouse"`

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47327/dd08e42b-9f8d-07d5-5843-3590b77932dc.png)



以前はGitHubは Advanced Search という機能で検索されていたようですが、こちらは現在はあまり結果が出てこないようで、記事などはそちらを案内されるのですが注意が必要です。

なお検索結果のインデックス化などには特定のサイズ以下だったり、インデックス化までに時間があるようで、こちらのポストで議論や相談がされていました。

https://github.com/orgs/community/discussions/18624
Qiita、はじめまして！Sea10です！

## はじめまして！

**Qiita初投稿だ！わいわい！**
 
 ということで、みなさん初めまして！Sea10（しーてん）です！
 初投稿は自己紹介をさせていただきたく！

## Who is Sea10？
- 生息地：福岡
- 所属　：九州工業大学情報工学部知的システム工学科
- 学年　：3年
- 部活　：C3
- 趣味　：面白いデザインを見ながらニマニマする/モノづくり/カンファレンス参加
- 言葉　：食べれる時に食べて、寝れる時に寝て、できるときにする

　アイコンのイラストは1年ほど前に書いたクジラです。最近はこれがトレードマークになっています🐋。（超巨大生物、ワクワクします。モササウルスとか。）

 ![IMG_1804.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2850764/81ec99d8-3289-83f3-7c3a-1e01419e5926.jpeg)


https://twitter.com/10derSea

↑のXに心に移りゆくよしなし事を、そこはかとなく書きつくっています。
 
　大学1年生の春休みのこと。５つほどバイト掛け持ちしてドタバタ働いしていた私に、「ハッカソンとか興味ない？」と友人に誘われたことがきっかけ。そして参加した学内ハッカソンで刺激をうけ、Web開発（というかものづくり）の沼にドボン！（引き込んでくれたことに心の底から感謝！）
　そんなこんなで 開発に携わりから1年ほど経ちました。
 - 使用技術・言語：React/Next.js/JavaScript/CSS/(TypeScript))
 
　フロントエンドをわちゃわちゃ勉強しつつ、ハッカソンでUIを作ったり、UXの議論をしているうちにWebデザインの魅力にハマりました。
　デザインの使用ツールは主にFigmaです。最近はワイヤフレームからつくり、そこでチームメンバーと要件等を検討するくらいにはなってきましたが、ハッカソンに出るたびに「まだまだチーム開発がしやすいFigmaの使い方、できるな！」と反省を繰り返す日々です。
　素材はCanvaで作ることが多いです。(もともとバイト先で使っていたこともあってかなりのヘビーユーザー、、、少し前に「作品数が500件を超えました！」の通知がきていました。) Canvaも良いですが、今年はAdobe CCを契約したのでAdobeを使い倒すぞの意志。

あとは、、あとは、、、今年もいろんなカンファレンスにいると思う（というか行く！）ので、仲良くしてください！（Sea10と呼んでもらえると喜ぶので、ぜひSea10と呼んでください！！）


## 帰りの会
　どうして突然記事を書き始めたのかというと、自分は色々な経験だったり技術系イベント、交流会に参加するはいいけどもアウトプットとか他の人に共有するということをあまりにも怠っているなぁと自覚したからです。今年は積極的に記事を書いたり、LTをするぞぃ！

ではでは！読んでいただき、ありがとうございました！またね〜
treeコマンド、指定パスを除外

利用するオープション： -I 
複数指定する場合： パイプ区切りで並ぶ
ワイルドカード(wild card）： 利用可
```例
 tree -I "node_modules|abc*|*efg|*hijk*" /home/ecs-user/workspace/
 ```
【終了コード9】ABC079 B - Lucas Number のTLE問題（C++, 配列と再帰関数の処理速度の違い）

Atcoder公式のC++教材、[AtCoder Programming Guide for beginners (APG4b)](https://atcoder.jp/contests/apg4b)に取り組んでいるのですが、3章の最初の演習問題で躓いたので共有します。

## ABC079  B問題「Lucas Number」 とは
chatGPTに聞いてみたルカ数について。
一言でまとめると、初項が2で2番目の項が1のフィボナッチ数列です。

> ルカ数列は、数学の世界で見られる興味深いパターンの一つです。この数列は、2つの初期値を持ち、その後の各項は直前の2つの項の和として生成されます。
最初の2つの項が2と1であるとき、次の項はそれらの和であり、その後も同様に続きます。つまり、数列は次のように始まります：2, 1, 3, 4, 7, 11, 18, 29, 47, ..

https://atcoder.jp/contests/abc079/tasks/abc079_b

### 【間違えた回答】再帰関数（C++）
最初問題を読んだ時、「お、これはPiscineでもやったアレではないか！」と再帰関数に飛びつきました。しかし実行してみると終了コード9(TLE)、、実行に時間がかかりすぎていると怒られてしまいました。

```cpp
#include <iostream>
using namespace std;

int ft_lucas(int64_t N)
{
	if (N == 0)
		return 2;
	if (N == 1)
		return 1;
	return (ft_lucas(N - 1) + ft_lucas(N - 2));
}

int main(void)
{
	int64_t N;
	cin >> N;
	int64_t ans = 0;
	ans = ft_lucas(N);
	cout << ans << endl;
	return 0;
}
```

### 【正解になった回答】配列（C++）

色々調べた結果、再帰関数はループよりは早いものの、配列に1つずつ要素をメモしておく処理よりは数倍遅いことがわかりました。


理由は明確で、ft_lucas(N-1)とft_lucas(N-2)からの呼び出しで複数回おなじ関数が呼び出されるため、より大きな項の数を求める場合には重複した計算が指数関数的に増えていきます。再帰が深くなればなるほどスタックに多数のフレームが重なり、スタックオーバーヘッドが起こるリスクも高くなります。


しかし配列を使用した場合は、計算結果を都度格納していくため。重複計算を防ぐことが可能になります。再計算が不要になり、処理時間が大幅に削減されることに繋がります。

```cpp
#include <iostream>
#include <vector>
using namespace std;

int main(void)
{
	int64_t N;
	cin >> N;
	vector<int64_t> lucasnum(N + 1);
	lucasnum.at(0) = 2;
	lucasnum.at(1) = 1;
	for (int i = 2; i <= N; i++)
	{
		lucasnum.at(i) = lucasnum.at(i - 1) + lucasnum.at(i - 2);
	}
	cout << lucasnum.at(N) << endl;
	return 0;
}

```
実際に二つのコードの実行時間の差は歴然でした、、
（下側が再帰関数によるもの、上側が配列によるもの）

![Screenshot 2024-04-03 at 11.49.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/455058/cd3668fb-4dc9-40b1-5e67-b6032a0e95bc.png)


## まとめ
再帰関数の方がスタイリッシュに記述できるので飛びつきがちでしたが、その都度適切なアルゴリズム・データ構造を利用できるように精進したいですね。

引き続き楽しいAtcoderライフを！
ExcelでChatGPTを呼び出して楽をする

# はじめに
* Excelで、表の集計でChatGPTを使いたい、と思うことは無いでしょうか？
    * たとえば、大量のアンケートを集計・分析したい、とか・・・
* 今回、OfficeScriptとChatGPT（OpenAI）を使って処理ができることが分かったので、共有したい

# やること

* 以下のような表をあらかじめ作っておいて、Productの内容をもとに、OpenAIにカフェメニューを挙げさせるサンプルを作る

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/10317/67d76315-be7c-1e8c-9156-2b2ebe6ef84d.png)


# コード共有

```TypeScript
async function main(workbook: ExcelScript.Workbook) {
  // Get the active worksheet.
  let sheet = workbook.getActiveWorksheet();

  // Get the table
  const table = sheet.getTable("CafeMenuTable"); // "CafeMenuTable" はテーブルの名前に置き換えます

  const tableDataRange = table.getRangeBetweenHeaderAndTotal(); // テーブルのヘッダーと合計行を含む範囲を取得

  const tableData = tableDataRange.getValues();

  for (let i = 0; i < tableData.length; i++) {
    const cellValueB = tableData[i][0]; // B列のセルの値を取得
    const cellValueC = tableData[i][1]; // C列のセルの値を取得

    // OpenAIにセルの値を送信して処理
    const response = await callOpenAI(cellValueB, cellValueC);

    // 結果をテーブルに転記
    const resultCell = tableDataRange.getCell(i, 2); // D列に書き込む
    resultCell.setValue(response);
  }
}


async function callOpenAI(inputTextB: string, inputTextC: string): Promise < string > {
  const EndPoint = "https://xxxxxxxxxxxxxxx.openai.azure.com/openai/deployments/gpt-35-turbo/chat/completions?api-version=2024-02-01";
  const Key = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxx";

  const payload = {
    "messages": [
      {
        "role": "system",
        "content": inputTextC
      },
      {
        "role": "user",
        "content": inputTextB
      }
    ],
    "temperature": 0.7,
    "max_tokens": 800,
    "stop": null,
    "stream": false
  };

  const options: RequestInit = {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'api-Key': Key
    },
    body: JSON.stringify(payload)
  };

  try {
    const response = await fetch(EndPoint, options);
    // console.log(EndPoint, options);
    if (response.ok) {
      const json: { choices: { message: { content: string } }[] } = await response.json();
      const content = json.choices[0].message.content;
      console.log(content);
      return content;
    } else {
      console.log('APIリクエストに失敗しました' + response.status);
    }
  } catch (error) {
    console.log('エラーが発生しました', error);
  }
}
```

## コードのポイント解説
* OpenAIのAPIは、Azure OpenAIをベースにしているので、Azureじゃない場合は、リクエストヘッダなど、適宜変更すること
* ~~テーブルのB列・C列などは決め打ちでデータを取得しているので、たぶんもっといい方法はあると思う~~
* テーブル全体を読み取るようにコードを修正しました
    * テーブル名の設定をお忘れなく！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/10317/2dfbbae4-66bf-bfdb-5704-853c1e61ba18.png)


## OfficeScript初めての方向けの補足
* 以下からスクリプトを追加することができます
    * [自動化] ⇒ [新しいスクリプト]
* すると、コードエディターの画面が現れるので、上記のコードに書き換えましょう
    * ※OpenAIのFQDNとAPIキーは各自調達の情報を入れること

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/10317/78b361fa-d5aa-ffa1-9a4f-62ad5b0ba9dc.png)

* 準備ができたら、「実行」ボタンでスクリプトが動きます

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/10317/330ce4af-a101-ed6d-5b76-e71a6dab8b1a.png)

# 実行結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/10317/65b186e7-c143-5294-f9f5-8b137c2a30fd.png)

##
プログラム練習問題：経理

https://paiza.jp/works/mondai/query_primer/query_primer__accounting


ひとまず前回のと同様にしてみたのですが
どうしてもエラーが発生する。

```py

N,K = map(int,input().split())
sections = {input() for _ in range(N)}
print(sections)
for _ in range(K):
    section, number, cost = input().split()
    print(section)
    section, total_cost = sections[section]
    sections[section] = (int(number), total_cost + int(cost))
    
print(sections)

```

```
Traceback (most recent call last):
  File "Main.py", line 5, in <module>
    sections[section].append((number,cost))
TypeError: 'set' object is not subscriptable

```
なぜだ？と思ってたら、最初の辞書のフォーマットを作るところを間違えていたようだ。

```py

#まちがい：これだと辞書にならなくてただの集合になる
sections = {input() for _ in range(N)}

#正しい：キー：値の形になっていて辞書になる
sections = {input():[] for _ in range(N)}

```
ちなみに、わたしは各部署の合計値を出力するのかと勘違いしてて
そうではなく、各部署のすべての取引をグループごとに出力することが正解だった。
なので正しくはこう。

```py
N,K = map(int,input().split())
sections = {input():[] for _ in range(N)}
for _ in range(K):
    section, number, cost = input().split()
    sections[section].append((number,cost))

for section, value in sections.items():
    print(section)
    for number, cost in value:
        print(number, cost)

    print("-----")

```
Linuxの基礎「基本構成、起動」

## 基本構成

- カーネル
OSの核。OS起動時、最初に読み込まれれる。メモリやCPUなど基本的なハードウェアを制御する。ユーザからの操作はシェルで受け付ける。
- シェル
ユーザとOSのインターフェース
人間様からの入力をコンピュータさんに伝えるプログラム
＊シェルスクリプト：シェルで解釈できる命令を寄せ集めて作ったファイル。参考：https://wa3.i-3-i.info/diff264shell.html
シェルは「受付役」である！（こまんどじゃない）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2781864/a7bdf47a-05ae-4894-75b2-e9045082038d.png)


- ディストリビューション
カーネルを取り巻くソフトウェアセット
カーネルだけではユーザがOSを利用することが困難なので、ソフトウェアと組み合わせて使用する必要がある。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2781864/8748640b-ef0c-48fe-f2e2-07653005da64.png)

- KVM(Kernel-based Virtual Machine)
これにより、Linuxサーバ上で仮想マシンを作成・実行・管理することができる。


## Linuxの起動まで
1.電源
2.BIOS/UEFI
　・HWチェック、初期化
　 ・ブートローダをメモリにロード
3.ブートローダ
　カーネルをメモリにロード
4.カーネル
　・メモリ初期化
　・仮のルートファイルシステム(initramfs)マウント
　・init、systemdプロセスを実行(初期プロセス。ほかのプロセスを実行)
5.サービス順次起動
MacOS下でcc-rsを使うパッケージのビルドに失敗する場合がある

## はじめに

`cc-rs`は`rust`のパッケージにおいて、依存する`C/C++`を間接的にビルドしてくれるパッケージである。
`cc-rs`のバージョン`1.0.90`[^1] が適用される場合に、以下のメッセージが表示され`MacOS`下でのパッケージビルドが失敗するようになった。

[^1]:issueによると1.0.83では、エラーにならないらしい

> warning: profiler_builtins@0.0.0: xcrun: error: unable to lookup item 'PlatformVersion' from command line tools installation
warning: profiler_builtins@0.0.0: xcrun: error: unable to lookup item 'PlatformVersion' in SDK '/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk'

具体的には、`duckdb-rs`を組み込んだ際に遭遇した。

この事象は、`cc-rs`の`issue`でも報告されている。

https://github.com/rust-lang/cc-rs/issues/1001

## 原因

デプロイバージョンを取得するために`cc-rs`で内部的に以下のコマンドが実行されている。

```
xcrun --show-sdk-platform-version
```

上記のコマンドは、`XCode`がインストールされている環境であれば問題なく取得できる（らしい）のだが、コマンドラインツールしかインストールしていない環境ではエラーとなってしまう。

このエラーに引きずられて、間接的に`cc-rs`に依存するパッケージのビルドが失敗していた。

### 回避策

`cc-rs`のコードを追いかけていたところ、`MACOSX_DEPLOYMENT_TARGET`環境変数の取得を試み、取得できなければ、`xcrun`を走らせるようになっていた。

https://github.com/BlackHoleFox/cc-rs/blob/ca3974f6d9a289d3a46b9016d74fb3524efbe8b1/src/lib.rs#L3425

そこで、`MACOSX_DEPLOYMENT_TARGET`にとりま`10.11`をセットした上で、ビルドを走らせたところ、ビルドが成功するところまでは確認できた。

```
$ export MACOSX_DEPLOYMENT_TARGET=10.11
$ cargo build
```

### まとめ

`cc-rs`の`issue`にも上がっているため、そのうち解決するとは思うが、これを書いている時点ではまだ解決していない。
この投稿が助けになれば幸いである。

#### 2024-04-14 追記

以下のプルリクにて、`show-sdk-platform-version`ではなく、`show-sdk-version`使用するよう変更された。

https://github.com/rust-lang/cc-rs/pull/1007

`show-sdk-version`の使用ではエラーにならないため、この回避策は不要になったと思われる。