OmniAICreator/Qiita-1.07M huggingface dataset:
ROS2の共有ライブラリを使用したプログラムの作成

【[ROS2関係トップページへ](https://qiita.com/NeK/items/7ac0f4ec10d51dbca084)】

【前:[ROS2における共有ライブラリ]】
【次:[ROS2におけるコンポーネント]】

[ROS2における共有ライブラリ]:https://qiita.com/NeK/items/48c55bd473091d41cc75
[ROS2におけるコンポーネント]:https://qiita.com/NeK/items/775c0ddb4e10ed091068

[ROS2における共有ライブラリ]で作成した共有ライブラリを使用する方法について．
他のROS2プログラムとの違いは，CMakeLists.txtでtarget_include_directoriesを使うことのみ．

Galacticまでは[こちら](https://qiita.com/NeK/items/f9642f66e9ce277733ec)

# 準備

```bash:terminal
$ cd ~/ros2_studies_ws/
$ ros2 pkg create minimal_comp_user --build-type ament_cmake --dependencies rclcpp minimal_comp --license Apache-2.0
```

[ROS2における共有ライブラリ]で作成したパッケージ:minimal_compを使用するため，dependenciesに追加．

# 共有ライブラリを使用したプログラム

- src/minimal_comp_user_main.cpp

## プログラム

```c++:minimal_comp_user_main.cpp
#include <rclcpp/rclcpp.hpp>
#include "minimal_comp/minimal_comp_node2.hpp"

int main(int argc, char * argv[]){
  rclcpp::init(argc,argv);
  rclcpp::spin(std::make_shared<minimal_comp::MinimalCompNode2>());
  rclcpp::shutdown();
  return 0;
}
```

### 概要
共有ライブラリのMinimalCompNode2を呼び出している．
パッケージ名をnamespaceにしていたので呼び出しは6行目のようにminimal_comp::MinimalCompNode2となる．

# package.xmlとCMakeLists.txt
関係する部分のみ抜粋．

## package.xml

```xml:package.xml
<package format="3">
  <depend>rclcpp</depend>
  <depend>minimal_comp</depend>
```

### 説明
rclcppパッケージとともに共有ライブラリのパッケージ(minimal_comp)を記述．

## CMakeLists.txt

```CMakeLists.txt
find_package(rclcpp REQUIRED)
find_package(minimal_comp REQUIRED)

add_executable(minimal_comp_user
  src/minimal_comp_user_main.cpp
)
ament_target_dependencies(minimal_comp_user
  rclcpp
  minimal_comp
)

target_include_directories(minimal_comp_user
  PUBLIC
    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
    $<INSTALL_INTERFACE:include/${PROJECT_NAME}>
)

install(TARGETS
  minimal_comp_user
  DESTINATION lib/${PROJECT_NAME}
)
```

### 概要
他のROS2と基本的に同じで，パッケージを見つけるfind_packageやターゲットと必要となるソースファイルの指定，インストール先などを記述．
共有ライブラリを使用する上で必要となるものがtarget_include_directoriesとなる．

### 説明
共有ライブラリなど自分のパッケージディレクトリにない外部のヘッダファイルにアクセスするために12～16行目が必要となる．
12行目に今回作成するターゲット名を書く．13～16行目は他でも同じ書き方になるのでコピペ．

# ビルド・実行

## ビルド
```bash:terminal
$ cd ~/ros2_studies_ws/
$ colcon build --symlink-install --packages-up-to minimal_comp_user
$ . install/local_setup.bash
```

共有ライブラリがビルドしてあるものとする．まだしていなければ，--packages-up-toに共有ライブラリのパッケージも一緒にしていしてやる．

## 実行

```bash:terminal
$ cd ~/ros2_studies_ws/
$ . install/local_setup.bash
$ ros2 run minimal_comp_user minimal_comp_user
```

# 共有ライブラリと作成プログラムが同一パッケージの場合
ここでは共有ライブラリとROS2プログラムが異なるパッケージの場合を作成した．
この時，共有ライブラリのパッケージ(minimal_comp)をfind_packageで指定し，ament_target_dependenciesでminimal_compを指定した．


共有ライブラリとROS2プログラムが同一パッケージの場合は，find_packageを書く必要がなく(自身のパッケージ名だから書けない)，~~ament_target_dependenciesではライブラリ名(add_libraryの名前)を指定すればよい．~~ 共有ライブラリ分だけtarget_link_librariesに書く(その他のrclcppなどはament_target_dependenciesに書く)．例えば以下の通り．

```CMakeList.txt
target_link_libraries(hoge
  fuge # 同一パッケージの共有ライブラリ名(add_libraryの名前)
)
ament_target_dependencies(hoge
  rclcpp
)
```


[公式githubの共有ライブラリにかかわるexmaple](https://github.com/ros2/examples/tree/master/rclcpp/minimal_composition)のCMakeLists.txtが参考になる．
OutSystems - Architecture Dashboardを利用する

2020年5月29日、ローコードプラットフォーム OutSystemsのArchitecture Dashboardがリリースされました。2020年の新コロナ禍による影響で各種イベントが休止・延期される中、まだあまり情報が表に出ていないのですが、以下の情報が発信されています。
https://www.outsystems.com/whats-new/architecture-dashboard-available/

当記事では、このOutSystems Architecture Dashboardの概要、機能、利用方法を紹介します。数あるローコード開発系ツールの中でも、このようなコード品質・アーキテクチャ改善用のダッシュボードが提供されているプラットフォームは少ないのではないでしょうか。このように、開発したアプリの保守性、品質、性能まで考慮している点は、OutSystemsの強みの一つであると言えます。

##Architecture Dashboardとは
Architecture Dashboardは、企業が抱える技術的負債を監視するためのツールです。ダッシュボードを利用することで、以下の恩恵が得られます。

* <strong>ITマネジャー・テックリードにとってのメリット：</strong><br>企業が保有する複雑なアプリケーションポートフォリオの構成を把握し、問題を特定できる。
* <strong>開発者にとってのメリット：</strong><br>開発上のベストプラクティスを参照し、陥りがちな実装上の問題（保守性、品質、性能面などの課題）を避けることができる。

Architecture Dashboardの詳細は以下のドキュメントをご参照ください。（~~2020年6月29日時点、コンテンツは英語のみですが、日本語翻訳作業が鋭意進行中です。~~ 7月10日、日本語版が公開されました。）
https://success.outsystems.com/Documentation/ja-jp/Architecture_Dashboard/Introduction_to_Architecture_Dashboard

##Architecture Dashboardの機能
ダッシュボード画面で分析レポートを参照し、以下の課題を確認・対応できます。

* <strong>ソースコード（設計情報）の課題</strong><br>開発モジュールの問題（構成面、セキュリティ、性能、保守性など）について、検索、詳細確認、エクスポート可能
* <strong>パフォーマンスの課題</strong><br>稼働中のアプリケーションについて、パフォーマンスログの分析結果に基づき、緊急対応が必要な改善箇所を特定可能

以下に、Dashboard画面のイメージを掲載します。
![OutSystemsAD1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502895/192ab7bb-924b-b158-a349-c3b74d07f591.png)

![OutSystemsAD2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502895/20cde397-c8d2-3b2f-84bf-20d36973a94b.png)
レポート内のメッセージは、すでに日本語化されており、以下のページで参照できます。
https://success.outsystems.com/Documentation/ja-jp/Architecture_Dashboard/Code_Patterns

##Architecture Dashboardの構成
Architecture Dashboardは、以下の2つの要素から構成されます。

* Architecture Dashboard本体（SaaSサービスとして提供 ※）
* 開発環境側で動作するProbeエージェント（開発環境にインストール）<br>

※Architecture Dashboardを利用するためにはSasSへのアクセスが必要です（データの暗号化、情報の扱い、匿名性などのセキュリティ面は考慮されています）。

また、アプリケーションの構成を分析するため、Discoveryコンポーネントも必要です。Discoveryを導入していない場合は、以下のForgeレポジトリからダウンロードします。
https://www.outsystems.com/forge/component-overview/409/discovery

##Architecture Dashboard利用の前提条件
上記ドキュメントの「[4. Access to Architecture Dashboard features](https://success.outsystems.com/Documentation/Architecture_Dashboard/Introduction_to_Architecture_Dashboard#Access_to_Architecture_Dashboard_features)」に記載のように、Free Edition (Personal Environment) では、Architecture Dashboardを利用できません。また、エディションによって、利用可能な機能が一部異なります。
OutSystems Platformのバージョンは、OutSystems 10以上（.NETスタック）に対応しています。

##Architecture Dashboardのセットアップ方法
Architecture Dashboardを利用するまでの大まかな流れを以下に記載します。

1. Architecture DashboardのSaaSサイト（[https://architecture.outsystems.com/](https://architecture.outsystems.com/)）にアクセスし、ライセンス登録済みのアカウントでログインします。

2. SaaSサイト上で、Probe対象となる環境（インフラ）のアクティベーションコードを登録します。

3. ForgeからDiscoveryコンポーネントをインストールし、Probe対象のDev環境にインストールします（未インストールの場合）。

4. Probe用のモジュールをダウンロードして、Dev環境にインストールします。詳細は以下のリンクをご参照ください（Architecture Dashboardの利用に必要な要件も、ドキュメント内に記述されています）。
https://success.outsystems.com/Documentation/Architecture_Dashboard/How_to_set_up_Architecture_Dashboard
UiPathの「データテーブルを出力」アクティビティと「データテーブルを生成」アクティビティおよびその応用について

#はじめに
UiPathには多くの便利なアクティビティがありますが、このドキュメントでは文字列とDataTableを変換する「__データテーブルを出力__」アクティビティ(Output DataTable)と「__データテーブルを生成__」アクティビティ(Generate DataTable)について、応用も含めて書こうと思います。なお似たような名前のアクティビティとして「__データテーブルを構築__」(Build DataTable)がありますが、これはDataTableを新規に作るための物なので、ここでは説明しません。

#２つのアクティビティの概要
##データテーブルを出力アクティビティ（Output DataTable）
DataTableからCSV形式の文字列を出力するアクティビティです。
設定項目はほとんどなく、入力としてDataTable、出力としてString型変数を指定します。
ヘッダー付き、カンマ区切り、カンマやダブルクォーテーション等の処理等自動で処理してくれます。

![img1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/477147/8c34a7df-649b-5609-87bf-2c9e05349d24.png)

個人的にはヘッダー有無、demiliterの設定などが自由にできるようしてほしいところです。

##データテーブルを生成アクティビティ(Generate DataTable)
文字列からDataTableに変換するためのアクティビティです。
このアクティビティは非常に強力で、固定長テキストフォーマットまたはCSV形式を解析して、DataTableに変換してくれます。CSVの解析は区切り文字、改行文字を任意に設定することもできます。また列ヘッダー有無や型の自動検出等の設定も可能です。

![img2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/477147/bd268bcf-ffc5-c90e-fdcf-afe9d3566079.png)

　個人的に注意が必要と思うのは、型の自動検出機能です。デフォルトでONになっていますが、この機能のために思わぬ読み取り結果になることがしばしばあります。この設定はOFFにしておくとすべてString型となりますので、意図せぬ結果を防ぐためにも基本はOFFにしておくほうが良いと思います。


#応用
## CSVを読み込みアクティビティ(Read CSV)の代替
　UiPathには__CSVを読み込み__アクティビティ、__CSVに書き込み__アクティビティといったCSV形式のファイルとDataTableとを変換するアクティビティがあります。
　特に__データテーブルを生成__アクティビティは読み込み用途の代替として強力です
例えば以下のようなヘッダ行が３列、データ部分が４列のCSVファイルがあったとします。

```sample.csv
A,B,C
1,2,3,4
5,6,7,8
```
　
これをCSVを読み込みアクティビティで読み込もうとすると以下のエラーが発生します。

    CSV を読み込み: test.csv の CSV ファイル形式は無効です。行 2 は、ヘッダー行よりも値を多く含んでいます。

しかし、以下のように__テキストをファイルから読み込み__アクティビティと__データテーブルを生成__アクティビティを組み合わせて読み込むと、このような不定形なCSVでも不足しているヘッダを自動的に補完して読み込んでくれます。

![img4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/477147/6fb1399e-f810-661e-81a8-2ec6f1f9349a.png)

以下は読みこんだ後のLocalパネルでみたデータテーブルの中身です。

![img5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/477147/74ad2543-f530-97f4-7c9e-afa17d53252e.png)

## DataTableの操作を文字列操作で実現
　DataTableと文字列を相互変換できると、DataTableでは複雑な操作が必要なケースでも一旦文字列にして、正規表現などの文字列操作で簡単に実現できるケースがあります。

例えば
![img6-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/477147/461b776e-86ef-2aea-15c9-6fe1fae10b6d.png)
のような表があり
![img6-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/477147/5af8faaf-38a8-7338-6ca4-735cfee4bce7.png)
のように属性値を一列にまとめたいとします。

正攻法的にはDataTableとして読み込み、繰り返し（各行）アクティビティを使って各行ごとに処理していくこといなりますが、これを文字列操作で行うと以下のようになります。
![img7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/477147/e0d36771-5b8a-0599-f97e-268b50ad77b7.png)
データ中にカンマやダブルクォートなどの特殊な文字が含まれないことが前提になりますが、２つの正規表現置換と１つの単純置換でこれを処理することができます。


#まとめ
このように特定の場面でとても有用な２つのアクティビティ、頭の片隅にでも良いので、覚えておくとよいでしょう。
Unixシェルの種類

# はじめに
Unixシェル、様々な種類がありますが、ちょっと調べてまとめてみました。
膨大な種類があるので、有名どころだけですが。。

# sh
初版：1977年
Unix等では、 `/bin/sh` は `Bourne Shell` 互換のシェルを指すらしいです。
シェルの中では最も基本的なシェルであり、 ここから `bash` や `ksh ` と派生していきます。

# bash
初版：1989年
`Bourne Shell` から派生したシェルであり、基本的に上位互換です。
コマンド履歴やコマンド名の自動補完等を行ってくれます。

# ksh
初版：1983年
こちらも `Bourne Shell` から派生したシェルであり、基本的に上位互換です。
コマンド履歴やコマンド名の自動補完等を行ってくれます。

# ash
初版：1991年
正式名称は `Almquist Shell` といいます。
軽量なため、組み込みLinuxに利用されます。
メモリが少ないマシンには最適ですが、 `bash`等が持つ補完機能は持っていません。

# dash
初版：1997年
正式名称は `Debian Almquist shell` といい、
Debian版Almquist Shell(ash)の意で、軽量かつPOSIX互換を目指すUnixシェルです。
基本的な機能は `ash` と同様です。
`Debian ` は `5.0` から、 `Ubuntu` は `6.10` から `/bin/sh` は この `dash` が使われています。

# csh
初版：1978年
言語としての全体的スタイルはC言語によく似ています。
このシェルから `tcsh` 等に派生していきます。

# tcsh
`csh` にファイル名補完、コマンドライン編集機能の機能を追加したもので、上位互換です。
履歴編集などの機能に優れ、簡単なC言語ソースコードの文法チェックに使われたりします。

# zsh
初版：1990年
`bash`,`ksh`,`tcsh` の非常に有用な機能も一部取り込まれています。
macOSでも、 `10.15 Catalina` からデフォルトのシェルとして設定されています。
その為、触れたことがある方も多いのではないでしょうか。
私は現在こちらの `zsh` をメインで使用しています。
通称「最強のシェル」です。

# fish
正式名称は `friendly interactive shell` といいます。
POSIX(シェルスクリプトの標準仕様)に非互換ではありますが、
デフォルトで非常に強力な機能を搭載しているらしいです。

# 最後に
思った以上に短くなってしまいましたが、以上になります。
自身として `zsh` を長年使っていますが、最近 `fish` に興味が出てきており、
少し触ってみようかと思います。
あまり触れたことのないシェルを知ることができ、良い機会だったと思います。
俺のgcsfuseがSELinuxのせいで動かないわけがない

SELinuxと共に生きるためのTips。
（SFTPがよかったんじゃが諸事情）

# 要望
・GCP Cloud StoreageをGCEインスタンスにマウントして、指定ユーザー（"ftp-user"とする）にFTP操作可能にさせたい
・ユーザーには /home 直下の情報をみせたくない
・ユーザーには /home/ftp-user/csv（マウント先）以下のみファイル操作可能にさせたい

# 結論
・FTPサーバ（vsftp）を立てて、Chrootするだけでは、GCSマウント先のデータ転送に失敗する
・SELinuxをDisableにすると、/home 直下が見れてしまう
・SELinuxを有効のまま、要件を満たすには、SELinuxのアクセス制御が必要

# 環境
## OS
> CentOS Linux release 7.8.2003 (Core) on GCP

## Tools
> gcsfuse version 0.28.1 (Go version go1.9.7)
> vsftpd: version 3.0.2

# 手順

### FTPサーバ構築

GCPにGCEインスタンスを立てて、FTPサーバを用意する

> GCEちょい技 – FTPサーバを構築する
https://www.apps-gcp.com/gce-casual-knowhow-02/
※pasv_address=**.**.**.**にGloacIPの追記が必要な点に注意

### chroot環境設定

FTPユーザーのホーム以下をchroot環境に設定する

> centos7のvsftpdでchroot環境を作る
https://qiita.com/mugi-tea/items/b6ee5d9127409667bc7c


### GCSをマウント

gcsfuseインストール。

マウント設定をfstabに仕込んで、`mount`で確認。
問題なければ、`mount -a`。

> vi /etc/fstab

```
[YOUR_STORAGE_NAME] [YOUR_MOUNT_DIR] gcsfuse implicit_dirs,user,rw,_netdev,allow_other,uid=100X,gid=100Y,dir_mode=0755,file_mode=0644,only_dir=[YOUR_STORAGE_PATH] 0 0
```

以下のように設定すると、hogeバケットの foo/bar パスのコンテンツを /home/ftp-user/bar にマウントできます。

|Key|Value|例|
|:-----------------:|:------------------:|:------------------:|
|[YOUR_STORAGE_NAME]|GCSバケット名|hoge|
|[YOUR_MOUNT_DIR]|GCE側マウントディレクトリ|/home/ftp-user/bar|
|[YOUR_STORAGE_PATH]|GCSバケットのマウント対象パス|foo/bar|


gcsfuseコマンドのパラメータに迷ったら公式を参照。

> GoogleCloudPlatform　gcsfuse
https://github.com/GoogleCloudPlatform/gcsfuse/blob/master/docs/mounting.md
> Mounting google storage bucket folder using gcsfuse
https://stackoverflow.com/questions/34390730/mounting-google-storage-bucket-folder-using-gcsfuse

### SELinux 設定

#### 課題

この時点でSELinuxが有効だと、FTPユーザーはマウントディレクトリが空に見える（データ転送に失敗している）
LinuxユーザーのパーミッションがFTPユーザーにひも付き、仮にマウントディレクトリを777にしても現象は解消しない。

SELinuxを無効にすると、マウントディレクトリの実体が見える…が 上位ディレクトリ /home の内容も見えてしまう。

#### 対策

ポリシーの検索用にツールを入れる
> yum install selinux-policy-devel selinux-policy-doc
> sepolicy booleans -a | grep fuse

SELinuxは有効にした状態で、アクセス制御を変更する（再起動不要）
> setsebool -P ssh_chroot_rw_homedirs on
> setsebool -P ftpd_use_fusefs on

以上。

---

#### Many thanks (;

> 「SELinuxのせいで動かない」撲滅ガイド
https://qiita.com/yunano/items/857ab36faa0d695573dd
> SELINUXが有効になっているサーバではvsftpdのデータ転送が使用できない
https://pentan.info/server/linux/selinux_vsftpd.html
> CentOSとchrootの設定
http://nothing-titile.hatenablog.jp/entry/2014/08/24/005436
> 【SELinux】Boolean の仕組みと getsebool / setsebool コマンドの使い方
https://milestone-of-se.nesuke.com/sv-advanced/selinux/boolean-getsebool-setsebool/
> gcs-fuseでマウントしたディレクトリをWebDAVで公開しようとしてハマった話
https://consulting.blog.rakumo.com/entry/20180426/gcsfuse-webdav
Draw.ioで途中からテンプレートを適用する方法

## どんな時にこの記事をみる？
・ ブランク状態から始めて途中からテンプレート利用したい時あるけど、メニュー多すぎて忘れてしまう
・ シートを追加した時に、ブランク状態で始まるのでテンプレートを適用したい

## 方法
1. ヘッダーメニューの`Arrange`を選択
2. `insert`を選択  
3. `Template...`を選択
【初心者向けハンズオン】kaggleの「住宅価格を予測する」を1行ずつ読み解く（第6回：目的変数の分布変換）

# お題
* [第1回の内容はこちら](https://qiita.com/WdknWdkn/items/a7f5e4a2cc492676e12a)
* [第2回の内容はこちら](https://qiita.com/WdknWdkn/items/95eccfae1aac99babe7a)
* [第3回の内容はこちら](https://qiita.com/WdknWdkn/items/54ee2cdc56d85d5e795c)
* [第4回の内容はこちら](https://qiita.com/WdknWdkn/items/b3ec4606d533770e5eb5)
* [第5回の内容はこちら](https://qiita.com/WdknWdkn/items/17aa03188f728b6b563e)

　有名なお題であるkaggleの「House Price」問題にみんなでチャレンジしていくことになったハンズオンの内容をメモしていく企画の第6回。解説というよりはメモのまとめだったりもしますが、どこかの誰かのためになれば幸いです。前回で準備がおわり、いよいよ解析段階に。

* 元々のお題：https://www.kaggle.com/c/house-prices-advanced-regression-techniques
* 参考にした記事：https://yolo-kiyoshi.com/2018/12/17/post-1003/

# 本日の作業

## 目的変数の分布変換

* 目的変数：後輩曰く「Yですね」 → 自分「。。。」
* 目的変数：http://www.gen-info.osaka-u.ac.jp/testdocs/tomocom/express/express8.html

> 学習データのSalePrice(住宅価格)の分布を確認します。
欠損補完の箇所で、プールがない住宅がほとんどであることがわかりました。
これは裏を返せばプールがあるような豪邸がいくつか存在するということであり、住宅価格がかなり歪な分布になっているのでは？と想定されます。

こうした仮設を元に描画するのが重要だなと振り返ります。とはいえまずは言われるがままにグラフをアウトプット。

```
sns.distplot(train['SalePrice'])
```

### seabornについて
「snsってなんだっけ？」ってなりました。最初過ぎて忘れていましたが、一番最初にインポートしていたライブラリの中にありましたね。これです。

```
import seaborn as sns
```

なるほどseaborn
* seaborn：どうやらグラフ描画のライブラリ。
* seaborn参照：https://qiita.com/hik0107/items/3dc541158fceb3156ee0
* distplot：seabornでヒストグラムを描画するメソッド。

### train['SalePrice']に入っていた内容を確認
あとは念のため、train['SalePrice']に入っていた内容を確認。
なるほどひたすら各が並んでいる列。
<img width="143" alt="スクリーンショット 2020-06-29 12.07.02.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215128/cd2ea2ca-50e6-f8b6-1c35-69b40acb8e76.png">

### アウトプットされたグラフ

そしてアウトプットされたグラフはこんな感じになりました。

```
sns.distplot(train['SalePrice'])
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215128/93723341-659b-b2d3-cdd6-db9bcc9deafd.png)

## 対数変換

> 予想どおり、かなり右側に分布の裾野が広がっています。
対数変換をすることで正規分布に近づけます。

とのことですが、「対数変換、とは」というところの確認。
* 対数変換参照：https://atarimae.biz/archives/13161#:~:text=%E5%AF%BE%E6%95%B0%E5%A4%89%E6%8F%9B%E3%81%A8%E3%81%AF%E3%80%81%E3%80%8C%E5%AF%BE%E6%95%B0,%E3%81%99%E3%82%8B%E3%81%93%E3%81%A8%E3%82%92%E6%8C%87%E3%81%97%E3%81%BE%E3%81%99%E3%80%82&text=%E5%85%B7%E4%BD%93%E7%9A%84%E3%81%AB%E3%80%81%E8%AA%AC%E6%98%8E%E5%A4%89%E6%95%B0,%E8%80%83%E3%81%88%E3%81%A6%E3%81%BF%E3%81%BE%E3%81%97%E3%82%87%E3%81%86%E3%80%82

```
sns.distplot(np.log(train['SalePrice']))
```

### 対数変換前後の配列の変化
これだけ出力してみます。

```
np.log(train['SalePrice'])
```
なるほど、潰れている。
<img width="147" alt="スクリーンショット 2020-06-29 12.17.28.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215128/b2b07034-fb4f-59b8-1b0f-6d1ec8e76170.png">

### アウトプットされたグラフその２

```
sns.distplot(np.log(train['SalePrice']))
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215128/c9c83d51-fc17-d392-f247-a06624f96206.png)

なるほどきれいめに正規分布になっている気がする。

## 予測モデルの構築

に、入りたかったですが、どうやら時間切れ臭いので今日はここまでです。

> 今回は変数量がかなり多いため、係数に強力なペナルティをかけたいのでLasso回帰を使って予測モデルを構築します。

予習がてらLasso回帰について調べて終了。

### Lasso回帰
* Lasso回帰参照：https://aizine.ai/ridge-lasso-elasticnet/

# おしまい。

分析レイヤーに入ってきてから、やはり背景知識の補充が必要だなと、理解しました。
主に回帰分析について。
テキストをHTML、CSSに変換しています。

テキストをHTML、CSSに変換しています。

添付画像の[CONTENTS]、目次ページをHTMLとCSSで作成しなくてはならなくなりました。

文字の大きさ、色の調整、’line-height’、’marigin'などを使い凡そのレイアウトはうまくいきました。

（質問１）
わからなかったのが、CONTENTSの右横にある赤の点線です。
これは<hr>を使って'dashed'などをかけてみたのですがうまくいきませんでした。
文字の横ではなく、文字の下側のラインに点線が出来てしまいます。
これは第１章の各項目でも同じことが言えます。文字の右横。文字の高さの中心と同じ高さに点線が作れません。

「 01 エクセルを起動・終了する ---------------------------------- 010」

うまく行く方法をご存知の方いらっしゃいましたら教えてください。

（質問２）
リストの左揃えはタグ<dd>に’margin-left’をかけて揃えています。
リストのページ（数字）を右端で揃えるには同様に'margin-right'でうまくいきますか？
点線の長さや、リストの文字数を勘案しても添付画像のようにキレイに右端で揃えるには
どうやったらよろしいでしょうか？

慣れないHTML、CSSで苦労しています。
ご教授お願い致します。




ちなみにこのリストは<dl><dt><dd>で作成しています。


**************************************************

CONTENST ---------------------------------------------------------------------
目次

【第一章】 これだけは知っておこう

01 エクセルを起動・終了する ---------------------------------- 010
02 ウィンドウの構成を知る ------------------------------------ 012
03 ワークシートの構成を知る ---------------------------------- 014
04 ファイル(ブック)を保存する -------------------------------- 016
05 保存した(ブック)を開く ------------------------------------- 018
06 Column 他のバージョンで開くには？------------------------- 020

*************************************************
![IMG_0455.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/263505/97530484-6a68-5db7-a9d3-49b84fcdffa2.jpeg)
Discordでbot作成時にエラーが出たのでメモ

##前提
bot作成時のエラー解消メモです
作り方じゃないのでご注意ください

##bot追加時のエラー

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208609/3f236d19-5882-4eb8-34ab-3ddc9aeb5646.png)

右側の`Add Bot`を押下

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208609/1bcf173f-3085-f534-cb71-96be37c06488.png)

`Yes,do it!`

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208609/86a2a5d8-351d-93da-b2d1-1a809adfc472.png)

`Too many users have this username, please try another.`というエラーが出てます。

同じ名前が多すぎるよ～ってことだと思うので、このエラーが表示された場合は`General Information`のところで名前を変えましょう。

##General Informationで名前を変える

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208609/1ed7da21-0aeb-ce04-5b82-811fe2d3f351.png)

右下の`Save Changes`を押下するのを忘れずに！（保存ボタン

##名前変更後に再度Add Botする
名前を変えて再度`Add Bot`すると、正常にAddできます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208609/ca081aa4-c304-fecf-dfe4-20327c16b2b0.png)

おわり
【Rust】緯度経度のcsvデータを読み込んで2点間の距離を求める

## 背景

500点くらいの位置情報がcsvデータにまとめられていて、その全ての組み合わせについて直線距離(km) を求めたい.

## 緯度経度から距離を求める

以下の記事を参考にさせていただきました.

- Python 経度・緯度で与えられた２点間距離計算
  - https://qiita.com/damyarou/items/9cb633e844c78307134a

```rust
pub fn cal_rho(lat_a: &f64, lon_a: &f64, lat_b: &f64, lon_b: &f64) -> f64 {
    const RA: f64 = 6378.140; // equatorial radius (km)
    const RB: f64 = 6356.755; // polar radius (km)
    let f: f64;
    let rad_lat_a: f64;
    let rad_lon_a: f64;
    let rad_lat_b: f64;
    let rad_lon_b: f64;
    let pa: f64;
    let pb: f64;
    let xx: f64;
    let c1: f64;
    let c2: f64;
    let dr: f64;
    let rho: f64;

    f = (RA - RB) / RA;
    rad_lat_a = lat_a.to_radians();
    rad_lon_a = lon_a.to_radians();
    rad_lat_b = lat_b.to_radians();
    rad_lon_b = lon_b.to_radians();
    pa = (RB / RA * rad_lat_a.tan()).atan();
    pb = (RB / RA * rad_lat_b.tan()).atan();
    xx = (pa.sin() * pb.sin() + pa.cos() * pb.cos() * (rad_lon_a - rad_lon_b).cos()).acos();
    c1 = ((xx.sin() - xx) * (pa.sin() + pb.sin())).powf(2.0) / ((xx / 2.0).cos()).powf(2.0);
    c2 = ((xx.sin() + xx) * (pa.sin() - pb.sin())).powf(2.0) / ((xx / 2.0).sin()).powf(2.0);
    dr = f / 8.0 * (c1 - c2);
    rho = RA * (xx + dr);
    rho
}
```

## csvの読み込み -> 距離の計算

`rdr.records()` で取得できる `StringRecordsIter` を使ってネストのループを作ろうと思ったが、うまく行かなかった.
そのため `Iter` -> `Vec` に変換して対応した.

```rust
extern crate csv;
use csv::StringRecord;
use std::env;
use std::error::Error;
use std::ffi::OsString;
use std::fs::File;

pub fn run() -> Result<(), Box<Error>> {
    let file_path = "file.csv";
    let file = File::open(file_path)?;
    let mut rdr = csv::Reader::from_reader(file);
    let _records = rdr.records();
    let records: Vec<StringRecord> = _records.map(|x| x.unwrap()).collect();
    let data_len = &records.len();
    
    for i in 0..*data_len {
        for j in 0..*data_len {
            let r_i = &records[i];
            let lat_i = &r_i[1];
            let lat_i: f64 = lat_i.parse().unwrap();
            let lng_i = &r_i[2];
            let lng_i: f64 = lng_i.parse().unwrap();
            let r_j = &records[j];
            let lat_j = &r_j[1];
            let lat_j: f64 = lat_j.parse().unwrap();
            let lng_j = &r_j[2];
            let lng_j: f64 = lng_j.parse().unwrap();

            let dist = cal_rho(&lat_i, &lng_i, &lat_j, &lng_j);

            println!("{}", dist);
        }
    }
    Ok(())
}
```

手探りの実装ですので、良い方法あればアドバイスお待ちしています。
IMのドキュメント内にある様々なAPIをまとめてみる その１

#IM開発備忘録

##IMのドキュメント内にある様々なAPIをまとめてみる その１

###画面アイテムのUI入力をAPIで呼び出す

- 画面アイテム 入力モード変更

Formaをコーディング中心で作るなら使えるが、
保守性という点であまりお勧めできないAPI
(入力切替のアクション設定で統一した方が、わかりやすいため)

```js:sample.js
formaItems.%画面アイテムID%.changeInputMode(object controlSetting)
```

- ボタン（申請・登録）処理

かなり便利。一つのボタンに複数の処理を入れる時などに有効
例えば登録はしたいけど、その前にメールも一緒に送りたい。など

```js:sample.js
sendRegistData()
```

- ボタン（一時保存）処理

申請・登録同様便利。

```js:sample.js
sendPreserveData(inputCheckFlag)
```

- ボタン（次へ）処理

```js:sample.js
sendNextForm()
```

- ボタン（戻る）処理

```js:sample.js
sendBackForm()
```

- ボタン（一覧へ戻る）処理

```js:sample.js
sendBack()
```

###他の記事

IMのドキュメント内にある様々なAPIをまとめてみる その２
https://qiita.com/ChrisKasahara/items/ac038ce0b704574ada16

IMのドキュメント内にある様々なAPIをまとめてみる その３
https://qiita.com/ChrisKasahara/items/b2b5724af834b85d6269
CircleCIで【Rerun job with SSH】がコケた時の対処法

# なにこれ
CircleCIでRerun job with SSHをしてコンテナにSSH接続した時にコケた時の対処法です。
前にも同じエラーでコケた記憶があるので、備忘録。

# 前提条件
CircleCIでSSH認証済み
capistranoを使ったデプロイができる（自分の場合）

# 結論
githubにローカルのSSH認証鍵を追加していませんでした。以上

# 過程
circleCIで自動デプロイはできている。
でも、【Rerun job with SSH】すると`Permission denied (publickey)`と言われる。

困ったので[公式のガイド](https://circleci.com/docs/ja/2.0/ssh-access-jobs/)を見ました。

#
```
GitHub または Bitbucket での認証確認
想定どおりにキーが設定されているかどうかは、コマンド 1つでテストできます。 GitHub の場合は、以下を実行します。

$ ssh git@github.com
```
ローカルで`ssh git@github.com`を叩くと以下のエラーが出る。
#
```
Permission denied (publickey)
```
ありゃ？と思い、EC2で`ssh git@github.com`を叩く。
#
```EC2.
PTY allocation request failed on channel 0
Hi （あなたのgithub名）! You've successfully authenticated, but GitHub does not provide shell access.
Connection to github.com closed.
```
ここでgithubから認証できてないことに気づく。笑
#
```
cd ~/.ssh
ls
cat id_rsa.pub
```
`cat id_rsa.pub`の中身を[githubのSSH認証ページ](https://github.com/settings/keys)に登録する。

その後、もう1回CircleCIから`Rerun job with SSH`を試してみる。
#
```
circleci@5745cd68d045:~$
```
無事コンテナ内に入れました！

# 感想
公式を読むことって大事だと思いました。
公式を読んで、分からなければ2次ソース（Qiitaなどの外部サイト）を調べることを意識します。
読書要点「プリンシプルオブプログラミング」第4章

# 第4章 視点

プログラミングの判断基準となりうる視点、見方である。
プログラミングは選択の連続だが、効率的に将来を見据えた開発を行う際の判断材料となりうる。

### 凝集度

モジュールに含まれる機能の純粋さを表す指標として、凝集度がある。
凝集度には7つのレベルがあり、低いレベルのものほど望ましくない。
凝集度が高いほど、設計やコードが見通しやすく、保守性や再利用性が向上される。
また疎結合性も高まり、そもそも他のモジュールの影響も受けにくくなる。

- レベル1：暗合的強度

モジュール内の要素に関連性が認められない状態。
例えば、たまたま重複していた機能をひとまとめにしたような場合が考えられる。
モジュールの機能が完結していないことが多く、他モジュールとの結びつきが強い可能性も高く、保守性・再利用性が低い。

- レベル2：論理的強度

モジュールの機能を抽象的に捉えてまとめた状態。
例えばデータの入出力、データベースのCRUD操作などの機能群をひとまとめにしたものがあげられる。
このモジュールが呼び出されたときに実行される命令は一部だけなので、機能ごとの関連性は弱くモジュール強度は弱い。

一方で良い面もあり、関連した機能の一部の論理を共有できること、プログラマがコードを把握しやすいこと、情報隠蔽がある程度できていることがあげられる。

- レベル3：時間的強度

特定の時間に連続して実行する複数の機能を1つにまとめた状態。
機能間の関連性は考えられていないが、初期処理のように同時に実行される機能がひとまとめにされている。

- レベル4：手順的強度

時間的強度に加えて、問題の処理に必要な一連の機能のうち一部をひとまとめにした状態。
時間的強度に手順的な関連性が認められ、強度が増している。
手順的強度のモジュールは、モジュール内の機能を取り出すことができない。
ただし、機能を取り出すための機能選択を行うと論理的強度となり、強度を落とすことになる。

- レベル5：連絡的強度

手順的強度に加え、機能の間でデータの受け渡し、同じデータの参照を行っており、データに関して繋がっている状態。
データによる結びつきの分、手順的強度より強度が増している。

- レベル6：情報的強度

特定のデータ構造を扱う機能を1つにまとめた状態。
データの修正時の影響をそのモジュールに限定でき、データ構造内部の情報隠蔽にもつながる。
論理的強度の問題点であったデータの扱いにくさが解消されている。

- レベル7：機能的強度

モジュール内のすべての命令が一つの役割を果たすために関連しあっている状態。
機能を呼び出すときはそのモジュールを呼び出すだけで完結しているので、モジュールの独立性が非常に高い。

### 結合度

モジュール間の関係の密接さを表す尺度。
レベルが高いほど関係が薄く疎結合である。
モジュールは独立であるほど安定になるため、結合の度合いが小さいものを作りたい。
できるだけ変数を用いたやり取りを行い、変数を引き渡す際も無駄な変数が生まれないようにしたい。

- レベル1：内容結合

モジュールの内容を共有している状態。
一方の変更が他方に影響を与えることが多い。

- レベル2：共通結合

共通域のデータを、複数のモジュールが共同で使用している状態。
データの変更が他モジュールに影響を与えてしまいがちで、可読性、再利用性、安全性が低い。
データを扱うモジュールの設計をしっかり行い、共通結合を採用しなくて済むような設計を行いたい。

- レベル3：外部結合

外部宣言されたデータを共有している状態。
データを共有しているが、必要なデータだけを宣言しているので、その保証されている分結合度は弱くなる。

- レベル4：制御結合

呼び出し側が、呼び出されるモジュールの制御に必要なパラメータを渡している状態。
呼び出し側が呼び出される側の論理を知っている必要があり、ブラックボックス化できない。
また、呼び出される側のモジュールが論理的強度になってしまう。
ただ、データの共有を行っているわけではないのでモジュール間の結合度は弱くなる。

- レベル5：スタンプ結合

共通域にないデータ構造を、パラメータを介して受け渡している状態。
不必要なデータを受け渡すことがある点が結合度を強めている。

- レベル6：データ結合

モジュール間のインターフェースで受け渡されるデータがスカラ型のものだけである状態。
相手モジュールのブラックボックス化ができる上、強度的にも機能的強度となる。
また、スタンプ結合で渡されるデータ構造のうち、全てが使用されている場合はデータ結合とみなしても良い。

### 直交性

直交性(Orthogonality)は、独立性と分離性に分けて考えることができる。
片方の変更がもう片方に影響を与えない場合、そのコードは直交していると言える。
直交しているコードはを記述していくと、変更の局所化により生産性が向上し、粗結合性によりコードの再利用が高まる。
また、依存性が低いため、堅牢なコードになりテストも容易になる。

コードの結合度を下げると直交性のあるコードの記述につながる。
そのためには、レイヤー化を行いモジュール間の関係を整理しながら結合度を小さくしていくと良い。

### 可逆性

可逆とは、ある条件を加えると元の状態に戻る性質である。
プログラミングにおける判断では、常に可逆性を持つようにする。

内部要因、外部要因のどちらにかかわらず、今まで行ってきた方針を転換することになるケースは頻繁にある。
やり直しがきかない場合の被害は大きくなってしまう。

過度に特定の技術に依存せず、やり直しがきくコードを書くようにしたい。

### コードの臭い

わかりにくい、修正しにくい、拡張しにくいと感じられる部分は、不吉な臭いを放っていると言える。
そのようなコードはリファクタリングの対象となる。

リファクタリングを行うためには、リファクタリングの対象を見極める能力を身につける必要がある。
よく見るコード、長すぎるコード、大きすぎるコード、多すぎるモジュール、名前が合わないコードは臭いを放っているかもしれない。

### 技術的負債

プログラミングには、素早く汚いコードを書くという選択肢と時間をかけてきれいなコードを書くという選択肢がある。
時間が十分に確保されているのであれば後者を選択すべきだが、やむを得ずに前者を選択する場合もある。
前者を選択した場合にそのコードは、技術的負債を抱えることになる。
技術的負債とは修正しにくい、理解しにくい、問題を抱えたコードの汚い部分である。
今は問題なくとも後々になってその問題が露呈する可能性が高い。
また、技術的負債を返済できなかった場合には利子が発生する。
負債を返済しないまま変更を加えると、コードは更に乱雑になっていき返済することができなくなってしまう。
最終的には保守も機能追加もままならないコードになる。

負債の発生はある意味必然であり、負債を発生させないこと以上に負債とうまく付き合うことが重要である。
負債を発生させざるを得ない場合は、負債の完済まで見通した計画を立てておくのが良い。
また、負債を作らないに越したことはないため、可能な限りきれいなコードを書くように努めたい。
「クソデカ文学コンバータ」作ってみた

# とりあえず作ったもの
https://kusodeka.v-to-i.com

使ってみてね！！

# 概要
言わずもがな、テキストを入力すると「[クソデカ羅生門](https://anond.hatelabo.jp/20200611125508)」みたいにしてくれるアプリです。

# どうやっているのか
コードは公開できるほどのものではないですし、テキスト変換の精度も低いので、ここではざっくりとだけ説明します。とりあえずさっさと作りたかったのですごく雑なアルゴリズムです。

1. クソデカ羅生門と羅生門の差分から、どのような単語の前に「クソデカ」みたいな言葉（クソデカワードとします）が挿入されているかの辞書（クソデカ辞書）を作る（例："羅生門": ["正気を疑うレベルでデカい", "クソデカい", "クソデカ", "トチ狂ったクソデカさの"]）
2. Mecabで形態素解析して、入力テキストの単語の品詞を特定する。
3. 一般名詞など特定の品詞の単語の前にクソデカワードを挿入する。この時、クソデカ辞書から、入力の単語と類似度が一番近いkeyのvalueを選んで挿入する（gensim使用）
4. 最後に言語モデルで出力テキストの自然さを評価し、おかしな部分がないかチェックする（あまりうまく行ってない）
5. webアプリはflaskで作っています。

# 最後に
ぜひいろいろ遊んでみてほしいのですが、「こうすればもっとうまくいく！」「こういう機能をつけたら良いんじゃない？」などコメントいただけるととてもありがたいです！
感想としては、webアプリを簡単にでも自作できて楽しかったのと、座学で勉強していたNLPをちょっとでも実践に活かせたのでよかったです。
vue.jsでzlib.jsを利用して圧縮データをPOSTしてみた

## 問題
aws lambdaへCSVファイルをPOSTする際にペイロードが6MBに制限されているため、クライアント側で圧縮する方法を調査してみました。
lambdaのペイロード制限に関しては下記に記載があります。
https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/gettingstarted-limits.html

## 事前準備
zlibjsというライブラリを利用するため、下記のコマンドでインストールを行います。
`npm i zlibjs --save`

## 実装例
```html
<template>
  <div id="app">
    <form>
      <label>ファイル:</label>
      <input type="file" @change="onChange" /><br />

      <button @click="onSubmit">送信</button>
    </form>
  </div>
</template>

<script>
import {Zlib} from 'zlibjs/bin/gzip.min'

export default {
  name: 'App',
  data() {
    return {
      file: null,
      type: {
        1: 'zip',
        2: 'gzip',
      }
    }
  },
  methods: {
    onChange(e) {
      e.preventDefault()
      this.file = e.target.files[0]
    },
    onSubmit(e) {
      e.preventDefault()
      const reader = new FileReader()
      reader.readAsArrayBuffer(this.file)
      reader.onload = async() => {
        const plain = new Uint8Array(reader.result)
        const gzip = new Zlib.Gzip(plain).compress()
        console.log(gzip)

        const params = {
          data: btoa(String.fromCharCode(...gzip))
        }
        // axiosなどでPOST
        // axios.post(URL, params)
      }
    }
  }
}
</script>

```

## まとめ
zlibjsを利用してクライアント側で簡単にgzip圧縮することができました。
テキストファイルであれば大きくファイルサイズを落とせるため、通信にかかる時間も少なくできます。
（圧縮にかかる時間はプラスされるのでどちらがベストかは環境によって要検証）

サーバサイドが100%lambdaの構成になっている場合などで選択肢として検討するのは良いかもしれません。
【MySQL】電話番号と郵便番号の頭の0が消える現象

開発中に電話番号と郵便番号の頭の0が消える現象が起こりました。

これはデータベースの仕様ですが、WEBアプリを作成するにあたって、頭の０が必要なもの（電話番号や郵便番号等）を扱う機会は多いです。

##電話番号と郵便番号の管理方法

**郵便番号**
日本国内だけであれば、「3桁 + 4桁]で固定の為、Int(7)で管理する。
ただし、頭の0が欠けちゃうので、ハイフンを取り除き、桁数を固定し、ZEROFILLオプションを付けておく。

※ZEROFILLとは、桁数が足りなかった時に、足りない分を0で埋めてくれるもの。

海外も対応する必要あれば、ルールが国によって異なります。
何かしら法則があって調整できればIntで良いかもしれないですが、対応する国が増えたり、色々ややこしそうであれば、Varcharで管理しておくのが良いかもしれません。

**電話番号**
桁数がまちまちなので、Varcharで管理するのが一般的なようです。
WordでMendeleyを用いて引用した順（Vancouver Style）に番号をふるスタイル

Zoteroでのやりかたはこちら: [Zoteroで引用した順（Vancouver Style）に番号をふるスタイル](https://qiita.com/Philosophistoria/items/d85c64c1a4b130f48159)

---

BibTeX使えば簡単ですが，Wordで引用方法や順番をカスタムするのが何気に大変でMendeley（やZoteroもできるはず）を使うとその辺も解決できます（文献情報の取得と管理も便利）．ただ，少なくともMendeleyは既存ライブラリにある多くのスタイルではA-Z順などしかなく[要検証]，引用順（登場順）に番号を振るのがスタイルライブラリになかった[要検証]（今はアップデートがあるかもしれない）ので，以前作ったカスタムスタイルを共有します．

IEEE系のスタイルをベースに本文中で引用した順（Reference Order）で
- 一個だけ引用[1]
- 二個だけ引用[5,6]
- 三個以上引用[12-16]
- 再引用含めて番号がとびとびになる場合[3,15-16,22]
のようになるようにしました

Mendeley のView > Citation Sytle > More Styles... > Get More Styles > Download Styleで以下URIを入れるとDLできて，右クリックで編集サイト（重い）で編集できます．
(著者名が消えてたので修正)

https://csl.mendeley.com/styles/468090591/ieee-akiphvmi


参考：https://marineasty.wordpress.com/2013/11/05/mendeley-%E3%80%81zotero%E3%81%A7citation-style-%E3%82%92%E8%A8%AD%E5%AE%9A%E3%81%99%E3%82%8B/ 
参考：https://www.elsevier.com/ja-jp/solutions/mendeley/mendeley-guide/mendeley_citation_style
手動でPOSTしてXSSを起こしてみた

そもそもなぜXSSを手動で引き起こすのか。

普通はXSSなんていうものはウェブFW使用していれば出ないんですよね。
そう、普通はね……

今回何を血迷ったのか、なぜかFW外でPOST処理を行ってしまったために引き起こってしまった悲しいXSS対策とツールを入れるのが面倒でXSSを手動で実行する悲しいやり方のお話です。

## そもそもXSSって
簡単にいうと`<>&"'`とかhtmlで特殊な記号として扱われているやつをホニャホニャ処理してあげないと大変なことが起こりまっせってことです。

```html
<input type="text"  name="test" value="">
```
とかいうのがあったとしましょう。
ここに、こんな値をPOSTされたとしてHTMLが返却されたとしましょう。

```
">script></script>"
```

```html
<input type="text" name="test" value=""><script></script>"">
```

とかやられるとJSが起動しちゃったりするわけですね。
そうすると不必要にPOSTされちゃったり、データ抜かれたりするわけです。
イントラネットとかだとこの辺りはFW（ファイヤウォール）でブロックとかしてたり、イントラネットじゃなくてもブラウザがブロックしてくれたりしてくれるのですが、今回はそこはなしということで。

## 手動POST
GETは普段みなさんがWEBページ見ているときのやっているやつですね。気にしてないと思いますが。
POSTはデータを投稿する時に使うのですが、これが普通にアクセスしていてはできないときました。
なのでpowershellを使ってちょっと工夫しないといけません。

```powershell
$ie = New-Object -ComObject InternetExplorer.Application
$ie.visible = $true

# テストしたいURL
$indexUrl = "~~~~"
$ie.navigate($indexUrl, $null, $null, $null)
$postUrl = "~~"

# csrftokenがあればここでコピーしましょう
# ここでPOSTデータを通常のリクエストURLに乗せるような形で&
$post = "test=">script></script>"&test2=hogehoge"
$postDatabyte = [System.Text.Encoding]::UTF8.GetBytes($post)
$header = "Content-Type:application/x-www-form-urlencoded `r`n"
$ie.navigate($postUrl, $null, $null, $postDatabyte, $header)

```

これでデータをPOSTし結果としては、XSSブロックされたとか出るでしょう。

っていうかReactとかVueとか昨今流行っている物を使うとこの辺って出ないんですよね。
ま、ReactとかVue使いましょうねってことかもしれません。
CloudSchedulerを使って定期的にCloudFunctionを実行する方法

## アーキテクチャ図

![CS2CF.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17419/2b69ec4e-535b-ac3b-0547-1ec86aee4cd2.png)

## PubSub起動にする
HTTP起動だとURLを知っているとキックされてしまい、とても危険なので、PubSubをトリガーとする
参考
https://cloud.google.com/scheduler/docs/start-and-stop-compute-engine-instances-on-a-schedule?hl=ja
>重要: Cloud Functions の HTTP ファンクションには認証がなく、安全ではありません。HTTP ファンクションは保護されず、あらゆる HTTP リクエストに応答します。つまり、インターネットから誰でも、この Compute Engine インスタンスを起動および停止できてしまいます。

## PubSubでCFを作成する
トリガをPubSubにする
<img width="562" alt="CF_create_topic.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17419/2cb84a21-c418-cfcf-50f1-3e0ea2fe718b.png">

イベントは何でも良いんだけけど、ここではimport-eventとする。
![CF_topic.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17419/f020721b-36ad-76c2-40b6-7b7911fbed1f.png)

## ソース
[前回](https://qiita.com/tab02733/items/9558d530d04eb228e41d)のを流用して、PubSub起動に変更する。
変更箇所は1step
変更前

```python:MAIN.PY
def importDs(request):
```
変更後

```python:MAIN.PY
def importDs(event, context):
```
テストを行い、動くことを確認します。

## Schedulerの設定
PubSubのトピックに先程のimport-eventを入れる
ペイロードは正直なところまだよくわかってないので、後ほど調査して書きます。

![CS_setting.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17419/c79c3b63-e9ee-7e34-7e1f-34ffaeb511b2.png)
CircleCI【Errno::ETIMEDOUT: Connection timed out - connect(2) for IPアドレス】の解決法

# なにこれ
CircleCIでcapistranoを使った自動デプロイで、
`Errno::ETIMEDOUT: Connection timed out - connect(2) for IPアドレス`
というエラーにハマりかけたので、対処法を残しておきます。

# 前提条件
CircleCIにSSH認証済み

# 結論
AWSのセキュリティグループのインバウンドルールを編集で、
タイプ【SSH】のソースをカスタムで【0.0.0.0/0】にする。以上

# エラー文
#
```CircleCI.
cap aborted!
SSHKit::Runner::ExecuteError: Exception while executing as ********@13.114.24.148: Net::SSH::ConnectionTimeout


Caused by:
Net::SSH::ConnectionTimeout: Net::SSH::ConnectionTimeout


Caused by:
Errno::ETIMEDOUT: Connection timed out - connect(2) for IPアドレス

Tasks: TOP => rbenv:validate
(See full trace by running task with --trace)

Exited with code exit status 1
```
# 推察
SSH認証エラーじゃなくて、`Connection timed out`なのね。
IPアドレスに繋がらない？？
CircleCIからAWSにSSH接続をしてるわけなので、AWSのセキュリティグループを確認してみる。
<img width="1263" alt="f0417b26cc9b661b2a5ef563b2d2e023.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/629015/7540cc37-7ec2-8a4c-dc27-ca0e36a9d6f1.png">
SSHがマイIPになってるやん！
以下の画像のように、
マイIPから全てのIPを許可するように変更
<img width="1271" alt="ac41e4c22772033394367ea846debe20.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/629015/6374a155-955d-4c8c-ff54-9086aa03a483.png">

もう1回CircleCIを走らせます。
<img width="695" alt="8621cd314e82e70951c0c1a931760131.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/629015/425b30bd-01c8-e299-61be-c0a8c0c358d1.png">
通りました！良かった〜

RDS導入のときにもハマりかけたので
インバウンドルールは気をつけないとですね。
[React & TS] styled-componentsで動的にスタイルを変更する

# やりたいこと

よくみるこれをReact & TypeScript とstyled-componentsで実装したい

![animation.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317515/86017322-54d4-9889-eb4c-70f67bde9e18.gif)


# 作ったもの

今回はstyled-componentsに真偽値を渡しておき、値を変更する形としました。

[styled-components本家](https://styled-components.com/docs/basics#adapting-based-on-props)にやり方が記載されています。

```hamburgerIcon.tsx
import React from 'react'
import styled, { css } from 'styled-components'

const Hamburger = styled.div<{ isOpen: boolean }>`
  position: absolute;
  top: 16px;
  right: 30px;
  width: 22px;
  height: 18px;
  transition: all 0.4s;

  span {
    :nth-of-type(2) {
      top: 50%;
      left: 50%;
      -webkit-transform: translate3d(-50%, -50%, 0);
      transform: translate3d(-50%, -50%, 0);
    }
  }

  ${(props) =>
    props.isOpen
      ? css`
          span {
            :nth-of-type(1) {
              -webkit-transform: translateY(20px) rotate(-45deg) scale(0.8);
              transform: translateY(8px) rotate(-45deg) scale(0.8);
            }

            :nth-of-type(2) {
              opacity: 0;
            }

            :nth-of-type(3) {
              -webkit-transform: translateY(-20px) rotate(45deg) scale(0.8);
              transform: translateY(-7px) rotate(45deg) scale(0.8);
            }
          }
        `
      : ''}
`

const HamburgerLine = styled.span`
  position: absolute;
  width: 100%;
  height: 3px;
  background-color: #707070;
  transition: all 0.4s;

  &:nth-of-type(1) {
    top: 0;
  }
  &:nth-of-type(2) {
    top: 8px;
  }
  &:nth-of-type(3) {
    bottom: 0;
  }
`

type PropsType = {
  isOpen: boolean
  toggleIsOpen
}

export const MenuHamburger: React.FC<PropsType> = ({
  isOpen,
  toggleIsOpen,
}) => {
  return (
    <Hamburger isOpen={isOpen} onClick={toggleIsOpen}>
      <HamburgerLine />
      <HamburgerLine />
      <HamburgerLine />
    </Hamburger>
  )
}
```

# 特別にやったこと

[本家](https://styled-components.com/docs/basics#adapting-based-on-props)を見ると以下の様に書かれています。

```jsx
import React from 'react'
import styled, { css } from 'styled-components'

const Button = styled.button`
  color: ${(props) => (props.primary ? 'white' : 'palevioletred')};
`

export const MenuHamburger: React.FC<any> = () => {
  return <Button primary>Primary</Button>
}

```

しかしTypeScriptだと、型解決が出来ずにIDEなどに怒られてしまいます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317515/5eff9190-3594-b001-4597-d4f317fc0846.png)

以下の通り、styleの生成時に型を設定してあげましょう。

```tsx
import React from 'react'
import styled, { css } from 'styled-components'

const Button = styled.button<{ primary: boolean }>`
  color: ${(props) => (props.primary ? 'white' : 'palevioletred')};
`

export const MenuHamburger: React.FC<any> = () => {
  return <Button primary>Primary</Button>
}
```

数が多い場合は、typeにまとめてもよいですね！

```tsx
import React from 'react'
import styled, { css } from 'styled-components'

type ButtonProps = {
  primary: boolean
}

const Button = styled.button<ButtonProps>`
  color: ${(props) => (props.primary ? 'white' : 'palevioletred')};
`

export const MenuHamburger: React.FC<any> = () => {
  return <Button primary>Primary</Button>
}
```
[Unity]ML-Agentsでサンプルプログラムが動かないときの対処法

#何があった？
Unityで強化学習ができるML-Agentsを使って見ようと思い、[ここ](https://github.com/Unity-Technologies/ml-agents/blob/master/docs/Installation.md)を見ながら環境を構築していたら、こんなエラーが

>The API number is not compatible between Unity and python. Python API: API-13, Unity API: 1.0.0.

Google翻訳

>API番号はUnityとPythonの間で互換性がありません。 Python API：API-13、Unity API：1.0.0。

#原因

どうやら調べてみると、

```
pip install ml-agents
```
でインストールできるml-agentsは古いらしく、[ml-agents](https://github.com/Unity-Technologies/ml-agents/tree/latest_release)の最新リポジトリからインストールする必要があるみたい

#対処法

[ml-agents](https://github.com/Unity-Technologies/ml-agents/tree/latest_release)をダウンロードし、ターミナルでml-agents-masterまで移動し、以下のコマンドで最新リポジトリのml-agentsをインストールできます。

```
pip install -e ./ml-agents
pip install -e ./ml-agents-envs
```
vue/nuxt/typescriptでtweenmaxを使う

#はじめに
アニメーションを描こうと思った際に使おうと思ったら色々と情報が少なく調べるのが大変だったのでメモがてら

##導入
今回nuxtを使っていたのでnuxtでやりますがvueも大して変わらないと思います。
nuxt、typescriptの環境は整っている前提で進めます

```
"nuxt": "^2.0.0",
"@nuxt/types": "^0.7.7",
"vue-property-decorator": "^8.4.2"
```

まずは使いたいプロジェクトファイルで

```
npm install --save gsap
```

します。
私の時は*"gsap": "^3.3.4"*でした。

@types/gsapというモジュールがありますがv3になって必要なくなったそうです、むしろ衝突するからいれるなとのこと
[このページ](https://greensock.com/forums/topic/19861-usage-with-typescript/)に書いてありました

次にtsconfig.jsonに

```
{
  "compilerOptions": {
    ...
  },
  "files": [
    "node_modules/gsap/types/index.d.ts"
  ]
}
```

を追記します。おそらくなくても大丈夫ですが、typescriptエラーが出る場合があるようです。
[このページ](https://greensock.com/docs/v3/Installation)の一番下に書いてあります。

この時点で

```example.vue
<template>
   <p class="hello">おはようございます</p>
</template>
<script lang="ts">
import { Vue, Component } from 'vue-property-decorator';
import { gsap } from 'gsap';
@Component
export default class Example extends Vue {
  mounted() {
    gsap.to(".hello", { x: 100, duration: 300ms })
  }
}
</script>
```

みたいな感じでかけば動くと思います。
後はドキュメントに乗ってるのでそれを参考に書いてください
[ドキュメントはこちら](https://greensock.com/docs/)

#最後に
初期描画時の処理はmountedとかで描けばできるのですが、scroll時の処理がうまくできないので色々調べて頑張ってみたいと思います、やり方知っている方おられればコメント欄とかで教えていただけると嬉しいです。
RedshiftでUNLOADしたgzipファイルをLambdaのPythonで処理をして再びgzipしてS3へアップする

# やりたいこと
タイトルの通りですが、Redshiftにデータウェアハウスがあり、普段ELTで処理することが多いのですが、プログラミングによるデータ加工が必要な場合もあります。

RedshiftのUNLOADを利用することでRedshiftからSQLの結果をS3にgzipファイルを作成することが出来ますので、S3へのputイベントをトリガーにLambdaで処理して再びgzipした状態でS3にアップ、ということをしてみました。

# UNLOAD
Lambdaは現時点では3008MBが最大となります。今回のような処理はファイルサイズが増えると必然利用メモリ量が増えてしまいます。
そこで、[MAXFILESIZE パラメータ](https://docs.aws.amazon.com/ja_jp/redshift/latest/dg/r_UNLOAD.html) を設定することでLambdaへ渡すファイルサイズを調整します。
完全なるケースバイケースですが、今回は50MBで設定してみました。

# Lambda上のコード
トリガ設定は割愛します。

```
import json
import boto3
import urllib.parse
import os
import sys
import csv
import re
import traceback
import gzip
import subprocess

s3client = boto3.client('s3')
s3resource = boto3.resource('s3')
SEP = '\t'
L_SEP = '\n'
S3OUTBACKET='XXXXXXXX'
S3OUTBASE='athena/preprocessing/XXXXXXtmp/'

def lambda_handler(event, context):
    
    bucket = event['Records'][0]['s3']['bucket']['name']
    key = urllib.parse.unquote_plus(event['Records'][0]['s3']['object']['key'], encoding='utf-8')
    taragetfile=os.path.split(key)[1]
    outputprefixA=os.path.split(key)[0].split("/")[-1]
    outputprefixB=os.path.split(key)[0].split("/")[-2]

    outputdata = "";
    try:
        dlfilename ='/tmp/'+key.replace("/","")
        s3client.download_file(bucket, key, dlfilename)
        gzipfile = gzip.open(dlfilename, 'rt') 
        csvreader = csv.reader(gzipfile, delimiter=SEP, lineterminator=L_SEP, quoting=csv.QUOTE_NONE)
        for line in csvreader:
            # 1行ずつ様々な処理をして、outputdataに格納していく。
            # 割愛された処理の中に、利用しているimportがございます。
            # ご了承ください
    except Exception as e:
        print(e)
        raise e
        
    print("memory size at outputdata:"+str(sys.getsizeof(outputdata)))
    os.remove(dlfilename)
    uploadbinary = gzip.compress(bytes(outputdata , 'utf-8'))
    print("memory size at uploadbinary:"+str(sys.getsizeof(uploadbinary)))
    uploadfilename='processed_'+taragetfile

    try:
        bucket = S3OUTBACKET
        key = S3OUTBASE+outputprefixA+"/"+outputprefixB+"/"+uploadfilename
        obj = s3resource.Object(bucket,key)
        obj.put( Body=uploadbinary ) 
    except Exception as e:
        print(e)
        raise e
 
    return 0

```

# チューニングしてゆく
実際のファイルでテストをしたところメモリエラーとなってしまいました。
コードの途中に挟んでいる `str(sys.getsizeof(outputdata))` はその確認用で、メモリサイズを見て状況を把握しました。コードには書いてませんが、gzip自体の対象データへの圧縮率も見ておくと良いと思います。
なお私が今回扱ったデータは、gzip圧縮後50MBだったのですが、処理後のデータ＋圧縮後のデータで1000MBものメモリを要してしまいました。やはり実際にやってみないと分からないものですね。Pythonのメモリ事情をもう少し調べた方が良いかもしれません。

なおLambdaはメモリサイズを増やすとCPUリソースなども増えますので、処理内容とファイルサイズ次第ですが、一度最大の3008MBだとどのくらい処理が速くなるのかは確認すると良いです。今回も、メモリを倍にしたら処理時間が半分に、というケースすらありました。

もし定常的に行う処理の場合、ここでのチューニングがランニングコストに直結するので重要度が高いです。

# 語彙の無い感想
Lambdaめっちゃ便利
Terraformで最新のECS Optimized AMIのidを取得する方法

AWS公式ドキュメントにSSMパラメータストアから取得できることが記載されています。
https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs-optimized_AMI.html

```console
$ aws ssm get-parameters --names /aws/service/ecs/optimized-ami/amazon-linux-2/recommended
```

Terraformでimage_idのみ利用したい場合は、下記のコードで取得可能です。

```
data "aws_ssm_parameter" "ecs_ami_id" {
  name = "/aws/service/ecs/optimized-ami/amazon-linux-2/recommended/image_id"
}

# Launch COnfigration等で利用する側
  image_id = data.aws_ssm_parameter.ecs_ami_id.value
```
Nuxt.js のライフサイクルを踏まえてトラッキングタグ埋め込みを実装する

Web サイトのエンハンスやマーケティングといった目的でユーザー分析や広告表示用のトラッキングタグを埋め込むことは開発の現場でよくあると思います。
Google Tag Manager などのツールを利用すれば比較的簡単に設定できますが、ユーザー ID など Web アプリケーション固有のデータをタグと併せて設定したい場合など、アプリケーション側にタグ埋め込み用のコードを書かなければならない場面もあります。

Nuxt.js のユニバーサルモードにおけるライフサイクル処理には幾つか種類があり、実行タイミングおよび SSR/CSR の実行有無がそれぞれ異なります。

- [Vue\.js と Nuxt\.js のライフサイクル早引きメモ \- Qiita](https://qiita.com/japboy/items/b67bae0bac1aeefb2680)
- [【Vue\.js】middlewareやplugins, fetchなどの実行順序 \| HAFILOG](https://hafilog.com/execution-order)

これらを踏まえ、タグやカスタム変数を設定する際にどのライフサイクル処理を選択すれば良いのか整理します。

## TL;DR

- クライアントサイドで一度だけ実行したい処理（タグ埋め込みなど）は plugins を利用する。
- ページ遷移ごとに実行したい処理（ページ遷移時の状態を表す変数の設定など）は Vue Router を利用する。

## シチュエーション

以下のようなタグやカスタム変数に関する処理をまとめた composition を適宜 import して利用するようなシチュエーションを想定します。
コードは TypeScript で記述していますが、本記事の要点に関しては JavaScript の場合と特に大きな相違はありません。

```js:compositions/sample-tag.ts
export function useSampleTag() {
  return {
    addTag() {
      // タグの埋め込み
    },
    addCustomValue() {
      // カスタム変数の設定
    }
  }
}
```

## タグの埋め込み

タグの埋め込みは直アクセスされたタイミングで行い、 nuxt-link での遷移時には実行したくないケースが多いと思います。
このような用途では直アクセス時のみ実行される plugins が利用できます。

```js:plugins/sample-tag.ts
import { useSampleTag } from '~/compositions/sample-tag'

export default () => {
  const st = useSampleTag()
  st.addTag()
}
```

## カスタム変数の設定

ページ遷移ごとにログイン状態やユーザー ID などの値を設定したい場合は Vue Router の `afterEach()` が利用できます。

```js:plugins/router.ts
import { useSampleTag } from '~/compositions/sample-tag'

export default () => {
  ctx.app.router!.afterEach((to, from) => {
    if (process.client) {
      const st = useSampleTag()
      st.addCustomValue()
    }
  })
}
```

### middleware を利用する場合

middleware を用いても同様のコードで似たような処理は実現できます。
ただしユニバーサルモードでは直アクセス時に SSR でしか処理されないため、 window オブジェクトの利用といった CSR が前提となる処理が失敗する可能性があります。
特別な理由がない限りは Vue Router を利用するのが無難だと思います。

### Vuex の Store を利用する場合

Vuex の Store から getter で値を取得したい場合、 `addCustomValue()` の引数に Store を取るようにして内部で getter を呼び出す作りにしても良いかもしれません。

```js:compositions/sample-tag.ts
import { Store } from 'vuex/types'
import { AuthenticationState } from '~/store/authentication'

export function useSampleTag() {
  return {
    addTag() {
      // タグの埋め込み
    },
    addCustomValue(store: Store<AuthenticationState>) {
      // カスタム変数の設定
    }
  }
}
```

```js:plugins/router.ts
import { Context } from '@nuxt/types'
import { useSampleTag } from '~/compositions/sample-tag'

export default (ctx: Context) => {
  ctx.app.router!.afterEach((to, from) => {
    if (process.client) {
      const st = useSampleTag()
      st.addCustomValue(ctx.store)
    }
  })
}
```
Node.js 開発環境構築

# はじめに
Node.js 未経験者が初めて開発環境を構築したときの備忘録です。
Homebrew や git はインストール済みの前提です。

## 環境
```
macOS Catalina version 10.15.5
Homebrew 2.4.2
git version 2.22.0
fish, version 3.0.2
```


# Node.js インストール
## anyenv インストール
こちらはnodenv以外にもあるpyenv等、様々なenv系のツールをまとめてくれるもの。
今回はこちらを経由して nodenv をインストールする。

https://github.com/anyenv/anyenv

インストール

```sh
$ brew install anyenv
```

初期化します。

```sh
$ anyenv init
# Load anyenv automatically by adding
# the following to ~/.config/fish/config.fish:

status --is-interactive; and source (anyenv init -|psub)
```

なんか出るので従います。

```sh
$ vi ~/.config/fish/config.fish

# 以下を追記
status --is-interactive; and source (anyenv init -|psub)
```

config を読み直します

```sh
$ source ~/.config/fish/config.fish
ANYENV_DEFINITION_ROOT(/Users/hoge/.config/anyenv/anyenv-install) doesn\'t exist. You can initialize it by:
> anyenv install --init
```

またなんかでるので従います。

```sh
$ anyenv install --init
Manifest directory doesn\'t exist: /Users/hoge/.config/anyenv/anyenv-install
Do you want to checkout ? [y/N]: y
Cloning https://github.com/anyenv/anyenv-install.git master to /Users/hoge/.config/anyenv/anyenv-install...
Cloning into '/Users/hoge/.config/anyenv/anyenv-install'...
remote: Enumerating objects: 48, done.
remote: Total 48 (delta 0), reused 0 (delta 0), pack-reused 48
Unpacking objects: 100% (48/48), done.

Completed!
```

### nodenv をアップデートできるようにする
以下を参考に anyenv-update プラグインをインストールする

https://qiita.com/silmisilon/items/be41110fa786fc514b9a

https://github.com/znz/anyenv-update
```sh
$ git clone https://github.com/znz/anyenv-update.git (anyenv root)"/plugins/anyenv-update"
```

これにより、最新バージョンの Node.js もインストールできるようになる


## nodenv インストール
https://github.com/nodenv/nodenv

インストール

```sh
$ anyenv install nodenv

# shell 再起動
$ exec $SHELL -l
```

Node.js インストール時に yarn もインストールされるようにプラグインを追加
https://github.com/pine/nodenv-yarn-install

```sh
$ git clone https://github.com/pine/nodenv-yarn-install.git (nodenv root)"/plugins/nodenv-yarn-install"
```

## Node.js インストール
https://nodejs.org/ja/

```sh
# 一覧を確認
$ nodenv install -l

# バージョンを指定してインストール
# https://nodejs.org/ja/download/ を確認して、最新のLTSバージョンを選ぶ
$ nodenv install 16.17.0

# デフォルトで使用するバージョンを設定
$ nodenv global 16.17.0

# shell 再起動
$ exec $SHELL -l

# Node.js のバージョンを確認
$ node -v
v16.17.0

# yarn のバージョンを確認
$ yarn -v
1.22.19
```

### 最新バージョンの Node.js が見つからない場合
最新バージョンの Node.js が見つからない場合、 nodenv をアップデートする
```sh
$ anyenv update

# 再度一覧を確認すると最新のバージョンが出てくるはず
$ nodenv install -l
```

### インストール時にエラーが出た場合
インストール時に以下のエラーが出た場合

```sh
default-packages file not found
```

default-packages ディレクトリを作成する

```sh
# default-packages を作成
$ touch $NODENV_ROOT/default-packages

# 再インストール
$ nodenv install 12.22.8
```

### ディレクトリごとにバージョンを切り替えたい場合
`nodenv local` を使うと特定のディレクトリで使用するバージョンを設定できます。

```sh
$ cd hoge/
$ nodenv local 12.22.8
# 上記を実行すると .node-version というファイルが作られる

$ node -v
v12.22.8
```

ここまでで Node.js を使えるようになりました。

# Visual Studio Code
Node.js の開発を行う際に便利なエディタ

以下よりzipをダウンロード
https://code.visualstudio.com/
解凍するとappファイルが出てくるのでApplicationsディレクトリに移動してインストール完了。

<br>
:white_check_mark: 環境構築完了です。

# 参考
https://qiita.com/kyosuke5_20/items/eece817eb283fc9d214f