OmniAICreator/Qiita-1.07M huggingface dataset:
X-Frame-Options で複数ドメインを指定する方法

基本的に1つしか指定できないので、条件に応じて出し分ける必要がある。

```security.conf
# 正規表現でマッチしたURLを環境変数に入れる
SetEnvIf Referer "^(https:\/\/*\.example\-1\.com)/.*" X_FRAME_OPTIONS_ALLOWED=$1
SetEnvIf Referer "^(https:\/\/*\.example\-2\.com)/.*" X_FRAME_OPTIONS_ALLOWED=$1

# マッチしなかった場合(環境変数に値がない場合)はDenyを指定する
Header set X-Frame-Options Deny env=!X_FRAME_OPTIONS_ALLOWED
```

# 追記

`ALLOW-FROM` はChromeやFirefoxが対応していないそうなので削除しました。
PWAに対応しようとしてハマった事

# PWAって意外と簡単にできる？
何を持ってpwaなのか少し曖昧ですが、serviceworkerを動かしてホーム画面に追加して起動がネイティブっぽくなるのは本当に簡単にできました。

### 参考記事
https://qiita.com/narikei/items/4240f03542f29e313989
https://qiita.com/hirotakasasaki/items/e3fb638b4f75a92bce8b

# PWAにした目的
運営しているサービスがユーザーさんの利用頻度がそこそこ高いサービスで、WEBサービスなのですが、たまにユーザーさんから「どうやったらインストールできるの？」や「導線がメールなので不便」のような声をたまに頂いておりました。
なので「Androidだけでも良いからブラウザで開いた時に【ホーム画面に追加する】の表示を出したいね。」と言う事になり、実装する事になりました。
ネイティブ的な動きをしたいからという目的ではありません。笑


# ハマった事

個人的には２つハマりポイントがありました。
### nginx周り

一つ目はnginx周りでハマりました。
ローカルでは問題なく動いていたのにstagingにあげると

```
Failed to register a ServiceWorker: A bad HTTP response code (404)...
```

みたいなエラーが出てきました。
どうやら/serviceworker.jsにアクセスするとnginxが404を返してる模様。
ローカルでアクセスすると問題なく表示されるんだけどね。。

cloudfrontを利用している影響でしょうか？
色々調べましたが、まだ原因はわかってません。。
nginx.confに下記を追記してあげると読み込んでくれるようになったのでとりあえあず良しとしました（良くない）

```

location = /serviceworker.js {
         proxy_pass http://localhost:3000/serviceworker.js;
         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
         proxy_set_header Host $http_host;
         proxy_set_header X-CSRF-Token $http_x_csrf_token;
         proxy_set_header X-Real-IP $remote_addr;
         proxy_set_header X-Forwarded-Proto 
         $http_cloudfront_forwarded_proto;
         proxy_redirect off;
        }
```

### iOSだと戻るボタンが無くなる問題に関して

次にiOSだと戻るボタンが無くなる問題に関してです。
アプリケーションを起動した時の画面の表示を設定できるオプションがありまして

- fullscreen(全画面表示)
- standalone（ステータスバーのみが表示されるネイティブアプリと同じような画面表示）
- browser(ブラウザと同じ表示)

上２つだとネイティブっぽくなるのですが、iosだとブラウザの戻るボタンが無くなるんですよね。

「iOSでホーム画面に追加している人がいたら致命的に使いにくいよな」と思い調べてみると、standaloneかfullscreenモードにして戻るボタンをつけましょうとのこと。
いや、pwaで起動されたかをチェックして全画面に戻るボタンつけるのって結構面倒じゃないですか？？

仕方ないのでbrowserモードにしてみたところ、iOSの方では戻るボタンが復活したのですが、なんとAndroidで【ホーム画面に追加する】という表示が消えるではありませんか！！！

ブラウザモードだったらホーム画面に追加しなくても良いよね？とchromeが判断しているのでしょうか。。あちらを立てればこちらが立たず。どうしようかな〜と思っていたら。


minimal-uiというオプションがもう一つある事を発見。（今更）
動きはbrowserの挙動と近いですが、iosでは戻るボタンも表示されるし、Androidでは【ホーム画面に追加する】という表示も出てきて当初の目的を達成できました。
めでたしめでたしです。


# PWAを触ってみた感想
ほんの触りの部分だけしか触ってませんが、他にもpush通知（Androidのみ）などの機能もあり可能性を感じました。
まだサービスの検証が終わってないスタートアップ系の企業はガンガン改善してリリースしての繰り返しになるので、プラットフォームの審査などがあるネイティブアプリよりWEBの方が相性が良いと思ってます。
WEBの表現がリッチになっていくのは嬉しい限りです。
時間がかかるコマンドを GNU parallel で 並列実行する

LibreOffice の `soffice` コマンドを使うと Word ファイルから文章を抽出してテキストファイルに変換できるのですが、遅いです。

変換対象の Word ファイルが 100 ファイルぐらいあったので手軽に bash の `for f in *.doc *.docx` で回したのですが遅すぎたので GNU Parallel を使って並列化しました。

```bash
$ parallel -j 5 soffice --headless --convert-to txt {} --outdir ./text ::: *.doc *.docx
```

`-j 5` で 5 ファイルずつ変換されます。
go初心者がunittestを書く

goを始めようと思い、unittestの書き方をまずは理解しようと思ったときの備忘録。

## ディレクトリ配置

```bash
~/dev/work/go/src
% tree
.
├── funcs.go
├── funcs_test.go
└── main.go
```

## ファイルの内容

### main.go

```go
package main

import (
	"fmt"
)

func main() {
	a := int32(1)
	b := int32(2)
	fmt.Println(Sum(a, b))
}
```

### funcs.go

```go
package main

func Sum(a int32, b int32) int32 {
	return a + b
}
```

- 単に `1 + 2 = 3` という計算を、関数 `Sum` でやっているだけ
- `Sum` は、 `引数a` , `引数b` 　を足して、結果をリターンするだけ

GOPATHを `~/dev/work/go` に通っている状態にして、試しに実行するとこうなる。

```bash
% cd ~/dev/work/go/src
% go run main.go funcs.go
3
```

まぁ、 `3` と出る。

ちなみに、 `go run` には引数で指定したファイルしか渡らないので、 `go run main.go` だけだと `func Sum` が存在しない旨のエラーが出る。（というのもgoのノウハウとして学んだ)

で、次にunittest。
同ディレクトリに `funcs_test.go` があるのでその内容。

### funcs_test.go

```go
package main

import (
	"testing"
)

type Provider struct {
	a   int32
	b   int32
	exp int32
}

func TestSum(t *testing.T) {

	providers := []Provider{
		Provider{1, 2, 3},
		Provider{2, 4, 6},
		Provider{3, 5, 8}}

	for i := range providers {
		testSum(t, providers[i])
	}
}

func testSum(t *testing.T, p Provider) {
	actual := Sum(p.a, p.b)
	exp := p.exp

	if actual != exp {
		t.Errorf("actual %v\nwant %v", actual, exp)
	}
}
```

CakePHPのProvider的にやりたかったので、こんな感じにしてみた。
ポイントと、goに慣れてない自分的に苦労したのは以下の点。

1. goだと、 `TestXXX` という関数がテストとみなされるようなので、内部で呼ぶものは `testXXX` とした

2. テストデータを一括で複数渡したかったが、そのやり方に苦労した。具体的には、
    - ProviderというStructを宣言
    - そのSliceを作り、初期値にa, b, exp(期待値)をセット
    - `TestSum` のループ内で、 `testSum` を呼び、その際Providerの内容を１つずつ渡す
    - actual(実際の計算結果)と、exp(期待値)に差がある場合、 `t.Errorf` を呼ぶ。これが assertEqualsに相当

もっといいやり方があるような気がする。。。。

実際に実行してみる。

```bash
% cd ~/dev/work/go/src
% go test funcs.go funcs_test.go
ok  	command-line-arguments	0.005s
```

