OmniAICreator/Qiita-1.07M huggingface dataset:
【Python初心者】社内業務の自動化（Webスクレイピング編）

# 自己紹介
ITでもプログラマでもない、生産系のEngineerです。
現在の業務も、プログラミングは必要ありませんが、自部門の業務改善に
　『Pythonのプログラミングが役にたつのでは？』
という、かなり単純な考えのもと、2020年9月から個人的にプログラミングを勉強し始めました。
最初の3か月間は、とにかく基礎的なことを身に着けるのに徹しました。
　『そろそろ、具体的に動くプログラムを作って業務に適用してやろう！』
と思い、ググりまくりながらプログラムを書いています。

# 取り組み内容
各種社内システムを使った、データの入力・登録・参照・更新などの業務が多々あります。
人頼りのやり方なので、データ量や頻度が多くなると（少なくてもですが...）、時間（工数）が増大しミスも増えます。
システム改修がいちばんと思いますが、予算、工数など現実的ではありません。
そこで、解決する手段として機械化（自動／半自動）を考えた。ツールは、無償・個人で取り組める、Python + Selenium でのWebスクレイピングを試すことにしました。

社内システム：
　・物品の管理台帳システム
　・InternetExplorer11上で動作
　・物品の登録、使用者、使用場所、入出庫の履歴管理など

# １．環境構築
Python3は構築済み。
ライブラリ selenium を使うことにした。
### selenium インストール
```Python3:Python3
pip install selenium
```

### Webドライバ
使用するWebブラウザにあったWebドライバが必要。
ドライバは、ルートに置くかパスを通す。
### InternetExplorer
下記から、IEDriverServer.exe をDL。
http://www.seleniumhq.org/download/
【IEの設定】
〔インターネットオプション〕⇒〔セキュリティ〕
　インターネット、ローカルイントラネット、信用済みサイト、制限付きサイト、４つのセキュリティレベルを合わせる。「保護モードを有効にする」にチェック。
〔詳細設定〕
　「拡張保護モードで64ビットプロセッサーを有効にする」を”有効”にする。
（下記を参考にしました）
<a href='https://blog2.k05.biz/2020/06/python-selenium-ie.html'>某氏の猫空</a>
https://blog2.k05.biz/2020/06/python-selenium-ie.html

### ChromeDriver
現在使用しているブラウザとドライバのバージョンを合わせる。
https://chromedriver.chromium.org/downloads

# ２．コーディング
必要なライブラリなどをインポートし、Google で検索してみる。

```Python3:Selenium
import time
from selenium import webdriver

driver = webdriver.Ie()       # IEDriverServer
# driver = webdriver.Chrome() # ChromeDriver

driver.get('https://www.google.com/')
time.sleep(5)
search_box = driver.find_element_by_name("q")
search_box.send_keys('ChromeDriver')
search_box.submit()
time.sleep(5)
driver.quit()
```

# ３．トライ結果
ブラウザ上の情報にアクセスするロジックはいたってシンプルで、簡単に情報を取得、入力、保存などができることがわかりました。
HTMLに記述されている、<code>id</code>、<code>name</code>などで、ターゲットを見つる。
<code>value</code>を取得したり、<code>send_keys</code>で値を書き込むことができる。
<code>click</code>でボタンを押し下げすることも可能。
必要に応じてメソッドを調べていくことでなんとかなりそうな感じであることが確認できました。。

上記テストの後、実際に社内システムのWeb画面にアクセスし、値の取得、書き込み、保存を行いました。。
プログラミング歴３か月、Webスクレイピングは初めてのスキルですが、約２日の短期間ででき、こんなに簡単なら、
　『もっと早よからやってみりゃ良かった！』
と実感です。

# ４．課題
とりあえず、人が操作している通りの動作をさせることができたのと、入力値のログファイルを作成することができたので、目的は達成できました。が、
　『動作が遅い!!』
これでは、数件の処理には対応できますが、数十件～数百件の処理は実用に耐えません。
<code>find_elment_by_id</code>を使って、HTML内の要素を探し、
<code>getAttribute()</code>でbox内の値の取得：16か所
<code>select_by_id</code>でリストの値の取得：3か所
行うのに、８秒ほどかかります。

もっと速い処理はできるのでしょうか??
AdventCalendarのネタの探し方

[mediba Advent Calendar 2020](https://qiita.com/advent-calendar/2020/mediba) の6日目担当 @tetaketa です。

AdventCalendarの季節ですね。
AdventCalendarにとりあえず「何か」で参加表明したものの「何か」って何だろうと悩んでおります。

今回はどうやってネタを探したかについて書こうと思います。

# どうやって探そう

企業AdventCalendarのネタとして鉄板なのは、日々の業務にまつわる話のような感じがします。
チームで何をやったか、どうやったか、その際の課題。その課題をどうやって解決したかなどですね。
もしくは、業務で困ったことなどもネタになりやすそうです。

私のチームでは、Scrapboxを使って日々の業務について、カジュアルに書いています。

以下に少し内容を編集、抜粋したスクリーンショットを貼りますが、ほぼ毎日チームメンバー全員で書いています。
内容としては、業務でやったこと、相談したいことなどから、日常生活などのことまで自由に書いています。

業務内容やその他のことを書いたり。
![スクリーンショット 2020-12-05 21.40.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/45028/2dd7e15c-b8c6-b540-f4aa-0d3b08f78f03.png)

相談ごとを書いたり。
![スクリーンショット 2020-12-05 22.03.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/45028/0f4a815e-1200-c5d7-bd55-84b5c945434b.png)

フォーマットはなく、自由に書いています。
そのScrapboxを見直していると、チームメンバーが「知らなかった」などコメントしているのを発見します。
![スクリーンショット 2020-12-05 10.16.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/45028/126b64c3-0e76-584f-dc81-c4417f8aab28.png)

# そんなわけで小ネタ

Datadogでは、ログのパターンをまとめられます。
このときは、リリース後にエラーが出ていないか、出ているとしたらどんな種類のエラーが出ているのかを確認しているときの話です。

検知しているエラーログにどのような種類があるのかを把握する場合に助かります。
![スクリーンショット 2020-12-05 22.17.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/45028/7ad74c15-8310-cbe7-ef87-1a2d2a62de01.png)

[公式のドキュメント](https://docs.datadoghq.com/ja/logs/explorer/patterns/) にも記載されていて、方法はそちらを見た方が確かだと思います。

# まとめ

日々の業務についてカジュアルに書いておくと、こんなときにも役に立つのでおすすめです。
【NeosVR】Indexコントローラのトリガー入力を優しくする方法

NeosVRのデフォルトだとIndexコントローラはトリガーを完全に引ききらないと反応しません。
先日KazuさんにSteamVRのコントローラバインド設定を教えていただき、トリガーを少し引くだけで反応するように設定できることを知りました！

あまり力入れなくてよくなるし、コントローラにも優しいのでおすすめです！

少し難しかったのでやり方を書いておきます。

1.編集設定を開く
SteamVRのウィンドウを右クリックして「設定」を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547843/774ffed4-936f-1c8e-baae-a4c9f8cda347.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547843/a154f227-a5f2-0bbc-b404-247923bc5945.png)
「コントローラ」を選択します。
画面左下の「詳細設定」を「表示」にし、「古いバインド設定UIを表示」を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547843/ce330ba4-9d03-fa77-cc4e-c0b1925e53d5.png)

「NeosVR」を選択します。

「現在のバインド」の「編集」を選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547843/0565ce43-95f7-c157-5db8-9986903cd169.png)

「knuckles」を選択します。
「トリガー」の右横にある「＋」を押します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547843/4f9b09c7-63c0-df9f-b232-a4fe53919476.png)

「ボタン」を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547843/24f07e5d-88d7-cbd3-95ec-564dfe9ecc5d.png)


「ボタン」の「クリック」の右横にある「なし」を選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547843/b5ed4ac3-73cb-931a-6887-0bfb770c76ce.png)

「trigger_click」を選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547843/52a1b8a2-2da4-8409-00fa-cd6519ed70b9.png)

「ボタン」項目の下部にある歯車アイコンを選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547843/9598be33-e0b1-1bab-daec-d6c46dcd9da5.png)

「クリック有効化のしきい値」と「クリック無効化のしきい値」を調整すると、どのくらいトリガーを引くと判定されるか設定できます。
設定は即座に反映されるので、NeosVRで実際に操作しながら調整するといいと思います。

私は「クリック有効化のしきい値」は75、「クリック無効化のしきい値」は70で使っています。
windows terminalでCygwinリモートワーク開発環境構築

会社ではMACを使っているのですが、自宅ではwindowsを使っているため、
windows環境でもっと開発しやすくできないかなと模索していたところ、
windows terminalが安定していてかつ使いやすかったので、Cygwinとwindows terminalで快適な開発環境を構築手順をまとめました。

