OmniAICreator/Qiita-1.07M huggingface dataset:
UE4 Configファイルから設定を取得する

# 概要
UnrealEngine4のConfigファイルの設定を取得するための方法メモです。


## 参考
以下の記事を参考にいたしました、ありがとうございます。
[UE4公式：FConfigFile::GetString](https://docs.unrealengine.com/4.26/en-US/API/Runtime/Core/Misc/FConfigFile/GetString/)
[設定ファイルのヒミツ](https://blog.cnu.jp/2015/12/19/hidden-parameter/)
[#Config Files, Read & Write to Config Files](https://michaeljcole.github.io/wiki.unrealengine.com/Config_Files,_Read_&_Write_to_Config_Files/)


## 環境
Windows10
Visual Studio 2017
UnrealEngine 4.26


# Configファイルについて
UEエディタで設定されるプロジェクト設定などが入っているデータファイルです。
[Config]フォルダ以下に置かれています。


# 実行例
## 関連コード
"Engine\Source\Runtime\Core\Public\Misc\ConfigCacheIni.h"


## FConfigFileを使った取得
例えば以下のような ini ファイルとセクションがあったとします。

```ini:DefaultEngine.ini
[/Script/Engine.Engine]
bAllowMultiThreadedAnimationUpdate=False
SmoothedFrameRateRange=(LowerBound=(Type=Inclusive,Value=20.000000),UpperBound=(Type=Exclusive,Value=60.000000))
bUseFixedFrameRate=True
FixedFrameRate=60.000000
DefaultTimecodeFrameRate=(Numerator=60,Denominator=1)
```

エディタで見ると以下のようになっています。
![ProjectSetting.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/303806/48dddc7e-fa2f-0ba0-fca6-3988cb54cbba.png)



上記iniファイルから `FixedFrameRate` を取得する場合、GConfigから取得できます。以下コード例

```cpp:サンプルコード
#include "Misc/ConfigCacheIni.h"

FString _FrameRate = FString(TEXT(""));

// 設定取得
bool _IsRet = GConfig->GetString(TEXT("/Script/Engine.Engine"), TEXT("FixedFrameRate"), _FrameRate, GEngineIni);

if( _IsRet ){
	UE_LOG(LogTemp, Log, TEXT("FrameRate = %s"), *_FrameRate);
}else{
	UE_LOG(LogTemp, Log, TEXT("??????"));
}

// 結果 > FrameRate = 60.000000 
```

上記例ではStringで取得していますが、`GetInt` `GetFloat` `GetBool` 等の型に応じてメソッドを使い分けて取得が可能です。


## その他のiniファイルの取得
iniファイル名を切り替えると取得ができます。
エンジン用iniファイル名`GEngineIni`以外だと
`GGameIni` `GInputIni` `GGameUserSettingsIni` があり適宜設定の必要があります。
　
以下、DefaultGame.ini から設定を取得するコード例。

```ini:DefaultGame.ini
[/Script/EngineSettings.GeneralProjectSettings]
ProjectID=012345678901234567890123456789AB
ProjectName=MyTestProject
Description=マイテストプロジェクト
```

```cpp:サンプルコード

FString _Description = FString(TEXT(""));
bool _Ret = GConfig->GetString(TEXT("/Script/EngineSettings.GeneralProjectSettings"), TEXT("Description"), _Description, GGameIni);
			
UE_LOG(LogTemp, Log, TEXT("%s"), *_Description);
```



# 各Platformでの RuntimeSettings を使った取得
プラットフォームに応じた `{Platform}RuntimeSetting` モジュールがある場合、それを .uprojectに追加することで設定クラス経由で取得が可能なようです。

```cpp:サンプル
#if PLATFORM_{Platform名}
#include "{Platform名}RuntimeSettings/Classes/{Platform名}RuntimeSettings.h"
#endif

static const U{Platform名}RuntimeSettings* Settings = GetDefault<U{Platform名}RuntimeSettings>();
if (Settings){
	// SampleString という設定を表示
	UE_LOG(LogTemp, Log, TEXT("%s"), *Settings->SampleString);
}


```


# まとめ
FConfigFileクラスで設定を書き換えることも可能ですので、自作ツールで設定を書き換える場合使えるかも。Configを取得するケースは少ないと思いますが、場合によって必要なこともあるかもしれません。
ShellScriptで複数行のファイルか変数を一行ずつ読み取る

## while read で ファイルを一行ずつ処理

ファイルを読み取る場合はdoneの後に `<` とファイルを指定することで1行ずつループができる。

``` shell
while read line
do
   echo "${line}"
done < sample.txt
```

## while read で 変数を一行ずつ処理

複数行の文字列が格納された変数をループさせたい場合は `<<<` と3連で記載するのが肝。

``` shell
while read line
do
   echo "${line}"
done <<< "${var}"
```

## 出力結果をパイプで渡す

`cat` などでファイルを標準出力し、ループにパイプで渡す方法もある。

この記述方法の場合、ループ内での変数の変更がループ外へ反映されないといった不都合があるので、あまりオススメしない。

```shell
cat sample.txt | while read line
do
   echo "${line}"
done
```
S3のファイルを署名付きURLでダウンロードする（AWS CLI）

# 概要
- S3バケット内のファイルに対して署名付きURLを発行し、ファイルダウンロードに時間制限をかける
- aws-cliを使ってURLを作成する

# 実行環境
- AWSのEC2インスタンスを使用（AmazonLinux）
- もちろん、configure周りを設定すればローカルPCでも可能（ただし、アクセスキー等の発行が必要なため、EC2の方がセキュリティに良い）

# 割愛する内容
- EC2インスタンスの構築方法
- EC2インスタンスへのアクセス制限方法

# やったこと
## URL発行
- aws-cliのコマンドを実行してURLを取得
- 制限時間は1秒〜7日間(604800秒)で、デフォルトは1時間(3600秒)

``` sh:
# aws s3 presign --expires-in [制限時間] s3://[bucket名]/[ファイルパス]
# example
aws s3 presign --expires-in 604800 s3://hiroky-bucket/souzai/korokke.pdf
```

## ファイルダウンロード
- 発行されたURLをブラウザのアドレスバーに入力、アクセスするとファイルダウンロードが開始される
初心者から見たDocker-その1

# 本記事の対象

* dockerを始めたいけど、dockerとはすら知らないかた、始めたけど、内容が多すぎてとりあえずどこまで把握すれば仕事中にまぁまぁ困らないのか分からない人。
* たくさんの記事を見てきましたが、よくイメージ、コンテナなど紹介される。んんんんん、意味わからない

# なぜDocker使う？

> 簡単に環境構築できる素晴らしい技術

上記の言葉よく見かけませんか？じゃ、今までDocker使わなくても、何が不便だったの？環境構築って環境できて、手順に沿って出来るし、大規模だってansibleあるし。何が簡単だ？

という疑惑を持ってる方いらっしゃいますか？
製品の開発において当然環境最低でも二つ必要でしょ、テスト環境と本番環境（呼び方色々）、開発者も決して一人ではないですね。協力会社や派遣のメンバーを取り入れてチーム作って、少なくても3、4人がいるでしょう。

> え？自分の環境には動いたのにね

この言葉よく聞きますね。はい、自分の環境には走ってるけど、本番環境には使えない。
その環境問題を解決するために費やしてきた時間が恐ろしかった。
環境構築は実に時間がかかり、下手したら最初からやり直すことも少なくありません。

以前、委託先から開発済みのjarかwarをもらって、運用チームはそこから必要な環境構築し（Mysql、ES、JDK、Rediasの環境）、搭載する流れになってました。もし環境とjarを全部まとめてもらってそのまま搭載するならだいぶ楽になりますね。

それを解決してくれたのはDockerだ！

ソースコードjar-----ソースと環境を一緒にパッケージング(イメージ)-----倉庫に入れる-----使う時にダウンロードしrunする
というイメージです。

# Dockerの歴史

Dockerがない時代にはVMという仮想マシンが多く使われた、2010年エンジニア何人が `dotcloud` という会社が設立し、仮想型コンテナソフトが業務のメインだった、経営は厳しくなり、その後オープンソースに変え、たくさんのエンジニアが参加できて、2014年Ver1.0リリースし、大ヒットになった。今もむしろエンジニアにとって必須なスキルになるぐらい需要になってる。
 
# Dockerの勉強

Go言語に基づいて開発されたオープンソース、勉強するのに以下の二点は不可欠。

* ドキュメント

https://docs.docker.com/
ESと違って、公式のドキュメント読んでも意味わからない、dockerの公式ドキュメントはかなり充実してる。

* 倉庫

https://hub.docker.com/
ソースと環境パッケージしたもの

# Docker何が出来る？（VMとDockerの違い）

正直私よく下の図が理解できませんでした。ハイパーバイサーなんてもホストもうまく理解できませんでした。
![スクリーンショット 2021-08-11 20.04.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/755874/435369e2-7ac8-83e8-7811-e9650d80bfce.png)

↑の図を飛ばします。

簡単に言いますと、例えば、アプリケーション５個、かつお互いに隔離な環境が必要とします。
仮想マシンの場合には物理のPC上に仮想マシンを５台作って、当然カーネルも5個を作らなきゃいけないですね。メモリやストレージも5台分用意しますね。たとえプロセスが発生しなくても、用意する必要があります。
Dockerの場合には物理のPC上にカーネル一個しか作らない、5個であろうか、何個であろうか、みんな共有してこのカーネルを使うことにする。ストレージやメモリの消耗は使う時だけ発生し、起動に所用な時間が少ないです。

[参照記事](https://www.itmedia.co.jp/enterprise/articles/1612/19/news041.html)


# DevOpsに対するメリット

Dev(開発者)とOps(運用者)が協力し合い、ソフトウェアを開発する行動です。
Dockerを使うことで開発に大きなメリットがあります。

* より効率的にリリース出来る

今までソースコードもらって、環境構築ドキュメント読んで、実行して、調整するなどな面倒な作業がなくなって、ただイメージを倉庫からダウンロードしてきて、ワンクリックでリリースできちゃいます。

* 製品の拡張にもっと便利

以前例えば、Mysql Ver9.0 ,tomcat 8, redis 5 の環境があります。
tomcatをバージョンアップしたい場合、他もバージョンアップしなきゃいけない、またバージョンアップの後テストもしなきゃいけない。
今は Mysql Ver9.0 ,tomcat 8, redis 5 というまとめたパッケージがあります。それをバージョンアップする際に、その出来上がったパッケージをダウンロードすれば良い。

* 自分のPCには動いてたのに　を解決

すべての環境が全く同じのため、自分の環境では問題なく動いた、他は動いてない問題を解決する。

* カーネルの最大利用

Dockerは各アプリの実行環境は同じカーネルを共有するため、最大限でカーネルを利用できて、メモリやストレージの消耗も極力に少ない
プロンプトの表示を短く表示

wsl やターミナルのプロンプト表示を短くしたい。
$cd ~ で移動して。 そこにある.bashrc 内にある下記の文を編集。
\w(小文字)を\W(大文字)にする！

```(.bashrc)
export PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\W\[\033[00m\]\$ '
```

参考：
https://www.softel.co.jp/blogs/tech/archives/2659

http://www.gnu.org/savannah-checkouts/gnu/bash/manual/bash.html#:~:text=The%20basename%20of%20%24PWD%2C%20with%20%24HOME%20abbreviated%20with%20a%20tilde.
IBMの量子コンピューターで1+1=2を計算してみた

### 量子コンピューターと言っても
量子コンピューターと言っても、使う人間が私のような素人では、３歳児並みの計算しかできません。そういうわけで、IBMのチュートリアルを見ながら１＋１をやってみようと思います。今日で３歳児の量子コンピューターです。
でも、IBMのcomposerは新バージョンになっていて、微妙に困りましたので、QIITAに３歳児の計算を載せておきます。

### ネットの情報はcomposerの旧バージョンばかり。。
[例：加算回路を作成する](https://qiskit.org/textbook/ja/ch-states/atoms-computation.html#adder)　がIBMのチュートリアルにありますので、これで１＋１を計算してみようと思いましたが、あれ？Xゲートがない。。。ネットの情報を見ても、前のバージョンのcomposerの情報ばかりで、最近の新バージョンのcomposer情報がありません。

チュートリアルの１＋１
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/336017/bf3fbe34-465f-d9e1-3757-88e250ef5bb8.png)

新バージョンのゲート
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/336017/7bd97f0b-e1f8-044b-6306-aec1a6108358.png)


この方の [qiita](https://qiita.com/kjtnk/items/8385052a50e3154d1022) だと・・
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/336017/d25f4bfb-635c-4477-b8fb-c867e0b64d7b.png) 
そのAdvancedのチェックボックスらしきものも見当たりません。

そういうわけで、素人なりにやってみました。間違いあれば教えてください。

###　量子|0⟩ から|1⟩へ反転 
とりあえず、量子ビットは４つ必要なようなので、q[0]～q[3]まで４つ作りました。左から来る量子は、 $|0⟩$ です。mesurmentゲートを置くと、0000になっています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/336017/17a6b9f5-3195-3449-999a-01f4ad535f44.png)

下の棒グラフの0000は、この並びq[3]q[2]q[1]q[0]になっています。つまり今は全てが　$|0⟩$　です。

まず　$|1⟩$　の状態を作り出す必要があります。少し悩みましたが、反転のゲートがありました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/336017/3c0d7594-304b-3282-c081-757ed84bb4df.png)

NOT gateの説明を読むと、これはパウリXゲートと呼ばれるもので、ゲートが状態 $|0⟩$ と$|1⟩$　の振幅を切り替えると書いてあります。
つまりこれを置くことで、量子を|0⟩ から|1⟩へ反転することができそうです。 

 ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/336017/8d7caf4c-3e73-1d47-9fc9-e4ade9aac79d.png)

