OmniAICreator/Qiita-1.07M huggingface dataset:
GASでWebアプリ｢映画鑑賞記録｣を作る⑮ ／ 新年（2022年）に対応する

# 今回やること
　2022年になりましたので、鑑賞履歴を新しくします。

# 今年用の「映画鑑賞記録」を作成
　新しい年になったら、新しい「映画鑑賞記録」（スプレッドシート）を準備します。
　　① 2021年の「映画鑑賞記録」をコピーして、新しいスプレッドシートファイルを
　　　作成します。ファイル名は任意で構いません。
　　② ①で作成した「映画鑑賞記録」の［鑑賞履歴］シートのデータをクリアします。 
![［鑑賞履歴］シート.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/510221/46024f09-bee6-4237-a022-5d30a85f1eaf.png)
　プログラムの設定を、2022年用に追加・修正します。
### Config.gs
　`YearSettings`に2022年の設定を追加します。（下図：1番上の行）
　`fileId`には、前述で作成したスプレッド－シートのファイルIDを記載します。（`'zzzzz…zz'`部分）
　　※**ファイルID** は、スプレッドシートのURLの`…/d/`と`/edit…`の間の文字列です。（上図：黄色の部分）

```javascript
 const YearSettings = [
  {id: '2022', year: '2022年', fileId: 'zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz'},  // ← この行を追加
  {id: '2021', year: '2021年', fileId: 'yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy'},
  {id: '2020', year: '2020年', fileId: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'}
]
```
### Vuejs.html
　`selectedYear`の値を`'2022年'`に変更します。画面の初期表示時に、この値（年）が使用されます。
　プログラムの処理としては、**Config.gs** の`YearSettings`の`year`とマッチングされます。

```javascript
var app = new Vue({
  el: '#app',
  data: {
    processingType: '記録追加',
    message: 'テスト',
    subject: '　追　加　',
　　　：
    viewingTypes : [],
    selectedYear: '2022年',  // ← この値を変更する
    optionYear: []
  },
　　：
```

　本当は、上記の作業だけで対応できる筈だったのですが、［鑑賞履歴］シートが空だと次のエラーが発生する事が分かりました。
![エラー表示.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/510221/04435b40-4213-f6f9-57ea-998b986f55de.png)
　この不具合に対応する為、下記のプログラム修正を行いました。

# プログラムの修正
### ViewingRecord.gs
　`getReportListBy()`関数を修正します。
　　①`results`を空の配列で初期化する処理を追加します。
　　②`results`にデータをセットする処理を、`lastRow`が`3`以上の時に実行する様に
　　　変更します。
　　　（［鑑賞履歴］シートにデータがある時のみ、`results`にデータをセットする。）

```javascript
function getReportListBy(cond) {
  cond.type = 'viewingRecord';
  Logger.log(cond);

  var fileId = getFileIdFromYearSettings(cond.year);
  var sheet = SpreadsheetApp.openById(fileId).getSheetByName('鑑賞履歴');
  var lastRow = sheet.getLastRow();
  var results = [];   // ← この行を追加する
  if(lastRow >= 3) {  // ← この行を追加する
    results = sheet.getRange(3, 2, (lastRow - 2), 6).getValues();  // ← var を削除
  }                   // ← この行を追加する

  return JSON.stringify(results);
}
```

```:以前のコード
function getReportListBy(cond) {
  cond.type = "viewingRecord";
  Logger.log(cond);

  var sheet = SpreadsheetApp.openById(ViewingRecordID).getSheetByName('鑑賞履歴');
  var lastRow = sheet.getLastRow();
  var results = sheet.getRange(3, 2, (lastRow - 2), 6).getValues();

  return JSON.stringify(results);
}
```

# 結果
　エラーが表示されなくなりました。
![結果.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/510221/3df95d6f-dc7f-bcee-0f7d-84a2e091eb1c.png)
**◆前の記事** [GASでWebアプリ｢映画鑑賞記録｣を作る⑭](https://qiita.com/pump33/items/e90659484900a641bd8e)
**◆次の記事** [GASでWebアプリ｢映画鑑賞記録｣を作る⑯](https://qiita.com/pump33/items/44acae506eb990b136a1)

**◆索引** [GASでWebアプリ｢映画鑑賞記録｣を作る《索引》](https://qiita.com/pump33/items/8ded0e6fd3c336e705f4)
Javascript46_object指向

オブジェクト：
値（変数、定数）と関数が含まれているものです。
変数と定数はプロパティと呼びます。
関数はメソッドと呼びます。

オブジェクトの分類：
1　既存の対象
ESの中に定義された対象
例：Math String Number Boolean Function Object

2 運用環境に提供された対象
例：BOM DOM

3 ユーザ定義対象
自分で作る対象。
Laravel MAMP DBに接続設定

php artisan　を実行しても

  SQLSTATE[HY000] [2002] No such file or directory (SQL: select * from information_schema.tables where table_schema = laravel_db and table_name = migrations and table_type = 'BASE TABLE')


上記のエラーが返されて、接続にうまくいかないケース。


①MAMP websiteで接続パラメータの確認をします。
ページの下の方に「MySQL」と書かれたタブをクリックして開くと、パラメータ情報が記載されています。
![スクリーンショット 2022-02-16 20.45.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/677104/62f8cf0d-ed3a-3f50-e153-483005492903.png)


②.envとdatabase.phpの編集
.envファイルを開いて、１１行目から必要な情報を入力します。

(例）
DB_CONNECTION=mysql
DB_HOST=localhost
DB_PORT=8889
DB_DATABASE=sample_db
DB_USERNAME=root
DB_PASSWORD=password

![スクリーンショット 2022-02-16 20.48.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/677104/3d66357b-9851-9051-d809-57c186ddd6c1.png)


database.phpはconfig　配下にあります。（４６行目にmysqlの接続情報記載されていますので、.envファイルと同様に記述します）


③arisanコマンド実行

　まず　Laravelさんはキャッシュクリアしないと変更されたプログラムが正常に更新されないケースがあります。

php artisan config:clear

を実行してから、

php artisan migrate

を実行します。

Migration table created successfully.と文言が出たらDBを確認して作成されていることを確認してみればテーブルが作成されていると思います。
【Mac】MaptureからFuwariへ乗り換えた話【スクリーンショット】

# はじめに
スクリーンショット、便利ですよね。

チートシートを見ながら作業したり、いわゆる写経にも使えます。

この度、スクリーンショットのアプリを「Mapture」から「**Fuwari**」へと乗り換えました。

実際に使ってみた感想などをシェアいたします。


## 結論
**Fuwari、最高です** :point_up_2::point_up_2::point_up_2:

Maptureの細かなストレスが見事に取り払われ、非常に使い勝手の良いアプリだと感じました。

Fuwariの詳細はこちらをご参照ください。
https://qiita.com/kentya6/items/00599091bd987b5fa797

https://github.com/sskmy1024y/Fuwari

　

## Maptureより優れている点は？

FuwariがMapture（Macでスクリーンショットを撮るアプリ）より優れていると感じた点は以下の通りです。

- Fuwariは「⌘ + w」でウインドウを閉じられる
- Fuwariはスクリーンショット撮影時に無音

逆にいえば、以上２点についてMaptureに不満を持っていました。

そこを見事に解決してくれたFuwari。

ありがとうございます。

**最高です**:point_up_2::point_up_2::point_up_2:

　

## スクリーンショットアプリの何が便利なのか？

最後に、（Fuwariに限らず）スクリーンショットアプリを使用すると何が便利なのか記述します。

私は以下の点に魅力を感じています。

- **常に　最前面に**　スクリーンショットを表示できる

どういうことか、詳しくみていきましょう。

下記の画像をご覧ください。

![05eb205bb007de4b055e1dd3db0ff478.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/238817/86f442df-0704-fafe-4042-4cf20aa3323d.gif)

**これが便利なんです！**

参考にしたい箇所を切り取って、画面の最前面に置いておけるのです。

Macの標準機能でもスクリーンショットは撮影できますが、最前面には置いておけません。

これに慣れると、作業効率が効率がグンとあがります。


　

# おわりに

Fuwari、素晴らしいです。

ネーミングもいいですし、つい使いたくなるアプリですね。

みなさまも是非お試しください。

一度使ってみると、その便利さに気付くでしょう。

**最高です**:point_up_2::point_up_2::point_up_2:

それでは。
javascript09 Nullとundefined

Nullの値は１つしかない、nullです。
nullとは存在しない意味です。

var a = null;
console.log(typeof a);
返された結果はObject.

undefinedの値はundefinedしかない。
変数を宣言したけど、値を代入してない場合、値はundefinedとなります。

var b = undefined;
console.log(typeof b);
javascript10_データの型変換String


方法１：toString()を使用します

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1067004/1fe05d22-ba3b-2dc0-b393-86b4bd5b6c38.png)

変数の型を変換したい場合は、toString()関数を使用します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1067004/007e4c12-aac8-037b-e126-d7476347d46e.png)

注意：
nullとundefinedはtoString()に適用しない

方法２：String（）関数を使用します。
nullとundefinedに対しては、直接文字列に変換します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1067004/9238a0d6-8cd6-35ac-9b53-18ea4f3adee0.png)
Javascript11_データ型の型変換number

値を数字に変換したい場合、下記の方法があります。

方法１：　Number（）関数を使います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1067004/a36bd2e4-4180-f765-a6d4-3a6bea970b99.png)

