OmniAICreator/Qiita-1.07M huggingface dataset:
本日の微分方程式（8日目）

なんとなく脳トレがてら1日1問解説しようと思っただけです．飽きたらやめます．

## 問題
```math
xy^{\prime} = 2x+3y,
```
を解け．
## ポイント
- パっと見変数分離できなさそうなので詰みです．
- 実は，両辺を $x$ で割ってみると， $\dfrac{\mathrm{d}y}{\mathrm{d}x}=f\left( \dfrac{y}{x} \right)$ の **同次形** で有ることがわかります．
    - 同次形では，$u=\dfrac{y}{x}$ とおくことにより，$ \dfrac{\mathrm{d}u}{\mathrm{d}x}=\dfrac{f(u)-u}{x}$ の変数分離形を作ることができます（数日後に証明を問題にします）．
    - $u$ は $x$ についての関数であることに注意してください．
- 同次形については，ヨビノリさんの動画もご参照ください．
    
https://www.youtube.com/watch?v=QeYOiFU6UNs

## 解説

はじめから $\dfrac{\mathrm{d}y}{\mathrm{d}x}=f\left( \dfrac{y}{x} \right)$ の形になっていれば 「$x=0$ の点は定義されていない」と見ることが出来ますが，今回は意図的に割り算を行うため，念の為場合分けを行いましょう（慣れればスルーで基本問題ないです）．

---
**① $x\neq 0$ のとき**

与式の両辺を $x$ で割ると，

```math
\begin{align*}
\dfrac{\mathrm{d}y}{\mathrm{d}x} &= 2+\frac{3y}{x},
\end{align*}
```

が得られます．これは，$\dfrac{\mathrm{d}y}{\mathrm{d}x}=f\left( \dfrac{y}{x} \right)$ の**同次形**なので，$u=\dfrac{y}{x}$ とおくことにします．
さて， これを整理した $y=ux$ の両辺を $x$ で微分することで（$u$ は $x$ の関数であることに注意しましょう），

```math
\begin{align*}
\dfrac{\mathrm{d}y}{\mathrm{d}x} &= \dfrac{\mathrm{d}u}{\mathrm{d}x}\times x + u\times 1=x\dfrac{\mathrm{d}u}{\mathrm{d}x} + u,
\end{align*}
```

が得られます（同次形のときは毎回この式が登場するので，ここまでの流れは暗記してもまぁ大丈夫です．）．ここで，$\left(f(x)g(x)\right)^\prime = f(x)^{\prime}g(x)+f(x)g(x)^{\prime}$ ，「積の微分」と呼ばれるものを用いました．上記を微分方程式に代入して整理すると，

```math
\begin{align*}
x\dfrac{\mathrm{d}u}{\mathrm{d}x} + u &= 2+3u,\\
x\dfrac{\mathrm{d}u}{\mathrm{d}x} &= 2+2u,\\
\therefore \dfrac{\mathrm{d}u}{\mathrm{d}x} &= \frac{2(1+u)}{x},
\end{align*}
```

無事に変数分離形を得ることが出来ました．残りの変形は，1日目から7日目までのものと同様です． 
わり算操作が入るので，$1+u\neq 0$ と $1+u=0$ で場合分けが必要なことに注意してください．

---
**$(~\mathrm{i}~)$ $1+u\neq 0$ のとき**

両辺を $1+u$ で割って， $x$ で積分を行うと，

```math
\begin{align*}
\dfrac{1}{1+u}\dfrac{\mathrm{d}u}{\mathrm{d}x} &= \frac{2}{x},\\
\int\dfrac{1}{1+u}\dfrac{\mathrm{d}u}{\mathrm{d}x}\mathrm{d}x &= \int \frac{2}{x}\mathrm{d}x,\\
\int\dfrac{1}{1+u}\mathrm{d}u &= \int \frac{2}{x}\mathrm{d}x,\\
\log{|~1+u~|}&=2\log{|~x~|}+C_1~~~(C_1:任意定数),\\
|~1+u~|&=e^{2\log{|~x~|}+C_1},\\
1+u&=\pm e^{C_1}e^{2\log{|~x~|}},\\
u&=C_2e^{2\log{|~x~|}}-1~~~(C_2\neq 0),\\
u&=C_2\left(e^{\log{|~x~|}}\right)^2-1,\\
u&=C_2|~x~|^2-1,\\
\therefore u&=C_2x^2-1,\\
\end{align*}
```
と $u$ を求めることができます．

---

**$(~\mathrm{ii}~)$ $1+u= 0$ のとき**  
 
$1+u= 0$，すなわち $u=-1$ のとき，微分方程式 $\dfrac{\mathrm{d}u}{\mathrm{d}x} = 2(1+u)$ に代入すると，

```math
\begin{align*}
(左辺) = 0,
\end{align*}
```

```math
\begin{align*}
(右辺) = 2\cdot(1-1)=0,
\end{align*}
```
となり，(左辺) $=$ (右辺) とわかるので解とわかります．ここで， $u=-1$ は，$(~\mathrm{i}~)$ の解 $u=C_2x^2-1$ において， $C_2=0$ としたものと見ることができます．

---

まとめると，$(~\mathrm{i}~)$ と $(~\mathrm{ii}~)$ の解を合わせて，$u=Cx^2-1~~~(C: 任意定数)$ と求まります．

さて， $u=\dfrac{y}{x}$ とおいたことを思い出して，


```math
\begin{align*}
\dfrac{y}{x}&=Cx^2-1,\\
\therefore y &=Cx^3-x~~~(C:任意定数).
\end{align*}
```
---
**② $x=0$ のとき**

$xy^{\prime} = 2x+3y$ に代入することで， $y=0$ を得ます．
また， $x=0$ のとき $y=0$ とは， ① における解 $y =Cx^3-x$ と矛盾ありません．
したがって， ①の解を導くための前提条件 $x\neq 0$ を撤廃するだけでOKです．

---
したがって，本問の微分方程式の解は，任意の実数 $x$ に対して，

```math
y =Cx^3-x~~~(C:任意定数).
```

## 【参考】 Wolframalpha

非常に便利なので皆さん積極的に使っていきましょう．

https://www.wolframalpha.com/input?i=xy%27+%3D+2x%2B3y&lang=ja
ARCore Geospatial Creator for Unityを使って、Googleマップを3Dで眺める

# はじめに
2023年5月10日（現地時間）Google は、開発者向けに主催する年次カンファレンス Google I/O にて世界規模のAR（拡張現実）体験を数分で作成できる Geospatial Creator を発表しました。

ということで、Unityで早速触ってみましょう！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96970/9a676d5b-a6ea-88b5-f1a0-7c75ba154349.png)

## 今日のゴール

https://twitter.com/iwaken71/status/1656688807184601088

# 参考ドキュメント

https://developers.google.com/ar/geospatialcreator/unity/quickstart?hl=ja

# 手順

- 前提
- APIの承認
- 地理空間サンプルアプリをimport


# 前提

- Unity2021.3以上
    - 記事ではUnity2021.3.19f1
   
# APIの承認

- Google Maps Tiles API
- ARCore Geospatial API

## Google Maps Tiles API

- https://console.cloud.google.com/apis/library/tile.googleapis.com?hl=ja
    - にアクセス
    - 有効にする

 ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96970/1ab66c9f-8993-18ee-4d44-c19627eac8cd.png)

## ARCore Geospatial API

https://zenn.dev/tkada/articles/04b44474149130

こちらの記事を参考。

APIキーを控えておきましょう。

Google Maps Tiles APIとARCore Geospatial APIは同じAPI Keyになります。

# Geospatialサンプルアプリをimport

Google ARCore Extensions for AR Foundationをinstallする。

- UnityでPackage Managerを開く
- [Add package from git URL]をクリック
    - `https://github.com/google-ar/arcore-unity-extensions.git`を入力

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96970/6a08ddb6-5c63-48e0-13ad-b09c250d195c.png)

- ARCore ExtensionsのSample「Geospatial Sample」をimport

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96970/31bb45df-bc05-c179-b849-8ca772aef0a1.png)

- ProjectSettings > XR Plug-in Management
    - Androidであれば`ARCore`, iOSであれば`ARkit`を有効にします。
    - ARCore Extensionsを選択
        - `Authentication Strategy`を`API Key`に変更し、先ほど入手したAPIキーを貼り付けます



# 必要な依存関係を追加する

## Cesiumをimport

- Cesiumをimportします
  - https://github.com/CesiumGS/cesium-unity/releases/ にアクセス
  - `com.cesium.unity-1.2.0.tgz` (2023/05/12の最新)をダウンロード
  - Package Managerにて+から **[Add package from tarball...]** を選択
  - 先ほどダウンロードしたtgzファイルを選択

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96970/80c740ac-18dd-6eb7-25f6-33b2f2adc88f.png)

- こちらYesを選択


## Geospatial Creatorを有効

- Switch PlatformでiOS or Androidを選択
- Project Settings > XR Plug-in Management > ARCore Extensions
    - [Geospatial]と[Geospatial Creator]にチェックを入れる
    - [Finish]をクリック


