OmniAICreator/Qiita-1.07M huggingface dataset:
Raspberry pi(paho)でのMQTTメッセージ購読の話

今回はRaspberry piでのMQTTメッセージを購読する方法を調査しました。
Raspberry piでの実装言語は… Pythonで行ってみました。
Pythonで行うとなるとpaho-mqttを使うと楽らしいので購読を行うだけのサンプルプログラムを作成してみました。

メインの参照はこちら…

https://github.com/eclipse/paho.mqtt.python

さまざまなサイトを見ましたが要素だけを抽出しました。
参考にさせていただいた全世界叡智に感謝
 履歴残していませんでした；；

## ターゲット
- hostname : raspberrypi
- Raspberry pi : 3 Model B v 1.2
- OS : Raspberry pi OSだったはず…
- debian : 10.12 (/etc/debian_version)
- uname : Linux raspberrypi 5.10.63-v7+ #1496 SMP Wed Dec 1 15:58:11 GMT 2021 armv7l GNU/Linux
- python : Python 3.7.3

今回の実装図はこんな感じになりました。
201にMQTTサーバ(mosquitto)があって
220に購読プログラムを走らせます
221からメッセージを発行します(mosquitto_pubコマンド)
これで、220発行のメッセージが220で購読後出力されれば疎通成功です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2481837/c5dd6366-8a08-1cc2-6a5c-6150146d73bc.png)

環境は前回の[MQTT(Mosquitto)サーバの構築の話 2022年5月28日](https://qiita.com/wataru775/items/2bb2c7adedc5fd93c936)の構成のままです。

# pahoのインストール
```bash
pi@raspberrypi:~ $ sudo pip3 install paho-mqtt
```

# 購読サンプルソース
さまざまな実装方法があるらしいですが、要素だけを出すとこんな感じでしょうか。
on_message部分でメッセージ内容だけを標準出力に表示するだけのプログラムです。

```python
#! /usr/bin/python3
import paho.mqtt.client as mqtt
 
host = '192.168.1.201'
port = 1883
topic = 'test'
 
def on_message(client, userdata, msg):
    print(str(msg.payload,'utf-8'))
 
def run():
    client = mqtt.Client()
    client.connect(host, port=port, keepalive=60)
     
    client.subscribe(topic)
    client.on_message = on_message
 
    client.loop_forever()
 
if __name__ == '__main__':
   run()
```

# 実行結果
購読プログラムの動作確認って単体だけではできません。
発行プログラム（コマンド）を実行してメッセージを購読できれば疎通確認とやっとなります。
そのため、
はじめに購読プログラムを実行してから、
発行プログラムを実行、
購読プログラムに発行メッセージが表示されることで
疎通確認となります。

1 . 実行確認するのに購読プログラムを実行します
```bash
pi@raspberrypi:~ $ python3 subscribe.py 
```
2 . 発行端末からMQTTメッセージ（topic test / 本文 “Test2”)を発行しました。
```bash
wataru@k8s-master:~ $ mosquitto_pub -h 192.168.1.201 -t test -m "Test2" -d
Client (null) sending CONNECT
Client (null) received CONNACK (0)
Client (null) sending PUBLISH (d0, q0, r0, m1, 'test', ... (5 bytes))
Client (null) sending DISCONNECT
```
3 . 疎通確認
```bash
pi@raspberrypi:~ $ python3 subscribe.py 
Test2
```

通りました。

# 終いに
今回はRaspberry piでMQTTメッセージの購読ができることを確認できました！

Pythonで行いましたが他言語も考えられるかなとか思いましたが、しばらく放置ですね。

元記事 : https://www.mmpp.org/archives/2929
【Java】「sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target」の対処法

# まえがき
このエラーに3時間ほど時間とられたので備忘として残しておく。

# 事象
`mvn clean`実行時に以下のエラーログが出力される。
```
sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target 
```
リモートリポジトリにある依存をダウンロードするときに、有効なSSL証明書がないと怒られる。

# 対処方法
SSL証明書は`$JAVA_HOME/lib/security/cacerts`に作成済だったのだが、こいつを読み取ってくれていなかったのが原因だった。読み取ってくれるように、`-Djavax.net.ssl.trustStore`オプションでキーストアを指定する。
```
mvn clean -Djavax.net.ssl.trustStore=C:\dev\tools\openjdk11\lib\security\cacerts
```

`.mavenrc`に以下のように記載してもよい。
```
MAVEN_OPTS="-Djavax.net.ssl.trustStore=C:\dev\tools\openjdk11\lib\security\cacerts"
```

# 参考

https://stackoverflow.com/questions/25911623/problems-using-maven-and-ssl-behind-proxy
githubリポジトリのサブフォルダーからnpm installしたい時

# 概要

github上で以下のような構成のリポジトリがあるとする。
ディレクトリ直下ではなく、サブフォルダにいくつかのpackageがまとまっているmonorepoだ。

```
.github/
packages/
├　package-a
│　└　package.json　# <- これをインストールしたい
└　package-b
　　　└　package.json
```

サブフォルダ内のあるpackage（例えばpackage-a）をインストールしたい時、単に　`npm i https://github.com/<user>/<repo>/packages/package-a`　とするだけではインストールできない。

```
npm i https://github.com/<user>/<repo>/packages/package-a

npm ERR! code E404
npm ERR! 404 Not Found - GET https://github.com/<user>/<repo>/packages/package-a
npm ERR! 404
npm ERR! 404  'https://github.com/<user>/<repo>/packages/package-a' is not in this registry.
npm ERR! 404 This package name is not valid, because
npm ERR! 404  1. name can only contain URL-friendly characters
npm ERR! 404
npm ERR! 404 Note that you can also install from a
npm ERR! 404 tarball, folder, http url, or git url.
```

# 対処法