注意点：
数字の文字列に関しては、直接数値に変換できます。
数字じゃない内容が含まれた場合は、NaNに変換されます。
値は空きである場合は、０に変換されます。
Booleanが数字に変換する場合、
True : 1
false : 0

nullは数字変換する場合、結果は０です。
undefinedは数字に変換する場合、結果はNanです。

Number関数の限界：
数値しか変換できない。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1067004/6240dd74-c0ae-9682-c37c-efba690a7e13.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1067004/31f09498-9e8d-dc67-27a1-ff311a0b7abd.png)

方法２：
parseInt()  →　文字列を数字に変換する
              数値の部分を取り出し、Numberに変換することができる。

parseFloat() →　文字列を小数に変換する
　　　　　　　　機能はparseInt()と同じです。小数部分まで取り出すことができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1067004/69bc9ac9-a9f0-4345-e32e-7b81c151e9b0.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1067004/7d7f287b-0b3c-2cb7-0a95-9e8e64b741ef.png)

注意：変数の値がBoolean値の場合、変換できません。
javascript12_数値の進数について

jsの中に、16進数を表したい時に、0xから始まる必要があります。

8進数は0から始めます。

2進数は0bから始めます。適用できないブラウザがあります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1067004/9a5d06d0-48d3-e7de-caa0-7a43b69ceda4.png)

上記の例を解決する方法としては、parseInt()のなかに、二番目の引数で進数を指定することができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1067004/006406e4-3f34-5fdb-55fa-f4394455ff5d.png)
javascript13_boolean値に変換

1 数値をboolean型に変換する場合、０とNan以外に、他の結果は全てTrueになります。
2 文字列からboolean型に変換する場合、空白以外に、他の結果は全てTrueになります。
3 nullとundefinedはfalseに変換されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1067004/3d35c66d-3159-cb08-e2c9-fe1bcf606954.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1067004/36305650-64b2-52de-1ed9-105fd9c2abad.png)
javascript14_演算

演算する時に、元の変数の値を変更しません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1067004/5268293d-ce03-d693-2ec5-e2438da36a4e.png)

数値じゃない値を計算する時に、該当数値をNumに変換します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1067004/d8e8557e-c416-4459-247f-046722970bef.png)
下記の書き方の方が頻繁的に使われます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1067004/c12fa6bb-7fd3-88a9-0558-86ed1bd14b11.png)

注意して欲しいケース：
result = 1 + 2 + "3";
console.log("result:" + result)
左から計算するため、結果は33です。

result = "1" + 2 + 3;
console.log("result:" + result)
結果は123です。

**加算以外に、全て数値型の文字列は全てNumberに変換されます。**
result = 100 - "2";
結果は98です。
result = 2 * "8";
結果は16です。
result = 2 * undefined;
結果はNaNです。
result = 2 * null;
結果は0です。

全ての値は- * /演算をする際にNumberに変換されます。
そのため、-0, *1, /1で値をNumberに変換できます。
例：
var d = "12";
d = d - 0;
console.log(typeof d);
javascript15_一元演算

＋：数値に影響を与えません。
ー：数値に影響を与えます。

文字列をNumberに変換する楽な方法：
値の前に＋を書くだけです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1067004/a7d02476-55ce-61b3-8e8c-665ee19b0bd1.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1067004/4a1eecff-b4d2-d176-1977-128f268ff270.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1067004/57351654-a965-a282-0083-9ff315db8a60.png)
JavaScript16_インクリメント

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1067004/0d74ae8b-2be5-68a6-8daf-587924f05e8a.png)
a++は元の値aです。
++aは計算した後の値（a+1)です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1067004/657d686c-1ebc-a060-a371-d6633b2e791f.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1067004/802cb560-83f5-25b8-61f9-e05ec672d087.png)

a--は元の値aです。
--aは計算した後の値(a-1)です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1067004/30079a5d-b212-5a83-bc8f-b6853f711c19.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1067004/eb68d038-3b70-4e64-8577-3edd8fc6db12.png)

練習：
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1067004/6c74e5e9-6293-d156-dc70-e8e05a9019f1.png)
JavaScript17_論理演算子とif文(三項演算子）, for文, switch文

jsは３種類の演算符があります。
&&
- 左側がtrueなら右側を返す
- 左側がfalseなら左側を返す
```ruby
const num = null;
const fee = num && "なんかが設定されました";
console.log(fee);
```
||　
- 左側がfalseなら右側を返す
- 左側がtrueなら左側を返す

```ruby
const num = 100;
const fee = num || "金額未設定です";
console.log(fee);
```


NOT: !

注意：
if文の条件式で２つの値が等しいかを比較する時に、
＝＝の代わりに＝＝＝（型まで一致する）を使用してください。
＝＝使うと予期せぬバグが押し込んでしまうことがあります。

一つの値に2回！！すると、値は変更しません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1067004/f4581aa2-13ac-f56c-8581-23ebaf786319.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1067004/1bbb3d11-28e0-e56a-8945-2703506c78b2.png)
よって、上記の特性を利用すれば、値をBooleanに変換することができる。！！を書けばOKです。
var a = "Hello";
a = !!a;

&&: 二つの値ともTrueの時だけ、Trueが返されます。

｜｜：一つのTrueがあれば、Trueが返されます。
　　　２つの値ともFalseの場合、Falseが返されます。

１つ目の値がtrueの場合、２つ目の値をチェックしません。
下記の例にalertは実行されません。
 true || alert("123"); 

**if文とネスト**
```ruby
const height = 80;

if(height >= 80){
    console.log("値は80以上です");
}else if(height >= 60){
    console.log("値は60以上です")
}else{
    console.log("値は60以下です");
}

// if文の中にif文があることをネストと呼ぶ
if(height > 160){
    if(gender == "女"){
        console.log("女の子で身長160以上です");
    }
}
```

三項演算子
```ruby

const height = 80;
// 否定を書くときに、型まで判定できる!==を書くのが普通
if(height !== 80){
    console.log("値は80ではありません");
}

const name = "田中";
const gender = "男";
// ANDを判定する：　&&
if(name ==="田中" && gender === "男"){
    console.log("1組の田中です");
}
// ORを判定する: ||
if(name ==="田中" || gender === "男"){
    console.log("1組または2組の田中です");
}

// 三項演算子
// 条件? 真 : 偽

const score = 60;

const comment = score >= 60 ? "good":"not good";
console.log(comment);
```
三項演算子を使った関数例：
```ruby
const checkSum = (num1, num2) => {
    return num1 + num2 > 100 ? "100を超えています" : "範囲内です";
}

console.log(checkSum(90,20));
```


for文
```ruby
const scores = [10, 20, 30, 40];

// 順番に取り出す
for(score of scores){
    console.log(score);
}

// 繰り返すの回数を決める場合はfor文を使う
for(i=0; i<10; i++){
    console.log(i);
}
```

switch文（if文の方が見やすいため、基本的にif文を書く）
case後の：とbreak;を描き忘れないように注意してください。
```ruby

const data = 1;

switch(data){
    case 1:
        console.log('1です');
        break;
    case 2:
        console.log('2です');
        break;
    case 3:
        console.log('3です');
        break;
    default:
        console.log('1-3ではありません');
        break;
}
```

数値を価格に表示するには、toLocaleStringメソッドを使う。
```ruby
const num = 13000;
// 注意：toLocaleString()は文字列に適用しません
console.log(num.toLocaleString);

const numif = typeof num === "number" ? num.toLocaleString() : "数値を入力してください";
console.log(numif);
```
Javascript18_演算ルール

＆＆　
AND演算のルール：
1 二つの値ともtrueであれば、後者の値を返す
2 二つの値ともfalseであれば、前者の値を返す

例：
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1067004/3c6b7548-4481-8416-b11f-4fdf111d2621.png)

｜｜
｜｜演算のルール：
1 １つ目の値がtureであれば、１つ目の値を返す
2 1つ目の値がfalseであれば、２つ目の値を返す

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1067004/80feb0a5-894b-e7a4-e54e-1d1db2f2e16b.png)
Javascript19_値の代入

+=
*=
/=
%=

非数値について比較する際に、非数値の部分を数値に変換してから比較します。

どんな値がNaNと比較する場合、結果はFalseです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1067004/0d57fb22-cab6-e226-553f-ac3168c4da8e.png)

二つの文字列になっている数値を比較する時に、必ず型変換しないといけません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1067004/40dbe73c-7a24-0950-8616-2c381246f388.png)

Unicodeを打つ方法
＼u4桁数字
例：console.log(ul\2620);

直接Htmlの中に書く方法
＆＃XX；　（ここのXXは10進数です）
例：<h1>&#9760;</h1>
[Ruby]インスタンス生成していないクラスが実行？

