OmniAICreator/Qiita-1.07M huggingface dataset:
databaseをmysqlにした後bundle installでエラーになる件

※備忘録

##問題
databaseをmysqlに変更したあと、Railsでアプリ作成をする際に、エラーが出た。


####実行コマンド

```terminal
$ rails _5.2.4_ new app_name --skip-coffee --database=mysql
```

```Gemfile
gem 'pry-rails'
```

```terminal
$ bundle install
```


####エラー内容

```
(省略)
ld: library not found for -lssl
clang: error: linker command failed with exit code 1 (use -v to see
invocation)
make: *** [mysql2.bundle] Error 1

make failed, exit code 2

Gem files will remain installed in
/Users/fengjianlengfu/.rbenv/gems/2.6.0/gems/mysql2-0.5.3 for
inspection.
Results logged to
/Users/fengjianlengfu/.rbenv/gems/2.6.0/extensions/x86_64-darwin-18/2.6.0-static/mysql2-0.5.3/gem_make.out
```


##やったこと
####・ログ内容のコマンドを実行(解決できなかった)

```
gem install mysql2 -v '0.5.2'
```

####・似たエラーを出している人の解決策を探す


[この方](https://qiita.com/SAYJOY/items/dd7c8fc7a3647e7ff969)が書かれていることを参考にするとエラーなくインストールできた。

```terminal
$ bundle config --local build.mysql2 "--with-ldflags=-L/usr/local/opt/openssl/lib"
$ bundle install
```


###わかったこと
・この部分が原因だった模様。

```
ld: library not found for -lssl
```

・上記のエラーはsslというライブラリが読み込めてない時に起こる。
→ライブラリが存在しないかライブラリへの接続リンクが間違っている。


・`$ bundle config --local`はローカルのアプリケーションに対して設定を行っている。
→[参照1](https://qiita.com/SAYJOY/items/dd7c8fc7a3647e7ff969#%E8%A7%A3%E6%B1%BA%E7%AD%96%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6) [参照2](http://ruby.studio-kingdom.com/bundler/bundle_config/)

##感想
正直、なぜこの方法で解決できたのかなど理解できていない。
今後学習をしていく中で振り返っていきたい。
FastAPIとDockerでPDFファイルをTIF画像に変換するAPIを作る

# はじめに
上司からFastAPIが良い感じらしいと聞いたので触ってみました。
シンプルにGETリクエストして文字を返すのではつまらないので、PDFファイルをTIF画像に変換するAPIを作成しました。

# FastAPIとは
[FastAPI](https://fastapi.tiangolo.com/)はPythonのWeb frameworkで、Flaskに似たようなものです。

# 開発環境
- Windows10 Pro
- Docker for Windows

# 実装

## ディレクトリ構成

```bash
root
├─app.py
├─Dockerfile
├─requirements.txt
└─test.pdf
```

## Dockerfile

```Dockerfile:Dockerfile
FROM python:3.8

# PDFの変換に必要なpopplerをインストール
RUN apt-get update && \
    apt-get install -y poppler-utils

# Pythonモジュールのインストール
COPY requirements.txt .
RUN pip install --upgrade pip && \
    pip install -r requirements.txt && \
    rm requirements.txt

# 変換したファイルを一時保存するためのフォルダを作成
RUN rm -rf /app && \
    mkdir -p /app/data/

# プログラムを配置
COPY app.py /app/app.py

EXPOSE 8000
WORKDIR /app
CMD ["uvicorn", "app:api", "--host", "0.0.0.0", "--port", "8000"]
```
今回は`python:3.8`のイメージを使用しましたが、Pythonが動いてpopplerがインストールできれば何でもいいです。

## requirements.txt

```requirements.txt:requirements.txt
fastapi
uvicorn
python-multipart
pdf2image
```
FastAPIを使うときは`fastapi`と`uvicorn`は必須
ファイルをアップロードするときは`python-multipart`が必要
PDFファイルを画像に変換するために`pdf2image`が必要

## app.py

```python:app.py
import os
from base64 import b64encode

import uvicorn
from fastapi import FastAPI, File, UploadFile
from pdf2image import convert_from_bytes
from PIL import Image

api = FastAPI()


@api.post("/")
async def post(file: UploadFile = File(...)):
    pdf_file = await file.read()
    tif_file = convert(pdf_file)
    return tif_file


def convert(pdf_file):
    output_folder = "./data"
    file_name = "temporary"
    output_file_path = f"{output_folder}/{file_name}.tif"

    # PDFの全ページをそれぞれjpgに変換して保存する
    image_path = convert_from_bytes(
        pdf_file=pdf_file,
        thread_count=5,
        fmt="jpg",
        output_folder=output_folder,
        output_file=file_name,
        paths_only=True,
    )

    # 全jpg画像を読み込む
    images = [Image.open(image) for image in image_path]

    # 全jpg画像を1枚のTIF画像に変換して保存する
    images[0].save(
        output_file_path, format="TIFF", save_all=True, append_images=images[1:],
    )

    # 全jpg画像を読み込み、base64でエンコードする
    with open(output_file_path, "rb") as f:
        tif_file = b64encode(f.read())

    # 保存している画像をすべて削除して、TIF画像のバイナリを返す
    for image in image_path:
        os.remove(image)
    os.remove(output_file_path)
    return tif_file


if __name__ == "__main__":
    uvicorn.run(api)
```

`convert_from_bytes`で`paths_only=True`にしないとメモリをかなり食うので注意が必要です。

# 実行
## Dockerの起動
1. Build

    ```bash
    docker build -t fastapi .
    ```

2. Run

    ```bash
    docker run --rm -it -p 8000:8000 fastapi
    ```

## APIリクエスト

```bash
 > curl -X POST -F 'file=@./test.pdf' http://localhost:8000 | base64 -di > ./test.tif
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  100  206M  100  206M  100  309k  27.0M  41409  0:00:07  0:00:07 --:--:-- 47.2M
```
base64でエンコードされて返却されるので、base64でデコードして書き込む必要があります。

# まとめ
ファイルをアップロードするのに`python-multipart`が必要だったり、少し躓くところはありましたが、FastAPIは非常に書きやすいと感じました。
ちょっと変わった母集団推定について悩んでいること(備忘録)

## こういう分布推定の問題、やったことありませんか
統計なりマーケティングなりに関わる仕事をしていると、限られたサンプルから母集団全体の傾向を知りたいというニーズには一度は出会ったことがあるだろう。それがいわゆる母集団推定であり、これに関しては信頼区間など、様々な指標・方法が確立している。
しかし時に、母集団推定と設定は似ているが、異なる対象を推定したいと考えることがある。文字で書き下すより、図で表現した方が正確に伝達できると思われるため図にて記す。

まずはふつうの母集団推定。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264219/701b604d-7773-ac88-d17e-2f4f07e52568.png)

で、これがやりたいと思っている推定。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264219/dfb02838-ea05-510e-4f69-56e229af16f9.png)

こんなニーズありえるの？と思われるかもしれないが、あり得るのである。詳細は伏せるが、例えば「特定のラベルを持つデータ」部分を「自社のアプリ利用者」と置き換えてもらえればピンとくるかもしれない。
日本の人口$N_{m}$は国勢調査なり人口統計でわかるし、全アプリ利用者$G_{m}$は運営元なら当然把握できるだろう。（簡単のため、ここではアプリは国内限定配信とし、1人が複数の端末にアプリをインストールしているケースはないものとする）
そして、特定の条件に合致するアプリ利用者数を調べたところ$G_{s}$人だった。この時、日本全体で同じ条件を満たす人は何人いるだろうか？という問題である。

この問題では、通常の母集団推定と関係性が異なっているせいで信頼区間はそのままでは使えない。数式で言うと二項分布の逆数比を求めるような問題になると思われる。
ベイズ的には標本総数の事前分布を一様分布と仮定して、$G_{s}$の値から事後分布を求める、というような解決法になるだろうが、上のような事例では同時に複数サンプリングをすることは出来ない。仮に毎日「特定の条件に合致するアプリ利用者数」を調べ続けたとしても、$N_{m}$はともかく、$G_{m}$、$N_{s}$は日々無視できないぐらい変化するからだ。

こうやって書いていると、絶対にどこかの先人が取り組んで一定の解法を出しているのではないかと思うのだが、自分の情報収集能力が足りないのか、今に至るまで「これだ！」という手法を見つけることが出来ていない。
ご存知の方いましたらぜひともご教示いただければ幸いです。。
distcc で分散ビルドしたいが多コア環境ではうまくいかないのメモ

# 環境

* Ubuntu 18.04, 20.04 混在
* clang コンパイラ
* Ryzen 16 コアマシン x 2

## 設定

distccでビルドを速くする
https://mongonta.com/f281-short-buildtime-with-distcc/

あたりを参考にして設定するだけです.

## njobs が少ない

`/etc/default/distcc` で JOBS="32" とかにしているが,

`distcc -j` とすると, 16 コア(32 threads)な CPU なのに `6`(メインノードを localhost で指定) や `8`(メイン, クライアントそれぞれを IP 指定) としか出ない.
`ninja -j32` とかしても 8 jobs(8 プロセス) に制約される.

どうも distcc のデザイン自体が古いようで, 多コアな環境に対応していないっぽい?ようです. :cry:

`localhost` だと 2 に制限されるのはここですね.

https://github.com/distcc/distcc/blob/master/src/hosts.c#L402

## 結論

なんか多コアでビルドする例もインターネットに無いようですし, 多コア向け distcc の開発もそんなに活発ではないようですので, 多コアでビルドしたい場合は IncrediBuild などの商用なツールを使ったほうがいいのかもしれません.
(あとはネットワークも 10 GbE or InfiniBand にしないとファイルの転送がボトルネックになりそう)

16 ~ 32 コアとかだと llvm みたいな大きめな C++ プロジェクトでも 7 分くらいで終わるので, ビルド時間が開発に大きなインパクトを与えるのでなければ, 無理して distcc で分散して早くする必要が無いというのもありそうです.
FlutterとReactNativeを両方使った感想

## 両方の経験
**React Native経験：**
- [UdemyのThe Complete React Native + Hooks Course [2020 Edition]のコース](https://www.udemy.com/course/the-complete-react-native-and-redux-course/)を40%を完了した
- 実務でAndroid側でiOS風の[アプリ評価のダイアログ](https://developer.apple.com/design/human-interface-guidelines/ios/system-capabilities/ratings-and-reviews/)をペアプロで作った

**Flutterの経験：**
- [UdemyのFlutter & Dart - The Complete Guide [2020 Edition]のコース](https://www.udemy.com/course/learn-flutter-dart-to-build-ios-android-apps/)を40%を完了した
- 実務で画面の作成とAPI呼び出し


## 結構違うと感じたところ
### 画面レイアウトの設定
#### 画面の要素の間のスペースを設定するとき
React Nativeでは、CSS感覚で[StyleSheet](https://reactnative.dev/docs/stylesheet)で、その要素にMarginとPaddingを設定するが、Flutterではその要素の外にWidget（例えばContainer)をwrapし、MarginとPaddingを設定する

#### 画面の要素の相対位置を設定するとき
React NativeのJSXはCSS感覚で、StyleSheetで[Flex](https://reactnative.dev/docs/flexbox)で要素を縦・横に並ぶことができるが、
Flutterの場合は、Widgetとして[Row](https://api.flutter.dev/flutter/widgets/Row-class.html)とColumnなどのを使って、要素の配置を設定する。

### テストについて
#### React Nativeの場合
React NativeではJS Bridgeが存在し、ソースコードをiOS・AndroidそれぞれのNative UI elementsに変換する。よって、iOSとAndroidのUIが異なるため、修正が入ったら、両方のテストが必要になる。

#### Flutterの場合
FlutterはSkiaという自分のエンジンがあるため、SikaでUIをレンダリングするため、プラットフォームによるUIの差がない。テストがより楽になる。
railsでのカラム名追加時に発生したエラーについて（rails db:migrate、rails db:rollback、add）

#目次

1.概要
2.原因
3.解決
4.反省
5.まとめ

#１.概要
ItemsテーブルとUsersテーブルの2つが存在し、最初に作ったItemsテーブルにuser-idのカラムを追加する為、`rails db:rollback`を2回実行した後に、`rails db:migrate`を実行したところ、マイグレートキャンセル：外部キーが存在しない、ユーザーSQLが存在しないというエラーが発生しました。（本来はaddを使いカラムを追加すべきでした。）

#2.原因
Usersテーブルを作成前に、Itemsテーブルを暫定的に作成していた為、Itemテーブルに紐付けたuser-idが存在しないというエラーが発生しました。

たとえば1つ目のItemsテーブルは**9月1日時点**で作成したとします。

```ruby
class CreateItems < ActiveRecord::Migration[6.0]
  def change
    create_table :items do |t|
      t.references :user,         foreign_key: true
     これがエラー原因に関連
      t.string :name,             null: false
      t.timestamps
    end
  end
     以下省略
```

2つ目のUsersテーブルは**9月5日時点**で作成したとします。

```ruby
class DeviseCreateUsers < ActiveRecord::Migration[6.0]
  def change
    create_table :users do |t|
      ## Database authenticatable
      t.string :name,                        null: false
      t.string :email,                       null: false, default: ""
      t.string :encrypted_password,          null: false, default: ""
     以下は省略
```
データベースのファイルは、db/migrate/20200905052006_devise_create_users.rbのようになるかと思いますが、数字部分「20200905052006」はデータベースが作成された日時を示しています。

`rails db:migrate`は古い日時の方から順番に1つずつデータベースをマイグレートしていくので、今回のケースでは9月1日に作成したItemsテーブルのデータベースのマイグレートが先に行われます。

つまり、Usersテーブルのマイグレートが未実施の状態でItemsテーブルをマイグレートしようとすれば、`t.references :user,foreign_key: true`が有効であると判断されず、外部キーがない、ユーザーのSQLがないといったエラーメッセージが表示されます。

#3.解決
日時部分の数字の名前を変更して時系列が逆になるようファイルを保存し直した上で`rails db:migrate`を行うとUsersテーブルが先にマイグレートされて外部キーが存在しない、ユーザーSQLが存在しないといったエラーが解消されます。

#4.反省
1.エラーに関する記録をしっかりと残していなかったので、正確なエラーメッセージを掲載できませんでした。投稿することも意識して緊張感持ってメモ等をしていこうと思います。

2.そもそも、addを使ってカラムを追加していれば時系列がおかしくなってエラーが発生することもなかった。
addにしていれば、Userテーブルをマイグレートした後にItemsテーブルのusser-idのカラムだけマイグレートすることができたはずでした。`rails db:rollback`を2回行って直接Itemsテーブルのカラム名に変更を加えたことが間違えでした。

#5.まとめ
1.カラムの追加はaddを使う方が良い。特に外部キーの追加だと`rails db:migrate`でエラーになることがあります。
2.データベースファイルの数字部分は作成日時を示しています。
3.`rails db:migrate`は古い日時から順番に行われます。（rails db:rollbackは直前1回分のマイグレートを取り消します。）
4.addを使わずカラムを追加して、外部キーが存在しないというエラーが発生した時は、データベースのファイル名の日時をマイグレートの都合が良いように変更します。
【Rails】Ruby On Rails でデータベースを紐付ける手順

#モデルの設定
サンプルは```1対多数```
###1側

```post.rb
class Post < ApplicationRecord
  has_many :comments
end
```

###多側

```comment.rb
class Post < ApplicationRecord
  belongs_to :post
end
```

#ルーティングの設定

```routes.rb
resources :posts do
  resources :comments
end
```

※createとdestroyのみ使用する場合、下記の記述で他のルーティングにアクセス制限をかけることができる。

```routes.rb
resources :posts do
  resources :comments, only: [:create, :destroy]
end
```

#フォームでのパラメーターの受け取り方
[ ]で囲う。

```???.html.rb
<%= form_for [@post, @post.comments.build] do |f| %>
<p>
  <%= f.text_field :body %>
</p>
<p>
  <%= f.submit %>
</p>
<% end %>
```


#Destroyでの出し方、受け取り方
ルーティングから、postsは```:post_id```、commentsは```:id```でパラメーターを受け取ることができる。

```
DELETE /posts/:post_id/comments/:id(.:format)      comments#destroy
```

```???.html.rb
<% if @post.comments.any? %>
<ul>
<% @post.comments.each do |comment|%>
<li><%= comment.body %><span> </span><span><%= link_to "[X]", post_comment_path(@post, comment), method: :delete %></span></li>
```

```???.controller.rb
def destroy
  @post = Post.find(params[:post_id])
  @comment = @post.comments.find(params[:id])
  @comment.destroy
  redirect_to post_path(@post)
end
```
git pull にorigin masterを付けなくても良い。

## やり方

```bash
% git branch -u origin/local_branch local_branch
```
このコマンドで`origin/local_branch`と`local_branch`を関連付けました。

## 確認

```bash

% git branch -vv
  60-first-fax   bbe32a0 wip
  auth0-controle 2da339c wip
* local_branch            8b31651 [origin/local_branch] Merge pull request #66 from akuraya/add-README
```

```bash

% git pull
Already up to date.
```

### 解除する場合

`origin/local_branch`と`local_branch`の関連付けを解除します。

__解除コマンド__

```bash

git branch --unset-upstream local_branch
```

__確認__

```bash

% git branch -vv
  60-first-fax   bbe32a0 wip
  auth0-controle 2da339c wip
* local_branch            8b31651 Merge pull request #66 from *****/add-README
```

４行目から`[origin/local_branch]`が消えていることが分かります。
GASでGoogle AnalyticsのDAUを取得する

Webサイトの分析をする中、週次でその時点のDAU（Daily Active User）の平均値を記録しておく必要がありました。
今回はDataportalを使用して可視化を行っていたので、スプレッドシートにデータを書き込む必要があります。

①スプレッドシートを作成してスクリプトエディタを開く
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206219/e2795a1e-7562-328e-dfea-b4889ec454a8.png)

②GASでGAのAPIを利用するために「Analytics Reporting API」を有効にします
※画面崩れはご愛嬌
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206219/8f048a6d-bba6-a202-8b81-8876bac3c46a.png)
これにより、`AnalyticsReporting`というクラス名でapiを呼び出すことができます。

③実装  

```javascript
function myFunction() {
  const response = AnalyticsReporting.Reports.batchGet({
    reportRequests: [{ // リクエストのBodyを作成
      viewId: '********', // GAのビューIDを指定
      dateRanges: [{startDate: '2020-04-01',endDate: '2021-03-31'}], // 取得する範囲を指定（今回は2020年度） 
      metrics: [{expression: 'ga:1dayUsers',}], // 取得する指標
      dimensions:[{name: 'ga:date'}] // 取得するディメンション
    }]
  });
  // レスポンスのJSON階層が根深いので頑張って取り出す
  const rows = JSON.parse(response).reports[0].data.rows;
  var sum = 0;
  rows.forEach(row => {sum += parseInt(row.metrics[0].values[0])});
  let dau = sum / rows.length; // 平均値を算出 
  // スプレッドシートに一行追加
  const spreadsheet = SpreadsheetApp.openById('スプレッドシートのID');
  const sheet = spreadsheet.getActiveSheet(); 
  const date = new Date(); // 取得日時を設定する為にDate型の変数を定義
  sheet.appendRow([Utilities.formatDate( date, 'Asia/Tokyo', 'yyyy/MM/dd'), dau]);
}
```
④結果確認
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206219/32379559-78c4-48d4-7370-84a642eec10d.png)

⑤あとはスクリプトをスケジュールで起動するように設定しておけばOK
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206219/b83a0664-f8a3-8dbe-6fb4-b9a62b667574.png)
ethtool

NICの状態を表示する。

```
# ethtool eth0
```

通常のサーバで実行すると以下のように表示される。

```
Settings for eth0:
         Supported ports: [ TP ]
         Supported link modes:   10baseT/Half 10baseT/Full
                                 100baseT/Half 100baseT/Full
                                 1000baseT/Full
         Supports auto-negotiation: Yes
         Advertised link modes:  10baseT/Half 10baseT/Full
                                 100baseT/Half 100baseT/Full
                                 1000baseT/Full
         Advertised auto-negotiation: Yes
         Speed: 1000Mb/s
         Duplex: Full
         Port: Twisted Pair
         PHYAD: 0
         Transceiver: internal
         Auto-negotiation: on
         Supports Wake-on: umbg
         Wake-on: g
         Current message level: 0x00000007 (7)
         Link detected: yes
```

LANケーブルが挿さっている場合は「Link detected: yes」と表示される。
LANケーブルが抜けている場合は「Link detected: no」と表示される。

1000BASEのNICを使用しているのにスピードが「100Mb/s」だったり、
デュプレックスが「half」になっている場合はHUBとの相性やケーブルの不具合などが考えられる。

ethtoolコマンドを実行して［Speed］や［Duplex］の値がおかしいときは、以下のように強制的に修正して状況が改善されるかどうかを確認する。

```
# ethtool -s eth0 speed 100　←速度を100Mb/sに設定
# ethtool -s eth0 duplex full　←デュプレックスをfullに設定
```
Node.js MySQLのクエリでプレースホルダーを使う



##前置き
Lambdaなどでmysqlを実行するにあたっての簡易メモ
仕様は[公式](https://github.com/mysqljs/mysql)から参照

`.query(sqlString, values, callback)`でプレースホルダー値を利用する
（connection作成は省略します。）

- 第一引数のSQLに**?**を指定する。
- 第二引数に配列を渡す。

## SELECT


```.js
const sql = "SELECT * FROM sample WHERE id = ?";
const data = [1];
const query = connection.query(sql, data, (err, results, fields) => {
  if (error) throw error;
  // ...
});
console.log(query.sql); // SELECT * FROM testdb.sample WHERE id = 1
```

フィールド名に使う場合は**??**を指定する。

```.js
const sql = "SELECT ?? FROM ?? WHERE id = ?";
const columns = ['id', 'name', 'email'];
const data = [columns, 'users', 1]
const query = connection.query(sql, data, (err, results, fields) => {
  if (error) throw error;
  // ...
});
console.log(query.sql); // SELECT id, name, email FROM users WHERE id = 1
```

## INSERT

複数カラムを１つの**?**で対応できます。

```.js
const sql = "INSERT INTO posts SET ?"
const post  = {id: 1, title: 'Hello MySQL'};
const query = connection.query(sql, post, (error, results, fields) => {
  if (error) throw error;
  // ...
});
console.log(query.sql); // INSERT INTO posts SET id = 1, title = 'Hello MySQL'
```

複数行のinsertを行う場合は**(?)**を使用する
※insertするレコード数分必要。

```.js
const sql = "INSERT INTO users(name, age, sex) VALUES(?),(?),(?)"
const data = [['boy', 23, 'man'], ['girl', 14, 'woman'], ['guy', 42, 'man']];
const query = connection.query(sql, data, (error,results,fields) => {
  if (error) throw error;
  // ...
});
console.log(query.sql); 
/*
INSERT INTO
 users(name, age, sex) 
VALUES 
 ('boy', 23, 'man'),
 ('girl', 14, 'woman'),
 ('guy', 42, 'man')'
*/
```

##UPDATE

```.js
const sql = "INSERT INTO users SET name = ? WHERE id = ?"
const data  = ['佐藤', 1];
const query = connection.query(sql, data, (error, results, fields) => {
  if (error) throw error;
  // ...
});
console.log(query.sql); // INSERT INTO users SET name = '佐藤' WHERE id = 1
```

複数行もinsertと同じ。
onduplicate key updateのサンプル

```.js
const sql = "INSERT INTO users(name, age, sex) VALUES(?),(?) ON DUPLICATE KEY UPDATE age = VALUES(age) "
const data = [['boy', 25, 'man'], ['girl', 16, 'woman']];
const query = connection.query(sql, data, (error,results,fields) => {
  if (error) throw error;
  // ...
});
console.log(query.sql); 
/*
INSERT INTO
 users(name, age, sex) 
VALUES 
 ('boy', 25'man'),
 ('girl', 16, 'woman')
ON DUPLICATE KEY UPDATE age = VALUES(age)
*/
```
Nixパッケージマネージャーの使い方

## Nixはいいぞ

私は仕事用に Windows(会社支給), 普段使い用に MacBook Pro(友達から貰った), 実験用に Manjaro Linux(会社の先輩から貰った)を使っていますが、その全てでパッケージマネージャーには nix を使ってます。
(ただし、Windows では Ubuntu on WSL2 の中で Nix を使ってます)

なぜ大人しく apt や homebrew を使うのでなく、Nix を使うのかというと、

1. 宣言的に環境を管理できるので環境の再現が容易
2. カスタマイズ性が高い
3. docker が要らない
4. アンインストールしたときに予期せぬところにゴミが残ることがない
5. 環境をいつでも戻すことができる

というところが嬉しいからです。

もっと Nix が普及してほしいので、Nix パッケージマネージャーのインストール方法と基本的なコマンドの使い方を紹介します。

## Nix のインストール

### Linux

```bash
$ sh <(curl -L https://nixos.org/nix/install)
```

ひとつ注意点があって、fish shell では実行できません。

### macOS

```bash
$ sh <(curl -L https://nixos.org/nix/install) --darwin-use-unencrypted-nix-store-volume
```

### .bashrc への追記

Linux でも Mac でも nix を使うための必要な環境変数の設定のために、 `~/.bashrc` に

```bash
if [ -e $HOME/.nix-profile/etc/profile.d/nix.sh ]; then
    . $HOME/.nix-profile/etc/profile.d/nix.sh;
fi
```

を追記しておきましょう。

### チャンネル登録

まず、使うためには、Nix パッケージのチャンネルを登録する必要があります。  
チャンネルの操作には`nix-channel`コマンドを使ってください。

```bash
$ nix-channel --add https://nixos.org/channels/nixpkgs-unstable
$ nix-channel --update
```

`nix-channel --update` は `sudo apt update` みたいなものです。

## パッケージ管理

パッケージ管理のためのメインのコマンドは `nix-env` です 。  
`nix-env`を使って、パッケージのインストール、アップグレード、削除、検索などができます。

### 検索

`nix-env -qa` でインストール可能なパッケージを全部閲覧できます。  
`nix-env --query --available` でも同じです。

```bash
$ nix-env -qa
aterm-2.2
bash-3.0
binutils-2.15
bison-1.875d
blackdown-1.4.2
bzip2-1.0.2
...
```

`-qa` のあとにパッケージの名前を入れれば特定のパッケージを検索できます。

```bash
$ nix-env -qa firefox
firefox-68.11.0esr
firefox-78.1.0esr
firefox-79.0
```

検索には正規表現も使えます。

```bash
$ nix-env -qa 'firefox-.*'
```

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

`nix-env -i`または`nix-env --install`でパッケージのインストールができます。

ちなみに、 `sudo` は要りません。インストールしたパッケージはシステムにではなく、ユーザーにインストールされます。システム全体への影響は(多分)ありません。

```bash
$ nix-env -i subversion
```

`nix-env -q`でインストール済みのパッケージを全て確認することができます。

```bash
$ nix-env -q
nix-2.3.7
subversion-1.12.2
```

### アップグレード

`nix-env -u`または`nix-env --upgrade`で環境を更新できます。

特定のパッケージのみのアップグレードをするときは、

```bash
$ nix-env -u subversion
```

全てのインストール済みパッケージをアップグレードするときは、

```bash
$ nix-env -u
```

### アンインストール

`nix-env -e`または`nix-env --erase`でパッケージをアンインストールできます。

```bash
nix-env -e subversion
uninstalling 'subversion-1.12.2'
```

### rollback

Nix では、パッケージを追加したり削除したりするたびに環境の generation が作成されます。  
そして、いつでもそれらの generation にロールバックすることができます。

例えば、パッケージインストールしてバグがあったから、うまく動いてたインストール前に戻したいって時に便利です。

generation の確認は、 `nix-env --list-generations`

```bash
$ nix-env --list-generations
   1   2020-08-03 23:36:12   
   2   2020-08-06 19:15:43   
   3   2020-08-06 19:55:13   
   4   2020-08-09 18:33:59   
   5   2020-08-09 18:34:46   (current)
```

`(current)`が現在の環境です。

ひとつ前の環境に戻るには、 `nix-env --rollback`をつかいます。

```bash
$ nix-env --rollback
switching from generation 5 to 4

$ nix-env --list-generations
   1   2020-08-03 23:36:12   
   2   2020-08-06 19:15:43   
   3   2020-08-06 19:55:13   
   4   2020-08-09 18:33:59   (current)
   5   2020-08-09 18:34:46   
```

特定の generation に移動するには、 `nix-env --switch-generation`をつかいます。

```bash
$ nix-env --switch-generation 2
switching from generation 4 to 2
```
jsQR ios14でフリーズするとき

jsQRを使用したときにandroidでは問題なかったが、iphoneではフリーズしてしまった


# こんなイシューあった

https://github.com/cozmo/jsQR/issues/185

これを読んだ感じ原因はぼんやりしているが

videoタグが表示されてないとダメそうってことがわかった

# こんなふうに変えたら動いた

・videoタグにautoplay, playsinline, mutedを設定
・videoタグをHTML内にappendする
・canvasはdisplay:noneとかしとく

# demoサイトを例にするとこう変えた
jsQRのデモサイト
https://cozmo.github.io/jsQR/

```js
    var video = document.createElement("video");
    document.body.append(video); // ココ
    var canvasElement = document.getElementById("canvas");
    var canvas = canvasElement.getContext("2d");
    var loadingMessage = document.getElementById("loadingMessage");
    var outputContainer = document.getElementById("output");
    var outputMessage = document.getElementById("outputMessage");
    var outputData = document.getElementById("outputData");



    // Use facingMode: environment to attemt to get the front camera on phones
    navigator.mediaDevices.getUserMedia({ video: { facingMode: "environment" } }).then(function(stream) {
      video.srcObject = stream;
      video.setAttribute("playsinline", true); // required to tell iOS safari we don't want fullscreen
      video.setAttribute("autoplay", true); // ココ
      video.setAttribute("muted", true); // ココ
      video.play();
      requestAnimationFrame(tick);
    });

    function tick() {
      loadingMessage.innerText = "⌛ Loading video..."
      if (video.readyState === video.HAVE_ENOUGH_DATA) {
        loadingMessage.hidden = true;
        outputContainer.hidden = false;

        canvasElement.height = video.videoHeight;
        canvasElement.width = video.videoWidth;
        canvas.drawImage(video, 0, 0, canvasElement.width, canvasElement.height);
        var imageData = canvas.getImageData(0, 0, canvasElement.width, canvasElement.height);
        var code = jsQR(imageData.data, imageData.width, imageData.height, {
          inversionAttempts: "dontInvert",
        });
        if (code) {
          outputMessage.hidden = true;
          outputData.parentElement.hidden = false;
          outputData.innerText = code.data;
        } else {
          outputMessage.hidden = false;
          outputData.parentElement.hidden = true;
        }
      }
      requestAnimationFrame(tick);
    }
【Git】特定のファイルの特定の箇所のコミット情報を閲覧したい

## 使い所
あるファイルのこの部分のコミット情報をみたい！という時に使えます。

## コマンド

ファイルのパスを出力します。分かっていれば必要ないです。

```
## ファイルのパス検索
find ./ -name "ファイル名"   // 一致検索
find ./ -type f -name "**" // Like検索
```

git logを行数でフィルタリングします。

```
## 行数からコミットを検索する
git log -L 開始の行数,終了の行数:{ファイルのパス}
```

直前のlogで出力されたコミット番号から、コミット情報を閲覧します。

```
## コミット情報閲覧
git show {コミット番号} // コミットの情報を見れる
```

ちなみにVSCodeの[GitLens](https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens)使えば同じことできます。

以上です。
TerraformのバージョンアップでInvalid indexが出た時の対処

小ネタです。

## ACMをterraformで作る方法が使えなくなってた件

これまでterraformでACMを作成する方法として

https://dev.classmethod.jp/articles/acm-cert-by-terraform/
こちらの記事を参考に作っていました。

それで、問題の部分は以下の部分です。
こちらは上記の記事から引用しています。

```hcl
resource aws_route53_record cert_validation {
  zone_id = data.aws_route53_zone.route53-zone.zone_id
  name    = aws_acm_certificate.cert.domain_validation_options[0].resource_record_name
  type    = aws_acm_certificate.cert.domain_validation_options[0].resource_record_type
  records = [aws_acm_certificate.cert.domain_validation_options[0].resource_record_value]
  ttl     = 60
}
```

しかし、最新版のawsプロバイダーではList型からSet型へ変わってしまうことで、以下のように差分が発生します。

```shell
$ tf plan

Error: Invalid index

  on aws_route53.tf line 30, in resource "aws_route53_record" "acm_example_jp_cert":
  30:   name    = aws_acm_certificate.example_jp.domain_validation_options[0]["resource_record_name"]

This value does not have any indices.
```

確かにドキュメントを読むと、
3.8.0だと`Set of` 2.70.0だと`list of`になってますね。

https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/acm_certificate#domain_validation_options
https://registry.terraform.io/providers/hashicorp/aws/2.70.0/docs/resources/acm_certificate#domain_validation_options

このことはTerraform0.13 にアップグレードしたとき、という話がよくありますが、どちらかというとプラグインの問題のはずです。

https://qiita.com/naomichi-y/items/4c22ef31d8cebae72e0b#aws_route53_record%E3%82%92%E5%89%8A%E9%99%A4%E3%81%97%E3%82%88%E3%81%86%E3%81%A8%E3%81%99%E3%82%8B

こちらの方は、for_eachを用いてstate mvにて対応しています。
SANsの時に複数の値が入ってくることがあり、そのためにSet/Listになっているので、
for_eachで作成することは理にかなっています。

私も最初この方法をとろうとしました。
しかしstate mvだと普段と流れが異なるため、手順を作って、レビューして、云々・・・と普通より面倒だったので、
コードのほうを変えて差分をなくす方法をとりました。
一時しのぎ。

## tolistを使う方法

ということで、あまり理には適っていませんが、以下でも対処できました。

```hcl
resource "aws_route53_record" "cert_validation" {
  zone_id = data.aws_route53_zone.route53-zone.zone_id
  name    = tolist(aws_acm_certificate.cert.domain_validation_options)[0].resource_record_name
  type    = tolist(aws_acm_certificate.cert.domain_validation_options)[0].resource_record_type
  records = [tolist(aws_acm_certificate.cert.domain_validation_options)[0].resource_record_value]
  ttl     = 60
}
```

ListじゃなければListにすればいいじゃないか、ということで組み込み関数を利用する方法です。
新規に作成する場合はfor_eachを使ったほうがいいでしょう。

## まとめ

https://www.terraform.io/docs/configuration/functions.html

terraformには組み込み関数がそこそこあります。
for_each使うときにtosetなんかはよく使いますね。

これらを使いすぎると可読性は落ちると思いますが、
便利なので必要に応じて使っていくと良いと思いました。
DWH / BIツールの比較検討、Snowflake / QuickSightの選定まで

#DWHを選定する
社内のDWHがハード保守期限を迎える為、クラウド上への移行を計画。AWS上に社内環境移行を推進中である為、DWHもデファクトスタンダードであるRedshiftを本命に考えておりました。本来はGCPに社内環境を構築してBigQueryを使いたかったのすが、GCPでOracleが使えない制約があり、AWSを選択しました。(AZULEは使用経験がなかった為、今回の選定から除外)

#DWH候補は下記の通り。
　■列指向DB　
　・Redshift　 ←　AWS環境ならデファクトスタンダード
　・RedshidtSpectrum
　・Athena
　・Snowflake　←　革新的DWHとして高い評価
　■行指向DB
　・Aurora
　・SQLserverExpress

#比較前に思っていたこと
　■売上データは増えても1000万行程度なので、列指向DBでなくても速度出るのでは？
　■可視化に使用予定のPowerBIは、オンライン上にデータ展開するので、行指向DBでいいのでは？
　■仕組みから変えるようになると大変なので、できれば最初に列指向DBを入れておきたい
　■費用に見合う速度がでなかったら意味ないので、その場合は行指向DBでいいかも

#ある程度使ってみて見えたこと
　■AWSのサービスを使いこなすのが難しい
　　・RedshidtSpectrumがよくわからない
　　・Athenaもすぐ使えそうなのに使えない
　　・Glueもよくわからない
　　・LakeFormationが便利そうだがよくわからない

　■PowerBIで気づいたこと
　　・インポートモードは列指向DBの利点を活かせない(全件読み込むのが無駄)
　　・列指向DBを使う場合はDirectQueryを使う方がベター(Snowflakeなら爆速)
　　・EC2側のスペックが低いと描画が遅い
　　・(わかっていたが)PowerBI(Web)にプッシュするので、On-Premises DataGatewayが必要になる
　　・On-Premises DataGatewayを入れたサーバが起動していないとPBIが使えない
　　・On-Premises DataGateway用のEC2が必要で、費用が掛かかる上に単一障害点となる
　　・クラウドDWHであるSnowflakeを使っても、On-Premises DataGatewayが必要となる

　■Redshiftで面倒だったこと
　　・テーブル定義の作成：テーブル定義作成時は長さまで定義が必要
　　・データ投入が面倒：CSVはgzip圧縮してS3に置いてから取り込む
　　・速度向上の為に列ごとの圧縮方法指定が必要
　　・起動が致命的に遅い(10分程度かかる)
　　・停止し忘れ = 課金。リザーブドインスタンスだと高くて割に合わない

　■Snowflakeでいいなと思ったこと
　　・課金体系：(計算速度 × 利用時間) + ストレージ費用
　　・従業課金だが、BigQueryのような1回のクエリで高額となるような心配はない
　　・とにかく早い。6年分(約50GB)をロードするのに約13秒！！！！
　　・SQLの実行速度も恐ろしく早い(XSサイズでも超早)
　　・費用もRedshiftの3分の1未満と低価格

#見えてきたDWHに求める要件
　■こうであって欲しい
　　・使った時間じゃない従量課金(BigQueryやAthenaみたいに)
　　・パフォーマンスが良くて安いこと(BigQueryやAthenaみたいに)
　　・自動停止(うっかり停止忘れがまあまあな金額になるのが怖い)
　　・自動起動(使うときは瞬時に使いたい)
　　・理解が簡単なもの(構築で挫折しないもの)
　　・データ投入が簡単なもの

#Snowflakeを使わない理由がなかったので、Snowflakeを選定。

#Snowflakeに合わせて、BIツールも再選定
　■比較したBIツール
　　・PowerBI
　　・QuickSight
　　・Tableau　←　コスト面で折り合わないのでさらっと確認のみ
　　・Re:Dash
　　・Metabase

　■結果としてQuickSightを選定
　　・機能はPowerBIに劣る部分が多いが、ビジュアルの良さや操作の簡便さは好印象
　　・Viewerライセンスがあり、従量課金で最大5ドル。
　　　(全員に使って欲しいが、使ってもらえるかわからない導入時には最適)
　　・デスクトップアプリがないので、動作は少しもっさり
　　・作成したビジュアルをバックアップする手段がないので本番運用時の対策が必要
　　・SPICEを使うと速度はほんの少しだけ早くなり、SnowFlakeのコストが大幅に下がる
　　　(SPICEに展開したデータを使用する為、SnowFlakeにクエリを投げる必要がない)

#選定までに行った調査は以上です。

#おまけ：Snowflakeへのデータロード手順
　■Snowflakeにデータをロードする手順
　　●通常の手順
　　・Snowflakeにテーブル作成
　　　(GUI or SnowSQLにてテーブル定義を実行)
　　・S3にバケットを作成
　　　(IAMにてポリシー制御も必要)
　　・データソースからCSVに出力してS3にアップロード
　　　(AWSCLIを使えばスケジューラ実行可能)
　　・Snowpipe or SnowSQLにてcopy into コマンドでデータロード
　　　(CSVデータを並列で取り込める為、指定のサイズに区切っておくこと)

　※copy into コマンドで取り込む際の注意点
　　・Snowflakeはdeleteしたデータを含むCSVファイルはリロードできない
　　　~~(再度CSV出力 → S3にアップしてcopy intoしてもダメ)~~
　　　COPYコマンド実行時に「FORCE=TRUE」オプションを付けることでリロード可能
　　　(CSVファイル名で判定しているので、ファイル名を変更しても取り込める)
　　例) 当月の間は修正伝票が入ることがあるので毎日取り込み直したい
　　　　①30日分のデータを出力　→　ファイル名+日付
　　　　②30日分のデータをSnowflakeからDelete
　　　　③ ①で出力したファイルを取り込めば、常に最新の30日分を取り込める

　　●Talendを使った手順
　　・Snowflakeにテーブル作成
　　・データソースからデータを取得し、Snowflakeに更新
　　　(当社ではOracleからデータを取得し、直接SnowFlakeに更新している)
　　　※通常の手順だと4ステップ必要だが、2ステップでロード可能
　　　※ステージング不要(Talendから直接Snowflakeテーブルに更新できる)
　　　※テーブルがないと参照先/更新先が指定できない
　　　　→　テーブル右横の3点ボタンを押し、カスタマイズテーブルでテーブル名を指定できるが、
　　　　　　謎のエラーが起きたり、一部の列はデータが欠損したのでおすすめしない

最後までお読みいただきありがとうございました。
【Java】Spring (Boot) で singleton service を使ってDBからデータを取得する

# Spring (Boot) で singleton service を使ってDBからデータを取得する
・enum にDI
・メモリの節約
・キャッシュ機能で高負荷に対応
・読み取りのみ
・ランキングなどで利用してます
・Spring Framework, Spring Boot 両対応
・マッパーの方は適宜読み替えて設定してください
・コンテキスト更新時に初期化
※参考：https://www.baeldung.com/spring-context-events

# 環境
・Spring Framework 4.2.9
・Spring Boot 2.2.1
・Java 1.8
・Windows 10 64bit

# SampleRankingSingletonService
・データを取得し保持するシングルトンサービス (enum)
・mapper を @Autowired で DI したいけどできないので初期化用のサービスを別に作る
・データの加工はプログラムを追加して対応すると良いと思います。（SQLの発行は高負荷）

```java
public enum SampleRankingSingletonService {

	INSTANCE;

	// 読み込みタイムアウト値調整
	public static final int TIMEOUT_SEC = 1800;

	private static final Object lock = new Object();

	private LocalDateTime queryDate;

	// 最新の ランキング
	private List<Sample> latestRanking = new CopyOnWriteArrayList<>();

	private SampleMapper sampleMapper;

	private SampleRankingSingletonService () {
	}

	public static SampleRankingSingletonService getInstance() {
		return INSTANCE;
	}

	private boolean isTimeout() {
		LocalDateTime execDate = LocalDateTime.now();
		if (queryDate == null) { return true; }

		LocalDateTime expireDate = queryDate.plusSeconds(TIMEOUT_SEC);
		if (expireDate.isBefore(execDate)) {
			return true;
		}
		return false;
	}

	public synchronized void reload() {
		queryDate = LocalDateTime.now();
		latestRanking = sampleMapper.selectLatestRanking(); // <- ランキング取得SQL発行
	}

	// データ処理を実行する前に呼び出す
	public void initQuery() {
		synchronized(lock) {
			if (isTimeout() || latestRanking.size() <= 0) {
				reload();
			}
		}
	}

	public Integer countLatestRanking() {
		return latestRanking.size();
	}
}
```

# SampleRankingSingletonInitializerService 
・DIにより自動生成

```java
@Component
public class SampleRankingSingletonInitializerService {

	@Autowired
	private SampleMapper sampleMapper;

	// Bean生成時にシングルトンサービスにDIしたマッパーをセットする
	@PostConstruct
	public void init() {
		SampleRankingSingletonService.INSTANCE.setSampleMapper(sampleMapper);
	}

	// コンテキスト更新時に初回クエリ実行 (PostConstruct で実行すると Spring Boot ではエラーになる)       
	@EventListener
	public void onContextRefreshedEvent(ContextRefreshedEvent evt) {
		SampleRankingSingletonService.INSTANCE.initQuery();
	}

	public SampleMapper getSampleMapper() {
		return sampleMapper;
	}

	public void setSampleMapper(SampleMapper sampleMapper) {
		this.sampleMapper = sampleMapper;
	}
}

```

# DI 用の xml ファイル
```xml
    <bean id="sample.sampleRankingSingletonInitializerService" class="org.sample.SampleRankingSingletonInitializerService">
        <property name="sampleMapper" ref="sample.SampleMapper" />
    </bean>
```

ブレイクポイント設定してサーバー起動後ちゃんと止まり、データが取れていればOKです。
以上、お疲れさまでした！
PowerShell でExcelを動かしたメモ

探せばいろんな所に載っている情報だけど、メモっておきました。COMオブジェクトの開放の部分の先頭の「\」は不要です。これがないと表示されない...

```ps.ps1
$excel = New-Object -ComObject Excel.Application
#画面表示
$excel.Visible = $true
#メッセージ表示
$excel.DisplayAlerts = $true
#フォルダの指定
$currentPath = (Convert-Path C:\Users\Admin\Documents)
#ファイル名指定
$filename = "sample.xlsx"
#開く（フルパスで開くとたくさんメッセージが出てしまう。何で？）
$book = $excel.Workbooks.Open($currentPath + "/" + $filename)
#保存
$book.Save()
#bookを閉じる
$excel.Workbooks.Close()
#Excelを閉じる
$excel.Quit()
#COMオブジェクトを開放
[[System.Runtime.Interopservices.Marshal]::ReleaseComObject($book)
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($excel)
```
Kotlinでフォルダ監視

# 普通にWatchServiceでフォルダ監視
Kotlinで書いてみました。
まだまだKotlin書き始めて日が浅いので**「無駄にKotlinぽく書きてえな」**とか考えながら書いてましたがちゃんとKotlinぽくなったかどうか。

無駄に〇〇とかやるのって意外と勉強になるよね。

```Monitor.kt
import java.nio.file.*
import java.nio.file.StandardWatchEventKinds.ENTRY_CREATE
import java.util.concurrent.TimeUnit

/**
 * WatchServiceでモニタしてみる
 */
class Monitor(
    private val dir: Path,
    private val watcher: WatchService = dir.fileSystem.newWatchService(),
    private val watchKey: WatchKey = dir.register(watcher, ENTRY_CREATE)
) : AutoCloseable {
    constructor(str: String) : this(Paths.get(str))

    fun submit(listener: (Path) -> Unit) {
        // 1秒だけ待機してあれば処理するなければ何もしない
        watcher.poll(1, TimeUnit.SECONDS)?.run {
            watchKey.pollEvents().forEach {
                listener(dir.resolve(it.context() as Path))
            }
            watchKey.reset()
        }
    }

    override fun close() = watcher.close()
}
```

なんかコード量少ないけどちゃんと動くのかな？
Monitorクラスを使ったmainメソッドが下記。

```Monitor.kt
/**
 * 1.5以降でGlobalScopeはデリケートがマークされてるんですね。
 * 以前書いたコード見てたらisActiveとかもいつの間にやらBANされてるし。
 */
import kotlinx.coroutines.*
import java.nio.file.*

suspend fun この辺でなんか他の事をする(running: ()->Boolean) {
    while (running()) {
        print("*")
        delay(100)
    }
    println("なんか他の事をする処理の終了")
}

suspend fun ちょっとだけまって終了ファイルを作る() {
    (3 downTo 0).forEach {
        delay(1000)
        println(" byeファイルを作るまであと $it 秒待つ")
    }
    println("bye ファイルを作る")
    Files.createFile(Paths.get("bye"))
}

suspend fun main() = coroutineScope {
    var running = true
    launch {
        // カレントフォルダを対象にする
        Monitor(".").use { mon ->
            while (running) {
                mon.submit {
                    if (it.endsWith("bye")) {
                        println("byeファイルが見つかった $it")

                        // byeという名称で作られたらフラグを落とす
                        running = false
                        // このファイルはいらないので消す
                        Files.delete(it)
                    }
                }
            }
        }
    }

    launch {
        この辺でなんか他の事をする { running }
    }

    launch {
        ちょっとだけまって終了ファイルを作る()
    }

    println("このメッセージが先に出るはず。プロセスはコルーチンが全部抜けてきたら終了")
}
```

# 参考：コルーチンのbuild.gradle（dependencies抜粋）
```build.gradle
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.1-native-mt'
```

# 使いたいからコルーチン
フォルダ監視にコルーチンは向いてるような気がするんですよね。ディレクトリ見つけたらその中身をなんかするとかの場合も、コルーチンなら1000でも10000でもディレクトリ見つけたらどーん、がーん、わーい、みたいな。

何言ってんだろね俺も。

というわけで、やっぱKotlinはコード量少なくて素敵だな、というお話。
Using a coroutine without a "GlobalScope" in Activity and Fragment


ActivityとFragmentで`GlobalScope`を使わないでコルーチンを利用する方法です。
`GlobalScope`を何気なく使わないようにしましょう。

### Do not use GlobalScope in local scope

At First. Look at follow code.
This example code is wrong coroutine usage.

```kotlin
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        setSupportActionBar(toolbar)

        GlobalScope.launch { 
            val data = withContext(Dispatchers.IO) {
                loadData()
            }
            initUi(data)
        }
    }
    ...
}
```

This is a bad way.
But Why?
According to Officially GlobalScope explains at ["GlobalScope - kotlinx-coroutines-core"](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-global-scope/index.html), GlobalScope's lifecycle scope is Process-Level. So, this loading data may keep running after Activity(also Fragment) Destroyed, and then happen unexpected behavior.


### How to use Coroutines on Activity and Fragment without GlobalScope : Use LifecycleScope

Use `androidx.lifecycle:lifecycle-runtime-ktx:2.2.0-alpha01` or higher.
So you can write like this example.


```kotlin
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        setSupportActionBar(toolbar)

        lifecycleScope.launch { 
            val data = withContext(Dispatchers.IO) {
                loadData()
            }
            initUi(data)
        }
    }
    ...
}
```

In Fragment, use viewLifecycleOwner.

```kotlin
class MyFragment: Fragment() {
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        viewLifecycleOwner.lifecycleScope.launch { 
            val data = withContext(Dispatchers.IO) {
                loadData()
            }
            initUi(data)
        }
    }
    ...
}
```

Because `LifecycleScope` is lifecycle-aware, so if you want to use asynchronous tasks with only Activity and Fragment lifecycle scopes, it makes more sense to use LifecycleScope than GlobalScope.

See more: https://developer.android.com/topic/libraries/architecture/coroutines
UA判定（User Agent）

```HTML
<script type="text/javascript">
    var isSp = navigator.userAgent.indexOf("iPhone") > 0 || navigator.userAgent.indexOf("iPad") > 0 || navigator.userAgent.indexOf("Android") > 0;
      if (isSp) {
        document.write('<div>スマホ、タブレットの時のみ表示</div>');
      } else {
        document.write('<div>PCの時のみ表示</div>');
      }
