OmniAICreator/Qiita-1.07M huggingface dataset:
ファイルシステムのあれこれ（要点だけまとめてみた）

# 概要

- メジャーなファイルシステムのあれこれを簡単に要点だけまとめてみる。（主に自分用）

# ご注意

- 詳しい内容は下記リンク先が非常にわかりやすくまとめられております。
    - [https://www.buffalo.jp/support/faq/detail/1079.html](https://www.buffalo.jp/support/faq/detail/1079.html)
    - ↑こちらの記事を読んだほうが絶対にいいです。

# ファイルシステムあれこれ

| 名前 | 特徴 | 制限 | 備考 |
| --- | --- | --- | --- |
| FAT32 | いにしえからのロングセラーなファイルシステム<br>MacでのWindowsでもLinuxでも使えるらしい | 1ファイルの最大容量は4GB<br>1領域の最大容量は2TB | MS-DOSでは7.0(1995年)からFAT32に対応したらしい
| exFAT | 最近の、MacでもWindowsでも使えるファイルシステムと言ったらこれ | 1ファイルの最大容量の制限無し（理論上は128PiBらしい、何だその単位）<br>1領域の最大容量は512TiB（またもや何だその単位） | 一応理論上の制限はあるものの、かなり大きいサイズなので容量制限は気にしなくて良さそう。<br>今我々が購入できる10TBとかのレベルなら全然問題ない。 |
| NTFS | もっぱら最近のWindowsのファイルシステムはこれ<br>Macでは読み取りできても書き込みできない | 1ファイルの最大容量の制限無し（理論上は16TiBらしい）<br>1領域の最大容量は256TiB | 最近のWindowsの主流らしい。FAT32からの進化版。<br>急に電源切れてもトランザクションログからロールバックしてくれたりするっぽい。|

# まとめ

- MacでもWindowsでも使いたい → exFAT
- Windowsでしか使わない → NTFS

# 参考文献

- [https://www.buffalo.jp/support/faq/detail/1079.html](https://www.buffalo.jp/support/faq/detail/1079.html)
- [https://ja.wikipedia.org/wiki/ExFAT](https://ja.wikipedia.org/wiki/ExFAT)
- [https://ja.wikipedia.org/wiki/NT_File_System](https://ja.wikipedia.org/wiki/NT_File_System)
Rubyで配列をちょっとした条件でソートする

ある配列を独自の条件でソートするときもsort_byを使うとたいへん便利なのですが、書き方を失敗したときのエラーがわかりにくい場合があったので記事を書いてみます（5年くらい前もあれ、となっていたので）。


```Ruby
> (1..10).to_a.reverse
=> [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
```
たとえばこんな配列があります。これを昇順にソートするには単純にsortを使えばよい。

```Ruby
> (1..10).to_a.reverse.sort
=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

```
これを偶数を先に並べるにはどうしましょう。
sort_byをつかうと簡単です。

```Ruby
> (1..10).to_a.reverse.sort_by{|i| i % 2}
=> [10, 8, 6, 4, 2, 9, 7, 5, 3, 1]
```

偶数が先だけれど、昇順にしたいときは、sort_byのなかを配列にするとできます。
```Ruby
> (1..10).to_a.reverse.sort_by{|i| [i % 2, i]}
=> [2, 4, 6, 8, 10, 1, 3, 5, 7, 9]
```

数字か文字列かで違うこともあまりないんですが、文字列で特定の文字を含む場合に先に並べたいときはどうでしょうか。同じようにやってみると・・・

```Ruby
>  ["さばねこ","いぬ","しばいぬ","ねこ","とらねこ","あきたいぬ" ]
.sort_by{|animal| animal.include?("いぬ")} 
ArgumentError: comparison of Array with Array failed
```

エラーが出ました。
配列と配列を比べていると失敗しています。ちょっとやっていることとわかりにくく、うまく説明できませんが、TrueやFalseといったBoolean同士を比較できないから、ではないかと思います（どなたかいい説明あれば教えてください・・・）

原因はさておき、どう解決するか。三項間演算子で数字にするのが簡単です。

```Ruby
> ["さばねこ","いぬ","しばいぬ","ねこ","とらねこ","あきたいぬ" ]
.sort_by{|animal| [animal.include?("いぬ") ? 0 : 1]}
=> ["いぬ", "しばいぬ", "あきたいぬ", "さばねこ", "ねこ", "とらねこ"]
```

いぬが先でそのなかで辞書順にするにはこうします

```Ruby
> ["さばねこ","いぬ","しばいぬ","ねこ","とらねこ","あきたいぬ" ]
.sort_by{|animal| [animal.include?("いぬ") ? 0 : 1, animal]}
=> ["あきたいぬ", "いぬ", "しばいぬ", "さばねこ", "とらねこ", "ねこ"]
```

はい。
簡単でした。多次元配列やHashの配列であっても同じような考え方でsort_byを使うと簡単です。配列に列挙していくのでなにを優先させるかもわかりやすいですね。

参考）
sort_by のドキュメントはこちら。
https://docs.ruby-lang.org/ja/latest/method/Enumerable/i/sort_by.html
PN532 NFC RFID module + elechouse を Arduino UNO で試す

(Felica/Mifare/NFC チャレンジシリーズ) その他の記事はこちら 「Felica/Mifare/NFC でいろいろ実験」
https://qiita.com/nanbuwks/items/1f416d6e45a87250ee0a

-----

「PN532 NFC RFID module を Arduino UNO で読む」
https://qiita.com/nanbuwks/items/42f639e7eec928181298

では Arduino UNO で SPI 接続を使ってライブラリをいくつか試してみました。その結果、

> FeliCa を使う場合は elechouse 一択のようです。

と書きました。以下は elechouse で更に SPI 接続以外での動作を検証してみました。



# 環境

- PN532 NFC RFID module
  - 電解コンデンサ追加加工済
  - cf., 「NFC モジュールの動作安定性を改善する」https://qiita.com/nanbuwks/items/a9217ba2e8f206b899b1
- WEMOS LOLIN32 (≒ ESP32-DevKitC )
- Arduino 1.8.10 Linux 版
  - portable 化済
- Arduino UNO
- Ubuntu Linux 20.04 AMD64
- NFC library for Arduino
  - https://github.com/elechouse/PN532

ライブラリは以下の記事のようにしてインストールしています。
「PN532 NFC RFID module を Arduino UNO で読む」
https://qiita.com/nanbuwks/items/42f639e7eec928181298

また、以下では実験ごとにスイッチをインターフェースに合わせて都度変更しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/29d19fd1-5102-7ca8-eed4-11e462ce788e.png)



# SPI

接続して
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/a2619397-b9e3-da6d-31dd-5155e0f895ac.png)


サンプルスケッチを呼び出します。
「ファイル」-「スケッチ例」-「カスタムライブラリのスケッチ例」-「PN532」-「ReadMifare」を選びます。

以下のように変更します。

```
#if 1
  #include <SPI.h>
  #include <PN532_SPI.h>
  #include "PN532.h"

  PN532_SPI pn532spi(SPI, 10);
  PN532 nfc(pn532spi);
#elif 0
  #include <PN532_HSU.h>
  #include <PN532.h>
      
  PN532_HSU pn532hsu(Serial1);
  PN532 nfc(pn532hsu);
#else 
  #include <Wire.h>
  #include <PN532_I2C.h>
  #include <PN532.h>
  PN532_I2C pn532i2c(Wire);
  PN532 nfc(pn532i2c);	
#endif
```

問題なく動作しました。


## 電源供給を 3.3V にしてみる

問題なく動作しました。


# I2C

以下のように接続します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/e6cee804-7bc0-766b-fcb7-cff8cc7982d1.png)

スケッチの先の部分をI2C用に直します。


```

#if 0
  #include <SPI.h>
  #include <PN532_SPI.h>
  #include "PN532.h"

  PN532_SPI pn532spi(SPI, 10);
  PN532 nfc(pn532spi);
#elif 0
  #include <PN532_HSU.h>
  #include <PN532.h>
      
  PN532_HSU pn532hsu(Serial1);
  PN532 nfc(pn532hsu);
#else 
  #include <Wire.h>
  #include <PN532_I2C.h>
  #include <PN532.h>
  PN532_I2C pn532i2c(Wire);
  PN532 nfc(pn532i2c);	
#endif
```

スイッチもI2C用に変更します。


問題なく動作しました。


# SWHSU 

以下のように接続します。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/a1121aa4-9365-985d-a463-58767764f7cc.png)


## スケッチ変更


シリアル接続については、通常は HSU モードであり、PN532_HSU.h をインクルードするのですが、これはハードウェアシリアルが前提のようです。
Arduino Uno にはハードウェアシリアルは1つしかなく、それをプログラムやprint表示に使うとすると PN532_HSU.hは使えません。

