OmniAICreator/Qiita-1.07M huggingface dataset:
tesseract使ってて知ったことの備忘録その1

## 実現したかったこと
マイキーの有名な(?)[いねえよなぁ!!?](https://alu.jp/series/%E6%9D%B1%E4%BA%AC%E5%8D%8D%E3%83%AA%E3%83%99%E3%83%B3%E3%82%B8%E3%83%A3%E3%83%BC%E3%82%BA/crop/W8LJCePySOaAfpNuKrIb)をOCR(光学文字認識)で抽出

## 結果
![スクリーンショット 2021-09-08 23.01.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/480924/6d625ecd-58a2-aa29-9ff1-f4f0326dbffa.png)

ご覧のように全然できませんでした。。どうしたら
![スクリーンショット 2021-09-08 23.02.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/480924/954b566f-2d5e-7de3-5060-7b66203a91e3.png)
って出力されんだ。。マイキーの顔とか吹き出しとかも無理矢理文字に起こしてるっぽい。改行とかもあるけどきれいに「いねえよなぁ!!?」だけ抽出できたら達成感やばそう！チューニングの方法をご存知の方いらっしゃいましたらぜひコメント欄で教えてください！

今回はそんな中でもわずかながら学びがあったので今回はそれを残したいと思います。

環境構築とかすぐ詰まる人なので、Google Colab上でやりました。今回のような出力となったコードはこちら
↓

```
from PIL import Image
import pyocr
import pyocr.builders
import cv2
img1 = Image.open('drive/My Drive/Colab Notebooks/maiky.jpg')

tools = pyocr.get_available_tools()
# コメント1
tool = tools[1]
print(tool)

builder = pyocr.builders.TextBuilder()
# コメント2
builder.tesseract_layout = 11

txt1 = tool.image_to_string(img1, lang='jpn', builder = builder)
print(txt1)
```

### コメント1
toolsは2種類あるらしく、試しにprintすると
tools[0]は

![スクリーンショット 2021-09-08 23.23.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/480924/d878bc24-e67c-b669-6274-3ca1f1d5b632.png)
tools[1]は
![スクリーンショット 2021-09-08 23.23.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/480924/c1f6a4af-6d67-408b-ed75-237d42166a36.png)
よくよく見ると呼び出してるのが違うみたいです。

### コメント2
`tesseract_layout`の数値を今回は11にしましたが、これは0〜13までの14種類のアルゴリズムがあるようで、試しに5とかにすると
![スクリーンショット 2021-09-08 23.33.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/480924/d3352c24-46fd-9510-72d1-cdfb3a6a84f4.png)
もっとカオスになります。「いねえよなぁ!!?」のどの文字も入ってないっていう。。

## 最後に
[こんな記事](https://qiita.com/henjiganai/items/7a5e871f652b32b41a18#4-ocr%E3%82%A8%E3%83%B3%E3%82%B8%E3%83%B3%E3%81%AE%E5%88%87%E3%82%8A%E6%9B%BF%E3%81%88)見てて、自分もOCRエンジンを2とかに切り替えたら精度上がるんじゃないかなと思ったんですが、Google Colab上で実現する方法がわからず。。もしかしてできない...?

## 参考記事
https://www.titanwolf.org/Network/q/2a70a184-fcc2-4d65-8f9d-0abc397b94d7/y
https://valmore.work/how-to-use-tesseract4-with-python/
Windows10でLinuxを使用する

#はじめに
Linuxの資格(LPIC1)を取得するにあたり、Linuxを触りながら勉強したい。
LinuxはOSS(オープンソースソフトウェア)なので無料入手可能だ。
しかし、ハードを買うお金がないため、現在使用しているWindows10にインストールする。
今回はVirtual Boxを使用してLinuxを導入していく。
手順は「[■ LPI Linux Essentials （エッセンシャル） 資格取得コース （LPI公認）](https://www.udemy.com/course/lpi-linux-essentials-k/)」を参考にします。
詳しく知りたい方は「Linux OS インストール」をぜひ見てください。
無料でプレビュー可！！
手順がややこしかったので、備忘録的に載せていく。

#Virtual Boxとは

> Virtualbox（バーチャルボックス）は、使用しているPCに仮想環境を構築して、他のOSをインストールすることができる仮想化ソフトです。

>Virtualboxを導入することにより、たとえばMac環境でWindowsやLinuxなど複数のOSを切り替えて使用することが可能になります。

[参考](sejuku.net/blog/72833)より。

**しかもディスクいらず 必要なものはPCだけ**


#環境
エディション	Windows 10 Pro
バージョン	21H1
インストール日	‎2021/‎07/‎24
OS ビルド	19043.1165
エクスペリエンス	Windows Feature Experience Pack 120.2212.3530.0

#手順の概要
1. Virtual Boxをダウンロード、インストール
1. Cent OS をダウンロード
1. Virtual Box内のハードを設定
1. Cent OS をVirtual Boxにインストール

 
#1.Virtual Boxのダウンロード、インストール
- [Virtual Box](https://www.virtualbox.org/wiki/Downloads)のページに行き**windows host**を押す

- デフォルト設定のままで「next」を押し続ける

![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/746532/90ee115f-c303-afaf-db47-cbf6ab95010e.png)

#2.Cent OSのダウンロード
- [Cent OSダウンロードページ](http://ftp.nara.wide.ad.jp/pub/Linux/centos/8-stream/isos/x86_64/)で**DVD**がついているものをダウンロード（ここでbootをダウンロードして一敗泣）
- **注意**　インストールはしないでください。

#3. Verual Boxのハードを設定
- 新規をクリック
- 名前を入力(今回はCentOS)
- メモリーサイズ指定(僕は1GB)
- 仮想ハードディスクを作成。ダウンロードしたCentOSより大きく(僕は16GB)
- VDIを選択
- 固定サイズを選択、ダウンロードしたCentOSより大きく(僕は16GB)

PCのスペックやLinuxの使用用途によってご自由調整してください
#4.CentOSのインストール
- 作成したモノ(僕の場合CentOS)の設定をクリック
![tempsnip.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/746532/1c76da60-bc5f-e5f5-8002-493d73739f34.png)

- ストレージタブ＞コントローラ：IDE＞光学ドライブの追加＞ダウンロードしたCentOSを選択
![tempsnip.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/746532/5114b4c2-9981-a62d-3346-75c9ef51932d.png)

- ネットワークタブ＞割り当て＞ブリッジアダプターをクリック

- 最初の画面に戻り、起動をクリック。下記のいつもの画面がでたら、インストール完了
![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/746532/5caf986b-6a47-802b-be35-c800780295ee.png)

# 5.CentOSの各種設定

![画面２.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/746532/a38386f8-c229-d294-64a5-e03282d54360.png)

- 上記画像におけるインストール先（なぞに右に、見切れる）、「ネットワーク」、「ソフトウェアの選択」、「時刻と日付」、「rootユーザー」と「パスワード設定」を行う
- インストール先はDiskを選択して完了
- ネットワークはホスト名を変更して完了（僕の場合はyokohama）
- ソフトウェアの選択はすべてチェックをいれる
- 時刻と日付は東京
- rootユーザーとパスワードを設定して完了
- 右下のCentOSのインストールをクリック

５．で設定したログインユーザーとパスワードを入力して完了！！！
意外と長かった～～～

※Vertual Boxを抜けるためには右のctrlを押したら抜けれます
使い捨てのコマンド作成

# 概要

頻繁に行う複数のコマンド入力を一回のコマンド入力で済むようにします。
npmとかwebpackとかgcloudとか書いていますが本題と関係ありません。

今回行う流れ

1. コマンド作成
1. コマンド登録
1. 使う

## 今回簡略化したかった操作

<details><summary>一例なので読まなくていいです。</summary><div>

1. npmプロジェクトのディレクトリに移動
1. webpackの出力先を削除
1. webpackで出力(バインド)する
1. gcloudでサーバーに送信

</div></details>

## コマンド作成

#### 好きなディレクトリにファイルを作成して開く

viじゃなくてVSCodeとかEmacsとかなんでも大丈夫です。

```zsh
$ cd ~/Documents/cmd
$ touch newcmd
$ vi newcmd
```

#### 中身を書く

```&&``` でコマンドを繋げると成功した場合に次の処理に進みます。

```vim
１つ目の操作 &&
次の操作 &&
次の操作
```

<details><summary>今回の例では次のようなコマンドになります。</summary><div>

```vim
pushd ~/project &&
rm -rf dist/* &&
npm start &&
gcloud compute scp --recurse ./dist/* hogeserver:/etc/hoge &&
popd
```

もう少しちゃんとやりたい人は変数やsetで調べると良いかもしれません。
こんな感じ↓

```vim
# setting
set -e -o pipefail

# var
readonly LIVETS_PATH="~/project"
readonly TRANSMITTER_PATH="dist/*"
readonly RECEIVER_PATH="hogeserver:/etc/hoge"

# main
pushd ${LIVETS_PATH}
rm -rf ${TRANSMITTER_PATH}
npm start
gcloud compute scp --recurse ${TRANSMITTER_PATH} ${RECEIVER_PATH}
popd
```
</div></details>

## コマンド登録

筆者がzsh使っているのでzshに書きます。
bashの人はそっちに書いてください。

#### ```~/.zshrc``` を開く

```zsh
$ vi ~/.zshrc
```

#### 追記する

```
alias newcmd='~/Documents/cmd/newcmd'
```

こうすることで```newcmd```と入力した時に```~/Documents/cmd/newcmd```と入力したのと同じことになります。

## 使う

次で動きます。

```zsh
$ newcmd
動く
```

<details><summary>動かない人向け</summary><div>

#### そんなコマンドがないと言われる人

```zsh
$ newcmd
zsh: command not found: newcmd
$ source ~/.zshrc
$ newcmd
動く
```

#### 権限がないと言われる人

```zsh
$ newcmd
permission denied
$ chmod 777 ~/Documents/cmd/newcmd
$ newcmd
動く
```

</div></details>
サービスアカウントファイルを使ってIDトークンを取得する

# はじめに

Google Cloud Platformの機能を使う場合、たいていは `GOOGLE_APPLICATION_CREDENTIALS` にサービスアカウントファイルのパスが設定されていることを前提とするので認証について深く考えず使えることが多い。

が、`GOOGLE_APPLICATION_CREDENTIALS`を使わず、サービスアカウントファイルをプログラム内で直接指定してREST APIを叩きたい時もある。
この場合、REST APIのリクエストのヘッダーには `Authorization: Bearer <IDトークン>` なるおまじないを埋め込まなくてはいけない。

このIDトークンの取得の仕方をよく忘れるのでここでメモっておく。

なお、あらかじめサービスアカウントには該当のCloud Functionsに対して `Cloud Functions 起動元 （roles/cloudfunctions.invoker）` のIAMが付与されていることを前提とする。

# サービスアカウントファイルを使ってIDトークンを取得する

特にひねりもなく回答をのっける。

## 実行環境

* Python 3.9

## 必要パッケージのインストール

```sh
pip install google-auth
```

## サービスアカウントファイルを使ってIDトークンを取得する関数

```python
from typing import Any, Dict, Union

from google.auth.transport.requests import Request
from google.oauth2.service_account import IDTokenCredentials


def get_id_token_from_service_account(
    service_account: Union[str, Dict[str, Any]], target_audience: str
) -> str:
    if type(service_account) is str:
        credentials = IDTokenCredentials.from_service_account_file(
            service_account,
            target_audience=target_audience,
        )
    else:
        credentials = IDTokenCredentials.from_service_account_info(
            service_account,
            target_audience=target_audience,
        )

    credentials.refresh(Request())
    token = credentials.token

    return token
```

## 使い方の例

以下の例では、最終的にhttpトリガーのCloud Functions `https://my_region-my-project.cloudfunctions.net/hello_world` を実行したかったものとする。
`hello_wolrd` 関数は認証が必要な関数であるためIDトークンが必要、というシチュエーションである。

```python
import json
import requests

url = "https://my_region-my-project.cloudfunctions.net/hello_world"

# サービスアカウントファイルのパスを与えてIDトークンを取得する。
id_token = get_id_token_from_service_account("./my-project-xxxxxxxxxxxx.json", url)
# 何らかの理由で既に service_account_obj = json.load(open("./my-project-xxxxxxxxxxxx.json")) のようにして
# サービスアカウントファイルを自前で読み込んでJSONパーズ済みの場合は 
#   id_token = get_id_token_from_service_account(service_account_obj, url)
# のようにパース結果を直接指定してもいい。

# Cloud Functionsを実行する。

# ヘッダーの設定
headers = {
    "Content-Type": "application/json",
    "Authorization": f"Bearer {id_token}",
}

# リクエストボディの設定
data_encoded = json.dumps({...}).encode()

response = requests.post(
    url,
    headers=headers,
    data=data_encoded,
)
print(response.text)

```

## 補足1

~~Cloud Functions以外でもGCPのREST APIなら `get_id_token_from_service_account` の第2引数にAPIのサービスエンドポイントを指定すればいいと思う（未確認）。~~

-> 他のGCP REST APIはIDトークンではなくアクセストークンを指定する。
   ソースコードも変わってくる。（後日追記予定）

## 2022/04/03 補足2

最近 [第2世代Cloud Functions](https://cloud.google.com/functions/docs/2nd-gen/overview?hl=ja) がサポートされた（執筆時点ではpre-GAだが）。

第2世代でも上述のソースコードで呼び出すことができるようだ。

ただし第2世代の場合は **該当のCloud Functionsに対して `Cloud Functions 起動元 （roles/cloudfunctions.invoker）` のIAMを付与するだけでなく、そのCloud Functionsと対応するCloud Runに対して `Cloud Run 起動元（roles/run.invoker）` も付与しなければならない** ことに注意する。
（第2世代のCloud Functionsの中身はCloud Runなので考えてみれば当然なのだがわかりにくい……）。

# おわりに

おわり。
最近IPv6/IPv4混在環境で問題が起きている気がしてならない件



最近起こる問題
---
ある特定のホストへのアクセス（ブラウザーから普通にウェブブラウジングするようなケースです）することができるのに、ある別の特定のホストはDNSが参照できずにホストが見つからないというようなことが起こっています。どうもIPv6とIPv4に関係している気がしてならないので、まずはIPv6ホストとして登録されているホストなのか確認する方法から紹介します。

IPv6アドレスをnslookupで調べるには
---
```shell_session
C:\>nslookup
DNS request timed out.
    timeout was 2 seconds.
既定のサーバー:  UnKnown
Address:  xxxx:xxx:xxxx:x::x
> set type=AAAA
> www.ibm.com
サーバー:  UnKnown
Address:  xxxx:xxx:xxxx:x::x

権限のない回答:
名前:    e7817.dscx.akamaiedge.net
Addresses:  2600:140b:d400:199::1e89
          2600:140b:d400:1a9::1e89
Aliases:  www.ibm.com
          www.ibm.com.cs186.net
          outer-global-dual.ibmcom-tls12.edgekey.net
```
のようにIPv6の結果を返すものもあれば下記のように返ってこないものもある。

```shell_session
C:\>nslookup
DNS request timed out.
    timeout was 2 seconds.
既定のサーバー:  UnKnown
Address:  xxxx:xxx:xxxx:x::x

> set type=AAAA
> yahoo.co.jp
サーバー:  UnKnown
Address:  xxxx:xxx:xxxx:x::x

名前:    yahoo.co.jp
```
これはIPv6アドレスが登録されているか否かによるので、IPv6でしか通信できない環境の場合、IPv4アドレスしか持たないホストへはアクセスできない。Windowsのバグ？なのかあるいはルーターでのトラブルなのかまだトラブルシューティングできていないのですが、IPv6のDNS情報は正しく保持されているのに、IPv4のDNS情報が欠落したりとか、VPNのせい？なのかWIFIをコネクションし直してDNS情報をRELOADすると正常になったりとか色々しています・・・。
ISPの回線をIPv6に切り替えてから起こるようになった気がするので、どこかに何か問題を抱えているのかもしれません。
Mac VisualStudioCode ターミナル起動時のshellをbashに設定する方法 2021-09-08現在

# 目的

- VisualStudioCode（以降VScode）のターミナル起動時のshellをbashに設定する方法をまとめる

# ご注意

- 本方法だとVScodeの初回起動時のターミナルはzshのままかもしれない。
- 本方法だと「VScodeを起動 → ターミナルを追加する」ときのみ新しいターミナルがbashで起動するようだ。
- 初回起動時のターミナルをbashにする方法を模索中。

# 方法

1. VScodeを起動する。
1. 画面右上の「Code」 → 「基本設定」 → 「設定」をクリックする。

    ![スクリーンショット_2021-09-08_23_21_41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/39338a0b-76ed-cd41-abf4-8378d9258b11.png)

1. 設定の検索部分に下記の内容を入力する。
    - `Terminal › Integrated › Default Profile: Osx `
1. ヒットした設定、`Terminal › Integrated › Default Profile: Osx `のプルダウンをクリックして「Bash」を選択する。
1. 即座に設定変更が反映されて、ターミナル起動時のshellが初期設定でbashになる。

# 設定してもなお、bashで起動しない時

1. VScodeのsetting.jsonを開き、下記のどちらかの記載がすでにされていた場合、コメントアウトしてみる。
    - `"terminal.integrated.shell.osx": "/bin/bash",`
    - `"terminal.integrated.automationShell.osx": "/bin/bash",`
グローバルIPとサブネットマスク スラッシュ表記 確認方法

## グローバルIP確認方法
以下のサイトにアクセスすることで確認できる。

https://www.cman.jp/network/support/go_access.cgi

## サブネットマスク スラッシュ表記 確認方法
### サブネットの確認
「コマンドプロンプト」→「ipconfig」→サブネットマスク確認
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/637823/2443c186-a7dd-98c3-0774-9621d4b49749.png)


https://www.ahref.org/subnet.php
https://www.cman.jp/network/term/subnet/p2/

### サブネット計算

|IPアドレス|192.168.1.65/28|
|----|----|
|ネットワークアドレス|192.168.1.64|
|ブロードキャストアドレス|192.168.1.79|

動画 19：30 頃


### ネットワーク構成図
|IPアドレス|192.168.10.33/28|
|----|----|
|ネットワークアドレス|192.168.10.32|
|ブロードキャストアドレス|192.168.10.47|

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/637823/159d7b4d-ee7d-2986-6475-f35c6fbaa5a9.png)
動画 22：08 頃

以下 youtube 参考
https://www.youtube.com/watch?v=uJ9bc85sHGg
【Rails】いいね順・投稿数順など　◯◯順の考え方・方法

# はじめに

https://qiita.com/mitsumitsu1128/items/18fa5e49a27e727f00b4

ランキング順で表示する際の考え方についてつまづいてしまったので、
考え方を残しておきます。

# 前提

投稿、いいね、タグ　それぞれの機能は作成済みとします。

#　方法

### 投稿に紐づくいいね順

まず、posts テーブルと　likesテーブルには、以下のようなデータが入っているとします。

![スクリーンショット 2021-09-08 22.38.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1902432/5abe6344-12a2-08fc-151d-7fea686c022e.png)

まずやることは、２つのテーブルを合体させます。

![スクリーンショット 2021-09-08 22.37.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1902432/5afc50bb-26a2-43fc-9455-59e5dac44d8c.png)

likesテーブルのpost_idとpostsテーブルのidは、同じになるはずです。

"postに紐づくいいねを、多い順に並べる"

↓

"likesテーブルのpost_idの数を集計し、多い順に並べる"

と言うことになります。

likesテーブルにはそもそもいいねをもらったpostのidが存在します。
likesのidは、いいねするたびに増えていきます。
post_idはlikesテーブルの中では重複するものであり、ひとつの投稿に着目すると、
その投稿がいいねされるたびにlikesテーブルの特定のpost_idは増えていきます。
だから、likesテーブルに存在するpost_idを数えれば、どのpostがどのくらいいいねをもらっているかがわかり、比較できるのです。

上記記事を参考に、

```ruby
 @posts = Post.find(Like.group(:post_id).order('count(post_id) desc').limit(3).pluck(:post_id))
```

と書くことができます。


```rb
#likeモデルのpost_idカラムを指定してまとめる
Post.find(Like.group(:post_id)
#post_idの多い順に並べる
.order('count(post_id) desc')
#並び替えたものの上位３つのpost_idを抽出する
.limit(3).pluck(:post_id))
```

### タグに紐づく投稿順

こちらも、いいねと同じ考え方でいけます。

まず、postとtagを結ぶpost_tagモデルが存在します。
今回行いたいのは、postに紐づくタグの中で、より多くのpostを保有するタグを順番に並べたいです。
簡単に言うと、よく使われるタグを順番に並べたいです。

post_tagテーブルと、tagテーブルは以下の通りです。

![スクリーンショット 2021-09-08 22.35.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1902432/fbbfd71f-b1cf-2b63-b401-815ecc6ecaf5.png)


これを合体します。


![スクリーンショット 2021-09-08 22.36.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1902432/fc19d872-5da0-9247-d8dc-572a8e5d147a.png)


今回は、post_tagテーブルのidと、tagsテーブルのtag_idが一致するはずです。
tagに紐づいているpostの数を比較したいので、
今回はpost_tagsテーブルの、tag_idの数を集計して、多い順に並べれば良いです。

postとtagのセットの情報が、post_tagsテーブルに保管されるので、
post_tagsテーブルのtag_idで頻出のタグは、より多くのpostに紐づいていると言うことです。


実際には、

```ruby
@tags = Tag.find(PostTag.group(:tag_id).order('count(tag_id) desc').limit(10).pluck(:tag_id))
```

と書くことができます。


```ruby
#Postモデルのtag_idカラムを指定してまとめる
Tag.find(PostTag.group(:tag_id)
#tag_idを数え数の多い順に並び替える
.order('count(tag_id) desc')
#並び替えたものの上位１０個のtag_idを抽出する
.limit(10).pluck(:tag_id))
```


いいねもタグも、最終的に複数のidをpluckで取り出しているので、
@posts, @tagsなどのインスタンス変数に格納し、ビューでeach文を使用してデータを一覧表示すればOKです。

https://pikawaka.com/rails/group

groupメソッドについても確認する必要がありました。


しっかり理解ができれば、色々簡単に応用ができます。
display:flexで横並びにした子要素の高さが変になってしまった時の対処法【HTML・CSS】

#display:flexで横並びにした子要素の高さが変になってしまった時の対処法
最近携わった案件で使用した、テクニックとして定着していなかった技術について。
これからHTML・CSSを学ぶエンジニアのヒントになればと思います。
##もくじ
#####1.　どういう問題が起きたか。
#####2.　問題に対する対処法。
#####3.　align-itemsについて。
#####4.　まとめ
##1.　どういう問題が起きたか。
・よくあるカードを横並びにしたやつ。上辺の基準として揃えたい。
・子要素に当たるカードは body に当たる部分に、全方向 padding 10pxを入れて、borderで囲みたい。
・子要素の高さはそれぞれ異なり、内容は変更される可能であるため、決め打ちではなくheight:autoを使いたい。

#####以上の条件を踏まえて、
親要素をdisplay:flexとし、子要素を横並びにすると、一番長い高さに揃えられてしまった。


<p class="codepen" data-height="300" data-default-tab="result" data-slug-hash="wvegmpB" data-user="shinendo" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/shinendo/pen/wvegmpB">
  </a> by ShinEndo (<a href="https://codepen.io/shinendo">@shinendo</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

##2.　問題に対する対処法。
####display:flexを指定した要素に「align-items:flex-start」を追加。
※上との違いをわかりやすくするためにborderの色をred → blue に変更しています。

<p class="codepen" data-height="300" data-default-tab="result" data-slug-hash="JjJEvKz" data-user="shinendo" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/shinendo/pen/JjJEvKz">
  </a> by ShinEndo (<a href="https://codepen.io/shinendo">@shinendo</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

無事、bodyの全方向に padding 10pxを含んだカードができた。

##3.　align-itemsについて。
####align-itemsは「flexboxの交差軸に対しての子要素の配置を決める」
※flex-direction:rowの場合はy軸方向の配置を。flex-direction:columnの場合はx軸方向の配置を。

flexboxのプロパティであるalign-itemsの初期値はalign-items:stretchである。
align-items:stretchはflexboxの子要素（flexboxアイテム）を伸縮する。

##4. まとめ
問題が生じた際、align-itemsの初期値がstretchであることを知らなかった。
というのも今まで、flexboxの子要素の高さは決め打ちしているものが多かったためである。
（あるいは、align-items:centerで中央揃えにしていた）

flexboxで複数個の子要素を並べる際は、align-itemsの指定をあらかじめしておくほうが、予期せぬ動きは避けられるだろう。

この機会に、align-items:flex-startを習得したい。
結構使うけどすぐ忘れる関数の使い方

##結構使うけどすぐ忘れる関数の使い方

phpの関数で使用頻度高いけど毎回ググってる関数の使い方を記憶に定着させるためにメモ。

#str_replace
特定の文字列を置換。

```php
$zip = "123-4567";
$zip = str_replace("-", "", $zip);
echo $zip;

//123456
```
郵便番号や電話番号などをDBに保存する際に”-”を除いた文字列で保存したい場合など。

私は悪い使い方として
古いプログラムを改修する際に誰が作ったか知らない俺俺関数で吐き出されたhtmlの一部だけ変更したい場合使ったりする。

```php
function OreOreFunction(){
    return "<p>俺俺俺俺だよ！</p>";
}

$text = OreOreFunction();//俺俺関数
$change_text = str_replace("だよ！", "ですよ！", $text);
echo $change_text;

//<p>俺俺俺俺ですよ！</p>
```

idをつけたい時などは

```php
function OreOreFunction(){
    return "<p>俺俺俺俺だよ！</p>";
}

$text = OreOreFunction();//俺俺関数
$change_text = str_replace("<p>", "<p id='oreore'>", $text);
echo $change_text;

//<p id='oreore'>俺俺俺俺だよ！</p>

```

本当はコードを読んでしっかり理解して関数の中身を理解した方がいいのだろうけど、そんな時間もないし古いコードの癖が強い関数のソースなんて興味が湧かない・・・

#explode,implode
特定の文字で文字列を切り分けて配列にしてくれる関数

```php
$ids = array();
$ids[] = 123;
$ids[] = 456;
$ids[] = 789;

$id_text = implode(",", $ids);

//どこかに渡す
//ゴニョゴニョされる
//返ってきた時

$id_array = explode(",", $id_text);
var_dump($id_array);
//array(3) { [0]=> string(3) "123" [1]=> string(3) "456" [2]=> string(3) "789" }

$id_text = implode(",", $id_array);
$SQL = "UPDATE SET ********* WHERE id IN({$id_text})";
var_dump($SQL);
//string(45) "UPDATE SET ********* WHERE id IN(123,456,789)"
```
データベースから抜き取ったレコードをどっかに渡してゴニョゴニョして返してもらう時、どのidのレコードを編集したか覚えておきたい時に使ったりした。

#strpos
文字列の中に特定の文字列が含まれるかどうか
こちらも初心者の頃にはどう役に立つかいまいち分からなかったな

注文番号やお問合せ番号などの業務的に使用する番号の頭にアルファベットなどをくっ付けて意味を持たすことはよくある話。
注文番号の一例
T００００１ /電話注文
F００００１　/FAX注文
W００００１　/WEB注文....

```php
$number = "F1234-001";

if(strpos($number, 'F') !== false){
    echo "FAX注文です！";
}elseif(strpos($number, 'T') !== false){
    echo "電話注文です！";
}elseif(strpos($number, 'W') !== false){
    echo "WEB注文です！";
}else{
    echo "???";
}

//FAX注文です！
```
なぜ条件式が　!==false なのかというと、先頭の文字列でマッチしてしまうと０を返してしまうから

```php
$number = "F1234-001";

if(strpos($number, 'F')){
    echo "FAX注文です！";
}elseif(strpos($number, 'T')){
    echo "電話注文です！";
}elseif(strpos($number, 'W')){
    echo "WEB注文です！";
}else{
    echo "???";
}

//???
```
Fが先頭にあるにも関わらず条件式の最後まで流れていってしまった
０文字目で見つかったから０を返したんですが
０はfalseと見なされてしまうので厳密チェックでしっかり判別しないといけないですね。

strposよく使うのですが、忘れがちなのが引数にどちらを入れていいか分からなくなることがよくある。
逆に入れてしまうともちろん上手く検索されませんので何か良い覚え方は無いものか・・・・・




疲れたので今回は以上です。
よく忘れる関数はまだまだあると思うのでちょくちょく追加していきます。
AtCoderログ：0105 - ABC 217 C

# 問題
https://atcoder.jp/contests/abc217/tasks/abc217_c

## 問題文
> $1,2,…,N$ が $1$ 回ずつ現れる長さ $N$ の数列を「長さ $N$ の順列」と呼びます。
> 長さ $N$ の順列 $P=(p_1,p_2,\ldots,p_N)$ が与えられるので、以下の条件を満たす長さ $N$ の順列 $Q=(q_1,\ldots,q_N)$ を出力してください。
> ・全ての $i (1 \le i \le N)$ に対して $Q$ の $p_i$ 番目の要素が $i$ である。
> ただし、条件を満たす $Q$ は必ずただ $1$ つ存在することが証明できます。

## 制約
> ・$1 \le N \le 2 \times 10^5$
> ・$(p_1,p_2,\ldots,p_N)$ は長さ $N$ の順列である。
> ・入力は全て整数である。

# 回答
## 回答1 (AC)
例を使って設問の意味を考えます。$N=3$ として $P=(3,1,2)$ という順列を考えると、これは $1$ を $3$ に、$2$ を $1$ に、$3$ を $2$ に置き換えるという意味で、$1 \to 3$, $2 \to 1$, $3 \to 2$ と表すことが出来ます。順列 $Q=(q_1,q_2,q_3)$ は置き換えた結果が $1,2,3$ になるので、
さきほどのルールの順番を入れ替えて $2 \to 1$, $3 \to 2$, $1 \to 3$ にすれば良いので、$Q=(2,3,1)$ となることがわかります。したがって、インデックスが $i$ のときに $p_i$ を読み込んだとすると、$q_{p_i}=i$ になる、つまりインデックス $p_i$ の値が $i$ になることがわかります。コードは以下のようになりました。

```C++:abc217c-1.cpp
#include <bits/stdc++.h>
using namespace std;

int main() {
  int n;
  cin >> n;

  vector<int> p(n);
  int t;
  for ( int i=0; i<n; i++ ) {
    cin >> t;
    p.at(t-1) = i;
  }

  for ( int i=0; i<n; i++ ) {
    cout << p.at(i)+1 << " ";
  }
  cout << endl;
}
```

# 調べたこと
## [AtCoder の解説](https://atcoder.jp/contests/abc217/tasks/abc217_c/editorial) → [公式解説](https://atcoder.jp/contests/abc217/editorial/2576)
回答1と同じ方針でした。

# リンク
## 前後の記事
- 前の記事 → [AtCoderログ：0104 - ABC 217 B](https://qiita.com/RoadLynton/items/031cddef375aecfc6559)
- 次の記事 → [AtCoderログ：0106 - ABC 161 C](https://qiita.com/RoadLynton/items/2da2409cf3d9fd7be195)
パケットの気持ちになってみる PART2: 〜 パケット送信編 〜

# PART2 Ethernetパケットを送ってみよう

なにやらネットワークを理解するためにパケットの気持ちになって考えるのが流行っているらしいので、実際にやってみたいと思います。PART2ではRAW Etherソケットを使って任意のパケットを送信し、それがネットワーク上に流れていることを確認することでパケットの操作のしかた学んでいきます。

| 出典         |         記事                  |
|:-----------:|:-----------------------------|
|日経XTECH     |[“パケットの気持ち”になってネットワークを理解する](https://xtech.nikkei.com/it/article/Watcher/20130902/501582/)|
|AWS          |[ネットワークの勉強方法を聞いてみた。](https://aws.amazon.com/jp/builders-flash/202106/way-to-learn-network/?awsf.filter-name=*all)

# 1 ツールの概要

- 機能概要:　2本のNICが同一LANに接続されているかどうかを確認するためパケットを送受信します
- 動作環境:　Linux CentOS8
- 開発言語:　C言語
- 関連技術:　RAW Etherソケット

# 2 処理概要
サンプルコード[ sndrcvpkt ](https://github.com/tanuki-project/Networking/blob/main/sndrcvpkt/)を用いて処理概要を説明します。
##2.1 ソケットの初期化
socket関数で送信用および受信用のRAWソケットを取得し、各々bind関数を用いて指定したNICに接続します。

|[sndrcvpkt.c: main()](https://github.com/tanuki-project/Networking/blob/main/sndrcvpkt/sndrcvpkt.c#L61-L78)|
|-|

## 2.2 パケットの送信
パケットを作成してwrite関数で送信用NICからパケットを送信します。
Ethenetの場合、ethhdr 構造体を送信バッファの0バイト目にマッピングし、宛先MACアドレス[6Byte]、送信元MACアドレス[6Byte]、フレームタイプ[2Byte]を設定することで送信パケットのヘッダ部分が生成されます。

|[sndrcvpkt.c: sndrcv_pkt()](https://github.com/tanuki-project/Networking/blob/main/sndrcvpkt/sndrcvpkt.c#L177-L201)|
|-|

## 2.3 パケットの受信
recvfrom関数で受信用NICでパケットを受信します。
受信したパケットは送信したパケットと内容が同じになっているはずです。

|[sndrcvpkt.c: sndrcv_pkt()](https://github.com/tanuki-project/Networking/blob/main/sndrcvpkt/sndrcvpkt.c#L203-L209)|
|-|

# 3 ツールの実行

## 3.1 事前準備

2本の同一LANに接続されたNICが用意できない場合は、検証用の veth pair を作成します。

```rb
$ sudo ip link add veth0-send type veth peer name veth0-recv
$ ip link show veth0-send
25: veth0-send@veth0-recv: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether 86:03:bb:2c:15:59 brd ff:ff:ff:ff:ff:ff

$ sudo ip link set veth0-recv up
$ sudo ip link set veth0-send up
```
## 3.2 ツールの起動
以下の書式でツールをルート権限で実行します。

```rb
$ ./sndrcvpkt {送信NIC名} {受信NIC名}
```

## 3.3 実行結果
パケットの送信と受信が成功すること、送信データと受信データが同じであることを確認します。

```rb
$ sudo ./sndrcvpkt veth0-send veth0-recv
write(3, 64) = 64
b612a50a5a46 8603bb2c1559 2020 0101
recvmsg(4) =  64
86:3:bb:2c:15:59 -> b6:12:a5:a:5a:46 type = 2020
b612a50a5a46 8603bb2c1559 2020 0101
write(3, 64) = 64
b612a50a5a46 8603bb2c1559 2020 0101
recvmsg(4) =  64
86:3:bb:2c:15:59 -> b6:12:a5:a:5a:46 type = 2020
b612a50a5a46 8603bb2c1559 2020 0101
write(3, 64) = 64
b612a50a5a46 8603bb2c1559 2020 0101
recvmsg(4) =  64
86:3:bb:2c:15:59 -> b6:12:a5:a:5a:46 type = 2020
b612a50a5a46 8603bb2c1559 2020 0101
^C
```

# 4 ツールのカスタマイズ

サンプルではでたらめなダミーパケットを送信していますが、ちゃんとしたIPパケットを作成して送信してみる、宛先をブロードキャストにしてみるなどして色々と試してみましょう。自分で手を動かして様々なパケットを作り出せるようになれば、自ずとパケットの気持ちもわかるようになるというものです。[(PART3に続く)](https://qiita.com/tanuki_mujina/items/bde70244b3ad816146e4)
パケットの気持ちになってみる PART1: 〜 パケットキャプチャ編 〜

# PART1 パケットの中身をのぞいてみよう

なにやらネットワークを理解するためにパケットの気持ちになって考えるのが流行っているらしいので、実際にやってみたいと思います。PART1ではパケットキャプチャを行うツールを自作し、サーバ上を流れるパケットをのぞいてみることでパケットの中身や解析方法をマスターするところから始めたいと思います。

| 出典         |         記事                  |
|:-----------:|:-----------------------------|
|日経XTECH     |[“パケットの気持ち”になってネットワークを理解する](https://xtech.nikkei.com/it/article/Watcher/20130902/501582/)|
|AWS          |[ネットワークの勉強方法を聞いてみた。](https://aws.amazon.com/jp/builders-flash/202106/way-to-learn-network/?awsf.filter-name=*all)

# 1 ツールの概要

- 機能概要:　指定したNICで送受信されるパケットを補足してパケットの概要を表示します
- 動作環境:　Linux CentOS8
- 開発言語:　C言語
- 関連技術:　RAW Etherソケット

# 2 処理概要

サンプルコード[ simple_pcap ](https://github.com/tanuki-project/Networking/blob/main/simple_pcap/)を用いて処理概要を説明します。

## 2.1 ソケットの初期化
　socket関数でRAWソケットを取得し、bind関数を用いて指定したNICに接続します。

|[pcap.c: init_socket()](https://github.com/tanuki-project/Networking/blob/main/simple_pcap/pcap.c#L83-L102)|
|-|

## 2.2 パケットの受信
　NICで受信したパケットをrecvmsg関数で一個ずつ読み取ります。

|[pcap.c: receive_packet()](https://github.com/tanuki-project/Networking/blob/main/simple_pcap/pcap.c#L125-L139)|
|-|

## 2.3 パケットの解析
　受信したパケットをイーサネット、IP、TCP/UDPといった順にヘッダを解析していきます。
　OSのヘッダファイルには各種プロトコルのヘッダ情報に対応する構造体が定義されています。
　それらと受信データを対応付けることでヘッダ情報の内容を読み取っていきます。

|[pcap.c: print_packet()](https://github.com/tanuki-project/Networking/blob/main/simple_pcap/pcap.c#L164-L203)|
|-|

　例えば、TCPでは以下のように受信データを対応付けることで内容の読み取りが容易になります。

- 受信データの 0～13バイト　→　ethhdr構造体にマッピング
- 受信データの14～33バイト　→　iphdr構造体にマッピング
- 受信データの34～53バイト　→　tcphdr構造体にマッピング

# 3 ツールの実行

## 3.1 ツールの起動
以下の書式でツールをルート権限で実行します。

```rb
./pcap {NIC名}
```

## 3.2 実行結果

ping等で通信を行います。

```rb
$ ping 192.168.10.1
PING 192.168.10.1 (192.168.10.1) 56(84) bytes of data.
64 bytes from 192.168.10.1: icmp_seq=1 ttl=64 time=1.17 ms
64 bytes from 192.168.10.1: icmp_seq=2 ttl=64 time=0.699 ms
64 bytes from 192.168.10.1: icmp_seq=3 ttl=64 time=0.723 ms
64 bytes from 192.168.10.1: icmp_seq=4 ttl=64 time=0.676 ms
^C
--- 192.168.10.1 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3086ms
rtt min/avg/max/mdev = 0.676/0.816/1.166/0.202 ms
```
受信したパケットの概要が表示されることを確認します。

```rb
$ sudo ./pcap enp0s25
ARP REQ(1) b8:ae:ed:72:34:c8/192.168.10.61 -> 0:0:0:0:0:0/192.168.10.1
ARP REP(2) 10:66:82:39:1a:30/192.168.10.1 -> b8:ae:ed:72:34:c8/192.168.10.61
192.168.10.61 -> 192.168.10.1 type = 0001
192.168.10.1 -> 192.168.10.61 type = 0001
192.168.10.61 -> 192.168.10.1 type = 0001
192.168.10.1 -> 192.168.10.61 type = 0001
192.168.10.61 -> 192.168.10.1 type = 0001
192.168.10.1 -> 192.168.10.61 type = 0001
192.168.10.61 -> 192.168.10.1 type = 0001
192.168.10.1 -> 192.168.10.61 type = 0001
ARP REQ(1) 10:66:82:39:1a:30/192.168.10.1 -> 0:0:0:0:0:0/192.168.10.61
ARP REP(2) b8:ae:ed:72:34:c8/192.168.10.61 -> 10:66:82:39:1a:30/192.168.10.1
^C
```

# 4 ツールのカスタマイズ

パケットキャプチャができることが確認できたら、表示内容をカスタマイズしたり対応するパケットの種類を増やしたりして、いろいろ試してみましょう。そうすれば、パケットの気持ちが少しはわかるようになるかもしれません。[(PART2に続く)](https://qiita.com/tanuki_mujina/items/48bec959ac8cbe859f5a)
【Rails】renderの基本形 / 省略形 / collection / local_assign.fetch

## 概要

今回はrenderに関して軽くまとめておく。(メモ)
基礎的なことだけど、見たときなんだ？？となったので。

## renderに関して

### 基本形

基本となる形は以下。

```erb
<%= render partial: "card", locals: {book: @book} %>
```

### 省略形

PF制作した際には下記を使用してた。
これは基本ではなく、省略形のパターンと知りました。

```erb
<%= render partial: "card", book: @book %>
```

ちなみに、省略ももっとあるらしい。

```erb
<%= render @books %>
```

実務で基本形を使用していた理由は、
locals: {} にしておくと、RubyMineの**補完**や**コードジャンプ**がより正確になるというメリットのため。

RubyMineは便利です。笑


### collection

collectionを使うことで
each文を使用せずに繰り返しの処理ができるみたい。


```erb
<%= render partial: "card", collection: @books =>
```

上記はつまり下記に書き換えれる。

```erb
<% @books.each do |book| %>
  <%= render partial: 'card', locals: {book: book} %>
<% end %>
```

このようにしてeach文をわざわざ書かなくてもeach文を走らせることができる。

### local_assigns.fetch

local_assigns.fetchを使用することによって特定の状況に限ってパーシャルに渡すことが可能です。
falseである時は表示がされない状況です。

```
local_assigns.fetch(:なんでもいい, false(or true))
```

以下の前提とした場合で説明します。

||books/index.html.erb| users/index.html.erb|
|:-:|:-:|:-:|
|_card.html.erbを|表示したい|表示したくない|

レンダリングされるファイル先でlocal_assigns.fetchをfalseに設定しておきます。

```erb:_card.html.erb
<% if local_assigns.fetch(:show_course, false) %>
  <%= render partial: "book_card", collection: books%>
<% end %>
```

表示したいbooks/index.html.erbファイルで、
_card.html.erbファイルをレンダリングする際に``locals: {show_course: true}``で**show_couser**を**ture**にします。

```erb:books/index.html.erb
<%= render partial: "card", collection: @books, locals: {show_course: true} %>
```


その逆で。表示したくないusers/index.html.erbなら、``locals: {show_course: true}``は入りません。

```erb:users/index.html.erb
<%= render partial: "card", collection: @books %>
```

※単純にlocalsに { show_course: false }とかでもいけると思います。

これに関しては、文献が全くないので先輩情報。ありがたや。

## 参考文献

https://railsguides.jp/layouts_and_rendering.html#render%E3%82%92%E4%BD%BF%E7%94%A8%E3%81%99%E3%82%8B

https://techtechmedia.com/partial-template-rails/
MAMPを使ってWordPressのローカル環境を作ってみる


https://qiita.com/mrd-takahashi/items/075540f357155888a51e

これの後を書こうと思って放置してた。
最近はWordPressもほとんど触っていないのだけど、ちょっと試したいことがあるので、そのついでに覚書を残すことにした。

Wordpressの環境だけをインストールするなら[LOCAL](https://localwp.com/)というお手軽ツールもあるんですが、まったく使ったことがないので今回は除外。

#環境
+ macOS Catalina(10.15.7)
+ [MAMP](https://www.mamp.info/) 6.3(最新ver.6.5 2021/09/05現在)
+ Wrodpress 5.8 (最新:2021/09/05現在)
+ アクセスするフォルダ：/Users/[username]/_data/localhost

6月頃から記事を書いては放置していたので、最新のMAMPはver6.5になっていたorz
念のため[最新バージョンのリリースノート](https://www.mamp.info/en/release-notes/mac/)を調べてみる(再び無駄な仕事増えたw)。
> + このバージョンでは、インテルとアップルのM1にそれぞれ最適化されたパッケージを提供しています。
> + 選択されたPHPキャッシュ設定が再び正しく保存されます。

今回の記事には関係ないので、アップデートせずにver6.3の環境のままにすることにします。
そして、わからなくなったらドキュメントに返れ→[MAMP (macOS) Documentation](https://documentation.mamp.info/en/MAMP-Mac/)

#前準備
生来めんどくさがりなので、「**最小限の力でWrodPressのローカル環境を作る**」が前提です。
まじめにやるなら「**hostsを書き換える**」事も発生しますが、**ポート番号でアクセス**するので、MAMPの設定だけ行います。

https://qiita.com/mrd-takahashi/items/98102064e58b9a226bc3

#phpMyAdminでDBを用意する
DBを用意します。
ローカルにインストールするので安直に```wp_local```というわかりやすい名前にしました。
[照合順序は公式でも明示](https://ja.wordpress.org/support/article/creating-database-for-wordpress/)している```utf8_general_ci```を使用して、「作成」ボタンをポチッと押下して完了。

![phpMyAdmin画面](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/44795/9502358b-cd9f-0a02-4f8a-54ec95bfd507.jpeg)

https://qiita.com/kazu56/items/6af85ffcf8d3954455ad

#Wordpressをインストールする
```wp-config-sample.php``` の中身を書いてインストールしてもよし、ウィザード使ってインストールしてもよし。
昔は中身を書いてインストールしてたんですが、手っ取り早くウィザードにお願いしちゃいます。
DLしたWordpressフォルダの中身を```/Users/[username]/_data/localhost```にまるっとコピーして、```http//localhost:50000```を叩くと以下のウィザードが始まります。
![WordPress セッティング](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/44795/ea67b3d7-1328-af67-d9fa-b90876b8509a.jpeg)

データベース接続のための入力項目は、MAMPのスタート画面に記載されています。
何も考えず、指示通りにコピペ。
![WordPress セットアップ](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/44795/0dfdd492-798b-1650-a384-255f563c3f74.jpeg)
粛々と入力していく。メールアドレスは保持しているものを記載。ローカルのテスト環境ですが、でたらめなメールアドレスの入力はNGです。
![WordPress インストール](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/44795/ace10b1b-efce-cdaa-0b63-a7130557c57b.jpeg)
例えば```hoge@hoge.com```とか```test@test.jp```なんて使うと大変なことになっちゃう可能性も。
hoge.comもtest.jpも所有者がちゃんといますw

https://qiita.com/jnchito/items/198a2561a36c2c2ef5e3

無事インストールとログインが済みました(スクショはなぜか6月w)。
デフォルトのテーマは**Twenty Twenty-One**みたいです。
![WordPress トップ](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/44795/73b7c24a-7784-e6e0-55ae-5ff47753748f.jpeg)

#公式ドキュメント
わからない時はドキュメントに戻る。日本語よりも本家の方が詳しい。
英語は苦手なんですが、現在は [Google 翻訳](https://translate.google.co.jp/?hl=ja)、[お試し翻訳｜AI自動翻訳『みらい翻訳』](https://miraitranslate.com/trial)、[DeepL](https://www.deepl.com/ja/translator)や、[英辞郎 on the WEB](https://eow.alc.co.jp/)・[英和辞典・和英辞典 - Weblio辞書](https://ejje.weblio.jp/)などのオンラインツールがあるので、どうにかなるはず。

+ [MAMP](https://documentation.mamp.info/)
+ [Wordpress](https://wordpress.org/)
+ [WordPress Codex](https://codex.wordpress.org/)
+ [Wordpress 日本語版](https://ja.wordpress.org/)
+ [WordPress Codex 日本語版](https://wpdocs.osdn.jp/Main_Page)
gcloudで cannot change locale のエラー

# エラー内容

macで以下のコマンドを打った時にエラー

``` zsh
$ gcloud compute scp --recurse ./hoge/* hogeserver:/etc/hoge
/etc/profile.d/lang.sh: line 19: warning: setlocale: LC_CTYPE: cannot change locale (UTF-8): No such file or directory
```

## 解決方法

**サーバー側の** */etc/environment* を開いて

``` zsh
vi /etc/environment
```

``` cannot change locale (UTF-8)``` と言われているので、以下を記述

``` txt
LANG=UTF-8
```

## 参考
https://stackoverflow.com/questions/22430747/locale-error-in-centos
https://akamist.com/blog/archives/665
[Ruby] 配列から特定の要素を 1 つだけ削除する

## やりたいこと

[Array#delete](https://docs.ruby-lang.org/ja/latest/method/Array/i/delete.html) や [Array#reject](https://docs.ruby-lang.org/ja/latest/method/Array/i/reject.html) を使って配列から任意の要素を削除する場合、その要素が重複している場合はすべて削除してしまう。

```rb
animals = ['🐈', '🐈', '🐕', '🐕', '🐈']
animals.delete('🐕') # destructive
#=> "🐕"
animals
#=> ["🐈", "🐈", "🐈"]

animals = ['🐈', '🐈', '🐕', '🐕', '🐈']
animals.reject { |animal| animal == '🐕' } # not destructive
#=> ["🐈", "🐈", "🐈"]
```

これを 1 要素のみ削除するようにしたい。

```rb
# こうしたい。
["🐈", "🐈", "🐕", "🐈"]
```

## 方法

[Array#index](https://docs.ruby-lang.org/ja/latest/method/Array/i/find_index.html) でインデックス値を取得し、その値を利用して要素を削除する。

```rb
animals = ['🐈', '🐈', '🐕', '🐕', '🐈']
animals.delete_at(animals.index('🐕')) # destructive
#=> "🐕"
animals
#=> ["🐈", "🐈", "🐕", "🐈"]

animals = ['🐈', '🐈', '🐕', '🐕', '🐈']
animals.reject.with_index { |_, i| i == animals.index('🐕') } # not destructive
#=> ["🐈", "🐈", "🐕", "🐈"]
```
AWS CloudTech ハンズオン1に必要な費用の調べ方についてまとめました

※2021.09.24更新



この記事を書いている私について

インフラ系PMO歴半年でAWSクラウドプラクティショナーを取ったばかりの私が
AWS CloudTechという動画学習サービスに参加し、AWSエンジニアを目指すための備忘録となります:fire:


この記事を書こうと思った経緯
---

構築の練習をしたいけど、実際どのくらい費用がかかるの！？と思い書きました。
AWS CloudTechで費用をまとめてくれていますが、AWSの料金体系は変わってきますし、
自分で確認したい初学者の参考になれば嬉しいです。



この記事で対象としている課題
---

AWSが公開している料金表を参考におおよその費用を算出します。

対象：AWS CloudTech ハンズオン1_基本的なブログサービスを構築する（シングル構成）





ハンズオン1の構成図
---

インターネットゲートウェイ、EC2、RDSとシンプルな構成です。

![AWS 課題1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/465906/d50478d0-a556-6654-44a1-9480b44f9770.png)



ハンズオン1の構築に必要な費用の一覧　（2021.9.8時点）
---


■EC2(オンデマンド)：Amazon Linux2 ＊無料利用枠あり
　タイプ:t2.micro

　 時間単価：＄0.0116
　 月額:＄0.0116 X 730H → ＄8.352

■データ転送

　 月あたりリクエスト数：25000
　 平均レスポンス(byte)：51200
　 1GBデータ量単価（OUT）：＄0.09
　 月額:(25000 X 51200 X  ＄0.09)/1000000000byte　→ ＄0.1152

■RDS(オンデマンド)
　タイプ:db.t2.micro

　 時間単価：＄0.017
　 SSDストレージ (GBあたり):＄0.115
　 GB量：5
　 月額:＄0.017 X 730H +　＄0.115 X 5　→ ＄12.985


 ◆月額(計):＄21.4522
 ◆時間単価（計）:＄21.4522 / 730H →　 ＄0.0294

　　　　　
　シンプルな構成なら時間当たり3円ほど、安いですね！


公式ページで見るべき場所の紹介
---

■Amazon EC2 オンデマンド料金
　https://aws.amazon.com/jp/ec2/pricing/on-demand/

■Amazon EC2 データ転送
　https://aws.amazon.com/jp/ec2/pricing/on-demand/

■旧世代インスタンスの価格　オンデマンド DB インスタンス
https://aws.amazon.com/jp/rds/previous-generation/



学習していない時に費用が発生しないようにするためには
---
■EC2(オンデマンド)→停止中にすること
料金は、インスタンスが開始してから、それが終了または停止するまでの、各インスタンスについて使用されるインスタンス時間当たりの料金となるため、使わないときは必ず停止中にしましょう。

(2021.09.24追記)
クラテクの黒川さん(@AwsskillC)に教えていただいたことを追記します。
・EC2は利用する時間（例：9:00〜18:00）のみ起動終了を制御するCloudWatch Eventsで作る仕組みもあり、
仕組みで強制するのもひとつの手になります。 
https://qiita.com/t-toyota/items/5d7455568f1856fd2f4e
→今のところ個人では使うことが無さそうですが、運用で有効活用できそうですね。

・EC2を停止していても、EC2にアタッチされているEBSボリュームを保持しているだけでわずかな料金がかるため注意しましょう。 https://aws.amazon.com/jp/premiumsupport/knowledge-center/ebs-charge-stopped-instance/
→これは本当に注意して欲しいので諸学所の方にはリンクも見ていただきたいです。
　スナップショットにも費用が発生するため注意が必要です。

■データ転送
Amazon EC2 受信 (イン)」/「送信 (アウト)」されるデータ転送量を基に請求されますので、データ転送が無ければ費用は発生しません。

■RDS(オンデマンド)
時間単位で、計算処理能力に対して料金が発生します。使わないときには必ず停止しましょう。

(2021.09.24追記)
クラテクの黒川さん(@AwsskillC)に教えていただいたことを追記します。
RDSは停止していても、1週間経過すると自動起動（！）するのでご注意ください。 
https://www.t3a.jp/blog/infrastructure/rds-auto-startup/
→これがあるので私はスナップショットを取っていたのですが、
　スナップショット保管にも費用が掛かるので、気にされる方はご注意ください。


最後に
---
想定していない料金が発生しないよう気けて
これからも勉強していきたいと思います。

内容に不備がございましたら、ご指摘いただけますと幸いです。

この記事はAWS初学者を導く体系的な動画学習サービス「AWS CloudTech」の課題カリキュラムで作成しました。
https://aws-cloud-tech.com

(2021.09.24追記)
本記事はクラテク黒川さん(@AwsskillC)にチェックいただいており、その際に下記のWEBサービスを使用して注釈を入れられたページのリンクを黒川さんから共有してもらいました。

・Webアノテーション
　https://web.hypothes.is/

　注釈を共有するのにとても便利なツールですね。素晴らしいです。
　私も取り入れてみようと思います！
【Rails】アソシエーションでnilを許可する

# はじめに

SNSサイトを作成中、投稿にキャンプサイト（未入力or１件まで）を紐付けようと思い、以下の通りにしたところ、
キャンプサイトが未入力時に、バリデーションで弾かれ、投稿ができなかっため、対処法を残しておきます。

# 前提

### Postモデル
- user_id
- image_id
- camp_site_id(FK)
- body

### CampSiteモデル
- site_name


# やりたいこと

キャンプサイトの登録は任意で、Postを投稿したい

#やったこと

```ruby:camp_site.rb
has_many :posts
```

```ruby:post.rb
belongs_to :camp_site, optional: true
```

"optional: true" がなかったために投稿時のcamp_site_idのnilが許可されず
エラーが表示されていました。

belongs_toが成り立たなくなるため、通常では、nilは許可されないそうです。
そのため、"optional: true"を記載してあげて、nilを許可する必要があります。

# 終わりに

深く理解していなかったために、初歩的な段階でつまづいてしまいました。
１から復習すること、些細なことでも調べ直すことが大切だと痛感しました。
【SAPIUI5】SuccessFactors連携時のOdata注意点

SuccessFactors等の拡張機能として、SAP BTP上でFioriのアプリを構築し、
OdataAPIを通じてSuccessFactorsと連携する場合の考慮点

## アプリごとに使用するEntityを絞る

manifest.jsonに記載のdatasourceの内容

**特に考慮しない場合**

```
"mainService": {
    "uri": "https://api10preview.sapsf.com/odata/v2",
    "type": "OData",
    "settings": {
        "odataVersion": "2.0",
        "localUri": "localService/metadata.xml"
    }
}
```

**例）Userのデータに絞る場合**

```
"mainService": {
    "uri": "https://api10preview.sapsf.com/odata/v2/User",
    "type": "OData",
    "settings": {
        "odataVersion": "2.0",
        "localUri": "localService/metadata.xml"
    }
}
```

**例）複数のEntityを参照する場合**

```
"mainService": {
    "uri": "https://api10preview.sapsf.com/odata/v2/User,EmpJob",
    "type": "OData",
    "settings": {
        "odataVersion": "2.0",
        "localUri": "localService/metadata.xml"
    }
}
```

となります。
Controllerにてアクセスする場合は

```
oDataModel.read("/User");
```

等でアクセスが可能になります。

実際のURLは

```
https://api10preview.sapsf.com/odata/v2/User,EmpJob/User
```

へアクセスされますが、問題なくデータが取得できます。


**注意点**

* 参照するEntityはかならず記載する
* $expand等で子階層のデータを参照する時も必ず子階層のEntity情報を記載する

SuccessFactorsの標準Entity等により、metadaのサイズが大きくなりがちかと思います。
多くのEntityを参照するアプリでも必ず指定するようにしましょう。

## 基本は1,000件のレスポンス

フィルター条件等を指定しても、最大1,000のレスポンスとなります。
\$top,\$skipを使用してPagingの考慮を行いましょう。

また、1001件以上データが存在する場合は、__nextのデータが存在します
__nextに記載されたURLへアクセスすると1001件目～のデータ取得できます。

こちらを使用してページング処理を行う場合はURLのパラメータとして
paging=snapshotを指定するようにしましょう

参考ページ
https://help.sap.com/viewer/b2b06831c2cb4d5facd1dfde49a7aab5/2105/en-US/12fe2115d865471f9e4b91d55ccfe51a.html


## その他

* bathでまとめてリクエス可能件数は180リクエスト
* upsertの可能件数は1000件

等

CDSビュー等で構築したoDataとは異なる独自の制限があることに注意しましょう
Googleフォームの回答を画面キャプチャで共有するときに便利なスクリプト

ちょっとした小技です。

Googleフォームの回答ですが、グラフが見やすいので、そのまま画面キャプチャして共有したいですよね。

ただ、回答数が多かったり、長い回答があるとテキストが見切れてしまうのが難点です...。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6760/7bbe2d74-d101-db71-0379-3ae495e761a5.png)

そんなときは、Chromeなら[Ctrl+Shift+I](https://developer.chrome.com/docs/devtools/open/#console)で開発者ツールのコンソールを開いて、次のスクリプトを実行します。

```javascript
javascript:(function(){
  document.querySelector('#tJHJj').remove();document.querySelector('.freebirdFormeditorViewResponsesSummaryResponsetrackerRoot').remove();
  for (const e of document.querySelectorAll('.m2 .freebirdCommonAnalyticsTextResponsesContainer')) {
    e.style.maxHeight = '1200px';
  }
})();
```

すると、スクロールバーが消えていい感じになります。ついでに、画面キャプチャーしたときに邪魔になるヘッダーと、個人情報となるメールアドレスも消しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6760/ebdf07cd-1e26-88bf-b086-d4789b35785f.png)

あとは、Chromeなら開発者ツールで[コマンドメニュー](https://developer.chrome.com/docs/devtools/command-menu/)を使い、```Ctrl+Shift+P```のあとに```full```を入力して、フルサイズの画面キャプチャーをすればOKです！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6760/649087c1-d3d6-4673-a381-5962f1ad72da.png)
laravel ドット記法を使用して配列やオブジェクトから値を取得する

# 目的

- laravelのヘルパーメソッドである`data_get()`を使い、ドット記法で配列やオブジェクトから値を取得する方法をまとめる

# 方法

- 下記のように記載することで配列やオブジェクトから任意の値を取得する事ができる。

    ```php
    data_get(配列やオブジェクト, ドット記法での値の指定);
    ```

# 参考文献

- [https://readouble.com/laravel/6.x/ja/helpers.html#method-data-get](https://readouble.com/laravel/6.x/ja/helpers.html#method-data-get)
VScode + Remote Containers + Anaconda3ベースDockerコンテナのJupyternotebook上で日本語のグラフを描く

# 背景
- データ分析環境を手軽にスクラップアンドビルドしたくてDockerを導入したのだが、SeabornやMatplotlibで日本語フォントを使ったグラフがかけなくて困っていた。

- 一般的にWeb上にある解決法ではなぜか全く解決しなかった（Windows上のNativeのAnaconda環境でSeabornの日本語設定するなど）。

- 最終的な解決方法がすごくスマートだったので記念に記録

# 環境
- ホストOS：Windows10 + WSL2
- Docker desktop, base image: continuumio/anaconda3
- 上記イメージを普通にdocker run
- VScodeのRemote Containersで上記コンテナにリモート接続
- VScodeで立ち上げたターミナルで色々どうにかしようとした

# 方法
- こういったページの方法を試して、手動でフォントをダウンロードしたりmatplotlibの設定を変えたが全然だめだった

https://qiita.com/keisuke-nakata/items/2309764d21438645f6b9

https://www.linuxtut.com/en/3b4d0f3e5514c9893d89/

```python:sample.ipynb
import matplotlib.font_manager as fm
fm.findSystemFonts(fontpaths=None, fontext='ttf')
# これでなぜか空のリストが返されてフォントが無いことになっている
```

- 以下のページを読んでいて、「確かにそもそもコンテナの中に最初からフォントがインストールされてるといいのでは？」と思いつく

https://stackoverflow.com/questions/20753782/default-fonts-in-seaborn-statistical-data-visualization-in-ipython

- おなじみ「もみじあめ」さんのサイトにたどり着く

https://blog.amedama.jp/entry/ubuntu-available-fonts

- 実際には以下の通りの手順

## Dockerfileの作成

```Dockerfile:Dockerfile
# ベースイメージ
FROM continuumio/anaconda3

# 必要なフォントのインストール
# 最初のapt updateは必須
RUN apt update
RUN apt -y install fonts-ipaexfont
RUN apt autoremove -y
RUN apt clean -y

# 作業ディレクトリの作成
WORKDIR /project_dir
```

## 日本語フォント入りのAnaconda3イメージの作成

```bash:VScodeのシェル上で以下のコマンド
# Dockerfileのおいてあるディレクトリで実行
docker build -t anaconda_with_jpfont .

```

## コンテナの作成
```bash:VScodeのシェル上で以下のコマンド
docker run -it --net='host' --mount type=bind,src="/mnt/c/Users/自分のユーザー名/マウントしたいフォルダ",dst=/project_dir anaconda_with_jpfont
```

## VScodeからコンテナへリモート接続

ここ見て

https://qiita.com/sho-hata/items/02ad47f67bce6816a69a#remote-container

## ipynb内での作業

```python:sample.ipynb
# 心配な人はこちらで実際にインストールされているフォントを確認
import matplotlib.font_manager as fm
fm.findSystemFonts(fontpaths=None, fontext='ttf')
# ['/usr/share/fonts/opentype/ipaexfont-gothic/ipaexg.ttf',
# ...
# '/usr/share/fonts/truetype/fonts-japanese-gothic.ttf']
# みたいな

# ないしはこちらで確認
import matplotlib as mpl
font_paths = mpl.font_manager.findSystemFonts()
font_objects = mpl.font_manager.createFontList(font_paths)
font_names = [f.name for f in font_objects]
print(font_names)
# ['IPAexGothic', 'IPAexMincho', 'IPAexMincho', 'DejaVu Serif', 'DejaVu Sans', 'DejaVu Serif', 'DejaVu Sans Mono', 'DejaVu Sans', 'DejaVu Sans Mono', 'IPAexGothic']

# seabornのipynb内での使用フォントを設定してから描画
sns.set(font='IPAexGothic') 
sns.barplot(x, y, data)

# matplotlibのフォントを設定してから描画
import matplotlib as mpl
mpl.rcParams['font.family'] = 'IPAexGothic'
plt.plot(x, y)
plt.xlabel('日本語！')
```

ipynbの最初に毎回指定するのは手間ですが、matplotlibrcやseabornの.pyファイルを書き換えないで良いので環境を破壊せず再現性もあり、スマートだと思いました。




#
railsチュートリアル 第４章 rubyにおけるクラス

###Rubyにおけるクラス
Rubyではあらゆるものがオブジェクト
この節では実際にオブジェクトをいくつか定義してみる。
メソッドをまとめるのにクラスを使っています
クラスからインスタンスが生成されることでオブジェクトが作成

####コンストラクタ
これまで示した多くの例の中でも、クラスを使ってオブジェクトのインスタンスを作成してきたのです。
例えばダブルクォートを使って文字列のインスタンスを作成しました。
これは```文字列のオブジェクトを暗黙で作成```するリテラルコンストラクタです。

```rb
>> s = "foobar"       # ダブルクォートは実は文字列のコンストラクタ
=> "foobar"
>> s.class
=> String
```
文字列がclassメソッドに応答しており、その文字列が所属するクラスを単に返している。
明示的に同等の名前付きコンストラクタを使う。
名前付きコンストラクタは、```クラス名に対してnewメソッド```を呼び出す。

```rb
>> s = String.new("foobar")   # 文字列の名前付きコンストラクタ
=> "foobar"
>> s.class
=> String
>> s == "foobar"
=> true
```
リテラルコンストラクタと等価ですが、動作の内容が明確に示されています
```配列でも、文字列と同様にインスタンスを生成できます ```。

```rb
>> a = Array.new([1, 3, 2])
=> [1, 3, 2]
```

ハッシュの場合は若干異なる。
Array.new は配列の初期値を引数に取りますが、
Hash.new はハッシュのデフォルト 値を引数に取る。
これは、キーが存在しない場合のデフォルト値です。

```rb
>> h = Hash.new
=> {}
>> h[:foo]            # 存在しないキー (:foo) の値にアクセスしてみる
=> nil
>> h = Hash.new(0)    # 存在しないキーのデフォルト値をnilから0にする
=> {}
>> h[:foo]
=> 0
```
メソッドがクラス自身に対して呼び出されるとき、このメソッドをクラスメソッド
クラスのnewメソッドを呼び出した結果は、そのクラスのオブジェクトであり、これはクラスのインスタンスとも呼ぶ。
lengthのように、インスタンスに対して呼び出すメソッドはインスタンスメソッドと呼ぶ。

####演習
１.リテラルコンストラクタとは？
隠れてインスタンスを作成
``` newメソッドを使わずインスタンスを作成すること```かな。

２.Rangeクラスとnewメソッドを使って、1から10の範囲オブジェクトを作れ。

```rb
>> Range.new(1,10)
=> 1..10
```
３.比較演算子==を使って、上記２つの課題で作ったそれぞれのオブジェクトが同じであることを確認しろ。

```rb
a = Range.new(1,10)
=> 1..10
>> b = 1..10
=> 1..10
>> a == b
=> true
```
【HTML&CSS】id属性とclass属性の使い分け方

#はじめに
プログラミング学習を始めた当初、idとclassってどういう使い分けをしてるんだろうって思っていました。
同じように思っている初学者がいると思うので、簡単に説明していきます。

#id属性
同じwebページ内で一度しか同じ値を使えません。
その代わり、class属性に優先してスタイルをつけることができます。
CSSに書く際は、頭に「#」を付けて記述します。

```html:HTML
<div id="id名">hello</div>
```

```css:css
#id名{
   color: red;
}
```

・同じ値は一度しか使えないので、これはダメ！

```html:HTML
<div id="hello">hello</div>
<div id="hello">こんにちは</div>
```

#class属性
idと違い、同じwebページ内で何度も同じ値を使うことができます。
CSSに書く際は、頭に「.」を付けて記述します。

```html:html
<div class="class名">hello</div>
```

```css:css
.class名{
   color: red;
}
```

・同じ値を何度でも使えるので、これはオッケー！

```html:HTML
<div class="hello">hello</div>
<div class="hello">HELLO</div>
<div class="hello">こんにちは</div>
```

#おわりに
基本的にはclass属性を使い、ピンポイントでスタイルを適用したいようなときがあればid属性を使えば良いでしょう。
PHP if文でfalseと判定される値

# 目的

- PHPのif文でfalseと判定される値をまとめる

# if文の式でfalseと判定される値

1. 下記のPHP公式ドキュメントのif文の記載に下記のように記載されている。
    - [https://www.php.net/manual/ja/control-structures.if.php](https://www.php.net/manual/ja/control-structures.if.php)
1.  「どのような値が false と評価されるかについては論理値への変換 を参照してください。」とあるのでリンク先を見てみる。
    - [https://www.php.net/manual/ja/language.types.boolean.php#language.types.boolean.casting](https://www.php.net/manual/ja/language.types.boolean.php#language.types.boolean.casting)
1. ドキュメントによると下記の値がif文でfalseとして扱われるようだ。
    - booleanの`false`
    - integerの`0`
    - floatの`0.0`および`-0.0`
    - 空の文字列、および文字列の`0`
    - NULL(値がセットされていない変数も含む)
    - 属性がない空要素から作成された SimpleXML オブジェクト。

![PHP__論理型__boolean__-_Manual.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/aee19a99-da4b-ec11-1557-6f724d7faa14.png)


# 参考文献

- [https://www.php.net/manual/ja/control-structures.if.php](https://www.php.net/manual/ja/control-structures.if.php)
- [https://www.php.net/manual/ja/language.types.boolean.php#language.types.boolean.casting](https://www.php.net/manual/ja/language.types.boolean.php#language.types.boolean.casting)
iOSのLINEの内蔵ブラウザが、POSTフォームのtarget=_blankで挙動が狂う件

# 概要
iOSのLINEブラウザの挙動がおかしくてはまりました。

# 現象

環境：iOSのLINEとYahooの内蔵ブラウザ（ほかにもあるかもしれない。）
条件：POSTメソッドのformタグで、target="_blank"が指定されている
結果：formのaction先には遷移するが、formのパラメータはすべて抜け落ちる

# 再現

## 1. コード

```html:html01.html
<html>
	<head>
		<meta charset="UTF-8" />
		<style type="text/css">
			div {
				border: 1px solid black;
				margin-top:10px;
			}
		</style>
	</head>
	<body>
		<div>
			targetなしPOSTフォーム
			<form action="./php01.php" method="POST">
				<input name="name" type="text" value="kumanobori" />
				<input name="type" type="text" value="kuma" />
				<input type="submit" />
			</form>
		</div>

		<div>
			targetありPOSTフォーム
			<form action="./php01.php" method="POST" target="_blank">
				<input name="name" type="text" value="kumanobori" />
				<input name="type" type="text" value="kuma" />
				<input type="submit" />
			</form>
		</div>
	</body>
</html>
```

```php:php01.php
<?php
echo 'REQUEST_URI: ' . $_SERVER['REQUEST_URI'] . '<br />';
echo 'POST:' . mb_ereg_replace("\r|\n", '', print_r($_POST, true)) . '<br />';
```

## 2. html表示
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/282159/0b385350-1f5d-bcc0-ddc9-5af57d0ef7ff.png)

## 3. 正常な結果（iOSのLine内蔵ブラウザのtargetありの場合以外）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/282159/03ef903f-ac09-de70-a2b6-9158ea6fe600.png)

## 4. 異常な結果（iOSのLine内蔵ブラウザのtargetありの場合）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/282159/0847f2ff-1424-1c92-419e-c59772eefe22.png)

# 回避策
User-Agentで判定して、 `target="_blank"` の出力有無を制御しました。

# その他

## 試した環境（以下は全部セーフ）
iOS slack内蔵
iOS chrome
iOS Safari
android Yahoo内蔵
android LINE内蔵
android chrome

## 上記コード以外で試した条件（以下は全部セーフ）
GETメソッドのformの `target="_blank"`
aタグの `target="_blank"`

## ポエム
LINEブラウザの挙動がおかしいってことで調査したのですが、こんな使い古したオプションで挙動が狂うとは思わず、思い切りはまりました。。。

2021.09.10 追記
スマホのLINE内蔵ブラウザだと「新しいウィンドウ」という概念がないわけで、そこに事情があるんだろうなという気はしなくもないです。
でもandroid版はちゃんとしてるしー。（新しいウィンドウは開かないけど、ちゃんと遷移する）
で、Yahooアプリに至っては内蔵ブラウザがマルチタブ対応してるからそこに事情を斟酌する余地すらないしー。
WatchDogTimerを作る

# WatchDogTimerを作る

## 仕様
- 日向で寛いでいる（という設定の）犬を撫でる！
- 一定時間撫でられなかった犬は，次に撫でようとしたときに吠えてくる
- 一度吠えた犬は二度と撫でさせてくれない


## コード
```Python:watchdog.py
from threading import Event, Thread


class WatchdogBark(Exception):
    pass

class Watchdog(Thread):

    def __init__(self, interval):
        Thread.__init__(self)
        self.interval = interval
        self.watching = True
        self.petted = Event()
        self.bark = False

    def stop(self):
        self.watching = False

    def pet(self):
        if self.bark:
            raise WatchdogBark()
        self.petted.set()

    def run(self):
        while self.watching:
            self.petted.wait(self.interval)
            if not self.petted.is_set():
                self.bark = True
                break
            self.petted.clear()

```

### 使い方
```Python:watchdog_sample.py
import random
import time

from watchdog import Watchdog, WatchdogBark


def main():
    # 3秒ごとに撫でないと怒っちゃう犬
    dog = Watchdog(3)
    dog.start()

    try:
        for t in range(20):
            print()
            # なんか処理 (ここではランダム時間sleep)
            processing_time = random.random() * 4
            print(f"{t+1}番目の処理は{processing_time:.1f}秒かかりそう...")
            time.sleep(processing_time)
            print("犬を撫でる！")
            dog.pet()
            print("撫でても吠えられなかった！！")
    except WatchdogBark:
        print("吠えられた.")
    else:
        print("吠えられることなく処理しきれました.")
    finally:
        dog.stop()
        dog.join()

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

## 感想
この犬，永遠に放置しても撫でようとしなければ吠えてこないぞ！！！！
（自分から吠えるようにするにはsignal使った割り込みをしましょう．）