</script>
```
GitHub Actionsでreviewdogにpylintしてもらってみた

# この記事について

GitHub Actionsでreviewdog + pylintを連携してみたので、その記録です。
やってみた系記事ですのであまり詳しい説明などはありませんが、どなたかの参考になれば幸いです。

# この記事には書いていないこと

GitHub Actionsやreviewdogそのものの説明はこの記事では行いません。
リンクを貼っておきますので、詳しく知りたい方はお手数ですがそちらをご確認ください。

* [GitHub Actions](https://docs.github.com/ja/free-pro-team@latest/actions)
* [reviewdog](https://github.com/reviewdog/reviewdog)
* [Reviewdog を飼ってコードレビューや開発を改善しませんか](http://haya14busa.com/reviewdog/)

ちなみに、linterによっては[Marketplace](https://github.com/marketplace?after=Y3Vyc29yOjIw&query=reviewdog&type=actions&utf8=%E2%9C%93)に既に用意があったりするので、覗いてみるのも良いと思います！


# コード

とりあえずコードが見たい、というあなたにはこちらをどうぞ！
このコードでは、functions/ディレクトリにpylintしたいコードが「ぺけぺけ.py」なファイル名で置いてあることになっています。ディレクトリ構成や*name*、*branches*、もろもろのバージョンなんかは適宜お使いの環境に合わせて変更してくださいね。
コードがイイ感じに書けたら、.github/workflows/配下に格納しましょう。

```YAML:.github/workflows/reviewdog.yml