そんな時は[gitpkg.now.sh](https://gitpkg.vercel.app/)を使おう。
サブフォルダからのインストールを簡単にしてくれるツールだ。

>　Using sub folders of a repo as yarn/npm dependencies made easy

使い方は　github.com　を　gitpkg.now.sh　に置き換えるだけだ。

```
npm i https://gitpkg.now.sh/<user/<repo>/packages/package-a?<commit>
```
パラメータに情報を指定する方法

#前提
この記事のVS codeにインストールしたrails6.0.0を使用しています。
私のオリジナルアプリケーションを用いて、誰かの役に立つ記事になればと思います。

#結論
以下のようにクリックされた時に送られてくるパラメータを指定してみましょう。
```
(例)
 <%= link_to "北海道", prototype_path(@prototypes, prefecture_id: 2), method: :get, class: :color%>
```

#アプリケーション概要
私は写真記録のオリジナルアプリを作成しました。
ユーザー登録をしているメンバーで日本一周の写真を記録するためのアプリケーションになります。

#詳細
ここからは少し私のオリジナルアプリの概要となります。
・トップ画面には日本地図をHTML+CSSで描いています。
・画像投稿時には、都道府県(prefecture_id)と投稿する写真を選択します。
・例えば、地図中の北海道をクリックしたときに「prefecture_id: 2」というパラーメータを送り、それに該当する画像のみをshow.html.erbで表示してくれるアプリケーションになります。

HTML+CSSで描いた日本地図と投稿時の選択したprefecture_idには関連性がないため、クリックしたときにprefecture_idの情報を送る必要があります。
そのときに繰り返しになりますが以下のようにすることで送るパラメータ情報を決めることができます。

```
index.html.erb

    <%# 北海道 %>
    <div id="hokkaido">
      <%= link_to "北海道", prototype_path(@prototypes, prefecture_id: 2), method: :get, class: :color%>
    </div>

    <%# 青森県 %>
    <div id="aomori">
      <%= link_to "青森", prototype_path(@prototypes, prefecture_id: 3), method: :get, class: :color%>
    </div>
    
    <%# 秋田 %>
    <div id="akita">
      <%= link_to "秋田", prototype_path(@prototypes, prefecture_id: 6), method: :get, class: :color%>
    </div>
```

最後まで読んでいただき有難うございました。
初投稿記事のため、分かりにくい点やダラダラ書いてしまったため、結論を先に書きました。
今後も自分のアウトプット＋誰かの役に立つ記事を書いていますのでよろしくお願いします。
Gitを初期設定する

Gitを使うユーザーを設定します。
```
$ git config --global user.name 'hikotaro'
$ git config --global user.eamil 'hikotaro_san@yaoo.co.jp'
```

Gitの出力をカラー表示するように設定します
```
$ git config --global color.ui auto
```

設定した内容は、~/.gitconfigファイルに保存されます。
```
[user]
	name = hikotaro
	email = hikotaro_san@yahoo.co.jp
[color]
	ui = auto
```
CentOSに最新のGitをインストールする

# Gitの最新版のソースを取得する
Webブラウザで、Gitの[ダウンロードサイト](https://git-scm.com/download/linux)を表示します。
[download a tarballリンク](https://mirrors.edge.kernel.org/pub/software/scm/git/)をクリックしてください。
![スクリーンショット 2022-05-29 7.31.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14202/1d1fc36c-62ba-b99a-49c6-b313ce116665.png)

ソースファイの一覧が表示されます。
最新バージョンのファイルをダウンロードしてください。
```
# cd /usr/local/src
# wget https://mirrors.edge.kernel.org/pub/software/scm/git/git-2.36.1.tar.gz
```

# Gitをインストールする
ダウンロードしたファイルを解凍し、インストールします。
以下のコマンドを実行してください。
```
# tar -zxf git-2.36.1.tar.gz
# cd git-2.36.1
# make prefix=/usr/local all
# make prefix=/usr/local install
```

# インストール結果を確認する
以下のコマンドを実行してください。
```
$ git --version
```

以上です。
STM32G031でアナログサーボ信号のモニター(PA2ハードウェアシリアル出力)


x 動作確認済み2022/5/31 13:03

x OBでGPIOがわかる人むけ


目的
アナログサーボ信号をモニターしたい。
既存のプログラムの使い回し

1
2 VDD
3 GND
4 GPIO デバッグ出力 9600bpsハードウェアシリアル

8 SWD SWCLK
7 SWD SWDIO
6 
5 PWM(IN) 400uS=0,2400uS=255


![o_con507.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/416baf89-dd57-73e2-5424-9b802712e303.jpeg)


![o_con508.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/f5e30f23-b68c-adab-e491-825afc9fe336.jpeg)



名前は、まだない



```c++


//PWM_to_MON_031_2

#include <Arduino.h>
#include <HardwareSerial.h>

#define in_pwm   PA11 // 5pin


//初期化
void setup() {

  delay(3000); //not Delete

  pinMode(in_pwm, INPUT); //pa11

  Serial.setTx(PA2_ALT1);
  Serial.setHalfDuplex();
  Serial.begin(9600);

} //setup


//メインループ
void loop()
{
 
  //PWMでデータ取得
  int pwmco2=pulseIn(in_pwm,HIGH,2000000);

/*
  //データの表示 debug
  Serial.println(pwmco2);
*/

  if(pwmco2 <= 400  ) { pwmco2 = 400; }
  if(pwmco2 >= 2400 ) { pwmco2 = 2400; }

  pwmco2 = pwmco2 - 400;
  
  int vel = (pwmco2 * 523) >> 12;
 
  //表示
  //send
  Serial.println(vel);

  //0.1秒の待ち
  delay(100); //←ここで送信間隔を調整する

} //loop





```
access2000の作法 その11

# 概要
access2000の作法、調べてみた。
終了ボタンを作ってみた。

# 終了ボタンを作る手順。
- menuフォームを右クリック。
- デザインビューを開く
- ボタンを設置
- ウィザードが開く　キャンセル
- プロパティシートを開く
- イベントを開く
- クリック時、コードビルダーを開く

```
Private Sub コマンド6_Click()
    '終了
    If MsgBox("終了します。お疲れ様でした。", vbYesNo) = vbYes Then
        DoCmd.Quit
    End If
End Sub
```

- フォームを閉じる
- 保存する


以上。
access2000の作法 その10

# 概要
access2000の作法、調べてみた。
デリートしてみた。

# デリートボタンを作る手順。
- menuフォームを右クリック。
- デザインビューを開く
- ボタンを設置
- ウィザードが開く　キャンセル
- プロパティシートを開く
- イベントを開く
- クリック時、コードビルダーを開く

```
Private Sub コマンド8_Click()
    DoCmd.RunSQL "DELETE FROM 売上テーブル WHERE 売上<5000;"
    MsgBox "ok"
End Sub
```

- フォームを閉じる
- 保存する

以上。
暇つぶしを提案してくれるAPIからタイピングゲームを作って暇つぶしをする

# タイピングゲーム

![スクリーンショット 2022-05-31 4.14.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2025002/5855b774-4d99-8f72-713b-de9b2eb11e71.png)

https://motokikando.github.io/js-typing-game/

# 使用API

退屈を紛らわす手段を提供してくれる The Bored API 

https://www.boredapi.com/(https://www.boredapi.com/)

JSON形式で下記のように返してくれる。activityが具体的な提案を表示してくれる。


```json
{
"activity": "Play a video game",
"type": "recreational",
"participants": 1,
"price": 0,
"link": "",
"key": "5534113",
"accessibility": 0
}
```
https://www.boredapi.com/api/activity/



# 構成

```
.
├── README.md
├── audio
│   ├── correctsound.mp3
│   ├── typingsound.mp3
│   └── wrongsound.mp3
├── index.html
├── main.js
├── style.css
```

# 主な機能

- Typing 入力処理　
    - 入力文字の色を追加 
    - タイピング音
- 問題表示
    - 入力文字列と問題が全て一致すると新しいテキストをレンダリングして表示
- 制限時間, エラー数, スコア 表示
    
- 結果の表示
    - WPM (1分あたりの入力文字数)
    - スコア
    - 入力ミスの数
    - ミスタイプしたアルファベット
    

# ソースコード
https://github.com/motokikando/js-typing-game
2022/5/31主にITとかセキュリティの記事

総務省、テレワークの中小企業向けセキュチェックリストを改訂
https://www.security-next.com/136889

ランサム攻撃、脆弱性含むネットワーク機器から侵入か - 月桂冠
https://www.security-next.com/136846

4月のDDoS攻撃は件数、最大規模ともに減少 - IIJレポート
https://www.security-next.com/136871

ネット流出の可能性ある個人情報、対象件数を修正 - 沖縄の信販会社
https://www.security-next.com/136878

あなたも騙されるフィッシング詐欺のヤバイ文面(5/29)
https://ascii.jp/elem/000/004/092/4092870/?rss

Webブラウザのトラフィック乗っ取るマルウェア「ChromeLoader」の活動が活発化(5/29)
https://news.mynavi.jp/techplus/article/20220529-2352899/

米商務省、サイバーセキュリティー関連ソフトなどの輸出管理に関わる最終規則を発表(5/27)
https://www.jetro.go.jp/biznews/2022/05/f5132ae13ccb19c7.html

Windowsの累積更新プログラム適用で、トレンドマイクロ製品に問題(5/29)
https://news.mynavi.jp/techplus/article/20220529-2353667/

米議員、中国のデジタル人民元を米国のアプリストアから閉め出す法案を公表(5/29)
https://jp.cointelegraph.com/news/draft-bill-to-ban-china-s-digital-yuan-from-us-app-stores

Drupalの使うライブラリに脆弱性、アップデートを(5/29)
https://news.mynavi.jp/techplus/article/20220529-2352843/

制裁下のロシアが仮想通貨での国際決済を検討：報道(5/29)
https://jp.cointelegraph.com/news/amid-sanctions-russia-weighs-crypto-for-international-payments-report

「仕事のiPhoneで“野良アプリ”を堂々と使う人」がゼロではない現実(5/28)
https://techtarget.itmedia.co.jp/tt/spv/2205/28/news01.html

自民党青年局のNFTが議員ツイートで流出!?　QRコードが写り込んだ画像により脆弱性を露呈(5/29)
https://news.nicovideo.jp/watch/nw10981860

NEWS pickup＆digest（2022/4/13～5/10）(5/30)
https://xtech.nikkei.com/atcl/nxt/mag/nnw/18/041800011/051900055/

ブラウザーの中に「偽ブラウザー」を表示する新手口
URLを確認しても防げないフィッシング(5/30)
https://xtech.nikkei.com/atcl/nxt/mag/nnw/18/041800013/051800053/
【Dokcer】node_modulesをボリュームで管理する【Remote-Containers】

## 全ファイルをボリュームで管理する
### Docker
```Dockerfile:/Dockerfile
FROM node:16.15.0

ENV TZ=Asia/Tokyo
ARG USERNAME=node

WORKDIR /myapp-frontend
COPY /package*.json /myapp-frontend
RUN npm ci
USER ${USERNAME}

EXPOSE 3000

```

```/docker-compose.yml
version: '3.9'
services:
  frontend:
    build: .
    volumes:
      - .:/myapp-frontend
    environment:
      NODE_ENV: development
    ports:
      - 3000:3000
      - 9229:9229
    volumes:
      - .:/workspace:cached
      - node-node_modules:/myapp-frontend/node_modules
    command: /bin/sh -c "while sleep 1000; do :; done"
volumes:
  node-node_modules:
```

### Remote-Containers

```.devcontainer/devcontainer.json
{
  "name": "myapp-frontend",
  "dockerComposeFile": ["../docker-compose.yml"],
  "service": "frontend",
  "workspaceFolder": "/myapp-frontend",
  "customizations": {
    "vscode": {
      "extensions": [
        "EditorConfig.EditorConfig",
        "dbaeumer.vscode-eslint",
        "esbenp.prettier-vscode",
        "VisualStudioExptTeam.vscodeintellicode",
        "arcanis.vscode-zipfs",
        "bradlc.vscode-tailwindcss"
      ]
    }
  }
}
```
pandasにコントリビュートした話

# 概要
バックエンド開発者のnakatomotoiです！
今回はpandasにコントリビュートした話をまとめたいと思います。

https://github.com/pandas-dev/pandas/pull/43459

# pandas とは？
pandasはPythonで利用されるライブラリで、データ分析の場面でよく使われるものです。
機械学習のコード等でよく使われており、Githubのstar数は34kを超えています。

https://pandas.pydata.org/docs/user_guide/index.html

# step1. issueを探す
まずは対応するissueを探しましょう。
pandasでは3k以上のissueがあり、中には対応が簡単なものもあります。
今回は最初のコントリビュートということで、`good first issue`のタグから探しました。
（簡単なやつは結構競争率高いです）

https://github.com/pandas-dev/pandas/issues/41241

# step2. 対応方針を決める
issueを選んだらどのように対応するか方針を固めましょう。
今回のissueでは、空パケットのデータフレームを読み込むときにエラーが発生するという問題が指摘されているようです。
問題自体の対応は別PRで既に行われているようなので、テストケースを追加する必要がありそうです。

```python
import pandas as pd
df = pd.DataFrame({"value": pd.array([], dtype=pd.Int64Dtype()})
df.to_parquet("/path")
df2 = pd.read_parquet("/path")
...
ValueError: need at least one array to concatenate
```

対応方針については質問を投げたら丁寧に回答もらえました。

![スクリーンショット 2022-05-31 0.43.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264506/46c19033-78b5-fa46-daf5-c60f8f3271d0.png)


# step3. PRを作成する
対応がわかったので、早速PRを作成しましょう。
OSSではコントリビューター向けにドキュメントがあるため、一読してから取り組みます。

# step4. レビューを元に修正する

https://github.com/pandas-dev/pandas/pull/43459

PRを作成すると、レビューをしてもらえるのでコメントを基に修正していきます。
時差の関係で夜中にコメントが入ることもありますが、なるべく早く対応していくとよさそうです。
pandasくらいの大きいライブラリだと、しばらくレビューもらえないこともあるみたい…

# まとめ
step 1~4 を経て、無事PRマージされました！
普段ライブラリは使う側ですが、OSSを通してコミュニティに微力ながら貢献できました。
思ったほどハードルの高いものではないので、これを読んだ初学者〜中級者の方も積極的にOSSコントリビュートしていきましょう！
Active Storageで添付元のレコードを削除したときに画像はどうなるのか

RailsのActive Storageを使用して画像を保存している場合に、画像を添付しているレコードを削除すると画像はどうなるのか(消えるのか、残るのか)を知りたかった。

dependent: :destroyのようなoptionがあるのかなと思ったのだけれど、Railsガイドを読んでもよくわからなかった。


Actice Storageのコードをみると、デフォルトで**dependent: :purge_later**という値がセットされていた。

```
def has_many_attached(name, dependent: :purge_later, service: nil, strict_loading: false)
　　省略
end
```
https://github.com/rails/rails/blob/fe8d41eda16911d2ef0e7d08bb02d16650b2dcdf/activestorage/lib/active_storage/attached/model.rb#L129

purge_laterは、[Raildガイド](https://railsguides.jp/active_storage_overview.html#:~:text=%E3%81%95%E3%82%8C%E3%81%BE%E3%81%99%E3%80%82-,4%20%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%92%E5%89%8A%E9%99%A4%E3%81%99%E3%82%8B,-%E6%B7%BB%E4%BB%98%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%92)によると、
**「Active Jobを介して、関連付けられているモデルと実際のリソースファイルを非同期で破棄します。」** とのことで、

つまり、has_one_attachedとかhas_many_attachedとかで特にoptionを設定しなければ、
添付されてるレコードを削除したときに、非同期処理で添付した画像も削除される。

[ここ](https://api.rubyonrails.org/v5.2/classes/ActiveStorage/Attached/Macros.html#:~:text=If%20the%20%3Adependent%20option%20isn%27t%20set%2C%20all%20the%20attachments%20will%20be%20purged%20(i.e.%20destroyed)%20whenever%20the%20record%20is%20destroyed.
)にも書いてあった。
Arduinoでドローンの製作記録

# コンセプト
Arduinoでドローンを製作をしてます。センサーの知識も無線の知識も制御の経験もなかったですが，制御プロクラムと送受信機（iphone app）は自作、フレーム、モーター、バッテリー、などは既製品使って自作したいと思います。

ここでは，ドローンが飛ぶまでの過程を書いていきたいと思います。

---------------------------------------
* [ドローンの構想，部品選定、組み立て](https://qiita.com/shinshin_kenkyuu/private/b48acee82889a79c087a)
* [モーターの可動確認](https://qiita.com/shinshin_kenkyuu/private/e8cbca4376335e7c50b5)
* [6軸センサーから姿勢角度算出](https://qiita.com/shinshin_kenkyuu/private/30762028f993c30be7f1)
* [esp32-camとの接続確認](https://qiita.com/shinshin_kenkyuu/items/a188ce3b7bad543b4069)
* [ArduinoでESP32を利用し、PS4コントローラと接続](https://qiita.com/shinshin_kenkyuu/items/9a02e80b05eefb6665c1)

未完成
* [気圧センサーから高度を算出]
* [気加速度センサーから上昇・下降速度を算出]
* [姿勢制御プログラムの作成]
六軸センサーから姿勢角度算出


## 姿勢制御
ドローンの姿勢を制御し水平に保つために、ドローンの姿勢を正確に検出する必要があります。
そのために、姿勢角を求めるセンサーを利用し、姿勢角を計算します。
ここで、MPU6050と呼ばれる６軸センサを使用しました。

MPU-6050では、ドローンのこれらの状態を数値で読み取ることができます。

ドローンの加速度（x方向、y方向、z方向）
ドローンの回転（ロール、ピッチ、ヨー）
## 電路図
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2697457/d42f910c-06ab-0554-f4ad-065c5e78a2f4.png)
## 姿勢算出
理論上、ジャイロのみまたは加速度センサーのみでも姿勢算出はできるのですが。

ジャイロセンサにはジャイロセンサの値を積分することで角度算出はできます。しかし、センサーをずっと静止させていても，ノイズや出力のズレやなどの影響で、出力値が外れていきます。
```c:test_gryo_.ino
float pitch = pitch + YAxis * timeStep;
float roll  = roll  + XAxis * timeStep;
float yaw   = yaw   + ZAxis * timeStep;
```
加速度センサーには静止している状態で、時間が経っても出力のズレがないですが。yawの角度算出が困難であり、センサー自体に動いている場合に正確な角度（pitch、roll）も算出できません。
```c:test_gryo_.ino
  float pitch = -(atan2(XAxis, sqrt(YAxis * YAxis + ZAxis * ZAxis)) * 180.0) / M_PI;
  float roll = (atan2(YAxis, ZAxis) * 180.0) / M_PI;
```

ジャイロからでも加速度センサーからでも姿勢算出はできるのですが、正確な数値とは言えません。ここで、ジャイロセンサーと加速度センサーを連動させる(センサフュージョン)ことで、補正を行ってより正確な姿勢角度を算出できます。

## 補正方法
ここで、Madgwickフィルタを利用して、より正確な姿勢角度を算出できます。

Madgwickフィルタは相補フィルターの一種、計算処理がほかのフィルタより軽いので，マイコンに対して優しいです．内容はちょっと難いので、興味があれば、この[サイト](https://qiita.com/fumiya_sato/items/df28dff5ccc653183235)を参考してください。

内容は分からなくでも大丈夫です、今回はMadgwickフィルタを使ってドリフト補正を行います。
## ソースコード
ここで参考したライブラリのリンク：

https://github.com/jarzebski/Arduino-MPU6050

https://github.com/arduino-libraries/MadgwickAHRS

~~~c:test_Madgwick_.ino
//MPU6050 Madgwick filter Test
#include <Wire.h>
#include <MPU6050.h>
#include <MadgwickAHRS.h>
Madgwick MadgwickFilter;
MPU6050 mpu;

// Timers
unsigned long timer = 0;
float timeStep = 0.01;

// Pitch, Roll and Yaw values
float pitch = 0;
float roll = 0;
float yaw = 0;

void setup() 
{
  Serial.begin(115200);
  MadgwickFilter.begin(200); //100Hz
  // Initialize MPU6050
  while(!mpu.begin(MPU6050_SCALE_2000DPS, MPU6050_RANGE_2G))
  {
    Serial.println("Could not find MPU6050 sensor");
    delay(500);
  }
  
  // Calibrate gyroscope. The calibration must be at rest.
  // If you don't want calibrate, comment this line.
  mpu.calibrateGyro();

  // Set threshold sensivty. Default 3.
  // If you don't want use threshold, comment this line or set 0.
  mpu.setThreshold(3);
}

void loop()
{
  timer = millis();

  // Read normalized values
  Vector acc = mpu.readNormalizeAccel();
  Vector gyr = mpu.readNormalizeGyro();

  // Calculate Pitch, Roll and Yaw
  pitch = pitch + gyr.YAxis * timeStep;
  roll = roll + gyr.XAxis * timeStep;
  yaw = yaw + gyr.ZAxis * timeStep;
  MadgwickFilter.updateIMU(gyr.XAxis, gyr.YAxis, gyr.ZAxis, acc.XAxis, acc.YAxis, acc.ZAxis);
  // Output raw
  Serial.print("補正前 Pitch = ");
  Serial.print(pitch);
  Serial.print(" Roll = ");
  Serial.print(roll);  
  Serial.print(" Yaw = ");
  Serial.println(yaw);
  
  Serial.print("補正後 Pitch = ");
  Serial.print(MadgwickFilter.getPitch());
  Serial.print(" Roll = ");
  Serial.print(MadgwickFilter.getRoll());
  Serial.print(" Yaw = ");
  Serial.println(MadgwickFilter.getYaw());

  // Wait to full timeStep period
  delay((timeStep * 1000) - (millis() - timer));
}
~~~
grpcurlを用いてgRPCサービスの動作確認を行う

# はじめに
gRPCサーバーの開発を進めている際に、gRPCサーバーに対しての動作確認をローカル環境で行いたい場合があると思います。
そこで、動作確認の際に、便利なツールが公開されているので紹介します。

# grpcurl

https://github.com/fullstorydev/grpcurl


# install
今回はMacにて使用するので、以下のコマンドでinstallを行います。
```
brew install grpcurl
```

# 事前準備
gRPCサーバーの`main.go`に以下の処理を追記しておきます。

下記のソースを追加
```
	s := services.NewServer(client)

	reflection.Register(s) ⇦追記
	log.Printf("server listening at %v", lis.Addr())
	if err := s.Serve(lis); err != nil {
		log.Fatal(err)
	}
```
上記の設定を追加しておかないと、grpcurlコマンド使用時に以下のようなエラーが返ってきます。

```
Failed to list services: server does not support the reflection API
```
# 使い方

* gRPCサーバーが提供しているservice一覧取得

```
$ grpcurl -plaintext localhost:50051 list
genproto.CatService
genproto.DogService
genproto.PonyService
```

* 特定のserviceが保有するrpc関数の一覧取得

```
$ grpcurl -plaintext localhost:50051 honeycomb.AlarmService list genproto.CatService
genproto.CatService.Create
genproto.CatService.Delete
genproto.CatService.Get
genproto.CatService.List
genproto.CatService.Update
```

* 特定のserviceが保有する特定のrpc関数を実行する
```
$ grpcurl -plaintext -d '{"name": "taro" }' localhost:50051 honeycomb.AlarmService/list
{
  response: {
  }
}
```

`-d`オプションを明示するとrequestフィールドを付与した上で関数を呼び出すことができます。
Calling `DidYouMean::SPELL_CHECKERS.merge!(error_name => spell_checker)' has been deprecated. Please call `DidYouMean.correct_error(error_name, spell_checker)' instead.を解決する話。(Mac railsコマンド等で出る)



# Calling `DidYouMean::SPELL_CHECKERS.merge!...が出た！
railsコマンドやbundleコマンドを実行すると、掲題のエラーが出る。
特にそれによって何かが使えないというわけではないが、エラー内容にある通り非推奨みたいだし、毎回これが出るのは不愉快なので、消したい思う！

# bundlerのバージョンを変えよう！

https://github.com/rubygems/rubygems/issues/5234

上記の記事によると、bundlerのバージョンをアップデートすればOKみたい。
さっそくやっていく！

```
$ bundle -v
Calling `DidYouMean::SPELL_CHECKERS.merge!(error_name => spell_checker)' has been deprecated. Please call `DidYouMean.correct_error(error_name, spell_checker)' instead.
Bundler version 2.2.17
$ bundle update --bundler 
.
.
.
Bundle updated!
```
アップデートが完了したので、もう一度コマンドを実行してみる。
```
$ bundle -v
Bundler version 2.3.14
```
versionが最新になり、かつ、さっきまで出ていたエラーも出なくなった！
以上！
【React】useNavigateをモック化してリンク遷移のユニットテストを書く

# 概要
useNavigateを使ってリンク遷移を行うReactアプリにて、useNavigateをモック化してリンク遷移テストを行います。
先月からReactとTypeScriptを使い始めたばかりなのため、指摘等ありましたらよろしくお願いします。

## 環境
- @types/react: 18.0.9
- @types/react-router-dom: 5.3.3
- @types/jest: 27.5.1
- react: 18.1.0
- react-router-dom: 6.3.0
- typescript: 4.6.4

# やり方
まずは実装コードの紹介。
```useRedirectSignUp.ts
import { useCallback } from 'react';
import { useNavigate } from 'react-router-dom';

export type ReturnType = {
  onClickSignUp: () => void;
};

// SigunUp画面への遷移
export const useRedirectSignUp = (): ReturnType => {
  const navi = useNavigate();
  const onClickSignUp = useCallback(() => navi('/signup'), [navi]);
  return { onClickSignUp };
};

```

`/signup`パスに遷移する`onClickSignUp`関数を提供する簡素なカスタムフックです。

続いてテストコード。

```typescript: useRedirectSignUp.test.ts
import { renderHook, act } from '@testing-library/react-hooks';
import { useRedirectSignUp } from '../../../hooks/SignIn/useRedirectSignUp';

// Navigatorモック準備
const mockedNavigator = jest.fn();
jest.mock('react-router-dom', () => ({
  ...jest.requireActual('react-router-dom'),
  useNavigate: () => mockedNavigator,
}));

describe('useRedirectSignUp', () => {
  test('サインアップ画面に遷移すること', () => {
    //renderHook実行
    const { result } = renderHook(() => useRedirectSignUp());

    // 関数実行
    act(() => {
      result.current.onClickSignUp();
    });

    // /signup を引数にNavigatorが呼び出されること
    expect(mockedNavigator).toHaveBeenCalledWith('/signup');
  });
});
```

重要なのが `useNavigate`をモック化している部分です。
useNavigateは以下の形式でモック化を行います。
```typescript
// Navigatorモック準備
const mockedNavigator = jest.fn();
jest.mock('react-router-dom', () => ({
  ...jest.requireActual('react-router-dom'),
  useNavigate: () => mockedNavigator,
}));
```

:::note warn
注意
上記Mock化コードは`describe`の中ではなく**外に**記述してください。
:::


```typescript
// /signup を引数にNavigatorが呼び出されること
expect(mockedNavigator).toHaveBeenCalledWith('/signup');
```



後はモック化した`mockedNavigator`を使って普通にテストを書いてしまってOKです。
お疲れさまでした。

# 参考
https://github.com/remix-run/react-router/issues/7811
[個人開発]米国株のバックテストを爆速比較ができるサイトを個人開発した話

個人開発で バックテストを比較できるサービスを作りました！
https://www.backtest-ranking.com/

高速に米国株のバックテストの結果を表示、比較できるところが売りのサービスです👍

### サービス概要
比較ページ
・銘柄を指定して戦略ごとのバックテストのランキングが見れます。

詳細ページ
・バックテストの詳細が見れます。

![ezgif.com-gif-maker.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2685085/bb7771ef-941d-2a4d-476f-0f18aa6c6c90.gif)


### バックエンド
Fastapiとpythonを使ってます。
![スクリーンショット 2022-05-30 15.11.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2685085/07cedd92-da2e-4d1d-1a10-3f397c170eae.png)

### フロントエンド
Reactとtyepscriptを使ってます。
![スクリーンショット 2022-05-30 15.12.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2685085/0b37a9c0-b5a0-93c2-287b-0d309e8c3703.png)

### インフラ
バックエンドのインフラははaws cdkで作成した以下の画像の構成です。
フロントエンドはnetlifyです。
![diagram.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2685085/ae7be854-3777-1703-a599-058ef0086693.png)

### 工夫したポイント
バックテストのランキングを表示するまでのスピードの工夫に一番力を入れました。

バックテストは定期的にバッチ処理をした上でバッチ処理したデータはディレクトリ内に直接保存しアクセスに応じてそのデータを返すようにしています。

この構成にすることでサーバーへの負荷を抑えた上でバックテストの結果を高速にクライアントサイドに返せます。

### 開発効率の工夫
Vscode とCi(継続的インテグレーション)にlinter, formeter、unit testを組み込むことで効率的に運用してます。


### 運用の工夫
バッチ処理はGitHub actionで定期的にバッチを行ってます。

バッチ処理完了後にdocker image を作成しecr にデプロイする処理もGitHub actions でおこなってます。

### バックエンド設計の工夫
バックエンドはCQRSパターンを使ってバッチ処理とバッチで作成されたバックテストのクエリー処理の動線を分けてます。

その上でdddを利用しCQRSのコマンドとクエリーで共通のオブジェクトはドメイン層にまとめてしまって、レポジトリ層やサービス層ではコマンドとクエリーで動線をわけてます。

### フロント設計の工夫
コンポーネントが疎結合になるようにコロケーション、contextを活用してます。


### インフラ設計の工夫
Cdkとtypescriptを活用することでtypescriptの型推論の恩恵を受けながら運用できるようんしてます。

### コストの工夫
バッチは全てgithub actionで行いバッチ処理したデータはdbを使わずディレクトリ内に直接保存することでコストを最低限にしてます。

### まとめ
初めてのwebサービス個人開発でしたがなんとか形になってよかったです笑

ここからは顧客の意見を取り入れながら使ってもらえるサービスに成長させていきたいので[サービスページ](https://www.backtest-ranking.com/)のヘッダー右上のfeedbackよりフィードバックを頂けると嬉しいです🙏
tweepy(TwitterAPI v2)で特定のアカウントの直近ツイートを数千件以上取得する方法

# 初めに
この記事で紹介するプログラムは下記のバージョンにおいて、2022年5月の時点ではTwitterAPI v2のアクセスレベルがEssentialの場合でも動作することを確認しています。また、TwitterAPIを利用する際のキー取得などの手続きの記載はここでは割愛しています。しかしながら、アクセスレベルがEssentialの場合は2022年5月の時点ではTwitterAPIの利用申請が**不要**であるため、興味がある方は是非ともこの機会に手続きしてみましょう！
この記事は備忘録という形で書かせていただきますが、出来るだけ分かりやすいものになるようにコメントなどを多く記載しました。
# バージョン
```
Python==3.9.2
tweepy==4.10.0
```
# 注意
この方法では直近3,200件のツイートしか取得することが出来ないため、それよりも昔のツイートも取得したい！という方は、申し訳ありませんが別の方法をお試しください。
# 本プログラム全文
```get_tweets.py
import tweepy
import json

# キー情報
bearer_token = "あなたのbearer_tokenをここに書いてください"
consumer_key = "あなたのconsumer_keyをここに書いてください"
consumer_secret = "あなたのconsumer_secretをここに書いてください"
access_token = "あなたのaccess_tokenをここに書いてください"
access_token_secret = "あなたのaccess_token_secretをここに書いてください"

# Twitter API v2 Client
client = tweepy.Client(
    bearer_token=bearer_token,
    consumer_key=consumer_key,
    consumer_secret=consumer_secret,
    access_token=access_token,
    access_token_secret=access_token_secret,
    wait_on_rate_limit=True,
)

# 直近のツイートを取得したいアカウントのユーザー名(@～～～)を指定
username = "直近のツイートを取得したいアカウントのユーザー名をここに書いてください"

# 直近のツイートを取得したいアカウントのIDを取得
user = client.get_user(username=username)
user_id = dict(user[0])["id"]

out_file_name = "out.json"
oldest_id = None
tweet_count = 0
tweets_data = dict()

while True:
    # ツイートを100件ずつ取得
    tweets = client.get_users_tweets(id=user_id, max_results=100, until_id=oldest_id)

    # ツイートを取得することが出来なくなったら終了
    if "oldest_id" not in tweets[3]:
        break

    for tweet in tweets[0]:
        # ツイートを追加
        tweet_dict = dict(tweet)
        tweets_data[tweet_dict["id"]] = tweet_dict["text"]

        tweet_count += 1

    # 次のループでさらに過去のツイートを取得出来るようにする
    oldest_id = tweets[3]["oldest_id"]

# ツイートをjsonデータとして保存
with open(out_file_name, "w", encoding="utf-8") as f:
    json.dump(tweets_data, f, ensure_ascii=False, indent=4)

#取得したツイートデータの数を出力
print(f"取得したツイートの数: {tweet_count}")
```
書き込んだキー情報とユーザー名に誤りがなければ、このプログラムがあるディレクトリ内にout.jsonという先ほど取得したツイートがまとまったjsonファイルが作成されます。続いてこのプログラムについて補足をさせていただきます。
```
wait_on_rate_limit=True,
```
tweepy.Client()において、この引数をTrueにすると、メソッドの呼び出し回数がレート制限に引っかかった場合に次のリセットまで待機してくれるので非常に便利です。ここは原則Trueにした方が良いと思います。
```
tweets = client.get_users_tweets(id=user_id, max_results=100, until_id=oldest_id)
```
まず、ここではoldest_idよりもidが小さいツイートの中で1~100番目にidが大きいツイートを取得しています。idは古いツイートほど小さくなります。なお、ツイートを100件ずつ取得している理由ですが、Client.get_users_tweets()において一度に取得することが出来るツイート数の最大が100件だからです。
```
oldest_id = tweets[3]["oldest_id"]
```
次に、ここでは先ほど取得したツイートの中で最もidが小さいツイートのidをoldest_idに代入することで、次のループではさらに過去に遡ってツイートを取得することが出来るようにしています。
# 最後に
もしも、もっと良いコードの書き方がある場合や誤った記述がある場合は、コメントにて教えていただけると非常に助かります......。
# 参考文献
[tweepy公式ドキュメント](https://docs.tweepy.org/en/stable/client.html)
論文：Few-Shot Object Detection with Attention-RPN and Multi-Relation Detector

## Info

- タイトル：Few-Shot Object Detection with Attention-RPN and Multi-Relation Detector
- カンファ：CVPR2020
- 著者：Qi Fan, Wei Zhuo, Chi-Keung Tang, Yu-Wing Tai
- 論文：https://arxiv.org/abs/1908.01998
- プロジェクトページ：https://github.com/fanq15/Few-Shot-Object-Detection-Dataset

## 概要
- 少ない正解付きデータから未知のカテゴリの物体を検出することを目的とした、新しいFew-Shot Object Detection ネットワークを提案。
- Few-Shot Object Detection用の1000カテゴリの新しいデータセットを構築。
- 一度ネットワークをトレーニングすれば、新しいカテゴリのデータを検出するのにファインチューニングや追加の学習が不要。

## 背景
- 既存の物体検出手法は、一般的に膨大な量のアノテーションデータに依存し、長い学習時間を必要とする。このため、近年では少数ショットによる物体検出が開発されている。
- 近年のFew-Shot Object Detectionの研究は、いずれもファインチューニングを必要とする。
- 本論文では再トレーニングやファインチューニングを行うことなく、新規物体の検出に適用できる一般的なFew-Shot Object Detection モデルを提案する。

## 手法

### データセット

<img width="400" alt="ファイル名" src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211712/c67ff62e-9781-8023-87a5-8419d4bd6716.png>

- 1000カテゴリに及ぶ多様なデータセットになっている。
- ボックスサイズやアスペクト比に大きなばらつきのあるオブジェクトが含まれ、テストセットには3つ以上のオブジェクトを含む画像が26.5%含まれており、チャレンジングなデータセットになっている。
- 画像枚数は66000でcocoの半分程度でコンパクトながらFew-Shot learningに効果的なデータセットになっている。

### ネットワーク
<img width="800" alt="ファイル名" src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211712/5509d695-9f6a-c653-8df3-9a62f100ccbe.png>

　Query Imageが物体検出を行いたい画像。Support Imageが検出したい物体の画像。Support Imageは複数枚同時にインプットすることもできる（画像は1枚の場合の図）。ResNet50をbackbornに使用し、Attention RPNでサポートとクエリの特徴量のマッチング関係を学習する。

<img width="500" alt="ファイル名" src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211712/bad869c7-8d1f-3280-f6bb-7a928c177d90.png>

　サポート特徴$X\in t^{S\times S \times C}$とクエリ特徴$Y \in t^{H\times W\times C}$の類似度として次のようなAttentionを考える。

```math
G_{h,w,c} = \sum_{i,j\in {1,...,S}} X_{i,j,c} Y_{h+i-1, w+j-1, c}
```
　$G \in t^{(H-S+1)\times (W-S+1)\times C}$はAttention mapである。本論文の実験では$S=1$が最も性能が良かった。Attention mapにさらにConvolutionをかけて、Region ProposalとClassificationを行う。

<img width="400" alt="ファイル名" src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211712/a67c1dab-14f3-ca32-cb68-d9e4ab7982ae.png>

　R-CNNと同様に、Attention RPNから出力されたRegionとスコアを後続のネットワークで修正し、精度を向上させる。サポートのROI PoolingとAttention RPNが出力したクエリのROI Poolingの関係を学習するMulti-Relation headを使用して、最終的なボックスとスコアを出力する。

### 学習戦略
　同一カテゴリの画像と異なるカテゴリの画像を組み合わせた triplet $(q_c , s_c , s_n )$ を用いて、一致と不一致の関係を学習させる。
　負のサポート画像の場合、背景の提案が大量に存在し、これが学習を支配するのが普通である。そこで、クエリ提案とサポート間のマッチングペアの比率をバランスさせる。

## 評価

<img width="400" alt="ファイル名" src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211712/0b8dd5c2-3398-a759-f755-35e6fab451cc.png>

COCOの結果だけ紹介。他は省略。既存の代表的手法であるMeta R-CNNとFeature Reweighting（どちらもファインチューンが必要）に対してSOTA達成。

下図は実際に予測された画像。

<img width="1200" alt="ファイル名" src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211712/06558e3c-7f07-a212-1495-26d07304db15.png>


## まとめと感想
- 新規カテゴリの学習なしに新規物体検出が出来る点が画期的だと思った。産業界ではデータ収集やアノテーションに費用と時間がかかったり、まれな事象のデータが取得しにくかったりするので活用の幅は広そう。
- 学習戦略でtripletを作成する必要があり、また画像データのバランスを取る必要があって、実用時にはチューニングが必要になって大変そう。
- サポートにどんな画像を入れるか選定が難しそう。サポートデータが溜まってきたとしてもすぐには精度向上に使用できないのが残念。改善の余地ありか。
【Linux】 Bashスクリプト

## 入力を受け付ける。

・入力された値を変数INPUTに定義する。

```
echo -n "input:"
read INPUT
echo $INPUT
```

・シェルスクリプト実行時に渡される引数。

```
./test.sh Japan America China
```

```
echo $1
echo $2
echo $3
```

## 変数

・変数に文字列を定義する。

```
TEST="aiueo" 
```

・ダブルクウォートも文字列として定義する。。

```
TEST='"aiueo"'
```

・変数にコマンドの実行結果を定義する。

```
TEST=`ls -l`
```

### 特殊変数

| 変数名 | 自動的に設定される値 |
| :---: | :--- |
| $? | 直前に実行されたコマンドの終了ステータスが設定される変数 |
| $! | バックグラウンドで実行されたコマンドのプロセスID が設定される変数 |
| $- | set コマンドで設定されたフラグ、もしくはシェルの起動時に指定されたフラグの一覧が設定される変数 |
| $$ | コマンド自身の PID (プロセスID)が設定される変数 |
| $# | 実行時に指定された引数の数が設定される変数 |
| $@ $* | シェルスクリプト実行時、もしくは set コマンド実行時に指定された全パラメータが設定される変数 |
| $LINENO | この変数を使用している行の行番号が設定される変数 |
| ${PIPESTATUS[@]} | パイプで連結した各コマンドの終了ステータスが設定される変数(配列) |
| $1, $2, …, ${10}, | set コマンドもしくはシェルスクリプトの N個目の引数が設定される変数 (位置パラメータと呼ばれる) |

・変数を参照する。

```
echo $TEST
echo ${TEST}
```

## 配列

・配列を定義する。

```
ARRAY=("Japan" "America" "China" "Korean")
```

```
ARRAY[0]="Japan"
ARRAY[1]="America"
ARRAY[2]="China"
ARRAY[3]="Korean"
```

・配列全体を参照する。

```
echo ${ARRAY[@]}
```

・インデックス番号で配列の要素を指定する。

```
echo ${ARRAY[0]}
```

## if

ある条件に従って処理を分岐する。

・ディレクトリ(/ect)の有無を確認し、表示する値を条件分岐する。
ファイルの有無の確認の場合は、-fにする。

```
if [ -d /etc ]; then
    echo "ディレクトリが存在します。"
elif
    echo "ディレクトリが存在しません。"
else
    echo "その他"
fi
```

・数値の比較

| 比較演算子 | 内容 |
| :---: | :--- |
| 数値1 -eq 数値2 | 数値1と数値2が等しければ真（=）|
| 数値1 -ne 数値2 | 数値1と数値2が等しくなければ真（!=、<>）|
| 数値1 -gt 数値2 | 数値1が数値2よりも大きければ真（>）|
| 数値1 -ge 数値2 | 数値1が数値2以上であれば真（>=）|
| 数値1 -lt 数値2 | 数値1が数値2よりも小さければ真（<） |
| 数値1 -le 数値2 | 数値1が数値2以下であれば真（<=）|

・文字列の比較

| 比較演算子 | 内容 |
| :---: | :--- |
| 文字列1 = 文字列2 | 文字列1と文字列2が等しければ真 |
| 文字列1 != 文字列2 | 文字列1と文字列2が等しくなければ真 |
| -n 文字列 | 文字列の長さが1以上であれば真 |
| -z 文字列 | 文字列の長さが0であれば真 |

・複数の条件分岐(ADN、OR、NOT)

```
read INT

if [ $INT -ne 0 ] && [ $INT -le 30 ]; then
    echo "True"
else
    echo "False"
fi
```

| 条件 | 記載方法 | 内容 |
| :---: | :---: | :--- |
| AND | -a または && | すべての条件式の結果が真の場合を返す |
| OR | -o または || | いずれかの条件式の結果が真の場合に真を返す |
| NOT | ! | 真偽を反転して返す |

## case

条件ごとに処理を分岐する。

・入力された値によって処理を分ける。(正規表現も可能。)

```
echo -n "input:"
read input

case "$input" in
    "1" ) echo "1が入力されました。";;
        [A-Z] ) echo "アルファベットが入力されました";;
        [0-9] ) echo "数字が入力されました";;
            * ) echo "上記以外";;
esac
```

## while

条件式が成り立っている場合、繰り返し処理を行う。

・aと入力している場合のみ繰り返す

```
#!/bin/bash

read key
while [ "$key" = "a" ]
do
  echo "ok"
  read key
done
```

## whileでインクリメントする方法。 (letでもexprのどちらでも可能。)

```
COUNT=0
while [ $COUNT -lt 10 ]
do
  echo ${COUNT}
 COUNT=`expr ${COUNT} + 1`
  let COUNT++
done

```

## for

条件式が成り立っている間、繰り返し処理を行う。

・iが10以下になるまで繰り返す

```
#! /bin/bash
for ((i=1; i<=10; i++))
do
  echo "$i"
done
```

## function

繰り返し利用するような処理をfunctionで関数化することでコードを短くすることができる。

・コマンドリスト関数の作成と呼び出し(function自体は省くこともできる。)

```
function command_list() {
   ls
}

command_list
```

## 参考

[https://www.pasonatech.co.jp/workstyle/column/detail.html?p=7450](https://www.pasonatech.co.jp/workstyle/column/detail.html?p=7450)

[https://shellscript.sunone.me/while.html](https://shellscript.sunone.me/while.html)

[https://shellscript.sunone.me/variable.html](https://shellscript.sunone.me/variable.html)
「Rails6」Active Storageを使った画像のプレビュー機能

<h1>前提条件</h1>

- ruby 2.6.8
- Rails 6.1.5
- ActiveStorageをインストール済
- slimを使用


<h1>概要</h1>
　画像を投稿する際,画像を選ぶと、画像プレビュー機能を実装してどういった画像が投稿されるか確認できるようにします。
イメージ画像を以下に添付します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2350392/1eb93912-5f29-4e32-a80a-86de599263ea.png)


<h1>実装</h1>
画像のプレビュー機能は、ActiveStorageのメソッドではなく、Jqueryで実装します。
そのためには、Jqueryを設置する必要があります。Rails6ですので、下記のコマンドが便利ですね。

```docker-compose run web yarn add jquery-ujs```


では下記がコードになります。※CSSのコードは除きます。
```ruby:posts/edit.html.slim
.form-upload.make-center-column
  .uploadimage
    - if @user.profile_picture.attached?
        = image_tag url_for(@user.profile_picture), class: "upload-image", id: 'preview'
    - else
        = image_tag "nopic.jpg", class: "upload-image", id: 'preview'
  = form_with model: [:info, @user], method: :patch do |user|
    = user.file_field :profile_picture, class: "photo_upload", id: "user_img", 
      onchange: "previewImage(this)",accept: 'image/jpg,image/jpeg,image/png,image/gif'

    .form-control
      = user.label "お名前"
      = user.text_field :name, value: current_user.name
    .form-control
      = user.label "自己紹介"
      = user.text_area :introduce, value: current_user.introduce

    = user.submit '編集', class: 'form-button'

  = link_to '戻る', info_user_path(current_user), class: 'form-button'

javascript:

 function previewImage(obj)
  {
    var fileReader = new FileReader();
    fileReader.onload = function() {
    var element = document.getElementById('preview');
    element.src = fileReader.result;
        }
        fileReader.readAsDataURL(obj.files[0]);
    };
```
3行目の`- if @user.profile_picture.attached?`は既に画像を設定しているかを確認して、画像がありましたら、現在の画像を表示します。
なかったら、`nopic.jpg`を表示します。

そして今回肝になるコードが8行目の下記です。
  ```ruby
= user.file_field :profile_picture, class: "photo_upload", id: "user_img", 
    onchange: "previewImage(this)",accept: 'image/jpg,image/jpeg,image/png,image/gif'
```
`onchange: "previewImage(this)"`で、ファイルの選択を検知するとJavascriptの`previewImage（this）`が実行されます。
ちなみに`accept:'image/jpg,image/jpeg,image/png,image/gif`で、アップロードする形式を限定することができます。

Javascriptのコードを説明しますと、選択されたイメージのURLをidが`preview`の要素のsrcに格納します。
```javascript
javascript:

 function previewImage(obj)
  {
    var fileReader = new FileReader();
    fileReader.onload = function() {
    var element = document.getElementById('preview');
    element.src = fileReader.result;
        }
        fileReader.readAsDataURL(obj.files[0]);
    };
```

つまり下記のコードは既に画像を設定されているときは、`＠user.profile_picture`を表しますが、新たにファイルが選択されると、imageタグのsrcが選択されたファイルのURLに変わって、選択されたファイルを表すことになります。

```ruby
= image_tag url_for(@user.profile_picture), class: "upload-image", id: 'preview'
```
<h1>終わりに</h1>
書いてみると、とても簡単なコードですが、具現化まで結構時間がかかりました。   

Active Storageにまだ慣れてないこともありますが、Active Storageにpreviewメソッドがあると勝手に思い込んだのが主な原因ですね、、
`Active storage image preview`ばかり検索していました。
何かを調べる時は思い込まないことが大事だとこのpreview機能から習うことができましたね。

以上です。imageのpreview機能を追加したい方々にちょっとでもご参考になったら嬉しいです。
ご指摘等ございましたら、教えてください。
Qiita編集(見出し、特殊文字 Unicode指定、表、シンタックスハイライト)

# ◯見出し記号
見出しは#表示文字の間に半角空白が必要。
#の数分見出しが下がる。１〜６まで使用可能
>&#35;[半角スペース]見出しタイトル
 # #H1
 ## ##H2
 ### ###H3
 #### ####H4
 ##### #####H5
 ###### ######H6

# ◯特殊文字 Unicode指定
基本UNICODEコード編集フォーマット
UNICODE表サイト：https://www.tamasoft.co.jp/ja/general-info/unicode.html
> &#[UNICODEコード10進];
> &#x[UNICODEコード16進];

使用例">"の場合　UNICODEコード:x003E
> &#38;&#35;x003E; と入力すると
> &#x003E;　と表示される


# ◯表
表の書き方
```
|デフォルト|左寄せ|右寄せ|センター| ←タイトル行
|-|:-|-:|:-:| ←データ部との境(:文字の寄せる方向)
|内容1|内容2|内容3|内容4| ←データ行
```
|デフォルト|左寄せ|右寄せ|センター| 
|-|:-|-:|:-:|
|内容1|内容2|内容3|内容4|

# ◯シンタックスハイライト 
◯開始記述
>&#x0060;&#x0060;&#x0060;[修飾子][言語名] 

修飾子は省略可能
修飾子は、差分"diff_"など
言語名は"c","c++","java","php"など

◯言語
```c
　```c
  printf("test¥n");
　```
```


◯言語の差分
・各行の始めで+, -を記述し、半角スペースをあける
```diff_c
　```diff_c
+  printf("test¥n");
-  printf("tests¥n");
　```
```

# 参考サイト
https://qiita.com/johnjohnjohnjohn/items/2f4c8431d5371cd6794b

https://help.qiita.com/ja/articles/qiita-edit

https://qiita.com/shizen-shin/items/d5eccb343a1c201e2f83
🔰【初学者用】Railsエラー画面の見方とデバッグ

# はじめに
Ruby on Railsのアプリケーションのデバッグ方法を記載していきます！
Railsアプリケーションの開発中は、エラーが頻繁に起きますよね。でもエラーを解決していくことでRailsの仕組みの理解に繋がっていきます。
エラーは自身の成長をサポートする友達^^

# エラー画面の見方
まずはエラー画面の見方から
- １行目にはエラーの概要
- 2行目にはエラー発生の予測場所
- 3行目にはエラー内容の詳細
- 4行目にはコード内のエラー発生場所
ざっくりこんな感じです。自身のエラー画面と比べてみると分かりやすいと思います！

# デバッグ
デバッグとは、プログラムの誤りを見つけて、手直しすることです。
実際にデバッグ作業を行うには`pry-rails`というGemを使用します！

## 実際にデバッグしてみましょう！
※投稿機能実装時、投稿する時にエラーが発生する場合を例とします。
まずはGemの確認から！以下のGemが導入されていることを確認してください
```rails:Gemfile
gem `pry-rails`
```
この`pry-rails`を導入することでbinding.pry（バインディングプライ）を使用することができるようになります。
binding.pryという記述をソースコードの中にすると、binding.pryが存在する箇所でRailsの処理を一時停止し、その状態でコンソールを起動できます。便利！！
```rails:app/controllers/posts_controller
 def create
    binding.pry
    Post.create(post_params)
 end
```
例えば、上記のようにbinding.pryを使用するとローカルサーバー内でSENDボタンをクリックすると(SENDボタンが投稿する役割を持っている場合。)処理が停止され、ターミナルでコンソールが起動されます。
ターミナルを確認するとこんな感じ、
```rails:ターミナル
Started POST "/posts" for ::1 at 2022-05-30 17:31:23 +0900
Processing by PostsController#create as HTML
  Parameters: {"authenticity_token"=>"7imvHaB3Z9fle2uEDeNY5mBJebreoWRs6Scs8zcEHuL5FHWhXS8eOpX8Db8i13SGuV1F8T9fTbgSCuBFkrOmDQ==", "post"=>{"image"=>"", "text"=>"aaa"}, "commit"=>"SEND"}
  User Load (0.8ms)  SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 ORDER BY `users`.`id` ASC LIMIT 1
  ↳ app/controllers/posts_controller.rb:50:in `move_to_index'

From: /Users/***/projects/post/app/controllers/posts_controller.rb @ line 14 PostsController#create:

    13: def create
 => 14:   binding.pry
    15:   Post.create(post_params)
    16: end

[1] pry(#<PostsController>)> 
```
[1] pry(#<PostsController>)> で入力待ちの状態になっています。
投稿時にformから送信したパラメーターを、paramsと入力して見てみましょう！
```rails:ターミナル
[1] pry(#<PostsController>)> params
=> <ActionController::Parameters {"authenticity_token"=>"7imvHaB3Z9fle2uEDeNY5mBJebreoWRs6Scs8zcEHuL5FHWhXS8eOpX8Db8i13SGuV1F8T9fTbgSCuBFkrOmDQ==", "post"=>{"image"=>"", "text"=>"aaa"}, "commit"=>"SEND", "controller"=>"posts", "action"=>"create"} permitted: false>
[2] pry(#<PostsController>)> 
```
このように、配列のような形で出力できます。
仮に、formで送信しても保存ができない場合があり、「formでパラメーターが送れていない可能性がある」と仮説を立てた場合に、このデバッグ方法で検証できます。
最後にexitと入力してコンソールを終了しておきましょう。

# まとめ
簡単ではありましたが以上がエラー画面の見方とデバッグの一例になります。
上記でも述べましたが、エラーを解決していくことでRailsの仕組みの理解に繋がっていきます。
「エラーは自身の成長をサポートする友達^^」

ではまた。

※プログラミングを始めてまだ月日も浅い初学者が投稿しています。誤字脱字等ありましたら申し訳ないです。何か間違いがあればご指摘いただけると助かります。
プリプロセッサーの定義方法(Swift・Objective-C)

## はじめに

SwiftとObjective-Cでプリプロセッサーの定義場所が異なってつまづいたので、備忘録がてら紹介します。

## 環境

- OS：macOS Monterey 12.3.1
- Xcode：13.2.1 (13C100)
- Swift：5.5.2

## プリプロセッサーの定義方法

プリプロセッサーの定義方法を紹介します。

### Swift

__PROJECT > Build Settings > Swift Compiler - Custom Flags > Other Swift Flags__
![スクリーンショット_2022-05-30_23_10_20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/138245/d6b7fad4-4cbe-c19b-61bf-1d57ea294333.png)

SwiftではOther Swift Flagsで `-D QA` のように指定します。

以下の例では、ビルド構成が `DEBUG` または `QA` の場合に `#if ... #endif` 内の処理がビルドに含まれます。

```swift:Foo.swift
#if DEBUG || QA
...
#endif
```

### Objective-C

__PROJECT > Build Settings > Apple Clang - Preprocessing > Preprocessor Macros__
![スクリーンショット_2022-05-30_23_11_25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/138245/b26c9b1c-71da-0e8c-841e-3c088743070c.png)

Objective-CではPreprocessor Macrosで `QA=1` のように指定します。

`#if ... #endif` の書き方はSwiftと同様です。

```objective_c:Foo.h
#if DEBUG || QA
...
#endif
```

`.h` ファイルのみでなく `.m` ファイルでも同じように書けます。

```objective_c:Foo.m
#if DEBUG || QA
...
#endif
```

ただSwiftとObjective-C（C言語）でプリプロセッサーの条件分岐の書き方は異なります。
Objective-Cでこの書き方だと、 `DEBUG` や `QA` が未定義の場合に予期せぬ動作になるかもしれません。

定義されているかどうかを判定したい場合、 `defined()` を使うのが無難です。

```diff_objective_c:Foo.h
- #if DEBUG || QA
+ #if defined(DEBUG) || defined(QA)
...
#endif
```

## SwiftとObjective-Cが混在している場合は注意

SwiftとObjective-Cが混在している場合、Other Swift Flagsのみ定義していると、Objective-Cのコードでプリプロセッサーが未定義となり予期せぬ動作になります。

私はここでつまづいて時間を溶かしました。
Preprocessor Macrosの定義を忘れないよう気をつけましょう。

## おわりに

これでSwiftとObjective-Cの両方でプリプロセッサーを定義できるようになりました！

## 参考リンク

- https://twitter.com/the_uhooi/status/1531116029090271233
- [プリプロセッサ条件分岐、OR条件 - Qiita](https://qiita.com/D-3/items/b98b63c2629496856654)
typescriptのジェネリクスについて



## はじめに
` <T>`などでよく目にするtypescriptのジェネリクスについてまとめる。

## 結論

```tsx

// <T>で型変数を決め、(value: T): Tでそれぞれ
// printAndReturn関数の引数の型と、
// 返り値の型の部分で型変数を使用している
function printAndReturn<T>(value: T): T {
  console.log(value);
  return value;
}

const value = "あいうえお"

// 引数はstringなので、<string>のように型引数にstringを指定する
returnValue = printAndReturn<string>(value);

console.log(returnValue)
```

### 詳細

型変数はTypeScriptの慣習でTを使うことが多い。このTはtemplateの略と言われているらしい。

型変数が2つある場合はTとUが用いられることがあり、その理由はアルファベット順でTの次がUだから、らしい。

## おわりに

ジェネリクスの使い方は理解していたが、なぜT,Uなのか、型変数・型引数などの呼称は知らなかったのでいい機会になった。
TFSでプロジェクトをソース管理に追加したのに、鍵アイコンが表示されないとき

sln(ソリューション)ファイルをチェックインしていても、以下の手順でやっていなかったら鍵アイコンが出なかった。

## 手順
ソリューションエクスプローラー＞ソリューションで右クリック＞ソリューションをソース管理へ追加
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538652/f4369400-0a85-184f-9252-ecee7b8c85c5.png)


追加しようとすると「既にソース管理下にあります」「ソース管理に既に追加されているので、もう追加はできません」メッセージが出ました。
今回は以前ソース管理に追加したのはわかっており、そのままでいいので「無視」ボタンにしました。


これで鍵アイコンやチェックアウトを表すチェックアイコン(レ点マーク)が表示されるようになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538652/677cba4d-e778-f498-a093-c7194df22503.png)
【GitHub】Markdown で折りたたみを表現する方法

# 概要
GitHubのマークダウンでの記載で折りたたみする場合の記載方法が独自にあったのでメモ

# 折りたたみ

```
<summary>折りたたまれるタイトル</summary>
<pre>
<code>
折りたたまれる部分
折りたたまれる部分
折りたたまれる部分
</code>
</pre>
</details>
```

Qiitaの記載はこうすると折りたたみできる

```
<details>
<summary>折りたたみ部分のタイトル</summary>
折りたたまれる部分
折りたたまれる部分
折りたたまれる部分
</details>
```

<details>
<summary>折りたたみ部分のタイトル</summary>
折りたたまれる部分
折りたたまれる部分
折りたたまれる部分
</details>

# 参考
https://gist.github.com/ericclemmons/b146fe5da72ca1f706b2ef72a20ac39d
【Vue+TypeScript】Vuetifyのボタンサイズをレスポンシブにする

# やりたいこと

Vuetifyのボタンコンポーネントである、`v-btn`を画面サイズでlargeにしたりsmallにしたり変えたい

```vue
<v-btn color="primary">ボタン</v-btn>
```

# 結論

結論から言うと下記のようになります。


```js
<v-btn color="primary"　v-bind="size">ボタン</v-btn>

size () {
    const btnSize = {xs:'x-small',sm:'small',lg:'large',xl:'x-large'}
    const size = btnSize[this.$vuetify.breakpoint.name];
    return size ? { [size]: true } : {}
}
```

まずは、`Vuetify`のブレークポイント(`xs`や`lg`等)がキーにして、そのキーの値として、v-btnのサイズを指定してあげます。
`this.$vuetify.breakpoint.name`は現在の画面サイズの名前が取れるので、オブジェクトへのアクセスをします。
`md`の場合は何も指定しなくて良いので、それ以外の場合は`x-small:true`のようにpropsで指定できる形で返します。

以下TSでの詳細になります。

# やりかた

## 1
まずはv-btnが受け取れるサイズを確認します。

https://vuetifyjs.com/en/api/v-btn/#props

以下がpropsで指定できます。mdの場合は何も指定しないようです。
- x-small
- small
- large
- x-large


この情報から型を作成します。

```typescript
type ButtonSize = 'x-small' | 'small' | 'large' | 'x-large';
```

propsで指定できるもののユニオン型ができました。

## 2

Vuetifyに定義されているブレークポイントの名称をインポートします。

```ts
import { BreakpointName } from "vuetify/types/services/breakpoint"

// BreakpointNameは"xs" | "sm" | "md" | "lg" | "xl"のようなユニオン型です。
```


結論で書いたように必要なのは

`{xs:'x-small',sm:'small',lg:'large',xl:'x-large'}`　のような、
`Vuetify`のブレークポイント(`xs`や`lg`等)がキーに、値としてv-btnのサイズのようなオブジェクトの型が必要です。

これを実現するには、mapped typeを使用します。

https://typescriptbook.jp/reference/type-reuse/mapped-types

これを利用すると下記のようにできます。

```typescript
import { BreakpointName } from "vuetify/types/services/breakpoint"
type ButtonSize = 'x-small' | 'small' | 'large' | 'x-large';

type ResponsiveButton = {
  [Key in BreakpointName]: ButtonSize
}
```

しかし、ここで問題があります。
BreakpointNameは"xs" | "sm" | "md" | "lg" | "xl"のようなユニオン型です。
ボタンのサイズはmdがないので、BreakpointNameから減らすか、ButtonSizeにダミー的な値を追加する必要があります。

ここではBreakpointNameから減らします。
`Exclude`を使用すると指定の型から対象を除いた型を作成できます。

```ts
import { BreakpointName } from "vuetify/types/services/breakpoint"
type ButtonSize = 'x-small' | 'small' | 'large' | 'x-large';

type ResponsiveButton = {
  [Key in Exclude<BreakpointName,'md'>]: ButtonSize
}
```

## 3

あとは先程のメソッドを作成します。
`$vuetify.breakpoint.name`で`md`が取得できてしまうのでその点だけ処理を追加しています。

```ts
size () {
    if (this.$vuetify.breakpoint.name === 'md') {
      return {};
    }
    const btnSize:ResponsiveButton  = {xs:'x-small',sm:'small',lg:'large',xl:'x-large'}
    const size = btnSize[this.$vuetify.breakpoint.name];
    return { [size]: true }
}
```
HTTPリクエストの基本4種類メモ

## GET
HTMLページを見たいときに使われるHTTPリクエスト
例)google検索でudemyを開くときにはgetメソッドを利用している

## POST
サーバーに情報を送り、それに対してレスポンスが欲しいときに使われるHTTPリクエスト
例)ユーザー名やパスワードを用いてログインしたいとき
例)ブログを投稿するとき


## PUT
情報を更新したいときに使われるHTTPリクエスト
例)アカウント情報を修正するときなど

## delete
情報を削除したいときに使われるHTTPリクエスト
例)登録情報を削除したい場合はdeleteメソッドを使う
【nodenv】BUILD FAILED (OS X 11.3.1 using node-build 4.9.43) のエラーが出た場合

M1 mac で `nodenv install x.x.x` をしようとしたときに以下のエラーがでたので、その対処方法です。

```bash
$ nodenv install 8.16.0
Downloading node-v8.16.0.tar.gz...
-> https://nodejs.org/dist/v8.16.0/node-v8.16.0.tar.gz

WARNING: node-v8.16.0 is past its end of life and is now unsupported.
It no longer receives bug fixes or security updates.

Installing node-v8.16.0...

BUILD FAILED (OS X 11.3.1 using node-build 4.9.43)

Inspect or clean up the working tree at /var/folders/7k/4bcqvyqn59ngsw819tg29xd00000gn/T/node-build.20220530225738.25230.3q6zxi
Results logged to /var/folders/7k/4bcqvyqn59ngsw819tg29xd00000gn/T/node-build.20220530225738.25230.log

Last 10 log lines:
1 error generated.
make[1]: *** [/private/var/folders/7k/4bcqvyqn59ngsw819tg29xd00000gn/T/node-build.20220530225738.25230.3q6zxi/node-v8.16.0/out/Release/obj.target/openssl/deps/openssl/asm/arm64-linux64-gas/sha/sha1-armv8.o] Error 1
In file included from ../deps/openssl/asm/arm64-linux64-gas/sha/sha256-armv8.S:1:
../deps/openssl/openssl/crypto/arm_arch.h:46:6: error: "unsupported ARM architecture"
#    error "unsupported ARM architecture"
     ^
1 error generated.
make[1]: *** [/private/var/folders/7k/4bcqvyqn59ngsw819tg29xd00000gn/T/node-build.20220530225738.25230.3q6zxi/node-v8.16.0/out/Release/obj.target/openssl/deps/openssl/asm/arm64-linux64-gas/sha/sha256-armv8.o] Error 1
rm 0f013dca09e35835b3d7c9ba7bcc5609f886b7cc.intermediate
make: *** [node] Error 2
```

## 環境

- macOS Big Sur（バージョン 11.3.1）
- nodenv 1.4.0

## 対応

古いバージョンの Node は ARM に対応してないしていないとのことなので、Intel モードのコンソール（Rosetta）で、操作する必要がある。

### 1. ターミナルの「情報」を開く

![スクリーンショット_2022-05-30_23_14_28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/238595/09fd9772-180a-bfcd-e7d6-49bcf32d2923.png)

### 2. 「Rosettaを使用して開く」にチェックを入れる

![スクリーンショット_2022-05-30_23_13_26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/238595/762e67c4-33d9-0763-4b9c-89e118147a9b.png)

### 3. Rosetta モードのターミナルを開く

先ほどエラーが出ていたコマンドを打つと、エラーが出ずにインストールができるようになっています。

```bash
$ nodenv install 8.16.0
Downloading node-v8.16.0-darwin-x64.tar.gz...
-> https://nodejs.org/dist/v8.16.0/node-v8.16.0-darwin-x64.tar.gz

WARNING: node-v8.16.0-darwin-x64 is past its end of life and is now unsupported.
It no longer receives bug fixes or security updates.

Installing node-v8.16.0-darwin-x64...
Installed node-v8.16.0-darwin-x64 to /Users/xxxxx/.anyenv/envs/nodenv/versions/8.16.0

$ nodenv versions
* 8.16.0
  14.17.1
```

### 4. ターミナルの設定を戻す

インストールが終わったら、先ほどチェックを入れた「Rosettaを使用して開く」のチェックを外します。

おわり。