OmniAICreator/Qiita-1.07M huggingface dataset:
Spring Boot + Thymeleaf データの反映の際のエラー解消法

#はじめに

Spring BootとThymeleafを使用したWebアプリ開発の勉強している際に
**org.thymeleaf.exceptions.TemplateInputException** が発生しました。

テンプレート(html)にJavaのデータを反映する際のエラーです。

#開発環境

* OS: Windows 10
* Java: 11
* Spring Boot: 2.3.5
* Thymeleaf: 3.0.11
* ビルドツール : Gradle
* IDE: Eclipse 2020-12

#問題箇所の特定方法

エラーログの中から「line」を検索すると
エラーが発生しているhtmlファイルの行番号、桁数が分かります。

`(例)` 
**エラー発生ファイル** 
 (template: "class path resource [templates/index.html]")
**エラー発生箇所** 
(line 10, col 21)

```
This application has no explicit mapping for /error, so you are seeing this as a fallback.

Wed Jun 23 11:46:32 JST 2021
There was an unexpected error (type=Internal Server Error, status=500).
An error happened during template parsing (template: "class path resource [templates/index.html]")
org.thymeleaf.exceptions.TemplateInputException: An error happened during template parsing (template: "class path resource [templates/index.html]")
(中略)
Caused by: org.attoparser.ParseException: Exception evaluating SpringEL expression: "taskForm.isNewTask" 
(template: "/index" - line 10, col 21)
(中略)
Caused by: org.thymeleaf.exceptions.TemplateProcessingException: Exception evaluating SpringEL expression: "taskForm.isNewTask" 
(template: "/index" - line 10, col 21)
(後略)

```

#原因
* **変数の名前間違い** 

単純なケアレスミスです。
Javaのコードを確認して修正してください。

* **Thymeleafの文法間違い** 

