OmniAICreator/Qiita-1.07M huggingface dataset:
tailコマンドで新規作成ファイルを追って表示

# やりたいこと

新規に作成されるlogファイルを追跡して表示する。関数などは作らずにワンライナーで実現できるようにする。

# tailコマンドの基本的な機能 

tailコマンドでlogファイルを表示し続けるには

```
$ tail -f file.log

# 一旦file.logがリネームや消去をされて新たにfile.logが作成されても表示し続ける
$ tail -F file.log 
```

のどちらかを使えば事足りるが、別名で新たなファイルが作成されたときにそれらを追跡して表示し続けるにはtailコマンドだけで完結させるのは無理。

# tailコマンドで最新ファイルの表示

file1.log, file2.log ... などの新たな別名のファイルにログが追加されているときに一番最新のファイルを表示するには

```
$ tail -f $(ls -tr file*.log | tail -n 1)
``` 

でいい。しかし、`$(ls -tr file*.log | tail -n 1)`部分はコマンドを打ったときの一回のみ実行されるため、その後新たなファイルができたとしても追跡して表示はしてくれない。

- 参考: https://stackoverflow.com/questions/3416467/how-to-tail-f-the-latest-log-file-with-a-given-pattern


# 場合によってはダメなパターン

```
$ watch -n 60 -c "ls -tr file*.log  | tail -n 1 | xargs tail"
```

とすれば60秒ごとに監視して新しいファイルができた場合にそちらを表示する。watchコマンドの`-c`はテキスト内のカラー表示。概ねこれでやりたいことは達成できるが、ログファイルにtqdmなどでプログレスバーを表示させたり復帰の`\r`が入っていると普通の`tail -f`では正常に表示してくれてもこの方法だと表示が崩れる。

- 参考: https://stackoverflow.com/questions/34957817/tail-the-most-recent-file

# 常に良いパターン

```
$ while : ; do timeout 60 tail -f $(ls -tr file*.log | tail -n 1)  ; done
```

watchコマンド内でのtailコマンドは復帰がある場合の表示が崩れるので無限ループとtimeoutを組み合わせた方法を使うことにより、通常のtailコマンドの結果を表示できるので表示も崩れない。
グリッドレイアウトの基本的な使い方

# はじめに

今まではIEを考慮してflexboxなどで、レイアウト実装をすることが多かったですが、IEのサポート終了に伴い、これからはグリッドを積極的に使用していくことができます。
そこで改めて基本的な使い方を、まとめようと思います。
※厳密にはIE用のベンダープレフィックスを指定すると、使用できましたがこれからはその必要がなくなりました！


## グリッドレイアウトの仕組み

グリッドはレイアウトの親要素となるコンテナとレイアウト内の子要素で作られます。

```
//HTML
<div class="wrap">
  <div>1</div>
  <div>2</div>
  <div>3</div>
  <div>4</div>
</div>

//css
.wrap {
  display: grid;
}
```

## グリッドの列と行

grid-template-columnsプロパティで縦列を指定し、
grid-template-rowプロパティで横行を指定した場合、
2x2グリッドが作成できます。

```
//HTML
<div class="wrap">
  <div>1</div>
  <div>2</div>
  <div>3</div>
  <div>4</div>
</div>

//css
.wrap {
  display: grid;
  grid-template-columns: 100px 100px;
  grid-template-rows: 100px 100px;
}
```


以上グリッドの基本的な使い方を超簡単にまとめてみました。
この他にも便利な機能がたくさんありますので、また別の記事でもまとめたいと思います。
【powershell】フォルダ内のファイル数をカウントする

# はじめに
任意のフォルダにファイルが入っている場合はそのファイルに対して処理を行い、そうでない場合は処理を終了したい場面があったので、方法をメモ。

# 内容
`Get-ChildItem`コマンドレット、そして`Count`プロパティを組み合わせて簡単に判別できる。
```powershell:
# ファイルの有無を検証するフォルダを設定する。
$testFolder = 'C:\test';
# 変数にフォルダ内のファイルの数を代入する。
$fileCount = (Get-ChildItem $testFolder -File -Recurse).Count
```
# 詳細
`Get-ChildItem`コマンドレットはフォルダ内のファイル、フォルダを取得し返す。

オプションとして`-File`、`-Recurse`を指定している。それぞれ、ファイルのみを取得する（`-File`）、子フォルダ内を再帰的に検索する（`-Recurse`）オプションとなる。

その後、`Count`プロパティにて取得したリストの要素数、つまり今回の場合はファイル数を返却する。

# まとめ
powershellに関しては今まで全く触れてこなかったので、しばらくは初級の内容を投稿する可能性があるが、ご了承いただきたい。

