OmniAICreator/Qiita-1.07M huggingface dataset:
CSVファイルへのログ書き込み（Python, C言語）

業務上、連続でデータを取る→CSVファイルに書き込んでログを取る、という作業が多かったので
備忘録的にまとめてみました。

# 環境
OS：Raspbian Buster 10
使用ソフト：Code-OSS(headmelted) 1.44.0

# プログラムの流れ
プログラムと同じ階層に空のCSVファイルを作成し、そこにログを書き込んでいきます。
『西暦/月/日 時:分:秒,(データ1),(データ2)...』という風に、日付を最初に付けて、
記録したいデータを後ろにカンマ区切りで羅列していきます。

# Python

```python:csvWrite.py
import csv
import datetime

n = 0
try:
    with open('logger.csv', 'a') as f:
        # ログを10回書き込む
        while n < 10:
            # 日付・時刻
            now = datetime.datetime.now()
            nowTime = '{0:%Y/%m/%d %H:%M:%S}'.format(now)

            # データ群
            data1 = 'data1'
            data2 = 'data2'
            data3 = 'data3'

            writeStr = nowTime + ',' + data1 + ',' + data2 + ',' +data3
            writeStr = writeStr.split(',')

            # 書き込んだ文字列の確認
            # 例：'2020/03/06 15:00:00','data1','data2','data3'
            print('writeStr = %s' % writeStr)

            writer = csv.writer(f, quoting=csv.QUOTE_NONNUMERIC)
            writer.writerow(writeStr)

            n += 1

except FileNotFoundError as e:
    print(e)
except csv.Error as e:
    print(e)
```

# C言語

```C:csvWrite.c
#include <stdio.h>
#include <time.h>

FILE *fp;
char *fname = "logger.csv";
struct tm tm;

int main(void)
{
    struct tm tm;
    time_t t;

    // データ群
    char data1[] = "data1";
    char data2[] = "data2";
    char data3[] = "data3";

    // ファイルを開く（無い場合は自動で作成される）
    fp = fopen(fname, "a");
    if(fp == NULL){
        printf("file can't open¥n");
        return -1;
    }

    // ログを10回書き込む
    for(int n = 0; n < 10; n++){
        // 日付・時刻
        t = time(NULL);
        localtime_r(&t, &tm);

        fprintf(fp, "\"%04d/%02d/%02d %02d:%02d:%02d\",",
            tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
            tm.tm_hour, tm.tm_min, tm.tm_sec);
        fprintf(fp, "\"%s\",\"%s\",\"%s\"\n", data1, data2, data3);

        // 書き込んだ文字列の確認
        // 例："2020/03/06 15:00:00","data1","data2","data3"
        printf("writeStr = \"%04d/%02d/%02d %02d:%02d:%02d\",\"%s\",\"%s\",\"%s\"\n", 
            tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
            tm.tm_hour, tm.tm_min, tm.tm_sec, data1, data2, data3);
    }

    fclose(fp);
    return 0;
}
```

# まとめ
IoT開発をしているとサーバーにログを残したりするのですが、インターネットに接続できなかった場合に
その間のログが消えた時の絶望感がすごいので、SDカードにCSVファイルを作ってこうしてこまめに保存できたらいいなと思います。
CodableでdecodeしたときにJSON側のパラメータが足りなくても落ちないようにする

追記-----------------------------
そもそもこんな面倒なことしなくても、
```
var path: String? = ""
```
にすれば落ちなくなるそうです。ご指摘ありがとうございます！
追記ここまで------------------------

何言ってるかよくわからないかもしれませんが。
自分で何やってるかもよくわかってないのでなんとなく書きます。

Codableを使ってJSONをデコードするときってこんなふうに書きますよね。↓

```Swift

struct BoardStruct: Codable {
    var name: String = ""
    var path: String = ""
}

func() {
    let boardStruct = try! JSONDecoder().decode(BoardStruct.self, from: data)
}
```

でも、これだとJSONに指定のパラメータ、例えばpathがJSONに入ってこないとき、try!で落ちてしまうのです。
で、それを回避するためにやったこと。
structを以下のように書き換える。

```Swift
struct BoardStruct: Codable {
    var name: String = ""
    var path: String = ""

    enum CodingKeys: String, CodingKey {
        case name
        case path
    }

    init(name: String, path: String) {
        self.name = name
        self.path = path
    }

    init(from decoder: Decoder) throws {
        let root = try decoder.container(keyedBy: CodingKeys.self)
        self.name = try root.decode(String.self, forKey: .name)
        // ↓ここでpathが入って来なくても落ちないようにする
        if let path = try? root.decode(String.self, forKey: .path) {
            self.path = path
        }
    }
}
```

以上です。
dcoderのinitを書くとデフォルトのinit（?）がなくなってしまうっぽいので自分で書き足しました。
Pythonはバックスラッシュの後ろにスペースが入っているとエラーになる


きっと常識的な知識なのだろうけど、やらかしてしまったから自戒の意味でもこの記事を認める。

#結論から
- バックスペースの後ろに空白をおいてはダメだぞ
- そもそもバックスペースで複数行に分ける記述は推薦されていないぞ

#ことの背景
他人が管理していたgitプロジェクトの、他人が書いたコードの修正をおこなった。
修正はSQL文のWhereを追加するという簡単なモノ。

以下のようにコードを修正した。

```python:修正前
    ...
    sql = "SELECT "\
          "  user.id ,"\
          "  user.name "\
          "FROM "\
          "  user "\
          "WHERE "\
          "  user.age >= 20 "\
          "  AND user.sex == 'M' "
    c.execute(sql)
    ...
```

```python:修正後

    ...
    sql = "SELECT "\
          "  user.id ,"\
          "  user.name "\
          "FROM "\
          "  user "\
          "WHERE "\
          "  user.age >= 20 "\
          "  user.age <= 60 "\␣␣␣␣␣␣␣␣
          "  AND user.sex == 'M' "
    c.execute(sql)
    ...
```

gitにコードを上げて、数週間後。
レビューも検証も通って本番への適用も終わって忘れている頃。
お客様からなんかSQLを引いているところが動いていないようだ言われた。
調査したら、こんなエラーが。

```
SyntaxError: EOL while scanning string literal
```

まじかよ。

#何でこんなことが起こったのか
実はこのコード、本番環境用とテスト環境用で管理が分かれているファイルだった。←この構造自体よくない
とはいえ、私の管理しているコードじゃないので、言われた通りにテスト環境用のコードを修正。
開発者テストが通った後に、修正したコードを本番用ソースにコピペして提出。
その時に末尾に空白が混じってしまったようだ。

修正部分は総合検証が実施され、合格。このとき使用されていたのもテスト用コード。
本番用のコードにしか空白が入っていないので未検出のままリリース。
そしてお客様が使ったときに発生してしまった。

#対策
組織的な開発フローで言いたいことはいくつもあるんだが、

とりあえず個人的にできる対策として以下を実施
*コピペ作業するときは空白をエディタに表示させるようにして作業するようにしました。
*作業後に空白無視せず比較を行ってチェックするようにしました。

なんともやるせない…
slackにgithubの通知が欲しいでヤンス

# はじめに

おはこんばんにちは！syomaです。
表題の通りですが、slackにて、githubでのマージやコミットなんかを通知できるようにします！

手順としましては

1. slackにgithubをインストール
2. slack内でgithub認証をする
3. slack内でリポジトリの登録

簡単でございます。では順に参りましょう！

# 1. slackにgithubをインストール

slack.github.com にアクセスし、「Add to Slack」をクリックします。

# 2. slack内でgithub認証をする

通知を受けたいslackチャンネルで下記の文を打ちましょう。

```slack:slack
/github signin
```

認証完了するとslackに下記の文が表示されます。

✅Success! @slack名 is now connected to @github名

# 3. slack内でリポジトリの登録

通知を受けたいチャンネルで、リポジトリを登録するためのコマンドを送信するのじゃっ

```slack:slack
/github subscribe owner/repository
```

owner/repository は 自分のリポジトリにしましょう！

```slack:slack
Subscribed to owner/repository
```
という一文が返ってきたら成功です！

もし、この時点で

```slack:slack
Either the app isn't installed on your repository or the repository does not exist. Install it to proceed.
```

という一文が返ってきた場合

<img width="696" alt="スクリーンショット 2020-03-06 18.13.08.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/586820/b22ec91a-42f1-08f6-cc28-e2a1679fbce3.png">

Install GitHub Appをクリックし、下の画像のように「All repositories」にチェックを入れSaveしましょう。

<img width="1440" alt="スクリーンショット 2020-03-06 18.13.31.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/586820/dd0422cc-1a1d-55e0-41d1-c0b71ea6a3aa.png">

そして、

```slack:slack
/github subscribe owner/repository
```

owner/repository は 自分のリポジトリにしましょう！

以上です！

# 最後に

twitterやっておりますので是非フォローよろしくお願いいたします。
https://twitter.com/syomabusiness
youtubeも始める予定です。
製造業はオワコンと嘆く前に自身でAI取り入れ方を考えてみる