ソフトウェアシリアルを使う場合は、PN532_SWHSU.h を使うようです。
以下のように直します。


```

#if 0
  #include <SPI.h>
  #include <PN532_SPI.h>
  #include "PN532.h"

  PN532_SPI pn532spi(SPI, 10);
  PN532 nfc(pn532spi);
#elif 1
  #include <SoftwareSerial.h>
  #include <PN532_SWHSU.h>
  #include <PN532.h>
  SoftwareSerial SWSerial( 10, 11 ); // RX, TX
  PN532_SWHSU pn532swhsu(SWSerial);
  PN532 nfc(pn532swhsu);
#else 
  #include <Wire.h>
  #include <PN532_I2C.h>
  #include <PN532.h>
  PN532_I2C pn532i2c(Wire);
  PN532 nfc(pn532i2c);	
#endif
```

問題なく動作しました。
【Solana, TypeScript】NFTのmintアドレスから現在のオーナーアドレスを取得


# 動作確認環境
- M1 Mac (macOS Monterey 12.3)
- @solana/web3.js : 1.36.0


# コード
`connection`変数は[@solana/wallet-adapter](https://solana-labs.github.io/wallet-adapter/)で取得したもの。
`getTokenLargestAccounts`で対象トークンを多く所有しているアカウントのアドレスがわかる。

```sample.ts
import * as web3 from '@solana/web3.js';

const getNftOwner = (mintAddress: string): Promise<string> => {
  return connection
    .getTokenLargestAccounts(new web3.PublicKey(mintAddress))
    .then(res => {
      const tokenAccount = res.value[0].address;
      return connection.getParsedAccountInfo(tokenAccount);
    })
    .then(res => {
      const data = res.value?.data as web3.ParsedAccountData;
      const ownerAddress = data.parsed.info.owner;
      return ownerAddress;
    });
};

// How to use (Owner of "Degen Ape #1506")
const address = 'HgDxaNFdq5HExHm2am6UfvVeiCTWdT6J5UzVu4NQR9MN';
getNftOwner(address).then(res => console.log('owner:', res));

```

# 参考
https://docs.solana.com/developing/clients/jsonrpc-api#gettokenlargestaccounts
dockerでnginxを使うその１

# はじめに
dockerでnginxを起動する
基本的なやり方は，dockerhubのサイトに記載されている
https://hub.docker.com/_/nginx

# 環境
- wsl2(Ubuntu 20.04)
- Docker version 20.10.13, build a224086
:::note info
Dockerはwsl2（Ubuntu20.04）にインストールされている
:::
# 準備
wsl2で利用でdockerは自動起動ではないので，dockerを起動する
``` bash
tlasu@XXXX:~$ sudo service docker start
 * Starting Docker: docker
 ```

# とりあえずの起動（失敗）
```
$ sudo docker run --name some-nginx -d nginx
```

```
tlasu@XXXX:~/docker_nginx$ sudo docker run --name some-nginx -d nginx
Unable to find image 'nginx:latest' locally
latest: Pulling from library/nginx
ae13dd578326: Pull complete
6c0ee9353e13: Pull complete
dca7733b187e: Pull complete
352e5a6cac26: Pull complete
9eaf108767c7: Pull complete
be0c016df0be: Pull complete
Digest: sha256:e1211ac17b29b585ed1aee166a17fad63d344bc973bc63849d74c6452d549b3e
Status: Downloaded newer image for nginx:latest
a956f848bb97787c26c1b203967e90d71c7020bf9b8640989f653858a4b96c91
```

ただし,  これだとアクセスできない。

# ポートを指定して起動
作成したコンテナに対して後からポート開放するのは面倒とのことなので，コンテナのnameを`some-nginx2`にして，
新しくコンテナを作り直す。
``` bash
$ sudo docker run --name some-nginx2 -d -p 8080:80 nginx
```
指定したポートにアクセスして，下記が表示されれば成功（上の例だと"http://localhost:8080"）
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2601976/361bef33-996d-6ec7-4fa5-11c3dc992139.png" width="75%">


# dockerコンテナの削除
```
sudo docker rm [コンテナID]
```
dockerを触っていると，たくさんのcontainerが立ち上がる。
`sudo docker ps`では表示されなくても，`sudo docker ps -a`でみると残っている。
一括削除の方法は下記。
コマンドオプションとかありそうだが・・・
``` bash
for a in `sudo docker ps -a |awk 'NR>1{print $1}'`; do sudo docker rm $a; done
```
KotlinでRestControllerAdvice

# 要約
- Kotlinのクラス参照はKClass型で、`::class`を使うと取得できる（Javaでいうと`.class`）
- `@ExceptionHandler`の引数にKClass型を与えると、この例外を拾って適切なレスポンスを返してくれる

# 実装

この例は、`HogeNotFoundException`が発生したときに404 (Not Found)を返すようなコントローラの実装です。

```kotlin
import com......HogeNotFoundException // 自由に
import com......dto.ErrorResponse // 自由に
import org.springframework.http.HttpStatus
import org.springframework.web.bind.annotation.ExceptionHandler
import org.springframework.web.bind.annotation.ResponseStatus
import org.springframework.web.bind.annotation.RestControllerAdvice

@RestControllerAdvice
class GlobalExceptionHandler {

    @ExceptionHandler(HogeNotFoundException::class)
    @ResponseStatus(HttpStatus.NOT_FOUND)
    fun handleHogeNotFoundException(exception: HogeNotFoundException): ErrorResponse {
        return ErrorResponse(exception.message)
    }
}
```

`ErrorResponse`は以下のように実装しています。

```kotlin
import com.fasterxml.jackson.annotation.JsonRootName

@JsonRootName("error")
class ErrorResponse (val message: String)
```

なんらかのHTTPリクエストに対して内部で`HogeNotFoundException`が発生すると、次のようなレスポンスが返ります。
```
{"error":{"message":"error message"}}
```

# 参考
- [Reflection with Kotlin - Baeldung](https://www.baeldung.com/kotlin/reflection)
Rubyをirbで触ろうとしたら文字化けした


# 事象
macのターミナルでrubyを触ろうとしたが、irbでの日本語入力で文字化けが発生
readlineというライブラリがあれば問題ないらしいが、そのライブラリはインストール済み
rubyはrbenvで3.1.1をインストールした
　　
# 原因
rubyのパスがmacにデフォルトでインストールされているrubyになっていてそれが原因で文字化けしていたらしい。
パスをrbenvの方に変更してあげればok

  
#  対応
下記サイトを参考にしながらコマンドを実行
https://outsource-systems.com/ruby-on-rails-install/
  
## パスを通す
```ruby
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile
echo 'if which rbenv > /dev/null; then eval "$(rbenv init -)"; fi' >> ~/.bash_profile

#変更の反映
source ~/.bash_profile
```
  
## rubyの再インストール
文字化けしたrubyはアンインストールして新しいバージョンを再インストール
```ruby
rbenv install --list #インストールできるバージョンを確認
rbenv install 3.1.1 #好きなバージョンを指定
which ruby #パスの確認
```
パスがrbenvになっていればok
【Github actions】Github Actionsで発生したSelenium::WebDriver::Error::UnknownError:unknown error: net::ERR_NAME_NOT_RESOLVEDを解消する

# 初めに

GitHub actions で Rails の Rspec の system test を行なっている際に Selenium::WebDriver::Error::UnknownError:unknown error: net::ERR_NAME_NOT_RESOLVED が発生しました。

## やりたい事

`Selenium::WebDriver::Error::UnknownError:unknown error: net::ERR_NAME_NOT_RESOLVED`を解消する

## 環境

- Rails 7.0.22
- Ruby 3.1.1
- Rspec 3.10.0

# workflow

GitHub actions 上で selenium docker を利用して、system spec を動かした際にエラーが発生

`Selenium::WebDriver::Error::UnknownError:unknown error: net::ERR_NAME_NOT_RESOLVED`
エラーメッセージに内容は名前が解消していないと出ています。

## rspec.yml

各コンテナの名前の名前を設定行います。
大切なのは Rails を実行しているコンテナの名前の設定がないとエラーが発生します。

```yaml:.github/workflows/rspec.yml
name: Rspec
on:
  push:
    paths-ignore:
      - README.md
      - Dockerfile
      - docker-compose.yml

jobs:
  rspec:
    runs-on: ubuntu-20.04
    env:
      RAILS_ENV: test
    services:
      database: #postgresコンテナの名前
        image: postgres:14.1
        ports:
          - 5432:5432
        env:
          POSTGRES_PASSWORD: password
          POSTGRES_USER: postgres
          POSTGRES_DB: test
        options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
      chrome: #seleniumコンテナの名前
        image: selenium/standalone-chrome
        ports:
          - 4444:4444
    container:
      image: ruby:3.1-alpine
      options: --name web #railsコンテナの名前を設定する
      ports:
        - 3000:3000
    steps:
      # 省略
```

# 参考

[コンテナ内でのジョブの実行](https://docs.github.com/ja/actions/using-jobs/running-jobs-in-a-container)

[container の options の一覧](https://docs.docker.com/engine/reference/commandline/create/#options)
.NET Framwork 2.0 のアプリを Azure AppService で動かしてみた

## 背景と目的

以前書いた「[.NET Framwork 2.0 のアプリを Azure AppService Hyper-V コンテナで動かしてみた](https://qiita.com/mnrst/items/92c93ef34265073b3af2)」のソースコードを使って、今回はもっとシンプルに AppService のランタイムに ASPNET|V3.5 を指定して、同じアプリが動くか試してみました。

## 前提条件

以前書いたソースコードが Azure Repos にある前提となります。

## AppService を作成

```bash:bash
# 環境変数をセットします
region=japaneast
prefix=mnraspnet
repourl=https://*******@dev.azure.com/*******/aspnetwebvb20/_git/aspnetwebvb20
repotoken=*****************

# リソースグループを作成します
az group create \
  --name ${prefix}-rg \
  --location $region

# AppService プランを作成します
az appservice plan create \
  --name ${prefix}-plan \
  --resource-group ${prefix}-rg \
  --sku FREE

# AppService を作成します
az webapp create \
  --name ${prefix}-app \
  --resource-group ${prefix}-rg \
  --plan ${prefix}-plan \
  --runtime "ASPNET|V3.5"

# Azure Repos からアプリをでプロします
az webapp deployment source config \
  --name ${prefix}-app \
  --resource-group ${prefix}-rg \
  --repo-url $repourl \
  --branch master \
  --git-token $repotoken

# AppService を開きます
open https://${prefix}-app.azurewebsites.net
```

## 参考

```bash:bash
# リソースグループを削除します
az group delete \
  --name ${prefix}-rg \
  --yes
```

<https://docs.microsoft.com/ja-jp/dotnet/framework/migration-guide/version-compatibility#backward-compatibility>
VirtualBoxでホストOSからゲストOSにteratermでSSH接続する

# はじめに
VirtualBox上の仮想マシンを操作する際、デフォルトのバーチャルモニターだと何かと不便なのでteratermを使ってSSH接続してみた。

# 前提
・windowsでVirtualBoxを動かしてるときの話です(macはしらん)
・VirtualBox上に仮想マシンを構築済み(本記事ではcentos stream 9)
・teratermをインストール済み

# 目次
1. ホストオンリーアダプターの作成・設定
1. IPアドレスの設定
1. teratermでつないでみる

# 1.ホストオンリーアダプターの作成・設定
今回はホストオンリーアダプターを使うことでホストOSからゲストOSへ接続する。
VirtualBoxマネージャーからホストネットワークマネージャーを開こう。
![image1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2602480/d2e0dc2e-bea1-da7a-4e23-dfe95ab79bf8.png)

ホストネットワークマネージャーで作成をクリックすると、新しいホストオンリーアダプターが作成される。
今回はVirtualBox Host-Only Ethernet Adapter #2が生成された。
この時生成されたアダプターのIPv4アドレスは**後で使うのでメモしておこう。**
![image2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2602480/2f6470f8-35a1-a57d-ecff-8c54676e653d.png)

続いて、先ほど作成したアダプターを仮想マシンにアタッチする。
VirtualBoxマネージャーより仮想マシンの設定を開き、左ペインよりネットワークを選択しアダプター2の設定を開こう。
![image3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2602480/45c436fd-05f2-d850-6d93-09d663d253ab.png)
「ネットワークアダプターを有効化」にチェックを入れ
「割り当て」をホストオンリーアダプターに変更。
「名前」には先ほど作成したアダプターを設定しよう。
![image4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2602480/28c36e4d-e74f-0a3b-278b-f660da8a24f4.png)

これで仮想マシンにNICを追加することができた。

# 2.IPアドレスの設定
以降は仮想マシン上で設定を行う。
仮想マシンでnmcli deviceコマンドを叩くと以下のように表示される。
`nmcli device`
![image5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2602480/3c158ac3-764d-9b6b-f75f-5dfd7fc466ff.png)

先ほど追加したNICはenp0s8というデバイス名で登録されている。
この状態ではネットワークの設定を行うことができないため、コネクションを作成しよう。
`nmcli connection add ifname enp0s8 con-name enp0s8 type ethernet`
![image6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2602480/e27256e8-5a98-0b1b-adc1-62e3558c5b12.png)
もう一度nmcli deviceコマンドをたたくとenp0s8のSTATEがconnectingになっている。

これでネットワークの設定を行えるようになったのでnmtuiコマンドで設定を行おう。
`nmtui`
enp0s8のコネクション設定を開き、以下画像の通りにする。
この時IPアドレスは**1.ホストオンリーアダプターの作成・設定**で作成したアダプターと
同じネットワークになるようになるように設定する。
![image7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2602480/5e5dc018-fd11-c1d8-da3a-d0e94641ce75.png)
今回ホストオンリーアダプターは192.168.153.1のため
NICのIPアドレスは192.168.153.10に設定した。

tuiを閉じてホストOSからpingをうってみる
```
C: >ping 192.168.153.10

192.168.153.10 に ping を送信しています 32 バイトのデータ:
192.168.153.10 からの応答: バイト数 =32 時間 <1ms TTL=64
192.168.153.10 からの応答: バイト数 =32 時間 <1ms TTL=64
192.168.153.10 からの応答: バイト数 =32 時間 <1ms TTL=64
192.168.153.10 からの応答: バイト数 =32 時間 <1ms TTL=64

192.168.153.10 の ping 統計:
    パケット数: 送信 = 4、受信 = 4、損失 = 0 (0% の損失)、
ラウンド トリップの概算時間 (ミリ秒):
    最小 = 0ms、最大 = 0ms、平均 = 0ms
```
ちゃんと疎通できている。
これでIPアドレスの設定も完了である。

# 3.teratermでつないでみる
いよいよteratermでつないでみる。

teratermをたちあげ、先ほど設定したipアドレスを入力する。
![image9.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2602480/e402b878-b5a8-b44b-4872-8b66585c8a23.png)

これでokを押すとSSH認証画面に飛ぶのでユーザやらパスワードやらを入力する。
(初回接続だとホスト認証画面が出るかも？)
![image10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2602480/9eedb389-ddae-627c-547b-50c3077ad015.png)
これにて目的達成である。

# 余談というか疑問
/etc/sysconfig/network-scriptsにファイルがなかったんですけど
NetworkManagerから行った設定って、どのファイルに記録されるんですかね。
```
[root@localhost ~]# ll /etc/sysconfig/network-scripts/
合計 0
```
【Solana】Anchorを使用したWebアプリのビルドに失敗するとき (polyfill)


# 動作確認環境
- M1 Mac mini (macOS 12.3)
- @project-serum/anchor : 0.23.0
- webpack : 5.70.0
- process : 0.11.10

# 事象
Solanaのクライアント側で`@project-serum/anchor`を使っていると、ビルドに失敗し以下のようなエラーに出くわすことがある。

```
Compiled with problems:

ERROR in ./node_modules/@project-serum/anchor/dist/browser/index.js 12:0-28

Module not found: Error: Can't resolve 'assert' in '<your project path>/node_modules/@project-serum/anchor/dist/browser'

BREAKING CHANGE: webpack < 5 used to include polyfills for node.js core modules by default.
This is no longer the case. Verify if you need this module and configure a polyfill for it.

If you want to include a polyfill, you need to:
	- add a fallback 'resolve.fallback: { "assert": require.resolve("assert/") }'
	- install 'assert'
If you don't want to include a polyfill, you can use an empty module like this:
	resolve.fallback: { "assert": false }
```

また、エラー文で示されているように`assert`パッケージをインストールしても正常にページが表示されない。

# 原因

`@project-serum/anchor`は内部でNode.jsのネイティブモジュールを使用しているが、webpack 5以降はNode.jsのpolyfillsが削除されたため、素直にビルドができなくなってしまった。
ちなみに、`react-script`なども内部でwebpackを使用しているため同じような事象が起こる。

# 対処方法

webpackのバージョンを4に落としてもいいのだが、それはそれで別の問題が発生する可能性あるため、ここではwebpackのプラグインを使って対処する。`react-script`を使用する場合のうまい対処方法は分からなかった。（ご存知の方いたら教えて下さい）

## 必要なパッケージをインストール

```sh
npm install assert process
```

## webpack.config.js にプラグイン設定を追加

```js
const webpack = require('webpack');

module.exports = {
  ...
  plugins: [
    new webpack.ProvidePlugin({
      process: 'process/browser',
      Buffer: ['buffer', 'Buffer'],
    }),
  ],
};

```

# 参考
https://www.npmjs.com/package/@project-serum/anchor
PoV とは？

## 勉強前イメージ

PoCとは違うの？

## 調査

### PoV とは

Proof of Value の略で、価値実証の意味になります。
課題を解決するためにシステムを導入する際、本当に導入して価値があるのかの検証をすることを指します。
例えば本格導入の前に、一部業務に対して試験的に導入してみてコストに見合った価値が得られるかを確認します。

### PoC とは違う？

PoC とは Proof of Concept の略で、日本語では 概念実証 になります。
新たなアイデアなど実現駅るかどうかや効果を検証することを言います。
詳細は [こちら](https://qiita.com/miyuki_samitani/items/42514df23c4867aa7c5e) をごらんください

PoCとPoVとの違いは以下です。

- PoV : 少しやってみて、導入して価値があるかどうかの検証
- PoC : 少しやってみて、できるかどうかの検証

## 勉強後イメージ

例えばずっとgmail使ってたけど、slack導入してみて勝ちがあるかどうかの検証
って感じかな？

## 参考

- [PoC 【Proof of Concept】 概念実証 / コンセプト実証](https://e-words.jp/w/PoC.html#:~:text=%E3%82%88%E3%81%8F%E7%9F%A5%E3%82%89%E3%82%8C%E3%82%8B%E3%80%82-,PoV%20(Proof%20of%20Value%EF%BC%9A%E4%BE%A1%E5%80%A4%E5%AE%9F%E8%A8%BC),%E3%81%AA%E3%81%A9%E3%81%A8%E8%A8%B3%E3%81%95%E3%82%8C%E3%82%8B%E3%80%82)
【expo-barcode-scanner】iOSのカメラインジケーターをオフにする

## iOSではカメラ使用中にインジケーターが点灯する

いくつか前のiOSバージョンから、カメラ使用中にステータスバーに緑のインジケーターが表示されるようになりました。

![img1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/681892/ca20d3cb-0d35-3400-19ef-67668a2bb550.png)

## BarCodeScanner

Expoには[BarCodeScanner](https://docs.expo.dev/versions/latest/sdk/bar-code-scanner/)というQRコードの読み取りなどに使用するコンポーネントがあります。

QRコードを読み取ってその情報を元に画面遷移を行う、というのはよくある使い方だと思います。

## 問題点

ドキュメント通りに実装すると、QRコードの読み取り機能を簡単に実装することができます。しかし、そのまま同じスタック内の別画面に遷移するとカメラがオフにならず、ステータスバーのインジケーターが点灯したままになります。

## 解決方法

画面遷移時に`<BarCodeScanner />`を非表示にしてあげることで上記の問題は解決します。

```javascript
import React, { useState } from 'react';
import { View } from 'react-native';
import { BarCodeScanner } from 'expo-barcode-scanner';
import { useFocusEffect, useNavigation } from '@react-navigation/native'

export default function App() {
  const [scanned, setScanned] = useState(false);
  const navigation = useNavigation()

  useFocusEffect(() => {
    setScanned(false)
  }))

  const handleBarCodeScanned = ({ type, data }) => {
    setScanned(true);
    navigation.navigate('NextScreen')
  };

  return (
    <View>
      {!scanned?
        <BarCodeScanner
          onBarCodeScanned={scanned ? undefined : handleBarCodeScanned}
        />
        :null
      }
    </View>
  );
}
```

ポイントは`!scanned`で、スキャン状態が`false`のときにだけ`<BarCodeScanner/>`を表示することです。そして画面遷移前に`scanned`を`true`にしてあげることで、`<BarCodeScanner/>`を非表示にします。

こうすることで画面遷移したときにステータスバーのインジケーターがオフになります。

遷移先の画面からバックボタンなどでQRコード読み取り画面に戻ってきたときに`useFocusEffect`を使用して`scanned`を`false`にすることで、再び`<BarCodeScanner/>`を表示することができます。

この処理を入れない場合、QRコード読み取り画面に戻ってきたときに`scanned`が`true`のままなので`<BarCodeScanner/>`が表示されないままになってしまいます。
【webpack】`SassError: Expected newline.` と4時間くらい戦った話【sass】


拡張子にも気を配りたいという話です。

# 何が起きたか
以下のようなエラーが発生し、コンパイルが通らず困りました。
いくら調べても解決策が見つからず、気がつけば何時間も経ってました。

```bash
ERROR in ./src/assets/style.sass (./node_modules/css-loader/dist/cjs.js!./node_modules/sass-loader/dist/cjs.js!./src/assets/style.sass)
Module build failed (from ./node_modules/sass-loader/dist/cjs.js):
SassError: Expected newline.
  ╷
1 │ h1 {
  │    ^
```

読み込ませようとしたスタイルシートは以下の通り。
ただ単に文字色を設定しているだけのもの。
拡張子が`css`であれば全く問題なくコンパイルされます。

```scss:style.sass
h1 {
  color: #222;
}
```

# 原因

＿人人人人人人人人人人人人人人人＿
＞　**拡張子が間違っていた！！！**　＜
￣Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^￣

シンプルに拡張子が誤っていました。
もしくは構文が誤ってた、と言うべきか。
今回、スタイルシートを`style.sass`として作成してしまいましたが、本来は`style.scss`とするべきでした。

もし`style.sass`とするのであれば、構文が変わってきます。

```sass:style.sass
h1
  color: #222
```

# 経緯

create-react-app ではなく webpack を使用して react のプロジェクトを構築していました。
webpack 公式を参考にして各ローダーを設定してましたが、
`sass`、`scss`に関してはどちらも`sass-loader`を通して読み込みます。
`sass-loader`の`sass`につられて拡張子まで`sass`にしてしまった、というわけです。

https://webpack.js.org/loaders/sass-loader/


## 余談

余談ですが、`sassOptions`の`indentedSyntax`に`false`を指定すればコンパイルが通るようになります。
全く無意味な情報ですが。

```js:webpack.config.js
{
  test: /\.(c|sc|sa)ss$/,
  use: [
    "style-loader",
    "css-loader",
    {
      loader: "sass-loader",
      options: {
        sassOptions: {
          indentedSyntax: false,
        },
      },
    },
  ],
},
```

https://webpack.js.org/loaders/sass-loader/#sassoptions
【個人開発】すべての投票を民主化するサービスを作ったよ【SPAポートフォリオ】


「すべての投票を、民主化したいよ」
どうも、スター宮☆みゆきです😊

今回はポートフォリオ用に**匿名性投票サービス**を作ったので、その知見を共有する記事をお送りします！

## 対象読者

- 駆け出しエンジニアの方
- SPAポートフォリオの作り方が分からない方
- 個人開発のアイデアが思いつかない方

※本サービスはスクール等には通わず完全独学で作成したので、独学でポートフォリオを作成されている方の役に立てば幸いです。
**ソースコードも全て公開しています。**

## サービス概要

![tobehonest.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208060/5578738a-f7b0-e190-7e54-a7255b1dfcf4.png)

### 「わたし、気になります！」

みんなに聞いてみたいけど聞けない。そんな疑問ってありませんか？

私はあります！

そんな疑問を匿名性の投票により解決するために、このサービスは誕生しました。

サービス名: みんなの投票箱
サービスURL: https://www.democratic-voting.com/
ソースコード: https://github.com/KamiHitoe/democratic-voting
開発者Twitter: https://twitter.com/starmiya_miyuki
**※現在はサービス停止しております**

### 使い方

おおよその使い方は一般的な掲示板サイトと同じく好きなトピックに対して投票、コメントができるようになっています。
投票、コメントをするにはユーザー登録が必要なので、ページ右上のハンバーガーメニューからログイン、ユーザー情報の登録を行ってください。
ユーザー情報を登録してもらえば、あとはご自由に投票、コメントすることが可能です！
※投票は一つのお題に対して一人一票制となっております。

利用イメージは以下のように、お題を選んでもらって、そのお題に対する投票数の確認等ができるようになっています。

![democratic-voting.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208060/0d291720-35c5-2bd6-9236-bbc9b7b7c0aa.gif)

また、本サービスでは性別や年代で限定してお題を作成、投票数の確認をすることができます。
なので、任意のお題に対して男性だけに絞った投票数を確認できたり、20代だけに投票、コメントを許可するお題を作成したりすることも可能です！
この機能によって、ある程度望まないコメント欄での争いを回避することができる上に、ひいては世界平和が実現するだろうと考えています😇

## 使用技術

- Frontend
  - Lang: JavaScript/TypeScript
  - FW: Nuxt.js
  - UI library: Vuetify
  - other: ESLint, Chart.js, Fuse.js
- Backend
  - Lang: Ruby: 2.7.5
  - FW: Rails 6.0.3
  - other: RSpec, Rubocop
- Infra
  - AWS(ECS, ECR, RDS, ALB)
  - DB: PostgreSQL 12.7
  - container: Docker
  - CI/CI: CircleCI
  - IaC: Terraform
  - Auth: Firebase Auth
  - Analytics: Google Analytics

## インフラ構成図

![architecture.prod.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208060/de5382d4-932d-9756-1e0a-3f13a03520b6.png)

ECSコンテナをALBでロードバランスさせる構成になっています。
FrontendとBackendは同一のドメインを用いていますが、ALBのPathルーティングによってFowarding先を決定しています。
画像はS3のバケットに格納するようにして、認証はFirebase Authに投げています。
また、CircleCIで一部自動ビルド、デプロイ。
AWSリソースはすべてTerraformでコード化しています。

## ER図

![ERD.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208060/496834c7-de28-5a90-fd39-463746387c86.png)

トピックとコメントテーブルをメインにいいねや投票、通報に関するテーブルを作成しています。

## 機能群

- トピック生成機能
- トピック検索機能
  - キーワード検索
  - カテゴリ検索
- カテゴリ機能
  - カテゴリ一覧表示機能
- コメント機能
  - コメント機能
  - いいね機能
  - 返信機能
  - ページネーション機能
- 通報機能
- 画像投稿機能
- Twitter連携機能
- ゾーニング機能
  - 性別・年代集計機能
  - 性別・年代限定機能
- グラフ可視化機能

## 開発ツール

IDE: [Visual Studio Code](https://code.visualstudio.com/)
プロジェクト管理: [GitHubのProjects](https://github.com/)
デザイン・作図: [Miro](https://miro.com/ja/)
開発時間管理: [KanbanFlow](https://kanbanflow.com/)

## リリースまでの開発工程

本サービスのベータ版リリースまでに**約65日間**の開発日数を要しました。
開発の間にGitHubでCloseしたIssueは**47個**ありました。
開発の工数はバランスがよくFrontend, Backend, Infraすべてにおいて3週間程度で開発していました。
本サービス開発過程の様子をすべてTwitterで公開しているので、ポートフォリオの制作過程を知りたい方は是非以下からご確認ください！

[開発の様子](https://twitter.com/starmiya_miyuki)

1/1/2022 - 3/7/2022まで毎日開発の進捗を記録しています😎

## さいごに

使って頂けた感想や不具合報告、ご要望ご質問等ドシドシお待ちしております！😊😊
開発者Twitter: https://twitter.com/starmiya_miyuki
【Solana, TypeScript】base58エンコードされたstring型アドレスをweb3.Publickey型に変換


# 動作確認環境
- M1 Mac (macOS Monterey 12.3)
- @solana/web3.js : 1.36.0

# コード

```typescript
import * as web3 from '@solana/web3.js';

const address = '3ME9Uv8NaWG5cwPnTtoSxzy9haaFAxKcVjpCLRUiDyod'; // string
const pubkey = new web3.PublicKey(address); // web3.PublicKey
```
CloudFunctionsでFirebaseStorageのフォルダーを削除

# CloudFunctionsでFirebaseStorageのフォルダーを削除
posts/{uid}/hoge.mp4
userImages/{uid}/pfp.jpeg
postImages/{uid}/postImage.png
があるとする
``` index.js
const bucket = admin.storage().bucket();
const uid = 'hogehoge';
// delete post storage
bucket.deleteFiles({
    prefix: `posts/${uid}`
});
// delete userImage storage
bucket.deleteFiles({
    prefix: `userImages/${uid}`
});
// delete postImages storage
bucket.deleteFiles({
    prefix: `postImages/${uid}`
});

```
SubRip(.srt)ファイル形式の時間文字列をPythonのtimedeltaから生成する

Pythonを使ってSubRipファイルを作成する際につまづいた箇所があったので記事にします｡

SubRipファイル(拡張子`.srt`)は動画の字幕に使われるファイルです｡
Youtubeの字幕でも使われています｡[^1]

[^1]:Youtubeヘルプ [サポートされる字幕ファイル](https://support.google.com/youtube/answer/2734698)

SubRipファイルは以下のような形式で記述する必要があります｡[^2]

[^2]:引用元: [SubRip - Wikipedia](https://en.wikipedia.org/wiki/SubRip#SubRip_text_file_format)
~~~
1
00:02:16,612 --> 00:02:19,376
Senator, we're making
our final approach into Coruscant.

2
00:02:19,482 --> 00:02:21,609
Very good, Lieutenant.

~~~

手元に各音源の再生時間と字幕のテキストがあれば機械的に生成できそうです｡[^4]

[^4]: 各音源と字幕が一対一対応すれば､ですが｡筆者はVOICEPEAKを使っていたのでこのニーズが発生しました｡

しかしこの`00:02:16,612`(時間:分:秒,ミリ秒)という形式の時間文字列をPythonでどう作るかでつまづきました｡

Pythonで時間の計算といえば`datetime.timedelta`だろうと思ったのですが､
単純な`str(timedleta)`ではミリ秒以下の表記ができません｡

また､`timedelta.microseconds`だと値が整数のため桁の扱いが厄介でした｡
整数を上から3桁目で丸めて4桁目以下を除外するということ…？ 桁数が変動する場合は…？

というわけでミリ秒以下も考慮して必要な時間の文字列をつくる関数を作りました｡


~~~python
from datetime import timedelta

def format_srttime(timedelta: timedelta) -> str:
    """timedeltaをSRT形式の時間表示にフォーマットする"""
    ss, mi = divmod(timedelta.total_seconds(), 1)
    mi = int(round(mi, 3) * 1000)
    mm, ss = divmod(ss, 60)
    hh, mm = divmod(mm, 60)
    srttime = f'{int(hh):02}:{int(mm):02}:{int(ss):02},{mi:03}'
    return srttime
~~~

`timdedelta.total_secondes()`を使って小数点以下を含んだ秒数を取得しています｡

`divmod`は割り算の商と余りを計算してくれます｡
取得した秒数を`1`で割ることで､整数部分(秒)`ss`と小数部分(マイクロ秒)`mi`に分離しています｡
同じような計算で秒`ss`を60で割って分`mm`､分を60で割って時間`hh`を計算しています｡[^3]

マイクロ秒は小数なので`round()`で丸めてから1000倍して無理やり整数にしています｡
こうすることで整数値に対する桁数処理の問題を回避しています｡

[^3]: 公式の計算を倣いました｡ 参考: [cpython/Lib/datetime.py](https://github.com/python/cpython/blob/e63894b3eed8ad2dd7690695f7f07bfbff59c05a/Lib/datetime.py#L576)

あとはその結果をf文字列に入れて桁数が揃うように`:02`などで0埋めの指定をします｡
あわせて､f文字列内で`float`になっている変数を`int`に揃えています｡

そんな感じで`00:02:16,612`のような形式で時間の文字列を生成できるようになりました｡
これで字幕作成が捗ります｡

実際､筆者はVOICEPEAK[^5]から音声ファイルと台詞のテキストファイルを出力して､
(再生時間の計算などしつつ)自動でSubRipファイルを作成できるようになりました｡
[^5]:[VOICEPEAK 公式](https://www.ah-soft.com/voice/6nare/#top)

**VOICEPEAK(音声化) → Python(字幕作成) → Premiere Pro(動画作成) → Youtube(動画公開)**

という流れで動画をつくれるようになったので､その話も機運が高まったらまた書きます｡
G検定の試験対策(主に試験当日の対策)

　この度、2022年 #1のG検定になんとか合格しました。私が行った試験対策をまとめましたので、ご活用いただければと思います。

## 試験当日までにやるべきこと
1. 模擬試験を行い、自分の実力・苦手分野を知る。(無料模試がネットに転がってます。)
2. カンペを作る。親切な方が作っているものを使用してもよいですが、自分の苦手分野がカバーされているとは限らないので、時間があるなら自作しましょう。また、カンペは素早く検索できるようにエクセル等で作成をオススメします。
3. カンペに頼らずに答えられる部分を増やすために、書籍等で勉強する。書籍を買う場合は、(本屋で中を確認してから)2,3冊買ったほうが良いと思います。試験範囲が広く1冊だけではカバーできない可能性が高いです。

　基本的には これだけですが、模試は色々なサイトのものを使ってみると実力がちゃんが分かります。

## 試験当日の準備
1. 1～200を書いた表を印刷する。(以降、チェック表と呼称)
2. カンペを用意する。

## 試験中の動き
1. 解ける設問から解く。
2. 解けない設問は一旦パスする。このとき、チェック表に印(後述)をつける。
3. 計算問題などの時間がかかる設問は後回し。(2～4問ほどなので点数への影響は少ない。)
4. 一通り解き終わったら、チェック表を基に解けていない設問をカンペ or 検索しながら解く。(解く順番は後述)

### チェック表につける印について

　解けていない設問を把握するだけなら、試験ページの左上にある★マークだけでよいですが、「おそらく合っているけど少し不安。」「ちょっと調べたら分かりそう。」「検索に時間がかかりそう。」など、『解けていない』にも様々な理由があります。
　そのため、チェック表を別途作成することをオススメしています。
　また、正解したと思っている設問にもチェックすることをオススメします。それは、チェックし忘れたらズレにズレて使い物にならないため、可能な限りズレることを防ぐためです。
　以下に私が作成したチェック表の凡例を記載します。参考までにどうぞ。

|凡例|凡例の意味|
|:--:|:--:|
|✓|ほぼ間違いなく正解している。|
|A|多分合ってるけど、少し心配。|
|B|分からないけど、少し調べたら分かりそう。|
|C|全然分からない。調べるのにも時間がかかりそう。|

## チェック表を用いた解く順番について
　当然ですが、時間のかからない設問から調べる必要があります。ですので、上記の凡例を用いる場合は、B→Cの順に検索することになります。Aについては、時間が余ったら調べる程度になるかと思われます。

## 最後に
　模試や勉強をしていると「意外と楽勝なんじゃね？？？」と思われるかもしれませんが、体感で模試の1.3倍くらい難しかったです。理由としては、以下の2点かなと思います。
- 模試より設問及び、選択肢の文章が長く、単純に時間が食われる。
- 検索に時間のかかりそうな設問が意外と多い。

　模試では200問を解いて、一通り解き終わるのに60分、検索に30分かかりました。ですが、本番では191問を解いて、一通り解き終わるのに80分、検索に40分かかり最後の1秒まで使い切りました。
(正直、模試が余裕になった時点で少し甘く見てました。)


私が行った試験対策は以上になります。これからG検定を受験される方の一助になればと思います。
[JavaScript/node/Bat] 特定フォルダ下位にある画像ファイルをランダムで開く

Windowsです。

BingWallPaperナントカってアプリをいれて、Bingの背景画像を壁紙にしてくれて日々、きれいな壁紙が切り替わるようにしています。

が、5,6枚くらいしか前画像をたどれなくて、ときどきBingが虫とか植物の拡大写真や動物の変な写真がアップされて気に食わなかったりするときがありまう。俺は生物系ではなく風景の写真が壁紙になってほしいのです。生物系はぎょっとしたりキモかったりするときもあるので、壁紙にしたくない。(今日とかカエルだし、人を選ぶだろ、カエル画像とかさあ)

で、壁紙ファイルは次の場所に保存されていることがわかり、昔みた壁紙ファイルも保持されているからよかったのです。

C:\Users\<ユーザー名>\AppData\Local\Microsoft\BingWallpaperApp\WPImages
C:\Users\<ユーザー>\OneDrive\画像\Bing Images

で、この中でお気に入りな画像を特定のフォルダに保存しています。

画像ファイラは、MassiGra使っているので、画像をみて気に入ったものがあれば壁紙に簡単にできるのですが、今回はこれをランダムで選びたいなと思いました。

## コード

```js:RandomOpenFile.js.bat
//&cls&node %0 %1&exit

// -----

// Main
var childProcess = require('child_process');
const fs = require('fs');
const path = require('path');

const readSubDirSync = (folderPath) => {
  let result = [];
  const readTopDirSync = ((folderPath) => {
    let items = fs.readdirSync(folderPath);
    items = items.map((itemName) => {
      return path.join(folderPath, itemName);
    });
    items.forEach((itemPath) => {
      if (fs.statSync(itemPath).isDirectory()) {
        readTopDirSync(itemPath);
      } else {
        result.push(itemPath);
      }
    });
  });
  readTopDirSync(folderPath);
  return result;
};

const _randomInt = (min, max) => {
  return Math.floor(Math.random() * (max + 1 - min)) + min;
};

// Batファイルに引数が渡ってくるときだけ
// フォルダ列挙して下位ファイルをランダムで開く
const argsArray = process.argv.toString().split(',');
if (argsArray.length === 3) {
  const folderPath = argsArray[2];

  let items = readSubDirSync(folderPath);
  items = items.filter(item => {
    const filename = item.toLowerCase();
    return (
      filename.endsWith('.jpg') ? true
      : filename.endsWith('.png') ? true
      : false
    )
  })
  const openFileIndex = _randomInt(0, items.length - 1);
  childProcess.exec(`${items[openFileIndex]}`);
}

setTimeout(() => {
  process.exit()
}, 100);
```
上記のコードだと、引数でパス渡さないと動かなくて、.lnkファイルを編集してフォルダパスを引数で渡してもいいのですがやや不便なので、同じフォルダにこのbatを呼び出す別batを定義します。

```bat:RandomOpenFile_壁紙.bat
call RandomOpenFile.js.bat "C:\..\...\...\壁紙"
```

これで、RandomOpenFile_壁紙.bat を呼び出すと、そのフォルダ下位にあるjpgかpngファイルをランダムに開きます。jpgやpng は MassiGra に関連づいているので、そこから壁紙にすることができます。

よかった。ちょっと便利。

壁紙はランダムにその日の気分とかで選びたいこともあったりなかったり。

## 参考

  Batファイルとして Node.js の JavaScript ファイルを動かす方法 - Qiita
  https://qiita.com/standard-software/items/51ca6f721017a07ca010

  node.js ファイル一覧の取得- Qiita
  https://qiita.com/standard-software/items/e37bfd1db0f5ada192cf
ec2を使ってdjangoのサイトを公開する

https://qiita.com/Bashi50/items/d5bc47eeb9668304aaa2

https://qiita.com/tachibanayu24/items/b8d73cdfd4cbd42c5b1d

これらの記事を参考にしました。

バージョンのライブラリをローカル環境と同じにすること、EC2の画面でのインバウンドルール辺りの知識
ログの話

開発手法に関しては話題が多い。
テストの手法に関してもテスティングフレームワークがあったりする。

ただそれはすべて開発フェーズの話であって本番サービスが始まってからの品質管理は丹念にログをみるしかない。

デバッグに関しては「プリントデバッグからの開放」なんてことがあるけど、ログに関してはプリントデバッグの延長で十分なケースも多い。

ログに関しては絶対王者の方式があるわけではないので、だましだましやってる。

## ログのため方あれこれ

### 標準出力に垂れ流し

これもあり。開発時のプリントデバッグと本番のロギングというのが同じ仕組み。何よりもストレージを使わない。今どきはtmuxのような仮想ターミナルを使えば、バックグラウンドで流していても大丈夫。開発では標準出力だけど本番環境では別のロギングツールにリダイレクトということもできる。

標準出力と言ってるけど、標準エラー出力もあり。どうせプログラムが例外吐くときには最終的には標準エラー出力になにか吐かれる。

結論：まずは標準出力ってのはあり

### データベースに書き出し

データベースに書き出しというのも現場ではよくある。ただし大半のRDBはデータの削除が重い。本来ログを貯めるのには向かない。したがってデータベースにログのようなものを書き出す際には、パーティショニングとセットで考える。パーティショニングを使えば、物理ファイルの削除でレコードの削除ができるため処理が軽い。

またデータベースに保存するのはどちらかと言うとinfoログであって、データベース接続のエラーがあった際のエラーログはデータベースには吐けない。

結論：複数マシンのログをリアルタイムに集めたければデータベースもあり。ただし古いデータのみをTruncateできる仕組みとセットで。

### syslogdで書き出す

syslogdを使うという手もある。syslogdのいいところはログサーバに転送することができるということ。ログサーバはUDPで受け付けることができるので、最悪ログサーバーが止まっていてもログを吐く側のプログラムが止まるということはない。Fluentdのようなログアグリゲーターも同じことはできる。Fluentdの場合はプラグインが充実してるので、BigQueryやS3にデータを置くこともできる。

ただいずれにしても、別途ミドルウェアが必要になり、開発環境と本番環境で構成が違いすぎるというのが難点。ロガーの出力先の切り替えを開発環境と本番環境でちゃんと切り替えておければOK。

結論：ログアグリゲーターを使うなら、本番と開発で切り替えれるように作ろう

### 単にファイルに書き出す

大半のツールがこれ。ただし複数サーバーで運用していると、ファイルに書き出されたログを集めるに一手間かかる。一日一回とか集めるのならいいんだけど、リアルタイムに今どんなことがおきてるかを観たいときには難しい。

またファイルに書き出す場合にはログローテーションも必要になる。どうしても後回しにしがちなのでログローテーションを後回しにするくらいなら、標準出力垂れ流しの方がまだ害がない。

## ログをみるサイクルを決めよう

ログをとるならログをみるサイクルを決めよう。

### 正常系と異常系の両方をみよう

なにか不具合があったときに追いかけるとかじゃぁなくて、平時から定期的のログをみよう。「便りがないのは良い知らせ」ではなく、まともに動いてる様をログから確認して初めてシステムは動いているといえる。

よくある現場ではエラーがなければセーフというみかたになりがちだけどそうではない。正常／異常 比率をみて初めて健康といえる。

### 利用されている機能の分布を見よう

リリースした機能のうち「使われない」機能があったならそれも一つの被害。使ってほしい機能が使われてないのは設計バグに近い。システムには「理想的な使われ方」があるはずで、その分布の通りになってないなら、問題があるということ。
突然docker-composeが使えなくなった

## 起きたこと
`docker-compose up` を実行したところ、以下エラーが発生してdocker-composeを使用できなくなった

```bash
$ docker-compose up -d
ERROR: The Compose file is invalid because:
Service app has neither an image nor a build context specified. At least one must be provided.
```
ちなみに前提として
- この事象が起きる前日までは同じコマンドで問題なく実行できてた。
- docker-compose.yml及びDockerfileに変更入れていない。

となっています。

### 環境
- Macbook Pro 2018 2.3 GHz クアッドコアIntel Core i5
- mac OS Monterey 12.2.1
- Docker Desktop for Mac 4.6.0

```エラーとなったdocker-compose.yml
version: "3"
services:
  app:
    build:
      dockerfile: ./Dockerfile
    container_name: app-container
    tty: true  
    volumes: 
      - .:/go/src/app
    ports: 
      - "3000:3000"
    depends_on: 
      - db
    environment:
      - "DB_USER=root"
      - "DB_PASSWORD=password"
      - "DB_NAME=todo_db"
      - "DB_HOSTNAME=db"
      - "DB_PORT=3306"
  db:
    image: mysql:5.7
    container_name: db-container
    ports:
       - "3306:3306"
    command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
    volumes:
       - "./mysql/db-data/:/var/lib/mysql"
       - "./mysql/my.cnf:/etc/mysql/conf.d/my.cnf"
    environment:
       MYSQL_ROOT_PASSWORD: password
       MYSQL_DATABASE: todo_db

```


## 解決までに試したこと
- `Service app has neither an image nor a build context specified` と出ているので、appサービスにimageやbuildを追加
- ファイルのインデントを揃える
- 全角スペースが紛れ込んでいないか調査

## 解決手段
`docker-compose` ではなく `docker compose` に切り替えたら解決しました。

`docker compose` はどうやらDocker Desktop3.4.0から入っている Compose V2コマンドのようです。
参考：https://docs.docker.com/compose/cli-command/
特に何も考えずにDocker for Desktopのアップデート行っていたので、何か入ったのかも

なぜこのような事象が起きているかは今のところ分かりません。
分かり次第追記すると思います。
RustにはGoto文がない

* Goto文を使うのは良くないけど、多重ループを抜けるのに、Goto文は便利だと思う
* RustにはGoto文がないので、代わりに`break`や`continue`を使う

## Rust入門書に載っていたサンプルコード

``` rust
fn main() {
    'looptop: for i in 0..4 {
        for j in 0..4 {
            if i == 1 && j== 2 {
                break 'looptop; // ここは、セミコロン`;`
            }
            println!("{} {}", i, j);
        }
    } 
}
```

実行結果

``` shell
0 0
0 1
0 2
0 3 // Rust入門書には、この行が書かれていなかった
1 0
1 1
```

## ポイントの整理

* Rustの繰り返し処理は3つ
    1. for文
    1. while文
    1. loop文
* 繰り返し処理の行頭にラベル名を書き、breakでそこにジャンプさせる
* ラベル名の先頭には単一引用符`'`が、末尾にはコロン`:`が必要
* 通常、breakの末尾にセミコロン`;`は要らないが、ラベルを書く場合は必要

## 参考

* [やさしいRust入門 日向俊二 | p.87 第4章 実行制御 | 4.2 繰り返し](https://www.cutt.co.jp/book/978-4-87783-498-2.html)
* [ネストとラベル | Rust By Example 日本語版](https://doc.rust-jp.rs/rust-by-example-ja/flow_control/loop/nested.html)
Discord.js+Expressでグローバル変数っぽい挙動をしたいメモ


Discord.js+expressでサーバーを立ててますが、ファイル分割をしている末端のファイル`hoge.js`などのなかでDiscord.jsのClientを呼び出したいというニッチな話

```
main.js
|- actions
|- router
    |- hoge.js
```

main.jsで`client = new Client()`をしていますが、末端でhoge.jsではclientオブジェクトが定義されてなく、`client.channels.cache.get().send()`を使いたいけど使えない、みたいな状態でした。

https://qiita.com/n0bisuke/items/cc4deef076ecf86421f5


main.jsで定義したclient変数をhoge.jsに持っていきたいなというところでしたが、公式ドキュメントとすぎゃーんさんの記事が参考になりました。

> [Express アプリケーションで使用するミドルウェアの作成](https://expressjs.com/ja/guide/writing-middleware.html)
> [expressでapp全体の設定値をroutesで使う(module間で変数を受け渡す)方法いろいろ](https://memo.sugyan.com/entry/20120110/1326197416)


## 結論: ミドルウェア関数とreqを利用

色々なやり方ある模様ですが、一旦このやり方で落ち着きました。

main.js内で[ミドルウェア関数](https://expressjs.com/ja/guide/writing-middleware.html)を作成し、`req.discordClient`を定義してclientを可能しておきます。

```main.js
//省略

const client = new Client(); //(色々端折っているけど)

const myConfig = function (req, res, next) {
    req.discordClient = client;
    next();
}

app.use(myConfig);

//省略
```

これで他の箇所で呼び出すreqオブジェクトでreq.discordClientが利用できるようになりました。

```hoge.js
module.exports = async (req, res) => {

	// Discord BotのClientオブジェクト
	const client = req.discordClient;
	`client.channels.cache.get(<discord_room_id>).send('ほげほげ');`
}
```

終わってみるとあっけないですが歴史がある分色々なやり方が紹介されていてなかなか調べにくかったですね......
Macでドライブを32bitにフォーマットする方法 ( Alpineをラズパイにインストールしたい ) 

なんとなく　Alpine Linux をインストールしたい気分になったので、インストールしてみました。
そこでハマったところ ( 32bitのフォーマット ) があったので、シェアしておきます。

# 使用マシン

 - Raspberry Pi 3 Model B　
 - Mac ( macOS12 )
   ↑ フォーマット用

# 手順

普通に、MicroSDカードをGUIで（ディスクユーティリティで）フォーマットして、Alpine のブートファイルを書き込んでいました。
書き込んだMicroSDカードを挿してラズパイを立ち上げました。
ラズパイを立ち上げたところ、初期設定の画面が出てくると思ったら、なんと虹色の画面がでてきました。

![raspberry-pi-boot-rainbow.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1868844/3bc3230f-9073-3a55-e1cc-a2f8fc8d061c.jpeg)

# ハマったところ

事前にCUPの情報を調べていましたが、ARMv7、つまり32bitでした。 ( 下のファイルで確認 )
フォーマットのときに、それを気にしていなかったです。
armv7用のダウンロードを落としてきていましたが、SDカードを64bit用にフォーマットしていたので、立ち上がりませんでした。( これが原因 )

```:ラズパイの/proc/cpuinfo
processor       : 0
model name      : ARMv7 Processor rev 4 (v7l)
BogoMIPS        : 38.40
Features        : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt 
vfpd32 lpae evtstrm crc32 
CPU implementer : 0x41
CPU architecture: 7
CPU variant     : 0x0
CPU part        : 0xd03
CPU revision    : 4
　　　　　　　　　　・
　　　　　　　　　　・
　　　　　　　　　　・
　　　　　　　　　　・
```

# 解決策

GUIからやっていたが、32bitのフォーマットがいまいちわからなかったので、コマンドで実行することにしました。

以下の手順で、SSD、HDD、SDカード、USBメモリ、その他、、、を32bit用にフォーマットできます。
ちなみに実行環境はMacです。

まずドライブのパスを調べます。ターミナルで以下のコマンドを実行します。

```:パスの調査
diskutil list
```

私の環境では、MicroSDカードが`/dev/disk4`だったので、それで進めますが、それぞれの環境に応じてパスを置き換えてください。

次のコマンドでFAT32にフォーマットできます。

```
sudo diskutil eraseDisk FAT32 "THE_NAME" MBRFormat /dev/disk4
```

ちなみに`diskutil`のmanページを覗いてみるとこんな感じでした。
>eraseDisk format name [APM[Format] | MBR[Format] | GPT[Format]] device 
>
>Erase an existing disk, removing all volumes and writing out a new partitioning scheme containing one new empty file system volume.  If the partitioning scheme is not specified, then an appropriate one for the current machine is chosen.  Format is discussed below in the section for the partitionDisk verb. Ownership of the affected disk is required.
>>既存のディスクを消去し、すべてのボリュームを削除して、1つの新しい空のファイルシステムボリュームを含む新しいパーティションスキームを書き出します。パーティションスキームが指定されていない場合は、現在のマシンに適切なスキームが選択されます。フォーマットについては、以下のpartitionDisk動詞のセクションで説明します。影響を受けるディスクの所有権が必要です。
>
>(partitionDiskのところの一部分)
>MBR[Format] specifies that a Master Boot Record scheme should be used.  This is the DOS/Windows-compatible partitioning scheme. 
>>MBR[Format]は、マスターブートレコードスキームを使用する必要があることを指定します。これは、DOS/Windows互換のパーティションスキームです。

なんにせよ、コマンド一発でできて簡単ですね。
GUIアプリを使ったのが間違いでした。

# Alpineを焼く

[Alpineのダウンロードページ](https://alpinelinux.org/downloads/)から、armv7用のファイルをダウンロードします。
私はラズパイ用の[こちらのv3.15](https://dl-cdn.alpinelinux.org/alpine/v3.15/releases/armv7/alpine-rpi-3.15.1-armv7.tar.gz)をダウンロードしました。

ダウンロードしたファイルを解凍、展開して、先ほどフォーマットしたMicroSDカードに書き込みます。

![alpin-boot.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1868844/82d7941a-7fae-7a01-8f79-8173c2cf4ea8.png)

# 最後に

あとはラズパイにブッ挿して起動するだけです。
TCPソケットとUDPソケットが同一のポートにbindした場合、クライアントからのメッセージはどちらが受信するのか？


試してみた。
結論は最後のまとめに書いてある。

## Serverを立てる


```rb
require 'socket'

### tcp1
tcp_socket = Socket.new(:INET, :STREAM)
tcp_socket.bind Socket.sockaddr_in(9002, '<any>')
tcp_socket.listen 5

### tcp2 (エラーでて作れなかった)
tcp_socket = Socket.new(:INET, :STREAM)
tcp_socket.bind Socket.sockaddr_in(9002, '<any>')
tcp_socket.listen 5
#=> Errno::EADDRINUSE: Address already in use - bind(2) for 0.0.0.0:9002

### udp1
udp_socket = Socket.new(:INET, :DGRAM)
udp_socket.bind Socket.sockaddr_in(9002, '<any>')

### udp2 (エラーでて作れなかった)
udp_socket2 = Socket.new(:INET, :DGRAM)
udp_socket2.bind Socket.sockaddr_in(9002, '<any>')
#=> Errno::EADDRINUSE: Address already in use - bind(2) for 0.0.0.0:9002


loop do
  res = IO.select([tcp_socket, udp_socket])
  res.first.each do |sock|
    case sock
    when udp_socket
      p '### data received in udp_socket'
      p sock.recvfrom(1024)
    when tcp_socket
      p '### data received in tcp_socket'
      client, from = sock.accept
      data = client.recv(1024)
      p data
      client.puts 'OK'
      client.close
    end
  end
end

```

## Clientからメッセージを送信する

```rb

require 'socket'


Socket.open(:INET, :DGRAM) do |sock|
  addr = Socket.sockaddr_in(9002, '127.0.0.1')
  sock.send('hello udp message', 0, addr)
end

Socket.open(:INET, :STREAM) do |sock|
  addr = Socket.sockaddr_in(9002, '127.0.0.1')
  sock.connect addr
  sock.write 'hello tcp message'
  p sock.gets
end
```


## Server側のstdoutは、、、

```
"### data received in tcp_socket"
"hello tcp message"


"### data received in udp_socket"
["hello udp message", #<Addrinfo: 127.0.0.1:60734 UDP>]
```


## まとめ

- 1つのポートにバインドできるのは、tcpソケット,udpソケットのそれぞれで最高１つまで。
- tcpソケットとudpソケットの両方が同一ポートにバインドされている場合、
   - tcpクライアントからのメッセージはtcpソケットが受け取る。
   - udpクライアントからのメッセージはudpソケットが受け取る。


## Ref

https://qiita.com/kudojp/items/d416d4c97f9d0512c088
【小ネタ】double click と single click で別のメソッドを発火させる

# 概要
onClick→single click
onDoubleClick→double click
としてoptionがあるのですが、両方を設定すると常にシングルクリックになってしまう課題の解消。

### 追記(2022/8/3)

コメント欄にさらにより良いコードが載っているので、活用される場合はそちらもご確認くださいmm

## version
![Screen Shot 2022-03-21 at 16.32.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/534071/1068c59d-b952-aab6-95f1-06bc4a6a00ae.png)

## やりたいこと
- 一つの要素でダブルクリックとシングルクリック(普通の左クリック)で別の判定をしたい
例えばECサイトなどで、
シングルクリック→商品を選択状態に(水色の枠でカードを囲むなど)
ダブルクリック→商品詳細ページに遷移
といったことを実装したい時のお話。

- Reactで実装
単にJavaScriptのお話なのですが、React触っているときに出たお話なので。
調べるとクラス構文のものは見つけましたが、関数コンポーネントのものは見当たらなかったので。

## 実際のコード

```ts
export default function App() {
  const singleClick = () => {
    alert("single click");
    console.log("single click");
  };
  const doubleClick = () => {
    alert("double click");
    console.log("double click");
  };

  let clickCount = 0;
  const handleSingleOrDoubleClick = () => {
    clickCount++;
    if (clickCount < 2) {
      setTimeout(() => {
        if (1 < clickCount) {
          doubleClick();
        } else {
          singleClick();
        }
        clickCount = 0;
      }, 200);
    }
  };

  return (
    <div className="App">
      <div onClick={singleClick}>single</div>
      <div onDoubleClick={doubleClick}>double</div>
      <div onClick={singleClick} onDoubleClick={doubleClick}>
        NG
      </div>
      <div onClick={() => handleSingleOrDoubleClick()}>click here</div>
    </div>
  );
}

```

https://codesandbox.io/s/singleordoubleclick-lv03qj?file=/src/App.js:0-826

## 参考にさせて頂いた記事

https://naughtldy.hatenablog.jp/entry/2018/02/27/160000
PHPの文字列検索でSQLの”LIKE”的に使える関数を作ってみた

# コピペ用関数

こちらをコピーして使ってください。

```php: functions.php
function strlike(string $haystack, string $needle): bool
{
    //
    $initial_is_percent = false;
    $final_is_percent = false;

    //
    if (substr($needle, 0, 1) === '%') {
        $initial_is_percent = true;
    } else {
        //no action.
    }

    //
    if (substr($needle, -1) === '%') {
        $final_is_percent = true;
    } else {
        //no action.
    }

    //
    $matched = false;
    if (
        $initial_is_percent === true &&
        $final_is_percent === true
    ) { //部分一致！
        $needle = substr($needle, 1); //最初の%を切る
        $needle = substr($needle, 0, -1); //最後の%を切る
        if (str_contains($haystack, $needle) === true) {
            $matched = true;
        } else {
            //no action.
        }
    } else if (
        $initial_is_percent === false &&
        $final_is_percent === true
    ) { //前方一致！
        $needle = substr($needle, 0, -1); //最後の%を切る
        if (str_starts_with($haystack, $needle) === true) {
            $matched = true;
        } else {
            //no action.
        }
    } else if (
        $initial_is_percent === true &&
        $final_is_percent === false
    ) { //後方一致！
        $needle = substr($needle, 1); //最初の%を切る
        if (str_ends_with($haystack, $needle) === true) {
            $matched = true;
        } else {
            //no action.
        }
    } else {
        if ($needle === $haystack) {
            $matched = true;
        } else {
            //no action.
        }
    }

    return $matched;
}

```

# 使い方

LIKE句のように「%」を組み合わせることで部分一致や前方一致が確認できます。

```php
//部分一致
strlike('abcdefg', '%cde%'); // -> true
strlike('abcdefg', '%ce%'); // -> false

//前方一致
strlike('abcdefg', 'abc%'); // -> true
strlike('abcdefg', '%abc'); // -> false

//後方一致
strlike('abcdefg', '%efg'); // -> true
strlike('abcdefg', 'defg%'); // -> false

//完全一致
strlike('abcdefg', 'abcdefg'); // -> true
strlike('abcdefg', 'abcd'); // -> false
```