OmniAICreator/Qiita-1.07M huggingface dataset:
TurbolinksでpushStateするには

JavaScriptでURLを変更し、ブラウザーではページ遷移しないようにするには、[history.pushState](https://developer.mozilla.org/en-US/docs/Web/API/History/pushState)を使います。Turbolinksが動いている環境でpushStateを使うと、ブラウザーの進む/戻るで挙動がおかしくなります。Turbolinks以外がpushStateしたときページでは、popstateイベントをTurbolinksが処理しないからです。

```text
Aページ -(pushState)-> Bページ -(リンク)-> Cページ -(戻るボタン)-> URLはBだけど画面が変わらない
```

Turbolinksを使っているときは、次のようにします。`/users?name=Taro` に戻ると、`turbolinks:load` イベントが発火するようになります。

```JavaScript
let path = "/users?name=Taro";
Turbolinks.controller.
  pushHistoryWithLocationAndRestorationIdentifier(path, Turbolinks.uuid());
```

参考: [Manage browser history manually for one part of my Turbolinks-enabled Rails 5 app - Stack Overflow](https://stackoverflow.com/questions/53173328/manage-browser-history-manually-for-one-part-of-my-turbolinks-enabled-rails-5-ap)
UiPath　Excelアプリケーションスコープ内のエラースクリーンショット取得方法について

どうも、夢ノ島です。

UiPathでExcelの自動化をするときにExcelアプリケーションスコープはよく登場すると思います。
そこで問題になるのは
#### Excelアプリケーションスコープ内でエラーになった場合、TryCatchで囲んでいても、Catch部に入ったときにはExcelが閉じてしまっているため、エラー状況をキャプチャ・スクリーンショット取得できない
という問題です。

例えば、Excelを開いて色を塗るボタンを押下するワークフローがあったとします。

![ExcelCap1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/636507/d1787c33-3998-e7c6-2499-a595c5d081c2.png)

このボタンが、メニューバーが非表示だったり、ウィンドウ幅が狭かったりと、見つけられなかったとします。

![Excel.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/636507/77439bd0-d018-75c5-04ff-b6568ec2ab6d.png)
こんな感じに

この見つけられなかった状況のスクリーンショットを撮るために、Catch部にスクリーンショットを撮るアクティビティを置いてみました。

![ExcelCap2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/636507/51711c30-7a13-8482-aaf2-d98dde3e0bb9.png)

さて、この結果どうなるでしょう？
取れたキャプチャがこちら

![20200630082358.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/636507/151947d2-b82c-0192-d26f-b0968dc3b6ed.png)

というExcelと関係ない後ろで開かれていた画面のスクリーンショットを取得してしまいました。

<B>これを解消するためには、Excelアプリケーションスコープのワークブック出力変数で変数を出力して操作することでスクリーンショット取得が可能になります</B>

![wbtes.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/636507/d019fdf2-3f31-7e82-5c14-03f03424265c.png)

こんな感じでWorkbookApplication変数を出力してやると

![20200630083437.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/636507/0eea628a-f8a5-6e1d-f621-f394b2ebc3a8.png)

ちゃんと撮れるようになりましたー！！
（WorkBookApplication変数を出力すると、Excelアプリケーションスコープを出ても自動的に閉じなくなるようです。

ただしこの状態のままだとワークブックが開いたままになりExcelプロセスが残ってしまう原因にもなるので、
ブックを閉じるアクティビティを入れておいた方が安心かもしれませんね。

![wbcatch.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/636507/b647098e-fbd5-45e9-7efb-2c283b795bb8.png)

おしまい
Swift 今更！？PencilKit

WWDC2020「PencilKitの新機能」を見て、PencilKitを使っていないと思い立ち始める。
PencilKitを使って作りたいものが過去からあり、そこに向かって、最初の最初、スタートライン前。

# とりあえずPencilKitを使う

## StoryBoardでPKCanvasView配置

![alt](http://www.cloudsquare.jp/kumonosu/wordpress/wp-content/uploads/2020/06/PencilKit01.png)

## ViewController.swift</h4>

```swift
import UIKit
import PencilKit

class ViewController: UIViewController, PKToolPickerObserver {

    @IBOutlet weak var pkCanvasView: PKCanvasView!

    var toolPicker: PKToolPicker!

    override func viewDidLoad() {
        super.viewDidLoad()
        pkCanvasView.allowsFingerDrawing = false
    }
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
    }
    
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        if let window = self.pkCanvasView.window {
            let toolPicker = PKToolPicker.shared(for: window)
            toolPicker?.addObserver(pkCanvasView)
            toolPicker?.setVisible(true, forFirstResponder: pkCanvasView)
            pkCanvasView.becomeFirstResponder()
            print("PKToolPicker Set")
        }
    }
}
```
# これだけでここまで出来る

![alt](http://www.cloudsquare.jp/kumonosu/wordpress/wp-content/uploads/2020/06/PencilKit02.png)
EC2(AmazonLinux2)にJenkinsを構築する

EC2 に Jenkins を構築したのでその作業のメモです。

鍵に権限を付与します。

```bash
$ chmod 600 KEY_NAME.pem
```

ログインします。

```bash
$ ssh -i KEY_NAME.pem ec2-user@{IP_ADDRES}
```

アップデートします。

```bash
$ sudo su -
$ yum -y update
$ yum -y upgrade
```

Nginx をインストールします。（必要な場合）

```bash
$ amazon-linux-extras install nginx1.12
$ yum -y install nginx
$ systemctl enable nginx.service
$ systemctl start nginx.service
$ systemctl status nginx.service
```

Docker をインストールします。

```bash
$ yum -y install docker
$ systemctl enable docker.service
$ systemctl start docker.service
```

マウントするディレクトリを作成します。

```bash
$ mkdir /home/jenkins_home
$ chown -R 1000:1000 /home/jenkins_home
```

Jenkins を起動します。

```bash
docker run \
  --name jenkins \
  --detach \
  --volume /home/jenkins_home:/var/jenkins_home \
  --publish 8080:8080 \
  --publish 50000:50000 \
  jenkins/jenkins:lts
```

Nginx の設定を追加します。（必要な場合）

```nginx:nginx.conf
user root;
worker_processes  4;

events {
    worker_connections 1024;
}

http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;
    include /etc/nginx/conf.d/*.conf;
}
```

```nginx:jenkins.conf
server {
    listen       80 default_server;
    listen       [::]:80 default_server;
    server_name  _;
    location / {
        proxy_set_header Host $http_host;
        proxy_pass http://localhost:8080;
    }
}
```

Nginxを再起動します。

```bash
$ systemctl restart nginx.service
```


参考

[Installing Jenkins ](https://www.jenkins.io/doc/book/installing/)
Ubuntu20.04がログインループで立ち上がらなくなった場合の対処法

https://musaprg.hatenablog.com/entry/2020/06/30/201759?_ga=2.116440197.22719078.1593501641-1427021627.1587146651 と中身は同じです。

- - -

大抵は[nvidiaドライバのバージョン不整合](https://qiita.com/musaprg/items/58d9dee4ef70e3882a9d)ですが、今回は自動ログインをオンにしたことが原因だった模様です。

以下サイト様に圧倒的感謝

http://ubuntuhandbook.org/index.php/2020/01/login-loop-auto-login-enabled-in-ubuntu-19-10-with-nvidia-driver

## 環境

```
$ cat /etc/lsb-release
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=20.04
DISTRIB_CODENALE=focal
DISTRIB_DESCRIPTION="Ubuntu 20.04 LTS"
```

## 原因

gdm3とnvidiaドライバの相性が悪いことが原因みたいです。どうやらgdm3の既知のバグのようで、20.04LTSでも発現します。

https://bugs.launchpad.net/ubuntu/+source/gdm3/+bug/1845801

## 解決方法

1. Ctrl+Alt+F2(ダメならF1~F5を順に押してみるとどこかでttyが起動する)を押して仮想コンソールに入る。

1. `sudo nano /etc/default/grub`でgrubの設定を開く。（viでもemacsでもなんでもいい）

1. `GRUB_CMDLINE_LINUX_DEFAULT="quiet splash"`から、`splash`を消す。
変更後は`GRUB_CMDLINE_LINUX_DEFAULT="quiet"`となっているのを確認。

1. nanoで開いた人はCtrl+X→yで保存・終了する

1. `sudo update-grub`で変更を適用

1. `sudo reboot`で再起動

これで直るはず
【Rails】lightbox2を用いて画像拡大機能の実装

# 目標
![ezgif.com-video-to-gif.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/579893/dcff8276-d5e5-df0b-fafb-712e8fd860fc.gif)
# 開発環境
・Ruby: 2.5.7
・Rails: 5.2.4
・Vagrant: 2.2.7
・VirtualBox: 6.1
・OS: macOS Catalina

# 前提

下記実装済み。

・[Slim導入](https://qiita.com/matsubishi5/items/b639ffddecc626856039)
・[投稿機能実装](https://qiita.com/matsubishi5/items/1a5aeab651b5822d1f2c)

# 実装

## 1.`application.html.slim`を編集

```slim:application.html.slim
doctype html
html
  head
    title
      | Bookers2
    = csrf_meta_tags
    = csp_meta_tag
    = stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track': 'reload'
    / 追記
    link href='https://cdnjs.cloudflare.com/ajax/libs/lightbox2/2.7.1/css/lightbox.css' rel='stylesheet'
    = javascript_include_tag 'application', 'data-turbolinks-track': 'reload'
    / 追記
    script src='https://cdnjs.cloudflare.com/ajax/libs/lightbox2/2.7.1/js/lightbox.min.js' type='text/javascript'
```

## 2.`image_tag`を編集

```slim:books/show.html.slim
= link_to @book.image.url, 'data-lightbox': @book.image do
  = image_tag @book.image.to_s
```
Dockerでローカル開発環境のhttps化

## はじめに

ローカル開発環境の https 化
https://blog.jxck.io/entries/2020-06-29/https-for-localhost.html
この記事を読ませて頂き、自分の環境ではこうやってるよというのをシェアしようと思いました。
Let's Encryptによる証明書ではなくて、自己署名証明書を使う方法になります。
また、記事で指摘されているlocalhostの特殊性を無視して粗があっても手早くhttpsで開発したい人向けです

## https化の動機
localhostとの通信を暗号化しても無意味な気がしますが、そうせざるをえない時もあります

- mixed contentでリソースがブロックされる
- FacebookのOAuthで設定するredirect URIはhttpsでないと動かない
- Geolocation APIで位置情報を取得する時にhttpsでないと動かない

そのような時のためにlocalhostをhttpsで使う手順があると便利です。
`ngrok`などもありますが、手元で完結するタイプの方法になります。

## steps
### `https-portal` imageを使う

こちらのイメージを使わせて頂きます。
https://github.com/SteveLTN/https-portal

```docker-compose.yml
version: "3"
services:
  https-portal3000:
    image: steveltn/https-portal:1
    ports:
      - '3443:443'
    environment:
      STAGE: local
      DOMAINS: 'localhost -> http://host.docker.internal:3000'
```

このコンテナは、 `https://localhost:3443` でリクエストを受けて `http://localhost:3000` にフォワーディングしてくれる設定となります。
(`localhost:3000` で開発中のアプリケーションが動いていることを想定)
`STAGE=local` [この環境変数を渡すと](https://github.com/SteveLTN/https-portal#test-locally)コンテナの中で自己署名証明書を作ってくれます（便利!)
[Docker Desktop for Mac](https://hub.docker.com/editions/community/docker-ce-desktop-mac/)を使っている場合はこれでOK。他の環境では `extra_hosts` の設定が必要でしょう。
例:

```docker-compose.yml
version: "3"
services:
  https-portal3000:
    image: steveltn/https-portal:1
    ports:
      - '3443:443'
    environment:
      STAGE: local
      DOMAINS: 'localhost -> http://host.docker.internal:3000'
    extra_hosts:
      - "host.docker.internal:host-gateway"
```

Dockerでアプリケーションを動かしている場合は `host.docker.internal` でなくて直接コンテナに繋いでも動くと思います。

### Chromeで警告を無視する

これをEnabledにすると **localhost** に限り自己署名証明書であっても警告が出なくなります。

```
chrome://flags/#allow-insecure-localhost
```
セキュリティを下げる設定ですが対象がlocalhostのみなので許容します。
以上で https://localhost:3443/ へhttpsアクセスできます。
【Nyantech ハンズオンシリーズ】機械学習を使って写真に写っている猫を見分けてみよう！をやってみた。ハマりポイントを補足

#概要
[【Nyantech ハンズオンシリーズ】機械学習を使って写真に写っている猫を見分けてみよう！](https://aws.amazon.com/jp/builders-flash/202003/sagemaker-groundtruth-cat/)をやってみました。
いろいろとハマってしまったので、補足します。

##環境
###リージョン
「us-east-1」のバージニアに統一する必要があります。（オレゴンとかでも大丈夫らしい）
私は説明をよく読まずにS3を東京リージョンにして進めてしまい途中でエラーとなり、やり直しました。
また、SageMakerも東京リージョンにしてしまったため再度やり直しました。

###画像のサイズ
使用する画像のサイズに制限があるようです。私が最初に準備した画像は4896x3672ピクセルでした。
このサイズでもRekognitionで「Train model」を開始するまでは問題なく進みます。しかし、Train Modelを開始して数秒後にTrain Failとなり、無効なデータが多すぎるとメッセージされてしまいます。
Rekognitionの要件には画像サイズは最大で4096x4096ピクセルとなっていますので、準備する画像はサイズを確認する必要があります。

![Rekognitionの表示](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208266/422565d1-4955-c66b-df22-e70c9d248b16.png)

###画像の枚数
学習に使う画像は1種類のラベルに付き10枚程度はあったほうが良いようです。理由は（推測）、RekognitionのTrain Modelの際に学習用データセットを2:8にテスト用と学習用に振り分ける設定を選択するためと思われます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208266/0ecc6511-f4ac-9132-8444-e6e65f5d8b9f.png)

##APIを実行して判定結果を確認する
学習が完了してモデルができあがると好きな画像を判定させることができます。
操作はすべてAWS CLIからAPIを実行するようになっています。
画像は事前にS3へアップロードしておく必要があります。

Rekognitionが発行してくれるAPIは、「Start Model」「Analyze image」「Stop model」の3つです。
使い始めるときに「Start Model」のAPIを実行して、判定時には「Analyze image」、停止するときは「Stop model」のAPIを実行します。「Stop model」しないと利用料金が発生し続けるようです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208266/8af00a03-a2f3-1e6c-1216-06b000bf4ffe.png)

###「Analyze image」時の注意
Start、StopのAPIはすぐに応答があったのですが、「Analyze image」のAPIを実行した初期は「準備ができていない」というエラーが返ってきました。StartのAPIでは「Starting」という返答があり、Rekognitionのコンソール上でもStartingと表示されていたので何がおかしいのか全くわかりませんでしたが、数分後には正常に動作できたので、おそらく「Analyze image」できるまでには時間がかかるようです。

##まとめ
以上の補足事項があれば、おそらく2～3時間で完了できるハンズオンかと思います。
ちなみに私の環境では19枚の画像を学習させて約50分の学習時間でした。
精度は50%でした。（画像の問題）

誰かの助けになれれば最高です。
リモートワーク通信節約の小ネタ

## Notes 
会社のオフィスに Notes サーバーがあって、自宅から VPN を使って Notes サーバーに接続する必要がある人向け。しかもその時の回線が光ファイバーじゃなくて ADSL 10M 以下の細い回線の人向けだと思います。

ネットワークのデータ圧縮を有効にすると、表示したいページを選択してから表示し終わるまでの間の時間がちょっぴり短くなる気がします。
注意点としては Notes サーバー側でもデータ圧縮を有効に設定する必要があります。 

![notes_port_a.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27855/ae81103f-3ec5-88f0-bc06-aadd0b9b68f1.png)

## Outlook 
通常のメールのほかに、複数のメーリングリストに参加していて、ふだんはほとんど読まなくてもいいニュースが流れているメーリングリストの場合に役立つかもしれません。 Outlook 起動時には、メーリングリストからのメールが届いているかどうかもチェックしてダウンロードしているようです。
「共有フォルダーのダウンロード」のチェックを外すと、メーリングリストに届いているメールのダウンロードがされなくなり、Outlook 起動時のメールダウンロードが完了するまでの時間が短くなります。
メーリングリストを見に行くと、そのときにダウンロードされるようです。

「共有フォルダーのダウンロード」の設定は、アカウント設定＞アカウント名と同期の設定＞その他の設定　にあります。
![outlook_a.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27855/adf30945-7394-77eb-6b41-53dee999b8b6.png)

![outlook_b.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27855/75bea5de-33e1-cb95-5040-975d072e0546.png)

![outlook_c.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27855/90a16077-8446-5514-7c9e-e1077596e205.png)
【Flutter, Dart】Validationを実装する


# バリデーション
```validation_helper.dart
// 英数のみ、6桁のバリデーション

class Validator {
  Validator._();

  static bool isValidHoge(String hoge) {
    const _hogeRegExpString = r'^[0-9a-zA-Z]{6}$';
    return RegExp(_hogeRegExpString, caseSensitive: true).hasMatch(hoge);
  }
}
```

接頭辞`r`を付することでraw(生)文字列として扱うことができます。（エスケープシーケンス回避のため）


参考記事
1.  [form用正規表現判定/備忘](https://qiita.com/fubarworld2/items/9da655df4d6d69750c06)
2.  [RegExp constructor](https://api.flutter.dev/flutter/dart-core/RegExp/RegExp.html)






# 実装例
```example_notifier.dart
// freezedを使用した実装例

class Hoge {
  void onChangeHoge(String hoge) {
    state = state.copyWith(hoge: hoge);
    if (Validator.isValidHoge(hoge)) {
      state = state.copyWith(isEnable: true);
    } else {
      state = state.copyWith(isEnable: false);
    }
  }
}
```
Java問題「言語仕様」初期化子(2)


次のプログラムをコンパイル・実行するとどうなるか？

```java

public class Knock013 {
    private static Object n;
    private static final Object HOGE = n.toString();
    public static void main(String[] arguments) {
        System.out.println("HOGE is " + HOGE);
    }
}
```

解答
変数の初期化がされていない。
nはnullの状態なので、toString()でNullPointerExceptionが発生する

```java

private static final Object HOGE = n.toString();
```

### final修飾子とは
final修飾子を指定した変数は、基本的に規定値など一度値を設定したら変更する必要がないときい使います。

変数は基本的に値を自由に変更できますが、一度値を設定したら変更されない変数のことを「定数」と呼びます。
クラス定数を指定する場合は、変数の宣言の頭にstatic finalを書きます。

- 変数へのfinal：変数へ値の再代入をできなくする(=変数へ値を代入できるのが最初の1回のみになる)
- クラスへのfinal：クラスを継承できなくする
- メソッドへのfinal：メソッドをオーバーライドできなくする
ログが重くて開けない時はファイル分割すればいいじゃない

※この記事は新入社員の問いにあることないことないことないこと話した備忘録です。

# 本日の困りごと
障害発生時のログ確認。早く中身を確認して原因特定したいのに、ログが1GB吐かれてて開けません！
## 環境
windows7以上。

# 経緯
いつもそんなに時間のかからない処理が超遅かったので気軽に「何か変なログ吐いてる？」と聞いたらこうなった。

# 本日の解決方法
なんでそんなにログファイルが重いのかはさて置き。

手っ取り早くファイルを分割して確認しよう！ということにした。
サクラエディタ(メモ帳以外のエディタ)なら開ける！…と言っても、スクロールする度にフリーズしていた過去があるのだ。。

powershellを起動して以下を実行すると解決する。
ファイル名やパスは適宜修正。

```powershell
$i=0; cat XXXX.log -ReadCount 10000 | % { $_ > result$i.log;$i++ }
```

「powershellって何ですか？どうしたらいいんですか⁉」という人は
[win]+[r]でpowershellと打ってみませう。
開いたそいつがpowershellだ。
コマンドプロンプトみたいな画面なのだが…うん、まぁ似たようなもんだ。(違いはまたいずれ…)

VSCodeが利用できる環境の場合[新しいターミナルを開く]でデフォルトで起動するのはpowerShellだったはず。

…という記事を編集している間に該当の障害は解決したらしい(手伝えよ)

## 参考サイト
[進撃の社畜：txtやcsvが1GBを超えて開けないならPowershellで分割しよう](https://kbn48.com/technical/270)
wordpress学習(2020/06/30)

###カスタムフィールド
カスタムフィールドは記事のタイトルや本文以外に別の情報を追加してサイトに掲載する属性を設定できる。

カスタムフィールドを使えば、「ピックアップ商品の中から、価格が500円以上のものを最新5件だけ表示する」「国産カテゴリで評価が5つ星のものをランダムに10件表示する」など細かな条件を指定できる。

カスタムフィールドを追加するとき名前と値を入力する。名前を価格や発売日、値に数値を入れる。

カスタムフィールドに入力しただけでは表示されずテーマのファイルを変更しなければならない。
VBSを覚えるための覚書。

※RPAを覚えるための覚書（ややこしい）。メモなので順次追加します。
※7/14アップデートしました。

# ウェブサイトと記事

[ぶろぐち　https://www.bloguchi.info/1282](https://www.bloguchi.info/1282)
VSCodeのextension「Code Runner」の設定方法詳細。だいぶわかりやすい。

[REONTOSANTA　https://knowledge.reontosanta.com/archives/838] (https://knowledge.reontosanta.com/archives/838)
VBSからExcelのファイル操作する。

[Takashi Kanamaru's Web Page - VBScriptの演算子](http://yasusigi.net/iasdocs/aspdocs/ref/vbs/vbscript/5.htm)
まあ、プログラムやってる人ならだいたいわかると思うんだけども、それでも言語によって方言があったりするので。このウェブサイトも個人の覚書っぽい。

[VBScript関数リファレンス - https://www.kanaya440.com/contents/script/](https://www.kanaya440.com/contents/script/)
VBSで取得した日付を好きにフォーマットしたくて調べた際に行き着いた。
たくさん載ってます。
特に日付処理のページ↓はうれしい。
https://www.kanaya440.com/contents/script/vbs/function/date/
https://www.kanaya440.com/contents/script/vbs/function/data/cdate.html

[バヤシタ - yyyymmdd 形式の文字列で現在日付を取得する方法](https://bayashita.com/p/entry/show/39)
上と同じく日付の操作で行き着いた。なんていうか、今まで使っていた言語にはさくっとフォーマットするコマンドがあったんだけど、VBSにはそういうのないのかな？


#ソース
※なにぶん初心者、コレおかしくね？ってところ見つけたらご指摘ください。

**任意のディレクトリの最新ファイルを取得する。**
これもどこかから探して持ってきたやつなんだけど、参照元が分からなくなっちゃった。

```ruby:GetLatestFileName
' //引数
' [path] 目的のディレクトリまでのパス 
' [dir] ディレクトリ名
' [ext] ファイル形式

Function GetLatestFileName(path, dir, ext)
    Dim wshShell, exec, line

    Set wshShell = CreateObject("WScript.Shell")

    Set exec = WshShell.Exec("cmd /c dir /a-d /b /o-d """ & path & dir & "\*." & ext & """")
    exec.StdIn.Close()

    Do While Not exec.Stdout.atEndOfStream
        line = Trim(exec.StdOut.ReadLine())
        Exit Do
    Loop 

    Set dir = Nothing
    Set exec = Nothing
    Set wshShell = Nothing

    GetLatestFileName = line

End Function

```


#MICHIRU RPA
GUI味の強いRPA。安価。非エンジニア向なので、私はコレ。

**MICHIRU RPAユーザーフォーラム**
https://michiru.co.jp/community/main-forum/
Swift：メニューバー(NSStatusBarButton)をクリックしたときの動作を左右クリックで分ける

```swift:AppDelegate.swift
@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {

    let statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
    var button: NSStatusBarButton!

    func applicationDidFinishLaunching(_ aNotification: Notification) {
        button = statusItem.button!
        button.title = "🤔"
        button.action = #selector(clicked(_:))
        // ここで受け取るイベントを登録しておく
        button.sendAction(on: [.leftMouseDown, .rightMouseDown])
    }

    @objc func clicked(_ sender: NSStatusBarButton) {
        let event = NSApp.currentEvent!
        switch event.type {
        case .rightMouseUp:
            print("right click")
        case .leftMouseUp:
            print("left click")
        default:
            break
        }
    }
}
```
Slackでユーザー名をシンプルかつカラーに表示させる。

あ- Slackを使ってると最初はいい感じだったが、
**ゆくゆくはみんなのユーザー名の前にあるアイコンがうざくなった。**
調べたところユーザー名をシンプルでカラーに表示できる方法があったので共有します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/163986/a4cbb090-714a-8c4e-2e4d-3f9220d2a3c2.png)

▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼▽▼

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/163986/09a60693-0ced-a638-0618-96f6f461d693.png)

**■変更方法は簡単**

```
１）「環境設定」をクリック
２）「メッセージ & メディア」タブを選択(ctrl + カンマ)
３)  コンパクトテーマを選択
```

追加で `/color @ユーザー名 3EB890` のように 6 桁の HTML カラーコードを使って好きな色を指定できます。
カラーコードは[原色大辞典](https://www.colordic.org/)をご参照して入力してみてください。
ソフトウェア開発のテスト工程で行う各テストの概要

ソフトウェア開発のテスト工程で行われる各テストの概要について整理しました。
以下記事の続きです。

■ソフトウェア開発のテスト工程について
https://qiita.com/quroooos/items/c78d04af8035d165fdc5

## 単体テスト
・機能確認テスト
　各モジュールが詳細設計書や機能仕様書通りに動作することを確認するテスト。
・制御フローテスト
　プログラムの「命令」や「分岐」などがすべて実行されるかを確認するテスト。
・データフローテスト
　データや変数の流れ（「定義」→「使用」→「消滅」）を確認するテスト。
　静的解析ツールなどを用いて行う。

## 結合テスト・機能テスト
・状態遷移テスト
　状態遷移図、状態遷移表に基づいて動作を確認するテスト。
・機能確認テスト
　複数モジュールで実現される機能が、詳細設計書どおり動作することを確認するテスト。

## システムテスト
・確認テスト
　一度テストされている事項を再度確認するテスト。
・評価テスト
　○×で判定しにくい品質に対して程度を判断するテスト。
　セキュリティテスト、ユーザビリティテストなどがある。
・負荷テスト
　動作しているシステムに負荷をかけて行うテスト。
・環境テスト
　ソフトウェアを取り巻くプラットフォームや周辺機器に注目して行うテスト。
　構成テスト、互換性テストなどがある。
・機能確認テスト
　ユーザーの要求を確認するテスト。
　OSや環境、複数の機能、設定値、入力値などを組み合わせて行う。
・アドホックテスト
　場当たり的に行うテスト。テスト設計は行わない。
・探索型テスト
　テスト担当者の経験をベースに行うテスト。
・リスクベースドテスト
　リスクを想定し、リスクの大きい部分から優先的に確認するテスト。

## 参考
■【この1冊でよくわかる】ソフトウェアテストの教科書―品質を決定づけるテスト工程の基本と実践
https://www.amazon.co.jp/dp/B00U17809A
CircleCIからCloudRunへ自動的にデプロイする

この記事は、CircleCIを使って、Google Cloud Platform(GCP)のサービスであるCloudRunへシステムをデプロイをする方法をまとめたものです。

CircleCIを使えばgithubにpushしたソースコードを自動的にテスト、ビルド、デプロイすることができます。
なおこの記事内では、githubとCircleCIの連携方法やCircleCIの利用方法については省略しています。

# ポイント
・ CircleCIのCloudRun用Orbsを利用する
・ GCPに接続するための接続情報を環境変数に設定する

## 構成
全体のイメージはこんな感じ。

![名称未設定ファイル.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/528040/c4667344-02d5-f253-5364-229ceb038745.png)



GitHubの構成はこんな感じ。
言語はGo1.13です。

```tex:github
/.circleci
  |-config.yml
/sample-app
  |-Dockerfile
  |-main.go
  |-... (他アプリソース群)
```



# 手順
1. CircleCIのジョブ設定 （設定ファイルを作成）
2. CircleCIからContainerRegistry、ClourRunに接続するための環境変数を設定
3. githubに対してソースコードをpushする

## CircleCIのジョブ設定
CircleCIのジョブ設定は、上記のようにgithubのリポジトリにconfig.ymlを追加するだけです。
CircleCIからCloudRunにデプロイするためのOrbsが提供されているのでそれを利用します。
今回はマネージドのCloudRunの方にデプロイするようにします。

ちなみにOrbsというのは、いわゆるライブラリのようなもので、他のymlファイルをインポートしています。
自分で書くのが難しい部分を使いやすくして提供してくれています。

下記の設定は、githubにpushされたソースコードをテスト、ビルド、デプロイする設定になっています。

```config.yml
version: 2.1 # use CircleCI 2.1

orbs:
    gcp-cloud-run: circleci/gcp-cloud-run@1.0.2

executors:
    build:
        docker:
            - image: circleci/golang:1.13 #

jobs:
    # テスト時はgo1.13のdockerイメージを使ってgo testを実行
    test-job:
        executor:
            name: build
        steps:
            - checkout
            - run:
                name: Run unit tests
                command: | 
                    cd sample-app
                    go test -v ./...
    # デプロイ時はContainerRegistoryにビルドしたイメージを保管し、CloudRunへのデプロイを実行
    # リポジトリのDockerfileに従ってビルドされる
    # プロジェクトID test-project-hogehogeは変更する
    deploy-job:
        docker:
            - image: 'cimg/base:stable'
        steps:
            - checkout
            - run:
                command: cd sample-app
            - gcp-cloud-run/init
            - gcp-cloud-run/build:
                source: ./sample-app
                tag: 'asia.gcr.io/test-project-hogehoge/sample-app:${CIRCLE_SHA1}'
            - gcp-cloud-run/deploy:
                image: 'asia.gcr.io/test-project-hogehoge/sample-app:${CIRCLE_SHA1}'
                platform: managed
                region: asia-northeast1
                service-name: sample-app
                unauthenticated: true

#テスト用ジョブ(テストのみ)とデプロイ用ジョブ(ビルド、デプロイ)に分けて実行
workflows:
    test-and-deploy:
        jobs:
            - test-job
            - deploy-job
```


## CircleCIの環境設定
CircleCIからGCPへ接続するためにはサービスアカウント情報が必要になります。
なのでアクセス権限を持ったサービスアカウント（のJSONキー）をGCP上で作成しておいてください。
※よくわからなければCloudBuildとCloudRunの管理者権限を付与すれば権限上は大丈夫

CircleCIでは下記を環境変数として設定することでGCPへの接続が可能になります。
・GCLOUD_SERVICE_KEY
・GOOGLE_PROJECT_ID
・GOOGLE_COMPUTE_ZONE

これらを下記のようにCircleCIプロジェクト設定画面で設定すればOK
![スクリーンショット 2020-06-30 16.46.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/528040/b35d10fd-705e-e953-1461-e67dd8234d4d.png)

※GCLOUD_SERVICE_KEYにはJSONキーをそのまま入れています

## CloudRunの設定
CloudRun側では特に設定は必要ありません。
CircleCIが完了すれば自動的にデプロイされます。

## CircleCIの実行
githubにソースコードをpushしてCircleCIを実行してみてください。
CircleCIでテストが実行され、ビルドしたコンテナイメージがCloudRunにデプロイするまでを自動的に実現することができます。

## 参考
https://circleci.com/orbs/registry/orb/circleci/gcp-cloud-run
https://circleci.com/docs/ja/2.0/google-auth/
CloudRunでRedis(Memorystore)を利用する

この記事は、Google Cloud Platform(GCP)のサービスであるCloudRunでRedisを利用するための方法をまとめたものです。


CloudRundもサーバレスVPCアクセスが利用可能になったので、CloudRunからMemorystore(Redis)を利用することが可能になっています。
CluodRunでできることがどんどん増えていくのは嬉しいですね。

## ポイント
- フルマネージドなインメモリサービスであるMemorystore(Redis,Memcached)を利用する
- Memorystoreを利用するにはプライベートアドレスでの接続が必要
- GAEやCloudRunから接続する場合はサーバーレスVPCアクセスを利用する  
※GAEやCloudRunはマネージドサービスなのでプライベートアドレスが付与されない


## 手順
1. サーバーレスVPCを作成
2. MemoryStoreを作成
3. CloudRunを作成

### 1. サーバーレスVPCを作成
CloudRunからアクセスするためのネットワークを作成します。

GCPコンソールから
VPCネットワーク > サーバーレスVPCアクセス
に移動し、コネクタを作成
（初回はAPIを有効化する必要あり）

例えば下記のように設定します。

![スクリーンショット 2020-06-30 12.49.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/528040/b7e2a3d8-a032-d4de-8f69-ab0c70d895e3.png)


### 2. Memorystore(Redis)を作成
次に、Memorystore(Redis)を下記の点に注意して作成します。

- ロケーションを 1.で作成したコネクタと同じリージョンにする
(今回はasia-northeast1)

- ネットワークを1.で作成したコネクタと同じネットワークにする
(今回はdefault)

これらを設定することで 1.のコネクタを通したVPCアクセスが可能になります。

### 3. CloudRunを作成
最後に、CloudRunにソースコードをデプロイします。

#### 変数設定にRedisのIPアドレス、ポート番号を指定する
2.で作成したMemorystoreのIPアドレス、ポート番号を環境変数に下記のように設定します。

![スクリーンショット 2020-06-30 12.45.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/528040/7687c8c7-24df-789f-c0cc-003966d78136.png)

#### 接続設定に作成したサーバーレスVPCコネクタを指定する
CloudRunが接続するコネクタを下記の画像のように設定します。

![スクリーンショット 2020-06-30 12.39.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/528040/bb2afcaa-4a0c-13c8-2e81-fa3f3550f20c.png)

#### サンプルコード

サイト訪問によるカウントアップ

```main.go
// Command redis is a basic app that connects to a managed Redis instance.
package main

import (
	"fmt"
	"log"
	"net/http"
	"os"

	"github.com/gomodule/redigo/redis"
)

var redisPool *redis.Pool

func incrementHandler(w http.ResponseWriter, r *http.Request) {
	conn := redisPool.Get()
	defer conn.Close()

	counter, err := redis.Int(conn.Do("INCR", "visits"))
	if err != nil {
		http.Error(w, "Error incrementing visitor counter", http.StatusInternalServerError)
		return
	}
	fmt.Fprintf(w, "Visitor number: %d", counter)
}

func main() {
	redisHost := os.Getenv("REDISHOST")
	redisPort := os.Getenv("REDISPORT")
	redisAddr := fmt.Sprintf("%s:%s", redisHost, redisPort)

	const maxConnections = 10
	redisPool = redis.NewPool(func() (redis.Conn, error) {
		return redis.Dial("tcp", redisAddr)
	}, maxConnections)

	http.HandleFunc("/", incrementHandler)

	port := os.Getenv("PORT")
	if port == "" {
		port = "8080"
	}
	log.Printf("Listening on port %s", port)
	if err := http.ListenAndServe(":"+port, nil); err != nil {
		log.Fatal(err)
	}
}

```

Dockerfileはかなり適当...

```Dockerfile
FROM golang:1.13
RUN mkdir -p /app
WORKDIR /app
COPY . /app
RUN go build /app/main.go
ENTRYPOINT ["/app/main"]
```

ここまでの設定ができていれば、リロードするごとに数値がカウントアップされるサイトがデプロイされていると思います。


### 参考サイト
GAEからサーバレスVPCへの接続
https://cloud.google.com/appengine/docs/standard/python/connecting-vpc?hl=ja#creating_a_connector

GAE/GoでRedisへ接続する
https://cloud.google.com/appengine/docs/standard/go/using-memorystore?hl=ja
background-attachment: fixed;は使うな！

自分メモで残しておきます。

#background-attachment: fixed;は使うな！

単刀直入にいうと、
**全ブラウザに対応していない**からです。

## Edgeでは背景画像の大きさが制御できない
Windows10で確認したところ、IEでは問題なかったのですが、
なぜかEdgeで background-size:cover; が効きませんでした。

## iPhoneのsafariでも背景画像の大きさが制御できない
iPhone6と10でも同じ不具合を確認しました。。。

他のブラウザでは大丈夫でしたが、
上記2つで問題が発生しました。。。
background-attachment: fixed; を解除すると、
背景画像の大きさが変わっているので、
必ず調整し直してください。

報告は以上です！！！
（何か良い対処方法がありましたら、ご教示いただけるとありがたいです。）
Eclipse 2020-06にアップデートしたら部分的に文字化けが発生したので、Eclipseをダウングレードした

使っていたEclipseを2020-03から2020-06にアップデートしたら部分的に文字化けが発生したので、その原因と対処法をまとめておく。

症状は下記URLに同じ。入力した日本語のコメント等の一部が□で囲った?マークになってしまう。
文字コードが原因ではない。
https://qiita.com/segur/items/60b3711c0c9207d23b2f

上記URLに書いてあるようにフォントを日本語対応のものに変えれば解決はするが、日本語のよい等幅フォントを探す手間がかかるし、デフォルトフォントのConsolasが気に入っている人にとっては苦しい。

調べてみると、この不具合は2018年辺りから確認されていたが、2019-12で一度解消されていたものらしい。
https://bugs.eclipse.org/bugs/show_bug.cgi?id=536562
しかし、2020-06のアップデートで再発してしまったと考えられる。（2020-06-30時点で上記URLに既に報告もあった）

そこで、2020-03に戻せば文字化けはなくなると考え、ダウングレードすることにした。
下記URLを参考にした。
https://stackoverflow.com/questions/28030294/how-to-downgrade-my-eclipse-luna-to-eclipse-kepler

Pleiadesで日本語化していた場合、
![eclipse-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/664985/321e2592-652c-fe55-65f1-c212bbeb6742.png)
ヘルプ -> Eclipse IDEについて
![eclipse-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/664985/71d33f3a-5cd6-7a9d-1b07-814f1e8598ce.png)
-> インストール詳細
![eclipse-3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/664985/7bcd49f8-a97b-e7a7-609a-6ed4842c190c.png)
-> インストールヒストリー

で、アップデート前の日付（なるべく直近がよいと思われる）を選び、下部にある「前回保存した状態に戻す」を選択することでダウングレードができた。
結果、コメント等の一部が□で囲った?マークになってしまう現象は起きなくなった。
今後のアップデートでバグが解消される可能性もあるが、いつ解消されるかは分からないので、既に2020-06にアップデートしてしまい、フォントを変えたくない人は試してみてよい方法だと思う。
Java問題「言語仕様」初期化子(1)

## 変数の初期化順について



初期化子(1)
次のプログラムをコンパイル・実行するとどうなるか？

```java

public class Knock012 {
    private static boolean flag;
    public static void main(String[] arguments) {
        System.out.println(flag);
    }
}
```
- 私の回答
コンパイルエラーとなる
flagが定義されてないからエラーになる


- 答え
- 
 ```
false が出力される。
```
falseが標準出力される。 論理値boolean型の初期値はfalseである。
単純に初期値があることを知りませんでした。
Azure VMの起動停止をStart/Stop VMs during off-hoursでサクッとスケジュールする

## 目的
AzureのClassic VM数台を内製ツールを使ってスケジューラーで起動、停止してたのですが、
ツールの保守終了に伴い、別の方法に移行する必要が出てきました。
と言ってもあまり工数はかけたくない・・・ということで、Azure Automationでサクッと実装してみました。
Classic VMでしか確認できてませんが、V2環境でも動作するはずです。（未検証）

タイトルにも書いてますが、[Start/Stop VMs during off-hours](https://docs.microsoft.com/ja-jp/azure/automation/automation-solution-vm-management)を利用しています。
RunbookギャラリーからStart Azure V2 VMsをインポートして利用する、といった事例の方が多いですが、Azureに関してあまり知識がなく、Classic VMでシンプルに実装するにはStart/Stop VMs during off-hoursを利用した方が良いのかと思います。

## 設定手順

### 1. Azure Automationからアカウントを追加

Azureにログインし、Automation アカウントを作成します。

<img width="378" alt="azure1.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/629630/3423eb9a-1d7a-0efa-4bd7-218f83dd9086.png">

今回はautomation-accountという名称のアカウントを作成し、アカウントを作成するサブスクリプション、リソースグループ、場所を設定します。

<img width="341" alt="azure2.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/629630/3ebeb167-30b2-be96-0171-46254f70b6ba.png">


### 2. VMの開始、停止の設定
作成したAutomationアカウントを開き、左のメニューの関連リソースから、VMの開始/停止を開きます。

<img width="354" alt="azure3.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/629630/7f669e9c-4249-ad92-8cd6-e6409838b751.png">

下記の通りソリューションを有効にします。

<img width="749" alt="azure4.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/629630/43602f4f-e966-ec96-0287-3530eccaa65f.png">

Start/Stop VMs during off-hoursの作成を実行します。
ソリューションの追加画面から、まずワークスペースを開いて新規作成します。

<img width="342" alt="スクリーンショット 2020-06-30 16.54.23.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/629630/8a2fc3f1-44df-15b8-ca9f-28a2de4c3a91.png">

ワークスペース名、サブスクリプション名、リソースグループ、場所をそれぞれ設定します。
<img width="674" alt="azure5.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/629630/6a6081bf-7ec9-45d3-3986-dba55827ee72.png">

作成したワークスペースを設定した後に、パラメータを設定します。

Target ResourceGroup Namesに制御したいVM名を設定します。
あるいはワイルドカードでリソースグループの全サーバーを指定したり、VM Exclude Listで除外指定もできるようです。
あとは起動、停止する時間を設定し（後で変更可能）、メール通知する場合は送信先のメールアドレスを指定します。

<img width="506" alt="azure6.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/629630/fd3d7bed-8693-22ed-1abb-1b01faa4b3c9.png">


設定が完了するとAutomationアカウントにタグが付与されます。
このタグをクリックすることで、スケジュールなどのプロパティを参照できます。

<img width="755" alt="azure7.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/629630/dbf561f7-49e3-a4e0-04d9-87c202511ac4.png">

### 3. 実行スケジュールなどの設定

スケジュールを開くと設定した起動時間、停止時間がそれぞれ設定されています。
このスケジュールを開くと時間が変更できたり、タイムゾーンを変更したりできます。

<img width="958" alt="azure8.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/629630/632031b9-526f-d3c1-f278-f3bfa6b28798.png">

あとは定刻になればVMの起動、停止が行われます。
スケジュール可能な条件は以下の通り、ありがちなスケジュールで設定ができます。

<img width="578" alt="azure9.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/629630/1bd29b1d-b8f0-cbcb-bfc8-3fc1d5a05b8c.png">

以上、GUIベースで完結して設定ができるので、インフラ系エンジニアには大変助かる機能でした。
レーザー彫刻機

#中華レーザー彫刻機を買った
アリババでレーザー彫刻機を購入。
組み立てで精度に影響すると（自分的に）ハマる可能性大なので
速攻使える（であろう）モデルを選定した。

[NEJE Master2 20W](https://ja.aliexpress.com/item/33059299228.html?spm=a2g0o.productlist.0.0.5c8846b2B4BzzM&algo_pvid=04ad4ff0-e897-4b16-83ce-aa2ec87a06fd&algo_expid=04ad4ff0-e897-4b16-83ce-aa2ec87a06fd-0&btsid=0be3764315935058155682145e7f2c&ws_ab_test=searchweb0_0,searchweb201602_,searchweb201603_)

製品名は20Wだが、レーザー出力が20Wではない。
20WはACアダプターの出力くらいに考えた方が良い。（さすが中華）
レーザー出力は5.5W。
自分の用途としては十分すぎるくらいのパワー。
しかしこの価格帯は　もれなく半導体レーザーなのでレーザー寿命が短い。
どんどんレーザー出力が落ちてくる筈なのでそこら辺も考慮してのオーバースペック。
（レーザーは消耗品と考える。　CNCにしても（高い）ドリルがすぐに使えなくなっちゃうし）

コロナも落ち着いてきたので注文後2週間で到着。
![IMG_3694.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/664748/290eaa97-42d5-6146-1e9f-55c73a0745cb.jpeg)

ほとんど組み上がってるので、組立はとっても簡単。

コントローラー
うっすい
<img width="768" alt="IMG_3695.jpeg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/664748/a500d36f-3105-4b10-d9b4-5b7b7c7aeaca.jpeg">

使うときはここにUSBとACアダプタをつなぐだけ
![IMG_3696.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/664748/ec494fcb-2893-01de-ffdb-741c33dbb0cb.jpeg)

組み立て。Y軸にX軸を載せるだけ。
<img width="1230" alt="2020-06-30 17_50_53-Clipboard.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/664748/68f5a5e6-14d0-46fd-5644-71566e75a959.png">

載せる前にレーザー用のハーネスコネクタをつないでおく。この手順だけ守れば、あとは完璧（笑）
<img width="768" alt="IMG_3710.jpeg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/664748/970c464b-2e17-fd9b-c3fc-56c2ff690b51.jpeg">

付属の袋ナットで締結
<img width="768" alt="IMG_3702.jpeg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/664748/3b147b9e-c0c8-94c7-0855-970a10b5d8ee.jpeg">

レーザーモジュール
<img width="768" alt="IMG_3703.jpeg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/664748/8fc548c1-3259-5dc8-c2fd-bc2e312ecb67.jpeg">

レーザーモジュールをネジとプラスペーサーでここにつける。
![IMG_3706.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/664748/93c78db1-b0ba-2abb-fbc4-59c3f42035e6.jpeg)

取り付け完了
<img width="1021" alt="2020-06-30 17_58_00-Clipboard.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/664748/ebfde988-757b-ab1c-cf2f-221e35410950.png">

ハーネスをコネクタに接続して完成。
<img width="569" alt="2020-06-30 18_00_23-Clipboard.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/664748/beccbc79-57b4-b58e-8ac1-bd6cd9cb869f.png">

精度を気にして組み立てる必要が全く無い。

あとは平らなところに置いて、レーザーの焦点を合わせて専用ソフトで焼くだけ。
まあ、焦げ臭いこと焦げ臭いこと。
箱に入れて排煙装置を設置したい。（そこまでくると業務用）
<img width="764" alt="2020-06-30 18_05_58-Clipboard.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/664748/2bde838b-c4a6-d0a6-8013-4574d48df227.png">

試しにバイクのキーホルダーに車名と自分の名前を彫ってみた
出力20％は強すぎた。
![IMG_3720.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/664748/7f8539bb-b7c7-3ac3-7dbf-be7e9bfe375c.jpeg)
github pages は node_modules や vendor ディレクトリ を無視する

タイトル通りですが、ハマったので残しておきます

ソース: https://help.github.com/ja/github/working-with-github-pages/about-github-pages-and-jekyll

```index.html

  <script src="./node_modules/mathjs/dist/math.js"></script>
```
要はこんな記述をすると、ローカルでは動作できてもgithubにホスティングすると読めなくなるようです。

```_config.yml
include: ['node_modules']
```
ドキュメントを読む感じだとこれで動きそうな気がしますが、動きませんでした。（どっか間違ってるんだと思いますが）

webpackでビルドしろってことですかね
Firestoreでcan't get serverTimestampが出てサーバータイムスタンプが使えない

## 結論: 静的変数を動的に呼び出しては行けない

[firebase. firestore. FieldValue](https://firebase.google.com/docs/reference/js/firebase.firestore.FieldValue#servertimestamp)

```js
import firebase from 'firebase/app'

// 初期化等は完了しているものとする

const db = firebase.firestore()
// 静的変数なので()で呼び出すとエラーになるため別で変数を定義する
const serverTimeStamp = firebase.firestore.FieldValue.serverTimestamp()

const postData.updated_at = serverTimeStamp

```

## 問題: `can't get serverTimestamp`でサーバータイムスタンプが呼び出せなくなった

この様に書くとエラーを吐き出す。
問題は`const db = firebase.firestore()`のように
動的に呼び出したfirestoreから呼び出そうとしているから

```js
import firebase from 'firebase/app'

// 初期化等は完了しているものとする

const db = firebase.firestore()

// ここでエラーが出る
const postData.updated_at = db.FieldValue.serverTimestamp()
```

解決するには下記のように静的に呼び出してあげる必要がある。

`firebase.firestore()` ->`firebase.firestore`

```js
const serverTimeStamp = firebase.firestore.FieldValue.serverTimestamp()
```

## 参考
https://stackoverflow.com/questions/50361962/cloud-functions-cloud-firestore-error-cant-get-servertimestamp
C言語で16文字でセグフォらせる

↓PythonをSegmentation Faultで落とすのが最近流行っているようなので。
[pythonを三行でセグフォらせる](https://qiita.com/autotaker1984/items/a8ba955acdc81c907b3d)
[pythonを2行でセグフォらせる](https://qiita.com/sh1ma/items/a6dd1bcca0d9725e7e67)
[pythonを1行でセグフォらせる](https://qiita.com/MysteriousMonkey/items/0e389ccdc12988dd4263)
[Pythonを33文字でセグフォらせる](https://qiita.com/kanimum/items/95a45d8be31ef75d4332)
[Pythonをctypesを使わずに1行でセグフォらせる](https://qiita.com/gyu-don/items/bd2d89f3abd7d0e1f072)
[Rustを5行でセグフォらせる](https://qiita.com/YoshiTheChinchilla/items/b9f4a6e92266e8ec9d3d)
主語(Python)をC言語に変えるだけで途端につまらなくなりますが、やってることは同じです。

# コード

gcc 10.1.0で動作確認(警告は出ます)

```c
*a;main(){*a=0;}
```

```terminal
> gcc segf.c
segf.c:3:4: 警告: データ定義が型または記憶域クラスを持っていません
    3 |    *a;main(){*a=0;}
      |    ^
segf.c:3:5: 警告: 型がデフォルトの ‘int’ に ‘a’ の宣言内でなります [-Wimplicit-int]
    3 |    *a;main(){*a=0;}
      |     ^
segf.c:3:7: 警告: 戻り値の型をデフォルトの ‘int’ にします [-Wimplicit-int]
    3 |    *a;main(){*a=0;}
      |       ^~~~
> ./a.out
zsh: segmentation fault (core dumped)  ./a.out
```

**注意** 現在のところ、C言語をセグフォらせる最小の文字数は5文字です。(下のコメント参照)

# 簡単な解説
これだけだとQiitaの記事要件を満たさないため、簡単に解説します。
まず最初に観るコードは↓です。

```c
int main(void) {
    int *a = 0;
    *a = 0;  // ←ここで落ちる
    return 0;
}
```

C言語にはポインタという概念があり、これを用いるとメモリの好きな番地にアクセスすることができます。
`int *a = 0`のところで`a`という名前のポインタを宣言し、メモリ番地`0`を代入します。2行目で`*a`に値を代入すると、実際には存在しないアドレス[^1] にアクセスすることになり、CPU例外が発生します。

## ショートコーディング

それでは、上のコードを短くしていきましょう。まず、C言語の規約上`return 0`は省略できます。[^5]また、返り値の型`int`も互換性の観点から省略できます。(省略すると暗黙的に`int`になります。)さらに、引数の`void`も書かなくても動きます。
これらを踏まえると以下のようになります。

```c
 main() {
    int *a = 0;
    *a = 0;  // ←ここで落ちる
}
```

ここで、ローカル変数`a`はグローバル変数として定義してもよいです。さらに、グローバル変数として定義すると、(よほど特殊な環境でない限り)`0`で初期化してくれるため初期化のための代入が不要です[^6]。

```c
*a;
main() {
    *a = 0;  // ←ここで落ちる
}
```

さらに、グローバル変数の型指定子`int`も省略できます。これで冒頭のコードになりました。


# おまけ。番地`*(0p00000000)`は本当に存在しないか?

古典的なコンピュータにおいて、メモリはたくさんの情報を保管しておくことができる装置です。しかしながら、たくさんの情報をメモリに送ったり、メモリから受け取るためには、直感的には送受信したい情報の個数分だけ電線が必要になります。しかしながら、CPUの動作速度に耐えられるような細い電線をたくさん用意するのはコスト面で不可能なので、メモリアドレスという概念を導入します。これにより、一度に読み書きできる情報の個数(1Byteという)[^2]は少ないながら、メモリアドレスを変更することによってたくさんの情報をメモリに保管することができます。

このように、メモリ大容量時代に欠かすことのできないメモリアドレスという概念ですが、これは`0`から始まる正の整数で表されるため、実際には`0`番地というアドレスは存在するのです。では、なぜセグメンテーションフォルトが起きたか、というと、OSが利用しているページングというCPUの機能が原因です。

コンピュータにおいてメモリアドレスはユニークです。よって、特に工夫しない場合、マルチタスキングOSにおいて複数のプロセスが同じメモリアドレス空間を所有することになります。この場合、メモリにアクセスする場合は、他のプロセスが利用しているメモリに間違ってアクセスしないように気をつけなければなりません。このような不便を解消するのがページングで、ページングを用いると各プロセスがそれぞれ別個の仮想アドレス空間を所有できます。このとき、仮想アドレス空間[^3]と物理(メモリ)アドレス空間の間を変換する表をOSが管理しています。

よって、本来プロセスは存在するすべてのアドレスにアクセスできるはずです。しかしながら、実際はそうはいかず、仮想アドレス空間においてもOSが幅を利かしています。プログラムから利用できるメモリの範囲を制限しているのです。このような仮想アドレス空間の区画分けをメモリマップといいます。[この記事](https://qiita.com/akachochin/items/8d7d46a5f51bd230abc2)にあるように、0番地の周辺は使用しないこととなっており(対応する物理メモリアドレスが割り当てられていない)、そのためセグメンテーションフォルトが発生します。[^4]

# おわりに

さらっと書くつもりが20分くらい掛かった。書かなきゃよかった。

[^1]: 後述。
[^2]: 実際にはメモリモジュール一本あたり64bitが同時に書き込めます。
[^3]: とリニアアドレス空間
[^4]: OSの実装方法によります。また、OSによっては別のCPU例外が発生します。
[^5]: `return 0;`を省略するのは間違い、と勘違いしている人が大勢います。
[^6]: セグフォを起こすだけなので、初期化しなくても大丈夫だろうけど。
APIについて噛み砕く

## 近況報告
　**実務経験を積みたい！**

　これにつきます。ツイッターでテックの先輩のつぶやきをみても，独学と実務では圧倒的に差があることが伺えます。どのくらい差があるのかは日を追うごとに興味が増します。私の周りにwebエンジニアがいないため，現状を聞くとなるとやはり面接先のPM・CTOが多くなります。ただ，「実務って独学で得られる経験となにが違いますか」なんて質問落とされそうだから言えないよねw　私が考える限りでは，納品スピード，瞬時にメソッドなど使用するものを決める能力，ggr力なのかな。ただ，やはり現場で肩を並べて経験しないと実感はできないでしょう。

#　本日のお題　　
**「API」**
　テックキャンプに参加していた時さっぱりわからずスルーしていた単語。ただ，就活をしていると多々現れる単語。「API」について噛み砕く

## API
[参考にしたサイト](https://wa3.i-3-i.info/word12428.html)
[APIの記事じゃないけどわかりやすい記事](https://qiita.com/TakahikoKawasaki/items/e37caf50776e00e733be)
API・・・　Application　Programing　Interface

　アプリケーションのプログラミングの表面。。。直訳しても意味が分からない略語である。**アプリケーションをアプリケーション外から操作する窓口**って感じで私は理解しました。

###具体例
　youtubeのAPIを用いて再生回数が１億回の動画をrubyで抽出し，出力しました。

　この場合「youtube」というアプリケーションの中の情報を，アプリケーション外から「ruby」を用いて抽出した，と考えられます。このように，外側からアクセスしてアプリケーションの情報を処理，抽出できるカラクリがAPIです。ここでのAPIは，**youtubeの情報を得るためのAPI**と考えられます。このようにAPIは目的によって動きが変わっていくものです。

データを抽出するAPI，アプリケーションを操作するAPI，同期するAPI，ソートを行うAPIなど

データとか情報　｜壁←API←壁｜　外側

　のような内側にアクセスするためのカラクリという認識で私は捉えています。つまり**APIがあれば誰でも中の情報が操れる**訳で，流石にセキュリティの観点でそれはマズイので対策としてyoutubeのAPIはAPIキーが発行されそれを利用してアクセスできるようになります。

### 実用例(天気予報)

```
require 'net/http'
require 'uri'
require 'json'
require 'time'
require 'date'

uri = URI.parse('http://weather.livedoor.com/forecast/webservice/json/v1?city=170010')
json = Net::HTTP.get(uri)
result = JSON.parse(json)
today_tel = result['forecasts'][0]['telop']
tomor_tel = result['forecasts'][1]['telop']
min_tem = result['forecasts'][1]['temperature']['min']['celsius']
max_tem = result['forecasts'][1]['temperature']['max']['celsius']
des = result['description']['text']
des1 = result['description']['publicTime']
des2 = DateTime.parse(des1)

#puts result
puts ' 天気予報 '.center(60, '-')
puts ''
puts '今日の天気は、' + today_tel + ' でしょう '
puts '最低気温は、' + min_tem + '℃' + ' でしょう '
puts '最高気温は、' + max_tem + '℃' + ' でしょう '
puts '明日の天気は、' + tomor_tel + ' でしょう '
puts ''
puts ' 天気予報 '.center(60, '-')
puts ''
puts ''
puts ' 天気概況文 '.center(60, '-')
puts
puts des
puts ''
print ' 天気概況文の発表時刻  '
puts des2.strftime('%Y年%m月%d日 %H時%M分%S秒')
puts ''
puts ' 天気概況文 '.center(60, '-')
```
ここではhttp://weather.livedoor.com/forecast/webservice/json/v1?city=170010 にアクセスして気象情報を抽出して表示しています。

## おわりに
　イメージが湧きましたでしょうか？もっといい表現があればおしえて！
Concatenateの理解

#はじめに

KerasのF.Cholletの本を読むと、Concatenate Layerの説明がされている部分がある。下記の絵で表現されているが、この中でどんな演算が行われているかさっぱりしたまなであった。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208980/aef0da78-b7d5-f84c-baff-b8b6951ae713.png)

ところで、U-NET, ResnetなどにConcatenate Layerが重要な役割をしていることから、これ以上にConcatenate Layerを理解せず放置することは良くない判断で、簡単なテンソルを利用し、Concatenate Layerの挙動を確認することにした。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208980/3ed9b0bb-00f9-8712-1f1c-1b848fcf3826.png)
図　[U-Net アーキテクチャー](https://www.researchgate.net/figure/The-3D-u-net-architecture-Blue-boxes-represent-feature-maps-The-number-of-channels-is_fig1_304226155)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208980/bc741bc8-afdf-fc2a-6b39-4d1bdd38d12c.png)
図　[ResNetブロック](https://qiita.com/takoroy/items/07c5039ab12b74137626)

#理解

Excelで、文字列結合の時に、Concatenate関数を使ったことがあったので、何となく**複数の配列を何の演算もせず、くっつける**イメージは持っていたが、[Teratail](https://teratail.com/questions/163385)にわかりやすい図があったので、ここで紹介する。

ここで、青と緑二つの行列がある。
これらの行列は2次元（2D)テンソルで、これらの行列のShapeは（3,3)である。
axisとは、テンソルの次元（Dimension)を言う。[テンソル(Tensor)の理解(2) : Shape]![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208980/9c722160-613a-1df8-6773-ad133d433db8.png)


axisは、テンソルの次元の軸も指す。（物理でのモーメントを表記するときのベクトルを考えるといいかもしれない。）

2次元テンソルの場合、axis = 0が垂直方向、　axis = 1が水平方向を意味する。

ただし、axis = -1とした場合は、一番最後の軸を意味する。Pythonのリストのスライスを考えるといい。

そして、Concatenate　Layerの時、結合方向を指定することが可能である。

**(1) axis = 0の場合、垂直方向に結合する。**

**(2) axis = 1の場合、水平方向に結合する。（ただし、2次元の場合、axis = -1も同じ意味）**


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208980/0bd0ba89-e213-4a85-49a2-4dc27eb40cd6.png)




#プログラムコード

```Python:

import tensorflow as tf
import numpy as np

#2D Tensorの準備
x1 = np.array([[1,3,3],
               [5,2,1],
               [0,9,5]])

x2 = np.array([[3,2,3],
               [8,7,4],
               [0,1,1]])

print('x1=',x1)
print('x2=',x2)

```
## Concatenate Layer　

###垂直結合
```Python
#Concantenate Layer
# axis = 0, 垂直方向結合
y1 = tf.keras.layers.Concatenate(axis=0)([x1,x2])
print('y1=',y1)
```

```Python:垂直結合結果
y1= tf.Tensor(
[[1 3 3]
 [5 2 1]
 [0 9 5]
 [3 2 3]
 [8 7 4]
 [0 1 1]], shape=(6, 3), dtype=int32)

```

###水平結合
```Python
# axis = 1 (or axis = -1)  水平方向結合
y2 = tf.keras.layers.Concatenate(axis=-1)([x1,x2])
print('y2=',y2)
```

```Python:水平結合結果
y2= tf.Tensor(
[[1 3 3 3 2 3]
 [5 2 1 8 7 4]
 [0 9 5 0 1 1]], shape=(3, 6), dtype=int32)

```
#まとめ

やっとConcatenate　Layerの動作を理解した。（気がする。）
ただし、Concatenateのスペリングがなかなか頭に入らない。（涙）

#参考資料
1. [axis=-1の意味がわかりません(Python・Keras)](https://teratail.com/questions/163385)
2. [tf.keras.layers.Concatenate](https://www.tensorflow.org/api_docs/python/tf/keras/layers/Concatenate?hl=ja)
3. [kerasで頭に描いたネットワーク構造を実現するためのTips ~ Lambda 編 ~](https://qiita.com/Mco7777/items/158296ed7f66aed2ffc3)
GCP環境でSQL Serverのバックアップファイルからデータ復元してBigQueryに入れるまで手順メモ

# 背景
SQLServerのバックアップファイル(.bak)をもらって、「これ復元してBigQueryに入れといて」って言われて時にやったことをメモ。

大まかな手順としては以下のようになる。

- Cloud SQLでSQLServerインスタンスを構築
- .bakをSQLServerにインポート
- Azure Data Studioを経由でアクセスし、CSVファイルを吐き出す
- BigQueryにロード


# Cloud SQLでSQLServerインスタンスを構築

GCPではSQLServer環境を構築できる。
方法は2つあって1つは[GCEで構築する方法](https://cloud.google.com/compute/docs/instances/sql-server/creating-sql-server-instances?hl=ja)。
もう一つは、[Cloud SQLで構築する方法](https://cloud.google.com/sql/docs/sqlserver?hl=ja)。
今回はCloud SQLで構築する。おそらくCloudSQLでやったほうが楽。

[クイックスタートのインスタンスの作成](https://cloud.google.com/sql/docs/sqlserver/quickstart?hl=ja#create-instance) 通りにコンソールでぽちぽちしてSQLServerのCloudSQLを立てる。

データベースバージョンが色々あるが、バックアップの復元目的であれば一番安いExpressで十分。リソースも最小で構築した。


# コンソールから.bakファイルをインポート
Cloud SQLのSQL Serverでは、コンソールから簡単に.bakファイルをインポートできる。
[BAK ファイルを Cloud SQL にインポートする](https://cloud.google.com/sql/docs/sqlserver/import-export/importing#console_1)

# Azure Data Studio を起動する
SQLServerをいじるには SQL Server Management Studio という統合環境ツールを使うのが相場らしい。
[SQL Server Management Studio (SSMS) とは何か?](https://docs.microsoft.com/ja-jp/sql/ssms/sql-server-management-studio-ssms?view=sql-server-ver15)

しかし、これは Mac OSでは動かない。今回はMacOSでも動作するAzure Data Studio というツールを使う。
[Azure Data Studio とは](https://docs.microsoft.com/ja-jp/sql/azure-data-studio/what-is?view=sql-server-ver15)

ここ↓からZIPファイル形式でインストールして起動させる
[Azure Data Studio のダウンロードとインストール](https://docs.microsoft.com/ja-jp/sql/azure-data-studio/download-azure-data-studio?view=sql-server-ver15)


# Cloud SQL Proxy経由でAzure Data Studio から SQLServer に接続する

Cloud SQL Proxy をダウンロードし、起動して接続する
[SQL Server Management Studio を使用してインスタンスに接続する](https://cloud.google.com/sql/docs/sqlserver/quickstart)

Azure Data Studioで以下の情報を入れて、Cloud SQL Proxy経由でSQLServerに接続する

- サーバー名: 127.0.0.1
- ユーザー名: CloudSQLで定義したユーザー名
- パスワード:  CloudSQLで定義したパスワード

# Azure Data Studio でクエリ結果をCSV吐き出し
クエリ結果をCSV保存できる。便利。 愚直に SELECT * でCSV作成しまくる。

# CSVをBigQueryにロードする
`bq load` コマンドでBQにロードする

以上
【Ruby】Hash#has_key? がまた一歩、公式のdeprecatedに近づいた？

`Hash#has_key?` は、Matz さんから、

> "has_key" has already been deprecated by "key?"

と[宣告](http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/43765)されて早８年、いろんな Style guide で、 `deprecated` だと指摘されています。例えば、

* [RuboCop](https://rubystyle.guide/#hash-key)
* [Airbnb](https://github.com/airbnb/ruby#deprecated-hash-methods)

しかし、公式には、`Hash#has_key?` は今なお、`Hash#include?` や `Hash#key?`、`Hash#member?` を抑えて、唯一サンプルコードで用いられているメソッドであり、全く `deprecated` さを感じさせない活躍ぶりです。

[日本語版](https://docs.ruby-lang.org/ja/2.7.0/class/Hash.html#I_HAS_KEY--3F)はこんな感じで、

```ruby:日本語版
p({1 => "one"}.has_key?(1)) #=> true
p({1 => "one"}.has_key?(2)) #=> false
```

[英語版](https://docs.ruby-lang.org/en/2.7.0/Hash.html#method-i-has_key-3F)は、こんな感じです。

```ruby:英語版
h = { "a" => 100, "b" => 200 }
h.has_key?("a")   #=> true
h.has_key?("z")   #=> false
```

しかし、これも Ruby 2.7 までのことで、Ruby 2.8 からは、サンプルコードでの活躍をほかのメソッドに譲り渡すことになりそうです。

GitHub に上がっている[最新のコメント](https://github.com/ruby/ruby/blob/master/hash.c#L4007)はこうなっています。

```ruby
/*
 *  call-seq:
 *    hash.include?(key) -> true or false
 *    hash.has_key?(key) -> true or false
 *    hash.key?(key) -> true or false
 *    hash.member?(key) -> true or false

 *  Methods #has_key?, #key?, and #member? are aliases for \#include?.
 *
 *  Returns +true+ if +key+ is a key in +self+, otherwise +false+:
 *    h = {foo: 0, bar: 1, baz: 2}
 *    h.include?(:bar) # => true
 *    h.include?(:nosuch) # => false
 *
```

`Hash#include?` 一押しって感じですね。とはいえ、`Hash#has_key?` はまだ２番手につけています。

個人的には、`Hash#has_key?` に愛着を感じており、書き捨てのコードなどでは今でも `Hash#has_key?` を使うことがあります。

いまだ公式には `deprecated` 宣言されていない（ですよね？）`Hash#has_key?` を使い続けていいものやら、それとも、心を鬼にして `Hash#key?` など他のメソッドを使うべきなのか？ 心が揺れ動きます。

本当に `deprecated` にする気があるのなら、早々に引導を渡してほしいと思う今日この頃です。
VSCodeの邪魔なツールチップを無効にする

カーソルがエディタの領域にあると、その下のテキストを見て出てくるこいつのことである
よくショートカットの直後にこっちが有効になって変な動きになってしまうので
こいつをそもそも出ないようにしたい
<img width="692" alt="スクリーンショット 2020-06-30 17.37.29.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23195/a713a85e-c082-6825-0893-b064ff034498.png">

「VSCode　ワークスペースを検索　邪魔」

なんとなく分かってたけど出ない
今回も自力で発見したのでメモ

**⌘+shift+Pから「user setting」と入力、hoverでフィルタ**

Hover:Delayの数値を300→でかい数字へ変更（単位ms)
<img width="557" alt="スクリーンショット 2020-06-30 17.38.32.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23195/0ac5ba5e-ed48-a16d-5b9c-6a616545893c.png">

要はマウスカーソルが当たってからツールチップを表示するまでの時間である
これで表示まで**50分**掛かる設定になった
*Hover:Enabledを外したら消えるかと思ったけど消えなかった*

便利な機能なのに勿体ない！という人もいるかもしれない
でも使わんから要らんねん、すまない

**終わり。**
cx_Oracle が使える Docker イメージ

Python アプリから Oracle データベースに繋ぐ必要があり Python クライアントを入れる Dockerfile を書いたのだが、やたら苦労したので備忘録として書いておく。

``` Dockerfile:Dockerfile
FROM python:3.7

RUN pip install cx_Oracle

# Install Oracle Client
ENV ORACLE_HOME=/opt/oracle
ENV LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$ORACLE_HOME/lib
RUN apt-get update && apt-get install -y libaio1 && rm -rf /var/lib/apt/lists/* \
 && wget -q https://download.oracle.com/otn_software/linux/instantclient/19600/instantclient-basic-linux.x64-19.6.0.0.0dbru.zip \
 && unzip instantclient-*.zip \
 && mkdir -p $ORACLE_HOME \
 && mv instantclient_19_6 $ORACLE_HOME/lib \
 && rm -f instantclient-*.zip
```

[cx\_Oracle 8 Initialization — cx\_Oracle 8\.0\.0 documentation](https://cx-oracle.readthedocs.io/en/latest/user_guide/initialization.html)

公式ドキュメントによると、cx_Oracle を利用するためには Oracle Client ライブラリがインストールされている必要があり、cx_Oracle は次の順序でライブラリの読み込みを試行する。

1. `cx_Oracle.init_oracle_client(lib_dir="...")` で指定したパス
2. OS のライブラリパス (`$LD_LIBRARY_PATH`)
3. `$ORACLE_HOME/lib`

このうち `1.` と `3.` の方法で試したときに、他のライブラリは読み込めるのに `libnnz19.so` だけ「そんなファイルは無い」などとエラーが出て大変困った。
原因はいまだにわからない。

`2.` の方法を試したところ問題なく読み込まれた。