OmniAICreator/Qiita-1.07M huggingface dataset:
Reactを使ってポートフォリオサイト作成

# はじめに
Reactの備忘録

Reactを使ってポートフォリオサイトを作成。

## Reactを選定した理由
多くの方に使われてる。
コンポーネントという仕組みに興味があったため。

## ポートフォリオサイトを作成する理由
ポートフォリオサイトを作って自分自身のやったことを整理して、これからのやったことも追加できるようにしよう作ろうと思ってます。

## 下調べしての気づき
* 時間をかけすぎない
時間をあまりかけず、まずは骨組みを作るイメージからスタートします。
サイトを作ることに時間をかけてしまって、大事な部分が疎かにならないように意識してます。

* 読み手が読みやすいを意識する。
白を基調とした構成で読みやすくする。
色であまりストレスを与えないようにする。

* 言葉をなるべくシンプルにする。
無駄をなくすことで相手に伝わりやすくなる。
伝えたいことがわかりやすくなる。

## 内容

```jsx:Header.js
import React from "react";

function Header(){
    return (
        <header>
            <h1>ポートフォリオサイト</h1>
            <nav>
                <ul>
                    <li><a href="#profile">Profile</a></li>
                    <li><a href="#skill">Skill</a></li>
                    <li><a href="#portfolio">Portfolio</a></li>
                </ul>
            </nav>
            <div className="social-icons">
                <a href="#"><img src="x-logo.png" alt="X"></a>
                <a href="#"><img src="github-logo.png" alt="GitHub"></a>
                <a href="#"><img src="qiita-logo.png" alt="Qiita"></a>
            </div>
        </header>
    );
}

export default Header;
```

```jsx:MainContent.js
import React from "react";

function MainContent(){
    return(
        <main>
            <h2 id="profile">Profile</h2>
            {/* {ここにプロフィールの内容を追加} */}

            <h2 id="skill">Skill</h2>
            {/* {ここにプロフィールの内容を追加} */}

            <h2 id="portfolio">Portfolio</h2>
            {/* {ここにプロフィールの内容を追加} */}
        </main>
    );
}

export default MainContent;
```

```jsx:App.js
import React from 'react';
import Header from './Header';
import MainContent from './MainContent';

function App() {
  return (
    <div className="App">
      <Header />
      <MainContent />
      <Footer />
    </div>
  );
}

export default App;
```

## 押さえておくべき点
* Reactはfacebookが開発したJavaScriptのライブラリ
* コンポーネントというブロックをファイルごとに作成して組み合わせる。
* コンポーネントで作成したものは今後、再利用することができる。
* App.jsでまとめて実行するファイルを作成。
* 実行はターミナルにて「npm start」を入力して実行することでReactアプリ起動。

