OmniAICreator/Qiita-1.07M huggingface dataset:
API Gatewayに繋がらねぇ...

## はじめに
普段、主にフロントエンドを触っているのですが、繋がらなかったり、予想外の挙動をしたりと頻繁にパニクるので備忘録的にまとめます。
他にもこんなハマりポイントがあったよみたいな話お待ちしております:running:


![computer_error_bluescreen.png](https://qiita-image-store.s3.amazonaws.com/0/223534/c105e974-698e-0f8f-48e0-a98fa578ca46.png)


### 1. Missing Authentication Token...???
##### 事象
認証なしに設定している、あるいはAuthenticationトークンをヘッダーに詰めていて適切にリクエストしているつもりなのに一生これが返ってくる

##### 原因
**APIのデプロイをしていませんでした**。
初っ端からしょうもないミスで申し訳ないんですが、本当によくやるので。。
リソースに変更を加えたらデプロイをしましょう。
<img width="502" alt="スクリーンショット 2019-03-06 23.34.50.png" src="https://qiita-image-store.s3.amazonaws.com/0/223534/d6f1dbf0-6b7a-e493-ac07-4bc91f17283b.png">

* * *

### 2. No 'Access-Control-Allow-Origin' header is present on the requested resource.
##### 事象
通信に失敗して、インスペクタをみたら何やら赤文字で標題のようなメッセージが表示されている

##### 原因
CORSの設定が適切ではなかった。

CORSは、**Cross-Origin Resource Sharing**の略です。
現在のブラウザでは、クロスサイトスクリプティングを防止するために、特定の条件の時を除き（単純リクエストと呼ばれます。[参考:オリジン間リソース共有 (CORS)](https://developer.mozilla.org/ja/docs/Web/HTTP/CORS#Examples_of_access_control_scenarios))、プリフライトリクエストというものを実際のリクエスト前に行い、そのレスポンスに応じてHTMLを取得したサーバ以外からデータを取得することを拒否するようになっています。

なので、API Gatewayを使う場合**CORSを有効化し、適切に設定してあげる必要**があります。
つまり今回のメッセージは、許容されないヘッダーがリクエストに含まれており、エラーが発生したということを示しているということですね。

主なHTTPレスポンスヘッダーは下記のようになります。

|HTTPヘッダー名|説明|
|:--|:--|
|Access-Control-Allow-Headers|実際のリクエストで許容するヘッダーを指定する|
|Access-Control-Allow-Methods|実際のリクエストで許容するHTTPメソッドを指定する|
|Access-Control-Allow-Origin|リクエストを許容するオリジンを指定する|


例えば、こんな感じ。
<img width="1439" alt="スクリーンショット 2019-03-07 0.00.43.png" src="https://qiita-image-store.s3.amazonaws.com/0/223534/3d0daa7c-4ec7-b717-25b8-650a598ad78c.png">

CORSの有効化を行った後でも**OPTIONSメソッドの統合レスポンスにて設定できます**。
設定したらデプロイしてね:expressionless:

* * *

### 3. エラーレスポンスがすベてCORSエラーになっている
##### 事象
エラーが返ってきてるのはわかるんだけど、CORSで弾かれて詳細が全くわからない。。

##### 原因
API Gatewayでは、統合レスポンスに何らかの原因で到達しなかった場合、ゲートウェイレスポンスというものを返却しますが、こちらのCORS設定が行われていませんでした。

<img width="1648" alt="スクリーンショット 2019-03-07 0.07.59.png" src="https://qiita-image-store.s3.amazonaws.com/0/223534/34cdafe5-e4ee-7032-6066-cf69acdb73d6.png">
こんな感じで、レスポンスヘッダーをよしなに設定してあげるとうまく行くかと思います:ok_hand:

* * * 

繋がって、ちゃんとエラーさえ返って来れば一安心です。

[こちら](https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/supported-gateway-response-types.html)にゲートウェイレスポンスの詳細が乗っています。
個人的には、エンドポイントの指定を間違えてAPI Configuration Errorが返ってくることとかよくありました。

## ところで...
ステージの設定で、Cloud Watch Logsにログを出力することが可能です。
<img width="1216" alt="スクリーンショット 2019-03-07 0.14.57.png" src="https://qiita-image-store.s3.amazonaws.com/0/223534/09f96fdf-6855-bf28-7dba-2f1f8cfed5d1.png">

それでは、素敵なAPI Gatewayライフを！
まずはVimを普通のテキストエディタ以上にしよう

vimを久しぶりに使ってイライラするのが、最低限のテキストエディタでもできるマウススクロールや文字の選択・コピペなどが出来ないことです。完全にvimを使いこなしている人ならあったらむしろ邪魔なのかもしれませんが、毎日触るわけではない人間からすると、vimを使うことでかえって生産性が落ちることがあります。

そこで、最低限普通のテキストエディタができる操作はできる状態に整えれば、vim離れもなくなるのではないかと思い、今回設定した内容をまとめました。


## 1. マウス操作を可能にする

`$ vim --version`で自分のvimがマウス操作を可能にできるバージョンでインストールされているか確認してください。
`-mouse`となっていればvimを入れ直して`+mouse`にしてください。


~/.vimrcに以下を追加。

``` vimscript
" 一行目: 全モードでマウス操作を可能に設定。
" 二行目: screen使用時にマウス操作がフリーズする対策らしい。
if has("mouse")
    set mouse=a 
    set ttymouse=xterm2
endif
```
特定のモードのみで有効にすることも可能です。

* [端末vimでマウスを使う \- Keep It Simple, Stupid](http://yskwkzhr.blogspot.com/2013/02/use-mouse-on-terminal-vim.html)

これで設定完了です。
例えばnormalモードで単語の上でダブルクリックすれば、その単語を選択した状態でvisualモードに移行し、更にもうワンクリックするとvisual lineモード（行選択モード)に移行できるようになっているはずです。

## 2. クリップボード連携でコピペ 
これも`+clipboard`になっているか確認。

~/.vimrcに以下を追加。

``` vimscript
" save yank on clipboard
if has("clipboard")
    set clipboard+=unnamed,autoselect
endif
```

### 複数行選択で行番号が入ってしまって邪魔な場合の対策
.vimrcで`set number`として行番号を表示している人は多いハズ。この状態でマウス操作で複数行コピペすると行番号まで入ってしまうのが鬱陶しい。

少し面倒だが、コピー前に

```
:set nonumber
```
として非表示にしてからコピーしてまた戻せばとりあえず回避出来ます。

（いい方法ご存知の方いらっしゃったらコメント下さい。)


## まとめ
vimは素晴らしいエディタだからこそ、最低限の設定はして生産性を落とさないようにしましょう。
Unityを始めてから1週間で出来た2D横スクロールゲーム

私がUnityをインストールしてからちょうど一週間が立ちましたので、成果物と参考HPを載せます。未経験者でもこれくらいは誰でも作れますよ的な記事です。

#成果物のゲーム
![6.gif](https://qiita-image-store.s3.amazonaws.com/0/370076/e7dd5cec-50c1-72a1-0816-67c39ddd2c7f.gif)
ストアとかで配布したら評価が★☆☆☆☆とかになるやつ。
練習用なのでWebGL形式で配布とかは見送りました。

#心がけたこと
配布されているアセットは出来るだけ使わずに（画像素材は除く）、手でプログラミングすること。自分が考えたもののうち、ネットに情報が少ない題材は記事にして公開すること辺り。

#実装できたもの
- 基本的な移動（横移動、一段ジャンプ）
- アニメーション：移動アニメ・死亡アニメなど
- プレバブの活用
- 攻撃要素：Fキーを押したら弾丸を発射
- 敵オブジェクト：プレイヤーが索敵範囲に入ったら近づいてくる
- 移動床タイル：上に乗っても横移動で滑り落ちないやつ
- 当たり判定：トゲにあたったら死亡判定
- UI：右上にあるリトライボタン
- ゲームクリア画面：質素なテキスト表示
- tilemapEditorの活用

#実装できなかったもの（~~面倒で付けなかったとも言える~~）
- プレイヤーの加速度を付けた移動演算
- ちゃんとした索敵：今の索敵では特定の索敵範囲に入ると全ての敵が反応してしまうため。（敵オブジェクトと索敵エリアをプレハブかつ親子関係にしたせい？配列とかで敵に番号を割り振るべきか？）
- レイヤーの設定ミス：後から見返してみたらUIとかのレイヤーが一番上になっていなかった。ケアレスミスなので問題なし。
- ゲームとしての面白さ：なにか目新しいものが欲しかった。何の脈絡も無くシューティングゲームに移行したりとか。2週目からプレイヤーと敵の関係が逆転するなど。（実は敵が人間でプレイヤーが化物とか）

#参考にしたもの
ネットの記事のみ。本も買いたいけど、自分の技術レベルにあった本がどれか分からないため、Google検索を活用してます。
- [私のいいね一覧](https://qiita.com/SpaceCore/like)：最初の20個くらいの記事は参考にさせて頂いたもの
- [Unityで2Dアクションゲームを作ろう](https://tech.pjin.jp/blog/tag/unity%E3%81%A72d%E3%82%A2%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3%E3%82%B2%E3%83%BC%E3%83%A0%E3%82%92%E4%BD%9C%E3%82%8D%E3%81%86/)
- [2Dスクロールゲームの例とゲームの作り方](https://openbook4.me/projects/161/sections/1077)
- [オブジェクトの親子関係を操作する方法](http://karaagedigital.hatenablog.jp/entry/2015/04/30/105856)
- [【Unity】Update()とFixedUpdate()には使い分けがあった!?](https://www.clrmemory.com/unity/proper-update-fixedupdate/)
- [【Unity】コライダーに特定の機能を追加するEffectorで少し遊んでみる](http://tsubakit1.hateblo.jp/entry/2016/01/21/015028)
- [Unityの2Dゲームで「当たり判定」の基本を30分で理解するコツ！(前編)](http://0310unity.hateblo.jp/entry/collision_detection_1)
- [【Unity】背景を設定する方法!Skyboxや単体画像の設定の仕方](https://www.sejuku.net/blog/83708)
- [プレーヤーのステータス⑥（リスタートボタンの作成）](https://codegenius.org/open/courses/17/sections/35)
他にも大量の役立つページが有りましたが、ブックマークを失念していました。
#今後作りたいもの

基礎の基礎はなんとなく理解できたので違う題材の練習用ゲームを作りたい。例えば下のようなゲームとか。

- タワーディフェンス：ステージクリア形式ではなく、無限に出てくる敵をトラップで圧殺してハイスコアを稼ぐようなタイプ。
- ダンジョンクローラー：Wizardryのようなキャラ育成要素のあるもの。
- ローグライク：Elonaのようなディープな育成要素のあるもの。

キャラのパラメータがグングン上がっていくゲームが好きなので、そんなものを試しに作ってみようかと思案中。
デスクトップアプリまとめ

# Linux, MacOS, (Win...)

## IRC

Polari

## git

GitKraken

## その他

### SNS

Slack
Mattermost
Discord

### Browser

Chrome
FireFox
Brave

### IDE

IntelliJ IDEA
VisualStudio Code

## Browsing IDE

Cloud9
Coder
CodePen
Lambda

## おもちゃ

sl
cmatrix
conky

## 仮想

VirtualBox
VMWare

## 仮想の仮想

Docker

## 仮想の仮想の管理

Kubernetes（くべるねてす）リストニア語ってググが...
minikube
microkube

## RDP

TeamViewer
RailsのSitemapGeneratorでS3を使うとAccess Deniedが出る問題

RailsのSitemapGeneratorでS3を使うとAccess Deniedが出る問題が起こったので解決方法を記録しておきます。

## RailsのSitemapGeneratorでS3を使うとAccess Deniedが出る問題
結論からいうと、S3バケットの公開設定をしていないためにAccess Deniedが起きていました。
S3でバケットを公開するようにしたら直りました。
## 終わりに
私は現在、Web3のサービスの開発をしています。詳しくはこちらの記事をご覧下さい。
        [無料でイーサリアムが当たる、Web3時代の寄付サイトを作った話](https://note.com/kenroda/n/n814347fbdcd7)
Docker+Spring-boot事始め

# はじめに
JavaというかSpringを触っている今日このごろ。
ついでにDockerで動かそうと思ったのでその設定をしていく。

# 前提
Dockerが使えること。
設定方法については割愛。

## 環境
* OS: macOS Mojave
* Docker: Docker for Mac v2.0.0.3

# Java(Spring boot)の設定
## 雛形ダウンロード
サクッと雛形を[Spring Initializr](https://start.spring.io/)で作成する。
指定したのは下記の通り。書いてないとこは何でも良いと思う。

|項目|内容|
|:--|:--|
|Project|Gradle project|
|Language|Java|
|Spring boot|2.2.0(SNAPSHOT)|
|packaging|jar|
|Java version|11|
|Dependencies|Lombok,Web|

## ちょっとアプリ改修
このままでも動くけど動くだけなので軽く修正を加えることにする。
内容はただ単にレスポンスを返せるようにするだけ。
まず、自動生成されたクラスと同列に`IndexController.java`を作成する。
作成したファイルの内容は下記の通り。

```sh
$ touch src/main/java/...(package)/IndexContoroller.java
```

```java
package hoge;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class IndexController {

    @GetMapping("index")
    public String getIndexPage() {
        return "Hello World!";
    }
}
``` 

# Dockerの設定
Dockerfileをちまちま書いていく。  
調べた感じみんなローカルでビルドした後Dockerで動かしているのが多かったけど、
全部Dockerでやりたいので必要なファイルをコピってビルド & 実行。
ホントはこんなCOPY連打しないほうが良いんだろうけど、対処法がわからないので一旦こうする。

## Dockerfile
```dockerfile
FROM openjdk:latest

RUN mkdir /app
WORKDIR /app
COPY ./gradlew /app
COPY ./build.gradle /app
COPY ./settings.gradle /app
COPY ./src /app/src
COPY ./gradle /app/gradle
ENTRYPOINT ["sh", "./gradlew", "bootRun"]
```

# 実行
準備が終わったので実行してみる。
ローカルから叩いてちゃんとレスポンスが取れればOK。

```bash
$ docker build -t spring-docker ./
# Springがport8080で起動するのでローカルからは3000にする
$ docker run -p 3000:8080 spring-docker
```

問題なく起動したら適当にcurlで確認してみる。
下記のようになればOK。

```bash
$ curl localhost:3000/index
  Hello World!
```

# まとめ
今回はDocker + Spring bootの設定をしてみた。
思ってたより楽だったし、久しぶりにJavaも触りたいのでもうちょっと何かを作ってみたいところ。
上でも書いたけど、意外とDocker上でビルドとかしないんだろうか。。。
それでは今回はこの辺で。
【初心者】図書館で借りた本を管理したい（外堀編）

最近、図書館で本を借りることが多くなったので
借りた本をいつ返すのか管理できないものかと考えて勉強中のpythonでどうのこうの

```python:main.py
import Bookshelf

if __name__ == '__main__':
    book_name = input('本のタイトル：')
    writer_name = input('作者：')

    Bookshelf.book(book_name, writer_name)
```
```python:Bookshelf.py
import os
import csv
import datetime

rental_day = datetime.date.today().strftime('%Y{}%m{}%d{}').format(*'年月日')
return_d = datetime.date.today() + datetime.timedelta(days=14)
return_day = return_d.strftime('%Y{}%m{}%d{}').format(*'年月日')

import csv
import datetime
import os

def book(book_name, writer_name):
    fieldname = ['貸出日', 'タイトル', '作者', '返却日']
    if not os.path.isfile('Bookshelf.csv'):
        with open('Bookshelf.csv', 'w', encoding='utf-16', newline='') as csv_file:
            writer = csv.DictWriter(csv_file, fieldnames=fieldname)
            writer.writeheader()
            writer.writerow({'貸出日':rental_day,
                             'タイトル':book_name,
                             '作者':writer_name,
                             '返却日':return_day})
            print('本日{}に借りた本は\n'
                  'タイトル:{}\n'
                  '作者:{}\n'
                  'は、{}までに返却しなければいけません。'.format(rental_day, book_name, writer_name, return_day))
    else:
        with open('Bookshelf.csv', 'a', encoding='utf-16', newline='') as csv_file:
            writer = csv.DictWriter(csv_file, fieldnames=fieldname)
            writer.writerow({'貸出日':rental_day,
                             'タイトル':book_name,
                             '作者':writer_name,
                             '返却日':return_day})
            print('本日{}に借りた本は\n'
                  'タイトル:{}\n'
                  '作者:{}\n'
                  'は、{}までに返却しなければいけません。'.format(rental_day, book_name, writer_name, return_day))
```

csvファイルの読み込みと追記で苦戦したけどなんとか最低限の形にはなった気が。。。

##まとめ
もっとスマートに記述できるとは思うけどできない
その繰り返し

また、お気づきの点がありましたら遠慮なくご指摘お願いいたします。
argparseなソースコードをgoogle colaboratoryで動かすとき

# argparseなソースコード
ディープラーニングとか勉強してるとき，githubに挙げられてるソースコードをとりあえず動かしてみる。こういうソースってコンソールから動かせるように引数をargparseで受ける構造になってることが多い。でもGPUなんて無いときはgoogle  colaboratoryにお世話になりますよね。
こんなときcolabにソースをコピペしていくことになるけど，argparseな変数を書き換えないといけないのでちょっとだけ面倒くさい。そこで，argparseを一発で書き換えられるようにした。

# 変換コード

```python
# argparse的な部分をコピーする
txt = """

parser = argparse.ArgumentParser()
parser.add_argument('--out', type=str, default='result',
                    help='Output directory')
parser.add_argument('--dataset-name', type=str, default='mscoco', choices=["mscoco", "stair_captions"],
                    help='MSCOCO dataset root directory')
parser.add_argument('--mscoco-root', type=str, default='data',
                    help='MSCOCO dataset root directory')
parser.add_argument('--max-iters', type=int, default=50000,
                    help='Maximum number of iterations to train')
args = parser.parse_args()

"""

print("class args():")
print(" "*4+"def __init__(self):")
for _ in range(20):
  index = txt.find("add_argument")
  if index < 0:
    break
  txt = txt[index:]
  txt = txt[txt.find("--")+2:]
  variable = txt[:txt.find("'")].replace("-","_")
  txt = txt[txt.find("default"):]
  txt = txt[txt.find("=")+1:]
  val = txt[:txt.find(",")]
  print(" "*8+"self."+variable+" = "+val) 
print("args = args()")
```

これを動かすと，

```python
class args():
    def __init__(self):
        self.out = 'result'
        self.dataset_name = 'mscoco'
        self.mscoco_root = 'data'
        self.max_iters = 50000
args = args()
```
という文字列が出力されるので，argparse的な部分と置き換えれば他のとこを修正する必要なく動かせる。

他にもっといい方法があるのかな。
アソシエーション分析とスペクトラルクラスタリングを行って、論文の共著関係を視覚化する

#はじめに
2日連続の投稿です。
今回は、[前回](https://qiita.com/gekko_datasci/items/cf097d7b3aa2f25594ac)取得したPubmedデータを利用して、簡単なクラスタリングを行います。
①pubmedの検索結果から、authors listを取得
②authors listにアソシエーション分析を行い、共著が多い人物セットを見出す
③アソシエーション分析結果に対して、スペクトラルクラスタリングを行う

#ライブラリ
```python

import time
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import chromedriver_binary
from selenium import webdriver
import pyfpgrowth
import networkx as nx
from sklearn import cluster
%matplotlib inline
```

#①pubmedからデータ取得
前回の投稿と同じ関数(get_pubmed_info)を使っているので、関数の内部は割愛します。
解析対象として、研究室内の派閥(グループ)が多そうな教授をチョイスしました。

```python

data = get_pubmed_info("miura masayuki")
author_list = [data[i][2].split(",") for i in range(len(data))]
author_list = [[author.split("(")[0].replace("\n"," ").strip(".").strip()
   for author in author_list[i]] for i in range(len(author_list))]
```
#②アソシエーション分析
pyfpgrowthで、先程取得したデータに対して、アソシエーション分析を行います。

```python

patterns = pyfpgrowth.find_frequent_patterns(author_list, 3)
G = nx.Graph()
for key in patterns.keys():
    if len(key) == 2:
        G.add_path(key)
pr = nx.pagerank(G)
plt.figure(figsize=(20,20))
nx.draw_networkx(G, node_color=list(pr.values()), cmap=plt.cm.Reds, font_size=15, node_size=5000)
```

###得られた結果をnetworkxで可視化
pagerankで、ネットワークの重要度を色付けしています(色が濃いほど重要)。
このネットワーク図からでも、クラスターの存在は感じられます。
![networkx.png](https://qiita-image-store.s3.amazonaws.com/0/371779/05af7537-ae0d-8641-accd-f39f82739e9e.png)

#③スペクトラルクラスタリング
pyfpgrowthで得たデータを整形して、スペクトラルクラスタリングを行います。

###データ整形過程

```python

key_list = []
value_list = []
for key, value in patterns.items():
    if len(key) == 2:
        key_list.append(key)
        value_list.append(value)
df = pd.DataFrame()
for (i, j), num in zip(key_list, value_list):
    df.loc[i, j] = num
    df.loc[j, i] = num
df = df.loc[df.columns].fillna(0)
sns.heatmap(df)

```
データ整形結果をヒートマップにすると、こんな感じです。
![heatmap.png](https://qiita-image-store.s3.amazonaws.com/0/371779/6dc2995b-4ff1-4d37-f41c-a1ca4dc54048.png)

###いざスペクトラルクラスタリング！

```python

spectral = cluster.SpectralClustering(n_clusters=9, affinity="precomputed")
result = spectral.fit(df.values)
result_se = pd.Series(result.labels_, index=df.index)
plt.figure(figsize=(20,20))
nx.draw_networkx(G, node_color=result_se, cmap=plt.cm.Accent, font_size=15, node_size=5000)
```
クラスタリング結果を先程のネットワーク図に反映させました。
キレイに研究グループごとに分かれていることがわかります。
濃い緑は哺乳類系、茶色はショウジョウバエ神経系、ピンク色は外部の共同研究先といった具合になっています。

参考
http://www.f.u-tokyo.ac.jp/~genetics/
![スペクトラルクラスタリング.png](https://qiita-image-store.s3.amazonaws.com/0/371779/082d96e2-1590-3749-28be-9e7cb508ebe4.png)

#終わりに
かなりきれいにクラスタリング出来ました。
はじめてのC++【10日目】

#はじめに
今日は時間がないので,皆様に頂いたコメントは明日読もうと思います。
あと花粉症辛すぎ。喉痒すぎ、鼻水出てきそうで出てこない悲しみ。
##ロベールのC++
###05-10　変数の寿命とスコープ
変数はそれぞれ有効期間を持ってる
例えば、グローバル変数はプログラムが実行されている間はずっと有効
　　　 　自動変数は関数を抜けるまで有効
こういった有効期限のことを「記憶期間」「寿命」などといったりする。
{ ブロック }の中で作られた変数はブロックより外では使えない.
std::coutとかよく使ってたけど「　::　」の名前は、スコープ解決演算子というカッコいい名前だった！

###05-11 リンケージ
リンケージとは、ファイルを超えて利用できるかどうかを言う。
超えられたら外部リンケージ、ファイル内のみなら内部リンケージという
externをつけた変数宣言は、別の場所で定義された外部リンケージのグローバル変数を扱うという宣言になる
staticをつけたら内部リンケージになる。

#終わりに
花粉症か風邪かわからん
少ないですが、今日はここまで
おやすみなさい
超シンプルなLambda Layersのサンプル（Python、Serverless Framework使用）

# Layer

複数のLambdaから呼ばれる共通モジュールをイメージ。

```terminal:ディレクトリ構成
common
├── serverless.yml
└── layer
    └── python
        └── util.py
```

```yaml:serverless.yml
service: sample-layer

provider:
  name: aws

layers:
  samplelayer:
    path: layer
```

```python:util.py
def hello():
    print('Hello, Lambda Layers World!')
```

# Lambda Function

上記のLayerモジュールを呼び出すLambda。

```terminal:ディレクトリ構成
function
├── serverless.yml
└── handler.py
```

```yaml:serverless.yml
service: sample-function

provider:
  name: aws
  runtime: python3.7
  iamRoleStatements:
  - Effect: "Allow"
    Action:
    - "lambda:InvokeFunction"
    Resource: "*"

functions:
  samplefunction:
    handler: handler.handle_request
    layers:
      - {上記のLayerをsls deployした時に表示されるarn}
```

```python:handler.py
import util

def handle_request(event, context):
    util.hello()
```

# 実行結果

![LambdaLayers.png](https://qiita-image-store.s3.amazonaws.com/0/269194/855d54d5-57b7-94df-7135-24576ba8cba7.png)

# 注意点など

- Layerは`/opt`に展開される。ここにはパスが通っており、Pythonだと`/opt/python`と`/opt/python/lib/python3.7/site-packages`が使えるので、Layerも`python`ディレクトリを含めたディレクトリ構成にする必要がある。
- デプロイされたLayerの中身はコンソール上で見たり編集したりすることができない。
- Layerはデプロイする度にバージョンが上がり、ARNも変わる。今回の例のように別スタックからARNでLayerを参照する場合は、Layerをデプロイしたら他の参照しているLambdaもデプロイし直す必要がある。
Gitレポジトリ作成方法について

SourceTree を使用して、Gitレポジトリの作成および利用方法を記載しています。
注意）メモのため、あくまで参考程度にしてください。

#対象サーバーにログインする
自PCで、リモートレポジトリを作成する場合は、この項目は不要。
例） 
ssh git-user@xxx.xxx.xxx.xxx

#リモートレポジトリとなるディレクトリを作成する
任意(/var/git)の場所に移動し、リモートレポジトリとなるディレクトリを作成する。
例） コマンドを使用する場合
cd /var/git 
mkdir (ProjectName).git

#Gitレポジトリを初期化する
上記作成ディレクトリに移動し、下記Git初期化コマンドを実行する。
予め、Gitコマンド関連をインストールする必要がある。
例）
git --bare init --share

#レポジトリを作成する
Xcode または SourceTreeから、ローカルレポジトリを作成する。

#SourceTree の設定から、上記で作成したレポジトリパスを設定する
上記ローカルレポジトリを指定して SourceTree を起動し、
「設定」-「リモート」-「追加」を選択し、「リモートの名前」と「URL/パス」を設定する。

例１） リモートの場合
origin 
ssh://(Git-User)@xxx.xxx.xxx.xxx/var/git/(ProjectName).git 

例２） 自PCの場合
origin 
file://localhost/Users/(UserName)/(TargetPath)/(ProjectName).git

#設定後、コミット＆プッシュする
以降、通常通り使用可能になる。
Angular7 でwebpack configを調整して、ng serveをできるようにする

# はじめに
LCNEM(https://lcnem.cc/) で、CIを導入することになった時、
cryptpモジュールを上手くバンドルできず、(https://yu-kimura.jp/2018/09/30/angular6-crypto/)

` ng serve `ができないという問題が生じていました。
それがなんとか解決できたので、その方法をご紹介します。

# 方法

1. 下記を `npm i ` する
` npm i -D @angular-builders/custom-webpack `

2. angular.jsonを変更する。
* serveのbuildのところを ` @angular-builders/dev-server:generic ` に上書きする。

```typescript

 "serve": {
          "builder": "@angular-builders/dev-server:generic",
          "options": {
            "browserTarget": "app-project-name:build"
          },
```

以上！


# 参考記事

https://codeburst.io/customizing-angular-cli-6-build-an-alternative-to-ng-eject-a48304cd3b21

https://github.com/meltedspark/angular-builders/tree/master/packages/dev-server
claspでpush先を切り替えるコマンドを書いた

# 初めに
[clasp](https://github.com/google/clasp)はGoogleAppsScript(GAS)のプロジェクトを操作するCLIツールです。

GASプロジェクトに入ってるファイルを丸々ローカルに落として着たり、逆にローカルにあるファイルをまとめてGASプロジェクト内にアップロードする作業を自動でやってくれる優れものです（そのほかにもいろいろ機能はありますがここでは説明しません）。

# 何がやりたいか
プロジェクトAには開発中のコードが入っていて、動作確認ができたら本番用のプロジェクトBにプッシュしたい。
でも、claspのコマンドには、プッシュ先を指定してプッシュするコマンドはないようです...

# プッシュ先を切り替えるコマンドを書いた
```bash:push.sh
#!/bin/bash -eu

RUNNING_MODE=$1
cd $(dirname $0)

if [[ $RUNNING_MODE = dev ]]; then
    SCRIPT_ID="xxx_dev_script_id_xxx"
elif [[ $RUNNING_MODE = stg ]]; then
    SCRIPT_ID="xxx_stg_script_id_xxx"
elif [[ $RUNNING_MODE = prd ]]; then
    SCRIPT_ID="xxx_prd_script_id_xxx"
else
    echo 'usage: ./push.sh <dev|stg|prd>'
    exit 1
fi

echo '{"scriptId":"'$SCRIPT_ID'"}' > .clasp.json
echo 'var RUNNING_MODE = '\'$RUNNING_MODE\'\; > RUNNING_MODE.js
clasp push
```

`./push.sh dev` コマンドで、 `xxx_dev_script_id_xxx` の箇所にあらかじめ入力しておいたスクリプトIDのプロジェクトへプッシュされます。 `./push.sh stg` 、`./push.sh prd` に関しても同様です。（開発、ステージング、本番のそれぞれの環境へプッシュ先を切り替える想定です。）

## 動作の仕組み
`clasp clone` したプロジェクトのスクリプトIDは、クローン先直下の `.clasp.json` ファイル内に次のように保存されます。

```json:.clasp.json
{"scriptId":"プロジェクトのスクリプトID"}
```

なので、上に示した `push.sh` の

```bash
echo '{"scriptId":"'$SCRIPT_ID'"}' > .clasp.json
```

の部分で無理やり上書きしています。単純。

claspには、 `.clasp.json` 内のパラメータを上書きするコマンド `clasp setting` があるのでこの部分は、

```bash
clasp setting scriptId $SCRIPT_ID
```

と書くこともできます。ただしこの場合 `.clasp.json` が元から存在し、キーとして `scriptId` を持っている必要があります。

ついでなので、どこにプッシュしたかをスクリプト側で取得するために、

```bash
echo 'var RUNNING_MODE = '\'$RUNNING_MODE\'\; > RUNNING_MODE.js
```

の部分で変数 `RUNNING_MODE` を埋め込んでおきます。

## デモ
```js:demo.js
function checkRunningMode() {
    Logger.log(RUNNING_MODE);
}
```

このコードを作成しdevモードでpushしてみます。

```
$ ./push.sh dev
└─ RUNNING_MODE.js
└─ appsscript.json
└─ demo.js
Pushed 3 files.
```

GASプロジェクトで、`checkRunningMode()` を実行してみます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/126305/ec12451c-9f3a-e1a1-8120-239dfdf07026.png)

# まとめ
claspのプッシュ先を切り替えるコマンドを作成しました。
作成したコードはこちらに置いておきます。
https://github.com/ttk1/clasptool
具体的な値でモナド則を確認する - List編

モナド則に具体的な値をいれて理解を深めてみましょう。
今回はListを使ってみます。

# モナド則

モナド則を確認しておきましょう。

```haskell
return x >>= f == f x
m >>= return == m
(m >>= f) >>= g == m >>= (\x -> f x >>= g)
```

# Listの実装

Listの実装を確認しておきます。

```haskell
return x = [x]

x >>= f = foldr (\y acc -> f y ++ acc) [] x
```

# モナド則に具体的な値をいれてみよう

## `return x >>= f == f x`

### xが1 で fが \a -> [a+1] の場合

#### 左辺

```haskell
return x >>= f

-- xは1である
return 1 >>= f

-- return を適用
[1] >>= f

-- >>= を適用
foldr (\y acc -> f y ++ acc) [] [1]

-- foldrを適用
f 1 ++ []

-- fは \a -> [a+1]
(\a -> [a+1]) 1 ++ []

-- 無名関数を適用
[1+1] ++ []

-- 残りを計算
[2]
```

#### 右辺

```haskell
f x

-- xは1である
f 1

-- fを適用
[1+1]

-- 残りを計算
[2]
```

右辺と左辺が一致した

### xが1 で fが \a -> [a, a+1] の場合

#### 左辺

```haskell
return x >>= f

-- xは1である
return 1 >>= f

-- return を適用
[1] >>= f

-- >>= を適用
foldr (\y acc -> f y ++ acc) [] [1]

-- foldrを適用
(\y acc -> f y ++ acc) 1 []

-- 無名関数を処理
f 1 ++ []

-- fを置き換えて、関数を適用
[1, 1+1] ++ []

-- 残りを計算
[1, 2]
```

#### 右辺

```haskell
f x

-- xは1である
f 1

-- fを適用
[1, 1+1]

-- 残りを計算
[1, 2]
```

右辺と左辺が一致した

## `m >>= return == m`

### `m`が `[]`の場合

### 左辺

```haskell
m >>= return

-- mは[]
[] >>= return

-- >>= を適用
foldr (\y acc -> f y ++ acc) [] []

-- foldrを適用
[]
```

mに一致する。

### `m`が `[1]`の場合

### 左辺

```haskell
m >>= return

-- mは[]
[1] >>= return

-- >>= を適用
foldr (\y acc -> f y ++ acc) [] [1]

-- foldrを適用
(\y acc -> f y ++ acc) 1 []

-- 無名関数を処理
f 1 ++ []

-- fを適用
return 1 ++ []

-- returnを適用
[1] ++ []

-- 残りを処理
[1]
```

mに一致した。

### `m`が `[1, 2]`の場合

### 左辺

```haskell
m >>= return

-- mは[]
[1, 2] >>= return

-- >>= を適用
foldr (\y acc -> f y ++ acc) [] [1, 2]

-- foldrを適用
(\y acc -> f y ++ acc) 1 $ (\y acc -> f y ++ acc) 2 []

-- $より後ろを処理
(\y acc -> f y ++ acc) 1 [2]

-- 無名関数を適用
f 1 ++ [2]

-- fを適用
return [2] ++ [2]

-- returnを適用
[1] ++ [2]

-- 残りを処理
[1, 2]
```

mに一致した。

## (m >>= f) >>= g == m >>= (\x -> f x >>= g)

### `m` が `[1]`, fが `\a -> [a+1]`、 `g`が \b -> [b+2]` の場合

#### 左辺

```haskell
(m >>= f) >>= g

-- 疲れたので一気に処理(心の声)
[1 + 1] >>= g
[2] >>= g
[2+2]
[4]
```

#### 右辺

```haskell
m >>= (\x -> f x >>= g)

foldr (\y acc -> (\x -> f x >>= g) y ++ acc) 1 []
((\x -> f x >>= g) 1) ++ []
(f 1 >>= g) ++ []
([1 + 1] >>= g) ++ []
[2 + 2] ++ []
[4]
```

### `m` が `[1, 2]`, fが `\a -> [a+1]`、 `g`が \b -> [b+2]` の場合

#### 左辺

```haskell
(m >>= f) >>= g
([1, 2] >>= f) >>= g
[2, 3] >>= g
[4, 5]
```

#### 右辺

```haskell
m >>= (\x -> f x >>= g)
foldr (\y acc -> (\x -> f x >>= g) y ++ acc) [1,2] []
([1+1] >>= g) ++ ([2+1] >>= g) ++ []
[2+2] ++ [3+2] ++ []
[4, 5]
```

左辺と右辺が一致した。
雰囲気がだけ察してほしい。

# 関連

https://qiita.com/eielh/items/cc4293ffefb1730f044b
EC2についてまとめてみた

# EC2
EC2はAWSにおける仮想サーバのことです。  
EC2の特徴の1つとして必要な時に必要な台数を起動し、必要がなくなれば解放できることが挙げられます。  
EC2インスタンスはVPCのサブネットないで起動するAZサービスです。  
EC2インスタンスを起動するまでには次のステップが必要です。  
## 初回起動の各ステップ
1. AMIの選択  
AMIはEC2インスタンスの仮想マシンイメージです。  
AMIにはルートボリューム(OSが格納されるボリューム)のテンプレートや、ルートボリューム以外のマッピング情報などが含まれています。  
AMIにはAWSから提供されるWindows Serverや各種Linuxディストリビューションの他、AWS Marketplaceで購入できる各種ソフトウェアのインストール済みイメージを利用することができます。  

1. インスタンスタイプの選択  
インスタンスタイプはEC2インスタンスのマシンスペックを規定する仮想CPUコア数やメモリ容量などの組み合わせです。
1. インスタンスの詳細設定
EC2インスタンスを起動するVPCやサブネットを選択し、必要に応じてIAMロールを割り当てたり、ユーザデータを設定したりします。  
ユーザデータはOSの起動スクリプトのようなものでEC2インスタンスの初回起動時に実行したい処理を設定することができます。  
例えば各種インスタンスにApache Web  Server等のソフトウェアをインストールするといったスクリプトを組めば、ユーザデータを設定した全てのインスタンスに対して初回起動時インストールが可能です。  
1. ストレージの設定
EC2インスタンスに接続するブロックストレージを設定します。  
デフォルトではEC2インスタンスに接続しているストレージデバイスにいはOSがインストールされます。  
ストレージデバイスにはEBSとインスタンスストアの2種類があります。  
EBSはAZ内に作成されるネットワーク接続型のブロックストレージで不揮発性(永続的なデータボリューム)という特徴があります。  
一方、インスタンスストアはEC2インスタンスの物理ホストの内蔵ストレージで揮発性(一時的なデータボリューム)という特徴があり、EC2インスタンスを停止すると保存されていたデータは削除されます。  
※デフォルトではインスタンスを再起動すると物理ホストが変わるため、インスタンスストアは揮発性となる  
1. タグ付け
タグは、キーと値のペアで構成され、例えば「Name」キーに「WebServer」値というタグをインスタンスに設定すると、コマンドで操作する際の絞り込み条件として利用することができます。  
1. セキュリティグループの設定
EC2インスタンスには少なくとも一つのセキュリティグループを適用する必要があり、このステップで新しいセキュリティグループを作成することも既存のセキュリティグループを設定することもできます。  
1. キーペアの選択
あらかじめ作成したキーペアを指定します。  
公開鍵はAWS上に保管され、ローカルにダウンロードしてある秘密鍵を利用し、認証を行うことができます。

## プレイスメントグループ
異なるAZ内インスタンス同士のアクセスには多少の遅延が発生します。  
※各AZは自然障害などに対しても冗長性を担保するため、互いに物理的に離れた場所に存在するため  
そこで、ある単一のAZにプレイスメントグループを作成し、その中にEC2インスタンスを作成すると、プレイスメントグループ内のEC2インスタンス間のネットワーク接続を高速化することができます。
※プレイスメントグループではネットワーク高速化のために、インスタンスを単一のAZに配置することとなるため、冗長性は損なわれる
iOSでUnity製アプリを起動したときに、別アプリで再生中の音楽が止まる問題の対策

iOSでUnity製アプリを起動すると、プロジェクト設定によっては別アプリで再生中の音楽が止まる。この問題を応急処置的に対策する。

## 方法

iOSプロジェクト出力結果の`UnityViewControllerBase.mm`のviewWillAppearへ下記を追記する。

```objc
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
[audioSession setCategory:AVAudioSessionCategoryAmbient error:nil];
[audioSession setActive:YES error:nil];
```

## 今後の課題

- プロジェクトを出力するたびに書き直す必要があるので、自動化が必要。
- ネイティブプラグイン化について要調査

## Androidの場合
Androidの場合は、対策しなくても音楽は止まらないため問題ない。

下記の設定によって、止まるようにできる。

1. File > Build Settings を開く
2. Player Settings のOther Settings　にある"Mute Other Audio Sources"　のチェックを入れる

※iOSでは、このチェックをどう設定しようと音楽が止まってしまう。


## 参考
[IOS] [FMOD] MUTE OTHER AUDIO SOURCES IS IGNORED WITH FMOD AND AUDIO>DISABLE UNITY AUDIO=TRUE
https://issuetracker.unity3d.com/issues/ios-fmod-mute-other-audio-sources-is-ignored-with-fmod-and-audio-disable-unity-audio-equals-true

Unityで開発したiOSアプリがサイレントモードでも音がなる時の対処法
https://aozolabo.wordpress.com/2015/01/31/issue-silentmode-on-ios/
AWS SFTP for S3の画面が開かなかった時に、一息ついて落ち着いてから見るページ

#SFTP for S3の画面にログインできない
###SFTP for S3の画面を開こうと試みる。

admin権限では問題無く画面が表示されるが、
###一般ユーザーでエラーとなる。
![unable.png](https://qiita-image-store.s3.amazonaws.com/0/276897/4b2b7fc4-1c7c-8fbf-cc8d-3db29898da4b.png)

```
Unable to load content
Something went wrong, you may not have permissions to access these resources. Refresh to try again
```
AWS管理の既存ポリシーにSFTP for S3のログイン権限が用意されていないので
###自分で作成する必要がある。

###IAMの管理画面でポリシーの作成をクリック
![image.png](https://qiita-image-store.s3.amazonaws.com/0/276897/cd645ff9-66c3-1a3a-897f-6b25ce1ea52c.png)

###サービスの選択を押下する
![image.png](https://qiita-image-store.s3.amazonaws.com/0/276897/d45f355d-fb25-e170-1390-70971fec2061.png)

###Transfer　と入力する。下部に出現するTransferを選択する。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/276897/1ba33c82-4ab5-fc14-b1f1-cfa6d46154b9.png)

###すべてのTransfer アクション　にチェックを入れる
![image.png](https://qiita-image-store.s3.amazonaws.com/0/276897/5e510312-b3e3-7e30-00cb-b407453c27e5.png)

###右側のチェックボックを２つ選択する
![image.png](https://qiita-image-store.s3.amazonaws.com/0/276897/97c20de8-6eac-1ca0-0e87-f5cf37ef9438.png)

###ポリシーの確認　を押下する
![image.png](https://qiita-image-store.s3.amazonaws.com/0/276897/264b85f0-98e1-cb1c-4336-31688e340e27.png)

###名前と説明を入力し、ポリシーの作成を押下する
![image.png](https://qiita-image-store.s3.amazonaws.com/0/276897/6c54ab2c-feb0-e01d-7af2-109547541802.png)

###IAMの画面より、アクセス権限の追加を選択する
![image.png](https://qiita-image-store.s3.amazonaws.com/0/276897/de4f33fa-3e39-804f-78de-8028c7b1c323.png)

###作成したポリシーをアタッチすればOK
![image.png](https://qiita-image-store.s3.amazonaws.com/0/276897/d2fc1627-fc7a-2f02-0745-13ecc1f5023e.png)

##おめでとうございます
![image.png](https://qiita-image-store.s3.amazonaws.com/0/276897/3052cc0b-fc26-bffb-e3c6-3e7ef2580eb6.png)

AWS管理の既存ポリシーにSFTP for S3のログイン権限が用意されていないので、
コーポレートレベルのAWS管理者など、困っている方も多いのではないだろうか。

SFTP for S3を使用する予定のある方は頭の片隅に置いておく or ストックしておくと
いざという時のトラブルシュートの時間短縮になることだろう。


ありがとうございました。
少しでもお役にたちましたら「いいね」をよろしくお願いします。
Golangにおいて正規表現で記号が含まれているのを省く

アルファベット+数字＋アンダーバーのみが含まれているかどうかを確認したいと思った。
なかなかコードが見つからず困る。

ぐぐりまくったら、StackOverFlowから下記の方法を発見した。

```go
regexp.MustCompile("正規表現").MatchString("文字列") //boolで返却
```

```golang
package main

import "log"
import "regexp"

var ValueCheck = regexp.MustCompile("^[0-9a-zA-Z_]+$").MatchString

func main() {
        var table = "Adas;lmf___/.d,"
    	if !ValueCheck(table) {
		log.Panicln("table name must be alphanumeric + _ .")
	}
}
```

#Ref
https://stackoverflow.com/questions/38554353/how-to-check-if-a-string-only-contains-alphabetic-characters-in-go
Raspberry PiでPythonからpicameraする


##モチベーション
[Raspberry Pi 導入（カメラ付き）] (https://qiita.com/atmaru/items/ea42aa7eaf35a09b5df9)にて、コマンドラインからカメラ画像を保存することできたので、次はプログラムから実行する。

##環境
RaspberryPi3B+
Raspbian 9.8
Python 2.7.13/Python 3.5.3
picamera 1.13

##準備：Pythonの確認
RaspbianにはPythonは何がはいっ
ているんだろう？

```
i@raspberrypi:~ $ python
Python 2.7.13 (default, Sep 26 2018, 18:42:22) 
[GCC 6.3.0 20170516] on linux2
```
2.7.13が入ってました。

```
pi@raspberrypi:~ $ python3
Python 3.5.3 (default, Sep 27 2018, 17:25:39) 
[GCC 6.3.0 20170516] on linux
```
3.5.3も入ってました。

/usr/binの下でpythonでgrepしたら

```
pi@raspberrypi:/usr/bin $ ls | grep python
〜略〜
python
python-config
python2
python2-config
python2.7
python2.7-config
python3
python3-config
python3.5
python3.5-config
python3.5m
python3.5m-config
python3m
python3m-config
```
一見すると2.7と3.5以外も入っているように見えるますが、どちらかにlinkされています。

##pythonから画像取得
日付＋時刻.jpgとして画像取得するコードは以下の通り。

```picture.py
import time
import picamera
from datetime import datetime
 
with picamera.PiCamera() as camera:
 camera.resolution = (1024, 768)
 camera.start_preview()
 time.sleep(2)
 timestr = datetime.now().strftime('%Y%m%d%H%M%S')
 camera.capture(timestr+'.jpg')
```

python2,3どちらでも動きました。

```
$ python2 picture.py
$ python3 picture.py
```
プレビュー画面表示の camera.start_preview()
カメラをヲームアップさせるための time.sleep(2)
は削除しても画像取得できました。
Kubernetes(k3s)の演習環境をVagrantで構築してみた

## はじめに
先日リリースされたk3sは、リソースが少ないIoT機器などを想定して作られているようですが、、、
リソースが貧弱な機器の代表として、企業SEのPCというものがあります。（個人的な印象です）
k3sはメモリ1GBのVMさえ立てられれば動くため、Kubernetesの勉強用途として最適まであるぐらい気に入って使用しています。


## 導入

VagrantとGitが入っている環境なら、以下で起動します。

```
git clone https://github.com/k8sinfo/k3s-vagrant.git && cd k3s-vagrant && vagrant up && vagrant ssh master
```

上記でMaster x 1、Node x 2 のクラスターが作られます。

リソースが足りない方向けに最小構成を用意しています。こちらはメモリ1GBのVMが1つだけ立ち上がります。

```
git clone -b minimal https://github.com/k8sinfo/k3s-vagrant.git && cd k3s-vagrant && vagrant up && vagrant ssh
```

## Gitが入っていないがVagrantは入っている場合

以下よりリポジトリをzipでダウンロード、展開します。必要に応じてブランチを選択して下さい。
https://github.com/k8sinfo/k3s-vagrant.git

その後、`cd k3s-vagrant && vagrant up && vagrant ssh`でOKです。

## Vagrantが入っていない場合
この機会にインストールしましょう・・・と言いたいところですが、企業のPCだとセキュリティーの関係で余計なものを入れられなかったりする場合もあると思います。
また、Vagrantではなくクラウド環境上で実行したい場合もあると思います。
その場合はLinuxのVMを用意して、以下を実行しましょう。

```
curl -sfL https://get.k3s.io | sh -
```

## アンインストール
VMをそのまま削除しましょう。

```
vagrant destroy
```

## その他
k3sについては以下、
https://hamlog.site/2019/02/28/k3s-installation/

k3sのvagrantでの導入については以下にまとめています。
https://hamlog.site/2019/03/04/k3s-vagrant-up/
tblsでコメントをSQLとは別に管理する

[tbls](https://github.com/k1LoW/tbls) を使ってDBドキュメントを生成するときに、テーブルやカラムのコメントだけ別で管理する方法を紹介します。

## 動作環境

* tbls 1.8.3
* PostgreSQL 11.2
* macOS Mojave 10.14.3

## 背景

tbls を使ってデータベース定義のドキュメントを自動生成していますが、次の理由からSQLのCOMMENT文とは別にコメントを管理したいというニーズがありました。

* DBマイグレーションツールを使っているため、テーブル定義を変えるたびに新しいSQLファイルが追加されることになるが、COMMENT文を使う方法だと最新のコメントがあちこちに分散する
* DBマイグレーションツールがSQLファイルのチェックサムを管理[^1] しており、コメントにtypoがあっただけの場合でも（元のSQLファイルではなく）別のSQLファイルを追加しなければならず煩雑になる

[^1]: ここでは、DBマイグレーションツールとして Flyway を想定しています。チェックサムの検証を無効化するオプションもありますが、意図しない修正を検出するために外したくないという事情があります。

## 解決方法

tbls にはリレーションやコメントに関する追加情報を与える機能があるので、これを使えばコメントだけを別で管理することができます。

* [Configration - Comments](https://github.com/k1LoW/tbls#comments)
* [Configration - Relations](https://github.com/k1LoW/tbls#relations)

例えば、次のような定義のテーブルがデータベース上に存在しているとして、

```sql
CREATE TABLE book (
  id           serial PRIMARY KEY,
  title        varchar(100) NOT NULL,
  author       varchar(100) NOT NULL,
  published_on date NOT NULL
);
```

マイグレーションSQLとは別に、以下のようなYAMLファイルにテーブルやカラムのドキュメントコメントを記載してあげます。

```yaml:db.yml
comments:
  -
    table: book
    tableComment: 書籍を管理するテーブル
    columnComments:
      title: タイトル
      author: 作者
      published_on: 出版日
```

あとは、tblsを実行するときに `--config` オプションでこのファイルを指定するだけです。

```sh
$ tbls doc --config db.yml \
    postgres://user:pass@localhost:5432/testdb?sslmode=disable ./dbdoc
```

生成されたドキュメントを確認すると、コメントが埋め込まれていることが分かると思います。

```md:./dbdoc/book.md
# book

## Description

書籍を管理するテーブル

## Columns

| Name | Type | Default | Nullable | Children | Parents | Comment |
| ---- | ---- | ------- | -------- | -------- | ------- | ------- |
| id | integer | nextval('book_id_seq'::regclass) | false |  |  |  |
| title | varchar(100) |  | false |  |  | タイトル |
| author | varchar(100) |  | false |  |  | 作者 |
| published_on | date |  | false |  |  | 出版日 |
...
```

## 補足

* tbls 1.6.0 より前は、 `--config` オプションではなく `--add` オプションでしたが、今後は `--config` が推奨です
* 必ずしもすべてのテーブル＆カラムにコメントを付ける必要はなく、コメントを付けたいものだけを記載すればOKです

## 参考情報

* [tbls is a CI-Friendly tool for document a database, written in Go.](https://github.com/k1LoW/tbls)
* [tblsでデータベースドキュメントを生成する（2.テーブル情報補完編）](https://qiita.com/k1LoW/items/92d1be1437a259827338)
HinemosのSOAP APIをpythonでつつくサンプル

お金を出せば[Hinemosコマンドラインツール](https://www.hinemos.info/option/commandlinetool)が使えるけれど、貧乏だからそんな金がない。
そんなあなたのためにPythonでHinemos SOAP APIをつつくサンプルコードを晒す。

# サンプルコード

まぁ、特に説明しなくてもコード見りゃわかるっしょ。
pathlib、requests、zeepはpipってください。

```python
#!/usr/bin/python
# -*- coding:utf-8 -*-

import pathlib
from zeep import Client
from zeep.transports import Transport
from requests.auth import HTTPBasicAuth
from requests import Session

# 認証情報
username = "hinemos";
password = "hinemos";

# WSDLはこの辺から取得　http://hinemos_manager:8080/HinemosWS/RepositoryEndpoint?wsdl
# 使えるSOAPメソッドの確認はCLIで下記のように行う。
# python -mzeep wsdl.xml 
WSDL_path = pathlib.Path.cwd().joinpath('wsdl.xml')

# SOAPクライアント作成
session = Session()
session.auth = HTTPBasicAuth(username, password)
client = Client(str(WSDL_path), transport=Transport(session=session))

# テスト
response = client.service.getNodeListAll()
print(response)
```


# 参考リンク

* http://thinkami.hatenablog.com/entry/2018/11/02/230458
* https://stackoverflow.com/questions/40173217/python-soap-client-with-zeep-authentication
paizaでのPython3学習記録。感情を伴うと記憶に定着しやすいそうなので、余計なことが今日もたくさん。

##以下、学習内容をVBAと比較していきます
あまり、引きずられて考えるのは良くないと思いつつも、今のベースは完全にVBAのため、VBAで言うところの何、という書き方をします。

##関数を作成する
###基本の記述

```.py
def 関数名():
    処理
```

VBAで言うところのSubもしくはFunction
definition
慣習として大文字から始めないというのは衝撃。
普段、大文字から初めて単語切り替えの度に大文字にしていた……。
EndProcessing　みたいな感じ。
変数名と区別しやすいようにしてたんだけどダメか……。
ああでも、必ず()が付くなら区別しやすいか。

###引数の記述方法

```.py
def 関数名(a):
    処理
```
超絶シンプル……。
変数の宣言要らないんだよなというのを改めて思い知らされる。
あとこれって参照渡し？　値渡し？？
→値渡しのようです。
関数内で代入前に　`global 変数名` と記述してあげると、スコープ外の変数にも代入できる。
ただし、使用は非推奨。

###戻り値の指定方法

```.py
def 関数名(a):
    処理
    return 戻り値
```
戻り値の使用方法はVBAと同じだけど、戻り値の戻し方？　は別。

##Pythonの四則演算
以下のサイトを参考にしました。
https://note.nkmk.me/python-arithmetic-operator/

べき乗って^じゃないんかーい！
道理で先日問題解く際に答えが合わなかったよね……。
急いでいたからそのものを掛けてしまった。
XCUIElementの取得が失敗したことをわかりやすくする

今日PageObjectPatternでUITestを書いていたんだけど、
下のような感じで要素の取得の部分はPageObject内に隠蔽して書かれていた。

```swift
var loginButton: XCUIElement { return view.buttons["LoginButton"] }

func tapLoginButton() -> LoginPage {
    loginButton.tap()
    return LoginPage(app: app)
}
```

この書き方だと、要素の取得に失敗したことが原因でUITestが失敗した場合に少しわかりにくい。
要素取れるかどうかのテストをわざわざ書きたく無いので、次のようなvalidateメソッドを作った。

```swift
import XCTest

extension XCUIElement {
    func validate(file: StaticString = #file, line: UInt = #line) -> XCUIElement {
        if !self.exists {
            XCTFail(file: file, line: line)
        }
        return self
    }
}
```

要素取得するたびに存在チェックするようにしておくと、もし取得できなかった場合どこで失敗したかすぐわかるのでチョット便利。

```swift
var loginButton: XCUIElement { return view.buttons["LoginButton"].validate() }
 ```
Laravel lolipop上でプロジェクト公開(備忘録)

#環境/前提
mac 10.6.8
lolipopスタンダードプラン
composerをlolipopにinstall済
データベースを使えるようにしておく
設定でsshを有効にしておく

#やること
・ターミナルでサーバーに接続

    $ssh [アカウント名]@[サーバー名] -p[ポート番号]
その後パスワードを入力。(コピペ可)

・ディレクトリ移動

    $cd web

・Laravelをinstallする

    $/usr/local/php7.1/bin/php composer.phar create-project --prefer-dist laravel/laravel プロジェクト名 "5.5"

・ブラウザでロリポップFTPにアクセスし、プロジェクト直下の.envをダウンロードする。

・.envを下記のように編集し、ロリポップFTPにて同じ場所にアップロードする。

DB_CONNECTION=mysql
DB_HOST=[サーバー名]
DB_PORT=3306
DB_DATABASE=[データベース名]
DB_USERNAME=[データベースユーザー名]
DB_PASSWORD=[接続パスワード]
CloudWatchについてまとめてみた

# CloudWatch
CloudWatchはAWSが提供するモニタリングサービスであり、結果に応じて様々なアクションを発生させることが可能です。  
CloudWatchにはメトリクスという概念が存在し「監視項目」という意味で使用されます。  
## メトリクス
CloudWatchでは各種AWSリソースから送られてきたモニタリングデータを保存し、メトリクス毎にグラフ化して表示することができます。  
下記のようなメトリクスは、デフォルトでCloudWatchにデータが送られてきて集計が行われます

* EC2インスタンスのCPU利用率  
* S3の格納オブジェクト総数  
* RDSインスタンスのメモリ空き容量  
※モニタリングデータの保持期間は2週間で、それ以降のデータは破棄されます  

## EC2のモニタリング
モニタリングを実施するにはEC2インスタンスやRDSインスタンスなどの各種AWSリソースからデータを送る仕組みや機能を用意する必要があります。  
RDSはマネージドサービスであるため、デフォルトで様々なモニタリングデータを収集しデータを送る機能が導入されていますが、  
EC2はマネージドサービスではないため、デフォルトではハイパーバイザが収集できるモニタリングデータのみを収集してデータを送っています。  
マネージドサービスではないEC2のメトリクスは次の2種類に大別することができます。

* 標準メトリクス  
ハイパーバイザが取得してCloudWatchに送信するメトリクス  

* カスタムメトリクス  
OSにインストールしたエージェントが取得してCloudWatchに送信するメトリクス  

EC2の標準メトリクスは下記12種類です。  

* CPUクレジット利用数(CPUCreditUsage)  
* CPUクレジット累積数(CPUCreditBalance)  
* CPUクレジット利用数(CPUUtilization)  
* 1秒あたりのDisk読み込み回数(DiskReadOps)  
* 1秒あたりのDisk書き込み回数(DiskWriteOps)  
* インスタンスストレージの読み取りバイト数(DiskReadBytes)  
* インスタンスストレージの書き込みバイト数(DiskWriteBytes)  
* 受信したバイト数(NetworkIn)  
* 送信したバイト数(NetworkOut)  
* OS/インフラストラクチャステータスチェックの成功(0)/失敗(1)(StatusCheckFailed)  
* OSステータスチェックの成功(0)/失敗(1)(StatusCheckFailed_Instance)  
* インフラストラクチャステータスチェックの成功(0)/失敗(1)(StatusCheckFailed_System)  
メモリの利用率など上記以外にも取得したいメトリクスがある場合にはカスタムメトリクスとして取得する必要があります。  

## アラーム
各メトリクスに対してはアラームを設定することができます。  
アラームとは事前に設定しておいた閾値を超えたときに所定のアクションを呼び出す状態繊維のことです。  
例えばEC2インスタンスのCPU使用率が70%という閾値を超えたらアラーム状態に遷移するという使い方ができます。  
そのアラームを受けてメール通知やインスタンスの停止などのアクションを呼び出すことができます。  
アラームには「OK」「アラーム」「不足」という3つの状態があります。

* OK  
閾値を超えていない正常な状態。  

* アラーム  
閾値を超えた状態。  

* 不足  
メトリクスが不足している状態。送信されているデータが不十分な状態。  
「EC2インスタンスのCPU利用率が3分間80%を超えていたらインスタンスを1台追加する」等のアラーム状態が一定期間継続しているようであれば、所定のアクションを実行するといった利用方法があります。
Office2010（Powerpoint2010）でmp4動画を再生

今どきPowerpoint2010のTIPSなんて… と思うけど書く。

Office365で作成したmp4入りスライドショーをPowerpoint2010で開かないといけないことがあった。そのままだと、シークバーに「メディアを再生できません」表示されて動画が再生できない。

Office2010のPowerpointでは動画フォーマットはWMVが推奨のようだ。
[PowerPointでサポートされているオーディオファイル形式およびビデオファイル形式](https://support.office.com/ja-jp/article/powerpoint-%E3%81%A7%E3%82%B5%E3%83%9D%E3%83%BC%E3%83%88%E3%81%95%E3%82%8C%E3%81%A6%E3%81%84%E3%82%8B%E3%82%AA%E3%83%BC%E3%83%87%E3%82%A3%E3%82%AA-%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E5%BD%A2%E5%BC%8F%E3%81%8A%E3%82%88%E3%81%B3%E3%83%93%E3%83%87%E3%82%AA-%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E5%BD%A2%E5%BC%8F-d8b12450-26db-4c7b-a5c1-593d3418fb59)

しかし、Office2010のPCにQuicktimeをインストールすればmp4も再生できるようになる。Quicktimeは[ここ](https://support.apple.com/kb/DL837?locale=ja_JP&viewlocale=ja_JP)からインストールできる。Windows10でも対応しているみたい。
数検１級 (2) 確率統計

# 問題

$0$ 以上の整数値を取る確率変数 $X$ が、下の確率分布に従うとき、次の問に答えなさい。

$$
\mathrm{P}(X=k) = \frac{3}{8}\cdot \left(\frac{5}{8}\right)^k \;\;\;\;\text{（$k$ は$0$以上の整数）}
$$

① $X$ の平均値を求めなさい。
② $X$ の分散を求めなさい。

<details><summary></summary><div>

# 知識

## 平均・分散

平均、分散をそれぞれ $\mathrm{E}(X)$, $\mathrm{Var}(X)$ として、

$$\mathrm{E}(X) = \sum_{k} k \cdot \mathrm{P}(X=k)$$
$$\mathrm{Var}(X) = \sum_{k} \left[ k - \mathrm{E}(X) \right]^2 \cdot \mathrm{P}(X=k)$$

分散は次の公式でも計算できる（大抵こちらのほうが簡単）。

$$\mathrm{Var}(X) = \mathrm{E} (X^2) - \left[ \mathrm{E}(X) \right]^2$$


## 幾何分布（Geometric Distribution）

確率変数 $X$ は、「確率 $p=\frac{5}{8}$ で成功する試行を繰り返すとき、はじめて失敗するまでの連続成功回数」と解釈できる。


# 解答

$p=\frac{5}{8}$ とする。

## ① 平均

幾何分布の構造を知っていれば、次のように考えることができる：

* 確率 $(p-1)$ でいきなり失敗（$X=0$）
* 確率 $p$ で、最初の試行に成功し、以下繰り返す

したがって、平均値 $\mathrm{E}(X)$ は次の関係を満たす。

$$ \mathrm{E}(X) = (1-p) \cdot 0 + p \left[1 + \mathrm{E}(X) \right] $$

これを解いて、

$$\mathrm{E}(X) = \frac{p}{1-p} = \frac{5}{3}.$$

という解法は、後から思いついた。計算ミスのリスクも小さく有効だと思う。ただしこの方法は、幾何分布だと思ってないと思いつきにくいし、分散の計算には使えない。数式のみから計算する方法として、下記がある。

定義より、
$$\mathrm{E}(X) = \sum_{k=0}^{\infty} k (1-p) p^k$$

無限和の計算なので、礼儀正しい解法は、まず $0$ から $n$ までの和を求めて、$n \to \infty$ の極限を計算することになるが、やや計算が煩雑になる。有限の値に収束することを前提にして、再帰的な構造を利用するのが簡単。

```math
\begin{align}
\mathrm{E}(X) 
&= \sum_{k=0}^{\infty} k (1-p) p^k \\
&= 0 + \sum_{k=1}^{\infty} k (1-p) p^k \\
&= \sum_{m=0}^{\infty} (m+1) (1-p) p^{m+1} \;\;\;\text{（$m\equiv k-1$）}\\
&= p \left\{ \sum_{m=0}^{\infty} m (1-p) p^m + \sum_{m=0}^{\infty} (1-p) p^m \right\} \\
&= p\cdot \mathrm{E}(X) + p \;\;\;\;\;\;\;\;\;\;\;\text{∵ $\sum_{m=0}^{\infty} (1-p) p^m = 1$}
\end{align}
```

したがって、

$$\mathrm{E}(X) = \frac{p}{1-p} = \frac{5}{3}.$$

## ② 分散

同じく、無限和の収束を前提に、再帰構造を利用する。

```math
\begin{align}
\mathrm{E}(X^2) 
&= \sum_{k=0}^{\infty} k^2 (1-p) p^k \\
&= 0 + \sum_{k=1}^{\infty} k^2 (1-p) p^k \\
&= \sum_{m=0}^{\infty} (m+1)^2 (1-p) p^{m+1} \;\;\;\text{（$m\equiv k-1$）}\\
&= p \left\{ \sum_{m=0}^{\infty} m^2 (1-p) p^m + \sum_{m=0}^{\infty} 2m (1-p) p^m + \sum_{m=0}^{\infty} (1-p) p^m \right\} \\
&= p \left\{ \mathrm{E}(X^2) + 2\cdot \mathrm{E}(X) + 1 \right\}
\end{align}
```

これを解くと、

$$
\mathrm{E}(X^2) = \frac{2p^2}{(1-p)^2} + \frac{p}{1-p}.
$$

したがって、

```math
\begin{align}
\mathrm{Var}(X) 
&= \mathrm{E}(X^2) - \left[ \mathrm{E}(X) \right ] ^2 \\
&= \frac{p^2}{(1-p)^2} + \frac{p}{1-p} \\
&= \frac{25}{9} + \frac{5}{3} \\
&= \frac{40}{9}.
\end{align}
```

# 感想

* 再帰的な構造に気づいて「最初とそれ以降」に分解するのがポイントか。
* $n$ までの和 $S_n$ を求めて $n \to \infty$ という方法は、この場合は計算ミスが怖い。

</div></details>
Ubuntu 18.04 LTS に WordPress 5.3 をインストール

Ubuntu にLAMP(Linux/Apache/MariaDB/PHP)環境のインストールを行う手順を以下に記します。


# タイムゾーンの変更

```
sudo su - 
timedatectl set-timezone Asia/Tokyo
```

# Apache のインストール

```
apt -y update
apt -y install apache2 
systemctl enable apache2
systemctl start apache2
```

webブラウザで Ubuntu 18.04 サーバのIPアドレス(```curl ifconfig.me```で確認できます)にアクセスして Apache のテストページが表示されるか確認します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/122952/366e8ac2-da22-72e6-6087-63275cd70ab7.png)

# PHP 7.2 のインストール

```
apt -y install php7.2 php7.2-mysql
```

# MariaDB 10.1 のインストール

```
apt -y install mariadb-server mariadb-client
systemctl enable mariadb
systemctl start mariadb
```

2019-09-24 時点では MariaDB 10.1.41 がインストールされました。

```sql

$ sudo mysql
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 97
Server version: 10.1.43-MariaDB-0ubuntu0.18.04.1 Ubuntu 18.04

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MariaDB [(none)]>
```

# WordPress のインストール

## WordPress ダウンロード/展開

現時点での[WordPress最新版](https://ja.wordpress.org/latest-ja.tar.gz)をダウンロードし、DocumentRoot配下に展開します。

```
cd /var/www/html
wget https://ja.wordpress.org/latest-ja.tar.gz
tar xvf latest-ja.tar.gz
chown -R www-data:www-data .
```

## WordPress 用データベース作成

文字コードUTF8でデータベース wordpress を作成します。

```sql

$ sudo mariadb
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 99
Server version: 10.1.43-MariaDB-0ubuntu0.18.04.1 Ubuntu 18.04

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MariaDB [(none)]> CREATE DATABASE wordpress DEFAULT CHARACTER SET utf8;
Query OK, 1 row affected (0.00 sec)
```

## WordPress 用 MariaDB ユーザの作成

さきほど作成したデータベースのみ全権限を持つ wordpress ユーザを作成します(ユーザ名/パスワードは適宜変更してください)。

```sql
GRANT ALL ON wordpress.* TO wordpress@localhost IDENTIFIED BY 'password';
FLUSH PRIVILEGES;
```

## WordPress 初期設定

webブラウザで xx.xx.xx.xx/wordpress にアクセスし、WordPressの初期設定を行います。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/122952/e2a57068-9ca9-de5a-9db6-273de5a97770.png)


![image.png](https://qiita-image-store.s3.amazonaws.com/0/122952/f44caded-dc3b-7ee1-199f-b4fab2d5ba9e.png)

正常にインストールが完了すると、指定したユーザ、パスワードでダッシュボードにログインできます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/122952/22f62119-d7eb-3c23-f5e6-e5c83dc207a5.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/122952/97b18877-34cd-470b-f586-9b4400ff4ff0.png)
sitemap.xmlにあるURLパスを列挙・巡回するCLIツール

## TL;DR
- [sitemapaths](https://github.com/hareku/sitemapaths)を使えばsitemap.xmlのURLパスを列挙できるよ
- `sitemapaths`を使ってワンラインでCloudFrontのキャッシュを消せるよ
- [sitemapbot](https://github.com/hareku/sitemapbot)を使えばsitemap.xmlのURLパス全てにHTTPリクエストできるよ

## sitemapathsのシンプルな使い方

https://github.com/hareku/sitemapaths
`sitemapaths`は、sitemap.xmlからURLパスを列挙するCLIツールであり、以下のように使える。

```bash
$ sitemapaths https://example.com/sitemap.xml
/
/first-post
/second-post
```

### sitemapathsでCloudFrontのキャッシュを削除
`sitemapaths`を使って、sitemap.xmlに記載されたURLパス全てをCloudFrontのキャッシュから削除できる。

```bash
# CloudFrontのディストリビューションIDを設定
$ CF_DISTRIBUTION_ID=ET5LGZEQ5FWA4

# sitemap.xmlのパスを列挙
$ PATHS=$(sitemapaths https://example.com/sitemap.xml)

# Invalidation（キャッシュの無効化）のリクエストを作成
$ INVALIDATION_REQUEST=$(aws cloudfront create-invalidation --distribution-id $CF_DISTRIBUTION_ID --paths $PATHS)

# InvalidationリクエストのIDを取得
$ INVALIDATION_ID=$(echo $INVALIDATION_REQUEST | jq -r '.Invalidation.Id')

# Invalidationが完了するまで待つ
$ aws cloudfront wait invalidation-completed --distribution-id $CF_DISTRIBUTION_ID --id INVALIDATION_ID
```

これは以下のようにワンラインで書ける。

```bash
$ CF_DISTRIBUTION_ID=ABCDEFG123
$ aws cloudfront wait invalidation-completed --distribution-id $CF_DISTRIBUTION_ID --id $(aws cloudfront create-invalidation --distribution-id $CF_DISTRIBUTION_ID --paths $(sitemapaths https://example.com/sitemap.xml) | jq -r '.Invalidation.Id')
```

## sitemapbotでsitemap.xmlのURLを全て巡回する
https://github.com/hareku/sitemapbot
`sitemapbot`を使えばsitemap.xmlに記載されているURL全てにアクセスできる。
主にキャッシュを再作成する用途として使う。

```bash
$ sitemapbot https://example.com/sitemap.xml
2019/03/03 19:02:41 crawling started: https://example.com/sitemap.xml
 213 / 213 [================================================] 100.00% 11s
2019/03/03 19:02:53 crawling finished: https://example.com/sitemap.xml, success:213, error:0
```
PWA: Workbox Routing 和訳 (その１)

# Workbox Routing

参照元ページ: https://developers.google.com/web/tools/workbox/modules/workbox-routing

## Workboxルーティングとは？

サービスワーカーはページに対するネットワークリクエストをインターセプト、つまり横取りできます。それによってブラウザへ返されるコンテンツは、キャッシュ経由、ネットワーク経由もしくはサービスワーカーが生成したものなど多岐に渡ります。

`workbox-routing`を使うことで、リクエストに対する様々なレスポンスを返すためのルーティングの設定を簡単に行うことができます。

## ルーティングがどのように行われるか

ネットワークへのリクエストがサービスワーカーの`fetch`イベントを発火させる時、`workbox-routing`は与えらたルートとハンドラーを使ってリクエストに対するレスポンスを返します。

![](https://developers.google.com/web/tools/workbox/images/modules/workbox-routing/workbox-routing-diagram.png)


上記の図で大切なことは:

- リクエストメソッドは重要です。デフォルトではルーティングは"GET"メソッドで指定されています。もし他のメソッドをインターセプトする場合は、明示的に渡してあげる必要があります。
- ルーティング登録の順番は大切です。一つにリクエストに対して複数のルーティングが設定されている場合は、一番最初に登録されているものがリクエストに対して有効になります。(補足: 上図で"Was there a matching Route?"で最初にマッチしたものが使用されるということですね）

ルーティングを登録するには、コールバック、正規表現、`Route`インスタンスで行います。

## ルートのマッチングとハンドリング

workboxのルーティングは二つの機能のみから成ります。
- マッチング: ルートがリクエストと一致しているかを決める
- ハンドリング: リクエストに対して`Response`でレスポンスを返す

Workboxはマッチングとハンドリングを行うヘルパー関数を持っていますが、もしそれとは異なる挙動を期待したい時はカスタムのマッチングとハンドリング関数を書くのがベストです。

マッチング関数は[`FetchEvent`](https://developer.mozilla.org/ja/docs/Web/API/FetchEvent)と`URL`オブジェクトを元にリクエストとマッチしているか否かの値を返します。簡単な例ですと、下記のように特定のURLとマッチさせることができます。

```js

const matchCb = ({url, event}) => {
  return (url.pathname === '/special/url');
}
```

ほとんどのユースケースは`url`か`event.request`が`Request`とマッチしているかを調べることでカバーされます。

ハンドラーでは与えられた`url`と`event`を元にどのようにレスポンスを返すかを決めることになります。それはネットワーク経由かキャッシュ経由、サービスワーカー(が生成したコンテンツ)経由かもしれません。

```js

const handlerCb = ({url, event, params}) => {
  return fetch(event.request)
    .then((response) => {
      return response.text()
    })
    .then((responseBody) => {
      return new Response(`${responseBody} 例えばここにコンテンツを追加できます`)
    })
}
```

ハンドラーは`resolve`されたタイミングで`Response`を返す`Promise`をリターンしなくはいけません。`params`の値は`match`関数の戻り値です。

これらのコールバックは次のように記述します。

```js

workbox.routing.registerRoute(matchCb, handelrCb);
```

一つ注意しなくはいけないのは"match"関数は同期的に値を返すため、非同期では使えません。なぜなら、`Router`は`fetch`イベントや`fetch`が失敗した場合のほかの`fetch`イベントに同期的にレスポンスを返す必要があるからです。

通常"handler"関数は`workbox-strategies`で提供されているストラテジーを使います。

```js

workbox.routing.regosterRoute(
  matchCb,
  new workbox.strategies.StaleWhileRevalidate()
);
```

## ルーティングの正規表現の書き方

大抵のケースでは"match"の代わりに正規表現を使います。Workboxによってこの部分を簡単に書くことができます。

```js

workbox.routing.registerRoute(
  new RegExp('/styles/.*\.css'),
  handlerCb
);
```

同一オリジンからのリクエストには、正規表現の適用が緩くなるため上記の書き方は次の３パターンでマッチします。

- https://example.com/styles/main.css
- https://example.com/styles/nested/file.css
- https://example.com/nested/styles/directory.css

しかし、クロスオリジンの場合はURLの先頭から一致する必要があります。想定外にサードパーティのアセットにマッチしてしまうことを防ぐためです。

- https://cdn.third-party-site.com/styles/main.css
- https://cdn.third-party-site.com/styles/nested/file.css
- https://cdn.third-party-site.com/nested/styles/directory.css

そのためクロスオリジンのリクエストに対しては
`new RegExp('https://cdn.third-party-site.com.*/styles/.*\.css')`のようにフルパスで記述します。