OmniAICreator/Qiita-1.07M huggingface dataset:
備忘録_スマホアプリ開発の現場に入る前に知っておくとよいこと。

#はじめに
自分はWeb系のエンジニアです。
スマホアプリ開発経験は０です。

今回、Webの技術でスマホアプリを開発する現場に常駐することになったので、
自分で調べたことをまとめます。

自分のアウトプット用なのと、
同じ感じの人の助けになればと思います。

理解が間違っているところがあれば、ご指摘頂けると嬉しいです。

#基礎知識
スマホのOSは__Android__と__iOS__がある。
日本ならiPhoneが多いけど、iPhoneだけ対応ってことは少ない。

Webと違いリリースは、サーバにデプロイして完了とはならない。
androidならPlayストア、iOSならApp Storeにアプリを公開する。

そのため、GoogleやAppleなりの審査がある。

つまるとこ、android用、iOS用で作らないといけない。

クロスプラットフォームな開発できるFWでも、プログラムは共通だけど、
リリースする際にAndroidとiOSで、それ用にビルドしてあげないといけない。

基本、__Android Studio__と__Xcode__は必要って認識で大丈夫だと思う。(現場によって異なるけど)

##Android スマートフォン
ユーザがアプリをスマホに入れるなら基本的に__Playストア__からアプリをインストールする。
Playストアからアプリを配布するには__Google Play Developer__に登録が必要。

###Playストアにアプリを公開するには?
__Google Play Console__というPlayストアへのAppの提出、Appの管理ができるサービスがある。
Google Play Consoleで以下を実施。
1. アプリ情報を登録
2. apkファイルをアップロード(※apkファイルではなくAndroid App Bundleを推奨)
3. レーティングを設定する
4. 価格と販売/配布地域を設定する
5. Appを審査へ提出
6. App ステータスの確認と審査で出た問題を解決する
7. 審査が承認されたらPlayストアに公開される。
※Android App Bundleを推奨。
2021年8月より、Google Playでの新規アプリの公開はAndroid App Bundleで行う必要がある。
アプリのコンパイル済みコードとリソースがすべて含まれた公開形式。
APKの生成と署名はGoogle Playが行う。

Google PlayはApp Bundleから、デバイス設定毎に最適化したAPKを生成、配信する。
それにより、個々のデバイスでAppを実行するのに必要なコードとリソースだけがDLされる。
Developerは、多様なデバイスのサポートを最適化する為に複数のAPKをビルド、署名、管理する必要がない。

###apkファイル?
__apk__ Android Application Packageの略。
ZIP形式でアーカイブファイルの一種。
拡張子は.apk
決まった手順でAndroid端末にアプリをインストールできるファイル。
ビルド後にできるファイルと思えば大丈夫。

作成方法(ビルド方法)はコマンドかAndroid Studioを利用。
僕がいる現場は、ビルドする時だけAndroid Studioを使っている。
コマンドでするにもAndroidのsdkが必要だから、Androi Studioはインストールした方が楽だった気がしますが、
もっと楽な方法はあるかもしれません。

##iPhone
ユーザがアプリをiPhoneに入れるなら__App Store__からアプリをインストールする。
App Storeからアプリを配布するには__Apple Developer Program__に登録が必要。

###App Storeにアプリを公開するには?
__App Store Connet__というApp StoreへのAppの提出、Appの管理ができるサービスがある。
App Store Connectで以下を実施。
1. 契約書に同意し、税金と銀行口座情報を入力する。
2. ユーザを追加して役割を割り当てる
3. Appを追加してビルドをアップロードする
4. 価格および配信状況を設定する
5. Appを審査に提出する。
6. App ステータスの確認と審査で出た問題を解決する
7. 審査が承認されたらApp Storeに公開される。

細かいところは、Appleの公式のガイドラインで確認してください。
少し混乱したのが、Appleの場合、Appをアップロードするのにアーカイブファイルを作成してアップロードするみたいな方法は一般的ではないみたい。
XcodeからGUIでビルドを実行して、そのままApp Store Connnectにアップロードできる。
アーカイブファイルを作成してアップロードする方法もあるようです。

#開発フロー

企画
↓
要件
↓
設計・実装
↓
ビルド
↓
テスト
↓
審査
↓
リリース

上記の流れで開発していく。
企画、要件、設計・実装の流れはWEBとさほど変わらない。

大きく異なるのはビルドとテスト。
自分はクロスプラットフォームな開発ができるFWを使って開発している現場だが、
ビルドをandroid用、iOS用でビルドする。

このビルドが大変、混乱した。

色々な方法があると思うが自分がいる現場は以下。

__Android__
Android Studioでapkファイルを作成する。
adb(Android Debug Bridge)コマンドがかかれたbatで実機にインストールする。

__iOS__
Xcodeでプロジェクトを開く。(FWでios用のプロジェクトは作成済み)
Xcodeから実機にインストールする。

テスト
実機で動作確認を行う。
デバックは、USBでPCに接続して、下記で確認を行う。

__android__
Chrome Inspector

__iOS__
Safari Inspector

#おわりに
ざっくりと当たり前のことを書きましたが、OSによってビルド方法が異なる、審査っていう工程があるっていうのを認識して
その周りを学習しないといけないです。
IonicやReact NativeとかはWebが分かるなら大丈夫だよーっていうのを信じたら痛い目にあいました。
AWSで自動デプロイした時、ArgumentError: Index name 'index_boards_on_user_id' on table 'boards' already existsが出た際の解決法

#開発環境
AWSサーバー使用
自動デプロイツールcapistrano使用
#状況
ローカル環境で、データベースを一度削除し、作り直して、'bundle exec cap production deploy'コマンドを実行した際に、以下のようなエラーが出た。

```
Caused by:
 DEBUG [7ec9cb7c] 	ArgumentError: Index name 'index_boards_on_user_id' on table 'boards' already exists
 DEBUG [7ec9cb7c] 	/var/www/forum-lastapp/shared/bundle/ruby/2.6.0/gems/activerecord-6.0.3.6/lib/active_record/connection_adapters/abstract/schema_statements.rb:1197:in `add_index_options'
/var/www/forum-lastapp/shared/bundle/ruby/2.6.0/gems/activerecord-6.0.3.6/lib/active_record/connection_adapters/mysql/schema_creation.rb:65:in `index_in_create'
/var/www/forum-lastapp/shared/bundle/ruby/2.6.0/gems/activerecord-6.0.3.6/lib/active_record/connection_adapters/abstract/schema_creation.rb:49:in `block in visit_TableDefinition'
/var/www/forum-lastapp/shared/bundle/ruby/2.6.0/gems/activerecord-6.0.3.6/lib/active_record/connection_adapters/abstract/schema_creation.rb:49:in `map'
/var/www/forum-lastapp/shared/bundle/ruby/2.6.0/gems/activerecord-6.0.3.6/lib/active_record/connection_adapters/abstract/schema_creation.rb:49:in `visit_TableDefinition'
/var/www/forum-lastapp/shared/bundle/ruby/2.6.0/gems/activerecord-6.0.3.6/lib/active_record/connection_adapters/abstract/schema_creation.rb:14:in `accept'
/var/www/forum-lastapp/shared/bundle/ruby/2.6.0/gems/activerecord-6.0.3.6/lib/active_record/connection_adapters/abstract/schema_statements.rb:315:in `create_table'
/var/www/forum-lastapp/shared/bundle/ruby/2.6.0/gems/activerecord-6.0.3.6/lib/active_record/connection_adapters/mysql/schema_statements.rb:81:in `create_table'
/var/www/forum-lastapp/shared/bundle/ruby/2.6.0/gems/activerecord-6.0.3.6/lib/active_record/migration.rb:890:in `block in method_missing'
/var/www/forum-lastapp/shared/bundle/ruby/2.6.0/gems/activerecord-6.0.3.6/lib/active_record/migration.rb:858:in `block in say_with_time'
/var/www/forum-lastapp/shared/bundle/ruby/2.6.0/gems/activerecord-6.0.3.6/lib/active_record/migration.rb:858:in `say_with_time'
以下省略
```
#解決法
ローカルでデータベース関連の内容を修正した場合の対処が必要となる。

```
本番環境で以下二つのコマンドを実行する
% rails db:drop RAILS_ENV=production DISABLE_DATABASE_ENVIRONMENT_CHECK=1
% rails db:create RAILS_ENV=production
```
その後、一度プロセスをkillした上で「bundle exec cap production deploy」を実行すると、エラーは解決された。
Reactとの連携３

```rb:hello_react.jsx
// Run this example by adding <%= javascript_pack_tag 'hello_react' %> to the head of your layout file,
// like app/views/layouts/application.html.erb. All it does is render <div>Hello React</div> at the bottom
// of the page.

import React from 'react'
import ReactDOM from 'react-dom'
import PropTypes from 'prop-types'

const Calc = props => {
  let n = props.number;
  let total = 0;
  for (let i = 0;i <= n;i++){
    total += i;
  }
  return (
    <div>ゼロから{props.number}までの合計は「{total}」です。</div>
  );
}

Calc.defaultProps = {
  number: 0
}

Calc.propTypes = {
  number: PropTypes.integer
}

