OmniAICreator/Qiita-1.07M huggingface dataset:
スロークエリの改善方法(with MySQL)

### はじめに

初めまして、スタートアップでGoのバックエンドエンジニアをやっている大学3年生です。

今回の記事を書こうと思ったきっかけとして、オープンソース版Twitterである『misskey』の[とあるissue](https://github.com/misskey-dev/misskey/issues/11078#issue-1785199654)で『slow query』が話題に上がる出来事がありました。それを受けて、自分でも理解度が浅かったのを自覚したのと、また丁度、最近、インターン先でSQLのパフォーマンスチューニングする機会を頂いたのが理由になります。


### スロークエリとは？
まずスロークエリ（slow query）とは、実行時間が一定時間よりも遅い(0.5~2.0秒など)クエリのことを言います。その原因は多岐にわたり、データベースの設定、SQLクエリの書き方、インデックスの不適切な使用などが考えられます。これらの原因を分析、特定し、そして適切な対処をとることでパフォーマンスの改善が見込めます。


### ボトルネックの特定
パフォーマンス問題を解決するためには、まずボトルネックとなっている部分、つまりパフォーマンスを落としている原因を特定することが重要です。スロークエリのボトルネックを特定するためには一般的に以下の手段があげられます。

1. クエリログの分析：ほとんどのデータベースシステムでは、実行されたクエリとその実行時間を記録するロギング機能があります。特に、MySQLにはスロークエリログという、特定の閾値を超えて実行時間がかかったクエリを記録する機能が存在します。これらのログを分析することで、パフォーマンスを低下させている特定のクエリを見つけ出すことが可能です。

https://qiita.com/vuvanly/items/525592cbc80c25efdcea

2. EXPLAINの利用：SQLのEXPLAINコマンドは、クエリがどのように実行されるか（実行計画）を表示します。これにより、データベースがどのインデックスを使用しているのか、テーブルのどの部分をスキャンしているのか、クエリが内部でどのように動作しているのかを理解できます。これを利用して、パフォーマンスの低下を引き起こしている可能性のあるクエリの動作を確認し、改善の手がかりを探します。

https://www.sambaiz.net/article/240/

3. システムリソースのモニタリング：クエリの実行に影響を与えるのはSQL文だけでなく、ハードウェアやネットワークのリソースも関係する場合もあります。したがって、CPUの使用率、メモリの使用量、ディスクI/O、ネットワークの状態などをモニタリングすることも重要となります。


### 具体的な改善案
#### 1.インデックス

パフォーマンスを向上させるための効果的な手段の一つは、データベースにおけるインデックスの使用です。インデックスは、特定のカラムを持つレコードを検索しやすくするための索引のようなもので、WHERE句や結合の条件、ORDER BY句の条件で対象のカラムを使用する場合に設定することが推奨されます。
ただし、インデックスはパフォーマンスを向上させる一方で、データを更新する際（挿入、更新、削除）にはオーバーヘッドを増加させます。これは、行の変更があるたびにインデックスも更新する必要があるからです。そのため、むやみにインデックスは設定せず、どのカラムに付けるのかは慎重に選ぶ必要があるとも言えます。

https://qiita.com/katsukii/items/3409e3c3c96580d37c2b

#### 2. バルクインサート
バルクインサートは、大量のデータを一括で挿入する方法。クエリ発行回数を抑えることで挿入速度を下げたり、ネットワークの通信量の減少、トランザクション処理の効率化などのメリットがあります。ただし、大量のデータを扱うので、メモリ消費量が激しい点、複数インデックスの更新処理がある点は注意が必要となります。
#### 3. SQLクエリの最適化
使用するSQLの書き方自体を最適化することでパフォーマンスを改善することができます。たとえば、不要な結合を省く、WHERE句での条件指定を最適化する、LIMIT句を使用して必要な結果のみを取得するなどがあります。

https://qiita.com/azukiazusa/items/ad7d8622e640f71448db

#### 4. 非正規化
非正規化とは、パフォーマンスを上げるためにあえて「正規化を崩す」こと。これは、データの結合(JOIN)の回数を抑えて、必要なデータをまとめて取れるようにあえて冗長なテーブル設計にすることと言えるでしょう。例えば、JOINが複数必要なるかつ、カラムの数が少ないケースや読み取り回数が頻繁にあるケースなどで使用することがあります。ただし以下の記事ように、一度正規化の場合で検証してみることや、どうして非正規化したのかをドキュメントを残しすなど、明確な目的と根拠を持って非正規化する必要があるのかどうかを考える必要があると思います。

https://www.edifist.co.jp/lecture/dbdesign/03_03/02/04.aspx#:~:text=%E6%98%8E%E3%82%89%E3%81%8B%E3%81%AB%E3%81%97%E3%81%BE%E3%81%99%E3%80%82-,%E9%9D%9E%E6%AD%A3%E8%A6%8F%E5%8C%96%E3%81%A8%E3%81%AF,%E3%81%8C%E5%BF%85%E8%A6%81%E3%81%AB%E3%81%AA%E3%82%8A%E3%81%BE%E3%81%99%E3%80%82

### まとめ
以上がスロークエリの原因と対策の概要です。これらのポイントを抑えて、データベースのパフォーマンス改善に取り組むことが重要です。ただし、具体的なケースによっては他にも注意すべきポイントが存在する場合がありますので、状況に応じて適切な対策を検討してください。

なお、最近は便利なORMフレームワークなどによって、クエリ最適化の自動化が進んでいますが、根本的な原理を理解し、パフォーマンスの改善に取り組むことは依然として重要かと思います。(ちなみに、今回初めての技術ブログ投稿で、まだまだ至らない点はあるかと思いますが、いいねでご応援いただけると嬉しいです!)
インターフェースデザインツール(figma)について


## はじめに
今回ポートフォリオ作成にあたりfigmaでワイヤーフレームを書きました。
最初は手書きで書こうとしていたのですが、どうせやるなら実践的な形で
やりたいと思い色々とネット検索をして(ツールの知識がないので)
たどり着いたのがfigmaでした。

無料(ページ数制限アリ)ですごく使いやすく、重宝したので
ワイヤーフレームを作成したいなってなった時によければ使ってみて下さい。

## figma(フィグマ)とは
![スクリーンショット 2023-07-06 21.01.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3478070/71d57773-bb27-5cb5-57e4-efb7714eb429.png)
（画像引用：Figmaオフィシャルホームページより）

Figma は、ブラウザベースのコラボレーション・インターフェース・
デザイン・ツールです。MacOSまたはWindows用の
デスクトップアプリケーションでオフラインでも利用できます。

## figmmの良かったところ(個人的に)
* ブラウザ上で手軽に利用できる
* 無料プランがある
* 図形描画ツールやテキストツールなど、デザイン制作に必要なツールや
機能が一通りある。

初めて使用したので探る探りではありましたが、操作性もよく一通りの
デザインなどができたため良かったと思いました。


## 最後に
今回作成したワイヤーフレームを一部公開しようと思います！
(出し惜しみする程の物でもないですが。。。笑)

![スクリーンショット 2023-07-06 21.22.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3478070/f1634945-e40f-be05-3577-baff08eeb929.png)


今回はpcサイズのフレームを使用し作成しました。
スマホサイズなどもありますので自身の目的によって選択して下さい！
npm install エラー

# はじめに  
「[サバイバルTypeScript](https://typescriptbook.jp/)」を使ってTypeScriptの勉強をやろうと思い、チュートリアルに従って準備をしていたところ、`npm install`でエラーをもらったので、解消方法をメモ。  
## 出てきたエラーの冒頭  
> npm ERR! code ECONNREFUSED
> npm ERR! syscall connect
> npm ERR! errno ECONNREFUSED

的なメッセージが冒頭に出現し、それ以降は`err`みたいなやつがいっぱい出てきました。  

## 解消方法  
このエラーに関する解消方法を記載している記事がたくさんあったのですが、それらの記事に共通して**IPv6による接続**が原因で発生しているらしいことを把握しました。どうやら、IPv4で接続するようにしないといけないみたいです。  
```
export NODE_OPTIONS=" --dns-result-order=ipv4first "
```
というわけで、上の1行をターミナル上で実行するだけで、無事`npm install`が実行できました！  
※[こちらのページ](https://github.com/nodejs/help/issues/1887)で記事を見つけました。

# おわりに  
興味が消え失せないうちに、TypeScriptを勉強します。
AWS素人の学習ロードマップ【初学者向け】

学習期間　約2週間
AMIに ``Amazon Linux 2023`` 使用
RailsAPIを独自ドメインでアクセス、SSL化しデプロイできるようになるまで

## 学習教材
[AWS：ゼロから実践するAmazon Web Services。手を動かしながらインフラの基礎を習得](https://www.udemy.com/course/aws-and-infra/ )
- 最新画面に追従してませんが補足のpdfがついているのであまり困りませんでした。
- これやればドメインアクセス、デプロイまでできます。
## SSL化

- AWSの公式ドキュメント　ACMとELBを使用してSSL化

https://docs.aws.amazon.com/ja_jp/acm/latest/userguide/gs-acm-request-public.html

https://docs.aws.amazon.com/ja_jp/elasticloadbalancing/latest/application/application-load-balancer-getting-started.html

https://docs.aws.amazon.com/ja_jp/elasticloadbalancing/latest/application/create-https-listener.html

https://docs.aws.amazon.com/ja_jp/Route53/latest/DeveloperGuide/routing-to-elb-load-balancer.html

https://repost.aws/ja/knowledge-center/elb-redirect-http-to-https-using-alb



## デプロイ

https://zenn.dev/naoki_mochizuki/books/1471ce20222227
- 有料ですがかなり参考にさせていただきました。　

https://zenn.dev/ito110/scraps/81b8dd657a5c6b

## エラー解決にお世話になった記事

- デプロイの記事はAMIに ``Amazon linux2`` を使用していたのでこれらの記事を参考に対応していきました。

https://qiita.com/Bjp8kHYYPFq8MrI/items/734ac300c04e447edef3


https://qiita.com/hoken/items/db6c1c86bccf4ebf9f21

https://blog.interstellar.co.jp/2023/05/11/amazon-linux-2023-nginx-install/

https://qiita.com/sotanaka/items/a6fd4e9b2420aec2c4b7

https://youtu.be/fpgxATbPNw4

以上！！
Astroファイルのimport文をフォーマットするPrettierプラグインを作りました


## はじめに

Astroファイルのimport文をフォーマットするPrettierプラグインを作りました。

普段、TypeScriptファイルなどのimport文をフォマットするために[prettier-plugin-organize-imports](https://github.com/simonhaenisch/prettier-plugin-organize-imports)というPrettierプラグインを使用しています。Astroファイルに対しても同様の機能がほしいと思ったのがきっかけで作成しました。

https://github.com/ot07/prettier-plugin-astro-organize-imports

https://www.npmjs.com/package/prettier-plugin-astro-organize-imports

このプラグインを使用すると、以下のようにimport文がソートされます。

![](https://raw.githubusercontent.com/ot07/zenn-qiita-contents/main/images/prettier-plugin-astro-organize-imports/demo.gif)

## インストール方法

まずは、依存関係のあるパッケージとともに、プラグインをインストールします。

```shell
npm install -D prettier typescript prettier-plugin-astro-organize-imports
# or
yarn add -D prettier typescript prettier-plugin-astro-organize-imports
# or
pnpm add -D prettier typescript prettier-plugin-astro-organize-imports
```

次に、Prettierの設定ファイルを編集します。ここでは、`.prettierrc`を編集する例を記載します。

```json: .prettierrc
{
  "plugins": ["prettier-plugin-astro-organize-imports"],
  "overrides": [
    {
      "files": "*.astro",
      "options": {
        "parser": "astro"
      }
    }
  ]
}
```

Prettierは公式ではAstroファイルをサポートしていないため、`prettier-plugin-astro-organize-imports`が提供する`astro`パーサーを使用するように設定しています。

これでインストールは完了です。Prettierのフォーマットコマンドを実行すると、Astroファイルのimport文がフォーマットされるはずです。

:::note alert
`prettier-plugin-astro`などの他のAstroファイル用プラグインがインストールされている場合、プラグインの自動ロード機能によってエラーが発生する可能性があります。そのような問題を避けるためには、後述の**プラグインの自動ロードを無効にする**設定を行ってください。
:::

## 他のPrettierプラグインとの互換性

他にも、Astroファイルに対して機能するPrettierプラグインとして、以下のものがあります。

- `prettier-plugin-astro`
- `prettier-plugin-tailwindcss`

これらのプラグインと併用する場合には、以下のような設定が必要です。

- `prettier-plugin-astro-organize-imports`を最後に読み込む
- `pluginSearchDirs`オプションを`false`に設定し、プラグインの自動ロードを無効にする

```json: .prettierrc
{
  "plugins": [
    "prettier-plugin-astro",
    "prettier-plugin-tailwindcss",
    "prettier-plugin-astro-organize-imports"
  ],
  "pluginSearchDirs": false,
  "overrides": [
    {
      "files": "*.astro",
      "options": {
        "parser": "astro"
      }
    }
  ]
}
```

## 参考にしたPrettierプラグイン

#### `prettier-plugin-organize-imports`

`.js`, `.jsx`, `.ts`, `.tsx`, `.vue`ファイルのimport文をフォーマットするプラグインです。import文をフォーマットする処理を実装する上で参考にしました。

https://github.com/simonhaenisch/prettier-plugin-organize-imports

#### `prettier-plugin-tailwindcss`

Tailwind CSS用のプラグインで、クラスをソートするものです。他のPrettierプラグインと互換性を持たせる方法を参考にしました。

https://github.com/tailwindlabs/prettier-plugin-tailwindcss

#### `prettier-plugin-astro`

Astroファイルをフォーマットする公式プラグインです。

https://github.com/withastro/prettier-plugin-astro

## さいごに

Astroファイルのimport文をフォーマットするPrettierプラグインである`prettier-plugin-astro-organize-imports`を紹介しました。

Astroで開発をしている方は、ぜひ試していただけると嬉しいです。

https://github.com/ot07/prettier-plugin-astro-organize-imports
CloudformationのカスタムリソースでVPCLambdaを呼び出したときのエラーと対処法

## はじめに
Cloudformationでカスタムリソースを使用していた時に、エラーが発生しました。
調査をしていると、LambdaをVPCLambdaに変更したことが影響しているとわかり、
AWS公式ドキュメント等を見ながら対処しました。
その時のエラーや対処法を残します。

## 発生したエラー
カスタムリソースから呼び出したLambdaでは、
応答を返却する為のcfn-responseモジュールから403エラーが返却。

https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/cfn-lambda-function-code-cfnresponsemodule.html

また、Cloudformationの画面では、
CloudformationスタックのステータスがUPDATE_IN_PROGRESSのまま一時間スタック。
これは、CloudformationがLambdaからの応答をタイムアウトまで待機しつづける為に起こります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380272/76d2a617-679d-5b8c-7802-fffe5279214d.png)

一時間経過すると、ステータスがUPDATE_ROLLBACK_FAILLEDとなります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380272/61ba28ff-2519-c2d1-cc58-4e15b8669987.png)


## 原因
VPCLambdaからS3へアクセスする道が無かった為。

Cloudformationのカスタムリソースでは、応答をモニタリングするために
S3バケットが各リージョンに用意されており、S3バケットを介してやり取りしています。
その為、VPCLambdaがCloudformationにレスポンスを返却する際にS3を介するわけなのですが、
VPCからS3への道が無かった為に応答が届きませんでした。

## 対処法
S3用VPCエンドポイントのポリシーに、カスタムリソース用のS3バケットを追加し許可する。
```json
"arn:aws:s3:::cloudformation-custom-resource-response-region"
```
regionは使用しているリージョンに変更します。


https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/cfn-vpce-bucketnames.html

## 補足
Lambdaの変数eventの内容を確認すると、
Cloudformationから渡されたパラメータが格納されています。
この中には、ResponseURLというパラメータが含まれておりここにS3の情報が入っています。



## おわりに
エラーが出て初めて、カスタムリソースがS3を使用していることを認識。
カスタムリソースの何とかして動かしてます感すごい。
EC2へのNode.jsインストール時のエラー 'GLIBC not found'

## はじめに
AWS EC2内の環境設定中、Node.jsのインストールでタイトルにあるエラーが出て詰まったのでまとめました。

OS：Amazon Linux 2

## エラー内容

nvmでNode.js（LTS版：18.16.1）インストール後、バージョン確認をしたところ、下記のエラーが表示されました。

```
node: /lib64/libm.so.6: version `GLIBC_2.27' not found (required by node)
node: /lib64/libc.so.6: version `GLIBC_2.28' not found (required by node)
```

<br>

Node.jsが実行されるために必要なGNU Cライブラリ（glibc）のバージョンがシステム上に存在していないとのことでした。

より具体的には

- `/lib64/libm.so.6: version GLIBC_2.27' not found (required by node)`
    - libm.so.6というライブラリに対して、バージョンGLIBC_2.27が必要で、それが見つからない。
- `/lib64/libc.so.6: version GLIBC_2.28' not found (required by node)`
    - libc.so.6というライブラリに対して、バージョンGLIBC_2.28が必要で、それが見つからない。

というエラーでした。

## 解決策


- システムのglibcをアップグレードする
- より古いバージョンのNode.jsをインストールする 

私は、1つ前のNode.js LTS版(16.20.1)をインストールして解決しました。


## おわりに
記事執筆中に気が付いたのですが、AWS公式に、Node.js の現在の LTS リリース (バージョン 18.x) をサポートしていないとのアナウンスがありました。
手順 4. の警告をご参照ください。

[チュートリアル: Amazon EC2 インスタンスでの Node.js のセットアップ](https://docs.aws.amazon.com/ja_jp/sdk-for-javascript/v2/developer-guide/setting-up-node-on-ec2-instance.html#setting-up-node-on-ec2-instance-procedure)



<br>
<br>

最後までお読みいただきありがとうございます。
記事内に不備がありましたら遠慮なくご指摘いただけると嬉しいです。


## 参考
[Node.jsリリース一覧](https://nodejs.org/ja/download/releases)
Gmailをslackチャンネルに転送する

https://slack.com/intl/ja-jp/help/articles/206819278-Slack-%E3%81%AB%E3%83%A1%E3%83%BC%E3%83%AB%E3%82%92%E9%80%81%E4%BF%A1%E3%81%99%E3%82%8B#h_01F4WDZG8RTCTNAMR4KJ7D419V

そんなに頻度はないですが、毎回都度調べてるのでこちらのやり方をまとめようと思います。有料プランに限るようなのでご注意ください。

## slackチャンネル専用のメールアドレスを発行する

チャンネルの右上のメニューを開く

![スクリーンショット 2023-07-06 20.16.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254217/fc812316-abc1-8639-ea29-564a45cc6b08.png)
 
インテグレーションを選択してこのチャンネルにメールを送信するをクリック

![スクリーンショット 2023-07-06 20.20.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254217/b293327e-3a0d-8d96-3fc2-011d5d690900.png)

チャンネル専用のメールアドレスが発行されるのでコピーしておく

![スクリーンショット 2023-07-06 20.20.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254217/254e9e88-47d0-fe79-6c4e-cda48c9a518a.png)

## Gmailにて転送設定を行う

設定を開く
![スクリーンショット 2023-07-06 20.25.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254217/1cfb4f92-4fdb-f490-115b-1d496369a259.png)

すべての設定を表示を選択
![スクリーンショット 2023-07-06 20.27.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254217/3a7cc644-7171-15e3-5a8e-69e3e7e2609a.png)

メール転送とPOP/IMAPタブを選択して、転送先アドレスを追加をクリック
![スクリーンショット 2023-07-06 20.29.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254217/c2fe9d6e-5104-2d5b-6b56-40a5c67fe2b5.png)

slackチャンネルで発行したメールアドレスを設定

![スクリーンショット 2023-07-06 20.32.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254217/04a79ba5-0076-f12f-af48-cbee0d6c4d6e.png)

確認コードが送られてくるので認証を行う
![スクリーンショット 2023-07-06 20.34.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254217/93dd286d-2ed2-523c-2a9f-437e53b14d4f.png)

認証が終われば完了！

## Gmailのフィルタリング機能を利用する

フィルタとブロック中のアドレスタブを選択して新しいフィルタを作成
![スクリーンショット 2023-07-06 20.39.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254217/530d1d50-b6ab-9c5a-74c8-49792cc2872d.png)

今回は特定のメーリスに届くものを指定したいのでToにアドレスを設定しました。
![スクリーンショット 2023-07-06 20.43.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254217/0263cf04-1ad0-617c-0765-d38eb6a467a0.png)


次のアドレスに転送するをチェックして、slackチャンネルで発行したメールアドレスを設定

![スクリーンショット 2023-07-06 20.45.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254217/7b14dd8e-2923-5f8b-29c0-da6a8a2566f9.png)


テスト送信
![スクリーンショット 2023-07-06 20.47.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254217/b1206cad-590d-9133-7c88-5cecb07c2230.png)

問題なし！

## 最後に運動通信社について
運動通信社は「日本を世界が憧れるスポーツ大国にする」というビジョンを達成するべく、一緒に働く仲間を募集しています！
PMやアプリエンジニア、Webエンジニアなど色んな職種を募集しておりカジュアル面談大歓迎なので是非採用フォームよりご連絡ください！
ぜひ一緒に、自分たちも世の中もワクワクするサービスを作りましょう！

https://sportsbull.jp/about/#recruit

Greenも活用中です

https://www.green-japan.com/company/9598
【JavaScript】string,hex,ArrayBufferの相互変換

# はじめに
string,hex,ArrayBufferを相互に変換したくなりました。
(hexは本記事では16進数の文字列という意味で用いています。)
ググってみると、Node用のBufferクラスを使ったサンプルが多かったので、関数を作成してみました。

# 注意
今回作成した変換する文字列の長さは1です。1より大きい文字列に対応する場合は自身で対応願います。
サンプルプログラムを見てもらえばわかりますが、
1byteの文字を変換する場合、str2buf、buf2hexでhexに変換すると '0033' になり、
str2hex で hexに変換すると '33' になります。
'0033'でも'33'でも元の文字に戻るのでこのままとしています。問題あれば修正して使ってください。
(個人的にはhexを得るときはstr2hexを呼ぶ)

# サンプルプログラム
4パターン書きました。

```html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>convert</title>
<script>
document.addEventListener('DOMContentLoaded', () => {
    const strJap = '山';
    const str = '3';

    // '山' -> str2buf -> ArrayBuffer -> buf2hex -> '5c71' -> hex2buf -> Arraybuffer -> buf2str -> '山'
    const buf00 = str2buf(strJap);
    const hex0 = buf2hex(buf00);
    const buf01 = hex2buf(hex0);
    const str0 = buf2str(buf01);
    console.log(hex0);
    console.log(str0);

    // '山' -> str2hex ->  '5c71' -> hex2str -> '山'
    const hex1 = str2hex(strJap);
    const str1 = hex2str(hex1);
    console.log(hex1);
    console.log(str1);

    // '3' -> str2buf -> ArrayBuffer -> buf2hex -> '0033' -> hex2buf -> Arraybuffer -> buf2str -> '3'
    const buf20 = str2buf(str);
    const hex2 = buf2hex(buf20);
    const buf21 = hex2buf(hex2);
    const str2 = buf2str(buf21);
    console.log(hex2);
    console.log(str2);

    // '3' -> str2hex ->  '33' -> hex2str -> '3'
    const hex3 = str2hex(str);
    const str3 = hex2str(hex3);
    console.log(hex3);
    console.log(str3);
});

/**
 * string to hex
 * @param {string} str string
 * @returns {string} hex
 */
function str2hex(str) {
    return str.charCodeAt(0).toString(16);
}

/**
 * hex to string
 * @param {string} hex hex
 * @returns {string} string
 */
function hex2str(hex) {
    return buf2str(hex2buf(hex));
}

/**
 * hex to buffer
 * @param {string} hex hex
 * @returns {ArrayBuffer} array buffer
 */
function hex2buf(hex) {
    hex = hex.padStart(4, '0'); // length to 4
    const array = [0, 1].map(i => parseInt(hex.substring(i * 2, (i + 1) * 2), 16));
    return new Uint8Array(array.reverse()).buffer;
}

/**
 * buffer to hex
 * @param {ArrayBuffer} buf array buffer
 * @returns {string} hex
 */
function buf2hex(buf) {
    const array = [...new Uint8Array(buf)].map(x => x.toString(16).padStart(2, '0'));
    return array.reverse().join('');
}

/**
 * string to buffer
 * @param {string} src string
 * @returns {ArrayBuffer} array buffer
 */
function str2buf(str) {
    return (new Uint16Array([].map.call(str, c => c.charCodeAt(0)))).buffer;
}

/**
 * buffer to string
 * @param {ArrayBuffer} buf array buffer
 * @returns {string} string
 */
function buf2str(buf) {
    return String.fromCharCode.apply('', new Uint16Array(buf))
}
</script>
</head>
<body>
</body>
</html>	
```

# 参考リンク
<a href="https://stackoverflow.com/questions/40031688/javascript-arraybuffer-to-hex">https://stackoverflow.com/questions/40031688/javascript-arraybuffer-to-hex</a>
<a href="https://gist.github.com/kawanet/352a2ed1d1656816b2bc">String と ArrayBuffer の相互変換 JavaScript</a>
ssh で接続するとランダムで背景色を変更する

最近ボケてて、ローカルのつもりでリモートを操作してあれれ? となることがたびたびあった。
よく使うリモートは背景などを設定しているが、使い捨ての設定などでも常に背景色を変更したい。


- ターミナルエミュレーター固有の設定は使用せず、なるべく標準の技術を使用する
- ssh コマンドに対してエリアスを設定して、操作に透過性を
- 色はランダムで決定
- ssh 終了時に復元する
- 視認性を下げないように、背景の輝度を低く

# 環境

- GNOME Terminal 3.44.0
  -  Ubuntu Linux 22.04 LTS GUI での標準ターミナル


であるが、xterm プロトコルを使用しているのでターミナルの種類に依存しないはず・・・だけれどもプロトコルサポート状況によってはダメなものもあるかも。


# sshrndcolor.sh

「ターミナルの情報をエスケープシーケンスで取得する」
https://qiita.com/nanbuwks/items/e691c3fd5f3f9a835a10

を用いて、以下のように作った。

```
restore_settings=$(stty -g) 
stty -icanon -echo
echo -en "\033]11;?\033\\"
read -d R -n 23 BGCOLOR
stty "$restore_settings"
BGCOLOR=`echo "$BGCOLOR" | tail -c +2`
OLDBGCOLOR=`echo "$BGCOLOR" | awk '{print substr($0, 11, 2) substr($0, 14, 2) substr($0, 19, 2)} '`
echo "$OLDBGCOLOR"
NEWCOLOR=`python3 -c "import random;print('{:06x}'.format(random.randint(0,100)*65536+random.randint(0,100)*256+random.randint(0,100)))"`
echo -e "\033]11;#${NEWCOLOR}\a"
ssh $@
echo -e "\033]11;#${OLDBGCOLOR}\a"

```

:::note
2024/5/14へんこ：
Ubuntu の初期設定に沿って、9行目の `python` を `python3` に変更しました。

:::


# 使用方法


- ` chmod 755 sshrndcolor.sh`
-  /usr/local/bin にコピー
- `$ echo alias ssh=/usr/local/bin/sshrndcolor.sh >> ~/.bashrc`
Misskeyの少し特殊なURLパス

Misskey v12.119.2 における少し特殊なURLパスを簡単に説明

:::note warn
あくまでコードを書かない鯖缶のために概要を記すものであり
正確な挙動を解説することを目的としていないのに注意されたい
（オープンソースソフトウェアはソースコード読むのが一番確実で手っ取り早い）
:::

インスタンスのドメインは`misskey.example`にしているので適宜変えてみて欲しい

### `/api`
投稿の作成・削除・タイムラインの取得などMisskeyインスタンスの各種APIを使用するための場所
結果はjsonで返ってくる
なおほとんどのケースにおいて`POST`で取得する必要がある、`GET`はダメ
ただ最近（Misskey v13付近から）は少しずつGET対応のエンドポイントが増えてきた
挙動は以下のコマンドのように確認可能
```
curl -X POST -H "Content-Type: application/json" -d '{}' https://misskey.example/api/ping
```

### `/url`
投稿に貼られたURLの概要を取得する場所
結果はjsonで返ってくる
この機能だけをMisskey本体から独立させたサマリー（概要）プロキシというものもある
https://github.com/mei23/summaly
挙動は以下のコマンドのように確認可能
```
curl -X GET "https://misskey.example/url?url=https://misskey.example"
```

### `/files`
オブジェクトストレージ不使用の設定の場合の
Internal Storageのファイルを取得する場所
実態ファイルはMisskeyのディレクトリの`files`フォルダーのものであるが
Nginx等のリバースプロキシとは異なるため、Misskeyアプリケーション以外へプロキシしてはいけない
まずDBに問い合わせしてドライブに存在しないファイルなら、実態ファイルは存在しようが`404`とDummyイメージを返し、
DBに存在する場合で、リクエストが画像の場合は適切な形式・サイズに変換し、動画の場合はサムネイルを生成し、
リモートファイルなら一時保存するなりして、`Content-Type` / `Cache-Control` などをセットして配信
なんらかの理由によりファイルシステムから読み出せないなら全て`500`で返す

### `/proxy`
主に外部のインスタンスのメディアファイルを取得する場所
この機能だけをMisskeyから独立させたメディアプロキシというものもある
https://github.com/atsu1125/media-proxy
（地味に宣伝）
挙動は以下のようなURL（`misskey.example`は適当なインスタンスに置き換え）にブラウザでアクセスすることなどで確認可能（Googleのエラー画像が取得できるはずである）
```
https://misskey.example/proxy/image.webp?url=https%3A%2F%2Fwww.google.com%2Fimages%2Ferrors%2Frobot.png
```

### `/note` `/users` `/@user`
言わずもがな、ノート・ユーザーを取得する場所なのだが…
ActivityPub特有の問題で、かなりややこしい！
たとえばリクエストが`Accept: application/activity+json`の場合にはjson形式でノート・ユーザー情報を返すが
リクエストが`Accept: text/html`の場合にはhtml形式でノート・ユーザー情報を返す
つまり同じパスなのにリクエストに含まれるAcceptヘッダーの内容の違いで全然違うものが返ってくる
これは連合先からノート・ユーザーが参照された場合にサーバーにはjsonで返さないと読めないからである
そのため不適切なプロキシが構成されている場合、ブラウザでユーザーページを開いたら急にブラウザにjsonが出てきてびっくり！とかある
なお全て`GET`で取得する必要がある、`POST`はダメ、意外

以下のコマンドで挙動を試せる

サーバーとして`@user`を取得する版
```
curl -H "Accept: application/activity+json" \
     -X GET "https://misskey.example/@user"
```

ブラウザとして`@user`を取得する版
```
curl -H "Accept: text/html" \
     -X GET "https://misskey.example/@user"
```

### `/api-doc`
ブラウザでアクセスするとAPIのドキュメントが見れる場所

### `/cli`
12.73.0以降のバージョンに存在する
一旦インスタンスにログインしてその状態で`/cli`に飛ぶとテキストのみのホームタイムラインと、公開範囲がパブリック連合ありで投稿される投稿フォームだけがある、すごく軽量なクライアントが起動する
災害時にデータ容量を食わないのでお得

### `/bios`
12.73.0以降のバージョンに存在する
LocalStorageの内容が編集できる、クライアントの設定などが入ってるので、起動しなくなった時に便利？
多分開発者くらいしか使いこなせない気がする

### `/nodeinfo/2.0`
Misskeyインスタンスの概要がjsonで手に入る
主に他のインスタンスが情報を取得するのに見る
ただとても軽いので人間が確認するのにも便利

### `/identicon`
v12から存在する
```
https://misskey.example/identicon/:userId
```
（userIdは`@username`ではなくユーザー情報にあるID）
初期で設定されていたユーザーのアイコンを取得できる

### `/`

他にもややこしいエンドポイントはいろいろあるが書き切れないので略
MathJaxのui/safeコンポーネントを使ったときのfont-sizeへの制限について調べた

:::note
MathJax v3.2.2
:::

MathJaxには、ui/safe componentがあり、hrefやstyleのフィルタリングや置換をしてくれます。

https://docs.mathjax.org/en/latest/options/safe.html

例えば`\style`を使ってfont-sizeに関して以下のように設定したとき、元のfont-sizeよりも大きくなるものの、一定のラインを上限として大きくならない制限がありました。
この制限はどの値を上限・下限としているのかを調べてみました。

~~~~
```math
\style{font-size:1000px}{Q}
```
~~~~

ソースコードを読んだところ、ここで処理をしていました。

https://github.com/mathjax/MathJax-src/blob/3.2.2/ts/ui/safe/SafeMethods.ts#L205-L223

```diff_typescript:ts/ui/safe/SafeMethods.ts#L205-L223
  /**
   * Filter a length value
   *
   * @param {Safe<N,T,D>} safe  The Safe object being used
   * @param {string} style      The style name being tested
   * @param {string} value      The value of the style to test
   * @return {string|null}      The sanitized length value
   *
   * @template N  The HTMLElement node class
   * @template T  The Text node class
   * @template D  The Document class
   */
  filterStyleLength<N, T, D>(safe: Safe<N, T, D>, style: string, value: string): string | null {
    if (!value.match(/^(.+)(em|ex|ch|rem|px|mm|cm|in|pt|pc|%)$/)) return null;
    const em = length2em(value, 1);
    const lengths = safe.options.styleLengths[style];
    const [m, M] = (Array.isArray(lengths) ? lengths : [-safe.options.lengthMax, safe.options.lengthMax]);
+   return (m <= em && em <= M ? value : (em < m ? m : M).toFixed(3).replace(/\.?0+$/, '') + 'em');
  },
```

そして具体的な値のデフォルト値はここに設定されていました。

https://github.com/mathjax/MathJax-src/blob/3.2.2/ts/ui/safe/safe.ts#L148

```js:ts/ui/safe/safe.ts#L148
    //
    //  CSS styles that are lengths needing max/min testing
    //    A string value means test that style value;
    //    An array gives [min,max] in em's
    //    Otherwise use [-lengthMax,lengthMax] from above
    //
    styleLengths: expandable({
      // 略
      fontSize: [.707, 1.44]
    })
```

結論、font-sizeは `0.707em` 〜 `1.44em` の間に収まってくれそうです。

今回はfont-sizeのみを確認しましたが、他にもui/safe componentは他にも色々な属性のフィルタリングを行ってくれています。
FigmaのPaddingとかが表示されなくなった

# 答え

`option` キー押すと出てくる。

![画面収録-2023-07-06-19.33.55.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/413699/653c19af-588d-6e18-cd57-c79a3fe6eb96.gif)

## 原因

わからない。突然出なくなってしまった。
Figmaそのものに『表示/非表示』の設定があるんでしょうか？
知ってる方いたら教えてください。
テスト98

```vb

'フォーム1にあるボタン
Private Sub 処理_Click()

'フォームを表示してユーザーのクリックを受け付けなくする
DoCmd.OpenForm "フォーム2", acNormal, , , , acWindowNormal

Stop

'いろいろメインの処理

Stop

'フォームを閉じる
DoCmd.Close acForm, "フォーム2", acSaveNo

End Sub

'--------------------------

フォーム2
Private Sub Form_Load()
DoCmd.MoveSize , , 10 * 567, 10 * 567
End Sub
(rails)Strong Parametersについて

# Strong Parametersとは
trong Parametersは、コントローラ内で使用されるパラメータを安全に扱うための機能です。
この機能は、アプリケーションに対するユーザーのリクエストからのパラメータ(※1)が、指定された許可リストに従ってフィルタリングされることを保証します。つまり、開発者が許可したフィールドのみがMass Assignment(※2)できるようになり、意図しないフィールドへの不正な代入を防ぎます。

(※1)パラメータは、関数やメソッド、プロシージャに渡される変数のことを指します。
(※2)Mass Assignmentは、Ruby on RailsなどのWebフレームワークで使われる機能の一つで、一度に複数のモデルの属性を設定できる機能を指します。

# なぜ、Strong Parametersが必要なのか？
Strong Parametersがなければ、悪意のあるユーザーはフォームに直接パラメータを注入し、予期しないフィールドを変更する可能性があります。これはデータの破壊や、セキュリティの侵害に繋がる可能性があります。
例えば、google chromeの開発者ツールよりHTMLを操作して、名前やメアドなどを簡単に書き換えてしまうことができます。
なので、Strong Parametersを適切に設定することで、Railsはリクエストされたパラメータをチェックし、許可されたフィールドのみを更新します。これにより、上記のような管理者フラグを含む重要なデータを不正に操作されるリスクを防ぐことができます。
![strong-parameter-1024x576.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3125392/4545de8e-36fc-1310-3f8b-f1a2a9b813ca.png)


# 具体的なコードとその解説について
![スクリーンショット 2023-07-06 20.31.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3125392/a455fb4f-1c9e-9766-e1e9-2d6a8c646762.png)

・まず上記のコードはRailsのStrong Parametersを使用してユーザーデータの更新を制御しているコードです。

では順に解説にしていきます。

1.User.find(params[:id]).update(user_params)：ここでは、まずparams[:id]を使って特定のユーザーをデータベースから見つけ出します。

2.update(user_params)を呼び出すことで、このユーザーの情報を更新します。
(※)user_paramsは次に説明するStrong Parametersによるフィルタリングメソッドです。

3.params.require(:user).permit(:name, :email)：この行がStrong Parametersの本質的な部分です。require(:user)は送られてきたパラメータの中に:userというキーが存在することを確認します。存在しない場合はエラーが発生します。

4.permit(:name, :email)は:userの下のパラメータの中で、:nameと:emailのみを許可します。これにより、これら以外のパラメータ（例えば:adminフラグなど(※1)）が送られてきた場合でも、それらは無視されます。
(※1)adminフラグとはユーザーが管理者（admin）かどうかを示すフィールドのことです。もし、誤って操作されると、ユーザーが不正に管理者権限を得るリスクがあるため、更新は厳重に制御される必要があります。


このように、Strong Parametersを使用することで、許可されたパラメータのみを安全に受け取ることができ、意図しないデータの更新を防ぐことができます。


コード中にあるprivateはメソッドがそのクラス内からのみ呼び出せるようにする指定子です。つまり、そのクラスの外部からはアクセスできません。
例えば、privateキーワードの下で定義したメソッドは、そのクラス自身やサブクラスから呼び出すことができますが、クラスの外部から直接呼び出すことはできません。これにより、メソッドの利用範囲を制限して、コードの安全性と整理を保つことができます。
Railsのコントローラーでは、内部でのみ使用するメソッド（たとえば、パラメータの処理など）をprivateとして定義するのが一般的です。
ちゃんと理解するSolidityのCallメソッドとインターフェイス（Solidity) #2

## はじめに

前回の記事では、Solidityの`call`メソッドについて詳しく解説しました。今回は、対照的に関数の通常の呼び出し方法について詳しく見ていきます。これらはよく似ていますが、利用方法と特性が異なります。

## セクション1: 通常の関数呼び出しとは何か？

通常の関数呼び出しはSolidityの抽象化を活用したもので、具体的な関数名と引数をコードに明示的に書きます。それにより、EVM（Ethereum Virtual Machine）がそれを解釈して適切な関数を呼び出します。これはコードの可読性と保守性を高めますが、一方で関数名や引数を動的に変更することはできません。

## セクション2: Ethereumアドレスとスマートコントラクトの関数

Ethereumのアドレスは基本的に20バイトのデータで、特定のスマートコントラクトまたは外部所有アカウント（EOA）を指します。しかし、このアドレス自体はただの数値であり、それが何を指しているのか（例えばスマートコントラクトであるならそのコントラクトにどのような関数が存在するのか等）の情報を含んでいません。

つまり、あるアドレスが示すコントラクトがどのような関数を持っているのかは、アドレスだけからはわかりません。Ethereumのスマートコントラクトは、外部から見ると単に一つのアドレスであり、その内部にどのような関数が存在するかは見えません。

したがって、スマートコントラクトの関数を呼び出すためには、まずそのコントラクトの型（またはインターフェース）を明示的に指定する必要があります。これを行うためにアドレスをキャストします。たとえば、あるアドレスがContractBを指していると知っている場合、そのアドレスをContractB型にキャストすることで、そのアドレス（つまりそのコントラクト）が持つ関数をSolidityから直接呼び出すことができます。

## セクション3: インターフェイスを使用した関数呼び出し

以下に、インターフェイスを用いた通常の関数呼び出しのコードを示します。

```solidity
pragma solidity ^0.8.6;

import "./IContractB.sol";

contract ContractA {
    IContractB public contractB;

    constructor(address _contractB) {
        contractB = IContractB(_contractB);
    }

    function callSetNumber(uint256 _number) public {
        contractB.setNumber(_number);
    }
}

pragma solidity ^0.8.6;

interface IContractB {
    function setNumber(uint256 _number) external;
}
```

このコードでは、`ContractA`は`IContractB`というインターフェイスを介して`ContractB`の関数を呼び出します。これは、`ContractA`が`ContractB`の特定の関数（この場合は`setNumber`）にのみアクセスすることを保証します。

もっと詳しく説明すると、`IContractB(_contractB)` は、Solidity での型キャストの一例です。この文脈では、`_contractB` のアドレスを `IContractB` として扱うよう指定しています。

`IContractB` はスマートコントラクトのインターフェースで、定義されている関数を呼び出すためには、まずそのインターフェースを実装したスマートコントラクトのアドレスを知る必要があります。そのため、ContractA のコンストラクターに渡されるアドレス `_contractB` を `IContractB` 型にキャストしています。これにより、ContractA はこのアドレスに対して `IContractB` インターフェースで定義された関数 `setNumber` を呼び出すことができます。

この型キャストは、`_contractB` が実際に `IContractB` のインターフェースを実装していることを確認するものではなく、単に呼び出し元がそのインターフェースに従ってメソッドを呼び出すことを可能にするものです。したがって、`_contractB` が実際に `setNumber` という関数を持っていることを保証する責任は、このコードを使用する開発者にあります。
## セクション4: インターフェイスを使用した関数呼び出しのメリット

インターフェイスを用いた関数呼び出しは、`call`メソッドと比較して以下のような利点があります：

1. 関数の存在とそのシグネチャの確認：コンパイラが存在とシグネチャをチェックするため、呼び出し先の関数が存在することと、その引数の型と数が一致していることが保証されます。

2. 可読性と保守性：コードはより明確で読みやすくなり、新しい開発者がプロジェクトに参加したときや、コードの保守が必要なときに理解しやすくなります。

3. 安全性：`call`メソッドとは異なり、関数の呼び出しは一連のエラーチェックを通じて安全性が向上します。

以上が、通常の関数呼び出しとインターフェイスを用いた関数呼び出しの方法とその特性についての説明です。
React18→React17にダウングレードした際に起こったエラーを解決した話

https://qiita.com/Ryo_Kitamoto/items/88e9d013b6a64235ee8b

## 前回の記事の続きになります


こちらの動画を参考にさせていただきます
>https://www.youtube.com/watch?v=MzJkWO73S70&list=PLX8Rsrpnn3IVOk48awq_nKW0aFP0MGpnn

## 開発環境
OS:mac
エディタ：vscode


### 開発途中でReact１８からReact１７にダウングレードしたためにいろんなエラーが発生した

#### エラー発生

```ターミナル:エラー文
Module not found: Error: Can't resolve 'react-dom/client' in '/User/~~/chatbot-demo/src'
```

ぐぐってみるとダウングレードに伴ってモジュールのパスがみつかっていないということらしい。

のでルートディレクトリに以下のコマンドを叩いてみた
```ターミナル:ターミナル
$ npm install react-dom@17.0.2
```

インストール完了後にnpm startすると同じエラーが発生したので
前回の記事に引き続き
・package-lock.json
・node_modules
を削除してnpm installしてみる

（このあたりのエラーはpackage-lock.jsonとnode_modulesを削除してnpm installすると解決することが多いらしいので同じようなことでお困りの方は試してみるのがいいかも？）

しかし、またもやエラーに。


### 原因と解決方法
React１８とReact１７ではindex.jsの記述方法に違いがあったため
src/index.jsの記述をReact１７に合うように変更する必要があるらしい

```js:index.js
import React from 'react';
// import ReactDOM from 'react-dom/client';　←React18では必要
import {render} from 'react-dom'; //←React17ではこちら
import './assets/styles/index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';

// const root = ReactDOM.createRoot(document.getElementById('root')); React18で必要
const root = document.getElementById('root'); //←React17ではこちら
render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  root
);
```

コメントアウトしているところがReact１８で必要な記述方法で
React１７で必要な記述方法はその直下に書いてみた。

無事エラーを吐かずにダウングレードに伴うエラーを解決できた。

## 所感
開発途中でバージョンをかえるというのは色々な依存関係で問題が起きそうで怖かったですが
次々にでてくるエラーを一つ一つ向き合って解決できたのは初心者エンジニアとして一歩成長できた気がします。
シートのコピー時に名前の定義が重複してウザいので解消してアドイン化する


## 目的
シートのコピー時に名前の定義が重複してウザいことが度々あり、
毎回ググって解消するのも面倒なのでアドイン化する。
名前の定義の削除でファイル容量が半分以下になったりと動作が軽くなることもある。


## 数式タグから名前の管理を開く（Ctrl＋F3）
全削除する。

## 改善されない場合
名前の定義の非表示項目を削除する必要がある。


### 開発タブからマクロを開く（Alt＋F11）
挿入タブから標準モジュールを選択。
以下のコードを記述

```vb:VBA
Sub DeleteName()

Dim name As Object

For Each name In Names
    If name.Visible = False Then
    name.Visible = True
    End If
Next

End Sub
```
実行（F5）すれば非表示項目が表示されるので全削除する。

## アドイン化
新規でbookを開く。
上記と同じようにマクロを作成。
ファイルを上書き保存し、その時ファイルのフォーマットで「Excelアドイン(.xlam)」を選択。

## アドインの追加
今回はクイックアクセスツールバーに追加する。
Excel最上部の「⏏︎(←これの下向き版)」マークをクリック。
その他のコマンドを選択。
オプションが開くので、「コマンドの選択」プルダウンで「マクロ」を選択。
その中から作成したアドインを選択し追加する。
クイックアクセスツールバーに表示され、いつでも好きに実行できる。
週一でPython勉強会やってみる⑪

前回の続き

https://qiita.com/Waddy675/items/04d01efb7a7015b863cb

先週はやる気が起きずパラっと読んで終了。長続きさせるには休むことも必要だと思う！

## フォントの設定
文字サイズや太文字などの設定もできる。
```py
from openpyxl.styles import Font
cell.font = Font(
    size = 14,
    bold = True,
    italic = True,
    color = 'FFFFFF'
)
```

## 背景色の設定
塗り方と色を設定する
```py
from openpyxl.styles import PatternFill
cell.font = PatternFill(
    fill_type = 'solid',
    fgColor = 'FF0000'
)
```

エクセルを触るだけがPythonではないのでWebサイトを触ってみることにする
## Requestsモジュール
使い方例↓
```py
import requests

url = 'https://api.aoikujira.com/time/get.php'
result = requests.get(url)

print(result.text) # 現在時刻が表示される
print(resut.status_code) # ステータスコードが確認できる
```

ステータスコードは
`200`のほかに`301`、`302`、`400`などがある。
`404`はNot Foundで知ってる人は多のでは？

## 画像のダウンロード
画像もダウンロードすることができる。後々にやるが、入力なんかもできる。
```py
import requests

url = 'https://uta.pw/shodou/img/3/3.png'

response = requests.get(url)

with open('gyudon.png', 'wb') as fp:
    fp.write(response.content)
```
`wb`は`w`の書き込み用という指定、`b`はバイナリモードという指定になる。
ちなみに`fp`って何でしょうか・・・

「教えてChatGPT先生！！」
> "fp" は、Pythonでファイルを扱う際によく使われる with open 文で使用される変数名の一つです。"fp" は "file pointer" の略であり、ファイルを指し示すポインタを表します。

だそうです。fとしてる人もいるのでややこさある・・・

ちょっとはPythonで色々できるようになってきたかもしれない。
npm install @mui/stylesでのエラーを解決した話

## npm install @mui/stylesでのエラーを解決した話


こちらの動画を参考にさせていただきます
>https://www.youtube.com/watch?v=9xgbLe_1Czg&list=PLX8Rsrpnn3IVOk48awq_nKW0aFP0MGpnn&index=7

## 開発環境
OS:mac
エディタ：vscode


### エラー発生：npm install @mui/stylesができなかった


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/627182/534aa161-8b4b-d55a-48d5-6378e8dcca32.png)


エラー文をよくみて色々とぐぐってみたところ
React１８にまだ@mui/stylesがサポートされていないとのこと。


React１７ならサポートされているとのことでバージョンダウンを試みる。

package.json内を変更する



```json:package.json
  "react": "^18.2.0",
  "react-dom": "^18.2.0",
```
書き換える
```json:package.json
  "react": "^17.0.0",
  "react-dom": "^17.0.0",
```

再度npm installして
npm install @mui/stylesはエラーに。
npm install @material-ui/stylesをためしてみてもエラーに。


### 解決した方法

package-lock.jsonファイルの削除
node_modulesフォルダの削除
package.json内の@testing-libraryの項目３つを削除

再度npm install
npm install @mui/stylesを実行すると
エラーを吐かずに完了した。
AIパワーで論文要約が簡単に！ ChatGPTを使った落合陽一式論文要約アプリ

## 完成品


完成品: https://paper-summarize.streamlit.app/

デモ
![無題の動画-‐-Clipchampで作成-_2_.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/157596/fe9316e1-7eed-a1db-bbcf-dc2f20542123.gif)

読みたい論文のarXiv IDを指定することでGPTがその論文を要約してくれます。

## イントロダクション

皆さんは論文を読む際、どのような方法を使っていますか？
私は英語が苦手なので、まずはDeepLを使って和訳してもらい、アブストラクト→結論→その他の順で読んでいます。
この方法では論文の理解には問題ありませんが、1本の論文にかかる時間が多くなり、大量の論文を読む際には利用しづらいという問題があります。

何か良い方法はないかと調べた結果、「落合陽一式論文要約フォーマット」と「ChatGPTを利用した論文要約」が有望に思えました。
そこで、論文を指定するだけで、ChatGPTを使って落合陽一論文要約フォーマットに要約してくれるアプリを開発しました。

これにより、圧倒的な時間短縮が可能です！

## アプリの機能と使い方

使い方は非常にシンプルです。

1. arXivで読みたい論文を見つける
1. arXiv IDを本アプリに張り付ける
1. 「要約開始」ボタンを押下する

これだけで、落合陽一フォーマットで論文をまとめてくれます。

## 技術的な詳細

本アプリは大きく分けて以下の3つの技術で構成されています。

* Streamlit
* OpenAI API
* LangChain

StreamlitはWebアプリケーションのフレームワークです。
フロントエンドもバックエンドもこれ一つで実装できる優れものです。
それなりの規模のアプリの場合はJamstackのほうがいいのですが、小規模アプリであればStreamlitのほうが高速に開発できました。
今回初めてStreamlitを使いましたが、使いやすかったです。

みんな大好きなOpenAI APIはOpenAIの言語モデルを提供するWeb APIです。
本アプリでは、テキストの関連性を測定するためにEmbeddings、質疑応答をするためにgpt-3.5-turboを利用しています。

LangChainはLLMを利用したアプリの構築をサポートしてくれるライブラリです。
本アプリのほとんどの処理はLangChainにやってもらっています。
論文データのダウンロード、論文のベクトル化、要約の生成はLangChainが提供している機能を使っています。

## 課題と今後の展望

* プロンプトの改善
    * 現状は適当に考えたプロンプトのため、改善することでもっといい感じに要約してくれそう。
* 要約結果の一覧化
    * 当初は要約されたものを全て保存し、自由に検索できるようにするアプリを構想していた。
    * ただ、論文を要約し、それを公開するのはどうやら著作権的に問題があることが分かったため、今回は私的利用になるようにその場限りの要約になるようにしている。
    * なんとかして、一覧化をしたい
* GPT4の利用
    * 精度は上がるとは思うが、金。。。

## 結論

満足のいくアプリが開発できた。
Streamlit結構気に入った。

## 7/9追記

* プロンプトを更新しました。
    * 深津式プロンプトをベースにプロンプトを構築しなおしました。
    * 精度は上がった気がします。
    * 出力の文字数を制限する命令はしているのですが、あんまり守られず、長い出力が増えてしまいました。
* APIキーを更新しました。
    * なぜか使えなくなっていたので、新しいキーに更新しました。
ターミナルの情報をエスケープシーケンスで取得する


ターミナルのエスケープシーケンスは送るだけじゃなく、情報を取得することができる。

- `\033[6n` カーソル位置を取得
- `\033]11;?\033\\` 背景色を取得


これらは、それぞれ ctlseqs.txt 

http://ftp.xfree86.org/pub/XFree86/current/doc/ctlseqs.txt
の "Functions using CSI , ordered by the final character(s)" → "CSI Ps n  Device Status Report (DSR)." および、 "Operating System Commands" に記述されている。

:::note
2023/07/11 追加
読みやすい資料がこちらにありました。
「ctlseqs(ms)」
https://invisible-island.net/xterm/ctlseqs/ctlseqs.html
:::

しかしながら、これらを実行してもデータを受け取るにはどうしたらいいのかな?
画面に出力できても、リダイレクトやパイプが効かないので通常の方法では変数に入れるとかができない。
ファイル入出力でデータを受け取ることが必要。


# 環境

- Ubuntu22.04 LTS
 
# read でカーソル位置を取得

こちらから、
「shell - How to get the results from &quot;\033[6n&quot; in an sh script - Unix &amp; Linux Stack Exchange」
https://groups.google.com/g/comp.unix.programmer/c/lYOtNfBynbk?pli=1

```
stty -icanon -echo; echo -en "\033[6n"; read -d R -n 12 ESCPOS; stty "$x_TERM"; \
ESCPOS=`echo "$ESCPOS" | tail -c +3`; echo "$ESCPOS"
```

"x_TERM" が設定されていないエラーが出るために以下のように変更
```
restore_settings=$(stty -g) ; stty -icanon -echo; echo -en "\033[6n"; read -d R -n 12 ESCPOS; stty "$restore_settings"; ESCPOS=`echo "$ESCPOS" | tail -c +2`; echo "$ESCPOS"
```

好きなのでワンライナーにしたが、普通のシェルスクリプトにしたら以下の通り。
```
restore_settings=$(stty -g) 
stty -icanon -echo
echo -en "\033[6n"
read -d R -n 12 ESCPOS
stty "$restore_settings"
ESCPOS=`echo "$ESCPOS" | tail -c +2`;
echo "$ESCPOS"
```


- `stty -icanon -echo` は 標準入力モードを使用不可、入力文字のエコーなし。
- `read -d R -n 12 ESCPOS` は12バイト読み出してESCPOSに格納。
- `tail -c +2` は2文字目から末尾までを表示。どうやら1文字目に esc 文字が入っているためこれをしないとうまく表示できなくなる。

## 実行結果
```
[16;1
```
# 背景色を取得

ワンライナー版
```
$ restore_settings=$(stty -g) ; stty -icanon -echo; echo -en "\033]11;?\033\\"; read -d R -n 23 BGCOLOR; stty "$restore_settings"; BGCOLOR=`echo "$BGCOLOR" | tail -c +2`; echo "$BGCOLOR"
```
シェルスクリプトファイル版
```
restore_settings=$(stty -g) 
stty -icanon -echo
echo -en "\033]11;?\033\\"
read -d R -n 23 BGCOLOR
stty "$restore_settings"
BGCOLOR=`echo "$BGCOLOR" | tail -c +2`
echo "$BGCOLOR"
```
## 実行結果

```
]11;rgb:3030/0a0a/2424
```

# bash 以外のやり方

read はbash でないと使えないらしい。bash 以外でも使えるようにするためには、こちらのやりかたが良さそう。
「Terminal output with escape sequences: Any way to get the flags at a given position?」
https://groups.google.com/g/comp.unix.programmer/c/lYOtNfBynbk/m/tZKR530Qjj0J

```
old_settings=$(stty -g)
stty -icanon min 1 time 2 -echo
printf '\033[6n'
IFS='[;R'
set -f
set -- $(dd count=1 2> /dev/null)
stty "$old_settings"

```

一応 bash と sh で動作することを確認しています。
Rails 注文確認画面でリロードした際のエラー回避

# はじめに
- RailsでECサイトにあるような注文フォームを作成
- 注文確認画面と、注文確定画面でリロードをするとエラーになる
- コントローラーに記述することでエラーを回避する

# 前提条件
- Orderモデルがある（name,email,telephone,addressカラム）

# 回避したエラー例
![スクリーンショット 2023-07-06 17.50.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3348867/284f660a-d6aa-7cab-eb9b-9cd8a58e5c20.png)

エラーを見ると、order_paramsメソッド内でパラメータが欠落しているか空であるということがわかる。

# ordersコントローラーの記述
```ruby:corders_controllers.rb
#　省略

def confirm
    # パラメーターがあるか確認
    if params[:order].present?
      @order = Order.new(order_params)
    else
      # ユーザーがリロードした時
      return redirect_to new_order_path
    end
end

# 省略

def complete
    @order = Order.find_by(id: session[:order_id])
    
    # ユーザーがリロードした時
    return redirect_to new_order_path if @order.blank?
    
    # sessionの注文情報を削除
    session[:order_id] = nil
end

private

def order_params
    params.require(:order)
          .permit(:name,
                  :email,
                  :telephone,
                  :delivery_address)
end
```
# エラー対策
まず、注文確認画面では、注文フォームから送られてきたパラメーターが存在するかどうかを確認。もし、ユーザーがリロードした場合は、空になるため、再度入力フォーム画面へリダイレクトするようにする。
```ruby
def confirm
    # パラメーターがあるか確認
    if params[:order].present?
      @order = Order.new(order_params)
    else
      # ユーザーがリロードした時
      return redirect_to new_order_path
    end
end
```

次に、注文確定画面でも同じ対策を。ここでは、注文を確定する際(createアクションで)に、sessionメソッドによって、キーと値のペアでデータを保存している。そのため、ユーザーがリロードすると、@orderが空になるため、「if @order.blank?」なら入力フォーム画面へリダイレクトするようにする。
```ruby
def complete
    @order = Order.find_by(id: session[:order_id])
    
    # ユーザーがリロードした時
    return redirect_to new_order_path if @order.blank?
    
    # sessionの注文情報を削除
    session[:order_id] = nil
end
```
# まとめ
- 「入力→確認→確定」のような処理では、リロードするとエラーになることがある
- パラメーターがあるかどうかで条件分岐をすることで、エラーを回避できる

他にもっといい方法がないか、学習していきます！
⚠️学習4ヶ月目の初学者による投稿です。
⚠️間違いがあるかもしれません。ご容赦ください。
⚠️ご指導、ご教授いただけると幸いです。
ちゃんと理解するSolidityのCallメソッドとインターフェイス（Solidity) #1

## はじめに

Ethereumのスマートコントラクト開発言語Solidityには、非常に強力で、使い方によってはリスキーな機能が含まれています。その一つが`call`メソッドで、今回の記事ではこのメソッドの詳細と使用例について深掘りしていきます。

## セクション1: `call`メソッドとは何か？

`call`メソッドは、Ethereum Virtual Machine（EVM）に直接指示を送る低レベルの関数呼び出し方法です。つまり、Solidityの抽象化を一部飛ばし、直接バイトコードレベルで関数を呼び出すということを可能にします。この`call`メソッドにより、関数名や引数を動的に指定することが可能となり、特定の条件下では非常に強力なツールとなり得ます。

しかし、このような強力なツールには注意が必要です。`call`メソッドは、リエントラント攻撃というリスクを孕んでいます。これは、呼び出し元の関数が終了する前に、呼び出し先のコントラクトが再度呼び出し元の関数を呼び出し、意図しない状態の変更や資金の移動を引き起こす可能性がある攻撃手法です。

## セクション2: コード例

次に、具体的なコード例を見ていきましょう。以下のコードは、あるスマートコントラクト（ContractA）が別のスマートコントラクト（ContractB）の関数を呼び出す例です。

```solidity
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0 <0.9.0;

contract ContractB {
    uint256 public number;

    function setNumber(uint256 _number) public {
        number = _number;
    }
}

contract ContractA {
    ContractB public contractB;

    constructor(address _contractB) {
        contractB = ContractB(_contractB);
    }

    function callSetNumber(uint256 _number) public {
        (bool success, bytes memory data) = address(contractB).call(abi.encodeWithSignature("setNumber(uint256)", _number));
        require(success, "Failed to call setNumber");
    }
}
```

## セクション3: `call`メソッドの使い方

ContractAの`callSetNumber`関数では、ContractBの`setNumber`関数を`call`メソッドを使って呼び出しています。その際に、関数のシグネチャ（"setNumber(uint256)"）と引数（`_number`）を`abi.encodeWithSignature`メソッドを使ってエンコードし、それを`call`メソッドに渡しています。

Ethereumのアドレスについて理解することが、この`call`メソッドを理解する上で重要です。Ethereumのアドレスは基本的に20バイトのデータで、特定のスマートコントラクトや外部所有アカウント（EOA）を指します。しかしこのアドレス自体は、指しているコントラクトがどのような関数を持っているのかなどの情報を含んでいません。したがって、そのアドレスが具体的にどのコントラクトを指しているのかをSolidityに教えるために、アドレスを特定のコントラクト型にキャストする必要があります。

## セクション4: コードのデプロイ

最後に、このコードを実際に動かすための手順について説明します。RemixというオンラインのSolidity開発環境を開き、新しいファイルを作成して上記のコードを貼り付けます。その後、コンパイラとして適切なバージョン（この場合は0.8.x）を選択し、"Compile"ボタンをクリックします。そして"Deploy & Run Transactions"タブを開き、まずContractBをデプロイします。デプロイが成功すると、そのアドレスが表示されるので、そのアドレスをコピーしておきます。次にContractAをデプロイする際に、コンストラクタの引数としてこのアドレスを入力します。

以上がSolidityの`call`メソッドについての基本的な説明とその使用例です。この機能を理解し適切に使用することで、スマートコントラクトの作成がより柔軟でパワフルになります。次回はこの`call`メソッドと対比する形で、関数の通常の呼び出しについて詳しく見ていきましょう。
LlamaindexとLangchainの違いとは？

### 概要

LlamaindexとLangchainは，GPTのような大規模言語モデル（LLM）を活用するためのライブラリです．正直僕はLllamaindexしか使ったことがなく，それぞれの違いについてよく理解していなかったので，記事にします．

# Llamaindex

結論から言うと，**Lllamaindexは独自のデータを使ったQAチャットができるLLMを簡単に作成できるライブラリです**．Lllamaindexも内部的にはLangchainを使っているみたいです．

そもそも，LLMのカスタマイズするためのパラダイムには主に2種類があります．

1. LLMをFine-tuningする
2. 入力プロンプトにコンテキストを埋め込む

Llamaindexは後者である「入力プロンプトにコンテキストを埋め込む」を，より性能よく，効率よく，安価に行うために様々なデータ取り込みやインデックス化を実施することが可能です．

Llamaindexでは，以下のようなインデックス構造が提供されています．

| インデックス名 | 説明 |
| --- | --- |
| GPTListIndex | 単にNodeのリストを保持し，クエリ時は先頭から順次処理し，それぞれの出力を合成 |
| GPTVectorStoreIndex | 各Nodeに対応する埋め込みベクトルと共に順序付けせずに保持し，埋め込みベクトルを使用してNodeを抽出し，それぞれの出力を合成 |
| GPTTreeIndex | ノードをツリー構造にして保持し，クエリ時はRootから探索して，使用するノードを決め，その出力を合成 |
| GPTKeywordTableIndex | 各Nodeからキーワードを抽出し，キーワードに対するNodeをマッピングして保持し，クエリ時はクエリのキーワードを使ってNodeを選択し，それぞれのノードの出力を合成 |

時間があったら別のタイミングでindexの種類についても記事にしたいと思います．

**参考**

- [LlamaIndexを完全に理解するチュートリアル その1：処理の概念や流れを理解する基礎編（v0.6.8対応）](https://dev.classmethod.jp/articles/llama-index-insert-index/)
- [LlamaIndexのインデックスを更新し，更新前後で知識が変わることを確認してみる](https://dev.classmethod.jp/articles/llama-index-insert-index/)

## Langchain

**Langchainは，LLMをwebサービスや自作のAPI，プログラムの実行環境，ターミナルなどに接続するライブラリです**．

Langchainは，LLMにさまざまな機能を付け加えるのに便利なものです．Langchainでは以下のようなコンポーネントが提供されています．

| コンポーネント | 説明 |
| --- | --- |
| Models | OpenAIをはじめとした様々な言語モデル・チャットモデル・エンべディングモデルを切り替えたり、組み合わせたりすることができる |
| Prompt | プロンプトの管理・最適化・シリアル化などをすることができる |
| Indexes | PDFやCSVなどの外部データを用いて回答を生成することができる |
| Chains | 複数のプロンプト入力を実行することができる |
| Agents | 言語モデルに渡されたツールを用いて、モデル自体が次にどのようなアクションを取るかを決定し、実行し、観測し、完了するまで繰り返すことができる |
| Memory | ChainsやAgentsの内部における状態保持をすることができる |

**参考**

- [LangChainの概要と使い方｜サクッと始めるプロンプトエンジニアリング【LangChain / ChatGPT】](https://zenn.dev/umi_mori/books/prompt-engineer/viewer/langchain_overview)
- [ChatGPTとLangChainで何でもできるAIを作る](https://qiita.com/fuyu_quant/items/c0b29f037b7834c19e2c)
- [LangChain Documentation](https://docs.langchain.com/docs/)

## 最後に

Lllamaindex とLangchainの違いについて簡単に記事にしてみました．

記事にする前は，全く別々の存在なのかと思っていたら，実は兄弟みたいな関係だったんだと言うことに気がつきました．

誰かの役に立てれば幸いです．
paiza.ioでelixir その242

# 概要
paiza.ioでelixirやってみた。
練習問題、やってみた。

# 練習問題
BVHプレーヤーを作成せよ。

# 写真

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18104/96f868db-8c11-6a6a-d92a-23d0a61060d3.png)

# サンプルコード


```
defmodule Main do
    def load(url) do
IO.puts """
<!doctype html>
<html>
  <head>
    <script src="https://unpkg.com/three@0.127.0/build/three.js"></script>
    <script src="https://unpkg.com/three@0.127.0/examples/js/loaders/GLTFLoader.js"></script>
    <script src="https://unpkg.com/three@0.127.0/examples/js/controls/OrbitControls.js"></script>
    <script src="https://unpkg.com/three@0.127.0/examples/js/loaders/BVHLoader.js"></script>
    <script src="https://www.unpkg.com/@pixiv/three-vrm@0.3.6/lib/three-vrm.js"></script>
  </head>
  <body>
    <script>
const clock = new THREE.Clock();
let camera, 
	controls, 
	scene, 
	renderer,
	mixer, 
	skeletonHelper;
"""
IO.puts "const url = '#{url}';"
IO.puts """
const loader = new THREE.BVHLoader();
loader.load(url, function(result) {
	skeletonHelper = new THREE.SkeletonHelper(result.skeleton.bones[0]);
	skeletonHelper.skeleton = result.skeleton;
	const boneContainer = new THREE.Group();
	boneContainer.add(result.skeleton.bones[0]);
	scene.add(skeletonHelper);
	scene.add(boneContainer);
	mixer = new THREE.AnimationMixer(skeletonHelper);
	mixer.clipAction(result.clip).setEffectiveWeight(1.0).play();
});
function init() {
	camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000);
	camera.position.set(0, 200, 300);
	scene = new THREE.Scene();
	scene.background = new THREE.Color(0xeeeeee);
	scene.add(new THREE.GridHelper(400, 10));
	renderer = new THREE.WebGLRenderer({ 
		antialias: true 
	});
	renderer.setPixelRatio(window.devicePixelRatio);
	renderer.setSize(window.innerWidth, window.innerHeight);
	document.body.appendChild(renderer.domElement);
	controls = new THREE.OrbitControls(camera, renderer.domElement);
	controls.minDistance = 300;
	controls.maxDistance = 700;
}
function tick() {
	requestAnimationFrame(tick);
	const delta = clock.getDelta();
	if (mixer) 
		mixer.update(delta);
	renderer.render(scene, camera);
}
init();
tick();
    </script>
  </body>
</html>
"""
    end
end

Main.load("https://cdn.rawgit.com/perfume-dev/example-max/3f383158/example-bvh/A_test.bvh")



```

# 成果物

https://paiza.io/projects/Ro3TbLpKpLkfjAF5qfLTZA?language=elixir


以上。
Dynamoidでページネーションを実装する

# Dynamoidでページネーションを実現する
DynamoDBのRubyアプリケーション用のORMであるDynamoidを用いて、ページネーションを実現する方法を紹介します
## 実現したいコト
1. レコードを数件取得する
2. その続きのレコードをさらに数件取得する
3. 2を繰り返す

## レコード取得部分の実装
#### Userテーブルからレコードを10件取得する
```ruby
records, metadata = User.record_limit(10).start(nil).find_by_pages.first
```
#### 続きのレコードをさらに10件取得する
```ruby
records, metadata = User.record_limit(10).start(metadata[:last_evaluated_key]).find_by_pages.first
```

#### `records`
- 取得したレコードの内容が格納される

#### `metadata`
- 取得できるレコードに続きがある際に`last_evaluated_key`パラメータが追加される
- `start`に`last_evaluated_key`を指定することで、続きのレコードを取得できる

---
### where句を使う場合
実際に使う際は検索条件などを加えたい場合が多いと思う
#### Userテーブルからレコードを10件取得する
```ruby
records, metadata = User.record_limit(10).where(条件).start(nil).find_by_pages.first
```
#### 続きのレコードをさらに10件取得する
```ruby
records, metadata = User.record_limit(10).where(条件).start(metadata[:last_evaluated_key]).find_by_pages.first
```
---
より詳しい内容は[Dynamoid GitHub](https://github.com/Dynamoid/dynamoid)をご確認ください
Nuxt3+Vuetifyアプリ新規作成チート手順

## 検索HELL

フロントエンドのツールはどうしてこうメジャーバージョンアップで破壊的変更をするんでしょうか？

:::note warn
エラーが出て詰まる -> ググるかChatGPTに聞く -> 古いバージョンについてのもので役に立たない -> もう一度ググるかChatGPTに聞く
:::

のループを多くの人がやっているのではないかと思います。

## 動機

https://www.tactico.jp/

自分で作って運営しているWebサービスがあるんですが、これはVue2+BootstrapVueのSPAでした。
これをVue3に移行しつつ、ついでにSSRも対応してOGPも万全にしよう、というものです。

## 目標と結果

- Nuxt3(必然的にVue3)で、
- Vuetifyを使い、
- SSRで動かす

以下の手順で、2023年7月時点での話ですが、しっかり成功しました。ご参考にどうぞ。
Vuetifyにしたのは、BootstrapVueはVue3には対応していない（少なくとも簡単にはいかない）からです。（この部分は若干ガセネタの可能性がある）

## Appの作成

Node.jsやnpmは充分新しいバージョンを入れている前提です。
まず公式サイトの手順に従い, `npm create vuetify` を試みました。が、これはプレーンなVueを使う構成であってnuxtを使うようになってない。なのでこの路線はダメです。

https://vuetifyjs.com/en/getting-started/installation/

なのでNuxtの記載に従い, `npx nuxi@latest init <project-name>` からいきます。

検索すると, `npx create-nuxt-app` もNuxtアプリ作成コマンドとして見つかりますが、これはNuxt2用、かつ対話型のコマンドでアプリの構成を細かく聞いてきます。nuxiコマンドだと対話要素はなく、最小構成のアプリがポンとできます。

## インストール

```bash
npm install vuetify 
```

vuetifyを入れます。検索すると、vuetify@nextにしろ、みたいな話もありますがそれは古い情報です。普通のvuetifyでよろしい。

## 設定ファイルの構成

フロントエンドではいつもここが泣き所ですが、試行錯誤の末の結果のみ書きます。

```typescript:plugins/vuetify.ts
import { createVuetify } from "vuetify";
import * as components from "vuetify/components";
import * as directives from "vuetify/directives";

export default defineNuxtPlugin((nuxtApp) => {
  const vuetify = createVuetify({
    components,
    directives,
    ssr: true,
    theme: {
        defaultTheme: 'dark'
    }
  });
  nuxtApp.vueApp.use(vuetify);
});
```

まあthemeのあたりはお好みで。nuxiコマンドだとpluginディレクトリを作ってもくれないので注意。自分で作ります。

```typescript:nuxt.config.ts
// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
  devtools: { enabled: true },
  build: {    
    transpile: [/vuetify/]
  }
})
```

`transpile: ` のところが一番時間を食った。これがないと、SSRで実行した時点でvuetifyのCSSファイルが処理できず、
`Unknown file extension ".css"` のエラーが出る。

## まとめ

```bash
npx nuxi@latest init <project-name>
cd <project-name>
npm install vuetify
<plugins/vuetify.ts, nuxt.config.tsを上記のとおり構成>
<app.vueをvuetifyコンポーネントを使うようちょろっと修正>
npm run dev

<手元のブラウザで動作確認>
```

これで問題なければアプリ開発本体に入れます！