OmniAICreator/Qiita-1.07M huggingface dataset:
AWS IoT Coreを使用してDynamoDBに保存する

# はじめに
> 前回の記事でraspberryPiとAWS IoT Coreを使った記事の続きでAWS IoT Coreで受け取ったデータをDynamoDBに保存してみようと思います。

 - 前回の記事

[AWS IoT Coreを使ってみた](https://qiita.com/arakirai1128/items/8d2b790d10d3b1b1ee32)



# 検証環境

 - RaspberryPi3B+
 - 温湿度センサー（DHT22）
 - AWS IoT Core 
 - AWS DynamoDB

# DynamoDB作成

1) AWSマネージメントコンソールにログインしてDynamoDBサービスを開き「**テーブル**」→ 「**テーブルの作成**」ボタンをクリックします

[AWSマネージメントコンソール](https://aws.amazon.com/jp/free/?trkCampaign=acq_paid_search_brand&trk=ps_a134p000006gTa8AAE&sc_campaign=acquisition_JP&sc_channel=ps&sc_publisher=google&sc_category=core&sc_country=JP&sc_geo=JP&sc_outcome=devadopt&sc_detail=aws%20%E3%82%B3%E3%83%B3%E3%82%BD%E3%83%BC%E3%83%AB&sc_content=Console_e&sc_segment=487544183236&sc_medium=ACQ-P|PS-GO|Brand|Desktop|SU|AWS|Core|JP|JP|Text|dx&ef_id=CjwKCAjwzOqKBhAWEiwArQGwaMhDocds6i3p8TBO2dBTU9auYiyfS6mMN1SSJET7wSPnmtZWwU0HhBoCH1QQAvD_BwE:G:s&s_kwcid=AL!4422!3!487544183236!e!!g!!aws%20%E3%82%B3%E3%83%B3%E3%82%BD%E3%83%BC%E3%83%AB&gclid=CjwKCAjwzOqKBhAWEiwArQGwaMhDocds6i3p8TBO2dBTU9auYiyfS6mMN1SSJET7wSPnmtZWwU0HhBoCH1QQAvD_BwE&all-free-tier.sort-by=item.additionalFields.SortRank&all-free-tier.sort-order=asc&awsf.Free%20Tier%20Types=*all&awsf.Free%20Tier%20Categories=*all)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/533263/0830338e-73af-29a2-9703-2c03af19ec7c.png)


2) テーブル作成するための必要な項目を入力して「**テーブルの作成**」をクリックします

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/533263/ad504f25-5dd8-59c9-5f5c-c66e12d73870.png)


3) しばらくすると作成したテーブルの状態項目に**active**になるので、これでDynamoDBの作成は、終了です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/533263/be9c75f5-ef48-ee2d-44ba-799c4621e162.png)

# AWS IoT Coreのルール作成

4) AWS IoT Coreサービスを開き「**ACT**」→ 「**ルール**」→ 「作成」をクリックします

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/533263/9c171b3c-6658-9c28-f3f1-2f211760b393.png)

5) ルール作成に必要な項目を入力します

- 名前： 任意の名前
- 今回ルールクリエイメントは、特に設定しません

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/533263/7543b3ce-6f3f-ecf0-0f8b-532670205799.png)

6) 「**アクションの追加**」 → 「**DynamoDBテーブルにメッセージを挿入する**」→ 「**アクションの設定**」をクリックします

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/533263/8fb4266b-ec21-8097-93c9-ea77af55849c.png)

7) アクションの設定をします

 - テーブル名： 先ほどDynamoDBで作成したテーブルを選択
 - パーティーションキー値：'${clientid()}'
 - レンジキーの値：'${now()}'
 - 新しくロールの作成（任意の名前で作成）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/533263/47fb3c2b-f331-e624-01d5-252b615d459a.png)

8)「**アクションの追加**」をクリックして作成します

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/533263/cea43c08-9669-afac-9403-fdeaff16a6a0.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/533263/2925b114-8b9e-b882-7bc9-82efe637ccd9.png)

# 動作確認

9) raspberryPiからIoT Coreデータが送られることを「**MQTTテストクライアント**」で確認します

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/533263/eaff2346-4988-4fc0-36b9-2f9c07792aad.png)


10) DynamoDBを開き作成したテーブルの項目をクリックしデータが格納されているか確認します

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/533263/19a24501-75bc-f59c-e795-2661ff6b0190.png)
REST APIにおけるクエリパラメタとパスの使い分け

# APIのURLとして以下のどちらが適切か


- https://api.hoge.com/users?id=12345
- https://api.hoge.com/users/12345



# 判断基準について
以下2点をもとにどちらで表現するか判断すると良い。

1. 一意なリソースを表すのに必要かどうか
⇛パスパラメータを利用

2. 省略可能かどうか
⇛クエリパラメタを利用

# ユースケース

### GET
```
GET https://api.hoge.com/users?id=12345
GET https://api.hoge.com/users/12345
```
この場合、ユーザIDは省略可能（全ユーザを取得可能）なので、クエリパラメタでの表現が適切。
○ GET https://api.hoge.com/users?id=12345　　　　　
### POST
```
POST https://api.hoge.com/users?id=12345
POST https://api.hoge.com/users/12345
POST https://api.hoge.com/users（bodyで指定）
```
この場合、ユーザIDを省略することは出来ないので、パスでの指定か、bodyでの指定が適切。
厳密には、POST時にusersという集合の中にユーザ12345はまだ存在しておらず指定出来ないので、
```
POST https://api.hoge.com/users（bodyで指定）
```
がbetterか。


### PUT
```
PUT https://api.hoge.com/users?id=12345
PUT https://api.hoge.com/users/12345
PUT https://api.hoge.com/users（bodyで指定）
```
この場合、ユーザIDを省略することは出来ないので、パスでの指定か、bodyでの指定が適切。
/usersという指定だと、users全体の集合をbodyに入れて集合全体更新するようなイメージもあるので、
明確に1ユーザ更新のAPIの場合、
```
PUT https://api.hoge.com/users/12345
```
がbetterか。




# まとめ
一般論でのbetterはこんな感じになるが、
一方で、更新系に関しては要件に依るところも大きく、APIごとに設計が分かれるところ。

```
GET https://api.hoge.com/users?id=12345
POST https://api.hoge.com/users（bodyで指定）
PUT https://api.hoge.com/users/12345
```



たとえば、複数ユーザの一括登録、一括更新をしたい場合に、
パス指定だと対応出来ないため、以下のような設計にしたとする。

```
GET https://api.hoge.com/users?id=12345
POST https://api.hoge.com/users（bodyで指定）・・・Nユーザ対応可能（N≧1)
PUT https://api.hoge.com/users/12345 ・・・1ユーザ対応可能
PUT https://api.hoge.com/users（bodyで指定） ・・・Nユーザ対応可能（N≧1)
```



そうすると下から二つ目は冗長なので、それなら最初から以下のように設計した方が良い。

```
GET https://api.hoge.com/users?id=12345
POST https://api.hoge.com/users（bodyで指定）・・・Nユーザ対応可能（N≧1)
PUT https://api.hoge.com/users（bodyで指定） ・・・Nユーザ対応可能（N≧1)
```



そのため、原則を踏まえて、API全体設計の整合やリクエスト要件を踏まえた上で、
それぞれ設計するのが良い。
Watson StudioからSQLQueryの実行結果のICOSバケットへのアクセス

Waston StudioからICOSデータにアクセスことについては以下の記事を書きました。

Waston StudioからICOSデータにアクセス - Qiita 

https://qiita.com/kawada2017/items/0e2f9cc3a03b3e88719b

この応用編としてSQL Queryの実行結果として出力されるICOSバケット内のデータにアクセスしてみます。

まず、ICOSバケットからソースになるファイルを選び、SQLでアクセスをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/efa6a095-11d2-a7d2-9e66-61c1f69fa4f2.png)


SQLQueryのGUIが開きます。自動的にFROM句にソースファイルが指定されています。
実行をするとJobsの結果のDetialsの中にResult locationが記録されます。この結果にWatson Studioからアクセスします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/6358d606-a7f9-d063-59eb-107d310a8a79.png)

まず、このバケットにアクセス可能なサービスIDを用意しておきます。[こちらの記事](https://qiita.com/kawada2017/items/0e2f9cc3a03b3e88719b#3-%E3%82%A2%E3%82%AF%E3%82%BB%E3%82%B9%E5%88%B6%E5%BE%A1)を参考にしてください。

SQL QueryのResult locationのバケットはsql-0b07badc-e815-4556-883d-ef760d082614でしたのでここへのアクセス権を追加します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/e4c8e542-0ca9-fc6d-ceb6-661b2f6748b7.png)

