OmniAICreator/Qiita-1.07M huggingface dataset:
【忘備録】AmazonLinux2にデスクトップ環境を作成し、GUIで操作する。

## 概要

### 内容
- AmazonLinux2インスタンスにリモートデスクトップでselenium環境を立てる方法

### 利点
- EC2をGUIで操作したい時のseleniumの挙動をGUIで確認したい時に便利

### 環境
- AWS：EC2
- OS：Amazon Linux2


## 作業工程

### EC2インスタンスを作成
- セキュリティグループのインバウンドルールを下記ポートを開ける
    - ポート番号：5901
    - ソース：パブリックにならないように、良きに設定する。

### MATE(デスクトップ環境)のインストール
- Amazon Linux 2 の Extras Library で提供されているデスクトップ環境 MATE をインストール

```
$ sudo amazon-linux-extras install mate-desktop1.x
```

### TigerVNCをインストール
- VNCサーバには、yum でパッケージ提供されている TigerVNC を利用

```
$ sudo yum install tigervnc-server
```

- TigerVNCのユニットファイルを配置し、ディスプレイ番号は「1」に配置

```
$ sudo cp /usr/lib/systemd/system/vncserver@.service /etc/systemd/system/vncserver@:1.service
```

- VNC接続用のパスワードを設定

```
$ vncpasswd

Password: <パスワードを入力>
Verify: <パスワードを入力>
Would you like to enter a view-only password (y/n)? n
A view-only password is not used
```

- TigerVNCの起動ユーザ設定
    - 起動ユーザの設定を ec2-user を変更

```:変更前
 sudo vi /etc/systemd/system/vncserver@:1.service

[Unit]
Description=Remote desktop service (VNC)
After=syslog.target network.target

[Service]
Type=forking

# Clean any existing files in /tmp/.X11-unix environment
ExecStartPre=/bin/sh -c '/usr/bin/vncserver -kill %i > /dev/null 2>&1 || :'
ExecStart=/usr/sbin/runuser -l -c "/usr/bin/vncserver %i"
PIDFile=/home//.vnc/%H%i.pid
ExecStop=/bin/sh -c '/usr/bin/vncserver -kill %i > /dev/null 2>&1 || :'

[Install]
WantedBy=multi-user.target
```

```:変更後
[Unit]
Description=Remote desktop service (VNC)
After=syslog.target network.target

[Service]
Type=forking

# Clean any existing files in /tmp/.X11-unix environment
ExecStartPre=/bin/sh -c '/usr/bin/vncserver -kill %i > /dev/null 2>&1 || :'
ExecStart=/usr/sbin/runuser -l ec2-user -c "/usr/bin/vncserver %i -SecurityTypes=TLSVnc"
PIDFile=/home/ec2-user/.vnc/%H%i.pid
ExecStop=/bin/sh -c '/usr/bin/vncserver -kill %i > /dev/null 2>&1 || :'

[Install]
WantedBy=multi-user.target
```

- 起動時にVNCサーバが立ち上がるようにサービスを登録

```
$ sudo systemctl enable vncserver@:1.service
```

- xstartupファイルを作成し MATE が起動するように設定

```
$ vi ~/.vnc/xstartup
```
```:xstartup
#! /bin/sh
exec mate-session &
```

- 実行権限を追加

```
$ chmod +x ~/.vnc/xstartup
```

- TigerVNCを起動
    -  ステータスを確認し、問題なく立ち上がっていることを確認

```
$ sudo systemctl start vncserver@:1.service

$ sudo systemctl status vncserver@:1.service
```

### TigerVNCで、デスクトップに接続

- クライアント側で、TigerVNCのソフトウェアをインストール
    - https://github.com/TigerVNC/tigervnc/releases

- VNCServerに下記を入力して接続
    - VNC Server：{IPv4 パブリック IP}:5901
    - パスワード：vncpasswdで設定したパスワード

- デスクトップが立ち上がっている事を確認したら終了。
![スクリーンショット 2020-09-06 16.19.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/692922/630dcd8d-13fa-9065-5e86-dd0d9814467e.png)
laravelのユーザー登録画面でのバリデーション

バリデーションを編集する方法です:point_up_tone2:

以下の階層から`RegisterController`へ入ってください。

```
.
└──laravel
    └── app
        └── Http
            └── Controllers
                └── Auth
                   └── RegisterController.php
```

```

<?php
// 略

class RegisterController extends Controller
{
    // 略
    protected function validator(array $data)
    {
        return Validator::make($data, [
            'name' => ['required', 'string', 'max:255'],
            'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
            'password' => ['required', 'string', 'min:8', 'confirmed'],
        ]);
    }
    // 略
}
```

validatorメソッドの中身を見てみましょう。

name(ユーザー名)は、必須(required)、文字列(string)、最大255文字まで(max:255)といった定義となっています。

email(メールアドレス)は、メールアドレスの形式であること(email)、usersテーブルの他のメールアドレスと被らないこと(unique:users)といった定義も行なっています。

passwordは、最低8文字以上(min:8)、自分の項目名_confirmedという別の項目(つまり、password_confirmedという項目)と同じ値であること(confirmed)といった定義も行なっています。

以下のように編集してみましょう。

```
<?php
// 略
class RegisterController extends Controller
{
    // 略    
    protected function validator(array $data)
    {
        return Validator::make($data, [
            'name' => ['required', 'string', 'alpha_num', 'min:3', 'max:16', 'unique:users'], //-- この行を変更
            'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
            'password' => ['required', 'string', 'min:8', 'confirmed'],
        ]);
    }
    // 略
}
```

追加されたバリデーションルールについて解説します。

`alpha_num`は、英数字であるかをチェックします。

