OmniAICreator/Qiita-1.07M huggingface dataset:
Nginxのエラー「413 Request Entity Too Large」の対処

###問題
Nginxを使ったアプリにおいて少し大きめのファイルをアップロードしたところ、このようなエラーが
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502145/f37adc39-e0c7-ad3b-a813-3270a3e79ae6.png)

ファイルサイズが大きすぎるためこのようなエラーが出るようです。

Nginxはデフォルトでは最大のアップロードのサイズが1MBとなっているようです。

###対処法
####Nginxの設定を変更
自分は環境構築にDockerを利用しているのでdocker/nginx/default.confを編集します。

```conf:default.conf
server {
    listen       0.0.0.0:80;
    server_name  localhost;
    charset      utf-8;
    client_max_body_size 10M; #追加
```
serverブロック内にclient_max_body_sizeの項目を追加またはサイズの変更をします。

####php.iniの設定を変更
自分の場合はphp側の変更をしなくても行けたのですが、必要な場合もあるようなのでメモ程度に書きます。
post_max_sizeとupload_max_filesizeの項目を編集します。

```ini:php.ini
post_max_size = 10M
upload_max_filesize = 10M
```

設定を変更したらnginxを再起動します。
プログラミングや勉強の作業メモをペンタブ+Redmineで管理する習慣を付けたら少しだけ幸せになった話

ビックウェーブ最終電車に間に合わない系エンジニアなので。
巷にもっと優秀なツールor技法があれば教えて頂けると嬉しいです。めっちゃうれしいです。

# はじめに

***皆さんはメモを書きますか？***

伝達事項や議事録の記憶、設計や競プロ中のアルゴリズム検討等
色々種類が有ると思いますが。

皆さんはこれらの作業を行う際に
メモを書いていらっしゃるのではと思います。いらっしゃいますよね？
お願いですいらっしゃってください でないとこの記事ここで終わっちゃいます。

では、それらの作業メモはどのように取っているでしょうか？
恐らくですが、皆さん ***以下の方法*** で取る事が多いのではないでしょうか。

1. ***ノート***
1. ***付箋***
1. ***テキストエディタ***
1. ***ペイントソフト***

では、これらの技法について、
それぞれの手法におけるメリット/デメリットを検討してみます。

# メモ手段のメリット/デメリット
## ノート
### メリット
* 自由に記述可能
* スペースが尽きた時の切り替えが高速(ページをめくるだけ)

### デメリット
* かさばる
* 記述内容の削除に時間がかかる(結果として、不要情報までもが蓄積されがち)
* [消しゴム利用時限定]消しカス遊びが本題になりがち

## 付箋
### メリット
* メモ内容の組み換えが可能
* (上手く使えば)記述内容の部分削除が高速になる
* 細かい単位に区切られたメモや<br>
  他者との共同作業が得意(作業タスク、アイデア管理等)

### デメリット
* スペースが狭い
* 巨大図、長文の読み/書きが困難

## テキストエディタ
### メリット
* 文字メモのスピードは最速
* 部分/全体削除が高速に処理可能
* かさばらない

### デメリット
* イメージを出力することが出来ない<br>
　→書き出し:「イメージ」→「文字」<br>
　　読み出し: 「文字」→「イメージ」<br>
　　のプロセスを踏む必要があり、ちょっと面倒くさい
* 検討ツールとして用いるにはやや不向き

## ペイントソフト
### メリット
* 自由に記述可能
* 部分/全体削除が高速に処理可能
* 内容の組み換えが可能
* かさばらない

### デメリット
* スペース尽きた際の処理速度が最遅レベル
* 記述可能になるまでの待機時間が長い
* メモ内容の管理が疎かになりがち<br>
  (ファイルを無造作に積み上げてしまう/それが嫌で必要なメモまで消してしまう等)
* [マウス限定]文字の難読化機能がデフォルトで付随している

# どれが良いのか
各々一長一短であり、時と場合により
以下のように使い分ける事が多いでしょう。

1. 人からの伝達事項メモ→ ***ノート***
1. ブレインストーミング、小さな作業タスク管理→ ***付箋***
1. 議事録の記述→ ***テキストエディタ***

では、***ペイントソフトの利点*** はどこでしょうか？
個人的には、以下のケースであれば
ペイントソフトが適していると思います。

* ***個人作業のメモ(設計下書き、検討内容、勉強メモetc...)*** 

という訳で、次の章からは
ペイントソフト+ペンタブを利用した個人作業メモの管理方法として
***Redmine*** を使用してみた例について簡単に紹介します。

# 本題
とはいった物の、そこまで難しい事はしていません。

1. ***自身の作業タスクをチケットへ起こし***
1. ***メモを書いたら、説明文と一緒にファイルをチケットへ上げて***
1. ***対象作業が終わったらクローズする***

それだけです。普段の使い方と殆ど変わりませんね。
しかし、それだけの作業でも次のような改善点がありました。

## 1. 必要なメモのみが蓄積されていく
ペイントソフトであれば部分/全体削除が ***高速*** である為
***不要なメモ*** が残留する事は ***殆どありません。***

後で見返したとき、不要なメモや落書きが残っていないだけで
非常に見やすいです。

## 2. 文字による説明補助が容易に付加できる
チケット更新時に

1. ***どんな作業をしたか***
1. ***メモの内容がどんな物か***

をテキストで残せるので、メモ内容を素早く理解できます。

加えて、テキストエディタの ***文字メモ最速メリット*** との合体技で
文章を手書きする作業ともオサラバです。

## 3.ファイル分けが整然と行われる

***フォルダへ無造作に積み上げられる*** 事も
***フォルダ名のみで分類分けされる*** 事もなくなり
作業毎、作業説明付きでファイル分けが行われます。

テキストファイルでも同様の利点は傍受できますが、
ペイントソフトでこの恩恵を受けられると大分使いやすいです。

# 終わりに
今回はRedmineでのペイントソフト利用事例を挙げてみましたが、
「それならこんなツールもあるよ！」「こんな感じでやった方が良いんじゃない？」
等の意見有りましたら、コメント等で教えて頂けると嬉しいです。

お絵描きデバイスを持て余している方や、メモの管理方法へ悩んでいる方にとって
少しでも参考になれば幸いです。

# 以下参考情報

## redmineの環境について
@win-chanma 様の記事を参考にbitnamiで構築しました。
https://qiita.com/win-chanma/items/5212ba36ad543d466832

## 自分の環境について

* ペンタブ(液タブ):Wacom chintiq 16 FHD
* ペイントソフト:krita
* OS:ubuntu18.04

とは言う物の、環境さえ整えることが出来れば
全てどれでもいいと思います。
板タブでお手軽に整えたり、スマホ/タブレット端末を活用してみても良いかもしれません。
指定行ごとに文字列を挿入し、最終行にも文字列を挿入する

# 目的
何らかの作業対象のテキストやファイルがあり、指定した行ごとに任意の文字列を挿入したい場合を想定。
具体的にはSQLでの指定行でのCOMMIT。
恐らく各RDBMSのCLIでは便利な機能があると思いますが、機械的に実行してしまいたい時に利用します。

# 注意
本来はある一連の作業がすべて終わった場合に明示的にCOMMITすべきであり、意図しない変更の場合のROLLBACKが大変になるリスクがあります。
単純INSERTであっても、変更を元に戻せる**安全な**UPDATE文やDELETE文を用意しておいた方が良いです。

# shellの動作について
改行コード込みの特定文字列をshell実行時引数で指定したかったのですが、今回はコードに**直書き**(ハードコーティング)しています。

# スクリプト

## 説明用の桁数揃えシーケンスファイルの作成
```bash
#!/usr/bin/env bash
# 4桁の数字を0埋めで0001 - 1500まで出力
seq -f "%04g" 1500 > TARGET.txt

head TARGET.txt
#0001
#0002
#0003
#0004
#0005
#0006
#0007
#0008
#0009
#0010

tail TARGET.txt
#1491
#1492
#1493
#1494
#1495
#1496
#1497
#1498
#1499
#1500
```

## insert_break
```bash
#!/usr/bin/env bash

# 使用方法：
# TARGET.txtに200行ごとに文字列「COMMIT;\n」を挿入
# sh insert_break.sh TARGET.txt 200

temp_file=$(mktemp)
basename_excluding_ext=`echo ${1} | sed 's/\.[^\.]*$//'`
edited_txt=${basename_excluding_ext}.edited.txt

cat ${1} | awk '{print $0} NR%'${2}'==0 {printf "COMMIT;\r\n"}' > ${temp_file}

# 作業後のファイル名を変更する
mv ${temp_file} ${edited_txt}

# 指定ファイルの行数が指定行数で割り切れない場合、最終行に同一の文字列を挿入する
if [ $(( `wc -l ${1} | awk '{print $1}'` % ${2} )) != 0 ];
then
  sed -i -e '$ a COMMIT;' ${edited_txt}
fi
```

