OmniAICreator/Qiita-1.07M huggingface dataset:
【flutter】インジゲーターをおしゃれにしてみた

こんにちは！
今回はおしゃれなインジゲータを表示できたので、そちらの方法を紹介したいと思います。
最後まで読んでいただけると嬉しいです！

# 使用したパッケージ

https://pub.dev/packages/gauge_indicator

# 実装内容

![Qiita_sample_image.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524127/c7bd8f59-9335-59f0-1b9b-7f4b59bed8bf.gif)


# 実装コード
```php
Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
          // AnimatedRadialGaugeウィジェットは、円形のゲージを表示します。
            AnimatedRadialGauge(
            //アニメーションの実行時間を7秒に設定します。
              duration: const Duration(seconds: 7),
              curve: Curves.elasticOut,
              radius: 100,
              value: value,
              axis: const GaugeAxis(
                min: 0,
                max: 100,
                degrees: 300,// ゲージの描画範囲を300度に設定します。
                style: GaugeAxisStyle(
                  thickness: 20,
                  background: Color(0xFFDFE2EC),
                  segmentSpacing: 4,
                ),
                segments: [
                  GaugeSegment(
                    from: 0,
                    to: 33.3,
                    color: Color(0xFFD9DEEB),
                    cornerRadius: Radius.zero,
                  ),
                  GaugeSegment(
                    from: 33.3,
                    to: 66.6,
                    color: Color(0xFFD9DEEB),
                    cornerRadius: Radius.zero,
                  ),
                  GaugeSegment(
                    from: 66.6,
                    to: 100,
                    color: Color(0xFFD9DEEB),
                    cornerRadius: Radius.zero,
                  ),
                ],
              ),
              //インジゲーターの下に表示される数字
              builder: (context, child, value) => RadialGaugeLabel(
                value: value,
                style: const TextStyle(
                  color: Colors.black,
                  fontSize: 46,
                  fontWeight: FontWeight.bold,
                ),
              ),
            ),
          ],
        ),
```


# 終わり
今回は以上になります！
今回も最後まで読んでいただき、ありがとうございました！
では、また次の記事で〜！
【Excel】表のナンバリングが崩れにくい！！OFFSET関数を使って保守性の高いファイル作成！

# 概要
皆さんは表とか作る際、左端にナンバーを振ってナンバリングとかしませんか？
IT業界で働いていると、テスト試験書だったり、QA表だったり、照会表だったり・・・
とにかく色々な所で表を作り、ナンバリングを行います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3618971/1507c758-d19a-d70a-8864-a504ebe176a1.png)

上記の様に作った後、行を挿入しないといけない・・・
なんてことありませんが？？
私は日常茶飯事です。。。

でも、普通に行挿入しちゃうと、下記の様に前のナンバーが残っていて、再度ナンバリングしないといけなくなり面倒くさいですよね？？
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3618971/ebfca031-47de-ff53-83b1-ff9ab46c099d.png)

そこで、エクセル関数を使うのですが、
```
=IF(ISNUMBER(A1),A1+1,1)
```
を使うと、最初の結果と同じになってしまいした。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3618971/f80c14e8-09e9-a5ba-e0dc-1eb33937f0fb.png)

```
=ROW()-1
```
を使うと、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3618971/bd5bd3e9-69b7-f8a7-e07d-10ef599c396e.png)
となり、行数で考慮されるため、今までの行が「４」以降に変わり、新企業に改めて「=ROW()-1」を入力すればよいですが、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3618971/2626d68b-b672-0e2c-d2a6-3f3736812971.png)
の様な歯抜けの番号をナンバリングするのには向きません。。。

# ナンバリングはこれで決まり！！
## 歯抜けも考慮したナンバリング
上記のような悩みを一挙に解決してくれるのが、この式です！！
```
=MAX(OFFSET(INDIRECT(ADDRESS(1,COLUMN())),0,0,ROW()-1,1))+1
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3618971/233bf208-01b5-7843-2261-c71c8b7e3032.png)
セルに数式を貼り付けるだけで、ナンバリングしてくれます！！
もちろん行を削除しても、自動で再度ナンバリングしてくれますので、超便利です！！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3618971/4d871488-8231-21d4-633d-8ccd57c3316b.png)


## 歯抜けの場合は「1」に戻したい場合のナンバリング
```
=N(OFFSET(INDIRECT(ADDRESS(ROW(),COLUMN())),-1,0))+1
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3618971/69f4f41f-6624-c511-753a-ed0636cb9c38.png)
連続した場合のみナンバリングを引き継ぎますので、歯抜けの場合は「1」からにしたい場合はこちらを使用しましょう！！


## 枝番を割り振りたい時
よく照会表とかQA表を作っていると、「1-1」とか「2-2」とか枝番管理をしたくなる時がありますよね？？
↓↓↓こんな風に↓↓↓
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3618971/93f31a09-342f-aa4c-febf-e8a6411e3978.png)

そんな時はこれを使いましょう。
``` 
#親番セルの関数
=MAX(OFFSET(INDIRECT(ADDRESS(1,COLUMN())),0,0,ROW()-1,1))+1
```
``` 
#枝番セルの関数
=IF(ISBLANK(INDIRECT(ADDRESS(ROW(),COLUMN()-1))),INDIRECT(ADDRESS(ROW()-1,COLUMN()))+1,IF((INDIRECT(ADDRESS(ROW()-1,COLUMN()-1))<>INDIRECT(ADDRESS(ROW(),COLUMN()-1))),1,IF((INDIRECT(ADDRESS(ROW()-1,COLUMN()-1))=INDIRECT(ADDRESS(ROW(),COLUMN()-1))),INDIRECT(ADDRESS(ROW()-1,COLUMN()))+1,"error")))
```
※親番の空白や、同じ数値のセルは、セルを空にしたり、リテラルを入力して対応する必要が御座います。


# 最後に
はい、ということで、今回は連番のナンバリングについて、まとめました。
意外と使用頻度高いと思いますので、良ければ「いいね」やブックマークをお願い致します。

最後になりますが、この記事が皆さんの成長の少しでも手助けになることを祈って、この記事を閉めたいと思います。
最後までお読みいただきありがとうございました。
Railsでのバリデーションの実装

# バリデーションとは
バリデーションは、データがアプリケーションの要件に適合していることを確認するための手段です。これにより、不正なデータがデータベースに保存されるのを防ぎます。例えば、ユーザー登録フォームでメールアドレスが正しい形式であることや、パスワードが一定の長さを満たしていることなどを確認するのが一般的です。
# Railsでのバリデーションの設定
Railsでは、モデルでバリデーションを設定するのが一般的です。以下に、Userモデルに対していくつかの基本的なバリデーションを設定する例を示します。
```ruby
class User < ApplicationRecord
  validates :name, presence: true
  validates :email, presence: true, uniqueness: true, format: { with: URI::MailTo::EMAIL_REGEXP }
  validates :password, length: { minimum: 6 }
end
```
* presence: true: 値が存在することを確認します。
* uniqueness: true: 値が一意であることを確認します。
* format: 値が指定された正規表現に一致することを確認します。
* length: 値の長さが指定された条件を満たすことを確認します。
# カスタムバリデーションの作成
場合によっては、組み込みのバリデーションだけでは不十分なことがあります。そんな時には、カスタムバリデーションを作成できます。
```ruby
class User < ApplicationRecord
  validate :password_complexity

  def password_complexity
    return if password =~ /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{6,}$/
    
    errors.add :password, "must include at least one lowercase letter, one uppercase letter, and one digit"
  end
end
```
この例では、パスワードが少なくとも1つの小文字、1つの大文字、1つの数字を含むことを確認するカスタムバリデーションを追加しています。
# バリデーションエラーメッセージの表示
バリデーションエラーが発生した場合、ユーザーに適切なフィードバックを提供することが重要です。Railsでは、errorsメソッドを使用してエラーメッセージを取得し、ビューで表示することができます。
```erb
<% if @user.errors.any? %>
  <div id="error_explanation">
    <h2><%= pluralize(@user.errors.count, "error") %> prohibited this user from being saved:</h2>
    <ul>
      <% @user.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
    </ul>
  </div>
<% end %>
```
# バリデーションエラーのデバッグ
バリデーションエラーが発生した場合、どのバリデーションが失敗したのかを特定することがデバッグの第一歩です。以下に、デバッグの手順を示します。
### 1. コンソールで確認
Railsコンソールを開き、手動でモデルにデータを保存してバリデーションエラーを確認します。
```shell
rails console
```
```ruby
user = User.new(name: "", email: "invalid", password: "short")
user.save # => false
user.errors.full_messages # => ["Name can't be blank", "Email is invalid", "Password is too short (minimum is 6 characters)"]
```
### 2. ログを確認
サーバーログを確認して、エラーメッセージやスタックトレースをチェックします。これにより、どのバリデーションが失敗したかを特定できます。
### 3. テストを書く
```ruby
require 'rails_helper'

RSpec.describe User, type: :model do
  it "is invalid without a name" do
    user = User.new(name: nil)
    user.valid?
    expect(user.errors[:name]).to include("can't be blank")
  end
end
```
# 最後に
この記事では、Railsアプリケーションでのバリデーションの基本的な設定方法と、カスタムバリデーションの作成方法、バリデーションエラーメッセージの表示方法、そしてバリデーションエラーのデバッグ方法について説明しました。バリデーションはデータの整合性を保つために非常に重要ですので、適切に設定し、デバッグ方法を理解しておくことが大切です。
# 参考
Railsガイド　バリデーション
https://railsguides.jp/active_record_validations.html
Javaで「指定した区切り文字で複数の文字列を連結し新しい文字列を作成する」の動作を確認してみた

