OmniAICreator/Qiita-1.07M huggingface dataset:
GitHub APIを使ってPRのCreateからMergeまでの時間を取得する

### やりたい事
レビュー待ちで、作業が滞ることがあり、改善の第一歩としてPRの時間を見える化する。
見える化の為に、PRのCreateからMergeまでの時間を取得するツールを作成する。

### 制限事項
- 最終的には、期間を指定して、その期間内の平均値を出すようにするが、この記事ではPR指定で時間を取得している。
- 取得する期間は営業日を考慮したものが好ましいが、この記事では考慮していない。

### ツール
下記のGitHub APIを使ってPRの情報を取得する。

```
https://api.github.com/repos/{owner}/{repo}/pulls/{pull_number}
```

上記のレスポンスには、PRの作成された日時とマージされた日時が含まれるため、その差を計算する事で、時間を取得しています。

また、プライベートリポジトリにも対応できるように、Personal access tokensも設定できるようにしました。
ツールについては、昔少しかじったPythonで作成してみました。

```python:GetElapsedTimeOfPr.py
import urllib.request
import json
import ssl
import datetime
import sys

argvs = sys.argv
url = argvs[1]
if len(argvs) > 2:
  token = argvs[2]
else:
  token = ''

ssl._create_default_https_context = ssl._create_unverified_context
req = urllib.request.Request(url)
if len(token) > 0:
  req.add_header('Authorization', 'token {}'.format(token))

with urllib.request.urlopen(req) as res:
  res_str = res.read().decode('utf-8')

json_str = json.loads(res_str)
KEY_TITLE = 'title'
KEY_CREATED_AT = 'created_at'
KEY_MERGED_AT = 'merged_at'
print('title:{}'.format(json_str[KEY_TITLE]))
print('created_at:{} '.format(json_str[KEY_CREATED_AT]))
print('merged_at:{}'.format(json_str[KEY_MERGED_AT]))

TIME_FORMAT = '%Y-%m-%dT%H:%M:%SZ'
created_dt = datetime.datetime.strptime(json_str[KEY_CREATED_AT], TIME_FORMAT)
merged_dt = datetime.datetime.strptime(json_str[KEY_MERGED_AT], TIME_FORMAT)
elapsed_time = merged_dt - created_dt
print('Elapsed time:{}h'.format(elapsed_time.total_seconds() // 3600))
```

#### 使い方

```
python3 GetElapsedTimeOfPr.py https://api.github.com/repos/{owner}/{repo}/pulls/{pull_number} {token}
```

- 第一引数：取得対象のURL
     {owner}:GitHubのアカウント名
     {repo}:リポジトリ名
     {pull_number}:PR番号
     参考：https://docs.github.com/ja/rest/reference/pulls#get-a-pull-request

- 第二引数：トークン
     {token}:プライベートリポジトリの場合はトークンを設定してください。
     参考：https://docs.github.com/ja/github/authenticating-to-github/keeping-your-account-and-data-secure/creating-a-personal-access-token

### 参考
https://docs.github.com/ja/rest/reference/pulls#get-a-pull-request

https://docs.github.com/ja/github/authenticating-to-github/keeping-your-account-and-data-secure/creating-a-personal-access-token

https://github.com/yoshihiro-kato/Sample/blob/ad35dff4a2cef7049671912495ebb31aed7e92e8/python/GetElapsedTimeOfPr.py
Azure Logic Apps で決まった時間のみ特定の処理を行いたい

#やりたいこと
Azure Logic Apps上で決まった時間のみ特定の処理を行いたい

#前提条件
* 起動トリガーは外部から一定間隔で呼ばれます
* また、外部から一定間隔で呼ばれる起動トリガーは容易に変更できないものとします

#全体概要
以下のような流れになります。
1. 現在の時刻を取得
1. タイムゾーンを変換＋出力フォーマットを指定
1. 指定されたフォーマットで分岐処理
1. （今回は、特定の時間にメールを送ります）

※具体的には以下。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/475457/97ce7cc7-daa3-6c6d-313f-89eb1fb30ce2.png)

#詳細な処理
1. 現在の時刻を取得
そのまま使用します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/475457/9bbacff9-6766-bac3-3957-865335971814.png)

1. タイムゾーンを変換＋出力フォーマットを指定
タイムゾーンを日本に変換します。あとは、出力フォーマットで、「HH:mm」を指定します。こうすることで、変換後のフォーマットは、例えば「"12:01"」のようになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/475457/a978e9be-67f4-b29b-a955-0541508597c8.png)

1. 指定されたフォーマットで分岐処理
条件を設定します。今回は、「12:00～12:02」の間に起動される処理のみ特定の処理を行います。注意点としては、比較する値は「"12:00"」のように文字列として明示的に指定しましょう。一度保存すると、表記が自動的に「12:00」のように変換されますが、自分で設定するときは明示的に文字列として入力する必要がありました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/475457/2558f575-58e9-8b02-a32f-deb614775f3e.png)

1. （今回は、特定の時間にメールを送ります）
ここは実現したい処理に依存しますが、上記の条件でTrue/Falseで処理を切り替えることが可能です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/475457/3bda47b6-3016-67f8-f56a-4fed489bac22.png)

#まとめ
外部から呼ばれる起動トリガーを容易に変更できない場合や、ローコードで実装している処理部で処理を完結する必要がある場合などで、時間ごとに処理を切り分ける場合などにも使用できます。
ipadで個人サーバー内のjupyter-notebookに接続する [Juno connect設定方法]

ipadで個人サーバー内のjupyter-notebookに接続する方法をここで紹介します。

サーバーに公開鍵認証方式で接続する際の設定が面倒だったので、ここで紹介できればと思います。
これができれば、外出先などでjupyter-notebookをいじる必要ができた場合などに非常に便利です。
※サーバーに接続できる端末は紛失しないようにしましょう。

1. 公開鍵の作成
Termius をダウンロード、generating keysで公開鍵、秘密鍵の作成（RSA, 2048）
(「編集する」マークを押すと秘密鍵が見える)
1. 公開鍵をサーバーに登録
1. 秘密鍵をコピーし、クリップボードにある状態にする。
1. 次にJuno connectで鍵の登録を選ぶ
1. ここでクリップボードからを選択
1. 鍵の作成の際に入力したパスワードを入力する
1. 接続先の情報を入力する
適当なファイルを塩基配列に変換してみるよ。DNAの塩基配列は2bitだから。

## はじめに

**この記事は遊びです。**
念のため書いておきますが、まじめな情報は期待しないでください。

## すべてのファイルを塩基配列に変換することを考えよう

DNAは4つの塩基（A、C、G、T）から成り立っているので、2bitだと考えることができます。
そう考えると、たぶんすべてのファイルを簡単に塩基配列に変換することができます。なのでCrystalでやってみました。


## binary → 塩基配列

適当なCrystalスクリプトでバイナリーストリングを2bitずつ取り出して、DNA塩基配列に変換するプログラムを記述します。

```crystal:b2dna.cr
BIT2BASE = {
  0 => { 0 => 'A', 1 => 'C' },
  1 => { 0 => 'G', 1 => 'T' }
}

while b = ARGF.read_byte
  4.times do |i|
    x1 = b.bit(i * 2)
    x2 = b.bit(i * 2 + 1)
    print BIT2BASE[x1][x2]
  end
end
```

## 塩基配列 → binary

逆に、塩基配列をバイナリ文字列に変換するCrystalのスクリプトも作ります。Crystal言語になれていないので、実は書くのにかなり時間がかかりました。

```crystal:dna2b.cr
require "bit_array"

BASE2BIT = {
  'A' => [false, false],
  'C' => [false, true],
  'G' => [true,  false],
  'T' => [true,  true]
}

b_array = [] of Bool

while c = ARGF.read_char
  b_array.concat BASE2BIT[c]
end

ba = BitArray.new(b_array.size)
b_array.each_with_index do |b, i|
  ba[i] = b
end

STDOUT.write(ba.to_slice)
```

…かなり効率の悪い書き方をしている可能性があります。（一回Bool Arrayを作って、それをBitArrayに移し替えて、最終的にSliceにして、バイナリをファイルに書き込むということをやっています。Rubyにしておけばよかった、とか思いましたがなんとか最後まで書きました。）多分一回全部メモリにデータを乗せるので、大きなサイズのファイルを変換するのには向いていないかもしれません。もっとよいやり方ができる方はコメント欄で教えてください。

さて、これをビルドします。

```
crystal build --release b2dna.cr
crystal build --release dna2b.cr
```

