OmniAICreator/Qiita-1.07M huggingface dataset:
【解決方法】iOS Safari で JavaScript からCSS変数を取得できない

:::note warn
2022年4月12日追記
iOS のバージョンを最新版の `15.4.1` にアップデートしたところ iPad （第9世代）以外は取得できました。
iPad （第9世代）は古いから取得できないんでしょうかね…
CSS変数の定義はメディアクエリ内で行わないほうがよさそうです。

* 取得できた:smile:
  * iPhone XR 15.4.1
    * Safari
  * iPhone X 15.4.1
    * Safari
  * iPad Pro （9.7インチ） 15.4.1
    * Safari
* 取得できない:confounded:
  * iPad （第9世代） 15.3.1
    * Safari
:::

## 現象

JavaScript から算出されたCSS変数を取得しようとすると iOS Safari だけ取得できない…:weary:

* 取得できた:smile:
  * Windows
    * Firefox 99.0
    * Google Chrome 100.0.4896.75
    * Microsoft Edge 100.0.1185.36
  * Android 12 Pixcel 3
    * Google Chrome 96.0.4664.104
  * Android 11 OPPO CPH2309
    * Google Chrome 100.0.4896.58
* 取得できない:confounded:
  * iPhone XR 15.2.1
    * Safari
  * iPhone X 15.2.1
    * Safari
  * iPad Pro （9.7インチ） 15.2.1
    * Safari
  * iPad （第9世代） 15.3.1
    * Safari

## 原因

スマホ時だけ変数を利用したかったため、メディアクエリ内で変数を定義していたのが原因でした…

```css:CSS
@media (max-width: 32.4375rem) {
  .mask {
    --mask-height: 500px;
    height: var(--mask-height);
  }
}

```

```javascript:JS
const mask = document.getElementById('js-mask')
const maskHeight = getComputedStyle(mask).getPropertyValue('--mask-height')
console.log(maskHeight) // 空
```

## 解決方法

メディアクエリ外で変数を定義すればOK！
これで算出されたCSS変数を取得できました:blush:

```diff_css:CSS
+.mask {
+  --mask-height: 500px;
+}
@media (max-width: 32.4375rem) {
  .mask {
-   --mask-height: 500px;
    height: var(--mask-height);
  }
}

```

```javascript:JS
const mask = document.getElementById('js-mask')
const maskHeight = getComputedStyle(mask).getPropertyValue('--mask-height')
console.log(maskHeight) // 500px
```
デバイスがオフラインの時に処理をしたい~NWPathMonitor~

# 環境
・Swift 5.5.2
・Xcode 13.2.1
# 動機
Firebase等、ネットワーク接続が必須なアプリで、デバイスがオフラインの時にアラートを出したい。etc...
# 其の一
有名なライブラリに[Reachability.Swift](https://github.com/ashleymills/Reachability.swift)というものがあるみたいですが、今回はAppleが標準で用意してくれている[NWPathMonitor](https://developer.apple.com/documentation/network/nwpathmonitor)を使用します。
# 其の二
シングルトンクラスを作成します。
```Swift:Network.swift
import Network

final class Network {
    static let shared = Network()
    private let monitor = NWPathMonitor()

    func setUp() {
        monitor.start(queue: .global(qos: .background))
    }

    func isOnline() -> Bool {
        return monitor.currentPath.status == .satisfied
    }
}
```
# 其の三
ネットワーク監視の開始をAppDelegateに記述します。
```Swift:AppDelegate.swift
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 
    Network.shared.setUp() 
    return true
}
```
# 其の四
例としてif文を使っていますが、guard文を使ってオフライン時はこれ以上先に進めないように早期リターンするのも良いと思います。
```Swift:ViewController.swift
if Network.shared.isOnline() { 
    // オンライン時の処理
} else {
    // オフライン時の処理
}
```
#  終わりに
以上になります。
Psych::BadAlias: Unknown alias: defaultというエラーの対処法

開発環境
mac OS バージョン11.6
エディタ
VScode

# やりたいこと
rails db:migrateやrails s -b 0.0.0.0などのrailsコマンドを実行しようとすると下記のエラーになる。

# エラー内容
```
# rails db:migrate
Calling `DidYouMean::SPELL_CHECKERS.merge!(error_name => spell_checker)' has been deprecated. Please call `DidYouMean.correct_error(error_name, spell_checker)' instead.
Rails couldn't infer whether you are using multiple databases from your database.yml and can't generate the tasks for the non-primary databases. If you'd like to use this feature, please simplify your ERB.
rails aborted!
Psych::BadAlias: Unknown alias: default
```
```
# rails s -b 0.0.0.0
Calling `DidYouMean::SPELL_CHECKERS.merge!(error_name => spell_checker)' has been deprecated. Please call `DidYouMean.correct_error(error_name, spell_checker)' instead.
(省略)
/usr/local/lib/ruby/3.1.0/psych/visitors/to_ruby.rb:430:in `visit_Psych_Nodes_Alias': Unknown alias: default (Psych::BadAlias)
```




# 試したこと
まずは```Calling `DidYouMean::SPELL_CHECKERS.merge!(error_name => spell_checker)' has been deprecated. Please call `DidYouMean.correct_error(error_name, spell_checker)' instead.```で検索をかける。
ま下記サイトをもとに
https://takagi.blog/thor-deprecation-warning-in-ruby-3-1/


```
# bundle update --bundlerCalling `DidYouMean::SPELL_CHECKERS.merge!(error_name => spell_checker)' has been deprecated. Please call `DidYouMean.correct_error(error_name, spell_checker)' instead.
（省略）
Bundle updated!
```
↓
（結果）変化なし

次に

```
gem 'thor'
```

を追加して```bundle install```

```
# bundle install
Calling `DidYouMean::SPELL_CHECKERS.merge!(error_name => spell_checker)' has been deprecated. Please call `DidYouMean.correct_error(error_name, spell_checker)' instead.
Fetching gem metadata from https://rubygems.org/...........
Resolving dependencies.........
（省略）
```
↓
（結果）変化なし

色々調べた結果、```Calling `DidYouMean::SPELL_CHECKERS.merge!(error_name => spell_checker)' has been deprecated. Please call `DidYouMean.correct_error(error_name, spell_checker)' instead.```はRuby3.1環境で出てくる警告(warining)で、railsが動かないのとは関係がなさそう。
なのでここではとりあえずスルーする。

railsが動かない原因は```psych::badalias: unknown alias: default```にありそう。


# 原因
```psych::badalias: unknown alias: default```で調べた結果、
psychというYAML解釈用のGemの4系と3系で解釈が変わっているのが原因らしい。
version違いで起きていたエラーみたいですね。


# 解決策
3系を使うように指示を出します。

gemfileに下記を追加
```
gem 'psych', '~> 3.1'
```
保存して```bundle install```を実行
出来なければ ```bundle update```を実行


無事railsコマンドが使えるようになりました。

<br>
<br>
<br>

あとがき…waningを出なくさせる方法もわかったら記事に書いていきます。




# 参考
https://techracho15.rssing.com/chan-43028113/all_p53.html
https://qiita.com/kandalog/items/8fd20f79ecf73034795a
Next.js Linkコンポーネント

## 始めに
Next.jsを学習中です。
学習したことのアウトプットをしていきたいと思います
今回はLinkコンポーネントについてまとめます。

##　Linkコンポーネントとは

Next.jsで使用される、リンクを付与するためのタグのことです。

```sample.js
<Link href="/">ホームへ戻る</Link>
```
このように記述することで、リンクを付与することができます。

## aタグとの違い
同じくリンクを付与することができるaタグとの違いは、

Linkタグではリロードなしの画面遷移ができるということです。

リロードがないため、高速な画面遷移ができます。

試しに以下のコードで画面遷移の様子を確認します。

```sample.js
<h1 className={styles.title}>
   Welcome to <Link href="/posts/firstPost">Next.js!</Link>
</h1>
```

<a href="https://gyazo.com/7025ac4b790138a4f5794da1a33ff553"><img src="https://i.gyazo.com/7025ac4b790138a4f5794da1a33ff553.gif" alt="Image from Gyazo" width="1000"/></a>

画面遷移してもリロードされていないことが分かります。

## まとめ
今回はLinkコンポーネントについてまとめました。

Linkを使うことで高速に画面遷移ができるので便利です。

さらに学習記録を投稿していきます。
AWS Organizationsで月間に閉鎖できるメンバーアカウント数は総数の10％

AWS Organizationsでは、2022年3月の以下のアップデートで、メンバーAWSアカウントを閉鎖できるようになった。Organizationsからメンバーアカウントを作成、CloudFormation StackSetsで管理者ロールを作成し、アカウント閉鎖もOrganizationsから実施すれば、最小限のライフサイクル全体の管理ができるので、rootユーザーを使う場面がなくなる。rootのパスワード設定や管理も必要なくなる。これはとてもうれしいアップデート。

https://aws.amazon.com/jp/about-aws/whats-new/2022/03/aws-organizations-central-account-closure-lifecycle-management/

ただ、実際にこれをやってみると1回目は成功したが、2回目は「You have exceeded close account quota for the past 30 days.」というエラーで実施できなかった。Service Quotasで確認したところ、 **総アクティブメンバーアカウント数の10％しか30日以内に閉鎖できない** というQuota（上限）があるようで、変更できないらしい。

# メンバーアカウント閉鎖に失敗

AWS Organizationsからのメンバー削除ダイアログで、対象のメンバーAWSアカウントIDまで指定して実行しようとしたところ、以下のようにエラーが表示された。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55718/82cdb378-fbae-36fa-6bde-4fe5a69dad4e.png)

