OmniAICreator/Qiita-1.07M huggingface dataset:
Expo の 2次元コードリーダー実装の留意点

# 2次元コード読み取りライブラリ `expo-barcode-scanner`
一般的には expo-barcode-scanner が紹介されているようです。
https://docs.expo.dev/versions/latest/sdk/bar-code-scanner/

## 実装例
```jsx
import { BarCodeScanner } from 'expo-barcode-scanner';

export default function BarcodeScanner {
  const [hasCameraPermission, setCameraPermission] = useState();
  useEffect(() => {
    BarCodeScanner.requestPermissionAsync()
      .then(({ status }) => {
        setCameraPermission(status === 'granted');
      });
  }, []);

  return (
    <BarCodeScanner
      onBarCodeScanned={({ type, data ) => {
        console.log(data);
      }}
      style={{ flex: 1}}
    />
  );
```

## 問題点
ただ、これには以下URLに示すように、react-navigation との相性で、一度スキャンされた後、2度目以降のスキャンがうまくできない事例があり、私自身も経験しました。
https://stackoverflow.com/questions/54333951/how-to-fix-redirect-uri-mismatch-error-while-using-expo-google-loginasync-in-r

パーミッションの状態取得や、ユーザダイアログなどは、とても調子よく動作するのに、本来の機能が連続で動作してくれず、アプリの再起動をユーザに促すのもどうかと思うので、他の手段を探していました。

# 原点に立ち返って、カメラライブラリ `expo-camera`
expo でフツーにカメラをインビューで動作させるライブラリです。
https://docs.expo.dev/versions/v44.0.0/sdk/camera/

## 実装例
```jsx
import { Camera } from 'expo-camera';

export default function BarcodeScanner {
  const [hasCameraPermission, setCameraPermission] = useState();
  useEffect(() => {
    Camera.requestCameraPermissionAsync()
      .then(({ status }) => {
        setCameraPermission(status === 'granted');
      });
    // Camera.requestPermissionAsync() も deprecated ですが、存在しています
    // まぁ、Camera 入れるだけなので、修正論点ないかと
  }, []);

  return (
    <Camera
      onBarCodeScanned={({ type, data ) => {
        console.log(data);
      }}
      style={{ flex: 1}}
    />
  );
```

上記のように、`expo-barcode-scanner` とほぼほぼ同じ記述で同じ機能が実現できます。

## 問題点
こちらは、`expo-barcode-scanner`で発現した問題は全く発生せず、なぜこの2つは分かれているのだろう？と疑問に思ったところ、今度はパーミッションを得る動作に難がありました。

* バーコードの読み取り制度が若干弱い

`expo-barcode-scanner` で読み取れていた解像度やサイズの 2次元コードが読み取りに時間がかかることがありました。
そのため、2次元コード側のサイズ変更を余儀なくされました。

# 私の結論
サイズ調整でデザインに影響あったとしても、読み取れないよりはマシです。

# まとめ
`expo` クロスプラットフォーム開発として非常に優秀ですが、細かいところで痛み分けをするポイントを度々感じます。
とはいえ、多くの恩恵を受けているので、文句を言わず頑張りましょう。
MIRACLE LINUX 8.4 を Azure でセットアップする

MIRACLE LINUX 8.4をAzureでセットアップする方法についてつらつらと書いてみます。

＊AzureポータルからVirtual Machinesを選択します。

![ml-azure-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654173/0e31ba63-8b4d-eb54-9355-594d8054ae64.png)

＊続いて 作成》仮想マシン と選択します。
![ml-azure-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654173/2042f9b0-6cea-5bdf-0901-f1bc1820a8fa.png)

＊リソースグループ、仮想マシン名、地域などを適切に設定します。(AWSとは異なり、特に東京リージョンでなくても大丈夫です。)
![ml-azure-3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654173/60c89b3e-38da-5428-2ae3-b93f89597b3f.png)

＊続いて、イメージの選択で すべてのイメージを表示 を選択します。Marketplace で MIRACLE で検索すると、候補に出てくるので、その中からMIRACLE LINUX 8.4を選択します。
![ml-azure-5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654173/ee1fa5fb-f725-59a6-6ce2-44e2b9682388.png)

＊次のサイズの選択ですが、イメージ発行者の推奨がどれも選べない場合は、すべてのサイズを表示 を選択して、妥当なものを選択します。選択した地域によって値段が構成が変わるのですが、メモリ4GB以上は欲しいところです。動かしてみるだけなら2GBでも可能だと思います。
![ml-azure-6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654173/de5885b3-591a-1b9a-37b0-1733040715c7.png)

＊管理者アカウント の各項目については、そのままでも構いません。確認と作成 に進みます。
![ml-azure-7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654173/60eaea98-6652-a7d1-30fc-40e74a65f27a.png)

＊作成 ボタンをクリックすると秘密鍵のダウンロードを促されるので、ダウンロードします。
![ml-azure-8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654173/d27cc34f-bb54-c1cf-ab59-a35421c9bb80.png)

＊デプロイが終わると次の画面で止まるので、リソースに移動 をクリックします。
![ml-azure-9.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654173/7bba15a1-62b8-64b8-4794-98938dc82a5e.png)

＊起動したVMをプロパティで確認します。
![ml-azure-a.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654173/5e09705d-b7a1-2ca4-bf27-d5435412ea39.png)

＊パブリックIPアドレスに対して先ほどダウンロードした秘密鍵を用いてSSH-2で接続します。ユーザー名はazureuserです。(puttyを使う場合はputtygenなどで変換が必要です。)
![ml-azure-b.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654173/5d9ad6b5-a6e8-36c7-c1f0-2a9f4d43a8d3.png)

＊最後に時間帯を正しくセットすればセットアップ完了です。
![ml-azure-c.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654173/eee47297-c14a-1786-27e2-c4b4dcdd2dcd.png)
DynamoDBエラー [The provided key element does not match the schema]

# エラー
LambdaからDynamoDBを呼び出す際、get_item(Key={UniqueId: 'UniqueId'})した時にでたエラー。
内容的にkeyのtypeが違うのかと思っていたらソートキーを指定しないといけなかったよう。。。

```bash
[ERROR] ClientError: An error occurred (ValidationException) when calling the GetItem operation: The provided key element does not match the schema
```

# 解決法
ソートキーを指定するもしくはDBのソートキーを指定しない。




```python
dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('テーブル')
res = table.get_item(Key={
            'UniqueId': 'UniqueId',
            'SortKey': 'SortKey'
        })
```
Ubuntu Server 20.04.3 LTS 構築#2 初期設定2

前回に引き続き、ubuntu serverの構築練習

<前回の内容>
ubuntu server 20.04.3 LTSのインストール迄

実施日:2022/01/17
<<今回の内容>>
GUI画面の表示方法

<GUI画面の表示方法>
CLIコマンドに慣れていないため、ディレクトリやファイルの保存先について
不明な点があった際にGUIで確認するための処置

[コマンド]
sudo systemctl set-default graphical
sudo systemctl get-default
⇒graphical targetと表示あり
　デフォルトでGUI環境に設定がされている。
　実際にGUIで表示出来るように設定する。

[コマンド]
sudo apt-get install xfce4
⇒コマンド実行後にgdm3/lightgdm/sddmのいずれかを選択する画面が表示されたため、
　gdm3を選択。その後、再起動を実施、GUIで表示されていることを確認

次回#3　セキュリティ設定1
多重代入についてまとめてみました

ruby silverの勉強をしていく中で間違えたところがあったのでその解説記事を挙げていきます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2485775/042e8990-4236-4e6f-1a92-2c972420c4a8.png)

##多重代入

###基本的な使い方
```
a, b, c = 1, 2, 3
a
# => 1
b
# => 2
c
# => 3
```
##右辺の要素が左辺の要素よりも多い場合は無視される
```
a, b = 1, 2, 3
a
# => 1
b
# => 2

#この場合3は無視される
```
##右辺の要素が左辺の要素よりも小さい場合は無視される
```
a, b, c = 1, 2
a
# => 1
b
# => 2
c
# => nil

該当する要素がない場合は、nilになる
```
##残った要素は、配列として格納される
```
a, *b = 1, 2, 3
a
# => 1
b
# => [2, 3]


・残りの要素が1つの場合でも、配列になる
a, *b = 1 ,2
a
# => 1
b
# =>[2]


・残りの要素がない場合でも、配列になる
a, *b = 1
a
# => 1
b
# => []
```

#ネスト
```
(a, b), c, d = [1, 2], 3
a
# => 1
b
# => 2
c
# => 3

カッコを使うことで、ネストした配列の要素を代入することができる
```

#左辺にカッコを使って、右辺に何もつけないとき
```
(a, b), c = 1, 2, 3
a
# => 1
b
# => nil
c
# => 2

bにはnilが代入され、3は無視される
```

何か間違いがあったら、ご指摘いただけると嬉しいです！

【参考記事】

https://qiita.com/yancya/items/c557864f307d429bbde4
RustのOptionの as_ref() と unwrap() と as_mut() を理解する

