OmniAICreator/Qiita-1.07M huggingface dataset:
iOS14で動画再生中、バックグラウンド移行時に一時停止されてしまう問題への対応

# 対応

* **`playerView.player = nil`**
* **`playerView.player = player`**

**の実行タイミングを変更する**

```
@objc func applicationWillResignActive(_ notification: NSNotification?) {
   playerView.player = nil
}

@objc func applicationDidBecomeActive(_ notification: NSNotification?) {
   playerView.player = player
}

```

# 経緯

* ユーザー「iOS14にアップデートしたらバックグラウンド再生がおかしい。なんとかしろ:rage::bangbang:」
* ワタクシ「:sob:」

# 対応前

* `applicationDidEnterBackground` で `playerView.player = nil` していた　※Appleさんの公開しているドキュメントの記載内容に従ったもの

# 調査

* 何もしてないのに動かなくなった　⇒　OSの仕様変わったな？
* バックグラウンドに行ったら一時停止　⇒　layerにplayer設定したままの動作と同じだな？

# 検証

1. 動画再生中、バックグラウンド移行前に、手動で `playerView.player = nil` する
2. バックグラウンドへ移行する

⇒**「再生が継続された！」**

導き出される結論は…
**「layer に player があるかどうかチェックしているタイミング変わったんじゃね？」**

# 再検証

1. `applicationDidEnterBackground` より前に実行される **`applicationWillResignActive`** で `playerView.player = nil` するよう変更
2. `applicationWillResignActive`に対となるのが **`applicationDidBecomeActive`** となるため、そこで `playerView.player = player` するよう変更

⇒**「再生が継続された！」**

※iOS11-14、iPadOS13,14で動作することも確認しました

# まとめ

* Appleさんのドキュメントのままに書いても動作しないため、OS不具合の可能性が高いです
    * iOS 14.2 beta2 で、この不具合が修正されているとの噂です

# その他

* iOS14がリリースされてしばらく経ちましたが、この対応方法を見かけなかったため記事を投稿します

# 参考文献