# windows terminal と　Cygwin で快適開発環境構築
### Cygwin　の install
[cygiwin公式サイト](https://www.cygwin.com/)からインストーラーをダウンロードして実行します。
"Choose A Download Site"で".jp"が付くサーバーを選んで次へ進みます。
"Select Packages"では、以下のパッケージを選択します。
- tar
- gawk
- wget
- bzip2

上記パッケージ選択後に次へ進みインストールを完了させます。

### windows terminal　の install
windows terminalはwindows Storeでインストール簡単にインストールできます。

## apt-cygのインストール

本家の開発が止まっているようなのでfork先から'apt-cyg'をダウンロード
- 本家様リポジトリ
 - https://github.com/transcode-open/apt-cyg
- forkされたリポジトリ
 - https://github.com/kou1okada/apt-cyg

apt-cygを以下のフォルダに移動し、権限を変更します。
インストール先がデフォルトの場合は以下となります。

```
mv apt-cyg c:/cygwin64/local/bin
chmod 0755 /usr/local/bin/apt-cyg
```

インストールが完了したら、国内のリポジトリに変更に変更します。

```
apt-cyg -m ftp://ftp.iij.ad.jp/pub/cygwin/ update
```

## windows terminal からCygwinを使えるようにする。
apt-cygでchereをinstallします。

```
apt-cyg install chere
```

cygwinをwindows terminalで開けるように設定します。
windows terminalの設定をクリックして、progules.jsonを開きます。
profiles.listの配列に以下を記載します。
パワーシェルで以下のコマンドを実行してuuidを入手します。

```
{
    "guid": "※uuidを入力※",
    "name": "Cygwin",
    "suppressApplicationTitle": true,
    "commandline": "C:\\cygwin64\\bin\\bash.exe /bin/xhere /bin/bash ~",
    "icon": "C:\\cygwin64\\Cygwin-Terminal.ico",
    "useAcrylic" : true,
    "acrylicOpacity" : 0.8
}
```

uuidの部分はWindows PowerShellで、以下のコマンドで入手。

```
$ [guid]::NewGuid() 
```

### かっこよくする

useAcrylicで透明度設定で、acrylicOpacityが１に近づくほど不透明になります。
他の各種設定は、以下URL参考。
https://qiita.com/syui/items/3236495744a16d982464


## 快適なVPN接続
### すべてVPNを通して、proxyを使う場合

windowsからVPNに接続します。
接続方法は、以下のURL参照してください。
  - https://www.seil.jp/doc/index.html#fn/pppac/use/pppac-client/win10_l2tp.html
Windowsでブラウザ以外で使うproxyの設定です。
以下のコマンドを管理者コマンドプロンプトで以下実行実行します。

```
$ netsh winhttp set proxy proxy-server=プロキシサーバーIPアドレス
```

3　元に戻す場合は、

```
$ netsh winhttp reset proxy
```

### vpnの必要な部分だけvpnを通す場合。
- コントロール パネル＞ネットワークとインターネット＞ネットワーク接続で、該当のVPNを右クリック⇒プロパティをクリックします。
- ネットワークタブでTCP/IPv4を選択＞プロパティをクリック＞詳細設定をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/186510/49a6bff1-4c09-18e3-46f3-894037a4d9ea.png)

- IP設定タブで「リモートネットワークでデフォルトゲートウェイを使う」のチェックを外します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/186510/565ee491-4bbe-870d-7d60-67b78da3f781.png)


以下をコマンドプロンプトで実行してインターフェイス一覧のからルーティングしたいVPN番号をメモします。

```
$ route print
```

コマンドプロンプトで以下を実行して、vpnを通すルーティングを設定します。

```
$ route -p add 接続先IPアドレス mask 255.255.255.255 VPNサーバIPアドレス metric 1 if 上記でメモしたVPNの番号
# exsample
route add 192.168.123.0 mask 255.255.255.0 123.12.123.123 metric 1 if 42
# 究極にめんどくさい場合
$ route add 192.168.0.0 mask 255.255.0.0 123.12.123.123 metric 1 if 42
```



#### 以下参考にさせていただいたページ
- windows terminal と cygwin関連
  - https://qiita.com/yokra9/items/e8b184021091dbabb8a1
- cygwin関連
  - http://umi-uyura.hatenablog.com/entry/2017/08/29/232433
  - http://dqn.sakusakutto.jp/2013/12/64bit_cygwin_apt-cyg.html
- vpn関連
  - https://qiita.com/ivankovoon/items/4939f9121c5ee8684cae
openFileInputでファイルを読み込むときのディレクトリの場所

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508704/aa2ae321-35f8-e459-977e-e5a4d404098e.png)
View -> Tool Windows -> Device File Explorer を開き、data/data/{パッケージ名}/files の中にあるファイルを探すみたい。

PC側のパッケージ名直下のfilesフォルダを探してるかと思ったらAndroidエミュレータ内のfilesフォルダを探してるんですね。結構悩んだのでメモ。
Macの作業効率向上設定とGoogle拡張機能について

<h2>はじめに</h2>
在宅勤務が新たな仕事のスタイルとして確立されている昨今、「効率化」がキーワードになっています。
本投稿では、Macに設定することで作業が捗るアプリや環境設定を紹介します。
様々なMac初期設定記事やおすすめアプリ動画を視聴し実践し、以下の内容が私にはフィットしました。
アプリケーション編/環境設定/Google拡張機能の３構成で紹介します。
本投稿で、皆さんの作業効率の促進に少しでも貢献できれば幸いです。

## アプリケーション編

