OmniAICreator/Qiita-1.07M huggingface dataset:
チームで編集しているファイルの更新状況を毎日教えてほしいと思って書いたGAS

# はいこんにちは、こんなケース無いですか？

:::note
共同編集しているファイルの更新状況を知りたい、けどGoogleドライブ見に行くの面倒くさい
:::

指定した任意のGoogleドライブ内のファイルの最終更新時刻を取得してメール通知すれば万事解決ですね！

下準備

:::note
1. searchfolderIDという名前のシートのA1セルに任意のGoogleドライブのidを入力。
2. 結果を入力するresultという名前のシートを準備。
:::


てなわけで、はいコード

:::note warn
1. "このスプレッドシートのA1セルのUrlを取得して貼ろう！"


2. "送信したいメールアドレス"
:::
この二つは任意に書き換えてください。


```javascript
function getFileListInFolder_yesterday() {
  const ss = SpreadsheetApp.getActive();
  const folderIDsheet = ss.getSheetByName("searchfolderID");
  const resultsheet = ss.getSheetByName("result");

  const folder_id = folderIDsheet.getRange(1,1).getValue();
  const folder = DriveApp.getFolderById(folder_id);
  const files = folder.getFiles();
  const list = [];    

  while(files.hasNext()) {
    const buff = files.next();
    list.push([buff.getName(), Utilities.formatDate(buff.getLastUpdated(), 'Asia/Tokyo', 'yyyy/MM/dd hh:mm:ss'),buff.getUrl()]);
  };

  const date = new Date();
  const today = Utilities.formatDate(date, 'Asia/Tokyo', 'yyyy/MM/dd 00:00:00');
  const day = date.getDate();
  date.setDate(day-1);
  const yesterday =  Utilities.formatDate(date, 'Asia/Tokyo', 'yyyy/MM/dd 00:00:00');
  
  const todayData = list.filter(x => x[1] >= yesterday);
  console.log(list);
  console.log(list.length);
  
  const range = resultsheet.getRange(1,1,list.length,list[0].length);

   // 対象の範囲にまとめて書き出します
  range.setValues(list);


  const body = ""

  const ssUrl = "このスプレッドシートのA1セルのUrlを取得して貼ろう！"

   // メール本文のHTMLを構築
  let htmlBody = "<html><body><h1>スプレッドシートデータ</h1><table border='1' cellspacing='0' cellpadding='5'>";
  
  // ヘッダ行を追加
  htmlBody += "<tr>";
  for (let i = 0; i < list[0].length; i++) {
    htmlBody += "<th>" + list[0][i] + "</th>";
  }
  htmlBody += "</tr>";
  
  // データ行を追加
  for (let i = 1; i < list.length; i++) {
    htmlBody += "<tr>";
    for (let j = 0; j < list[i].length; j++) {
      htmlBody += "<td>" + list[i][j] + "</td>";
    }
    htmlBody += "</tr>";
  }
  
  htmlBody += "</table></body></html>";



  //html += `<p> ${list}</p>`;
  htmlBody += `<p><a href="${ssUrl}">スプレッドシート</a>で確認！</p>`;
  GmailApp.sendEmail("送信したいメールアドレス","昨日からの更新ファイルとか", body, {htmlBody: htmlBody});
  
  }

```

# こんな感じのGメールが届きます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1167883/868e77bc-d55f-1cee-946e-6218af074f84.png)


これを動かすと、任意のGoogleドライブのファイルリストを作成してくれた上に最終更新日を教えてくれます。
適度な時間ベースのトリガーをつけておくと、メール通知してくれて変更があったファイルだけを開いて確認すればよくなりますね！
みんな、GASのコードってどうやって保存してる？？

# GAS最大の弱点ってヴァージョン管理じゃね？
と、思ってるやまねこです。

書いてるとおり、GASは一度書き換えて保存すると元に戻せないんですよ、ヨヨヨ。
(※、と、思ってるんですけど誰かヴァージョン管理できる昨日知ってる人いたら教えて）

## Googleドキュメントでコードブロック作って貼る。
組織でGoogleWorkspaceの有料版を利用している場合は、Googleドキュメントにコードブロックを挿入することができます。
Googleドキュメントには、変更履歴を過去に戻れる機能がついていて、回復した版を別ファイルとして複製して差分を確認することもできる、最新の版に名前をつけることもできます。
GASを書き換える際にGoogleドキュメントのコードブロックに上書きして、コメントを残しておけば立ち戻ることができるということですね。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1167883/02a743b3-c105-bd03-57ce-3ab2f4cdb5fa.png)


## VisualStudioCodeで保存する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1167883/846160a7-05b3-cfdd-f084-792e8f71ccdd.png)


これはGASの編集時にも地味に使える技なんですが、GASのエディタってライトモードしか標準でついていなくてあまり視認性が良くないと思っています。

なので、ある程度まとまったコードを書いたらVisualStudioCodeに張り付けてjs形式で保存していい感じの色分けをすることで文法の誤りを確認したりしています。

コードがいい感じに色分けしている状態でそのままVisualStudioCodeを使ってGitで管理してもいいでしょうし、gitの利用が制限されている環境であれば、まぁ、アナログにver.0xみたいに保存してもいいでしょう。



私はVisualStudioCodeで色付けしたものをGoogleドキュメントで保存して、折衷案とってます。

# めっちゃ困るパターン
コンテナバインドスクリプトで書いている場合、ファイルを変更履歴から以前の版に戻っても、GAS自体には版無いので、ファイルの編集・復元作業ととGASの編集を同時並行に行うと訳わからんくなります。

なので、ファイル編集時はどっちかだけにしましょうね。
【UEFN】Verseで信号リモコンのシグナルを購読してみる

## 背景
信号リモコンで小道具を配置する動画をXで見かけてたり、BOX PVPでもこのリモコンを使って特殊な行動をやっていたのもあって調査してみました。

## やること

信号リモコンを実行したときにログを出す。

### 動画

<iframe width="560" height="315" src="https://www.youtube.com/embed/pn_0gdB0XFs?si=drca-wHR6RxOUloO" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>

## 実装

### 利用するDevice
アイテムグランター
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/692976/7ea9f84a-3b05-233c-fbff-d013a908b032.png)
レア度が``コモン``のリモコンを取得できるようにします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/692976/a1308e48-aada-e72d-453d-89d6008d6a9c.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/692976/cd1540de-76dd-5554-d093-3ee71184dbc7.png)

信号リモコンマネージャー
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/692976/bd5d63eb-9980-2920-0080-853e8234a328.png)
信号リモコンのティアをコモンにします。（レア度がコモンのリモコンにしか反応しないようにしています）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/692976/1ebf5fce-cf33-5fb5-0614-04fbc561bf63.png)

### 全体コード

```
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }

signal_controller_device := class(creative_device):

    @editable
    Signal:signal_remote_manager_device=signal_remote_manager_device{}

    @editable
    Item:item_granter_device=item_granter_device{}

    OnBegin<override>()<suspends>:void=
        Signal.PrimarySignalEvent.Subscribe(OnSignal)
        Players := GetPlayspace().GetPlayers()
            for (Player : Players):
                Item.GrantItem(Player)

    OnSignal(Agent : agent):void=
        Print("Signal Received")
```

``item_granter_device``で信号リモコンをプレイヤーに持たせます。

``PrimarySignalEvent``でリモコンのイベントを購読し``OnSignal``を実行します。
### ``signal_remote_manager_device``を見てみる

```
 # Used to trigger a custom response to a *Primary* or *Secondary* signal, sent by a *Signal Remote* item.
    signal_remote_manager_device<public> := class<concrete><final>(creative_device_base):
        # Signaled when a player has triggered the *Primary* signal using a *Signal Remote* item.
        # Sends the `agent` that triggered the signal.
        PrimarySignalEvent<public>:listenable(agent) = external {}

        # Signaled when a player has triggered the *Secondary* signal using a *Signal Remote* item.
        # Sends the `agent` that triggered the signal.
        SecondarySignalEvent<public>:listenable(agent) = external {}

        # Enables this device.
        Enable<public>():void = external {}

        # Disables this device.
        Disable<public>():void = external {}
```
``PrimarySignalEvent``では左クリックしたイベント購読で、``SecondarySignalEvent``が右クリックイベントの購読になります。あとはデバイスの有、無効化関数になります。

## まとめ
こちらの信号リモコンは比較的プレイヤーの好きなタイミングにイベント購読させることができるのでカスタムな武器を作れそうだと感じました。

## 余談
この度UEFN/Verseに関するオープンコミュニティサーバーを建ち上げました。ちょっとでも興味があれば奮ってご参加くださいませ。

https://discord.gg/MehcNhyxdq
C# Structパフォーマンス向上Tips

C#のStructは使い方を間違えるとパフォーマンスが大きく落ちるような罠がいくつかあるので、それらを簡単にまとめていきたいと思います。


# structの防衛的コピー
readonlyで宣言されたstructのプロパティやメソッドを呼び出すと、readonlyであることを保証するためにコピーを生成してそれを操作する挙動をします。
```cs
struct Foo
{
    int num = 7;

    public int NumProperty => num;

    public int GetNum()
    {
        return num;
    }
}
//------------------------------
{
    readonly Foo foo = new Foo();
    int tmp = 0;

    tmp = foo.num; //フィールドにアクセス　コピーなし
    tmp = foo.NumProperty; //プロパティー呼び出し　コピー発生
    tmp = foo.GetNum(); //関数呼び出し　コピー発生
}
```