0.この記事の内容
============
製造業で特に生産技術の方が、個人単位でどのようにAI/機械学習に取り組むべきかを考えてみました。勉強中のため公開するほどのソースもありませんが、御意見御指摘等頂けたら幸いです。
1.読んでほしい人
============
・プログラム経験有無にかかわらず製造業に携わる方
・自身の会社がAIに取り組んでおらずオワコンを感じている方
・製造業×AIの達人の方(ご指導頂くため笑)
2.製造業におけるAI導入のジレンマ
============
企業によってAI開発に力を掛けている企業とかけてない企業に分かれますが、かけてない企業にとっては下記が悩みと思います。
○コスト対効果で即効性が出にくい
・分析手法が確立されきった分野であり、対応方法は人でまかなえている
　各項目の相関確認、lot毎のBoxプロットなどマクロ×人で時間をかけて分析している。
・QCDが追及され目先の業務に追われるため、担当がAI導入を検討する時間がない
・SW企業に依頼するにしてもコストが大きく、会社の上の方が理解できず、期間での費用対効果を厳しく求められる
○成功例の共有文化がない
・会社のノウハウに該当するため、情報具体例の共有がしにくい。
　だいたいブラックボックス&売り物になる。
⇒会社として力を入れてない企業にとっては参入障壁が高い分野と思います。業務とは別に個人開発をしはじめる事により、他のIT関係の方と共有や意見交流が出来ると考えました。
3.製造業で機械学習を取り入れるには？
============
三菱UFJ銀行傘下の三菱UFJリサーチ＆コンサルティング発表の「ものづくり分野における人工知能技術の活用に関する調査報告書」によると、製造業におけるAI活用は下記のような分野が考えます。
![l_mm_mono_ka_03.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/448643/2827a614-d981-eb7b-afbb-e3a142a2127e.jpeg)
https://www.meti.go.jp/meti_lib/report/H29FY/000119.pdf
このうち、個人単位で一番考えやすい「歩留り」について、ゼロからいかにAIを取り入れるかを考えてみようと思います。
###Q.なぜ歩留り改善なのか？
⇒A.一番データ環境が揃っており入手しやすく導入しやすいから。また自分の業務に一番近い項目だから。
3.ものづくり歩留りにおけるAI活用方法
============
歩留りとは、製造工程でinputした数に対するoutput(良品数)です。
プロセス工程で製造したものを検査工程で検査し良品のみを出荷します。歩留まりが悪いほど損失を生むため、生産技術の人は歩留まり向上に力を費やします。
今回は検査結果の分析とプロセスへのフィードバックを機械学習を使用する事により、分析時間の短縮を試みるものです。
4.検討内容
============
###ログ整理⇒不良itemをランダムフォレストで推測⇒重要度の高いパラメータ抽出までを行うアルゴリズムをpythonで作成し使ってみる。
4-1.各工程のlogを入手し取り込む
    S/N等で各プロセスと紐づいているデータを出来るだけ一つのテーブルにまとめる。
4-2.空白のデータを除去する。(dropna使用)
4-3.文字列を数値化し分析しやすくする。(OneHotEncoder使用)
4-4.不良が発生しているItemをy、その他のItemをXとし、Xを正規化する。
4-5.機械学習させる。（ランダムフォレスト使用）
4-6.答えと推測値が近いか確認する。
　近い⇒重要度の高いパラメータあり。
　異なる⇒logに重要度の高いパラメータが無い可能性有
4-7.重要度の高いパラメータを確認する。
    パラメータからプロセスのどこが悪いかを推測しフィードバックをかける。
5.個人運用後のメリット
============
###改善効果見込み：分析にかかる時間▼1‐2日
従来は層別に可視化し人が判断していたため、分析時間に数時間-数日取られていたが準備さえできていれば一瞬で答えがでるため、答えに向かった資料作製のみを準備すればいい事が分かりました。どのくらい役に立ったかの評価はこれからです。課題としては、結び付けられるデータに限りがあることですが、これは地道に活動を進めてデータ紐づけの重要性を多方面に認識してもらうしかないと思います。
個人としてデータ詳細を見る前に、「とりあえず機械学習で分析してあたりをつけてみる」をしばらく習慣化したいと思います。
FlutterでAPI29での実行時、image_pickerがpermissionエラーになる問題の対処法

# エラー内容
FlutterでAPI29(Android Q)でのアプリ実行時、image_pickerやfile_pickerといったパッケージでストレージにアクセスできない。

エラーメッセージは以下のようなものでした。
>Unable to decode stream: java.io.FileNotFoundException: /storage/emulated/0/Pictures/image.jpg: open failed: EACCES (Permission denied)


# 環境
Flutter: version 1.14.6, channel beta
Android: API29 (Android Q)
Gradle: 5.6.2 (android/gradle/wrapper/gradle-wrapper.propertiesに記載)
Gradle plugin: 3.5.3(android/build.gradleに記載)
image_picker: 0.6.3+4

# 対処法
app/src/main/AndroidManifest.xmlに追記すればOKです。

```xml:AndroidManifest.xml
<application android:requestLegacyExternalStorage="true" ... >
        ...
</application>
```