# Map TilesでGeospatial Creator Originを追加する


シーンには「Geospatial Creator Origin」が一つだけ必要です。

- ヒエラルキーから
    - XR > AR Geospatial Creator Originを選択
- AR Geospatial Creator Originを部ジェクトのInspector
    - `Add Cesium Georeference Component`をクリック
- Google Map Tiles API Keyを入力
- Cesium Georeferenceにて
    - 緯度経度を入力

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96970/0cae5e8c-740f-3706-34ab-1fbf888357b7.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96970/d9c57b14-19cd-867f-b7fb-46715051834d.png)

# できた！

https://twitter.com/iwaken71/status/1656688807184601088


# 参考資料: クイックスタート
https://youtu.be/I8tIy4lBrGI
複数の Cloudflare Tunnel (cloudflared) を1つの Windows 内で動かす

# Cloudflare Tunnel (cloudflared) を Windows Server にインストール
1つ目のトンネルは以下の手順・コマンドによってインストールできます。

- [Set up a tunnel through the dashboard · Cloudflare Zero Trust docs](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/tunnel-guide/remote/)
> ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/3716a9f4-57f0-0028-8680-6fc86a7bd978.png)


```bat:
cloudflared.exe service install eyJxxx
```

以下のディレクトリにインストールされます。

```bat:
where.exe cloudflared.exe

C:\Program Files (x86)\cloudflared\cloudflared.exe
```

# 1. 複数のレプリカトンネルを作成する場合
同じトンネル ID・トークンを使って同じコマンドを実行することでレプリカトンネルを作成できます。

```mermaid:
graph RL

subgraph Cloudflare
Tunnel[Tunnel ID]
end

subgraph Windows
Cloudflared-1
Cloudflared-2
end

Cloudflared-1 --> |Connector ID-1|Tunnel
Cloudflared-2 --> |Connector ID-2|Tunnel
```

## レプリカトンネルの作成
レプリカトンネルとは、以下の説明があります。

- [Tunnel availability and failover · Cloudflare Zero Trust docs](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/deploy-cloudflared-replicas/)
> Cloudflare Tunnelは、ユーザーが可用性とフェイルオーバーのシナリオに対応するために、当社のコネクタであるcloudflaredの追加インスタンスを展開することも可能です。これらの一意なインスタンスをレプリカと呼びます。各レプリカは4つの新しい接続を確立し、必要に応じて起点への追加入り口として機能します。各レプリカは同じトンネルを指すようになっています。これにより、単一のcloudflaredがダウンした場合でも、ネットワークが稼働し続けることが保証されます。
> 設計上、レプリカはトラフィック誘導（ランダム、ハッシュまたはラウンドロビン）を提供しません。代わりに、Cloudflareに到着した要求は地理的に最も近いレプリカに転送されます。その距離計算が失敗した場合や接続が失敗した場合は他を再試行しますが、どの接続が選択されるか保証されません。

以下のコマンドを使ってレプリカトンネルを複数 (`Cloudflared2`, `Cloudflared3`) 作成できます