Watson StudioのICOSへの接続オブジェクトを作成します。[こちらの記事](https://qiita.com/kawada2017/items/0e2f9cc3a03b3e88719b#4-watson-studio%E3%81%A7%E6%8E%A5%E7%B6%9A%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%82%92%E4%BD%9C%E6%88%90)を参考にしてください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/0c8256ae-07ab-157f-fc9a-09d22c41bdc1.png)

接続オブジェクトから接続データを作成します。resultのフォルダの中にSQL QueryのResult locationからjobidを選択します。[こちらの記事](https://qiita.com/kawada2017/items/0e2f9cc3a03b3e88719b#5-watson-studio%E3%81%A7%E6%8E%A5%E7%B6%9A%E3%83%87%E3%83%BC%E3%82%BF%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%82%92%E4%BD%9C%E6%88%90)を参考にしてください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/3be95989-2dd8-a370-cae9-cf9d9eb7534f.png)

プレビューをするとデータが参照できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/9afe660c-4c02-efbf-b740-30fd4f4519b5.png)


テスト環境
CP4D 4.0.1
LaravelのAPIをNuxtでよんでバリデーションエラーが発生した時302になる

Nuxtでなくてもなると思いますが、LaravelのAPIをよんでバリデーションエラーが発生した場合、
422とステータスコードをする仕組みをLaravelは持っているようですが、
APIじゃないと誤認すると302のリダイレクトになります。
どう言うロジックでAPIであると判定しているか多分以下↓

```php
    /**
     * Determine if the current request probably expects a JSON response.
     *
     * @return bool
     */
    public function expectsJson()
    {
        return ($this->ajax() && ! $this->pjax() && $this->acceptsAnyContentType()) || $this->wantsJson();
    }
```

これを追うと、
リクエストヘッダーのContent-Typeをapplication/jsonを設定するか、X-Requested-With: XMLHttpRequestにするか、または両方設定するかのようです。
Nuxtのデフォルトヘッダーでしっかり設定して302にならないようにしておきましょう。
【カオピーズの新技術】機能性AI-OCRエンジンYomokkaで省力化、高速化、生産性向上！

株式会社カオピーズは、これまでAI開発で積み上げてきた画像解析技術、研究開発経験を活かし、2020年に発表した「**Kaopiz OCR**」を改良し続けてきました。そして2021年、**Kaopiz OCR**は新たに手書きOＣR機能などを盛り込んだ「**<a href="https://kaopiz.com/ja-news-kaopiz-ocr-engine-yomokka/">機能性AI-OCR　Yomokka</a>**」として再誕しました。
**Yomokkaは活字・手書きを問わず、あらゆるフォーマットの書類から指定した情報を⾃動的に識別し、99.5％までの高精度で読みとることができます**。

**機能性AI-OCRとは？**
「機能性AI-OCR」とはどんなフォーマットの書類からでも、必要情報を自動的に探し出して読み取ることができるカオピーズの技術です。従来型のOCRではあらかじめ読み取りたい部分を範囲設定する必要がありましたが、この技術によりYomokkaは文字の位置関係やキーワードから必要情報と関連項目を自動的に判断し、読むことができます。
Yomokkaの対応範囲は広く、身分証明書などの定型文書はもちろん、名刺や請求書などの取引先ごとにフォーマットが異なる非定型文書や、アンケート用紙に書かれた手書き文字なども柔軟に読み取ることが可能です。
![Yomokka-engine.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1217452/311573cd-e0b0-4b68-da37-df3a4f04a99a.png)
**機能性AI-OCR Yomokkaの特徴**
カオピーズのOCRはデビューから2021年現在までの1年間、多くの事業者様に信頼され、採用・導入されてまいりました。またお客様と技術や製品を共同開発するたびにエンジンを絶えず改良し続けています。今回新たに対応した手書き読み取り機能のほか、機能性AI-OCR　Yomokkaには以下のような特徴があります。

**①　どんな書類、どんな画像からも柔軟に読み取り可能**
汎用型エンジン以外に、パスポートや免許証などの各種特化型エンジンもご用意しております。

**②　必要な情報を⾃動的に探して読み取り可能**
取引先ごとにフォーマットが異なる書類も、一枚ずつ範囲指定をせずに、まとめて読んで簡単にデータベース化することができます。

**③　読みたいキーワードに関連する内容・値を判断**
請求書などは、表内のキーワードと縦横の位置関係から内容を判断し、読み取ります。

**④　手書き文字、国内で一般的に使用されている殆どのフォントに対応**
横文字であれば、活字と手書き文字が混在していても読むことができます。

**⑤　スマートホンで撮影した画像からも読み取りが可能**
カオピーズの高精度の画像処理技術で、斜めに置かれた書類や、台形画像を補正し、内容を読み取ります。

**⑥　読み取り精度は業界トップクラス！**

**⑦　ご要望に沿ったカスタマイズを行い、お客様専⽤のエンジンも開発可能**
![Yomokka-精度.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1217452/b8d502b2-229a-3845-4e22-29c38f9d67a5.png)
自社サービスへの組み込みや、OCR以外の技術と組み合わせた導入などのご相談も承っております。
**▶ カオピーズのAI-OCRのデモサイト：https://ocr.kaopiz.com/**

**「その書類、Yomokkaで読もっか？」**
**「Yomokka」- Kaopiz OCRは、従来のOCRにAIを搭載した、新しいタイプの文字読取りエンジンです。
さまざまな文章を高精度で認識し、正確にデジタルデータ化することで、業務効率の向上やコスト削減に大きく貢献いたします。**
Yomokkaという名前は口語である「読もっか？」に由来します。Kaopizの機能性AI-OCRはいつでもご利用者様のすぐそばにあり、まるで親しい友人のように、気軽に読み取りを行う存在でありたいというメッセージが込められています。

文字読取りAIエンジンを導入して業務効率化を図りたい方やDXを促進させたい方はお気軽にカオピーズへお問い合わせください。

**※カオピーズではOCR以外のシステム開発のご相談も幅広く承っております。弊社株式会社カオピーズは、2014年9月に創業してから7年目を迎え、お客様に高品質な<a href="https://kaopiz.com/ja-system-development/">システム開発</a>、<a href="https://kaopiz.com/ja-digital-transformation/">DX推進支援</a>、<a href="https://kaopiz.com/ja-aws-cloud-service/">AWS導入支援</a>、<a href="https://kaopiz.com/ja-ai-image-recognition/">AI画像認識</a>等の<a href="https://kaopiz.com/ja-offshore-service/">オフショア開発</a>サービスをリズナーブルな価格で提供しています。お客様のご要望をヒアリングした後、それに見合った開発基盤、プロセス、体制などを提案し、お客様と合意した上、システム開発を行なっていきます。多くの企業様を支え続けてきた300案件以上からくる豊富な知見とノウハウで、ご要件に見合ったクラウドサービスの提案からクラウド環境の設計、構築、運用をまでサポートいたします。カオピーズにとって、案件の初期のみならず、開発期間中に出てくる課題を解決し、要件変更に対して最適な対策を取るなどが必要です。そこで、システム開発に関してお悩みがあれば、是非お気軽にご相談ください。**
Tailwindで青枠を消す方法

# 起こっている問題
Tailwindを使っていると、ボタンを押したときに青枠が出てきます。
設定しなくても勝手に出てくるので、消す方法を探しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/456505/6c81d554-e54f-c420-b88d-6152e7426da8.png)


# 修正方法
``tailwind.css``の下記のコードをコメントアウトします

### 修正前
```css:tailwind.css
button:focus {
  outline: 1px dotted;
  outline: 5px auto -webkit-focus-ring-color;
}
```

### 修正後
```css:tailwind.css
/* button:focus {
  outline: 1px dotted;
  outline: 5px auto -webkit-focus-ring-color;
} */
```

# 終わりに
これで青枠が消えました。
地味に気になるので、消えて良かったです！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/456505/d093467b-5d9f-6044-0e26-49edb3d1b8ef.png)
IOT: MQTT クライアントのサンプル

IOTのサンプルの仕様を作ってみました。
実装は、別ページで紹介します。

ブローカーを
>example.com

トピックを
>example/test01

とします。
認証は使いませんから、パブリック MQTT ブローカーでテストができます。

クライアントは、ブローカーに subcribe して MQTT の内容により、次の動作をします。

1) led を　ON にする。
　
 次の payload を受け取ると、

>```json
{"state": {"desired" : {"led" : "on"}}}
```

LED　を ON にし、

次の payload を publish します。

>```json
{"state": {"reported" : {"led" : "on"}}}

2) led を　OFF にする。
　
 次の payload を受け取ると、

>```json
{"state": {"desired" : {"led" : "off"}}}
```

LED　を ON にし、

次の payload を publish します。

>```json
{"state": {"reported" : {"led" : "off"}}}

##検証スクリプト##

```bash:publish_on.sh
#
#	publish_on.sh
#
BROKER="example.com"
TOPIC="example/test01"
message='{"state": {"desired" : {"led" : "on"}}}'
#
mosquitto_pub -d -t orz -m "${message}" -h ${BROKER} --topic ${TOPIC}
#
```