## 概要
[Rc::as_ref()](https://doc.rust-lang.org/std/rc/struct.Rc.html#method.as_ref) の入れ所がよく分からなくなってきたので、作ったもの。

## 環境

* Ubuntu 18.04 LTS
* rust 1.58

## 解説

### as_ref()を使わない場合

#### 例1: sを消費して中身を取り出す

```rust
fn main() {
    let s: Option<String> = Some(String::from("hello"));
    let t: String = s.unwrap().push_str(" world!");
    assert_eq!(t, "hello world!");
}
```

#### 例2: unwrap() は消費する

次の例は、unwrap で所有権の移動が起こるので、コンパイルエラーとなる。

```rust
fn main() {
    let s = Some(String::from("hello"));
    if s.unwrap().len() == 5 {  // move
        let mut t = s.unwrap();
        //          ^ value used here after move
        t.push_str(" world!");
        assert_eq!(t, "hello world!");
    }
}
```

### 例3: as_ref()を使う

`Option::as_ref()` は、`&Option<T>` を `Option<&T>` に変換します。
`&String` のように借用するのであれば、消費はされません。

* `s.as_ref()`: Some("hello") → Some(&"hello")
* `s.as_ref().unwrap()`: Some(&"hello") → &"hello"
* `s.as_ref().unwrap().len()`: &"hello" → &"hello".len()

※ String は省略しています

```rust
fn main() {
    let s = Some(String::from("hello"));
    if s.as_ref().unwrap().len() == 5 {
        let mut t = s.unwrap();
        t.push_str(" world!");
        assert_eq!(t, "hello world!");
    }
}
```

## 応用例: as_mut()

* `self.msg.as_mut()` の部分が `self.msg` の場合に失敗するのは上で説明した通り、move されるからです
* `self.msg.as_ref()` では、可変の借用ではないので `String::push_str()` は使えません
* `Option::as_mut()` は mut Option(T) → Option(&mut T) に置き換えます
* `&mut String` なら、push_str() は使えます。

```rust
struct Msg {
    msg: Option<String>,
}

impl Msg {
    pub fn push_str_in_option(&mut self, a: &str) {
        if let Some(msg) = self.msg.as_mut() {
            msg.push_str(a);
        }
    }
}

fn main() {
    let mut s = Msg { msg: Some(String::from("hello")) };
    s.push_str_in_option(" world!");
    assert_eq!(s.msg, Some(String::from("hello world!")));

    let mut s = Msg { msg: None };
    s.push_str_in_option(" world!");
    assert_eq!(s.msg, None);
}
```

次のようにも書ける。
`Option::map(f)` で `Option(&T)` → `Option(f(&T))` とできる。

```rust
//...

impl Msg {
    pub fn push_str_in_option(&mut self, a: &str) {
        self.msg.as_mut().map(|s| s.push_str(a));
    }
}

//...
```
【合格】Pythonエンジニア認定基礎試験の勉強方法

Pythonを業務で触った経験があり、体系立てて一度勉強したかったので、そのモチベーション到達点として試験を利用しました。民間会社が行っているものなので知名度は微妙ですが、学習レベルがまだ初歩段階の人にはよく受けられている試験です。

https://cbt.odyssey-com.co.jp/pythonic-exam.html

「基礎試験」と「データ分析試験」があり、今回受験したのは基礎試験です。Pythonの基礎文法が問われる試験です。

# 受験状況
- 勉強期間：20日
- 受験日：2021年2月
- 使用教材
    - Udemy動画 [現役シリコンバレーエンジニアが教えるPython3入門+応用+アメリカのシリコンバレー流コードスタイル | Udemy](https://www.udemy.com/course/python-beginner/)
    - 公式テキスト [Pythonチュートリアル第3版](https://www.amazon.co.jp/Python%E3%83%81%E3%83%A5%E3%83%BC%E3%83%88%E3%83%AA%E3%82%A2%E3%83%AB-%E7%AC%AC3%E7%89%88-Guido-van-Rossum/dp/4873117534/ref=sr_1_2?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&keywords=Python%E3%83%81%E3%83%A5%E3%83%BC%E3%83%88%E3%83%AA%E3%82%A2%E3%83%AB&qid=1638350751&s=books&sr=1-2)（※最新は[第4版](https://www.amazon.co.jp/Python-E3-83-81-E3-83-A5-E3-83-BC-E3-83-88-E3-83-AA-E3-82-A2-E3-83-AB-E7-AC-AC4-E7-89-88-Guido-van/dp/4873119359/ref=dp_ob_title_bk)になってますのでご注意ください）
    - サイト（無料）[DIVE INTO EXAM](https://diver.diveintocode.jp/dive_into_exam/1) 模擬試験
    - サイト（無料）[PRIME STUDY](https://study.prime-strategy.co.jp/) 模擬試験
- 費用合計：13400円
    - Udemy動画：1400円 
    - 公式テキスト中古：1000円
    - 受験料：11000円

# 使用教材と勉強方法
最初に動画から入りました。基礎文法の講座ならなんでもよいかと思い、Udemyセールのタイミングで目についた動画を購入しました。

https://www.udemy.com/course/python-beginner/

現在は、少しUdemyの雰囲気が変わってしまってあまり好きではない（主観）ので、人に薦めるなら[jmoocのgacco](https://gacco.org/)や[coursera](https://www.coursera.org/)を推したいかな〜とおもってます

https://diver.diveintocode.jp/dive_into_exam/1

動画を倍速で見て概観を掴んだら、[DIVE INTO CODE](https://diver.diveintocode.jp/dive_into_exam/1)で模擬試験を受けてみました。375 / 1000点でひどい点数でしたが、とりあえずどんな問題が出るのかを把握。

次に、公式テキストのPythonチュートリアルを読み始めます。

![20211201115428.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/927088/6e192c46-bce2-bd0e-c7fd-9a9941547202.jpeg)

公式テキストに目を通し終わったら、模擬試験に再びトライします。試験の中で出てくるコードを実行しながらゆっくり進めました。解きながら知識を吸収します。模擬に出てきた内容は公式テキストにマークしたり書き込みしたりして、必要知識を一元化しておきます。

![20211201185839.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/927088/1c9439c1-8f55-1287-adad-9b3c306f7393.png)

![20211201185848.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/927088/8ad93e09-688f-4263-792d-35aec4d7a4d9.png)

# 試験結果
- 合格
- 975 / 1000点（合格基準点は700点）

![20211203161553.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/927088/b277d196-860f-3653-e077-fddfc0288c1a.png)

![20211201125739.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/927088/5e8bd924-bdb6-9f2e-4c53-a82c496ec6d2.jpeg)

▼追記：Pythonデータ分析試験も合格しました。

https://qiita.com/n-k-j/items/8df610c27eb57a79869b
N予備プログラミング　〜冬コンテスト作品制作過程を晒してみる③〜

みなさんこんばんわ

せまる〜♪ショッカー！（締切！）という事で、どんどん実装していきます。
”18.【サービス開発3】データモデルの実装とユーザーの保存”を写経でGO！！

しかーし！今回はDB設計なので慎重に行くとこ！
データベース作成と定義を自前のやつにさしかえて作りますよと。うーさすがに緊張！

![スクリーンショット 2022-01-25 21.38.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2345137/805ec9ec-f7e1-3395-7779-6b5aea17dba7.png)
データベース名とデータ置き場（ボリューム先）名両方しっかり確認してyml書き直しーの！
docker-compose exec db bash　でデータベース用コンテナ開きーの！
psqlコマンドでデータベース作成！！渾身のポチっとな！！！

![スクリーンショット 2022-01-25 22.11.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2345137/660be9d7-3a36-6d57-8fc4-0dd893ed68af.png)
いったー！データベース作成でドキドキしすぎた。ふう〜。
このあとデータモデル実装も大事なとこだけど、MPが尽きたから今日はこのへんで！
（それにしても4−18長いなー。めちゃ実装量あるじゃん。。うーん鬼畜回の悪寒！！）
DynamoDB ローカル環境(LocalStack)起動時にテーブルを作成する

[先日の記事](https://qiita.com/rkunihiro/items/646d9c39923cf8b23533)で紹介した LocalStack Docker コンテナを利用したローカル AWS 環境に DynamoDB サービスを追加して
コンテナ起動時にテーブルを自動作成するようにする方法です

## Docker の設定

```yaml:docker-compose.yml
version: '3.8'

services:
  localstack:
    container_name: localstack
    image: localstack/localstack:0.13.3
    environment:
      SERVICES: dynamodb
      INIT_SCRIPTS_PATH: /docker-entrypoint-initaws.d
      DEFAULT_REGION: 'ap-northeast-1'
    ports:
      - '4566:4566'
    volumes:
      - ./init:/docker-entrypoint-initaws.d
      - ./json:/json
```

-   環境変数(environment)
    -   `SERVICES` に `dynamodb` を追加
    -   `INIT_SCRIPTS_PATH` はコンテナ起動時に自動的に実行されるシェルスクリプトのディレクトリを指定します
-   volumes
    -   ホスト側の init ディレクトリをコンテナの /docker-entrypoint-initaws.d (環境変数`INIT_SCRIPTS_PATH`で指定したパス)にマウントしています
    -   ホスト側の json ディレクトリをコンテナの /json にマウントしています

## テーブル定義 JSON

テーブル定義 JSON ファイルを作成し、json ディレクトリに保存

```json:json/todo.json
{
    "TableName": "todo",
    "AttributeDefinitions": [
        {
            "AttributeName": "id",
            "AttributeType": "S"
        }
    ],
    "KeySchema": [
        {
            "AttributeName": "id",
            "KeyType": "HASH"
        }
    ],
    "ProvisionedThroughput": {
        "WriteCapacityUnits": 5,
        "ReadCapacityUnits": 10
    }
}
```

以下のコマンドで JSON ファイルのスケルトンを生成することができます

```sh:
aws dynamodb create-table --generate-cli-skeleton
```

## テーブルを作成する初期化スクリプト

cli-input-json オプションでテーブル定義 JSON ファイルを指定して
create-table を実行するスクリプトを作成し init ディレクトリに保存

```sh:init/create-dynamodb-table.sh
#!/bin/bash

# テーブルを作成
awslocal dynamodb create-table --cli-input-json file:///json/todo.json

# テーブル一覧を表示
awslocal dynamodb list-tables
```

## コンテナ起動

```sh:
docker compose up -d
```

## CLI でアイテムを登録・取得してみる

### localstack コンテナ内で実行

```sh:
# コンテナ内のシェルを実行
docker compose exec localstack bash
```

```bash:
# アイテムを登録
awslocal dynamodb put-item --table-name todo --item '{"id":{"S":"0001"},"title":{"S":"Qiita記事を書く"}}'

# アイテムを取得
awslocal dynamodb get-item --table-name todo --key '{"id":{"S":"0001"}}'
```
[JS](基礎)関数について

アウトプットとして

Rubyでいうところのメソッドを、JavaScriptでは関数と呼びます。
この関数ですが、いろんな記述の方法があるので整理するために記事にしたいと思いました。

##いろいろな関数定義
### 関数宣言(function)
JavaScriptでは`function 関数名(){ 処理 }`と記述することで関数を定義することができます。

```javascript
function 関数名(引数) {
  // 処理
}
```
```javascript:例
function calc(num1,num2){
  return num1*num2
}

const num1 = 3
const num2 = 4
console.log(calc(num1,num2)) // => 12
```


###無名関数
無名関数は、関数名なしで関数を定義することができます。より簡潔なコードが記述できるという点がメリット。

```javascript
const hello = function(){
  // 処理
}
```
```javascript:例
const calc = function(num1,num2){
  return num1*num2
}

const num1 = 3
const num2 = 4
console.log(calc(num1,num2)) // => 12
```

###即時関数
関数を定義すると同時に実行される構文。関数を定義してから呼び出すという手間を省くことができます。

```javascript
(function 関数名(引数) {
  // 処理
})(呼び出すための引数)
```

```javascript:例
(function countNum(num) {
  console.log(num)
})(1) // => 1
```

`()の中にfunctionからはじまる関数定義そのものを配置する`ことで、その関数を即実行するということができるようになります。

###アロー関数
functionの記述を省略し、その代わりに`（）=>`という記述によって関数を定義する構文です。より短い記述で関数定義をできるという点がメリット。

このアロー関数を少し深掘りしてみた記事がこちら→[アロー関数を少しだけ深掘りしてみた](https://qiita.com/Shi-raCanth/items/f3774f2648afd688008d)

```javascript
// 無名関数
const 変数名 = function(){
  処理
}

// アロー関数
const 変数名 = () => {
  処理
}
```

```javascript:例
const countNum = (num) => {
  console.log(num)
}
countNum(1) // => 1
```

##それぞれの関数の特徴まとめ
|関数定義の種類|特徴|
|-----------|---|
|関数宣言|標準的な関数の定義|
|無名関数|関数を多く使用するコードであるときに使用する。関数名の重複を避けることができる。|
|即時関数|流用する可能性のない関数を定義するときに使用する。別途関数を定義する手間がない。|
|アロー関数|無名関数または即時関数において、より省略した記述をしたい時に使用する。|

##JavaScriptの関数における戻り値
JavaScriptでは関数の戻り値をreturnを用いて明示する必要があります。


```javascript:returnを記述していない例
function calc(num1, num2){
    num1 * num2
}

num1 = 3
num2 = 5
console.log(calc(num1, num2))
// => undefined（戻り値が取得できてない）
```


```javascript:returnを記述した例
function calc(num1, num2){
   return num1 * num2 //戻り値を得るためにreturnを記述
}

num1 = 3
num2 = 5
console.log(calc(num1, num2))
// => 15
```

##まとめ
関数名などは覚える必要はないと思いますが、どういう書き方があってどういう特徴があるのかは覚えておきたいです！

※補足等ありましたらコメントいただけると幸いです。
【合格】Azure Fundamentals（AZ-900）勉強方法

データ分析基盤を整える業務で利用するクラウドがAzureに決定されたため、業務が始まるまでにAzureのことを知っておこうと思い、受験しました。クラウドの経験はそもそもなく、初心者の状態です。Azure Fundamentalsは、Azure系資格の中で最も難易度が低い試験です。

https://docs.microsoft.com/ja-jp/learn/certifications/exams/az-900

https://mta.odyssey-com.co.jp/outline/AZ-900.html

Microsoftの「AZ-900」という試験に合格（700点以上）すると、Azure Fundamentalsが認定されます。簡単な試験と思ってかなり油断して受験してしまいましたが、思いのほか難しかったです。試験が始まってから分からない問題に連続して当たってしまい、結構焦って、落ちたかも…と思っていたので、心臓に悪かったです。もっとしっかり勉強していけばよかった。

# 受験状況
- 勉強期間：10日
- 受験日：2020年4月
- 使用教材
    - 市販テキスト [合格対策 Microsoft認定 AZ-900 Microsoft Azure Fundamentalsテキスト&問題集](https://www.amazon.co.jp/%E5%90%88%E6%A0%BC%E5%AF%BE%E7%AD%96-Microsoft%E8%AA%8D%E5%AE%9A-AZ-900-Microsoft-Fundamentals%E3%83%86%E3%82%AD%E3%82%B9%E3%83%88/dp/4865942319)
    - Microsoft公式サイト [ラーニングパス「Azureの基礎」](https://www.amazon.co.jp/%E5%90%88%E6%A0%BC%E5%AF%BE%E7%AD%96-Microsoft%E8%AA%8D%E5%AE%9A-AZ-900-Microsoft-Fundamentals%E3%83%86%E3%82%AD%E3%82%B9%E3%83%88/dp/4865942319)
- 費用合計：15700円
    - 市販テキスト：2500円
    - 受験料：13200円

# 使用教材と勉強方法
![20211201115724.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/927088/3c8f4637-ddd5-9bbe-005f-7a08fca1bc27.jpeg)


- Azure Fundamentalsテキスト＆問題集

当時は試験が始まったばかりで、このテキストが唯一の市販本でした。この市販本はコアな知識がギュッとまとまっていて、初心者にとって非常にわかりやすく、導入としては最適な本です。巻末に問題もついているので、知識の確認もできます。

ただし、これ1冊ではカバーしきれません。巻末の問題も、本番試験と比較して簡単すぎます。最初の1冊としてこなした後、別の教材をプラスする必要があります。

Microsoftの公式サイトでは、無料で学べるラーニングパスが公開されています。AZ-900の範囲の知識もまとまっているので、一読しました。

- [Azure の基礎 第1部:Azure の主要概念に関する説明 (AZ-900) - Learn | Microsoft Docs](https://docs.microsoft.com/ja-jp/learn/paths/az-900-describe-cloud-concepts/)
- [Azure の基礎 第2部:Azure の主要サービスに関する説明 (AZ-900) - Learn | Microsoft Docs](https://docs.microsoft.com/ja-jp/learn/paths/az-900-describe-core-azure-services/)
- [Azure の基礎 第3部:Azure のコア ソリューションおよび管理ツールに関する説明 (AZ-900) - Learn | Microsoft Docs](https://docs.microsoft.com/ja-jp/learn/paths/az-900-describe-core-solutions-management-tools-azure/)
- [Azure の基礎 第4部:一般的なセキュリティ機能およびネットワーク セキュリティ機能に関する説明 (AZ-900) - Learn | Microsoft Docs](https://docs.microsoft.com/ja-jp/learn/paths/az-900-describe-general-security-network-security-features/)
- [Azure の基礎 第5部:ID、ガバナンス、プライバシー、およびコンプライアンス機能に関する説明 (AZ-900) - Learn | Microsoft Docs](https://docs.microsoft.com/ja-jp/learn/paths/az-900-describe-identity-governance-privacy-compliance-features/)
- [Azure の基礎 第6部:Azure Cost Management およびサービス レベル アグリーメントに関する説明 (AZ-900) - Learn | Microsoft Docs](https://docs.microsoft.com/ja-jp/learn/paths/az-900-describe-azure-cost-management-service-level-agreements/)

本来であれば、この後に問題演習を積む必要があります。受験当時はテキストが他に何も販売されていなかったので私はこのまま突撃してしまいましたが、現在では黒本などありますので、必要に応じて補うことをおすすめします。

# 試験結果
- 合格
- 752点（合格基準は700点）

![20211201125901.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/927088/2b946f1c-498e-8128-97bc-762f4983f69c.jpeg)

![20211201194100.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/927088/74e1a202-d35f-3b3d-da4a-e0e392632eb6.png)

クラウドの基礎知識が学べたので、入門としてはよかったです。実務上、直接的に役立つかと言われるとちょっと疑問は残りますが、一通りさらっていたことで上司の会話にもついていけましたし、その範囲内では役立ったと感じます。
Ubuntu Server 20.04.3 LTS 構築#1 初期設定1

Ubuntu Server 20.04.3 LTSを使用して、構築の練習
まずはOSのインストールについて記録

実施日:2022/01/16
使用端末
ThinkCentre M72e Tiny 3856CTO(以下、端末とする)

CPU:Celeron G465 (1.9GHz)
メモリ:4GB
ストレージ:500GB(SanDisk製)

Ubuntu20.04.3を下記のサイトからダウンロード
[ubuntu japanese team]
[ubuntu japanese team]:https://jp.ubuntu.com/download

ダウンロードしたものをポータブルSSDに保存
端末のUSBポートに接続し、ブートしてインストール画面に
表示される項目に沿って進める。

【インストール内容】
言語:英語
IPアドレス:DHCP(後々、固定IPアドレス設定)
プロキシサーバー:設定なし
ミラーサイト:デフォルト値
LVM:あり(設定値はデフォルトのまま)(暗号化なし)
サーバー名/ユーザー名を設定
OpenSSH:なし
Featuerd Server Snaps:選択なし
後々、コマンド使用しての設定をしたいため、
今回のインストールではデフォルト値で実施。

問題なく、インストール完了。

次回
構築#2　初期設定2
【初心者の方へ】UiPathのプロパティー周りのクセ

#1.はじめに
みんさんにこんにちは。
UiPathで開発をやっていると、ロジックはあっているのに何故か動かないなんてことを
経験することがあります。
原因はいろいろ考えられますが、まず私がUiPathを始めたばかりの時に、
対処に若干てこずらされたことを紹介したいと思います。
ここを意識して開発ができていれば、もっと楽に開発が進められただろうなと思う
内容を紹介します。
UiPathのクセのような部分ですね！
これからUiPathを始めるという方や、始めたての方に
役に立ったらうれしいです。


#2.Simulate,SendWindowMessage,Defaultの設定
まずはベタなところから。
クリックアクティビティーなどを使用する際に、選択可能なプロパティーに、
Simulate,SendWindowMessage,Defaultがあります。
特に意図がなければSimulteを使用することをお勧めします。
ですが、システムによっては、SimulateやSendWindowMessageでは
動かないシステムも存在しています。
これらのプロパティーを持つアクティビティーでエラーが出てしまったら、
扱うシステムに対してSimulate,SendWindow,Defaultのうち動作する設定になっているか
確認してみてください。
以外と、UiPathを始めたての時は、いろんな原因を考えてしまい、
Simulate,SendWindow,Default設定をミスっていることに気づかなかったりします。

**Simulate,SendWindow,Defaultの設定を選べるアクティビティーを
使用するときはまず、これらの設定が正しいことを初めに確定することをお勧めします。**

ちなみに、TypeIntoアクティビティーで、SimulateがNGだと下記のようなエラーが
でました。
*Source: Type Into SimulateTest
Message: The target element is disabled. Set 'AlterIfDisabled' to 'True' to enable interaction with disabled elements.
Exception Type: UiPath.Core.ElementOperationException*




#3.WaitForReadyの設定
次は、WaitForReadyですが、こちらもクリックアクティビティー、TypeInto等の
アクティビティーのプロパティー項目として存在します。
WaitForReadyは、Complete、Interactive、Noneの3つが選べます。
一般的には、動作が安定しやすいため、
Complete(対象画面のUI要素がすべて表示されてから動作を行う設定)を
使用することが多いのではないかと思います。
現場によっては、Completeを基本設定とするルールがあると思います。
しかし、こちらはCompleteにしていると、処理が全く先に進まないことがあります。
何故なら、処理対象画面が完全に表示されているように見えて、
ロボットでは表示されていない判定をすることがあるからです。
(詳細な原因はわかりませんが、レスポンス系統に不備のある業務システムでよく発生する印象です。
例えば、画面の一部がまれにうまく表示されないシステムなど（笑）)
この場合は、WaitForReadyをInteractiveもしくはNoneに変更すると、正常に動作します。

**何故か動作が先に全く進まないけど、エラーも出ない場面に直面した時は、WaitForRedyの設定を疑ってみてください。**

#4.SendHotKeyアクティビティーのDelayBefore,DelayAfterの設定
指定したキーを入力するアクティビティーである、SendHotKeyを使用している時、
何故かうまくキーが入力されないということがあります。
そんな時には、DelayBefore,DelayAfterに500msec程度(任意)を設定してみると良いと思います。
そうすると、無事入力されるという現象に多々遭遇しました。
予測として、ロボットのキー入力の動作が速すぎて、うまくシステムに入力されない現象が存在
していると考えています。

**ルールとして、SendHotKeyのDekayAfterとDelayBeforeには500msec程度を
設定して使用すると決めておいても良いかと思います。**

#5.まとめ
まだまだたくさんのUiPathプロパティー周りのクセがあるような気がしますね、、、
全部は紹介しきれていませんが、
ここで紹介したことが、初心者の方の開発の後押しになれば幸いです。
Dynamics 365 Finance and Operations ~ Small Parcel Shipping（小包・宅配）を利用する ~

2022/1/24 に発表されたニュースで、FedEX と Microsoft が LaaS（Logistics as a service）ということで、D365 を含むクロスプラットフォームによる、コマース・SCM・物流の分野でさらに強化していくことになったようです。2022年後半に D365 OIM で機能がリリースされるようです。

https://news.microsoft.com/2022/01/24/fedex-and-microsoft-announce-new-cross-platform-logistics-solution-for-e-commerce/

すでに、D365 FO では、Small Parcel Shipping という機能があり、truckloadより少ないLTLでの少量配送のために、FedEX、UPS、DHLなどの配送業者のレートエンジンを活用することができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2375815/ab83de0d-813e-9101-2c84-d94533c56bd7.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2375815/f1f97f57-2203-b00a-f9a9-817f55bb13c9.png)


エンジンは各自カスタマイズ（新規Model）を作成いただく必要がありますが、こちらにデモエンジン（dll）があるので、設定とともに試してみてください。

https://docs.microsoft.com/ja-jp/dynamics365/supply-chain/warehousing/small-parcel-shipping

こんな感じでデータを取ってこれます。このRateは諸掛として販売注文に加算されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2375815/7bc28ed6-2871-6a42-0b20-05ea26b14562.png)
また、ZPLプリンターに印刷することもできますので、これを荷物の段ボールに貼り付けましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2375815/0d0d053a-576a-6f52-3054-c275bea8041a.png)
HTMLで画像が歪んだ時の対処法

## 現状の問題

- トップページ（HTML）の画像が歪む
以下の画像のように横幅が合わせるため画像が歪む
![ genjou](https://user-images.githubusercontent.com/61651779/150978470-33b6a8f8-142b-4d4a-8d2c-fc932f334370.png)

### 理想
adobe XDで作成した以下画像のように歪まないようにしたい
![risou](https://user-images.githubusercontent.com/61651779/150979973-ff6729ca-844d-4cec-9606-678d871c0799.png)

## 理想に近づけるために
インストラクターの方に方法と参考サイト両方を教わり、以下方法を実行
figureタグに[overflow:hidden](はみ出した部分を隠す)
imgタグに[object-fit: cover;]（中央でトリミングしてくれるらしい）
以下コード

```html
<div class="contents_item">
  <figure class="item_img"><!--overflow:hidden-->
    <img src="images/event_yamagata.png" alt=""><!--object-fit: cover;-->
  </figure>
  <div class="item_title">
   <p>
    グッズ展開催in山形
   </p>
  </div>
  <div class="item_genre">
    <div class="genre_flex">
      <p>開催場所：</p>
      <p>山形県</p>
    </div>
    <div class="genre_flex">
      <p>期間:</p>
      <p>2021/4/5~5/5</p>
    </div>
  </div>
  <div class="item_botton">
    <a class="item_click" href="#">
      <p>詳細はこちら</p>
    </a>
  </div>
</div>
```

```css
.item_img {
    width: 252px;
    height: 214px;
    overflow: hidden; /*ここ*/
    margin: 0 0 20px 0;

    display: flex;
    justify-content: center;
}

.item_img img {
    width: 100%;
    height: 100%;
    object-fit: cover; /*ここ*/
}
```

## 結果
歪みがなくなりました！
（以下HTMLファイルの画像）
![kekka](https://user-images.githubusercontent.com/61651779/150982123-d60483ec-dc66-4afe-bfb2-721336a8895d.png)

## 参考サイト
[1行追加でOK!CSSだけで画像をトリミングできる「object-fit」プロパティー](https://www.webcreatorbox.com/tech/object-fit)
【合格】Java Silver（Java SE 8 Programmer I）勉強方法

職業訓練校でJavaをせっかく学んだので、形にするために受験しました。受験料が高すぎて（26600円）個人的には全然薦めてないです。自分が受験したときは8でしたが、現在は11にバージョンアップされています。そして、受験料がなんかさらに上がったような…？

[Java SE 11 Programmer I (1Z0-815-JPN) 試験 | Oracle University](https://www.oracle.com/jp/education/certification/1z0-815-jpn-48261-ja.html)

# 受験状況
- 勉強期間：1か月程度
- 使用教材：[徹底攻略 Java SE 8 Silver 問題集[1Z0-808]対応（＝黒本）](https://www.amazon.co.jp/%E5%BE%B9%E5%BA%95%E6%94%BB%E7%95%A5-Java-Silver-%E5%95%8F%E9%A1%8C%E9%9B%86-1Z0-808/dp/4844339931)
- 費用合計：30100円
    - 黒本：3500円
    - 受験料：26600円（受験当時）

# 使用教材と勉強方法
![20211201013703.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/927088/56c38487-1ae9-2b10-65dc-412b9715ce7f.jpeg)

- Java Silver黒本

黒本一択です。これしかやってません。巻末の模擬試験をやり込めるか否かが合格ラインです。1章ずつ進めていき、最後に模擬試験を3周しました。模擬試験は解説を確認し、細かいところまでできるようにして行った方がいいです。

簡単だと評価する先輩方も多いですが、初心者にとっては結構難しい試験だと思います。試験料も高いですし、油断しない方がいいです。

# 試験結果
- 合格
- 得点率88％（合格ライン65％）

![20211201215720.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/927088/e1b6096b-4bdf-5278-fe70-fcdbcc3dccb4.png)

![20211201125908.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/927088/762f41b3-0070-979d-b320-b609418b0b88.png)
メディアクエリ（@media）の書き方は色々あるけど、あんまり気にしなくてOK【screen all device-width only】

古いサイトや他人様の書いたコードを見ると「screen」「all」「device-width」「only」など、知らないメディアクエリの書き方が結構あったので念のため調べてみた。

## 結論
一般的な環境で閲覧されるサイトであれば、基本的には`@media(max-width:500px){} `といった一般的な書き方でOK。

## 他の色々な書き方
### @media screen and
@mediaの後に screen and とか all and と書いてあるのをたまに目にする。
実は、「結論」に載せた
`@media(max-width:500px){} ` という書き方は、
`@media all and (max-width:500px){}` を省略したものらしい。

allの部分はデバイスの種類を指定できるようになっており、
all（すべてのデバイス）の他に、print（プリンター）やspeech（スピーチシンサイザ）や
screen（printやspeechを除いたもの全て。一般的なＰＣやスマホ。）がある。

なので、例えば
`@media screen and (max-width:500px){} `は
デバイスの種類がscreenでなおかつその幅が500px以下の場合を表す。

つまり、プリンタやスピーチシンセサイザを無視するなら
allもscreenもほぼ変わらないはずなので
やはり「結論」に書いたとおり
`@media(max-width:500px){}`でＯＫということになる。

### max-widthとmax-device-width

max-widthはブラウザの幅。max-device-widthはデバイス自体の幅。
なので、`@media (max-device-width:1000px)`と指定した場合
デバイス自体の画面幅が1001px以上ならブラウザの幅を手動で小さくしても効かない。

### @media only

古いcss等だと@media only screen and ～　といった感じで only　が付く場合がある。

これはメディアクエリに対応してない古いブラウザ（IE8とかなので10年以上前？）用に使ってた書き方なので、現在では必要なし。

## 改めて結論
基本的には`@media(max-width:500px){} `でOK
Docker Desktop for WindowsをやめてWSL2上のDocker Engineへ移行する

### はじめに

そろそろ移行するべ[^1]と起動手順とかのスクリプト設定を自分で書いてみた。せっかくだから Qiita に投稿しようかなと思って検索したら、やはり皆さん同じことを考えているようで大体同じ感じでした。二番煎じなんだろうけど若干異なる部分もあえて投稿してみます。

なお、

- WSL2 側のディストリビューションは Ubuntu 20.04
- 自分はGUI操作を行わないので不要なので代替UIは不要
- k8s も使わないのでk8sは不要

という感じです。

[^1]: Docker Desktopが有料化、2022年1月31日までが猶予期間 - [Docker is Updating and Extending Our Product Subscriptions](https://www.docker.com/blog/updating-product-subscriptions/)

### 削除手順

Docker Desktop は普通にコントロールパネルから削除する。

WSL2 側に Docker Desktop が設定したと思われる docker グループが作成されていると思うが、これは後で使うのでそのまま残しておく。

```sh
$ cat /etc/group | grep ^docker
docker:x:1001:username
```

### Docker Engine インストール

こちらも通常のUbuntuへのインストール手順で

https://docs.docker.com/engine/install/ubuntu/

### 起動設定

今現在の WSL2 は Systemd などからサービス起動が行われないので、シェル起動時に実行する。`~/.bash_profile` に記載したいが他のシステムとドットファイル郡を同じにしたいというのもあるので、WSL2 かどうかを確認し、Docker の PIDファイルが用意されていなければ起動するというようにした。

```sh
# WSL2 + Docker
if [[ "$(uname -a)" =~ ^Linux.*-microsoft-standard.*GNU\/Linux ]]; then
  if [ ! -f "/var/run/docker.pid" ]; then
    sudo /usr/sbin/service docker start
  fi
fi
```

これだけだと、`sudo`時にパスワードを聞かれるので使い勝手が悪い。

ここで、Docker Desktopが残してくれた docker ユーザーグループがあるので、dockerグループ(`%docker`)のみサービス起動のパスワードを聞かない(`NOPASSWD`)ようにsudoersに設定する。

```sh
$ sudo visudo -f /etc/sudoers.d/docker
```
```sh
%docker ALL=(ALL) NOPASSWD: /usr/sbin/service docker start
```

### Docker Compose

Ubuntu の apt で入るものは古いので brew で入れる。[Homebrew](https://brew.sh/index_ja) が入ってない人はまずそこから。

```sh
$ brew install docker-compose
```

ということで、自分が使う範囲ではOKになりました。
Power Automate: Teams のチャットでも「リンクのコピー」を取る。ついでに各種 ID も取得して Automate 開発の助けを

# 背景
Teams で「リンクのコピー」が徐々に増えていっているのは有難いことですが、未だに「チャット」では取得出来ません。

ということで、昔から使ってる「リンクのコピー」生成 Automate を共有

こんな感じで、チャネルには有る筈のメニューが無い
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/120072/4014e148-65b8-f7e7-8e0a-532fa9d5aab8.png)

# Automate で実現することによる利点
- チャットでもリンクが取れるので、「メッセージ保存」「タスクの生成」以外の選択肢が作れる
- アクセス権がある人であれば、対象メッセージを共有しやすくなる。
    - チャットの「返信」は便利ですが、チャネルだとコピペして ">" 引用しかないので。
- 検索した際に、前後メッセージがみたいのに表示されない！って時に使う事で、前後メッセージもみられる

# 実際の動作例
1. リンクの欲しいメッセージを表示（検索結果でもOK）<br>![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/120072/6d6c0a6b-54f0-d3d3-6066-dabc44731432.png)
1. 対象メッセージで右クリックメニュー（検索結果でも使えるのがポイント）<br>![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/120072/dde67d4f-7451-64b2-2f10-0acf835f8561.png)
1. Submit を押して起動<br>![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/120072/42458784-f778-7fc9-4ffc-1973e3528d1b.png)
1. リンクが Automate Bot から送付されてきます。<br>![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/120072/4e19c2e6-d055-464c-d233-c36d4f2f16ea.png)

# 概要
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/120072/fa3cb975-6d78-b674-ec49-7b0bad886e5c.png)

特に難しいことはしてなくて
1. Teams メッセージでの Trigger
1. 結果を編集して、Compose へ
1. あとは、Compose.Outputs を Post するだけ

```html:メッセージ表示用
<p><a href="@{triggerBody()?['entity']?['teamsFlowRunContext']?['messagePayload']?['linkToMessage']}">リンク元へ</a></p>
<p>@{triggerBody()?['entity']?['teamsFlowRunContext']?['messagePayload']?['subject']}</p>
<blockquote>@{triggerBody()?['entity']?['teamsFlowRunContext']?['messagePayload']?['body']?['content']}</blockquote>
<table border = "1" cellspacing="1"  align="center" style="border: 1px ;color:silver;">
	<tr style="vertical-align: middle;">
		<td>Team ID</td>
		<td><p>@{triggerBody()?['entity']?['teamsFlowRunContext']?['channelData']?['team']?['aadGroupId']}</p>	
        </tr>
	<tr  style="vertical-align: middle;">
		<td>Channel ID</td>
		<td><p>@{triggerBody()?['entity']?['teamsFlowRunContext']?['channelData']?['channel']?['id']}</p></td>
	</tr>
	<tr  style="vertical-align: middle;">
		<td>Message ID</td>
		<td><p>@{triggerBody()?['entity']?['teamsFlowRunContext']?['messagePayload']?['id']}</p></td>
	</tr>
	<tr  style="vertical-align: middle;">
		<td>Conversation Name</td>
		<td><p>@{triggerBody()?['entity']?['teamsFlowRunContext']?['conversation']?['name']}</p></td>
	</tr>
	<tr  style="vertical-align: middle;">
		<td>Conversation ID</td>
		<td><p>@{triggerBody()?['entity']?['teamsFlowRunContext']?['conversation']?['id']}</p></td>
	</tr>
	<tr  style="vertical-align: middle;">
		<td>Is Group</td>
		<td><p>@{triggerBody()?['entity']?['teamsFlowRunContext']?['conversation']?['isGroup']}</p></td>
	</tr>
</table>
```


# ソースは、以下から取得して、Zip 化して Import
https://github.com/baku2san/PowerAutomateTools

# あとがき
Automate の Adaptive Card Editor に問題があって、現在編集出来ないみたいで、その辺りは説明省略したけど・・
Version 1.5 対応でもっと便利になってくれると期待 :laughing:
Office365やPower Platformのアイコン素材

Office365やPPFを使った社内システムを1枚ペラで説明する資料をつくる機会があり、
サービスのアイコンの所在を検索したのでその時の取得場所の備忘録です。


https://icons8.jp/icons/set/microsoft-office

https://docs.microsoft.com/ja-jp/microsoft-365/solutions/architecture-icons-templates?view=o365-worldwide

https://docs.microsoft.com/ja-jp/power-platform/guidance/icons

https://docs.microsoft.com/ja-jp/azure/architecture/icons


https://office365atwork.com/o365/microsoft-office-365-logos/
Laravelでマイグレーションしようとすると「Class "Doctrine\DBAL\Driver\AbstractMySQLDriver" not found」と出る

備忘録として。

## エラー内容
テーブルのカラム内容を変更しようとして、変更用のマイグレーションファイルを作成しました。
しかし、マイグレーションを実行しようとするとなぜかそこファイルだけ実行時にコケてしまう。

```
$ php artisan migrate
Migrating: 2022_01_11_205136_change_work_statuses_table_column

   Error 

  Class "Doctrine\DBAL\Driver\AbstractMySQLDriver" not found

  at vendor/laravel/framework/src/Illuminate/Database/PDO/MySqlDriver.php:8
```

## 解決策

該当のライブラリを新しくする

```
$ composer require doctrine/dbal
```

再度マイグレーションを行う

```
$ php artisan migrate
Database seeding completed successfully.

```
CDP Nifi安装

# 概要

安装nifi在cdp上。然后发现nifi或者说cfm，cdp并不直接带。


# 前期准备

想要完成安装，需要完成两件事：

1. 在add service里面添加nifi
1. 在parcels里面添加cfm（nifi和nifiregister）

## 在add service里面添加nifi

https://docs.cloudera.com/cfm/2.1.2/download/topics/cfm-download-locations.html

```
cd ~
mkdir tools
cd ~/tools
wget https://username:password@archive.cloudera.com/p/cfm2/2.1.2.0/redhat7/yum/tars/parcel/NIFI-1.13.2.2.1.2.0-283.jar
sudo mv NIFI-*.jar /opt/cloudera/csd
cd /opt/cloudera/csd
sudo chown cloudera-scm:cloudera-scm ./*
sudo chmod 644 ./*
sudo service cloudera-scm-server restart
```
username:password 需要license。所以，试用版是不能装nifi的

## 在parcels里面添加cfm

照着下面的链接做，另外别忘在Parcel Repository & Network Settings 里面设置

https://docs.cloudera.com/cfm/2.1.2/deployment/topics/cfm-add-parcel-url.html

- HTTP authentication username override for Cloudera Repositories
- HTTP authentication password override for Cloudera Repositories

https://archive.cloudera.com/p/cfm2/2.1.2/redhat7/yum/tars/parcel/

# 安装nifi

照着下面的链接做

https://docs.cloudera.com/cfm/2.1.1/deployment/topics/cfm-add-nifi-base.html

## Troubleshooting

### 现象
Exception: Failed to create ranger repository 'Cluster1_nifi', response code is '401', output is '{"statusCode":401,"msgDesc":"Authentication Failed"}'.

``` 
+ exec /var/run/cloudera-scm-agent/process/1546336147-nifi-NIFI_NODE/scripts/control.sh
Traceback (most recent call last):
  File "/var/run/cloudera-scm-agent/process/1546336147-nifi-NIFI_NODE/scripts/common-scripts/ranger.py", line 725, in <module>
    main()
  File "/var/run/cloudera-scm-agent/process/1546336147-nifi-NIFI_NODE/scripts/common-scripts/ranger.py", line 661, in main
    keytab=args.keytab, principal=args.principal)
  File "/var/run/cloudera-scm-agent/process/1546336147-nifi-NIFI_NODE/scripts/common-scripts/ranger.py", line 330, in create_ranger_repository
    _create()
  File "/var/run/cloudera-scm-agent/process/1546336147-nifi-NIFI_NODE/scripts/common-scripts/ranger.py", line 319, in _create
    ranger_service_name, code, out))
Exception: Failed to create ranger repository 'Cluster1_nifi', response code is '401', output is '{"statusCode":401,"msgDesc":"Authentication Failed"}'.
```
### 调查

```ruby
sudo tail /var/log/cloudera-scm-server/cloudera-scm-server.log -f
```

### 原因
原因是nifi和ranger通信的时候出现了401.
所以不用ranger的话直接关掉就可以通过，然后如果需要ranger的话，需要设置kerberos
リソースレコードの種類

権威DNSサーバーとフルサービスリゾルバの2種類があります。
権威DNSサーバーは、ゾーンという単位でドメイン名とIPアドレスを管理しています。
ゾーンの中には、レコードとという単位でドメイン名とIPアドレスを紐付ける情報を保持しています。

下表のレコードをリソースレコードと呼びます。

|リソースレコード|説明|
|---|---|
|Aレコード|address recordの略。ドメイン名とIPv4アドレスを紐付けるレコード|
|AAAAレコード|quad a recordとも呼ばれる。ドメイン名とIPv6アドレスを紐付けるレコード|
|PTRレコード|pointer recordの略。IPアドレスとドメイン名を紐付けるレコード|
|NSレコード|Name Serverレコードの略。他の権威DNSサーバーの場所を保持するレコード|
|SOAレコード|Start of authority recordの略。権威DNSサーバーのゾーンの情報を保持するレコード|
|CNAMEレコード|Canonical name recordの略。AレコードやAAAAレコードの別名を示すレコード|
|エイリアスレコード|Route53独自のレコード。CNAMEレコードと違い、別名からIPアドレスのように１回のクエリで取得できる。AWS内のリソースに限り指定できる。|
Laravelのディレクティブや変数をVueコンポーネントで使いたいときはslotが便利

環境：vue2

Laravel側のディレクティブや変数の内容をVueのコンポーネントの中で使いたい場合、
propsで頑張るのではなく、slotを使ったほうがシンプルに実装できるかも。
↓こんな感じ（必要な個所以外は省略しています）

```html:blade側
<side-menu>
      @auth
      <ul class="side-menu-inner">
        <li><a href='{{ route("users.show", ["uid" => Auth::user()->uid]) }}'>マイページ</a></li>
        <li><a href='{{ route("users.info") }}'>通知</a></li>
      @endauth
      @guest
      <ul class="side-menu-inner">
        <li><a href='{{ route("users.register") }}'>新規登録</a></li>
        <li><a href='{{ route("users.login") }}'>ログイン</a></li>
      </ul>
      @endguest
</side-menu>
```

```vue:SideMenu.vue
<template>
    <div>
       <slot></slot>
    </div>
</template>
```
特に`@guest`と`@auth`の出し分けなんかは、slot無しでやろうと思ったらbladeからAuth::check()の結果をvueにpropsで渡して、v-ifで出し分けて～とかになってしまうので。

なお、Laravelからjsへの値の受け渡し方法については、下記記事様がわかりやすくまとめてくださってます。

https://qiita.com/ntm718/items/638a8f56d30d047ead0f
RTX3080TiがPyTorchから認識されなくて困った件

#起こった問題
RTX3080TiがPyTorchの最新バージョンの1.10.1から認識されなくて困った。CUDAは11以降を使っている。

## 動作環境
- Ubuntu20.04上のdockerコンテナ内で作業
- dockerイメージ: nvidia/cuda:11.3.0-devel-ubuntu20.04
- cuda: 11.3
- cuDNN: 上記cudaに対応したもの
- Nvidia driver: 495.46


Webで何件が見つけたこのエラーが出てしまう。

```
GeForce RTX 3080Ti with CUDA capability sm_86 is not compatible with the current PyTorch installation.
The current PyTorch install supports CUDA capabilities sm_37 sm_50 sm_60 sm_70 sm_75.
If you want to use the GeForce RTX 3080Ti GPU with PyTorch, please check the instructions at https://pytorch.org/get-started/locally/
```

PYTorchから対応しているバージョンを出してみてもsm_70までしか利用できないようだ。RTX3080Tiを使うにはsm_86が必要。



```
import torch
print(torch.cuda.get_arch_list())
['sm_37', 'sm_50', 'sm_60', 'sm_70', 'sm_75']
```

#解決方法
いろいろ試した結果、PyTorchのPreview版を使うことで解決

こちらのサイトでPreviewを選ぶとStableよりも新しいバージョンのインストールコマンドが出てくるので、そちらを利用した（表示されたエラーメッセージは正しかった）
https://pytorch.org/get-started/locally/

動いている環境での、インストールされているPyTorchのバージョンは以下のようになっていた。

```
# pip list | grep torch
torch                              1.11.0.dev20220124+cu113
torchaudio                         0.11.0.dev20220124+cu113
torchvision                        0.12.0.dev20220124+cu113
```

torch.cuda.get_arch_list()の結果も変わっていた。

```
import torch
print(torch.cuda.get_arch_list())
['sm_37', 'sm_50', 'sm_60', 'sm_70', 'sm_75', 'sm_80', 'sm_86']
```
PAOの事

昔々FreeBSD 2の頃に日本にはノートパソコンなどのモバイル環境でFreeBSDを使うための[PAO](http://www.jp.freebsd.org/PAO/index-j.html)というパッケージ(ディストリビューション)がありました。

当時のノートパソコンは486や初期のpentiumでpccardがほぼ標準で付いていました。このpccard(PCMCIA)のサポートを強化したものがPAOでした。今から思うと不思議なくらい日本はFreeBSDの開発者がいました。

pccardのサポートはAndrew McRaeさんが作ったものをPAOチームがいろいろなデバイスの確認や対応を行っていったと流れだったようです。

まだサイトは残っているところもありますが、再構築はほぼ不可能です。

表向きにはPAOはメインラインにマージされたという事になっていますが、実質的には使えるところは使って、再実装されたというほうが正しいと思います。

力技で実装したためかドライバはpccard,pccbb,cardbus,excaとディレクトリが分かれていて、見通しの悪いセンスの悪いものになっています。

PAOの頃はCIS(Card Information Structure)はpccardcというコマンドでダンプできたのですが、dumpcisコマンドに変えられました。dumpcisのmanページにpccardcの事がまったく触れられてないのは、悪意を感じてしまいます。

FreeBSD 3ではa.outからELFへの変更やカーネルモジュールの導入など大きくコードが変わりました。

それに伴いPAOとnewcardとのコンペになり大きな部分ではnewcardが採用されました。

PAOはISAベースのPCMCIAのカードをいろいろサポートしていました。ただ時代的には次はpciベースのcardbusに変わるだろうと思われていました。

FreeBSD 3ではpciの実装も大きく変わり、newbusになりました。

そんなこんなで、PAOの多くの部分は捨てられnewcardやnewbusになりました。

とはいえその後cardbusはそれほど普及せずUSBが主流になりました。

PAOなきあとのFreeBSDはPAOでは使えていたpccardが使えなくなるものありましたが、USBに移行していったので、それほど大きな問題にはならなかったと思います。

このことにより多くの日本のFreeBSDの開発者が去っていくことになりました。

pccardはPCMCIAとCardBusというまったく違ったアーキテクチャをサポートしていたり、PCMCIAは統一規格ではあったものの、いろいろな方言があり、実装が難しかったことも、対応を難しくしたような気もします。

PCMCIAのサポートは13Rで消されました。環境依存かもしれませんが、12Rで試してみるとpanicしました。FreeBSDはプロジェクトの管理ができてなくて、動かなくなったから消すって事になってしまったようです。

この問題は二つの側面があると思います。

まず一つはPAOチームが現状のサポート状況をちゃんと伝えたれてなかったという事があると思います。それは聞く耳をもたない米国の開発者がいたのかもしれませんが、やはりネイティブではない日本人は折衝の能力はかなり厳しいものがあります。また自分の手元にないデバイスのサポートの理解は非常に難しかったりします。

もう一つは米国のメインラインの開発者がcardbusへの移行を急ぎすぎたというとことがあったように思います。これは後からみればあまり意味の無いことだったのですが、当時はそうは思われてなかったのだと思います。

未来の事はだれも分からないですが、あまり良い判断ではなかったと思われますし、この事がFreeBSDの衰退につながる一因になった気もします。

もしPAOチームを無碍にすることがなかったら、PCMCIAサポートが動かなくなって終了のような事は無かったのかもしれません。

当時PAOは使っていましたが、開発には参加していなかった私がこういうのを書くのもおこがましいのですが、なにか書いておかないと忘れ去られると思って書きました。
enum のDisplayName が指定した文字列と一致するか調べる拡張メソッド

flags でないなら単純に指定されたenum値のDisplayName と文字列を比較すれば良いけどflags だと タイプ1，タイプ2、合成値 みたいな定義になってタイプ1かタイプ2のどちらかでよい場合、って言う比較があると便利かも、と思った

こんな感じでどうだろう

```csharp
    [Flags]
    public enum SampleTypes
    {
        [Display(Name = "flag none")]
        None = 0,
        [Display(Name = "flag 1")]
        Flag1 = 1,
        [Display(Name = "flag 2")]
        Flag2 = 2,
        [Display(Name = "flag 3")]
        Flag3 = 3,
        [Display(Name = "flag 4")]
        Flag4 = 4,
        [Display(Name = "flag all")]
        FlagAll = 7
    }
    class Program
    {
        static void Main(string[] args)
        {
            var flag = SampleTypes.Flag3;
            // Your code here!

            System.Console.WriteLine(flag == SampleTypes.Flag3);
            System.Console.WriteLine(flag.DisplayNameMatches("flag all"));
            Console.ReadLine();
        }
    }

    public static class EnumExtension
    {
        public static string DisplayName(this Enum value)
        {
            var enumType = value.GetType();
            var memberInfo = enumType.GetMember(value.ToString()).First();
            if (memberInfo == null || !memberInfo.CustomAttributes.Any())
            {
                return value.ToString();
            }

            var displayAttribute = memberInfo.GetCustomAttribute<DisplayAttribute>();
            if (displayAttribute == null)
            {
                return value.ToString();
            }

            return displayAttribute.Name ?? value.ToString();
        }
        public static bool DisplayNameMatches(this Enum value, string name)
        {
            foreach(var e in Enum.GetValues(value.GetType()))
            {
                var sourceValue = (Enum)e;
                
                if(value.HasFlag((Enum)e))
                {
                    var displayName = DisplayName((Enum)e);
                    if (displayName == name)
                    {
                        return true;
                    }

                }
            }
            return false;
        }
    }

```
接続元のEC2と同じAZのマウントターゲットを消してみた

マウントをしたければこちらの記事を参考にしてください（こちらをベースに話を進めます）
https://qiita.com/watanabe-tsuyoshi/items/fd572d3df418c964f592

EC2
ap-northeast-1a

EFS
ap-northeast-1a
ap-northeast-1c

EC2ではこんな感じでマウント

```txt:（なんでマウントターゲット先IPじゃなくてループバックアドレスなんだ・・・？）
sudo mount -t efs -o tls fs-***********:/ efs　→EFSのコンソール画面でアタッチをクリックするとこのコマンドが出てくる
df -h
127.0.0.1:/     8.0E     0  8.0E   0% /etc/amazon/efs
```
EFSの挙動でマウントターゲットを明示的に指定しないと同じAZのマウントターゲットに接続します。通信速度を速めるためとAWSドキュメントに書いてありました（どこに書いてあるかわかりませんでしたすみません）

コンソール画面でマウントターゲットを削除します。

```
sudo umount /etc/amazon/efs
→何も起こりません（要はアンマウントに失敗します）
```

ここから２つのことを実験
①再度マウントしたらどうなるか（ap-northeast-1cのターゲットに接続するのか）

```txt:別のAZのマウントターゲットを経由してマウントしない　→　マウントターゲット指定しないと同じAZのマウントターゲットにつながるっぽい
$sudo systemctl restart nfs
$sudo mount -t efs -o tls fs-***********:/ efs
Failed to resolve "fs-03f2ee373347cf52d.efs.ap-northeast-1.amazonaws.com" - check that your file system ID is correct, and ensure that the VPC has an EFS mount target for this file system ID.
See https://docs.aws.amazon.com/console/efs/mount-dns-name for more detail.
Attempting to lookup mount target ip address using botocore. Failed to import necessary dependency botocore, please install botocore first.　→　マウント失敗
```

②明示的に指定すればどうか

```txt:明示的に指定すればもう一つのマウントターゲットを経由してマウント
$sudo systemctl restart nfs
$sudo mount -t nfs4 -o nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport (ap-northeast-1cのマウントターゲットプライベートIP):/ efs
$df -h
(ap-northeast-1cのマウントターゲットプライベートIP):/  8.0E     0  8.0E   0% /etc/amazon/efs →　マウント成功
```
実装に詰まったときはワクワクさんとゴロリになりきろう

「[達人プログラマー](https://www.amazon.co.jp/%E9%81%94%E4%BA%BA%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9E%E3%83%BC-%E7%AC%AC2%E7%89%88-%E7%86%9F%E9%81%94%E3%81%AB%E5%90%91%E3%81%91%E3%81%9F%E3%81%82%E3%81%AA%E3%81%9F%E3%81%AE%E6%97%85-David-Thomas/dp/4274226298/)」に、ラバーダッキングという方法が紹介されています。それは次のような方法です。

[ITエンジニアなら問題の自己解決力を高めるラバーダッキング法を覚えよう！](https://www.java-career.com/column/industry-knowledge/rubber-ducking-method/)

> ラバーダッキング法とは、問題が発生した際に、ラバーダック（ゴム製のアヒルのおもちゃ）に悩みを話しかけることで、頭の中を整理し、問題の解決を図る方法です。
> エンジニアであれば、プログラムのバクなどによるエラー（不具合）に遭遇したとき、一人で考え込んでいても一向に解決しなかった問題が、アドバイスを求めるために誰かに話すことで、案外その場で自己解決してしまった経験がある方は少なくないでしょう。基本的にエンジニア自身でコーディングしたソースのミスを自分自身で探すことは気が付きにくく、第三者に相談したほうが問題解決しやすいというのが一般的ではないでしょうか。
> そして、これがまさにラバーダッキング法の効果です。

ここにあるのは、「**人に説明しているときに問題を自己解決することが多い。ならおもちゃに話しかけても整理できるんじゃないか**」という発想です。

そのため、多くのソフトウェアエンジニアは、ラバーダッキング用のおもちゃやぬいぐるみを持っているはずです。私も家にあひるちゃんやケロちゃんのぬいぐるみを常備して、時折、彼らに話しかけています。

![kerochan.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/127762/c6057bfb-64b3-313b-1317-37a30a6b5974.jpeg)




ただ、実際にやってみても、私はあまり問題を整理できている実感はありませんでした。「あひるちゃんやぬいぐるみに相談するとき、どういうノリで相談すればいいか分からない」「一方的に話しかけるのが続けづらい」「アニメでケロちゃんに相談して問題が解決しているのを見たことがない」という問題があったんだと思います。

そしてここ最近、その問題は「**もしこの仕事が『つくってあそぼ』なら、ワクワクさんとゴロリはどんな会話をするだろうか**」を想像することでカバーできそうだと考え始めました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/127762/9fa1b7e6-a882-5dec-7754-f01003a9035a.png)

このワクワクさんとゴロリという組み合わせが絶妙で、好奇心旺盛で素直なゴロリと、それを見守りつつ計画・アドバイス・メンタリングするわくわくさんという役割分担が分かりやすいです。同僚のコメントではゴロリがラバーダックの役割だと表現していますが、実際には両方のキャラクターの視点を同時に考え、自分のタスクを多角的な視点で客観的に見られているように感じます。

* ワクワクさんの立場でゴロリに説明をする
* ゴロリの立場で自分の詰まっている状況を問いかける、もしくは分からない点を質問する
* ゴロリはワクワクさんのスキルや知識を信頼していし、ワクワクさんはゴロリの好奇心や学習意欲を認めている（彼らは理想的なメンター&メンティーの関係なんじゃないかと思いますｗ）

これを「**ワクワクさんメソッド**」と名付けたので、皆さんもぜひ使ってみてください👨‍🏭
Windowsプロセス間通信の速度比較


# Windowsプロセス間通信の速度比較

Windows環境でのプロセス間通信では、一体どういった構築がベストなのか、資料を見つけられなかったので適当に測定してみました。
こういうのは環境によるところが多いので、常に使えるデータではありませんが、一つの参考にはなるかもしれません。

測定環境：Windows10 / Rysen5-5600X / SSD2TB / メモリ64GB

※ Debugビルドしかしていないし、全くもって厳密な測定ではありません…



# 調べたい項目

* プロセスの起動速度
* 通信手法
* 転送バッファサイズと転送速度の関係

プロセスを新規に起動してそれに対してプロセス間通信を行う、という処理一式の軽量さを判断したいので、プロセス自体の起動速度も比較対象に含めました。


# より詳細な比較項目

* SubSystem:Console vs SubSystem:Windows
* 匿名パイプ vs 共有メモリ（=メモリマップトファイル）
* 小さいバッファサイズで細切れに転送 vs 大きいバッファサイズで一度に転送

通信手法については、名前付きパイプ・ソケット通信・ファイルで通信 などは速度面から不利であることが分かっているので、検証対象から外しました。

一般的には共有メモリがダントツで早いと言われていますが、Windowsのそれはメモリマップトファイルであり、メモリにキャッシュはされるもののストレージのアクセス速度が足を引っ張る可能性があります。


# 測定方法

1. 子プロセスを起動
1. 匿名パイプで、親から子にデータ要求を出す
1. 匿名パイプ or 共有メモリ で、子から親にBlockSize分のデータを渡す
1. 親が累計TotalSize分のデータを受け取ったら終了。まだなら2に戻る

以上を SubSystem:Console の場合と SubSystem:Windows の場合で行う。


# 結果

プロセス起動処理にかかる時間は

|   |  Console 匿名パイプ | Windows 匿名パイプ | Console 共有メモリ |  Windows 共有メモリ |
|:---|---:|---:|---:|---:|
| 一度目 |  17ms  |  2ms  | 17ms |  2ms  |
| 以降 |  2ms  |  2ms  | 2ms |  2ms  |

転送処理にかかる時間は、TotalSize:1GB のとき

|   |  Console 匿名パイプ | Windows 匿名パイプ | Console 共有メモリ |  Windows 共有メモリ |
|:---|---:|---:|---:|---:|
| BlockSize:32KiB | 430ms | 500ms | 500ms | 440ms |
| BlockSize:64KiB | 250ms | 290ms | 370ms | 315ms |
| BlockSize:128KiB | 150ms | 175ms | 245ms | 230ms |
| BlockSize:256KiB | ★ 130ms  | 130ms | 130ms | 135ms |
| BlockSize:512KiB | 200ms | 200ms | ☆ 115ms | 130ms |
| BlockSize:1MiB | 410ms | 430ms | 485ms | 490ms |
| BlockSize:4MiB | 450ms | 565ms | 470ms | 480ms |
| BlockSize:8MiB | 620ms | 690ms | 515ms | 505ms |
| BlockSize:16MiB | 760ms | 820ms | 715ms | 700ms |
| BlockSize:32MiB | 1045ms | 1090ms | 950ms | 930ms |

# 考察

### プロセス起動にかかる時間について

SubSystem:Consoleの場合はコンソールウィンドウに標準出力をアタッチしたりなんなりとかのコストがかかるでしょうから、 SubSystem:Windows の方が早いのは理解できます。連続して何度も呼んだ場合は何かしらのキャッシュに乗るのか、WindowsでもConsoleでも同じ2msになりました。

### 転送処理にかかる時間について

Subsystemは関係ないだろうと思っていたのですが、おおむね（特に匿名パイプは）Consoleの方が早いという結果が出ました。原因は分かりません。（誤差かもしれません）

共有メモリは僅かに早いケースもあるものの、匿名パイプとほぼ変わらないという結果になりました。
もしかしたら読み込みの同期に匿名パイプを併用する必要がある以上、その同期に時間が掛かってしまって速さを発揮できていないのかもしれません。ただ同期は絶対に必要なので、やはり結果的にはWindowsの共有メモリによる通信は匿名パイプとほぼ変わらないと言えるかもしれません。
少なくとも「共有メモリがダントツで早い」というイメージからはほど遠いですね…。

### 総評としては

Subsystem:Console の方がスタンドアロンでのテストが行いやすいというメリットがあるので、同じイメージファイルで何度もプロセスを立ち上げるのであればほぼ速度は変わらないですし、メンテナンス性の観点からConsoleにした方がよさそうです。

また共有メモリは途中で例外が起きたりしてうっかり開放をしそびれると、再起動まで永久に無駄ファイル&メモリが残り続ける恐れがあります。
そういう扱いが危険な代物でもあるので、早さの違いがほぼないのであれば選択するメリットはないでしょう。

使用すべきブロックサイズは環境にもよるでしょうが、とりあえず表から見て256KiBあたりが良いであろうことは分かります。

# 結論

今回のケースでは

* Subsystem:Console
* BlockSize:256Kib
* 通信方式:匿名パイプ

とするのが良いかなという結論が得られました。
Swift UI で作った macOS 用アプリでウインドウを閉じた時にアプリを終了させる方法

## ウインドウを閉じたらアプリを終了させる様にしたい
　Swift UI でアプリを作ってみたのだが、ウインドウの赤丸をクリックしてウインドウを閉じてもアプリは終了していなかった。
　新規プロジェクトで Swift UI のテンプレートが作られるが、そのままだとウインドウを閉じてもアプリは終了しないものになっている。
　ウインドウを閉じたらアプリを終了させるのはどうするのかと調べてみても、なかなか見つからなかった。
　解決につながったサイトの情報は文末に記載。

### ウインドウが閉じられたらアプリを終了させる方法
　新規プロジェクトで作られるテンプレートの＜アプリケーション名＞App.swift のソースに、

```swift:
@NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
```

と

```swift:
class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate {
    func applicationDidFinishLaunching(_ aNotification: Notification) {
    	 
    }

    func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
        // 最後のウィンドウを閉じたらアプリケーションも閉じる
        return true
   }
}
```
を追加することで実現できた。

ソースコードの位置がわかるように、＜アプリケーション名＞App.swift　のコード全体は次のとおり。

```swift:yourAppNameApp.swift
import SwiftUI

@main
struct **You APP name**: App {
    @NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate {
    func applicationDidFinishLaunching(_ aNotification: Notification) {
    	 
    }

    func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
        // 最後のウィンドウを閉じたらアプリケーションも閉じる
        return true
   }
}
```

### 参考にしたサイト
　このサイトの情報を参考にさせていただきました。
ありがとうございます。
https://zenn.dev/ohnami/articles/b9da5d63aba119