Ruby環境設定後、スクリプトファイルを試験動作させて素朴な疑問がわいたので記録しておく。
内容がほぼ個人的備忘録であり、疑問レベルも恐らく初歩も良いところと思う。
しかし、同じ疑問で躓く人が他に居るかもしれず、何かの役に立てれば幸いだと思う。

#疑問に思ったこと
インスタンス生成していないクラスが、なぜ実行されているのか？

#用意した環境

* Windows 10 Pro / 21H2
* RubyInstaller 3.1.0-1 released

#用意したファイル
標準出力へ文字列を出力クラスを定義しただけのスクリプトファイル。
メソッドもなし。

```test1.rb
class Foo
	p "PUT TEST"
end
```

#想定していた実行結果
「p "PUT TEST"」は、クラス「Foo」内にメソッドも実装せず記述しただけなので、
Rubyインタプリタから実行しても、標準出力に"PUT TEST"は出てこないはずと思っていた。

……正直言うと上のスクリプト「test1.rb」は、単にメソッドを記述忘れで出来たミス産物なので、
本当は「p "PUT TEST"」はメソッドへ実装する予定だった。
「それなら想定もクソもないし最初からそう書け」と思われるかもしれないが、
備忘録として話がややこしくなるので、ここは暫し目をつぶって貰いたい。

#実際の実行結果
上記スクリプトファイルをRubyインタプリタで起動。
下記のように、"PUT TEST"が出力された。

````txt:コマンドプロンプト
> ruby test1.rb
"PUT TEST"
```

#他に試したこと
スクリプト、クラス定義、初期化メソッド、独自メソッド、
インスタンス生成、メソッド呼び出しのタイミングでそれぞれ、デバッグ文字列を出力するようにしてみた。

```test2.rb
p "test:outer:1"

class Foo
	p "test:inner:1"
	def initialize()
		p "test:initialize"
	end
	p "test:inner:2"
	def test1()
		p "test:test1"
	end
	p "test:inner:3"
end

p "test:outer:2"
tObj=Foo.new
p "test:outer:3"
tObj.test1
p "test:outer:4"
```

````txt:コマンドプロンプト
>ruby test2.rb
"test:outer:1"
"test:inner:1"
"test:inner:2"
"test:inner:3"
"test:outer:2"
"test:initialize"
"test:outer:3"
"test:test1"
"test:outer:4"
```
#試した結果わかったこと

最初の出力が、スクリプト先頭の"test:outer:1"
次の出力がクラス「Foo」すぐ内側へ記述した"test:inner:1"が出力ということは、
クラスのすぐ内側へ記述したものは、その定義クラスが読み込まれた時点で実行されているようである。

#文献#

ここまで調べた後にRubyのリファレンスマニュアルを良く見たら、「クラス定義」の説明中に、下記の記載があるのを見付けた。
>クラス定義式中には任意の式を書くことができクラス定義の際に実行されます。

https://docs.ruby-lang.org/ja/latest/doc/spec=2fdef.html#class

#他に参考とした外部URL

Rubyでクラス宣言直下に書かれたメソッドの実行時期
https://yucatio.hatenablog.com/entry/2016/06/30/224138


Rubyのクラス定義直下のメソッドの実行タイミングについて
https://dev.classmethod.jp/articles/ruby-under-class-method-timing/
（備忘録）20220216 品川塾 前田 裕二 様

# 1. はじめに
2022年2月16日に[日鉄興和不動産](https://www.nskre.co.jp/)様主催にて開催された『品川塾 前田裕二 さん』での登壇内容について、自分への備忘録としてまとめてみました。ご参考になれば幸いです。

# 2. 登壇内容

## 講師
- 前田 裕二 様

## 3倍密度
- 3倍密度・・・20代：1日24時間働けば3倍成果が出る
- 量質転換・・・量をこなせ
- 1秒は学びを閃くには十分な時間の長さ
- もちろんリラックス時間をとるのも大切

## 思考を3本走らせる
- INPUT・・・相手の話をしっかり理解して受け止める：まず聞く
- OUTPUT・・・自分の疑問や意見、アイデアを溜めておく：話したいこと
- ASSOCIATION・・・関連付けて違うアジェンダにも役立てる：いちばん頭を使うところ

- 頭の中に3つを同時に働かせて考えている
- 普段のメモの割合は、3:3:2

## アソシエーションの能力、関連付けの力
- 3つの抽象化
- 話したいエピソード

## 単純アソシエーション
- シンプルマインド
- 抽象化せずに具体単語でアソシエーション

## 構造アソシエーション
- 抽象化してからのアソシエーション

## アソシエーションの活用
- 学びや法則、気づき、疑問などを抽出
- それを受けて、別のアジェンダ（解決したい課題）に紐づける



## おまけ
- 話したい具体的なエピソードを持っておく
- 待ち受けておく
- メモをストックしておく。
- メモ魔の抽象化転用は無敵


---
## スッキリ
- 批判思考をしている
- 反論後の3手先を考えている
- 地頭は関係ない、訓練できる、まずはアソシエーション思考をやってみる

## 普段
- 会社のメンバとよく話す
- 膝を使わなくてもできるスポーツはディベートである

## 原動力
- 腹の底にある人生の軸
- 小さなスイッチ、この後アイスを食べよう

## 人生の軸
- 8歳の悲しい自分を肯定してあげたい
- 兄の幸せを最大化すること
- 自分自身が夢に向かって頑張れていること
- 登りたい山がありすぎる

## 夢
- 日本から世界に通じるサービスを実現したい
- ネットサービスの領域
- GAFAの弱いところで成果をだしたい

---
## 組織づくり
- ビジョンを大事にするひとを採用する、能力は後回し
- 能力を発揮できずやめていくひとが多くなってしまった
- そのひとの強み、ステージを用意できているか、強みを
- 能力を発揮できない状態はかわいそうなこと
- WILLよりもCAN
- 情報が自分に集まってくる仕組みをつくる

## 寝る時間
- 平日は3時間寝る
- 土日は6時間寝る
- 研究では毎日7時間寝るがよい

## INPUTの時間
- 生きながらINPUTできるものを考えている
- タクシー広告
- タクシー運転手のしぐさ
- 本を読む

---
## 関連付ける先
- その時々で変わるが、大枠は変わらない
- 自分のミッションに関連付ける

## 精神状態、健康状態
- 精神状態は健康状態に左右する
- 健康状態が一番だいじ
- 運動はする
- 食事はとる
- やばくなったら寝る

## 講演中に考えていたこと
- メモ魔熟ドリルを小学生向けに仕上げること
- 10歳に思考3本走らせるにはどう伝えるといいのか？
- 抽象化のトレーニングドリル
- 2022年3月26日発売予定
- 予約開始はそろそろ

## 移動時間にしていること
- 15分間のショートナップ
- 電話している
- 資料書いている
- アイデアを書いている
- 窓の外を見ると一瞬で寝る

## 40代の3倍密度はどうなっていそうか
- アソシエーション先が家族になっていそう

## モチベーション
- モチベーションスイッチが二重構造になっている
- 頑張れる
- もっと高みに登ってやる
- INPUT、本を読む、活用したいと思う、試してみたい
- 自分にとってのモチベーションアップ方法をいっぱい知っておく
- 優秀な人はみんなごきげん
- 機嫌が悪い人とはどんなに優秀でも一緒に働きたくない、あのゲーテも言っている
- とは言え、人間は不機嫌になることもある、そのまま不機嫌で居続けることは罪である
- 仕事は絶対に不機嫌をもたらすもの
- BE:FIRSTは世界に行ってほしい

## 組織の本質
- 業績が悪い時こそ、組織の本質が出る
- きわめてご機嫌に手を打てる組織が強い
- LINE、Slack、スタンプは必ず押す
- 2倍のテンション高いスタンプを送る
- 不機嫌な社内テキストコミュニケーションをしている人は何を考えているのか
- メールしか使えな組織は、DXを相談しに行った方がよい

## 20代にやっておくべきこと
- 難しい、目標によってかわる
- しっぱいをたくさん積む、ひとつでも多く失敗する
- 20代はまだこれから
- 失敗から学ぶ癖をつける
- 失敗のために、挑戦をする、真っ先に手を挙げる

## 60代に若いころやってほしかったこと
- ないです
- 過去には戻れない
- なんで今これをやらないのかなと思うことはある
- 変態紳士クラブを知ろうとしないと、感性が衰える
- 年齢は関係ない、年齢のせいにするのは簡単
- 陰キャJKに惚れる陽キャDKの歌

## やめて次に行くときに捨てきれなかったこと
- 仲間、このルートで行きたいことが変えられない
- 自分は切り替えられる、ルートを変えられる
- なるべく会話して、新しいゴールを合わせている
- すべての挑戦がぜったいうまくいくわけではない
- 絶対達成したい目標は挑戦がうまくいくまで諦めない
- 違う時は方法を柔軟に変える、チューニングに時間をさく

# 3. おわりに
アソシエーションの参考になれば幸いです。
講師 前田裕二 様 、主催してくださいました[日鉄興和不動産](https://www.nskre.co.jp/)様、感謝いたします。

2022/02/16 NISHIZONO Takahiro
CloudWatchLogsにあるログを30日経過後S3にエクスポートする方法

#はじめに
こんにちは、山田です。
今回は、CloudWatchLogsにあるログを30日経過後S3にエクスポートする方法について記載していきます。

#構成図
構成図は以下の通りです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1096361/4e862dfc-e291-535d-6789-9507ba280c4a.png)

**①：EC2インスタンスにCloudWatchエージェントを導入し、対象のログがCloudWatchLogsに送られるように設定する。
②：S3バケットを作成し、CloudWatchLogsからのアクセスを許可するようにバケットポリシーを変更する。
②：30日前のログをS3にエクスポートするLamnbda関数を準備する。
④：EventBrigdeにて毎日23:59にLambda関数が実行されるようにスケジューリングする。**


#S3バケットの作成
①：AWS管理コンソール　-> S3 -> バケット -> バケットを作成をクリックし、一意の名前でバケットを作成する。
②：バケット作成後、以下のようにバケットポリシーを変更しCloudWatchLogsがアクセスできるように設定する。

```
{
	"Version": "2012-10-17",
	"Statement": [
		{
			"Effect": "Allow",
			"Principal": {
				"Service": "logs.ap-northeast-1.amazonaws.com"
			},
			"Action": "s3:GetBucketAcl",
			"Resource": "arn:aws:s3:::<対象のバケット名>"
		},
		{
			"Effect": "Allow",
			"Principal": {
				"Service": "logs.ap-northeast-1.amazonaws.com"
			},
			"Action": "s3:PutObject",
			"Resource": "arn:aws:s3:::<対象のバケット名>/*",
			"Condition": {
				"StringEquals": {
					"s3:x-amz-acl": "bucket-owner-full-control"
				}
			}
		}
	]
}
```

#Lambda関数の作成
①：AWS管理コンソール -> Lambda -> 関数 -> 関数の作成をクリックする。
②：以下のようにコードを記述する。

```python
import boto3
import datetime

log_group_name = '<対象のロググループ名>'
log_stream_name = '<対象のリグストリーム名>'
s3_bucket_name = '<対象のS3バケット名>'
s3_prefix = '<S3のプレフィックス>'

def lambda_handler(event, context):
    
    #実行日取得
    to_time = datetime.datetime.now()  
    
    #30日前取得
    from_time = datetime.datetime.now() - datetime.timedelta(days=30) 
    
    #エポック時刻取得(float型)
    epoc_from_time = from_time.timestamp()
    epoc_to_time = to_time.timestamp()
    
    #エポック時刻をミリ秒にしint型にキャスト
    m_epoc_from_time = int(epoc_from_time * 1000)
    m_epoc_epoc_to_time = int(epoc_to_time * 1000)
    
    client = boto3.client('logs')
    
    response = client.create_export_task(
        logGroupName         = log_group_name,    
        logStreamNamePrefix  = log_stream_name,　　
        fromTime             = m_epoc_from_time,     
        to                   = m_epoc_epoc_to_time,
        destination          = s3_bucket_name,
        destinationPrefix    = s3_prefix
    )
    return response
```

③：S3のアクセス権限が付与されているIAMロールを設定する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1096361/99588c9c-80d7-e88a-b15b-2d6d495c93a1.png)