Crystal言語の素晴らしいところは、実行時間が非常に速いことです。[Heng Li氏もCrystalは速い](https://lh3.github.io/2020/05/17/fast-high-level-programming-languages)と言ってます。
しかし、`--release` をつけるとコンパイルにはかなり時間がかかります。

動作確認をします。

```
echo "吾輩はネコである。名前はまだない。" | ./b2dna
```

GGCTAAGCCTTCACCTATTCGCCCTACTGAACTTCCTACTTAACGTACTACTCAACTATCTACTGAACTGCCTACTGAACCAACTACTCAACTCACTACTAAACCAACGGCTAAGCGTACGGCTGCACGTACTACTGAACTTCCTACTGAACCTTCTACTGAACAACCTACTGAACCCCCTACTGAACAGACTACTAAACCAACCCAA

塩基配列が表示されました。

```
echo "吾輩はネコである。名前はまだない。" | ./b2dna | ./dna2b
```

とすると、

吾輩はネコである。名前はまだない。

と表示されます。

とりあえず私のPC上では、画像ファイルや動画ファイルなんかも、塩基配列に変換が成功するようです。（ひょっとしたら多少不具合の出るファイルがあるかもしれません。その場合はコメント欄で教えてください。）

```
cat 変換したいファイル | ./b2dna > hoge.seq
```

でOK。

## いや、ちょっと待てよ、この記事は何の役に立つんだよ？

さあ？　あまり役には立たないのではないでしょうか。

まあ面白いからいいじゃないですか。


この記事は以上です。
Sequel Ace SSH接続エラー[ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!  ]

こんなエラーが出てくる

```
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that a host key has just been changed.
The fingerprint for the RSA key sent by the remote host is
...
```

```
誰かが悪さをしている可能性があります。
誰かが今、あなたを盗み聞きしているかもしれません（中間者攻撃）。
また、ホストの鍵が変更されただけという可能性もあります。
リモートホストから送信された ECDSA 鍵のフィンガープリントは次のとおりです。
-------です。
システム管理者にお問い合わせください。
/Users/akidon/Library/Containers/com.sequel-ace.sequel-ace/Data/.keys/ssh_known_hosts_strictに正しいホストキーを追加すると、このメッセージが表示されなくなります。
```

とあったので変更

現在の正しいホストキーは

.ssh/known_hostsに追加済だと思うので
それを
/Users/akidon/Library/Containers/com.sequel-ace.sequel-ace/Data/.keys/ssh_known_hosts_strict
に追加して完了

このエラーで丸1日間作業が止まりました
ABEMA番組表を改善するユーザースクリプト


## はじめに

ABEMAの番組表を改善するユーザースクリプトです。

主な機能として、アニメの一括放送などで縦長になった番組で上部に隠れる番組タイトルを表示させます。

![abema_timetable_kaizen](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/311399/2eb599f9-6fe8-56a1-5be4-179392c03c58.png)

Tampermonkey (Chrome)などの拡張機能をインストールした後、以下のリンクをクリックすると追加できます。

https://raw.githubusercontent.com/querykuma/AbemaTimetableKaizen/main/Abema_timetable_kaizen.user.js

## その他の改善

現在時刻のバーを常に表示させ、アニメ番組までスクロールさせ、モーダルウィンドウをクリックして閉じます。

## 解説

ABEMAといえばアニメの一挙放送です。スクリーンショットの例で言えば、呪術廻戦の一挙放送は５時間半の長時間で、番組欄が縦長になります。

番組タイトルが上部バーの上にあって表示されないので、このユーザースクリプトは、番組タイトルを上部バーの下に移動させて表示させます。

このユーザースクリプトは、現在時刻のバーが時間とともに消えないようにすることで、現在時刻のアニメ番組を確認しやすくします。

このユーザースクリプトは、アニメ番組までスクロールさせることで、アニメ番組を確認しやすくします。
Googleスプレットシート 選択セルの下に行を追加するショートカット

# 目的

- Googleスプレットシートにて選択しているセルの下に行を追加するショートカットをメモ的にまとめる

# 情報

- 筆者のPCのOSはMacである。

# 方法

1. 下記キーを押下してメニューを開く
    - `control` + `option` + `i`
1. 下記キーを押下してセルの下に行を追加する。
    - `b`
Rails API + MYSQLでherokuにデプロイする流れ

RailsのAPIとMySQLで作ったアプリを初めてherokuにデプロイする過程でハマったところがあるので、記録として書いておこうと思います。初心者なので至らぬ箇所もあるかとは思いますがご容赦下さい。

### 環境
・Windows10
・Ruby 2.6.6
・Rails 6.1.3
・MySQL2 0.5
・heroku

### 前提
* herokuアカウント登録済
* heroku CLIインストール済（[インストールはこちらから](https://devcenter.heroku.com/ja/articles/heroku-cli)）
* herokuクレジットカード登録済み（clearDB使うため。無料）

# デプロイするソースをコミット
```
# デプロイするアプリのディレクトリに移動
$ cd ~
# 初期化
$ git init
# ディレクトリ以下のファイルを選択
$ git add --all
# コミット
$ git commit -m "first commit"
```
# herokuにログイン、新規app作成
下記コマンドでブラウザからログインします

```
$ heroku login
```
デプロイするアプリのディレクトリでapp作成します。

```
$ heroku app:create <appの名前>
```
これによりアプリの公開URLが`https://(appの名前).herokuapp.com`となります。
ちなみにアンダーバーが使えないようです。

# リモートリポジトリの確認
```
$ git remote -v
```
* リモートリポジトリ`heroku https://git.heroku.com/(appの名前).git`が作成されているのを確認
* git initでローカルリポジトリを作成してからheroku createしたので、リモートリポジトリは自動で追加されます

# DBの設定
herokuのDBはデフォルトでPostgreSQLなので、MySQLを使う場合手動で設定する必要があります
下記コマンドで作成

```
$ heroku addons:create cleardb:ignite
```
* ignite(無料プラン)でherokuでMySQLが使えるcleardbを利用
* 本人確認のためにクレジットカードをherokuに登録する必要があります。
* https://heroku.com/verify から登録できます。
※こちらが英語での住所変換に便利でした。http://judress.tsukuenoue.com/

# DBの環境設定
cleardbのURLを確認します。

```
$ heroku config
```
次が確認できます。

```
CLEARDB_DATABASE_URL: mysql://<ユーザー名>:<パスワード>@<ホスト名>/<データベース名>?reconnect=true
```
このURLの情報をもとに各変数を設定します。

```
$ heroku config:add DB_NAME='<データベース名>'
$ heroku config:add DB_USERNAME='<ユーザー名>'
$ heroku config:add DB_PASSWORD='<パスワード>'
$ heroku config:add DB_HOSTNAME='<ホスト名>'
$ heroku config:add DB_PORT='3306'
$ heroku config:add DATABASE_URL='mysql2://<ユーザー名>:<パスワード>@<ホスト名>/<データベース名>?reconnect=true'
```
今回はMySQL2を使用しているのでmysql2とするのを忘れずに

# database.ymlの修正

```database.yml
production:
  <<: *default
  database: <データベース名> # 変更
  username: <ユーザー名> # 変更
  password: <%= ENV['APP_DATABASE_PASSWORD'] %>
```
cleardbのURLで確認したやつです
変更をコミットします

```
$ git add -all
$ git commit -m "update"
```
# herokuにデプロイ
リポジトリをherokuにpush

```
$ git push heroku master
```
マイグレーション

```
$ heroku rake db:migrate
```

問題がなければ次のコマンドで`https://(appの名前).herokuaopp.com`にアクセスできます。

```
$ heroku open
```
# 補足
今回詰まった原因はmysql2ではなくmysqlにしていたことでした。デプロイできない原因を調べている中でapplication.rbやproduction.rbでassets関連の修正を加えることで改善するという記事を見かけてこの辺をいじったことでさらにハマってしまいました。
結果的には、RailsのAPIモードで作成したアプリであればassets周りは触らなくていいことが分かりました。データを返すAPIとしての機能だけならassetsは必要ないというのは納得です。
新卒エンジニア研修【2ヶ月間でやったこと】

ベストベンチャー100に選ばれている企業に勤めております。
新卒研修の振り返りで2ヶ月間(4~5月)でやったことを残します。

## 4月第１週
総合職研修
- PDCA
- タスク管理
- ビジネスマナー 

## 4月第2週
- 開発手法
    - スクラム開発
        - 1スプリントを繰り返す開発手法
        - 1スプリントは、スプリントプランニング、デイリースクラム、スプリントレトロスペクティブ、スプリントレビュー

- クラウドネイティヴ
    - クラウド技術を最大限活かそうという考え方
    - 開発スピードの向上、属人化を防ぐ、品質向上などの利点がある
- 開発ステージについて
    - ローカル開発環境
    - テスト環境(プログラムが動くかどうかの確認)
    - ステージング環境(本番環境の検証のための環境)
    - 本番環境
- Laravel環境構築
    - Docker for M1 Mac、Workbenchのインストール
    - PHP基本構文、DB、

## 4月第3週
- [チーム・個人]におけるKPI(key performance indicator)について
目標を達成するために必要なアクティビティ
    - べロシティ
    →ストーリーポイント(タスクの難易度)をどれくらいこなしたか
    - デプロイ回数
    →お客さんにどれだけデリバリーできたか
    - サーバー応答速度
    → ブラウザの速度
    - 工数・工期予実達成率
- 開発フローについて
    1. テスト設計
    1. テストケース作成
    1. 単体テスト
    1. 結合テスト
    1. 総合テスト
    1. UAT (User Acceptance Testの略)
- 目標設定
基本的には上長がこの1年間で期待していることを目標に定めた。
サービス全体像の理解やサービスアーキテクチャの理解、他部門からの依頼処理など

- [ソフトウェアのテスト技法](https://www.amazon.co.jp/%E3%81%AF%E3%81%98%E3%82%81%E3%81%A6%E5%AD%A6%E3%81%B6%E3%82%BD%E3%83%95%E3%83%88%E3%82%A6%E3%82%A7%E3%82%A2%E3%81%AE%E3%83%86%E3%82%B9%E3%83%88%E6%8A%80%E6%B3%95-%E3%83%AA%E3%83%BC%E3%83%BB%E3%82%B3%E3%83%BC%E3%83%97%E3%83%A9%E3%83%B3%E3%83%89/dp/4822282511/ref=asc_df_4822282511/?tag=jpgo-22&linkCode=df0&hvadid=295723231663&hvpos=&hvnetw=g&hvrand=9456867488746355426&hvpone=&hvptwo=&hvqmt=&hvdev=c&hvdvcmdl=&hvlocint=&hvlocphy=1009303&hvtargid=pla-527403648937&psc=1&th=1&psc=1) の輪行開始
    - テストファーストな考え方を知る。
    バグが出て修正する時間の方が勿体無い。
    - 良いコードとは。
        - 可読性
        - 保守・運用のしやすさ
        - 昔は少ないコード、処理速度に重点を置いていた(CPUやメモリの関係)
    -  - ブラックボックステスト(要件定義署や仕様書に合っているか確認するテスト手法) 
        - 同値クラステスト
        - 境界値テスト
        -ディシジョンテーブルテスト
        - ペア構成テスト
        - 状態遷移テスト
        - ドメイン分析テスト
   
    - ホワイトボックステスト(パスや処理の流れなどプログラム内部に沿ったテスト手法)
        - 制御フローテスト
        - データフローテスト
    - テストの終了条件
    - テストログ、テスト不具合レポート
        - エビデンス、ログを示す、再現手順がしっかりと書かれているかが大事。
    - xdebugを入れてPHPUnitを実行
    
正直、テストに関しては実際に作成&実践してみないと実感がわかない。

- Dockerコマンド
    - docker-compose build
    - docker-compose up
    - docker-compose down
    - docker-compose exec アプリ名 シェル
- Laravel
    - ディレクトリ構造
    - MVCモデル
    - artisan コマンド
    
## 4月第4週
- CI/CDについて
    - CI(各開発者の作業結果を自動的に統合、単体テストや結合テストの自動化)
    - CD(開発物を素早くユーザに供給する)
- AWSについて
    - ELB
    - ECS
    - Lambda
    - Cloudwatch
    - S3
    - DynamoDB
    - ECR
    - Fargate
    - Athena
    - QuickSight

- GAS(Google Apps Script)環境構築
- Golang デバッグ環境構築

## 5月第1週
- Google チャットbot開発。新規入会者数を決まった時間に通知する。
使用した技術
- Golang, CloudWatch、Lambda、APIGateway、DynamoDB

####　所感
静的言語型付け言語を初めて触った。構造体やポインタの概念など改めて勉強になった。Golangの醍醐味である並列処理などは行っていないので、暇があったらやってみたい。

## 5月第2週
- 記念日Slack通知bot開発
スプレッドシートから記念日を取得し、Slackに通知する。
要件定義から、運用計画、デモまで行った。

使用した技術
- GAS、SlackAPI

#### 所感
開発したBotが初めて動いた時(エンジニアの誕生日を祝った)はとても嬉しかった。今後も社内で運用していくので、より良いサービスに改築していきたい。

## 5月第3週~4週
- Github活動見える化
リポジトリ情報(プルリクやレビュー情報など)を大量に取得し、Amazon QuickSightでデータの可視化を行う。

使用した技術
- Golang、ECS、ECR、Fargate、athena、QuickSight、Github API

#### 所感
チームや個人の貢献度を測るための指標づくりの一環として開発を行った。ビッグデータ
の取得やBIツールを使った分析方法を学べたのはよかった。
Oracle実験室 / Oracle Restart環境構築 (19.3)

私が勉強中のOracle DatabaseについてVirtualBox環境でのセットアップ・各種操作等を行った際のメモです。

# Oracle実験室 / CentOS7.xインストール
https://qiita.com/Que-sera-sera/items/5caf51f9f55f962ff7e3

# Oracle実験室 / CentOS7.x 初期設定
https://qiita.com/Que-sera-sera/items/aaafef29ba9c620f5431

# Oracle実験室 / Oracle Database 19c Grid Infrastructure (19.3) インストール
https://qiita.com/Que-sera-sera/items/c23143f9cb62bb360aa9

# Oracle実験室 / Oracle Database 19c (19.3) インストール
https://qiita.com/Que-sera-sera/items/02a77134bb95c5d8e202

# Oracle実験室 / Oracle Database 19c (19.3) Database Configuration Assistant (DBCA)
https://qiita.com/Que-sera-sera/items/1415e48238cc6d84d429
ExpandoObjectクラスを使ってみた

# ExpandoObjectクラスを使ってみた
業務の中で使用する場面があったので、備忘録として残しておきます。
## どんなクラス？
「System.Dynamic」名前空間のクラス。事前に定義していない型のオブジェクトに対して、動的にプロパティを設定することができる。
## 実際に使ってみた
### ExpandoObjectのインスタンスを生成し,動的にプロパティを設定
```C#
using System;
using System.Dynamic;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            //ExpandoObjectクラスのインスタンスを生成
            dynamic obj = new ExpandoObject();

            //プロパティを設定
            obj.Height = 100;
            obj.Width = 50;

            //各プロパティを出力
            Console.WriteLine("Height: {0}", obj.Height);
            Console.WriteLine("Width: {0}", obj.Width);
        }
    }
}
```

出力結果

```
Height: 100
Width: 50
Apple
Strawberry
Grapes
30
40
```

### JsonにSeriaizeしてみた
ExpandoObjectで生成したオブジェクトをJsonにSerializeする。

```C#
using System;
using System.Collections.Generic;
using System.Dynamic;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            //ExpandoObjectクラスのインスタンスを生成
            dynamic obj = new ExpandoObject();

            //List
            var list = new List<string>();
            list.Add("Apple");
            list.Add("Strawberry");
            list.Add("Grapes");

            //Dictionary
            var dict = new Dictionary<string, int>();
            dict.Add("Apple", 400);
            dict.Add("Strawberry", 600);
            dict.Add("Grapes", 500);

            //プロパティを設定
            obj.Height = 100;
            obj.Width = 50;
            obj.fruits = list;
            obj.people = dict;

            //各プロパティを出力
            Console.WriteLine("Height: {0}", obj.Height);
            Console.WriteLine("Width: {0}", obj.Width);
            Console.WriteLine(obj.fruits[0]);
            Console.WriteLine(obj.fruits[1]);
            Console.WriteLine(obj.fruits[2]);
            Console.WriteLine(obj.people["taro"]);
            Console.WriteLine(obj.people["jiro"]);
        }
    }
}

```
出力結果

```
{"fruits":{"Apple":400,"Strawberry":600,"Grapes":500}}
```

### どんなJsonでもDeserializeしてみたい
構造がわからないJsonをExpandoObjectを使ってDeserializeする。
sample.json

```json
{
    "Name": "Taro",
    "Age": 30,
    "Country": "Tokyo",
    "Skill": [
        "Football",
        "Guitar"
    ]
}
```

```C#
using Newtonsoft.Json;
using System;
using System.Dynamic;
using System.IO;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            //Jsonファイル読み込み
            var sr = new StreamReader(@"C:\work\sample.json");
            var json = sr.ReadToEnd();

            //ExpandoObjectクラスのインスタンスを生成
            dynamic obj = JsonConvert.DeserializeObject<ExpandoObject>(json);

            //プロパティ出力
            Console.WriteLine(obj.Name);
            Console.WriteLine(obj.Age);
            Console.WriteLine(obj.Country);
            Console.WriteLine(string.Join(",", obj.Skill));
        }
    }
}

```

出力結果

```
Taro
30
Tokyo
Football,Guitar
```

## さいごに
今後の業務でもお世話になりそうです。忘れたら見直そう～
【アルゴリズム】JavaScriptでステップ/ピラミッド問題を解く

#はじめに
Qiitaの競技プログラミング研究月間ということで、アルゴリズムの記事を書いています。
今回はステップ問題とピラミッド問題をまとめました。

JavaScriptでアルゴリズムの勉強をされている方の参考になれば幸いです。
記事を順次まとめていきますので、その他の記事についてはマイページからご覧ください。

https://qiita.com/suzuki0430

https://qiita.com/official-events/5a0502a2d94ed6a00c30

#ステップ
引数に整数Nを与えたときに、N段の階段状になるように'#'を出力してください。
右側の'#'が入っていない部分にはスペースをいれてください。

```
   steps(2)
       '# '
       '##'
   steps(3)
       '#  '
       '## '
       '###'
```

##解答
Row方向とColumn方向の二重ループで一行ずつ'#'を入れていく方針を考えます。

まずは空の文字列`stair`を用意し、Rowの値と同じ個数分'#'を入れていきます。
'#'の右側にはスペースを入れていき、Columnのループが終わったら`console.log(stair)`でその段の文字列を出力します。
`console.log(stair)`の出力後にrowを1つ増やし、以上と同じ処理を`row = n-1`まで繰り返します。

![スクリーンショット 2021-06-12 20.49.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/f9d7d928-ce4c-6deb-33d9-10f6b1fb2b7d.png)

```js:index.js
function steps(n) {
  for (row = 0; row < n; row++) {
    let stair = '';

    for (column = 0; column < n; column++) {
      if (column <= row) {
        stair += '#';
      } else {
        stair += ' ';
      }
    }
    console.log(stair);
  }
}
```

再帰関数を使って解く方法も考えます。

関数の中に3つの分岐を作成します。
`stair`を更新しているときは`if (stair.length <= row)`の分岐に入り、`stair`に'#'かスペースを挿入したあとに`steps(n, row, stair)`の再帰呼び出しを行います。
一行分の`stair`が完成したら`if (n === stair.length)`の分岐に入り、rowを1増やして再帰呼び出し`steps(n, row + 1)`を行います。
rowが全て埋まったら`if (n === row)`の分岐に入り、再帰呼び出しを終えて`return`を返します。

```js:index.js
function steps(n, row = 0, stair = '') {
  // 全部の行ができたらreturnを返す
  if (n === row) {
    return;
  }
  // 1行分のstairが完成したらここに入る
  if (n === stair.length) {
    console.log(stair);
    return steps(n, row + 1);
  }
  // 最初は必ずここに入る
  if (stair.length <= row) {
    stair += '#';
  } else {
    stair += ' ';
  }
  steps(n, row, stair);
}
```

#ピラミッド
引数に整数Nを与えたときに、N段のピラミッド状になるように'#'を出力してください。
両側の'#'が入っていない部分にはスペースをいれてください。

```
   pyramid(1)
       '#'
   pyramid(2)
       ' # '
       '###'
```

##解答
ステップのときと同様に、まずはRowとColumnの二重ループで考えます。

各行には(2 * n - 1)個の'#'が入るため、中央の'#'の位置を`const midpoint = Math.floor((2 * n - 1) / 2)`として出すことができます。
空の文字列`level`を用意し、`(midpoint - row <= column && midpoint + row >= column)`の範囲に'#'、それ以外の部分にスペースを入れていきます。

各行に入る'#'の個数と'#'が入る位置に気をつければ、ステップのときとほとんど同じ流れで解くことができます。

```js:index.js
function pyramid(n) {
  // #を付与する位置を算出する
  const midpoint = Math.floor((2 * n - 1) / 2);

  for (let row = 0; row < n; row++) {
    let level = '';

    for (let column = 0; column < 2 * (n - 1) + 1; column++) {
      if (midpoint - row <= column && midpoint + row >= column) {
        level += '#';
      } else {
        level += ' ';
      }
    }
    console.log(level);
  }
}
```

以下が再帰関数を使う場合です。

```js:index.js
function pyramid(n, row = 0, level = '') {
  if (row === n) {
    return;
  }

  if (level.length === 2 * n - 1) {
    console.log(level);
    return pyramid(n, row + 1);
  }

  const midpoint = Math.floor((2 * n - 1) / 2);
  let add;
  if (midpoint - row <= level.length && midpoint + row >= level.length) {
    add = '#';
  } else {
    add = ' ';
  }

  pyramid(n, row, level + add);
}
```

#参考資料
https://www.udemy.com/share/101WNkCEEZd11VRno=/
【論文紹介】Virtual ingredients for food and beverages to create immersive taste experiences The sensation of taste as an electronic media

#論文紹介
論文：Virtual ingredients for food and beverages to create immersive taste experiences The sensation of taste as an electronic media
著者：Nimesha Ranasinghe, Kuan-Yi Lee
https://dl.acm.org/doi/abs/10.1007/s11042-015-3162-8

#どんなもの
　<font color="gray">(目的：身近な飲食物とシームレスに統合して、没入感のある味覚体験を提供する技術を開発する)</font>
　飲食時の味覚をデジタルで拡張するSpoon+とBottle+を開発した。

　人工の化学物質などの味付け成分を含めることなく、味覚操作することができるSpoon+とBottle+の開発をした。Spoon+とBottle+は、舌の電気刺激と仮想の色を重ね合わせることで味覚を増強する。実験では、酸味・苦味・塩味・無味(何も提示しない)の4種類を提示して被験者に評価してもらった。その結果、苦味と酸味、苦味と酸味と塩味など混合感覚が多く見られた。このことから、特定の味覚の区別はつかず、混合感覚のように説明することがわかった。また、薄い酸味、塩味、苦味の無色の水を飲んだ時には、異なる溶液を識別することができなかった。


#先行研究と比較してすごいところ
　食品や飲料の新しい成分が導入されているが、現在市場に出回っている香辛成分のほとんどが人工化学物質である。
　<font color="gray">人工化学物質には2つの問題がある。１つ目は、人工調味料を長期間使用することは人体に悪影響を及ぼすこと。２つ目は、特定の料理や飲み物を口にするときに成分を調整して味をリセットすることができないこと。</font>
　料理中に食材を使用するのと同じように実際の味覚を増強し、仮想的に調節可能な食材や飲料に提供する技術。(Spoon+とBottle+を使うことで、香料を加えることなく食品や飲料の味覚をデジタル的に強化できる。)これによって、味覚の仮想的なシミュレーション、電子的に調整可能な食品や飲料の成分の２つを実現することができる。

#技術や手法のキモ
　Spoon+とBottle+には、舌への電気刺激と飲料に仮想的な色を重ね合わせる２つの技術を組み合わせている。<font color="gray">それぞれ、デジタル制御システムと飲食時にユーザの舌に接触する２つの銀製電極が組み込まれていて、制御システムが電気刺激と色刺激を設定すると器具のマウスピースに取り付けられた銀電極が対応するパラメータを出力する。</font>
　電流の大きさ、周波数、電極の極性など電流の特性を調整することで、酸味・苦味・塩味の異なる仮想味覚を提示する。
　飲み物に仮想の色を重ね合わせるために、超光速RGB LED電球を使用した。赤・緑・青の３色のみが主感覚にマッピングされるため、味覚を仮想的に協調できる。

　Bottle+は底の部分にダイヤルがあり、ダイヤルを回すことで飲み物の色を調整できる。(赤は苦味、緑は酸味、青は塩味の色を飲み物に重ね合わせて、電気刺激の強さを調整できる。)
　Spoon+もBottle+と同様にspoon本体の持つ部分を回転させることで酸味と塩味をデジタルで強調できる。

#検証方法
Bottle+(ストローに舌をおいて電極に触れながら水を飲んでもらう)
　水を飲むときに酸味・苦味・塩味・無味(人工的な刺激がないOFFモード)を提示して、それぞれの刺激に対してその感覚がどのように感じるか(どのようにすっぱい(/苦い/塩っぽい/味がしない)と感じるか)グラフを使って評価してもらう。
→苦味＋酸味、苦味＋酸味＋塩味のような混合感覚が多く見られた。これらの混合感覚は、参加者の個人差によるものと考えられる。参加者は、特定の味覚の区別がつかず、混合感覚のように説明することが多い。

Spoon+
　レモンや塩が入っていないおかゆをSpoon+で食べるときに、塩味・酸味・無味の感覚を提示した。それぞれの刺激に対してその感覚がどのように感じるか(どのようにすっぱい(/塩っぽい/味がしない)と感じるか)グラフを使って評価してもらう。
→軽度の酸味を塩味と認識する被験者がいることがわかった。人は酸味、塩味、苦味の無色の溶液を飲んだとき，その感覚が軽度であれば，異なる溶液を識別することができなかった。

#議論
　人は自然に食べ物の化学物質を唾液で溶かして、口全体を使って味覚を知覚する。Spoon+やBottle+では舌先からの刺激を与えているもので、普段の食事とはかけ離れているのではないか。
→お箸やお茶碗、フォーク、ストローなど技術と一体化した道具を作ることで、舌が様々な飲食物とどのように相互作用するか研究することができるのでは？

　人によって味の好みや感じ方は違い、人によって様々な強さの異なる味覚を感じている。酸味が好きな人もいれば塩味が好きな人もいるように、人の好みには感覚がある。逆に酸味や塩味が苦手な人の場合、調査結果に影響を与えるのではないか。

　水とおかゆを使って基本的な味覚をシミュレートしていたが、このアプローチで他の様々な食品や飲料で味を検証してみたほうがいいのではないか。


#気になった論文
　鳴海先生のEvaluating cross-sensory perception of superimposing virtual color onto real drink: toward realization of pseudo-gustatory displays (参考文献)
　Cruz A, Green B (2000) Thermal stimulation of taste. Nature 403(6772):889–892 (参考文献)
　The multisensory perception of flavor (参考文献)
list配列からblock配列を削除する操作

```js
const list=["a","b","c","d"]
const block=["b","c"]
gene=[]
list.filter(c=>{if(!block.includes(c)){gene.push(c)}})
console.log(gene)
```
                                                                                                                                                                                                                                         
list配列からblock配列を削除する操作
Javaでグラフを描く（72要素をチャートにする）

今回の作業履歴
https://github.com/jamcookjapan/grapy/commit/23856753ab76f3722cfd9333d5f6452fe518d491

少しずつグラフをブラッシュアップしたことと、折線グラフの説明を表示可能にしました。また30要素固定表示していたものを、入力によってX軸を動かします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1535669/adb7639d-2382-65bb-2315-7e714b93dd18.png)

試しにフジシールインターナショナルさんの4/1〜6/11までの始値と終値をグラフにしてみましたが、X軸のラベル間隔が気になりなることとY軸が微妙にズレて描画しているように思います。肝心の株価推移ですがしばらく下落基調だったものが少し上向きになってきました。そのうちX0日平均株価線も実装しようかと思っています。
[JS]mapで配列を回す

## はじめに
Javascript初学者です。JavaScriptのmapを使ったので備忘録として残しておきます。

## 使い方

```
let Array = [1,2,3,4,5]
let doubleArray = Array.map( value => {
   return value * 2
})

console.log(doubleArray); 
=> [2,4,6,8,10]
```

returnを省略するやり方もありそちらのほうがシンプルな見た目になります。

```
let Array = [1,2,3,4,5]
let doubleArray = Array.map( value =>  value * 2 )

console.log(doubleArray); 
=> [2,4,6,8,10]
```

## 参考

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/map
switch 文の代替コードを見ていると Either を連想してしまう悪癖


下記の記事を読んでいていて、どうしても`Either`が思い浮かんじゃったんですよね。

[TypeScriptでany・unknown型の値を型セーフにハンドリングしたい](https://qiita.com/uzawa-sorich/items/6e94d7d4145977160a3b)

どういうことかと言うと、`Either`っぽいコードを用意して、

```.ts
type LambdaOfEither<T, U> = <V>(caseOf: CaseOfEither<T, U, V>) => V;

type CaseOfEither<T, U, V> = Readonly<{
    left: (value: T) => V,
    right: (value: U) => V
}>;

function left<T, U = never>(value: T): LambdaOfEither<T, U> {
    return caseOf => caseOf.left(value);
}

function right<U, T = never>(value: U): LambdaOfEither<T, U> {
    return caseOf => caseOf.right(value);
}
```

あとは`switch`文の機能に代わる高階関数を用意して、

```.ts
const typeSwitch = left;

function typeCase<T, V extends T, U>(typeGuard: (value: T) => value is V, transformer: (value: V) => U): CaseOfEither<T, U, LambdaOfEither<T, U>> {
    return {
        left: value => typeGuard(value) ? right(transformer(value)) : left(value),
        right
    };
}

function typeDefault<U, V extends U>(defaultValue: V): CaseOfEither<unknown, U, U> {
    return {
        left: _ => defaultValue,
        right: value => value
    };
}
```

高階関数に食わせる関数を用意すれば、

```.ts
function isBoolean(value: unknown): value is boolean {
    return typeof value === "boolean";
}

function isNumber(value: unknown): value is number {
    return typeof value === "number";
}

function isString(value: unknown): value is string {
    return typeof value === "string";
}
```

似たようなのが実装できるじゃないですか。

```.ts
function test(jsonString: string): boolean {
    return typeSwitch(JSON.parse(jsonString))
        (typeCase(isBoolean, value => value))
        (typeCase(isNumber, value => value !== 0))
        (typeCase(isString, value => value === "true" || value === "yes"))
        (typeDefault(false));
}

["true", "false", "0", "1", "-1", "100", `"true"`, `"yes"`, `"hoge"`].map(test);
// => [true, false, false, true, true, true, true, true, false]
```

みなさんにも、こういう悪癖ありませんか？
Sec3 【VPC】ネットワークを構築しよう

ハンヅオンで進めているUdemyの教材内容をまとめておきます。

## Sec3 【VPC】ネットワークを構築しよう

### 12. AWSのネットワークを学ぼう

#### リージョン

AWSの各サービスが提供されている地域のこと、AWSのサービス使用する場合には、まずリージョンを選ぶ必要がある

#### アベイラリティゾーン

複数のデータセンターを束ねたもの、リージョン内にいくつかの独立したアベイラティゾーンがある。例えば、1つのアベイラビリティティゾーンが災害で利用できなくった場合に備え、データセンターを独立させ、いくつかに分割してある。

#### VPC：Vurtual Private Cloud

AWS上に仮想ネットワークを作成できるサービス。
リージョンを選択し、アベイラビリティゾーンに跨って作成する。リージョンに対してVPCを構え、その中をサブネットで分割する。例えば、プライベートサブネットやパブリックサブネットなど、利用目的に合わせて分割する。このようにVPCをサブネットで分割する事を「冗長性を高める」といい、AWSのベストプラクティス。

### 13, 14ネットワークのIPアドレスを決めよう

#### IPアドレス

ネットワーク上の機器を識別する住所。32ビットの整数値。

#### パブリックIPアドレス

インターネットを使用する際のIPアドレス。世界中で一意性を補償する必要がある為、ICANNという団体が管理しており、プロパイダーがその貸し出し窓口を担う(AWSもその一つ)。

#### プライベートIPアドレス

インターネット上で使用されないIPアドレス、規定の範囲内からアドレスから選択する。

### 15. VPCを作成しよう

1. VPCを設定
2. サブネットでVPCを分割する
3. ルーティングを設定

### 16. サブネットを作成しよう

VPCだとIPアドレスの範囲が大きから、サブネットで小さくする分割することで扱いやすくする。分割する2つの利点。アベイラリティゾーンを分けて配置する事で、災害などに対して耐障害性が上がる。セキュリティ上の理由で、アクセスを限定する

### 17. ルーティングの設定

####  今回の目的

パブリックサブネットからインターネットに接続できる様にする。ゆくゆくはパブリックサブネットにはWebサーバーを設置する。その為にルーティングを設定する。

IPアドレスはインターネットの住所...インターネット上でどこにいるか分かる
→　では、どのデバイスがどのIPアドレスと結びついているか？ = ルーターが管理している。

ルーターがIPアドレスを管理し、ネットワークとネットワークがIPアドレスを通じて接続する（ルーティング）。

### VPCに対して、ルートテーブルが紐づいている。

ルートテーブルは、宛先のIPアドレスと次のルーター（AWSではターゲットと言う）という書式で設定。
パブリックサブネットのIPアドレスをルートテーブルに設定する事で、パブリックサブネットをインターネットに接続させる。
そしてそれ以外の宛先は、インターネットゲートウェイ（VPCとインターネットを繋げる仮想のルーター）へ指し示す。こいつも新規で作成する必要がある。

### 18. ネットワーク設計のポイント

これまでハンヅオンで進めてきたインターネット設計に関する注意点の紹介。
AWS クライアントVPN OpenVPNのvpnux Clientを使って接続する

# 1. vpnux Client をダウンロードする

こちらから最新のソフトをダウンロードします。現時点では
vpnux Client Standard Edition 2.3
です。

https://www.vpnux.jp/download

# 2. 証明書を作成する

以下を参考にサーバー証明書・サーバー秘密鍵は Certificate Manager にインポートし、クライアント証明書・クライアント秘密鍵はローカルの適当なフォルダにダウンロードします。

https://qiita.com/quryu/items/a0a10cee045c28491d3a

# 3. クライアントVPNエンドポイントを作成する

サーバー証明書・クライアント証明書は、いずれも手順2で Certificate Manager にインポートしたものを選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/88bb0306-b788-96da-55df-4b6e1217294e.png)

以下のように設定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/0634df80-6539-e2e7-9ae0-a9527322ca77.png)

作成後、「関連付け」で、エンドポイント作成時に選択した VPC 内の適当なサブネットを選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/1fda38c7-43fc-685d-2f12-ee370aca16c6.png)