document.addEventListener('DOMContentLoaded', () => {
  let el = (<div>
    <Calc number="100" />
    <Calc number="200" />
    <Calc number="300" />
    </div>);
    let dom = document.querySelector('#hello');
    ReactDOM.render(el, dom);
})
```

![20210502-182942.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/960935/3d73dda5-c85a-3e9f-9591-a8db604c9011.png)



**for文の基本書式**
for 文では指定した回数だけ繰り返し処理を行うことができます。


`for (初期化式; 条件式; 変化式){`
  `実行する文1;`
  `実行する文2;`
  `...`
`}`


代入演算子

|名称|略記演算子|意味|
|--|--|--|
|代入|x = y|x = y|
|加算代入|x += y|x = x + y|
|減算代入|x -= y|x = x - y|
|乗算代入|x *= y|x = x * y|
|除算代入|x /= y|x = x / y|
|剰余代入|x %= y|x = x % y|
|べき乗代入|x **= y|x = x ** y|
|左シフト代入|x <<= y|x = x << y|
|右シフト代入|x >>= y|x = x >> y|
|符号なし右シフト代入|x >>>= y|x = x >>> y|
|ビット論理積 (AND) 代入|x &= y|x = x & y|
|ビット排他的論理和 (XOR) 代入|x ^= y|x = x ^ y|
|ビット論理和 (OR) 代入|x I= y|x = x I y|
|論理積代入|x &&= y|x && (x = y)|
|論理和代入|x II= y|x II (x = y)|
|Null 合体代入|x ??= y|x ?? (x = y)|

比較演算子

|演算子|説明|true を返す例|
|--|--|--|
|等価 (==)|被演算子が等しい場合にtrueを返します。|3 == var1,"3" == var1,3 == '3'|
|不等価 (!=)|被演算子が等しくない場合にtrueを返します。|var1 != 4,var2 != "3"|
|厳密等価 (===)|被演算子が等しく、かつ同じ型である場合にtrueを返します。Object.is や JavsScript での等価も参照してください。|3 === var1|
|厳密不等価 (!==)	被演算子が等しくなく、かつ/または同じ型でない場合にtrueを返します。|var1 !== "3",3 !== '3'|
|より大きい (>)|左の被演算子が右の被演算子よりも大きい場合にtrueを返します。|var2 > var1,"12" > 2|
|以上 (>=)|左の被演算子が右の被演算子以上である場合にtrueを返します。|var2 >= var1,var1 >= 3|
|より小さい (<)|左の被演算子が右の被演算子よりも小さい場合にtrueを返します。|var1 < var2,"2" < 12|
|以下 (<=)|左の被演算子が右の被演算子以下である場合にtrueを返します。|var1 <= var2,var2 <= 5|

>MDN web Docs
>https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Expressions_and_Operators

|インクリメント演算子|++|
|--|--|
|デクリメント演算子|--|

インクリメント演算子は、1を足し、デクリメント演算子は、1を引く、という機能を持ちます。

i++はiに１を加えます
Safariで見ているページを Chromeで開くブックマークレット【iPhone】

#はじめに
いまSafariで見ているページをGoogle Chromeで開くブックマークレットです。iPhone Safariで動作確認しました。

#コード
```js
javascript:location.protocol='googlechrome:';
```

#解説
ChromeのURLスキームは``googlechrome:``
です。表示しているページの通信プロトコル（URLの先頭）をこれに置き換えます。それ以外の部分は変更しません。Qiitaを例に取るとこうなります。

**使用前**
https://qiita.com

**使用後**
googlechrome://qiita.com

この時スマホはSafariで``https``による通信を行わず、Chromeを起動してQiitaを開きます。

#その他のブラウザ
Firefoxで開くためのコードほかを挙げておきます。

**Firefox**

```js
javascript:location.href='firefox://open-url?url='+location.href;
```

**Firefox Focus**

```js
javascript:location.href='firefox-focus://open-url?url='+location.href;
```

**DuckDuckGo**

```js
javascript:location.protocol='ddgQuickLink:';
```

#あとがき
HTMLのaタグでmailto:と指定すればメールソフトが立ち上がるリンクとなる、というのはよく知られていると思います。あれもURLスキームの１つを利用したもの。アプリの切り替わり方は似たような感じになります。

本日もありがとうございました。

#参考
URLスキームについては、こちらが詳しいです。

1. https://ios.gadgethacks.com/how-to/always-updated-list-ios-app-url-scheme-names-paths-for-shortcuts-0184033/
とりあえずgradleを触る(mac)

ここに書いてあるチュートリアルのメモ書き。
https://docs.gradle.org/current/samples/sample_building_java_applications.html

# gradleインストール
```
brew install gradle
```

手動インストールは面倒なのでhomebrew利用。

# おためしPJ作成
gradle initで選択するもの。

- Select type of project to generate: 
    - **2: application** を選択。
- Select implementation language: 
    - **3: Java** を選択。
- それ以外はデフォルト値（エンター押すだけ）でOK

```
$ mkdir demo
$ cd demo
$ gradle init
Starting a Gradle Daemon (subsequent builds will be faster)

Select type of project to generate:
  1: basic
  2: application
  3: library
  4: Gradle plugin
Enter selection (default: basic) [1..4] 2

Select implementation language:
  1: C++
  2: Groovy
  3: Java
  4: Kotlin
  5: Scala
  6: Swift
Enter selection (default: Java) [1..6] 3

Split functionality across multiple subprojects?:
  1: no - only one application project
  2: yes - application and library projects
Enter selection (default: no - only one application project) [1..2] 

Select build script DSL:
  1: Groovy
  2: Kotlin
Enter selection (default: Groovy) [1..2] 

Select test framework:
  1: JUnit 4
  2: TestNG
  3: Spock
  4: JUnit Jupiter
Enter selection (default: JUnit 4) [1..4] 

Project name (default: demo): 
Source package (default: demo): 

> Task :init
Get more help with your project: https://docs.gradle.org/7.0/samples/sample_building_java_applications.html

BUILD SUCCESSFUL in 42s
2 actionable tasks: 2 executed
```

- demoディレクトリの下にPJができあがっているはずなので確認する。

# おためしHelloWorld
```
$ ./gradlew run
Downloading https://services.gradle.org/distributions/gradle-7.0-bin.zip
..........10%...........20%...........30%..........40%...........50%...........60%...........70%..........80%...........90%...........100%
Starting a Gradle Daemon, 1 incompatible Daemon could not be reused, use --status for details

> Task :app:run
Hello World!
```

# おためしビルド
```
$ ./gradlew build

BUILD SUCCESSFUL in 8s
7 actionable tasks: 6 executed, 1 up-to-date
```


ビルドすると以下ファイルができあがる。

```
app/build/distributions/app.tar
app/build/distributions/app.zip
app/build/libs/app.jar
```
覚えておきたいAWSの用語

# はじめに
自分の学習メモ用です。
とりあえず単語だけ並べています。

将来的に解説の記事をかければと思っています。

- IAM
    - ユーザー、グループ、ポリシー、ロール
- VPC
    - CIDRブロック、サブネット、アベイラビリティーゾーン、インターネットゲートウェイ、NATゲートウェイ、ルートテーブル、セキュリティグループ
サイコロの出目によって出力する文章を変化させる処理

ポートフォリオ作成のためのphpでの機能実装。

出目の数が4以上の場合「スライムの攻撃をかわした」
それ以外の場合「スライムから10のダメージを受けた」を出力する。

```
<?php

$dice = rand(1,6);
echo "サイコロは".$dice."\n";
if($dice >= 4){
    echo "スライムの攻撃をかわした";
}elseif ($dice <= 3) {
    // code...
    echo "スライムから10のダメージを受けた";
}
?>
```

学習当初はif文とelseifすら理解できていなかったが、さすがに実務で学習すれば理解が定着しているように思う。
ダブルクォーテーション「"」ではなくウムラウト「¨」が入力されてしまう時の対処

## 事象

想定しているダブルクォーテーション「"」ではなくウムラウト「¨」が入力される。
  
  
## 環境

OS:  OracleLinux 6.7(VertualBox上の仮想マシン)  
キーボード:  Keychron k2(v1) ※英字キーボード
  
  
## 発生状況

ターミナルで vi を使用してファイル編集を試みていた。  
ダブルクォーテーションを入力しようとしたところ「"」ではなくウムラウト記号「¨」が入力された。  
念のため、ターミナル外でテキストファイルを開いて入力してみたが同様の結果だった。  
  
  
## 対処

キーボードのレイアウト設定が「デッドキー付き」になっていたので、別のレイアウトに変更した。  
※OS インストール時に自分で意図せず選択していたのかも。
  
  
## 対処手順

GUIで以下の対処を実施した。

1. 画面上部バーにある「システム」を選択
2. 「設定」を選択
3. 「キーボード」を選択
4. 「レイアウト」タブを選択
5. 「追加」ボタンを選択
6. 「国」で「アメリカ合衆国」を選択
7. 「系列」で「英語(US)」を選択
  
  
## 雑記

デッドキーとやらが有効なレイアウトに設定していた事が原因で発生していたようです。
https://ja.wikipedia.org/wiki/%E3%83%87%E3%83%83%E3%83%89%E3%82%AD%E3%83%BC
JavaScriptでオブジェクトを使う


## JavaScriptの基礎知識

HTMLでJavaScriptを読み込む方法

**scriptタグを使う**

```HTML
<script>
この中にプログラムを書いていく
</script>
```

例として日付を表示させるには

```HTML
<script>
document.write('<p class="days">2020/01/01</p>');
//末尾にセミコロンをつけるのはJavaScriptの決まり
</script>
```

JavaScript部分の解説↓

**document.write(　)**

* document→オブジェクト（対象）
* write→メソッド（命令）
* (　)→パラメーター（設定）

↓
**訳：「オブジェクトに対してパラメーターの内容をメソッドしてね」**

---

###オブジェクトの考え方

オブジェクトのみだと実体がない！（設計図のみの状態）
なので、
オブジェクトを使うときは実体化＝**インスタンス化**してから使う

**インスタンスにするには**
newメソッドを使って変数に代入する

```HTML
let today = new Date();
```

インスタンスにしたところで、

```HTML
<script>
let today = new Date();
document.write('<p>今日は'+ today.getDate() '日です</p>');
</script>
```

まず、new Dateでオブジェクトをインスタンス化し、
設計図のみだったものから、todayという実体を作る。
作った実体に対して　.getDateメソッド　で日付を取得する。
（document.writeのdocumentも実はブラウザが用意していたインスタンス）



引数に値を入力することも可能↓

```HTML
<script>
let newYear = new Date(2021/1/1);
document.write('<p>元旦は'+ newYear.getDate() '日です</p>');
</script>

