OmniAICreator/Qiita-1.07M huggingface dataset:
【GAS】SpeadSheetをDB代わりに使うWebサービスを作る際に考えたこと 

## はじめに
現在社内向けにGoogleWorkspace上のWebサービスを制作中で、データ管理をMySQLなどのDBではなくGoogle SpreadSheetで行う形にしています。
シートをデータ検索をしようと思った際、よく記事で

**「getValuesで全体を配列に入れて処理すれば高速！」**
**「QUERY関数の方が更に高速！」**

と割と便利そうなことは書かれているのですが、実際にやってみるとgetValuesはデータ量が増えれば比例してかなりの時間がかかるようになりますし、QUERY関数は便利ではあるものの専用にシートを使うので、同時編集されないようにロックなどを考える必要があります。
スプレッドシート操作はそこそこ時間がかかるので、ロックした場合は同時に複数の人がアクセスした際の懸念が残ります。

色々考えた結果、今回は以下の仕様でスプレッドシートを管理することにしました。

## Googleアカウント名でユーザ毎に検索用シートを作る
スプレッドシートのQUERY関数はgetValuesと比べると格段に高速です。
データ量が数千件、一万件以上になったときのことを考えるとQUERY関数で検索機能を実装した方が良いでしょう。
[参考：スプレッドシート内をGASで検索するのはQUERY関数利用が桁違いに爆速だった](http://pineplanter.moo.jp/non-it-salaryman/2021/06/03/gas-query/)

ただこの関数を使う場合、関数を記述するためのシートが必要になります。
今回は、WebサービスにログインしているGoogleアカウント名、つまりメールアドレスでシートを作ることにしました。
初回はシートを作成する分だけ少し時間がかかります（気にするほどではないですが）。

```javascript
var sheetName = Session.getActiveUser().getUserLoginId();
var ss = SpreadsheetApp.openById(SHEET_ID);
var ws = ss.getSheetByName(sheetName);
if(!ws){
    //末尾にシートを追加する
    ws = ss.insertSheet(sheetName,ss.getNumSheets());
}
```
## スプレッドシート操作時はUserLockを行う
処理がぶつからないようにユーザロックをかけておきます。
```javascript
var userLock = LockService.getUserLock();
if(userLock.tryLock(5000)){
//スプレッドシートでの検索処理
  userLock.releaseLock();
}

```
これにより、本人はロックをかけているので処理がぶつかることはなくなり、他のユーザは別のシートになるためQUERY操作しても問題ない状態を作ることができました。

## 注意点
シートを作った直後にQUERYをsetValueすると、処理の都合でgetValuesした際の中身が空になってしまうことがありました。
なので、QUERYをsetValuesした後はSpreadsheetApp.flush()でシートを更新することをおすすめします。
```javascript

//QUERYをセット
ws.getRange("A1").setValue(query);

//シートを更新する
SpreadsheetApp.flush();

//結果を取得
var values = ws.getDataRange().getValues();

```

## 補足
この形で作ると処理をしたユーザの数だけシートが増えていくことになりますが、今回自分が作るサービスでは多くても100程度だろうというところと、ワークシートはかなりの数を作成できるので、似たような状況の人がいたらこの形で作るのもよいかもしれません。
トリガーで深夜にGoogleアカウント名のシートを棚卸しするなどもいいかもです。


## おわりに
身も蓋もないですが、こんな縛りプレイみたいなことをせずにできれば普通にMySQLなり使った方がいいですね。
[Rails] ActionPack の routing と args のメモ

SEO対策タスクでルーティング、リダイレクト周りを色々触っている。
この時使ったテクニックのメモ。
(追記)それぞれで別の記事にするべきだった気がしてきた


### pathから対応Controller#Action求める

```rb
> Rails.application.routes.recognize_path("https://www.jobs.com/companies/1")
=> {:controller=>"companies", :action=>"show", :id=>"1"}
```


### コントローラ内のargs


- クエリパラメータで指定されたparamsの一覧 -> `request.query_parameters`
- URLパスで指定されたparamsの一覧 -> `request.path_parameters`


## rails console で、pathを確認する


```rb
> Rails.application.routes.url_helpers.companies_path
=> "/companies"
```
next lint を lint-stagedと組み合わせるやり方

# 概要

lint-staged で next lint を実行するように設定した時に詰まったのでメモしておきます。
以下のように対象のファイルに next lint を実行するように設定していたのですが、これだとエラーがでて上手く動作しませんでした。

```json
"scripts": {
  "dev": "next dev",
  "build": "next build",
  "start": "next start",
  "lint": "next lint",
  "prepare": "husky install",
},
"lint-staged": {
  "*.{js,jsx,ts,tsx}": "npm run lint"
}
```

```
error - Failed to load env from .env.production.local Error: ENOTDIR: not a directory, ...
```
# やり方

公式によるとどうやら .lintstagedrc.js に以下を追加する必要があるようでした。

```js
const path = require('path')

const buildEslintCommand = (filenames) =>
  `next lint --fix --file ${filenames
    .map((f) => path.relative(process.cwd(), f))
    .join(' --file ')}`

module.exports = {
  '*.{js,jsx,ts,tsx}': [buildEslintCommand],
  // 他に実行したいコマンドをここに書く
}
```

こちらのファイルを追加してもエラーが発生する場合は、 package.json に lint-staged の設定が残っていないか確認してください

# 参考

Basic Features: ESLint | Next.js
https://nextjs.org/docs/basic-features/eslint#linting-custom-directories-and-files

`@next/env` `@next/lint` breaks when ran via `lint-staged` · Issue #33096 · vercel/next.js
https://github.com/vercel/next.js/issues/33096
新しいPCが来たのでバイオインフォ用に環境のセットアップ(2) 

前回（[新しいPCが来たのでバイオインフォ用に環境のセットアップ](https://qiita.com/Miya_devbiologist/items/1fa6b8921bb96dfd858a)）に引き続き、PCのセットアップ。

今回はRを入れて、Jupyterで動かす。
ついでにRstudioもいれる。

- Rのインストール
- Jupyterのインストール
- Rstudioのインストール

# Rのインストール
Rのインストールは以下のサイトなどを参考。
[Ubuntu Packages For R - Brief Instructions](https://cran.r-project.org/bin/linux/ubuntu/)
[HEROIC 2021](https://home.hirosaki-u.ac.jp/heroic-2020/1502/)

```terminal:R_install
#インストール時に必要なパッケージのインストール
apt install --no-install-recommends software-properties-common dirmngr
#pubkeyを取得する。
wget -qO- https://cloud.r-project.org/bin/linux/ubuntu/marutter_pubkey.asc | sudo tee -a /etc/apt/trusted.gpg.d/cran_ubuntu_key.asc
#最新のバージョンをインストールするためにrepositoryを追加
sudo add-apt-repository "deb https://cloud.r-project.org/bin/linux/ubuntu $(lsb_release -cs)-cran40/"
#Rのインストール
sudo apt install r-base

#Rの起動
sudo R
```

```R:R
R version 4.1.2 (2021-11-01) -- "Bird Hippie"
...
```
と表示されるので、バージョンを確認する。
バージョンが古いとSeuratなどが使えなくて困る。

```R:R
#インストールの確認ついでに必要なパッケージをインストール。
install.packages(c('tidyverse','Matrix','BiocManager','Seurat')
```

# Jupyterのインストール
次に、JupyterでRを動かす。
以下を参考。
[How To Set Up a JupyterLab Environment on Ubuntu 18.04](https://www.digitalocean.com/community/tutorials/how-to-set-up-a-jupyterlab-environment-on-ubuntu-18-04)
```terminal:jupyter_install
sudo apt install python3-pip
sudo pip3 install jupyterlab
jupyter lab
```
次にRを動かせるようにする。
```R:R
install.packages('IRkernel')
IRkernel::installspec(user=FALSE)
```

jupyterを起動して、Rが動くことを確認！

# Rstudioのインストール
Rstudioも入れておく。

```terminal:Rstudio_install
sudo wget https://download1.rstudio.org/desktop/bionic/amd64/rstudio-2022.02.0-443-amd64.deb
sudo apt install ./rstudio-2022.02.0-443-amd64.deb
```
Ubuntuのアプリケーション内にRstudioができているはずなので、起動してみる。
良い資料

最近資料を読んだり書いたりして思ったこと。

資料同士に重複がないこと。
曖昧な記載はしないこと。
網羅性を確認すること。
わかりやすいこと。(2番めとかぶる)
例にはできるだけ実際のデータを利用すること。
(画面イメージの商品コード入力項目の入力例にあああとかではなく、HL1254と記載する。)
Raspberry PiでMySQLの保存先を変更する方法

## 概要
Raspberry PiでMySQLを使用する際にデータベースの保存先を外付けSSDにしたかったが、configの`datadir=`を変更しただけではMySQLが起動しなくなった。

```
$ systemctl status mysql.service
mysql.service: Failed with result 'exit-code'.
Failed to start MySQL Community Server.

$ journalctl -xe
systemd[1]: mysql.service: Failed with result 'exit-code'.
-- Subject: Unit failed
-- Defined-By: systemd
-- Support: http://www.ubuntu.com/support
--
-- The unit mysql.service has entered the 'failed' state with result 'exit-code'.
systemd[1]: Failed to start MySQL Community Server.
-- Subject: A start job for unit mysql.service has failed
-- Defined-By: systemd
-- Support: http://www.ubuntu.com/support
--
-- A start job for unit mysql.service has finished with a failure.
```
そんなエラーログを吐かれてもさっぱりである。

どうやら追加設定が必要な模様。
## 実行環境
- マシン：Raspberry Pi B+
- OS：Ubuntu Server 20.04.4
- MySQL：Ver 8.0.28-0

## 外付けSSDのフォーマット・マウント

```
パーティション作成
$ sudo parted /dev/sda
(parted) mklabel gpt
(parted) mkpart
Partition name?  []?
File system type?  [ext2]? ext4
Start?0%
End? 100%
Warning: You requested a partition from 0.00B to 128GB (sectors 0..250069679).
The closest location we can manage is 17.4kB to 1048kB (sectors 34..2047).
Is this still acceptable to you?
Yes/No?Yes
(parted)q

フォーマット
$ sudo mkfs.ext4 /dev/sdb1

マウント
$ sudo mkdir /mnt/mysql

$ sudo nano /etc/fstab
～～追記～～
/dev/sda2               /mnt/mysql      ext4    nofail          0       0

$ sudo mount -all
$ sudo chown mysql:mysql /mnt/mysql -R
```
## MySQLの設定
```
$ sudo systemctl stop  mysql.service

$ sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf
～～[mysqld]以下　追記、又は編集～～
datadir = /mnt/mysql
```
## AppArmorの設定

```
$ sudo nano /etc/apparmor.d/tunables/alias
～～追記～～
alias /var/lib/mysql/ -> /mnt/mysql/,

$ sudo systemctl restart apparmor.service
```

## MySQLのデータを移動・再起動
スラッシュの付け方を間違えると、階層が変わるので注意
```
$ sudo rsync -avuz /var/lib/mysql.bak/ /mnt/mysql

$ sudo systemctl start mysql.service
```

## 参考文献
straightweeds.hatenablog.com 様
[RaspberryPiにmysqlをインストール](https://straightweeds.hatenablog.com/entry/2017/04/17/170655)

DigitalOcean Community 様
[How To Move a MySQL Data Directory to a New Location on Ubuntu 16.04](https://www.digitalocean.com/community/tutorials/how-to-move-a-mysql-data-directory-to-a-new-location-on-ubuntu-16-04)
300日後に個人開発をするワニ

初投稿です．初投稿にして一回書いた記事が全部消えました．もう最近ずっとこんな感じです．

こちらの記事に触発されて，何かのきっかけになればと思って衝動的にアカウントを作ってしまいました．
https://qiita.com/kojimadev/items/f4a6539e4fbc641c6fa0

# 自己紹介

この春，某公立大学の情報系の学部を首席で卒業し大学院に進学するものの，特に実績らしい実績もなくこれからどうしようといったところです．プログラミングで何か面白いことがしたいなと日々思っています．

経歴としては，大学ではじめてプログラミングに出会い，まず競技プログラミングAtcoderを始めました．1年半くらいで青までいきましたが，頭打ちになってしまい放置．その後，Atcoder青という肩書だけ背負って某企業の開発アルバイトに応募したところなんと採用していただきました．

開発アルバイトは9か月目になりますが，体調を崩したり卒論が忙しかったりしてあまり力を入れることができていません．仕事は社内webサービスの開発が主で，GoやPythonを書いてました．自分でプロジェクトをもったことはありません．

# やりたいこと
単刀直入に言うと，**プログラミングでもっとお金を稼ぎたい**と思っています．なんだこいつと思われるかもしれませんが，家がかなり貧乏なので，お金がほしいというのが正直なところかなり原動力になっています．とはいえまだ実績も実力も知識もプランも何もないので，すべては今から始まります．

# やること
まずやるべきだろうなと思ったことは，

1. 開発アルバイトに全力を尽くして経験値を得る
2. 人脈を広げる
3. インプット・アウトプットを増やす

この3つです．Qiitaをはじめるのも2,3の一環です．

今のところ挫折する気しかしないですが，この記事が伝説の始まりとなることを祈って頑張っていきたいと思います．
【MySQL】SELECT結果をダブルクォーテーション付きCSVファイルに出力する方法

# はじめに
MySQLのSELECT結果をダブルクォーテーションなしで出力すると、Excelで開いた時にズレることがあるので、ダブルクォーテーション付きCSVファイルに出力する方法を備忘として残しておきます。

# コマンド
```
mysql -u {user} -p {DB} -h {host} -e "select文" | sed -e 's/\t/","/g;s/^/"/;s/$/"/' > /tmp/test.csv
```
【sed部分の説明】
`sed -e 's/\t/","/g;s/^/"/;s/$/"/'`
- `-e`オプション：指定したスクリプト（条件式）で変換処理を行う
- `s/\t/","/g`：全てのタブを「ダブルクォーテーション-カンマ-ダブルクォーテーション（","）」に置換
- `s/^/"/`：行頭にダブルクォーテーション付与
- `s/$/"/`：行末にダブルクォーテーション付与

# おわりに
出力ファイル内にマルチバイト文字が含まれているとExcelで開いた時に文字化けする（文字コードがUTF-8の場合）ので、適宜文字コード変換すること。
Jupyterのipynbファイルからソースを取り出すPowerShellスクリプトを書いてみた。

# はじめに

JupyterLabに書き溜めたPythonのソースをセルごとに別ファイルで
書き出したくてPowerShellスクリプトを書いてみました。

## 動作

スクリプトを実行するとスクリプトが置かれたフォルダを検索し、
ipynbファイルを順次検索、cell順に連番を付与しファイルに書き出します。

## スクリプト

```powershell
# ターゲットファイルの拡張子
$target     = "*.ipynb"
# 書き出すファイルの名前
$basename   = "source"
# 書き出すファイルの拡張子
$ext        = ".py"
# 開始番号
$count      = 1

# このスクリプトがあるフォルダを起点に、指定拡張子のファイルリストを取得する
$itemList = Get-ChildItem  -File $PSScriptRoot -Include $target -Recurse

# ファイルごとに処理
foreach($item in $itemList){
    # JSON文字列をPSCustomObjectに変換
    $json = Get-Content $item.FullName | ConvertFrom-Json
    # cellsオブジェクト内を走査
    foreach($c in $json.cells){
        # ソース格納用
        $str = ""
        # ソースオブジェクト内を走査し、文字列を追加していく
        foreach($s in $c.source){
            # 処理時間計測マクロを削除
            if($s.IndexOf("%%time") -ne 0){
                # 1行ごとにソースを追記
                $str += $s.ToString()
            }
        }
        # ソースが存在する場合、ファイルに書き出す。
        if($str -ne ""){
            # 書き出すファイルパスを作成
            $path = ($PSScriptRoot + "\" + $basename + ("{0:000}" -f $count++) + $ext)
            # ファイルを書き出す
            $str | Out-File $path
        }
    }
}
```
Google Optimize でどのテストパターンがアサインされているか確認する

## 結論

※結論ファーストなので．

テストのターゲットになっているサイトへアクセス時，
`_gaexp` という名前のcookie に情報が格納されている．

cookie 情報はDevTools の`Application > Cookies` から確認できる．


![Screen Shot 2022-03-08 at 17.41.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/532781/1b231455-dfd0-c852-e5d5-3b1416ec9e94.png)

例えば`_gaexp` の値が次のようだとすると，

```
GAX1.2.XXXXXXXXXXXXXXXXXXXXXX.99999.1!YYYYYYYYYYYYYYYYYYYYYY.88888.2
```

テストID`XXXXXXXXXXXXXXXXXXXXXX` のテストパターン`1` , およびID`YYYYYYYYYYYYYYYYYYYYYY` のテストパターン`2` がアサインされている状態．

※今現在のページに当たっていなくても，過去にアサインされたテストの情報がこのcookie に残っていることもあるそうなので注意．

**参考：**

https://stackoverflow.com/questions/44412241/is-it-possible-to-read-experimentid-and-variationid-in-javascript-with-google-op


## そもそも

結論のほかに書くことが無かったです．

Cookie もっと勉強しよう．

![761C08CB-91F9-4A9B-B5ED-681472EA9983.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/532781/071a3870-939a-b5b7-2da0-a55b23eac8dc.png)
[Rails]すでにあるテーブルのカラムにバリデーションを設定したい

## 「Rails」バリデーションを追加する方法を初心者向けに解説

モデル設計時にテーブルのカラムにNotNULL制約を付け加えるのを忘れてしまった。。。
そんな時にテーブルのカラムに後からバリデーションを付与する方法を紹介します！

### 環境
・macOS
・Rails 5.2.6.3
・ruby 2.5.1

## 既にあるテーブルのカラムにバリデーションを追加する手順

１、新規マイグレーションファイルを作成
２、マイグレーションファイルに追加したいバリデーションを記入
３、rails db:migrateを実行

それでは実践に入っていきます。

### 実践
私はusersテーブルのnameカラム・emailカラム・passwordカラムにNOTNULL制約を付け加えるのを忘れたので付け加えていきます。上記の3つの手順に従って、

##### １、新規マイグレーションファイルを作成する
```ruby
rails g migration change_user_validation
```
これでマイグレーションファイルの作成が完了。

##### ２、マイグレーションファイルに追加したいバリデーションを記入する

```ruby
class ChangeUserValidations < ActiveRecord::Migration[5.2]
  def change
    change_column_null :users, :name, false
    change_column_null :users, :email, false
    change_column_null :users, :password, false
  end
end
```

これでusersテーブルのnameカラム・emailカラム・passwordカラムにNOTNULL制約を付与するマイグレーションファイルが完成。

##### ３、マイグレーションファイルを実行する

```ruby
rails db:migrate
```

これで完成。

今回のNotNULL制約はrailsのモデルではなくDB側に付与されるので、SQL文でNULLのデータを作成しDBに登録しようとするとエラーが出るはずです。NULLのデータが弾かれていると無事に完成です。
Ruby 問題 if,else ポイント付与サービス

# はじめに
１月〜プログラミングスクールで学習しています。
これから平日は~3/25まで毎日何かしらアウトプットのため投稿を続ける予定です。
今回は if,elseを使って簡単なポイント付与サービスを実装します。
※Ruby2.6.5の環境を使用し学習しています。

# 問題
トレーディングカードショップで商品を購入するとポイントが付与されます。
以下条件によってポイント付与率が変わります。
- 購入金額が9999円以下の場合、1%のポイント
- 購入金額が10000円以上の場合、2%のポイント
このように付与されるポイントを出力するメソッドを作ります。

また、購入者が誕生日の場合はポイントが5倍になります。
誕生日の場合はtrue, 誕生日でない場合はfalseで表します。
また、小数点以下をすべてのポイント計算が終わったあとに.floorメソッドを使って切り捨てます。
```Ruby:例
calculate_points(500, false) → 5ポイント付与されました。
```

# 実装例
まずは購入金額が9999円以下の場合、1%のポイント、購入金額が10000円以上の場合、2%のポイントが付与されるように下記記述しました。
amount = 購入金額　is_birthday = 誕生日か、それ以外か（ture,falseで表します）
```Ruby 
def calculate_points(amount, is_birthday)
  if amount <= 9999
    point = amount * 0.01
  else 
    point = amount * 0.02
  end
```

次に誕生日か否かの条件分岐を作ります。
.floorメソッドについて（Ruby 3.1ですが）
[Ruby 3.1 リファレンスマニュアルinstance method Float#floor](https://docs.ruby-lang.org/ja/latest/method/Float/i/floor.html)
```Ruby
if is_birthday
    point = point * 5
  end
  puts "#{point.floor}ポイント付与されました。"
end
```

上記2つを合わせたのがこちらになります。
```Ruby
def calculate_points(amount, is_birthday)
  if amount <= 9999
    point = amount * 0.01
  else 
    point = amount * 0.02
  end
  if is_birthday
    point = point * 5
  end
  puts "#{point.floor}ポイント付与されました。"
end
```

今回ポイントとなったのはポイントには小数点が必要ないことです（ポイントだけに:innocent:）
そこで.floorメソッドを使うことでうまく表現することができました〜
ではまた:running:
Laravel api.phpに記載したURLがデフォルトで/apiついてしまう件

LaravelでAPI作成のした際にルーティングでroutes/api.phpに記載すると /api/〜になってしまう。

/api部分を無くすためには以下を削除すればいい。

```php:app/Providers/RouteServiceProvider.php
Route::prefix('api') //prefixを削除
         ->middleware('api')
         ->namespace($this->namespace)
         ->group(base_path('routes/api.php'));
```

これと同じ挙動でweb.phpなどでも活用できる
```php:
Route::prefix('user')->group(function () {
    Route::post('/', 'App\Http\Controllers\UserController@index');
    Route::post('create', 'App\Http\Controllers\UserController@create');
});
```
prefix使えばまとめることも簡単ですね。
本日のRuby基礎練習問題(22/3/10)

# 本日のRuby基礎練習問題(22/3/10)

**問題**
ECサイトのポイント付与サービスを考えます。
購入金額が999円以下の場合、3%のポイント
購入金額が1000円以上の場合、5%のポイント
このように付与されるポイントを出力するメソッドを作りましょう。

ただし誕生日の場合はポイントが5倍になります。
誕生日の場合はtrue, 誕生日でない場合はfalseで表します。
また、小数点以下をすべてのポイント計算が終わったあとに切り捨てましょう。

呼び出し方：
calculate_points(amount, is_birthday)

出力例：
calculate_points(500, false) → ポイントは15点です
calculate_points(2000, false) → ポイントは100点です
calculate_points(3000, true) → ポイントは750点です

 # 私の回答
```qiita.erb
def calculate_points(amount, is_birthday)
  if amount <= 999
    point = amount * 0.03
  else
    point = amount * 0.05
  end

```

**解説**
購入金額におけるif関数は記述できたが誕生日における関数をどう記述すればよいかわからず
途中式で制限時間を超えてしまったため回答できず。

# 模範回答
```qiita.erb
def calculate_points(amount, is_birthday)
  if amount <= 999
    point = amount * 0.03
  else
    point = amount * 0.05
  end
　　　　if is_birthday
    point = point * 5
  end
  puts "ポイントは#{point.floor}点です"
end
```

**模範解説**
まず、購入金額が999円以下なのかまたは1000円以上なのかを判断し、2~6行目で処理しています。
そして、購入金額に対して0.03または0.05をかけて、ポイント数を計算して変数pointに代入しています。

その後に、is_birthdayがtrueの場合すなわち誕生日の場合は、直前で定義した変数pointに5をかける処理を行います。

最後に、point.floorとしてポイントの小数点以下を切り捨て、獲得したポイント数を出力しています。

別解としては、条件式をamount <= 999からamount < 1000に変える方法などがあります。

# 感じたこと
正直、模範解答を見ると簡単に思えてしまう。
しかし回答中はかなり悩んでしまう。まだ自分自身のレベルが垣間見えるが、
プラスに考え伸び代があると思い、日々失敗を繰り返し吸収していきたい。
.floorメソッド(小数点切り捨て)を学べてよかった。
Googleスプレッドシートの列の幅を自動調整させたときにマージンをつくるには

# 背景：Googleスプレッドシートでの列の幅の自動調整

* Googleスプレッドシートでは、列を選択した状態で、カーソルを列の右端の境界線上に合わせると、［➡］になります。
    * 複数の列も選べます。その場合、いちばん選択した中で右の列の右側の境界線上にカーソルを合わせます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64391/e3c64752-5554-bf60-e390-718d26d0a1d2.png)

* この状態でダブルクリックすると、列内のもっとも幅の広いデータに合わせて列の幅が自動調整されます。（フォントサイズの影響も受けるので、「幅の広さ」は文字数だけでは決まりません）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64391/eb34b313-9fa5-6f7e-d6f9-e918ef929ea0.png)

* この機能自体はとても便利で重宝しますが、調整される列の幅にはマージンがほとんどなく、設定もできないようです。列数が多いと、表示がゴチャついて眼に優しくありません。

# やりたいこと：列の幅を自動調整しつつ、セル内のマージンを確保したい


* で、お手軽にマージンをつけてみます。方法というよりライフハックに近いですが。。。

# 方法

* 以下のように、少しだけ手間をかけるだけです。

1. 幅を自動調整したい列を選択する。
1. フォントを１サイズ（または好きなだけ）大きくする。
1. 列番号の境界線をダブルクリックして列の幅を自動調整する。
1. フォントサイズを元に戻す。

## 注意点

* 自動調整したい範囲内に、複数のフォントサイズが混在している場合、この方法は使えません。

**おしまい**
ESP8266についてメモ


# 参考
クルマのバッテリー電圧・車室内のCO2濃度をリアルタイムにグラフ表示
https://qiita.com/ktr_ymuc/items/a2be3ccbb0eb555758fc

 ESPr Developer (ESP-WROOM-02 ( ESP8266 )
https://www.mgo-tec.com/blog-entry-ss-wroom-howto01.html

https://ehbtj.com/electronics/esp8266-as-arduino/

https://qiita.com/hatt0519/items/44e9573ac9999f843fbf

# ESP8266

ESP8266は中国の上海に拠点を置く企業Espressif Systemsによって製造されているTCP/IPスタックとマイクロコントローラを備えたWi-Fiモジュールである

「ESP8266」は、正式には「CPU」と「Wi-Fi機能」が搭載されたIC「ESP8266EX」（Epressif Systems社）に、「周辺回路のクロック」や「メモリ」「パターンアンテナ」「シールド」を付けてモジュール化した、「ESP-WROOM-02」を指します。

CPU: Tensilica Xtensa LX106 running at 80 MHz
技適対応済み

# Arduino
Arduino Nano
 CPU: ATmega168  (v3からATmega328）
100日でSQLの達人になる@LeetCode! Day38 <ORDER BY句の変数名について>

## 1212. Team Scores in Football Tournament （問題レベル: Medium）要課金
今日の問題はこれ。
試合結果の表からチーム毎のポイント計算しリストアップするという問題。

比較的ややこしい問題ですが、30日以上も毎日`SQL`問題に取り組んできたので自力で解けるようになってきました。
この問題の面白かったのは、`JOIN ON`句で`OR`を使ったところだと思います。
`OR`を使わないともうひとつ`JOIN`句が必要になり冗長なクエリになってしまいます。

今日のポイントは`ORDER BY`句では`SELECT`文で作成した新しい変数名をそのまま使えるとした。
まだきちんと理解できていないので復習が必要ですが、最初はこのように同じクエリを長々と記載していました。
```
SELECT t.team_id, t.team_name,
SUM(CASE
WHEN t.team_id = m.host_team AND m.host_goals > m.guest_goals THEN 3
WHEN t.team_id = m.host_team AND m.host_goals = m.guest_goals THEN 1
WHEN t.team_id = m.host_team AND m.host_goals < m.guest_goals THEN 0
WHEN t.team_id = m.guest_team AND m.host_goals > m.guest_goals THEN 0
WHEN t.team_id = m.guest_team AND m.host_goals = m.guest_goals THEN 1
WHEN t.team_id = m.guest_team AND m.host_goals < m.guest_goals THEN 3
ELSE 0 END) AS num_points
FROM teams t
LEFT JOIN matches m
ON t.team_id=m.host_team OR t.team_id=m.guest_team
GROUP BY t.team_id, t.team_name
ORDER BY
SUM(CASE
WHEN t.team_id = m.host_team AND m.host_goals > m.guest_goals THEN 3
WHEN t.team_id = m.host_team AND m.host_goals = m.guest_goals THEN 1
WHEN t.team_id = m.host_team AND m.host_goals < m.guest_goals THEN 0
WHEN t.team_id = m.guest_team AND m.host_goals > m.guest_goals THEN 0
WHEN t.team_id = m.guest_team AND m.host_goals = m.guest_goals THEN 1
WHEN t.team_id = m.guest_team AND m.host_goals < m.guest_goals THEN 3
ELSE 0 END) DESC, t.team_id ASC
```
しかし、`Discuss`を見て、`SELECT`文で作成した新しい変数名をそのまま使えると知り下記のように修正しました。
実行速度も早くなった気がします。

```:SQL
SELECT t.team_id, t.team_name,
SUM(CASE
WHEN t.team_id = m.host_team AND m.host_goals > m.guest_goals THEN 3
WHEN t.team_id = m.host_team AND m.host_goals = m.guest_goals THEN 1
WHEN t.team_id = m.host_team AND m.host_goals < m.guest_goals THEN 0
WHEN t.team_id = m.guest_team AND m.host_goals > m.guest_goals THEN 0
WHEN t.team_id = m.guest_team AND m.host_goals = m.guest_goals THEN 1
WHEN t.team_id = m.guest_team AND m.host_goals < m.guest_goals THEN 3
ELSE 0 END) AS num_points
FROM teams t
LEFT JOIN matches m
ON t.team_id=m.host_team OR t.team_id=m.guest_team
GROUP BY t.team_id, t.team_name
ORDER BY
num_points DESC, t.team_id ASC
```
`LeetCode Curated SQL 70`の`Medium問題`も残りあと少しです。

* [LeetCode](https://leetcode.com/)の問題は、`MS SQL Server`で解いています。
Sync Gateway機能解説：Sync Gateway間レプリケーション


## はじめに

ここでは、Couchbase Mobileを構成するSync Gatewayの機能であるSync Gateway間レプリケーションについて解説します。

なお、Couchbase Mobileについては、[Couchbase Mobileアプリケーション開発へのロードマップ](https://qiita.com/yoshiyuki_kono/items/6a5bbad1b02196a25417)に記事をまとめている他、（これらの記事を元に構成した）以下の電子書籍を無償で頒布しています。

https://techbookfest.org/product/5074954942939136?productVariantID=5042865933647872

また、Couchbase Mobileは、Couchbase LiteとCouchbase Serverとのデータ同期機能を提供します。Couchbase Serverの存在意義、機能詳細、利用方法等については、拙著[NoSQLドキュメント指向データベースCouchbase Serverファーストステップガイド(インプレスR&D刊)](https://www.amazon.co.jp/-/en/%E6%B2%B3%E9%87%8E-%E6%B3%B0%E5%B9%B8/dp/429560030X/ref=tmm_pap_swatch_0?_encoding=UTF8&qid=1636588872&sr=1-1)や、[NoSQL/JSONデータベースCouchbase Server理解・活用へのロードマップ](https://qiita.com/yoshiyuki_kono/items/4c9cd1115efc5807b49f)にまとめてある記事をご参考ください。

## 背景

Sync Gateway間レプリケーション(Inter Sync Gateway Replication)は、複数のSync Gatewayの同期を実現します。

これによって、クラウドや中央データセンターと同期するSync Gateway(と対応するCouchbase Server)をエッジデータセンターに配置することができ、Couchbase Liteを使ったモバイルサービスは、ローカリティーの恩恵を実現することができます。

Couchbase Mobileは、古典的なスタートポロジーからなる構成に留まらず、マルチティア階層のネットワーク構成に対応することができます。

### 関連技術

#### エッジデータセンター

各パブリッククラウドから、下記のようなエッジデータセンターを実現するサービスが提供されています。

- AWS Local Zones
- AWS Wavelength
- AWS Outpost
- Azure Edge Services
  - Azure Edge Zones
  - Azure Edge Zone w/ Operator
  - Azure Private Edge Zone
- Google Distributed Cloud Powered by Anthos

多階層でのデータ同期においては、きめ細かいアクセス制御を実施しながら、集中型クラウドクラスターと多数のエッジクラスター間で同期する必要があります。このような要件は、エンタープライズレベルでますます重要となっています。


#### データストリーミング

センサー等のIoT/エッジデバイスで発生した情報を収集するための技術として、データストリーミングを実現する様々な技術が存在します。オープンソーステクノロジーとしては、例えば以下のようなものがあります。

- Apache Kafka
- Apache NiFi
- Apache Flink
- Apache Spark Streaming

このようなデータストリーミングの技術を組み合わせ、下流と上流でデータの入出力を構成する代わりに、エッジデバイス上のデータベースとしてCouchbase Liteを用いて、通信に関わる処理をCouchbase Mobileに委ねることが考えられます。

## Sync Gateway間レプリケーション解説

### アーキテクチャー

Sync Gateway間レプリケーションでは、ローカルのSync Gatewayノードのレプリケータにより、Sync Gatewayを介してローカルのCouchbase Serverデータベース内のドキュメントに対して行われた変更が、リモートのCouchbase Serverデータベースに対して反映されることが、ローカルとリモートのSync Gatewayノードの協働によって保証されます。変更は、レプリケーションの構成に従って、他のSync Gatewayインスタンスにレプリケートされます。

ここでのローカル/リモートの関係は、変更の発生した側をローカルと捉えた、いわば相対的な関係です。エッジ=ローカル、リモート=中央データセンターではなく、同期は双方向でありえることにご注意ください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176567/6ba385c2-475b-01e5-4a1e-91f1b14895d4.png)



Sync Gateway間レプリケーションは、Couchbase Liteクライアントとのレプリケーションと同様に、WebSocketに基づいています。両者は、まったく同じプロトコルです。


### ユースケース

マルチクラウド展開モードでは、多数のマルチエッジクラスターがクラウドデータセンターのクラスターと同期します。
各エッジは、万が一クラウドデータセンターへのネットワークが失われた場合も自律的に動作します。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176567/30278864-e842-837b-1712-4be660afeba9.png)


### Couchbase ServerのXDCRとの関係

Couchbase Serverは、XDCR(クロスデータセンターレプリケーション)という、クラスター間同期の機能を提供しています。
Sync Gateway間レプリケーションは、Couchbase LiteアプリケーションとCouchbase Serverとの同期を行う場合に特有の処理を担います。Couchbase Liteどの同期が介在しない場合は、複数のCouchbase Serverの同期のために、Sync Gateway間レプリケーションを用いるのではなく、XDCRを用います。

### 関連情報

https://docs.couchbase.com/sync-gateway/current/sync-inter-syncgateway-overview.html
useFutureと useStream(Flutter Hooks)

# useFutureとuseStream
`FutureBuilder`や`StreamBuilder`が楽に書けるようになります。使い方は一緒ですが、`Future`の方が使用頻度が多いので、`useFuture`を取り上げていきます。

```dart
AsyncSnapshot<T> useFuture<T>(
  Future<T>? future, {
  T? initialData,
  bool preserveState = true,
}) {
  return use(
    _FutureHook(
      future,
      initialData: initialData,
      preserveState: preserveState,
    ),
  );
}
```
[preserveState]をtrueにすると、Futureのインスタンス（クラスをもとに作成したオブジェクトの実体）が変わった場合でも現在の値を維持することができます。通常はtrueの状態です。`useMemoized`を使用して状態を一度保存しないと何度も呼ばれてしまうのでセットで考えて下さい。
# useFutureの使用例
```dart
final data = useState(0);

final getData = useMemoized(() => getApplicationDocumentsDirectory(), [data.value]);

final dataFuture = useFuture(getData);

useEffect(() {
  Future.delayed(const Duration(seconds: 10), () {
    data.value += 1;
  });
}, [data.value]);
// Flutterの場合、Delayを使用する場合Futureを使う。
```
```dart
 final getImageData = useMemoized(
        () => getImageSize(
            hoge.id, dirSnapshot.hasData ? dirSnapshot.data!.path : null),
        [dirSnapshot.data]);
    final hogeImageSizeSnapshot = useFuture(getImageData);

    useEffect(() {
      if (hogeImageSizeSnapshot.hasData &&
          hogeImageSizeSnapshot.data != null) {
        imageSize.value = hogeImageSizeSnapshot.data;
      }
      return;
    }, [hogeImageSizeSnapshot.hasData]);
```
# useStreamの使用例
```dart
class DataCounter extends HookWidget {
  const DataCounter({Key? key})
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    final controller = useStreamController<int>();
    return Scaffold(
      appBar: AppBar(
        title: Text('カウンター'),
      ),
      body: HookBuilder(
        builder: (context) {
          final count = useStream(controller.stream);
          return GestureDetector(
            onTap: () => controller.add(count.data! + 1),
            child: Text('現在 ${count.data} 回目'),
          );
        }
      ),
    );
  }
}
```
【ダッシュボードページ】ダッシュボードページを作ってみる

ダッシュボードページは、単一のページ上に複数のタイルを貼り付けて作成していきます。各タイルには、その中に表示するWEBレポートのURLリンクが定義されています。
[Sharperlight](https://sharperlight.jp/)でダッシュボードページを作るには、[Sharperlight](https://sharperlight.jp/)サービスのフロントページからアクセスします。
URLはこんな感じです。
http://{yourservername}/mdService1Rest/Index
このようなページが表示されます。
![2022-03-10 Figure 01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2530698/dc875aa2-254d-adf5-0190-918191cae6c3.png)
左側のメニューの[ダッシュボード]、そして[リスト]をクリックします。
![2022-03-10 Figure 02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2530698/915802bb-eab0-86cf-8e64-7aac376f8291.png)
右側にダッシュボードページの一覧が表示されます。（既に作成されたものがある場合）
![2022-03-10 Figure 03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2530698/e18adf0c-88ac-490b-7aea-7a01d869362a.png)
[新規]ボタンをで新しいダッシュボードページを作成します。
先ずは土台となる[ページ]の定義を行います。**コード**、**グループ**、**タイトル**を入力します。
![2022-03-10 Figure 04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2530698/f12c28f7-824d-ddc8-496d-00c531f5a3cb.png)
次に[タイル]の作成をします。**コード**、**タイトル**、**コンテンツURL**を入力します。
![2022-03-10 Figure 05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2530698/7e9dead2-f04a-a2e6-82d2-61fd6c87abca.png)
[保存]ボタンで保存するとタイルがひとつ配置されたダッシュボードページが表示されます。
![2022-03-10 Figure 06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2530698/38c1b434-d27d-4c72-a107-75e1d39d1bc0.png)
そのままでは表の下部が切れてしまっているので、タイルの縁にマウスを移動して、タイルのサイズを調整します。調整が済んだらマウスをタイル右上の角にあて[ギア]アイコンを表示します。[ギア]アイコンでタイルの編集モードに移動し、[保存]ボタンを押すことでタイルの大きさの調整が確定されます。
![2022-03-10 Figure 07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2530698/9b1d594a-3ea6-1eae-2829-0563c95096ff.png)
このような感じになります。
![2022-03-10 Figure 08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2530698/2da33447-60c9-0246-c8d3-7cc5e91473a6.png)
もう一つタイルを追加してみましょう。[ギア]アイコンで編集モードに入ります。
[新規]ボタンで新しいタイルを追加します。
上記と同じように**コード**、**タイトル**、**コンテンツURL**を入力します。
![2022-03-10 Figure 09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2530698/5aea1604-8308-d168-405c-32696c31db82.png)
[保存]ボタンを押すと...、既存のタイルの下側に新しいタイルは追加されるので、ドラッグして配置を変更します。サイズもね。
配置とサイズが決まったら[ギア]アイコンで編集モードに入り、[保存]ボタンで決定します。
![2022-03-10 Figure 10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2530698/6a60f3d1-27c7-fc9b-4f6a-4b0cecb36680.png)
このようにタイルを追加して、お好みのダッシュページを作成してみてください。

:postbox:[Sharperlight](https://sharperlight.jp/)
PostgreSQLでinvalid input syntax for type jsonエラー


PostgreSQLのsql/plpgsqlでinvalid input syntax for type jsonエラーが起こる場合。

例えば以下のようなエラーに遭遇した。

```
ERROR:  invalid input syntax for type json
DETAIL:  The input string ended unexpectedly.
CONTEXT:  JSON data, line 1: 
SQL state: 22P02
```

## 解決

jsonファイルの最後に空白行が入っている可能性があるので、それを除去する。

> it was beucase an empty line in the end of the json file
> [sql - PostgreSQL invalid input syntax for type json The input string ended unexpectedly - Stack Overflow](https://stackoverflow.com/questions/67665557/postgresql-invalid-input-syntax-for-type-json-the-input-string-ended-unexpectedl)
EJSを使用したnpm run実行時の引数をHTMLに埋め込む方法

# 概要

`npm run`実行時に渡された引数をHTML内に埋め込む方法です。  
今回はHTMLテンプレートエンジンである[EJS](https://ejs.co/)を使用します。

# 手順

今回は以下のHTML内の`<%= foo %>`部分を`npm run`実行時に渡された引数で置換します。

```index.ejs
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8" />
  </head>

  <body>
    <p>本日は<%= foo %>なり</p>
  </body>
</html>
```

## EJSのインストール＆ejsファイル作成

以下のコマンドでEJSをインストールします。

```cmd
npm install ejs
```

続いて、テンプレートファイルとなるEJSファイルを作成します。  
今回は`index.ejs`というファイル名にします。

```index.ejs
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8" />
  </head>

  <body>
    <p>本日は<%= foo %>なり</p>
  </body>
</html>
```

## scripts部分へビルドコマンドを登録する

EJSを使用して、EJSファイルをからHTMLファイルを生成するためのビルドコマンドを`package.json`の`scripts`部分に定義しておきます。  
今回は`build`としておきます。

```package.json
"scripts": {
    "build": "ejs ./index.ejs -o ./index.html foo=${npm_config_bar}",
}
```

`${npm_config_〇〇}`と定義しておくと、`npm run`実行時に`--〇〇=△△`を引数として渡すことで`${npm_config_〇〇}`の部分が`△△`に置き換わります。  
Windows環境の場合、`${npm_config_bar}`を`%npm_config_bar%`に変更してください。

## HTML生成

それでは`npm run build --bar=晴天`を実行しましょう。このコマンドを実行することで、`ejs ./index.ejs -o ./dist/index.html foo=晴天`が実行されます。  
実行が終了したら、生成されたHTMLを見てみましょう。

```index.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8" />
  </head>

  <body>
    <p>本日は晴天なり</p>
  </body>
</html>
```

`<%= foo %>`の部分が`晴天`に置き換わっていたらOKです。

# おわりに

以上がテンプレートエンジンであるEJSを使用した`npm run`実行時の引数をHTMLに埋め込む方法でした。  
「もっと簡単に`npm run`実行時の引数をHTMLに埋め込む方法があるよ～」という方がいらっしゃれば、コメントで紹介いただければ嬉しいです。

# 参考文献

- [EJS](https://ejs.co/)
- [config -npm Docs-](https://docs.npmjs.com/cli/v8/using-npm/config)
- [npm_package_config と npm_config](https://akabeko.me/blog/2016/09/npm_package_config-and-npm_config/)
GAS の API に js の fetch で doPost する


GAS の API に js の fetch で doPost する際に
地味にハマったのでメモ。
まずは、正常に動作するもの。

```js
fetch([post url], {
  'method': 'POST',
  'body': JSON.stringify([post obj]),
  "Content-Type" : "application/json"
})
.then(response => {
  return response.json();
})
.then(json => {
  // レスポンス json の処理
})
.catch(err => {
  // エラー処理
});
```

GAS 側はこんな感じ

```js
function doPost(e) {
  log("doPost e.postData.contents = " + e.postData.contents);

  let ret = {
    "message" : "success",
  };

  return ContentService.createTextOutput(JSON.stringify(ret)).setMimeType(ContentService.MimeType.JSON);  
}
```

これで ret の値が json として取得出来る。ハマりポイントとして
fetch のオプションに ```no-cors``` なるものがあるのだが

```js
fetch([post url], {
  'method': 'POST',
  'mode': 'no-cors',
  'body': JSON.stringify([post obj]),
  "Content-Type" : "application/json"
})
.then(  => {
// 省略...
```

にすると ```response.json()``` が取得出来ない。```no-cors``` って
「同一オリジン以外も yes」では無く「同一オリジン以外は no」ということの模様。
[fetchのmodeについて](https://qiita.com/ryokkkke/items/79f1d338e141d4b7201b#fetch%E3%81%AEmode) の記事がわかりやすいです。
```no-cors``` なので確かに「ダメ、CORS」ですよねー、ということで
ここを完全に読み違えてのハマりポイント。

合わせて fetch の戻り型は response オブジェクトで json 取得する場合は
```response.json()``` の promis 型というのも失念してて更にどハマり。
個人的にはなんで promis 型やねんというツッコミはあるものの、
それなりの理由がある設計のなのだろうと勝手に自己完結。
VSCode の Emmet のキーバインドを変える

Emmet は Zen-Coding の頃から興味はあって、Emmet に名前が変わった頃から本格的に使い始めたため、かれこれ10年くらいの付き合いになります。
ただ、当時使ってたエディタが、デフォルトの Tab キーによる展開がうまく行かず、ずっと `Ctrl + E` にしていました。[^1]
なので、VSCode でもそれができるようにしたときのメモです。

[^1]: なんで Ctrl+E　なのかというと、EmmetのEって覚えるためにEにしてたってだけだったのですが、案外押しやすい組み合わせ（USキーボードで CAPS と Ctrl いれかえてると、Aの左が Ctrl になるので左手をほんの少し動かすだけで小指 Ctrl、中指 E にできる。

## なんでいまさら？

VSCode 自体今まで使ってきてなかったわけじゃなくて、普通に使ってました。
ただ、あまりこれで HTML 書くことはなくて、Markdown だったり、単にメモ帳だったり、IDEで編集してるメインのコードとは別に参照用として開くときに使ったりといった感じでした。

なので、別に Emmet 使わなくても困らなかったのです。
ただ、いざちゃんとHTMLを書く機会がきてみると、やっぱり無いと辛いなってことで、ちゃんと設定することにしました。

> 各種ショートカットキーとかは Mac 用のものなので、他の OS の場合は適宜読み替えてください:bow_tone1:

## 設定方法

まずは、何らかの方法でキーボードショートカットの設定を開きます。

- コマンドパレット `Cmd + Shift + P` を開いて「キーボードショートカット」で検索し、キーボードショートカットを開く
- `Cmd + K` → ` Cmd + S` と連続で入れても呼び出せます（**K**eyboard **S**hortcut）
    - `Cmd` 押しっぱなしにして K → S と連続で入れるイメージかと思ったら、意外と猶予時間があるっぽい？
- メニューバーから「基本設定」→「キーボードショートカット」

などなど、道順はいろいろあるのでお好きな方法でどうぞ。

そうすると、キーボードショートカットというタブが開くので、上部の検索フィールドで、`Emmet`と検索し、
`Emmet: 略語の展開` という項目を探して、ショートカットキーを自分にあったもの（今回なら `Ctrl + E`）に変更します。

使いたいキーによっては既に設定がされていて衝突する場合があり、その場合ショートカットキーの記録ウィンドウにメッセージが出ますのでそこをクリックすると既存の設定を参照できます。
そのうえで、どうするかというと

- 新しいショートカットキーを別のものに変える
- 古いショートカットキーを別のものに帰るか、バインディングを剥がす
- あきらめる

のいずれかになると思います。

もちろん、`いつ` というフィールドで指定された条件を満たしてるときだけなので、うまく動くこともあるかもしれませんが、不意に意図しない挙動をすると面倒なのでやっぱりかぶらないように調整するのが良いと思います。

今回も見事 `Ctrl + E` は`cursorLineEnd` とかぶってましたが、使ったこと無いので消しました。


### JSON ファイルからも編集できる

キーバインドは `keybindings.json` を使っても設定できます。
規定値を剥がす場合は `command` の頭に `-` をつけるようです。

なので今回は

```json
[
    {
        "key": "ctrl+e",
        "command": "-cursorLineEnd",
        "when": "textInputFocus"
    },
    {
        "key": "ctrl+e",
        "command": "editor.emmet.action.expandAbbreviation",
        "when": "config.emmet.triggerExpansionOnTab && editorTextFocus && !editorReadonly && !editorTabMovesFocus"
    },
    {
        "key": "tab",
        "command": "-editor.emmet.action.expandAbbreviation",
        "when": "config.emmet.triggerExpansionOnTab && editorTextFocus && !editorReadonly && !editorTabMovesFocus"
    }
]
```

こんなかんじの JSON になりました。


## 新しいショートカットキーで展開出来ない場合

実はショートカットキーかえただけだと動かなくて、Emmet の展開を実行するためには、設定で
Emmet: Trigger Expansion On Tab（`emmet.triggerExpansionOnTab`）のチェックを ON にする必要がありました。

> あ、キーボードショートカットじゃなくて、アプリの設定です。`Cmd + .` とかで開くほう。

これ、めっちゃ **Tab** って設定名にも入ってますが、正確には `editor.emmet.action.expandAbbreviation` を実行するキーという意味っぽいです。
デフォルトだとオフかもしれないし、頭の回転が良い人は、「あ、Tabで展開はもう要らないかよね」ってなってチェックを外してしまった方もいるかもしれませんが、これは罠です。
このチェックが ON じゃないと、自分で設定したキーで展開されません。

じゃあなんで今までチェック入ってなかったのにうごいてたのか？というと、これは正確には `editor.emmet.action.expandAbbreviation` が実行されて展開されていたのではなくて、**入力中にでてくる補完タグ候補を Tab キーで選んでいただけ** というのが真相のようです。

そのため、たとえば Emmet: Show Expanded Abbreviation の値を never とかにすると、候補も出なくなるし、Tabキーでの展開もされなくなるはずです。
ということは逆に言うと補完候補が出ている場合は、キーバインドを剥がしたとしても Tab でもなんとなく展開できちゃいます。

どっちを取るのかは人それぞれですが、特に無害そうなので自分はこのままにしてます。候補が出るのはそれはそれで便利だしね。
初心者ブロックチェーンに挑戦する　9

# 急がば回ろう

## 時間が足らない

 昔なら夜を延ばすところだ。そのほうがヨーロッパ時間の友だちたちと同時性を保てるし。だけど、脳みその健康寿命を考えて、朝を前倒しにするほうを選ぶ。朝日がもろに当たるデスクの向きをちょっと変えてみる。

 3-1、もう来た、朝一で@@.solの更新。つまりファイルのほうね。バックエンドのほうね。失敗というのは、しているときには苦しいけれど、その分しっかり記憶の焼き付くので、結局、後の効率はよかったりする。

　次の&&.jsも問題はなかった。昨日、そこにはないエラーを探して、穴が開くほど読み込み続けたファイルだから。

　でも次の$$.jsがわからない。いや、どこかで見た、どこだっけな、という感じ。バックエンド側のフォルダで、無事発見。コミュニティーにパンくずをまく。私より前を走ってる強者には用がないけど、あとからくる迷子には、もしかしたら道しるべになるように。

　3-1はそこそこ順調に進み･･･と言い出したときが一番危ないのだが･･･最後のテストに来て、一行目にこう書かれれば悪い気はしない。

- Compiled 1 Solidity file successfully

　だけど、テキストのテスト結果と、後半の雰囲気が違うのだ。

- Error: missing argument:

　そら来た。何ちゃらいっぱい書いてあるけど、多分そうかな、と思うのが、nodeに繋がってないということ。この試みの最初に、あの、本物の、アナコンダのような、ウワバミのような、あの長い長い鎖を見てしまったトラウマで、私は毎回、ネットワークを切ってしまう。もう三章まで来たテキストでは、特段「つなげ」と書いていないのだけれど、多分、これは繋ぎっぱにするか、少なくとも毎回、準備としてすべきことなんだろうな、と思う。

　が、どうやって繋ぐんだっけか。

　テキストを遡ろうにも、画面がぐちゃぐちゃだ。このぐちゃぐちゃさ加減が思考に影響する。片付けないと･･･。

## こうなったら環境構築だ

　といっても、パソコンの設定ではない。デスク環境そのものをリ・コンストラクトだ。もう処分する気でいた古くて小さなwindowsまで援用しようとして、また、ディスク容量が足りなくて、アップデートできませんと言われる。えーい、本格的に改造しちゃる。勉強部屋ごと改装だ。

それって逃避？

うるさいっ。邪魔しないで！
docker on lima setup

# Overview
備忘録

https://github.com/lima-vm/lima

# Setup
## yaml
[docker.yaml](https://github.com/lima-vm/lima/blob/master/examples/docker.yaml)をもとにyaml作成


この辺を適宜変更
```
mounts:
 - location: "~"
 - location: "/tmp/lima"
~
~
~
~
cpus: 2
memory: "8GiB"
disk: "100GiB"
```

## start lima
```
limactl start ./docker.yaml
limactl shell docker
```


# localhost側のdockerの設定等
[こっち](https://qiita.com/junk616/items/39916387522e825f9647)
makeについて

コンパイルだけでなくソースファイルの管理など、さまざまなタスクを自動で管理してくれる`make`。複数のファイルが依存して構成されているものをコンパイルする作業の負担を減らすために作られたコマンドで、再コンパイルを効率よく行う為に、必要性のある部分のみを効率的に実行してくれます。更新する際に、依存しているファイルが変更されたか存在していないかが判断材料になっているようです。

# 「Makefile」

makeコマンドを実行するために欠かせないファイルで各ルールや変数定義によって構成されます。プログラムを構成しているファイル同士の関係や、各ファイルの生成手順を書き、コンパイルに必要なコマンドの情報置く場所です。この`Makefile`への記述次第で各種プログラムや文書のファイルで`make`を実行し作業の効率化を図れます。

```sql
make
```
と入力するだけで、`Makefile`に記述したコマンドが実行されます。自動でファイルのタイムスタンプ(データベースとファイルの最終更新時刻)を比較して更新が必要なものを選び、不要なビルドをスキップしながら的確に更新が必要と判断したものを作成します。大規模なプロジェクトになると100以上のソースファイルがありますが、ビルド毎に修正されるソースファイルの数が少ない場合が多く、すべてを毎回ビルドするのは大変なのでmakeが使われるのです。

# 「GNUmakefile」
makeコマンド`-f`か`--file`でファイル名をしていしないと、`GNUmakefile`->`makefile`->`Makefile`の順にmakefileを探そうとします。通常ではMakefileが推奨されますが、GNUmakefileはあくまでも`GNU make`の為のもので他のバージョン の`make`では処理できないmakefileを使う場合に使用するようにします。`GNU make`はCSSやJava Scriptのサイズ圧縮などの反復作業やメンテナンス作業の自動化に特化しています。
[WIP] 404ページコレクション

# 404ページコレクション

変わった404ページをメモしてみる。

## [GitHub](https://github.com/404)

スターウォーズが元ネタ？ マウスカーソルを動かすとなんとなく立体感が出る。

## [amazon.com](https://www.amazon.com/404)

ランダムでワンコが表示される。かわいい。

## [note](https://note.com/_404)

note 404 美術館になっている。

参考：

https://note.com/info/n/nb56ddb9bb070

## [ZOZOTOWN](https://zozo.jp/404.html)

NOT FOUND の文字が、商品になっている。

## [ヤマト運輸](https://www.kuronekoyamato.co.jp/ytc/404error.html)

クロネコが段ボールに隠れている。かわいい。

参考：

https://qiita.com/omoshiro_web/items/3fb149ff0a185b82caf2

## [タイトー](https://www.taito.co.jp/Error/404)

スペースインベーダー（っぽいゲーム？）ができる。

参考：

https://ja.wikipedia.org/wiki/%E3%82%B9%E3%83%9A%E3%83%BC%E3%82%B9%E3%82%A4%E3%83%B3%E3%83%99%E3%83%BC%E3%83%80%E3%83%BC

## [ウェンディーズ](https://www.wendys.com/404)

バーガータイム風のミニゲームができる。

参考：

https://ja.wikipedia.org/wiki/%E3%83%90%E3%83%BC%E3%82%AC%E3%83%BC%E3%82%BF%E3%82%A4%E3%83%A0

## [プラチナゲームズ株式会社](https://www.platinumgames.co.jp/404/)

シューティングゲーム(アクションゲーム?)ができる。

## [Romain Brasier氏のページ](https://www.romainbrasier.fr/404/)

大量(404人?)のレミングスが降ってくる。マウスカーソルを合わせるとパラシュート（傘？）が開く。

参考：

https://ja.wikipedia.org/wiki/%E3%83%AC%E3%83%9F%E3%83%B3%E3%82%B0%E3%82%B9

## [Oracle](https://www.oracle.com/jp/404/)

靴下が見つかる。ちなみに、[英語版](https://www.oracle.com/404/)だと鍵がみつかります(2021-03-10時点)。

以前は、英語版でも靴下が見つかっていたみたいなので、日本語版もそのうち変わるのかも。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">英語だと、We found a sock. なので、「靴下の片方だけ見つかった」というニュアンスですか <a href="https://t.co/cExTvv2GrT">https://t.co/cExTvv2GrT</a> <a href="https://t.co/FGm5BQI7Rr">pic.twitter.com/FGm5BQI7Rr</a></p>&mdash; 徳丸 浩 (@ockeghem) <a href="https://twitter.com/ockeghem/status/1481927462762156032?ref_src=twsrc%5Etfw">January 14, 2022</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

## [Qiita](https://qiita.com/404)

特に変わった感じではなさそうですが、とりあえず。

参考：

https://qiita.com/nginx/items/e9afcdd7374c9eaeb472
Bitbank から ohlcv を取得しよう!

## この記事のメリット

:::note info
取引開始時まで遡って収集することができる。
:::

:::note info
Bitbank は金融庁により暗号資産交換業者として登録されているためホワイトな取引を行うことが可能。
:::
##### 暗号資産交換業者登録一覧
https://www.fsa.go.jp/menkyo/menkyoj/kasoutuka.pdf

## 注意点
:::note alert
この記事は投資・投機を推奨するものではありません。
ご自身でリスク等をしっかりと確認しご自身の判断で実行するようにお願いいたします。
:::

# ライブラリインポート
~~~ruby:Python3
import time
import json
import requests
import numpy as np
import pandas as pd
from datetime import datetime, date, timedelta
~~~

# 通貨ペアと期間指定
~~~ruby:Python3
headers = {'Content-Type': 'application/json'}
api_url_base = 'https://public.bitbank.cc'
pair = 'xym_jpy'
period = '1min'

# 通貨ペアの例　pair
 #btc_jpy, xrp_jpy, xrp_btc, ltc_jpy,
 #ltc_btc, eth_jpy, eth_btc, mona_jpy,
 #mona_btc, bcc_jpy, bcc_btc, xlm_jpy, xlm_btc

# 期間の例　period
 #1min, 5min, 15min, 30min, 1hour
~~~

# 開始日を設定して収集
~~~ruby:Python3
df_xym = pd.DataFrame([])

s = 0
n=datetime(year=2021, month=10, day=5) + timedelta(days=s)


def api_ohlcv():
    api_url = '{0}/{1}/candlestick/{2}/{3}'.format(api_url_base, pair, period, timestamp)
    response = requests.get(api_url, headers=headers)

    if response.status_code == 200:
        ohlcv = json.loads(response.content.decode('utf-8'))['data']['candlestick'][0]['ohlcv']
        return ohlcv
    else:
        return None


# 日本時間の AM 9:00 前に実行しエラーが出た場合には下記の timedelta(days=1) を timedelta(days=2) に変更してください。

while (n<=(datetime.today() - timedelta(days=1))):
    n=datetime(year=2021, month=10, day=5) + timedelta(days=s)
    timestamp = "{0:%Y%m%d}".format(n)
    ohlcv = api_ohlcv()
    open, high, low, close, volume, timestamp = [],[],[],[],[],[]
    for i in ohlcv:
        open.append(i[0])
        high.append(i[1])
        low.append(i[2])
        close.append(i[3])
        volume.append(i[4])
        time_str = str(i[5])
        timestamp.append(datetime.fromtimestamp(int(time_str[:10])).strftime('%Y/%m/%d %H:%M:%S'))



    date_time_index = pd.to_datetime(timestamp) # convert to DateTimeIndex type
    df = pd.DataFrame({'Open': open, 'High': high, 'Low': low, 'Close': close, 'Volume': volume}, index=date_time_index)
    df_xym = pd.concat([df_xym, df], ignore_index=False)

    s += 1
~~~

# カラム名変更、index を datetime に、float 型に変更
~~~ruby:Python3
df_xym = df_xym.reset_index()
df_xym.columns = ['Time','Open','High','Low','Close','Volume']
df_xym.set_index('Time', inplace=True)
df_xym = df_xym.astype(float)
df_xym

# df へコピーする場合
df_xym = df_xym.reset_index()
df_xym.columns = ['Time','Open','High','Low','Close','Volume']
df_xym.set_index('Time', inplace=True)
df_xym = df_xym.astype(float)
df = df_xym.copy(deep=False) 
df
~~~



![Screenshot 2022-02-22 at 17-10-31 Untitled ipynb (3) - JupyterLab.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2339630/5efc77cb-e96b-32f6-af2b-9dd962f01eb1.png)



# 参考文献
https://github.com/yuyasugano/ohlc/blob/master/ohlc.ipynb

https://tsukimitech.com/bitbank-api-python/
go、パス

## パス
GOROOT
goのsdkの場所なのであまり触る必要がない。
またgoは後方互換性があるように作られているので普通に最新のバージョンにすればいいだけなので、あまり触ることはない。

GOPATH
go用のワーキングスペースを決める。
GOPATH/src/にプロジェクトを作成する。

https://pleiades.io/help/go/configuring-goroot-and-gopath.html#:~:text=GOPATH%20%E3%81%AF%E3%80%81%E3%83%AF%E3%83%BC%E3%82%AF%E3%82%B9%E3%83%9A%E3%83%BC%E3%82%B9%E3%81%AE,%E3%80%81%25USERPROFILE%25%2Fgo%EF%BC%89%E3%80%82

https://www.techscore.com/tech/Go/Lang/Basic2/
Solar2DのAndroidアプリにusesCleartextTrafficを設定する

## http通信エラー
Solar2Dで開発したAndroidアプリからサーバーへhttp通信しようとすると、usesCleartextTrafficがデフォルトfalseのためエラーが発生しました。
## 解決方法
最も良いのはhttps通信にすることですが、諸事情によりそれはできないので、プロジェクト直下に`AndroidResources/res/xml/network_security_config.xml`を作成して解決しました。
```xml:network_security_config.xml
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <domain-config cleartextTrafficPermitted="true">
        <domain includeSubdomains="true">sample.com</domain>
    </domain-config>
</network-security-config>

```
## 補足
usesCleartextTrafficをtrueにするため、build.settingsのapplicationChildElementsに`android:usesCleartextTraffic="true"`を追加しようと試みましたがうまく`AndroidManifest.xml`には記述できず、この解決方法で対応しました。
Prolific PL2303: "PL2303HXA PHASED OUT SINCE 2012. PLEASE CONTACT YOUR SUPPLIER."

## そもそも
ちゃんと 1,500円 ぐらいするものを買ったほうがいい:
[FTDI USBシリアル変換アダプター（5V/3.3V切り替え機能付き） - スイッチサイエンス](https://www.switch-science.com/catalog/1032/)

## 本文

USBポートを差し替える度にやっている気がするので, メモしておく.

Prolific のチップ (PL2303HXA) の入った USB-シリアルTTL 変換ケーブルを Windows 10 マシンに繋ぐと, デバイスマネージャで
`PL2303HXA PHASED OUT SINCE 2012. PLEASE CONTACT YOUR SUPPLIER` といった名前になり, 使用できない.
![mmc_4GRBDMPcnp.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100016/bb8d0b9b-4960-4764-86ab-fbaaa0e47ecc.png)

例えば[この変換ケーブル](https://www.amazon.co.jp/dp/B00K7YYFNM).

詳しい原因と経緯については [iFamily Software の記事](http://www.ifamilysoftware.com/news37.html)にまとまっており, ドライバの修正を行うためのプログラムが配布されている.
模造品や海賊版への対策の弊害で, Prolific の新しいドライバが入っていると, これら古いデバイスは軒並み使えなくなるとのこと.
記事の指示通りに`PL2303_64bit_Installer.exe`を使用して修正する.
[日本語の記事](http://hp.vector.co.jp/authors/VA005815/RSCircuit%20diagram/PL-2303USB.html)も存在する.

実際に再起動が必要だったことはないが, 「デバイスを無効にする」→「デバイスを有効にする」操作は必要だった.

[各ベンダの USB-シリアルTTL 変換器についての情報](https://ht-deko.com/arduino/usb2serial.html).
模造品や海賊版が多かったらしい.
psych::badalias: unknown alias: defaultで Railsコマンドが使えないを解決

## はじめに
Rails6の環境を構築している際に発生したエラーに対処していきます。

## 環境
M1 Mac
VS code
Ruby 3.1.0
bundler 2.3.3

## エラーメッセージ
`psych::badalias: unknown alias: default`

## 発生した理由
psychというYAML解釈用のGemの4系と3系で解釈が変わっているのが原因です。
こういったversionの違いで起きるエラーは大変ですね。

## 解決方法
3系を使うように指示を出します。

`gemfileに下記を追加`
```rb
gem 'psych', '~> 3.1'
```

保存して`bundle install`を実行
出来なければ `bundle update`を実行

## 追記項目
@scivola さんから情報をいただきました。
ありがとうございます。
こちらは `rails 6.1.4`　以降で解決されているようです。
6系を使う際はそれ以上の`version`を使うと良いで!! 

## おわりに
個人的な話ですが、Ruby on Railsチュートリアルで`mimemagic`問題で4日間エラーと戦ったあたりから
エラーメッセージを見ても怖くなくなりました笑

## 参考

https://teratail.com/questions/343233
Raspberry Piで4Kモニターに60Hzで表示する方法

# 概要
Raspberry Pi OSに接続したモニターに4k、60Hzで表示する方法です。  
（144Hzで表示させる方法は分からないので、ご存知の方がいたら教えてください。）

# 動作を確認した環境

- 本体：Raspberry Pi 4 Model B
- OS: Raspberry Pi OS (32-bit)
- モニター：LG 27GP950-B

# 手順

1. `/boot/config.txt`の最後に`hdmi_enable_4kp60=1`という行を書き加える。  
    ![02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2585392/c9896d8c-7d9c-3f47-ae09-df5abd4311c3.png)
1. Raspberry Piをリブートする。
1. メニュー > 設定 > Screen Configurationを開く。  
    ![03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2585392/0b7a08b5-f60f-deee-8a93-6e5eeb26ad55.png)
1. HDMI-1の表示を右クリックすると出てくるコンテキストメニューのFrequencyに60Hzが追加されているので、これを選択し適用ボタンを押す。  
    ![04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2585392/494871b8-16b1-a408-d7fc-c6086f779b85.png)
1. リブートするか尋ねるダイアログが出るので、指示に従ってRaspberry Piをリブートする。  
    ![05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2585392/67f82427-3dc8-2d74-5936-3c181eb7cd6e.png)

これでRaspberry Piで4k、60Hzの表示が可能になる。
Rails mimemagic問題でRails new .　できないを解決

 ## はじめに
Rails 6系の環境を用意している際に`mimemagic`問題に遭遇した


## 環境
M1 Mac
VS code
Ruby 3.1.0
bundler 2.3.3

## エラーコード
```rb
Ensure you have either installed the shared-mime-info package for your distribution, or
obtain a version of freedesktop.org.xml and set FREEDESKTOP_MIME_TYPES_PATH to the location
of that file.

This gem might be installed as a dependency of some bigger package, such as rails,
activestorage,
axlsx or cucumber. While most of these packages use the functionality of this gem, some gems
have
included this gem by accident. Set USE_FREEDESKTOP_PLACEHOLDER=true if you are certain that you
do not need this gem, and wish to skip the inclusion of freedesktop.org.xml.

# 重要なのは下記 mimemagicが曲者です
An error occurred while installing mimemagic (0.3.10), and Bundler cannot
continue.

```

## エラーが起きる理由
このエラーは`rails 6.0.3.4`をinstallしようとした際に発生しました。
詳しいことは省きますが、`mimemagic`のライセンス問題で発生しています。
解決策は`6.0.3.6`をinstallすることです。

詳しく知りたい方はこちらを確認してください

https://zenn.dev/catnose99/scraps/e4e6042fbbfd60

一言で言うなれば`mimemagic`問題を解決しているversionを使えばエラーは出なくなります。

## おわりに
実は少し前にRuby on Rails チュートリアルを完走したのですが、チュートリアルの中で`mimemagic`問題で沼ってしまい1週間くらい無駄にした苦い経験があります。(tutorialではrails以外のgemも変更した)

今回遭遇した際は冷静に対処できました
Typescriptでメソッドチェーン可能なオブジェクトを作る簡単なサンプル

# 簡単なサンプル
```typescript
const calc = {
  result: 0,
  add(x: number) {
    this.result += x;
    return this;
  },
};

calc.add(2).add(3); // 5
```

thisを返すといい感じにつなげられる。

# 初期値ありのサンプル
```typescript
const calc = (init: number) => ({
  result: init,
  add(x: number) {
    this.result += x;
    return this;
  },
});

calc(10).add(2).add(3); // 15
```

初期値が必要な時の書き方。

# クラスで書くサンプル
```typescript
class Calc {
  constructor(private result: number = 0) {}
  public add(x: number) {
    this.result += x;
    return this;
  }
}

new Calc(10).add(2).add(3); // 15
```

privateなんかでアクセスコントロールできる。
使う時にnewするのが面倒なら、初期値受けとってインスタンスを返すファクトリを作ってもよい。

# 実際の利用

上記のように足し算するだけだと使い道はないが、一般的には連続的に値を処理したい場合などで使われる。

```typescript:array
[...Array(100)].map((_, i) => i).filter((i) => i % 2).reduce((a, b) => a + b);
```

```typescript:cypress
cy.get('.todo-list li').find('label').should('contain', 'todo A')
```

```typescript:jquery
$('div').css('background-color', 'black').css('color', 'white').fadeOut(2000);
```
TypeScriptでのexportなどメモ

# tsでのexport,importなどのメモ
これは、情報の共有とかでなく
個人的なメモです。
細かいこと知りたい人は、ほかの方の記事を参考にしてください:bow_tone1:

## import
ライブラリのインポート時に使用する。
```ts
import {hogehoge} from 'HOGE'

//　自作したライブラリは
import {zisaku} from './myLibrary'
//　自作したライブラリのdefaultで設定されていれば
import mylib from './myLibrary'

```
これは、基本的なので忘れないと思う。

## export
ファイル内の関数などを外部ファイルでも使用できるようにするために宣言する

```ts
export const hogehoge1=()=>{
     return "test"
}

export default function hogehoge2(){
     return "test"
}
```
関数の前に宣言する
基本的なので、忘れない....と思う

### まとめる
Reactとか開発していてcomponentとか増えてきたとき
ある程度似ているものは、同じ場所から import できるようにしたいときは
```ts
export * from './hogehoge'
```
上記の文でライブラリの橋渡しできる
インポートする際は、このファイルを指定することでこれを仲介して多くの機能を同時にインポートすることができる。

## メモ
`export * from './hogehoge'`
これのやり方を毎回忘れる
GitHubだけがバージョン管理だと思うなよ

- GitHub
    - オープンソースではほぼこれ一択
- Bitbucket
    - フォルト非公開なのがお仕事利用ではちょうどいい。アトラシアン系はドキュメンテーションや課題管理ツールが便利。ちょっとしたテキストメモもGit管理するのが便利なら、非公開リポジトリを基本としてスタートしたBitbucketが一番最初の選択肢になるは当然。
- GitLab
    - オープンソース。自前でホスティングできるのがいいところ
- Azure DevOps
    - Microsoft製のバージョン管理ツール。昔はVisualSourceSafeという製品でバージョン管理機能を提供していたが今はカバーする機能が増えてAzure DevOpsという名前で課題管理やテストやデプロイまでできちゃう。Web系開発の現場では使われないが、VisualStudioプロジェクトやOfficeドキュメントのバージョン管理には便利
- Subversion
    - ローカルリポジトリがない。add,commit,pushが分離されずに常にリモートで管理される。そのため操作の階層が浅くて初心者にもわかりやすい。TortiseSVNというSVNクライアントを入れるとシェル統合されて非エンジニアが使いこなせる。Subversionのサーバーは立てやすいのでプライベート領域でバージョン管理するのにも便利。コメント込でバージョン管理できるDropBoxとして使える。