```bash:publish_off.sh
#
#	publish_off.sh
#
BROKER="example.com"
TOPIC="example/test01"
message='{"state": {"desired" : {"led" : "off"}}}'
#
mosquitto_pub -d -t orz -m "${message}" -h ${BROKER} --topic ${TOPIC}
#
```

##MQTT のモニター##

```bash:subscribe.sh
#
#	subscribe.sh
#
BROKER="example.com"
TOPIC="example/test01"
#
mosquitto_sub -d -t orz -h ${BROKER} --topic ${TOPIC}
#
```

##実装例##

[Python3: MQTT クライアントのサンプル](https://qiita.com/ekzemplaro/items/0f43e837907470dddb8b)
[Node.js: MQTT クライアントのサンプル](https://qiita.com/ekzemplaro/items/7efd2fedebe63ca55606)
インタフェイスのプロパティを使いたい場面

#初めに

C#のインタフェイスには、プロパティを定義できるというユニークな機能がある[^1]。
メソッドを定義するのは分かるけど、この機能はいったい何の得があるのか、~~一つだけ~~例を見つけたので共有する。

#ASP.NET MVCのページング用プロパティ
検索結果一覧につきもののページングは、汎用性が高いパーツの一部である。複雑なシステムの場合、多数のページでページングが必要になる。だからと言って、それぞれのページ用にページングを実装するのはコストがかかる。
そこで考えられるのがページング専用のクラスだが、これは面倒な多重継承問題があるので使いたくない。

つまりインタフェイスの出番である。インタフェイスで必要な値を渡せれば、クラスのように扱うことが可能だ。

``` サンプル.CS
public interface IPager
{
    int PageNo { get; set; }
    int MaxPages { get; set; }
}

public class HogeViewModel : IPager
{
    // モデルで使う（リストなど）プロパティは省略
    public int PageNo { get; set; }
    public int MaxPages { get; set; }
    // インタフェイスなので実装が必要
}
```
このようにすれば、ページャ生成用のパーシャルビューやヘルパに必要なプロパティのみを渡すことが可能になる。

#ジェネリック型のファクトリメソッド
上を一般化した、というよりこちらがインターフェイスの本領だと思われる。
例えば、以下の様なクラスを定義したとする。

``` 型.cs
public class Base
{
    public int Foo { get; set; }
}

public class Derived: Base
{
    public double Bar { get; set; }
}
```

この`Base`型を戻り値にしたメソッドを作成する。

``` メソッド.cs 
public static Base Create(int i)
{
    return new Base{ Foo = i };
}

static void Main(string[] args)
{
    Derived baz = Create(1); // ダウンキャストができないのでエラー
}
```

C#はダウンキャストが基本的にできないのでこのような書き方は不可能（実用的には割と使うパターンなのに！）。
これをインタフェイスに変えてみても、インタフェイスの実装がBaseなのでDerivedに変換不能である。

``` インタフェイス実装.cs
public Interface IBase
{
    int Foo { get; set; }
}

public class Base: IBase
{
    public int Foo { get; set; }
}

public class Derived: IBase
{
    public int Foo { get; set; }
    public double Bar { get; set; }
}

public static IBase Create(int i)
{
    return new Base{ Foo = i };
}

static void Main(string[] args)
{
    Derived baz = Create(1); // Create戻り値の中身はBaseなのでDerivedにできない。エラー
}
```

このようなときは`Create`をジェネリック化する。

``` メソッド.cs 
public static T Create<T>(int i): where T: IBase, new()
{
    return new T{ Foo = i };
}

static void Main(string[] args)
{
    Derived baz = Create<Derived>(1); // ジェネリックメソッドに型情報を教える必要がある
}
```

この規模なら`Derived`がBaseを直接継承して、`T`の型制約を`Base`にした方が楽[^2]ではあるが、インタフェイスは複数継承できるので、大規模開発ならインタフェイスを使う意味があるかもしれない。

#まとめ
上記で示した通り、使用頻度が高いプロパティはインタフェイスにまとめると、コード全体の汎用性が増す。
また、例えばページング用プロパティの他にも検索条件用のインタフェイスも実装できるので、型の制約に捉われないメソッドを使用できるようになる。

欲を言えば、Visual Studio（2015です）でインタフェイス実装宣言をした際に自動でプロパティを生成できるといいなと思った。クイック操作で追加されるプロパティは`NotImplementedException`付きなので使い辛い。自動実装プロパティにしてほしい。

#更新履歴
2021/12/01 「ジェネリック型のファクトリメソッド」項追加。

[^1]: Javaの場合、プロパティはアクセサをメソッドで実装することになるから、ある意味Javaにも存在する機能ではある。他の言語はよく知らないので、そこまでユニークというわけでもないかも。

[^2]: その場合でも、`Base`を継承する方全てがパブリックのコンストラクタを持っているとは限らないので、`new()`制約は外せない。
データの全体像を見るときはPandas Profilingが便利

最近Pandas Profilingという便利なライブラリがあることを知りました。
備忘録も兼ねて使い方をまとめておきたいと思います。

## インストール方法
pip, condaいずれでもインストール可能です。

pipの場合
```pip install pandas-profiling
```
condaの場合
```conda install -c conda-forge pandas-profiling
```

## 実行方法
試しにsklearnのBoston Housingデータを使ってみました。

```python
#使用するライブラリ
import numpy as np
import pandas as pd
from pandas_profiling import ProfileReport
from sklearn.datasets import load_boston
```

まずはBoston Housingデータをデータフレーム化します。

```python
boston = load_boston()
df = pd.DataFrame(boston.data, columns=boston.feature_names)
```

このデータフレームを```ProfileReport```に入れることで、データ全体の相関など様々な統計量を表示してくれます。

```python
ProfileReport(df)
```
jupyter notebook上に表示された結果の一部。変数の個数だけでなく、dfの行数や欠損値、重複行の有無なども教えてくれます。

![スクリーンショット 2021-10-05 11.05.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/374715/6b2633cd-dd18-d957-84f9-8c95d84dd171.png)

出力結果のgif。変数間の相関なども可視化されています。

![output_5f.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/374715/36ac364e-eaa9-29e6-fd58-a328a0b5f6c7.gif)



最後に、得られた結果はhtmlやjsonとして保存ができます。

```python
profile = ProfileReport(df)
profile.to_file('boston.html')
```

##　参考
[Pandas Profiling](https://github.com/pandas-profiling/pandas-profiling)
Reactにおけるスプレット構文（...name）

![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654025/a2f85e68-0a0c-c4d6-6d36-d8546cf88e4a.png)



#スプレッド構文

###スプレット構文を使うと配列をマージしてくれてとても便利です。
mapメソッドも使ってすべての要素をブラウザに表示させてみます。


```react:Spread.js
import React, { useState } from 'react'

export const Spread = () => {

  const [items, setItems] = useState([]);
 
  const addItem = () => {
    setItems([...items,
    {
      id: items.length,
      value: Math.floor(Math.random() * 10) + 1
    }])
  }
  
  return (
    <div>
      <button onClick={addItem}>Add a number</button>
      <ul>
        {
          items.map((item) =>
            <li key={item.id} >id: {item.id} ; Number {item.value}</li>)
        }
      </ul>
    </div>
  )
}
```
#実際の動き

![2021-10-04_19h04_17.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654025/2bfc6f6a-c499-6a96-54a0-875cda9a6abe.gif)
Windows 11においてPowerShellが文字化けする事象

2021/10/5にWindows 11がリリースされました。
レイアウトが大きく変わると最初は戸惑いますね。。

#PowerShellの日本語が文字化けする問題
Windows 11にアップグレードしてPowerShellを開くと日本語が文字化けします。
こちらの事象はWindows server 2022でも発生していた事象です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/258122/96e3da68-3e67-6078-bbbd-7ec331b27d4e.png)

詳細はMicrosoftのサポートチームが記事を書いてますので、参照いただければ。
https://jpwinsup.github.io/blog/2021/08/19/UserInterfaceAndApps/PowerShell/PowerShell-Console-characters-garbled-for-CJK-languages/

#回避策
2つありますが、簡単な方を。
1. PowerShellを開きます。
2. ウィンドウを右クリックにて選択し、プロパティを選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/258122/1066f97d-3c2c-39dc-5953-11c923b7f8bc.png)
3. フォントを選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/258122/ca44d2b9-19a6-5184-dc3f-ed03ab04e518.png)
4. フォントを「MSゴシック」に変更します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/258122/6ca74ff7-fcba-b2dd-c5e5-05e954c90f72.png)
5. OKを選択します。
6. 文字化けが直りました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/258122/7ba7721c-3788-5ef8-93c0-19d017628286.png)
Windows Server 2016でdateの戻り値にはまった for 社内環境でネットできない人