#EventBridgeの作成
①：AWS管理コンソール -> Amazon EventBridge -> ルール -> ルールを作成をクリックする。
②：以下のように、毎日23:59に発動されるようにスケジューリングを組む。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1096361/e6b2991d-d70f-8686-57e3-d636cb8b5ff6.png)


③：ターゲットで先ほど作成したLambda関数を指定する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1096361/c4cb230e-21d1-5f3c-f25c-bebf61afc8d4.png)

#動作確認
①：対象のバケットにログが保管されているか確認する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1096361/f24acc48-759f-f2dc-b401-b25eb423cdfe.png)

②：エクスポートされたログは、圧縮されているのでログの中身を確認するときは解凍する必要がある。
　解凍後以下のようにログを見ることができれば完了です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1096361/b697c64d-e0ae-9821-0b54-f71023f816a4.png)
Rで決定木を試してみた、そしてクロス集計で結果を再確認してみた

決定木をより良く理解するために、Rで決定木を実行した。
そして数値がどのように決定木で使用されているのかを理解するためにスプレッドシートで関連する数値を集計してみて確認した。

データの用意が面倒なので、ディフォルトのタイタニック沈没のデータを使ってます。
Rのコードは下記の通り

```
 install.packages("rpart")
 install.packages("rpart.plot")
 install.packages("partykit")
 library(rpart)
 library(rpart.plot)
 library(partykit)

 tmp <- data.frame(Titanic)
 df <- data.frame(
  Class = rep(tmp$Class, tmp$Freq),
  Sex = rep(tmp$Sex, tmp$Freq),
  Age = rep(tmp$Age, tmp$Freq),
  Survived = rep(tmp$Survived, tmp$Freq)
)
 head(df)

 ct <- rpart(Survived ~ Class + Sex + Age, data = df, method = "class")
  print(ct) 

  rpart.plot(ct, type = 1, uniform = TRUE, extra = 1, under = 1, faclen = 0)  
  plot(as.party(ct))  

  write.csv(tmp, "/Users/data.csv", quote=FALSE)  
```

結果がこちら
![Rplot.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/369945/4388af7e-64da-8480-53db-9f321261d9e7.png)

生存のYesとNoはまずMaleとFemaleで一番分かれるということがわかる。
直感的な数値を見てみましょう。
やり方は下記のデータテーブルを
![15071645009689_.pic.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/369945/b1b6bc81-8e55-b864-e1da-c875d8312e94.jpeg)
このようにピポットテーブルで集計してみました。
所謂クロス集計です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/369945/8adb0dcb-c9a9-9b0c-20b2-782ce6c32b93.png)
これで見るとFemaleとMaleの生存率に大きな差があることがわかります。

さらに次の要因も見てみましょう。
ClassとAgeがありますが、今回はAgeを選んで集計してみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/369945/d150b3c9-f289-f3df-1b2b-b1d0d150af86.png)
特に3行目と4行目に注目したいですが、Maleの中でもChildの方が生存率は45%程度あり、Adultは20%しかないことがわかります。

というようにクロス集計でも結果は得られますが、決定木を使用した方が効率が良いということを実感できました。
今回は決定木から見て集計したので楽でしたが、ピポットテーブルだと網羅的に集計しないと生存率の差は分かりませんよね。
【#1932エラー】phpMyAdminがおかしいとき

ローカルのDBって日頃手入れしていないと突然おかしくなったりしますよね。

昨夜の自分もそうでした。
急いでるときこそすぐ壊れる、phpMyAdminの応急処置について備忘録として記載します。

##①エラー：#1932 - Table 'phpmyadmin.pma__tracking' doesn't exist in engine

自分はXAMPPからインストールしたphpMyAdminをそのまま使っていたのですが、突然上記のエラーが出てphpMyAdmin上の操作が何もできなくなりました。

phpmyadmin.pma__trackingはあります。
phpMyAdminのDBを物理的に削除し再度XAMPPから入れなおしてもエラー解消せず。

調べたところ、phpMyAdminのバージョンによってはバグで#1932のエラーが出続けるケースがあるようです。（このエラーに関する情報少なかったので、どのバージョンなのかは不明。自分は4.5.1で上記の事象になりました）
phpMyAdminのログイントップ下部により安定していると表示されていたバージョンをオフィシャルサイトから落としました。（4.9.10）

##②ログインできない
上記の流れでエラーは消えました。歓喜もつかの間、ログインできません。
config.inc.phpを確認しようと探したところ見つかりませんでした。
元々XAMPPから落としたほうに入っていた4.5.1のconfig.inc.phpを4.9.10に入れたところ、無事ログインできるようになりました。

##まとめ
表題のエラーに関する記事が少なかったのでこの方法を世に送り出しましたが、
バージョン違いのキメラphpMyAdminを生み出してしまってよかったのかはわかりません。

どうしても困ってしまった午前二時や、エラーパターンの一例としてご活用ください。
【Flutter】Flutterの文字コード＆API送受信時の文字コードの扱い

# Flutterの文字コード
FlutterのString型の文字コードは**UTF-16**。

https://api.dart.dev/stable/2.16.1/dart-core/String-class.html

> String class
> A sequence of UTF-16 code units.

# API送受信時における文字コード変換

一般的に、APIサーバーは**UTF-8**を使用するため、文字コードを変換する必要がある。

### 送信時 (UTF-16→UTF-8)

```dart
import 'dart:convert';
import 'package:http/http.dart' as http;

const request = "あいうえお";
final requestUtf8 = utf8.encode(request);
await http.post(url, body: requestUtf8);
```

### 受信時 (UTF-16←UTF-8)

```dart
import 'dart:convert';
import 'package:http/http.dart' as http;

final response = await http.get(url);
final responseUtf16 = utf8.decode(response);
```
LinuxでのTCP通信の処理(Listenまで)

