OmniAICreator/Qiita-1.07M huggingface dataset:
パッケージの mix test を実行した場合かどうかを判定するには

Pelemay 0.0.7 から 0.0.10 までは，`mix test` を実行すると CpuInfo の出力値が表示されてウザかったという問題がありました。( Issue: https://github.com/zeam-vm/pelemay/issues/123 )

これは Pelemay 0.0.7 で NIF 実行時にエラーが発生した時に，CpuInfo の出力値をエラー表示してデバッグに役立てるという機能を実装したのですが，Pelemay のテストケースの中にわざとエラーを出力させるというものが含まれていたので，`mix test` の表示がわずわらしくなったということなのです。

そこで，Pelemay 0.0.11 でのバグフィックスということで，Pelemay パッケージ上で `mix test` を実行したかどうかを判定する機能を実装してみました。

diff はこちら:
https://github.com/zeam-vm/pelemay/commit/b21f1b913044599532e1942be850a9331273948b

判定している方法はこちらです。

```elixir
    case {Mix.env(), Mix.Project.config() |> Keyword.get(:package) |> Keyword.get(:name)} do
      {:test, "パッケージ名"} -> # パッケージ上で mix test 実行中
      _ -> # それ以外
    end
```

Mix のそれぞれの関数のリファレンスマニュアルはこちら:

* https://hexdocs.pm/mix/Mix.html#env/0
* https://hexdocs.pm/mix/Mix.Project.html#config/0

この方法で判定して，ETS にフラグを入れてあげ，実行時にフラグを見て出力するか判定するようにしたところ，うまく実装できました！
仮想マシンのfirebase loginでハマった(Centos7)

# 起こったこと
ログインしてみようとしたんですよ。

```
$ firebase login
```

URLが出てきて、リンク先がひらけない。いきなりの出来事すぎて驚いたからポートを確認した。

```
$ sudo firewall-cmd --list-all

public (active)
  target: default
  icmp-block-inversion: no
  interfaces: enp0s9
  sources: 
  services: dhcpv6-client http https ssh
  ports: 8080/tcp 80/tcp 443/tcp
  protocols: 
  masquerade: no
  forward-ports: 
  source-ports: 
  icmp-blocks: 
  rich rules: 
```

いや、https用のポートは開いてんなあ。

# 答え

ここで解決されてた。
・[firebase loginで正常にfirebaseが起動してくれません。](https://teratail.com/questions/135344)

```
$ firebase login --no-localhost
```

そしたら、URLに飛べた。感謝感謝。

# 参考
[firebase loginで正常にfirebaseが起動してくれません。](https://teratail.com/questions/135344)
Vueの開発環境構築でハマった(Centos7)

# はじめに
インストールが終わって、さあ画面を確認しようというところまで来て`npm run dev`を叩いたら接続できなかった。そんな時に疑うものをまとめました。

# 疑うものリスト

・firewall
・babel
・別の理由

## firewall

これが真っ先に出てきますね。コマンドで設定してあげましょう。

```
# まずポートが開いているかを確認
$ sudo firewall-cmd --list-all

# 以下のコマンドで開ける(例:8080の場合)
$ firewall-cmd --permanent --zone=public --add-port=8080/tcp
$ sudo firewall-cmd --reload
```

## babel

こんなエラーが出ました。

```
{ parser: "babylon" } is deprecated; we now treat it as { parser: "babel" }.
```

`parser`を動いているものに対応させてくれってことなので設定しにいきましょう。

```
$ vi node_modules/vue-loader/lib/template-compiler/index.js 
```

`babylon`を`bable`に修正しました。

```node_modules/vue-loader/lib/template-compiler/index.js 
// prettify render fn
if (!isProduction) {
  code = prettier.format(code, { semi: false, parser: 'babel' })
    }
```

## 別の理由

・[コンテナの中のwebpack-dev-serverにアクセス出来なくてハマった件](https://7me.oji.0j0.jp/2016/webpack-dev-server-docker-container.html)

自分もこれと同じようにして解決しました。以下のようになりました。

```package.json
{
  "name": "mymarkdown",
  "description": "A Vue.js project",
  "version": "1.0.0",
  "author": "yCroma",
  "license": "MIT",
  "private": true,
  "scripts": {
    "dev": "cross-env NODE_ENV=development webpack-dev-server --hot --host=0.0.0.0",
    "build": "cross-env NODE_ENV=production webpack --progress --hide-modules"
  },
```

内容としては`--host=0.0.0.0`を追記しました。また自分はヘッドレスで使っているので、`--open`を削除しています。ここらへんは好みですね。

### 保険

人によっては`SELinux`の設定がまだな場合もあるので、一応それも載せておきます。

```
# permissive でダメなら disabled も試してみると良いかも
$ sudo vi /etc/selinux/config
SELINUX=permissive
```

```
$ sudo reboot
```

# まとめ
コロナの影響もあって、Qiitaの記事もいつもより多く投稿されていますね。なんかVueやる人も増えてそうだし。環境構築の手助けにでもなったら幸いです。

# 参考

・[vue-cliでSPAプロジェクトを作ろうとしたら引っかかったバグ](https://snow-cat.net/?p=188)
・[コンテナの中のwebpack-dev-serverにアクセス出来なくてハマった件](https://7me.oji.0j0.jp/2016/webpack-dev-server-docker-container.html)
[C言語アルゴリズム]ブロック移動

#C言語でブロック移動アルゴリズムを実装した
##学習にあたって使用した環境
* ideone.com (https://ideone.com/)
※オンライン上でプログラミング学習ができるサイト

##参考資料
C言語による最新アルゴリズム辞典 (奥村晴彦著/1991年初版 技術評論社：249ページ)

##ブロック移動アルゴリズムの概要
文字列内のブロックを入れ替えるアルゴリズムである。

(以下は参考資料により引用)
>エディタの命令、例えばテキスト xyz12345abcdefg 中でブロック12345をブロックabcdの後に移動すると、xyzabcd12345efgとなる。この操作は12345とabcdとを交換すること、あるいは12345abcdを右に4文字だけ回転することでもある。

今回は、"PROGRAMMING"と"PRINCIPLES"の2単語で構成される文字列"PROGRAMMINGPRINCIPLES"を、単語を入れ替えて"PRINCIPLESPROGRAMMING"とする処理を実装した。

##ソースコード
```C:moveblock.c
/* ブロック移動 block move */

#include <stdio.h>
#include <stdlib.h>

char sentense[] = "PROGRAMMINGPRINCIPLES";

int main(void) {
	printf("%s\n",sentense);
	// 文字列移動関数を呼び出す
	rotate(0, 10, 20);	
	printf("%s\n", sentense);

	return EXIT_SUCCESS;
}

// 文字列反転関数
// 文字列長が奇数の場合には、中央の文字だけは反転しない
void reverse(int i,int j){
	// 置換するためのtmp変数
	int t;
	while(i < j){
		t = sentense[i];
		sentense[i] = sentense[j];
		sentense[j] = t;
		
		// 処理する文字列位置を一つずつずらす
		i++;
		j--;
		
		printf("%s i=%d j=%d (reverse)\n", sentense, i, j);
	}
}

// 文字列移動関数
// reverse関数を呼び出す
// 第一引数：入れ替える文字列の左側部分の開始位置
// 第二引数：入れ替える文字列の右側部分の開始位置
// 第三引数：入れ替える文字列全体の文字列
void rotate(int left, int mid, int right){
	reverse(left, mid);
	reverse(mid + 1,right);
	reverse(left, right);
}

```

##実行結果
期待した通りの結果になった。

```C:result.txt(任意)
Success #stdin #stdout 0s 4488KB

PROGRAMMINGPRINCIPLES
GROGRAMMINPPRINCIPLES i=1 j=9 (reverse)
GNOGRAMMIRPPRINCIPLES i=2 j=8 (reverse)
GNIGRAMMORPPRINCIPLES i=3 j=7 (reverse)
GNIMRAMGORPPRINCIPLES i=4 j=6 (reverse)
GNIMMARGORPPRINCIPLES i=5 j=5 (reverse)
GNIMMARGORPSRINCIPLEP i=12 j=19 (reverse)
GNIMMARGORPSEINCIPLRP i=13 j=18 (reverse)
GNIMMARGORPSELNCIPIRP i=14 j=17 (reverse)
GNIMMARGORPSELPCINIRP i=15 j=16 (reverse)
GNIMMARGORPSELPICNIRP i=16 j=15 (reverse)
PNIMMARGORPSELPICNIRG i=1 j=19 (reverse)
PRIMMARGORPSELPICNING i=2 j=18 (reverse)
PRIMMARGORPSELPICNING i=3 j=17 (reverse)
PRINMARGORPSELPICMING i=4 j=16 (reverse)
PRINCARGORPSELPIMMING i=5 j=15 (reverse)
PRINCIRGORPSELPAMMING i=6 j=14 (reverse)
PRINCIPGORPSELRAMMING i=7 j=13 (reverse)
PRINCIPLORPSEGRAMMING i=8 j=12 (reverse)
PRINCIPLERPSOGRAMMING i=9 j=11 (reverse)
PRINCIPLESPROGRAMMING i=10 j=10 (reverse)
PRINCIPLESPROGRAMMING
```

##所感
* プログラミング学習環境としてのideone.comは大変使い勝手がよい。利用方法は調べたらもっとありそうなので、今後の課題とする。
* やはり、書いて動かしてみると理解が進みやすい。
* 実際にこのアルゴリズムを製品コードに組み込むケースを想定すると、若干の手直しが必要になりそうと思う。(グローバル変数の文字列をガシガシ編集したりするのはどうなんだろう、という観点)
ソフトマックス関数の微分


研究室スライドの練習がてら
#softmax関数とは？
$n$ 次元のベクトル $ \boldsymbol{x}=(x_1 ,x_2 ,\dots x_n), \boldsymbol{y}=(y_1 ,y_2 ,\dots y_n)$
に対して
$$ f(x_i)= softmax(x_i) = \frac{e^{x_i}}{\sum_{j=1}^{n} e^{x_i}}   (i=1,2,\dots,n)$$

である。
基本的にはニューラルネットワークでの多クラス分類によく使われるため、避けて通れない関数である。

ニューラルネットワークを学んでいてこの記事に行き着いたと思うのでそのつもりで説明していこうと思う。

まず簡単な例について考えましょう。<img width="665" alt="qiitaNN1.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/564429/48533956-d100-3080-6028-3af9a957fde0.png">
ソフトマックス関数とはニューラルネットワークで出力が$x_1,x_2$で出てきてそれをソフトマックス関数によって確率におきかえることによって各ラベルの可能性として出力するものなのです。
今上の図で $\boldsymbol{x}=(1,2)$ であるときの $\boldsymbol{y}$ について考えていきましょう。
分母の $\sum_{j=1}^{n} e^{x_i} $ は共通であるため先に計算します。
以降簡略化のため $Z=\sum_{j=1}^{n} e^{x_i} $ とします

$$Z=\sum_{j=1}^{2} e^{x_i}=e^{x_1}+e^{x_2}=e^1+e^2=10.107...$$

$$y_1=\frac{e^{x_1}}{Z}=\frac{e}{10.107..}\approx 0.269$$
$$y_2=\frac{e^{x_2}}{Z}=\frac{e^2}{10.107..}\approx 0.731$$

となるので $\boldsymbol{y}=(0.269,0.731)$ となります。
このことからソフトマックス関数の性質 $$\sum_{j=1}^{n} y_j=1$$ も確認できます。
要するにニューラルネットワークの出力が $\boldsymbol{x}$ だったときソフトマックス関数に通すことで $y_1$ のラベルである確率は26.9%, $y_2$ のラベルである確率は73.1%であると大雑把に言いかえることもできます。<img width="701" alt="qiitaNN2.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/564429/5585e403-86a3-a1e1-21ee-cf6b1d703893.png">


#softmax関数の微分
本題です。
なお
$$Z=\sum_{j=1}^{n} e^{x_i} $$
としています
ソフトマックス関数の微分は任意の$y_i$に対してどの$x_k$で微分するかいろんなパターンがありますので偏微分です。いきなり $k$ がでてきましが $k,i\in [1,n]$ です。

ソフトマックス関数
$$y_i=softmax(x_i) = \frac{e^{x_i}}{\sum_{j=1}^{n} e^{x_j}}$$
$k=i$の時の微分は商の微分を使い、次のようになるはずです。
$$\frac{\partial y_i}{\partial x_i}=\frac{e^{{x_i}^\prime}Z-e^{x_i}Z^{\prime}}{Z^2}$$
この中の微分について以下にくわしく
$$e^{{x_i}^\prime}=e^{x_i}$$
$$Z=\sum_{j=1}^{n} e^{x_j}=\dots e^{x_{i-1}}+e^{x_i}+e^{i+1}+\dots$$
$$Z^{\prime}=(\sum_{j=1}^{n} e^{x_j})^{\prime}=e^{x_i}$$
これらを代入すれば
$$\frac{\partial y_i}{\partial x_i}=\frac{e^{x_i}Z-e^{x_i}e^{x_i}}{Z^2}=\frac{e^{x_i}}{Z}(1-\frac{e^{x_i}}{Z})=y_i(1-y_i)$$

$i\neq k$では
$$\frac{\partial y_i}{\partial x_j}=\frac{-e^{x_i}e^{x_j}}{Z^2}=-y_i y_j$$

これらをまとめると
$$\frac{\partial y_i}{\partial x_j}=\begin{cases}
  \ y_i(1-y_i) 　　(i=k)\\\
  \
  \-y_i y_j　　　(i\neq k)
\end{cases}$$
となる。

一応これで微分結果は表せましたが、
$$\delta_{ij}=\begin{cases}
  \ 1 　　(i=k)\\\
  \ 0　　 (i\neq k)
\end{cases}$$
を用いることで、ニューラルネットでは以下の様に表されている場合が多い
$$\frac{\partial y_i}{\partial x_j}=y_i(\delta_{ij}-y_j)$$
Raspbianでswapを無効化

## はじめに

Rspberry Piでswapを有効化/無効化する手順について記載する。

## swapを無効化

1. 現在の状況を確認する。

    ```shell
    $ free
    ```

    ```
                  total        used        free      shared  buff/cache   available
    Mem:         378160       55372      231080        2736       91708      270744
    Swap:        102396           0      102396
    ```

1. swapを無効化する。

    ```shell
    $ sudo swapoff --all
    $ sudo systemctl stop dphys-swapfile
    $ sudo systemctl disable dphys-swapfile
    $ systemctl status dphys-swapfile
    ```

1. 設定変更されたことを確認する。

    ```shell
    $ free
    ```

    ```
                  total        used        free      shared  buff/cache   available
    Mem:         378160       64748      207916        2736      105496      261168
    Swap:             0           0           0
    ```

## swapを有効化

1. 現在の状況を確認する。

    ```shell
    $ free
    ```

1. swapを有効化する。

    ```shell
    $ sudo systemctl start dphys-swapfile
    $ sudo systemctl enable dphys-swapfile
    $ systemctl status dphys-swapfile
    ```

1. 設定変更されたことを確認する。

    ```shell
    $ free
    ```
Ubuntu系ゲストOSでWUXGA表示

## 概要

VMware PlayerのゲストOSにDebianをインストールし、X-Windowを起動したところ、WUXGA(1920x1080)表示できなかった。サポートしているディスプレイモードを表示しても、WUXGAの選択肢すらない。

そこで、WUXGAの選択肢を追加し、設定できるようにした。

## ディスプレイモードを確認

1. 現在、ディスプレイがサポートしているディスプレイモードを確認する。

    ```shell
    $ xrandr
    ```

    ```shell
    Screen 0: minimum 1 x 1, current 1024 x 768, maximum 8192 x 8192
    Virtual1 connected primary 1024x768+0+0 (normal left inverted right x axis y axis) 0mm x 0mm
       800x600       60.00 +  60.32  
       1600x1200     60.00  
       1680x1050     59.95* 
       1400x1050     59.98  
       1280x1024     60.02  
       1440x900      59.89  
       1280x960      60.00  
       1360x768      60.02  
       1280x800      59.81  
       1152x864      75.00  
       1280x768      59.87  
       1024x768      60.00  
       640x480       59.94  
    Virtual2 disconnected (normal left inverted right x axis y axis)
    Virtual3 disconnected (normal left inverted right x axis y axis)
    Virtual4 disconnected (normal left inverted right x axis y axis)
    Virtual5 disconnected (normal left inverted right x axis y axis)
    Virtual6 disconnected (normal left inverted right x axis y axis)
    Virtual7 disconnected (normal left inverted right x axis y axis)
    Virtual8 disconnected (normal left inverted right x axis y axis)
    ```

## ディスプレイ設定を一時変更

1. ディスプレイモードを追加するため、1920x1080モードのディスプレイ設定を確認する。

	```shell
	$ cvt 1920 1080
	```

	```shell
	# 1920x1080 59.96 Hz (CVT 2.07M9) hsync: 67.16 kHz; pclk: 173.00 MHz
	Modeline "1920x1080_60.00"  173.00  1920 2048 2248 2576  1080 1083 1088 1120 -hsync +vsync
	```

1. 実行結果のModeline以下をコマンドライン引数に指定し、`1920x1080_60.00`モードを新規登録する。

    ```shell
    $ xrandr --newmode "1920x1080_60.00"  173.00  1920 2048 2248 2576  1080 1083 1088 1120 -hsync +vsync
    ```

1. `1920x1080_60.00`モードが登録されていることを確認する。

    ```shell
    $ xrandr
    ```

    ```shell
    (中略)
    Virtual8 disconnected (normal left inverted right x axis y axis)
      1920x1080_60.00 (0x297) 173.000MHz -HSync +VSync
            h: width  1920 start 2048 end 2248 total 2576 skew    0 clock  67.16KHz
            v: height 1080 start 1083 end 1088 total 1120           clock  59.96Hz
    ```

1. `Virtual1`ディスプレイを`1920x1080_60.00`モードに設定する。

	```shell
	$ xrandr --addmode Virtual1 1920x1080_60.00
	```

1. `1920x1080_60.00`モードが`Virtual1`ディスプレイに登録されていることを確認する。

    ```shell
    $ xrandr
    ```

	```shell
	Screen 0: minimum 1 x 1, current 1024 x 768, maximum 8192 x 8192
	Virtual1 connected primary 1024x768+0+0 (normal left inverted right x axis y axis) 0mm x 0mm
	(中略)
	   1920x1080_60.00  59.96  
	Virtual2 disconnected (normal left inverted right x axis y axis)
	(以下、省略)
	```

1. `1920x1080_60.00`モードで出力するよう、`Virtual1`ディスプレイを設定変更する。

	```shell
	$ xrandr --output Virtual1 --mode 1920x1080_60.00
	```
flutterで一部だけスクロールさせたい

Expandedが大事で、Expanded抜きでListViewだけだとスクロールできない。

```dart

    Scaffold(
      appBar: ApplicationBar(
        title: 'HOME',
      ),
      body: Column(
        children: <Widget>[
          Text('固定表示させたい'),
          Text('スクロールさせたくない'),
          Expanded(
            child: ListView(
              children: <Widget>[
                Text('スクロールさせたい'),
                Text('スクロールさせたい'),
              ],
            )
          ),
        ],
      ),
      bottomNavigationBar: 省略,
    )
```

高さ指定したい時は、Expandedの代わりにContainerでwrapしてheightを指定してやれば良い
YOLOFaceで顔検出やーる（Windows10、Python3.6）

# はじめに
リアルタイムの物体検出アルゴリズムであるYOLOv3の顔検出版であるYOLOFaceをやってみました。CPUでも動くよ。

# システム環境
- Windows10（RTX2080 Max-Q、i7-8750H、RAM16GB）
- Anaconda 2020.02
- Python 3.6

# 導入
[yoloface](https://github.com/sthanhng/yoloface)をクローンします。

yoloface環境を作ります。

~~~
$ cd yoloface-master 
$ conda create -n yoloface python=3.6
$ conda activate yoloface
$ pip install -r requirements.txt
~~~

[yolov3-wider_16000.weights](https://drive.google.com/file/d/1xYasjU52whXMLT5MtF7RCPQkV66993oR/view?usp=sharing)をダウンロードし、model-weightsフォルダに置きます。

下記のコマンドを実行し、顔検出できるか確認しましょう。samplesフォルダに顔検出したいファイルを入れておきます。

- 画像の場合

~~~
$ python yoloface.py --image samples/outside_000001.jpg --output-dir outputs/
~~~

![meeting_11_304_yoloface.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63863/ed31368d-9924-71fb-a026-f0d372d4cc22.jpeg)
![outside_000001_yoloface.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63863/a325df9e-20ac-7505-6c76-48b7e9887128.jpeg)

かなり精度いいですね！

- 動画の場合

~~~
$ python yoloface.py --video samples/hoshino.mp4 --output-dir outputs/
~~~

![hoshino_yoloface_Trim.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63863/211d933f-3064-a942-954b-6013b70628e5.gif)


- ウェブカメラの場合（数値を変えることでカメラデバイスを切り替えられます）

~~~
$ python yoloface.py --src 1 --output-dir outputs/
~~~

![bandicam-2020-05-06-02-55-53-074.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63863/88b49b73-4f4c-e4a7-a049-03d8eef26d16.gif)

リアルタイムに顔検出できました。
お疲れ様でした。
Firebaseを利用するうえで苦しんだポイント

# Firebase
## Firestore
○Firestoreのルール設定で苦しんだ
Nuxtで作成したWEBサイトにアカウント作成機能を実装した。
アカウント作成時にアカウント情報をFirestoreに登録する機能を作成したかったが、何故か登録できずに苦しんだ。
公式サイト・解説サイトを回ったが、自分と同じ状態の人はいなかった。
解決策としては、**Firestoreのルール設定（セキュリティ設定）**を変更することで解決することができた。
慣れが危険なrootでのchownコマンドchmodコマンド

## はじめに
rootでのchownとchmodはちょっと打ち間違えるとサーバぶっ壊しますよ～油断禁物ですよ～とお伝えしたいので投稿。

## やるかもしれない例（危ないので実行厳禁）
### 現在ディレクトリ配下の所有者やパーミッション変更
```console
$ # 所有者変更
$ cd /home/testuser/targetdir
$ chown -R ${ユーザ名}:${グループ名} ./

$ # パーミッション変更
$ cd /home/testuser/targetdir
$ chmod -R ${パーミッション} ./
```
もし上記のように実行していたら是非やめることおすすめします。

#### 「./」の個所を誤って「/」と打った場合
対象が現在ディレクトリ配下でなく全てのディレクトリになってしまい様々な機能が壊れます。もとに戻すのはほぼ不可能。

よくサーバ壊すコマンドで話題に挙がる`rm -rf /`はCentOSバージョンによってはセーフティ機能が働く可能性があるが、上記はセーフティ機能は現状ないと思われるので実行されてしまう。

#### 「-R」使いたい場合
変更コマンドのディレクトリ指定を絶対パスにする、
または直前で`cd`コマンドで変更対象ディレクトリの1階層上まで移動し変更対象ディレクトリ指定する。

```console
$ # 変更コマンドのディレクトリ指定を絶対パスにする
$ chown -R ${ユーザ名}:${グループ名} /home/testuser/targetdir
$ chmod -R ${パーミッション} /home/testuser/targetdir

$ # 直前で`cd`コマンドで変更対象ディレクトリの1階層上まで移動し変更対象ディレクトリ指定
$ cd /home/testuser/
$ chown -R ${ユーザ名}:${グループ名} targetdir
$ chmod -R ${パーミッション} targetdir
```

## さいごに
「そんな打ち間違えしない」と過信しないでください。
人ってすぐケアレスミスするので。そのケアレスミスでサーバ壊したらシャレにならんので。
grasshopper C#コンポーネントクイックリファレンス

#趣旨
grasshopper上でC#コンポーネントを書く際のクイックリファレンス
個人の備忘録を兼ねるのでやれば増える予定。

公式ドキュメントへのリンク
https://developer.rhino3d.com

#out出力
outに文字や数字を出力する場合

```C#
//文字列の場合
Print("xxx");

///文字列以外
Print(xxx.ToString());
```

配列の中身を出力するメソッド

```C#
public double[] ArrayPrint(double[] n)
{
  double[] result = new double[n.Length];
  Print("[");
  for (int i = 0; i < n.Length; i++)
  {
    Print(n[i].ToString());
  }
  Print("]");
  return result;
}
```

#GH_path (グラフトとかする際のパス)
```C#
GH_path(n)
//nがそのままpathになります。

GH_path(0)
//　→　{0}
GH_path(0,0)
//　→　{0,0} 
```
直接"{0}"とかで指定することはできません。

#DateTree（多次元配列）
```C#
//ブランチ 
DataTree.Branch(GH_path);
//GH_pathで指定したlistを抽出する

//DataTreeに要素を追加
DataTree.Add(x, GH_path);
// x:追加したいオブジェクト

// listをまとめて追加はできないのでforeachで入れる
foreach(class item in List)
{
   xxx.Add(x, GH_path);
}
```

#Polyline
公式document [Polyline Curve](https://developer.rhino3d.com/api/RhinoCommon/html/T_Rhino_Geometry_PolylineCurve.htm)
使い方は難しくないので割愛します。
ただ注意点として、Polylineの配列の中をRefObjectに入れたい場合、Polylineになっていない要素（Pointが2つAddなりをされていない場合）が含まれている場合、配列全てが参照されずにEmptyになります。
その場合以下のようなエラーが出ます。

```C#
Script Exception: オブジェクト参照がオブジェクト　インスタンスに設定されていません
```
対策として以下のように要素数で1以下のものを切ってしまうといいかもしれません

```C#
if(Crv.Count() > 1)
{
  CrvList.Add(Crv);
}
```
Guardを使ってrspecテスト自動化

# rspecのテストを自動化する。

rails内で作業ファイルを保存した際に自動でテストが実行されるようにする。

まず下記のgemを追加。

```ruby
group :development, :test do

  gem 'rspec-rails'
  gem 'guard'
  gem 'guard-rspec'
```

その後bundle installして

```ruby
rails g rspec:install
```

これでrspecの準備は完了。

## guardを導入して自動化

下記のコマンドでGuardfileを作成する。

```ruby
bundle exec guard init
```

そして`bundle exec guard`で実装完了。

...のはずが、cannot load fileエラーが発生した。

原因はディレクトリに日本語が入ってしまっていたから。
なのでディレクトリの名前を変えて`bundle exec guard`で解決。
PDOで取得したデータが、数値型データなのに文字列データに変わってしまう原因

最近Haskellの勉強をしていて、型って大事だな〜っていうのを改めて思っていたんですが型でふと思い出したことがありました。
長年、PHPを使用して仕事をしているのですが、PDOを使って数値のデータを取得するとなぜか文字列に変換されてしまうあれです。
あれのせいで型を意識せずになんとなくPHPを使っている方も多いのでは？なんて思いながらHaskellを勉強していたのですが、そもそもあの訳わからない変換はなぜ起きてしまうのか、今回きちんと向き合ってみようと思いました。

###まずPDOとは
PHP Data Objectsの略。
PHPからデータベースへアクセスするのを便利にしてくれるもの。
もっとちゃんと書くと、MySQLやPostgreSQLなどデータベースには様々な種類があって、
本来であればそのデータベースごとにそれぞれ合わせた書き方をしてデータ取得をしなくてはならないところ、
このPDOを使用するとどんなデータベース相手でも同じ書き方で操作出来るようになる便利な子。

###今回の謎
PDOを使ってデータベースから数値のデータを取ってくると、文字列で返ってくる
つまり「100」が「’100’」で返ってきたりします。
気になる方は実際に試してみてください。

###なぜこうなるのか
いきなり結論ですが、PDOのプリペアドステートメントという設定が原因

###プリペアドステートメントとは
SQLを実行するときに「変更されると思われる箇所」を変数にしてSQLの命令文を作る機能のことです。
例えば

```sql

SELECT * FROM table_name WHERE table_id = 1;
```

table_idが1のデータを取りたいときはこのようにSQLを書きますが、table_idが2のデータを取りたいときはこうなります

```sql
SELECT * FROM table_name WHERE table_id = 2;
```
最後のtable_idの指定が変わっただけです。
このWHERE句に指定しているtable_idの値を変数化する動きのことをプリペアドステートメントといいます。
つまり、時と場合によって変更が必要と思われるSQLの一部を変数化して命令文を作る設定が「プリペアドステートメント」です。
ここで言われている変数というのは「バインド変数」と呼ばれています。

###メリットは？
①実際にSQLの全文を解析する必要があるのは最初の１回だけになり、あとはバインド変数を変えればいいだけなのでSQLの解析〜コンパイルまでの時間を短縮できる
　＝高速化
②SQL インジェクション（悪意を持ってSQLを書き換え、システムに害を与えるようなSQLを実行させる攻撃）に対して有効。なぜ有効かというと、SQLの解析が終わったあとにバインド変数を実際の値に置換するので、致命的なSQLインジェクションが発生する余地を極力減らしてくれる
　＝セキュリティ担保


上記の理由から見ても、プリペアドステートメントは基本的にONにしておいた方が良い設定だと言えます。
ですが、リペアドステートメントをONにしていると、本来は数値型のデータが文字列として返却されてしまうということです。
取得結果をキャストすれば良いかも知れませんが、値によっては予期せぬ結果になってしまいそうな気はします。
注意が必要ですね。
[swiftui]ForEachでid: \.selfが煩わしい場合のextension

ForEachでループする場合Identifiableのstructを使う方法とid: \.selfがあります。
これをしないとui上でどれがどれかプログラム上判断がつかなくなるので仕様として必要になります。

```
struct Hoge: Identifiable {
    let id: UUID
    var name: String
}

struct Test: View {
    var hoges = [Hoge]()

    var body: some View {
        VStack {
            ForEach(hoges) { hoge in
                Text(text: hoge.name)
            }
        }
    }
}
```

Identifiableをつけるのがルールであるが、プリミティブな型の場合はできないため、id: \.selfが必要になります。

```
struct Test: View {
    var names: [String]

    var body: some View {
        VStack {
            ForEach(names, id: \.self) { name in
                // do something
            }
        }
    }
}
```

めんどくさいですよね。extensionで整理

```
extension ForEach where Data.Element: Hashable, ID == Data.Element, Content: View {
    init(values: Data, content: @escaping (Data.Element) -> Content) {
        self.init(values, id: \.self, content: content)
    }
}
```

これでこうなります

```
struct Test: View {
    var names: [String]

    var body: some View {
        VStack {
            ForEach(values: names) { name in
                // do something
            }
        }
    }
}
```

これで良いね
Laravel の リレーションするfactoryで既存のIDを使う

#やりたいこと
リレーションするfacotryでidを毎回作ると、テストデータとしてSeederで入れたときエグい量になるので
作成済みのものにしたい
なかったら作成してもよい

#やりかた

```php:HogeFactory

<?php

use App\Models\{User,Hoge};
use Faker\Generator as Faker;

$factory->define(Hoge::class, function (Faker $faker) {
    return [
        'name' =>  $faker->name,
        'user_id' =>  optional(User::inRandomOrder()->first())->id ?? factory(User::class)->create()->id
    ];
});
```

`optional`で`User::inRandomOrder()->first()->id`がなくてもnullになるので
nullになったら`factory(User::class)->create()->id()`する

ただ一回作ると、後はおんなじのになってしまう

いい方法がないかな

#追記

既存のレコードからID取得
fakerのboolがtrueかつ、既存のレコードのIDがあったら、既存のを、
ない、または、boolがfalseになったらfactoryでcreateするようにした

いい感じにランダムになるからこれでようさそう

```php
<?php

use App\Models\{User,Hoge};
use Faker\Generator as Faker;

$factory->define(Hoge::class, function (Faker $faker) {
    $random_id = optional(User::inRandomOrder()->first())->id;
    $id = $faker->bool && $random_id ? $random_id : factory(User::class)->create()->id
    return [
        'name' =>  $faker->name,
        'user_id' => $id
    ];
});
```
Laravelでファイルアップロード機能を実装する

# 目次
Laravelの記事一覧は下記
[PHPフレームワークLaravelの使い方](https://qiita.com/toontoon/items/c4d0371e504c37f6576e)

# Laravelバージョン
動作確認はLaravel Framework 7.19.1で行っています

# 前提条件
[eclipseでLaravel開発環境を構築する。デバッグでブレークポイントをつけて止める。(WindowsもVagrantもdockerも)](https://qiita.com/toontoon/items/76fa7f8845b20776077c)
本記事は上記が完了している前提で書かれています
プロジェクトの作成もapacheの設定も上記で行っています

# Controllerにメソッド追加
(1) /sample/app/Http/Controllers/SampleController.phpにuploadメソッドを追記

```
    public function upload(Request $request)
    {
        $file = $request->file('a');

        if (!is_null($file)) {

            date_default_timezone_set('Asia/Tokyo');

            $originalName = $file->getClientOriginalName();
            $micro = explode(" ", microtime());
            $fileTail = date("Ymd_His", $micro[1]) . '_' . (explode('.', $micro[0])[1]);

            $dir = 'upFiles';
            $fileName = $originalName . '.' . $fileTail;
            $file->storeAs($dir, $fileName, ['disk' => 'local']);

        }

        return view('sample.upload');
    }
```

Request#fileメソッドでアップロードされたファイルを取得できます
storeAsメソッドでstorage/app配下にファイルを保存できます
storeAsメソッドの第一引数はstorage/app配下の保存先ディレクトリを指定します
storeAsメソッドの第二引数は保存後のファイル名を指定します
storeAsメソッドの第三引数はconfig/filesystems.php内のdisks配列のkeyをdisk要素で指定します
config/filesystems.php内のdisks配列を見るとわかりますが、そこでファイルの保存先のrootをstorage/app配下に指定しています


(2) /sample/routes/web.phpに下記を追記
`Route::post('sample/upload', 'SampleController@upload');`

# viewの作成
/sample/resources/views/sample/upload.blade.phpファイル作成

```upload.blade.php
<html>
    <head>
        <title>sample</title>
    </head>
    <body>

        <form action="{{ url('sample/upload') }}" method="post"  enctype="multipart/form-data">
            @csrf
            <input type="file" name="a" >
            <input type="submit" >
        </form>

    </body>
</html>

```

# 動作確認
`http://localhost/laravelSample/sample/upload`

ファイルを選択して
送信ボタンクリック


storage/app配下にupFilesフォルダができて、その中にアップロードしたファイルが格納されました
AWS ClientVPNを使って社内のPCにリモートアクセス

# 1. テレワーク
在宅勤務、テレワーク、必須な世の中になってきていますが、みなさん、どのように対応されていますか？

使い慣れた会社のデスクトップを、家からアクセスできるようにするのが一番便利かと思います。
AWSを使った、その方法について紹介します。

社内ネットワークがDirect ConnectやVPNでAWSのVPCと接続されているのが前提となります。

社内ネットワークの平均通信負荷もクライアント1台あたり0.1〜0.3Mbps程度で低めです。
ゼロトラストネットワークでもなく、今風のアーキテクチャではないですが...。

# 2. AWSの利用法
今回、使ったサービスです。

　・AWS Client VPN
　・AWS NLB
　・AWS EC2（Windows Serverで、RD Gatewayサービス)を複数台
　・AWS AD Connector
　・AWS CloudWatch

# 3. 構成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/632378/f3999f71-dacf-4443-d144-d4c3129b1ea3.png)

 RD Gatewayは、AZを分けて、NLBで冗長化しています。
 オンプレミスのADに対して、AD Connectorで接続して、Client VPNの認証用に使っています。
 Client VPNでは、RD GatewayやオンプレのDNSサーバーに対してのみ経路を開くことで、最小限の開放としています。
 Client VPNのログは、CloudWatchで確認できます。

# 4. 守りたいもの
在宅で社内のPCにアクセスできるとして、画面キャプチャを防げないのはあきらめ。
ファイルの流出やクリップボード経由の流出を守りたいところにこだわりたいです。

リモートアクセス時のその制御を行うのが、RD Gatewayです。
接続承認ポリシー(RD CAP)の設定で、デバイス リダイレクトを無効にすることで、実現できます。

# 5. Client VPNでハマりやすい点
・AWS製のClientソフトはおすすめしません。
 接続が不安定です。
 WindowsならOpenVPN、MacならTunnelblickが無難です。

・Client VPNは、仕様上限の2000同時アクセスにいつでも耐えられるわけではないです。
 使い方に合わせて、スケールアップしていく仕様となっています。
 Client VPNの接続数を多く確保するためには、Client VPNに関連付けしているAZを3つ以上確保することをおすすめします。
 また、Client VPNのサブネットは、/20 ビットマスクより多くのIPを確保することをおすすめします。
 /22 ビットマスクだと、1024個のアドレスが確保されますが、Client VPNの仕様だと可用性から半分の利用が推奨されているので、512同時接続までとなります。
　　https://docs.aws.amazon.com/ja_jp/vpn/latest/clientvpn-admin/what-is.html#what-is-limitations
 関連付けが1つで、立てたばっかりのClient VPNだと、100接続程度で飽和します。
 15分程度でスケールアップが実行されて、上限は緩和されますが、急激にはキャパシティは増えません。

・複数のClient VPNインスタンスを設ける際、IPアドレスは重なっても構いません。
 NATで切れているためです。
グリーンスクリーンを設置するのも大変だから顔だけ切り出して背景画像に重ねてみた

Webカメラから自分の顔を検出して、顔だけを背景画像に重ねてみました。　Web会議アプリにWebカメラとして映像を渡せば、お手軽にWeb会議に顔を出せます。
<img width="320" alt="treeman_example.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/138680/0363ae98-098d-9094-056b-4844c3878d45.png">
小さい頃なりたかった（？）木の賢者にもなれます。

注）目伏せ追加機能はありません。　目伏せはこの記事のためにスクショに後から手加工で追加しました。


# 課題
3月上旬から在宅でWeb会議ざんまいの日々になってます。
あまりに部屋が汚いのでビデオはなしで、音声と画面共有だけでなんとかやってきました。でもやはりWeb会議だけでは今までの信頼貯金を取り崩している気がします。たまに顔を映してくれる人がいると、とても嬉しく思ってる自分がいます。自分も顔出したい。　でも部屋が狭いし汚すぎる！

普通は諦めて部屋を片付けたてきれいにするか、グリーンスクリーンを設置してクロマキー合成でビデオにするのでしょう。でもやっぱり片付けられない。。。

会社で広く使われているWeb会議システムはWebExです。　Zoomのように背景変換機能はないし、Teamsのようにすぐ開発してくれる気がしない。。。　Zoomもたまに使ってるけど、マシンパワーが足りなくてグリーンスクリーン必須。でもグリーンスクリーンを設置する場所もない。。。

# 妄想

そういえばZoomは顔だけじゃなくて、手や上半身を背景から切り出している。だからヘビーな処理が必要なのか？？
顔検出だけと割り切れば非力なPCでもそんなに負荷なくできるはず！？

# 構想
こんな感じ。

Webカメラ --> OpenCVで顔領域検出 --> 顔領域だけ取り出して背景画像に重ね合わせ --> Webカメラの映像として出力 --> WebExやZoomで利用

[このブログ記事](https://medium.com/analytics-vidhya/facial-landmarks-and-face-detection-in-python-with-opencv-73979391f30e)を見るとやはりOpenCVでFace detectionはすぐできそう。
切り出しや重ね合わせはPython Imaging Library (Pillow)でやったことある。
１秒間に5回ぐらい処理できれば、Web会議で使うには十分な映像になるはず。
さて、作った画像をWebカメラの映像として出力するのはどうしたらいいか？　OpenCVにそんな機能はなさそう。PythonからもMacやWindowsで使えるライブラリは見つからず。

あまりスマートではないけど、MacOSならCamTwistで画面の一部をキャプチャーして他のソフトにWebカメラとして認識させて渡すことができるらしい。

# やってみた

コードを[GitHub](https://github.com/aerialist/treeface_cam)に上げておきました。
自分が書いた部分はPythonですが、OpenCVとQtを利用しています。
GW中なのでまだ実戦投入してません。早く次のWeb会議にならないかなぁ。
Nuxt×CloudFunctions×SendGridでメールを送信する

複雑な家庭の事情でどうしてもNuxt×CloudFunctions×SendGridでメールを送信する必要があったのでメモ。


## Functionsの実装
### ライブラリのインストール
Node.js用のライブラリがあるのでこれをインストールします。
https://github.com/sendgrid/sendgrid-nodejs

```
cd functions
npm install --save @sendgrid/mail
```

### SendGridのAPIキーをセット
APIキーはfirebaseプロジェクトの環境構成にセットしちゃいます。

```
firebase functions:config:set sendgrid.apikey="hogehoge"
```

詳しく知りたい場合は[公式説明で](https://firebase.google.com/docs/functions/config-env?hl=ja)

### index.jsを編集
```/functions/index.js
const sgMail = require('@sendgrid/mail')
sgMail.setApiKey(functions.config().sendgrid.apikey)

exports.sendMail = functions
  .region('asia-northeast1')
  .https.onCall(
    async (data, context) => {
      const msg = {
        to: data.to,
        from: 'no-reply@test.com',
        cc: data.cc || '',
        bcc: data.bcc || '',
        replyTo: data.replyTo || '',
        templateId: data.templateId,
        dynamic_template_data: data.dynamic_template_data
      }
      try {
        await sgMail.send(msg)
      } catch (error) {
        console.error(error);
        if (error.response) {
          console.error(error.response.body)
      }
  }
})
```

## Vueの実装
以下のような、招待メールを送るサンプルです
<img width="500" alt="sample.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253912/4af5f97c-7a8c-3e8e-c462-682b91eae976.png">

```index.vue
methods: {
  async sendMail(to_email, recipient_name, team_id, invite_url) {
    const sendInvite = await firebase
      .app()
      .functions('asia-northeast1')
      .httpsCallable("sendMail");
    sendInvite({
      to: to_email,
      templateId: '#SendGridのメールテンプレートのID',
      dynamic_template_data: {
        subject: 'TESTへの招待が届いています',
        name: recipient_name,
        team: team_id,
        url: invite_url
      }
    })
    .then((res) => {
      console.log(res.data)
    })  
  }
}
```

SendGridにはDynamicTemplatesという機能があり、作成したメールテンプレートのIDをtemplateIdとしてパラメータにセットし、テンプレート内で使用したい値をdynamic_template_dataの中に格納して渡すと、メール文面にいい感じにセットされます。
bootstrap4 card 行間・高さ・位置調整し、ぬるっと拡大する

# bootstrap4 card 行間・位置調整し、ぬるっと拡大する
※個人作業レベルのメモです
※公式ドキュメントは以下
　https://cccabinet.jpn.org/bootstrap4/components/card

・完成状態
<img width="412" alt="スクリーンショット 2020-05-06 0.48.52.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172696/67e2cc4d-ceca-602a-1c6a-696f7dcef159.png">

・拡大後
<img width="405" alt="スクリーンショット 2020-05-06 0.49.02.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172696/719004fd-3b75-0d61-69dc-b8e37617f3ff.png">


・要素の真ん中に表示する
　divタグのclassに「mx-auto」を指定する

・行間調整する
　pタグに「style="line-height:XXrem"」を指定する

・高さ調整する
　pタグに「style="height:Xrem;"」を指定する
　img、タグのclassに「height:XXrem;」を指定する
　※各要素で高さが決まっている為、各要素のstyleに指定すれば調整可能

```html:qiita.html
  <div class="mx-auto" style="width: 20rem; height:25rem;">
    <div class="card zoom">
      <img class="card-img-top" src="XXX" alt="XXX" style="width: 20rem; height: 12rem;">
      <div class="card-body">
        <h4 class="card-title">TEST</h4>
        <p class="card-text" style="line-height:1.5rem;text-align:left;height:6rem;">
          TEST</br>
        </p>
        <a href="XXX"class="btn btn-secondary btn-sm">Demo</a></p>
      </div>  
    </div>
  </div>
```

・ぬるっと拡大する
以下コードをcssに記載し、
上記htmlのclassに指定する。

```css:qiita.css

    .zoom {
      -webkit-transition: all 0.5s ease;
      transition: all 0.5s ease;
    }
    .zoom:hover {
      cursor: pointer;
      transform: scale(1.1, 1.1);
      box-shadow: 0 0 20px black;
    }

```
UdonSharp日記～ObjectOwnerの変更～

#やったこと
前回の道の動的生成ではObjectOwnerの課題がありましたので、それを解決します。
具体的には、あるオブジェクトをピックアップした際に動的生成を行っているオブジェクトのOwnerを変更します。

加えて、十字のモデルを部屋っぽいモデルに変更しました。
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">blenderとUdonSharpをこねこね。モデル変更とpickupオブジェクトを介して、統括のGameObjectのOwnerを変更するギミックを作成したよ。<br>(このワールドは同期変数から新たに部屋を作成しているので、同期変数に書込みできると新たな部屋を作れる。つまり部屋を作れる = Owner)<a href="https://twitter.com/hashtag/MadeWithUdon?src=hash&amp;ref_src=twsrc%5Etfw">#MadeWithUdon</a> <a href="https://twitter.com/hashtag/UdonSharp?src=hash&amp;ref_src=twsrc%5Etfw">#UdonSharp</a> <a href="https://t.co/9r07t79Ws7">pic.twitter.com/9r07t79Ws7</a></p>&mdash; はんちょ (@sadimensions) <a href="https://twitter.com/sadimensions/status/1257689011545468928?ref_src=twsrc%5Etfw">May 5, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

＃参考文献
[黒鳥さんのUdonメモ](https://kurotori4423.github.io/KurotoriMkDoc/VRChat/UDON%20%E3%81%A7%E3%82%AE%E3%83%9F%E3%83%83%E3%82%AF/#setowner)　SetOwner

#やり方

```C#:
    //public変数の宣言抜粋
    //----Objectオーナー変更機能OnerChange----
    //名前空間 OC-----------------------------
    public GameObject OC_Cube;//オーナー変更のきっかけとなるキューブ
    //----------------------------------------
    //Owner変更処理部分抜粋
    //----Oner Change機能 の実装----
    public void OC_Update()
    {
        //キューブのオーナーを得る
        var cubeOwner = Networking.GetOwner(OC_Cube);
        if (Networking.IsOwner(Networking.LocalPlayer, OC_Cube))
        {
            //キューブのオーナーが自分かつこのスクリプト（道を生成しているスクリプト）の
            //オーナーが自分ではない場合
            if (Networking.GetOwner(this.gameObject) != cubeOwner)
            {
                //このスクリプトのオーナーを自分に変更する
                Networking.SetOwner(Networking.LocalPlayer, this.gameObject);
            }
        }
    }
```
![無題.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/627563/a61d68bb-e3f6-ab69-eca7-ad11db08e5b2.png)

この方法でオーナーを変えたいオブジェクト以外を操作（今回は他オブジェクトのオーナーになる）することで、目的のオブジェクトのオーナーを変更することができます。

#失敗談
他のオブジェクトのUdonSharpコードから他のオブジェクトのOwnerを変更することはできません。


SetOwnerの関数は第二引数にgameObjectを取ります。

```C#:
Networking.SetOwner(Networking.LocalPlayer, gameObject);
```

そのためgameObjectCubeさえ指定ができれば、どこからでもOwnerを変更できると考え
”Cubeの方に”下記のUdonSharpを組みました。

```C#:
    public override void OnPickup() {
        //managerはpublic変数でとってきた道の動的生成をするgameObject
        Networking.SetOwner(Networking.LocalPlayer, manager);
    }
```

cubeをピックアップした際にcubeが参照しているmanagerというgameObjectのOwnerを自分に設定することを意図しているのですが、これは意図通りに動作しませんでした。

他のオブジェクトから該当オブジェクトのOwnerを変更出来たら、同時にOwnerを主張する他オブジェクトをたくさん作れてしまうので、ちょっと無理のある考え方でした。
rake実行時の NameError: uninitialized constant Xxxxx の解決について

# 今回の問題
rakeタスクを実行して以下のようなエラーがでた。

ただしこのエラーが出て条件として、「**別プロジェクトからコピーで取ってきたrakeを実行**」して通常見ないエラーが出現した。

```sh:rake実行後のエラー
rake aborted!
NameError: uninitialized constant AutoRakeTask
```

# 解決
rakeのタスク名と一緒に`:environment`を付与することで解決した。

```ruby:before
  task :exec do
```

から

```ruby:after
  task exec: :environment do
```

へ変更したら、事象が解決した。

ちなみに、environmentについて調べた結果、以下の記事にたどり着いたが、記載されていたエラー文も同じものだった。
参考：[Rails における rake タスクの :environment について](https://qiita.com/FumiyaShibusawa/items/11035fc640bb36a615ad)
UnrealEngine4のお勉強④DLCをローカルサーバで確認する

この記事は[UnrealEngin4のお勉強③の記事](https://qiita.com/kotarotaro_A/items/97140ec8f9e4ddd7738c)の続きになります。
そちらを読んでからご覧ください。

先述の記事でUE4でのDLCについてビルドするとこまで完了したので、
この記事では自分のローカルサーバにDLCをアップロードし、アプリからDLして利用できることを確認します。
あくまでローカルでの確認用の作業です。
僕が実際に使ってみて確認が楽だった方法のご紹介になります。
サーバとかばりばり詳しいマンの方々はさくっとすっ飛ばして、必要な部分だけお読みください。

### サーバの準備
さて、ローカル確認用のサーバ環境ですが、Windows IISを使います。
僕の環境はWindows10なのでその手順になります。

「コントロールパネル」→「プログラム」→「Windowsの機能の有効化または無効化」→「インターネットインフォメーションサービス」にチェックを入れて「OK」ボタン。
完了したらブラウザを開いて、http://localhost/ を開いて以下の画像が表示されればOKです。
![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212804/cc1bc8fc-15e9-c322-699d-957613e77bac.png)

次に、IISマネージャーを開きます。
windowsの検索バーに「IIS」と入力すると出てきます。
「ディレクトリの参照」→「有効にする」（右端にあります）
![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212804/179e2916-83bb-8d31-b683-d921a722006e.png)
![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212804/454ab9d9-39a7-7082-4b80-7eff39945e48.png)

引き続きIISマネージャー上で「MIMEの種類」→「追加」（右端にあります）
ファイル名の拡張子には「.chunk」、MIMEの種類には「text/plain」をそれぞれ設定します。
![4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212804/9df29fe9-06c5-8ef5-f16a-bef9e0847b8f.png)

これでサーバ環境はできたはずですので、必要なファイルをサーバにあげます。
自分のPCのCドライブ直下に「inetpub」というディレクトリがあることを確認し、その中の「wwwroot」ディレクトリの中に移動します。
そこがhttp://localhost/ です。
さてここに、「dlc」というフォルダを新規作成しておきます。

そして、前回の記事でビルドしたDLCの出力結果のディレクトリに行きます。
以下の3つのディレクトリができているはずですので、

- 1
- CloudDir
- ManifestDir

3つとも
C:\inetpub\wwwroot\dlc
ここにコピーします。

ここまでできたらいったんブラウザから
http://localhost/dlc/ManifestDir/DlcSample_pakchunk11.manifest
http://localhost/dlc/CloudDir/ChunksV3/[数字]/[hogehoge].chunk
この2つをたたいてみてください。
manifestファイルとchunkファイルがDLできればOKです。

### アプリでの確認
それではお待たせしました。
実際に作成したアプリでDLができることを確認していきましょう。

前回の記事で作成したexeを実行します。
ManifestURL = http://localhost/dlc/ManifestDir/DlcSample_pakchunk11.manifest
CloudDir = http://localhost/dlc/CloudDir
InstallDir = Content/1
とそれぞれ入力したら、DLボタンを押下。
DL処理が完了したら、
OpenLevel = LevelPak1
と入力して、OpenLevelボタンを押下。

これでLevelPak1に無事遷移できれば成功です。

引き続き
ManifestURL = http://localhost/dlc/ManifestDir/DlcSample_pakchunk21.manifest
CloudDir = http://localhost/dlc/CloudDir
InstallDir = Content/2
とそれぞれ入力したら、DLボタンを押下。
DL処理が完了したら、
OpenLevel = LevelPak2
と入力して、OpenLevelボタンを押下。

これで、LevelPak2に遷移できればこちらも問題ありません。

（当然ですがlocalhostの部分を自身のローカルIPアドレスにしても問題ありません）

ちなみに。
アプリを再起動した場合、そのままLevelPak1に遷移することはできません。
必要なコンテンツのDLは済んでいますが、Mount処理がされていないので利用することができません。
今回作成したアプリの場合、マウントのみを実行する処理を組んでいないので、
再度MnifestURL、CloudDir、InstallDirの3つを同様に入力してDLボタンを押下する必要があります。

ただ、DL処理が完了している場合は、最初のManifestファイルを読み込んだ段階でインストール処理は飛ばしてくれるので、
何度もDLしてしまう恐れはありません。
マウントの処理だけ通ってくれるのでレベル遷移することが可能になります。

### 終わり
2つの記事にわたって説明が長くなりましたが、以上でUE4でのDLCの実装・ビルド・確認方法のご紹介を終了します。
ただ、DLC機能はものすごく奥が深く、また作成されるアプリの開発方針等にもよって非常に頭を悩ませること間違いなしです。

- どこまでアプリに入れて、どこまでDLCにするか
- すでに開発が進んでディレクトリ構成が入り組んでいるプロジェクトにどうやってPrimaryAssetLabelで設定を入れていくか

などなど。
当然、今回紹介しきれていない機能もUE4の中にたくさんあるので、ある程度使いこなせば解決することもたくさんあります。
要は、頑張って片っ端から試してみる根性が大切です。

まだまだ書きたいネタがあるので、[次回](https://qiita.com/kotarotaro_A/items/a6892e0c305c6a40d06c)はDefaultPakFileRules.iniについて書きたいと思ってます。
Dockerとは？

#1.Dockerとは
乱暴に言うと、ソフトウェア単位に仮想化環境で動かすためのアプリケーションです。
Dockerの環境を入れて、Dockerで管理する「コンテナ」という単位でソフトウェアをセットアップしても、他のソフトへの競合もなく、セットアップしたコンテナを他のDockerにコピーして素早く同じ環境を構築できます。

VMWare、Hyper-V、KVMなどと違うところは、これらがマシン単位での仮想化であるのに対し、Dockerはアプリケーション単位での仮想化であるということです。
OSはDockerが稼働しているOSをそのまま利用します（利用には制限がかかっており、コンテナの外の操作はできないようになっています）。


#2.Dockerと仮想化環境（マシン単位のもの）との比較

比較として大きなところで下記のものがあります。

| 項目 | Docker | 仮想化環境 |
|:-------:|:-------:|:-------:|
| 仮想化対象  | OS上のアプリケーションの動作をサンドボックス化する<BR>仮想化された範囲をコンテナとして管理  | ハードウェアをエミュレーション<BR>仮想化された設定内容やストレージ、メモリを管理  |
| 実行対象  | アプリケーションソフトとデータ  | OS上で動く全て  |
| メリット  | ・仮想化単位はアプリケーションとデータのみなので、容量が小さい<BR>・ほかのコンテナ、OS実環境からは直接アクセスできないため、不正アクセスしづらい、されてもOS本体、他のコンテナにアクセスできない |OS、アプリケーションがセットアップされた完成された環境になるため、物理ハードやOSバージョン,ハードウェアに左右されない|
| 制限等 | ・OSのコマンドや環境はDockerを動作させているOSの種類、バージョンが異なると動作しない場合がある。|OSとアプリケーションのセットのため、容量が大きい。また、仮想環境実行のための設定ファイル等が多い|
|その他| 実行したいアプリケーションのコンテナがインターネットで公開されているため、構築しやすい|1つのハードウェアで様々なOS、バージョンのアプリケーション環境が動作する|
【Python】pickle モジュールを使用すると、オブジェクトをファイルに保存することができる

# pickle モジュールを使用すると、オブジェクトを保存することができる

Python にて、辞書型のデータをローカル上に保存する処理を作成する際に、
「あれ？どうやって保存するんだ？」と疑問に思い、調べた結果、
`pickle`モジュールの存在を初めて知ったので、学習ノートとして残しておきます。（最終的に JSON に保存しました）

## 要約
- pickle モジュールを使用すると、オブジェクトを別ファイルにバイナリ形式として保存することができる。
- しかし、pickle モジュールはセキュリティ的観点より、使用はあまり推奨されていない。
- また、保存したファイルは Python 固有のものである。

## pickle モジュールについて

pickle モジュールを利用するとオブジェクトをバイナリ形式でファイルに保存することができます。

> pickle モジュールは Python オブジェクトの直列化および直列化されたオブジェクトの復元のためのバイナリプロトコルを実装しています。
[Python公式ドキュメント：pickle](https://docs.python.org/ja/3/library/pickle.html)

ちなみに「pickle」とは、「漬物」という意味だそうです。すぐに消えてしまうオブジェクトを保存するということから来ているのでしょうか。なんかかっこいい

## pickle モジュールを使ってみる

### ファイル作成

バイナリ形式の書き込みモードでファイルを開き、保存したいオブジェクトを`pickle.dump()`ですれば、保存できます。

```python:createPickle.py
import pickle

d = {"name":"ndj", "age":25, "hobby": "YouTube"}
with open("sample.pickle", mode="wb") as f:
    pickle.dump(d, f)
```

### ファイル読み込み

バイナリ形式の読み込みモードでファイルを開き、`pickle.load`すれば読み込めます。

```python:loadPickle.py
import pickle

d = {}
with open("sample.pickle", mode="rb") as f:
    d = pickle.load(f)
```

### 辞書型以外のオブジェクトも保存することができる

ちなみに辞書型以外のオブジェクトも同様の方法で保存することができます。

```python:otherPickle.py
import pickle

def dumpPickle(fileName, obj):
    with open(fileName, mode="wb") as f:
        pickle.dump(obj, f)

dumpPickle("lst.pickle", [1,2,3,4,5])
dumpPickle("str.pickle", "helooooooo!!")
dumpPickle("int.pickle", 11114)
```

読み込みも同様です。

```python:otherPickle.py
import pickle

def loadPickle(fileName):
    with open(fileName, mode="rb") as f:
        return pickle.load(f)

lst = loadPickle("lst.pickle")
_str = loadPickle("str.pickle")
_int = loadPickle("int.pickle")
```

## 使用には注意が必要らしい

Python の公式ドキュメントの Pickle のページトップには警告が記述されています。

> 警告 The pickle module is not secure. Only unpickle data you trust.
It is possible to construct malicious pickle data which will execute arbitrary code during unpickling. 
[Python公式ドキュメント：pickle](https://docs.python.org/ja/3/library/pickle.html)

あんまり自信がないですが、ファイルの中身をオブジェクトに変換する過程でファイルの中身がコードだった場合、実行されちゃうよってことでしょうか。。
意図しないコードが実行されたら困るので、使い所には気を付ける必要がありそうです。

## 参考
[Python 公式：Pickle](https://docs.python.org/ja/3/library/pickle.html)
SQLのWITH句の使い方や感想

WITH句を使うと見やすくなるとなんかで聞いたので使いました。
使い方数点共有や感想述べます。

## WITH句とは
副問い合わせ（サブクエリ）に名前を付けれる機能。
一時テーブルみたいなもの。

```html:With句構成例
-- WITH句
WITH test AS (
SELECT * FROM テーブル名 WHERE 条件
)

-- WITH句テーブル検索
SELECT * FROM test;
```
WITH句内のSQLが先に実行される。

## どんな使い方すればよいの？
数点思いついたり目にした。

例①：抽出項目ごとにWITH句作成し最後にJOIN
例②：複雑なUPDATEしたいとき処理単位ごとにWITH句作成しUPDATE
例③：複雑な検索結果ごとにWITH句作成し最後にJOIN等
例④：さわりたくない複雑なSQLにちょっと条件加えたい、カウントしたい

### 例①：抽出項目ごとにWITH句作成し最後にJOIN

```html:例：「氏名」「社員番号」「メールアドレス」「ID有効期限」を抽出
-- 「氏名」「社員番号」抽出
WITH 社員情報 AS (
SELECT ID,氏名,社員番号 FROM 社員情報テーブル WHERE なんか条件
)

-- 「メールアドレス」抽出
, メールアドレス AS (
SELECT ID,メールアドレス FROM 属性テーブル 
WHERE 属性名 = 'メールアドレス'
)

-- 「ID有効期限」抽出
, ID有効期限 AS (
SELECT ID,ID有効期限 FROM 属性テーブル 
WHERE 属性名 = 'ID有効期限'
)

-- WITH句JOINしてSELECT
SELECT 
  社員情報.氏名
  , 社員情報.社員番号
  , メールアドレス.メールアドレス
  , ID有効期限.ID有効期限
FROM 
  社員情報
  JOIN メールアドレス
    ON 社員情報.ID = メールアドレス.ID
  JOIN ID有効期限
    ON 社員情報.ID = ID有効期限.ID
;
```

上記くらいの抽出であればわざわざWITH句使わなくてもよいと思うが、
例えば抽出項目が増えて、たくさんJOINやJOINの際ANDしないといけなくなった場合、
とても見づらいSQLになってしまうのでそんな場合に有効。

### 例②：複雑なUPDATEしたいとき処理単位ごとにWITH句作成しUPDATE

```html:例：ポスグレ配列内のID有効期限の値が残り10日以内のものだけ有効期限を30日延長

-- ID有効期限の所持例：{20200507.20201010,20200513}
-- 処理対象求め → 配列展開 → 対象30日延長した値表示 → 再配列化 → UPDATEでいけるはず

WITH 処理対象ID AS (
-- 処理対象IDと有効期限配列をSELECT
)

, 処理対象の有効期限の配列展開 AS (
-- 頑張ってunnestとwith ordinalityを使い配列展開
FROM 
  処理対象ID 
)

, 有効期限が残り10日以内のもの30日延長 AS (
-- 頑張ってCASE文とか使い、
-- 残り10日以内の有効期限の表示を30日延長した値を表示するSELECT
FROM 
  処理対象の有効期限の配列展開
WHERE 
  ${有効期限が残り10日以内のものだけ}
)

, 有効期限を再度配列化 AS (
-- 配列再度配列化するSELECT
FROM 
  有効期限が残り10日以内のもの30日延長
)

-- 最後のWITH句を使ってUPDATE
UPDATE
  属性テーブル
SET
  有効期限 = 更新後有効期限
FROM 
  有効期限を再度配列化
WHERE
  ${「有効期限を再度配列化」に存在するID}
;
```


上記を一つのUPDATE文だけで表現するとなかなかの見づらさだと思うが、
処理単位ごとに分ければまだ見やすいはず。。。行数長くなるが。。。

また、分けたほうがデバッグもしやすい。
バグってそうなWITH句直後にSELECT文入れれば途中経過SELECTできる。

```html:例：デバッグ

~~~ いろんな処理  ~~~

, バグってそうなWITH句 AS (
-- バグってそうなSELECT
)

-- デバッグ!!!!!
SELECT * FROM バグってそうなWITH句;
```

### 例③：複雑な検索結果ごとにWITH句作成し最後にJOIN等

```html:例：2つのテーブルのそれぞれ条件にあったIDを抽出したい

WITH 複雑な検索結果１ AS (
SELECT ID FROM 社員情報テーブル WHERE ${なんか条件}
)

, 複雑な検索結果２ AS (
SELECT ID FROM 属性テーブル WHERE ${なんか条件}
)

-- 複雑な検索結果１２両方存在するIDを求めたい
SELECT 
  複雑な検索結果１.ID
FROM 
  複雑な検索結果１
  , 複雑な検索結果２
WHERE
  複雑な検索結果１.ID IN ( 複雑な検索結果２.ID )
;
```

上記のような使い方してる方もいた。

### 例④：さわりたくない複雑なSQLにちょっと条件加えたい、カウントしたい

```html:例：さわりたくないSQL結果をカウントしたい

WITH さわりたくないSQL AS (
-- さわりたくないSQL
)

-- カウントしたりちょっと条件加える
SELECT 
  count(*)
FROM 
  さわりたくないSQL
WHERE
  ${ちょっと加えたい条件}
;
```

上記は案外使う場面あった。
ただカウントしたいだけなのに数行なおさなくてよかったり、
たくさんJOINしててどこに条件加えればよいかパッと見づらい場合とか。

## 感想（メリットやデメリット）
### メリット
①見やすい
プロジェクト特有のデータ持ち方見慣れていない人には助かる気がする。
1個所にたくさんJOINされているSQLやっぱ見づらい。

### デメリット
①行数が長くなりがち
プロジェクト特有のデータ持ち方マスターしている人にとっては1個所にたくさんJOIN集まってる方が見やすいかも？

②WITH句内の検索結果多いとパフォーマンスに影響
WITH句検索結果はメモリ上に保存されるので、検索結果を多くなりすぎないよう工夫が必要な場合があるかも。
rails tutorial (rails6)備忘録3

【環境】
・mac OS Mojave
・バージョン10.14.6（18G2022）
・Visual Studio Code 

学習テーマ
rails tutorialの勉強の備忘録として残す。

1. [rails tutorial (rails6)備忘録1](https://qiita.com/m_masashi/items/0c9c2b81ca735f61a9c3)
1. [rails tutorial (rails6)備忘録2](https://qiita.com/m_masashi/items/34f37fcf9457868b357f) 



```terminal
$ rails generate scaffold User name:string email:string
$ rails db:migrate
$ rails server
```
Scalaで各種ソートアルゴリズムを実装する

これは [GW Advent Calendar 2020 俺GW頑張った](https://gw-advent.9wick.com/calendars/2020/82) の6日目の記事です。
前日の記事は `Fediverseにいます` さんの いろいろした です。
翌日の記事は `しそのは とうふ` さんの [結果何かやったかと言われたら微妙](https://nantai-yuto.hatenablog.com/entry/2020/05/05/230901) です。

# はじめに
ソートアルゴリズムの復習とScalaの学習を兼ねて、各種ソートアルゴリズムをScalaで実装してみる。
ソースは [nhiguchi/sort-samples](https://github.com/nhiguchi/sort-samples) にアップした。

# 基本trait
```scala
trait Sorter {
  def sort(seq: Seq[Int]): Seq[Int]
}
```

# 各種ソートアルゴリズム

## 挿入ソート
```scala
object InsertionSorter extends Sorter {
  override def sort(seq: Seq[Int]): Seq[Int] = {
    seq.foldLeft(Seq.empty[Int]) { (seq, e) =>
      val index = seq.indexWhere(_ > e)
      if (index == -1) {
        seq :+ e
      } else {
        seq.take(index) :+ e :++ seq.drop(index)
      }
    }
  }
}
```

## マージソート
```scala
object MergeSorter extends Sorter {
  override def sort(seq: Seq[Int]): Seq[Int] = {
    def _sort(leftSeq: Seq[Int], rightSeq: Seq[Int]): Seq[Int] = {
      (leftSeq, rightSeq) match {
        case (left, Nil)  => left
        case (Nil, right) => right
        case (leftHead :: leftTail, rightHead :: rightTail) =>
          if (leftHead < rightHead) leftHead +: _sort(leftTail, rightSeq)
          else rightHead +: _sort(leftSeq, rightTail)
      }
    }
    val pivot = seq.size / 2
    if (pivot == 0) seq
    else {
      val split = seq.splitAt(pivot)
      _sort(sort(split._1), sort(split._2))
    }
  }
}
```

## クイックソート
```scala
object QuickSorter extends Sorter {
  override def sort(seq: Seq[Int]): Seq[Int] = {
    if (seq.size <= 1) seq
    else {
      val pivot = seq(seq.size / 2)
      sort(seq.filter(_ < pivot)) ++
        seq.filter(_ == pivot) ++
        sort(seq.filter(pivot < _))
    }
  }
}
```

# まとめ
Scalaは高レイヤ処理が得意だが、関数型言語は低レイヤ処理にも強いことを改めて感じた。
これ以上複雑なソートになると実装が大変だが、気が向いたら残りのソートも実装してみたい。

# 参考サイト
- [ソートを極める！ 〜 なぜソートを学ぶのか 〜](https://qiita.com/drken/items/44c60118ab3703f7727f)
[Rails] has_secure_passwordについて


最近使う機会が増えてきたhas_secure_passwordについてまとめてみました。

***


```
class User < ApplicationRecord
  .
  .
  .
  has_secure_password
end
```

***

##具体的なメリット

1.セキュアにハッシュ化したパスワードを、データベース内のpassword_digestという属性に保存できるようになる。
2.2つのペアの仮想的な属性 (passwordとpassword_confirmation) が使えるようになる。**また、存在性と値が一致するかどうかのバリデーションも追加される。**
3.authenticateメソッドが使えるようになる (引数の文字列がパスワードと一致するとUserオブジェクトを、間違っているとfalseを返すメソッド)。

***

##has_secure_passwordを使うための条件

1.gemに'bcrypt'を追加する。
2.モデルにpassword_digest属性を追加する。

***

##注意点


メリットの2つ目のバリデーションは新規登録の際のみに適用されるので、更新で空文字列であれば登録できてしまう。→**追加でpresenceのバリデーションを自分で書く必要がありそうですね。**
リモート勉強会のススメ（By connpass)

#はじめに
5月になって急に暖かくなり始めましたねー。
みなさまはいかがお過ごしでしょうか。
コロナの影響で自宅から出られないのは、正直気が滅入っちゃいますよねー

こういう時期は一人でいるよりも誰かと話すと気持ちが楽になります。
＋プラスせっかく時間があるのですから、この時間を自己研鑽に当てれれば最高です。

そこでおすすめなのが「リモート勉強会」です！！

今回は普段僕が利用している勉強会プラットフォーム「connpass」で
勉強会に参加する方法をかんたんに紹介したいと思います。

#勉強会に参加する方法
##1.connpassに登録する
まずはconnpassにユーザー登録します。[こちら](https://connpass.com/dashboard/ "こちら")からアクセスできます。
![2020-05-05_23h03_20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/338998/ff1de4b2-b7e7-b1e5-fe6f-5d609a3e1015.png)

メールアドレスの他、FacebookやTwitterのアカウントでもログインできます。
各アカウントで登録しておけば、各アカウントの友達が関係しているイベント情報が表示されるようになります。
Githubでも登録できますが、誰でもアカウント画面からGithubのページに遷移できてしまうので、ちょっと注意が必要です。

あとやっておくと便利なのが、Googleカレンダーとの連携です。
勉強会へ参加登録すると自動的にカレンダーに反映されます！
「利用設定画面」で登録できます。
![2020-05-05_23h19_42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/338998/8907cd03-19dc-34c4-4990-a99d604a5efa.png)

##2.勉強会を探す
connpassに登録が完了したら、さっそく参加してみたい勉強会を探してみましょう。
キーワードに自分が興味のある分野や、参加してみたい勉強会の種類を入力します。

* ひたすらモクモク勉強したい-> 「もくもく会」
* 有識者の発表が聞きたい->「LE」

![2020-05-05_23h22_56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/338998/ac6871f1-7d05-9de8-9f14-5f46859a49b5.png)

##3.勉強会に参加登録する
いい感じの勉強会が見つかったら参加登録します。
参加登録する前にまず募集枠を確認します。
募集人数が満員になっていた場合は、補欠参加となり、欠員が出たときに繰り上がります。
（補欠登録したのを忘れて、直前に繰り上がり当日ドタキャンしてしまう場合はよくあるので注意しましょう）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/338998/f2881ced-16c7-736f-2e33-ed937228868c.png)

LT枠に間違っても登録しないようにしましょう。主催者が一番焦ります（笑）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/338998/dc1cdd33-0163-15fb-c7b3-7457c1422263.png)

正しい募集枠を選択して確定を押すと登録完了です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/338998/c62e0eb6-49ec-f24a-52a2-b1178f269459.png)

##4.勉強会当日までに
参加登録をしたあとは、勉強会を楽しみ待ちながら、参加のための準備をします。
基本的にリモート勉強会は、以下のサービスを使用することが多いです。
事前に基本的な使い方を学んでおくと良いでしょう。

* zoom
* discord
* slack(チャットのみ）

また、何かわからないことや確認したいことは、
connpass内のチャットで聞くことができます。（勉強会のページの一番下あたり）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/338998/6ed96ba4-8fc0-e82e-3847-c7d92cd29f99.png)


当日どうしてもドタキャンしちゃうときも、こちらのチャットに一報あると主催者としては安心します。

#まとめ
以上、簡単になりましたが、connpassの使用方法についてご説明しました。
リモート勉強会は最初なかなかハードルが高いですが、皆さん気さくな方ばかりで非常に楽しく参加できます。
ぜひ参加してみてください。

次は勉強会を主催するためのtipsを書こうと思います。
新規ツイートを正規表現でマッチングしてSlack通知したいときはGASをはさむのが楽

# 背景

あるアカウントの、あるパターンの文字列を含むツイートをslack通知したいなと思い、一番サクッとやれる方法は何かなと思い調べました。　
パターンは、最初は時刻を含むツイート(21時, 10:00など)でマッチングしますが、今後増やしたりする可能性があります。
できれば単一のサービスのみでslackのwebhookにPOSTしたかったですが、正規表現でマッチしてPOSTできるようなサービスはなさそうなので、GASを挟めばお手軽かなと思い、作ってみました。


# 手順

## 1. IFTTTで特定のアカウントのツイートをGoogle Spreadsheetに挿入していくアプレットを作成する

IFTTTの手順に従って作成するだけで非常にかんたん。
できればIFTTTだけで実現したかったですが、単純なことしかできなそうなので、Spreadsheetに一旦放り込みます。

## 2. 作成されたSpreadsheetから、GAS Projectを作成する。

コードはこんな感じでしょうか。

```js

function myFunction() {
  const SS_ID = "xxxxxx";
  const TARGET_SHEET = "ツイート一覧";
  const WEBHOOK_KEY = "https://hooks.slack.com/services/xxxxxx/xxxxxxxxx/xxxxxxxxx";
  
  const sh = SpreadsheetApp.openById(SS_ID).getSheetByName(TARGET_SHEET);
  const values = sh.getRange('A:A').getValues();
  const lastRow = values.filter(String).length;
  
  const newTweet = {
    time: sh.getRange(lastRow, 1).getValue(),
    content: sh.getRange(lastRow, 3).getValue(),
    url: sh.getRange(lastRow, 4).getValue()
  };
  // 正規表現
  const pattern = /[0-9０-９]{1,}時|[0-9０-９]{1,}[:：][0-9０-９]{1,}/g;
 
  if (pattern.test(newTweet.content)) {
    const options = {
    "method"  : "POST",
    "headers" : {"Content-type":"application/json"},
    "payload" : '{"text":"<@通知先>\n' + newTweet.url + '", "username": "notification", "icon_emoji": "アイコンに使うemoji"}'
  }
  
    UrlFetchApp.fetch(WEBHOOK_KEY, options);
  }
}
```

## 3. トリガー設定

トリガーは、spreadsheetにレコードが挿入されたとき、つまり`変更時`で良いです。


これで、以下のフォーマットで通知してくれるようになりました。

```
notification  13:16
@tachibanayu24
http://twitter.com/xxxxxxxxx/status/xxxxxxxxxxxx 
```

ただ、ツイートされてから3分くらいのタイムラグがあります。