##1.はじめに
本当に本当にビギナー。かつ、社内環境はネット出来ないし、家に帰らないと自由に触れる環境が無い人向けです。ちょっと手の動くエンジニアなら詰まる事はないので・・・　読まずにそっと閉じてください。。。

##2.日本語環境でのecho
…タイトルの通りなんですが、社内環境ってネット接続できないサーバーが多いですよね。で、よそ（海外）でサーバーを借りたんだけれども、社内のテスト環境でどうしても設定が再現できず。家で立てているWindowsServer2016(評価版)の設定をいじったら、素直に反映されたというお話。。。
![echo_before.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/620073/3985cd20-7c54-ec18-7cf9-27ace3961aa2.png)
まぁ、当然なんですが、特にLang周りを触ってないとこういう戻り値になります。

##3.ネットできない環境だとここで詰まります（当たり前ですが。。。）
United States の言語設定を最上位にして満足していたんですが、その先の言語パッケージのインストールも必須で、ここが社内だとネット出来ずに止まっていました。。。
![LangUnitedStatsl.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/620073/cf68e2d0-ee12-4ded-4d8c-de7ebdfb8821.png)
##4.英語化した後の姿
まさに、この画面の「言語パックをダウンロードしてインストールします」です。
![LangUS_installl.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/620073/27a05251-d9f0-59a7-7d3b-ca9ad8c1ecfd.png)
インストールが終わると「再ログイン後に反映されます」と言われるので、念のためリブート。
![servermanager.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/620073/6c179164-d684-d311-b17f-9c00fa40ff64.png)
ひゃっほー　サーバーマネージャが英語化されました。
で、この後はLang周りをどんどんとUSA化してゆきます。そして、最後。
![lang.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/620073/29a23608-41b9-c527-2b3a-b10195e09aab.png)
Format: を言語設定に合わせるにセット。

![echo_after.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/620073/9730c9fc-fb56-863b-b49c-1c4dc156ae18.png)

海外から借りてきた環境と同じ戻り値を得られました。。。
AWSクラウドプラクティショナー受験記

AWS認定のクラウドプラクティショナー（
CLF）に合格しましたので、勉強方法などの受験記を残します。

## AWS CLF概要
AWSによる認定資格で、役職別の基礎コースにあたる資格。

* 試験時間：90分
* 受験料：100USドル
* 実施日：通年
* 出題形式：選択式（複数選択の問題あり）
* 出題数：65問 
* 受験方法：テストセンター or リモート

※いずれも2021/10/4現在の情報です