NOT gateを置いたら量子が反転して、$|1⟩$になり、0011になりました。
0011（q[3]q[2]q[1]q[0]）ですので、q[1]q[0]が１になっています。

後はチュートリアルのようにやれば１＋１ができそうです。

### 分かると簡単なcomposer
でも、一筋縄ではいきませんでした。。。（泣）composerのマニュアルのどこにgateの操作方法があるのか、最初は分かりにくいので、自分の意図したgateを作るやり方に気づくのが時間がかかりました。でもわかるとっても簡単でした。[ここの動画](https://quantum-computing.ibm.com/composer/docs/iqx/build-circuits#build-edit-and-inspect-quantum-circuits)を見ていればわかります。。
そのため、私がやった方法を書いて残しておきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/336017/a3a415ee-0f26-85da-da78-c9830463d5e3.png)
デフォルトの右のゲートを左のゲートのように伸ばす方法は、上の小さな●をマウスで選択して、![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/336017/e28ea584-b0d4-f560-1367-fd13ba139b0b.png)　から　![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/336017/fcfa8abb-422d-1aa6-ca6d-a831cd570a48.png)　のような選択状態になるのを待って、ドラッグして伸ばすだけです。

さてこのようにして、composerで１＋１ができました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/336017/725c72a9-da3a-472d-0b58-18504b32e409.png)


回答は、1011（q[3]q[2]q[1]q[0])ですので、ここのq[3]= $2^1$　q[2]= $2^0$ に当てていますので、答えは２となりました。

やっと１＋１が計算できました。

冬までには、もう少しレベルアップしたいと思います。
Scanner.nextInt()の落とし穴

###Scanner.nextInt()の落とし穴
これはあまり参考書などに載っていなくて、ちょっと苦労したコードの落とし穴を記事として記しておきます。
もし、同じような落とし穴にはまってしまった人の参考になれば幸いです。

###エラーの内容
以下のようなコードを実行すると、



```
あなたの年齢は？
44
あなたの名前は？
あなたの名前:   あなたの年齢は: 44
```
年齢を入力したあと、間髪入れずに

あなたの名前は？
あなたの名前:   あなたの年齢は: 44

と表示されてしまいます。

###エラーの理由
これは、整数を入力したあとのエンターキーの入力がStringとして残っていて悪さをしてしまいます。そのため、
整数を入力したあとのエンターキーの入力を処理する必要があります。



### 該当するソースコード
```java
import java.util.Scanner;



public class Main {
   public static void main(String[] args) throws Exception {

	   Scanner scanner= new Scanner(System.in);
       System.out.println("あなたの年齢は？");

       int age = scanner.nextInt();
       System.out.println("あなたの名前は？");
       String name = scanner.nextLine();


       System.out.println("あなたの名前: " + name + "  " + "あなたの年齢は: " + age );




   }
}
```



### 直したソースコード
```java
import java.util.Scanner;



public class Main {
   public static void main(String[] args) throws Exception {

	   Scanner scanner= new Scanner(System.in);
       System.out.println("あなたの年齢は？");
       int age = scanner.nextInt();
       scanner.nextLine();  //注目！！13行目
       System.out.println("あなたの名前は？");
       String name = scanner.nextLine();

       System.out.println("あなたの名前: " + name + "  " + "あなたの年齢は: " + age );
   }
}
```
確率で条件分岐するコード

# はじめに

例えば、60%の確率でa、25%の確率でb、15%の確率でcを出力するようなプログラムを実装します。

※本稿では、実装例として言語はTypeScriptで書いていますが、本稿の主旨に言語は関係ありません。

# 失敗例

この問題の解法としてパッと思いつくのは、以下のようなif文で分岐させるコードです。

```typescript
/// 失敗例

// [1,100]の区間でランダムな整数を返す関数
const rand = () => Math.floor(Math.random() * 100) + 1

// 小数点第一位で丸める関数
const round = v => Math.round(v * 10) / 10

// 試行回数
const N = 10000

let a = 0, b = 0, c = 0
for (let i = 0; i < N; i++) {
    if (rand() <= 60) {
        // 60%の確率でa
        a++
    } else if (rand() <= 25) {
        // 25%の確率でb
        b++
    } else {
        // 15%の確率でc
        c++
    }
}

// 結果出力
console.log(`a: ${round(a / N * 100)}%\nb: ${round(b / N * 100)}%\nc: ${round(c / N * 100)}%`)
```

このプログラムは一見正解な気がしますが、実際の結果は

```
a: 59.7%
b: 9.7%
c: 30.6%
```

みたいになります。aは60%に近い結果となっていますが、bとcはかなり違っています。これは何故でしょうか?

失敗例のコードだと、bのブロックに入る実際の確率は、

(100% - 60%) * 25% = 10%

です。これはaではない(確率40%)かつbである(確率25%)という条件になってしまっているからです。

# 正解例

正解は以下のようなコードになります。

```typescript
/// 正解例

// 小数点第一位で丸める関数
const round = v => Math.round(v * 10) / 10

// 試行回数
const N = 10000

let a = 0, b = 0, c = 0
for (let i = 0; i < N; i++) {
    switch (randomWithProbabilityTable({
        a: 60,
        b: 25
    })) {
        case "a":
            a++
            break
        case "b":
            b++
            break
        default:
            c++
    }
}

// 結果出力
console.log(`a: ${round(a / N * 100)}%\nb: ${round(b / N * 100)}%\nc: ${round(c / N * 100)}%`)
```

