OmniAICreator/Qiita-1.07M huggingface dataset:
【laravel】Eloquentの基本

# 概要
laravelではEloquentというORMが用意されています。
ORMとは「Object-Relational-Mapping」の略で、互換性のないデータを自動的に変換して、相互にデータをやり取りできるようにするための仕組みです。
`Illuminate\Database\Eloquent\Model`クラス（以下：モデルクラス）で記述したPHPスクリプトはこの仕組みによって、データベースから取得したレコードはPHPのオブジェクトに変換し、データベースに値を渡す時はPHPオブジェクトをレコードへ変換して渡されます。
これがModelクラスによるデーターベースの操作です。

# モデルクラスの作成
モデルクラスはartisanコマンドで作成し、指定がなければapp直下に保存されます。
ですが、個人的な好みでapp直下よりModelsファイルがあった方がしっくりくるので、Modelsファイルを作成した例を記述していきます。

## コマンド
例としてSampleというモデルクラスを作成します。
`php artisan make:model`というコマンドを使用します。

また、Laravelはテーブル名に基づきインデックス名を付けるため、クラス名は対象とするテーブル名と同じ名前（頭文字は大文字）にしてください。

```:コマンド
php artisan make:model Models/Sample
```


## ディレクトリ構造
ここでは上記のようにModelsファイルを追加した場合のディレクトリ構造を記述します。

```:ディレクトリ
app
├─ Models
|  ├─ Sample.php
```

## ファイルの中身
namespaceとuse元と作成したモデルクラスしか記述されていません。

```php:Sample.php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Sample extends Model
{
  //
}
```

# モデルクラスの基本設定
設定をしなければデフォルトのまま編集していくことができますが、デフォルトの設定を変更することもできます。

## テーブルとの紐付け
Eloquentではテーブル名に基づきクラス名を付けると上記しました。
ですが、laravelでEloquentの使用を想定せずにスネークケースで命名されたテーブルも多々あります。
その場合は`$table`プロパティでモデルとテーブルを紐づけることができます。

```php:Model
class Sample extends Model
{
  /**
   * モデルと関連しているテーブル
   *
   * @var string
   */
  protected $table = 'sampl_table';
}
```

## 主キー
Eloquentではカラム名が`id`のカラムが主キーと設定されています。
主キーを`id`以外のカラムに設定したい場合は`$primaryKey`プロパティで設定します。

````php:Model
class Sample extends Model
{
  /**
   * テーブルの主キー
   *
   * @var string
   */
  protected $primaryKey = 'user_id';
}
```

### オートインクリメントを無効にする
Eloquentでは主キーがオートインクリメントで増加する整数値であるとデフォルトで設定されています。
そのため、オートインクリメントまたは整数値ではない値を主キーを使う場合は`$incrementing`プロパティを`false`に設定します。

```php:Model
class Flight extends Model
{
  /**
   * IDが自動増分されるか
   *
   * @var bool
   */
  public $incrementing = false;
}


## タイムスタンプ
デフォルトだとデータベース上に存在する`created_at`と`updated_at`を自動的に更新します。
この更新を無効にするには`$timestamps`を`false`に設定します。

```php:Model
class Sample extends Model
{
  /**
   * モデルのタイムスタンプを更新するかの指示   
   * 
   * @var bool
   */
  public $timestamps = false;
}
```


## データベースの接続先
Eloquentモデルはデフォルトでアプリケーションに設定されているデータベースを使用します。
設定とは異なるデータベースへ接続する場合は`$connection`プロパティで設定します。

```php:Model
class Flight extends Model
{
  /**
   * モデルで使用するコネクション名
   *
   * @var string
   */
  protected $connection = 'connection-name';
}
```

## 属性の保護
`$guarded`は値を代入したくないカラムを設定するプロパティです。
モデルではNotNull以外の項目に値が揃わないと保存ができません。
ですが、idのように主キーやオートインクリメントとして設定されているカラムはデータベース側で自動でフィールドを振り分けるため、モデルで値を設定する必要はありません。
このような場合に`$guarded`で値を代入しないと設定します。

```php:Model
<?php
class Flight extends Model
{
  /**
   * 代入しない属性
   *
   * @var array
   */
  protected $guarded = ['id'];
}
```
Pickleできないオブジェクトを扱う際の並列計算（pathos）


## 概要

`joblib`, `multiprocessing`を利用して、プロセス並列計算をする時に、以下のようなエラーに遭遇した時の解決方法を紹介する。プロセス間通信に利用しているpickle化ができないことが原因である。

```{python: error1}
TypeError: cannot pickle 'SwigPyObject' object
```
```{python: error2}
PicklingError: Could not pickle the task to send it to the workers.
```

## 解決方法

`pathos`を使用する。`pip install pathos`でインストールできる。

```{python: code}
# some_func, some_listは適当に作成してください
from pathos.multiprocessing import ProcessingPool
pool = ProcessingPool(nodes=4)
result_list = pool.map(lambda x: some_func(x), some_list)
```

## おまけ

保存の際のpickle化であれば、`dill`を用いることで解決する。

## 参考

- https://pathos.readthedocs.io/en/latest/
- https://stackoverflow.com/questions/19984152/what-can-multiprocessing-and-dill-do-together
- http://nishidy.hatenablog.com/entry/2016/02/01/225750
【docker-compose】Nginxコンテナのログをローテートする

docker-composeで動かしているNginxコンテナのログをローテートする方法です。気づいたらログでディスクが溢れちゃった...となる前に設定しておくことをおススメします。

ホスト側にNginxコンテナのログディレクトリがマウントされているとします。

## 1. ホスト側でlogrotateの設定を行う

設定はよしなに変更してください。

```conf:/etc/logrotate.d/nginx
/path/to/nginx/logs/*.log
{
    missingok
    notifempty
    daily
    rotate 10
    compress
    delaycompress
    sharedscripts
    postrotate
        /usr/local/bin/docker-compose -f /path/to/docker-compose.yml kill -s HUP docker-service-name
    endscript
}
```

- `postrotate` ではローテート後の処理を記述します。ローテーションを行ってもNginxは過去のログファイルに追記し続けるため、ローテート後にNginxにログを開き直させます。
- シグナルは `USR1` だと無理っぽい
- 動かない場合はログファイルのパーミッションを確認しましょう
- `docker-service-name` はdocker-composeの `services` のディレクティブ名であり、コンテナ名ではないことに注意

```yaml:docker-compose.yml
...
services:
  nginx: // これ
...
```

## 2. テストする（強制 `logrotate`）

```shell
$ logrotate -f /etc/logrotate.conf
$ ll /path/to/nginx/logs
合計 xxxxxxx
-rw-r--r-- 1 user user xxxxxxxx  5月 15 14:50 access.log
-rw-r--r-- 1 user user xxxxxxxx  5月 15 14:50 access.log-20200515
-rw-r--r-- 1 user user xxxxxxxx  5月 15 14:50 error.log
-rw-r--r-- 1 user user xxxxxxxx  5月 15 14:50 error.log-20200515
...
```

うまくいけば新しいログが日付のついていない `*.log` の方に追記されていきます。
HerokuにFastlyを導入しようとしてやめた話

Herokuで運営しているアプリにFastlyを導入しようとしたのですが、一筋縄ではいかず、一旦保留にすることに決めました。CDN導入以外にもやるべきことがたくさんあるので。。
いつかまた挑戦する時のために、今回の学びをまとめておきます。