## 使用例
```bash
# 200行ごとに特定文字列（例 COMMIT;\n）の挿入
sh insert_break.sh TARGET.txt 200

# 確認

# 200行付近
cat TARGET.edited.txt | head -n 204 | tail -n 10
#0195
#0196
#0197
#0198
#0199
#0200
#COMMIT;
#0201
#0202
#0203

# 最終行付近
tail TARGET.edited.txt
#1492
#1493
#1494
#1495
#1496
#1497
#1498
#1499
#1500
#COMMIT;
```

# 参考URL
[n行ごとに改行を挿入するシェルスクリプト](http://gomisai.blog75.fc2.com/blog-entry-620.html)
[bashで四則演算(+-×÷)や割り算のあまり(剰余)を求めるような簡単な計算をする | 俺的備忘録 〜なんかいろいろ〜](https://orebibou.com/2016/06/bash%E3%81%A7%E5%9B%9B%E5%89%87%E6%BC%94%E7%AE%97-x%C3%B7%E3%82%84%E5%89%B2%E3%82%8A%E7%AE%97%E3%81%AE%E3%81%82%E3%81%BE%E3%82%8A%E5%89%B0%E4%BD%99%E3%82%92%E6%B1%82%E3%82%81%E3%82%8B/)
[bashの変数展開によるファイル名や拡張子の取得 - Qiita](https://qiita.com/mriho/items/b30b3a33e8d2e25e94a8)
【SourceTree】プッシュの数字が出てるのにプッシュ先が何も出ないんだけど...(メモ)

SourceTreeでいざプッシュ！ってときになんかプッシュ先が何もでなくて困りました。なんだこれ。
一応解決したのでメモ程度に書き残すことにしました。初投稿です。


#なんかプッシュに何も出ないんだけど
と友人が言い出したので中身見てみると...
プッシュ押してみると本当に何もない。ローカルブランチとかリモートブランチとか全く出てこない。
####<font color="HotPink">どうやってプッシュしたものか...</font>
ほとんどないGitの知識を使ってコマンドラインでプッシュしたりなにしたりしてたのですが、どうやらリモートブランチの追跡がどうたらが問題だったみたい。

#リモートブランチをちゃんと追跡していなかったらしい
ありったけの知識を振り絞って何もわからなかったのですが、どうやら答えは死ぬほど簡単。

左側にあるブランチからブランチをチェックアウト後
#####右クリックで出てくる「リモートブランチを追跡」で、適当なブランチを選択して終わり！

なんだと。死ぬほど簡単。
追跡するリモートブランチが正確ではなかったため、pushしても弾かれる始末だったようです。
結果がお粗末；；

#ここからはほぼ僕のためのメモ
<font color="LightSeaGreen">ローカルブランチ</font>と<font color="Brown">リモートブランチ</font>ってのがある。
<font color="LightSeaGreen">ローカルブランチ</font>は自分のパソコンの中に。
<font color="Brown">リモートブランチ</font>は例えば<a href="href="https://github.com/">Github</a>や<a href="https://bitbucket.org/product/" >Bitbucket</a>などのホスティングサービスという、
####「あなたたちのリポジトリをサーバー側で管理しています。」
といったサービスや、自分で作ったサーバーなどの、オンラインで接続することができるGitシステムで管理されているブランチのこと(多分)。

<details><summary><font color="MediumOrchid">そもそもリポジトリって?</font></summary>
ソースコードなどを管理する箱のようなもの(と勝手にとらえている)。
ソースコードが入ったフォルダを管理する感じみたい。


リポジトリのおおもとをサーバーに任せていたら、URLや鍵さえわかればどこからでもアクセスできるし、どこからでもソースがいじれるというわけ。
ローカルとリポジトリを定期的に同期しておけば、最新のプロジェクトをオフラインでさわることができたりもする。オンラインになってリモートブランチにプッシュしたら、オフラインの作業を最新にすることができる。

ただ、今回の場合はローカルとリモートがちゃんと同期・関連付けができていなかったようで、もう一度やり直すと直ったって感じ。
複数のディレクトリ内のファイルをaws cliでs3へアップロード

### aws cli でのエラー

findで、現在のディレクトリ以下に存在するディレクトリ内のファイルをまるっとs3にあげたいなと。
そこで再帰的に


```
find . -type d  | aws s3 cp - s3://${S3_BUCKET} --recursive
```

のようにしたら `Reason: exit status 255` でエラー。
ドキュメントによると、エラー内容は↓とのこと。
> 255: コマンドが失敗しました。リクエストが送信された AWS CLI または AWS サービスのいずれかでエラーが生成されました。
https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/cli-usage-returncodes.html

### --debugオプションをつける

CodeBuildには正しい権限を付与しているはず。。。
`--debug`オプションで検証。


```
Streaming currently is only compatible with non-recursive cp commands
```

と怒られていました。...ですよね。

### コマンドの修正


```
find . -type d | while read dir
do
  aws s3 cp $dir s3://$S3_BUCKET/$dir --recursive
done
```

`./dir`内のファイルはs3の`/dir/`以下に、`./dir2`内のファイルは`/dir2/`以下に無事アップロード完了。
Docker composeでマウントするボリュームを変更したいならコンテナ作り直そう

## TL;DR

マウントするボリュームを変更したいなら以下のいずれかを実行して、コンテナを明示的に削除しないといけない

* `docker-compose up --renew-anon-volumes`
* `docker-compose down && docker-compose up`
* `docker container rm ${DB_CONTAINER_ID} && docker-compose up`



## 何をしたかったのか

開発に使っているdbコンテナで実験がしたかったが、既存のデータを壊したくなかったので、docker-compose.ymlを書き換えることで新規のデータボリュームをアタッチしたかった

Before

```docker
version: ‘3.5’
services:
  db:
    image: mysql:5.6.38
    environment:
      MYSQL_ROOT_PASSWORD: password
    volumes:
      - mysql-data:/var/lib/mysql
  web:
    image: ruby
    volumes:
      - workspace:/workspace
    depends_on:
     - db
    tty: true
volumes:
  mysql-data:
  workspace:
```

After

```docker
version: ‘3.5’
services:
  db:
    image: mysql:5.6.38
    environment:
      MYSQL_ROOT_PASSWORD: password
    volumes:
      - mysql-data_new:/var/lib/mysql # <- ここ
  web:
    image: ruby
    volumes:
      - workspace:/workspace
    depends_on:
     - db
    tty: true
volumes:
  mysql-data_new: # <- ここ
  workspace:
```


## 何が起きたのか
以下の警告が出て

> WARNING: Service "db" is using volume "/var/lib/mysql" from the previous container. Host mapping "your-project_mysql-data_new" has no effect. Remove the existing containers (with `docker-com...

既存の"your-project_mysql-data"が使われ続けた。というか、以前のコンテナが使われ続けて、以前のデータボリュームがマウントされてしまった。

## どうすればいいのか

```
docker-compose down
```

https://docs.docker.com/compose/reference/down/

してコンテナを削除するか、DBコンテナだけ消したいなら

```
docker container rm ${DB_CONTAINER_ID}
```

すればいい。DBコンテナなら必要なのはデータボリュームだけだろうからコンテナをさくりと作り直すのがいい。

もしくは、 `-V or --renew-anon-volumes` オプションをつけて、以前のコンテナからのボリュームの再利用を止めればいい。

https://github.com/docker/compose/issues/2481#issuecomment-279082091
https://github.com/docker/compose/issues/4476

このへんをみてようやく気づけた。

## なぜそうなったのか

コンテナにアタッチしたボリュームは変更できない。なのでコンテナを作り直さないといけないんだけど、webの方のコンテナは `docker-container up` を実行しただけで作成し直してくれた。コンテナを作り直してくれる条件が不明。
[API] VSCodeでAPI Specを書く環境を整える

# はじめに
- API開発を行うにあたりAPI Specを作成しようと思い立つ
- 巷にはSwagger Editorなどもあるが、WEBで作成->Export->ソース管理の手間が面倒に感じた
- どうせならサーバーサイドプログラムと合わせて同じGitレポジトリで管理したい
- それならば、VSCodeで一緒に開発したい（<-今ココ）


# VSCode Extentionのインストール
- `Cmd + Shift + x`でEXTENTIONSを開く
- `Swagger Viewer`と`OpenAPI (Swagger) Editor`をインストールする

[Swagger Viewer]
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245534/c64f6573-6d5e-f4b3-f2a7-a6ffd8d0a9cf.png)

[Swagger Editor]
![スクリーンショット 2020-03-23 10.51.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245534/996ace55-084d-7713-618b-bd588a0939a8.png)



# VSCodeでAPI Specを作成
- `Cmd + Shit + P`でコマンドパレットを開き、`new openapi`と入力する
- Suggestionが出るので、OASのバージョンとフォーマット（json or yaml）を選択
![スクリーンショット 2020-03-23 10.53.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245534/f68231b4-63de-125e-f774-b856c1ab6f4e.png)



# Preview参照
- `Cmd + Shift + P`でコマンドパレットを開き、`Preview Swagger`と入力
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245534/cf76dea3-9509-d2c7-39eb-890ed5c2e8bd.png)

- 編集がリアルタイムに表示されるViewerが開く
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245534/38815aba-0e99-250e-dbc9-f7813a258198.png)




# 参考
- [swagger specどうやって管理する問題](https://qiita.com/k3nNy_51rcy/items/64f96761a7f931b7e4b1)
- [VSCodeでリファクタリング・保守するOpenAPI](https://qiita.com/tMinami/items/5b1a921e82b4c7979cd1#openapi-swagger-editor)
- [VSCodeで効率的にswaggerを書く。](https://qiita.com/kawasukeeee/items/e0973f762e12aaa256e2)
- [VSCodeでswagger 2.0のyamlドキュメントを快適に書きたい](https://qiita.com/k3nNy_51rcy/items/4ff27df02cad77de544a)
Elixir: ひらがなとカタカナの相互変換

Elixirでひらがなをカタカナに、あるいはカタカナをひらがなに変換したい場合、[Exkanji](https://github.com/ikeikeikeike/exkanji)というパッケージが使えます。

しかし、Exkanjiは外部コマンド _nkf_ を利用しているので、環境構築にひと手間かかるし、パフォーマンス的にも不利です。

Unicodeでは、対応するひらがなとカタカナのコードポイントはちょうど `0x60` 離れているので、外部コマンドを使ったり、変換表を作ったりしなくても単純な計算で求められます。

### 実装

次に、私が作った `StringHelper` モジュールのソースコードを示します。

```elixir
defmodule StringHelper do
  @moduledoc """
  文字列を扱う汎用的なヘルパー関数群。
  """

  @doc """
  与えられた文字列に含まれるひらがなをカタカナに変換して返す。
  """
  def hiragana2katakana(source) do
    source
    |> String.codepoints()
    |> Enum.map(&do_convert_h2k(&1))
    |> Enum.join()
  end

  defp do_convert_h2k(cp) when cp < "\u3041" or cp > "\u3096", do: cp

  defp do_convert_h2k(cp) do
    <<n::utf8>> = cp
    m = n + 0x60
    <<m::utf8>>
  end

  @doc """
  与えられた文字列に含まれるカタカナをひらがなに変換して返す。
  """
  def katakana2hiragana(source) do
    source
    |> String.codepoints()
    |> Enum.map(&do_convert_k2h(&1))
    |> Enum.join()
  end

  defp do_convert_k2h(cp) when cp < "\u30A1" or cp > "\u30F6", do: cp

  defp do_convert_k2h(cp) do
    <<n::utf8>> = cp
    m = n - 0x60
    <<m::utf8>>
  end
end
```

関数 [String.codepoints/1](https://hexdocs.pm/elixir/String.html#codepoints/1) は、文字列をコードポイント（Unicodeキャラクター）のリストに変換します。

パターンマッチング `<<n::utf8>> = cp` で、コードポイント `cp` に与えられた整数値が `n` にセットされます。

ひらがなからカタカナに変換する場合は、`n` に `0x60` を加えて `m` とし、`<<m::utf8>>` でコードポイントに戻しています。逆に、カタカナからひらがなに変換する場合は、`n` から `0x60` を引いています。

なお、このモジュールはいわゆる「半角カナ」には対応していません。

### 利用法

上記のソースコードを `string_helper.ex` という名前で適当なディレクトリに保存し、コンパイルしてください。

```bash
$ elixirc string_helper.ex
```

IEx を起動して、こんな風に使います。

```
$ iex
iex(1)> StringHelper.hiragana2katakana("かっとやさい")
"カットヤサイ"
iex(2)> StringHelper.katakana2hiragana("ダブルクォート")
"だぶるくぉーと"
```
【Dart】pubspec.yamlのバージョン指定方法

# はじめに

こちらの内容を参考にしています。

https://dart.dev/tools/pub/dependencies#version-constraints

# pubspec.yamlのバージョン指定方法

ここでは、セマンティックバージョニングによってバージョン指定されていることを前提としています。
また、解説のため、メジャーバージョンはx,マイナーバージョンはy,パッチバージョンはzで表現しています。

DartのSemanticバージョニングについての説明はこちら
https://dart.dev/tools/pub/versioning#semantic-versions

## 未指定 or `any`

どのバージョンでもokという指定方法。
できる限り一番最新のバージョンがインストールされる。（他に指定しているライブラリのバージョンに依存している場合は、できる限り最新のバージョンになる）

```pubspec.yaml
dependencies:
  rxdart:       # どちらでもOK
  provider: any # どちらでもOK
```

ただ、anyは、パフォーマンス上の理由から推奨されていないそうです。

## `x.y.z`

記述したバージョンのみの指定。この場合は4.0.3が必ず取得される。

```pubspec.yaml
dependencies:
  provider: 4.0.3
```

## `>=x.y.z`

記述したバージョン以上の指定。この場合4.0.3は含まれる。

```pubspec.yaml
dependencies:
  provider: >=4.0.3
```

## `>x.y.z`

記述したバージョンより大きいバージョンの指定。この場合4.0.3は含まれない。

```pubspec.yaml
dependencies:
  provider: >4.0.3
```

## `<=x.y.z`

記述したバージョン以下の指定。この場合4.0.3は含まれる。

```pubspec.yaml
dependencies:
  provider: <=4.0.3
```

## `<x.y.z`

記述したバージョン未満の指定。この場合4.0.3は含まれない。

```pubspec.yaml
dependencies:
  provider: <4.0.3
```

## `'>=a.b.c <x.y.z'`

a.b.c以上、x.y.z未満という指定方法。

```pubspec.yaml
dependencies:
  provider: '>=4.0.3 <5.0.0'
```

## `^x.y.z`

Caret syntaxと呼ばれるもの。
指定したバージョンの下位互換性を担保できるものが指定でき、セマンティックバージョニングの慣例に従って指定される。
例えば、`^2.1.0`と指定した場合、`'>=2.1.0 <3.0.0'`の省略形として定義できる。

```pubspec.yaml
dependencies:
  provider: '^4.0.3'  # '>=4.0.3 <5.0.0'の意味
```

※Caret syntaxはDart1.8.3に追加されたので、1.8.3以上でないと使えないそうです。

# まとめ

今までとりあえずCaret syntaxで指定していましたが、他にも色々指定方法があったので勉強になりました。参考になれば幸いです。
TypeScript: Prepend<1, [2, 3]> ─ タプル型の先頭に要素を追加するユーティリティタイプ

TypeScriptでタプル型の先頭に要素を追加した型を導出するユーティリティタイプです。

## `Prepend<T, U>`

### TypeScript 3.9以下

```typescript
type Prepend<T, U extends unknown[]> = 
  Parameters<(t: T, ...u: U) => void>
```

### TypeScript 4.0以上

```typescript
type Prepend<T, U extends unknown[]> = [T, ...U]
```

TypeScript 4.0で[可変長タプル型](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-0.html#variadic-tuple-types)が導入されたため、関数型の引数を経由する必要がなくなり、よりシンプルに書くことができます。

## 使い方

```typescript
const t1: Prepend<1, [2, 3]> = [1, 2, 3]
const t2: Prepend<1, []> = [1]
const t3: Prepend<1, [2]> = [1, 2]
const t4: Prepend<boolean, [null, undefined, number]> = [false, null, undefined, 0]
```

## 試す

このユーティリティタイプを試したい方は、[TypeScript Playground](https://www.typescriptlang.org/play?noImplicitReturns=false&target=7&module=1&ts=4.0.2#code/HYQwtgpgzgDiDGEAEAVAnjCBleAnAljAC4DMAnEgN4BQSSRGyACrhJsACYA8KANEgFUkEAB5EInKEgCuwANbAA9gHdgAbQC6APiQBeJExC5wEcbihcAFEQBcqfgDon0uwICUenQDdF+DltokeEVgKCJ6AEY7FjYJbgj+NQAmfhJtPSQ1BKQUpDTA4NDwoiTo1nZ4xPT9LI0CkLD6EjLYzi5s5OrM7KS6ukLGogAWFoquACNFRQAbCBBgROBpaen+WQ4IADN8YAgOfiWwcYhcLrVNkGmoCAPl1ZlOLZ29-gAGOoBfam-QSFgEZDoTA4AjEIavKiBBiYAzlOI8fhCUTiSQPBQqdRnPhIJwOAR9IINYpRWGtSqZXJpHQ1HqpAkDYqlUljDpnCL0olNUbwjq9andfi9epFegjZnwyYzOYLTJLFZrR7bXb7JCHY6nfnnS7XW7yh4bJUvJDvagfIA)をご覧ください。
macで2つのWi-Fiに同時に接続する

# やりたいこと
- macOSXで2つのWi-Fiに同時に接続する
  - 内蔵無線LANとUSBのWi-Fiドングルを併用する

# やったこと

### モノの準備
IO-DATA WN-G300UA https://www.iodata.jp/lib/product/w/4162.htm
ネット最安1200円〜店頭1800円くらいの中堅Wi-Fiドングル

### 接続の確認
システムレポートを確認すると認識自体はされているが、NICとしてはまだ使えない
<img width="392" alt="スクリーンショット 2020-03-23 11.33.16.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/529606/83bd9835-74d7-b547-5f84-e712ad8d935e.png">

### ドライバーのインストール
IO-DATA公式ではMacOSXをサポートしていない
<img width="605" alt="スクリーンショット 2020-03-23 11.34.50.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/529606/2b0881b2-f3e5-d974-a4fc-6ef496799d41.png">



<font color='red'><h2>(2022年3月15日追記)</h2></font>
**TP-LINKのドライバに相乗りする手法はmacOS 10.7~10.11までしかサポートされておらず、現行のBig Surでは動作不可能になっていた**
調べたところBig Sur向けの色々なチップセット向けのドライバを開発している神を発見したので紹介する
https://github.com/chris1111/Wireless-USB-Big-Sur-Adapter
SIPを無効にする必要があるため、導入しているセキュリティソフトによっては導入不可能なケースもあるかもしれないことに注意

インストールしたところIO-DATA WN-G300UAの動作もきちんと確認できた


<font color='red'><h2>(追記ここまで)</h2></font>
※以下はmacOS 10.7~10.11向けの古い情報であることに注意

---

ので、同一チップセット `RTL-8192CU` の別メーカーのドライバーを導入する

今回のケースでは、**TP-LINK TL-WN823N V1**という機種がMacOSX向けにドライバーを公開していたので乗っかる
https://www.tp-link.com/la/support/download/tl-wn823n/#video
ありがたいことにhowtoビデオまで公開されているので、参考にしながらインストールを進める

### 再起動する
なんかいる
<img width="211" alt="スクリーンショット 2020-03-23 11.39.14.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/529606/994a22e3-067a-a8f8-e90b-a9df2dda3c3b.png">

macのネットワーク設定からはWi-Fi接続ができない
どこにいるかというとココ
<img width="354" alt="スクリーンショット 2020-03-23 11.42.45.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/529606/0921a38e-eab7-c40d-e45d-3f2d0e5c596d.png">
TP-LINK製のよくわからんツール経由でWi-Fiに接続することができる

少なくともWPA2エンタープライズでは接続できるのでひとまずヨシ
Node.js: printf風の文字列フォーマットをする方法

Node.jsでprintf風の文字列フォーマットをするには、`util.format`関数を使うと良い。


```javascript
const {format} = require('util')
const message = format('値は %s のうちのどれかにしてください', [1, 2, 3])
console.log(message)
//=> 値は [ 1, 2, 3 ] のうちのどれかにしてください
```

公式ドキュメント: [Util | Node.js v13.11.0 Documentation](https://nodejs.org/api/util.html#util_util_format_format_args)
初めてのAzureFunctions作成してみた (JSON Arrayのソート)

# はじめに
個人の記録的な投稿であることをご容赦ください。
Power Automateを利用していると、配列のソート操作がないことにしばしば悩まされます。
簡易的なソートであれば[Power Automateの枠内で対応できます](https://qiita.com/h-nagao/items/765826be914fbf08bd02)が、ちゃんとした(?)ソートではないので、制限が多いです。
そんなちょっとした不満を解消する手段として、Azure Functionsを使ってみたので、その記録です。

# つくる/つくった
参考にしたのは公式のHello worldです。
(Ref. https://docs.microsoft.com/en-us/azure/azure-functions/functions-create-first-azure-function )
得意な言語というのも特にないので、javascriptで始めました。

今回作るものは、
1. JSONの配列、プロパティ名を入力として
2. 対象のプロパティでソートされた配列を返す
というだけのシンプルなものです。
なお、ちょっとした都合により、JSON配列はURLにパラメータとして含めることで受け渡しています。
普通にやるならリクエストのBodyに入れるんでしょう。

想定するのは以下のようなGETのリクエストです。

```
https://abcdefg.azurewebsites.net/api/HttpExample?src=<JSON配列のURL encodeした値>&prop=<ソートしたいプロパティ>
```
できたFunctionsはindex.jsを少しだけHello Worldから変更しただけのものです。

```index.js
module.exports = async function (context, req) {
    context.log('JavaScript HTTP trigger function processed a request.');

    function sortByProperty(property) {
        return function (a, b) {
            if (a[property] > b[property])
                return 1;
            else if (a[property] < b[property])
                return -1;

            return 0;
        }
    }

    if (req.query.src && req.query.prop) {
        context.res = {
            // status: 200, /* Defaults to 200 */
            body: JSON.parse(decodeURI(req.query.src)).sort(sortByProperty(req.query.prop)),
            headers: {
                'Content-Type': 'application/json'
            }
        };
    }
    else {
        context.res = {
            status: 400,
            body: "Please pass a name on the query string or in the request body"
        };
    }
};
```

sortByPropertyでリクエストのパラメータにあるpropを参照します。ソートする対象は、エンコードされているので、まずdecodeURIで文字列に戻して、さらにJSONとして扱うためにJSON.parseしました。


# おわり, 動かした結果
unsorted がもとの入力になります。(画像右側)
これを、HTTPアクション (Azure Functionsよびだし) を通すことで、"name"でソートされた結果を得ることができました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202779/708c73dc-b049-bf12-c7b0-677ed8d1d2dc.png)

このような感じで、サービスに備わっていない機能を少しだけ、とても簡単なコードで補完できるのはとても良いですね。
次はバインディングを利用して、OneDriveやSharePointに保存されたファイルへのアクセスを試してみたいと思います。
Raspberry Pi 4 で画面を回転

# 間違った方法
Web で検索すると、よく `/boot/config.txt` の末尾に `display_rotate=1` などを追加する方法がでてきますが、Raspberry Pi 4 では**うまくいきません**。

# 正しい方法

1. 左上のメニューから Screen Configuration を選択<br />![2020-03-23-105010_1920x1080_scrot.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/9718/1106f1c9-efed-fde2-774c-af7cef3a869b.jpeg)
2. 回転したい画面上で右クリックして「向き」を変更。✔マークをクリック。<br />![2020-03-23-105041_1920x1080_scrot.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/9718/bfad93b0-8907-e416-1926-5d3667839181.jpeg)
![2020-03-23-105104_1920x1080_scrot.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/9718/5e9c0dbb-f4e1-7283-955f-47752e2ee44a.jpeg)
3. 完了！<br />![2020-03-23-105127_1080x1920_scrot.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/9718/4df664cc-cab7-7797-a4b2-5060a9c36a68.png)

# 参考にしたサイト
- https://www.raspberrypi.org/forums/viewtopic.php?t=247619
🐬【MySQL】外字を含むレコードのINSERT方法


## やりたいこと
今時外字を扱うなんてと言われたらそうでもあるが扱う必要が出てきた場合
sqlファイルにてSQLを書いて実行することで外字の文字列をINSERTできる

## やったこと

hogeデータベースにテストテーブルであるgaiji_test_tableを作成

```create_table.sql
CREATE TABLE `gaiji_test_table` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `gaiji` varchar(100) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
```

### 🔵ファイルからSQLの実行
SQLファイルを作成し任意のディレクトリに格納
（今回は/test/test/gaiji.sql）

```gaiji_insert.sql
INSERT INTO gaiji_test_table (gaiji) VALUES ('');
```

↓

```terminal
[root@localhost ~]# mysql hoge < /test/test/gaiji.sql 
```

```terminal
mysql> select * from gaiji_test_table;
+----+-------+
| id | gaiji |
+----+-------+
|  1 |      |
+----+-------+
1 row in set (0.00 sec)
```

### ❌ターミナルでそのまま実行した場合
外字が消えてしまう


```
mysql> INSERT INTO gaiji_test_table (gaiji) VALUES ('');
Query OK, 1 row affected (0.02 sec)

mysql> select * from gaiji_test_table;
+----+-------+
| id | gaiji |
+----+-------+
|  1 |      |
|  2 |       |
+----+-------+
2 rows in set (0.00 sec)
```
RestTemplate (TestRestTemplate)のfollow redirectsを無効にする

日本語だとなんと言えば良いのか分からないが、follow redirectsってあるじゃないですか。302 Foundなどが返ってくると、HTTPクライアントが自動的にLocationを見て、再度そちらにリクエスト投げてくれるやつ。これ、アプリケーションを作成するときはHTTPクライアントの機能として大変便利だと思います。

ただテストのときは無効にしてえ〜〜〜とも思います。

Spring BootにはRestTemplate、というかTestRestTemplateがありますが、これ、デフォルトだとfollow redirectsが有効です。

というわけで無効にしましょう。無効にするにはClientHttpRequestFactoryと、それを適用したTestRestTemplateをDIすればOKです。

## follow redirectsを無効にしたClientHttpRequestFactoryの生成

まずfollow redirectsを無効にします。これはTestRestTemplateに与えられるClientHttpRequestFactoryにて設定します。

HTTPクライアントに利用するライブラリによって実装クラスが異なるため、微妙に設定方法も変わってきます。

### org.apache.http.client.HttpClient

たぶんこれで良い…… と思う。手元に環境がなかったため未確認。

```kotlin
import org.apache.http.client.HttpClient

// org.apache.http.client.HttpClient用のClientHttpRequestFactory
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory

// ...

val client = HttpClientBuilder.create()
    .disableRedirectHandling()
    .build()
HttpComponentsClientHttpRequestFactory(client)
```

### okhttp3.OkHttpClient

こっちは確認済み。

```kotlin
import okhttp3.OkHttpClient

// okhttp3.OkHttpClient用のClientHttpRequestFactory
import org.springframework.http.client.OkHttp3ClientHttpRequestFactory

// ...

val client = OkHttpClient().newBuilder()
    .followRedirects(false)
    .build()
OkHttp3ClientHttpRequestFactory(client)
```

### java.net.HttpURLConnection

こっちも確認済み。

これは外からjava.net.HttpURLConnectionを与えるわけではないので、他とはちょっと違う感じになる。

```kotlin
import java.net.HttpURLConnection

// java.net.HttpURLConnection用のClientHttpRequestFactory
import org.springframework.http.client.SimpleClientHttpRequestFactory

// ...

object : SimpleClientHttpRequestFactory() {
    override fun prepareConnection(connection: HttpURLConnection, httpMethod: String) {
        super.prepareConnection(connection, httpMethod)
        connection.instanceFollowRedirects = false
    }
}
```

## ClientHttpRequestFactoryを適用したTestRestTemplateをDI

まずTestRestTemplateのBeanを定義する。以下は利用するHTTPクライアントがOkHttp3の場合ですが、他の場合は適宜与えるClientHttpRequestFactoryを変えてあげてください。

```kotlin
@TestConfiguration
class TestRestTemplateConfig {

    @Bean 
    fun testRestTemplate(): TestRestTemplate =
        TestRestTemplate(RestTemplateBuilder().requestFactory {
            val client = OkHttpClient().newBuilder()
                .followRedirects(false)
                .build()
            OkHttp3ClientHttpRequestFactory(client)
        })
}
```

最後にテストクラスでDIする。

```kotlin
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@Imports(TestRestTemplateConfig::class)
class SomeTest(
    private val testRestTemplate: TestRestTemplate,
    @LocalServerPort private val port: Int
) : StringSpec({
    // ...
})
```
MicroPythonにCモジュールを追加するために...

とりあえず簡単な関数を実装してみる。

どれもmicropythonでできることなのでCで実装する必要はないけど、
今後何かしらをCで実装する必要が出てきたときのために最低限の確認。

ドキュメントが見当たらなくてgitに上がっているmicropythonのコードを追いかけて実装したので別の方法があるかも...(ご存知の方はご指摘いただけると幸いです。)



- 文字列を引数にとってリターン

```
STATIC mp_obj_t return_str(mp_obj_t str_obj)
{
    const char *str = mp_obj_str_get_str(str_obj);

    return mp_obj_new_str(str, strlen(str));
}
```

###　list操作
- 作成

```
STATIC mp_obj_t list_new()
{
    mp_obj_t list_items[] = {
        mp_obj_new_int(123),
        mp_obj_new_float(456.789),
        mp_obj_new_str("hello", 5),
    };

    return mp_obj_new_list(3, list_items);
}
```

- append


```
//追加したいitemがint型の場合
STATIC mp_obj_t list_append(mp_obj_t list_obj, mp_obj_t item_int_obj)
{
    mp_int_t item_obj = mp_obj_get_int(item_int_obj);
    mp_obj_t item = mp_obj_new_int(item_obj);

    mp_obj_list_append(list_obj, item);

    return list_obj;
}
```

- remove

```
// 削除したいitemがint型の場合
STATIC mp_obj_t list_remove(mp_obj_t list_obj, mp_obj_t item_int_obj)
{
    mp_int_t item_obj = mp_obj_get_int(item_int_obj);
    mp_obj_t item = mp_obj_new_int(item_obj);

    mp_obj_list_remove(list_obj, item);

    return list_obj;
}
```

## dict操作
- 作成

```
STATIC mp_obj_t dict_new()
{
    mp_obj_t dict_obj = mp_obj_new_dict(3);
    mp_obj_dict_store(dict_obj, mp_obj_new_str("test_key1", 9), mp_obj_new_int(123));
    mp_obj_dict_store(dict_obj, mp_obj_new_str("test_key2", 9), mp_obj_new_float(456.789));
    mp_obj_dict_store(dict_obj, mp_obj_new_str("test_key3", 9), mp_obj_new_str("hello", 5));

    return dict_obj;
}
```

- 追加、変更

```
//追加したい要素が['str型': 'str型']の場合
STATIC mp_obj_t dict_store(mp_obj_t dict_obj, mp_obj_t key_str_obj, mp_obj_t value_str_obj)
{
    mp_obj_dict_get_map(dict_obj);
    const char *key = mp_obj_str_get_str(key_str_obj);
    const char *val = mp_obj_str_get_str(value_str_obj);

    mp_obj_dict_store(dict_obj, mp_obj_new_str(key, strlen(key)), mp_obj_new_str(val, strlen(val)));

    return dict_obj;
}
```

- 削除

```
// 削除したいkeyがstr型の場合
STATIC mp_obj_t dict_delete(mp_obj_t dict_obj, mp_obj_t key_str_obj)
{
    mp_obj_dict_get_map(dict_obj);
    const char *del_key = mp_obj_str_get_str(key_str_obj);

    mp_obj_dict_delete(dict_obj, mp_obj_new_str(del_key, strlen(del_key)));

    return dict_obj;
}
```

## MicroPythonから呼び出せるようにするには...

```
// 呼び出す関数
STATIC mp_obj_t return_str(mp_obj_t str_obj)
{
    const char *str = mp_obj_str_get_str(str_obj);

    return mp_obj_new_str(str, strlen(str));
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(return_str_obj, return_str);

// モジュールとして定義
STATIC const mp_rom_map_elem_t example_module_globals_table[] = {
    // micropythonでimportするときの名前
    {MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_example)}, 
　　// micropythonから関数を呼び出すときの名前
    {MP_ROM_QSTR(MP_QSTR_return_str), MP_ROM_PTR(&return_str_obj)},
};

STATIC MP_DEFINE_CONST_DICT(example_module_globals, example_module_globals_table);

const mp_obj_module_t example_cmodule = {
    .base = {&mp_type_module},
    .globals = (mp_obj_dict_t *)&example_module_globals,
};
```

上記のように.Cを作ってmicropythonのソースに追加してビルドする。
micropythonからは、

```
import example
example.return_str()
```
のようにして使う。
Google Apps Scripts - キャッシュ

よく忘れて、毎回探しに行くので、メモ。

[リファレンス](https://developers.google.com/apps-script/reference/cache)

## 使い方


### オブジェクトの取得
とりあえず、スクリプトキャッシュ。

```Javascript:
var cache = CacheService.getScriptCache();
```
### キャッシュへの投入
put(キー, 値, 保持期限(秒))

```Javascript:
cache.put('test_key', 'test_value', 60 * 60 * 6);
```

### キャッシュからの取得
get(キー)

```Javascript:
var cache_value = cache.get('test_key');
```

### キャッシュから削除
remove(キー)

```Javascript:
cache.remove('test_key');
```

## 備考

### スコープ
|項目|スコープ|
|:-:|:-:|
|getDocumentCache()|ドキュメント|
|getScriptCache()|スクリプト|
|getUserCache()|現在のユーザとスクリプト|

### 制限および割り当て
|項目|内容|
|:-:|:-:|
|キーの長さ|250文字|
|値のサイズ|100KB|
|保持期限|6時間|

## 処理時間の計測
### 数バイトのキーおよび値で計測した結果。
スプレッドシート等へのアクセス時間を考えると、非常に高速。

|回数|1|2|3|4|5|6|7|8|9|10|Avg.|
|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
|Get cache object(ms)|2|2|2|3|3|3|2|3|3|2|3|
|Remove key from cache(ms)|90|145|88|56|102|98|100|61|90|51|88|
|Put value to cache(ms)|52|51|57|57|55|61|56|60|53|53|56|
|Get value from cache(ms)|20|21|19|13|23|23|24|17|21|23|20|

### 250文字のキーと、100KBの値で計測した結果。
上とほぼ一緒。特にデータサイズには依存しない模様。

|回数|1|2|3|4|5|6|7|8|9|10|Avg.|
|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
|Get cache object(ms)|2|2|2|2|2|2|4|4|2|1|2|
|Remove key from cache(ms)|55|91|91|100|91|94|98|93|97|92|90|
|Put value to cache(ms)|52|52|57|62|60|58|66|54|54|59|57|
|Get value from cache(ms)|22|21|21|21|23|23|28|24|22|20|23|


## その他
### 簡易的なデータ置き場として
保存期間が6時間、データサイズ100KBと、それなりの容量を使えますし、
データサイズによる処理時間の劣化もなさそうですので、一時的なデータ
置き場として色々と使い道がありそうです。

```Javascript:

  var obj = {
    "key1": "test_value1",
    "key2": "test_value2",
    "key3": "test_value3",
    "key4": "test_value4",
    "key5": "test_value5",
    "key6": "test_value6"
  };

  var cache = CacheService.getScriptCache();

  cache.put('test_key', JSON.stringify(obj), 60 * 60 * 6);

  var value = JSON.parse(cache.get('test_key'));
  
  Logger.log(value);
  Logger.log(value.key1);
```

実行結果：

```
[yy-mm-dd HH:MM:SS:000 PDT] {test_key3=test_value3, test_key5=test_value5, test_key4=test_value4, test_key2=test_value2, test_key1=test_value1, test_key6=test_value6}
[yy-mm-dd HH:MM:SS:000 PDT] test_value1
```
MicrosoftTeamsのExploratoryライセンスを入れてみた

新型コロナの影響でテレワーク対象者が出てきたので、急遽 Microsoft Teams の **Exploratoryライセンス**を入れた話をする。
このライセンスは、Teamsが含まれないOffice365プランを契約するテナントだけに与えられた特権であり、調査・検証用の特別な試用版として**2021年1月**まで無料で使えるもの。
有料版と殆ど変わらないので、このタイミングで使わない手は無い。

以下、導入手順をメモする。

# テナント管理者による事前設定
Microsoft 365 管理センターから行う。

## 独自ドメインを割り当ててみる
必要なければ本項は読み飛ばしてもらって構わない。
取引先とファイル共有するときには独自ドメインがあった方が分かり易いので前もって割り当てた。Teamsの運用を始めた後にドメインを変えようとすると面倒くさいことになるからだ。

### 独自ドメインの入力
[設定] :arrow_right: [ドメイン] :arrow_right: [ドメインの追加] の順にクリックし、独自ドメインを入力する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236222/0f93e430-0535-79bd-a40b-6bd990d50544.png)
### TXT値を控える
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236222/73fc516a-cfbd-62a3-a515-bd255d04eeba.png)

### TXTレコードの追加
今回は、お名前ドットコムのGMO提供レンタルサーバである**共用サーバSD**のコントロールパネルから、先ほど控えた値のTXTレコードを追加した。
変更ではなく追加なので注意。共用サーバSDのTXTレコードにはSPFが予め設定されている。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236222/2f69af3c-dd6a-c797-0a78-d754afe028d5.png)
`dig`コマンドで反映が確認できたら、Office365側で確認ボタンをクリックしよう。受け入れられたら追加したTXTレコードは削除して構わない。

### ユーザー名の変更
各ユーザー名をクリックし、`@`以降を独自ドメインに変更する。（全ユーザーを一括で変えるにはPowerShellを使うしかないのかな？）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236222/c28a8131-b6ae-b689-20b9-6bab9c17f32b.png)

## Exploratoryライセンスの購入
Microsoft Teams Exploratory 試用版を購入する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236222/1707e18a-c82f-cea2-9e0d-bea46f59ba33.png)

## Teams をユーザーに割り当てる
ユーザー名をクリックし、[ライセンスとアプリ] より、Exploratory にチェックを入れて保存する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236222/25621cf0-c3c6-eace-72f0-9bfe928d6922.png)

# 一般ユーザー（利用者）ごとの設定
利用者が https://teams.microsoft.com にサインインして行う。
Exploratory には、SharePoint Online が含まれているので、SharePointファイルを**OneDrive同期アプリ**で同期させてみよう。

## OneDriveと同期させる
[チーム] のファイルタブから [同期] をクリックする。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236222/8dbfae23-d64a-7486-101c-3e7bc9dc0ca2.png)
【OneDrive を開く】をクリックする。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236222/a9ef090f-d225-96d3-e9b1-a276b8f4d59a.png)

### OneDrive同期アプリの設定
OneDriveにサインインするだけで設定が完了する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236222/614697be-e77c-c9a8-b2f5-34f56c0ac7b5.png)

### エクスプローラで確認
エクスプローラの左のウィンドウに、組織の名前で追加されるので確認する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236222/a103179c-6919-cc71-782d-61baf4c4162c.png)

## モバイルアプリの導入
BYODが認められているなら、AppStore や GooglePlay からモバイルアプリ版をダウンロードしてもらおう。
エンジニア登竜門：はじめに

##はじめに
**これは、「プログラミングを触ったことがある」レベルの男が、何を血迷ったのかエンジニア仕事を始めたことで、どのくらいの期間でどこまで成長できるのかを記録するためのものである。**
これからエンジニアを目指す人、エンジニア未経験だけど仕事してみたい、そんな人たちの参考になれば幸いだ。

##スペック
しかし、こういう記事をネットで見つけて読んだとき、俺はいつも哀しい気持ちになる。
**「初心者」って名乗る人から、全然わからん専門用語が飛び出し始めるからだ。** ~~エンジニアの謙遜信じられない。~~
もちろんそんなことない記事も多くあるのだが、同じような哀しみを持つ人が少しでも減ればいいなと思う。
まずは嘘偽りなく、自分の現状のレベルを確認しよう。

####年齢と学歴
  27歳、理工学部数学科出身、塾講師としてしばらく働いた後、退職して半年無職だった。理系ではあるがプログラミングを専門的に触ったことはなし。
####プログラミング言語
　オモチャレベルでC言語、pythonを触った。ProgateというWebプログラミング学習サイトでhtml、css、phpも少し触った。基本の文法が少しわかる程度。(変数の宣言とか)
作ったものも特にない。pythonのGUIモジュールを使ってじゃんけんソフトを作ったくらいだ。もちろん、人に見せられるようなレベルじゃない。
おっと、カッコつけてGUIモジュールとか言ってしまった。分からない人には「コイツも謙遜エンジニアか…」と思われるかもしれないが、そんなことは決してない。
要するに「ウィンドウとかボタンとかそういうのが最初から入ってるpython用の道具箱を使って、ボタンを押したら1/3で勝ち！と表示するオモチャを作った」ということだ。これをどう感じるかは結局読み手次第になってしまうが、プログラミング入門として「テトリスを作ろう！」があるのと比べれば、何となくレベルがわかると思う。
####ハードウェア
これも決して詳しいわけではないが、PCでゲームをするのが好きなのでパーツの換装はいくつかやった。メモリを増設したり、SSD換装したり、グラボ交換したり。繋いだら動く。数字が大きければ強い。ハードウェア知識としてはそのくらいだ。
####ソフトウェア
ソフトウェアは全然わかってない。OSの種類は沢山あるなーくらい。オブジェクト指向とかも何となく聞いたことあるけどよくわからない。そもそも、ソフトウェアの話なのかこれ？あとフレームワークという言葉もよくわかってない。
####そもそもなぜエンジニアに？
元々コンピューターとプログラムは好きだから。本当にそれだけ。なにかが特別できるわけでもないし、身近にそういうことに詳しい人もいない。ちゃんと勉強したこともない。
仕事を辞めてから何をしようか悩んでたけど、Twitterでフルリモートのエンジニアを募集していて、酔った勢いでDMをぶん投げた。正直に上記のようなスペックの話をしたら半分呆れられて、「やっぱりエンジニアになるには勉強が必要やなぁ」と思ってたらなぜか入れてもらえた。
フルリモートという世界を他の職種にも持ち込めたらもっと幸せな世の中になるだろうし、そういうのを覗いてみたかった好奇心が募集に飛びついた一番の理由。

##乞うご期待
さて、自分のエンジニアスペックはこんなもんだ。
もう察したと思うが、**ズブの素人にもほどがある**。こんなんで果たして仕事としてエンジニアを出来るのか？遊びじゃねえんだぞ？
こんな未経験エンジニアが、**仕事として食えるほど成長できるのか、やはりクビを切られて無職に戻るのか**、お楽しみに。
cheerio-httpcli （Node.js）を使用したときに参考にしたもの


下記2点で情報入手
①Webサイト
[cheerio-httpcli - Node.js用WEBスクレイピングモジュール](https://www.npmjs.com/package/cheerio-httpcli)
②書籍(P53～P67)
[Webクローラー/ネットエージェント開発テクニック](https://www.socym.co.jp/book/993)

補足：上記以降の対応 NODE_PATH %AppData%\Roaming\npm\node_modules を 具体的なパスに変更
Flutter iOSでGenerated.xcconfigが読み込めない場合の解決策

FlutterでiOS用にFlavorを追加した際に、Schemaは正常に追加できるものの、自作のxcconfigからGenerated.xcconfigをimportしようとしたところ、xcodeでrunした際に、Generated.xcconfigが見つからない旨のエラーが出ていました。
このままでは開発環境情報をハードコーディングする羽目になってしまう、と困っていたところ、英語で解決策が乗っているサイトを見つけました。
https://www.reddit.com/r/Flutter/comments/d1otpq/different_build_configurations_in_iosxcode_wont/

「コピーは絶対するな。新しいxcconfigファイルが置かれているフォルダを確認しろ。」
ということでxcconfigが置かれているフォルダを確認したところ、一つ階層がずれていることに気づきました。
最初Flutterのバグかと思っていましたが、ただのケアレスミスでした。
これで、今の所iOSでもFlavorは十分使えるようになりました。
今後気を付けたいと思います。

>Don't do that, don't copy and paste the Generated.xcconfig file because you lose important build settings like FLUTTER_BUILD_NUMBER and FLUTTER_BUILD_NAME. You'll want those.

>Check to make sure your new scheme xcconfig file is in the right folder. It should be on disk in the Flutter folder. After several hours of banging my head, I noticed that my Production.xcconfig file it was NOT in the /Flutter directory, but a directory above. That's not right, and was causing the error.

>I think when you right click and add a xcconfig file in XCode, it doesn't put it in the correctly directory.
TestCafeのローカル実行はVSCのプラグインが便利

みなさんE2Eテストしてますか。
私は[TestCafe](https://devexpress.github.io/testcafe/)+外部サービスが手軽でクロスブラウザの実行ができて良いなと思ってますが、実行確認をする際に毎回外部サービスで実行させるのは手間ですよね。

そんなときは[TestCafe TestRunner](https://marketplace.visualstudio.com/items?itemName=romanresh.testcafe-test-runner)
VisualStudioCodeをお使いの場合はこのプラグインが便利です。

実行したいテストコードで右クリック
<img width="263" alt="スクリーンショット 2020-03-23 10.49.09.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/89213/9a289fbd-a4d2-f849-acdc-d51666ef5019.png">

ローカル（私の場合はMacOS）のブラウザを起動してテストすることができます。
Jest Runnerのように実行できるのでE2Eテストを気軽に追加できると思います。
Flutter gallery_saver Androidのみ保存でエラーになる問題の解決

[gallery_saver](https://pub.dev/packages/gallery_saver) は、写真・画像をカメラロール、ギャラリーに保存するライブラリ。
※ 使用バージョン gallery_saver 1.0.7

Androidのみ、同一ファイル名で保存をかけた場合、エラーとなる問題がある。

発生するエラー。

```kotlin
E/AndroidRuntime(21661): FATAL EXCEPTION: DefaultDispatcher-worker-1
E/AndroidRuntime(21661): Process: com.example.myapp, PID: 21661
E/AndroidRuntime(21661): java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String android.net.Uri.getLastPathSegment()' on a null object reference
E/AndroidRuntime(21661): 	at android.content.ContentUris.parseId(ContentUris.java:85)
E/AndroidRuntime(21661): 	at carnegietechnologies.gallery_saver.FileUtils.insertImage(FileUtils.kt:79)
E/AndroidRuntime(21661): 	at carnegietechnologies.gallery_saver.GallerySaver$saveMediaFile$1$success$1.invokeSuspend(GallerySaver.kt:69)
E/AndroidRuntime(21661): 	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
E/AndroidRuntime(21661): 	at kotlinx.coroutines.DispatchedTask.run(Dispatched.kt:238)
E/AndroidRuntime(21661): 	at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:594)
E/AndroidRuntime(21661): 	at kotlinx.coroutines.scheduling.CoroutineScheduler.access$runSafely(CoroutineScheduler.kt:60)
E/AndroidRuntime(21661): 	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:742)
D/FlutterView(21661): Detaching from a FlutterEngine: io.flutter.embedding.engine.FlutterEngine@97f0662
```

問題は確認されているがリリースまで進んでいない模様。
[fix error if image was selected twice #41](https://github.com/CarnegieTechnologies/gallery_saver/pull/41)

### 関連issue
- [twice save expection #37](https://github.com/CarnegieTechnologies/gallery_saver/issues/37)
- [App crashes on Android when saving an image that has been saved already](https://github.com/CarnegieTechnologies/gallery_saver/issues/31)

ひとまずの解決として、保存をかけたい一時保存しているファイルのファイル名を、ランダムにするなどして対応することで、エラーが発生せず保存ができた。

```dart
/// ランダムな文字列を生成するメソッド
static String randomString(int length) {
  const _randomChars =
      "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
  const _charsLength = _randomChars.length;

  final rand = Random();
  final codeUnits = new List.generate(
    length,
    (index) {
      final n = rand.nextInt(_charsLength);
      return _randomChars.codeUnitAt(n);
    },
  );
  return new String.fromCharCodes(codeUnits);
}

// GallerySaver.saveImageに渡すため一時保存するファイル名をランダムにする
const tmpFileName = randomString(10) + '.png';
```

### 参考URL
- [Dartでランダムな文字列を生成する](https://qiita.com/najeira/items/49f58fbbe45dc1e4c479)
chefの有償化についてのリンク

### 商用化まとめ

Chefの有償化とオープンソース化
https://www.creationline.com/lab/30958

新しいChefのご紹介：いつでも100%オープンソース
https://www.creationline.com/lab/30980

Chef社全製品の有償化・オープンソース化についてのFAQ
https://www.creationline.com/lab/30973

コミュニティにとって新生Chefが意味すること
https://www.creationline.com/lab/30986


### 脱chefの一案

ChefからAnsibleへ引っ越したい人のためのAnsible入門
https://www.slideshare.net/iwashi86/chefansibleansible-50941335
Slackで簡単にアンケートが作れるPollyが便利

Slackでアンケート取りたいときってありますよね
そんなときPollyというSlack Appが便利です。
導入も1分くらいでできます。

こんなアンケートが簡単に作れます。
![スクリーンショット 2020-03-23 10.27.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/268801/1629f090-9d64-b797-dfc2-a38cd5077a25.png)

結果もこのようにわかりやすく表示されます。
結果は設定次第で、アンケート作成者だけしか見られないようにもできます。

![スクリーンショット_2020-03-23_10_27_51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/268801/9b65e2ca-1a23-82f8-5610-9ddde0c7a92c.png)

私たちのチームでは、実装方法についてゆるく意見募集するときなんかに使っています。

![スクリーンショット_2020-03-23_10_30_42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/268801/a7d05c3f-b535-ab49-9c63-5914af15ae30.png)


# 導入方法
![スクリーンショット_2020-03-23_10_22_46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/268801/088002ca-015a-2a80-5190-89b4d392ec22.png)

あとは流れにそってポチポチしていけば導入完了です。

# 使い方

```
/polly
```
アンケートを投稿したいチャンネルを開いて、テキストボックスに`/polly`とだけ打って送信します。

![スクリーンショット 2020-03-23 10.25.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/268801/77cccd69-a19e-d46f-813f-e25a5d7e2e1f.png)

使い方などが出てきます。

`Create a poll`をクリックするとアンケート作成ダイアログがでます。

![スクリーンショット 2020-03-23 10.39.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/268801/e13a25cc-8d95-5ab3-1f40-c21a16171315.png)

これでアンケートを作成できます。

# 価格

https://www.polly.ai/pricing

無料でも最低限の機能は使えそうですが、
有料版には複数質問やCSV出力、繰り返しなど、色々便利な機能がありそうです。
Pollyで業務効率化にできそうであれば課金するのもアリかもしれません。

以上、参考になりましたら幸いです。
headerとflashの間にできた謎の余白を消す(Devise, Bootstrap)

#はじめに

**（*注意：この記事は学習の一環として実装した物の一部を記事にしています。本記事には正しくない情報、方法を含んでいる可能性があります。）**

先日投稿した[こちら](https://qiita.com/Roy1473/items/b7e6eede261c63ea9216)で
Bootstrap + jQueryを使ってFlashなどの
<b>`<button>xボタン</button>`をクリックした時にFlashを閉じる機能</b>を実装しました。
ただ、ブラウザ場で確認すると、
Flashが消える前、後、双方で<header>と<body>の間に余白がないようにぴったり合わしているにも関わらず
`<header>`と`flash`の間に余白が発生していました。

この余白を無くすことができたので紹介します。

#開発環境
- bootstrap-sass (3.3.7)
- devise (4.7.1)
- jquery-rails (4.3.1)
- rails (5.2.4.1)

#前置き (layoutのデザイン)


```html:header.html
<header class= "header-layout navbar navbar-fixed-top navbar-inverse">
```
- Bootstrapの`navbar-fixed-top`を使ってheaderを固定しております。
- headerの下の`<%= yield%>`には画面いっぱいの背景画像を設置した表紙のようになっております。
- headerの下にHome画面レイアウトの背景画像がぴったりになるように`<body>``padding: top;`でしっかり合わせてあります。)

- <b>(余白ができていた時のパターン↓)</b>

```application.html.erb
<body>
  <%= render 'shared/header'%>
  <%= notice %> //ここ
  <%= alert %>　//ここ
<body>
```

*おそらく、ユーザーがログイン後に`flash[:notice]`が呼び出された場合、flash[:alert]は、余白としてflash[:norice]の上に表示されていました。

#if構文（余白消えない。）
まずこちらを参考にしてみた
https://whatsupguys.net/programming-school-dive-into-code-learning-47/]

```app/views/shared/_flash_messages.html.erb

<% if notice %>
  //条件
<% elsif alert %>
　//条件
<% end %>
```
上記のように条件を組んでブラウザ場の検証を確認しても

```
ブラウザ場の検証(要素)
<header> </header>
  "       "   //謎の余白
<class= "alert">
  "ログインしました" //flash[:notice]
</>
```
flashは表示できているけれど`<header>`と`flash`が呼ばれる`<class= "alert">`の間に`"   " `と表示され、余白の原因になっています。
おそらく`"　"`の部分は表示されなかった`flash[alert]`だと思います。
if構文を使って条件を書いても、、余分な余白は表示されたままでした。

#解決方法
解決方法を調べてみました。
まず、deviseとBootstrapを組み合わせる祭、Bootstrapには、
deviseの`key`である`[:notice]`,`[:alert]`に対応したクラスがないので、`key`を置き換えることが最善です。

こちらを参考に
(https://hachy.github.io/2019/10/15/flashes-with-devise-and-bootstrap.html)

```ruby:app/helpers/users_helper.rb
module UsersHelper
#Bootstrapに対応できるdevise flash[notice][alert]を変更する
  def bootstrap_alert(key)
    case key
      when "alert"
       "warning"
      when "notice"
        "success"
      when "error"
       "danger"
    end
  end
end
```

`:alert`→`:warning`,`:notice`→`:success`に変更する為にヘルパーメソッドを作成しています。(devise userモデルを使っているのでそのヘルパーを利用しています。)

##Flashの変更

<b>変更前↓ （deviseのデフォルト）<b/>

```application.html.erb
<body>
  <%= render 'shared/header'%>
  <%= notice %> //ここ
  <%= alert %>  //ここ
<body>
```

<b>変更後↓</b>

```ruby:app/views/shared/_flash_messages.html.erb
<% flash.each do |key, value| %>
  <div class="alert alert-<%= bootstrap_alert(key)%> close-flash">
    <%= value %>
    <button type="button" class="close close-button" data-dismiss="alert">
      &times;
    </button>
  </div>
<% end %>
```
- deviseのデフォルトflashを変更し、Bootstrapに組み合わせれるように作ったヘルパーメソッド`bootstrap_alert(key)`を使ってdevise `flash[:key]`を変更。


これでFlash[:key]がしっかりBotstrapにも適用でき、余分な`"  "`が呼び出されることなく、特定のflash[:key]だけが呼び出され余白も消えました。

#参考にしたサイト

- https://whatsupguys.net/programming-school-dive-into-code-learning-47/)

- https://hachy.github.io/2019/10/15/flashes-with-devise-and-bootstrap.html)
SQLAlchemyで複数のデータベースに接続

# DB情報の設定
```python:config.py
class SystemConfig:

    SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://{user}:{password}@{host}/{db_name}?charset=utf8'.format(**{
        'user': 'sample_user',
        'password': 'passwd',
        'host': 'host',
        'db_name': 'sample_db_1'
    })
    
    SQLALCHEMY_BINDS = {"second_sample_db": SECOND_SAMPLE_DATABASE}

    SECOND_SAMPLE_DATABASE = 'mysql+pymysql://{user}:{password}@{host}/{db_name}?charset=utf8'.format(**{
        'user': 'sample_user',
        'password': 'passwd',
        'host': 'host',
        'db_name': 'sample_db_2'
    })