# 防衛的コピーの対策
c#7.2からstructにreadonlyをつけれるようになりました。
このstructはすべてのフィールドにreadonlyをつけなければいけない制約がありますが、コピーが発生しなくなります。
```cs
readonly struct Foo
{
    readonly int num = 7; //コピーなし

    public int NumProperty => num; // コピーなし

    public int GetNum() // コピーなし
    {
        return num;
    }
}
```

# 構造体の参照渡し
構造体を引数に渡すときはコピーされて渡されます（値渡し）。
サイズの大きい構造体の場合は効率が悪いので、このコピーを無くす方法として参照渡しがあります。
個人的な参照渡しをするかどうかの判断は以下の2つです。

- 高頻度でその関数が呼び出される
- 構造体のサイズが16byteよりも大きい

c#7.2以降は3つの参照渡しがあります。
- ref （入力・出力）
- out （出力）
- in （入力）

in修飾子は少し特殊で読み取り専用の参照渡しでreadonlyな形で渡されます。
つまりreadonlyで宣言していない構造体でも、inで参照渡しした関数内でメソッド・プロパティを呼び出してしまうと防衛的コピーが発生します。inで参照渡しをする場合はreadonlyで定義された構造体に限定したほうがいいと思います。

```cs
void FooMethod(in Foo foo) {
    // ここでプロパティ・メソッドにアクセスするとコピー発生
    var tmp = foo.GetNum(); // コピー発生
}

Foo foo = new Foo();
FooMethod(foo);
```

# boxing回避
構造体を参照型であるobject型にキャストするために、ヒープにコピーするのがboxingです。
このboxingが頻発するとガーベジコレクションが走ってパフォーマンスに影響を与えるので可能なら回避するべきです。
意図的にobject型にキャストすることは殆ど無いですが、等価判定でEqualsが実装されていない場合にboxingが発生します。
```cs
Foo foo1 = new Foo();
Foo foo2 = new Foo();
var isEquals = foo1.Equals(foo2); // Equalsが実装されていないためobjectにキャストされて比較される
```
IEqualsインターフェイスを実装すれば構造体のEqualsメソッドが呼ばれます。
```cs
struct Foo : IEquatable<Foo> {
    int num;

    public override bool Equals(object obj) {
        return obj is Foo foo && Equals(foo);
    }

    public bool Equals(Foo other) {
        return num == other.num;
    }
}
```
辞書のキーとして使用する構造体の場合はGetHashCodeメソッドもオーバーライドしておくほうがいいです。
```cs
struct Foo : IEquatable<Foo> {
    //...省略
    public override int GetHashCode() {
        return HashCode.Combine(num);
    }
}
```

IEquatableインターフェイスの実装と、GetHashCodeの実装はVisualStudioの場合は自動実装することができます。
実装したい構造体を選択して [ctrl + .] → EqualsおよびGetHashCodeを生成する...
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3485511/623d9245-0b6b-0cc3-5c33-300985364806.png)

c# 10.0から追加された[record struct](https://ufcpp.net/study/csharp/datatype/record/#vs-normal-struct)ではIEquatableインターフェイスがコンパイラで生成されるのでこちらを使用するのも手です。


# メモリ構造の確認
sharplabを使うとClassやStructの実際のメモリ上の構造を確認できます。

https://sharplab.io/

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3485511/036ba5a3-444b-0027-1fc3-eb8156955905.png)


# 参考資料
https://ufcpp.net/study/csharp/cheatsheet/ap_ver7_2/
https://qiita.com/fksk36/items/dfa7e7e6ab40ecdd4ee8
2023/12/19主にITとかセキュリティの記事

AWS Well-Architected Tools のレンズカタログで選択出来る全てのレンズの設計原則と柱をまとめてみた(12/17)
https://dev.classmethod.jp/articles/aws-well-architected-tools-lens/

[レポート] AWS WickrとAmazon EventBridgeによるインシデントレスポンスの強化についてのChalkTalkに参加しました #AWSreInvent #BIZ203-R(12/17)
https://dev.classmethod.jp/articles/reinvent2023-wickr-biz203/

不正送金被害急増２６倍　銀行かたり偽サイト誘導(12/17)
https://www.yomiuri.co.jp/local/chubu/news/20231217-OYTNT50000/

経済安保の機微情報に接触可能な官僚・研究者を一元審査する機関新設へ…ＡＩや半導体分野想定(12/17)
https://www.yomiuri.co.jp/economy/20231217-OYT1T50006/

千葉の国立木更津高専にサイバー人材育成拠点、出前授業や企業アドバイザーにも(12/17)
https://news.yahoo.co.jp/articles/1e8b408c525bf5a4356affc26b2333fc36011ef9?source=rss

デジタル強国エストニア、年2000件攻撃に揺るがず
テクノ新世インタビュー　カリユライド前大統領(12/17)
https://www.nikkei.com/article/DGXZQOUC13DHY0T11C23A2000000/

年末年始の詐欺対策5選(12/17)
https://news.yahoo.co.jp/articles/ce038cd1bce4e426ff19e98fa77afcfa4bc06c62?source=rss

NTTデータの責任者が語る「生成AIの活用法」　他社にまねできない強みとは？(12/17)
https://news.yahoo.co.jp/articles/857e8fac502b657016929bdc27231a9fc96fe2f2?source=rss

DeFiの始め方を解説｜代表的なプロジェクトや注意点も紹介(12/16)
https://crypto-times.jp/how-to-defi/

予算・税制作業大詰め　―政府、重要課題 来年度へ先送り(12/16)
http://allatanys.jp/blogs/22657/

日・カンボジア外相会談(12/17)
https://www.mofa.go.jp/mofaj/press/release/pressit_000001_00109.html

日本ＡＳＥＡＮ友好協力５０周年特別首脳会議　総理夫妻主催晩餐会(12/16)
https://www.kantei.go.jp/jp/101_kishida/actions/202312/16asean_dinner.html

「doda」のブロック機能が働かず　転職活動が勤務先に露見する不具合(12/18)
https://xtech.nikkei.com/atcl/nxt/mag/nc/18/020600011/121300146/

日立、富士通、NEC　これからの新オフィス(12/18)
https://xtech.nikkei.com/atcl/nxt/mag/nc/18/020600014/121200163/

クアルコムがエッジAIの覇権奪取へ、お手本は「NVIDIAモデル」(12/18)
https://xtech.nikkei.com/atcl/nxt/column/18/00001/08694/

過去との決別が不可避　試練の全銀システム(12/18)
https://xtech.nikkei.com/atcl/nxt/mag/nc/18/020600014/121200164/

RAG（Retrieval Augmented Generation）(12/18)
https://xtech.nikkei.com/atcl/nxt/mag/nc/18/020600009/121200157/

老朽化より深刻な「高齢化」問題　システムが抱える時限爆弾(12/18)
https://xtech.nikkei.com/atcl/nxt/mag/nc/18/020600007/121300156/
隠し属性付与でFinderを綺麗にしよう

:::note warn
この記事における、綺麗とか汚いとか、ありえるとかありえないとか、好きとか嫌いとかは全部私の主観による独断と偏見で決定されています。
:::

## お前らの書類フォルダはダサい

全ての作業が始まる書類フォルダ、周りの人とか見てると意外と整ってる人がいない印象です。直接PDFとか写真とかを書類フォルダにおくのはオブジェクト指向じゃないので論外[^1]として、なんかこれ、整然としませんね…。

![スクリーンショット 2023-12-19 3.31.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/628782/191ab8c0-75fd-d738-7ac5-42575f892b08.png)

フォルダの数が多すぎます！多いのが問題というより、使っていないフォルダが多いのが問題です。

作業するためにアプリを開いたら、立ち上げた瞬間脳死で`~/Documents`にフォルダを用意する**おせっかいアプリのせい**で、使っていないのに存在する（のに消したらまた復活する）フォルダがいくつも存在します。

## 隠し属性を付与する

消したらまたぶり返すので、あくまでもFinder上で消えてくれればいいです。今回はディレクトリに隠し属性を付与することでこの問題を解決しようと思います。（見えないだけでディレクトリは存在するので新たに作られる心配もありません！）

### 公式

```zsh:隠し属性付与
SetFile -a V "PASS"
```

```zsh:やっぱり見えるようにしたい時
SetFile -a v "PASS"
```

大文字か小文字かを変えるだけで、隠し属性を付与できます。よくあるディレクトリ名の先頭に.をつける方法だと、ディレクトリ名が変わってしまうので、今回はSetfileを用いました。

![スクリーンショット 2023-12-19 3.41.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/628782/979e22b3-dcdc-3440-c6bb-c377357cc8c2.png)

こんな感じでターミナルにフォルダをドラッグ&ドロップすると簡単にディレクトリ名を移せます。""はディレクトリ名にスペースが含まれる時用の対策なので、スペースが含まれないディレクトリであれば""は不要です。（あってもいいです。）

![スクリーンショット 2023-12-19 3.44.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/628782/9447b002-2acc-a0a5-d066-6b4c4f11e5f5.png)

