OmniAICreator/Qiita-1.07M huggingface dataset:
Rails7 deviseにおけるユーザ登録メールの日本語化

# はじめに
みなさん、こんにちは、 **torihazi**です。

今日は現在使用しているdeviseに関する記事になります。

タイトルの件について色々調べ回ったのですが、

どれもダメで今回やった方法がうまく行ったのでその方法です。

# 結論
`app/views/devise/mailer/confirmation_instructions.html.slim` を作成し、
日本語で書けばいいだけです。

# どういうことか。
何もしないデフォルトの状態では、deviseは元々ある`app/views/devise/mailer/confirmation_instructions.html.slim`を参照します。

```app/views/devise/mailer/confirmation_instructions.html.slim
p
  | Welcome 
  = @email
  | !
p
  | You can confirm your account email through the link below:
p
  = link_to 'Confirm my account', confirmation_url(@resource, confirmation_token: @token)
```

そしてユーザ登録したら次のメール文面が飛んできます。
![スクリーンショット 2024-04-25 7.09.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2567812/a0d163a2-aab8-1496-b578-7c0579987e1d.png)

そのため次のファイルを新規作成し、オーバーライド？します。
```
# mkdir -p app/views/devise/mailer
# touch app/views/devise/mailer/confirmation_instructions.html.slim
```

あとはそこに対応する日本語を書くだけです。
```app/views/devise/mailer/confirmation_instructions.html.slim
p
  | ようこそ 
  = @email
  | !
p
  | 下記のリンクをクリックして、本人認証をお願いします。
p
  = link_to '本人認証を行う', confirmation_url(@resource, confirmation_token: @token)
```

メール文面は次のようになります。
![スクリーンショット 2024-04-25 7.13.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2567812/2d77fcd4-4174-2393-e141-87df08a0b4e2.png)

# 終わりに
自分の調べかたが悪いのでしょうか。

devise.rbの `config.mailer = Users.mailer`に直せ、みたいな記事があったと思うのですが

そちらでは思っていたものと異なっていました。

とりあえず治ってよかったです。

他の方法があれば教えてください。
cscの作法 その498

# 概要
cscの作法、調べてみた。
OleViewを、見つけたので、コンパイルしてみた。

# 参考にしたページ

https://qiita.com/kob58im/items/5570c79a8f1f4c5347c2


# 実行結果
msedgeを起動しておくと下記のような情報がとれる
```
>ov1
Creating Shell instance...
created.
System.__ComObject
GUID: d30c1661-cdaf-11d0-8a3e-00c04fc9e26e
Name: IWebBrowser2
Documentation: Web Browser Interface for IE4.
TKIND_INTERFACE
IDLFLAG_NONE
TYPEFLAG_FHIDDEN, TYPEFLAG_FDUAL, TYPEFLAG_FOLEAUTOMATION, TYPEFLAG_FDISPATCHABLE
19 functions:
 Navigate2
 [in] VT_VARIANT* URL
 [in, opt] VT_VARIANT* Flags
 [in, opt] VT_VARIANT* TargetFrameName
 [in, opt] VT_VARIANT* PostData
 [in, opt] VT_VARIANT* Headers
 QueryStatusWB
 [in] OLECMDID cmdID
 [out, retval] OLECMDF* pcmdf
 ExecWB
 [in] OLECMDID cmdID
 [in] OLECMDEXECOPT cmdexecopt
 [in, opt] VT_VARIANT* pvaIn
 [in, out, opt] VT_VARIANT* pvaOut
 ShowBrowserBar
 [in] VT_VARIANT* pvaClsid
 [in, opt] VT_VARIANT* pvarShow
 [in, opt] VT_VARIANT* pvarSize
 ReadyState
 [out, retval] tagREADYSTATE* plReadyState
 Offline
 [out, retval] VT_BOOL* pbOffline
 Offline
 [in] VT_BOOL pbOffline
 Silent
 [out, retval] VT_BOOL* pbSilent
 Silent
 [in] VT_BOOL pbSilent
 RegisterAsBrowser
 [out, retval] VT_BOOL* pbRegister
 RegisterAsBrowser
 [in] VT_BOOL pbRegister
 RegisterAsDropTarget
 [out, retval] VT_BOOL* pbRegister
 RegisterAsDropTarget
 [in] VT_BOOL pbRegister
 TheaterMode
 [out, retval] VT_BOOL* pbRegister
 TheaterMode
 [in] VT_BOOL pbRegister
 AddressBar
 [out, retval] VT_BOOL* Value
 AddressBar
 [in] VT_BOOL Value
 Resizable
 [out, retval] VT_BOOL* Value
 Resizable
 [in] VT_BOOL Value
0 variables:

```

以上。
Flutter 〜１発で治ったUnable to boot the Simulator〜

なぜか定期的に発生するiOS SimulatorのUnable to boot the Simulator。
以下の手順で解決ができた。

macのターミナルで以下を実行して隠しフォルダを表示できるようにする。
```bash
defaults write com.apple.finder AppleShowAllFiles -bool true
killall Finder
```

Finderを起動して以下のPATHに移動
/Users/ユーザー名/Library/Developer/CoreSimulator/Caches/dyld/22A400(自分の場合は22A400)

com.appleから始まるiOSシミュレーターのディレクトリを削除するとiOSシミュレータが起動するようになった。
Flutter 〜flutter clean実行後のTarget of URI doesn't existエラーになった時の対処〜

# 突然のコンパイルエラー
flutter cleanの後にコンパイルエラーが発生。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3743467/811415e7-1c68-2ed3-bb87-caf115d08193.png)

# コマンド実行で解決
プロジェクトフォルダにターミナルで移動後、以下のコマンドを実行。
```bash
flutter packages get
flutter packages upgrade
```

VS Codeを再起動してエラーが解消した。
Jetpack ComposeでenableEdgeToEdgeが効かないですよ

# enableEdgeToEdgeが効かない
```Kotlin
/**
 * onCreate
 * アクティビティの生成
 * @param savedInstanceState Bundle?
 */
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    enableEdgeToEdge(statusBarStyle = SystemBarStyle.dark(
        Color.Red.toArgb())
    )

    setContent {
        Link22Theme {
            // A surface container using the 'background' color from the theme
            Surface(
                modifier = Modifier.fillMaxSize(),
                color = MaterialTheme.colorScheme.background
            ) {
                Screen()
            }
        }
    }
}
```
と書けば、ステータスバーが赤になるかと思いきやならない...。

# 原因
当該画面のTheme.ktのテーマ設定が悪い。
上記コードだと、「Link22Theme」の部分。
定義元に飛ぶと以下のようなコードがある。

```Kotlin
@Composable
fun Link22Theme(
    darkTheme: Boolean = isSystemInDarkTheme(),
    // Dynamic color is available on Android 12+
    dynamicColor: Boolean = true,
    content: @Composable () -> Unit
) {
    val colorScheme = when {
        dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
            val context = LocalContext.current
            if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
        }

        darkTheme -> DarkColorScheme
        else -> LightColorScheme
    }
    val view = LocalView.current
    if (!view.isInEditMode) {
        SideEffect {
            val window = (view.context as Activity).window
            window.statusBarColor = colorScheme.primary.toArgb()
            WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = darkTheme
        }
    }

    MaterialTheme(
        colorScheme = colorScheme,
        typography = Typography,
        content = content
    )
}
```
この中の下記の部分をコメントアウトすれば適用される。

```Kotlin
val view = LocalView.current
if (!view.isInEditMode) {
    SideEffect {
        val window = (view.context as Activity).window
        window.statusBarColor = colorScheme.primary.toArgb()
        WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = darkTheme
    }
}
```

つまり、設定しても上書きされてたってわけですね。
ちょっと沼りました。
「問題解決力を鍛える!アルゴリズムとデータ構造」とLeetCodeで学ぶ再帰と動的計画法(メモ化)

# 概要
『問題解決力を鍛える! アルゴリズムとデータ構造』を読んだ内容を活かせる例を見つけたので、その解法を記事にしてみました。