関連付けが完了すれば、エンドポイントが使用可能になります。この関連付けが完了するまで、かなり時間がかかります。3 ～ 10 分程度かかると思います。

# 4. vpnux Client を使ってエンドポイントに接続する

設定ファイルをダウンロードします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/d121aeec-1710-519e-fb6b-cc66037dbba5.png)

設定ファイルを開き、

```
remote cvpn-endpoint-xxxxxx.prod.clientvpn.ap-northeast-1.amazonaws.com 443
```

となっている部分を編集します。`cvpn-endpoint-xxxxxx` の前に `a.` を追加します。編集後は以下のようになります。

```
remote a.cvpn-endpoint-xxxxxx.prod.clientvpn.ap-northeast-1.amazonaws.com 443
```

`a.` の部分は任意で問題ありません。この任意の文字列を追加しなければ、以下のエラーにより接続に失敗します。

```
RESOLVE: Cannot resolve host address: cvpn-endpoint-xxxxxx.prod.clientvpn.ap-northeast-1.amazonaws.com:443 ()
```

原因はドキュメントに記載されています。

> Client VPNエンドポイントの設定ファイルには、remote-random-hostnameというパラメータがあります。このパラメーターは、DNSキャッシュを防ぐために、クライアントがDNS名の前にランダムな文字列を付加することを強制します。