[alpha_num - Laravel公式](https://readouble.com/laravel/6.x/ja/validation.html#rule-alpha-num)

min:3は、3文字以上であるかをチェックします。

max:255については、max:16に変更しました。


unique:usersは、usersテーブルの他のレコードのnameカラムに、(ユーザー登録画面から)リクエストされたnameと同じ値が無いことをチェックします。

以上で編集完了です:relaxed:
【Ruby】ハッシュの中にハッシュ（２重ハッシュ）

プログラミング学習中で、これはメモしたいと思ったものを、自分用に分かりやすく残しておきます。

## そもそもハッシュとは
ハッシュというのは「データ」とそれに対応する「名前」のセットを要素として持つ値のことを言います。
コードで記述すると以下のようなものを指します。

```ruby
vegetables = {"avocado": "300円", "broccoli": "250円", "edamame": "200円"}
```

この記述でいきますと、

- アボガドは、300円
- ブロッコリーは、250円
- 枝豆は、200円

というように、セットになっております。
そして、ハッシュにおいては、データを**バリュー**、それに対応する名前を**キー**と呼びます。

## 2重ハッシュとは
バリューに当たる部分もハッシュ状態になっている状態のことです。
状態としては、以下のようなものを指します。

```ruby
my_favorite_foods = {vegetable: {green: {name: "broccoli"}}}
```

これを分解していきますと、

`vegetable`という**キー**とセットになのが、`green: {name: "broccoli"}`という**バリュー**。
その中の`green`という**キー**とセットなのが、`name: "broccoli"`という**バリュー**。
その中の`name`という**キー**とセットなのが、`"broccoli"`という**バリュー**。

という状態なのが分かります。

## 2重ハッシュの値の取り出し方
値の取り出し方は、２次元配列でのやり方と似ております。

```ruby
my_favorite_foods = {vegetable: {green: {name: "broccoli"}}}
```

こちらのコードで、`"broccoli"`のみを出力したい場合は、以下のように記述します。

```ruby
puts my_favorite_foods[:vegetable][:green][:name]
```


このコードは、
`my_favorite_foods`という**ハッシュ**の、
`:vegetable`という**キー**に当たる**バリュー**の、
`:green`という**キー**に当たる**バリュー**の、
`:name`という**キー**に当たる**バリュー**を出力します。
という意味です。

変数（ここでいうと`my_favorite_foods`の部分）に続けて、キーを横に並べて記述することで、取り出すことが可能になります。

ちなみに、出力結果は以下のようになりました。

```ruby
broccoli
```

## まとめ
ハッシュというのは、キーとバリューがセットになっているものをいう。
2重ハッシュというのは、バリューに当たる部分がさらにハッシュ状態になっているものをいう。
2重ハッシュの深い位置にあるバリューを取り出す際は、キーを順番に横に並べて記述する。
【Unity】往復処理

```
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

// ずっと、往復する
public class Forever_MoveSin : MonoBehaviour
{
	public float speedX = 1; // スピードX：Inspectorで指定
	public float speedY = 0; // スピードY：Inspectorで指定
	public float speedZ = 0; // スピードZ：Inspectorで指定
	public float second = 1; // かかる秒数：Inspectorで指定
	
	float time = 0f;

	private void FixedUpdate()  // ずっと、往復する
	{
		time += Time.deltaTime;
		float s = Mathf.Sin(time * 3.14f / second); // 移動量を求める
		this.transform.Translate(speedX * s / 50, speedY * s / 50, speedZ * s / 50); 
	}
}
```

secondの値を変更すると、その値の秒数間でアタッチされたゲームオブジェクトが往復する。

下記のGifは
インスペクターから
second = 6
に値を変更した例

つまり、6秒間で往復を行う

![ezgif.com-video-to-gif.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/685271/0c849190-25f7-32b6-c35c-f78e1b552431.gif)
[Ruby]　アクセサメッソドについて

## アクセサメソッドとは
インスタンス変数を読み書きするメソッドのこと

~~~ruby
　class User
　　#　引数で受け取った値を@nameに代入するメッソド（セッター）
　  def name=(name)
　    @name = name
　  end

　　# インスタンス変数の内容を参照するメソッド(ゲッター)
   def name
     @name
　 end
 end

> user = User.new
> user.name = "Alice"
=> "Alice"
~~~


attr_accessorメッソドを使えば、セッターやゲッターメッソドを定義する度にこのような機械的なコードを書かなくて済むようになる。


## attr_accessor

~~~ruby
　class User
    #　@nameを読み書きするメッソドが自動的に定義される 
　  attr_accessor :name
　end


> user = User.new
> user.name = "Alice"
=> "Alice"
~~~

また、複数の引数を渡すと、複数のインスタンスに対するアクセサメッソドを作成することができる。


~~~ruby
　class User
　  attr_accessor :name, :age, :address
　end


> user = User.new
> user.name = "Alice"
=> "Alice"
> user.age = 18
=> 18
>user.address = "東京"
=> "東京"
> user
=> #<User:0x00007ff18907ff30 @name="Alice", @age=18, @address="東京">
~~~

また、インスタンス変数の内容を読み取り専用にしたい場合はattr_reader、逆に書き込み専用にしたい場合はattr_writerを使う。

## attr_reader

~~~ruby
　class User
　  attr_reader :name

    def initialize(name)
      @name = name
    end
  end


> user = User.new("Alice")
> user.name
=> "Alice"
# 読み取り専用なので、@nameを変更しようとするとエラーになる
> user.name = "Selka"
NoMethodError (undefined method `name=' for #<User:0x00007fdccb16b958 @name="Alice">)
~~~


## attr_writer

~~~ruby
　class User
　  attr_writer :name

    def initialize(name)
      @name = name
    end
  end

> user = User.new("Alice")
> user.name = "Selka"
=> "Selka"
# 書き込み専用なので、@nameの参照はできない
user.name
NoMethodError (undefined method `name' for #<User:0x00007fe2ee8a8620 @name="Selka">)
~~~
Githubにコミットするとき.swiftファイルが出てこない時

#swiftやってる時にコミットできなくなったんでアウトプットしておきますね（；
初歩的なことですがつまづいてしまったので記事にしました。
具体的なこというとこんな感じ
通常
![スクリーンショット 2020-08-22 13.58.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/679429/1c2dfdeb-3f74-e740-932d-63f651dc0ba7.png)
問題のやつ
![スクリーンショット 2020-08-21 14.58.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/679429/05427478-c359-0c6c-cfac-b61e70fd1aed.png)

#原因
.gitignoreファイルを作ってPodsをコミットしないようにする

#解決策
<h4>.gitignoreを作成した後、Xcodeを一旦閉じて、プロジェクトを開き直してからコミット画面を確認する</h1>

プロジェクトのファイルからターミナルを開いて、

<h4>touch .gitignore

ファイルを作ってその中に

<h4>Pods/*
プロジェクト名.xcworkspace

で、ターミナルに戻って

<h4>pod install


<h5>僕の場合これでも直らなかった。。

Pods配下がコミットされていたのを後から.gitignoreで対象外にしているようで、
リポジトリの中からPods配下を削除できていないよう...


とにかくpodsをいじくれば！！笑

1.プロジェクトフォルダをターミナルで開く
2.Xcodeを閉じる
3.次のコマンドをターミナルに入れ、Pods配下をリポジトリの管理対象から削除する
<h4>   git rm --cached -r Pods

4.次のコマンドをターミナルに入れ、管理対象から削除したファイルをコミット(削除確定)する。
<h4>   git commit -m "delete pods"

5.Xcodeでプロジェクトを開く

これでできました！！

#考察
僕の場合、cocoapods使うのにgitignore作ってなかったからそうなってしまったのかも
結構時間かかってしまったw

見ていただきありがとうございました！
Google Formの回答からGitHubのissueを作る

Google Formの回答からGitHubのissueを立てたい需要があったので、それまでの流れを順番に説明していきます。
とり急ぎコードが必要な方は、最初に設定 -> 一番下のコードをコピペで良いと思います。


# 1. googleフォームからGASを起動する
とりあえずメールを送らせて、フォームからGASが起動したことを確認します

```js
 function onFormSubmit(e) {
   MailApp.sendEmail({
     to: "メール送っても怒られないアドレス",
     subject: "適当な件名",
     body: "適当な本文"
   });
 }
```

GUI上での設定やスクリプトは下記を参考にしました
https://valmore.work/auto-response-from-google-form/

# 2. googleフォームから値を取得する
googleフォームの回答がメールに送られるようにします
e.namedValuesに 質問: 回答 のobjectの形で記録されているので、それを取得してメールで送ります
あとでgithubのissueを作る時にmarkdownを文字列の形式で送る必要があるので、それを見越してマークダウンっぽい形でメールを送るようにしておきます

```js
 function onFormSubmit(e) {
   keys = Object.keys(e.namedValues).sort()
   qa_list = keys.map( key => "### " + key + "\n" + e.namedValues[key] + "\n")
 
   MailApp.sendEmail({
     to: "メール送っても怒られないアドレス",
     subject: "適当な件名",
     body: qa_list.join("\n")
   });
 }
```

eの中身が気になる場合はこちらに公式の説明があります
https://developers.google.com/apps-script/guides/triggers/events

# 3. githubにissueを作る

前提として、アクセストークンを取得しておく必要があります。
生成時にしか中身が見れないようなので、保存しておくことを忘れずに。
https://github.com/settings/tokens

アクセス権限としてはrepoを全て有効にしたところうまくいきました。
その上で、下記を実行します

```js
 function onFormSubmit(e) {
   keys = Object.keys(e.namedValues).sort()
   qa_list = keys.map( key => "### " + key + "\n" + e.namedValues[key] + "\n")
 
   url = "https://api.github.com/repos/オーナー/リポジトリ名/issues"
   token = "GitHubのトークン"
   options = {
     "method": "post",
     "headers" : {
       "Authorization": "token " + token, 
       "Accept": "application/vnd.github.v3+json"
     },
     "payload" : JSON.stringify({
       "title": "title",
       "body": qa_list.join("\n")
     })
   }
   UrlFetchApp.fetch(url, options)  
 }
```

基本的には公式ドキュメント通りにパラメータを設定すればよいです
https://docs.github.com/en/rest/reference/issues#create-an-issue

最初tokenの使いどころが分からず、404が帰ってきていたのですが下記を参考にAuthorizationを書いたら行けました
https://stackoverflow.com/questions/28936959/github-api-create-issues-return-404-not-found

UrlFetchAppのパラメータに関してはgasの方のドキュメントも参考にしてください
https://developers.google.com/apps-script/reference/url-fetch/url-fetch-app
【メモ】hoverで画像の上に文字

検索した１番目に持ってた記事で動かず、
2番目のものもうまくいかなかったので
今後自分がまた使えるように軽まとめ。

--- HTML ---

```
<div class="hover-effect">
 <img src="images/four.png">
 <div class="mask">
   <p class="caption">文字</p>
 </div>
</div>
```

--- CSS ---

```
.hover-effect {
　overflow:　hidden;
　position:　relative;
}
.hover-effect .caption {
　text-align:　left;
　padding: 10px;
  color:　#fff;
  font-size: .85em;
  display: table-cell;
  vertical-align: middle;
}
.hover-effect .mask {
　width:　100%;
　height:　100%;
　position:　absolute;
　top:　0;
　left:　0;
　opacity:　0;
　background-color:　rgba(0, 0, 0, 0.728);
  display: table;
}
.hover-effect:hover .mask {
　opacity:　1;
}
```


maskをhoverで表示させる感じ。
Pythonで特定URLの画像ファイルを取得する

## 概要
機械学習等を行う場合、多数の画像ファイルを取得する必要がある。
その場合、WEBサイトから画像ファイルを取得する必要がある為、
URLの画像ファイルをダウンロードする際に使用するサンプルPGを下記に記載。

## 前提条件
- requests
- python3.7

### サンプルPG
```get_img.py
import requests

# ファイル出力先
OUTPUT_PATH = "D:\\img"

def main():
    url = "https://example.com/sample/sample.jpg"
    get_img(url)


def get_img(url):

    # URLにGET通信でアクセス
    res = requests.get(url)

    # 200以外のステータスの場合は処理終了
    if res.status_code != 200:
        return

    # コンテンツタイプを確認
    content_type = res.headers["content-type"]

    # コンテンツタイプが画像出ない場合処理終了
    if 'image' not in content_type:
        return

    # 画像データを変数に代入
    image = res.content

    # URLからファイル名を取得(URLの末尾を取得)
    f_name = url.split("/")[-1]

    # 画像ファイルを出力
    with open(OUTPUT_PATH + "/" + f_name, "wb") as fout:
        fout.write(image)

if __name__ == "__main__":
    main()
```

### 参考サイト
- [PythonでURLからファイル名を取得してみる](https://alicehimmel.hatenadiary.org/entry/20101121/1290316337)
CentOSでUPSの設定をする

CentOSで２台のコンピュータの電源バックアップをした時の備忘録です。

UPS１台だけでLANでつながれた複数台のコンピュータの自動シャットダウンの設定が可能です。当たり前ですがスイッチングハブもUPSでバックアップする必要があります。

## セットアップ内容
### 接続の設定
- 1台をUPSとコンピュータをUSBでつないでサーバにする。
- もう一台をサーバにつなぐクライアントにする。

### シャットダウン設定
以下の二つの条件いずれかが成り立ったときにシャットダウンします。
- 残り電池残量が60%以下になったとき
- 残り予想駆動時間が３分以下になったとき

## 設定（共通）
### インストール設定
CentOSにインスコします。

```sh
$ sudo yum install http://springdale.math.ias.edu/data/puias/unsupported/7/x86_64//apcupsd-3.14.14-5.sdl7.x86_64.rpm
```

新しい安定バージョンが出たらそっちを使ってください。

### シャットダウンの設定
こんな感じで設定します。
**よく、TIMEOUTを60とかで設定し、停電後６０秒後にシャットダウンをする例がありますが、TIMEOUTはあまり使わないでって書いてあります。**
`TIMEOUT 0`でTIMEOUTを無効化します。
代わりに、`BATTERYLEVEL`で危険バッテリーレベルを書いて、`MINUTES`でシャットダウンにかかりそうな時間を書きます。
いずれかの条件が満たされればシャットダウンが実行されます。

ここでは、シャットダウンには３分あれば大丈夫(`MINUTES`の設定)で、`BATTERYLEVEL`は60%以下になったらシャットダウンするように書きました。

```sh:/etc/apcupsd/apcupsd.confの中
TIMEOUT 0 
BATTERYLEVEL 60
MINUTES 3
```

## 設定（サーバ側)
サーバー側では、ポートの開放をして、接続の許可を行います。
また、ハードウェア設定でusbから取得するようにしてサーバとして機能するようにします。

### ポート開放
解放ポートの確認

```sh
$ sudo firewall-cmd --list-all
```

まだなら、ポート開放

```sh
$ sudo firewall-cmd  --permanent --add-port=3551/tcp
```

### 接続の許可

```sh:/etc/hosts.allow
apcupsd:(ip address to allow)
#
# hosts.allow   This file contains access rules which are used to
#               allow or deny connections to network services that
#               either use the tcp_wrappers library or that have been
#               started through a tcp_wrappers-enabled xinetd.
#
#               See 'man 5 hosts_options' and 'man 5 hosts_access'
#               for information on rule syntax.
#               See 'man tcpd' for information on tcp_wrappers
#
```

### UPSのデバイスを確認
`ls /dev/usb*`等で確認する。（UPSをたくさんつながない限り、すぐにわかると思う）

### UPSの設定ファイル
USB接続をして、NETSERVERを有効化、PORT番号を3551であることを確認します。

```sh:/etc/apcupsd/apcupsd.confの中
UPSCABLE usb
UPSTYPE usb
DEVICE /dev/usb/<your usb device here!!>
NETSERVER on
NISIP 0.0.0.0
NISPORT 3551
```

## クライアント側
```sh:/etc/apcupsd/apcupsd.confの中
UPSCABLE ether
UPSTYPE net
DEVICE 10.4.200.96:3551
```
【Unity】Update()　FixedUpdate()　違い

Update()は、
1秒間に60~80回ほど実行されるが、パソコンのスペックやそのときの処理によって変化してしまう。つまり、時間間隔が一定でない。

FixedUpdate()は、
1秒間に実行される回数が決まっており、デフォルトでは50回。

使う用途としては、

Update()では、移動量を定義する。理由は、FixedUpdate()は1秒間に実行される回数が少なめで、ユーザーからの入力にすぐに反応できないため。

FixedUpdate()では、実際の移動処理を定義する。理由は、Update()は時間間隔が一定でないので、この中に移動処理を行うと滑らかに動かない可能性があるため。
【Go】web サーバーの作成方法が様々あるのでまとめる

<font color="red">※注意: この記事ははあくまで個人学習用に整理しただけの記事で、内容としては不完全なものになります。読んでも参考にならない可能性が高いです。</font>

go を学習していて、Web サーバーの作成方法がいくつもあったので、自分の整理用にまとめる。

### パターンその1

handler というハンドラ関数を定義

- http.HandleFunc を使用
- index という handler 関数を別で定義し、http.HandleFunc の第二引数に関数を渡す
- 最後に http.ListenAndServe 実行

```golang
package main


import (
	"fmt"
	"net/http"
)

func index(writer http.ResponseWriter, request *http.Request) {
	// 省略
}

func main() {
	http.HandleFunc("/", index)
	if err := http.ListenAndServe(":8080", nil); err != nil {
		log.Fatal("ListenAndServe:", err)
	}
}
```

### パターンその2
templateHandler、独自のハンドラを作成

- http.Handle を使用
- 最後に http.ListenAndServe 実行

```golang
package main

import (
	"html/template"
	"net/http"
)

type templateHandler struct {
	once synce.Once
	filename string
	templ *template.Template
}
func (t *templateHander) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	t.once.Do(func() {
		t.templ = template.Must(template.ParseFiles(filepath.Join("templates", t.filename)))
	})
	t.templ.Execute(w, nil)
}

func main() {
	http.Handle("/", &templateHandler{filename: "index.html"})

	if err := http.ListenAndServe(":8080", nil); err != nil {
		log.Fatal("ListenAndServe:", err)
	}
}
```

templates/index.html

```html
<html>
  <head>
    <title>index</title>
  </head>
  <body>
    <div>index</div>
  </body>
</html>
```



### パターンその3
templateHandler、独自のハンドラを作成

- http.NewServeMux() で mux（マルチプレクサ）を作成
- mux.HandleFunc に URL パターンと index handler 関数を渡す
- http.Server 構造体のポインタで初期化。その際に Handler field に mux を渡す
- 初期化したものを server 変数へ代入
- 最後に server の ListenAndServe メソッドを実行

```golang
package main

import (
	"net/http"
	"time"
)

func index(writer http.ResponseWriter, request *http.Request) {
	// 省略
}

func main() {
	mux := http.NewServeMux()
	mux.HandleFunc("/", index)
	server := &http.Server{
		Addr:           config.Address,
		Handler:        mux,
		ReadTimeout:    time.Duration(config.ReadTimeout * int64(time.Second)),
		WriteTimeout:   time.Duration(config.WriteTimeout * int64(time.Second)),
		MaxHeaderBytes: 1 << 20,
	}
	server.ListenAndServe()
}
```

http.Server という構造体は以下のようになっている
https://golang.org/src/net/http/server.go?h=ListenAndServe#L2520

```golang
type Server struct {
	Addr			string
	Handler			Handler // handler to invoke, http.DefaultServeMux if nil
	TLSConfig		*tls.Config
	ReadTimeout		time.Duration
	ReadHeaderTimeout	time.Duration
	WriteTimeout		time.Duration
	IdleTimeout		time.Duration
	MaxHeaderBytes		int
	TLSNextProto		map[string]func(*Server, *tls.Conn, Handler)
	ConnState		func(net.Conn, ConnState)
	ErrorLog		*log.Logger
	BaseContext		func(net.Listener) context.Context
	ConnContext		func(ctx context.Context, c net.Conn) context.Context
	inShutdown		atomicBool
	disableKeepAlives	int32
	nextProtoOnce		sync.Once
	nextProtoErr		error
	mu			sync.Mutex
	listeners		map[*net.Listener]struct{}
	activeConn		map[*conn]struct{}
	doneChan		chan struct{}
	onShutdown		[]func()
}
```

Server struct の Handler field に http.NewServeMux() で作成したマルチプレクサを登録しているようだ。

他にもパターンを発見したら随時更新していく。
Jest + enzymeでフロントエンドテストと戦った(Part1)

## はじめに
ここ最近フロントエンド開発で、テスト書きまくったのでその知見を残しておきたいだけの記事です。
あと僕が長文書けないので3つくらいに分けてまとめていきます。

## インストール
```shell
$ npm install --save-dev jest
$ npm install --save-dev enzyme jest-enzyme
```

## 実行
`package.json`に下記を追加 -> `yarn test`で実行する。

```json
{
  "scripts": {
    "test": "jest"
  }
}
```

これからjestの記法をレベルに応じて列挙して行こうかなと思います。

## レベル1（基本的文法）
まずは基本の基本。下記に簡単なテストを書きました。

```javascript
describe('足し算', () => {
  it('2 + 3 = 5', () => {
    const result = 2 + 3
    expect(result).toBe(5)
  });
});
```
足し算の結果があってるかをテストしました。
Jestでは `describe` と `it` を使いテストケースを記載していきます。
個人的には`describe`は大きいまとまりを作って、`it`はそのdescribeに沿ったより具体的なテストケースを記載すると思っています（この部分は人それぞれで使い方が違うと思います）。

`expect`でテストが合ってるかを判定します。
`describe`,`it`,`expect`はどのテストを書くにしても必ず出てきます。

## レベル2（マッチャー）
Matcher（マッチャー）といわれるものがありまして、これは等価性を判定するテストです。
先ほどのレベル1で出てきたテストもマッチャーテストです（足し算の結果が予測した値と同じか否か）。

`toBe(5)`の部分です。
逆に「ではない」と意味合いを込めてテストしたい場合は、`not`を使用します。

```javascript
describe('足し算', () => {
  it('2 + 3 = 5', () => {
    const result = 2 + 3
    expect(result).not.toBe(6)
  });
});
```
他にも`toBeEqual`というマッチャーもあって、これはオブジェクトまたは配列を評価する際に使用します。

```javascript
it('object assign', () => {
  const object = {one: 1};
  object['two'] = 2;
  expect(object).toEqual({one: 1, two: 2});
});
```

あとは
expect(value)において、valueが

- toBeNull ... `null`の時に一致する
- toBeUndefined ... `undefined`の時に一致する
- toBeDefined ... `undefined`以外に一致する
- toBeTruthy ... `true`の時に一致する
- toBeFalsy ... `false`の時に一致する

となります。いわゆる特定の値かどうかを知りたい場合にこういうヘルパーメソッドを使います。

## レベル3（イベント）
徐々にフロントエンドっぽくなってきますが、Webサイトにアクセスしたらユーザーは何かしらのアクションを起こします。例えばボタンクリックしたり、ログインしてマイページに遷移したり、まあキリがないくらいいろんな行動があります。そういうユーザーアクションが正しく動作してるかをテストします。

ここでよく使われるメソッドは

- `toHaveBeenCalled`
- `simulate`

です。

例えば下記のようなボタンに対して

```jsx
export const Sample: React.FC = () => {
  return <Button onClick={onClickEvent}>button</Button>
}
```
に対して、このボタンがちゃんとクリックされるとイベントが発火されるかを確かめます。

```js
describe('click event', () => {
  it('Button', () => {
    const wrapper = shallow(<Sample />)
    wrapper.find(Button).simulate('click')
    expect(onClickEvent).toHaveBeenCalled()
  })
})
```
`shallow`というのは簡単に言うとコンポーネントをテスト上で表示するメソッドです。
コンポーネントから`Button`タグを探して（find）、そのボタンをクリックするシミュレートをかけます。（ここがユーザの動作アクション）
で、最終的にクリックしたら、ButtonのonClick属性が機能しているかを`expect(onClickEvent).toHaveBeenCalled()`で評価しています。

`toHaveBeenCalled`には他にも似たようなメソッドがあって、`toHaveBeenCalledTimes`(メソッド等が何回呼ばれたかを判定する)や`toHaveBeenCalledWith`(メソッドで呼ばれた結果は何かを判定する)といったメソッドもあります。

## その他
先ほども出てきましたが、テストでは下記のような書き方が頻繁に出てきます。
頻繁に出てくるような書き方を各`it`ごとに書くのは面倒なのでこの部分も効率化できます（具体的には`beforeEach`というのがあって、`it`が呼ばれる前に実行してくれます）

```js
const wrapper = shallow(<Sample />)
wrapper.find(Button).simulate('click')
```

この記事はここまで。
次回の記事でお会いしましょう。
【Go】http.ListenAndServe と server.ListenAndServe の違いについて

<font color="red">※注意: この記事ははあくまで個人学習用に整理しただけの記事で、内容としては不完全なものになります。読んでも参考にならない可能性が高いです。</font>

### http.ListenAndServe と server.ListenAndServe の違い

http.ListenAndServe 使用例

```golang
func main() {
    http.Handle("/", &templateHandler{filename: "index.html"})

    if err := http.ListenAndServe(":8080", nil); err != nil {
        log.Fatal("ListenAndServe:", err)
    }
}
```

server.ListenAndServe() 使用例

```golang
func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/", index)
    server := &http.Server{
        Addr:           config.Address,
        Handler:        mux,
        ReadTimeout:    time.Duration(config.ReadTimeout * int64(time.Second)),
        WriteTimeout:   time.Duration(config.WriteTimeout * int64(time.Second)),
        MaxHeaderBytes: 1 << 20,
    }
    server.ListenAndServe()
}
```

http パッケージの Server struct を確認
https://golang.org/src/net/http/server.go?h=ListenAndServe#L2520

```golang
type Server struct {
	Addr			string
	Handler			Handler // handler to invoke, http.DefaultServeMux if nil
	TLSConfig		*tls.Config
	ReadTimeout		time.Duration
	ReadHeaderTimeout	time.Duration
	WriteTimeout		time.Duration
	IdleTimeout		time.Duration
	MaxHeaderBytes		int
	TLSNextProto		map[string]func(*Server, *tls.Conn, Handler)
	ConnState		func(net.Conn, ConnState)
	ErrorLog		*log.Logger
	BaseContext		func(net.Listener) context.Context
	ConnContext		func(ctx context.Context, c net.Conn) context.Context
	inShutdown		atomicBool
	disableKeepAlives	int32
	nextProtoOnce		sync.Once
	nextProtoErr		error
	mu			sync.Mutex
	listeners		map[*net.Listener]struct{}
	activeConn		map[*conn]struct{}
	doneChan		chan struct{}
	onShutdown		[]func()
}
```

server struct の ListenAndServe method を確認すると

```golang
func (srv *Server) ListenAndServe() error {
	if srv.shuttingDown() {
		return ErrServerClosed
	}
	addr := srv.Addr
	if addr == "" {
		addr = ":http"
	}
	ln, err := net.Listen("tcp", addr)
	if err != nil {
		return err
	}
	return srv.Serve(ln)
}
```

上記のようになっており、特に仮引数は受け取らない。

続いて http ListenAndServe 関数を確認

```golang
func ListenAndServe(addr string, handler Handler) error {
	server := &Server{Addr: addr, Handler: handler}
	return server.ListenAndServe()
}
```

こちらは addr 文字列と handler を仮引数で受け取るようになっている。
関数内部で Server struct のポインタで初期化し、server.ListenAndServe() を実行している。

つまり、`http.ListenAndServe(":8080", nil)` で実行する場合、addr と handler しか指定することができない。また、その際に、マルチプレクサも指定できない為、`DefaultServeMux` が使用されるという仕組みのようだ。

自分の中の結論としては、

- addr と handler のみを指定したい場合は http.ListenAndServe を使用
- addr と handler 以外も指定したい場合は server.ListenAndServe を使用

という理解になった。
Unityでマルチプレイするなら(せめて)PUNではなくPUN2を使おう

# 概要
　Unityでマルチプレイゲームを作るときに真っ先に選択肢に上がるPhoton。それのUnity用のSDKがPhotonUnityNetworking(以下PUN)なのですが、PUNは結構昔からあるPUNと、2018年頃にリリースされたPUN2の2つがあります。で、いざPhotonを使おうと「Unity Photon」とかで検索すると出てくる記事の殆どはPUNの方の記事で、「PUN2」で調べても日本語記事は数えられるくらいしかヒットしません。仕事でPUNを1年半触ってからPUN2を半年弱触ってみて、これもうPUN使うメリットないな？ってなったので、PUN2を使おうぜっていうお話です。

# そもそもPhotonってなんぞ？？？
一応
　超簡単に言うとゲーム用リアルタイム通信SaaSです。一重にPhotonといってもRealTimeやBolt、ChatやVoiceなど色々細分化はされてます。それらが統合されUnity用に提供されているSDKがPUN。
　バックエンドを意識しないで実装でき、課金も同時接続の上限に比例するだけと個人開発者でも使いやすいのがUnityでマルチプレイ作る時に真っ先に挙げられる理由かなと。あと個人的には標準でUDPで通信できて、通信間のフレームの座標の補完をかけてくれるのがポイント高いです。こいつらの実装は重すぎるので......

#ここが凄いよPUN2
　PUNと比較してかなり強化されています

1. APIがだいぶイケイケになった
1. Instantiateのカスタマイズが可能になった
1. コネクションが強化された

　順番に説明します。

## 1.APIがイケイケになった
　正直PUNがイケてなさすぎたっていうのはある。触ったことある人ならわかると思うんですがメンバの命名規則が完全にMS標準から外れてたり、コールバック系が動的な処理っぽいとこがあったりで、なんというか「C#ぽくない」ライブラリだったんですよねPUN。それがだいぶC#ぽくなりました。命名規則がかなり一般的なものになり、コールバックも殆どがInterface経由の呼び出しになってパフォーマンスがマシになってる、らしい。

## 2.Instantiateのカスタマイズが可能になった
　PUN使ったことない人に説明しますと、PUNではPrefabを*PhotonNetworking.Instantiate*メソッド(以下*Instantiate*)で生成することで、他のプレイヤーとのオブジェクトの同期を取ります。ただPUNの場合このメソッドが曲者で、生成するPrefabはResourcesから読み込む以外の指定方法がありません。あのResourcesです、Unity公式にDon't use it.って言われてるあのResources。一応すげー頑張れば別のところから読み込むことも不可能ではないっぽいのですが、一回やって失敗して諦めました。
　ところがぎっちょんPUN2では、*IPunPrefabPool*実装クラスを設定することによって*Instantiate*内部のPrefab読み込み方法を簡単にカスタマイズすることができるようになりました。例えばSerializeFieldで参照しているGameObjectや、ちょっと頑張ればAssetBundle内のPrefabも指定できます。そのためパフォーマンス面やアセット管理面がPUNに比べて劇的に改善してます。

## 3.コネクションが強化された
　これが一番大きいです。PUNでは、アプリケーションのフォーカスが少しの間(体感は1秒ちょっと)外れるとコネクションが切れ切断扱いになります。このフォーカスが外れるというのはスマホアプリだと頻繁に発生し、例えば「バッテリーが残りn%です」ダイアログが出るだけで起きます。つまりスマホアプリでPUNを使う場合、特にリアルタイム性が強いゲームでは復帰処理はほぼ必須でした。(ちなみにPUNの再接続機能は「コネクションの再接続ができる」ってだけなので、ゲームに復帰するならデータの復元等は自前で実装する必要があります)
　がしかし、PUN2ではこのコネクションが強化されてます。具体的に言うと体感5秒ちょっとはフォーカスが外れても切れません。ここまでくると意図的にアプリを最小化したり本体をスリープさせたりしないと起こらないので、仕様的に切断扱いにしてゲームから弾けばいい範疇です。ただ、どうして＆どうやって強化されたのかはわかりません......公式には書かれてない(そもそもPhotonはそんなにDoc充実してない)し流石にSocket通信周りの実装読むのはしんどいので......知ってる人いたら教えてほしいです.......

# ここがデメリットだよPUN2
　流石に美味い話だけではありません。概要で書いたようにインターネットの大海に落ちているPhotonの記事は殆どがPUNのもので、つまり落ちてるサンプルコードもPUNのものです。前述のようにPUN2では命名周りやコールバックの仕組みが変わってるので、サンプルコードをコピペしただけではコンパイルがまず通りません。通ってもコールバックが取れてなかったりします。ただ逆に言うとPUNではなくPUN2を使うデメリットはこれだけです。

# まとめ
　今からUnityでマルチプレイゲーム作るなら、コーディングに自信がなくてサンプルコードの改変すら不安ってわけじゃないならPUN2を使うことをお勧めします。また、[コード面での変更と対応をまとめてくださってる方](https://junyablog.site/post-2973/)や[PUNからの移行方法をまとめてくださってる方](https://synamon.hatenablog.com/entry/2019/04/18/193751)もいらっしゃるのでこの辺を見ればコーディング自信なくてもそんなに困ることもないんじゃないかなーって思います。
　ただそもそも、ある程度以上の規模の開発でPhotonを使うのは結構しんどいです。仕事でも1000行強のラッパークラスで色々機能足して使ってます。~~最近だと流行りの[Fall GuysがUnity+MultiPlayで実装されてるみたい](https://unity.com/ja/case-study/mediatonic-fall-guys#serving-massively-multiplayer-environment)ですしそっちに挑戦してみるのもいいんじゃないんでしょうか。~~(よく読んだらそんなことどこにも書いてなかった)無責任発言で締めます、おわり。
できるだけ正確に、住所を3分割（都道府県・市区町村・その他）する(C#)

#機能
- 住所を分割します。
- 都道府県、政令市・郡、市区町村のふりがな・フリガナを返します。
- 郡を省略可能
- 市区町村以降の住所の文字列部分は、空白・改行で2分割も行います。

### 分割例
福井県あわら市○○1-1-1 ××× → 福井県 , あわら市 , ○○1-1-1 ××× , , ふくいけん , あわらし

### できるだけ正確に
できるだけ正確に、とは、分割したい住所の「都道府県」「政令市・郡」「市区町村」が不正確な場合は、分割しないということです。

- 足羽県あわら市○○1-1-1　→　足羽県は昔あったが今は無いので、分割されない。

## ダウンロード
次のページでダウンロードできます。
https://github.com/mikihiro-t/AddressSplitter

DLL
https://github.com/mikihiro-t/AddressSplitter/releases/tag/v1.0

## テスト用ウインドウ
![20200822fig1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132059/75e04919-6256-f02a-dbed-e8b67789abe1.png)

## 照合用データ
照合用に
AddressSource.csv
を利用します。
https://www.e-stat.go.jp/municipalities/cities/areacode
からダウンロードしたデータを加工します。

北海道の行政区分が振興局になっているものは、
- 政令市･郡
- 政令市･郡（ふりがな） を追加しています。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132059/33f40ec4-e9ab-3ce2-1660-47c099273500.png" width=70%>
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132059/6b7ef093-426f-c753-2096-2721681e50ba.png" width=70%>

- AddressSource.csvの更新は、不定期ですが行う予定です。
- 総務省の全国地方公共団体コードのリストは、郡が含まれていないなど、要件を満たさなかったので、利用できませんでした。


## 町域
このプログラムでは、市区町村まで分割し、町域以降はまとめます。市区町村を分割するのと同様の処理を行えば、郵便番号のデータを利用して町域部分も分割することはできるはずです。
[Geolonia 住所データ](https://github.com/geolonia/japanese-addresses)を、利用することもできるでしょう。


## 留意点
- 檮原町→略式表記「梼原町」では分割できない。→AddressSourceにデータを追加すれば対応できるでしょう。
- 七ヶ宿町など、「ヶ」が「ケ」では分割できない。→表記のゆれをプログラムで対応すれば解決できるでしょう。
- 支庁は住所表記とは無関係。例：三宅村は「東京都三宅村」が住所。「東京都三宅支庁三宅村」という表記は誤り。

## 必要環境
.NET Framework 4.7

Visual Studio 2019で作成しています。

## 作成動機
住所分割を行うことが多く、その都度Excelで数式を作成して分割していましたが、大概手間なので、簡単に正確に行えるようにするため作成しました。私が行っている利用方法は、[ガンジス顧客管理での住所分割の処理](https://ganges.pro/customer/blog/addresssplitter/)を参照願います。

## 履歴
2020-08-22 Ver 1.0 [できるだけ正確に、住所を3分割（都道府県・市区町村・その他）する(VBA)](https://qiita.com/hiro_t/items/03661ce9478f308041b7)を、C#に移植し機能を追加したものです。
ポイ活&節約サイト【Cポン】のLPをWordpressで作ってみた。

【作ったページ】
https://hukugyou-diamond.com/lp/cpon-2/

LP（ランディングページ）をWordpressで簡単に作る方法の一つとして、Colorfulというテーマを購入してColorfulテーマを使って作る方法があります。

今回私はColorfulを使って作成しました。

どんな感じで作れるのか？などは本文最上部のリンクから見て頂ければ分かるのですが、基本的にソースコードを一から打つことはしなくても大丈夫です。

カスタムボタンを使ってブロックエディターで出来るので、初心者の方向けだと思います。

ちなみに、Cポンとは飲食店などで代金が20%オフになる決済システムの事です。

ちょっとマーケティング戦略的な話をします。

先にチャージしておくので必ず加盟店に行かないと使えません。

なので、従来のクーポンと違う点は、クーポンをばらまいたけど客は結局来なかった・・・ということが激減するわけです。

新型コロナウイルスの影響もあり、飲食業界が大変だとは思いますがその飲食業界を応援するという意味もあります。

最近巷で流行っているということもあり、LPを作ってみました。

（Cポンの評判を調べて頂ければ分かりますが、Cポンの悪い評判は一つも見当たりませんね😊）
Firestore,zapier,gmailを使って、受信したメールアドレスをFirestoreに登録する

# はじめに
・zapier(ザピアー)を利用してfirestoreにgmailのメール内容を登録する機能を作成したので、備忘録として記事を作成
※zapierは無料で使えます。今まで有料にする必要がなかったので、有料になる条件は調べてません。

# 概要
![zapierの連携について.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/695965/fb91bb00-dd7b-5d84-46b2-4d8558cbf610.png)

# 設定手順

## 前提条件
①Firebaseプロジェクトを作成し、コレクションを作成済であること
※必ずFirebaseプロジェクト作成後、Firestoreの設定、コレクションの作成まで行うこと。これをしないとzapier側の設定画面で読み込みが行われません。

## 設定概要
・新しいGmailが届いたタイミングで、メールの内容をFirestoreの特定のコレクションに登録する。
・今回はコレクション名：testコレクションを対象とします。

## zapierにログイン
<a href=“https://zapier.com/“>zapierへGo</a>
①ログインを行なってください。

## 設定方法
ログインしたら以下の様な設定画面となります。
![スクリーンショット 2020-08-22 12.23.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/695965/9e78f590-d178-3164-7ec0-b0ee49ba401b.png)

以下の様に設定してください。
![設定②.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/695965/53e2e7ef-8ffc-870c-0614-69547f61af11.png)

「TRY IT」をクリック
こんな画面に遷移します。
メールを受信する対象のGmailアカウントを登録しましょう。
設定したらcontinueを押下
![設定3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/695965/189458a7-ec00-f638-eff0-c177722f1bd0.png)

今回はラベルやインデックスは利用しないので、そのまま「continue」
![設定４.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/695965/eb828bd7-e6bf-3dd0-d25f-75aac1c47571.png)

これでメール側の設定は完了です。
![設定⑤.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/695965/cf43dba4-a7c5-aef5-7b8a-40b418bbf860.png)

次にFirestoreの設定に移りましょう
Firestoreの設定部分をクリック
![設定６.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/695965/4e64be44-561a-b93f-c947-010acbf606ac.png)

Firebaseが紐づいているGoogleアカウント、Firebaseプロジェクトのプロジェクト名を設定します。
![設定７.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/695965/c2467c20-73dc-8d67-3a3e-85d006a5f566.png)

こんな感じに設定します。
ここで「Collection」のところですが、先にFirebaseプロジェクトを作成して、Firestoreの設定を終わらせて、コレクションを作成するところまで終わらせておかないと、読み込んでくれません。

Dataのことろですが現在「mail_body」と指定している名称で、フィールドが作られます。
つまり、上記の設定だと、test/mail_bodyというフィールドに入るということになります。
![settei9.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/695965/91671ac3-9e4d-7d0f-b2fa-12cd6c926fb9.png)

これにて設定完了です。
現在Warinigが出ていますが、すでに一回設定している内容なので、もうテストは必要ないよという内容です。
本来は、下の2つのボタンはアクティブになっています。
「TEST&CONTINUE」ボタンを押すと、テストメールが送られて、Firestoreのtestコレクションにデータが作成されているはずです。
![設定１０.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/695965/069911d6-4e7f-f2e8-d0a1-0bb7799a44ce.png)

設定した内容はZapsタブで「ON」「OFF」できます。
![設定１１.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/695965/92dd7cc2-a9ff-33b0-c4e2-45120690a333.png)

以上
Qiita記事にさりげなく（けど、わかりやすく）環境を記載する

![macOS-11.0 Beta（20A5354i）](https://img.shields.io/badge/macOS-11.0 Beta（20A5354i）-brightgreen) ![Last Modified-2020/08/22](https://img.shields.io/badge/Last Modified-2020/08/22-brightgreen)

# はじめに

先日、@uhooi さんの以下のツイートを見た時に、おっ自分と同じ悩みだなーと思い、**Qiita記事**の**「どこに」**、**「どうやって」**、**環境を書く**といいのか、少し考えてみました :thinking: 

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">Qiitaに記事を投稿するときにいつも悩むのが「環境」を書く位置なんだよな<br>バージョンによって動作が異なることもあるから書くんだけど、流れをぶった切ってしまうことが多い<a href="https://t.co/TbpySzD4S3">https://t.co/TbpySzD4S3</a> <a href="https://t.co/MI6kc36E8H">pic.twitter.com/MI6kc36E8H</a></p>&mdash; ウホーイ (@the_uhooi) <a href="https://twitter.com/the_uhooi/status/1296304653915701253?ref_src=twsrc%5Etfw">August 20, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

# さりげなく（けど、わかりやすく）環境を記載する方法

過不足なく簡潔に環境を記載する方法を模索した結果、**OSSのREADME.md**でよく利用されている**badgeサービス（[Shields IO](https://shields.io)や[Badgen](https://badgen.net)など）**を利用して、**環境を記事の1番上にbadge表示**することにしました。[^1]

[^1]: @mikkame さんの「[Qiitaコントリビューション数をGitHubのプロフィールに貼ってドヤれるサービスを作った](https://qiita.com/mikkame/items/f2c60d9caf8a8e38ec50)」を読んで、**Qiitaでもbadge表示できる**じゃん、と気づきました。

この記事でも、**[Shields IO](https://shields.io)**を利用して**OSバージョン**と**更新日付**を**記事の1番上にbadge表示**しています。

## Shields IOを利用してbadge表示する方法
ここからは、**Shields IOでbadgeを表示する方法**について説明していきます。
[Shields IO](https://shields.io)では以下の形式でURLにアクセスすることで、**badgeのsvg画像**を取得することができます。

```markdown
https://img.shields.io/badge/<LABEL>-<MESSAGE>-<COLOR>
```

Qiitaの記事では、次のmarkdownの書き方で、

```markdown
![<LABEL>-<MESSAGE>](https://img.shields.io/badge/<LABEL>-<MESSAGE>-<COLOR>)
```

以下のような表示が可能になります。
![<LABEL>-<MESSAGE>](https://img.shields.io/badge/<LABEL>-<MESSAGE>-<COLOR>)

なお、今回は触れませんでしたが、見た目を変更できる**style**オプションや、その他オプションがまだまだあるので、興味ある人は[Shields IO](https://shields.io)のサイトを確認してください。

以下、**OSバージョン**と**更新日付**の例です :thumbsup: 

### e.g. OSバージョン
```markdown
![macOS-11.0 Beta（20A5354i）](https://img.shields.io/badge/macOS-11.0 Beta（20A5354i）-brightgreen)
```
![macOS-11.0 Beta（20A5354i）](https://img.shields.io/badge/macOS-11.0 Beta（20A5354i）-brightgreen)
 

### e.g. 更新日付
```markdown
![Last Modified-2020/08/22](https://img.shields.io/badge/Last Modified-2020/08/22-brightgreen)
```
![Last Modified-2020/08/22](https://img.shields.io/badge/Last Modified-2020/08/22-brightgreen)

# 最後に
私は今回の方法で**さりげなく（けど、わかりやすく）環境を記載**するといいかなと思っているのですが、正解のない話なので**別のいい方法知ってるよ**、などコメントいただけると嬉しいです :raised_hand:

# 参考情報
* [Shields IO](https://shields.io)
* [Badgen](https://badgen.net)
Vivado > XADC Wizard > vauxp1/vauxn1, vauxp3/vauxn3などを追加する > Channel Sequencerの指定をする

```txt:動作環境
PYNQ-Z1
Ubuntu 18.04 LTS
Vivado v2019.1
```


### 参考ブログ

- [AXI XADCをSDKで使う](https://seinzumtode.hatenadiary.jp/entry/20171208/1512718542)
    -  Shohei Aoki (id:seinzumtode) 様
    - 情報感謝です

上記を参考にXADCを試そうとしていた。


### vauxp1/vauxn1, vauxp3/vauxn3などの足し方がわからない

上記のBlock Designと自分の作成したBlock Designを比べたところ、XADC WizardのAD入力のピンが足りない。
vauxp1/vauxn1, vauxp3/vauxn3などがないが、その出し方がわからなかった。

![コメント 2020-08-22 122954.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32870/84580a58-957b-4a9c-bcc8-4fec20fcd3cf.png)

### 出し方

- [Tutorial XADC IP - Vivado 2017.2 - Arty Board](https://www.youtube.com/watch?v=hiCroFFLd6w)

2:17頃に設定がある

1. Basicタブの「Single Channel」を「Channel Sequencer」に変換する
2. Channel Sequencerタブにて「Channel Enable」をチェックする

![コメント 2020-08-22 122954.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32870/7cb4c305-b9c6-abd4-d1d8-c364a152f39e.png)

![コメント 2020-08-22 123313.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32870/2e74efd3-b3e5-ba3b-80b2-a4b9d85717e5.png)
VisualStudioCodeのインストールと日本語化(Windows10)

#プロローグ
Windows10へ日本語化されたVisualStudioCodeを導入してみます。

#手順
公式のダウンロードサイトへアクセスします。
（googleで「VisualStudioCode ダウンロード」と検索すれば先頭に出てきます）
左の「今すぐダウンロード」を押下します。
![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538462/6738012d-b48b-ed02-ed02-983e4f284cb9.png)

左のWindowsを選択します
![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538462/e252796e-fd07-4c89-93c4-893ab25ba33b.png)

ダウンロードされたexeを実行します。

「同意する」を選択し、「次へ」を押下します。
![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538462/3b9ff860-30d4-dd9e-1db2-2f5e2077667f.png)

好きなパスを選択し、「次へ」を押下します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538462/74c62d03-d963-6f39-4cc5-d2e70fdc7498.png)

デフォルトで「次へ」を押下します。
![5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538462/45b13ffd-a403-e940-c718-3e1988deebee.png)

デフォルトで「次へ」を押下します。
![6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538462/530b644c-0eba-1328-fab9-50af3d58bf49.png)

「インストール」を押下します
![7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538462/8c52c6a2-2006-da9c-2a90-b514d9d70125.png)

「完了」を押下します。
![8png.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538462/0d2b03e2-ffff-e311-418c-91d52a994bad.png)


これで、導入は完了です。そのまま日本語化していきます。

「View - CommandPalette」を開きます。
![9png.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538462/da69e413-730a-aa68-f886-b5ddfa9680dc.png)

「Configure Display Language」を選択します。
![10png.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538462/fae694b2-ab3c-cf64-f491-1cbd18315218.png)

表示された「install additional Languages」を選択します。
![11png.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538462/4f2890cc-98ba-5d40-8bc9-d23db3133e1c.png)

左に表示された一覧から「Japanese Language Pack for Visual Studio code」を選択し、右側の「Install」を押下します。
そして、右下に表示された「Restart Now」を押下します。
![12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538462/e76e3acd-759e-6643-e2dc-94e3fb413bdb.png)

再起動されると画面が日本語になりました。
![13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538462/e88229c2-ea24-da2b-3ae9-0bd219ae3de3.png)

#エピローグ
サクッとVSCを導入することができました。
所要時間は10分というところでしょうか。
以上です
multiprocessingによる複数引数とプロセス数指定に対応した並列処理[PDF保存]

# 背景

ある測定データ(csv)のグラフをPDFで保存したかった。
データ数が多く(1000データ)なると時間がたくさん(8minほど)かかってしまったので改善しようと思った。

# 目的

大量のPDFの保存を高速かつ、動作時にPCが固まらないようにしたい。

# 手法

pythonを用いて主に以下のことをした。

1. 全測定データ(csv)を参照
2. 全測定データを配列に保存
3. 全測定条件データを読み込み
4. ひとデータごとに全データでループ ← これを高速化
    1. 測定データ・測定条件を受け取り（引数が複数）
    2. matplotで描写
    3. PDFで保存

### そのまま

pythonで普通にfor loop

### Treading

諸事情で実装経験があったのでやってみた
→こいつは１コアしか使えないので速度は大きく変わらない模様。
それでも4minも変わるのは意外

### multiprocessing.Process

並列処理したい関数に引数が複数あるので、Processを利用した。
しかしいくらggってもスレッド数の制限方法が分からず、それは実装できなかった。
そのせいで、実行すると1000個の並列処理をするのでPCがカクカクになってしまった。

### multiprocessing.Pool

引数は1つしか持てないが、スレッド数を簡単に指定できる。
以下のような関数をカマス事で複数引数にも対応できた。 

```python
def wrapper(self, args):
	return self.f(*args)
```

# コーディング

multiprocessingのコーディングは以下のようにやればOK。
コーディングの仕方だけがわかる最小構成（並列化の意味はない）ものです。
詳細はこの記事などが参考になります。
[Pythonの並列処理・並行処理をしっかり調べてみた - Qiita](https://qiita.com/simonritchie/items/1ce3914eb5444d2157ac)

```python
from multiprocessing import Process, Pool
import multiprocessing

def f(a, b): # 並列処理する関数
    print(a, end=",") # 動作確認だけなのでシンプルに

def wrapper(args):  # Poolは引数を1つしか取れないので、これをかまして展開する。
    f(*args)

def main():
    # Processによる並列処理(複数引数：o, プロセス数指定：x)
    print('Process')
    p_list = []
    for i in range(20):
        p = Process(
            target=f,
            kwargs={'a': i, 'b': 0})
        p.start()  # 開始
        p_list.append(p)

    for p in p_list:
        p.join()  # 終了を待つ

    # Poolによる並列処理(複数引数：x, プロセス数指定：o)
    print('\nPool')
    p = Pool(multiprocessing.cpu_count())  # CPUの数の分並列処理
    values = []

    for i in range(20):
        values.append((i, 0))
    p.map(wrapper, values)  # 単数引数を複数に展開する

if __name__ == "__main__":
    main()
```

結果

```python
Process
0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,
Pool
6,0,8,14,2,10,16,3,11,17,5,13,18,1,9,15,4,12,19,7,
```

# 結論

### 動作時間

以下のような順位になりました。

1. multiprocessing（Poolによりスレッド数をPCのコア数に制限）
1min 50sec
2. multiprocessing（スレッド数の制限なし）
4min 30sec程度
3. threading
5min 41sec
4. 並列化なし（pythonによる自動並列？のみ）
9min 33sec

※試行錯誤の過程のメモなので、同等の条件で測定していない場合があります。目安程度に。

# 考察

1.と2.の比較をします。
1.は８個ずつ順に処理を進める事になります。

2.は全処理（今回は1000の処理）を同時並行で行う事になります。

- 2.の方が早くなるはずではないのか？
実は、CPUは原理的に並列処理が不可能。
並列処理思われているものは、実は代わる代わるやることを変えているだけ。
例えば処理１、２、３の並列処理は
処理１（1μs実行）→処理２（1μs実行）→処理３（1μs実行）→処理１（さっきの続きから1μs実行）→・・・
というように並列に見せかけているだけである。
よって
処理１（終わるまで実行）→処理２（終わるまで実行）→処理３（終わるまで実行）
としても原理的には時間は変わらない。（こともある）
ただ、コア数（CPUの数）の分だけは並列処理が出来るので、それまでは処理が早くなる。

- なぜ早くなったのか？
メモリが豊富に使えるようになったから？
2.では1000の処理をやっているのでメモリがカツカツになってしまう。
そこが改善されたからではないか？
コマンド：du, find, rsync

#rsync
https://qiita.com/sota0113/items/cda2cd726cefab4faa67

#duコマンド

ディレクトリのサイズ（合計）を表示

```
$ du -sh [ディレクトリ]
```


#findコマンド
ディレクトリ以下のファイルを検索
※参考：https://uxmilk.jp/8583

```
$ find ./ -name "*.txt" -type f
```


（Google Colab）実行結果をリストへ

```
a = !find ./ -name "*.txt" -type f
a = list(a)
```
テンプレート関数の可変引数テンプレートをdecltypeを使って再帰処理できない

# コンパイルエラー: 戻り値型推論のdecltype内で再帰処理

複数の型を受け取り末尾の型を返す処理を考える。
試しに下のようなコードを書いてみたがコンパイルできなかった([テストコード@Wandbox](https://wandbox.org/permlink/Pi6yLnGYSZz8GoSk))。

```tail_failure1.hpp
template<typename T>
T get_tail();

template<typename first, typename ...rest>
auto get_tail() -> decltype(get_tail<rest...>());

template<typename ...args>
using tail = decltype(get_tail<args...>());

tail<int> val1 = 0; // -> int val1
tail<int, char> val2 = 0; // -> char val2
tail<int, char, float> val3 = 0; // compile error
```

# 成功: クラステンプレートによる再帰

結論としては、以下のようにクラステンプレートを用いることで実現できた。

```tail_success.hpp
template<typename first, typename ...rest>
struct tail_impl
{
    using type = typename tail_impl<rest...>::type;
};

template<typename T>
struct tail_impl<T>
{
    using type = T;
};

template<typename ...args>
using tail = typename tail_impl<args...>::type;
```

## 注意点

`template<typename T> struct tail_impl<T>`の`<T>`は必要。
一つ目のtail_implの定義に対して、こちら(二つ目)の定義は特殊化である。
`<T>`がなければ再定義(重複)となりコンパイルエラー。
特殊化がある場合はそちらが優先して採用されるため、引数が一つのときは二つ目の定義が採用される。
すなわち再帰が終了する。

`using tail = typename tail_impl<args...>::type`の`typename`を忘れないように。
忘れがちであるが、これを付けないと`tail_impl<args...>::type`が型名であるとコンパイラが解釈せずにエラーとなる。

# 応用例

当初実装したかったもの。
複数の型からサイズが最大となる型を選択。
複数型を格納するためのバッファのサイズを決定するために利用。

```cpp
#include <type_traits>

template<typename first, typename ...rest>
struct max_size_type
{
    //暫定最大サイズ型
    using provisional_type = typename max_size_type<rest...>::type;
    //最大サイズ型
    using type = typename std::conditional<(sizeof(first) >= sizeof(provisional_type)), first, provisional_type>::type;
};

template<typename T>
struct max_size_type<T>
{
    using type = T;
};

//使用例
max_size_type<int8_t, int64_t, int32_t, float, double>::type val;
```

# 関数テンプレートを用いた再帰で問題ない例

以下の例は関数テンプレートで問題ない。
関数の宣言が確定できるかが成否の分かれ目か。

```cpp
//再帰終端
template<std::nullptr_t = nullptr>
constexpr size_t sum_of_size()
{
    return 0;
};

template<typename first, typename ...rest>
constexpr size_t sum_of_size()
{
    return sizeof(first) + sum_of_size<rest...>();
}

//使用例
sum_of_size<int8_t, int16_t, int32_t, int64_t>(); // -> 15
```

一つ目のsum_of_sizeのテンプレート引数`template<std::nullptr_t = nullptr>`について、`template<typename T>`や`template<typename T = void>`とするとコンパイルエラーとなる。
可変引数(二つ目の定義)のsum_of_sizeと一致するからである。[^1]
[^1]: パラメータパックは内包する引数が0個の場合も可
呼び出される関数の候補が1つに絞れなければならない。
型名でなければよいので`template<int = 0>`はOK。

# 参考

- [(C++テンプレート)関数テンプレートでの再帰について - Qiita](https://qiita.com/Alreschas/items/7b8a167ddc05fe25432c)
- [可変引数テンプレート - cpprefjp](https://cpprefjp.github.io/lang/cpp11/variadic_templates.html)
関数宣言と関数式の巻き上げの違いについて

##巻き上げとは？

今回は関数の巻上げについて解説します。

関数の書き方には関数宣言と関数式があります。
結論から言うと、**関数宣言では巻き上げが起こり**、**関数式では巻き上げが起こりません。**

##関数宣言の場合

```javascript
function hello() {
console.log(こんにちは！)
};
```

こういった関数宣言の場合、どこで`hello();`と記述しても、きちんと関数が呼び出されます。
宣言の前に呼び出しを書いてもOKです。これが巻き上げが起こるということです。


##関数式の場合

一方、関数そのものを定数に代入する関数式の場合、

```javascript
const hello = function () {
console.log(こんにちは！)
};

console.log(hello);
```
ではきちんと呼び出されますが、
console.log(hello);を関数式より先に書いてしまってはいけません。`Undefined`（未定義）となります。


定義する前に定数は使用できませんでしたね。それと同じです。

以上、関数宣言と関数式の違いでした。
Hello, OpenAPI - OpenAPI GeneratorでRubyクライアントを生成

![openapi-generator-4.3.1](https://img.shields.io/badge/openapi generator-4.3.1 -brightgreen) ![ruby-2.7.1p83](https://img.shields.io/badge/ruby-2.7.1p83 -brightgreen)

# はじめに

引き続き OpenAPI で遊んでいる。今回は[前回](https://qiita.com/narutaro/items/32cc20c88d3c48c4bd89)の `PetStore` モックサーバに対して、`openapi-generator`[^1] で生成したRubyクライアントからアクセスしてみる

[^1]: https://openapi-generator.tech/


# インストール

`openapi-generator` をインストールするところから

```
$ brew install openapi-generator
```

次に `ruby` クライアントの生成

```shell
$ openapi-generator generate -i https://raw.githubusercontent.com/openapitools/openapi-generator/master/modules/openapi-generator/src/test/resources/2_0/petstore.yaml -g ruby -o .
```

何やらたくさんファイルが生成される

```
.
├── Gemfile
├── README.md
├── Rakefile
├── docs
│   ├── ApiResponse.md
│   ├── Category.md
│   ├── Order.md
│   ├── Pet.md
│   ├── PetApi.md
│   ├── StoreApi.md
│   ├── Tag.md
│   ├── User.md
│   └── UserApi.md
├── git_push.sh
├── lib
│   ├── openapi_client
│   │   ├── api
│   │   │   ├── pet_api.rb
│   │   │   ├── store_api.rb
│   │   │   └── user_api.rb
│   │   ├── api_client.rb
│   │   ├── api_error.rb
│   │   ├── configuration.rb
│   │   ├── models
│   │   │   ├── api_response.rb
│   │   │   ├── category.rb
│   │   │   ├── order.rb
│   │   │   ├── pet.rb
│   │   │   ├── tag.rb
│   │   │   └── user.rb
│   │   └── version.rb
│   └── openapi_client.rb
├── openapi_client.gemspec
└── spec
    ├── api
    │   ├── pet_api_spec.rb
    │   ├── store_api_spec.rb
    │   └── user_api_spec.rb
    ├── api_client_spec.rb
    ├── configuration_spec.rb
    ├── models
    │   ├── api_response_spec.rb
    │   ├── category_spec.rb
    │   ├── order_spec.rb
    │   ├── pet_spec.rb
    │   ├── tag_spec.rb
    │   └── user_spec.rb
    └── spec_helper.rb
```

クライアントに関しては `lib/openapi_client.rb` と `lib/openapi_client` を意識すれば良さそうです

# 生成したコードの使い方

`lib` 以下にクライアント関連の Ruby モジュールが生成されている。ドキュメントがあんまりないので、コード読みながら手探りで作ったクライアントはこんな感じ

```ruby:pet-client.rb
$LOAD_PATH.push('.')

require 'openapi_client'

OpenapiClient.configure do |config|
  config.base_path = ""
  config.host = "localhost:4010"
  config.api_key['api_key'] = 'special_key'
  #config.debugging = true
end

api_instance = OpenapiClient::PetApi.new
data = { "id": 112354, "category": { "id": 0, "name": "taro" }, "name": "john", "photo_urls": [ "http://image.com" ], "tags": [ { 
body = OpenapiClient::Pet.new(data)

begin
  puts "GET"
  pet = api_instance.get_pet_by_id(1)
  puts JSON.pretty_generate(pet.to_body)
  
  puts "POST"
  pet1 = api_instance.add_pet(body, {:header_params => {"Content-Type": "application/json", "accept": "application/json"}})
  puts pet1.class
rescue OpenapiClient::ApiError => e
  puts "Exception when calling PetApi->add_pet: #{e}"
end
```

- `Pet.new(data)` で渡す `data` は `Hash` で渡す。`JSON` の時のキーそのままではだめで、スネークケースで渡さないといけない（`PhotoUrls` > `photo_urls`）

このクライアントでモックサーバに対して、`GET` `POST` ともに動いているようなので、ひとまずよしとします。まだ Hello World レベルですが、ここまではいい感じで使えるコードが生成されているように感じます。あとは、もうちょっとドキュメントあると嬉しいのだが...できればサンプルコードとか。ご存知のかた教えて下さい

さて、とりあえず使い方がわかった気がするので、次にいきます
zabbixの見た目をカスタマイズしたい

[0.やりたいこと・なぜやるのか](#やりたいこと・なぜやるのか)
[1.ゴール](#ゴール)
[2.画像の準備](#画像の準備)
[3.環境の準備](#環境の準備)
[4.Zabbixサーバへ画像の転送](#Zabbixサーバへ画像の転送)
[5.ソースコード書き換え](#ソースコード書き換え)

#やりたいこと・なぜやるのか
zabbixの見た目を可愛くしたい！画像とか色とか背景とか変えちゃいたい時ありますよね？
私はフロントエンドエンジニアではないのですが、基本的なことは理解しているのでちょっといじっちゃいます。
なぜやるのかについては、私にとっては「気分転換！！ZABBIXをもっと知る！」です。
一方でActive/Standby構成をとっている方もいると思うので、見た目ですぐにどちらのマシンにアクセスしているかをわかりやすくもできると思います。

#ゴール
今回はしたの画像のようにZABBIXの画像をtamolabに変えちゃいます！
![スクリーンショット 2020-08-22 11.12.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647963/ebb3b100-17ed-bf51-a177-e284ba19366c.png)

#画像の準備
画像を作成します。私はパワーポイント信者なのでパワポで作成します。
サイズは特に気にしなかったですが、88 × 20(tamlab.JPG)で作成してみました。作成したらデスクトップに保存します。
![スクリーンショット 2020-08-22 11.00.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647963/8ef1f2b9-85d0-ca6b-4605-8f11bacf8e69.png)

#Zabbixサーバへ画像の転送
画像をサーバに転送します。

```bash:データ転送
PC$ scp ~/Desktop/tamlab.png <zabbixserverのIP>:~/
#うまくいけば、下記のように転送される
tamlab.png               100%   31KB   3.0MB/s   00:00
PC$ 
```
#画像の配置
転送した画像を下記の場所に移動させます。

```bash:データ移動
zabbix-server$ sudo mv ~/tamlab.png /usr/share/zabbix/assets/img/ 
```

#ソースコード書き換え
下記の階層に移動して、ファイルを確認します。

```bash:階層移動
zabbix-server$ cd /usr/share/zabbix/assets/styles
zabbix-server$ ls 
blue-theme.css  dark-theme.css  hc-dark.css  hc-light.css
```
blue-theme.css  dark-theme.css  hc-dark.css  hc-light.cssについては下記の設定に紐付いています。
例えば、アカウント設定（GUI）で「Dark」テーマを選べば、dark-theme.cssが適用されるようになります。
![スクリーンショット 2020-08-22 11.25.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647963/d9754e07-46dc-4719-a4cf-b36fd5c849cc.png)

「Dark」テーマを編集していきましょう。.logoを編集していきます。

```bash:/usr/share/zabbix/assets/styles/dark-theme.css
zabbix-server$ sudo vim /usr/share/zabbix/assets/styles/dark-theme.css
...
.logo {
  float: left;
  display: block;
  width: 95px;
  height: 50px;
  background: url(../img/tamlab.JPG) no-repeat 0 11px; }
  //background: url(../img/icon-sprite.svg) no-repeat 0 -903px; }
...
```
元のコードはコメントアウトして、参照画像を`../img/icon-sprite.svg`から`../img/tamlab.JPG`に変えました。
![スクリーンショット 2020-08-22 11.42.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647963/b0f9ac4c-2f8c-2118-766c-8a2ad20ce5b7.png)

終わり。他にもCSSをいじると色々とカスタマイズできます！