エラーメッセージは以下。

```
ConstraintViolationException
You have exceeded close account quota for the past 30 days.
```

# Service Quotasを確認する

見たところ関係しそうなQuota（上限）はこれ。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55718/bc415ccc-fcdf-b86b-1cb2-bf1611264722.png)

|フィールド|値|
|---|---|
|クォータの名称|30日の間に閉鎖できるアカウント数。実際に閉鎖できるアカウント数は総アクティブメンバーアカウント数の10％。|
|適用されたクォータ値|利用不可|
|AWSのデフォルトのクォータ値|1|
|調整可能|いいえ|

調整可能ではないので、これ以上のメンバーアカウント閉鎖は各アカウントにrootでサインインして行う必要がある。実際には1つは閉鎖できたので、上限は次のいずれかだと思われる。

* アクティブメンバーアカウント総数の10%（切上げ）が閉鎖できるアカウント数の上限。
* アクティブメンバーアカウント総数の10%とデフォルトのクォータ（＝1個）の大きい方が上限。

私の個人環境では、そもそも「Default maximum number of accounts」が拡張申請されてなくてデフォルト値10なので、アクティブメンバーアカウントは10個を超えられず、上記どちらかは確認できていない。

# 参考

本機能のリリースとドキュメント。ただし本記事投稿時点でQuotaの情報は見つけられなかった。

