OmniAICreator/Qiita-1.07M huggingface dataset:
Railsで検索機能を実装する場合の4つのパターン

## パターン
- Ransackを使う
- コントローラーに書く
- Concernに書く
- Formオブジェクトを使う

## Ransackを使う
Ransack使うとソートも楽に書けそうだけど、
追々カスタマイズが面倒そうなのもあり最初から自分で書くことが多い。
あまり使ったこと無いので、ケースによっては触ってみたい。

## コントローラーに書く
```ruby
comments = Comment.where(post_id: params[:post_id]) if params[:post_id].present?
```

こんな感じでコントローラーに直接where文を書くケース。
何も考えずに書くとこうなることが多いと思う。
条件式が増えてくると複雑度上がってrubocopに怒られる。

## Concernに書く
```ruby
module Comments
  module SearchModule
    extend ActiveSupport::Concern

    def self.do_search(params)
     comments = Comment.all
     search_by_post_id(comments, params[:post_id]) if params[:post_id].present?
    end

    def self.search_by_post_id(comments, post_id)
      comments.where(post_id: params[:post_id])
    end
  end
end
```

```
Comments::SearchModule.do_search(params)
```

検索用のConcernモジュールに切り分ける方法。FatControllerはこれで解消できるので良さげ。
ただConcern特有のお作法があるので、自由に書きづらく、単純なクラスが使いたくなった。

## Formオブジェクトを使う
```ruby
class CommentSearchForm < SearchForm
    attribute :post_id, Types::Maybe::Coercible::Integer

    def do_search
     comments = Comment.all
     search_by_post_id(comments, post_id.value_or) if post_id.value_or.present?
    end

    private

    def search_by_post_id(comments, post_id)
      comments.where(post_id: post_id)
    end
end
```

```ruby
Dry::Types.load_extensions(:maybe)

module Types
  include Dry::Types.module
end

class SearchForm < Dry::Struct
end
```

```ruby
CommentSearchForm.new(params.to_hash.symbolize_keys).do_search
```

モデルに紐付かないパラメータを処理する時にはVirtusが便利で良く使われているそう。

ただ公式リポジトリをみると作者が後継ライブラリである`dry-rb`を推奨していたので、dry-rbを元に実装した。
https://github.com/solnic/virtus

厳密には型指定のみなので以下の3つ。
`dry-validation`でバリデーションも出来るようなので、後々使ってみたい。

```ruby
gem 'dry-monads'
gem 'dry-struct'
gem 'dry-types'
```

普通にgemをインストールすると一番古い0.5系になるので、最新の使い方とは違うので注意。

Formオブジェクトを使ったことで、次のようなメリットがある気がする。
まだ余り使いこなせてないので詳しい方の意見聞きたい。

* フォーム関連の処理が書かれていることがひと目でわかる
* FatController対策になる
* シンプルなクラスでテスト書きやすい
「=」を使わずにFizzBuzzを書く

#「=」を使わずにFizzBuzzを書きたい
特に意味はない
20までのFizzBuzzを「=」を使わずにいろんな言語で書いてみよう

#C言語

```c:c_fizzbuzz.c
#include <stdio.h>

int main(void) {
  int n;
  n++;
  while (1) {
    if (n%3 && n%5) {
      printf("%d", n);
    }
    else{
      if (!(n%3)) {
        printf("Fizz");
      }
      if (!(n%5)) {
        printf("Buzz");
      }
    }
    printf("\n");
    n++;
    if (n>20) {
      break;
    }
  }
  return 0;
}
```

初期化しないと0が入るのを利用（ただしコンパイラによる）
0がFalse、それ以外がTrueになる性質を使って条件分岐する

#Python
```python:python_fizzbuzz.py
def fizzbuzz(n):
    if n%3 and n%5:
        return n
    else:
        if not n%3 and not n%5:
            return "FizzBuzz"
        elif not n%3:
            return "Fizz"
        elif not n%5:
            return "Buzz"

for x in list(map(fizzbuzz, range(1,21))):
    print(x)
```
Pythonはインクリメントがないため
rangeで整数のリストを作成
mapで一気すべてに関数を掛ける方式に
地味に

```python
print("hoge", end="")
```

が使えないのでつらかった

#Java
```java:java_fizzbuzz.java
class java_fizzbuzz {
  static int n;
  public static void main(String[] args) {
    n++;
    while(true){
      if (!String.valueOf(n%3).equals("0") && !String.valueOf(n%5).equals("0")) {
        System.out.print(n);
      }
      else{
        if (String.valueOf(n%3).equals("0")) {
          System.out.print("Fizz");
        }
        if (String.valueOf(n%5).equals("0")) {
          System.out.print("Buzz");
        }
      }
      System.out.println();
      n++;
      if (n>20) {
        break;
      }
    }
  }
}
```
クラスのメンバ変数は0で初期化されることを利用
Booleanが0 or その他で処理していないことが判明
一旦Stringに変換してequalsで比較することでイコールを使わずに済んだ

#まとめ
どうしても「=」を使いたくないときに参考にしてください
なお変数は明示的に初期化するようにしましょう

もっといい書き方がある！
この言語ではこうできる！
等ありましたらコメントへお願いします
C#でメモ化を実装してみた

C#でデコレーター風味でメモ化を実装してみました。
デコレーターをC#で実装しようとやっていた際に思いついたのでここに置いておきます。
# メモ化とは
重い計算をする際によく用いられます。
同じ引数だと同様の結果を返すような場合、二回目も三回目も同様に計算を行うのは処理時間の都合上**非常に無駄**です。
なので、引数と戻り値のリストを作成しておいてリストにある引数が渡された場合計算をせずにリストから結果を渡すというものです。
# 実装方法
*Dictionary*で計算結果を保持して引数が存在するかの比較を行います。
今回は以下のように実装しました。
計算を行うメソッドはこのように記述しました。

```csharp
public long Decorate_square (long x) {
    return x * x;
}
```

今回は簡単に二乗を行うメソッドです。
そして実行を代行するメソッドはこちらです。

```csharp
public long square (long sx2) {
    Func<long, long> f = Decorate_square;
    Decorator.memo<long, long> memo = new Decorator.memo<long, long> ();
    return memo.Invoke (data, f, sx2);
}
```

Funcでメソッドを変数にいれてメモ化のメソッドの型を指定してメモ化のメソッド経由で実行します。
そして、メモ化を行うメソッドはこのように実装しました。

```csharp
using System;
using System.Collections.Generic;
using System.Reflection;
public class Decorator {
    public Dictionary<object,object> data = new System.Collections.Generic.Dictionary<object,object>();
    public class memo<arg01, ans> {
        public ans Invoke (Dictionary<object,object> data, Func<arg01, ans> f, object x) {
            if (data.ContainsKey((arg01)x)) {
                Console.WriteLine ("-O-");
                return (ans) data[(arg01)x];
            } else {
                Console.WriteLine ("-X-");
                ans res = (ans) (object) f ((arg01) x);
                data[(arg01)x] = res;
                return res;
            }
        }
    }
}
```

# これらを書くのは面倒では。。。
はい、その通りです。
そこでRoslynのVisualStudio拡張機能の修正機能です。
私の別の記事で書きましたがこれはソースコード自体を自動で書き換えることができます。
なので、[memo]などと記述した際に自動でデコレーターメソッドを記述できるようになると非常に良いです。

# 全コード
```csharp:generic.cs
using System;
using System.Collections.Generic;
using System.Reflection;

public class deco2 : Decorator {
    public long square (long sx2) {
        Func<long, long> f = Decorate_square;
        Decorator.memo<long, long> memo = new Decorator.memo<long, long> ();
        return memo.Invoke (data, f, sx2);
    }

    public long Decorate_square (long x) {
        return x * x;
    }
}

```

```csharp:memo_decorator.cs
using System;
using System.Collections.Generic;
using System.Reflection;

public class Decorator {
    public Dictionary<object,object> data = new System.Collections.Generic.Dictionary<object,object>();
    public class memo<arg01, ans> {
        public ans Invoke (Dictionary<object,object> data, Func<arg01, ans> f, object x) {
            if (data.ContainsKey((arg01)x)) {
                Console.WriteLine ("-O-");
                return (ans) data[(arg01)x];
            } else {
                Console.WriteLine ("-X-");
                ans res = (ans) (object) f ((arg01) x);
                data[(arg01)x] = res;
                return res;
            }
        }
    }
}
```

以下は実行部分です

```csharp:Program.cs
namespace twice_arg_memo {
    class Program {
        static void Main (string[] args) {
            deco2 d = new deco2 ();
            Console.WriteLine (d.square (2));
            Console.WriteLine (d.square (2));
            Console.WriteLine (d.square (3));
            Console.WriteLine (d.square (4));
            Environment.Exit (0);
        }
    }
}
```
Pythonにてcsvファイルを用いてFirestoreにデータを一括登録する

# 概要

以下のことは済ませているものと考えてください。契約状態によって確認事項が違いますので、簡単に。

- Firestoreを有効化
- APIキーの発行（firebaseにて出来ます）
- firebaseはnativeモード

# データ構造

## csvファイル体裁
区切りは「,」としています。たまに規則通りになっていないcsvファイルがあったりするので、その辺りは気を付けてください。

![testimage.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/159492/f7ede899-25e1-2cb1-0372-1e0e6de39092.jpg)

## DB構成

今回は簡単な構成です。uniqueというところを主キーのように考えており、重複なしです。