`randomWithProbabilityTable`関数の実装は以下の通りです。

```typescript
function randomWithProbabilityTable<
    TABLE extends { [key: string]: number }
>(probabilityTable: TABLE): keyof TABLE | null {
    // 確率テーブルを確率の低い順にソート
    const table = Object.entries(probabilityTable)
        .sort(([, a], [, b]) => a - b)
        .reduce(
            (result, [key, val]) => ({ ...result, [key]: val }),
            {}
        ) as TABLE

    // [1,100]の区間でランダムな整数を生成
    const rand = Math.floor(Math.random() * 100) + 1
    let rate = 0
    for (const key in table) {
        rate += table[key]

        if (rand <= rate) {
            return key
        }
    }

    return null
}
```

このプログラムの実行結果は以下のようになります。

```
a: 60.3%
b: 24.8%
c: 14.9%
```

だいたい期待した通りの数値に近い結果となっています。

確率テーブルに則った結果を得たいときは、正解例のコードのようにひと手間必要です。

:::note info
`randomWithProbabilityTable`のような関数を1つ用意しておけば、引数を変えるだけで色々な場面で使い回せます。
:::

# 参考
https://iwb.jp/javascript-random-lottery-program-probability-settings/
【PHP】ポートフォリオ

##はじめに
プログラミング学習の成果物として今回のポートフォリオを制作いたしました。
アウトプットとしてこちらに投稿します。
##ポートフォリオについて
『MusicRoot』　という名前でCDのECサイトを開発いたしました。
なぜなら私は音楽が好きなのですが、
最近はコロナの影響でライブが中止になり路上やライブハウスで販売していた
インディーズアーティストのCDを買う手段があまりないと感じからです。
開発に当たって出品者はアピールをしやすいように紹介文の機能や在庫管理しやすい出品履歴機能を、
買い手にはスムーズに取引できるようにコメント機能を付けて
自分と同じように音楽を好きな人が利用したくなるよう意識しました。
##開発環境
・PHP7.32.4
・MySQL
・Github
・Visual Studio Code
##実装機能
| No|    機能　   　　|  　　　　　　機能について 　　　　　 |
|:-:|:-----------------:|:-----------------------:|
| 1 |新規登録機能|                                   |
| 2 |ログイン機能|                                   |
| 3 |ゲストログイン機能|新規登録不要の予め登録されているユーザー|
| 4 |商品の検索機能|商品一覧から該当の商品を検索できる|
| 5 |ページネーション機能|商品一覧、出品履歴、購入履歴一覧ページに|
| 6 |商品出品機能|商品の在庫数、値段、ジャンル、アーティスト名、画像などを登録|
| 7 |商品の編集機能|金額、在庫、説明文を編集できる|            
| 8 |商品の削除機能|　出品者が商品を削除できる|
| 9 |商品の停止機能|出品者が商品を停止できる|
| 10|カートに追加機能　|商品をカートに追加できる|
| 11|カートの数量変更　|商品の数量を変更できる|
| 12|カートの削除機能　|商品の削除できる|
| 13|商品購入機能　|商品の購入する|
| 14|コメント機能　|商品についてコメントができる|
| 15|コメント削除機能　|コメントを削除できる|
| 16|アーティストの検索機能　　|アーティストを検索できる|
| 17|アーティストの登録機能　|未登録のアーティストを登録|
| 18|マイページの編集機能　　　|マイページを編集できる|

##DB設計
![er図](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/644274/21560d71-5dae-5e3b-a488-074d7223ade8.png)

##セキュリティ対策
PHPとMySQLのみで、SQLフレームワークを使用せずに開発したのでセキュリティ、脆弱性の対策をしています。

**SQLインジェクション**
formからpostされてきた値を変数に格納し、PDOのprepareメソッドを利用して作成したステートメントに値をバインドさせることによってアプリケーションに意図しないSQL文が注入されることを阻止しています。

**クロスサイトスクリプティング(XSS)**
![xss](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/644274/2acc353c-8a88-5a19-0f63-6ff3a35aa341.png)

上記の関数を使用してechoを使い値を出力するときはエスケープ処理をするようにしました。
ex) echo h(hoge);

**クロスサイトリクエストフォージェリ (CSRF)**
トークンを利用して正しいリクエスト
であるかをチェックしました。
まずpost送信を行う全てのformに対して、ランダムな文字列のトークンを生成してセッションにトークン値をセットします。そしてpost送信の受け取って操作をする前に、そのトークンの照合行い、異なる値の場合には、ログインページに飛ばします。
また、トークンの文字列が盗まれて再利用されないようにトークンの破棄もしています。
##デプロイ
conohaのvpsを使って、デプロイいたしました。
手順として、ssh接続し、本番環境にファイル、データベースの設置。
その後、セキュリティを高めるために、rootログインを禁止にし、filewallの設定でポート番号を変更しました。

[MusicRoot](http://118.27.18.210/top.php)

##工夫した点
コーディングの可読性の意識
わかりやすくするため関数や変数名などはなるべく詳細な命名を。
また共通化するため共通した処理をまとめたファイルをrequire、includeなどの関数を使って取り込みました。

MVCモデルの意識
model、view、controllerと意識しながらそれぞれのフォルダを作成することで役割ごとに効率的に開発していきました。

##ポートフォリオの課題点
・デザイン面でユーザーが見やすいレスポンシブ対応やUIなどの意識もしていきたい。
・実装したい機能がまだある（パスワード再設定や編集ページには全て確認ページを設置など）
・DB設計が不十分
→重複したDBの値などがありDBを発行しすぎている。なのでDB設計を見直すことにより処理をもっとシンプルにできると考えている。

##まとめ
初めて環境構築からデプロイまで経験してみて、様々なエラーに直面し
大変でしたが解決していくことで自走力がつきました。
今回フレームワークを使うことなくphpのみで開発することで
セキュリティに関する知識や対策、SQL文について理解を深めることができたので
今後の開発に活かしていきたいです。
Gradleでテスト実行時の文字化け @Sql

# 問題

```java
  @Test
  @Sql({"/data.sql"})
  void 検索テスト() {
    TestData result = service.find();
    assertThat(result.getData()).isEqualTo("テスト");
  }
```

data.sqlで入れたデータを検索してassertするとき、
JUnitで実行の場合は成功するがGradleで実行した場合は文字化けが発生する。

### 状況

データベースに格納されたデータ自体が文字化けしている。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224453/113c6dde-ee45-da7a-8fc1-c0a6c7c1d9a7.png)

```
org.opentest4j.AssertionFailedError: 
expected: "テスト"
but was : "繝�繧ｹ繝�"
```

### 原因

`@Sql` でデータ投入するとき文字化けしている。

# 結論

build.gradleにこの設定を追加する。
利用するファイルのエンコードをしてくれる模様。

```groovy:build.gradle
tasks.withType(Test) {
    systemProperty "file.encoding", "UTF-8"
}
```

### 解決

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224453/90ab6b14-a9e5-240e-3fbb-553927d61ade.png)
Adalo開発に関するあれこれ

現在NoCodeツールAdaloを用いて開発を始め約1年ほど経過しましたが、未だにNoCodeならではの実装の難しさを感じることがあるため、自分が詰まったところを記事に残していきたいと思います。
基本的なことから応用的なことまで幅広くドキュメントとして残せればと思います。
また、Adaloは開発スピードも早く定期的に仕様が変更されることがありますので、もし間違っている点などありましたら指摘をいただけると助かります。

## 開発関係
### webとアプリでアイコンの表示される高さがちがう。
前提としてAdaloはwebで開発を進めアプリとしてリリースします。
アイコンとテキストを横並べにして表示させる際にwebだと設定した値よりアイコンが3ピクセルほど高く表示されてしまう。
よって開発中は
→アイコンを3ピクセル下げて設定する。
という対応を取るのが王道です。

しかし、アプリとしてリリースする3ピクセル下げた値がそのまま表示されます。（android,iOS共に）
→アプリに合わせるのが望ましいので通常通り中央揃えで設定しましょう。
※開発中webの表示が少し崩れているのは許容する必要あり

## iOSビルド関係
### スクリーンショットが必要
iOSは審査が厳しいのでスクリーンショットが必要です。
「simulator」というアプリでスクショを取るのが良いでしょう。
5.5インチ
→iPhone 6 Plus,iPhone 6s Plus,iPhone 7 Plus,iPhone 8 Plus
6.5インチ
→iPhone XS Max,iPhone 11 Pro Max
12.9インチ
→iPad Pro 12.9
### ipadのスクリーンショットが取れない
ipad(12.9インチ)のスクショを撮ろうとすると強制的にスマホの表示になってしまします。
→11インチでスクショを取る、「2048x2732」のサイズにリサイズする
### fontsizeが設定した値になっていない
意図せぬところでテキストが折り返している、、、、
→スマホ側のfontsizeの設定が適応されている(Dynamic Type)
→一般的な開発であればスマホの設定を無視するコードを記述できますが現状Adaloにはその機能はないので諦めて文字が大きい場合でも崩れをすくなるする対応が必要

https://spinners.work/posts/kudakurage-ios-dynamic-type-design/

https://forum.adalo.com/t/automatic-font-resizing-based-on-device/6705


## androidビルド関係
### サブスクリプション購入のコンポーネントが機能しない
まずはテストするためのアカウントを用意する必要があります。
通常のテスターだと購入ボタンをクリックしても何も動かないので
「設定」→「ライセンステスト」からテスト用のアカウント（メールアドレス）を追加します。
→これで設定しましたが現在テスト環境で動作しないのでもう少し調査を進めます。
VSCodeでPythonのLambda(SAM)をローカルデバッグしようとしたらハマった