- [Running multiple instances in Windows · Cloudflare Zero Trust docs](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/installation/#running-multiple-instances-in-windows)

https://gist.github.com/kyouheicf/324a852ca913f03b544a04561a35095e

Windows Server 内には複数サービスが作成されます。

```
compmgmt
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/950e4d3e-9567-06a2-2f9b-3e7751f21961.png)

## 確認

Cloudflare Zero Trust ダッシュボードでは、複数のレプリカトンネルが確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/0bd1d635-c603-a91b-6454-6b535549f13a.png) 

# 2. 複数のアカウント向けにトンネルを作成する場合
異なるトンネル ID・トークンを使って異なるコマンドを実行することで複数のアカウント向けにトンネルを作成できます。

```mermaid:
graph RL

subgraph Cloudflare
subgraph Account-1
Tunnel1[Tunnel ID-1]
end
subgraph Account-2
Tunnel2[Tunnel ID-2]
end
end

subgraph Windows
Cloudflared-1
Cloudflared-2
end

Cloudflared-1 --> |Connector ID -1|Tunnel1
Cloudflared-2 --> |Connector ID -2|Tunnel2
```

## 異なるアカウント向けに複数トンネル作成

同様に以下のコマンドを使って異なるアカウント向けにトンネルを複数 (`Cloudflared2`, `Cloudflared3`) 作成できます。

https://gist.github.com/kyouheicf/324a852ca913f03b544a04561a35095e

# 参考

- [Windows における Cloudflared トラブルシューティング - Qiita](https://qiita.com/khayama/items/84ab5685ab8462222034)
- [sc create - Windowsコマンド虎の巻](https://windows.command-ref.com/cmd-sc-create.html)
BardでQiitaの記事を書かせてみた

# 設定登録
Bardが日本語に対応したとのことなので、Bardを使用してみました。
規約に同意したら、すぐに使えるようになりました。
ChatGPTの時と同様にQiitaの記事を書かせてみました。以前のChatGPTの記事は以下の通り。

https://qiita.com/nori-channel/items/01d4d5ce67479523256c

# Bardで記事作成を依頼
以下の通り依頼しました。
```
Qiitaの記事を書いてみて。
```
すると以下の回答が返ってきました。
```
はい、ここにQiitaの記事があります。

タイトル：Bard：Google AIの大規模な言語モデル
Bardは、Google AIの大規模な言語モデルです。テキストの生成、言語の翻訳、さまざまな種類のクリエイティブコンテンツの作成、質問への回答を有益な方法で行うことができます。まだ開発中ですが、次のような多くの種類のタスクを実行することを学びました。

あなたの指示に従い、あなたの要求を慎重に完了するために最善を尽くします。
オープンエンド、やりがいのある、または奇妙な質問であっても、その知識を使用して包括的かつ有益な方法で質問に答えます。
詩、コード、スクリプト、楽曲、メール、手紙など、さまざまなクリエイティブなテキスト形式のテキストコンテンツを生成します。
Bardは、エンジニア、データサイエンティストに役立つツールになる可能性があります。プログラミングのヒントやコツ、データサイエンスのテクニックなどの記事の作成に使用できます。また、Bardを使用して、技術ドキュメントやチュートリアルを生成することもできます。

Bardはまだ開発中ですが、将来がどうなるか楽しみです。エンジニアやデータサイエンティストに価値のあるツールになる可能性を秘めていると信じています。
```
次は以下の命令を与えてみました。
```
Qiitaの記事を「BardでQiitaの記事を書かせてみた」というタイトルで書いてみて。
```
すると以下の回答が返ってきました。
```
はい、どうぞ。

BardでQiitaの記事を書かせてみた
Bardは、Google AIの大規模な言語モデルです。テキストの生成、言語の翻訳、さまざまな種類のクリエイティブコンテンツの作成、質問への回答を有益な方法で行うことができます。

今回は、Bardを使ってQiitaの記事を書くことにしました。最初はどのような記事にしようか迷いましたが、最終的には「BardでQiitaの記事を書かせてみた」という記事にすることにしました。

Bardにどのような記事を書いてほしいかを伝えると、すぐに書き始めました。最初はどのような内容になるのか不安でしたが、Bardは私の意図をよく理解し、期待以上の記事を書いてくれました。

Bardの記事は、以下のような特徴があります。

簡潔でわかりやすい。
有益で役立つ。
誤字脱字がない。
Bardの記事は、Qiitaの記事を書く人にとって、大きな助けになると思います。Bardを使えば、短時間で質の高い記事を作成することができます。

まだ開発中ではありますが、Bardの将来が楽しみです。Bardは、人々の生活をより便利で豊かにしてくれる可能性を秘めていると信じています。
```
# まとめ
BardはChatGPTの時とはまた違った形の記事を書いてくれました。
頼み方で出力結果は変わると思いますが、ChatGPTよりちゃんと指定しないといけない感じでしょうか。ちょっと記事っぽくないですよね。
いずれにせよ面白いツールです。何かの役に立てばと。
RubyKaigi 2023で行ったLTの解説

# はじめに

RubyKaigiとの連動企画ということで、先日行ったRubyKaigi 2023でのLTのセルフ解説をしたいと思います。

## 題材

https://github.com/okuramasafumi/rspec-current.vim

`rspec-current.vim`という、カーソルの位置に応じた`subject`や`context`の内容を返すRSpec用VimプラグインをRubyで書きました。LTではこのプラグインの解説をメインに行いました。

## 実装の解説

プラグインの名前こそ`.vim`ですが、中身はほぼ全てがRubyで書かれています。特に`RubyVM::AbstractSyntaxTree`クラスを使うところが肝で、ASTを取得することでそこから構文要素（例えば`subject { described_class.call }`における"described_class.call"に当たる部分）を取り出すことができます。この際、Ruby3.2で追加された`keep_tokens: true`オプションを利用する必要がありました。

トーク中で流した`node.children.last.children.last.tokens.map { _1[2] }.join`の部分ですが、最初の`node`がsubjectを包む`ITER`ノード、その`children.last`で`SCOPE`ノード、そのさらに`children.last`で`CALL`ノード、そこに`tokens`メソッドを呼ぶことで各要素が配列の配列として返ってきます。各要素はその3番目の要素がトークンの文字列であるような配列であるので、`_1[2]`でそれを取得して`join`すると元のソースコードに近い文字列を得ることができます。このへんの詳細は[金子さんの記事](https://yui-knk.hatenablog.com/entry/2022/11/23/171608)を参照するとわかりやすいです。

## 簡単なの？

発表の中でこのプラグインのようなものを作ることは"Easy"だと言いましたが、実際はどうなのでしょうか。たしかに、実装としてはASTをいじる以外に難しいことはやってはいません。

ただし、実装している最中はASTを手探りするしかありませんでした。各ノードの構造についてはドキュメントが一切なく（意図的なものだと思います）、そのためまずはRubyだけのVimとは無関係なファイルを用意してそこで色々実験して、得られた知見をVimプラグインに適用する形で実装を進めました。

`rubyeval`を用いる方法も、一度気づいてしまえばなんてことはないのですが、最初はRubyからVimに値を返す方法がわかっていませんでした。このへんはVimプラグインを書いたことがないということが大きいですね。
Azure Container Appsを用いてRabbitMQを起動させてみた

# はじめに
どうも、クソ雑魚のなんちゃてエンジニアです。
本記事は我がAzure Container AppsでRabbitMQを立てた際の ___注意ポイント___ を記載していこうかと思う。
我の備忘録程度に書くのであまり期待しないでほしい。

# Azure Container Appsとは？

https://azure.microsoft.com/ja-jp/products/container-apps

「Azureでコンテナを複数立ててアプリを組みたい！でもkubernetesは触ったことあまりないので自信がない」
こういった層に受けるのがこのサービスではなかろうか？

# RabbitMQとは？

https://www.rabbitmq.com/

メッセージングキューの非同期通信オープンソースである。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2036848/e38b4e76-5383-e849-2885-641bb3ead42a.png)
上記コンソールである。

# 環境
2023/05/11での環境である。
## 開発環境
+ Docker 20.10.21
+ terraform 3.55.0
## 本番環境
+ Azure Container Apps
+ Azure Container Registry
+ A Container Apps environment
+ Vnet

これだけ書いとけばもうわかる人にはイメージついてるのではないかと思う。

# 注意ポイント
## 【ポイント1】コンテナイメージ
これはまぁMac使いの人がハマる瞬間ですね。
MSなので`arm64`でイメージを作るのは止めましょう(偏見)。以下のDockerfileのように`amd64`を明示しておけばいいです。
```dockerfile:Dockerfile
FROM --platform=linux/amd64 rabbitmq:3.11.15-management

EXPOSE 5672 15672
```

## 【ポイント2】Ingress設定
これはHTTP通信を行うコンテナ以外のコンテナを動かす際に注意しなければなりません。
redisの`6379`とかもそうですね。
Azure Container Appsでの外部コンテナ間TCP通信を行うには ___カスタムVnetを用意しないといけません。___
外部公開しないのならば別に気にしなくていいのですが、
公開しない設定でもVnet上でコンテナは立てることができるので、どうせならVnetを用意しておいて損はないでしょう。
___まぁキューイングサービスは外部公開しないですけどね。___

以下参考。

https://learn.microsoft.com/ja-jp/azure/container-apps/ingress-overview

## 【ポイント3】TerraformでのIngress設定
ポイント２同様、HTTP通信を行うコンテナ以外のコンテナを動かす際に注意しなければなりません。
現在Azure Container Appsでのコンテナ間通信はTCP通信をサポートしていますがTerraformさんがサポートしてくれてません。

https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/container_app

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2036848/27b48774-e976-f19e-64e0-fd332a58698e.png)
TCPサポートしてくれているところなら以下のように書いてる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2036848/3d999545-f990-2c6f-84db-052ab0a6f3f2.png)

なのでAzure Container AppsをTerraformで管理したいなら、一旦手動(Azure Portal上)で立てて以下のように`data`で取り込むのが良いと思います。
```terraform:container.tf
data "azurerm_container_app" "rabbitmq" {
  name                = "rabbitmq-app"
  resource_group_name = "example-resources"
}
```

## 【ポイント4】Ingressにおける２Port設定
基本的にAzure Container Appsは１コンテナ１Portの解放構成です。
`ターゲットポート`の欄で設定します。
まぁコンテナはプロセスなんで１つの役割でいこうってのもわかります。
でも`rabbitmq`の場合は結構困ったりします。以下ように複数Port使ったりするので。

|Port Number |内容 |
|---|---|
|5672|メッセージの送受信（非暗号化）|
|15672|管理画面コンソールアクセス|

この場合、TCPでのIngress設定をしたときのみの解決策があります。
`公開されたポート`に２つめのPortを開ければいいです。

![S__10985485.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2036848/36f39ada-f4a9-3175-4038-15dfffb3cde3.jpeg)
以下参考

https://learn.microsoft.com/ja-jp/azure/container-apps/ingress-how-to?pivots=azure-cli&tabs=azure-cli%2Cportal%2Carm-template

# まとめ
ただの備忘録デス。
間違ってたら教えてください。
Azure Container Apps何もわからん。
JS, TSで特定の条件に応じた値を可読性高く変数に格納する解

## 概要
特定の条件（ケース）に応じて値を定義、生成する関数を定義する際に、何がベストか、ずっと考えてきた。

## 解
### 変数定義（JS）
```JavaScript
// timeframe = day or night
const timeframe: TimeFrame = ...;
const displayText = {
  day: '昼間です',
  night: '夜です',
}[timeframe];
```

### 変数定義（TS）
```TypeScript
type TimeFrame = 'day' | 'night';
const timeframe: TimeFrame = ...;
const DISPLAY_TEXT_HASH:{ [K in TimeFrame]: string } = {
  day: '昼間です',
  night: '夜です',
}
const displayText = DISPLAY_TEXT_HASH[timeframe];
```

### 変数定義（TS 4.9 or later）

```Typescript
type TimeFrame = 'day' | 'night';
const timeframe: TimeFrame = ...;
const displayText = ({
  day: '昼間です',
  night: '夜です',
} satisfies { [K in TimeFrame]: string })[timeframe];
```

### 関数（JS）
```JavaScript
const generateDisplayTextByTimeframe = (args) => ({
	day: '昼間です',
    night: '夜です'
})[args];
```

### 関数（TS）
```JavaScript
type TimeFrame = 'day' | 'night';
const generateDisplayTextByTimeframe = ((
  baseHash: { [K in TimeFrame]: string }
) => (key: TimeFrame): string => baseHash[key])({
  day: '昼間です',
  night: '夜です',
});
```

### 関数（TS 4.9 or later）
```JavaScript
type TimeFrame = 'day' | 'night'; 
const generateDisplayTextByTimeframe = (key: TimeFrame):string => ({
	day: '昼間です',
    night: '夜です'
} satisfies { [K in TimeFrame]: string })[key];
```

## 三項演算子
```Javascript
// timeframe = day or night
const timeframe: TimeFrame = ...;
const displayText = timeframe === 'day' ? '昼間です' : '夜です';
// 真逆の判定なら分からなくもない

// timeframe = day or afternoon
const timeframe: TimeFrame = ...;
const displayText = timeframe === 'day' ? '昼間です' : '午後です';
// dayではないときに午後なのは違和感がある
```

## if文
```Javascript
// timeframeを変数定義スコープ内で参照している想定
const timeframe: TimeFrame = ...;
const displayText = (() => {
  if(timeframe === 'day') return '昼間です';
  return '夜です';
})();
// 真逆の判定なら分からなくもない

// timeframe = day or afternoon
const timeframe: TimeFrame = ...;
const displayText = (() => {
  if(timeframe === 'day') return '昼間です';
  return '午後です';
})();
// dayではないときに午後なのは違和感がある
```

## switch文
```Javascript
// timeframe = day or afternoon
const displayText = (() => {
  switch (timeframe) {
    case 'day':
      return '昼間です';
    case 'afternoon':
      return '午後です';
    default:
      // @htsignさまよりご教授いただきましたので追記
      // case が漏れている場合はここで型エラーになるので足りないことに気付ける
      const _value: never = timeframe;
      throw _value;
  }
})();
// 分かりやすいが冗長 lint使っているとdefaultが必須
```

## find
```Javascript
// timeframe = day or afternoon
const LIST_TIMEZONES_AND_CHARACTORS = [
  { timeframeKeyName:'day', text:'昼間です' },
  { timeframeKeyName:'afternoon', text:'午後です' },
]
const displayText = LIST_TIMEZONES_AND_CHARACTORS.find( item => item.timeframeKeyName === timeframe )?.text;
// オーバーキル
```

## 総括
よって **解** が適切ではないだろうか。

---
> Special thanks：同僚のTさん
Jestでライブラリからwindow.URL.createObjectURLのエラーが出たときの対処法

Jestでフロントのテストを書いている際に、下記のようなエラーが出ました。

![スクリーンショット 2023-05-11 232936.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/295930/b3504b6e-3e01-c9c3-aaaa-9c3497a3fc8e.jpeg)

調べると、モックを使って解消する方法は簡単に見つかると思いますが、
今回はライブラリが引っかかってしまうケースです。

なので、こういう場合は**対象のテストファイルではなく**
セッティングファイルに記述します。

reactなら、**src配下にあるsetupTests.ts**です。

```src/setupTests.ts
// window.URL.createObjectURLを呼び出すが、node.js上では利用できないのでモック関数と追加する
window.URL.createObjectURL = function () {
  // 型に対応するためにstringを返す
  return '';
};

```

他にもwindow.HTMLCanvasElementでもテスト時にエラーが出ましたが、
同じようにsetupTests.tsにモックを作って解消できます。
10日間継続して分かった技術記事を毎日書くことのメリット・デメリット

# はじめに
5月2日から11日まで毎日技術記事を投稿してきました。
今回は、10日間継続したからこそ分かる**技術記事を書く習慣を身に付けるメリット・デメリット**を紹介します。

# メリット
## いつか触ろうと思っていた技術を学ぶきっかけになる
「`R.swift`を使った文字列のハードコーディング対策」, 「お問合せとして、アプリ内でメーラーを立ち上げる」といった分野に興味はあったのですが、手は出していませんでした。

しかし、**毎日技術記事を投稿しなければならない**という制約によって、上記の技術に触るきっかけになりました。

:::note info
参考: 実際に書いた記事
[【Swift】R.swiftを使った文字列のハードコーディング対策](https://qiita.com/kasiwa/items/7c726a9007adcff3fadb)
[【Swift】アプリ内でメールを送信する](https://qiita.com/kasiwa/items/5253b90b0d8417ed68e9)
:::


## 他人の技術記事により興味を持つようになる
自分が技術記事を書くことによって、**iOSの最新技術を試す**, **難しい技術をわかりやすく説明する**といったことを行っている人をより尊敬するようになります。

読み手のことを考え、`構成`・`技術内容`・`書き手の視点`の要素が詰まっている記事は本当に素晴らしい！

# デメリット
## 毎日書くと、思ったより時間を取られる
私は10日間、簡単な技術記事を投稿していませんが、以下の流れで執筆していました。
**①興味のある技術を思い出す・探す**
**②ローカルで試す**
**③備忘録として記事に残す**

上記によって、1-2時間は時間を取られてしまいます。
**毎日投稿すると、興味はあるけれども、ローカルで試すのに長時間がかかってしまう分野**に手を出せなくなってしまうデメリットがあります。

# 今後どうするか？
**2日に1回程度の頻度**で投稿していきます。

デメリットのところに記載した通り、**興味があっても、長時間かかってしまう分野に億劫になってしまいました。**
そのため、技術記事を投稿するのをやめるのではなく、**長時間かけて試したい技術を深ぼれる投稿頻度を探していきます。**

# おわりに
自分の中で`いつか触ろうと思っていた技術を学ぶきっかけになる`が技術記事を書く上で一番のメリットだと思います！！！

毎日書く必要はないですが、技術記事を備忘録として残す習慣を身に付けましょう！
自作アプリpart1

# 記録用
自作アプリを制作するにあたり、どういったことをしたのか覚えておくために、記録しておく。

参考
[【rails環境構築】docker + rails + mysql で環境構築（初心者でも30分で完了！）](https://www.youtube.com/watch?v=Fq1PH0Gwi8I&t=1264)
[[Rails] DockerでRails + MySQLの開発環境をつくる手順](https://qiita.com/jshimazu/items/ba13ce87dfdb11e2d1d9)
[DockerでMysqlへのdb:createに失敗する](https://teratail.com/questions/285018)

# dockerでの環境構築
アプリのディレクトリを作るVScodeで開き、そこのターミナルに記述
```
touch Dockerfile docker-compose.yml Gemfile Gemfile.lock .gitignore
```
主に5つのファイルが必要。

### Dockerfile
```Dockerfile
FROM ruby:3.2.2
#使用するイメージとバージョン
RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs
#Railsで開発する時に必要。-qqはエラー以外何も吐かない。-yは全部yesで実行。

RUN mkdir /myapp
WORKDIR /myapp
#作業ディレクトリの設定

COPY Gemfile /myapp/Gemfile
COPY Gemfile.lock /myapp/Gemfile.lock
#左のホスト側を右のコンテナ側にコピー

RUN bundle install
COPY . /myapp
#ホスト側のディレクトリとか全部をコンテナ側にコピー
```

### Gemfile
```
source 'https://rubygems.org'

ruby '3.2.2'

gem 'rails', '~> 7.0.4.3'
```
**Gemfile.lockは空**

### docker-compose.yml
```ruby
#バージョンを書くのは現在では意味がないらしい
services: #アプリケーションを動かすための各要素をserviceと呼ぶ。webとdbと名づける。
  db:
    image: mysql:latest
    ports: #コンテナ内のポート番号をホストの番号にマッピングするという意味。
           #コンテナ内のwebサーバにhttp://localhost:3306でアクセスできるようになる
      - "3306:3306"
    volumes: #データを永続化するためにある。
      - ./.data/db:/var/lib/mysql
    environment:
      MYSQL_USER: root #←ここがエラーの原因
      MYSQL_ROOT_PASSWORD: password
  web:
    build: .
    command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
#pidはプロセスIDのこと。server.pidにpidが書かれているとサーバーが起動中と判断されてしまう。
    volumes:
      - .:/myapp
#dockerとローカルのディレクトリを同期するように設定。実行後、ローカルにも同様のファイルが生成される。
    ports:
      - "3000:3000"
    depends_on:
      - db  
#依存関係を示している。db→webへと起動する。
```

ここまで書いたら**コマンドを実行**

runコマンドではimageも構築から、コンテナの構築・起動までしてくれる。この時点で上記ファイルで書いた、.:/myappによりローカルに同様のファイルが生成される。
```
docker-compose run web rails new . --force --database=mysql --skip-bundle
```
コマンドを実行後はいろいろなファイルが作られたり、Gemfileが更新されているのでビルドする。
この時点でdatabase.ymlを書き換えておかなければならないところがある。
```
database.yml

default: &default
  ・
　　　　・
  username: root
  password: password ←ここにdocker-compose.ymlで指定したパスワードを入力
  host: db ←localhostからdocker-compose.ymlの最下で書いたdbを記入
```
ローカルのファイルを更新した時はbuildを忘れずに。
```
docker-compose build
```
その後にコンテナを起動する。
```
docker-compose up
```
すると**エラー**が出た
```
ActiveRecord::DatabaseConnectionError
There is an issue connecting with your hostname: db.
Please check your database configuration and ensure there is a valid connection to your database.
```
**解決策**
参考の動画の通りに、docker-compose.ymlのdbに**MYSQL_USER: root**を記述していたが、rootは指定しなくても作られるらしいので、別のユーザー名を指定するか、いらないなら削除しておかなければならないらしい。
[DockerでMysqlへのdb:createに失敗する](https://teratail.com/questions/285018)

その後、別のエラーが出るが、それはデータベースがないというエラーなので作成する。
```
docker-compose run web rails db:create
```
再レンダリングすると、ローカルでrailsを開くことができる。
FauxPilotを試してみます

# はじめに
GitHub CopilotのOSSクローン[FauxPilot](https://github.com/fauxpilot/fauxpilot)を試してみます. WindowsのWSLにインストールしてみます.

## 必要なコンポーネント
- Docker
- Docker Compose >= 1.28
- NVIDIAのGPU
    - 2GB, 7GB, 13GB, 32GBのモデルがあり, それぞれのサイズ分のGPUメモリが必要
- nvidia-docker
- curl
- zstd

# セットアップ
WSLはインストール済みとします. 私はDebian 11を使っています.
次のコンポーネントインストールそれぞれは, 公式の手順で大丈夫だと思います.

## CUDA Toolkit
CUDAのドライバが必要なので, ***Windows側で***ツールキットをインストールします.
[CUDA Toolkit](https://developer.nvidia.com/cuda-toolkit)

# Docker
apt経由だと古いバージョンなので, 本家からパッケージをインストールします.

[Docker - Install Docker Engine on Debian](https://docs.docker.com/engine/install/debian/)

# NVIDIA Container Toolkit
`nvidia-docker`をインストールします.
このNVIDIAの拡張をインストールしないとGPUが見つけられず立ち上がりません.

[NVIDIA Container Toolkit - Installation Guide](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/install-guide.html)

# Faux Pilot
ツール類をインストールして,
```bash
$ sudo apt install -y git curl zstd
```

FauxPilotをインストールします.
```bash
$ git clone https://github.com/fauxpilot/fauxpilot.git
$ cd fauxpilot
$ ./setup.sh
```

モデル選択以外はエンター連打でいいと思います.

<details><summary>setup.sh</summary><div>

```
Checking for curl ...
/usr/bin/curl
Checking for zstd ...
/usr/bin/zstd
Checking for docker ...
/usr/bin/docker
Enter number of GPUs [1]: 1
External port for the API [5000]:
Address for Triton [triton]:
Port of Triton host [8001]:
Where do you want to save your models [/home/taqu/fauxpilot/models]?
Choose your backend:
[1] FasterTransformer backend (faster, but limited models)
[2] Python backend (slower, but more models, and allows loading with int8)
Enter your choice [1]: 1
Models available:
[1] codegen-350M-mono (2GB total VRAM required; Python-only)
[2] codegen-350M-multi (2GB total VRAM required; multi-language)
[3] codegen-2B-mono (7GB total VRAM required; Python-only)
[4] codegen-2B-multi (7GB total VRAM required; multi-language)
[5] codegen-6B-mono (13GB total VRAM required; Python-only)
[6] codegen-6B-multi (13GB total VRAM required; multi-language)
[7] codegen-16B-mono (32GB total VRAM required; Python-only)
[8] codegen-16B-multi (32GB total VRAM required; multi-language)
Enter your choice [6]: 4
```
</div></details>

```bash
$ sudo bash ./launch.sh
```

成功すると, `http://localhost:5000`にサンプルページが表示されるはずです.

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27065/669fd4ff-fa3b-cc51-6850-9a6627a1bd7f.png)