```:DB構成
unique ---------- item1 
       |           | a : 1
       |           | b : 2
       |            ∟ c : 3
       |
       |--------- item1 
       |           | a : 4
       |           | b : 5
       |            ∟ c : 6
       |
       ∟ --------- item1 
                   | a : 7
                   | b : 8
                    ∟ c : 9
```

# プログラム

以下のようなことをやっています。

- データ読込み
- firestore変数初期化
- 一つずつfirestoreに登録

```python:sample.py
import firebase_admin
from firebase_admin import credentials
from firebase_admin import firestore
import csv 

# データ読込み
def read_data(file_name):
    csv_data = None 
    with open(file_name) as r:
        reader  =csv.reader(r)
        csv_data = [row for row in reader]# 二次元配列で変数に格納
    print('read end')
    
    return csv_data

# firebase 初期化処理
def firebase_init(api_key):
    api_key = credentials.Certificate(api_key)
    # 前回の初期化状態が残っているみたいなので、まずは前回の初期化状態を削除
    if(len(firebase_admin._apps)):
        firebase_admin.delete_app(firebase_admin.get_app())
    firebase_admin.initialize_app(api_key)
    print("init end")

# メイン処理／データ登録
def exe(data):
    db = firestore.client()# firestoreクライアント変数
    header = data[0]# ヘッダ取得
    doc_base = db.collection(header[0])# uniqueの所を設定
    print("proc start")
    for i in range(1, len(data)):# 2行目からがデータなので、1から
        doc_ref = doc_base.document(data[i][0])# a,b,cのところを設定
        buf = {}# バッファ
        for j in range(1, len(data[i])):
            buf[header[j]] = data[i][j]
        doc_ref.set(buf) # firestoreに保存
    print("poc end")

def main():
    file_name = "csvファイル名"
    api_key = r'api_key'

    # ファイル読込み
    data = read_data(file_name)

    # firebase初期化
    firebase_init(api_key)

    # メイン処理
    exe(data)

if __name__=="__main__":
    main()
```

# おわりに

今回まっさらな状態での一括登録ですが、途中でfirestoreからデータを一括取得し、差分チェックしてfirestoreに更新するというようにすると良いとは思います。api呼び出し回数が減るので、料金が減るんですかね。試算はしていませんが。
Apache Tomcat連携サーバで、CookieにSameSite属性を設定する方法

2/17以降にGoogle Chromeを使って、
「①あるドメインのURL」→（POST or GET）→
「②別ドメインのURL」→（POST）→
「③元のドメインのURL」
の順でページ遷移した場合、セッションが切れるように仕様が変わる事が判明したので、
対応方法をメモ。
（例：リンク型決済サイト等を利用して、クレカ決済の部分だけ他社サービスを利用している場合等）


#発生する現象
2/17までのChromeでは、上記のように
「①あるドメインのURL」→（POST or GET）→
「②別ドメインのURL」→（POST）→
「③元のドメインのURL」
の順でページ遷移をした場合でも、③の時点で①のセッションが切れずに残っています。
その理由は、①の時点でCookieに保存されたセッション用キーが、③の際にもサーバ側に送信されているからです。

今回、この挙動が変わり、SameSiteという属性をNoneに設定していないCookieについては、③の際に元のドメインのサーバに送信されなくなります。
すると、元のドメインのサーバ側では③のリクエストが①のリクエストど同一セッションである判定ができませんので、
セッション切れと同じ状態となります。

この変化は、以下の理由によります。
サーバ側では、Cookieに保存されたセッション管理用のキーをブラウザからサーバに送信してもらう事で同一セッションかを判別しています。
Cookieに「SameSite」という属性が明示的に指定されていない場合、
2/17までのGoogle Chromeではデフォルトが「SameSite=None」を指定した場合と同じ挙動をしていました。
「SameSite=None」では、あるドメインのサイトから別ドメインのURLにPOSTやGETで遷移する場合でも、
遷移先のドメインで設定したCookie情報をHTTPヘッダーに入れて送信してくれるので、
遷移先のドメインで既にセッション管理している場合は、そのセッション情報を引き続き利用する事ができました。

これは、「ユーザーがあるサイトにログイン中である場合、第3者が作成した別サイトから、そのサイトに対してログイン中のセッションを使ってPOSTリクエストを送る事ができる」
事を意味しており、CSRF攻撃を可能とする危険性を持った仕様です。

その属性が、2/17日以降、Chrome 80ではデフォルトで「Lax」を指定した場合と同じ動作に変わります。（一度にすべてのChromeで変わるのではなく、徐々に対象を拡大するようです）
「SameSite=Lax」は「GETの場合は遷移先ドメインが管理しているCookie情報を一緒に送信しても良いが、POST等、特定の条件ではダメ」という指定です。

その結果、「SameSite」属性を明示的に指定していないサイトは、CSRF攻撃に対して安全性が高まりますが、
半面、リンク型決済サイト等、別ドメインのページから決済完了画面にPOSTで遷移する機能を利用するECサイトでは、
セッションを維持するためのCookieが決済サイトへの遷移する前とは異なる値になってしまい、
同一セッションとして処理する事ができなくなります。


#事前の検証方法
１：Chromeで以下の設定ページを開きます。
chrome://flags/
２：設定の中から、「SameSite by default cookies」と「Cookies without SameSite must be secure」を「Enabled」に切り替えてChromeを再起動します。
これで、2/17以降のChromeのデフォルトと同じ動作になるはずです。
３：ChromeでF12ツールを開き、「Application」タブの左メニューで「Cookies」を開きます。
※JSESSIONIDについては、フィルターに「SESSION」等を入れれば絞り込めます。
４：JSESSIONIDが、決済サイトへの遷移前と、戻って来た時とで違っていたら、セッションは切れています。


#対応方法
Cookie「JSESSIONID」にSameSite=None属性をつければ、現行のデフォルトと同じ動作となるので、セッション切れを回避できます。
ただし、このCookieは自動で作成されているので、何らかの方法で割り込んでSameSite=Noneを付ける必要があります。
サーバがApacheでTomcatに連携している場合、
以下の方法でサーバ側の対応を行えば、SameSite=None属性をCookieに追加することができます。


□概要
mod_headers.soを使って、Apache→Tomcatの連携部分の設定に、
Cookieに一律SameSite=Noneを設定する指定を追加します。
以下に手順を書きます。

１：Apache→Tomcatの連携部分の設定ファイルを開きます。
※「/etc/httpd/conf.modules.d/00-proxy.conf」または、
「/etc/httpd/conf.d/proxy_ajp.conf 」等

２：各転送設定を以下のように変更します。

□変更前：　
`ProxyPass /{アプリケーション名}/ ajp://localhost:8009/{アプリケーション名}/`

□変更後：
`<Location /{アプリケーション名}/>
ProxyPass ajp://localhost:8009/{アプリケーション名}/
Header edit Set-Cookie ^(.*)$ $1;Secure;SameSite=None
</Location>`

３：Apacheを再起動します。



以上
自分用コードメモ - 特定のCookieを持つかどうか判別して処理をする

よくある、特定のCookieを持つかどうかで処理を分けたいときに使うコード。例えば特定のアンケートを回答した人には同じアンケートを表示しないなどといった運用をするときに使う。


```.js
function getCookieValue(cookie_name) {
    const cookies = `; ${document.cookie}`
    const values_string = cookies.split(`; ${cookie_name}=`);
    if (values_string.length > 1) return values_string.pop().split(';')[0];

}

/* 上記の修正前。filterとか正規表現を不要にした。
function getCookieValue(cookie_name){
  const cookie_name_reg = new RegExp(cookie_name+"=");
  const target_cookie = document.cookie.split(";").filter(function(c){return c.match(cookie_name_reg)});
  if(target_cookie.length != 0) return target_cookie[0].split("=")[1]
}
*/

function setCookie(cookie_name, cookie_value, days){
  if( !getCookieValue(cookie_name) ){
    doSomthing();

    const cookie_path = "path=/;"
    //有効期限の設定
    const nowdate = new Date();
    nowdate.setTime(nowdate.getTime() + days*24*60*60*1000); //日付データ
    const limit_date = nowdate.toGMTString(); //GMT形式に変換.
    const cookie_expires = "expires=" + limit_date + "; ";

    document.cookie =   cookie_name + "=" + cookie_value + ";" +
                        cookie_expires + cookie_path;
 }
}

function doSomthing(){
  console.log("doSomthing!");
}
```

---
もっと洗練された方法があれば、細かい指摘でも結構なのでコメントください！！