# システムコールから辿る

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2352304/a2937b8d-2e63-f1ad-552a-fb9ba876ca74.png)


## [sys_listen()](https://elixir.bootlin.com/linux/latest/source/net/socket.c#L1716)
システムコールで呼ばれる大元の関数
1. 引数から作成されたソケットを取得
1. backlogの精査
1. inet_listen()の呼び出し

### sock->ops->listen()とは？
socket構造体にあるproto_ops構造体です。[ソースコード](https://elixir.bootlin.com/linux/latest/source/include/linux/net.h#L114)

proto_ops構造体は[include/linux/net.h](https://elixir.bootlin.com/linux/latest/source/include/linux/net.h#L137)で宣言されておりipv4でTCP通信の際の実装は[net/ipv4/af_inet.c](https://elixir.bootlin.com/linux/latest/source/net/ipv4/af_inet.c#L1020)で行っています。

TCP通信はinet_stream_opsになるので、inet_listen()が呼び出されています。


## [inet_listen()](https://elixir.bootlin.com/linux/latest/source/net/ipv4/af_inet.c#L192)


1. sockをロック
1. sockの状態、Typeの精査(CloseとListen以外の状態をはじく、STREAMのみ通す)
1. バックログの更新
1. sockの状態がListen以外なら
    1. tcp_fastopenの設定(まだ分からない)
    1. inet_csk_listen_start()の呼び出し



上記の実装をする事でlisten状態でも、BackLogの更新が可能になる
## [inet_csk_listen_start()](https://elixir.bootlin.com/linux/latest/source/net/ipv4/inet_connection_sock.c#L1038)
1. reqsk_queue_alloc()でバックログで指定された分だけSYNリクエストキューの割り当て
1. ACK回りのメンバをリセット
1. sockの状態を「TCP_LISTEN」に変更
1. ポートを割り当てることが出来たら
    1. inet_spotにポートを登録
    1. dst_entryをリセット
    1. リスニングソケットハッシュへ登録
1. ポートの割り当てができなかったら、
    1. sockの状態を「TCP_CLOSE」に変更
    1. 1番で割り当てた、queueを削除



## 参考文献
https://www.codetd.com/ja/article/11908254#3.2%20inet_csk_listen_start%28%29
https://elixir.bootlin.com/linux/latest/source/net/ipv4/af_inet.c#L192
https://elixir.bootlin.com/linux/latest/source/net/ipv4/inet_connection_sock.c#L1038
https://elixir.bootlin.com/linux/latest/source/net/socket.c#L1716
https://elixir.bootlin.com/linux/latest/source/include/linux/net.h#L114
https://elixir.bootlin.com/linux/latest/source/include/linux/net.h#L137
https://elixir.bootlin.com/linux/latest/source/net/ipv4/af_inet.c#L1020
【VTuber】推しのVTuberの記録(アーカイブ)を保存する方法考えてみた

# 背景
・自分の最推しが去年の3月に卒業した時にアーカイブを保存してたので，後の人のために残しておきたいと思ったから。
・単純にデータを持つことによる優越感を得るため。
・推しの輝きを統計にしてみたいと思ったから。
# 環境
・Windows 10
・yt-dlp
# 1.保存するコンテンツを決める
まず，記録するといっても何を記録するかによって使うツールなどが変わります。
アーカイブ保存する時に候補になるサイトは，
・YouTube
・ツイキャス
・Twitch
・ニコニコ
と色々ありますが，
ここでは，YouTubeの動画を自前のHDDにぶち込む方法について解説したいと思います。
・動画
・概要欄などの細かい情報
# 2.用意するもの
・モラル(割とグレーよりの黒かもしれないグレーなので)
・PCとそれなりの大容量のHDD(2TB以上あると安心)
・[yt-dlp](https://github.com/yt-dlp/yt-dlp)
# 3.実際の手順
1.[yt-dlp](https://github.com/yt-dlp/yt-dlp)でyt-dlp.exeダウンロードしてくる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2422677/1ceeb14a-81cd-da3f-ef9a-77819d8565b4.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2422677/e1e6a373-7aea-5ede-cd20-4a4adff16866.png)
2.yt-dlpをダウンロードしたら任意の場所に移しておく。(pathを通したい人は調べて通しておく。)
3.PwoerShell等で任意の場所でウィンドウを開く。
#4.yt-dlpの使い方
基本的な使い方は
`yt-dlp.exe [動画のURL]`
で好きな動画をダウンロードできる。
推しのチャンネルの動画すべてをダウンロードするなら
`yt-dlp.exe [チャンネルURL]`
でできる。
オプションとかで画質設定やLiveチャットもダウンロードできるらしい。
詳しくは私は[ここ](https://note.com/kame_equal_eros/n/nc1e25fca17e9)を参照してますね。
# 最後に。
以前，私の推してる推しが引退してしまってその時にかなり困ったので，記事でも書くかぁと思って書きました。
あくまでこれは個人でたしなむものなのでダウンロードしたものをYouTube等にアップロードはしないでくださいね。
悪用厳禁ですよ。うん。
これで救われる(？)人がいればいいなぁって勝手に思ってます。
もう少し解説できるように修正等して行きたいです。
# 参考資料
・[yt-dlp](https://github.com/yt-dlp/yt-dlp)
・[推しVtuberのアーカイブを全部保存したい人へ - youtube-dlの使い方【2021年12月12日更新】](https://note.com/kame_equal_eros/n/nc1e25fca17e9)
・私の知り合い()
Tips: Vimを開くたびにローカルの不要なgitブランチを削除する

## これは何
gitのブランチは、気を抜くと膨大になりがちです。そのままでも実害はないですが、ブランチが増えすぎると、checkoutなどで該当のブランチを探すのも面倒になるため、できれば不要なものは整理しておきたいです。
とはいえ毎回手動で不要なブランチの削除を行うのも面倒なので、僕は使っているエディタのVimの起動スクリプトで不要なローカルブランチを削除するようにしていいます。
この記事ではその方法を短文ですが紹介します。

## 結論
以下のコードを`vimrc`に記載すれば完了です。

```vim
call system("git branch -d $(git branch --merged | grep -v main | grep -v master | grep -v '*')")
```

## コマンドの説明
### `call`
`call`はVim scriptで関数を呼び出すための構文です。今回は`system`という関数を呼び出しています。

https://vim-jp.org/vimdoc-ja/usr_41.html#41.6

### `system`
`system`はVim script内でシェルコマンドを実行するための関数です。これを用いるとシェルコマンドをVim script内で実行できます。今回は`git branch -d $(git branch --merged | grep -v main | grep -v master | grep -v '*')`というコマンドを実行しています


https://vim-jp.org/vimdoc-ja/usr_41.html#system-functions

### `git branch -d $(git branch --merged | grep -v master | grep -v '*')`

このシェルコマンドは、大きくふたつ分けることができます

1. `git branch -d`
2. `git branch --merged | grep -v main | grep -v master  | grep -v '*'`

一つ目のコマンドは「指定されたブランチを削除する」コマンドです。
二つ目のコマンドは「不要なブランチを指定洗い出す」ためのコマンドになります。
二つ目のコマンドで洗い出したブランチを、一つ目のコマンドで削除している、という流れになります。

二つ目のコマンドは以下のようなことをしています。

1. マージされたブランチの一覧を表示する
1. ブランチの一覧から以下のブランチを取り除く
    - main
    - master
    - チェックアウトしているブランチ

`git branch -d $(git branch --merged | grep -v master | grep -v '*')`で、この条件に合致するブランチのみを削除することができます。
外延の継承と内延の継承のメモ

Twitterで外延の継承という言葉を見かけて、知らなかったので調べてみた。
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">外延の継承のところに「継承」を使い、リスコフの置換原則に清々しいまでに違反した設計の代表例が、JDBCのStatementとPreparedStatementです。</p>&mdash; kawasima (@kawasima) <a href="https://twitter.com/kawasima/status/1493883480849059841?ref_src=twsrc%5Etfw">February 16, 2022</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

# まとめ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/87538/0f3094a0-b9ab-5fd3-454b-80f369587b9d.png)


# 図解の元ネタ
https://iwatam-server.sakura.ne.jp/software/devintro/inheritance/x10.html
FTPSとかあるの？

## 勉強前イメージ

SFTPじゃなくて？

## 調査

### FTPS とは

File Transfer Protocol over SSL/TLS の略で、
FTPの弱点をカバーしたプロトコルになります。
ファイルの転送途中に盗聴を防ぐために、SSLとTLSを使って安全に転送できるようにしています。
SSLとTLSについては [こちら](https://qiita.com/miyuki_samitani/items/ff270a994d2e9e3d8132) に詳細記載していますが
SSLのバージョンアップ版がTLSで、基本TLSが使用されているのですがSSLが一般化したことから `SSL/TLS` と呼ばれています。

### FTPS の暗号化方式

- Explicitモード

明示的なモードで、
通信開始時は暗号化せずに通信を確立、
その後クライアントからのAUTHコマンドで暗号化を行います。

- Implicitモード

暗黙的なモードで
通信開始時から暗号化を行うモードです。
FTPの20,21番ポートを使用せずに989,990ポートを使用します。
この方式はクライアントがFTPSに対応している必要があります。

### SFTP とは

SSH File Transfer Protocol の略衣、
sshを使用して暗号化するファイル転送プロトコルです。
基本的にはFTPSと同様のセキュリティ対策を行うことができます。

#### FTPS と SFTP の違い

- FTPS : SSL/TLSを利用した暗号化プロトコル
- SFTP : SSHを使用した暗号化プロトコル

## 勉強後イメージ

初めてこんなプロトコルあるって知った。
似た感じだと思ったけど違うんだね。

## 参考

- [FTPS（File Transfer Protocol over SSL/TLS）とは](https://medium-company.com/ftps/)
- [FTP、FTPS、SFTP、SCPの違いってなんなんだ？](https://liginc.co.jp/398039)
[初心者向け][エンジニア的思考] エラーメッセージは解決方法は言ってくれてない。現象と理由と解決方法を分けて考える。

## 初心者あるある「エラーメッセージの通りやってるのに解決できない＞＜」

:::note info
エラーメッセージに書いてある通り対処しようとしても解決できない。
その裏に潜むことを考えないといけない。
:::

みたいなのTwitterで見かけました。
でもそんな超能力者みたいなことできません……というかそもそもエラーメッセージって解決方法は教えてくれないんですよ。

## 注意すべきは「現象」と「理由」と「解決方法」をわけて考えること

エラーメッセージのほとんどは「**現象**」です。

- ログインできませんでした
- 404ファイルがありませんでした
- シンタックス（構文）がおかしいです

これらはすべて「**現象**」つまり「**エラーが起きましたよ**」ということしか言ってないです。
解決策は言ってません。だからここであせっちゃだめなんです。

JavaScript のエラーメッセージの例です。

```javascript
console.logg("吾輩は猫である")
Uncaught TypeError: console.logg is not a function
```

### まず現象を確認する

このエラーは 「**console.logg が function じゃない**」 と言ってます。それしか言ってないです。
これがエラーの「**現象**」です。起きたことを淡々と言ってるだけです。

### 次に理由を考える

今回の例では **console** オブジェクトに **logg** という **function** がないからエラーになってます。
これがエラーになった「**理由**」です
こにはある程度の知識が必要です。

### そして解決方法を考える

**logg** は **log** のスペルミスだ！修正しよう！です。
ここには意思が入ります。
※ひょっとしたら logg という function を作るのが正解かもしれませんが

**＊＊＊**

すごい当たり前のこと言ってるように聞こえると思いますがエラーメッセージを見てすぐに解決策を考えてテンパる人はこのステップを理解していないことが多いです。

特にまだ勘所がわからないうちは「エラーだ！どうやって解決しよう！」の前に冷静にこのステップで考えてみることをおすすめいたします。

## [ここから中級者向け説明] エンジニアの用語で理解しなおす

少しだけ正しい用語で解説します。

- **バグ（欠陥）** ・・・プログラム上に埋め込まれた不具合のことです。ここでちょっと注意ですがこれは現象のことではなくプログラム上のミスのことです。
- **故障（フェイラー）** ・・・バグのあるプログラムを実行した結果起きた問題です。これがエラーという現象です。
- **ミス** ・・・バグを埋め込んでしまった要因です。例えば知識不足とか仕様理解不足とかです。
- **インシデント** ・・・思った通りに動かないことです。

バグがあってもそのバグが実行されなければ故障は発生しません。
実行されないコードにバグがあっても問題おきません。
またはバグが複数あってうまく連携して結果的に故障が発生せずに正しい動きになってしまうこともあります。
テストのやり方が間違っていたら本当は問題ないのにインシデントになることもあります。
バグをなおすだけではなくバグが生まれた原因であるミスの対策も必要です。
などなど

・・・ただ単に「バグ」というざっくりした用語だとざっくり理解しかできませんが、用語やステップを理解することでより細かくバグのことを考えることができます。

※ 用語は ISTQB/JSTQB というソフトウェアテスティング資格の用語です。
100日でSQLの達人になる@LeetCode! Day16 <case when or iif(条件,真,偽)>

## 610. Triangle Judgement （問題レベル: Easy）要課金
今日の問題はこれ。
３辺の長さが与えられた時、三角形が成立するかどうかを判定するという問題。

多段階の条件分岐が必要な難問かと思ったが、`or`や`and`でまとめればシンプルな問題。
SQLは**ベン図**を思い浮かべよという教えを見たことがあったがまさにそのとおり。

こちらが最初に考えた回答。Solutionに提示してある回答とほぼ同じだった。
```
select x, y, z, 
case when (x < y+z) and (y < x+z) and (z < x+y) then 'Yes'
     else 'No' end as triangle
from triangle
```

Discussを覗くと、一番Voteを得ていた回答は`if(条件,真,偽)`を使った回答。
しかし、何度実行してもacceptされない。
調べたところ`MS SQL Server`では`if(条件,真,偽)`は使用できずに
`iif(条件,真,偽)`を使う必要があるということ。
（もちろん`if 条件 begin 真　end else bein 偽 end`も使える)

```
select x, y, z, 
iif(x < y+z and y < x+z and z < x+y,'Yes', 'No') as triangle
from triangle
```
これならば通った。

基本は汎用性の高い`case when`で良いが、`iif文`も短く記載できるので覚えておこう。

* [LeetCode](https://leetcode.com/)の問題は、MS SQL Serverで解いています。
@Value でファイルから読み取った値を JUnit でモックする

## 動作環境

- Spring Boot v2.4
- JUnit v5.7


## 結論

`org.springframework.test.util.ReflectionTestUtils` で mock できる。
`@Value` を付与しているフィールドに値が付与される。

```java
ReflectionTestUtils.setField(instance, "name", "value", String.class);
```


## 理由

mock をしないとテスト実行で値が null となってしまう。


## 具体的には

例えば `@Value` を利用しているクラス。
`org.springframework.beans.factory.annotation.Value`

```java:FooService.class
@Service
@RequiredArgsConstructor
public class FooService {
  @Value("${app.configValue}")
  private String configValue;
```

上記に対するテスト。
インスタンスを生成する必要がある。

```java:FooServiceTest.class
@ExtendWith(MockitoExtension.class)
class FooServiceTest {
  @Spy
  @InjectMocks
  private FooService fooService;

@BeforeEach
  public void beforeEach() throws IOException {
    mapper = mock(fooMapper.class);
    service = spy(new FooService(mapper));

    ReflectionTestUtils.setField(service, "configValue", "bar", String.class);
  }
```

テスト対象のクラスで `@RequiredArgsConstructor` (`lombok.RequiredArgsConstructor`) を利用しており引数付きでインスタンスを作成する場合には、こんな雰囲気の記述でモックできる。


## 参考

https://spring.pleiades.io/spring-framework/docs/current/javadoc-api/org/springframework/test/util/ReflectionTestUtils.html

https://stackoverflow.com/questions/23162777/how-do-i-mock-an-autowired-value-field-in-spring-with-mockito

https://tech.excite.co.jp/entry/2021/04/18/205538

https://qiita.com/rebi/items/e575a556dd27106894b0
DSNサーバと言われた時にどれがどれかいつも迷うのでまとめました。

## この記事の目的
我々がインターネットを通じてあれやこれやするためには
DNSが必要不可欠であることは皆さま良くご存じかと思います。

ドメイン名を名前解決するその動作イメージは分かりやすいと思っているのですが、
そこに関わる**DNSサーバ(ネームサーバ)と呼ばれるものをややこしく感じる**のは私だけでしょうか。。

役割が異なる2つのサーバを一緒くたにDNSサーバと呼んでいることもそうですし、さらにそれぞれに対して同義語が複数存在しているのも原因かと思います。

「今言っているDNSサーバってどれのこと？」
となりがちなので、私の頭の整理がてらまとめておくことにしておきました。

## 書いていないこと
DNSの仕組みについては一切書いていませんので、他の記事、サイト、本等をご参照ください。

一応一番下に私がDNSを理解するために役立った記事をまとめておきましたので、
ご興味があればご参照ください。

## DNS簡易図
![dns.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2523625/0548d9d5-46e7-4e47-17ff-1f45dfbb75fc.png)


以下では上記の図の
**DNSクライアント**、**DNSキャッシュサーバ**、**DNSコンテンツサーバ**についての別名、他の呼び方をまとめます。

他にもありましたらぜひ教えてください。

## DNSキャッシュサーバ
- フルサービスリゾルバ(フルリゾルバ)
- オープンリゾルバ
　通常フルリゾルバは特定のネットワークからのリクエストのみ受け付けるように設定されていますが、**不特定多数からのリクエストも受け付けるようになっているフルリゾルバ**のことをオープンリゾルバと呼ばれています。
セキュリティ的に宜しくありません。

- パブリックDNS
　誰もが利用できるフルリゾルバです。
　というと、じゃあオープンリゾルバとの違いは？となりますね。
　これはちゃんとした定義は調べても違いが不明だったので参考程度に聞いて欲しいですが、
　オープンリゾルバの中でも、ある団体が適切に運用しているものを特にパブリックDNSと呼ばれているように感じました。
GoogleのパブリックDNS(8.8.8.8)が有名です。

## DNSコンテンツサーバ
- 権威サーバ

- ルートサーバ
一番上位の権威サーバを特にルートサーバと呼ばれます。

## DNSクライアント
- スタブリゾルバ

クライアント、と呼ばれるだけあってこれはDNSサーバではありません。
フルリゾルバはDNSサーバだから、同じようにリゾルバがつくからこっちもそうだと思うとやられます（何に）。
また、単純にリゾルバと言われるときはこのスタブリゾルバだと思っていいです。


## 参考情報
- とりあえず動かす DNS
(https://zenn.dev/koyamaso/books/cbc1f9f136634c)
- 超入門DNS
(https://atmarkit.itmedia.co.jp/ait/articles/1706/21/news041.html)
- DNSの仕組みと運用
(https://atmarkit.itmedia.co.jp/ait/series/2257/)
- TCP/IP - DNSとは その3
(https://www.infraexpert.com/study/tcpip24.html)
コマンドプロンプトでよく使うショートカット（備忘録）

|キー|アクション|
|:-------:|:------|
|Tab|カレントディレクトリ内を先頭から順に表示する|
|↑|1つ前のコマンドを表示する|
|↓|1つ後のコマンドを表示する|
[React] バックエンドを考えずにREST APIを試す

# ReactでREST APIを試したい
Reactを触っていると、バックエンドの情報をページに表示したい場面は多々ありますよね？（まあjs含めなんですが。）
しかし、バックエンドの設計はまだしてないし...というか自分携わらないし...

そんな時にJSON Placeholder × Axios です。

## JSON Placeholder
[JSONPlaceholder](https://jsonplaceholder.typicode.com/)
このサイトが何かというと、超簡単にRESTで実装されたAPIを含んでいます。
更に以下の特徴があります。

* 必要な情報に合わせてダミーデータが返ってくる。（ex.userデータ）
* POSTやPATCHなど、データを登録・変更する作業が伴っても、実際に登録はされない

## Axios
ReactでAPIにアクセスするためにはいくつか方法がありますが、手っ取り早く使うならAxiosと呼ばれるサードパーティがおすすめ。
インストールしておけば、getなど直感的にAPIと繋がれます。

今回はuserの情報で遊んでみます。

### install

```zsh:
$ npm install axios
```

### 使い方の例

```javascript:　App.js
import axios from "axios";

export default function App() {
  const onClickUsers = () => {
    axios
      .get("https://jsonplaceholder.typicode.com/users")
      .then((res) => {
        console.log(res.data);　//今回はconsoleにログを出す
      })
  };

  return (
    <div className="App">
      <button onClick={onClickUsers}>users</button>
    </div>
  );
}
```

上の例はボタンを押したらコンソールにuser情報をgetしてきます。

#### get

```javascript:
axios.get("https://jsonplaceholder.typicode.com/users")
```

#### POST

```javascript:
const user = { name: `test` }
axios.post("https://jsonplaceholder.typicode.com/users", {user})
```

#### PATCH
```javascript:
const user = { name: `test` }
axios.patch("https://jsonplaceholder.typicode.com/users/{id}", {user})// idは自分の好きなように設定

```

#### DELETE
```javascript:
axios.delete("https://jsonplaceholder.typicode.com/users/{id}", {user})// idは自分の好きなように設定
```

# まとめ
とりあえずAPIを試したいとか、軽いモチベーションの時はJSON Placeholderは非常に有用そう。
Axios は設定も簡単なので、APIを使用するときは重宝するでしょう。
[FlyweightPatter]wikiのサンプルをpython3で実装


```python:flyweight_pattern_wiki.py
"""Flyweight Pattern
https://ja.wikipedia.org/wiki/Flyweight_%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3

Desc:
等価なインスタンスを[別々の箇所]で使用する際に、
一つのインスタンスを再利用することによって、
プログラムを省リソース化することを目的とする。

(UML
FlyweightFactory  <>-------------- Flyweight
- pool:Flyweight[]
+ getFlyweight()

(Ex
StampFactory      <>-------------- Stamp
- pool: Stamp[]
+ get()
"""

# Flyweight
class Stamp:

    def __init__(self, type: str):
        self.__type = type

    def print(self):
        print(self.__type)

# FlyweightFactory
class StampFactory:

    def __init__(self):
        self.__pool = {}

    # FlyweightFactory.getFlyweight()
    def get(self, type: str) -> Stamp:
        if type not in self.__pool:
            self.__pool[type] = Stamp(type)
        return self.__pool[type]

    @property
    def pool_size(self) -> int:
        return len(self.__pool)


if __name__ == '__main__':
    factory = StampFactory()
    stamps = []

    for c in 'たかいたけたてかけた':
        stamps.append(factory.get(c))

    for stamp in stamps:
        stamp.print()

    print('StampFactory pool size=', factory.pool_size)


```
MySQLでdumpしたビューがテーブルとして登録されてしまう件を解消する

## 概要

* RDSのMySQL(5.7.33-log)からdumpしたデータをインポートしたらビューがテーブルとして登録されてしまった

## 解決方法

* ビューはビュー、テーブルはテーブルで別々にdumpする

## 解決例

私は業務の都合上PHPで実施しなければいけなかったので、こんな感じで実施しました。

```sample.php
$ignore_views = [];
$sql = 'select TABLE_NAME from information_schema.TABLES where TABLE_SCHEMA = "schemaname" and TABLE_TYPE = "VIEW";';
$select_view_cmd = "/usr/bin/mysql -uusername -ppassword -hhostname schemaname -e'" . $sql . "'";
$views = [];

exec($select_view_cmd, $views, $result_code);
$view_tables = '';
if ($result_code === 0) {
    foreach ($views as $view) {
        if ($view === "TABLE_NAME") {
            continue;
        }
        $ignore_views[] = "--ignore-table=schemaname." . $view;
        $view_tables .= ' ' . $view;
    }
}
if (!empty($view_tables)) {
    $create_view_cmd = "/usr/bin/mysqldump --skip-tz-utc --single-transaction --no-tablespaces -hhostname -uusername -ppassword schemaname" . $view_tables . " > create_view.sql";
    exec($create_view_cmd);
}

$ignore_table = '';
if (!empty($ignore_views)) {
    $ignore_table = implode(' ', $ignore_views);
}
$dump_cmd = "/usr/bin/mysqldump --skip-tz-utc --single-transaction --routines --events -hhostname -uusername -ppassword schemaname " . $ignore_table . " > dump.sql";
exec($dump_cmd);

```
Visual Studio Codeを用いたLambda関数ローカルデバッグ方法メモ

* vscodeでLambdaをデバッグする方法についてメモする。



## 事前準備

* SAMプロジェクト作成

  * ひな形作成

    ```shell
    sam init
    ```

    ※ランタイムはPython3.8を選択。

## 手順

**1.vscodeでSAMプロジェクトを開く。**

**2.Lambda関数`app.py`を開き、テスト用にコメントアウトを外す。**

```python
import json
import requests


def lambda_handler(event, context):
    """Sample pure Lambda function

    Parameters
    ----------
    event: dict, required
        API Gateway Lambda Proxy Input Format

        Event doc: https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-input-format

    context: object, required
        Lambda Context runtime methods and attributes

        Context doc: https://docs.aws.amazon.com/lambda/latest/dg/python-context-object.html

    Returns
    ------
    API Gateway Lambda Proxy Output Format: dict

        Return doc: https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html
    """

    try:
        ip = requests.get("http://checkip.amazonaws.com/")
    except requests.RequestException as e:
         # Send some context about this error to Lambda Logs
         print(e)
         raise e

    return {
        "statusCode": 200,
        "body": json.dumps({
            "message": "hello world",
            "location": ip.text.replace("\n", "")
        }),
    }
```

**3. 28行目`ip = requests.get("http://checkip.amazonaws.com/")`にブレークポイントを設定する。**

**4. 関数`lambda_handler`上部に表示される「`AWS:Edit Debug Configuration`」を選択する。**

**5.`{project_dir}/template.yaml:HelloWorldFunction`を選択する。**

* SAM Debug Configuration Editorが開く

**6. 「`Invoke`」を選択する。**

* ブレークポイントで一時停止し、再開(F5押下)後にデバッグコンソールに実行結果が出力される。

```
{"statusCode": 200, "body": "{\"message\": \"hello world\", \"location\": \"xxx.xxx.xxx.xxx\"}"}END RequestId: f17e83bf-567a-4ccf-989a-de759b47aa87
REPORT RequestId: f17e83bf-567a-4ccf-989a-de759b47aa87	Init Duration: 0.23 ms	Duration: 6440.49 ms	Billed Duration: 6441 ms	Memory Size: 128 MB	Max Memory Used: 128 MB	
```



## 参考情報

* https://docs.aws.amazon.com/ja_jp/toolkit-for-vscode/latest/userguide/serverless-apps-run-debug-no-template.html
minikube （学習＆開発用にローカルでKubernetesを実行可能）環境にHello World的なアプリ（hello-minikube）をデプロイ

# 概要

以下の内容を簡潔にした記事です！！
https://minikube.sigs.k8s.io/docs/start/
※windows環境

以下の記事も参考にさせて頂きました！！
https://speakerdeck.com/minorun365/outikurasutawozuo-tutek8stozhong-liang-kunarou

# 前提条件

Docker Desktop for Windowsをインストールする

https://hub.docker.com/editions/community/docker-ce-desktop-windows

以下のいずれかがあれば、インストール不要
・Docker
・Hyperkit
・Hyper-V
・KVM
・Parallels
・Podman
・VirtualBox
・VMware
・Fusion/Workstation

※デフォルトの設定でOK

※「WSL2のインストールが必要」との警告が出たら、インストールしよう

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103334/c6eba90c-f37e-bc90-3208-861a68a7bb78.png)

※Docker Desktop for WindowsでKubernetesを管理したければ、Enable Kubernetesにチェックをいれよう

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103334/d4083525-60ed-c6f8-6369-536725e325e8.png)

# minikubeのダウンロード

PowerShellを起動して、以下コマンドを実行

```
New-Item -Path 'c:\' -Name 'minikube' -ItemType Directory -Force
Invoke-WebRequest -OutFile 'c:\minikube\minikube.exe' -Uri 'https://github.com/kubernetes/minikube/releases/latest/download/minikube-windows-amd64.exe' -UseBasicParsing
```

# Pathの設定

「管理者」としてPowerShellを起動して、以下コマンドを実行

```
$oldPath = [Environment]::GetEnvironmentVariable('Path', [EnvironmentVariableTarget]::Machine)
if ($oldPath.Split(';') -inotcontains 'C:\minikube'){ `
  [Environment]::SetEnvironmentVariable('Path', $('{0};C:\minikube' -f $oldPath), [EnvironmentVariableTarget]::Machine) `
}
```

# minikube起動

コマンドプロンプト開いて、以下コマンドを実行

```
minikube start
```

# サンプルアプリをデプロイ

コマンドプロンプトで以下コマンドを実行すると、ブラウザが起動してnginxからのメタデータの確認ができる

```
kubectl create deployment hello-minikube --image=k8s.gcr.io/echoserver:1.4
kubectl expose deployment hello-minikube --type=NodePort --port=8080
kubectl get services hello-minikube
minikube service hello-minikube
```

別のコマンドプロンプトで以下コマンドを実行すると、
http://localhost:7080/
からアクセス可能になる

```
kubectl port-forward service/hello-minikube 7080:8080
```

# ダッシュボード起動

別のコマンドプロンプトで以下コマンドを実行すると、ダッシュボードが起動される
先程、デプロイしたアプリが確認できる

```
minikube dashboard
```


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103334/0bd0ffea-734f-1faf-e6dc-061020a42532.png)

# 勉強方法

Hello World的なアプリのデプロイでkubernetesの雰囲気をつかんだ後は
以下のサイボウズの研修資料で勉強するのが良き
https://cybozu.github.io/introduction-to-kubernetes/

# 余談

Docker Desktop for Windowsをインストールした時にでる画面なのだが、一部のキャラクターが誰かわからん。。。
詳しい方教えて下さいm(__)m

・タコ：Neo4j ? Argo ? Kraken ?　→　svnの隣ということで、github説（猫＆タコ）がでてきました！
・カメ：k8s ?　→　TortoiseSVNと教えて頂きました！
・クジラ：docker
・カイ? ：shell script ?
・ペンギン：linux
・ネズミ：go
・カニ：rust

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103334/14ebf514-e3f4-db37-fe69-d7cc341ee4c8.png)
PAMをAI on Browserで動かしてみた！

# 目次
- PAMとは
- AI on Browserで動かしてみる
- 実際のコード
- PAMを応用した手法

# PAMとは
PAM (Partitioning Around Medoids) とは、クラスタリングの手法の1つです。

PAMを用いたクラスタリングの流れは以下の通りです。

1. ランダムに$k$個の点 (medoid) を選択する
2. 各点を最も近いmedoidのクラスタに振り分け、コストを計算する
3. 各medoidについて、medoidでない点と交換してみて、コストを計算する
4. 3で計算したコストがこれまでのコスト未満の場合、交換した点を新たなmedoidとする
5. どの点を交換してもコストが改善されなくなるまで、2から4を繰り返す

コストはクラスタ内の点の非類似度の総和を表しています。
非類似度として用いる関数には複数の種類がありますが、後述するAI on Browserではユークリッド距離を用いています。

ところで、有名なクラスタリングの手法で、k-means法があります。
PAMとk-means法では、コストとして用いる関数が異なります。
PAMではmedoidと各点の距離を、k-means法ではクラスタの重心と各点の距離の2乗をコストとして用います。
このため、PAMはk-means法よりも外れ値の影響を受けにくいというメリットがあります。

# AI on Browserで動かしてみる

ブラウザ上で機械学習のデモを実施できるサイト<a href="https://ai-on-browser.github.io/?data=manual&task=CT&model=pam&dimension=2">AI on Browser</a>を用いて、実際にPAMを実行してみます。

![AI_on_Browser_PAM_2.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2318771/c697dfb0-345e-43bc-0920-9d10e2572729.gif)


画像の中で「Step」ボタンを1回押すごとにEpochの数値が1増えています。
この瞬間に、上述の3と4に該当する処理が1回行われています。
最初の2回はクラスタの分け方に変化が見られますが、それ以降は変化が見られません。
3回目以降の処理では、既にどの点を交換してもコストが改善されない状態になっていると考えられます。

AI on Browserについて詳しく知りたい方は、<a href="https://qiita.com/norimy/items/669c19ecf2256b3ecae5">こちらの記事</a>をご覧ください。

# 実際のコード
AI on BrowserのコードはGithub上に公開されています。
<a href="https://github.com/ai-on-browser/ai-on-browser.github.io/blob/v0.3.0/lib/model/pam.js">PAMの実装コード</a>

# PAMを応用した手法
PAMを応用した手法として、以下の2つが挙げられます。

- CLARA (Clustering LARge Applications)
- CLARANS (Clustering Large Applications based on RANdomized Search)

CLARAとは、データの中からランダムにサンプルを抽出し、そのサンプルに対してPAMを用いることで、処理を高速化する手法です。

CLARANSとは、$N$個のデータを$k$個に分類するパターンをランダムに1つ定め、それに近いパターンとコストを比較していくことで、最適な分類パターンを探していく手法です。

また、これらの手法についても、AI on Browserでデモを行うことができます。
Docker ふたつの方法で環境構築時のコマンド群をまとめる

# 概要

- dockerを使ったローカル開発環境の構築だとしても、いくつかのコマンドを実行する必要がある。その手間を２つのアプローチでコマンドをまとめて解決してみようと思う

# 完成したもの

- [https://github.com/miriwo0104/laravel9_test_docker/tree/shell_and_make](https://github.com/miriwo0104/laravel9_test_docker/tree/shell_and_make)

# 前提

- 今回環境構築時のコマンドをまとめるために使用するDocker環境は下記で.envを外出ししたものを使用する。
    - [Docker .envファイルを用いてポート情報を外出しする](https://qiita.com/miriwo/items/61da44febc2f11ada7dd)

# まとめたいコマンド

- 今回例に上げるlaravelのローカル開発環境構築時にgit cloneとは別に下記のコマンド実行が必要になる。

    ```terminal
    $ docker-compose up -d --build
	$ docker-compose exec php composer install
	$ docker-compose exec php cp .env.example .env
	$ docker-compose exec php php artisan key:generate
	$ docker-compose exec php php artisan migrate
    ```

- 上記5コマンドを一つにまとめたい。

# 方法その1 （shellスクリプトの使用）

1. docker-compose.ymlファイルと同じ階層に下記のshellスクリプトファイルを用意する。

    ```install.sh
    #!/bin/bash
    
    docker-compose up -d --build
    docker-compose exec php composer install
    docker-compose exec php cp .env.example .env
    docker-compose exec php php artisan key:generate
    docker-compose exec php php artisan migrate
    ```

1. 記載が終わったら下記コマンドを実行してinstall.shの権限を変更する。

    ```terminal
    $ sudo chmod 755 install.sh
    ```

1. 下記コマンドを実行してスクリプトファイルを実行する。（コマンドの最後の`1>&2`エラーも標準出力に出してくれるおまじないです。）

    ```terminal
    $ ./install.sh 1>&2
    ```

# 方法その2 （Makefileの使用）

※こちらの知見は同じプロジェクトに参画されているエンジニアさんから教えていただいたものです。

1. docker-compose.ymlファイルと同じ階層に下記のMakefileを用意する。

    ```Makefile
	.PHONY: install
	install:
		docker-compose up -d --build
		docker-compose exec php composer install
		docker-compose exec php cp .env.example .env
		docker-compose exec php php artisan key:generate
		docker-compose exec php php artisan migrate
    ```

1. 下記コマンドを実行する。

    ```terminal
    $ make install
    ```