//元旦は2021/1/1日です
```


こんな感じでオブジェクトはインスタンス化して使う。
※ただし一部例外もあり（Mathオブジェクトとか）

以上
[SQL] LeetCodeの無料問題集を活用して、SQLを使いこなし（3）

#Q178 Rank Scores
![Q178.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1082021/20017876-bf7b-d880-6f30-af3ce39c8781.png)

##質問
スコアをランク付けするSQLクエリを記述します。 2つのスコアの間に同点がある場合は、両方のランキングが同じである必要があります。 同点の場合、次のランキング番号は次の連続する整数値である必要があることに注意してください。

たとえば、上記のスコアテーブルが与えられた場合、クエリは次のレポートを生成する必要があります（最高スコアの順に並べる）。

##Code書き方のHINT
この問題は順位付けの関数についての理解を問われています。
REF:https://johobase.com/sqlserver-rank-denserank-rownumber-ntile/

###rank()：
順位を付ける関数。
比較対象の値が同じ場合は同じ順位になり、その次は順位を飛ばします。

###dense_rank()：
順位を付ける関数。
比較対象の値が同じ場合は同じ順位になり、その次は順位を飛ばしません。

###row_number()：
連番ををつける関数。
比較対象の値が同じ場合であっても同じ順位にはならず、順位をカウントアップします。

###イメージ図
![Q178image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1082021/33ede047-8007-0b6e-c3c7-ac87b0b2f69c.png)

rank関数とdense_rank関数の違いは
同じ順位になった場合、その次は順位を飛ばすかどうかです。

<b>今回は、順位を飛ばさないと要求されているので、dense_rankを使います。</b>

##Code

```SQL
select Score,
dense_rank() over (order by Score desc) as `Rank`
from Scores
```

##Code検証結果
![Q178_result.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1082021/b318cd5e-38f3-0b3a-fb25-7421e8df47a5.png)
こんな書き方もできるSwift


#init

```swift
class Sample {

 static let sample:Sample = .init()
 private init() {}

}
```
ここではSampleクラスの中で、そのインスタンスを内部にあるsampleに代入している。その際、`.init()`がイニシャライザとして使われており、ドットの前は self ＝ Sample を指す。動作の上では`.init()`で十分で、private initはなかったとしても問題ない。ただし、ここではスコープを限定するためにinitに対してprivateという属性をつけている。

普段インスタンスを別クラスからしか作った試しがなく、自身をクラス内の変数にインスタンスとして代入するというのはみたことがありませんでした。またinitメソッドも`.init()`のようには作ったことがなく、スペースを取って別物のように扱っていたので、インスタンスを作る際に`.init()`で済ませられるのはとても新鮮に感じられました。



追記していきます。
Swift5.4 変更点まとめ

2021/04/26に[Swift5.4のリリース](https://swift.org/blog/swift-5-4-released/)があったのでその変更点をまとめました。

## Swiftのアップデート一覧

- Support for multiple variadic parameters in functions, subscripts and initializers ([SE-0284](https://github.com/apple/swift-evolution/blob/main/proposals/0284-multiple-variadic-parameters.md)): 複数の可変長引のサポート
- Extend implicit member syntax ([SE-0287](https://github.com/apple/swift-evolution/blob/main/proposals/0287-implicit-member-chains.md)): 暗黙のメンバー構文の拡張
- Result builders ([SE-0289](https://github.com/apple/swift-evolution/blob/main/proposals/0289-result-builders.md))
- Local functions supporting overloading: ローカル関数におけるオーバーロードのサポート
- Property wrappers for local variables: ローカル変数におけるプロパティラッパーのサポート

## Support for multiple variadic parameters in functions, subscripts and initializers ([SE-0284](https://github.com/apple/swift-evolution/blob/main/proposals/0284-multiple-variadic-parameters.md))

複数の可変長引数のサポート

今まで可変長引数は1つしか指定できなかったが複数指定できるようになった

```swift
func foo(_ a: Int..., b: Double...) { }

struct Bar {
  subscript(a: Int..., b b: Int...) -> [Int] { a + b }

  init(a: String..., b: Float...) { }
}
```

## Extend implicit member syntax ([SE-0287](https://github.com/apple/swift-evolution/blob/main/proposals/0287-implicit-member-chains.md))

暗黙のメンバー構文の拡張

メンバーアクセスのチェーンをサポートし以下のように記述できるようになった。

```swift
let milky: UIColor = .white.withAlphaComponent(0.5)
let milky2: UIColor = .init(named: "white")!.withAlphaComponent(0.5)
let milkyChance: UIColor? = .init(named: "white")?.withAlphaComponent(0.5)
```

## Result builders ([SE-0289](https://github.com/apple/swift-evolution/blob/main/proposals/0289-result-builders.md))

新しい構造体を `@resultBuilder` で定義することで、新しい結果ビルダーを定義することができる。定義するメソッドとプロパティは `static` にする必要がある。
以下の結果ビルダーを使えば、その以下のように文字列を結合できる。

```swift
@resultBuilder
struct StringBuilder {
    static func buildBlock(_ strings: String...) -> String {
        strings.joined(separator: "\n")
    }
}
```

値を定義する際には、`buildBlock` メソッドを直接使用する必要があった。
そのため、各`String`要素にはコンマを入れなければならなかった。

```swift
let stringBlock = StringBuilder.buildBlock(
    "Tokyo",
    "Kanagawa",
    "Saitama",
    "Chiba"
)
```

代わりに関数の中で`@StringBuilder`を使えば、カンマを使わずに同じことができる。

```swift
@StringBuilder func makeSentence() -> String {
    "Tokyo"
    "Kanagawa"
    "Saitama"
    "Chiba"
}
```

## Local functions supporting overloading

ローカル関数におけるオーバーロードのサポート

以下のように記述できるようになった。

```swift
func outer(x: Int, y: String) {
  func doIt(_: Int) {}
  func doIt(_: String) {}

  doIt(x) // calls the first 'doIt(_:)' with an Int value
  doIt(y) // calls the second 'doIt(_:)' with a String value
}
```

## Property wrappers for local variables

ローカル変数におけるプロパティラッパーのサポート

以下のように記述できるようになった。

```swift
@propertyWrapper
struct Wrapper<T> {
  var wrappedValue: T
}

func test() {
  @Wrapper var value = 10
}
```
【Flutter】TextFormField, TextField onChanged使用時のカーソル位置が最初に戻ってしまう不具合対応

## 発生していた不具合
TextFormFieldに入力するたびにカーソルの位置が最初に戻ってしまう現象が発生していました。
<img src=https://user-images.githubusercontent.com/47769514/116806605-7d254b80-ab58-11eb-8765-edba9919b48a.gif width=300>

## 原因
入力ごとに毎回初期値が変更されたとみなされ、カーソルの位置が初期状態に戻ってしまっていた。

## 修正方法
初期値入力時に、テキスト情報に併せて、offset情報も渡す。

コード例

```dart
controller: TextEditingController.fromValue(
                TextEditingValue(
                    text: initialString ?? "",
                    selection: TextSelection.collapsed(offset: 
                        initialString?.length ?? 0), 
                ),
            ),
```

実際の修正例
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261934/ded8a138-fa47-d699-10ad-9ec77b4bbdf1.png)


## 参考記事

https://stackoverflow.com/questions/56872752/flutter-how-to-get-cursor-in-text-field-to-stop-moving-to-the-beginning/56875613
WSL2上のGUI(WSLg)で日本語入力を行う際の最低限の設定

# 前提条件
Windows10 Build:21364以降のWSL2でLinuxカーネルのアップデートが完了していること
Windows11でも動作確認済み

# 設定コマンド

```sh:
sudo apt update
sudo apt install -y fcitx-bin fcitx-mozc dbus-x11 language-pack-ja 
sudo update-locale LANG=ja_JP.UTF8

cat << EOS | sudo tee /etc/fonts/local.conf
<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<fontconfig><dir>/mnt/c/Windows/Fonts</dir></fontconfig>
EOS