name: reviewdog
on: 
  pull_request:
    branches:
      - develop
jobs:
  reviewdog:
    name: reviewdog
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v1

      - name: Set up Python 3.8
        uses: actions/setup-python@v1
        with:
          python-version: 3.8

      - name: Setup pylint
        run: |
          python -m pip install --upgrade pip
          pip install pylint
          pylint --generate-rcfile > ~/.pylintrc

      - name: Setup reviewdog
        run: |
          mkdir -p $HOME/bin && curl -sfL https://raw.githubusercontent.com/reviewdog/reviewdog/master/install.sh| sh -s -- -b $HOME/bin
          echo ::add-path::$HOME/bin

      - name: Run reviewdog
        env:
          REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          pylint --rcfile=~/.pylintrc functions/*.py | reviewdog -efm="%f:%l:%c: %m" -reporter=github-pr-review

```


# 何が起きるの？

functions/配下にhello.pyを格納して、developブランチにプルリクを出してみました。
さて、どうなるかな...？

![pylint_reviewdog_pr01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/624944/47223a53-3485-41e1-4916-6ca41061a629.png)

おっ、何か動いてますね！
少し待っていると...

![pylint_reviewdog_pr02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/624944/a4d2273f-cfe6-83a0-eaf8-805bac8f12d9.png)

じゃん！
こんな感じで、pylintで引っかかった部分をコメントとして表示してくれます。
ワンちゃんがとってもかわいいですね🐶 引っかかった悲しみも癒してくれそうな気がします。


# 終わりに

いかがでしたでしょうか。
今回はGitHub Actionsで動かしてみましたが、reviewdogはローカルでも動作しますので、興味のある方は試してみてくださいね。

それでは！
GitLab上の自作WebアプリをDockerイメージのビルド時にcloneする

自作アプリをGitLabからビルド時にcloneするDockerイメージを作成しました。
メモとして残します。

## 動作環境
Windows10 Pro
GitLab
## 参考文献
https://qiita.com/Brutus/items/da63d23be32d505409c6

## 手順
### GitLabの自分のアカウントのTokenを取得
GitLabの右上メニューの「Settings」を押します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285286/5cbafa47-de13-b01f-f0fa-c02c9d086d11.png)

左メニューの「Access Token」を押します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285286/48b4ba34-197d-a6df-0c4f-fcbdea79df1c.png)

Nameに任意のToken名を入力し、Scopeのapiにチェックを入れて「Create personal access token」を押します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285286/9286eb36-e832-69e4-51e7-ad2bcf29b303.png)

作成に成功すると、Tokenが表示されるので控えておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285286/1a17f28c-4e87-7d0c-b780-7be192a54d55.png)

### Dockerイメージをビルドする
以下の要領で__Dockerfile__を作成します。

```dockerfile:Dockerfile
# Nodeのexpressのアプリを入れたかったので、nodeのイメージを利用
FROM node:10.12

# 作業ディレクトリを指定
WORKDIR /src

# 作成者情報を記述
LABEL maintainer="y.matsumoto <y.matsumoto@sample.com>"

# -Cオプションでcloneするディレクトリを明記。https://oauth2:[GitLabで取得したToken]@gitlab.com/[プロジェクト名]/[リポジトリ名].gitがURLの構文
RUN git -C / clone chat https://oauth2:[GitLabで取得したToken]@gitlab.com/[プロジェクト名]/[リポジトリ名].git

# expressの実行ファイル系がappディレクトリに入っているので作業ディレクトリにコピー
RUN cp -r /[リポジトリ名]/app/* /src

# clone後、不要なファイルを削除
RUN rm -rf /[リポジトリ名]

# 作業ディレクトリにアプリのpackage.jsonがコピーされているので、npm installがそのまま実行できる
RUN npm install

# コンテナを実行時、サーバが開始するように設定
CMD npm start
```

Dockerfileのあるディレクトリで以下のコマンドを実行してビルド&コンテナを実行します。

```
docker image build . --tag [任意のタグ名]
docker container run -d -p 3000:3000 [指定したタグ名]
```

ブラウザでアクセスすると問題なく立ち上がりました！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285286/b8aa7e30-0b58-afa1-ae55-a45f9213d94f.png)


以上です。

ビルド時に自作のアプリの最新版をpullしたdockerイメージを今回作成しましたが、
後々registryにpushして、Kubernetesでpullして利用できる形を目指していきたいと思います。

もしご指摘等ありましたら是非お願い致します！
ヘッダーメニューの横並びが上手くいかなかった時の対処方法

ヘッダーメニューの横並びができなかった時の対処方法を解説していきます

１回頭に入れたけど忘れちゃったって人のために要点だけをまとめました

```html:html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>サイト名</title>
    <link rel="stylesheet" href="stylesheet.css">
  </head>
  <body>
    /*ここからがヘッダーメニューのコードはここから*/
    <header>
      <div class="container">
        <div class="header-logo">ロゴ名</div>
        <div class="header-list">
          <ul>
            <li>リスト名</li>
            <li>リスト名</li>
            <li>リスト名</li>
          </ul>
        </div>
      </div>
    </header>