すっきりしました！！！！

## 一時的に見たい時

一時的に隠しフォルダにアクセスしたいときはFinderを開いた状態で`Cmd+Shift+.`で見ることができます。元に戻す時も同じショートカットが使えます。

![スクリーンショット 2023-12-19 3.46.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/628782/ca51b6f7-be27-9cd3-5917-0369a6e7ce1a.png)

隠しフォルダはこんな感じで薄く表示されます。

## 終わりに

整理整頓して良い新年を迎えましょう。

[^1]: いろんな意見があっていいと思います。はい。
Google Chatスペースに爆速でメンバー招待

## はじめに
スカラパートナーズAdventcalendar19日目担当です。
今回はGoogle Chatスペースにメンバーを簡単に一括追加、削除を行える「Bulk Member Manager」について紹介します。

## Bulk Member Managerとは？
Googleが2023年9月27日に提供を開始したGoogle Chat専用のアプリです。

![スクリーンショット 2023-12-19 1.18.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3480255/05380972-d17e-4625-7039-02233305a026.png)

従来Google Chatにメンバーを追加する方法はグループ単位や、メールアドレス、名前を1件ずつ打ち込む必要があったため、100人以上のメンバー追加だとかなり根気のいる作業でした。

このような作業を手助けしてくれるのが「Bulk Member Manager」なのです！！
Google Chatのスペース管理者、もしくはメンバーを管理する権限を持つスペースのメンバーであれば、チャット入力欄に下記コマンド入力で一括操作を行うことができます。

- `/addDialog`：メールアドレスをコピー＆ペーストしてメンバーを追加
- `/removeDialog`：メールアドレスをコピー＆ペーストしてメンバーを削除
- `/addCsv`：CSVファイルを添付してメンバーを追加
- `/removeCsv`：CSVファイルを添付してメンバーを削除


## いざ実践
Google Chatスペース作成については割愛させてもらいます。

### 前提条件
- 「Bulk Member Manage」を利用するには、下記のいずれかである必要があります
    - スペースの管理者
    - メンバーを管理する権限を持つスペースのメンバー
- アプリをインストールするには、Google Workspace管理者の許可が必要です

### 手順
#### 1. スペースにBulk Member Managerアプリを追加
スペース名をクリックし「アプリと統合」をクリックします。

![スクリーンショット 2023-12-19 2.29.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3480255/d6018bda-1aea-f159-8208-c47abf13bde4.png)

ページ右の「アプリを追加」をクリックします。

![スクリーンショット 2023-12-19 2.27.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3480255/1f811ca6-2635-c17a-dbfd-f612e38336ad.png)

「Bulk Member Manager」と入力し、下に出てきたアプリをクリックし追加します。

![スクリーンショット 2023-12-19 2.31.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3480255/4b7224e7-470e-8d8c-d66a-cd45fe49be80.png)

#### 2. CSVファイルを使ってメンバーを一括追加する
使用するCSVファイルには、追加メンバーのメールアドレスが格納されていれば問題ありません。
CSVファイル例）

![スクリーンショット 2023-12-19 2.41.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3480255/9e9cb091-46e2-f0e5-e667-498d55f0eae0.png)

次に、Chat入力欄に`/addCsv`とコマンドを入力後、CSVファイルを添付し送信します。

![スクリーンショット 2023-12-19 2.44.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3480255/4dd55a3d-f31a-23c9-5df7-686129b3d2b3.png)

すると「Bulk Member Manager」の設定を促されるので、Googleアカウントへのアクセスをリクエスト許可しましょう。

![スクリーンショット 2023-12-19 2.47.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3480255/3b37ecda-0166-f0fa-c439-0aaf1063f4ae.png)

設定が完了後、チャットスペースに戻るとCSVファイルに格納されていたメールアドレスをもとにメンバー追加が行われます。

![スクリーンショット 2023-12-19 2.53.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3480255/f43f6916-1e84-b832-cf38-0cd23b13ca44.png)

これでCSVファイルを用いた一括でのメンバー追加は以上になります。

また、CSVを用いてメンバー削除する場合は、メンバー追加時と同じ要領で`/removeCsv`と入力後、CSVファイルを添付し送信することで一括削除することができます。

今回の記事ではメールアドレスでのメンバー追加/削除方法は紹介できなかったので、気になる方は下記公式サイトからチェックしていただければと思います。

https://support.google.com/chat/answer/14016224#zippy=%2Ccsv-%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%2C%E3%83%A1%E3%83%BC%E3%83%AB%E3%82%A2%E3%83%89%E3%83%AC%E3%82%B9


## おわりに
以前Google Chatスペースに100人以上のメンバー追加をすることがあり、心が折れそうになりましたが、このアプリとの出会いで救われました。

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;~ Thank you Bulk Member Manager ~
【Vue3.2】styleタグ内でv-bind

# はじめに
Styleタグ内でJSの変数とか書けないかなーと思ったら、ちょうどいいものがあったので備忘録がてら書きます。

# 環境
- vue@3.2.41
- TailwindCSS

# やってみた
- ref変数
```vue:hoge.vue
<template>
  <div class="p-10">
    <p class="text-blue font-bold text-xl">ほげほげ</p>
  </div>
</template>

<script setup>
import { ref } from "vue"

const textColor = ref('#0000ff')
</script>

<style scoped>
.text-blue {
  color: v-bind(textColor)
}
</style>
```

![スクリーンショット 2023-12-19 1.15.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/752214/001d6fd8-d626-ba24-4154-bb4853076955.png)

---

- 関数呼び出し
```vue:hoge.vue
<template>
  <div class="p-10">
    <p class="text-green font-bold text-xl">ほげほげ</p>
  </div>
</template>

<script setup>
import { ref } from "vue"

const textColor = () => '#48bb78'
</script>

<style scoped>
.text-green {
  color: v-bind(textColor())
}
</style>
```
![スクリーンショット 2023-12-19 1.14.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/752214/631d7cdb-15dc-9ecd-3ba8-175d91842d0a.png)

---

- Styleタグ内でJavaScriptの式を記述
:::note warn
JavaScript式を展開するためには、
v-bindの引数をシングル(ダブル)クォーテーションで囲ってください。
:::
```vue:hoge.vue
<template>
  <div class="flex items-center">
    <p class="text-line p-10 font-bold text-5xl">ほげほげ</p>
    <button
      class="bg-gray-300 h-10 w-1/5 border-2 border-black"
      @click="isUnderLine = !isUnderLine">
      ボタン
    </button>
  </div>
</template>

<script setup>
import { ref } from "vue"

const isUnderLine = ref(true)
</script>

<style scoped>
.text-line {
  text-decoration: v-bind('isUnderLine ? "underline" : "overline"');
}
</style>
```
![タイトルなし.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/752214/5a100f4d-20ee-8d68-13dc-475ac4687074.gif)

---

- 時間経過によるStyleの変化
```vue:hoge.vue
<template>
  <div class="bgc">
    change background-color
  </div>
</template>

<script setup>
import { ref } from "vue"

const bgStyle = ref('#ed8936')
setTimeout(() => {
  bgStyle.value = '#7f9cf5'
}, 3000)

</script>

<style scoped>
.bgc {
  background-color: v-bind(bgStyle);
}
</style>

```
![タイトル動画.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/752214/daff7782-3fd9-b9ae-cf55-9502235f2627.gif)

# まとめ
意外と便利。
Styleタグ内で複雑なJavaScriptは書けないが、簡単な変数・式展開であれば可能でした。

# 参考
https://ja.vuejs.org/api/sfc-css-features#v-bind-in-css
Web サイトやアプリケーションのモックアップを作る際、Figma データとして作らないもの

## この記事の概要

`Figma での○○の作り方` といった記事は多く存在します。
一方で `○○は Figma で作らない方が良い` といった記事は少ないように思います。

意外と、無理やり頑張ってしまったがゆえに後々大変になるパターンもあるので、作らない方が良さそうなものを 3 つ紹介します。

:::note warn
一般的なベストプラクティスとしてまとまっているジャンルではないので、かなり個人的な考えに基づきます。ご了承ください。
:::

## 複雑なネストが必要になるコンポーネント

例として `ol` や `ul` の挙動をしっかり再現するためのコンポーネントを考えてみます。

これらの要素はネストがあり、しかも `ol > ol` `ul > ul` `ol > ul` `ul > ol` と色々なパターンがあります。
そしてリストアイテムがいくつ並ぶかも分かりません。

Figma は 2023 年 12 月現在、コンポーネントの中に自由にコンポーネントを配置することができません。
予めパターンを網羅できるのであれば variants として定義できますが、逆に言えば網羅しない限りは中途半端なデータになってしまいます。

リスト要素の組み合わせ爆発の起きやすさから考えるに、コンポーネント化するには適さないと言えるでしょう。

### 対処法

ことリストにおいては `Type settings` の `List style` での設定だけにとどめておきましょう。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214677/dba67ddf-ea52-c942-cdc6-9dde3c804080.png)

マーカーの色やスタイルを変えることはできませんが、Figma データが中間生成物であることを考えれば十分だと思います。

正式なマーカーのスタイルは Storybook 上などで確認して、Figma での完全再現を求めないことをオススメします。

