OmniAICreator/Qiita-1.07M huggingface dataset:
toggleボタンと確認ダイアログ

# 作りたかったもの

状態を切り替えるtoggleボタンを押したら確認ダイアログが登場，

- 「OK」が押されれば状態が変化，同時にfirestore上に作成した，ボタンのboolean型fieldが変化
- 「キャンセル」が押されれば状態は変化しない，当然firestore上のfieldも変化しない

みたいなアイテムを作成．

# 問題点

初め以下のようなコードで実装していました．

```vuejs:example.vue
<template>
  <input
    type="checkbox"
    @click="onConfirmChangeStateDialog"
    :checked="state"
   />
</template>

<script>
onConfirmChangeStateDialog () {
  var title = 'test'
  if (window.confirm(title)) {
    let newState = !this.state
    this.changeEventHandler(newState)
  } else {
  }
},
</script>
```

`changeEventHandler()`はfirestoreにfieldの値をsetするためのメソッドです．`state`は算出プロパティです．このコードのままだと，firestoreの状態の変更は当然実装できていますが，確認ダイアログで「キャンセル」を押してもボタンの状態が変化してしまいます．

# 解決方法

そこで，イベントインターフェースの`.preventDefault()`メソッド([MDN Web docs](https://developer.mozilla.org/ja/docs/Web/API/Event/preventDefault))を用いてボタンのDOM動作を中止させると，作りたかったものが実装できました．

```vuejs:example.vue
<template>
  <input
    type="checkbox"
    @click="onConfirmChangeStateDialog($event)"
    :checked="state"
   />
</template>

<script>
onConfirmChangeStateDialog (e) {
  var title = 'test'
  if (window.confirm(title)) {
    let newState = !this.state
    this.changeEventHandler(newState)
  } else {
   e.preventDefault()
  }
},
</script>
```

以上です．
EC2インスタンスのGitを慌ててアプデする前に

## gitに脆弱性だと...?アプデしなきゃ...

お馴染みのgitに[悪意のあるURLにより資格情報を不正に取得されてしまう脆弱性](https://github.com/git/git/security/advisories/GHSA-qm7j-c969-7j4q)が発覚し話題になりました

当然アップデートするべきですが、自分の環境だけでなく、
開発環境のサーバもgitが入って、そちらもアップデートする場合もあると思います。

## EC2インスタンスの場合
開発者の工夫によりけりですが、通常EC2インスタンスでgitを導入する最も素朴な方法はyumでgitをインストールする方法です

しかしyumでのインストールでは執筆時点(2020年４月末)の最新版が`git 2.23.1`

yum updateしても変わらず、

`git 2.23系`でのgit公式での対策済みバージョンは`2.23.2`です

yum経由ダメじゃん、どうする、brewを経由するか？直接最新版を指定するか？

となる前に

## 公式の情報を確認

[Amazon Linux Security Center](https://alas.aws.amazon.com/)というサイトがあります

最近話題のgit脆弱性に関する情報はこちら
https://alas.aws.amazon.com/AL2/ALAS-2020-1409.html

> Issue Overview:
With a crafted URL that contains a newline in it, the credential helper machinery can be fooled to give credential information for a wrong host. The attack has been made impossible by forbidding a newline character in any value passed via the credential protocol. (CVE-2020-5260 )

google翻訳
> 改行を含む巧妙に細工されたURLを使用すると、資格情報ヘルパー機構がだまされて、間違ったホストの資格情報が提供される可能性があります。資格情報プロトコルを介して渡される値に改行文字を禁止することにより、攻撃を不可能にしました。（CVE-2020-5260）

つまりマイナーバージョンとしては2.23.1でありながら例の脆弱性については対策済のものが配信されています(amazon独自のyumリポジトリが存在する)
念の為脆弱性のないバージョンにしたいだけなら`yum update git`するだけでOKですね
慌てて特殊な方法でインストールして忘れたころに混乱するよりは、
公式の対応を待った方が良さそうです
AWS偉いという話でした
AWS Certificate Manager (ACM) でSSL証明書を取得する

前回 [**CloudFront を介してS3 のコンテンツを公開する**](https://qiita.com/kanadeee/items/247d114170587e683c4f) という記事を書いた流れで、
「CloudFront で独自ドメインを使いたい」と思い立ちました。

調べたところ、SSL証明書が必要だと発覚。
せっかくなのでAWS Certificate Manager (ACM) でSSL証明書を取得してみました！

#用意するもの
- お好きなドメイン

　※ 今回は[ムームードメイン](https://muumuu-domain.com/)で取得しました

#ざっくり手順
- ACM で証明書のリクエスト
- ドメインサービス側でCNAME レコードを登録

# 気をつけること
ACM で取得するSSL証明書を**CloudFront で使いたい場合、米国東部（バージニア北部） リージョンでSSL証明書をリクエスト**する必要があります。
![computer_woman3_cry.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/156147/7b24006c-e7f4-247b-9ee0-61ef088343ab.png)
[公式ドキュメント](https://docs.aws.amazon.com/ja_jp/acm/latest/userguide/acm-regions.html)にも記載がありますが、見逃してしまいハマりました。

#作業手順
**1.　[AWSコンソール](https://console.aws.amazon.com/console/home) にログイン**

**2.　[ACM] と入力し、Enterキーを押す**
Certificate Managerが出てきます。これがACMです。

------------- ▼ ここからACM の作業 ▼ -------------

**3.　[証明書のリクエスト] をクリック**
![スクリーンショット 2020-04-29 16.18.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/156147/04dd837f-a234-4f51-e7f4-80a570469985.png)

**4.　[パブリック証明書のリクエスト] を選択した状態で[証明書のリクエスト] をクリック**
![スクリーンショット 2020-04-29 16.18.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/156147/0db64c2e-679d-d698-5860-6f31810bb9dc.png)

**5.　取得したドメイン名を入力し、[次へ] をクリック**
![スクリーンショット 2020-04-29 16.18.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/156147/96cbd8a3-856f-8d42-7381-82a607f6718a.png)

**6.　[DNS の検証] を選択した状態で[次へ] をクリック**
※ Whois情報に記載されているメールアドレスは自分の手元で受信できないため、今回は [DNS の検証]をえらんでいます
![スクリーンショット 2020-04-29 16.19.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/156147/da34ba53-5f81-ff11-014b-a1d16f6a79d2.png)

**7.　必要であればタグを追加し、[確認] をクリック**

**8.　検証状態が[検証保留中] になっていることを確認する。CNAME の情報を控えておく**
ドメインサービス側でCNAME レコードを登録する必要がある
![スクリーンショット 2020-04-29 16.19.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/156147/143a4eb2-1414-d46e-9c41-532a2638f56c.png)

------------- ▲ ここまでACM の作業 ▲ -------------

**9.　ドメインサービス側にCNAME レコードを登録**
※ 余談
　No.8の画面の名前と値をそのままCNAME レコードに登録したらダメでハマりました。
　ムームードメインだったので、[こちら](http://blog.serverworks.co.jp/tech/2017/12/04/acm-dns/)の記事を参考にさせていただき解決！

**10.　しばし待つ**

------------- ▼ ここからACM の作業 ▼ -------------

**11.　ACMの管理画面を更新し、[発行済み] になっていれば問題なし**
![スクリーンショット 2020-04-29 16.30.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/156147/3e896d2c-f029-b3b0-e907-479334cbc10f.png)


------------- ▲ ここまでACM の作業 ▲ -------------
#参考

- [サポートされているリージョン - AWS Certificate Manager](https://docs.aws.amazon.com/ja_jp/acm/latest/userguide/acm-regions.html)

- [ムームードメインで取得したドメインをS3+CloudFront+Route 53+ACMに適用させるときにハマった](https://qiita.com/aokabin/items/b03850c450d50abed952)

- [カスタム設定のセットアップ方法 | ムームードメイン](https://muumuu-domain.com/?mode=guide&state=custom_setup)

- [ACM証明書発行をDNS検証で行う – サーバーワークスエンジニアブログ](http://blog.serverworks.co.jp/tech/2017/12/04/acm-dns/)
Android、Chrome、CloudFrontの組み合わせでNo 'Access-Control-Allow-Origin'が表示される

CloudFrontでリソースの配信を行っているとき、Android版のChromeだけでCORSのエラーが表示されることがある。

```
No 'Access-Control-Allow-Origin' header is present on the requested resource
```

CloudFrontにおけるCORSの設定は [CloudFront からの「No Access-Control-Allow-Origin」を解決する](https://aws.amazon.com/jp/premiumsupport/knowledge-center/no-access-control-allow-origin-error/) に書いてある通りなのだが、この設定を行っていてもエラーが起きることがある。

[Cross-origin request from cache failing after regular request is cached.](https://bugs.chromium.org/p/chromium/issues/detail?id=409090) によると、Chromeがキャッシュからリソースを読み込んだときに起きる現象とのこと。バグではなく仕様扱いなので、今後修正されることはない。

## 再現条件

自分が遭遇した範囲でのこのCORSエラーの再現条件は下記の通り。

- Android版Chromeを使っている
- CloudFrontからリソースの配信を行っている
- 基本的なCORSの設定は正しく行っている
- scriptタグやstyleタグにcrossorigin="anonymous"属性が付いている
- 二度目以降のページ表示で上記のエラーが起きる

## 解決策

- CloudFrontから配信しているリソースは、`crossorigin="anonymous"`をscriptタグやstyleタグから削除する

私の場合はこれで解決できた。ただし、キャッシュの設定方法や影響範囲は多岐にわたるため、細かい条件によっては他の方法が必要になると思われる。

一例として、Railsでコンパイルしたリソースを下記のコードで配信しているとこのエラーに遭遇する。特に変わったことをしていないのなら、下記のコードで`crossorigin: "anonymous"`を付ける意味はなく、単純に削除してよい。

```ruby
= stylesheet_link_tag 'application', media: 'all', crossorigin: "anonymous"
= javascript_include_tag 'application', crossorigin: "anonymous"
= javascript_pack_tag 'application', crossorigin: "anonymous"

# 上記のコードでは crossorigin: "anonymous" を削除しても問題ない
```

## 参考リンク

[409090 - Cross-origin request from cache failing after regular request is cached. - chromium](https://bugs.chromium.org/p/chromium/issues/detail?id=409090)

[CloudFront からの「No Access-Control-Allow-Origin」を解決する](https://aws.amazon.com/jp/premiumsupport/knowledge-center/no-access-control-allow-origin-error/)
言語処理100本ノック　第4章: 形態素解析 事前処理

仕事で自然言語処理をやることになったが、全くの素人のため100本ノックで学習する。
Pythonも素人なので、ひとまず解いてみて先人の方のよりイケてるコードを学びたい。
仕事で自然言語処理モデルのBERTを使うため、事前学習モデルとして[京大 黒橋・村脇研究室のBERT日本語Pretrainedモデル](http://nlp.ist.i.kyoto-u.ac.jp/index.php?BERT日本語Pretrainedモデル)を利用する予定。
調べると形態素解析はJuman++を使わないとダメなので、100本ノックでMeCabを使て〜、の部分はJuman++をつかって〜、に読み替えて解く。

実行環境
・MacOS
・[Anaconda](https://www.anaconda.com/products/individual)からのJupyter Notebook
・Python 3.7


# 第4章: 形態素解析 事前処理
> 夏目漱石の小説『吾輩は猫である』の文章（neko.txt）をMeCabを使って形態素解析し，その結果をneko.txt.mecabというファイルに保存せよ．このファイルを用いて，以下の問に対応するプログラムを実装せよ． なお，問題37, 38, 39はmatplotlibもしくはGnuplotを用いるとよい

### データ前処理
ひとまずファイル読み込み

``` python
# ファイル読み込み
# https://note.nkmk.me/python-file-io-open-with/
path = '/Users/shin/Documents/NLP/neko.txt'
with open(path) as f:
    l = f.readlines()
    print(l)
```

結果

```
['一\n', '\n', '\u3000吾輩は猫である。\n', '名前はまだ無い。\n', '\n', '\u3000どこで生れたかとんと見当がつかぬ。\n', '何でも薄暗いじめじめした所でニャーニャー泣いていた事だけは記憶している。\n', '吾輩はここで始めて人間というものを見た。\n', 'しかもあとで聞くとそれは書生という人間中で一番獰悪な種族であったそうだ。\n', 'この書生というのは時々我々を捕えて煮
```


データの中身を見ると、
・\n
・\u3000
・半角スペース ※後述の形態素解析で処理出来ないことが発覚
といった不要な文字があるので削除する。

``` python
# 改行（\n）、全角空白（\u3000）の削除
# https://qiita.com/tag1216/items/df6c93bdb823dd48af6c

# Pythonで文字列のリスト（配列）の条件を満たす要素を抽出、置換
# https://note.nkmk.me/python-list-str-select-replace/
table = str.maketrans({
    '\n': '',
    '\u3000': '',
    # 半角スペースだと形態素解析でうまくいかなかったため、全角スペースに変換
    # https://qiita.com/trtd56/items/2f62a390eb8fab92e801
    ' ':'　'
})

l_replace = [s.translate(table) for s in l]
l_replace
```

結果

```
['一',
 '',
 '吾輩は猫である。',
 '名前はまだ無い。',
 '',
 'どこで生れたかとんと見当がつかぬ。',
```

上記のように空のリストデータが残るので、データ削除する。

``` python
# 空のlist削除
# https://note.nkmk.me/python-list-clear-pop-remove-del/
l_replace = [s for s in l_replace if s != '']
```

以上で前処理が出来たので、順次問題を解いていく。
Xcode11にアップデートしたらsimulatorで実行時エラー

Xcode10を公式からダウンロードして使用していたためアップデートの通知とか来なくて、しばらくアップデートを怠っていた。
iOSのバージョンアップに伴いXcodeのバージョンアップも必要となりバージョンアップしたところ、ビルドしてsimulatorで実行するまでに色々つまづいたけど、ここが一番ハマったから備忘録

## simulatorで実行時エラー
やっとビルド成功してsimulator起動した〜！！と思ったら

```
FIRInstallationsItem registeredInstallationWithJSONData:date:error:
```

とか出て落ちる。
ずっと原因がわからず1日ハマった。
### 解決策
Cartfilに

```
binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseCrashlyticsBinary.json"
```

を追加して

```
carthage update --platform iOS --cache-builds
```
でいけた。何で。。。以前のバージョンでは無くてもはいけてたのに。。。

以下を参考にさせてもらって必要なライブラリを再確認
https://qiita.com/zizi4n5/items/5a4ecbe8bb7ab40a56d4
【Laravel】Laravel初期設定

#①タイムゾーン変更

タイムゾーンを下記の通り変更する。

```php:config/app.php
// 変更前
'timezone' => 'UTC',

// 変更後
'timezone' => 'Asia/Tokyo',
```

#②Locale変更

Localeを下記の通り変更する。

```php:config/app.php
// 変更前
'locale' => 'en',

// 変更後
'locale' => 'ja',
```

#③DB文字コード変更

DB文字コード変更を下記の通り変更する。

```php:config/database.php
// 変更前
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',

// 変更後
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
```

#④デバッグバーのインストール

ディレクトリ移動

```
公文）
$ cd /Applications/MAMP/htdocs/プロジェクト名
例）
$ cd /Applications/MAMP/htdocs/laravel_test
```

デバッグバーのインストール

```
$ composer require barryvdh/laravel-debugbar
```

サーバ起動

```
$ php artisan serve
```

起動確認
http://127.0.0.1:8000

#⑤デバッグバー非表示設定

デバッグバー表示設定を下記の通り変更する。

```env:.env
// 変更前
APP_DEBUG=true

// 変更後
APP_DEBUG=false
```

Laravel cacheクリア

```
$ php artisan cache:clear
```

Laravel configクリア

```
$ php artisan config:clear
```
在宅勤務で集中力が切れたときにやること。

#別に在宅勤務に限った話ではないが、集中切れたときにやることリスト。
適宜更新していきます。

## 単純作業タスクを消化する
ここで言う単純作業は、あとはやるだけの状態になっていて、決定したり判断したりすることが不要なタスクのことです。
集中力がないときにやることリストみたいなものを作って置いて、集中切れたときにそれを消化すると、手を動かしているうちに集中を戻すことができてタスクも進みます。
日頃から緊急でないタスクはリストにしておくのが大事で、今やらなくてもいいことを常に頭の中に入れながら仕事するのは集中の妨げになるので、今やらなくてもいいことはリストにして（締切があるものはslack等でリマインダーもセット）、今やるべきことにより集中できるようにしましょう。
例） テストケース通りのテストデータをDBに入れる、勤怠入力

## タイピングゲーム
集中できないときには手を動かすのがいいと思います。タイピングしているうちにいつの間にか集中力が戻ってきます。
タイピングの速度も上がって一石二鳥です。
私が使っているのは以下の2つのサイトです。夢中になりすぎてタイピングゲームの方に長時間集中してしまうと本末転倒なので注意。

- [e-typing](https://www.e-typing.ne.jp)
    - 会員登録するとスコアの推移を見れるので、便利。最高レベルGoodだったのが、2ヶ月立って最高レベルThunderまで来ました。
- [寿司打](http://typingx0.net/sushida/play.html)
    - スピードを意識してひたすらやりたいときはこっち使ってます。タイピング音が気持ちいいです。高級10000円コースでやってます。

## 筋トレ
在宅だと運動不足になりがちだと思うので、息切れしない程度に筋トレします。気分転換になり、集中力の回復になります。
[プッシュアップバー](https://www.amazon.co.jp/s?k=%E3%83%97%E3%83%83%E3%82%B7%E3%83%A5%E3%83%90%E3%83%BC&__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&ref=nb_sb_noss_1) は必須アイテムです。筋肉の肥大化というよりは、肩甲骨や股関節のストレッチを意識してやるのが個人的にはおすすめです。

振動が響かないお家の方には縄なし縄跳びもおすすめです。
専用の[縄跳び](https://www.amazon.co.jp/s?k=%E7%B8%84%E3%81%AA%E3%81%97%E7%B8%84%E8%B7%B3%E3%81%B3&__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&ref=nb_sb_noss_1)も売っていますが、適度な重さの棒2本あればできます。縄跳びの縄っていらなかったんだなと気づくはずです。

## 寝る。
在宅勤務だとすぐベッドに移動できるのが素晴らしいですね。タイマーかけてベッドに入りましょう。
机にうつ伏せで寝るのはあまりおすすめしないです。
5分くらいでもいいので、短時間の睡眠をすると集中力の回復になります。

## 遠くを見る。
ずっとPCの画面を見続けると目が疲れます。目の疲れは集中力の低下に繋がります。
窓の外の遠くの景色を見てリフレッシュしましょう。
遠くと近くを交互に見ると視力のトレーニングにもなります。

## 瞑想
マインドフルネスも集中力回復に効果的。
やり方は本とかサイトとかを見てもらえればと思いますが、周りの雑音が少ない在宅だからこそマインドフルネスの練習もしやすいはず。

# 集中するために

## 仕事場周りのものを減らす。
仕事に関係のないものが多いと集中が切れやすくなるそうです。
集中できる環境を構築していきましょう。
私もまだまだなので、日々よりよい環境を目指して行きます。

# まとめ
在宅勤務になって、集中できる状況を自分で作る必要が出てきた人も多いと思います。
この機会に自分の集中できる空間作りをしていきましょう。
ReactNativeの自動linkがiOSだけ動かなくて困った話

# 初めに
ReactNativeを0.5xから0.6xにアップデートした際、0.60.0から導入されたライブラリの自動リンク機能がiOSだけ動かなくて困っていた。

仕方ないので、
`一旦手動でlink → iOSの差分だけコミット`
としていたのだがさすがに面倒なのでちゃんとした対策は無いものか、と思って調べたらちゃんとあった。

# やったこと

`reactnative auto link not working` でググったら、react-native-fsというライブラリの ↓ というissueのコメントが見つかった。
https://github.com/itinance/react-native-fs/issues/708#issuecomment-559785244

これに従い、Podfileに `use_native_modules!` の一文を加えたら直った。
https://react-native-community.github.io/upgrade-helper/?from=0.59.9&to=0.61.5

↑ の差分を見て、手で必要な分だけ導入していった際に入れ忘れたらしい。

# 終わりに

長期間メンテすることがわかっているプロジェクトをReactNativeで作るのは全くおすすめできない。
フルコミットできる熟練者がいる場合は変わってくるのかもしれないが。
OWASP ZAP2.9.0で脆弱性診断をする - アンチCSRFトークンを設定する

この記事について、マニュアルのAnti CSRF Tokensページを参考にしています。このページを見てわからないことがあれば、マニュアルを参照してください。

公式サイト：https://www.zaproxy.org/
OWASP ZAP Desktop User Guide：https://www.zaproxy.org/docs/desktop/
Anti CSRF Tokens：https://www.zaproxy.org/docs/desktop/start/features/anticsrf/

# Getting Started
ZAPの簡単な使い方は、公式ドキュメントの[Getting Started](https://www.zaproxy.org/getting-started/)を参考にしてください。また、私なりにまとめた[OWASP ZAP2.9.0で脆弱性診断をする - Getting Started](https://qiita.com/koujimatsuda11/items/3d5b7eac5f9455015ba6)も見ていただける幸いです

# バージョン
この記事では、OWASP ZAP2.9.0のデスクトップアプリを使っています。

# アンチCSRFトークン
アンチCSRFトークンとは、CSRF攻撃防止するためにフォーム内に存在するランダムなトークンです。診断対象サイトにて存在する場合、スキャンする時に正常にスキャンできないことがあります。
ZAPでは、アンチCSRFトークンのパラメータ名を設定しておくことで、Automated scanやManual Exploreの際に、アンチCSRFトークンを検出するとその時のURLと値を保持し、Active scanや他のスキャンをする時に、自動で再生成するオプションがあります。

## 設定方法
### パラメータ名の設定
パラメータ名の設定は、Automated scanやManual Exploreの前に設定しておきます。

1. ヘッダのオプションアイコンをクリックします
2. オプション画面でアンチCSRFトークンをクリックします
3. 追加ボタンをクリックします
4. トークン名を入れて追加ボタンをクリックします

<img width="436" alt="スクリーンショット 2020-04-29 22.37.08.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/343282/b0606bc1-9187-c512-ff64-aaef5162f042.png">

<img width="862" alt="スクリーンショット 2020-04-29 22.42.16.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/343282/51622748-003f-bba4-143c-be3f51ffb203.png">

<img width="761" alt="スクリーンショット 2020-04-29 22.42.38.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/343282/a7e67e0c-1450-2c82-6a39-52e29bcb9ff1.png">

### Active scanで有効にする

1. ヘッダのオプションアイコンをクリックします
2. オプション画面で動的スキャンをクリックします
3. アンチCSRFトークン利用にチェックを入れます

デフォルトでは設定されていませんので、Active scanの前に必要であればチェックをしましょう。ただし、このチェックを入れるとスキャンに時間がかかることがあります。通常マルチスレッドで行われるスキャンが、1つのスレッドになります。

<img width="436" alt="スクリーンショット 2020-04-29 22.37.08.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/343282/b0606bc1-9187-c512-ff64-aaef5162f042.png">

<img width="862" alt="スクリーンショット 2020-04-29 22.45.51.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/343282/8374952b-2d1a-8b27-98bf-8322977c5b44.png">


# 参考
https://www.zaproxy.org/
https://www.zaproxy.org/docs/desktop/
https://www.zaproxy.org/docs/desktop/start/features/anticsrf/
https://www.zaproxy.org/docs/desktop/ui/dialogs/options/ascan/
https://www.pupha.net/archives/1637/
AWS EC2のReserved Instancesはもう買うのをやめて、Savings Planに切り替えよう

# TL; DR

EC2において、Reserved Instancesはもう買う必要がない。
利用確約でコストを下げるならSavings Planを、キャパシティを確保するにはオンデマンドキャパシティ予約を行う。両方組み合わせればRIと同様の機能でより柔軟性が上がる。
[Savings Plan](https://aws.amazon.com/jp/savingsplans/)
[EC2のFAQ](https://aws.amazon.com/jp/ec2/faqs/)にも以下のように書かれている。

> 引き続き EC2 RI を購入することはできますか?
> 
はい。既存のコスト管理プロセスとの互換性を維持するために引き続き RI を購入することができ、お使いの RI は全体的な請求額を削減するために Savings Plans と並行して機能します。ただし、Savings Plans は RI と同様の節約を実現しながら追加の柔軟性を提供するため、RI の期限が切れたときは Savings Plans にサインアップすることをお勧めします。

RDSなどEC2以外のReserved Instancesは引き続き利用価値がある。


# RIを購入するメリット（だったもの）
- コスト削減
1年間,3年間利用を確約することで、EC2インスタンス費用が割引されます。

- キャパシティ予約(Standard Zone RIのみ)
RIを購入した分はインスタンスが確保されているため、一時的なキャパシティ不足で起動失敗することがなくなります。
小規模だとあまり影響はありませんが、大規模なサービスだとリソース不足による起動失敗はしばしば発生します。

Savings Planはコスト削減機能に特化したものです。キャパシティ予約機能は「オンデマンドキャパシティ予約」として独立しました。
Savings Planとオンデマンドキャパシティ予約を組み合わせることでRI同様の運用になります。

# 割引率は同じ
Convertible RIとComputing Savings Plan、Standard RIとEC2 Savings Planの割引率は同じ。

# Savings Planのほうが優れている点(Computing Savings Plan vs Convertible RI編)

## EC2だけでなく、Fargate, Lambdaにも適用できる
Computing Savings PlanはEC2インスタンスだけでなく、FargateやLambdaも割引します。ただし、Fargate, LambdaはEC2より割引率が低いです。
EC2, Fargate, Lambdaを併用している場合、割引率が高い順に適用されていくため、割引率が最も悪いLambda(10%)に割引が適用されることはあまりないと思います。むしろLambdaに割引が適用されているのであれば、買いすぎの可能性があります。

## 全リージョンで有効
Convertible RIはリージョン単位で購入しましたが、Compute Savings Planは全リージョンで有効です。DR用のサブリージョンなど量が微妙すぎるリージョンでも買いやすくなりますね。

## インスタンス交換が不要
Convertible RIは、c5->r5などインスタンスファミリー交換ができますが、手動で交換操作を行う必要がありました。そのため新しいファミリーに入れ替えるときなど、交換作業を忘れていると無駄になってしまいます。
Compute Savings Planは特定のインスタンスファミリーに結びつかないので、割引率が高くなるように勝手に適用されます。
なお同一インスタンスファミリー内での振り分けはConvertible RIでも自動的に行われます。

# Savings Planのほうが優れている点(EC2 Instance Savings Plan vs Standard Region RI編)
## テナンシー、OSが切り替え可能
Reserved Instancesは購入時のテナンシー（共有インスタンスか専有インスタンスか）、OS（無料Linuxか商用LinuxかWindowsか）が異なるインスタンスには適用できませんでした。
Savings Planは異なるテナンシーやOSでも自動的に適用されます。実際に入れ替えることは少なそうですが、買い間違えて悲惨な思いをすることはなくなります。

# Savings Planのほうが優れている点(共通)
## 値下げの恩恵が受けられる
RIは購入時にインスタンスあたりの値段が決まるため、あとで値下げがあっても恩恵を受けられませんでした。Savings Planは台数ではなく時間あたりの料金ので確約します。そのため将来的に値下がりすると、費用は同じで起動できるインスタンス数が増えるというメリットがあります。

# Reserved Instancesのほうが優れている点
##同一リージョンでのボリュームディスカウントがない
1リージョンで有効なRIの金額が合計500,000USDを超えると超えた分は割引があります。Savings Planにはありません。
ただ、この割引は下限で日本円で約5500万円という高いハードルと、超過した分の割引率が5%（さらに4,000,000USDを超えた分は10%）という微妙な割引率なので、これが関係してくる人はあまりいないかと思われます。

## 計算が面倒

常時起動しているインスタンスに対してSavings Planを購入することが多いと思いますが、インスタンスタイプ×台数で購入すれば良かったRIに対してSavings Planの購入は若干面倒です。

東京リージョンのt3.large（共有テナンシーLinux）を1年前払いなしで購入するとします。
1年前払いなしプランは0.0685USD/hなので、仮に10台相当購入するとした場合、0.0685 * 10 = 0.685USDをコミットすればよいことになります。オンデマンドインスタンスの価格でコミットしてしまうと過剰に購入することになるのでご注意ください。
AWS WorkSpace/AmazonlinuxのGUIのキーボードレイアウト変更

無知なせいでちょっとだけはまったので備忘に投稿します。
WorkSpaceを日本語設定で作成すると、キーボードレイアウトも当然日本語キーボードなのですが、普段usレイアウトを使っていて不便だったので修正することにしました。

## 現在の設定を確認


X11が jpレイアウトになっている。

```console
    $ localectl
       System Locale: LANG=ja_JP.utf8
           VC Keymap: us
          X11 Layout: jp
```

## 設定変更

x11のキーマップをusに

```console
    $ sudo localectl set-x11-keymap us
    $ localectl
    System Locale: LANG=ja_JP.utf8
    VC Keymap: us
    X11 Layout: us
```

これで入力できるかな？と思ったけど、変化なし。

IMEのプロパティをみると、

![052.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613415/1d06c52e-9789-aaf4-aa76-e94723423ff9.jpeg)

「システムのキーボードレイアウトを使用する」にチェックが入っていないので入れます。
IMEをGUI上から再起動したが、反映されないので、OSを再起動。

無事英語キーボードで入力可能になりました。
つまらないところですが、覚えてられそうにないので記載。

## 参考
- CentOS7 ( RHEL7 ) でサクッとキーボードレイアウトを変更するよ！
https://dotanba.net/centos7-01/

参考にしたのですが、console keymapsはエントリなし、で帰ってきました。何か違いがあるのかしらん。
$ sudo localectl list-keymaps
Couldn't find any console keymaps.
Ruby と Perl と Java と Python で解く AtCoder ATC 002 B 繰返し二乗法

# はじめに
AtCoder Typical Contest(ATC) とは、競技プログラミングにおける、典型問題を出題するコンテストです。
AtCoder さん、ありがとうございます。

# 今回のお題
*[AtCoder Typical Contest 002 B - n^p mod m](https://atcoder.jp/contests/atc002/tasks/atc002_b)*

今回のテーマ、繰返し二乗法
# Ruby
例えば、2 の 64 乗を求める場合、単に 2 を掛けるのであれば 63 回掛け算が必要ですが、計算結果を利用することにより 6 回の掛け算で求めることができ、計算の高速化につながります。

```2^64.rb
n = 2
63.times do
  n *= 2
end
puts n # => 18446744073709551616

n = 2
6.times do
  n *= n
end
puts n # => 18446744073709551616
```
これを奇数乗や除数にも適応したのが、繰返し二乗法になります。

```ruby.rb
n, m, p = gets.split.map(&:to_i)
def mpow(n, p, mod)
  r = 1
  while p > 0
    r = r * n % mod if p & 1 == 1
    n = n * n % mod
    p >>= 1
  end
  r
end
puts mpow(n, p, m)
```
関数化しておけば、後々のコンテストにも~~コピペ~~使用できます。

# Python
```python.py
n, m, p = map(int, input().split())
def mpow(n, p, mod):
    r = 1
    while p > 0:
        if p & 1 == 1:
            r = r * n % mod
        n = n * n % mod
        p >>= 1
    return r
print(mpow(n, p, m))
```
後置 if はもう少し勉強してからになります。
# Perl
```perl.pl
chomp (my ($n, $m, $p) = split / /, <STDIN>);
sub mpow {
  my ($n, $p) = @_;
  my $r = 1;
  while ($p > 0) {
    $r = $r * $n % $m if $p & 1;
    $n = $n * $n % $m;
    $p >>= 1;
  }
  $r;
}
print mpow($n, $p), "\n";
```
*Perl* の場合、$m のスコープでエラーにならないようです。
# Java
```java.java
import java.math.BigInteger;
import java.util.*;

class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        BigInteger n = new BigInteger(sc.next());
        BigInteger m = new BigInteger(sc.next());
        BigInteger p = new BigInteger(sc.next());
        sc.close();
        System.out.println(n.modPow(p, m));
    }
}
```
*Java* には BigInteger クラスに modPow メソッドがあります。

||Ruby|Python|Perl|Java|
|:--:|:--:|:--:|:--:|:--:|
|コード長|183 Byte|217 Byte|227 Byte|386 Byte|
|実行時間|7 ms|17 ms|3 ms|106 ms|
|メモリ|1788 KB|3060 KB|640 KB|24020 KB|
# まとめ
+ ATC 002 B を解いた
+ Ruby に詳しくなった
+ Python に詳しくなった
+ Perl に詳しくなった
+ Java に詳しくなった
ビジュアルプログラミングツール”Noodl”　画面スクロールの方法

#概要
こんにちは！
Noodlerのまえぷーです！

Noodl ver2.0βになってからノードの使用が**ガラッ**と変わりましたね。

その中で以前は存在した「**Scroll viewノード**」は廃止され。**Groupノードへ統合**されたようです。

今回はNoodl ver2.0βで画面スクロールを実装する方法について解説します。

最終的には次のように動かして確認しました。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">Noodlで画面スクロールする方法が分かった！<br>Qiita記事書きます～。<a href="https://twitter.com/hashtag/Noodl?src=hash&amp;ref_src=twsrc%5Etfw">#Noodl</a> <a href="https://t.co/VdCKlCcYV6">pic.twitter.com/VdCKlCcYV6</a></p>&mdash; まえぷー@出窓菜園 BWG (@kmaepu) <a href="https://twitter.com/kmaepu/status/1255483707940655104?ref_src=twsrc%5Etfw">April 29, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

#方法
　Groupノードの４項目を設定でスクロールさせることができるようになります。

|  属性  |  内容  |  備考  |
| ---- | ---- | ---- |
|  Scroll Behavior  |  スクロールタイプ  |  --  |
|  Scroll Direction  |  スクロール方向  |  スクロールタイプがNoodlの場合のみ　|
|  Snap Enabled  |  スナップ動作の有効/無効  |  スクロールタイプがNoodlの場合のみ　|
|  Show Scrollbar  |  スクロールバーの表示  |  スクロールタイプがNoodlの場合のみ　|


【Scroll Behavior】
次の３つの項目があります。

|  属性  |  内容  |
| ---- | ---- |
|  None  |  スクロールなし  |
|  Native  |  従来の、パソコンのスクロール  |
|  Noodl  |  スクロールしたときだけ小さなバーが出現する  |

【Scroll Direction】
次の３つの項目があります。

|  属性  |  内容  |
| ---- | ---- |
|  Vertical  |  垂直方向  |
|  Horizontal  |  水泳方向  |
|  Both  | 両方向  |

【Snap Enabled】
チェックを付けると、スナップ動作を有効にできます。

【Show Scrollbar】
チェックを付けると、常にスクロールバーが表示されるようになります。

#おわりに
　Noodl ver2.0になってからモヤモヤしていたことが１つ解決できました！
このスクロールって外部イベントをトリガにできたりしないかなぁ...。

#参考
[Noodl 2.0β版の機能デモをつくってみた(実装編4：シングルページアプリケーション)](https://qiita.com/macole/items/ebeefd92a61e4c755d1b)
Vapor4のControllerを使ったRoutingパターン

Vapor4でControllerを使わないRoutingは[公式ドキュメント](https://docs.vapor.codes/4.0/routing/)にあるんだけど、Controllerを使う方法が公式ドキュメントになかったので、ここにまとめておく。


### パラメータなしコントローラを使う

```swift
import Vapor

func routes(_ app: Application) throws {
    // Controllerを指定
    let helloController = HelloController()
    app.get("hello", use: helloController.hello)
}

final class HelloController {
    func hello(_ req: Request) throws -> String {
        return "Hello, World"
    }
}
```

### パラメータ指定

```swift
func routes(_ app: Application) throws {
    // Controllerにパラメータを指定するパターン
    let todoController = ToDoController()
    // ex) GET: todo/1
    app.get("todo", ":id", use: todoController.find)
    // ex) GET: todo/search/hoge/
    app.get("todo", "search", ":query", use: todoController.search)
    // ex) GET: todo/1/comments/2
    app.get("todo", ":id", "comments", ":comment_id", use: todoController.comment)
}

struct ToDoController {
    func find(_ req: Request) throws -> String {
        guard let id = req.parameters.get("id") as Int? else {
            return "パラメータが設定されていない"
        }

        return "\(id)のToDo"
    }

    func search(_ req: Request) throws -> String {
        guard let query = req.parameters.get("query") as String? else {
            return "クエリが設定されていない"
        }

        return "\(query)で探す"
    }

    func comment(_ req: Request) throws -> String {
        guard let id = req.parameters.get("id") as String? else {
            return "idが設定されていない"
        }

        guard let commentId = req.parameters.get("comment_id") as String? else {
            return "コメントidが設定されていない"
        }

        return "todo\(id)のコメント\(commentId)返す"
    }
}
```

### カスタムなRouterを作る

- bootメソッドが必須
    - 細かいRoutingをしていく
    - 共通のメソッドを実装しやすい
        - もしかしたらbootで共通のメソッドを適用させる方法があるかも

```swift
struct APIV1Router: RouteCollection {
    func boot(routes: RoutesBuilder) throws {
        let apiRouter = routes.grouped("api").grouped("v1")
        apiRouter.get("hello", use: self.hello)
        apiRouter.get("world", use: self.world)
    }

    func hello(req: Request) -> String {
        validSession()
        return "api/v1/hello"
    }

    func world(req: Request) -> String {
        validSession()
        return "api/v1/world"
    }

    private func validSession() {
        // 共通のセッションに関する何か
    }
}
```
[Mac]Library not loaded: /usr/local/opt/openssl/lib/libssl.1.0.0.dylib error 解決法






# 起きたこと

Ruby プロジェクトで bundler 実行でエラー

(それ以外に、openssl に依存する場合は起こりうる)

``` 
$ bundle ./
...
Library not loaded: /usr/local/opt/openssl/lib/libssl.1.0.0.dylib (LoadError)
Referenced from: /Users/user/.rbenv/versions/2.6.3/lib/ruby/2.6.0/x86_64-darwin14/openssl.bundle
Reason: image not found - /Users/user/.rbenv/versions/2.6.3/lib/ruby/2.6.0/x86_64-darwin14/openssl.bundle
```

openssl 1.0.x がインストールされていないときに発生。

# 解決法

openssl v1.0.x をレポジトリからインストール しbrew switch

```
$ brew install https://github.com/tebelorg/Tump/releases/download/v1.0.0/openssl.rb
$ brew switch openssl 1.0.2t
```

確認する

```
$ brew list --versions openssl
openssl 1.0.2t
$ ls /usr/local/Cellar/openssl/
1.0.2t
$ /usr/local/opt/openssl/bin/openssl version
OpenSSL 1.0.2t  10 Sep 2019
```

通った。



# 参考


https://github.com/kelaberetiv/TagUI/issues/86

https://stackoverflow.com/questions/59269208/errorrootcode-for-hash-md5-was-not-found-when-using-any-hg-mercurial-command
vim-jp.org/slacklog


# プロフィール

![robot-cut2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1862/5afc3616-12ec-6ae7-7a13-04bf8a4dd3c2.jpeg)

* 藤原 拓也 a.k.a. tyru
* Twitter: `@_tyru_`
* GitHub: `@tyru`
* Vim 狂

---

# slacklog とは

vim-jp Slack のログを HTML 化するプロジェクト
https://github.com/vim-jp/slacklog

* Slack の流量が多く、無料枠なのでログがすぐ消えてしまうという問題があった
* Slack 上で交わされた会話を検索しようとしてとっくの昔に見れなくなってたりとか
* 自分のブログに Slack の会話を引用したいけど消えてたりとか

---

# もったいない

作ったろ

---

<img width="1552" alt="スクリーンショット 2020-05-02 19.53.47.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1862/90697a03-e4d8-7b1e-2cfe-eefc1dd5b188.png">

---

<img width="1552" alt="スクリーンショット 2020-05-02 19.53.55.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1862/c47b871a-2446-3b9a-178b-a77f0db64ce7.png">

---

<img width="1552" alt="スクリーンショット 2020-05-02 19.54.33.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1862/e9f9de23-5106-bcd5-4da6-86746474abe1.png">

---

<img width="1136" alt="github-and-twitter-attachments.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1862/9c7cab34-b4ae-4487-8c60-24b3f87a2487.png">

---

<img width="1136" alt="pre.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1862/b8b98c67-2e98-14f1-9a97-522f4df365b8.png">

---

<img width="1136" alt="channel-link-and-code.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1862/840985b5-863f-64c1-3f1a-242d7a7c34db.png">

---

* vim-jp.org というサイトのリポジトリを元に、Jekyll の .html ファイルに変換するスクリプトを Go で書いた
* 最初はほぼ見た目を整えただけ
* 色んな方が協力してくれてどんどん機能が追加されている (感謝)

---

# 主な機能要件

* パーマリンク (to slacklog, and to Slack)
* スレッド対応
* ファイル・絵文字対応 (ローカルにダウンロードしている)
  * リポジトリを超過しないように squash したりして頑張っている

---

# プライバシー

* 削除依頼に対して割と気を使っている
* ログを残そうって話が出た段階から「後から削除してほしいって依頼が来たらどうする？」という事をちゃんと話し合っていた
* もちろん現状でその要件を100%カバーできている訳ではないものの、設計でそれをとても重視して進めている

---

# 開発メンバー募集

* 変換: Go
* フロント: テンプレート、JS
  * モバイル対応も順次対応中
* Dockerfile もすでに用意されているので Go のランタイムだけあれば開発が始められます
  * Docker を使わない場合は Ruby や Jekyll のインストールが必要です
* 興味だけでも持ってくれた方は vim-jp Slack の #vim-jp チャンネルで話し合いながら進めているので、覗くだけでもぜひ
Kubnernetes入門書(AWS EKS)をやっていきなりつまづいた

# Kubnernetes入門書(AWS EKS)をやっていきなりつまづいた
GW初日でKubnernetes入門しようかと書籍を購入してみたのですが
AWS側の仕様変更でいきなりつまづいたので書いてみます

## 書籍
[Kubernetes on AWS～アプリケーションエンジニア 本番環境へ備える](https://www.amazon.co.jp/dp/B087PTX51W/ref=dp-kindle-redirect?_encoding=UTF8&btkr=1)

## エラー内容
書籍通り、EKSクラスターを作成。
その後、

```
$ kubectl get nodes
No resources found.
```
EKSクラスターに接続するできるか確認するために打鍵するように記載があったのですが、どうやらエラーの模様・・・

## 原因
EKSクラスターを構築する方法ですが、執筆時点ではMC（マネジメントコンソール）とeksctlというCLIの2パターン存在するようです。
書籍ではeksctlで作成するのですが、実際はコマンドの引数に応じてCloudFormationサービスがいい感じに諸々作成してくれるので、デバッグがEKS初心者には中々厳しかったです。。。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/240644/159d2fe4-e707-f3a7-1ced-04f8cf46e784.png)
仕様が変わりましたとことです。。。

リンク先の英文を見ると
[Upcoming Changes to IP Assignment for EKS Managed Node Groups](https://aws.amazon.com/jp/blogs/containers/upcoming-changes-to-ip-assignment-for-eks-managed-node-groups/?nc1=h_ls)
>Starting April 20, 2020 April 22,2020, we’ll be updating the behavior of managed nodes groups to no longer assign public IPs to nodes. After this date, public IP assignment must be controlled via the subnet settings where the node is instantiated.

>This change will only affect new managed node groups created on or after April 20th April 22nd, the behavior of existing managed node groups will not change.

どうやら、いままではデフォルトでパブリックIPアドレスを自動でEC2に割り当てていたのをやめたみたいです。
今後はVPC(サブネット)の自動割り当てIPの設定でコントロールしてくれとのこと。

ノード用のEC2をみると、確かにプライベートIPしか採番されていなかったです。

おそらくパブリックIPがないので、ローカルPCからNodeへの疎通が取れないのが原因だった模様。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/240644/6afd8e97-5f0e-ff41-1320-559dc8623893.png)

## 改善
### 1.  サブネットの自動割り当てIPを有効化
VPCの画面から、サブネットを選択して「自動割り当てIP設定の変更」を選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/240644/2ab980e7-4f61-d6a9-c718-3ddf9b7f1480.png)

チェックボックスにチェックをつけて、保存します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/240644/35177e02-ea6e-824d-0107-9e7735d40a94.png)

※ちなみにMCではなく、NW周り用のスタックテンプレートを修正して、UpdateStackでも良いかと思います。
### 2.  EC2を再作成(ASG)
確かパブリックIPはEC2を再作成しないと割り当てしてくれなかった気がしたので、
AutoScalingGroup経由でEC2を再作成。
(EC2の停止→起動で行けたような気もしますが、確認しませんでした。。。
試してみていけなればこの手順でお願いします)

「希望キャパシティ」「最小」を0にして保存します。
しばらくすると、グループ内のEC2を0にしてくれます。
その後、再度「希望キャパシティ」「最小」を元の数値に戻して保存すると
EC2が新しく作成されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/240644/6b1eef86-b62f-c2f8-8044-f491a2413c71.png)

アクティビティ履歴から、様子が見れます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/240644/81baeaa5-b004-4f9e-067d-679aad8e9a00.png)


### 3.確認
無事にパブリックIPがEC2に割当たった状態で

```
 kubectl get nodes
NAME                                               STATUS   ROLES    AGE     VERSION
ip-192-168-1-150.ap-northeast-1.compute.internal   Ready    <none>   5m13s   v1.14.9-eks-1f0ca9
ip-192-168-2-63.ap-northeast-1.compute.internal    Ready    <none>   5m17s   v1.14.9-eks-1f0ca9
``` 

ようやく見えました。。。。。

##最後に
書籍内でVPC周りは用意されたCFnテンプレートで作成するので、AWSの知識が薄い方はつらいのかなーと思い執筆してみました。

EKSはAWSサービス複数を自動で管理してくれるサービスのようなので、何かエラーがあるときは各々のサービスにある程度知識がないとついらんだなー感じましたｗ。

この書籍を2020/05以降購入された方は必ず突き当たると思われるので、是非ご参考に！！！！
【いまさら数学】逆元とは

## 逆元とは
以下，wikipediaから引用
>逆元 （ぎゃくげん、英: inverse element）とは、数学、とくに抽象代数学において、数の加法に対する反数や乗法に関する逆数の概念の一般化で、直観的には与えられた元に結合してその効果を「打ち消す」効果を持つ元のことである。

まず"<b>元</b>"とは，「何らかの集合Gが与えられた時，Gの中に含まれる要素のことを一般的に"元"と言う」．

例えば，ある実数集合Gが
$$ G=(1,2,3,4,5) $$
と与えられた場合，集合Gを構成する1,2,3,4,5は集合Gに対する元と呼ぶ(個人的には"要素"という方がしっくりくるが)．

その元に対して，「打ち消す」効果を持つものを<b>逆元</b>と呼ぶ．
式で表すと，
$$x・y=y・x=e(単位元) ・・・　①$$
となり，
互いに「yはxの逆元」，「xはyの逆元」となる．


例として，x=2としたとき，①の式に当てはまるようなyは1/2(2の逆数)である．

そのため，1/2は2の逆元であり，反対に2は1/2の逆元と言える．

## 単位元とは
ついでに単位元について軽く説明．

<b>e(単位元)</b>とは
$$a・e=e・a=a$$
となるようなeを示し，実数空間で言うと"1"である．

## まとめ
単に実数が一つ与えられたときの逆数は何かって言われた場合，その数の逆数を答えれば良い．

ただ，複雑な計算式や複素数空間等が絡んできた場合は，そうとは言い切れないので注意．
ローカルリポジトリをGitHubにpushする

備忘録。
公開鍵は作成済み、GitHubに設定できているものとします:blush:

##手順

```sh
//該当ディレクトリに移動する
$ cd workspace/test

//ディレクトリをgit管理下に置く(初期化)
$ git init

//ディレクトリの状態を確認
$ git status

//コミット
$ git commit -m "first commit"

//リモート(GitHub)にアクセス
$ git remote add origin https://github.com/[GitHubのアカウント名]/[リモートのリポジトリ名].git

//masterブランチへpush
$ git push origin master
```
SFDXプロジェクト の Apex クラスはフォルダわけして配置可能であるか？

みなさん、こんにちはCLBです。
今回は SFDX プロジェクトでフォルダ階層を分ける事が可能なのかどうか試してみました。

# ネタバレ
できる。

# 事前準備
用意する開発ソフト・拡張パックは以下の通り。
- Visual Studio Code(VS Code)
- [Salesforce Extension Pack](https://marketplace.visualstudio.com/items?itemName=salesforce.salesforcedx-vscode)
- Salesforce Developer Edition の組織。無い人はサインアップしてください。

# プロジェクトの作成
- 適当な場所にフォルダを作成する。
- そのフォルダで `force:project:create` をしてプロジェクトを作る。
- 作成したプロジェクトのフォルダをVS Codeで開く。

細かい部分については、 https://base.terrasky.co.jp/articles/5f2tk 等を参考にしてみてください。

# クラスを作る
1. VS code でコマンドパレットを開いて、 `>SFDX create apex class` を選択する。
1. 作成するクラス名に適当な名前 (ClassFooとか) をつけて作成する。作成するフォルダに関してはデフォルト(`force-app/main/default/classes/ClassFoo.cls`)とする。
1. そのあと、 `classes` 配下に `foo` フォルダを作成し、作成したクラスをそのフォルダに移動する。(`force-app/main/default/classes/foo/ClassFoo.cls`)になる。
1. 作成したクラスに適当なメソッドを作成する。

```
public with sharing class ClassFoo {
    public ClassFoo() {
    }
    // 適当なメソッド
    public void foo() {
        System.debug('Hello');
    }
}
```

# デプロイしてみる
スクラッチ組織にデプロイしてみる。

```
時刻 sfdx force:source:push --json --loglevel fatal
時刻 sfdx force:source:push --json --loglevel fatal
 が終了コード 0 で終了しました。

=== プッシュされたソース
STATE  FULL NAME  TYPE       PROJECT PATH                                            
─────  ─────────  ─────────  ────────────────────────────────────────────────────────
Add    ClassFoo   ApexClass  force-app/main/default/classes/foo/ClassFoo.cls         
Add    ClassFoo   ApexClass  force-app/main/default/classes/foo/ClassFoo.cls-meta.xml
```

問題無くできた。  
スクラッチ組織の開発者コンソールから呼び出してみる。

```
ClassFoo foo = new ClassFoo();
foo.foo();
```

これも正常終了。

# 他のクラスからの呼び出し
さっきと同じ手順で `force-app/main/default/classes/` に `bar`フォルダを作成し、  
`ClassBar.cls`を作る。
そしてそのクラスから先ほど作った `ClassFoo.cls` を呼び出すようにする。
こんな感じ

```
public with sharing class ClassBar {
    public ClassBar() {

    }
    public void callFoo () {
        ClassFoo foo = new ClassFoo();
        foo.foo();
    }
}
```

結果的にフォルダ構成は以下のようになる。
![img.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/360434/0e6a459b-0ddf-c9db-c393-c079a1329a0f.png)

`ClassBar.cls` をデプロイする。問題無くできた。
(デプロイ実行結果は省略)
スクラッチ組織の開発者コンソールから呼び出してみる。

```
ClassBar bar = new ClassBar();
bar.callFoo();
```
これも正常終了。問題なさそう。

# 結論
- フォルダを分割する事はできる。
- 他のフォルダクラスを呼び出す際にもフォルダ名をつける必要は無い。(つまりリファクタをする必要もない。)
grafanaのhelm適用時にdefault backend - 404が出たときの対応法

# grafanaのhelm適用時にdefault backend - 404が出たときの対応法

プチ ハマったので、メモ。

## 現象

GCPのGCLB(ingress)経由など、proxy下でgrafanaにアクセスする際に `default backend - 404`が出る。

## 対応方法

[github](https://github.com/helm/charts/tree/master/stable/grafana)に記載されているが、defautlの設定値は以下

| ingress.path | Ingress accepted path | `/` |
|:--|:--|:--|

これを、こうする。

| ingress.path | Ingress accepted path | `/*` |
|:--|:--|:--|
【システム設計】システム間連携のアーキテクチャ

複数のシステムを連携させる場合、連携方法を検討する必要がある。
「システム間連携」の処理方式を4つの基本パターンに分類する。

①リソース共有（データベース共有、ディスク共有）
・データベース共有
ネットワーク経由で、別システムのDBサーバーにアクセスする。
Oracleの場合、DBリンクを利用する。
他のDBMSでも、同等の機能がある。

・ディスク共有
NFS(ネットワークファイルシステム)を利用して、Linux間のファイルを共有する。
→ネットワークを介してサーバ上のストレージ領域を、
ローカルストレージと同様にマウントして使える。

②アプリケーション連携（RPC、Webサービスなど）
システムに外部から呼び出すことのできる「インターフェース（API）」を用意する。
連携する側は公開されているインターフェースを利用して、
システムが蓄えているデータへのアクセス（CRUD）や、ビジネス機能の呼び出しを行う。

③ファイル連携
以下のようなプロトコル、機能、製品を利用して、ファイルを転送する。
・FTP（File Transfer Protocol）プロトコルでファイルを転送する。
・WebDAV（HTTP（S）プロトコル）を利用してファイルを転送する。
・HULFT（ファイル連携ミドルウェアの有償の製品）を利用してファイルを転送する。

④メッセージ連携
データや処理要求などを「メッセージ」として交換することで、
分散システム上で動作する複数のアプリケーションを非同期に連携させる。
非同期のため、一方のアプリケーションの実行状況によって
他方のアプリケーションの実行状況やユーザーの操作が束縛されない。
【ふくだ学習録】サブスクリプション part5【46日目】

# ふくだ学習録とは？
ふくだが学習したことの備忘録。
目に見える形で残すことによってやる気を出す個人的な作戦です。
他人に見せるように書いているわけではないので、すごく読みにくいです。

### 読了した本
[データベースエンジニア養成読本 [DBを自由自在に活用するための知識とノウハウ満載!]](https://www.amazon.co.jp/dp/4774158062/ref=cm_sw_r_tw_dp_U_x_FPphEbQFPPQRM )
[ゼロから作るDeepLearning](https://www.amazon.co.jp/dp/4873117585/ref=cm_sw_r_tw_dp_U_x_y3UiEbF3P6EG7)
[PHPフレームワーク CakePHP 3入門](https://www.amazon.co.jp/dp/4798048577/ref=cm_sw_r_tw_dp_U_x_.YIjEbS1K4JY2)
[SQLアンチパターン](https://www.amazon.co.jp/dp/4873115892/ref=cm_sw_r_tw_dp_U_x_XlokEbJQK2N7P )
[Docker入門](https://www.amazon.co.jp/dp/480071091X/ref=cm_sw_r_tw_dp_U_x_xVlmEbN8ZTHZK)
[PHPフレームワーク Laravel入門](https://www.amazon.co.jp/dp/4798052582/ref=cm_sw_r_tw_dp_U_x_zzeyEbJBSQ5Y0 )


### 今読んでいる本

[サブスクリプション](https://www.amazon.co.jp/dp/B07HY1TND9/ref=cm_sw_r_tw_dp_U_x_lp4NEb3EEGD7Y)

# 第11章 マーケティング 4つのPが変わった
これまでマーケティングで重要だとされていた4つのPは下記。

- **製品（Product）**　人々が望むような商品を作り、パッケージ化すること
- **価格（Price）**　競争力があり、自社と顧客の両方にとって合理的な価格設定
- **プロモーション（Promotion）**　魅力的なチャネルで商品を宣伝すること
- **場所（Place）**　便利で人を惹きつける場所に商品を流通させること

サブスクリプション・エコノミーとなることで、最初のP（製品）がS（サービス）へと変わる。
それに伴い、残りの3つも変わっていく。

### 場所
流通チャネルに対してもウィンになるようなモデルを構築することが重要。
サブスクリプションモデルとすることは、仲卸業者から良い見方をさればい場合がある。（顧客と直接1:1でつながろうとするから）
流通チャネルに対してもウィンとなるようなモデルを築くことで、ウィンウィンウィンの関係を気づけるようにする。

### プロモーション
これまでのマーケティングは、プル（大がかりの広告）とプッシュ（手数料、マーケティング開発資金、チャネルリベート）の組み合わせで行われてきた。サブスクリプションモデルとなった場合、これらの組み合わせの考え方が変わってくる。理由としては、ブランド価値の作り方が、広告によるプロモーションから製品を通しての体験へと移行していくから。

では、どうすれば顧客に製品を体験してもらえるのか？　それにはストーリーテリングが重要になってくる。
ズオラではスリールームズという3つのメンタルモデルで考えている。
**①製品の物語（それはいかにニーズに応えるのか）**
**②市場の物語（必要としているのは誰か）**
**③サービスとユーザーを広いソーシャルな空間で結びつける物語（それはなぜ求められるのか）**

プロモーション（特にここでは広告と同意義）では、③を伝える役目を担ってもらう。

### 価格
もっとも難しい部分。何故ならば製品（Product）ではなく、体験（Service）に対して価格設定を行わなければならないから。

プライシングとパッケージングを考える必要がある。

まずプライシングに関しては、先述したように製品にではなく体験ベースで行わなくてはならない。
基本料金と従量課金制の両方を取り入れるとうまく設定しやすくなることが多い。

次にパッケージングに関しては、基本プランに対して追加購入で機能を足せるようなプランにすることが多い。
ただし、基本プラン（無料プラン）に全ユーザーの７０％ほどが滞在している状態が続いているのは、ビジネス的にいい兆候ではないと言われている。

# 第12章 営業 8つの新しい成長戦略
営業方法として重要なのは、顧客に対して「教える 」という行為。
「我が社にはあなたと同じようなお客様がたくさんいらっしゃいます。サービスについて話す前に、あなたと同じ業界の他の企業が実行していることから学んだベンチマークとインサイトに関してお話しさせてください。」ということを伝えることによって、より対等により効果的な営業ができる。（相互メリットが生まれる）

# 第15章 組織にサブスクリプション文化を根付かせる
サブスクリプション文化を根付かせるには、縦割り文化をやめるべき。
なぜなら縦割り文化は、製品（製品ライン）中心の考え方の時の最適解であり、中心を顧客に変えた場合は違う組織体験が求められるようになるから。

### PADREとは
ズオラが用いている、会社全体の可視化を行うための8つのサブシステム。それぞれの中心に顧客が置かれている。

#### ①パイプライン(Pipeline)
パイプライン（ポジションとも呼ぶ）を管理するためのサブシステム。
このサブシステムのゴールは、市場での認知を高め、それを需要に繋げること。

#### ②獲得(Acquire)
顧客の獲得をゴールとするサブシステム。カスタマー・ジャーニーの管理もこのシステムの仕事。

#### ③導入（Deploy）
顧客の事業をいかに迅速かつ効率的に導入できるか、という部分を管理するサブシステム。

#### ④利用（Run）
顧客がそのサービスを長期にわたって利用してもらうために、利用されている機能を判断（精査）するためのサブシステム。

#### ⑤拡大（Expand）
利用拡大のためにより顧客に使ってもらうための機能を考えたりするためのサブシステム。

#### ⑥人材（People）　⑦製品（Product）　⑧資金（Money）
人材育成、製品を開発するための環境、成長させるための賢いお金の使い方


# 今日の一言
読み終わった！オモロー
【システム設計】システム開発の際に用意すべき環境

システム開発を実施するにあたり、
最低でも3つの環境（開発、ST、本番）を用意することが望ましい。
大規模かつ長期間運用するようなシステムでは、4～5種類程度作成する。

- 開発サーバ（開発環境）
→開発者が開発・単体テストを行う環境。

- 検証環境（テスト環境）
→単体テストが完了し、結合テストを行うための環境。

- ステージングサーバ（ST環境、OT環境、デモ環境）
→ステージングには、結合テストが完了した機能がデプロイされる。
要件を満たしているか、実際の運用をするにあたって支障がないかなどを確認・レビューする。

- リハーサル環境（保守環境）
→本番と全く同じ構成の環境を用意して、本番と同じ状況の環境。

- プロダクションサーバ（本番環境）
→実際のユーザ（顧客）がシステムを利用する本番環境。
複数ドキュメント(document)以下のサブコレクション(subcollection)を一度に取得して orderBy をかけたい

## 概要
Cloud Firestore の、複数ドキュメント(document)にぶら下がる サブコレクション(subcollection)以下を
一度に取得して、sortNumの値で orderBy をかけたい。

```
collection/
├── document1
│   └── subcollection
│       ├── documentA
│       │   └── sortNum: 4 (数字)
│       └── documentB
│           └── sortNum: 2 (数字)
└── document2
    └── subcollection
        ├── documentC
        │   └── sortNum: 1 (数字)
        └── documentD
            └── sortNum: 3 (数字)
```


- こんなことはできない例  
https://firebase.google.com/docs/reference/js/firebase.firestore.CollectionReference#doc
を 読む限り、doc メソッド には ワイルドカード機能はないので、以下のようなことはできません。

```
const db = firebase.firestore()
const ref = db.collection('collection')

ref
  .doc('*') // こんなことはできない
  .collection('subcollection')
  .orderBy('sortNum', 'desc')
  .limit(3)
  .get()
  .then((snapshot) => {
  ...
```

## 方法
collectionGroup を使えば、同名の Collection以下 を一度に取得することができます。

```
const db = firebase.firestore()

db.collectionGroup('subcollection')
  .orderBy('sortNum', 'desc')
  .limit(3)
  .then((snapshot) => {
  ...
```

しかしこれだけでは、orderByを使おうとすると 以下エラーが出力されます。

```
FirebaseError: The query requires a COLLECTION_GROUP_DESC index for collection content and field sortNum. You can create it here: https://console.firebase.google.com/...
```

要するに、Collection group スコープ は 自動的に index が作られないからです。
Firebase コンソール の インデックス画面を見ると以下のようになっています。
<img width="1062" alt="スクリーンショット 2020-04-29 21.00.58.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/111658/cf9db140-fdc3-709e-6cfd-eb819ce14f0c.png">

1. 除外を追加 へ
<img width="946" alt="スクリーンショット 2020-04-29 21.04.14.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/111658/7be8ab63-bd92-e9ab-67e7-4273a8ac0efc.png">

1. indexを設定したい コレクション ID と フィールドのパス を入力
コレクション グループ の チェックボックス をオン
<img width="729" alt="スクリーンショット 2020-04-29 21.02.33.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/111658/ce795dcf-0121-eadc-5c3f-4991a1eaba50.png">

1. 必要なものを選んで有効にします
<img width="733" alt="スクリーンショット 2020-04-29 21.03.40.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/111658/31388d35-bce3-0734-96db-7b3168c8fdfe.png">

1. しばらくすると index設定が完了します。
「除外」という名前はややこしいですが、要するに 自動インデックス設定 に上書きをする感じです。
コレクション の チェックボックスは オン にしていないので、Collection scopeの設定上書きは
されないわけです。
<img width="983" alt="スクリーンショット 2020-04-29 21.15.16.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/111658/f4c786c6-a678-6ce0-2a6d-a66414294c46.png">


orderBy が desc で、limit を 3 にしているので
これで 以下の順にデータが並んだ状態で 計3つを取得できます。

```
documentA
documentD
documentB
```
【在宅勤務を快適に】透過ウィンドウのススメ(Windows編)

皆様はじめまして。あきたと申します。

愛知県にあるソフトウェア企業に新卒入社した駆け出しプログラマです。Qiita初投稿ですので暖かく見守って下さると幸いです。

## はじめに

在宅勤務の方も、そうでない方も皆様いかがお過ごしでしょうか。

**家に居る時間が長くなる分、パソコンに向き合う時間をいかに生産性高く過ごす**のはエンジニアにとって重要なテーマだと思います。

そこで最近、私が見つけたのはこの記事です。

>[アニメ見ながらプログラミングするために透過ウィンドウを実装する方法](https://paiza.hatenablog.com/entry/2019/07/12/%E3%82%A2%E3%83%8B%E3%83%A1%E8%A6%8B%E3%81%AA%E3%81%8C%E3%82%89%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0%E3%81%99%E3%82%8B%E3%81%9F%E3%82%81%E3%81%AB%E9%80%8F%E9%81%8E%E3%82%A6)

この記事に書いてあることを試すと、この様な事が出来ます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411902/f60da491-48ea-8424-9894-9537e05a560f.png)
そうです。なんと透過ウィンドウを使用する事で、一つのモニターを画面分割する事無く**アニメを観ながら作業が出来る**のです！！

その中でも今回はこのWindows対応の[NaiNai](https://forest.watch.impress.co.jp/library/software/nainai/)
を使い、透過ウィンドウを使用してみました。

※win10についての対応は言及されていませんでしたが、こちらでは問題なく使えました。

## 使い方

[ダウンロードサイト](https://forest.watch.impress.co.jp/library/software/nainai/)
より、任意のフォルダにダウンロードします。

起動するとこんな感じ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411902/199d9665-5f80-4b98-37ce-d8a19ba0fda2.png)

【設定】ボタンを押すと現在開かれているウィンドウ一覧が上に表示されます。(下には透過ウィンドウとして登録されている設定が表示されます。)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411902/774f8f6a-4b5a-4d35-2c5f-1cfd86436f8d.png)

透過させたいウィンドウをクリックし、追加ボタンを押します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411902/d806feea-921d-f562-27df-275e1981acba.png)

すると登録画面はこの様な形になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411902/7f43a0a8-7beb-bbd7-24c8-152e5e649c10.png)
ウィンドウタイトル名を入れ、条件を「4パターン」から選ぶことで、自由に透過ウィンドウの範囲を設定する事が出来ます。

不透明度の「アクティブ時」の数字を変えることで、透過度合いを設定できます。

その他にも、左にある項目で色々と設定できる様なので、試行錯誤して試してみると良いと思います。

## Q＆A

### Q.透過したいサイト全てに登録する必要がある？

A.いいえ。
「指定したサイトURLの含まれたタブ」であれば透過されます。

→5月5日追記
「指定されたサイトURLの含まれたタブ」でも透過されない場合があります。
どういうパターンの時に透過されないのかは未検証です。

### Q.ウィンドウを複数重ねて表示しても透過される？

A.透過されます。

例えば下図の画像の場合、

- 一段目:Eclipse(透過率80%)
- 二段目:Qiita(透過率80%)
- 三段目:AmazonPrime

という条件でスクショしています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411902/33899884-cd53-ed2b-a3af-a42331398a16.png)
二段重ねで動画を観ることはそこまで無いとは思いますが、このような事も可能です。

## おわりに

Qiita初投稿なので、何か間違っている部分があったらご指摘いただけると幸いです。

少なくともこの記事を書いている間にアニメを3~4話観れたので、twitterやプログラミング等、片手間でアニメを観れる素晴らしいツールだと思います。
Emacsでファイル保存しながらiTerm2でコマンドを実行する

Terraformを勉強してる時に都度Emacsでコード書いて、iTermで実行というのが面倒だった。
そしてEmacsからコマンドを実行する方法を試したがコマンド実行中に他プロセスを実行するというのが上手にできなかったり絵文字が表示できなかったり不便だったのでAppleScriptとして別に別けた。
ネットワークアクセスが発生し時間がかかるコマンドと組み合わせると効果が高い。
今回は例として`terraform apply`を実行しているが、一回書けば応用が効くと思うのでオススメ。(多分引数として渡せばもっと便利かなあ)

## まずはAppleScriptの準備。
下記のscptファイルを任意の場所に保存する。

```iterm.scpt
tell application "iTerm"
	set _current_session to current session of current window
	tell _current_session
		# ここに操作したいことを書く
		write text "terraform apply"
	end tell
end tell
```

## init.elで関数定義
emacs-lispは単純で、あとは関数定義とキーマップに割り当てるだけ。

```init.el
(defun tf-apply ()
  "Save and terraform apply"
       (interactive)
       (save-buffer)
       (shell-command "osascript ~/.emacs.d/script/iterm.scpt"
(global-set-key (kbd "C-c u a") 'tf-apply)

```


(参考)
[iTermをAppleScriptで操作する](https://www.rasukarusan.com/entry/2019/04/06/224538)
BIシステム構築の要件定義・設計フェーズですること

#概要
自分の開発経験とBIシステム構築実践入門を読んだポイントを踏まえて記載します。

参考文献URL
https://www.shoeisha.co.jp/book/detail/9784798109312

#要件定義フェーズ
いわゆる業務システムと異なり、業務分析→システム化スコープの確定というプロセスが踏みづらい。BIシステム独自のやり方として以下の取り組みがあげられる。
###①管理ポイント／鮮度分析
管理ポイント：分析の切り口での粒度の異なる個々の項目のこと→ディメンションテーブルのカラムに相当
管理項目：ファクトテーブルの数値カラム

2次元の表に配置して、分析に必要とされる管理ポイントと管理項目の組み合わせを明確にするタスク

鮮度分析：補足するデータの断面、保持期限を定義する

###②ディメンショナルモデリング
管理ポイント分析の結果をもとにデータウェアハウスの概念的なスキーマを定義するタスク
ディメンションとは情報をみる切り口のことである。
②-1　ディメンションの定義
・管理ポイントをディメンションに分類し
・各ディメンションことに階層化する

###③ソースデータ分析
分析ニーズとして挙がっている項目が、業務系システムから取得可能かどうかを判定する。
たとえ取得だとしても時間などリアルタイム性が求められるものはリソースの負荷を考慮する。

###④シナリオ作成
ユーザのシナリオをつくることで要件を整理する。
④-1 アプリケーション概要
　・ターゲットユーザ
　・アプリの概要、目的
　・どのようなデータを見せるか
④-2 シナリオ作成
　「誰が」、「いつ」、「どこで」、「何のデータ」で整理する。

#設計フェーズ
クラスを CSV にデバインドする (C#)

# クラスを CSV にデバインドする (C#)

例えば

```csharp
public class Contract
{
    public int RowId { get; set; }
    public string Id { get; set; }
    public DateTime StartDate { get; set; }
    public DateTime? StopDate { get; set; }
    public int CustomerId { get; set; }
}
```

みたいなクラスがあって

```
RowId,Id,StartDate,StopDate,CustomerId
0,C0001,2020-03-01T00:00:00.0000000,2020-04-01T00:00:00.0000000,0
1,C0002,2020-03-01T00:00:00.0000000,,1
```

みたいな、CSV にデバインドしたいという話です. CSV 変換は今回は話題にしたくないので、`string.Join(",", v)` で片付くということにしましょう.

以下みたいなコードを書くと、`Save(stream, contracts);` でデバインドできます.

```csharp
public static string[] Debind<T>(Dictionary<string, int> headerMap, T obj)
    where T : class
{
    var result = new string[headerMap.Count];
    foreach (var p in typeof(T).GetProperties())
    {
        if (!headerMap.ContainsKey(p.Name)) continue;
        var i = headerMap[p.Name];

        var type = Nullable.GetUnderlyingType(p.PropertyType);
        if (type == null)
        {
            type = p.PropertyType;
        }
        else
        {
            if (p.GetValue(obj) == null)
            {
                result[i] = "";
                continue;
            }
        }

        if (type == typeof(DateTime))
        {
            result[i] = ((DateTime)p.GetValue(obj)).ToString("o");
        }
        else
        {
            result[i] = (p.GetValue(obj) ?? "").ToString();
        }
    }
    return result;
}

public static void Save<T>(Stream stream, IEnumerable<T> content, Encoding encoding = null)
    where T : class
{
    if (encoding == null) encoding = Encoding.UTF8;
    using (var writer = new StreamWriter(stream, encoding))
    {
        var header = typeof(T).GetProperties().Select(e => e.Name).ToList();
        var headerMap = new Dictionary<string, int>();
        for (var i = 0; i < header.Count; i++)
        {
            headerMap[header[i]] = i;
        }
        writer.WriteLine(string.Join(",", header));

        foreach (var o in content)
        {
            writer.WriteLine(string.Join(",", Debind(headerMap, o)));
        }
    }
}
```

なお、プロパティではなくフィールドにしたい場合には `.GetProperties()` が `.GetFields()` になり、`.PropertyType` が `.FieldType` になります.

また、class ではなく struct にしたい場合には、`where T : class` が `where T : struct` になり、`p.GetValue(obj)` が `p.GetValueDirect(__makeref(obj)))` になります.

関連記事: [CSV をクラスにバインドする (C#)](https://qiita.com/c-yan/items/aa3025642b32201454c8)