次に vpnuxClient.exe を開きます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/f749fd1b-7777-92be-f1e2-d038bc7357f4.png)

プロファイルを選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/584adf00-9e68-287e-0ba8-6258784a5a2f.png)

インポートを選択し、設定ファイルを選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/0a3648de-13a3-9073-d559-5f07bd416e60.png)

編集を選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/8a7c199d-ab6a-2e92-6d71-5e43322f9eed.png)

「証明書認証（PKI）を使用」にチェックを入れます。証明書・秘密鍵はそれぞれ手順2でダウンロードしたものを選択します。その後、保存を選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/6426f111-d863-6cd7-d906-6236b95eb5a3.png)

プロファイルに今インポートした設定ファイルを選択し、接続をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/47fb53af-a30e-c939-e59e-a05316534cf8.png)

隠れているインジケーター（デスクトップ右下にある ＾ のマーク）をクリック → vpnux Client を右クリック → 表示をクリックの順に操作すると、以下の画面がウィンドウが表示され、接続できたことが確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/3cfc2945-3813-580d-578a-8b1729d2e85f.png)

# 参考記事

https://www.vpnux.jp/

https://docs.aws.amazon.com/ja_jp/vpn/latest/clientvpn-user/ubuntu-troubleshooting.html

https://qiita.com/quryu/items/a0a10cee045c28491d3a
【初心者でもわかる】文字が書けるルーズリーフをCSSで再現する

