OmniAICreator/Qiita-1.07M huggingface dataset:
【Rails】flashメッセージ

## flashメッセージとは
アクション実行後に簡単なメッセージを表示させるRailsの機能。

## コントローラーに記述
### flashuの書き方
```
flash[:キー名]="表示したいメッセージ"
```
これはコントローラに記述する。
キー名は好きな名前をつけることができる。
```
# 例）
flash[:notice] = "お知らせです"
flash[:alert] = "警告です"
```
フラッシュメッセージを表示させる箇所にはviewファイルに以下のように記載する。
```
<%= flash[:キー名] %>

```
### noticeとalertオプション
キーには好きな名前をつけることができるが、`notice`と`alert`は、`<%= flash[:キー名] %>`と書く必要がない。viewファイルは以下のようにflashを省略してかける。
```
<%= notice %>
<%= alert %>
```
他のキー名でもまとめて書くことができるその場合は下記のようになる。
```
redirect_to ("パス"), flash{キー名: '表示するメッセージ' }

```
### redirect_toでnoticeとalertを使う時はまとめて書くことができる
```
redirect_to ("パス"), notice: '表示するメッセージ' 
```
```
class TasksController < ApplicationController
  def index
    @tasks = Task.order(created_at: :desc) # order(created_at: :desc)は作成順に降順に並び替え、タスクを取得
    @tasks = Task.new # 新しいTaskオブジェクトを作成
  end

  def create
    @task =Task.new(task_params) # フォームの入力値を使って新しいTaskオブジェクトを作成
    if @task.save # @taskをデータベースに保存したとき
      redirect_to root_path, notice: "タスクを作成しました"
      # noticeはflashメッセージの成功通知　root_pathはトップページへリダイレクト
    
    end
  
end

```
GitHub個人アクセストークン発行手順

# はじめに

本記事はGitHubにあるリポジトリをクローンする際などに必要となる個人アクセストークンの発行手順をまとめています。

# 手順

## ①GitHubにログイン後、Settingsを押下

![スクリーンショット 2025-02-27 14.43.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2730977/d9d598d6-398e-4736-b2ab-ed852824dd3b.png)

## ②Developer settingsを押下

![スクリーンショット 2025-02-27 14.45.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2730977/4f3e75d7-fc81-4598-a107-d4df5405f8c4.png)

## ③Personal access tokesのプルダウンからTokens（classic）を押下

![スクリーンショット 2025-02-27 14.46.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2730977/b469af42-dd3a-4ed7-af4b-f4270a777f67.png)

## ④Generate new tokenのプルダウンからGenerate new token（classic）を押下

![スクリーンショット 2025-02-27 14.48.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2730977/8d1a8b46-ce7e-461b-a46c-ba832d1bec0e.png)

## ⑤必要事項入力、選択しGenerate tokenを押下

- note: トークンを発行する経緯を記載
- Select scopes: 必要な権限を付与
    - 基本的にはrepoのみチェックを入れる
    - 詳細は[公式ドキュメント](https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/scopes-for-oauth-apps)参照

# 補足事項

- 発行したトークンは一度しか表示されません
    - トークンのコピーができていなかった場合、削除し再度個人アクセストークン発行を行う
- トークンの使用が終わったら削除推奨
- 不要なスコープは付けず、必要最低限の権限を付与するのがセキュリティ上のベストプラクティス
Ridgepoleを使ってPostgreSQLのFILLFACTORを設定した

# はじめに

Gakken LEAPで働いていますkoboriです。私が担当をしているプロダクトでは、Rails標準のマイグレーションではなく、Ridgepoleというgemを採用しています。私はこのプロダクトの担当に就いた際に初めてRidgepoleを使ったのですが、スキーマ定義を宣言的に記述することができ、非常に気に入っております。

日々の運用の中で、あるタイミングから時折スロークエリログが検知されるようになりました。対象は更新系のクエリで、書き込み性能の改善をする必要が出てきました。対象のWeb APIではINSERTよりUPDATE文が圧倒的に多いこと、RDSのメトリクスの数値から、HOT機能を活用するためにFILLFACTORの設定を行うことにしました。

そんな背景から、小ネタではありますが、今回はRidgepoleを使ってPostgreSQLのFILLFACTORを設定した時の話を書いていきます。

Ridgepoleについては、GithubのREADMEをご参照ください。

https://github.com/ridgepole/ridgepole

また、HOT及び、FILLFACTORについては、Let's Postgresの下記のシリーズを参考にしました。

https://lets.postgresql.jp/documents/tutorial/hot3

# FILLFACTORを設定する

まず、下記のクエリで現状の設定を確認し、reloptionsの値からFILLFACTORが設定されていないことを確認します。

```sql
SELECT relname, reloptions FROM pg_class WHERE relname = 'table_name';
```