cat << EOS >> ~/.profile
export GTK_IM_MODULE=fcitx
export QT_IM_MODULE=fcitx
export XMODIFIERS=@im=fcitx
export DefaultIMModule=fcitx
fcitx-autostart &> /dev/null
EOS
```

# 日本語入力

GUIアプリを立ち上げてctrl+spaceで入力可能となります
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/267629/a25bd8e1-ae0c-88f1-0aaa-9549eeb7c45c.png)

# Windowsからの直接起動

Windowsが自動生成するショートカットだとbashは間に挟みませんが、これをやらないと日本語入力が出来ません

```bash:
wslg.exe ~ -d Ubuntu bash --login -c gedit
```
backgroundをif文で使うときに気をつけたいこと

## 想定する読者
javascriptのコードを書いていて、ある要素のbackgroundの値を分岐判定に使おうと思った人。(例えばすごろくゲームを作っていて、赤色マス、青色マスの判定を画面の色に頼っていたとか)
追記-- コメントよりご指摘頂いてますが、だいぶ穴のある記事になっています。ごめんなさい。

## きっかけ
 私は背景の色を#を使って指定してしまう癖があるのですが、backgroundの色に応じて処理を変えたいときにif(node.style.background="#~~~")という感じでかいても絶対にtrueの処理に入ってくれなっかのです。これの解決にはだいぶ頭を悩ませてしまったので、備忘録として記録しておきます。
　結論からいうとこれにはブラウザがbackgroundの値をどのような形で保持しているか、が原因でした。ブラウザが保存する形式に合わせないと==でも===でもfalseになってしまします。以下の実験では、プロパティの設定時ではいずれも有効と判断される記入例を並べ、if文にかけた時の判定を比べてみました。

## 実験コード
```javascript
 <body>
   <div id="box" style="width:300px;height: 300px;background: #f55;"></div>