# 参考文献
- [フォルダ内のファイル一覧を取得し、全てのファイルを処理する方法](https://bayashita.com/p/entry/show/228)
Next.js * GraphQL * Prismaで詰まった話(relation)

# TL;DR (結論だけを知りたい方はこちらからどぞ)

``` pages/api/graphql.ts
const resolvers = {
    query: {
        students: async(parent: undefined, args: {}, context: Context) => {
            return await context.prisma.student.findMany({
                includes: {
                    subjects: true
                }
            })
        }
    }

}
```

そうです。このIncludesがとっても味噌だったのですね。
チュートリアル的な記事がいっぱいあったのですが、リレーションを組んでいるのがなかったので、困ってました。

誰かの手助けになれば幸いです。
Editor拡張する際に躓いた点

この記事ではUnity2021.3.7f1を使っています。

私がUnityのEditor拡張の機能を試していた際に
躓いた点をまとめました。

また躓いたら、書き足そうと思います。

***
## ShowPopupなのに枠が表示される
下の例はShowPopupの使い方に誤りがあります。
さて、正しい使い方は？

~~~ cs
    //誤った例
    GetWindow<HogeWindow>.ShowPopup();
~~~
***

正解は

~~~ cs
    // 正しい例
    CreateInstance<HogeWindow>.ShowPopup();
~~~

GetWindow関数ではなく、<b>CreateInstance関数</b>を使って作成したインスタンスに対して、ShowPopup関数を実行する必要があります。

誤った例でも実行自体はできますが、通常のウィンドウの見た目になってしまいます。

***
## GUILayoutUtility.GetLastRect関数が機能しない
GUILayoutUtility.GetLastRect()関数は現在のEventのタイプがRepaintの場合にしか正常な値を返しません。

通常時は問題ないですが、Buttonの条件分岐と組み合わせると不具合が起きます。

ButtonがTrueを返す可能性があるのは現在のEventのタイプがmouseDownの時です。
(ちなみにButtonの判定をした後はUsedになる)

そのため、Buttonの判定時にGenericMenuやDropdownWindowを生成したい場合には、
Repaint時のGetLastRectの返り値をキャッシュする必要があります。

実際に実装すると下記のようになります。

~~~ cs
    private Rect buttonRect;

    private void OnGUI()
    {
        var isPressed = EditorGUILayout.DropdownButton(new GUIContent("DropdownButton"), FocusType.Passive);
        
        //EventType.Repaintの時にのみキャッシュする
        if (Event.current.type == EventType.Repaint)
        {
            buttonRect = GUILayoutUtility.GetLastRect();
        }
        
        if(isPressed)
        {
            //適当にGenericMenuを生成してます
            var menu = new GenericMenu();
            menu.AddItem(new GUIContent("テスト"), false, null);
            //buttonRectの場所にGenericMenuを表示
            menu.DropDown(buttonRect);
        }
    }
~~~

***
# ScriptableSingletonでCustomPropertyDrawerが機能しない

hideFlagsを弄る必要がある。
生成時に一度実行すればいいので、OnEnable関数内で実行する。
~~~ cs
private void OnEnable(){
    hideFlags &= ~HideFlags.NotEditable;
}
~~~

ScriptableSingletonのOnEnable()が実行されるのは、
Save関数の実行後であることにだけ注意。

## PropertyDrawer内ではEditorGUILayout,GUILayoutは機能しない
タイトル通りです。PropertyDrawerではLayout系は正しく機能しないことが多いです。

またPropertyDrawer内のGetPropertyHeightを弄る必要もあります。

これを弄らないと、クリックの判定であったり、他のPropertyの位置に不備が出ます。

# GUIStyleを使ってボタンのスタイルを変更する方法
GUI.skinにbuttonを含む、色々なUIオブジェクトのデフォルトGUIStyleが入っている。
そこからGUIStyleを複製して、好きなところを変更すると良い。
正規表現でログの情報を抽出・整形(awk編)

# はじめに

awkを使って、ログの一部の情報だけを抽出・整形して表示したい。

ここでは、apacheのアクセスログ`/var/log/httpd/access_log`から、
送信元IPとステータスコードを抽出することを目標にする。

# 内容

### ①サンプルになるアクセスログを用意する

```:access_log
123.123.123.123 - - [24/Feb/2022:10:03:34 +0000] "GET /favicon.ico HTTP/1.1" 404 196 "http://999.999.999.999:8080/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.82 Safari/537.36"
111.111.111.111 - - [24/Feb/2022:10:03:37 +0000] "GET /Carousel%20Template%20%E3%83%BB%20Bootstrap%20v5.0_files/bootstrap.bundle.min.js.%E3%83%80%E3%82%A6%E3%83%B3%E3%83%AD%E3%83%BC%E3%83%89 HTTP/1.1" 404 196 "http://999.999.999.999/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.82 Safari/537.36"
```

### ②awkを叩く

* `awk 'match($0, /正規表現/, a) {print a[0], a[1]}' ファイル名` が基本。
    * 正規表現内で`(hoge)`と書くことで、`()`内でヒットした文字列をキャプチャして配列`a`に保存することができる。`a[0]`はマッチした文字列全体をあらわし、`a[1]`以降にキャプチャした文字列が格納される。


* 記法は`awk 'フィールド {アクション}' ファイルパス`
    * 正規表現は`/[0-9+]/`のように`//`で囲って記載。
* match関数を用いる。引数はmatch(文字列, パターン, 結果格納先)
* `(^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+).*\[(.*)\] \"(.*)\" ([0-9]+) ([0-9]+) \"(.*)\" `でマッチングさせる。
    * awkでは使えないが(要出典)、判読しやすいように名前付きキャプチャで書くと以下。
      `(?<IP>^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+).*\[(?<Date>.*)\] \"(?<Request>.*)\" (?<StatusCode>[0-9]+) (?<Size>[0-9]+) \"(?<URL>.*)\" `
* `OFS = ", "`で区切り文字を", "にしてcsv形式で出力させている。
* 送信元IPとステータスコードだけをうまく抽出できた。

```
[ec2-user@ip-xxx-xxx-xxx-xxx log_bk]$ awk 'match($0, /(^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+).*\[(.*)\] \"(.*)\" ([0-9]+) ([0-9]+) \"(.*)\" /, a) && OFS = ", " {print a[1],a[4]}' access_log
123.123.123.123, 404
111.111.111.111, 404
```

* 特定IPからの通信に絞りたい場合は以下。
    * フィールドに`&& a[1]=="111.111.111.111"` を加える。
```
[ec2-user@ip-xxx-xxx-xxx-xxx log_bk]$ awk 'match($0, /(^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+).*\[(.*)\] \"(.*)\" ([0-9]+) ([0-9]+) \"(.*)\" /, a) && a[1]=="111.111.111.111" && OFS = ", " {print a[1],a[4]}' access_log
111.111.111.111, 404
```

# 補足
鯖缶の視点としては、不審なIPからの通信が成功してしまっているか判別する際に便利かもしれない。
上記のフィールドに`&& a[4]==200`を追加して引っかかってしまった日には・・・
The parameter 'pretrained' is deprecated since 0.13 and will be removed in 0.15, please use 'weights' instead.

# 以前の書き方

ResNet50 を転移学習するサンプルコードで、以下のように`pretrained`を引数に指定しているものがあります。

```python
from torchvision.models import resnet50

pretrain_model = resnet50(pretrained=True)
```

これは古い書き方で、v0.15以降では使えなくなります。v0.13.1時点では次のようなメッセージが表示されます。

> UserWarning: The parameter 'pretrained' is deprecated since 0.13 and will be removed in 0.15, please use 'weights' instead.

# 解決策

[公式のドキュメント](https://pytorch.org/vision/stable/models.html)を引用します。

```python
from torchvision.models import resnet50, ResNet50_Weights

# Old weights with accuracy 76.130%
resnet50(weights=ResNet50_Weights.IMAGENET1K_V1)

# New weights with accuracy 80.858%
resnet50(weights=ResNet50_Weights.IMAGENET1K_V2)

# Best available weights (currently alias for IMAGENET1K_V2)
# Note that these weights may change across versions
resnet50(weights=ResNet50_Weights.DEFAULT)

# Strings are also supported
resnet50(weights="IMAGENET1K_V2")

# No weights - random initialization
resnet50(weights=None)
```

古いサンプルコードと同じ結果を得たければ`IMAGENET1K_V1`を、精度の良い転移学習を行いたい場合は `IMAGENET1K_V1` または `DEFAULT`を選ぶのが良さそうです。
今回の場合は前者なので、下記が解決策となります。

```python
from torchvision.models import resnet50, ResNet50_Weights

#pretrain_model = resnet50(pretrained=True)
pretrain_model = resnet50(weights=ResNet50_Weights.IMAGENET1K_V1)
```
【webpack】webpack の導入

## 前提条件

以下手順を実施済み

[【npm】Nodejsプロジェクトの準備](https://qiita.com/onkz/items/de3ec36724ee8956922c)

## 1. パッケージのインストール

`webpack`本体と、`webpack`をCLIで実行するために`webpack-cli`をインストールします。

```cmd
> npm i -D webpack webpack-cli
```

## 2. エントリポイントの準備

`webpack`のデフォルト設定では、変換元となるファイルは`src`ディレクトリ内の`index.js`です。

```cmd
> mkdir src
> type nul > src/index.js
```

## 3. バンドルの作成

`webpack`の役割は、ウェブコンテンツを構成するファイルをまとめることです。特に、複数のJavaScriptファイルを一つにする機能は最たる例と言えます。

ここでは代表的な例として、関数だけを記述した[モジュール](https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Modules) `functions.js` を用意して、エントリポイント（`src/index.js`）で読み込みます。

```js:functions.js
export function hello(elm){
  elm.innerHTML = 'Hello World!';

  return elm;
}
```

```js:index.js
import { hello } from "./functions";

const elm = document.createElement('div');

document.body.appendChild(hello(elm));
```

ここまでで、`webpack`を試す最低限の準備が整いました。試しに、以下のコマンドを実行してファイルをビルドしてみます。

```cmd
> npx webpack
```

プロジェクトルート直下に、`dist/main.js`ファイルが作成されていれば成功です。

:::note warn
`webpack`は、`ES2015`で標準化された`import/export`構文を[トランスパイル](https://ja.javascript.info/polyfills#:~:text=transpiler%E3%81%AF%E3%80%81%E3%82%BD%E3%83%BC%E3%82%B9%E3%82%B3%E3%83%BC%E3%83%89%E3%82%92,%E3%81%99%E3%82%8B%E3%81%93%E3%81%A8%E3%81%8C%E3%81%A7%E3%81%8D%E3%81%BE%E3%81%99%E3%80%82)します。しかし、`ES2015`の他の機能は変換しないため、もし使用している場合、`webpack`の[ローダーシステム](https://webpack.js.org/concepts/loaders/)経由で[`Babel`](https://babeljs.io/)などのトランスパイラを使用する必要があります。
:::

## 4. ビルドしたファイルの動作確認

`main.js`を読み込むHTMLファイルを作成して、ブラウザで実行します。

```cmd
> type nul > dist/index.html
```

:::note
`VSCode`では、HTMLファイルに`html:5`と打ち込んでEnterを押すと、テンプレートが自動で挿入されます。
:::

```html:dist/index.html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <script src="./main.js"></script>
</body>
</html>
```

ブラウザに`Hello World!`と表示されれば問題ありません。

## 5. 構成ファイルの使用

プロジェクトルート直下に`webpack.config.js`ファイルを作成することで、`webpack`の挙動をカスタマイズすることが可能です。

```js:webpack.config.js
const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist'),
  },
};
```

では、作成した構成ファイルを使用してビルドを実行してみます。

```cmd
> npx webpack -c webpack.config.js
```

ビルドが成功すれば問題ありません。

:::note
`webpack`は、`webpack.config.js`ファイルが存在する場合、デフォルトでその構成を読み込みます。ただし、上記の用に`-c|--config`オプションを使用することで、任意の名前を使用することができます。
:::

## 6. ビルドコマンドをNPMスクリプトに追加

CLIに毎回コマンドを打ち込むのは、特にオプションをしている場合に非常に手間がかかります。そのため、[npmスクリプト](https://docs.npmjs.com/cli/v8/using-npm/scripts)を使用してショートカットを作成します。

```json:package.json
{
  "scripts": {
    "build": "webpack -c webpack.config.js"
  }
}
```

これで、ビルドを実行する際は`npm run build`と打ち込むだけで同様の結果が得られるようになります。
今年もやってきたデータベースNULL戦争

# 今年もやってきた

今年入社してきた新人が会社のシステムにも、社内のメンバーとの関係にも慣れ、自分の意見を積極的に言えるようになってきました。

そして、毎年のように議論されているあの話が、再びやってきました。

そう、**データベースのカラムの型に NULL を許すか** どうかです。

<img width="400px" alt="テーブル定義" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/801258/df625ea5-6891-70d5-3e4d-b55d7d48d4f7.png">

私は何回もこの話を話しているので、私の現在の意見をまとめておきます。

## 最初に結論

意味として NULL が正しい場合、NULL を許すことは **正しい** 。  
しかし SQL の世界は時の流れが遅く、まだ NULL の扱いは **危険** なままだ。

## NULL を許容することは **正しい**

主に新人たちが、こちらの意見を取ります。  

新人たちは、TypeScript や Rust といった、いわるゆ **NULL 安全** な言語を学んだ状態で入社、
あるいは会社の仕事でコードを書いています。

彼らは NULL を恐れません。人類はもう NULL を安全に扱えることを知っているからです。

新人たちは思います。なぜデータベースの世界では、 NULL を扱うべきなのに空文字などで代用しようとするのか？

アプリケーションとデータベースの持っている型の違いに彼らは苦しむのです。

> なぜ、正しくコードを書かないのか？と...

<img width="400px" alt="新人" src="https://3.bp.blogspot.com/-VTskDt5ZKjg/VkcajSmU9BI/AAAAAAAA0c8/bhpNvsUSmmw/s800/shinsyakaijin_man2.png">

## NULL を許容することは **危険**

主に古参メンバーが、こちらの意見をとります。  

古参のメンバーは昔から SQL の扱いに慣れており、 NULL を扱った操作で苦しい思いをしてきました。  
当時 NULL 安全といったアイデアはまだなく、たびたび期待通りに動かないバグに苦しみ、
その経験はやがて

> NULL という値を受け入れること自体がバグを発生する要因なのだ

という考えに辿り着きました。
彼らの苦しみは並大抵のものではなく、 [NULL撲滅委員会](http://mickindex.sakura.ne.jp/database/db_getout_null.html) なる恐ろしい会が立ち上がるほどでした。

...データベースを操作するための言語である SQL は、進化が非常に緩やかな言語です。  
**現状** 、SQLはNULL安全な言語になっていません。 SQLの操作においる NULL の扱いは、 **いまだに危険** なのです。  

そのため、名前が入力されていない状態も、彼らは NULL ではなく空文字などで代用し、ひと時の安寧を得ているのです。

> バグよ生まないでくれ！と...

<img width="400px" alt="新人" src="https://illustkun.com/wp-content/uploads/2018/08/illustkun-00859-backache.png">

## 弊社は今、どういう状態か

弊社は古いサービスを複数持っていることもあり、基本方針として **可能な限りNULLを許さない** という方針をとっています。  

## 私個人はどう思っているのか？

アプリケーション言語で SQL のクエリをラップし、安全なまま操作すればいいと思います。  

Rust とか TypeScript などにある NULL 安全な ORM を使うなりしてクエリを扱えば良いです。

新しいプロジェクトなら NULL を許容し、安全な言語を介して正しく書く方が生産性は高いと思います。
IntelliJ で半角スペースがやたらでかい四角で表示される

最近 IntelliJ 2022.2 にアップデートしたが、エディタ上の半角スペースが画像の様にやたらでかく表示される**ことがある。**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2183718/1696789a-2ae2-8fe8-6e92-c008ff439a4c.png)
(画像は後述の YouTrack の issue より引用)

**ことがある**、という部分が厄介で、最初は普通の表示(もっと小さくて薄い)なのだが、何かの拍子にでっかく濃くなってしまう。
特に、IntelliJ を起動したまま PC をスリープにして、復帰すると必ずこの状態になる。

こんなときは慌てず騒がず YouTrack の issue で報告されていないかを探す。
あった。
[Large bold square whitespaces after waking up the laptop / connecting display](https://youtrack.jetbrains.com/issue/IDEA-289794/Large-bold-square-whitespaces-after-waking-up-the-laptop-connecting-display)

[スリープから復帰したときに症状が出る](https://youtrack.jetbrains.com/issue/IDEA-289794/Large-bold-square-whitespaces-after-waking-up-the-laptop-connecting-display#focus=Comments-27-5866437.0-0)、というのを言っている人がいる。まさにそれ！
> Everything is fine until after the computer displays go to sleep and then wake up.

そして簡単なワークアラウンドが[ここ](https://youtrack.jetbrains.com/issue/IDEA-289794/Large-bold-square-whitespaces-after-waking-up-the-laptop-connecting-display#focus=Comments-27-6072457.0-0)に書いてあった！
> there is a quick fix - zoom the font and the problem disappears. So looks like the size of the dot is calculated too soon after resume

フォントのサイズを変えると治るとのこと。自分の環境では `ctrl` 押しながらマウスホイールでフォントを変えられるように設定しているので、これで治った！
この issue を見るまでは毎回 IntelliJ を再起動していたので、とても助かった。
3層にわたってoverlayするKustomize実装

### 実現したいこと
例えば本番環境(prd)かつバージョン2のアプリケーションの設定値をKustomizeでoverlayして、3層にわたって設定値を適用したい
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/481044/e93981b3-f69d-4bf6-0853-57751e61f16b.png)

### 環境
Kustomize Version: v4.5.4

### 実装
以下ユースケースで考える
**nginx DeploymentのCPU requestを本番環境では1000mにする  
また本番環境にデプロイするバージョン2のアプリケーションはカナリアリリースをしたいため、replicas 1でスタートしたい**

#### ディレクトリ構成
開発環境(dev)とversion1は空ディレクトリのみ用意してる
```tree:tree
.
├── base
│   ├── deployment.yaml..........A
│   └── kustomization.yaml.......B
├── components
│   ├── v1
│   └── v2
│       └── kustomization.yaml...C
├── dev
│   ├── base
│   ├── v1
│   └── v2
└── prd
    ├── base
    │   └── kustomization.yaml...D
    ├── v1
    └── v2
        └── kustomization.yaml...E
```

#### ファイル内容
A.
```yaml:base/deployment.yaml
# 通常のDeployment
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx
```
B.
```yaml:base/kustomization.yaml
---
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
  - deployment.yaml
```
C.
```yaml:components/v2/kustomization.yaml
# 本番環境と開発環境共通のversion2 label付与
---
apiVersion: kustomize.config.k8s.io/v1alpha1
kind: Component
nameSuffix: -v2
labels:
  - pairs:
      version: v2
    includeSelectors: true
```
D.
```yaml:prd/base/kustomization.yaml
# 本番環境ではCPU request　1000mにする
---
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
bases:
  - ../../base/
patches:
  - patch: |-
      apiVersion: apps/v1
      kind: Deployment
      metadata:
        name: nginx
      spec:
        template:
          spec:
            containers:
              - name: nginx
                resources:
                  requests:
                    cpu: "1000m"
```
E.
```yaml:prd/v2/kustomization.yaml
# version2はreplicas 1にする
---
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
bases:
  - ../base/
components:
  - ../../components/v2
replicas:
  - name: nginx
    count: 1
```

### Kustomize実行結果
```shell:shell
kubectl kustomize prd/v2
```
```yaml:実行結果
# CPU request 1000mとreplicas 3がどちらも適用されている
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    version: v2
  name: nginx-v2
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
      version: v2
  template:
    metadata:
      labels:
        app: nginx
        version: v2
    spec:
      containers:
      - image: nginx
        name: nginx
        resources:
          requests:
            cpu: 1000m
```
AtCoder Beginner Contest 263

## [A - Full House](https://atcoder.jp/contests/abc263/tasks/abc263_a)

シミュレートします。

```C++:C++
#include <bits/stdc++.h>
 
#define rep(i,n) for(int i=0; i<(n); ++i)
#define repx(i,x,n) for(int i=x; i<(n); ++i)
#define fixed_setprecision(n) fixed << setprecision((n))
#define execution_time(ti) printf("Execution Time: %.4lf sec\n", 1.0 * (clock() - ti) / CLOCKS_PER_SEC);
#define pai 3.1415926535897932384
#define NUM_MAX 2e18
#define NUM_MIN -1e9
 
using namespace std;
using ll = long long;
using P = pair<int,int>;
template<class T> inline bool chmax(T& a, T b){ if(a<b){ a=b; return 1; } return 0; }
template<class T> inline bool chmin(T& a, T b){ if(a>b){ a=b; return 1; } return 0; }

int main() {
    vector<int> A(5);
    rep(i, 5) cin >> A[i];

    sort(A.begin(), A.end());


    if((A[0] == A[2] && A[3] == A[4]) || (A[0] == A[1] && A[2] == A[4])){
        cout << "Yes" << endl;
    }else{
        cout << "No" << endl;      
    }

    return 0;
} 
```

## [B - Ancestor](https://atcoder.jp/contests/abc263/tasks/abc263_b)

dfsかループでシミュレートします。

```C++:C++
#include <bits/stdc++.h>
 
#define rep(i,n) for(int i=0; i<(n); ++i)
#define repx(i,x,n) for(int i=x; i<(n); ++i)
#define fixed_setprecision(n) fixed << setprecision((n))
#define execution_time(ti) printf("Execution Time: %.4lf sec\n", 1.0 * (clock() - ti) / CLOCKS_PER_SEC);
#define pai 3.1415926535897932384
#define NUM_MAX 2e18
#define NUM_MIN -1e9
 
using namespace std;
using ll = long long;
using P = pair<int,int>;
template<class T> inline bool chmax(T& a, T b){ if(a<b){ a=b; return 1; } return 0; }
template<class T> inline bool chmin(T& a, T b){ if(a>b){ a=b; return 1; } return 0; }

int main() {
    int N;
    cin >> N;
    vector<int> P(N+1);
    repx(i, 2,  N+1) cin >> P[i];

    int cnt=0;
    while(1){
        N = P[N];
        cnt++;
        if(N==1) break;
    }
    cout << cnt << endl;

    return 0;
} 
```

## [C - Monotonically Increasing](https://atcoder.jp/contests/abc263/tasks/abc263_c)

dfsを使うと綺麗に実装できます。

```C++:C++
#include <bits/stdc++.h>
 
#define rep(i,n) for(int i=0; i<(n); ++i)
#define repx(i,x,n) for(int i=x; i<(n); ++i)
#define fixed_setprecision(n) fixed << setprecision((n))
#define execution_time(ti) printf("Execution Time: %.4lf sec\n", 1.0 * (clock() - ti) / CLOCKS_PER_SEC);
#define pai 3.1415926535897932384
#define NUM_MAX 2e18
#define NUM_MIN -1e9
 
using namespace std;
using ll = long long;
using P = pair<int,int>;
template<class T> inline bool chmax(T& a, T b){ if(a<b){ a=b; return 1; } return 0; }
template<class T> inline bool chmin(T& a, T b){ if(a>b){ a=b; return 1; } return 0; }

int N, M;
set<vector<int>> st;

void dfs(vector<int> v){
    if(v.size() == N){
        st.insert(v);
        return;
    }

    int i = 1;
    if(v.size()) i = v[v.size()-1] + 1;

    for(; i<=M; i++){
        v.push_back(i);
        dfs(v);
        v.pop_back();
    }
}

int main() {
    cin >> N >> M;

    dfs(vector<int>());

    for(vector<int> s:st){
        for(auto v:s){
            cout << v << ' ';
        }
        cout << endl;
    }

    return 0;
} 
```
Ubuntuにfishをインストールする方法

## はじめに
備忘録
タイトルの通りです。
実行環境はUbuntu20.04（bash : `$`の方）です。
途中からfish（ : `~>`）に変わっています。
どなたかのお役に立てれば幸いです。
## fishのインストール
次のコマンドでfishをインストールできます。
```bash
$ sudo apt install fish
```
インストールが終わったらfishのバージョンを確認してみましょう。
```bash
$ fish -v 
```
## シェルをfishに変更
まずfishのパスを確認します。
```bash
$ cat /etc/shells
```
上のコマンドを実行すると、シェルのパス一覧が表示されます。
```bash
# /etc/shells: valid login shells
/bin/sh
/bin/bash
/usr/bin/bash
/bin/rbash
/usr/bin/rbash
/bin/dash
/usr/bin/dash
/usr/bin/tmux
/usr/bin/screen
/usr/bin/fish
```
`chsh`コマンドでfishのパスを設定します。
```bash
$ chsh -s /usr/bin/fish
```
:::note
次にターミナルを開いた時にfishが起動します。
SSHをしている場合は、一度`exit`して接続を閉じてから、再びSSH接続してください。
`user@host ~>`になっていれば大丈夫です。
:::
## fisherのインストール
次のコマンドでfisherをインストールできます。
```bash
~> curl -sL https://git.io/fisher | source && fisher install jorgebucaran/fisher
```
:::note
文字化けする場合は、
~> sudo apt-get install fonts-powerline
でフォントをインストールしましょう。
:::
fisherのバージョンを確認してみましょう。
```bash
~> fisher -v
```
## プラグインのインストール
便利なプラグインがいろいろ用意されています。
興味がある人は調べてみてください。
```bash
# テーマのプラグイン
~> fisher install oh-my-fish/theme-bobthefish
# 訪れたことがあるディレクトリにすぐ移動できるプラグイン
~> fisher install jethrokuan/z
```
:::note
`fish_config`コマンドで、ブラウザからテーマをいじる等もできます。
:::

# ブログもあります
https://kyit.net/how-to-install-fish/
マンデルブロ集合を JavaScript で高速に描く（WebGL編）

# はじめに

![MiConv2022-08-26 22.32.49.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/73493/410dfa38-3bd9-34f2-6909-004b1bca6cd5.gif)


[前の記事](https://qiita.com/Satachito/items/0d963da40f2afca6920e)でマンデルブロ集合を Canvas要素に書く方法をご紹介しました。imageData を使う方法で、ms オーダーまで高速化できましたが、それでもまだリアルタイムな描画とはいえませんでした。また処理時間が画素数に比例するので、大きなエリアでは描画されるまでにタイムラグが生じていました。

https://qiita.com/Satachito/items/0d963da40f2afca6920e

前の記事の描画部分を WebGL に取り替えて高速化を図るのが本校の目的です。

実際に使えるようにしたサイトを作りました。
見たい場所をマウスで選択してズームしていけるようになっています。よかったら参考にしてください。

https://slip.828.tokyo/Mandelbrot.html


# プログラム

## バーテックスシェーダー

ほぼ何もやることがありません。ソースは以下のようになります。

```
attribute vec3 position;
void
main() {
    gl_Position = vec4( position, 1.0 );
}   
```

## フラグメントシェーダー

理論については[前の記事](https://qiita.com/Satachito/items/0d963da40f2afca6920e)を参考にしてください。

```
precision highp float;
precision highp int;

uniform vec2	min;
uniform vec2	max;

uniform vec2	resolution;
uniform int		iterations;

int
Mandelbrot( vec2 c ) {
	vec2 z = c;
	for( int i = 1; i < 1000; ++i ) {
		if ( i >= iterations ) break;

		vec2 z2 = z * z;
		if ( z2.x + z2.y > 4.0 ) return i;
		
		z = vec2(
			( z2.x - z2.y )
		,	( z.y * z.x * 2.0 )
		) + c; 
	}
	return 0;
}

void
main( void ) {
	int i = Mandelbrot(
		vec2(
			min.x + ( max.x - min.x ) * gl_FragCoord.x / resolution.x
		,	min.y + ( max.y - min.y ) * gl_FragCoord.y / resolution.y
		)
	);
	gl_FragColor = vec4( float( i ) / float( iterations ), 0.0, 0.0, 1.0 );
}
```


# 呼び出し側（JavaScript）

* min, max には実部と虚部の最小値と最大値を入れて渡します。
* resolution にはキャンバスの大きさを入れて渡します。
* iterations には繰り返し回数を入れて渡します。

```
const minLocation           = gl.getUniformLocation( program, 'min'             )   
const maxLocation           = gl.getUniformLocation( program, 'max'             )   
const resolutionLocation    = gl.getUniformLocation( program, 'resolution'      )   
const iterationLocation     = gl.getUniformLocation( program, 'iterations'      )   

const
Draw = () => {
    gl.viewport( 0, 0, CANVAS.width, CANVAS.height )

    gl.clear( gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT )

    gl.useProgram( program )

    gl.uniform2f( minLocation           , Number( R_MIN.value ), Number( I_MIN.value ) ) 
    gl.uniform2f( maxLocation           , Number( R_MAX.value ), Number( I_MAX.value ) ) 
    gl.uniform2f( resolutionLocation    , CANVAS.width, CANVAS.height )
    gl.uniform1i( iterationLocation     , Number( NUM_I.value ) ) 

    gl.vertexAttribPointer      ( 0, 2, gl.FLOAT, false, 0, 0 ) 

    gl.enableVertexAttribArray  ( 0 )
    gl.drawArrays               ( gl.TRIANGLES, 0, 6 ) 
    gl.disableVertexAttribArray ( 0 )
}

```

# 最後に

実際のソースは彩色の部分とかが付け加わっています。ペラ１で以下の場所にありますので、よかったら参考にしてください。

https://github.com/Satachito/SliP/blob/main/Mandelbrot.html
特定のUSBデバイスを鍵にして、プログラムと連携させる

USBデバイスにはそれぞれ固有のIDが割り振られています。
特定のUSBデバイスが挿入されたときに、プログラムで処理をしたいときに使えるNode.jsのライブラリを公開しました。

https://github.com/TakutoYoshikai/usbkey

## 使い方
### インストール
以下の二つのコマンドを実行する。
```bash
npm install -g TakutoYoshikai/usbkey

# ローカルのnode_modulesにもインストール
cd /path/to/project
npm install --save TakutoYoshikai/usbkey
```

### USBデバイスの登録
このコマンドを実行した後、USBデバイスを挿入する。
```bash
usbkey-register
```
`./usbkey`というファイルが生成されます。これが登録されたUSBメモリを検知する、ハッシュ化されたパスワードのようなものです。

以下のJavaScriptファイルを用意し、好きな処理を追加する。

```javascript:index.js
const usbkey = require("usbkey");

usbkey.observe("./usbkey", {
  add: function() {
    // 挿入された時の処理
　　　　　　　　console.log("added");
  },
  remove: function() {
　　　　　　　　// 取り出された時の処理
    console.log("removed");
　　　　},
});
```

以下で実行する。
```bash
node index.js
```

登録されたUSBデバイスを挿入したときに`added`と出力され、取り出すと`removed`と出力されます。
anacondaをインストールする際に"conda.exe: error while loading shared libraries: libz.so.1: failed to map segment from shared object: Operation not permitted"というエラーが発生する

# エラー
レンタルサーバーにcondaを入れようとした時にエラーが発生しました。

```
$ bash Miniconda3-py39_4.12.0-Linux-x86_64.sh
~~~
Unpacking payload ...
/home/yakitori/miniconda3/conda.exe: error while loading shared libraries: libz.so.1: failed to map segment from shared object: Operation not permitted
/home/yakitori/miniconda3/conda.exe: error while loading shared libraries: libz.so.1: failed to map segment from shared object: Operation not permitted
```

# 解決策
調べると `TMPDIR` を設定すれば良いらしいです。
[https://github.com/ContinuumIO/anaconda-issues/issues/11587](https://github.com/ContinuumIO/anaconda-issues/issues/11587)
[https://peaku.co/questions/3181-condaexe:-error-al-cargar-bibliotecas-compartidas:-libzso1](https://peaku.co/questions/3181-condaexe:-error-al-cargar-bibliotecas-compartidas:-libzso1)
上で示唆されている通り実行すると

```
$ mkdir ~/tmpconda
$ TMPDIR=~/tmpconda bash Miniconda3-py39_4.12.0-Linux-x86_64.sh
~~~
Thank you for installing Miniconda3!
```
無事インストールできました:raised_hands_tone2:
画像生成AI(MidjourneyとStableDiffusion)の生成結果をキーワードから一覧化するページ(Twitter検索)

# はじめに

画像生成AIサービスであるMidjourneyとStableDiffusionの生成結果をTwitterで検索し、キーワードから生成画像を調べたり使われている単語の分析を表示したりするページを作成しました

https://ai-art-twitter-hashtag.herokuapp.com

## キーワードから画像一覧
- Midjourney
![スクリーンショット 2022-08-26 21.37.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/638747/247d92d4-36b1-3263-dd2e-e7898c345115.png)
- Stable Diffusion
![スクリーンショット 2022-08-26 21.36.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/638747/5355fe9d-16af-d8c2-683c-1a3dedb59519.png)

## 頻出単語
![スクリーンショット 2022-08-26 21.37.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/638747/bb61e839-378d-4b84-dc88-1bc9bce53a55.png)

## ワードクラウド
![スクリーンショット 2022-08-26 21.38.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/638747/58ea4c6c-76a7-2ad0-19b9-d1d51dd0c0ec.png)

## 共起語ネットワーク
![スクリーンショット 2022-08-26 21.38.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/638747/20b89d37-0709-0dbf-a72f-45dd6065bf1e.png)


# 実装

https://github.com/sentencebird/twitter-hashtag-analysis

主な登場人物
```
tweepy==4.10.0
nlplot==1.4.0
streamlit
mecab-python3~=1.0.5
sqlalchemy
psycopg2
```

## ツイート収集→DBへ保存
https://github.com/sentencebird/twitter-hashtag-analysis/blob/main/src/cron.py

1時間に1回の頻度で実行する　

各サービスごとに以下のテーブルを作成
- ツイート一覧
　　- 一度に5000件取得する
- キーワードとツイートの対応
 - MeCabで形態素解析を行い、単語のインデックスを作る
- ストップワード
  - 適当に設定 https://github.com/sentencebird/twitter-hashtag-analysis/blob/main/src/cron.py#L36
- キーワードの出現頻度
  - ワードクラウド表示までの時間を短縮できるようにあらかじめカウントしておく


## ウェブ表示
https://github.com/sentencebird/twitter-hashtag-analysis/blob/main/src/app.py

streamlitで実装


# おわりに
そもそもプロンプトの文をツイートするわけではないため、ノイズが多いようです

デプロイしたそばからデプロイ先のherokuの無料枠廃止アナウンスがあったので、すぐに消え去るかも

https://blog.heroku.com/next-chapter
Math.clz32とUTF-8

`Math.clz32`という標準関数を、UTF-8の処理に使ってみた話です。

## `Math.clz32`とは

`clz`はCount Leading Zeros（先頭にある0の数を数える）の略で、その名の通り、引数を32ビット符号なし整数として、先頭に入る0の個数を数える関数です。

ARMやx64にも搭載されるなどメジャーで高速に実行可能な演算で、他のビット操作を構築する要素としても使えるとのことです。

## UTF-8のビットパターン

UTF-8はマルチバイトの文字コードですが、シフトJISで起きた「ダメ文字」（2バイト目にASCIIの範囲まで使っていたことで、ASCIIとしての処理が行われて誤作動する）などの問題を避ける、そして文字の区切りをはっきりさせるために、バイトごとのビットパターンによって使われる場所をはっきり区分けしています[^1]。

| 16進 | ビットパターン | 役割 |
|:-:|:-:|:-:|
| 0x00 - 0x7F  | 0xxxxxxx | 1バイト文字  |
| 0x80 - 0xBF  | 10xxxxxx | マルチバイト文字の2バイト目以降  |
| 0xC0 - 0xDF  | 110xxxxx | 2バイト文字の1バイト目  |
| 0xE0 - 0xEF  | 1110xxxx | 3バイト文字の1バイト目  |
| 0xF0 - 0xF7  | 11110xxx | 4バイト文字の1バイト目  |
| 0xF8 - 0xFF  | 11111xxx | 未使用  |

ご覧のように、「先頭に続く1の数」でバイトの種類を判定しています。

## `Math.clz32`で計算

`0xFF`とのXORを取れば、0は1に、1は0に反転するので、`Math.clz32`で処理するのにふさわしい形となります。そして、上位24ビットは0なので、`Math.clz32(value ^ 0xFF) - 24`とすれば、使いやすい値が取れます。特に、2～4がそのまま取れるのも便利なところです。

| 16進 | ビットパターン | 演算結果 |  役割 |
|:-:|:-:|:-:|:-:|
| 0x00 - 0x7F  | 0xxxxxxx | 0 | 1バイト文字  |
| 0x80 - 0xBF  | 10xxxxxx | 1 | マルチバイト文字の2バイト目以降  |
| 0xC0 - 0xDF  | 110xxxxx | 2 | 2バイト文字の1バイト目  |
| 0xE0 - 0xEF  | 1110xxxx | 3 | 3バイト文字の1バイト目  |
| 0xF0 - 0xF7  | 11110xxx | 4 | 4バイト文字の1バイト目  |
| 0xF8 - 0xFF  | 11111xxx | 5以上 | 未使用  |


## 外部リンク

* [Math.clz32() - MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Math/clz32)
* [Find first set - Wikipedia英語版](https://en.wikipedia.org/wiki/Find_first_set)

[^1]: この表はビットパターンだけ表すために簡略化したもので、0xC0、0xC1、0xF5-0xF7は正当なUTF-8には登場しません。
Goでerrors.New()の値を比較するテストでイコールにならない

# はじめに

Goを始めたばかりでAPIを一つ作成してテストに挑戦したところかなり沼ってしまいました
原因を解明するのにかなり時間がかかってしまったので初心者のためにまとめます
自分が探していた情報がある記事がなかったので(仕組みを知っているならあたりまえの理由だった)、役に立てばと思います

# 問題

```go:main.go
package main

import (
	"errors"
	"fmt"
)

func main() {
	err1 := errors.New("error")
	err2 := errors.New("error")

	fmt.Println(err1 == err2)
}
```

この結果が`false`になってしまうのが理解できずにテストでも同じような比較をしていたのでテストが通らず困りました

# 解決方法

これは`errors.New`の処理をみれば納得できます

```go
func New(text string) error {
	return &errorString{text}
}

// errorString is a trivial implementation of error.
type errorString struct {
	s string
}

func (e *errorString) Error() string {
	return e.s
}
```

erros.Newを呼び出すと`func New`で`return &errorString{text}`が返ってきます
ポインタが返却されていました

変数を別に定義するとそれぞれポインタが変わるのでポイント同士の比較はエラーになるのは当然でした

# おわりに

ここに気付くのにかなりいろいろな英語の記事を読みました
あたりまえのことなのかあまり触れられている記事がなかったため原因にたどり着くのに時間がかかりましたが、やっとテストが通るようになってよかったです

これからどんどんテストを書いていきたいです

# 参考

https://gist.github.com/fospathi/1e6f5aea622abb52bddc9bcb1ffee858
Golang reflectパッケージのお勉強

## TL;DR
reflectパッケージを使うと、構造体のタグの値を取得できる。（実行時に構造体の値から）

## はいり
今回は[Reflectパッケージ](https://pkg.go.dev/reflect)について勉強します

パッケージを勉強するときはexamplesを一通り見て回ります。

その際exampleのコードを見ながら自分でもちょっとしたコードを書くので、そのコードを載せます。

Reflectパッケージには8つのexampleがありました。
順番に見ていきましょう

Kind
MakeFunc
StructOf
StructTag
StructTag.Lookup
TypeOf
Value.FieldByIndex
Value.FieldByName


## Kind

まずは定義を見ましょう。型の名前っぽいものが羅列されてます。

```go
type Kind uint

const (
	Invalid Kind = iota
	Bool
	Int
	Int8
	Int16
	Int32
	Int64
	Uint
	Uint8
	Uint16
	Uint32
	Uint64
	Uintptr
	Float32
	Float64
	Complex64
	Complex128
	Array
	Chan
	Func
	Interface
	Map
	Pointer
	Slice
	String
	Struct
	UnsafePointer
)
```

reflect.Value型にある Kind というメソッドで Kind型の値を取得できます。
Kindというくらいなので、実行時の値の型の種類が取得できます。
注意すべき点は型の名前ではなく種類という点くらいですね。

```go
type (
	User struct {
		ID   uint
		Name string
	}
)

type (
	IFoo interface {
		Hey() string
	}
	FooImpl struct{}
)

func (f FooImpl) Hey() string { return "foo" }

func main() {
	i := 1
	v := reflect.ValueOf(i)
	fmt.Println(v.Kind(), v.Type())

	var foo IFoo = FooImpl{}
	v = reflect.ValueOf(foo)
	fmt.Println(v.Kind(), v.Type())

}

// int int
// struct main.FooImpl
```

## MakeFunc
時代はジェネリクスなので、割愛します。

## StructOf

動的に構造体の定義を生成できます。

```go
	typ := reflect.StructOf([]reflect.StructField{
		{
			Name: "Height",
			Type: reflect.TypeOf(float64(0)),
			Tag:  `json:"height"`,
		},
		{
			Name: "Age",
			Type: reflect.TypeOf(int(0)),
			Tag:  `json:"age"`,
		},
	})

	v := reflect.New(typ).Elem()
	fmt.Println(v)
	v.Field(0).SetFloat(0.4)
	v.Field(1).SetInt(2)
	fmt.Println(v)
// {0 0}
// {0.4 2}
```

このコード例は以下のような構造体定義となります

```go
type Human struct {
  Height float64
  Age    int
}
```

## StructTag

構造体のフィールドにつけるタグのことです。

```go
type S struct {
	F string `species:"gopher" color:"blue"`
}
```

以下のように実行時の構造体から取得します。
実行時なのが個人的には悲しい点です。
コンパイル時やコンパイル前にタグに楽にアクセスできる方法があればいいなと思います。

```go
	s := S{}
	st := reflect.TypeOf(s)
	field := st.Field(0)
	fmt.Println(field.Tag.Get("color"), field.Tag.Get("species"))

```


## StructTag.Lookup
前述のStructTagのラベルの存在確認です。

```go

	s := S{}
	st := reflect.TypeOf(s)
	for i := 0; i < st.NumField(); i++ {
		field := st.Field(i)
		if alias, ok := field.Tag.Lookup("alias"); ok {
			if alias == "" {
				fmt.Println("(blank)")
			} else {
				fmt.Println(alias)
			}
		} else {
			fmt.Println("(not specified)")
		}
	}
```

## TypeOf

引数の値の型の情報を返す関数です

```go
	t := reflect.TypeOf((*io.Writer)(nil))
	fmt.Println(t)
// *io.Writer
```

## FieldByIndex
プライベートなフィールドにもアクセスできる手段です。
```go
	type user struct {
		firstName string
		lastName  string
	}

	u := user{"suke", "nishi"}
	s := reflect.ValueOf(u).FieldByIndex([]int{0})
	fmt.Println(s)
// suke
```

## FieldByName
プライベートなフィールドにもアクセスできる手段です。
FieldByIndexとほぼ同じなので割愛。

## まとめ
Go1.18からジェネリクスが導入されて出番が多少減ってはしまいしたが、
構造体からタグ情報を取得する定番の方法なのでまだまだreflectパッケージは現役そうです
Goのテストでiniファイルの読み込みでエラーがでる(no such file or directory)

# はじめに

仕事でGoのテストを書き始めたのですが、ファイルの読み込みでつまずいたので共有します

# 問題

以下のコードでGoのテストの実行中に途中でエラーが発生してしまいました

```go:base_test.go
func LoadConfig() {
	cfg, err := ini.Load("config/config.ini") # ここでエラー
	if err != nil {
		log.Fatalln(err)
	}
```

```
b7159993c977:/go/src/test# go test
2022/08/26 02:35:22 open /config/config.ini: no such file or directory
```

main.goで呼び出すときは動いていましたなぜかテストで動かすと動かなくなりました

# 解決方法

ファイルパスを絶対パスで指定したところテストが動きました

```go:base_test.go
func LoadConfig() {
	cfg, err := ini.Load("/go/src/config/config.ini")
	if err != nil {
		log.Fatalln(err)
	}
```

# おわりに

ファイル読み込みは絶対で書いたほうがいいなと思いました
これを解決するのに1時間もかかったのがもったいなかったです
InDesign SDK Plugin メモ

InDesign SDK Plugin メモ
~~~C++
// GetExecutionContextSession()
// ISessionクラスのポインタを取得
//
// virtual IApplication* ISession::QueryApplication()
// IApplicationクラスのポインタを取得
//
// 例
InterfacePtr<IApplication> iApplication(GetExecutionContextSession()->QueryApplication());

// virtual IDocumentList* IApplication::QueryDocumentList() const
// IDocumentListクラスのポインタを取得
//
// 例
InterfacePtr<IDocumentList> iDocumentList(application->QueryDocumentList());

// PUBLIC_DECL UIDRef GetUIDRef(const IPMUnknown *obj)
// オブジェクトのUIDRefを返します。
// 
// UIDRef(UniqueIDReference)
//
// IPMUnknown
// すべてのインターフェイスが継承するルートクラス
//
// 例
UIDRef objectUIDRef = ::GetUIDRef(obj);

// UIDList(const IPMUnknown* object)
// コンストラクタ
//
// 例
UIDList parentUIDList(object);

// UIDList(const UIDRef& ref)
// コンストラクタ
//
// 例
UIDList objectUIDList(ref);
~~~
【JS】new Date、1桁or2桁の年 での挙動がおかしい原因

# 起こった問題

```sample.js
const date1 = new Date('0001-12-17 03:24:00');
// > Thu Jan 12 2017 03:24:00 GMT+0900 (日本標準時)
const date2 = new Date('0011-12-17 03:24:00');
// > Sun Nov 12 2017 03:24:00 GMT+0900 (日本標準時)
```

`date1`で、期待しているのは`1年12月17日`です。

ん？？ `2017年1月12日`。

~~1桁or2桁の年、つまり、1~99年の場合にバグります~~
~~しかし、年が100年以上の場合、再現できません~~

```sample.js
const date3 = new Date('0111-12-17 03:24:00');
// > Thu Dec 17 0111 03:24:00 GMT+0918 (日本標準時)
const date4 = new Date('1111-12-17 03:24:00');
// > Sun Dec 17 1111 03:24:00 GMT+0918 (日本標準時)
```

(@im44794さん からの[ご指摘](https://qiita.com/baby-0105/items/4444588fcdc1e32a83f9#comment-e614f059a0a7fc30b872)より修正:2022/08/27） 

> バグというより、文字列指定での正常動作は保証されていないだけです。
([ご指摘](https://qiita.com/baby-0105/items/4444588fcdc1e32a83f9#comment-e614f059a0a7fc30b872)より)

> Note: 注: Date コンストラクター (および同じように動作する Date.parse()) を使用した日付文字列の解析は、ブラウザーによって違いや矛盾があるため、使用を避けることを強くお勧めします。。
・RFC 2822 書式の文字列の対応は慣習的に行われているだけです。
・ISO 8601 形式の対応は、日付のみの文字列 (例えば 1970-01-01) が地方時ではなく UTC として扱われる点が異なります。
[mdn](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Date/Date#%E3%82%BF%E3%82%A4%E3%83%A0%E3%82%B9%E3%82%BF%E3%83%B3%E3%83%97%E6%96%87%E5%AD%97%E5%88%97)

今回私が実装していたように、文字列指定するとブラウザごとにECMAスクリプトのバージョンの差異によって正常に動作しない場合があるみたいです

対応としては、ブラウザごとの差異をなくすためにも、異なる書式に対応したライブラリを使用することを推奨する記載が`mdn：Date.parse()`の方にあります
([mdn：Date.parse()](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Date/parse)）


# どのように修正するかの結論

```sample.js
const date = new Date('0001-12-17T03:24:00');
// > Mon Dec 17 0001 03:24:00 GMT+0918 (日本標準時)
```

>let today = new Date()
let birthday = new Date('December 17, 1995 03:24:00')
let birthday = new Date('1995-12-17T03:24:00')
let birthday = new Date(1995, 11, 17)            // the month is 0-indexed
let birthday = new Date(1995, 11, 17, 3, 24, 0)

[mdn](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Date/Date)にも記載ありますが、年月日と時間の間の`T`が漏れていました

（@551361sさん の[ご指摘より](https://qiita.com/baby-0105/items/4444588fcdc1e32a83f9#comment-0317e14bd61acf2ec592)以下追記：2022/08/27）

> 日本では年-月-日の順で記述するのが普通なので上記結果は違和感があるかもしれませんが、国によっては月-日-年の順だったりもするので、どちらとも解釈できてしまうケースで意図した結果とならないことがあるのは仕方のないことです。 
なので、ISO8601形式であれば日付部分は年月日の順に指定することが国際的に定められているので、年が0099以下であっても上手くいくわけです。
（@551361sさん の[ご指摘より](https://qiita.com/baby-0105/items/4444588fcdc1e32a83f9#comment-0317e14bd61acf2ec592))

`'0001-12-17T03:24:00'`は、[ISO8601](https://ja.wikipedia.org/wiki/ISO_8601)に準拠した形式であるため、パースが可能です

以下の[ISO8601](https://ja.wikipedia.org/wiki/ISO_8601)の拡張形式に準拠したものも。

```sample.js
const date1 = new Date('0001-09-27');
// > Thu Sep 27 0001 09:18:59 GMT+0918 (日本標準時)
```

# 上記の対応でも、環境によってはバグ？

開発環境では、日付のパースが正常にできていたのですが、リリース環境ですとパースエラー。。。

なので、日付の処理は素直にライブラリで対応しましょう
今(2022/8 時点)だと、[datefns](https://date-fns.org/)ですかね

datefnsのparseを使用して対応

```sample.js
const result = parse('0001-12-17 03:24:00', 'yyyy-MM-dd HH:mm:ss', new Date())
// > Mon Dec 17 0001 03:24:00 GMT+0918 (日本標準時)
```

動作確認してませんが期待した結果が得られるはずです

https://date-fns.org/v2.29.2/docs/parse
Elasticsearchのもやもや: ドキュメントの更新はPUTかPOSTか

### はじめに

Elasticsearchを使っていて、もやもやした内容を、
メモ書きレベルで、思うがまま書いてみます。

### Elasticsearchのドキュメント更新

Elasticsearchのドキュメント更新リクエストは、
PUTメソッドまたはPOSTメソッドを使用します。
その違いや使い分けに、モヤモヤ感が。。。

### PUTとPOSTによるドキュメント更新を比較してみた

- 更新範囲の違い
    - PUT: 指定ドキュメントIDを持つドキュメントを丸ごと置き換える(※ [Elasticsearchリファレンスを参照]( https://www.elastic.co/guide/en/elasticsearch/reference/8.4/docs-update.html#update-api-example))
    ```
    PUT test/_doc/1
    {
      "counter" : 1,
      "tags" : ["red"]
    }
    ```

    - POST: 指定ドキュメントIDを持つドキュメントの一部フィールドを置き換える
        - 方法1: `doc`句による更新(※ [Elasticsearchリファレンスを参考]( https://www.elastic.co/guide/en/elasticsearch/reference/8.4/docs-update.html#_update_part_of_a_document))
        ```
        POST test/_update/1
        {
          "doc": {
            "name": "new_name"
          }
        }
        ```
        - 方法2: `script`句による更新
        ```
        POST test/_update/1
        {
          "script" : {
            "source": "ctx._source.counter += params.count",
            "lang": "painless",
            "params" : {
              "count" : 4
            }
          }
        }
        ```

- ドキュメントの再インデックス
    - 両者ともにインデックスが再作成され、ドキュメントのバージョンが上がる

### おわりに

Elasticsearchのドキュメント更新方法として、
POSTがスクリプトを使用でき、PUTより柔軟に利用できそうです。
【typescript】ioredis-mockを用いてjestでのredisのテストを行う

## ioredis-mockって何？

```ioredis```の全ての操作をメモリ内で実行するエミュレーター。実際にサーバーにアクセスすることなしにioredisをモック化することができる。

https://www.npmjs.com/package/ioredis-mock

今回はこれを```Fake```として用いてRedisのテストを行う。

## 導入





```
npm i ioredis-mock
```

```@types/ioredis-mock```という型定義ファイルもあるが、こちらは思ったように使用することができなかったため```require("ioredis-mock")```としてimportする。

## 使ってみよう

redisインスタンスは以下のように作ることができる。

```ts
const Redis = require("ioredis-mock");

const redis = new Redis();
```
ioredisと全く同じように使ってみよう。

```ts:redis.test.ts
const Redis = require("ioredis-mock");

const redis = new Redis();

it("should get value", async () => {
    const id = "hoge";
    const value = "fuga";
    await redis.set(id, value);
    const gotValue = await redis.get(id); 
    expect(gotValue).toEqual(value);
});
```
設定した値を再び取り出すだけ。特にサーバーなど何も設定せずこれだけで動かせる。

## expireを試してみよう

jest上で動かすため、redis上で経過する時間をテスト上で好きに制御することができる。

```ts
it("should expire", async () => {
    jest.useFakeTimers();
    const id = "hoge";
    const value = "fuga";
    await redis.set(id, value, "EX", 60);
    jest.advanceTimersByTime(60_000);
    const gotValue = await redis.get(id); 
    expect(gotValue).toBeNull();
});
```
60秒後に有効期限切れとなるキーを設定し、60秒経過させて取り出す。値がnullとなることを確認した。
SharePoint+αで守っていきたい運用ルール（SE用）

# 投稿者の紹介
お疲れ様です。
私は従業員数が15,000人を超える大手販売会社のIT部門に所属するSEです。
会社ではOffice365を利用して結構長いのですが、普通に使っていくだけではなくて、私の実体験からSharePointを使う時に+αで守ったほうが良いルールというのを紹介しようと思います。

もし知らないことがあってそれいいなって思ったら真似してみてくださいな


# SharePointを使う時に+αで守っていきたいルール
私が考えるSharePointを使う上で守ったほうが良いルールは以下の4つです。

**1.ドキュメント類のアクセス権は必要以上に厳しくしない
2.チェックイン、チェックアウトを必須にする
3.設計書や仕様書などはプロジェクト単位で管理しない
4.SharePointのテンプレートを用意する**

少し言っている意味が分からないルールもあるかと思うので、どうして私がそのように考えるのか詳しく説明していきます。

## 1.ドキュメント類のアクセス権は必要以上に厳しくしない
アクセス権というのはアクセスすること、つまりドキュメントの中身を見て編集できるかどうかを設定する権限です。
アクセス権を適切に設定することで、部外者の人（例：社外の人）に対して情報が漏洩しないように設定することが出来るので、セキュリティ的にとても重要な機能です。

ただ、このアクセス権を例えば上司以外に見せたくないからといって、上司だけが見れるようにしたり、プロジェクトメンバーや所属部署のメンバーだけにしか見えないようにすると、下記の問題が起きます。
- 同じような文書を作成するときに他の人のドキュメントをコピーや参考にすることが出来ない。
- 情報の透明性が確保できないため、人間の心理的に不満が溜まりやすい。
例：上司だけ、幹部だけが知っている情報がたくさんあると、部下はいちいち上司に尋ねなくてはならなくなる。また、会社の方針に疑問を抱いた従業員がいた場合、理由が分からないと各々の解釈に委ねることになり、疑問が不満に近い場合、守らない場合がある。
つまり、***後ろめたいことがないなら隠さない！！***
- 部署間をまたいで情報を共有することで獲得できたビジネスチャンスに気づかない。

上記は一例ですが、アクセス権を厳しくしすぎることで色々と不都合なことが起きそうだなというのは伝わったかと思います。

ただ、それでも他部署にアクセス権を与えると勝手に変更されたり、社外の人がアクセスして情報が漏洩する恐れがあるから怖いというのもわかります。
そういう場合は、アクセス権を参照権限のみに限定すること。
また、情報漏洩についてはSharePointの権限で管理するのではなく、もっと上位の認証システム（AD認証の導入など）で管理し、従業員への情報漏洩に対する教育で対処することをお勧めします。

中間管理職の方の場合SharePointのアクセス権で管理するのが早い場合もありますが、情報漏洩は会社全体で対処すべき問題ですし、SharePointの権限だけでは部署ごとに任せることになり、ヒューマンエラーが起こる原因になります。

## 2.チェックイン、チェックアウトを必須にする
SharePointにはチェックイン、チェックアウトという機能があります。
これはファイルの排他的制御を可能にする機能です。

つまり、誰かがファイルをチェックアウトしているときは、他の人が編集できなくする機能です。


この機能はライブラリの設定でデフォルトにすることが可能です。
デフォルトにすることで、第三者の意図しない編集を防ぐことができます。

また、チェックインの履歴は残り、チェックイン時にコメントを残すことも可能なので、ファイルがどのような意図で作成され、どのように修正されたのかがそのファイルが作られてから数年後でもわかるようになります。

もちろんデメリットはあります。
ユーザーからしたら毎回チェックイン、チェックアウトを強制されるので慣れるまでは正直めんどくさいです。
また、誰かがファイルをチェックアウトしたまま退職した場合、編集が出来なくなり管理者でなければ解除が出来なくなります。

これらのデメリットとメリットを比べながら、ビジネス視点で判断することが大切だと私は思います。
## 3.設計書や仕様書などはプロジェクトごとに管理しない
これは私がSEだから経験できたお話なのですが、あるシステムの設計書を保存するときにプロジェクト名のフォルダを作って管理していたことがあったのです。

・・・別に良いのでは？と思いがちですが、プロジェクトのフォルダ名で管理していくのは注意が必要です。

例えばシステムに改修が入った場合、新たに「○○システム法改正対応プロジェクト」というフォルダを作ってそこで設計書を管理していくとしましょう。
１度や２度の改修だったらどのフォルダに入っている設計書が最新なのかはわかります。
ただ、改修が２桁になってくるとどの設計書が最新なのか、また、どの段階で改修されたのかがわからなくなってきてしまいます。
さらに、システムの改修ではすべての仕様書を修正するのではなく、一部の仕様書を修正するというケースが多いです。

そうなってくると、色々なフォルダに似たような設計書が保管されることになり、どれが最新版なのかわからなくなるケースが起こります。

なので、常に最新版の設計書はどれかわかるような構成に設計することを忘れないようにしてください。

## 4.SharePointのテンプレートを用意する
私の部署ではシステムごとにSharePointのライブラリを構築しています。
今までは新しいシステムを構築するときに、そのプロジェクトメンバーがライブラリを設計していたのですが、ライブラリごとに構成が異なっていてファイル名やフォルダ名、階層がバラバラでした。

バラバラでも運用はできるにはできるのですが、やっぱり統一できるところは統一したほうが、もしSharePointから別のファイル管理システムに移行したときに色々スムーズに移行できますし、新しいメンバーがプロジェクトに参画した時にどこになんのファイルがあるのかわかるかと思います。
更に最近だとPowerAutomateを使った自動化も流行ってます。
自動化するときにフォルダ名の命名規則や階層がある程度揃っていたほうがやっぱり色々とやりやすいです。

なのでライブラリのフォルダ構成などは可能な限りテンプレートを用意し、統一するように意識しましょう。

統一するときはサイトテンプレートを使うとかテンプレート用のフォルダを事前に用意しておくと便利でしょう。

# 最後に
SharePointの運用で困っている人へ、少しでも手助けになったらいいなと思って執筆しました。
長い文章、最後まで読んでいただきありがとうございました。
【Flutter】iOSでキーボードが消えない時の対処法

結論から書くとGestureDetectorの中にキーボードが表示される設定をしているViewを入れてあげればOKです。

ソースはこちら。

    GestureDetector(
      onTap: () => FocusScope.of(context).unfocus(),
      child: 　Widget
    )

もしTextFormFieldなどの集合体のViewを作成している場合、その集合体のViewに上記を入れましょう。

共通化してるTextFormFieldなどのクラスに上記を入れるとそのView上では機能しますが、それ以外のViewではそれ以外の場所をタップした時にうまく動作しないなどの問題が出る可能性があるからです。

参考までに。
Unity 2022.1.x - URP環境のDecalがFXAAを適用したカメラに映らない現象について

大分久しぶりの記事になってしまいましたが、今回はUnityで遭遇したバグについてです。
かなり限定的な状況で発生するバグですが、誰かの助けになるかもしれないので書き残します。

本記事は2022/08現在の情報で、すぐ情報が古くなりますのでご承知おきください。

# 結論
先に結論だけ書いておくと、URPの14系で修正されているようなのでUnity2022.2を使用しましょう。
現状2022.2はベータ版なので、不安な人は本リリース版を待ちましょう！

Unity2022.1を使い続ける人は、Cameraの`Anti-aliasing`を`SMAA`にすればデカールが表示されるようになります。

# バグについて
今回のバグは、まっさらなURPサンプルプロジェクトでも再現しました。
詳細を以下に書きます。

## 環境
- Unity2022.1.14f1(2022.1.11f1でも再現)
  - UnityというよりはURPのバージョン依存と思われます。今回はURP 13.1.8。
- Android(iOSは未確認)
  - エディタ(Windows)・実機ともに発生
- URP環境でDecalを使用すると発生

## 事象
URP環境でDecalを使用すると、シーンビューには映るのにゲームビューには映りません。
下の例では、左がシーンビュー、右がゲームビューです。
青い画像のデカールが配置されていますが、ゲームビューには映っていません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62924/85a789f7-0be0-6458-1b04-c1caa9ae76c6.png)

## 再現手順
```
1. Unity2022.1.14f1で新しいプロジェクト(テンプレートは「3D Sample Scene (URP)」)を作成
2. Build SettingsからAndroidにSwitch Platformする
3. UniversalRenderer(Universal Renderer Data)を選択、Add Renderer Featureから「Decal」を追加
4. 「Shader Graphs/Decal」シェーダーを適用したMaterialを作成、Base Mapに適当な画像を設定
5. HierarchyにURP Decal ProjectorをアタッチしたGameObjectを作成、4.で作成したMaterialを設定する
6. Rotation.Xに90を設定して、シーンビューでデカールが表示されていることを確認する
7. ゲームビューを見ると、デカールが描画されていないことが確認できる
8. HierarchyのMain Cameraの「Anti-aliasing」をSMAAに設定すると、ゲームビューで描画されることが確認できる
```
（この手順、「Unityにバグレポート出すぞ！」と思って書いたものなんですが、結局不要に...。この記事で供養ということで...

## 原因
細かい原因はわかりませんが、どのような条件で発生するかを調査しました。
以下の条件を満たすときに上記の現象が発生するようです。
- URP ver13系（確認したのは13.1.8）
- Android環境
- Cameraの`Anti-aliasing`を`FXAA`に設定
- Decalを使用
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62924/1f59fc15-24fb-1825-5163-306c3d7882e7.png)

## 対策とまとめ
現状確認できる二つのUnityバージョン（異なるURPのバージョン）でどうなるか調査しました。
結果、URP 14系では修正されているようです。

ということで、今回の事象に悩まされている方はUnity2022.2を使用するとよさそうです。（現状ベータ版なので、心配な人は本リリースまで待ちましょう）

現状では、アンチエイリアスの設定を`SMAA`にすればデカールは表示されますので、Unity2022.1環境でデカールを使いたい人はそれで耐え忍ぶ感じになりそうです。
もしくは、これからのバージョンで修正がURP 13系にも取り入れられるかもしれません。

問題ない動作が確認できたUnity
- Unity 2022.2.0b5：URP 14.0.3
- Unity 2023.1.0a6：URP 15.0.0
# カメラの移動【Unity の教科書 P291 ～ P293】

# カメラ📷 にプレイヤを追従させる🏃‍♂️

<details>
  <summary>① コントローラスクリプトの作成</summary>
✨[ カメラ ]も[ オブジェクト ] なので，コントローラスクリプト 📃 で動かせる✨
⇒ Create ⇒ C# Script ⇒ ファイル名「CameraController」
</details>

<details>
  <summary>① スクリプトの仕組み</summary>
プレイヤの移動を追従🏃‍♂️するように，フレームごとにプレイヤの座標を調べて，カメラの座標に反映。
</details>

<details>
  <summary>① スクリプトのアタッチ</summary>
カメラ📷 が（プレイヤの動きにしたがって）移動なので
「コントローラスクリプト 📃」 ⇒　「カメラオブジェクト📷」にアタッチ
 
</details>
StimulusJSでCropper.jsを使ってみた

タイトルの通り、StimulusJSでCropper.jsを使ってみました。
最近の自分は、StimulusJSのキャッチアップしています。なので、布教がてらに実装例を紹介しようと思います。

よくあるユースケースだとクロップ後の画像をサーバに送信しがちですが、この画面では、画像のクロップ範囲 **のみ** をサーバに送信します。


![スクリーンショット 2022-08-26 19.24.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/42642/e8b78c08-3e15-8523-8910-aefca35e0699.png)

よくあるWEBアプリでは、画像をS3などのストレージサービスに保存しますが、自分が作っているWEBアプリではherokuで動いていることもあり、DBに何でも突っ込みたかったので画像をBASE64でエンコードしてDB内に保存しました。また、登録した画像は公開することを考えていないので、パフォーマンスは考慮しなくていい、という理由もあります。

この実装では、「編集時のクロップエリアの復元」「クロップエリアの送信」ができます。
実装は次の通りです。

```html
<h1><%= @sketch.name %>の切り取り範囲の編集</h1>

<% crop_data = @sketch.crop_data %>
<%= form_with model: @sketch, url: feature_splatoon2_sketch_path(@sketch), method: :put, data: { controller: "image-crop", target: "image-crop.form", "image-crop-crop_data-value": crop_data } do |f| %>

  <%= render "shared/validation_errors", f: f %>

  <canvas id="image-canvas"></canvas>
  <div>
    <img src='data:<%= @sketch.encoded_image %>' id="sketch-image" style="display:none;">
  </div>
  <%= f.hidden_field :crop_data, data: { target: "image-crop.crop_data" } %>

  <div class="form-group row mt-5">
    <a href="#" class="form-control btn btn-primary" data-action="click->image-crop#submit">更新する</a>
  </div>
<% end %>

```

```js
import { Controller } from "@hotwired/stimulus"
import Cropper from 'cropperjs';

// Connects to data-controller="image-crop"
export default class extends Controller {
  static values = {
    cropData: Object,
  }

  static targets = [
    'crop_data',
    'form',
  ]

  connect() {
    const sketchImage = document.getElementById('sketch-image');
    const cvs = document.getElementById('image-canvas');
    const ctx = cvs.getContext('2d');

    const img = new Image();
    img.src = sketchImage.src;
    cvs.width = img.width;
    cvs.height = img.height;

    const onload = function(instance) {
      ctx.drawImage(img, 0, 0);
      instance.cropperValue = new Cropper(cvs, {
        zoomable: false,
        aspectRatio: 320 / 120,
        background: true,
        responsive: false,
        viewMode: 1,
        dragMode: "none",
        minCropBoxWidth: 320,
        minCropBoxHeight: 120,
        ready() {
          instance.cropperValue.setData(instance.cropDataValue)
        },
      })
    };
    img.onload = onload(this)
  }

  submit(event) {
    event.preventDefault();
    this.crop_dataTarget.value = JSON.stringify(this.cropperValue.getData());
    this.formTarget.submit();
  }
}
```


ソースコードは公開しています。

https://github.com/splaplapla/procon_bypass_man_cloud/blob/26f758fd6a819c7043fc8c5271de029c08dc48b2/app/javascript/controllers/image_crop_controller.js

StimulusJSはいいぞ。