* [AWS Organizations で AWS アカウントの解約を一元管理し、エンドツーエンドのアカウントライフサイクル管理が容易に](https://aws.amazon.com/jp/about-aws/whats-new/2022/03/aws-organizations-central-account-closure-lifecycle-management/)
* [AWS アカウント の閉鎖 - AWS Organizations](https://docs.aws.amazon.com/ja_jp/organizations/latest/userguide/orgs_manage_accounts_close.html)

Quotaに引っかからない範囲での削除については、以下が参考になる。

* [[アップデート] AWS Organizationsでメンバーアカウントを簡単に削除できるようになりました | DevelopersIO](https://dev.classmethod.jp/articles/close-aws-member-accounts-with-closeaccount-api/)
よく使う入力規則の例

# 数値関連

数値になっていません。
```
OR(
   ISBLANK(AccountNumber),
   NOT(ISNUMBER(AccountNumber))
)
```
 7 桁で指定してください。
```
OR(
   ISBLANK(AccountNumber),
   LEN(AccountNumber) <> 7
)
```
 100000000000以内で指定してください。
```
OR(
   AnnualRevenue < 0,
   AnnualRevenue > 100000000000
)
```
割合は 0 から 100% の間で指定してください。
```
OR(
   Mix_Pct__c > 1.0,
   Mix_Pct__c < 0.0
)
```

整数を指定してください。
```
FLOOR( My_Integer__c) <> My_Integer__c
```

数量は -50 から 50 までの値を指定してください。
```
ABS( Volume__c) > 50
```
# 日付関連
平日を入力してください
```
CASE(MOD( My_Date__c - DATE(1900, 1, 7), 7),
0, 0,
6, 0,
1) = 0
```
今月の日付を入力してください。
```
OR (
YEAR( My_Date__c ) <> YEAR ( TODAY() ),
MONTH( My_Date__c ) <> MONTH ( TODAY() )
)
```

月の最終日を指定してください。
```
DAY(My_Date__c) <>
IF(Month(My_Date__c)=12, 31, DAY(DATE(YEAR(My_Date__c),MONTH(My_Date__c)+1,1) - 1))
```
完了予定日には今月以降の月を指定してください。
```
AND(
  OR (
     ISNEW(),
     ISCHANGED( CloseDate )),
  CloseDate < DATE( YEAR(TODAY()), MONTH(TODAY()), 1) )
```
終了日には、開始日より後の日付を指定してください。
```	
Begin_Date__c > End_Date__c
```
# 郵便番号
郵便番号は必須です。
```
ISBLANK( BillingPostalCode)
```
 郵便番号は 99999 または 99999-9999 の形式で指定してください。
``` 
AND(
 OR(BillingCountry = "USA", BillingCountry = "US"),
 NOT(REGEX(BillingPostalCode, "\\d{5}(-\\d{4})?"))
)
```
# 項目関連チェック

[その他]指定の場合、理由が必要です。
```
AND(
  ISPICKVAL( Reason, "Other" ),
  ISBLANK(Other_Reason__c)
)
```

[Status] が「New」に戻されない
```
AND(
  ISCHANGED( Status ),
  NOT(ISPICKVAL(PRIORVALUE( Status ), "New")),
  ISPICKVAL( Status, "New")
)
```
完了した商談は、Number_of_Line_Items__cが下方修正はできません。
**PRIORVALUE**関数：更新前の値を取得。
```
AND(
　OR(ISPICKVAL(StageName, "Closed Won"), 
　　　ISPICKVAL(StageName, "Closed Lost")), 
Number_of_Line_Items__c <  PRIORVALUE(Number_of_Line_Items__c) )
```
このフェーズには [納品日] が必要です。
```
AND (
  OR (
     ISPICKVAL(StageName, "Closed Won"),
     ISPICKVAL(StageName, "Negotiation/Review")),
ISBLANK(Delivery_Date__c)
)
```

重要商談(50000以上)はすべて承認されたときにだけクローズできます。
```
AND(
   OR(
      ISPICKVAL(StageName,"Closed Won"),
      ISPICKVAL(StageName,"Closed Lost")),
(Amount > 50000),
NOT(ISPICKVAL(Approval_Status__c ,"Approved")))
```

所有者だけ、Personal_Goal__c を更新できる
```
AND(
   ISCHANGED( Personal_Goal__c ),
   Owner <> $User.Id 
)
```
一度登録した後に変更できません。
```
AND(
   NOT( ISNEW() ),
   ISCHANGED( Guaranteed_Rate__c )
)
```


参照リンク：
https://help.salesforce.com/s/articleView?id=sf.fields_useful_field_validation_formulas.htm&type=5

役立つリンク：
https://help.salesforce.com/s/articleView?id=sf.customize_functions_a_h.htm&type=5
Mac最強の電卓アプリ

# きっかけ
ある日、M1のmacに乗り換えたことにより、今まで使っていた電卓アプリが使えなくなった。
mac標準の電卓は私にとってはちょっと使いにくい。

# 答えはPythonにあり
ターミナルなりiTermなりでPythonが開ければ、対話形式で計算ができて、電卓としては申し分ありません。
だがこれでは`ターミナルを開く`→`Python`という二段階が必要。
でもやっぱりSpotlightなりAlfredなりで一発で開けられるアプリが欲しいなぁ。

# 簡易的なアプリを作る
というわけでAutomatorを使って簡易的なアプリを作ります。

1. Autmatorを開き、新規書類を選択。
2. 書類の種類は「アプリケーション」を選択。
3. Automatorで`AppleScriptを実行`を選択し、以下の感じでスクリプトを入れる。

```applescript
on run {input, parameters}
	
	tell application "Terminal"
    	do script "exec ~/venv/bin/python" --ここはそれぞれの環境でのpythonのパスを入れて
    	activate
	end tell
	
	return input
end run
```

`~/venv/bin/python`のところはそれぞれの環境でのpythonのパスを入れてください。
`exec`はPython対話環境を`^D`で終了した時に、ターミナル自体も閉じるのに必要です。ただし、ターミナルの設定で`シェルの終了時`のところを`シェルが正常に終了した場合は閉じる`とかに設定しておかないとウィンドウは閉じてくれないと思います。

これで完成。

あとはAlfredなりSpotlightなりで`あなたが決めた名前.app`を開けば、Pythonという名の最強の電卓を使うことができます。
ubuntuでgo関連のパッケージをインストールした時のメモ

## やりたかったこと
こちらのパッケージをubuntuのサーバーに入れようとした

https://github.com/prasmussen/gdrive

README.mdには、ソースからのインストールは下記のコマンドでやろうということだった

```
go get github.com/prasmussen/gdrive
```

そうかgoがいるのか、と思いapt-getでgoをインストールした後に`go get`のコマンドを実行しようとしたらハマったのでメモ。

### 間違えた手順と発生したエラー

```bash
sudo apt-get golang
go get github.com/prasmussen/gdrive
```
でインストールしようとしたところ、`go get`のところでこんなエラーが
```
package context: unrecognized import path "context" (import path does not begin with hostname)
```

どうやらバージョン問題っぽい。apt-getで入れると古いのが理由のようで。よくある失敗パターンですね。

https://github.com/golang/dep/issues/1985

### 復旧+再インストール

apt-getで入ってしまったgoを消して、ソースからダウンロードしてインストール

```bash
sudo apt-get purge golang*
sudo rm -rf /usr/lib/go-1.6/ /usr/lib/go-1.6/src/ /usr/lib/go-1.6/src/runtime/ /usr/lib/go-1.6/src/runtime/race
wget https://go.dev/dl/go1.18.linux-amd64.tar.gz
sudo tar -C /usr/local -xzf go1.18.linux-amd64.tar.gz
mkdir -p ~/go; echo "export GOPATH=$HOME/go" >> ~/.bashrc
echo "export PATH=$PATH:$HOME/go/bin:/usr/local/go/bin" >> ~/.bashrc
source ~/.bashrc
```

さらに、最近のgoはgo getだと怒られるパターンがあるっぽく、go installでバージョンも指定して入れる必要があるぽい

```
$ go get github.com/prasmussen/gdrive
go: go.mod file not found in current directory or any parent directory.
	'go get' is no longer supported outside a module.
	To build and install a command, use 'go install' with a version,
	like 'go install example.com/cmd@latest'
	For more information, see https://golang.org/doc/go-get-install-deprecation
	or run 'go help get' or 'go help install'.
```

代わりに以下のコマンド

```bash
go install github.com/prasmussen/gdrive@latest
```
Google App Scriptでセルの背景色が連続する時の開始セル、終了セルを取得するってみんなに言ってたっけ？

# 経緯
サウザンドサニー号の船員の出勤シフト管理をするときにスプレッドシート上のクルーのセルがどこからどのセルまでなのかを知りたいので作りました。
目視で確認すれば、５列目から１４列目と言うことは分かるのですが、コード内でベタ打ちする箇所をなるべく減らすために関数にしてみました。

![スクリーンショット 2022-04-11 17.25.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/566867/3d4093ae-8861-bce0-f56d-d9a840995564.png)

この関数を使用することで期待する戻り値は５列目、１４列目という数値になります。


```javascript:Google App Script

function colorEndClmJudge(cellBgColor, shName){
  // 最終的にリターンする時に使用するオブジェクト
  let answer = {
    startClm : 0,
    endClm   : 0
  };
// 　特定の背景色の開始カラムのセルを判定をするフラグ
  let startFlg = false;
  // 特定の背景色の終了カラムのセルを判定をするフラグ
  let endFlg = false;
  // while文を処理するための変数
  let cnt = 1;

  // お決まりのオブジェクト取得。シート名は関数に引数２で取得
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var sh = ss.getSheetByName(shName);

  // 開始フラグと終了フラグがfalseの間処理継続
  while((startFlg == false) || (endFlg == false)){
    // セルの背景色を取得
    let bgColor = sh.getRange(1,cnt).getBackground();

    // 初めてスタッフのセルに来たとき
    // startFlg == falseとしないと処理が続くので必要
    if(bgColor == cellBgColor && startFlg == false){
      answer.startClm = cnt;
      startFlg = true;
    }
    // セルの背景色が変わったタイミング(終了セル)を判定
    if(startFlg == true && answer.startClm > 1 && bgColor !== cellBgColor){
      answer.endClm = cnt - 1;
      endFlg = true;
    }

    cnt++;
  }

  // オブジェクトで返す
  return answer;
}
```


```javascript: 実行メインコード
  const clueBgColor =　"#00ffff";
  const shName = "海賊王";
　　　　let bgColorClms = colorEndClmJudge(clueBgColor,shName);
　　　
  console.log(bgColorClms);

  // bgColorClms = {
  //    startClm : 5,
  //    endClm   : 14
  // };
```
rollback transactionの原因の解決方法

## 解決したいこと

ユーザーが写真付きの記事を投稿した際にターミナルでrollback transactionのエラーが表示され、DBに保存ができない状態を解決したいです。
　
## 解決するために試したこと
１、　　rollback transactionのエラーについて気になったので調べました。
簡単にいうと保存するためのカラムの培養が不足しておりsaveができない状態とのこと。

２、　コントローラのcreateアクションにある@dish.saveの記述を@dish.save!にすることでエラーの内容が表示されるとのことでそちらも試して見たところ「Validation failed: User must exist」エラーが出ました。直訳すると「バリデーションに失敗しました。ユーザーは存在しなければなりません」となりました。Userモデルに値が入っていないためバリデーションで弾かれていた様子。

３、　２で出てきたエラー「Validation failed: User must exist」を調べると解決の鍵となる内容の記事に出会いました。

## 解決方法

optional: trueをDishモデルに記述
＊/app/models/dish.rb
```ruby
class Dish < ApplicationRecord
  has_one_attached :dish_image
  belongs_to :user#, optional: true
  has_many :comments, dependent: :destroy
  has_many :favorits, dependent: :destroy

end
```
これは外部キーがnilでもDBに保存できるとのことでした。
でもよくよく考えるとnilではいけないのでコメントアウトしました。

さらに見ていくとコントローラのストロングパラメータに以下のような記述が、、
```ruby
merge(user_id: current_user.id)
```
すぐに調べたところmergeメソッドとあり、実際の記述がなくてもidなどを運べるという内容の記述でした！
この記述でuser_idカラムを運ぶことができエラーが解消されました。
以前学習してうっすら覚えていたform_withで使用したhidden_fieldに似ているような感じがしました。idなどを運べるようにするのはこれからも必要になると思うのでhidden_fieldも復習します。
1日かかってようやく解決できた、、
時間はかかったけど嬉しい、、

------

## 関連コード
＊/controllers/dishes_controller.rb
```ruby
class DishesController < ApplicationController
  def new
    @dish = Dish.new
  end

def create
    @dish = Dish.new(dish_params)
    @dish.save！
    #binding.pry
    redirect_to user_path(current_user.id)

  end

  private

  def dish_params
    params.require(:dish).permit(:dish_name, :introduction, :user_id).merge(user_id: current_user.id)

  end

end

```
-------
＊/models/dish.rb
```ruby
class Dish < ApplicationRecord
  has_one_attached :dish_image
  belongs_to :user#, optional: true
  has_many :comments, dependent: :destroy
  has_many :favorits, dependent: :destroy

end
```
------
＊/dishes/new.html.erb
```ruby
<h1>Dishes#new</h1>
<p>Find me in app/views/dishes/new.html.erb</p>
<h2></h2>
<%= form_with model: @dish, url: dishes_path(current_user.id), method: :post do |f|  %>
<%= f.label "料理名" %>
<%= f.text_field :dish_name %>
<%= f.label "料理画像" %>
<%= f.file_field :dish_image, accept: "image/*" %>
<%= f.label "紹介文" %>
<%= f.text_area :introduction %>
<%= f.submit "CookOPする" %>
<% end %>
```
ESP32のEEPROM機能で日本語を保存してみた

EEPROMの日本語保存でちょっとだけ手間取ったのでメモ

```c
#include <EEPROM.h>
int a = 0;

//値を入力する構造体作成
struct CONFIG {
  char camnm[37];//12文字入るサイズ　12バイト*3+1バイト
  char ch1nm[37];
  char ch2nm[37];   
};

void setup(){
  Serial.begin(9600);

  //保存されている設定値を読み取る
  EEPROM.begin(300);//サイズ指定
  CONFIG buf;
  EEPROM.get<CONFIG>(0, buf);

  //保存されている値を表示
  Serial.println( buf.camnm );
  Serial.println( buf.ch1nm );
  Serial.println( buf.ch2nm );
}

void loop() {
  if(a == 0){
    String strcamnm = "かめらの名前";
    String strch1nm = "チャンネル1";
    String strch2nm = "チャンネル2";

    strcamnm.toCharArray(buf.camnm,37);
    strch1nm.toCharArray(buf.ch1nm,37);
    strch2nm.toCharArray(buf.ch2nm,37);
  
    EEPROM.put<CONFIG>(0, buf);
    EEPROM.commit();
    a = 1;
  }

```

ちなみに全角文字は3バイトを使用するとのことで、最後に\nがつくため+1バイトが必要らしい。
「char camnm[37];//12文字入るサイズ　12バイト*3+1バイト」の部分。
Docker for MacでMySQLが突然動かなくなったので対応した備忘録

### 結論だけ先に書く
全部消してビルドし直したら復活した

以下コマンド
`$ docker-compose down --rmi all --volumes --remove-orphans`
[引用元](https://qiita.com/suin/items/19d65e191b96a0079417)

### 突然MySQLが死んだ。
唐突に起動しなくなったMySQL。
一体どうしたんだろうと思いエラーログを見た所、怪しい所が以下

```
2021-10-12T02:03:46.471647Z 0 [ERROR] InnoDB: Write to file ./ibtmp1failed at offset 0, 1048576 bytes should have been written, only 0 were written. Operating system error number 28. Check that your OS and file system support files of this size. Check also that the disk is not full or a disk quota exceeded.
2021-10-12T02:03:46.471659Z 0 [ERROR] InnoDB: Error number 28 means 'No space left on device'
2021-10-12T02:03:46.471664Z 0 [Note] InnoDB: Some operating system error numbers are described at http://dev.mysql.com/doc/refman/5.7/en/operating-system-error-codes.html
2021-10-12T02:03:46.471668Z 0 [ERROR] InnoDB: Could not set the file size of './ibtmp1'. Probably out of disk space
2021-10-12T02:03:46.471674Z 0 [ERROR] InnoDB: Unable to create the shared innodb_temporary
2021-10-12T02:03:46.471682Z 0 [ERROR] InnoDB: Plugin initialization aborted with error Generic error
```

どうも容量が足りないらしい。

Volumeを削除しちゃえば良いんだと考え以下を実行
`$ docker volume rm $(docker volume ls -qf dangling=true)`
その後いつもどおり compose up

###まだ生き返らない
エラー内容が変わったけど生き返らない
どうなってるんだ。。。

```
2021-10-12T02:44:44.484525Z 0 [Warning] TIMESTAMP with implicit DEFAULT value is deprecated. Please use --explicit_defaults_for_timestamp server option (see documentation for more details).
2021-10-12T02:44:44.485530Z 0 [ERROR] --initialize specified but the data directory has files in it. Aborting.
2021-10-12T02:44:44.485589Z 0 [ERROR] Aborting
```

[この記事](https://hensa40.cutegirl.jp/archives/8396)にたどり着いたので従ってみる。

```
$ docker ps -a
$ docker rm [コンテナID]
$ docker-compose build
$ docker-compose up -d
```

###まだ生き返らない。。。
もうどれが原因かわからないので全消ししてビルドし直してみる。
`$ docker-compose down --rmi all --volumes --remove-orphans`
[引用元](https://qiita.com/suin/items/19d65e191b96a0079417)

なおった。
よくわからない。。。
SPA+LaravelをAWS ECSへデプロイするのにしんどかったところ

フロントエンドはreact、バックエンドはlaravelっていうよくあるSPA構成をAWSのECSにデプロイするのはかなり大変だった。
慣れていればかんたんなのだろうが、不慣れなうちはみるみる工数が溶けてしまう。
本当にECSにデプロイすべきなのか検討してほしい。

## 開発環境の構築はかんたん。dockerのノリでECSを触ると火傷するよ

開発環境は、もちろん出来合いのdocker-composeでかんたんに構築できる。フロントエンドはyarn startとかyarn devとかでlocalhostにサーバーが立ち上がるし、バックエンドはlaravelなので特に難しいところはなにもない。

## ECSにデプロイする

ECSにデプロイするためには、フロントエンドもバックエンドもビルドして固めないといけない。

## laravelを含めたバックエンドのビルド

よくある開発用のdocker-composeファイルだと、ローカルのソースフォルダをそのままbindしているが、ビルドするときはDockerFileでコピーしないと中身が空っぽになる。
そして、composer installなどの初期化設定もすべてDockerFileのコマンドに含めないといけない。
サンプルコードはないので自分で書く必要がある。

## CORS、セッション管理

ECSは複数のEC2に分散されるため、laravelのセッション管理をステートレス、かつセッションドメインを明示しないと動かない。Sticky sessionにする方法もあるらしいが、開発環境はコンテナ１個なので動かなくなることが多いよね・・

## フロントエンドのデプロイ先,APIの接続先。

フロントエンドも、もちろんビルドしてデプロイしないといけないのですが、nginxでホストするならこれもイメージを作ってビルドする必要がある。もしくは、お手軽にS3+CloudFrontの構成を作る必要がある。Amplifyという手段も。
そんなことは開発中は何も考えてなかったので、インフラの構築のこともちゃんと考えておこう。

そして、開発中は同じlocalhostだが、ECS環境だと別のドメインになったりする。ドメインが変わってもちゃんと動くのか確かめておかないと、CORSでブロックされてハマってしまう。

## WAF,ALB,VPC,RDSも工数泥棒

本番環境なら、WAF,ALB,VPC,RDSもしっかりセキュリティを考えて構築しないといけないだろう。どこかに問題があると即・繋がらないになってしまうので、切り分けが大変。AWSに精通している人なら問題ないだろうが、不慣れな人にやらせるとあっという間に一ヶ月ぐらい工数が溶けてしまう。

## 結論
EC2上にdocker-composeで建てるほうがよっぽど楽です・・・
アクセスが増えてきて、1台のEC2でさばけなくなってからECSコンテナ管理へ移行しても、遅くないと思う。エンジニアの貴重な工数を何に使うのか、ECSの構築は恐ろしく工数が溶ける。
覚悟してかかってください。
～memo～ VScodeのyamlファイルでIncorrect type. Expected "array"

VScodeでyamlファイルを開くと、持っているファイル全部で
「Incorrect type. Expected "array" yaml-schema: Ansible Tasks File」
とwarningが出るようになりました。
「YAML」拡張機能でのチェックに引っかかっている様子・・・
ちょっと調べても日本語のサイトが出てこなかったので一応メモとして記載します。

settings.jsonに
`"yaml.schemaStore.enable": false`
を追記すれば解消します。
【NestJS】GraphQL初心者がテストを書こうとしたら詰まった

# 結論
GraphQLは一般的なRESTと違い、httpステータスは200で返してレスポンスのerrorsキーにエラーの情報を入れるのが一般的なので、リクエストが成功したかどうかのテストの際はres.statusで判断してはいけない。

# 概要
最初 NestJS(driverはApolloDriver) + GraphQL で認証実装後に以下のようにResolverのテストを書いたら、`res.status` は200だよってエラーになった。

```typescript
import { Test, TestingModule } from "@nestjs/testing";
import { INestApplication } from "@nestjs/common";
import * as request from "supertest";
import { HogeModule } from "src/hoge.module";

describe("HogeResolver", () => {
  let app: INestApplication;
  let HogeService = { findUnique: () => ["test"], findMany: () => ["test2"] };
  let resolver: HogeResolver;

  beforeAll(async () => {
    const module: TestingModule = await Test.createTestingModule({
      imports: [HogeModule],
    })
      .overrideProvider(HogeService)
      .useValue(HogeService).compile();

    resolver = module.get<HogeResolver>(HogeResolver);
    app = module.createNestApplication();
    await app.init();
  });

  afterAll(async () => {
    await app.close();
  });

  it("should be defined", () => {
    expect(resolver).toBeDefined();
  });

  it("sample", async () => {
    const res = await request(app.getHttpServer())
      .post("/graphql")
      .send({
        query: "{ hoge { edges { node { id } } } }",
      })
    expect(res.status).toBe(401)
  });
});

```

`console.log(res.text)` でresの中身を見ても、
```text
{"errors":[{"message":"Unauthorized","extensions":{"code":"UNAUTHENTICATED","response":{"statusCode":401,"message":"Unauthorized"}}}],"data":null}
```
となっていて、401エラーのようだったから、なんでres.statusが200なの？？？？？って感じだった。

→それで色々調べてみたら、結論にも書いた通り、RESTとはエラーの返し方が違うみたいで、GraphQLではhttpステータスは200で返してレスポンスのerrorsキーにエラーの情報を入れるのが一般的らしいことがわかった。

なので最終的には以下のようになった。
```typescript
import { Test, TestingModule } from "@nestjs/testing";
import { INestApplication } from "@nestjs/common";
import * as request from "supertest";
import { HogeModule } from "src/hoge.module";

describe("HogeResolver", () => {
  let app: INestApplication;
  let HogeService = { findUnique: () => ["test"], findMany: () => ["test2"] };
  let resolver: HogeResolver;

  beforeAll(async () => {
    const module: TestingModule = await Test.createTestingModule({
      imports: [HogeModule],
    })
      .overrideProvider(HogeService)
      .useValue(HogeService).compile();

    resolver = module.get<HogeResolver>(HogeResolver);
    app = module.createNestApplication();
    await app.init();
  });

  afterAll(async () => {
    await app.close();
  });

  it("should be defined", () => {
    expect(resolver).toBeDefined();
  });

  it("sample", async () => {
    const res = await request(app.getHttpServer())
      .post("/graphql")
      .send({
        query: "{ hoge { edges { node { id } } } }",
      })
    expect(res.body.errors[0].extensions.response.statusCode).toBe(401);
  });
});

```

# 備考
今回ORMにprismaを使っているので、`let HogeService = { findUnique: () => ["test"], findMany: () => ["test2"] };` となっていますが、ここはORMごとに変わるので注意。
NestJS側というより、GraphQL側の仕様の話だったので、もっとちゃんとドキュメント読もうねという話だった。
ChromebookからVSCodeを使ってみた

# Chromebookから編集してみた

## 問題なく使える（マークダウン）
### リンク
[リンク(google)](http://google.com)

### 画像
![lol_logo.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2628013/ac868e7f-6525-830a-23a4-4a6bf3683d8c.jpeg)

---
## 作業風景
* 通常
![s1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2628013/d09ace69-a2ad-73a5-b5e9-c837e8f3daed.png)

* 全画面モード

![s2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2628013/a8128b15-ffa6-952e-26c6-d6a22a672fc4.png)

> * 全画面モードでやると快適  
> * 正直Chromebookローカルで起動するエディタは使い物にならなかった・・・  
> * Googleドライブを直接開けるのが良い

### 日本語化はできるのか

![s3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2628013/8ab54356-0f53-36e5-dce2-91e603f79f43.png)

日本語化の拡張機能はWeb版だと使用できない

---
## テーマ（見た目）変更は？

![s4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2628013/d2b79590-1b76-9090-1db5-46d063706caa.png)


テーマ変更できます！  
他にもタブキーの空白数や、フォントサイズなども変更可能

---
## Chromebookからより快適に使うために

### **ネイティブアプリっぽく使いたい！**

* Chromeのメニューからショートカットを作成。  
※ウインドウとして開くにチェックを入れておこう。

![s6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2628013/fc4dfcd4-e342-ae29-f427-b57ada4ac2ea.png)


* ショートカットをシェルフに固定

![s7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2628013/388a16f5-6438-5103-5b96-ab366af9fb69.png)


* まるでネイティブアプリ！

![s5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2628013/81d6761d-e281-4b25-5c93-44d5bcdaa51f.png)


---
## まとめ

* Visual Studio CodeのWeb版をChromebookから使ってみた
* とりあえずマークダウンを試してみた
* 使えない拡張機能は結構ありそう
* ChromebookはAndroidアプリを使えるが、機種によっては使えないアプリもあるため、ブラウザベースはありがたい
* 普通にめちゃくちゃ使いやすい
* マークダウン以外の言語でも試してみよう

文字書くのは全く問題ない!
Carlaの立ち上げの際に表示されるcrashの原因とその解決法

この記事では，ubuntu18.04を使用しています．Unreal Engine_4.26を使用し，Git Hubと連結している状態です．
最後のCarlaを立ち上げる際にエラーが表示されてしまった人向けの記事です．


Carlaをインストールして，いざ立ち上げようとした際に以下のようなエラーが出てしまったことはありませんか？

![carla error.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2627267/1d7af569-c153-8f01-97bc-19290e5e1130.png)

「実際のプロセスがクラッシュしました」と表示されてしまっています．
この状況に出くわした時に何が原因なのかについて調べても出てこなかったため，記事を書いてみることにしました．


<dl>
  <dt>原因</dt>
  <dd>このcrashの原因は，Carlaを何らかの方法でクローンやダウンロードした際に全てがちゃんとダウンロードされていないことが原因であると考えられます．
</dd>
</dl>

<d1>
  <dt>解決策</dt>
  <dd>Carlaのファイルがしっかりダウンロードされていないことがわかりました．しかし，Carlaの中には多くのフォルダが存在していて，個人で調べても何がしっかりダウンロードされていないのかわからないです．
なので以下に確認してほしいフォルダを記述したいと思います．

>cd /Carla/unreal/CarlaUE4/Content/carla

このディレクトリに移動してもらい，中身を見てみてください．おそらく中は空っぽだと思います．なのでここに必要なファイルをダウンロードします．
</dd>
</d1>


<d1>
<dt>解決方法</dt>
<dd>
以下のコマンドを実行してください．

>sudo gedit ~/Carla/Util/BUildTools/BuildCarlaUE4.sh

これでBuildCarlaUE4.shを編集できるようになったと思います．ここの中に１つ書き込むことで実行した時にダウンロードでき、そのままCarlaを立ち上げることができます．


![Screenshot from 2022-04-11 15-50-28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2627267/1de93577-c4e1-9f7a-5fd4-5d48e45033fe.png)

source $(dirname "$0")/Environment.shの一行下に写真のように以下の文を追加します．

>UE4_ROOT=~/UnrealEngine_4.26

これを追加して保存して閉じます．その後もう一度Carlaをmake launchで立ち上げることで成功します．


</dd>
</d1>

Carlaを立ち上げることはできたでしょうか？もしうまく行かなっかた場合は，元々あるCarlaのファイルを消してもう一度Carlaのクローンもしくはダウンロードをしてみると良いかもしれません．




以上読んでいただきありがとうございました．
Pythonで特殊ディレクトリの絶対パスを取得する

# 結論はこれ
```python
import os
os.popen('powershell -Command ([Environment])::GetFolderPath("""MyVideos""")').read().rstrip('\n')
```
"MyVideos"の部分を変更で別の場所も取得できます
詳しくは以下のURLで

https://docs.microsoft.com/ja-jp/dotnet/api/system.environment.specialfolder?redirectedfrom=MSDN&view=net-6.0

# 経緯やはまった部分(何かの参考になれば)
PythonでGUIソフトを作っている際にファイルを保存する処理で、PicturesやVideosに保存したかったのですが、
"Python 特殊ディレクトリ"と調べても、パスを結合させて"C:" + "Users/user" + "Videos"という説明の記事しかなくて、しかもこの場合システムフォルダをDドライブなどの別のドライブやディレクトリに移動させていると対応できませんでした。

Pythonに限らず調べてみると、.NETのEnvironmentクラスのGetFolderPathを使えば取得できることがわかりました。

Pythonで.NETを使う"pythonnet"があるらしいですが~~なんかよくわからなかったので~~今回はosモジュールを使用してpowershellで出力した文字を標準出力で取得するやり方にしました。

powershell上では
```powershell
[Environment]::GetFolderPath("MyVideos")
```
だけで取得できるのですが、subprocessではpowershellを実行できないので、osモジュールで使用するためにCommandオプションを使用して
```bat:NG
powershell -Command [Environment]::GetFolderPath("MyVideos")
```
、、、では実行できませんでした。
Commandオプションを使う場合で括弧がある場合は、`[Environment]`が返されたオブジェクトのメソッドにアクセスするために`([Environment])`にする必要があるらしいです。(優先順位かな？)
```bat:括弧がないのでOK
powershell -Command [Environment]::GetFolderPath
```
```bat:OK
powershell -Command ([Environment])::GetFolderPath("MyVideos")
```
これをosのpopenで実行しようとしてもまたエラーです。
```py:NG
import os
os.popen('powershell -Command ([Environment])::GetFolderPath("MyVideos")')
```
理由はpopen内のクォーテーションを認識させるには三つ必要なことです。なので
```py:OK
import os
os.popen('powershell -Command ([Environment])::GetFolderPath("""MyVideos""")')
```
でようやく実行できます。ただ、これだとまだ文字列ではないので最後に
```py:OK
import os
os.popen('powershell -Command ([Environment])::GetFolderPath("""MyVideos""")').read().rstrip('\n')
```
文字列として読み取って、余分な改行を削除してようやく絶対パスが取得できました。
VBScriptバッチファイルで特定日数が経過したバックアップファイルを削除する

自分用の忘備録です。

### やりたいこと
- Windows Server2012で
- 特定日数が経過したファイルを削除する
（ファイル名に日付が含まれている状態）

### 実行方法
- タスクスケジューラにバッチファイルを登録
- バッチファイル内でVBScript(.vbs)を実行
- VBScriptで特定日数を経過したファイルを探して削除する

#### バッチファイル(~.bat)
```bat
call "C:\Windows\SysWow64\wscript.exe" "C:\~~\backup_delete.vbs"
・・・
```

#### バッチファイルから呼び出すVBScript(~.vbs)
```VB
' 特定日数以前の日付を取得
' 180日より前のファイルを削除する
date_before = DateAdd("d",-180,DATE())
date_before = replace(date_before,"/","")

set fso = CreateObject("Scripting.FileSystemObject")
' フォルダパスを記載。
basepath = "C:\~~"
set folder = fso.getFolder(basepath)

' 以前のファイルを削除する
dim file
for each file in folder.files
	delete_file_path = basepath & "\" & file.name
    If CLng(fso.GetBaseName(delete_file_path)) < CLng(date_before) Then
		fso.DeleteFile delete_file_path
	End If
next
```
FreeBSDのwatchdogを実装してみた

FreeBSD/mipsのMediatekにwatchdogの実装がなかったので、ちょっと書いてみました。

実はNTPD ST1サーバが時々固まることがあって、ワークアラウンドとしてwatchdogを使うことを思いつきました。

gonzoさんのmips/atheros/ar71xx_wdog.cを雛形にしました。

mediatekはmt7620までとこれ以降はアーキテクチャが違うようなので、v1としてmt7620までをサポートします。

FreeBSD/mipsではtimerはmips 4k共通のtimerを使っていて、SOCにはそれ以外にtimerが用意されていることが多いです。RT3050ではtimer0とtimer1があり、そのうちtimer1がwatchdogとして使える仕様になっています。

RT3050のデーターシートはネットでダウンロードできます。Timer1は0x120からあり、0がR/WのTimer設定(16bit)で4がROのTimer値で8がR/Wで設定になります。

probeとattachとwatchdog用のfnを作れば良いようです。

watchdogはタイマーを設定して有効にして、タイマーが切れる前に再設定して動きます。再設定せずにタイマーが切れるとリセットします。

普通のFreeBSDは突然リセットするのはまずいのですが、ZRouterではrootfsをroでmountしているので、いつリセットされてもOK牧場です。

呼び出しはユーザランドのwatchdogとwatchdogdがやってくれます。watchdogは単発で呼び出してwatchdogdはデーモンで定期的に呼び出します。watchdog一回実行するとしばらくするとリセットしてしまいます。watchdogdをkillで終了させると、watchdogを止めます。

ざっくりざっくり実装してみました。

https://github.com/ZRouter/freebsd-src/commit/2069c54ba33a19c77d9b7d21aeabedade45cc807

sysctlで見るとこんなです。

```
# sysctl -a | grep wdog
dev.mtk_wdog.0.debug: 0
dev.mtk_wdog.0.%parent: palmbus0
dev.mtk_wdog.0.%pnpinfo: name=watchdog@120 compat=ralink,rt3050-wdt
dev.mtk_wdog.0.%location: 
dev.mtk_wdog.0.%driver: mtk_wdog
dev.mtk_wdog.0.%desc: MTK WatchDog(Timer1) Controller (v1)
dev.mtk_wdog.%parent: 
```

fnにはWD_INTERVALが25で飛んできますが、だいたい10秒間隔で呼ばれます。これは絶対に25秒以内には呼ぶからってことだと思われます。

watchdogで再起動した場合はresetレジスタにフラグが立ちます。

実際どれくらい進んでいるかカウンターを見てみました。初期値
0xf000に対し0xa39bになっていたので、

0xf000 - 0xa39b = 0x4c65(19557)

だいいたい1955 per 1 sec

これはおそらく

(192MHz * 2) / ( 65536 * 3 )

に近いですが、3がどっから出てくるのかは不明です。

とりあえず最大時間(0xffff)で33秒くらい待つようにしてみました。

atherosの方を試してみたのですが、watchdogdを起動するとじきにリセットされてしまいます。タイマーの設定値がまずいようです。AtherosのTimer値は32ビットで、最大値でも1分くらいみたいです。あまり厳密にタイマー値は計算せず最大値も良い気がします。

最初からwatchdogを前提にしたシステムは如何なものかと思いますが、場合によっては有効な方法かもしれません。

ただwatchdogはテストが困難なので、ご利用は計画的にお願いします。

しばらくぶりで近所の公園に行ったら、犬を連れた人が以前に比べてすごく多い気がしました。巣ごもり需要ででしょうか？

reset起因のレジスタをsysctlの値に追加してみました。

```
dev.mtk_wdog.0.rststat: 2
```

MediatekのSOCは電源ON,reboot,watchdogの区別ができます。

電源ONやwatchdogの場合は通常起動してrebootの場合はプロセスの起動を行わないようにしました。これはリモートからrerootでflashをupdateするときに、プロセスが上がってしまうとrerootがメモリ確保に失敗してupdateが上手く出来ないためです。

AtherosのSOCはwatchdogの区別しかできないようです。
【初投稿】Markdown試してみた

# はじめに
- 初投稿です。
- markdownいろいろ試します。
- 参考は以下の記事です。

https://qiita.com/Qiita/items/c686397e4a0f4f11683d

↑これリンクカードって言うらしいです。

![Screenshot 2022-04-11 15.22.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2627348/0f20fd05-9044-f76b-a328-65266e645af7.png)
↑左側にmarkdownを書くとリアルタイムで右側のプレビューに反映されます。便利。
このキャプチャ、ローカルのエクスプローラーからドラッグ・アンド・ドロップしたらなんかURL出てきた。
Amazon S3にバケットを作ってそこに放り込んでる[^1]っぽい？:wink:

```
# はじめに
- 初投稿です。
- markdownいろいろ試します。
- 参考は以下の記事です。
```
チートシートだと言語やファイル名を記述していますが、` ```で挟めばコードブロックができます。`
~~あ~~
<details><summary>fold</summary>

- なにもない
</details>

:::note info
- いいね
:::
:::note alert
~~気をつけて~~
https://qiita.com/Qiita/items/c686397e4a0f4f11683d#code-blocks---%E3%82%B3%E3%83%BC%E3%83%89%E3%83%96%E3%83%AD%E3%83%83%E3%82%AF
:::
:::note warn
注意
:::

| 1 | 2 | 3 | 4 |
|:-:|:-:|:-:|:-:|
|   |   |   |   |
|   |   |   |   |

```diff_php
- イメージダウン
+ しめーじダウン
```

以上になります、今後はQiitaでアウトプットしていきたいですね

[^1]: 確証はありません
ピンポイントSAS 値から特定の文字・数字のみを取得する処理:COMPRESS関数　～医薬統計実務の備忘録 ～

ある項目の文字列値から特定の文字のみを除去あるいは取得する処理について。

問題
とある生データについて、ある項目の値である文字列から数字のみを抜き出して出力したい。
データ例：「test day1」→「1」にしたい。

解決
COMPRESS関数を用いて、数字のみを抽出します。
・使用した関数
　COMPRESS
使用方法
COMPRESS('項目',,'KD')
→項目の文字列から数字のみが除去される。
　KDについて、これは修飾子により取得する文字列を制御した形になります。
　Kで第二引数について、数字を扱うよう追加し、Dで第二引数の値のみを第一引数の文字列から残すことを定義します。

例：
　data a;
  txt=compress('あいうえお100',,'KD');
  run;
結果
　txt=100

COMPRESSについて
元の文字から指定した文字を削除した文字列を返す関数。これで不要な文字列の削除などが可能になります。
第1引数のみ指定すると、文字列中のブランクが除去されます。基本的にこの用法が多いです。
※全角スペースは除去できません。

構文
COMPRESS(文字列, 文字列から消したい文字, 修飾子)

第一引数：文字列
ここで指定された文字列から、特定の文字が削除されます。
この引数には文字定数、変数または式を指定します。必須項目。

第二引数：文字列から消したい文字
文字列から消したい文字を記載します。このリスト内の文字が第1引数の文字列から削除されます。
好きな文字を削除できますが、2文字以上の組み合わせの文字列を削除することはできません。
(→連続した文字列の削除方法についてこちらを参照ください。https://www.sas.com/offices/asiapacific/japan/service/technical/faq/list/body/ba045.html)

また、第3引数に修飾子※後述を指定することで様々な拡張が可能です。
文字リストを初期化する文字定数、変数または式を指定します。
※値にはクォーテーションマークが必要です。

第三引数：修飾子
第二引数で設定した削除リストに定数を追加したり(d修飾子など)、逆に削除リストの文字列のみ残すよう(K修飾子)できます。
文字定数、変数または式を指定します。
※値にはクォーテーションマークが必要です。

修飾子として使用できる文字は次のとおりです。
aまたはA	文字のリストにアルファベット文字を追加します。→アルファベット（A-Z）消します。
cまたはC	文字のリストに制御文字を追加します。→改行などを消します。
dまたはD	文字のリストに数字を追加します。→数字（0‐9）消します。
fまたはF	アンダースコア文字および英文字を文字リストに追加します。→アンスコ（_）とアルファベット消します。
gまたはG	文字のリストにグラフィカル文字を追加します。→図形文字（数字、英字、仮名、漢字、仮名、特殊文字といったおおよそ目に見える文字）
hまたはH	文字のリストに水平タブを追加します。
iまたはI	保持する、または削除する文字の大文字と小文字を区別しません。
kまたはK	リスト内の文字を削除するのではなく、保持します。→第二引数の文字のみを保持します。
lまたはL	小文字を文字リストに追加します。
nまたはN	数字、アンダースコア文字および英文字を文字リストに追加します。
oまたはO	COMPRESS関数が呼び出されるたびに第2引数および第3引数を実行するのではなく、一度だけ実行します。DATAステップ(WHERE句を除く)またはSQLプロシジャでO修飾子を使用すると、第2引数および第3引数が変更されないループでCOMPRESSを呼び出すときに、より迅速に実行できます。
pまたはP	文字のリストに句読点を追加します。
sまたはS	文字のリストに空白文字(空白、水平タブ、垂直タブ、キャリッジリターン、ラインフィード、フォームフィード、NBSP ('A0'x - 10進数のASCIIコードで160))を追加します。
tまたはT	第1引数と第2引数から末尾の空白を取り除きます。
uまたはU	大文字を文字リストに追加します。
wまたはW	印刷可能文字を文字リストに追加します。
xまたはX	文字のリストに16進文字を追加します。
ヒント
modifierが定数の場合、引用符で囲みます。一組の引用符で複数の定数を指定します。modifierを変数または式として表すこともできます。
GAEでAPIキーを隠したいとき

React、node.js、Google Cloud Platform (App Enigine)で[SORTFUL](https://sinceretech.app/ "SORTFUL")というチームビルディングゲームを作る際に出てきた疑問を、備忘録として記事にまとめています。

<h1>はじめに

今回SORTFULを開発するにあたって、Webex APIとSlack APIを使いました。
ここで問題になってくるのが、APIキーの隠し場所です。

そこで私は、GAEにデプロイする際に使うyamlファイルに環境変数として登録する方法を選びました。
以下にその方法を記します。

<h1>環境変数を設定する方法

GAE(Google App Engine)では、app.yamlに環境変数を設定できます。(参考：[app.yaml構成ファイル](https://cloud.google.com/appengine/docs/standard/go/config/appref?hl=ja "app.yaml構成ファイル"))

```app.yaml
env_variables:
  MY_VAR: "my value"
```

ただし、app.yamlはGAEにデプロイされてしまいます。（見ようと思えば誰でも見れる状態）

そこで、.gcloudignoreを使った以下の手順で設定します。
①secret.yamlに環境変数を定義し、
②app.yamlでsecret.yamlをincludeで読み込み、
③.gcloudignoreでsecret.yamlファイルをコミット対象から除外する。

```app.yaml
includes:
-secret.yaml
```
```secret.yaml
env_variables:
  webex_token: "********"
```
```.gcloudignore
secret.yaml
```
未経験からAWS SAA試験合格

# はじめに
・会社を辞め、未経験からエンジニアへの転職を決意し学習開始(2022年2月～)
・某プログラミングスクール受講
・2022年2月 AWS CPT試験 合格
・AWS SAAの勉強は約2ヶ月

# 学習方法
[(模擬問題付き)改訂新版 徹底攻略 AWS認定 ソリューションアーキテクト − アソシエイト教科書[SAA-C02]対応](https://www.amazon.co.jp/%E6%A8%A1%E6%93%AC%E5%95%8F%E9%A1%8C%E4%BB%98%E3%81%8D-AWS%E8%AA%8D%E5%AE%9A-%E3%82%BD%E3%83%AA%E3%83%A5%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%82%A2%E3%83%BC%E3%82%AD%E3%83%86%E3%82%AF%E3%83%88-%E3%82%A2%E3%82%BD%E3%82%B7%E3%82%A8%E3%82%A4%E3%83%88%E6%95%99%E7%A7%91%E6%9B%B8-SAA-C02/dp/4295010650/ref=sr_1_4_sspa?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&crid=3V4UOFGFUEL7P&keywords=AWS+SAA&qid=1649652138&sprefix=aws+saa%2Caps%2C217&sr=8-4-spons&psc=1&spLa=ZW5jcnlwdGVkUXVhbGlmaWVyPUFJODVYN0wxTldBT0QmZW5jcnlwdGVkSWQ9QTA2ODAwODQxQ1MwS1NPNVRRQ0Q2JmVuY3J5cHRlZEFkSWQ9QTM5WFNTTTZXMUJJODImd2lkZ2V0TmFtZT1zcF9hdGYmYWN0aW9uPWNsaWNrUmVkaXJlY3QmZG9Ob3RMb2dDbGljaz10cnVl)

暗記は意識しませんでしたが、一通り読み込んで、分からない部分は調べていました。また、模試の解説を読んで理解できなかった時に、こちらのほうが分かりやすい部分もあるので復習に使ってました。

[Udemy【2022年版】これだけでOK！ AWS 認定ソリューションアーキテクト – アソシエイト試験突破講座](https://www.udemy.com/course/aws-associate/)

アソシエイト試験突破に必要な知識を動画学習、ハンズオン、3回分の模擬テストで学んでいきます。私の場合、動画を2倍速で視聴し、ハンズオンの部分は限定的に行っていました。また、こちらも模試で解説を読んでも理解できなかった時に、復習に使ってました。

[Udemy【2022年版】AWS 認定ソリューションアーキテクト アソシエイト模擬試験問題集（6回分390問）](https://www.udemy.com/course/aws-knan/)

それぞれ1回目の正解率が50％～70％くらいでした。5～6周くらいしたと思います。これがあったから合格できたと言っても過言ではないです。

[Cloud Tech](https://kws-cloud-tech.com/)

日本最大級のAWS学習オンライスクールらしいです。AWS SSAの模試が200問無料で解けます。

[AWS公式の模擬試験](https://dev.classmethod.jp/articles/aws-exam-free-practice/)

最終の仕上げとして使いました。

# AWS SAA試験について感じたこと
試験日当日は、気温が高めで、試験会場も暖房が効いた閉鎖空間だったので、暑さで集中力をかなり削がれました。時間配分もうまくいかず最後の2問も残してしまい、あきらめモードでしたが合格できてよかったです。スコアは729点でギリギリでした。
SpringBootでRedisの設定を外から与えても動かない時

## 事象

SpringBootの[公式手順](https://docs.spring.io/spring-data/data-redis/docs/current/reference/html/#redis:connectors:jedis)に則って

```java
@Configuration
class AppConfig {

  @Bean
  public JedisConnectionFactory redisConnectionFactory() {
    return new JedisConnectionFactory();
  }
}
```

と書いて、外から設定を

```bash
export SPRING_REDIS_HOST="xxx.xxx.xxx.xxx"
```

と与えてみたものの、localhostを参照しているような挙動になってしまう。

※ spring.redis.host を環境変数で設定している

## 解決

```java 
  @Bean
  public JedisConnectionFactory redisConnectionFactory() {
    return new JedisConnectionFactory();
  }
```

を削除したら動いた。
AppStore のトップセールス情報を確認する方法

# これはなに
iOS アプリの売上ランキングサイトってどうやって情報を収集しているのか？
を調べた時のメモ

# メモ
2017 年ごろからアプリ内でのトップセールスの表示がなくなったが、引き続き RSS からは参照が可能らしい（動作保証されているかは不明だが、現在も利用可能）
https://mobiledevmemo.com/happens-app-store-intelligence-industry-no-grossing-chart/

Apple 公式の RSS Generator、ここでは top-free / top-paid のみ参照可能なURLを生成してくれる
https://rss.applemarketingtools.com/

Apple 公式の RSS フィードの一覧ページ（こちらも top-free / top-paid のみ）
https://www.apple.com/jp/rss/

トップセールス用の URL はこちらのブログでまとめてくださっていたので、参考にさせていただきました
https://noasmark.hatenadiary.org/entry/20111220/1324346337

日本のアプリのトップセールスランキング上位200件（上限は200件の模様、201以上の値を設定するとエラーになる）
https://itunes.apple.com/jp/rss/topgrossingapplications/limit=200/xml

日本のアプリのゲームカテゴリ内のトップセールスランキング上位200件
https://itunes.apple.com/jp/rss/topgrossingapplications/limit=200/genre=6014/xml
SQL (part1)

# SQL文
```
create database DB名//データベース作成

drop database DB名//データベース削除

creata table DB名.テーブル名(
    カラム名  データ型　default  デフォルト値　制約　comment 'コメント'
);//テーブル定義

drop table DB名.テーブル名//テーブル削除

desc テーブル名
show full columns from テーブル名//テーブルのカラムなどの情報

show create table DB名.テーブル名//テーブル定義の確認

use DB名//上記に記述していたDB名の部分を省略できる

select database()//現在useでアクティブ状態になっているとこを確認する

CREATE table DB.register_table (
	key1 int primary key 
);//主キーの作成

CREATE table DB.register_table (
	key1 int,
	key2 int,
	primary key(key1 , key2)
);//複合主キーの作成

CREATE table DB.register_table (
	key1 int auto_increment primary key
);//オートインクリメントを作成する。テーブルごとに一つの列しか指定できず、
指定した列にはインデックスを設定する必要がある。

show index from register_table;//インデックスの確認

alter table register_table;//テーブル定義の変更

add column key2 varchar(20) not null;//カラムを追加

add column key3 varchar(20) not null after key1;//key1の後に追加

add column key4 varchar(20) not null first; //最初に追加

modify column key2 int;//カラムの設定を変更する

drop column key2;//カラムを削除

//外部キー作成の注意事項
・型を合わせる
・インデックスを自動付与
・ON DELETE:レコード削除時のアクション
・ON UPDATE:レコード更新時のアクション
・CASCADE:親テーブルを削除または更新し、子テーブル内の一致する行を削除または更新する
・RESTRICT:親テーブルに対する削除または更新操作を拒否する。

外部キー定義
alter table テーブル名
add constraint 制約名（削除時に使用）
foreign key(対象のキー名)
       references 親テーブル名（テーブルキー名）
       on update cascade
       on delete restrict//省略可

```

データ型については → https://qiita.com/shuhoyo/items/07d28cec6babe788051d

制約については → https://qiita.com/Uejun/items/de02b78adbc54868d40d

*オートインクリメント → 値を自動で設定する。尚、値は一意のものである

*インデックス → primary keyやuniqueなど
Firebase 使って リアルタイムコミュニケーションがとれる 仮想空間を作ってみた

メタバースというワードを耳にするけど、VRが必要だったり、会員登録が面倒だったりするので仮想空間を手軽に試せる広場「ひろばー」を作ってみた。

https://hillover.yoshiweb.net/

## 特徴

- ログイン不要で参加できる
- 広場で他の人の名前なんて知らないので、ニックネームなど不要
- 他の人の発言を聞き逃すと取り戻せないので、過去ログなどは表示しない。


## 機能

- 歩く
- 話す


## 技術

- Firebase
  - 各キャラクターの色、座標、コメントをサイトにアクセス中のユーザーで共有
- three.js / pixi.js
  - キャラのアニメーションをPixi.jsで動かす。
  - Pixi.jsで動かしてる canvas をテクスチャとして three.js で3D空間に描画させた。
- Github Pages にカスタムドメイン設定
  - Firebase hostingだと転送量で課金されるため、Github Pagesにドメインを割り当てた。

## 苦労した点

- キャラを動かすイラストを描くのがつかれた。

## 今後

- キャラを増やしたり、アクションを増やしたいけど、やる気と時間次第...
Laravel のメールアドレスチェック

# 経緯
Laravelを使用しているシステムから大量のエラーメールが送信されていました。
結論から言うとメールアドレスが RFC 2822 で規定した形式になっていなかった為に発生したエラーです。


### Laravelのメールアドレスチェック
```
local.ERROR: Swift_RfcComplianceException: Address in mailbox given
[ someone@hugahuga.com ] does not comply with RFC 2822,
3.6.2. in /var/www/html/hogehoge/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/MailboxHeader.php:355
```

上記の例でいうとメールアドレスの前後にスペースが入っていました。
LaravelのSwift_Mailerってメールアドレスの形式チェックを行うんですね。。。



### 対応
事前にメールアドレスのチェックを行ってエラーがあればメール送信処理を行わない、かつエラーメールを送信する。
こちらで対応してめでたしめでたし。
『個人開発』行きたい場所をストックしよう 「ココイコApp」

## はじめに

![スクリーンショット 2022-04-10 21.33.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1390680/81bd3e80-3d80-8506-5a30-61f7ea5fd451.png)

ここ行こ？
コロナが終わったらね。
いつしか行きたかった場所を忘れてしまう。を解決するアプリ「ココイコ」をリリースしました!!

## サービス開発の経緯
大学生でエンジニア長期インターンに挑戦したく、ポートフォリオを作りたかった
どんなものを作ろうかと考えていた時期に、コロナが終わったらここ行こうね!!と話していた場所
をすっかり忘れていたことに気がついた
世の中の多くの人が、「ここ行きたい!! 」「コロナが終わったらね。」という会話をしているのではないかと考えた

こんな感じ？
![sp-kokoiko-qiita.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1390680/dceaa798-35d9-058a-5359-b089cb1992d8.png)


## サービス概要
![about.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1390680/de4ff3e4-ff72-dd88-e251-2009ab679964.png)

行きたい場所をストックしていつまでも忘れない。
自分だけの行きたい場所リストを作成!! 友人や恋人と作る旅行リストとしても使える。そんなアプリ




https://www.kokoiko-app.com/





## 使用技術

### バックエンド
- Ruby 3.1.0
- Rails 6.1.4.7

### 主なgem
- Devise
- Carrierwave
- MiniMagick
- Action Mailer

### フロトントエンド
- javascript
- bootstrap5
- sass

### デプロイ
- Heroku

## 使い方

### 1.新規登録・ログイン
メールアドレスとpasswordで新規登録・ログインします。
![location_add.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1390680/dee65fab-835b-7702-d25a-d19d78e33875.gif)




### 2.目的地を追加
PCなら画面左、スマホやタブレットは画面上部に新規登録ボタンが存在します。
![add_location.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1390680/8e8378ee-379b-daee-6fdf-729a2b61ff55.gif)






### 3.編集・削除
行き先の編集や削除は詳細ページから
![edit_delete.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1390680/21811c7b-478f-31ac-b060-9a38e5e11111.gif)





## 今後の変更予定
・AWSへのデプロイ
・テストの充実
・友人や恋人との目的地リスト共有機能

## 終わりに
基本的に独学なので誰かに聞くことができなかった。
Amazon S3の設定やテストを走らせることのできないバグ。usersのnameがnilにならないようDBでもvalidationでも設定しているが、nilになる可能性があるとアラートされるなど、自分ではどうしてもわからない点があった。
ひとまずリリースまで到達することができたが、やはり独学だと限界があるかもしれない。
誰かに聞くことができる環境は最高なんだなと改めて思いました。

今後も精進していこうと思います。

githubアカウント
https://github.com/kandalog

Twitterアカウント
https://twitter.com/tee__eng

https://www.kokoiko-app.com/
【Laravel】Carbon::make を使うとnull値を取得できる

### 概要

Carbon::parse(null)でCarbonインスタンスを生成すると期待した挙動にならなかった。
- 期待した挙動）
    - null値が返る
- 実際の挙動）
    - Carbonインスタンス（perseメソッドを呼び出した時点の時刻が設定されている）

毎回、引数のnullチェックするのは冗長なので、良い方法が無いか調べていると以下のissuesを見つけた。
- https://github.com/briannesbitt/Carbon/issues/1917#issuecomment-545757377

### 結果的にどう対応するか？

Carbon::makeを利用すると、引数がnullの場合にnull値を取得できる。

```php
var_dump(Carbon::make(null)); // null
var_dump(Carbon::make('2019-06-15 12:34:54')); // Carbon instance
```

ちなみにQiita記事等で良く見かける実装方法だと、null値では無く、メソッドの呼出し時刻でCarbonインタンスが生成されるので使い方に注意。

```php
new Carbon(null); // Carbon::make('現在の時刻') で生成したインスタンスと同じものを返す
Carbon::parse(null) // Carbon::make('現在の時刻') で生成したインスタンスと同じものを返す
```
Xserverにaws cliをインストールする

# 目的
 XserverのデータをまるっとAWSのs3にバックアップをしたい。
そのためaws-cliをXserverにインストールする。

ちなみに、昨夜(2022/04/11)の大幅障害の影響です。

参考サイトにした内容をまとめた感じなので、サラッと実行したコマンドだけ表示しています。

# はじめに

aws-cliをインストールする手順はいろいろとあるようですが、pythonsのパッケージ管理ツールのpipでインストールするのが、一番らくちんみたいです。

Xserverには、pythonのバージョン２，バージョン３とインストールされていますが、レンタルサーバの掟として、新規にパッケージを追加することはできないみたいです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/133291/f6741ece-44a5-f9f7-9741-e39b0ae6bbff.png)

そのため、自分のローカル環境にPythonをインストールして、その上でaws-cliをインストールするようです。

# 手順

## ダウンロード

```bash
wget https://repo.anaconda.com/archive/Anaconda3-2022.05-Linux-x86_64.sh
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/133291/ff252b27-9b1b-b2d1-2ec1-83dfeecd3487.png)


## インストール

```bash
bash Anaconda3-2021.11-Linux-x86_64.sh
```

プロンプトがでたら```Enter```で次へ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/133291/09d9e0b5-48b6-1762-8674-b92bf412d0eb.png)

EUR（エンドユーザーライセンス）がでるので、```space```を何回か押して、最後に進みます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/133291/b245fc7f-ac5c-a32d-f248-da7f73541e5b.png)

EURを承諾するのなら、```yes```と入力します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/133291/11f6f8b7-7dcd-8e0e-fada-8bfc83cd73ae.png)

デフォルトのインストール先は```$HOME/anaconda3```です。
こちらでよければ、```Enter```を押します。変更する場合は、パスを入力します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/133291/6f4905eb-f077-fa06-c01a-349b47b3da0f.png)

インストールが始まりますので、しばらく待ちます。
そうすると、インストールが完了後に、設定をするかどうか聞いてきます。
設定をしてもらいたいので、```yes```と入力します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/133291/c4e20f0e-4698-9862-ab54-2deef8a1671c.png)

以下の画面が表示されて設定が完了となります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/133291/c55bf2e1-2137-ed12-179a-74619b628b9f.png)