次に、PostgreSQLのドキュメントを参照し、FILLFACTORの設定方法を確認します。
FILLFACTORの設定方法は、[CREATE TABLE](https://www.postgresql.jp/document/16/html/sql-createtable.html) または、[ALTER TABLE](https://www.postgresql.jp/docs/16/sql-altertable.html) で設定することができます。今回は作成済みのテーブルに設定を追加するため、ALTER TABLEを使用することになります。

ここからが本稿の本題です。Ridgepoleを使ってテーブルオプションを設定していきます。

まずは、淡い期待を抱きつつ `#create_table`の引数にFILLFACTORの設定を書いてみましたが、設定は反映されませんでした。
調べた限り、そもそもActiveRecordにテーブルオプションを設定するインターフェースが存在しないようで、別の方法を探すことにしました。

```ruby
create_table :table_name, fillfactor: 90 do |t|
  # ...
end
```

過去に似たような事例がないかとRidgepoleのissuesを漁っている中で、`#execute` を使うことで今回のユースケースをカバーできるのではないか、ということが分かりました。余談ですが、READMEにも`#execute` に関する記載があり、見落としていたことが後になって分かりました。READMEはしっかり読まねばと猛省しました。

それでは `#execute` を使って設定をしていきます。

```ruby
execute 'ALTER TABLE table_name SET (FILLFACTOR = 90)' do |c|
  # 既に設定済みの場合は実行をスキップする
  c.raw_connection.query(
    "SELECT relname, reloptions FROM pg_class WHERE relname = 'table_name'AND 'fillfactor=90' = ANY(reloptions)"
  ).each.size.zero?
end
```

これにより、テーブルオプションの設定が反映され、2回目以降の適用時にはスキップされるようになりました。

FILLFACTORに設定する値は、テーブルの更新頻度やデータ量を見て決めていく必要があります。設定する際はご注意ください。

また、今回紹介した書き方だと、 `#execute` を削除して適用しても、FILLFACTORの設定は削除されません。FILLFACTORの設定をリセットする場合は、下記のクエリを実行した上で、`#execute` の記述を削除する必要があります。明示的に値を100に設定するのも良いかもしれません。

```sql
ALTER TABLE table_name RESET (FILLFACTOR);
```

FILLFACTORの設定を運用中のテーブルに適用する際は、過去のデータに対してもFILLFACTORの設定を適用するか否かの判断をする必要があります。
過去のデータに対しても設定を適用する場合は、VACUUM FULLを実行することで設定を適用することができます。しかし、テーブルに対する排他的ロックが必要になる、余分なディスク容量が必要になる、などの注意点がありますので、実行をする際は慎重に検討をする必要があります。詳細は下記をご参照ください。

https://www.postgresql.jp/docs/16/sql-vacuum.html

過去のデータに対して設定を適用する必要がない場合は、新たに作成されるページから設定が適用されるようです。

# おわりに

経過観察中ではありますが、本対応を実施して以降、該当のWeb APIに起因するスロークエリログは検出されていません。事象が改善されない場合は、WAL書き込みの非同期化、パラメータのチューニングなど、次の対応を検討していく予定です。

# エンジニア募集

Gakken LEAP では教育をアップデートしていきたいエンジニアを絶賛大募集しています。
ぜひお気軽にカジュアル面談へお越しください。

https://gakken-leap.co.jp/recruit/
Properties validation failed for resourceと怒られたら

CloudFormationのデプロイしてたらこんな感じのエラーが出ました。

> Properties validation failed for resource AWSResourceMethod with message: #/AuthorizationType: #: only 1 subschema matches out of 2 #/AuthorizationType: failed validation constraint for keyword [enum]

使用していたCloudFormationテンプレートはこちらです。

```yaml
AWSTemplateFormatVersion: 2010-09-09
Description: "API Gateway"
Parameters:
  FunctionName:
    Type: String
Resources:
  Api:
    Type: "AWS::ApiGateway::RestApi"
    Properties:
      Name: "api"
  ResourceAWS:
    Type: "AWS::ApiGateway::Resource"
    Properties:
      RestApiId: !Ref Api
      ParentId: !GetAtt Api.RootResourceId  
      PathPart: aws
  LambdaPermission:
    Type: "AWS::Lambda::Permission"
    Properties:
      FunctionName: !Sub "${FunctionName}"
      Action: "lambda:InvokeFunction"
      Principal: "apigateway.amazonaws.com"
  AWSResourceMethod:
    Type: "AWS::ApiGateway::Method"
    Properties:
      RestApiId: !Ref Api
      ResourceId: !Ref ResourceAWS
      AuthorizationType: NONE
      HttpMethod: GET
      Integration:
        Type: AWS
        IntegrationHttpMethod: "POST"
        Uri: !Sub "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:${FunctionName}/invocations"
    DependsOn: "LambdaPermission"
```
## 解決する方法

なんてことはないNoneをNONEにするだけ


```diff
AWSTemplateFormatVersion: 2010-09-09
Description: "API Gateway"
Parameters:
  FunctionName:
    Type: String
Resources:
  Api:
    Type: "AWS::ApiGateway::RestApi"
    Properties:
      Name: "api"
  ResourceAWS:
    Type: "AWS::ApiGateway::Resource"
    Properties:
      RestApiId: !Ref Api
      ParentId: !GetAtt Api.RootResourceId  
      PathPart: aws
  LambdaPermission:
    Type: "AWS::Lambda::Permission"
    Properties:
      FunctionName: !Sub "${FunctionName}"
      Action: "lambda:InvokeFunction"
      Principal: "apigateway.amazonaws.com"
  AWSResourceMethod:
    Type: "AWS::ApiGateway::Method"
    Properties:
      RestApiId: !Ref Api
      ResourceId: !Ref ResourceAWS
-      AuthorizationType: NONE
+      AuthorizationType: None
      HttpMethod: GET
      Integration:
        Type: AWS
        IntegrationHttpMethod: "POST"
        Uri: !Sub "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:${FunctionName}/invocations"
    DependsOn: "LambdaPermission"
```

参考にしたサイトではNONEだったのでそれで動いていた時期があったのかもしれません。

## 感想

それにしてもエラーメッセージが不親切すぎる。NONEは受け入れられませんと言ってくれればいいのに
基本情報技術者試験 Writeup

先日、基本情報技術者試験を受けてきたので、その記録です。Writeupとありますが具体的な問題への言及はありません。(規約で禁止されています)

:::note
私はある程度の開発経験者です。非プログラマでこれから基本情報技術者試験に向けて勉強しようとする方には参考にならないかもしれません。
:::

## 試験概要
IPAのエンジニアの登竜門的な試験。科目AとBに分かれていて、ともに60%以上の得点で合格。
[試験公式ページ](https://www.ipa.go.jp/shiken/kubun/fe.html)

## 勉強について

### 勉強時間
10時間くらい

### やったこと
最初に過去問を解いた段階で試験Aは60%、試験Bは90%くらいあり、知識(特に語句)が不足していたので、A問題の過去問をひたすら解いて覚えました。

使用したサイト。大変助かりました。
https://www.fe-siken.com/s/fekakomon.php

こちらで1500問くらい問題を解きました。

:::note
過去問を見る際は、試験形式は度々変更されており、今後も変更され得ることに留意してください。
:::

## 出題内容について

### 科目A
主に知識が問われます。
過去問からの出題傾向はあったのですが、恐らく過去問になかった内容の問題も散見されました。過去問はせいぜい過去10年程度で十分だと思います。

### 科目B
アルゴリズムが8割、セキュリティが2割です。

アルゴリズムと言いましたが、スタックや二分探索木が分かれば十分で、AtCoder茶色もあれば余裕だと思います。

疑似言語なので、特定の言語を覚えていく必要はありません。

配列のインデックスは0スタートと1スタートが混在しているのでケアレスミスに注意しましょう。

セキュリティに関しては、~~ぶっちゃけ配点が少なそうなので、アルゴリズムが取れるならやらなくても問題ないと思います~~
科目A程度の知識と常識があれば普通に解けると思います。

総じて形式の確認さえできれば、特段の対策は必要ないでしょう。

## 試験時Tips
- 私が利用したテストセンターでは、準備出来次第テストを開始することができ、予定よりも早く開始することができました。
- 試験時間は90+10+100分となっていますが、それぞれ前倒しすることが出来ます。ただし、A問題や休憩時間を削ったからといって後の時間が延びる訳ではないです。
- よって自信がある方は後に予定があっても1時間程度で受けられます。
- 「後で見直す」ボタンがあってちょっと便利でした。
- __電卓は使えません。__

## さいごに
私は時間的余裕がなかったので暗記で乗りきりましたが、出題範囲の知識は知っていて損はないと思うので、合否に関わらず後のためにも体系的に学習するのは良い選択だと思います。エンジニアにとってそこまでハードルは高くないので、学生や初心者にもおすすめです。
sysモジュールの使い方

# Pythonの`sys`モジュールとは？

## **📝 はじめに**

Pythonの`sys`モジュールは、**システム（環境）に関する情報を取得・操作するための標準ライブラリ** です。
特に **「コマンドライン(ターミナルで命令するコマンドのこと)引数の処理」** でよく使われます。

この記事では、
- `sys`モジュールの基本的な使い方
- `sys.argv` を使ったコマンドライン引数の処理
- `sys.exit()` を使ったプログラムの終了方法

について解説します。

---

## **`sys`モジュールとは？**

Pythonには **「標準ライブラリ」** という組み込みの機能があり、その一つが `sys` モジュールです。
今回私がプログラミングの制作課題時に使用した`sys`モジュールの使い方について解説します。

### **✅ `sys`モジュールの基本的な使い方**
まずは `sys` モジュールを使うには、Pythonに組み込み済みなので **`import sys`** を書くだけで使えます。

```python
import sys  # sysモジュールをインポート
```

この `sys` モジュールには、
- **コマンドライン引数を取得する (`sys.argv`)**
- **プログラムを終了する (`sys.exit()`)**
- **Pythonのバージョン情報を取得する (`sys.version`)**
- **Pythonの実行環境のパス情報を取得する (`sys.path`)**

といった便利な機能が含まれています。

---

## **`sys.argv` とは？（コマンドライン引数の取得）**

### **💡 `sys.argv` の基本**
`sys.argv` を使うと、**Pythonスクリプトを実行するときに渡されたコマンドライン引数** を取得できます。

```python
import sys

print(sys.argv)  # コマンドライン引数をリストとして出力
```

#### **✅ 実行例**
```sh
$ python script.py hello world
```

#### **🔹 出力結果**
```python
['script.py', 'hello', 'world']
```

- `sys.argv[0]` → スクリプト名 (`script.py`)
- `sys.argv[1]` → 最初の引数 (`hello`)
- `sys.argv[2]` → 次の引数 (`world`)

📌 **ポイント**
- `sys.argv` は **リスト型** なので、`sys.argv[1]` のようにアクセスできます。
- **引数がない場合は `sys.argv` の長さ(len)が `1` になる**（スクリプト名だけ）。

---




## **`sys.exit()` でプログラムを終了する**

`sys.exit()` を使うと、**プログラムを途中で終了** できます。

```python
import sys

print("プログラムを開始します")
sys.exit(0)  # 正常終了（0を指定）
print("この行は実行されません")
```

 **`sys.exit(1)` のように `1` を指定するとエラー終了** になります。

- **`sys.exit(0)`** → 正常終了（問題なし）
- **`sys.exit(1)`** → 異常終了（エラー）


```python
print(f"{month} is neither a month number (1..12) nor a name")
sys.exit(1)  # 異常終了
```
エラーが発生したときに **`sys.exit(1)` を使うと、明確にプログラムを終了できる！**

---

## **✅ まとめ**

🎯 **`sys`モジュールのポイント**
- `sys.argv` → **コマンドライン引数を取得** する
- `sys.exit()` → **プログラムを強制終了** する
- `sys.version` → **Pythonのバージョン情報を取得** する
- `sys.path` → **Pythonの検索パスを取得** する

- `sys.exit(1)` で **間違った入力をエラー終了** させた
Dockerを使用してKibanaの環境構築をする手順

# はじめに
KibanaはElasticsearchのデータを視覚化するためのダッシュボードのパッケージです。
本記事では、Dockerを使用してKibana環境を構築する方法を解説します。

# 前提条件
- Dockerがインストールされていること
- Docker Composeがインストールされていること
- 基本的なコマンドライン操作の知識

# 環境構築の手順
1.Kibana用のプロジェクトのディレクトリを作成します。
```bash
mkdir kibana-sample
cd kibana-sample
```
2.docker-compose.ymlファイルを作成
```yml:docker-compose.yml
version: '3'
services:
  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:8.17.2
    container_name: es01
    environment:
      - discovery.type=single-node
      - xpack.security.enabled=false  # 無料機能のみ利用するため、セキュリティ機能を無効化
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
    ulimits:
      memlock:
        soft: -1
        hard: -1
    volumes:
      - elasticsearch-data:/usr/share/elasticsearch/data
    ports:
      - 9200:9200
    networks:
      - elastic

  kibana:
    image: docker.elastic.co/kibana/kibana:8.17.2
    container_name: kib01
    ports:
      - 5601:5601
    environment:
      - ELASTICSEARCH_HOSTS=http://elasticsearch:9200
      - XPACK_SECURITY_ENABLED=false  # 無料機能のみ利用するため、セキュリティ機能を無効化
    depends_on:
      - elasticsearch
    networks:
      - elastic

volumes:
  elasticsearch-data:
    driver: local

networks:
  elastic:
    driver: bridge
```
※詳しく調べていないため自信がありませんが、有料版（サブスクリプションプラン）ではセキュリティ機能が付いているようです。誤っている可能性があるため、ご自身でご確認お願いします。

このdocker-compose設定では
- Elasticsearchを単一ノードモードで実行
- セキュリティ機能を無効化（開発環境向け）
- Kibanaをポート5601で公開
- データの永続化のためのボリュームを設定

3.docker-composeの起動
```bash
docker-compose up -d
```
`docker-compose ps`等でElasticsearchとKibanaのコンテナが起動していることを確認します。

4.Kibanaへアクセス
下記URLをブラウザで開きます。
```
http://localhost:5601
```

# データ投入
curlコマンドを使用して、サンプルデータをElasticsearchに登録し、Kibanaで閲覧できるようにします。

```bash
# インデックスの作成
curl -X PUT "localhost:9200/my_index"

# データの投入
curl -X POST "localhost:9200/my_index/_doc" -H "Content-Type: application/json" -d'
{
  "name": "Example Data",
  "timestamp": "2024-02-27T10:00:00",
  "value": 42
}'
```

# おわりに
Dockerを使用することで、Kibanaの環境構築を迅速かつ再現性の高い方法で行うことができます。

本番環境での使用を検討している場合は、適切なセキュリティ設定を行い、リソース要件についても考慮することをお忘れなく。

# 参考文献
https://www.elastic.co/guide/en/kibana/current/docker.html
【C#】.NET9 では `void` インスタンスを作れなくなっていた話

# はじめに

参考：<https://neue.cc/2011/12/13_358.html>

.NET Framework 時代は ↓ で `void` のインスタンスを得られますが、.NET9 ではエラーになるようです。

```cs
using System.Runtime.Serialization;

var v = FormatterServices.GetUninitializedObject(typeof(void));// エラー！
Assert.Equal(typeof(void), v.GetType());
```

`FormatterServices` は古いクラスにマークされていたり .NET Framework -> .NET の流れで変更があったのでしょうか。コンパイルはエラーにならず、実行時エラーになるためランタイム側であれこれした結果かもしれません。

やはり `void` インスタンスを取得できるのはまずかったか・・・

## サンプルコード

<details>
<summary>テストコード</summary>

```cs
using Xunit;

public class __VoidInstanceTest
{
    [Fact]
    void CreateVoidError()
    {
        // 少なくとも .net9 では例外になる
        Assert.Throws<ArgumentException>(() =>
            System.Runtime.CompilerServices.RuntimeHelpers.GetUninitializedObject(typeof(void)));
    }

    [Fact]
    void CreateRefStructError()
    {
        Assert.Throws<NotSupportedException>(() =>
            System.Runtime.CompilerServices.RuntimeHelpers.GetUninitializedObject(typeof(Span<int>)));
    }

    [Fact]
    void SkipConstructor()
    {
        var person = (Person?)System.Runtime.CompilerServices.RuntimeHelpers.GetUninitializedObject(typeof(Person));

        Assert.NotNull(person);
        Assert.Equal(null!, person.Name);
        Assert.Equal(0, person.Age);
    }
}

file class Person
{
    internal string Name { get; } = "Default Name";
    internal int Age { get; } = 20;

    public Person() { }

    public Person(string name, int age)
    {
        this.Name = name;
        this.Age = age;
    }
}
```

</details>

## `System.Runtime.CompilerServices.RuntimeHelpers`

古いクラスマークされたクラスの[置き換えクラス](https://learn.microsoft.com/ja-jp/dotnet/api/system.runtime.compilerservices.runtimehelpers?view=net-9.0)が用意されているようです。

そもそもの古いクラスはオブジェクトのシリアル化関係の用途のようです。[`GetUninitializedObject()`](https://learn.microsoft.com/ja-jp/dotnet/api/system.runtime.compilerservices.runtimehelpers.getuninitializedobject?view=net-9.0#system-runtime-compilerservices-runtimehelpers-getuninitializedobject(system-type)) は**コンストラクタを呼ばずにインスタンスを生成する**という結構ハック的な処理をしています。

```cs
file class Person
{
    internal string Name { get; } = "Default Name";
    internal int Age { get; } = 20;

    public Person() { }

    public Person(string name, int age)
    {
        this.Name = name;
        this.Age = age;
    }
}

var person = (Person?)System.Runtime.CompilerServices.
    RuntimeHelpers.GetUninitializedObject(typeof(Person));

Assert.NotNull(person);
Assert.Equal(null!, person.Name);
Assert.Equal(0, person.Age);
```

↑ の `Person` クラスは引数ありと引数なしの２種類のコンストラクタがありますが、`GetUninitializedObject()` によって得られたインスタンスはどちらのコンストラクタも経由していません。全てのフィールドが 0 または `null` で埋められます。

## おわりに

いろいろ試してみましたが、.NET9 で `void` のインスタンスを取得する方法は見つけられませんでした。一方で面白いクラス（`RuntimeHelpers`）を発見できたので、パフォーマンス関係に使えないか試してみようと思います。
Verticaテクノロジー概要

## Verticaテクノロジー概要

このコースを終了することで、以下を理解することができます:

- Vertica プラットフォームの構成要素

## The Vertica Analytics Platform

Verticaは、最も要求の厳しいビッグデータ分析イニシアチブに対応するために、最初のコード行から構築された最も先進的な統合分析プラットフォームです。Verticaは、妥協のない速度、制限のないスケール、および幅広い消費モデルを提供します。

### Verticaの構成要素

Vertica アプリケーションは、以下の4つの主要な目標に基づいて構築されています:

1. **パフォーマンス**
    - 列指向のデータ構造（プロジェクション）にデータを格納することで、不要なデータのスキャンを省略し、高速なクエリを実現。
    - エンコードアルゴリズムにより、ディスクアクセス時間を短縮。

2. **スケーラビリティ**
    - 分散型の超並列アーキテクチャを採用し、ノードを追加することでスケールアップ可能。

3. **信頼性**
    - クラスタ内のノードに冗長データを分散し、障害時のデータ損失を防止。
    - Webベースの管理コンソールを提供し、監視と管理を簡素化。

4. **展開の自由度**
    - オンプレミス、主要なパブリッククラウド、またはハイブリッド環境に柔軟に展開可能。
    - 既存のETLやBIツールと統合可能。

## パフォーマンス上の利点

### 列ストアデータベース

- 従来の行ストアデータベースでは、クエリの実行時にすべてのカラムをスキャンする必要がある。
- Verticaでは、データを列ごとに格納するため、クエリに関連する列のみを読み取ることが可能。
- これにより、ディスクI/Oを削減し、クエリ応答時間を大幅に短縮。

### データエンコーディング

- データはエンコードされた状態で格納され、必要なときにのみデコードされる。
- これにより、CPUの処理負担を軽減し、ストレージの節約を実現。
- 一般的なエンコード方式:
    - **Block_dictエンコード**: ソートされていない高カーディナリティの文字データ向け。
    - **Deltarange_compエンコーディング**: ソート済みデータの値の差分を格納。
    - **実行長エンコード (RLE)**: 連続する同じ値を圧縮。

## 信頼性と拡張性

### 超並列処理（MPP）

- VerticaはLinuxベースの標準的なハードウェア上で動作。
- クラスタ内のすべてのノードが同等なピアノードとして機能。
- 追加のノードをシームレスに統合可能。

### データ冗長性（K-Safety）

- データのコピーを複数のノードに保持し、障害時もデータのアクセシビリティを確保。
- ノード障害時も継続的なクエリ実行が可能。

## 使いやすさ

### 自動データベース設計

- データベースデザイナ（DBD）が、最適なデータストレージ構造を提案。
- 代表的なデータセットとクエリセットを指定することで、適切なデータレイアウトを生成。

### マネージメントコンソール（MC）

- ブラウザベースの管理ツール。
- データベースの作成、構成、監視を統合的に実施。
- システムアラートの設定やデータベース診断機能も搭載。

## 展開の自由度

### 展開オプション

- **エンタープライズモード**
    - 独自のハードウェア環境でインストール可能。
    - クラウド環境（Amazon, Azure, Google）にも対応。

- **Eonモード**
    - コンピューティングとストレージを分離。
    - 共有ストレージを利用し、スケールしやすい構成が可能。

## 高度な分析

### 組み込み分析関数

- SQL分析関数を拡張し、予測分析や機械学習処理を容易に実行可能。
- 地理空間データ分析、時系列データ処理、異常検出機能を提供。

### 機械学習

- データ準備からモデル構築、評価、デプロイまでのワークフローをサポート。
- データベース内で直接機械学習モデルを実行可能。
rex0220 印刷屋プラグイン 共通CSSを追加

[rex0220 印刷屋プラグイン 見積書のCSSカスタマイズ２](https://qiita.com/rex0220/items/c13d92d9ef89356f6dd3) で作成した見積書のCSSを共通CSSに追加して、請求書に適用してみます。

# 概要

新しく作ったCSSで見積書と請求書のデザイン共通化したい場合など、共通CSSに登録して各帳票に適用します。

![2025-02-27_12h59_22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/4352ac0b-0a58-474b-a066-e1daff76407b.png)

請求書のデザイン比較

![2025-02-27_13h00_18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/4220b99e-6c12-44e5-a5eb-8e1cfb75a820.png)

見積書・請求書のデザイン比較

![2025-02-27_13h01_41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/f81b02ed-b0a1-4d5b-9fa2-064a2cf3e575.png)

# 共通CSS設定

追加ボタンをクリックして、CSSを追加します。

![2025-02-27_13h07_00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/22b1ba7d-1069-4908-9c3d-b309975fe34b.png)

# 請求書のカスタマイズ

既存の「請求書」を複写して、設定と確認を行ってみます。

![2025-02-27_13h09_04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/4d67aea1-7d92-48cc-8feb-0118ed9f3ba3.png)

クラス名を pcraft-inv から pcraft-est　にすべて変更します。

![2025-02-27_13h10_50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/b27e3f1c-9e36-40aa-b84a-7839eb9cc94b.png)

```js:
PAGE_HTML(
  TAG("div", ATTR("class", "pcraft-est-header"), "請求書"),

  TAG("div", ATTR("class", "pcraft-est-info"),

    // header left
    TAG("div", ATTR("class", "pcraft-est-left"),
      TAG("p", 宛名 & " 御中"), VTAG("br"),
      TAG("p", ATTR("class", "pcraft-est-estimate-amount"), "請求金額:", TAG("span", FVAL(合計金額))),
    ),

    // header right
    TAG("div", ATTR("class", "pcraft-est-right"),
      TAG("p", "請求番号: " & 請求番号),
      TAG("p", "発行日: " & DATE_FORMAT(請求日, "YYYY年M月D日")),
      TAG("p", "登録番号： T0000000000000"),
      TAG("p", "株式会社サンプルサンプルサンプル"),
      TAG("p", "開発 太郎"),
      VTAG("img", ATTR("class", "pcraft-est-seal", "width", "250", "height", "120", "src", "#{&f(20250224144344126E43A90D994A32B0C6FE39295544E7236)}"))
    )
  ),

  // table
  TABLE_HTML(見積明細,
    OPT("pref", "pcraft-est-item-"),
    ARRAY("#", ROWNO(見積明細) + 1),
    商品名, 数量, 単価, 金額
  ),

  // summary
  TAG("div", ATTR("class", "pcraft-est-summary-section"),
    // 備考
    TAG("div", ATTR("class", "pcraft-est-remarks-section"),
      TAG("strong", "備考:"), VTAG("br"),
      TAG("span", "ご注文ありがとうございます")
    ),
    // 合計
    TAG("div", ATTR("class", "pcraft-est-total-section"),
      TAG("table",
        TAG("tbody",
          TAG("tr", TAG("th", "小計"), TAG("td", FVAL(小計金額))),
          TAG("tr", TAG("th", "消費税 (10%)"), TAG("td", FVAL(消費税))),
          TAG("tr", TAG("th", TAG("strong", "合計")), TAG("td", TAG("strong", FVAL(合計金額))))
        )
      )
    )
  ),

  // footer
  TAG("div", ATTR("class", "pcraft-est-footer"),
    TAG("p", "お振込先: 〇〇〇〇銀行 〇〇支店（普）0000000 〇〇〇〇〇〇"),
    TAG("p", "＊お振込み手数料はご負担をお願い致します")  
  )
)
```
Windowsユーザー必見：効率的なHTTPテストツール5選

## はじめに

日常の開発作業で、便利なHTTPテストツールがあると、作業効率が大きく向上します。自分に合ったHTTPテストツールを選ぶことは、大切な道具を選ぶのと同じくらい重要です。今回は、特にWindowsで使いやすい5つのHTTPテストツールを紹介します。どれか一つは、あなたのニーズに合うでしょう。これらの効率アップツールを一緒に見てみましょう！

## [Apidog](http://www.apidog.com/jp/?utm_source=opr&utm_medium=a2qiita2)
![apidog-client-JP-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3968588/c3ea3c4e-8b69-456c-9bcc-c9787d128bac.png)

[Apidog](http://www.apidog.com/jp/?utm_source=opr&utm_medium=a2qiita2)は、HTTP、HTTP2、WebSocketなどのプロトコルをサポートしており、IDEAプラグインを統合しています。APIドキュメント、デバッグ、設計、テスト、モック、そして自動化テストを統合したAPI協力プラットフォームです。インターフェースはとても親しみやすく、日本語化されているため、簡単に使えます。開発中に、ドキュメント管理、デバッグ、モックデータの自動生成が可能で、フロントエンドとバックエンドの分離開発に非常に便利です。

![apidog-データをインポート-10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3968588/f5a608e9-9018-4103-a7d2-e2d1a87ae1f2.png)

特に役立つ機能は、テストケースを整理する機能で、レポートをエクスポートすることも可能です。SwaggerやPostmanの形式をインポートすることも簡単です。


## [Postman](https://www.postman.com)
![postman-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3968588/42d80086-5be4-42be-a769-4f22e8c9f381.png)

[Postman](https://www.postman.com)は最も人気のあるHTTPテストツールの一つで、何百万人もの開発者が使用しています。直観的なインターフェースで、初心者でも簡単に使えます。あらゆる種類のHTTPリクエストに対応しており、過去の履歴が保存されるので、必要なときにすぐにアクセスできます。

チームメンバーとコレクションや変数を共有でき、重複する設定は不要です。JavaScriptによる自動化テストスクリプトをサポートしており、便利なコードスニペットも提供されています。

## [Insomnia](https://insomnia.rest)
![Insomnia-3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3968588/7b583f10-b977-4f61-8b28-178f5180477b.png)

[Insomnia](https://insomnia.rest)はシンプルでエレガントなAPIテストツールです。インターフェースはさわやかで、GraphQLをサポートしています。テーマの切り替えが可能で、夜間作業にも適しています。

リクエストはフォルダー形式で管理でき、環境変数の設定も強力です。SwaggerやOpenAPIのドキュメントをインポート、エクスポートでき、共同作業がしやすいです。

## [Hoppscotch](https://hoppscotch.io)
![Hoppscotch-4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3968588/edf87a2a-3efe-41c3-a6b2-e1c6093a9f44.png)

[Hoppscotch](https://hoppscotch.io)は軽量のAPIテストツールで、完全に無料でオープンソースです。インターフェースはシンプルですが、機能は強力です。PWAをサポートしており、インストールして通常のアプリのように使えます。ショートカットキーのサポートも充実しています。

リアルタイムコラボレーション機能が優れており、WebSocketテストも便利です。テスト結果からドキュメントを生成する機能もあります。

## [SoapUI](https://www.soapui.org)
![SoapUI-5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3968588/761ca51e-039f-4e15-9245-840a0c275c34.png)

[SoapUI](https://www.soapui.org)は古くからあるAPIテストツールで、企業向けのテストに適しています。SOAPだけでなくREST APIのサポートも充実しています。機能が非常に多く、負荷テストやセキュリティテストも可能です。

テストデータをExcelなどから取り込みでき、アサーションが強力です。インターフェースは少し古いですが、全面的なテストに適しています。

## まとめ
以上がWindowsにおける5つのHTTPテストツールの紹介です。それぞれに特長と利点があります。Apidogのような一体化設計はチーム使用に最適ですが、どれを選ぶかはあなたの具体的なニーズ次第です。
UnrealEngineのマルチプレイヤーのPlay In Editorで途中参加を試す

UnrealEngineで[Number of Players]を2以上にして[NetMode]を[Play As Listen Server]などとしてのPlya In Editorすると最初から接続状態でマルチプレイを確認できます。
途中参加するような場合どうすればいいのか不明だったので試してみました。
その内容を記録しておきます。

# 試した環境

UE5.5.3のThirdPerson Template

# 試した内容

以下の接続方法で途中参加のマルチプレイができます。
ただし、ネットワーク速度に制限をかけたような場合も無視されるようです。

1. [NetMode]を[Play Standalone]に
1. [Number of Players]を2以上に
1. [Selected Viewport]でPlay In Editor実施
1. サーバーにしたいウィンドウのコンソールコマンドで「open ThirdPersonMap?listen」実施 => マップが開きなおされる
1. クライアントにしたいウィンドウのコンソールコマンドで「open 127.0.0.1」を実施 => 接続

----

以下の接続方法で、初回接続状態にはなってしまいますが途中参加のマルチプレイができます。
ネットワーク速度に制限をかけたような場合も適用されるようです。

1. [Advanced Settings]の[Multiplayer Options] > [Enable Network Emulation]をチェック、[Emulation Target]を[Everyone]に、[Network Emulation Profile]を[Bad]にして遅いネットワーク状態を設定
1. [NetMode]を[Play As Listen Server]に
1. [Number of Players]を2以上に
1. [Selected Viewport]でPlay In Editor実施
1. 再接続したいクライアントのコンソールコマンドで「open ThirdPersionMap」を実施 => 切断される（5.をせずにいきなり6,も実施可能）
1. 同クライアントのコンソールコマンドで「open 127.0.0.1:17777」を実施 => 再接続される、Network Badも適用されるかんじ([17777は[Advanced Settings] > [Multiplayer Options] > [Server] > [Server Port]のデフォルトの値)
Copilotを使ってOutlookで空き時間に会議を予約する方法

## はじめに
Microsoft Copilotは、Outlookでのスケジュール管理を簡単にする便利なツールです。ここでは、Copilotを使って空き時間に会議を予約する方法をわかりやすく説明します。

## 手順

### 1. Copilotを起動する
まず、ブラウザでOutlookを開き、右上のCopilotアイコンをクリックしてCopilotを起動する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3123325/c06667b8-de34-4aff-b93b-7b9d61fc00cd.png)

:::note info
ポイント
- ブラウザのCopilotは個人の情報（メールやチャット）と共有フォルダを参照できるが、他人の情報にはアクセスできない
- OutlookのCopliotは、他人と予定表を共有していれば、Copilotは参照できる
- 有料ライセンスが必要
:::

### 2. 会議の予約を依頼する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3123325/a2ba80d4-03b0-41a6-a818-59b29525aa5b.png)
スケジュールを押下する　または　下記のようにプロンプトを作成する。
#### プロンプトを作成するにあたって
- 名前はシステムに登録されているIDで記入する。ただしよく使う人であればフルネームでなくても出てくる。
- 提示される時間範囲は、稼働時間内の空き時間になる。（稼働時間はファイル＞オプション＞予定表＞稼働時間で設定できる）
- 時間指定も文言に追加すると可能（2025/6/4　追記）
- 文言は基本適当でも理解してくれる。
- 時間指定の文言は無視される（稼働時間参照のため）。
- たまにIDを勝手に日本語翻訳して上手く動かない場合があるがその場合は、IDは英語ままでと記載する。
- /を入れて名前を記入できる（2025/6/4　追記）
```
Hoge hoge,Honge Hongeと来週の1時間ミーティングをスケジュールする。
```

#### 複数人の会議を開くとき
全員分の名前を記入するのは面倒な場合、下記のように既にスケジュールされている会議から参加者を引っ張ってきてもらうようにする。

:::note warn
注意
- 会議名をあいまいにすると別の会議の参加者が表示されるので、会議名は正確に記入する
- 正確に記入しても、参加者が足りないこともあるので必ず確認すること
- 主催者と記入しないと、主催者が抜けていることがある
:::

```
HogeHoge定例の主催者と参加者で、3月に1時間打ち合わせをおこないたい。
```

会議の一部の人間だけの場合、参加者の一覧を訊くともできる。
```
HogeHoge定例の主催者と参加者の名前を教えてください。
```
### 3. 提案された時間を確認する
Copilotが提案する時間を確認し、都合の良い時間を選ぶ。
このような返信がくるので編集を押下する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3123325/e925860f-ddfa-44c3-aa19-96981264f9e6.png)


開いた先で候補が3つ提示される。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3123325/6cc61cf4-d559-440c-8605-b90b213bd1cb.png)

スケジュールアシスタント画面も開ける。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3123325/fbd6c766-daf5-46d8-864f-19944250159c.png)

### 4. 会議の詳細を入力する
会議のタイトル、場所などの詳細を入力する。

:::note info
時間の候補が消えた場合は、「時間の検索」を押すと再度検索され、候補表示される
:::

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3123325/c598dbf6-312f-468e-9342-0115bad68dfa.png)


### 5. 会議を送信する
すべての詳細を確認し、「送信」ボタンをクリックして会議の招待を送信する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3123325/41186a34-d6af-4538-af5b-167150cde240.png)

## まとめ
Copilotを使えば、Outlookでの会議予約がとても簡単になります。ぜひ試してみてください！
カンパニー制のスタートアップのエンジニアになるな

## まとめ

グループ化したスタートアップのリスク/デメリット

- SOの価値が下がることがある/もしくはそもそも魅力的なSOが無い  
- エンジニアは評価制度が統一されておらず、フラストレーションが溜まるばかり
- カンパニーCTOなどの肩書きはあるが、実質「店長」のような扱いを理解して
- 大企業のような評価制度の恩恵（難易度の高い挑戦が評価される）がなく、利益額だけで評価されがち  

---

最近、スタートアップ界隈でよく聞く「グループ化（カンパニー制）」に関して、気になる話があったので共有したいと思います。特にエンジニアとして転職を考えている方は要注意です。

## 同僚の事例：SO（ストックオプション）の価値が下がった

### 1. 同僚が上場企業（前職）でストックオプションを貰っていた
上場前からSOを持っていたため、当然ながら上場後はその価値が大きく上がることが期待されていました。

### 2. グループ化した際にSOの価値が下がることを知らされていなかった 
あるタイミングで会社が「グループ化」し、既存のSOが思ったよりも価値を持たなくなる（あるいは条件が変わる）事態が発生。期待していたリターンが大きく損なわれる結果となりました。

(普通に詐欺っぽいけど...上場前だしSOは関係無いのかな?)

## 「カンパニー企業」は上場できない（しない）企業が取る手法？

グループという体制だと、ある事業部を切り離して売却する際などにスムーズなことが多いです。実質的には「いつでも売却しやすいパッケージ」を作っているに近いと言えます。

## カンパニーCTOは「店長のような役職」

### カッコよさだけのCTO？
恥ずかしいと思って下さい。

### 転職時に意味がない肩書き
新しい職場に移る際に、採用担当が見るのは「何をやってきたのか」「どんな成果を残したのか」。肩書きはそれほど重要ではありません。華やかな肩書きだけを与えられても、実のところ転職市場ではあまり有利にならないのです。


## エンジニアの悲惨な現状

- **評価制度が統一されていない**  
  グループ化した企業はそれぞれのカンパニー（事業部）で評価基準がバラバラだったり、全社的な評価指標があいまいなことが多いです。結果として、「頑張っているのに報われない」エンジニアが増えます。
- **SO（ストックオプション）が無い／価値が乏しい**  
  先述の通り、上場目指して一発逆転を狙うようなスタートアップ企業でなければ、エンジニアが大量のSOを与えられるということも少なくなります。上場を目指していない、あるいは買収される前提の場合、SOの魅力は大幅に下がります。
- **フラストレーションばかりが溜まる**  
  グループ化した企業では、エンジニアが頑張っても大きなリターン（キャリアアップや報酬アップなど）が見込めにくいのが現実。挑戦のしがいもなく、技術力よりも「どれだけコストを下げられるか」「どれだけ安定稼働させられるか」しか注目されない場合もあります。

## ビジネス職は嬉しいかもしれない

- **大企業の場合の評価**  
  - 「今期30億利益達成できる可能性が10%の高難度のチャレンジ」  
  - 「今期100億利益達成できる可能性が90%の低難度のチャレンジ」  
  大企業では、「難易度の高いチャレンジに取り組んだ人」や「リスクは高いが大きな可能性に賭けた人」が評価される傾向があります。失敗したとしても、挑戦そのものが認められることがある。
- **カンパニーCEOは簡単なことでも評価される**  
  カンパニー制では、「今期の利益額」に対してシンプルに評価されがち。  
  - 難易度が高くて利益が少ない仕事よりも、難易度が低くても利益が大きい仕事を達成した方が評価されやすい。  
  そのためビジネス職の人にはメリットがあるかもしれません。

## 対策

### 1. 優秀な人はグループ化した企業に入らない
今後優秀になるエンジニアのキャリアのために、入らないで下さい

### 2. 業務委託で構成してください
Rubyのマージテク

Rubyで競技プログラミングする人向けのTipsです。

マージテクってありますよね。
set とか Hash をマージするときに要素数の少ない方を列挙することで計算量を削減するやつ
RubyのHashにmergeメソッドあるのですけど、このとき引数に要素数が少ない方を選ぶとマージテクになります

```ruby
large, small = small, large if large.size < small.size
large.merge!(small)
```

もちろん eachで回すより、mergeでやるほうが早いです。

```ruby
small.each{|k, v| large[k] = v }
large.merge!(small) # こっちのが早い
```

コメントで以下の質問を頂いたので追記

> key が重複しないとき専用でしょうか？
> key が重複すると、large の値が上書きされてしまうので、それでいいのか考えた上で使う必要のあるテクだと思います。

上記のコードだと上書きになります。
たしかにマージテクを語る上ではキーが重複した場合のほうが重要かもしれませんね。
キーが重複したときに値をうまくマージしたいときにはブロック付きのほうを使います。

```ruby
large.merge!(small) do |key, ours, theirs|
    ours + theirs
end
```
これでキーが重複したときはその値の和になります。

```irb
# ブロックなし (上書き)
{ a: 1, b: 2 }.merge!({ a: 1, c: 2})
=> {:a=>1, :b=>2, :c=>2}

# ブロック付き (ブロック引数でマージ処理)
{ a: 1, b: 2 }.merge!({ a: 1, c: 2}){|k, a, b| a + b }
=> {:a=>2, :b=>2, :c=>2}
```

たまにマージ処理後にもうこのキーは不要なので削除したいというときがあるんですが、それはできないんですよね。
defaultと同じ値のときに削除にならないかなーと思うときがあります。
【WPF】TextBoxからTextBlockに変更する際の注意事項　※スクロールバーなど

## はじめに
数年前に開発したデスクトップ版アドインを最近一部改修したので、
備忘録として記載いたします。

## 背景
メッセージ表示で使用しているコンポーネントをCSSのように装飾したい
例：「<font color="red">**こんな風に**</font>特定の文字列のフォントを変更したい要望でした」

## 課題と対応策
### 課題１
使用しているコントロールが TextBox のため、特定の文字列のみの装飾に対応していない。
⇒  **TextBlock** に変更する

### 課題２
TextBlock が VerticalScrollBarVisibility プロパティや BorderBrush プロパティに対応していない。
⇒  個別にスクロールバー、ボーダーを配置する（これが大変でした）

## 実装
### 改修前
改修前の TextBox の中身がこんな感じです。
コントロールのプロパティでレイアウトを一括で設定しています。
```xml
<Grid.ColumnDefinitions>
    <ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
    <RowDefinition Height="*" />
    <RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBox  Height="70" FontSize="12" Name="MessageLabel" Foreground="Red" 
    FontWeight="Bold" Text="" VerticalScrollBarVisibility="Visible" IsReadOnly="True" 
    TextWrapping="Wrap" BorderBrush="#FFD5DFE5" Grid.Column="0" Grid.Row="0" />
```

### 改修後
次に改修後の TextBlock の中身がこんな感じです。
課題２で記載した TextBlock で用意されていないプロパティについては個別にコントロールを配置しました。

ただしスクロールバーを配置する際は、
Gridの高さを **Height="*"** ではなく、**数値**で指定するようにしてください。
理由はおそらく以下の記事に書いてある通りかと思いますが、スクロールのノブが表示されなくなります。

https://nomoredeathmarch.hatenablog.com/entry/2019/01/20/181139

```xml
<Grid.ColumnDefinitions>
    <ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
    <RowDefinition Height="75" />
    <RowDefinition Height="*" />
</Grid.RowDefinitions>
<Border BorderBrush="#FFD5DFE5" BorderThickness="1">
    <ScrollViewer>
        <TextBlock FontSize="12" Name="MessageLabel" Foreground="Red" 
            FontWeight="Bold" TextWrapping="Wrap" Grid.Column="0" Grid.Row="0">
        </TextBlock>
    </ScrollViewer>
</Border>
```

また上のコードでは文字の装飾を赤文字・太字で設定していますが、
通常の黒文字で表示したい文字列についてはxaml.cs側で以下のように対応してください。
```C#
MessageLabel.Inlines.Clear();
MessageLabel.Inlines.Add(new Run() { Text = "これは黒文字", FontWeight = FontWeights.Normal, Foreground = Brushes.Black });
MessageLabel.Inlines.Add(new Run() { Text = "これは赤文字・太字" });            
```
【 HSP3 】【 HSP3Dish 】30年改善と愛用が継続する日本国産プログラミング言語の現在地2025年

# HSP3 の現在地

むかしの「HSP」から情報が更新されていない人は「Windowsのラッパースクリプト言語」という認識のままかもしれません。しかし、2025年現在 HSP は、マルチプラットフォーム開発用のプログラミング言語に進化成長を遂げています。

## 伝えたい その１ 【 Try HSP3Dish 】があります

ブラウザでHSPプログラミングと実行ができる『 Try HSP3Dish 』というモノがあります。



![ipadでtryhsp.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/196881/8fa20b02-d73f-452e-8869-b817be32154b.png)

いますぐ使ってみる

http://peppermint.jp/products/hsp/try.html

:::note warn
注意
Mac の 標準ブラウザ Safari で動作しない場合は Chrome または Firefox ブラウザを使用してください。
HSP3Dish製のWebアプリは ふるいMac（プロセッサが Intel Core i）でうまく動作しない可能性があるとの報告があがっています。ブラウザを Chrome か Firefox で使用すれば、Intel Mac 上でも HSP製Webアプリが動作します   
:::

パソコンを持っていなくても、タブレットやスマホだけでもプログラミングが可能になっています。
（現状は「プレイグラウンド」以上、「ブラウザIDE」未満といった感じ）
　
![tryhsp3dish.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/196881/34507e2d-799a-49d7-84c2-f2107e5bd8ad.png)

Svelte や Ruby の Playground のようなモノ

## 伝えたい その２ 【 WebDish 】があります

Windows用HSPとほとんど同じ書式や文法にほんのすこし制限をつけたのが HSP3Dish です。
HSP3Dish でプログラミングすることで、その出来上がったプログラムスクリプトは
・ラズパイ Linux上のHSPでもそのまま実行できる
・付属の変換ツール（HSP3Dish Helper）でC++に変換し、iOSやAndroidで動作させられる
・付属の変換ツール（HSP3Dish Helper）でWebアプリ化できる

そしてこの変換ツールを使わなくても、サイト上の WebDish ページからプログラムスクリプトを送信すると、自動的にWebアプリ化して、しかもそのWebアプリが動作するページまで生成して、そのURLを発行してくれるのです。

いますぐ使ってみる

https://dev.onionsoft.net/seed/webdish.html

つまり、パソコンを持っていなくてもプログラミングができるだけでなく、ネットを使ってその作ったプログラム作品を自由に世界へ向けて公開できるサービスなのです。

***


## 伝えたい その３ 【 ラズパイ Linux 版 HSP3Dish 】があります

これまで普通のひとが「プログラミングがしたい」「プログラミングを学びたい」となった場合、まずは、WindowsマシンかMacのパソコンを購入するというこの二つの選択肢しか現実的にはありませんでした。
しかし、2025年現在では、プログラミング学習の実用がじゅうぶん果たせるレベルの
『 ラズパイ Linux 版 HSP3Dish 』が存在しています。

いますぐ使ってみる

https://hsp.tv/make/hsp3linux_pi.html

実際に全国で「 ラズパイ + HSP3Dish 」という組み合わせでプログラミング教室やプログラミング講座が行われています。（導入のための予算や機材が小規模で済む良さもあります）


***

## 一目でわかるHSP全体図

![hsp全体図20250223.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/196881/074d17ab-554c-48f3-9306-f1ba7f9e8296.png)



## あれから30年。Windows 95 の頃に登場した国産プログラミング言語 HSP

『 Windows 95 』という製品をおぼえていますか？ 1995年に発売したオペレーティングシステム (OS)です。
いまは 2025年だから、あれから 30年の時間が経過しています。
当時のOSやプログラミング言語（およびコンパイラ）は有料で高価で、だれでも気軽にプログラミングが試せるという環境がなかなかありませんでした。　

 ```hsp:hsp3dish_test.hsp
 #include "hsp3dish.as
 
	screen 0,320,480
	title "タイトル"

*main
	redraw 0
	color 0,0,32 : boxf
	color 255,255,255 : pos 10,24 : mes "こんにちは"
	redraw 1
	await 33
	goto *main
    
```
さらにその10年、15年まえにはパソコン（PC8001、PC8801、MZ-2000、X-1、FM-7、MSX）を買うとプログラミング言語が無料で付属してきて、本体を買ってすぐにだれでもプログラミングができるという環境を体験してきた人たちからすると「おお！ Windows 95、便利でかっこいいじゃん！ でも、気軽にプログラミングできないよ… 」という状況のさなかに、この国産プログラム言語 HSP が登場したことは、当時のプログラミング少年や市井の個人プログラマーにとっては非常にありがたい存在だったのです。


https://junji.jp/net/hsp3/


---
Qiita に 別の記事も書きました

https://qiita.com/Junji_Suzuki_JBOYSOFT/items/39c625c256a6778788d8
JavaScriptPrimer 改訂２版を読んで

## まえがき
JavaScriptPrimer 改訂２版を読んでのまとめ書き（感想）となります。
筆者は、JavaScriptを書いてきてはいましたが、レビューされるような環境がなく、目的の動きにはできるものの可読性やメンテナンス性などにおいては自身の判断でしかない状態で本書を読みました。

## よかったところ
* 古いVersionでの記述方法や、当時にはどのような問題があって新しいVersionで機能が追加されたのかを学べること
* サンプルコードが豊富で実際に動かすことができ、疑問の思ったところは手元で試しやすいところ
* `Promise`/`Async/Await`について整理できていなかったが、本書では順序立てて説明されていて理解しやすい
    * `Promise`はコールバック地獄を解消するため
    * `Promise`では`then()`の連鎖が長くなり、可読性が悪くなるケース
    * `Async/Await`を使えば、同期処理のように記述でき、また`try-catch`で例外処理できてシンプル
    * `Async/Await`の使用が基本的には推奨でありつつも、並列処理のシーンでは`Promise.all()`を使うなど、`Promise`も必要であること
* （出力して確認しつつ）期待通り動いてるし良いかという程度で`this`を使っていたが、少し深く知れたこと
* todoアプリのハンズオンで、段階的にMVCの構造になっていくまでを体験できるところ

## 学んだこと
* `call`/`apply`/`bind`による明示的な`this`の指定
```javascript
function sayHi() {
  console.log(this.name);
}
const person = { name: "Dave" };
const bindSayHi = sayHi.bind(person);
bindSayHi();
sayHi.call(person);
sayHi.apply(person);
```
* アロー関数の`this`は外側の`this`をそのまま引き継ぐと覚えておけばよいこと
    * なので、下記のような関数で起こる問題を
    ```javascript
        function Person(name) {
          this.name = name;
          setTimeout(function () {
            console.log(this.name); // undefined
          }, 1000);
        }
        
        const user = new Person("Bob");
    ```
    * アロー関数にすることで解決できる
    ```javascript
        function Person(name) {
          this.name = name;
          setTimeout(() => {
            console.log(this.name); // "Bob"
          }, 1000);
        }
        
        const user = new Person("Bob");
    ```
    * 逆に、下記のようなケースではアロー関数でなく、通常の関数を使う
    ```javascript
        const obj = {
          name: "Bob",
          greet: () => {
            console.log(this.name);
          }
        };
        obj.greet(); // undefined
    ```
* ECMAScript 2022から、`Array.prototype`に`at`メソッドが追加されたこと
    * 配列の末尾を表す際、`配列.length - 1`のようにせず、`配列.at(-1)`と簡潔に書けるようになっている
    ```javascript
    const arr = [10, 20, 30, 40, 50];
    
    // 従来の方法
    console.log(arr[arr.length - 1]); // 50
    
    // at() メソッドを使う方法
    console.log(arr.at(-1)); // 50
    ```

## 難しかったところ
* `this`の指すものが様々な要素（クラス、通常の関数、アロー関数、イベント内など）で変わる点がややこしい
    * 使用頻度の高いものなので、適宜本書を参照するなり、MDNを参照するなりで理解を深めていければと思う
* 文字列、Unicodeに関する範囲については、読んでいるときは「なるほど〜」とはなるが、理解しづらく感じる
    * 当範囲と対面した際には本書を参照しつつ、問題解決していければと思う

## さいごに
かなりボリュームのある教材と感じました。
すでにある程度Javascriptを書く機会があったので、知っている範囲も多く、またMDNという非常にありがたい資料もあるので、そちらで済んでしまうような部分もあったかもしれません。
しかし、MDNでは複雑に感じた説明も、本書ではより理解しやすく噛み砕かれて記載されているので、過去に読んでなんとなく使っていたものも、より深く知ることができたように思います。
また、自分としては最後にあったtodoアプリを作成するハンズオンで、JavaScriptのコードを書く上での一つの設計例を学べたのは良かったなと感じています。

JavaScriptの初学者の方、また必要な範囲だけを学んでとりあえず動くものを作れてはいるくらいの方（自分のような）は、興味があればぜひ一読されてみることをおすすめいたします。
任意のCryptomatteデータを作成する

前回BlenderからAfterEffectsにマスクデータを受け渡す方式として
OpenEXRで保存したCryptomatteデータの基本的な使い方を試しました

https://qiita.com/yukimituki11/items/23f3e311dca58bbf4f19

::: note
AfterEffectsではエフェクトでクリックした部分のマスクを抽出できるので
動画の編集作業をする人には　複数のマスクやカラーマスクより扱いやすく感じるようです
ただ、標準機能では現状ではオブジェクトやマテリアル基準で分けるデータしか作成できません
:::

任意の塗分けのマスクを作れないかCryptomatteの規格について調べてみました
仕様については　wikipediaからもリンクされているこのPDFの通りのようです
https://raw.githubusercontent.com/Psyop/Cryptomatte/master/specification/IDmattes_poster.pdf
どうやら
- オブジェクトやマテリアルといったものにIDを割り当て
- 画像のピクセル毎に画像の要素にどのIDのものが寄与している割合が大きいかを計算して順序付けし
- 順序付けした順に**レベル**（画像処理ソフトのレイヤのようなもの）で上になるようにする
- **カバレッジ**（そのIDの要素がピクセルにどの程度寄与してるか）を記録する
といった処理のようです

1つのチャンネルの値の違いの塗分けでIDを表現するようですが
OpenEXRは各チャンネル32ビット浮動小数点でデータを記録するので　1チャンネルだけでも十分な数です
（16ビット浮動小数点や圧縮方式によっては24ビット精度になるようです）
また、1つのレベルはIDとカバレッジのペアで構成されていて
1つのレベルで画像のRGBAのうち2チャンネルづつを利用するようです
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151493/3e4764ba-f416-4fc2-8ae4-168efe35081b.png)
Cryptomatteを設定する時のデフォルトのレベルが6でレンダーレイヤが3つ出力されているのはこういったことですね
## Cryptomatteの値
Cryptomatteで出力された画像の値を見ておきます
左がレンダリングのプレビュー画像　右がCryptomatteで　赤色のオブジェクトは半透明です
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151493/1e215e87-0f0f-493f-a32c-e90834836e44.png)
Cryptomatteは一見ただの緑色主体の画像ですが
IDを示す数が入っていて とても大きな値だったりマイナスだったりします
半透明の重なりのない部分はRとGのチャンネルのみ値が入っていて
オブジェクトIDなので　重なりがなければ同じオブジェクトの部分では全部同じ値です

一方の半透明の部分はRにピクセル内で一番寄与している画像のID　Gにカバレッジ　Bに２番目に寄与しているIDと場所により変化します

::: note
半透明で重なったものが幾つもある場合にはレベルが何階層も必要になりますが
単純な塗分けマスク程度の場合は　IDを示すベタ塗りと境目のアンチエイリアス情報のペアだけしか必要ないので
レベルも2つ（レンダーレイヤー1つ分）で済みそうです
:::
Cryptomatteデータを自分で作ってみたいと思います。
## IDでの単純な塗分け
まず左端のような画像を元に　透明　白　黒　ではっきり塗分けたCryptomatte画像をAfterEffectsに読み込ませてみました
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151493/4b4ed980-7687-41af-9035-a7a0a64d010a.png)
黒部分のRチャンネル(Xチャンネル)に0.1　白部分に0.5と　ID代りの数値を入れています
透明部分は空白を表す0.0　1つのレベルしか使用していないのでアルファは全て0に設定しました
![2502601.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151493/1c43eb09-1dd5-4961-a524-3a3ea7b12932.gif)
AfterEffectsに読み込んだところ　「Criptmatte」のエフェクトを手動で設定する必要はありますが
マットの範囲の指定を読ませることができました
## グレースケールのCryptomatte変換
仕様に合わせてグレースケールのマスクを作る計算をし
- カバレッジのカバレッジの一番大きいIDをRチャンネルに
- 重なりがある場合はもう一方のIDをBチャンネルに
- 上のレイヤのカバレッジ（グレースケール）をGチャンネルに
- 1から上のレイヤのカバレッジを引いた値をアルファチャンネルに

をBlenderのコンポジションノードで作るとこうなります
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151493/1d64b059-e479-46d3-a482-5c21f8945b5e.png)
AfterEffectsで読ませてみました
![2502602.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151493/aea2e9b0-7ddd-4453-b875-2d7de32676e5.gif)
うまくマスク抽出できているようですね

もっとシンプルに　カバレッジに応じて前後を変えなくても
AfterEffectsではそれぞれのマスクのIDとグレースケールのみでも
同様に選択ができましたので　その計算をしてみました
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151493/8262aacb-8d9f-4c70-a323-dfc9ff1cffb6.png)
これならかなりシンプルですね。