▼公式サイトリンク
[AWS認定](https://aws.amazon.com/jp/certification/)
[AWS Certified Cloud Practitioner](https://aws.amazon.com/jp/certification/certified-cloud-practitioner/)

## 受験者スペック
* エンジニア歴10数年
* 簡単なスクリプトを書ける
* システム設計がおおまかにできる
* AWSの知識・実務経験
  * S3をなんとなく知っている（CDNだと思っていた）
  * 手順書に沿ってELBやS3バケットの操作をしたことがある

## 結果
812点で合格しました。700点が合格ラインなので、結構危なかったですね。

⚠注意⚠
どの問題が間違っていたかはフィードバックされません。また、受験時はメモなども一切持ち帰れないため答え合わせもできません。

## 学習コストまとめ

|内容|コスト|
|-|-|
|学習時間|42時間|
|期間|約1ヶ月（2021/9/2〜2021/10/3）|
|受験費用|12,100円（2021/9/15時点のレートに基づく100USドル分）|
|教材費用|4,088円（内訳は次項にて）|

ちなみにVUEピアゾンで受験しました。

## 使った教材や勉強方法
|教材名・リンク|用途や勉強方法|費用|
|-|-|-|
|AWSコンソール|サービスそのもの。無料利用枠の範囲内で軽くハンズオン。|0円|
|[AWS認定資格試験テキスト　AWS認定 クラウドプラクティショナー](https://www.amazon.co.jp/AWS%E8%AA%8D%E5%AE%9A%E8%B3%87%E6%A0%BC%E8%A9%A6%E9%A8%93%E3%83%86%E3%82%AD%E3%82%B9%E3%83%88-AWS%E8%AA%8D%E5%AE%9A-%E3%82%AF%E3%83%A9%E3%82%A6%E3%83%89%E3%83%97%E3%83%A9%E3%82%AF%E3%83%86%E3%82%A3%E3%82%B7%E3%83%A7%E3%83%8A%E3%83%BC-%E5%B1%B1%E4%B8%8B-%E5%85%89%E6%B4%8B/dp/4797397403/)|知識をつけるベースとして。1回通して読み、練習問題を2周解いた。これだけで網羅するのは難しい。|2,618円|
|[AWS公式サイト](https://aws.amazon.com/jp/)|サービス内容や料金のディクショナリーとして、なにか気になれば都度検索して内容を確認していた。|0円|
|[クラウドプラクティショナー 模擬試験 CLF-C01](https://play.google.com/store/apps/details?id=aws.cloud_practitioner.exam)|問題集として2周解いた|720円 (全問題アクセスのアプリ内課金)|
|[AWS認定資格 無料WEB問題集＆徹底解説 クラウドプラクティショナー](https://aws-exam.net/clf/)|問題集|0円|
|[Web問題集でAWS試験に合格しよう](https://learn-aws.cafe/)|問題集|0円|
|[AWS認定　クラウドプラクティショナー　模擬問題集 Kindle版](https://www.amazon.co.jp/dp/B081D4ZJ5G/)|仕上げの模擬試験として1回解いた。|750円|
|twitter|独自のハッシュタグをつけて、特に間違えた問題のポイントをつぶやいた。|0円|

## 実際にCLFを受けてみての感想

### 本試験
試験時間は90分ですが、実際は30分ほどで解答と見直しまで終わりました。すべて選択問題でかつ、なにか計算したりということがないため早く終わるはずです。

難易度は [AWS認定　クラウドプラクティショナー　模擬問題集 Kindle版](https://www.amazon.co.jp/dp/B081D4ZJ5G/) よりも少し優しく感じました。ちなみに同問題集で2つある模擬試験の正答率が84%と67%と、若干不安を残す状況で受験しました。特に複数選択問題が模擬試験に比べて少なく、感覚的に10問に1つあるかないかくらいでした。

### 学習のポイント
AWSはとにかくサービス数が多いため、これを網羅すればOK！という線引きが難しいです。AWS認定資格試験テキストで出てきたサービスは確実に押さえた上で、問題集を解く→出てきたサービスやコンポーネントをAWS公式サイトで検索する　を繰り返しました。

その他、押さえるべき点を列挙します。

* よく問題に出るサービスの配置先を覚える
  * 例）EC2はAZ、S3はリージョン
* サービスの利点について、導入するとどう楽になるのか、何の要素（伸縮性？コスト？）が向上するのか叩き込む
* セキュリティなのか伸縮性なのかコストなのか、など、問題文の論点をしっかり把握する癖を身につける
  * 例）伸縮性のことを問われているのにAuto Scalingを選択してしまうと誤りだったりする
* 責任共有モデルの、AWSの責任とユーザーの責任をしっかり叩き込む
* コスト周りのツールが混同しがちなので、機能と利用要件を整理する
  * 例）Cost Exploler、AWS のコストと使用状況レポート、Budgets
* サポートの内容は各プランの内容をしっかり叩き込む

### ハンズオンのすすめ
無料利用枠の中でできる限りやることを推奨します。筆者はAMIからEC2インスタンスを作成し、インターネットからHTTPSアクセスできるまでと、ELBと組み合わせたAuto Scalingの設定までをやりました。VPC・サブネット・セキュリティグループはネットワークに疎い方にはとっつきにくいかもしれないため、ハンズオンしてみると良いです。

⚠注意⚠
EC2インスタンスを起動しっぱなしにして無料枠の月750時間を超えたり、EC2インスタンスを停止してもElasticIPをリリースしていないと課金されてしまいますので注意してください。筆者は、やらかして2ドルほど請求が来てしまいました汗

  
これから受験を考えている人、受験する人の参考になれば幸いです。
IBM Cloud: IAMにおけるIBM Cloud ConsoleへのIPアドレスによるアクセス制限方法

#1. はじめに
IBM Cloudのcustomer portalへのアクセス制限については、

- ユーザーレベルでのIPアドレス制限![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62127/1778749d-6618-7c70-615c-4a354d887bbc.png)
- アカウントレベルでのIPアドレス制限![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62127/1e9c1085-939f-20ab-2930-6297dfe4cfcd.png)

の2つが存在する。この制限方法の仕様について挙動を確認してみた。

参考ドキュメント
https://cloud.ibm.com/docs/account?topic=account-ips&interface=ui


#2. 検証結果
##検証1: 新規ログインとログイン済みのセッションの挙動

||テストケース概要|結果|
|:--|:--|:--|
|1|許可しているIPアドレスからの新規ログイン  |  ログイン可能 |
|2|許可していないIPアドレスからの新規ログイン  |  ログイン不可 。ただし、以下の矢印から別アカウントにスイッチは可能![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62127/a308f431-ead1-24cc-47dd-8c41a1b4fc45.png)|
|3|別アカウントに新規ログインした後に、対象アカウントにスイッチ  | スイッチ不可。 ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62127/6ff3ac6b-ccc1-aa46-8ebf-83e4d2918579.png) |
|4|すでにログイン済みの状態で、そのユーザーに対して既存のIPアドレスからアクセスできないように設定   | そのままアクセスできた。一旦ログアウトして再度ログインしたらログイン不可となった。  |
|5|アクセスを許可しているアドレスから一旦ログインした後、端末のIPアドレスを許可されていないアドレスに変更  |そのままアクセスできた。このあと、他のアカウントにスイッチして戻ってきても、やはりブロックされずにそのままアクセスできた。  |


以上の結果より、IPアドレス制限のチェックはログインの時にしか実施しておらず、一旦ログインさえできてしまえばそのセッションは影響を受けないようだ。
逆に5の結果からすると、一旦ログインできた状態で、その端末を別の場所に持ち出しても継続して一定時間はアクセスできてしまうことがわかる。デフォルト値は以下のようになっているため、場合によってはこのあたりのセッション時間を短くすることも考慮した方が良さそうだ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62127/ce13394b-3161-7b4a-8022-7a6ccd3ddebe.png)


##検証2: ユーザーレベルの設定とアカウントレベルの設定の組み合わせ


||テストケース概要|結果|
|:--|:--|:--|
|1|アカウントレベルでは許可されているが、ユーザーレベルでは許可されていないIPアドレスからの新規ログイン |  ログイン不可|
|2|アカウントレベルでは許可されていないが、ユーザーレベルでは許可されているIPアドレスからの新規ログイン |  ログイン不可|
|3|アカウントレベルでもユーザーレベルでは許可されているIPアドレスからの新規ログイン |  ログイン可|

ユーザーレベルがアカウントレベルより優先される訳でもなく、両方設定している場合は両方の条件が満たされていないとログインできなかった。基本のアクセス制限はアカウントレベルで一律に実施し、特別なユーザーに限ってはさらにそこから絞り込むという使い方を想定しているのかもしれない。[IBM Cloud docs](https://cloud.ibm.com/docs/account?topic=account-ips&interface=ui)にも以下のような記載があった。



:::note warn
If an IP address restriction is defined for both the account and the user, the IP address needs to match both specifications to be able to generate an IAM token.
:::
【学習記録】入力の配列による保持 （paizaランク D 相当）

## 目次
- 問題文
- 書いたコード
- 解答例
- 学んだこと
- 振り返り


背伸びしてチャレンジしたらBランクの問題が解けた…！！
のですが、時間かけすぎて?ランク認定されず。
悔しいのと、基本的な型や関数の理解がボロボロ抜けてるいうことが分かったので、スキルチェックではなく解答例が見れるpaizaラーニングでもう少し基礎を固めます。


### 問題文 
[詳細はコチラ](https://paiza.jp/works/mondai/c_rank_level_up_problems/c_rank_std_in_out_step4)

>整数 n と n 個の数 a_1, ..., a_n が改行区切りで与えられます。与えられた a_1, ..., a_n の中で最も大きい数を出力してください。

>入力値
n
a_1
...
a_n

>解答例
3
20
19
2

>出力例
20

要約すると、n個整数が与えられてその中から大きい値を出力せよ、という問題。

## 書いたコード
``` main.py
count = int(input())
ans = 0

while count > 0:
    b = int(input())
    if b > ans:
        ans = b
    count -= 1
print(ans)
```

whileループの中で受けた値を一つ一つ評価している形。

## 解答例

``` main.py
n = int(input())

A = [0] * n

for i in range(n):
    a = int(input())
    A[i] = a

print(max(A))
```
Aには何が…？

>受け取った a_1, ..., a_n をひとつのリストで保持したいので、最初に A = [0]*n として、長さ n のリストを作っています。[0]*n は「0 が n 個並んだリスト」を作るときの書き方です。

なるほど、n = 2であれば `[0, 0]`の形でリストを作ってるんですね。
forループ内で上書きして、max関数で最大値を抜き出す…と。

## 学んだこと
1. `[i] * n`　でリストが作れること
1. max関数の存在

## 一言
ポートフォリオでも今回同様、ループ内で評価する、という書き方をしていたのでリファクタリングのチャンス。
実行時間も毎回評価するより、max関数で一発で見た方が早くなりそうな印象。
Pythonは簡単に実行時間確認できたはずなのでそちらも調べてみたいと思います。
pandasで複数CSVファイルを読み込んで、結合する


```ruby:python
import os
import pandas as pd

#ゲットファイルリスト
path = 'testData/'
files = os.listdir(path)
train_csv = list(filter(lambda x:(x[0:6] == 'train_' and x[-4:] == '.csv'),files))

#ゲットCSVデータ
data_list = []
for fileitem in train_csv:
    tmp = pd.read_csv(path + fileitem,header=0)
    data_list.append(tmp)

#結合
dataset = pd.concat(data_list,ignore_index = False)
```
Oracle 統計情報のロック方法

ほとんどのケースでは統計情報は自動取得に任せてしまっていいんですが、処理の作り方によっては特定の時点の統計情報で固定しておきたい、って場合が出てきます。

たとえば、夜間の自動統計取得時には0件なのに、早朝のバッチ処理の中で大量のデータを取り込んで回すような処理では、夜間の統計ではなく、データ取り込み後の統計を使いたいものです。
もちろん、取り込み後に統計を取り直すのが王道かもしれませんが。

# 統計がロックされているか確認する

```
SELECT TABLE_NAME, STATTYPE_LOCKED, NUM_ROWS, LAST_ANALYZED FROM USER_TAB_STATISTICS WHERE OWNER = 'XXXXX' AND TABLE_NAME = 'XXXXX';
```

STATTYPE_LOCKEDがALLになっていたらロックされてます。

# 統計情報をロックする
```
EXEC DBMS_STATS.LOCK_TABLE_STATS('SCHEMA_NAME','TABLE_NAME');
```

# 統計情報のロックを解除する
```
EXEC DBMS_STATS.UNLOCK_TABLE_STATS('SCHEMA_NAME','TABLE_NAME');
```

--------------------
# DatapumpでMETADATA_ONLY（定義だけ）インポートをすると統計がロックされる罠
意識してロックした覚えはないのに統計情報がロックされていたので原因を調べました。

https://docs.oracle.com/cd/F19136_01/sutil/datapump-import-utility.html

>METADATA_ONLY: データベース・オブジェクト定義のみがロードされます。表の行データはロードされません。CONTENT=METADATA_ONLYを指定した場合、ダンプ・ファイルからインポートされる任意の索引または表の統計は、インポート操作の完了後にロックされることに注意してください。
Oracle 統計情報を移植する

# 移行元でエクスポートする

■移行元DB

```
EXECUTE DBMS_STATS.CREATE_STAT_TABLE(OWNNAME=>'schemaname', STATTAB=>'STAT_TABLE', TBLSPACE=>'USERS');
EXECUTE DBMS_STATS.EXPORT_SCHEMA_STATS(OWNNAME=>'schemaname', STATTAB=>'STAT_TABLE', STATID=>NULL, STATOWN=>'schemaname');
```

統計情報を出力するワークテーブルSTAT_TABLEを作成し、統計情報をエクスポートするイメージです。

# 移行先でインポートする

■移行先DB

```
EXECUTE DBMS_STATS.CREATE_STAT_TABLE(OWNNAME=>'schemaname', STATTAB=>'STAT_TABLE', TBLSPACE=>'USERS');
INSERT INTO STAT_TABLE SELECT * FROM STAT_TABLE@DB_LINK;
EXECUTE DBMS_STATS.IMPORT_SCHEMA_STATS(OWNNAME=>'schemaname', STATTAB=>'STAT_TABLE', STATID=>NULL, STATOWN=>'schemaname');
```

移行先でも同じプロシージャでワークテーブルSTAT_TABLEを作成し、移行元からDBLINK越しのINSERTを行います。
本番⇒検証の場合はファイアウォールがあるため、逆向きでINSERTします。

■移行元DB

```
INSERT INTO STAT_TABLE@DB_LINK SELECT * FROM STAT_TABLE;
```

DBLINKが使えない環境の場合は、テーブルを個別にEXPDPでダンプ出力して持ってきてもいいと思います。
Let's encryptのルート証明書が9月末で期限切れになった影響で、apt-get updateに失敗する問題が発生している件について


## エラーの内容
こんな感じで、Dockerビルドして、CI/CD走らせていたのに、突然、apt-get updateできなくなった。

```
##[error]The command '/bin/sh -c apt-get update && apt-get install gettext-base' returned a non-zero code: 100
##[error]The process '/usr/bin/docker' failed with exit code 100
```

なんだかみんななっているらしい。
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">Let&#39;s encryptのルート証明書が9月末で期限切れになった影響で、apt-get updateに失敗する問題が発生している <a href="https://t.co/ukyi1dRkX8">https://t.co/ukyi1dRkX8</a></p>&mdash; dada (@dada4386) <a href="https://twitter.com/dada4386/status/1443749945945649181?ref_src=twsrc%5Etfw">October 1, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


## 対処

dockerの中のubuntuを使っている場合

```docker
RUN apt-get update; apt-get install ca-certificates
```

ubuntuを使っている場合

```sh
apt-get update; apt-get install ca-certificates
```

ubuntuが公式で修正してくれたら、これはしなくていいはず。
初学者必見！本番環境だけNoMethodError undefined method

![スクリーンショット 2021-10-05 9.48.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653881/dfc014d0-de0f-1b61-bcc4-2663a71abf39.png)

このように本番環境だけ、undefined methodエラーがでた。


trades_countカラムをローカルで追加したので、


本番環境のターミナルで

```
 rails db:migrate RAILS_ENV=production
```

を実行すれば本番環境でカラムが追加されるので、
エラーが解決できる。
登録画面を作りします

今回は前回リスト画面の続きで登録画面を作りします。
1.	Add WebScreen画面を作りします。画面名をPersonAddとします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1209998/02de4929-e358-e0ac-d770-8bc649c16c2a.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1209998/de164806-0f87-0564-ccf6-04956c3dd03f.png)

PersonAdd画面を右クリックして、Add Local Variableをクリックして、画面変数を作りします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1209998/22e3592e-7ae5-20ba-86e9-b4e16bcad96e.png)
画面変数のnameをpersonにネーミングします。
DataTypeはEntityのPersonを指定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1209998/71a77f04-ffee-b9c7-1262-5913799e0ee8.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1209998/6f4b8628-3a60-e97b-d46a-f308cab0df72.png)
Widege boxからForm のwidegeと三つinputのwidegeをPersonAdd画面に追加します。
追加後のイメージ図。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1209998/2aea0ea4-7fdb-48cc-bafb-4da65305f983.png)
FormのSourceRecordは変数personを指定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1209998/473ef6b2-f34f-f997-1d03-bbacdd6dbb67.png)
Widege boxからbuttonのwidegeをPersonAdd画面に追加します。
ButtonのDestinationをNew Sceen Actionをクリックして、作成後はSavePersonActionにネーミングします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1209998/d6a48433-a7f4-0f54-f92e-30ee5798b770.png)
データを登録ために、SavePersonActionにPerson EntityのCreatePersonを画面に追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1209998/3a285491-ec26-9bd2-d767-8bfcde7e377d.png)
CreatePersonを追加後、SourceはForm1.Recordを指定します。

widegeBoxからDestinationをSavePersonActionに追加します。
追加後、Destination属性を「MainFlow\ListGamen
」で指定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1209998/698338e8-daf7-1088-da33-5649cc534bc1.png)
list画面から追加画面に遷移のボタンを追加して、destinationの属性はMainFlow\PersonAddを指定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1209998/711bb679-47d4-80fd-a6c9-589d4fbb35f0.png)
最後は1-Click Publishして、テストします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1209998/59b184ae-86ae-1abe-6203-fd020dcf72e9.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1209998/1a6efebc-88b3-d301-0b44-e73ae25aad97.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1209998/0f2cddd3-a85b-a891-e950-77172559d237.png)
TCAのReducerからスクロール位置を変更する

