OmniAICreator/Qiita-1.07M huggingface dataset:
google-api-client,optimist(trollop)を使うと出てくる3つのエラーとその解決法

# 概要
Youtube APIを使用し動画情報を 取得し一覧表示するサイトを作成中にハマってしまったエラーが3つありましたので、エラーパターンと解決法を書き残しておきます。
Gemのバージョンにも関わってきますし、デプロイしているサーバの環境にも依存しますので、このエラー現象と解決方法は2019年7月段階のものだということを念頭に置いてください。

# 環境
- Ruby 2.4.1
- rails 5.2.3
- google-api-client 0.8.6
- optimist 3.0.0(旧trollop)
- サーバ
 - heroku

# エラーパターン1：oprimist(trollop)のエラー
```SQL:herokuのlog
2019-06-18T11:44:23.578660+00:00 app[web.1]: Error: unknown argument '-p'.
2019-06-18T11:44:23.578668+00:00 app[web.1]: Try --help for help.
```
このパターンはoptimist::optionsメソッドにコマンドライン引数がぶっこまれていることが原因です。
heroku上だと-pと-eオプションを付けた状態でrails sされます。
参考(optimistのソース)：https://github.com/ManageIQ/optimist

ローカル環境でサーバを起動する際にrails s だけで起動している場合は起きないのにheroku上だと上記のエラーが出てきます。ためしにローカル環境でrails s -p ポート番号(3000) -e development で起動して実行するとheroku上で動かした時と同じエラーが出ることが確認できます。
もしくはbyebugでoptimist::optionsをコールする直前のARGVの値を参照してみると確認できると思います。

# エラーパターン1：解決法
1．ARGVの値をローカル変数に退避
2．ARGVを空にする
3．optimist::optionsをコール
4．ARGVの値を元にもどす

```SQL:修正コード
arg_array_save = []
arg_array_save = ARGV.shift(4)
opts = Optimist::options do
     ・・・中略・・・
arg_array_save.each do |arg_push|
  ARGV.push(arg_push)
end
```
こんな感じになります。ARGVはメソッド内で値を直接操作すること(代入等)ができないので、shift,push等のメソッドを使用していじっています。
もっといい方法があればぜひコメントで教えて頂きたいです。

# エラーパターン2：ArgumentError
```SQL:herokuのlog
2019-06-24T14:54:19.983862+00:00 app[web.1]: F, [2019-06-24T14:54:19.983799 #4] FATAL -- : [d9d33d3a-e4a5-4ba3-a0f6-7286855ae09a] ArgumentError (header field value cannot include CR/LF):
```
このパターンは上記のパターン1を解決した後にでてくる可能性があります。
原因はgoogle-api-clientのバージョンとrubyのバージョンがうまく対応できてないみたいです。gem内のソースコードレベルの原因は解明できてません。~~gem内の解析めんどくさい~~

解決法は2つありますが、解決法パターン1推奨です。

# エラーパターン2：解決法パターン1
Rubyのバージョンを 2.4.1にする。
なぜかわかりませんが2.4.6から2.4.1にバージョンを落とすと解決しました。エラーメッセ的には改行コードだと思うので、その辺の処理がバージョンによって違うのでしょうか。
わかる人いたら教えてください。

# エラーパターン2：解決法パターン2
google-api-client を最新のバージョンにする。
2019年7月現在だと最新は0.30.4です。
Gemを最新にするとエラーパターン2のエラーメッセージは消えるのですが、後述するエラーパターン3の状態に陥ってしまうので、この解決法はあまりお勧めできないです。
GitHubのReadmeを全部翻訳してバージョンによる変更点を全部理解すると解決できるかもしれないです。

# エラーパターン3：LoadError
```SQL:herokuのlog
2019-06-20T16:12:55.170597+00:00 app[web.1]: /app/vendor/bundle/ruby/2.4.0/gems/bootsnap-1.4.4/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:33:in `require': No such file to load -- google/api_client.rb (LoadError)
```
ファイルが読み込めてないみたいですが、heroku内のファイルとかいじれるんでしょうか。

# エラーパターン3：解決法
boot.rbのbootsnapをOFFにする。

これでエラーメッセージ自体は消えますが、また同じようなエラーが今度はactivesupportで出てきます。
この解決ルートは永遠と上記の解決法を繰り返す羽目になりそうだったので、やめました。
ですので、エラーパターン3の明確な解決法はわかってない状態です。

# まとめ
- エラーパターン3の最終的な解決法は不明。エラーパターン2でrubyのバージョンを変えて解決する方が現実的。
- Gemのバージョンアップなどで仕様が変わるのでReadmeを読まないとわからない。
- 英語を勉強する必要がある。
マルチプラットフォームGUI作りに挑戦 ~tcl/tkの環境構築~

#環境
OSX 10.14.5
Homebrew 2.1.6

#イントロ
画像処理プログラムを動かすオープンソースでマルチプラットフォームな簡易GUIを簡単に作成したかった。
候補にはopencvのhighguiをこねくるか、cvuiを入れるか、qtをお勉強するかだったが、パラメタのやり取りを外部のテキストファイルとやり取りしたい、でも学習コストはかけたくないとか考えるうちにtcl/tkを利用することにした。
`shellscript`が得意なら、この選択肢はとてもいいはず。
`WSL2`の普及への期待からもshell言語は色々試すのは価値があるかもと。

もともとOSXにはtcltkが入っているが、どうせなら最新バージョンを入れるところから。
でも元気がなかったのでビルドはやめてHomebrewを使った。
(本当のところは、デフォルトで入っている`tcl/tk`さんは、表示がもっさりしていて、refleshかけないとbuttonのtextが表示されず、一度reinstallしたかったから)

#現在のバージョン確認
```
% wish 
% info patchlevel
8.5.9
```
実はそんなに古くないらしい
(最新は8.6.9 2019/07/01現在)

#Homebrewでのインストール
```
brew update
brew install tcl-tk
```
`tcltk`ではなく`tcl-tk`とハイフン入りなのに注意
(良い子は事前に調べておきましょうね）

```
If you need to have tcl-tk first in your PATH run:
  echo 'setenv PATH /usr/local/opt/tcl-tk/bin:$PATH' >> ~/.tcshrc

For compilers to find tcl-tk you may need to set:
  setenv LDFLAGS -L/usr/local/opt/tcl-tk/lib;
  setenv CPPFLAGS -I/usr/local/opt/tcl-tk/include;

For pkg-config to find tcl-tk you may need to set:
  setenv PKG_CONFIG_PATH /usr/local/opt/tcl-tk/lib/pkgconfig;

==> Summary
🍺  /usr/local/Cellar/tcl-tk/8.6.9: 2,983 files, 50.1MB
```
と言われるので、自前でパスを通しましょう(次節)

# tclshとwishのパスの整理
### 実行ファイル
もともと通っているものは`which wish`、`which tclsh`と`ls -l`で調べておく。
なお私のデフォルトシェルは`tcsh`です。

```
% ls -l `which tclsh wish｀
lrwxr-xr-x /usr/bin/tclsh -> tclsh8.5
lrwxr-xr-x /usr/bin/wish -> wish8.5
```
`tclsh8.5`、`wish8.5`にリンクされているだけのようなので、削除して`~/.tcshrc`に`brew`が教えてくれたパスを書き込んでパスの再読み込み(`source ~/.tcshrc`)しておく。
### ライブラリパス
pkg-config用に通しておく。`C++`でプログラミングしない人は無視していい。

# デモスクリプトの確認
widget Demoを見てみる。
まず、`info patchevel`でバージョン確認。

```
% wish
% info patchlevel
8.6.9
```
出てきているwishのwindowから、ファイルメニューで`File>Run Widget Demo`
無事に表示されればOK。
Demoを見ているだけでも楽しいですね。


