OmniAICreator/Qiita-1.07M huggingface dataset:
「20代エンジニアのためのプログラマー勉強法」個人メモ


9月29日(土)に行われた、まつもとゆきひろさん(以下Matzさん)による講演、「20代エンジニアのためのプログラマー勉強法」に参加してきました。
私も遅ればせながら、内容をまとめました。

## 参加した目的

- Matzさんのご尊顔を拝む
    - 私は初めてでした
- エンジニアとしての学びの方法を知る、考える
- 何かを始めるきっかけとする
    - 私としては、これが最大の目的

## 内容まとめ

### 学生と社会人の勉強法の違い

- 満点のあるなし
    - 苦手を克服、得意を伸ばす
    - 得意なこと、好きなことを頑張った方がいい
    - 上限はない

- 記憶と把握
    - 知識のインデックスを把握しておく
    - 細かい部分を記憶しておく必要はない
        - 調べれば良い

- 試験と常在戦場
    - スコープを外れた勉強

- 評価軸、1次元と多次元

- メインとサブ
    - 社会人は働くことがメイン

- 安定と変化
    - ITでは色々変化がある
        - 古くなる
        - トレンドが変わる


### なぜ勉強するのか？

- なぜ勉強する？
    - 成功したい
    - 金が欲しい
    - 良好な人間関係が欲しい
    - 嫌なことはしたくない
    - 好きなことをしたい

- これらの実現確率を高めるもの
    - 高評価
    - 尊敬
    - 尊重

- 何を勉強するか選ぶ方法
    - この講演ではヒントだけ

### 内省

- 内省
    - 自分を見つめる、座って考える
- 走り出すのは楽
- 走り続けるのは大変
- どこに向かうのかはもっと大切
    - 得意なこと、苦手なこと
    - 好きなことは上達する

### パターン認識は成功の秘訣

- あなたが尊敬する人は
    - そういう人たちからパターンを探す
- 知られることが大切
    - 尊敬: 人格的
    - 尊重: 人格は無関係
- 成功するためには知られる必要がある
    - 有名である = 価値がある

- キャズムを乗り越えるには
    - ニッチに進出
    - その後、横展開

- 勉強をアウトプットして目立つ
    - マーケティングとして自分売り込むための手段が勉強

### あなたはどう勉強するか

- パターン認識して抽象化
    - 内省
        - 自分を一番知ってるは自分
        - だから自分で決めなくてはならない

- インベントリ
    - 自分の在庫について真剣に考える
        -そして決める
        - どこまで妥協
            - 職種
            - 収入

- 予想は外れる
    - こうなるはずだからこうする
        - 決めたことを変えてもいい
    - 未来将来を考えるのは無駄、当たらない
        - 自分の傾向を考えるべき

### 具体的にどう勉強するのか

- 暇、退屈だというのは注意報
    - モチベーション管理ができていない
    - 時間管理
        - 優先順位をつける

- プライベートを犠牲にしない
    - 生産性を高める
    - 忙しすぎない状態にする
    - 仕事の中で勉強する
        - それができないのは尊重されていない
            - 転職しましょう
    - 時間を作るために
        - 短い時間で成果をあげる
        - バッファを積む
    - 社畜で死ぬのはやめましょう

- アウトプットをする
    - インプットも重要だが、アウトプットはもっと重要
    - アウトプットは敷居高い
        - 心理的障壁
            - はずかしい
    - アウトプットによる定着
        - クオリティは棚上げ
    - アウトプットを繰り返す
    - アウトプットが楽になる
    - 最適化されていく
    - 心理的障壁の低下

- 人間の可塑性
    - 置かれた環境によって人は変化できる
    - 自分が固定的だと思わない方がいい
    - 予想もしない方向で変化するかも

- 基礎を抑える
    - コンピュータサイエンス
    - アルゴリズム
    - コンピュータアーキテクチャ

- 英語について
    - 知識を得るため
    - 日本語では最新の情報は得られない
        - ガラパゴスから出る
    - 必要なレベルで
    - 語学は場数
        - 完璧を目指さない

- コンフォートゾーン
    - 幸せな空間
        - 成長がない
    - 普段やらないことをやる
inspectメソッド(ruby)

inspectメソッドはオブジェクトに対して使うことでそのオブジェクトの内容がわかるような文字列を返してくれる。


例

```
user = User.find(1)
p user.inspect
```

出力結果

```
"#<User id: 1, name: \"test_user\", created_at: \"2018-10-06 16:45:52\", updated_at: \"2018-10-06 22:34:48\", email: \"xxx@x.x\", password: \"xxxxxxx\", remember_token: \"xxxxxxxx\", deleted: false>"
```
記事投稿初心者が「エンジニアは全員技術記事を書くことを習慣化した方がいいぞ」を読んだ感想

# はじめに

会社勤めも３年目に入ったものの、大したアウトプットができていませんでした。なんとか現状を脱却しようと、少しづづ記事を上げていたところ、なんともタイムリーな記事が上がっていました。