::: note
ただ注意が必要なのは
一番上のレベルに有効なIDを設定せずに下のレベルにマスクのデータを設定してもマスクとして機能しないようで
また カバレッジを0に設定した状態でも　そのカーソル位置に設定されているIDを選択してしまうようです。
一方で　複数のレベルのカバレッジの合計を1にする計算がファイルの仕様に書かれていますが
重なった部分の 片方のカバレッジを1にする等、合計が1でない設定でもエラーにはならないようです
:::

図の　中央やや左の「比較」を使った計算が入っているのは不要な箇所にはID0を設定するためのものです

このまま使うということはないかもしれませんが
何かの参考になれば幸いです
AMIの登録解除の際にスナップショットも併せて削除するシェルを作ったのでご紹介

# 目次
[1.はじめに](#1-はじめに)
[2.前提条件](#2-前提条件)
[3.シェル](#3-シェル)
[4.まとめ](#4-まとめ)

# 1. はじめに
どうも皆さんこんにちは。こんばんは。おやすみなさい。
インスタンス削除に伴って紐づくリソースを削除する際にAMIの登録解除がありますよね。

今回私が対応した時にAMIが三桁個あってAMI確認→紐づいているスナップショット確認→AMI登録解除＆スナップショット削除の流れがコンソールでやるのが面倒くさすぎたのでシェルを作りました。

作った当初は覚えてても自分でも忘れるので、備忘の念も込めてここに記載します。
同じような人の役に立てれば、なお良し！！！！！
じゃ、本題です。

# 2. 前提条件
今回は引数指定でのシェルを採用しているので、事前に解除したいAMIをリストアップしてIDを認識しておく必要が有ります。
対話形式型ではないので一回打つと処理が走ってしまいますので注意が必要です。

（削除するなら削除対象のリストアップは事前にしていると思いますが、、、、、）

！！！！！！！削除作業系は削除前の確認が重要です！！！！！！！

環境ですが、今回はCloudshellにシェルをアップロードして権限付与し実行する流れです。

# 3. シェル
今回作成したシェルです。

```shell:delete_ami_snapshots.sh
#!/bin/bash

# AMI ID を引数で指定
AMI_ID=$1

if [ -z "$AMI_ID" ]; then
  echo "使用方法: $0 <AMI_ID>"
  exit 1
fi

echo "指定された AMI: $AMI_ID"

# AMI に紐づくスナップショットを取得
SNAPSHOT_IDS=$(aws ec2 describe-images \
  --image-ids "$AMI_ID" \
  --query "Images[].BlockDeviceMappings[].Ebs.SnapshotId" \
  --output text)

if [ -z "$SNAPSHOT_IDS" ]; then
  echo "AMI $AMI_ID に紐づくスナップショットはありません。"
else
  echo "AMI に紐づくスナップショット: $SNAPSHOT_IDS"
fi

# AMI を解除
echo "AMI $AMI_ID を解除します..."
aws ec2 deregister-image --image-id "$AMI_ID"
echo "AMI $AMI_ID を解除しました。"

# スナップショットを削除
for SNAPSHOT_ID in $SNAPSHOT_IDS; do
  echo "スナップショット $SNAPSHOT_ID を削除します..."
  aws ec2 delete-snapshot --snapshot-id "$SNAPSHOT_ID"
  echo "スナップショット $SNAPSHOT_ID を削除しました。"
done

echo "AMI および紐づくスナップショットの削除が完了しました。"
```

上記をコピーして適当に保存してください。
名前は今回「delete_ami_snapshots.sh」にしています。

Cloudshellに保存したものをアップロードしたあとで権限の付与をしてください。
※ls -alとかで権限確認してね！！

```shell:権限付与
chmod +x delete_ami_snapshots.sh
```
シェルを実行します。
以下のような指定方法です。
./delete_ami_snapshots.sh　＜amiID＞

```shell:実行例
./delete_ami_snapshots.sh ami-123456789abc
```

改行コードで引っかかる場合は以下を打ってください。
```shell:権限付与
sed -i 's/\r$//' <改行コード変えたいシェルとかの名前>
```

# 4. まとめ
実行する際は改めて登録解除するAMIIDが間違っていないかをくれぐれもご確認ください。
削除作業は慎重に行いましょう。
それではどうぞご安全に！！！！！！！！！！！！！！！
Discordのbot制作で永遠にエラーが出た話

# 初めに
初めて記事を書くので見ずらい・読みずらいのは許してください
# エラーが出るまでの経緯
先日、あるVTuberのファンディスコ鯖用にbotを作っていました。
私は一からコードを書くことが出来ないので、[EOi](https://x.com/exteoi"Twitter")さん制作の[Bot「MiniPotato」](https://note.com/exteoi/n/n00342a623c93)を改変する形で制作していました。
# エラー発生とその内容
EOiさんの記事を参考にして、トークンとアプリケーションIDを入力し、いざ動かそうというときにエラーが発生しました。
```ruby:Logs.txt
/rbd/pnpm-volume/46caa70e-a7c8-4768-905c-69edf7aa9a22/node_modules/@discordjs/ws/dist/index.js:1132
          error: new Error("Used disallowed intents")
                 ^
Error: Used disallowed intents
    at WebSocketShard.onClose (/rbd/pnpm-volume/46caa70e-a7c8-4768-905c-69edf7aa9a22/node_modules/@discordjs/ws/dist/index.js:1132:18)
    at WebSocket.connection.onclose (/rbd/pnpm-volume/46caa70e-a7c8-4768-905c-69edf7aa9a22/node_modules/@discordjs/ws/dist/index.js:676:17)
    at callListener (/rbd/pnpm-volume/46caa70e-a7c8-4768-905c-69edf7aa9a22/node_modules/ws/lib/event-target.js:290:14)
    at WebSocket.onClose (/rbd/pnpm-volume/46caa70e-a7c8-4768-905c-69edf7aa9a22/node_modules/ws/lib/event-target.js:220:9)
    at WebSocket.emit (node:events:526:28)
    at WebSocket.emitClose (/rbd/pnpm-volume/46caa70e-a7c8-4768-905c-69edf7aa9a22/node_modules/ws/lib/websocket.js:260:10)
    at TLSSocket.socketOnClose (/rbd/pnpm-volume/46caa70e-a7c8-4768-905c-69edf7aa9a22/node_modules/ws/lib/websocket.js:1272:15)
    at TLSSocket.emit (node:events:538:35)
    at node:net:687:12
    at TCP.done (node:_tls_wrap:580:7)
```
初見で原因を見抜ける人はいないと思います。
`Used disallowed intents`日本語にすると「使用禁止インテント」
意味が分かりません。
エラーの内容もDiscord.jsなどのパッケージのファイルで、参照の仕方が違うのかと確かめてもミスはなく、困り果てていました。
# 解決
**プログラム**に対して何をやってもエラーが治らず困り果てていたところ、一つの妙案を思いつきました
**Discord側**の設定です。
使用禁止って事は何か権限が足りないのかもしれないと。
![Discord Developer Portal - Google Chrome 2025_02_27 11_24_24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4022438/2d803c22-5a39-4bb8-b4d2-fb910c85ae2a.png)
### これだ!
この一番下の設定をオンにして再起動したら何事もなかったかのように稼働しました。
めでたしめでたし
## 参考文献
https://note.com/exteoi/n/n00342a623c93
【Nativescript】platforms内のファイルを`ns run(prepare) ***`実行時に自動で変更したい→フックスクリプトを導入した備忘録

# はじめに
Nativescript+Angularを用いてアプリ開発中、`ns run android`実行前に`platforms/android/gradle.properties`の値を手動で変更していました。
ただ、package.json変更時など毎回手動変更はつらい（忘れる）ので、スクリプトで実行できるように調整しました。
その際に調べた内容・対応方法の備忘録になります。

# 環境
- MacOS: 14.7.2
- nodeバージョン: v16.14.2
- Nativescript 8.5.3
- Angular 15.2.10


## 今回追加するフックスクリプトの内容について ※参考程度に。飛ばしても問題ないはず

開発環境構築中に`ns run android`実行した際、下記のエラーが発生しました。

```
FAILURE: Build failed with an exception.
* What went wrong:
Gradle build daemon disappeared unexpectedly (it may have been killed or may have crashed)
```

MacOSとの問題がありそうですが、下記のStackOverflow回答を参考に`platforms/android/gradle.properties`の`org.gradle.jvmargs`の値を変更して解消しました。
[Gradle build daemon disappeared unexpectedly (it may have been killed or may have crashed) while building Android project on Jenkins - Stack Overflow](https://stackoverflow.com/questions/37171043/gradle-build-daemon-disappeared-unexpectedly-it-may-have-been-killed-or-may-hav)

今回は、上記の値を変更を行うスクリプトを`ns run(prepare) ***`実行に動作するよう調整します。

# フックスクリプトファイルを追加する

- `hooks`や`platforms`と同階層に`custom-hooks`ディレクトリを追加します。
	- 追加する場所はお好きなところでOKです。
- `custom-hooks`直下にhook type名でディレクトリを追加、そのディレクトリにスクリプトファイルを追加します。
	- typeはNativescriptの[Hooks Configuration Reference](https://docs.nativescript.org/configuration/nativescript#hooks-configuration-reference)を参照。
	- 今回はwebpackのコンパイル前に実行したいので、`custom-hooks/before-prepare/modify-gradle-properties.js`を追加しました。
- 以下は、スクリプトファイルの内容です。参考までに。
```custom-hooks/before-prepare/modify-gradle-properties.js
const fs = require('fs');
const path = require('path');

module.exports = function ($logger, $projectData, hookArgs) {
    const gradlePropertiesPath = path.join($projectData.platformsDir, 'android', 'gradle.properties');

    if (fs.existsSync(gradlePropertiesPath)) {
        let content = fs.readFileSync(gradlePropertiesPath, 'utf8');

        // org.gradle.jvmargs を変更
        content = content.replace(
            /^org\.gradle\.jvmargs=.*$/m,
            'org.gradle.jvmargs=-Xmx15g -XX:+UseParallelGC'
        );

        fs.writeFileSync(gradlePropertiesPath, content, 'utf8');
        $logger.info('Updated gradle.properties: org.gradle.jvmargs modified');
    }
};
```


# nativescript.config.tsに`hooks`を追加する
- nativescript.config.tsに`hooks`を追加します。
- `hooks`内には追加するhookのtypeとスクリプトファイルのパスを追加します。
	- typeは[Hooks Configuration Reference](https://docs.nativescript.org/configuration/nativescript#hooks-configuration-reference)参照


```nativescript.config.ts
import { NativeScriptConfig } from '@nativescript/core'

export default {
  ...（省略）
  hooks: [
    {
      type: 'before-prepare',
      script: 'custom-hooks/before-prepare/modify-gradle-properties.js'
    }
  ]
} as NativeScriptConfig

```


ここまで設定すれば、`ns run(prepare) android`実行時にフックスクリプトが実行されます🎉


# おまけ
ChatGPTの回答で`App_Resources/Android/app.gradle`の設定を追加する方法もあったのですが、自分の環境では動作しませんでした。
念のため参考に載せておきます。

> `App_Resources/Android/app.gradle` に以下を追加すると、`gradle.properties` の `org.gradle.jvmargs` の値をカスタマイズできます。
```App_Resources/Android/app.gradle
android {
    project.ext {
        set("org.gradle.jvmargs", "-Xmx4g -XX:+UseParallelGC")
    }
}
```