# 概要

[TCA](https://github.com/pointfreeco/swift-composable-architecture)のReducerからスクロールビューのスクロール位置の操作をする方法がわからなかったのですが、NotificationCenterを使ったら出来ました。
他にもっと良い方法はあるかもしれません。

# ソースコード
github: https://github.com/k-yamada/TCAScrollFromReducer

```NSNotification+.swift
extension NSNotification {
    static let scrollUp = Notification.Name.init("scrollUp")
    static let scrollDown = Notification.Name.init("scrollDown")
}
```

```AppView.swift
struct AppView: View {
    let store: Store<AppState, AppAction>
    let items = (1...100)

    var body: some View {
        WithViewStore(store) { viewStore in
            ScrollViewReader { scrollProxy in
                ZStack {
                    ScrollView {
                        ForEach(items, id: \.self) { Text("\($0)"); Divider() }
                    }
                    .onReceive(NotificationCenter.default.publisher(for: NSNotification.scrollUp)) { _ in
                        scrollProxy.scrollTo(items.first!)
                    }
                    .onReceive(NotificationCenter.default.publisher(for: NSNotification.scrollDown)) { _ in
                        scrollProxy.scrollTo(items.last!)
                    }
                    VStack {
                        Spacer()
                        HStack {
                            Button("Up") {
                                viewStore.send(.upButtonTapped)
                            }
                            Spacer()
                            Button("Down") {
                                viewStore.send(.downButtonTapped)
                            }
                        }
                    }.padding(EdgeInsets(top: 20, leading: 20, bottom: 20, trailing: 20))
                }
            }
        }
    }
}
```

```AppCore.swift
import ComposableArchitecture
import SwiftUI

struct AppState: Equatable {
}

enum AppAction: Equatable {
    case upButtonTapped
    case downButtonTapped
}

struct AppEnvironment {
    var mainQueue: AnySchedulerOf<DispatchQueue>
}

let appReducer = Reducer<AppState, AppAction, AppEnvironment> { state, action, environment in
    switch action {
    case .upButtonTapped:
        NotificationCenter.default.post(name: NSNotification.scrollUp, object: nil, userInfo: nil)
        return .none
    case .downButtonTapped:
        NotificationCenter.default.post(name: NSNotification.scrollDown, object: nil, userInfo: nil)
        return .none
    }
}
```
[Rails] Rspec ポートフォリオに実装したsystem spec

# はじめに
ポートフォリオに実際に実装したsystem specの部分です。
テストコードについては勉強したけどテストコードを実際に書く時にどんなことをテストすればいいか分からないと思ったのでそういった人の参考になれば嬉しいです。
テストしたのは投稿機能部分です。

## イベント投稿

```ruby:spec/system/event_spec.rb
require 'rails_helper'

RSpec.describe "Event",js: true ,type: :system do
  let(:user) { create(:user) }

  describe "イベントの表示" do
    let!(:events) { create_list(:event, 3) }
    subject { visit root_path } 
    it "イベント一覧を表示できる" do
      subject
      events.each do |event|
        expect(page).to have_css(".card-title", text: event.name)
      end
    end

    it "イベント詳細を表示できる" do 
      subject
      event = events.first
      click_on event.name
      expect(page).to have_css(".event-title", text: event.name)
      expect(page).to have_css(".card-text", text: event.text.to_plain_text)
    end
  end

  describe "イベント投稿" do
    context "ログインしている場合" do
      before do
        sign_in user
      end

      it "イベントを投稿できる" do 
        visit new_event_path 
        event = FactoryBot.create(:event, name: "name", text: "text")
        fill_in "イベント名", with: event.name
        fill_in_rich_text_area "イベント詳細", with: event.text

        click_button "作成する"

        expect(page).to have_content name
        expect(page).to have_content text
      end

      it "nameが未入力だとイベント投稿できず投稿ページにリダイレクトされる" do 
        visit new_event_path 
        event = FactoryBot.create(:event, name: "name", text: "text")
        fill_in  "イベント名", with: ""
        fill_in_rich_text_area "イベント詳細", with: event.text
        click_button "作成する"

        expect(current_path).to eq new_event_path 

      end
    end

    context "ログインしてない場合" do 
      it "イベント投稿を押すとログイン画面に遷移する" do 
        visit root_path
        click_on "イベント作成"

        expect(current_path).to eq new_user_session_path
      end
    end
  end

end 
```

## 解説
Capybaraの`fill_in`メソッドを使うことでフォームに値を入力、`click_button`メソッドで投稿ボタンを押すことで投稿できるかのテストをしています。

```ruby:spec/system/event_spec.rb
    it "イベントを投稿できる" do 
      visit new_event_path 
      event = FactoryBot.create(:event, name: "name", text: "text")
      fill_in "イベント名", with: event.name
      fill_in_rich_text_area "イベント詳細", with: event.text

      click_button "作成する"

      expect(page).to have_content name
      expect(page).to have_content text
    end
```

nameに`presence: true`のヴァリデーションを設定してるので投稿出来ず同じページにリダイレクトされるテストです。

```ruby:spec/system/event_spec.rb
    it "nameが未入力だとイベント投稿できず投稿ページにリダイレクトされる" do 
      visit new_event_path 
      event = FactoryBot.create(:event, name: "name", text: "text")
      fill_in  "イベント名", with: ""
      fill_in_rich_text_area "イベント詳細", with: event.text
      click_button "作成する"

      expect(current_path).to eq new_event_path 

    end
```

`fill_in_rich_text_area`というのは、今回投稿フォームにAction Textを使用しているのでAction Text内でもテストができるようにしたヘルパーです。
詳しくは以下の記事を参考にしてください。

https://qiita.com/tegnike/items/122c078c7cd1087ce7c3

以上です。
【第五回】コロナ禍だから何かできることをー　自宅療養者連絡ツール　ー

#前回までのあらすじ
[【第四回】コロナ禍だから何かできることを](https://qiita.com/STYLE-D/items/8af17d7f94f5c351ede0)

####実装イメージ図
LINEを利用して保健所の担当者の負担を軽減するとともに、自宅療養者はいつでもつながっている安心感を持たせるためのツールイメージです。
![メイン.00_00_51_12.Still002.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/744261/cf85391e-aef8-5a59-0a1d-63d8e15412ce.jpeg)

##ユーザー登録の仕組みを作ろう
![システム構成-ユーザー登録.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/744261/a834c741-8adf-06af-6c5b-a5ce167aa55a.jpeg)

LINE Developers
[LIFFアプリを開発する](https://developers.line.biz/ja/docs/liff/developing-liff-apps/)

###今回はLINEbotの設定部分の説明

まずは、LINEbotの設定するパスから。
チャンネルの設定
[LINE Developers](https://developers.line.biz/ja/)
メッセージ関連の設定
[LINE Official Account Manager](https://manager.line.biz/)

登録などはほかで詳しく説明しているため割愛します。

###チャンネルの設定
[LINE Developers](https://developers.line.biz/ja/)
重要な部分だけ抜粋

######[チャンネル作成時]
チャネルの種類は「LINEログイン」


######[チャンネル基本設定]タブ
| 項目 |設定値|
|:-|:-|
|リンクされたボット|前回作成したLINE Botを選択|

######[LIFF]タブ
| 項目 |設定値|
|:-|:-|
|LIFF URL|<strong>後で必要なので覚えておく</strong>|
|LIFFアプリ名|好きな名前で|
|サイズ|指定したページの大きさを設定します。大きい順に、Full・Tall・Compactで<br>今回は"Compact"に設定してます|
|エンドポイントURL|LINE上に表示するページがあるURLです<br>URLは<strong>https</strong>のみ有効となってます。|
|Scope |LINEの情報で取得する項目です。<br>詳しくは[こちら](https://developers.line.biz/ja/docs/liff/registering-liff-apps/#registering-liff-app)|




###LINE Official Account Managerの設定
メッセージ関連の設定
[LINE Official Account Manager](https://manager.line.biz/)

ここでは画面に表示するユーザー登録のメニューボタンを作成します。
上記のサイトを開いてログインすると、アカウントリストに以前作成したチャンネルが存在しています。
選択したら「トークルーム管理」から「リッチメニュー」を選びます。
上の方は適当に入力して、テンプレートを選択します。
![リッチメニュー.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/744261/932e68e6-a68c-206c-cb70-b5c212fc6095.png)


テンプレートは色々ありますが、今回は一番右上を選択します。
![テンプレート.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/744261/9b28e734-67b3-b743-307f-011e1119cfce.png)

画像はなんとなく登録して、ユーザー登録のボタンとなるAの部分のアクションを設定します。
![アクション.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/744261/f3434f74-c854-c7dc-e224-464c442c5798.png)



![ユーザー登録.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/744261/1b00c662-aade-554e-10f0-b49860dee224.png)


###LIFFの仕組みイメージはこんな感じ
![システム構成ー登録イメージ.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/744261/9451ba04-022c-1a92-e710-5c20ccd4b32d.jpeg)


#コロナ禍だから何かできることをー　自宅療養者連絡ツール　ー
[【第一回】実装イメージ図と動画](https://qiita.com/STYLE-D/items/743bbecae1c52be15505)
[【第二回】LINEからデータを取得して返すまでの流れ](https://qiita.com/STYLE-D/items/9e0addd8df44be8590b7)
[【第三回】LINEからデータベースまでの流れ](https://qiita.com/STYLE-D/items/345e9142948c00330e71)
[【第四回】データベースへの更新までの流れ](https://qiita.com/STYLE-D/items/8af17d7f94f5c351ede0)
->>[【第五回】ユーザー登録の仕組み-LINEbotの設定部分](https://qiita.com/STYLE-D/items/a745bd5faeb68caa1d7c)
[【第六回】ユーザー登録の仕組みLIFFで表示する画面の開発](https://qiita.com/STYLE-D/items/321038c8028eb9e9231f)
[【第七回】データベースの構造](https://qiita.com/STYLE-D/items/71085dc9a04042f33f0e)
[【第八回】WEB画面上でできる機能](https://qiita.com/STYLE-D/items/c32e9098a3948e8f32f2)
[【第九回】まとめ](https://qiita.com/STYLE-D/items/77eb6a8e61ae2201565f)
【Rails】Slim-rails

##概要
Ruby製のHTMLテンプレートエンジンであるslimを使えるようにするGem。Slimはコーディングを速くするだけでなく、コードを読み込むスピードも速くなる。

##手順

###インストール

Gemfile

```
gem 'slim-rails'
```

bundle installh後、viewファイルの拡張子を「html.slim」に変更。



###実装例

html.erbの時

```
<% @posts.each do |a| %>
 <p><%= a.title %></p>
<% end %>
```

slim-rails適用後

```
- @posts.each do |a|
  p = a.title
```

###ポイント

- <% %>　というruby記法の使用宣言が、「-」となっていること
- <%= %> というruby記法の使用宣言 + 出力が、「=」となっていること
- 閉じタグが不要なこと（段落によって閉じタグを判断する)
- `<p> ~~ </p>`という記法が、「p」となっていること（HTMLタグの<> や、閉じタグは不要)
【Rails】Redis install

##Redisとは
・KVSを保存する、NoSQLの一つ
・インメモリ方式
・セッションなど有効期限のあるデータを扱う場合
　や、ランキングデータなど重たいSQLを走らせた
　い場合に用いる


##実装手順
####Redisのインストール
```
$ brew install redis
```

####Redisサーバーの起動

```
$ redis-server
```

####Redisへの接続

```
$ redis-cli
```

###使用方法

SET を使って Key と Value を設定しデータを保存する。
GET で Key を指定して Value を取得する。
quit で redis-cli を終了。

```
redis> SET mykey "A"
OK
redis> GET mykey
"A"
redis> quit
```

###Railsへの導入

```
gem 'redis-rails'
```
後にbundle install

config/enviroments/◯◯.rbの使用したい環境

```
config.cache_store = :redis_store, 'redis://localhost:', { expires_in: 90.minutes }
```

###ブラウザでの出力

config/initializers/redis.rb

```
Redis.current = Redis.new
```

hostやpostを指定できるので、AWSのElastiCacheのRedisなどを使う場合はそれぞれを指定する必要がある

###ルーティング

```
resource :redis, only: %i[show]
```


app/controllers/redis_controller.rb

```
class RedisController < ApplicationController
 def show
   Redis.current.set('mykey', 'A')
 end
end
```

config/initializers/redis.rb で定義した Redis.current に対して値を保存

app/views/redis/show.html.erb

```
<%= Redis.current.get('mykey') %>
```

***

###用語

KVS(キーバリューストア) 〜
キーとバリューのシンプルなデータを保存するタイプのデータベースのことで、RDBのような複雑なデータは扱えない反面、高速に動作するという特徴がある。

NoSQL 〜
Not Only SQLの略。RDBMSではないデータベースシステムを表す概念。
SQL Server / SQL Database の最新情報を入手できるイベント

SQL Server の最新情報ですが、Microsoft が開催している、[Microsoft Ignite](https://myignite.microsoft.com/home) / [Microsoft Build](https://mybuild.microsoft.com/home) 以外のイベントでも積極的に情報の発信が行われています。

私が把握している範囲では、次のようなイベントの情報を追っておくとよいのではと思います。

PASS については、[Redgate commits to the future of PASS](https://www.red-gate.com/blog/redgate-events/redgate-pass) でアナウンスがありましたが、Redgate が運営を引き継ぎ、PASStv で過去の PASS Summit の動画の公開や、PASS Data Community Summit により、PASS Summit のような大規模イベントを継続してくれていますので、引き続き PASS 経由で情報をキャッチアップするということもできるのではないでしょうか。

- [PASS Data Community Summit](https://passdatacommunitysummit.com/)
    - [PASStv](https://www.youtube.com/user/SQLPASSTV)
    - [SQLSaturday](https://sqlsaturday.com/)
- [Data Platform Summit](https://www.dataplatformgeeks.com/)
- [SQLBits](https://sqlbits.com/)
- [SQL Server & Azure Conference (Devintersection Conference)](https://devintersection.com/#!/?track=sql)
【React】コンポーネントのmemo化

## 再レンダリングのタイミングと対象
1. stateが更新されたコンポーネント
2. propsに変更のあったコンポーネント
3. 再レンダリングされたコンポーネントの配下コンポーネント  
※organismsが再レンダリングされるとmolecules・atomsも再レンダリングされる。

## 問題
今回取り上げる問題は3つ目のタイミング。
親のstateが更新された際に、子コンポーネントの再レンダリングを回避する。
そのために使うのがmemo。

```
memo( ここに子コンポーネントの関数を入れる )
// 例
import {memo} from "react"
const test = memo( ({text}) => { 
  return <h1>{text}</h1>
 } )
```
memoを付けたコンポーネントはpropsに変化が無い限り再描画されない。
※上記の例だとtextに変化がない限り再レンダリングは起きない。
HTTPメソッド、ステータスコード

## HTTPメソッド 
### 概要
HTTPのリクエストメッセージを特徴付けるメソッドについてのまとめ。

### 8つのメソッド
HTTPメソッドの役割は、クライアントが行いたい処理をサーバーに伝えること。
種類は以下の8 つ。

#### GET
リソースの取得

#### POST
子リソースの作成、リソースへのデータの追加、その他の処理

#### PUT
リソースの更新、リソースの作成

#### DELETE
リソースの削除

#### HEAD
リソースのヘッダ(メタデータ)の取得

#### OPTIONS
リソースがサポートしているメソッドの取得

#### TRACE
自分宛にリクエストメッセージを返す(ループバック)試験

#### CONNECT
プロキシ動作のトンネル接続への変更

※補足
- TRACEとCONNECTはほとんど使わない
- GET、POST、PUT、DELETEは、これら4つで「CRUD(クラッド)」という
　性質を満たすため、代表的な処理と言える。
　CRUDとは、Create(作成)、Read(読み込み)、Update(更新)Delete(削除)というデータ操作の基本となる4つの処理のこと。


## ステータスコード 

### 概要
リクエストの結果得られるレスポンスメッセージの意味を表す、ステータスコードについてのまとめ。


### ステータスラインから見たステータスコード
レスポンスメッセージの1行目に記載されるステータスライン。
プロトコルバージョン、ステータスコード、テキストフレーズから構成されている。

HTTP/1.1 200 OK
Content-Type: application/xhtml+xml; charset=uft-8

```
<html xmlns="http://www.w3.org/1999/xhtml">
 ... 
</html>
```
この例では「 200 」がステータスコード。200はクライアントのリクエストが正常に終了したことを示す。

### ステータスコードの分類とその意味
ステータスコードは3行の数字であり、5つに分類される。

#### 1xx　処理中
処理の継続を示す。クライアントはそのままリクエストを継続するか、サーバーの指示に従ってプロトコルをアップデートして再送信する。

#### 2xx　成功
リクエストが成功したことを示す。

#### 3xx　リダイレクト
他のソースへのリダイレクトを示す。クライアントはこのステータスコードを受け取ったとき、レスポンスメッセージのLocationを見て新しいリソスーへ接続する。

#### 4xx　クライアントエラー
クライアントのリクエストによるエラーを示す。エラーを解消しない限り正常な結果が得られないので、同じリクエストをそのまま再送信することはできない。

#### 5xx　サーバーエラー
サーバー側でのエラーを示す。サーバーでの原因が解決すれば、同一のリクエストを再送信して正常な結果が得られる場合がある。



### よく使われるステータスコード一覧

200 OK　〜　リクエスト成功

201 Created　〜　リソースの作成成功

301 Moved Permanently　〜　リソースの恒久的な移動

303 See Other　〜　別URLの参照

400 Bad Request　〜　リクエストの間違い

401 Unauthorized　〜　アクセス権不足

404 Not Found　〜　リソースの不在

500 Internal Server Error　〜　サーバー内部エラー

503 Service Unavailable　〜　サービス停止

***

#### 用語

リソース 〜
Webを構成するもの

子リソース 〜
別のリソースのコンテキスト内でのみ存在するリソースのこと

ループバック 〜
送った物が、そのまま自分に返ってくる動作のこと

トンネル接続 〜
インターネットなどの公衆回線網上にある2点だけを結ぶ閉じられた仮想的な直結通信回線を確立すること

Location 〜
現在表示しているドキュメントのWEBアドレスに関する情報
【JavaScript】スコープの学習の振り返り②　レキシカルスコープとスコープチェーン


#はじめに

Udemyの[【JS】ガチで学びたい人のためのJavaScriptメカニズム](https://www.udemy.com/course/javascript-essence/)の講座の振り返りです。

前回の記事

https://qiita.com/nao0725/items/d643787e022ff22e3cfc

#目的

* スコープについての理解を深める


#本題
###1.レキシカルスコープ

**コードを書く場所によって参照できる変数が変わるスコープのこと**です。

**実行中のコードからみた外部スコープのこと**という意味もある。


````javascript
// aを定義する（スクリプトスコープ）
let a = 2;
// 関数fn1を定義(スクリプトスコープ)
function fn1(){
  // 関数fn1の中に変数bと関数fn2を定義(関数スコープ)
  let b = 1;
  console.log(b);
  function fn2(){
    // fn2の中に変数cを定義
    let c = 3;
  }
  // ここでfn2を実行するとa,b,fn1を参照できる
  fn2();
}
// ここでfn1を実行するとaを参照できる
fn1();

// スクリプトスコープはどこからでも参照可能　=> 変数aはどこでも参照可
// 自身のスコープより外部のスコープを参照できる
````

###2.スコープチェーン

**スコープが複数階層で連なっている状態（上記レキシカルスコープの例）**のこと

例

````javascript
let a = 2;
function fn1(){
    let a = 1;
    function fn2(){
        // fn1,fn2で同じ変数aを定義し出力する
        // ブラウザで確認すると「3」と表示される
        let a = 3;
        console.log(a);
    }
    fn2();
}
fn1();
````

続いて、let a = 3をコメントアウトすると下記のようになります。
 
`````javascript
let a = 2;
function fn1(){
    let a = 1;
    function fn2(){
        // もしここで下記をコメントアウトすると
        // let a = 3;
        console.log(a);
    }
    fn2();
}
fn1();
// a = 1という値が出力される
// スコープが複数階層になっている場合には１つずつ外側に同じ変数がないか探しにいく
// 見つかった時点で、その変数を参照する
````

次はグローバルスコープとスクリプトスコープに同じ変数名がある場合について考えていきます。

```javascript
// スクリプトスコープ
let a = 2;
// グローバルスコープ
window.a = 4;
function fn1(){
    // let a = 1;
    function fn2(){
        // let a = 3;
        console.log(a);
    }
    fn2();
}
fn1();

// この場合実行結果はa = 2が出力される
// グローバルスコープはスクリプトスコープより外側のスコープであるということがわかる
// 内側のスコープから探しに行く　=　見つからない場合にエラーにな
```

よく間違える例
let a = 3をconsole.logより下に定義した場合どうなるのか

```javascript
// スクリプトスコープ
let a = 2;
// グローバルスコープ
window.a = 4;
function fn1(){
    // let a = 1;
    function fn2(){
        console.log(a);
        let a = 3;
    }
    fn2();
}
fn1();

// main.js:8 Uncaught ReferenceError: Cannot access 'a' before initializationというエラーが発生
// letを使った変数宣言より前に値を取得しようとするとエラーになる
```

もしもletではなくvarだったら

```javascript
// スクリプトスコープ
let a = 2;
// グローバルスコープ
window.a = 4;
function fn1(){
    // let a = 1;
    function fn2(){
        console.log(a);
        if (true){
            var a = 3;
        }
        // var = 3;のホスティングが働いてしまうのでundifinedと出力される
        // ホスティングとはコンテキスト内で宣言した変数や関数の定義をコード実行前にメモリーに配置すること
    }
    fn2();
}
fn1();

````

今日はここまで！

#参考にさせて頂いた記事

* [【JS】ガチで学びたい人のためのJavaScriptメカニズム](https://www.udemy.com/course/javascript-essence/)
AWS CDKのaws-lambda-nodejsで"Error: spawnSync docker ENOENT"が出るとき


# 問題
Lambda関数をTypeScriptで書くために`aws-lambda-nodejs`を使おうとしたら、下記のエラーが出た。

```sh
Error: spawnSync docker ENOENT
 at Object.spawnSync (node:internal/child_process:1083:20)
    at Object.spawnSync (node:child_process:812:24)
    at dockerExec
...
```

# 解決策
実行環境がmacOSの場合は、ビルドモジュールである`esbuild`を手動でインストールするのが良いらしい。

```
$ npm install --save-dev esbuild@0
```
or

```
$ yarn add --dev esbuild@0
```

# 参照
https://docs.aws.amazon.com/cdk/api/latest/docs/aws-lambda-nodejs-readme.html#local-bundling