1. Apple : [Playing media while in the background using AV Foundation on iOS](https://developer.apple.com/library/archive/qa/qa1668/_index.html)
2. Apple : [Playing Audio from a Video Asset in the Background](https://developer.apple.com/documentation/avfoundation/media_playback_and_selection/creating_a_basic_video_player_ios_and_tvos/playing_audio_from_a_video_asset_in_the_background)
3. @KenNagami : [iOSアプリのライフサイクル](https://qiita.com/st43/items/81c7f9ae79126a01ae51)
4. @bosteri_bon : [iOS14で「バックグラウンド再生」が出来ない不具合への対処](https://qiita.com/bosteri_bon/items/2c93822b39ca725d5055)
zabbix5.0をRaspbianに構築する手順

# 作業環境
| OS・ハード | バージョン |
|:--|:--|
| Raspberry Pi | 3 Model B + |
| Raspbian | Buster |

# nginxをインストールする

```
$ sudo apt-get install nginx
$ sudo systemctl start nginx
```

# php-fpm
php-fpmのインストール

```
$ sudo apt install php7.3-fpm php7.3-mysql
```

コメントアウトの解除
※php-fpm.sockは該当バージョンを指定

```
$ sudo vi /etc/nginx/sites-available/default
location ~ \.php$ {
  include snippets/fastcgi-php.conf;
  # With php-fpm (or other unix sockets):
  fastcgi_pass unix:/run/php/php7.3-fpm.sock;
  # With php-cgi (or other tcp sockets):
  #fastcgi_pass 127.0.0.1:9000;
}
```

php.iniの設定を修正

```
$ sudo vi /etc/php/7.3/fpm/php.ini
cgi.fix_pathinfo=0
```

# mysql(mariadb)
mysqlのインストール

```
$ sudo apt install mariadb-server
$ sudo mysql_secure_installation
```

zabbix用のDBを準備

```
$ mysql -uroot -p
password
mysql> create database zabbix character set utf8 collate utf8_bin;
mysql> create user zabbix@localhost identified by 'password';
mysql> grant all privileges on zabbix.* to zabbix@localhost;
mysql> quit;
```

# ZABBIX
zabbixのインストール

```
wget https://repo.zabbix.com/zabbix/5.0/raspbian/pool/main/z/zabbix-release/zabbix-release_5.0-1+buster_all.deb
dpkg -i zabbix-release_5.0-1+buster_all.deb
apt update
apt install zabbix-server-mysql zabbix-frontend-php zabbix-nginx-conf zabbix-agen
zcat /usr/share/doc/zabbix-server-mysql*/create.sql.gz | mysql -uzabbix -p zabbix
```

DBパスワードの設定

```
$ vi /etc/zabbix/zabbix_server.conf
DBPassword=password
```

nginx用のconfファイルを修正

```
$ vi /etc/zabbix/nginx.conf
listen 80;
server_name zabbix.;
```
整数に3桁区切りカンマを入れる (JavaScript)

# 結果

処理前

```
123456789
```

処理後

```
123,456,789
```

# 方法

## 方法1

toLocaleStringを使う
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Number/toLocaleString

## 方法２

NumberFormatを使う
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat

## 方法3

humanize を使う
https://github.com/HubSpot/humanize

# 実装


## 実装方法2

```js
var number = 3500;

console.log(new Intl.NumberFormat().format(number));
// → '3,500' 英語(U.S.)ロケールの場合
```


## 実装方法3

### 準備

```sh
yarn add humanize-plus
```

### 実装

```js
import Humanize from 'humanize-plus'

let r = Humanize.intComma(123456789)
console.log(r) // 123,456,789
```
docker-composeのnode_modules問題にぶちあったたときにやった対処方法

# どんな問題？
最近、`docker-compose`で`node`の環境を構築するとき、コンテナ内の`node_modules`がどっかに行ってしまうという問題に遭遇した。

いろいろ調べてみると、`docker-compose`でローカルのディレクトリをコンテナ内にマウントする際に、上書きされてしまい、コンテナ内の`node_modules`が消えてしまっているようだ。

# どう解決したの？
https://qiita.com/suin/items/e53eee56da23d476addc の記事などを見ると、`volumes`を作成し、`node_modules`をそこにマウントすることで、回避することができるようだ。
しかし、これだとローカル環境に`node_modules`が生成されないため、`TypeScript`でパッケージの型定義を参照できず、`TypeScript`を使うことの旨味が薄れてしまう。

この問題を解決するために、`docker-compose`の`command`で`yarn install`もすることで、コンテナ内の`node_modules`をローカルにマウントされるようにして、ローカルに`node_modules`を持ってくるようにしている記事を見た。
だが、この方法だと`docker-compose up`をするときに`yarn install`が走ってしまうため、`docker-compose up`でまあまあな時間がかかってしまった。（ローカルにコンテナ内の`node_modules`を持ってくるのにもめちゃめちゃ時間がかかっているようだった）

そこで、以下のような方法で解決をするようにした。（この解決方法がBadであれば、教えていただけると勉強になります）

```
.
├── app
│   ├── Dockerfile
│   └── src
│       ├── package.json
│       └── yarn.lock
└── docker-compose.yml
```

```dockerfile:Dockerfile
FROM node
WORKDIR /app
ADD src/package.json /app/
ADD src/yarn.lock /app/
RUN yarn install
```

```yaml:docker-compose.yml
volumes: # ←追加
  app_node_modules: # ←追加
services:
  app:
    image: ./app
    volumes:
      - ./app/src:/app/
      - ./app/src/node_modules:/app/node_modules # ←追加
# 以下略
```

ここまでは https://qiita.com/suin/items/e53eee56da23d476addc の記事と同じだ。
これにプラスで、パッケージを追加したときに以下のスクリプトを実行すること（`docker-compose build`の代わり）で、ローカルにも`node_modules`を生成するようにした。

```sh:build.sh
docker-compose build
cd app/src && yarn install
```

とりあえずこれで、`node_modules`が消えてしまう問題と型定義がない問題を解決することができた。
しかし、ローカル環境に`node`があることが前提になってしまっているため、もっといい解決方法があればいいな〜と思っている。（が、わからなかった）

# おわりに
いい解決方法があればぜひ教えていただけると嬉しいです。
Symbol from NEMのSDKを利用して暗号と署名検証を行う方法

今回はsymbol-sdkを利用してブロックチェーンを使わずに暗号化と署名検証を使う方法を説明します。
（※sdk非推奨の方法を記載していたのでコメントアウトしました）


# 事前準備

### スクリプト埋め込み
```js

(script = document.createElement('script')).src = 'https://xembook.github.io/nem2-browserify/symbol-sdk-pack-0.21.0.js';
document.getElementsByTagName('head')[0].appendChild(script);
```
chromeのコンソールを開いてsymbol-sdkのスクリプトを埋め込んでください。

### ライブラリインポートとアカウント生成
```js
nem = require("/node_modules/symbol-sdk");

alice = nem.Account.generateNewAccount(nem.NetworkType.TEST_NET);
bob = nem.Account.generateNewAccount(nem.NetworkType.TEST_NET);
```
今回の暗号化や署名に使用するライブラリのインポートとアカウントを生成しておきます。

# 暗号化

```js
message = 'Symbol from NEM!';
//encodedMessage = nem.Crypto.encode(alice.privateKey,bob.publicKey,message);
encryptedMessage = alice.encryptMessage(message ,bob.publicAccount);

```
送信者の秘密鍵、受信者の公開鍵、メッセージの順に指定します。
受信者の公開鍵でメッセージを暗号化する、という点がポイントです。

> "294C8979156C0D941270BAC191F7C689E93371EDBC36ADD8B920CF494012A97BA2D1A3759F9A6D55D5957E9D"

暗号化できました。

#復号化
```js
/*
decodedHex = nem.Crypto.decode(
  bob.privateKey, 
  alice.publicKey,
  "2561a6aa1c5ea51b3971fc907d28678db6d28c568a30249541eb9d411eced23ee0f432a58459acb741f7b963"
)
nem.Convert.decodeHex(decodedHex)
*/
bob.decryptMessage(
  new nem.EncryptedMessage(
    "294C8979156C0D941270BAC191F7C689E93371EDBC36ADD8B920CF494012A97BA2D1A3759F9A6D55D5957E9D"
  ),
  alice.publicAccount
).payload
```
受信者の秘密鍵、送信者の公開鍵、暗号化メッセージの順に指定します。
受信者の秘密鍵で復号化するという点がポイントです。
出力されたHEX文字をデコードします。

> "Symbol from NEM!"

無事復号化されました。

# 署名

```js
payload = nem.Convert.hexToUint8(nem.Convert.utf8ToHex("Symbol from NEM"));
signature = nem.Convert.utf8ToHex(nem.KeyPair.sign(alice.keyPair, payload));
```
署名したいメッセージをHEX変換してUint8Arrayに変換します。
署名したいアカウントのキーペアを指定して署名、HEX変換します。
メッセージを作成したアカウントの秘密鍵で署名するというのがポイントです。

> "7FCF7614D60BF4506DEC7D1B55AA1A0972881FA8FC40D0D0FBAAAF77E036E7988270E57631D49FCE253C4FB992E17C9360FA948B6626B2954E7DAB84DA7D6E01"

署名テキストが出力されました。

# 検証
```js
nem.KeyPair.verify(
  alice.keyPair.publicKey,
  nem.Convert.hexToUint8(nem.Convert.utf8ToHex("Symbol from NEM")),
  nem.Convert.hexToUint8( 
 "7FCF7614D60BF4506DEC7D1B55AA1A0972881FA8FC40D0D0FBAAAF77E036E7988270E57631D49FCE253C4FB992E17C9360FA948B6626B2954E7DAB84DA7D6E01")
)
```
検証したいアカウントの公開鍵、検証したいメッセージ、署名テキストの順で指定します。
メッセージを作成したアカウントの公開鍵で検証する、というのがポイントです。

> true

無事、trueが出力されました。


このように、暗号、復号、署名、検証はブロックチェーンと切り離して使うことができます。場合によってはこれらの出力結果をメールでやりとりして信頼性検証を行うこともできます。


では、ブロックチェーンを使うことの意味とは何でしょうか？
このことを、ぜひ一度振り返って考えてみるきっかけにしていただければ幸いです。
groupbyではなくpivot_tableで集計する

#groupbyで集計

groupbyで集計すると、下のようになるデータがあったとする。

![2020-10-15 (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/791293/3ff982fa-d45c-acb3-634e-6b3192b60185.png)

しかしこの表、「PC-Aのquantityを異なる月毎に比較したい」なんて時には非常に見づらい。

これを解決するには、例えばindexにitem_name、columnにpayment_monthを指定すればいい。
そのためには、pivot_tableを使った集計を活用しよう。

#pivot_tableで集計
![2020-10-15 (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/791293/4474e550-8194-5b30-a2f8-110cf07133e1.png)

グラフによる可視化もラクチンである。

![2020-10-15 (3).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/791293/611db6f7-5589-415c-2f9f-b4ba3d236e01.png)
Docker[<none>タグの一括削除]

#Dockerの<none>タグを一括削除する！
Dockerを触ってるとこんな感じで不要なimage(**noneタグ**)がいっぱい溜まりますよね〜

```
$docker images
REPOSITORY                TAG                 IMAGE ID            CREATED             SIZE
portfolio_api             latest              9091a77d3e50        3 minutes ago       661MB
<none>                    <none>              bda133c88b3a        3 hours ago         659MB
portfolio_front           latest              206c69e39bc8        5 hours ago         325MB
<none>                    <none>              c9c1cc1f82af        3 days ago          325MB
<none>                    <none>              90b26537b543        3 days ago          659MB
rails_nuxt_docker_front   latest              6a76df1e3e15        8 days ago          652MB
rails_nuxt_docker_api     latest              f81ab2aa1da3        9 days ago          664MB
node                      14.11.0-alpine      b85fc218c00b        4 weeks ago         117MB
ruby                      2.7.1-alpine        b46ea0bc5984        3 months ago        52.3MB
postgres                  12.3-alpine         17150f4321a3        3 months ago        157MB
node                      14.4.0-alpine       3bf5a7d41d77        4 months ago        117MB
```
こいつらが溜まるとGBが喰われて非常に厄介なのです。

```
$ docker rmi <IMAGE ID>
```
でチマチマ消すのも面倒なので一括でnoneタグを取得して削除しましょう！

## noneタグのみ抽出

- `$docker images`に `-f`オプションをつけると検索フィルターをかけることができます
- `'dangling=true'`でnoneタグ(dangling image)を指定できます

```
$ docker images -f 'dangling=true'
REPOSITORY                TAG                 IMAGE ID            CREATED             SIZE
<none>                    <none>              bda133c88b3a        3 hours ago         659MB
<none>                    <none>              c9c1cc1f82af        3 days ago          325MB
<none>                    <none>              90b26537b543        3 days ago          659MB

```

## 本題＜noneタグ＞の一括削除
上記のオプションを`$docker rmi`にぶち込みましょう！！！

```
$docker rmi $(docker images -f 'dangling=true')
Deleted: sha256:bda133c88b3a3aca1cdd971ffe23a2bea32295e782d1225b7753151f47c40e3d
Deleted: sha256:99c5e34963f7b0ae63427b9e24b1d66778daeda863c078f46476ea9229f17771
...
```
ArrayListとLinkedListの違い

先日ArrayListとLinkedListの違いわかるかと聞かれて、答えることができなかったので備忘録として記事します。

## ArrayListとは
ArrayListは、内部的には配列を利用したリスト構造です。標準的な配列と異なる点は、サイズを後からでも変更できる点です。その性質上、インデックス値による値の読み書きは高速ですが、要素の挿入／削除は、配列サイズが大きくなるほど、また、操作位置が先頭に近くなるほど遅くなります。

・メリット
要素がそれぞれ順序番号を持っており、全てメモリ上でインデックス化されているため、特定の要素へすぐにアクセスすることが出来ます。

・デメリット
末尾意外の要素を足したり引いたりすると、それ以降の要素全ての順序番号を繰り上げたり繰り下げたりするための再配置処理が行われ、
その処理により ArrayList 内の要素には全て 0 から 全要素数から 1 引いた数までの全ての順序番号が付けられた要素がキレイに並び続けるのですが、処理が行われる分時間がかかってしまいます。
そしてその処理数は要素数に比例して多くなります。

| メソッド | 概要 |
| ---------- | ----------- |
| add([int index,] E e)    | 指定位置indexに要素eを挿入（index省略で末尾に挿入）     |
| clear()     | リストからすべての要素を削除      |
| contains(Object e)	     | リストに要素oが含まれているかを判定  |
| get(int index)         | be          |
| indexOf(Object e)	      | 要素oが登場する最初のインデックス値を取得      |
| isEmpty()	    | リストが空か判定     |
| remove(int index｜Object o)	    | 指定の要素を削除     |
| set(int index, E e)		    | index番目の要素を設定int size()	   |
| int size()	    | リストに含まれる要素の数を取得     |



## LinkedListとは
LinkedListは、要素同士を前後双方向のリンクで参照するリンクリストを表します。その性質上、要素の挿入／削除はリンクの付け替えで済むため、ArrayListに較べても高速です。反面、インデックス値によるランダムなアクセスは苦手であるという性質を持ちます。このような理由から、挿入／削除操作が多い状況ではLinkedListを、それ以外の場合はArrayListを使う、という使い分けになるでしょう。
LinkedListクラスで利用できるメソッドは、ArrayListと同じですので、そちらも合わせて参照してください。以下は、LinkedListで要素をセットし、その内容を順に読み込む例です。

・メリット
要素を足したり引いたりする際は、リンク情報を書き換えれば終わりなので、再配置処理が行われない分 ArrayList よりも高速です。

・デメリット
それぞれの要素は順序番号を保持していないため、特定の要素を取り出す際は、先頭もしくは末尾から一つずつ順序を数えていく必要があるため、順序番号があらかじめ保持されている ArrayList に比べると、それだけ多くの時間がかかります。

## 結論
ArrayListは要素に対してランダムなアクセスを必要とし、配列内の要素に対して挿入／削除の操作があまり必要ない場合に利用。
LinkedListは末尾の要素以外に追加/削除処理を頻繁に必要とし、特定の要素に対するアクセスが必要としない場合に利用。

## 参照
[LinkedList と ArrayList の使い分け方](https://qiita.com/BumpeiShimada/items/522798a380dc26c50a50)
[ArrayListクラス](https://java-code.jp/236)
Java 初めての例外処理（備忘録）

#Java練習
Javaで開発をすることになり現在Javaを勉強中
[スッキリわかるJava入門 第2版 スッキリわかるシリーズ](https://www.amazon.co.jp/dp/B00MIM1KFC/ref=dp-kindle-redirect?_encoding=UTF8&btkr=1)を使ってJavaの勉強。

2章2-3のアプリをアレンジしてみた。

## 2章2-3のアプリをアレンジ
[dokojavaを使用してブラウザでJavaを実行](https://dokojava.jp/execute)

```java:Main.java
import java.util.Scanner;
import java.util.Random;

public class Main{
  public static void main(String[] args) {
    System.out.println("ようこそ占いの館へ\nあなたの名前を入力してください");
    String name = new Scanner(System.in).nextLine();
    System.out.println("あなたの年齢を入力してください。");
    String ageString = new Scanner(System.in).nextLine();
    if (isNumber(ageString)){
      int age = Integer.parseInt(ageString);
      int fortune = new Random().nextInt(4) + 1;
      System.out.println("占いの結果が出ました。");
      System.out.println( age + "歳の" + name + "さん、あなたの運勢は" + fortune + "です");
      System.out.println("1:大吉、２：中吉、３：吉、４：凶");
    } else {
      System.out.println("年齢が数字ではありません");
    }
  }
  static boolean isNumber(String num) {
    try {
      Integer.parseInt(num);
      return true;
    } catch (NumberFormatException e) {
      return false;
    }
  }
}
```

```java:Main.java
import java.util.Scanner;

public class Main {
	public static void main(String[] args) {
	    System.out.println("ようこそ\n数字を入力してください");
        String s = new Scanner(System.in).nextLine();
        try {
            //例外が発生する可能性のある処理
            int i = Integer.parseInt(s);
            System.out.println(i);
        } catch (NumberFormatException e) {
            //例外が発生した場合の処理(例外が発生しなければ行われない処理)
            System.out.println("ちゃんと数字を入力してください!");
        }
    }
}
```

###例外処理とは？

プログラムの実行中に，プログラムが予想していなかった事態が起きることがあります

ざっくり言うと例外処理とは，上で述べたような異常事態（例えば想定したデータ型と違う）の発生を通知するための仕組みです．

異常事態の発生を検知したメソッド（例えばparseInt）は例外を「投げ」ます。

これにより異常事態が発生したことを知らせます。

Javaの用語では「投げる」ことを「スロー(throw)する」と言います。

異常事態が発生したメソッドを呼び出した側のメソッドでは，投げられた例外を「捕える」ことができます．捕まえれば，適切な処理を行うことができます(同じく捕まえることを「キャッチ(catch)する」と言います)

##参考にした記事（いつもありがとうございます。）

- [プログラミング入門(例外処理)](http://www.media.osaka-cu.ac.jp/~matsuura/2008-PP/Exception.html)
- [【Java】try-catchで例外処理を実装しよう！Exceptionクラスの使い方](https://www.sejuku.net/blog/26344)
Cookiecutterを使って爆速でiOSプロジェクトを作成する


![cookiecutter_medium.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6691/6fa8fca6-d843-a740-d6b7-1f3fb3fcf98a.png)

## はじめに

あなたは開発スタート時に毎回新しいXcodeプロジェクトを作っていませんか？
過去のプロジェクトから使ってもいいヤツらを 「Add Files to」していませんか？
xcconfigやcarthageの設定を毎回行っていませんか？

そうです、まさに私のことです。

というわけで、開発初期のスピードを加速させるべく
[Cookiecutter](https://github.com/cookiecutter/cookiecutter) というPythonのツールを利用して `コマンド一発`で
iOSプロジェクトを作成している例があったので真似してみることにしました。

## クイックスタート

サンプルとしてCleanArchitectureでの開発用テンプレートを作成してみました。
https://github.com/SatoshiN303/iOS-Blueprints-CleanArchitecture

```bash
$ brew install cookiecutter
$ cookiecutter https://github.com/SatoshiN303/iOS-Blueprints-CleanArchitecture.git
```

上記のコマンドを叩くと 「アプリ名」 「会社名」 「バンドルID」が聞かれます。
それぞれを入力するとプロジェクト作成完了です。
![スクリーンショット 2020-10-14 18.35.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6691/ea66cefa-b65c-5164-7642-e8449c53b218.png)

## 生成されるサンプルプロジェクトについて
* bootstrap.sh
* .xcconfig (debug/adhoc/release)
* CocoaPods Settings
* Carthage Setting
* no Main.storyboard
* DependencyInjection SampleCode
* Github's Repositry search process SampleCode

テンプレートの基本的な内容として上記を設定してみました。
普段通りに実装したものをCookiecutterテンプレートとして転用できるのは便利です。


ちなみに開発時には削除することになりますが
GitHubAPIを叩いて画面に表示するサンプルコードを追加しています。
理由としてCleanArchitectureで誰が何の責務を持っているか把握する為になります。
![スクリーンショット 2020-10-15 0.03.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6691/062e80df-6d86-1bfa-4f38-53d9b0ff06a1.png)

## Cookiecutterについて

主に機械学習向けのPythonパッケージやDjango等の
プロジェクトテンプレートを作成することができるツールです。

[ドキュメントはこちらです。](https://cookiecutter.readthedocs.io/en/1.7.2/)

[cookiecutter-template](https://github.com/topics/cookiecutter-template) を確認すると様々なテンプレートが公開されています。
まずは Forkして カスタマイズするのも有かなと思います。

[Android向け](https://github.com/general-mobile/kotlin-android-mvvm-starter)や[iOS向け](https://github.com/topics/cookiecutter-template?l=swift)もそれなりにありました。
ちなみにローカルでも利用可能でした。

```
# Create project in the current working directory, from the local
# cookiecutter-pypackage/ template
$ cookiecutter cookiecutter-pypackage/
```

## 補足: Xcodeのプロジェクトテンプレートを使わなかった理由

Xcodeのオレオレプロジェクトテンプレート作成も試みたのですが
以下の理由で色々と面倒だったので cookiecutter を使う感じになりました。

- TemplateInfo.plist をガッツリ弄るのが億劫
- xcconfig とか BuildSettingsも設定したい

Xcodeの自作プロジェクトテンプレートに詳しい方などいらっしゃいましたら
コメント頂けると幸いでございます🙇‍♂️

## 今後やりたいこと
- 再利用できるものはEmbedded Framework化した上でテンプレートに含める
- 他のアーキテクチャも作りたい
- テンプレートプロジェクト自体をCIで回してテンプレートの形骸化を防ぎたい
- シェルスクリプト使ってAWS Amplifyも合わせて設定する

## まとめ
Cookiecutter向けに自分好みのテンプレートを作って開発初期スピードを加速させよう

## 参考
* [これでもう悩まない！機械学習のためのフォルダ構成テンプレートを使おう。](https://qiita.com/qmiyajun/items/5039b97a159b9f521e49)
* [Rightpoint/ios-template](https://github.com/Rightpoint/ios-template)
* [【2020年版】Xcodeのファイルテンプレートを自作する](https://note.com/shu223/n/ndcf7e7eade7c)
SendGrid/Rails/Heroku 設定(個人的なメモ)

# 概要
Heroku の SendGrid アドオンを設定に関して。
流れとしては、
1.SendGridアドオン追加する
2.SendGrid側のダッシュボードからAPIキーを発行する。(Velify Integrationはこの時点ではせずに画面待機）
3.HerokuにAPIキーを設定する
4.Heroku にdeployしたRailsアプリからからメール送信
5. SendGridの"Verify Integration" を実行し、検証を完了させる。

### ハマるポイント

#### 認証エラーの件
「そもそもアドオンが追加できない" / SendGrid側の画面に飛ぼうとすると認証エラー」でアクセスできない。
こちらは、heroku側のサポートに問題を報告して対応してもらうことで解決した。
(最初SendGrid側にサポートに連絡していたが、そちらでは上手く解決できなかった）

・認証エラーになりSenGrid側の画面に飛べないことを伝える
・問い合わせの優先度つけを行う質問が届くのでそれに応える。(テスト系ですかみたいな簡単なもの）
・数日おいたのち、heroku側からリアクティベートするために、利用目的やメアドの管理方法などの数個の質問に答えてくださいメールが送られてくる。
・質問に答えると、数日でアクティベートされた旨ののメールが届きアクセスできるようになる。
(対応には時間がかかるので、ゆとりを持ちつつ、１週間程度動きがなさそうなら、まだ問題は解決されてません的な、伺いメールを出す）


#### SendGrid側のVerify Integration

APIキーを作成後に下記を実行しようとする。
Send an email from your application using the code you just integrated. If that runs without error, click "Verify Integration".
なかなか、チェックが完結しない。画面をそのまま保持した上でテストメールの送信を試みる。
※ この時点で、すでに、APIキーは発行されているのでheroku上に設定して、Rails側から送信を行うと送信された。
メールが１通送られたのち再度、Verify Integration　すると今度は検証がOKになった。


#### SndGrid側のAPIキー発行の流れ

##### 画面１
![Screen Shot 2020-10-14 at 23.36.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/678939/13fd7611-4176-4451-08c3-b048614d802c.png)

##### 画面2
![Screen Shot 2020-10-14 at 23.37.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/678939/2990b40e-5dd1-c411-63df-2880800769d0.png)
今回はSMTPで送る。
(Web APIを叩いて送る方が柔軟なので推奨）

##### 画面3
![Screen Shot 2020-10-14 at 23.38.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/678939/2ece70ca-2f97-6568-5c8e-7f91be33b8a9.png)
Key名をいれるて、作成ボタンを押下する。
ここでのKey名は、後ほどSengGridの管理ダッシュボードでの識別に使用する。
わかりやすい名前推奨。

##### 画面4
![Screen Shot 2020-10-14 at 23.41.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/678939/43fbfec2-75a2-c52d-7dda-ca5b3e2f01d1.png)
何かしらパスワードがくるので、これをコピー（忘れないように必ず）
herokuの環境変数などに設定して、メールを送信する際に参照して使用する。
この時点ですでにメールを遅れる状態になっているが、右下の、Nex Verify Integrationが上手くできなかった。
この画面を開いたまま、heroku上にプログラムをあげて、発行されたばかりのAPIKey使用してをテスト送信を1回行ったのち、
Nex Verify Integration　をすると正常に完了できた。
KotlinでMockitoのArgumentCaptorが使えない

なぜか日本語の情報が乏しかったのでメモしておきます。

## KotlinではArgumentCaptorが使えない!!
Javaの代表的なモックライブラリ[Mockito](https://site.mockito.org/)。いろんなことができるMockitoですがその中の一つに**モックのメソッドに渡したパラメータの検証を可能にする**`ArgumentCaptor`があります。

Mockito自体は当然Kotlinでも使えるのですが、ArgumentCaptorはKotlinでは使えない！！（場合が多い）

### エラーの例
具体的にはこんなコードで実行すると

```kotlin:テスト対象
class Sample {
    fun testTarget(message:String, value:Int) {
        println("%s%d".format(message,value))
    }
}
```

```kotlin:テストコード
@RunWith(PowerMockRunner::class)
@PrepareForTest(Sample::class)
class SampleTest {
    val instance = PowerMockito.mock(Sample::class.java)
    val messageCaptor: ArgumentCaptor<String> = ArgumentCaptor.forClass(String::class.java)
    val valueCaptor: ArgumentCaptor<Int> = ArgumentCaptor.forClass(Int::class.java)

    @Test
    fun test() {
        instance.testTarget("This is", 3)

        Mockito.verify(instance,Mockito.times(1)).testTarget(messageCaptor.capture(),valueCaptor
            .capture())

        Assert.assertEquals("This is", messageCaptor.value)
        Assert.assertEquals(1, valueCaptor.value)
    }
```

こんなエラーが出力されます。

```
java.lang.IllegalStateException: messageCaptor.capture() must not be null
```

これの原因はエラーメッセージにもあるように「**Non-NullableなパラメータにNullが入ったから**」ですね。

### KotlinのNull安全な仕様が裏目に……
そう、`ArgumentCaptor.capture()`は**Nullを返すのです**。JavaはパラメータのNullチェックをしないので問題ありませんでしたが、KotlinはNullチェックしてくれます。その優しさが裏目に出てしまったのです。

もちろんメソッドのパラメータをNon-Nullableにすれば実行できます。しかしそれでは本末転倒です。

## mockito-kotlinを使う
その解決策が[mockito-kotlin](https://github.com/nhaarman/mockito-kotlin)を使うことです。

mockito-kotlinの`capture()`を使うことでキャプチャしつつテストメソッドには値をそのまま渡すことができます。

```kotlin:修正後のテストコード
@RunWith(PowerMockRunner::class)
@PrepareForTest(Sample::class)
class SampleTest {
    val instance = PowerMockito.mock(Sample::class.java)
    val messageCaptor: ArgumentCaptor<String> = ArgumentCaptor.forClass(String::class.java)
    val valueCaptor: ArgumentCaptor<Int> = ArgumentCaptor.forClass(Int::class.java)

    @Test
    fun test() {
        instance.testTarget("This is", 3)

        //ArgumentCaptor.capture()は使わずにmockito_kotlin.capture()を使う
        //mockito_kotlin.capture()の引数はArgumentCaptorのインスタンスをそのまま入れれば良い
        Mockito.verify(instance,Mockito.times(1)).testTarget(capture(messageCaptor),capture(valueCaptor))

        Assert.assertEquals("This is", messageCaptor.value)
        Assert.assertEquals(3, valueCaptor.value)
    }
}
```

お手軽簡単ですね。

mockito-kotlinはMockitoをよりKotlinぽく使うためのライブラリのようですが、私のように「Javaで覚えた書き方そのまま使いたい」という面倒くさがり屋にも大変有用です。

## 参考
*[Mockito ArgumentCaptor for Kotlin function](https://stackoverflow.com/questions/38715702/mockito-argumentcaptor-for-kotlin-function "Mockito ArgumentCaptor for Kotlin function")*
Git 簡単に変更前の状態に戻す方法(コミット前)

#コミット前の変更点をターミナルを使わずに変更する

![5a8e0bb5a8678f4f589fba16c996ee70.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/725167/cdb5302d-f939-cbc8-ec1f-68caa9f27b03.png)

#他のブランチに移動するだけ！
##この方法が最も簡単に変更前に戻す方法なのですが、他の事例に当てはまる場合は下記の方の記事を参考にするといいですよ！
https://qiita.com/tani-shi/items/3419600447292abf6c79
@tani-shi

#現場からは以上です！
【React】mapのループ内で最後の要素にだけ処理を行う

## 概要

mapで繰り返ししている要素の最後の要素にだけスタイルを変えたいみたいなのがありあまり調べても出てこなかったのでメモとして残しておきます。

## 実装

```javascript
const list = [
  {name: "john1"},
  {name: "john2"},
  {name: "john3"},
]

return (
  <div>
    {list.map((l, i) => (
      <div style={(list.length - 1 === i) ? "最後の要素に付与するクラス名" : "最後以外に付与するクラス名"}>{l.name}</div>
    )}
  </div>
)
```
【Python】配列、dictionaryの生成方法、ループ方法、リスト内包表記をまとめてみた

pythonの配列、dictionaryの生成方法、ループ方法をまとめてみました。
新しい言語を学ぶ際には取り急ぎ配列、dictionaryの生成方法、ループ方法を抑えておきたいと思いますので、そのような情報がまとまっていれば便利だと思い、自分への備忘録も兼ねて本記事を投稿致しました。
最近pythonに入門された方の参考になったり、備忘録的な形で利用していただければ幸いです。
※Python 3.7.6で実施しました。

# 配列のループ
```python:array.py
# 配列生成
arr = [] # 空配列生成
arr = [1, 2, 3] # 要素のある配列生成

# 1～5までの配列要素追加　
# ※ range(start, stop)でstart～stop-1までが繰り返される。
# 　 startを指定しない場合は0から開始される。
for i in range(1, 6):
    arr.append(i)

# 配列のループ
for val in arr:
    print(f"val:{val}")

# indexを持つ配列のループ
for index, val in enumerate(arr):
    print(f"index:{index}, val:{val}")
```
index付きループをさせたい場合に`enumerate`のスペルをよく間違えるので
自分へのコピペ用にしようと思います。。

# dictionaryのループ
```python:dict.py
dic = {} # 空dictionary生成
dic = {"key1": 1, "key2": 2}  # 要素のあるdictionary生成

# dictionaryの追加
for i in range(1, 6):
    key = f"key{i}"
    dic[key] = i

# dictionaryのkeyをループ
for key in dic.keys():
    print(f"key:{key}, val:{dic[key]}")

# dictionaryのvalueをループ
for val in dic.values():
    print(f"val:{val}")

# dictionaryのkey, valueをループ
for key, val in dic.items():
    print(f"key:{key}, val:{val}")
```
dictionaryの`items()`は配列の`enumerate`のスペルよりは大分出てきやすいですね:relaxed:
dictionaryはkeyだけループさせたい場合もvalueだけループさせたい場合も
とりあえず`items()`を利用してkey,value両方取れるようにしてしまって良いと思います。

# リスト内包表記
配列やdictionaryから新たな配列やdictionaryを生成したい場合に利用します。
通常の方法であれば、まず空配列を作り、次に元の配列をループさせて新たな配列に要素を追加するという手順を踏む必要がありますが、内包記法を用いればそのような操作が1行で実現できます。
パフォーマンスも向上します。

```python:list_comp.py
# 元の配列・dictionaryの初期化
arr = [1, 2, 3, 4, 5]
dic = {"key1":1, "key2": 2, "key3": 3, "key4": 4, "key5": 5}

# 配列から配列 (各配列要素を2倍に)
conv_arr = [val * 2 for val in arr]
print(conv_arr)

# 配列からdict (indexをkeyに)
conv_dic = {f"key{index + 1}": val for index, val in enumerate(arr)}
print(conv_dic)

# dictから配列 (key-valueの文字列に)
conv_arr = [f"{key}-{val}" for key, val in dic.items()]
print(conv_arr)

# dictからdictに (keyを大文字にし、valueを2倍に)
conv_dic = {key.upper(): val * 2 for key, val in dic.items()}
print(conv_dic)
```

文法的には配列の場合が
`[式 for 任意の変数名 in イテラブルオブジェクト]`

dictionaryの場合が
`{キー: 値 for 任意の変数名 in イテラブルオブジェクト}`
となります。

# リスト内包表記(IF付き)
リスト内包表記にIF条件を付与し、条件に合致する要素のみを抽出する事もできます。

```python:list_comp.py
# 元の配列の初期化
arr = [1, 2, 3, 4, 5]

# 配列から配列 (偶数要素のみを取得しかつ要素を10倍に)
conv_arr = [val * 10 for val in arr if val % 2 == 0]
print(conv_arr)
```

本記事が少しでも皆様のご参考になりましたら幸いです。
それでは良いpythonライフを！
DjangoのListViewで投稿データを逆順にソートする

## 環境

・Python：3.8.5
・Django：3.1.2
・仮想環境：venv
・エディタ：Pycharm

## やること

・投稿ができる掲示板アプリで投稿を逆順にしたい

## 事前準備

・`models.py` の投稿クラスに作成日時である `created_at` (名前はなんでもいいけど)を追加します。

※ `timezone` のインポートも忘れずに。

```python:models.py

from django.utils import timezone


class Post(models.Model):
    title = models.CharField(max_length=20)
    content = models.CharField(max_length=140)
    created_at = models.DateTimeField(default=timezone.now)

    def __str__(self):
        return self.title
```


## View

・汎用ビューの `ListView` をインポートし、`PostListView` クラスに継承する。
・`ordering` で先ほど作成したモデルの `created_at` の逆順をかく。


```python:views.py

from django.views.generic import ListView
from .models import Post


class PostListView(ListView):
    model = Post
    context_object_name = 'posts'
    ordering = ['-created_at']
    template_name = 'index.html'
```

## できたはず

自分はこれでできました。
もしうまく反映されていないようでしたらmigrationかけてみるかコードの間違いがないかを再確認してみてください。
草が生えない...だと？

githubでコミットすると回数に応じて薄い緑から濃い緑になる。パッと見た感じ草が生えているように見えることから開発してコミットすることを'草を生やす'と言うことがある。
　使用例.
　「就活するんだったらやっぱりgithubに草は生やした方がいいですね。そこはかなり見られます。」

## コミットしているけど草生えないけど？
　いつものようにグーグルで検索したところ、メールアドレスが登録されてないことが原因のほとんどと指摘している方が多かった。でも私の場合は違った。

##　結論　masterブランチにコミットしてなかったから
　私はmasterからブランチを切ってそこに毎回コミットしていたので、反映されていなかった。でも安心して欲しい。masterブランチにマージすれば、過去にブランチにコミットした分もちゃんと草が生えます。
## なぜmasterブランチにコミットしないのか
 1.本番環境を最新ブランチで問題なく動くのを確かめる。
 2.masterブランチにマージする。
　この流れが身についているから。masterブランチにいきなりコミットするのは自分自身の開発でもためらってしまう。
OpenCVでプレイ動画の切り出しを自動化した話・OpenCVSharp導入ドハマり編

# 記事シリーズの目的

最近ゲームの解説動画を作っているのですが、その編集が面倒だったため自分の知ってる知識の寄せ集めでツールを作りました。

そのツールの応用性が高そうなので、ツールとソースを公開しておきたいというのと、広範囲の知識を寄せ集めたので応用例として残しておく価値があると思ったことから、ある程度分野ごとにまとめて記事にします。

https://github.com/mickie895/ONI_Movie_Cutter
今回作ったソースは上記からダウンロードできます。
使いたい人がいたら各自でビルドして使ってください。

## ツールの目的

Oxygen Not Incluidedのプレイ動画を今作っているのですが、その動画はポーズ画面を全カットする編集を行う予定にしています。

試しに1.5時間のプレイ動画に対してカット編集を行ったら5時間かかったこと、今後もシリーズが続くことから手作業によるカット編集は無理があると判断し、ツールを作ることにしました。

## 記事シリーズの予定

以下のような3つの記事を作成予定。

1. OpenCVSharp導入編(これ)
2. [OpenCVでの動画解析編](https://qiita.com/mickie895/items/8d77c477d9f094ca9e94)
3. [GUI実装編](https://qiita.com/mickie895/items/e6d23c5b17fa6c1ef19d)

## この記事の目的

OpenCVSharpの導入にドハマリしたのでそのメモ。
適当な解説記事を確認して導入しようとしたら記事の情報が古かった上にトラブった時の情報が無かった。

# VisualStudioにOpenCVをインストールする時の手順

## 要約

Nugetのパッケージの説明文はよく読んで、記事投稿時点ではOpenCvSharp4.Windowsをインストールすればいいよ。

## インストール方法

ソリューションエクスプローラの背景で右クリックし、NuGetパッケージの管理を選択する。
![nuget_1.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395414/54bd0c66-d8f0-2c66-b77a-8bfd4b348d73.png)
「参照」が選択されているから、検索欄にopencvsharpと入力して、**説明文をよく読んで必要なものをインストールすること。**
![nuget_2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395414/920278c0-3dc6-578b-6db3-348a46e059ad.png)
2020年10月時点では、Windowsなら`OpenCvSharp4.Windows`を選択すればオールインワンのパッケージがインストールできて手間がない。


## よく読まなかった人の末路

自分はよく説明文を読まず、他の記事だけパッと見てOpenCvSharp4だけインストールしてしまった。
すると`Mat m = new Mat();`を実行するだけで
`System.TypeInitializationException`や`DllNotFoundException`が発生する。
また、これらの解決のために検索したが情報がかなり古く、NuGetパッケージの見直しの情報を示している人はいなかった。
~~よく説明文を読まずに導入するマヌケは自分だけだったのかもしれない。~~

例外をよく読んだらNuGetパッケージ内に含まれてないDLLを参照しようとしていたので、OpenCVSharpのインストールが完全ではなかったことに気が付き、どうにか導入できました。


こんなことにも数日ハマってしまったけど、とりあえず導入はできた。
[次回、実際にOpenCVを使ってカット予定の場所を特定するアルゴリズムを組むところまで。](https://qiita.com/mickie895/items/8d77c477d9f094ca9e94)
ポートフォワードした際のSCPがわからなくなった時

セキュリティの関係などで、ポートフォワードして踏み台サーバ経由で通信した際のファイルのやりとりがいつもしくじるので備忘録を書きました。

#実行コマンド

まずはポートフォワードします。 ` 1234:1.2.3.4 ` がSSH接続に使用するポートと接続先のIPアドレス、 ` user@5.6.7.8 ` が踏み台サーバにログインするユーザ名と踏み台サーバのIPアドレスです。　` /.ssh/id_rsa ` は秘密鍵のパスです。


```
ssh -A -L 1234:1.2.3.4 -i /.ssh/id_rsa user@5.6.7.8
```

scp接続します。

```
scp -r -i /.ssh/id_rsa -P 10022 user@localhost:/etc/path/to/file /file/ /local/pc/path
```
サーバ上の`/etc/path/to/file`のディレクトリの中身をローカルPC上の`/local/pc/path`に転送するというコマンドです。
`転送元の場所 転送先の場所`の並びとなります。
XCode12でビルド先をシミュレーターにするとビルドできない!

## ビルド先をシミュレーターにするとビルドできない!

XCode12からビルドシステムが変わったみたいで、
`building for iOS Simulator, but linking in object file built for iOS, for architecture arm64` 
エラーが発生し、ビルドできないプロジェクトがあるみたいです。

## 解決方法

arm64をArchitecturesからはずせばいいらしい。

Build Settings > Architectures > Excluded Architectures > Any iOS Simulator SDK = arm64
<img width="100%"  src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/738919/88d67dba-a6ad-54ea-cee5-a53008c23ead.png">

### CocoaPodsの設定

CocoaPodsのライブラリでエラーが出ている場合は、`Podfile`に↓を追記する。

```shell:Podfile
post_install do |installer|
  installer.pods_project.build_configurations.each do |config|
    if config.name == "Develop"
      config.build_settings['EXCLUDED_ARCHS[sdk=iphonesimulator*]'] = 'arm64'
    end
  end
end
```
Android Studioでエミュレータ起動に失敗したとき

#はじめに
Android Studioでアプリ開発をやってみようと思ったとき、エミュレータ起動でつまってしまったのでメモ。

#マシン
・MacBookAir 13 2013
・Catalina 10.15.4
・Intel Core i7
・メモリ8GB

#エラー内容
Error while waiting for device: The emulator process for AVD Nexus_6_API_27 was killed.

#試してみたこと
http://www.aqlier.com/2018/12/28/android-haxm/

これでもダメ。
最初からインストールし直すも「/.Android~」配下のファイルが生きているため、以下手順でアンインストール。

https://blog.codecamp.jp/programming-androidstudio-mac
→再度インストールから試したところうまくできた。
Microsoft Docsの手順通りに作業したら1日で5万請求された話

Microsoft Docsを使って、Azure Data Factoryの勉強をしていたら1日で5万請求された事件を記録しておく。
問題のページは[こちら](https://docs.microsoft.com/ja-jp/azure/data-factory/tutorial-bulk-copy-portal)。

正直Azure Synapse Analyticsを使う予定はなかった。
ただData Factoryで何ができるのか？を体感するためにMicrosoft Docsに載っている事は一通り試しておこうというぐらいの軽い気持ちだった。

# そもそもAzure Synapse Analyticsが無料ではない
12カ月間の無料枠に存在しないサービスなので、利用した時点で料金が発生してしまう。
なのになんで作業したかって？

問題のページに

> Azure サブスクリプションをお持ちでない場合は、開始する前に 無料アカウント を作成してください。

って書いてあったからです。
じゃあ無料枠で出来るものと思うじゃんか。

# 手順通りに作業しても失敗する
どこで止まってしまうかは覚えていないが、書いてある通りに作業するとエラーとなってしまう。
かなり煮詰まってしまったので、一晩寝かして明日頑張ろうと作業を中断した。(この時は無料枠と信じきっていたので、サービスは残したままでした。)

# かなりお高い
Azure Synapse Analyticsを作る際のスペックの初期値が1時間2,195.20円です。（1000 cDWU）
仮にこのサービスを1日稼働させてしまった場合には

　24時間　×　2,195.20円　＝　52,684.8円

と1日で5万円も課金されてしまいます。

# 結果
何も動かすことなく、5万円の請求が発生した。

# 教訓
* 無料枠は正確に把握しておく。
* サブスクリプションはこまめにチェックする。
* 不要なサービスは即削除。

# Microsoftに期待すること
* 無料枠を超えるときは教えて欲しい。(無料アカウントの場合は特に)
* Microsoft LearnにData Factoryに関するページを用意してほしい。
【Rails】画像をアップロードせずに編集するとデフォルトの画像で上書きされてしまう問題の解決方法【ActiveStorage】

現在、個人開発中のアプリがかなり完成に近づきつつある中、新たに新しいバグを見つけてしまいました、、（リリース前に気付いてよかった）

前置き長いんで、さっさと解決策知りたい方はこの辺飛ばしちゃってください！！

プロフィール画像などの画像のアップロード機能をActiveStorageを使い実装したのですが、以下のようなバグが見つかりました。
例えば、あるユーザーがユーザー編集ページで画像の設定をしたとします。そして、次にパスワードを変更したくなったとします。当然、ユーザーは新しいパスワードのみ入力し、その値が編集ページのフォームからポストされます。
このとき、フォームのファイル選択エリア(file_field)には前回アップロードした画像は設定されていないので、この状態でupdateアクションが実行されてしまうと前回アップロードした画像が消えてしまい、アプリで設定しているデフォルトの画像で上書きされてしまうのです。

つまり、このままでは編集をするたびに毎回画像をアップロードしないと画像の変更を維持できないという最悪のUXをユーザーに提供してしまいます。これはなんとかしなければいけませんね、、というわけで解決策を書きます！

##解決策（超簡単）
コントローラーに以下を追記します。

```ruby:users_controller.rb
def update
    @user = User.find(params[:id])
    @user.avatar.attach(params[:avatar]) if @user.avatar.blank?
    if @user.update(user_params)
      flash[:success] = 'プロフィールを更新しました'
      redirect_to @user
    else
      render 'edit'
    end
  end
```

**if @user.avatar.blank?**を追記するだけです！
実は、ググってもググってもActiveStorageですでに設定された画像のキャッシュを作成するやり方がわからなかったので、自分で考えて色々と仮説検証した結果この一文を編み出し、解決することができました。（CarrierWaveなら出てきた）
テストもパスすることを確認しました。（この一文を消すとちゃんとテストに失敗することも確認済）

しかし、これだと

**既に設定されている画像を普通に変更する場合、```@user.avatar.bkank?```がfalseになって```@user.avatar.attach(params[:avatar])```が実行されずに画像の変更ができないんじゃないか？**

と思いませんか？僕は思います。。ｗでも、実際画像は更新されるし、テストもパスするのでとりあえずこれで良しとしようと思います。

**どなたかこのあたりの原理が分かる方、もしよければコメントいただけますと幸いです。**

##テストコード
補足として実際にパスしたテストコードをここに掲載します（関連する部分のみ抜粋）。

```ruby:upload_image_spec.rb
require 'rails_helper'

RSpec.describe '画像のアップロード', type: :system do
  let(:user) { FactoryBot.create(:user) }

  before do
    valid_login(user)
  end

  # 画像をアップロードして保存する
  def upload_user_avatar(user)
    visit edit_user_path(user)
    attach_file 'user_avatar', "#{Rails.root}/spec/fixtures/images/test.jpg"
    click_on '保存する'
  end

  it 'userがアップロードした画像がマイページに表示されること' do
    upload_user_avatar(user)
    expect(page).to have_selector("img[src$='test.jpg']")
  end

  it "userが画像の更新に成功すること" do
    visit edit_user_path(user)
    attach_file 'user_avatar', "#{Rails.root}/spec/fixtures/images/updated_test.jpg"
    click_on '保存する'
    expect(page).to have_selector("img[src$='updated_test.jpg']")
  end

  it "画像をアップロードせずに編集したとき、デフォルトの画像データで上書きされないこと" do
    upload_user_avatar(user)
    visit edit_user_path(user)
    click_on '保存する'
    expect(page).to have_selector("img[src$='test.jpg']")
  end
end
```

#最後まで読んでいただきありがとうございます！
日々学んだことをアウトプットしてます！ご指摘などあれば是非ともコメントいただけますと嬉しいです！！
DockerでWordPressの開発環境をサクッと構築する

# TL;DR

https://github.com/qpSHiNqp/docker-wordpress-dev

これcloneして `docker-compose up` すると、一瞬でWordPress環境立ち上がるよ

# 目次

1. dockerでWordPress環境を構築するまで
2. テーマ作るときに、コンテンツ部分に適用されるスタイルをよしなにいい感じにする（ついでに書いてるだけ）

# dockerでWordPress環境を構築するまで

## 経緯

たまたまちょいとWordPressいじる必要が生じた。開発環境を整えるために今までは、ローカルにMySQLサーバ立てて、DB作って、ユーザーと権限周り作って整えて、PHP環境作って、Webサーバ環境作って、繋いで、動作確認して... っていういわゆるMAMP的なのを頑張ってやってたんですが、今どきそんなこと普通しないだろ、ということでdockerで環境構築すべく、ちょっと調べた。

## 構築方法

調べたところ、`wordpress` っていうdocker imageもあるし、当然のことながら `mysql` もdocker imageあるので、 `docker-compose.yml` いい感じに作ればいじゃんという話になり、作った。

作り方についても、 [`wordpress` のDocker HubのDescription](https://hub.docker.com/_/wordpress)に丁寧に書いてあったので、参考情報として記載しておく。
ページ中頃の、「... via docker stack deploy or docker-compose」というセクションほぼそのまんま。

今回はテーマとかプラグインを作りたかったので、`wp-content/themes`と`wp-content/plugins`をマウントするように`docker-compose.yml`を作成。
※最新のものはGitHubを参照してください。

```yml
version: '3.1'

services:

  wordpress:
    image: wordpress
    restart: always
    ports:
      - 8080:80
    environment:
      WORDPRESS_DB_HOST: db
      WORDPRESS_DB_USER: exampleuser
      WORDPRESS_DB_PASSWORD: examplepass
      WORDPRESS_DB_NAME: exampledb
    volumes:
      - wordpress:/var/www/html
      - ./wp-content/themes:/var/www/html/wp-content/themes
      - ./wp-content/plugins:/var/www/html/wp-content/plugins

  db:
    image: mysql:5.7
    restart: always
    environment:
      MYSQL_DATABASE: exampledb
      MYSQL_USER: exampleuser
      MYSQL_PASSWORD: examplepass
      MYSQL_RANDOM_ROOT_PASSWORD: '1'
    volumes:
      - db:/var/lib/mysql

volumes:
  wordpress:
  db:
```

これで、 `docker-compose up` して `http://localhost:8080` 開くともうすぐに WordPressのセットアップ画面が開くので、そのまま進めば管理画面にいける。

# テーマ作るときに、コンテンツ部分に適用されるスタイルをよしなにいい感じにする（ついでに書いてるだけ）

ここから先、本当についでに書いただけなので、自分の備忘録程度にしか使えないと思います。

自作テーマ作るときに、レイアウトとか、見た目とかをstyle.cssで制御すると思うんだけど、WordPressってWYSIWYGエディタを備えたCMSなので、コンテンツ部分のレイアウトはWordPress備え付けのものを生かしてあげる必要がある。

備え付けのstyleは、[CSS « WordPress Codex](https://codex.wordpress.org/CSS#WordPress_Generated_Classes)の「WordPress Generated Classes」セクションに書いてあるもの。これを自作テーマの最下部にコピペしてあげることで、WYSIWYGで編集時に指定したレイアウトを実際の表示に反映することができた。

当初、自作style.cssで定義したスタイルがコンテンツ部分の要素にまで適用されていたことでWYSIWYGと見た目がずれてしまうという問題が発生してしまっていたが、おそらく下記のようにマークアップの責任分界をするとだいたい上手くいくような気がする。

- テンプレ/テーマ側では、コンテンツをwrapするブロック要素のレイアウトだけHTML+CSSで制御してあげて、 `<?php the_content(); ?>` とかで埋め込むコンテンツ本体のレイアウトに影響しないような`style.css`にしてあげる
- `<h1>`（か、若しくは`<h2>`）が投稿タイトルの表示を担当するなら、これはテンプレ側で表示を制御してあげたほうがいい
- 文字色とかフォントとか、レイアウト以外の見た目に関するスタイルは style.css で定義して、コンテンツにも適用させるようにつくる（厳密に言うとフォントが違うとレイアウトも違ってくるとも言えるけど）

要は、WordPressのWYSIWYGが吐くHTMLなんて予想不可能なので、コンテンツ本文のブロックレイアウティングを自作cssで制御するのはたぶん無理。むしろテーマ作る側は、コンテンツ本文のブロックレイアウティングを制御せず放任してあげるようなクラス/セレクタ設計が必要な気がします。

カラーパレット機能とかサポートするように作っても良かったが、今回はそんなにWordPressの機能をフルサポートするような汎用的なテーマを作る必要がなかったので、最小限の機能サポートだけで実装した。
DB設計で悩んだ、本日の学び

#はじめに
　本日の学習したDB設計について、簡単にまとめる。

##必要なテーブル・カラムの洗い出し

・エンティティはデータが登録させるタイミングを考える。
・プルダウンはActiveHashを用いることができる。
・ActiveHashを用いたカラムはinteger型また、カラム名に`_id`をつけるとわかりやすい。
・郵便番号や電話番号はstring型で扱うのがよい。
・日付はdata型を用いる。
・数値も文字も入る可能性があるカラムはstring型。
・テーブル名で判断できるカラム名はつけない。
ex)usersテーブルで、カラム名をuser_nameとしない。nameでよい。
　
##ActiveHash
・ER図には繋がりを記載する。
・gem 'active_hash'を導入する。


##アソシエーション
・１対１の関係にある場合、親に`has_one`を子に`belongs_to`の記述をする。
・親子の判断は、もう一方が存在しえないと、存在しない方が「子」。
・外部キーをどこに置くのか、よく考える。
無料のOpenShift on IBM Cloudでサンプル為替アプリを試す。Red Hat Universal Base Image(UBI)を使用

無料で試せるOpenShiftアプリ、今回はIBM コードパターンのサンプル為替アプリを試してみました。
元ネタはこちらです。
https://developer.ibm.com/patterns/deploy-to-openshift-4-redhat-universal-base-image/
日本語で紹介しているブログもありました、
[Code Pattern] Red Hat Universal Base Image(UBI)を使用して、サンプル為替アプリをOpenShift on IBM Cloudにデプロイする方法
https://qiita.com/ayatokura/items/6c6abaae906043302a8e

今回も無料で使えるOpenShiftのサイトを使ってやってみたいと思います。
Skills Network Labs. https://labs.cognitiveclass.ai/tools/theiaopenshift/lab/tree?md_instructions_url=https://cf-courses-data.s3.us.cloud-object-storage.appdomain.cloud/deploy-ai-microservices-on-kubernetes/tutorial-instructions.md
サイトの使い方は
https://qiita.com/osonoi/items/a5d3a5fe78455c842f42

* DockerhubにイメージをあげるのでDockerHubのアカウントが必要です。
*  PC,MacにDocker, Gitが入っている必要があります。
*  PC,MACに環境を作るのが難しい方はOpenShiftの操作から初めて下さい。その場合$DOCKERHUB_USERNAMEにyosonoiと入れて下さい。

まず最初に
ローカルのPCの環境にコードをクローンします。

```
git clone https://github.com/IBM/python-ubi-openshift.git

cd python-ubi-openshift
```
次に変数を設定してDocker イメージを作成し,そのイメージをDockerHubにアップします。"Your-dockerhub-username"にはご自分のDockerHubのアカウント名を入れて下さい。


mac

```
export DOCKERHUB_USERNAME=<your-dockerhub-username>
docker build -t $DOCKERHUB_USERNAME/currencyexchange-py:v0.0.1 .
docker login
docker push $DOCKERHUB_USERNAME/currencyexchange-py:v0.0.1
```

Windows

```
SETX DOCKERHUB_USERNAME "your-dockerhub-username"
docker build -t $DOCKERHUB_USERNAME/currencyexchange-py:v0.0.1 .
docker login
docker push $DOCKERHUB_USERNAME/currencyexchange-py:v0.0.1
```


この後はOpenShiftのコマンドで以下を入力して下さい。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59741/c6d24535-aab6-02d0-7f32-5f2a56930280.png)
$DOCKERHUB_USERNAMEにはご自分のDocker Hubのアカウント名を入れて下さい。


```

oc new-app $DOCKERHUB_USERNAME/currencyexchange-py:v0.0.1

oc expose svc/currencyexchange-py

oc status
```
下の様に出力されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59741/f7fe5546-a314-4111-8781-815cc9969ba9.png)
出力の６行目からある　http://currency.... がアプリのURLになります。コピーしてブラウザーの別タブで開いて下さい。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59741/e261dda3-a51b-34be-434c-580fdfe08d20.png)
こちらがアプリの画面になります。
使い方をみてみましょう。
Currency も右側の矢印をクリックして下さい。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59741/f8d10367-3061-db81-03b9-01d352d88a69.png)
一番下のGETをクリックしてください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59741/c2360283-8eb2-dfff-4c26-4a5c85516566.png)
右側のTry it outをクリックして上から日本円(JPY), 米国ドル(USD), 単価　１を入力して"Execute"ください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59741/8aca72c2-c7be-2161-d274-fb79adcc636c.png)
$1が105.9円というのが得られます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59741/369b12ae-0f4c-8420-841e-677fd6640b72.png)

CurlのコマンドをコピーしてPC, Macのコマンドラインでも確かめることができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59741/04d71804-75db-819b-6681-8ead17914ee5.png)

## それでは是非試してみてください！
Azure Web Jobs（NoAutomaticTrigger）でDIさせる方法

Azure Functionsで依存関係の挿入する方法は、[Microsoft Docs](https://docs.microsoft.com/ja-jp/azure/azure-functions/functions-dotnet-dependency-injection)に記載されていた。
しかしWeb Jobsでやる方法がどこにも記載されていなかったので、備忘録として残しておく。

# 前提
タイトルでも記載しているようにここで記載する方法は、NoAutomaticTrigger(Web Jobsを手動で起動する場合)のやり方です。
自動トリガーの場合は、Azure Functionsと同じ方法でコンストラクタでの依存関係の挿入ができるらしい。

# 手動トリガーで同じ方法が使えない理由
手動トリガーで起動させるには、対象のメソッドにある制約が生まれる。

* NoAutomaticTrigger属性を付与する。
* publicである。
* staticである。

この最後の制約のために、コンストラクタで依存関係が挿入できない。

# 結論
コンストラクタがダメなら引数で依存関係を挿入する。

```C#:Functions.cs
[NoAutomaticTrigger]
public static void CreateQueueMessage(ISampleServiceA sampleServiceA)
{
    sampleServiceA.DoIt();
}
```

```C#:Program.cs
static async Task Main(string[] args)
{
    var builder = new HostBuilder();
    builder.ConfigureWebJobs(b =>
    {
        b.AddAzureStorageCoreServices();
        b.AddAzureStorage();
    });
    builder.ConfigureServices((context, services) =>
    {
        services.AddSingleton<ISampleServiceA, SampleServiceA>();
    });
    var host = builder.Build();
    using (host)
    {
        var jobHost = host.Services.GetService(typeof(IJobHost)) as JobHost;
        // GetService<T>でインスタンスを取得する
        // この時にDictionaryのKeyは引数名と一致させておくこと
        var inputs = new Dictionary<string, object>
        {
            { "sampleServiceA", host.Services.GetService<ISampleServiceA>() }
        };

        await host.StartAsync();
        // Functionsを呼び出す際に引数として渡してあげる
        await jobHost.CallAsync("CreateQueueMessage", inputs);
        await host.StopAsync();
    }
}
```

ここに辿り着くまでに結構時間かかった。
【AWS S3】AWS Access Key Id you provided does not exist in our records エラー【Rails AWS EC2】

#エラー内容
本番環境(EC2)で画像投稿(carrierwave)をしようとすると、下記のようなエラーが起きる。
![スクリーンショット 2020-10-14 1.02.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/620533/47d0b3da-9714-9fa2-290d-15d9f85cc071.png)

表示されている(読み込まれている)AWSAccessKeyIdは以前使用していた古いキー。
エラー文から古いアクセスキーが読み込まれているため整合性が取れず起きたエラーと判断した。

#エラーに対するTRY
* .bash_profileの更新
* .envファイルの確認
* .awsディレクトリ以下の確認(config,credentials)
* aws configure の確認
* AWS IAMによりポリシー等の確認
 
.bash_profileでexportしていたアクセスキーIDが確かに古いものであった。
それを新しいアクセスキーに更新し、.envファイルやIAM、aws configure listを確認し、最新のアクセスキーIDに更新されたことを確認したが、画像を投稿すると同じエラーが生じる。Dockerを利用しているため、古いイメージなどを消去するも、変化なし。

```ruby:ローカル
$ aws configure list
AWS Access Key ID [None]: AKIA...(新しいアクセスキー)
AWS Secret Access Key [None]: KEY...(新しいアクセスキー)
Default region name: ーーー
Default output format: json
```

#解決方法
途方に暮れていたところ、

**「EC2で不具合が起きた時はまずEC２インスタンスを再起動させよ」という先人の言葉をふと思い出した。**

「あぁ、これが正解だ」と思った。恐らく、EC2インスタンスを再起動させないと上手く読み込まれない。EC2インスタンスからexitでログアウトし、再ログイン(ssh)ではダメだった。

**EC2インスタンスを再起動させると予想通り解決し、正常に画像を投稿できた。**

#学び
AWS CLIにより、ターミナルから古いキーを新しいキーに変更した場合は必ずEC2インスタンスを再起動させる！

AWSアクセスキーID等は最新なのに私と同じようなエラーが起きるという方はまずは使用しているEC2インスタンスを再起動させてみてください。
Laravel:componentの設定

【概要】
----------------------------------------
<b>1.componentフォルダの作成</b>

<b>2.該当のblade.phpで呼び出し</b>

<b>3.開発環境</b>



1.componentフォルダの作成
----------------------------------------
そもそもコンポーネントとは、レイアウトを最初から記述するために省き冗長にならないように部品のように使える仕組みです。ヘッター、フッターはどこのページにいっても同じデザインで使いたい場合に各レイアウトを統一する時に使用することが多いです。

viewsフォルダにcomponentsフォルダを作成します。さらにcomponentsフォルダの中に、適当なblade.phpファイルを作成します。

```php:resorces/components/message.blade.php
  <div class="message">
    <p class="msg_title">{{$msg_title}}</p>
    <p class="msg_content">{{$msg_content}}</p>
  </div> 
```

<br>
2.該当のblade.phpで呼び出し
----------------------------------------

```php:resorces/views/hoge/index.blade.php
<html>
<head>
  @component('components.message') #---❶
   @slot('msg_title')　#---❷
    こちらにメッセージを入力してください。
   @endslot　#---❷

   @slot('msg_content')
    メッセージの内容。
   @endslot
  @endcomponent　#---❶
</html>
</head>

```

❶：コンポーネントを使用するためにどのフォルダのどのファイルを使用するかを決めています。"components"ファイルの、"message.blade.php"なので、('components.message')とコーディングしています。範囲の終わりには@endcomponentで括れます。

❷：massage.blade.phpの{{＄msg_title}}、{{＄msg_content}}の変数を使用するために記述しています。cssが反映された状態でindex.blade.phpにそのまま使用できます。@slot(名前)でそのまま使用できます。 @endslotで括れます。

<br>

3.開発環境
----------------------------------------
PHP 7.4.10
Laravel 8.9.0
Apache 2.4.41
<br>
Oculus Quest2のWebブラウザをざっくり触ってみた

# はじめに

さて、Oculus Quest2が発売されました。
1つのデバイス・ガジェットとしてもちろん興味を惹かれるところはありますが、Webを本業とするエンジニアとしては、**Webブラウザでどんなことができるか**も気になるところ。

しかしあらゆるレビューでハードウェアばかり語られて、ソフトウェア、特にWebブラウザについては全く触れられていないので、個人メモとして書き残しておきます。

# Quest1との比較

Oculus Quest
![quest1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199678/d44b5dee-20af-1dde-f50c-f55d31f21638.jpeg)

Oculus Quest2
![quest2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199678/b81edbc0-f5d4-6f0f-c316-a1feb8b33c8a.jpeg)


どちらも本体のスクショ機能で撮影。1440*1440のjpgファイルとして保存されました。
Oculusはfacebookアカウント必須なので、facebookには数クリックでアップできます。
Type-CケーブルでPCと繋げば、直接静止画や動画ファイルをやり取りできます。

|項目|Quest|Quest2|
|:-----------|:------------|:------------|
|ユーザーエージェント|5.0 (Linux; Android 7.1.1; Quest) AppleWebKit/537.36 (KHTML, like Gecko) OculusBrowser/11.1.0.1.64.238873877 SamsungBrowser/4.0 Chrome/84.0.4147.125 Mobile VR Safari/537.36|5.0 (Linux; Android 10; Quest 2) AppleWebKit/537.36 (KHTML, like Gecko) OculusBrowser/11.1.1.3.64.24840954 SamsungBrowser/4.0 Chrome/84.0.4147.125 Mobile VR Safari/537.36|
|ディスプレイサイズ|800*450|1320*598|
|ブラウザウィンドウサイズ|3段階に変更可能<br>800*450<br>600*450<br>400*450|3段階に変更可能<br>1056*479<br>800*479<br>480*479|

UAはなんか盛々ですねｗ

どうでもいい小ネタですが、
Quest1の場合はブラウザサイズを変えるとディスプレイとブラウザ両方の数値が(jsの数値上)同じ値に変わります。
Quest2の場合はブラウザサイズを変えてもディスプレイサイズは1320*598のまま変わらず、ブラウザサイズの数値のみ変わります（本来こちらが正しい挙動）。

# WebVR

WebVRも、当然ながらちゃんと動きます。
A-Frameの公式サンプルを見てみます。

![vr1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199678/e7a3b014-c945-b74f-9ce2-83fa50f2a8a0.jpeg)

右下の「VR」ボタンを押すとこんなダイアログが出て、
![vr2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199678/557f6d30-0bd5-5a69-a948-9199660a6b44.jpeg)
許可を押すとVR空間へ。
![vr3.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199678/f9bb3fad-bb83-d599-a449-8fcf040e69d0.jpeg)

当然空間を歩き回ることも可能です。

このあたりはQuest1と特に変わりないです。

# その他

1年ほど前にQuest1を触っていた頃は、Basic認証使ったページに行くと認証ウィンドウが出ず、URLに認証情報埋め込む原始的なやり方じゃないと見れなかったのですが…
知らない間にQuest1でも認証ウィンドウが出て使えるようになっていましたし、Quest2も当然、何の問題もなくBasic認証できます。

対応ブラウザが限られるWebRTCやIndexedDB等も（ちょっと触った程度ですが）使うことができました。
恐らくChromeのような実装スピードではないでしょうが、新しい技術もそれなりに使えそうです。

なお、今のところデバイスとして「カメラ」は認識しないことを確認しています。
Questは本体にカメラが4つ付いていますが、景色を映す用途ではなく、6DoF（空間内をしゃがんだり歩き回ったりできる自由度）の空間認識のために使われているためです。

# 余談　結局買いなの？

Quest1を持っていなければオススメです。
解像度やリフレッシュレート等のスペックが順当に進化しており、小さく軽くなって、値段も安くなった**完全上位互換**という印象。

Quest1を持っているなら、無理に買わなくても良いかもしれません。
ストアは共通でQuest2専用アプリも無いので、ナンバリングは変わっていますが、PS4→PS5のようなものではなく、PS4→PS4 Proのような上位機種(ただし値段は安い)といった立ち位置です。
[JavaScript] thisを使用する理由

# 概要

フロントエンド初心者による学習忘備録。
本投稿ではJavaScript(jQuery)で使用する`this`についてまとめる。


## 環境

* jquery-3.5.1.min.js

```html
<script src="./js/vendor/jquery-3.5.1.min.js"></script>
```

## thisを使用するメリット

メリットとして以下3点が主に挙げられる。

* 処理のパフォーマンス向上
* コードの流用
* 複数のセレクタを指定した場合の処理の切り分け

### パフォーマンス向上

jQueryの処理は`メソッドチェーン`として順に処理される仕組みであり、ブラウザは指定されたセレクタを手がかりにHTMLから要素を集めるが、`this`を使用しないとメソッドが実行されるたび毎回集めることになりCPUに負荷が掛かってしまう。

```js
$(function () {
  $('.test').on('click', function () {
    $('.test').css('color', '#ebc000');
  });
});
```

以下のように変更する。

```js
$(function () {
  $('.test').on('click', function () {
    // thisは上の状態を保持している
    $(this).css('color', '#ebc000');
  });
});
```

### コードの流用

以下はそれぞれのクラスに対してcssのプロパティを変更する処理だが、内側の処理に重複が見受けられる。

```js
$(function () {
  $('.test').on('mouseover', function () {
    $('.test').css('color', '#ebc000')
  });
});

$(function () {
  $('.test-class').on('mouseover', function () {
    $('.test-class').css('color', '#ebc000')
  });
});
```

内側のcssを変更する処理は同じのため、`this`を使用し以下のように処理を簡潔にすることがベスト。

```js
$(function () {
  function colorChange() {
    $(this).css('color', '#ebc000')
  };

  $('.test').on('mouseover', colorChange);
  $('.test-class').on('mouseover', colorChange);
});
```

### 処理の切り分け

以下処理では複数のセレクタを指定しcssメソッドを実行しているが、いずれかの要素にイベントが発生した際指定されている全ての要素にメソッドが反映されてしまう。

```js
$(function () {
  $('header, .test-class, .test').on('mouseover', function () {
    $('header, .test-class, .test').css(
      'background-color', '#ae5e9b'
    );
  });
});
```

`this`を使用し以下のように変更することで、各セレクタに対しそれぞれメソッドが反映されるようになる。

```js
$(function () {
  $('header, .test-class, .test').on('mouseover', function () {
    $(this).css(
      'background-color', '#ae5e9b'
    );
  });
});
```

---