Config = SystemConfig
```

# 起動時のファイル設定
```python:database.py
from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

def init_db(app):
  db.init_app(app)
```

```python:__init__.py
import config

from flask import Flask
from api.database import db


def create_app():

    app = Flask(__name__)

    # config.pyを読み込む
    app.config.from_object('config.Config')
    db.init_app(app)

    return app

app = create_app()
```

```python:app.py
from api import app

if __name__ == '__main__':
  app.run()
```

# DBのモデル
```python:model.py
from database import db

class SecondSample(db.Model):
    __bind_key__ = 'second_sample_db' #config.pyで設定したもの
    __tablename__ = 'second_sample_table'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String)
    date = db.Column(db.DATETIME)

    def __init__(self, id, name, date):
        self.id = id
        self.name = name
        self.date = date
```

参考：[flask_sqlalchemyで複数のデータベースに接続したい](https://qiita.com/kk7679/items/d6bc10f6916c435d4d34)
コーディングする際に注意すべきこと（最低限）

注意すべきことを、なるべく具体的に列挙しました。

# 現代ではIDEが指摘してくれる事項
以下のようなものは、優秀なツールだと警告が出るので自然に直りますが、気をつけましょう。
## 冗長な変数
## 一箇所からしか呼ばれない関数
## 複数箇所であっても常に同じ値が入る引数
## どこからも呼び出しのない関数
## 冗長なif
## 到達不能コード(引数が定数になってしまっている結果到達不能になる場合も含む)
## 変なインデント
## typo
## コピペ的関数
## 無駄なパブリックメンバー
## その他lintレベルの自明なフォーマット崩れ

# より本質的な可読性（上記を除いたもの）
## 設計
### 同じことを２回書かずに済む構造を意識する
一般論として、人間が発生させる失敗について統計を取ると、**条件が同じであれば**適当な確率に収束します（もしこれに疑いがあれば、自分で統計を取って試してみてください...）
つまり、単純に試行回数が少ない≒同じことを何度も書かない方が、単純な失敗によるバグは少なくなりやすいということです。もちろん、状況によっては敢えて冗長な書き方をしたほうがバグが少ない場合もありえますが、原則としては記述量が少なくなる事を目指す方がよいでしょう。
### 似たような分岐を何度も書かずに済む構造を意識する（少し難しい）
ある区分に応じた分岐をコードの至るところに埋め込むような実装になると、その区分が増えた場合に、全ての分岐に手を加える必要があり、コードも読みにくくなります。
そこで、区分が増えたことによる分岐がなるべく局所的に済むようなオブジェクト・クラスの設計を心がけます。実際、オブジェクト指向の元になった考え方の一つである、構造化プログラミングでは、以下のような事が提唱されていました。
「データ構造とアルゴリズムは密接に結びついているため、データ構造とアルゴリズムは別々のところで管理するのではなく、一体化したものとして管理をする。これによって、修正の影響範囲を小さく抑えたり、可読性を高めるメリットがある。」

オブジェクトの考え方を持つプログラミング言語では、`someObject.method()`のようにしてmethodを呼び出す事ができます。このmethodの実際の定義をsomeObjectのクラスの定義に書いておけば、呼び出し元で分岐する処理を書かずにすみ、誤った処理が混入する可能性を減らすことができます。
## 変数の寿命は極力短くする、スコープを限定する
単純ですが、ある変数が使用可能な領域を小さくしてしまえば、その変数を間違って使う可能性は減ります。また、この変数の値はどこで変更されて、どの値になっているのか？という事を意識してコードを読む負荷も減り、わかりやすくなります。
## 関数・変数名を自然な英語に近くする
自然な名前になるようにします。これを徹底すると、自然と関数が単一責務を果たすようになります。
具体的には、名前をつけるとき/関数を修正するときに以下の原則に従うようにします。
### 関数名は動詞から始める
### 必要なら目的語をきちんと示す
### 名前と実態の乖離した嘘をつかない
### 副作用がある場合や破壊的な場合は、必要に応じてそれがわかるようにする
### 全体で統一感のある命名方法にする、同じ概念に同じ言葉を使う
ハンガリアンの推奨ではなくて、一つの単語の意味がプログラム全体でなるべく同じ意味になるようにします。あるいは、全てのドメインでの意味の統一が難しい場合には、適当なコンテキストにおいて同じ意味になるようにします。（DDDでいう境界づけられたコンテキスト毎）
これら以外の観点として、変数名の長さとスコープの長さが相関関係になるイメージを持つというのもあります。一文字の変数は特定の短いループや内包表記などに留め、大域的な変数は意味のある名前にする、など。
## 複雑度を下げる
### ガード節などでネストを浅くしてかつ異常時の離脱を促す
インデントが深くなると、単純に今の処理がどの分岐やループに対応しているのかを読むのがつらくなります。
## できる限り一つ一つの処理を参照透過にする
つまり、状態によって出力・結果が変わらないようにします。
## 副作用を極力排除する
つまり、参照透過と合わせて、関数は引数を受け取って戻り値を返すこと以外はなるべくやらないようにします。
（もちろんDBアクセスなどで副作用が必要になる場合はあります）
## 全体(あるいは一定の領域)で書き方に統一感を持たせる、いわゆる「空気を読む」
名前に限らず、関数やメソッドの書き方などもなるべく統一感をもたせるようにします。
例えば、オブジェクト指向的な記述において、共通的にインターフェイスを定義しておいて、複数のクラスでそのインターフェイスを実装して取り扱うようなやり方があります。これは言語・文法のレベルで統一感を生み、また無駄なコードを削減できます。
関数の内部においても、処理の順序や書き方をできる限り統一するようにします。
## （静的な型付けのない言語であっても）型を意識する
関数が参照透過で副作用のないものになると、引数によって戻り値は一意に決まります。この戻り値がどのような型なのか、また受け取る引数の型は何なのか意識すると、コードを読みやすく・書きやすくなります。