<script type=text/javascript >
window.onload=function(){
    const box=document.getElementById("box");
    console.log(box.style.background);
    console.log(box.style.background=="#f55");
    console.log(box.style.background=="rgb(255,85,85)");
    console.log(box.style.background=="rgb(255, 85, 85)");
    console.log(box.style.background=="rgb(255, 85, 85) none repeat scroll 0% 0%");
//以下省略
```
#実験結果

### chromeの場合
rgb(255, 85, 85)　⇐ブラウザが保存している色情報の形式
false　⇐16進数表記　
false　⇐rgb表記（スペース無し） 
<font color="OrangeRed"><u>true</u></font> 　⇐rgb表記　
false　⇐色以外のrepeat設定などを含めた文


### firefoxの場合
rgb(255, 85, 85) none repeat scroll 0% 0%　　⇐ブラウザが保存している色情報の形式
false　⇐16進数表記　
false　⇐rgb表記（スペース無し） 
false　⇐rgb表記　　
<font color="OrangeRed"><u>true</u></font> 　⇐色以外のrepeat設定などを含めた文

\#ではじめる16進数表記での色指定(2行目)はif文では意図した処理になりません。rgbで指定するにも","の後にしっかりスペースを開けて合わせないといけないようですね(3,4行目参考)。また
私の環境では２つしか比べられませんでしたが、ブラウザによってbackgroundの保存形式が違うではないですか。

訂正 （この場合、backgroundColorでプロパティを保存してけば、両ブラウザでは保存形式は同じになり、3個目の照合でtrueになります。）
サンプルアプリレベルのZoomSDKをSwiftUIで実装するときに詰まったポイントN個

ひょんなことからZoomSDK使ったミーティングアプリをSwiftUIで実装するという素晴らしくチャレンジングな開発に関われたので忘備録も兼ねていろいろ書いていこうと思います。

今回はとりあえず動くものをと言うことで、[Zoom client SDKのSwiftサンプル](https://github.com/zoom/client-sdk-iOS-getting-started)と同じレベルくらいに持っていきます。

##つまりポイント①：ライブラリが一元管理できない
[Zoomさんの公式ドキュメント](https://marketplace.zoom.us/docs/sdk/native-sdks/iOS/getting-started/integration)では、FrameworkをXcodeに直入れする方法を丁寧に教えてくれています。

CocoaPodsで入れさせてくれ。。
同じことを思った先人がいたのか、方法はありました。他人のリポジトリを晒していいのか不安なので、気になる人は「zoomSDK CocoaPos」あたりで検索かけてみてください。多分一番上にきます。

##つまりポイント②： `unrecognized selector sent to instance`
まずは動作確認ということで、UIKitで組まれているclientSDKのサンプルにある`ViewController.swift`を適当にリネーム＆レイアウトをXibで書き換えて`UIViewControllerRepresentable`に噛ませて動かしてみました。結果はタイトル通りです。

どうやらデフォルトのZoomミーティング画面を表示するタイミングでエラーになっているらしい。
Life Cycleに**UIKit App Delegate**を選んでいたので、**SceneDelegate**が悪そう？と当たりをつけていくと案の定消したら動作しました。こちらの記事が大変参考になりました。
https://qiita.com/koogawa/items/3472398afd4d809148b0

##つまりポイント③：TextFieldを２つ含めたAlartがSwiftUIだけで組めない

clientSDKのサンプルとさよならするために上記の`ViewController.swift`に実装されていた内容をSwiftUIで実装し直します。しかしここでもまた問題が。

↓のようなミーティングIDとパスワードを入力させるAlartを出したかったのですが、そもそもTextField１つですら現時点で出すことができないという悲しみ。。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/329127/53be1198-146b-2969-85b4-b828404e9d54.jpeg" width="200">

こちらの記事がTextField１つの時の実装方法を書かれていたので、参考にさせていただきました。
`@Binding var text: String` を配列にして、UIAlertControllerに`forEach`で配列分`addTextField`してあげたりしたら複数並べられます。
https://www.yururiwork.net/%E3%80%90swiftui%E3%80%91textfield%E4%BB%98%E3%81%8Dalert%E3%82%92%E8%A1%A8%E7%A4%BA%E3%81%99%E3%82%8B/

##最後に
公式のリファレンスが難読性高かったり、実装例が全然見つからない難しさはありますが、走りきったらいい感じにハイになれそうです。次回はZoom色を削るためにUIのカスタマイズを行いたいと思います。
LinuxでグローバルIPv6通信するにはICMPv6もiptablesで通してね

Raspberry Pi をIPv6に繋ごうとしたらハマったのでメモ。

自宅にIPv6(IPoE)回線が開通したので、Raspberry Pi で `ip a` を叩いたが、リンクローカルアドレスのみ表示されて、グローバルユニキャストアドレスが見えない。。。
同じルーターに接続しているWindows端末では、ちゃんと[ダンシング亀](http://www.kame.net/)が見えるのに。

~~~
% ip a
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    inet6 fe80::xxxx:xxxx:xxxx:xxxx/64 scope link
       valid_lft forever preferred_lft forever
~~~

OSのバージョンやiptablesの設定について。

~~~
% cat /etc/os-release
PRETTY_NAME="Raspbian GNU/Linux 9 (stretch)"
NAME="Raspbian GNU/Linux"
VERSION_ID="9"
VERSION="9 (stretch)"
VERSION_CODENAME=stretch
ID=raspbian
ID_LIKE=debian
HOME_URL="http://www.raspbian.org/"
SUPPORT_URL="http://www.raspbian.org/RaspbianForums"
BUG_REPORT_URL="http://www.raspbian.org/RaspbianBugs"

% sudo ip6tables -L -v -n
Chain INPUT (policy DROP 2 packets, 370 bytes)
 pkts bytes target     prot opt in     out     source               destination
   37  3644 ACCEPT     all      lo     *       ::/0                 ::/0
 5326 6783K ACCEPT     all      *      *       ::/0                 ::/0                 state RELATED,ESTABLISHED

% sudo iptables -L -v -n
Chain INPUT (policy DROP 7894 packets, 2297K bytes)
 pkts bytes target     prot opt in     out     source               destination
   16   800 ACCEPT     all  --  lo     *       0.0.0.0/0            0.0.0.0/0
26736 7192K ACCEPT     all  --  *      *       0.0.0.0/0            0.0.0.0/0            state RELATED,ESTABLISHED
~~~

で、早速結論ですが、下記のみっつの事実がポイントでした。

- 今回の私の回線ではSLAAC(RA)によりグローバルユニキャストアドレスが決定される。
- SLAACの中でICMPv6が使われる
- 自ホスト発でも、このICMPv6のパケットは RELATED ルールでマッチしない

というわけで、

~~~
-A INPUT -p ipv6-icmp -j ACCEPT
~~~

というルールを追加して、

~~~
% sudo ip6tables -L -v -n
Chain INPUT (policy DROP 2 packets, 370 bytes)
 pkts bytes target     prot opt in     out     source               destination
   39  3784 ACCEPT     all      lo     *       ::/0                 ::/0
 5986 7626K ACCEPT     all      *      *       ::/0                 ::/0                 state RELATED,ESTABLISHED
  186 17076 ACCEPT     icmpv6    *      *       ::/0                 ::/0
~~~

としてあげれば無事グローバルユニキャストアドレスが取れました。

~~~
% ip a
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    inet6 2xxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxx/64 scope global mngtmpaddr noprefixroute dynamic
       valid_lft 2591910sec preferred_lft 604710sec
    inet6 fe80::xxxx:xxxx:xxxx:xxxx/64 scope link
       valid_lft forever preferred_lft forever
~~~
Raspberry Pi のsysbench fileio調査結果

家に転がってたラズパイ3B+とフラッシュUSBを使ってメディアNASサーバ(Nextcloud)を作ってみたら思ったより遅くて、結局ラズパイ4+SSD起動で作り直したんですが、体感で3倍くらいの速度差があったので、折角なのでsysbenchで数値を測ってみた結果を共有します。

# 環境
- ハードA) Raspberry Pi 3B+
- ハードB) Raspberry Pi 4(8GB)
- ドライブ1) SanDisk microSDXC SDSQUA4-128G-GN6MN (A1対応 Class10 U1)
- ドライブ2) SanDisk USBメモリ SDCZ430-256G (USB3.1)
- ドライブ3) Crucial SSD CT1000NX500SSD1 (SATA3)
- Ubuntu Server 21.04 ※共通
- sysbench 1.0.20 ※共通

# 組み合わせ
- ハードA(R3) + ドライブ1(SDカード), ドライブ2(USBメモリ)
- ハードB(R4) + ドライブ3(SSD)

# コマンド
```
sudo sysbench --file-test-mode=rndrw --test=fileio run
```

# sysbench結果
※結果は5回試行の平均値

|                     |                         | R3+SDカード  | R3+USBメモリ  | R4+SSD      | 
| ------------------- | ----------------------- | -------: | --------: | -------: | 
| File operations:    | reads/s:                | 287.07   | 196.99    | 946.344  | 
|                     | writes/s:               | 191.374  | 131.338   | 630.888  | 
|                     | fsyncs/s:               | 617.884  | 428.58    | 2023.55  | 
| Throughput:         | read, MiB/s:            | 4.486    | 3.076     | 14.788   | 
|                     | written, MiB/s:         | 2.99     | 2.052     | 9.856    | 
| General statistics: | total time(s):          | 10.06618 | 10.0765   | 10.04978 | 
|                     | total number of events: | 10918    | 7506.6    | 36088.2  | 
| Latency (ms):       | min:                    | 0.01     | 0.012     | 0.02     | 
|                     | avg:                    | 0.918    | 1.38      | 0.28     | 
|                     | max:                    | 61.01    | 191.774   | 11.022   | 
|                     | 95th percentile:        | 2.528    | 7.57      | 0.92     | 
|                     | sum:                    | 9971.102 | 9976.134  | 9951.17  | 

体感と同様に数値で見てもやはり3倍強程度の差がありました。そしてLatencyを見るとSSDの方がめちゃくちゃ安定してますね。コレはメモリ8GBの恩恵もあるような気がしてます。

## 余談
ラズパイ3B+でNextCloud動かした時は、SDカード上でMySQLを動かして、データストレージをUSBという構成で動かしてたんですが、画像アップロード中はLoadAverageが平均3〜5で、メモリもパツパツでフル回転な感じで、いつ死ぬかかなり不安だったので、精神の安定的な意味でもR4+SSD直起動(電源付きUSBハブ経由)構成にしたのは悪くない選択かなと。
ちなみにこんな感じ。
![R4_SSD_USBHUB.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33372/2bb5df5f-4d9b-b444-efef-9dcfd7a53b94.jpeg)
ケーブルとかハブとか繋ぎ方にこだわるとSSDはもう少し速度上がったりするのかもしれません。
誰か様の「俺の環境だともっと速いぜ！」みたいな情報お待ちしてます。
wsl pythonの仮想環境：pyenv+venv+pip

Pythonで自然言語処理や機械学習の勉強をしています。
Pythonの仮想環境について調べた結果を記録します。

### 環境

* windows 10 Home
* WSL2 +Ubuntu 18.04
* Python 3.6.9 → Python 3.9.4
* Visual Studio Code


### 仮想環境について調べた動機

現在、WSL(ubuntu18.04)にもともと入っていたPython3.6.9を使っており、
3.6のサポート終了が近づいているのでバージョンアップしようと思ったが、
環境を切り分ける方がいい気がしたので少し調べた。


### 各ツールについて知ったこと等のメモ

**pyenv：** 異なるバージョンのPythonを共存させ、切り替えて使つためのツール。
　pyenvは gitからコピーさせていただく。
　pythonインストール時に必要なツールをaptで入れる。
　環境変数の設定が必要（.profile, .bashrc等に書く）。
　　#インストール時に参考にさせていただいたサイト：
　　　https://www.python-izm.com/tips/pyenv/
　　　https://aadojo.alterbooth.com/entry/2020/08/19/095654
　　　https://home.hirosaki-u.ac.jp/heroic-2020/970/

**venv：** 開発プロジェクトごとに環境（利用パッケージ）を分けて管理するのに使う。

**virtualenv：** venvと同じようなことができるが、pytyon2系でも使えるらしい。venvより古くからあるらしい。

**pip：** パッケージをインストールするためのツール。インストールしたものをRequirement.txtというファイルに記録して、別の環境で再現するのに使える。

**pipenv：** パッケージの管理と仮想環境管理が両方行えるらしい。pipの代わりに使えるらしい。Requirement.txtの代わりにPipfileとPipfile.lockという2つのファイルに記録し、必須のものと依存しているものを分けて記録できるとのこと。

### 現時点で利用する環境

pyenv+venv+pipで管理しようと思います。

理由：

* pythonのバージョンアップをしてユーザー環境にpythonを置くのが当初の目的なので、
  pyenvで新バージョンを入れるのが良いと思った。
* 素人が勉強・練習するだけなので環境を分ける必要があるかどうかわからなかったが、
  後でリセットしやすいように仮想環境を作って使うことにした。そのためにvenvを使った。
  virtualenvの後継がvenvとのことで、そちらにした。
* pipenvだけでpyenv+venv+pipと同じ事ができる様子なのだが、
  先にpyenvを入れてしまっていて、venvとpipは入っていたので、上記構成で行くことにした。


### Visual Studio Codeでの利用

　venvで作った環境（フォルダー）を開いて、インタプリターとしてvenvの中にあるpythonを指定することで、"Hello python"が動いた。VS codeのターミナルでは(venv)という表示が出て、仮想環境のshellで動いている様子だった（特に設定しなくてもそうなった）。
GitHubでリポジトリを作成した後に出てくるコマンドラインの説明

# 0. はじめに
この記事は[GitHub](https://github.com/)でリポジトリを作成した後、リモートリポジトリにpushするまでのコマンドラインの説明です。

# 1. GitHubにリポジトリを作成すると…
GitHubにリポジトリを作成すると以下の画面になりますね。
この***...or create a new repository on the command line***に記載されている、コマンドライン通り打てばpushされます。
実際、このコマンドライン1行1行はどのような動作をしているのでしょうか…

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/205371/021c9971-f223-2d57-f8e2-c53df577aa42.png)

***...or create a new repository on the command line***の内容は以下です。

```bash
echo "# QiitaArticle" >> README.md
git init
git add README.md
git commit -m "first commit"
git branch -M main
git remote add origin https://github.com/ktguy0723/QiitaArticle.git
git push -u origin main
```

# 2. コマンドラインの説明

## 2-1. `echo "# QiitaArticle" >> README.md` 
`echo`（エコー）はコマンドラインに表示するコマンドです。
`>>`と組み合わせることで「ファイルに追記する」ことができます。

```bash:ファイルに追記する
$ echo 文字列 >> ファイル名
```
とすることで、文字列をファイル名で指定したファイルに出力できます。
よって `echo "# QiitaArticle" >> README.md`は**「# QiitaArticle」をREADME.mdに出力する。**という意味になります。

ちなみに、README.mdはプロジェクトの説明など記述するプロジェクトの説明書です。
GitHubでリポジトリを開くとソースの下に記載されているあれですね。

## 2-2. `git init`
**現在のディレクトリの直下にローカルリポジトリを新規に作成する**コマンドです。
また、以下のようにディレクトリを指定することで、**指定したディレクトリにリポジトリを作成**することもできます。

```bash:指定したディレクトリにリポジトリを作成する
$ git init ディレクトリ名
````

## 2-3. `git add README.md`
**ファイルをインデックスに追加する**コマンドです。
インデックスに追加することでGitで管理する対象のファイルになります。

以下のようにすると、**現在のディレクトリ直下のすべてをインデックスに追加**します。

```bash:現在のディレクトリ直下のすべてをインデックスに追加
$ git add .
```

また、以下のコマンドで変更部分の確認や、差分の確認ができます。

```bash:変更部分の確認
$ git status
```

```bash:インデックスに登録されたファイル情報と現在ファイルの差分を表示
$ git diff
```

## 2-4. `git commit -m "first commit"`
コミットします。ファイルの変更をgitレポジトリに保存する役割があります。
`-m`コマンドをつけることでコミットメッセージを残すことができます。
よって`git commit -m "first commit"`は**"first commit"というコメントをつけてコミットする**という意味になります。

## 2-5. `git branch -M main`
**現在のブランチをmainに変更します。**
GitHubは[デフォルトブランチをmasterからmainに変更した](https://www.publickey1.jp/blog/20/githubmainmastermain.html)ので、上記コマンドで変更するようにこのコマンドがあるようです。
ちなみに、ブランチとは独立した開発ラインを意味します。

上記コマンドを実行する前に下記コマンドでブランチの一覧を確認すると**master**ブランチになっていることが確認できます。

```bash:ブランチの一覧を表示
$ git branch
```

## 2-6. `git remote add origin https://github.com/ktguy0723/QiitaArticle.git`
***現在のローカルリポジトリにリモートのリポジトリを追加***するコマンドです。

```bash:現在のローカルリポジトリにリモートのリポジトリを追加
$ git remote add [リポジトリ名] [URL]
```
リポジトリ名は一般的にoriginが使用されます。


## 2-7. `git push -u origin main`
**mainをoriginへプッシュ**します。

-uオプションについては下記参照
https://qiita.com/shumpeism/items/1b8027c8905ca826416d

# 3. おわりに
最後まで読んでいただきありがとうございました。

# 4. 参考記事
https://www.atlassian.com/ja/git/tutorials/using-branches

https://gist.github.com/kurotaky/5483492
【Mac】Macのアプリケーションを削除する

<< [【Mac】MacOSの基本操作を覚える](https://qiita.com/hiroyuki-eguchi-0731/items/99a0d771fda18adac9f0)

# Macのアプリケーションを削除する

アプリケーションをインストールしたものの削除したいなぁ、、、と思い始めて、はたと気づく。
やり方を把握していない、と。w

こういう時は公式サイトを見るに限りますね。

■ Mac で App を削除する方法 - Apple サポート
[https://support.apple.com/ja-jp/HT202235](https://support.apple.com/ja-jp/HT202235)

https://support.apple.com/ja-jp/HT202235

なるほど、Launchpad か Finder のどちらから削除か。

Launchpad は iPad で慣れているイメージだから簡単な気がするけど、なんとなく Windows　みたいにアプリケーションの削除をしてみたいので Finder　を使ってみた。

「ゴミ箱に入れる」という行為が妙に引っかかる表現だけど、アプリケーションの削除みたいだ。

無事に削除できた。

---

2021-05-02 初稿
jQuery基礎メモその2

#jQueryの準備
jQueryを使用するには、jQueryライブラリを読み込む必要がある。
jQueryの読込
>```html:index.html
<head>
  <script src="https://...jQuery.min.js"></script>
</head>
```

jQueryファイルの読込
<head>タグの中に書くことはできるが、</body>の直前に書くことで、WEBページの表示速度をより早めることができる。
>```html:index.html
<head> 
  ・・・
</head>
>
<body>
  ・・・
  <script src="script.js"></script>
</body>
```

bodyの終了タグの直前で読み込む。

jQueryの型
基本的な型
>```javascript:script.js
$(document).ready(function() {
  // この中にjQueryを書く
});
```

readyイベントの省略
>```javascript:script.js
$(function() {
  // この中にjQueryを書く
});
```

#モーダルを表示
①モーダルをcssで非表示にする。
>```css:stylesheet.css
.login-modal-wrapper {
  display: none;
}
```

②ログインボタンにクリックイベントを設定
③clickイベントでモーダルを表示
>```javascript
$('#login-show').click(function() {
  $('#login-modal').fadeIn();
});
```

#モーダルを隠す
閉じるボタンをクリックした時に、モーダルが閉じるようにする。
>```javascript
$('.close-modal').click(function() {
  $('#login-modal').fadeOut();
});
```

#addClass・removeClass
addCrassメソッドを用いるとクラスを追加することができ、removeClassメソッドは反対にクラスを取り除くことができる。
addClass
>```javascript
$('.text-contents').addCrass('text-active');
                          // クラス名の前にドットは不要
```

removeClass
>```javascript
$('.text-contents').removeCrass('text-active');
                          // クラス名の前にドットは不要
```

#this find 復習
thisに.マウスが乗っているlesson-hoverクラスが入ってfindでその中の.text-contentsを取得している。
>```javascript
$('.lesson-hover').hover(
    function() {
      $(this).find('.text-contents').addClass('text-active');
    },
    function() {
      $(this).find('.text-contents').removeClass('text-active');
    }
  );
```

#hasClassメソッド
hasClassメソッドは、引数に指定したクラスを、オブジェクトが持っているか判定する時に使用する。
オブジェクトがそのクラスを持っていればtrue、持っていなければfalseを返す。
>```html:index.html
<div class="answer">・・・</div>
>
<div class="answer open">・・・</div>
```

>```javascript:script.js
$('.answer').hasClass('open');
// 結果: false
>
$('.answer open').hasClass('open');
// 結果: true
```

hasClassを用いたアコーディオン
>```javascript:script.js
if ($('.answer').hasClass('open')) {
  // openクラスを外す処理
  // 答えを隠す処理
} else {
  // openクラスをつける処理
  // 答えを表示する処理
```

>```javascript:script.js
$('.faq-list').click(function() {
  var $answer = $(this).find('.answer');
  if ($('.answer').hasClass('open')) {
    // answerにopenがある場合
    $answer.slideUp();
    $answer.removeClass('open')
  } else {
    // そうでない場合
    $answer.slideDown();
    $answer.addClass('open');
  }
});
沈思黙考：Optional(java)

Optional型を使うといわゆるヌルポから解放されるといいますが、どのような場合に使うとよいでしょうか？

参照先の値がnullになる可能性がある、あるいは、nullにならないことがわかっていないケースで、参照先の値をOptional経由で取り出すと、参照先がnullでない場合にだけ値を取り出せるので、Optionalのありがたみがわかります。

たとえば、変数userにユーザー情報が入っていて、user.getCompany()でユーザーの会社情報が取得でき、user.getCompany().getCompanyName()で会社名が取得できるとします。
このとき、user,user.getCompany(),user.getCompany().getCompanyName()のいずれの値もnullになる可能性がある場合に、所属する会社名を出力する処理を、Optionalを使って以下のように書くと、userがnullの場合や、会社が未登録、あるいは、会社名が未登録の場合は、"不明"と表示されます。

```java:sample01
System.out.printf("所属：%s\n",Optional.ofNullable(user)
		.map(u->u.getCompany())
		.map(c->c.getCompanyName())
		.orElse("不明"));
```
慣れてきたらメソッド参照を使って以下のように書くようになるでしょう。

```java:sample01-1
System.out.printf("所属：%s\n",Optional.ofNullable(user)
		.map(User::getCompany)
		.map(Company::getCompanyName)
		.orElse("不明"));
```


もしこれをifをつかって同じことをやると、ここまですっきり書くのは難しいでしょう。3項演算子を使っても冗長になってわかりにくいものになるのも想像できるでしょう。

上記のように値の取り出しを連鎖的に行う場合に、null判定なしに記述できるのが、Optionalの便利な点です。

値の取り出しだけでなく、値の変換を伴う場合もOptionalは便利です。
user.getUserType()でユーザーの社員区分が数値として取り出せて、Map\<Integer,String> typeNameMapを参照して区分名称を得るという場合をみてみましょう。getUserType()がnullを返す可能性と、区分がtypeNameMapに存在しない場合があると想定します。typeNameMapに該当する区分があればその区分名を、なければ"区分なし"と表示します。

```java:sample02
System.out.printf("社員区分：%s\n",Optional.ofNullable(user)
		.map(u->u.getUserType())
		.map(t->typeNameMap.get(t))
		.orElse("区分なし"));
```

このように処理の結果の値を使って次の値を得るような場合も便利です。

判定を行う場合の例を見てみましょう。
userに趣味を示すgetHobby()があり、これをみて読書が好きかどうか判定する場合を考えます。
次の処理は、getHobby()が"読書"を返す場合はtrueを返し、そうでない場合はfalseを返し、userがnullあるいは、getHobby()がnullの場合はfalseを返します。

```java:sample03
System.out.printf("趣味が読書：%b\n",Optional.ofNullable(user)
		.map(u->u.getHobby())
		.map(h->h.equals("読書"))
		.orElse(false));
```

続いて、趣味がある場合にだけ、読書好きかどうかのフラグを設定する場合を考えましょう。
これもよくある例なのですが、userがnullやgetHobby()がnullの場合はセットをしない点が良い点です。sample03では、userがnullやgetHobby()がnullの場合は、falseが出力されましたので、この違いを確認してください。

```java:sample04
Optional.ofNullable(user)
		.map(u -> u.getHobby())
		.map(h -> h.equals("読書"))
		.ifPresent(f -> user.setReading(f));
```
これはメソッド参照を使うと以下のように書けます。

```java:sample04-1
Optional.ofNullable(user)
		.map(User::getHobby)
		.map("読書"::equals)
		.ifPresent(user::setReading);
```

さて、ここまで見てきてOptionalは便利だと感じてもらったと思いますが、なんとなくすっきりしない点があるのを感じませんか？

1つ目は、userやgetCompany()などのどこでnullになったのか、わからない点です。
どの値がnullになることを許容されるのかというのは、設計上重要な問題で、これを明確にしないで実装を行うと、いたるところでヌルポが発生するのは自明です。Optionalを使うことでヌルポがなくなったとしても、設計上の問題を解決したことにはなりません。
Optionalを使うときは、ヌルポを握りつぶしてよいのかどうかを、考えてるとよいでしょう。

2つ目は、orElse()に記載した値です。この値を何にするかを考えることは、重要な設計の一つだと思います。値がnullを返した時にどういうふるまいをすべきか、よく考えるべきです。
適切な値を返すだけでなく、値を返さずに、例外を投げることが必要な場合もあるかと思います。

このようにみると、Optionalを使うことで設計の不備やドキュメントの不備が見えてきます。Optionalは、必要なところで使うと便利であるが、必要以上に使うとバグの発見を先送りすることにつながります。

Optionalを使うところでは、設計を確認するチャンスかもしれません。
要素の種類（ブロックレベル要素、インライン要素）を変更する方法

##### 要素を横並びにする方法の1つは、横並びにしたい要素の親要素にdisplay: flex;を指定します。
```
.parent{
 display: flex;
}
```
上記の記述で子要素たちは横並びになります。

また、子要素たちを中央に配置したり等間隔に並べたりしたい場合は、FlexBoxを用いて配置を調整することもできます。

######主軸方向（水平方向）の調整には、justify-contentプロパティを使用します。

```
.parent{
 display: flex;
justify-content: space-around;
}
```

上記のように記述すれば横に並んだ子要素たちが水平方向に等間隔に並びます。

他にも、

左寄せ↓

```
justify-content: flex-start;
```


右寄せ↓

```
justify-content: flex-end;
```


中央寄せ↓

```
justify-content: center;
```

などがあります。

水平方向の調整はこのような感じで行います。

###### 続いては、垂直方向の調整方法について。

交差方向（垂直方向）の要素の調整にはalign-itemsプロパティを用います。

```
.parent{
 display: flex;
justify-content: space-around;
align-items: center;
}
```

上記の記述で、子要素たちは垂直方向の中央に配置されました。


他には、

上揃え↓

```
align-items: flex-start;
```


下揃え↓

```
align-items: flex-end;
```

となります。
useEffect 第二引数の使い分け

#投票アプリ作成中、よくわからない警告に遭遇(Part2)
投票アプリ以外でも、時々、今回の警告は見たことがあった。。。

######React Hook useEffect has a missing dependency: 'email'. Either include it or remove the dependency array  react-hooks/exhaustive-deps

######（日本語）React Hook useEffectに依存関係がありません： 'email'。それを含めるか、依存関係配列react-hooks / exhaustive-depsを削除します

###大前提として、useEffectについて
第一引数には、副作用として実行する関数を与える。
useEffectに指定することで、副作用は、コンポーネントの描写が終わった後に実行される。

第二引数によって、副作用を実行する頻度を設定できる。
第二引数を与えなかった場合、コンポーネントの描写後、毎回実行。
🔴第二引数に空の配列を与えた場合には、初回描写時にのみ実行。
🔵第二引数に配列を与え、要素として変数を指定すると、指定した変数に変更があった場合のみ実行。

###警告が出る理由
第２引数を指定していないから。

今回、以下のコードのように、
######🔴第二引数に空の配列を与えた場合には、初回描写時にのみ実行。
としていた。

```javascript
  // ユーザー情報を取得
  useEffect(() => {
    firebase
      .firestore()
      .collection("users")
      .where("email", "==", email)
      .onSnapshot((snapshot) => {
        const getUsers = snapshot.docs.map((doc) => {
          return {
            ...doc.data(),
            docid: doc.id,
          };
        });
        setUsers(getUsers);
      });
  }, []);
```

###対処法
以下のコードのように、
######🔵第二引数に配列を与え、要素として変数を指定すると、指定した変数に変更があった場合のみ実行。
とすることで、警告は消えた。

```javascript
}, [email]);
```

#その他の警告と対処
①
######React Hook useEffect has missing dependencies: 'history.location.state.birthday', 'history.location.state.email', 'history.location.state.gender', 'history.location.state.password', and 'history.location.state.username'. Either include them or remove the dependency array  react-hooks/exhaustive-deps

（対処）
こちらも 🔴第二引数を空の配列 としていたため、
以下のように、 🔵変数を指定した ことで、警告は消えた。

```javascript
  }, [history]);