リスト以外であっても、代表例だけを作るとか、コンポーネント化を諦めるとか、選択肢はたくさんあります。

いずれにせよ、すべてを Figma で再現するのが重要なわけではない、という意識が大事です。

## 背景のビューごと表示するモーダルウインドウ

タイトルだけでは分かりづらいので、画像をご覧ください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214677/2f132130-cae5-8552-82c7-2dbad61fcab4.png)

上の場合、モーダルウインドウ（とその背景の半透明の黒いレイヤー）以外は同じ見た目です。
たしかにこのように作るとビューの一覧化には便利なのですが、弊害もあります。

- 半透明の黒いレイヤーの後ろにある要素を触りたいとき、いちいち前面の要素が邪魔になる
- 背景の要素が古くなっている場合、それに気づきづらい

要は、背景はあくまでコピー & ペーストした要素でしかないため、データとしてリンクしていないのが厄介ということです。
もちろんビューをすべて一体のコンポーネントにしてしまえば良いですが、それはそれで大変な場合もあります。

ちなみに代表例としてモーダルウインドウを挙げましたが、ドロワーメニューなども同じです。

### 対処法

モーダルウインドウだけを作り、実際の表示はプロトタイプで確かめます。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214677/b4736a56-6b5d-67f1-fb01-c22ce0e9b270.png)

このように、プロトタイプモードで `Open overlay` を選び、`Add background` にチェックを入れれば先ほど作っていたものと同じ見た目を再現できます。

## コードとは違う単位のコンポーネント

例えば `input` と `label` をまとめた次のようなコンポーネントがあるとします。
（説明の中核の部分だけを表すためにかなり雑に書いています）

```jsx
function InputComponent({ label, inputType }) {
  return (
    <label>
      <span className="text-sm">{label}</span>
      <input type={inputType} className="border border-gray-300 rounded-md p-2 mt-1 block w-full" />
    </label>
  );
};

export default InputComponent;
```

このとき、Figma で `input` と `label` をそれぞれ別のコンポーネントにしない、ということです。
この例の場合は大したことありませんが、コードと Figma でコンポーネントの粒度や名前が変わると、コミュニケーションのロスが発生します。

逆に、コード側も `input` と `label` がそれぞれ別のコンポーネントになっている場合、分けた方が良いでしょう。

### 対処法

データの作り自体は上記の通りです。

コミュニケーションとしては、デザイナーとエンジニアが普段から話し合っておくことが重要です。

実際にはどうしても違う単位にせざるを得ないこともあるでしょうが、その場合にもどんなプレフィックスをつけるかとか、どういうディレクトリ構造にするかとか、話し合うことで認識の齟齬が減ります。

## 最後に

日頃レビューで指摘する機会が多いものを抽象化してまとめました。

色々な場面で当てはまるように抽象化して書いたら 3 種類だけになり、しかも全部当たり前な感じになってしまいました。
そっくりそのまま当てはまらなくても、近しい場面は結構あると思います。
そんなとき、この記事を思い出してもらえたら幸いです。
【UEFN】Verseでプレイヤーの移動速度上げてみる

## 背景
BOX PVPで移動速度の速い役職を作るために調べました。

## やること
トリガーを押したら移動速度が常に上がるようにすること。
### 動画
<iframe width="560" height="315" src="https://www.youtube.com/embed/QQuENbWQupA?si=5Ky4JMEbN1chbijE" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>

## 実装

### 利用するDevice
ムーブメントモジュレーター
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/692976/0fa296ba-ebaf-934f-bcec-f7bc90bd0704.png)
設定
- スピードを3倍
- 効果無限

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/692976/c8fa1140-39e3-96b2-1cd5-199c522f9c50.png)

いつものトリガー
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/692976/8a923ce4-73e1-6844-2d7f-b151181e48c4.png)


### 全体コード
```
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }


player_speed_device := class(creative_device):

    @editable
    Speed:movement_modulator_device=movement_modulator_device{}

    @editable
    Trigger:trigger_device=trigger_device{}

    OnBegin<override>()<suspends>:void=
        Trigger.TriggeredEvent.Subscribe(SpeedUp)

    SpeedUp(QAgent : ?agent):void=
        if (Agent := QAgent?):
            Print("Speeding up")
            Speed.Activate(Agent)
```

とても単純でスピードを速くしたいプレイヤーの``Agent``を取得してActivateするだけでした。


``movement_modulator_device``の中身を見てみます。
```
    # Used to temporarily modify the speed of `agent`s and vehicles.
    movement_modulator_device<public> := class<concrete><final>(creative_device_base):
        # Signaled when this device is activated by an `agent`.
        # Sends the `agent` that activated this device.
        ActivationEvent<public>:listenable(agent) = external {}

        # Enables this device.
        Enable<public>():void = external {}

        # Disables this device.
        Disable<public>():void = external {}

        # Activates this device's movement effect on `Agent`.
        Activate<public>(Agent:agent):void = external {}
```

凄く単純でデバイスの有、無効化やActivateしたときのイベント購読だけでした。スピードの調整は行えないようです。

## まとめ
こちらもすごく単純にいけそうでした。

欲を言うとこのようなデバイスを介さずに``fort_character``から直接操作出来たらいいのになと思いました。

## 余談
この度UEFN/Verseに関するオープンコミュニティサーバーを建ち上げました。ちょっとでも興味があれば奮ってご参加くださいませ。

https://discord.gg/MehcNhyxdq
コンセプト検証用をアプリをGlideでつくる！~みんなの一推しの飯屋を知りたいだ！~

こんにちは、ウエハラ　シンペイです。

普段飲み歩きするのが、好きでよくする質問があります。

### 「一番好きな飯屋教えて！」 

単純におすすめを聞くよりも、その人ならではのこだわりやストーリーが見えて、話聞くだけでもワクワクするんですよね。この質問をもっといろんな人に聞いてみたいと思いアプリにしようと思うのですが、今回はまずコンテンツを集めて検証するための検証用アプリをGlideとGoogle Formでサクっとつくってみました！

https://note.com/shinpex/n/nfe2229006c69

# Google Formで回答フォームを作成

Glideはスプレットシートのデータを使えることもあり、回答がそのままスプレットシートで管理できるGoogle Formで回答フォームを作成しました！

https://docs.google.com/forms/d/e/1FAIpQLSfxShurqXAqpVLYmOvJGSPSU296FcmaWbdaD5Q0v3RvCszs6A/viewform

↓こんな感じで解答が管理できます

<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3618353/a6868af0-0f6a-d4b0-66cb-066d92854f04.png">


# Glideでデータのビューをつくる

検証用ということで、とにかく時間をかけずにつくりたかったのでノーコードツールのGlideを使っていきます。Goole様ありがとう。

https://www.glideapps.com/


### 1.New AppsからGoogle Sheetsを連携
Glideのアカウント作成後にNew Appsを押すとSelect a sourceとしてGoogle Sheetsを選択できます。ここで先ほど作成したGoogle Formのものを選択します

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3618353/cc799327-e650-d8c1-c092-2f030875ac31.png)

### 2.GlideでSTYLEを選択してレイアウトを決定

今回は写真などもないので、シンプルなリストビューで作成していきます。一覧と詳細ページが自動で作成されており、アプリからコンテンツの「追加」「編集」「削除」ができるかどうかも簡単に設定できます。今回はGoogle Formからの登録に統一したかったので全てのチェックを外し、ボタンを押すとGoogle FormのURLに遷移するようにしました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3618353/92527326-5200-c9ce-6fee-c892d00615f4.png)


### 3.追加したいコンポーネントやアクションを編集
こんな感じでCOMPORNENTSからボタンを選び、ラベルやアクションを追加しました。同じような流れで詳細ページにマップを表示したりして簡単に追加することができました。

![スクリーンショット 2023-12-19 2.30.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3618353/ee5db1c3-c88f-c443-7b5b-ee54022a027d.png)


### 4.プライバシーなどの設定をしてパブリッシュ

アイコンの設定や公開範囲などを設定してパブリッシュを押すと簡単にサイトを公開知ることができました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3618353/6e28ce5b-cba6-50c0-3888-e3948991f39e.png)

##### 今回作成したアプリ
https://s-app-9aa4.glide.page/dl/d0a5f4

# 感想
ノーコードをつかうことで簡単にアプリケーションを作成することができました。実際の開発ではもう少しこだわりたいポイントがあるのでどうかなってところはありますが、検証段階のプロトタイプとしてはかなり使えるんじゃなかと感じました！
ビブルカードっぽいアプリを作りました。