# どうも7noteです。文字が書けるルーズリーフをCSSだけで再現

学生さんがよく使う、ルーズリーフ。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/661092/c566b866-8a9c-ba90-84f2-d0b3e83ab243.jpeg" width="300" >


これをCSSで再現して、文字も書けるようにします。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/661092/73dadcaa-514c-98c1-9674-2dd4d1846752.gif" width="300" >



## ソース

```index.html
<div class="paper">
  <textarea></textarea>
</div>
```

```style.css
body {
  background: #F9EBB8; /* 背景色に淡い黄色を指定 */
}

.paper {
  width: 300px;     /* 紙の幅を指定 */
  height: 450px;    /* 紙の高さを指定 */
  background: #fff; /* 紙の色を白に指定 */
  border-radius: 5px;  /* 角は若干丸くする */
  padding: 20px 20px 20px 50px; /* 左に多い目に余白を取る */
  position: relative;  /* 基準位置とする */
}

.paper::before {
  content: '';  /* 疑似要素に必須 */
  width: 10px;  /* 穴の幅を指定(穴の幅以上あればOK) */
  height: 90%;  /* 穴をあける距離を指定。紙の高さの90%に指定 */
  background-image: radial-gradient(circle, #F9EBB8 5px ,transparent 5px);  /* 円形のグラデーションを作成。色はbodyの背景色と同じ */
  background-repeat: repeat-y; /* Y軸方向に繰り返す */
  background-size: 10px 20px;  /* 「背景の穴のサイズ(10px)」「穴のサイズ(10px)＋穴から次の穴までの距離(10px)」を指定 */
  display: inline-block; /* インラインブロック要素にする */
  position: absolute;    /* 相対位置に指定 */
  left: 10px;            /* 左から10pxの位置に指定 */
  top: 0;                /* 上下中央に配置するため0に指定 */
  bottom: 0;             /* 上下中央に配置するため0に指定 */
  margin: auto;          /* 上下中央に配置するためautoを指定 */
}

.paper textarea {
  outline: none;  /* アウトラインを無効化。ビジュアル優先 */
  background-image: linear-gradient(180deg, rgba(100, 100, 100, 0) 0%, rgba(100, 100, 100, 0) 95%, #aaa 95%); /* 罫線を引く */
  background-size: 100% 2em;  /* 1段分の罫線を引く為、背景のサイズを指定 */
  line-height: 2em; /* 行間を指定 */
  border: none;     /* デフォルトの枠線を無効化 */
  width: 100%;      /* 幅いっぱいに指定 */
  height: 100%;     /* 高さいっぱいに指定 */
  resize: none;     /* リサイズを無効化 */
}
```
## 解説