```

②
######React Hook useEffect has missing dependencies: 'email', 'question.answer1Id', 'question.answer2Id', and 'question.docid'. Either include them or remove the dependency array  react-hooks/exhaustive-deps

（対処）
こちらも 🔴第二引数を空の配列 としていたため、
以下のように、 🔵変数を指定した ことで、警告は消えた。

```javascript
  }, [email, question]);
```

#今後は、、、
第二引数が、
⭐空の配列が適している場合、変数を指定する場合⭐
これらを適切に使い分けれるように、まだまだ勉強が必要だ。。。
Django 勉強中に Pylint を入れたら滅茶苦茶エラー出た話


Djangoチュートリアル学習中にPylintを入れたところ、よくわからんエラーが大量に出たお話

環境 VSCode

やったこと
・Djangoチュートリアル
・pip install pylint-django
・VSCodeのsettings.jsonに
```"python.linting.pylintArgs": [
    "--load-plugins=pylint_django",
]
```
　を追加。

起きた事
・missing module docstring （Docstring 書いてね～）

やった事
・VSCodeのSettingsに以下を追加
``` "python.linting.pylintArgs": [
        [...]
        "--disable=django-not-configured",
        "--django-settings-module=<mainapp>.settings",
    ]
```

おわりに

チュートリアルでも、ちゃんとDocstringを書こう！

```
# これはコメント 1処理ごとに説明を書くと逆にゴチャゴチャするよ
```
```
””” これはDocstring 関数などの説明のために書かれるドキュメント
　　　クラスや関数の再利用性や読みやすさ向上のためになるべく書いてみよう
”””
```

` 👈　これどうやって出すんだよ
NVIDIA DCH -> Standard ドライバ(windows)をインストールするメモ