[「クソアプリ Advent Calendar 2023」](https://qiita.com/advent-calendar/2023/kuso-app)の22日目の記事です。

昨日の記事は[Korangeさん](https://zenn.dev/korange)の
[インターネット上のすべてのサイトの文末に「知らんけど」を追加してデマ情報から身を守ろう](https://zenn.dev/korange/articles/5eb1e338f4cdc6)です。

# **1. 作ったもの**

ビブルカードっぽいアプリを作りました。

(開発者とワンピースとは一切関係がありません。)
(このアプリはしょーもないですが、ビブルカードにはロマンがあります。)

<img href="https://apps.apple.com/jp/app/id6471923198" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/158647/87c09b95-8ebf-f999-a885-df045cb6b20c.png" width="200">

![Screenshot 2023-12-19 at 1.44.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/158647/85d23866-8494-dd1f-6627-35acd93586aa.png)


# **2. 機能紹介**

- 端末を近づけるだけでカードの交換ができます。
- ユーザーのいる方向にカードが動きます。
- ユーザーの調子が悪いとカードが燃えます。(明示的に設定)

# **3. 技術的な部分**

- アプリ
    - 交換部分
        - MultipeerConnectivity
- バックエンド
    - BFF
        - Vercel + Next.js (無料)
    - データベース
        - FireStore (無料)
- イラスト
    - DALL·E 3

# **5. ちょっと頑張ったところ**
- ユーザーがFirebaseのログイン操作をせずに手軽に使えるようにしたかったのでBFFを噛ませてアカウントがなくても使えるようにした。
- 一応…カードを交換したユーザーの位置情報を直接ローカルにフェッチせずにBFF側で方向だけ計算して返すようにした。
- ホントはただカードがもぞもぞ動くだけにしてクソさ(シュールさ)を演出したかったけど、一度アプリレビューがリジェクトされたためにUIをわかりやすくせざるを得なかった。
    - AppleのレビューはPCで行っているらしく、わざわざ外で撮ったデモビデオを送ったりした。端末2台を別端末のカメラで撮った。
    - たしかになんの文脈も知らない人が見たら謎アプリすぎるなと思う。
- なんとなくロマンを感じたので、わざわざMultipeerConnectivityを使った。手渡し感もあって良かった。
- SwiftUIのアラートはViewに紐づいていたり、状態を保持するというめんどくささがあるため簡単なアラートを出すのも億劫だが`AlertManager.shared.show(...)`みたいな感じでどこからでも呼び出せるようにした。個人開発などで手軽に実装したい場合に有用なので別途記事にする予定。
Pythonで「リストの要素を昇順または降順に並び替える」の動作を確認してみた

# 概要

Pythonで「リストの要素を昇順または降順に並び替える」の動作を確認してみました。以下のページを参考にしました。

https://www.javadrive.jp/python/list/index11.html

# 実装

以下のファイルを作成しました。
```sample.py
numlist = [84, 75, 92, 90, 78]
newnumlist = sorted(numlist)
print("Before:", numlist)
print("After: ", newnumlist)

colorlist = ["Blue", "Red", "Green", "White", "Black"]
newcolorlist = sorted(colorlist)
print("Before:", colorlist)
print("After: ", newcolorlist)

numlist = [5, 3.14, 4.78, 4]
newnumlist = sorted(numlist)
print(newnumlist)

mylist = ["80", 75, 45, "68"]
#newmylist = sorted(mylist)

colorlist = ["Blue", "Red", "Green", "White", "Black"]
upcolorlist = sorted(colorlist)
downcolorlist = sorted(colorlist, reverse=True)
print("Orig:", colorlist)
print("ASC: ", upcolorlist)
print("DESC:", downcolorlist)

animallist = ["Cat", "monkey", "bear", "Sheep", "cow"]
sortlist = sorted(animallist)
lowersortlist = sorted(animallist, key=str.lower)
print(animallist)
print(sortlist)
print(lowersortlist)
```

以下のコマンドを実行しました。
```
$ python3 sample.py 
Before: [84, 75, 92, 90, 78]
After:  [75, 78, 84, 90, 92]
Before: ['Blue', 'Red', 'Green', 'White', 'Black']
After:  ['Black', 'Blue', 'Green', 'Red', 'White']
[3.14, 4, 4.78, 5]
Orig: ['Blue', 'Red', 'Green', 'White', 'Black']
ASC:  ['Black', 'Blue', 'Green', 'Red', 'White']
DESC: ['White', 'Red', 'Green', 'Blue', 'Black']
['Cat', 'monkey', 'bear', 'Sheep', 'cow']
['Cat', 'Sheep', 'bear', 'cow', 'monkey']
['bear', 'Cat', 'cow', 'monkey', 'Sheep']
```

# まとめ

何かの役に立てばと。
テストシナリオを作る時に考える縦軸と横軸の話

この記事は[ ソフトウェアテストの小ネタ Advent Calendar 2023 21日目 ](https://qiita.com/advent-calendar/2023/software-testing-koneta)の記事です。

こんにちは。QAエンジニアをしている @afracs です。
今回はテストシナリオを考えるときに、『個人的に意識していること』を整理する内容となっています。
QAやテスト界隈が長い方々からすれば当たり前の内容かと思いますが、将来の自分のために言語化しておこうと思い、本記事を書くことにしました。

## 縦軸と横軸
縦軸と横軸とは何を指すのか？
私の中では、以下の内容を指す言葉として使用しています。

<strong> 縦軸 </strong>
ある時点でのシステム状態の断面。

（具体例）
* 注文前　　→ （注文操作を行う） → 注文後
* バッチ処理前 → （バッチを流す） → バッチ処理後

<strong> 横軸 </strong>
時間経過や状態遷移によって変化していく流れ。

（具体例）
* 株式の注文受付日　→ 約定日 → 受渡日
* 口座開設申込 → 内容の審査 → 審査完了 → 口座開設完了

このような内容をテストシナリオとして考えるときに、縦軸と横軸として意識しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/890862/32dc3dd2-db0c-f4b8-6f6e-048e189615ca.png)


## これを意識すると何がお得なの？

一番のメリットはこの2つです。
* シナリオの漏れを無くしやすい
* ユーザーストーリー上どこまでできているのかの把握がしやすい

QAEやテスター視点で見ると、テストシナリオの作り方が変わってきます。

## テストシナリオの作り方が変わるって？？
テストシナリオを作るときに、前提条件によって書きっぷりが変わってきます。
具体的には以下のことを意識して作っていく感じですね。
* この機能は今後運用していくサービスだから、リグレッションテストで再利用できるように作ろう
* この機能は今回限りの部分だから、この仕様に合わせて細かく作ろう

特に新規機能のテストシナリオは細かく作りがちです（だって気になるし）
ただ、それをそのままリグレッションテストとして<strong>今後もずっと実施し続けるかといえばNO</strong>です。

当然、不要な部分を削ぎ落として使い回していきたくなります。

## テストシナリオの再利用
リグレッション用のテストシナリオとしては、機能担保や画面崩れがないことを確認したい意図のものを使っていきたいと考えるのが王道かと思います。
となると、上述した横軸ベースのテストシナリオがあると便利です。

逆に縦軸のテストシナリオが必要になる場面としては、画面操作や機能改修が大幅に行われた場合になります。
ただ、この場合は、新仕様に合わせてテストシナリオも手を入れることになると思うので、再利用というよりもカスタマイズといったイメージに近いかもしれません。

## どこまで意識してテストシナリオを組めばいいのか？
実際に本当にど新規でテストシナリオを作る場合、どこまでの未来を想定して作るのかは結構悩みどころだと思います。
細かくみたいポイントもあるでしょうし、時間との戦いもあるし、、、と色々考えなければいけません。。。

私個人のおすすめとしては 『両方作る』 です！
作り方としてはこれ↓
1.　横軸の全体の流れを見るテストシナリオを最初に作る
2.　細かく見たいポイントは縦軸方式で作っていく
3.　横軸シナリオの方に、画面デザインとかリグレッションでも見たくなる部分を追加する
4.　入れ込めないものを縦軸シナリオの方に追加で作っていく

こうやって作ると、全体を俯瞰してみるテストシナリオと、詳細部分を細かく見ていくテストシナリオが出来上がります。
最初のテストタイミングだと全部実施することになりますが、その後の再利用するシーンでは必要なシナリオだけ抽出してテストすれば良くなるので、テストシナリオ多すぎ問題とかの解決策の１つになるかも？


## 最後に
取り止めもなくつらつらと書いてしまいましたが、他の方々がちゃんと有益な記事を書いてくださっているので、私のは初心者が書いたんだな的な感じで暖かい目で読んでもらえると嬉しいです。
また、これ以外にもこんな考え方取り入れてみるといいかも！など、コメントいただけると助かります。

ここまで読んでくださった方々、ありがとうございました！
【UEFN】Verseで武器のダメージ倍率増やしてみる

## 背景
今回も前回と同じでBOX PVPで武器のパワーが上がる役職を作りたいということで調べ始めました。

## やること
トリガーを踏んだらそのプレイヤーの攻撃力を2倍にする。

### 動画
<iframe width="560" height="315" src="https://www.youtube.com/embed/j1JdaY3JkQs?si=9jPD6qqoFFzifHiE" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>

## 実装

### 利用するDevice
ダメージ増幅パワーアップ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/692976/2ac5b243-6bb9-0e6a-75fb-6b6e8e9201b5.png)
ダメージ増幅の設定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/692976/fc6b9b04-393a-8b34-a7cd-4126b8fcbb87.png)


いつものトリガー
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/692976/1c611f6e-5e23-5adf-7182-3a73da80573a.png)


### 全体コード
```
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }


player_damage_device := class(creative_device):

    @editable
    Damage : damage_amplifier_powerup_device = damage_amplifier_powerup_device{}

    @editable
    Trigger :trigger_device = trigger_device{}

    OnBegin<override>()<suspends>:void=
        Trigger.TriggeredEvent.Subscribe(DamageUp)

    DamageUp(QAgent : ?agent):void=
        if (Agent := QAgent?):
            Print("Damage Up")
            Damage.SetMagnitude(2.0)
            Damage.Pickup(Agent)

```
トリガーに反応すると攻撃の倍率を上げて踏んだプレイヤーに付与させています。

