OmniAICreator/Qiita-1.07M huggingface dataset:
自分の母校の授業で使われた謎解きゲームが公開終了したので色々振り返る

こんにちは、フロントエンドエンジニアの[てりー](https://twitter.com/teriteri_code)です。
僕の詳しいプロフィールは[こちら](https://terrblog.com/about/)

# 注意
これは2020年に限定公開で書いた記事ですが、2021/10/14に全体公開にしました。
もうプレイは出来ません。

# はじめに

今まで業務でVueやAngularしか触ってきませんでしたが、漠然とReactを経験してみたいと思っていた所、タイミングよく誘われたので案件を手伝いました。

https://uminazo.jp/

**それがなんと年末に母校の海洋問題の授業で使われました。**

https://social-innovation-news.jp/?p=1176

2月末で公開が終わったので、React未経験からこのプロジェクトを通して学んだ技術的な知見をまとめていきます。

**※クライアント側の許可を貰って、確認頂いた上で当記事は投稿しています。**

# uminazoとは
https://uminazo.jp/

日本財団が推進する「[海と日本プロジェクト](https://uminohi.jp/)」の一環で作られた「海洋問題」について学べる謎解きゲームです。
中身はそこそこ難しく、自分はある程度の謎を知っているにも関わらず、クリアするのに1hほど時間がかかりました。

**※海なぞ水族館2020はすでに公開は終わっているのでプレイは出来ません。**

# 携わった事
1人がメインで動いていて、ノベル部分や全体の設計など大方の事はその方が行なってくれました。
自分はその方にレビューを貰いながら、個別に謎の実装に携わりました。

個別謎として1つ例を出すとパズル謎です。
## パズル謎の実装

![LL_img_242459_3.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/397606/243fc917-b859-eab4-4a2b-2cb539f9b0a9.jpeg)

右側にあるピースがクリックにより90度回転し、真ん中のマス目にはめ込むことでパズルを解くことが出来ます。
ピース1つ1つに正解座標と正解回転度数を仕込んで、近くまでドラッグしたらピタッとハマる仕様になっています。

ドラッグにはreact-draggableを使いました。
クリックによる回転と合わせてこちらの記事で詳細は解説しています。
[react-draggableにクリックイベントを追加する](https://qiita.com/terry_6518/items/8e99bb8eae099e09bd66)

# uminazoを通して得た学び
## あるあるじゃない機能作り
今までは業務システムのフロント開発をメインで行なってきました。
新しい画面の作成、機能の追加、バックエンドからのAPIの繋ぎ込みや、ライブラリの導入、バグ修正などです。

見たことある機能が多く、経験したことがなくてもググると類似の機能に関する情報が見つかりやすいものでした。
ですので、サーバー側の人との適切な連携、ドキュメントをしっかり読む力、などが養われていたかなーと感じていました。

しかし、今回作成したのはブラウザゲームで、ブラウザ上でゲーム性を表現しなくては行けません。
それぞれの謎が実装のイメージが湧きにくいし、ググっても近しい情報はない事が多かったです。

ですので、正攻法でそもそも実装できるのか？
難しいならどうすればゲームとして成立する形で実装できるか？

みたいな所に頭を悩ませました。
詳細設計が綺麗に立たない分、「ユーザーにどの様に使われたいか？」という部分を常に頭に置いて実装を行う事ができ、、良い開発体験が積めたかなと思っています。

## スマホ対応可能のブラウザゲーム
スマホ対応も兼ねました。
静的なページのスマホ対応は普段行なっていますが、ゲームでは特殊なイベント操作が多く、スマホデバイス上でそれらを実現するのに苦労しました。
例えば、先ほど上げたパズル謎の例では、ドラッグイベント発火時にクリックイベントも同時に発火してしまうので、useStateでフラグを作り同時発火を防ぐ対処をしました。


## photoshopの使用
今までもフロントエンドの業務ではコーディングの指標としてXDやskech、figma、zeplinを使ってきました。

しかし今回はphotoshopではコーディングの指標としてだけではなく、パーツを簡単に加工し切り抜くなどのちょっとした操作まで行いました。
制作の最終盤はデザイン担当の人に作り直して貰う時間もなかったので、要件を満たせるレベルでのデザイン修正なども経験できました。

# まとめ
ここ最近はtoB系の業務システムの開発が多く、自分の技術的な成長にエンジニアリングの喜びを感じていました。
しかし、今回は運よく自分が携わったサービスが母校で使われた事で、「世の中に役に立つ物を作る」というエンジニアリングの根底にある喜びを再確認できました。

今年の4月でプログラミングを始めて3年になりますが、今後のキャリアの中で「技術的に成長」「世の中への貢献」の両輪をどう追い求めていくのか。考えるきっかけになりました。

#### 僕がやったReactの勉強方法

https://terrblog.com/react-typescript-how-to-master/

#### 未経験からメガベンチャー転職までの軌跡
https://terrblog.com/mega-venture-job-change-roadmap/
MetaPostでベン図する

[MetaPostでベン図するパッケージ](https://www.ctan.org/pkg/venn)があったので触ってみた．
ことの発端はLaTeXのものを求めて `texdoc` で探したときまで遡る．

```terminal
$ texdoc -lI venn
 1 /usr/local/texlive/2020/texmf-dist/doc/metapost/venn/README
```

該当1件であり，しかも `.../metapost/...` だからMetaPost用のパッケージだ，これは予想外．検索の仕方が悪かったのか，もしくはTeX Liveをフルインストールしていなかったからかもしれない．（追記： `$ texdoc -s -l venn` で [`venndiagram` パッケージ](https://www.ctan.org/pkg/venndiagram)が検索結果に含まれるようになります．）
`README` の内容は単簡にして明瞭だ．試してみよう．

```metapost:main.mpのプリアンブル
input venn;

prologues:=3;
outputformat:="png";
outputtemplate:="%j-%c.png";

%%% ...
```

`input venn;` でパッケージを読み込める． `prologues ...` は出力するファイルに関する指定である．ここではPNGファイルを出力しているが，EPSはもちろん[^post]，SVGも指定できる．

[^post]: MetaPostの "Post" は PostScript から来ている．

```metapost:main.mpの1個目の図
%%% ...

beginfig(0);
  draw_venn_two(false,true,false,true);
  %%%           |     |    |     |> Right except Left
  %%%           |     |    |> Left and Right
  %%%           |     |> Left except Right
  %%%           |> Universe

  draw_venn_one(false,true) shifted (1in,-1in);
  draw_venn_none(true) shifted (2in,-2in);
endfig;

%%% ...
```

用意されている `draw_venn_ﾎﾆｬﾗﾗ` の引数には，領域が塗られるかどうかを真偽値で指定する．

![main-0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326541/527db429-ec50-1e95-6ccb-63bc349e2590.png)

塗り潰す色を変更してみたく，実装を流し読みした．パッケージのファイルは `$ less $(kpsewhich venn.mp)` で閲覧できる．
`venn_shade` がお目当てのものらしく， `vardef` で定義されている．だから， `input venn;` 以降でこれを再定義すればよいだろう．
また，3つの集合を描く `draw_venn_three` もある．使ってみよう．

```metapost:main.mpの2個目の図
%%% ...

beginfig(1);
  vardef venn_shade = black enddef;
  draw_venn_three(true,false,true,false,true,true,false,false);
endfig

%%% ...
```

![main-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326541/55fea14b-3a79-6186-a5ef-4383f4a36763.png)

最後にソースコード全体を掲げる．これを `main.mp` としてファイルに保存し， `$ mpost main.mp` すれば `main-{0,1}.png` が得られる[^mp]．

[^mp]: MetaPostのコマンド名は，ところによって `mp` になることもあるだろう．

```metapost:main.mp
input venn;

prologues:=3;
outputformat:="png";
outputtemplate:="%j-%c.png";

beginfig(0);
  draw_venn_two(false,true,false,true);
  draw_venn_one(false,true) shifted (1in,-1in);
  draw_venn_none(true) shifted (2in,-2in);
endfig;

beginfig(1);
  vardef venn_shade = black enddef;
  draw_venn_three(true,false,true,false,true,true,false,false);
endfig

bye
```
「うっせぇわ」でわかる再帰関数

```py
def ussee(i):
    if i <= 0:
        return "わ"
    else:
        return f"うっせぇ{ussee(i-1)}"

print("はぁ？")
print(ussee(3))
# はぁ？
# うっせぇうっせぇうっせぇわ
```

関数`ussee(i)`は、8行目のように呼び出すとその処理の中で5行目のように自身を呼び出します。

このように処理の中で自身を呼び出すことを**再帰呼び出し**といい、再帰呼び出しを行う関数を**再帰関数**といいます。

再帰関数に終わりを設定しないと無限ループになるため大抵は自身を呼び出さないタイミング、すなわち再帰呼び出しの「終わり」を設定します。

`ussee(i)`だと以下の部分が「終わり」に該当します。

```py
    if i <= 0:
        return "わ"
```

そういった終わりの部分は**基底部**といいます。

この例では引数`i`が呼び出しごとに1減算され、0以下になったときに再帰呼び出しが終わります。

`ussee(i)`に0を指定した場合、以下が出力されます。

```py
print("はぁ？")
print(ussee(0))
# はぁ？
# わ
```

わ (← かわいい)

`ussee(100)`を呼び出すとどうなるでしょう。

```py
print("はぁ？")
print(ussee(100))
# はぁ？
# うっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇうっせぇわ
```

では`ussee(1000000000)`ではどうでしょう。

```
RecursionError: maximum recursion depth exceeded in comparison
```

再帰呼び出しが多くなりすぎると、Pythonは途中でエラーを出して止めてくれます。

関数は呼び出しごとにメモリに呼び出し情報を蓄積するため、特に再帰関数の場合は制限をかけないと大量に呼び出し情報を蓄積してしまいます。(後述する不具合の原因となります)

以下のコードでPythonはどれくらいなら許してくれるのか確認することができます。

```py
import sys
print(sys.getrecursionlimit())
# 例: 1000
```

1000回までなら大丈夫だそうです。

以下のコードで制限を変更することもできます。

```py
sys.setrecursionlimit(100000)
```

制限を緩くしすぎた場合、それは大量の呼び出し情報の蓄積を許すことになるため今度はメモリ自体の許容範囲を超えてしまう**スタックオーバーフロー**という不具合が発生し、プログラムはクラッシュします。

```py
import sys

sys.setrecursionlimit(100000)

def ussee(i):
    if i <= 0:
        return "わ"
    else:
        return f"うっせぇ{ussee(i-1)}"

print("はぁ？")
print(ussee(100000))
print("あ")
```

この場合、最初の`はぁ？`しか出力されません。異常エラーのため、クラッシュについては何も出力してくれません。

こういった呼び出し情報を大量に蓄積してしまう問題を解決するため、「再帰関数はループしている」という性質をもとに

呼び出し情報を蓄積しないようなプログラムに書き換える**末尾呼び出し最適化**という処置を施すプログラミング言語も存在します。

再帰関数は最初はわかりづらかったり、メモリ上の制限があったりなど注意することはありますが、実世界の問題解決において「再帰的な構造」という発想は問題の構造を把握する上で非常に重要な考え方です。また、そういう文脈で最適化という処置が存在することも踏まえると、再帰関数をおさえておく価値は大きいように思います。

---

`わ` が可愛いかったので つい出来心で書いてしまいました。お力になれたら幸いです。

---

追記: 本当にくだらないですがある発見をしましたので共有します。

```py
def ussee(i):
    if i <= 0:
        return "わ"
    else:
        return f"うっせぇ{ussee(i-1)}"

social_rules = ["最近の流行は当然の把握", "経済の動向も通勤時チェック", "純情な精神で入社しワーク"]

print("はぁ？")
print(ussee(len(social_rules)))
# はぁ？
# うっせぇうっせぇうっせぇわ
```

```py
def ussee(rules):
    if not rules:
        return "わ"
    else:
        rules.pop()
        return f"うっせぇ{ussee(rules)}"

social_rules = ["最近の流行は当然の把握", "経済の動向も通勤時チェック", "純情な精神で入社しワーク"]

print("はぁ？")
print(ussee(social_rules))
# はぁ？
# うっせぇうっせぇうっせぇわ
```

こちらの変更では`pop`の戻り値の使い捨ても相まって詩的なコードになりました。
【Rspec】Error: raise WrongScopeError の解消【原因:rspec_railsのバージョン】

Rspecを初めて書こうと思い、色んな記事を参考にrspec_rails をインストールしてテストを書きました。いざRspecを走らせてみると、以下の様なエラーが発生しました。

```
Failure/Error:
       raise WrongScopeError,
             "`#{name}` is not available from within an example (e.g. an " \
             "`it` block) or from constructs that run in the scope of an " \
             "example (e.g. `before`, `let`, etc). It is only available " \
             "on an example group (e.g. a `describe` or `context` block)."
     
       `name` is not available from within an example (e.g. an `it` block) or from constructs that run in the scope of an example (e.g. `before`, `let`, etc). It is only available on an example group (e.g. a `describe` or `context` block).
```

どうやら`name`がスコープの範囲外にあるのがダメと言っています。しかし`name`に全く身に覚えがなかったので途方にくれていました。

色々調べてみると、rspec_railsのバージョンが古いと同様なエラーが発生する様です。

そこでrspec_railsのバージョンを現在の最新バージョン(4.0.2)に変更したところ、解決しました。
同様のエラーに悩んでいる人の助けになれば幸いです。
== null よりも ===null と === undefined を使おう

## Re: === undefined よりも == null を使おう - Qiita

[=== undefined よりも == null を使おう - Qiita](https://qiita.com/monaka_ben_mezd/items/0ded17ee5d3265b4ce4b)

こちらの記事の反対のスタイルでコード書いています。コメント欄に書くとだらだらと長くなるので記事にします。

等価演算子「==」をnullのときだけ使うという謎の風習が以前からあって、そうすると業界に曖昧コードが増えてしまうのは残念なので書きます。

[nullとundefined - TypeScript Deep Dive 日本語版](https://typescript-jp.gitbook.io/deep-dive/recap/null-undefined#rtoreberuroot-levelnoundefinednochekku)
> == nullを使ってundefinedと nullを両方ともチェックすることを推奨します。一般的に2つを区別する必要はありません。

どこの一般なんだろうか。プログラマ以外の開発者ではない一般ピープルのことか？


## 曖昧判定「==」

JavaScript で多くのミス、不具合、バグ、が曖昧判定からきています。
「==」と「===」の違いです。
「==」は等価演算子。曖昧判定で、JavaScriptでは全ての場面で不要です。
「===」は厳密等価演算子。厳密判定で、JavaScriptではこちらを使いましょう。


ある値が、1 (数値)もしくは '1' (文字列) のどちらかが入っているかわからないけど、なんとなく動くコードというのは次のように書くことができます。

```js
if (value == 1) {
  // value が数値でも文字列でも動く
}
```

これは、あまり推奨されません。valueが数値か文字列かどちらかわからないという状態では開発するのが難しいです。

```js
if (value === 1) {
}
// もしくは
if (value === '1') {
}
// ちゃんと区別して書こう
```

null と undefined も同じ理由で区別する方が望ましいです。

## null と undefined を一緒くたに扱うと発生するバグ

関数引数のデフォルト値指定や、分割代入のデフォルト値指定では、nullは無視され、undefinedだけがデフォルト値指定されるので、nullとundefinedは明確に区別しておいたほうがいいです。

簡単に例を書きます。

```js
const result = funcA();
if (result == null) {
  funcB(result);
}

function funcB(value = '無効です') {
  console.log(value)
}
```
この場合、funcAで値やオブジェクトなどが返る場合はfuncBは実行されず、undefinedが戻るとき「無効です」と表示され、null の場合は「null」と表示されます。

何も実行されないのか、「無効です」か、の2種類ではなく「null」が表示される場合があります。よく知ってないと不具合につながるようなあぶない挙動です。

なので、funcAの戻り値に、nullを返すのかundefindeを返すのかは、重要な問題になります。

一文字一句間違えてはいけない正確にコードかかなければいけないのがプログラマの仕事です。null と undefined という別の値を似たようなものとして混ぜてしまうと、プログラムが正確に動きません。

「==null」で分岐しているコードを読んでも、判定後の値がnullなのかundefinedなのかを読み取ることはできません。わざわざ動かさないと本当の値がわかりません。リファクタリングするときに、result がnullかundefinedか不明なので、関数のデフォルト値判定で使えないことになるので、リファクタリングしにくいコードになります。

null と undefined は同じものではないのです。同じものではないのに、「==null」として同じものとして判定する必要ないです。

## 対策

大抵の場面は

```js
if (result === null) {
  funcB();
}

if (result === undefined) {
  funcB();
}
```
どちらかで済みます。正しくどちらかを書きましょう。

厳密に「==null」と同じことをしたいのなら

```js
if (result === null || result === undefined) {
  funcB();
}
```
このように書いておくとよいです。
この書き方は「==null」と比べて冗長(無駄に長い)に思うかもしれませんがそうではなく、書いた人の意図が正しく伝わるようになっているので「==null」よりも読みやすいので、よい書き方です。文字数が少ないのがよいコードなのではありません(書くときにさぼってるだけ)。文字数が多かろうが少なかろうが読みやすいコードがよいコードです。

この書き方なら result が null か undefinedか、どちらかになる場合を想定して判定しているのだな、と確実にわかるような書き方です。

「==null」というコードを見ると下記のような迷いが起こります。

1. 間違って「==」を使っているのか、
2. あるいは、nullを見るためだけに使っているのか、
3. あるいは、undefinedをみるためだけに使っているのか、
4. あるいは、null も undefined も両方とも判定しているのか、

これらのどれを意図しているのかわからないコードになります。あいまいです。

「if (result === null || result === undefined) {」の場合は、4. に確定します。

## 参考

[プログラミングでは、より短いコードで同じ目的を達成した方が、優れているのですか？ - Quora](https://qr.ae/Tm2RNb)

## 大事なこと

繰り返します。厳密等価演算子使え、というのはあらゆるところで言われていることです。

JavaScript で多くのミスが曖昧判定からきています。
「==」と「===」の違いです。
「==」は等価演算子。曖昧判定で、JavaScriptでは全ての場面で不要です。
「===」は厳密等価演算子。厳密判定で、JavaScriptではこちらを使いましょう。

単にこれを守っていればいいだけです。nullのときの例外なんて考える必要ありません。

バグだらけのプロジェクトだとコードに曖昧さがあると即死につながるので、這いつくばってでも前に進んで生き残るためには、こうした方がいいよ的な、現場からは以上です。
VMware fusion にOpenWrtをインストール

#はじめに
備忘録として、VMware fusion にOpenWrtをインストールする方法を記事ににしました。
基本的には参考URLを現環境で実施しているだけです。

#環境
OS:MacOS catalina 10.15.7
VMware fusion:Player バージョン 12.1.0
OpenWrt:openwrt-19.07.7 x86

#事前準備
##OpenWrtイメージのダウンロード
[OpenWrt Downloads](https://downloads.openwrt.org/)からイメージをダウンロードします。
使用するデバイスによってダウンロードするイメージが異なります。
今回は、Stable Releaseのx86 / generic / combined-ext4.img.gz
を使用しました。

```
$ wget https://downloads.openwrt.org/releases/19.07.7/targets/x86/generic/openwrt-19.07.7-x86-generic-combined-ext4.img.gz #イメージのダウンロード
$ gzip -d openwrt-19.07.7-x86-generic-combined-ext4.img.gz #.gzファイルを解答
```

![スクリーンショチE�� 2021-03-01 11.09.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/287677/d23fe383-df87-df02-653a-a50cce851ff8.png)

##OpenWrtイメージのカーネルバージョンの確認
VMwareの設定の際に、カーネルのメジャーバージョン情報が必要です。
そのため、事前に確認しておきます。
[Devices and kernel versions](https://openwrt.org/packages/pkgdata/kernel)から確認します。
今回は4.14.162 でした。

![スクリーンショチE�� 2021-03-01 11.08.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/287677/4e7e598b-85bb-efbd-c61c-9a234f87b0c0.png)

##QEMUのインストール
上記のイメージはそのままの状態ではVMware Fusionにはインストールできません。
そこで、仮想マシン用のイメージ作成アプリである。QEMUを使用します。


```
brew install qemu #QEMUのインストール
qemu-img convert -f raw -O vmdk openwrt-19.07.7-x86-generic-combined-ext4.img openwrt-19.07.7-x86-generic-combined-ext4.img.vmdk #QEMUによる仮想マシン用ファイル作成
```

これで、事前準備はおわりです。

#OpenWrtの仮想マシンを作成する

「カスタム仮想マシンを作成」を選択
![スクリーンショチE�� 2021-03-01 11.07.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/287677/403d18ca-9053-4ae0-4ded-e5022e4edc84.png)

「Linux＞その他のLinux4.Xカーネル」を選択。
カーネルのバージョンは上記で調べたものを選択する。
![スクリーンショチE�� 2021-03-01 11.08.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/287677/bc8cd12a-5991-4f1b-152c-b9abd66dde30.png)

ここは、レガシーBIOSじゃないと動かない。
![スクリーンショチE�� 2021-03-01 11.09.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/287677/25654990-a7a6-0ee7-01d7-92a8ba80fcba.png)

「既存の仮想ディスクを使用」で先ほど作成した.vmdkファイルを選択する。
![スクリーンショチE�� 2021-03-01 11.09.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/287677/fbc4c642-d77f-cb4e-d9b6-bae36866a222.png)

設定は終わり。
名前を付けて終了する。
![スクリーンショチE�� 2021-03-01 11.10.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/287677/ecb6ec70-3a2d-31dc-4f50-ef3d7933e180.png)

ここで、作成されたファイルのうち、.vmxファイルを修正します。
一番最後に、「ethernet0.virtualDev = "e1000"」を追記します。
![スクリーンショチE�� 2021-03-01 16.13.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/287677/83efbf53-f9ec-4559-7e70-ab7a406d5a30.png)


![スクリーンショチE�� 2021-03-01 16.12.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/287677/7ca754e9-abb6-7d32-33d2-ae12e25b7e44.png)

あとは普通に起動します。
![スクリーンショチE�� 2021-03-01 16.14.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/287677/3f05d8a9-5245-e783-07eb-27ca3de04640.png)
おつかれさまでした。

#参考URL
・OpenWrt wiki，OpenWrt on VMware HowTo
https://openwrt.org/docs/guide-user/virtualization/vmware
・nknskn ネタ置き場 ，Macbookの移行でVMware Fusionの仮想マシンが動かなくなった話
https://news-nknskn.hatenablog.com/entry/2020/06/01/024025
AWS EC2でuserdataの実行結果Logの確認

# 確認方法
* userdataを利用すると、起動後にインスタンスにユーザーデータを渡し、一般的な自動設定タスクを実行したり、スクリプトを実行したりできます。
* userdataはcloud-initを介して実行されるので、cloud-initの出力ログファイルを見ることで、userdataの実行結果ログが確認できます。

```
cat /var/log/cloud-init-output.log
```
#40 代おっさん EC2 を作成してみた ③ 


## 本記事について

本記事は AWS 初学者の私が学習していく中でわからない単語や概要をなるべくわかりやすい様にまとめたものです。
もし誤りなどありましたらコメントにてお知らせいただけるとありがたいです。

## AWS 作成図

![vpc.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/641940/b9dc8ad4-bba8-2910-9975-96abff578ad3.png)

VPC 部分については
<https://qiita.com/kou551121/items/2535fe3de57a5c813687>
今回は EC2 を作成することをメインにしております。
EC2 詳しく知りたい方は
<https://qiita.com/kou551121/items/54acbecd4fa147bc4d51>

## 構築手順

❶EC2 を作成する。
<https://qiita.com/kou551121/items/56f2e075d33fbf345787>
（❶ の部分を知りたい方はこちらを）
❷ セキュリティーグループの作成
https://qiita.com/kou551121/items/b86ea1dafb04501e741d>
❸EC2 インスタンスに接続

## 3.EC2 インスタンスに接続

インスタンスに接続するためには ssh でログインするためのツールが必要となります。
自分は Windows なので下に参考資料あげておきます！！
Tera Term（今回はこれがあれば十分だと思います！！）
<https://dev.classmethod.jp/articles/aws-beginner-ec2-ssh/>

VSCode で Remote Development 仕様の場合（ケースはリモート先のファイル編集を行うというシチュエーション用）
<https://qiita.com/HoriThe3rd/items/b2f6c440f096106cf89e>

ちなみに VSCode を使用する場合こちらも使うかもしれませんので（WSL）
<https://qiita.com/matarillo/items/61a9ead4bfe2868a0b86>

今回はとりあえず Tera Term のほうで行きたいと思います！
下がインスタンスにログインした様子です。このようになっていたらログインできたことになります。
![ec2ログイン.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/641940/68362a9e-c54a-6292-6770-fb0314b64931.png)
とりあえずインスタンスに接続しましたが、
Public IP アドレスだとインスタンスを停止、再起動するたびに変わるので不便です。
Elastic IP アドレスに変えたいと思います。
まずは VPC の画面に行き Elastic IP をクリック（赤枠）
![ElasticIP.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/641940/310d4de6-3ae1-ab8c-0282-c4e92b9be069.png)
下の画面に画面遷移
![割り当て.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/641940/cb2f2b2c-d9e6-5f7f-92f6-1f221f7bbfc6.png)
Elastic IP アドレスの割り当て（赤枠）をクリック
![割り当てクリック.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/641940/b280077d-4658-812f-f6bf-c82b897012b6.png)
今回は何もせず下のほうにある割り当て（赤枠）をクリック
![IPアドレスの関連付け.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/641940/4315f177-9daf-64d1-d509-cebb93cf859c.png)
赤枠を見ると割り当てられていますね。それだけでは終わらずつぎはアクションから Elastic IP アドレスの関連付けをクリックしてください。
そうすると下のように画面遷移します
![関連付け.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/641940/d2b4f3db-9258-623c-d0d6-baebc0403ea3.png)
赤枠に自分の作ったインスタンス（自分は Test-EC2-public)を選んでください
選んだら関連付け（青枠）クリック
関連付けられたか確認したいのでサービスから EC2 を選んで自分が選んだインスタンスの詳細を確認してください
![インスタンス詳細.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/641940/ce65d399-c114-93a8-faef-c85822b6af95.png)
このように変わっていると思います。

## 最後に

これでいつでも EC2 インスタンスに入れると思います。これで EC2 インスタンス作成は終わりたいと思います。
でも実は構成図にはまだ終わっていないところがあります。
そうです、NAT gateway から　 Private subnet の EC2 に繋げていない・・・・
次回はそれをやりたいと思います！！
頑張ります！

またこの記事は AWS 初学者を導く体系的な動画学習サービス「AWS CloudTech」の課題カリキュラムで作成しました。
<https://aws-cloud-tech.com/>
ArchLinux （物理サーバー）を仮想サーバーに移したときのハマりポイント

久しぶりに ArchLinux （物理サーバー）を仮想サーバーに移したときにいくつかハマった。

## 稼働しながら`/`をバックアップする方法

```txt:/tmp/excludes.excl
.bash_history
/dev/*
/mnt/*
/tmp/*
/proc/*
/sys/*
/usr/src/*
/run/*
/var/lib/docker/*
```

```sh
tar --numeric-owner -cpf /mnt/mysystem.tar / \
 -X /tmp/excludes.excl
```

* /mnt/mysystem.tar を手元にコピーしておく。
* ArchLinuxのLiveCDでブートし、パーティションを切って解凍まですます。
* `arch-chroot`でchrootする。

## GRUBインストールに失敗する

```sh
grub-install /dev/vda
```

```sh
grub-install --force /dev/vda
```

これをしたが、今度は`/`のマウントに失敗していた。

![スクリーンショット_2021-03-01_13-59-49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/25728/070c1c43-025c-bea6-78ec-a2531056b167.png)


もともとmbrだったにGPTで用意してしまったことが原因だった。

* [Arch Linux インストール (非EFI) - Qiita](https://qiita.com/tukiyo3/items/48fb3b4b41642743d581)も参照。


### GPT を mbrに変換

```sh
# GPTであることに気がついたコマンド
blkid /dev/vda*
```

```sh
# GPT を mbrに変換
parted /dev/sdb -s mklabel msdos
```

### crc32cモジュールが不足しているため起動できない

![スクリーンショット_2021-03-01_14-07-44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/25728/61bc1454-ebb5-ebd8-6bfd-7893c1abfe4f.png)

* arch-chrootした後、

```diff:/etc/mkinitcpio.conf
- MODULES=""
+ MODULES="crc32c"
```

```
mkinitcpio -p linux
```

他にも`vboxhost`でエラーになる場合は`vboxhost`の`HOOKS=`指定を削除しておく。

## 一般ユーザーでpingが出来ない

* 「pingが許可されていない」など出る。nagiosでの監視も動かなくなるので治す。

```sh
chmod u+s /usr/bin/ping
```
Mac（Catalina）にSQLWorkbench/Jをインストールする

初めてのQiita投稿です。
お手柔らかにお願いします。

筆者の環境
・macOS Catalina バージョン10.15.7

＜インストール手順＞
1. Java（JDK,JRE）をインストールする。
2. SQLWorkbench/Jをインストールする。

【ハマったポイント】
SQLWorkbench/Jとの整合性なのか、Java11ではSQLWorkbench/Jが正常に起動しなかった。
SQLWorkbench/Jを起動すると、"Unable to load Java Runtime"と表示される。

#1.Java（JDK,JRE）をインストールする。
SQLWorkbench/Jを動かすためには、Javaが必要となります。
JREの入手先としてはいくつかありますが、OpenJDKのビルドであるZulu提供のものを使用します。
([Zulu版 Java JRE](https://jp.azul.com/downloads/zulu-community/?version=java-8-lts&os=macos&package=jdk)）

Zulu（他のJavaサポート）については、nowokayさんがまとめて下さっていますのでこちら参照ください。
https://qiita.com/nowokay/items/edb5c5df4dbfc4a99ffb#zuluzulu-enterprise

Java8をインストールしていきますが、macOSのバージョンが10.15ですので、"10.13 or later"のDMG版を選択して、ダウンロードします。
ダウンロード完了後、ファイルを開き、ウィンドウに従ってインストールを進めます。

---
（Javaインストールの確認）
Macでターミナルを開き、下記コマンドを実行する。
```$java -version```

下記のようなバージョン情報が表示されていれば、インストールは正常に完了しています。

```
openjdk version "1.8.0_282"
OpenJDK Runtime Environment (Zulu 8.52.0.23-CA-macosx) (build 1.8.0_282-b08)
OpenJDK 64-Bit Server VM (Zulu 8.52.0.23-CA-macosx) (build 25.282-b08, mixed mode)
```

Javaのパスが設定されていない場合、javaコマンドが実行できないため、下記作業を行う必要があります。
環境変数を通す。

```$export JAVA_HOME=`/usr/libexec/java_home -v 1.8`
```

```$source ~/.bash_profile
```

再度、```java -version```でバージョン情報が表示されるか確認してください。

#2. SQLWorkbench/Jをインストールする。
[パッケージダウンロード先](https://www.sql-workbench.eu/download-archive.html)より"Mac Package"から、macOS用のパッケージをダウンロードします。

ダウンロードフォルダに圧縮ファイルがあるので、そちらを解凍すると、SQLWorkbenchJ.appが追加されます。
一度、ダブルクリックで開いて、正常に起動できれば画像のような画面が表示されます。
（※筆者はここで、冒頭に書いたエラーが発生）
![スクリーンショット 2021-03-01 18.30.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1023924/63e0386a-deee-dc44-1798-ca9e4eb5358b.png)

Javaはインストールして、SQLWorkbenchもインストールできたけれど、初期設定がうまくできていない状態という解釈で
以下の作業を行います。

ターミナルを開き、SQLWorkbenchの設定ファイルを加筆修正。
```$vi /Applications/SQLWorkbenchJ.app/Contents/Info.plist```

加筆の内容は以下。

```
<key>JVMRuntime</key>
<string>zulu-8.jdk</string>
```
対象のファイルを保存し、シンボリックリンクの作成を行う。

```
$ln -s /Library/Java/JavaVirtualMachines/zulu-8.jdk /Applications/SQLWorkbenchJ.app/Contents/PlugIns/
```

改めて、SQLWorkbenchJのアプリケーションを起動すると、正常に起動！！
ドライバ入れるとこまでがインストールセットだと思いますが、本題はエラーで詰まった話なので割愛。
パッケージ、インポートについて簡単にまとめてみた

実務で絶対使うと言っても過言ではないではないので、簡単にまとめました。

#パッケージ
###簡単に言うとグループ(パッケージ)を作ってクラスを分かりやすくすること

（例）車を作るとします
![ev002.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/700700/c98720d5-7fe0-f62d-676b-b00b48e02b56.jpeg)

車を作るのに様々なクラス(部品)が必要で、組み合わせることで車が出来上がります。
図のようにグループわけをすることによってクラスが見やすくなり、変更・追加が楽になります。
クラスが増えれば増えるほど、パッケージの必要性が感じると思います。
ソースはインポートで見ていきましょう。

#インポート
###簡単に言うと他のクラスを呼び出し使うことができます。

（例）車を作るとします

![ev001.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/700700/859d8923-f179-85d7-5ac0-58567b598014.jpeg)
ソースの階層はこんな感じです。
Main.javaで他のクラスを呼び出し、車を作りたいと思います。

まずはクラスから

```java:Exterior.java
package exterior;

public class Exterior {
	public void bon() {
		System.out.println("ボンネット");
	}

	public void mirror() {
		System.out.println("ミラー");
	}

	public void taiya(){
		System.out.println("タイヤ");
	}

	public void glass() {
		System.out.println("ガラス");
	}
}
```
```java:Interior.java
package interior;

public class Interior {

	public void air(){
		System.out.println("エアコン");
	

	public void handle(){
		System.out.println("ハンドル");
	}

	public void seat(){
		System.out.println("座席");
	}
}
```

```java:Other.java
package other;

public class Other {

	public void engine(){
		System.out.println("エンジン");
	}

	public void scarf(){
		System.out.println("マフラー");
	}
}
```
ここまででパッケージでも説明したようにクラス分けができていると思います。
では、クラスを呼び出していきましょう。

```java:Main.java
package main;

import exterior.Exterior;//外装呼び出し
import interior.Interior;//内装呼び出し
import other.Other;//その他呼び出し

public class Main {

	public static void main(String[] args) {
		//インスタンス
		Exterior e = new Exterior();
		Interior i = new Interior();
		Other o = new Other();

		//外装
		e.bon();
		e.mirror();
		e.taiya();
		e.glass();
		
		//内装
		i.air();
		i.handle();
		i.seat();
		
		//その他
		o.engine();
		o.scarf();
	}

}

//実行すると
ボンネット
ミラー
タイヤ
ガラス
エアコン
ハンドル
座席
エンジン
マフラー

```

このようにインポートを使って他のクラスを呼び、車が出来上がります。
あくまで、ここでは例ですけど、流れはこんな感じです。
初学者がPHPを学び始める3

##関数について
関数とは、あるまとまった処理を行い、値を返すものです。 PHPには便利な関数がもとから組み込まれていて、それらは組み込み関数と呼ばれます。
「strlen」は組み込み関数の1つで、文字列の文字数を返します。
配列の要素数を数える「count」、ランダムな整数を返す「rand」などが他にもあります。
ここで、()の中に与える値を「引数」といいます。

以下の場合は上から
５
３
１０〜１５のランダムな数字が出力されます。

```php
  <?php

    $str = 'こんにちは';

    echo strlen($str);
        
    $array = array('HTML', 'CSS', 'PHP');

    echo count($array);
    
    echo rand(10,15);
    
  ?>

```

##関数の作り方
関数を作るには「function 関数名(){ 処理 }」という書き方をします。
関数名は自由につけることが出来ます。
関数は「関数名()」というように()をつけて呼び出すことができます。

以下の記述の場合は
Hello, world!!を出力します。

```
  <?php
    function hello(){
      echo "Hello, world!!";
    }

    hello();
  ?>

```


また関数を呼び出す際、関数に値を渡すと、関数内でその値を利用することができます。この値を「引数」と呼びます。引数を関数内で使うには、それを受け取るための「仮引数」を、関数の定義時に指定しておく必要があります。仮引数が複数ある場合はカンマ（,）を用いて指定します。

例えば四角形の面積を出したいときは下記のようになります。
この場合だと50が出力されます。

```
  <?php
    function squareArea($height,$width){
      echo $height*$width;
    }
        
    squareArea(5,10);
  ?>
```
R で chron パッケージでの年の計算

# はじめに
R で日付データの取り扱いを悩んでいたところ、`chron`というパッケージを見つけた。こちらの方がDate型やPOSIXct型より扱いが簡単そうなので、使ってみる。あるデータセットで扱うために、年/月/日付のデータから小数の年表示（例えば2021/3/1 -> 2021.164）と計算するような関数の作成を目指す。

※2021/03/03 計算間違いを修正した。

# `chron`での日付の扱い
以下、chron パッケージを用いた場合の年月日の扱いを書いていく。

```R:
> date_str <- "2021/03/01"
> dts <- dates(date_str, format = "y/m/d")
> dts
[1] 03/01/21
>
> #日付の足し算、引き算
> dts + 1
[1] 03/02/21
> dts - 1
[1] 02/28/21
>
> #日付の差分
> date_str2 <- "2021/03/15"
> dts2 <- dates(date_str2, format = "y/m/d")
> dts2 - dts
Time in days:
[1] 14
```
直感的に足し算、引き算が取り扱える。関数でうるう年の判定も可能

```R:
> #年を直接入力するとTRUE,FLASEが返ってくる。
> leap.year(2021)
[1] FALSE
> leap.year(2020)
[1] TRUE
>
> #日付をそのまま入れてもよい
> leap.year(dts)
[1] FALSE
> 
```

日付から年、月、日をそれぞれ独立に取り出したい時は、

```R:
> mdy <- month.day.year(dts)
> mdy$year
[1] 2021
> mdy$month
[1] 3
> mdy$day
[1] 1
```
とすると、個別に扱える。


# 小数年を計算する関数
`chron`を用いて小数年を計算する関数を作成した。

※日数を計算する所の修正を行った。

```R:calc_year.R
library(chron)

calc_year <- function(date_str) {
  dts <- dates(date_str, format = "y/m/d")
  mdy <- month.day.year(dts)
  date_str0 <- paste(mdy$year-1, "/12/31", sep = "")
  dts0 <- dates(date_str0, format = "y/m/d")
  if (leap.year(dts)) {
    days <- 366
  } else {
    days <- 365
  }
  res <- mdy$year + (as.integer(dts - dts0))/days
  return(res)
}
```

実際に動かしてみると、

```R:
> source("calc_year.R")
> calc_year("2021/3/1")
[1] 2021.164
```

となり、計算ができていることが分かる。


# おわりに
今回は年月日のみ扱ったが、`chron`では時刻も扱えるようであるので、それについても記事にしていきたい。
【HTML】お問い合わせフォームの作成方法

今回は、HTMLでフォームの外観を作成する方法について解説します。

#formとは
　フォームとは、ユーザーが入力した情報をサーバーに送信するためのページになります。サーバーとのやり取りが必要になるので、Webサイトを作る中でもこのフォームを扱うページはPHPなどの言語で書かれることが多くなります。HTMLではフォームの入力する部分などの外観を作ります。
　その入力された値をPHPで渡すことで、PHPでPOST通信（個人情報を扱うときに使う方法）を行ってサーバーに送信されます。

##actionで送信先を指定する
　フォームデータの送信先はaction属性で指定します。具体的にはform action="送信先のURL"という形になります。何も指定されていない場合は、現在のページのURLが送信先になります。

```
<form action="/example.php">
</form>
```
##methodで送信方法を指定する
formのmethod属性では、データをどのように送るか指定します。ここで使用できるメソッドはGETとPOSTの２種類があります。お問い合わせフォームの場合はPOSTにするのが一般的です。

```
<form action="/example.php" method="post">
</form>
```
　formに書くこれら２つの属性についてはhtmlを学習中の段階では深く理解してなくても問題ありません。PHPを使用してフォームのデータ処理をするときに理解できれば大丈夫です。

#フォーム部品一覧
　次にフォームの各項目について学んでいきましょう。<form>の中にはテキストの入力欄やチェックボックス、ファイルのアップロードなどさまざまな部品を入れます。ここでは特に覚えておきたいフォーム部品を中心に紹介していきます。
##inputタグ
　まずはテキストフィールドの作り方です。テキストフィールドは改行なしの文章を入力するためのフォームです。作成にはinputタグを使用します。

```
<input type="text" id="name" name="name">
```
　inputタグでは<type="○○">に書く内容によって表示されるテキストフィールドの種類を指定します。今回は名前の入力欄を作るためにtype="text"としました。
　その他にも数字専用の入力フィールドにするなら<type="number">、メールアドレスにするなら<type="email">などがあります。


##labelタグ
labelタグは、inputなどの入力欄にラベル（項目名や説明）をつけるために使用します。

```
<label for="email">E-mail</label>
<input type="mail" id="email" name="email">
```
「何を入力する欄なのか」がわかるようにするために使うということです。
ここで決めているid属性(input id="○○")がlabelのfor属性(label for="○○")と紐づいています。こうすることで項目とフォームを連動させることができます。
「name="○○"」は、それぞれのフォームで入力された値を管理するidやclass名のようなものです。name属性はその入力項目のデータをPHPで操作するときに使用します。これにより、一度に複数の値が送信された場合でも、どれがどの値かを認識することができます。

##textareaタグ
　<textarea>タグは複数行の入力欄を設置するために使います。textareaはテキストを入れ子にする必要はありませんが、閉じタグを書かなければいけないので注意しましょう。

```
 <label for="message">MESSAGE</label>
 <textarea id="message" name="message"></textarea>
```
　テキストエリアの入力フィールドは、ボックスの右下をドラッグすることでサイズを自由に変更することができます。もしサイズの変更ができない場合は、CSSでresize :both;と指定します。

#まとめ
　今回はHTMLでフォームの外観を作る方法について解説してきました。ここで解説したのはほんの一部で、他にもoptionタグやbottomタグなどがあります。
自分の作りたいフォームに合わせて、調べながらコーディングしていきましょう。
VSCode + Docker で C++ 使って WASM の開発環境構築

## ことの発端

VSCode + Docker で C++ の開発環境が簡単に構築できたので、そのまま、WASMの開発環境も作ってしまえということで、メモ書きです。

## 手順

### 1. コンテナ作成〜CMakeLists.txt作成

前に投稿させていただいた「[C++の開発環境をDockerで構築してVSCodeでリモート開発](https://qiita.com/terukazu/items/93a7362d501dd1d4a401) 」の手順で進めますが、```Docker``` ファイルを下記のファイルに差し替えてください。[^1]

```dockerfile:Dockerfile
FROM ubuntu:18.04
RUN apt-get update \
 && apt-get install -y build-essential gdb cmake git python3 \
 && git clone https://github.com/emscripten-core/emsdk.git \
 && cd emsdk \
 && ./emsdk install latest \
 && ./emsdk activate latest \
 && echo 'source "/emsdk/emsdk_env.sh"' >> $HOME/.bashrc
```

コンテナのビルドやっちゃった人は、```Dockerfile``` を上のように書き換えて [F1] でコマンドパレットを表示させて```Remote-Containers: Revuild Container```を実行すれば、コンテナをリビルドしてくれます。（便利だ〜）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1038343/bf090970-53f2-21ae-deb9-c51c11518e8a.png)


[^1]: [本家の手順](https://emscripten.org/docs/getting_started/downloads.html)では ```.bash_profile``` に環境設定を追加していますが、```.bashrc``` に追加する変更を加えてます。（参照記事： https://teratail.com/questions/45915 ）



### 2. CMakeLists.txt に追加

```CMakeLists.txt``` に下記の行を追加します。

```cmake:CMakeLists.txt
set(CMAKE_EXECUTABLE_SUFFIX ".html")
```

こいつを指定しないと html を自分で書くことになります。

### 3. emscripten 用のビルドフォルダを作成してビルド

下記の順にコマンドを実行

```sh
mkdir build.em
cd build.em
emcmake cmake ..
make
```

### 4. 実行

ブラウザのセキュリティ設定によりますが、大概はローカルファイルだとwasmのロードでエラーして動かないと思います。
なので、VSCodeの拡張機能 [Live Server : ritwickdey.liveserver](https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer) をインストールします。インストールすると、右下に ```Go Live``` が表示されるので、こいつを押下します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1038343/1351d6c3-010d-4e01-ce85-055f400c8a5b.png)

ブラウザにファイル一覧が表示されると思うので、```build.em``` フォルダをクリックして、```html```ファイルをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1038343/77082ea7-74af-1de0-57aa-dd8d58bdf3a3.png)
こんな感じで表示されれば完成です。
作品を評価し合うwebアプリ作ってみた[個人開発]

##今回作ったもの

https://www.prorp.net/

タイトルにもある通り、絵や音楽、プログラムなどの作品を評価し合うコミュニティサイト「**PRORP**」です。

![4598734.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/480294/65cd29dd-9a94-ba01-0bc4-c7338faa3195.png)

##なぜ作ったか

ヤフー知恵袋でこんな質問を見つけました。

https://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q1396515328

塾に通ってるなら他人からの評価は簡単に受けれますが、独学の場合なかなかしっかりと評価を受けるときってありません。
以外に他人の評価って欲しがってる人が多いのかなと思い作りました。

##工夫した点

**投稿一覧ページをなくした**
公開したばかりでユーザーも投稿も少ないのでユーザーが投稿が見る方法は検索だけにしました。
使いずらいですが最初はしょうがないんです。すみません(__)

**★評価機能**
![25478.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/480294/e5f2ca60-1332-99ce-c5c1-941add00965a.png)

Raty.jsというものを使い実装しました。まあまあ簡単です。
平均値も求められます。

##開発効率化のために
今回、初めて土台アプリを作ってみました。
というのもwebアプリ開発って結構同じことの繰り返しじゃないですか。CRUD、コメント機能、いいね機能、通知機能など
どんなアプリを作るにしろ必要な機能ってありますよね？
それを毎回毎回作ってたら非効率的だなと思いある程度、完成してる土台アプリを先に作りそれをコピーしてPRORPを作りました。

土台アプリでの開発の感想としては**とにかく早いです**
当たり前ですが新しく１から作るよりカスタムするほうが早いので、土台アプリは作ってよかったなーと思ってます。

土台アプリに実装した機能としては

- CRUD
- コメント機能
- いいね機能
- 通知機能
- ヘッダー、フッター、２カラムレイアウトなどのCSS
- 新規登録、ログイン機能
- ユーザーIDとの紐付け

くらいです。もちろん土台アプリなので汎用性重視です。

##使ったgem

```
（省略）

gem 'ridgepole'
gem 'slim-rails'
gem 'html2slim'
gem 'pry-rails'
gem 'devise'
gem 'kaminari'
gem 'activeadmin'
gem 'rack-attack'
gem 'rails-i18n'
gem 'devise-i18n'
gem 'devise-i18n-views'
gem 'carrierwave'
gem 'fog-aws'
gem 'dotenv-rails'
gem 'rmagick'
```

##まとめ

土台アプリは今度からも使っていこうと思います。
作りたいものがない、という方は土台アプリをまず作ってみるといいかもしれません。

まぁとにかくこの**PRORP**使ってくれ！（誘導下手）

https://www.prorp.net/

ツイッターもやってます！
https://twitter.com/yamada1531
package.jsonから.envを参照したい場合

# 状況
package.jsonに記述したスクリプトからトークンを参照したい場合があり、以下のように直接記述するのではなく.envで管理したい。

```js
// package.json

...
  "scripts": {
    "chromatic": "npx chromatic --project-token ABCDEFG1234"
  }
...
```

# 対応

package.jsonに埋め込めなさそうだったので、大人しく別のスクリプトファイルに切り出すことにした。

```ini
# .env

CHROMATIC_TOKEN=ABCDEFG1234
```


```js
// scripts/chromatic-upload.js

const path = require("path");
const dotenv = require("dotenv");
const env = dotenv.config({path: path.join(__dirname, "../.env") }).parsed;
const exec = require("child_process").exec;

const res = exec(`npx chromatic --project-token ${env.CHROMATIC_TOKEN}`);
console.log(res);
```

package.jsonから呼び出すように修正。

```js
// package.json

...
  "scripts": {
    "chromatic": "node scripts/chromatic-upload.js"
  }
...
```
amplifyの作法 その9

#概要

amplifyの作法、調べてみた。
qubo使ってみた。

#参考にしたページ

https://qiita.com/tamurahey/items/0c70a3d37e1367f3d67c

#サンプルコード

```
from typing import Set, Tuple, Container, Dict, Sequence
from amplify import (BinaryPoly, BinaryQuadraticModel, sum_poly, gen_symbols, Solver, decode_solution, )
from amplify.client import FixstarsClient

def to_dict(q: np.ndarray) -> Dict[Tuple[int, int], float]:
	assert len(q.shape) == 2 and  q.shape[0] == q.shape[1]
	return dict(zip(zip(*np.nonzero(q)), q[q != 0]))

def qubo_number_partition(number_set: Container[float]) -> np.ndarray:
	number_set = np.array(list(number_set))
	c = number_set.sum()
	q = np.outer(number_set, number_set)
	np.fill_diagonal(q, number_set * (number_set - c))
	return q

s = np.array([3, 1, 1, 2, 2, 1])
q = to_dict(qubo_number_partition(s))
f = BinaryPoly(q)
model0 = BinaryQuadraticModel(f)
solver = Solver(client)
result = solver.solve(model0)
values = result[0].values
print("a")
for i in range(6):
  if (values[i] == 1):
    print(s[i])
print("b")
for i in range(6):
  if (values[i] == 0):
    print(s[i])


```

以上。
【Nuxt】pluginsにはexport default を付けよう

Nuxt.jsのpluginsを使っててハマった話

## 結論
pluginは`export default` しようぜって話

```~/plugins/plugin.js
export default () {
  // 処理
}
```

## 背景
pluginは`nuxt.config.js`の上から順番に実行されるが、`export default`をしないと順番通りに実行されない。

```nuxt.config.js
  // first→second→thaad　の順番で実行される
  plugins: [
    {
      src: '~/plugins/first.js',
      mode: 'client'
    },
    {
      src: '~/plugins/second.js',
      mode: 'client'
    },
    {
      src: '~/plugins/thaad.js',
      mode: 'client'
    },
  ]
```

## 理由
下記のようにpluginsがbuildされるが、
`export default`をしていないと、importされた時点で即コードを実行するので順番通りにならない。

```_nuxt/index.js 
/* Plugins */
import nuxt_plugin_first_08b2fdac from 'nuxt_plugin_first_08b2fdac' // Source: ./first.js (mode: 'client')
import nuxt_plugin_second_18f96a7c from 'nuxt_plugin_second_18f96a7c' // Source: ./second.js (mode: 'client')
import nuxt_plugin_thaad_68e7ee02 from 'nuxt_plugin_thaad_68e7ee02' // Source: ./thaad.js (mode: 'client')

if (process.client && typeof nuxt_plugin_first_08b2fdac === 'function') {
  await nuxt_plugin_first_08b2fdac(app.context, inject)
}
if (process.client && typeof nuxt_plugin_second_18f96a7c === 'function') {
  await nuxt_plugin_thaad_68e7ee02(app.context, inject)
}
if (process.client && typeof nuxt_plugin_thaad_68e7ee02 === 'function') {
  await nuxt_plugin_thaad_68e7ee02(app.context, inject)
}
```
git を使った後にファイルが消えたときの対処法

#概要
ブランチからmasterブランチへ移動できていない状況で add、 commit、 push、 出現したエラーの対処などをして、その後エディタを開いたらファイルが１週間前の状態に戻っているように見えました。

#対処法
`git reflog` を実行してcommitした履歴、resetした履歴、pullした履歴などを表示して状況を確認します。
新しい履歴が上から順に表示されます。

```
$ git reflog
66d7034 (origin/master, master) HEAD@{1}: commit: update file
6cb041e HEAD@{2}: commit: update file
33162bd HEAD@{4}: commit: add new file
56d4f81 HEAD@{5}: checkout: moving from 
56d4f81 HEAD@{6}: checkout: moving from master to origin/maste
```
戻りたい履歴の番号を把握して、 `git checkout 戻りたい履歴の番号` を実行します。
例えば今回は `6cb041e` まで戻したいので以下のように実行しました。

```
$ git checkout 6cb041e
```
【超かんたん】Fakerを使ってダミーデータを作成しよう！

Fakerを利用してアプリにダミーデータ生成します。
今回は、deviseで作成したusersテーブルにダミーデータを投入していきます。

##Fakerとは
Fakerとはランダムな値を生成してくれるGemです。人名、メールアドレス、パスワードの生成はもちろんのことゲームのタイトルやアニメのキャラクター名なども生成してくれる遊び心がいっぱい詰まったGemです。

https://github.com/faker-ruby/faker

では、さっそく使って行きましょう！

##Fakerの導入
####gamのインストール

```:Gemfile
gem 'faker'
```

```:ターミナル
bundle install
```

導入完了。

##ダミーデータの生成

今回は、deviseで生成されるemailとpasswordの他にnameカラムを追加しています。

deviseで生成されるemailカラムには一意性制約があるので**Faker::Internet**と**free_email**の間に**unique**と記述しています。

passwordもデフォルトでは6文字以上でないと登録できないので**min_length: 6**と記述しています。この辺りはご自身が制作するアプリの仕様によって適宜変更していきましょう。
 
```ruby:db/seeds.rb
50.times do
  name = Faker::Name.name
  email = Faker::Internet.unique.free_email
  password = Faker::Internet.password(min_length: 6)
  
  User.create(
    name: name,
    email: email,
    password: password,
    password_confirmation: password
  )
end
```

```:ターミナル
rails db:seed
```

それでは確認してみましょう。
![user.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1140333/3188803c-02c1-71d9-53c7-cac975bbaf8b.jpeg)

ダミーデータが生成されています。

##日本語化してみよう
Fakerは一部、日本語対応しています。

日本語化はとても簡単です。
config/application.rbに**config.i18n.default_locale = :ja**という記述を追加するだけです。

```ruby:config/application.rb
module アプリ名
 class Application < Rails::Application
   # Initialize configuration defaults for originally generated Rails version.
   config.load_defaults 6.0
   config.i18n.default_locale = :ja #追加
   # 省略
 end
end
```
```:ターミナル
rails db:seed
```

![user2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1140333/84a9ac83-6985-a125-51de-bf80ca49a119.jpeg)
日本語化完了。


##番外編

ここからは番外編です。
ダミーデータの名前を変更してみましょう。
詳しくは[公式ドキュメント](https://github.com/faker-ruby/faker)を参照しましょう。

まずはja.ymlファイルを作成します。

```:ターミナル
touch config/locales/ja.yml
```

[こちら](https://github.com/faker-ruby/faker/blob/master/lib/locales/ja.yml)の記述を作成したja.ymlにコピペし日本語化しましょう。
※日本語化できるのは一部だけです。

####ポケモンの場合
```ruby:db/seeds.rb
50.times do
  name = Faker::Games::Pokemon.name #変更
  email = Faker::Internet.unique.free_email
  password = Faker::Internet.password(min_length: 6)
  
  User.create(
    name: name,
    email: email,
    password: password,
    password_confirmation: password
  )
end
```
```:ターミナル
rails db:seed
```
![p.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1140333/707a82bc-887b-3b23-8edd-6084b3f0ec97.jpeg)

####スーパーマリオの場合

```ruby:db/seeds.rb
50.times do
  name = Faker::Games::SuperMario.character #変更
  email = Faker::Internet.unique.free_email
  password = Faker::Internet.password(min_length: 6)
  
 #以下略
```
```:ターミナル
rails db:seed
```
![ま.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1140333/08cc9fd5-c91a-e4c7-cfba-4550b084d843.jpeg)

####ドラゴンボールの場合
```ruby:db/seeds.rb
50.times do
  name = Faker::JapaneseMedia::DragonBall.character #変更
  email = Faker::Internet.unique.free_email
  password = Faker::Internet.password(min_length: 6)
  
 #以下略
```
```:ターミナル
rails db:seed
```
![d.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1140333/80e22a94-6002-834d-54c4-7c99a14a9fb1.jpeg)
こちらは日本語対応していないみたいです。

以上になります。
MinikubeでKubernetes(K8S)を体験してみる

# はじめに

Kubernetes(K8S)の小規模パッケージ「Minikube」なら簡単に操作を体験することができます。
以下に環境構築から簡単な使い方をまとめてみました。

# システム要件

以下の環境が構築されているものとします。
未導入の場合は適宜インストールを実施してください。
細かいバージョンは不一致でも問題ないと思います。

Windows 10
https://www.microsoft.com/ja-jp/software-download/windows10

VirtualBox 6.1.18
https://www.virtualbox.org/wiki/Downloads

Vagrant 2.2.14
https://www.vagrantup.com/downloads

Git Bash
https://gitforwindows.org/

# 手順

Git Bash にて

```
作業ディレクトリへ移動します
$ cd work

GitHubで公開されている「Minikube on Ubuntu」をクローンします
$ git clone https://github.com/odonzyk/minikube-vagrant.git

クローンしたディレクトリへ移動し起動します
$ cd minikube-vagrant
$ vagrant up

リスタートしたほうが良いようなので、一旦停止し再度起動します
$ vagrant halt
$ vagrant up

VagrantにSSH接続します
$ vagrant ssh
```

SSH接続後、Minikubeを起動してみます

```
$ minikube start
```

既存イメージのechoserverというWebサーバーを利用して、Podの作成と、8080番ポートでのサービス公開を行ってみます

```
Pod作成
$ kubectl create deployment hello-minikube --image=k8s.gcr.io/echoserver:1.10

Service登録
$ kubectl expose deployment hello-minikube --type=NodePort --port=8080
```

Podの状態やServiceを確認してみます


```
$ kubectl get pods
NAME                              READY   STATUS    RESTARTS   AGE
hello-minikube-5d9b964bfb-z6ghh   1/1     Running   0          21m

$ kubectl get services
NAME             TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
hello-minikube   NodePort    10.101.65.232   <none>        8080:30439/TCP   21m
kubernetes       ClusterIP   10.96.0.1       <none>        443/TCP          95m

```

URLを確認します

```
$ minikube service hello-minikube --url
http://192.168.49.2:30439
```

確認したURLをcurlコマンドでアクセスしてWebサーバーからの応答を見てみます

```
$ curl http://192.168.49.2:30439


Hostname: hello-minikube-5d9b964bfb-z6ghh

Pod Information:
        -no pod information available-

Server values:
        server_version=nginx: 1.13.3 - lua: 10008

Request Information:
        client_address=172.17.0.1
        method=GET
        real path=/
        query=
        request_version=1.1
        request_scheme=http
        request_uri=http://192.168.49.2:8080/

Request Headers:
        accept=*/*
        host=192.168.49.2:30439
        user-agent=curl/7.58.0

Request Body:
        -no body in request-
```

サービスの削除

```
$ kubectl delete services hello-minikube
```

Podの削除

```
$ kubectl delete deployment hello-minikube
```

Minikubeの停止

```
$ minikube stop
```

# 参考サイト

https://github.com/odonzyk/minikube-vagrant
https://kubernetes.io/ja/docs/setup/learning-environment/minikube/
[MCWorks64]円グラフの回りを動的に移動するラベル

MCWorks64で円グラフに追従するラベルの作り方です。
これを応用して色々表現できそうです。

![edyf6-cprh6.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504156/9dc62d13-254e-b5ea-c5c4-3ace94695285.gif)

#作り方
![凡例1ダイナミックオンスクリーン - コピー.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504156/40511079-3774-9a18-dade-5b4b4648079e.png)
１．動かしたいラベル（正確には「プロセスポイント」）をスタート位置に配置。
２．ダイナミックプロパティの「位置」を追加。
３．追加した「位置」ダイナミックプロパティの「ダイナミックオンスクリーンの編集」をクリック。
４．図のように移動するルートのパスを設定できるようになるので、移動したいルートを引いてあげる。
以上

LowLimitを0、HighLimitを100にしてあるので、例えば、DataSourceが50だとラベルは6時の方向に移動します。

ルートを工夫することで色々表現できそうです。工場ラインを流れるワークとか、トラック上を走る人の位置表示とか。
データ分析やデータの見える化に必要な表現ではないですが、見栄えにこだわるなら使えそうです。
EKSのIAM Roles for Service Accountsをterraformで設定する



terraformだけでIAM Roles for Service Accountsを設定する際のメモ

## AWSの設定(IAM Role)

```
data "aws_eks_cluster" "this" {
  name = "my_cluster"
}

data "tls_certificate" "this" {
  url = data.aws_eks_cluster.this.identity[0].oidc[0].issuer
}

resource "aws_iam_openid_connect_provider" "this" {
  client_id_list  = ["sts.amazonaws.com"]
  thumbprint_list = [data.tls_certificate.this.certificates[0].sha1_fingerprint]
  url             = data.aws_eks_cluster.this.identity[0].oidc[0].issuer
}

data "aws_iam_policy_document" "eks_pod" {
  statement {
    effect = "Allow"
    principals {
      identifiers = [aws_iam_openid_connect_provider.this.arn]
      type        = "Federated"
    }
    actions = ["sts:AssumeRoleWithWebIdentity"]
    condition {
      test     = "StringEquals"
      variable = "${aws_iam_openid_connect_provider.this.url}:aud"
      values   = ["sts.amazonaws.com"]
    }
  }
}

resource "aws_iam_role" "this" {
  name               = "iam-roles-for-service-accounts-role"
  assume_role_policy = data.aws_iam_policy_document.eks_pod.json
}

resource "aws_iam_role_policy_attachment" "this" {
  role       = aws_iam_role.this.name
  policy_arn = "arn:aws:iam::xxxxxxxxx:policy/policy-name-with-path"
}
```

## K8Sの設定(service account)

```
resource "kubernetes_service_account" "default" {
  metadata {
    name      = "my_service_account"
    namespace = "default"
    annotations = {
      "eks.amazonaws.com/role-arn" = aws_iam_role.this.arn
    }
  }

  automount_service_account_token = true
}
```

## podからservice accountを指定

```yaml
apiVersion: v1
kind: Pod
metadata:
  name: my-pod
spec:
  serviceAccountName: my_service_account
  automountServiceAccountToken: false
...
```
Debug.Log()に複数の変数を入れる

#　前置き
すごい技とかじゃないけど、上の方にこの記事がなかったから自分が普段やってるのを書く。
もしかしたらこの書き方がダメな理由があるか、もっと良い方法があるのかも。

#　本題

$""の中の{}に変数を書いて使う。
やり方はこう↓

```C#:a
int price = 200;
int num = 3;
string name = "太郎";
int money = 1000;

Debug.Log($"{price}円のリンゴを{num}個買ったら、{name}の所持金は{money - price * num}円だよ。");
//=> 200円のリンゴを3個買ったら、太郎の所持金は400円だよ。
```
MySQL8.0のUDFを使ってみる

# 目的
MySQLのUDFをビルドし使ってみる。(とりあえずサンプルをビルドする。)

# 事前準備

- 下記のコマンドを実行しコンパイラ・ツール等をインストールしておく。

```
# apt-get update
# apt-get install -y libmysqlclient-dev
# apt-get install -y g++
# apt-get install -y gcc
# apt-get install -y git
```

<!--
- mysql.hが配置されているディレクトリを調べる。

```
# mysql_config --cflags
-I/usr/include/mysql
```
-->

- プラグインを配置するディレクトリを調べる。

```
# mysql_config --plugindir
/usr/lib/mysql/plugin
```

# ビルド

```
# git clone --depth=1 https://github.com/mysql/mysql-server.git
# cd mysql-server/sql
# gcc --shared -o /usr/lib/mysql/plugin/udf_example.so \
    -fPIC \
    `/usr/bin/mysql_config --cflags` \
    udf_example.cc
# mv ./udf_example.so /usr/lib/mysql/plugin/udf_example.so
```

# MySQLへUDFを登録

```
mysql> CREATE FUNCTION metaphon RETURNS STRING SONAME "udf_example.so";
mysql> CREATE FUNCTION myfunc_double RETURNS REAL SONAME "udf_example.so";
mysql> CREATE FUNCTION myfunc_int RETURNS INTEGER SONAME "udf_example.so";
mysql> CREATE FUNCTION sequence RETURNS INTEGER SONAME "udf_example.so";
mysql> CREATE FUNCTION lookup RETURNS STRING SONAME "udf_example.so";
mysql> CREATE FUNCTION reverse_lookup RETURNS STRING SONAME "udf_example.so";
mysql> CREATE AGGREGATE FUNCTION avgcost RETURNS REAL SONAME "udf_example.so";
```

# 登録したUDFを確認する
```
mysql> SELECT * FROM mysql.func;
```

# 登録したUDFを使ってみる

```
mysql> select sequence(9);
```

# 登録したUDFを削除する

```
mysql> DROP FUNCTION metaphon;
mysql> DROP FUNCTION myfunc_double;
mysql> DROP FUNCTION myfunc_int;
mysql> DROP FUNCTION sequence;
mysql> DROP FUNCTION lookup;
mysql> DROP FUNCTION reverse_lookup;
mysql> DROP FUNCTION avgcost;
```
約数を高速に計算する方法

とりま備忘録.高速に計算する方法


```c++

std::vector<long long> divisor(long long n) {
    std::vector<long long> ret;
    for (long long i = 1; i * i <= n; i++) {
        if (n % i == 0) {
            ret.push_back(i);
            if (i * i != n) ret.push_back(n / i);
        }
    }
    sort(ret.begin(), ret.end()); // 昇順に並べる
    return ret;
}
```


これによって,O(√N)で計算できるそう.楽やね～
chmodとファイルの権限

# 前提
今回の記事は、chmodを最近使い始めた私が理解したこと整理するために書いたものです。
この記事でファイルの権限等について、全てが分かるわけではないのでご了承ください。
今回の記事で紹介するのはlinuxコマンドとなります。
使用マシンは"macbookpro(2020) 13インチ"です。

# linuxコマンドでファイルの権限を確認する方法
以下のコマンドで確認できます。

```
ls -l
```

# 権限の見方
![スクリーンショット 2021-02-21 10.01.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/598360/444a9f8d-25d1-6092-cc94-0c8e29c14ede.png)

画像は私の環境のものです。
（user名が写っていますが、qiitaのアカウント名と同じなので加工しておりません）

今回は一番左の権限について（"drwxr-xr-x"の部分）のみ触れていきます。

* **文字数**
添付画像を見て分かる通り、全てハイフン含め10文字になっていることがわかります。
これら10文字の文字列を、**1文字-3文字-3文字-3文字**に区切って見ていきます。
-rw-r--r--
drwxr-xr-x
drwxr-xr-x  


* **最初の1文字目**
"-(ハイフン)"または、"d"が表示されている部分です。
これは、
**"-(ハイフン)"**：ファイル（viで開けるやつだと思います）
**"d"**：ディレクトリ

* **2〜4文字目、5〜7文字目、8〜10文字目**
2〜4文字目
　→　所有者の権限を表しています。今回だと、"matsumotoyuutarou"の部分が所有者になります。
5〜7文字目
　→　所有者グループの権限を表しています。今回だと、staffの部分です？
8〜10文字目
　→　その他（所有者、所有者グループ以外）の権限を表しています。  
r, w, x, - のいずれかが入っていることがわかります。
2〜4文字目, 5〜7文字目, 8〜10文字目 の文字はそれぞれ必ず以下それぞれの権限があればrwxの順番で表示されることになっています。
で、権限がない部分は"-"が入るわけです。
例えば、読み取り可能で、それ以外は不可な場合は"r--"のようになります。
**"r"**：読み取り可能
**"w"**：書き込み可能
**"d"**：実行可能

# chmodコマンド

```
chmod 777 ファイル名
```
例えば、上記のようなコマンドを入力すると、ファイルの権限を変えることが可能です。
今回は数字の部分（777のところ）を記載します。
前述で出てきたr, w, xには、それぞれ数字があり、以下のような数字が割り振られています。
r：4
w：2
x：1

**777の3桁の数字は、以下を表します。**
一番左の桁：前述の2〜4文字目の権限
真ん中の桁：前述の5〜7文字目の権限
一番右の桁：前述の8〜10文字目の権限

で、777の3桁それぞれの数字自体は、どの操作が可能なのかを表していて、可能な操作の数字を足した数になっています。
例えば、"7"は読み取り、書き込み、実行全てが可能になります。
読み取りと書き込みだけを可能にする場合は、"6"になるといった感じです。

ファイルaが「所有者の読み取りのみ可能」（r--------）だとします。
これを、「所有者と所有グループが読み込みと書き込み可能、その他が読み込みのみ可能」の状態にしようとすると、以下のようなコマンドになります。

```
chmod 664 ファイルa
```

# 最後に
今回は、最近学んだことを整理するために記事を書きました。
分かりづらい部分や、間違いがありましたらご指摘いただけますと幸いです。
Gitで過去の全てのコミットのAuthor情報を変更

自環境以外の環境で開発作業をしていてGitにコミットする時はAuthor情報を自分の情報に直して[^conf]からコミットするのが正しいやり方ですが、うっかり直し忘れてコミットをしてしまった後に気付いた場合、1つだけなら`git commit --amend --author="Auter's Name<author@email.com>"`で修正出来ますが、修正箇所が多数の場合は以下の様にします。

```bash
git filter-branch -f --env-filter "GIT_AUTHOR_NAME='new_user_name'; GIT_AUTHOR_EMAIL='new_email'; GIT_COMMITTER_NAME='old_user_name'; GIT_COMMITTER_EMAIL='old_email';" HEAD
```

* `old_user_name`が`new_user_name`に、`old_email`が`new_email`に書き換わります。

# Reference
* [Git の Commit Author と Commiter を変更する](https://qiita.com/sea_mountain/items/d70216a5bc16a88ed932)

[^conf]: `git config --local user.name="User Name"`, `git config --local user.email="user@email.com"`で作業リポジトリ限定でユーザー情報を自分の情報に変更出来ます。