[公式リファレンス](https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html#using-texts)を読んで修正してください。


* **getterの名前が [get + フィールド名] になっていない** 

htmlに反映させたいデータを
エンティティなどのオブジェクトのフィールドから値を取り出す場合
getterのメソッド名が **[get + フィールド名]**  になっていないとエラーになります。

`修正前`

```Form.java

public class Form {

	private boolean isNewItem;
        
        // 自動生成だとgetterのメソッド名が
　　　　// フィールド名だけになってしまうことがあるので
　　　　// getIsNewItemに修正しましょう。
	public boolean isNewItem() {
		return isNewItem;
	}

	public void setNewItem(boolean isNewItem) {
		this.isNewItem = isNewItem;
	}
}

```

`修正後`

```Form.java

public class Form {

	private boolean isNewItem;
        
　　　　// getIsNewItemに修正しました。
	public boolean getIsNewItem() {
		return isNewItem;
	}
(後略)
}

```

boolean型のフィールドのgetterを自動生成すると
getterのメソッド名がフィールド名だけになってしまうことがあるので
変数や文法の間違えが見つからない場合はgetterの確認をしてみてください。

#参考サイト

https://trans-it.net/spring-boot-dataaccess-importantpoint/
[WPF] あるView/ViewModelから、別のView/ViewModelのメソッドを呼びたいとき

# もくじ

https://tera1707.com/entry/2022/02/06/144447

# やりたいこと

あるViewModelから、別のViewModelのメソッドをどうしても呼びたいということがあった。
無理やり感があるが、その時に試したやり方をメモ。

# サンプル

どちらのView/ViewModelにも属さないシングルトンのクラスを用意して、そこにdelegate(Action)を持たせる。

そこに、呼ばれたい側のViewModelでGetInstanceして、呼ばれたいメソッドを登録する。
その後、呼びたい側のViewModelでGetInstanceして、呼びたいメソッドを呼ぶ。

```メソッドを登録するシングルトンのクラス.cs
    public sealed class FunctionManager
    {
        private static FunctionManager _instance = new FunctionManager();
        public static FunctionManager GetInstance() => _instance;
        private FunctionManager() { }

        // 呼びたい/呼ばれたいメソッドを登録するAction
        public Action MyFunc = null;
    }
```

```呼ばれたい側のViewModel.cs
    class ViewModel : BindingBase
    {
        FunctionManager fm = FunctionManager.GetInstance();

        public ViewModel()
        {
            fm.MyFunc += (()=>
            {
                Debug.WriteLine("Func呼びました。");
            });
        }
    }
```

```呼びたい側のViewModel.cs
    class ViewModel2 : BindingBase
    {
        FunctionManager fm = FunctionManager.GetInstance();

        public ViewModel2() { }

        public void Yobu()
        {
            fm.MyFunc?.Invoke();
        }
    }
```

※これがよいやり方なのかどうかは不明。
　DIコンテナを使って、ここでいうところの`FunctionManagerクラス`(位置づけ的にはModelか)を、両ViewModelにオブジェクト注入してやれば何となくそれっぽくなるのかも？

※別にViewModel間でなくても、どこからでも呼べてしまいそうなので、変な使い方すると後で大変になりそう。
PowerShell　演算子まとめ


■PowerShell 演算子

条件式の判定時などによく使うものの、結構うろ覚えだったり忘れてしまったりするので備忘録としてまとめておく。

```

-eq …　　等しい（==）

-ne …　　等しくない（!=）

-gt …　　～より大きい（>）

-ge …　　～以上（>=）

-lt …　　～より小さい（<）

-le …　　～以下（<=）

-like …　　ワイルドカードと等しい

-notlike …　　ワイルドカードと等しくない

-match …　　正規表現と等しい

-notmatch …　　正規表現と等しくない

```
django-filter利用時にOR検索したかった件

初めてdjango-rest-frameworkを利用したシステムを作ることになった。
django-filterと言われるものが有るらしいので入れた際に時間がかかった点について書いていこうと思う。
https://django-filter.readthedocs.io/en/stable/guide/install.html

## やりたかったこと
Customerモデルを検索するときにcustomerNameに値を渡したら、customerNameに該当するものとfuriganaに該当するデータを出力したかった。

## 結果
filters.FilterSetにカスタムのやつ作る

```shell:コマンドプロンプト
$ pip install django-filter
```
djangoのsettings.pyのINSTALLED_APPS追記する。

```python:settings.py
INSTALLED_APPS = [
    ...
    'django_filters',
]
```

Djangoにアプリを追加したり色々、ここらへんは省略。
以下はmodelsやview、serializerの設定を記載します。


```python:models.py
class Customer(models.Model):
    customerCode = models.IntegerField("得意先コード")
    customerName = models.CharField("得意先名", max_length=100)
    furigana = models.CharField("ふりがな", max_length=100, blank=True, null=True)

    def __str__(self):
        return self.name
```

```python:serializers.py
from rest_framework import serializers
from .models import Customer

class CustomerSerializer(serializers.ModelSerializer):
    class Meta:
        model = Customer
        fields = '__all__'
```

```python:views.py
from rest_framework import  viewsets
from django_filters import rest_framework as filters
from django.db.models import Q
from .serializers import CustomerSerializer
from .models import Customer

class CustomerFilter(filters.FilterSet):
    # フィルタの定義
    customerCode = filters.NumberFilter(lookup_expr='exact')
    customerName = filters.CharFilter(method='myCustomNameFilter')
    furigana = filters.CharFilter(lookup_expr='contains')

    class Meta:
        model = Customer
        # フィルタを列挙する。
        # デフォルトの検索方法でいいなら、モデルフィールド名のフィルタを直接定義できる。
        fields = ['customerCode','customerName ', 'furigana']

    def myCustomNameFilter(self,queryset,name,value):
        #ここでOR検索を入れる。fieldsのcustomerNameに入ってきた値を利用
        return Customer.objects.filter(Q(customerName__startswith=value)|Q(furigana__startswith=value))


class CustomerViewSet(viewsets.ModelViewSet):
    queryset = Customer.objects.all()
    serializer_class = CustomerSerializer
    filter_class = CustomerFilter
```

```python:urls.py
from rest_framework import routers
from .views import  CustomerViewSet

router = routers.DefaultRouter()
router.register("customers",CustomerViewSet)
```
.env.developからgraphql playgroundが読み込めない

# 課題
gatsbyでgraphql playgroundが使いたい。

使うために、
1. .env.developmentに```GATSBY_GRAPHQL_IDE=playground```を追加し,
2. ```npm install --save-dev env-cmd```をし、
3. package.jsonに```env-cmd .env.development gatsby develop``` を追加し、
4. ```npm run develop```した

しかし、playgroundが開かない


# 解決策
package.jsonに```env-cmd -f .env.development gatsby develop``` を追加。

参考：https://stackoverflow.com/questions/56301852/env-cmd-error-failed-to-locate-env-file-in-gatsby
【Kotlin】when の ( ) 内に変数を定義して { } 内で使う

```Kotlin
fun Request.getBody() =
        when (val response = executeRequest()) {
            is Success -> response.body
            is HttpError -> throw HttpException(response.status)
        }
```

（[Conditions and loops | Kotlin](https://kotlinlang.org/docs/control-flow.html#when-expression) より）

こんな風に `when` の `(` `)` 内（subject）に変数を定義して、それを `{` `}` 内（body）で参照することができます。
変数は `val` のみで、`(` `)` 内全体の値を代入するものである必要があります。

[Kotlin 1.3](https://kotlinlang.org/docs/whatsnew13.html#capturing-when-subject-in-a-variable) から追加された機能とのこと。[^1]

[^1]: 先日 IntelliJ に教えてもらうまでこの機能のことを知りませんでした…:flushed:

／以上
zsh まとめ

# 概要

- 現在macで使っているシェルは`zsh`
- 何となく使用していたため(一度調べたが忘れた)、今回調べた内容をまとめる

## 参考資料：[初心者向け：Zshの導入](https://qiita.com/iwaseasahi/items/a2b00b65ebd06785b443)

# zshとは

- 真名は[**Z Shell**](https://ja.wikipedia.org/wiki/Z_Shell)
- Unixのコマンドシェルの1つ

# 導入

## 1. インストール～デフォルトシェルの設定

### [yum](https://eng-entrance.com/linux-package-yum)を使用してインストールする

```
sudo yum install -y zsh 
#-yはすべての問い合わせにYesと回答したものとして実行するサブコマンド
```
[参考](https://www.atmarkit.co.jp/ait/articles/1608/29/news019.html)

### 使用するシェルの変更

- デフォルトシェルが`zsh`出ない場合は以下でデフォルトシェルを変更する

```
which zsh #zshのPathを確認
-> /usr/bin/zsh
chsh -s /usr/bin/zsh #ログインシェルをzshに変更
```

### シェルを再起動して確認

```
exec $SHELL -l
echo $SHELL
-> usr/bin/zsh
```

### zshrc にもろもろの設定を行う

- ここの設定自体は別記事にて詳細に説明予定

# zshフレームワークについて

- zshの設定をまとめて行うことができるもの
- 代表的なものとして[oh-my-zsh]()など(今は[Zinit](https://github.com/zdharma/zinit)なるものがホットらしい)
- プロンプトテーマ、便利なプラグイン、自動アップデート等を行ってくれる

# まとめ

- zshもプラグインで相当使い勝手がよくなることが分かった
- シェルの仲間なので、コンテナで使用するシェルが変わっても基本的な使用方法や考え方は変わらないはずなのでシェルについてもっと調べていきたい
zsh 設定ファイルと読み込み順　まとめ

# 概要

- ~/.zshrc にこれまであまり考えずに設定やプラグインを追加してきた
- zsh関連のファイルが他にもあることを知った
- どのファイルにどの情報を記載するのか気になったので調べたことをまとめる

### 参考資料：[.zshenvと.zshrcの場所・違い・設定方法を解説します！](https://suwaru.tokyo/zshenv/)
### 参考資料2：[https://qiita.com/muran001/items/7b104d33f5ea3f75353f](https://qiita.com/muran001/items/7b104d33f5ea3f75353f)

# シェルの呼称について

- シェルは使い分けによって以下のように分類される

## 1. ログインシェル

- ログイン時に起動するシェル

## 2. インタラクティブシェル

- ログインシェルから起動されるシェルでユーザと対話が可能なシェル

## 3. シェルスクリプト

- OSを操作するためのシェル上で実行できるプログラミング言語によって書かれた簡易的なプログラム
- [参考](https://e-words.jp/w/%E3%82%B7%E3%82%A7%E3%83%AB%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%97%E3%83%88.html)

# 設定ファイルの種類

## 1. .zshenv

- ログインシェル、インタラクティブシェル、シェルスクリプト、どれでも常に必要な設定を定義する
- どの場合でも必要な環境変数などはここに記述する
- 影響範囲が広いので設定する内容は最小限にすべき

## 2. .zprofile

- ログインシェルの場合に１度だけ読み込まれる
- ログインシェルに必要な情報のみを記載する

## 3. .zshrc

- ログインシェル、インタラクティブシェルの場合に読み込まれる
- zprofileの内容でインタラクティブシェルに必要だがシェルスクリプトには不要な情報を記載する

## 4. .zlogin

- ログインシェルの場合に１度だけ読み込まれる
- .zprofileとは読み込み順が異なる
- .zprofileとの併用を避けたほうが無難

## 5. .zlogout

- exitコマンドでzshを抜けたときに読み込まれる

# 設定ファイルの格納場所

- 設定ファイルの格納場所によって読み込み順序が異なる

## 1. ~/　(local)

- 自分が個別に設定したい内容をここに置く
- localはglobalを上書きするので全体より自分の設定が優先される
- ~については`$ZDOTDIR`が設定されていればそのディレクトリ、なければホームディレクトリをさす

## 2. etc/ (global)

- すべてのユーザーで有効にしたい設定についてはここにあるファイルに設定する
- globalはlocalに上書きされる

# 使用するシェルによる読み込みファイルと順序

- それぞれ global > local の順に読まれていく

## [1. ログインシェル](# 1. ログインシェル)

- zshenv > zprofile > zshrc > zloginの順


## [2. インタラクティブシェル](# 2. インタラクティブシェル)

- zshenv > zshrcの順

## [3. シェルスクリプト](# 3. シェルスクリプト)

- zshenvのみ

## 4. ログアウトするとき

- ログアウト時のみlocal > globalになる
- ~/.zlogout > etc/.zlogout

# 応用

## zsh関連ファイルの量が多く、読み込み時に時間がかかるとき

- 設定ファイルをコンパイルできる
- コンパイルしてできる`.zshrc.zwc`はzshで自動で読み込まれるようになっている

```
zcompile ~/.zshrc
```

# まとめ

- 参考資料がすごく整理されていたのでわかりやすかった
- 設定ファイルは複数あり、シェルの使用用途によって読まれるファイルが異なることが分かった
- 今まで何となく.zshrcに設定していたのはログインシェルとインタラクティブシェルの両方に設定でき、且つ.zshenvより影響範囲の少ないということで.zshrcに設定していたことが分かった
typescript upgrade-interactive まとめ

# 概要

- node.jsのパッケージを更新する際に、インタラクティブに更新できるツールがあるらしい
- それについて調べてまとめることにする

## 参考資料：[npmやyarnで管理するライブラリを対話的に更新する際のtips](https://www.ipride.co.jp/blog/3969)

# upgrade-interactiveとは

- node.jsのパッケージ更新で使用するupgradeについて、対話的に行えるツール
- ライブラリの更新を行う際に更新後のバージョンをわかりやすく表示する

1. メジャーバージョンアップ：赤で表示
2. マイナーバージョンアップ：黄色で表示
3. パッチバージョンアップ　：緑で表示

- 対象のバージョンをアップするかをそれぞれ選択することができる

# 使い方

## 1. 更新する

- package.json内で指定した範囲内で実行

```
yarn upgrade-interactive 
```

- package.json内で指定した範囲外で実行
- 依存関係が崩れるかもしれないので更新は慎重に行う

```
yarn upgrade-interactive --latest
```

## 2. 更新する内容を選択する

- 選択しているパッケージの変更　　　　　　　　　：↑キーまたは↓キー
- 選択しているパッケージをインストール対象にする：spaceキー
typesync まとめ

# 概要

- typesyncというプラグインの存在を知ったので調べたことをまとめる

## 参考資料：[TypeScriptの@typesパッケージをtypesyncで自動管理する](https://qiita.com/yamadashy/items/225f287a25cd3f6ec151)

# typesyncとは

- package.jsonを参照し、不足しているTypeScriptの型定義ファイルを自動で追加してくれるツール

# 使い方

## 1. インストール

```
yarn add -D typesync
#または
npm install -D typesync
```
## 2. preinstallの設定

- インストールコマンドを実行する前にtypesyncが走るようにする
- こうしないとインストールしてもtypesyncが走っていないのでTypeScriptの型情報が入ってこない
- コマンドを打ってもいいが面倒くさいので自動で走るように設定する
- package.json内のscriptにpreinstallとして設定する

```json:package.json
 "scripts": {
    ...
     // 初回インストール時にはtypesyncがないので || : を付け加えてエラーを無視します。
+    "preinstall": "typesync || :"
  }
```

# 流れ

## 1. 必要なパッケージを追加する

```
yarn add axios
```

## 2. インストールする

- インストールが実行される前にscriptsに登録していたpreinstallに設定しているtypesyncが走って型情報を取得
- 型情報がpackage.jsonに追加されるので、それを元にyarnでインストールが実行される

```
yarn install
```

# まとめ

- プロジェクトにTypeScriptを採用する場合には導入するパッケージに付随する型情報も必要になる
- その型情報を自動でインストールするために有用なツールということが分かった
- これからJavaScriptを使用するプロジェクトではTypeScriptは必須となるため、それに付随する作業を少しでも減らせるようにできたと思うのでよかった
nodenv-default-packages まとめ

# 概要

- nodenv-default-packagesというプラグインの存在を知ったので調べてまとめる

## 参考資料：[nodenvでいつも使うパッケージを自動でインストールする](https://qiita.com/tea-red/items/361c72df55b3fcd0e0bf)

# nodenv-default-packagesとは

- nodenvのプラグイン

- nodeインストール時に自動的に指定したパッケージをインストールすることができる

- デフォルトでインストールするものを固定しておくことでインストール忘れを防げる

# 使用方法

## 1. インストール

```
mkdir -p "$(nodenv root)/plugins"
git clone https://github.com/nodenv/nodenv-default-packages.git "$(nodenv root)/plugins/nodenv-default-packages"
```

## 2. デフォルトでインストールするパッケージの設定

- default-packagesファイルを作成する

```
touch $(nodenv root)/default-packages
```

- 作成したファイルにデフォルトでインストールするパッケージを記載する

``` Default-packages
yarn
Typescript
ts-node
typesync
```

# まとめ

- プロジェクト毎に共通してインストールするものをまとめることができるのは便利だと思った
- まだ使用歴はないがこれから使用していく
anyenv まとめ

# 概要

- anyenvというenv系のバージョン管理ツールを一元管理できるツールの存在を知った
- どういうものかについて調べたことをまとめる

## 参考資料：[anyenv のススメ](https://dev.classmethod.jp/articles/encouragement-for-anyenv/)

# anyenvとは

- env系のバージョン管理ツールを一元管理できるツール
- 任意のディレクトリ以下で使用する特定のバージョンを固定できる
- プロジェクト毎に使用するバージョンの違いを意識する必要がなくなる
- **env系のツールを簡単にインストールできる

#使いかた

## 1. ツールのインストール

- homebrewを使ってインストールする

```
brew install anyenv
```

- パスを通す

```
echo ‘eval “$(anyenv init -)”’ >> ~/.zshrc
```

- ターミナルの再起動

```
Exec $SHELL -l
```

## 2. anyenvを使用したバージョン管理ツールのインストール

- anyenv経由でバージョン管理ツールをインストール

```
anyenv install nodenv
anyenv install rbenv
```

- バージョン管理ツールで任意のバージョンをインストール

```
rbenv install 2.5.0
nodenv install 12.18.3
```

- 使用中のバージョン確認

```
anyenv versions
->管理しているバージョン管理ツールの中での現在のバージョン情報が表示される
```

- 使用端末で使用するバージョンを固定する

```
nodenv global 12.18.3
```

- 任意のディレクトリ以下で使用するバージョンを固定する

```
~/user/samplepj $ nodenv local 12.18.0

```

# プラグイン

## anyenv-update

- env系をまとめてアップデートしてくれる

### なぜ必要か

- バージョン管理ツール内のバージョン情報は、それ以降更新していなければツールをインストールした時のままになっている
- バージョン管理ツールを更新しなければ新しいバージョンをインストールできない
- これらの更新をここに行うのは面倒
- anyenv-updateプラグインでenv系のバージョン情報をまとめて更新してくれる

参考：[nodenvをupdateして新しいバージョンのNode.jsをインストールする](https://qiita.com/turara/items/6b7f4a8e3770a7074072)

### [インストール方法](https://github.com/znz/anyenv-update)

```
mkdir -p $(anyenv root)/plugins
git clone https://github.com/znz/anyenv-update.git $(anyenv root)/plugins/anyenv-update

```

# 実際の流れ(2021/6/24)

rbenvをanyenvで管理したかったので

### 1. [homebrew](https://qiita.com/Islanders-Treasure0969/items/1a847b1c1bd1e25de394)からrbenvを削除
### 2. anyenvでrbenvをインストール

をやりました。以下流れ

## 1. homebrewからアンインストール

```
$ brew uninstall rbenv
``` 

## 2. anyenvからrbenvをインストール

```
$ anyenv install rbenv
```

## 3. [パスを通す](https://qiita.com/Islanders-Treasure0969/items/c911c2bce63affb91fe2)

```
$ echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.zshrc
```

## 4. シェルを再起動

```
$ exec $SHELL -l
```

#まとめ

- anyenvの使用方法を知ることができた
- anyenv-updateは重宝しそう
- バージョン管理ツールで新しいバージョンをインストールする前に更新する必要があることは知らなかったので知れてよかった
- 早速使用する機会ができたのでよかった
要素がクラスを持つかを複数条件で判定する

# クラスの有無を複数条件で調べたい
　JavaScriptで要素をいじっていると、クラス使って要素の判別をしたくなる時があります。
　要素がクラスを持っているかを調べるときに便利なのが`classList`。`classList`の仕様については説明に自信がないので割愛させていただきます。

```javascript:singleClass.js
// element = <p class="hoge"></p> だとします。

if(element.classList.contains("hoge")){
    console.log("Element has 'huga'!");
}
```
　で、これに加えて"fuga"クラスを持っている場合を判定したいんです私は。
　単純にやると、以下のようになります。

```javascript:multiClass.js
if(element.classList.contains("hoge") || element.classList.contains("fuga")){
    console.log("Element has 'hoge' or 'fuga'!");
}

// ちなみにこうやっても最初の1つしか見てくれない
if(element.classList.contains("hoge","fuga")){
    console.log("Element has 'hoge' or 'fuga'!");
}
```
　もっと増えた時ただただ長くなるだけだし、同じこと何回も書くのが個人的に好みではありません。
　調べていると、引数指定を`"hoge"&&"fuga"`とやれば複数指定できる！って記事見つけたんですけど、順番入れ替えて調べると最後の1つしか見てないようです。

## 解決法(力技)
　まあ、脳筋の私ですのでスマートなやり方ではないです。`className`を使いました。`className`の仕様については説明に自信(以下略失礼)。

```javascript:multiClass.js
if(/(hoge|fuga)/.test(element.className.split(/\W/)){
    console.log("Element has 'hoge' or 'fuga'!");
}
```
　一応説明させていただきますと、`Element.className`でクラスリストを文字列で取得し、正規表現を使用してスペースで分割した配列を、これまた正規表現で`test`しています。
この`test`時に複数のチェックしたいクラス名を指定しています。(`test`って配列に対してもちゃんとチェックしてくれるんですね。)

## 追記
　コメントにてご指摘頂きました。ありがとうございます。`test`メソッドに配列を渡した場合、全部の要素に対してチェックしてくれるのではなく、配列を文字列化したものに対してチェックしているらしいです。確認不足でした。コメント欄に検証結果をいただいています。

それを受けまして、力技は変わらず2種類ほど対策と別の方法を記述いたします。

```javascript:test_array.js
// これだと"true"判定される
/(hog|f)/.test(element.className.split(/\W/))
// true

// 単純に対策
/(^|,)(hoge|fuga)[,$]/.test(element.className.split(/\W/))
// true

/(^|,)(hog|f)[,$]/.test(element.className.split(/\W/))
// false

// 別の方法
element.className.split(/\W/).some(name => /^(hoge|fuga)$/.test(name))
// true
```
前者は、配列を文字列化していることに単純に対応したものです。カンマで分けられた部分に対してチェックをしています。
後者は、classNameを分けた配列に`some`でチェックをしています。まあ結局全部調べるというのを自分で書いているだけですし、正直OR条件で書いているのとそこまでもう変わらないんじゃ…という気持ちがあります。

---
　私が気がついていない誤字・脱字、それは違うよボケェ！というところあれば私としても是非直したいので教えてください。
CloudWatch Logs InsightにてAPI Gatewayのアクセスログ用のクエリを書いてみた

##背景
API Gatewayを外部からの窓口とするシステムにて、システムへのアクセス数を集計する必要があったので、CloudWatchに出力しているアクセスログをインプットにAPI Gatewayへのリクエスト数を集計することにした。

アクセスログは、API にアクセスしたユーザーと、呼び出し元が API にアクセスした方法を記録するログで、アクセスが発生する度に以下の形式のログレコードが生成される。

```json:アクセスログの形式
{ 
  "requestId":"8d2f5138-019f-449c-bde7-432ff3cb49a1", 
  "ip": "103.4.xxx.xxx", 
  "caller":"-", 
  "user":"-",
  "requestTime":"13/Feb/2021:07:45:36 +0000", 
  "httpMethod":"GET",
  "resourcePath":"/hello", 
  "status":"200",
  "protocol":"HTTP/1.1", 
  "responseLength":"26" 
}
```

CloudWatchの[CloudWatch Logs Insights](https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/logs/AnalyzingLogData.html) という機能を利用すると、特定のロググループに対してクエリを実行してログを検索/分析することができる。

##やったこと
CloudWatch Logs Insightsにて以下の手順でクエリを実行し、結果を出力した。
1. 分析したい期間を指定
2. 分析したいAPI Gatewayのアクセスログのロググループを指定
3. 実行したいクエリを入力して「クエリの実行」をクリック
4. クエリの結果を「結果をエクスポート」から出力
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271327/06e2438a-751a-b2a5-c12a-18cb13ebb2b9.png)

今回は1日ごとのリクエスト数を集計したかったので、以下のクエリを用意した。

```
fields @message
| stats count(*) as count by substr(requestTime,0,11) as date
| limit 100
```
**クエリの内容**

* 「fields」ではログイベントから収集したいフィールドを指定。利用したいアクセスログのフィールドは@messagesフィールドだったので「fields @message」と指定

* 「stats」は集約統計用のコマンド。今回使った「count()」などがサポートされている

* 「count(*) as count by substr(requestTime,0,11) as date」の部分は、アクセスログのrequestTime項目（例：13/Feb/2021:07:45:36 +0000）の先頭から11文字（13/Feb/2021 の部分）をdateと名付け、dateが同じであるログイベントごとにログイベント数をcountという項目名で集約するという意味。

* 「limit」では表示したい件数を指定。10000まで指定可能 


##参考
[CloudWatch Logs Insights クエリ構文](https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/logs/CWL_QuerySyntax.html)

https://blog.serverworks.co.jp/insight-restapi-access-log
[エラー]Command failed due to signal: Abort trap: 6

#　エラーの内容
- Command failed due to signal:
-  Abort trap: 6

# エラー原因
- エラーは解決しましたが、なぜ発生したのかよくわかりません。
- 知ってる方がいらっしゃったら教えていただきたいです。

# エラー解決方法
## 参考文献
https://stackoverflow.com/questions/30724897/command-failed-due-to-signal-abort-trap-6

https://www.cloverfield.co.jp/2018/04/12/macos-4/?doing_wp_cron=1624448177.9456870555877685546875

https://tomato-develop.com/command-compileswift-failed-with-a-nonzero-exit-code-error-solutions/

- エラー解決方法
- Build Settings -> Swift Compiler - Code Generation ->  Optimization LevelをOptimize for Speed[-O]に設定したら解決しました。

![名称未設定.001.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1379778/d56ecefc-0494-aaa6-f622-c4c1eb134595.jpeg)
AWSの勉強してるときよく見る単語集その②

##　はじめに
[その①](https://qiita.com/pankata79/items/bbc511f4faba0ad574f4)の続きになります！
内容はざっくりです！
よろしくお願いします！(｡･ω･)ﾉﾞ

##　プロキシサーバー
　プロキシサーバーとは**インターネットへのアクセスを代理で行うサーバー**のこと。通常はパソコンやモバイル端末を経由して直接Webサイトへアクセスし、サーバーがデータをブラウザに返すことで画面にサイトが表示される。
　
　これに対してプロキシサーバーを使用するとブラウザで直接Webサイトにアクセスせずにプロキシサーバーにアクセスし、代わりにプロキシサーバーがサイトにアクセスし画面にサイトを表示してくれる。

##　スキーム
　スキームとは**体系的な計画・計画を伴う枠組み・体系的な構想・基本的な仕組み**などの意味を持つ英単語。
　単純な工程表や組織図のようなものではなく、関係する人や組織、モノ、システムなどの機能や役割、権利や権限、相互の関連性などを挙げ、また、これらの要素間のお金や情報、指示や手続きのなどの流れを体系立てて記述したものを意味する。複雑な場合は図で表すことも多い。

##　サーバレス
　サーバレスとは**その言葉の通りサーバーを持たないという考え**。サーバーを持たないことでサーバーの管理を不要にし、上のレイヤーのみに集中するシステム開発を実現するという概念。
　クラウドの特有の考え方

##　プロセス
　プロセスとは**稼働しているプログラミングの一つ一つ、または「工程」「手順」のこと**

##　SSH
　SSHとは**ネットワークを介して別のコンピューターにログインして操作するためのソフトウェアの一つ。**
　通信が暗号化されるためにインターネットなどを経由して安全にアクセスすることができる。
　SSH通信とかね

##　ハイパーバイザー
　ハイパーバイザーとは**サーバーの仮想化などで利用されるコンピューターを仮想化するための制御プログラム**
　代表的なハイパーバイザーとしては、VMwareのvSphereやマイクロソフトのHyper-V、Linuxで使われるKVM（Kernel-based Virtual Machine）などがある。
　ハイパーバイザーには、ハードウェア上で直接動作するネイティブハイパーバイザーと、OS上で実行されるホストハイパーバイザーがあります。性能面で優れているのはハードウェア上で直接実行するネイティブハイパーバイザーで、仮想化されたサーバー上でシステムを実行するなど、エンタープライズ用途で広く利用されています。

##　ITガバナンス
　ITガバナンスとは**企業などが経営方針にそってIT戦略を策定し、情報システムの導入や運用を組織的に管理・統制する仕組み。**
　経済産業省では「ITガバナンスとは経営陣がステークホルダのニーズに基づき、組織の価値を高めるために実践する行動であり、情報システムのあるべき姿を示す情報システム戦略の策定及び実現に必要となる組織能力である」と定義している。

##　おわり
　今まで何となくで流してきた単語の意味も少しづつ分かってきました。また明日から仕事がんばろ！

(｡･ω･)ﾉﾞバイバイ
KaggleチュートリアルのTitanicに取り組んでみた

# はじめに
Kaggleの初心者向けチュートリアルであるTitanic(タイタニック)に取り組んでみました。
最初は精度80%超えることを目標としていましたが、チュートリアルなので可視化とか色々触ってみることを目的に途中で変えたので最終的な精度は77%です。

# 参考にした記事
[Kaggleに登録したら次にやること ～ これだけやれば十分闘える！Titanicの先へ行く入門 10 Kernel ～](https://qiita.com/upura/items/3c10ff6fed4e7c3d70f0 "Kaggleに登録したら次にやること ～ これだけやれば十分闘える！Titanicの先へ行く入門 10 Kernel ～")
[KaggleチュートリアルTitanicで上位2%以内に入るノウハウ](https://qiita.com/jun40vn/items/d8a1f71fae680589e05c "KaggleチュートリアルTitanicで上位2%以内に入るノウハウ")
[タイタニック号の乗客の生存予測〜80%以上の予測精度を超える方法（モデル構築＆推論編）](https://yolo-kiyoshi.com/2020/01/22/post-1588/ "タイタニック号の乗客の生存予測〜80%以上の予測精度を超える方法（モデル構築＆推論編）")

# Notebook
コードやグラフ等は下記のNotebookを参照してください。

[Kaggle Notebook](https://www.kaggle.com/helvetica822/titanic-randomforest "Kaggle Notebook")

# やってみて感じたこと
* データの背景について理解することが大切
データにも現れているが、避難では女性や子供が優先されていた。
港の値はS(サウサンプトン)が多いが、これは出発の港であるため。
事故は深夜に起きた。

* データの傾向から想像することが大切
いいチケット、高い運賃の人は生存率が高い。→高いお金を出せる(社会的地位があると思われる)人も優遇されていた可能性があると思われる。
日中なら家族や同室の人でもバラバラに行動していることも考えられるが、深夜ならみんな部屋で寝ている可能性が高く、行動を一緒にした可能性が高い。
1人や5人以上で行動したと思われる人は生存率が低い。→1人ではどう逃げればいいか分からず逃げれなかったり、人数が多いと身動きが取りにくかったのではないかと思われる。
* 欠損値の補完はデータによって単純に全体の最頻値や中央値、類似データの最頻値や中央値、モデルを作って推定等といった手法がある。
* これまで数値や区分値といった値にばかり目がいっていたが、Nameのような文字列データから傾向を読み取ることも大切。
* データセットにある特徴量の取捨選択だけでなく、データから特徴量を作ることで精度向上に役立てられる。

# 次の予定
今度は回帰問題であるHouse Priceをやってまた記事に書こうと思います。
Linux 特定のコマンドをsudoで実行できないようにする

root ユーザーでログインし、以下のコマンドを実行します。

```
visudo
```

`cp` コマンドを `sudo` で実行できないようにするには、以下を最後の行に追加します。

```
Cmnd_Alias MYCMD = /usr/bin/cp
%wheel  ALL=(ALL)       ALL, !MYCMD
```

設定後、`:wq` でエディタを終了します。

この設定後、root ではないユーザーで `cp` コマンドを実行してみると以下のようにパスワードを聞かれます。

```
$ sudo cp sample.txt rename.txt
[sudo] password for ec2-user:
```

正しいパスワードを入力すると以下のように実行できない旨が表示されます。

```
$ sudo cp sample.txt rename.txt
[sudo] password for ec2-user:
Sorry, user ec2-user is not allowed to execute '/bin/cp sample.txt rename.txt' as root on ip-172-31-15-131.ap-northeast-1.compute.internal.
```

パスワードの設定を行うためには以下のコマンドを実行します。

```
passwd ec2-user
```

その後、新しいパスワードを設定します。

```
Changing password for user ec2-user.
New password:
BAD PASSWORD: The password is a palindrome
Retype new password:
passwd: all authentication tokens updated successfully.
```

# 参考記事

https://kkino.hatenablog.com/entry/2013/05/27/190912
【Golang】[64]byte や [32]byte を []byte や string に変換する（配列をスライスに変換する）

# Just Cast Away

> 固定長の配列（`array`）を可変長の配列（`slice`）にしたい。
> しかし、スライスにキャスト（型変換）してもエラーが出ます。

つまり、`[size]T` の配列を、`[]T` のスライスに変換したいのです。

```golang:キャストできない例
foo := [64]byte{}
bar := []byte(foo)
// Output: cannot convert foo (type [64]byte) to type []byte
```
```golang:stringにキャストできない例
foo := [16]byte{}
bar := string(foo)
// Output: cannot convert foo (type [16]byte) to type string
```

わかっちゃえば何てことないのですが、[「"golang" byteの配列を文字列に変換」でググって](https://www.google.com/search?q=%22golang%22+byte%E3%81%AE%E9%85%8D%E5%88%97%E3%82%92%E6%96%87%E5%AD%97%E5%88%97%E3%81%AB%E5%A4%89%E6%8F%9B&tbs=qdr:y)も、タイトルからは `[]byte` のスライスを `string` にキャストする方法しか出てこなかったので、自分のググラビリティとして。

## TL; DR <sub><sup>（今北産業）</sup></sub>

1. `[:]` を使って `slicing` する（スライスとして配列を参照させる）
2. `foo[:]` は `foo[0:len(foo)]` と同等
3. [オンラインで以下の動作を見る](https://go.dev/play/p/-p8HCiPuNrO) @ Go Playground

    ```golang
    foo := [64]byte{}  // 配列として領域を確保する
    bar := foo[:]      // bar は foo の領域を参照する
    baz := string(bar) // []byte を string にキャスト
    
    // 型を確認する
    fmt.Printf("%T\n", foo)
    fmt.Printf("%T\n", bar)
    fmt.Printf("%T\n", baz)
    
    hoge := [128]string{}
    fuga := hoge[:]
    
    // 型を確認する
    fmt.Printf("%T\n", hoge)
    fmt.Printf("%T\n", fuga)
    
    // Output: 
    // [64]uint8
    // []uint8
    // string
    // [128]string
    // []string
    ``` 

## 参考文献

- [Slice defaults](https://go.dev/tour/moretypes/10) | A Tour of Go @ Go.DEV
- [Slices](https://go.dev/blog/slices-intro#slices) | Go Slices: usage and internals | Blog @ Go.DEV
- [How to compare [32]byte with []byte in golang?](https://stackoverflow.com/a/27762870/8367711) @ StackOverflow
- [Golang: 配列からスライスに変換する](https://blog.web-apps.tech/golang-array-to-slice/) @ something tech
- [Goのarrayとsliceを理解するときがきた](https://qiita.com/seihmd/items/d9bc98a4f4f606ecaef7) @ Qiita
メールアドレスをWebに書くときのホスピタリティ

このように書くと、たぶん、きっと、おそらく、、、クロールbotに捕食されにくい。はず。知らんけど。

脆弱性診断などで引っかかった場合、この方法でパスできる。

ポイントは、CSSの `content: attr();` を使うこと。

# HTML
``` HTML
<a class="email" data-domain="domain" data-user="user" data-subject="タイトル" data-body="本文">@</a>
```

# CSS
``` CSS
.email {
  cursor: pointer;
}

.email::before {
  content: attr(data-user);
}

.email::after {
  content: attr(data-domain);
}
```

# JavaScript
``` JavaScript
var mailto = document.querySelector('.email')
mailto.addEventListener('click', onClick)

function onClick () {
  var user = event.target.getAttribute('data-user')
  var domain = event.target.getAttribute('data-domain')
  var subject = event.target.getAttribute('data-subject')
  var body = event.target.getAttribute('data-body')
  window.location.href = 'mailto:' + user + '@' + domain + '?subject=' + subject + '&body=' + body
}
```

# DEMO

CodePen
https://codepen.io/YusukeNakaya/pen/LYWqJyW


# 免責
保証はできません（笑）
npm scripts で特定のフォルダ内のファイルだけ削除

フォルダ内にある自動生成されるファイルを削除したかったので、書きました。

再帰的なサブフォルダをたどる処理はないです。

File: package.json

```json 
   "scripts": {
     "clean": "node -e \"(() => { const fs=require('fs'), dir='./AAA/'; fs.readdir(dir, (e, f) => {if(e){throw e} f.forEach(f => { if(fs.statSync(`${dir}${f}`).isDirectory()){return;} fs.unlinkSync(`${dir}${f}`)})}) })()\"",
```

もっと短くかける気もしますが、とりあえず。


下記もご参考に。

npm package.json でファイルコピーする時 cpx 使わなくてもいい - Qiita
https://qiita.com/standard-software/items/924ea6c0d6d398bf2a62
Nuxt.jsで作成されたテキストの入力フォームにjavascriptから文字列を入力してsubmitしたい

# はじめに

Nuxt.js で作成されたWebアプリの一部を jsで自動的に入力したいみたいな希望、ありますでしょうか(たぶんない)

しかし、普通に `input.value = 'hogehoge'` としただけでは、　`submitButton.submit()` としただけでは、DOM入力した内容が反映されないと思います

本稿では、それの解決をします

# 解決策

この例では、[このページ](https://developer.mozilla.org/ja/docs/Web/HTML/Element/input)にあるinputタグのtypeが `ｔｅｘｔ` のものを参考にしています

```diff_javascript
const input = document.querySelector('input[name="text"]')
input.value = 'xxx'
+ const event = new Event('input')
+ input.dispatchEvent(event)

- document.querySelector('form').submit()
+ document.querySelector('input[name="submit"]').click()
```

## ポイント

### ポイント1

Nuxt.jsの場合、ユーザがキーボード入力した内容をNuxt.jsの内部に反映させる必要があります
この反映を実施するのが `input` イベントになりますので、これを　`dispatchEvent(...)` で強制的に発火させます
これは、 `input.value = 'hogehoge'` と javascriptで書き換えさせるだけでは、キーボードから入力したように、変更が反映されない為です

### ポイント2

**ここはうろ覚えです、間違えていたらごめんなさい**

Nuxt.jsで `submit()` イベントを発火させる場合、純粋に `form.submit()` させても、実際にsubmitされません(超うろ覚えポイント / もしかしたらsubmitされてるかも、間違えたらごめんなさい)
この為、 `type="submit"` が定義されているボタンに対して `click()` イベントを発火させる必要がある為、このように記述します

## まとめ

1. inputの内容はとりあえずいつも通り `input.value = 'hogehoge'` すればよい
1. 入力後、 `input.dispatchEvent(new Event('input'))` で `input` イベントを強制発火
1. submitも `submit()` ではなく `button.click()` でクリックイベントを発火させる

間違えていましたらご指摘をお願い致します
この記事があなたのお役に立てることを祈ります
Power Automate のトリガー条件を使ってOutlookを色んな条件でトリガーさせる方法。

# やりたいこと
Power Automate にて、Outlook 365の「新しいメールが届いたとき」のトリガーを使うときに、
メールの件名に’AAA’または’BBB’が含まれている時だけトリガーしたい。


# ダメな例

図のように件名フィルタのところに、複数キーワードをスペースで指定しても思うようにキャッチしてくれません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/342467/d9fa1ca5-1fe9-cc42-5a1a-0ffcb2b2a3c7.png)




# 良い例

「トリガー条件」というものを使って設定していきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/342467/c2865253-e3f2-8972-c809-fb09fec5ee72.png)
トリガーとなる「新しいメールが届いたとき（v3）」の右上のミートボールメニューをクリック⇒設定をクリック。



![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/342467/69334be8-999a-0a68-4bd0-c6dba0c5a0f4.png)

「トリガーの条件」のところに下記のようにする。
`@or(contains(triggerBody()?['Subject'], 'AAA'),contains(triggerBody()?['Subject'], 'BBB'))`


トリガー条件を使うときは、一番最初の図の「件名フィルター」のところは空白でおｋ。
`'Subject'`のところを変更すればメールの件名以外も条件にできるようなので調べてみてください。


# 条件指定例

- アンド検索（件名にAAAとBBBが両方含まれていたらtrue）
    - `@and(contains(triggerBody()?['Subject'], 'AAA'),contains(triggerBody()?['Subject'], 'BBB'))`


- 完全一致（件名がAAAだったらtrue）
    - `@equals(triggerBody()?['Subject'], 'AAA')`
Docker備忘録 Ubuntu20.04+pyvenv+pipenv




コマンド編

カレントディレクトリにあるDockerfileからaというイメージを生成
`$ docker build . -t a`

aというイメージからbというコンテナを作成してラン
`$ docker run -it -d --name bcd a`

作ったbcdというコンテナの中に入る
個人的には.bash_profileやPATHをみる目的で使うことが多い
`$ docker exec -it bcd bash`


デバッグ編

```
find . -name "requirements.txt"
ls -la | grep hoge
```


実際に作ったDockerfile

```Dockerfile...
FROM --platform=linux/x86_64 ubuntu:20.04

WORKDIR /work

SHELL ["/bin/bash", "-c"]

RUN apt list --upgradable
RUN apt update

RUN apt install -y build-essential libffi-dev libssl-dev zlib1g-dev liblzma-dev libbz2-dev libreadline-dev libsqlite3-dev git wget

RUN git clone https://github.com/yyuu/pyenv.git ~/.pyenv

# pyenv
ENV PYENV_ROOT /root/.pyenv
ENV PATH $PYENV_ROOT/bin:$PATH
ENV PATH $PYENV_ROOT/shims:${PATH}

# Install Python and set default
RUN pyenv install 3.7.4
RUN pyenv global 3.7.4

# Install pipenv
RUN pip install pipenv

RUN pipenv install \
flake8 \
jupyter \
jupyterlab \
kaggle \
matplotlib \
numpy \
pandas \
pep8 \
scikit-learn \
scipy \
seaborn \
signate \
sympy \
xgboost \
Pillow

```

備考

- aptを使うのはよくないらしい。
- 今の認識だとbash_profileをビルド時に読み込めないっぽいのでENV命令を使う必要がありそう 
- pyenvのglobal,localの挙動よくわからん
- pipfile,pipfile.lockを使えばもっと簡潔なディレクトリ構成になりそう
 
【pyvenv】

- .pyvenv/versions/3.8.10/binの下にあるpipコマンドなどを使用している

> https://teratail.com/questions/25930から↓
- localを指定すると実行したディレクトリ内だけでPythonのバージョンが固定される
- globalは、フォルダを超えてそのバージョンが使われる
localはフォルダごとにバージョンを変えたい場合に使用できます。
globalは、すぐに起動して確認したい場合などで、標準で入っていてほしいバージョンを指定します。
アプリケーションであればlocalを指定しているほうが、外のバージョンが変わった際に悩まずに済みます。
verb と引数 ― J 言語入門

値 (noun) の型の次は、verb の説明をします。verb は、これまでに出てきたように、「関数」に似た機能です。ここでは、もう少し掘り下げてみようと思います。

# 引数の数と名前

verb と他の言語の「関数」の違いの一つは、引数の数です。J の verb は、引数が 1 個の **monad** と引数が 2 個の **dyad** だけです。3 個以上の値を渡したい場合は、配列やボックスを使います。

## monad

monad の場合、引数は verb の右側に 1 つだけです。この引数には、`y` という名前がついています。実際に `y` という変数が定義されているわけではありませんが、慣習的に この名前で呼ばれています。

```j
NB.  -  y
     -  3
```

## dyad

dyad の場合、2 つの引数のうち、左側は `x`、右側は上と同様に `y` と呼びます。これらの名前は頻繫に使うので、覚えておいてください。

```j
NB.  x  -  y
     5  -  3
```

# 何もしない verb

verb の中には、計算をせず、引数を返すだけのものがあります。単独では使いませんが、他の機能と組み合わせると便利な場合があります。

## `]`

`]` は、右の引数 (`y`) を返す verb です。monad も dyad も、常に `y` を返します。

```j
   ]3
3
   5 ] 3
3
```

## `[`

`[` は、左の引数 (`x`) を返す verb です。dyad は `x` を返しますが、monad の場合は `x` の代わりに `y` を返します [^1]。

```j
   [3
3
   5 [ 3
5
```

## 代入との組み合わせ

`]` と `[` は、代入 (`=:`) と一緒に使うと便利です。

代入 `a=: b` の実行結果は `b` と同じ値です [^2] が、最後に実行した式が代入の場合、 REPL は その結果は表示しません。

```j
   NB. 実行結果は 100 だけど、次の行に表示されない
   a=: 100
   NB. b には a=: 200 の実行結果 (200) が代入される
   b=: a=: 200
```

しかし、代入と同時に代入した値を確認したい場合があります。以下のように変数名を再入力すれば確認できます [^3] が、2 回に分けて入力するのは面倒ですよね。

```j
   a=: i.3
   a
0 1 2
```

そこで役に立つのが `]` (monad) です。代入の結果を `]` に渡すことで、最後に実行されるのが `]` になるので、REPL は結果を表示します。もちろん、`[` (monad) を使っても同じです。

```j
   NB. ] (a=: i. 3)
   ]a=: i.3
0 1 2
```

さらに、複数の代入を一行で実行したい場合には、`[` (dyad) が使えます。

```j
   NB. a=: i. (3 [ (b=: 'abc' ; 'de'))
   a=: i.3 [ b=: 'abc' ; 'de'
```

一見何をしているのか分かりづらいですが、式の実行の順序 (右から左) を考えれば解読できると思います。

---

\[ 前 : [データ型のまとめ](https://qiita.com/unsigned-wrong-wrong-int/items/cf81b30258582f89676e) \] \[ [目次](https://qiita.com/unsigned-wrong-wrong-int/items/e15a87999cedfc6b9ced) \] \[ 次 : [高階関数](https://qiita.com/unsigned-wrong-wrong-int/items/77cb04ee82b856c52622) ]

[^1]: したがって、`]y` と `[y` は同じ動作になります。
[^2]: J の代入は文ではなく式です。
[^3]: これまでの例では、この方法を使っていました。
git addを実行した際に、Gitが改行コードをLFからCRLFへ変更しようとするエラーを回避する方法「warning: LF will be replaced by CRLF in *」

##原因

Git addを実行した際にファイルの改行コードをLFからCRLFに変更しようとすることでエラーが発生しております。

```
$ git add <ファイル名>
warning: LF will be replaced by CRLF in <ファイル名>
The file will have its original line endings in your working directory
```
##回避策

回避方法としては以下コマンドを入力することでCRLFへ変更しなくなります。

```
$ git config --global core.autoCRLF false
```

##動作確認
再度 git add のコマンドを入力し、エラーが出力されていないことを確認。

```
$ git add <ファイル名>
```

##終わりに
今回はGitにおけるgit addコマンド実行時の改行コードによるエラーの対処方法についてまとめました。
少しでもお役に立てれば幸いです。
PC-9801でDPMI

MS-DOSは16Bitで動作してますが、DPMI(DOS protected mode interface)という仕組みで32Bitのコードを実行することができました。

![DPMI.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104066/3c347ff2-846e-a604-d438-8f52a09e3a6d.png)

PC-9801ではMS-DOS 5.0AからDPMIが入っていました。

ハードディスクは無いのでフロッピーでDPMIが起動できるようにファイルをコピーしてみました。

```
A>dir

 ドライブ A: のディスクのボリュームラベルはありません.
 ディレクトリは A:\

COMMAND  COM    48402  92-11-11    0:00
HIMEM    SYS    11024  92-11-11    0:00
EMM386   EXE    91742  92-11-11    0:00
CONFIG   BAK      241  21-06-21   19:33
EXTDSWAP SYS    30858  92-11-11    0:00
DPMI     EXE   405566  92-11-11    0:00
DPMI     INI      440  21-06-22    1:40
DPMI     SYS    10591  92-11-11    0:00
CONFIG   SYS      118  21-06-22    0:40
D86MGRH  386    45433  92-11-11    0:00
D86MGRN  386    45945  92-11-11    0:00
DDDH     386    80444  92-11-11    0:00
DDDN     386    80956  92-11-11    0:00
DMCPD    386     9588  92-11-11    0:00
DPDH     386     9322  92-11-11    0:00
DPDN     386     9300  92-11-11    0:00
DPFD     386     9275  92-11-11    0:00
       17 個のファイルがあります.
   243712 バイトが使用可能です.
```

かろうじてHD 1.2Mフロッピー一枚に納まります

CONFIG.SYSはこうなっています。

```
FILES=30
SHELL=\COMMAND.COM /P /MSG
DEVICE=HIMEM.SYS
DEVICE=EMM386.EXE /P=352 /UMB /T=EXTDSWAP.SYS
DOS=HIGH,UMB
```

これでDPMIを起動するとプロテクトモードになります。

JWasmにあったサンプルコードをopen watcom v2でアセンブリしてEXEを作り実機で確認しました。

https://github.com/yamori813/ow-pc98/blob/main/dpmi/dpmi32.asm

コードを見ると16bitで実行が始まり途中で32bitに替えているようです。
Heroku ✕ Python　環境設定

# Herokuの開発環境のはじめの一歩

個人用のメモです。参考にしたリンクを書いているので、一次資料を当たってもらうのが確実です。

## 開発環境を整える

virtualenvを使ってローカルから独立したPythonの開発環境を作ります。

```
# インストール
pip install virtualenv

# 環境の作成
virtualenv <your-env>

# 環境の適応
source <your-env>/bin/activate

# ライブラリのインストール
<your-env>/bin/pip install <tool名>

# 環境から出る
deactive
```

## Herokuのデプロイに必要なファイル

- runtime.txt
- requirements.txt
- Procfile

## runtime.txt

Pythonランタイムを指定するには、使用する正確なバージョン番号を宣言するruntime.txtファイルをアプリのルートディレクトリに追加します。

[Specifying a Python Runtime | Heroku Dev Center](https://devcenter.heroku.com/articles/python-runtimes)

### format

大文字と小文字が区別され、スペースを含まない、

```runtime.txt
python-3.9.9
```

pythonのバージョンは以下のコマンドで確認できます。

```
python -V
```

### サポートされているruntimeのバージョン

こちらのサイトで確認することができます。

[Heroku Python Support | Heroku Dev Center](https://devcenter.heroku.com/articles/python-support#supported-runtimes)

## requirements.txt

現在のアプリケーションの依存関係を書き出したファイルです。環境を再現するのに必要になります。

freezeコマンドを使って書き出します。

```
pip3 freeze > requirements.txt
```

### requirements.txtで指定したパッケージ一覧をインストールする

今回は使いませんが、指定したパッケージの環境をpip3コマンドで環境にインストールできます。

```
pip3 install -r requirements.txt
```


## Procfileファイル

サーバーが立ち上がって最初に実行されるコマンドを指定するファイルです。

```
web: python main.py
```

[Procfile | Heroku Dev Center](https://devcenter.heroku.com/ja/articles/procfile)
認証にCloudFrontの署名付きCookieを使って、hls.jsでストリーミング配信しようとしたら、激ハマりした話。

CloudFrontの署名付きCookieの設定方法などについては、ネット上に沢山の記事があるかと思いますので、割愛します。

構成としては、ブラウザから自前のサーバ( https://hoge.com )にアクセスしCookieをブラウザに食わせ、その後hls.jsを使ってCloudFront( https://streaming.hoge.com )からストリーミング配信する形。

とてもシンプルだし簡単にできるかと思っていたのだけど、自前のサーバがブラウザに食わせたCookieをhls.jsがCloudFrontに送ってくれないことが判明。

調べてみたところ、
https://github.com/video-dev/hls.js/issues/246
に書かれているように、hls.jsは初期設定のままだとCookieを送ってくれないらしい。

というわけで、以下のコードを書く。

```javascript
let config = {
	debug: true,
	xhrSetup: function ( xhr, url ) {
		xhr.withCredentials = true; // do send cookie
	}
};

let hls = new Hls( config );
```

これで無事にCookieが送られるようになったものの、今度はCORSエラーが発生。

CloudFrontのオリジンはS3で、S3のCORS設定は以下のようにしている。

```json
[
    {
        "AllowedHeaders": [
            "*"
        ],
        "AllowedMethods": [
            "PUT",
            "POST",
            "GET"
        ],
        "AllowedOrigins": [
            "*"
        ],
        "ExposeHeaders": []
    }
]
```

m3u8ファイルのレスポンスヘッダにも、きちんとAccess-Control-Allow-Origin:*が含まれている。

で、更に調べてみた結果、以下のページを発見。
https://github.com/video-dev/hls.js/issues/329

クレデンシャルフラグがtrueの時というのが、正直なところきちんと理解できているわけではないんだけど、ようするにAccess-Control-Allow-Originにワイルドカードを設定しているのがダメで、きちんとドメインを設定する必要があるらしい。

となると、S3のCORS設定だとできないので、CloudFront Functionsに以下のコードを設定して、Viewer Responseに設定する。

```javascript
function handler(event)  {
    var request = event.request;
    var response  = event.response;
    if (request.headers['origin']) {
request.headers['origin']) {
        response.headers['access-control-allow-origin'] = {value: request.headers['origin'].value};
    }
    return response;
}
```

これで解決かと思いきや、引き続きCORSエラーが発生。
ブラウザのエラーログには以下のログが吐かれる。

```
The value of the 'Access-Control-Allow-Credentials' header in the response is '' which must be 'true' when the request's credentials mode is 'include'. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.
```

ようするに、レスポンスヘッダのAccess-Control-Allow-Credentialsにtrueを設定する必要があるとのこと。

というわけなので、先程のCloudFront Functionsを修正。

```javascript
function handler(event)  {
    var request = event.request;
    var response  = event.response;
    if (request.headers['origin']) {
request.headers['origin']) {
        response.headers['access-control-allow-origin'] = {value: request.headers['origin'].value};
        response.headers['access-control-allow-credentials'] = {value: 'true'};
    }
    return response;
}
```

これで、やっとストリーミング配信が正常にできるようになりました。

とりあえず動いたものの、仕組みについては完璧に理解できているわけではないので、間違いなどありましたらご指摘頂けますと嬉しいです。
Azureを適当に勉強していく

## 目的
最近仕事でAzureを触ることが多い。抽象的に理解はしているつもりだが、いざシステムの構築を任されると何もできないと思う。実際に手を動かして、実装を進めながらインフラ周りの知識もつけていければと思う。ゴールはまだないが、ウェブアプリを作成することにする。
→日記でも作ってみようかなあ

#### 記事
[WebAppsを配置する](https://qiita.com/UpTheChels/items/982f401fe3c9ce9f5eb0)
[WebAppsにコードを発行する](https://qiita.com/UpTheChels/items/8b17620e4c9e25403bc9)
DFS系の問題の解き方@JS


JavaScriptでDFSを解くやり方を書いていきます。


##重要なポイント
基本的には、下記のような木構造で表せるときにDFSを使うことができる

使うときのポイント
・一番深いときの数値を見つける
・ノード配列を用意する（＜ーノード配列の詳細は後ほど）


一番深いときの数値とは、下記のように木構造があったとき、視点から一番遠いところまでの数のこと
　　![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/638950/540332ab-cc99-e718-5c12-2f148eea2f4d.png)


・ノード配列を用意する
ノード配列（ホントは名前とかあるのかもしれないですが、勝手に名付けました。すみません、、）とは
次にノードに行くときに候補として上がる物の配列のこと！
上の写真の場合、
『0』のノード配列は,[1,2,3]
『1』のノード配列は,[4,5] ...
といった具合になる。

このポイントを抑えて、下記テンプレートを使えば、DFSは大体、実装できるはず、、

##競プロの問題と参考のコード

これは競プロのC問題。

あなたはスーパーハッカーです。高橋君を攻撃対象に定めたあなたは、
高橋君のパソコンのパスワードに関して次の事実を突き止めました。
長さは 
N
 文字である。
a, b, c 以外の文字は含まれない。
高橋君のパソコンのパスワードの候補として考えられる文字列をすべて列挙してしまいましょう。


この問題は下のような、◯を視点とした木構造で表せる。
=>DFSが使える！

```
　　　　　　　　　　|---a---
　　　　　　|-a---b---
　　　　　　|　　　|---c---
　　|-a-b---
　　|　　　|-c---
　　|
◯---b--
　　|-c--
```

実際のコードがこちら。


```index.js

const depth = 3 //一番深い階層（今回は）入力値3が与えられたとする）
const abc = ["a","b","c"] //今回のノード配列

dfs([]) //からの配列を引数に取る

function dfs(arr){
  //（ここの条件は問題によって変わるのでよく考える人用あり）
  if(arr.length === depth) return //配列の長さと深さが一致したらリターンする

  for(let i = 0;i<abc.length;i++){//ノード配列を回す
    arr.push(abc[i])
    if(arr.length === depth){
      ans += arr.join("") + "\n"
    }
    dfs(arr) //再起的に使う
    arr.pop() //末尾削除
  }
}
```

このコードを実行すれば、下記のような文字列列挙ができる

```
aaa
aab
aac
aba
abb
abc
aca
acb
acc
baa
bab
bac
bba
bbb
bbc
bca
bcb
bcc
caa
cab
cac
cba
cbb
cbc
cca
ccb
ccc
```
【初心者でもわかる】ポップアップウィンドウの作り方

#どうも7noteです。ポップアップウィンドウの作り方

ポップアップを開いたまま、サイトの閲覧を継続してもらいたいなら、
ポップアップを**別ウィンドウ**で開くように設定してみましょう。

最近のwebサイトのポップアップは全て閲覧中のウィンドウの真ん中に出るので
ポップアップを消さないと閲覧を続行できません。

別タブで開くと、同様にタブを切り換えないと両方の情報を同時に見ることができません。

古典的な方法ですが、javascriptを使ってリンク先を専用の別ウィンドウでポップアップ表示させる方法を紹介します。
最近めっきりみなくなったので備忘録的に記事に残します

（スマホが主流の時代なんで、別ウィンドウで開くはあんまり使われないかもですが。。。）

## ポップアップウィンドウの表示方法

```index.html
<a href="javascript:void(0);" onclick="window.open('{リンク先URL}','{ウィンドウ名}','{ウィンドウ設定}');return false;">別ウィンドウを開く</a>

<!-- 入力例 -->
<a href="javascript:void(0);" onclick="window.open('http://hoge-hoge.com/window/','window1','width=500,height=300');return false;">別ウィンドウを開く</a>
```

## 解説

`window.open()`で開いたウィンドウはタブ分けやブックマーク、拡張機能などのメニューなしで開かれます。
ウィンドウ設定の箇所で表示位置や大きさを変更することが可能です。

（top,left,width,hright等）

ちなみにスマホの場合は単純に別タブで開かれます。ウィンドウの概念がないため。

## まとめ

PC表示の時しか使わない手法ですが、「別ウィンドウで情報を確認しながら入力フォームに情報を入力してもらう」みたいに使いどころはあるかなと思います。
javascriptといっても難しい計算や処理を入れていないので、普段javascriptに触れていない方でも簡単に実装が可能です。

#### おそまつ！
~ Qiitaで毎日投稿中!! ~
[【初心者向け】WEB制作のちょいテク詰め合わせ](https://qiita.com/7note)
【AIZU ONLINE JUDGE】Webエンジニアがアルゴリズムの問題を解いてみる

## 目次
* 自己紹介
* AIZU ONLINE JUDGEとは
* やろうと思った経緯
* 問題例
* 所感

## 自己紹介
* 新卒3ヶ月目Webエンジニア(バックエンド)
* バックエンド未経験で自社開発系企業に入社
* 大学では情報工学を専攻．フロントエンドも少し触る
* 現在は社内の案件に触れて実務を行っている

## AIZU ONLINE JUDGEとは？
AOJとよばれるもので、だれでもプログラミングの問題が解けるサイトです。
問題の内容はプログラミングコンテストのようなところで出される、お題に沿ってプログラムをつくって提出→採点をしてくれるサイトです。同じようなサイトに競技プログラミングのAtCoder( https://atcoder.jp/?lang=ja )なんてものもあったりします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1288580/99c7c48e-6361-f841-3ded-dbab2da2845c.png)

https://onlinejudge.u-aizu.ac.jp/

## やろうと思った経緯
大学の専攻が情報工学で、よく課題に使われていたサイトだったのでそこで認知
↓
web系の言語でも自分の実力を測りたい！もっと早く正確にコーディングできるようになりたい！
↓
せっかくなので取り組んだ手ごたえをアウトプット

## 問題例
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1288580/0acdf9ca-e185-bcf6-61cb-f4769acc429c.png)
今回は学生時代よく触れていた「アルゴリズムとデータ構造」という分野で一問取り組んでみたいと思います。
画像の通り分野ごとに何十問と問題があるのですが最初の「1_A:Insertion Sort」(挿入ソート)を解いていこうと思います。
### アルゴリズムがあまりわからない方へ
せっかく具体例を出して取り組むのでちょっとした解説を。
今回取り組む問題は"ソート"という配列等の並び替えをする手法の一つです。問題にもなっている挿入ソートの他にもマージソートやクイックソートなど、並び替えひとつとっても手法がいろいろありまして、計算速度やデータの形に合わせて使い分けます。
この挿入ソートは並べ替えの際に自然に思いつく手法で、配列の最初から順に大小を見ていって徐々にソートしていく手法です。
### 内容
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1288580/da828846-d9e9-4d7a-ded0-b153564327e1.png)

問題はこのようになっていて実行時間の制限や使用メモリの制限もあります。
最初にアルゴリズムの概要、どのようなプログラムを作るのかが書いてあります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1288580/68e4a69b-60cf-9b18-50a2-9d75f1af6c6c.png)
続いて具体的な制約と入出力例があります。
問題説明で理解できなかった場合は具体例を眺めると意外と理解できたりします
https://onlinejudge.u-aizu.ac.jp/courses/lesson/1/ALDS1/all/ALDS1_1_A
### 実際解いてみる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1288580/dd488b2f-0220-8e06-e632-d78fbc2c7ee6.png)
プログラムはブラウザで直接かけるようになっていて、今回はPHPを使って書いていきます。
他にも多くの言語が対応しており、Web開発の言語はほぼ対応していました。

実際のコード↓

```php5
<?php
$input = STDIN; //標準入力
$input_num = trim(fgets($input)); //1行目：要素数
$input_line = fgets($input); //2行目：入力値

$N = $input_num;
$A = explode(" ",trim($input_line));


echo $input_line; //最初の一行
 
for($i = 1; $i < $N; ++$i){
    $v = $A[$i];
    $j = $i - 1;
    while($j >= 0 && $A[$j] > $v){
        $A[$j+1] = $A[$j];
        $j--;
    }
    $A[$j+1] = $v;
    
    //ループごとに進捗を出力
    echo implode(" ",$A)."\n";
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1288580/86a4360d-c087-6fa0-37b5-c102b0b1e79b.png)

コードを書き終え、”submit”ボタンを押すとこのようにチェックが入ります。問題なければACCEPTEDになり
問題があると指摘されます！今回は問題なく動作したので合格ということになりました。
アカウントを作ることでスコアが蓄積されるのでゲーム感覚で楽しめますよ！

## 所感
久しぶりにアルゴリズムの問題をといて思ったことは"よりシンプルに早く"という考え方を忘れてはいけないなと思いました。
webアプリケーションを開発している現場だとどうしてもフレームワークに頼っていて今回で言うソートのような裏側で何が動いているのかを知らずに使っている点も多くあると思います。ソートの解説で軽く触れたようにデータによっては相性が悪く相当計算に時間がかかってしまうアルゴリズムもあるので、”この裏では何が起こっているの？”という視点は少なからずエンジニアには必要なのかなと感じました。
Ubuntu の Mysql と PowerDNS のバージョン組み合わせでRestAPIが使えなくなる現象について 

#Ubuntu の Mysql と PowerDNS のバージョン組み合わせでRestAPIが使えなくなる現象について 
ある仕事でオフラインの Ubuntu に debパッケージで mysql-server と PowerDNS をインストールし、 
運用ではスケジュールで各マシンのIP情報を監視し、変更内容をバッチコマンドの RestAPIで DNS情報を更新するような使い方だったのですが、
**通常のdebパッケージだけでインストールしたら何故か RestAPIでの DNS情報の更新ができない現象になったので、**
<h4>同じ現象で困った人は以下にこの現象になった環境を記載しますので、対処方法を参考にしていただければ幸いです。</h4>

##現象環境
| OS | DNS | DB |
|:-|:-|:-|
| Ubuntu20.04 | pdns-server_4.2.1-1build2 | mysql-server_8.0.22-0|

<dl>
  <dt>主に使用したdebパッケージ</dt>
  <dd>
    libmysqlclient21_8.0.22-0ubuntu0.20.04.3_amd64.deb<br>
    mysql-client_8.0.22-0ubuntu0.20.04.3_all.deb<br>
    mysql-client-8.0_8.0.22-0ubuntu0.20.04.3_amd64.deb<br>
    mysql-client-core-8.0_8.0.22-0ubuntu0.20.04.3_amd64.deb<br>
    mysql-common_5.8+1.0.5ubuntu2_all.deb<br>
    mysql-server_8.0.22-0ubuntu0.20.04.3_all.deb<br>
    mysql-server-8.0_8.0.22-0ubuntu0.20.04.3_amd64.deb<br>
    mysql-server-core-8.0_8.0.22-0ubuntu0.20.04.3_amd64.deb<br>
    pdns-server_4.2.1-1build2_amd64.deb<br>
    pdns-backend-bind_4.2.1-1build2_amd64.deb<br>
    pdns-backend-mysql_4.2.1-1build2_amd64.deb<br>
  </dd>
  <dt>（関連パッケージを入れると多くなってしまうので割愛させていただきます。）</dt>
</dl>

インストールは検索などで得た上記のようなパッケージでそのままインストールして問題ありませんが、
PowerDNS 用の初期設定で使う Mysql 用スキーマ（テーブル作成SQL） に問題がありました。
パッケージは以下のものになります。
pdns-backend-mysql_4.2.1-1build2_amd64.deb
インストールされると下記にMySQL用のスキーマが下記 Dir に入ります。
/usr/share/pdns-backend-mysql/schema/schema.mysql.sql
**これをそのまま実行して初期設定をしたら何故か RestAPIでの DNS情報の更新ができない現象になりました。**
設定ファイルの
/etc/powerdns/pdns.conf
api=yes
になっているので、APIは使えるはずです。。。

##考察
まず、大きな違いのあるアップデートを考えると、Mysql の バージョンアップでの utf8mb4 対応などが思い付きました。
utf8 の4バイト文字対応です。
これを原因とするトラブル対応はいろいろあったのでこれに関わる部分を修正する事にしました。

##対処方法
予想で対処したのですが、結果は正常に各機能が動作するようになったのでこの方法を記載します。
インストール用スキーマの内容を確認。

各テーブル内の項目毎に文字列部分には utf8 を指定していたので、
この部分を削除

```
 VARCHAR(40) CHARACTER SET 'utf8' DEFAULT NULL,
　↓　↓　↓
 VARCHAR(40) DEFAULT NULL,

```

項目ではなく、各テーブルのデフォルト文字列を utf8mb4 に変更。

```
Engine=InnoDB CHARACTER SET 'latin1';
　↓　↓　↓
Engine=InnoDB CHARACTER SET 'utf8mb4';

```

一度作成されたDatabaseを削除して、この修正したスキーマで初期設定を行い起動する事で RestAPI で DNS情報を更新できるようになりました。
<h3>今後のPowerDNS 側のアップデートで対応される事を期待しております。</h3>
3秒で終わるLaravel開発環境構築

# はじめに
DockerでLaravelの開発環境構築するか〜と思ってネット上をサーフィンしていたところ、2020年末にLaravelが公式に[Laravel Sail](https://laravel.com/docs/8.x/sail)というめちゃくちゃ便利なDocker環境をリリースしていたのでまとめました。
Dockerの知識がない方でも簡単に始められます。Laravel Sail初見の方はぜひ騙されたと思ってやってみてください！

# 環境
- macOS Big Sur
- Docker Desktop 3.3.3

Laravel Sailを使うためには`Docker Desktop`のインストールが必要です。

https://docs.docker.jp/docker-for-mac/install.html

https://docs.docker.jp/docker-for-windows/install.html

# 新規Laravelアプリの作成とSailのインストール

```terminal:terminal
$ curl -s https://laravel.build/<アプリ名> | bash
```
アプリの作成とsailのインストールは同時に行われます。
これで`mysql`・`redis`・`meilisearch`・`selenium`・`mailhog`がデフォルトで利用できるようになりますが、`with`クエリを使って

```
mysql
pgsql
mariadb
redis
memcached
meilisearch
selenium
mailhog
```

から使いたいサービスを個別指定することも可能です。

```terminal:mysqlとredisを指定する例
$ curl -s "https://laravel.build/example-app?with=mysql,redis" | bash
```

#既存のLaravelアプリにSailをインストールする場合

```terminal:terminal
$ composer require laravel/sail --dev
$ php artisan sail:install
```

# アプリの起動
```terminal:terminal
$ cd <Laravelアプリ>
$ ./vendor/bin/sail up -d
```

`http://localhost/`にアクセスするとLaravelの初期画面が、`http://localhost:8025`にアクセスするとMailHogが表示されます。Sailをバックグラウンドで起動するにはデタッチの`-d`オプションを追加します。

# エイリアス設定
呼び出し時に毎回`./vendor/bin/sail`と入力するのが面倒なので、エイリアスを設定します。こちらの記事を参考にさせて頂きました。

https://qiita.com/terufumi1122/items/1bbb1cf96e376e30e9fc

```terminal:bashシェルの場合
$ alias sail='./vendor/bin/sail'
$ source ~/.bash_profile
```

```terminal:zshシェルの場合
$ alias sail='./vendor/bin/sail'
$ source ~/.zshrc
```

# コマンド実行

```terminal:artisanコマンド
$ sail artisan make:controller コントローラ名
```

```terminal:phpコマンド
$ sail php --version
```

```terminal:composerコマンド
$ sail composer require ベンダー名/パッケージ名
```

```terminal:nodeコマンド
$ sail node --version
```

```terminal:npmコマンド
$ sail npm run dev
```

```terminal:phpunitコマンド
$ sail test 
```

# 例：Vueのセットアップ

コンテナ起動、停止以外は普段通りです。どれだけ普段通りか示すためにVueとの連携手順を載せておきます。

```terminal:terminal
laravel/uiのダウンロード
$ sail composer require --dev laravel/ui #この時点でresources/js/components以下にvueコンポーネントが設置される

vueの認証スカフォールドの生成
$ sail artisan ui vue --auth #認証機能をつけたい場合(ちなみにlaravelはjetstreamを推奨してます)

パッケージのインストール
$ sail npm install

ビルド
$ sail npm run dev #または sail npm run watchで自動コンパイル
```

# アプリ(コンテナ)の停止

```terminal:terminal
$ sail down #または'Ctr + C'
```

# おわりに

いかがでしたか？Docker Desktopがあらかじめインストールされていた方は

```terminal:terminal
$ curl -s https://laravel.build/<アプリ名> | bash
$ ./vendor/bin/sail up
```

の2行、時間にして約3秒くらいで開発環境構築が完了しましたね！(適当)

自分でDockerfile, docker-composeファイルを作る手間なくMySQL, mailサーバ等開発における必要最低限の機能が用意されるのでめちゃくちゃ便利です。

まだ試してない方はぜひ使ってみてください！

# 参考

https://laravel.com/docs/8.x/sail