OmniAICreator/Qiita-1.07M huggingface dataset:
crontabに関するメモ（自分用備忘録）

## この記事の目的
ここに書かれていることは、私のオリジナリティな部分は全くありません。
普段crontabを設定しようとして、「あれ？どう書くんだっけ？」と、ど忘れしがちなので自分のよく使う設定内容・方法についてまとめておきます。

## 設定確認方法
・自分自身（ログイン中のユーザ）の設定確認
``crontab -l``

・任意のユーザの設定確認（権限がある場合に限る）
``crontab -l -u ユーザ名``

・当該サーバにcrontabを設定しているユーザ一覧（rootユーザのみ実行可能）
``ls -l /var/spool/cron``
ここで表示されたファイル名（＝ユーザ名）をcatしても、crontabの設定内容が確認できる
## 設定編集方法
``crontab -e``
## crontabの書き方
・基本書式
``分　時　日　月　曜日　コマンド``
| 項目 |  内容  |  備考  |
| :---: | :---: | :---: |
|  分  |  0～59  ||
|  時  |  0～23  ||
|  日  |  1～31  | 30 と指定したら2月は実行されない|
|  月  |  1～12  ||
|  曜日  |  0～7[0と7は日曜日]  ||
|  コマンド  |  実行したいコマンド  ||

## 具体的な書き方の例
### よく使われる書き方
````crontab
* * * * * command           ・・・毎分実行
0 * * * * command           ・・・毎時0分に実行
5 4 * * * command           ・・・毎日4時5分に実行
0 1,3,5-9 * * * command     ・・・1,3,5,6,7,8,9時に実行
0 10 * * 1-5 command        ・・・平日（月-金）の10時に実行
*/10 * * * * command        ・・・10分毎(0,10,20,30,40,50分)に実行
````

### ちょっと応用
````crontab
* * * * * sleep 30 ; command   ・・・毎分30秒に実行（毎分実行のものと若干ずらす際によく使用する）
3-53/10 * * * * command        ・・・3,13,23,33,43,53分に実行
2-57/5 * * * * command        ・・・2,7,12,17,22,27,32,37,4247,52,57分に実行
````

### その他特殊な書き方
````crontab
@reboot command    ・・・（cronデーモンが）起動した際に一度だけ実行
@daily command     ・・・毎日0時0分に実行（0 0 * * *　と同じ）
@weekly command    ・・・毎週日曜日の0時0分に実行（0 0 * * 0　と同じ）
55 23 28-31 * * /usr/bin/test `date -d tomorrow +\%d` -eq 1 && command  ・・・月末の23時55分に実行
````
### その他特殊な書き方（その２）
#### 第○△曜日に実行したい場合
・考え方
 第1△曜日：1～7日に登場
 第2△曜日：8～14日に登場
 第3△曜日：15～21日に登場
 第4△曜日：22～28日に登場
この日付の範囲であり __かつ__ 曜日が合致すればよい。
````crontab
0 2 1-7 * * /usr/bin/test `date +\%w` -eq 5 && command  ・・・毎月第1金曜日2時に実行
0 2 8-14 * * /usr/bin/test `date +\%w` -eq 1 && command  ・・・毎月第2月曜日2時に実行
0 2 15-21 * * /usr/bin/test `date +\%w` -eq 3 && command  ・・・毎月第3水曜日2時に実行
````

:::note warn
#### 注意事項
毎月第1金曜日2時に実行したくて、
````
0 2 1-7 * 5 command
````
としてしまうと、1～7日　もしくは　金曜日　の2時に実行されてしまう。
:::


:::note alert
## 厳重注意事項
``crontab -r``
とすると、設定が全削除されてしまいます。
Yes / No も聞いてきません。
:::

## その他
・ターミナルでコマンドを直接実行すると動くのに、cronだと思ったように動かない・・・というトラブルが稀によくある
　→コマンド実行時とcron実行時の環境変数の違いが原因かも
◇Windowsのバッチファイルで空きドライブを検索

# ◇空きドライブを検索
サンプルのみ掲載します。

## 空きドライブ検索のサンプル

```bat:UnUsedDrive.bat
@echo off


:: 遅延展開宣言 バグの原因になる場合があります;
setlocal enabledelayedexpansion


:: 空きドライブを検索;
set DRIVE=ABCDEFGHIJKLMNOPQRSTUVWXYZ
for /l %%i in (0,1,25) do (
  set DL=!DRIVE:~%%i,1!
  if not exist !DL!: (set UNUSEDRIVE=!UNUSEDRIVE!!DL!)
)
echo 使われていないドライブは [%UNUSEDRIVE%] です。
echo.


:: 終了処理（エクスプローラーから起動されていた場合はPauseする）;
:EndProcess
echo %cmdcmdline%|find /i "%~f0">NUL
if %ERRORLEVEL% equ 0 Pause
goto :end
```


# おわりに

他にも「[⧉Windowsのバッチファイルのテクニックをご紹介します][]」にてご紹介させて頂いております。
お時間ありましたら覗いてみてください
お粗末様でした。

https://github.com/Aromatibus/

[⧉Windowsのバッチファイルのテクニックをご紹介します]: https://qiita.com/aromatibus/items/12fa4775f38c9889a686
Ubuntu22.04 Python3.10.6をインストールする

## 概要
リモートサーバのUbuntu22.04にPython3.10.6をインストールします。最初からインストールされているPythonはOS用なので、別のPythonをインストールして使うのが一般的とのことでした。Ubuntu22.04にもともとインストールされていたPythonは3.10.4でした。作業PCはWindows10 Proです。

記事を参考にさせていただきました。
[Ubuntu環境のPython](https://www.python.jp/install/ubuntu/index.html)
[デフォルトでインストールされているPythonを使うのはヤバイ](https://self-development.info/ubuntu%E3%81%AB%E6%9C%80%E6%96%B0%E3%83%90%E3%83%BC%E3%82%B8%E3%83%A7%E3%83%B3%E3%81%AEpython%E3%82%92%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB%E3%81%99%E3%82%8B/)

## 手順1 ビルドツール・ライブラリのインストール
UbuntuにPythonをインストールするにはビルドする必要があるらしいので、ビルドツールをインストールします。
```
$ sudo apt update
$ sudo apt install build-essential libbz2-dev libdb-dev \
  libreadline-dev libffi-dev libgdbm-dev liblzma-dev \
  libncursesw5-dev libsqlite3-dev libssl-dev zlib1g-dev uuid-dev
```
GUIなしのWebサーバとして動かしたいのでtk-devは消しました。

## 手順2 Python3.10.6をダウンロード
[ここ](https://pythonlinks.python.jp/ja/index.html)からダウンロードします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2744334/0af8f50f-1d3d-1a6e-86be-da3177ebd0b7.png)

## 手順3 リモートサーバに送る
作業PCにダウンロードしたPythonを、teratermにドラッグアンドドロップして、リモートのUbuntuのホームディレクトリに送ります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2744334/6cba3e77-d61c-df1b-ec5e-d298adb2d533.png)
ちゃんと送られてました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2744334/2cce5ed1-3e8b-e404-bc23-1703ee5a8920.png)

## 手順4 Pythonを解凍してビルドしてインストール
```
$ tar xJf Python-3.10.6.tar.xz
$ cd Python-3.10.6
Python-3.10.6$ ./configure
Python-3.10.6$ make
Python-3.10.6$ sudo make install
```

## 手順5 再起動と確認と後片付け
```
Python-3.10.6$ sudo shutdown -r now
$ python3 --version
$ sudo rm -rf Python-3.10.6
$ sudo rm -rf Python-3.10.6.tar.xz
```
VULTRにWindows10をインストールしてアプリを動かす

VirmachのWindows Server 2016のメンテナンスが長期化したため、VULTRでBidmachineを動かそうと思った。断片的な情報が英語などであるが、わかりづらかったので出稿。

目次
<B>１．Windows10.isoとvirtio-win.isoのダウンロード
２．Windows10.isoとvirtio-win.isoをImgBurnで結合
３．Dropboxなどで結合したisoファイルをアップロード
４．インストールで、つまづく２カ所</B>

<B>１．Windows10.isoとvirtio-win.isoのダウンロード</b>

Windows10.isoは、<a hef="https://www.microsoft.com/ja-jp/software-download/windows10">このページ</a>のツールを今すぐダウンロードから。

virtio-win.isoは、<a hef="https://github.com/virtio-win/virtio-win-pkg-scripts/blob/master/README.md">このページ</a>のLatest virtio-win ISOから。

<b>２．Windows10.isoとvirtio-win.isoをImgBurnで結合</b>

(1)Windows10.isoとvirtio-win.isoをWinRARなどで、解凍。
a)Windows10.isoから以下のフォルダとファイル
　①boot
　②efi
　③sources
　④support
　⑤autorun.inf
　⑥bootmgr
　⑦bootmgr.efi
　⑧setup.exe

  b)virtio-win.isoから以下のフォルダ
　⑨virtio-win

  の9個のファイルとフォルダを選択。
（ImgBurn---モード>ビルド)

※ファイルがすべて同じフォルダからになってますが、1つのフォルダにまとめておいたからです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80274/92a26990-571a-a48d-18f7-3cbac67ace93.png)