　　/*ここまでがヘッダーメニューのコード*/
  </body>
</html>
```

では次にcssを説明していきます

```css:css
li {
  list-style: none;
  float: left;　←ここに「float: left;」を入れる
  padding: 33px 20px;
}

header {
  height: 90px;　←ヘッダー自体に高さを指定する
  background-color: #26d0c9;
  color: #fff;
}

.header-logo {
  float: left;　←ここに「float: left;」を入れる
  font-size: 36px;
  padding: 20px 40px;
}

ヘッダーメニューを横並びにしたい時は「float: left;」を「li」と「.header-logo」に入れる

また、floatを指定すると親要素の高さが0になるので、ヘッダー自体にheightを指定します
```

これで完璧。
git update-git-for-windows を打つと killing n Git Bash instances って言われた時の対処法

# 結論
**gitbashをどこかしらで起動してます[^5]。閉じましょう。**

# 前書き/後書き
　Windows環境でgitをアップデートする場合、
①(コマンドプロンプトや)gitbashで`git update-git-for-windows`と入力する
②インストーラをダウンロードしてくる[^10]
のどちらかになりそうですが、①でアップデートしようとした際、

```shell
$ git update-git-for-windows
Git for Windows 2.22.0.windows.1 (64bit)
Update 2.28.0.windows.1 is available
Download and install Git for Windows 2.28.0 (killing n Git Bash instances) [N/y]?
```
と出て、**kill**の文字にびびってあたふたした時の解決策メモです。
　良く考えれば分かりそうですが、それだけにコンソールに出た文言で検索しても何も出なかったので、記事化しました。
　ちなみに、②の場合でも似たような事になります(そしてbashを落とすまではインストールできない)。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/314204/748d82d8-4b3d-c92e-8236-3ca0f45747ad.png)


[^5]: VSCodeとか
[^10]: コマンド叩いても、結局はインストーラが起動してます
「もっとプログラム脳を鍛える数学パズル」_Q01 (code:Ruby) -> Rust

[「もっとプログラマ脳を鍛える数学パズル」](https://www.shoeisha.co.jp/book/detail/9784798153612)をRustで書き直すのは、ボケ防止にちょうど良いかもしれない、と思った。

# Q01:一発で決まる多数決

本筋ではないけれど、このクイズの設定の微妙なところは、「じゃんけんの勝ち負け」は「多数決の勝ち負け」と関係ないところ。「チョキ2でパー2なら、チョキの勝ちじゃん」と、本筋でないところで混乱した・・・

## Ruby

```ruby:q01_2.rb
N = 100