# Visual Studio Code Plugin

VSCode用のプラグインが公開されているので, Copilotのプラグインを流用するよりいいでしょう ([Fauxpilot](https://marketplace.visualstudio.com/items?itemName=Venthe.fauxpilot)).

# まとめ
よくインフェランスに時間がかかってサーバからの応答がなくなるので, つよつよサーバが必要だと思います. 問い合わせ頻度も設定する必要があると思います.

補完それ自体は, 逐一本質でないコメントを書くのが面倒くさいです. 頭でロジックを考えながら, 同時にインターフェイスを書いていく人間には煩わしいだけなのかなと思いました. リファクタリング提案や, よりよいフォーマッタやリンタになったらいいなと思います.
【Xcode エラー】「Failed to prepare device for development」【解決方法】

実機でビルドしてみたら


「Failed to prepare the device for development. This operation can
fail if the version of the OS on the device is incompatible with the installed version of Xcode. You may also need to restart your Mac and device in order to correctly detect compatibility.」

といったエラーが発生

![スクリーンショット 2023-05-11 21.49.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359201/b90370ff-7e9c-483c-6ea8-c5fec022c8a3.png)

↓

「Failed to prepare device for development」で検索

↓

《参考記事》【Xcode】「Failed to prepare device for development」の
エラーが発生して iPhone 実機ビルド・デバッグができない事象【備忘録】
https://negichou.com/troubleshooting-xcode-failed-to-prepare-device/

を読む。

①iPhone の再起動　②Xcode の再起動　③Mac の再起動　を試す。

↓

上手くいかず。

↓

Mac のバージョンを最新にする

↓

上手くいかず。

↓

Xcode のバージョンを最新にするために他の記事に載っていたAppStoreのアップデート欄からアップデートする方法を試そうとするもできず。

↓

「Xcode アップデート できない」で検索。

↓

《参考記事》【図解】Xcodeのアップデートできない症状への対策【初心者OK】
https://www.kyougokumakoto.com/2019/10/xcode-not-download.html

を読む。

↓

AppleのDeveloperサイトから直接ダウンロード。

↓

解決。

【今回の経験から学んだこと】

Xcodeのバージョンをアップデートする際は
「App Storeから」アップデートではなく
「公式サイトから直接」アップデートするのが一番確実。

（App Storeからだと最新のバージョンにアップデートできない時があるので。）
Astro × AWS(s3,CloudFront)でドキュメントルート以外に遷移した際、AccessDeniedになりハマった件

## はじめに
タイトルの件で少し詰まったので、同じ問題で困っている方への一助となれば幸いです。

## 状況
最近流行り？のSSGである[Astro](https://astro.build/)(今回利用したテンプレートは[astro-paper](https://github.com/satnaing/astro-paper))をs3にデプロイした際に、s3で指定したドキュメントルート(index.html)ではページが表示できているのですが、他のページに移動した際にAccessDeniedとなり、期待する動作ができませんでした。


## 原因
### **遷移した先のパスが違いました。**
そのため別途設定が必要でした。

Astroではbuildした際に`/dist`配下に静的ページ表示用のhtmlファイルが出力されるのですが、そちらのディレクトリ構成を把握できていませんでした。
`/dist`以下のディレクトリ構成は以下のようになります。
```
├── index.html
└── posts
    ├── 1
    │   └── index.html
    ├── hazimemashite
    │   └── index.html
    └── index.html

※対象の部分を抜粋
```
今回意図する動作として最上位の階層の`index.html`から`hazimemashite`ディレクトリ内の`index.html`のページへと遷移したいといった感じです。

実際に遷移した際のパスは
`任意のドメイン名(またはCloudFrontのドメイン)/posts/hazimemashite/`
となっており、`hazimemashite`ディレクトリ内の`index.html`を参照できていませんでした。

実際に
`任意のドメイン名(またはCloudFrontのドメイン)/posts/hazimemashite/index.html`
とすると期待するページを表示できました。

しかし遷移するたびに手動で`index.html`を付け足すのはユーザビリティが最悪なので(笑)、、、

[Astroのドキュメント](https://docs.astro.build/ja/guides/deploy/aws/#cloudfront-functions-setup)に以下の方法がありました。

## 解決方法
### CloudFront Functionsの実装でした。

```javascript:astrofunction.js
function handler(event) {
  var request = event.request;
  var uri = request.uri;

  // Check whether the URI is missing a file name.
  if (uri.endsWith('/')) {
    request.uri += 'index.html';
  } 
  // Check whether the URI is missing a file extension.
  else if (!uri.includes('.')) {
    request.uri += '/index.html';
  }

  return request;
}

※ ドキュメントより引用
```

コードに関しては説明するまでもないですが、ユーザより`/`または`.`でパスのリクエストがあった際に参照するファイルは`/`または`.`以降の`index.html`のファイルにするよとういうものです。

私はTerraFormでAWSリソースを管理していたため以下のように実装を進めました。

```terraform:cloudfront.tf
# CloudFront Functionsリソースを定義
resource "aws_cloudfront_function" "astrofunction" {
  name    = "redirect"
  runtime = "cloudfront-js-1.0"
  comment = "redirect function"
  publish = true
  # 今回astrofunction.jsは./src内に配置しました
  code    = file("./src/astrofunction.js")
}

resource "aws_cloudfront_distribution" "cozycozy-blog" {
    〜
  default_cache_behavior {
    # default_cache_behavior内に関数を付与するコード追加
    function_association {
      event_type   = "viewer-request"
      function_arn = aws_cloudfront_function.astrofunction.arn
    }
  }
    〜
}
```

以上のコードを記述後`terraform apply`を問題なくリソースを更新後、
`任意のドメイン名(またはCloudFrontのドメイン)/posts/hazimemashite/`
    のパスで無事、サブディレクトリのページに遷移できました！

## 終わりに
[Astroのドキュメント](https://docs.astro.build/ja/guides/deploy/aws/#cloudfront-functions-setup)は各プラットフォームごとにデプロイ方法が丁寧に描かれており、初心者に優しいなと感じました。

また今回実際に作成したページが[こちら](https://cozycozy.blog/)になります！
(スタイルとかレスポンシブがイマイチな気がするので勉強して直したい、、、)

読んでいただいた方ありがとうございました！

## 参考
https://docs.astro.build/ja/guides/deploy/aws/#cloudfront-functions-setup

https://qiita.com/yoshuuua/items/08f6df850ef36ac41d4d
スマレジプラットフォームAPIとRenderを使用したLINEシフト通知を作った話

# はじめに
弊社では従業員のシフト通知を手作業でLINEで通知しておりました。

Renderのサーバーを使用した自動デプロイとスマレジ・プラットフォームAPIを使用したシステムを構築したのでシェアさせてください。

なお、LINE通知にはLINE Notifyを使用しています。

# 構成図
![シフトbotの構成図.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/608795/20ff5ac8-e16a-8f4c-602d-8e522ff84180.png)

# 解決した課題・ポイント
webhookでの通知後、毎朝従業員が出勤前に確認することを自動化することができました。

[Render](https://render.com/)を使用したことにより、WebアプリをGitHub連携で簡単にデプロイでき工数をかけず実装できました。


デプロイをプレビューできるのでとてもわかりやすいですね。
![スクリーンショット 2023-05-11 22.36.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/608795/67788416-11c9-8c1a-2df9-6c0c7f323d0b.png)

タイムゾーンの設定がUTCのみだったのでそこはすこし使いずらい点かもしれません。

## 最後に

まだまだ日常業務で困った課題をシステムで解決していきます。
ピザが最高においしいのでぜひ弊社に食べにいらしてください！！

都内で5店舗を経営する株式会社アキッチョ
[アキッチョHPはこちら](https://akiccio.com/)
Flutter RiverpodのProviderのドキュメントメモ

## Providerとは
Riverpodにおいて、変更を通知することができる状態管理のためのクラス。"ref"というオブジェクトを介して、状態をProviderの内外で参照することができる。

## refのwatch, listen, readの違い
### サンプルコード
```dart
class Person {
  final String name;
  final int age;

  Person({required this.name, required this.age});
}

final personProvider = StateProvider((ref) => Person(name: "nemo", age: 15));

class TopPage extends ConsumerStatefulWidget {
  const TopPage({Key? key}) : super(key: key);

  @override
  ConsumerState<TopPage> createState() => _TopPageState();
}

class _TopPageState extends ConsumerState<TopPage> {
  @override
  Widget build(BuildContext context) {
    final Person person = ref.watch(personProvider);

    ref.listen(personProvider, (previous, next) {
      print('previous: $previous, next: $next');
    });

    return Column(
      children: [
        Text(person.name),
        ElevatedButton(
          onPressed: () {
            ref.read(personProvider.notifier).state = Person(name: "nemo", age: 16);
          },
          child: const Text('Increment'),
        ),
      ],
    );
  }
}

```
以上が、ref.watch, listen, readの全てを盛り込んだコード。

### watch
ref.watchはProviderの状態を監視し、変更が起こった際にWidgetの再レンダリングをする。

サンプルコードであれば、personProviderが持っているPersonクラスの値が更新されたタイミングでTopPageの再レンダリングを行う。

Providerから生えているselect関数を使うと、この再レンダリングをより効果的なタイミングで行うことができるようになる。サンプルコードを考えると、Textとして表示しているのは、Personのnameプロパティの値だけであり、personProviderの状態のageプロパティだけが変更された場合は再レンダリングを行うのは不必要である。

そのような場合は、サンプルコードのref.watchを行っている行のコードを

```dart
final Person person = ref.watch(personProvider.select((person) => person.name));
```

のようにしてあげることで、nameプロパティの値が変更された時のみ再レンダリングを走らせるようにすることができる。

### listen
ref.listenはref.watchと同様に、Providerの状態を監視する。しかしref.watchと違い、状態の更新を検知したときに再レンダリングではなく、引数で渡したCallbackの処理を行う。

第一引数と第二引数で、それぞれ一個前の状態と最新の状態の両方を取得することができる。

ref.listenは主に、Snackbarの表示やエラーダイアログの表示などに使用される。

### read
ref.readは1番単純に、Provider状態を取得する。watchやlistenのように監視することはなく、readを行った時点での状態をそのまま取得する。

## 注意点
基本的にはref.readは使わず、ref.watch, listenを使用するのが好ましい。ref.watch, listenでは実現不可能なケースにのみref.readを使うべきである。

## 参考文献
https://riverpod.dev/docs/concepts/reading#using-refread-to-obtain-the-state-of-a-provider
Misskeyアップデートに関する備忘録

お久しぶりです
個人的なMisskeyのアップデート関連の備忘録です

## Misskey本体のアップデート

正直ドキュメントあるんでこれを読みながらすれば問題無いとは思いますが
https://misskey-hub.net/docs/install/manual.html#misskey%E3%81%AE%E3%82%A2%E3%83%83%E3%83%95%E3%82%9A%E3%83%86%E3%82%99%E3%83%BC%E3%83%88%E6%96%B9%E6%B3%95

動かしている上で普段のアップデートではこの作業をすれば良さそうです。過信はしないでね
```
git pull
NODE_ENV=production pnpm install --frozen-lockfile
NODE_ENV=production pnpm run build
pnpm run migrate
sudo systemctl restart <systemdで登録しているmisskeyのサービス名>
```

## Nodeのバージョンアップ
リリースノートにNode.jsのバージョンアップが必要の場合、使っているNodeのバージョン管理ツールによって異なりますが、`n`を使っている場合は基本安定版を入れるコマンドだけで大丈夫です。
```
n stable
```

## pnpmのバージョンアップ
13.12.x → 13.13.xにアップデートしたときに詰まったので。
```
npm install -g pnpm
```
`NODE_ENV=production pnpm install --frozen-lockfile`でエラーになったので、pnpm起因というは結構早く気づけたんですが、情報があまりなく苦戦しました…

## cronを使って証明書を自動で更新
`sudo su -`でrootユーザーになった後、`crontab -e`でcrontabファイルを編集します
そして以下のように書き換えます
```
35 4 * * * systemctl stop nginx && certbot renew; systemctl restart nginx 
45 4 * * * reboot
```
意味合いとしては、毎日4:35にnginxをストップし、証明書の再発行します
証明書の再発行が完了したら、nginxを再起動します
また起動し続けるとエラーが起きてしまう時期があったため、毎日4:45にサーバー自体の再起動をしています

## 証明書の更新(非推奨)
Let's Encrypt を使っている場合、定期的に証明書を更新する必要があります。
cronなどで設定するのがおそらく楽なんでしょうが、めんどくさいので私の鯖では手動で更新をかけています。
正直オススメはしません。Cloudflareやcronなどが使えるのであればそっちでやったほうが早いです

### 現在動かしているサービスを止める
```
sudo systemctl stop <systemdで登録しているmisskeyのサービス名>
sudo systemctl stop nginx
```
### 証明書を更新する
```
sudo certbot certonly -d <サーバーのドメイン> -m <連絡を飛ばしてほしいメールアドレス> --standalone --dry-run
--問題無ければ以下のコマンドを実行--
sudo certbot certonly -d <サーバーのドメイン> -m <連絡を飛ばしてほしいメールアドレス> --standalone
```

本当はcloudflare使いたいけど、linodeの相性が悪いので…
cloudflareがダウンした場合でも使えるのは魅力ですが


## 履歴
2023/5/11   初版公開
2023/6/17   pnpmバージョンアップについて追記
2025/5/3    証明書の更新を自動化していたことを失念していたため、備忘として記載
個人的 VS Code おすすめ設定・拡張機能

# はじめに
自分は就職して 1 年が経過しました。業務では主に、C/C++、Python を書いています。実際の業務で利用している中で役に立った VSCode の拡張機能を紹介したいと思います。また、特定の言語に対する拡張機能の紹介ではないので様々な人に見てもらい役に立てれば幸いです。

# おすすめ設定

## 文字エンコーディングの自動推測

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/137295/c4f22ace-75d4-4fb5-7dd9-16c20cb6f844.png)

この設定を有効にすることで UTF-8 の環境で Shift-JIS のファイル、Shift-JIS の環境で UTF-8 のファイルを開いた際に自動でエンコードを推測して文字化けを直してくれます。

# おすすめ拡張機能

## Trailing Spaces

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/137295/2433df71-f87a-ef95-4563-f93f59d344b0.png)

コード内に存在する末尾の半角スペースを可視化してくれます。単語と単語の間の半角スペースは可視化されないので、末尾の不要な半角スペースのみ可視化してくれます。末尾の半角スペースが残っていることでバグが発生するケースはほとんどありませんが、やはりソースコード内の不要なものはメンテナンス性を下げるので可視化する拡張機能を導入しています。

## Code Spell Checker

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/137295/a19b1b30-5f69-7c3d-baa9-8cd18b63dac5.png)

コード内で書いた英単語のスペルをチェックしてくれます。複数形や過去形、アンダーバー区切りの英単語に対応してくれています。変数にそれなりの意味をもつ英単語を選ぶ際に、短くて知っている単語などは検索せず書こうとするケースが多いと思います。もちろん、スペルミスしないのが良いと思いますが、やはり人間は忘れ、間違える生き物なのでスペルチェックをしてくれる拡張を導入しています。

## indent-rainbow

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/137295/137de3ea-8358-14ee-80b0-8a8d0132849f.png)

ソースコード内のインデントのネストの段階を色ごとに可視化してくれます。言語によってはインデントにくずれがあってもエラーやバグにならないものもありますが、こちらもメンテナンス性を下げないように導入しています。
【RubyKaigi2023】型でIRBの補完がパワーアップ！【katakata_irb】

# はじめに

RubyKaigi2023 1日目に参加しました！さまざまな発表がありましたが、特に僕がすごいと思ったセッションを紹介します！！

本記事は RubyKaigi 1日目のセッションの１つである、`@tompng` さんによる発表 「Power up your REPL life with types」の概要とその中で出てきた `katakata_irb` gem を試しに使ってみた記事です。

# セッション概要

https://rubykaigi.org/2023/presentations/tompng.html#day1

発表のタイトルは「Power up your REPL life with types」です！タイトルの通り、型(RBS)でREPL(IRB) をパワーアップさせる、という発表です。

## IRBの現状

ruby を対話形式で実行できる irb ですが、現状でも以下のように補完してくれます。(配列のメソッドが候補に出てきます。)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325392/f4fbd132-dc4f-1876-60d3-162cf3dc6369.png)

上記の例では結構いい感じの補完候補が出ていそうです。しかし、正規表現で実装されているため場合によっては誤った候補が出たり、メソッドチェーンの場合の補完の候補が微妙だったりします。

そこで、型の情報を使っていい感じに補完ができたらいいよね？と言うことで発表者の `@tompng` さんが実装したのが `katakata_irb` gem です！すごい！！

## `katakata_irb` gem

https://github.com/tompng/katakata_irb

gem の名前は「型」と「カタカタ(擬音)」が由来しているらしいです。

`katakata_irb` が型の情報を使っていい感じに補完する仕組みを簡単に説明します。

1. 不完全なコードから、構文木を作成し
    - irb 自体がインデントの深さなどの情報を持っているので、その情報を使って足りないカッコや `end` などを補完します。
    - 補完したコードを Ripper でパースし構文木が得られます。
2. RBSでメソッドチェーンを評価
    - 構文木の種類に応じて、カーソルの位置にはどんな型が来るのかをパターンマッチして求めます。
    - 型がわかるので、適切な補完候補が得られます。
3. IRBの補完を上書きする
    - 型情報から補完候補が得られたので、IRBの補完を上書きします。

このような流れで、メソッドチェーンの場合も適切な補完候補が得られるようです。なんと、上記の実装が約150行で実装できているらしいです！

さらに、もっと強力な補完がしていですよね？ということで、以下も実現されているらしいです！

- 変数のスコープや、if の結果に応じて型が変わる場合も適切な型から補完候補が得られる
- each や map など、ブロック内の変数も、型変数を考慮して適切な候補が表示される
- sig ディレクトリやrbs_collection.taml による型情報が仕様される

# 実際に使ってみた

使い方は、 `katakata_irb` をインストールして、irb上で require するだけです！

```ruby
λ irb                     
irb(main):001:0> require "katakata_irb"
=> true
```

## メソッドチェーン

### `katakata_irb` を使わない場合

`n.abs` は `Integer` ですが、以下のように、Integer 以外のメソッドも表示されてしまっています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325392/83361736-a306-7a4d-0bfa-dd699963e194.png)

### `katakata_irb` を使った場合

`n.abs` が `Integer` 型と判定され、 `Integer` のメソッドのみ表示されています！！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325392/f99e1040-f1a1-d541-cf52-b24961eb8508.png)

## if の条件によって型が変わる場合

以下のように、if の結果によって `Integer` や `String` になる場合はどうなるでしょうか？

```ruby
Proc.new do
  a = 1
  if a > 0
    a = "hoge"
  end
  a. <- ここを補完する
```

### `katakata_irb` を使わない場合

先ほどと同様に、適切な候補を出すことができていません...

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325392/11ec68b1-1e81-8bf1-c303-b32cf6a936fc.png)