# ざっくりまとめ

* `pyenv global 3.9.6`とかでPython3.9系をGlobalで利用するようにしていた
* この状態でVS CodeからLambdaのデバッグを実行しようとするとSAMのビルドでコケる
  * SAMでビルドするときに`python3.8`を要求するため
* `pyenv global 3.9.6 3.8.10`とかで`python3.8`が実行できるようにしてあげる必要があった

# 背景

SAMでアプリをカキカキしようとしてデバッグしたいなーと思ってぺいっとF5をを押したらこんなエラーが

```
2021-08-11 18:32:36 [INFO]: 
Build Failed

2021-08-11 18:32:37 [INFO]: Error: PythonPipBuilder:Validation - Binary validation failed for python, searched for python in following locations  : ['/Users/****/.pyenv/shims/python3.8', '/Users/****/.pyenv/shims/python', '/usr/bin/python'] which did not satisfy constraints for runtime: python3.8. Do you have python for runtime: python3.8 on your PATH?

2021-08-11 18:32:37 [WARN]: "sam build" failed: /Users/****/work/test-sam/app___vsctk___template.yaml
```

ちーん

## 試したこと

python3.8が叩ければいいんだなということで色々試す

* このsam templateが置かれてるWorkDirで`pyenv local 3.8.11`
  * 普通にコマンドライン上では`sam build`ができるようになる
  * VS Code経由でsam実行されると同じエラー
  * どうやらpyenvのアレコレが効いてくれない...
* `pyenv global 3.8.11`
  * 普通に通るようになった
  * どこのパスにいても`python3.8`を実行できるようにしておけばいいんだな
  * でもこうするとglobalで使いたいpython3.9が使えなくなってまうで...

## 解決

* `pyenv global 3.9.6 3.8.11`
  * バージョン複数指定してあげるとそのバージョンのバイナリも叩けるようになる
  * 知らなかった
* バージョンは先頭に来る方が優先される模様
  * ↑の場合は`python` `pip`とかで実行されるpythonは`3.9.6`になる

## 結論

pyenvのドキュメントちゃんと読みましょう
MacにMySQLのインストールと設定

#始めに