**[*Qiita アカウント*](https://qiita.com/MasatoEmata)**
**[*Twitter アカウント*](https://twitter.com/masatoemata)**
チェックボックス検索のテストパターン（検索対象データが単一の場合）

検索条件がチェックボックス形式のときのテストパターンについて、自分の考えを纏めてみました。

##前提の話
チェックボックス形式の検索条件がどういうものか、以下で説明します。

###チェックボックス形式の検索条件
チェックボックス：選択可能な項目が列挙されていて、項目を選択すると選択状態となる
※（一般的には）左隣にある☐が☑となる

複数の不変の選択肢があり、複数の設定が可能なところが特徴です。

※中には、プルダウン形式で複数の設定が可能なUIとなっている場合もあります。
　チェックボックス形式と同じように考えてもよいと思います。　

検索条件が不変の選択肢の場合の多くは、検索対象も不変の場合が多いです。
そういった意味では、テストパターンは有限といえます。

###注意点
複数の選択肢が設定可能な項目の場合、検索対象のデータも複数設定可能な場合があります。今回は、設定値は単一のケースを記載します。


##テストパターンについて
自分は、検索条件が検索条件がチェックボックス形式のときは、一致している/していないを網羅できるようにパターンを作成しています

###事前準備データ
| |対象項目の設定値|その他（対象に関連の無い項目）|
|:--|:--|:--|
|データA|[未設定]|任意|
|データB|データ①|〃|
|データC|データ②|〃|
|データD|データ③|〃|
※データが3パターンあり、検索条件として3つのチェックボックスが表示されている場合を想定

###検索結果の確認

| |対象項目の<br>検索条件|その他の<br>検索条件|取得結果|補足|
|:--|:--|:--|:--|:--|
|no.1|[未設定]|[未設定]|A, B, C, D|①：F、②：F、③：F|
|no.2|①のみ|〃|B|①：<font color="Red">T</font>、②：F、③：F|
|no.3|②のみ|〃|C|①：F、②：<font color="Red">T</font>、③：F|
|no.4|③のみ|〃|D|①：F、②：F、③：<font color="Red">T</font>|
|no.2|①、②|〃|B, C|①：<font color="Red">T</font>、②：<font color="Red">T</font>、③：F|
|no.3|①、③|〃|B, D|①：<font color="Red">T</font>、②：F、③：<font color="Red">T</font>|
|no.4|②、③|〃|C, D|①：F、②：<font color="Red">T</font>、③：<font color="Red">T</font>|
|no.4|①、②、③|〃|B, C, D|①：<font color="Red">T</font>、②：<font color="Red">T</font>、③：<font color="Red">T</font>|

　
ポイントとして、まずチェックボックス検索に限らないところでは
　●検索条件が複数設定できる場合は、対象以外の検索条件は必ず**<font color="Red">未設定</font>**にし、試験対象に影響が出ないようにする。
　　⇒組み合わせのテストは、それぞれの対象1つずつのテストが完了してから行う。
　●事前準備データの設定値が未設定の場合も考慮する。
　●検索条件が未設定の場合も考慮する。
辺りを思っています。

チェックボックス検索に関連するポイントとしては、
　●検索条件に表示されている全てのデータを用意して、各検索条件がT/Fだった場合を網羅するようなパターンとする
辺りを思っています。

##注意（件数が膨大になる場合）
チェックボックス検索の場合、パターンは有限ではありますが場合によってはパターンは膨大になります。
※データがn種類あった場合、未設定を含めると
　用意可能なデータ：n+1種類
　検索条件：2^n種類
　⇒2^n回検索を行い、その度にn+1種類のデータのヒットする/しないを確認する
そんな場合は、だいたい以下のパターンに絞って確認しています。
　・チェックボックスが未設定の場合
　・チェックボックスが1つのみの場合
　・チェックボックスが複数件ONの場合
　　　⇒だいたい、1/4～1/2ほど
　・チェックボックスが全てONの場合

###例：①～⑱まで、データパターンがあった場合

| |対象項目の<br>検索条件|補足|
|:--|:--|:--|:--|:--|
|no.X|[未設定]|未設定の確認|
|no.X|①のみ|チェックボックス1つずつの確認|
|no.X|②のみ|〃|
|…|…|…|
|no.X|①、③、⑤、⑦|チェックボックス複数の確認|
|no.X|②、④、⑥、⑧|〃|
|no.X|⑨、⑪、⑬、⑮|〃|
|…|…|…|
|no.X|①～⑱|チェックボックス全ての確認|

期日やコストによって、どこまでやるか判断しています。

##最後に

検索画面の試験の場合は、個々の検索条件が仕様通り動作しているかを確認した後に、それぞれの条件を組み合わせた検索についても試験を行う必要があると思っています。流れとしては、
　①個々の検索条件が正しく動作する
　②それぞれの検索条件を組み合わせた検索が正しく動作する
だと思っています。
⇒①の試験で個々の条件の担保を取ったうえで、②にて組合せの試験を行うなど

検索条件を組み合わせる場合の試験パターンについては、以下のページに記載しています。
[複数の条件がAND条件/OR条件で繋がれているときのテストパターン](https://qiita.com/yasuhisa_ito/items/ca7ad58abd2b408110f0)
参考になれば幸いです。

あと、今回は検索対象データが単一の場合について記載しましたが、検索対象データが1データに複数設定できる場合もどこかで投稿したいと思います。
Ruby の型関連の情報まとめ

2020年にリリースが予定されている Ruby のバージョン3から型定義・静的解析ができるようになる予定なので、現時点（2020年9月30日）の情報をまとめてみる。

## 型シグネチャ
- 型情報を定義したファイル
  - Ruby3 では Ruby ソースコード本体に型情報を書かない
  - 拡張子は `.rbs`
  - ⇨記述のための言語は Ruby ではない
- 型シグネチャをもとに後述の型検査を実行する
- Ruby3 に同梱される

#### リポジトリ
RBS: https://github.com/ruby/rbs

## 型プロファイラ
- 型シグネチャ無しで型検査する（レベル1型検査）
- 型エラーの可能性を指摘
- 型の推論結果を用いて型シグネチャのプロトタイプを生成する
- 型プロファイラも Ruby3 本体に同梱される

#### リポジトリ
TypeProf: https://github.com/ruby/typeprof/blob/master/doc/doc.ja.md

## 型検査器
- 型シグネチャを使って静的検査をする（レベル2型検査）
- 高速化は目的としておらず、エラーなどを実行前に発見することが目的
- こちらは Ruby3 には同梱されず、各々 gem をインストールして使うことになる

#### リポジトリ
steep: https://github.com/soutaro/steep
sorbet: https://github.com/sorbet/sorbet

## 参考資料
- [型なし言語のための型](https://speakerdeck.com/soutaro/xing-nasiyan-yu-falsetamefalsexing)
- [Ruby 3 の型解析に向けた計画](https://www.slideshare.net/mametter/ruby-3-149141725)
- [RubyKaigi 2019 "A Type-level Ruby Interpreter for Testing and Understanding" の発表要旨](https://techlife.cookpad.com/entry/2019/04/16/164858)
- [The challenges behind Ruby type checking
](https://speakerdeck.com/soutaro/the-challenges-behind-ruby-type-checking?slide=39)
- [Sorbet: A type checker for Ruby 3 you can use today!](https://sorbet.org/docs/talks/ruby-conf-2019)
- [Type Checking Ruby Programs with Anntations](https://soutaro.hatenablog.com/entry/2017/09/17/151221)
hibernateの複合キー

```java
@Entuty
@Table(name="sample")
public class SampleRecord {

    @Column(name = "id")
    private long id;

    @Column(name = "name")
    private String name;

    // 省略
}
```


```
Exception in thread "main" org.hibernate.AnnotationException: No identifier specified for entity: com.sample.jpa.SampleRecord
```

`@Entity`を使う場合は`@Id`を指定しないといけないっぽい

```java
@Embeddable
@Table(name="sample")
public class SampleRecord {

    @Column(name = "id")
    private long id;

    @Column(name = "name")
    private String name;

    // 省略
}
```
`@Embeddable`を使ってみるとコンパイルエラーすることなくできました。
C# 最大公約数と最小公倍数

競技プログラミングの問題を解いている際に、最小公倍数か最大公約数を求める必要に迫られる場合がある。
今までは、先人らのコードをコピペして使わせて頂いていたのだが、モラル的に良くない気がしたので自分用に書いたため、貼っておく。
型などlongの方が良いかも。
最大公約数はユークリッドの互除法より得られ、また最小公倍数は元の2数をかけて最大公約数で割ったものから得られる。

9,18の場合...最大公約数は9,これらをかけて9で割るので18が最小公倍数となる。


```csharp

// 最大公約数
public static int Gcd(int a, int b)
    {
        var x = 0;

        while (true)
        {
            // 割り切れたら
            if (a % b == 0)
            {
                return b;
            }
            else
            {
                x = a % b;
                a = b;
                b = x;

            }
        }
        
    }
// 最小公倍数
    public static int Lcm(int a, int b)
    {
        var x = Gcd(a, b);

        return a * b / x;
    }

```

呼び出し方としては、こんな感じで(適当にMain関数の中で呼んだけど)指定してあげてね。

```csharp

using System;

    public static void Main()
    {

      var a = 10;
      var b = 20;
      Console.WriteLine(Gcd(a,b));
      Console.WriteLine(Lcm(a,b));

    }

```
【DS】複数行を同時に処理するときの特殊なアルゴリズム

query databaswの部分は後半の処理を考慮した設定。
1件目だけ1列目を通る
以降は「企業コードがかわった場合」をとおる
Query　DBとも関係があるかも→クエリからひっぱてきて登録したら2件目以降の部分をとおる

1件目かどうかが大事なポイント→liting_codeの変数に値が格納されているか否かが大事　これがなければ1件目になる

<img width="692" alt="2.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556084/5c295317-8b0e-d9d6-153c-6202fba40cd0.png">
docker-composeで.envで開発/本番環境を切り替える

## docker-composeで簡単に環境を切り替えたい
webアプリの開発でサーバーやDBを同時に立ち上げるためにdocker-composeを使用していて
テストや動作確認もコンテナ上で起動してやる場合、webアプリを動かすコンテナとして

- 「production環境」 alpineをベースに最小限のパッケージとモジュールを入れて余分なキャッシュ等も消し、できるだけイメージサイズを小さくしたもの
- 「development環境」 テスト関連やgit、分析ツール等の開発に必要なものが入ったやや大きいサイズのイメージのもの

のように２種類以上の環境を使い分けたくなりますが、これをどう切り替えたらいいかという話

単純にdocker-compose.ymlを複数作って -f　でファイル名を指定する方法や
公式ドキュメントにあるように[拡張用のcomposeファイルを作る方法](http://docs.docker.jp/compose/production.html)もありますが、doker-compose内で展開できる[環境ファイル](http://docs.docker.jp/compose/env-file.html)の`.env`ファイルを使用して切り替えるのが色々と応用がきくのでお薦めです。

## 作りたい環境に合わせたDockerfileを用意する
まず環境に合わせたDockerfileを用意します。
例えば

- 「Docker_production」 
- 「Docker_development」
 
という感じに分けます。


## .envを用意しdocker-composeに変数を指定する
.envファイルをdocker-compose.ymlと同じ階層に用意するとdocker-compose内で変数のように展開できます。
これを利用するとbuildするDockerfileのファイル名も変数で切り替えることができます。
以下はRailsを想定してますが、他のケースでも応用できると思います。
### docker.compose.yml
```
version: '3.7'
～　省略 ～

  rails:
    build:
      context: .
      dockerfile: Dockerfile_${DOCKER_RAILS_ENV}
    image: webapp_${DOCKER_RAILS_ENV}
    volumes:
      - .:/myapp
      - public:/myapp/public
      - sockets:/myapp/tmp/sockets/
    ports:
      - "3000:3000"
    links:
      - db
    environment: 
      RAILS_ENV: $DOCKER_RAILS_ENV
      DB_HOST: db
      DB_USERNAME: $DB_USERNAME
      DB_PASSWORD: $DB_PASSWORD
      RAILS_SERVE_STATIC_FILES: 

～ 省略～
```
### .env
```
DOCKER_RAILS_ENV=production
DB_USERNAME=root
DB_PASSWORD=password
```

### buildするDockerfile名に.envに書かれた環境変数を利用する 
docker-compose.ymlの`dockerfile: "Dockerfile_${DOCKER_RAILS_ENV}"`の部分で
production環境用に作った`Docker_production`というファイルと`Docker_development`というファイルを.envで定義した環境変数で指定し切り替えています。[^1]
.envを`DOCKER_RAILS_ENV=development`とすればdevelopment環境でupができます。
[^1]: MySQLやRailアプリに指定するDBパスワード等も一緒に記述してます。

### 作成されるイメージ名を指定する
デフォルトでは_"ディレクトリの名前"___"指定したコンテナ名(rails)"_になるので環境を変えても同じイメージ名がつきます。
build:の下にimage:を指定するとイメージ名を指定してビルドできます。
->https://docs.docker.com/compose/compose-file/#build
`image:webapp_${DOCKER_RAILS_ENV}`でビルドされるイメージ名も環境で変わるようにしています。[^2]


[^2]: `image: app_name:tag_name`の形式でタグも指定できるのでそちらに環境の名前をつけてもいいです。

ちなみに`${DOCKER_RAILS_ENV:-development}`とすれば変数が指定されていない場合のデファルト値がdevelopmentになります
必要であれば使用するDBのイメージ名で`image: mysql:${DB_VER-5.7}`として公式イメージのバージョンの指定にも応用できます。
参考：[案外知られてないdocker-composeの環境変数定義の記法](https://qiita.com/fagai/items/b944950b26af19453c02)

### 注意点
ホストマシンのLinux等に.envで指定した変数と同名の環境変数が設定されてる場合、**ホストマシンの環境変数が優先される**ので、意図的に使いたい場合を除いて名前がバッティングしないようにする必要があります。
今回の例だとRAILS_ENVは使われやすいので、DOCKER_RAILS_ENVとしてかぶらないようにしてます。

またデフォルトで.gitignoreや.dockerignoreに.envは入ってないので間違ってもAWSのキーなどのクレデンシャルを.envに記述してgithubやdockerhub等にアップロードしないように注意しましょう。


## Dockerfileは複数必要か
結局この方法だとdocker-composeは１つで済みますがDockerfileの方は２種類以上必要になります。
これも一つにできないかと考えたのですが、複雑になるのでやめました。[^3]

[^3]: やろうと思えばdocker-composeの`build:`の`arg:`オプションを使ったり`env_file:`を指定して、パッケージのリストを切り替えるみたいなことはできます。環境によってDockerfileが殆ど変わらない場合はそれでいいのですが、自分の場合はかなり違うこともあって無駄に見づらく複雑になるだけでした。

アプリのルートをどうしてもDockerfile一つにしたい場合は、片方のDockerfileを配下に別のディレクトリを作って入れてdocker-composeの`context:`の部分を切り替えるようにすればいいと思います。
【DS】log4jについて

"https://docshield.kofax.com/RPA/ja_JA/10.4.0_rp4hrvtqrq/help/kap_help/reference/c_creatinganddeletingtables.html
ここにあるが、DBのテーブル名は変えられないらしい　細かい数字も変えられない"

C:\Users\kanrihonbu_RPA07\AppData\Local\Kapow\10.4.0.3_314\Configuration
ここに設定ファイルがいろいろある
https://www.techscore.com/tech/Java/ApacheJakarta/Log4J/1/
log4jの解説
↓
一行ずつログファイルを出す　〇〇：〇〇　処理開始というログファイルを作成

以下もみてみる
https://knowledge.bizrobo.com/hc/ja/articles/360038154052?input_string=mc%E3%81%AE%E3%83%AD%E3%82%B0%E7%94%BB%E9%9D%A2%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6
https://docshield.kofax.com/RPA/ja_JA/10.4.0_rp4hrvtqrq/help/kap_help/mgmtconsole/c_logsmc.html
CSS : アイコンのタップ範囲を表示に影響なく広げる

- 16x16のアイコンをスマホ、タブレットでタップさせるのは厳しい
- でも不格好なのでアイコンを大きくしたくない

以上の要件から生まれたのがコレ↓

<p class="codepen" data-height="290" data-theme-id="5973" data-default-tab="result" data-user="KazuyoshiGoto" data-slug-hash="gOpamxw" style="height: 290px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="アイコンのタップ範囲を広げるスタイル">
  <span>See the Pen <a href="https://codepen.io/KazuyoshiGoto/pen/gOpamxw">
  アイコンのタップ範囲を広げるスタイル</a> by Kazuyoshi Goto (<a href="https://codepen.io/KazuyoshiGoto">@KazuyoshiGoto</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

従来のアイコン要素に一つクラスを加えるのみで実現させている。

## HTML

```html
<i class="fas fa-question-circle taparea"></i>
```

Font Awesome標準のアイコン表示に`taparea`クラスを付与しているだけ。

## CSS

```css
.taparea {
  position: relative;
}
.taparea:after {
  display: block;
  content: '';
  position: absolute;
  width: 40px;
  height: 40px;
  top: -12px;
  left: -12px;
  border-radius: 50%;
  background: rgba(255, 0, 0, 0.2); /* 分かりやすくするための色付け。実装時は外す */
}
```

40x40の`:after`要素がアイコンの中央に来るように生成しています。
absolute配置なのでアイコン以外の要素を邪魔しません。
【DS】特定の文字がでるまで待ち続ける

以下のようにするとぐるぐるし続ける
<img width="263" alt="2.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556084/47eba8df-4e2e-8def-26d4-a3fe5ca85433.png">
APLpyで軸目盛の間隔を調整したいとき

APLpyで軸目盛の間隔を調整する方法で少しハマったのでメモ。

例えばx軸の主目盛の表示間隔を調整したいときはdegrees単位で

```.py
fig.ticks.set_xspacing(0.0005)
```

とやればいい。ただし、このとき引数はlabel formatの最小単位の倍数でなくてはならない。
いま

```.py
fig.ticklabels.set_xformat(hh:mm:ss.ss)
```

としていたなら最小単位は0.001sすなわち0.015arcsec、したがって0.015/3600 degreeである。これを基準にしていっぱんにはnを整数としてn*0.015/3600の数をset_xspacingに入れてやれば良い。

####参考
[APLpy documentation — aplpy v2.0.2](https://aplpy.readthedocs.io/en/stable/index.html#reference-api)
[Overlapping tick labels on x axis · Issue #258 · aplpy/aplpy](https://github.com/aplpy/aplpy/issues/258)
VLOOKUPのいやなところ。

VLOOKUPはExcelで表引きの出来る大変便利で強力な関数です。個人的にはVLOOKUPが使えるか否か、がExcel初級と中級との分岐点だと思っています。

しかし、その便利なVLOOKUPにも、いくつかいやなところがあります。

・元々のLOOKUPはいろいろと仕様がショボかったので改良型のVLOOKUP/HLOOKUPが出来たが、ほぼ同じ関数なのにデータの並び方向でV(垂直)とH(水平)の２つ分かれてしまっている。
　→結果「なんとかLOOKUP」という関数が３つもある(LOOKUP, VLOOKUP, HLOOKUP)。
　→ちなみにVLOOKUP以外を使ったことはありません。

・自分としては完全一致で使うことが大半だが、デフォルトは完全一致ではない。
　→「検索の型」として第4パラメータで指定する。
　→LOOKUPにはない仕様。

・検索対象と表引き結果をひとつの範囲で指定する仕様なので、データを加工しないと利用できないことがある。
　→戻り値の列は検索範囲の列と隣接して行が一致していないといけない。
　→戻り値の列は検索範囲の列よりも後ろにないといけない。
　→戻り値はひとつしか返せないので、複数の列を引いてくるには、その数分似たような式を並べないといけない。

・一致するデータがない場合、エラー値 #N/A になってしまう。
　→いちいちIFERRORで包んでエラー時には "" にする、とか小細工しないといけない。

新たに追加されたXLOOKUPは「似たような関数がたくさんある」を悪化させる以外、他のすべてを解決してくれます。

・一致モード（第5引数）のデフォルトは「0」
　→0 - 完全一致。 見つからない場合は、#N/A が返されます。 これが既定の設定です。

・従来の「範囲」は検索範囲（第2引数）と戻り配列（第3引数）の２つに分けられた。
　→離れていてもいいし、縦でも横でもいいし、別シートや別ブックでもいい。

・戻り値は戻り配列（第3引数）として指定可能。
　→新たにサポートされたスピルという仕組みで、一つの式で複数のセルに渡る値を参照できる。

・[見つからない場合]（第4引数）が追加された。
　→見つからなかった場合、ここに指定したデータが#N/Aの代わりに返される。

・[検索モード]（第6引数）が追加された。
　→シーケンシャルサーチ／バイナリサーチ／昇順／降順を指定できる。
　　 1:シーケンシャル／昇順（デフォルト
　　-1:シーケンシャル／降順
　　 2:バイナリ／昇順
　　-2:バイナリ／昇順
　※バイナリサーチ：高速だが検索範囲はソートしてある必要がある。
　※シーケンシャル：検索範囲はソートしておかなくてよいが検索速度にばらつきがある。


【参考】XLOOKUP 関数 - Office サポート
https://support.office.com/ja-jp/article/xlookup-%E9%96%A2%E6%95%B0-b7fd680e-6d10-43e6-84f9-88eae8bf5929

他にもMATCHのスピル版（スピルバンｗｗｗｗ）のXMATCHとか、スピルのおかげで実現できる待望のFILTERとかSORTとかも追加になっています。喜ばしいことです。
Wordpressのダッシュボードを徹底的に削除する

機能自体を削除するのは難しいらしいので、
メニューからダッシュボードの項目を削除をし、
ログイン時のURLを変えるといいらしい

```php:functions.php
/*
 *  管理画面から削除
 */
function remove_dashboard_menus()
{
    remove_menu_page('index.php');
}
add_action('admin_menu', 'remove_dashboard_menus');

function redirect_dashiboard()
{
    if ('/wp/wp-admin/index.php' == $_SERVER['SCRIPT_NAME']) {
        wp_redirect(admin_url('edit.php'));//投稿形式post以外でリダイレクトさせたい場合、URLを変える必要がある
    }
}

add_action('admin_init', 'redirect_dashiboard');
```

# 参考

[ログイン後にダッシュボードには移動せずに、他ページヘリダイレクトさせる方法]
(https://www.nxworld.net/wordpress/wp-redirect-dashiboard.html)
php-fpmのプロセス数の調整方法を間違えていました

## はじめに

普段30msでレスポンスを返しているリクエストがたまに100msで返す、
こういったレスポンス遅延に関して勘違いしていました。
その誤った知識でphp-fpmのプロセス調整を行っていたことが分かったので、
そのことに関して書きます。

この遅延があったことにより、

```
11: Resource temporarily unavailable
```

というエラーが発生してしまっていました。

インフラまわりに携わっている方で自分のような勘違いをされている方は少ないかもしれませんが、
自分の戒めのためにも記録しておこうと思います。

## 構成

webアプリケーションを以下のような形で運用しています(簡略化しています)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/143313/f972719f-0c5d-e3a0-6f68-a73089a926fa.png)

以下たびたびプロセスと言っているのはphp-fpmの子プロセスのことです。

スレッドでもほとんど同じような状況になるとは思っていますが、ここではプロセスのお話です。

## 勘違いしていた認識

レスポンスの遅延には大まかに分けて２つパターンがあると考えています。

* 単一リクエストの遅延
* 並列で処理を受けることによる遅延

単一のリクエストの遅延は処理するデータ量やネットワーク周りの影響によるものだと考えています。

要因としては、例えば

* DBのデータ量増加による問い合わせの性能劣化
* APIリクエストエラー時の再送処理による性能劣化
* ネットワーク障害による通信遅延
* DBから受け取ったデータ量によりアプリケーションの性能が変化する場合

といったところでしょうか。

今回勘違いしていたところは、並列で処理を受けることによる遅延の方です。

どのように認識していたかというと、

* クライアントから見た場合遅延が発生する
* リクエストがプロセスに割り当てられてからの処理に遅延は発生していない

という認識でした。図(表)にすると以下のような感じです。
縦が時間の経過、横が１リクエスト(プロセス)、表の内容がそのリクエスト(プロセス)がどういう状態にあるか、を書いています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/143313/9eafa2cc-bded-1218-053d-0b31b2311c7a.png)

リクエスト1,2は正常にレスポンスが返り、リクエスト3,4はコアが2つしかないので遅延が発生するという考えです。
もう少し詳しく言うと、

* リクエストを同時に受けた場合php-fpmは子プロセスにリクエストを割り当てる
* 先に受けたリクエストを処理するためリクエスト3,4は待機状態となりCPUが空いたら実際の処理が開始される
* CPUが割り当てられるまでの待機時間 + 処理時間により待機時間分だけ遅延が発生する

といったところです。

最初に処理中となるまでアプリケーションは処理時間の計測を開始しないから、
複数同時にプロセスに割り当てられても処理時間は変わらないと考えていました。


## 認識を改めた後

まあ、要するに、タイムスライスのことをすっかり忘れていたという話です。
正しかった方の認識を図にするとこんな感じです
(もちろん100msでスライスされることはあり得ないのでイメージ図です)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/143313/cab4a4a6-2fa2-c7f7-cea4-27ae434c2b5c.png)

* リクエストを同時に受けた場合php-fpmは子プロセスにリクエストを割り当てる(listenしているソケットでacceptしたタイミング)
* 割り当てられたプロセスはCPUに処理が割り当てられる
* プロセスの数だけ単位時間当たりの1プロセスに割り当てられるCPU時間が減るためレスポンスの遅延が発生する

勘違いしていたころは500msの時点でプロセスがidleになり新たなリクエストを受け付けられますが、認識を改めた後では900ms経過しないと新しいリクエストを受け付けられないということになります。

つまりCPUに割り当てが必要なプロセス数が増えれば増えるほど新しくリクエストが受けられるまで時間がかかるということになります。

## 勘違いにより何が起こっていたか

冒頭でもお話しした通り、Resource temporarily unavailableというエラーが発生してしまっていました。
プロセス数が増えすぎてリクエストを受けきれず、listenのbacklogを使いつくしたということです。
そしてこの原因特定にかなりの時間がかかりました。

ありもしないネットワークの障害を疑ったり、DBのデータの量を計測したり、
スロークエリのログを確認したり、といった無駄なことをしていました。

自分の勘違いによると、アプリケーションの処理時間は普段と変わらない想定なので、
アプリケーションのパフォーマンスのグラフを見てタイムスライスの影響を疑えませんでした。

プロセス数足りないから動的に追加しておいた、というログは出ていたので、
php-fpmの初期プロセス数を増やすということをしていましたが、
単位時間当たりのCPU割り当て時間が減るだけで、新たにリクエストが受けられず、
全くの逆効果だったというわけです。

## この遅延を発生させないために

遅延は起こっていてもエラーが発生していなかったり、
遅延が許容範囲内であれば問題はありません。

この遅延が問題となっている場合に取りうる対処は主に3つかなと思います。

* アプリケーションのパフォーマンスを改善する
  * 1コアあたりのタイムスライス頻度を減らす方法
  * レスポンスが速ければ速いほどCPUが処理しなければならない時間は少なく済みます
* CPUのコア数を増やす
  * 1コアあたりに割り当たるプロセス数を減らす方法
  * コアの数が少ないからプロセスが使えるCPU時間が減るのでこれを増やします
* 起動するプロセス数を少なくしてlisten時のbacklogを増やす
  * idleなプロセスができるまでacceptを待つ方法
  * listenで指定できるbacklog数まではリクエストを待機できる
  * もともと自分の認識していた遅延の状態に近いもの

今回は手っ取り早くCPUのコア数を増やして対処しました。
これからまた調整していく予定です。

## まとめ

プロセス数を調整した値が実際は使い物にならなかったということなので、
認識を新たに再度調整していきたいと思います。

なんでこんな勘違いをずっとしていたんでしょうね。

おそらくパフォーマンスの劣化をハードウェアで補うようなパワープレーばかりしていて、
あんまり深く考えてなかったからかな。
理解が深まったのは良しとして、雰囲気で運用していたことはエンジニアとして反省します。
用雙指標來反轉陣列（筆記）

在看別人的解題說明時看到的一個寫法，因為簡單好理解印象很深刻，所以把它記錄下來。

執行的次數則是陣列中元素個數的一半。
對於很大的陣列來說的話，應該會非常有效率。

時間複雜度是 O(n) ，屬於線性時間。

``` swift
func reverse<T>( _ array: inout [T]) {
    if array.isEmpty { return }
    var start = 0
    var end = array.count - 1
    
    while start < end {
        let temp = array[start]
        array[start] = array[end]
        array[end] = temp
        
        start += 1
        end -= 1
    }
}
```

#### 稍微重構

抽 swap 出來

``` swift
func reverse<T>( _ array: inout [T]) {
    if array.isEmpty { return }
    var start = 0
    var end = array.count - 1
    
    while start < end {
        swap(&array, start, end)
        
        start += 1
        end -= 1
    }
}

func swap<T>(_ array: inout [T], _ a: Int, _ b: Int) {
    if a >= array.count || b >= array.count {
        // Do nothing or assert with message over here
        return
    }
    let temp = array[a]
    array[a] = array[b]
    array[b] = temp
}
```

### 用用看

來餵個簡單的 test case

``` swift
var array = [1, 3, 4, 5]
reverse(array)
```

執行完之後， array 就會變成 `[5, 4, 3, 1]` 。

#### 圖解

```
[1, 3, 4, 5]
 s        e  (s=start) (end)

// swap the values
[5, 3, 4, 1]
 s        e

// moving the pointers
[5, 3, 4, 1]
    s  e

// swap the values
[5, 4, 3, 1]
    s  e

```

以上。
ActiveRecordメモ

## find
```rb
user = User.find(97)
user = User.find(97, 103) #　複数の場合
```

主キー(基本的にid)に対応するレコードを取り出すことができる。
見つからない場合、例外(`ActiveRecord::RecordNotFound`)が発生する。

## find_by
```rb
user = User.find_by(email: 'hogehoge@example.com')
user2 = User.find_by(age: 24, name: 'Jack')
```

与えられた条件に合致するレコードのうち、最初の1件を取り出す。
複数条件で絞り込むこともできる。
見つからない場合`nil`を返す。

## where
```rb
users = User.where(age: 24) #24才のユーザー
users2 = User.where(age: [24, 26]) #24才or26才のユーザー
users3 = User.where(age: 24).limit(3).order(id: :asc) #24才のユーザーをidが小さい方から3人
users4 = User.where.not(age: 24) #24才以外のユーザー
users5 = User.where(age: 24).where(region: 'Asia') #24才でアジアに住むユーザー
users6 = User.where(age: 24).or(User.where(name: 'Jack')) #24才、またはJackという名前のユーザー
users7 = User.where(age: 24).where(region: 'Asia').or(User.where(name: 'Jack')) #24才でアジアに住むユーザー、またはJackという名前のユーザー
```
与えられた条件に合致するレコードの集合を取り出す。
見つからない場合`ActiveRecord_Relationクラス`を返す。空の配列っぽいけど、配列ではないらしい。

## 結合
パターンが多すぎるので、使用したものを適宜追加するスタイルでいきます。
### 1対多
```rb:使用するデータ
class User < ApplicationRecord
  has_many :items
end

class Item < ApplicationRecord
  belongs_to :user
end
```

```rb:「user>email」,「item>price」の複数条件で検索し、全てのカラムのデータを取得
users = User.joins(:items).select("users.*, items.*").where(users: {email: 'hogehoge@example.com'}).where(items: {price: 300})
```
`select`で取得するカラム名を指定
`where(テーブル名{検索条件})`で検索条件を指定

### 1対1
```rb:使用するデータ
class User < ApplicationRecord
  has_one :user_datum
end

class UserDatum < ApplicationRecord
  belongs_to :user
end
```
```rb:user_data>email受け取りフラグが立っているユーザーを取得
users = User.joins(:user_datum).select('users.*, user_data.*').where(user_data: {receive_email: 1})
```
`joins`の引数が単数形になる。
※dataの単数形がdatum

## 特定の時間より前
```rb
User.where('updated_at <= ?', Time.zone.parse('2020/09/08 15:50:00'))
```

## 関連テーブルのレコード数でソート
```rb
class PLine < ApplicationRecord
  has_many :likes, dependent: :destroy
end

class Like < ApplicationRecord
  belongs_to :p_line
end
```
```rb
@p_lines = PLine.select('p_lines.*', 'count(likes.id) AS favs')
                .left_joins(:likes)
                .group('p_lines.id')
                .order('favs desc')
```
```erb
<% @p_lines.each do |p_line| %>
  <p><%= p_line.p_line %></p>
  <p><%= p_line.favs %> points</p>
<% end %>
```

## 関連カラムが複数あって、変数の値がどちらかに一致する。関連カラムをすべてincludeしたい。
```rb
class Mc < ApplicationRecord
  has_many :videos
end

class Video < ApplicationRecord
  belongs_to :mc1, class_name: 'Mc'
  belongs_to :mc2, class_name: 'Mc'
end
```
```rb
@videos = Video.includes(:mc1, :mc2).where(mc1_id: @mc)
               .or(Video.includes(:mc1, :mc2).where(mc2_id: @mc))
```

## `statusが0`かつ`その他のどれかが1`
```rb
@orders = Order.where(status: 0)
                   .merge(Order.stock_status_done.or(Order.content_status_done).or(Order.email_status_done).or(Order.shipping_status_done).or(Order.where.not(personnel_id: nil)))
```

## 参考
[【rails】find・find_by・whereについてまとめてみた](https://qiita.com/nakayuu07/items/3d5e2f8784b6f18186f2)
目的別プログラミング言語の選び方【初心者向け】


# 前書き
簡単な言語は？
オススメの言語は？
最強の言語は？
なんでもできる言語は？

などなど、プログラミングを始めたばかりの頃は、どうやってプログラミング言語を選べばいいのか中々分からないかと思います。

プログラミングというのはあくまでも、何かを実現するための手段です。
なので、プログラミング言語は実現したいものに応じて選ぶのがベストです。

つまり、プログラミング言語は、目的別に選びましょう。

というわけで目的別に分類してみました。

※ライブラリやフレームワークは紹介していません。


# 目的別プログラミング言語
### ホームページを作りたい (データ通信などがない静的Webサイトのこと)
- HTML
- CSS

### ホームページをオシャレにしたい (データ通信なしでフロントだけをデザイン)
- JavaScript

### Webアプリを作りたい (データ通信などができる動的Webサイトのこと)
- JavaScript or PHP or Java

### デスクトップで使えるツールを作りたい
- C# (Windows)
- Java

### スマホアプリを作りたい
- Swift (iPhone)
- Java/Kotlin (Android)

### ゲームを作りたい
- C#/Unity

### ちょっとしたコマンドラインツールを作りたい
- Python

### AIツールを作りたい
- Python

### Microsoft Officeをいい感じにしたい
- VBA

### Google Docsをいい感じにしたい
- Google Apps Script

### データベースを操作したい
- SQL

#後書き
たぶん初心者が最初にやりたいと思う内容ってこれくらいなのかなと思います。他にもあれば追加しますのでコメントください！
Postgresqlに接続できない時の対処法

##開発環境
- ubuntu(WSL)
- Postgresql

##解決策
サーバを起動させるために下記のコマンドを実行します。

```
sudo /etc/init.d/postgresql start
```
##エラー内容
`rails s`したら、以下のようになった。

```
PG::ConnectionBad (could not connect to server: No such file or directory
        Is the server running locally and accepting
        connections on Unix domain socket "/var/run/postgresql/.s.PGSQL.5432"?

```
`pg_lsclusters`で現状確認。
やっぱり、downしてる。

```
10  main    5432 down   postgres /var/lib/postgresql/10/main /var/log/postgresql/postgresql-10-main.log
```

下記のコマンドでクラスターを起動させる。
`pg_ctlcluster 10 main start`

```
install: cannot change owner and permissions of ‘/var/run/postgresql’: No such file or directory
Error: Could not create /var/run/postgresql/10-main.pg_stat_tmp: No such file or directory
```

サーバが起動していないことが原因だあるらしいので、下記のコマンドを実行させる。

```
sudo /etc/init.d/postgresql start
```
これで動きました。

##参考文献
- [PostgreSQL で psql コマンド を実行したら could not connect to database postgres: could not connect to server: No such file or directory と出る場合](https://obel.hatenablog.jp/entry/20181027/1540588239)
- [Why psql cannot to connect server?](https://translate.google.com/translate?hl=ja&sl=en&u=https://stackoverflow.com/questions/31645550/why-psql-cant-connect-to-server&prev=search)
- [postgresqlの設定](https://qiita.com/sibakenY/items/407b721ad1bd0975bd00)
固まったRailsのローカルアプリケーションサーバ（Puma）を強制終了する

Railsを使用して開発していると、Pumaが固まって`Ctrl+C`でもPumaを終了できないことがあります。そんな時は以下のコマンドで解決！

```
pkill -9 -f 'puma 4.3'
```

上記のコマンドを叩いた後、ターミナルにて以下のように表示されていれば強制終了成功です。

```
Killed: 9
```
Wordpressのライター機能(作者名)を徹底的に削除する

タイトルの通り、Wordpressのライター機能(作者名)を徹底的に削除する。

# 管理画面から

## 投稿画面
管理画面投稿ページの「投稿者」は、管理画面から非表示に出来る。
### Gutenbergの場合
右上の3点メニュー -> オプション　-> 該当するチェックボックスを外す
### Gutenberg以外の場合
右上の表示オプション　-> 該当するチェックボックスを外す

# functions.phpでの記述
```php:functions.php
/**
 * 　ライターページ停止
 */

add_filter( 'author_rewrite_rules', '__return_empty_array' );

/**
 *  管理画面記事一覧: 作成者削除
 */
function custom_author_columns($columns) {
    unset($columns['author']); 
    return $columns;
}
// 投稿
add_filter('manage_posts_columns', 'custom_author_columns');
// 固定
add_filter('manage_pages_columns', 'custom_author_columns');
【初心者向け】HTMLで簡単な表を作るときに手っ取り早く作る

#tbodyって何？っと思ってググったときに便利なタグだと思ったのでメモ

参照：https://html-coding.co.jp/annex/dictionary/html/tbody/
↑めっちゃわかりやすいです。


#使用するタグ一覧

```html
<table>
<thead>
<tr>
<th>
<tbody>
<tfoot>
```

これだけです。


#表のコード

```html
<table border = "5">
  <thead>健康診断データ
    <tr>
      <th>身長</th>
      <th>体重</th>
      <th>視力</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>175cm</td>
      <td>70kg</td>
      <td>1.0</td>
    </tr>
    <tr>
      <td>175cm</td>
      <td>150kg</td>
      <td>1.5</td>
    </tr>
    <tr>
      <td>170cm</td>
      <td>65kg</td>
      <td>10.0</td>
    </tr>
  </tbody>
  <tfoot>
    <tr>
      <td>普通</td>
      <td>メタボ気味</td>
      <td>視力マサイ</td>
   </tr>
  </tfoot>
</table>
```

#完成画像

<img width="218" alt="スクリーンショット 2020-02-12 17.42.06.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540462/8d6c7407-bb19-a7a9-f48a-ea919251b274.png">

#ポイント

tableにborderつけないと線が表示されなかったため、参照サイトにborderをつけてます。
talbeの構造もhead body footer で作ることが出来るんだなぁと理解（footerとか使わなくても出来ますが）


#最後に

最後までみていただきありがとうございました。
ここは、違う、ここはこうした方が良い等々ございましたらご指摘いただけますと幸いです。
Hugoでお手軽ポートフォリオを作る

最近，知らない場所で知らない人に自分が作ってきたものを紹介する機会が増えたんですが，毎回それをするのはコストがかかるし自分でも覚えてないものがあったりするので，そういうときに「これ見てください」って言って済ませられるように**過去の制作物をまとめたポートフォリオ**を作りました．


https://te260ku.github.io/portfolio/

Golangベースの静的なサイトジェネレーターとして知られる**Hugo**を使って作ったポートフォリオサイトを**Github Pages**にデプロイしています．これについて書いた記事はすでにたくさんありますが，デプロイの手順が少し複雑だったので，自分用の備忘録として残しておこかと思います．

<img width="2018" alt="スクリーンショット 2020-02-12 17.32.49.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/263889/fb02ac5e-18ed-6ccb-72b2-093d64ef4bc6.png">


# 1. 公開用サイトを作成する

## 作業環境
macOS Mojave 10.14.6

## Hugoのインストール
まずはhomebrewを使ってhugoをインストールします．

```terminal
$ brew install hugo
```

## サイトの生成
任意のディレクトリに移動して，以下のコマンドを叩きます．ちなみにhugoはサイト生成の速さを推しているらしく，信じられないくらい速く終わります．

```terminal
$ hugo new site portfolio
```

## テーマを適用する
hugoでは基本的にテーマと呼ばれるテンプレートのようなものをベースにサイトを作ります．今回はhugo-cardsテーマをsubmoduleとして登録します．そうすると，元からあったthemesディレクトリの中に色々なファイルが生成されます．

```terminal
$ git submodule add https://github.com/bul-ikana/hugo-cards themes/hugo-cards
```

## 設定ファイルの編集
hugoでは設定ファイルが意外と重要です．ルートディレクトリにある```config.toml```を編集します．今回の変更点は以下の通りです．その他細かい設定は．テーマの開発者が公開しているドキュメントの通りに進めれば大丈夫だと思います．

- baseUrlを```https://<アカウント>.github.io/blog```にする

- ```theme = "hugo-cards"```で使用するテーマを指定する

- piblishDirを```docs```に変更する
ビルドされたファイル群が作業ディレクトリ直下の`docs`ディレクトリに保存されるように変更します．デフォルトだと`public`ディレクトリが自動生成されます．

- canonifyurlsを```true```にする
相対パスではなくbaseurlを基点とした絶対パスに変更します．


```config.toml
baseURL = "https://te260ku.github.io/portfolio/"
languageCode = "en-us"
title = "Tetsuro Okuya"
theme = "hugo-cards"
publishDir = "docs"
canonifyurls = true
```

## ローカルサーバーで動作確認
以下のコマンドを実行するとサーバーを建てることができます．ここで適用したテーマが正しく表示されているか確認します．

```terminal
$ hugo server -D
```

## 公開用にビルドする
デプロイするファイル群を生成するには以下のコマンドを実行します．これも相変わらず爆速です．

```terminal
$ hugo
```

すると，ルートディレクトリ配下に```docs```というディレクトリが新たに追加されます．この中に公開に必要なファイルが入っています．

# 2. Github Pagesにデプロイする
ビルドまで終わったので，いよいよデプロイしていきます．githubで適当なリポジトリを作ったうえで，まずは作業ディレクトリの中身を丸ごとpushします．

```terminal
$ cd portfolio
$ git init
$ git add .
$ git commit -m "initial commit"
$ git remote add origin リポジトリのURL
$ git push origin master
```

無事pushできたら，githubリポジトリの```Settings```にある```Github Pages```というエリアを確認します．先ほど生成された```docs```ディレクトリを公開したいので，```Source```の項目を```master branch / docs folder```に変更します．

<img width="727" alt="スクリーンショット 2020-02-01 15.13.54.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/263889/5d10dd88-1ebc-57af-efaf-ce74ac1f46f1.png">


その後しばらくすると，```https://ユーザー名.github.io/portfolio/ ```にサイトがデプロイされていることを確認できます．github pagesは変更が反映されるまでに時間がかかることがあるので注意．

# 3. 記事の更新
今後何事もなければポートフォリオに掲載する作品は増えていくと思います．その際には以下のコマンドを実行して，生成されたファイルに記事の内容を記述し，上記の手順にしたがって再度リポジトリにpushすればサイトの内容が更新されます．ビルドするのを忘れずに．

```terminal
$ hugo new <記事名>.md
```

# 4. おわりに
HugoとGithub Pagesを使うと，名刺代わりになるようポートフォリオサイトを秒速で作れる．
Ubuntu18.04のapache2.4で複数ドメインでのActiveDirectory認証

エンタメ系企業の社内もろもろを担当している、引き続き絶賛チックチックチックにはまり中のakibinです。
Shake The ShudderというアルバムのFive CompaniesやThottle Serviceって曲もすげーかっこいい。

#やってみたこと
前回[こちら](https://qiita.com/aakimi/items/4c0dad009b0928b1438c)でApacheの特定ディレクトリへのアクセスにBasic認証を発生させ、認証をADのユーザで行いましたが、その続きとして複数のAD（別のフォレストのドメイン）でユーザ認証を行う場合のApache設定ファイルの書き方を調べました。

#前提条件
【対象ディレクトリ】
/var/www/html/himitu

【設定ファイル】
/etc/apache2/sites-available/auth-ad.conf
※デフォルトの設定ファイルをコピーして作成

【ADサーバ1】
・サーバ名： hoge-ad.hoge.co.jp
・ADへアクセス時に認証するユーザ： hogeadmin
・ADへアクセス時に認証するユーザもパスワード： hogeadminpw

【ADサーバ２】
・サーバ名： fuga-ad.fuga.co.jp
・ADへアクセス時に認証するユーザ： fugaadmin
・ADへアクセス時に認証するユーザもパスワード： fugaadminpw

#設定方法

```bash
User@test-server:~$ sudo vi /etc/apache2/sites-available/auth-ad.conf

# 以下を追記
<AuthnProviderAlias ldap hoge-ad>
	AuthLDAPBindDN "cn=hogeadmin,dc=hoge-ad,dc=hoge,dc=co,dc=jp"
	AuthLDAPBindPassword "hogeadminpw"
	AuthLDAPURL "ldap://hoge-ad.hoge.co.jp:389/dc=hoge-ad,dc=hoge,dc=co,dc=jp?sAMAccountName?sub?(objectClass=*)"
</AuthnProviderAlias>
                
<AuthnProviderAlias ldap fuga-ad>
	AuthLDAPBindDN "cn=fugaadmin,dc=fuga-ad,dc=fuga,dc=co,dc=jp"
	AuthLDAPBindPassword "fugaadminpw"
	AuthLDAPURL "ldap://fuga-ad.fuga.co.jp:389/dc=fuga-ad,dc=fuga,dc=co,dc=jp?sAMAccountName?sub?(objectClass=*)"
</AuthnProviderAlias>

<Directory "/var/www/html/himitu">
	AuthName "User/Password"
	AuthType Basic
	AuthBasicProvider hoge-ad fuga-ad
	require valid-user
</Directory>
```

AuthnProviderAliasで各ADの認証設定を書き、AuthBasicProviderで認証を許可する各AD認証設定を指定する感じですね。
これで該当のディレクトリアクセス時にどちらのADのユーザでも認証ができました！

あまりないシーンかもしれませんが、なかなか情報出てこなかったので参考までに投稿しました。

めでたしめでたし。


＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊
◆ Twitterアカウント
@AkibinMusic

◆ Youtubeチャンネル
https://www.youtube.com/channel/UC-JOpwEnJn3gCrUA4NdCYgg
Wordpressのコメント機能を徹底的に削除する

タイトルの通り、Wordpressのコメント機能は使わないので徹底的に削除する。
# 管理画面から
## 設定 -> ディスカッション
チェックボックスを全て外す
## 投稿画面
管理画面投稿ページの「ディスカッション」は、管理画面から非表示に出来る。
### Gutenbergの場合
右上の3点メニュー -> オプション　-> 該当するチェックボックスを外す
### Gutenberg以外の場合
右上の表示オプション　-> 該当するチェックボックスを外す

# functions.phpでの記述
```php:functions.php

/**
 * 　コメント禁止
 */

add_filter( 'comments_open', '__return_false');

/**
 *  管理画面記事一覧: コメントカラム削除
 */


function custom_comment_columns($columns) {
    unset($columns['comments']); 
    return $columns;
}

// 投稿
add_filter('manage_posts_columns', 'custom_comment_columns');
// 固定
add_filter('manage_pages_columns', 'custom_comment_columns');

/*
 *  管理画面バーから削除
 */
function remove_admin_bar_comments_menu() {
    global $wp_admin_bar;
    $wp_admin_bar->remove_menu('comments'); //コメント
}
add_action( 'admin_bar_menu', 'remove_admin_bar_comments_menu', 99 );


/*
 *  管理画面メニューから削除
 */
function remove_comment_menus () {
    remove_menu_page( 'edit-comments.php' );          // コメント
}
add_action('admin_menu', 'remove_comment_menus');

```
dependent: :destroy 削除が出来ない

##今回のテーマは、destroyメソッドが機能しない。
という事で、今回陥った点からまずお話致します。

トークルームにてチャットが出来るアプリにて、マイページでトークルームを削除できない。
という問題点でかなり悩まされる事に。

##テーブル
```
memo_roomsに関連付けられているのは、
usersテーブル  
memo_room_postsテーブル
categoriesテーブル
favoritesテーブル
memo_room_commentsテーブルの５つです。
```

##解決前までの流れ。

最初にルームが削除出来ないと気付いた時には
``` def destroy
    @memo_room.destroy
      flash[:success] = 'メモルームを削除しました。'
      redirect_to user_path(current_user)
  end```

コントローラにてこんな感じで書いていました。これにより、失敗しても成功しても削除しました。としか出ず、成功はしてるが、
何故か削除出来ていない。こう勘違いしました。どうしても解けず、質問掲示板にて質問をしました。

```def destroy
    if @memo_room.destroy
      flash[:success] = 'メモルームを削除しました。'
    else
      flash[:danger] = '削除に失敗しました。'
      redirect_to user_path(current_user)
    end
  end```

すると・・・成否がどちらでも削除しました。となっているので失敗してるかわからないと、このようにアドバイスを頂き、
アドバイス通りにelseを追加したところ、削除に失敗しました。と表示されました。

成功してると思っていたが、実は失敗していたんですね。それに気づかず成功してる前提で何故消えないかを探していたので
見つかる訳がありません。

##rbファイル dependent: :destroy  間違い編

```
class MemoRoom < ApplicationRecord
  belongs_to :user
  belongs_to :category, dependent: :destroy
  
  has_many :memo_room_posts, dependent: :destroy
  has_many :favorites, dependent: :destroy
  has_many :memo_room_comments, dependent: :destroy


class MemoRoomPost < ApplicationRecord
  belongs_to :user
  belongs_to :memo_room, dependent: :destroy

has_many :memo_room_posts, dependent: :destroy
  has_many :memo_rooms, dependent: :destroy
  has_many :categories, dependent: :destroy
  has_many :favorites, dependent: :destroy

```

dependent: :destroyをとりあえず付けておけば削除した際に関連されたデータも消えるから便利程度の認識でした。
ここがまず間違いでこれのせいで、関連付けが多すぎて網目のようになり、ぐちゃぐちゃになってがんじがらめになっていると教えて頂きました。


そして、アドバイスとして一番わかりやすかったのが、

**こいつを消したら意味が無くなるもの に対してのみその方向でのdependent: :destroyをつけるようにしてください**

この時、初めて使い方をきちんと知りました。とてもわかりやすい一言でした。

##解決編

```
class MemoRoom < ApplicationRecord
  belongs_to :user
  belongs_to :category
  
  has_many :memo_room_posts, dependent: :destroy
  has_many :favorites, dependent: :destroy
  has_many :memo_room_comments, dependent: :destroy
```

memo_roomを消す事により、トークルーム内の投稿であるmemo_room_postsとmemo_roomにいいねされたfavoritesそしてトークルームが消えればコメントも無意味になるのでmemo_room_commentsこの3つのみにdependent: :destroyを付けてみました。

すると、がんじがらめから抜けて綺麗になったのか、削除する事が出来ました。必要のない項目にdependentがダメ！なんて初心者は知らない人もいると思うので、役に立つメモとなればと思います。
知ったかぶり　←しっかりと知識をつけることの大切さを知りました。

https://teratail.com/questions/240800

実際に質問した際のURL。も載せておきます。

以上上手く削除が出来なくなってしまった際のメモでした。参考になればと思います。
速度改善_雑多メモ

最近、WEBの速度改善することが多いので、
忘備録として。


#IntersectionObserver

速度改善の定番の手法として、
「画像の遅延読み込み」がある

いろんな遅延読み込みのライブラリが世の中にはあるが、
IntersectionObserver APIを用いた方法が、Google推奨しているため
なるべくそれを使いたい。

Googleの見解：
https://developers.google.com/search/docs/guides/lazy-loading


```html:html
<img class="lazyload" src="dummy.png" data-src="hoge.png">
```

上記の書き方で、画面外時にはdummyを表示し、
画面内になったら、hoge.pngをロードし、表示する、ということが可能。

APIを使う側の記述は省略。


#iframeの遅延読み込み

上のIntersectionObserverを叩く側をちょっと改良すれば
imgタグだけでなく、iframeタグでも遅延読み込みを利用できる。

PageSpeed Insightsのスコアを見る限り、
画像の3倍くらい効いてくる。

iframeはyoutube埋め込みとかで、
メチャンコ重たくなっているケースが多い。

画像とiframeの遅延読み込みを整備するだけで
スコアがグンとあがることがあるのでオススメ。


#TinyPngしてみる
画像がバカでかい時があるので。
半分くらいのサイズになることが多いです。

画像の鮮明さと速度のトレードオフで悩むことはあるけど
あんまり考えないで、ツールに解を出してもらうほうが効率的だと思います。

https://tinypng.com/


#jQuery

jQuery関連は読込の順番変えると動かなくなったり
そもそも最初に読むライブラリが重かったりで、
速度上、不利。あんまり使わない方がいい、、、

けど、使わざるを得ない状況も多いので。

acync　で気持ちマシになるかも


#CSS

重要じゃないCSSは遅延させたほうがいいらしい。
Insightでも毎回指摘されるポイント。
けど、CSSはどれも重要、という状況も多く、あんまり解決にならない。


#WordPress

遅延読み込み系のプラグインは多数ある。
ありすぎて、どれがイイかわかんなくなる。

JetPackでも遅延読み込み化することができるみたいだけど、
ぼくは　`a3 Lazy Load`　を使ってみている。

画像もiframeも遅延読み込みできるし
そこそこスコア改善できたので。

もっといいプラグインがあるかもしれないし、
結局は、WP（とjQuery）が重すぎて
ハイスコアは狙えない、となってしまうことも。

最適解見つけられてないです。


追記：

#CSSの遅延読込みを実現する最もシンプルな方法

レンダリングをブロックしてしまう要因として
CSSは結構デカい。
単にこれを導入するだけでモバイルスコアが10くらいあがることも。

https://qiita.com/rana_kualu/items/95a7adf8420ea2b9f657


印刷用のCSSと指定して、ロードが完了したら、
スクリーンにも適用する、的な処理をonloadで用意している。

```html:html
<link rel="stylesheet" href="/path/to/my.css" media="print" onload="this.media='all'">
```
※2/27追記
　CSSが先にロードされていること前提で書かれているjsコードがあれば
　CSS遅延させると不具合が発生したりしなかったりする。危険。
　一括でCSSを遅延読み込みさせる場合は十分な動作確認しないと痛い目みます。


#jsの遅延読み込みdefer

同じくレンダリングを大きくブロックしてしまう要因としての、js

Javascript、とくにjQuery関連は
ロードの順番が大事なことがあるので
入念な動作検証が必要。


deferとasyncがあるが、実行順番を考慮すると
全部一括でdeferにするのが良いと思われる。

https://qiita.com/phanect/items/82c85ea4b8f9c373d684

deferもasyncもつけていないと、
結構なレンダリングブロック時間が生じてしまうので、結構重要


#そもそも、モバイルスコアは評価が厳しすぎる？

以下のサイトらによると、、、

>https://webtan.impress.co.jp/e/2018/12/21/31382
ただし残念ながら現状のPageSpeed Insightsによるモバイルのパフォーマンス測定は、日本のサイトに関しては信頼できる数値になっていない。計測が米国から行われているからだ。

>https://boundworks.net/pagespeed-insights/
2018年11月に入ってからスコアの判定方法が変更になり、3G回線時の通信速度をベースとした、より厳格な基準となっています。

これが本当だとすると、
「アメリカから、3G回線」での評価になるので、

これをこのまま「日本国内から国内に4GやWi-Fiでアクセス」する
現代のモバイル事情に当てはめると、
若干厳しすぎるのかもしれません。

少なくとも、一概に「Pagespeed insights スコア90以上を目指せー！」
とは言えないかと思います。