# 概要

Javaで「指定した区切り文字で複数の文字列を連結し新しい文字列を作成する」の動作を確認してみました。
以下のページを参考にしました。

https://www.javadrive.jp/start/string/index17.html

# 実装

以下のファイルを作成しました。
```JSample17_1.java
import java.util.List;

class JSample17_1{
  public static void main(String[] args){
    String str1 = String.join("-", "Apple", "Grape", "Melon");
    System.out.println(str1);

    List<String> strings = List.of("One", "Two", "Three");
    String str2 = String.join(" * ", strings);
    System.out.println(str2);
  }
}
```

以下のコマンドを実行しました。
```
$ javac JSample17_1.java 
$ java JSample17_1 
Apple-Grape-Melon
One * Two * Three
```

# まとめ

何かの役に立てばと。
FVMをWindows環境に設定する

# FVMとは
複数の Flutter SDK バージョンを管理するための簡単、強力、柔軟なツールです。
https://fvm.app/
に詳細な内容が記載されています。

# FVMの使用例
複数のFlutterアプリを開発している場合やFlutterアプリを開発後それなりに期間が経っている場合、
アプリ毎やアプリのVersion毎でFlutter SDKのVersionが異なる事がしばしばあると思います。
ビルド時に必要なFlutter SDKのVersionを毎度切り替えるのは大変です。
各アプリやアプリの各Version毎にFVMを通じFlutter SDKのVersionを指定することで
ローカルのFlutter SDKのVersionを切り替えることなくビルド可能になります。

# FVMのインストール
## コマンドによるFVMのインストール

```
$ choco install fvm
```

## ファイルダウンロードによるFVMのインストール
https://github.com/leoafarias/fvm/releases
から対象のファイルをダウンロードします。

> 例：fvm-3.1.7-windows-x64.zip をダウンロード

ダウンロードしたファイルを任意の場所に展開する。

> 例：C:\Fvm\3.1.7\fvm

展開したFvmにパスを通します。

# FVMコマンドが使えるか確認
下記コマンドを打ち込んでインストールしたVersionの情報が表示されることを確認。

```
$ fvm -v
```

以上でFVMをWindows環境に設定出来るかと思います。
spring mvc。RestController。httpメソッドがpost またはputまたはpatch。bodyをnullにしたらエラーになった。curlでリクエストなげたら、curlコマンドでエラーになった。仕様？


Spring MVCにおいて、`@RestController`で定義されたエンドポイントに対してPOST、PUT、PATCHメソッドを使ってリクエストを送る場合、リクエストボディがnullであるとエラーになることがあります。これは通常の動作であり、Spring MVCの仕様の一部です。

## 詳細
#### HTTPメソッドとリクエストボディ:

POST, PUT, PATCHメソッドは、リクエストボディにデータを含むことを期待しています。
リクエストボディがnullの場合、Springはデフォルトでエラーをスローする可能性があります。
#### エラーハンドリング:

Spring MVCでは、リクエストボディが必要な場合に`@RequestBody`アノテーションを使用します。このアノテーションは、リクエストボディがnullであるとHttpMessageNotReadableExceptionをスローします。
#### curlコマンドの例:

正常なリクエストの例（JSONデータを含む場合）：
```bash
curl -X POST http://localhost:8080/api/example -H "Content-Type: application/json" -d '{"key":"value"}'
```

リクエストボディをnullにするとエラーになる例：
```bash
curl -X POST http://localhost:8080/api/example -H "Content-Type: application/json" -d ''
```
## 解決策
#### リクエストボディが必須でない場合:

エンドポイントメソッドの引数に`@RequestBody(required = false)`を指定します。これにより、リクエストボディがnullでもエラーが発生しなくなります。
```bash
@PostMapping("/example")
public ResponseEntity<String> handlePost(@RequestBody(required = false) MyRequestBody body) {
    if (body == null) {
        // ボディがnullの場合の処理
        return ResponseEntity.badRequest().body("Request body is missing");
    }
    // 正常な処理
    return ResponseEntity.ok("Success");
}
```[
#### ボディが必須である場合:

リクエストボディがnullでないことを前提にしたエラーメッセージを提供することができます。
```bash
@PostMapping("/example")
public ResponseEntity<String> handlePost(@RequestBody MyRequestBody body) {
    // ここにビジネスロジックを記述
    return ResponseEntity.ok("Success");
}
```
エラーメッセージやレスポンスを改善することで、クライアントが何が間違っているのかを理解しやすくなります。
SQLでJOINを使わず累計を出す方法(ウィンド関数)

# はじめに
売上などの累計を出すときにテーブルを自己結合してだしていましたが、ウィンドウ関数なるものを使えば簡単に出せると知ったので自分のメモとして残しておこうと思います。

# コード
```sql
-- ウィンド関数を使った方法
SELECT
	  date
	, uriage as '今日の売上'
	, SUM(uriage) OVER(ORDER BY date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) as '累計'
FROM 
	uriage_data
WHERE 
	date between '2023-01-01' and '2023-12-31'
ORDER BY 
	date;


-- JOINで実装する方法
SELECT
	  uriage_data_a.date,
	, MAX(uriage_data_a.uriage) as '今日の売上'
	, SUM(uriage_data_b.uriage)
FROM 
	uriage_data as uriage_data_a
LEFT JOIN 
	uriage_data as uriage_data_b
ON 
	uriage_data_a.date >= uriage_data_b.date
	AND uriage_data_b.date BETWEEN '2023-01-01' AND '2023-12-31'
WHERE 
	uriage_data_a.date between '2023-01-01' and '2023-12-31'
GROUP BY 
	uriage_data_a.date
ORDER BY 
	uriage_data_a.date;
```

ウィンド関数の方はdateでソートしたものに対して最初の行から現在の行までSUMして出力しています。
JOINの方は現在の日付以前のものをJOINしてSUMしています。

# おわりに
ウィンド関数を使えばJOINするテーブルが増えたとしてもシンプルにかけるので良いと思いました。
今回は累計を出しましたが他にもn行前を取得などできたり色々な使い方ができそうです。
ただ、自分自身SQLの研修で複雑なクエリを1000行近く書いた経験が現在もいきていると思っているので、SQL勉強中の方はまずはJOINに慣れて頭の中でもある程度クエリが想像できるようになってから楽するために使う方がいいのかなと思いました。(本当に個人的な意見です...)
ジンバルモーター、SimpleFOCで動かす(5) - 磁気エンコーダAS5600

# はじめに

[前回の](https://qiita.com/8ga3/items/ae252c0afe864eb960ae)の続きです。

今回使用した磁気エンコーダについてになります。

# AS5600

AliExpressでAS5600を購入しました。購入した理由は、SimpleFOCがデフォルトで対応しているからでした。

色々調べると、これを書いている時点では精度・入手性においてMT6701を買っても良さそうなので、後ほど試してみたいと思ってます。AS5600より少し高いくらいのようです。

購入したボードの写真です。

![IMG_6740.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3569302/607701c2-e3f2-69fc-3fd2-1c5f6d756389.jpeg)

# 回路図

どんな回路図になっているかわからないので、表面のパターンとドキュメントのリファレンス回路図を参照しながら、自分で回路図を書いてみました。

![AS5600-Circuit.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3569302/fab3c151-1560-0c3b-f4ca-3d305c19b5c1.png)

ドキュメントにはVDDが5Vのときと、3.3Vのときの回路が提示されています。

![AS5600-Ref.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3569302/e17ee90f-15e4-1e2c-9e64-965e0dd8d72e.png)

このボードはVDDが3.3Vになるように接続されていました。

# 5V対応に加工

私はArduino Uno R4 Minimaは5Vで使えると都合がいいわけです。

実は試しに3.3Vのところに、Minimaの5V出力を繋いでみました。壊れるということはなくI2Cで通信可能でした。ただやはりブートのタイミングによってはAS5600とI2Cで繋がらなくなりました。

やはりマニュアル通り5V設定に変えました。R1に0Ω抵抗がありますが、これを外すだけで5V設定になることがわかります。

チップ抵抗をはんだごて２本を使って、両端子に当てながらハンダを溶かして外しました。

![IMG_6741.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3569302/55de7091-b900-8c15-77a6-31cc0b0f0113.jpeg)

あとで余分なハンダを吸い取って、ヤニ汚れをきれいにして完了です。

動作確認し、問題なく動作しました。

# おわりに

販売ページに回路図を書いておいてくれれば助かるのになと思いました。このくらいならさほど苦労はないですが、ちょっと面倒です。

次回、電流センサーを追加してみたいと思います。

https://qiita.com/8ga3/items/859013d7fd19154372ec
VoiceOverの読み上げを意識したコンポーネントを作成しました

## きっかけ

4月に弊社が[アクセシビリティプロジェクト](https://blog.qiita.com/accessibility-project/)を立ち上げた事もあり、日々のプロダクトに対するアクセシビリティの課題に取り組んでおります。

https://qiita.com/gilly/items/18456a7812691c9491ad

上記の記事でも書かれていますが、`2024/7/5`のような日付は`にせんにじゅうよん すらっしゅ なな　すらっしゅ　ご`とVoiceOverに読まれてしまいます。

![VoiceOverの画像「2024/7/5」と書かれている](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3443/159df612-c51b-f15c-9c9d-08ee2f6817aa.png)

ではtimeタグにして`datetime`に正しい文字をいれてみてはどうかと考えました。

```html
<time datetime="2024年7月5日">2024/7/5</time>
```
それでもVoiceOverでの読み上げは`にせんにじゅうよん すらっしゅ なな　すらっしゅ　ご`です。

そこで「**aria-labelが使えないタグで読み上げを設定したいときの対処方法**」の記事にも書いてあった実装方法を応用したコンポーネントを作成してみました。

## 実際のコード


```ReadItem.tsx
import { css } from "@emotion/css" //css in jsはemotionを使っています
import { ReactNode, ElementType, AllHTMLAttributes } from "react"

interface Props extends AllHTMLAttributes<HTMLElement> {
  children: ReactNode
  readText: string
  tagType: ElementType
}

export const ReadItem = ({
  children,
  readText,
  tagType: Tag = 'span',
  ...props
}: Props) => {
  return (
    <>
      <Tag {...props} aria-hidden="true">
        {children}
      </Tag>
      <span css={hasAriaLabelStyle}>{readText}</span>
    </>
  )
}

const hasAriaLabelStyle = css({
  transform: 'scale(0,0)',
  position: 'absolute',
})
```

Propsに`children`と読み上げ用の`readText`、HTML要素も汎用的に変更できるように`tagType`を渡しています。
ではこのコンポーネントを使用して先程の日付を読み上げるように文章をつくってみます。

```App.tsx

import { ReadItem } from "./ReadText";

export default function App() {
  return (
    <div className="App">
      <p>
        <ReadItem
          tagType="time"
          readText="2024年7月24日"
          dateTime="2024年7月24日"
        >
          2024/7/24
        </ReadItem>
        は土用の丑の日
      </p>
    </div>
  );
}
```

実際のHTMLではこんな感じになっています。

```html
<p>
    <time datetime="2024年7月24日" aria-hidden="true">2024/7/24</time>
    <span class="css-1afofdy">2024年7月24日</span>
    は土用の丑の日
</p>
```

VoiceOverでも正しく読まれたようです。

![VoiceOverの画像「2024年7月24日は土用の丑の日」と書かれている](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3443/cb99d329-cfe7-9ea9-ce09-7fb7173e8d95.png)

実際に作成したdemoがこちらです。

https://codesandbox.io/embed/modest-meitner-vt2xj2?fontsize=14&hidenavigation=1&theme=dark
React での配列の並び替え

## React での配列の並び替え
本記事は以下記事での配列の並び替えについての続きです。

https://qiita.com/axoloto210/items/cb93a3aebc3521cace3f

## 直接配列を並び替えるメソッド
配列を並び替えるメソッドとして代表的なものに`sort()`や`reverse()`があります。

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/sort

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/reverse

これらのメソッドは配列要素を直接並び替えるため、`useState()`のset 関数に並び替えた後の配列を渡しても、「配列が変更された」とみなされず、画面に反映されません。

```ts
const array = [4, 5, 2, 3, 1]

const newArray = array.sort()

console.log(newArray)
// [ 1, 2, 3, 4, 5 ]

console.log(Object.is(array, newArray))
// true
```

`sort()`や`reverse()`によって配列を並び替えてからset 関数に渡したいときには、あらかじめ配列の複製を作成しておき、複製した配列を並び替えてから渡す必要があります。

```ts
const array = [4, 5, 2, 3, 1]

const newArray = [...array].sort()

console.log(newArray)
// [ 1, 2, 3, 4, 5 ]

console.log(Object.is(array, newArray))
// false
```
スプレッド構文`[...array]`によって、新しい配列を作成することで、並び替え後の配列と元の配列の参照は別のものとなっています。

## 新たに配列を返す並び替えメソッド
比較的新しいメソッドとして、並び替えた配列を新しい配列として返すメソッドが存在します。
TypeScript の設定ファイルの変更が必要だったり、古いブラウザでは動作しない場合があったりもしますが、新しい配列を返すメソッドについても整備されてきています。

https://devblogs.microsoft.com/typescript/announcing-typescript-5-2/#copying-array-methods

### `toSorted()`
`sort()`に対応するメソッドとして`toSorted()`が追加されています。

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/toSorted

```ts
const array = [4, 5, 2, 3, 1]

//@ts-ignore
const newArray = array.toSorted()

console.log(newArray)
// [ 1, 2, 3, 4, 5 ]

console.log(Object.is(array, newArray))
// false
```
`sort()`とは異なり、並び替え後の配列が元の配列`array`とは別の配列となっていることがわかります。

### `toReversed()`
`toReversed()`は`reverse()`に対応する、並び替え後の配列を新たな配列として返すメソッドです。

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/toReversed

## 余談:　`splice()`に対応するメソッド
並び替えを行うメソッドではないですが、配列へ直接要素の追加・削除・置き換えができるメソッドに`splice()`があります。
この`splice()`に対応する、新たな配列を返すメソッドとして`toSpliced()`というメソッドも追加されています。

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/toSpliced

配列を複製してから`sort()`や`reverse()`を使用するという方法がとられることが多いと思いますが、`toSorted()`や`toReversed()`というメソッドがあることを知っておくといずれどこかで役立てられるかもしれませんね。
使い慣れたLINEのUIでChatGptを使いたくて。。。。Make+ChatGptを使ってノーコードで質問に変態っぽく答えるLineBotを作った

# ChatGptをLineで使いたいな…
ブラウザからではなく、使い慣れたLineでChatGptを使えたら…
あらかじめ短文で指示しなくても回答が得られたら…
 そんなことを思い、ChatGptさん本人にきたり、ググってみたりして
 初めてLINEBOTをMAKEを使ってノーコードで作ってみました。

# 困った…①
Qiitaやnoteの記事を見ていると大体一年前に書かれた記事が多く、
MakeのUIや細かい表記が変わっていて、「本当にこれで出来るのか…？」と
手探りで作りました。

# 困った…②
また、困ったことが…ChatGptのAPIで返すトークン数が課金制になっており、遊びに使うにはハードルが高いな…転職して給料かなり減ったし…手をつけるのに三日ほど悩みました。

# 決意、いざ着手
　そして、7月9日22:00に意志を固め、着手し約90分ほどで完成に行きつきました！！その手順について簡単に各記事などを引用しつつ記述します。

# 下準備①　ベースとなるオウム返しBOTの作成
　https://note.com/nocode_dev/n/nb2d4e39eb25f
 ↑のnote記事を参考に基本となるオウム返しBOTを作成しました。

# 下準備②　ChatGptのAPIキーの準備と有料会員化
①APIキーの準備　
 https://qiita.com/kenichiro_ayaki/items/afff43e78e75b0ae8d8a
　↑このQiita記事を参考にAPIの準備をしました。
 　基本的に内容に変化はなく、スムーズに手配できました。

②有料会員化
　https://ai-market.jp/howto/chatgpt-api-cost/
 ↑この記事を参考にコストと利用までの手順を確認し、約10ドル、1600円ほど課金しました。
 
# ChatGpt APIをMAKEで使えるように！
　https://note.com/nocode_dev/n/n86af1c0d6ec2
 ↑この記事を元に作成しました。
 変更点としては①「Create a Completion」の表記が
![表記.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3820956/26035769-5fa2-516e-cee0-46d29873f116.png) 
 に変更されてました
 ②一番下に![Token.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3820956/801fab95-2ec3-5563-7440-7625e6b3ffbf.png)
入力＆出力のトークン数を記載する場所がありました
ここの変更点は要注意点だと思っています。

# ここが面白い
　Message Contentの欄で答えるBOTのキャラクターを詳細に設定できるところです。
 たとえば、「真面目に」と入力すれば…
 ![真面目.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3820956/1a4cb20a-fae3-e09a-d4b9-52c83718660e.png)
こんな風にまじめに答えますし、「ツンデレなお姉さまキャラ」や「おばあちゃんの知恵袋風」、「コギャルっぽく」などといった様々なキャラをたま～に変なキャラになりますが、なりきって返答を返してもらえます。
そこで私は変態なキャラクター…面白いかもと…
![LINE.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3820956/02d2e6f3-008f-4780-bd2d-9d02787def2b.png)
やってしまいましたww
いい感じに変態に答えてくれるBOTになりました。
ちょっとセクハラ気味な質問をすると変態度を増しで答えてくれるので、
いい玩具として完成しましたとさ。

# 最後に
今回、初めてということでnoteやQiitaなどの様々な記事をそのまま流用して作成しました。
若干、ChatGptの規約が変わり、APIを使って返答をもらうのに課金が必要となってしまい、若干の想定外の出費になってしまいましたが、いい勉強になりました。

# MAKEシナリオ全容
![全体①.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3820956/4a4ea5c0-aa13-a7e5-bbf2-4749a2a696e5.png)

![みぎ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3820956/47e8cfe7-623a-3756-0460-9d29c655ba34.png)
![真ん中①.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3820956/008490b7-14eb-445a-6dd7-7de2b519fe9c.png)
![真ん中２.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3820956/b4b11219-7f34-c58b-0a76-5cb5623eed05.png)
![ひだり.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3820956/fc833abb-ee79-326f-fb8f-e6d31c66bb91.png)


以上
【Swift】Dateを含むStructのencoder/decoder

次のようなStructを例にencoder/decoderを実装します。

```swift:Post.swift
struct Post {
    
    var id: String
    var text: String
    var createdAt: Date
    
    init(id: String, text: String, createdAt: Date) {
        self.id = id
        self.text = text
        self.createdAt = createdAt
    }
    
}
```

Swiftでは、`Date`型の変数を単純にencodeすると`timeIntervalSinceReferenceDate: TimeInterval`になります。
そのため、データベース等に保存する際はISO8601形式などの文字列に変換すると便利です。

```swift:Post.swift
extension Post: Codable {
    
    enum CodingKeys: String, CodingKey {
        case id
        case text = "text_data"
        case createdAt
    }
    
    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(id, forKey: .id)
        try container.encode(text, forKey: .text)
        let formatter = ISO8601DateFormatter()
        let dateString = formatter.string(from: createdAt)
        try container.encode(dateString, forKey: .createdAt)
    }
    
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.id = try container.decode(String.self, forKey: .id)
        self.text = try container.decode(String.self, forKey: .text)
        let dateString = try container.decode(String.self, forKey: .createdAt)
        let formatter = ISO8601DateFormatter()
        if let date = formatter.date(from: dateString) {
            self.createdAt = date
        } else {
            throw DecodingError.dataCorruptedError(forKey: .createdAt, in: container, debugDescription: "")
        }
    }
    
}
```

実際に使ってみましょう。
まずはencodeです。

```swift
func addPost() {
    let post = Post(id: "aaa", text: "Hello!", createdAt: Date())
    let encoded = try! JSONEncoder().encode(post)
    do {
        guard let jsonObject = try JSONSerialization.jsonObject(with: encoded, options: []) as? [String: Any] else { return }
        print(jsonObject)
    } catch {}
}
```

```
["createdAt": 2024-07-09T15:30:26Z, "text_data": Hello!, "id": aaa]
```

ちゃんとencodeできました。
次に、decodeもやってみましょう。

```swift
let jsonData = """
{
   "id": "aaa",
   "createdAt": "2024-07-09T15:02:51Z",
   "text_data": "Hello!"
}
"""

func fetchPost(){
    guard let data = jsonData.data(using: .utf8) else { return }
    do {
        let decoded = try JSONDecoder().decode(Post.self, from: data)
        print(decoded)
    } catch {}
}
```

```
Post(id: "aaa", text: "Hello!", createdAt: 2024-07-09 15:02:51 +0000)
```
受け取った`String`型の日付をdecodeして`Date`型にすることができました。
スケルトンコンポーネントの作り方 part2

:::note info
この記事は2部構成です。
[part1はこちら](https://qiita.com/shamokit/items/1bc56c213f5c3a859896)
:::

前回のpart1でほぼ終わっているのですが、stateを管理する`createLoading`関数とSkeletonコンポーネントを作成していないので作成していきましょう。

## createLoading
createLoading.svelte.tsというファイル名で以下を作成します。

```ts
import { getContext, hasContext, setContext } from 'svelte';

type Context = {
	state: boolean;
};

export const createLoading = (symbol: symbol) => {
	if (hasContext(symbol)) {
		return getContext<Context>(symbol);
	}
	let loading = $state(true);
	const rune = {
		get state() {
			return loading;
		},
		set state(v) {
			loading = v;
		}
	};
	setContext<Context>(symbol, rune);
	return rune;
};
export const getLoadingContext = (key: symbol) => getContext<Context>(key);
```

ひとつのローディンググループごとにユニークなキーを持たせてそれに関連するスケルトンの表示状態を一度に変更かつ他のローディングの状態に依存させたくないので、Symbolを使用します。

クソ便利ですね！

中身はざっくり説明するとContextにrunesを詰めて返しているだけです。

getLoadingContext関数を作成し、いちいちgetContextに型を渡さなくていいようにしています。

## Skeleton
Skeleton.svelteを以下で作成します。

```svelte
<script lang="ts">
	import type { Snippet } from 'svelte';
	import { createLoading } from './createLoading.svelte';

	let {
		key,
		time = 3000,
		children
	}: {
		key: symbol;
		time: number;
		children: Snippet;
	} = $props();

	let loading = createLoading(key);
	setTimeout(() => {
		loading.state = false;
	}, time);
</script>

{@render children()}
```

このコンポーネントの中身はやりたい実装に合わせて調整してください。

今回は要件にある
- スケルトンの内部でさらに別のスケルトンを表示する場合、同期されないようにしたい（スケルトンごとにグルーピングしたい）

これが確認できればそれでいいので単純にtime秒待ってから`loading.state`をfalse（loading終了）に書き換えています。

この例ではContextで下層のコンポーネントにstateを渡す関係でこういった微妙なコンポーネントが必要になってしまっていますが、実際はSymbolの管理や適切な命名によって気にならなくなると思います。
Windows10 システム環境変数の編集方法

# 注意点
- 「環境変数の編集」で起動するとシステム環境変数の編集ボタンが非活性になることがある

# How to
- システムを開き、検索ウィンドウに「システム環境変数の編集」と入力すればOK

![240710_システム環境変数の編集方法.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/222234/36d269ae-37be-1617-1834-5faa86294fc2.png)
Windows環境のNew Relic .net agent APMが反映されない

New Relic APMの学習目的で、開発マシンに.net agentをmsiファイルからインストールしたが、New Relicの画面にAPMデータが表示されない。。。

フォルダ「C:\ProgramData\New Relic\.NET Agent\Logs」に、ファイルも生成されておらず、権限の問題か？とも考えたが、特に問題なさそう。

調査した結果、環境変数に以下が設定されていないことが原因だった。

|環境変数名|値|
|--|--|
|CORECLR_ENABLE_PROFILING|1|

上記を設定することで、New Relicの画面にAPMデータが表示されるようになったので備忘録としてメモ。

開発マシンのAPMデータをNew Relicに反映させることができるようになったため、APM画面にどのように表示されるかということなどが、学習しやすくなった！！


なお、次のサイト
https://docs.newrelic.com/jp/docs/apm/agents/net-agent/configuration/net-agent-configuration/
に、
> Windows .NET エージェント インストーラーは、これらをデフォルトで IIS に追加するか、 Instrument Allを有効にしたときにシステム全体の環境変数として追加します。例外: IIS 以外でホストされている .NET Core アプリケーションを計測するには、 CORECLR_ENABLE_PROFILING を手動で設定する必要があります。.NET Framework アプリケーションの .NET Framework の互換性と要件を 参照してください。

という記載がある。

.net agentのインストールガイドラインは、New Relicの画面で紹介されるので、この情報もそこに記載してほしいと思ったこの頃。
Histoireがtailwind.config.jsのカスタムテーマを読み込んでくれない時に、読み込んでもらうための方法

Vue系列でも使える強力なStorybook代替「Histoire」ですが、2024年7月上旬現在、特定の条件を満たすとtailwind.config.jsのカスタムテーマを読み取ってくれないというバグがあります。

https://histoire.dev/guide/vue3/stories.html

https://github.com/histoire-dev/histoire/issues/667

せっかくHistoireはTailwind CSSのデザインシステムをデフォルトでカタログ内に列挙してくれるという素晴らしい機能があるのに、これでは勿体ないですよね。

このためだけに別途tailwind config viewerを用意するのもなんだか無駄に手間をかけることになってしまうし...

そういうわけなので、この **「なぜかHistoireがtailwind.config.js（あるいはtailwind.config.ts）を読み取ってくれないバグ」** の修正方法があったので、ここに記録しておきます。

## 修正方法

下記プルリクにそのものズバリが書いてあります。

https://github.com/histoire-dev/histoire/pull/723

> When using ESMs export default instead of module.exports in tailwind.config.{js,ts} the tailwindConfig is nested in a default key which then incorrectly merges with tailwinds default config like this:
> ![333889625-e07e099e-0f55-4a5a-8ac1-b64328ad96a6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/267156/0ba70248-4871-c197-a1e5-d9164d1c7d03.png)

というわけなので、冒頭にある通り結論は下記です。

- tailwind.config.jsのexportが `export default {...}` だと動かないよ
- **`module.exports = {...}` だと動くよ**

筆者も実際に試してみたところ、ちゃんとTailwind CSSのカスタムテーマが読み込まれるようになりました。これで別でtailwind config viewerを立ち上げたりしなくて済むし、チームメンバーを誤解させることもなくなる。やったー

上記のプルリクが最新版Histoireに反映されたら `export default {...}` でも問題なく動作するようになると思いますが、ひとまずそれまではこれで回避しましょう

## 著者紹介

沖縄でなんか色々作ってるWebエンジニアです。最近はAWSの次世代版サーバレス開発キット「SST Ion」激推しの人になっています。得意なフロントエンドはReact系列よりVue系列で、Nuxt3はプレビュー版の頃からプロダクトに使うなどしていたので愛着があります（プレビュー版の頃は、AWS SDKとの噛み合わせが悪かったりとたくさん苦しんだ思い出がある）。

日報SaaS「RevisNote」の運営もやってます。社内SNSの代わりにもなるので、興味があればぜひ社内に導入してみてください。随時機能更新中です。

https://revisnote.com/
【.gitattributes+liguist】githubリポジトリの言語統計情報をハックする

github repositoryのプログラミング言語情報が認識されないことがあり、その際に.gitattributeとlinguistを使って解決できたので、ついでに他も色々調べてみました。

以下のようなリポジトリの言語統計情報は[List repository languages API](https://docs.github.com/ja/rest/repos/repos?apiVersion=2022-11-28#list-repository-languages)によって各言語のコードバイト数に基づき算出されるらしい。
![スクリーンショット 2024-07-09 23.56.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1640807/97bfe743-4d3b-41cc-3bf3-2656a1021053.png)

github : https://github.com/github-linguist/linguist/tree/master

### 書き方の前提
```.gitattributes
// 有効化
{path} linguist-{name}
// or
{path} linguist-{name}=true

// 無効化
{path} -linguist-{name}
// or
{path} linguist-{name}=false
```
{path}は.gitattributesファイルからの相対パスで対象のディレクトリやファイルを指定

## linguist-language
```.gitattribute
*.rb linguist-language=Ruby

*.es linguist-language=js
*.es linguist-language=JS
```
リポジトリに存在するファイルに対する言語情報を明示的にオーバーライドする
指定できるのは[languages.yml](https://github.com/github-linguist/linguist/blob/master/lib/linguist/languages.yml)に定義されているもの。
(意図せず認識されない場合はこれで解決することも)

## linguist-detectable
```.gitattributes
# 有効化
*.sch linguist-detectable

*.hoge linguist-language=HogeLang linguist-detectable

# 無効化
tools/export_bom.py -linguist-detectable
```
デフォルトで認識されるのは[languages.yml](https://github.com/github-linguist/linguist/blob/master/lib/linguist/languages.yml)で定義されている中のtypeが`programing` or `markup`のものだけなので、設定を更新したい場合は上記のように指定する。
`*.hoge linguist-language=HogeLang linguist-detectable`のように`linguist-language`と組み合わせてオリジナルの情報を加えることも可能。

## linguist-documentation
```.gitattributes
# 有効化
docs/* linguist-documentation

# 無効化
docs/formatter.rb -linguist-documentation
```
デフォルトで[documentation.yml](https://github.com/github-linguist/linguist/blob/master/lib/linguist/documentation.yml)に定義されているドキュメント用ディレクトリは言語統計から除外されているが、設定を更新したい場合は上記のように指定する。

## linguist-vendored
```.gitattributes
# 有効化
path/to/vender/* linguist-vendored

# 無効化
node_modules/* -linguist-vendored
```
jsライブラリなどの第三者から提供されたコードはデフォルトで[vender.yml](https://github.com/github-linguist/linguist/blob/master/lib/linguist/vendor.yml)に定義されており、言語統計から除外されているが、設定を更新したい場合は上記のように指定する。

## linguist-generated
```.gitattributes
*.gen.dart linguist-generated

*.min.js -linguist-generated
```
コンパイルなどで自動生成されたファイルはデフォルトで[generated.rb](https://github.com/github-linguist/linguist/blob/master/lib/linguist/generated.rb)で定義されており、言語統計かつdiffから除外されるが、設定を更新したい場合は上記のように指定する
diffが多すぎるけど.gitignoreに指定するのはなんか違う場合とかに使える
【PostgreSQL】メタコマンド〜\set〜

## 初めに

Qiita Engineer Festa 2024に参加中。完走目指してます。
他のメタコマンドについては以下から読んでください。

https://qiita.com/dai_chi/items/04f5339d54130f070671

## \setとは

> psqlの変数`name`を`value`、または複数のvalueが与えられた場合はそれらを連結したものに設定します。 第一引数しか指定されない場合は、変数に空文字列の値が設定されます。 変数を未設定とするには、`\unset`コマンドを使用してください。
> 
> 
> 引数をまったく取らない`\set`は、現在設定されているpsql変数すべての名前と値を表示します。
> 
> 変数名には、文字、数字、アンダースコアを使用することができます。 詳細は、後述の[変数](https://www.postgresql.jp/docs/15/app-psql.html#APP-PSQL-VARIABLES)を参照してください。 変数名は大文字小文字を区別します。
> 
> psqlの動作を制御する、あるいは接続状態を表す値に自動的に設定される、という点で特別な変数がいくつかあります。
> 

[\setについて](https://www.postgresql.jp/docs/15/app-psql.html#:~:text=%5Cset%20%5B%20name%20%5B%20value%20%5B%20...%20%5D%20%5D%20%5D)

## 試してみた

```sql
postgres=# \set myvar 'Alice'
postgres=# select * from employees where name = :'myvar';
 id | name  | department | salary 
----+-------+------------+--------
  1 | Alice | HR         |  60000
(1 行)
```

## まとめ

手動実行するときにバインド変数として活用しています。
任意サイズ以上JSON作成python（調整しやすい）

```python
import json
import os

# 6MB以上のJSONファイルを生成する
def generate_large_json(filename, target_size_mb):
    data_list = []
    buffer_size = 10000  # バッファサイズを大きめに設定

    # 初期データを生成
    data = {
        "id": 0,
        "name": "example_name_0",
        "value": "example_value_0",
        "description": "This is a sample description to ensure the JSON file is large enough."
    }

    # ファイルを開く
    with open(filename, 'w', encoding='utf-8') as f:
        f.write('[')
        while os.path.getsize(filename) < target_size_mb * 1024 * 1024:
            for i in range(buffer_size):
                data["id"] += 1
                data["name"] = "example_name_" + str(data["id"])
                data["value"] = "example_value_" + str(data["id"])
                data_list.append(data.copy())

            # バッファをファイルに書き出し
            if data["id"] != buffer_size:
                f.write(',\n')
            f.write(',\n'.join(json.dumps(d, ensure_ascii=False) for d in data_list))
            data_list = []

        f.write(']')

# 6MB以上のファイルを生成
generate_large_json('large_data.json', 6)

# ファイルサイズを確認
file_size = os.path.getsize('large_data.json')
print(f"Generated JSON file size: {file_size / (1024 * 1024):.2f} MB")

```
ubuntu22 gdm3のオートログイン

# 背景

先日、Ubuntu 22にアップデートしました。Sunshineを使ってMoonlight経由でリモートデスクトップ環境を構築しようと考え、設定を進めました。最終的にうまく動作するところまで設定を完了しましたが、さらにオートログインを設定したいと思い立ち、設定を開始しました。

多くの情報では、以下の設定を保存して再起動する方法が紹介されています。

```/etc/gdm3/custom.conf
[daemon]
AutomaticLoginEnable = true
AutomaticLogin = user-name-here
```

# 問題発生
設定方法は正しいはずなのに、ログイン画面が表示され続ける問題に悩まされました。何度もログインを試みても、再びログイン画面に戻ってしまう現象で、一日ほど時間を費やしました。

# 実際の原因
実は設定は正しかったのですが、再起動直後にはオートログインが機能していたものの、操作がないために一定時間経過後にロックされてしまっていました。AutomaticLoginは再起動時の自動ログインのみを指しており、ロック後の自動ログインは含まれていません。

# 解決方法
GUIを使って以下の設定を行い、Automatic Screen LockとLock Screen on Suspendをオフにすることで、問題を解決できました。現在のところ、この設定でうまく動作しています。

![Screenshot 2024-07-09 at 23.36.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96637/2172cdbb-b2ef-5e7c-e5f3-9104497cac3d.png)

# 注意点
私自身、AutomaticLoginを「毎回自動ログインしてくれる」と誤解していたため、混乱しました。AutomaticLoginOnBootのような変数名であれば、より早く問題に気付けたかもしれません。

そもそも再起動時にもログインできない場合は、他の原因が考えられるため、その際は別の対策が必要かもしれません。
【Drupal】entityを定義した時のルーティング設定

Drupalのルーティングで発見があったのでメモとして残す。

# 現在まで書いていた書き方
```yml:member.routing.yml

entity.member.canonical:
  path: '/admin/member/{member}'
  defaults:
    _entity_view: 'member.full'
    _title: 'Member'
  requirements:
    _permission: 'administer site configuration'

entity.member.edit_form:
  path: '/admin/member/{member}/edit'
  defaults:
    _entity_form: 'member.edit'
    _title: 'Edit Member'
  requirements:
    _permission: 'administer site configuration'
# 削除や編集など続くルーティングを記述
```

当初よくある、crudのルーティングをrouting.ymlに記述していた。
ただ、ContentEntityBaseを継承したエンティを作成する場合、必ず？linksにurlを記述しないと行けないので
どちらにもurlを記述しないといけなく面倒だなと思っていた。

# 簡略化
```php:Member.php
<?php

namespace Drupal\member\Entity;

use Drupal\Core\Entity\ContentEntityBase;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\Entity\EntityTypeInterface;

/**
 * Defines the Member entity.
 *
 * @ContentEntityType(
 *   id = "member",
 *   label = @Translation("Member"),
 *   base_table = "members",
 *   entity_keys = {
 *     "id" = "id",
 *     "label" = "name"
 *   },
 *   handlers = {
 *     "view_builder" = "Drupal\Core\Entity\EntityViewBuilder",
 *     "list_builder" = "Drupal\member\MemberListBuilder",
 *     "views_data" = "Drupal\member\MemberViewsData",
 * 
 *     "form" = {
 *       "default" = "Drupal\member\Form\MemberForm",
 *       "add" = "Drupal\member\Form\MemberForm",
 *       "edit" = "Drupal\member\Form\MemberForm",
 *       "delete" = "Drupal\Core\Entity\EntityDeleteForm"
 *     },
 *     "route_provider" = {
 *       "html" = "Drupal\Core\Entity\Routing\AdminHtmlRouteProvider",
 *     }
 *   },
 *   admin_permission = "administer site configuration",
 *   links = {
 *     "canonical" = "/admin/member/{member}",
 *     "edit-form" = "/admin/member/{member}/edit",
 *     "delete-form" = "/admin/member/{member}/delete",
 *     "collection" = "/admin/member",
 *     "add-form" = "/admin/member/add"
 *   },
 * )
 */
class Member extends ContentEntityBase
{
}
```

Entityにroute_providerを指定することで、自動的に設定してくれる様になる。
よくあるcrudのルーティングに関してはDrupalのコアファイルにAdminHtmlRouteProviderを指定するだけで、自動的にurlを設定してくるのでrouting.yamlを記述しなくて良くなる

逆にrouting.yamlを使う場合はEntityを使わずにControllerBaseから継承したControllerから何かしたい場合は記述する方法しか無いと思う。
eccubeとかなら、@ Routea アノテーションを使えたりするけど、drupalには無いと思う。
Azure Container Appsにおける基本的なネットワークの概念を解説

# はじめに
3回に分けてAzure Container Apps（以下、ACA）の内部ネットワーク構造に注目し、深堀りしていきます。
以下の流れで説明していきます。

第1回目：Azure Container Appsにおける基本的なネットワークの概念を解説
第2回目：Azure Container Appsにおいて選択できるネットワークオプションとパラメータを解説
第3回目：Azure Container Appsにおけるパターンごとのネットワーク構成図を解説

今回は第1回目で、**ACAにおける基本的なネットワークの概念**について解説していきます。

# ACAとは

- 2022年5月にGAした、**コンテナベースのアプリケーション**を簡単にデプロイ、管理、スケールできるサービス
- フルマネージドk8sベースのアプリケーションプラットフォーム
- k8sが提供する高い可用性、セキュリティ、スケーラビリティの恩恵を受けつつ、**k8sの管理は不要**
- KEDA、Dapr、EnvoyといったCNCFプロジェクトの成果物をマネージドサービスとして提供する

# 用語

### Azure Container Apps Environment
Azure Container Apps Environment（以降、ACAE）は、ACAのインスタンスをホストするための論理的な境界。
ACAEは、アプリケーションが実行されるネットワーク、セキュリティ、およびリソースの設定を提供する。
ACAE内のアプリケーションは、同じリージョン内で物理的に近い場所に配置され、低遅延の通信を実現する。
ちなみに、Azure portalから作成する場合、ACAEの画面からACAEのみを新規作成することはできない（2024年7月初時点）。なので、ACAの作成画面にてACAEの「新規作成」を選択することでACAEの設定が可能になる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2475719/6158fdb9-481c-d7ec-4c1a-fa16ce7eaa9e.png)


### Revision
ACAの特定のバージョン（断面）のこと。
構成管理のための概念。

その他、ACAの基礎知識は以下の記事を参考にしてください。

https://qiita.com/maplejava/items/22a4b431d70e092b7d68

# ネットワークについて整理しようと思ったきっかけ

https://learn.microsoft.com/ja-jp/azure/container-apps/networking?tabs=workload-profiles-env%2Cazure-cli

以下のMSLearnの通り、ACAEとACAのネットワークオプションによって内部的なネットワーク構成やアプリの公開範囲が異なります。そこで、オプションごとのネットワーク構成の違いについて整理したと思ったのがきっかけです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2475719/94641d13-f5b9-214d-679c-0ce427c93c31.png)

なお、以降は主にACA外との通信に使用するイングレスやロードバランサに焦点を当てていますが、マイクロサービス間の通信について興味のある方はDaprについても調べてみて下さい。

https://dapr.io/

# ACAの基本的なネットワーク構成
図にすると以下のようなイメージです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2475719/c97597e1-e4ac-6aca-de0b-1c5bbe86d14e.png)

### ポイント
1. VNet統合していない場合は、**VNetはユーザから非可視となる。** つまり、VNet統合しないとVNetに関して何か設定することは一切できない
1. イングレス（Envoy）は、**ACAと同時に作成され、ACA内のコンテナ間で通信を負荷分散するコンポーネント。** 実態としてはACA内に存在するPodの一つ
1. ロードバランサは、**ACAEと同時に作成され、ACAE内のACA間で通信を負荷分散するAzureリソース**

# 「イングレス」と「ロードバランサ」の違い
イングレスもロードバランサもどちらも負荷分散に関するコンポーネントですが、上記のポイントの通り役割が異なります。
また、ロードバランサはACAEをパブリック設定にした場合は1つ作成されますが、プライベート設定にした場合、インバウンド通信用とアウトバウンド用で2つ作成されるという違いがあります。

まとめると以下の通りです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2475719/bd0b830d-559d-cbec-a58e-dc9a68051f59.png)

# 「イングレス エンドポイント」と「アプリケーションURL」の違い
上記に関連して混同しやすい概念を説明します。
### イングレス エンドポイント
例えば「/api」パスを指定するなど、アプリケーションの中でもイングレスによるルーティングが必要な、特定のサービスにアクセスするために利用する。
例）https://azfooaca-aca01--s2nydbk.redgrass-bb786982.japaneast.azurecontainerapps.io

### アプリケーションURL
application URLはACAのアプリケーション全体にアクセスするために利用するURL。
例）https://azfooaca-aca01.redgrass-bb786982.japaneast.azurecontainerapps.io

### 使い分け
バックエンドのアプリ間で処理をする場合や特定のサービスにアクセスする場合は「イングレス エンドポイント」を使う。
ユーザ等がフロントエンドにアクセスする場合には「アプリケーションURL」を使う。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2475719/3ad670ef-2f57-2486-306e-f989de5325ad.png)


# おわりに
ACAの基本的なネットワークの概念についてまとめてみました。
次回はACAにおいて選択できるネットワークオプションとパラメータの意味について解説していきます。
サーブレットからJSPに値を渡す例

サーブレットが起動されたときに自動的に実行されるメソッドとして、init メソッドを使用します。このメソッド内でインスタンスを作成し、それをJSPに渡すための方法を示します。以下はそのJavaプログラムの例です。

サーブレットクラス
java
コードをコピーする
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.RequestDispatcher;
import java.io.IOException;

@WebServlet("/myServlet")
public class MyServlet extends HttpServlet {
    private MyClass instanceA;

    @Override
    public void init() throws ServletException {
        // インスタンスAを作成
        instanceA = new MyClass();
        // 必要な初期化処理を行う
        instanceA.setSomeData("Initialized Data");
    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // インスタンスAをリクエスト属性に設定
        request.setAttribute("instanceA", instanceA);
        
        // JSPにフォワード
        RequestDispatcher dispatcher = request.getRequestDispatcher("myPage.jsp");
        dispatcher.forward(request, response);
    }
}
MyClass クラス
java
コードをコピーする
public class MyClass {
    private String someData;

    public String getSomeData() {
        return someData;
    }

    public void setSomeData(String someData) {
        this.someData = someData;
    }
}
JSP ファイル (myPage.jsp)
jsp
コードをコピーする
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html>
<head>
    <title>My Page</title>
</head>
<body>
<%
    MyClass instanceA = (MyClass) request.getAttribute("instanceA");
    if (instanceA != null) {
%>
        <p>Data from instanceA: <%= instanceA.getSomeData() %></p>
<%
    } else {
%>
        <p>InstanceA is not available.</p>
<%
    }
%>
</body>
</html>
説明
サーブレットクラス (MyServlet):

init メソッド: サーブレットが初期化されるときに自動的に呼び出され、ここでインスタンスA (MyClass オブジェクト) を作成し、必要なデータを設定します。
doGet メソッド: クライアントからのGETリクエストに応答します。request オブジェクトの属性にインスタンスAを設定し、JSPにフォワードします。
MyClass クラス:

インスタンスが持つデータフィールドとそのゲッター・セッターを定義します。
JSP ファイル (myPage.jsp):

サーブレットから渡されたインスタンスAを取得し、そのデータを表示します。
このプログラムにより、サーブレットが起動されるとインスタンスAが作成され、GETリクエストが来たときにJSPに渡されて表示されるようになります。
ポップアップ選択からディレクトリ移動：fzf と enhancd の設定

# 概要

シェル操作時にショートカットキーから、移動先のディレクトリをポップアップ選択して移動したい。

使用ツール、環境は以下の通り。

- fish
- enhancd
- fzf

# 設定方法

`fzf-tmux` に `-p` オプション指定して実行することで、選択画面をターミナル上にポップアップさせることができる。これをエイリアスに登録。

```shell:alias.fish
alias fzf-tmux "fzf-tmux -p 80%"
```

ショートカットキー(ここでは `Ctrl + ]` )で `enhancd` を実行設定する。環境変数 `ENHANCD_FILTER` に `fzf-tmux`を指定すること。


```shell:config.fish
function fish_user_key_bindings
    bind \c] "ENHANCD_FILTER='fzf-tmux:fzf' cd; commandline -f repaint"
end
```

# 実行

![スクリーンショット 2024-07-09 23.49.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/57640/e8132d86-9f1c-bb27-be42-92bc448e9107.png)
Docker環境で"The engine "node" is incompatible with this module. Expected version ~"のエラーを解消する

初心者です。Docker+Rails6+Nuxt.jsで認証機能付きのポートフォリオサイトを作ろうとしていたある日のことです。
Nuxt.jsアプリを動かすためのDockerimageが突然ビルドできなくなり、以下のようなエラーがコンソールに出力されていました。
初心者なのでエラー文と仲良くする習慣づけのために備忘録残しておきます。
(それにしても急に出たのはなんでだろう、タイミングよくサポートするバージョンが変わったんだろうか)

## エラー文
動いてた時代のDockerFileのままdocker buildしてみたらこんなエラーが出ました。
```zsh
126.4 error cssnano@7.0.3: The engine "node" is incompatible with this module. Expected version "^18.12.0 || ^20.9.0 || >=22.0". Got "16.13.1"
126.5 error Found incompatible module.
126.5 info Visit https://yarnpkg.com/en/docs/cli/install for documentation about this command.
```
nodeのバージョンが悪いって言われてる...?今まで文句言わなかったくせに...

## 環境情報
環境はこんな感じ
```
OS:MacOS 14.1.1（Sonoma）
```
DockerFileにはこんな感じでAlpineベースのnodeを指定していました。
```Dockerfile
FROM node:16.13.1-alpine
```
## 念の為dockerコンテナ上のnodeのバージョンを確認
```
$ docker-compose run --rm front node -v
v16.13.1
```
当たり前に自分が指定したバージョンで動いてました。怖いからやりたくないけど言われた通りにnodeのバージョン上げるしかないのね

## バージョンアップ先のnodeを決めよう
```
Expected version "^18.12.0 || ^20.9.0 || >=22.0". Got "16.13.1"
```
お前は16.13.1とかいう古いバージョン使いすぎてるから最低でも18.12.０とかにしなさいよ、ということか。
素直なので一番今のバージョンに近い18.12.0にしてみることにする。

:::note info
ちなみにNode.jsの16系のサポートは２０２３年にとっくに切れていた。なんで環境構築初期は動いてたんだろう。わかりません
> [Node.jsのリリースノート](https://nodejs.org/en/about/previous-releases)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3629712/d6fe72c0-b4cd-b7ef-fd47-21d5254cd5c7.png)
:::

## nodeの新しいイメージのタグを探しに行く
DockerFileで指定するベースイメージとなるnodeのタグを探しに行く。
指定する用のタグはdockerhubで確認できる。

https://hub.docker.com/_/node/

こんな感じで欲しいバージョンのタグを探せる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3629712/0627e2d1-a653-3be3-1ed5-31a8f594358c.png)

自分は今回AlpineLinuxのイメージを使うことに。
お目当てのタグを控えます＞`18.12.0-alpine`
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3629712/9d578498-3813-edc7-0098-a903b2c99de6.png)

:::note info
#### ところでAlpineLinuxって何？
非常に軽量なLinuxディストリビューションの一つで、muslとBusyBoxがベースのなっている。
libcに一般的な互換性が不足している等欠点もあるので、導入時は気をつけたい、けどDockerイメージの軽量化に役立つので今回の勉強用の開発環境にはちょうど良かったです。
:::

### DockerFileを修正しよう
今まで指定していた古いバージョンの記載を消して、dockerhubから連れてきた新しいバージョンを指定してやります。
```DockerFile
#FROM node:16.13.1-alpine //コメントアウト
FROM node:18.12.0-alpine　 //新規追加
```
### ビルドし直してみる！
DockerFileで指定した新しいNodeのバージョンでビルドし直してみる。
```zsh
$ docker-compose build
[+] Building 467.9s (24/24) FINISHED
```
せ、成功だ〜〜〜〜〜〜〜！！！！！！！

### 結論
普通にエラー文読もう
特定のVMをコピー，削除するシェルスクリプト

## はじめに
ESXiホスト上で特定のVMを別のディレクトリにコピーし，元のVMを削除するシェルスクリプトを紹介します．このスクリプトは，システム管理者がVMの管理を効率的に行うために役立ちます．


### 必要なツール
・ESXiホスト
・シェルスクリプト

#### シェルスクリプトの概要
このスクリプトは，以下の手順で特定のVMを処理します．

1.　指定されたVMの電源状態を確認し，必要に応じて電源をオフにします．
2.　VMのディレクトリをコピー先にコピーします．
3.　コピーが完了したらコピー元のVMを登録解除して削除します．



#### スクリプト
以下は，特定のVMを処理するシェルスクリプトです．
```
#!/bin/bash

# 引数の確認
if [ $# -ne 1 ]; then
  echo "使用法: $0 <VM名>"
  exit 1
fi

# VM名の取得
VM_NAME=$1

# コピー先ディレクトリ
COPY_DEST="コピー先ディレクトリ指定"
VM_SOURCE_DIR="コピー元ディレクトリ指定"

echo "VMの処理中: $VM_NAME"

# VMの状態をチェック
VM_ID=$(vim-cmd vmsvc/getallvms | grep -w "$VM_NAME" | awk '{print $1}')
if [[ -z "$VM_ID" ]]; then
  echo "VMが見つかりません: $VM_NAME"
  exit 1
fi

POWER_STATE=$(vim-cmd vmsvc/power.getstate $VM_ID | tail -1)
if [[ "$POWER_STATE" == "Powered on" ]]; then
  echo "VMが起動しているため,今から電源を切断します: $VM_NAME"
  vim-cmd vmsvc/power.off $VM_ID
  if [[ $? -ne 0 ]]; then
    echo "VMの電源をオフにできませんでした: $VM_NAME"
    exit 1
  fi
fi

# VMディレクトリをコピー先にコピー
VM_DIR="$VM_SOURCE_DIR/$VM_NAME"
DEST_DIR="$COPY_DEST/$VM_NAME"
echo "コピー中 $VM_DIR から $DEST_DIR へ"
cp -r $VM_DIR $DEST_DIR
if [[ $? -ne 0 ]]; then
  echo "VMディレクトリをコピーできませんでした: $VM_NAME"
  exit 1
fi

echo "正常にコピーされました: $VM_NAME"

# コピー元VMを削除する
echo "コピー元のVMを削除中: $VM_NAME"
rm -rf $VM_DIR
if [[ $? -ne 0 ]]; then
  echo "VMを削除できませんでした: $VM_NAME"
  exit 1
fi

echo "VMのコピーと削除が完了しました。"

```


### 使い方
スクリプトをファイルに保存します．例えば，vm_copy.shという名前にします．

:::note warn
注意　これをしないと実行できません．
実行権限を付与します：
```
chmod +x vm_copy.sh
```
:::
 
VM名を引数にしてスクリプトを実行します：
```
./vm_copy.sh VM名
```

#### 実行結果

```
VMの処理中: exampleVM
VMが起動しているため,今から電源を切断します: exampleVM
Powering off VM: exampleVM
コピー中 /vmfs/volumes/datastore1/exampleVM から /backup/exampleVM へ
正常にコピーされました: exampleVM
コピー元のVMを削除中: exampleVM
VMのコピーと削除が完了しました。
```
exampleVMというVMを処理を行った場合このように実行されます．

### まとめ
このシェルスクリプトを使うことで，ESXiホスト上の特定のVMを効率的に管理できます．不要になった元のVMを自動で削除することで，ストレージの最適化にも貢献します．
AIZU ONLINE JUDGEの提出前コードをVSCodeでテストする


アルゴリズムの勉強のため、AIZU ONLINE JUDGEを始めて使おうとしたら、提出前コードをVSCodeでテストするのに標準入力の受け取り方が分からなかったので、メモ。

## 環境
Mac
zsh

### プログラム
ファイル名(実行例): main.js
```javascript
function Main(input) {
  const data = input.split("\n");

  《略》
  console.log(hoge);
}

Main(require("fs").readFileSync("/dev/stdin", "utf8"));
```

※標準入力のコードは　https://yukicoder.me/wiki/stdio を参考にしました。


### 実行と標準入力の受け取り
```zsh
node main.js <<EOF
1 2
3 4
EOF
```
EOFで囲んだ内部に標準入力の内容を入力したりコピペする。
（他の方法でもやったのですが、入力値以外の文字情報もなぜか受け取ってしまってエラーになることがありました）

### スクショ
成功例
![成功例](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/435274/d977f274-00e4-0c74-5ad5-53c73131f58b.png)

失敗例
![失敗例](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/435274/5aaa6f34-e8e0-8c0c-41a4-9bc8da571b55.png)
入力文字(input変数)に余計な文字列が入ってしまっている。

## 参考
https://yukicoder.me/wiki/stdio
https://qiita.com/take4s5i/items/e207cee4fb04385a9952
過去のXcodeをインストールしたい

## 初めに
本記事は古いmacOSに対応したXcodeをインストールする方法を書いてみました。
自身の備忘録的に書いてますので読みづらいため参考までに

## こんな状況ですか？
アプリ開発勉強したい・・・！
そうだ学生時代に使っていたXcodeでやるぞ〜

![スクリーンショット 2024-07-09 22.46.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3834613/940251e3-fadb-183b-48b0-e4c30ccb2c5a.png)

え・・・！？
どうも私のMacbookPro(2017年購入)はソフトウェア更新してもmacOSがMontereyバージョン12.X.Xという感じで画像の要求である「macOS14.0」よりアップデートできないことが発覚しました。

## 解決策
色々調べてみたところ, 過去のXcodeをインストールすることができるとのこと。
まずは自分のmacOSに対応したXcodeを調べることに下記のページから検索できます。
https://developer.apple.com/jp/support/xcode/
↓ページ一部抜粋
![スクリーンショット 2024-07-09 22.55.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3834613/812b7f9c-3f46-e5d7-e213-9bb9b19c9c8b.png)
あったぞ, どうやらXcode14.0が対応バージョンのようだ

ここで過去のXcodeバージョンのデータが下記ページからインストールできるらしい
https://developer.apple.com/download/all/
↓ページ一部抜粋
![スクリーンショット 2024-07-09 22.58.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3834613/0db396a1-28f5-6cf8-f046-1f476f5072b1.png)

あった！ここの「Xcode 14.xip」をクリックするとインストーラーのダウンロードができるぞ

![スクリーンショット 2024-07-09 23.01.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3834613/a20f5352-2232-13b2-d071-ac0078a5e9f7.png)
~~※展開なっが・・・・~~ 展開にはかなり時間がかかります, 覚悟してください。

無事展開できた様子
![スクリーンショット 2024-07-09 23.20.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3834613/98fe6d53-f6ec-4a0e-347a-815728774e92.png)

起動すると・・・・・
同意書がこんにちは, 問題なければagreeしちゃって下さい。
![スクリーンショット 2024-07-09 23.22.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3834613/22df874f-d8ef-b108-3639-b58de37a59a9.png)

agreeを押すと, PC自体のパスワードを聞かれるかも

おおお, ようやくインストールできそうだ。
必要そうなデバイスをクリックして「Install！！」
![スクリーンショット 2024-07-09 23.25.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3834613/3219f8a4-fa2b-fa41-db1a-e59b3f296680.png)

インストール進んでるな〜
![スクリーンショット 2024-07-09 23.27.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3834613/2aa35643-aea7-92e7-2fe3-705e5e876141.png)

・・・しかし, ダウンロードしたアプリケーションの権限を設定しないといけないらしい。
![スクリーンショット 2024-07-09 23.28.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3834613/284667f5-27e2-9b00-391a-d720bcc10fe0.png)
macの設定から許可してあげましょう。

できた！！学生の時にみたやつだ
![スクリーンショット 2024-07-09 23.33.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3834613/3ea4859e-5c8a-2df5-df23-e36aa56c5da6.png)

これで少しだけ環境構築ができたぞーというところまでで終わります。
（夜中に作ったので記事内の文言適当で申し訳ない）