(2)ImgBurnで上記9個のファイルを以下の設定で結合。
　
・オプション---データの種類 MODE1-2048
              ファイルシステム UDF
UDFリビジョン 1.02

・詳細設定>オプション　イメージをブータブルにするにチェック---エミュレーションの種類　なし（カスタム）
ブートイメージ  windows.iso￥boot￥etfboot.com
開発者ID Microsoft Corporation
ロードするセグメント 07C0
ロードしたセクター　8
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80274/2c5d91ba-c27a-1b6f-3a13-e95706b118b7.png)

(3)結合
上図では、C:￥custom￥windows5.isoとして出力。

<b>３．Dropboxなどで結合したisoファイルをアップロード</b>
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80274/95689f72-ace7-4a11-cfa9-badb3cf225d4.png)

リンクをコピーし、VULTRのProducts>ISOsの+マークから、ADD ISO
リンクをペーストあるいは、ADD ISOから。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80274/bbdae6f2-6173-ea58-8eae-a4c67cb9a64c.png)

バックアップは必要なかったが、High Performanceの方が良さそうな気がしたので、推奨を受けて+$1。あまり変わった気がしなかったが・・・

<b>４．インストールで、つまづく２カ所</b>
SCSIとNICのドライバーのところ。SCSIのドライバーがないので、インストールするSSDが見つからない。NICのドライバーがないので、インターネットに繋がっていない。

1)SCSI
ドライバーを読み込み>Windows10**iso￥viostor￥w10￥amd64を選択して、インストール

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80274/5c6a0a66-da0d-256e-83df-2b7935b5ae21.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80274/715ef7c9-073f-5e93-c563-e783499c5c44.png)

2)NIC

ネットワークに接続しましょうという文字がでてくるので、左下の”インターネットに接続していません。”をクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80274/0d6970d9-3db4-7130-c17e-29ca38ce9873.png)

続いて、左下の”制限された設定で続行する”をクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80274/5c459270-7162-3531-1677-8e0b47e53a15.png)

ネットに繋がっていないまま、気にせずインストール。その後、コントロールパネル>デバイスマネージャー>イーサネットコントローラーを選択して、ドライバーの更新をする。
フォルダは、Windows10***.iso￥virtio-win￥NetKVN￥w10￥amd64

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80274/557732af-24d0-f0c9-46c7-f0d9f637be46.png)
私のような非エンジニアの方のお役に立てたら、幸いです。
。
【学習記録】応用情報②

# はじめに
学習記録の②です。

# 間違えたところ・知らなかった単語

## ARP(Address Resolution Protocol)について
* IPアドレスから対応する機器のMACアドレスを取得するプロトコル

### ARPによるMACアドレス取得手順
> 1.要求パケットに、MACアドレスを得たいIPアドレスと、送信元のIPアドレス・MACアドレスを格納して、同一セグメント内にブロードキャストする。
2.要求パケットを受け取った各ノードは、解決目的のIPアドレスを確認し、自分のIPアドレスであれば、自分のMACアドレスを送信元に伝える。

### ほかの選択肢から学んだこと

略称|正式名称|説明|
---|---|---
DHCP|Dynamic Host Configuration Protocol|端末に対して動的にIPアドレスを割り当てるためのプロトコル|
TCP|Transmission Control Protocol|IPネットワークにおける誤り制御のためのプロトコル|



## 分散DBシステムについて
* 地理的に離れた場所にある複数のDBを、まとめて１DBとしてアクセスできるようにしたDBのこと
* 分散データベースを利用する際に、データベースが複数の場所に離れていることを意識せずに利用するために求められる性質を「透過性」といい、次の6つの種類がある

### 位置に対する透過性(location transparency)
利用者はそれが配置されている位置を意識せずにデータベースを利用できること
### 移動に対する透過性(migration transparency)
データの格納サイトが変更されても、利用者のアプリケーションや操作方法に影響がないこと
### 分割に対する透過性(fragmentation transparency)
一つの表が複数のサイトに分割されて格納されていても、利用者はそれを意識せずに利用できること
### 複製に対する透過性(replication transparency)
同一のデータが複数のサイトに格納されていても、利用者はそれを意識せずに利用できること
### 障害に対する透過性(failure transparency)
あるサイトで障害が発生しても、利用者はそれを意識することなく利用できること
データモデルに対する透過性(data model transparency)
各サイトのDBMSが扱うデータモデルが異なっていても、利用者はそれを意識することなく利用できること

## SL理論(状況対応リーダーシップ理論)について 
* いかなる状況にも対応可能な万能のリーダ行動は存在しないという主張の元、リーダーシップの有効性を状況との関係でとらえる
* 状況要素のうち最も重要である部下や集団（フォロワー）の能力及び意欲の水準（レディネス）ごとに、有効性が高いリーダシップのスタイルを示したモデルのこと

> 本モデルでは、部下に対する「指示の度合い」及び「協労の度合い」により、リーダシップのスタイルを以下の4つに分類しています。
高指示・低協労（スタイル1、教示的）
職務遂行について指図することが多く、支援的な活動をすることは少ない
高指示・高協労（スタイル2、説得的）
職務遂行についての指図、支援的な活動がともに多い
低指示・高協労（スタイル3、参加的）
職務遂行についての指図することが少なく、支援的な活動をすることが多い
低指示・低協労（スタイル4、委任的）
職務遂行についての指図、支援的な活動がともに少ない
また、特定の課題に対する部下や集団の成熟度（レディネス）を、「能力」及び「意欲」により以下の4段階に分類し、順番に移行していくものとしています。
低能力・低意欲（レディネス1）
能力が低く、意欲も低い（または不安を感じている）
低能力・高意欲（レディネス2）
能力は低いが、意欲は高い
高能力・低意欲（レディネス3）
能力は高いが、意欲は低い（または不安を感じている）
高能力・高意欲（レディネス4）
能力が高く、意欲も高い
このように定義された4つのリーダシップのスタイルと4つのレディネスを結び付けたのがSL理論です。有効性の高いリーダシップスタイルは、部下の成熟度により以下のように結び付けられます。最良のリーダシップスタイルは存在せず、それぞれのスタイルは状況によって、適切であったり、効果的であったりします。

## ディープラーニング(Deep Learning)について
* 人間や動物の脳神経を模したアルゴリズム（ニュートラルネットワーク）を多層化したものに十分な量のデータを与えることで、人間の力なしに自動的に特徴やパターンを学習させること

### ほかの選択肢から学んだこと
名称|説明|
|---|---
|エキスパートシステム|ある分野に特化した知識を基にルールベースの推論を行うことで、専門家レベルの問題解決を行うこと
|（木構造やグラフ構造に対する）深さ有線探索|試行錯誤しながら条件を満たす解に到達する方法であり，場合分けを行い深さ優先で探索し，解が見つからなければ一つ前の場合分けの状態に後戻りする探索方法|エキスパートシステム|ある分野に特化した知識を基にルールベースの推論を行うことで、専門家レベルの問題解決を行うこと
医療崩壊予防のために技術者ができること

# 技術者はなんとか新しいものを簡潔に作るしかない

というわけで、まずこんなのをスパゲティーコードでもいいので作ってみました

https://github.com/nanakochi123456/covid19-checker

坦々と情報を蓄積していたのですが、もう一度急いで情報を徹夜で収集し、とりあえずリリース

おまけに Wordpressにも対応

https://neet.co.jp/covid19

あ、4K4KっていってたHP ここのことです。ごめんなさい。隠してて

医療資格がなくても、ニュース等の情報を集め、
みんながどこでも使えるツールにしておきたかったわけです。

というわけで、これ、よろしければ、パクって使ってください、のお願いをしにまいりました。

国外では、チャットボットっぽいもっと優れたツールもあるようですが（でもソースを見たら大したことしてなかった）

これも、大したことはしていません。

ですが、いつ、どこで、情報の少ない人に

コロナにかかっても、（[医療崩壊で受診しないで、という声明がでても](https://www.jiji.com/jc/article?k=2022080200964&g=soc)）

受診させずに、なんとか医者に行かずに対処させる方法を
多くの人に浸透させればと考えます。
簡易Excel Viewer(ReoGridコンポーネント使用)の作成 CSVファイル読み込み編

# はじめに
前記事にてCSVファイルの読み込みをした場合、Excel同様に前0が消えてしまったという段階でした。

https://qiita.com/yaju/items/29b68f4b99ce8002bb05

残念ながら北海道の都道府県コード"01"が、Excel同様に前0が消えてしまう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/ab575589-4c07-9864-8f96-d3e194f10384.png)

コメントも頂いたのですが、文字列書式を設定してからCSVファイルの読み込みをすれば前0が消えなくなるのではと思って試したのですが、Loadメソッドを実行するとワークシートを新規作成するようになっているため、予め文字列書式を設定しても意味がなかったです。

# どうにか出来ないか?
ReoGridコンポーネントはExcelに近い見た目と操作感があるので、前0が消えないようになればCSVエディタとしての使い道が広がる。

ソースコードがGitHubに公開されているので、何をしているかが分かります。

https://github.com/unvell/ReoGrid/blob/master/ReoGrid/IO/CSVFormat.cs

