OmniAICreator/Qiita-1.07M huggingface dataset:
【OCI】SQL DeveloperでOracle Base Database Serviceに接続する方法

## はじめに
Oracle Cloud Infrastructure（OCI）のOracle Base Database Service (BaseDB)へ、SQL Developerを用いて接続する方法をご紹介していきます。

## BaseDBにSQL Developerで接続する
**■ 前提条件**
OCI上でBaseDBを作成します。
以下のチュートリアルを参考に作成してみましょう。
今回はチュートリアルと同様にパブリックサブネットにBaseDBを作成しています。

https://oracle-japan.github.io/ocitutorials/basedb/dbcs101-create-db/

 **1. データベースの接続設定の確認**
SQL Developerを起動し、画面左上の緑の「+」ボタンをクリックして、データベースの接続先設定を追加します。
![画像14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3072173/cd15c20e-c625-47fd-aa23-b808cfa85a34.png)

以下の内容を入力していきます。
* Name ： 任意の接続先名
⇒名前は自由に決めて大丈夫です
* データベースのタイプ ：データベースの種類 
⇒「Oracle」を設定します
* ユーザー名 ： Oracleデータベースのユーザー名
⇒今回はSYSユーザーを使います
* ロール：ユーザーが割り当てられているロール
⇒SYSユーザーなので、「SYSDBA」を今回は選択
* パスワード ： パスワード
⇒BaseDBを作成したときに設定したSYSのパスワードです
* ホスト名 ： データベースサーバーのホスト名またはIPアドレス
* ポート : データベースのポート
⇒デフォルトでは1521になっています
* サービス名 : データベースのSID

※ホスト名とサービス名の確認方法は後述致します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3072173/fb79dfee-22d2-e892-dacf-05d7793c5235.png)

 **2. ホスト名とサービス名の確認方法**
 ホスト名とサービス名を確認するためにOCIコンソールへアクセスします。
 BaseDBの画面で、作成したデータベースをクリックします。
![画像17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3072173/c949b36e-4dec-a6ec-513f-02ecdef933c1.png)

画面の下部までスクロールして、Resourcesの「データベース」タブを選択します。
作成してあるデータベース名をクリックします。
![画像19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3072173/1e2011aa-f54e-1ddb-b0d2-2eeccf165389.png)

「DB接続」というボタンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3072173/14a5972c-3e3b-d11a-9f2e-1752964d1459.png)

接続文字列の簡易接続を表示させます。
1521というポート番号の後ろに書かれている文字列がSQL Developerの設定項目にあった「サービス名」になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3072173/3294f00e-4beb-8fc8-2728-91e03e625671.png)

※CDBへの接続ではなくPDBへの接続の場合は、画面下部のResourcesの「プラガブル・データベース」タブを選択し、接続したいPDBを選択します。その後、画面上部のPDB接続をクリックし、接続文字列の簡易接続を表示させます。1521というポート番号の後ろに書かれている文字列がPDBに接続したい場合の「サービス名」になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3072173/573fb781-57df-46e8-1637-cd3c99758980.png)

続いて、1つ前の画面に戻り、画面の下部までスクロールして、Resourcesの「ノード」タブを選択します。
そこで記されている、「パブリックIPアドレス」がSQL Developerの設定項目にあった「ホスト名」になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3072173/2546169a-420a-8603-677f-68f61313370a.png)

 **3. ホスト名とサービス名の確認方法**
 上記の手順を元に、項目を入力することができたら「テスト」のボタンをクリックします。
 左側のステータスで「成功」と表示されれば、データベースの接続が可能です。
 最後に接続をクリックすれば、データベースに接続することができSQLによる操作ができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3072173/0f273a2d-b187-f5c7-b101-f9391d59ac76.png)

## おまけ
CDBに接続した場合は、以下のSQLを実行することでPDBのセッションに変更することができます。
~~~SQL
alter session set container = <PDB名> ;
~~~

## おわりに
OCIで作成したBaseDBにSQL Developerで接続する際の方法を紹介しました。
特にホスト名やサービス名は、最初どこを確認すればいいか分からない部分だと思うので、そういった際に本記事がご参考になれば幸いです。
Rails7におけるdeleteメソッドの不具合解消（削除リンクが期待通りに動作しない問題）

# Rails 7で削除リンクが期待通りに動作しない問題とその解決策

## はじめに

Rails 7アプリケーション開発中、特定の条件下で`link_to`を使用して生成した削除リンクが期待どおりに機能しない問題に直面しました。この記事では、問題の詳細と解決策、そして原因についての考察を共有します。

## 問題の詳細

`link_to`ヘルパーを使用して、Railsアプリケーション内でリソースの削除を行うリンクを以下のように作成しました。

```erb
<%= link_to "削除", customer_registration_path(current_customer), data: { turbo_method: :delete} %>
```

しかし、このリンクをクリックすると、期待されるDELETEリクエストではなく、GETリクエストが送信され、アプリケーションは単にインデックスページにリダイレクトされました。ログは以下のように表示されました。

```
Started GET "/customers.3" for ::1 at 2024-02-18 00:17:49 +0900
```

## 解決策

問題の解決には、リンク生成時の`customer_registration_path`への引数の渡し方を変更することが効果的でした。

```erb
<%= link_to "削除", customer_registration_path(id: current_customer.id), data: { turbo_method: :delete} %>
```

この変更により、削除リンクが正しく機能し、期待通りのDELETEリクエストが送信されるようになりました。

## 原因と考察

### パスヘルパーの引数

`customer_registration_path(current_customer)`と記述した場合、Railsは`current_customer`オブジェクトから自動的にidを抽出し、期待するURLを生成しようとします。しかし、`.3`のような不正な形式でURLが生成される場合があります。これは、内部的なURL生成処理に何らかの不整合があることを示唆しています。

### 明示的なパラメータ指定

`customer_registration_path(id: current_customer.id)`のように、idを明示的にハッシュ形式で指定することで、Railsは正確に期待されるURL形式を生成できます。この方法は、パラメータを明示的に指定することで、内部処理におけるあいまいさを排除する効果があると考えられます。

### 環境や設定の影響

この問題は、特定のRailsバージョンや設定によって発生する可能性があり、アプリケーションの設定や依存関係のバージョンに依存する場合があります。特に、Railsのバージョンアップに伴う内部APIの変更が関連している可能性があります。

### 追記
この問題はRails7であればpatchメソッドでも生じますが、deleteと同様にすれば解消します。

## まとめ

Railsアプリケーションにおいて、`link_to`を使用した削除リンクが期待通りに機能しない問題は、パスヘルパーへの引数の渡し方を変更することで解決可能な可能性があります。
起動テンプレート簡易設定例　学習記録

起動テンプレート作成例


マネジメントコンソールのEC2サービスの起動テンプレートから起動テンプレートの作成を押下します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3146038/18d56226-c9d3-69e5-1e2e-a8aa7da00262.png)

-------------------------
１．起動テンプレート名と説明
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3146038/ac83423e-dd52-9d66-c2bc-60f2a70598d8.png)
起動テンプレート名を任意で入力します(必須)：nicnic69

-------------------------
２．アプリケーションおよびOSイメージ(Amazon マシンイメージ)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3146038/f8389c79-945f-911b-344c-881a7309e7b9.png)
AMI、インスタンスタイプ、キーペアを選択します
・AMI(任意)：Amazon Linux 2023 AMIを選択　
→クイックスタートから選択し、AMIは無料枠を指定し、プルダウンから選択
・アーキテクチャ(任意)：64 ビット (x86)
→今回はアプリケーションなどを動かさないため、なんでもよい。Intelを指定

３．インスタンスタイプ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3146038/056e35b0-09a8-9716-a0ff-c13306fd3c8b.png)
・インスタンスタイプ(任意)：t2.micro
→インスタンスタイプは無料枠を指定し、プルダウンから選択

-------------------------
４．キーペア (ログイン)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3146038/da009b30-a999-7ea0-1c09-f7ed78f62548.png)
・キーペア名(任意)：プルダウンから既存のキーペア指定

-------------------------
５．ネットワーク設定(任意)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3146038/e6879575-5f5b-5b7c-99f7-9a391e0db373.png)
・サブネット(任意)：簡易的にテストしたいため、パブリックサブネットを指定
・ファイアウォール (セキュリティグループ)(任意)：プルダウンから既存のキーペアを指定
　Allow:SSH,HTTP,HTTPS

 -------------------------
６．高度な詳細
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3146038/3f231087-ae95-0519-e994-5c38591926ec.png)

ユーザーデータオプションにて以下を指定する
テストしたいので、index.html配置予定
※簡易化させたいので、また調べる
sudo yum update -y
sudo yum install httpd -y 
sudo chkconfig httpd on

-------------------------
起動テンプレート削除CLI
aws ec2 delete-launch-template --launch-template-id lt-テンプレートID --region リージョン名

-------------------------

Auto Scaling のガイダンスチェックボックスにチェックを入れると、必須項目だけ表示されるんですね。。
知らなかった

起動テンプレートの作成だけなら、AMIの指定だけで良いみたい
エラー対処

以下エラーが出てきた時の対処法。

Assets\Scripts\StatusAuthoring.cs(2,13): error CS0234: The type or namespace name 'VisualScripting' does not exist in the namespace 'Unity' (are you missing an assembly reference?)

 

スクリプトから以下を削除する。