### `katakata_irb` を使った場合

`String` のメソッド `ascii_only?` や `Integer` のメソッド `abs` が表示されています！！！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325392/01111558-71be-2785-0ff5-b99d6720793a.png)

# さいごに

やはり型の力は偉大です！Rubyが型でこれからもっと便利になっていってほしいですね！

発表者の `@tompng` さん本人による以下の記事も読みましょう！！

https://qiita.com/tompng/items/bca1e40976ec4f2a71c8
【personal-study】binary-tree-first-search

this is binary tree at personal study.
this binary tree blog theme handle first serach.


```mermaid
graph TD;
    1-->2;
    1-->3;
    2-->4;
    2-->5;
    3-->6;
    3-->7;
```

search pattern exist three way.
so three way is preorder,inorder and postorder


```python
class Tree:
    def __init__(self,data,left=None,right=None):
        self.data = data
        self.left = left
        self.right = right
```


### Preorder
print root data, then move left subtree, move right subtree.

#### output
1→2→4→5→3→6→7

```python

def dfsPreorder(root):
    if root is None:
        return
    print(root.data)
    dfsPreorder(root.left)
    dfsPreorder(root.right)
```
### Inorder
move left subtree, then print root data, move right subtree.

#### output
4→2→5→1→6→3→7

```python
def dfsInorder(root):
    if root is None:
        return
    dfsInorder(root.left)
    print(root.data)
    dfsInorder(root.right)
```