Loadメソッドを呼ぶとワークシートを新規作成してしまうので、Loadメソッドを使わずにワークシートに文字列書式を設定してからCSVファイルの読み込みだけするReadメソッドを呼べばいいわけです。

## リフレクションで呼ぶ
Readメソッドは、internal static class CSVFormatに定義されています。
internalは「アセンブリが同じクラスのみから参照できる」というアクセス修飾子です。

```cs:CSVFormat.cs
internal static class CSVFormat
{
     public static void Read(Stream stream, Worksheet sheet, RangePosition targetRange, 
			Encoding encoding = null, int bufferLines = DEFAULT_READ_BUFFER_LINES, bool autoSpread = true)
```

この場合、通常の呼び出しでは使えないですがリフレクションを使用することで呼び出すことができます。
列Aだけ文字列書式にしてから、Readメソッドを使ってCSVファイルを読んでみます。

```cs
private void button1_Click(object sender, EventArgs e)
{
    string path = "c03.csv";

    Worksheet sheet = grid.CurrentWorksheet;
    sheet.SetRangeDataFormat("A:A", CellDataFormatFlag.Text);

    RangePosition targetRange = RangePosition.EntireRange;

    Assembly asm = Assembly.LoadFrom("unvell.ReoGrid.dll");
    Module mod = asm.GetModule("unvell.ReoGrid.dll");
    System.Type type = mod.GetType("unvell.ReoGrid.IO.CSVFormat");
    if (type != null)
    {
        using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
        {
            MethodInfo readMethod = type.GetMethod("Read");
            object ret = readMethod.Invoke(null, new object[] { fs, sheet, targetRange, Encoding.Default, 512, true });
        }
    }
}
```

![スクリーンショット 2022-08-05 1.48.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/36c54afd-32dc-8f16-cf7c-14e8a7fa5374.png)

# TSVファイルの読み込み
ReoGridコンポーネントでは、Excel(xlsx)、RGF(ReoGrid Format)、CSV(カンマ区切り)ファイルしか対応していません。
よって、タブ区切りのTSVファイルを読む込みしたい場合、もう一工夫する必要があります。

CSV(カンマ区切り)ファイルに限定されているのは、正規表現でカンマ区切りで固定されているからです。

```cs:CSVFormat.cs
private static Regex lineRegex = new Regex("\\s*(\\\"(?<item>[^\\\"]*)\\\"|(?<item>[^,]*))\\s*,?", RegexOptions.Compiled);
```

これをタブ区切りにするには、リフレクションを使ってタブ用の正規表現に書き換えればいいわけです。

```cs
Regex lineRegex = new Regex("\\s*(\\\"(?<item>[^\\\"]*)\\\"|(?<item>[^\\t]*))\\s*\\t?", RegexOptions.Compiled);

var fiedlineRegex = type.GetField("lineRegex", BindingFlags.Static | BindingFlags.NonPublic);
fiedlineRegex.SetValue(null, lineRegex);
```

テスト用のCSVファイルをタブ区切りのTSVファイルして、読み込んだところ表示することができました。

この仕組みを使えば指定した文字列区切りで読むことが可能になります。
また正規表現で桁区切りにすれば、固定帳ファイルも読めるようになるはずです。

# 最後に
全列を文字列書式にしてしまうのもあるのですが、Schema.iniファイルのような定義ファイルを用意してCSVファイルを読むようにすればいい。文字列書式でもコードなら左寄せではなくて中央表示にしたりも出来ますし、数値も3桁区切り表示とかも出来ます。

作り込めばいろいろ出来そうです。夏休みも近いので挑戦してみようかな。
【個人開発 | GAS】QiitaトレンドをGmailで送信するプログラムを作る

# はじめに
<!-- 発端や概要を記載 -->
Qiitaから毎週メールで「先週LGTMが多かった投稿ベスト20」が届く。
こんな感じでトレンドを取得してGmailで送信するプログラムを作ります。
トリガーで1日おきで指定すればお手軽に定期的に実行できます。
＊サクッと作ったコードですのでもっといい書き方があったらコメントください,,,!