###Alfred
Macを使っている人であれば是非使って頂きたいのがランチャーアプリ「Alfred」です。
こちらのアプリを使うようになってからは、トラックパッドを使ってアプリケーションを起動する機会がとても減りました。
他のアプリケーション起動はもちろん、詳細設定すればよく利用するサイトも開くことが可能です。
詳細設定の方法についての参照記事は[こちら](https://qiita.com/aj2727/items/dc16ce9a5ab4424567f0)です。

###BetterSnapTool/BetterTouchTool
こちらのアプリは有料にはなってしまいますが、お金を払う分(360円)だけの価値があると思います。
トラックパッドでの操作機能を拡大させる機能や、デフォルト設定では面倒な画面分割をしてくれます。
設定次第では、マウスやショートカットキーでやっていたこと全てトラックパッドでできるようになります。
こちらのアプリをインストールする上で注意する点としては
***BetterSnapToolを使わないとしても先ずはBetterSnapToolを購入してください。***
***BetterSnapToolを購入することでBetterTouchToolの有効期限が無期限になります。***
購入についての内容はこちらの[リンク](https://40chousennsya.com/app6)が分かりやすかったので掲載します。


まずは、画面分割について紹介します。(BetterSnapTool)

デフォルトの設定では、ウィンドウを拡大させる緑色のボタンを長押しすることで右ウィンドウと左ウィンドウの分割が可能です。
ですが、デフォルトの画面分割は使いにくく分割するまでの時間もかかるため非常にストレスでした。
ショートカットキー設定を行うことで、右左ウィンドウ分割だけでなく、上下ウィンドウ分割や全画面拡大もしてくれます。
筆者はオプションボタンで各動作の設定していますが、commandキーやShiftボタン等でも設定出来ます。

![スクリーンショット 2020-12-06 1.46.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/788536/2d3a03bc-683a-76be-72c3-4c38daf59ff1.png)



次に、トラックパッドの操作機能について(BetterTouchTool)
具体的な機能として、キーボードのショートカットキーやスクリーンショットの実行等をトラックパッドの操作でアシストしてくれます。
１つ注意点として、あまり多くのトラックパッド設定を行ってしまうと誤動作が起きることがあるので最小限の設定が好ましいかもしれません。
色々いじってみた結果、設定内容は画像のように、新規ウィンドウ立ち上げとウィンドウ削除、アプリケーション強制終了を設定しています。
![スクリーンショット 2020-12-06 1.11.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/788536/731c0cba-6b4c-5b68-f7a2-a709c9c09c6e.png)


##環境設定編
### Google日本語入力
Macのデフォルト設定されている文字変換の機能としてKotoeri(ことえり)が入っていますが変換精度があまり良くないので導入しました。
Google日本語入力は自動で学習して、多用されているワードを変換上部に持ってきてくれるため文字入力の手助けになります。

また、ユーザ辞書を設定することで入力が面倒なメールアドレスや名前等を設定した文字を入力するだけで変換してくれる機能もあります。この設定した内容については、エクスポートしiCloud上に保存しておくことでパソコンを変えたとしても以前設定した内容を反映させることも可能です。

###キーボード設定
小さな設定ではありますが、キーのリピートを設定することで文字を削除するスピードや、操作キーで文字を移動する際のスピードをが向上します。
システム環境設定→キーボードから設定できます。
![スクリーンショット 2020-12-06 2.06.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/788536/326d8e7b-46e3-dd53-6db0-2725640e959e.png)


##Google拡張機能
###AutoPagerize

![スクリーンショット 2020-12-06 2.14.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/788536/2815709e-012e-3577-edc9-277eb3a64865.png)

通常の場合、画面下部にこのようなページャーがあります。
わざわざクリックして次画面に遷移させるのが面倒だなって思っていました。
AutoPagerizeの拡張機能を導入すると、わざわざクリックしなくても下にスクロールすることで自動でページを追加してくれます。

###SearchPreview
こちらの機能は、検索した時に画像のプレビューも一緒に表示してくれる機能です。
以前検索した内容で再度そのサイトにアクセスしたい場合、わざわざサイトにアクセスして内容を確認する必要がなくなるため地味にありがたい機能です。


![スクリーンショット 2020-12-06 2.21.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/788536/64e8a801-8263-7410-c6d5-99253f35d253.png)

##終わりに
本投稿は私のQiita初投稿です。
可読性を高めるためにMarkdown方式を学ぶ良い機会になりました。
これからも効率性を高めるアプリや設定等があれば随時更新していく予定です。
ここまで読んでくださりありがとうございました。
英語のテックジョークで使えそうなワード

#良さげなワード
arrays（配列）-> a raise（昇給）
bugs（バグ） -> bugs（虫）
dark（IDEなどのダークモード） -> dark（暗闇）
table（DB table） -> table（机）
server（サーバ） -> server（レストラン店員）
cache（キャッシュ） -> cash（現金）
Java（プログラミング言語） -> java（コーヒー）

#今日の名言
I hope my death makes more cents than my life.（私はこの人生よりも硬貨な死を望む）
※ make cents（小金を稼ぐ）とmake sense（意味をなす）をかけている

-アーサー（映画ジョーカーの主人公）
Python学習 基礎編　~変数とは？~

こちらはPython学習の備忘録です。
プログラミング初心者や他の言語にも興味、関心をお持ちの方の参考になれば幸いです。
* * *

###変数とは？

『変数とは、データ(値)を入れておく箱のようなもの。
この箱(変数)に、名前(変数名)をつけることで、その名前を用いて変数に値を入れることや変数から値を取り出すことができる。』
と覚えていたのですが、ありがたいコメントをいただきましたので下記へ訂正させていただきます。
※詳細は下記コメントを参照願います。

『**Pythonの変数は「箱」として覚えない方がいいです。
Pythonの「値」はすべて「オブジェクト」です。
変数代入は「オブジェクトに変数名を付けて記憶する」「変数からオブジェクトを参照する」「オブジェクトと名前をbindする」と思った方がいいです。**』

**独学で学習している身としては、本当に勉強になるし、こういうご指摘は大変ありがたいです！！**

実際のプログラムの中には多くの値が登場する。
プログラムを書く時は、それらの値を間違えないように、そして値の変更に対応しやすいようにすることが求められる。
それを助けるための1つの仕組みが変数である。
###変数を使うメリットとは？

- **同じ要素を繰り返し使える**

似たコードを何度も書くのは面倒ですが、変数を使えば同じ値を繰り返し使えて、楽にコードを書くことができる。

- **変更に対応しやすい**

後々、値の変更が必要になった場合、変数を用いていれば、代入する値を変更するだけですべての変更に対応できる。

- **何の要素かわかりやすい**

データに名前をつけることで、扱っているデータの中身が何を表しているかが明確になる。その結果、コードがより読みやすくなる。

###変数の定義

変数は**「変数名 = 値」**で定義する。
変数名はクォーテーションで囲む必要はない。
またプログラミングの**「 = 」**は、**「等しい」**という意味ではなく、**「右辺を左辺に代入する」**という意味になる。

```py:script.py
# Hondaをnameへ代入する
name = 'Honda'  # 変数名 = 値
```

###変数の値を取り出す

下記のように**「print( 変数名 )」**とすることで、変数の値を出力することができる。
しかし、 **print( '変数名' )**のようにクォーテーションで囲んでしまうと、変数ではなく文字列として認識されてしまうので注意が必要です。

```py:script.py
name = 'Honda'  # 変数名 = 値
print(name)  # print( 変数名 )
print('name')  # クォーテーションで囲む

# 出力結果
Honda
name  # 文字列として認識され、nameとそのまま出力されてしまう
```
###変数名の付け方

変数名は自由につけることができるが、守らなければいけないルールがいくつかある。

```py:script.py
# 良い例
date  # 小文字、英単語を用いる
user_name  # 2語以上の場合は、アンダーバーで区切る

# 悪い例
1name  # 数字開始
namae  # ローマ字
名前  # 日本語 
```
また、[PEP: 8](https://pep8-ja.readthedocs.io/ja/latest/#id24)に記載のある下記内容も気をつけたほうが良いでしょう。

**『単一の文字 'l' (小文字のエル)、'O' (大文字のオー)、'I'(大文字のアイ) を決して変数に使わないでください。フォントによっては、これらの文字は数字の1や0と区別が付かない場合があります。』**

* * *
###おわりに

とりあえず、基本的なことは記載したつもりですが必要があれば今後追記していきたいと思います。
また、ご指摘などござありましたら、遠慮なくお願い致します！！

もっとどっぷりと学習する時間が欲しい...
再帰的なmodule type

この記事は[ML Advent Calendar 2020](https://qiita.com/advent-calendar/2020/ml) 6日目の記事です。

----

OCamlの定義にはデフォルトで再帰的なものと非再帰的なものがある。

型やクラスの定義はデフォルトが再帰で、値の定義やモジュール、モジュール型の定義は非再帰だ。

型の定義で再帰をしたくない場合は `type nonrec` と書き（OCaml 4.02.2以降）、値の定義で再帰したい場合は `let rec` と書く。モジュールは `module rec` と書く。ではモジュール型の場合は？

そもそもそんなもの必要なのかという気もするが、第一級モジュールでオブジェクトみたいなことをしはじめると出てこなくもない。

たとえば、非破壊的な、2次元平面上の点を考える。

レコードで書くとこんな感じだろう。

```ocaml
type t = { x : int; y : int }

let move ?(dx = 0) ?(dy = 0) {x; y} =
  { x = x + dx; y = y + dy }
```

クラスにするとこう。

```ocaml
class point = fun ~x ~y ->
  object (self)
    method x = x
    method y = y

    method move ?(dx = 0) ?(dy = 0) () =
      new point ~x:(self#x + dx) ~y:(self#y + dy)
  end
(*
class point :
  x:int ->
  y:int ->
  object
    method move : ?dx:int -> ?dy:int -> unit -> point
    method x : int
    method y : int
  end
*)
```

`move` メソッドの戻り値型は自分自身なので、再帰が必要になってしまった。

第一級モジュールで書こうとすると、 `module type rec` のようなものはないのでどうしたものかというところだけど、実は再帰モジュールを使うとどうにかなる。

```ocaml
module rec Point : sig
  module type S = sig
    val x : int
    val y : int

    val move : ?dx:int -> ?dy:int -> unit -> (module Point.S)
  end
end = Point

let rec make_point ~x ~y : (module Point.S) =
  (module struct
    let x = x
    let y = y

    let move ?(dx = 0) ?(dy = 0) () =
      make_point ~x:(x + dx) ~y:(y + dy)
  end)
```

モジュール型自体では再帰せず、外側のモジュールを再帰させる。やりたいことはシグネチャレベルで完結しているので、モジュール定義の右辺では単に自分自身を参照する。

これで問題（？）は解決したのだけれど、OCamlのissuesを眺めると[再帰モジュール関連のものが結構あり](https://github.com/ocaml/ocaml/issues?q=is%3Aissue+is%3Aopen+label%3Arecursive-modules)、使っていて大丈夫なんだっけという気持ちがよぎる。[module type recを直接サポートしようというissue](https://github.com/ocaml/ocaml/issues/6818)もあるがはたして。

## 補足

クラスをつかった定義では `move` の戻り値を `point` にしたが、後々の拡張を考えると（するかどうかは別として）、 self type を使っておいた方がよい。こうしておくと、サブクラスでも `move` の定義がそのまま使える。

```ocaml
class point = fun ~x ~y ->
  object (_ : 'self)
    val x = x
    val y = y
    method x = x
    method y = y

    method move ?(dx = 0) ?(dy = 0) () : 'self =
      {< x = x + dx; y = y + dy >}
  end
(*
class point :                                                                                       
  x:int ->                                                                                          
  y:int ->                                                                                          
  object ('a)                                                                                       
    val x : int                                                                                     
    val y : int                                                                                     
    method move : ?dx:int -> ?dy:int -> unit -> 'a                                                  
    method x : int                                                                                  
    method y : int                                                                                  
  end                                                                                               
*)
```
【メモ】 Markdown Preview Enhanced で作ったHTMLを即席でスライド化する

Markdown Preview Enhanced で HTML を出力したものを使って、
即席プレゼン資料を作りたいとき用。

まず、資料を作る段階で、ページ分けしたい場所を `---` で区切る。

```markdown
あああああああ
あああ
あああああああああああああああああ
ああああああああああああ

---

- [x] ああああああああああああああ
- [x] あああああああああああああああああああああ
- [x] ああああああああ
- [ ] ああああああああああああああああああああああああ

---

| AAAAAAAAAAAA                | AAAAAAAAAAAAA                |
| --------------------------- | ---------------------------- |
| AAAAAAAAAAAAAAAAAAA         | AAAAAAAAAAAAAAAAAAA          |
| AAAAAAAAAAAAAAAAAAAAAAAAAAA | AAAAAAAAAAAAAAAAAAAAAAAAAAAA |

```

つぎにブラウザで HTML を開き、 DevTools でコンソールを開く。
そして、次のコードを実行

```js
let page = 0;
const list = [
  ...document.querySelectorAll(".markdown-preview > div > *"),
  0,
].reduce(
  ({ p, mem }, c) =>
    c.tagName === "HR" || !c
      ? ((c.hidden = true), { p: [...p, mem], mem: [] })
      : ((c.style.display = "none"), { p, mem: [...mem, c] }),
  { mem: [], p: [] }
).p;
const show = ({ key } = {}) => {
  if (key) {
    if (key === "ArrowLeft" && page !== 0) page--;
    if (key === "ArrowRight" && page !== list.length - 1) page++;
  }
  list.forEach((els, index) =>
    els.forEach((el) => (el.style.display = index === page ? "block" : "none"))
  );
};
show();
window.addEventListener("keydown", show, true);
```
これで矢印キーでページ遷移が可能な即席スライドが完成する。
ただ、スタイルが左寄りのままなので、かなり改良の余地あり。

ブックマークレット版

```js
javascript:(()=>{let page=0;const list=[...document.querySelectorAll(".markdown-preview > div > *"),0].reduce(({p:p,mem:mem},c)=>"HR"!==c.tagName&&c?(c.style.display="none",{p:p,mem:[...mem,c]}):(c.hidden=!0,{p:[...p,mem],mem:[]}),{mem:[],p:[]}).p,show=({key:key}={})=>{key&&("ArrowLeft"===key&&0!==page&&page--,"ArrowRight"===key&&page!==list.length-1&&page++),list.forEach((els,index)=>els.forEach(el=>el.style.display=index===page?"block":"none"))};show(),window.addEventListener("keydown",show,!0);})()
```
AWSクラウドプラクティショナー取得までの記録

#はじめに
会社でAWS資格取得を推進しているし費用補助もあるしいっちょ頑張ってみようと言うスタート。
産休前にSAA取るぞ！と大見得を切っていたけど結局勉強が滞ったので、ハードル低めにCLFからにしました。

#前提条件
・SE歴7年、インフラ経験無し、AWSはLambdaとCloudWatchを少し。

#勉強方法
・AWSの基本が1日で学べる！超入門講座セミナー視聴（4H)
　AWSのトレーナーの方が実際に画面で操作しながら主要機能を一通り解説してくれました。

・AWS認定資格試験テキスト(2week)
　1回読破。隅から隅まで読むけど深追いはせずにさらりと。章末問題も忘れずに。
　https://www.amazon.co.jp/AWS認定資格試験テキスト-AWS認定-クラウドプラクティショナー-山下-光洋/dp/4797397403

・WEB問題集(3week)
　https://aws.koiwaclub.com
　
　1周目:1問30秒程度のスピードで軽く。解説はさらりと。
　　　　問題に慣れる&出題傾向を把握する事が重点。※正答率1/7〜5/7
　2周目:1問2、3分程度掛けてじっくり。正誤に関わらず解説もしっかり読む。
　　　　ググったりも。※正答率3/7〜6/7
　3周目:2周目で間違えた問題のみ。解説もしっかり読む。※正答率5/7〜7/7
　4周目:3周目で間違えた問題のみ。解説もしっかり読む。※正答率6/7〜7/7
　5周目:4周目で間違えた問題のみ。解説もしっかり読む。※正答率6/7〜7/7
　6周目:ゴリ押しで全問もう一度。1周目ぐらいのスピードで軽く。※全体の正答率95%
　※2〜5周目では「なぜその選択肢が正なのか&他の選択肢ではなぜ誤なのか」を考えながら解きました。

#本番試験
ピアソンで予約し、最寄りの試験センターに行きました。
制限時間は100分で、見直し含め大体60分ちょっとくらいで出ました。
しかし教本にもweb問題集にも出てこなかったサービス名がちょくちょく出てきて焦りましたね。
心を乱してはいかん！と落ち着きを取り戻せたのは良かったです。
模擬試験を受けるとこの辺りカバーできたのかな？
結果、スコアは908/1000でした！中々健闘したのでは！！

#勝因
コツコツ積み重ねる事で知識が定着したのではと思います。
勉強時間は毎日バラバラで15分の日もあれば2時間の日も。平均1時間くらい？
煮物中とかもキッチンにPC置いてweb問を解いてました。
また、新人さん達がどんどん取得している様子を見て勝手に気合をもらいました。
資格試験の問題集や過去問と言うのは大抵1周目は惨敗するのですが、それは当たり前だと思ってあまり落ち込まずに淡々と進める。徐々に正答率が上がるのを楽しめると続けられる気がします。

#おわりに
資格取得は5,6年ぶりでしたが嬉しいものですね。
SAAの取得も考えているので、今回CLFで得た知識を忘れない内に受けようと思います。
学び直し rubyがミニツク編 Part1

Rubyの基礎から学び直しましょう。railsの前にまだまだRuby言語の理解と知識が足りてないから。

今日の教科書
[Rubyがミニツク](http://www.minituku.net/courses/566428009/contents/685444640.html)

#コーディング規約
[コーディング規約](https://shugo.net/ruby-codeconv/codeconv.html)
実は読んだことがなかったんだ…。なんとなくで進んできてしまった。

##メモ

 - インデントはスペース２回。普通にタブ使ってました。
 - クラスや要素の区切りに改行、最初と最後はしない。
 - コメントはメソッド中には書かない。(モジュールとか公式のメソッドは別)#で書く。そもそもコメントが必要なら[リファクタリング](https://ec-orange.jp/ec-media/?p=24443)せよ。
 - 論理演算には!や&&や||を使用する.
 - スコープが狭いループ変数には、i, j, kという名前をこの順序で使 用する。
 - スコープが狭い変数名には、クラス名を省略したものを使用してよい。 (例: eo = ExampleObject.new)

他にもたくさんあるけど、理解できて実践できそうなのはこれぐらいです。

#Hello,Ruby !
http://www.minituku.net/courses/566428009/contents/534527671.html
##メモ
`irb`:Rubyのコマンドプロンプトを起動
printメソッド:引数で渡された値を端末へ出力、改行なし
\n:バックスラッシュ記法で使う、改行
putsメソッド:引数で渡した値を端末に表示した直後に改行して出力
pメソッド:引数を出力、オブジェクトの種類を調べるのに便利、文字列は[""]で囲まれる、改行あり、人にとって理解しやすい形で出力
「’’」(一重引用符)と「""」(二重引用符):後者にバックスラッシュが使える
バックスラッシュ(\):特殊文字の特別な意味を無効

 >シングルクォート（’）で定義された文字列オブジェクト中の記号は基本的に解釈なしに、そのまま表示。記号の前にバックスラッシュ（\）を付けることでエスケープ出来ます。「記号をエスケープする」とは、記号の特別な意味を無視する意図を宣言すること。

 >rubyコマンド：-Cオプションを使うとワーキングディレクトリを指定出来ます。
-eのオプションを指定するとその後に実行したいコードをシングルクォートに囲んだままで指定できます。小文字の-cオプションを使うと、その後ろにくるプログラムが文法チェックされますが、コードは実行しません。

[演習ドリル](http://www.minituku.net/courses/566428009/contents/383376235.html)
入れ子のforeach文での非同期処理（async / await）

## きっかけ
Reduxでコンテンツを管理していて、それぞれのコンテンツについて非同期の通信を行わなければいけなくなったときに、こんがらがったので記事にしておきます。

## 前提
以下のような関数があるとして、更新後のstateを表示したいとします。

```js
const state = {
  singers: [
    {
      name: 'Alice',
      status: false
    },
    {
      name: 'Bob',
      status: false
    },
  ],
  composers: [
    {
      name: 'InvalidUserName',
      status: false
    },
    {
      name: 'Charlie',
      status: false
    }
  ]
};

// 指定した時間がかかる非同期の関数
const sleep = time => new Promise(resolve => setTimeout(resolve, time));

// Stateの更新
const setStatus = (key, index, bool) => {
  state[key][index].status = bool;
};

// 名前の長さが10以下なら真を返す
const checkName = async (name) => {
  await sleep(500);
  return name.length < 10;
};
```

## ダメな例１（文法エラー）
以下のように書きたいところですが、`await`は`async`直下にしかかけないので、シンプルに`forEach()`の中には書けません。

```js
const asyncFunc = async () => {
    Object.keys(state).forEach((key) => {
    state[key].forEach((content, index) => {
      const result = await checkName(content.name); // 文法エラー
      setStatus(key, index, result);
    });
  });

  // stateの表示
  console.log(state);
};

asyncFunc()
```

## ダメな例2（awaitできない）
forEachは`async`で書けるので、以下のようにすればいける気がするのですが、`forEach`の仕様で`await`してくれません。

```js
const asyncFunc = async () => {
  await Object.keys(state).forEach(async (key) => {
    await state[key].forEach(async (content, index) => {
      const result = await checkName(content.name);
      setStatus(key, index, result);
    });
  });

  // stateの表示
  console.log(state);
  await sleep(1000);
  console.log(state);
};

asyncFunc();
```
### 出力
`await`されず、１回目の出力ではstateが更新されていません。実用では`checkName()`にかかる時間が分からないので、`forEach()`を`await`する必要があります。

```

{
  singers: [ { name: 'Alice', status: false }, { name: 'Bob', status: false } ],
  composers: [
    { name: 'InvalidUserName', status: false },
    { name: 'Charlie', status: false }
  ]
}
{
  singers: [ { name: 'Alice', status: true }, { name: 'Bob', status: true } ],
  composers: [
    { name: 'InvalidUserName', status: false },
    { name: 'Charlie', status: true }
  ]
}
```

## 解決策
`Promise.all()`という、引数の配列内の全てのPromiseがresolveかrejectされるまでresolveしないという静的メソッドがあるので、それを使用します。`Promise.all()`で囲うこと、`forEach()`を`map()`に書き換えるのがポイントです。

```js
const asyncFunc = async () => {
  await Promise.all(Object.keys(state).map(async (key) => {
    await Promise.all(state[key].map(async (content, index) => {
      const result = await checkName(content.name);
      setStatus(key, index, result);
    }));
  }));

  // stateの表示
  console.log(state);
};

asyncFunc();
```
### 出力
正常に更新後のstateが出力できています。

```
{
  singers: [ { name: 'Alice', status: true }, { name: 'Bob', status: true } ],
  composers: [
    { name: 'InvalidUserName', status: false },
    { name: 'Charlie', status: true }
  ]
}
```

## 参考
* [async-awaitでもforEachしたい！](https://qiita.com/jkr_2255/items/62b3ee3361315d55078a)
5日目：遊園地で学ぶVCIスクリプト導入とBGM再生

この記事はアドベントカレンダー「[【VCI】テーマパークを作ろう](https://adventar.org/calendars/5855) 」の5日目の記事です。
※本来やろうとしていたアニメーションによる観覧車作成はスクリプトが複雑になりそうだったため後半に回し、
予定を変更して簡単なスクリプトで書けるBGM再生について説明します。

# 今回の内容
今回は観覧車を作成しながら以下のことを学びます

* BGMの再生方法
* スクリプトの導入

※今回の方法ではおそらく他の方とBGM同期ががされませんのでご承知おきください。

# BGMの再生手順
今回は作業に当たり再生したい音楽ファイルが必要です。形式はmp3であれば問題ないかと思います。

## UnityでのBGMの設定
VCIでBGMを再生するためには「Audio Source」コンポーネントを使用する必要があります。
何らかのオブジェクトにAudio Sourceを使用するか、Hierarchyを右クリック>Audio>AudioSourceでAudioSourceコンポーネント付きのオブジェクトを作成してください。

作成が終わったら音楽ファイルをAudiosourceのAudioClipの部分にセットします。
以下が追加後のSSです。
![20201205-204756.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64877/13e3bddc-d677-ed98-de4c-50a293022627.png)
AudioSourceの設定は以上ですが、音声ファイルを再生するためにはスクリプトを書く必要があるため引き続きその準備をしていきます。

まず、VCIObjectコンポーネントがついているコンポーネントをクリックします。
するとInspecterの下の方にscriptという項目があるかと思います。
その少し上にある「Script Enable Debiugging」にチェックと入れてください。
(このチェックを入れることでVキャスをやりながらスクリプトを修正することができるようになります)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64877/abde51a9-0f71-2d72-cb6f-f99cba2617eb.png)

完了したらエクスポートしてseedOnlineに取り込んでください。


**■補足：scriptの項目について**
scriptではスクリプトを何個使用し、それぞれがどのような名前・内容なのかを記載する部分です。
複数個のスクリプトの読み込むことも可能ですが、構成を凝らない限りは1つのファイルで問題ないと思います。
Sourceの部分になにか書かれているかと思いますが、内容はあとで書き換えるのでここでは触れなくて大丈夫です。


### スクリプトの作成 
ここからはVキャス及びエディタでの作業となります。

スクリプト入VCIを作成するためにはまずスクリプト書くための開発環境を作成する必要があります。
公式に手順がありますので以下を参照しながら環境を整えてください
　・[VCIスクリプトの開発環境の導入](https://virtualcast.jp/wiki/doku.php?id=vscode:tutorial)
　・[VCIスクリプトのチュートリアル](https://virtualcast.jp/wiki/doku.php?id=vci:script:tutorial)

上記2つを行い環境が整いましたら早速BGM再生部分を作成して行きます。
まだVCIを読み込んでいない場合は一度読み込みを行ってください。

一度VCIを出すと上記URLで書かれていたワークスペースの中にseedOnlineで指定したVCI名の名前のフォルダが作成されており、そのなかに_main.luaというファイルがあるかと思います。これがスクリプト本体のファイルです。
早速このファイルを弄っていきたいところですが、\_main.luaというファイルは修正を加えても勝手にseedOnline上のデータで上書きされてしまいます。
そのため、編集しながら動きを確かめる際には「\_main.lua」をコピー＆リネームして「main.lua」というような先頭のアンダーバーを削除したファイルを作成し、そのファイルに修正を加えていきます。

今回はBGMを再生したいため、以下のページを参考にコードを書いていきます
　・ [assets.audio (ExportAudio) (オーディオ制御)](https://virtualcast.jp/wiki/doku.php?id=vci:script:reference:exportassets:audio)

上記ページを参考に作成したコードが以下のようになります。

``` lua
-- trueならオーディオをループ再生、falseなら１回だけ再生
local audioLoop = true
-- 0 から 1 の間で音量を指定
local audioVolume = 0.005   
--"Bgm"をループ再生する
vci.assets.audio._ALL_Play("Bgm", audioVolume, audioLoop)
```

基本Vキャスは爆音なのでVolumeは内部で聞きながら値を調整すると良いかと思います。
また、設定するAudio名がわからない場合は、Vキャス内に表示されているデバッグウィンドウをグリップすると確認できます。
何回かグリップするとVCIに入っているAudioの一覧を見ることができます。Audio以外の一覧も見れるので覚えておくと便利です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64877/495cf57b-87e6-4a5e-b624-5b17ac3fcb23.png)

これで音の設定は完了です。

**■補足：関数の「_ALL_」の有無の違いについて**
同じ効果の関数でも関数名に_ALL_がつくものとつかないものがある場合があります。
この場合、_ALL_がついてないものは自分のPCのみで動き、_ALL_がついているものは関数が呼ばれると部屋内の全員が実行されます。
今回のケースでは後からRoomに人が入ってきたときに、_ALL_がついているとすでに部屋の中にいる他の人も最初から再生されてしまうとおもい_ALL_はつけませんでした。(動作確認中)


# 終わりに 
いかがでしたでしょうか？
スクリプト関係の環境もできたので少しずつスクリプトが必要な内容も紹介していこうと思います。
明日は装着VCIについて書く予定です。
gitで異なるバージョンの２ファイルの差分を見る方法

## コマンド

`diff -u <(git  cat-file -p HEAD:file1.rb) <(git cat-file -p HEAD^^:file2.rb)`


## 豆知識

GitHubのコメントに貼り付ける時は、(```diff) でスニペットを囲ってやったらいい感じに色がついて見やすくなる。
Flutterで簡易イベント検索アプリを作ってみた

# 1.Flutterとは
Flutterを使用すると、iOSおよびAndroidで単一のコードからネイティブアプリを構築できます。
つまり、クロスプラットフォーム開発技術です。
これは単一のコードを記述できるテクノロジーであり、アプリケーションはAndroidおよびiOSプラットフォームの両方で動作します。
![スクリーンショット 2020-12-06 2.19.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/85181/185535f4-1f70-af12-5369-29a0cf778704.png)

## Dart言語とは
Dartは、ウェブアプリケーション開発のためにGoogleが開発したプログラミング言語です。 大規模なアプリケーション/サービス開発をサポートできるクラスベースのオブジェクト指向言語。

# 2.Flutterを学ぼうと思った理由
クロスプラットフォームの技術選定をする機会が増え、下記のような理由があり自分でも少し勉強しておこうと思いました。

- まずは簡単にプロトタイプをネイティブアプリで作りたいです。

- ネイティブアプリを作りたいので、FlutterかReact Nativeでお願いできませんか？

- 技術選定する上でどんなものか自分も知っておきたい。
など

## FlutterとReact Nativeどちらが良いのだろうか？
フレームワーク比較表を見ていただければ分かる通り、両者は非常によく似ています。
React Native が既存の Web 技術をベースに設計されているのに対して、Flutter は全く一から設計されています。
 ![スクリーンショット 2020-12-06 2.26.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/85181/05c30ebd-ec77-905c-2309-f5d338bb063f.png)


# 3.開発したサービス
Connpass APIを使用して簡単にイベント情報を検索できるアプリを開発しました。
![スクリーンショット 2020-12-06 2.27.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/85181/c37beefd-210e-7812-9b08-8a0100771111.png)
![7ab923b017234c224977b2f4113caf82.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/85181/e7b08ba1-4e6e-16dd-4471-f56626aaedf6.gif)



# 4.実装してみた感想
- 書いた感じ、TypeScriptに似ている気がする。  

- Widgetを作るのが難しい。これをお客さんの指定したデザイン当てる作業とかは大変そう。

- 学習コストは高そうだけど、慣れればきっちり書けるので、良さそう。 


# 参考
https://speakerdeck.com/bumptakayuki/flutterru-men
[Kotlin]Rangeを使って簡潔に値が範囲内かチェックする

Rangeを使う。
変数iが1以上90以下かチェックする例。

```kotlin
// 昔ながらのチェック方法：1 <= i && i <= 90
if (i in (1..90)) {
    print("iは1以上90以下です")
} else {
    print("iは1未満または90より大きいです")
}
```

ここはIntの例だが、Comparableを実装しているクラスなら、同じ方法でチェックできる。

## 補足
最初、Rangeオブジェクト的なものを作成していて、ベタなチェック方法よりもパフォーマンス悪いのでは考え、調査のためにbyteコードをDecompileしてみた。

```java:DecompileしたJavaコード
      int var2 = this.i;
      boolean var3;
      String var4;
      if (1 <= var2) {
         if (90 >= var2) {
            var4 = "iは1以上90以下です";
            var3 = false;
            System.out.print(var4);
            return;
         }
      }

      var4 = "iは1未満または90より大きいです";
      var3 = false;
      System.out.print(var4);
```

最終的には"1 <= i && i <= 90"が実行されていることが分かる。なので、ベタな方法と性能が劣るということはなさそう。
cpanmの--test-onlyでサーバーを壊した話

## cpanm の --test-only
言わずと知れた、perlのcpanモジュール用のCLIツール、そのオプションに、 `--test-only` があります。

https://metacpan.org/pod/distribution/App-cpanminus/bin/cpanm

実際にモジュールをインストールせずに、単体テストを通して合格するかを試すことができます。

## 実際に使ってみる
とあるモジュールを使用したいという事で、以下のコマンドでテストを実行していきます。

```shell
cpanm --test-only [モジュール名]

...
Successfully tested [モジュール名]
```

テストが通ったのが確認できました。問題なく使えそうです。

本番環境でも同じく実行して、テストが通りました。これで明日のリリースは大丈夫ですね！

## 事件が起きた
「サーバーが立ち上がらないんだけど、誰か何かした？」という、不穏な連絡が飛んできました。

でているのはモジュールエラー。以下のようなものが大量にでています。

```
Can't locate 〇〇.pm in @INC (@INC contains: ~~~...)
```

モジュール関係。。もしや。。ですが、今回やった事はモジュールのインストールの `--test-only` なので本番には影響を与えていないはずです。

しばらく調査しているとcpanmの--test-onlyのリファレンスに怪しい記述がありました。

> 依存のあるモジュールやディストリビューションに このオプションを指定した場合、これらの依存は、現在持っていなければ、 インストールされます。
> 引用元： http://perldoc.jp/docs/modules/App-cpanminus-1.7001/lib/App/cpanminus/fatscript.pod

これが原因でした。 `--test-only` でテストのみ実行しているつもりですが、この仕様で依存するモジュールの更新とインストールを行っていました。

ローカルでは依存するバージョンが通っていたので依存関係のインストールが発生せずサーバーも問題なく起動して気づかなかったです。本番サーバーのみモジュールのバージョンが一部古く、そこに紐づく依存関係がすべて更新してしまった事でサーバーエラーが発生しました。

今回インストールしようとしたモジュールに依存するモジュールをインストールし、更にそれに依存するモジュールが更に存在する場合ピラミッド式にインストールを行ってしまっているようでした。

## 修正作業
モジュールのバージョンがおかしくなってしまったので、cpanfileを使って動いていたバージョンのモジュールをインストールして戻そう！という話になったので、本番環境のcpanfileを使ってモジュールを全て再インストール。

```
cpanm --installdeps [cpanfileのパス]
```

ここでも大量のエラー。すでに新しいバージョンがインストールされていたり、配布が停止しているモジュールがあったりで大変な事になりました。

* 一部のモジュールは今回インストールしようとしたモジュールの依存関係に紐づくバージョンに
* 一部のモジュールはcpanfileのバージョンに
* 一部のモジュールは旧本番環境のバージョンのままに

という3つの状態が混在してしまい大変な事になりました。。。

結局エラーのメッセージを一つ一つ潰して動くバージョンをインストールしていくという作業を永遠と繰り返してサーバーを復旧させました。

## 最後に
公式リファレンスをよく見ずに使うのは気を付けましょうという基本的な話でした。ただ、 `--test-only` といういかにも安全なオプション名をしているのにこの挙動は予測できなかった。。

ちゃんとcpanfileをメンテナンスしていれば、何かあった時も簡単に戻せるはずなので反省点です。

明日の記事はtake_3さんです。
HHKB Professional HYBRID Type-S 無刻印 を買ってmacでDvorak配列に設定してみた

私は詳しい人ではないですが、調べながらやってみて、そんなに難しくなかったので投稿させていただきました。m(_ _)m

- macOS Catalina Version 10.15.6
- HHKB Professional HYBRID Type-S 無刻印／白

---

HHKBのキーボードのこちらを購入しました

https://www.amazon.co.jp/gp/product/B082TWHY76/ref=ppx_yo_dt_b_asin_title_o02_s00?ie=UTF8&psc=1

HHKB Professional HYBRID Type-S 無刻印／白（英語配列）

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/142910/62b79301-6f25-cd37-612a-3064a7229f95.png" width="390">

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/142910/84004b3b-4283-5c5c-be90-66f640c0fcca.png" width="390">

# Macモードに設定する

参考

https://www.pfu.fujitsu.com/hhkeyboard/leaflet/hhkb_backview.html

キーボード背面にある２つ目のDIPスイッチをONに設定しておく

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/142910/71d9f5a7-2727-9eba-681d-6b9c3dd2e7f4.png" width="500">

**必ずキーボードの電源を切っている状態でスイッチを切り替える**

この設定をしないとcommandキーが動作しません

# bluetoothで接続した

キーボードの電源を入れたら普通に接続できる

「System Preferences」→「Keyboard」→「Set Up Bluetooth Keyboard」

![Screen Shot 2020-12-06 at 1.12.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/142910/8b66b853-399c-3417-3199-67456b7e460c.png)

Bluetoothを使わない場合は USB Type-C ケーブルで接続することもできる

その場合はケーブルは同梱されていないので自分で用意する

---

この状態で以下のような配列だったと思います

![61ECiYo2heL._AC_SL1280_.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/142910/fe8a2db6-0d32-9b4b-9b31-b0229048911d.jpeg)

引用：https://www.amazon.co.jp/HHKB-Professional-HYBRID-%E8%8B%B1%E8%AA%9E%E9%85%8D%E5%88%97%EF%BC%8F%E7%99%BD-PD-KB800W/dp/B082TT132L/ref=sr_1_6?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&dchild=1&keywords=hhkb+%E8%8B%B1%E8%AA%9E&qid=1607185477&sr=8-6

# Dvorak配列に変更する

「System Preferences」→「Keyboard」→「Input Source」

「＋」を押して「Dvorak」を追加する

追加できたら <kbd>Ctrl</kbd> + <kbd>Space</kbd> で切り替えれた

# 日本語入力も Dvorak で入力できるようにする

参考：https://qiita.com/raccy/items/23ae3658f3c70a9cd599

「System Preferences」→「Keyboard」→「Input Source」→「Japanese」

「Romaji layout」を「Dvorak」に変更する

![Screen Shot 2020-12-06 at 1.48.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/142910/6fd078af-7e7c-f8ba-6d4c-25b1a22bb824.png)

この設定でできました！

# その他

macのキーボードビューワーを使うと、dvorak配列で自分がどのキーを押しているかわかりやすかったです。

参考：https://support.apple.com/ja-jp/guide/mac-help/mchlp1015/mac

![Screen Shot 2020-12-06 at 1.56.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/142910/d183df71-6bc2-93f7-bd3a-45729296d690.png)

![Screen Shot 2020-12-06 at 1.59.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/142910/599e2e62-2cf7-eb6c-74f9-3c23ec37f8b2.png)

以上です。m(_ _)m
ridgepoleでRANGEパーティショニング

以前、[ridgepoleでRANGEパーティショニング（したかったけどワーニングが気持ち悪いから諦めてSQL書いた件）](https://qiita.com/taka0125/items/05740de838494c9c7548) という記事を書いた。
これは、ridgepoleでの管理を諦めて生SQLを叩いていた。

しかしその後、複数人で開発しているとちらほら問題が…。
そこでもう少し試行錯誤してみて、ちょっと無理矢理だけどridgepoleにスキーマ管理させる方法を見つけたので書いておく。

# やり方

`options` に無理矢理クエリを組み込む！w

```ruby
comment = "コメント"
partitions = []
partitions << "PARTITION p201610 VALUES LESS THAN ('2016-10-01') ENGINE = InnoDB"
partitions << "PARTITION p201611 VALUES LESS THAN ('2016-11-01') ENGINE = InnoDB"
# partitionは必要に応じて、列挙する
partitioning_sql = "/*!50500 PARTITION BY RANGE COLUMNS(created_at)\n(#{partitions.join(",\n ")}) */"

create_table "histories", id: false, primary_key: %i[id created_at], force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='#{comment}'\n#{partitioning_sql} */" do |t|
  t.datetime "created_at", null: false
  t.datetime "updated_at", null: false
end
```

- この手法を使う場合コメントも`options`にいれないとエラーが出る

# 注意点

MySQLのバージョンによってはパーティショニングしているテーブルに対してのカラム追加はオンラインDDLにならない事がある。
カジュアルに実行してしまうとサービス停止に追い込む事になるので気をつけましょう！
Ansible Tips: 独立した複数要素による多重ループを1重ループにならすにはproductフィルターが使用可能

備忘

# 課題: Ansibleにおいて、独立した複数要素を変化させるループをどうするか。
例えばxを[x0, x1]、yを[y0,y1], zを[z0,z1]の範囲で変化させるloopを考える。
素直な実装としては、x,y,zで3重loopで対応する方法が考えられる。
しかしAnsibleではblockに対するloopができない為、include_tasksにloopを適用することで多重ループに対応する必要がある。つまりtasksファイルを3つに分割しなければならず、可読性、メンテナンス性などに影響が生じる。

# ソリューション: productフィルターにより1重ループにならす
productフィルターを用いて1重ループにならす事で対応できるケースがあると思われる。

   

## task例

```yaml
  - debug:
      var: item
    loop: "{{ x|product(y,z) }}"
    vars:
      x: [x0, x1]
      y: [y0, y1]
      z: [z0, z1]
```

- 2次元ならばx|product(y)で良い
- 次元を増やすにはproductフィルターに与えるlistを増やす

## 実行ログ例

```
TASK [debug] ********************************************************************************************************
ok: [localhost] => (item=['x0', 'y0', 'z0']) => {
    "ansible_loop_var": "item",
    "item": [
        "x0",
        "y0",
        "z0"
    ]
}
ok: [localhost] => (item=['x0', 'y0', 'z1']) => {
    "ansible_loop_var": "item",
    "item": [
        "x0",
        "y0",
        "z1"
    ]
}
ok: [localhost] => (item=['x0', 'y1', 'z0']) => {
    "ansible_loop_var": "item",
    "item": [
        "x0",
        "y1",
        "z0"
    ]
}
ok: [localhost] => (item=['x0', 'y1', 'z1']) => {
    "ansible_loop_var": "item",
    "item": [
        "x0",
        "y1",
        "z1"
    ]
}
ok: [localhost] => (item=['x1', 'y0', 'z0']) => {
    "ansible_loop_var": "item",
    "item": [
        "x1",
        "y0",
        "z0"
    ]
}
ok: [localhost] => (item=['x1', 'y0', 'z1']) => {
    "ansible_loop_var": "item",
    "item": [
        "x1",
        "y0",
        "z1"
    ]
}
ok: [localhost] => (item=['x1', 'y1', 'z0']) => {
    "ansible_loop_var": "item",
    "item": [
        "x1",
        "y1",
        "z0"
    ]
}
ok: [localhost] => (item=['x1', 'y1', 'z1']) => {
    "ansible_loop_var": "item",
    "item": [
        "x1",
        "y1",
        "z1"
    ]
}
```

- 各要素はitem[0], item[1], item[2]などとして参照可能



# 注意/制約

- 要素が独立していないケースでは使用できない
- blockに対する操作では無いので、複数タスクに対して行うには、以下の様な対応が必要
    - それぞれのtaskでloopを行う
    - include_tasksに対するloopとする
        - この場合、1回のファイル分離が必要。(なお、多重ループによる実装では、ループ毎にファイル分離が必要なので、それよりは少なくて済む)
PharoLauncherでSmalltalk環境を作る(2020年末版）

#Smalltalkを触ってみよう！
Pharo（Smalltalk）を扱う環境を作ります。
PharoおよびSmalltalkは一見とっつきにくいですが、非常に柔軟で、使いやすい言語・環境です。
実は当初Smalltalk環境のインストールが済んだ後の内容を書きたかったのですが、ネットで探した「最初の一歩」的な記事が少し古い状態になっていたので、2020年末時点での環境を作りをまとめました。
##環境
OS Windows 10 (20H2) 
Pharo 8.0.0(stable)
PharoLauncher 2.2

MacOSもいけるはずなのですが、最近のOS環境変動が大きいらしく、自分が試したところこの先にやりたいことで少々予想外の挙動が見られました。そのため今回はWindowsでご紹介します。インストールまでなら特に問題ありません。Linuxは最近使える環境を整備していないのでパスします。Window以外のインストールについては公式のドキュメント（https://pharo-project.github.io/pharo-launcher/ ）を参照してください。
#ダウンロード＆インストール
Pharoの公式サイト(https://pharo.org/download )からダウンロードします。
![スクリーンショット 2020-12-06 010715.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63203/44fc0435-dc7f-f306-2bbd-94253191d2a7.png)
画面のWindows版のボタンをクリックするとpharo-launcher-2.2.msiというファイルがダウンロードされるので、クリックしてインストール開始です。
次のような画面がポップアップしてくるので、下の方のNextボタンをクリックします。
![スクリーンショット 2020-12-06 001147.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63203/f996f6b5-3653-e364-b365-7206f278a7b8.png)
インストール先のフォルダを聞いてきますので必要に応じて変更してください。こだわりがなければデフォルトで。Installボタンをクリックします。
![スクリーンショット 2020-12-06 001452.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63203/22b33087-8988-4c50-abe1-8671c66c2dbe.png)
無事にインストールが終わると終了画面が表示されるのでFinishボタンをクリックします。
![スクリーンショット 2020-12-06 001528.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63203/c7eafa4d-30e2-d84d-70ac-832b548e4985.png)
デスクトップに空飛ぶ灯台ロケットのアイコンができていると思います。クリックして立ち上げましょう。
![スクリーンショット 2020-12-06 001600.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63203/37c776c2-dd12-090e-85cf-c6ea9943a589.png)

#イメージの設定
Pharoをはじめ、SmalltalkはVMとイメージのセットで動作します。PharoLauncherはこれらのVMとイメージをワンセットで管理してくれます。
まず、起動すると真っ白な状態になっています。左上のNEWボタンをクリックしましょう。イメージ作成画面がポップアップします。
![スクリーンショット 2020-12-06 003700.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63203/8e6fc67b-bc7a-da6c-8d04-17508ed06f54.png)
一番左の1. Choose a template category:から、Official Distributionsを選びます。次に、2.Choose a template: からPharo 8.0-64bit(stabile)を今回は選択します。そして、右下のCreate imageボタンをクリックするとイメージのダウンロードが始まります。
![スクリーンショット 2020-12-06 003733.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63203/aa6c5200-3cb9-3f38-0e64-547cfde70539.png)
templateのPharo 8.0-64bit(stabile)を選択して、Launchボタンをクリックしてみましょう。
![スクリーンショット 2020-12-06 013155.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63203/cee4eed0-c386-491d-f3e4-88c05bb8fb41.png)
またダウンロードが始まります。これは初回のみ、イメージに適したVMをダウンロードするためです。しばらくすると、ダークモードのPharo8.0が立ち上がります。
![スクリーンショット 2020-12-06 004438.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63203/3a473bb5-aa27-47f3-53fe-6ec8b230d81b.png)
お疲れ様でした！
#まとめ＆次回予告
Pharo8.0を立ち上げるまでの手順をごく簡単に書き連ねました。
次はこのPharo環境に簡単にビジュアライゼーションができるRoassalを導入してCOVID-19の状況を可視化してみたいと思います。
#参考
PharoLauncher Documentation
https://pharo-project.github.io/pharo-launcher/

Pharo (Smalltalk) で Morph を使って "大石泉すき" を表示する
https://qiita.com/fibered_alto/items/1cad5a5d4c115c873ec5

PharoLauncherで日本語を使えるようにする3つの方法
https://qiita.com/tomooda/items/fa60135868df43e7ba78
「ファイル名を指定して実行」 への登録と履歴の削除

「ファイル名を指定して実行」を仕事場でも、
プライベートでも使用する回数が増えてきたため、まとめてみました。

##開き方
すでに使用されている方が多いと思いますが、
Windowsのロゴ(スタート)キーと「R」キーを同時に押すことで開くことができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/520258/4293ae31-4569-cb0d-3710-3743febd6839.png)


また、ショートカットが使用できないときは、スタートの[Windowsシステムツール]からも選択できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/520258/3f75bd2d-df8a-e28a-2e5d-2310898b50d9.png)

##使用できるコマンド
多くのコマンドを使用できるため、私自身がよく使用するものだけ記載してみました。

||コマンド名|用途|
|:--|:--|:--|
|1|cmd|コマンドプロンプトが起動|
|2|《開きたいパス》|エクスプローラから指定されたパスのフォルダ、ファイルが開く|
|3|mspaint|新規のペイントが開く|
|4|wordpad|新規のワードパッドが開く|
|5|calc|新規の電卓が開く|
|6|excel|新規のexcelが開く|
|7|winword|新規のwordを開く|
|8|regedit|レジストリを開く|
|9|notepad|新規のメモ帳を開く|
|10|mstsc|リモートデスクトップ接続|

他のコマンドは、「ファイル名を指定して実行 コマンド」などと検索するたくさん出てきます。

##名前の登録
ここから題名の内容に入ります。
Micosoftのアプリじゃないものを登録したいときには、レジストリーを編集して追加することができます。

<font color="Silver">※以降レジストリの変更を行います。
　ご存じの通り誤ったものを変更及び削除すると正常に動かなくなるため注意して変更してください。
　なお、動かなくなってしまっても責任は負いかねるためご了承願います。</font>

まずレジストリを開き、アドレスバーに下記のパスを入力します。
　**コンピューター\HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths**

**「App Paths」**内を右クリックして[新規]-[キー]をクリックします。
 　新しいキー#《数字》 を《実行したい名前》.exe に変更します。

新規に作成した、キーを選択します。
キーの中にある文字列値(一番上の「(規定)」と書かれている値)を右クリックし修正を開きます。
![h2RGg9z4tKoPOZzjOMN71607182650-1607183409.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/520258/3042bb67-2008-97a6-d145-69b04052e9db.gif)

これで登録は完成です。
使用する際に入力したアプリの実行パスを入力すれば使用できます。
図の場合は、「**escndv**」を**ファイル名を指定して実行**に入力します。

##履歴の削除
活用していくと、履歴がどんどん増えていきませんか？
手が覚えている名前を実行するのであれば何も問題ないですが、
久しぶりに使用するコマンドや長いフォルダパスなどは、履歴から探したほうが早かったりしますよね。

でも、履歴が多いと探すだけ途方もなく時間がかかってしまうこともあるため、
使用しなくなったものは、削除してしまうのも一つの方法かと思い書いてみました。

<font color="Silver">※重ねてのこととなりますが、
　レジストリの変更を行って万が一動かなくなってしまっても責任は負いかねるためご了承願います。</font>

レジストリを開き、アドレスバーに下記のパスを入力します。
**コンピューター\HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\RunMRU**

このキーの中から**(規定)**以外の不要となった文字列値を選択します。
右クリックしたのち削除します。
![Rf1lhoeABgIVTUmiixhx1607185672-1607185704.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/520258/2ee1b7a9-0060-627a-99ba-6a55d724f140.gif)

削除したあとも新たに**ファイル名を指定して実行**に入力すると再度実行できます。

##参照したサイト・記事
・記事の内容
[ファイル名を指定して実行でサクラエディタを開く方法](https://qiita.com/sekiya-don/items/4d7698de36d28cb7e0e9)
[「ファイル名を指定して実行」の履歴を削除したい](https://www.itmedia.co.jp/help/tips/windows/w0064.html)
[「ファイル名を指定して実行」でよく使うもの](http://www.cori95.net/etc/windowsr.html)

・マークダウンの記載方法
[Qiita マークダウン記法 一覧表・チートシート](https://qiita.com/kamorits/items/6f342da395ad57468ae3#%E8%A6%8B%E5%87%BA%E3%81%97)
[Qiitaのマークダウンで色をつける方法[140色]](https://qiita.com/twipg/items/d8043cd4681a2780c160)
有益でGPLなWindowsアプリをめも。

# 有益でGPLなWindowsアプリをめも。
GPLやMs-PLなど自由に配布できるのをめも。
自分が使い易いと思ったのなので、偏ってるかも

## マニュアル作りに使えそう
###[ScreenToGif](https://www.screentogif.com/)
This tool allows you to record a selected area of your screen, live feed from your webcam or live drawings from a sketchboard. Afterwards, you can edit and save the animation as a gif, apng, video, psd or png images.
HTMLやPDF(未確認)にgifで貼れば、動く画像でマニュアルとか作れる？

## テキスト変換
###[Pandoc](https://pandoc.org/)
Pandocとは、 文書作成ツール や出版作業の基礎的なツールとして用いられるフリーかつオープンソースのドキュメント・コンバータである。
[Pandoc ユーザーズガイド 日本語版](http://sky-y.github.io/site-pandoc-jp/users-guide/)
[インストーラ](https://github.com/jgm/pandoc/releases)
[テスト環境](https://pandoc.org/try/?text=%23%23%23%23test&from=markdown&to=html)
Markdown(VSCodeで作成) -> Textile(Redmine)なんかができる？

## その他
### [WinMerge](https://winmerge.org/)
WinMerge は、 オープンソースのWindows 用比較・マージツールです。WinMerge は、フォルダとファイル両方が比較でき、理解しやすく扱いやすいテキスト形式で差異をビジュアルに表現します。
【 指（体）で体感、Julia ! 】 Juliaは数学に近い書式で、ギリシア文字を含む数式をコードに書いていける

Juliaは、ギリシャ文字を含めて、数学の記法に近い書式でのコード記述が可能であることを体で体感してみた。

__（ 参考 ）__

・ [物理で使う数値計算⼊⾨：Julia⾔語による簡単数値計算](http://park.itc.u-tokyo.ac.jp/kato-yusuke-lab/nagai/julianote.pdf)

![julia.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/862466/2115d116-a67a-dff4-9218-bd64bbc0db43.gif)

____

###Juliaのコード記法が、数学のそれと近い点

1. 「*x*を2倍した値」をコードに記述するとき、_'2*x'_と書かずに、_'2x'_と書くことができる。（乗算記号を省略できる。つまり、数学と同じ記法）
2. ギリシア文字をそのままの姿で記載できる。
3. 配列の番地は、0番地始まりでなく、1番地始まり。(R言語や１MATLABと同じ。統計解析処理や科学技術計算に特化した言語の共通点）

___

さらに、今回は実演しませんでしたが、bashのパイプライン処理のように、_'処理A |> 処理B'_ と続けたり、「f○g」の書式で、関数合成を表現できます。

・ [@yurluciaさん 2020年11月15日に更新 「Julia便利記法・機能備忘録(関数編) 」](https://qiita.com/yurlucia/items/6a5daf2b57e24682bc99)

なお、Pythonで、パイプライン演算子ライクな自作演算子を定義する試みも見つけました。Juliaでは、標準仕様で、パイプライン演算子を使えます。

・ [@ukyo-suさん 2019年08月19日に更新 「Pythonにパイプライン演算子（のようなもの）+α」](https://qiita.com/ukyo-su/items/862feb3cae95596ea063)

```julia:Julia
julia> α = 2.54
2.54

julia> β = log10(1000)
3.0

julia> ζ = 2sin(5π+23) + log10(3200)
5.197590786670251

# 関数fは、右辺でxを使っていなかったw 
# （これじゃxの入力値にかかわらず、常に同じ値を返す定数（恒等式）だ！）
julia> f(x) = cos(3π+2) + 3α + 7β + ζ
f (generic function with 1 method)

julia> f(198)
34.233737623217394

julia> f(log10(3455))
34.233737623217394

# 関数gは、右辺でxを使っていなかったw 
# （これじゃxの入力値にかかわらず、常に同じ値を返す定数（恒等式）だ！）
julia> g = x -> log10(654) + log2(5328) + tan(α+β)
#1 (generic function with 1 method)

julia> g(431)
14.276008523031766

julia> g(218)
14.276008523031766

julia> κ
ERROR: UndefVarError: κ not defined

julia> ϵ
ERROR: UndefVarError: ϵ not defined

julia> 
```
既存のRailsアプリにNginxを導入しよう！(Docker, docker-compose)

[前回の記事](https://qiita.com/GoodWave2020/items/9b81b5278f43d2e856cb)で作ったRailsアプリにNginxを導入していきます。やっとここまできた！

## 環境
- Docker 19.03.13
- docker-compose 1.27.4

## Dockerfile

```Dockerfile:rails_test/containers/nginx/Dockerfile
FROM nginx:1.15.8

RUN rm -f /etc/nginx/conf.d/*

ADD nginx.conf /etc/nginx/conf.d/rails_test.conf

CMD /usr/sbin/nginx -g 'daemon off;'
```

Dockerfileを新しくディレクトリを作ってそこに置きます。ここらへんのファイル構成は任意です。
特に[Nginxの記事](https://qiita.com/GoodWave2020/items/bd9fb0010a1b126452c2)と変わったところはありませんのであまり説明しませんが、新しく設定ファイルを作成して保存するためADDしています。余計な設定ファイルはrmで消しておきます。

## nginx.conf
設定ファイルを書きます。

```conf:rails_test/containers/nginx/nginx.conf
upstream rails_test {
    server unix:///rails_test/tmp/sockets/puma.sock;
}

server {
    listen 80;
    server_name localhost;

    access_log /var/log/nginx/access.log main;
    error_log /var/log/nginx/error.log debug;

    root /rails_test/public;

    client_max_body_size 100m;
    error_page 404             /404.html;
    error_page 505 502 503 504 /500.html;
    try_files  $uri/index.html $uri @rails_test;
    keepalive_timeout 5;

    location @rails_test {
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_pass http://rails_test;
    }
}
```
ソケット通信をするためupstreamにてパスを指定しています。

## docker-compose.yml
以下に修正します。


```diff:docker-compose.yml
version: '3'
services: 
  app:
    build: .
    volumes: 
      - .:/rails_test
+     - tmp-data:/rails_test/tmp
+     - public-data:/rails_test/public
+    command: puma -C config/puma.rb
-    command: bash -c "rm -f tmp/pids/server.pid && rails s -b 0.0.0.0"
-    ports:
-      - 3000:3000
    environment:
      WEBPACKER_DEV_SERVER_HOST: webpacker
    env_file: 
      - ./environments/db.env
    depends_on: 
      - db
  webpacker:
    build: .
  environment:
    NODE_ENV: development
    RAILS_ENV: development
    WEBPACKER_DEV_SERVER_HOST: 0.0.0.0
  volumes: 
    - .:/rails_test
  command: ./bin/webpack-dev-server
  ports:
    - 3035:3035
+ web:
+   build:
+     context: containers/nginx
+   volumes:
+     - public-data:/rails_test/public
+     - tmp-data:/rails_test/tmp
+   ports:
+     - 80:80
+   depends_on:
+     - app
  db:
    image: mysql:5.7
    volumes: 
      - rails-db:/var/lib/mysql
    env_file: 
      - ./environments/db.env
volumes: 
  rails-db:
+ tmp-data:
+ public-data:
```

ソケット通信するので3000番ポートが必要なくなります。コマンドも直接pumaを起動するものに変更します。
また、tmpディレクトリを永続化してNginxでpuma.sockを見れるようにしています。

## puma.rb
pumaのファイルにて以下修正します。差分のみ表示しています。

```diff:puma.rb
- port        ENV.fetch("PORT") { 3000 }
+ app_root = File.expand_path("../..", __FILE__)
+ bind "unix://#{app_root}/tmp/sockets/puma.sock"
```

あとは前回同様にビルドしたのちに起動すればNginxとRailsにアクセスできます。

```
$ docker-compose build
$ docker-compose up -d
$ curl http://localhost
<!DOCTYPE html>
<html>
<head>
  <title>Ruby on Rails</title>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
　　　　　　　　　　　　　・
　　　　　　　　　　　　　・
　　　　　　　　　　　　　・
```

## 所感
これで本番環境とほぼ同じ構成にできました。あとはこれをECSを使ってEC2で動かすのみといったところです。