Nicehash 動かすと１日くらいすると頻繁にプチフリするようになった...
再起動すると治る.



https://github.com/nicehash/NiceHashQuickMiner/issues/292

それ, DCH ドライバのメモリリークが原因の可能性あります!
nvidia-smi -l 走らせてリークしているか確認してね.

Windows で NVML API を使うようなアプリ(機械学習, レイトレなど)を回すときも注意必要そうです!

https://nvidia.custhelp.com/app/answers/detail/a_id/4777/~/nvidia-dch%2Fstandard-display-drivers-for-windows-10-faq

長期的には DCH が推奨なのでしょうが, とりあえずは Standard 版にして回避します.

## DCH -> Standard に変える

検索すると DCH/Standard 選べるみたいな記事ありますが, 最近(2021/05/02)だと DCH がデフォになってしまったようです.

普通だと Standard 版は出てこないです.
Advanced search で探しましょう.

https://www.nvidia.com/Download/Find.aspx

ドライバインストール時は Custom から clean インストール選ぶと良さそうです!

## その他

プチフリは Windows Insider preview(dev を選んでしまったので製品版にもうもどせない) + 3070 + DCH で発生しました.

リークしているようだが DCH + Titan V + 製品版 Windows 10 ではあまりプチフリしない.

Insider Preview との組み合わせだとよりプチフリがひどくなるのかも.
React.jsとNext.jsについて

## React.jsとNext.jsについて勉強しているので、その情報を共有するために投稿します。

solidityとスマートコントラクトを勉強していたが、React.jsと組み合わせてアプリケーションを作成している例をたくさん見かけたこともあり、
React.jsもこの際理解しようということにしました。

違いは下記の通り。

### React.js： SPAを考えて作成されている。HTMLをJavaScriptの中に記述できるJSX機能が強力！

### Next.js：React.jsに各種ライブラリを統合してパッケージ化したもの。React.jsを拡張させることができる！

ソースコードは、下記GitHubで公開中

<a href="https://github.com/mashharuki/react_app">react_app</a>
<a href="https://github.com/mashharuki/next_app">next_app</a>

どちらも npx コマンドを利用することで土台部分を自動的に作成してくれるため、すぐに開発に入ることができるという強みを持っている！

要素をコンポーネント化することで、複雑な画面でもプラモデルもパーツを組み立てる感覚で開発できるところが面白いし、扱いやすいと感じた。
エンタープライズ向けのWebページでもおそらく課題になるであろう画面レイアウトの統一とも相性がものすごく良いとも考えている。

ただ、従来のHTMLやJSPと比べてかなり書き方が変わるため、いきなり導入するということはかなりハードルが高そうだが、
かなり柔軟に開発できるためメリットの方が大きいのではないかと考えている。

要素をコンポーネント化することで、複雑な画面でもプラモデルのパーツを組み立てる感覚で開発できるところが面白いし、扱いやすいと感じた。
エンタープライズ向けのWebページでもおそらく課題になるであろう画面レイアウトの統一とも相性がものすごく良いとも考えている。

ただ、従来のHTMLやJSPと比べてかなり書き方が変わるため、いきなり導入するということはハードルが高そうだが、柔軟に開発できるため導入するメリットの方が大きいのではないかと考えている。(特にNext.jsについては、HTMLファイルが無くなり、全てJavaScriptでの記述となるため従来のやり方に慣れている方からには、少なからず抵抗があるかもしれません。。。)

※現在、勉強中のため、適宜追記していきたいと考えています。

以下、参考にした書籍となります。