今回の問題は、[LeetCodeの1137. N-th Tribonacci Number](https://leetcode.com/problems/n-th-tribonacci-number/description/)です。

まずは自分で挑戦したいという方は『問題解決力を鍛える! アルゴリズムとデータ構造』の再帰の章を読み、挑戦した後にこの記事の続きを読んでください。

# 詳細

## 内容
今回解く問題は以下のとおりです。
```TeX
T0 = 0, T1 = 1, T2 = 1
n >= 0 のとき、Tn+3 = Tn + Tn+1 + Tn+2
与えられた n に対して、Tn の値を返します。
```

## 単純な再帰

まず、再帰を使った初期の解法は以下のようになります。

```swift
class Solution {
    func tribonacci(_ n: Int) -> Int {
        if n == 0 {
            return 0
        } else if n == 1 || n == 2 {
            return 1
        }
        return tribonacci(n - 1) + tribonacci(n - 2) + tribonacci(n - 3)
    }
}


```

しかし、これでは n=36の場合に時間制限に引っかかります。
これは、再帰呼び出しが重複しており、計算量が爆発的に増加するためです。

ここで登場するのがメモ化です。
メモ化は動的計画法の一種であり、再帰的な問題を解く際に特に有効です。再帰呼び出しによる処理の重複箇所を保存しておき、再利用することで、処理速度を向上させることができます。
今回はトリボナッチ数列を解くのに使用していますが、他にもフィボナッチ数列や、より複雑な最適化問題や探索問題など、さまざまな問題に適用可能です。

そして、再帰処理の実行結果をメモを使用して保存するように修正したコードが以下のようになります。

```swift
class Solution {
    func tribonacci(_ n: Int) -> Int {
        var memo: [Int: Int] = [:]
        memo[0] = 0
        memo[1] = 1
        memo[2] = 1
        if n < 3 {
            return memo[n]!
        }
        for i in 3...n {
            memo[i] = (memo[i - 1] ?? 0) + (memo[i - 2] ?? 0) + (memo[i - 3] ?? 0)
        }
        return memo[n]!
    }
}
```

この解法では、計算済みの結果を memo に保存し、計算済みであれば関数を再呼び出すのではなく値を直接返します。これにより、重複計算を避け、大幅な効率化が達成されます。

以上となります。
実際に本を読んでみたけどいまいち理解しきれない時には実際に解いてみるとより知識が定着しやすくなると思うので、そういった方の助けになれば幸いです。
Javaで「クラス、メソッド、フィールドとは」の動作を確認してみた

# 概要

Javaで「クラス、メソッド、フィールドとは」の動作を確認してみました。
以下のページを参考にしました。

https://www.javadrive.jp/start/const/index2.html

# 実装

以下のファイルを作成しました。
```Robot.java
class Robot{
  int currentPosition = 0;

  public static void main(String[] args){
    Robot robot = new Robot();
    robot.report();
    robot.moveForward();
    robot.report();
  }

  void report(){
    System.out.println("Current Position = " + currentPosition);
  }

  void moveForward(){
    currentPosition = currentPosition + 1;
  }
}
```

以下のコマンドを実行しました。
```
$ javac Robot.java 
$ java Robot 
Current Position = 0
Current Position = 1
```

# まとめ

何かの役に立てばと。
【ネタ】トラフィックからaws sdkが行っている処理を見る

## 概要？？

AWS SDKを使用してアプリケーションを開発する際、SDKが実際にどのようなHTTPリクエストを送信しているのかを確認したい場面がありますよね！！？？この記事では、Windowsにおいて、ワイヤーシャークを使ってAWS SDKのトラフィックを解析する方法を説明します。

## 必要なツール

- Wireshark
- AWS SDK for Python (Boto3)
- Python

:::note info
SDKに関してはboto3を利用しましたが、AWSにアクセスできるのであればなんでも大丈夫です。
:::

## 手順
### Wiiresharkのセットアップ
1. Wiresharkをインストールし、起動します

2. 適切なネットワークインターフェースを選択します。Windowsの場合は、「Wi-Fi」という名前のインターフェースを選択します

3. フィルタを設定して、AWS SDKのトラフィックのみを表示するようにします。AWS SDKはHTTPSを使用するため、以下のフィルタを適用します
   ```sh:絞り込みバー
    443
   ```
### 送信用の簡単なコードの準備
4. AWS SDKを使用するPythonスクリプトを準備します。以下は、S3バケットのリストを取得する例です
   ```python
   import boto3

   s3 = boto3.client('s3')
   response = s3.list_buckets()

   print('Existing buckets:')
   for bucket in response['Buckets']:
       print(f'  {bucket["Name"]}')
   ```

5. AWS認証情報を設定します。環境変数、`~/.aws/credentials`ファイル、またはIAMロールを使用して設定できます。
:::note info
最悪のところ、認証情報などは空で誤った状態でも認証→400エラーが見られるので大丈夫ですが、正常な流れを見たい場合はすべて正しく認証設定などを行ってください。
:::

:::note warn
自分は、認証を適当な状態で実行しているため本当にこのスクリプトが動くかは保証できません。
基本は動くはずですが動かない場合は各自修正のほどお願いいたします。
:::

### SSL複合化の準備

6. SSLキーログファイルを設定します。コマンドプロンプトまたはPowerShellを管理者権限で開き、以下のコマンドを実行します。
   - コマンドプロンプトの場合：
     ```
     set SSLKEYLOGFILE=%cd%\keylog.log
     ```
   - PowerShellの場合：
     ```
     $env:SSLKEYLOGFILE = "$pwd\keylog.log"
     ```
:::note info
これを実行することで、SSLの暗号化をWiireshark上で解除してみることができますが、重要な情報であるため必要がなくなった場合は即座に **完全に削除することを推奨します**
:::

### スクリプトの実行
7. 同じターミナルセッションで、Pythonスクリプトを実行します。
:::note info
これにより先ほどのSSL暗号と同じ暗号パターンで暗号化されるためWiireshark上で複合化できるようになる
:::

### Wiiresharkの複合化の設定
8. 関連するパケットを選択し、詳細を確認します。SSLで暗号化されたペイロードを復号化するには、Wiresharkの設定で、「(Pre)-Master-Secret log filename」フィールドにキーログファイルのパスを指定します。

↓自分の説明よりもわかりやすいのでこちらから、複合化の設定を行うようにしてください

https://qiita.com/gaichi/items/364879b3c4cb68830652


9. Wiresharkでキャプチャされたパケットを確認します。AWS SDKのリクエストは、宛先がAWSのサービスエンドポイントになっているはずです。

:::note info
エンドポイントもわからないよ、、となっているでしょう、、
そのような場合は443で絞り込んだ後に、（application/json）となっている通信を見るとよいいでしょう。暗号化されたものは本来見えないため、Wiiresharkによって複合化された今回の通信情報である可能性が限りなく高いです。
:::


## まとめ

Wiresharkを使用することで、AWS SDKが行っているHTTPリクエストの詳細を確認することができます。これにより、アプリケーションとAWSサービス間の通信をデバッグしたり、パフォーマンスを最適化したりする際に役立ちます。ただし、SSLキーログファイルには機密情報が含まれているため、取り扱いには十分注意してください。

## おまけ
SDKの使えない環境においてAPIからアクセスする際にどのように動いいているのかあたりをつけたいと思い行ったが、認証回りが地獄であるためLambdaなどを利用することを考えようと思う、、、

https://tech.upgarage-g.co.jp/entry/2023/08/01/154203

本家のAWSの設定周りもこれの前に見たが、地獄であった、、無念である、、、
Vue.jsのアプリケーションをGithub Pagesに公開するにあたって困ったこと

# 背景
* Vue.jsで作ったアプリケーションを簡単に共有したい
* **GitHub Pagesよくわからん**
* **Github Pagesにデプロイしたけどjsとcssが404吐く**
* build: npm, vite

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3669834/fcb36f9c-1a2c-e583-dc39-c43eb32ede2e.png)

# やったこと
### build先をdistからdocsに変更する
 GitHub Pagesはrootか/docsしかソースフォルダとして指定できないようです
 https://docs.github.com/ja/pages/getting-started-with-github-pages/configuring-a-publishing-source-for-your-github-pages-site

 vite.config.jsにoutDirを明示的に指定しました。
```javascript
export default defineConfig({
  plugins: [vue(), VueDevTools()],
  resolve: {
    alias: {
      '@': fileURLToPath(new URL('./src', import.meta.url))
    }
  },
  build: {
    outDir: './docs'
  }
})
```

これでnpm run buildをするとdocsにビルドされるようになります。
https://ja.vitejs.dev/guide/static-deploy

 
### baseで基準となるパスを指定
デフォルトではドメイン直下のルートパスが指定されているようです。

https://qiita.com/hidekita/items/ca12fc95ec48cb9252c2

明示的にパスを指定してあげます。
```javascript
export default defineConfig({
  base: '/moriyamato-pf/',
  plugins: [vue(), VueDevTools()],
  resolve: {
    alias: {
      '@': fileURLToPath(new URL('./src', import.meta.url))
    }
  },
  build: {
    outDir: './docs'
  }
})
```

# おわり
npmなんもわからん。javascriptなんもわからん。viteなんもわからん。webなんもわからん。
結論、なんもわからん。

Vue.jsやりながら勉強していこうと思います。
[Laravel]Dockerfileで上位階層をCOPYする方法

## はじめに
自分用の個人開発のメモ/備忘録として記録していきます。

## やりたいこと
Laravel環境をマルチステージビルドで構築しようとしていて
上位階層にあるソースコードをコピーしたい。


## 問題点
・docker回りのファイルと、ソースファイルを別のディレクトリにまとめていた
・これまで相対パスで```../source```のように指定していた
・マルチステージビルドのためにDockerfile内で``` COPY ../source /app ```としたいが親ディレクトリは参照出来ない

```
.
├── docker-dir
│   ├── README.md
│   ├── docker-compose.yml
│   ├── .env
│   └── infra
│       ├── mysql
│       │   ├── Dockerfile
│       │   └── my.cnf
│       ├── nginx
│       │   └── default.conf
│       └── php
│           ├── Dockerfile
│           └── php.ini
└── source-dir
    ├── README.md
    ├── app
    ・
    ・
    ・
```