ためしに、 `Provider{3, 5, 8}` の部分を `Provider{3, 5, 9}` とかに変えてみるとこうなる。
(3 + 5 = 9というデータにしてみたことになる）

```bash
% go test funcs.go funcs_test.go
--- FAIL: TestSum (0.00s)
    funcs_test.go:33: actual 8
        want 9
FAIL
FAIL	command-line-arguments	0.007s
```

ちゃんとアサーションがコケた。（まぁ当然・・・）

とりあえずここまではわかったので、次はモックの方法を学んでまた書いておく。
LocalStorageを利用する際に気をつけること

# localStorageのサポート状況、利用可能であることを確認する
localStorage を使用する前に、現在のブラウザーセッションでサポート済みかつ使用可能であるかをはじめに確かめる。
例えば、利用しているブラウザでCookieの利用をOffにしているブラウザや、一部のプライベートブラウズ（AndroidのWebViewでWebStorageを有効にしていない時など）でそのまま利用するとエラーとなる。

localStorage がサポート済みかつ使用可能であるかを検出する関数は[公式](https://developer.mozilla.org/ja/docs/Web/API/Web_Storage_API/Using_the_Web_Storage_API)でも公開されている。

```javascript

function storageAvailable(type) {
	try {
		var storage = window[type],
			x = '__storage_test__';
		storage.setItem(x, x);
		storage.removeItem(x);
		return true;
	} catch(e) {
        return e instanceof DOMException && (
            // everything except Firefox
            e.code === 22 ||
            // Firefox
            e.code === 1014 ||
            // test name field too, because code might not be present
            // everything except Firefox
            e.name === 'QuotaExceededError' ||
            // Firefox
            e.name === 'NS_ERROR_DOM_QUOTA_REACHED') &&
            // acknowledge QuotaExceededError only if there's something already stored
            storage.length !== 0;
    }
}

// 利用例
if (storageAvailable('localStorage')) {
	// localStorage をちゃんと使用できます
} else {
	// 残念ながら localStorage は使用できません
}
```
上記例では、localStorageに対し、ダミーで値をセットし削除を行うことで、正常に処理が終了した際にはtrueを、問題が起きた際はExceptionに処理を流すことで判定を行なっている。

localStorageに値をsetしたり、getするさいに都度呼ぶのが面倒なときはsetやget自体を関数にしすることで上記同様エラーを回避することができる。

``` javascript
function setItem(key, value) {
    try {
        localStorage.setItem(key, value);
    } catch(e) {
        // 戻り値は任意
        return  false;
    }
}

function getItem(key) {
    try {
        localStorage.getItem(key);
    } catch(e) {
        // 戻り値は任意
        return  false;
    }
}

setItem('hoge', 'fuga');
let item = getItem('hoge');
if (!item) {
    console.log(item); // false
} else {
    console.log(item); // fuga
}
```
aws

# EC2
- filter search

Below will find "web1", "web41", "web444" instances which have that tag "Name".

```
tag:Name : ^web[0-9]*
```

# CloudWatch
- decrypt CloudWatch Logs Message Event
    - https://docs.aws.amazon.com/lambda/latest/dg/services-cloudwatchlogs.html
    - `cat event.json | jq -r .awslogs.data | base64 -D | gzip -d | jq .`

# scripts
- get specific EIP's Name

```
ips=(
xx.xx.xx.xx
yy.yy.yy.yy
)

for ip in ${ips[@]}
do
echo -ne "$ip\t"
echo $(aws ec2 describe-addresses --filters "Name=public-ip,Values=${ip}" | jq '.Addresses[].Tags[] | select(."Key" == "Name").Value  ')
done
```
新版AWS認定試験のソリューションアーキテクト アソシエイト（SAA）に合格した話

Qiitaやネット上にこの手の記事は溢れてはいるのですが、とはいえ自身もそういった記事に大変助けられたので書いておきます。

# AWS経験歴

- 過去に1年ほどAWSで運営されているサービスの運用を行う
- それ以外は趣味でLightsailやLambdaをちょこっと使うぐらい

# やった教材

**合格対策 AWS認定ソリューションアーキテクト - アソシエイト**
![](https://images-fe.ssl-images-amazon.com/images/I/51uTIvkpfiL._SL500_SS75_SS75_.jpg) 
大塚康徳(日立インフォメーションアカデミー) 
固定リンク: http://amzn.asia/d/iDh8Pxi

- 3回通読（問題も毎回解く）
- 情報が古くなっているとこは適宜自分で補完して頑張る

**Amazon Web Services 基礎からのネットワーク&サーバー構築 改訂版**
![](https://images-fe.ssl-images-amazon.com/images/I/51KpOuWyhYL._SL500_SS75_SS75_.jpg)
玉川憲 
固定リンク: http://amzn.asia/d/g3Ud209

- 2回通読

**手を動かしながら2週間で学ぶ AWS 基本から応用まで**
Kei Kanazawa
固定リンク: https://www.udemy.com/share/100taiAkMdcllaRXo=/

- 10日ほどで一通り視聴
- 実際に動画を見つつ構築するのは、セクション8まで（それ以降はRoute53なども絡んできて、時間がかかりすぎると思ったため）
- VPCやサブネットの理解はこのコースをやることで一番理解できました。感謝。

**AWS クラウドサービス活用資料集**
![](https://d1.awsstatic.com/events/jp/ed_jp_blackbelt_logo.81ff6f508d2f0f1a512b5ef0abf8499d4a9d2b8d.png)
固定リンク: https://aws.amazon.com/jp/aws-jp-introduction/

- いわゆるBlackbeltセミナー
- 全部の資料に対して、資料の最初から最後まで目を通すのは無理なので、最初の方の概要説明部分を中心に広く浅く読む
    - 興味が強いサービスは全部読む


**AWS模擬試験**

- 2回受験、ただし私の場合は2回とも同じ問題（順番は違う）だった
- あくまで本番試験のユーザーインターフェースに慣れておくために受験するのを推奨


# やらなかった教材

- 「AWS認定試験 問題集」で出てくる問題集サイト
    - すべての問題を解けるプランを契約したのですが、実際にはほとんどやりませんでした
    - 問題数が多すぎて、中途半端にしかできなさそうだったので
- ホワイトペーパーを読むこと
    - アソシエイトレベルでは必要ないと判断したため
- AWSトレーニングクラス
    - そんなお金はなかった

# 試験申し込み～受験まで

- 意外とすぐに日程が埋まる可能性が高い
    - 最初申し込んでいた日程からずらそうと思って、試験日の2～3週間前に他の日程の確認をしたら結構埋まっていて諦めました
    - 私のように遠方へ試験へ行く（福井から名古屋）場合は、鉄道の切符も含めて事前に取ってしまうと思うので、気をつけてください
- 受験会場によっては、早めに受けられる
    - 名古屋の会場で受けたのですが、1時間前にいったら、50分ほど早めに受けることができました
    - また会場で勉強するスペースもあるので、無駄に喫茶店とかで時間潰すよりはさっさと行ったほうが良いと思います

# 受験

- 時間はめちゃくちゃたっぷりあります、焦らず解きましょう
- 択一/複数選択 試験なので、ぶっちゃけ知識が多少不足しても推測で選択できます。諦めずにがんばりましょう

# 受験後

- 合否を確認できる場所
    - 受験会場のPC（受験で使用したやつ）に、受験終了後に表示
    - AWS トレーニングと認定ポータル

この2つだけです。旧版だと受験完了のメールにも合否が書いてあったようですが、新板だと特に記載はありません。また、ポータルで見れるまでにはタイムラグがあります（2018/11現在だと最大営業日5日以内らしいですが、私の場合は営業日1日目で見れるようになりました）。

ポータルで見れるようになるまでは受験会場のPCで表示された合否だけが知りうる情報になります。不安かもしれませんが待ちましょう。

# 感想

合格できて良かったです。私の場合、試験を受けに行くためだけでも交通費が1万円程度かかり、試験料と合わせて3万円弱の試験を受けに行くイメージだったので（笑

ネットの情報によっては長文の問題がなくなったなどと書かれていたりしますが、私の場合はかなりガッツリ長文問題が出ました。なので、出題傾向はその時その時で違うのではないかと思います。

AWS認定試験で一番不安なのは、本番試験の情報が全然ないことだと思います。自分が知らないサービスの問題（もしくは知っているサービスの未知の機能・制約）が出てきたらどうしようという不安は私もすごく感じていました。しかし実際受けてみたら、知らない情報が出てくる問題でも、既存の知識と合わせて推測すればわかる（選択肢を絞り込める）問題も多かったです。そういう点では知識が不足していてもなんとかなります。これから受験される方、諦めずに頑張ってください。
AugurにJavaScriptで接続する

Augurには「[augur.js](https://github.com/AugurProject/augur.js)」というJavaScriptのライブラリが提供されており、短いコードで簡単に接続して情報を取得する事が可能です。
APIリファレンスが[公式ドキュメント](https://docs.augur.net/#api-overview)にあります。

# カテゴリ一覧を取得するサンプルコード

以下は、アプリ経由でAugurに接続し、カテゴリ一覧を取得するサンプルプログラムです。
アプリを起動してテストネット(rinkeby)に接続してから、nodeで下記を実行します。

```Javascript
// Augurアプリを使ってローカルに接続ポイントを作り、テストネットのカテゴリ一覧を取得する。
// 実行前にAugurアプリを起動し、テストネットに接続しておくこと。
// by jansnap https://qiita.com/jansnap

var Augur = require("augur.js");
var augur = new Augur();

// メインネット
// http://127.0.0.1:8080/?augur_node=ws%3A%2F%2F127.0.0.1%3A9001&ethereum_node_http=https%3A%2F%2Fgethnode.com%2Fhttp&ethereum_node_ws=wss%3A%2F%2Fgethnode.com%2Fws#/categories
//var ethereumNode = { http: "https://gethnode.com/http", ws: "wss://gethnode.com/ws" };
//var augurNode = "ws://127.0.0.1:9001";

// テストネット(rinkeby)
// http://127.0.0.1:8080/?augur_node=ws%3A%2F%2F127.0.0.1%3A9001&ethereum_node_http=https%3A%2F%2Frinkeby.augur.net%2Fethereum-http&ethereum_node_ws=wss%3A%2F%2Frinkeby.augur.net%2Fethereum-ws#/categories
var ethereumNode = { http: "https://rinkeby.augur.net/ethereum-http", ws: "wss://rinkeby.augur.net/ethereum-ws" };
var augurNode = "ws://127.0.0.1:9001";

// connect
function connect(){
  return new Promise((resolve,reject) => {
    augur.connect({ ethereumNode, augurNode }, (err, connectionInfo) => {
      resolve();
    })
  })
}

// get address set from an Augur Node.
function getSyncData(){
  return new Promise((resolve,reject) => {
    augur.augurNode.getSyncData(
      function(error, result) {
        console.log('getSyncData result=%o', result);
        resolve(result['addresses']['Universe']);
      });
  })
}

// get categories.
function getCategories(universe){
  return new Promise((resolve,reject) => {
    augur.markets.getCategories({
      universe: universe,
      sortBy: "popularity",
      isSortDescending: true
      }, function (error, categories) {
        console.log('getCategories result=%o', categories);
        resolve();
      });
  })
}

connect()
.then(() => getSyncData())
.then((universe) => getCategories(universe));

```


大まかな流れは、以下の通りです。

- augur.jsの読み込み

- [augur.connect](https://docs.augur.net/#augur-connect-connectoptions-callback)関数でAugurに接続

- [augurNode.getSyncData](https://docs.augur.net/#augur-augurnode-getsyncdata-callback)関数で[universe](https://docs.augur.net/#universe)関数のIDを取得

- 取得した[universe](https://docs.augur.net/#universe)を使って、[markets.getCategories](https://docs.augur.net/#augur-markets-getcategories-p-callback)関数でカテゴリ一覧を取得

APIリファレンスにはサンプルコードも載っているので、接続できてしまえば色々できそうです。


# JavaScriptの書き方を教えてください。。。

カテゴリを取得した後、Ctrl-Cで止めないと終了しません。しかし、用事がすんだらdisconnectして終了したいです。
プログラムの最後に「process.exit(0);」を入れると、connectする前に終了してしまいました。
分かる方いらっしゃいましたら、コメントいただけますとありがたいです。
ビッグデータをPythonとApache Arrowで扱ってみた

# はじめに

業務でビッグデータを扱うことになり、データの読み込みとプログラム間の受け渡しに課題が出てきたことから、なにか良いアイディアがないかなと思っていたらApache Arrowというのがあるとご紹介頂いたので試してみた。

# 試してみたこと

機械学習のためにデータを読み込むのですが、ある特定のCSVデータを繰り返し読み込む必要があるので、このCSVをApache Arrow形式に変換して保存し、ファイルの読み込みを高速化できるか試してみる。

# pyarrow

PythonでApache Arrowを使用するには、pyarrowというライブラリを使うらしい。
これは、以下のコマンドでインストール。

```
pip install pyarrow
```

# CSVデータ読込

キー、日時、値の3つの列があるデータが100,000件入っていて、サイズが42MBのファイルが10個あり、以下のコマンドでCSVファイルの読み込み時間を計測。

```py:read_csv.py
import glob
import pyarrow as pa
from time import time

files = glob.glob('data/*.csv')

df_org = pd.DataFrame()

start_time = time()

for file in files:
    df_org = pd.concat([df_org, pd.read_csv(file)])
        
df_org = df_org.reset_index(drop=True)

print("read csv : {0}s".format(time() - start_time))
```

上記スクリプトを実行すると以下の結果となりました。

```
read csv : 13.279139041900635s
```

# Arrowデータ書出

```py:
import glob
import pyarrow as pa
from time import time

files = glob.glob('data/*.csv')

df_org = pd.DataFrame()

for file in files:
    df_org = pd.concat([df_org, pd.read_csv(file)])
        
df_org = df_org.reset_index(drop=True)

start_time = time()

table = pa.Table.from_pandas(df_org)
record_batch = table.to_batches()

with pa.OSFile("pw.arrow","wb") as sink:
    
    schema = record_batch[0].schema
    writer = pa.RecordBatchFileWriter(sink, schema)
    writer.write_batch(record_batch[0])
    writer.close()
    
print("write arrow : {0}s".format(time() - start_time))
```

上記のスクリプトを実行すると以下のとおり。

```
write arrow : 7.5772552490234375s
```

# Arrowデータ読込

書き出したArrowデータを読み込んでみる

```py:
import pyarrow as pa
from time import time

start_time = time()

reader = pa.RecordBatchFileReader("pw.arrow")
rb = reader.get_record_batch(0)
df_aw = rb.to_pandas()

print("read arrow : {0}s".format(time() - start_time))
```

上記のスクリプトを実行すると以下のとおり。

```
read arrow : 3.1053459644317627s
```

# 考察

1,000,000件のデータをCSV形式から読み込んだ場合とArrow形式(?)で読み込んだ場合の比較

|形式|時間[s]|
|---|---:|
|CSV|13.28|
|Arrow|3.11|

...圧倒的ですね(^_^;)

CSV形式とArrow形式でのファイルサイズの比較

|形式|サイズ[MB]|
|---|---:|
|CSV|420|
|Arrow|600|

...約1.4倍ですね(^_^;)

この差をどう捉えるかですが、新しく入ってきたCSVデータを一回Arrow形式に変換して保存することにより、次からの読込時間は圧倒的に早くなりそうですね。

とりあえず、今の仕事では反応速度の方が重要なので、Arrow形式を使ってデータを保存してみたいと思います。

以上、現場からきむらが報告いたしました。
varとletとクロージャーとブロックスコープと・・・

##はじめに
ES7,ES8はもちろん、ES6でもいまだに「へぇ〜」と思うことが出てきて
自分の理解の「狭さ」や「浅さ」に驚くが、めげずに備忘録をつけていこうと思う
それに公の場にさらしておけば、親切な人が間違いを指摘してくださることもあるし
##変数参照の勘違い問題を解決するlet
以下は言い古され感のある初心者がやりがちな有名なサンプルですね

```Javascript
const operations = []	
for (var i = 0; i < 5; i++) {
  operations.push(() => {
    console.log(i)
  })
}
for (const operation of operations) {
  operation()                          // 5 5 5 5 5
}
```
昔の解決策としては以下のようになります

```Javascript
const operations = []
for (var i = 0; i < 5; i++) {
  (i => {
    operations.push(() => {
      console.log(i)
    })
  })(i)
}
for (const operation of operations) {
  operation()                         // 0 1 2 3 4
}
```

ES6以降では以下のようにすれば問題を回避できる

```Javascript	
const operations = []	
for (let i = 0; i < 5; i++) {
  operations.push(() => {
    console.log(i)
  })
}

for (const operation of operations) {
  operation()                         // 0 1 2 3 4
}
```
単に var を let にしただけ。
やりがちな問題としての原因が、直感的に間違いのコードを書いてしまうわけだから
そのとおりの記述でvarをletにするだけで修正できるのはいいことだと思います

##理解の確認とポイント
たとえばブロックスコープの説明を見ただけで、このことを連想できるのは難しいと思う。
ということはその他のことについての理解も表面的なことにとどまってしまってる
可能性があるということで、１つ１つもう少し掘り下げていかないとな〜とおもいます
で、少しだけブロックスコープについて確認した内容をクロージャといっしょにまとめます
間違いなどあればご指摘いただければと思います

####クロージャー
内側の関数は外側の関数のスコープへの参照を持っているので、
外側の関数のプライベート変数にアクセスでき、また、
外側の関数は内側の関数から参照されている変数の参照カウントをゼロにせずに
GCに持ってかれないように保持しているのがクロージャーの仕組み
ポイントは内側の関数が持ってるのは外側の関数の変数の参照であり値ではないこと

####ブロックスコープ
いくつか見てみましたが一番スッと理解出来た文が以下でした。
上記のfor文の変数 i について、サンプルとともに掲載されてたものです
>This works because on every loop iteration i is created as a <font color="Red">new variable </font>
each time, and every function added to the operations array gets its own 
<font color="Red">copy of i.</font>　　　　　　　　　　　　　　　　　　　　　[参照元へ](https://medium.freecodecamp.org/the-complete-javascript-handbook-f26b2c71719c)

要は昔の解決策のIIFEの引数の役割をそのまましてくれてるわけですね。
この文は、この件に限らず、ブロックスコープの少し深めの理解につながったかなと思います。
SlackでBotに何か喋らせたくて、Rubyでコード書いてたら、いくつもエラーが出たけど、奮闘してたらやっと動いた話。

#やりたかったこと

**Slackで誰かの発言に反応してくれるBotを作る**…！！
すぐできると思ったら意外と手こずってしまった

#Botを動かすための手順

##①SlackのBot作成

Bot作成は意外と簡単

詳しくは[ブラックPEOPLEくん（Slack Bot）との熾烈な戦い](https://qiita.com/kurararara/items/f25afe5fc87da328776a)へどうぞ

##②ターミナルでGemをインストール


```
$ gem install slack-ruby-client
```
として`slack-ruby-client`をインストールする

```
Fetching: gli-2.18.0.gem (100%)
Successfully installed gli-2.18.0
Fetching: hashie-3.6.0.gem (100%)
Successfully installed hashie-3.6.0
Fetching: slack-ruby-client-0.13.1.gem (100%)
Successfully installed slack-ruby-client-0.13.1
Parsing documentation for gli-2.18.0
Installing ri documentation for gli-2.18.0
Parsing documentation for hashie-3.6.0
Installing ri documentation for hashie-3.6.0
Parsing documentation for slack-ruby-client-0.13.1
Installing ri documentation for slack-ruby-client-0.13.1
Done installing documentation for gli, hashie, slack-ruby-client after 1 seconds
3 gems installed
```
となればインストール完了！

##③記事を参考にコードをコピペして貼り付ける！笑

[rubyでslack apiを叩いてbotを動かす](https://qiita.com/saekis/items/aa049c1630db04e4263d)を参考にしつつコピペどーん！

```Ruby.rb
require 'slack-ruby-client'

  Slack.configure do |conf|
    conf.token = 'xoxb-*****************'
  end

  # RTM Clientのインスタンス生成
  client = Slack::RealTime::Client.new

  # slackに接続できたときの処理
  client.on :hello do
    puts 'connected!'
    client.message channel: 'your_channel_id', text: 'connected!'
  end

  # ユーザからのメッセージを検知したときの処理
  client.on :message do |data|
    if data['text'].include?('こんにちは')
      client.message channel: data['channel'], text: "Hi!"
    end
    if data['text'].include?('かしこい') || data['text'].include?('えらい')
      client.message channel: data['channel'], text: "Thank you!"
    end
    if data['text'].include?('おやすみ')
      client.message channel: data['channel'], text: "Good night"
    end
  end

  # Bot start
  client.start!
```
ちなみに`xoxb-*****************`の部分は
bot作成後の以下の画面の下の方のTOKENを書けばOK
<img width="1071" alt="スクリーンショット 2018-11-19 11.03.37.png" src="https://qiita-image-store.s3.amazonaws.com/0/260056/535b3bb4-7a21-3a9c-2b2e-811895ac7db9.png">


##④ターミナルで実行


あとは

```
$ ruby bot.rb
```
とターミナルに打ち込む

うまくSlackと繋がればターミナル上に

```
connected!
```
と表示される

##⑤動いた！！！！！

<img width="704" alt="スクリーンショット 2018-11-19 10.52.27.png" src="https://qiita-image-store.s3.amazonaws.com/0/260056/aeb7607d-25e7-6a09-d317-61e6069938a5.png">


動きましたああ

嬉しい

#参考にした記事

[ブラックPEOPLEくん（Slack Bot）との熾烈な戦い](https://qiita.com/kurararara/items/f25afe5fc87da328776a)
[rubyでslack apiを叩いてbotを動かす](https://qiita.com/saekis/items/aa049c1630db04e4263d)
[slack-ruby-client/hi.rb at master · slack-ruby/slack-ruby-client]
(https://github.com/slack-ruby/slack-ruby-client/blob/master/examples/hi_real_time/hi.rb)
Windows10で実行ファイルへのパスを通す手順

#はじめに
パスを通す手順をいつも忘れるのでメモ。そして記憶定着のため。

#ステップ1
「システムのプロパティ」ウィンドウを表示させます。

- 「Windowsキー + s」を押して、表示された検索ボックスに「システムの詳細」と入力します。

<img src="https://qiita-image-store.s3.amazonaws.com/0/245438/4cddb5d6-a8a2-1a06-5601-f145cf9d4727.png" title="" alt="image.png" width="60%" height="" >

- 「システムの詳細設定の表示」という項目が検索結果に表示されるので、選択します。すると、「システムのプロパティ」ウィンドウが開きます。

<img src="https://qiita-image-store.s3.amazonaws.com/0/245438/7850dda3-0f7f-0607-0505-627089dbc498.png" title="" alt="image.png" width="60%" height="" >

#ステップ2
表示された「システムのプロパティ」ウィンドウでは、すでに「詳細設定」タブが選択されていると思います。

- 「環境変数」というボタンがあるのでクリックすると、以下のウィンドウが表示されます。

<img src="https://qiita-image-store.s3.amazonaws.com/0/245438/8cdc87b0-384d-ff2a-3664-fa1b14487905.png" title="" alt="lkadjfsadfasdf.png" width="60%" height="" >

- 「ユーザーの環境変数」のほうで「Path」を選択して「編集ボタン」を押します。
<img src="https://qiita-image-store.s3.amazonaws.com/0/245438/72a72b4b-fe41-8267-ff04-5ec07bdf4b6c.png" title="" alt="clipboard_20181119105554.png" width="60%" height="" >
- 「新規」ボタンを押して**パスを通したい実行ファイルが存在するフォルダのパス**を入力し、「OK」ボタンを押します。実行ファイルのパスではいけません。
<img src="https://qiita-image-store.s3.amazonaws.com/0/245438/b2178e15-98e9-a952-1d15-09e644158a01.png" title="" alt="image.png" width="60%" height="" >

#確認
さて、これで`C:\Program Files (x86)\NuGet`フォルダへのパスを通したので、このフォルダ内にある実行ファイルをコマンドラインからファイル名だけで実行することができるようになります。

試してみます。

1. [nuget.org](https://www.nuget.org/downloads)からNuGetのコマンドラインインターフェース`nuget.exe`をダウンロードします。
2. それを`C:\Program Files (x86)\NuGet`にコピペします。
3. コマンドラインで`nuget`と入力しまてEnterを押します。

<img src="https://qiita-image-store.s3.amazonaws.com/0/245438/270c5ed3-1cbd-9ffa-2f61-f457349c0109.png" title="" alt="dfalksdjfa.png" width="60%" height="" >

ファイル名だけで実行できました。
IngressでBasic認証を使いたい時のメモ

IngressでBasic認証を使うとき用のメモ。
ほぼ、以下ページのトレースです。

[Basic Authentication - kubernetes/ingress-nginx - github](https://github.com/kubernetes/ingress-nginx/tree/27c863085e2503e5f3249b596f24529fc2488baa/docs/examples/auth/basic)
[Basic Authentication - NGINX Ingress Controller](https://kubernetes.github.io/ingress-nginx/examples/auth/basic/)

# 設定の流れ

## htpasswdでパスワード生成
```shell
$ htpasswd -c auth foo
New password:
Re-type new password:
Adding password for user foo
```

## secretの作成
```shell
$ kubectl create secret generic basic-auth --from-file=auth -n $namespace
secret/basic-auth created
```

## 作ったsecretの確認
```shell
$ kubectl get secret basic-auth -o yaml -n $namespace
apiVersion: v1
data:
  auth: Zm9vOiRhcHIxJExQdVB2NmpTJEFObWV4NVloUmIwT1JGYXE0TjVYaS8K
kind: Secret
metadata:
  name: basic-auth
  namespace: $namespace
type: Opaque
```

## マニフェストの作成
```ingress.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: ingress-with-auth
  annotations:
    # 認証タイプを指定
    nginx.ingress.kubernetes.io/auth-type: basic
    # 作成したsecret名を指定
    nginx.ingress.kubernetes.io/auth-secret: basic-auth
    # 認証時のメッセージ。なくても良さげ
    nginx.ingress.kubernetes.io/auth-realm: "Authentication Required - foo"
spec:
  rules:
  - host: foo.bar.com
    http:
      paths:
      - path: /
        backend:
          serviceName: http-svc
          servicePort: 80
```

## デプロイ
```shell
kubectl create -f ingress.yaml
```

以上で、Basic認証が有効になっているはずです。

# 参考ページ
[Basic Authentication - kubernetes/ingress-nginx - github](https://github.com/kubernetes/ingress-nginx/tree/27c863085e2503e5f3249b596f24529fc2488baa/docs/examples/auth/basic)
[Basic Authentication - NGINX Ingress Controller](https://kubernetes.github.io/ingress-nginx/examples/auth/basic/)
SQLアンチパターン勉強会　第九回：ラウンディングエラー（丸め誤差）

# はじめに  

本エントリーは某社内で実施するSQLアンチパターン勉強会向けの資料となります。

本エントリーで書籍「[SQL アンチパターン](https://www.oreilly.co.jp/books/9784873115894/)」をベースに学習を進めます。書籍上でのサンプルコードはMySQLですが、本エントリーでのサンプルコードはT-SQLに置き換えて解説します。

# ラウンディングエラーとは
　実数を2進数でエンコードして格納する浮動小数点数のデータ型を利用すると数値に丸めが発生し、正確な数値を格納できません。また、丸めが発生した状態で連続で計算を行うことで、誤差累積が大きくなります。特に積の計算で誤差が大きくなります。
　このような計算の典型が、金融システムにおける利子の複利計算になりますので、金融アプリケーションでは正確な値を使うことが極めて重要です。

# 有限精度・無限精度
　3分の1のような有理数は0.3333…のような循環小数で表す場合、正確な値は少数では表現できません。桁数とは数の精度であり、循環小数では無限精度が必要になります。
　この問題への妥協策は、可能な限りオリジナルに近い値、例えば0.333などの有限精度の値を使用することです。しかしこの妥協策は丸めの振る舞いによる誤差を生み出します。
1/3 + 1/3 + 1/3 = 1
0.333 + 0.333 + 0.333 = 0.999

# アンチパターン：FLOATデータ型を使用する
　SQLのFLOATデータ型は、IEEE754標準にしたがって実数を2進数形式でエンコードします。IEEE754では、浮動小数点数を2進数形式で表現します。2進数形式で無限精度が必要な値は、10進数で表現される値とは異なります。
　10進数では有限精度で表せる値、例えば59.95を2進数で正確に表すには、無限精度が必要です。FLOATデータ型で無限精度は扱えないため、2進数形式で格納できる近似値を使います。その近似値を10進数形式で表すと、59.950000762939になります。
つまりデータベースのFLOAT型に格納している値は、全て有限精度で表されているとは限らず、全ての値が丸められている可能性を想定しなければなりません。


# アンチパターンの見つけ方
FLOAT型,REAL型を利用しているデータベースとなります。


# アンチパターンを用いてもよい場合
FLOATは、INTや、NUMERICなどのデータ型がサポートするよりも広い範囲の実数を扱う場合に適しています。
FLOATが最適なものの代表例は、科学技術計算を行なうアプリケーションです。


# 解決策：NUMERIC　データ型を使用する
FLOATやREALなどのデータ型の代わりに、NUMERICやDECIMALを用いて、固定制度の小数点数を表すようにしましょう。


## 最後に
出来る限りFLOAT型は避けましょう。
WindowsのCtrlキーとAltキーを入れ替える

#目的
普段Mac(USキーボード)を使っている人が，Windowsを操作する際に思うこととして「Command(⌘)+Cでコピーをしたいけど，⌘の場所とAltの場所が違うのが嫌」であることがあげられると思います。
そこで，Windowsにおいてスペースキーにより近いAltキーをCtrlキーにすることでCommandキーと同じ感覚で使えるようにするための裏技(?)です。
#実証環境
- Windows10
- USキーボードを使用(Logitech K780)

#方法

レジストリエディタを書き換える

1. Win+Rで「ファイル名を指定して実行」を起動し，`regedit`と入力→レジストリエディタの起動
2. `HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Keyboad Layout`の順に辿る
3. 「編集→新規→バイナリ値」
4. ファイル名を「Scancode Map」にする
5. ファイル名の上で右クリック→修正
6. 以下のように入力する

    ```
    00 00 00 00 00 00 00 00
    05 00 00 00 1D 00 38 00
    38 00 1D 00 1D E0 38 E0
    38 E0 1D E0 00 00 00 00
    ```

7. 再起動する

#最後に
レジストリの変更は時にやばいことになります。
自己責任で操作してください。
Androidで画面にツールチップみたいなのを表示する

# やりたいこと

ツールチップ Tooltip、バルーン balloon、ヒント Hint、吹き出し、とかいろいろ呼んでるけど
このボタン押して欲しいときに画面になにか出したいときは
レイアウトにそれ用のViewを追加してVisibleをGoneとかで制御するのがお手軽だけど
それではちょっと対応できない局面で試した方法をφ(..)メモメモ

# decorviewを使う

decorviewについてはレイアウトの最上流の親分みたいなの。そんな感じの。
なので一番上に重なって表示されるの。そんな感じなの。

```java
        ViewGroup decorView = (ViewGroup) activity.getWindow().getDecorView();
        LayoutInflater inflater = LayoutInflater.from(activity);
        View tooltip = inflater.inflate(R.layout.tooltip_desuyo, null);
        decorView.addView(tooltip);

        tooltip.findViewById(R.id.hogehoge).setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                // タップされたら消えるよ
                v.setVisibility(View.GONE);
            }
        });

```

# 弱点もある

インタラクションやスクロールにはそのままでは追従できないので
固定画面ならまぁこの方法でいいかも。
１つのフォームに、郵便番号による自動入力が複数ある場合


いつも住所の自動入力に使わせて頂いている[yubinbango.js](https://github.com/yubinbango/yubinbango)

今回は1つのフォーム内に、住所を入力する項目があった。

例えば以下のように

* お届け先入力
* 上記以外の住所入力

があったとする。（今回はEC-CUBE）

デフォルトのままだと、郵便番号を入力する場合、どちらの住所欄にも入力されてしまう。

別々にしたい住所欄フォームに対して、h-adrのClassをつけてあげる。

```
<div class="h-adr">
  <span class="p-country-name" style="display:none;">Japan</span>

  〒<input type="text" class="p-postal-code" size="3" maxlength="3">
  <input type="text" class="p-postal-code" size="4" maxlength="4"><br>

  <input type="text" class="p-region" readonly/><br> <input type="text" class="p-locality" readonly/><br>
  <input type="text" class="p-street-address"/><br> <input type="text" class="p-extended-address"/>
</div>
```


これでバッチリ！
MindMup 2 > メニュー: Edit > Reset node position > ノードの配置を調整する

```txt:動作環境
Windows 10 Pro (64bit) バージョン 1803 (April 2018 Update)
Google Chrome バージョン: 70.0.3538.102（Official Build） （64 ビット）
```

MindMup 2 (MindMup Goldで使用中)
https://app.mindmup.com/

ノードの位置がおかしくなった時に手動で調整していた。
自動調整機能があるようだ。

下記のメニューを使う。

```
[Edit] > [Reset node position]
```

ショートカットは未調査。下記には記載はなさそう (2018年11月)。
https://www.mindmup.com/tutorials/keyboard.html
画像認識のためのOpenCVの使い方

私が画像認識に使って覚えたことを書いていきます。

#画像・動画読み込みと表示
```py
# -----動画読み込み、再生-----
import cv2
# 動画読み込み　０はデバイス番号
cap = cv2.VideoaCapture(0)

while True:
    # 動画読み込み,
    _,frame = cap.read()
    # 動画を反転させる
    frame = cv2.flip(frame, 1)

    # 動画を表示
    cv2.imread("frame", frame)
    # キー入力を待機させる　0xFFにてASCIIコードする（バージョンによっては無いとエラーが起きる）
    K = cv2.waitKey(1)&0xFF
    # 文字列を数字に変換する
    Q = ord("q")
    # "q"を入力すると終了する
    if K == Q:
        break
# 表示画面の破棄 ＊特定のウィンドウのみを終了させたいときは関数の中にウィンドウ名を入力する
cv2.destroyAllWindows()
# 読み込んだ動画を開放する
cap.release()



# -----画像の読み込み-----
image = cv2.imread("画像")

cv2.imshow("image", image)
# キー入力待機　何かキー入力を行ったら終了する
cv2.waitKey(0)
cv2.destroyAllWindows()
```
#グレースケールから輪郭の描画まで
```py
# グレースケール
gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
# ２値化
_,th = cv2.threshold(gray, 50,255, cv2.THRESH_BINARY)
# 判別分類法（２値画像が鮮明になったためこれをよく使った。）
ret,th = cv2.threshold(gray, 0,255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)
# -----輪郭-----
# 画像の大きさで中身がすべて０の画像
only_cnt = np.zeros(image.shape)
# 輪郭を検出する
_,contour,hi = cv2.findContours(th, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
for i in contour:
    # 検出した領域を囲む周囲長を計算する
    epsilon = 0.001 * cv2.arcLength(i, True)
    # 輪郭の近似
    approx = cv2.approxPolyDP(i, epsilon, True)
    # 輪郭の描画
    cv2.drawContours(only_cnt, [approx], -1, (0,0,255), 2)
```


＊OpenCVの輪郭とは黒い背景から白い物体を検出する事です。（これが分かっていなくて上手くいかない事がありました。）
# 背景差分
```py
cap = cv2.VideoCapture(0)
# 背景オブジェクトを作成
fgbg = cv2.createBackgroundSubtractorMOG2()


while True:
    _,frame = cap.read()
    # 画像に適用させる
    mask = fgbg.apply(frame)
    # 画像表示
    cv2.imshow("frame", mask)
    if cv2.waitKey(1)&0xFF==ord("q"):
        break

cv2.destroyAllWindows()
cap.realease()
```

# 色の検出
```py
import cv2
import numpy as np

cap = cv2.VideoCapture("bal.mp4")
# 色の上限と下限を指定（この場合は赤）
upper = np.array([120,255,200])
lower = np.array([100,210,110])

while True:
    _,frame = cap.read()
    frame = cv2.flip(frame, 1)
    # 画像をHSVに変換
    hsv = cv2.cvtColor(frame, cv2.COLOR_RGB2HSV)
    # 色を葉に指定して２値化する
    mask = cv2.inRange(hsv, lower, upper)
    # mask画像の画素値が０(黒)以外部分のみを表示する
    frame_color = cv2.bitwise_and(frame, frame, mask=mask)
    # 画像の分離
    H,S,V = cv2.split(frame_color)
    # ２値化
    _,thresh = cv2.threshold(H, 0,255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)
    # 画像の表示
    cv2.imshow("frame",thresh)

    K = cv2.waitKey(1)&0xFF
    if K == ord("q"):
        break

cv2.destroyAllWindows()
cap.release()
```
# まとめ
まだまだ、面白い機能があります。色々紹介していきたいです。
紹介してほしいものがありましたらコメントお願いします。（わかる範囲で紹介させて頂きます。）
zshにエイリアスとかいう奴入れたら人生の時間がn秒増えて神

## 概要
エイリアスを知らない方向けです

この記事を見ることで人生の時間が消費されてしまったら意味がないので30秒で終わる記事にします

## 秒で終わらせたい人向け概要
- `~/.zshrc`に任意の`alias`を設定
- `source ~/.zshrc`して読み込み
- 終了

## エイリアスとは？
ショートカットみたいな奴です
いつも打つクソ長いコマンドを短く打てるようになります

```vim:~/.zshrc
alias ショートカットコマンド='元コマンド'
```

みたいな感じで登録させます

登録したら読込みさせる魔法の呪文を実行してください

```zsh
source ~/.zshrc
```

## エイリアスの登録例

```vim:~/.zshrc
alias gs='git status'
```

```zsh
git status
```

だったら設定さえすれば

```zsh
gs
```

とかで見れるようになります 

これで皆さんの貴重な人生の時間が増えますね


## 私の設定

```vim:~/.zshrc
# alias
## SSH
alias sshk='ssh-keygen -t rsa'
alias vsc='vi ~/.ssh/config'

## Node
alias n='node index.js'

### npm
alias ni='npm install'
alias nu='npm uninstall'
alias nr='npm run'

### yarn
alias ya='yarn add'
alias yr='yarn remove'
alias yi='yarn install'

## git
alias gs='git status'
alias gaa='git add'
alias gc-b='git checkout -b'
alias gc-m='git commit -m'
alias gp='git push'

## zsh
alias sz='source ~/.zshrc'
alias vz='vi ~/.zshrc'
```

## おわり
おすすめの設定やエイリアスなどがあれば教えてください
VBAで重複チェック



力技でループを使って重複チェックをするとどうしても遅かった。
rangeオブジェクトをvariant型にいれると配列処理できることとDictionaryオブジェクトを覚えたので試しに作ってみたら爆速になったので覚書
重複のあるセルを見つけたものから色付けしていく。
戻り値として作成した配列を返してくれるのでいろいろ使える。

```vb
Function CheckDuplex(ByVal column_No As Long, ByVal n As Long, ByVal sh As Object)
'リストのユニーク配列を返す
'元のリストは操作しない
'返すユニーク配列はセルの値をキーに行番号を紐づけた連想配列になっている
'重複チェックを行い重複があればそのセルを色付けする
'戻り値としてユニーク配列と重複があったかどうかの判定用Boolean値を返す
'column_No:重複チェエクしたい行の番号、n:表の始まり補正数、sh:その表のあるタブのワークシートオブジェクト
   
    Dim CELLVALUE As Variant
    Dim isDuplex As Boolean
    Dim dic As Variant, dicKey As Variant
    Dim maxRow As Long, i As Long
     
    
    Set dic = CreateObject("Scripting.Dictionary")
    maxRow = sh.cells(Rows.count,column_No).end(xlUp) 
    
    isDuplex = False
    
    CELLVALUE = Range(sh.Cells(1 + n, column_No), sh.Cells(maxRow, column_No))
    
    For i = 1 To maxRow - n
        dicKey = CELLVALUE(i, 1)
        If dicKey <> "" Then
            If dic.exists(dicKey) Then
                sh.Cells(i + n, column_No).Interior.Color = RGB(255, 200, 200)
                sh.Cells(dic.Item(dicKey), column_No).Interior.Color = RGB(255, 200, 200)
                isDuplex = True
                                
            Else
                dic.Add dicKey, i + n   'ライセンス番号をキーに記入してあるセルの行番号を紐づけ
            End If
        End If
    Next i
    
    CheckDuplex = Array(dic, isDuplex)
End Function
```
コーディングにおける正しいコメントとは？

###この記事の目的
*一般的なルールはあるものコメントは各職場や個人の考えによって書かれることが多い。
いろんな方の意見を聞いて何が正解かわからなくなった新人エンジニアが答えを探す*

##きっかけ
新しい職場でコメントについて先輩社員の方からご指摘をいただいたので
リーダブルコードを読み返してコメントの作法について考えてみました

##これまでの私のコメントに関する体験
###職場1：人が短期でどんどん入れ替わる職場
コメント記法も各々の自由。特にルールも無い状態

###職場2：リーダーA
コメントは書くな、コードを読めば理解できるはずという感じの方針。
必要最小限のみコメントを書く。
###リーダーB
コメントは多いほど良い、ぱっと見でわかることでもコメントで残そう。
必要以上にコメント書くようになる。

###職場3：⇦いまココ

#この機会にコメントに関して自身の経験を通して再確認させられたこと
####コメントは端的に要約して短くまとめる、冗長にならない
必要な情報だけコメントで残す。
不必要な情報が混在すると必要な情報が分かりにくくなる。
####コードの説明のためのコメントではなく機能の説明をする
そのコードでどんな機能を持たせたいのかをか書く。
####コードを見ればわかることをいちいち書く必要はない
丁寧に説明しようとして何でもかんでもコメントするとかえって分かりにくくなる。
####無駄なコメントを読んだらその分コードを読む時間が無くなる。
コード把握しやすくするためのコメントで分かりにくくなっては意味がない。
####誰に書くのかをしっかりイメージする
- そのコードを見るチームのメンバー
- 1年後の自分
- コードを書いた自分自身が後からわかるように書く

####そもそもコメントが必要ないコードを書く
- 命名の段階でわかりやすい名前を付ける

####考えを残しておく
　コードを書いた際に自分が何を考えて書いたのかを明確にしておく。

##まとめ
**本質は振り返ってみるととても基本的なことでした。**
何事にも基本は大事だと再認識させられました。
良い習慣を身につけられるように今後も意識的にコメントを残してきたい。
タブ 通知 Notification 青い丸 JavaScript 方法 非アクティブ タイトル 変更 検知

# 結論

1. `document.title`を書き換えるとできる
2. したがって、JavaScriptを使ったブラウザ用の通知方法、APIがあるわけではない(`document.title`や`alert`にはJSを使いますが)
3. 「タブをピン留め」状態の場合にしかおきないので、遭遇数が少ない → 情報少 ・ 非再現 ・ 説明が伝わらない

ググり力が試されて疲れた…
ピン留め状態限定ということを理解していなかったので、そこを理解して検索キーワードを加えると早かっただろう。
仕様に詳しい方がいらっしゃれば情報元などを教えてもらえると助かります。

(他にもロード完了など通知タイミングはいろいろありそうですが、検証は大変なのでドキュメントがほしい)

***

通知のための実装としては`window.addEventlister('blur')`,`window.addEventlister('focus')`でアクティブ状態を管理して、
`blur`なら通知タイミングで`document.title`を書き換え。
`focus`時にタイトルをデフォルトに戻すことで通知用青丸アイコンを出せるでしょう。
`focus`時にタイトルを戻す必要は薄く感じるが、二回目の通知で同名変更→アイコン出ない だと嫌なので戻しておくのが吉だと思います。


# 環境


* Firefox 61
* Windows 7

***

非アクティブのタブに変更がおきたときにタブのファビコンの下に青い丸がついて通知される方法を実装したかった。

![2018-11-07_11h33_34.png](https://qiita-image-store.s3.amazonaws.com/0/62354/d1216e44-f36c-f93f-0b29-4c3cb8fd362c.png)

私が自作したプログラムで起きた範囲内だと、非アクティブ状態でのalert時に発生していた。
だが、SlackやTwitterなどの他のサービスでも変更時に通知されていたので、その方法を知りたかった。

調べても調べてもアドオンやファビコンの書き換えや特にプッシュ通知のことばかりでて困った。

長い処理の完了待ちの間を別タブでブラウジングしているとき、プッシュ通知では厚かましく、alertではOKが面倒というなかで
奥ゆかしいアトモスフィアを感じさせるこのテクニックは実際重要。

# 情報源

* [JavaScript - ページをピン留め時にのみ可能な、プッシュ通知でない通知を行う方法｜teratail](https://teratail.com/questions/73698)

ありがとう…！ありがとう…！
結局回答もプッシュ通知の話ばかりされていますが、質問者様が自己解決してそれをわざわざ載せてくれるという有能。

* [ピン留めされたタブ | Firefox ヘルプ](https://support.mozilla.org/ja/kb/pinned-tabs-keep-favorite-websites-open)

>ピン留めされたタブのページの内容が変更されると、青く光って通知されます。 

「ピン留め」がキーワードだと判明したらすぐ出ました…
「ページの内容が変更」がどの程度なのかが気になるのです。

# 擬似コード

```js
var active = false
window.addEventListener('blur', e = > {
  active = false
})
window.addEventListener('focus', e = > {
  active = true
  document.title = originalTitle
})
function hoge() {
  // hogehoge
  if (!active) {
    document.title = document.title + '完了' // とか(1)とか
  }
}

```

# その他

* [タブに青い丸が表示されるのを止めたい - Google プロダクト フォーラム](https://productforums.google.com/forum/#!msg/chrome-ja/K2NRQGkKJAE/M7Pti64uAgAJ)
* [Operaのタブの青丸なんですか - 公式サイトのどこかに書いてありました... - Yahoo!知恵袋](https://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q1053418425)
* [Chromeブックマークタブの青い丸 - ブラウザ 締切済み| 【OKWAVE】](https://okwave.jp/qa/q9330600.html)

同士…
【Vue.js】Nuxt.jsの自分用テンプレートを作ってみた

# 概要

Nuxt.jsのプロジェクト作成といえば、`vue init`ですね。

```bash
$ vue init nuxt-community/starter-template my-project  
```

しかし頻繁にプロジェクトを作ってると「これ毎回入れてるよなー」ってものが出てきて、面倒になってきます。
そこで新しく自分のtemplateを追加してみました。

# 手順
## nuxt-community/starter-templateをforkする

ゼロから作りたいわけじゃないんで、`nuxt-community/starter-template`をベースに自分なりにカスタマイズすることにします。
ということで、まず自分のアカウントに`nuxt-community/starter-template`を`fork`します。

## 自分好みに改変

あとは`starter-template/template`を自分好みに改変するだけです。
今回は、`pug`と`stylus`を追加してみることにします。

```diff
diff --git a/template/package.json b/template/package.json
index 3bcc572..e30ae3b 100644
--- a/template/package.json
+++ b/template/package.json
@@ -20,6 +20,10 @@
     "eslint": "^4.19.1",
     "eslint-friendly-formatter": "^4.0.1",
     "eslint-loader": "^2.1.1",
-    "eslint-plugin-vue": "^4.0.0"
+    "eslint-plugin-vue": "^4.0.0",
+    "pug": "^2.0.3",
+    "pug-plain-loader": "^1.0.0",
+    "stylus": "^0.54.5",
+    "stylus-loader": "^3.0.2"
   }
 }
```

# 動作テスト
## 自分のTemplateを元に作成

Templateに自分のRepositoryを指定して作成してみます。

```bash
$ vue init dorarep/starter-template my-project
$ cd my-project
$ yarn install
```

## 動作テスト

Componentを`pug`と`stylus`を使う形に修正して...

```components/AppLogo.vue
<template lang="pug">
  .VueToNuxtLogo
    .Triangle.Triangle--two
    .Triangle.Triangle--one
    .Triangle.Triangle--three
    .Triangle.Triangle--four
</template>

<style lang="stylus">
  .VueToNuxtLogo
    display inline-block
    animation turn 2s linear forwards 1s
    transform rotateX(180deg)
    position relative
    overflow hidden
    height 180px
    width 245px

  .Triangle
    position absolute
    top 0
    left 0
    width 0
    height 0
...
```

無事動作しました！

# おわりに

記事にするまでもない簡単な内容かもしれませんが、あまり情報もなさそうなのでまとめてみました。

AppLogoなどの削除や、Atomic DesignのDirectory構成の作成、.gitignoreの修正など、まだまだやれることはありそうです。

また汎用的なTemplateだけでなく、『ゲーム用テンプレート』『LP用テンプレート』みたいにSpecificなテンプレート資産を増やしていくと、開発効率上がりそうですね。
【Raspberry Pi】Python3環境下でOpenCVのインストールに苦戦した時のログ

Raspberry PiでOpenCVを動かしたくてインストールしようとしたが、エラーが多発したので解決法をメモ。

Python3.5.3で

`$ sudo apt-get update
$ sudo apt-get install python-imaging
$ sudo apt-get install libopencv-dev
$ sudo apt-get install python-opencv`

を叩いてインストールするも、python3に入っていなかった。

デフォルトのpythonがpython2のため、そちらに入ってしまっていた模様。

以下の記事(https://qiita.com/K_M95/items/41a24d7639cffc629227
)を参考にしてインストールを試すも、

`TypeError: unsupported operand type(s) for -=: ‘Retry’ and ‘int’`

というエラーが発生。

sudoを抜いて

`pip3 install opencv-python`

を実行したところ、python3の方にインストールすることが出来た。

`python3`

を実行し、対話型シェルスクリプトを起動して、ちゃんとインストールが出来ているか確認すると

`ImportError: numpy.core.multiarray failed to import`

というエラーが発生。

調べてみると、numpyのアップデートをすればいいらしいので、

`pip3 install -U numpy`

でアップデートをするも、エラーは消えず。

Python3とPython2が入っていて、その両方にnumpyがあるため参照先がおかしくなっている？？

現在原因を究明中
php-master-changes 2018-11-18

今日は 64bit Windows での微修正、ドキュメントの更新、pg_insert() が 'TIME WITHOUT TIMEZONE' のデータ型を持つフィールドへ空文字列や null 以外の値を突っ込めない問題の修正があった！

# 2018-11-18
## weltling: Simplify assignment on 64-bit
- https://github.com/php/php-src/commit/b0591f5e46b52432ddcd0d682b9d6931c9e0a330
- [7.3~]
- 64bit Windows では LARGE_INTEGER の QuadPart を使うよう修正
- 前はなぜか上位 32bit と下位 32bit で分けて代入していた

## weltling: [ci skip] Update NEWS
- https://github.com/php/php-src/commit/cf764f02b30080b423124ac6f165905abf581f93
- [7.2~]
- [昨日のやつ](https://qiita.com/sj-i/items/852c93c5eb556ab0dc0b#weltling-fixed-bug-77111-php-winexe-corrupts-unicode-symbols-from-cli-parameters)の NEWS への追記

## andygajetzki: FIX [#77047](https://bugs.php.net/bug.php?id=77047): fixes regex for "time" data type
- https://github.com/php/php-src/commit/369c991d5cbb0e606ec1106697925d6d958cc4bf
- [7.3~]
- ext/pgsql で、pg_insert() が 'TIME WITHOUT TIMEZONE' のデータ型を持つフィールドへ空文字列や null 以外の値を突っ込めない問題の修正
- 正規表現に誤りがあった

## weltling: Add test for bug [#77047](https://bugs.php.net/bug.php?id=77047)
- https://github.com/php/php-src/commit/282a63dace099b03bb97fa2be8e8e9a48b4d69db
- [7.3~]
- ↑のテストを追加

## weltling: Fixed bug [#77047](https://bugs.php.net/bug.php?id=77047) pg_convert has a broken regex for the 'TIME WITHOUT TIMEZONE' data type
- https://github.com/php/php-src/commit/0434141ce93d5033e47c46c78a8849ac3bfa4311
- [7.1~]
- ↑ の 7.1 （以降）へのバックポート

## weltling: [ci skip] Update NEWS
- https://github.com/php/php-src/commit/68442312e38beb93b2a744cf5c8dd5881aa225d4
- [7.1~]
- ↑の NEWS への追記
請負契約と準委任(SES)契約について必ず押さえておきたいこと

## 1. はじめに

開発契約で関係する契約の種類としては、主に、**請負契約**、**準委任(SES)契約**があります。
各契約形態の特徴やメリット、デメリットを備忘録として以下にまとめます。(受注側目線で記載しています。)
なお、実際に作業を行うのはシステムエンジニアであり、仕事の内容が大きく変わるわけではありません。


## 2. 概要

#### A. 請負契約
 - **システム開発の成果**に対して発注側が受注側に報酬を支払う契約。

#### B. 準委任(SES)契約
 - 発注側から受注側へ**システム開発作業を委託**し、受注側がそれを受諾することによって成立する契約。
 - 「委任」と「準委任」の違いは法律行為かどうか。準委任は**法律行為を含まない委託**を指す。


## 3. 報酬

#### A. 請負契約
 - **成果物**に対して報酬を受け取る。
  - (例) プログラムを作成し納品する
 - 成果物を納品後、発注側で成果物に問題がないことを確認した後で報酬が支払われることが一般的。

#### B. 準委任(SES)契約
 - **労働時間**に対して報酬を受け取る。
  - (例) 要件定義書の作成をサポートする
 - 報酬は一定期間ごとに支払われるのが一般的。

## 4. 指揮系統

#### A. 請負契約
 - 受注側にある。

#### B. 準委任(SES)契約
 - 受注側にある。

![契約.PNG](https://qiita-image-store.s3.amazonaws.com/0/247638/b2433e11-b180-2bd3-1a2a-ade092429130.png)


## 5. 完成責任

#### A. 請負契約
 - あり。

#### B. 準委任(SES)契約
 - なし。


## 6. メリット

#### A. 請負契約
 - 低コストで成果物を完成させることができればお得。
   - (例) 1000万の仕事を700万の開発費で完成させれば300万の利益となる。

#### B. 準委任(SES)契約
 - 決められた労働時間の中で発注側の期待値以上の成果であれば、信頼度や単価の向上に繋がる。
 - システム開発の失敗に対してリスクを負わない。(当たり前だが、準委任とはいえ成果物の期待はされる)


## 7. デメリット

#### A. 請負契約
 - コスト内でシステムを完成させることができなければ赤字になる可能性がある。
   - (例) 1000万の仕事を1300万の開発費で完成させれば300万の損失となる。
 - 成果物の結果責任を負うことになるのでリスクが高い。
   - (例) 納品後、仮にバグが見つかった場合、対応する義務(瑕疵担保責任)が課される。

#### B. 準委任(SES)契約
 - 決められた労働時間の中で発注側の期待値以下の成果であれば、信頼度や利益の低下に繋がる。
 - 責任範囲が狭いため利益が少ない。
vs2010でLNK1123エラー

#概要

vc++のビルドでLNK1123エラーを吐いた。
対処した。

#環境

windows vista sp2
Visual Studio 2010

#調査
cvtres.exeで、動かん。

#対処
cvtres.exeをリネーム。(cvtres_.exe)


以上。
PePy: PyPi に登録したパッケージのダウンロード数を知る

# はじめに
昔は PyPi のサイトを見ると、パッケージ毎にダウンロード数がでてたようなおぼろげな記憶があるのですが[^1]、最近、カッコ良く変わったサイトを見ると出てこなくなってました

[^1]: これ、記憶違いだったかもしれません。単に、ランクみたいなのが出てただけだったかも

ソフトウェアの価値って、どんなに立派なコードでも誰も使ってなかったらゼロであって、使われて初めて生じるものだと思っているので、たとえお粗末さんなパッケージと言えども自分が書いたパッケージであれば、どれぐらい使ってもらっているのか大変興味があります

なんでダウンロード数が出なくなったかについては[こちら](https://stackoverflow.com/questions/38102317/why-pypi-doesnt-show-download-stats-anymore)に事情がでてました
大変だかららしいです

# [PePy](https://pepy.tech/)
その大変だから辞めてしまったというパッケージのダウンロード数を、計算してくれるサイト [PePy](https://pepy.tech/) を Petru Rares さんという方が公開してくださっています
ありがとう！

こんなかんじで、総ダウンロード数、月ごとの集計、週毎の集計、日々の集計をだしてくれた上にバッジまでつくってくれます
![スクリーンショット 2018-11-19 8.22.01.png](https://qiita-image-store.s3.amazonaws.com/0/46544/7991592d-6d47-f3d7-aeea-46e174c10a02.png)
![スクリーンショット 2018-11-19 8.22.21.png](https://qiita-image-store.s3.amazonaws.com/0/46544/0215f31e-64cf-2763-d03c-1269fb6f59c1.png)

バッジを自分のリポジトリの README.md にコピペしておくと Github も PyPi も「おっ！」ってかんじでオサレになります

![スクリーンショット 2018-11-19 8.01.02.png](https://qiita-image-store.s3.amazonaws.com/0/46544/394f7c55-f1a1-25df-bb38-53c1abda69fb.png)

![スクリーンショット 2018-11-19 8.01.31.png](https://qiita-image-store.s3.amazonaws.com/0/46544/dd000402-85bf-ae8f-36b5-011f2fa83b58.png)

# 補足
前述の[記事](https://stackoverflow.com/questions/38102317/why-pypi-doesnt-show-download-stats-anymore)によると ***pypinfo*** というツールも使えるようですが、こちらはまだ試していません

# 感想
予想より二桁ほど多くダウンロードしてもらってて、率直に嬉しいです (^o^)/
ってか、全くパブリケーションしてないパッケージを誰がこんなにダウンロードしてくれてるんだろ？

# references
- [Stack Overflow の記事](https://stackoverflow.com/questions/38102317/why-pypi-doesnt-show-download-stats-anymore)
  - こちらに、PyPi からダウンロード数が消えた事情と、PePy, pypinfo の紹介があります
- [Reditでの PePy のアナウンス](https://www.reddit.com/r/Python/comments/8mke9i/pepytech_now_is_open_source/)
  - ５ヶ月ぐらい前から公開してくださってたんですね、気がついてなくてもったいなかったです
AWSのIAMユーザーの設定方法

プログラミングで必要な環境構築をAWSで作っていこうと思います。
まずはIAMユーザーを作ってみたので、メモしておきます。

AWSログイン後、IAMの設定画面へ遷移する。

左側の『ユーザー』をクリックする。

ユーザー名に適当に好きな名前を入れる。
アクセスの種類：AWSマネジメントコンソールへのアクセス　を選択。
パスワード：カスタムパスワード
パスワードリセット：自分で使う場合はチェックを外す

次のステップのボタンをクリック。

■アクセス権限設定
『既存のポリシーを直接アタッチ』を選択。
『AdministratorAccess』にチェック。
次のステップを押下。

ユーザー作成を押下。

閉じる。

ダッシュボードに戻る。

サインインリンク：
https://038394729298.signin.aws.amazon.com/console

の数字のところを任意でわかりやすい名前に変更する。

アカウントエイリアス：好きな名前入力
「はい作成する」押下。

サインイン用URLをコピーをしておく。

サインアウトする。

さっきコピーしたURLにアクセスする。

ユーザー名：作成したユーザー名
パスワード：

入れてサイインインする。

これで入れたらIAMユーザーが無事に作られている。
UEFIについての調べてみた

# はじめに
前回の[CentOS6からの変更 ブートローダGrubについて](https://qiita.com/hot_study_man/items/c7e0951d51149f9f76a4)でBiosから起動するGrubについていろいろ調べた際に、UEFIについても調べたので今回はUEFIについてまとめてみました。

# 結論
* UEFI == BIOS
  - ブートローダを読み込む役割は変わらない。機能拡張されている
 
* UEFIに対応しているサーバとは?
  - マザーボードのROMにUEFIが格納されている
* OSがUEFIに対応しているとは?
  - GPT形式のパーティションが作成できて、そのパーティションにUEFI用のブートローダを配置できること
* なぜGPTを使うのか
  - MBRのパーティションテーブルは2TBまでしか格納できないので
  - ちなみに**2TB以下でも使用**できる

# BIOSとは
* MBRのブートローダをメモリに読み込んで実行する
* 仕様上メモリを1MBしか扱えない
* サーバに搭載されたデバイスの設定はBIOSの画面からではなく、Ctrl+Aなどのキーで個別に設定画面を呼び出す(これはデバイスのファームウェアが用意？)


# UEFIとは
* UEFI == BIOS
  - システムBIOSの後継として開発された規格
  - ブートローダを読み込む役割は変わらない
  - ただしMBRを読み込まないで後述するEFIシステムパーティションにあるブートローダをメモリに読み込む
* BIOSの制限を取り除いて、機能拡張
* サーバメーカによってはGUI持つUEFIもある
* UEFIから個別のデバイスの設定ができる
* GPT形式の**EFIシステムパーティション(VFAT形式-ファイルシステム)**に格納されたブートローダを直接起動(grub.efi)
  - メモリの制限が取り除かれたので、ファイルシステムも理解できるようになっている
* **UEFIに対応しているサーバとは**
  - マザーボードのROMにUEFIが格納されている

# BIOSとUEFI
ともにマザーボードメーカが製造

# UEFIとGPT
## GPT(GUID Partition Table)
* パーティションを作成する規格・形式のこと
* 通常、2TB以上のパーティションを扱う場合に使用する。
   - しかし、**2TB以下でも使用**できる
* GPT形式のディスクからブートローダを呼び出すには**BIOSではなくてUEFIが必要**
* ブートローダが格納されたパーティション(/boot/efi)はESPラベルがつけられる
  - /boot/efiにgrub.efi(grubステージ2相当のブートローダ)が格納
* OSがUEFIに対応しているということはGPT形式のパーティションが作成できて、UEFI用のブートローダがあるということ

### GPT作成方法
* partedコマンド
* RHEL6ではインストーラもGPTに対応しているので、ディスクが2TB以上であれば、自動でGPT形式でパーティションを作成してくれる

### なぜGPT
* 通常のMBRにパーティションテーブルを格納する場合にパーティション情報が2TBまでしか格納できない。よって2TB以上のパーティションを扱う場合に別の方法が必要で、それがGPT

## CentOS7インストールでのパーティション
### 2TB以下
通常のパーティション作成。ブートローダは**Grub2**を使う

### 2TB以上
GPT形式でパーティション作成。ブートローダは**grub.efi**を使う 

2TB以下のディスク。EFIのディレクトリは存在したけど何もはいっていなかった。

```sh

# ll /boot/efi/EFI/centos/
合計 0
```

### 2TB以下でGTPを使う
2TB 以下のディスクも GPT パーティションとしてインストールしたければ、インストーラーをブートする時のカーネルオプションに inst.gpt を指定しておくと、ディスクの容量にかかわらず GTP パーティションが使われるようになる


## BIOSからUEFIへの変更(試してない)
DELLのサーバしか調べていませんが**UEFIに対応しているサーバ**は(試してはいない)Biosから変更可能のようでした。ですが、UEFIに変更するにもとなってディスクのパーティション形式もGPTに変更しないといけません。そうでないとUEFIがEFIシステムパーティションを見つけようとするが見つけられずに止まりそうです。これは逆も然りかもしれません。
JavaScriptでオブジェクトを分割代入してしまうとthis呼び出しできなくなる

ある機能を実装し終わっていざ動かしてみるとthis呼び出しがundefinedになって原因がわからず、どうやっても動かないので１時間分の作業を初期化までしてようやくわかったので同じような過ちを起こさないように備忘録として残して起きます

まずオブジェクトのthis呼び出しですが、オブジェクトに関数があるとき、その関数の中からthisを使うと簡単に自身のプロパティにアクセスできるものです

```js
const obj = {
  hoge: 1,
  fuga() {
    return this.hoge
  }
}

obj.fuga() // -> 1
```


> `fuga() {}`といのはES5で新しく追加された簡略構文で`fuga: function () {}`と同等です
> また、アロー関数はthisを束縛しないため使用できません
> 使えるのはメソッドだけで`piyo: this.hoge`のようなことはできません
> つまりゲッターを使えば`get piyo() { return this.hoge }`のようにできます

それで問題の分割代入ですが、

```js
const obj = {
  hoge: 1,
  fuga() {
    return this.hoge
  }
}

obj.fuga() // -> 1

const { fuga } = obj

fuga()     // -> undefined
```

考えてみればそうなのかもしれないけど、知らないとなかなか気づけなさそうです...

ちなみにthisの中身はwindow(node.jsではglobal)になるようです

仕様の確認や検証が不十分なのでおかしいところがあれば指摘していただけると助かります...
node-redで、フローをクリップボードへ書き出す際の注意点 (Terminalの一行最大文字数制限)

## 最初に結論

node-redでフローを書き出す際は、
- ファイルへの書き出しが一番無難
- やむを得ずクリップボードを使う際はインデントのあるJSONフォーマットにする

## 以下詳細

[LINEのメッセージをGoogle Homeで読み上げる(機能強化版)](https://qiita.com/tinoue@github/items/17f737f3e7d693877ecf) のコメントでsekinetpさんに指摘されたのですが、gitにあげているnode-redのフローのjsonが4096バイトで切れている問題がありました。

フローはクリップボードに書き出して、ターミナルで```cat - > file```で保存していました。使ったターミナルソフトが悪かったのかなと思い、別ので試しても同じです。ターミナル上では正常にペーストされているように見えても、ls -lすると4096バイト。
4096バイト以上で出力されているフローも1件あり、そのフローはインデントありJSONで書き出されていました。問題のあるフローはインデントなしJSONです。

調べてみると以下の記事が見つかりました: [How to read over 4k input without new lines on a terminal?](https://unix.stackexchange.com/questions/131105/how-to-read-over-4k-input-without-new-lines-on-a-terminal)。Linux terminalのcooked mode時の一行の最大長制限が4kバイトとのこと。ちなみにMacOSだと1kだそうです。
インデントなしでフローを書き出すと全て1行で出力されるため、この制限に引っかかっていました。

しかし、20年近くLinux使ってきてて今頃この仕様に気づくとは... 今までホントに大丈夫だったのか心配です。