長方形を用意して、装飾をつけていきます。
重要になってくるのが、**罫線、左の穴、四角の角丸**です。
これらを再現することでそれらしさを表すことができます。

左の穴は擬似要素を使って再現していますが、div要素を二重にして再現することも可能です。

**textareaと組み合わせる事で、実際にルーズリーフに文字を書いてるように見せかけています。**

## まとめ

社会人になるとなかなか筆記用具とかを使わなくなるので少し懐かしい感じがします。
特にit系の仕事だとほぼパソコンでメモしてしまうので、本当に使う機会が少ないですね。

だからこそWEBで再現することで面白さが出せます！


#### おそまつ！
~ Qiitaで毎日投稿中!! ~
[【初心者向け】WEB制作のちょいテク詰め合わせ](https://qiita.com/7note)
Laravel sail で環境構築

**Laravel Sail**というのを知ったのでちょっと試しに環境構築してみた。

**Laravel Sail**とは？
Laravelのローカル開発環境構築に必要なパッケージなどがまとめられているDockerイメージ
今までは、Composer入れて〜xampp入れて〜、という感じでしたが
Sailはコマンド2つ叩くだけで環境もプロジェクトも作成できます。
※xamppはインストールされません。

##手順##
※macbook air intel chip
※Dockerはインストール済み
#####1.[プロジェクト作成]#####
プロジェクトを作成したいディレクトリに移動し
```curl -s "https://laravel.build/プロジェクト名" | bash``` を実行

#####2.コンテナ起動#####
```./vendor/bin/sail up```を実行

コンテナ起動中は、以下のコードが繰り返されますが問題ありません。
```mailhog_1       | [APIv1] KEEPALIVE /api/v1/events
meilisearch_1   | [2021-06-12T12:07:00Z INFO  actix_web::middleware::logger] 127.0.0.1:52060 "GET /health HTTP/1.1" 200 22 "-" "Wget" 0.000144
meilisearch_1   | [2021-06-12T12:07:30Z INFO  actix_web::middleware::logger] 127.0.0.1:52064 "GET /health HTTP/1.1" 200 22 "-" "Wget" 0.000115````

ブラウザで``http://localhost/``に移動
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569318/622121a3-4ac5-e54a-c187-9a9eb0bc095b.png)

無事表示されました。

#####3.コンテナ停止#####
ターミナルで別タブを立ち上げ
```./vendor/bin/sail up```

#####エイリアスの設定#####
```alias sail='bash vendor/bin/sail'```
これで
```sail コマンド```
で実行可能


一旦、ここまでで
もう少し開発進めてみて、**Laravel Sail**の良いところ、悪いところ
追記していきたいと思います。
【React】 僕だけ知らない Tip (React.Children.toArray)



```react
//Bad case
{someData.map(item =>(
     <div key={item.id}> hello! </div>
))}
```

```react
//Good case
{React.Children.toArray(
  someData.map(item =><div> hello! </div>)
)}
```

React.Children.toArrayを利用する場合、key属性は要らなくなります。
stack machineを愛でる。 その2

#概要

stack machineが、好きだ。
俺仕様の命令コードです。


#命令コード

|コード|ニーモニック|動作|
|:--|:--|:--|
|0x96|push "x"    |スタックに"x"を積む、1個増える。
|0x21|add         |スタックの二番目と、一番目を文字列連結、1個減る。
|0x0a|+           |スタックの二番目と一番目を足す、1個減る。
|0x0b|-           |スタックの二番目から一番目を引く、1個減る。
|0x0d|/           |スタックの二番目を一番目で割る、1個減る。
|0x0c|*           |スタックの二番目と一番目を掛ける、1個減る。
|0x1d|set         |スタックの二番目を変数として、一番目の値をセット、1個減る。
|0x1c|get         |スタックの一番目を変数として、その値をセット
|0x5c|++          |スタックの一番目をインクリメント
|0x0e|=           |スタックの二番目を一番目で比較、1個減る。
|0x0f|>           |スタックの二番目を一番目で比較、1個減る。
|0x12|!           |スタックの一番目を反転
|0x9d|if  skip    |スタックの一番目が真ならskipに飛ぶ
|0x99|jp loop     |loopに飛ぶ
|0x18|int         |スタックの一番目を整数化
||end         |停止

以上。
WindowsでもBusyBoxを使う

## ダウンロード

https://frippery.org/busybox/

* https://frippery.org/files/busybox/busybox.exe
* https://frippery.org/files/busybox/busybox64.exe

## アプレット

```sh
busybox.exe bash
```

## 注意点

* 配布されているものはコマンドプロンプト上で日本語入力が扱えない （.configで自分で適宜コンパイルが必要。）

## busybox の httpd を使ってみる

```sh
# -f を付けないとバックグラウンド起動する。
busybox httpd -f
```

* http://localhost:80 にアクセスし表示されることを確認。


### httpd.conf

https://openwrt.org/docs/guide-user/services/webserver/http.httpd

```apache:httpd.conf
# allow from All
A:*

# Basic認証をかけることもできる
# /path:user:pass     username/password
# /path:user:pass
```

* CGIが`/cgi-bin/`で動くと思うのだが、ただのテキストファイルとして扱われた。(実行権限は付与している。)調査中

https://git.busybox.net/busybox/tree/networking/httpd.c

多分CGIが有効にされていない。自分でビルドするか・・・

![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/25728/f4f20554-0942-edd8-8c97-5a241e3dac6c.png)
Linux: 大量の日付付きダミーログファイルを作成する方法

シェルスクリプトの単体試験で時々使用するのでメモしておきます。

###### 実施環境：

```Linux:Linux
[testuser@testhost ~]$ uname -a
Linux testhost 4.18.0-147.8.1.el8_1.x86_64 #1 SMP Thu Apr 9 13:49:54 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
[testuser@testhost ~]$ echo $SHELL
/bin/bash
```

日付付きのダミーログファイルを作成する場合、1つ2つであればtouchコマンドを使用して次のように簡単に作成できます。

```Linux:Linux
[testuser@testhost ~]$ touch -t 202106010900 test.log.20210601
[testuser@testhost ~]$ ls -l
-rw-r--r-- 1 testuser testgrp 0 Jun  1 09:00 test.log.20210601
```

上記は日付をピンポイントで指定していますが、「今日から3日前」のように日付を相対的に指定する場合、dateコマンドを利用して次のように書けます。

```Linux:Linux
[testuser@testhost ~]$ date
Sun Jun  6 13:41:14 JST 2021
[testuser@testhost ~]$ touch -t `date -d "3 days ago" +%Y%m%d0900` test.log.`date -d "3 days ago" +%Y%m%d`
[testuser@testhost ~]$ ls -l
-rw-r--r-- 1 testuser testgrp 0 Jun  3 09:00 test.log.20210603
```

ただ、大量のダミーログファイルを作成する場合、これらのコマンドを単に1つずつ入力していくのは非常に大変です。
ではどうするかというと、for文を利用します。
以下の例では昨日から1週間前までの分のダミーログファイルを作成しています。

```Linux:Linux
[testuser@testhost ~]$ date
Sun Jun  6 13:48:30 JST 2021
[testuser@testhost ~]$ for ((i=1;i<=7;++i)); do touch -t `date -d "$i days ago" +%Y%m%d0900` test.log.`date -d "$i days ago" +%Y%m%d`; done
[testuser@testhost ~]$ ls -l
-rw-r--r-- 1 testuser testgrp 0 May 30 09:00 test.log.20210530
-rw-r--r-- 1 testuser testgrp 0 May 31 09:00 test.log.20210531
-rw-r--r-- 1 testuser testgrp 0 Jun  1 09:00 test.log.20210601
-rw-r--r-- 1 testuser testgrp 0 Jun  2 09:00 test.log.20210602
-rw-r--r-- 1 testuser testgrp 0 Jun  3 09:00 test.log.20210603
-rw-r--r-- 1 testuser testgrp 0 Jun  4 09:00 test.log.20210604
-rw-r--r-- 1 testuser testgrp 0 Jun  5 09:00 test.log.20210605
```

少々長いコマンドですが、1つ1つ手入力するよりは圧倒的に楽になります。
数値やオプションを変更すれば、より長い期間のダミーログファイルを作成することや、日付付きでなく連番のファイルを作成することも可能です。
Unityで作ったAndroidアプリをAndroidStudioのプロファイラでCPUや消費電力を調べる

Unity で Android アプリを作ったときに、実機でのCPU使用率や電力消費を調べたくなることありませんか？
Android Studio に付属している Profiler を使えば、下のように表示することができます。
![2021-06-04_15h54_25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/506075/2b84ed74-b58d-1738-6306-f5ee04ac7f1c.png)
ここではその手順を記載しています。

# 前提
製品           | version
---------------|----------------------
Windows        | 10
Unity          | 2019.4.26f1
Andorid Studio | 4.2.1

# 手順
## Unity
1. Android Studio のプロファイラ対象にしたいプロジェクトを開きます。
1. Export Project にチェックを入れて、Export を押す。  
![2021-06-04_15h38_10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/506075/ceea57eb-d14a-5559-9c9f-68c4e0d92eff.png)
1. 保存場所を指定して保存

## Android Studio
1. Open an Existing Project から Unity で Export したフォルダを選択して開きます。開くのに少し時間がかかる可能性があります。右下のほうがロード中みたいになっていると思うので、ただただ待ちます。  
![2021-06-04_15h41_04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/506075/eaabd4cf-9a63-8213-f21d-b3d0eaf73f24.png)
2. View → Tool Windows → Profiler  を選択します。  
![2021-06-04_15h43_32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/506075/c244726e-4e40-702f-1475-3bee72714092.png)
3. こんな感じで Andorid Profiler が開きます。
![2021-06-04_15h45_54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/506075/641d7886-8380-984e-5be8-0a13bbe73c41.png)
4. 検証したい端末が設定されていることを確認して、実行ボタン（緑色の三角）を押します。  
![2021-06-04_15h46_59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/506075/af20affd-052c-a642-87c0-48e4140aab24.png)
5. こんな感じでデータが表示されていれば成功です。  
![2021-06-04_15h49_04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/506075/6b6ffb8f-4ec9-d68f-5370-9d173d847276.png)

# 注意
## プロファイラしたデータはファイルで保存することができません
* CPU/MEMORY に関しては個別にプロファイラと計測することで、ファイルに保存することが可能です
    * MEMORY のデータを取得している、私の実機環境（Pixel4）では、画面がカクつく程度の影響が出ました
* NETWORK/ENERGY に関しては保存不可です

## Battery Historian という方法も
別ツールの Battery Historian であればファイル保存が可能です。
[こちら](https://qiita.com/matchyy/items/325ac3bb28b42723a538)で紹介しています。

### 参考
https://stackoverflow.com/questions/44219983/export-android-studio-profiler-data
Unityで作ったAndroidアプリを Battery Historian でCPUや消費電力を調べる

Battery Historian とは Android 端末の消費電力などをグラフ化してみることのできるツールです。
実行すると以下のようなグラフをブラウザで確認できるようになります。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/506075/539cf4fd-c458-bc40-63e8-87b5ec47a120.png" width=680 />
以下は、[ここ](https://developer.android.com/topic/performance/power/setup-battery-historian?hl=ja)を参考に実施した手順などをまとめてあります。

# はじめに
以下メリットはありますが、

* Browser で確認でき、誰でもデータの取り込みができるようになる
* ファイルで保存できる
* 項目が非常に詳細

手軽さで行ったら AndroidStudio のプロファイラのほうが手軽です。
[こちら](https://qiita.com/matchyy/items/9025f176f9a9801b3a51)で紹介しています。

## 前提
* Windows 10
* Docker 19.03.13

# 手順
大きく分けて二つあります。
1. docker を含む表示環境の準備
2. データの収集と表示

## docker を含む表示環境の準備
1. docker のインストール（他サイトにたくさんあるので割愛します）
2. コマンド実行
docker -- run -p 8080:9999 [gcr.io/android-battery-historian/stable:3.0](http://gcr.io/android-battery-historian/stable:3.0) --port 9999
    1. エラーが出る場合は参考のエラー箇所を参照
3. [http://localhost:8080/](http://localhost:8080/) にアクセスする
4. ↓ が表示されればOK

![2021-06-03_21h47_02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/506075/f0a396f2-1e25-5d7e-19bb-b1f113321d3b.png)


## データ収集と表示
1. 以下コマンドを実行。  
$ adb shell dumpsys batterystats > batterystats_yyyyMMddHHmmss.txt
2. 以下コマンドを実行。私の環境では2～3分ほどかかりました。  
$ adb bugreport > bugreport.zip
2～3分かかった。
3. 2 を実行すると bugreport.zip 以外に、 `bugreport-flame-RQ1A.210205.004-2021-04-03-12-25-27.zip` が出ていることを確認します。
    1. 実行したタイミングで日付などは異なります。
    2. この後このデータを使用して表示します。
4. Browse を押して、3 のファイルを選択すると、右側に Submit ボタンが出てくるので押す。  
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/506075/441d8911-943c-881f-7fb0-1276fa3ea7af.png" width=540 />
5. 正常に取り込まれていれば、こういう感じで表示されるはず。うまくいかない場合は参考のエラー箇所を参照。  
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/506075/539cf4fd-c458-bc40-63e8-87b5ec47a120.png" width=680 />

# 参考
## docker image が起動できない invalid publish opts format エラー
以下のような `docker: invalid publish opts format` が出る場合。

```
docker: invalid publish opts format (should be name=value but got '172.18.111.2:9999').
See 'docker run --help'.
```
### 原因

```
Once you start Docker, it should tell you the IP address of the machine it is using. If, for example, the IP address is 123.456.78.90, Historian will be available at http://123.456.78.90:<port>.
```

とあったので、IP 指定で実行しろという意味なのかと間違えていた。

### 対応
$ docker --run -p 172.18.112.1:9999 gcr.io/android-battery-historian/stable:3.0 --port 9999

と実行していたのだが、そうではなく、

$ docker --run -p 172.18.112.1:8080:9999 gcr.io/android-battery-historian/stable:3.0 --port 9999

と実行すればいい。
現状の docker では、上記通りIP指定なしでも問題ない。

## ファイルが正常に読み込めない out of memory

画面上部に赤いトーストみたいなものが表示されている場合はうまくいっていない。

私の場合は、out of memory が出ていた。これは docker を動かしているターミナル上に表示されていた。

```
fatal error: runtime: out of memory
```

### 原因
読んだ通りメモリ不足。

### 対応
stackoverflow を基に、512MB しかなかったので4096に変更して、docker 再起動、コマンド再実行で、やり直したところうまくいった。
STM32L010とWS2812Bと十進BASIC(PC)で光らす。


目的
PCからマイコンを通してWS2812Bをコントロールする。

前提、まず、STM32L010とWS2812Bのシリアコントールを構築
STM32L010とWS2812Bを使い、シリアルから入力された値で光らす。

https://qiita.com/caa45040/items/e44ea0008f5e4584671a

x ポートは、各自違う


```

OPEN #1:NAME "COM6:9600,n,8,1,x"

DO WHILE 1=1
   PRINT #1:CHR$(48);
   PRINT #1:CHR$(49);
   PRINT #1:CHR$(50);
   WAIT DELAY 2
   PRINT #1:CHR$(33);
   PRINT #1:CHR$(0);
   PRINT #1:CHR$(0);
   WAIT DELAY 2
   PRINT #1:CHR$(0);
   PRINT #1:CHR$(33);
   PRINT #1:CHR$(0);
   WAIT DELAY 2
   PRINT #1:CHR$(0);
   PRINT #1:CHR$(0);
   PRINT #1:CHR$(33);
   WAIT DELAY 2
LOOP

END


```

![10basic_ws2812b.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/e507ff28-95b5-8f70-434e-579d062332fd.jpeg)
stack machineを愛でる。

#概要

stack machineが、好きだ。
余り、実装を知らない。flashと.netくらいしか。(mindもあった。)
flashは、終了したが、ruffleがあるらしい。
動的に、swfを生成して、ruffleに、食わせてみた。

#サンプルコード

```


function _arrayBufferToBase64(buffer) {
	var binary = '';
	var bytes = new Uint8Array(buffer);
	var len = bytes.byteLength;
	for (var i = 0; i < len; i++) 
	{
		binary += String.fromCharCode(bytes[i]);
	}
	return window.btoa(binary);
}
swf21 = [
	0x46, 0x57, 0x53, 0x04, 0xB0, 0x00, 0x00, 0x00, 0x68, 0x00, 0x10, 0x40, 0x00, 0x04, 0x10, 0x00,
	0x00, 0x0C, 0x01, 0x00, 0x43, 0x02, 0x33, 0x33, 0x33, 0x3F, 0x03, 0x79, 0x00, 0x00, 0x00, 
	0x96, 0x03, 0x00, 0x00, 0x69, 0x00, 0x96, 0x03, 0x00, 0x00, 0x30, 0x00, 0x1d, 0x96, 0x03, 0x00,
	0x00, 0x78, 0x00, 0x96, 0x03, 0x00, 0x00, 0x69, 0x00, 0x96, 0x03, 0x00, 0x00, 0x69, 0x00, 0x1c,
	0x96, 0x03, 0x00, 0x00, 0x31, 0x00, 0x0a, 0x1d, 0x96, 0x04, 0x00, 0x00, 0x32, 0x39, 0x00, 0x96,
	0x03, 0x00, 0x00, 0x69, 0x00, 0x1c, 0x0f, 0x12, 0x9d, 0x02, 0x00, 0xd6, 0xff, 0x96, 0x03, 0x00,
	0x00, 0x69, 0x00, 0x1c, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4D, 0x09, 0x01, 0x00, 0x60, 0x0A, 0x3F, 0xC0,
	0x0A, 0x3F, 0xC0, 0x60, 0x08, 0x78, 0x00, 0x05, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x40, 0x00
];
function tob(url) {
	var bin = atob(url);
	var buffer = new Uint8Array(bin.length);
	for (var i = 0; i < bin.length; i++)
	{
  		buffer[i] = bin.charCodeAt(i);
	}
	var blob = new Blob([buffer.buffer], {
		type : 'application/x-shockwave-flash'
	});
	var blob_url = window.URL.createObjectURL(blob);
	return blob_url;
}
var b = _arrayBufferToBase64(swf21);
var l = '" type="application/x-shockwave-flash" width="300" height="300"></embed>';
var swf = '<embed src ="' + tob(b) + l;
document.write(swf);


```

#成果物

https://embed.plnkr.co/plunk/s092Y4LArTJMJW4C

以上。
Visual Studio Codeの制限モード解除について

# 制限モードの解除について
制限モードの解除は[公式](https://code.visualstudio.com/docs/editor/workspace-trust)で非推奨になっており、操作を行った本人の自己責任になります。
解除はこれを理解した上で行ってください。

# 制限モード
Visual Studio Codeの[新リリース](https://code.visualstudio.com/updates/v1_57)においてセキュリティに関する`Workspace Trust`の機能が正式リリースされました。

しかし、まだリリースされたばかりで機能がよくわからない。拡張機能が制限されて使えず困っている。制限モードが必要となる開発をやってない方など制限モードがリリースされる前の状態で作業をしたい方もいると思います。

本記事はそんな方向けに制限モードを解除して`version1.57`前のVscodeにする方法についてです。


# 解除方法
Vscodeの画面左下にある歯車マークから`設定`をクリック。検索バーに`security.workspace.trust.enabled`を入力してください。
`Workspace Trust`の機能を有効化するかしないかが出てきます。
![スクリーンショット (18).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/844088/4a151acd-2232-ecb5-3edc-310c2cd5452f.png)

ここでチェックを外し、再起動をかけると制限モード導入前`version 1.57`の状態でVscodeを使用することが可能になります。この設定は後からでも変更可能です。

以上が制限モードの解除になります。

# 最後に

今回は制限モードそのものを解除してしまいましたが、冒頭に申し上げた通り公式からは非推奨になっているので、そこは注意してください。


参考文献

https://code.visualstudio.com/updates/v1_57

https://code.visualstudio.com/docs/editor/workspace-trust
【Node.js】https と fs で、ファイルをダウンロードする

## 環境

```shell
$ node -v
v16.2.0
$ yarn info https version
1.0.0
$ yarn info fs version
0.0.1-security
$ yarn info  typescript version
4.3.2
```
## コード

使いやすいようにPromiseを併用しています。
コードは TypeScriptなので、JavaScriptの場合は型定義を消してください。

```ts
const download = (uri: string, filename: string) => {
  return new Promise<void>((resolve, reject) =>
    https
      .request(uri, (res) => {
        res
          .pipe(createWriteStream(filename))
          .on("close", resolve)
          .on("error", reject);
      })
      .end()
  );
};

download(
  "https://cdn2.bulbagarden.net/upload/thumb/8/88/006Charizard-Gigantamax.png/600px-006Charizard-Gigantamax.png",
  "Charizard-gmax.png"
).then(() => console.log("done"));
```

ローカルに `Charizard-gmax.png` というファイルがダウンロードできているのが確認できます。

![600px-006Charizard-Gigantamax.png (600×600)](https://cdn2.bulbagarden.net/upload/thumb/8/88/006Charizard-Gigantamax.png/600px-006Charizard-Gigantamax.png)

## かんたんな解説

[res.pipe](https://nodejs.org/api/stream.html#stream_readable_pipe_destination_options) は引数に [stream.Writable](https://nodejs.org/api/stream.html#stream_class_stream_writable) オブジェクト (今回の場合は [fs.createWriteStream](https://nodejs.org/api/fs.html#fs_fs_createwritestream_path_options) ) を指定することで、そのオブジェクトにすべてのデータを自動で流し込んでくれます。


## 参考

- [Stream | Node.js v16.3.0 Documentation](https://nodejs.org/api/stream.html#stream_class_stream_writable)
- [File system | Node.js v16.3.0 Documentation](https://nodejs.org/api/fs.html#fs_fs_createwritestream_path_options)
- [How to use fs.createWriteStream? | Node.js](https://nodejs.org/en/knowledge/advanced/streams/how-to-use-fs-create-write-stream/)