ただし、requestLegacyExternalStorageはAPIレベル29以降の機能のため、29以降のバージョンを指定する必要があります。Gradleのバージョンによってはアップグレードが必要かもしれません([buildToolsリリースノート](https://developer.android.com/studio/releases/build-tools?hl=ja))。

```gradle:app/build.gradle
android {
    compileSdkVersion 29
    buildToolsVersion "29.0.0"
...

defaultConfig {
...
        minSdkVersion 21
        targetSdkVersion 29
...
}
```

AndroidManifest.xmlを編集した後は次のコマンドでbuildフォルダを削除してビルドし直す必要があります。

```bash

flutter clean
```

# 備考
APIレベル29以降をターゲットとするアプリは、外部ストレージへのアクセス権限が以前のAPIレベルと異なっているようです([対象範囲別ストレージ アクセスを管理する](https://developer.android.com/training/data-storage/files/external-scoped?hl=ja))。
今回のケースでは、対象範囲別ストレージ機能を無効にすればエラーを解消できました([image_pickerのpermissionエラーについてのissue](https://github.com/flutter/flutter/issues/29422))。

また、紛らわしいのですが、AndroidManifest.xmlにてREAD_EXTERNAL_STORAGEについて追記する必要はありませんでした。[simple_permissions](https://pub.dev/packages/simple_permissions#-installing-tab-)、[permission_handler](https://pub.dev/packages/permission_handler#-readme-tab-)といったpermissionを扱うためのパッケージも不要でした(外部ストレージ読み取りの権限が付与されていてもエラーが解消されなかった)。

# まとめ
AndroidManifest.xmlに追記、アプリのターゲットバージョンを指定することでエラーが解消できました。FlutterとAPIレベル29と外部ストレージの組み合わせ特有の問題で、原因を探るのに苦労したので記事にしておきます。
Flutterのpermissionについては理解が不十分なところがあり、本記事の内容にご意見、ご質問などがあればコメントいただけると助かります。
centos 7 ssh更新 (写経 and 実践) ソースビルド

## ssh version 更新 ##

centos 7のyumでupdateしても、最新のversionのopensshにならないです。

```
ssh -V
#結果--
OpenSSH_7.4p1, OpenSSL 1.0.2k-fips  26 Jan 2017
--#
```

最新に更新してみたいです。
https://adan.jp.net/blog/programing/1741
をやったけど、、、
askpass??で失敗。


以下の手順でやりました。ほぼ記載通りの写経ですが、

# Step 1. OpenSSLを最新にする。
https://qiita.com/shadowhat/items/68fd55b532c04b13acf5

## DL & INSTALL

```
cd /usr/local/src
wget https://www.openssl.org/source/openssl-1.1.1d.tar.gz
tar xvzf openssl-1.1.1d.tar.gz 
cd openssl-1.1.1d
./config -fPIC shared
make
make install
```

## ランブラリ参照

```
vim /etc/ld.so.conf
```

```
#以下を追加
/usr/local/lib64 #←これを追加
include ld.so.conf.d/*.conf
#load
ldconfig
```

##確認

```
ldconfig -p | grep ssl
#結果---
libxmlsec1-openssl.so.1 (libc6,x86-64) => /lib64/libxmlsec1-openssl.so.1
libxmlsec1-openssl.so (libc6,x86-64) => /lib64/libxmlsec1-openssl.so
libssl3.so (libc6,x86-64) => /lib64/libssl3.so
libssl.so.10 (libc6,x86-64) => /lib64/libssl.so.10
libssl.so.1.1 (libc6,x86-64) => /usr/local/lib64/libssl.so.1.1
libssl.so (libc6,x86-64) => /usr/local/lib64/libssl.so
libssl.so (libc6,x86-64) => /lib64/libssl.so
libevent_openssl-2.0.so.5 (libc6,x86-64) => /lib64/libevent_openssl-2.0.so.5
---#
/usr/local/bin/openssl version
#結果---
OpenSSL 1.1.1d  10 Sep 2019
---#
```

# Step 2. OpenSSHを最新にする。

https://qiita.com/shadowhat/items/434fe12ab2eadbf818fe

## INSTALL

```
#あらかじめキーファイルの権限を変更
chmod 600 /etc/ssh/ssh_host_*
cd /usr/local/src
wget http://ftp.jaist.ac.jp/pub/OpenBSD/OpenSSH/portable/openssh-8.2p1.tar.gz
tar xvzf openssh-8.2p1.tar.gz 
cd openssh-8.2p1
./configure --with-ssl-dir=/usr/local/lib64
make
make install
```
##確認

```
sshd -V
#結果--
unknown option -- V
OpenSSH_8.2p1, OpenSSL 1.1.1d  10 Sep 2019
--#
```

# Step 3. sshd_config設定

```
vim /etc/ssh/sshd_config
```

```
#sshd_configのGSSAPI設定を無効にします
GSSAPIAuthentication yes
GSSAPICleanupCredentials no
↓
#GSSAPIAuthentication yes
#GSSAPICleanupCredentials no

#security強化,以下を追加
KexAlgorithms ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521, diffie-hellman-group14-sha1,diffie-hellman-group-exchange-sha256
Ciphers aes128-ctr,aes192-ctr,aes256-ctr
MACs hmac-sha2-256,hmac-sha2-512,hmac-sha1

#security強化,特定ユーザのみ許可
AllowUsers user1, user2
```
Vue.js + TypeScriptで簡単なToDoリストをつくってみた

#概要
フロント初心者がVue.js、TypeScriptの勉強のために簡単なToDolistを作ってみました。
今回作成にあたり、[Vue.js と TypeScript で Todo リストアプリを実装した](https://qiita.com/Nossa/items/b2e38bea4bda87a1de12)を参考にしました。

#環境設定
####Node.jsをインストール
npmコマンドを使用するために、まずはNode.jsをインストールします。
[Node.jsのダウンロード](https://nodejs.org/en/download/)

####コマンドラインインタフェースをインストール
Vue.jsを使う環境を準備するためのコマンドラインインタフェースをインストールします。
`npm install -g @vue/cli`

####プロジェクトの生成
`vue create 【任意のプロジェクト名】`

####プロジェクトの環境を設定
①　Manually select featuresを選択
②　TypeScriptを選択する
※spaceで選択、aで全選択

#実装
参考にしたサイトのポイントだけピックアップします。
###クラス構成
1.src/registered.tsクラスにToDoリストのデータの処理について実装
2.src/task.tsはモデルのような役割を実装
3.components/ToDoList.vueにtemplate(html)とtypescriptの処理を記載
→ここで、registered.tsのメソッドを使用する

###データの保存
ローカルストレージに保存するようにする。
Storableインターフェースに依存するようにし、localStorageは DI する。

```typescript:registered.ts
constructor(
    private storage: Storable = localStorage
  ) { }
```
【reset.css】chromeのデベロッパーツールで表示される”user agent stylesheet”によりCSSが適用されない状態を解消する。

#問題
 模写コーディングをしていると、CSSが適用されないことがあり、chromeのデベロッパーツールで確認すると、”user agent stylesheet”によって打ち消されていることがわかった。
#原因
　”user agent stylesheet”はブラウザ毎のデフォルトのCSS設定である。
#対策
　このデフォルトのCSS設定を解消し、まっさらな状態で模写コーディングを始めるには、リセットCSSというものによって、そのデフォルトのCSSを打ち消して、表示を揃えてからコーディングを進める必要がある。
　またreset.cssには種類があり状況によって選択する必要がある。
　
***すべてリセットするタイプ***
>・Eric Mayer’s Reset CSS 2.0
>・HTML5 Doctor Reset CSS

***デフォルトのスタイルが当てられるタイプ***
>
>・Normalize.css
>・ress
>・Reboot.css


また、2019年で人気なのは
>
>- [Eric Meyer’s “Reset CSS” 2.0](https://meyerweb.com/eric/tools/css/reset/)
>- [HTML5 Doctor Reset CSS](http://html5doctor.com/html-5-reset-stylesheet/) 

であるが、***Eric Meyer’s “Reset CSS” 2.0***はHTML5に対応していない為、***HTML5 Doctor Reset CSS***を使うほうが良いとのことだった。

# 参考文献

この記事は以下の情報を参考にして執筆しました。

- [2019年版！おすすめのリセットCSSまとめ](https://webdesign-trends.net/entry/8137)
- [リセットCSS は何を使う？ まとめ８選と2019年版ランキング
](https://original-game.com/reset-css/)
rails5 has_many dependent: :delete_allでミスるとレコード全件消える

# なんかたまにテーブルのレコード全部消えると思って調べた。

たまに1テーブルのレコードが全件消える。いつも同じテーブルというわけではない。
もちろん、全レコード削除するようなコード書いてないのに消える。
怖いので調べた。

## ログ見てたら怪しいやついた。

```text
jiburi-web | Started GET "/kamikakushi" for 172.22.0.1 at 2020-03-06 17:09:18 +0900
jiburi-web | Cannot render console from 172.22.0.1! Allowed networks: 127.0.0.0/127.255.255.255, ::1
jiburi-web |   Chihiro Destroy (6.1ms)  DELETE FROM `chihiros`
jiburi-web |   ↳ app/models/chihiro.rb:3:in `<class:Luser>'
jiburi-web |   
jiburi-web | ArgumentError (The :dependent option must be one of [:destroy, :delete_all, :nullify, :restrict_with_error, :restrict_with_exception], but is :3):
```

 `DELETE FROM chihiros`しているやつがいた。
直後のエラーが怪しいので更に調査。

## 調べたら再現できた

```rb:chihiro.rb
class Chihiro < ApplicationRecord
  has_many :sens, dependent: :delete_all

  # :delete_allをミスして「:」を忘れ、delete_allにしてた。
  has_many :aburayas, dependent: delete_all
```

## 色々試した結果、わかったことは2つ。
1. `chihiros`テーブルにレコードが存在する状態で、上のようなソースになっていると、`DELETE FROM chihiros`が実行される。
2. 外部キーがきちんと貼られていれば、以下のような`ActiveRecord::InvalidForeignKey `が発生するのでレコードは消えない。

```text
jiburi-web | ActiveRecord::InvalidForeignKey (Mysql2::Error: Cannot delete or update a parent row: a foreign key constraint fails (`jiburi`.`chihiros`, CONSTRAINT `fk_rails_7f965bd3db` FOREIGN KEY (`aburaya_id`) REFERENCES `aburayas` (`id`))):
```

## なぜそうなるのかわからない

理由がわからなくて辛い。。
どこのソースを追えばいいのか。
が、ミスしっぱなしで放置しなければ本番環境で発生しなさそう?
페이스북 링크 공유 시 수정본 미리보기 나오게 하기

![fb-preview.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144058/0654a3bf-8b3d-0338-d9ed-f31329117ff6.png)

## TL;DR
보통 제가 작성한 포스팅을 그룹에 공유하기 전에, 담벼락에 나만보기로 공유해보고 셀프 점검을 합니다.
미리보기가 맘에 안 드는 경우 수정을 합니다.
수정 후 다시 담벼락에 공유를 해도 수정본이 적용이 안 됩니다. :cry:
> Q. 포스팅을 재작성해서 URL을 새로 따야하나요? 
> A. :white_check_mark: 아닙니다. **[공유 디버거](https://developers.facebook.com/tools/debug )** 를 사용하면 쉽게 해결 할 수 있습니다.

## 공유 디버거
최초 페이스북에 링크가 공유될 때, 페이스북은 링크의 정보를 캐싱해둡니다.
그렇기 때문에 수정본이 있더라도, 같은 링크를 재공유할때 캐싱된 정보가 사용됩니다.
그래서 캐싱된 정보를 지워주고, 재공유를 하면 수정본이 잘 반영 됩니다.

### 캐싱 정보 지우기
https://developers.facebook.com/tools/debug 
#### 1) 공유 디버거 > URL 입력 > 디버그
![fb01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144058/b230ce81-1ce8-e5c1-e829-9b880a799e42.png)

#### 2) 다시 스크랩 > 링크 미리 보기
![fb02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144058/e2ceed0f-7805-533b-1ff0-ec9c68c13436.png)

> :dizzy: 이제 수정본 미리보기를 페이스북에 노출할 수 있습니다.

## 참고 링크
- https://developers.facebook.com/tools/debug
- https://developers.facebook.com/docs/sharing/webmasters/images/
[Quick Start Guide] PostgreSQL 11 + Rails 6 (on CentOS 7)


### Install postgresql packages

- postgresql11.x86_64
- postgresql11-devel.x86_64
- postgresql11-libs.x86_64
- postgresql11-server.x86_64

```
# yum install https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm
# yum install postgresql11 postgresql11-devel postgresql11-libs postgresql11-server
```

### Set PATH

```
# echo 'export PATH=/usr/pgsql-11/bin:$PATH' >> /home/<your user>/.bash_profile
# echo 'export PATH=/usr/pgsql-11/bin:$PATH' >> $HOME/.bash_profile
# export PATH=/usr/pgsql-11/bin:$PATH
```

### Initialize database

```
# postgresql-11-setup initdb
```

### Edit /var/lib/pgsql/11/data/pg_hba.conf

```
# sed -i -E '/^#/!s/^/#/g' /var/lib/pgsql/11/data/pg_hba.conf
# echo "local   all   postgres                    trust" >> /var/lib/pgsql/11/data/pg_hba.conf
# echo "host    all   postgres    127.0.0.1/32    trust" >> /var/lib/pgsql/11/data/pg_hba.conf
```

### Start service

```
# systemctl enable postgresql-11.service
# systemctl start postgresql-11.service
```

### Create rails app

```
# su - <your user>
$ rails new <app name> --database=postgresql
```

### Edit config/database.yml

Add host, port, and username:

```
default: &default
  adapter: postgresql
  encoding: unicode
  # For details on connection pooling, see Rails configuration guide
  # https://guides.rubyonrails.org/configuring.html#database-pooling
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  host: 127.0.0.1
  port: 5432
  username: postgres
```

Comment out username and password

```
production:
  <<: *default
  database: blog_production
  #username: blog
  #password: <%= ENV['BLOG_DATABASE_PASSWORD'] %>
```

### Setup db

```
$ bin/rails db:setup
$ bin/rails db:migrate
```

### Start server

```
$ bin/rails server -b 0.0.0.0
```
Linux a summary shortcut key

仕事でLinux環境を触るようになったので、学習を始めました。
自分用なので参考にならないかもです。

忘れないようにOut put したいと思います。

編集中

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/593830/21f38592-4720-7e82-6df8-480dd9045445.png)
unliftioをなるべくわかりやすく紹介してみます

# 概要

今回はHaskellerであれば必ず遭遇するであろう「コールバック関数に`liftIO`が適用できない」という問題を解決するパッケージ、[unliftio](https://hackage.haskell.org/package/unliftio)を紹介したいと思います。

- `unliftio`はモナド変換子に関するパッケージ
- コールバック関数`IO a`を`MonadUnliftIO m => m a`にしてくれる
- 導入は簡単
- 一部の変換子(`StateT`, `WriterT`）には意図的に対応していない

## `unliftio`はモナド変換子に関するパッケージ

ライブラリを見れば一目瞭然なのですが、`unliftio`はモナド変換子に関するパッケージです。しかし、提供している関数は一見すると既存の関数に`liftIO`を適用しただけのようにみえます。
ここでは`readFile`を例にあげてみましょう。

```haskell
readFile :: FilePath -> IO ByteString
```

ただし、単なる`IO`ではなくモナド変換子`ReaderT Env IO`を使用する関数内で`readFile`を呼び出すにはどうすればよいでしょうか？ 1つの方法は、`ReaderT`値コンストラクターを手動でアンラップすることです。

```haskell
myReadFile :: FilePath -> ReaderT Env IO ByteString
myReadFile fp = ReaderT $ \_env -> readFile fp
```

しかし、この方法では`myReadFile`は`ReaderT Env IO`に限定した関数となります。よってほとんどの人は`MonadIO`を利用します。これによって`myReadFile`は任意のモナドスタックで利用可能な関数となります。

```haskell
myReadFile :: MonadIO m => FilePath -> m ByteString
myReadFile = liftIO . readFile
```

## `liftIO`ではコールバック関数`IO a`を引数にする関数に対応できない

では今度はコールバック関数`IO a`を引数にする関数`withBinaryFile`をみてみましょう。

```haskell
withBinaryFile :: FilePath -> IOMode -> (Handle -> IO a) -> IO a
```

`withBinaryFile`は引数に`(Handle -> IO a)`を取っています。これを先程と同様
`ReaderT Env IO a`にすることはできます。

```
myWithBinaryFile fp mode inner =
  ReaderT $ \env -> withBinaryFile
    fp
    mode
    (\h -> runReaderT (inner h) env)
```
しかしここでは`MonadIO`を利用することはできません。なぜなら`liftIO`は外側の`IO a`を`m a`にすることはできても、コールバック関数`(Handle -> IO a)`を`(Handle -> m a)`にすることはできないからです。`ghci`で試すと以下のようになります。

```haskell
> :t liftIO . withBinaryFile "." ReadMode
liftIO . withBinaryFile "." ReadMode
  :: MonadIO m => (Handle -> IO a) -> m a
```

これを解決するのが`unliftio`です。

## `unliftio`

`unliftio`を利用することで上記を問題を解決することができます。

```haskell
import Control.Monad.IO.Unlift

myWithBinaryFile
    :: MonadUnliftIO m
    => FilePath
    -> IOMode
    -> (Handle -> m a)
    -> m a
myWithBinaryFile fp mode inner =
  withRunInIO $ \runInIO ->
  withBinaryFile
    fp
    mode
    (\h -> runInIO (inner h))
```

簡単に説明すると、`withRunInIO`を適用するとコールバック関数（ここでは`runInIO`）が生成されます。これを`liftIO`と同様に`IO a`を返すコールバック関数に適用すればいいのです。

## 導入は簡単

`unliftio`の導入は一見面倒に見えますが、実は既存のライブラリをunlift化したものを既に提供しており、利用者はインポートを入れ替えるだけで容易に導入できます。例えば`async`パッケージを`unliftio`に対応した関数に入れ替えたい場合には以下の様すればよいのです。

```haskell
-- import Control.Concurrent.Async
import UnliftIO.Async
```

またカスタムPreludeである[RIO](https://hackage.haskell.org/package/rio)ではデフォルトでunlift化された関数が利用可能となっています。

## 注意点

注意点としてはunlift化した関数は[`ReaderT`,`IdentityT`のモナドスタックのみに対応しており](https://hackage.haskell.org/package/unliftio-core-0.2.0.0/docs/Control-Monad-IO-Unlift.html#t:MonadUnliftIO)、`StateT`,`WriterT`には対応していません。これはおそらくですが、この2つのモナド変換子はいくつか問題があり、使用しないほうが良いと判断したためかと思われます。ここでは詳しく説明しませんが、

- [StateTは競合性のある関数で利用すると問題が発生する](https://scrapbox.io/LugendrePublic/ReaderT_%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3(%E7%BF%BB%E8%A8%B3))
- [WriteTはスペースリークが発生する](https://qiita.com/ruicc/items/28b1cd2df215ce724e26)

といわれています。
FirebaseをCarthageで管理する XcodeGen編

Firebaseの管理をCocoaPodsからCarthageに移し替える作業があったのでまとめておきます

# 環境
Xcode: 11.2.1
Carthage: 0.34.0
XcodeGen: 2.13.1


# 手順
[Firebaseの公式ドキュメント](https://github.com/firebase/firebase-ios-sdk/blob/master/Carthage.md)にCarthage管理のやり方が記載されているのでこちらに沿って移行していきます

今回はFirebaseAuthのみ追加するパターンです


CartFileに`FirebaseAuth`のバイナリを書き込みます
※ アナリティクスのバイナリは必須のようですので`FirebaseAnalyticsBinary`も追加します

```swift:CartFile
binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseAuthBinary.json"
binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseAnalyticsBinary.json"
```

書き込めたらCarthageを更新

```
$ carthage update --platform ios
```

次はproject.ymlを更新します
`Carthage/Build/iOS/`に追加されている`framework`を`dependencies`に書き込んでいきます
`OTHER_LDFLAGS: $(inherited) $(OTHER_LDFLAGS) -ObjC`をドキュメンに沿って追加して
`Firebase.framework`のみ`link`を外しておきます

```yml:project.yml
targets:
  YourAppName:
    settings:
      base:
        OTHER_LDFLAGS: $(inherited) $(OTHER_LDFLAGS) -ObjC
    dependencies:
      - framework: Carthage/Build/iOS/Firebase.framework
        embed: false
        link: false
      - framework: Carthage/Build/iOS/FIRAnalyticsConnector.framework
        embed: false
      - framework: Carthage/Build/iOS/FirebaseAnalytics.framework
        embed: false
      - framework: Carthage/Build/iOS/FirebaseAuth.framework
        embed: false
      - framework: Carthage/Build/iOS/FirebaseCore.framework
        embed: false
      - framework: Carthage/Build/iOS/FirebaseCoreDiagnostics.framework
        embed: false
      - framework: Carthage/Build/iOS/GoogleAppMeasurement.framework
        embed: false
      - framework: Carthage/Build/iOS/GoogleUtilities.framework
        embed: false
      - framework: Carthage/Build/iOS/GTMSessionFetcher.framework
        embed: false
      - framework: Carthage/Build/iOS/nanopb.framework
        embed: false
```

# framework達の依存関係がうまくいっていない
これでビルドしたところXcodeでエラーがでました
framework同士の依存関係が合っていないため、あるはずのframeworkが見つからないというエラーでした

`Cartfile.resolved`を確認すると`FirebaseAnalytics`と`FirebaseAuth`の最新版が入っていました

```swift:Cartfile.resolved
binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseAnalyticsBinary.json" "6.18.0"
binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseAuthBinary.json" "6.18.0"
```

最新版同士では依存関係がうまくいかないようなのでバージョンを指定します
依存関係がうまくいくバージョンを確認する方法は`CocoaPods`で`Firebase`をインストールしたときの`Podfile.lock`でインストールされているバージョンを見て確認しました(もっと良い方法があるかもしれません)

バージョンを指定した`CartFile`

```swift:CartFile
binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseAuthBinary.json" == 6.5.0
binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseAnalyticsBinary.json" == 6.5.0
```

これでうまくビルドできました

CarthageでFirebaseを管理する場合、Podsの用にうまいことframeworkのバージョンを管理してくれないようでした




















参考にした記事: https://qiita.com/noppefoxwolf/items/101a9f19d2d6b5d4447b
初心者のReact Native + Redux

プログラミング歴半年の素人が書いています。

間違いのないようご自身でも良く調べた上でお願いいたします。


# 対象読者

- モバイルアプリ開発をしたことがない人
- JavaScriptの基礎構文がわかる人
- Reactを知っている人
- Macの人

わたしです。 試行錯誤の記録となっておりますので、ベストプラクティスではない可能性が十分含まれておりますことご了承ください。

#環境構築

## Node.jsのインストール

https://nodejs.org/en/download/

上記リンクからダウンロードできます。

## ReactNativeのインストール

各種必要素材をインストトールします。

```
brew install node
brew install watchman
```

以下「cocoapods」は2020年2月の最新バージョンだとエラーになる事があります。
その場合は古いバージョンを指定してインストトールすると治るかもしれません。

```
sudo gem install cocoapods
```

react-nativeの新規プロジェクトを作成します。

```
npx react-native init AwesomeProject
```

新規プロジェクトが作成できたら、以下のコマンドで起動。

```
cd AwesomeProject
npx react-native run-ios
```

これで起動できない時もある...。上記は公式チュートリアル通りなんだけど。で、もっかいやり直すと動いたりする。



## Reduxのインストール

ReduxはStateを一元管理するための仕組みです。
なぜStateの管理を一つにまとめる必要があるかというと、複数のコンポーネントが独自にStateを管理している状態は見通しが悪く、バグの原因になったり修正のしにくい状態だからです。

以下のコマンドでReduxをインストールする事ができます。

```
yarn add redux react-redux
```


## React Native + Redux のディレクトリ構成

- ReactはViewの管理（見た目を作るコンポーネントなど）
- ReduxはStateの管理（State管理のために「action」「reducer」「State」の３つが必要）

をそれぞれ行います。
したがって、ルート直下は以下のような構成とします。

```
[project root]
 `- [src]
     |-[actions]
     |-[reducers]
     |-[states]
     |-[views]
        |-[components]
        `-[containers]
```

合わせて、ルート直下にある App.js を削除し、src/views/containers/App.js を作成します。



## 画面を作って表示させる

画面を作ることと、表示させることは、２つの異なる作業です。
まずは、画面を作って行きます。

### Component と Containerについて

先ほどのディレクトリ構成で「View」を構成する2つの要素のディレクトリを作成しました。それぞれ以下のような役割分担になっています。

####「Components」は見た目を担当

Componentsは見た目を担当し、ロジック部分には関与しません。

####「Containers」はロジック部分を担当

Containersはロジック部分を担当し、見た目には関与しません。
したがって、Containerは自分自身のDOM要素は持たず、Styleなども当たりません。



### 画面を作る

では画面を作ります。

見た目を担当する部分は「Components」内に作ります。
最初の画面なので「LandingPage.js」と名付けました。
ますばシンプルに、画面中央に挨拶を表示。

```react:components/LandingPage.js
import React from 'react';
import { Text, View, StyleSheet} from 'react-native';

const LandingPage = () => (

    <View style={styles.View}>
        <Text>はじめての React Native</Text>
    </View>
)

export default LandingPage;

//スタイルの記述はこんな感じ。使えば分かる感じのヤツ！
const styles = StyleSheet.create({
    View: {
        flex: 1,
        flexDirection: 'column',
        justifyContent: 'center',
        alignItems: 'center',
    }
})
```


### 表示させる部分を作る

表示させる、ロジックを担当する部分は「Containers」内に記述。

シンプルに、先ほど作った`LandingPage.js`を表示するだけです。

```react:containers/App.js
import React, { Component } from 'react';
//
import LandingPage from '../components/LandingPage';


export default class App extends Component {
    
    render() {
        return (
            <LandingPage />
        );
    }
}
```

### `index.js`から呼び出す

では、ルート直下の`index.js`から`views/contaieners/App.js`を呼び出します。
これで、アプリ起動後に`LandingPage.js`の画面が描画されます。

```react:index.js
/**
 * @format
 */

import {AppRegistry} from 'react-native';
import App from './src/views/containers/App'; //ここでimport
import {name as appName} from './app.json';

// そして呼び出し
AppRegistry.registerComponent(appName, () => App); 
```


## 次回に続く

https://qiita.com/ShinKano/items/7f0b91358039260bae2a
Amazon.co.jpの商品ページURLを短縮化するVBScript

# Amazonの商品ページを短縮したい！
Amazonで見つけた商品を友人とかに共有するとき、どうしてますか？
昔は短縮URLを発行する機能があったのですが、今は廃止されてしまいました。（2020/03/06時点）

例えば、Apple AirPods ProのURLを送ろうとすると、

[https://www.amazon.co.jp/Apple-アップル-MWP22J-A-AirPods/dp/B07ZPS4FSW/ref=sr_1_1_sspa?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&keywords=apple&qid=1583476380&sr=8-1-spons&psc=1&spLa=ZW5jcnlwdGVkUXVhbGlmaWVyPUExNjRaNUlYSjNTNlJLJmVuY3J5cHRlZElkPUEwMTc4MDgzNzdDQzg1VllRUVA0JmVuY3J5cHRlZEFkSWQ9QTVISTFYUlM5S0RVSSZ3aWRnZXROYW1lPXNwX2F0ZiZhY3Rpb249Y2xpY2tSZWRpcmVjdCZkb05vdExvZ0NsaWNrPXRydWU=](https://www.amazon.co.jp/Apple-アップル-MWP22J-A-AirPods/dp/B07ZPS4FSW/ref=sr_1_1_sspa?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&keywords=apple&qid=1583476380&sr=8-1-spons&psc=1&spLa=ZW5jcnlwdGVkUXVhbGlmaWVyPUExNjRaNUlYSjNTNlJLJmVuY3J5cHRlZElkPUEwMTc4MDgzNzdDQzg1VllRUVA0JmVuY3J5cHRlZEFkSWQ9QTVISTFYUlM5S0RVSSZ3aWRnZXROYW1lPXNwX2F0ZiZhY3Rpb249Y2xpY2tSZWRpcmVjdCZkb05vdExvZ0NsaWNrPXRydWU=)

このとおり。
**長い。めちゃくちゃ長い。**

そこでGoogle先生に聞いてみると、最短で [https://amazon.co.jp/dp/B07ZPS4FSW/](https://amazon.co.jp/dp/B07ZPS4FSW/) まで短くなることが分かります。
短くなってすっきり！

でも、いちいち手でURL編集するのダルい…

# VBScriptで作っちゃえ
ということで、簡単に短縮するツールを作ってしまえということで作りました。
Amazon（Japan）の長いURLをコピーしてクリップボードに入れ、VBScriptを起動させると、URLを短縮してクリップボードに入れなおしてくれるプログラムです。

## VBScriptプログラム

```VB:amzn&nbsp;shorter.vbs
Option Explicit
Dim objRE
Dim objHTML
Dim objClip
Dim objREC
Dim WSH
Dim URL
Dim SURL
Dim PDCD

'クリップボードからテキスト抽出
Set objHTML = CreateObject("htmlfile")
URL = Trim(objHTML.ParentWindow.ClipboardData.GetData("text"))

'正規表現でAmazonの商品ページURLかを判断し、URL短縮化＋クリップボードに保存
Set objRE = CreateObject("VBScript.RegExp")
objRE.Pattern = "https://www.amazon.co.jp/.*dp/*"

If objRE.Test(URL) Then
  PDCD = Mid(URL, InStr(URL, "/dp/") + 4, 10)
  SURL = "https://amazon.co.jp/dp/" & PDCD & "/"

  Set WSH = CreateObject("WScript.Shell")
  Set objClip = WSH.Exec("clip")
  Set objREC = objClip.stdIn
  objREC.WriteLine SURL
  objREC.Close
  WScript.echo "Done."
Else
  WScript.echo "It's NOT the URL of the Amazon item page."
End If

Set objRE = Nothing
Set objHTML = Nothing
Set WSH = Nothing
Set objClip = Nothing
Set objREC = Nothing
```

## 使い方
1. Amazonの商品ページのURLをコピー
2. VBSファイルを起動
3. ［Done.］のメッセージが表示されたら完了
4. メールなどにペースト

使ってみてエラーが発生したり、もっと構文をコンパクトにできる方法があればコメントいただけると嬉しいです！

#あとがき
VBScriptでクリップボードに保存させるところが本当に苦労しました…
VBAとかと同じくDataObjectでアクセスできるといいんだけど…
アイスクリームの支出と気温の関係を調べた


データ解析初心者がpandasの勉強として、1世帯当りのアイスクリーム支出額と気温との関係を調べてみました。(なんとなく結果は見えていますが...)
参考にしたのは「[Pythonによるデータ解析入門](https://www.amazon.co.jp/Python%E3%81%AB%E3%82%88%E3%82%8B%E3%83%87%E3%83%BC%E3%82%BF%E8%A7%A3%E6%9E%90%E5%85%A5%E9%96%80-%E5%B1%B1%E5%86%85-%E9%95%B7%E6%89%BF/dp/4274222888/ref=sr_1_3?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&keywords=Python+%E3%83%87%E3%83%BC%E3%82%BF%E8%A7%A3%E6%9E%90&qid=1583399806&sr=8-3)」と以下の2つのサイトです。

[アイスクリームBIZ 統計情報](http://www.icecream.or.jp/biz/data/expenditures.html)
[気象庁 月別平均気温](http://www.data.jma.go.jp/obd/stats/etrn/view/monthly_s3.php?%20prec_no=44&block_no=47662)

上のサイトに掲載されている2018年のデータを用いました。

## Webスクレイピングでデータを取り出し

```python3
import pandas as pd
import math
import matplotlib.pyplot as plt
import statsmodels.api as sm
ice_url = 'http://www.icecream.or.jp/biz/data/expenditures.html'
temp_url = 'http://www.data.jma.go.jp/obd/stats/etrn/view/monthly_s3.php?%20prec_no=44&block_no=47662'
ice = pd.read_html(ice_url)[0] 
temp = pd.read_html(temp_url)[0]
# [0]で1つ目のテーブルを指定している
ice_2018 = ice.iloc[1:13, 5].astype(float)
temp_2018 = temp.iloc[144, 1:13].astype(float)
# 2018年のデータのみ取り出して数値型に変換
month = pd.DataFrame([i for i in range(13)])
# n月を用意しておく
```
これで2018年の1世帯当りのアイスクリーム支出金額と月間平均気温を取り出せました。

## 相関を求める

```python3
icecream = pd.concat([month, ice_2018, temp_2018], axis=1)[1:]
# n月とアイス支出金額、平均気温を結合
x_data, y_data = icecream[144], icecream[5]
avetem, aveex = x_data.sum() / 12, y_data.sum() / 12
# 年間平均気温(avetem)とアイスクリーム支出(aveex)が求められた
```
相関係数は共分散 $S_{xy}$ などを用いて以下のように表されました。

```math
r = \frac{S_{xy}}{\sqrt{S_{xx}S_{yy}}}
```
この式に従って相関係数 $r$ を以下のように愚直に求めました。（もっと良いやり方があると思います）

```python3
for i in range(len(icecream)):
    ex = icecream.iloc[i,1] - aveex
    tem = icecream.iloc[i,2] - avetem
    extem += ex*tem
    ex0 += ex**2
    tem0 += tem**2
extem0 = math.sqrt(ex0)*math.sqrt(tem0)
r = extem / extem0
# r = 0.8955143151163499
```

この結果からアイスクリームの支出と気温の間に強い相関があることが分かります。

## 回帰分析

最後にpandasのolsを用いて回帰分析をしてみます。

```python3
X = sm.add_constant(x_data) # 切片を求めるには必要らしい
model = sm.OLS(y_data, X)
results = model.fit()
a, b = results.params[0], results.params[1]
# a:切片、b:傾き
plt.plot(x_data, a+b*x_data)
plt.scatter(icecream[144], icecream[5])
```

![ice_temp.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531768/36d93784-45b7-c377-914a-ff5a44a9ebc0.png)

結果、図のような回帰直線が得られました。

なるほど、暑くなるとアイスを食べたくなるようです。
AWS Cloud9にVisual Studio Codeで接続する備忘録

#前提条件

- AWS Clode9にEnvironmentを作成済みであること
- ローカルPCにVSCodeがインストール済みであること
- VSCodeのExtensionでRemote Developmentがインストール済みあること

#ローカルPCの設定

1. TeraTermなどで秘密鍵、公開鍵を作成する。
1. C:\Users\[ユーザー名]\.sshに秘密鍵のファイルを保存する。
1. C:\Users\[ユーザー名]\.ssh\configにEC2の設定を追加する

```
Host [任意のホスト名]
    HostName [EC2のホスト名かIPアドレス]
    User ec2-user #デフォルトではec2-user
    IdentityFile C:\Users\[ユーザー名]\.ssh\[秘密鍵ファイル名]
```

#AWS EC2の設定
1.Clode9が動作するEC2のセキュリティグループのインバウンドにSSHを追加する。
※デフォルトでSSHが2つほど登録済みであるが、これとは別にローカル環境からSSHで接続する場合の設定を登録する。

#Clode9の設定
1./home/ec2-user/.ssh/authorized_keysファイルに公開鍵を追記する。
※デフォルトで登録されている公開鍵はCloud9IDEが使用する鍵がなので、消さないように要注意
RubyXL シート名一覧の取得

リファレンスが読めない・・・
SheetNameクラスやSheetNamesクラスというものがあるが・・・

とりあえずこれで解決した。

```rb
workbook    = RubyXL::Parser.parse("sample.xlsx")
sheet_names = workbook.sheets.map(&:name)
```
Promise.rejectがSpyOnできない！Spyしただけなのに実行されてしまう！

Jasmine 3.3

Promiseでrejectしてエラー吐くようなパターンをテストしたい場合のお話ですが、
どうやら仕様？の様です（参照にGithubのissueのリンクあるのでよかったら見てね）

**どうやらfakeAsyncの時の仕様みたい？**

(完全には解決してない問題。誰か教えて！)

とりあえず下記二つは同じな様なので、適宜読み替えてください
（公式ドキュメントで見つけれてないので、違うかも）

```javascript:promise.component.ts
Promise.reject('Something error occured!') ;

new Promise( function( resolve, reject ) {
	reject( "Something error occured!" ) ;
} ) ;
```

ダメな書き方（returnValueを使う）

```javascript:test.component.spec.ts
spyOn(testService, 'func').and.returnValue(Promise.reject('Something error occured!'));
```
良い書き方（callFakeを使う）


```javascript:test.component.spec.ts
spyOn(testService, 'func').and.callFake(() => {Promise.reject('Something error occured!'); });
```

returnValueだとその場で評価されてしまうみたい？（どうかと思うけど）


ちなみにfakeAsyncのなかだと、下記の様にキャッチするけど、Uncaught Promise Errorになるので、微妙にモヤモヤします。(上記で飛ばした'Something error occured!'だとマッチしない）

```javascript:test.component.spec.ts
expect(() => {
  TestObj.func();
  tick();
}).toThrow();
```

####参考
https://github.com/jasmine/jasmine/issues/1648
https://github.com/jasmine/jasmine/issues/1590
カテゴリカルデータ分析からみるロジスティック回帰

「人文・社会科学のためのカテゴリカル・データ解析入門」(2005)より

## 対数線形モデルとロジスティック回帰の関係
- 対数線形モデルのオッズ比の自然対数と、ロジスティック回帰係数とは、同じ値

## 多項ロジット・モデル
- 式を複数作る。式の数は「項目数 - 1」
- パラメーターは式で別々

## 順序ロジット・モデル
- 順序を1,2,3,4とした時、2以上の確率、3以上の確率、4の確率のロジットで式を複数作る。式の数は「項目数 - 1」 
- パラメーターは共通

## 対数線形モデルとの使い分け
- 従属変素と独立変数に分けられれない場合、対数線形モデルがよい。
- 従属変素と独立変数がはっきりしている場合、ロジスティック回帰がよい。
- 回帰モデルの方が対数線形モデルより古く広い分野で知られている。

## 回帰分析との使い分け
- 順序のある離散変数が従属変数の場合は回帰分析が使われることが多い。
- しかし、①恣意的、②連続変数ではない、③従属変数の上限下限を過ぎてしまう、―という問題があり、順序ロジットが良い。
RStanをMacにソースからインストール

# はじめに

『[時系列分析と状態空間モデルの基礎: RとStanで学ぶ理論と実装](https://www.amazon.co.jp/exec/obidos/ASIN/4903814874/basic05-22/)』を読みながら時系列分析を勉強していて、RStanをMacにインストールしました。

その時に[Installing RStan from source on a Mac](https://github.com/stan-dev/rstan/wiki/Installing-RStan-from-source-on-a-Mac)を参照しながらソースからインストールしたのですが、サイトの手順通りだとうまくいかなかったので、備忘録として書きます。

# Macにインストールする時に躓いたこと

とりあえず上記サイトの通りに行いました。

(1) [macOS R toolchain installer](https://github.com/rmacoslib/r-macos-rtools#how-do-i-use-the-installer)からインストーラを入手し、macOS R toolchainをインストール

(2) 古いRStanを削除し、また.RDataファイルも削除するため、Rで以下を実行

```
remove.packages("rstan")
if (file.exists(".RData")) file.remove(".RData")
```

(3) Rを再起動し、RStanのインストール

```
install.packages("rstan", type = "source")
```

ここでエラー発生。

どうもコマンドラインツールがないと言われているようだったんですが、Xcodeはインストール済みだし、macOS R toolchain installerが「XCode CLI」をインストールするとサイトにも書かれているし、変だなーと思いつつも、コマンドラインツールをインストールすべくターミナルで以下を実行。

```
xcode-select --install
```

そうして改めてRStanのインストールを実行したら、無事インストールできました。

# 後で気づいたこと

もしかして、そもそもソースからインストールする必要なかったかも？と今では思っています。

ただ、[RStan Getting Started (Japanese)](https://github.com/stan-dev/rstan/wiki/RStan-Getting-Started-(Japanese))を参照しながら下記コマンドでインストールしたら、

```
install.packages('rstan', repos='https://cloud.r-project.org/', dependencies=TRUE)
```

インストール自体はできたものの、実際に使用した時にエラーが出てしまいました。

確かPermission Errorが出ていたと思います。もしかしたら[こちらのサイト](https://sonnamonyaro.hatenablog.com/entry/2019/11/16/092734)のように、まずclang-7をインストールしておけば良かったのかもしれません。

結局、私はソースからインストールして現在問題なく使える状態になりました。
Office365の個人用マクロブック何処へ・・・

# 経緯
筆者の仕事は設計書作成が大半を占めるため、
畢竟、生粋のVBA愛用者だったわけだが
新しいPCに買い替えてから見つからない・・・。<br>
何がって・・・<br>

そう
### 個人用マクロブック（物理名： PERSONAL.XLSB）
が！

いつも新しいPCをセットアップするときは、Google先生に聞いて、ふむふむ、どうやらここにあるらしい・・・

```
C:\Users\[ユーザー名]\Appdata\Roaming\Microsoft\Excel\XlStart
```
と何も考えず指示通りに行けばでてたのに・・・。
ああ、愛しの個人用マクロブック、いずこへ行ってしまったの？
<br><br><br>

## そういえば
ちょっと待てよ?<br>
個人用マクロブックって、たしか、
初期状態だと作成されてないから何か適当なマクロつくって
個人用マクロブックに保存しないといけなかったような・・・（遠い記憶)


### てなわけで早速つくってみた！
よし！これで同じディレクトリ見に行けば・・・<br>

### ない・・・
なぜだろう・・・（ここから約3分悶々とする）

# 場所 
結論です

```
C:\Users\[ユーザー名]\AppData\Local\Packages\Microsoft.Office.Desktop_8wekyb3d8bbwe\LocalCache\Roaming\Microsoft\Excel\XLSTART
```
### **↑ここ！**
<br>

# まとめ
今回どうして気づけたかというと、
エクスプローラの検索を久々に使って<br>
`PERSONAL.XLSB`<br>
を検索したから。（まずそれだろ！というツッコむ方、ごもっともです・・・）<br>
ブラウザ検索は（壁にあたったらすぐあきらめて）秒速でするのに、
エクスプローラの検索はノロノロしてるのが嫌で敬遠していた。<br><br>

これからはもっとちゃんと使ってあげよう・・・。
EditorWindowのファイル名とクラス名が異なるとLayoutをSaveしても保持されない問題

#環境
Unity2019.2.10
#問題
Unityで自作のEditorWindowを作ることがあると思うのですが、ものによってはLayoutをSaveしても次回開いた時に保持されないWindowがあったので調査しました。
#原因
クラス名とファイル名が異なるものについては現象が起こるということがわかったので修正したらうまく保持されるようになりました。

[【Unity】EditorWindowのファイル名とクラス名が異なるとUnity起動時に "Error while reading window layout" が出る](https://qiita.com/r-ngtm/items/c4f8965ec7672034fa86)
というのもあるのでエラーで気づけるかもしれませんがバージョンによってはエラー出ないかもしれないので注意です。
メールからSMSを送る方法を発見！試してみたら驚くほど簡単だった件  


##メールからSMSが送れるらしい！？
マーケティング界隈ではメールマガジンの絶望的な開封率とコンバージョン率は有名な話。うちの会社のマーケティング担当毎週何回もメルマガ配信をおこなっていますが、なかなか手応えを感じられず困っています。  

そこで、開封率がメールの20倍以上？あると言われているSMSの導入を我が社で検討したいと考え、色々調べていたら面白いサービスを発見しました！

**なんとメールからSMSが送れるらしい！！！**

GmailやOutlookなんかからもOKの模様。 

一体どうやって？設定面倒くさくないの？開発スキルがほとんどない自分にも設定できるのか？色々な疑問や不安を感じつつも、とりあえず簡単らしいので実験的にやってみたら、超絶簡単だったので、皆さまと共有します。  

##なぜSMSがマーケティングなどで使えるのか  
SMS（ショートメッセージ）は携帯電話番号宛に送られるメッセージ。開封率は80%以上と言われていて、メッセージに記載されているリンクへのクリック率（CTR）もなかなかいいらしい。 

マーケティング利用だけではなく、督促や予約などのリマインド、二段階認証（SMS認証）としても使える優れもの。メールに代わる、顧客との新たな連絡手段として導入している企業も増えているそうです。たとえば、EC、ホテル、飲食店などがド入例としてあります。  

しかし、携帯電話でSMSを送る場合1端末あたり200件の制限があり、企業SMSを大量配信したい場合にはSMS配信サービスを利用しなければならない。配信数が少ない企業でも、初期費用や月額費用がかからないSMS配信サービスを利用すれば安心してSMSを送ることができるようです。 

**つまり、SMS配信サービスを利用すれば企業はパソコンからSMSが送れるようになり、同時に一斉配信も可能となる…ということです。**

##SMS配信サービス会社は色々ある  

SMSの配信サービス会社を調べてみたところ、内資・外資問わず意外とたくさんありました。それぞれの特徴はSMSサービス会社を比較している記事たちを参照してください。

いずれも基本的な配信方法としては、各社が用意するSMS配信ツールの管理画面に顧客の電話番号を登録して配信する方法とAPIを利用してSMS送信システムを既存システムに組み込んで配信する方法があります。 

**しかし、どうやらCM.comという会社のSMS配信サービスは上記の基本的な配信方法以外に、GmailやMicrosoft OutlookなどメールからSMSを配信する方法もあるようです！**

新しいツールを導入すること、使い方を覚えること、顧客情報を新しいツールに登録することなど、ちょっとした手間が導入のハードルになっている企業にとっては嬉しいサービスですね。 

##CM.comなら今使っているメールソフトから送信可能  
前置きが長くなりましたが、早速CM.comのメールからSMSをサービス「Mail SMS」を試してみました！ 

**■メール（Gmail）からSMSを送るまで**
①CM.comのアカウントを作成する  
<img width="1253" alt="スクリーンショット 2020-03-06 16.24.48.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/598042/a4e1449a-dd6d-03fd-14c6-8cc0f9f5f65f.png">
https://www.cmtelecom.jp/
CM.com Japanの公式サイトの問い合わせフォームからアカウント開設の申請をします。外資系ではありますが、日本語サイトから問い合わせれば日本人のスタッフが対応してくれます。  

②Mail SMS設定をおこなう  
<img width="968" alt="スクリーンショット 2020-03-06 16.27.48.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/598042/e633f5b9-9017-bbc1-94bf-ac32fe18d0e6.png">
作ってもらった自分のアカウントにログインし、メールからSMSを送るための設定をします。送信元のメールアドレスとSMSを受信した人に表示される送信元名を設定するだけ。   

③GmailからSMSを送る 
<img width="977" alt="スクリーンショット 2020-03-06 16.32.29.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/598042/a2f625ea-e666-0d0b-7eed-45e474051996.png">
あとはGmailを開いて、普通のメールと同じようにメッセージを作ります。 
まず送りたい相手の電話番号をメールアドレス形式**【国番号＋電話番号＋＠mail-sms.com】**にしてToに入れ、本文に全角70文字以内のテキストを入れて、送信ボタンを押すだけ！ 

**結果は…届いた！！！！！！！**
しかもメッセージを送った後、自分のGmailにSMS配信完了の連絡が届いていた！親切すぎる！ 

ちなみにスマートフォンのGmailアプリからも試してみましたが、こちらでも問題なくSMSを配信することができました。パソコン以外からも送れるのは、忙しすぎる人には便利かもしれません。 

##ところでCM.comって何者？  
CM.comはオランダに本社を持つ会社。最近ユーロネクスト・アムステルダムで新規上場を果たしていて、ヨーロッパで知名度も信頼度も高い会社のようです。2018年に日本法人ができ、既存の日系SMS配信サービス会社とは少し違った独自のサービスを展開しています。初期費用も月額費用も不要で、送った分だけの課金という料金体系もポイントが高い。私はぜひ利用を検討してみたいと思いました！ 

CM.Japan株式会社
https://www.cmtelecom.jp/
[Unity] 画面比率に合わせて背景を全画面に引き延ばす

# 1. 注意点
・Canvas Scaler(Script)を外す。
・適用したいImage(Script)があるオブジェクトに下記のスクリプトをアタッチする。
・isAllScreenをInspector上でtrueにする事で使用できる

# 2. コード
```csharp:BackGroundScaler.cs
using UnityEngine;

public class BackGroundScaler : MonoBehaviour
{
    /// <summary>
    /// 縦横比を無視して全画面にするか
    /// </summary>
    [SerializeField]
    private bool isAllScreen = true;


    private void Start()
    {
        Vector2 standardResolution = GetComponent<RectTransform>().sizeDelta;

        if (isAllScreen)
        {
            //縦横比率を変更してでも全画面に合わせる。
            float magnification_x = 0;
            float magnification_y = 0;

            float height = Screen.height;
            magnification_y = height / standardResolution.y;
            float width = Screen.width;
            magnification_x = width / standardResolution.x;

            GetComponent<RectTransform>().sizeDelta = new Vector2(standardResolution.x * magnification_x, standardResolution.y * magnification_y);
        }
    }
}


```
agora.io クイックスタートガイド WindowsSDK用

#概要
この記事はSDKに内包されているサンプルのビルドガイドです。

##STEP1. Agora Accountの作成とApp IDの取得
1.https://sso.agora.io/signup?source_id=3 からデベロッパー登録をします
2.ダッシュボードから新規プロジェクトを作成します。
<img width="2284" alt="appid_1.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/300377/5bbe45d6-19e6-5032-2a43-05f127fdd9b8.jpeg">
3.プロジェクト名を入力してSubmitボタンをクリックします。
4.作成したプロジェクトが一覧に表示されます。
<img width="2276" alt="appid_2.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/300377/aa129684-2364-9fbf-0020-fd82c93bbe0f.jpeg">

##STEP2. Agora Windows SDKの取り込み

**SDK＆サンプルファイルを取得**
1.[ダウンロードページ](https://docs.agora.io/en/Agora%20Platform/downloads)からWindowsSDKをダウンロードします。
![download.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/300377/c2497deb-b0e5-636d-91a2-ca058853aaca.png)
2.フォルダ構成は以下のようになっています。
![sdk.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/300377/11601e1d-d043-0043-e75d-8d0f7e30b117.png)
3.sdkフォルダをサンプルプロジェクト配下にコピーします。今回はOpenVideoCall以下にコピーしてみます。
![movesdk.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/300377/dd13fe07-bd23-9650-d70c-d6a930b8c43f.png)
4.さきほど取得したApp IDをagoraobject.hに追記します。
![appid.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/300377/cffc198e-e0a2-a6fb-704f-983d165874bf.png)
5.OpenVideoCall.proを[Qt](https://www.qt.io/)で開き、実行をクリックします。
![qt.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/300377/3d7801f4-c95a-559e-18b9-2df1c3d2399e.png)
6.ビルドが完了したらアプリが起動します。「Input Name」にチャネル名を入力してjoinChannelをクリックします。
![join2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/300377/65d325ae-254c-4ec0-6430-9ab87544c81d.png)
7.ビデオ通話が開始します。
![room.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/300377/52dc38ec-1904-6161-a12d-b60b146627a3.png)


##STEP3.実装
SDKに内包されているサンプルか、[APIドキュメント](https://docs.agora.io/en/Video/API%20Reference/cpp/index.html)を参考に実装を開始します。


---
##最後に
[**agora.ioに関するお問い合わせはこちらから**](https://jp.vcube.com/lp/agora?utm_campaign=Agora&utm_source=Qiita&arc_id=dc92477e31b306a96f48)
[<img width="912" alt="スクリーンショット 0001-08-15 13.41.56.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/300377/5d815376-9b89-08fa-9434-6fa7e8e67bcf.png">](https://jp.vcube.com/lp/agora?utm_campaign=Agora&utm_source=Qiita&arc_id=dc92477e31b306a96f48)
---
【Zabbix】snmptrapの監視設定が上手くいかない時の対処方法

# Zabbixでのsnmptrapの監視がうまくいかなかった時
zabbixのsnmptrapでのは設定変更がたくさんあり、被疑箇所がいっぱいなので以下にまとめます。

設定方法は以下を確認してください。
https://qiita.com/shiojojo/items/827d530a7f41d52b2663


## tcpdumpでsnmptrapが受信できているか確認
Zabbixサーバー上で以下のコマンドでsnmptrapのキャプチャを行ってください。

```
tcpdump -i <zabbixサーバのインターフェース> src host <snmptrapするホスト> and dst port snmptrap
```
パケットが確認できなければ、ネットワークまたは監視対象に問題があります。

## パケットは届いているが上手くいかないとき
/var/log/messageを確認します。
以下の内容の時は

```
 snmptrapd[XXXXX]: No access configuration
```
/etc/snmp/snmptrapd.confの認証の設定が間違っている可能性が高いです。

### /var/log/messageでsnmptrapdはあるがsnmpttが動いていない。
正常時は/var/log/messageに以下のログが続けてでます。

```
 snmptrapd[XXXXX]: <snmptrapのログ>
 snmptt[XXXXX]: <snmptrapのログ>
```
しかし、snmptrapdのログはでてくるかsnmpttのログが出てこない場合がある。
/etc/snmp/snmptt.iniが正しく記載されていない。またはsnmpttのサービスが上手く動いていない可能性が高い。

### /var/log/messageには正常に吐かれているが/var/log/snmptt/snmptt.logにログが出ない。
/etc/snmp/snmptt.iniが正しく記載されていない場合があります。
また、ログ出力がおかしい場合があるため、rsyslogやlogrotationの設定を確認する。

### 想定のログ出力がされていない。
/var/log/snmptt/snmptt.logのメッセージに\$1や\$2や\$3があれば、snmptt.confの設定が間違っている可能性が高いです。
以下の設定規則で記載しなおしましょう。

```
EVENT <event_name> <OID> "category" severity
FORMAT format_string
```
### 想定のログが出力されているがZabbix画面上で受信できない。
ZabbixのGUIのsnmptrapの監視設定が間違っている可能性があります。
ZabbixServer自身にsnmptrap.fallbackの設定を行い。snmptrapの設定方法があっているかチェックしましょう。

### snmptrap.fallbackは上手くいくがsnmptrap[regexp]は上手くいかない。
snmptrap[regexp]のregexpは正規表現です。正規表現が今一度あっているか確認しましょう。


### 参考文献

[https://www.zabbix.com/documentation/4.0/manual/config/items/itemtypes/snmptrap] 
[https://www.miraclelinux.com/product-service/zabbix/tech-lounge/zbx-tl-001]
OmniAuth の  `/auth/:provider`  で GET を受け付けるのはやめましょう （あるいは CVE-2015-9284 について）

# tl;dr

* OmniAuth を使う場合、まずはユーザーを `/auth/:provider` のような URL に誘導すると思います。
* このパスで GET リクエストを受け付けることは、セキュリティの観点から推奨されていません。
* 具体的には、ユーザーが意図しないうちに、意図しない外部アカウントを紐付けてしまう危険性があります。
* 代わりに POST を受け付けるようにしましょう。

# どんな問題が発生するの？

## 前提

Web サイト A と B が存在するとします。
Web サイト A は OmniAuth を使って、「B のアカウントでログイン」のような機能を実装しています。そして `/auth/site_b` で GET リクエストを受け付けています。

アリスとマロリーがいるとします。アリスは被害者、マロリーは攻撃者です。
アリスは A のアカウントを持っていて、A にログイン済みですが、B のアカウントは紐付けていません。マロリーは B のアカウントを持っていて、あらかじめ A に対する認可を済ませてあります [^1] 。

## 攻撃開始

さて、マロリーの攻撃が始まります。
アリスはマロリーの攻撃により、マロリーのアカウントで B にログインした状態になってしまいました [^2] 。さらに続けて、マロリーは A の `/auth/site_b` に GET リクエストを投げされられてしまいました [^3] 。

アリスは B の認可画面にリダイレクトされますが、アリスは（マロリーのアカウント）で B にログインした状態なので、ID/PW を聞かれることはありません。さらに、マロリーは既に A に対する認可を済ませてあるので、認可画面も表示されません。ただちに A の `/auth/site_b/callback` にリダイレクトされます。

結果的に、 **アリスの A アカウントにマロリーの B アカウントが、何の確認もなく紐付けられてしまいます** 。言い換えると、マロリーがアリスの A アカウントに対して全権を手に入れてしまったのです。

この問題には [CVE-2015-9284](https://nvd.nist.gov/vuln/detail/CVE-2015-9284) という名前がついています。

# 何が原因なの？

ひとつは、アリスがマロリーの B アカウントでログインさせられてしまったことです。これは単に B の脆弱性で、今回のメインテーマではないので省略します。

もうひとつは、A の `/auth/site_b` が GET リクエストを受け付けていることです。
もしここが POST だけを受け付けて、なおかつ CSRF 対策が有効になっていれば、A にログインする前に必ず A の画面上で何らかのインタラクト（ログインボタンを押すなど）をユーザーに求めることができます。GET だと CSRF 対策ができないので、ユーザーに意識させないまま OAuth 連携処理を完了できてしまうのです。

# どうすればいいの？

詳細は https://github.com/omniauth/omniauth/wiki/Resolving-CVE-2015-9284 に載っているので、それに従えばよさそうです。おおざっぱにいうと、 [omniauth-rails_csrf_protection](https://github.com/cookpad/omniauth-rails_csrf_protection) という gem を入れた上で、 `/auth/:provider` への GET リクエストを全て POST に置き換えていく、という流れになります。omniauth-rails_csrf_protection は、Rails に搭載されている CSRF 対策と同じメカニズムを OmniAuth の Rack ミドルウェア層に導入する Gem のようです。

重要なのは、この脆弱性が OmniAuth のアップデートによって直るたぐいのものではなく、我々 OmniAuth ユーザー側の対応が必要な点です。自分の Web サイトで `/auth/:provider` への GET リクエストが要件上必須かどうか検討して、必要なければ排除してしまいましょう。

# 余談

この問題のことを知ったのは、dependabot が作った PR がきっかけでした。

`security` ラベルが付いていたので、最初はこの v1.9.1 で脆弱性が直ったのかな？と思ったのですが、[v1.9.1](https://github.com/omniauth/omniauth/releases/tag/v1.9.1) のリリースノートにも「脆弱性を直した」とは書かれていません。どうやら、単に [GitHub Advisory Database](https://github.com/advisories/GHSA-ww4x-rwq6-qpgf) の `Affected versions` の更新が遅れているだけのようです。v1.9.1 でこの脆弱性が直っているわけではありません。

# 参考

* https://github.com/omniauth/omniauth/wiki/Resolving-CVE-2015-9284
* https://github.com/omniauth/omniauth/pull/809
* https://github.com/cookpad/omniauth-rails_csrf_protection
* https://github.com/advisories/GHSA-ww4x-rwq6-qpgf

[^1]: マロリーの B アカウントの「連携済みアプリ」欄にサイト A が表示されているような状態にする、という意味です。
[^2]: 例えばサイト B に脆弱性があって CSRF された、など。
[^3]: 例えば iframe でひっそりロードする、など。