using Unity.VisualScripting;
【Next.js】Imageコンポーネントで画像が表示されるまでスケルトンを表示させる方法

以下のように記述することでImageコンポーネントに設定した画像のロード中にスケルトンスクリーンが表示されます。

```jsx
import Image from 'next/image'

function skeleton(w: number, h: number) {
  return `
    <svg width="${w}" height="${h}" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
      <defs>
        <linearGradient id="g">
          <stop stop-color="#d1d5db" offset="20%" />
          <stop stop-color="#f3f4f6" offset="50%" />
          <stop stop-color="#d1d5db" offset="70%" />
        </linearGradient>
      </defs>
      <rect width="${w}" height="${h}" fill="#d1d5db" />
      <rect id="r" width="${w}" height="${h}" fill="url(#g)" />
      <animate xlink:href="#r" attributeName="x" from="-${w}" to="${w}" dur="1s" repeatCount="indefinite" />
    </svg>`
}

function toBase64(str: string) {
  if (typeof window === 'undefined') {
    return Buffer.from(str).toString('base64')
  } else {
    return window.btoa(str)
  }
}

export function Avatar({avatarUrl, name}) {
  return (
    <Image
      src={avatarUrl}
      alt={name}
      width={128}
      hight={128}
      placeholder={`data:image/svg+xml;base64,${toBase64(skeleton(128, 128))}`}
    />
  )
}
```

https://nextjs.org/docs/app/api-reference/components/image#placeholder
Next.js でローカルファイルを読み込む【Module not found: Can't resolve 'fs'】

# tl;dr
こういうエラーが出たときは
```
Module not found: Can't resolve 'fs'
  1 | import path from "path";
> 2 | import fs from "fs";
  3 | export default function Home({ content }) {
  4 | const fileContents = fs.readFileSync(filename, "utf8");
  5 | return <AnotherPage content={content} />;
  ```
以下2点を抑えて`fs`を使用。
- `pages/*`のファイルで定義する
- `getStaticProps`の中で使用する

```javascript:pages/siteinfo.js
import path from "path";
import fs from "fs";
export default function Home({ content }) {
  return <AnotherPage content={content} />;
}

export async function getStaticProps() {
  const filename = path.join(process.cwd(), "contents/memo.md");
  const fileContents = fs.readFileSync(filename, "utf8");

  return {
    props: { content: fileContents },
  };
}
```

# なぜ`pages/*`以外のディレクトリでは使用出来ない？
https://nextjs.org/docs/pages/building-your-application/data-fetching/get-static-props

> ページからgetStaticProps（静的サイト生成）という関数をエクスポートすると、Next.jsはビルド時にgetStaticPropsが返すpropsを使用してこのページをプリレンダリングします。(Deepl翻訳)

ページのレンダリング時に`getStaticProps`が実行されるからのようです。
エラーメッセージ`Module not found: Can't resolve 'fs'`は少しわかりづらいような気もします。

# なぜ`getStaticProps`の中で使用する？

`async`や`await`は`getStaticProps`内で使用する必要があります。詳細な理由は後ほど調べます。

# どういう状況でこのエラーに遭遇した？

next.js14でサイトを作成中の初心者です。
markdownで書かれた記事をhtmlに変換して表示しようと試行錯誤していたところ、こんなエラーに遭遇しました。
助言や質問があれば気軽にコメントください。一緒に解決しましょう。
BashとPowershellのAPI実行結果の違いについて

PowerShellを使い始めBashと挙動の違いがあったのでまとめました。

Fivetran APIを使いユーザー情報を取得します。

### Bashの場合

```Bash
# APIとAPI秘密鍵を設定する
API_KEY="xxxxxxxx" #置き換える
API_SECRET_KEY="xxxxxxxxxxxxxxxxxxx" #置き換える

curl -X GET "https://api.fivetran.com/v1/users" -u $API_KEY:$API_SECRET_KEY | jq
```

実行結果
![スクリーンショット 2024-02-17 22.37.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449065/7375bec9-4bd3-213f-72ff-7a3239898944.png)

ユーザー情報が表示されていますね。

### PowerShellの場合

```PowerShell
# APIとAPI秘密鍵を設定する
API_KEY="xxxxxxxx" #置き換える
API_SECRET_KEY="xxxxxxxxxxxxxxxxxxx" #置き換える

# Base64でエンコード
$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $apiUser, $apiSecret)))

# ヘッダーの準備
$headers = @{
    "Authorization" = "Basic $base64AuthInfo"
}

# APIエンドポイント
$url = "https://api.fivetran.com/v1/users"

# GETリクエストの実行
$response = Invoke-RestMethod -Uri $url -Method Get -Headers $headers

# レスポンスの表示
$response
```

PowerShellは書く処理が多くて嫌になりますね。
curlコマンドだとエンコードやヘッダーは自動でやってくれるそうでその分書く量が減ると知りました。

実行結果
![スクリーンショット 2024-02-17 23.06.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449065/5f09f783-b53a-2499-922e-dcb28416ba8e.png)

APIはBashと同じものを使っています。
Successと表示されているもののユーザー情報が表示されていません。

## 原因：PowerShell Invoke-RestMethodの仕様
この原因はInvoke-RestMethodコマンドレットにありました。

> PowerShellでInvoke-RestMethodを使ってAPIからJSONレスポンスを受け取った場合、そのレスポンスは自動的にPowerShellオブジェクトに変換されます。
この挙動は便利ですが、結果の表示が必ずしも期待通りにはならない場合があります。
具体的には、Success @{items=System.Object[]}のように表示された場合、これはPowerShellがitemsプロパティがオブジェクトの配列を含んでいることを示していますが、配列内の各オブジェクトの内容までは直接表示しません。
by chatGPT

```PowerShell
data
---
@{items=System.Object[]}
```

この出力結果はitemsが空という意味ではなく、このitemsプロパティはオブジェクトの配列配列を持つという意味だったそうです。
まじかよ。

なぜJSONを表示しないのかも聞きました。

> PowerShellのデフォルトの出力フォーマットは、深いネストされたオブジェクトや配列の内容を全て展開して表示しないことがあります。これは、特にコンソールウィンドウでの読みやすさを考慮した結果です。System.Object[]は、配列内のオブジェクトがPowerShellによって一般的なオブジェクトとして認識されていることを示しますが、具体的な内容はこの表示からはわかりません。

なるほど。
ネストが深くなるJSONはわざと表示しないようにしているようですね。

### 解決方法：ConvertTo-Jsonを使う
ネストが深いJSONを表示するときはConvertTo-Jsonコマンドレットを使うと良いそうです。
そうすることでレスポンスオブジェクトを再びJSON文字列に変換してから表示します。

```PowerShell
...
# GETリクエストの実行
$response = Invoke-RestMethod -Uri $url -Method Get -Headers $headers

# レスポンスの表示
$response| ConvertTo-Json | Write-Output # コマンド追加
```

実行結果
![スクリーンショット 2024-02-17 23.20.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449065/70824c84-2a38-4ed2-f3fa-b60c5b1da9ef.png)

ユーザー情報が表示されました。
表示されないときはエンコードやヘッダーがうまく設定できないと疑いましたが、最初から取れていたんですね。

今回のようにネストが深いときは警告文が出ています。

> WARNING: Resulting JSON is truncated as serialization has exceeded the set depth of 2.

日本語
警告：シリアライズが設定された深さ 2 を超えたため、結果の JSON は切り捨てられます。

JSON関連で警告も出ていました。
itemsプロパティの値が整形できていないのはネストの上限値を超えたことが原因かもしれませんね。

設定で変更できるかもしれません。

## まとめ
PowerShellでJSONを表示するときは必ず ConberTo-Json を付けましょう。

PowerShellでもAPIコールできないこともないですが、Bashの方が使いやすいなと思いました。
【Vue.js】inputタグで選択した画像を表示する

こんな感じで選択した画像が表示されます。

