OmniAICreator/Qiita-1.07M huggingface dataset:
StringJoinerの機能を僕はまだ知らない

SQLをJavaアプリ側で動的に組み立てる処理を書いていて、StringJoinerがなかなか便利だな、と感じました。
これまでは引数が一つのコンストラクタを使い、愚直にaddしていく使い道しか知らなかったのですが、引数3つのコンストラクタさんと`merge`メソッドさんつよつよ。~~`Stream`の`collect(Collectors.joining("")と同じでは~~

## わたしが今までしていた単純な使い方

```java
StringJoiner sj = new StringJoiner(",");
sj.add("a");
sj.add("b");
sj.add("c");
assertThat(sj.toString(), is("a,b,c");
```
てな具合で、文字列をくっつけることができますね。
ループでくっつけるようなことをすると、末尾の","に関して制御をしなければならないので、それに比べたら楽だしわかりやすい。

Streamで書き直すと以下のような感じ。

```java
Stream.of("a", "b", "c").collect(Collectors.joining(","));
```

こっちの方が断然シンプルだ。
ただ、引数の数が多いとか、パターンに応じて値を変えたりなど複雑になってくると、Collectionと関数型インターフェース、`map`と`filter`でなどを組みあわせて実現しないと可読性が下がりますね。

## コンストラクタにprefixとsuffix的なのを渡せる

```java
StringJoiner sj = new StringJoiner(",", "[", "]");
sj.add("a");
sj.add("b");
sj.add("c");
assertThat(sj.toString(), is("[a,b,c]");
```

わー便利。
当然、空文字指定も可能なので

```java
StringJoiner sj = new StringJoiner(",", "SELECT ", "");
sj.add("column1");
sj.add("column2");
assertThat(sj.toString(), is("SELECT column1,column2");
```
このようなこともできます。すごい。

## mergeメソッドでくっつける

`merge`メソッドは二つのStringJoinerを合成します。
合成するといっても、引数で渡したStringJoinerをprefix、suffixを無視してtoString()したものを呼び出した側に一つの要素として使いするイメージです。
同じ区切り文字だと複数回`add`を行ったのと同じ結果になります。

```java
         StringJoiner sj1 = new StringJoiner(" ");
         sj1.add("table_name");
         sj1.add("on");
         StringJoiner sj2 = new StringJoiner(" AND ");
         sj2.add("column1 = column2");
         sj2.add("column3 = column4");
         sj2.add("column5 = column6");
         assertThat(sj1.merge(sj2).toString(), is("table_name on column1 = column2 AND column3 = column4 AND column5 = column6"));
```

先述の通り、引数で渡した側のprefix、suffixは無視されるのでそこは注意ですね。

```java
         StringJoiner sj1 = new StringJoiner(",", "[" , "]");
         sj1.add("hoge");
         sj1.add("moge");
         StringJoiner sj2 = new StringJoiner(":", "<", ">");
         sj2.add("foo");
         sj2.add("bar");
         sj2.add("baz");
         StringJoiner merged = sj1.merge(sj2);
         merged.add("the end");
         assertThat(merged.toString(), is("[hoge,moge,foo:bar:baz,the end]"));
```
M1 MacでDockerを使ってUbuntu環境上にMinicondaを導入する時にハマった

## 結論

`FROM` 行に `--platform=linux/amd64` を加える。

```Dockerfile
FROM --platform=linux/amd64 ubuntu:latest
# [以下省略]
```

## 経緯

M1 MacでDockerを使ってUbuntu環境上にMiniconda3をインストールしようとした。

Dockerfileおよびインストール用のシェルスクリプトは以下。

https://github.com/yhilpisch/py4at/tree/master/ch02/Docker

しかしビルド後 `docker run` してもipythonがunknownになった。
コンテナの中に入って確認してみると、/root/miniconda3/にはconda.exeしかなく、binディレクトリほかは生成されていなかった。
再度コンテナを生成する際にログを確認してみると、当然その後のpandasやipythonのインストールにも失敗していたことがわかった。

## 解決

こちらのstackoverflowの記事にたどり着いた。

https://stackoverflow.com/questions/71040681/qemu-x86-64-could-not-open-lib64-ld-linux-x86-64-so-2-no-such-file-or-direc

Miniconda3のインストーラがx86-64とIntelのマイクロプロセッサ向けなので、Dockerコンテナの中のUbuntuがM1 Macと知らずにそのまま展開しようとするのが原因だった。
M1 MacであることをDockerコンテナに教えてあげるには、Dockerfileの `FROM` と　`ubuntu` の間に `--platform=linux/amd64` を入れてあげるとよかった。

```Dockerfile
FROM --platform=linux/amd64 ubuntu:latest
```
HPのPCのWi-Fi強度が弱い問題の解決

## 使用PC
HP ENVY x360 Convertible 13-ay1xxx
WIndows 11

## 現象
同じWi-Fiに同じ場所から接続しているのに他のデバイスに比べて明らかに速度が遅かったり、Wi-Fiに接続しているのに「インターネットなし」となり接続できない問題が購入時から起こっていた。

## 解決法
ネットワークドライバのバージョンを手動で更新した。

### バージョンの確認法
`スタートボタン上で右クリック＞デバイスマネージャー＞ネットワークアダプター＞Realtek RTL8852AE WiFi 6 802.11ax PCIe Adapter＞ドライバー`
確かもともとのバージョンは6001.0.10.323（うろ覚え）

ちなみに、この画面に「ドライバーの更新」というボタンがあるが、`ドライバーの更新＞ドライバーを自動的に検索`としても最新版であるとされ、更新されなかった。

### バージョンの手動更新
以下のサイトの「ドライバーを**ダウンロード**」をクリックし、ダウンロード（インストールは押さない）。
https://jp.drvhub.net/devices/network/realtek/rtl8852ae-wifi-6-802-11ax-pcie-adapter
バージョン6001.0.10.344がダウンロードされる。

ダウンロードしたzipファイルを任意のフォルダに解凍し、
`スタートボタン上で右クリック＞デバイスマネージャー＞ネットワークアダプター＞Realtek RTL8852AE WiFi 6 802.11ax PCIe Adapter＞ドライバー＞ドライバーの更新＞コンピュータを参照してドライバーを検索＞参照＞解凍したフォルダをクリックして「OK」`

以上でインストールされる。
今のところ通信速度が向上し、「インターネットなし」の問題も解決している。
超いまさら感があるけど、アニメ風写真→イラスト変換AIの学習済みモデル

# 画像スタイル変換

画像のスタイル変換と言えば、Neural Image Style Transferで有名になった、かなり昔の技術です。
![result1-a.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/595605/2ccbd6bc-a173-d7f4-c9ea-0333a0975b5d.png)
ゴッホの星月夜スタイルの画像とか有名ですよね。
古い実装ですが、私の[著作](https://www.amazon.co.jp/dp/4863542348/)でも紹介しています。

## Cycle-GANによるスタイル変換

ところが、Neural Image Style Transferは逆伝播によって画像を生成するので、生成の度に異なるスタイル画像を指定可能であるものの、とても遅いという問題があります。
そこで、Cycle-GANで画像の変換タスクを予め学習させておき、そのGeneratorを使ってスタイル変換を行おう、という手法があります。
この手法のメリットは、実行速度が速いことで、例えばスマートフォンアプリなどのモバイルデバイスでも十分に実行可能な速度で画像のスタイル変換を行うことが出来ます。
一方、予めアニメ風ならアニメ風のスタイルを学習させる訳なので、その都度異なるスタイル画像を指定することは出来ません。

![cycle-gan](https://camo.githubusercontent.com/16fa02525bf502bec1aac77a3eb5b96928b0f25d73f7d9dedcc041ba28c38751/68747470733a2f2f6a756e79616e7a2e6769746875622e696f2f4379636c6547414e2f696d616765732f7465617365725f686967685f7265732e6a7067)


# anime-style-transferを持ってきた

さて、Cycle-GANでもってスタイル変換を行うモデルですが、どうせ誰かが学習済みモデルを作成して公開しておいてくれるだろうと思ってみたのですが、GitHubで探してみたけど、以外とありそうで無い・・・
やっと見つけたのが、[こちら](https://github.com/racinmat/anime-style-transfer)のリポジトリです。
学習済みモデルも公開されているし、サンプル（↓）を見る限り、中々よさそう。

![sample](https://camo.githubusercontent.com/96d24e32041d2b5f1d7856d7f8dcd92c4e3e652a71d9a7c52d3c46bae464f5d7/68747470733a2f2f7261772e6769746875622e636f6d2f726163696e6d61742f616e696d652d7374796c652d7472616e736665722f6d61737465722f61646532306b2d696d616765732e706e67)

## TensorFlowの学習済みモデル・・・が？

やったー！あった！と思っていざ使おうと思ったら、おや？環境構築が難しいぞ・・・。
学習済みモデルの.pb形式って、TensorFlowの遙か昔のバージョンのじゃ・・・？
色々と試行錯誤してみたものの、当時のTensorFlow環境を現在のサーバー上で再構築するのは、めちゃくちゃ大変と言うことがわかりました。
これは、折角モデルを公開してくれているのに、気軽に使うことが出来ないぞ・・・。

## 変換すべし

ということで、PyTorchおよびtensorflow2で使えるように、モデルファイルを変換しておきました。
自分で作ったモデルじゃないので、あまり偉そうに出来る話じゃないですが、もし何か需要があればご利用ください。

https://github.com/tanreinama/anime-style-transfer-tf2_pytorch
VirtualBox ホストオンリーアダプターが作れない/再インストールもできない話（MAC）

# 原因
○　https://www.virtualbox.org/wiki/Downloads
☓　https://www.oracle.com/jp/virtualization/technologies/vm/downloads/virtualbox-downloads.html

グーグル検索だと、後者のoracleのほうが上に来るんですよね。。（2022/4現在）

DLするのが久しぶりだったので、「オラクルサイトからの配布に変わったのかな、、」と訝しみつつwww.oracle.comからダウンロード。
MACにインストールしてみたらホストオンリーアダプターが作れないわ、再インストールに失敗するわと。。

よくよく確認したら、VirtualBoxのサイトから配布されているのが6.1.32、オラクルサイトは6.1.14でした。
VirtualBoxのサイトから6.1.32を落として再インストールしたら、全てうまく行きました。

一応変なアドウェア仕込んでるパッケージ配布している偽装サイトじゃないよな、、という確認はしつつだったのですが。

切れっ端のサイトが残ってしまっているのか。
なんだかなぁ。。
DockerでNext.jsとMongoDB環境の構築をする

Next.jsとMongoDBを利用して個人アプリ開発を行おうと思い、Dockerで環境構築を行うことにした。

この記事では、DockerでNext.jsとMongoDB環境の構築方法を解説します。

ちなみに私はMacを使用しています。

## 環境構築

まずはDockerfileとdocker-compose.ymlを以下のような構成で配置します。

```
SampleProject
 - docker
    - Dockerfile
 - docker-compose.yml
```

### Dockerfile

```dockerfile:Dockerfile
FROM node:14.15.4-alpine

EXPOSE 3000
ENV HOST 0.0.0.0

# コマンド実行
RUN apk update && \
    apk add git && \
    npm install -g @vue/cli nuxt create-nuxt-app
```

### docker-compose.yml

```docker-compose.yml
version: '3.8'

services:
  # 参考:dockerhub(https://hub.docker.com/_/mongo)
  mongo:
    image: mongo:4.4.5
    restart: always
    ports:
      - 27017:27017
    environment:
      MONGO_INITDB_ROOT_USERNAME: root
      MONGO_INITDB_ROOT_PASSWORD: example
    # データを永続化する場合必要。volumesがないとdocker-compose downする度にデータが消える
    volumes:
      - ./db:/data/db
      - ./configdb:/data/configdb

  mongo-express:
    image: mongo-express:0.54
    restart: always
    ports:
      - 8081:8081
    environment:
      ME_CONFIG_MONGODB_ADMINUSERNAME: root
      ME_CONFIG_MONGODB_ADMINPASSWORD: example
      ME_CONFIG_MONGODB_URL: mongodb://root:example@mongo:27017/

  nuxt:
    build: ./docker/
    ports:
      - 9000:3000
    volumes:
      - ./SampleProjects:/workspace
    working_dir: /workspace
    command: npm run dev
    # ホットリロードを有効にする
    environment:
      - CHOKIDAR_USEPOLLING=true
    links:
      - mongo
```

### Dockerサービスの立ち上げ

```
$ docker-compose build
```

### Nuxtアプリケーションの作成

```
$ npm create-nuxt-app プロジェクト名
```

各種設定を聞かれますので、自分の作成したい内容に合わせて設定して下さい。

### Dockerサービスの起動

```
$ docker-compose up -d
```

docker-compose.ymlで「command: npm run dev」と設定しているのでdocker-compose upをすればすぐにブラウザで起動していることを確認できます。

nuxtアプリケーションが起動していることが確認できます。

http://localhost:9000/

Mongo-expressが立ち上がっており、ブラウザ上でMongoDBの内容を確認することができます。

http://0.0.0.0:8081/


### まとめ

これでDockerでNuxt.jsとMongoDBの環境構築は完了です。

私もdocker-compose.ymlの設定などは色々な記事を参考にしましたが、人によって色々な書き方があるなーという感想です。

それぞれのローカル環境によっても変わってくるとは思いますので参考にしていただければと思います。
【React / Next】関数コンポーネント(Functional Component)のpropsをジェネリクスにして汎用的に使いたい

# やりたいこと
- TypeScriptで共通コンポーネントを作成する際、抽象度を上げて汎用的に使いたい
- ジェネリクスを使って汎用性を上げる

# 想定ケース
- テーブルコンポーネントを作成して、共通部品として使用する
- 共通コンポーネントへは、「レコードデータ / カラムデータ / ヘッダー情報」 を props で渡す
  - 今回はヘッダー情報のみ、フロントで保持する

# コード
## 型情報
```typescript:types/table.ts
type Props<T> = Partial<T> & {
  width?: string | number;
};

// ヘッダーのlabelと幅を定義、さらにデザイン済みのReactNodeを含める
type ColumnObject<T> = {
  headerLabel: string;
  headerWidth?: string | number;
  // itemの幅はヘッダーの幅と同一とするため、headerWidthの値を使用
  item: (_props: Props<T>) => React.ReactNode;
};

// fetchしたデータ以外に「編集ボタン」など、UIにしかないカラムを定義
type ColumnsKey<T> = keyof T | 'editButton';

// 表示させるカラムkeyと、そのkeyに連動した中身を定義
export type Columns<T> = {
  [_key in ColumnsKey<T>]?: ColumnObject<T>;
};
```

## 共通コンポーネント側
```tsx:CommonTable.tsx「通常のfunctionで記述したケース」
// 共通コンポーネントなので、型定義は全てジェネリクス
interface Props<T> {
  columns: Columns<T>;
  records: T[];
}

// function Component<T>(props: Props<T>) {}  の記述にすることで、共通コンポーネントを使用する側で、型を指定できる
export function CommonTable<T>({ columns, records }: Props<T>) {
  // 省略
  return (
    <>
      <div className={styles.head}>{headers}</div>
      {records.map((record) => (
        <div className={styles.record}>{record}</div>
      ))}
    </>
  );
}
```

```tsx:CommonTable.tsx「アロー関数で記述したケース」
// 共通コンポーネントなので、型定義は全てジェネリクス
interface Props<T> {
  columns: Columns<T>;
  records: T[];
}

// 使用側で型を指定
export const CommonTable = <T,>({ columns, records }: Props<T>) => {
  // 省略
  return (
    <>
      <div className={styles.head}>{headers}</div>
      {records.map((record) => (
        <div className={styles.record}>{record}</div>
      ))}
    </>
  );
}
```

関数のジェネリクスについては、筆者が以前に以下の記事を書いたので、もしご興味がございましたらご覧ください。

https://qiita.com/tak001/items/3ba5e399757050ce2b55#%E5%9F%BA%E6%9C%AC%E7%9A%84%E3%81%AA%E4%BB%98%E4%B8%8E-1

## 使用側
```tsx:pages/list.ts
export const UserList = () => {
  // 省略

  const columns = getItems();

  return (
    {/* 共通コンポーネント使用時に、型を指定 */}
    <CommonTable<FetchTableData>
      records={data}
      columns={columns}
    />
  )
}
```


```tsx:items.tsx
// デザイン済みのitem
export const getItems = (): Columns<FetchTableData> => {
  // 省略
  return {
    title: {
      headerLabel: 'title',
      headerWidth: '50%',
      item: ({ title, width }) => (
        <Text flexBasis={width}>
          {title}
        </Text>
      ),
    },
    createdAt: {
      headerLabel: 'created_at',
      headerWidth: '20%',
      children: ({ createdAt, width }) => (
        <Text flexBasis={width}>
          {getFormattedDate(createdAt, 'yyyy年MM月dd日')}
        </Text>
      ),
    },
  };
};
```

# まとめ
- 共通コンポーネント側
  - Functional Component定義時に、関数ジェネリクスで記述する
  - 例：`function Component<T>(props: Props<T>) {}` 
  - アロー関数での記述例：`const Component = <T,>(props: Props<T>) {}` 
- 使用側
  - 共通コンポーネント使用時に、型を指定
  - 例： `<Common<FetchData> />`
Ubuntu 22.04にmecab-ipadic-neologdをインストール

```bash
sudo apt update \
  && sudo apt install -y mecab libmecab-dev mecab-ipadic-utf8 git make curl xz-utils file python3-pip \
  && pip install mecab-python3 \
  && git clone --depth 1 https://github.com/neologd/mecab-ipadic-neologd.git \
  && cd mecab-ipadic-neologd \
  && ./bin/install-mecab-ipadic-neologd -n -y
```

# テスト
```bash
echo "import MeCab
tagger = MeCab.Tagger('-Ochasen -d /usr/lib/x86_64-linux-gnu/mecab/dic/mecab-ipadic-neologd')
print(tagger.parse('鬼滅の刃'))" | MECABRC=/etc/mecabrc python3
```
```text
鬼滅の刃	キメツノヤイバ	鬼滅の刃	名詞-固有名詞-一般		
EOS
```


エビデンス

https://youtu.be/f2vaQ-TE-to

# 参考
https://github.com/neologd/mecab-ipadic-neologd
【新人プログラマーへ】転職への考え方

新卒社会人もそろそろ２週間過ぎ、ある程度の割合で会社をすでに辞めてしまう人もいるだろう。
また、この会社には将来性がないなとか、転職はいつからとか考えている人も少なからずいる筈である。
そんなあなたに進める転職という道筋。
ちなみに筆者はこんな時期で辞めた経験はないが、辞めた人の体験談を踏まえてこんな記事を書くことくらいはできる。
# すでに退職してしまった人へ
たぶんもうIT業界からは離れたい筈である。自分は違う会社や職種が合っていると実感している筈である。
なので、IT業界からは足を洗って、他の職種での転職活動を勧める。
ただ、社会人としてやっていけないと思っているのであれば別問題である。
まずは、会社に入社したということであれば、社会人としてやっていくつもりであるはずであるのにも関わらず、
会社を見切り、退職という選択肢をとったのであれば、普通にサラリーマンとしては働いていけないだろう。
それはそれとしてつらい人生だと筆者は思うのだが。

# 転職を考えている人へ
転職を考えているそんな人へはこんなアドバイスがある。
まずは、転職したい理由を考えよう。
* IT業界は合っていないが、今転職するはないなと考えている。
* IT業界で働いていこうと考えてはいるが、会社に将来性が見いだせない

上記の人であれば、そもそも職種として合っていないので、すぐにでも転職活動を開始した方がいいと思う。
長くやっていれば慣れるということもあるが、根本的に合っていないとなるとやはり別の職種に行き、長いこと勤めていたほうが自分のためになると考える。
逆に下の考えをもっている人に取っては少しの間会社にいてもいいのかもしれない。ただ、３年は勤めないととかは考えてはいけない。
少なくとも１年もいれば会社の大体の事情はつかめてくる。（いや、もはや１年もいなくてもと思う。）
なので、自分が経験が積めるなこの会社はとか思ったら、別にその会社にいるのもありだし、
それでも将来性がとか思うのであれば、一定の期間のあとに辞めればいい。

# 引き留めに合うんじゃないか
確かに辞めるときには引き留めにあう。まあ、それはしょうがない。相手も上司なので、会社として引き留めなければならないことも理解しておいたほうがいい。
ただ、だからといって転職しない理由にもならない。しつこく引き留め行為をするような人であれば、そう感じた時に速攻退職代行でかつ弁護士資格を持っている人を雇えばいいだけのことである。
まあ、大体の引き留めが
「今のプロジェクトの進行状況とか君に割り振る仕事はどうするんだね。」
という人が多いので、
<strong> 「仕事を管理できていない管理職のあなた方が悪いんでしょ。私は平社員として働いてきましたけど。  
私一人がいなくなったところで何も会社としては変わらないですし、そういう風に経営できていないのであれば、経営陣の問題です。」</strong>
と冷静に淡々といえばいいだけです。
ちなみに弁護士資格を有する退職代行は大体7万くらいからあり、10日分の有給の給料が丸々なくなると思う。

# 転職で求められるスキル
ぶっちゃっけどんなにできる人間でもコミュニケーション能力がなかったり、アピールできない人間は求められないのだと思う。
人事に携わったことはない。
ｄが、自分が転職してきた過去を鑑みると、自分を落としてきた企業を見ると変なところで厳しかったり、なんか合わなかったりで落とされているんだと思う。
ぶっちゃっけ雇う側としても雇った人間が地雷の可能性ははらんでいるので。


# 辞めるの先?転職先を見つけてから辞める?
これもパターンによりますね。転職先が決まってから転職したほうがいいですが、まあ、そういったことを待ってくれない状況もありますからね。
ただ、転職先が見つからない状況で辞めた場合でもこれだけは肝に銘じておいた方がいいです。
<strong>今より絶対いい会社に行くし。自分の納得した条件で転職する。でなければ働かないと。</strong>

# 働かなくてもいいという価値観を持つ
別に働かなくてもいいんだと思えば、案外転職活動も楽でに出来ます。
ただ、お金が底をつきそうとか思う不安もあります。
２０代なら親を頼るって選択肢はなくさない方がいいです。
もし頼ればないのであれば、役所にいって生活保護を申請しましょう。
案外簡単に転職活動中で申請が通ったりします。
別に恥ずかしい事でも何でもないですよ。
別に生活保護だからって周りの人には分かりません。
まあ、分かってしまうのは大家と不動産屋と病院に行った時くらいです。

# 最後に
<strong>よい転職活動を。</strong>
【Rails】booleanは実質メソッド!?と感じたこと

# はじめに

Railsアプリケーション開発中にbooleanのカラムに対してこんなコードに遭遇した.

```ruby:db/migrate/create_post.rb
class Posts < ActiveRecord::Migration[5.2]
  def change
    create_table :posts do |t|
      t.string name
      t.boolean draft, comment: '下書き'
    end
  end
end
```

```ruby:post.rb
// 下書きがどうかを判定するメソッド
def draft?
 draft == 1
end
```

## マジックナンバーは使用しない
まず大前提としてマジックナンバーの使用は避けた方が良い。保守性に影響がでてしまう。

https://qiita.com/daichi0713/items/d838eda12a664bc04542#4%E3%83%9E%E3%82%B8%E3%83%83%E3%82%AF%E3%83%8A%E3%83%B3%E3%83%90%E3%83%BC%E3%81%AF%E4%BD%BF%E3%82%8F%E3%81%AA%E3%81%84

もしメソッドとして書くのであればこっちの方が良いのではないか
```ruby
def draft?
 draft == true
end
```

# booleanの判定は、メソッドとして定義しなくても使える
これがこの記事で一番伝えておきたかったこと。

:::note info
`boolean`で定義されたカラムは、デフォルトで真偽判定をメソッドのようにして行える
:::

どういうことかというと・・・

わざわざメソッドを定義しなくてもデフォルトで↓が使える

```ruby:post.rb
post.draft?
=> true
```

## その理由
参考となる部分を発見👀

https://github.com/rails/rails/blob/main/activerecord/lib/active_record/attribute_methods/query.rb

さらに調べてみると`ActiveModel`で定義されているらしい

https://api.rubyonrails.org/classes/ActiveModel/Type/Boolean.html

https://api.rubyonrails.org/classes/ActiveModel/AttributeMethods.html#method-i-respond_to-3F


# せっかくなので、booleanについて他に色々調べてみた
## ■ booleanを使用するときの注意点

https://qiita.com/jnchito/items/a342b64cd998e5c4ef3d

https://qiita.com/mmaumtjgj/items/b9b04ea64e30a16569fe

## ■ booleanの 「いとをかし」 な話
https://qiita.com/rotelstift/items/70461f35c0d691e7b246


# 参考

https://teratail.com/questions/40723?sort=1
Kintone クエリの書き方

すぐ忘れてしまう人（自分）へ

退職日が空欄
```
var paramFields = "&fields=$id," + encodeURIComponent("社員番号,氏名,在籍状況,組織選択");
  var paramQuery = "&query=" + encodeURIComponent("退職日=\"\" order by 社員番号");
```

社員番号で絞る
```
 var paramFields = "&fields=$id," + encodeURIComponent("社員番号,受講者名,開始日,終了日,研修名,研修提供会社,研修方法");
  var paramQuery = "&query=" + encodeURIComponent("社員番号=" + syainCode + "order by 社員番号");
 ```
ドロップボックスのとき
```
var paramFields = "&fields=$id," + encodeURIComponent("物品管理番号,必ず選択,入庫日,物品種類,購入入庫目的,廃棄日,廃棄方法");
    var paramQuery = "&query=" + encodeURIComponent("必ず選択 in (\"" + target + "\")");　//"メールアドレス=\"" + email + "\""
   
```
【React】Error: Objects are not valid as a React child (found: object with keys {label, value}).  If you meant to render a collection of children, use an array instead.

エラー対応の個人的な備忘録です。

# やりたいこと
- DOM要素をループさせて、リスト表示させたい

# エラーメッセージ

```
Error: Objects are not valid as a React child (found: object with keys {label, value}). 
If you meant to render a collection of children, use an array instead.
```

# 原因
- オブジェクトそのものを表示しようとしたため、エラーになった
- リファクタ時に分割代入していた箇所が漏れてしまい、表示時にオブジェクトそのものになってしまっていた

# 解決方法
- {obj} ではなく {obj.title} で表示するようにする
PDFファイルのページ数を取得するVBScript

ポイントは、

- PDFファイルは、ADODB.Stream を使って UTF-8 のテキストモードで読み込む
- 読み込んだデータから、正規表現でページヘッダを探してカウントする

この２つだけ。

<font color="red">※ VBSファイル自体は Shift-JIS で保存することを忘れずに</font>

```vb:VBScript
'// PDFページ数取得.vbs
'// エクスプローラ上で、このスクリプトファイルに PDFファイル（複数可）をドラッグ＆ドロップする

Set adoStream = CreateObject("ADODB.Stream")
Set fso = CreateObject("Scripting.FileSystemObject")
Set regEx = CreateObject("VBscript.RegExp")
regEx.Global = True
regEx.Pattern = "/Type\s*/Page[^s]"

reStr = "ファイル名" & vbTab & "ページ数"

For Each arg In WScript.Arguments
    If fso.GetExtensionName(arg) = "pdf" Then
        With adoStream
            .Charset = "UTF-8"
            .Type = 2    '//［1：バイナリモード］ ［2：テキストモード］
            .Open
            .LoadFromFile arg
            pageCnt = regEx.Execute(.ReadText).Count
            .Close
        End With
        reStr = reStr & vbCrLf & fso.GetFileName (arg) & vbTab & pageCnt
    End If
Next

Msgbox reStr
```
face-api.jsでゲームを作ったみた

顔認識のライブラリ「face-api.js」を見つけたので、勉強がてらゲームを作ってみました。

画面の上から落ちてくるフルーツを、画面に映った自分の口でキャッチするゲームです。

![ゲーム画面](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/136795/40cf6b50-23aa-24cb-be38-cd6abd867ed5.png)


- ゲームサイト
    - https://yoshiaki110.github.io/pakupaku/
- ソース
    - https://github.com/Yoshiaki110/pakupaku/

face-api.jsの使い方は、いろいろ記事があるので、それらを見てください。

- 工夫した点など、
  - 画面に映った、VIDEO画像は鏡面にしたほうが直感的な動きになるので、反転させている
  - そのためタイトル画面まで反転するので、さらに反転させ、正しく見えるようにした
  - カメラ利用許諾のブラウザダイアログがでてから実際にカメラが利用できるまで時間がかかるので、その間ゲームを始めないようにした

公開するのに、Herokuを使うつもりだったが、Github Pageで足りそうなので、初めて利用しましたが、pushした後、実際に反映されるまで数分かかりました。
最初気が付かず、なぜできないか、いろいろ調べてしまいました。
DATA + AIサミット2022のご案内

弊社Databricksの最大の年次イベントのご紹介です。

**イベント名**: DATA + AI SUMMIT 2022
**日程**: 2022/6/27〜6/30(米国時間)
**場所**: アメリカ サンフランシスコ(バーチャルとのハイブリッド開催)
**費用**: バーチャル：無料、現地参加：以下[リンク](https://databricks.com/jp/dataaisummit/north-america-2022)をご覧ください。
**登録ページ**: 

https://databricks.com/jp/dataaisummit/north-america-2022

# 登壇者

**Andrew Ng**
Landing AI 創業者兼 CEO、DeepLearning.AI 創業者

**Zhamak Dehghani**
Thoughtworks エマージングテクノロジー部門ディレクター

**Christopher Manning**
スタンフォードAI研究所(SAIL) ディレクター

**Daphne Koller**
insitro創業者兼CEO

**Peter Norvig**
ACMおよびAAAIのフェロー、Google研究本部長(Director of Research)、書籍「Artificial Intelligence: A Modern Approach」の著者

**Ali Ghodsi**
Databricks共同創業者兼CEO、Sparkのオリジナルクリエーター

**Brooke Wenig**
Databricksディレクター、Machine Learning Practice

**Matei Zaharia**
Databricks共同創業者、チーフテクノロジスト、Apache Spark™ & MLflowのオリジナルクリエーター

**Hilary Mason**
Hidden Door 共同創業者兼CEO

**Reynold Xin**
Databricks共同創業者兼チーフアーキテクト

**Ganesh Jayaram**
John DeereのVice President兼Global Information Technology

**Tristan Handy**
dbt Labs 創業者兼CEO

**George Fraser**
Fivetran 創業者兼CEO

# DATA + AIサミット2022に参加すべき5つの理由

1. データ + AIの世界におけるソートリーダーのキーノート: AIのパイオニアかつ書籍「Artificial Intelligence: A Modern Approach」の著者であるPeter Norvig、Landing AI 創業者兼 CEO、DeepLearning.AI 創業者であるAndrew Ng、データメッシュのクリエーターであるZhamak Dehghani 

1. Databricks上級執行役員からのインサイト: 共同創業者兼CEO Ali Ghodsi、共同創業者兼チーフアーキテクト Reynold Xin、チーフテクノロジスト、Apache Spark™ & MLflowのオリジナルクリエーター Matei Zaharia

1. Apache Spark™、Delta Lake、MLflow、Apache Flink、PrestoDB、dbtなどのオープンソーステクノロジーをカバーする200以上のテクニカルセッション

1. 最大の業界セクターにおける最もイノベーティブなブランドをフィーチャーした専用のインダストリーフォーラムとトラック

1. Databricksレイクハウス、Apache Spark™プログラミング、機械学習モデルの管理、新たな認定バンドルを含む、対面かつバーチャルの広範なトレーニングプログラム

### Databricks 無料トライアル

[Databricks 無料トライアル](https://databricks.com/jp/try-databricks)
2011年のJavaScript本は古い - 2022年、freeCodeCamp日本語版の紹介

## 2011年のJavaScript本は古い
[パーフェクトJavaScript (PERFECT SERIES 4) | 井上 誠一郎, 土江 拓郎, 浜辺 将太 |本 | 通販 | Amazon](https://www.amazon.co.jp/dp/477414813X)

<img src="https://images-na.ssl-images-amazon.com/images/I/51yrjFsf-2L._SX390_BO1,204,203,200_.jpg" width=30% />

先日久方ぶりに出社した際にフロアの隅にたたずんでいる本を見つけ読んでみたものの、時代を感じる内容だったのでそのメモとポエムです。「パーフェクトJavaScript」。2011年の本である。古い（進次郎構文である）。
目次: https://gihyo.jp/book/2011/978-4-7741-4813-7


## 技術書は高い

買うか借りるか。どうやって手に入れるかの選択肢はこんな感じではないだろうか。

- 買う
    - メリット: 読める。筆者に対価を払える。何度も自由に好きなときに読める。
    - デメリット: 自分の懐に厳しくなる。
    - 物理で買う
        - メリット: 書き込める。開きながら写経できる。不要になったら捨てるか売るか、人にあげられる。
        - デメリット: 場所をとる。
    - 電子書籍で買う
        - メリット: 場所の節約になる。持ち運んでどこでも読める。
        - デメリット: 写経したいときにPCでやろうとすると画面を支配される。売れない。
- 借りる
    - メリット: 自分の懐に優しくなる。
    - デメリット: 順番を待つ必要がある。もう一度読みたくなる可能性がある。
- つまり技術書とは
    - プログラミング言語のVersionやそのタイミングでの流行を抑える必要がある内容か
    - アルゴリズムなどある程度普遍的な内容か
    - 版を重ねたベストセラーか

など、都度吟味して買うものであったりする。


## JavaScriptを学ぶ

ちょうど [Java力をあげるための指針 - きしだのHatena](https://nowokay.hatenablog.com/entry/2022/04/15/120608) という記事。

> あとは手を動かすんですが、LeetCodeやりましょう。
https://leetcode.com/
easyをたくさんやるのがいいです。

他にも: [言語の習得やアルゴリズムの学習に最適 オンラインジャッジサイト一覧（2021/12 版) - Qiita](https://qiita.com/shibacho/items/20f6eee42083c620b556)


[ECMAScript - Wikipedia](https://ja.wikipedia.org/wiki/ECMAScript)
[JavaScript | MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript)
[sorrycc/awesome-javascript: 🐢 A collection of awesome browser-side JavaScript libraries, resources and shiny things.](https://github.com/sorrycc/awesome-javascript)
[JSer.infoについて - JSer.info](https://jser.info/about/)


## 記事
[JavaScript文字列操作18本ほど - Qiita](https://qiita.com/e99h2121/items/79d6d4db9182c8c9540d)
[JavaScript Array操作 15本ほど - Qiita](https://qiita.com/e99h2121/items/f3746fb3a68b2f4f2741)
[【JavaScript】ES2022の新機能 - Qiita](https://qiita.com/rana_kualu/items/8bafecd760ae69cfac41)
[エープリルフールなのでJavaScriptの信じがたい話をします - Qiita](https://qiita.com/suin/items/461c096bef318a259c80)

など結局オンラインの何かで済ませたくなる気持ちにもなる。


## そこでfreeCodeCampという選択肢

[プログラミングを無料で学ぶ: 多忙な人々のためのプログラミング講座](https://www.freecodecamp.org/japanese/)

https://www.freecodecamp.org/japanese/news/learn-programming-by-yourself-freecodecamp-japanese-released/

[フルスタックなWeb開発が無料で学べるFreeCodeCamp - Qiita](https://qiita.com/applideveloper/items/9ef1f8258f54982b8045)

前置きが長くなったがfreeCodeCamp カリキュラム日本語版が2022年1月31日リリースされている。
わずかばかりですがコントリビュータとして参加させて頂きました。有難うございます。

買うか、借りるか。本は色々、教材も色々ありますが一つの選択肢としてオススメです。
以上です～
【Next.js】メタデータの設定方法

## はじめに
　本記事は、プログラミング初学者が学習を進めていて疑問に思った点について調べた結果を備忘録も兼ねてまとめたものです。
　そのため、記事の内容に誤りが含まれている可能性があります。ご容赦ください。
　間違いを見つけた方は、お手数ですが、ご指摘いただけますと幸いです。


## メタデータの設定方法
メタデータの設定は以下のように行います。

### `Head`コンポーネントで設定する
メタデータを設定するには`Head`コンポーネントを使用します。
`index.js`に`Head`コンポーネントを追記します。

```index.js
import Head from 'next/head'  // 追記
import styles from '../styles/Home.module.css'
import Image from 'next/image'
import Link from 'next/link'

export default function Home() {
  return (
    <div className={styles.container}>

      <Head>
        <title>Sample App</title>
        <meta name="description" content="Generated by create next app" />
        <link rel="icon" href="/favicon.ico" />
      </Head>
      <main className={styles.main}>
        <h1 className={styles.title}>
          Welcome to
          <Link href="/posts/samplePost">
            <a> Sample App!!</a>
          </Link>
        </h1>
        <Image src="/sample/sample.jpg" alt="Sample Image" width={500} height={500} objectFit="contain" />
      </main>
    </div>
  )
}
```

すると以下のように、タブに表示されているタイトルが変更されました。

![スクリーンショット 2022-04-16 5.14.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2342443/14446870-44d8-d7cc-b81d-e1ec28ea7a50.png)


### 他のページも変更する
ページごとにメタデータを設定できます。
`samplePost.js`を以下のように修正します。

```samplePost.js
import Head from "next/head";  // 追記

export default function() {
  return (
    <>
      <Head>
        {/* title追加 */}
        <title>サンプルページ</title>
      </Head>
      <h1>samplePost</h1>
      <p>samplePostのページが表示されています。</p>
    </>
  );
}
```

すると`samplePost`のページにおいてもタグに表示されているタイトルが設定したとおりに変更されました。


![スクリーンショット 2022-04-16 5.15.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2342443/a016f9bf-a59e-bde7-9ce7-33d6d453696e.png)
Pytorchモデルの保存、読み込み

保存

```python
torch.save(model.state_dict(), PATH)
```

読み込み

```python
model = TheModelClass(*args, **kwargs)
model.load_state_dict(torch.load(PATH))
model.eval()
```

🐣
****
フリーランスエンジニアです。
お仕事のご相談こちらまで
rockyshikoku@gmail.com

Core MLやARKitを使ったアプリを作っています。
機械学習／AR関連の情報を発信しています。

[Twitter](https://twitter.com/JackdeS11)
[Medium](https://medium.com/@rockyshikoku)
[GitHub](https://github.com/john-rocky)
[Laravel]プロジェクトを立ち上げた時にやることまとめ

composer
node.js
をインストールしている前提



# プロジェクトの立ち上げ
```:ターミナル
composer create-project --prefer-dist laravel/laravel (プロジェクト名)
```

## Vue,Reactの使用時
```:ターミナル
composer require laravel/ui --dev
```
必要があれば```--dev```の前にバージョンの指定をする
bootstrapもいじれるけどオフライン開発でもなければ[公式](https://getbootstrap.jp/)のCDN利用で十分

## breeze(認証まわり)
*laravel8以降
```:ターミナル
composer require laravel/breeze
php artisan breeze:install
```

## package.jsonの確認
しなくてもいいけど```npm install```で何がインストールされるのかは見ておきたい
確認ができたら実行
```:ターミナル
npm install ; npm run dev
```
VScode上では&&でつなぐとエラーを吐くので注意

# タイムゾーン/言語設定の変更
```php:config/app.php
'timezone' => 'Asia/Tokyo',
'locale' => 'ja',
'faker_locale' => 'ja_JP',
```


# DBの作成
```sql:MySQL
create database (DB名);
```

# .envの修正
```php:.env
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=(DB名)
DB_USERNAME=(DBユーザ名)
DB_PASSWORD=(DBユーザのパスワード)
```

# マイグレーションファイル
### 作成
```
php artisan make:migration create_(テーブルの名前)_table
```

### 実行
```
php artisan migrate
```





# SASSを使うとき

```PHP:webpack.mix.js
mix.js('resources/js/app.js', 'public/js')
    .sass('resources/sass/app.scss', 'public/css');
```
```mix.メソッド名('コンパイルするファイルパス','出力先');```
の形式で書く。

- ```npm run dev```
- ```npm run watch```
ざっと書いてコンパイルするだけなら前者、編集しながら確認したいときは後者

# その他
[Laravelにデバッグバーを導入する方法](https://migisanblog.com/laravel-debugbar-install/)


# 参考記事
[そろそろ適当にnpm installするのを卒業する](https://zenn.dev/ikuraikura/articles/71b917ab11ae690e3cd7)
[【Laravel】SASS(scss)の使い方実例。Laravel Mixを使って簡単にコンパイルする方法](https://qiita.com/shizen-shin/items/2dea785c3fdbc7c96a60#1-nodejs%E3%81%A8npm%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB)
adb関連のあれこれ(Mac)

## 今回の内容
- adbとは？
- adbコマンドのファイルパスを通す
- adbコマンドでapkをインストールする
- AndroidデバイスでFirebaseDebugViewモードを有効にする方法

## マシン環境
- マシン：M1 MacbookAir 2020
- OS：Mac OS Big Sur Ver.11.3.1
- シェル：zsh 5.8 (x86_64-apple-darwin20.0)

## adbとは？
adbとは、「Android Debug Bridge」の略で、Android端末とやりとりするための多用途なコマンドである。アプリのインストールやアンインストール、クラッシュログの出力など、さまざまな操作が行える。

### Android SDK locationを調べる
- Android Studioを起動しSDK Locationを調べる
- 大体、`/Users/ユーザ名/Library/Android/sdk`が初期設定から変更しない場合ある。

## adbコマンドのファイルパスを通す
adbコマンドはsdkフォルダのplatform-toolsにあるので、ファイルパスを通すために下記を追記する。
```
export PATH=$PATH:/Users/ユーザ名/Library/Android/sdk/platform-tools
```
上記を追加する場所は下記コマンドを実行して、設定ファイルを開く。
```
$ open ~/.zshrc
$ vim ~/.zshrc
  または
$ open ~/.bash_profile
$ vim ~/.bash_profile
```
- テキストエディタを選択する場合: open
- vimを選択する場合：vim

上記コマンドどちらを選択するかは、下記コマンドを実行しシェルの種類を確認する。
```
echo $SHELL

// 実行結果
/bin/zsh
```

bashの編集後は下記コマンドを実行し、編集内容を反映させる。
```
$ source .zshrc
$ source .bash_profile
$ source .bashrc
```

次に、USBデバッグを有効にした上でUSBケーブルでスマホをつなぎ、下記コマンドを実行するとデバイスがつながっているかを確認できる。
```
$ adb devices
```
## adbコマンドでapkをインストールする
```
adb install xxx.apk
```
xxx.apkはapkファイルまでのパスを入れる

## AndroidデバイスでFirebaseDebugViewモードを有効にする方法
ターミナルで`cdコマンド`で下記に移動する。
```
/Users/takayukishoji/Library/Android/sdk/platform-tools
```

下記コマンドを実行することでデバッグモードが有効になる。
```
adb shell setprop debug.firebase.analytics.app <package_name>
```

下記サイトのように記載はされているが、コマンドの実行場所が記載がないため、ぬまる原因になった。2つめのサイトでその原因を知ることになった。
- [イベントのデバッグ](https://firebase.google.com/docs/analytics/debugview?hl=ja)
- [Enable Firebase DebugView mode on an Android device](https://write.agrevolution.in/enable-firebase-debugview-mode-on-an-android-device-a6aa108acb56)
FiddlerでWeb応答を任意のファイルに変更する

# 環境
windows10
Progress Telerik Fiddler Classic

# 実現すること

ブラウザで特定のサイトを開いたら、自分が表示したい内容を書いたhtmlファイルで置き換えます。

# 方法

1.`C:\Users\PCユーザの名前\Documents\Fiddler2\Captures\Responses`に書き換えたい内容のファイルを置きます。
たとえば、下記の内容のtest.htmlファイルを置きます。

```html:test.html
<h1>hello</h1>
```

2.ブラウザのプロキシ設定でfiddlerプロキシ(デフォルトではlocalhost:8888)を通します


3.fiddlerのカスタムルールを開きます

4.OnBeforeResponseを検索してスクリプトを追記します
例えば、下記の内容を追記します。
qiitaからのレスポンスをtest.htmlに置き換えたかったら下記のようにします。

```Java
static function OnBeforeResponse(oSession: Session) {

//ここから追記
if (oSession.host == "qiita.com") {
    oSession["x-replywithfile"] ="test.html";
}
//ここまで追記

}
```

5.カスタムルールを上書き保存します

6.実際にブラウザでサイトを開いてみます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246006/6e87db79-af01-ebbe-4a40-bb00db579be4.png)
このような表示に置き換わっていたら成功です。
Pythonで複数行の文字列を複数行の文字列に置換する

Pythonで複数行に渡る文字列を別の文字列へ置換したい場合、`re`モジュールの`re.DOTALL`を指定して`re.sub()`を使用する。

# サンプルコード解説
以下は、ファイルの中の複数行の文字列に一致する箇所を新たな複数行の文字列に置換し、同名のファイルとして保存するサンプルコードとなる。

```py:multiline_convert.py
#!/usr/bin/env python3
import sys
import re

target = r'\tfor \(hoge\) {\n\t\thogehoge;\n\t}*'

replace = """	if (hoge) {}
		for (hoge) {
			hogehoge;
		}
	}"""

f1 = open(sys.argv[1],'r')
f2 = open(sys.argv[1]+"_new",'w')
body = f1.read()
distbody = re.sub(target, replace, body, flags=re.DOTALL)
f2.write(distbody)
f1.close()
f2.close()
os.remove(sys.argv[1])
os.rename(sys.argv[1]+"_new", sys.argv[1])
```

`target`はタブや改行も含め、すべて正規表現で記述する必要がある。
サンプルコードでは以下の文字列を意図している。
```
	for (hoge) {
		hogehoge;
	}
```

`replace`は改行を含む文字列として記述できる。
サンプルコードでは以下の文字列を意図している。
```
	if (hoge) {}
		for (hoge) {
			hogehoge;
		}
	}
```

# サンプルコードの呼び出し方法

サンプルコードを使う時は以下のように呼び出す。

```shell
python multiline_convert.py hoge.txt
```

サンプルコードに記載した`target`と`replace`の場合だと、以下のような結果となる。
```:hoge.txt(置換前)
hoge1
hoge2

	for (hoge) {
		hogehoge;
	}

eof
```

```:hoge.txt(置換後)
hoge1
hoge2

	if (hoge) {
		for (hoge) {
			hogehoge;
		}
	}

eof

```

# おわりに

ターゲットにする複数行文字列を正規表現での文字列に変換できるスクリプトが欲しい
【TypeScript】TypeScriptの第一歩

# 概要
TypeScriptをチャレンジ、最初の第一歩を踏み出したい方向けの記事となります。

# 環境・前提
PC：MacBook Pro
使用エディタ：VSCode

`npm`というパッケージ管理システムを利用するため、Node.jsが必要になります。
『[こちらのダウンロードページ](https://nodejs.org/ja/download/)』から(LTS※推奨版を)ダウンロード、インストールしてください。

★Node.jsインストールすると「npm」というコマンドが使用できるようになります。

```sh
#　執筆時点の各バージョン
node -v
v16.14.2

npm -v
8.5.0
```

# 手順

## プロジェクトの初期化
まずは、Mac標準のターミナル開き、以下のコマンドを入力していきます。

```sh
# 任意のディレクトリ内で作業ディレクトリを作成 & 移動
mkdir sample_frontend
cd sample_frontend
```

```sh
# VSCodeを開く(VSCodeが開いたら「command + jキー」を押し、ターミナルを表示してください)
code .
```

```sh
# VSCodeのターミナル(もしくは先ほどまでのターミナル)で
# 初期化(続けて質問されるがここでは全てEnter ※最後は「yes & Enter」)
npm init
```
<details><summary>npm init(実行例)</summary>

```sh
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.

See `npm help init` for definitive documentation on these fields
and exactly what they do.

Use `npm install <pkg>` afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.

# ↓ここから私たちが入力する(今回は基本的にEnter ※後で書き換えることも可能)
package name: (sample_frontend) 
version: (1.0.0) 
description: 
entry point: (index.js) 
test command: 
git repository: 
keywords: 
author: 
license: (ISC) 
About to write to /Users/andotaishi/dev/sample_frontend/package.json:

# ↓の内容が後述のpackage.jsonに記載されている
{
  "name": "sample_frontend",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}


Is this OK? (yes) yes
```
</details>

作業ディレクトリ内に「package.json」が作成されたことを確認する。

## TypeScriptを導入
ターミナルに戻り、引き続き実行。
```sh
# -g オプションをつけてグローバルに「typescript」パッケージをインストール
npm install -g typescript
```

```sh
# 「hello.ts」というファイルをVSCodeで新規作成するか以下コマンドで作成
touch hello.ts
```

作成した「hello.ts」に以下を記載。
```hello.ts
const message: string = 'Hello World';

console.log(message);
```

ターミナルに戻り、以下のtscコマンドでJavaScriptへ変換。
```sh
# 「tsc」コマンドは、typescriptパッケージをインストールすると使用可能になる
tsc hello.ts
```
作業ディレクトリ内に「hello.js」というコンパイルされたjsファイルが作成されました。
(※ファイルの中身も比較してみてください)

今後は、`tsconfig.json`というコンパイル時のオプション設定ファイルを作成したり、`ESLint`の導入など...チャレンジしてみましょう！！
Framerで少しリアルな入力フォームのプロトタイプを作る

## この記事の概要

最近は UI デザインツールとプロトタイピングツールはセットになりつつあります。
しかし、ユーザーテストで実際に情報を入力してもらい、それにあわせて結果を変化させるのは難しいです。

今回は入力フォームの内容と、その内容を保持した確認画面風のプロトタイプを[Framer](https://www.framer.com/)で作ってみます。

## 完成物

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214677/48030a50-0658-f886-a131-093b4ee5084f.gif)

およそ上の GIF の通りですが、実物は以下のリンクからご覧ください。

https://framer.com/embed/1CujRBWPDxpnzIGsvUQB/xeLZJbiN5?highlights=0

## 作り方

### GUI 側の作成

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214677/40758f99-c9b1-118d-757b-066e23711ee2.png)

画面上に必要なのは今回はこれだけです。

- 入力フォーム
  - `type="text"`な input 要素 2 つ
    - Framer の Built-in Components
  - ボタン
    - 確認画面に進む用
- 確認画面
  - 簡単な表組み（？）
    - フォームで入力する内容に対応した項目が載っている
  - ボタン
    - （データが反映されているのを確認するために）前のページに戻る用

### Code Editor 側の作成

`Assets > Code > Create Code File`から`New override`を選びます。
ここではファイル名は`Override`としておきましたが、なんでも OK です。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214677/52343b39-af3f-a9e1-1eb2-59ffbe6da5f2.png)

あとはこんな感じでコードを書きます。

```typescript
import type { ComponentType } from "react";
import { createStore } from "https://framer.com/m/framer/store.js@^1.0.0";

const useStore = createStore({
  userName: "",
  email: "",
});

export function withUserName(Component): ComponentType {
  return (props) => {
    const [store, setStore] = useStore();
    console.table(store);
    return (
      <Component
        {...props}
        value={store.userName}
        onChange={(content) => setStore({ userName: content })}
      />
    );
  };
}

export function withEmail(Component): ComponentType {
  return (props) => {
    const [store, setStore] = useStore();
    return (
      <Component
        {...props}
        value={store.email}
        onChange={(content) => setStore({ email: content })}
      />
    );
  };
}

export function withResult(Component): ComponentType {
  return (props) => {
    const [store] = useStore();
    return (
      <Component {...props} userName={store.userName} email={store.email} />
    );
  };
}
```

割と普通のReactですが、多少違う箇所を説明します。

- `import { createStore } from ~`
  - Framer独自の状態管理用ライブラリと思ってください
  - これを使えばプロトタイプ内で値を保持できます
- `onChange={(content) => setStore({ userName: content })}`
  - input要素なので最初は`(event) =>setStore({ userName: event.target.value })`になるのかと思っていました
  - 特にそういうわけではなく、ただ引数を指定するだけでOKのようです

### GUIをコードをマージ

`input`の方には、単に`Overrides`から該当の関数を適用するだけです。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214677/0a1eab60-e132-6ffe-b983-03a052b6c5da.png)

確認画面の方は、`Variables`からそれぞれの項目に名前や型指定をした上で、`Overrides`から該当の関数を適用します。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214677/c24d5a17-ff85-dc89-22c4-da4bbe41e36c.png)

改めて、最初に貼った通りですが入力内容にあわせて確認画面に進めます。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214677/48030a50-0658-f886-a131-093b4ee5084f.gif)

今回は簡単な作りですが、もう少し項目を増やしたり適切なタイミングで出したりすれば`選んだ項目にあわせてメッセージが変わる`ようなプロトタイプも実現できます。

---

最後まで読んでくださってありがとうございます！
Twitterでも情報を発信しているので、良かったらフォローお願いします！

https://twitter.com/xrxoxcxox
React詰まった点

# 画面が真っ白に


`index.tsx`のファイルで以下のように行なっている
```
import React from "react";
import ReactDOM from "react-dom";

ReactDOM.render(
  <React.StrictMode>
    <h1>OK</h1>
  </React.StrictMode>,
  document.getElementById("root") as HTMLElement
);
```
しかし、画面が真っ白に…
![スクリーンショット 2022-04-16 0.47.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1719742/1aba0834-756a-b3fb-6114-9ce0bf8ed031.png)

### 原因
**`index.tsx` とは別に、何もしていない空の`index.ts`があったから**
`index.tsx`の中身の`ReactDOM.render`を表示したいのに、`index.ts`が優先されてしまった。
その結果表示が真っ白に

### 解決策
`index.ts`を消す
![スクリーンショット 2022-04-16 1.01.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1719742/e8d36966-ab5c-ac34-ce7e-4d7fd91f41a8.png)


# webpackのbuild時、TSモジュールがresolveできない

### 解決策
`webpack.config.ts`に以下を追記
```

module.exports = {
  :
  :
  resolve: {
    extensions: ['.tsx', '.ts']
  },
};
```

こうすることでこれらの拡張子を順番に解決しようとする。複数のファイルが同じ名前を共有しているが拡張子が異なる場合、webpack は配列で最初にリストされた拡張子のものを解決し、残りはスキップする。

# 参考

https://webpack.js.org/configuration/resolve/#resolveextensions
https://stackoverflow.com/questions/43595555/webpack-cant-resolve-typescript-modules
RuboCop::Cop::Rails::PresenceというRubocupの指摘

Rubocupでリファクタリングをしていたところ、モデルのメソッドで次の指摘を受けました。

```ruby:models/sample.rb
C: [Correctable] Rails/Presence: 
Use [別テーブルのidを格納した配列を弄る処理].presence || [] 
instead of [別テーブルのidを格納した配列を弄る処理].blank? ? [] : [別テーブルのidを格納した配列を弄る処理]
変数 = [別テーブルのidを格納した配列を弄る処理] ? [] : [別テーブルのidを格納した配列を弄る処理]
```

元の記述は配列を処理した結果、空配列であれば空配列を、そうでなければ処理した結果の配列を返すという単純なものですが、同じ記述を2回繰り返す点でDRY原則に沿った書き方ではありませんでした。

今回の指摘で簡潔に書けることがわかりましたが、これまで見かけない記述でしたのでまとめておきます。

## 短絡評価について

https://qiita.com/gyu-don/items/a0aed0f94b8b35c43290

>||の場合は、
>||の左側がtrueの場合、||の右側が何であってもtrueを返す
>||の左側がfalseの場合、||の右側がtrueだとtrueを返し、falseだとfalseを返す
>つまり、&&の左側がfalseの場合や、||の左側がtrueの場合は、右側の値を見る(評価する)必要はない。言語として、この場合は>右側の値を評価しない、という仕様にすれば、多少は無駄が省けるかもしれない。そのような仕様を「短絡評価」と呼ぶ。

今回の場合は、`[別テーブルのidを格納した配列を弄る処理].presence`がtrueであれば、trueを返し、
そうでなければ、右側の値をbool値を返すということになります。

実際に返すのは次の記事に記載のある規則に従い、bool値ではなく処理の結果か空の配列になります。

https://qiita.com/Yametaro/items/17f9b2baa67440b8664a

```javascript:sample.js
const name = user.name || "名無しさん";
```
>ワイ「ああ、なんかワイも見たことあるわ」
ワイ「ユーザーの名前が存在すれば、その名前を代入するし」
ワイ「存在しなければ取り敢えず"名無しさん"を代入しとく、いうやつやな」
ワイ「if文とか使わずスッキリ書けるな」

今回はif文ではなく、三項演算子を使用しておりましたが、それでも簡潔にかけました。

## presenseメソッドについて
Rubocupのページには次の記述があります。

https://www.rubydoc.info/gems/rubocop-rails/RuboCop/Cop/Rails/Presence

>This cop checks code that can be written more easily using `Object#presence` defined by Active Support.

presenceメソッドを使えば、簡単に書ける場合をチェックしているとのことです。
presenceメソッドの説明をしていないので、他のページを見ましょう。

https://apidock.com/rails/Object/presence

> Returns the receiver if it’s present otherwise returns nil.

存在していればその値を返し、そうでなければ`nil`を返すとのことです。
記載されている例でも短絡評価が使用されています。

```ruby:sample.rb
state   = params[:state]   if params[:state].present?
country = params[:country] if params[:country].present?
region  = state || country || 'US'
```
```ruby:sample.rb
region = params[:state].presence || params[:country].presence || 'US'
```
次の記事にもあるように色々と簡潔な書き方ができるので積極的に使用したいですね。

https://techracho.bpsinc.jp/hachi8833/2021_02_26/62326
Obsidianのマリアージュ技その3｜Alfredのスニペットで書類に画像を貼る

## はじめに

便利メモツール[Obsidian](https://obsidian.md)。さらにラクして使うコツ、なかでも練度のあがってきたものをご紹介します。

### AlfredのSnippetsと組み合わせる

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184128/d74a22ff-057a-9749-b315-80c6480ec45f.png" width=60%>

[Alfred](https://www.alfredapp.com)は、macOS用の作業効率化ツールです。有料版（パワーパック）は34ユーロもしくは59ユーロですが、このツールにピンと来る人なら元が取れるとおもいます。わたしにとっては、過去最高級のコスパでした。

Alfredにはたくさんの機能がありますが、そのなかでもObsidianとのマリアージュが最高の`Snippets`との組合せ例のご紹介です。


## 書類のなかに画像を貼る

文を補足するために、画像を貼れるとなにかと便利なのは、言うまでもありません。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184128/9f46944f-80cc-6713-259d-f1186546a068.png" width=60%>

ObsidianでのMarkdownとしては、下記の形式になります。

```
![img|300](https://aaa.bbb.ccc/ddd.png)
```

このなかの`![img|300]`は、下記を意味します。

- `!`: プレビューを表示せよ
- `img`: 任意の文字列（なんでもいい）
- `|300`: 幅300ピクセルで表示せよ

ちなみに、Obsidianは幅指定しかできません。経験上は300ピクセルぐらいが自分にはちょうどいいです。

## 画像を貼るMarkdownコードをスニペットとして登録する

Alfred Preference > Features > Snippets　であたらしいスニペットを登録します。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184128/ef3dfdcf-bd3a-424c-6e93-42a7e00b2207.png" width=60%>

**Keyword（発火トリガー）** を`!imo`とし、
**Snippets** に下記を登録します。

```
![img|300]({clipboard:0})
```

## 動作イメージ

![output.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184128/9e6eed76-7e5f-9a07-f218-11e7302cc98a.gif)


## おわりに

Markdownで画像を貼り付けられると、つくれるドキュメントの幅が大きく広がります。しかしMarkdownの画像リンクはすぐ忘れちゃうし、覚えていても手打ちはめんどうくさいです。

そんなときこそSnippets。Alfredじゃなくてもいいとおもうんですが、ツールの助けをかりられると、すごく楽になります。


### 参考リンク

[Obsidian｜使いかたとコツ（目次）](https://qiita.com/hann-solo/items/ac8fa75394813e8adcf4)

### 環境

ここで書いていることは、下記のバージョンで実施しました。

- Alfred 4.6.4
- Obsidian 0.14.5
- macOS Monterey 12.3.1
- MacBook Pro (14-inch, 2021)
Apacheでリバースプロキシを設定し、特定のドメイン名でのリクエストは別の端末へ振り分ける



## 概要

今まで１つドメインで１台のWEBサーバーを動かしていた。今回ドメインを増やしたが、今までのWEBサーバーとは別の端末にアクセスするようにしたかった。ブロードバンドルーターのNATでできないか調べたところ、ドメイン名で振り分ける機能はなかった。そのためブロードバンドルーターのNAT設定はそのままにし、既存のWEBサーバーのApacheでリバースプロキシを設定する事とした。

https://twitter.com/Yamine1San/status/1514461257910599681?s=20&t=j7LOYqOsyMqO-XbqO7C48g

### 最初に思いついてボツになった方法

![リバースプロキシ１.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1765459/1e0f3d6a-4475-a526-8892-ec60956a6759.jpeg)

### Apacheでリバースプロキシを設定する方法

![リバースプロキシ２.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1765459/fbe199ae-e959-f628-f889-3ed8eafd4a60.jpeg)

## リバースプロキシの設定（192.168.0.1側）

### hostsにdevelop.ddns.netを追加

今回のWEBサーバーはWindows機なので
C:\Windows\System32\drivers\etc\hosts
にdevelop.ddns.netを追加

```C:\Windows\System32\drivers\etc\hosts
192.168.0.2 develop.ddns.net
```

### Apacheの設定

#### httpd.confの確認

```apache\conf\httpd.conf
#次の３つのモジュールが有効になっている事を確認
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_http_module modules/mod_proxy_http.so
LoadModule ssl_module modules/mod_ssl.so
```
#### HTTP接続用の設定

develop.ddns.net用のVirtualHostを追加

```apache\conf\extra\httpd-vhosts.conf
<VirtualHost *:80>
    ServerName develop.ddns.net:80
    ProxyPreserveHost On
    ProxyPass / http://develop.ddns.net/
    ProxyPassReverse / http://develop.ddns.net/
    ProxyPassReverseCookieDomain develop.ddns.net public.develop.ddns.net
    ProxyPassReverseCookiePath / /
</VirtualHost>
```

#### SSL接続用の設定

develop.ddns.net用のVirtualHostを追加
HTTP接続に比べてSSLEngine、証明書、SSLProxyEngineの設定が増えている

```apache\conf\extra\httpd-ssl.conf
<VirtualHost *:443>
    ServerName develop.ddns.net:443
    SSLEngine on
    #develop.ddns.net用のLet's Encryptの証明書
    SSLCertificateFile "conf/certs/develop.ddns.net-crt.pem"
    SSLCertificateKeyFile "conf/certs/develop.ddns.net-key.pem"
    SSLCertificateChainFile "conf/certs/develop.ddns.net-chain.pem"
    SSLProxyEngine On
    ProxyPreserveHost On
    ProxyPass / https://develop.ddns.net/
    ProxyPassReverse / https://develop.ddns.net/
    ProxyPassReverseCookieDomain develop.ddns.net example.develop.ddns.net
    ProxyPassReverseCookiePath / /
</VirtualHost>
```

## 192.168.0.2側の設定

リバースプロキシ経由での接続に対応させるための特別な設定は必要ない
develop.ddns.netでアクセスされた時の記述を通常通り行えばよい

SSL証明書のSSLCertificateFileはリバースプロキシを設定している側と同じものを指定した
Permissions 0644 for 'sample_rsa' are too open.　解決方法

## 初めに
dockerコンテナの./sshディレクトリからEC2にsshキーで接続しようとしたら下記のようなエラーが発生。

```
root@???:~/.ssh# ssh -i "sample_rsa" ec2-user@ec2-??-??-??-??.ap-northeast-1.compute.amazonaws.com

The authenticity of host 'ec2-??-??-??-??.ap-northeast-1.compute.amazonaws.com (??.??.??.??)' can't be established.
ECDSA key fingerprint is SHA???????????????.
Are you sure you want to continue connecting (yes/no)? yes   

Warning: Permanently added 'ec2-??-??-??-??.ap-northeast-1.compute.amazonaws.com,??.??.??.??' (ECDSA) to the list of known hosts.
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@         WARNING: UNPROTECTED PRIVATE KEY FILE!          @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Permissions 0644 for 'sample_rsa' are too open.
It is required that your private key files are NOT accessible by others.
This private key will be ignored.
Load key "sample_rsa": bad permissions
ec2-user@ec2-??-??-??-??.ap-northeast-1.compute.amazonaws.com: Permission denied (publickey,gssapi-keyex,gssapi-with-mic).
```

## エラー
どうやらこのエラーは、`秘密鍵のパーミッションが644になってて他のユーザーからアクセスできるような権限では使えないよ！`と指摘してるようです。

```
Permissions 0644 for 'sample_rsa' are too open.
```

## 解決
以下のようにパーミッションを変更してあげれば解決できます。
ちなみに`600`は、`所有者のみ読み書き可能`という権限設定になります。

```
~/.ssh# chmod 600 sample_rsa
```

これで再度EC2に接続で成功！

## 参考記事
https://umebius.com/dev/permissions-0644-for-xxx-key-are-too-open-%E6%A8%A9%E9%99%90%E3%82%92%E3%81%A9%E3%81%86%E3%81%99%E3%82%8C%E3%81%B0%E3%81%84%E3%81%84%EF%BC%9F/
nobody(nfsnobody)ユーザとは何なのか

# nobody(nfsnobody)ユーザとは何なのか

RHEL8 のリリースノートにて、`nobodyユーザがnfsnobodyユーザから置き換わる`という内容があります
本件について、nobody,nfsnobodyユーザについてを確認し、NFSを利用しているチームでのOS移行時の影響範囲を調べます

## そもそもnobodyユーザとは

nobodyユーザとは非特権ユーザのことで、特定のシステムを動作させるためだけの権限や特定のデータに対する所有者権限のみを持たせたユーザとなります
付与されている権限はUNIXシステム上で最も低い権限のようで、rootユーザの対局となるユーザともいわれています
基本的にNFSを使う際に利用するユーザのようで、NFSの他にはCGIを実行するユーザとして使われることもあるそうです

## nobody と nfsnobody の違い

いろいろ調べてみましたが、特に違いがなさそうです
基本的にnobodyユーザを利用するシーンがNFSを利用する場合なので、
記事によってnfsnobodyユーザを利用するかnobodyユーザを利用するかがバラバラでした
非特権(最も権限が絞られている)ユーザである ということに関しては同じなので、
用途的にも同じになるのだと思います

- nobodyに関する参考 : [nobody](https://www.wdic.org/w/TECH/nobody)

nobodyに関する記事、めちゃくちゃ少ない

## NFSでの使われ方

NFS上にデータディレクトリを作成する際、ディレクトリ(とその中のファイル)の所有者はnfsnobodyユーザとして作成することが通例となっているようです
/etc/exports というファイルに`all_squash`と設定すると、 設定したnfsディレクトリ内のファイルに対するアクセスはnfsnobodyユーザでのアクセスとなり、
その他のユーザの権限による不正な書き込みや削除などからNFSディレクトリ内のファイルを守ることができます

- 参考1 : [NFSのマウント設定例](https://www.qoosky.io/techs/cd08747dff)
- 参考2 : [Linux NFSサーバの設定](http://www.kuromogu.com/ha/comp/sw/soft_linux_nfs.html)

CentOS7(RHEL7)までは、OSが自動でnfsnobody(uid:65534, gid:65534)ユーザを作成しているので、
このユーザをそのまま利用できます

## 用途が同じかつ、名前がややこしいので統合

上述の通り、nobodyユーザとnfsnobodyユーザで違いはないため、
RHEL8からはnfsnobodyユーザが作成されなくなり、nobodyユーザのみが作成されるようになります

- CentOS7 で確認

```
# cat /etc/passwd | grep nobody
nobody:x:99:99:Nobody:/:/sbin/nologin
nfsnobody:x:65534:65534:Anonymous NFS User:/var/lib/nfs:/sbin/nologin
```

- RockyLinux8 で確認

```
# cat /etc/passwd | grep nobody
nobody:x:65534:65534:Kernel Overflow User:/:/sbin/nologin
```

UID:65534, GID:65534 の nobody ユーザのみが作成されていることが分かります

## 影響について

nobodyユーザとnfsnobodyユーザの持っている権限が同じなので、
`nfsnobodyユーザでできていたことがnobodyユーザになったことでできなくなった`みたいなことが起こり得ないと判断できるため、
影響もないと判断できると思います

# 終わりに

以上、`NOBODY ユーザーが NFSNOBODY に置き換え`のリリースノートの内容について、
そもそもnobodyユーザとは何なのか から詳しく調べてみました
全く役に立たなそうですが、雑学程度にはなったのではないでしょうか

リリースノート読むの面白いですね
CloudFormationでCodeBuildを作成しようとしたら「Not authorized to perform DescribeSecurityGroups」になった

# 起きたこと

 - CodeBuildプロジェクトをCloudFormationで作るためにyamlテンプレートを作成し`AWS::CodeBuild::Project`を追加した。
 - またプロジェクトにアタッチするロールとポリシーを一緒に作りたかったので、同じテンプレートに`AWS::IAM::Role`と`AWS::IAM::Policy`を追加した。
 - 作成したテンプレートをもとにスタックを実行すると`Not authorized to perform DescribeSecurityGroups`というエラーメッセージが表示され作成に失敗した。

# 原因
## 1. 必要な`ec2:DescribeSecurityGroups`の権限が不足している。（エラー文そのまま）
  - VPC内にCodeBuildを作成する場合に必要な権限となる。
  - 他にも必要な`ec2:`のポリシーがあるが、基本的には公式が載せている[このポリシー](https://docs.aws.amazon.com/codebuild/latest/userguide/auth-and-access-control-iam-identity-based-access-control.html#customer-managed-policies-example-create-vpc-network-interface)をアタッチすればOK
  - （上記の公式ポリシーの`StringEquals`の部分をyamlで上手く指定できず困ったのだが、[こちらの記事](https://qiita.com/Ichi0124/items/414d9f9f4833ace03adf)を参考に`!Sub |`を使ってみたところ無事指定できた。）

## 2. ポリシーの作成が完了する前にCodeBuildの作成が完了してしまい、権限が無いものとして扱われた。
 - 自分の場合はこちらだった。
 - ポリシー→CodeBuildの順で確実に作成されるよう、以下のように`DependsOn属性`をCodeBuildに追加したところ、無事にCodeBuildを作成できた。
```yaml
CodeBuildProject:
    DependsOn:
      - CodeBuildIamPolicy
    Type: AWS::CodeBuild::Project
```
- ポリシー用テンプレートを作成しスタック実行した後に、CodeBuild用テンプレートを実行するやり方でもいい。

参考: [Receive "Not authorized to perform DescribeSecurityGroups" when creating new Project in AWS CodeBuild](https://stackoverflow.com/questions/52843460/receive-not-authorized-to-perform-describesecuritygroups-when-creating-new-pro)
バージョン管理をして、ファイルの変更があった時に nuxt-link で遷移しても強制的にリロードを走らせたい

# 環境・前提
Nuxt(version 2.15.8)
generate static
cache-control no-cache

## 目的・実装内容
`nuxt-link` で遷移してもファイルのバージョンが最新でなければ強制的にリロードを走らせて、最新のファイルを反映したい。
`static/json/router.json` の `version` 値が store の state( `version` ) と差分あればリロードして最新ファイルを読み込むようにする。
(generate したファイルをアップロード後に `json` の `version` を更新してアップロードすれば閲覧者は次にサイトにアクセスした際にリロードが走る)

```ruby:nuxt.config.js
plugins: [
  { src: '~/plugins/router.js', mode: 'client' },
],
```

```ruby:plugins/router.js
export default () => {
  app.router.afterEach(() => {
    // ページ遷移の度に実行する
    store.dispatch('app/fetchRouterJson');
  });
};
```

```ruby:static/json/router.json
{"version":1} // 初期値(運用時はこのファイルをアップデート)storeに保存されている値と差分あるかを判定する
```

```ruby:store/store.js
import Vuex from 'vuex';
import app from './app';

export const store = new Vuex.Store({
  modules: {
    app
  }
})
```

```ruby:store/app.js
const state = () => ({
  version: 1, // 初期値
})

const actions = {
  async fetchRouterJson({ commit, state }) {
    try {
      const results = await this.$axios.$get(`/json/router.json?timestamp=${new Date().getTime()}`);
      if (results.version !== state.version) { // stateと違えば処理を行う
        // storeに最新のバージョンを保存
        commit('setVersion', results.version);
        // リロード
        window.location.reload();
      }
    } catch (error) {
      console.log(error)
    }
  },
}

const mutations = {
  setVersion(state, payload) {
    state.version = payload;
  }
}

export default {
  namespaced: true,
  state,
  getters,
  actions,
  mutations
}
```

修正ファイルをアップロード後に、強制的にリロードしたい場合は `static/json/router.json` の `version` の値を更新してアップロードすれば差分のある閲覧者はリロードが走る。