## やり方
・docker-compose.ymlのcontextを上位階層にしておく
・contextからパスで、Dockerfileもソースファイルのディレクトリも指定する

### .env
各種環境変数で指定しておく
```.env
CONTEXT_DIR=../   　　　　　　　　　　　　　　　　　　　# docker-compose.ymlから見て上位階層にしておく
CURRENT_DIR=docker-dir 　　　　　　　　　# docker-compose.ymlのディレクトリ
HOST_VOLUME_DIR=source-dir 　# ホスト側のボリュームディレクトリ名
WEB_HOST_PORT=８０
DB_HOST_PORT=３３０６
```

:::note info
docker-compose.ymlの配下にソースコードを配置する場合は
CONTEXT_DIR=./
CURRENT_DIR=.
のように設定する。
:::



### docker-compose.yml

```yml:docker:docker-compose.yml
version: "3.9"

services:
    app:
        build:
            context: ${CONTEXT_DIR}  #ここでcontextを上位階層にしておく
            dockerfile: ${CURRENT_DIR}/infra/php/Dockerfile  #上位階層からのパスで指定　 
            args:
                - HOST_VOLUME_DIR=${HOST_VOLUME_DIR}  #contextからのパス
        ports:
            - 5173:5173
        volumes:
            - ./infra/php/php.ini:/usr/local/etc/php/php.ini
            - ${CONTEXT_DIR}${HOST_VOLUME_DIR}:/var/www/html/
        ・
        ・
        ・
        （割愛）
```

### Dockerfile
これでsource-dirのCOPYが出来る
```docker:Dockerfile
・
・
・
# アプリケーションのソースコードをコピー
ARG HOST_VOLUME_DIR
WORKDIR /app
COPY ${HOST_VOLUME_DIR} /app
・
・
・
```




## おわりに
・上位階層を指定するわけではなく、下位の階層指定となるようにcontextを上位へ持ってくるということに気づくのに時間がかかった
・素直にディレクトリ構成変えればよいので、あまり使わないかもしれない


## 参考

https://qiita.com/Masataka_Sugia/items/a4e0bbec2a9b236a377e
ルータ/L2スイッチ/L3スイッチ/L4スイッチ/L7スイッチの違い

<!-- memo
> ルータ/L2スイッチ/L3スイッチ/L4スイッチ/L7スイッチの違いを押さえておけば
ハイパーバイザ