```
# Sets the *Magnitude* for this powerup, clamped to the Min and Max defined in the device.
        # Will not apply to any currently applied effects.
        # For the Damage Amplifier Powerup, this is the damage multiplier.
        SetMagnitude<public>(Magnitude:float):void = external {}
```
``SetMagnitude``では攻撃の倍率を``float型``で宣言する形になっています。

## まとめ
こちらも簡単に実装できました。本当に基本的な能力の部分を変更するのは簡単そうですね。

## 余談
この度UEFN/Verseに関するオープンコミュニティサーバーを建ち上げました。ちょっとでも興味があれば奮ってご参加くださいませ。

https://discord.gg/MehcNhyxdq
酢ぶるぐ

## 在宅ペペロンチーノ
在宅ペペロンチーノ、それは昼休みの限られた時間で作るペペロンチーノ。
アンチョビの代わりにシーチキンで作成する為材料集めも楽だし、賞味期限も長いもんばかりなので買い溜めて週2くらいこれ食ってる。

### 材料
- パスタ
- 塩
- 岩塩(あれば)
- オリーブオイル
- パセリ
- シーチキン缶
- 唐辛子(輪切り)
- ニンニク(国産かスペイン産が良い、中国産は臭いばかり目立つのであんまり※個人の感想)

### 所要時間
10分

### 作り方
#### 水を沸騰させる
・鍋に水をいれ加熱。水は多いければ多いほどパスタがモチョチョしなくなるので3㍑くらいで茹でる、塩は小さじ二杯入れる。沸騰させる。
#### 下ごしらえ
・沸騰させている間にニンニク2片くらい刻む。あんまり細かくしなくてよい、めんどいし焦げると味が悪くなるので。
・オリーブオイル大匙3くらい強火でニンニクを炒める、揚げる感覚に近い。ペペロンチーノは油とニンニクのバケモンなので多すぎてもよい。泡が出てきたら弱火。
#### 茹で
・このタイミングで沸騰してるはずなのでパスタを茹でる。製品と好みによるが7分くらい。

#### 輪切り唐辛子
・を大匙1(任意)くらい炒める。焦がすな。

#### シーチキン
・を油ごと炒める。製品によって水分があるのか跳ねるのでかき混ぜる、超弱火。生臭さを飛ばすので1分くらい炒められると良いね。

#### ゆで汁
・を適当にオリーブオイル諸々の中へぶち込む。大匙3くらい。乳化を祈り、混ぜる。

#### 岩塩
・グラインダー10ひねりくらい。味見しながら。なければ普通の塩、小さじ1/2くらいから試すこと。

#### 終わり
・パスタが茹で上がった頃なので混ぜる、しかしオリーブオイル諸々の火は止めておく事。さもないと触感が焼きそばとなる。
・盛り付け。
・パセリを散らす。パセリ意外と大事、たくさんかけるとうまお。


## 在宅アラビアータ
在宅アラビアータ、それは昼休みの限られた時間で作るアラビアータ。
トマトジュースで作る、賞味期限長いもんばかりなので買い溜めて週2くらいでこれ食ってる。

### 材料
- パスタ
- 塩
- 岩塩(あれば)
- オリーブオイル
- 黒コショウ
- スライスベーコン
- 唐辛子(輪切り)
- トマトジュース(無塩)200mlくらい
- ニンニク(国産かスペイン産が良い、中国産は臭いばかり目立つのであんまり※個人の感想)

### 所要時間
15分

### 作り方
#### 水を沸騰させる
・鍋に水をいれ加熱。水は多いければ多いほどパスタがモチョチョしなくなるので3㍑くらいで茹でる、塩は小さじ二杯入れる。沸騰させる。後の工程が詰まってるので急いで沸かさなくてよい。
#### 下ごしらえ
・沸騰させている間にニンニク2片くらい刻む。あんまり細かくしなくてよい、めんどいし焦げると味が悪くなるので。
・オリーブオイル大匙3くらい強火でニンニクを炒める、揚げる感覚に近い。泡が出てきたら弱火。

#### ベーコン
・を短冊に切って炒めます。焦げる2個手前くらいまで。

#### 輪切り唐辛子
・を大匙1(任意)くらい炒める。焦がすな。

#### トマトジュース
・200mlをオリーブオイル諸々と融合。混ぜ、強火。
・**トロミが付くまで水分を蒸発させる必要がある、パスタの茹で上がりまでにしなければならないので火力を惜しまない。水分の蒸発が甘いとトマトジュースの味になるので良くない。**

#### 茹で
・このタイミングで沸騰してるとベスト。パスタを茹でる。製品と好みによるが7分くらい。

#### ゆで汁
・を適当にオリーブオイル諸々の中へぶち込む。大匙3くらい。乳化を祈り、混ぜる。ペペロンチーノの時ほど効果を感じないので真剣にやらなくて良い。
・パスタの茹で上がりが迫っている場合はしなくて良い。

#### 岩塩
・グラインダー10ひねりくらい。味見しながら。なければ普通の塩、小さじ1/2くらいから試すこと。

#### 黒コショウ
・グラインダー3ひねりくらい。調子に乗ると辛過ぎてやられる。

#### おわり
・パスタが茹で上がった頃なので混ぜる、しかしトマトジュース諸々の火は止めておく事。さもないと触感が焼きそばとなる。
・盛り付けて完成。
・好みでパルミジャーノ(無限)

### 備考
・ソースは先に完成しても良いがパスタは先に茹で上げるな。(のびるので)
・焦がすな。
・ニンニクとオリーブオイルけちるな。
・味見をしろ。
・やっぱり緑茶と八丁味噌が良いよ。
中学数学の交差する図形のアニメーション

# はじめに
中学数学でよく見る、図形が交差するときの重なった部分の面積を manim で書いてみました。面積や長さなどは書いていないのであくまでイメージを補完するためのものです。

# gif
大半の人はコードに興味ないと思うので結果から。

![RightAngleExample_ManimCE_v0.18.0.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3525137/a5cd3d05-7c12-a413-372b-fff9696e4894.gif)

# ソースコード
自分の環境（jupyter lab or jupyter notebook）がある人は最初のセルに下のコードを実行してから
```python
from manim import *
import numpy as np
```
これ↓をコピペ実行すればできます。
（コメントは見ないで......）
```python
%%manim -qm RightAngleExample
class RightAngleExample(Scene):
    def construct(self):
        vertex_1 = np.array([-1, -1, 0])
        vertex_2 = np.array([-3, -1, 0])
        vertex_3 = np.array([-1, 1, 0])
        line1 = Line(vertex_1, vertex_2)
        line2 = Line(vertex_1, vertex_3)
        line3 = Line(vertex_2, vertex_3)
        # RightTriangle = VGroup(line1, line2, line3)
        # Polygon is a closed shape with vertices with lines connecting them
        RightTriangle = Polygon(vertex_1, vertex_2, vertex_3, color=RED, fill_opacity=0.5)


        # Create the hexagon
        vertex_hex_1 = np.array([0, -1, 0])
        vertex_hex_2 = np.array([2, -1, 0])
        vertex_hex_3 = np.array([2, 0, 0])
        vertex_hex_4 = np.array([1, 0, 0])
        vertex_hex_5 = np.array([1, 1, 0])
        vertex_hex_6 = np.array([0, 1, 0])
        line_hex_1 = Line(vertex_hex_1, vertex_hex_2)
        line_hex_2 = Line(vertex_hex_2, vertex_hex_3)
        line_hex_3 = Line(vertex_hex_3, vertex_hex_4)
        line_hex_4 = Line(vertex_hex_4, vertex_hex_5)
        line_hex_5 = Line(vertex_hex_5, vertex_hex_6)
        line_hex_6 = Line(vertex_hex_6, vertex_hex_1)
        #hexagon = VGroup(line_hex_1, line_hex_2, line_hex_3, line_hex_4, line_hex_5, line_hex_6)
        hexagon = Polygon(vertex_hex_1, vertex_hex_2, vertex_hex_3, vertex_hex_4, vertex_hex_5, vertex_hex_6, color=BLUE, fill_opacity=0.5)
        
        # Define the starting and ending positions
        start_pos = np.array([3, 0, 0])
        middle_pos_1 = np.array([2, 0, 0])
        middle_pos_2 = np.array([1, 0, 0])
        end_pos = np.array([0, 0, 0])
                
        # Set the position of the RightTriangle
        RightTriangle.move_to(start_pos).set_fill(RED, opacity=0.5)
        # Create the animation
        
        self.add(hexagon)
        self.play(RightTriangle.animate.move_to(middle_pos_1), rate_func=linear, run_time=1) 
        self.wait()
        self.play(RightTriangle.animate.move_to(middle_pos_2), rate_func=linear, run_time=1)
        self.wait()
        self.play(RightTriangle.animate.move_to(end_pos), rate_func=linear, run_time=1) # move_to(end_pos) is that center of RightTriangle moves to end_pos 
        self.wait()
```
Opalとは（2023年版）