[エンジニアは全員技術記事を書くことを習慣化した方がいいぞ](https://qiita.com/HiromuMasuda0228/items/a71dea7ef4d77a30b118)

この記事を読んで共感したところ、自分なりにこれから記事を投稿していく中で、意識していきたいことをまとめています。記事投稿の初心者仲間:beginner:のためになればと思います。


## 元記事
原文:page_facing_up:はこちらです。
>[Why Every Software Engineer Should Write Articles](https://medium.freecodecamp.org/why-every-software-engineer-should-write-articles-f35bdae72d1b?gi=bd162ab8e8ab) 


## なぜ全てのエンジニアが記事を書くべきか
>近頃、コンピュータサイエンス産業は複雑かつ急速に進化しており、複雑な技術やコンセプトを簡単な方法で説明するという事がより重要になってきている。

昨今は、機械学習・ブロックチェーン・VRなど、新しいことがどんどんでてきます:computer:。それらを全てキャッチアップしていくことは、困難だけど、できている人もいるし、すごくわかりやすく説明している人もいます。自分も新しいことを調べるときはとにかくわかりやすいサイトをあら捜しします。:sweat_smile:

どうすれば、複雑な技術を自分に身につけることができて、人にもわかりやすく説明できるのか？:confused:
これは、エンジニアの永遠の課題かなと思います。

## 解決策
>　技術に関する情報を要約したり、簡単に理解できるような情報に変えることができる記事を書く人がより必要とされる

ばりばり英語を読むことができたり、様々な経験を積んだエンジニアなら原文やGit:octocat:のソースコードを見て理解することができるのかもしれないけど、そんなの難しいってときありますよね。

とりあえず新しい言葉や技術がでると、とにかくググって:earth_asia:一番わかりやすそうなページを探します。
フォントがきれいで、図がたくさんあって、わかりやすさが雰囲気から伝わってくるあれです。~~曖昧なのは勘弁~~

わかりやすい記事があると、難しいドキュメントを何度も読み返さずにすむし、早く技術を理解できるようになる。それ故に、発展のスピードも早くなる。だから、みんなそういったコンテンツや記事を作っていこう。:point_right:

とことらしいですが、なんでみんながそれをしなきゃいけないの？:open_mouth:

## 記事を書いた人が勝つ

記事を書くことで、とにかく成長できると。（具体的には次のよな感じ）

- **しっかりとした知識が身につく**
- **達成感を得られる**
- **より良いコミュニケーションにつながる**
- **思いもよらない機会に巡り会える**

> 簡潔に説明できないならば、十分に理解できているとは言えない

まさにそのとおりです。でもそれが結構むずかしい。しかし、これは記事を書く:pencil2:ことで解消していくと思います。

『記事を書く』ということは、困っている人（未来の自分かもしれない）を意識をして書くため、そのことを隅から隅まで理解しようとします。

下手なことを書くとネットの強者から叩かれるから、ちゃんとソースを載せないと、、、とか。:fearful:

わかってないといざコメントが来たときに返信できないからもう少し調べよう、となります。そして結果的に、ある技術の細かいところまで調べることが、理解が深まることに繋がっていくと思います。

記事の投稿頻度は下がりますが、一つの記事をしっかり仕上げるということが、何よりの自信になります。（Review数とか数字が伸びると目に見えて達成感ありますよね。）

わかりやすい記事を書くには

#### ひたすら練習
とのことなので、頑張って継続していきたい。


## 自分の記事で他人が享受する利益

他の人にどんな影響を与えることができるのか？

- **知識**

> 簡潔で良い記事を書くことで、他の人の学びをぐっと助けている

　より多くの人が発展のスピード:running:に追いつけるようにすることで、産業を推し進めていることに貢献している、といった意識を持つことで継続して記事を投稿できるようになるのかな。

　何か目的がないと結局は続かないので、こういった根本を見つめ直すことは常に意識していきたい。


- **様々な視点**

> 1つのコンセプトに対していろんな記事があることはとても大切なことである。

　複数の視点:bird:をもつことで、様々なアプローチの仕方があることを理解するってことかな？自分にとっては難しいことでも、他人にとっては簡単なこともある。その逆もしかり。

　だから、自分が投稿する技術の記事を読み漁って、ちょっと違う視点から書く。といったことをしてくと有益なのかな。

## まとめ

　記事投稿をし始めた自分にとっては、かなりモチベーションにつながる記事でした。技術書を読んだり:books:、QiitaのROM専だったりしたけれど、継続的に記事を投稿できるよう、

**しっかり技術を見につける　:arrow_right:　わかりやすく投稿する**

といったサイクル:arrows_counterclockwise:を回していきたいです。
Vue.js：webpackを使わずにコンポーネントを実装する

テスト用のVue.jsアプリを作るときに、動作確認しやすいように`vue-cli`を使わずにCDNからスクリプトを読み込む形式で作りました。このときにどのようにコンポーネントを実装すればよいか調べるのに時間がかかったため、簡単に整理してみました。

#### ポイント

* スクリプト読み込み
  * Vue.js本体 → コンポーネント → メイン（Vueインスタンス生成）の順で読み込む。
  * head要素に上記の順序でscript要素を組み込むめばよい。（deferを指定するとレンダリングをブロックせずに高速化できる。）
* テンプレート
  * 以下のいずれかの方法でVueコンポーネントのtemplateプロパティに設定する。（[参考](https://jp.vuejs.org/v2/api/#template)）
    * HTMLのhead要素に `<script type="text/x-template>` でテンプレートを記述し、templateプロパティにセレクタ（#で始まる文字列）を指定して参照する。（src属性を使って外部ファイルに切り分けることはできない。）
    * templateプロパティに文字列としてHTMLを記述する。

#### サンプルアプリ

index.html

```html
<html>
	<head>
		<!-- Vue.jsアプリケーション読み込み
		     ※ new Vue() 実行前にコンポーネントを読み込むこと。 -->
		<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js" defer></script>
		<script src="./component/string-template.js" defer></script>
		<script src="./component/script-template.js" defer></script>
		<script src="./script.js" defer></script>

		<!-- scriptテンプレートを使ったコンポーネント -->
		<script type="text/x-template" id="script-template">
			<div>
        <p>Script Template</p>
      </div>
		</script>
	</head>
	<body>
		<!-- Vueアプリのマウントポイント -->
		<div id="app"></div>
	</body>
</html>
```

script.js

```javascript
const app = new Vue({
  el: '#app',
  template: `
    <div>
      <string-template />
      <script-template />
    </div>
  `
})
```

component/string-template.js

```javascript
Vue.component('string-template', {
  template: `
    <div><p>String Template</p></div>
  `
})
```

component/script-template.js

``` javascript
Vue.component('script-template', {
  template: '#script-template'
})
```
Go製のテストツールGossを使ってみた

# Goss
Go製のテストツールを使ってみた

- OS
  - Amazon Linux

### インストール
`# curl -fsSL https://goss.rocks/install | sh`

- インストール完了と実行できるかの確認

`# goss --version`

```
-bash: goss: コマンドが見つかりません
```

- コマンドが見つからない場合の対処例

`$ sudo visudo`

```
Defaults    secure_path = /sbin:/bin:/usr/sbin:/usr/bin
↓
Defaults    secure_path = /sbin:/bin:/usr/sbin:/usr/bin:/usr/local/bin
```

### テストを作成する
`# goss autoadd nginx`

```
Adding Group to './goss.yaml':
```

- nginx.yaml

```
package:
  nginx:
    installed: true
    versions:
    - 1.10.3
port:
  tcp:80:
    listening: true
    ip:
    - 0.0.0.0
  tcp:443:
    listening: true
    ip:
    - 0.0.0.0
  tcp6:80:
    listening: true
    ip:
    - '::'
service:
  nginx:
    enabled: true
    running: false
user:
  nginx:
    exists: true
    uid: 498
    gid: 497
    groups:
    - nginx
    home: /var/lib/nginx
    shell: /sbin/nologin
group:
  nginx:
    exists: true
    gid: 497
process:
  nginx:
    running: true
```

### 実行する
`# goss validate`

```
...................

Total Duration: 0.040s
Count: 19, Failed: 0, Skipped: 0
```
MongoDBの運用周り備忘録

# MongoDBの運用周り備忘録

- 環境
  - AWS EC2
  - 構成: Primary / Secondary / Arbiter
  - OS: Amazon Linux
  - MongoDB: 2.6.11

## MongoDBのリングにノードを追加・削除する

- 構成からノードを外すコマンド(MongoDBのプライマリで実行
  - `>rs.remove("<MongoDBのIPアドレス>:27017");`
- 追加したノードをセカンダリから同期をする場合
  - `> SECONDARY> rs.syncFrom("<MongoDBのIPアドレス>:27017")`

- 不要になるMongoDBを切り離す
  - 下記のコマンドはPrimaryで実行する
  - `$ rs.remove("<MongoDBのIPアドレス>:27017");`


## Primary/Secondaryの重み付け設定をする

- Priorityの設定
  - 0 - 1000 の値で値が高い方が優先してプライマリに昇格する
  - 優先度が高いほうがプライマリとして動作する

- Configをセット
`> cfg = rs.conf()`

- 配列の数字は_idを入れる( 0は絶対にPrimaryにならない状態になる)
- プライオリティを10にセット
`> cfg.members[1].priority = 50`

- 設定を反映する
`> rs.reconfig(cfg)`

- 反映後の設定を確認する
`> rs.conf()`

```
testRep1:SECONDARY> rs.conf()
{
	"_id" : "testRep1",
	"version" : 11,
	"members" : [
		{
			"_id" : 0,
			"host" : "172.31.2.198:27017",
			"arbiterOnly" : true
		},
		{
			"_id" : 1,
			"host" : "172.31.7.138:27017",
			"priority" : 50
		},
		{
			"_id" : 2,
			"host" : "172.31.2.117:27017",
			"priority" : 30
		}
	]
}
```

## スロークエリを削除する

- `> db.currentOp()`
  - 実行中のクエリを表示する
    - `"desc" : "repl writer worker "`
        - レプリカのためのクエリなので削除してはいけない
- `> db.killOp(opid)`
  - クエリを削除する

## oplogについて

> 手順の一例を記載しますといいましたが、実際は障害のは発生したインスタンスをオンラインに戻せば自動的にデータは同期されます。
これには条件があり、 オンラインに復帰した MongoDB の oplog コレクションに格納されている同期用のデータが古くなっていないことが条件となります。oplogコレクションは capped collection(キャップ付コレクション) という種類のコレクションで、事前に定められたサイズを超えるデータが投入されると古いデータから順番に削除されるようなコレクションです。

- oplogを確認する
`PRIMARY> db.getReplicationInfo()`
Visual Studio Code で Laravel 開発する時に便利な拡張機能

- [PHP Extension Pack](https://marketplace.visualstudio.com/items?itemName=felixfbecker.php-pack) : PHP 用の拡張機能詰め合わせ
- [Laravel Extension Pack](https://marketplace.visualstudio.com/items?itemName=onecentlin.laravel-extension-pack) : Laravel 用の拡張機能詰め合わせ
- [PHP Namespace Resolver](https://marketplace.visualstudio.com/items?itemName=MehediDracula.php-namespace-resolver) : use の追加やクラス名をフルネームに変換する
- [PHP DocBlocker](https://marketplace.visualstudio.com/items?itemName=neilbrayfield.php-docblocker) : PHP Doc のコメントを簡単に作れる
- [Laravel goto view](https://marketplace.visualstudio.com/items?itemName=codingyu.laravel-goto-view) : blade ファイルへのハイパーリンク機能
- [Laravel goto controller](https://marketplace.visualstudio.com/items?itemName=stef-k.laravel-goto-controller) : コントローラ@アクション へのハイパーリンク
- [phpcs](https://marketplace.visualstudio.com/items?itemName=ikappas.phpcs) : 構文チェッカー
- [phpcbf](https://marketplace.visualstudio.com/items?itemName=persoderlind.vscode-phpcbf) : php の書式を整形する
- [DotENV](https://marketplace.visualstudio.com/items?itemName=mikestead.dotenv) : `.env` ファイルの編集を補助する
C#のコンソールアプリケーションで非同期処理をするときのメモ

・コンパイラ > Asyncなメソッドから呼び出せ
・解説サイト > Taskは使うな
・現実 > やってみたら即終了するじゃねぇか！

なお、例外の際デバッガの行表示はMainメソッドに戻ってきてしまう。
呼び出し履歴や、例外の行番号を確認すること

使い方

```C#
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApplication2
{
    class Program
    {
        static void Main(string[] args)
        {
            //コンソールアプリケーションからAsyncを呼び出す大元はTaskを使用する
            Task task = MainAsync();
            //終了を待つ
            task.Wait();
        }

        //AsyncなMain。ここでは非同期処理をawaitを使って同期的処理のように扱うことができる
        static async Task MainAsync()
        {
            await xxxxxxxxAsync();
        }
    }
}
```

#戻り値が欲しい場合
```C#
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApplication2
{
    class Program
    {
        static int Main(string[] args)
        {
            //コンソールアプリケーションからAsyncを呼び出す大本はTaskを使用する;
            return MainAsync().Result;
        }

        //AsyncなMain
        private static async Task<int> MainAsync()
        {
            await xxxxxxxxAsync();
            return 0;
        }
   }
}
```

#参考文献
Taskを極めろ！async/await完全攻略
https://qiita.com/acple@github/items/8f63aacb13de9954c5da

できる！C#で非同期処理(Taskとasync-await)
https://www.kekyo.net/2016/12/06/6186
Chrome/Firefox拡張をwebpack+Babelでいい感じにつくる雛形

# Chrome/Firefox拡張をwebpack+Babelでつくる雛形

タイトルのものを作りました。

- webpack + Babel(ビルド)
  - Chrome版とFirefox版を別々のビルドとして作成できる
      - 本体機能: src/options.js → options.js
      - 設定画面: src/content.js → content.js
- Jest(テスト)
- [.babelrc](https://github.com/wtetsu/webextensions-webpack-boilerplate/blob/master/.babelrc), [.eslintrc](https://github.com/wtetsu/webextensions-webpack-boilerplate/blob/master/.eslintrc) などが雰囲気だけある
- [GitHub Actionsによるテスト](https://github.com/wtetsu/webextensions-webpack-boilerplate/blob/master/.github/workflows/ci.yml)

## 経緯

これは、私が実際にリリースしたChrome/Firefox向け拡張から、そのアプリ特有の部分を削ったものです。なので、いちおう実績のある構成といっていいかと思います。今の構成に行き着くまでそれなりに紆余曲折があったので、次の拡張をつくるためのいい感じの出発点となれるべく、整理した感じです。

最小構成でも最強構成でもないですが、言うなれば、いい感じの小さい出発点、という感じでしょうか。

## ソース

https://github.com/wtetsu/webextensions-webpack-boilerplate


## 使ってみる

### 準備

```sh
git clone https://github.com/wtetsu/webextensions-webpack-boilerplate.git
cd webextensions-webpack-boilerplate
npm install
```

### ビルド(Chrome)

```sh
npm run build-chrome
```

dist-chromeにビルドが作成される。

それを chrome://extensions/ から読みこめばすぐさまブラウザ上で実際に試せる。

リリースは、上記をzipしたものを https://chrome.google.com/webstore/developer/dashboard から登録するだけ。


開発時は、以下を実行したままコードを変更すれば、勝手に再ビルドしてくれて便利。

```sh
npm run watch-chrome
```


### ビルド(Firefox)

```sh
npm run build-firefox
```

dist-firefoxにビルドが作成される。

それを about:addons から読みこめばすぐさまブラウザ上で実際に試せる。

リリースは、上記をzipしたものを https://addons.mozilla.org/developers/ から登録するだけ。


開発時は、以下を実行したままコードを変更すれば、勝手に再ビルドしてくれて便利。


```sh
npm run watch-firefox
```


### テスト

\_\_test\_\_ に追加してください。

普通にテスト。

```sh
npm run test
```

watchしておくと、変更したファイルに関連するテストだけ勝手に実行してくれて便利です。

```sh
npm run test-watch
```


### とりあえず実行した様子

alertが出ます。本当にそれだけです。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/106401/df5edf19-49c6-c091-802d-985661f9c8d1.png)

ちなみにここのコードはこうなっているのですが、関係ないブラウザの方はビルド時にifごと消え去ります。

```js
if (BROWSER == "CHROME") {
  alert("hello, world!(CHROME)");
}
if (BROWSER == "FIREFOX") {
  alert("hello, world!(FIREFOX)");
}
```
Scalaでforとかmapとかを理解するためにサンプルコードで試してみた

## 概要
最近、Scalaのソースを見るようになって、forとかmapとかをけっこう使ってるなと感じました。考え方としてはharry0000さんの[for式のforeach/flatMap(map)展開について](https://qiita.com/harry0000/items/e37ca3bfb68df839bf55)に書いてある内容なのですが、Javaとかと違って関数型特有の書き方の感じがするので、自分でサンプルコード作って動作を確認しました。

## 前提
事前に下記のようなInt型のOptionのListを用意します。この中身を取得するサンプルコードを作っています。

```
// OptionのListを用意
val optionList = List[Option[Int]](Some(1) ,Some(2) ,Some(3) ,Some(4))
```

## サンプルコード
[1_mapで展開して中身を1足してみる](#1_mapで展開して中身を1足してみる)
[2_foreachで展開して中身を1足してみる](#2_foreachで展開して中身を1足してみる)
[3_flatMapで展開して中身を1足してみる](#3_flatmapで展開して中身を1足してみる)
[4_flattenで展開](#4_flattenで展開)
[5_for式で展開して中身を1足してみる](#5_for式で展開して中身を1足してみる)


### 1_mapで展開して中身を1足してみる
Listの中身をmapで展開して1足したものを返すメソッドです。

```
  // mapで展開
  def usingMapPlusOne(optionList:List[Option[Int]]):List[Option[Int]] = {
    optionList.map(_.map(_ + 1))
  }
```

戻り値はList[Option[Int]]になるので、戻り値を出力するとこのような結果になります。

```
List(Some(2), Some(3), Some(4), Some(5))
```

### 2_foreachで展開して中身を1足してみる
Listの中身をforeachで展開して1足したものを出力するメソッドです。foreachは返り値を返さない（Unit）なので、取得したものをそのまま出力します。

```
  // foreachで展開
  def usingForaeachPlusOne(optionList:List[Option[Int]]):Unit = {
    // 出力用に1プラスしてprint
    def printPlusOne(num:Int):Unit = println(num + 1)
    optionList.foreach(_.foreach( printPlusOne(_) ) ) 
  }
```
結果は下記の通りとなります。

```
2
3
4
5
```
### 3_flatMapで展開して中身を1足してみる
Listの中身をflatMapで展開して1足したものを返すメソッドです。

```
  // flatMapで展開
  def usingFlatMapPlusOne(optionList:List[Option[Int]]):List[Int] = {
    // 出力用に1プラス
    def plusOne(num:Int):Option[Int] = Some(num + 1)
    optionList.flatMap( _.flatMap( plusOne(_) ) )
  }
```

mapとの違いはOption型ではなく、展開された状態のリストで戻り値が返ります。

```
List(2, 3, 4, 5)
```
### 4_flattenで展開
Listの中身をflattenで展開してみます。

```
  // flattenで展開
  def usingFlatten(optionList:List[Option[Int]]):List[Int] = {
    optionList.flatten
  }
```

基本的にはflatMapと機能的には同じですが、簡単に中身を展開できます。

```
List(1, 2, 3, 4)
```

### 5_for式で展開して中身を1足してみる
最後はfor式で展開して1足したものを返すメソッドです。

```
  // for式で展開
  def usingForPlusOne(optionList:List[Option[Int]]):List[Int] = {
    for{
      numberOption <- optionList
      number <- numberOption
    
    } yield{
      number + 1
    }
  }
```

これでfaltMapと同じ形式で、展開された状態のリストで戻り値が返ります。

```
List(2, 3, 4, 5)
```
Electronの環境構築とVisual Studio Codeでデバッグする方法

# 概要
環境構築からサンプルアプリをVisual Studio Codeでデバッグするまでの手順

# 事前準備
* Node.jsがインストールされていること

# 手順
## ディレクトリを作る
```
mkdir sample
cd sample
```

## package.jsonを作る
```
npm init
```

## electronをインストール
```
npm install electron --save-dev
```

## 基本となるファイルを作る
```
mkdir src
cd src
touch index.html
touch main.js
touch package.json
```
##### [ファイル構成]
- /node_modules
- /**src**
 - **index.html**
 - **main.js**
 - **package.json**
- package.json

### index.html
```
<html>
<head>
  <meta charset="UTF-8">
  <title>タイトル</title>
</head>
 
<body>
  <h1>Sample</h1>
</body>
</html>
```

### main.js
```
const electron = require('electron');
const app = electron.app;
const BrowserWindow = electron.BrowserWindow;

var config = require('../package.json');

const path = require('path');
const url = require('url');

let mainWindow;

function createWindow() {
  mainWindow = new BrowserWindow(
    {
      title: config.name,
      width: 800, 
      height: 600
    });

  mainWindow.loadURL(url.format({
      pathname: path.join(__dirname, '/index.html'),
      protocol: 'file:',
      slashes: true
  }));

  mainWindow.openDevTools();
  
  mainWindow.on('closed', () => {
    mainWindow = null;
  });
}

app.on('ready', createWindow);

app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit();
  }
});

app.on('activate', () => {
  if (mainWindow === null) {
    createWindow();
  }
});
```

### package.json
```
{
  "main": "main.js"
}
```

## コマンドでアプリ実行
#### Windows
```
./node_modules/.bin/electron src
```

#### Mac
```
npx electron src
```

## Visual Studio Codeのビルド設定（Windows/Mac共通）
1  左メニューの虫マークを押下してビルド画面を開く
![menu.png](https://qiita-image-store.s3.amazonaws.com/0/55157/0279d340-5ac7-43d9-3368-919636d43ec1.png)

2  「構成の追加...」を押下
![add.png](https://qiita-image-store.s3.amazonaws.com/0/55157/711df19e-1e92-16d2-9250-791f99e008db.png)

3 コマンドパレットのところに「Node.js」と入力
launch.jsonの雛型ができる

4  launch.jsonを編集

```
"configurations": [
   {
      "name": "Debug",
      "type": "node",
      "request": "launch",
      "cwd": "${workspaceRoot}",
      "program": "${workspaceRoot}/src/main.js",
      "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron",
      "runtimeArgs": [
          "--enable-logging"
      ],
      "args": [
          "."
      ],
      "windows": {
          "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron.cmd"
      },
      "console":"integratedTerminal",
   }
]

```
5  デバッグ
「Debug」になっているのを確認して再生ボタンを押下
![build.png](https://qiita-image-store.s3.amazonaws.com/0/55157/bd051d20-1118-d0a5-3410-b8d0854052ba.png)
画像クラスの違い(UIImage vs CGImage vs CIImage)

#1 UIImage,CGImage,CIImageそれぞれの違い
<img width=700 src="https://qiita-image-store.s3.amazonaws.com/0/256340/ac16ca45-444a-825c-746d-5a99d2b6c246.png">

###1.1 UIImage
 - UIKitフレームワークのクラス。
 - 主に既存の画像を使った処理を行う。
 - 長さや位置の指定は "point" 単位で行う
 - 主にviewとのやりとりに使用する

###1.2 CGImage
 - CoreGraphicsフレームワークのクラス。
 - オリジナルの図形などを描画したいときに使用する。
 - 長さや位置の指定は "pixel" 単位で行う
 - 画像加工やピクセル単位の処理を行う場合はだいたいこいつを使う(IN/OUTがCGImageになる)。

<img width="400" alt="qiita-square" src="https://qiita-image-store.s3.amazonaws.com/0/256340/da19ab4a-1a2f-05f6-7b9d-e43005f8218a.png" >

###1.3 CIImage
 - CoreImageフレームワークのクラス。
 - 画像にフィルタ/エフェクトをかけるときに使用する。
 
<img width=600 src = "https://qiita-image-store.s3.amazonaws.com/0/256340/1c830506-97c7-6d3e-cd59-79795ecebc7e.png">

参考記事　[1]
#2 各画像クラス変換コード
###2.1 UIImage -> CGImage

```swift:
let cgImage = uiImage.CGImage
//UIImageからCGImageへの変換時に方向パラメータを喪失する。
//画像の向きがおかしくなるためUIImage.imageOrientationを保持しておかなければならない
//https://qiita.com/YutoMizutani/items/de6c0f2200bdbab10624
```
参考記事　[２]
###2.2 CGImage -> UIImage

```swift:
let uiImage = UIImage(CGImage: cgImage)
```
参考記事　[２]
###2.3 UIImage -> CIImage

```swift:
let ciImage = uiimage.CIImage
```
参考記事　[２]
###2.4 CIImage -> UIImage

```swift:
let uiImage = UIImage(CIImage: cgImage)
```
参考記事　[２]
###2.5 CGImage -> CIImage

```swift:
let ciImage = CIImage(CGImage: cgImage)
```
参考記事　[２]
###2.6 CIImage -> CGImage

```swift:
let context = CIContext(options: nil)
let cgImage = context.createCGImage(ciImage, fromRect: ciImage.extent())
```
参考記事　[２]
#3 ローカルファイルに対する読み書き
未完成の項目です。
UIImage経由で読み書きを行なっているものが多かった気がしますが、？？？に入るコードをご存知の方はコメントして頂けると幸いです。
###3.1 UIImage->URL
```swift:
let pngImageData = UIImagePNGRepresentation(image) 
pngImageData!.write(to: URL(fileURLWithPath: path), options: .atomic)
```
参考記事　[3]

###3.2 URL->UIImage
```swift:
let image = UIImage(contentsOfFile: path)
```
参考記事　[3]
###3.3 CGImage->URL
？？？
###3.4 URL->CGImage
？？？
###3.5 CIImage->URL
？？？
###3.6 URL->CIImage
```swift:
let ciimage = CIImage(contentsOf: url)
```

#参考記事
>[[1] Qiita Swift画像処理の基礎知識 〜iOS標準の画像クラス・フレームワーク〜](https://qiita.com/jok/items/7c9e384abb29c161f56b)
>[[2] Qiita [UIImage] <-> [CGImage] <-> [CIImage] In Swift](https://qiita.com/imanishisatoshi/items/34d1e488d412cd23fd8d)
>[[3] Qiita 【Swift4】URL先の画像をアプリ内に保存＆ロードする](https://qiita.com/orimomo/items/a22ab921decd41df7dfa)
helmを再セットアップする

```shell
$ helm reset --force
Tiller (the Helm server-side component) has been uninstalled from your Kubernetes Cluster.
```
```shell
$ cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ServiceAccount
metadata:
  name: tiller
  namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  name: tiller
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
  - kind: ServiceAccount
    name: tiller
    namespace: kube-system
EOF
serviceaccount/tiller created
clusterrolebinding.rbac.authorization.k8s.io/tiller created
```
```shell
$ helm init --service-account tiller --upgrade

$HELM_HOME has been configured at /Volumes/Data/tk_hamaguchi/.helm.

Tiller (the Helm server-side component) has been installed into your Kubernetes Cluster.

Please note: by default, Tiller is deployed with an insecure 'allow unauthenticated users' policy.
To prevent this, run `helm init` with the --tiller-tls-verify flag.
For more information on securing your installation see: https://docs.helm.sh/using_helm/#securing-your-helm-installation
Happy Helming!
```
```shell
$ helm version
Client: &version.Version{SemVer:"v2.11.0", GitCommit:"2e55dbe1fdb5fdb96b75ff144a339489417b146b", GitTreeState:"clean"}
Server: &version.Version{SemVer:"v2.11.0", GitCommit:"2e55dbe1fdb5fdb96b75ff144a339489417b146b", GitTreeState:"clean"}
```
Cloud Formationでnginx+wordpress環境を用意する

## 作った動機
cloudformatonを使ったことがなかったというのもあり、勉強がてら触ってみることにした。
特にサーバ環境とかを考慮しないのであれば、wordpress用のCFNテンプレをAWSが用意してくれているので問題無いんですが、LAMP構成（あんまり好きじゃないｗ）だったり、phpが古かったりで、あまり今の構成のまま本番運用に使えないなーって感じ、新しく作ることに。
また、弊社ではwordpressを使う開発も結構多いので、作っておいて運用まで使えれば問題ないかなとも思って作成しました

## Cloudformationとは？
Amazon Web Servicesリソースのモデル化およびセットアップに役立つサービス。
インフラストラクチャ管理がコード化されているため、簡略化できすばやく複製ができちゃうすばらしいサービスです。

## 前提条件
・VPCの知識
VPCはいちいち作り直す事もないかなーと思ったので、今回のテンプレートには、VPC環境を1から作るようなフォームは用意していません。なので、VPC環境は、事前に用意しておいてください。
・Route53にHostedZoneの設定
Route53にDNSの設定をするとドメインも設定できるようになってます

・AWSの簡単な知識
基本的にこのテンプレートを使えば誰でも構成できるようにしているつもりですが、VPCも然りAWSの知識も少し知ってないと使うことが少し厳しいかもしれません

・LB・CF/ACMの準備
ACMを使用したSSLの設定を前提として作成してますが、cloudfront/LBにどちらにぶら下げるかはプロジェクト次第かと思ったので、今回はCFNの設定に組み込みませんでした。
プロジェクトごとにどちらを使用するかを決めて対応するようにしてください。

## 使用環境
PHP ver7.1
php fpm
nginx 
Mysql ver5.7

### 参考にしたサイト
以下のリンクのテンプレートを参考に作成させていただきました。
https://github.com/stelligent/cloudformation_templates/tree/master/labs/wordpress
リンクのテンプレでも充分に使えるんですが、以下の理由からカスタマイズを行いました。
・構築済みRDSを使用する前提だったので、テンプレ内でDBが自動生成されない（今回はローカルDBを構築するように設定を変更しました）
 ・PHP, Mysqlのバージョンを最新にしたい
・EFSは不要なので消したい


## 作成環境
![AWS Networking.png](https://qiita-image-store.s3.amazonaws.com/0/106177/fe09b350-11fd-829f-07b5-bccbef3d3009.png)


## テンプレート
テンプレートは以下に公開してます
https://github.com/katyou/cloudfront-wordpress-nginx/blob/master/wordpress-nginx.yml

あとはGOするだけです！
[![Wordpress Lab](https://s3-ap-northeast-1.amazonaws.com/cf-templates-7wwmrrgy23ei-ap-northeast-1/cloudformation-launch-stack.png)](https://console.aws.amazon.com/cloudformation/home?#/stacks/new?&templateURL=https://s3-ap-northeast-1.amazonaws.com/cf-templates-7wwmrrgy23ei-ap-northeast-1/wordpress-nginx.yml)<br>
Nginxでメンテナンスページの設定をする

# Nginxでメンテナンスページの設定をする

Nginxでメンテナンスページを表示する設定について


## 環境
- OS 
  - Ubuntu 16.04.4 LTS
- nginx
  - nginx version: nginx/1.10.3 (Ubuntu) 

## 設定
- maintenance.htmlを配置する
  - `/var/www/html/maintenance.html`
- 503レスポンスのときに動作を定義する
  - `error_page 503 @maintenance;`
- `@maintenance`の処理を定義する
  - `rewrite ^(.*)$ /maintenance.html break;`

- maintenance.htmlのファイルが存在したら、503を返すように設定し、`@maintenance`の処理を実行させる
  - メンテナンス時に`maintenance.html` をセットする 

```
if (-e /var/www/html/maintenance.html) {
      set $maintenance true;
}
if ($maintenance = true) {
      return 503;
}
```


```/etc/nginx/sites-enabled/test.conf
  1 server {
  2   listen 80 default_server;
  3   listen [::]:80 default_server;
  4
  5   root /var/www/html;
  6   index index.html index.htm index.nginx-debian.html;
  7   server_name X.X.X.X;
  8   error_page 503 @maintenance;
  9
 10   if (-e /var/www/html/maintenance.html) {
 11         set $maintenance true;
 12   }
 13   if ($maintenance = true) {
 14         return 503;
 15   }
 16
 17   location / {
 18     try_files $uri $uri/ =404;
 19   }
 20
 21   location @maintenance {
 22     rewrite ^(.*)$ /maintenance.html break;
 23   }
 24 }
```

- curlで叩くと以下がレスポンスがかえってきた

```
<html>
<p> This is Maintenance Page </p>
</html>
```
Babel 7 へのアップグレードにあたっての修正点

## これはなに？

とあるリポジトリを Babel 6 から Babel 7 にアップグレードするにあたって、公式のアップグレードガイドを読んで修正が必要だった箇所をまとめたものです。網羅的に記載している物ではないので、細部は公式のアップグレードガイドを参照してください。

## 公式のアップグレードガイド

- [Upgrade to Babel 7](https://babeljs.io/docs/en/v7-migration)

あわせて [Babel 7 Released](https://babeljs.io/blog/2018/08/27/7.0.0) も読むと、今回のリリースの背景等も知ることができて面白いでしょう。

## .babelrc の有効範囲の変更

`.babelrc` に設定した内容は `package.json` ファイルを含むディレクトリ配下には適用されなくなっています。 `node_modules` 配下も同様の制限を受けます。これは monorepo のように複数のパッケージを管理する際の拡張性に配慮した変更のようです。

一方で `babel.config.js` を利用する場合にはこの制限を受けません。 monorepo のような特殊な要件が必要なければ `babel.config.js` への移行を検討して良いでしょう。

[Config Files · Babel](https://babeljs.io/docs/en/config-files)

## パッケージのリネーム

公式で提供される各パッケージは scoped packages として提供されるようになりました。今後は公式で提供されるパッケージと、コミュニティーによってメンテナンスされているパッケージとがより区別しやすくなります。

```diff
- babel-preset-env
+ @babel/preset-env
```

変更点が多い場合は [babel-upgrade](https://github.com/babel/babel-upgrade) というアップグレードをサポートするツールが提供されているので、こちらを使ってみても良いでしょう。

## プロポーザルな仕様をサポートするプラグインのリネーム

プロポーザルな仕様をサポートするプラグインの名前に `-proposal-` が追加されるようになりました。

```diff
- @babel/plugin-transform-class-properties
+ @babel/plugin-proposal-class-properties
```

## 各種プリセットは非推奨となった

- babel-preset-es2015
- babel-preset-es2016
- ...
- babel-preset-stage-0
- babel-preset-stage-1
- ...

などのプリセットは非推奨となりました。代わりに `@babel/preset-env` に移行して、 プロポーザルな仕様をサポートしたい場合は個別にプラグインを利用する必要があります。

## babel-polyfill はプロポーザルな機能をデフォルトで提供しなくなった

プロポーザルな仕様を polyfill したい場合は個別に import する必要があります。

```javascript
import "core-js/fn/array/flatMap";
```

## Flow をサポートする独立したプラグインの提供

これまでは `babel-preset-react` に [Flow](https://flow.org/) のサポートが含まれる形で提供されていましたが、Babel 7 からは独立したプラグインとして提供されるようになりました。なおこの変更は Babel 7 が TypeScript をサポートにするにあたり、複雑さを回避するための措置のようです。

```javascript
- "presets": ["@babel/preset-react"]
+ "presets": ["@babel/preset-react", "@babel/preset-flow"]
```

## babel-node コマンドの提供元パッケージの変更

`babel-node` コマンドは `babel-cli` パッケージの一部として提供されていましたが、Babel 7 からは分離され `@babel/node` パッケージとして提供されるようになりました。もし利用している場合は追加でインストールする必要があります。

なお `babel-node` というコマンド自体の名称は変更されていません。

## babel-core/register は非推奨となった

`babel-core/register` は非推奨となりました。代わりに `@babel/register` が提供されているのでこちらに移行する必要があります。 mocha から利用している場合は以下のような修正が必要です。

```javascript
- mocha --compilers js:babel-core/register
+ mocha --compilers js:@babel/register
```

## その他パッケージのアップグレード

コミュニティーによって提供されるパッケージもアップグレードが必要なケースがあります。関連パッケージのリリースノートや CHANGELOG をひととおり確認した方が良いでしょう。

例:

- [babel-loader](https://github.com/babel/babel-loader/releases/tag/v8.0.0)
- [babel-eslint](https://github.com/babel/babel-eslint/releases/tag/v9.0.0)
- [babel-preset-power-assert](https://github.com/power-assert-js/babel-preset-power-assert/releases/tag/v3.0.0)
Railsでmigrate時にER図を自動生成する設定

## 目的
- ドキュメントとシステムが乖離する要因を消す。
- 視覚的に把握する。
- 共有の手間をなくす。

## 手順
1. graphvizをインストールする
2. gemを指定する
3. タスクを定義する

##1. graphvizをインストールする
```
# ディストリビューションに合わせて
yum install graphviz
　or
apt-get install graphviz
```


##2. gemを指定する

```ruby:Gemfile

group :development do
  gem 'rails-erd'
end
```

##3. タスクを定義する

```ruby:Rakefile
require_relative 'config/application'

Rails.application.load_tasks

# migrateのタスクをフックする
Rake::Task['db:migrate'].enhance do
  if Rails.env.development?
    Rake::Task[:after_migrate].invoke
  end
end

# migrateの後のタスク
task :after_migrate do
  Rake::Task[:create_erd].invoke
end

# ER図を作成
task :create_erd do
  # attributes=foreign_keys,primary_keys,timestamp (属性は主キー、外部キー、タイムスタンプを表示)
  # sort=false (カラム名をアルファベット順にしない)
  # filename=hage-erd (ファイル名)
  # filetype=png (ファイル拡張子)
  sh 'bin/rake erd attributes=foreign_keys,primary_keys,content,timestamp sort=false filename=hage-erd filetype=png'
end
```

# 実行する
```
rails db:migrate
```
Firebase Realtime Database 並べ替えられた(queryOrdered)結果の受け取りかた 

Firebase Realtime Databaseで`queryOrdered`メソッドで並べ替えられたデータを`DataEventType.value`で取得する。

`observe`メソッドで`DataEventType.childAdded`などのchildイベントをリッスンすると子要素ごとにコールバックが発生してしまう。
`DataEventType.value`ではすべての子要素を含んだsnapshotでコールバックが発生する。
並べ替えられた子要素には`DataSnapshot.children: NSEnumerator`プロパティでアクセスできる。`DataSnapshot.value`の子要素は並べ替えられていない。


```swift:example
Database.database().reference()
    .child("users").child(user.uid)
    .queryOrdered(byChild: "created")
    .observe(DataEventType.value) { (snapshot) in

        // 子要素は並べ替えられていない
        print(snapshot.value)

        // 並べ替えられた順に子要素が反復される
        for case let child as DataSnapshot in snapshot.children {
            print(child.value)
        }
}
```



```swift
import Firebase

extension DataSnapshot {
    
    var childSnapshots: LazyMapSequence<NSEnumerator, DataSnapshot> {
        return self.children.lazy.map { $0 as! DataSnapshot}
    }
  
}
```
includesで一度に孫のデータまで取得する方法【ActiveRecord】

Railsでは、`includes`メソッドを使うことでモデル取得時に関連するモデルのデータも取ってくることができる。

これは下記のように記述することで孫の関係にあるモデルも取得することができる。


```controller.rb
@user = User.includes(:first_children, :second_children, :third_children).includes(:first_children => :grand_child).find(1)
```

```user.rb
class User < ApplicationRecord
  has_many :children
end
```

```first_child.rb
class FirstChild < ApplicationRecord
  has_many :grand_children
  belongs_to :user
end
```

```second_child.rb
class SecondChild < ApplicationRecord
  belongs_to :user
end
```

```third_child.rb
class ThirdChild < ApplicationRecord
  belongs_to :user
end
```

```grand_child.rb
class GrandChildren < ApplicationRecord
  belongs_to :child
end
```
CQRSとDynamoDBのデータ構造設計

## 概要

最近DynamoDBで設計を行っています。DynamoDBは、NoSQLであり様々な制約があります。ですので、RDBの設計とは異なる考え方をしなければなりません。

四苦八苦しながら、設計をしていて、何とか形になってきています。そんな中、CQRSという考え方があることを知りました。自分が何となくやっていたことと近いこともあり、改めてCQRSとDynamoDBのデータ構造設計についてまとめてみました。


## CQRSとは

CQRSは「Command Query Responsibility Segregation：コマンドクエリ責任分離」という意味です。

DBへの書き込み(Command)と読み込み(Query)を別物として捉える考え方です。

書き込み用と読み込み用のそれぞれのDBを分離するイメージですね。


RDBのリードレプリカに似ています。  
リードレプリカで、書き込み用DB(マスター)と読み込み用DB(スレーブ)があります。マスターとスレーブではデータ構造(スキーマ)は完全に同じです。  
それに対してCQRSでは、書き込み用と読み込み用でデータ構造が異なっていることを許容していると言えます。


## DynamoDBでのCommand用データ構造

DynamoDBには、以下のような特性があります。

- NoSQLなのでRDBのようなトランザクションがない
  - 複数レコードにまたがる更新にアトミック性を保持することができない
- 1レコードの更新はアトミック性がある

この特性を考慮すると、Command用データ構造を設計する場合、
アトミック性を保持したいデータは1レコードに収められるようなデータ構造にしておく必要があります。


## DynamoDBでのQuery用データ構造

またDynamoDBには、以下のような特性もあります。

- 1テーブルにつき作成できるインデックス数が限られている(GSI/LSIそれぞれ5個まで)
- ソートするカラムはインデックスが貼られている必要がある(RangeKey)

これらの制約を考慮し、データの取得パターンに応じて、最適なデータ構造(Query用データ構造)を作っておく必要があります。

例えば、以下のようなアクセスパターンがあるでしょうか。

- ソート/検索/ページネーションを行い、データの一覧を取得する
- グループに所属しているユーザー数を取得する(あらかじめ集計しておくなど)

Query用のDBはDynamoDBに限らず、Elasticsearchを用いても良い場合もありそうです。


## CommandデータからQueryデータへの変換について

CommandデータからQueryデータを作成する必要があります。これはDynamoDB Stream経由で、Lambdaを実行して行うのが良さそうです。作成失敗した時はLambdaが再試行するので、ほぼ変換は成功させることができます。


## その他の考察

どうしてもトランザクションが欲しい場合は、Kinesis経由でRDBを用いてデータを更新することも可能だと思います(Command)。
更新が成功したら、DynamoDBやElasticsearchにQuery用データを作成することができます。


## 参考


【AWS re:Invent 2017 レポート】サーバレスアプリケーションのデータレイヤーの最適化 #SRV301
https://www.yumemi.co.jp/ja/tips/tips2018/reinvent2017_03

CQRSとイベントソーシングの使用法、または「CRUDに何か問題でも？」
https://postd.cc/using-cqrs-with-event-sourcing/

DynamoDBデータモデリング虎の巻：第壱巻 〜前提知識編〜
http://marcy.hatenablog.com/entry/2018/07/31/213705
ポリモーフィズムはいいぞ


## 前置き
オブジェクト指向をあまり理解できていないと前から感じていたので、ちょうどKindleの50%セールで買った[オブジェクト指向でなぜつくるのか 第2版](https://www.amazon.co.jp/dp/B00ISP0QB2/ref=cm_sw_r_tw_dp_U_x_dd3TBbF9YEHX7)を読みました。

そこでオブジェクト指向プログラミングの3大概念であるクラス、ポリモーフィズム、継承についてしっかりと学んだのですが、クラスや継承は知っていてもポリモーフィズムは意味すらしりませんでした。

この記事ではポリモーフィズムという考えかたの簡単な説明（をしているqiita記事の紹介）、どういうメリットがあるか、またPython3を用いてポリモーフィズムに基づいたコードを実際に書いてみようと思います。

## そもそもポリモーフィズムとは？
[プログラマー１年生がポリモーフィズムについて学んだのでＲＰＧで説明する。](https://qiita.com/Nossa/items/b6e2f4ed0fa079359fc5#%E6%8A%BD%E8%B1%A1%E3%82%AF%E3%83%A9%E3%82%B9%E3%81%A8%E3%82%A4%E3%83%B3%E3%82%BF%E3%83%BC%E3%83%95%E3%82%A7%E3%83%BC%E3%82%B9%E3%81%AE%E4%BD%BF%E3%81%84%E5%88%86%E3%81%91)
この記事の『ポリモーフィズムってなんだ？』のところにわかりやすい説明が載っています。

## なんでこの程度の考え方が３大概念の１つとか言われるのか？
ポリモーフィズムは、言ってしまえば、『異なるクラス間の類似したメソッドの名前を統一しただけ』です。

なぜこの程度のことが３大概念の１つとまで言われているのでしょうか？

プログラミングの入門書においても、オブジェクト指向について説明するときにクラスや継承と違って、ポリモーフィズムの説明はしない本もあります。僕がプログラミングを始めたときに読んだPythonの入門書が実際にそうでした。

ポリモーフィズムが役に立つ具体的な開発場面を想像していきましょう。

##### 例
あなたはとあるゲームの制作にかかわっています。

このゲームは規模が大きくとても1人ではできるものではないので複数でチームを組んでゲーム制作をしています。（あくまで例です。僕はゲーム制作の経験はないです。）

キャラクターの行動設定をAさん担当していて、攻撃があったときの戦闘処理をあなたが担当していたとします。

ここでは役職が3つあって剣士なら攻撃メソッドがslash、魔術師なら攻撃メソッドがcast、武闘家だったら攻撃メソッドがpunchだったとしましょう。

あなたは、役職ごとに対応するslash、cast、punchメソッドを呼び出して、相手に1ダメージを与えるという処理を書きました。

ここでAさんが新しい役職の銃士を定義して攻撃メソッドをshootとするようにコードを書き加えた場合、<b>あなたもまたshootメソッドが呼び出して処理（相手に1ダメージ与える）が行われるように、自分が書いたコードを修正する必要があります。</b>

この例なら書き直す部分は少ないですが、<b>大規模開発ではコードが膨大なのでコードの書き直しは思わぬ致命的なバグを生むこともあります。</b>

ここでAさんとあなたでポリモーフィズムの考え方に基づいて、役職にかかわらず攻撃メソッドはattackと名付け、あなたは攻撃の際にattackメソッドを呼び出して戦闘処理をするようにコードを書いておけば、Aさんが新しい役職を付け加えても、あなたはコードを書き直す必要がないため、効率性の面でもセキュリティの面でも良いことばかりということなのです。

## 実際に書いてみよう Python3編
今回は、Python3の組み込みライブラリであるabcライブラリを使ってポリモーフィズムの考え方に基づいたクラス定義をしてみようと思います。

abcライブラリは、抽象クラスを定義するためのライブラリです。(おそらく<b>AB</b>stract <b>C</b>lassからabc)

抽象クラスについての説明は、ここではしません。
Javaですが、[【納得Java】抽象クラス(abstract)を使うメリット](https://www.sejuku.net/blog/22689) にわかりやすく説明が載っています。

それでは、ポリモーフィズムの考え方に基づいてクラス定義をしましょう。

``` sample.py
from abc import ABCMeta, abstractmethod

#元となる人間クラス
class Human(metaclass = ABCMeta):
    
    #抽象メソッド
    @abstractmethod
    def attack(self):
        pass
    
    #共通メソッド
    def defend(self):
        return "防御"

#受け継ぎ先のクラス
class Saber(Human):
    def attack(self):
        return "斬撃攻撃"

class Archer(Human):
    def attack(self):
        return "射撃"

class Caster(Human):
    def attack(self):
        return "魔術攻撃"
```

抽象クラス(サンプルコードではHumanクラス)を定義するにはインポートしたABCMetaを継承します。

また抽象メソッドを定義するには、デコレーターとしてインポートしたabstractmethodでメソッドを修飾してあげます。

今回は上の例にならってメソッド名をattackにします。

そしてその後の具体的な役職でこのHumanクラスをインポートしてattackメソッドをオーバーライドしています。

こうすることで役職ごとの攻撃メソッドをattackで統一することができます。

ここで抽象クラスをわざわざ継承しなくても、個別に役職クラスを定義して攻撃メソッドの名前をattackにすればいいのでは？
と思うかもしれません。

しかし、サンプルコードと違って実際の開発場面ではHumanクラスが役職クラスたちに共通のインスタンス変数やメソッド(サンプルコードではdefendメソッド)をたくさん持っていることが多いので、Humanクラスでこの共通変数・メソッドを定義してから具体的な役職でHumanクラスを継承することで役職ごとに共通変数・メソッドを繰り返し定義する手間が省けるのです。

## まとめ
以上でポリモーフィズムの簡単な説明を終えます。僕自身も勉強したばかりなので間違っているところがあるかもしれませんが、そこはおおめにみてくだしあ
pyenvでPythonのバージョン管理

Python で開発したいけど、ローカル環境を汚したくないので、バージョン管理ツールをつかって Python を入れようと思います。

今回は pyenv を使って導入します。

[https://github.com/pyenv/pyenv](https://github.com/pyenv/pyenv)

## 環境

* macOS 10.14.6
* zsh 5.3 (x86_64-apple-darwin18.0)

## Homebrew インストール

公式サイト https://brew.sh/index_ja からインストール

## pyenv インストール

```
# Homebrew でインストール
$ brew install pyenv
```

インストール後に `~/.zshrc` に追記が必要。

``` bash:.zshrc
...
export PYENV_ROOT="$HOME/.pyenv"
export PATH="$PYENV_ROOT/bin:$PATH"
eval "$(pyenv init -)"
...
```

追記後、おまじないコマンド

```
$ source ~/.zshrc
```

## python インストール

```
# v3.6.5 インストール
$ pyenv install 3.6.5

# 使用する python の設定
$ pyenv global 3.6.5

# 開発リポジトリ以下の python を指定する場合
$ cd /path/to/repository
$ pyenv local 3.6.5
```

これで、python の設定はおしまい。
TouchDesigner Particle SOPを使ってみる

TouchDesignerでもやってて楽しいPartile SOPの使い方を見てみます。
↓こんな感じのパーティクルが発生するようにしてみます。
![all_1.png](https://qiita-image-store.s3.amazonaws.com/0/31194/f93f0a76-38cb-b535-2d04-28e9e45dc854.png)


ネットワークは下図のようになります。
![all_2.png](https://qiita-image-store.s3.amazonaws.com/0/31194/f5389be8-3dff-adac-8c29-fdb3443a37b6.png)
Grid SOP > Transform SOP > Sort SOP > Particle SOP > Geometry（Mat）

###Grid SOPのパラメーター
<img src="https://qiita-image-store.s3.amazonaws.com/0/31194/8af72090-6248-b212-5003-45cd1ba3f8b4.png" width=50%>
**Orientation**を**ZX Plane**にし、Y軸に沿ってパーティクルを発生させるようにしています。

**Compute Nomarls**を**OFF**にしていますが、ONのままだとパーティクルが法線方向に投射されてしまうので、OFFにしています。

また、RowsとColumnsの数を変えると縦横のPointの数が変わるので、色々変化させられると思います。


###Transform SOPのパラメーター
<img src="https://qiita-image-store.s3.amazonaws.com/0/31194/029a9626-abf7-3cb9-66df-d50427ef95b2.png" width=50%>
ここは特に変えていないのですが、**Uniform Scale**を大きくすると最終的にPaticle発生の形が大きく変わるので、作りたい絵に合わせて変更しても面白くなります。

###Sort SOPのパラメーター
<img src="https://qiita-image-store.s3.amazonaws.com/0/31194/ad286364-85c8-48eb-8426-fd3299915745.png" width=50%>
**Point Sort**を**Random**にしています。
ここをDefaultのままにしておくと、**パーティクルがGrid SOPのPointの順番に発生してしまう**ので、パーティクルっぽく発生順をランダムにしておきます。

###Particle SOPのパラメーター
<img src="https://qiita-image-store.s3.amazonaws.com/0/31194/a2105292-54ca-d5d6-4747-8989bfca68ab.png" width=50%>
Patcile SOPは色々いじれるパラメータがあります。
まずは**Stateタブ**で**Particle Type**を**Render as Point Sprites**にします。
デフォルトのRender As Lineだと形状がLineになるので、粒子になるように変更します。

<img src="https://qiita-image-store.s3.amazonaws.com/0/31194/c09d00b7-d6c3-293d-74ff-62bbbd20f727.png" width=50%>
次に**Forcesタブ**です。
ここはParticleに対する外からの力を設定します。これで動きがかなり変わってきます。
今回は**External Force**の**Y**に-1を加えて下方向に投射させています。
また**Turbulence**は乱気流のような動きをさせられるパラメータで、それぞれに値を設定しました。

<img src="https://qiita-image-store.s3.amazonaws.com/0/31194/fe6fbfde-3cab-12be-5e5e-bfe943d7c5ad.png" width=50%>
そして**Particlesタブ**では、パーティクル自体の設定をします。
**Birth**は毎秒投射されるパーティクルの数、**Life Expect**はパーティクルの生存期間です。

GeotmetoryとMaterialは単に表示用に繋いだので、ここはなんでも良いと思います。
以下にYouTubeのリンクを貼っておきます。
パーティクルは楽しいですね:satisfied:
[![IMAGE ALT TEXT HERE](https://qiita-image-store.s3.amazonaws.com/0/31194/a4210fcb-a562-0131-b6fd-5964be909729.png)](https://www.youtube.com/watch?v=9KkoKxJWq50)
Railsのビューで、マスター供給用のJavaScriptを生成する

Webシステムを作っていると、データベースにマスターを入れることもありますが、それをJavaScript側にどのように引き渡すかも、悩ましいものです。

なお、ここでは「管理画面からならマスターは更新できる」というものではなく、「デプロイ時にシードするだけで、Railsから変更することはない」ようなマスターについて考えます。

# いくつかの手法

このようなことをしたい場合にも、いくつか方法が考えられます。

## JSON APIを用意する

いちばん素直に考えれば、APIを叩いてJSONを返すようにすることでも値の受け渡しは可能です。

* メリット…サーバサイドはシンプルに実装できる
* デメリット…同期的な取得ができない、使うJavaScriptのコードが煩雑になる

## JavaScriptに書き出す

Sprocketsや`rails-erb-loader`などはRails環境で`.js.erb`を実行できますので、事前に必要な値を書き出したJavaScriptを生成しておく、という手段もあります。

* メリット…事前にJavaScriptにまでしておくため、実行時の負担は少ない（Turbolinksにも載せられる）
* デメリット…デプロイ環境にもDBの準備が必要になる、`rails-erb-loader`が別プロセスの呼び出しになるなどデプロイ処理が複雑化する

## ビューに書き出す

ビューに適当な方法で値を埋め込んで、それを読み取って使う、という方法もあります。

* メリット…ふつうのビューとJavaScriptだけで、別に処理を用意する必要がない、値は動的に生成するけど同期的な取得も可能
* デメリット…使うページごとに値を書き出していくと、ビューが肥大化する

## JavaScriptを返すビューを用意する

今回は、この方法について取り上げます。

* メリット…Railsで制御するので自由度が高い
* デメリット…自由度が高い分、正しく作るのが難しい

# JavaScriptのビュー

Railsのビューはフォーマットごとに用意できるので、適切なコントローラーの`show`アクションに対応するように`show.js.erb`を作ればいい…とも思えるのですが、そのままでは`ActionController::InvalidCrossOriginRequest`となってしまいます。

## CSRF対策→止める

JSONPなどにも使われている技ですが、`<script>`に書かれた`src`のURLは、クロスオリジンであっても読み込み、実行が可能となっています。そのため、`<script>`として実行されうる、GETリクエストに対してJavaScriptを返すアクションは、`protrect_from_forgery`が有効な場合、`ActionController::InvalidCrossOriginRequest`を起こすようになっています。

とはいえ、今回のようなマスター系を前提とした場合、CSRFは問題となりません。`protrect_from_forgery except: :show`のように上書きして、CSRF対策自体を止めましょう。

## キャッシュの活用

そのままやっていては、毎回毎回データベースを読んでJavaScriptとして吐き出すことになって、効率が悪すぎます。

* ビューレベルのキャッシュ→`<%= cache do %>...<% end %>`を活用して、出来上がったビューをキャッシュしておきましょう。
* ブラウザキャッシュ→コントローラーで`expires_in`をかけると、キャッシュヘッダの調節ができます。マスター更新時のキャッシュ無効化のために、適当なキーを指定しておいてもいいかも知れません。
* Turbolinks→ビューとして生成したJavaScriptにも、Turbolinksは問題なく適用できます。`data-turbolinks-track="reload"`（Turbolinks 5の場合）を設定しておきましょう。
Amazon linux 2に、EOSを入れる。

# Amazon linux 2に、EOSを入れる。


## 1. とりあえず、dockerをいれる。

```
yum update
yum install docker
service docker start
```

## 2. EOSのdocker imageをpullする。

```
docker pull eosio/eos
```

## 3. gccとg++とcmakeをインストールする。

```
yum install gcc gcc-c++
wget https://cmake.org/files/v3.12/cmake-3.12.3.tar.gz
tar cmake-3.12.3.tar.gz && cd cmake-3.12.3/
./bootstrap && make && make install
```

## 4. boostをいれる。

```
cd
wget https://sourceforge.net/projects/boost/files/boost/1.68.0/boost_1_68_0.tar.gz
tar xvzf boost_1_68_0.tar.gz
cd boost_1_68_0
./bootstrap.sh
./b2 install
```

## 5. EOS Contract development kitをインストールする。

```
cd
yum install git
git clone --recursive https://github.com/eosio/eosio.cdt
cd eosio.cdt/
```

ここで、build.shの中身を修正する。

- 修正前

```
Amazon Linux AMI
```

- 修正後

```
Amazon Linux
```

bootrstrapのincludeファイルをコピーする。

```
cp -rfp boost_1_68_0/boost/* ~/eosio.cdt/libraries/boost/include/boost/
```

ビルドする。

```
./build.sh SYS
```

インストールする。

```
./install.sh
```
[Azure] Visual Studio CodeでAzure Functionsのデバッグができないとき

下記のURLにある手順に従っても、うまくいかないんです。
という時の対処方法です。

Deploy to Azure using Azure Functions
https://code.visualstudio.com/tutorials/functions-extension/getting-started

# 現象

Visual Studio Codeでデバッグ実行すると、下記メッセージが表示されます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/59475/83d3f37c-d424-24a7-71c5-c4c3f24cd894.png)

Cannot connect to runtime process, timeout after 10000ms (reason: Cannot connect to the target: connect ECONNREFUSED 127.0.0.1:5858).

# 環境

* macOS High Sierra
* node 10.11.0
* Visual Studio Code 1.27.2
* Azure Functions拡張 0.11.0
* Azure functions tools 2.0.3
* .Net Core SDK 2.1.402


# 原因

node実行時に、`--inspect=5858`オプションが渡っていないから。

# 解決策

`launch.json`を開くと、`preLaunchTask`として`runFunctionsHost`が設定されています。

`tasks.json`を開きます。

```json:tasks.json（抜粋）
{
  "version": "2.0.0",
  "tasks": [{
      "identifier": "runFunctionsHost",
      "type": "shell",
      "command": "func host start",
      "options": {
        "env": {
          "languageWorkers:node:arguments": "--inspect=5858",
        }
      },
  }]
}
```

`--inspect=5858`と書いてありますが、理解してもらえてないご様子です。

nodeの環境変数を直接指定することにします。

```json:
          "NODE_OPTIONS": "--inspect=5858"
```

このように書き換えます。

# そのうち・・・・・・

Azure functions toolsが修正されて、この記事不要になるかな？
MojaveでMacPorts

## Mojaveがきた。MacPortsは?

インストールされているものはそのままでも動くかも知れませんが，OSをアップグレードしたら，MacPortsも入れ直しです。Mojave用のbuildbotがコンパイルして，徐々にバイナリが揃ってきています。バイナリがないものは，自分のMacでコンパイルする必要があるので時間がかかります。

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">MacPortsのMojave用バイナリの提供には最低ひと月はかかるとのこと。それまでは自分のMacでコンパイルするので時間がかかる <a href="https://t.co/FP1115Np0l">https://t.co/FP1115Np0l</a></p>&mdash; Takeshi Enomoto (@takeshi_enomoto) <a href="https://twitter.com/takeshi_enomoto/status/1046206824024879104?ref_src=twsrc%5Etfw">2018年9月30日</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> 

## Mojaveにアップデートしてしまったら

`port`コマンドは使えなくなります。[Migration](https://trac.macports.org/wiki/Migration)に従って，入れ直しましょう。少し補足します。

1. Xcodeが更新されているか確認
2. Command-line Toolsを入れる。<br />`$ sudo xcode-select --install`
3. MacPortsのソースを[ダウンロード](https://github.com/macports/macports-base/releases)してコンパイルしてインストール
4. `restore_ports.tcl`は現在インストールされているものを再現しようとします。不要なものはアンインストールしておきましょう。依存関係がないものを一気にアンインストールするには`$ sudo port -u uninstall`とします。
5. 後はMigrationに従います。[ccache](https://trac.macports.org/wiki/howto/ccache)をインストールしていた場合は，先にインストールすると良いでしょう。

## gccがコンパイルできない

私の場合は`libgcc7`（[ticket/57196](https://trac.macports.org/ticket/57196)）や`libgcc8`がコンパイルできませんでした。
数日後別のマシンでは問題ありませんでしたが，記録として残しておきます。

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">aquatermやlibgcc8でエラーが出た。システムヘッダの場所の変更が原因とのこと。/Library/Developer/CommandLineTools/Packages/macOS_SDK_headers_for_macOS_10.14.pkgを入れたらlibgcc8のコンパイルはいけそう <a href="https://t.co/5EDHURTSy2">https://t.co/5EDHURTSy2</a></p>&mdash; Takeshi Enomoto (@takeshi_enomoto) <a href="https://twitter.com/takeshi_enomoto/status/1046208868819374080?ref_src=twsrc%5Etfw">2018年9月30日</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> 

APFSとの相性が悪いので，1スレッドでコンパイルせよという[コメント](https://trac.macports.org/ticket/57196#comment:1)もありましたが，うまくいきませんでした。

[Compiling GCC 8 on macOS Mojave](https://solarianprogrammer.com/2017/05/21/compiling-gcc-macos/)に従って，システムヘッダをインストールしたら，gcc関連のportsのコンパイルが通りました。
```
$ open /Library/Developer/CommandLineTools/Packages/
```
`macOS_SDK_headers_for_macOS_10.14.pkg`をダブルクリックしてインストールします。

## おわりに

`restore_ports.tcl`でうまくいかない場合は，`variant`などを見直したり，`myports.txt`から削除したりして対処しました。個別にインストールしたらうまくいったものもありました。High Sierraで使っていたものは，ほぼ再現できました。

不具合に遭遇したら，[ProblemHotlist](https://trac.macports.org/wiki/ProblemHotlist)[やMojaveProblems](https://trac.macports.org/wiki/MojaveProblems)を確認してくだい。困ったときはメーリングリストで相談してください（英語）。Portfileのバグを発見したり，改善・解決方法が見つかったりしたら，チケットで報告してください。

* [github.com/macports/macports-ports](https://github.com/macports/macports-ports)
* [ドキュメンテーション](https://guide.macports.org)
* [FAQ](https://trac.macports.org/wiki/FAQ)
* [MailingLists](https://trac.macports.org/wiki/MailingLists)
* [Tickets](https://trac.macports.org/wiki/Tickets)
* [El CapitanへのMigration](https://www.enomosphere.net/2015/10/04/macports-migration-elcapitan/)
寿司のネタを判定してくれるアプリをPWAでつくった話

## 概要

https://aisushicat.com/

Webフロントエンドエンジニアがアプリリリースするときにだいたい通りそうなワードをまとめました。

- アプリの説明(寿司の写真を撮ると、かわいい猫がネタをあててくれる)
- PWA化
- SPA(React + Redux + ReactRouter)
- SEO設定

細かい話は別記事にまとめようと思います。今回は各項目大きなところだけ書きます。

ちなみにリリースしたのは結構前です。笑

## アプリの話

[AI寿司猫](https://aisushicat.com/)と呼んでます。

<img width="480" alt="スクリーンショット 2018-10-06 7.43.54.png" src="https://qiita-image-store.s3.amazonaws.com/0/252348/f0ffa218-677f-7ea4-4af7-6eb552dfabaa.png">

っていう感じなんで、スマホから使ってください。このページ見てる人の6割程度がスマホだと思うので、3~4割のPCで来た方はいいねやらしてからスマホで見ていただけると(_ _)w

現状実装している機能としては、「ネタ判定」、「寿司辞書」、「Ai」の3つです。本当はアルバムっていうのも実装したかったんですが、サクッと作って出すという方針によって削られました

### ネタ判定

写真を撮影して、画像を学習済みの寿司ネタ判定APIに投げて結果を表示するだけの単機能なので、WebRTCとか使って撮影画面をリッチにしたいなとか思ってました。

おそらくPWAの機能だと思うんですが、当初はWebRTCを使ってアプリ内に独自の撮影機能を埋めこもうと思って[こんなところとか参考にしながら](https://developers.google.com/web/updates/2016/12/imagecapture)検証してました。

しかし最初からわりと懸念していた事項なんですが、PWA（というか話を限定するとWebRTCをiOSで使うこと？）には現状まだ問題があって、iOSの対応がかなり遅いことと、制限が強いです。  
Safariでは一応カメラのデータをリアルタイム表示できるんですが、Chromeではできないとか、Androidでどうなるのかもわからんので、確認も大変だし、リリースしてからもいろんな問題が起こりそうだったんでここは断念してinput type="file"で実装しました。

しかし実はinputタグの挙動も、iOSだとカメラで撮影するかライブラリから選択するか選べるんですが、（検証した）Androidだとライブラリからしか選べないようでした。なので（理想的な話から考えると）ちょっと物足りない実装になってます。

この辺りの話はちょっと長くなってしまうので暇なときに別でまとめようと思います。暇なときに。

### 寿司辞書

教師データが用意できた、学習済みの寿司に関する説明を見ることができるページです。  
ちょっと失敗したのは、多言語対応の方法です。これ、英語のページを見るときにも日本語の言語データが読み込まれちゃっているので、言語が増えると圧倒的重さになっちゃいます。

多言語対応するときは、必ず言語ファイルは分割しましょう。

### Ai

スタッツとも呼んでますが、寿司猫に食わせた寿司の量とか割合を見ることができるページです。  
一応最小限のデータは出てますが、実はまだ開発中です(_ _)w

理想的には、全員が投げた画像の数や割合、学習済みの数とか各ネタの精度なども出せるといいんですが、これ自体は特に収益化するアプリでもないのでこの程度の実装にとどめてます。

## PWA化

知らない人向けに書くと、昔から細々と存在するWebRTCなどや、ネイティブの機能をWebから使えるような新しいAPIをまとめた仕様を[PWA(Progressive Web App)](https://developers.google.com/web/progressive-web-apps/?hl=ja)と呼んでいて、Googleが主導して進めています。最近Appleも対応を発表したので、おそらくこれからの動向がもっとも注目されている技術です。

主なところだとservice-workerによるプッシュ通知ができることで知っている人が多いんじゃないでしょうか。  
当初カメラを使うことと、ホーム画面に追加した時の見た目を制御するためにPWA化が必要なんじゃないかと思っていたのでmanifest.jsonやservice-worker.jsを追加してたんですが、別にそういうわけでもなさそうなのと、PWA自体発展途上なのでいったいPWAとはなんなのかとかいう話はきっとかなり長くなります。

これも暇になったら別の記事にしたいと思います。

PWA化には入らなさそうな作業として、htmlのmetaタグでiPhoneでホーム画面に追加した時の見た目とか変えられるんですが、manifest.jsonにも確か同じような設定があって、今後統一されるんですかね。そういう曖昧な部分がまだ往々にしてあるんで、PWAについて調べるときは基本的にはGoogleのページを参考にしてください。

## SPA

ホーム画面に追加してもらって、アプリのようにシームレスに使ってもらいたかったのでSPA化を決めました。  
状態が多そうだったのでRedux導入してみたのですが、ネタ判定のページがcanvasでの画像操作とかタッチイベントをゴリゴリやる系のコードだったので状態が肥大化して、結局必要だったか不明な感じになりました。

本当にRedux必要か考え直すことを強要してくるブログとかも見渡していると存在するので、（僕も本当に必要かわからない場面がたまにあるので）Reduxが必要な時ってどんなときだっけ、とかそういうのも別の記事で暇なときに（）

ちなみにReduxとReactRouterは特に中間ライブラリとか使わずに結合しました。たぶん現状のバージョンだとReactRouterの状態をReduxに入れたりするようなライブラリ使わなくても十分自然に結合できるので特にこのまま使ってしまって問題ないし、むしろ実装がわかりやすくなると思いました。

## SEO

SEO対策というかは、titleタグやmetaタグをきちんと設定する技術的な話です。

これも別記事にまとめようと思います（）

## 終わりに

だいぶ雑な記事になってしまいました…どっちかというとこれを見ていただける方に使ってみて欲しかったっていうのが一番大きいです。笑

いいねめっちゃいただけたりすると記事を書くモチベが爆上がりします。。

ちなみに、[SSR](https://qiita.com/jigen098/items/6b8873b618ba298d7ffa)はやりませんでした。特にメリットを感じなかったからです。

[SSRに関する記事](https://qiita.com/jigen098/items/6b8873b618ba298d7ffa)も過去に書いたので、導入検討している方は判断材料にしてみてください。