-->
# はじめに
私が「[インフラエンジニアの教科書](https://www.c-r.com/book/detail/935)」を読んでいる中で以下の文面がありました。
> ネットワーク機器の主な役割は通信の交換です。ネットワーク機器のカタログをみるとさまざまな情報が明記されていて難しく感じられるかもしれませんが、基本的には接続するサーバやネットワーク機器の数やコネクタの違いと、どの程度の通信量をどの程度、高速に交換したいのか、そして、**ルータ/L2スイッチ/L3スイッチ/L4スイッチ/L7スイッチ**の違いを押さえておけば、ネットワーク機器の選定を大きく間違えることはありません。

私は`ルータ/L2スイッチ/L3スイッチ/L4スイッチ/L7スイッチ`の違いが分からなかったので、本記事にまとめます。

# スイッチとは
スイッチとは、電気信号を受け取り、それを他のポートに転送するネットワークデバイスを指します。

# レイヤ
L2スイッチ、L3スイッチとありますが、そもそもL2やL3は何を表しているのかわかりませんでした。
調べてみるとLというのはレイヤ(Layer)のLで、対応しているネットワークのレイヤーを表していました。
ネットワークのレイヤとは**OSI参照モデル**の階層のことです。
[Wikipedia](https://ja.wikipedia.org/wiki/OSI%E5%8F%82%E7%85%A7%E3%83%A2%E3%83%87%E3%83%AB)では以下のようになっております。
|レイヤー|名前|説明|
|---|---|---|
|第7層|アプリケーション層|具体的な通信サービス（例えばファイル・メールの転送、遠隔データベースアクセスなど）を提供。HTTPやFTPなどの通信サービス|
|第6層|プレゼンテーション層|データの表現方法（例えばEBCDICコードのテキストファイルをASCIIコードのファイルへ変換する）|
|第5層|セッション層|通信プログラム間の通信の開始から終了までの手順（例えば接続が途切れた場合、接続の回復を試みる）|
|第4層|トランスポート層|ネットワークの端から端までの通信管理（エラー訂正、再送制御など）|
|第3層|ネットワーク層|ネットワークにおける通信経路の選択（ルーティング）。データ中継|
|第2層|データリンク層|直接的（隣接的）に接続されている通信機器間の信号の受け渡し|
|第1層|物理層|物理的な接続。コネクタのピンの数、コネクタ形状の規定など。銅線–光ファイバ間の電気信号の変換など|

つまり、L2スイッチはデータリンク層のスイッチ、L3スイッチはネットワーク層のスイッチということでした。

# ルータ
ではルータは他のスイッチと何が違うのでしょうか？

そもそもルータとはネットワーク上のデータを制御し、異なるネットワーク間でデータを転送する役割を担います。
インターネットやイントラネットなどのネットワークを接続し、パケットをルーティングして目的地に届けます。
ルータもL3スイッチと同様にネットワーク層のデータを処理します。

# ルータとL3スイッチの違い
では同じレイヤの処理をするルータとL3スイッチは何が違うのでしょうか。
chatGPTによると3つの点で異なるそうです。
> #### 処理方法
> ルータはソフトウェアによってパケットの処理を行います。パケットが到着するたびにソフトウェアがパケットのヘッダーを解析し、適切な出力ポートに転送するため、パフォーマンスが比較的低いです。
一方、L3スイッチはハードウェアによってパケットを処理します。ASIC（Application-Specific Integrated Circuit）と呼ばれる専用のチップセットを使用して高速なパケット処理を行うため、パフォーマンスが優れています。
> #### ポート数
> 通常、ルータには少数のポートしか搭載されていません。ルータは主に異なるネットワーク間の接続を担当するため、必要なのはポート数よりも柔軟なルーティング機能です。
L3スイッチには通常、複数のイーサネットポートが搭載されており、スイッチングとルーティングの両方の機能を提供します。これにより、複数のネットワークセグメント間での高速な通信が可能となります。
> #### 価格
> 一般的に、L3スイッチはルータよりも価格が低く、同等のルーティング機能を提供します。これは、L3スイッチが高速なハードウェアベースの処理を行うため、ルータよりもコストが低いためです。

# まとめ
スイッチやルータはそれぞれ異なるレイヤのデータを管理します。
そのため処理するレイヤや機能でスイッチやルータを使い分けます。

インフラを構築する際に「[インフラエンジニアの教科書](https://www.c-r.com/book/detail/935)」はとても役に立つ内容となっているのでおすすめします。
え？anacondaをDockerで環境構築できて、しかもVSCodeでコーディングが完結しちゃうんですか？

# 1. はじめに
今回の内容は題名そのまんまです。簡潔に、忘備録も兼ねて要点だけを整理しようと思います。

前提として、Docker(for windows/Mac)やVSCodeはインストールしているものとします。この記事では、Dockerの詳しい解説はしませんのでご了承ください。

# 目次
- [1. はじめに](#1-はじめに)
- [2. anacondaをDockerで](#2-anacondaをdockerで)
- [3. VSCodeでNotebook](#3-vscodeでnotebook)
- [4. Kernelの選択](#4-kernelの選択)
- [5. おわりに](#5-おわりに)

# 2. anacondaをDockerで
```Dockerfile
FROM continuumio/anaconda3

RUN pip install --upgrade pip

WORKDIR /workdir

ENTRYPOINT ["jupyter-lab", "--NotebookApp.ip=0.0.0.0", "--port=8888", "--no-browser", "--allow-root", "--NotebookApp.token='mNFS7wbnEIm/Cd/PTe4h/cgboVXIUBszH5MxU8o7rFg'", "--NotebookApp.allow_origin='*'"]

CMD ["--notebook-dir=/workdir"]
# 上のコマンドが、作業ディレクトリーの名前を定義するものです。
# 例えば、`import os`して、`print(os.getcwd())`すると、/workdirが出てきます。
```
Dockerでanacondaを構築すると、Pythonのインタープリターがないので、そのままではコードを実行することができません。
このためカーネルを選択するとき、コンテナーで稼働している既存のJupyterサーバーに接続しなければなりません。
Jupyterサーバーに接続する時はトークンを求められるのですが、もしDockerfile記述時にトークンを指定しておかなければ、Jupyterが自動的に生成されるトークンを毎回調べなければなりません。
そのときは、コンテナーに対して`jupyter notebook list`というコマンドを実行してトークンを調べることになります。
これが面倒なので、`"--NotebookApp.token='mNFS7wbnEIm/Cd/PTe4h/cgboVXIUBszH5MxU8o7rFg'"`のようにDockerfile内に予め指定しておく方が吉です。

```compose.yml
version: '3'

services:
  filed-anaconda:
    container_name: filed-anaconda
    build: .
    ports:
    - "8888:8888"
    volumes:
    - type: bind
      source: ./src
      target: /workdir
```
上のcompose.ymlファイルにおいては、バインドマウントの指定であるvolumesプロパティが肝です。
何もしなければコンテナー内のディレクトリーとホストマシンのディレクトリーはつながりがないので、編集内容がコンテナー内のディレクトリーに反映されません。
バインドマウントはホストマシン側の作業ディレクトリーをコンテナー内のディレクトリーに反映させる処理です。
ソースコードはsrcディレクトリーに書いていて、バインドマウント先をworkdirディレクトリーにしています。


# 3. VSCodeでNotebook

まず、VSCodeでPythonを扱いやすくする拡張機能を入れます。これはどちらかといえば入れておいた方がいいものです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2826164/d7b1590e-7b8b-d4fb-59a9-63e9868a0491.png)

次に、VSCodeでJupyter Notebookを扱うための拡張機能をインストールします。これは入れておくべき拡張機能です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2826164/109ebfb4-1f72-d5a0-09a0-56203c23f586.png)


# 4. Kernelの選択
カーネルを選択する時は、各コードを実行する時か、カーネルを選択するアイコンをクリックすることで選択バーが出てきます。
今回は、Pythonの実行環境ではなく、すでにあるJupyterサーバーに接続することになります。
以下の画像の状態です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2826164/19ea923a-7ff7-8037-5ea2-2a13091f0516.png)

Jupyterサーバーに接続するときは、tokenをクエリーパラメータとして指定した形でURLを入力します。
パスワードは特に設定していないので、パスワード入力を求められたら空のままエンターキーを押せば大丈夫です。
以下の画像のような状態です。
URLは`http://localhost:8888/lab?token=mNFS7wbnEIm/Cd/PTe4h/cgboVXIUBszH5MxU8o7rFg`のような形になっています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2826164/d4986dc2-28b9-196f-d5cf-1ca456216fc4.png)

# 5. おわりに

今回は、細かい理屈は飛ばし飛ばしで環境構築の流れをまとめていきました。
もし、Dockerの詳しい理屈を知りたいのであれば、別のサイトを参照するか、わたしがまとめた記事を参照することをお勧めします。

（宣伝）
https://qiita.com/TechForGood/items/7cd5d7dc1ff06d6e1d9d
[Windows10] ようこその画面で張り付く

# 事象
パスワード入力後に「ようこそ」の画面で数時間以上WaitingCursorのまま起動しない

# 環境
- Windows10 Pro
- バージョン 22H2

# 前提
ログイン画面上でShiftキーを押しながら再起動ボタンを押して
「お待ちください」が表示されるまでShiftキーを押しておくとオプション画面を起動。
トラブルシューティングから下記の操作を実行しています。

# やった事
- chkdsk の実行
コマンドプロンプトを起動して以下のコマンドを実行するだけ。
問題が検出されなかったので、ディスク自体には問題ないらしい。
```
chkdsk
(中略)
Windows でファイル システムのスキャンが終了しました。
問題は見つかりませんでした。
これ以上の操作は必要ありません。
(中略)
```
- 最新の更新プログラムのアンインストール
特に変わらず。
- インターネット切断
物理的にLANケーブルを引っこ抜く。特に変わらず。
- システムの復元
1週間前に復元ポイントが作成されていたので実行。
かなり時間がかかった割に効果がなく、特に変わらず。
- セーフモードで起動
2時間くらい放置したら起動した。
セーフモード前でも待ってたら起動したかも...。
- 通常モードで起動
数時間放置して起動を確認。
- 一時ファイルの削除（原因？）
Cドライブが残り70GBだったので何が食い散らかしてるかチェック。
C:\Users\[ユーザー名]\AppData\Local\Tempフォルダの使用容量が109GBを超えていた。
すべて削除。

# 改善しない場合に試そうとしていた事
- システムファイルチェッカー
```
# 実行方法
# 管理者でコマンドプロンプトを起動して下記コマンドを実行するだけ
SFC /scannow
```
- OSのクリーンインストール (最終手段)

# まとめ
ディスククリーンアップでは削除されない一時ファイルものもある？らしい。
1回/年くらいは確認したほうがいいかも。
（そもそもディスククリーンアップも長年していなかったが...）

# 参考資料
- [Windows で PC をセーフ モードで起動する - Microsoft サポート](https://support.microsoft.com/ja-jp/windows/windows-%E3%81%A7-pc-%E3%82%92%E3%82%BB%E3%83%BC%E3%83%95-%E3%83%A2%E3%83%BC%E3%83%89%E3%81%A7%E8%B5%B7%E5%8B%95%E3%81%99%E3%82%8B-92c27cff-db89-8644-1ce4-b3e5e56fe234)
- [システム ファイル チェッカー ツールを使用して不足または破損しているシステム ファイルを修復する - Microsoft サポート](https://support.microsoft.com/ja-jp/topic/%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0-%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB-%E3%83%81%E3%82%A7%E3%83%83%E3%82%AB%E3%83%BC-%E3%83%84%E3%83%BC%E3%83%AB%E3%82%92%E4%BD%BF%E7%94%A8%E3%81%97%E3%81%A6%E4%B8%8D%E8%B6%B3%E3%81%BE%E3%81%9F%E3%81%AF%E7%A0%B4%E6%90%8D%E3%81%97%E3%81%A6%E3%81%84%E3%82%8B%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0-%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%92%E4%BF%AE%E5%BE%A9%E3%81%99%E3%82%8B-79aa86cb-ca52-166a-92a3-966e85d4094e)
GTMのコンテナ数に上限はあるのか？〜SaaSツール運営の裏話を添えて〜

# 1アカウントあたり2,000コンテナが上限！

![1アカウントあたり2,000コンテナが上限](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/509379/091b6279-4008-8e32-64d5-2986db910415.png)

Google Tag Manager（以下「GTM」）のコンテナ数に、はたして上限はあるのか...？

答えはYES。
**1アカウントあたり2,000コンテナ**が上限となっているようだ。

:::note warn
「ようだ」と書いているのは、これが公式ドキュメントなどに記載されているわけではなく、**実際に発生した事象により判明した新事実**[^1]であるため。
:::
[^1]: かなり調べたのですが、それらしい記事は見つかりませんでした。もし既出情報であればコメントなどで教えてください...！

なお、ここでの「アカウント」とは、Googleアカウントなどとは異なり、GTMにおいてコンテナの上階層に位置するものだ。

> タグ マネージャー アカウントは、階層構造の最上位に位置します。通常の場合、企業ごとに必要なアカウントは 1 つです。タグ マネージャー アカウントには 1 つ以上のコンテナが含まれています。
（[タグ マネージャー ヘルプ](https://support.google.com/tagmanager/answer/6103696?hl=ja)より引用）

# 上限に達してしまったら？

普通に利用している限り、2,000コンテナの上限を超えることはまずないはずだ。
しかし、万が一超えてしまっても問題ない。

対処法は極めてシンプルで、**新たに別のアカウントを作成し、その下層にコンテナを作成すればよい**のだ。

![新規アカウントを作成](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/509379/c7f93324-b566-90fb-6a9f-cc5de3b4d52e.png)

アカウント数に上限があるのかどうかは現時点では確認できていないが、
仮に「1ユーザーが閲覧できるアカウント」の上限が100アカウントだとしても、20万コンテナを作成することができる。

# なぜ上限が分かったのか？（+ SaaSツールにおけるエラー対応の裏側）

最後に、今回この新事実が判明した経緯についても述べておこう。

私は、「[Optimize Next](https://optimize-next.com)」という**無料で使えるABテストツール**の開発責任者を務めている。
2023年9月にサービス終了を迎えたGoogle製のABテストツール「Google オプティマイズ」を代替するツールとして、国内で最も利用されている[^2]。

[^2]: [Google オプティマイズ廃止後の代替ツール検討、選定状況に関する調査結果](https://kwmlabo.com/research-release/1497)より

「Optimize Next」は、GTMの仕組みを応用することで、自社のサーバーに依存しない運用を実現し、無料でのツール提供が可能となっている。

かんたんに説明すると、
* 「Optimize Next」上でコンテナを新規作成すると、裏側ではGTMコンテナが作成される
* ABテストを開始すると、そのGTMコンテナ内にタグが作成される

というような具合だ。

*（実は「Google オプティマイズ」の正体もGTMだったのだが...その説明はまたの機会としよう。）*
***
さて、前置きが長くなってしまったが、
そんな「Optimize Next」は先日、**コンテナ数2,000件を突破**した。

これは、**リリースから約半年でおおよそ2,000サイトに導入していただいた**という証であり、この新サービスの快進撃に社内も歓喜していた。

・・・ところが、である。

ちょうどそのタイミングを境に、コンテナの新規作成時にエラーが起きていることが発覚したのだ。
最初はGTMのREST APIを呼び出す際に何らかの問題が発生しているのかと疑ったが、どうもそうではないらしい。

・・・まさか、上限を、超えている・・・？！

そのまさかだった。
着実にグロースしてきているこのタイミングで、なんたる失態・・・。
しかし慌てている暇はない。すぐに対応方針をまとめ、実行に移した。

![Slackのログ](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/509379/1754e7a2-7366-5f5d-6663-ee899609ce30.png)

エンジニアとしてはすぐにでも修正作業に取り掛かりたかったが、**SaaSツールは信用がすべて**。
エラーに巻き込んでしまったユーザーさんへのお詫びを最優先事項とした。

すぐに対応できたこともあり、該当するユーザーが10名ほどだったのが、不幸中の幸いである。

修正作業自体も、そこまで複雑なものにはならなかった。
結局、**発覚から2時間以内にすべてのエラーを解消**することができた。

# あとがき

実は、「GTMのコンテナ数に上限があるのでは？」という懸念は、「Optimize Next」のPoCの段階で既に上がっていました。

そこで、APIをループさせて叩きまくりコンテナを大量に作成して検証してみたのですが・・・
*（Googleさん、その節は負荷をかけてしまいすみませんでした！）*

1,000コンテナ以上作成できることを確認して、「GTMのコンテナ数に上限はない」と結論づけてしまっていました。
まさか上限が2,000だったとは・・・。

詰めが甘かったとしか言いようがありませんが、せめて公式ドキュメントに記載があれば・・・（笑）
いや、2,000コンテナも利用するユーザーなんてレアケース中のレアケースだし、Googleさんを責めるのはお門違いですね。

***

今回の件を通して、結果的に多くの学びを得ることができました。
多くの方にご利用いただけているからこそ起こったハプニングであり、まさに「嬉しい悲鳴」ですね。

ご不便をおかけしてしまったユーザーさんには申し訳ないですが、この反省を活かして引き続きより良いサービス運営に励んでいきたいと思います！
今後のビジョンについてはこちらのインタビュー記事でも語っておりますので、ぜひぜひご一読いただけると嬉しいです！

https://kwmlabo.com/interview/1982
【Rails】バリアントを事前に生成する方法【ActiveStorage】

以下のように`preprocessed: true`を記述することでバリアントを事前に生成できます。

```rb:app/models/user.rb
class User < ApplicationRecord
  has_one_attached :avatar do |attachable|
    attachable.variant :thumb, resize_to_limit: [100, 100], preprocessed: true
  end
end

```
Viteでテンプレートを利用せずにVanilla JS環境を作成する

## はじめに

viteには`Vanilla JS`用のテンプレートが用意されており、簡単に使うことができます
```bash
$ npm create vite@latest vite_project -- --template vanilla

Scaffolding project in E:\Users\tkykn\git\bundle\vite_project...

Done. Now run:

  cd vite_project
  npm install
  npm run dev
```

ただちょっとしたサンプルには不要なファイルも多いので、テンプレートを利用せずクリーンな環境を作成してみます(npmのライブラリを手軽に利用できるアプリを作ることができます)

* --template vanilla で作成したファイル一覧

![image-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276579/599e7cff-ba47-1443-1a88-c0dcb1d9c564.png)



## viteのインストール

* npmの初期化、viteインストール
* cowsayはviteでnpmライブラリを読み込めることを確認するためにインストール

```bash
$ npm init -y
$ npm i -D vite
$ npm i cowsay
```

## npmモジュールを利用したサンプルWebアプリの作成



### main.jsの作成

viteはcommonJSをrequire()で読み込めないのでstatic importを行う
```bash
$ cat <<EOF > main.js
import * as cowsay from 'cowsay';
console.log(cowsay.say({ text: 'ウシだよ！' }));
EOF
```

### index.htmlの作成

`main.js`は絶対パスにする。Viteが変換処理(npmライブラリを読み込ん)をしてくれるのでブラウザで実行できる

```bash
$ cat <<EOF > index.html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vite App</title>
  </head>
  <body>
    <script type="module" src="/main.js"></script>
  </body>
</html>
EOF
```

### package.jsonの修正

開発サーバーとビルドを行えるようにする

```diff_javascript:package.json
{
  "name": "vite_no_template",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
-   "test": "echo \"Error: no test specified\" && exit 1"
+   "dev": "vite",
+   "build": "vite build",
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "vite": "^5.2.10"
  },
  "dependencies": {
    "cowsay": "^1.6.0"
  }
}
```

## 動作確認

### 開発サーバーを実行する

```bash
$ npm run dev
  VITE v5.2.10  ready in 160 ms

  ➜  Local:   http://localhost:5173/
  ➜  Network: use --host to expose
  ➜  press h + enter to show help
```
ブラウザで開くと、コンソールに「cowsay」が表示されていることが確認できる

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276579/dd8cfa5f-b054-04f6-ee72-264f94b141bc.png)


### ビルドを行う

ビルドを行うと`./dist`フォルダに出力される

```bash
$ npm run dev

> vite_no_template@1.0.0 build
> vite build

vite v5.2.10 building for production...
✓ 4 modules transformed.
dist/index.html                0.29 kB │ gzip: 0.22 kB
dist/assets/index-BcOpN3_6.js  4.01 kB │ gzip: 1.99 kB
✓ built in 144ms
```

簡易http-serverを起動して表示すると、コンソールに「cowsay」が表示されていることが確認できる


```bash
$ npx http-server ./dist
Starting up http-server, serving ./dist

Available on:
  http://127.0.0.1:8080
Hit CTRL-C to stop the server
```
![image-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276579/146ed897-34c9-f36b-6b0c-44c0f45335cf.png)
個人開発記録　2024/04/24

# 目的

* とにかくやりきることが一番の目的
* 毎日発信することで、発信する習慣作り
* 個人開発をやりきることで、実績作り


# 開発するもの

X(旧Twitter)のパクリ

# 構成

* バックエンド
    * python
    * django REST framework
* DB
    * postgreSQL
* フロントエンド
    * React

# 本編

今日はフロント編
Reactの環境構築をしていく
参考は以下サイト

https://zenn.dev/web_tips/articles/abad1a544f3643

## npm/Node.jsのインストール

以下からnodeをインストール

https://nodejs.org/en/download

バージョン確認

```bash
node --version
```

npmフォルダを作成する
nodeをインストール直後は、これがないと後続のコマンドでエラーになる

```bash
mkdir /c/Users/ユーザー名/AppData/Roaming/npm
```

Djangoがあるフォルダまで移動して、reactプロジェクトを作成
この場所がいいかは不明だが、同じ場所のほうが管理しやすそうなのでDjangoがあるフォルダにつくる

```bash
cd /c/workspace/likeX/
npx create-react-app react_app
```

起動!!!

```bash
cd rect_app
npm start
```

万華鏡写輪眼開眼!!!

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2263720/d37a79c1-3f5a-b134-0d84-85055aab752e.png)

chromeにreact開発必須ツール
`React developer tool`をインストール

https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi?hl=ja

ディレクトリ構成は`bulletproof-react`のものを採用
メリットはよくわかってないけど、これがいい的な記事が上がっていたので、これを採用

仕事で関わったreactプロジェクトでは`Atomic Design`が採用されていたため、どんな違いがあるか楽しみ

https://github.com/alan2207/bulletproof-react/blob/master/docs/project-structure.md

chatGPT作の、以下シェルを実行して`src`配下に各ディレクトリを作成

```shell
#!/bin/bash

# ディレクトリを作成する基本パス
base_dir="./"

# 作成するディレクトリのリスト
directories=(
    "assets"
    "components"
    "config"
    "features"
    "hooks"
    "lib"
    "providers"
    "routes"
    "stores"
    "test"
    "types"
    "utils"
)

# ディレクトリを作成する
for dir in "${directories[@]}"; do
    mkdir -p "${base_dir}${dir}"
done

echo "All directories have been created."
```

`features`配下に機能単位のディレクトリを作成していくらしいので
`features`配下に`creatUser`ディレクトリを作成

```bash
mkdir /react_app/src/features/creatUser
```

さっきのシェルを修正して、`createUser`配下に以下ディレクトリを作成
プラス`index.ts`ファイルも作成

```
"api"
"assets"
"components"
"hooks"
"routes"
"stores"
"types"
"utils"
```

## 感想

仕事で出来合いのreactプロジェクトを作成したことはあるけど、環境構築からはしたことがなかったのでいい勉強になった。
新しいディレクトリ構成がどうなるか楽しみ！
明日は画面を作っていく！
Node.jsで普通のJavaScriptをimportする

ES ModulesでもCommonJS でもUMDでもない普通のJavaScriptをimportする方法を調べました。ブラウザで動作するスクリプトをテストしたいときにご活用ください

# 普通のJavaScript

```js:script.js
class MyClass {
    hello() {
        return 'こんにちは'
    }
}
```

# 普通のJavaScriptを変更することなくimportする

## eval⭐⭐⭐⭐⭐

```js:main.mjs
import { readFileSync } from 'node:fs'

eval(readFileSync('script.js') + ';global.MyClass=MyClass')

console.assert(new MyClass().hello() === 'こんにちは', 'あいさつすること')
```

普通のJavaScriptを変更しなくていいのでオススメ

## import⭐⭐⭐

```js:main.mjs
import { readFileSync } from 'node:fs'

const script = readFileSync('script.js') + ';export { MyClass }'
const { MyClass } = await import('data:text/javascript;base64,' + Buffer.from(script).toString('base64'))

console.assert(new MyClass().hello() === 'こんにちは', 'あいさつすること')
```

evalのときよりも複雑

# 普通のJavaScriptを変更してimportする

## eval⭐⭐⭐⭐

```js:script.js
class MyClass {
    hello() {
        return 'こんにちは'
    }
}

globalThis.MyClass = MyClass
```

```js:main.mjs
import './script.js'

console.assert(new MyClass().hello() === 'こんにちは', 'あいさつすること')
```

テストコードがシンプルになるのでオススメ

エラーが発生したときに分かりやすい
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/435735/5bf0cd2c-4737-0955-7fc8-ec08cd3f031d.png)

Node.js環境だけ晒すのもありよりのあり
```js:script.js
class MyClass {
    hello() {
        return 'こんにちは'
    }
}

if (typeof global === 'object') global.MyClass = MyClass
```

## import⭐⭐

```js:script.mjs
class MyClass {
    hello() {
        return 'こんにちは'
    }
}

export { MyClass }
```

```js:main.mjs
import { MyClass } from './script.mjs'

console.assert(new MyClass().hello() === 'こんにちは', 'あいさつすること')
```

`script.js`を`script.mjs`に変えたり、scriptタグに`type=module`属性を付与する手間が増えるのでオススメできない

https://qiita.com/7mpy/questions/a69bea0a336f959690bd
起動しないSurfaceのスペックをシリアルナンバーから確認する方法

# 起動しないsurfaceのスペックを確認する方法

## 背景
部屋の片づけをしていたところ、昔使っていたsurfaceが出てきたが充電器が見つかりませんでした。
スペック次第では充電器を購入しようと思いましたが、スペックが分からなく、シリアル番号からスペックを検索する方法を探しました。

## シリアル番号の記載位置
シリアル番号の記載位置についてはこちらを参考にしてください
https://support.microsoft.com/ja-jp/surface/microsoft-%E3%81%BE%E3%81%9F%E3%81%AF-surface-%E3%83%87%E3%83%90%E3%82%A4%E3%82%B9%E3%81%A7%E3%82%B7%E3%83%AA%E3%82%A2%E3%83%AB%E7%95%AA%E5%8F%B7%E3%82%92%E6%A4%9C%E7%B4%A2%E3%81%99%E3%82%8B-6c0abc0c-2b45-247d-f959-70e504e55fa5

## スペックの検索方法
以下のリンクへアクセスし、各項目を入力
https://mybusinessservice.surface.com/en-US/CheckWarranty/CheckWarranty
【Laravel】データ連携時のuse宣言を忘れずに

Laravelを学習していて、use宣言の重要性に気づいたので記事にまとめました。

:::note info
Laravel10で学習していたため、Laravel10の使用を前提としています。（新バージョンでも執筆時点ではこの点は変わっていないという認識です）
:::
# use宣言とは?
use宣言が何かを説明する前提として、MVCモデルについて簡単に説明します。
### MVCモデルについて
LaravelはPHPのフレームワークで、「MVCモデル」という設計でアプリケーションを開発していきます。以下のように、役割を分担して記述していきます。
<dl>
<dt>M（モデル）</dt>
<dd>データ処理関連。データベースとの連携。</dd>
<dt>V（ビュー）</dt>
<dd>ブラウザに表示される画面部分</dd>
<dt>C（コントローラー）</dt>
<dd>モデルとビューの橋渡しなど。ロジック部分。</dd>
</dl>

この3つに加えて、
<dl>
  <dt>ルーター</dt>
  <dd>リクエストをどのコントローラーのどのメソッドで処理するか割り振る</dd>
  も必要になります。
</dl>

データ処理関連はモデルに記載されているため、データベースの情報を使って何かしようとするときは、コントローラーに「●●というモデルを使うよ！」というお知らせをします。また、ルーターでも「○○というコントローラーを使うよ！」と知らせる必要があります。

これが「use宣言」です。

# use宣言をする
例えば、以下のような機能を持たせたいとします。

- 店のリストからお気に入り登録する
- 店の情報にはエリア情報が紐づけられている
- お気に入り登録した店を一覧表示する際、エリア情報も見たい

この場合、以下のような構成が考えられます。（ビューファイルは省略）

| モデル | コントローラー | テーブル |
|:-:|:-:|:-:|
|  Shop.php |  ShopController.php  | shops_table |
|  Area.php |  AreaController.php | areas_table  |
| Favorite.php  | FavoriteController.php  | favorites_table |

各テーブルの情報を利用してビューファイルに表示させるには、まずモデルにリレーションの記述をします。

```php:モデル
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

               ~ リレーションのための記述 ~
```
モデルの中のuseはデフォルトで入ってくるものなのでそのままでOK。
リレーションのための記述（１対多など）を記載します。

コントローラーは以下のように記載します。ここでuse宣言です。

```php:ShopController
<?php
namespace App\Http\Controllers;
use App\Models\Shop;
use App\Models\Area;

~ 下記省略 ~
```
お店の情報にエリア情報を紐づけるためにareas_tableに入っているデータを利用したいです。この場合、Areaモデルを使用しますと`use App\Models\Area`でuse宣言しています。use宣言をすることにより、Areaテーブルのデータを利用した処理ができるようになります。

:::note warn
つまり、use宣言していないと他が完璧でもデータベースから情報を持ってくることができません！
:::

店をお気に入り登録する際は、shops_tableの情報を利用したいです。

```php:FavoriteController
<?php
namespace App\Http\Controllers;
use App\Models\Favorite;
use App\Models\Shop;

~ 下記省略 ~
```
この場合、Shopモデルを使用しますと`use App\Models\Shop`でuse宣言しています。このように、使いたいモデルがあるときはuse宣言します。

ルーター（web.php）には、内容にもよりますが`ShopController` `AreaController` `FavoriteController` などの使用するコントローラーのuse宣言をします。


```php:web.php
<?php
use Illuminate\Support\Facades\Route;

use App\Http\Controllers\ShopController;
use App\Http\Controllers\AreaController;
use App\Http\Controllers\FavoriteController;

~ 下記省略 ~
```

# use宣言は書き忘れがち
use宣言は重要ですが、とにかく書き忘れがちです。（特に、後からモデルやコントローラーを追加したとき忘れがち…）

データが反映されない場合、ロジック部分やビューなどを見直しても原因不明なら、use宣言しているかもぜひ確かめてみてください。
selinuxの無効化

# はじめに
selinuxとは、システムにアクセスできるユーザーを管理者がより詳細に制御できるようにする Linux® システム 用のセキュリティ・アーキテクチャです。この設定値が有効になっているとうまくサービスにアクセスできない場合などがありますので必要な際に無効化してみてください。

# 無効化方法
以下コマンドにてSELinuxの設定を確認できます。
```
getenforce
```
出力結果が``Enforcing``なら有効化になっています。``vi /etc/selinux/config``にて設定ファイルを書き換えましょう。以下のように変更してください。
```
# This file controls the state of SELinux on the system.
# SELINUX= can take one of these three values:
#       enforcing - SELinux security policy is enforced.
#       permissive - SELinux prints warnings instead of enforcing.
#       disabled - No SELinux policy is loaded.
SELINUX=disabled
# SELINUXTYPE= can take one of these two values:
#       targeted - Targeted processes are protected,
#       mls - Multi Level Security protection.
SELINUXTYPE=targeted
```

次に再起動をかけます。
```
reboot
```


# 参考文献
* [SELinuxとは](https://www.redhat.com/ja/topics/linux/what-is-selinux)
* [SELinuxの設定値の確認](https://access.redhat.com/documentation/ja-jp/red_hat_enterprise_linux/6/html/security-enhanced_linux/sect-security-enhanced_linux-working_with_selinux-enabling_and_disabling_selinux)

* [SELinuxの無効化](https://access.redhat.com/documentation/ja-jp/red_hat_enterprise_linux/8/html/using_selinux/enabling_and_disabling_selinux-disabling_selinux_changing-selinux-states-and-modes)
FastAPI x React のDockerチートシート

# ディレクトリ構成

```
AppName
    ├─ back
    │   └ Dockerfile
    ├─ front
    │   └ Dockerfile
    └ docker-compose.yaml
```

## 事前準備
- requirements.txt

# back の Dockerfile

```Dockerfile
FROM python:3.10.6
WORKDIR /app
COPY requirements.txt requirements.txt
RUN pip install -r requirements.txt
COPY . .
CMD ["uvicorn", "main:app", "--reload", "--host", "0.0.0.0", "--port", "8000"]
```

# front の Dockerfile

```Dockerfile
FROM node:14
WORKDIR /app
COPY . .
RUN npm install
RUN npm run build
CMD ["npm", "start"]
```

# docker-compose.yaml
```yaml
version: '3'
services:
  {App名}-backend:
    build:
      context: ./back
    ports:
      - "8000:8000"
    restart: always
    tty: true
    depends_on:
      - db

  {App名}-frontend:
    build:
      context: ./front
    ports:
      - "3000:3000" 

  db:
    image: postgres:15
    volumes:
      - ./app/db/postgresql/data:/var/lib/postgresql/data
      - ./app/db/postgres:/docker-entrypoint-initdb.d
    ports:
      - "5432:5432"
    environment:
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=postgres
      - POSTGRES_INITDB_ARGS=--encoding=UTF-8 --locale=C

volumes:
  postgres_data:
```

# Python側でのDB接続箇所

```python
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, scoped_session


SQLALCHEMY_DATABASE_URL = "postgresql://postgres:postgres@db:5432/postgres"

if SQLALCHEMY_DATABASE_URL:
    engine = create_engine(
        SQLALCHEMY_DATABASE_URL,
        pool_pre_ping=True,
    )
    SessionLocal = scoped_session(
        sessionmaker(autocommit=False, autoflush=False, bind=engine)
    )

    Base = declarative_base()
    Base.metadata.create_all(bind=engine)

# Dependency
def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()

```
ラスターデータの外枠

# こんな図を作ります
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/811209/5be428a6-1a83-633f-345f-4f5b1987f8db.png)
ググっても出てこなかったので作りました．↓

# ラスターデータの外枠をcartopy上で描写
全球-180, 180, -90, 90の範囲で
解像度5分(赤道で9km)の0と1で構成されたラスターデータを考えます
x軸方向に4320
y軸方向に2160
グリッド存在する空間だと思ってください
1の部分は東京の都市域を表す都市マスクです
全球のうち138-141E, 34-37Nの範囲をcartopyで描写します
描写された空間内に1で示された都市マスクの外枠をハイライト
```python
import numpy as np
import matplotlib.pyplot as plt
import cartopy.crs as ccrs

one = 138
two = 141
three = 34
four = 37
upperindex = (90-four)*12
lowerindex = (90-three)*12
leftindex = (180+one)*12
rightindex = (180+two)*12
img_extent = (one, two, three, four)
delta = 360/4320
x_left = img_extent[0]
y_top = img_extent[3]
    
raster_data = mask[upperindex:lowerindex, leftindex:rightindex]

# outer
upper_indices = []
lower_indices = []
left_indices = []
right_indices = []
rows, cols = raster_data.shape
for i in range(rows):
    for j in range(cols):
        if raster_data[i, j] == 1:
            if raster_data[i-1, j] == 0:
                upper_indices.append((i, j))
            if raster_data[i+1, j] == 0:
                lower_indices.append((i, j))
            if raster_data[i, j-1] == 0:
                left_indices.append((i, j))
            if raster_data[i, j+1] == 0:
                right_indices.append((i, j))
                
fig = plt.figure(figsize=(10,10))
ax = plt.subplot(projection=ccrs.PlateCaree())
ax.coastlines()
ax.set_extent(img_extent)
ax.gridlines(draw_labels=True)               

for up in upper_indices:
    x_index = x_left + delta*(up[1])
    y_index = y_top - delta*(up[0])
    ax.plot([x_index, x_index+delta], [y_index, y_index], color='red', transform=ccrs.PlateCarree())
for lo in lower_indices:
    x_index = x_left + delta*(lo[1])
    y_index = y_top - delta*(lo[0]+1)
    ax.plot([x_index, x_index+delta], [y_index, y_index], color='red', transform=ccrs.PlateCarree())
for le in left_indices:
    x_index = x_left + delta*(le[1])
    y_index = y_top - delta*(le[0]+1)
    ax.plot([x_index, x_index], [y_index, y_index+delta], color='red', transform=ccrs.PlateCarree())
for ri in right_indices:
    x_index = x_left + delta*(ri[1]+1)
    y_index = y_top - delta*(ri[0]+1)
    ax.plot([x_index, x_index], [y_index, y_index+delta], color='red', transform=ccrs.PlateCarree())

plt.show()
```
Excel VBAで複数項目の時系列csvファイルをグラフ化

## 概要

下記のような形式のcsvの時系列データについて、毎回グラフ化するのが面倒だったので
マクロを作りました。
数が増えてくると、設定するのが大変だったので

| Time | データA列 | データB列 | データC列 |
| -- | -- | -- | -- |
| 1 | A1 | B1 | C1 |
| 2 | A2 | B2 | C2 |
| 3 | A3 | B3 | C3 |
| … | … | … | … |
## 使い方
Main()マクロ実行後、上記の形式のcsvファイルをライブラリで選択してください。
あとはSheet1のシートにグラフが系列の数の分作成されます。
データ列はこれより数十個に増えても動作します。
各図の表題は、データn列となりグラフが、作られます。お好みに合わせて修正して下さい。

## ソースコード

```vb
' 概要: シートをクリアして、csvからグラフを作成
Sub Main()
    CopyCSVContents
    CreateGraphs
End Sub

' 概要: csvファイルを取り込む
Sub CopyCSVContents()
    Dim csvLine As String
    Dim csvValues As Variant
    Dim csvFilePath As String
    Dim rowIndex As Long, columnIndex As Long

    ' CSVファイルのパスをダイアログで指定
    csvFilePath = Application.GetOpenFilename("CSVファイル (*.csv), *.csv")

    ' ADODBストリームオブジェクトを作成
    With CreateObject("ADODB.Stream")
        .Charset = "UTF-8"
        .Open
        .LoadFromFile csvFilePath
    
        ' ストリームの終端までループを実行
        Do Until .EOS
            csvLine = .ReadText(-2)
            rowIndex = rowIndex + 1
            csvValues = Split(csvLine, ",")
            For columnIndex = 0 To UBound(csvValues)
                ' CSVの値をExcelシートに挿入
                Cells(rowIndex, columnIndex + 1) = csvValues(columnIndex)
            Next columnIndex
        Loop
        .Close
    End With
End Sub
' 概要: グラフを作成する。
Sub CreateGraphs()
    Dim ChartOne As Chart
    Dim RngGraph As Range
    Dim i As Integer
    Dim lastRow As Long
    Dim lastColumn As Long
    
    ' ワークシートを指定
    Set ws = ThisWorkbook.Sheets("Sheet1")
    
    ' 最終行列の取得
    lastRow = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row
    lastColumn = ws.Cells(1, ws.Columns.Count).End(xlToLeft).Column

    ' グラフを作成するためのループ処理を開始
    For i = 1 To lastColumn - 1
        ' グラフの表示位置とサイズの設定
        Set RngGraph = Cells(2 + 10 * (i - 1), 2).Resize(10, 20)
        Set ChartObj = ActiveSheet.ChartObjects.Add(RngGraph.Left, RngGraph.Top, RngGraph.Width, RngGraph.Height)
        Set ChartOne = ChartObj.Chart

        ' A列をx軸、i+1列をy軸として範囲を格納
        Set chartRangeX = ws.Range(Cells(2, 1), Cells(lastRow, 1))
        Set chartRangeY = ws.Range(Cells(2, i + 1), Cells(lastRow, i + 1))
    
        ' グラフの種類を散布図に設定
        ChartObj.Chart.ChartType = xlXYScatterSmoothNoMarkers
    
        ' データ範囲を設定
        ChartObj.Chart.SetSourceData Source:=Union(chartRangeX, chartRangeY)
        
        ' タイトルを設定
        ChartObj.Chart.HasTitle = True
        ChartObj.Chart.ChartTitle.Text = ws.Cells(1, i + 1).Value
        
        ' 注釈を非表示
        ChartObj.Chart.HasLegend = False
    Next i
    
    MsgBox "グラフを " & lastColumn - 1 & " 個作成しました。"
End Sub


```
Angular v17 ＆ Jest & Angular Testing Library セットアップ覚書

`Angular` v17の新規プロジェクトに `Jest` と `Angular Testing Library` をセットアップする覚書です。

# パッケージのバージョン

watchモードが使いたいので、ビルダーに [@angular-builders/jest](https://www.npmjs.com/package/@angular-builders/jest) を使います。

- `@angular/core` 17.3
- `jest` 29.7
- `@testing-library/angular` 16.0
- `@angular-builders/jest` 17.3

# パッケージのアンインストール & インストール

`Karma` 関連のパッケージを削除します。

```sh
npm remove karma\
  karma-chrome-launcher\
  karma-coverage-istanbul-reporter\
  karma-jasmine\
  karma-jasmine-html-reporter
```

`Jest` と `Angular Testing Library` 関連のパッケージをインストールします。

```sh
npm i -D jest \
  @types/jest\
  @angular-builders/jest\
  @testing-library/jest-dom\
  @testing-library/angular
```

# ユニットテストの設定

`Angular` の設定を変更します。

```ts:angular.json
"architect": {
  "test": {
    "builder": "@angular-builders/jest:run",
    "options": {
      "polyfills": [
        "zone.js",
        "zone.js/testing"
      ],
      "tsConfig": "tsconfig.spec.json",
      "configPath": "jest.config.ts"
    }
  }
}
```

```ts:tsconfig.spec.json
{
  "extends": "./tsconfig.json",
  "compilerOptions": {
    "outDir": "./out-tsc/spec",
    "types": [
      "jest",
      "@testing-library/jest-dom"
    ]
  },
  "include": [
    "src/**/*.spec.ts",
    "src/**/*.d.ts"
  ]
}
```

`Jest` の設定ファイルを新規に作成します。

```ts:{PROJECT_ROOT}/jest.config.ts

import type { Config } from 'jest';

const esmPackages = ['@angular/*', '@testing-library/angular'];

const config: Config = {
  preset: 'jest-preset-angular',
  setupFilesAfterEnv: ['<rootDir>/jest.setup.ts'],
  moduleDirectories: ['node_modules', '<rootDir>'],
  transformIgnorePatterns: [`node_modules/(?!(${esmPackages.join('|')})/)`],
};

export default config;
```

```ts:{PROJECT_ROOT}/jest.setup.ts

import '@testing-library/jest-dom';
import 'jest-preset-angular/setup-jest';
```

# 最初のテスト

試しにコンポーネントを新規作成してテストしてみます。

```ts:test1.component.ts

import { Component } from '@angular/core';

@Component({
  selector: 'app-foo',
  standalone: true,
  template: `
    <input title="foo" />
    <button>bar</button>
  `,
})
export class Test1Component {}
```

```ts:test1.component.spec.ts

import { render, screen } from '@testing-library/angular';
import { Test1Component } from './test1.component';

describe('Test1Component', () => {
  it('test', async () => {
    await render(Test1Component);

    expect(screen.getByTitle('foo')).toBeInTheDocument();
    expect(screen.getByRole('button')).toBeInTheDocument();
  });
});
```

```sh
ng test --watch

 PASS  src/app/test1.component.spec.ts
  Test1Component
    ✓ test (113 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        1.386 s
Ran all test suites related to changed files.

Watch Usage
 › Press a to run all tests.
```

テストが通りました！

利用パッケージもそれほど多くなく、かんたんにセットアップできますね。
Bootstrap制作的几个站点分享

:root {
  --custom-color-list-page-number: #FF6F0F; /* 定义自定义颜色变量 */
} 
2.文章列表的分页样式
.paging_list {
list-style: none;
display: flex;
justify-content: center;
margin-top: 20px;
padding: 0;
}
.paging_list li {
margin: 0 5px;
}
.paging_list a {
display: flex;
align-items: center;
justify-content: center;
background-color: #f0f6ff; /* 小正方形背景颜色 */
width: 40px;
height: 40px;
border-radius: 3px;
text-align: center;
border-radius:17px;
text-decoration: none;
color: black;
font-size: 14px;
}
.paging_list a:hover {
background-color: var(--custom-color-list-page-number); 
}
.paging_list li.on a {
background-color: var(--custom-color-list-page-number);  /* 选中状态的背景颜色 */
}
3.在手机端一行只显示一个项目
/* 媒体查询 - 手机端样式 */
@media screen and (max-width: 767px) {
    .zdy_list_one {
        width: calc(100% - 20px); 
    }
}
4.自定义标题的颜色和居中
.n-title{
text-align: center;
color: #000;
} 
5.最好用的调整模块代码
.zdy_s_gun_app_item {
  flex: 0 0 calc(25% - 20px); /* Adjust the width as needed */
  display: flex;
  flex-direction: column; /* Stack items vertically */
  justify-content: center;
  align-items: center;
  background-color: #f0f6ff; /* 小正方形背景颜色 */
  text-align: center;
  padding: 20px;
  margin-right: 20px; /* Adjust the margin as needed */
  box-sizing: border-box; /* Include padding in element's total width and height */
} 
下面是我自己建的几个站点仅供参考部分代码每个站点都稍有改动 
- [https://yabo.ac/](https://yabo.ac/)
- [https://www.yabo.gg/](https://www.yabo.gg/)
- [http://yabo-cc.com/](http://yabo-cc.com/)
- [https://www.kobebryantshoes10.com/](https://www.kobebryantshoes10.com/)
AUTO_INCREMENTで正しくインクリメントされない

C#の勉強の中でSQLiteを使ってたら、主キーかつAUTO_INCREMENTを実現するのに無駄に時間がかかったのでそれについて。
# ハマったこと
SQLiteを使って下のコードでidが主キーかつAUTO_INCREMENTなテーブルを作成したつもりが、レコードを追加してもインクリメントされなかった。
```sql:失敗コード
CREATE TABLE hoge(
    id INTEGER AUTO_INCREMENT PRIMARY KEY,
    //他のフィールド
)
```

# 解決策
SQLiteではPRIMARY KEYを設定したら勝手にオートインクリメントになるらしく、AUTO_INCREMENTを付ける必要がなかった。消したらうまくいった。（別に付けても動いてくれて良くないか???）
```sql:成功コード
CREATE TABLE hoge(
    id INTEGER PRIMARY KEY,
    //他のフィールド
)
```
# まとめ
SQLiteで主キーかつAUTO_INCREMENTにするときは、PRIMARY KEYだけ付ければよい(MySQLでは両方書く必要がありそうなので、違いに注意）。
AWS CLI 備忘録

# この記事について

AWS CLI でよく使うコマンドを個人的な備忘のためにまとめます。

# プロファイル関係

AWS CLI をどのアカウントのどのユーザーとして使うかを管理する。
こちらは AWS CLI ではなく一般的なターミナルのコマンドとして行う。

## プロファイルの一覧を見る

どんなプロファイルを作っていたかを確認する

```shell:プロファイルの一覧を見る
cat ~/.aws/config
```

出力はこんな感じ。アクセスキーなどは表示されない。profile のあとの名前でアクセスを切り分ける。

```shell:出力結果
[profile chihiro-work]
region = us-east-1
output = json
[profile chihiro-personal]
region = ap-northeast-1
output = json
```

## プロファイルの切り替え

AWSCLIコマンドをどのプロファイルでたたくかを切り替える

```shell:プロファイルの切り替え
export AWS_PROFILE=プロファイル名
```

# S3関係

## バケットの一覧を表示

```shell:バケットの一覧を表示
aws s3 ls
```

## ファイルのアップロード

```shell:バケットの一覧を表示
aws s3 cp コピー元ファイルのパス s3://bucket-name/path/
```

# 今のところは以上です

ちょこまか足していきます。
Ubuntuから外付けHDDへのマウント

UbuntuからNTFSフォーマットされた外付けHDDをマウントする方法です。
以下にその手順を説明します。

### 必要なパッケージのインストール

UbuntuでNTFSファイルシステムをサポートするためには、`ntfs-3g` パッケージが必要です。このパッケージは、NTFSパーティションの読み書きを可能にするものです。まずは、このパッケージがインストールされているかを確認し、インストールされていなければインストールします。

```bash
sudo apt-get update
sudo apt-get install ntfs-3g
```

### 外付けHDDのマウント

1. **デバイスの識別**:
   まず、接続されている外付けHDDのデバイスファイルを確認します。`lsblk` コマンドを使用して、接続されているデバイスの一覧を表示できます。

   ```bash
   lsblk
   ```

   このコマンドの出力から、外付けHDDが例えば `/dev/sdb1` として識別されていることを確認します。

2. **マウントポイントの作成**:
   マウントする前に、マウントポイントとなるディレクトリを作成します。

   ```bash
   sudo mkdir /media/external
   ```

3. **マウントの実行**:
   以下のコマンドを使用して、NTFSフォーマットされた外付けHDDをマウントします。

   ```bash
   sudo mount -t ntfs-3g /dev/sdb1 /media/external
   ```

   ここで、`/dev/sdb1` は外付けHDDのデバイスファイル、`/media/external` は作成したマウントポイントです。

### 自動マウントの設定

毎回コマンドを実行してマウントするのではなく、システム起動時に自動でマウントさせたい場合は、`/etc/fstab` ファイルにエントリを追加します。

```bash
sudo nano /etc/fstab
```

以下のような行をファイルに追加します：

```
/dev/sdb1 /media/external ntfs-3g defaults 0 0
```

これで設定が完了し、Ubuntu上でNTFSフォーマットの外付けHDDを使用する準備が整います。データの交換やバックアップなどに利用できます。
Atcoderを真剣に入色しよう～

# 自己紹介
24歳求職中(2024年時点)
卒業大学 立命館大学理工学部電子情報工学科
趣味 ブレイクダンス 音楽
好きな音楽はUK GarageやDnBのようなBreakBeatsが起源の音楽です
# きっかけ
大学2年次に授業内容を具体的に掴むためにAtcoderを始めた。しかし現状はC問題でいっぱいいっぱいである、そのため真剣に入色するために自分の解法のアウトプットと解説や実装を読んで理解したことをメモとして残していくこととする。
# 目標
現在のレートが107(入茶は400から)のため、2024年中にC問題を難なく解けるようにしてD問題を解けるようにする。
Rustを用いて入色する。
# 使用する言語
今はC++を使用している。今後はRustを用いる。
そのため、Rustに関するライブラリやチートシートをメモとしてQiita上に残していくこととする。

# 最終目標
入水したいです、頑張ります。