# 目次
<!-- タイトルとアンカー名を編集 -->
1. [コードの紹介](#コードの紹介)
1. [ライブラリについて](#ライブラリについて)
1. [おまけ](#おまけ)
1. [参考文献](#参考文献)

<!-- 各チャプター -->
<a id="#コードの紹介"></a>
# コードの紹介
```
// Qiitaのトレンド記事を取得しHTMLメールとして送信する。
function sendQiitaTrendMail() {
  // URL取得
  const response = UrlFetchApp.fetch('https://qiita.com/');
  // コンテンツ取得
  const content = response.getContentText('utf-8');
  // タイトルとURLはh2タグ内に記載があるので配列として取得する
  const articleArray = Parser.data(content).from('<h2 class="css-1t4fpk1">').to('</h2>').iterate();

  // メールの本文
  let body = '';
  // 各タイトル及びURLを配列から取得し本文に追加
  for (let i = 0; i < articleArray.length; i++) {
    // 記事のタイトル取得(タイトルは配列で取得しているため1個目の値を取得する)
    let title = Parser.data(articleArray[i]).from('>').to('<').iterate();
    title = title[0];

    // トレンド記事のURL
    let url = Parser.data(articleArray[i]).from('href="').to('"').iterate();

    // 本文にタイトルとURLを追記して改行
    body = body + '<a href="' + url + '">' + title + '</a>';
    body = body + '<br>--------------------<br>';
  }

  //送信先のメールアドレス
  const recipient = 'hogefuga@example.com';
  //件名
  const subject = '今日のQiitaトレンド';
  //送信者の名前
  const options =
  {
    name: '自動送信：Qiitaトレンド',
    htmlBody: body,
  };

  // メール送信
  GmailApp.sendEmail(recipient, subject, body, options);
}
```

<a id="#ライブラリについて"></a>
# ライブラリについて
### Parser
GASでスクレイピングを行う際に使用したライブラリ。
from(),to()で文字を抜き出すために使っています。
今回だとQiitaのトレンドのh2タグ内のaタグのhrefからURLを取得し、記事のタイトルも取得しています。

<a id="#おまけ"></a>
# おまけ
前日までに送信したQiitaトレンド通知メールを削除する関数。
```
// sendQiitaTrendMailで作成したメールのうち、昨日までに送信されたものを削除
function deleteQiitaTrendMail() {
  // dateオブジェクト作成
  let date = new Date();
  //現在の「日」を取得
  let day = date.getDate();
  //前日日付にしたいので-1する
  date.setDate(day - 1);
  //日付の表示形式を整形する
  let yesterday = Utilities.formatDate(date, 'JST', 'yyyy/MM/dd');

  // 検索条件 件名が「今日のQiitaトレンド」かつ昨日までに送信されたメールを検索。
  let query = 'subject:今日のQiitaトレンド before:' + yesterday;
  console.log(query);
  let threads = GmailApp.search(query);
  let messageThreads = GmailApp.getMessagesForThreads(threads);

  // ゴミ箱に移動
  for (let messages of messageThreads) {
    messages[0].moveToTrash();
  }
}
```

<a id="#参考文献"></a>
# 参考文献
- [Parserライブラリを使用してGASでスクレイピングをする](https://specially198.com/scraping-with-gas-using-parser-library/)
No-IP Dynamic Update Client (DUC) for Linuxをインストール

No-IPで作成したドメインとコロコロ変わるIPを紐付けしてくれるDUCをRaspberry PIにインストールしたのでその過程をメモ

# 環境
・RaspberryPi4 Debian GNU/Linux 11 
・DUC 2.1.9

# インストール
インストールのためのパッケージをダウンロードするため適当なディレクトリに移動
```
cd /home/noip
```

パッケージをダウンロード
```
sudo wget https://www.noip.com/client/linux/noip-duc-linux.tar.gz
```

パッケージを解凍
```
sudo tar vzxf noip-duc-linux.tar.gz
```

解凍後に生成されるディレクトリに移動し, コンパイル
```
cd noip-2.1.9-1/

sudo make
sudo make install
```

ここで入力箇所がいくつかあるので順に入力していく
```
Please enter the login/email string for no-ip.com
▶︎  No-IPアカウントのemail


Please enter the password for user 《入力したemail》     
▶︎  No-IPアカウントのパスワード


* hosts are registered to this account.
Do you wish to have them all updated?[N] (y/N)         
▶︎  登録済みホストのIPを全てデバイスのIPに更新するかどうか（デフォルト：N） 
▶︎  Nにするとひとつひとつ更新するか聞いてくる


Please enter an update interval:[30]                   
▶︎  更新頻度を入力（デフォルト：30分）


Do you wish to run something at successful update?[N]  
▶︎  更新に成功した場合に何か実行するか（デフォルト：N）
```

プログラムの実行
```
sudo noip2
```

エラーが出た場合, 実行ファイルのパスが通ってない可能性があるので, 頻繁に実行コマンドを打つ予定がなければ以下のコマンドを入力
```
sudo /usr/local/bin/noip2
```

# 起動状態の確認
以下のコマンドで実行中DUCの詳細が確認できる（オプションの-Sは大文字）
```
sudo noip2 -S
```
***\* noip2 process active.*** と表示されればDUCは問題なく起動している

# 自動起動の設定
PCを起動した際に自動でDUCを起動するように設定する

まずは自動起動のためのスクリプトファイルを`/etc/init.d/`にコピー
OSがDebianの場合, debian.noip2.shをコピーする
```
sudo cp debian.noip2.sh /etc/init.d/noipd
```

noipdのパーミッションを変更
```
sudo shmod +x /etc/init.d/noipd
```

自動起動を登録
```
sudo update-rc.d noipd defaults
```

# 新バージョンへの移行
アップデートしたい場合はプロセスを停止して実行ファイルを削除する必要がある
```
ps -u | grep noip2
```
プロセス`noip2`の詳細を表示させてプロセスIDを記録
```
kill 《プロセスID》
```
でプロセス終了

その後, 実行ファイルとconfigファイルを削除する
```
sudo rm /usr/local/bin/noip2
sudo rm /usr/local/etc/no-ip2.conf
```
これらが成功したら再度[インストール](#インストール)を行う

# 参考
https://www.noip.com/support/knowledgebase/install-ip-duc-onto-raspberry-pi/
格安のステッピングモータ(28BYJ-48)(＋ULN2003ドライバーボード)をArduinoで簡単に制御する


## 購入したもの
Amazonで格安のステッピングモータを購入して、とりあえず簡単に制御してみたのでその際のメモです。
購入したものは、[ステッピングモータ+ 28BYJ-48 ULN2003ドライバーボード セット](https://www.amazon.co.jp/gp/product/B07LBRNZB6/ref=ppx_yo_dt_b_asin_title_o00_s00?ie=UTF8&psc=1 "28BYJ-48")です。
私が購入したタイミング(2022/8/1)では699円でした。
3つ入りなので1つあたり250円弱。ドライバーボードもセットなので、ちゃんと動くのであれば格安ですね。

## 環境
・Arduino：私の場合はESP8266を使用しましたが、Arduinoであればピン番号だけ読み替えてもらえれば基本同じだと思います。
・端末：Windows10

## 物理的な接続
ULN2003ドライバーボードとESP8266は以下のように接続しました。
特に意味は無く、開発ボードの隣り合わせた4つの端子を選んだだけです。
IN1　・・・ GPIO16
IN2　・・・ GPIO14
IN3　・・・ GPIO12
IN4　・・・ GPIO13
+　・・・　5V電源の+
-　・・・　5V電源の-

念のため電源は別のUSBから取りました。

## 使用したライブラリ
最初は[Stepper](https://www.arduino.cc/reference/en/libraries/stepper/)というライブラリを試していたのですが、なぜかうまく制御することができず、
代わりに[CheapStepper](https://github.com/tyhenry/CheapStepper)というライブラリを使用しました。
ライブラリの説明にも”__An Arduino library for the 28BYJ-48 stepper motor using ULN2003 driver board__”と記載されていたので、ぴったりなのかなと思いました。

## スケッチ
### とりあえず動作させてみるスケッチ
```スケッチ
#include <CheapStepper.h>

const int A = 16; //IN1
const int B = 14; //IN2
const int C = 12; //IN3
const int D = 13; //IN4

CheapStepper stepper (A, B, C, D);

void setup() {
    stepper.setRpm(12); // sets speed (10-24 rpm, hi-low torque)
	// <6 rpm blocked in code, may overheat
	// 23-24rpm may skip

}
void loop() {
    stepper.moveDegreesCW (60); // 時計回りに60°回転
    delay(1000);
    stepper.moveDegreesCCW (30); // 反時計回りに30°回転
    delay(1000);
}
```

これだけで簡単に任意の方向に任意の角度回転させることができました。
ただし、原因は不明ですが
``stepper.moveDegreesCW (180);``
のように回転角を大きくすると、私の使用していたESP8266は頻繁に再起動を繰り返してしまいました。
小さい回転角を連続で繰り返す（例えば15度を12回連続動作させる）場合は問題無いので良いのですが少し気持ち悪いです。
原因の特定・対処ができれば追記します。

### 時計の秒針のような動きを再現する例
ステッピングモータの動作時間に影響されずに定期的なタイミングで次の動作指定ができるように少し工夫しました。

具体的には、
1.  最初に、変数 $prev$ に0を代入（初期化）
2.  $millis()$ でArduinoの起動からの経過時間を知り、それを変数 $curr$ に代入
3.  $curr$ - $prev$ が目的の実行周期以上になるまで待つ
4.  ステッピングモータを動作させる
5.  $prev$ に $curr$ を代入する
6.  2.に戻る

注意点として、実行周期（変数 $interval$）は、4.で指示するステッピングモータの動作に必要な時間よりも長く設定する必要があります。

```スケッチ
#include <CheapStepper.h>

const int A = 16; //IN1
const int B = 14; //IN2
const int C = 12; //IN3
const int D = 13; //IN4

CheapStepper stepper (A, B, C, D);

unsigned long prev, next, interval;

void setup() {
    stepper.setRpm(12); // sets speed (10-24 rpm, hi-low torque)
	// <6 rpm blocked in code, may overheat
	// 23-24rpm may skip

  prev = 0;         // 前回実行時刻を初期化
  interval = 1000;  // 実行周期を設定

}
void loop() {
  unsigned long curr = millis();    // 現在時刻を取得
  if ((curr - prev) >= interval) {  // 前回実行時刻から実行周期以上経過していたら
    stepper.moveDegreesCCW (6);     // 6°（1秒分）回る
    prev = curr;                    // 前回実行時刻を現在時刻で更新
  }
}
```

## 今後
とりあえずこれで時計を作成してみようかなと思います。
できればNTP時計を作りたいのですが、ステッピングモータは現状の回転位置を知ることができないので、何か手立てを考えなければいけませんね。
M1 Mac上でx86ビルドのバイナリを動かしたかった時にクソ遅くてもうろたえないためのメモ

M1 Mac + UTM + ARM VM + ARM Ubuntu

    $ sudo sysbench --test=cpu run
    (snip)
    CPU speed:
        events per second:  8260.90
    (snip)
    
    $ sudo sysbench --test=memory run
    (snip)
    Total operations: 80760775 (8075066.03 per second)
    
    78867.94 MiB transferred (7885.81 MiB/sec)
    (snip)


M1 Mac + UTM + Q35/ICH9 + X86 Ubuntu

    $ sudo sysbench --test=cpu run
    (snip)
    CPU speed:
        events per second:   263.97
    (snip)
    
    $ sudo sysbench --test=memory run
    (snip)
    Total operations: 8733803 (873039.18 per second)
    
    8529.10 MiB transferred (852.58 MiB/sec)
    (snip)
Git奮闘録

# 概要
Gitを使った行き詰った問題や、その解決できた参考サイトをまとめた、Git初心者の備忘録。
基本自分の記録用に書いているので、自分の起こった出来事→その解決法という形式で記述する。
**Git初心者が書いています。説明等に間違い・誤解を招く表現があると予想されます。正確な仕様・詳しい解説は各自調べてください。~~何かしらの被害が出てもおあしす　としか言えません。~~**


# Electronでnode_modulesをcommitしてしまった
## 発生した問題
electronアプリを作成し、GitにPushしようとしたとき、node_modulesの中にあったelectorn.exeのサイズが100MBを超えており、pushできなかった。
Gitでpushする際、100MB以上のファイルをアップロードできない。これを解消するため、node_modulesをGitの管理から外し、すべてのcommitから削除する必要があった。
↓と同じ問題。
https://teratail.com/questions/225579
## 解決とその参考サイト
### Gitの管理から外す
.gitignoreを作成し、node_modulesをGitの管理から外す

【参考】
[.gitignoreを後から設定し、git管理から除外する方法](https://qiita.com/yutosa3/items/25ab031c8061e8c9a4c4)
git rm で管理対象から外す。
【参考】
[【Git】git rm --cached [ファイル名]：ファイルを管理対象から除外する](https://qiita.com/megu_ma/items/a438a71c84145f14d04e)

### すべてのcommit履歴から削除
git filter-branchを使用
```
git filter-branch --index-filter "git rm -rf --cached --ignore-unmatch node_modules" --prune-empty -- --all 
```
【参考】
[git最強のオプション filter-branch](https://qiita.com/Spring_MT/items/f60c391b5dbf569a1d12)
[git filter-branchで過去の全てのcommitから画像ファイルの追加/変更をなかったことにしてリポジトリを軽量化する](https://dskd.jp/archives/46.html)

git-rebase 
最初のコミット以外全部消えた！と勘違いしたり（多分rebaseの途中）ととても疲れた。
```
git rebase -i 初期コミットのハッシュ
```
【参考】
[7. rebaseでマージする｜サル先生のGit入門【プロジェクト管理ツールBacklog】](https://backlog.com/ja/git-tutorial/stepup/13/)
[初心者がgit rebase使って格闘した記録](https://qiita.com/mikene_koko/items/d9c8e093b56648e569ad)
[Git 間違って rebase しちゃったのを元に戻したい](https://chaika.hatenablog.com/entry/2021/01/18/140000)
[git のコミット履歴をすべて消す（現時点の状態の1コミットだけにする）](https://qiita.com/okashoi/items/6b1a8ca9a4b001200167)

## 教訓
生成される画像ファイルや、node_modules等バックアップ対象外のファイル・フォルダはあらかじめ.gitignoreで除外する。
Firebase Cloud Messaging (FCM) で iOS アプリに Silent Push できないときに確認すること

# 課題

プッシュ通知において、ユーザーに明示的に通知バナーなどを見せることなく、アプリに特定の処理をさせるための Silent Push (Silent Notification) を実現したい。今回はFCMを利用するが、公式のリファレンス通りではどう頑張ってもうまく行かず時間を溶かしてしまったのでメモ

# 現在地

一般的なバナーありのNotificationは正常に動作しているが、逆立ちしても Silent Push が実現できない

* 環境
    * Swift 5
    * iOS 15（13以降であれば同じ）

# 結論

## ① ペイロードが間違っている

Silent Push を実現するためには、ペイロードに以下が必要。

* `content-available: true`
* ヘッダの設定
    * `apns-push-type: "background"`
    * `apns-priority: "5"`

FIrebase Admin SDK (Node.js) で実装するなら、こんな感じになる

```javascript
const message = {
    apns: {
        headers: {
            "apns-push-type": "background",
            "apns-priority": "5"
        },
        payload: {
            aps: {
                contentAvailable: true
            }
        }
    },
    data: {
        hoge: "fuga"
    },
    token: "トークン"
};

await admin.messaging().send(message)
// エラー処理などは省略
```

## ② Method Swizzling を無効にする

Firebase iOS SDK は、何かと気が利く。デフォルトでは、APNsのトークンを発行通知を受け取るメソッドに介入して、Firebaseへのトークンの登録処理を肩代わりしてくれたりする（[Method Swizzling = 実装入れ替え](https://firebase.google.com/docs/cloud-messaging/ios/client#method_swizzling_in)）のだが、これが厄介者だった！

Silent Push を受け取った際に呼ばれる `func application(_ application: UIApplication, didReceiveRemoteNotification...` が、このMethod Swizzlingの影響を受けて全く呼ばれなくなってしまうのだ。

Silent Push を実現するには、これを無効にする必要がある。詳細は[こちら](https://firebase.google.com/docs/cloud-messaging/ios/client#method_swizzling_in)にある通りなのだが、要は以下のように変更する。

### `Info.plist`に定義を追加
* Key: `FirebaseAppDelegateProxyEnabled`
* Value: `NO`

### APNsトークンのFCMへの登録を自力で行う
```swift
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
    Messaging.messaging().apnsToken = deviceToken
    // 残りの処理...
}
```

これで、 Silent Push によって以下のメソッドが呼ばれるようになった。
```swift
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
    Messaging.messaging().appDidReceiveMessage(userInfo)
    print(userInfo["hoge"]) // -> fuga

    // 処理 (30秒以内)

    completionHandler(.newData) // 処理結果をiOSに報告するのを忘れずに
}
```

めでたしめでたし。

# この解決策の懸念

Method Swizzling は、 Analytics 関連でも様々な気配りをしてくれているようなので、Analyticsによる効果検証を行いたい場合はもう少し深堀りする必要がありそう。
Laravel における Cookie認証 にて Access-Control-Allow-Origin ヘッダが返ってこない原因

## Sanctum(Laravel) における Cookie認証 にて Access-Control-Allow-Origin ヘッダが返ってくる時と返ってこない時があった

API 開発において フロントエンド が バックエンド と Cookie認証 をしている場合、 POST 系のプリフライトリクエストに対して `Access-Control-Allow-Origin` が返ってくる時と返ってこない時があり、少し混乱したので表にまとめます。

## ざっくり Access-Control-Allow-Origin ヘッダが返ってこない条件

- CloudFrontのヘッダポリシーでCORSの設定を行い、オーバーライドしている
    - この場合はCloudFrontでのCORSの設定をオフ or オーバーライドしないようにすれば良い
- Laravel 側の config/cors.php が特定の条件を満たす場合
    - 以下表にまとめる

### フロントからCookieを送らない場合

| レスポンスの Access-Control-Allow-Origin ヘッダ | supports_credentials | allowed_origins  |
| --- | --- | --- |
| `Access-Control-Allow-Origin: *` |  `false`  | `['*']` |
| `Access-Control-Allow-Origin: フロントのオリジン` | `false` | `[フロントのオリジン]` |
| ヘッダ自体返らない |  `false`  | `[当該しないオリジン]` |
| `Access-Control-Allow-Origin: フロントのオリジン` |  `true`  | `['*']` |
| `Access-Control-Allow-Origin: フロントのオリジン` | `true` | `[フロントのオリジン]` |
| ヘッダ自体返らない |  `true`  | `[当該しないオリジン]` |

## まとめ

`allowed_origins` に間違えてオリジンを設定していた場合など、リクエスト元のオリジンが `allowed_origins` に一致するオリジンがないと Access-Control-Allow-Origin ヘッダ自体が返らないようでした。
はじめ、1つのオリジンしか登録していなかったため、ちゃんと設定しているのにもかかわらずヘッダが返らないのは、何かブラウザのデフォルトの仕様なのかな？　と見当違いなところを調べてしまって時間がかかってしまったのでメモします。
ちなみに、初めてこの現象に遭遇した際、この CloudFront の CORS 関連のヘッダをオーバーライドしていたことも相まって、原因が複数存在したのでとても戸惑いました。笑
このメモが誰かの解決の手助けになれば幸いです。
ウェブ小説の取得

## ウェブ小説の形式
ウェブ小説では独自の注記で書かれていてHTMLの形式に変換してサイトで表示してある。なのでサイトでふりがなを取得する場合は、HTML４やHTML５で書かれている。サイトによりHTMLのルビの書き方が違うので、小説を取得する場合は一旦同じ形式に変換しないとバラバラの表記になる。


| |ルビ|傍点|
|:----|:----|:----|
|小説家になろう|（全角または半角）</span>\|</span>漢字《ルビ》|｜漢《・》｜字《・》|
|カクヨム|（全角または半角）｜漢字《ルビ》|《《○○》》|
|青空文庫|（全角縦線）｜漢字《ルビ》|○○［＃「○○」に傍点］|

タイトル、著者名、説明をトップページから取得し各話のリンクから、章と節を取得する。章や節などはページを分割する。

下記内容はカクヨムのサイトをどのようにDOMからパースするか。
https://github.com/kyukyunyorituryo/AozoraEpub3/blob/master/web/kakuyomu.jp/extract.txt
DNSキャッシュポイズニング とは

## 勉強前イメージ

攻撃系のやつ？

## 調査

### DNSキャッシュポイズニング とは

DNSのキャッシュサーバに偽の応答をキャッシュさせることで
そのキャッシュを使ったユーザを偽のサイトに誘導する攻撃手法になります。

基本的なDNSの応答は以下になります。

![12022-08-02 # DNSキャッシュポイズニング とは - diagrams.net - Go.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/129517/a52ddd1a-e6e5-b627-efe4-125f7f010bcc.png)


1. クライアントPCからDNSキャッシュサーバに聞きに行く
2. DNSキャッシュサーバはわからないのでルートのDNSサーバに聞きに行く
3. ルートのDNSサーバはわからないのでcomのDNSサーバを教える
4. comのDNSサーバに聞きに行く
5. comのDNSサーバはわからないのでexampleのDNSサーバを教える
6. exampleのDNSサーバに聞きに行く
7. IPアドレスを教えてもらう
8. DNSキャッシュサーバはクライアントPCにIPアドレスを教える
9. ユーザはX.X.X.Xへアクセスを行う
10. DNSキャッシュサーバは一定期間キャッシュする

しかし、DNSキャッシュポイズニングは上記のDNSのキャッシュを利用して
偽の情報をDNSキャッシュサーバに伝えて一定時間キャッシュさせることで
キャッシュを利用したユーザに偽のサイトへ誘導することができます。

![22022-08-02 # DNSキャッシュポイズニング とは - diagrams.net - Go.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/129517/bc446875-d36d-711e-5ce0-714c8862f57f.png)


1. クライアントPCからDNSキャッシュサーバに聞きに行く
2. DNSキャッシュサーバはわからないのでルートのDNSサーバに聞きに行く
3. ルートのDNSサーバはわからないのでcomのDNSサーバを教える
4. comのDNSサーバに聞きに行く
5. comのDNSサーバはわからないのでexampleのDNSサーバを教える
6. exampleのDNSサーバに聞きに行く
7. IPアドレスを教えてもらう ※ ここで偽サイトのIPを教えられる
8. DNSキャッシュサーバはクライアントPCにIPアドレスを教える
9. ユーザはY.Y.Y.Yへアクセスを行う
10. DNSキャッシュサーバは一定期間キャッシュする ※ ここでDNSキャッシュサーバは偽のIPアドレスをキャッシュしてしまったので他のユーザも一定期間偽サイトにアクセスを行う

※一定期間というのはTTL値によって異なります。

### DNSトンネリング との違い

[DNSトンネリング](https://qiita.com/miyuki_samitani/items/dd5d81cf539467578fd2) とは
DNSの仕組みを利用してサイバー攻撃や情報漏洩に悪用する攻撃ですが、DNSを利用する点は同様です。
DNSトンネリングとの違いは以下になります。

- DNSトンネリング : DNSによってマルウェアなどに感染させること
- DNSキャッシュポイズニング : キャッシュサーバに悪意のある情報を記憶させること

## 勉強後イメージ

DNSってめっちゃ攻撃に使われてる感じがする・・・・
あとトンネリングとキャッシュポイズニング、途中まで一緒だから難しい

## 参考

- [DNSキャッシュポイズニング](https://jprs.jp/glossary/index.php?ID=0171)
- [DNSキャッシュポイズニングとは？対策や最近の状況を解説](https://www.kagoya.jp/howto/engineer/infosecurity/dns-cache-poisoning/)
Mac OSでLimaをOS起動時にstartする

Mac起動時に `limactl start default` する方法のメモ。
以下のファイルを任意の場所に作成し、`chmod +x` しておく。

```bash
#!/bin/bash
limactl start default
```

あとは「システム環境設定」→「ユーバーとグループ」→「ログイン項目」を開いて
上記ファイルを追加する。
dotfilesをFinderで表示するには ⌘ + Shift + `.` でok
Rubyのデバッグを楽にする

### はじめに
プログラムをデバッグするときはデバッガーを使ったり二分探索でコメントアウトしてみたり、ログを出してみたりすると思います。
私はログを出してデバッグするのをよくやるのですが、ただ単に

```ruby
p "value => #{value}"
```
とか
```ruby
p value
```

とかでデバッグしようとしたとき、小さいスクリプトならそれでも問題ないですが、大きいプログラムでやると他にもたくさんログが出ていて出力したものを見失いますよね。

なので自分はよく以下のように書いていました。
```ruby
# こんなのとか
p '++++++++++++++'
p 'value'
P '++++++++++++++'
# こんな感じ
p '--- (~_~) ---'
p 'value'
p '--- (~_~) ---'
```

他の箇所が絶対に出さないようなものを出していました。顔文字は結構おすすめです。デバッグ中のすさんだ心がかわいい顔文字で癒される。
でもこれ毎回毎回やるのは面倒だなと思いました。
最近業務でフロントエンドをよくやっていたのもあってRubyでもconsole.logのようなものを使いたいと思うようになりました。

console.logは行番号も出力してくれて便利だと思いました。

### Console.logを作る

要件はファイル名、行番号が出ること、そして好きなものを出力させられることです。
ドキュメントを調べてみると組み込みライブラリに[Kernelモジュール](https://docs.ruby-lang.org/ja/latest/class/Kernel.html)というものがあり使えそうなメソッドがありましたので、出来上がったものがこちら

console.rb
```ruby
module Console
  def self.parse_caller(at)
    if /^(.+?):(\d+)(?::in `(.*)')?/ =~ at
      file = $1
      line = $2.to_i
      method = $3
      [file, line, method]
    end
  end

  def self.log(text)
    puts parse_caller(caller.first)[0..1].join(':')
    puts text
    puts 
  end
end

Console.log('hoge')

# console.rb:18
# hoge

```

### 説明
```ruby
# callerメソッドはメソッドの中で呼びだすとファイル名、行数、メソッド名を決まった形で返してくれる。
p caller
# ["console.rb:24:in `<main>'"]

# callerの返り値の形式が渡ってきたらparse_callerでファイル名、行数などをパースしている
parse_caller(caller.first)

```


### Active Supportの自動require
いい感じのものができたので実際に使いたいが、毎度毎度ログを出したいだけなのに
```ruby
require './console.rb'
```
とか書きたくない。と思っていたらActive Supportにauto requireというものがあるらしい！便利！

config/application.rbに以下のように書くと
```ruby
config.autoload_paths << Rails.root.join("lib")
```
/lib以下にconsole.rbファイルを配置しておくだけでrequireを毎回しなくてよくなるんだそうな。
あとはconsole.rbをgitignoreするなりして。

## 終わり
結構勉強になった。karnelモジュール面白そうなメソッドたくさんあった。  


参考
https://docs.ruby-lang.org/ja/latest/class/Kernel.html
https://blog.mirakui.com/entry/20100924/active_support
https://www.javadrive.jp/ruby/regex/ini/index4.html


## 追記
使ってみたら全然目立たないのでこうした方がいい
```ruby
  def self.log(text='')
    puts '(~_~)(~_~)(~_~)(~_~)'
    puts parse_caller(caller.first)[0..1].join(':')
    puts text
    puts
    puts '(~_~)(~_~)(~_~)(~_~)' 
  end
```

## 追記
ricecreamめっちゃ便利  
https://github.com/nodai2hITC/ricecream
[How to config Gmail as mail sender] [SMTP config]

**Gmail side (use 2-Step Verification and generate app password) :**
---
*Because Google stopped supporting “Less Secure Apps” as of May 30th, 2022, seems this is a MUST to config this.
1. Google Account -> Security -> 2-Step Verification -> Input password as asked -> Turn ON (you could use SMS to get Gmail code to activate 2-Step Verification)
2. Google Account -> Security -> 2-Step Verification -> App password -> Input password as asked -> Select the app as mail and device... -> e.g. Other(Custom name) -> Input app name e.g. MyApp -> Generate
3. Copy a 16-character password
4. Use a 16-character password with Gmail username (xxx@gmail.com) in Liferay side.

Google support link [here](https://support.google.com/accounts/answer/185833?hl=en).

**Liferay side:**
---
1. Control Panel -> Server Administration -> Mail
 1.1 Outgoing SMTP Server: smtp.gmail.com
 1.2 Outgoing Port: 465
 1.3 Use a Secure Network Connection: checked
 1.4  User Name: xxx@gmail.com
 1.5 Password: [Generated by Gmail side]
1. Control Panel -> Instance Settings -> Email -> Email Sender
1. Send as xxx@gmail.com
ざっくりわかる Sprocketsとは

# 何を実現してくれるか

sprocketsはapp/assets 配下にある jsファイルとcssファイルをそれぞれ 1 つのファイルにまとめる。JavaScript と CSS をまとめる理由は、Rails アプリケーションにブラウザでアクセスした時にサーバへのアクセス回数を減らすため。

# 関連する用語

### アセットパイプライン

Sprocketsがcssファイルとjavascriptファイルをまとめるまでの工程はアセットパイプラインと呼ばれている。

### コンパイル

特定のプログラミング言語を用いて記述されたコンピュータープログラムを他の言語 を用いて記述された同じプログラムに形を変えることである。Sprocketsではアセットパイプラインの処理を実行することをコンパイルという。

### ディレクティブ

ディレクティブとはSprocketsで外部ファイルを読み込むための記述のことである。
ここでいう外部ファイルとはjsの場合`app/assets/javascripts`配下、cssの場合`app/assets/stylesheets`配下にあるファイルのことである。

以下の例でははapplication.jsにtop.jsという別のjsファイルを読み込んでいる。（以下のようにコメント化して書く必要がある。）

```jsx
//= require top
```
Sprocketsで外部のjs・cssファイルを読み込むときにはこのディレクティブを使う。

## 利用法

Sprocketsがコンパイルしたスタイルシートをerbで読み込むには`include_tag`というヘルパーメソッドをerbファイルに記載すれば良い。このヘルパーメソッドは`sprockets-rails`というgemに記載されている。

```html
application.cssを読み込む場合
<%= stylesheet_link_tag 'application' %>

application.jsを読み込む場合
<%= javascript_include_tag 'application' %>
```

よくapplication.html.erbで見るやつ。

### 参考にした記事

[Ruby on Rails で sprockets から Webpacker へ移行し、移行できないものは共存させる方法 - Qiita](https://qiita.com/tatsurou313/items/645cbf0a3af4c673b5df)
ライブラリのインポートから前処理

# １.ライブラリのインポート
```Python
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
```
# ２.csvファイルのインポート
`pd.read_csv(csvファイル名)`　
　csvファイルをインポート

`.iloc[:,:-1]`　
目的変数と説明変数に分ける。あらかじめ末尾に目的変数を持ってくると汎用性高い。
```Python
# csvファイルのインポート
dataset = pd.read_csv('hoge.csv')

#目的変数と説明変数に分ける
X = dataset.iloc[:, :-1].values
y = dataset.iloc[:, -1].values
```

# ３.欠損値処理
**scikit-learn** の **SimpleImputer** を用いる。
`[1:4] (1〜3列目)` の`null` を `mean(平均値)` で埋める。
`.fit()`で内部で演算する。
`.transform()`で実際に変換してくれる。

```Python
from sklearn.impute import SimpleImputer
imputer = SimpleImputer(missing_values=np.nan, strategy='mean')
imputer.fit(X[:, 1:4])
X[:, 1:4] = imputer.transform(X[: ,1:4])
```

# ４.カテゴリ変数処理

列を***transform***すると同時に、***OneHotEncoding***を行う。
scikit-learnは本当に便利。

```Python
from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import OneHotEncoder
ct = ColumnTransformer(transformers=[('encoder', OneHotEncoder(), [0])], remainder='passthrough')
X = np.array(ct.fit_transform(X))
```

***OneHotEncoding***のように列を増やさず、
`はい/いいえ` とか `True/False`とか`生/死`とかを`0/1`で表現するには以下。
```Python
from sklearn.preprocessing import LabelEncoder
le = LabelEncoder()
y = le.fit_transform(y)
```
# ５.trainデータとtestデータに分ける
`random_state` を1にしない（0にする）と毎回分けられるデータが違ってしまう。
```Python
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.3, random_state = 1)
```
# ６.フィーチャースケーリング
***敷地面積*** と ***住宅価格*** のように単位が異なる（数字の大きさが異なる）変数の重みを
`StandardScaler()`で同じにするイメージ。

```Python
from sklearn.preprocessing import StandardScaler
sc = StandardScaler()
X_train[:, 4:] = sc.fit_transform(X_train[:, 4:])
X_test[:, 4:] = sc.transform(X_test[:, 4:])
```
【yocto】レシピが上書きされるかを確認する

# はじめに
- レシピに記述したとおりになっていない場合、bbappendでレシピ内容が上書きされている可能性がある。(他の可能性もあるが…)
- そのようなときは、bbappend一覧を表示するコマンドがあるので、意図せずbbappendが当たっていないかを確かめることができる。

# コマンド
~~~
bitbake-layers show-appends
~~~

# 例
- 環境変数設定
~~~
takeshi@X250:~/yocto/poky$ source oe-init-build-env 

### Shell environment set up for builds. ###

You can now run 'bitbake <target>'

Common targets are:
    core-image-minimal
    core-image-sato
    meta-toolchain
    meta-ide-support

You can also run generated qemu images with a command like 'runqemu qemux86'

Other commonly useful commands are:
 - 'devtool' and 'recipetool' handle common recipe tasks
 - 'bitbake-layers' handles common layer tasks
 - 'oe-pkgdata-util' handles common target package tasks
~~~
− bbappend一覧を確認(bbとbbappendを対で表示してくれる)
~~~
takeshi@X250:~/yocto/poky/build$ bitbake-layers show-appends
NOTE: Starting bitbake server...
WARNING: Host distribution "zorin-15" has not been validated with this version of the build system; you may possibly experience unexpected failures. It is recommended that you use a tested distribution.
Loading cache: 100% |####################################################################################################################################| Time: 0:00:00
Loaded 1298 entries from dependency cache.

Summary: There was 1 WARNING message shown.
=== Appended recipes ===
busybox_1.31.0.bb:
  /home/takeshi/yocto/poky/meta-poky/recipes-core/busybox/busybox_%.bbappend
formfactor_0.0.bb:
  /home/takeshi/yocto/poky/meta-yocto-bsp/recipes-bsp/formfactor/formfactor_0.0.bbappend
linux-yocto_4.19.bb:
  /home/takeshi/yocto/poky/meta-yocto-bsp/recipes-kernel/linux/linux-yocto_4.19.bbappend
linux-yocto_5.2.bb:
  /home/takeshi/yocto/poky/meta-yocto-bsp/recipes-kernel/linux/linux-yocto_5.2.bbappend
psplash_git.bb:
  /home/takeshi/yocto/poky/meta-poky/recipes-core/psplash/psplash_git.bbappend
xserver-xf86-config_0.1.bb:
  /home/takeshi/yocto/poky/meta-yocto-bsp/recipes-graphics/xorg-xserver/xserver-xf86-config_0.1.bbappend
linux-yocto-dev.bb (skipped):
  /home/takeshi/yocto/poky/meta-yocto-bsp/recipes-kernel/linux/linux-yocto-dev.bbappend
~~~
rails aborted! TZInfo::DataSourceNotFound: tzinfo-data is not present. Please add gem 'tzinfo-data' to your Gemfile and run bundle install C:/Users/ml/rails_tutorial_2/hello_app/config/environment.rb:5:in 

# 概要
Windowsでrailsの環境構築をしていて、rails new したらでてきたエラーです。

**環境**
windows10
Rails 6.0.5.1
ruby 3.1.2p20


```shell
C:\Users\ml\rails_tutorial_2>rails _6.0.4_ new hello_app
       exist
   identical  README.md
   identical  Rakefile
   identical  .ruby-version
   identical  config.ru
    conflict  .gitignore
Overwrite C:/Users/ml/rails_tutorial_2/hello_app/.gitignore? (enter "h" for help) [Ynaqdhm] Y
       force  .gitignore
   identical  Gemfile
         run  git init from "."
Reinitialized existing Git repository in C:/Users/ml/rails_tutorial_2/hello_app/.git/
   identical  package.json
       exist  app
 (中略)
Bundle complete! 14 Gemfile dependencies, 70 gems now installed.
Use `bundle info [gemname]` to see where a bundled gem is installed.
         run  bundle binstubs bundler
Skipped bundle since it already exists.
If you want to overwrite skipped stubs, use --force.
       rails  webpacker:install
Rails couldn't infer whether you are using multiple databases from your database.yml and can't generate the tasks for the non-primary databases. If you'd like to use this feature, please simplify your ERB.
Rails couldn't infer whether you are using multiple databases from your database.yml and can't generate the tasks for the non-primary databases. If you'd like to use this feature, please simplify your ERB.
rails aborted!
TZInfo::DataSourceNotFound: tzinfo-data is not present. Please add gem 'tzinfo-data' to your Gemfile and run bundle install
C:/Users/ml/rails_tutorial_2/hello_app/config/environment.rb:5:in `<main>'
./bin/rails:4:in `<main>'

Caused by:
TZInfo::ZoneinfoDirectoryNotFound: None of the paths included in TZInfo::ZoneinfoDataSource.search_path are valid zoneinfo directories.
C:/Users/ml/rails_tutorial_2/hello_app/config/environment.rb:5:in `<main>'
./bin/rails:4:in `<main>'
Tasks: TOP => app:template => environment
(See full trace by running task with --trace)
```
# 解決方法

## 手順１
まず、Gmfileの　```tzinfo-data```　のプラットフォームの指定を以下のように削ります。
```ruby
# 変更前
- gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
# 変更後
+ gem 'tzinfo-data'
```
## 手順２
bundle installを実行します。
bundle install が失敗する場合、gemfile.lockを一度削除してから再度bunde install してください。

## 手順３
rails new

## 余談
このプラットフォームの指定だとucrt64版が対象にならないため、tzinfo-dataが読み込まれません。それに起因して起きたエラーのようです。詳しい情報を知りたい方は参考記事をご覧ください。

# 参考記事
大変お世話になりました。
https://ja.stackoverflow.com/questions/86685/rails-new-%e3%82%92%e5%ae%9f%e8%a1%8c%e3%81%97%e3%81%9f%e9%9a%9b%e3%81%ab-rails-aborted-%e3%81%a8%e3%81%84%e3%81%86%e3%82%a8%e3%83%a9%e3%83%bc%e3%81%8c%e5%87%ba%e3%81%a6%e8%a7%a3%e6%b1%ba%e3%81%a7%e3%81%8d%e3%81%be%e3%81%9b%e3%82%93
【プログラミング学習検討者必見！】文系人間がプログラミングを学び始めた理由

プログラミングの超初心者ながらQiitaを書き始めました（笑）

今回のテーマは、「文系人間がプログラミングを学び始めた理由」についてです。
タイトル通り、私は、大学では文系の学部に所属しながらも、現在はほぼ独学でプログラミングを学んでいます。
勉強する中でつまづくことも多く大変ですが（笑）

それでは、なぜ私がプログラミングを勉強しようと思ったのかについて迫っていきましょう！

# **筆者のプロフィール**
- 地方の文系学部に通っている大学生
- 大学は、外国語・国際系の学部
- 大学に入学するまでプログラミングをした経験は無し
- プログラミング学習歴は4か月（2022年8月現在）

# プログラミングの技術レベル
こんなことを書いておきながらも、超初心者なので出来ることも限られていますが…
HTMLとCSSを使ってホームページを制作出来るくらいのレベルです…（笑）
JavaScriptは完全に身についていない状態なので、習得出来たとは言えない感じです。（現在学習中）
これからも精進して参ります！

# 習得予定のプログラミング言語
①JavaScript
②TypeScript
③Node.js

年内はこれらの言語を習得出来るように頑張る予定です。
また、プログラミング言語学習をする上で何らかのツールを利用するのも良いと思うので、CourseraやUdemyも使って勉強していこうと考えています。

# プログラミングを学び始めた理由
やっと本題に入りますが…（笑）

私は、今までにプログラミングを勉強したことは無かったものの、パソコンや電子機器を触ることが好きなタイプの人でした。

例えば、小学校低学年の頃、慣れない手つきでパソコンの操作をしながらオンラインでオセロゲームをしたり、卓球ゲームをしたり（笑）

プログラミングの知識はゼロでありながらも、パソコンに触れることが好きな子どもでした。

中学校に進学した頃、当時仲の良かったクラスメートがプログラミングを出来る部活に入部しており、そこでの活動の話を聞いてプログラミングの存在を知りました。その当時はプログラミングに関心が無かったので、特に何も思っていなかったのですが（笑）

高校生になって、学校で情報の授業が始まってから絵を描けるソフトであるGIMPを使ったりするなどしてパソコンに触れる機会がグッと増えました。

そこで、やはりパソコンを使って何か作品を作ることは楽しいなと強く思うようになりました。中学生の時のクラスメートが話してくれていたこともあってか、そのことも相まってプログラミングを勉強したいと思い始めました。

そこから、まず簡単なホームページを作れるレベルに到達できるようにしようと考えるようになりました。

ここまでが私の、「プログラミングを学び始めた理由」についてです。

# さいごに
初投稿の記事なので、内容に関して至らぬ点が多々あるかと思いますが、とりあえずは書くことに慣れていこうかと考えております。

これからは、自分が習得した知識や情報を皆さまにも提供出来る形で記事を作っていく予定です。

お読みいただき、ありがとうございました！
Vue.jsの学習をしていく上で構成図を自分なりに理解してみる。

# Vue.jsのコンポーネントを理解するために構成図を作成

これまでバックエンドをメインにしていたのですが、フロントエンドも両方とも理解したくなってきたのでVue.jsのコンポーネントを理解するために構成図を作ってみました。

正直概念は合っているかどうかわかりませんが、、

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542047/ac217338-7d5e-c0f6-f6fa-c943d37b6ac5.png)
[Android]デバイス上で表示されているViewのサイズを取得する

# はじめに
各デバイスサイズ毎に表示されているViewの大きさを取得して比較したかった際にかなり躓きました。
やりたかったこととしてはデバイス毎にScrollViewの全体のサイズと表示されている領域の高さの差分を取得してスクロールできる領域があるかどうかを確認するといった内容です（もっとスマートなやり方があれば知りたい......）

ViewのIDを何度も確認したりDataBinding部分やgetHeightの中身を読んでも解決せず......
何度かデバッグモードで動かしているうちに見当がついて調べたところ解決しました。

# 課題
自身の環境ではレイアウトはxmlでConstraintLayoutを使用して記述しており、DataBindingでFragmentから参照しています。
そこでViewのサイズを下記コードで取得しようとしたところ0で返ってきてしまいました。
```kotlin
binding.viewId.height
```

不思議に思い一度デバッグで確認していたところViewが生成されるより前に取得を試みているようです。


# 解決方法
viewTreeObserverの中であれば取得可能です。
```kotlin
binding.viewId.viewTreeObserver.addOnGlobalLayoutListener {
    binding.viewId.heightbinding.viewId.height
}
```

自分はScrollViewの高さを知りたかったのでaddOnScrollChangedListenerで取得しましたがviewTreeObserverの中であれば良いようです。
viewが生成されて高さや幅を持つタイミングがviewTreeObserverのようです。



ミスによりadapterやlifeCycleOwnewなど生成される前に参照してエラーが起こることはありますが今回はエラーでもNullでもなく値が０で返ってくる分少し悩みました......
Reduxについてアウトプット

### Frontendの開発で、Reactを初めて利用しています。<br>その中で、よく分かってなかった、reduxについて改めて勉強し直したので、そのアウトプットです。<br>もし間違った記述などあったら、コメントよろしくお願いします。

## 【Reduxとは】
まず、Reduxについてですが、画面上の状態を管理するフレームワークとなります。<br>状態とは、簡単に以下の様な感じになります。
・今、画面上に、どんな値を表示させているのか？
・今、どんな画面が表示されているのか？

こんな状態を、Reduxで保持してやり、必要に応じて、サーバサイドへデータを送付したり、再表示してくれます。
因みに、Reduxはフレームワークとなるので、Reactだけでなく、Angularでも利用可能となります。

## 【Reduxの種類】
Reduxには、以下の5種類の処理が存在します。<br>※簡単な説明も追記しておきます。
種類|概要
--|--
Action|操作された際にどんな動作を行うのか記載する。
Action Creater|画面上で何か操作された際、Actionを作成してくれる。
Store|アプリケーションの状態を保持する。
State|アプリケーションの状態
Reducer|渡されたActionとStateから新しいStateを作成し返す。

### 【Reduxの大まかな流れ】

図にするとこんな感じとなります。下手な絵ですみません。。。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/196226/e84c9d38-bc07-d55d-220e-4945aef7cc63.png)

因みに、図のState1とState2は別物となります。
Reduxでは、State（状態）は、上書きせず、新たに作成するようになります。


## 【実装の流れ】
以下の流れで、実装を行っていきます。
①Stateの作成
②Stateの初期化
③Actionの作成
④Reducerの作成
⑤Reactへ反映

### ①Stateの作成
アプリ内で、現状保持しているデータが何か？を格納するための箱のようなもので、MVCモデルで言うとこの、Modelとなります。
```authTypes.ts
/* ログインする際のユーザIDとPWを格納するためのデータ型 */
export interface LOGIN_CREDENTIAL{
    usercd: string;
    password: string;
}
```

### ②Stateの初期化
当アプリの画面に遷移した際に、初期値に何を挿入しておくか？を決めます。
```authSlice.ts
const initialState: LOGIN_CREDENTIAL = {
    usercd: "",
    password:""
}
```

### ③Actionの作成
対象画面上で、何かイベントが発生した際に、どのような動作を行うか？を記載します。
```authSlice.ts
/* 認証のための非同期関数(Action) */
export const fetchAsyncLogin = createAsyncThunk(
    "auth/login",
    async (auth: LOGIN_CREDENTIAL) => {
        const res = await axios.post<JWT_ACCESS>(
            `${process.env.APP_API_URL}/api/auth/token`,
            auth,
            {
                headers: {
                    "Content-Type": "multipart/form-data",
                },
            }
        );
        
        return res.data;
    }
);
```

### ④Reducerの作成
対象画面上で、イベントが発生し、Actionが作成された際、新しいState(状態)を作成してくれる処理となります。<br>MVCモデルで言うとこの、Controllerとなります。
```authSlice.ts
//Reducerの作成
export const authSlice = createSlice({
    name:"auth",
    initialState,
    reducers: {
        //Stateの更新
        init_Credential(state) {
            state.usercd = "";
            state.password = "";
        }
    },
    extraReducers: (builder) => {
        //fetchAsyncLoginが成功した場合の処理
        builder.addCase(
            fetchAsyncLogin.fulfilled,
            (state, action: PayloadAction<JWT_ACCESS>) => {
                //cookieにサーバサイドから取得したJWTを保存する。
                setCookie("accesstoken", action.payload);
                action.payload.accesstoken && (window.location.href="/tasks")
            }
        );
    }
})
```

以上の内容が、AuthSlice.ts内に記載する内容となります。<br>Reduxをアプリ上で利用するための下準備のような感じです。

### ⑤Reactへ反映
そして、ここからが（と言っても1つだけですが。。。）Reactアプリで、Reduxを利用するための処理となります。
Reduxを作成しただけでは、イベントが発生しても、Reduxを呼び出すことができません。Reactとreduxを紐づけることで、初めて状態管理が出来るようになります。

```Auth_login.tsx
    const dispatch = useDispatch();
    const login = async () => {
        await dispatch(fetchAsyncLogin(credential));
    }
```


## 参考にしたサイト
https://qiita.com/kitagawamac/items/49a1f03445b19cf407b7

今回は、ここまでとなります。
docker-compose up -dが出来なくて詰まった話

## はじめに
最近学習しているKotlinの書籍の中でDockerを使用するため、学習メモとして残しております。
Dockerに対しての知識は浅いので、認識が間違っている点があったらご指摘いただけると嬉しいです。

## 今回の事象
docker-compose.ymlファイルを作成したのち、下記コマンドを実行するとエラーが発生する。

```
MacBook-Pro docker % sudo docker-compose up -d
[+] Running 0/0
 ⠋ Container mysql_host  Starting                                                                                                                                                                      0.1s
Error response from daemon: network docker_default not found
```

## 解決方法
「docker_default」と言う名前のネットワークが存在しないため発生したエラー。
そのため、下記コマンドでネットワークを作成後、再度コマンドを実行することで成功しました。

```
-- ネットワークの作成
MacBook-Pro docker % docker network create --driver bridge docker_default
920b19dd460fc74d656df6c42243494735c1fdff0d116232b350f1513c3de732

--  
MacBook-Pro docker % sudo docker-compose up -d                                                 
[+] Running 1/1
 ⠿ Container mysql_host  Started
```

## 補足
### Docker Composeとは
構築に関わるコマンドの内容を1つのテキストファイルとしてまとめ、一気に実行したり、停止・破棄することができる。
参考として今回使用したdocker-compose.ymlファイルを記載します。
ちなみにcommandの横にある「mysqld」の「d」の記載漏れでエラーになり、なかなか気づかず奮闘したので今後気をつけよう…。

```
version: '3'
services:
  # MySQL
  db:
    image: mysql:8.0.30
    ports:
      - "3306:3306"
    container_name: mysql_host
    environment:
      MYSQL_ROOT_PASSWORD: mysql
    command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
    volumes:
      - ./db/data:/var/lib/mysql
      - ./db/my.cnf:/etc/mysql/conf.d/my.cnf
      - ./db/sql:/docker-entrypoint-init-initdb.d
```

## さいごに
今回の場合はエラーメッセージをしっかり読めばすぐに解決したことだなと反省しました。英語が苦手で、英語で書かれたサイトは今まであまり見ていなかったけど、google翻訳が思っていたより有能で、これからは逃げずにしっかり確認してみようと思いました。

## 参考資料
・Kotlin サーバーサイドプログラミング実践開発