:::note info
[Ruby Advent Calendar 2023](https://qiita.com/advent-calendar/2023/ruby) 18日目の記事です。
:::

みなさんOpalを知っていますか？
[Opal](https://github.com/opal/opal)はRubyスクリプトをブラウザ等のJavaScriptランタイムで動かすための仕組みです。

Qiitaでも[2016年](https://qiita.com/advent-calendar/2016/opal)、[2018年](https://qiita.com/advent-calendar/2018/opal)にOpalのアドベントカレンダーが立てられていましたが、最近は[Opalのタグ](https://qiita.com/tags/opal)もやや寂しい状況です[^1]。
[^1]:今回のアドベントカレンダーでは[DXOpalでスイカゲームみたいなのを作った](https://qiita.com/sonota88/items/105e141d18372918e675)がありましたね

ブラウザでRubyを動かすといえば[ruby.wasm](https://github.com/ruby/ruby.wasm)がすっかりお馴染みになった感もありますが、Opalもまだまだ開発が続けられ、近日中にv2.0のリリースが予定されていますので、今改めてOpalの紹介をしておきます。

## Opalの仕組み
Opalはざっくり言うと、以下の3つが合わさったものと言えます。

- コードをRuby→JavaScriptに変換するトランスパイラ
- クラス・メソッドのシステムなど、Rubyランタイムのコア部分の実装
- Rubyの組み込みクラス・標準ライブラリの実装

3つ目の各種クラス（Array, Stringなど）も基本的にRubyで実装されていて、Opalの利用者側が書いたコードと合わせてコンパイルされます。
また、1つ目のトランスパイラはRubyで実装されているのですが、これ自体もJavaScriptへ変換することで、[TryRubyのプレイグラウンド](https://try.ruby-lang.org/playground/)[^try-ruby]のようにブラウザ上で入力されたRubyコードをその場で動かすといったことも可能になります。
[^try-ruby]:このプレイグラウンドはもともとOpalによるものがデフォルトでしたが、現在はruby.wasmがデフォルトでOpalと選択できるようになっています

## Ruby⇔JavaScriptの連携
Opalでは``` `` ```または`%x{}`の中にJavaScriptコードを書くことができます[^backtick]。
さらにその中に`#{}`でRubyコードを埋め込むことができ、Ruby⇔JavaScriptの連携が容易です。
[^backtick]: v2.0以降はマジックコメント`# backtick_javascript: true`が必要になります（無ければ通常のRubyと同じ`` Kernel#` ``の呼び出しになる）。

```rb
# backtick_javascript: true
`#{(1..3).to_a}.map(x => x * 2)`.each_with_index do |x, index|
  puts `Math.pow(x, index)`
end
```

こんな風にRubyとJavaScriptをごちゃまぜに書いてもちゃんと動きます。
この例では`Range#to_a`の戻り値に`Array.prototype.map`を呼び出し、その結果に対して`Array#each_with_index`を呼び出しています。
つまり、RubyのArrayがそのままJavaScriptのArrayとして使えたり、その逆ができています。
他にも、以下のクラスはRuby⇔JavaScript間で連携できるようになっています。

|Ruby|JavaScript|
|:---:|:---:|
|Array|Array|
|Boolean|Boolean|
|Exception|Error|
|Hash|Map|
|Number|Number|
|Proc|Function|
|Regexp|RegExp|
|String|String|
|Time|Date|

## RubyとOpalの非互換
Opalは、主にJavaScript上の制限のためにいくつか本家であるCRubyと仕様が異なっている面があります。

たとえば、上の表でRuby列に見慣れないクラスがあるのに気づいたでしょうか？OpalではCRubyにないクラスが一部存在しています。

### Booleanクラス
true, falseがそれぞれ`TrueClass`, `FalseClass`のインスタンスであることは変わりませんが、両クラスとも`Boolean`の子クラスという形になっています。

### Numberクラス
最も大きな非互換の1つが`Integer`, `Float`の区別が存在せず、`Number`クラスのみになっていることです。
そのため、整数演算でも場合によっては浮動小数点誤差が発生することがあります。
それが問題になる場合は`BigDecimal`か、JavaScriptの`BigInt`を使います。

### 文字列の破壊的操作
もう一つの大きな非互換が、文字列の破壊的操作ができないことです。これはJavaScriptの文字列がimmutableであることから来る制限です。
`str << other`ができないので、`str += other`に置き換える必要が出てきます。

これは文字列だけの話なので、配列の`arr << other`は問題ありません。

### SymbolがString
RubyのSymbolは現在のOpalではサポートされておらず、`Symbol`クラスは`String`のエイリアスになっています。
シンボルリテラルを書いても文字列としてコンパイルされるので、`:foo == "foo"`がtrueになります。

## ruby.wasm vs Opal
WASMによってCRubyが直接ブラウザ上で動くようになった今、Opalを使うメリットは何でしょうか？

- JavaScriptとの親和性
  - 上にも書いたように、OpalはJavaScriptの配列をそのままRubyの配列として扱えるなど、直感的な操作が可能です
  - ruby.wasmでは、JavaScriptのオブジェクトをRuby側に持ってくると常に`JS::Object`なので、変換が必要になってきます

一方、ruby.wasmの利点もあります。

- CRubyとの互換性
  - ruby.wasmは基本的にCRubyをそのままWASMに移植したものなので、ほとんど同じ動きをすると言えます。
  - Opalは先述した非互換以外に、標準クラスの各メソッドも仕様を元にRuby+JavaScriptで再実装したものであるため、細かいエッジケースでCRubyと異なる挙動をする箇所が少なからずあります。
AWS Cloud Practitioner対策10 - 自動スケーリングとモニタリング

# はじめに
AWS Cloud Practitionerの対策として自分用に執筆しています。

:::note warn
この記事はAWS・執筆学びたての初心者が執筆しています。間違いがあるかもしれませんので、ぜひ各項目にある**参考文献**も確認してください。また、誤りがあればコメントで教えていただけると幸いです。
:::

## Elastic Load Balancing
受信したアプリケーションまたはネットワークのトラフィックを単一アベイラビリティーゾーンの服すターゲット、または複数のアベイラビリティーゾーンに分割する。
負荷分散。
 - Application Load Balancer
 - Network Load Balancer
 - Classic Load Balancer

### ロードバランサ―のモニタリング
 - Amazon CloudWatch メトリクス
     - システムが想定通りに動作しているのを確認し、メトリクスが許容範囲外になった場合にアクションを開始するアラームを作成します。
 - アクセスログ
     - ロードバランサーに送られるリクエストの詳細情報をキャプチャ―する
 - CloudTrailログ
     - APIのやりとりを誰が何をいつどこでしたかを記録する

### 異常なターゲットを検出した際のどうさ
そのターゲットのトラフィックのルーティングを停止
ターゲットが再び正常になったことを検出すると、トラフィックのルーティングを再開
正常なターゲットにトラフィックをルーティング

## CloudWatch
モニタリング兼可視化のサービス。
 - モニター対象
     - AWSリソース
     - AWSで実行されるアプリケーション
 - 収集と追跡
     - 標準メトリクス
     - カスタムメトリクス
 - アラーム
     - Amazon SNSトピックに通知を送信
     - Amazon EC2 Auto ScalingまたはAmazonEC2アクションを実行
 - イベント
     - AWS環境の変化に適合するルールを定義、これらのイベントを1つ以上のターゲット関数またはストリームにルーてぃんぐして処理する

## EC2 Auto Scaling
アプリケーションの可用性を維持するのに役立つ。フリーと管理機能を使用して、フリートの状態と可用性を維持する。

### Auto Scaling グループ
Auto Scalingの管理を目的に論理グループとして扱われるEC2インスタンスの集合。

### スケールアウトとスケールイン
スケールアウト：インスタンスの起動
スケールイン：インスタンスの削除

### 起動設定
 - AMI（Amazon マシンイメージ）
 - インスタンスタイプ
 - VPC とサブネット

## CloudTrail
APIアクティビティを記録する。

## Trusted Advisor
アーキテクチャのベストプラクティスの推奨
IAMユーザーの作成と削除の監査ログなど。

## SNS
Simple Notification Service。イベントベースのメッセージング。

## SQS
Simple Queue Service。キューをポーリングして取得sるう

### SNSとの違い
メッセージをプッシュ式で送信するのがSNS。

SQS : メッセージを受け取る
SNS : メッセージを送信する
AWS Cloud Practitioner対策9 - クラウドアーキテクチャ

# はじめに
AWS Cloud Practitionerの対策として自分用に執筆しています。

:::note warn
この記事はAWS・執筆学びたての初心者が執筆しています。間違いがあるかもしれませんので、ぜひ各項目にある**参考文献**も確認してください。また、誤りがあればコメントで教えていただけると幸いです。
:::

## AWS Well-architectedフレームワーク
クラウドアプリケーションとワークロード向けに実装可能な、高い安全性、性能、障害耐性、効率性を備えたインフラの構築をサポートするガイド。

 - 運用上の優秀性
     - 開発をサポートし、ワークロードを効率的に実行し、運用に関するインサイトを得て、ビジネス価値をもたらすためのサポートプロセスと手順を継続的に改善する
 - セキュリティ
     - データ、システム、資産を保護し、クラウドテクノロジーを活用してセキュリティを強化する
 - 信頼性
     - 意図した機能を期待通りに正しく一貫して実行するワークロード。ライフサイクル全体を通じて、ワークロードを運用及びテストする。
 - パフォーマンス効率
     - システムの要件を満たすためにコンピューティングリソースを効率的に使用し、要求の変化とテクノロジーの進化に対してその効率性を維持する
 - コスト最適化
     - 最低価格でビジネス価値を実現するシステムを実行できる
 - サステナビリティ
     - 環境に対する影響、特にエネルギーの消費と効率性に焦点を当てている。

## 信頼性と可用性
 - 信頼性
     - ユーザーが必要な時に機能を提供できるシステムの能力
 - 可用性
     - システムが正常に動作している時間、または要求されたタスクを正しく実行している時間の割合

## Trusted Advisor
AWSのベストプラクティスにしたがってリソースをプロビジョニングするためのガイダンスをリアルタイムで提供するオンラインツールです。

 - コスト最適化
     - リソースの使用状況を調べ、未使用のリソースやアイドル状態のリソースの排除やリザーバブドキャパシティーの活用など、コストを最適化するためのレコメンデーションを提示します。
 - パフォーマンス
     - サービスの制限を確認し、プロビジョニングスループットを活用し、使用率が高すぎるインスタンスをモニタリングして、サービスのパフォーマンスを向上させます。
 - セキュリティ
     - セキュリティーのギャップを埋め、さまざまなセキュリティ機能を有効にし、アクセス許可を確認することで、セキュリティを向上させます。
 - 耐障害性
     - オートスケーリング、ヘルスチェック、マルチAZ配置、バックアップ機能を活用し、可用性と冗長性を高めます。
 - サービスの制限
     - 使用料がサービス制限の80%を超えているサービスがないかを確認する。
ReactのshallowEqualとは？

## はじめに
Reactを学び始めてから2ヶ月経ったころ、ずっとおまじないと言われてきたReduxをついに教わりました。
ソースコードを見ていると、`useSelector`でReduxストアからデータを取得するとき、最後におまけのような見慣れない`shallowEqual`という単語が書かれていました。

先輩に聞いてみると、意味自体はシンプルなのですが前提知識がないと理解が難しい内容だったのでまとめてみます。
React/JavaSctipt/TypeScriptを学び始めたばかりの、プログラミング初心者の方の参考になれば嬉しいです。

## shallowEqualとは？
前提知識を整理する前に、先に結論を書きます。

:::note info
- `shallowEqual`はReactのReduxライブラリが用意している関数
- オブジェクトの浅い比較を行う
- コンポーネントの再レンダリングを防ぐために便利
:::

`shallowEqual`はReactのReduxライブラリが用意している関数（ユーティリティ関数）でオブジェクトの浅い比較を行います。この浅い比較について、JavaScriptにおけるオブジェクトの比較について知らないと理解が難しいので、先にこちらを解説します。

## JavaScriptにおけるオブジェクトの比較
JavaScriptにおいて、オブジェクトの比較は**参照**の比較をします。参照の比較とは、オブジェクトが保存されるメモリの番地（住所）を比較するということです。オブジェクトの中身を見て比較をしているわけではありません。
新しくオブジェクトを作るとメモリのどこかに保存されます。次にオブジェクトを作ると、また新しい場所に保存されます。そのオブジェクト同士の参照を比較すると、保存場所が異なるため、異なるオブジェクトと判定されます。

よって、JavaScriptでは[厳密等価演算子](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Strict_equality#%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%81%AE%E6%AF%94%E8%BC%83)を使って比較すると、以下のような中身が全く同じオブジェクトでも`false`と判定されます。
```React
const object1 = {
  name: "hello",
};

const object2 = {
  name: "hello",
};

console.log(object1 === object1); // true
console.log(object1 === object2); // false
```
全く同じ内容のオブジェクトなのに異なるオブジェクトと判定されるのは、直感と反しますよね。ポイントは、オブジェクトの中身まで確認していない、あくまで保存場所を比較している点です。

この厳密等価演算子を使ったオブジェクトの参照が同じかどうかだけを比較する方法を、**浅い比較（Shallow Comparison)** と呼びます。

## shallowEqualとは？
これを踏まえた上であらためて`shallowEqual`について考えます。
`shallowEqual`はオブジェクトの浅い比較を行います。前項で説明した通り、通常の厳密等価演算子で比較をする場合、オブジェクトの中身が同じでも別のオブジェクトとみなされますが、`shallowEqual`を使用することで、**オブジェクトの中身が一致する場合に`true`** を返します。
```react
const obj1 = { name: "hello" };
const obj2 = { name: "hello" };

console.log(obj1 === obj2); // false
console.log(shallowEqual(obj1, obj2)); // true
```
### shallowEqualのメリット
`shallowEqual`を使うことで、**無駄な再レンダリングを回避**できます。

通常、`useSelector`は Reduxストアに変更があると、それを検知してコンポーネントを再レンダリングします。ただし、全ての変更がそのコンポーネントにとって必要なわけではありません。
Reduxストアは大きな1つのオブジェクトで構成されており、reducerで新しいオブジェクトを作成すると、中身が同じであっても参照が異なります。これが、不要な再レンダリングを引き起こす原因です。
そこで`shallowEqual`を使用すると、中身が同じであれば同じオブジェクトとみなされるので無駄な再レンダリングを防ぐことができます。

### shallowEqualの使い方
`useSelector`の第2引数（比較関数）に`shallowEqual`を直接指定します。これにより、`selectorReturningObject`が返すオブジェクトの比較に`shallowEqual`が適用されます。
```react
import { shallowEqual, useSelector } from 'react-redux'

// 直接第2引数に渡す
const selectedData = useSelector(selectorReturningObject, shallowEqual)
```

### shallowEqualの注意点と使い所
- `shallowEqual`はオブジェクトの１段階目しか見ないので、ネストしたオブジェクトには使えません。階層が深いものに使用したい場合は、自分で作りましょう。
- 基本的に`useSelector`でオブジェクトを参照するときは無駄な再レンダリングを防ぐため、`shallowEqual`で比較しましょう。
- プリミティブ型（数値、文字列、真偽値など）の場合は、通常の厳密等価演算子で比較されるため、`shallowEqual`で比較する必要はありません。
- 配列に対しては効果がないので注意。

## 終わりに
Reduxを使用する際に、`useSelector`と`shallowEqual`を組み合わせることで、不要なコンポーネントの再レンダリングを防ぐことができ、パフォーマンスの向上につながります。
`useSelector`でデータを取得するときは、パフォーマンスも意識して書けるようにしましょう！
HTMLでtailwindcssのクラス名を自動整形する方法

## はじめに
備忘録です
React等でtailwindcssを使用し、class名を自動で並び替える方法は紹介されています
本記事ではHTMLでtailwindcssを使用し、class名を自動で並び替える方法を紹介します

## 追加前提
これらを使います
npmの利用方法がわかっていることが前提です
tailwindcssのセットアップについては解説しないので[公式](https://tailwindcss.com/docs/installation)をみながら設定してください その際CDNではなくCLIを使用して設定してください

1 VSCode
2 eslint
3 node(npm)

## 設定手順 

1 package.jsonを初期化する(tailwindcssの環境を構築しているはずなので不要)
```shell
$ mksir sample # ディレクトリ作成
$ cd sample # 作成したディレクトリに移動
$ npm init -y # package.jsonの初期化
```

2 必要なpackageをinstall
```shell
$ npm i -D eslint eslint-plugin-tailwindcss @angular-eslint/template-parser
```

3 EsLintの初期化
```
$ npm init @eslint/config
```
インタラクティブに色々聞かれるので下記の順で選択
```
1 To check syntax and find problems
2 JavaScript modules (import/export)
3 None of these
4 No
5 Browser
6 JavaScript
7 Yes
```

4 プロジェクトのルートに作成された.eslintrc.jsの内容を書き換える
下記のように書き換えてください
```js
module.exports = {
  extends: ["plugin:tailwindcss/recommended"],
  overrides: [
    {
      env: {
        node: true,
      },
      files: [".eslintrc.{js,cjs}"],
      parserOptions: {
        sourceType: "script",
      },
    },
    {
      files: ["*.html", "*.blade.php"],
      parser: "@angular-eslint/template-parser",
    },
  ],
};
```

5 ファイル保存時にeslintのformatがされるように設定する
```
1 VSCode上でcommand + shift + pでコマンドパレットを開く
2 「>settings]と検索し 「基本設定: ユーザー設定を開く(JSON)」
3 下記の設定を加える
```

```json
{
  "editor.formatOnSave": true,
  "files.autoSave": "onFocusChange",
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": true 
  }
}
```

6 一応reload
```
1 VSCode上でcommand + shift + pでコマンドパレットを開く
2 >reloadで検索しウィンドウをreload
```

ここまでで設定完了です
index.htmlに下記を入力すると波線が引かれます
```
<p class="p-6 mt-6">あいうえお</p>
```

保存すると下記のように並び順が整理されます
```
<p class="mt-6 p-6">あいうえお</p>
```

## 参考
https://github.com/francoismassart/eslint-plugin-tailwindcss