### Postorder
move left subtree, then move right subtree, print root data.

#### output
4→5→2→6→7→3→1

```python
def dfsPostorder(root):
    if root is None:
        return
    dfsPostorder(root.left)
    dfsPostorder(root.right)
    print(root.data)
```


## references


https://www.udemy.com/course/50-problems/
pullしたらPlease commit your changes or stash them before you merge. Abortingエラー

本番環境でpullしたときに出たエラーです。

# エラー内容
SSHに接続して下記を実行すると、、
```
$ git pull origin main

Please commit your changes or stash them before you merge.
Aborting
```
「マージする前に変更をコミットするか、隠してください」とのことです。

# 解決法
**結論：強制的にマージする**
リモートの最新状況に合わせに行くような強制マージなので、問題になることは無いはずです。
もちろん、pullしようとしていたmainが正しい前提。

```
// リモートの最新を取ってくる
$ git fetch origin main

// mainを、リモート追跡のmainに強制的に合わせる
$ git reset --hard origin/main
```
でOKです。


参考：

https://qiita.com/15grmr/items/433ee3b47828aaad32a8
ST7735(パラレル)で遊ぶ1(1か月から3か月の長期記事)ATM0177B5

目的
秋月で売っているST7735で
実用的な物を作る
完成するか未定