<a href="https://www.amazon.co.jp/Solidity%E3%81%A8Ethereum%E3%81%AB%E3%82%88%E3%82%8B%E5%AE%9F%E8%B7%B5%E3%82%B9%E3%83%9E%E3%83%BC%E3%83%88%E3%82%B3%E3%83%B3%E3%83%88%E3%83%A9%E3%82%AF%E3%83%88%E9%96%8B%E7%99%BA-%E2%80%95Truffle-Suite%E3%82%92%E7%94%A8%E3%81%84%E3%81%9F%E9%96%8B%E7%99%BA%E3%81%AE%E5%9F%BA%E7%A4%8E%E3%81%8B%E3%82%89%E3%83%87%E3%83%97%E3%83%AD%E3%82%A4%E3%81%BE%E3%81%A7-Kevin-Solorio/dp/4873119340/ref=sr_1_1?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&amp;dchild=1&amp;keywords=%E5%AE%9F%E8%B7%B5%E3%82%B9%E3%83%9E%E3%83%BC%E3%83%88%E3%82%B3%E3%83%B3%E3%83%88%E3%83%A9%E3%82%AF%E3%83%88&amp;qid=1620045258&amp;s=books&amp;sr=1-1">実践スマートコントラクト開発</a> 

<a href="https://www.amazon.co.jp/Node-js%E8%B6%85%E5%85%A5%E9%96%80-%E7%AC%AC3%E7%89%88-%E6%8E%8C%E7%94%B0%E6%B4%A5%E8%80%B6%E4%B9%83/dp/479806243X/ref=sr_1_1?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&amp;crid=18Z0LT4DYU5K8&amp;dchild=1&amp;keywords=node.js%E8%B6%85%E5%85%A5%E9%96%80&amp;qid=1620045582&amp;s=books&amp;sprefix=node.js%2Cstripbooks%2C256&amp;sr=1-1">node.js超入門</a> 

<a href="https://www.amazon.co.jp/React-js%EF%BC%86Next-js%E8%B6%85%E5%85%A5%E9%96%80-%E7%AC%AC2%E7%89%88-%E6%8E%8C%E7%94%B0%E6%B4%A5%E8%80%B6%E4%B9%83-ebook/dp/B08XBNGYVH/ref=sr_1_1_sspa?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&amp;crid=1IUZ4MBQJ88A8&amp;dchild=1&amp;keywords=react&amp;qid=1620045617&amp;s=books&amp;sprefix=re%2Cstripbooks%2C279&amp;sr=1-1-spons&amp;psc=1&amp;spLa=ZW5jcnlwdGVkUXVhbGlmaWVyPUExRkwySUQySEFPMzVTJmVuY3J5cHRlZElkPUEwNzMwOTQ3NDA2Mlg4N0ZLQlpWJmVuY3J5cHRlZEFkSWQ9QTJXVkYzVzBSNENLMkUmd2lkZ2V0TmFtZT1zcF9hdGYmYWN0aW9uPWNsaWNrUmVkaXJlY3QmZG9Ob3RMb2dDbGljaz10cnVl">React.js&amp;Next.js超入門</a>
[Rails] Rspecでテストコードの実装②

#はじめに
テストコードの実装①をみていない方は[こちら](https://qiita.com/tochisuke221/items/b4c300b29fc6c85b2d9b)からご覧ください。

# 基本的なテストコードの書き方

まずは、どのような形でテストコードを書いていくのか確認です。
Userモデルにはnameカラムとemailカラムがあるとします。

```spec/models/user_spec.rb
require 'rails_helper'

RSpec.describe User, type: :model do
  describe 'ユーザー新規登録' do
   context '登録ができるとき' do
　　　it '全ての項目が正しく入力されている時' do
     　#その処理を記述
　　　end
　　end
   context '登録ができないとき' do
　　　it 'nameが空では登録できない' do
      # nameが空では登録できないテストコードを記述します
    　end
    　it 'emailが空では登録できない' do
      # emailが空では登録できないテストコードを記述します
    　end　
　　end
  end
end
```

ひとつづつ確認しましょう。

###describe
describeとは、テストコードのグループ分けを行うメソッドです。
「どの機能に対してのテストを行うか」をdescribeでグループ分けします。
describeにつづくdo~endの間に、さらにdescribeメソッドを記述することで、入れ子構造をとることもできます。

###context
特定の条件を分けたい場合に用います。contextはどのような「状況」を確認したいのかを書きます。

###it
describeメソッド同様に、グループ分けを行うメソッドです。
itの場合はより詳細に、「describeメソッドに記述した機能において、どのような状況のテストを行うか」を明記します。contextよりもさらに詳細なものを記述します。

###example
exampleとは、itで分けたグループのことを指します。

以上のような形で、どの機能のどんな状況のときにテストコードが実行されるのか、わかりやすい形でグループ分けしてやります。

#テストコードの具体的な実装(正常系)

それではグループ分けしたのちに、処理を実際に記述していきます。

```spec/models/user_spec.rb
require 'rails_helper'

RSpec.describe User, type: :model do
  describe 'ユーザー新規登録' do
   before do
    @user=User.new(name:"tochi",email:"abc@abc")
   end
　　context '登録ができるとき' do
　　　it '全ての項目が正しく入力されている時' do
     　expect(@user).to be_valid
　　　end
　　end
  #中略
  end
end
```
こちらも1つづ解説します。

### before do~end
これはコントローラーでいうbefore_actionと同様で、予め処理を走らす前に行いたい処理を記述します。今回は@userというインスタンスを作成しておきます。

###expect
検証で得られた挙動が想定通りなのかを確認する構文のことです。
expect().to matcher()という基本的な形をもとにに、テストの内容に応じて引数やmatcherを随時変えて記述します。のちに出てくるinclude等がこれにあたります。

######matcher(参考)
matcherは、「expectの引数」と「想定した挙動」が一致しているかどうかを判断します。
###be_valid
インスタンスが正しく保存されるかどうか判断します

以上を踏まえると、以下の一文は、@userがちゃんとDBに保存されたかどうかを確認します。

```ruby
expect(@user).to be_valid
```

#テストコードの具体的な実装(異常系)
だんだん慣れてきたところで、異常系についてもさらっと触れておきましょう。


```spec/models/user_spec.rb
require 'rails_helper'

RSpec.describe User, type: :model do
  describe 'ユーザー新規登録' do
   #省略
   context '登録ができないとき' do
　　　it 'nameが空では登録できない' do
      @user.name=""
      @uer.valid?
      expect(@user.errors.full_messages).to include("名前を入力してください")
    　end
    　it 'emailが空では登録できない' do
      # 自分で考えてみよう！
    　end　
　　end
  end
end
```

新しく出てきたものがあるので解説します。


###valid?
valid?は、バリデーションを実行させて、エラーがあるかどうかを判断するメソッドです。
エラーがない場合はtrueを、ある場合はfalseを返します。

これを実施しないと、errorsが発生しないため、エラー文を引き出すことができません。
###errors
errorsは、インスタンスにエラーを示す情報がある場合、その内容を返すメソッド
###full_messages
full_messagesは、エラーの内容から、エラーメッセージを配列として取り出すメソッド。だいたい、errorsとセットで使います。

###include
includeは、「expectの引数」に「includeの引数」が含まれていることを確認するマッチャです。

以上より、以下の処理は、nameカラムが空だったとき、エラーがあるかどうか確認して、発生したエラー文に「名前を入力してください」と出るかどうかを確認しているということになります。

```ruby
    @user.name=""
    @uer.valid?
    expect(@user.errors.full_messages).to include("名前を入力してください")
```
ちなみに、エラー文は日本語になっていないことがあるかと思いますので、どんなエラー文が出るかはbinding.pryで止めて確認してください。

#終わりに
お疲れ様でした。今回、簡単にモデルの単体テストについてまとめてみました。いかがでしたか。
やはり、実際に手を動かしてやってみることが一番いいと思いますので、是非やってみてください

つづき！！！↓
[Rspecでテストコードの実装③](https://qiita.com/tochisuke221/items/1825806dc16c9bdf7716)
Vuexについてざっくりまとめてみた

#Vuexとは？
- JavascriptのフレームワークであるVue.jsを使ったアプリケーションで用いられる状態管理ライブラリ。

- つまり、Vueのプロジェクト内で使われているデータをより扱いやすくするためのの管理システム。

#なんのためにある？
- アプリケーション内にあるデータのやりとりの利便性や可読性を上げるため。

![スクリーンショット 2021-03-11 21.48.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/997177/4091b05d-3331-0d8d-4cad-37408be508a1.png)

- アプリケーション全体で使用するデータをコンポーネント毎ではなく、一つの場所でまとめて管理するという考え方。

- Vuexを使用せずに、図内左端のコンポーネントにあるデータを右端のコンポーネントで使用する場合、propsや$emitが繰り返され、コードの可読性が低下する（データが追いにくくなる）。

#Vuex内でのデータの動き
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/997177/c58e90f1-fcf7-6b90-3183-b7b6af0022e6.png)
Thymeleaf x Spring boot x IntelliJ で spring.thymeleaf.cache=false が効かない場合の対処法

やっと解決したぜ..
公式ドキュメントでフォローしてほしい気持ち。

## 結論
```application.properties
spring.thymeleaf.prefix=file:src/main/resources/templates/ ## コレ！！！
spring.thymeleaf.cache=false ## spring-boot-devtools を使っていれば不要
```

## バージョン

- org.springframework.boot: 2.4.5