cnt = 0
0.upto(N) do |left|
    left.upto(N) do |right|
        all = [left, right - left, N - right]
        cnt += 1 if all.count(all.max) == 1
    end
end
puts cnt
```

## Rust

```rust:main.rs
fn main() {
    let mut q01 = Q01{ num_of_people:100,};
    let answer = q01.solve();

    println!("{}", answer);
}

struct Q01 {
    num_of_people:i64,
}

impl Q01 {
    pub fn solve(&mut self) -> i64 {
        let mut count = 0;
        for left in 0..=self.num_of_people {
            for right in left..=self.num_of_people {
                let all = vec![left, right - left, self.num_of_people - right];
                let max_value = all.iter().max().unwrap();
                let c = all.iter().filter(|&v| *v == *max_value).count();
                if c == 1 {
                    count += 1;
                }
            }
        }
        return count;
    }
}
```

こんな感じか。`Vec`に対する最大値や走査は一旦`iter()`を通す必要がある。
移動

要約
カッコいい移動にしたいけど、やり方すぐに忘れるから、備忘録作る!!


####カッコいい移動とは？
(´・ω・)とりあえず、マブラヴを履修してこようか
流石に、白銀の変態機動は真似しないで良いから
~~流石にあんな機動作ろうと思ったら、脳髄が沸騰してしまうので今の所難しそうです。~~

###1.移動について
前置きが長くなりました。
現実味のある所に落ち着くと、マブラヴでの戦術機の移動方法には大まかに二種類あります。
ジェットエンジンとロケットエンジンによる加速です。
~~一応、徒歩も移動方法の一種だけど、BETAがやってくる所で歩いて移動はしたくないよね、うん~~

ジェットエンジンは徐々に加速して燃費が良く、そこそこの速度が出ます。
ロケットエンジンは瞬発力があるけど、燃費が最悪で、緊急時にしか使いません。
~~一つの推進機にジェットエンジンとロケットエンジンを搭載する戦術機が変態と言わない。マブラヴ世界線ではそうでもしないと生き残れないです。~~

そこで、現在開発中の同人シミュではダッシュと緊急回避という用途として移動方法を二種類ほど用意します。

###2.ダッシュと緊急回避の識別


ダッシュと緊急回避の識別どうすんの？
とりあえず、WASDを押し続けた時と、連続して二回クリックした時で動作変えれば良いんじゃないの？
ということで下記のBPを考えてみました。

A押した時、間隔が一定以下なら真(True)を返す、真なら緊急回避、それ以外は普通のダッシュを行う
Timer関数の中身は差分が1/2秒以下なら真(True)を返すだけ
![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244620/bcd2b8b4-abcc-d905-588b-97d36ece4faf.png)

![キャプチャ3.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244620/c9ca343b-6339-3388-91fd-8d726ab07e50.png)
時間を取得するのはどうやってるのか、というと動作完了後にNow関数でローカル時刻(PCの時間)を取得しています。
また、それだけだと一度目の動作がおかしいので、開始直後に時刻を一度取得しています。
![キャプチャ4.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244620/d1ce653d-9598-42ed-cdd7-b5ea453a7c69.png)
![キャプチャ2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244620/341f8d33-3708-6ad6-e70e-43c55b0daedd.png)

##動いたー！！
と思ったのですが、やり方がスマートではない気がする。
Twitterでもっとマシなやり方は無いかとつぶやいた所、よしかわはずれ氏(@yoshikawahazure)よりリプライがありました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244620/ac998969-65c9-8843-d293-a3c6593c8e9d.png)
![EjAu2PPVgAES-0X.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244620/c261b95f-0ad9-b1a7-7615-f6859ee43a17.jpeg)

こっちの方が断然楽じゃん……OTL
おそらく、Branchの部分を『Switch on ~』(Switch on Int等)に変えれば、複数の分岐に繋げれる気がする

<br>
<br>
<br>
<br>
<br>
ホームページ作成でcssを用い横並びのブロックを作成する方法

#ホームページ作成でcssを用いて横並びのブロックを作成する方法　「初心者用」
##はじめに
ホームページ作成で横並びのカラムの記述方法を初心者でもわかるようにまとめてみました。
横並びのカラムは使用頻度が高く簡単に思われがちですが、ブロックの間に隙間が空いてしまったり
他にもいろんな横並びの方法が存在していて、初心者にはなかなかとっつきにくいと思います。
初心者の私があとで見直すためのメモとして残しているものです。
よかったら参考にしてみてください。コピペするだけで再現できるように全てのコードを省かず載せています。
目標は下の画像です。

![スクリーンショット 2020-09-30 17.55.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/460280/7fc874db-c0e0-c7bd-2ada-9f5fe180fcbb.png)



##まずはhtml
htmlは下記のように親要素**wrapper**と子要素**wrapper-sub A**、**wrapper-sub B**で構成されています。
最初の！DOCTYPE〜bodyまでは挿入コードで基本みんな同じになります。


```index.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>###</title>
<link rel="stylesheet" href="###">
</head>
<body>

 <div class="wrapper">
   <div class="wrapper-sub A"></div>
   <div class="wrapper-sub B"></div>
 </div>

</body>
</html>
```
##次はcss


```index.css
.wrapper {
    width:100%;
    font-size:0px;
    height:300px;
}

.wrapper-sub {
    width:calc(100%/2);
    font-size:16px;
    height:100%;
    display:inline-block;
}

.A {
    background-color:red;

}

.B {
    background-color:blue;
}
```

cssは左側と右側の要素の色を赤と青に設定します。
ここで重要なのは親要素に（.wrapperで）font-size:0;を指定することです。
font-size:0;を行わないと下記画像のように横に並ばずに上下に並んでしまいます。
子要素の間には隙間があります。この隙間を考慮すると、全体のちょうど半分のサイズにならずに下に回り込んでしまいます。
そこでこの隙間をなくすためにfont-size:0を使用します。
![スクリーンショット 2020-09-30 18.00.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/460280/adcb5fcd-a6a9-cca5-8d79-da7bce82017f.png)