![o_cop513.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/d43f3a7d-ad58-b7bc-ad13-86ee2a48a2c5.jpeg)

![o_cop514.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/28c9d811-be89-cf9a-f1bc-cb8f46ecbe08.jpeg)

![o_cop515.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/1446def4-818c-c4c5-3cdf-96eb8d11c2d1.jpeg)
ベルヌーイ試行の平均が従う分布_その2

## はじめに 

A/Bテストについての書籍で気になる点があったので確認するためのメモです。
以下が気になった点です。

> 2標本t検定では、類似した分布からなる2つの変数の差を見ているため、正規性の仮定がもっともらしいものであるために必要なサンプルサイズが少なくなる傾向がある。これは、介入群とコントロール群が等しいトラフィック配分を持つ場合は特に当てはまる(Kohavi,DengandLongbotham,etal.2014)。
Ron Kohavi,Diane Tang,Ya Xu,大杉 直也. A/Bテスト実践ガイド　真のデータドリブンへ至る信用できる実験とは (Japanese Edition) (p.316). Kindle 版. 


【参考】前回の考察


https://qiita.com/TSYK11361/items/efe5e0439548c7d2fecd  

## メモ 

### RECAP
1000人のうち、1名が商品を購入する時、0.1%の購入率だと表現することができます.  
ここで、1000人のそれぞれが同一の確率で購入すると仮定します. 
すると、成功確率pのベルヌーイ試行に従う確率変数を1000回抽出した標本平均はざっくりいってpである確率が最も高いということになります.  
この抽出作業をp=0.001とした上で、1000回繰り返しヒストグラムにしたのが下図です. 

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1276668/4832cf3c-a4ca-dd70-a3bd-f29a4e78e882.png)

### 本稿メモ
さて、再度引用文です.  

> 2標本t検定では、類似した分布からなる2つの変数の差を見ているため、正規性の仮定がもっともらしいものであるために必要なサンプルサイズが少なくなる傾向がある。これは、介入群とコントロール群が等しいトラフィック配分を持つ場合は特に当てはまる(Kohavi,DengandLongbotham,etal.2014)。
Ron Kohavi,Diane Tang,Ya Xu,大杉 直也. A/Bテスト実践ガイド　真のデータドリブンへ至る信用できる実験とは (Japanese Edition) (p.316). Kindle 版. 

これを実際に自分の目で確認しよう、というのが本稿の趣旨です.  
Bingに聞いたところ以下のようなコードを書けば描画できるみたいです.  
その描画結果は下段です.  



```python
'''
確率pのベルヌーイ試行をそれぞれn回繰り返し
標本平均をとる作業を2回行い
それらの数値の差をとったものの分布を描画するには
PythonのNumPyとMatplotlibライブラリを使うことができます。
以下は、この作業を行うためのコード例です。
'''
import numpy as np
import matplotlib.pyplot as plt

n = 1000  # 試行回数
p = 0.001 # 成功確率
m1 = np.random.binomial(n, p, size=1000) / n  # 標本平均1
m2 = np.random.binomial(n, p, size=1000) / n  # 標本平均2

plt.hist(m1 - m2, bins=50)
plt.show()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1276668/ea1f1153-b1c7-6d42-4112-f55a02de1481.png)

確かに、正規分布に似た形状になっています.  
なお、著書内ではt検定のための検定統計量は上述の計算結果をさらに標準偏差で割り、標準化します. 

t検定統計量はnが30程度あれば正規分布で代用しても差し障りなかったような記憶があります.  
私にとってはt分布, 正規分布いずれなのかそもそも判別がつきませんが、そもそもベルカーブな形状なのか一度見てみましょう.  

n = 16で描画してみたいと思います.  
n = 16で自由度30になるというあいまいな記憶をもとにしています.  

```python
import numpy as np
import matplotlib.pyplot as plt

n = 16  # 試行回数
p = 0.001  # 成功確率
m1 = np.random.binomial(n, p, size=10000) / n  # 標本平均1
m2 = np.random.binomial(n, p, size=10000) / n  # 標本平均2