(2020/06/02追記)
Cloudflareを導入しました。かなり良い感じです!
[Cloudflareを使ってみた - Qiita](https://qiita.com/d0ne1s/items/8b3cdb915042367c2bb5)

## 用語
- Aレコード
    - 名前解決方法の1つ。
    - hogehoge.com = 123.456.78.90 みたいに指定する
- CNAMEレコード
    - 名前解決方法の1つ。
    - hogehoge.com = fugafuga.com みたいに指定する
- apexドメイン
    - wwwがないドメイン

## Fastlyを導入しようとした経緯
ページの表示が遅いのでCDNの導入を検討。
Herokuを使う場合、CloudFrontとFastlyの2種類があり、Fastlyの方がおすすめされている雰囲気だったのでFastlyに決定。
全てのCDNサービスの中でFastlyだけ?がインスタントパージという機能を提供している。

CDNにキャッシュされているデータを、任意のタイミングでプログラム側から即時削除できるらしい。
これにより(画像やCSSだけでなく)ページデータ全体をキャッシュすることができ、かなりの高速化+変更があった時には即時反映することができる。[Dev.to](https://dev.to/)や[Resume](https://www.resume.id/)で利用されているらしい。(月6000円くらいかかる+設定はわりと大変)

[Fastly + Rails + Herokuでページをキャッシュする方法 | Cat Knows](https://catnose99.com/rails-heroku-fastly/)

## やったこと
- Herokuアプリのダッシュボードから「Resources」>「Fastly」を検索>「Provision」
- Fastlyのダッシュボードにアクセスし「Configure」>「Edit Configurations」>「Domains」>「Create Domain」より、自分のサイトのドメインを追加
- ドメイン一覧が表示される画面に移動するので、追加したドメイン横の「Test domain」をクリック
- `hogehoge.com.global.prod.fastly.net`というURLで自分のサービスのトップページが表示
    - 自分のサービスのURLがhogehoge.comの場合
    - ドメイン追加から10分くらい経過していないと、エラー画面が表示された
- DelelopertoolのNetworkタブを開いてからページを再読み込み→読み込まれた画像を選択すると、Response Headers内に`X-Cache: HIT`という項目があることが確認できた
    - このページの画像はCDNを経由して取得されたということ
- 今度はhogehoge.comにアクセスして同様の手順を踏んでみると、`X-Cache: HIT`という項目があることが確認できない
    - このURLではCDNから配信されていないということ

hogehoge.comにアクセスした時にCDNから配信できていないと意味がないので、どうにかせねば、となった

## 調べてわかったこと
- hogehoge.comにアクセスした時にもCDNから配信できるようにするためには、DNS周りの設定を変更する必要がある (hogehoge.comへのアクセスをFastlyにリダイレクトする必要があるため)
- CNAMEレコードの設定を編集して対応する方法が推奨されている
- apexドメインを使っている場合、CNAMEレコードを編集する方法が使えないので、Aレコードを編集する別の方法で解決
- Aレコードを編集する際に設定するIPアドレスは、各自でFastlyに問い合わせて教えてもらう必要がある

## 導入を見送った理由
私のサイトはapexドメインで運用しているので、色々めんどくさそうだと思って導入を見送りました。
ドメイン周りはPointDNSというHerokuアドオンで設定しているのですが、PointDNSの設定自体もあまり理解していないまま適当に片付けてしまったので、そもそもどこかのタイミングでDNS周りの勉強をした方が良さそうです。

私のサイトは他にも改善できるポイントがたくさんあるので、それらの修正が終わってからきちんとDNS周りの勉強をして、そこでFastlyも導入をしようと思います。

## 疑問
### Fastly側でSSLの設定をしないとどうなる?
アセットを取得する時の通信がSSLじゃなくなって、ブラウザに表示されるURLは https://hogehoge.com のままだと思っているんですけど、合ってますか?

## 参考サイト
- [Fastly | Heroku Dev Center](https://devcenter.heroku.com/articles/fastly)
- [CNAME レコードの追加 | Fastly ヘルプガイド](https://docs.fastly.com/ja/guides/adding-cname-records)
- [Using Fastly with apex domains | Fastly Help Guides](https://docs.fastly.com/en/guides/using-fastly-with-apex-domains)
- [Fastly経由の配信に切り替える手順 - Qiita](https://qiita.com/AtsushiFukuda/items/f8efe8a1613a7fda9894)
- [Fastly + Rails + Herokuでページをキャッシュする方法 | Cat Knows](https://catnose99.com/rails-heroku-fastly/)
- [Heroku の Fastly を使ったアセットの CDN 化 - ボクココ](https://www.bokukoko.info/entry/2016/12/20/233936)
なんかすごい田舎のガス屋さんを見つけた

ハンディプリンタを買いました。Yahoo!のイベント申込みシステム「PassMarket」で使おうと思って買いましたが、なんと現行バージョンではプリンタ機能が無いということがわかりました。

んで、これをどうにか活用できないかなと、ネットサーフィンしていて見つけたのがこれ。

http://www.mklpg.com/

大分の田舎のガス屋さんが作ったガス検針システムです。

・・・Qiitaにお住まいのエンジニアさんにしてみれば、ただのアプリ＋プリンタですし、別にすごくもないかもしれませんが、素人の私からすると、これはすごい。

何がすごいって、その、「自分が使いたいから作ったら、なんだかよかった」という感じで、小さいガス屋さん向けに中古機材も組み合わせて廉価にほぼ頒布みたいな感じで売っているのです。

そんなこんなで200以上の小さいガス屋さんに売れているようです。
リスト化された項目に対して適切にmarginを提供する方法


& li {
text-align: center;
margin-top: 3.5rem;
      }

この記述方法だと、リストの一番最初のリストにmarginがついてしまい
レイアウトが崩れることが多い。


& li {
text-align: center;
      &+li {
        margin-top: 3.5rem;
      }

対応方法としては、&+liとしてあげると、liの直後のliを指定することができる。
【Golang】クロージャー

#【Golang】クロージャー

Golangの基礎学習〜Webアプリケーション作成までの学習を終えたので、復習を兼ねてまとめていく。 基礎〜応用まで。

```
package main
//クロージャー
//変数に格納して、実行できる。

import "fmt"


//クロージャーによるジェネレーター
//関数を返す関数、関数を引数に取る関数（渡された関数をそのまま実行する）
//返り値でfunc() intを返す
func incrementGenerator() func() int {
	//変数宣言時のみ
	x := 0
    //ここまでがクロージャーで定義

	//funcを返す ２回目以降はここから
	return func() int {
		x++
		return x
	}
}

//クロージャーの使い方
func circleArea(pi float64) func(radius float64) float64 {
	return func(radius float64) float64 {
		return pi * radius * radius
	}
}

func main() {
	//counterはインナー関数になる
	counter := incrementGenerator()
	fmt.Println(counter())
	//>>1
	fmt.Println(counter())
	//>>2
	fmt.Println(counter())
	//>>3
	fmt.Println(counter())
	//>>4
	
	//変数に格納して実行
	//引数を先に指定
	//同じ関数で引数を動的にし、インナー関数の引数を動的に買えたい場合
	//pi=3.14
	c1 := circleArea(3.14)
	//c1はインナー関数になる
	//インナー関数の引数
	//radius=2
	fmt.Println(c1(2))
	fmt.Println(c1(3))
	
	//上記と同様
	c2 := circleArea(3)
	fmt.Println(c2(2))
	fmt.Println(c2(3))
}

```
vue.jsにgoogle analyticsを導入する

# やりたいこと
vue.jsで構築したサービスにgoogle analyticsを簡単に入れたい

#使用するライブラリ
[vue-gtag](https://github.com/MatteoGabriele/vue-gtag)

# 実装
## ライブラリのインストール
```
npm install vue-gtag
```

``` main.js
import Vue from 'vue'
import App from './App.vue'
import router from "./router";
+ import VueGtag from "vue-gtag";


+ Vue.use(VueGtag, {
+   config: { id: "UA-xxxxx-xxx" }
+ });

new Vue({
  router,
  render: h => h(App)
}).$mount('#app')
```

以上です！

アナリティクスのページで確認
<img width="382" alt="スクリーンショット 2020-05-15 14.16.55.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/160183/d289f713-51da-153d-dfbd-177f33c2a210.png">
railsからSQL基礎

##学習の準備
###mysqlの起動

```terminal
$ sudo service mysqld start
```
###mysqlへの接続

```terminal
mysql -u root
```
このコマンドの意味は、「mysqlにユーザー名rootで接続する」という意味です。

##データベースの操作

###データベースの確認
####SHOW DATABASES
データベースを一覧で表示することができます。

```terminal
SHOW DATABASES;
```
SQL文を実行すると、存在するデータベースを一覧で見ることができます。

###データベースの作成
####CREATE DATABASE
データベースやテーブルを作成する際は、CREATEを使います。
以下のように、指定した名前のデータベースを作成できます。

```terminal
CREATE DATABASE データベース名;
```
また、Cloud9の仕様上、mysqlにて日本語を保存するためには、
**DEFAULT CHARACTER SET utf8**というオプションをつけて
データベースを作成する必要があります。

```terminal
CREATE DATABASE データベース名 DEFAULT CHARACTER SET utf8;
```

###データベースを選択
USE文でデータベースを選択することができます。

```
USE データベース名;
```

##テーブルの操作
###テーブルの確認
####SHOW TABLES
選択したデータベースに存在するテーブルを一覧で表示します。

```terminal
SHOW TABLES;
```
###テーブルの作成
####CREATE TABLE
テーブルを作成する際は、テーブル名とテーブルに作成するカラムの名前とそのカラムの型を指定します。

```terminal
CREATE TABLE テーブル名 (カラム名 カラム名の型, ……);
```
カラムの型は、以下のようものがあります。

| 型名        |     保存できる値|  
|:-----------|--------------:|
| INT        |         整数型 | 
| FLOAT      |    浮動小数点型 |     
| VARCHAR(m) |       文字列型 |

###テーブル構造の確認
テーブル構造は以下のように確認できます。

```terminal
SHOW columns FROM テーブル名;
```

###カラムの追加
####ALTER TABLE
カラムの修正、追加、削除はすべてテーブルの修正なので**ALTER TABLE**を使います。
カラムの追加方法は以下のようです。

```terminal
ALTER TABLE テーブル名 ADD カラム名 カラムの型;
```
カラムを複数追加するときは

```terminal
ALTER TABLE テーブル名 ADD (カラム名 カラムの型, ……);
```

###カラムの修正
カラムの修正も**ALTER TABLE**を使って、以下のように記述します。

```terminal
ALTER TABLE テーブル名 CHANGE 古いカラム名 新しいカラム名 新しいカラムの型:
```
またこのとき、**カラムの型を変更していなくても再度記述する必要があります。**

###カラムの削除
カラムの削除も**ALTER TABLE**を使って、以下のように記述します。

```terminal
ALTER TABLE テーブル名 DROP カラム名;
```
##データの操作
以下のコードでテーブルにあるレコードをすべて表示できるので適宜利用してください。

```terminal
SELECT * FROM テーブル名;
```

###データの登録
####INSERT
テーブルにデータを登録することができます。
INSERT文は、全てのカラムに値を登録するか、特定のカラムに値を登録するかで記述が異なります。

**全てのカラムに値を登録するとき**

```terminal
INSERT INTO テーブル名 VALUES(値1, 値2, 値3);
```
**特定のカラムに値を登録するとき**

```terminal
INSERT INTO テーブル名(カラム名1, カラム名2) VALUES(値1, 値2);
```
このとき指定されていないカラムには、NULLが挿入されます。

###データの更新
####UPDATE
データの更新をする際は、テーブル名、変更内容、条件を指定します。

```terminal
UPDATE テーブル名 SET 変更内容 WHERE 条件;
```
条件のところで、更新をしたいデータを **id=2** などで指定します。
###データの削除
####DELETE
データの削除をする際は、テーブル名と条件を指定します。

```terminal
DELETE FROM テーブル名 WHERE 条件;
```
リモートにあるファイル・フォルダをローカルに

# ファイルの大きさを一応調べる

# ローカルにいる状態

### リモートからファイルをローカルにコピー

```
scp 接続名:コピーしたいファイルのリモートでのPATH ファイルを置きたいローカルのPATH
```

### リモートからフォルダをローカルにコピー

```
scp -r 接続名:コピーしたいファイルのリモートでのPATH ファイルを置きたいローカルのPATH
```
指数分布と最尤推定量(MLE)のpythonのコードの例

ここでは指数分布に関する基本的な問題と、それに関連した最尤推定量の問題をpythonで解いてみたいと思います。

## 問題1

```math:

N氏のホームページのアクセスの間隔のT(単位：時間)は、指数分布\\
f(t) = 2 e^{-2t}~~~~ (t>0)\\

に従うという。このときP(T\leq 3)を求めよ。
```
まずは諸々の準備。

```python:
import sympy as sym
from sympy.plotting import plot
sym.init_printing(use_unicode=True)
%matplotlib inline
oo = sym.oo # 無限大
(x,t) = sym.symbols('x t')
```
次に確率密度関数のグラフを描いてみます。

```python:
expr =2* sym.exp((-2*t))
# 得られた関数のグラフを0から3まで図示。
plot(expr, (t, 0, 3))
#念のため確率密度関数になっているかも確認。
sym.integrate(expr, (t, 0, oo))
```

![ダウンロード.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359600/d6451ae5-642e-3dfc-2135-a631c3c62327.png)

最後に与えられた問題を解きます。

```python:
sym.integrate(expr, (t, 0, 3))
```
## 問題2

今度は最尤推定量の問題を解いてみましょう。

```math:

N氏のホームページのアクセスの間隔のT(単位：時間)は、指数分布\\
f(t) = x e^{-xt}~~~~ (t>0)\\

に従うとする。Tの独立な観測値の組を任意に入力し、その値の組に対応するxの最尤推定量を求めよ。
```
尤度関数を定義。変数がtではなくxに変わります。(普通はxではなくλを使います。）

```python:
#まずは観測値の組を入力。
A = list(map(float, input().split()))
#次に尤度関数を定義。
expr = 1
for t in A:
    expr = expr*(x * sym.exp((-x)*t))

print(expr)
# xを変数とする尤度関数のグラフを0から3まで図示。ここでは3という数字は特に意味なし。何でもよい。
plot(expr, (x, 0, 3))
```

次に対数尤度関数を定義。

```python:
L = sym.log(expr)
print(L)
# 得られた関数のグラフを図示。
plot(L, (x, 0, 3))
```
微分して0になる場所を計算します。

```python:
L_1 = L.diff(x,1)
plot(L_1, (x, 1, 3))
sym.solve(L_1, x)
```
本来なら以下の様に2階微分も計算して、常にマイナスとなることも確かめなければいけません。しかしこの計算は人間が行えば簡単ですが、Pythonは途中で式変形をして計算を楽にするということをしないため、上手くいきません。(´·ω·`)ｼｮﾎﾞｰﾝ。

```python:
L_2 = L_1.diff(x,1)
plot(L_2, (x, 1, 2))
print(L_2)
```
NSView alphaValueが効かない時に

`NSView.alphaValue`を設定するとViewが全く見えなくなるバグに遭遇したので、解決方の備忘録。

これを半透明したいのに
<img width="69" alt="スクリーンショット 1.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117659/872e4f65-1a92-3633-f583-409ec24ecb76.png">

完全に消えてしまう。
<img width="59" alt="スクリーンショット.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117659/06bc997f-6e96-7e16-c57b-ec4a373131b0.png">

`alphaValue`は内部的に`CALayer`を使っているようで、場合によっては`CALayer`なしでもうまく行くが、場合によってはうまくいかないようなので

```swift
view.wantsLayer = true
```
で解決した。
【Rails】Bundlerのバージョンを2.0.2に戻したい（開発中）

### 開発中にBundlerのバージョンを2.0.2に戻したいそんな時があれば、、、

**下記コマンドで１発です**

```
gem uninstall bundler && gem install bundler -v 2.0.2 && rm -rf Gemfile.lock && rm -rf vendor/bundle && bundle
```

このコマンドがやっていること

- 現在使用しているbundlerを削除
- 新しくbundlerのバージョン2.0.2をインストール
- Gemfile.lockを削除
- vendor/bundleを削除
- bundle install

参考

https://qiita.com/Nedward/items/ee70b8196398dc1e3017
https://www.slideshare.net/cuzic/rubygems-bundler
https://qiita.com/egopro/items/aba12261c053eecd6d19
Flex: PCで左←右、スマホで上→下の並び

お題：広い画面でボタンを右から左に並べ、狭い画面で上から下に並べる。

![flex-direction.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/308595/80335d31-b441-fceb-9bd3-a1aebe095fd2.png)

答え：`flex-direction: row-reverse` を使う。

参考: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Flexible_Box_Layout/Aligning_Items_in_a_Flex_Container#Alignment_and_flex-direction

HTMLはこんな感じです。Bootstrapを使っています。

```html
    <div class="card shadow m-4">
      <div class="card-body">
        <div class="row row-submit">
          <div class="col-sm text-right">
            <button type="button" class="btn btn-primary">送信</button>
          </div>
          <div class="col-sm">
            <button type="button" class="btn btn-danger">キャンセル</button>
          </div>
        </div>
      </div>
    </div>
```

CSSはこんな感じです。デフォルトはモバイル版とし、広い画面で（`@media (min-width: 576px)`）デザインを変えています。

```CSS
  .row-submit button {
    width: 100%;
  }
  .row-submit .col-sm:not(:last-child) {
    margin-bottom: 1.5rem;
  }
  @media (min-width: 576px) {
    .row-submit {
      flex-direction: row-reverse;
      justify-content: flex-end;
    }
    .row-submit button {
      width: auto;
    }
    .row-submit button.btn-primary {
      width: 70%;
    }
    .row-submit .col-sm:not(:last-child) {
      margin-bottom: 0;
    }
  }
```

Bootstrapでは、.row と .col-sm には次のようなCSSが設定されています。

```css
  .row {
    display: flex;
    flex-wrap: wrap;
  }
  @media (min-width: 576px) {
    .col-sm {
      flex-basis: 0;
      flex-grow: 1;
    }
  }
```
deviseによるユーザー管理

#deviceとは

Rubyのライブラリ（拡張機能）であるgemの一つ。

アプリケーションにおけるユーザ-新規登録・ログイン機能を簡単に実装できる。


#導入方法

Gemfileに記載

```
gem 'devise' 
```

ターミナルのアプリケーションのディレクトリにて

```
bundle install
```

deviseの設定ファイルの作成

```
rails g devise:install
```

**deviseのモデル作成用コマンド**でuserモデルを作成

このコマンドで、ファイルの新規作成と、ユーザーのログイン・新規登録で必要なルーティングが生成される（routes.rbにdevise_for :usersが自動追記されるため）

```
rails g devise user
```

マイグレーションファイルの実行

```
rails db:migrate
```

deviseに対応したビューファイルの作成

これでシンプルな見た目の新規登録画面とログイン画面が生成されます

新規登録画面はapp/views/devise/registrations/new.html.erb、
ログイン画面のビューはapp/views/devise/sessions/new.html.erb　が対応しています。


```
rails g devise:views
```

以上で新規ユーザー登録とログイン機能の基本的な実装はできます。

deviseを導入することで使用できるメソッドや、deviseによるユーザー登録情報の項目を追加したい場合（現状ではEメールと、パスワードのみの登録しかできない）は調べてみて下さい
Python実装中に開発環境でよく使うコマンド

自分用メモですが。。。随時追加予定。

# 前提
プロジェクトルートに移動した状態で使う。

# venv起動／停止
venv\script\activate
venv\script\deactivate

# サーバー起動
python manage.py runserver --setting={prj_name}.local_settings.py

# proxy認証
set HTTP_PROXY=http://{id}:{password}@{host}:{port}
set HTTPS_PROXY=http://{id}:{password}@{host}:{port}
Shell Script入門 はじめに

#はじめに
こちらでは shell script について簡単に書いきたいと思います。

といっても入門レベルの簡単なことです。

備忘録も兼ねているのでシリーズ化していけたらなぁ。。。
まだ書く全体的なボリュームが考えられてないのでその〜みたいにして目次用の記事を書くとかにしたいですね。

#環境
Vagrant
Debian

#基礎
まずはじめに実行のしかたから

```shell:hello.sh
#!/bin/sh

echo "Hello World!"
```

保存します。

そのままだと実行できないので権限の変更をします。

```
$ sudo chmod 775 hello.sh
```

実行結果

```
./hello.sh 
Hello World!
```
##コメントアウト
まずはコメントアウトから

```shell:hello.sh
#!/bin/sh

#表示されないよ
echo "Hello World!"
```

実行結果

```
./hello.sh 
Hello World!
```

##変数
値を格納する上での基本もう少し深掘りした内容は[こちら](https://qiita.com/I_S-657/items/75c9d03c757b19845b93)

```sh:hello.sh
#!/bin/sh

HELLO="Hello World!"
 
echo $HELLO
```

実行結果

```
./hello.sh 
Hello World!
```

##配列
配列ですがshでは機能がたりないのでbashを使います。

```sh:hello.sh
#!/bin/bash

ARRAY=("huga" "fugo" "fuga")

echo "${ARRAY[*]}"

```

実行結果

```
$ ./hello.sh 
huga fugo fuga
```

##if

```sh:hallo.sh
#!/bin/bash

if [ $a -gt $b ]
then 
    echo "変数aが変数bより大きい"
else
    echo "変数aが変数bより小さい"
fi
```

##for

```sh:hello.sh
#!/bin/bash

for ((i=0 ; i<5 ; i++))
do
echo "hello"
done
```

実行結果

```
$./hello.sh 
hello
hello
hello
hello
hello
```

##while

```
#!/bin/bash

a=0
while [ $a -ne 5 ]
do
    echo "hello"
    a=`expr $a + 1`
done
```

実行結果

```sh:hello.sh
$ ./hello_1.sh 
hello
hello
hello
hello
hello
```

#まとめ
いかがだったでしょうか
軽いまとめのつもりで書いていますがこんどはもっと変数や比較など
深掘りした内容を書いていきます。
(IRC bot) go-ircevent を使ってみた

* 後で見る: [21 best Golang Bot Building libraries | Dunebook](https://www.dunebook.com/best-golang-bot-building-libraries/)
* [go-irceventでお手軽IRC bot作成 - Qiita](https://qiita.com/kitak/items/f885466c8c4eba90aa53)さんの記事が 2013年! と随分前ですが参考になりました。
* [github.com/thoj/go-ircevent](https://github.com/thoj/go-ircevent) の [sample.go](https://github.com/thoj/go-ircevent/blob/master/examples/simple/simple.go) にオウム返しを追加したのが以下。


```go:bot1.go
package main

import (
	"crypto/tls"
	"fmt"
	"github.com/thoj/go-ircevent"
	"time"
)

const channel = "#server"
const serverssl = "myirc.server.com:6697"

func main() {
	ircnick1 := "ircbot"
	irccon := irc.IRC(ircnick1, "ircbot")
	irccon.VerboseCallbackHandler = true
	irccon.Debug = true
	irccon.UseTLS = true
	irccon.TLSConfig = &tls.Config{InsecureSkipVerify: true}
	irccon.AddCallback("001", func(e *irc.Event) { irccon.Join(channel) })
	irccon.AddCallback("366", func(e *irc.Event) {})
	err := irccon.Connect(serverssl)
	if err != nil {
		fmt.Printf("Err %s", err)
		return
	}

	// PRIVMSGメッセージをイベントとして、コールバックを登録する
	irccon.AddCallback("PRIVMSG", func(e *irc.Event) {
		time.Sleep(1000 * time.Millisecond)
		irccon.Notice("#server", e.Message()) // おうむ返し
	})

	irccon.Loop()
}
```

```sh
go get github.com/thoj/go-ircevent

go run bot1.go
```

`ircbot` という名前で `#server`に join してくる。
ircbot にダイレクトメッセージを送ると `#server` にオウム返ししてくれます。
Linuxサーバー負荷検証


負荷検証するためのメモ

## CPUに対して負荷をかける

```bash
# yesを使う場合
# 1プロセスで足りない場合は複数実行
yes > /dev/null &

# gzipを使う場合
# 1プロセスで足りない場合は複数実行
gzip -9 < /dev/urandom > /dev/null &

# opensslを使う場合
openssl speed -multi `grep processor /proc/cpuinfo|wc -l`
```

解説
`&`の意味: バックグラウンド実行
`<bash_command> > /dev/null`の意味: 出力を破棄する。なにも表示されない。
`<bash_command> < /dev/urandom`の意味: 乱を生成しコマンドに対して引数として渡す

## メモリ負荷
以下の実行にyesコマンドの実行結果を/dev/nullにレンダリングし続けることで使用メモリを高めていく。

```bash
# 1プロセスで足りない場合は複数実行
/dev/null < $(yes) & 

# malloc, free 関数を繰り返し実行するコマンド
# --vm で起動するプロセス数を指定する。
# --vm-bytes で確保するメモリを指定する。
# --vm-keep を指定すると、free を行わず確保したままにしてくれる。
stress --vm 2 --vm-bytes 1G --vm-keep
```

注意事項:
`--vm-bytes`の値が大きすぎる場合、メモリに乗らないためエラーとなる
その時は、`--vm プロセス数` × `--vm-bytes数`が合計のメモリサイズになるためプロセス数を増やすことで合計メモリを上げることができる。

## 容量負荷
以下のコマンドにてファイル生成をすることができる。
全てNULL文字で埋まったファイルを生成する。

注意事項
メモリに乗り切らないぐらいの大きさのbsを指定すると失敗する。
この場合にはbsの値を小さくして、countの値を大きくすることで対応する。
bs*countの値が最終的に出力されるファイルサイズとなる。


```bash
# 100バイトのファイルを生成する
dd bs=100 count=1 if=/dev/zero of=file1 
# 102400バイトのファイルを生成する
dd bs=1K count=100 if=/dev/zero of=file2 
# 100000バイトのファイルを生成する
dd bs=1KB count=100 if=/dev/zero of=file3 
```

ログファイルなどがボトルネックになっている場合は、削除or移動して空き容量を確保すること

```bash
# 最終更新日が90日以上経過している"*.log"ファイルの削除
find $TARGET_DIR -type f -name "*.log" -mtime +90 | xargs rm -f
```

最後には立ち上げたプロセスの分削除すること

```bash
jobs

# jobsの実行結果に出てくるプロセス分 引数を取り削除
kill %1 %2 %3 ... %N
```
【決定版】 SlackBotのリマインダー設定方法(/remind)

# 結論
## 基本型
```
/remind [who or where] "
[what]
" [when]
```
↑この形で全て解決です。

コピーして[who or where] [what] [when]を
自分が設定したい内容に変えればOKです！


## 例1 )

```
/remind me "
定例朝MTG 9:00-
　→[zoom等のURL]
" every weekday 9:00
```
と設定すれば

**▶︎ [who or where]**
　→  me(自分)に対して
**▶︎ [what]**
　→ 『""』で囲まれた内容で
**▶︎ [when]**
　→ 平日の9:00に

リマインダーが設定されます。


## 例2 )

```
/remind #happy "
@happyman さん
こちらの確認お願い致します！🙏
　→ [Googleスプレッドシート等のURL]
" tomorrow 14:00
```
と設定すれば

**▶︎ [who or where]**
　→ #happyチャンネルで
**▶︎ [what]**
　→ happymanさんにメンションがつきながら
**▶︎ [when]**
　→ 次の日の14:00に

リマインダーが設定されます。

# 詳細の説明
## [who or where] について
大きく3パターンです。

・ **自分に対して** →『me』
・ **誰かに対して** →『@----』
・ **チャンネルに対して** →『#----』

## [what] について
**『""』の間に書きたい内容をひたすら書いていくだけです。**
もちろん、改行等もそのままリマインダーに反映されます。

『""』の中にメンションをつければそれもしっかりとメンションとして機能します。
どこかのチャンネルで、特定の人にメンションをつけつつ、
リマインダーを設定したい時に便利です。

今は時勢的にzoomやwhereby等を使って毎日のMTGをしている方が多いと思います。
同じURLを、決まった時間に、毎回自分で送るのはすこし面倒。

↑そんな時はリマインダーの文章中にzoomやwherebyのURLを入れてしまうと便利です。
狂い無く、毎回設定した同じ時間に、MTGの為のURLを届けてくれるようになります。

## [when] について

この部分はものすごく融通が効きます。(Slackのエンジニアさんすごい...)
わりと適当に書いても認識してくれて、しっかりと設定してくれます。

**▶︎ 例1：『15:00』と打った時**
　→ 同じ日の15:00にリマインド。(15:00を過ぎた時間に設定していた場合は次の日の15:00)

**▶︎ 例2：『5/20 3PM』と打った時**
　→ 5/20の午後3時にリマインド。

**▶︎ 例3：『18:00 every other Monday』と打った時**
　→ 隔週で月曜日の18:00にリマインド。

他にも色々な設定方法あるので
『Slack リマインダー 毎日』『Slack リマインダー 毎月』
とか調べれば大抵出てきます！


## リマインダーを削除したい時
```
/remind list
```
↑と打つと、自分が設定しているリマインダー一覧を見せてくれます。
その中から**消したいものを『Delete』するだけ**です！
Javaで使用するvarについて（Local Variable Type）

この記事は、他の言語でプログラミングしていた人が、Javaでプログラムを組んでみると言う人に向けて書いた記事です。

### Local Variable Type とは
ざっくりまとめると、Javaでもvarが使えるようになりました、ということのようです。

### varとは
複数の言語にvarはありますが、ここではJavaのvarにのみ着目します。
varは「variable（＝変数）」のことです。その名の通り、変数を使うときに使用します。ただし、Javaではローカル変数にしか使用できません。
varは型のように
var 変数名 = 変数の内容;
と記載しますが、他の変数と違って変数の内容が必ず必要です。
なぜなら、varの型は変数の内容によって変化するからです。

具体例として、次のようなものが挙げられます。

```practice.java
class Main {
  public static void main(String[] args) {
    var i = 5; //int型
    var msg = "Hello World!"; //String型
    var c = 'あ'; //char型
  
    System.out.println(msg+i); //Hello World!5 が表示
    System.out.println(c); //あ が表示
  }
}
```

iの場合、左側が整数であることから、varは整数型の int として扱われます。このように、左側で明らかにこの言語だと推測できる場合のみvarは使用できます。

### varの意義
varはあくまで型推論型で、決まった型の方がわかりやすく思えますが、大きな利点があります。それが、型の指定を省略できる ということです。

#### 型の指定を省略できる
これも文章だけだとわかりにくいので例を2つ記載します。
1.リスト、クラスの記載

```practice_list.java
List<String> Obj1 = new ArrayList<String>(); //varなし
var Obj2 = new ArrayList<string>(); //varあり

```

```practice_class.java
class Main {
  public static void main(String[] args) {
    longname_callclass cc = new longname_callclass(); //varなし
    var vcc = new longname_callclass(); //varあり
  }
}

class longname_callclass {
  public void callmethod() {
    System.out.println("呼び出し成功");
  }
}
```

このように左の型を省略出来るので、文章が短くなり、結果読みやすくなります。

2.数字の記載


```practice_number.java
var i = 1; // int
var l = 3L; // long
var d = 1.0; // double
var f = 4.2f; // float;
```

整数型はLのあるなしでintとlongが別れるようになるため、数字だけ見たときに型が分かるようになります。
【Rails】ActiveRecord::PendingMigrationErrorが出たときの対処法

# エラー内容
ActiveRecord::PendingMigrationError
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/637994/cdf7cc5e-2814-d1a7-30fc-16aafa518aee.png)

# 環境
* Rails 6.0.3
* psql (PostgreSQL) 12.2

# 概要
* このアプリケーションに対応するマイグレーションファイルをmigrateし忘れてますよ、というエラー
* 再度migrateすることで解決

# 原因
1. db:create後にmigrateし忘れている
2. すでに以前同じ名前でRailsアプリを作成とmigrateまでしていて、(何らかの事情で)アプリを消して、再度同じアプリ名で作成してmigrateしようとしたらエラーが発生

# 対策
####1. db:create後にmigrateし忘れている

この場合は作成したアプリを`bundle exec rake db:create`でDB作成後にmigrateし忘れているだけなので（マイグレーションファイルを変更するだけではmigrateされない）、

```
bundle exec rake db:migrate
```
をターミナルで入力することで解決

####2.すでに以前同じ名前でRailsアプリを作成とmigrateまでしていて、(何らかの事情で)アプリを消して、再度同じアプリ名で作成してmigrateしようとしたらエラーが発生

私はこのパターンで「アプリ消してるのに何でmigrateできないの？」とちょっと戸惑っていました💦<br/><br/>Migrationファイル自体は（同じ名前なので）存在するが、今回作成したアプリとMigrationファイルが関連づけられていないことが原因なので、一度すでに作成されていたMigrationファイルを下記コマンドでリセットします。

```
rails db:migrate:reset
```

migrateをリセットできたので、再度migrateします。

```
bundle exec rake db:migrate
```
サーバーを再起動すれば解決です！

```
rails s
```

余談ですが、`db:create`や`db:migrate`などデータベース関連の更新でうまくいかない時はサーバーを`Ctrl+C`で閉じてからやることをおすすめします（結構このパターンでのエラーもあるので注意）。

#####※migrateの状態が分からない時に調べるコマンド

```
rails db:migrate:status
```
Perfumeボイスを高位合成/HDLコード生成でZynq実装！その3

#Octave FilterとEnvelope Filterの統合
前回設計した帯域分割用のOctave Filterと、包絡線を取るEnvelope Filterを統合して、いよいよVocoderとして動作する状態にする。

##シンセサイザー音
シンセサイザー音と声音を掛け合わせるので、Simulinkでシンセ音を作成する。
とりあえず、昔のFMシンセみたいな音で良いので、Audio ToolboxのWave Synthesizerブロックを使って、鋸波（-1から+1までインクリメントを繰り返すだけ）を生成する。このブロックはピアノの音とかでもMATLABに波形データを取り込めば、指定したピッチで出力してくれるので面白い。
なお、このブロックはFPGA実装できないので、後で変更する必要がある。

![waveSynthBlock.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/477651/886ef800-e73b-c2db-fe4c-61409e583ef9.png)

このブロックをドレミファソラシドの8音用意して、和音を鳴らせるようにして、それぞれのOn/Offスイッチを付けたモデルがこれ。MATLAB/Simulinkはこういったベクターデータを使った処理でも、モデルは1個だけ作ればいいのでとても簡単。
![waveSynthTop.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/477651/09416b64-ba56-3b3f-ab9a-1e6158c73ef6.png)

440Hzの基準音であるA3音を鳴らして周波数を確認。
![waveSynthSaw.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/477651/ec1b5f93-fa24-44a3-32c3-83833c39886d.png)

ちなみにドレミファソラシド各音の周波数はこの式で求められる。

```matlab:
Pitch = [60, 62, 64, 65, 67, 69, 71, 72];   % MIDI Note No.
waveFreq = 440*(2.^((Pitch-69)/12))';
```

鋸波はいい具合に高調波が乗っているのでこれがシンセ音としては良い。
![waveSynthSpectru.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/477651/ec30edba-11a3-7da4-abf2-c4a7a44ec18f.png)

こちらはいわゆるドミソのCコード（C言語じゃなくて和音のC）の時間軸応答とスペクトル。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/477651/5e91ea33-d6f7-b1ed-3b5e-956dff076187.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/477651/00dd17f6-2ff5-52fa-d430-b10447b462df.png)

##全コンポーネントの統合
これまで作成したコンポーネントを1つのモデルに統合する。
コンポーネント単位では期待した動作をしていたが、統合してシミュレーションすることで、問題が出てくることもあるので、このモデルでのシミュレーションはとても重要。また、このモデルで初めてVocoderとしての官能評価（ちゃんと音で聴いて評価）も可能になる。また、アルゴリズム自体の統合検証を主目的にしているので、まだ固定小数点化したりHDLコード生成できるようにはなっていない。

トップ階層には、音声データの入出力、シンセ音のOn/Offスイッチなどを配した。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/477651/b5b2081a-16d6-f34a-7e67-c1a42b24fcef.png)

実装対象であるサブシステムの処理内容：Octave Filterを音声データとシンセ音にかける（9ch出力）⇒音声データ側のOctave Filterの出力をEnvelope Filterにそれぞれ9ch分かける⇒Envelope Filterの出力をシンセ音のOctave Filter出力に9chそれぞれ掛ける⇒9chデータをMix（加算）⇒音量調整して終わり。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/477651/2ea00f2d-9f11-b84e-8ef3-2ee2d091a060.png)

A3音と音声データを入力にしてVocoderをかけてみると、下図のようにVocoder音は綺麗に440Hzとその倍音という構成のスペクトルになった。
上段の水色波形は音声入力の時間軸とスペクトル、下段赤色波形はVocoder出力の時間軸応答とスペクトル
![spectrum.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/477651/69765b6d-593c-da86-025f-a900befc373a.png)


その4に続く。次回はいよいよ実装かな。
実験コードを書く時の備忘録 ~ pythonでのロギング

# はじめに

新しく実験コードを書く時、ロギング周りのおまじないをいつも忘れるのでコピペできるよう書いておきます。

* 単一のスクリプトには収まらない程度には大規模な実験コード（機能ごとに自作のライブラリがある）
* とはいえ世間一般で広くライブラリとして使われるほどではない（所詮実験コード）

のような状況を前提にしています。

# コード

ライブラリのほうのファイルには以下のように書いておきます。
`logger.setLevel(log.INFO)`のところでこのファイル限定でのロギングレベルを設定しています。（機能ごとにデバッグしたいときに便利）

```module.py
import logging as log

# ファイルごとのlogger設定
logger = log.getLogger(__name__)
logger.setLevel(log.INFO) # モジュールごとに表示レベルを変える

def func():
    logger.info("From module.func()")
    logger.info("Key1\tValue")
    logger.debug("これは表示されない")
```

そして、このライブラリをimportして使うメインスクリプトの方には以下のように書きます。
`formatter`あたりの定義はお好みで。こちらの例のようにメッセージとそれ以前をタブ区切りしておくと、後でログファイルを処理するときに楽です。

**2020/06/03修正： 表示するログのレベルはハンドラ側で設定しなければならなかったようなので、そのように修正しました。**

```main.py
import logging as log

import module

# ファイルごとのlogger設定
logger = log.getLogger(__name__)

if __name__=="__main__":
    formatter = "[%(asctime)s] %(levelname)s - %(name)s\t%(message)s"
    handlers = [log.StreamHandler(), log.FileHandler("logfile")]
    for handler in handlers:
        handler.setLevel(log.INFO) # 表示するログのレベル
    log.basicConfig(
        level = log.DEBUG, format = formatter, handlers = handlers
    )

    # ログ出力
    logger.info("Call module.func() from main.")
    module.func()
    logger.debug("こっちは表示される")
```

# 出力例

以下のようなログが標準出力とファイルにそれぞれ出力されます。
どのファイルからログが吐かれたかわかるので便利。

```logfile
[2020-05-15 12:39:20,676] INFO - __main__       Call module.func() from main.
[2020-05-15 12:39:20,677] INFO - module From module.func()
[2020-05-15 12:39:20,678] INFO - module Key1    Value
[2020-05-15 12:39:20,680] DEBUG - __main__      こっちは表示される
```
スプレッドシート上でfacebookのURLをユニコード変換する関数

#facebookのリンクがマイマップ上で機能しない
飲食店支援のサイトを作っているのですが、グーグルフォームで店舗のfacebookを入力してもらうと、日本語URLを入れてくる方がいます。
が、これをそのままマイマップの個別項目に入力しても、リンクが途切れちゃって表示されません。

##関数で解決

こちらの任意のセルのところをfacebookアドレスの入力欄に設定しました。

=IF(ENCODEURL(REPLACE(SUBSTITUTE(SUBSTITUTE(IF(ISERROR(LEFT(任意のセル,FIND("?",任意のセル)-1)),任意のセル,LEFT(任意のセル,FIND("?",任意のセル)-1)),"www.",""),"m.facebook","facebook"),1,21,""))<>"","https://facebook.com/"&ENCODEURL(REPLACE(SUBSTITUTE(SUBSTITUTE(IF(ISERROR(LEFT(任意のセル,FIND("?",任意のセル)-1)),任意のセル,LEFT(T3,FIND("?",任意のセル)-1)),"www.",""),"m.facebook","facebook"),1,21,"")), "")

##具体的なポイント
###ENCODEだけでは対応できず
単純にUNICODE変換の関数「ENCODEURL」を使えるかと思ったのですが、こちらの関数を使うとfacebook.com/の最初のスラッシュも認識されず、ドメインがきれいに認識されません。
###モバイルページのリンク・wwwのありなしにも対応
.comあとのスラッシュを認識させるため、文字数で区切りましたが、m.facebookやwwwの有無で失敗するので先にモバイルを表記するm.facebookとwww.facebookなどの表記もすべてwwwなしのPCアドレスに統一してからユニコードに置き換えています。

##懸念点　Facebook以外のアドレスを入力されるとバグる
IF関数でfecebookを含む、などの条件分岐を加えたら良いのかもですが、たまに、ここにInstagramのURLなどを入力されるとバグります。

#現状は使ってません
上記の関数はグーグルフォーム→スプレッドシート→WordPressというフローの中で使っていましたがAppSheetに移行したので、不要になりました。

現行サイト　https://www.appsheet.com/start/5e535202-170c-4eca-889e-cb86425f8e34
TypeScriptで関数の引数の型を共通化したい！

※ネタです。実務での使用はお勧めしません。

TypeScriptでは、関数のシグネチャを切り出すことができます。おなじみ

```typescript
type MyFunc = (a: number, b: string) => void;
```

のようなやつです。実際に使用する際は

```typescript
const myFunc: MyFunc = (a, b) => {};
```

のようにすることで、左辺の型から右辺の引数の型が推論されます。しかしながら、この手法は通常の関数に対して適用することができません。

そこで、次のようにします。

```typescript
function myFunc2(...[a, b]: Parameters<MyFunc>) {}
```

スプレッド構文と分割代入の合わせ技です。どうしてもデコレーターを使いたいなどというときに役に立つかも？

※あくまでネタです。可読性が著しく下がるのでお勧めしません。
Azure 仮想マシンと外部の端末とのスループットを測定する

#やりたかったこと
以下の条件のもと、Azure 上の Ubuntu 18.04 LTS の仮想マシンと、外部の端末（今回は自宅のパソコン）のスループットを計りたい。

- 出来るだけ Firewall に穴をあけたくない。
- 自宅のネットワークのルーターの静的 NAT の設定を変えたくない。
- IP 制限のような形はとりたくない。

##ちょっとハマったところ
Azure の NSG の設定と、iPerf3 の通信の方向について。

#手順
##まずは Azure の仮想マシンを立てる
手順については割愛。下記ドキュメントを参照ください。
[クイック スタート:Azure portal で Linux 仮想マシンを作成する](https://docs.microsoft.com/ja-jp/azure/virtual-machines/linux/quick-create-portal)

##ネットワークセキュリティグループの設定
Azure Portal から iPerf3 が使うポートで通信が可能になるような設定を行う。

![コメント 2020-05-15 120845.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199379/00859fa5-efc8-bdf3-379e-4c8c0f66a219.png)

1. まずは「ネットワーク」タブを開く
2. 「受信ポートの規則を追加する」ボタンを押して、受信ポートの規則を追加。
3. 設定項目としては以下の通り。
    - ソース : Any
    - ソースポート範囲 : *
    - 宛先 : Any
    - 宛先ポート範囲 : 5201
    - プロトコル : Any
    - アクション : 許可
    - 優先度 : 任意で設定
    - 名前 : 任意で設定

##iPerf3 をインストール
おなじみの apt でどうぞ。サーバー側、クライアント側、両方にセットアップする必要があります。

```
sudo apt install iperf3
```

クライアント側が Windows 10 端末だとしたら、WSL を使うのが便利だと思います。
[Windows 10 用 Windows Subsystem for Linux のインストール ガイド](https://docs.microsoft.com/ja-jp/windows/wsl/install-win10)
※近々、WSL2 が正式リリースされる見込みなので、この辺の手順は変わるかもしれません。

##測定！
Azure の VM 側では iPerf3 をサーバーモードで実行します。

```
iperf3 -s
```

画面表示に Port 5201 で待ち受けてる旨表示されれば準備完了。
こんな感じ。

```
-----------------------------------------------------------
Server listening on 5201
-----------------------------------------------------------
````

そしたら、例えばクライアント側で以下のコマンドを実行。
一番重要なのは、"-R" オプションで、これをつけ忘れると、Azure VM 側からデータを送ろうとして NSG の設定や、自宅のファイアウォールやルーターの NAT に阻まれて失敗します。

```
iperf3 -c <AzureVM の Global IP アドレス> -R -t 60 -P 5 --logfile ログファイル名
```

上記のコマンドの場合、TCP のスループットを 60 秒間、5 スレッドで測定、となっているはず。
細かい iPerf3 の使い方については、ネットを見れば色々あると思うので、自分の好きな設定でやってください。

##参考までに測定結果抜粋
家の 300-500Mbps くらい出る光回線で TCP を使って転送速度を計ってみた結果こんな感じ。
Azure VM のサイズも "Standard D2s v3 (2 vcpu 数、8 GiB メモリ)" ということでそこまで大きくないやつ。

もっと速い環境とか、もっとでかい VM サイズを使えば、もっと速くなるかも。

```
[ ID] Interval           Transfer     Bandwidth       Retr
[  5]   0.00-60.00  sec   494 MBytes  69.1 Mbits/sec  1899             sender
[  5]   0.00-60.00  sec   493 MBytes  69.0 Mbits/sec                  receiver
[  7]   0.00-60.00  sec   474 MBytes  66.3 Mbits/sec  2085             sender
[  7]   0.00-60.00  sec   473 MBytes  66.2 Mbits/sec                  receiver
[  9]   0.00-60.00  sec   644 MBytes  90.0 Mbits/sec  1469             sender
[  9]   0.00-60.00  sec   643 MBytes  89.9 Mbits/sec                  receiver
[ 11]   0.00-60.00  sec   337 MBytes  47.1 Mbits/sec  2194             sender
[ 11]   0.00-60.00  sec   336 MBytes  47.0 Mbits/sec                  receiver
[ 13]   0.00-60.00  sec   450 MBytes  62.9 Mbits/sec  2440             sender
[ 13]   0.00-60.00  sec   449 MBytes  62.7 Mbits/sec                  receiver
[SUM]   0.00-60.00  sec  2.34 GBytes   335 Mbits/sec  10087             sender
[SUM]   0.00-60.00  sec  2.34 GBytes   335 Mbits/sec                  receiver
```

#まとめ
割とでかいデータをサクサクとやり取りできる環境があると思うので、是非 Azure 使ってみてください！
パフォーマンスが気になったら、この記事を参考にして、スループットとかも計ってみてくださいませ！
Bitriseのステータスをモニターに表示して物理的に監視するツールの紹介

# 背景
- Bitriseのワークフローが成功/失敗したらSlackに通知する仕組みはよくある
- 物理的にモニターにステータスを表示するツールの紹介はあまり見ない
- 複数のCIサービスのステータスを1画面でみたい

# ツール
https://github.com/marcells/node-build-monitor
こんな感じで表示される
![node-build-monitor.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/122255/cd16bdd4-74d0-c4ae-06db-62c409dcbc65.png)
https://builds.mspi.es/ にデモがあるので見てみてください。
表示形式の変更や、失敗したら音を鳴らすなどの設定も可能です。

# やってみる
GitHubのREADMEで詳細は記載されているので、ここではBitriseを使用したハッピーパスだけ紹介します。

- リポジトリを任意のディレクトリへClone
- https://github.com/marcells/node-build-monitor#bitrise にあるように、`app/config.json`を以下のように記述
 
```
{
  "monitor": {
    "interval": 300000,
    "numberOfBuilds": 12,
    "latestBuildOnly": false,
    "sortOrder": "date",
    "debug": false
  },
  "services": [
    {
      "name": "Bitrise",
      "configuration": {
        "slug": "BitriseでのアプリID",
        "token": "パーソナルアクセストークン"
      }
    }
  ]
}
```

- アプリIDはURLの`app`以降の文字列
<img width="579" alt="Screen Shot 2020-05-15 at 14.10.11.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/122255/d274c581-5f0c-625e-3ca0-3ea3c41c2a41.png">
- パーソナルアクセストークンはここで作成できる
<img width="1265" alt="Screen Shot 2020-05-15 at 14.12.56.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/122255/cc69daf6-f182-3911-a33d-399c17de33d6.png">

- Nodeでローカル実行(https://github.com/marcells/node-build-monitor#run-it-manually-during-development)
- Dockerで実行(https://github.com/marcells/node-build-monitor#run-it-with-docker-compose-in-production)

# 最後に
Slackの通知でも気づくことはもちろんできますが、オフィスのサイネージなどで表示しておけばエンジニアだけでなく、PMやデザイナーなどのプロジェクトメンバー全員がCIのステータスを気にする意識が持てるので、そこから何かいいアクションに繋がればいいなと思います。
用語まとめ

##IPアドレス
**インターネットを使うときに、コンピュータに割り当てられる住所のこと**

- 通信において、送信先を特定するときに使われる情報。
- コンピュータ向けのネットワークの住所。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/250960/d8d3a6a6-c74c-4019-7480-265b557ea89f.png" width=50%>



##ドメイン
**ドメイン名の略称**
**インターネットを使うときに、コンピュータやネットワークに割り当てられる住所のこと**

- インターネットやメールで使われる情報。
- 人間向けのネットワークの住所。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/250960/480c09e6-5ae9-4813-d75b-7a9913d12a1a.png" width=50%>

URLなら「http://」または「https://」の部分がドメイン名。
https://  ```qiita.com```  /drafts/95da7cefb6c2e45dc4b2/

メールアドレスなら「＠」以降がドメイン名。
hogehoge@ ```gmail.com```



##HTTP、 HTTPS
**通信するとき、WEBページのファイルとかを受け渡しするなどに使う約束事**

- HyperText Transfer Protocolの頭文字を取ってHTTP。プロトコルは約束事。
- 通信をする時は足並みを揃える必要がある。
　

- HTTP'S'のSは「Secure」、危険のない安全な、という意味。
- HTTPと違い、通信の内容が暗号化されて安全にWEBの情報をやり取りできる。


##キャッシュ
**訪問したWEBページの情報を一時的に保存しておく仕組み、またはその保存データ**

- 訪れたページの、表示するためにダウンロードしたWEBページの画像やテキストを  
一時的にPCや携帯の中に保存しておくこと。
- 自分の機器のなかに保存されているので、再表示のスピードが上がる。


<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/250960/e75a1498-7f6c-1ea1-2661-e66524fd3fff.png" width=50%>



##絶対パス、相対パス、ルート相対パス
**HTMLでのURLの書き方のこと**

1. 絶対パス
    - 誰からみても変わらない場所
    - 外部サイトのリンクなどを載せたい場合



2. 相対パス
    - 自分からみた相手の場所
    - 一般的なwebサイトの開発で使用



3. ルート相対パス
    - 最上階からみた相手の場所
    - 大規模案件（複数名で開発、ファイル構造が深い）の場合



<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/250960/17bfdf35-19a3-d629-95e8-2353317e27c8.png" width=50%>



##文字コード
**コンピュータ界における文字に割り当てられた数字のこと**
**表現される文字のもとになっている数字**

- コンピュータは「０」と「１」しか分からないので、例えば  

> 0000001　→　あ 
0000010　→　い
0000011　→　う  

　というように置き換えている。

- HTMLのheadのmetaタグで設定する

　　```<meta charaset="UTF-8">```  



<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/250960/1eb2afce-6efe-f3da-3c23-c08132dad787.png" width=50%>




##改行コード
**コンピュータに「改行しろ」と指示する制御文字のこと**

- 制御文字はコンピュータに指示を出す文字のこと。
- OSによって色々種類があるが、「改行」をコンピュータがわかるように文字列にしている。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/250960/475f99be-ff1d-d8dc-6fa0-e27249201251.png" width=50%>


##HTML、 XHTML、 HTML5

**HTMLの種類のこと**

1. HTML

- Hyper Text Markup Language の頭文字をとった名前。
- 文章の見出しをつけたり、リンクをつけたりしてページの構造が指定できる言語。  
テキストにハイパー（テキスト以上）の意味を持たせてマークアップ（目印）をつけて意味付けをする言語。

```html
<title>見出しだよ</title>
<ol>
　<li>リストアイテムだよ</li>
</ol>
```

2. XHTML

- xml と HTML の特徴を合わせたwebページを作るための言語、HTMLの拡張。
- xml は「しっかりルール通りに記述しないと表示できない」という特徴がある。
- 廃止になった‥。

```html
<title>見出しだよ</title>
<Ol>
　<li>リストアイテムだよ
<!-- エラーが起きて表示されない！　-->
</ol>

```

3. HTML5

- HTMLのバージョン5。今のところ最新の規格。
- 動画や音声の埋め込みが簡単になる、構造を表す新しい要素(header,sectionなど）が加わったので  
　HTMLを構造的によりスッキリ描けるようになった。
Typescriptのprivateメンバーにアクセスする方法(型アサーションを使わずに)

Typescriptのクラスのprivate, protectedメンバーは通常クラス外からアクセスできないが、``` obj['field名'] ```の書き方をすることでアクセスできる。  

``` typescript
class SomeClass {
    field: string = 'this is public.';
    protected protectedField: string = 'this is protected.';
    private privateField: string = 'this is private.';
    constructor() {
    }
}

const obj = new SomeClass();
console.log(obj.field);
console.log(obj.protectedField); // コンパイルエラー
console.log(obj.privateField); // コンパイルエラー
console.log(obj['field']);
console.log(obj['protectedField']); // コンパイルエラーにならない
console.log(obj['privateField']); // コンパイルエラーにならない
```

# 参考
https://github.com/microsoft/TypeScript/issues/19335
Typescriptのconst assertionは必ずしも書き換え不可能ではない

# const assertionとは

Typescript 3.4 から導入された機能。
以下のように記述することで、オブジェクト・配列に対して再帰的に readonly を付与した型として扱ってくれる。

``` typescript
const val1 = {
    value: 'hoge'
};
val1.value = 'fuga'; // これはエラーにならない

const val2 = {
    value: 'hoge'
} as const;

val2.value = 'fuga'; // コンパイルエラー

// as constを使わずに同じことをすると以下になる。
const val3: { readonly value: 'hoge' } = {
  value: 'hoge'
};

val3.value = 'fuga'; // こちらもコンパイルエラー
```

上記のようにas constを用いることで冗長な型指定をせずにreadonlyを付与、また型のwideningを抑制できる。  

# readonlyを付与していても書き換えができるパターン
readonlyが付与されていても同じ構造の型でreadonlyが指定されていない型に代入可能なため、
明示的に型のアサーションを行わずとも書き換え可能な型に代入ができる(配列が含まれない場合のみ、配列のときの挙動は後述)  
  
たとえば以下のように。

``` typescript
const cval = { prop1: 'prop1!' } as const;
// cval.prop1 = ''; // これはコンパイルエラー

const val: { prop1: string } = cval; // コンパイルエラーにならない
val.prop1 = 'prop1?';

console.log(cval.prop1); // => prop1?

function f(val: { prop1: string }) {
    val.prop1 = 'prop';
}
f(cval); // これもエラーにならない

console.log(cval.prop1); // => prop
```

## 配列が含まれている場合
readonlyがついた配列は動きが違い、readonlyではない配列に型アサーションなしで代入することはできない。

``` typescript
const cval2 = { arry: ['arry1'] } as const;
cval2.arry.push('arry2'); // これはコンパイルエラー

const val2: { arry: string[] } = cval2; // エラーになる

f2(cval2); // これもエラーになる
function f2(val2: { arry: string[] }) {
    val2.arry.push('arry!');
}


const cval3 = ['str1', 'str2'] as const;
cval3.push('str'); // これはコンパイルエラー

const val3: string[] = cval3; // エラーになる

f3(cval3); // これもエラーになる
function f3(val3: string[]) {
    val3.push('str');
}
```

# Object.freezeの場合
Object.freezeの戻り値の型は Readonly\<T\>なので同様に代入が可能  
ただし、実行時にエラーが発生する。

``` typescript
// Object.freezeを使った場合は実行時エラーになる。
const fval = Object.freeze({ prop: 'hoge' });
const val: { prop: string } = fval; // コンパイルエラーにならない
val.prop = 'xxx'; // 実行時にエラーが発生する
```

# 参考

https://typescript-jp.gitbook.io/deep-dive/type-system/readonly
JavaScript ページ内の文字列をすべて置換するコード

```js
javascript:void((function(){var d=document;d.body.innerHTML=d.body.innerHTML.replace(/FROM_STRING/g,'TO_STRING');})());
```


<!--

日本語のIT関係の説明を読んでいるとカタカナ語が多すぎて目がチカチカする。

ゆくゆくは、すべてのカタカタを拾って、一覧作るスクリプト
一覧をすべて英語に置き換えるスクリプト
置き換えた一覧の辞書的な解説を意味をページの最下部に付け加えるスクリプト
を実装して、拡張機能化したい。

```js
javascript:void((function(){var d=document;d.body.innerHTML=d.body.innerHTML
.replace(/エンティティ/g,'Entity')
.replace(/アトリビュート/g,'Attribute')
.replace(/システムシナリオ/g,'System scenario')
.replace(/シナリオ/g,'Scenario')
.replace(/リレーション/g,'Relation')
.replace(/ユースケース/g,'Use Case')
.replace(/アクティビティ/g,'Activity')
.replace(/カーディナリティ/g,'Cardinality')
.replace(/テーブル/g,'TABLE')
.replace(/システム/g,'SYSTEM')
.replace(/インプット/g,'input')
.replace(/マスタ/g,'master')
.replace(/ステップ/g,'STEP')
.replace(/トランザクション/g,'Transaction')
.replace(/オブジェクト/g,'Object')
.replace(/データベース/g,'DATABASE')
.replace(/パターン/g,'Pattern')
.replace(/ルール/g,'Rule')
.replace(/アメリカ/g,'US')
.replace(/ポピュラー/g,'popular')
.replace(/エンジニア/g,'Engineer')
.replace(/データ/g,'DATA')
.replace(/アクター/g,'Actor')
.replace(/サイト/g,' Site')
.replace(/サービスレベル/g,'Service level')
.replace(/レベル/g,'Level')
.replace(/ショッピング/g,'Shopping')
.replace(/チーム/g,'Team')
.replace(/レビュー/g,'Review')
.replace(/ブラッシュアップ/g,'BrushUP')
.replace(/フロー/g,'Flow')
.replace(/ツール/g,'Tool')
.replace(/リバース/g,'Reverse')
.replace(/ドメイン/g,'domain')
.replace(/デフォルト/g,'default')
.replace(/インフラ/g,'infrastructure')
.replace(/フローチャート/g,'Flow chart')
.replace(/デザイン/g,'Design')
.replace(/カラー/g,'COLOR')
.replace(/ワイヤーフレーム/g,'Wire-frame')
.replace(/キー/g,'KEY')
.replace(/サーバー/g,'SERVER')
.replace(/ハンズオン/g,'Hands-on')
.replace(/ユーザー/g,'user')
.replace(/チャート/g,'CHART')
.replace(/クラウド/g,'CLOUD')
.replace(/イメージ/g,'IMAGE');
})());
```
-->





###参考
[ページ内の文字列を置換するブックマークレット](https://kakikukeko.sakura.ne.jp/blog/replace-string-bookmarklet/)
【JavaScript】わたし的「スッキリJS記法」集

##はじめに
「こんな書き方もできるんだ！便利！」と感じた記法。
随時更新予定！

##プロパティを動的に生成する
プロパティをブラケットで括ることで、式の値から動的にプロパティを生成できる（Computed Propaty Names）。
※普通に書くとなぜか表示されないため、ブラケット部分コメントアウトしてます。

```sample.js
 let i = 0;
 let todo = {
   month: new Date().getMonth(),
   date: new Date().getDate(),
   //['ToDo'+ ++i]: '買い物',  
   //['ToDo'+ ++i]: '片付け',
   //['ToDo'+ ++i]: '掃除'
 };
 
 console.log(todo);
```
↓ 結果
<img width="488" alt="スクリーンショット 2020-05-15 10.51.57.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/459055/445072da-df80-2ce9-2aab-ed5927b1218d.png">


##配列の値の代入をまとめる（分割代入）
```sample.js
let data = [50, 45, 8, 10, 33, 1, 3];

//分割代入
let [x0, x1, ...other] = data;
  //通常の代入方法
  // var x0 = data[0];
  //var x1 = data[1];
  
console.log(x0, x1);
console.log(other);
```
↓結果
<img width="201" alt="スクリーンショット 2020-05-17 7.35.20.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/459055/6a20d54d-f628-8dda-4f21-30aa592b75b4.png">



##配列から重複する値を削除
```sample.js
const fruits = ['apple', 'banana', 'orange', 'banana'];
 
console.log(new Set(fruits)); //オブジェクト型
console.log( [...new Set(fruits)] );  //配列
```
↓結果
<img width="297" alt="スクリーンショット 2020-05-17 6.52.44.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/459055/0d2c1c5a-a99c-566d-5426-328baecfab4e.png">


##オブジェクト内のメゾット定義省略記法
```sample.js
 let member = {
   toString: function(){ //
   	//..処理
   },
 }

 //省略記法
 let member = {
   toString(){
     //..処理
   },
 }
```

##オブジェクトのキーと値の名前が同一のときの省略記法
```sample.js
function greet(firstName, lastName, message) {
   const data = {
     lastName,   //lastName: lastNameと同意
     firstName,
     message,
   };
   console.log(`わたしは${data.lastName} ${data.firstName}です、${data.message}`);
}
```


##return文の省略
```sample.js
const members = {
 name: "ハナコ"
};

//return文を省略したとき（一行の場合に省略可）
const getName = (key) => members[key];

   // 複数行で書いたとき
   // const getName = (key) => {
   //   return members[key];
   // };

const membername = getName('name');
console.log(membername); // 結果:ハナコ
```
rails データの保存の書き方 （書籍 vs 実務  ）

書籍などは

```ruby
def create
  @post = Post.new(post_params)
  if @post.save
    flash[:success] = "保存に成功"
    redirect_to posts_path
  else
    flash[:error] = "保存に失敗"
    redirect_to new_post_path
  end
end
```

実務では、調査の為の例外のログの吐き出しと、slcakなどの通知ツールする所までがセットで、保存機能が完結する

```ruby
def create
  @post = Post.new(post_params)
  @post.save! # 例外を発生させる
    flash[:success] = "保存に成功"
    redirect_to posts_path
  
  rescue StandardError => e
    logger.fatal "#{e.class}: #{e.message}"
    slack_notify(e) # slackへの通知
    flash[:error] = "保存に失敗"
    redirect_to new_post_path
  end
end
```
検索機能の実装（form_tag・ransack）

#検索機能の実装
form_tagとransackをそれぞれ用いた２種類の検索機能の実装を行いました。
特にこだわりがなければransackを用いたほうが簡単に実装できると感じました。

##form_tagを用いた検索機能の実装

タスク一覧が表示されるtasks#indexページに、検索フォームを実装しました。
検索フォームに入力されたキーワードがデーターベースにあるタスク名と部分一致した場合、そのタスクを表示されるようにしました。
（html.slim, bootstrapを用いました。）

```ruby:views/tasks/index.html.slim
h1 タスク一覧

= link_to '新規登録', new_task_path, class: 'btn btn-primary'

= form_with :method => "get", local: true do |f|
  .form-group
    = f.submit "Search", :name => nil ,class: "btn btn-outline-primary"
    = f.text_field :search
    
.mb-3
table.table.table-hover
  thead.thead-default
    tr
      th= '名称'
      th= '詳細'
  tbody
    - @tasks.each do |task|
      tr
        td= task.name
        td= task.description
        td= task.created_at
```

検索フォームで入力されたキーワードが引数としてparamsの中に入るように、
taskscontrollerのindexにsearch(params[:search])を追加しました。

```ruby:controllers/tasks_controller.rb
def index
    @tasks = Task.search(params[:search])
end
```

最後にtaskモデルにsearchというメソッドを作成しました。
検索フォームで入力されたキーワードがsearchメソッドの中で処理され、
キーワードと部分一致するタスクがあれば、その結果がページに表示されます。
部分一致するタスク名がない場合は、allとして全てのタスクが一覧として表示されます。

```ruby:models/task.rb
class Task < ApplicationRecord
  def self.search(search)
    if search
      where(["name LIKE ?", "%#{search}%"])
    else
      all
    end
  end
end
```

##ransackを用いた検索機能の実装

簡単なタスクアプリの一覧ページに実装しました。
（html.slim, bootstrapを用いました。）

Gemfileにransackを追加し、ターミナルでbundle installしました。

```ruby:Gemfile
gem 'ransack' #１番下でOK
```

検索に引っかかったタスクのみ表示するため、
indexメソッドを以下に書き換えました。

```ruby:controllers/tasks_controller.rb
def index
  @q = User.ransack(params[:q])
  @users = @q.result(distinct: true)
end
```

indexのviewsを書き換えました。

```ruby:views/tasks/index.html.slim
= search_form_for @q, class: 'mb-5' do |f|
  .form-group.row
    = f.label :name_cont, '名称', class: 'col-sm-2 col-form-label'
    .col-sm-10
      = f.search_field :name_cont, class: 'form-control'
  .form-group
    = f.submit class: 'btn btn-outline-primary'
```

また、ransackにはソート機能もあるため、並べ替えも行いたい場合はransackをお勧めします。