```~/.bashrc```を確認すると、パスが追加されています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/133291/96874646-1bd1-68b6-fb65-1394f6ef35dd.png)


## 環境設定

自動的にPATHにanacondaのパスが追加されるのですが、```~/.local/bin```は、追加されなかったので、.bashrcまたは.bash_profileのPathに追記します。

このように```~/anaconda3```と```~/.local```が追加されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/133291/1175df92-269b-15eb-be6d-81acde4a5eb6.png)


以下のように```~/.local```のパスを追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/133291/3aacfd11-62cb-1f3b-706e-13d293b01198.png)


パスを更新したので読み込み直しをします。

```bash
source ~/.bash_profile
source ~/.bashrc
```

## aws-cliをインストール

```bash
pip install awscli
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/133291/97fa24a0-95d0-aa7f-3eb8-4bf4a98565e1.png)

エラーがでたようなので、もう１度。。。大丈夫みたい。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/133291/d02312f9-4521-8b42-5f1b-86180eab411a.png)

# 最後

これで```aws s3 sync```が使えて、無事にバックアップができました。

# 参考サイト

* https://blog.s0014.com/posts/2018-03-09-sakura-s3-cli/
* https://science-log.com/pc%E9%96%A2%E9%80%A3/xserver%E3%81%AE%E3%82%B5%E3%83%BC%E3%83%90%E3%83%BC%E3%81%ABpython%E3%82%92%E5%B0%8E%E5%85%A5%E3%81%99%E3%82%8B/
* https://www.anaconda.com/
v-data-tableテーブルのヘッダーで改行をする方法

headerスロットを使います。
https://vuetifyjs.com/ja/api/v-data-table/#slots
```
<v-data-table :items="items">
  <template v-slot:header>
    <thead>
      <th>ID</th>
      <th>名前</th>
      <th>緊急<br />連絡先</th>
    </thead>
  </template>
</v-data-table>
```