## 参考
[Homebrew] (https://formulae.brew.sh/formula/tcl-tk)
複数の yield returnで失敗

今や滅多に使わないyield return ですが、
yield returnの方が可読性良いな、と思った個所があったので久々に書いたらしくじりました。
ダサい失敗で恥ずかしいですが、2回もやったので備忘録。


##概要
yield return で返り値を複数パターンの条件で分けたかった。

###失敗したコード

```C#
foreach (var data in datalist)
{
    var data2 = hoge(data);

    if (data2 == null)
    {
        yield return null;
    }
    yield return hogehoge(data2);
}
```

ifの中のyield return ではそのループ処理は止まりません。
よってifの後のyield return も評価されます。
この時一回のループで2要素が返されてしまいます。
今回は```hogehoge(null)```で例外が出たので気づけましたが、
例外出ないと結構厄介なバグになるかもしれませんね。

どうもreturn 句でそこで処理が止まるイメージがある様です。
でもそしたらループ毎止まるので、その点でも間違ってますね。


###上手く行くコード

```C#
foreach (var data in datalist)
{
    var data2 = hoge(data);

    if (data2 == null)
    {
        yield return null;
    }
    else
    {
        //ifでどちらかのyield 
        yield return hogehoge(data2);
    }
}
```

```C#
foreach (var data in datalist)
{
    var data2 = hoge(data);

    if (data2 == null)
    {
        yield return null;
        //ちゃんと帰りましょう
        continue;
    }
    yield return hogehoge(data2);
    
}
```



```C#
foreach (var data in datalist)
{
    var data2 = hoge(data);

    if (data2 == null)
    {
        //null不要ならそのままcontinue;
        continue;
    }
    yield return hogehoge(data2);
    
}
```

```C#
foreach (var data in datalist)
{
    var data2 = hoge(data);
    //1行で表現したり
    yield return data2 == null ? null : hogehoge(data2);
    
}
```


yield returnを複数書かなければならないと思ってしまう設計自体が悪そうなんで、
もっと構造自体を変えられないか検討した方が良いかも知れませんね。
[JS]連想配列に任意の値と等しい要素があれば、それらのインデックスを全て取ってくるサンプル~map編~

# 2019/07/21追記
コメント欄に、とても的確なご指摘を頂いております。
内容としては、

- 今回はmapではなくforEachで書くべき
- 偽を捨てるなら三項演算子じゃなくif文ワンライナーで良いのでは

とのことでした。
まさに、おっしゃられる通りですので、ぜひ皆様にもコメント欄を見てもらえればと思います。
また、その過程で本記事は、あえてそのまま残しておこうと思います。

# いきなり結論コード
<p class="codepen" data-height="265" data-theme-id="0" data-default-tab="js,result" data-user="riotam4" data-slug-hash="vqrbjQ" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="配列の一致インデックス取得map版">
  <span>See the Pen <a href="https://codepen.io/riotam4/pen/vqrbjQ/">
  配列の一致インデックス取得map版</a> by riotam (<a href="https://codepen.io/riotam4">@riotam4</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>
# 解説
```javascript:キモになる部分
      let idx = []
      this.users.map((v,k) => v.gender == '男' ? idx.push(k) : []) // ここはコメント欄で指摘を頂いております。そちらもご参照ください。
      return idx
```
まず、idxという変数を用意。
this.usersにデータが入っているので、それらをmapします。

mapメソッドの引数は、関数型で書く必要があり、アロー関数と三項演算子でワンライナーにしてしまってます。
要は、this.usersをv(value)とk(key)にして順番に呼び出す。
→そのvのgenderキーが`'男'`と等しければ、idx変数にpushしていく。そうじゃなければ、[](←何もしない)という感じ。
## ちなみに
`[]`部分は`null`や`''`、`0`など、falsyなものなら、挙動に問題はありません。

2019/07/21追記 : ここはコメント欄で指摘を頂いております。そちらもご参照ください。
# 最後に
さいごまでありがとうございました。
# 関連記事
同じ挙動のものをflatMapで作ってみました。[こちら](https://qiita.com/riotam/items/3a85a13696652d0ce481)
アプリのログは何を取ればよいの？

この記事は同僚へのアプリのログは何を取るべきなのだろうか？という悩みの説明のために作成しました。
私自身アプリを開発した事はないので、勉強としての情報収集でもあります。

アプリを作成したい or したけれども、利用者は何に興味があるのかを調べたい方のきっかけになれば幸いです。

# 対象の方
+ 同僚へのアプリのログは何を取るべきなのだろうか？という悩みの説明
+ アプリを作成したい or したけれども、利用者は何に興味があるのかを調べたい方

# 参考情報（大感謝）
**Wantedly, Inc.の住友孝郎さん**がDroidKaigi2018で講演された
[「アプリを成長させるためのログ取りとログ解析に必要なこと」の発表資料](https://www.slideshare.net/TakaoSumitomo/ss-87583483)を参考にさせていただきました。

# ログを取るのはなぜか？
+ ログだけでユーザの操作を把握できるようにする。
 + ログから異常な動作を探る		
+ 機密情報はいれない		
 + 取扱の困るデータはログに含めないこと
 + パスワード
 + メッセージ
 + 利用規約やプライバシーポリシーに反する内容

# まとめ
## エンジニアリングとしての指標
 + **クラッシュ率**
 + **ANR発生率**(Application Not Responding)
 + **レンダリング時間**
 + **APIのレスポンス時間**

## 非エンジニアリングとしての指標(Firebase Analytics等で確認可能)
 + **インストール数**（アプリの場合には単純にインストール数）
 + **アクティブユーザ数**
  + DAU(Daily Active User)：１日のユニークユーザ数
  + WAU(Weekly Active User)：７日間のユニークユーザ数
  + MAU(Monthly Active User)：１か月のユニークユーザ数（場合により２８日や３１日で換算するなど違いがある）
 + **リテンション率（継続率）**
  + 期間ごとにユーザを分けて分析する(Firebase Analytics等で確認可能)
  + コホート分析（https://biz.trans-suite.jp/11257）

```
★注意点★
　多機能なアプリの場合、機能毎にリテンション率を確認しないと、どの機能がユーザを繋ぎとめているのか確認する事が困難
```

+ **コンバージョン率**（https://www.seohacks.net/basic/terms/conversion-rate/）
 + CVR(Conversion Rate)
 + Webサイトに発生したアクセスのどれだけがサイトの成果＝コンバージョン（商品の購入や申込み）を達成したかの割合を表す指標
 + 一般的にはCVR（コンバージョン率）＝コンバージョン数 / 訪問数（セッション数）で算出。

```
例：1000件のセッションで10件のCVを獲得した場合はCVRは1％となります。
　流入経路や商品カテゴリ単位、個別ページ単位といった分析単位ごとのパフォーマンスの良し悪しを表す指標です。
　ビジネスの成果を表すCVRの状況から、Webサイトで施策の優先度や施策を行うページを洗い出すことが出来ます。
```

# 必要となるインフラ
+ **ログ収集サービス**(例：Firebase Analytics)
+ **データストア**(例：Firebase Analytics / Google BigQuery)
+ **抽出環境**(例：Google BigQuery)
+ **可視化**(例：Google BigQuery / Excel)

# アプリケーションで採取するべきログの種類

## イベントログ
 + **Viewの操作**
  + タップした、スワイプしたなどの端末での操作状況
 + **直接的なコンバージョン**
  * ログは多ければ良いデータではないが、採取する事で分析の幅が広がるため、採用する分析ツールによるがViewとコンバージョン両方採取する事が良い

|項目例|備考|
|:---|:---|
|ユーザ識別子||
|画面名||
|イベント名|タップ / ロングタップ / スワイプ / PCであればクリック・ダブルクリック等|
|イベント追加情報|View ID / コンバージョンIDなど|


**<イベントログ例>**

|ログ日時|ユーザ識別子|画面|画面追加情報|イベント|イベント追加情報|
|:---|:---|:---|:---|:---|:---|
|2019/xx/xx 99:99|99999|ログイン|クリック|||
|2019/xx/xx 99:99|99999|メニュー|クリック|メニューId:9999||
|2019/xx/xx 99:99|99999|通知一覧|UserId:99999|クリック|通知Id:xxxxx|
|2019/xx/xx 99:99|99999|通知詳細|UserId:99999;通知Id:xxxxx|クリック||
|2019/xx/xx 99:99|99999|メニュー|クリック|メニューId:8888||
|2019/xx/xx 99:99|99999|活動情報登録||クリック|データ登録|

## スクリーンログ
 + 画面名
 + 遷移先の画面名
 + 滞在時間
 + アプリからの離脱もわかるように

|項目例|備考|
|:---|:---|
|ユーザ識別子||
|画面名||
|次の画面名||
|滞在時間||
|必要があれば定量的な数字をロギング|・画面のどこまでスクロールしたのか<br>・どの画面が何秒表示されたのか|

**<スクリーンログ例>**

|ログ日時|ユーザ識別子|画面|画面追加情報|次の画面|次の画面追加情報|滞在時間(ms)|
|:---|:---|:---|:---|:---|:---|:---|
|2019/xx/xx 99:99|99999|ログイン||メニュー||20000|
|2019/xx/xx 99:99|99999|メニュー||通知一覧|UserId:99999|5000|
|2019/xx/xx 99:99|99999|通知一覧|UserId:99999|通知詳細|UserId:99999;通知Id:xxxxx|40000|
|2019/xx/xx 99:99|99999|通知詳細|UserId:99999;通知Id:xxxxx|||60000|
|2019/xx/xx 99:99|99999|メニュー||活動情報登録||120000|
|2019/xx/xx 99:99|99999|活動情報登録||||180000|
LiBOVRを.libから.dllに変換する

# 概要
Oculus riftのtouch controllerを用いたアプリケーションをC#で書く必要があり、libをdllに変換しました。libをdllに変換する基本的な方法は、visual studioのプロジェクトでdll生成用のものがあるので、それを使う場合が多いです。しかし今回はlibのプロジェクトもしくはソリューションを用いてdllに変換します。ただし、以下の方法はOculus SDKのLiBOVRのみに適用できる方法かもしれないので、参考にする方は注意して頂けると幸いです！

# 実行環境
次に実行環境を示します。

| Soft and Hard       |       バージョン | 
|:-----------------:|:------------------:|
| Windows 10 pro         |              16.04 |  
| Oculus SDK         |              1.37.0 |  
| Visual studio 2015          |             Community Update 3 |

# 変換手順
今回の手順では、Oculus SDKのLiBOVRのディレクトリ内にあるVisual studio 2015用のプロジェクトを用いてdllを出力します。まずは、そのプロジェクトを開きます。該当のプロジェクトは、Oculusの公式ページからダウンロードできるOculus SDKの中にあるので、Oculus SDKをダウンロードします。

[Oculus SDKをダウンロード](https://developer.oculus.com/downloads/package/oculus-sdk-for-windows/)

ダウンロード後、zipを解凍し、以下のディレクトリにある、プロジェクトを開きます。

```
\ovr_sdk_win_1.37.0\LibOVR\Projects\Windows\VS2015
```

以上が前準備となります。次に、dll用のVisual Studio 2015のプロジェクトの設定手順について述べます。

#### 1. プロジェクト名を選択した状態で、右クリックし、プロパティを開きます。
![5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169342/9c6eb212-37b1-66de-d135-26ef6b8a4aa6.png)



#### 2. 構成プロパティ -> プロジェクトの規定値 -> 構成の種類をダイナミックライブラリ(.dll)に変更します。
![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169342/46f82461-a7f0-3c6e-dddb-0aac3c626167.png)



ここで、dllが生成されるディレクトリを変更する場合には、全般->出力ディレクトリの指定ディレクトリを変更します。

#### 3. リンカー -> 入力 -> 追加の依存ファイルに、msvcrt.lib,winmm.lib,setupapi.libを追加します。
![3.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169342/d7275170-4d94-4743-3945-01309b263c9b.png)


#### 4. プロパティページの右下のOKを選択し、プロパティページを閉じます。
#### 5. プロジェクトをリビルドし、dllを生成します。

![4.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169342/bf05c267-d9ee-25ac-dcc9-1961bfe64f67.png)


生成されたdllは、全般->出力ディレクトリで指定しているディレクトリに出力されています。

# まとめ
今回は、C#でアプリケーションを作るために、dllをlibから生成しました。次は、C#を用いたアプリケーションを作りたいと思います。また、上記の内容で間違っている箇所や、改善したほうが良い点等ありましたら教えていただけると嬉しいです！

# 参考にしたもの
https://forums.oculusvr.com/developer/discussion/7979/sdk-0-3-1-and-compiling-libovr-to-dll
https://stackoverflow.com/questions/40780047/build-lib-as-dll-instead
[JS]連想配列に任意の値と等しい要素があるとき、findIndex関数だとインデックスが1つしか取得できない件

# 今回したいこと
```
    users:[
      {name:'花子', age:'10', sex:'女'},
      {name:'太郎', age:'11', sex:'男'},
      {name:'次郎', age:'12', sex:'男'},
    ],
```
このデータから、sex='男'のuserのインデックスを取得したい。
つまり、1と2が変えればOK。

# findIndexを使ったけど1つしか取れない
<p class="codepen" data-height="265" data-theme-id="0" data-default-tab="js,result" data-user="riotam4" data-slug-hash="EBRmpo" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="findIndexサンプル">
  <span>See the Pen <a href="https://codepen.io/riotam4/pen/EBRmpo/">
  findIndexサンプル</a> by riotam (<a href="https://codepen.io/riotam4">@riotam4</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>
ご覧の通り、男は次郎くんもいるのですが、帰ってくるインデックスは1…つまり、太郎君しか認識できません。
これは、よくない。
# fileterを使ったけど、インデックスが取れない
<p class="codepen" data-height="265" data-theme-id="0" data-default-tab="js,result" data-user="riotam4" data-slug-hash="orywGK" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="filterサンプル">
  <span>See the Pen <a href="https://codepen.io/riotam4/pen/orywGK/">
  filterサンプル</a> by riotam (<a href="https://codepen.io/riotam4">@riotam4</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>
今度はfilterを使ってみました。
次郎君も認識できますが、インデックスを返してくれないので、微妙。
# 結論

 - findIndexは要素１つしか取れない
 - 2つ目以降の一致したインデックスは無視されるので注意
 - 全て取りたいなら、forEach等で回して、一致するインデックスを１つずつ配列にpushしていくパターンが早そう

# 追記
Twitterで募集してみたが、やはり一発で全インデックスを取得できるメソッドはなさそうな。
みなさま、貴重なご意見ありがとうございました。

結局、map,flatMap,reduce等を使って作る感じとのこと。
ということで、サンプルコードつくって関連記事に貼ってますので、参考にどうぞ。
ありがとうございました。
# 関連記事
mapメソッドで作ってみました。[こちら](https://qiita.com/riotam/items/393d466bd5323419e442)
flatMapメソッドで作ってみました。[こちら](https://qiita.com/riotam/items/3a85a13696652d0ce481)
VisItをコマンドラインから起動してsiloファイルを可視化したいだけ

# 概要

フリーの可視化ソフトVisItを用いて可視化する準備方法をご紹介します。

# VisItとは

ローレンスバリモア国立研究所が開発しているフリーの可視化ソフトです。siloやvtkのような、流体計算結果を可視化するのに重宝しています。

# インストール

## インストール環境

私の環境は以下になります。

macOS: 10.14.5
MacBook Pro(13-inch, 2017モデル)

## インストール方法

[ここ](https://wci.llnl.gov/simulation/computer-codes/visit/executables)から最新版をダウンロード(2019/07/04現在3.0が最新)。VisIt-3.0.0-10.13.dmgをダブルクリックすると下図のようなウィンドウが出てくるので、そこからアプリケーションにコピーします。

![スクリーンショット 2019-07-04 12.34.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/335400/11cc3d36-9a8a-e60b-1989-08b184c1ca97.png)

# 起動

VisItをダブルクリックすることでも起動できますが、今回はコマンドラインから起動することを目標とします。

## VisItの場所

Macの場合、VisItは以下の場所にあります。

```
/Applications/VisIt.app/Contents/Resources/bin/visit
```

## 可視化用Pythonスクリプト

このvisitを用いて、以下のPythonスクリプトを実行してみましょう。

```python
# Make a plot
OpenDatabase("/Applications/VisIt.app/Contents/Resources/data/globe.silo")
AddPlot("Pseudocolor", "speed")
DrawPlots()
 
pc = PseudocolorAttributes(1)
pc.colorTableName = "hot_desaturated"
SetPlotOptions(pc)
```

## 実行とその可視化結果

以下のようにして実行します。いちいちパスを打つのが面倒という方は`/Applications/VisIt.app/Contents/Resources/bin`にパスを通しておくとよいでしょう。

```
/Applications/VisIt.app/Contents/Resources/bin/visit -cli -s sample.py
```

すると以下のようなウィンドウが現れます。

<img width="752" alt="スクリーンショット 2019-07-04 12.51.14.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/335400/0bc66694-37cc-3e66-8257-f98d176b007d.png">

マウスでグリグリと回転させることも可能です。

<img width="752" alt="スクリーンショット 2019-07-04 12.51.27.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/335400/881ac704-c765-4cbe-3cc3-458bbd7ed4a0.png">

# 結言

今回は`/Applications/VisIt.app/Contents/Resources/data/globe.silo`というサンプルデータを描画してみましたが、他のSiloファイルも描画してみたいところです。
SPFを使ってメール送信者偽装を防ごうとしてみる


## SPFってどういうことができるの？
Sender Policy Framework　の略。
なりすましメール絶許マン。

+ メールはSMTPという仕組みを使って配信される
+ 配信元メールアドレス「from」は結構自由にSMTPに送れてしまう。偽り放題。
    + 悪意のある人が、自分のフリをして、迷惑メールを他人に送っていたら困る
      + 自分にクレームが来る…
      + 自分のドメインの信用が下がる…

そこで

+ 自分が持っているドメインのメールアドレスの配信元を宣言する仕組みがSPF

## ざっくりすぎる図解
![SPF.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/162838/55783980-9abb-3024-ec8a-167f812e4813.png)

1. メールを送信[^1]
2. 受信メールサーバが、メール情報にある「送信元」が指定されているドメインDNSが許可している送り元からの送信かを確認
3. DNSのSPFの内容で正規送り元か判断がつく！[^2]
4. 送り先の人は偽装メールを受け取らずに済んでみんな幸せ

## SPFってどう指定するの？
### 許可したい送信元がどこなのかを把握
ドメインですか？IPですか？
例として今回はドメイン`watashinomono.com`として、SMTPサーバのドメイン`kyokashitai.domain.com`とIP`XX.XX.XX.XX`からのメール送信を許可（宣言）していきます。

### SPFレコードをDNSに記載
SPFはDNSにあるものです。
DNSのゾーンファイルにあるものです。
ドメイン名をレコード名とするTXTレコードとして公開されるものです。

ゾーンファイルの記述は以下の通り。

```terminal:
watashinomono.com. IN TXT "v=spf1 +include:kyokashitai.domain.com +ip4:XX.XX.XX.XX ~all"
```

+ v=spf1
    + SPFのバージョンを指定
+ +include:kyokashitai.domain.com
    + ドメインに対する指定は`include`に続ける
+ +ip4:XX.XX.XX.XX
    + IP4形式のIPに対する指定は`ip4`に続ける
+ ~all
    + 全てに対する指定

SPF記述は先頭（左側）にあればあるほど優先されるので「all」に対する宣言は末尾（右側）に書くようにしましょう。

#### クオリファイア
Q.ドメインやIPの前に「+」とか「~」が頭についてるけどなんですか？
A.クオリファイアアです。使用許可だけではなく使用禁止も宣言できます

| 記号  | 意味 |
|:-------:|:------|
| +|許可|
| ~|禁止|
| -|禁止（強い）|


### SPF落とし穴
SPFとして指定できるレコード数には限りがあるらしいので、TXTに記載したからと言って設定が通っているかは怪しいらしい…？
以下のようなサイト・ツールを使って設定した自DNSにSPF情報を取得しに行ってみましょう。

https://mxtoolbox.com/spf.aspx

+ DOMAIN:watashinomono.com
+ IP:XX.XX.XX.XX

そもそもTXTに文字数制限があるのでIPを書き連ねるはあんまりよくなさそうですね…送信元が自分が持ってるSMTPサーバなら、そいつもきちんとDNS設定して1ドメインにまとめてあげたりしてください。


## 受信メールサーバ側
わかりやすさを優先して、ざっくり図解をDNSがYesNo返すみたいな書き方にしてしまったけど、DNSはSFPレコードを持っているだけなのでした。
Yes,Noの判断は受信メールサーバ側で行います。

SPFレコードは受信メールサーバ側の挙動あってこそのものです。
あなたがメールサーバの管理者であればSPFチェックの仕組みと、不正であった場合の対応の決定・処理の設定を行いましょう。

1. メールを受信したら
2. 送信者のメールアドレスからドメイン（@以降）を取り出して
3. ドメインのSPFレコードをDNSに問い合わせて
4. 返ってきた結果と、メール送信元IPアドレスを照合し、
5. メールを破棄、迷惑メール扱いにする、等の処理を行うようにしておく

## オマケ
送信者偽装を防ぐ「送信側」のドメインが打てる手は、SPF以外に
`DomainKeys Identified Mail（DKIM）`、`Sender ID`もあります。


## 参考元
DNSのSPFレコードについてあらためて学習してみた
http://fanblogs.jp/nowlogging/archive/34/0

SPFレコードの設定・書式について理解する
https://monaski.hatenablog.com/entry/2016/02/27/150530


[^1]: 送信してから受信メールサーバへ届くまでに、SMTP使ったりとかしてるけど、今回の図では略
[^2]: 受信メールサーバ側で判断がつくだけなので、そのまま受信ユーザへメールが送信されることがあります。受信メールサーバ側で、SPF設定と異なる送信元である場合はユーザに送信しないとか迷惑メールの扱いにする等の挙動設定が必要。
PrettierをPHPでも使う方法

### PHP版のPrettierをインストール

```
$ npm install -g prettier @prettier/plugin-php
```

### VSCodeの保存時に自動的にPrettierを起動させる

VSCodeのsettings.jsonに下記を追記する
※settings.jsonを開くには画面左下の⚙️を押し、右上の{}を押す

```
  "editor.formatOnSave": true,
```

### Prettier整形させたくない時
- **prettier-ignore** のコメントつけるとその下の行は整形されない

```
// prettier-ignore
$hoge = [
1,2,3,
  4,5,6
];
```
linuxを簡単に使えるようにWindowsでおなじみの効果イコールをする為のコマンドのメモ

##個人的なメモ書き
linuxを簡単に使えるように極力1行でWindowsでおなじみの効果イコールをする為のコマンドのメモ

##ディスク関連のコマンド

````
df 
````

ラズベリーパイでの例

````
$ df
ファイルシス   1K-ブロック    使用   使用可 使用% マウント位置
/dev/root         30787388 4007108 25497180   14% /
devtmpfs            494912       0   494912    0% /dev
tmpfs               499520       0   499520    0% /dev/shm
tmpfs               499520   12888   486632    3% /run
tmpfs                 5120       4     5116    1% /run/lock
tmpfs               499520       0   499520    0% /sys/fs/cgroup
/dev/mmcblk0p1       41853   22685    19168   55% /boot
tmpfs                99904       0    99904    0% /run/user/1001
````

## ディスクをマウントする

````
mount -o uid=1001,gid=1002 /dev/sdb1 /mnt/ssddata
````

ユーザーIDやグループIDがわからない場合はこうする

ユーザーIDを見る例

````
whoami | id
````

グループID一覧を見る例

````
cat /etc/group
````




#ディスクキャッシュとディスクを同期してキャッシュを開放する
````
sudo sync && sysctl -w vm.drop_caches=3
````

#windowsアップデートと同じことをする

#セキュリティーアップデートのみ

````
sudo apt update && sudo apt upgrade
````
#ソフトウェアのアップデート

````
sudo apt update && sudo apt dist-upgrade
````

##ディスクをマウント等の関連

#ディスクの一覧をみる
````
sudo fdisk -l
````

#UUIDの確認

````
sudo blkid
````
注意：
mountで　/dev/sdbなどでマウントを行うと再起動したときに　/dev/sdaと/dev/sdbなどの順番が入れ替わった時に
マウントポイントがかわってしまうのでUUIDを利用してマウントするほうが間違えなくて良い。
この場合　/etc/fstab を書き換えて　mount -a とすると即座に反映される


#起動時にディスクをマウントする場合
注意：テストを行わないでマウントをするのはあまり推奨しない

````
　proc            /proc           proc    defaults          0       0
　PARTUUID=2374e4bc-01  /boot           vfat    defaults          0       2
　PARTUUID=2374e4bc-02  /               ext4    defaults,noatime  0       1

　# ssd hdd usb
　UUID=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX　mnt/ssd ext4 defaults 0 0

　# a swapfile is not a swap partition, no line here
　#   use  dphys-swapfile swap[on|off]  for that
````
uuid　UUID=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX　mnt/ssd ext4 defaults 0 0　を
/et/fstab に追加する

注意！　間違えるとＯＳが起動しなくなる

#Windowsのスタートアップにあたる仕組み

/etc/rc.local

````
#!/bin/sh -e
#
# rc.local
#
# This script is executed at the end of each multiuser runlevel.
# Make sure that the script will "exit 0" on success or any other
# value on error.
#
# In order to enable or disable this script just change the execution
# bits.
#
# By default this script does nothing.

# Print the IP address
_IP=$(hostname -I) || true
if [ "$_IP" ]; then
  printf "My IP address is %s\n" "$_IP"
fi

exit 0

````
exit 0より前に必要なスクリプトを組み込んだりコマンドを実行すると良いです。
しかし、サービスとして扱うものとは別です。
こちらで起動したものは安全な終了が保証されない場合がありますので要注意
[備忘録]vscode -> mac install by homebrew

# vscode -> mac install スル

# 今までの問題点
- [公式サイト](https://code.visualstudio.com/docs/?dv=osx)でzipダウンロードした場合、ファイル実行になり、applicationsに入らない🤔
- ダウンロードファイル消したらvscodeツカエナイ

# 解決
homebrewでインストールすればよかった
`brew cask install visual-studio-code`
Googleスプレッドシートにfaviconを表示する

秒で誰でも簡単にGoogleスプレッドシートにfaviconを表示する方法
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103957/83f82da6-bc46-ba16-136d-bf627f50cdc5.png)
# 数式
以下のような数式を表に入れるだけ。
B2セルにURLやドメインが入っている前提。
B2セルに入れる内容は`https://`の部分を省略してドメインだけの状態にしても動く場合がある。

```:
=IMAGE("https://www.google.com/s2/favicons?domain="&B2)
```

# 関連記事
- [SEOに使えるGoogleスプレッドシート関数 - Qiita](https://qiita.com/aqril_1132/items/3037d77b5f669e8d1228)
Scriptチートシート（随時更新予定）

# sedで文字列置換

```shell

echo abcdefabcdef | sed -e 's/^abc/Z/g'
#結果Zdefabcdef
```

#xargsを使い、コマンドの結果を用いて処理をする

```shell

ls -1 | xargs -t -I{} sh -c  "echo {}"
# オプションの説明
# ls
# -1：ファイル名のみ表示
# xargs
# -t:実行するコマンド表示
# -I:{}を置換する
# sh
# -c 文字列をコマンドとして実行する
```

#バックグラウンド処理
長時間かかる処理などで使用する。
バックグラウンド実行には最後に&を入れる。

```shell

sleep 100 &
jobs
#結果
#[1]+  Running                 sleep 100 &
#バックグラウンド処理を削除するときは下記で行う
#kill %(job番号)
```

#findでpermissionerrorを表示させない

```shell

find / -name java  2>/dev/null
```

#sortしてuniqのものだけ抽出

```shell

sort -u obj
```

#一部のファイルサイズ取得

```shell

ls -la 対象 | awk '{ total += $5 }; END { print total }'
```

#メモリなどの状況確認

```

dstat -tamf
```

#シンボリックリンクの作成と削除

```shell

#シンボリックリンクの作成
ln -s taisyo link_name

#シンボリックリンク削除
unlink link_name
#※rmだと元ファイルも削除されるため注意

```

#圧縮したファイルの比較

```shell

zdiff file1 file2
```

# Loop(for)

```
  for detail in `seq 1 10`
  do
    echo "yes"
  done
```

# 日付でループ
```shell

S_DATE="20180101"
E_DATE="20180201"

for (( DATE=${START_DATE} ; ${DATE} < ${END_DATE} ; DATE=`date -d "${DATE} 1 day" '+%Y%m%d'` )) ; do
  echo ${DATE}
done
```

参考資料
https://eng-entrance.com/linux-job#i-3
https://qiita.com/hawaku/items/2f0a8454bbead2f070d2
【React】最小限のreact-router実装で仕組みを理解する

#概要
react-routerの使い方を理解するために最小限の実装をする

#成果物
![out.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390000/1043e09f-93ff-6548-7cbe-c6962db2d48d.gif)

#手順
1. Reactの新規プロジェクト作成
2. react-routerをインストール
3. srcディレクトリの中身を削除
4. index.jsとApp.jsを作成

##1. Reactの新規プロジェクト作成
```shell:
$ npx create-react-app hogehoge
```

##2. react-routerをインストール
```shell:
$ cd hogehoge
$ npm install react-router
```

##3. srcディレクトリの中身を削除
```shell:
$ rm -rf ./src/*
```

##4. index.jsとApp.jsを作成
```jsx:index.js
import React from 'react';
import ReactDOM from 'react-dom';
import AppRouter from './App.js';

ReactDOM.render(
	<AppRouter />,
	document.getElementById('root')
);
```

```jsx:App.js
import React from 'react';
import { BrowserRouter as Router, Route, Link } from 'react-router-dom';

function Index(){
	return <h2>Home page is here!</h2>
}

function About(){
	return <h2>This is About page.</h2>
}

function Users(){
	return <h2>This is User page.</h2>
}

function AppRouter(){
	return(
		<Router>
			<div>
				<nav>
					<ul>
						<li>
							<Link to='/'>Home</Link>
						</li>
						<li>
							<Link to='/about/'>About</Link>
						</li>
						<li>
							<Link to='/users/'>Users</Link>
						</li>
					</ul>
				</nav>
				<Route path='/' exact component={Index} />
				<Route path='/about/' component={About} />
				<Route path='/users/' component={Users} />
			</div>
		</Router>
	);
}

export default AppRouter;
```

#Happy Hacking :sunglasses: !

#参考
https://reacttraining.com/react-router/web/guides/quick-start
Pug初学者の備忘録：ループ編

Pug初学者の備忘録です。どんどん追記していきます。

forループ+クラス名を変数展開
---
```html:完成イメージ
<ul class="items">
  <li class="item i1"></li>
  <li class="item i2"></li>
  <li class="item i3"></li>
</ul>
```
↑のようにループで`li.item`を生成しつつ、それぞれに`i1`、`i2`、`i3`…と番号付のクラス名を振り当てたいときに。

```javascript:index.pug
ul.items
  - for (var i = 1; i < 4; i++)
    li(class="item i" + i)
```
もっとスマートな書き方がありそうな気がする… :sweat: 

each+連想配列
---
```html:完成イメージ
<ul>
  <li><a href="./">Top</a></li>
  <li><a href="./about">About</a></li>
  <li><a href="./works">Works</a></li>
  <li><a href="./contact">Contact</a></li>
</ul>
```
よくあるメニューリストを作るときに。

```javascript:index.pug
ul
  -
    var menus = [
      { text: "TOP", url: "./" },
      { text: "About", url: "./about" },
      { text: "Works", url: "./works" },
      { text: "Contact", url: "./contact" }
    ]

  each menu in menus
    li: a(href=menu.url)=menu.text

```

使いこなせたら楽しそう！引続き勉強していきます。
Angular CLI プロジェクトで npm link を使うための設定

`angular.json` に `"preserveSymlinks": true` を追加する。

```json:angular.json
{
  "projects": {
    ...
    "my-app": {
      "architect": {
        ...
        "build": {
            "builder": "@angular-devkit/build-angular:browser",
            "options": {
              "index": "src/index.html",
              "main": "src/main.ts",
              "polyfills": "src/polyfills.ts",
              "tsConfig": "src/tsconfig.app.json",
              ...
              "preserveSymlinks": true
            }
        },
        ...
      }
      ...
    }
    ...
  }
}
```
以上。

## 参考
[Problem with ng serve --preserve-symlinks Angular/CLI 6.0.0 #10735](https://github.com/angular/angular-cli/issues/10735#issuecomment-387636803)
Goの基本的な型変換

## 数値 (int) → 文字列 (string)
```go
var i int = 123
var toString string

toString = strconv.Itoa(i) // "123"
toString = string(i)       // 123がrune(Unicode)として認識され "{" になるので注意!!
```
Unicode 123(10進数)は `{`
参考 : https://www.codetable.net/decimal/123

## 文字列 (String) → 数値 (Int)
```go
var s string = "123"
var s2 string = "Doraemon"
var toInt int

toInt, _ = strconv.Atoi(s)
fmt.Println(toInt) // 123

var err error
toInt, err = strconv.Atoi(s2) // panicにはならないが、toIntにはゼロ値、errにはエラーメッセージを返す。
fmt.Println(toInt) // 0
fmt.Println(err)   // strconv.Atoi: parsing "Doraemon": invalid syntax
```

## 文字列 (string) → 論理型 (bool) 
```go
var s string = "true"
var toBool bool
toBool, _ = strconv.ParseBool(s)
fmt.Println(toBool) // true
```

- `ParseBool`で変換できる文字列
    - true : "1", "t", "T", "true", "TRUE", "True"
    - false : "0", "f", "F", "false", "FALSE", "False"

## 論理型 (bool) → 文字列 (string)
```go
var b bool = false
var toString string
toString = strconv.FormatBool(b)
fmt.Println(toString) // "false"
```

## Interface{}の変換
interface{}にはどんな型でも入れるのが可能。
ただし、入れた値そのままでは元の型としての処理はできないので、`.(元の型)`で明示してからは可能になる。

```go
type Robot struct {
	name   string
	birth  int
}

func main() {
	dora := Robot{
		name:  "Doraemon",
		birth: 2112,
	}

	Anything := map[string]interface{}{
		"valString": "文字列",
		"valInt":    1234,
		"valBool":   true,
		"valStruct": dora, // Robot (struct)
	}
}

	var stringValue string
	var intValue int
	var boolValue bool
	var structValue Robot

	// エラーのパターン
	Anything["valInt"]++
	// invalid operation: Anything["valInt"]++ (non-numeric type interface {})
	stringValue = Anything["valString"]
	// cannot use Anything["valString"] (type interface {}) as type string in assignment: need type assertion
	name := Anything["valStruct"].name
	// Anything["valStruct"].name undefined (type interface {} is interface with no methods

	// 正常のパターン
	Anything["valInt"] = Anything["valInt"].(int) + 1
	stringValue = Anything["valString"].(string)
	name := Anything["valStruct"].(Robot).name // "Doraemon"

```
https://play.golang.org/p/gMAvWMA7oNb

interface{}から型を明示して取り出すときは元の型ではないとエラーになる。(暗黙的な変換はできない)

```go
	// panic
	name := Anything["valInt"].(string) 
	// panic: interface conversion: interface {} is int, not string

	// 正常 (元の型を明示して出したあとに型変換する。)
	name := strconv.Itoa(Anything["valInt"].(int)) // "1234"
```

### map[string]interface{}の for range
mapの値を`interface{}`にしたら、どの型とでもいれられるが、rangeで回すときは内部で適切な処理のためには値の本当の型を判別し、変換が必要になる。
Type switchを使うと便利。(参考 : [A Tour of Go - Type switches](https://tour.golang.org/methods/16))

```go
var intValue int
...
	for i, v := range Anything {
		switch v := v.(type) { // 元の型キャストされる (switchのスコープ内のみ)
		case int:
			intValue = v 
			fmt.Printf("%s:%d (%T)\n", i, v, v) // valInt:1234 (int)
		case string:
			fmt.Printf("%s:%s (%T)\n", i, v, v) // valString:文字列 (string)
		case bool:
			fmt.Printf("%s:%t (%T)\n", i, v, v) // valBool:true (bool)
		case Robot:
			fmt.Printf("%s: %+v (%T)\n", i, v, v) // valStruct: {name:Doraemon birth:2112} (main.Robot)
		...
		}
	}
```
https://play.golang.org/p/1bmT13JTA3O
ROS2における名前空間・ノード名の使い方

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

メモ程度

topicやservice，パラメータにアクセスする時．
基本的に同じ名前で起動はできない．そこでノード名や名前空間をつけて区別させる．
この時，それぞれにアクセスするために，以下のように指定する？

/[名前空間の名前]/[ノード名]/[サービスの名前]

名前空間がなかったり区別する必要がなかったら以下のように指定する？

/[ノード名]/[サービスの名前]

自動的に同じ名前空間のサービスに接続してくれたらありがたいんだけど．

参考
- [How to use namespace](https://discourse.ros.org/t/how-to-use-namespaces/2236/2)
【ffmpeg】QiitaにGif動画を投稿する時にコマンドラインでGifファイルに変換する方法

#概要
Qiitaに動画を投稿する時にコマンドラインでGifファイルに変換する
ffmpegという万能動画変換ツールを使います

#手順
1. ffmpegをインストール
2. 動画を変換する

#ffmpegインストール
```shell:
$ brew install ffmpeg
```
#動画をGifに変換
```shell:
$ ffmpeg -i input.mov -r 10 -f gif out.gif
```
- -i
変換したい入力ファイルパス

- -r
フレームレート（fps）

- -f
変換するフォーマット

#Happy Hacking :sunglasses: !

#参考
https://ffmpeg.org/
【個人備忘】イラレでwebデザインカンプを作るとき、各段階でやること&やらなくていいこと

自分とコーダーさん(orコーディングをする自分)双方の手間が最小限になる段取りを模索中です。
あと忘れがちな設定作業とかの備忘リスト。随時追記します

#デザイン前
###◆やる
**・カラーモードをRGBに**
**・単位関係をすべてpixelに**
**・表示を「ピクセルプレビュー」モードに**
**・オブジェクトの線設定を「内側」に**
　　∟「新規オブジェクトに基本アピアランスを適用」設定をオフ
↑↑↑※毎度やるのは面倒だしヒューマンエラーが怖いので、設定をまるごとテンプレートに保存してしまうとラク！（方法は後日記載）

**・デフォルトのフォントをfont-familyで使う予定のものに変えておく**
　　∟大体はメイリオ or MSゴシック or 游 

#デザイン中
###◆やる（気を付ける）
**・線(border)単体を表現するときは「(borderの太さ)pxの塗」で表現する**
　　∟奇数幅pxの線を引きたい時、デフォルトだと線の中心がピクセルに整合するのでにじんでしまう
**・素材画像を使う時は「ccライブラリ」で管理する**
　　∟他ファイルでバリエーション作る時もラク
　　∟Adobe Stockの画像とかワンクリックで購入→置き換えまでできちゃうので最高
　　∟画像だけじゃなくて書式や色コードも管理できてほんとに最高
**・要素はこまめに「ピクセルグリッドに整合」**
　　∟オブジェクトの位置やサイズを微調整しているとき、何かの拍子に小数点ができてしまうことがわりとよくある
　　 にじみで印象変わるとわかりづらいので、「なんかぼやっとしてるな」と思ったらこまめに整合アイコンを1クリック

###◆やらなくていい
**・レイヤー整理**
　　∟デザインを試行錯誤してる時にいちいちレイヤー構造まで見なくちゃいけないのは手間
**・マージンや文字サイズ等の数字を設計**
　　∟大まか(印象が変わらない程度。誤差1~5px以内)に合わせるなどの管理は都度必要だが、ピッタリ合わせる必要はない。なぜならデザインの試行錯誤中に結局何度でもずれるから
　　∟数字で管理する癖をつけるのもいいが、最初から数字ありきでやっているとデザインが固くなる気がする。バウンドボックス使って感覚的にいじって、デザインが決まってから数字で整えていく方が柔軟なデザインになる…私は

#デザイン提出時
###◆やる
**・数字まわりの設計**
　　∟マージン系は「整列と分布」パネルで細かな誤差を修正し、端数を切上げor下げして管理しやすい近似値に
　　∟文字サイズは16pxを基準に「em」で管理しやすい近似値に

###◆やらなくていい
**・レイヤー整理**
　　∟直しが入る可能性があるから、作業スペースは片付けないでおく
　　∟使わなかったデザインパーツとかもまだとっとく

#デザイン後(Fix時)
###◆やる
**・非表示オブジェクトを削除**
　　∟オブジェクト→すべてを表示　で非表示だったものだけ選択された状態になる
**・飾り文字をアウトライン化(ウェブフォント使用予定のもの除く)**
**・SVG吐き出し予定オブジェクトの軽量化処理**
　　∟パスのアウトライン化、合体、アピアランスの分割
　　∟吐き出した後のコード編集も忘れずに

###◆やる（コーダーさんに引き継ぐ場合）
**・レイヤー整理**
**・画像やリンクファイルの埋め込み(or納品ファイル用にリンク再設定)**
google app scriptでconnpassの新着イベントをメール

## これ何？
connpassのページをいちいち開いて検索するのが面倒だったのでconnpassのapiとgasを利用して新着イベントを定期的にメール送信するスクリプトを書きました。

## 使用するapiのパラメータ
|パラメータ名  |説明  |
|---|---|
|count  |取ってくるイベントの個数  |
|keyword_or  |イベントのタイトル、キャッチ、概要、住所をOR条件部分一致で検索  |
|order  |検索結果の表示順(3が新着順)|

詳しくは[API リファレンス](https://connpass.com/about/api/)を参照


## コード

```javascript
function myFunction() {
  //キーワードから開催している都道府県を限定
  var keyword_or = 'keyword_or=東京&keyword_or=埼玉'
  
  //新着順
  var order = 'order=3'
  
  //上位25件を取得
  var count = 'count=25'
  
  var url = 'https://connpass.com/api/v1/event/?' + keyword_or + '&' + order + '&' + count
  
  var json = UrlFetchApp.fetch(url).getContentText("UTF-8");
  
  var j = JSON.parse(json);
  
  var events = j.events
  
  var title = 'Connpass新着イベント';
  
  var text = '★新着イベント★\n\n----------------------------------------------------------------------------\n';
  
  for(i in events){
    text = text + 'title : ' + events[i]['title'] + '\n';
    text = text + 'catch : ' + events[i]['catch'] + '\n';
    text = text + '開催日時 : ' + events[i]['started_at'] + ' ～ ' + events[i]['ended_at'] + '\n';
    text = text + '開催場所 : ' + events[i]['address'] + events[i]['place'] + '\n';
    text = text + events[i]['event_url'] + '\n\n';
  }
  
  text = text + '----------------------------------------------------------------------------\n';
  
  MailApp.sendEmail('example-mail@gmail.com', title, text);
}

```

## 定期実行にする
1. スクリプト編集の画面から`編集>現在のプロジェクトのトリガー`を選択

2. `トリガーを追加`を押下して以下のような感じで定期実行にする
<img width="726" alt="スクリーンショット 2019-07-04 11 30 03" src="https://user-images.githubusercontent.com/22609383/60635846-b6c58800-9e03-11e9-87c7-c4ffdacaf84f.png">
macOS Mojave で warmd プロセスがCPUを消費する現象について

・どうやら、よく使うアプリやファイルをキャッシュしておく機能らしい。
・ウォームアップの `warm` であって、ワーム `worm` ではないので心配しなくて良い。
・SSDだと効果的だけど、HDDでは悪影響？

## warmd を停止する方法
```
Kill warmd:
launchctl kill -9 system/com.apple.warmd

Disable warmd:
launchctl disable system/com.apple.warmd
```

### 参考
https://apple.stackexchange.com/questions/355039/process-warmd-keep-busy-mac-after-upgrading-to-macos-10-14-4
https://vilimpoc.org/blog/2014/01/15/provisioning-os-x-and-disabling-unnecessary-services/
http://www.macinside.info/process.php?id=2476
Windows 10 May 2019 Update (build 1903)を実行したら Vagrant環境が立ち上がらない


Windows 10 May 2019 Update (build 1903)を実行したら
Vagrantが立ち上がらなくなってしまった。

#超概要
Windows10の大型アップデート　May 2019 Update (build 1903)を実行するとHyper-Vが有効になってました。Hyper-Vを無効にすると元通り、治ります。


#詳細
Vagrantが出すエラーは下記の通り

```
==> default: Booting VM...
There was an error while executing `VBoxManage`, a CLI used by Vagrant
for controlling VirtualBox. The command and stderr is shown below.

Command: ["startvm", "6b4d817d-53dc-43df-a5fe-6e2b25a710b5", "--type", "headless"]

Stderr: VBoxManage.exe: error: Call to WHvSetupPartition failed: ERROR_SUCCESS (Last=0xc000000d/87) (VERR_NEM_VM_CREATE_FAILED)
VBoxManage.exe: error: Details: code E_FAIL (0x80004005), component ConsoleWrap, interface IConsole
```

May 2019 Update では、Windowsのサンドボックスが追加されたということもあるし、Hyper-Vの方が有効になってしまって、VT-Xモードが使えなくなってしまった予感があります。
https://tech.nikkeibp.co.jp/atcl/nxt/column/18/00723/042400005/

### VT-xを有効にするには
Windowsキーを押して、cmdと打ち込んで、出てきたコマンドプロンプトアイコンを右クリックして「管理者として実行」を押します。
![cmd-admin.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/102740/5feeb3f1-81c4-2c4c-b1ab-bdc6804441b2.png)

```
bcdedit /set hypervisorlaunchtype off
```

と打って`この操作を正しく終了しました。`　と出たところで再起動します。
以上で、VT-xモードが有効になり、Vagrantが起動できるようになります。
今日の学習０７０３

##今日やったこと
①(https://railstutorial.jp/chapters/beginning?version=5.1#cha-beginning) を参考にわからない単語のピックアップ。
→Ruby gem
bundle install
bundle updateの実行方法
MVCやREST
ジェネレータ、マイグレーション
ERB
scaffold
MITライセンス
DSL (ドメイン固有言語)
API
gem (ページネーションや画像アップロードといった特定の問題を解決するためのgemなど)、
クラウド統合開発環境 (クラウドIDE)
AWS Cloud9
Bashなどの標準的なシェルコマンドラインインターフェイス
②授業の動画の復習
③実際に演習
Ⅰ、Ruby on Railsで使うRuby gemはどのWebサイトにありますか？
→ （RubyGems.org）として置かれている
Ⅱ、現時点でのRailsの最新バージョンはいくつですか？
→https://rubyonrails.org/ で確認。現在は5.2.3
Ⅲ、Ruby on Railsはこれまでに何回ダウンロードされたか？
→ RubyGems.orgで確認。現在は37,303,000,945。
##学んだこと
①開発環境を大別すると、テキストエディタやコマンドラインを使う環境と、IDE (統合開発環境) の2つに分けられる。
②ローカルサーバーはコントロール＋Cで閉じることが可能。
③Railsのアプリを表示するには、http://localhost:3000/
React Native製Androidアプリでコマンドラインビルドすると画像が表示されないことがある

`--assets-dest android/app/src/main/res` を `react-native bundle` のオプションに付与する。

すると画像リソースとしてresディレクトリ以下に配置されるようになる。
でもこれを付与しなくても動くときもあるのが謎。。
Gitでstashする方法

- 環境
    - CentOS Linux release 7.6.1810 (Core)
    - git version 1.8.3.1
    - xfc4-terminal 0.8.7.4

# ユーザを設定していない場合は設定する
[ユーザを設定しないままstashすると怒られるので設定していない場合は設定する。](https://qiita.com/ponsuke0531/items/f7c5b8dd9a9ae6ad0a25)

# 名前を付けて`stash`する

```bash
# お名前をつけてstashする
$ git stash save stash-name
Saved working directory and index state On branch-c: stash-name
HEAD is now at bf54b9a

# stashできたか確認する
$ git stash list
stash@{0}: On branch-c: stash-name

# 一応ローカルの変更がstashされてなくなったか確認する
$ git diff HEAD
$
```

# `stash`を適用する
[Gitでstashを適用する方法 - Qiita](https://qiita.com/ponsuke0531/items/2229495ba7e7c3dc0e01)

# 参考
- [変更を一時的に退避！キメろgit stash - Qiita](https://qiita.com/fukajun/items/41288806e4733cb9c342)
- [名前をつけてgit stashする - モヒカンは正義](https://blog.pinkumohikan.com/entry/git-stash-with-name)
Vue.js・Nuxt.js ハマりポイントまとめ

Vue.js や Nuxt.js で開発していてハマったポイントを雑多にまとめています。（随時追加予定）

# props を子コンポーネントにそのまま流したい

具体的な例をあげると、UI コンポーネントをラップしたコンポーネントを作りたい場合などです。

こういうときは、`$attrs` を使うとよいです。  
https://isoppp.com/note/2018-12-16/what-is-vue-attrs/

# computed は型アノテーションが必須

明らかに推論できるような場合でも、型アノテーションが必須です。  
記述していない場合はエディタがエラーを表示します。

```ts
computed: {
  fullName(): string { // 明らかに string を返すが、アノテーションは必須
    return `${firstName} ${lastName}`;
  }
}
```

正しく Vue コンポーネントを記述しているのに何かエラーになる場合、これが原因のことが多いです。

# リロードした時だけ動かない

具体的な例をあげると、「リロードした時だけ動かない。他のページから遷移してきた場合は動く。」のようなケースです。

これは、SSR と CSR に起因しているケースが多いです。  
サーバーサイド（クライアントサイド）でしか実行してはいけない関数を実行してしまっていないか確認しましょう。例えば、サーバーサイドで window オブジェクトにアクセスしているとかです。

# テーブル周りでエラーになる

`thead` や `tbody` を書けば直ることがあります。  
Vue では、自動的にこれらを補完するため、SSR と CSR で DOM が一致せず、エラーになります。
【Swift】初めてのXcode


この記事では、Webページを表示するアプリを作ります。

# プロジェクトを立ち上げる。

1. Xcodeを起動し、起動画面から"Create a new Xcode project"を選択。
2. 次に、"Single View Application"を選択。
<img width="808" alt="SingleView.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/452171/db5d34bd-0f3e-4462-4219-dd2efe841607.png">
3. 次の画面ではプロジェクト名を入力。"Organization Identifier"では、ドメイン名を逆に記入するが、練習なので、com.exampleとする。
　<img width="780" alt="Optimize.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/452171/2ae59af3-53ad-09e0-ed78-08065cd9f334.png">
4. 保存する場所を選択する。

# 実行する

1. プロジェクトが立ち当たったら、どの種類のデバイスでシュミレータを実行するかを選択します。デフォルトではiPhone XRになっているはず。IPad AirやiPhone 8などいろいろ選択することができます。
2. 機種を選択したら、実行してみます。左上にある三角のボタン(▶︎)を押すことでコードを実行することができます。Cmd+Rでも同じようにできます。
3. 白い画面が現れたら成功。
 <img width="622" alt="run.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/452171/6e4d41c0-3375-5848-adca-f885218d4fc0.png">
4. Cmd+.で実行をストップできる。

# Webサイトを表示してみる。
今回は、Webページを画面に表示させてみます。
### WebKitの導入
`ViewController.swift`を開き、`import UIKit`の下に`import WebKit`を追加する。

```Swift:ViewController.swift
import WebKit
```
### 画面を作る
WKWebViewを生成。

```Swift:ViewController.swift
var webView: WKWebView!
```
### `viewDidLoad()`の前に以下のメソッドを追加
注：この段階ではまだ動かない。

```Swift:ViewController.swift
override func loadView() {
    webView = WKWebView()
    webView.navigationDelegate = self
    view = webView
}
```
* 厳密には`loadView()`を`viewDidLoad()`の前に置く必要はない。`ViewController`クラスのどこに`loadView()`を置いてもいいが、`loadView()`が`viewDidLoad()`の前に呼ばれるため、実行される順番がわかりやすいように上のような記述にしている。
* `WKWebView`と呼ばれる部品のインスタントを作り、これを`webView`プロパティとひもづける。

### デリゲートを実装する。
上記のエラーを解消する。

以下のコードを

```Swift:ViewController.swift
class ViewController: UIViewController {
```
以下のように変更する。

```Swift:ViewController.swift
class ViewController: UIViewController, WKNavigationDelegate {
```

* `class A: B`とすることで、AクラスはBの機能を実装できる。
* `class A: B, C`となっている場合、AはBを継承していることを示し、Cがデリゲートされていることになる。
* 上記のコードでは、`ViewController`が`UIViewController`の子クラスであり、`WKNavigationDelegate`をデリゲートすることになる。

# 表示するURLの設定

`viewDidLoad()`の`super.viewDidLoad()`の後に以下のコードを書く。

```Swift:ViewController.swift
let url = URL(string: "https://www.apple.com/")!  //任意のURL
webView.load(URLRequest(url: url))
webView.allowsBackForwardNavigationGestures = true
```

* string型の`URL`型に保存。
* URLから`URLRequest`オブジェクトを生成する。
* urlの冒頭は`https://`とすること。

# 実行
Cmd+Rで実行すると、シュミレータにWebページが表示される。
<img width="804" alt="shot.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/452171/8e10454c-ad4b-fc69-e329-2f494609232a.png">

### 終わりに
まずはWebページを表示するシンプルなアプリを作ってみました。
これから色々なアプリを作って練習していこうと思います。
fields_forについて

### はじめに
fields_forとはform内で異なるモデルを編集することができるようになるもの。
今回はitemテーブルとimageテーブルが１対多の関係の時にimageテーブルに画像を保存できるようにするために使用した。

###使い方

```new.html.haml

.item-contents__image
              %label
                .item-contents__image__box
                  .input-area
                  .item-contents__image__box__text
                    ドラッグアンドドロップ
                    またはクリックしてファイルをアップロード
                  #file-preview
                  = f.fields_for :images do |i|
                    =i.file_field :url, class: 'item-contents__image__drop-file', type: "file"
```

```item.controller.rb

def new
    @item = Item.new
    @item.images.build
end

def create
    Item.create(item_params)
    redirect_to root_path, notice: '商品を出品しました'
end


private
  def item_params
    params.require(:item).permit(:name, :description, :price, images_attributes: [:url, :item_id])
  end
```

``` item.rb
  has_many :images
  accepts_nested_attributes_for :images
```

```image.rb
  belongs_to :item
  mount_uploader :url, ImageUploader
```
これらの記述をすることでimageテーブルに画像を保存することが可能になります。
firebase-toolsの使い方

## firebase-toolsとは？
Firebaseプロジェクトの管理、表示、デプロイ等ができます。

## インストール

```
npm install -g firebase-tools
```

私は`npx`派なのでここからはnpxで動かしていきます。

## ログイン

```
npx firebase login
```
ブラウザが起動しポップアップが表示されるので使用したいgooogleアカウントでログインしてください。

## プロジェクト設定

```
npx firebase init
```


<img width="1300" alt="スクリーンショット 2019-07-04 10.16.53.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/115687/4ba2010a-1e4c-73b7-6260-dfe58e85cbf7.png">


上記のように複数の項目が選択できるので設定したい項目を選択しましょう。

## デプロイ

```
npx firebase deploy
```

これでfirebaseで使用している機能すべてをデプロイできます。
機能ごとにデプロイしたい場合は

```
npx firebase deploy --only hosting
```

このように`--only`をつけることによって機能ごとにデプロイ出来ます。


## firebaseの管理画面を開く
```
npx firebase open
```
<img width="511" alt="スクリーンショット 2019-07-04 10.20.56.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/115687/8e3d0ce7-014a-02f2-7798-4057ae153c3f.png">

今回も上記のように複数の項目が選択できるので開きたい項目を選択しましょう。

## 既存のプロジェクトにfirebaseを追加する
まずfirebase管理画面でプロジェクトを追加します。
次に追加したいディレクトリ内で

```
npx firebase use
```
上記のコマンドを打つことでアカウントに紐づいている
プロジェクトを選択する事ができるので追加してください。

## ローカルで開発する
```
npx firebase serve
```

上記のコマンドでローカルサーバーが起動します。


## 公式リファレンス・githubリポジトリ

https://firebase.google.com/docs/cli/?hl=ja
https://github.com/firebase/firebase-tools
namespace / 指定ディレクトリ以下にコントローラーを作成


## 概要

コントローラーやビューを生成する時、
**それぞれ同じ名前になってしまうコントローラー名やビューの名前がある。**
例えば、`user` `photographer` `admin` ぜんぶContacts.controller（お問い合わせ用）を持っています。

そこでディレクトリを指定してコントローラーを生成してあげたら、
**admins**ディレクトリ下にあるものは**admins/contacts**というURLとして呼び出してくれると思ったわけですが・・

ここでルーティング内の重複はどうなるんだ〜となりました。
そこでnamespace（名前空間）という便利なものを知ったのでメモします:sunny:

## Namespace（名前空間）とは

名前の重複があるときに、衝突を防ぐために使用します。

##前提

今回はgem deviseでログイン機能を実装したため、
すでに`$rails g devise:controllers admins`コマンドで`admins`ディレクトリがあります。
そこにdevise関連ではないコントローラーも追加していきたい！という感じ。

##とりあえずコントローラーを生成してみよう

```
rails generate controller admins/Contacts edit update
```

これだけ:relieved::v_tone4:

##ルーティングは?

上のコマンドでコントローラーを生成した時点で、
`namespace :admins do --- end`という枠組みが作成されて、
namespace枠内に、GETやPOSTなど自分が追加したアクションがきちんとルーティングされています:clap_tone3: 今回はResourcesに書き換えました。

```config/routes.rb

Rails.application.routes.draw do

  namespace :admins do
    resources :contacts, only:[:edit, :update]
  end
  # アドミンのcontacts
  
  resources :contacts, only:[:new, :create]
  # ユーザーのcontacts

  devise_for :photographers
  devise_for :admins
  devise_for :users

end
```

これで名前が一緒でも、
「このcontactsはadminね。」　「このcontactsはuserね。」という感じで
判別してくれるわけです :relaxed: :fist_tone3:
Python　ディクショナリについて

##ディクショナリとは
基本的にはリストと同じような構成です．リストと違う点は，キー(key)とバリュー(value)をセットで1つの要素として持つのが特徴です．

タプルは()，リストは[]で作成しますが，ディクショナリは{}を使用します．1つの要素をkey:valueのように配置し，要素の間は','で区切ります．ディクショナリに対して繰り返し処理を行うとkeyを取得できるので，これを利用してvalueを取り出す事もできます．

##ソースコード
```dict.py
test_dict_1 = {'YEAR':'2015', 'MONTH':'4', 'DAY':'6'}
print(test_dict_1)
print('========================')

for i in test_dict_1:
    print(i)
    print(test_dict_1[i])
    print('------------------')

print(test_dict_1['YEAR'])

print('----------------------------------')
#keyの取得→valueの取得
print(test_dict_1.get('YEAR'))
print(test_dict_1.get('YEARS'))

print('----------------------------------')
#valueの取得
print(test_dict_1.get('YEAR', 'NOT FOUND'))
print(test_dict_1.get('YEARS', 'NOT FOUND'))
```

```
{'YEAR': '2015', 'MONTH': '4', 'DAY': '6'}
========================
YEAR
2015
------------------
MONTH
4
------------------
DAY
6
------------------
2015
----------------------------------
2015
None
----------------------------------
2015
NOT FOUND
```

##参考にさせていただいたもの
ディクショナリ：Python-izm
https://www.python-izm.com/basic/dict/
Pythonで表の文字列長を揃えて表示(CLI)

自分用にメモ。

Python 2.7用

表(２次元のリスト)のデータをPythonで整形する

マルチバイト文字は考慮しないで、lenとmaxを使って各列の最大長を取得して揃える

```python:print_table.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
table = [
  ["column1", "column2", "column3"],
  ["abc", "defghijklm", "nopqrstuvwxyz"],
  ["abcdefghi","jklmn", "opqrstuvwxyz"],
]

align_table = [
  ("column1", "r"),
  ("column2", "l"),
  ("column3", "r"),
]

"""
  文字列長が合わないときは空白で埋める
"""
def adjust_length(input_str, adj_length, align='r'):
    return '{:{}{}s}'.format(input_str, {'r': '>', 'l': '<'}[align], adj_length)

max_length = [ max([ len(y) for y in x ]) for x in zip(*table) ]

print("\n".join([
  " ".join([
    adjust_length(x,mlen,align) 
    for x,mlen,align in zip(row,max_length,zip(*align_table)[1])
  ])
  for row in table
]))
  
```


実行結果

```
$ python print_table.py
  column1 column2          column3
      abc defghijklm nopqrstuvwxyz
abcdefghi jklmn       opqrstuvwxyz
$
```
Python　リスト(list)について

##リスト(list)とは
タプルと違い，作成後に要素の追加や削除が出来ます．いつでも要素の変更が出来るため，非常に汎用性が高いです．

##ソースコード
```list.py
test_list_1 = ['python', 'abc', 'efg', 'python', '1000', 'abc', 'abc']
print(test_list_1)

print('-----------------------------')

for i in test_list_1:
    print(i)

test_list_2 = []
print(test_list_2)

print('-------------------------------')
#要素の追加
test_list_2.append('python')
test_list_2.append('web')
test_list_2.append('aaaaa')
test_list_2.append('zzzzz')
print(test_list_2)

print('-------------------------------')
#インデックスを指定して追加
test_list_1.insert(1,',')
test_list_1.insert(3, '-')
print(test_list_1)

print('----------------------------------')
#要素の削除_1
test_list_1.remove('abc')
print(test_list_1)

print('----------------------------------')
#要素の削除_2
print(test_list_2.pop(3))
print(test_list_2)

print(test_list_2.pop())
print(test_list_2)

print('----------------------------------')
#要素のインデックスを取得
print(test_list_1.index('efg'))

print('----------------------------------')
#リスト内での要素数の取得
print(test_list_1.count('python'))
```
```
['python', 'abc', 'efg', 'python', '1000', 'abc', 'abc']
-----------------------------
python
abc
efg
python
1000
abc
abc
[]
-------------------------------
['python', 'web', 'aaaaa', 'zzzzz']
-------------------------------
['python', ',', 'abc', '-', 'efg', 'python', '1000', 'abc', 'abc']
----------------------------------
['python', ',', '-', 'efg', 'python', '1000', 'abc', 'abc']
----------------------------------
zzzzz
['python', 'web', 'aaaaa']
aaaaa
['python', 'web']
----------------------------------
3
----------------------------------
2
```

##参考にさせていただいたもの
リスト：Python-izm
https://www.python-izm.com/basic/list/
JavaScriptからTypeScriptへ移行した際に Property  does not exist on type '{}'. で怒られた話


どうも、最近TypeScriptを書いてます(仮) entakuです。

今回はJavaScriptからTypeScriptへ何も考えずに移行した際に 怒られた話です。

# 1.発生事象
何も考えずに移行した後、いつものように `npm run dev` すると以下のように怒られました。

```
 ERROR  ERROR in /Users/endo/{プロジェクト名}/{フォルダ名}/{ファイル名}.ts                                                                      09:38:19
89:32 Property {パラメータ名} does not exist on type '{}'.
```

ページは見えるのですが気持ち悪いですね。
これでも動くは動くんですよ。

# 2.原因調査

恒例のstackoverflow先輩!!!
https://stackoverflow.com/questions/34274487/property-does-not-exists-on-type

```typescript
let bar = <any>{};
bar.foo = "foobar"; 
```

なるほど型を指定しないとダメなようです。
{パラメータ名}はクラス定義したものから参照していたのですが、そのためエラーが出ていました。
今回はanyにしないとダメですね。

# 3.対応

stackoverflow先輩の言うとおりにします。

```typescript
let {パラメータ名} = <any>{};
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55864/ba7e4f55-c68d-82fe-dbca-f2a263a89859.png)

🎉🎉🎉🎉🎉🎉

# 4.まとめ

早く型安全な世界にしたい