## 参考にしたサイト
[コンポーネント](https://qiita.com/akari_0618/items/9269b6432f36fe84d8d8)
AtCoder ABC 086 C - Traveling の解答(by Python)

# 前置き
今更ですが、[AtCoder に登録したら次にやること ～ これだけ解けば十分闘える！過去問精選 10 問 ～](https://qiita.com/drken/items/fd4e5e3630d0f5859067) という記事に触発され、過去問10問を自分なりにコードを書いて解いてみました。
だいたいはスムーズに解けたのですが、その10問目である086Cの問題でafter_contest01.txtのテストがどうしても通らなくてハマっていたのが、原因を突き止めたので記事にします。
Pythonで記述していますが、ハマりポイントは言語とは関係ありませんので別言語で書いてハマってしまっているよという方にも参考になると思います。

# 問題の内容
[こちら](https://atcoder.jp/contests/abc086/tasks/arc089_a)にもある通り、旅行プランが可能かどうかを判定する問題です。
1秒ごとに上下左右に移動し、なおかつその場にとどまれないという条件から、時刻tと座標合計の偶奇が一致するかというところに着目すれば大枠は解けています。もちろん、以下のような大きすぎる移動も不可であることも含めないといけません。解説に関しては問題番号で調べればたくさんでてくるのでここらで割愛します。
```
# 入力例2
1
2 100 100
```

# 自分が作った最初の解答
```py
  N = int(input())
  plan = [input().split() for i in range(N)]

  def isRealizable(t, x, y):
    if x + y > t:
      return False
    elif (x + y) % 2 == t % 2:
      return True
    else:
      return False

  # t = 1 のとき
  if not isRealizable(int(plan[0][0]), int(plan[0][1]), int(plan[0][2])):
    print("No")
    exit()

  # t = 1 だけ判定済
  for i in range(1, N):
    diff_t = int(plan[i][0]) - int(plan[i - 1][0])
    diff_x = int(plan[i][1]) - int(plan[i - 1][1])
    diff_y = int(plan[i][2]) - int(plan[i - 1][2])
    if not isRealizable(diff_t, diff_x, diff_y):
      print("No")
      exit()

  print("Yes")
```

ほぼほぼ正解だったのですが、after_contenst01.txtのテストだけ失敗してしまい、頭を抱えて悩んであれこれ調べました。

# ハマりポイント1 嘘解法
[ABC086C - Traveling の嘘解法](https://qiita.com/Kyan_TK/items/65251777d63e00448a70)
上記のような記事も見つけたのですが、こちらは時刻tに対して原点からの移動が可能かどうかを判定しているコードが出回っているという記事でした。自分はdiff_t, diff_x~~の辺りでその部分はしっかり考慮できていたため、そういった間違いもあるのかという程度でした。旅行プランが可能かどうかなのは瞬間瞬間だけでなく一連の移動が可能かどうかを見ないといけないですね。(こちらも記事に詳細があるので解説は省きます。)

# ハマりポイント2 絶対値
after_contest01.txtが通らない原因は結論から言うと、diff_xやdiff_yがマイナスになるときに答えが間違ってしまうからです。diff_x, diff_yを絶対値に修正します。
```py
    diff_x = abs(int(plan[i][1]) - int(plan[i - 1][1]))
    diff_y = abs(int(plan[i][2]) - int(plan[i - 1][2]))
```
なぜこれでうまくいくのかを言うより具体的になぜ修正前のコードではダメだったのかの例を記します。
```
2
100 50 50
102 0 0 
```
100秒後に(50, 50)に到達し、その2秒後に(0, 0)に到達できるか？を考えることになります。人間の目からでは当然"No"とわかりますが、修正前のコードではこちらが"Yes"になってしまいます。
大きすぎる移動ははじかないといけないので x + y > t としていましたが、diff_x, diff_yがマイナスであると、この条件がクリアされてしまっていたということです。言い方を変えると、関数isRealizableの引数xやyは"移動量"としなければいけませんでした。


# 余談
初期条件をt=0, x=0, y=0としてt=1のケースをループに含めてしまうのがよかったですね。
Azure App Service で WordPress と MySQL のマルチコンテナーを試してみた

Azure で WordPress の検証環境を用意する場合、Azure Database for MySQL を用意して VNET 統合したり、維持するのにそれなりの利用料金が発生したりするので、Terraform で簡単に作っては削除してを繰り返していました。インフラ構成を検証するのではなく、単に WordPress の機能検証を Azure App Service 上で行い、数日間その環境を維持したい場合、もっと安くできなかと思っていました。そこで MySQL もコンテナーで動かしたら良いのではと思い立ち、実際に Azure App Service で WordPress と MySQL のマルチコンテナーを試してみました。

## ローカルで Docker Compose を試す

```yaml:appservice-docker-wordpress.yml
version: '3.3'
services:
    db:
        image: mysql:8.0.35
        volumes:
            - db:/var/lib/mysql
        restart: always
        environment:
            MYSQL_ROOT_PASSWORD: somewordpress
            MYSQL_DATABASE: wordpress
            MYSQL_USER: wordpress
            MYSQL_PASSWORD: wordpress
    wordpress:
        depends_on:
            - db
        image: wordpress:latest
        volumes:
            - wordpress:/var/www/html
        ports:
            - "8000:80"
        restart: always
        environment:
            WORDPRESS_DB_HOST: db:3306
            WORDPRESS_DB_USER: wordpress
            WORDPRESS_DB_PASSWORD: wordpress
volumes:
    db:
    wordpress:
```

```bash:bash
# ローカルで WordPress + MySQL の動作検証
docker compose -f appservice-docker-wordpress.yml up -d

# ローカルで WordPress + MySQL を停止
docker compose -f appservice-docker-wordpress.yml down

# 再起動後も WordPress + MySQL が動作するか検証
docker compose -f appservice-docker-wordpress.yml up -d

# ボリュームも削除して検証終了
docker compose -f appservice-docker-wordpress.yml down --volumes
```

## Azure App Service 検証環境を構築

```bash:bash
# 環境変数にセット
prefix=mnrdwp
region=japaneast

# リソースグループを作成
az group create \
  --name ${prefix}-rg \
  --location $region

# App Service Plan を作成
az appservice plan create \
  --name ${prefix}-plan \
  --resource-group ${prefix}-rg \
  --sku B1 \
  --is-linux

# Web App for Container の Compose 版を作成
az webapp create \
  --name ${prefix}-app \
  --resource-group ${prefix}-rg \
  --plan ${prefix}-plan \
  --multicontainer-config-type compose \
  --multicontainer-config-file appservice-docker-wordpress.yml

# 「常時接続」をオンに設定
az webapp config set \
  --name ${prefix}-app \
  --resource-group ${prefix}-rg \
  --always-on true
```

## 検証環境の作成直後の様子

コンテナーが Web App にダウンロードされた直後の最初のアクセス時は `Error establishing a database connection` が表示されました。

![appservice-docker-wordpress-01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/675885/4fd788a4-0a68-f712-bdf7-60138f3115b1.png)

1 分後くらいにアクセスすると、DB の準備が整ったのか、WordPress のインストール画面が表示されました。

![appservice-docker-wordpress-02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/675885/e9827f38-f591-975d-040d-2a98d1ddb224.png)

## 参考

https://learn.microsoft.com/ja-jp/azure/app-service/tutorial-multi-container-app
小マイコンのSPIの速度を測って遊ぶ1(STM32G071)


目的
SPIの初期化の値による速度を
カウンターを使って計って遊ぶ

参考

https://qiita.com/caa45040/items/d4da4f6d2427bfbe16c7


https://qiita.com/caa45040/items/ac0703eeebbd14b96bda




●結果

DIV8 170KHz

![o_cop743.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/b5412c0b-7ce4-61bd-b163-4161397209ac.jpeg)

DIV4 183KHz

![o_cop744.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/25077c8b-9039-61f6-c886-46cdef5fd019.jpeg)


DIV2 170KHz

![o_cop745.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/b9071e02-e4ce-73f4-e587-6bc74298f7b3.jpeg)


●プログラム

```c++



//SPI_spped_ck_071_1

//ヘッダーファイル
#include <SPI.h>

//定義
#define DW    digitalWrite

//初期化
void setup() {

  // SPIの初期化
  SPI.begin(); // 11 12 13
  pinMode(SS, OUTPUT); // 10
  DW(SS, LOW); // 10
  SPI.setDataMode(SPI_MODE0);
  SPI.setBitOrder(MSBFIRST);

  //SPI.setClockDivider(SPI_CLOCK_DIV2);  // 8Mhz
  //SPI.setClockDivider(SPI_CLOCK_DIV4);  // 4Mhz
  SPI.setClockDivider(SPI_CLOCK_DIV8);  // 2Mhz

loop1_a:
  SPI.transfer(0b00011111); //1
  SPI.transfer(0b00011111); //2
  SPI.transfer(0b00011111); //3
  SPI.transfer(0b00011111); //4
  SPI.transfer(0b00011111); //5

  SPI.transfer(0b00011111); //6
  SPI.transfer(0b00011111); //7
  SPI.transfer(0b00011111); //8
  SPI.transfer(0b00011111); //9
  SPI.transfer(0b00011111); //10
goto loop1_a;

} //setup

//無限ループ
void loop() {
} //loop



```
Aiven、オープンソースのClickHouse®ベータ版でクラウドデータウェアハウスのマーケティングに参入

<font color="Grey">*[Aiven enters cloud data warehousing marketing with open source ClickHouse® Beta](https://aiven.io/blog/aiven-for-clickhouse-beta-now-available)の翻訳です。*</font>

2022年9月14日

# オープンソースのClickHouse®ベータ版でクラウドデータウェアハウスのマーケティングに参入

Aiven for ClickHouse®はどなたでもお試しいただけます。ぜひお試しください！

**更新：[Aiven for ClickHouseがGAになりました！[](/blog/aiven-for-clickhouse-now-generally-available)**。


## ClickHouse®はスピードのために作られました


ClickHouse®は、高速でオープンソースのクラウドデータウェアハウスです。高度なSQLクエリーを使用して、リアルタイムで分析データレポートを作成できます。ClickHouseは、1サーバーあたり1秒間に数億行、数十ギガバイトのデータを処理できるように構築されています。ClickHouseはすでにUber、Spotify、ドイツ銀行などの大手企業で採用されています。


ClickHouseは、業界をリードするクエリ・パフォーマンスをサポートする一方で、カラム型ストレージと圧縮の革新的な使用により、ストレージ要件を大幅に削減している。ClickHouseベンチマークによると、SnowflakeやRedshiftのような独自のクラウドデータウェアハウスよりも6～7倍高速です。


Aiven for ClickHouseは、Aivenの強力なサービスポートフォリオをクラウドデータウェアハウス市場に拡大します。このフルマネージドサービスにより、本番グレードの高性能データウェアハウスを簡単に導入し、他のAivenサービスでお馴染みの高度な機能、つまりダウンタイムなしの容易なスケーリング、容易なデータベースフォーク、クラウド/リージョン移行を楽しむことができます。


また、Aiven for Apache Kafka®、Aiven for PostgreSQL®、Aiven for Grafana®など、Aivenの他の製品との統合機能も組み込まれており、ストリーミングデータソースと運用データベースのデータの両方を使用した分析パイプラインの作成、分析結果の可視化が簡単に行えます。新機能にご期待ください！


## 何に使うの？


ClickHouseは、膨大な量のIoTテレメトリ、顧客行動、インフラのメトリクスとログ、クリックストリームデータなどを、スケールとパフォーマンスに妥協することなく分析するために使用されます。


アナリティクスのワークロードを運用データベースからオフロードし、さまざまなデータソースからデータを集約することで、ClickHouseは、運用アプリケーションのパフォーマンスを損なうことなく、360°の完全なアナリティクスビューを保証します。


ClickHouseの詳細については、[ClickHouseとは](https://aiven.io/blog/what-is-clickhouse)をご覧ください。


## "ベータ "とはどういう意味ですか？


Aiven for ClickHouseベータ版は、開発作業や本番レベルのワークロードのテストにご利用いただけます。ClickHouseデータベースを立ち上げ、スタートダッシュを切る絶好の機会です。オンライン分析アプリケーションを開発し、フィードバックを送りながら、必要な機能と性能を正確に実装することができます。ClickHouse Betaサービス用のAivenを作成すると、400ドルのクレジットが付与されます。このキャンペーンは、既存のお客様はもちろん、Aivenのサービスを初めてご利用になる方にも有効です。


ただし、ベータ版サービスではベーシック・レベルの無料サポートはご利用いただけますが、より高いレベルのサポート・パッケージは現在ご利用いただけません。


##始めるには？


新規のお客様は、まずAivenのトライアルアカウントを作成してください。


1.[Aiven Consoleにログイン](https://console.aiven.io/signup).
2.新しいClickHouseサービスを作成します。
3.新しく作成したサービスにデータをロードします。
4.組み込みのクエリーエディターを使用して、分析クエリーを開発し、テストします。
5.お楽しみください！


詳細については、[Aiven for ClickHouseベータ版製品ページ](https://docs.aiven.io/docs/products/clickhouse.html)をご覧いただき、[製品ドキュメントはこちら](https://developer.aiven.io/docs/products/clickhouse/)をご確認ください。


## まとめ


Aiven for ClickHouseベータ版はこちらです。ご感想やご質問はTwitter、または[beta@aiven.io](mailto:beta@aiven.io)までお寄せください。


--

Aivenと私たちのサービスに関する最新ニュースや、オープンソースに関するちょっとした情報を入手するには、[月刊ニュースレターを購読](https://aiven.io/newsletter)してください！Aivenに関する日々のニュースは、[LinkedIn](https://www.linkedin.com/company/aiven/)と[Twitter](https://twitter.com/aiven_io)のフィードでご覧いただけます。


サービスアップデートの情報を知りたい方は、[変更履歴](https://aiven.io/changelog)をご覧ください。


データインフラの管理にお困りですか？<https://console.aiven.io/signup>から今すぐ無料トライアルにお申し込みください！
[Swift]抽象クラスによる疑似名前空間の代替手段

オブジェクト指向言語による開発において、副作用のない汎用ユーティリティメソッドをstaticな領域に定義するってことが時折あると思う。

その際、関数を裸のままトップレベルに定義するとコード補完の候補がゴチャついたり、そもそもJavaなんかはトップレベルに直接関数を定義できなかったりして、static関数だけを集めた抽象クラスを名前空間代わりにしてグルーピングしたりすることがある。（あるよね？）

あくまで疑似名前空間でグルーピングして整理することだけが目的なので、抽象クラスにしてインスタンス化できないようにしておくのである。

しかし、Swiftには`abstract`修飾子が無く、`interface`相当の`protocol`は関数が実装を持つことを文法レベルで禁止している。

```swift
protocol Util {
  static func fn() {} // error: protocol methods must not have bodies
}
```

前置きが長くなったが、結論としてはSwiftでは`enum`を使うのが一般的（？）なようだ。

```swift
enum Util {
  static func fn() {
    print("Hello!")
  }
}

let util = Util() // error: 'Util' cannot be constructed because it has no accessible initializers
Util.fn() // Hello!
```

普段あまりSwiftを書かない僕は「ひょっとしてSwiftでは疑似名前空間ができないのか？」とちょっと戸惑った。
ので、オブジェクト指向言語は慣れているけどSwiftはあまり書いたことがないなあと言う方々に向けて一応書き残しておくことにした次第。

P.S.
**`Util`** みたいな、それにカテゴライズできる定義が無限に存在しそうな命名は可能であれば避けましょう。
おはようございます。ソフトウェア エンジニアとして自分のサービスを紹介する Web サイトを開発しています。

EcoiEnv Webアプリケーション開発、人工知能AIアプリに焦点を当てています。 JavaScriptを使用して。 我々は提供しています 教育、金融、電子商取引、ソーシャルメディア、ヘルスケアなど、さまざまな分野のソフトウェア開発におけるエンドツーエンドの支援。 当社は、Web アプリケーション レベルの脆弱性攻撃、Web サーバーの悪用を含む AI アプリケーション、OWASP 攻撃、XSS、SQL インジェクションに対して取り組んでいます。


![Captura de tela de 2023-11-03 19-32-14 copy.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3587964/2664ad71-2040-0fa1-95aa-31cb2fe5165d.png)
[React]フォームとStateの連動（+プルダウンと一覧の連動）

# はじめに

* Reactで入力フォームを作る際、入力値をStateに設定するが、入力フォームが増えるとその分Stateを用意するのは面倒
* Stateの更新時にでプロパティ変数（`[プロパティ変数名]:値`）を使うことで、ひとつのStateで管理できるようになる。

# 手順解説

## 1.Stateの定義

* オブジェクトにして、各プロパティをフォーム要素にする

```tsx
  const [form, setForm] = useState({
    feedId: data[0].id,
    note: '',
  });
```

## 2.フォームの定義

* value属性にStateのプロパティ値を設定する
* name属性をStateのプロパティ名と一致させる
* onChangeイベントに渡す関数は同じものにする

```tsx
    <select name="feedId" id="feedId" onChange={handleForm}>
      {data.map((v) => (
        <option key={v.id} value={v.id}>
          {v.name}
        </option>
      ))}
    </select>
    ...  
   <input
      type="text"
      name="note"
      value={form.note}
      onChange={handleForm}
    />
```

### 3.Stateの設定（onChangeイベントの処理）

* Stateのプロパティ名と値が、フォームのnameとvalueに一致するため、プロパティ変数で汎用的に設定ができる。

```tsx
  const handleForm = (e) => {
    setForm({
      ...form, // スプレッド構文でコピー
      [e.target.name]: e.target.value, // 差分を設定する際、プロパティ変数を使用する。
    });
  };
```

# 実装例

プルダウンを変更すると一覧が切り替わるコンポーネントを作成する。（テキストボックスとプルダウンのフォーム値をStateと連動）

* サンプルデータ

```json:list-data.json
[
  {
    "id": 1,
    "name": "プルダウン1",
    "list": [
      {
        "name": "リスト1-1",
        "memo": "めも1-1"
      },
      {
        "name": "リスト1-2",
        "memo": "めも1-2"
      }
    ]
  },
  {
    "id": 2,
    "name": "プルダウン2",
    "list": [
      {
        "name": "リスト2-1",
        "memo": "めも2-1"
      },
      {
        "name": "リスト2-2",
        "memo": "めも2-2"
      }
    ]
  }
]
```

```tsx:コンポーネント
import { useState } from 'react';
import listData from '../../data/list-data.json';

export const PullDownAndList = () => {
  const data = listData;

  const [form, setForm] = useState({
    feedId: data[0].id,
    note: '',
  });

  const handleForm = (e) => {
    setForm({
      ...form,
      [e.target.name]: e.target.value,
    });
  };
  // プルダウンの値（form.feedId)に応じた一覧でフィルタする
  const fillerList = () => data.filter((d) => d.id === +form.feedId)[0].list;

  return (
    <div>
      <select name="feedId" id="feedId" onChange={handleForm}>
        {data.map((v) => (
          <option key={v.id} value={v.id}>
            {v.name}
          </option>
        ))}
      </select>
      <div>
        一括メモ：
        <input
          type="text"
          name="note"
          value={form.note}
          onChange={handleForm}
        />
      </div>

      {fillerList().map((l) => (
        <li key={l.name}>
          {l.name} : {l.memo} : {form.note}
        </li>
      ))}
    </div>
  );
};

```
Magick::ImageMagickError: cache resources exhausted の対応方法（Heroku）

#### 趣旨

今回はエラー対処したメモとして記事を書きます

### エラー内容

```
Magick::ImageMagickError: cache resources exhausted 
```

原因：画像変換時のキャッシュリソースの不足でエラー

### 環境

- Heroku
- マイクロサービスアーキテクチャー
  - api（Rails）
  - front

### 背景

Herokuアプリで本番運用始めたらエラー発生！
Sentryを入れてたおかげで気づけた `Magick::ImageMagickError` のエラーが発生している
（本番運用の実際の大きいデータを入れてみて気づきました。。。）

エラーを調べるとpolicy.xmlを修正したら良さそう。。
けどローカルでの修正方法のみで、viで直に修正しろとか書いてました（Heroku上ではできない）

#### imagemagickの公式ドキュメント

https://imagemagick.org/script/security-policy.php


#### Heroku側の設定方法

https://help.heroku.com/RFDJQSG3/how-can-i-override-imagemagick-settings-in-a-policy-xml-file

## 対応方法

①Herokuのセッションを開始

`heroku ps:exec -a あなたのアプリ名`

②現在のpolicyを確認

`$ convert -list policy`

実際の結果
```
Path: /etc/ImageMagick-6/policy.xml
  Policy: Resource
    name: disk
    value: 1GiB
  Policy: Resource
    name: map
    value: 512MiB
  Policy: Resource
    name: memory
    value: 256MiB
  Policy: Resource
    name: area
    value: 128MB
  Policy: Resource
    name: height
    value: 16KP
  Policy: Resource
    name: width
    value: 16KP
  Policy: Delegate
    rights: None
```

ファイルを作成する必要があるので、現在のファイルを見に行く

`$ cat /etc/ImageMagick-6/policy.xml`

③Herokuのオーバーライト用の設定ファイルを作成

api配下に `.magick/policy.xml` を作成

```policy.xml
<policymap>
  <!-- memoryエラー対象用として1GiBにアップデート -->
  <policy domain="resource" name="memory" value="1GiB"/>
  <policy domain="resource" name="map" value="512MiB"/>
  <policy domain="resource" name="width" value="16KP"/>
  <policy domain="resource" name="height" value="16KP"/>
  <policy domain="resource" name="area" value="128MB"/>
  <policy domain="resource" name="disk" value="1GiB"/>
  <policy domain="delegate" rights="none" pattern="URL" />
  <policy domain="delegate" rights="none" pattern="HTTPS" />
  <policy domain="delegate" rights="none" pattern="HTTP" />
  <policy domain="path" rights="none" pattern="@*"/>
  <policy domain="cache" name="shared-secret" value="passphrase" stealth="true"/>
</policymap>
```


④環境変数の設定

`heroku config:set MAGICK_CONFIGURE_PATH=/api/.magick -a あなたのアプリ名`

apiを設定したのは、マイクロサービスアーキテクチャーに対応し
api配下にファイルを置いたことに対応するため。


### 終わりに

対応は難しくなかったけども
こんな記事が2時間前にあればもっと困らなかった。。
僕のように時間を費やす人を少なくするため、ここにメモします！
Dartで完全コンストラクタパターンを実装

# はじめに
個人的に完全コンストラクタパターンが好みで、Dartで実装する方法を調べたので記事にしました。

# 完全コンストラクタパターンとは
コンストラクタでクラス内のメンバ変数を全て正常な値で初期化する設計方法です。
また初期化以降は、変更できないようにしておくことで、作成後不正な状態になることはありません。

正常な状態でしか生成できなくするため、そのクラスを使う際に、不正な場合の動作を気にする必要がなくなります。

# Dartでの実装
```user_name.dart
// ユーザー名を表すクラス
// ユーザー名は仕様として、1文字以上、10文字以下の制限があるとする
class UserName {
  // ClassName._()でプライベートコンストラクタとなり、
  // 別のファイルからアクセスできない
  UserName._(this._value);
  final String _value;

  factory UserName({required String value}){
    if(value.isEmpty){
      throw Exception("UseNameBlankError");
    }

    if(value.length > 10){
      throw Exception("UserNameTooLong");
    }

    return UserName._(value);
  }

  String get value => _value;
}
```

使う側は以下になります。

```dart:create_user_use_case.dart
class CreateUserUseCase {
  Future<User> handle(String inputUserName){
    // もし、入力したユーザー名が仕様を満たさない場合例外が発生
    final userName = UserName(inputUserName);
    // 何かしらの登録処理
    return User(name: userName);
  }
}
```

# メリット
上記の例だと、UseCase内でバリデーションを行うこともできます。
```dart:create_user_use_case.dart
class CreateUserUseCase {
  Future<User> handle(String inputUserName){
    if(inputUserName.isEmpty){
      throw Exception("UseNameBlankError");
    }

    if(inputUserName.length > 10){
      throw Exception("UserNameTooLong");
    }

    // 何かしらの登録処理
    
    return User(name: inputUserName);
  }
}
```

ただこうしてしまうと、更新の時にも同じロジックが必要になるので、同じ処理が複数必要になります。
```dart:update_user_use_case.dart
class UpdateUserUseCase {
  Future<User> handle(String updatedUserName){
    // 更新時も同じバリデーションになる
    if(updatedUserName.isEmpty){
      throw Exception("UseNameBlankError");
    }

    if(updatedUserName.length > 10){
      throw Exception("UserNameTooLong");
    }

    // 何かしらの登録処理
    
    return User(name: updatedUserName);
  }
}
```

もし、仕様が変わり、文字数が11文字になったら、2箇所更新する必要があり、更新漏れの可能性が出てきます。
（実装者なら大丈夫かもしれませんが、実装者がプロジェクトを離れ、新規の人がメンテナンスすることになったら漏れる可能性は大きいです）

ロジックをまとめるだけなら、共通のメソッドを用意すれば済みますが、ユーザー名に関する使用はユーザー名のクラスにあるのが自然かなと思います。
【初学者向け】クッキーとセッションの仕組みとリスク

# 【初学者向け】クッキーとセッションの仕組みとリスク

ウェブサイトでログインすると、しばしば「ログイン状態が保持される」ことを経験するでしょう。これは、クッキーとセッションという仕組みが密接に関連しています。この記事では、なぜログイン状態が保持されるのか、その仕組みを説明し、セキュリティのリスクについても議論します。

## クッキーとセッションの役割

**クッキー**は、小さなデータの断片であり、ウェブサーバーからユーザーのブラウザに送信され、ブラウジングセッション中に情報を保持します。クッキーにはユニークな識別子や設定、状態情報などが含まれ、ウェブサイトとユーザー間のコミュニケーションを可能にします。

**セッション**は、ユーザーのブラウジングセッションを管理する仕組みで、クッキーを使用して特定のセッションを識別します。セッションは、ユーザーがウェブサイトにアクセスし、ウェブサーバーとやり取りする間の一連のアクティビティと関連情報を管理します。セッションはログイン状態の保持に非常に重要です。

## ログイン状態の保持

ログイン状態を保持する仕組みは、以下の手順で機能します：

1. **ログイン**: ユーザーがユーザー名とパスワードを入力し、サーバーが認証を行います。認証が成功すると、ユーザーはログイン状態に移行します。

2. **セッションの開始**: ログインが成功すると、サーバーはユーザーに一意の識別子（セッションID）を生成し、クッキーとしてユーザーのブラウザに保存します。

3. **セッション情報の保存**: サーバーはユーザーのセッション情報（たとえば、ユーザーID、設定、権限など）をサーバー側に保持します。

4. **クッキーの送信**: ブラウザは毎回リクエストを送信するたびに、クッキー（セッションID）をサーバーに送信します。サーバーはこのIDを使用して、ユーザーのセッションを識別し、関連情報を提供します。

5. **ログアウト**: ユーザーがログアウトすると、セッションは終了し、クッキーも削除されます。

## セキュリティのリスクと注意点

ログイン状態の保持は便利ですが、セキュリティ上のリスクも伴います。以下に注意点を示します：

1. **セッションハイジャック**: クッキーを傍受する攻撃者によって、セッションが乗っ取られ、不正アクセスが行われる可能性があります。

2. **不適切なクッキーの使用**: セッションクッキーにはセッション情報が含まれているため、適切に保護されていない場合、情報漏洩のリスクが高まります。

3. **不適切なセッション管理**: 適切なセッション管理が行われていない場合、セッションが不正に生成または操作される可能性があります。

セキュリティを強化するために、以下の対策が重要です：

- **HTTPS**: セキュアな通信プロトコル（HTTPS）を使用し、デ

ータの暗号化を確保します。

- **セッションの有効期限**: セッションは一定期間で無効になるよう設定し、セッションハイジャックのリスクを軽減します。

- **セッション管理の適切な実装**: セッション管理を慎重に実装し、不正アクセスやデータ漏洩のリスクを最小限に抑えます。

ログイン状態の保持は便利な機能ですが、セキュリティを考慮して適切に実装することが重要です。セキュリティ対策を講じ、ユーザーのデータを守ることが不可欠です。
（続）bundle updateしたらRedmineが起動できなくなった

# はじめに
以前書いた[記事](https://qiita.com/Mattani/items/ae74a373c9bdf8bf1cd0J "bundle updateしたらRedmineが起動できなくなった")の関連。事象としては、bundle updateしたらPassengerエラー発生し、Redmineが起動できなくなった、というものですが、その後の追跡調査によってどういう場合に発生するのかわかりましたのでまとめておきます。

# 原因
調査の結果、Nokogiri 1.11ではHTMLというクラスがNokogiri 1.12ではHTML4というクラスに名称変更されていることが判明。ちなみにLoofahは1.20までHTMLで1.21以降HTML4になっている。

RedmineとRubyのバージョンの組み合わせによってはNokogiri 1.11以前が選択される場合がある一方、Loofahのバージョンは指定されておらず、最新バージョンが選択される。

つまり、RedmineのGemfileでNokogiri 1.11以前が選択される場合は、クラス名変更の整合が合わず問題が発生する。この問題は、loofah1.21がリリースされた2023/5/10以降に発生することになる。

# 事象発生のメカニズム

* Gemfileにおける'~>'の意味は以下となっている
指定されたバージョンのメジャーバージョン、マイナーバージョンは指定したバージョンと同じであり、パッチバージョンは最新のものが選ばれる。
* Gemfileで書かれている最新のNokogiriが選ばれるわけではない。
例えば、```'~> 1.10.10'```と書かれていた場合、1.10系の最新版が選ばれ、1.11以上は選ばれない。
* RedmineのGemfileでは、loofahのバージョンは指定されていない
loofahは必要に応じて最新版が読み込まれる。loofah2.21.0以降ではHTML4が使用されている。

:::note info
* つまり、Gemfileの記述でNokogiri1.11が読み込まれるような環境では、loofahとバージョンが合わないためエラーが発生する。
* この問題は、loofah2.21.0がリリースされた2023/5/10以降、Nokogiri1.10より前のNokogiriを読み込むようなGemfileになっている場合に発生する。
:::

# 対策

Nokogiri 1.11が選択される環境では、Gemfile.localを作成してloofahを2.20になるようにするのがよい。
```ruby:Gemfile.local
gem 'loofah', '~> 2.20.0'
```
こうすれば、2.20系の最新版が選択され、Nokogiriとのバージョン不整合も発生しなくなる。

前の記事では、案２としてRedmineのGemfileを修正する、ということも書いていたが、後述するとおり、新しいRedmineではNokogiriのバージョンの指定も上がっているため、この問題は発生しない。逆にあえてその時にNokogiriのバージョン指定が1.11になっているということは、それ以上のバージョンに上げた場合のテストがされていないことを意味しているため、勝手にRedmineのGemfileのバージョン指定を書き換えることはオススメできない。
loofah 2.21.0がリリースされた後にのみおこる問題であるため、それを抑止するのが適切な対処である。

:::note warn
この問題は（loofah 2.21がリリースされた後）Ruby、Redmineのバージョンが古い場合にのみ発生する。
:::

# 事象が発生する環境まとめ

Redmineの各バージョンのGemfileの書き方を調査した結果、RubyのバージョンとRedmineのバージョンで読み込まれるNokogiriのバージョンは以下の通りである。
Nokogiri 1.11以前が読み込まれる環境では問題が発生し、Nokogiri 1.12以上が読み込まれる環境では問題は発生しない。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/138759/5d481add-a118-6f5d-2a38-2842fcb83120.png)
【Flutter】riverpodを使い複数のAPI（非同期処理）から全ての値を取得するまでローディングを表示する

# はじめに
![Screenshot 2023-11-03 at 21.53.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/167220/8278425a-2cee-d2be-653a-bdd06fd91936.png)

👆のような画面があったとして、A, B, Cとそれぞれ違うAPIから値を取得していたとする。
A, B, Cの全てを取得するまでローディングを表示させたい場合どうすれば良いのか？を記録しようと思います！

# 前提
riverpodは`hooks_riverpod`ではなく👇の`flutter_riverpod`です。

https://pub.dev/packages/flutter_riverpod

riverpodの導入部分は省きます。

# APIから取得した値を返す`FutureProvider`を用意
まずは値を取得するProviderを作ります。

・複数のAPIを同時に取得するために`Future.wait`を利用
・型セーフに扱うためにレコード型(`_HomeContents`)を使用

https://qiita.com/pioneerwalk/items/ebc3309abc04229dbd3a



```dart:main.dart
class HomeProviders {
  static final contents = FutureProvider<_HomeContents>((ref) async {
    late int itemA;
    late bool itemB;
    await Future.wait([
      ref.watch(MyPageProviders.itemAProvider.future).then(
            (value) => itemA = value,
          ),
      ref.watch(MyPageProviders.itemBProvider.future).then(
            (value) => itemB = value,
          ),
    ]);
    return (
      itemA: itemA,
      unreadChat: itemB,
    );
  });
}

typedef _HomeContents = ({
  int itemA,
  bool itemB,
});

typedef _Providers = HomeProviders;
```



```dart:mypage_providers.dart
class MyPageProviders {
  static final itemAProvider = FutureProvider((ref) async {
    // APIから値を受け取る
    return itemA;
  });

  static final itemBProvider = FutureProvider((ref) async {
    // APIから値を受け取る
    return itemB;
  });
}
```

# 画面に表示するウィジェットを実装

```dart:main.dart
class Home extends ConsumerWidget {
  const Home();

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    return Padding(
      padding: const EdgeInsets.all(16),
      // 先ほど定義した_Providers = HomeProvidersのこと
      child: ref.watch(_Providers.contents).when(
            // 読み込みが完了したら表示される
            data: (data) {
              return Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  _ItemA(data.itemA),
                  _ItemB(data.itemB),
                ],
              );
            },
            // 読み込みエラーの場合に表示される
            error: (_, __) => Center(
              child: Column(
                mainAxisSize: MainAxisSize.min,
                children: [
                  const Text('エラーが発生しました'),
                  FilledButton(
                    onPressed: () => context.pop(),
                    child: const Text('閉じる'),
                  ),
                ],
              ),
            ),
            // 全て読み込まれるまでローディングを表示
            loading: () => const Center(
              child: CircularProgressIndicator(),
            ),
          ),
    );
  }
}
```


https://pub.dev/documentation/riverpod/latest/riverpod/AsyncValue-class.html


`when`を使うことで状況によって表示するウィジェットを見やすいコードで簡単に実装できます:heart_eyes:

これで複数のAPI（非同期処理）から全ての値を取得するまでローディングを表示することが可能かと！
Unityのスクリプトテンプレートを変更してデフォルトのインデント数を変える

# C#のインデントはデフォルトで4っぽい
最近Unityで遊び始めました。
C#スクリプトのコーディングをする際はVSCodeを使用しています。

私はVSCodeの設定にてインデント数を2に設定しているのですが、Unityから作成したC#スクリプトはデフォルトのインデント数が4になっていました。
C#では4を用いるのが一般的…なんでしょうか？

とにかく私は2が好みでしたので、一度Shift+Tabで全てのインデントを消し、Tabでもう一度インデントを空けたところ…
なぜか4のインデントがされる…なぜ？

仕方ないのでファイルを一回消して、VSCode上から作り直したC#スクリプトから書き直しました。VSCodeから作成した同じ名前のC#スクリプトでは、無事にVSCodeの設定が適用されているようで、インデント数は2で書くことができました。

いちいちVSCode上で新規C#ファイルを作成し、本来なら自動生成されるはずのScriptテンプレートも手打ちしていくのは面倒。しかしインデント4はなんか嫌だなぁ…できれば変更したい。

# スクリプトテンプレート

生成された瞬間のインデント数を変えられないかな～と調べたところ、以下の記事を見つけました。

https://support.unity.com/hc/en-us/articles/210223733-How-to-customize-Unity-script-templates

UnityEditorのインストールディレクトリ（Windowsの場合`C:\Program Files\Unity\Editor\Data\Resources\ScriptTemplates`）内にある、`81-C# Script-NewBehaviourScript.cs.txt`を変更すれば良いらしいです。

他のスクリプトテンプレートも自由に変更できるようですが、Assetsの作成でC#スクリプトを追加している分にはこの`81`だけ変えれば良いっぽいですね。
```text:81-C# Script-NewBehaviourScript.cs.txt（デフォルト）
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

    #ROOTNAMESPACEBEGIN#
public class #SCRIPTNAME# : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        #NOTRIM#
    }

    // Update is called once per frame
    void Update()
    {
        #NOTRIM#
    }
}
#ROOTNAMESPACEEND#

```
たしかに4文字インデントになっています。
以下のような2文字インデントに変えちゃいましょう。
```text:81-C# Script-NewBehaviourScript.cs.txt（変更後）
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

  #ROOTNAMESPACEBEGIN#
public class #SCRIPTNAME# : MonoBehaviour
{
  // Start is called before the first frame update
  void Start()
  {
    #NOTRIM#
  }

  // Update is called once per frame
  void Update()
  {
    #NOTRIM#
  }
}
#ROOTNAMESPACEEND#

```
保存してもう一度UnityからC#スクリプトを作成してみます。
VSCodeで開いたところ、無事にインデント数を2にして編集できるようになっていました。

# こんなことしてるけど正しいのか？
VSCode上で「開いたファイルのインデント数に合わせて空気を読む」みたいな設定がありそうだなぁ～とは思いつつ、それを変更すると他の場面で面倒なことになりそうだなぁ～とか考えてやりませんでした。

そもそもJavaとかC#みたいなのはインデント数4でやるのがお作法なんでしょうか？？
見にくい気がして私は嫌なんですよね、4って。
【Python】内包表記まとめ

Pythonでは多くの変数の値を格納できるリスト型の変数があります。他言語における配列と似ています。

リスト型の変数はより効率的に記述するために内包表記という書き方があり、そちらを使ったリストの表現が主流です。


#1から100までリストに格納する。



```python:test.py
list1 = []
for i in range(1,101):
    list1.append(i)

print(list1)

>>[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81,
82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100]

```
`list1.append()`のメゾッドで（）の中身の値を格納しています。

このようになります。またPythonはインタプリタ言語なのでかなり処理が遅いです。
実際に処理時間を計ってみましょう。


今回は100万まで数えた場合の数字を数えます。（のちに内包表記で行った場合の経過時間を比較します。）


```python:test.py
import time

t1 = time.time() #プログラム初期の時間
list1 = []
for i in range(1,10**6+1):
    list1.append(i)

t2 = time.time() #処理を行った後の時間

print("経過時間：",t2-t1,"[s]")

>>経過時間： 0.25067615509033203 [s]
```

約0.25秒かかりました。

#内包表記の文法

内包表記の基本文法を紹介します。

```
変数名 = [i for i in 配列型の変数]
```

ここでfor文の文章に似ています。`[]`の中の`i`にあたる変数は任意の変数で大丈夫です。

実際に 1から100までの数字をリスト型の変数に格納しましょう。

```python:test.py
list1 = [i for i in range(1,101)]
print(list1)

>>[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75,
76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100]
```

このように簡単に掛けるようになります。また`if文`でリストに入る変数に条件をかけて、絞ることもできます。
こちらの方が実行時間も節約できておすすめです！
Pyspark（Jupyter Notebook）をDevcontainerで構築する

## 背景
Pyspark（Jupyter Notebook）をDevcontainerで構築することで、自分の環境を汚さず、いつでも同じ環境を即座に立ち上げて、Pyspark（Jupyter Notebook）を好きなだけローカルで検証できるを整えたかった。
それに、新しく`Asure Synapse Analytics Apache Spark Pool`だったり、`Azure Databricks`の案件に入るメンバーに対して、それらをキャッチアップする環境を用意してあげたかったのがことの発端です。
結局、環境を作るのが一番しんどいので。

## Devcontainer
Visual Studio Code、Devcontainerで環境を作成しています。
詳細はリポジトリを参照してください。

## リポジトリ
以下のリポジトリで公開していますので、よかったら利用してみてください。

https://github.com/silverlining-yamashita/try-pyspark

## 備考
画面収録した動画をgifに変換するために[ffmpeg](https://tech.ckme.co.jp/ffmpeg.shtml)を利用した。
Installは以下の通り。

```
brew install ffmpeg
```

※Mac、Homebrew
GitHubで役立つ開発者のためのコードスニペット集


# 1️⃣はじめに
この記事では、GitHubで便利なコードスニペットを紹介してみる。

# 2️⃣基本的なGitコマンド
```
git clone [リポジトリURL]
```
### リポジトリのクローン
このコマンドを使うことで、GitHub上のリポジトリをクローンすることができる。


```
git checkout -b [新しいブランチ名]
```
### ブランチの作成と切り替え
新しい機能を開発する際には、新しいブランチを作成して作業を行うのが一般的


```
git add .
git commit -m "[コミットメッセージ]"
```
### ステージングとコミット
変更をステージングし、コミットメッセージとともにコミットする。
これにより、変更が履歴に記録できる。

```
git push origin [ブランチ名]
```
### リモートにブランチをプッシュ
ローカルのブランチをGitHubのリモートリポジトリにプッシュする。


### プルリクエスト
GitHubのウェブサイトで「New pull request」ボタンをクリックし、変更をレビューしてもらい、メインブランチにマージを要請する。

```
git fetch origin
git merge origin/[メインブランチ名]
```
リモートの変更をフェッチし、マージします。コンフリクトがあれば、手動で解決し、再びコミットする。


### まとめ
　この記事では、GitHubでの開発をスムーズにするためのコード一覧を紹介した。これらのコマンドと機能を覚えておくことで、日々の開発プロセスがより効率的になるので、身につけていきたい。
[SAA-C03]Amazon MQ

# 更新日
- 2023/11/03 初回

# [Amazon MQ](https://aws.amazon.com/jp/amazon-mq/)

## これ何？[^1]
オープンソースメッセージブローカー[Apache ActiveMQ](https://activemq.apache.org/) or [RabbitMQ](https://www.rabbitmq.com/)のAWSによるマネージドサービス。

### [補足] メッセージ・ブローカーとは
- 「アプリケーション、システム、サービスが相互に通信し、情報を交換することを可能にするソフトウェア」[^3]で、つまるとこ異なるシステム間を連携する際にメッセージ(テキスト)を介して連携するためのソフト・サービスのこと。
- Amazon SQSやAmazon MSKなどもメッセージ・ブローカーにカテゴライズされる。[^4]

## 何が便利？
- メッセージブローカーなので非同期処理を行いたいときに利用される。（いうに及ばず）
- SQSと比較すると、SQSがAWSによってオートスケーリングしてくれるフルマネージドサービスである一方、Amazon MQはインスタンスタイプなため自身で設定したCPU・メモリで性能が決定する。[^5]ということで、DynamoDBとRDSなどと関係は似ているように思えた。一定の性能を必ず担保する必要がある場合やRabbitMQを利用した既存システムからのクラウド移行プロジェクトなどであれば、MQの方が都合がよいと言えるかもしれない。基本的にSQSでいいように思う。

## 利用例
- 一定の性能を必ず担保する必要がある場合
- RabbitMQを利用した既存システムからのクラウド移行プロジェクト

# 実装[^3],[^4]
のちほど追記します

# Ref
[^1]: [Amazon MQ とは？](https://qiita.com/miyuki_samitani/items/5c59bf4270181699395f)
[^2]: [AmazonMQとは?メッセージブローカーサービス](https://www.skyarch.net/column/amazonmq/)
[^3]: [メッセージ・ブローカーとは](https://www.ibm.com/jp-ja/topics/message-brokers)
[^4]: [代表的なマネージドサービスのメッセージブローカーまとめてみる](https://zenn.dev/miya_tech/articles/fc638e15893d87)
[^5]: [【徹底解説】AWSのメッセージングサービス)](https://www.sunnycloud.jp/column/20210525-01/#:~:text=SQS%E3%81%AF%E3%80%81AWS%E3%81%AB%E3%82%88%E3%82%8B%E8%87%AA%E5%8B%95,%E3%82%B9%E3%83%AB%E3%83%BC%E3%83%97%E3%83%83%E3%83%88%E3%81%A8%E3%81%97%E3%81%A6%E4%BF%9D%E8%A8%BC%E3%81%95%E3%82%8C%E3%81%BE%E3%81%99%E3%80%82)
Advancementのtickトリガーについて ~execute as @a at @sからの卒業~

## はじめに
MinecraftのAdvancement（進捗）におけるtickトリガーをよく使うのでその使い方について書いてみました

## 本題

#### 前提知識と使い方
そもそもAdvancementはそのプレイヤーが条件に当てはまるときに達成することのできるもので、達成時に経験値を付与したり、そのプレイヤーにfunctionを実行させたりできるものです。今回話す「トリガー」というものはその条件の大部分を占めます。



「tick」トリガーは、名前からそのままで、「毎tickこの進捗を達成する」というものです。言い換えると、「強制的に毎tickこの進捗を達成してしまう」という条件になります。


これだけだと、プレイヤーはワールドに入った瞬間、進捗を達成してハイ終わり。

そこで先ほど話した、「進捗は達成したプレイヤーにfunctionを実行させることができる」という話から、tickの進捗を達成したプレイヤーに、その進捗を剝奪するfunctionを実行させます。

~~~json:tick.json
{
  "criteria": {
    "requirement": {
      "trigger": "minecraft:tick"
    }
  },
  "rewards": {
    "function": "minecraft:advancement_tick.mcfunction"
  }
}

~~~
~~~mcfunction:advancement_tick.mcfunction
advancement revoke @s only minecraft:tick

~~~

こうするとどうなるかと言うと、プレイヤーは、進捗達成→function実行→進捗達成→function実行、と、毎tickこのループを繰り返します。つまり、このfunction内に書いたコマンドはすべて、プレイヤー主体で毎tick実行できる、と言うことです。

#### 伝えたいこと
毎tick繰り返しfunctionを実行するだけなら、データパックのタグのtick.jsonにまとめればいい話なんですが、大事なのは、 **プレイヤー主体** でfunctionを実行できるということです。

~~~mcfunction:advancement_tick.mcfunction
#アイテムを使う(useのスコアが１以上になっている)とタイマー(playerのtimeのスコア)を進める
execute if score @s use matches 1.. run scoreboard players add @s time 1

#タイマーが200tickを超えるとスコアをリセットする
execute if score @s time matches 200.. run scoreboard players reset @s use
execute if score @s time matches 200.. run scoreboard players reset @s time
~~~
例えばこんな風に、プレイヤーのアイテムのクールダウンを簡単に付けられます。
~~なお全部の先頭に「execute as @a at @s」つければ...~~

また、副次効果として、 **プレイヤー主体で毎tick実行したいもの** （プレイヤーにパーティクルを出す・プレイヤーのスコアをリセットする、など）と、 **ワールド主体で毎tick実行したいもの** （制限時間のスコア計算とか）を、分けることができるので、綺麗な（後からデバッグしやすい）データパックになると思います。 **整理大事!**　←ここが一番伝えたい

#### 最後に
データパック触り始めてmcfunctionだけ使っているとどうしても`execute as @a at @s`を使いまくっていたので、そういった方向けに書いてみました。
もし同じような境遇の人、そして、tick.jsonの中がごちゃごちゃになっている人は是非一度試してみてください。

## あとがき
私は技術畑出身でもなければ、minecraftの隠された仕様みたいなのを日々探ってるというようなものでもないです。これじゃなきゃできない、このやり方が最高効率、といったわけではないので、データパックに興味をもった人・触り始めた人が、 **「この書き方分かりやすい」「これだとやりやすい」** といったものを見つけてもらえればと思います。
、
【Rails】CORSの設定方法

## `rack-cors`をインストール

Gemfileに以下を追記して`bundle install`します。

```Gemfile:Gemfile
gem 'rack-cors'
```

```terminal
bundle install
```

## configを作成

`config/initializers/cors.rb`に設定を記述します。 

```config/initializers/cors.rb 
# Be sure to restart your server when you modify this file.

# Avoid CORS issues when API is called from the frontend app.
# Handle Cross-Origin Resource Sharing (CORS) in order to accept cross-origin AJAX requests.

# Read more: https://github.com/cyu/rack-cors

Rails.application.config.middleware.insert_before 0, Rack::Cors do
  allow do
    # 許可するオリジンを記述（APIを叩く側のドメインを記述）。
    origins 'localhost:3000'

    # 許可するリソースファイルを記述
    resource '*',
      headers: :any,  # CORS リソース要求で許可される HTTP ヘッダー
      expose: ["access-token", "expiry", "token-type", "uid", "client"], # レスポンスの HTTP ヘッダをクライアントに公開
      methods: [:get, :post, :put, :patch, :delete, :options, :head],       # 許可するメソッドを記述
      
  end
end
```
rex0220 計算式プラグイン 期間計算

[計算式プラグイン](https://qiita.com/rex0220/items/ae83c6d5ce50ff7a9a73)で、期間計算を行ってみます。

# 概要

DATE_DIFF、DURATION_FORMAT 関数を使うと 1年3ヶ月18日のような、期間計算を簡単に設定できます。

![2023-11-03_23h27_05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/3497ce5a-cae9-bc3d-01bb-baff56e9210d.png)

# 操作例

![2023-11-03_23h28_36.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/1367efd5-ba8f-2a45-9e58-b31bf6b6d9c0.gif)

# 計算式プラグインの設定

DATE_DIFF 関数で、この期間は何日かは求めることができますが、何年何ヶ月と何日のような計算はできません。
このような計算は、DURATION_FORMAT 関数を使うと簡単に変換できます。


「期間」の設定内容
- IF(AND(開始日,終了日),　// 開始日と終了日が入力済みの場合、
    - DURATION_FORMAT(  // ミリ秒の値を指定した書式に変換
        -  DATE_DIFF(開始日,終了日,"milliseconds"),  // 開始日と終了日の期間をミリ秒に変換
        -  "$%Y年@Mヶ月@D日" // 1年3ヶ月18日のような書式を指定
    -  )
-  )

DATE_DIFF、DURATION_FORMAT 関数のオプション

- DATE_DIFF(項目|"TODAY", 項目|"TODAY", type)　：日付差分関数
    - type:"years", "months", "weeks", "days", "hours", "minutes", "seconds", "milliseconds"
- DURATION_FORMAT(項目, format)　：期間関数 (指定単位の数値を取得)
    - "$": 期間がマイナスの場合、"-"を表示
    - "#Y", "#M", "#D", "#H", "#m", "#s": 指定単位に変換TOTAL（小数を含む）
    - "%Y", "%M", "%D", "%H", "%m", "%s": 指定単位に変換TOTAL（小数を含まない）
    - "@Y", "@M", "@D", "@H", "@m", "@s": 指定単位に変換
    - "HH", "MM", "SS": 指定単位を取得（２桁表示）


![2023-11-04_22h09_55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/5f42b676-2069-f8c6-ba1e-5214ef1f8672.png)


```.js
// 期間 SINGLE_LINE_TEXT
IF(AND(開始日,終了日),
  DURATION_FORMAT(
    DATE_DIFF(開始日,終了日,"milliseconds"),
    "$%Y年@Mヶ月@D日"
  )
)

// 年 NUMBER
IF(AND(開始日,終了日),
  DURATION_FORMAT(
    DATE_DIFF(開始日,終了日,"milliseconds"),
    "$%Y"
  )
)

// 月 NUMBER
IF(AND(開始日,終了日),
  DURATION_FORMAT(
    DATE_DIFF(開始日,終了日,"milliseconds"),
    "@M"
  )
)

// 日 NUMBER
IF(AND(開始日,終了日),
  DURATION_FORMAT(
    DATE_DIFF(開始日,終了日,"milliseconds"),
    "@D"
  )
)

// 期間日数 NUMBER
DATE_DIFF(開始日,終了日,"day")
```
[Emacs] dired-dwim-targetを一時的に無効化する

こんにちは！

EmacsのDiredには`dired-dwim-target` という変数があります。これを`t` に設定しておくと、複数のDiredバッファを開いてる際、ファイルのコピーや移動をしようとすると、空気を読んで(**d**o **w**hat **i** **m**ean)別のバッファがコピーや移動の対象として選択されます。

![ezgif-3-b679113268.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/597113/bc5dc5c6-f5b8-b5e4-b63c-c5f0729cc404.gif)

とても便利なのですが、たまに「いや、今は空気を読まずに自分のバッファで操作して:sweat_smile:」ってときありませんか？

自分の場合そういう時、一旦別のバッファに移動・コピーしてから、移動先のバッファに移ってもう一度元のバッファに戻してました... (皆さんはどうしてるんでしょう?)


universal arguments (C-u) をつけた操作で`dired-dwim-target`の効果が切り替わってくれるとEmacsぽいかなと思って試してみたのですが、そんな都合よく切り替わってはくれませんでした🤷

何か良い方法がないか`dired-dwim-target` のドキュメントを読んでみると、どうやらこの変数には関数もセットできるようです。ということは

```elisp
(setq dired-dwim-target (lambda () (if current-prefix-arg nil (dired-dwim-target-next))))
```

とすれば自分が期待する挙動になりました！これでデフォルトではコピー・移動先を推測してくれて、`C-u` をつけた時だけ今いるバッファが対象になります！

以上です！他にもっといい設定あるよ、とか間違ってる点などあればご指摘頂けると嬉しいです :laughing:
2進展開を用いた計算量を減らすテクニック

# はじめに
「プログラミング・ビットコイン」というオライリーの本にて、2進展開を使って計算量を減らすテクニックに出会って感動したので、それを記録しておくためにこの記事を書きました。

# 2進展開とは
情報系の学問を学ぶにあたって前菜として出てくる2進数。ある整数$a$を2進数で表すために、
```math
a = a_{0}\times2^0 + a_{1}\times2^1 + a_{2}\times2^2 + \dots + a_{n}\times2^n
```
を満たすような$a_0, a_1, \dots, a_n$を求める必要があります。言い方を変えると、$a$を2の累乗を用いて表す必要があるということです。このような表し方を**2進展開**といいます。
例えば整数14を考えてみましょう。整数14は
```math
\{14\}_{10} = 0 + 2 + 4 + 8 =  0 \times 2^0 + 1 \times 2 ^ 1 + 1 \times 2 ^ 2 + 1 \times 2 ^ 3 = \{1110\}_{2}
```
のように2進展開で表すことができます。

# 2進展開を用いて計算量を減らすテクニック
本題のテクニックについてですが、用いるところはかなりシンプルです。
以下の問題を解くアルゴリズムを考えます。
```
ある整数mについてn回加算を繰り返した時に得られる数を求めなさい。ただし、使用可能な演算は加算のみとする。
```
`ただし、使用可能な演算は加算のみとする。`という文に疑問を持つ方もいると思います。これについては[補足](#補足)で浅く説明しています。興味ある方は参照してください。

さて、アルゴリズムについて、パッと思いつくのはfor文で$n$回ループさせることでしょうか。
プログラムは以下のようになります。
```python
# O(n)
m = 3
ans = 0
n = 10
for _ in range(n):
    ans += m
print(ans) # ans = 30
```
こちらの計算量は$O(n)$となります。このアルゴリズムには欠点があります。それは$n$が大きくなったとき計算にかなりの時間を要してしまうということです。

そこで改良版として2進展開を用いたアルゴリズムを考えます。
まず、求める解は`m=3, n=10`のとき、
```math
\begin{align}
ans &= m \times n\\
    &= 3 \times 10 \\
    &= 3 \times (0 \times 2^0 + 1 \times 2^1 + 0 \times 2^2 + 1 \times 2^3)\\
    &= 0 \times 3 \times 2^0 + 1 \times 3 \times 2^1 + 0 \times 3 \times 2^2 + 1 \times 3 \times 2^3\\
    &= 0 \times 3 \times 1 + 1 \times 3 \times 2 + 0 \times 3 \times 4 + 1 \times 3 \times 8\\
    &= 0 \times (3) + 1 \times (3 + 3) + 0 \times (3 + 3 + 3 + 3) + 1 \times(3 + 3 + 3 + 3 + 3 + 3 + 3 + 3)　\tag{1}
\end{align}
```
のようになります。式変形では`n`を2進展開したのちそれぞれの項に`m=3`をかけています。式$(1)$を用いてプログラムを書いていきます。
コードに落とし込むと以下のようになります。
```python
# O(log_2(n))
m = 3
ans = 0
current = m
while n:
    if n & 1:
        ans += current
    current += current
    n >>= 1
print(ans) # ans = 30
```
プログラムの内容を説明していきます。
先ほどの例ではなかった変数`current`は、`ans`に加算する値を格納しておきます。
```python
while n:
    ...
    n >>= 1
```
`n >>= 1`では、`n`の2進数値に対して1ビット右にスライドしています。これと`while`によって`n`の2進数値の各桁に対して演算を行なっていきます。
```python
if n & 1:
    ans += current
current += current
```
`if n & 1`は`n`の2進数値の1桁目が1であるか0であるかの条件分岐を行なっています。1のとき`ans += current`を実行し、0のときif文内には入らず処理が継続されます。
先ほどの処理によって`n`はスライドされるので、これは演算対象の桁における数が0か1かを判断していることになります。
すなわち、この処理は式$(1)$における0をかけているか1をかけているかに対応しています。

`current += current`によって演算する桁に応じた加算する数を用意しています。`current`の初期値は3です。ループしていくと`current`の値は`3, 6, 12, 24`となります。この処理は式$(1)$における括弧内（$(3 + 3)$や$(3 + 3 + 3 + 3)$など）と対応しています。

説明を踏まえて、このプログラムの全体を眺めてみると、式$(1)$の計算を行っていることがわかりますでしょうか。ループ回数については`n`を2進数にしたときの桁数に依存します。桁数と言えば`log`ですね。したがって計算量は$O(log_2(n))$となります。

いかがでしょうか。2進展開を用いることで計算量を$O(n)$から$O(log_2(n))$まで減らすことができました！

# 補足
数学の世界で群と呼ばれる概念があります。詳しい定義は調べて欲しいのですが、この郡で定義される演算は1つのみです。そしてビットコインで扱われる有限巡回群において定義される演算は加算であり、その加算は非線形です。したがって、同じものをn回加算した後の数値を求める方法は、正直に1つずつ足していく方法しかありません。そのときに今回紹介した2進展開を用いることになります。これより深いことは実際に本を読んでみることをおすすめします！！

# 参考にした資料
[プログラミング・ビットコイン](https://www.oreilly.co.jp/books/9784873119021/)
vol1: DHCP のやりとりをwiresharkで覗いてみる

DHCP を用いると、IP Address がDHCPサーバーによって割り振られることは
もちろん理解していたが、他にどんな情報が割り振られるのかをあまり意識して
いなかった。

ということで、wireshark でpacketのやりとりを
のぞいてみた

### 全体的なやりとり

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271660/2a25b5a8-2e54-0417-a4fe-c43d10157b04.png)

- MAC address
DHCP Server(SuperMic_40:cd:5f)
DHCP Client(Vmware_25:cc:71)

DHCP Discover -> DHCP Offer -> DHCP Request -> DHCP ACK
という流れ

### DHCP Discover
![DHCP_discover.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271660/b41f3e96-1b2d-6e7b-cb6f-5241b56453fb.png)

DHCP server がネットワーク内に存在するかどうかを、クライアントが見つけるためのPhase。
ブロードキャスト転送を使い、ネットワーク内全体にDHCP Discover で問い合わせ。
お願いしたいIPアドレスがあればそれをRequested IP Addressで、
また、追加でどんな情報が欲しいかをParameter Request List で要求する

### DHCP Offer
![DHCP_offer.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271660/e75efb3a-c785-9fd3-86be-013db8d9dca7.png)



もしネットワーク内に、DHCPサーバーがいれば、DHCP サーバーがDHCP Offer メッセージで応答する。
(もしDHCPサーバーがいなければ、だれも反応しないで、クライアントはタイムアウトで終了）
ここではクライアントにユニキャスト転送で、直接応答する。

DHCP Offer では、Client に割り振る IP アドレスが返される。
(ここではあくまでOffer であって、まだIP address は確定していない)

IPアドレス以外にも、DHCP server のIPアドレスと、
Clientのネットワーク設定に必要な以下の基本情報が返される

- Subnet mask
- router
- Domain Name
- Doman Name Server 

### DHCP Request

![DHCP_request.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271660/8d5facbd-3261-629d-a453-e55d30582d00.png)


クライアントは、提案されたIPアドレスを使用する通知をブロードキャストで送信
(もしDHCPサーバーが複数いる場合は、この通知で、自分が提案したアドレスが落選したかどうかを知ることができる)

### DHCP ACK

![DHCP_ack.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271660/c5d04e8c-2331-3bd5-3e4d-226b56bfedaa.png)


サーバーは、要求を許可するメッセージをクライアントに送信
これで手続きが完了

### テストに使用したコマンド
(DHCPサーバーがネットワーク内にいることが前提で)
クライアント側で以下のコマンドを実行することで、DHCPの一連のプロセスがモニタリングできる

$sudo dhclient -r
$sudo dhclient 

### 参考

ほかにもDHCPNAK, DHCPINFOM, DHCPRELEASE, DHCPDECLINE といった、メッセージがある。

メッセージの正しい定義を確認したい場合は、
DHCPを定義しているRFC2131を参照するとよい。

https://datatracker.ietf.org/doc/html/rfc2131

Parameter Request List の定義は

https://datatracker.ietf.org/doc/html/rfc2132
OpenShift Localを導入してOpenShiftをためす

# 目的
OpenShift Local（CRC) を動かして OpenShift 環境をためします

# インストール

インストールは以下を参考にします。CentOS 9に導入しました。

https://access.redhat.com/documentation/ja-jp/red_hat_codeready_containers/2.0/html-single/getting_started_guide/index#installing-codeready-containers_gsg

おおまかにやったことは

- CRCのダウンロード
- crc 実行ファイルの ~/binへのmv （~/binのPATHへの追加）
- crc config set preset openshift （デフォルトなので必要無し）
- crc setup
- crc start
- pull secret を取得

pull secret は
https://cloud.redhat.com/openshift/install/pull-secret
から取得します。事前にとっておいてもいいですが、crc start 実行中に入力をもとめられたときでも問題ありません。

# OpenShiftにログイン

OpenShift Webコンソール、oc を使ってログインします。先に紹介したリンク で ”3.4.2. OpenShift CLIによるOpenShiftクラスタへのアクセス” に詳細があります。

oc コマンドは以下から openshift-client-linux-4.13.14.tar.gz をダウンロードしました。
https://mirror.openshift.com/pub/openshift-v4/clients/ocp/4.13.14/

実行するのは

- ocのダウンロード
- 解凍した kubectl oc の ~/bin への mv
- oc login -u kubeadmin / oc login -u developer

パスワードをつかわなくてもログインできましたが、パスワードは crc startが完了したときに出力されてます。

# トラブルシューティング ハンズオン

OpenShiftの操作をするのにハンズオンの課題を用意しました。githubからクローンして書かれている課題をときつつOpenShiftの構成方法や使い方を覚えていきます。

```
git clone https://github.com/pdprof/openshift-liberty.git
cd openshift-liberty
```

レポジトリにはディレクトリがあり、それぞれが何のハンズオンかを示しています。

- htpasswd （ユーザー管理など）
- ingress （ネットワーク）
- resource （リソース制限）
- route （SSLネットワーク）
- schedule （PODの割り当て）

# ハンズオン内容の確認

ハンズオンを始めるときには、kubeadminでログオンします。各ディレクトリにREADME.mdがあるので、それをみて課題を確認します。

ディレクトリ中には start.sh が用意されているので、実行するこで環境の準備ができます。課題が終わったら finish.sh を実行してリソースの削除などを実行してクリーンアップします。

```
oc login -u kubeadmin
./start.sh
./finish.sh
```

kubeadmin ユーザーを無効にする手順もあります。環境を削除して作り直してもよい前提でやるか、別の管理ユーザーが有効であることをたしかめた上で実行します。方法を知っておくだけでも良いです。

回答の準備はありませんが、start.shの中身や構成時に使用している yamlファイルなどがヒントになります。


# トラブルシューティング用のコマンドなど

よく使うコマンド（状況確認、構成時の説明確認）などをメモしておきます。

```
oc get all
oc get node 
oc get node -o wide
oc describe pod
oc exec -it <pod_name> bash
oc create dc --help
oc explain oauth
oc explain oauth.spec
oc explain oauth.spec.templates
oc explain limitrange
oc explain limitrange.spec
```

yamlのもとにするテンプレートはOpenShift Consoleを使ってリソースを作成するときなどに表示されます。コマンドだけでなくブラウザを使用するのも有効です。


# まとめ

OpenShift Local（CRC)を使ってOpenLibertyを動かして、操作方法を学びました。
Next.jsプロジェクトを新規作成した際にファイルのあちこちでエラーが出た

## はじめに
Next.jsで新規プロジェクトを作成した際にファイルのあちこちでエラーが出ました。

**モジュール '○○' が見つかりません。'moduleResolution' オプションを 'node' に設定することか、'paths' オプションにエイリアスを追加することを意図していましたか?**

といったエラーが出力され、`moduleResolution`に問題があったのは一目瞭然でしたが、
今回はその解決方法について備忘録としてQiitaに投稿します。
（~~なんでデフォルト設定でエラーになるんだ。。~~）

### 対象者
Next.jsでプロジェクトを作成して、上記のエラーに悩まされている人です。
解決方法だけではなく、一応エラー内容にも触れています。

### 前提
`node`等のバージョン情報を記載しておきます。
関係ないですが、この記事を書き始めてから`next.js`のバージョンが`14.0.1`になっていることに気づきました。
| library | version |
|:-:|:-:|
| node | 18.17.1 |
| npm | 9.6.7 |
| next.js | 14.0.1 |


## プロジェクトを作成する
プロジェクトを作成したい場所で、以下コマンドを実行。
```terminal:terminal
npx create-next-app@latest
```
色々聞かれますが、全部デフォルト回答でOK
```terminal:terminal
What is your project named? my-app
Would you like to use TypeScript? No / Yes
Would you like to use ESLint? No / Yes
Would you like to use Tailwind CSS? No / Yes
Would you like to use `src/` directory? No / Yes
Would you like to use App Router? (recommended) No / Yes
Would you like to customize the default import alias (@/*)? No / Yes
```
プロジェクト作成完了です。
お手軽ですね。


## src/app/page.tsxを確認してみる
「やった、作成できたぞ！」と思ったらファイルのあちこちでエラーが出力されていました。
冒頭でもお話しした通り、**'moduleResolution' オプションをうまいことやれ！** とのことなので`tsconfig.json`ファイルを見に行きます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3581713/c325f573-ee5c-485e-8e63-74a47afa2544.png)


## tsconfig.jsonファイルを修正
結論、以下のように修正するとエラーがすべて消えました。
```diff_json:tsconfig.json
{
  "compilerOptions": {
    ・・・
-   "moduleResolution": "bundler",
+   "moduleResolution": "Node16",
    ・・・
  },
  "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
  "exclude": ["node_modules"]
}
```


## moduleResolutionの説明
[Typescript公式サイト](https://www.typescriptlang.org/tsconfig#moduleResolution)いわく、moduleResolutionは **「モジュール解決戦略を指定します。」** とのことです。
...うーん、よくわからん。

よくわかりませんが、設定値の説明を見てみるとモジュールのエクスポートやらインポートのやり方を指定しているようです。

要約すると以下のような感じ。

| 設定値 | 説明 |
|:-|:-|
| 'Node16', 'Nodenext' | Node.js の最新バージョンの場合はこれでOK。ECMAScript と CommonJS の両方がサポートされている。 |
| 'Node10', 'Node' | v10 より古い Node.js バージョン用。CommonJS のみをサポート。おそらく最新のコードでは使用する必要はない。 |
| 'Bundler' | Node16 や Nodenext とほぼ同様。違う点は、bundler はインポートの相対パスにファイル拡張子を必要としない。 |
| 'Classic' | 旧バージョンで使用していた。今は使わない。 |

※**ECMAScriptをサポート**：モジュールは `import` `export` で扱うよ
※**CommonJSをサポート**：モジュールは `require` `exports` で扱うよ

初期値の`Bundler`を設定しておくと、**モジュールをインポートするときに拡張子をいちいちパスに記載しなくてよい**くらいのメリットしかないっぽいので、`Node16`、もしくは`Nodenext`の指定でよさそうです（これら2つに違いはなさそう）。


## おわりに
以上です。
誰かのお役に立ちますように ^^