plt.hist(m1 - m2, bins=50)
plt.show()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1276668/36c1fbf1-a1f7-6781-d006-e4641873d9ff.png)

大変素晴らしい形状になりました.  

## まとめ 

- 以下引用については、特に否定することはできませんでした.  
- 離散かつ、標本サイズと、取りうる離散値の最小間隔がアンバランスな場合、t分布にせよ正規分布にせよ、二項分布と同様にスパースな(一般的になんと表現するのでしょうか？)形状になることは変わらない.  
- 1000人のうち1人が購入するような事例で実務上意味のある差を検出するためには正規性の仮定に必要なサンプルサイズが何なのかにもよるが、それだけでは足りなさそう.

 > 2標本t検定では、類似した分布からなる2つの変数の差を見ているため、正規性の仮定がもっともらしいものであるために必要なサンプルサイズが少なくなる傾向がある。これは、介入群とコントロール群が等しいトラフィック配分を持つ場合は特に当てはまる(Kohavi,DengandLongbotham,etal.2014)。
Ron Kohavi,Diane Tang,Ya Xu,大杉 直也. A/Bテスト実践ガイド　真のデータドリブンへ至る信用できる実験とは (Japanese Edition) (p.316). Kindle 版.
lastメソッドについて

# はじめに
レコードが存在しない場合でもlastメソッドを呼んだ時にエラーにならない！
lastメソッドの前に、ぼっち演算子を記載しないとエラーになるのかなと思ったので気になった、、

### 該当するコード
viewでadmin_informationsの最終レコードを表示させたい
※admin.admin_informationsが存在しないパターンもある

```views/admins/index.html.slim
- information = admin.admin_informations.last
```

それぞれのモデルは以下です。
```models/admin.rb
class Admin < ApplicationRecord
  has_many :admin_informations
~省略~
end
```
```models/admin_information.rb
class AdminInformation < ApplicationRecord
  belongs_to :admin
~省略~
end
```

### モデルが空の場合にlastメソッドを呼び出してもエラーにならない理由
* レコードが存在しない場合はnilを返すのではなく、***空のActiveRecord::Relation***を返すから
* has_manyで定義された関連モデルも同様に、レコードが存在しない場合は***空のActiveRecord::Relation***を返すから

→今回のパターンの場合、has_manyを使うことによって定義されるadmin_informationsは、関連付けられたすべてのオブジェクトのリレーションを返し、存在しない場合関連するモデルの空のActiveRecord::Relationを返す


### lastメソッドについて
* lastメソッドとは、最後のレコードを取得するメソッド
* データベースのテーブルの最後の行を取得するために使用する
* アクティブレコードオブジェクトが存在しない場合(テーブルが空の場合)には***nil***を返す

### まとめ
アクティブレコードオブジェクトが存在しない場合には***nil***を返す！！！

## 参照
[last | Railsドキュメント](https://railsdoc.com/page/last)
[has_many](https://apidock.com/rails/ActiveRecord/Associations/ClassMethods/has_many)
[AWS Q&A 365][Step Function]AWSのよくある問題の毎日5選 #54

# 1. AWS Step Functionsは、顧客データのセキュリティを確保するためにどのような対策を取っていますか？
回答：AWS Step Functionsは、データの安全性を確保するために、データの静的および転送時の暗号化、IAMポリシーによるアクセス制御、さまざまなセキュリティ標準と規制への適合など、さまざまなセキュリティ対策を実装しています。

# 2. AWS Step Functionsは、サービスへの不正アクセスからどのように保護していますか？
回答：AWS Step Functionsは、ネットワークセキュリティ、アイデンティティおよびアクセス管理、監視とログ記録など、複数のレイヤーのセキュリティコントロールを使用して、不正アクセスから保護しています。

# 3. AWS Step Functionsは、顧客がセキュリティポストを監視および管理するためのツールを提供していますか？
回答：はい、AWS Step Functionsは、監査とコンプライアンスのためのCloudTrail、リソースのインベントリと設定管理のためのAWS Config、脅威検出のためのAmazon GuardDutyなど、さまざまなツールを提供しています。

# 4. AWS Step Functionsは、サービスの統合のセキュリティをどのように確保していますか？
回答：AWS Step Functionsは、安全なAPIとプロトコルの使用、IAMポリシーによるアクセス制御、さまざまなセキュリティ標準と規制の遵守などによって、サービスの統合のセキュリティを確保しています。

# 5. AWS Step Functionsは、データ漏洩やその他のセキュリティインシデントに対してどのような対策を取っていますか？
回答：AWS Step Functionsは、定期的なセキュリティ評価と監査、インシデント対応計画の実施、顧客にセキュリティポストを向上させるためのツールとガイダンスの提供など、さまざまな対策を取っています。
[AWS Q&A 365][Step Function]Daily Five Common Questions #54

# 1. What measures does AWS Step Functions take to ensure the security of its customers' data? 
Answer: AWS Step Functions implements various security measures to ensure the security of its customers' data, such as encryption at rest and in transit, access control through IAM policies, and compliance with various security standards and regulations. 

# 2. How does AWS Step Functions protect against unauthorized access to its services? 
Answer: AWS Step Functions uses multiple layers of security controls to protect against unauthorized access, such as network security, identity and access management, and monitoring and logging. 

# 3. Does AWS Step Functions provide any tools for customers to monitor and manage their security posture? 
Answer: Yes, AWS Step Functions provides various tools for customers to monitor and manage their security posture, such as CloudTrail for auditing and compliance, AWS Config for resource inventory and configuration management, and Amazon GuardDuty for threat detection. 

# 4. How does AWS Step Functions ensure the security of its service integrations? 
Answer: AWS Step Functions ensures the security of its service integrations by using secure APIs and protocols, implementing access control through IAM policies, and complying with various security standards and regulations. 

# 5. What steps does AWS Step Functions take to protect against data breaches and other security incidents? 
Answer: AWS Step Functions takes various steps to protect against data breaches and other security incidents, such as conducting regular security assessments and audits, implementing incident response plans, and providing customers with tools and guidance to improve their security posture.
Chat GPTに音声合成のやりかたを聞いてみた

# はじめに
　Chat GPTにマルナゲドンシリーズ第2弾です。前回は音声認識に挑戦しました。mp3ファイルの文字起こしを行いました。
https://qiita.com/yukinocoding/items/7cf5fa7363f224a0ce11
　今回は文字起こしで生成されたテキストをもう一度音声に戻してみようと思います。
# やったこと
　Chat GPTに「Colabで動作するテキストを読み上げる音声合成プログラムを有料のAPIを用いないで書いて」と丸投げしました。生成したコードをそのまま前回のコードに繋げただけなので、開発時間は15分です。最初はGoogleのSpeech to Textを提案してきましたが、有料APIは使いたくなかったので使わないようにお願いしました。
　mp3ファイルがあれば、Colabにコピペで動作します。

```python:main.ipynb
!pip install pydub
!pip install SpeechRecognition
!pip install gTTS
from google.colab import files
from pydub import AudioSegment
import speech_recognition as sr
import os

def convert_audio_to_wav(input_file, output_file):
    audio = AudioSegment.from_mp3(input_file)
    audio.export(output_file, format="wav")

def transcribe_audio(input_file, output_file):
    r = sr.Recognizer()
    with sr.AudioFile(input_file) as source:
        audio = r.record(source)
        text = r.recognize_google(audio, language="ja-JP")
        with open(output_file, "w", encoding="utf-8") as f:
            f.write(text)

# mp3ファイルのアップロード
uploaded = files.upload()

# アップロードされたファイルのパスを取得
input_file = list(uploaded.keys())[0]
output_file = "/content/output.txt"

# mp3をwavに変換
converted_file = "/content/converted.wav"
convert_audio_to_wav(input_file, converted_file)

# 音声を文字起こししてテキストファイルに保存
transcribe_audio(converted_file, output_file)

# 一時ファイルを削除
os.remove(converted_file)
output_file = "/content/output.txt"

# テキストファイルを読み込んで表示
with open(output_file, "r", encoding="utf-8") as f:
    text = f.read()
    print(text)


from gtts import gTTS
from IPython.display import Audio

def text_to_speech(text, output_file):
    tts = gTTS(text=text, lang='ja')  # 言語を指定することもできます
    tts.save(output_file)
    return Audio(output_file)

output_file = "output.mp3"
audio = text_to_speech(text, output_file)
audio
```

# 結果
　自分の声のmp3ファイルを作成して、実行してみました。いつもよく聞くGoogleアシススタントの声で音声が返ってきました。ところどころ間違いはありますが、自分の言ったことそのまま話してもらえるのはロマンがありますね。プロポーズなど普段は直接言えないもどかしい思いを伝えるときに使えそうですね。
webのクローンサイト 学習記録　まとめ

# TMDB



https://qiita.com/RYA234/items/045f3d2c03684572d3f9


https://qiita.com/RYA234/items/61143e3405c1f1c6f077

# Google

https://qiita.com/RYA234/items/3aa9934b5dc3ae8fa9fc


https://qiita.com/RYA234/items/7cf917e4e5cdea668e2e

# instagram