MySQLの勉強を始めようと思い、[ここ](https://qiita.com/griffin3104/items/c7908359a3e3e18cd269)の記事を参考にインストールと設定を行なった。

#環境
```
macOS: Catalina 10.15.7
xCode: version 12.4
```

#MySQLのインストール

いつものように、`Homebrew`でインストールする。

```bash:terminal
$ brew install mysql
```

#MySQLの設定

サーバーを起動して初期設定を行う。

```bash:terminal
$ mysql.server start
$ mysql_secure_installation
```

以下のことに関して聞かれるので、それぞれ設定を行う([リンク](https://mariadb.com/kb/ja/mysql_secure_installation/))

- root userのパスワード変更
- anonymous usersの削除するか否か
- リモートからのrootログインを許可するか否か。
- test DBを削除するか否か
- 権限テーブルを読み込ませて、上記の変更をすぐに反映させるか否か

終わったらサーバーを停止する。

```bash:terminal
$ mysql.server stop
```

# 実行

### サーバーの起動
```bash:terminal
$ mysql.server start
```

### MySQLの実行

```bash:terminal
$ mysql -uroot -p
```
`ctrl+d`で抜けられる。

### サーバーの停止

```bash:terminal
$ mysql.server stop
```
docker-compose buildでファイルが間違っていないはずなのにエラーになる (ERROR: no such image: サービス名 : invalid reference format

# はじめに

Dockerで開発環境を作ろうと`Dockerfile`と`docker-compose.yml`を用意して立ち上げようとしたらエラーになり、かなり苦戦したのでまとめたいと思います。

いままで意識したことなかった意外なところに原因がありました。
記事でも見かけたことがなかったのでまとめます。

# 問題

Pythonの環境を作るために以下の記事から`Dockerfile`と`docker-compose.yml`をコピーしていました。

https://qiita.com/reflet/items/4b3f91661a54ec70a7dc

別の環境で全く同じファイルで動作済みでしたので、今回も利用しようとしたらエラーになりました。

使用したコマンド

```
$ docker-compose build
```

エラーが発生しました。

```
PS C:\Users\jinwa\Desktop\workspace\ドッカー課題> docker-compose up
ERROR: no such image: _python3: invalid reference format
```

別のディレクトリを作成して動かしたときは動きました。なぜかこのディレクトリのみ動きません。

# 解決方法

まさかのディレクトリ名に原因がありました。

```
ドッカー課題
┣ dockerfile
┣ doeker-compose.yml
```

という構成で、**「ドッカー課題」**という名前にしていました。

検証したところ日本語オンリーのディレクトリ名ではdocker-composeを利用できませんでした。

以下のような名前なら動きました。

```
例: docker-kadai, docker課題
```

# おわりに

思えばいままで日本語だけのディレクトリで動かしたことはありませんでした。
ほぼやることはないと思います。それゆえに原因究明にかなりの時間を使ってしましました。


# 参考
- [dockerで簡易にpython3の環境を作ってみる](https://qiita.com/reflet/items/4b3f91661a54ec70a7dc)
[oVirt] トラブルシュートメモ

# これはなに？

oVirtに関する雑多なメモです。

# Windowsゲストに動作しないデバイスが残る

## 現象

デバイスマネージャを開くと正常に動作しないデバイスとして **HID Button over Interrupt Driver** が表示されます。

## 解決策

次の手順でデバイスを削除します。

1. 次のレジストリサブキーを削除する
    * HKLM\SYSTEM\DriverDataBase\DriverPackage\hidinterrupt.inf_x86_<some instance ID>\Descriptors\ACPI\ACPI0010
2. 次の場所にあるレジストリ値 **hidinterrupt.inf** を削除する
    * HKLM\SYSTEM\DriverDataBase\DeviceIds\ACPI\ACPI0010
2. デバイスマネージャからデバイスを削除する
2. 再起動する

## 参考リンク

* https://bugzilla.redhat.com/show_bug.cgi?id=1377155

# ゲストエージェントをインストールしているのに、インストールしていないと表示される

## 現象

oVirt管理コンソールから見ると、VMのところに **The latest guest tools needs to be installed and running on the guest**

## 現象確認方法

ホストノードに接続し、`virsh` コマンドで本当にゲスト情報が取れないのか確認します。

```console
virsh -c qemu:///system?authfile=/etc/ovirt-hosted-engine/virsh_auth.conf
```

virsh上で以下のコマンドを実行します。情報が取れれば、ゲストエージェント自体は動作しています。

```
guestinfo VM（ドメイン）名
```

```
domifaddr VM（ドメイン）名 --source agent
```

## 解決（？）策

ホストノードを一旦メンテナンスモードにします。

## 参考リンク

* https://bugzilla.redhat.com/show_bug.cgi?id=1981946
COVID-19危機におけるDatabricksのビジネス継続性

[Business Continuity at Databricks During COVID\-19 Crisis \- The Databricks Blog](https://databricks.com/blog/2020/03/17/business-continuity-at-databricks-during-covid-19-crisis.html)の翻訳です。

親愛なるお客様

COVID-19が日々の生活に影響をもたらし続けている中、我々は、数多くの人々にか欠かすことのできないサービスを提供している医師の皆様に加えて、影響を受けている方々のことを考えています。

我々は新たな現実に適合しており、皆様には我々が皆様に対するフォーカスを失っていないこと、この進化する状況の中で実行するための包括的なプランを持っていることを知っていただきたいと考えています。いつものように、皆様はDatabricksのテクノロジーとスタッフを信頼していただきたいと考えております。

我々は、多くのお客様において我々のプラットフォームが重要な役割を担っていることを完全に理解しており、皆様が信頼するに至ったワールドクラスの顧客体験を変わらず提供し続けることにコミットしています。Databricksプラットフォームの信頼性の維持に注力し続け、我々のカスタマーサクセスチームは、グローバル規模で皆様をサポート、アドバイスし続けることを楽しみにしています。念のためですが、Databricksの可用性は、[status\.databricks\.com](https://status.databricks.com/)からリアルタイムで確認することができます。

お客様に対するコミットに加え、従業員、コミュニティの健康、安全が我々の最優先事項です。従業医のワークフロムホームを導入し、イベントのキャンセルあるいはバーチャル化を行い、出張を制限することで、COVID-19の拡大を防ぐ手段を講じています。このような時期においても、皆様と我々のチームが安全であり、つながっており、サポートされていることを強調させてください。

最後に、進化し続ける状況に対応し続けるために、Databricksのシニアリーダーによるタスクフォースを結成しました。このグループは、デイリーベースでベストプラクティスを監視し、お客様、従業員が最も関心を持っている分野で必要なアクションをとります。COVID-19に関連するDatabricksのアップデートに関しては、[COVID-19リソースハブ](https://databricks.com/databricks-covid-19-resource-hub)をご覧になってください。

皆様から信頼されるパートナーであることが、我々のミッションであり特権であると考えています。この困難な時期においても、皆様と皆様のビジネスに信頼できるサービスを提供することにフォーカスし続けます。お身体を大事になさってください。

ご健康をお祈りします。

Ali Ghodsi
Databricks CEO & Co-Founder

### Databricks 無料トライアル

[Databricks 無料トライアル](https://databricks.com/jp/try-databricks)
JUCEで作成したAUv3プラグインのウィンドウのリサイズ問題

##Big Surか、それ以外か。##
すべての説明は下記のJUCEフォーラムのポストにある通りで、現状Logic Pro XはBig Surでないとまともにリサイズ可能なプラグインを扱えない。残念ながら。

https://forum.juce.com/t/auv3-resizing-issue-on-macos-not-ios/43811/10

プラグイン内部からリサイズを仕掛けても、AU(v2)だと正常にリサイズができるコードでもAUv3だとリサイズができない。このことはAUv3の普及を妨げているとしか思えないが、現状はそういう感じでBig Surより前のOSではAUv3はあまり良い動作をしてくれない。

##Big Surで動いたコード##
下記がBig Surでホスト(Logic Pro X)からリサイズがまともに動いたコード。プラグインエディタのコンストラクタで行っている。

```HogeAudioProcessorEditor.cpp
    int defaultWidth = 1024;
    int defaultHeight = 768;
    double ratio = (double)defaultWidth / (double)defaultHeight;
    setResizable (true, false);
    setResizeLimits (defaultWidth / 2,
                     defaultHeight / 2,
                     defaultWidth * 2,
                     defaultHeight * 2);
    getConstrainer()->setFixedAspectRatio (ratio);
```

こうすることでプラグインのウィンドウがリサイズ可能になり、リサイズ時にresized()が呼ばれる。それを受けてUIを再配置する…のはめんどくさいと思ったでしょ？思うよ。なので、Logicの内蔵プラグインがやっているような単純な拡大縮小を行うには、resized()で下記のようにすればよい。前提として、エディタ全体を覆うComponentを持っているものとする。

```HogeAudioProessorEditor.cpp
    int width = getWidth();
    double scale = (double)width / (double)mainComponent->getWidth();
    mainComponent->setTransform(AffineTransform::scale(scale, scale));
```
2要素認証を設定したGitHubアカウントのリポジトリをSourceTreeからクローンする方法

# 前提
- PCはMac（Windowsでもほぼ同じだと思います）
- GitHubに2要素認証設定済み
- SourceTreeインストール済み

# やること
[GitHubの2要素認証](https://docs.github.com/ja/github/authenticating-to-github/securing-your-account-with-two-factor-authentication-2fa/configuring-two-factor-authentication)を設定しているアカウントのリポジトリをSourceTreeでクローンする。

# やりかた
### GitHubでトークンを発行する
1. GitHubのSettingsページに遷移する
<img width="400" alt="e0ff39f2-441e-4d67-b8c2-b4b527790b8a.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635329/e5b23171-bf2e-06bf-a346-4678e07d186d.png">

1. 左のリストの【Developer Settings】をクリック
1. 左のリストの【Personal access tokens】をクリックし、【Generate New Token】をクリック
![5ea6db4f-a000-42dc-8599-24a31c6eff97.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635329/c10199f9-33d0-bc08-f42a-508988fc180d.png)

1. Noteに任意の名前、Expirationに有効期限、Select scopesにrepoを選択してページ下部の【Generate token】をクリック
![1b929a67-c901-4d29-8082-7d73460046b9.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635329/e90d3af6-d991-b67a-1b5b-f1f58f89bcf7.png)

1. ページに表示されているトークンをコピーし、メモ帳などに貼り付けておく
![e4878d24-3d27-4975-ac89-c3f88fa18222.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635329/1f7e7bda-6807-52f5-e924-ef8af6b04fb4.png)

### SourceTreeでクローン
1. Sourcetreeの【新規】の【URLからクローン】をクリック
<img width="400" alt="ea82cc3b-951d-4428-8ff2-64d8a1137ca8.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635329/5dc82a9e-a2cf-5f24-40be-4ad3095ccab5.png">

1. ソースURLにクローンしたいリポジトリのURLを入力
<img width="400" alt="83e2640c-3f52-4ec6-a1e8-b47beddc34a1.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635329/ae305108-2ca0-467d-c3ab-85dad077e022.png">

1. 開いた認証画面のUsernameに【自分のGitHubアカウント名】、Passwordに【先程コピーしたトークン】を入力してOKをクリック
<img width="400" alt="70ebfcbe-8adc-47ab-8678-4acad067ec1c.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635329/41c79b33-3656-f401-7ec7-118521af733f.png">

1. 【クローン】をクリック
<img width="400" alt="80fc1227-1817-4f46-a46a-4c2f41232def.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635329/6b609f9b-5a65-e3cb-5fd2-ad934ed85055.png">

1. 該当リポジトリのSourcetreeの画面が表示されていればOK
![a7506eb1-1ba5-4451-8c4f-cf41b173f35c.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635329/02bb88bb-3bd3-c3e3-9aac-53457edc0451.png)
Amplify 自動生成したクエリをカスタマイズしたい。

こんにちは。Amplify 歴が2年近くになってきた coa00 です。プロダクトで使っているのでどんどんノウハウがたまっているのですが、発表する時間がないです（スタートアップつらい）

お手軽なTipsを一つ紹介します。

## リレーショナルについて

graphql は model と model 同士を関連させることができます。

例えばユーザをチームに所属したい場合だと下記のように many to many で結合することができます。

```
type User
  {
  id: ID!
  firstName: String
  lastName: String
  teams: [UserTeam] @connection(keyName: "byUser", fields: ["id"])
}


type UserTeam
  @model(subscriptions: null)
  @key(name: "byUser", fields: ["userID", "teamID"])
  @key(name: "byTeam", fields: ["teamID", "userID"])
  {
  id: ID!
  teamID: ID!
  userID: ID!
  team: Team! @connection(fields: ["teamID"])
  user: User! @connection(fields: ["userID"])
}

```

詳しくは下記をよんでいただければと思います

https://docs.amplify.aws/cli/graphql-transformer/connection


### リレーショナルの課題

便利なのですが、一つ問題があります。
生成される list などのレスポンスが大きくなっていきます。

上記の例で話すと

User.teams の下に UserTeam　の配列がかえってきます。
さらに UserTeam は、UserとTeam　がはいっていいます。

（無限ループ的につながっていくのではないか、と思ったかたご安心ください。maxDepth というものがあります）

### 解決策

解決策はたくさんあると思います。
リレーショナルをやめるのもよいと思いますし、
自動生成を使わないのも手ですね。

ケースバイケースですね。


## 自動生成されたクエリをオーバライドする。

Amplify の素晴らしさはスピードだと思っているので、
自動生成もリレーショナルも使い、かつ3分ぐらいでできる解決策を考えてみました。


### やり方

シンプルです笑

#### src したに cgraphql というディレクトリを作成し、mutation, queries を作成する

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5565/ed347d55-9b89-6811-93a9-da920054c966.png)

####  先頭で対応する　qraphqql　を　import export する。 

その下にオーバライドしたいクエリをコピーします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5565/1be40182-d339-2713-0fd3-56bb0960df07.png)

####  コピーしたクエリから不要なレスポンスを削除します。

使う時は、src/graphql/queries ではなく src/cgraphql/queries を import して使います。

## まとめ

- リレーショナルを使う時は、レスポンスサイズをコントロールしましょう
- 自動生成したものはオーバライドすることで小さくしましょう。
【Ruby】単体テストコードのためのgem導入について

今回はテストコードを書くための3つのgemの導入について書いていきます。

RSpec,Faker,FactoryBotです。
RSpecはRuby on Railsのテストコードを書くために用いられるGemです。
Fakerはテストの際に用いるユーザー名やメールアドレスなどを、一定のルールに則り生成するGemです。
FactoryBotはインスタンスをまとめることができるGemです。Fakerと組み合わせて使うことができます。

最初は「コードは動けばそれでいいのでは？」みたいなことを考えていましたが、
今は品質を担保するためには必要な作業だと思っています。

さて導入の方法ですが、まず今作業しているアプリケーションのGemfileに移動し、

```Gemfile
group :development, :test do
   # Call 'byebug' anywhere in the code to stop execution and get a debugger console
   gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
   gem 'rspec-rails', '~> 4.0.0'       
　　　　　　gem 'factory_bot_rails'　 
　　　　　　gem 'faker'　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　#4〜６行目に追記しています
 end
```

4〜６行目のように記述します。この時必ず「group :development, :test do」へ記述します。
似たような記述があるので注意したいですね。

次に、ターミナルでテストをしたいファイルのあるディレクトリに移動します。
ターミナルで以下のように記述します。

```
% cd ~/(今作業しているディレクトリ名)
% bundle install
```

次に

```
rails g rspec:install
```

最後に
「.rspec」というファイルにテストコードの結果をターミナル上で可視化するための記述を追加します。

```
--format documentation
```

以上でRSpecの導入は完了です。
Aceエディターにキーボードイベントを追加する

今までMonacoエディターを使っていたのですが、シンタックスハイライトがAceの方が充実しているのでAceに乗り換えることに。

## Monacoの場合
キーボードイベントを追加したい場合、MonacoだとonKeyDownというメソッドがあったので、キーボードイベントをすぐ登録できたのですが、Aceにはない。

````js
let = editor = monaco.editor.create(<options>); // IStandaloneCodeEditor
editor.onKeyDown((e) => {
    <commands>
});
````
## Aceの場合
Aceの場合は以下のように登録できる(なぜかAPI Docsに乗ってないがeditor.hasOwnProperty("commands")はtrueになる)。

````js
let editor = ace.edit("<element id>");
editor.commands.addCommand({
  name: "<command name>",
  exec: () => {<commands>},
  bindKey: {mac: "<key>", win: "<key>"} // e.g. {mac: "cmd-p", win: "ctrl-p"}
});
````

しかし上の方法で登録するとデフォの入力ができなくなってしまう(そうしたい場合はいいのだが)。結局

````js
window.onload = () => {
  editorElement = document.getElementById("<element id>")
  editorElement.addEventListener("keydown", <command name>}

let <command name> = (e) => {
  <commands>
//  e.g.
//  if (e.ctrlKey && e.key == "p") {    
//    console.log(e.key)
//  }
}
````
のようにする。

####メモ
VueでAceを使うときsessionをVueのdataに代入するとマルチカーソル使ったときにバグる。uiのエディターとsessionの同期がVueで阻害される?
openslideをインストールする手順 [Windows10,Google colab]

#概要#
本記事では、
以下のそれぞれの環境でopenslideをインストールした手順を紹介します。
**・Windows10 (Anaconda-Jupyter notebook)**
**・Google Colaboratory**

#実行環境#
**[ローカル環境]**
Windows 10 Pro(64bit)
Anaconda 4.10.3
JupyterNotebook 6.1.5
Python 3.7.9

#[Windows10]#
##ダウンロード手順##
１．以下のOpenSlide for Windowsのダウンロードページへ行く。
　　https://openslide.org/download/#windows-binaries

２．最新(2017-11-22)の64-bit版をダウンロードする。

![[openslide]Windows用.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1872129/09360da2-514a-bc88-76f1-d3cffc422b34.png)

##インストール手順##
１．ファイルを展開し、作成されたファイルの中にある
　　binフォルダ内のDLLファイル群全てを
　　現在使用しているpython.exeが置いてあるフォルダに置く。

２．pythonにて"pip install openslide-python"を実行する。


３．インストール成功後に「Note: you may need to restart the kernel to use updated packages.」
　　と表示されるため、カーネルをリスタートする。

##使用開始方法##
・pythonにて"import openslide"を実行。

#[Google Colaboratory環境]#
##インストール手順##
・Google Colaboratory上で以下のコマンドを実行する。

!apt update && apt install -y openslide-tools
!pip install openslide-python

![[openslide]Windows用3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1872129/e4237643-8ce5-dbb1-7bb8-6821d3d9086d.png)
↑インストールが正しく行われたことを確認。

※openslide-toolsをインストールしないとimport時に以下のようなエラーが出ました。

![[openslide]Windows用4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1872129/00876f81-5e07-5acf-e57a-69a2c7145430.png)


##使用開始方法##
・インストール後、pythonにて"import openslide"を実行。

#参考URL#
以下のQ&Aを参考にしました。

[Windows 10]
https://teratail.com/questions/213899

[Google Colaboratory]
https://stackoverflow.com/questions/54208457/is-it-possible-to-use-openslide-on-google-colab
[Vue.js]computed(算出プロパティ) の基本 

```HTML
<div id="app">
  <h1>computed</h1>
  <input type="text" v-model="inputText">
  <br>{{inputText}}<br>
  {{changeTextCapitalize}}
</div>
```
returnで返さないと反映されない

```Vue.js
new Vue({
  el: "#app",
  data: {
    inputText: 'hello'
  },
  computed: {
    changeTextCapitalize: function () {
        return this.inputText.toUpperCase();
    }
  }
})
```
非公開のリソースにURLでアクセスされたらどうする？（例えば非公開記事とか）

# やりたいこと
投稿者が記事を投稿します。
投稿した記事は、公開／非公開（active/inactive）を設定できます。
読者側のページでは、公開記事を一覧で表示して（index）、各記事にアクセスできるようにします（show）。

読者側ページでは非公開記事は表示せず、遷移する動線を提示しないのは当然ですが、これをしただけでは、記事のURLからアクセスされたら非公開記事も見られてしまいます。

どうすれば良いでしょうか？

環境
ruby 2.7.2
Rails 6.1.3.1

投稿データを扱うこんなモデルがあったとします:point_down:
公開／非公開を制御するために、statusというカラムを持たせて、enumで定義します。

```ruby:post.rb
# == Schema Information
#
# Table name: posts
#
#  id               :bigint           not null, primary key
#  title            :text             not null
#  body             :text             not null
#  status           :integer          not null
#  created_at       :datetime         not null
#  updated_at       :datetime         not null

enum status:       { active: 10, inactive: 20 }

```

# 選択肢１：リダイレクトする
リダイレクトすれば確かに非公開記事にはアクセスできなくなりますが、それが本当に存在しない出鱈目なURLの記事にアクセスした時と違う挙動（例外とか）になると、そのURLの先にリソースがあるということは暗示してしまいます。
Webサイトの考え方にもよるかもしれませんが、多くの場合は、本当に存在しないURLと同じ挙動になるように、例外を出してあげた方が良さそうです。

一応リダイレクトを是として実装するなら、こんな感じでしょうか。

```ruby:
class Reader::PostsController < Reader::ApplicationController
  before_action :set_post, only: %i[show]

  def show
    redirect_to reader_posts_path if @post.status_inactive?
  end

  private

  def set_post
    @post = Post.find_by(id: params[:id])
  end
end
```


# 選択肢２：404を返す
404とは、HTTPステータスコードの一種で、「サーバーには到達できたけど、あなたのリクエストしたもの（投稿）は無かったよ」というメッセージです。つまり、全く存在しない記事のURLを入力された時と同様、例外にしてあげます。
実装は驚くほどシンプルに実現できます。

```ruby:post_controller.rb
class Reader::PostsController < Reader::ApplicationController
  before_action :set_post, only: %i[show]

  def show; end

  private

  def set_post
    @post = Post.active.find_by!(id: params[:id])
  end
end
```
<dl>
  <dt>.active</dt>
  <dd>モデルのenumで定義している`active`をこんな感じで使えるんですね！これでpostsテーブルの中で、find_byする前にactiveのものだけに絞り、非公開記事を除くことができます。　</dd><br>
  <dt>find_by!</dt>
  <dd>エクスクラメーションマークのないfind_byとは違う挙動をします。指定されたレコードが見つからなかった時、find_by!はfind_byはActiveRecord::RecordNotFound例外を発生させ、find_byはnilを返します。find_by!は見つからなかったらその時点で「Couldn't find Post with...」というエラーを出してくれます。find_byはビューをレンダーするところまで到達して、 @postを参照し、ここで`undefined method 'title' for nil:NilClass`というエラーになります。</dd>
</dl>
開発者ツールのNetworkでも確認できます。

`find_by!`
![couldnfiind404.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/645077/9a75d2be-5d7e-3886-f4ab-b4f66c5ae080.png)

`find_by`
![undefinedmethod500.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/645077/f1e7d178-2049-3748-5ca7-feab45aa86bf.png)
find_byの時は500エラーなのか:astonished:

# 最後に
何かご指摘などあれば、ビシビシお願いします。

毎度毎度ですが、会社の先輩　@Y_uuu さんに大感謝です。

https://fusic.co.jp/
CHIRIMENをNode.jsで使用してみる⑴ ー 導入編 ー

今回が初投稿になります。拙い文章かもしれませんがよろしくお願いします。
## はじめに
本記事では、複数回に分けてCHIRIMENコミュニティが提供しているnpmパッケージnode-web-gpioとnode-web-i2cを使用してNode.jsからGPIOとI2Cを制御するために私が実施した方法とそのサンプルコードを記載した備忘録になっています。
今回はCHIRIMENの紹介や後述のNode.js版の導入方法などがメインとなっています。

## CHIRIMENについて
CHIRIMENとは、Web ブラウザからハードウェア制御を可能にしたプロトタイピング環境です。簡単にいえば、WebブラウザのJavaScriptからGPIOやI2Cの制御が可能ということです。CHIRIMENについて詳しく"[CHIRIMEN OPEN HARDWARE](https://chirimen.org/)"を参照してください。I2Cの対応デバイスについては"[CHIRIMEN 対応デバイスリスト](https://tutorial.chirimen.org/partslist)"を参照してください。
またCHIRIMENは、Raspberry Piやmicro:bitなどで使用可能ですが、本記事はRaspberry PiでCHIRIMENを使用した場合についての記事になります。

## Webブラウザ版とNode.js版
Raspberry PiでCHIRIMENを使用する場合、Webブラウザを使用するWebブラウザ版とNode.jsを使用するNode.js版の２つの方法があります。主な違いを以下にまとめました。

- Webブラウザ版
  - Raspberry PiにCHIRIMENのOSイメージをインストールすることで使用可能。
  - WebブラウザのJavaScriptからGPIOやI2Cの制御が可能。
  - センサーで測定した値はWebブラウザに出力される。
  - IoT初心者のプログラミング学習に向いている。
  
- Node.js版
  - Raspberry PiにNode.js環境を構築することで使用可能。
  - Webブラウザを使用せずにJavaScriptからGPIOやI2Cの制御が可能。
  - センサーの動的な処理が可能。
  - IoTやWoTシステムの設計に向いている。
 
Webブラウザ版を使用するために必要なCHIRIMENのOSイメージのインストール方法については"[CHIRIMEN for Raspberry Pi の SD カードを作成する](https://tutorial.chirimen.org/raspi/sdcard)"を参照してください。
Node.js版の使用にはRaspberry Pi内にNode.jsの環境構築が必要です。しかし、Webブラウザ版とNode.js版の動作を比較するなどの目的でCHIRIMENのOSイメージのインストールを行った場合、OS内にNode.js環境がデフォルトでインストールされているため、新たにNode.jsの環境構築を行う必要はありません。

## Webブラウザ版とNode.js版の違い
Webブラウザ版との主な違いは、大きく分けて3点あります。主に動作方法やHTMLから読み込むJavaScriptとNode.jsで使用できるメソッドによる違いになります。

- 動作方法による違い
  - Webブラウザ版
     - WebブラウザからJavaScriptを参照するため、動的にセンサーを扱いたい場合にはHTMLそのものを動的に更新する必要がある。
  - Node.js版
     - 動的にセンサーを扱いたい場合にはNode.jsを直接実行できればいいのでNode-REDなどに組み込む場合でも扱いやすい。
- 出力方法の違い
  - Webブラウザ版
     - JavaScript内でDocument.getElementByIdやinnerHTMLを使用することでセンサーの値を取得し、HTML上に出力。
  - Node.js版
     - 上記の方法では出力できないため、console.logなどで出力する。
- パッケージ呼び出し方法による違い
   - パッケージ呼び出し時のコードの冒頭に書く"おまじない"の書き方が異なっている。（詳しくは後述）

## 環境構築
Raspberry Piへのnode-web-gpioとnode-web-i2cのインストール方法について説明します。Raspberry PiへCHIRIMENのOSイメージをインストールもしくは、直接Node.jsをインストールした後、作業用のディレクトリを作成し、作業用ディレクトリ内で以下のようにコマンドを実行することでインストールできます。詳しくは"[WebGPIO API や WebI2C API を Node.js から使う](https://tutorial.chirimen.org/raspi/nodejs)"を参照してください。

```
// npm のためのファイル package.json の作成
% npm init -y

// node-web-gpio と node-web-i2c のインストール
% npm install node-web-gpio 
% npm install node-web-i2c
```
## さいごに
今回は以上になります。次回からの記事では、CHIRIMENをRaspberry Piから使用するための対応デバイスのドライバ・回路図・サンプルコードをセットにした "[Examples](http://chirimen.org/chirimen/gc/top/examples/)"に記載されているWebブラウザ版でのJavaScriptのサンプルコードのNode.js版への書き換え方法などを記載していきます。次回はnode-web-gpioでのGPIOの使用方法についての記事になる予定です。

- [次の記事「CHIRIMENをNode.jsで使用してみる⑵ ー GPIO編 ー」はこちらから](https://qiita.com/okojon/items/85046f3c8483c61ff8c3)
Next.js に Bing Custom Search の Hosted UI を埋め込む

SSRでやるべきことではないはずだし、一時的なものだがググっても出なかったので。

`import Script from 'next/script'`として大文字の`<Script>`でやっても、`</body>`タグ直前に検索フォームは出るが検索はできない。通常の小文字の`<script>`タグを使い、閉じタグ？の`</script>`を消して`<script src="" />`という形にすると上手く行った。

```tsx:index.tsx
          <script type="text/javascript" 
              id="bcs_js_snippet"
            src="https://ui.customsearch.ai/api/ux/rendering-js?customConfig=xxx&market=ja-JP&version=latest&q=&setLang=JA&safeSearch=Strict"
          />
```
PHPの配列について【PHP入門】

配列とは変数名が同じで、複数のデータを1つの箱のことです。

配列が便利な場合を見ていきましょう

```index.php
<?php
$name1 = "一郎";
$name2 = "二郎";
$name3 = "三郎";
$name4 = "四郎";
$name5 = "五郎";

print($name1)
print($name2);
print($name3);
print($name4);
print($name5);

?>
```

ブラウザには以下のように表示される

```
一郎二郎三郎四郎五郎
```

プログラミングは楽するためにコードを書きたい！！と思う人が多い
これをより簡潔にするのが配列になる

先ほどのコードを配列で表すと以下のようになる

```index.php
<?php

$name1 = ["一郎","二郎","三郎","四郎","五郎"];

print ($name1[0]);  #配列では1番目がname[0]になる name[0]=一郎
print ($name1[1]);  #配列では2番目がname[1]になる
print ($name1[2]);  #配列では3番目がname[2]になる
print ($name1[3]);  #配列では4番目がname[3]になる
print ($name1[4]);  #配列では5番目がname[4]になる

?>
```

配列の要素を追加する

配列に新しい要素を追加したい時があります、その時に便利なのがarray_pushです

```php
<?php

$name1 = ["一郎","二郎","三郎","四郎","五郎"];

array_push($name1,"六郎");
print($name1[5]);
?>
```


そうすると配列の箱の中身に[六郎]が入ります

※追記
もしも関数に1つ要素を加えるなら　$array[] = $var;　の方がいいです

参考サイト：
https://www.php.net/manual/ja/function.array-push.php
2011年度　広島大学　数学【２】（対数）※不等式評価のテクニック付き

# 問題文

次の問いに答えよ。

(1)$\log_23 = \frac{m}{n}$を満たす自然数$m,n$は存在しないことを証明せよ。

(2)$p,q$を異なる自然数とするとき、$p \log_23$と$q\log_23$の小数部分は等しくないことを証明せよ。

(3)$\log_23$の値の少数第１位を求めよ。


# 読んで思ったこと

- 背理法じゃん。
- (3)は、いま数学系YouTuberがこぞって取り上げている「不等式評価」というやつ。$\log_23$を上下から評価して少数第１位を決定する。
- 不等式評価を知らなければ、(1)(2)は解けないとダメだねぇ、そうだろう当時のワイくんよ。


# 解答

(1)
$\log_23 = \frac{m}{n}\ \dots\ [1]$を満たす自然数$m,n$が存在すると仮定する。
[1]式より

```math
2^{\frac{m}{n}} = 3\\
\Leftrightarrow\ \ 2^m = 3^n
```

となり、左辺は偶数、右辺は奇数であるため、上式は成り立たない。
よって[1]式を満たす自然数$m,n$は存在しない。

(2)
$p,q$を異なる自然数とし、$p\log_23$と$q\log_23$の小数部分が等しいと仮定する。$p>q$としても一般性を失わないので$p>q$とし、$l$を自然数として$p\log_23 - q\log_23 = l\ \cdots\ [2]$と表せる。
[2]式より

```math
(p-q)\log_23 = l\\
\Leftrightarrow\ \ \log_23 = \frac{l}{p-q}
```

上式は(1)の結果に反するので、$p\log_23$と$q\log_23$の小数部分は等しくない。

(3)

>★少数第$N$位を決定するには不等式評価★
>
>実数$x$の少数第１位が３であるとき、
>
>```math
>1.3 < x < 1.4
>```
>
>が成り立つ。
>同様に少数第２位が5であれば
>
>```math
>1.35 < x < 1.36
>```
>
>が成り立つ。
>このように、$x$を上下から挟んでやることで、目的の位の数を決定することができる。
>
>---
>
>決定する位が深いほどより厳しい評価が必要になるため奇抜なアイデアを要することがあるが、今回は少数第１位だけでよいのでパッと思いつくガバガバ評価で十分かもしれないし、不十分かもしれない。
>今回の場合、$\log_22 < \log_23 < \log_24$と評価すると$1 < \log_23 < 2$となり、小数部分を１つも決定できない。
>対数の性質として、真数が大きくなるほど対数の増加は穏やかになり、$\log_23$をわざと自然数倍してから評価することで厳しい評価が可能になる。

$\log_28 < \log_29$より$3 < 2\log_23\ \ \Leftrightarrow\ \ 1.5<\log_23\ \cdots\ [3]$

>👆ほんとは$\log_28 < \log_29 < \log_2 10$としたいが、$\log_25$の値が与えられていないので不可能。一旦下からの評価だけしておく。

$\log_2 243 < \log_2 256$より$5\log_23 < 8\ \ \Leftrightarrow\ \ \log_23 < 1.6\ \cdots\ [4]$

>👆$\log_2 3^s$を$\log_2 2^t$で上から押さえつけられる、かつ対数の値が一番近くなるような$s,t$をいろいろ試して探してみる。（$s$を１ずつ増やしてやってみるのが得策）
>このとき[3]から少数第１位は５だとわかっている（頭でわかっているだけで数学的に示されたわけではない）ので、不等式を変形した結果```<1.6```となればゴールイン。
>実際に$s$を変化させて$t$を決め、不等式評価してみると...
> <dl>
>   <dt>$s=2$</dt>
>   <dd>$\log_2 3^2 < \log_2 2^4\ \ \Leftrightarrow\ \ \log_23 < 2$　ガバガバ</dd>
>   <dt>$s=3$</dt>
>   <dd>$\log_2 3^3 < \log_2 2^5\ \ \Leftrightarrow\ \ \log_23 < 1.666 \cdots < 1.7$　惜しい</dd>
>   <dt>$s=4$</dt>
>   <dd>$\log_2 3^4 < \log_2 2^7\ \ \Leftrightarrow\ \ \log_23 < 1.75$　まだダメ</dd>
>   <dt>$s=5$</dt>
>   <dd>$\log_2 3^5 < \log_2 2^8\ \ \Leftrightarrow\ \ \log_23 < 1.6$　OK!!</dd>

[3]式と[4]式から$1.5 < \log_23 < 1.6$であるため、$\log_23$の少数第１位は５。


# おまけ

まぁ、入試問題にせずともコンピュータに計算させちゃえば一発なんですけどね

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/820696/a504229c-6470-1e16-1c53-5028254db628.png)

[高精度計算サイト](https://keisan.casio.jp/exec/system/1260332465)



# ふりかえり

- (2)で$p\log_23 - q\log_23 = k$ (0以外の整数)とすると、最後の分数の分母が負の数になる可能性があり(1)の結果から結論を導けないというところで減点されてしまいます。
- 不等式評価はいろんな問題で出るから、マスターするとメチャ強い。
Triggerを使用して履歴テーブルを作ってみる

備忘録として後世の自分に託す

## はじめに

SQL Server データベースプロジェクトで作成する  
履歴を残すテーブルを作成する  

例：Departmentテーブル  

```sql
CREATE TABLE [dbo].[Department]
(
  [Id] INT NOT NULL PRIMARY KEY,
  [DepartmentName] NVARCHAR(10) NULL
)
```

今回はDepartmentテーブルに対してinserted, update, deleteなどの操作が行われたときに履歴を残すためにTriggerを使ってみる  

## 履歴テーブルを作成する

まずは履歴を保存しておくテーブルを作っておく  

例：DepartmentHistoryテーブル  

``` sql
CREATE TABLE [dbo].[DepartmentHistory]
(
  [Id] INT NOT NULL PRIMARY KEY,
  [DepartmentName] NVARCHAR(10) NULL,
  [Action] NVARCHAR(10) NULL,
  [UpdateDate] DATETIME2 NULL,
)
```

- [Action]：どんな操作が行われたかを保管する列
- [UpdateDate]：更新が行われ時間を保管する列

## Triggerを作成して履歴を残す処理を実装する

Triggerを追加する  
今回は一例としてDepartmentテーブルに対してUPDATEが走ったときに履歴テーブルへとデータをINSERTするような処理を書いてみます。  

例：DepartmentUpdateTrigger  

```sql
CREATE TRIGGER [DepartmentUpdateTrigger]
  ON [dbo].[Department] // どのテーブルに対して？
  FOR UPDATE // どんな操作が行われたら？
  AS
  BEGIN
    INSERT INTO
        [dbo].[DepartmentHistory](
          [Id],
          [DepartmentName],
          [Action],
          [UpdateDate]
      )
        SELECT
          [Id],
          [DepartmentName],
          'UPDATE',
          GETUTCDATE()
        FROM
          inserted
  END
```

- FOR: 今回の例ではUPDATEだが、INSERT、DELETEなどでも同じように描ける。
- inserted: insertedテーブルからSELECTして履歴テーブルへと登録している。
  - もしDELETE文がトリガーならば、ここはdeletedで実際にdeletedしたデータがテーブルとして表されている。  

以上によりDepartmentテーブルに対してデータが更新されるとDepartmentHistoryテーブルに更新が走るので試してみてください。

## まとめ

爆速で履歴作れたのでいい感じだと思いました。  
データベースの知識が乏しく感じるのでもっと勉強せねばならない。
小型 BashScript 集


# はじめに

Qiita に投稿した小型の BashScript 集.
投稿が新しいものほど先頭に来るようにしている.

|コンテンツ|一言|
|:--|:--|
|[[sed]【一行野郎】「コメント『#』のみ」「空行」「スペースのみ」を取り除く](https://qiita.com/robozushi10/items/5addd649fdabe24a9833)| sed の <font color=#449944>削除</font> 処理|
|[【一行野郎】コロン「:」区切りによる最短一致と最長一致の取り出し](https://qiita.com/robozushi10/items/1e56eece082643fb58da)||
|[[03] Jenkins の実行済み全ジョブのコンソールログを PlainText で取得する](https://qiita.com/robozushi10/items/62f31ec80a7cdef405f4)|Jenkins API操作|
|[[02] Jenkins API を使って実行済みジョブのジョブ番号を取得する](https://qiita.com/robozushi10/items/53b0bf8aceb8f7a4e11f)|Jenkins API操作|
|[[01] Jenkins API を使って全ジョブの URL を取得する](https://qiita.com/robozushi10/items/e40b76ace457ae894151)|Jenkins API操作|
|[【1行野郎】最新の chromedriver をダウンロードする [2021/08/25確認済み]](https://qiita.com/robozushi10/items/6f4d963baa15457fcae2)||
|[【1行野郎】行頭「#」と空白のみの行を削除して表示させる](https://qiita.com/robozushi10/items/517fa7e2b0e1c1af2751)||
|[scpを毎回自動でパスワード入力して実行する](https://qiita.com/robozushi10/items/75e44523115290776ffa)||
|[【1行野郎】ldd で表示されるライブラリの合計サイズを算出する](https://qiita.com/robozushi10/items/c37ce0be48ce215ded26)||
|[【1行野郎】あるファイルに対して、fzfで絞り込みしてからCtrl-kでクリップボードにコピーする](https://qiita.com/robozushi10/items/70d4295254e14c366d54)|2017年ころに fzf + locate 相当の CLIツールを作ったときの処理の一部である|
|[【一行野郎】コンソール表示しつつ xclip でクリップボードにもコピーする](https://qiita.com/robozushi10/items/b4f1b5ffa839a9de9148)||
|[【1行野郎】複数パスを対象にディレクトリを列挙するが、特定ディレクトリのみ探索しない２](https://qiita.com/robozushi10/items/1f46a28d687e282cd238)|2017年ころに fzf + locate 相当の CLIツールを作ったときの処理の一部である|
|[【1行野郎】複数パスを対象にディレクトリを列挙するが、特定ディレクトリのみ探索しない](https://qiita.com/robozushi10/items/31579e659b60efe54e75)||
|[【1行野郎】Elasticsearch レスポンスの「green」「yellow」「red」を色付けする](https://qiita.com/robozushi10/items/791ed44a6d9fe4759de1)||
|[【1行野郎】できる限り速くファイルに列挙したデータをアーカイブにする](https://qiita.com/robozushi10/items/4645ec7c760743dfc268)||
|[【1行野郎】Bash ShellScript で URLエンコードとデコードをする](https://qiita.com/robozushi10/items/550e7c3c54d7ce01a737)||
|[【BashでJSON】JSONファイルそのものをPOSTする](https://qiita.com/robozushi10/items/11fca399af05a8ab72c2)|
|[【BashでJSON】複雑なJSONデータをPOSTする](https://qiita.com/robozushi10/items/863dd124c60694957167)|||
Apexトリガ二重起動抑止パターンと一括レコード処理時の破綻ケースについて

Apexトリガにおいて、ワークフローやプロセス、およびその他のトリガによる再帰的な処理の発生を防ぐために、Apexトリガの二重起動抑止(=リエントランス制御)をコードに組み込むパターンがままある。

以下、代表的なコード。

```java:SampleTriggerHandler.cls
public with sharing class SampleTriggerHandler {
  public static boolean triggerCalled = false;
  public static void handleBeforeUpdate(List<SampleObject__c> recs) {
    for (SampleObject__c rec: recs) {
      // 何らかの処理
    }
  }
}
```

```java:SampleTriger.trigger
trigger SampleTrigger on SampleObject__c (before update) {
  if (!SampleTriggerHandler.triggerCalled) {
    SampleTriggerHandler.triggerCalled = true;
    SampleTriggerHandler.handleBeforeUpdate(Trigger.new);
  }
}
```

このようなトリガが設定されている場合、一括でのレコード処理を実施可能な環境下では不具合あるいはシステムの破綻をきたす可能性がある。

[一括 DML 例外処理](https://developer.salesforce.com/docs/atlas.ja-jp.apexcode.meta/apexcode/apex_dml_bulk_exceptions.htm)

> デフォルト設定で SOAP API から発生した一括 DML コールが原因でエラーが発生した場合、または Database DML メソッドの allOrNone パラメータが false に指定されている場合は、ランタイムエンジンが少なくとも部分的な保存を試みます。
> 最初の試行で、ランタイムエンジンはすべてのレコードを処理します。入力規則や独自のインデックス違反などの問題によるエラーを生成したレコードは、除外されます。
> 1. 最初の試行でエラーが生じた場合、ランタイムエンジンは、エラーを生成しなかったレコードのみを含む 2 回目の試行を行います。
> 2. 最初の試行でエラーを生成しなかったすべてのレコードが処理され、競合の条件などが理由でエラーを生成したレコードがあれば、それも除外されます。
> 3. 2 回目の試行中に追加エラーがあった場合、ランタイムエンジンは、初回と 2 回目にエラーを生成しなかったレコードのみを含む 3 回目 (最後) の試行を行います。エラーを生成したレコードがある場合、操作全体は失敗し、エラーメッセージ「Too many batch retries in the presence of Apex triggers and partial failures (Apex トリガと部分的な失敗がある場合にバッチ試行の回数が多すぎます)」が表示されます。

つまり、一括処理対象の複数レコードの中に（入力規則チェック等で）エラーとなるレコードを含む場合、エラーとなるレコード以外の正常処理されたレコードに対しては２回目のDML処理が実行される。

上記のようなstatic変数によるフラグでの二重起動抑止が組み込まれている場合、2回目のDMLではすでにトリガの呼び出しフラグがセット済みであるため、二重起動抑止がされ、トリガハンドラのコードが実行されないまま正常系のレコードが保存される。これにより、保存時に必ずトリガが動作することを期待しているシステムは破綻した状態となる。

自分が試したPoCのコードは以下のレポジトリにおいておく。
https://github.com/stomita/dml-trigger-allornone-test

これらの詳細について解析したもの。
https://github.com/KrishnaKollu/poc-approaches-salesforce-trigger-recursion

> In a nutshell, leveraging just static variables to manage trigger recursion is unsafe and could result in your trigger not firing consistently.

例えば、データローダーなどは動作的に`allOrNone=false`の扱いとなっているかとおもうので、該当トリガを含むオブジェクトが一括ロードを行う対象となっている場合は顕著に発生する。

最近はリストビュー以外にもUIから一括で更新を行うツール類が増えてきているため、開発時に標準詳細画面からの単票更新のみを想定していると、このような問題に後から直面することになる。

残念ながらこれといって王道の回避方法があるわけではなく、可能であれば二重起動抑止制御を行わないで済むようなシンプルなトリガ構成としておくのが最良と思われる。ApexでSalesforceの開発を行う開発者におかれましてはぜひ注意されたい。