[Vue Playground](https://play.vuejs.org/) で試してみてください。

![スクリーンショット 2024-02-17 23.20.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/92383/a08cacce-0be4-0261-a7c7-b917a3bfadaa.png)

```vue
<template>
  <form>
    <div>
      <input type="file" @change="displayImage" />
      <img :src="image" style="max-width: 500px" />
    </div>
  </form>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const image = ref<string>('');

function displayImage(event: Event) {
  let file = event.target.files[0];
  if (new RegExp('^image/(png|jpeg)').test(file.type)) {
    let url = URL.createObjectURL(file);
    image.value = url;
  } else {
    image.value = '';
  }
}
</script>

```
公式ドキュメントでつかんだReactの核心Part2 ~Context編~

# はじめに
どうもこんにちは！僕は現在1ヵ月本気の技術力向上を目指している現役高校生1年生です！
今回は前回の記事に引き続き、僕がReactで躓いたところを解説していきたいと思います。Context編、ぜひお楽しみあれ

# Contextってなに？
そもそもContextって何だろうと思って調べてみました。
こちらの記事がとてもわかりやすかったです。この段階ではコンテキスト(Context 以降カタカナで記述します)の語義を理解して、コンテキストの仕様を府に落とすことが大事かなと思います。
<font color="grey">僕はこういう語源から入るの好きなタイプです。でも英語話者からしたら当たり前なのか笑</font>

https://qiita.com/dojyorin/items/0bd3ef167991cfc703b1

# コンテキストの仕様を簡易的に理解する
ReactではContextは、コンポーネント分割によりコードが複雑になってしまった場合に状態管理を便利にしてくれるものです。
<br/>
コンテキストが必要になってくる流れを簡単にまとめると、
1. コンポーネントを分割する・増やす
2. ネストが深くなっていく
3. Propsを渡す回数が多くなってくる 
3. めんどくさくなってくる笑
4. コンテキスト使おう！！

とまぁこんな感じです.

コンテキストを一言で表すと<font color="red">**グローバルな状態管理をしてくれる機能**</font>です。
Context = 機能ということではありませんが、だいだいこんなイメージをつかめていればOK！次の手順を読めば理解が深まるかもです。


# Contextの使い方♪
**1.Contextを生成しよう**
コンテキストは言わば状態を管理する変数。
```react
export const Context = createContext(初期値);
```

**2.Contextに状態を渡そう**
```react
export const App = () => {
    return(
        <Context.provider value={渡したい値}>
            {/** いくつかのコンポーネント */}
        </Context.provider>
    );
}
```
このContext.providerで囲まれた子要素は、*value="渡したい値"*
をコンテキストとして受け取ることができる。どれだけネストされたコンポーネントであったとしても、

**<font color="red"><コンテキスト名.provider>によって囲まれている、かつそれがコンポーネントにとって最も近いプロバイダー(provider)である*</font>**

ならばvalueの値をコンテキストとして受け取ることが出来る。

> --語義から考えるプロバイダーとは--
その語義のとおり、**提供するもの**という意味です。プロバイダーでコンポーネントを囲むことによって、値を提供することが出来ます。

3.Contextを受け取ろう
```react
const { Context } = useContext(UserContext);
```
<font color="red">**useContext**</font>というhooksを使用することで、**最も近い**プロバイダーから提供されたコンテキストを参照することが出来るようになります。
これにて晴れてコンテキストを使用することが出来ます。思う存分状態使いましょう！！笑

# コンテキストを使用する際の注意点
コンテキストを使う上で意識しておくべきことはずばり、プロバイダーによって渡されたvalue(コンテキスト)が変更された際にその値を使用しているコンポーネントは<font color="red">**再レンダリングの対象**</font>となるということです。

それに加えて、Reactの再レンダリングが走る条件である、

<font color="red">**その親コンポーネントがレンダリングされること**</font>

が悪さをしてきます。
<br/>
<br/>

言葉だけだとわかりにくいのでサンプルコードで考えてみましょう
```react
const Context = createContext(初期値);
export const App = () => {
    return(
        <Context.provider value={渡したい値}>
            <SampleA />
        </Context.provider>
    );
```
```react:SampleA.jsx
export const SampleA = () => {
    const Context = useContext();
    return(
        <>
            <p>{context}</p>
            {/**SampleBはコンテキストを使用しない*/}
            <SampleB />
        </>
    );
}
```
仮にこのようにプロバイダーにSampleAというコンポーネントが子要素として設定されているとします。
そのSampleAのコンポーネントの中身は、上記のように構成されています。

SampleBというコンポーネントはコンテキストの値を使用することはありませんが、SampleAがコンテキストを使用することでSampleBもまた、レンダリング対象になってしまいます。
<br />
<font color="red">**memo**</font>を使用することでそれを解決することが出来ます。
```react:SampleB.jsx
export const SampleB = memo(() => {
    return(
        <div>
            {/** 複雑な処理 */}
        </div>
    );
});
```
このようにすることで、SampleBは自身に渡されるprops(今回はない)が変更されない限り再レンダリングを走らないように設定することが出来ます。

# 終わりに
コンテキストについてもなれてしまえば簡単なので、グローバルな状態管理の便利さを体感してみましょう！！
さらにRecoilというものを使えばもっと簡単にグローバルな状態管理を実装することが出来ます。気になる方はぜひ知らべてみてください！

# 参考

https://ja.react.dev/learn/passing-data-deeply-with-context

https://qiita.com/dojyorin/items/0bd3ef167991cfc703b1

# About Me
- Twitter
https://twitter.com/shunii_hack
Laravelのアクセサ（Accessor）について

## 前提
- Laravelを最近はじめたばかりのエンジニアの卵です。
- 生暖かい目で見守ってください。
- また間違った情報があればご指摘いただけると泣いて喜びます。


## アクセサ（Accessor）ってなに
- アクセサは、ある属性（プロパティ）の値を取得する際に、その値を変換して返す方法です。

#### 使えそうな場面

1. 「first_name」と「last_name」苗字と名前を別カラムで管理しているけど、表示するときはデータ取得する時にはそれをくっつけた「full_name」として取得したい！
2. 金額を数値だけで取っているけど、「￥10,000-」みたいな形で取得したい！
3. 頭文字だけ大文字にしたい！
    →こんなときに便利です！！:v_tone1:


## 使い方
#### 1. モデルに記載していきます！

- フルネームを取得する例 （例：「山田 太郎」）

    ```php:User.php
    protected $appends = ['full_name']; # モデルのインスタンスがJSON形式に変換される際に、「full_name」の結果が新しい属性として追加する場合にいれる！
    
    public function getFullNameAttribute()
    {
        return $this->last_name . ' ' . $this->first_name;
    }
    ``` 

#### 2. 実際呼び出してみる！
- アクセサ名の「getFullNameAttribute」部分の「get」と「Attribute」をなくした「FullName」→「full_name」スネークケースで記述することで呼び出せます！
    - Tinkerで検証！
        ```php: Tinker
        php artisan tinker
        
        > $user = User::find(1);                                       
        echo $user->full_name;
        ```
    
        ```php:結果
        吉本 亮介⏎
        > 
        ```
        表示された:v_tone1:


### 3.さいごに
- Laravelには便利なものがたくさん集まっていて毎日楽しいです。
- 記載情報に不備などがあればご指摘いただけますと幸いです。
FastAPI on_eventがlifespanに変更

こんばんは、最近FastAPI触ることあったので備忘です。

これまで起動時と終了時の処理は`on_event`を使用していましたが、最新バージョンではなくなってしまいました。

https://fastapi.tiangolo.com/advanced/events/

旧コード：
```python
@app.on_event("startup")
async def startup_event():
    print("startup event")
    
@app.on_event("shutdown")
async def shutdown_event():
    print("shutdown event")
```

新コード：
```python
from contextlib import asynccontextmanager

from fastapi import FastAPI

@asynccontextmanager
async def lifespan(app: FastAPI):
    print("startup event")
    yield
    print("shutdown event")
    
app = FastAPI(lifespan=lifespan)
```

以上、簡単な備忘でした。
AdGuard を使って TradingView のモーダルウィンドウ広告を非表示にする。

## この記事の目的

この記事では、[AdGuard](https://adguard.com/) を使って [TradingView](https://www.tradingview.com/markets/) のモーダル広告を表示させない方法を説明します。

## どんな広告が表示されるのか

無料で TradingView のチャートを使用している場合、一定時間を経過すると、以下のようなモーダルウィンドウ広告が表示されます。  
この広告を非表示にします。

![withAd](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/566576/e497199d-ffca-57ec-cf57-78fb25dd64ac.png)

## 手順

### 手順 1. ブラウザに AdGuard の拡張機能をインストール

以下は AdGuard のインストールページへのリンクです。  
使用しているブラウザに合わせて拡張機能をインストールしてください。

- [Chrome](https://chromewebstore.google.com/detail/adguard/bgnkhhnnamicmpeenaelnjfhikgbkllg?hl=ja)
- [Edge](https://microsoftedge.microsoft.com/addons/detail/adguard/pdffkfellgipmhklpdmokmckkkfcopbh?hl=ja-JP)
- [Firefox](https://addons.mozilla.org/ja/firefox/addon/adguard-adblocker/)

### 手順 2. AdGuard のユーザールールの設定

ユーザールール内に、以下のルールを追記して保存します。

```
|https://static.tradingview.com/static/bundles/go-register-dialog.\*
```

![User rules](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/566576/f104ca5f-db19-d4ac-0344-be0d038703e0.png)

## 補足

[Stylus](https://addons.mozilla.org/ja/firefox/addon/styl-us/) のブラウザ拡張機能も試しています。  
うまく行けば追記します。
安全なWebアプリケーションの作り方1,2章の記録

# 自己紹介
初めまして。
2月より組込エンジニアからwebエンジニアに転職しました。
web系に関しては転職前に友人とvueを使ったサイトを学んだ(少し教えてもらった程度)くらいで知識がほぼない状態です。
これからいろいろなことを学んではいかないと思っているので、その備忘録、モチベーションの維持として記事を作成していきたいと思っています。

## 目標
- できる限り毎日学習をしていきたいのですが、現実的に難しいので、一週間に5時間程度できたらと思っています。
（継続できるようになったら増やしていきたいですね。。。）

## 学習すること
- **安全なWebアプリケーションの作り方**を学んでいこうと思います。

### 選んだ理由
- 将来的にWebアプリを作成してみたいから
- 基本的なことを学べそうだから
  - 基本的なこととは
  1. HTTP、セッションについて
  1. セキュリティについて
  1. 脆弱性について
  1. 開発プロセスについて
- 知名度が高く、学べることが多いと思ったから

### 今日やったこと
- 実習環境のセットアップ
FireFoxの設定ではFoxyProxyのバージョンアップに伴い、設定のインポートができなくなっていました。
ここで少しハマりました。
徳丸さんのX(旧：Twitter)でもあるので、おいておきます。
- https://x.com/ockeghem/status/1754367906459291910?s=20
- https://wasbook.org/foxyproxy8.html
nodebrew導入（Macbook Pro）

# はじめに
初めまして！
エンジニアになって数年、今まで本を読むだけでしたが、もっとプライベートで楽しみながら成長したい！自分が学んだ足跡を残していきたい！と思い記事をかきました！
最終的には自在に開発できるようになりたいと思っています。:triumph:
いろいろな記事を参考にさせてもらっています。:bow_tone2:
その中でもこれってどういう意味？とかつまづいたところを念入りに書いていこうかと思います。:fist:

# 今回の目的
前回の続きです！

https://qiita.com/mr_noppo/items/49508bb10e6f3370afe2

# 使用したものや事前準備
Macbook Pro
Homebrew

# 主に参考にさせていただいた記事
https://zenn.dev/takuty/articles/36073d76ed50c5

# nodebrewってなんぞや
一言で言っちゃえば、Node.jsのバージョン変更が簡単にできるようになるツールです。
バージョンなんてそんな気にしなくていいのでは？と思ってしまいましたが、甘すぎました。:frowning2:
パッケージの依存関係やAPIの互換性で問題が発生することや、チーム開発でバージョンが一致していないと予期せぬバグの原因になることがあるようです。
各プロジェクトのバージョンに合わせたNode.jsにするなら、すぐに切り替えられるnodebrewが重要なのも納得です。

# nodebrewをインストールする
※Node.jsが入っていない前提で話を進めます。
下記のサイトでコマンドをコピーして、実行します。

https://formulae.brew.sh/formula/nodebrew
```
brew install nodebrew
```
無事入りました。:fist:

![スクリーンショット 2024-02-17 20.22.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/943982/f9b2253b-b89b-b488-e7c4-4c4f860810fa.png)

# nodebrewの設定
インストールした後にセットアップしろっていう指示が来てるようです。完全に見逃していました。:eyes:
![スクリーンショット 2024-02-17 20.27.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/943982/8fbf382a-cdcb-1911-9b87-8c63b52a32b2.png)

下記のコマンドはnodebrewが必要とするディレクトリを作成してくれます。実行します。
```
/opt/homebrew/opt/nodebrew/bin/nodebrew setup_dirs
```
次に実行シェルを確認して恒久的なPATH 環境変数を設定します。
```
echo $SHELL
```
※以降、zsh前提で進めます。

Vimでファイルを編集します。（ファイルが存在しない人は作成して問題ありません）
```
vim ~/.zshrc
```
iボタンを押すと編集できます。下記のコマンドを貼り付けましょう。
```
export PATH=$HOME/.nodebrew/current/bin:$PATH
```
ターミナル上からnpmやnodeコマンドを実行するためのPATH 環境変数を設定しました。
（※他にも推奨設定のパスがありますが、この段階で設定するとcurrentが設定されずnodeコマンドを使えない現象が発生するかもしれません。その場合は該当パスを消して実施してみてください。）

escキーを押したら:wqで保存して抜けましょう。

設定を適用させるために下記コマンドを実行
```
source ~/.zshrc
```


# Node.jsのインストール
試しに最新版をインストールしてみます。
```
nodebrew install latest
```

無事にインストール完了です！
![スクリーンショット 2024-02-17 21.10.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/943982/f4eff9f7-dd32-f97e-9d57-7b9eb3fe14b5.png)

別バージョンもインストールします。
```
nodebrew install v18.17.0
```

インストールされているか確認します。
```
nodebrew list
```

無事インストールされました。ただcurent:noneになっているのでバージョン選択していなかったことに気づきます。:frowning2:
![スクリーンショット 2024-02-17 21.18.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/943982/4365edd3-b4db-6993-b8da-b21327d53a9d.png)

バージョンを選択します。
```
nodebrew use v18.17.0
```

無事使えることを確認できました。

![スクリーンショット 2024-02-17 23.02.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/943982/927fa8d2-bd43-5ddb-e58c-9cd4bd291fbe.png)


# 最後に
実はNode.js自体は既に入れていたので一旦消す作業をしたのですが、そちらは深呼吸していないとやっていけませんでした・・・。（慎重にやりましょう）

# 次のお話
https://qiita.com/mr_noppo/items/d3bca0ad2a5d4bd9fcc7
RazorPages備忘録#1

# RazorPagesの備忘録#1
PageのフォームのポストでModelにデータを渡してアクセスしたいプロパティがある場合は、

 **BindProPerty**を使用する。

以下の例はViewのHTMLに名前、年齢、性別を入力してバインドさせたい場合。
```Person.cs
public class Person
{
    [Key]
    public int Id { get; set; }
    
    [Required]
    [DisplayName("名前")]
    public string Name { get; set; }

    [Required]
    [DisplayName("年齢")]
    public int Age { get; set; }

    [DisplayName("性別")]
    public string Gender { get; set; }
}

```
```PersonModel.cshtml.cs
public class PersonModel : PageModel
{
    private readonly ApplicationDbContext _context;

    [BindProperty]
    public Person person { get; set; }
    //以下処理が続く
```

View側のcshtmlのフォームにてPOSTをした場合、入力した内容をPOSTメソッドで引数を受け取る処理がMVCでは必要の認識だが、
RazorPageでBindPropertyを用いることで引数の処理を書かなくてもよくなっている。


サーバーバリデーションを行うときはどうすればよいのか疑問はあるが、これから勉強していくうちに解決するかもしれない。
~~クライアント側のバリデーションで事足りる場合はそもそも必要ないのかもしれないけども。。。~~


こういうアーキテクチャを**MVVM**というのだっけ？？
ディレクトリ構成を表示する（Windows/bat)


## ソースコード
``` dirOutput.bat
@echo off
tree /f /a > dir.txt
```
## 表示結果

    フォルダー パスの一覧
    ボリューム シリアル番号は F48B-C0E3 です
    D:.
    |   dir.txt
    |   dirOutpu.bat
    |   
    +---03.MindMap
    +---Design
    |   \---ParameterSheets

## ユースケース
- システム設計時の共有と記録
- システム構築時の共有と記録
- システム運用時の共有と記録
- システム移行（更改）時の差分比較

## 対象
- Terraform
- WebServer
- AppServer
...

## 実行環境
- Windows11


## もっとほしい情報
- 権限
- 容量
- 差分が生じたときのアラート監視

## 問題点
- <b>すべて表示する</b>と見にくくなる
- 時間差、サーバー間の比較がしにくくなる

## 解決策
- それぞれの目的に応じた情報のみを表示をして比較する
Reactのロゴ（App-logo）が回らない！？


# 困ったこと

React を学ぼうと、create-react-appで最初に作成されるサンプルアプリを立ち上げたら、ロゴが回らない！

解説動画とかだと、くるくる回っているのに、なぜだ！？

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/239851/8c0e0cd0-be1d-86e0-962f-dd387937d9af.png)

# 環境
macOS: 13.6.4
chrome: 121.0.6167.184
node: v18.16.0
react: 18.2

# 解決方法
原因は、React側でも、Chrome側でもなく、OSの設定側にありました。

[システム設定] -> [アクセシビリティ] -> [ディスプレイ] を開き、 [視覚効果を減らす] が「**オン**」になっていれば、「**オフ**」 にするとくるくる回るようになりました！

![2024-02-1722.17.18-ezgif.com-video-to-gif-converter.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/239851/6d6dd0e3-7c0e-949d-f35a-e11a9c3b60ac.gif)


# 所感
下記のように、prefers-reduced-motion　がApp.cssに記述されています。
このprefers-reduced-motion は、ユーザの環境設定によって、余計な動きを抑制に関係するもののようです。

このようなOSの環境設定で反映さるものがあったとは、知らなかった。。。



```css
@media (prefers-reduced-motion: no-preference) {
  .App-logo {
    animation: App-logo-spin infinite 20s linear;
  }
}@media (prefers-reduced-motion: no-preference) {
  .App-logo {
    animation: App-logo-spin infinite 20s linear;
  }
}
```



# 参考
https://azukiazusa.dev/blog/prefers-reduced-motion-or-application-settings/
REXXプログラミング入門⑤

# REXXプログラミング入門⑤

データの取得と処理は、多くのプログラミングタスクの中心です。REXXを使ってメインフレーム上のデータセットから情報を読み取り、その内容を解析・表示する能力は、自動化スクリプトを作成する際に非常に有用です。この演習では、特定のデータセットからデータを読み取り、その内容を指定されたフォーマットでNETLOGに出力する方法を学びます。

## ⑤データの取得とNETLOGへの出力

目的は、指定されたデータセットからデータを読み取り、それを解析してNETLOGに特定のフォーマットで出力するREXXスクリプトを作成することです。

### スクリプトの内容

以下は、データセットからデータを読み取り、NETLOGに出力するREXXスクリプトの例です。

```rexx
/* REXX */
OPTIONS EXMODE
TRACE R
ADDRESS TSO "ADDRESS NETVASIS"
PIPE (QSAM 'MYLIB.MYDATASET(MYDATA)') | (STEM data.)
SAY 'MYDOMAIN *** ST MYPROG5 WRITE' data.0 'LINE *************'
DO i = 1 TO data.0
  SAY 'MYDOMAIN' data.i
END
EXIT
```
### スクリプトの解説
- `PIPE (QSAM 'MYLIB.MYDATASET(MYDATA)') | (STEM data.)`: この命令は、指定されたデータセットからデータを読み取り、それを`data.`ステム変数に格納します。`PIPE`を使用してデータを読み込み、`STEM`でデータを構造的に管理します。
- `SAY 'MYDOMAIN *** ST MYPROG5 WRITE' data.0 'LINE *************'`: 読み取ったデータの総行数を表示します。`data.0`には読み取った行数が格納されています。
- `DO i = 1 TO data.0`: このループは、読み取った各行に対して処理を行います。ループ内で、各行の内容を`SAY`命令を使って出力します。
### 実行方法
このスクリプトをメインフレーム上でREXXプログラムとして実行します。スクリプトが正しく機能している場合、指定されたデータセットから読み取ったデータがNETLOGに出力されます。

### まとめ
演習5では、REXXを使用してメインフレーム上のデータセットから情報を取得し、その内容を解析して出力する基本的な方法を学びました。この技術は、ログの解析、レポートの生成、システム監視など、多岐にわたるタスクに応用することができます。
JavaScriptでファイルをダウンロードする方法

認証が必要なAPIなどにアクセスして、レスポンスをファイルとしてダウンロードしたいときのやり方。

```js
async function download(url, filename) {
  // ダウンロードしたいURLにアクセス
  let res = await fetch(url, {
    method: 'GET',
    headers: { 'Authorization': "xxxxxxxxxxxxxxxxxxx" },
  }).catch((err) => err.data)
  
  if (res.status == 200) {
    // blobとして内容を読み出す
    const content = await res.blob()
    // オブジェクトURLを生成
    const objectUrl = (window.URL || window.webkitURL).createObjectURL(content)
    // オブジェクトURLをhref属性に設定したaタグを作成
    const a = document.createElement('a')
    a.href = objectUrl
    a.download = filename
    // aタグをクリック
    a.click()
  }
}

var url = "https://www.yahoo.co.jp/"
var filename = "index.html"
await download(url, filename)
```
REXXプログラミング入門④

# REXXプログラミング入門④

プログラミングにおいて、外部システムやアプリケーションから情報を取得し、そのデータを基に処理を行うことは一般的なタスクです。REXX言語を使用して、このようなタスクをメインフレーム環境で実行する方法を学ぶことは、システム管理や自動化のスキルを高めるのに役立ちます。この演習では、セッション接続情報を取得し、その情報を解析して特定のフォーマットで出力するスクリプトを作成します。

## 演習4: セッション情報の取得と出力

この演習の目的は、外部コマンドを使用してセッション接続の情報を取得し、それを解析して特定のフォーマットで出力するREXXスクリプトを作成することです。

### スクリプトの内容

以下は、セッション接続情報を取得し、解析して出力するREXXスクリプトの例です。

```rexx
/* REXX */
OPTIONS EXMODE
TRACE R
ADDRESS TSO "ADDRESS NETVASIS"
PIPE (CORRCMD 'DISPGW') | (SEPARATE) | (STEM session.)
DO i = 1 TO session.0
  PARSE VAR session.i domain 'FROM' ownDomain 'TO' otherDomain ':' status
  SAY 'MYDOMAIN ST MYPROG4 SESSION FROM' ownDomain 'TO' otherDomain ':' status
END
EXIT
```
### スクリプトの解説
- `PIPE (CORRCMD 'DISPGW') | (SEPARATE) | (STEM session.)`: この行は、`DISPGW`コマンドを実行し、その出力を解析して`session.`ステム変数に格納します。`PIPE`と`STEM`を使用することで、出力の各行を個別の要素として扱うことができます。
- `DO i = 1 TO session.0`: このループは、`session.`ステム変数に格納された各行に対して処理を行います。
- `PARSE VAR session.i domain 'FROM' ownDomain 'TO' otherDomain ':' status`: この命令は、セッション情報の各行を解析し、ドメイン名、自ドメイン、相手ドメイン、ステータスに分割します。
- `SAY 'MYDOMAIN ST MYPROG4 SESSION FROM' ownDomain 'TO' otherDomain ':' status`: 最後に、解析した情報を指定されたフォーマットで出力します。
### 実行方法
このスクリプトをメインフレーム上でREXXプログラムとして実行します。正しく機能している場合、セッション接続の情報が指定されたフォーマットで出力されます。

### まとめ
この演習を通じて、外部コマンドの実行結果を取得し、その結果を解析して利用する方法を学びました。このスキルは、実際の業務でシステムやアプリケーションの状態を監視し、自動化するためのスクリプトを作成する際に非常に有用です。
DNSキャッシュのクリア方法（ubuntu22.04版）

独自ドメインでのWEBサーバーへの接続テストをする場合など、DNS情報の浸透状況のほかに、クライアントのDNSキャッシュが邪魔をすることがある。
ここでは、このDNSキャッシュのクリア（フラッシュ）方法について記述する。

なお、Windows版、ubuntu20.04版については、【[DNSキャッシュのクリア方法](https://qiita.com/wmx/items/276c41b25467510fd0fc)】を参照。

## Linux (ubuntu22.04) 向け

#### DNSキャッシュの統計表示
```
root@localhost:~# resolvectl statistics
```

##### 表示例

```
DNSSEC supported by current servers: no

Transactions            
Current Transactions: 0
  Total Transactions: 21
                        
Cache                   
  Current Cache Size: 6
          Cache Hits: 0
        Cache Misses: 63
                        
DNSSEC Verdicts         
              Secure: 0
            Insecure: 0
               Bogus: 0
       Indeterminate: 0  
```

#### DNSキャッシュのクリア
```
root@localhost:~# resolvectl flush-caches
```
REXXプログラミング入門③

# REXXプログラミング入門③

REXXプログラミングにおける条件付き終了と外部コマンドの実行結果の取得は、多くの実践的なシナリオで非常に役立ちます。この演習では、外部コマンドの実行結果に基づいてプログラムの流れを制御し、特定の条件が満たされた場合にプログラムを終了させる方法を学びます。

## 演習3: 条件付き終了

この演習の目標は、特定の外部コマンドを実行し、その結果として特定のメッセージが検出された場合にプログラムを終了するREXXスクリプトを作成することです。

### スクリプトの内容

以下は、指定された条件下でプログラムを終了させるREXXスクリプトの例です。

```rexx
/* REXX */
OPTIONS EXMODE
TRACE R
ADDRESS TSO "ADDRESS NETVASIS"
CMD = 'S JR1,D=''MYLIB.MYDATASET(MYMEMBER)'''
SAY CMD
CALL OUTTRAP 'cmdout.','*'
CALL ADDRESS TSO CMD
CALL OUTTRAP 'OFF'
found = 0
DO i = 1 TO cmdout.0
  IF POS('IEF404I MYMEMBER', cmdout.i) > 0 | POS('IEF450I MYMEMBER', cmdout.i) > 0 THEN DO
    found = 1
    LEAVE
  END
END
IF found = 0 THEN
  SAY '*** MYPROG3 TIMEOUT ***'
EXIT
```
### スクリプトの解説
- `CALL OUTTRAP 'cmdout.','*'`: この命令は、外部コマンドの実行結果を`cmdout.`ステム変数に捕捉します。これにより、後で結果を解析することができます。
- `CALL ADDRESS TSO CMD`: 指定された外部コマンドを実行します。
- `DO i = 1 TO cmdout.0`: このループは、外部コマンドからの出力を解析し、特定のメッセージが含まれているかどうかをチェックします。
- `IF POS('IEF404I MYMEMBER', cmdout.i) > 0 | POS('IEF450I MYMEMBER', cmdout.i) > 0 THEN DO`: 特定のメッセージが検出された場合、`found`変数を設定し、ループから脱出します。
- `IF found = 0 THEN SAY '*** MYPROG3 TIMEOUT ***'`: 特定のメッセージが検出されなかった場合、タイムアウトメッセージを表示します。
### 実行方法
このスクリプトをメインフレーム上でREXXプログラムとして実行します。スクリプトは外部コマンドを実行し、その結果を監視して、特定のメッセージが出力されたかどうかを確認します。

### まとめ
この演習では、REXXプログラミングにおける重要な概念である外部コマンドの実行と結果の取得、条件付き処理の実装方法を学びました。これらのスキルは、実際のシステム管理や自動化タスクで非常に役立ちます。
Fivetran APIをコールしユーザー情報を確認する

Fivetran APIの使い方をまとめました。

コールするエンドポイント

```bash
https://api.fivetran.com/v1/users
```

https://fivetran.com/docs/rest-api/users#request

FivetranでAPIキーを発行する
![スクリーンショット 2024-02-17 22.12.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449065/651bc595-64c7-6121-6666-887f5a14e142.png)

スクリプトを用意する
```bash
# APIとAPI秘密鍵を設定する
API_KEY="xxxxxxxxx"
API_SECRET_KEY="xxxxxxxxxxxxxxxxxxx"

curl -X GET "https://api.fivetran.com/v1/users" -u $API_KEY:$API_SECRET_KEY | jq
```

実行結果

```json
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   440  100   440    0     0    638      0 --:--:-- --:--:-- --:--:--   638
{
  "code": "Success",
  "data": {
    "items": [
      {
        "id": "waste_westerly",
        "email": "xxxxxxxxx@gmail.com",
        "given_name": "Ryuto",
        "family_name": "S",
        "verified": true,
        "invited": false,
        "picture": "https://lh3.googleusercontent.com/a/ACg8ocKYZKgLsW2cgfkl4XB3ZwFVIKQcwCEJ_kYDkZDKIt48=s96-c",
        "phone": "xxxxxxxx",
        "role": "Account Administrator",
        "logged_in_at": "2024-02-17T05:48:19.407Z",
        "created_at": "2024-02-17T03:56:53.950621Z",
        "active": true
      }
    ]
  }
}
```

ユーザー情報が確認できましたね。
GETリクエストはパラメーターも少なく簡単でした。

## まとめ
今回はFivetran APIの使い方をまとめました。
GETリクエストだけでしたが雰囲気は掴めたかなと思います。

Fivetran APIではユーザーを確認する以外にも、コネクタ（Connector）を作成したり、ディスティネーション（Destination）の作成もできます。

詳しくはFivetranとは？[Fivetran APIの使い方と各種設定方法を紹介](https://tellingbook.com/fivetran-api-how-to-use/)で紹介しているのでぜひご覧ください！
REXXプログラミング入門②

# REXXプログラミング入門②

REXXを使用した基本的な繰り返し処理の実装方法を学ぶことは、プログラミングスキルの向上において重要なステップです。この演習では、REXXスクリプトを使用して、特定のメッセージを一定間隔で複数回出力する方法を紹介します。

## 演習2: 繰り返し処理

目標は、1秒間隔で特定のメッセージを5回出力するREXXスクリプトを作成することです。これにより、繰り返し処理と時間制御の基本を学びます。

### スクリプトの内容

以下は、求められたタスクを実行するREXXスクリプトの例です。

```rexx
/* REXX */
OPTIONS EXMODE
TRACE R
DO i = 1 TO 5
  SAY '*** ST MYPROG2 LOOP TEST' i '***'
  CALL SLEEP 1
END
EXIT
```
### スクリプトの解説
- `OPTIONS EXMODE` と `TRACE R`: スクリプトの実行モードとトレースレベルを設定します。これにより、スクリプトの実行過程を追跡できます。
- `DO i = 1 TO 5`: この行は、i変数を1から5まで繰り返すループを作成します。つまり、ループ内のコマンドが5回実行されます。
- `SAY '*** ST MYPROG2 LOOP TEST' i '***'`: 現在のループカウンタ（i）とともにメッセージを出力します。これにより、どの繰り返しが実行されているかを視覚的に確認できます。
- `CALL SLEEP 1`: このコマンドは、次の繰り返し処理の前に1秒間プログラムの実行を停止します。これにより、出力間の時間間隔を制御できます。
### 実行方法
このスクリプトをメインフレーム上でREXXプログラムとして実行します。スクリプトが正しく機能している場合、指定されたメッセージが1秒ごとに出力され、各メッセージの間には明確な遅延があります。

### まとめ
この演習を通じて、REXXでの繰り返し処理と基本的な時間制御の実装方法を学びました。このような基本的な概念の理解は、より高度なスクリプトの作成に向けた土台となります。
REXXプログラミング入門①

# REXXプログラミング入門①

REXXは、特にIBMのメインフレームで広く使用されているプログラミング言語です。その簡潔さと柔軟性により、多くの自動化タスクに利用されています。この入門記事では、REXXプログラミングの基本を学び、最初の簡単なスクリプトを作成する方法を説明します。

## 演習1: パラメータの出力

この演習では、プログラムに渡されたパラメータを出力するREXXスクリプトを作成します。もしパラメータが渡されなかった場合は、特定のメッセージを表示します。

### スクリプトの内容

以下は、パラメータをチェックし、それに応じてメッセージを出力するREXXスクリプトの例です。

```rexx
/* REXX */
OPTIONS EXMODE
TRACE R
ADDRESS TSO "ADDRESS NETVASIS"
ARG param
IF param = "" THEN
  SAY '*** ST MYPROG1 PARAMETER NOT FOUND ***'
ELSE
  SAY '*** ST MYPROG1 ' param '***'
EXIT
```
### スクリプトの解説
- OPTIONS EXMODE と TRACE R: スクリプトの実行モードとトレースレベルを設定します。トレースレベルRは、スクリプトの実行中に実行される命令を表示します。
- ADDRESS TSO "ADDRESS NETVASIS": スクリプトがNETVASIS環境で実行されることを指定します。これはメインフレームの特定の環境に依存するコマンドです。
- ARG param: スクリプトに渡された最初のパラメータを変数paramに格納します。
- IFステートメント: 渡されたパラメータが空文字列（""）かどうかをチェックし、条件に応じて異なるアクションを実行します。パラメータがない場合は、エラーメッセージを表示し、そうでない場合はパラメータを出力します。

### 実行方法
このスクリプトをREXXプログラムとして保存し、メインフレーム上で実行します。コマンドラインからパラメータを渡して実行することで、スクリプトがパラメータを適切に処理し、出力する様子を確認できます。

### まとめ
この演習は、REXXプログラミングの基礎的なコンセプトを理解し、簡単なスクリプトを作成する良い出発点です。パラメータの処理は、多くのスクリプトで共通するタスクであり、この基本をマスターすることは、より複雑なプログラムを作成する上で役立ちます。
Python : Yahoo迷惑メールを取り扱う

Yahooメールの迷惑メールがいつも大量になっている私。

定期的にpythonで消したい。

って、言っても迷惑メールに入ってるから削除作業しても、そのままそこにいらっしゃるコードになってしまった。

完全削除はできないため。<br>なので、どんなメールが迷惑メールに来てるか、リストを見るくらいのコードになってしまった。<br>収穫は、迷惑メールのフォルダ名は、==Bulk Mail== というのが分かったこと

***

# 最初に "IMAP有効" にする

Yahoo!メールでIMAPを有効にするには、アカウントの設定から「IMAP/POP/SMTPアクセス」を選択し、「IMAP」の「有効にする」をクリックして確認します。さらに、「Yahoo! JAPAN公式サービス以外からのアクセスも有効にする」を選択することで、外部アプリやメールクライアントからのアクセスを許可します。これにより、IMAPを介したメールの送受信が可能になります。

参考<br><https://support.yahoo-net.jp/PccMail/s/article/H000007321>

***

# 迷惑メールのフォルダ名は、Bulk Mail

参考<br><https://answers.microsoft.com/en-us/outlook_com/forum/all/yahoo-imap-spam-folder-not-recognized-by-outlook/f002d53b-4124-4237-b9cd-4f02dca7d542>

***

# それを踏まえて、Pythonコード

ログ出力と結果表示の機能を含むPythonスクリプト

```python
import imaplib
import email

# 接続設定
imap_url = 'imap.mail.yahoo.co.jp'
username = 'your_username@yahoo.co.jp'  # 実際のユーザー名に置き換えてください
password = 'your_password'  # 実際のパスワードに置き換えてください

# IMAPサーバーに接続
mail = imaplib.IMAP4_SSL(imap_url)
mail.login(username, password)

# 迷惑メールフォルダを選択
mail.select('"Bulk Mail"')  # フォルダ名が異なる場合は適宜修正してください

# フォルダ内のすべてのメールを検索
status, messages = mail.search(None, 'ALL')
if status == 'OK':
    messages_to_delete = messages[0].split()
    print(f"Found {len(messages_to_delete)} messages to delete.")
    # メッセージリストを反転して最新のメールから処理
    for num in messages_to_delete[::-1]:
        _, msg_data = mail.fetch(num, '(RFC822)')
        msg = email.message_from_bytes(msg_data[0][1])
        print(f"Deleting: {email.header.make_header(email.header.decode_header(msg['Subject']))}")
        # メールを削除
        mail.store(num, '+FLAGS', '\\Deleted')
    mail.expunge()
    print(f"Deleted {len(messages_to_delete)} messages.")

# ログアウト
mail.logout()
```

このスクリプトは、迷惑メールフォルダ内のメールを検索し、見つかったメールの数と各メールの件名を表示した後、それらを削除します。メールを実際に削除する前に件名を表示することで、どのメールが削除されるかを確認することができます。

***

# 結果

実際、消せてないけど。既読にはなる。

    Found 4 messages to delete.
    Deleting: So you installed DeployScript. Now what?
    Deleting: 【重要なお知らせ】お試し体験を終了された方へ
    Deleting: お試し体験終了をした方へ　おすすめ番組のご紹介！
    Deleting: OpenWeatherMap Account confirmation
    Deleted 4 messages.

まあ、迷惑メールの中身が分かったということで。嬉しくもないけど。

勝手に消えるし、まあいいか。
【備忘】Auth0とは何か

## Auth0とは
Auth0はサーバーレスアーキテクチャを用いた認証クラウドサービス（Identity as a Service, IDaaS）で、認証基盤そのものをサービスとして提供するソリューション。
自社で実装すると手間の掛かる、ユーザのログイン情報やトークンの管理をなどを簡単におこなうためのサービスである。

※認証プラットフォームとしてはauth0の他にOktaが有名であり、Auth0社はOkta社によって買収されているがauth0とOktaは現在でも異なる認証プラットフォームである。

### Auth0とOktaの違い
#### 結論
中小企業向けか大企業向けかの違い
#### 詳細
**Auth0**：開発者中心のアプリケーションや小規模な企業向けに簡単な認証ソリューションを提供している。
**Okta**：大規模なエンタープライズ環境や複雑なアクセス管理ニーズを持つ組織向けに設計されている。

## 特徴
### Auth0の特徴
**1.開発者中心のアプローチ**
Auth0は、開発者向けに設計されており、シンプルで使いやすいAPIやドキュメントが提供されている。
これにより、開発者は迅速に認証プロセスを実装し、カスタマイズ可能。

**2.柔軟性とカスタマイズ性**
Auth0は、さまざまな認証機能をカスタマイズして組み合わせることができる。
シングルサインオン（SSO）、マルチファクタ認証（MFA）、ソーシャルメディア認証など、多彩な認証方法を選択可能。

**3.開発者フレンドリーなインターフェース**
Auth0のダッシュボードは使いやすく、直感的な操作が可能。
設定やカスタマイズを行うための豊富なオプションが提供されている。

**4.スケーラビリティ**
Auth0は、大規模なアプリケーションやサービスにも対応しており、成長に合わせて柔軟に拡張することができる。

### （余談）Oktaの特徴
**1.エンタープライズレベルのセキュリティ**
Oktaは、エンタープライズ環境でのセキュリティを重視しており、高度な認証方法やアクセス管理機能を提供している。
（大規模な組織や企業向け）

**2.広範なアプリケーションとの統合**
Oktaは、多くのエンタープライズアプリケーションとのシームレスな統合を可能にする。
これにより、従業員やパートナーがさまざまなアプリケーションにアクセスする際のユーザーエクスペリエンスが向上する。

**3.コンプライアンス要件の遵守**
Oktaは、厳格なコンプライアンス要件を満たすための機能を提供しており、特に規制やセキュリティポリシーに準拠する必要がある組織に適している。

**4.ユーザーエクスペリエンスの最適化**
Oktaは、ユーザーの認証プロセスをシームレスにすることに焦点を当てており、使いやすさとセキュリティを両立させている。

## Auth0の主な機能
### 1.シングルサインオン(SSO)
シングルサインオンは、一度のユーザ認証処理で、Webサービス、アプリ、クラウドサービスなど独立した複数のソフトウェアシステム上にてログインできるようにする機能。
この機能によって、「Google Workspace」や「Microsoft 365」などの利用頻度の高いサービスに簡単にログイン可能。
複数サービスのIDやパスワードを管理する手間が省けるため、余計なストレス削減や業務効率アップに繋がる。

### 2.ユニバーサルログイン
ユニバーサルログインは、中央認証サーバー経由でユーザーを認証する方法。
ユーザーエクスペリエンスの簡略化、アプリ統合の効率化、管理の改善、認証セキュリティの向上といった利点がある。
複数の多要素認証を組み合わせて利用することでより強固なセキュリティ対策にもなる。

### 3.アダプティブ多要素認証（Adaptive MFA）
「Google Authenticator」や「Duo」を活用することで多要素認証も実現可能。
また、IDやパスワードの入力だけでログインするのではなく、SMSを利用したワンタイムパスワード、指紋認証や顔認証などユーザーの生体認証を活用することで、より本人認証の精度を高められる。

## Auth0が使用例
Auth0が使用されるケースの一例として、以下があげられる

**ウェブアプリケーションの認証**
Auth0は、ウェブアプリケーションでのユーザー認証を実施する。
開発者はAuth0を導入することで、ユーザー登録、ログイン、セッション管理などの認証関連のタスクを簡素化し、セキュリティを向上させることが可能。

**シングルサインオン（SSO）**
多くの企業や組織がAuth0を使用して、シングルサインオン（SSO）を実現している。
これにより、ユーザーは1回のログインで複数の関連アプリケーションやサービスにアクセス可能。

**API保護**
Auth0は、APIを保護するための機能も提供している。
開発者はAuth0を使用してAPI認証やアクセス制御を実装し、APIエンドポイントに対するセキュリティを強化することが可能。

**モバイルアプリケーションの認証**
モバイルアプリケーション開発者もAuth0を活用して、アプリ内のユーザー認証を実装している。
これにより、モバイルユーザーは安全かつ簡単にアプリにログインし、サービスを利用することが可能。

**IoTデバイスの認証**
IoT（Internet of Things）デバイスの開発者はAuth0を使用して、デバイスの認証を実装している。
これにより、IoTデバイスがアプリケーションやクラウドサービスに安全に接続が可能。

**企業内システムのセキュリティ強化**
大規模な企業や組織は、Auth0を使用して内部システムやリソースへのアクセスをセキュアに管理する。
これにより、社内の情報セキュリティが向上し、データ漏洩や不正アクセスのリスクを軽減可能。

**開発者エコシステムの支援**
Auth0は、開発者向けのポータルやドキュメントも提供しており、開発者が認証関連のタスクを効率的に実装できるようサポートしている。

## まとめ
Auth0は、**簡単に導入できる認証・認可プラットフォーム**である。
https://auth0.com/jp
テスト駆動開発（TDD）とは何か？

# はじめに
最初にイメージを掴んでいただくために要約をします。
#### テスト駆動開発（TDD）とは…
- ソフトウェア開発プロセスの手法の1つ
- テストファーストな開発手法

#### 従来のプロセスとの違いは…
- 細かなサイクルを繰り返す点(TDDサイクル)
- テストファーストな開発手法

#### メリット、デメリットは…
- メリット  = 早い段階でのエラー検知、開発の効率化、負担軽減など
- デメリット = テストコード作成の時間がかかる、コストがかかるなど

# テスト駆動開発（TDD）の基本サイクル
開発者がソフトウェアのコードを書く前に、そのコードが期待通りに機能することを検証するテストケースを書くことから始めます。
TDDサイクルは、「<font color="Red">レッド</font>、<font color="green">グリーン</font>、<font color="blue">リファクタリング</font>」と呼ばれる3つの主要なステップで構成されます。

### 1\. <font color="Red">レッド</font>(失敗)
最初に、実装したい機能のテストコードから先に作成します。
このステップではテスト対象のコードがないので当然失敗します。
### 2\. <font color="green">グリーン</font>(成功)
次に、テストをパスするために、テスト対象のコードを最小限作成し、機能を実装します。
このステップでは、テストが成功するようにします。
### 3\. <font color="blue">リファクタリング</font>(改善)
最後に、コードをリファクタリングして、より効率的で保守しやすい形に改善します。
このステップでは、テストを通過しつつコードの品質を向上させることを目指します。

# なぜテストケースを先に作成するのか?
### 1\. 品質の向上
TDDは、品質の高いソフトウェアを開発するための手法です。
テストケースを最初に作成することで、開発者はソフトウェアの期待される動作を明確に理解し、それに基づいてコードを設計します。
結果として、テストケースによって期待される動作が確認され、品質の高いソフトウェアが生み出されます。

### 2\.バグの早期発見
TDDでは、テストケースを先に作成し、それに基づいてコードを実装します。
このプロセスにより、開発者はコードを追加するたびにテストを実行し、不具合やエラーを早期に発見することができます。
バグが発生した場合、開発者はすぐに原因を特定して修正することができます。

### 3\. リファクタリングの促進
TDDは、コードを小さなステップで開発するため、開発者がコードをリファクタリングしやすくします。
テストケースがある場合、リファクタリングを行っても、テストが失敗しない限り、コードの動作は保持されます。
このため、開発者はコードを改善し、より効率的で保守性の高い形に変更することができます。

### 4\.要件の明確化
TDDでは、テストケースを最初に作成するため、開発者はソフトウェアの動作について詳細に考える必要があります。
これにより、ソフトウェアの要件が明確化され、開発プロセス全体が効率的に進行します。
また、テストケースは仕様書としての役割も果たし、ソフトウェアの動作を他の開発者や関係者と共有するための重要なドキュメントとなります。

<br><br>
テスト駆動開発とは、Kent Beck（ケント・ベック）さんが提唱した開発手法です。
Kent Beckさんの著書で、「原典」とも呼ばれる「テスト駆動開発入門」には、TDD（テスト駆動開発）の目標について、以下のように記載されています。
> 「動作するきれいなコード」
ロン・ジェフリーズのこの簡潔な言葉は、TDD（テスト駆動開発）の目標である。
動作するきれいなコードは、あらゆる理由で価値がある。

つまり **動作するコードを書きながらきれいに直していく**
ことを目標としています。
# テスト駆動開発のデメリット
上記にあるようにTDDには様々なメリットがあります。
しかしいくつかのデメリットもあります。

### 1\.時間と労力の増加
TDDを実践するためには、テストコードの作成に追加の時間と労力が必要です。
最初の段階でテストを作成し、その後に実装するため、開発の初期段階での作業量が増加します。

### 2\.慣れるまでの時間と労力
また今まで採用している開発手法から大きく変わる場合、やり方の変化に慣れるのにも時間と労力が必要です。
TDDの学習をするための時間と労力が、TDDを導入したことによるメリットをも上回る場合があります。

<br><br>
TDDを導入する際にはプロジェクトやチームのニーズに合わせて、メリットとデメリットのバランスを考えて、採用するかどうかを検討する必要があります。

# 実際にTDDでログイン機能を作成した感想
ここまで学習し、学んだことをまとめてきました。

ここからは実際にLaravelで、自身でもテストケースを先に行い、機能を実装したことを書こうと思っていたのですが、

***Laravelの開発自体に慣れていなかったのでメリットもデメリットも感じづらかった。***

というのが率直な感想です。

ただテストケースを作成しながら進めているので、どんどんテスト内容が充実していくのは安心感がありました。
またテストを繰り返し、エラーを修正することで機能の実装に近づくことを実感できるので、***どう修正すればよいかわからなくなっても、次に何をするべきか見当がつかなくなる。
といったことはなかった***のは、メリットと呼べるのではないかと思いました。
[ESLint] console.logでもうレビューを受けたくない

# 目的
プルリクでデバッグや検証用に使ったconsole.logの消し忘れにより
何度もレビュー受けていた。
そのためESLintでconsole.logがあればエラー出るように修正した。

# やり方
1. *特定のファイル*以外でconsole.logを許可しないようにESLintを設定する
1. *特定のファイル*でのみログが出せるようにする


# 1. *特定のファイル*以外でconsole.logを許可しないようにESLintを設定する

*.eslintrc.json*
```
{
  "extends": "next/core-web-vitals",
  "rules": {},
  "overrides": [
    {
      "files": ["*.js", "!log.js"],
      "rules": {
        "no-console": ["error"]
      }
    }
  ]
}

```

##### "files": ["*.js", "!log.js"]
!log.jsファイル以外のjsファイルが対象

#####   "no-console": ["error"]
no-consoleというconsoleに関するESLintの規則を利用している。
console系は使えないように。

```
"no-console": ["error", { "allow": ["warn", "error"] }]
```
単にconsole.logを使いたくないならこのようにするとconsole.warn, console.errorは利用できるが、検証用にログを残す場合はモジュールでまとめて、それを利用する方が管理しやすいのでこの案は不採用

2.  *特定のファイル*でのみログが出せるようにする

*utils/log.js*　　
※フォルダ名やファイル名は任意

```
/* eslint-disable no-console */

export default class log {
  static info = (obj) => {
    console.info(obj);
  };
  static warn = (obj) => {
    console.warn(obj);
  };
  static error = (obj) => {
    console.error(obj);
  };
}

```

ログを残したい場合はこのモジュールをimportして利用する。


### VS CodeでESLintのエラーが出ない場合
各自のVS CodeにESLintの設定が必要なので、
VS Codeのsetting.jsonに以下を追記

```
 "eslint.enable": true,
  "eslint.validate": [
    "javascript",
    "javascriptreact",
    "typescript",
    "typescriptreact"
    // 必要に応じて他の言語も追加
  ],
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": "explicit"
  }
```
Python入門　関数について学んだ備忘録を残します。

関数の処理がいまいち理解できずで、解決するため下記の動画を見ながら学びました。

Python入門 第10回｜引数と戻り値を根本から理解して関数を使いこなそう！【高校情報1】
https://youtu.be/wlOkT_6Qce4?si=PIt_5FJbIoUGPQVL

目次に沿って、メモを残します。

## 00:00 今回のテーマ
## 00:47 ２種類の関数
提供されている関数
→組み込み関数

・組み込み関数一覧
https://docs.python.org/ja/3/library/functions.html

自分で作る関数
→ユーザー定義関数

## 01:54 関数の作り方
def 関数名（引数、・・・）:
    【処理】

defとは、definititon:定義

関数の動きを理解するには、２つの値の動きを理解する必要がある

関数とはあるところから呼び出して使うことになりますが、
関数の呼び出し元から受け取ることができます。受け取る値のことを引数と呼びます

引数：呼び出し元から受け取る値
戻り値：関数の処理のあとに呼び出し元に返す値. return文 で指定
（returnを指定しない場合は None が返る）

## 03:58 引数と戻り値の動き
プログラムを実行したときに、数学の点数と英語の点数を入力すると、画面に表示したり、２つの平均点を表示したりそのようなプログラムがあったとします。

関数を使うときには「呼び出す側」と「呼び出される側」がある

### 引数と戻り値があるないパターンの一例
#### 引数×　戻り値×
```python
def usage ():
    print("０＝１００の数値を")
    print("入力して下さい")

#　呼び出し方
usage()#　これが呼んで、上記の関数に行って、処理を終えて、またここに処理内容が戻ってくるイメージ（戻り値ないけど

```
動きのイメージは、呼び出し元usage()→呼び出し先 関数def usage ():→usage()

#### 引数◯　戻り値×
```python
def display (e, m):
    print("英語は", e, "点")
    print("数学は", m, "点")

#　呼び出し方
display(90,80)

```
動きのイメージは、呼び出し元display(90,80)→呼び出し先 関数def display (90,80):→display(90,80)これでprint文の内容が表示される

#### 引数◯　戻り値◯
```python
def get_avg (e, m):
    a = ( e + m ) / 2
    return a

#　呼び出し方
avg = get_avg(90,80)
```
動きのイメージは、呼び出し元avg = get_avg(90,80)→呼び出し先 関数def get_avg (90,80):
→呼び出し元に計算結果を返す 85.0
イメージは、get_avg(90,80)　これが、85.0　になるイメージ。avg = 85.0

引数と戻り値を理解することが関数の動きを読み取るコツ

## 10:02 サンプルプログラム
【英語と数学の平均点を計算】０～１００の数値を入力して下さい
英語の点数を入力 ->【ここに入力すると掲載される仕組み】

英語をの点数を入れると、自動で、「数学の点数を入力 ->」の入力項目が表示される入力すると、その下に
英語は90点
数学は80点
平均は85.0です
と表示される簡単なプログラム

```python
def usage ():
    print("【英語と数学の平均点を計算】")
    print("０～１００の数値を入力して下さい")

def display (e, m):
    print("英語は", e, "点")
    print("数学は", m, "点")

def get_avg (e, m):
    a = ( e + m ) / 2
    return a

usage()

eng = int(input("英語の点数を入力 ->"))
math = int(input("数学の点数を入力 ->"))

display(eng, math)

avg = get_avg(eng, math)
print("平均は", avg, "点")
```

最初の3つの関数は、メインの方で呼び出されると処理が走る箇所

メインの箇所はusage()から下。
usage()が呼ばれるとdef usage ():が走る。
引数がないので、呼び出し先も呼び出し元もない状態

次に
```python

eng = int(input("英語の点数を入力 ->"))
math = int(input("数学の点数を入力 ->"))

#　input(""英語の点数を入力 ->"")の部分から処理され、コンソール上から数値を入力されるとここに入る。例えば９０と入力すると

eng = int(input("90"))

#　そして、90はint関数に渡されて、このようになります
#　結果、整数の９０に置き換わる
eng = 90

```

次にDisplay関数に渡される
```python
display(eng, math)

#　　↓

display(90, 80)

#　上記が下記に渡されて
def display (e, m):
    print("英語は", e, "点")
    print("数学は", m, "点")
#　数字は下記のところ入る
def display (90, 80):
    print("英語は", 90, "点")
    print("数学は", 80, "点")

```

次に平均を計算するところ
上記の様にengとmathには９０, ８０が入る
```python

avg = get_avg(eng, math)
print("平均は", avg, "点")

#　上記が動くと下記の関数が走り出す
def get_avg (e, m):
    a = ( e + m ) / 2
    return a

#　上記が動くと下記に戻って置き換わる
avg = 85.0

```
## 13:56 まとめ
この様に、プログラムは行ったり来たりして動いています。
処理が追えるようになればもっとプログラムが楽しくなる！
UPM形式のUnityパッケージをインストールしたときにScripting Define Symbolsを自動設定したい

下記のコードをUnity Package内のEditorディレクトリに内に含めることで、パッケージインストール時やSwitch Platform時に現在のプラットフォームのProject SettingsのScripting Define Symbolsにシンボルを追加することができる

```cs
using System.Linq;
using UnityEditor;

[InitializeOnLoad]
class AddDefineSymbols
{
    // Scripting Define Symbol list to add
    static readonly string[] symbolsToAdd = {
            "UNITYGLTF_FORCE_DEFAULT_IMPORTER_ON",         // Enable UnityGLTF import
            "GLTFAST_FORCE_DEFAULT_IMPORTER_OFF",          // Disable GLTFast import
            "UNIGLTF_DISABLE_DEFAULT_GLTF_IMPORTER",    // Disable importer in UniVRM
            "UNIGLTF_DISABLE_DEFAULT_GLB_IMPORTER",     // Disable importer in UniVRM
        };

    // This method will be called on load of Unity Editor
    static AddDefineSymbols()
    {
        AddDefineSymbolsToPlayerSettings(symbolsToAdd);
    }

    private static void AddDefineSymbolsToPlayerSettings(string[] symbols)
    {
        // Get the current list of defined symbols for the active build target
        string definesString = PlayerSettings.GetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup);
        var allDefines = definesString.Split(';').ToList();
        bool symbolsAdded = false;

        foreach (var symbol in symbols)
        {
            if (!allDefines.Contains(symbol))
            {
                allDefines.Add(symbol);
                symbolsAdded = true;
            }
        }

        // Only update the define symbols if new symbols were actually added
        if (symbolsAdded)
        {
            PlayerSettings.SetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup, string.Join(";", allDefines.ToArray()));
        }
    }
}
```

https://chat.openai.com/share/370bccba-c949-4e1c-9cfd-e5450d8b2baa