OmniAICreator/Qiita-1.07M huggingface dataset:
情報設計ってなんだ？成果物ってなんだ？どんな良いことがあるんだ？

この記事は　[セゾン情報システムズ Advent Calendar 2021](https://qiita.com/advent-calendar/2021/sisco) 13日目の記事です。
サービスデザインチーム所属のUIデザイナーです。
デザイン工程において「情報設計」と呼ばれるものがなんなのか、どういった目的を持ち、どういった成果物を作るのか、ゆる〜くご紹介したいとおもいます。

# 情報設計ってなんだ？
一言でいうと**複雑な情報をわかりやすく伝えること**です。
例えば音楽情報にアクセスしたユーザーに対し「ビートルズ」「オアシス」「コールドプレイ」という情報が与えられたとき、「有名なUKロックバンド」と構造化すれば、ロックバンドに詳しくない人でも個々の情報を理解する手がかりになります。
そのままでは利用しにくいデータを整理整頓して、**ユーザーが目的を達成しやすくする**ことが情報設計の工程です。

# 工程としてはどのあたりなの？
ソフトウェアの開発工程としてはUI設計にあたります。
「プロダクトの画面デザインをする」場合は、取り組むべき課題が洗い出され、解決策が要件として定義されたあと、解決策を具体化する始まりの部分になります。
![Frame 1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/54497/af25f5c1-d934-bae2-6967-a9f9068fd809.png)


# 成果物ってなに？
案件の性質や参加メンバーによりけりですが、ほぼ必ず必要になるのは以下３つです。
それぞれ最終成果物である画面を作るための中間成果物に位置します。
※呼び方は現場によって色々あります。一例です。

## ディレクトリマップ＠Excel
画面のディレクトリ構造をリスト化します。
画面名称、URL、パスファイル、階層レベル、title、SEO情報など**情報の縦の関係性**を一覧化したものです。
コンテンツ要素が重要な場合はコンテンツリストを作りますが、それとは別視点の情報構造を示します。
【利点】概念やラベル・文章の一貫性、画面の抜け漏れがひと目でわかります

## サイトストラクチャ@ホワイトボードツール、Figma/XD
画面構成をツリー化またはフロー化した図表です。**情報の横の関連性**を俯瞰します。
大きくは５パターンに分かれ、プロダクトの用途に合わせて採用します。
「階層型分類構造」「ファセット型構造」「Web型構造」「ハブ＆スポークス型構造」「直線型構造」
【利点】ナビゲーションやインタラクション、呼び出し関係の整理ができます

## ワイヤーフレーム＠Figma/XD
色や意匠を施す前の**画面の設計図**です。
エリア分け、レイアウトパターン、見出しと文章量、ナビゲーションの確認、ラベル定義、などを確認します。
【利点】プロトタイプ化することで「実際に使っているかのようにテスト検証する」ことが可能になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/54497/f4e69024-1b8d-5e31-6c06-cbbad16d3bf9.png)[- Wires jpより](https://www.behance.net/gallery/67284971/Wires-jp)


# 実際に作るとどんな良いことがあるの？

**構造を見える化**することで、**ユーザーが目的を達成できるか**の評価がしやすくなります。
抽象的で曖昧だった関係性を図表として具体的に視覚化するので非常にローコンテキストになり、案件参加メンバー間の認識合わせも容易になります。

以下は実際の案件で作成したサイトストラクチャとディレクトリマップになります。
改善案件だったので、現状調査として両ドキュメントを作成したところ、課題を洗い出すことができました。
サイトストラクチャでは、コンテンツAとコンテンツBが**不適切な呼び出し関係**になっていました。
ディレクトリマップでは、**画面構成上あるべき画面がない**ことが浮き彫りになりました。

「なんとなく使いにくい」と感じていいた部分を、見える化することで、課題として認識することもできます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/54497/c47e38fe-e8b5-1fde-02f2-4d8cc2494352.png)

# まとめ
真面目につくるとガチの設計書を作るわけなので、設計書＝納品物が文化になっている場合は抵抗も大きいかもしれません。
ですが、ローコンテキストなコミュニケーションツールとして非常に有用なので、ぜひ一度作ってみてはいかがでしょうか。

以上、ご一読ありがとうございました。
JavaScriptだけでMultiple Select

普段は何かしらのライブラリを探して使ってますが、お勉強がてらJavaScriptだけで作ってみることに。

# 実装する機能
* セレクトボックスをクリックすると選択肢が表示
* 選択肢をクリックするとセレクトボックスに移動
* セレクトボックスの選択肢を×ボタンで削除可能
* セレクトボックス内 or 要素外をクリックすることで選択肢を閉じる

# 完成品
<p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="oNGYZmN" data-user="tks-hsmt" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/tks-hsmt/pen/oNGYZmN">
  Untitled</a> by Takeshi Hashimoto (<a href="https://codepen.io/tks-hsmt">@tks-hsmt</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

# 実装時のポイント① クリックイベント時の要素判定
イベントを設定した要素(addEventListenerの対象要素)を取得したい場合は「Event.currentTarget」を利用します。
イベントが発生した要素(クリックされた要素)を取得したい場合は「Event.target」を利用します。
今回だと要素外のエリアをクリックされたかを調べる際にはEvent.targetを利用しています。
逆に、表示エリアや選択エリアをクリックされた場合は要素のclassを取得したい為、Event.currentTargetを利用しています。

```javascript:handleOuterAreaClick
    document.addEventListener('click', (e) => {
      if (e?.target?.closest('.multiple-select')) return;
      this.close();
    });
```
```javascript:handleDisplayAreaClick
    const displays = document.querySelectorAll('.display-area') || [];
    [...displays].forEach((display) => display.addEventListener('click', (e) => {
      this.toggle(e.currentTarget);
    }));
```
[Event.target](https://developer.mozilla.org/ja/docs/Web/API/Event/target)
[Event.currentTarget](https://developer.mozilla.org/ja/docs/Web/API/Event/currentTarget)

# 実装時のポイント② 親要素の取得
今回クリックされた要素の親を遡って、その配下のエリアを取得して表示を変える仕組みにしています。
要素の親を検索する為、Element.closest()を利用しています。
※IEは対応していません。

```javascript:toggle
    const select = element.closest('.multiple-select').querySelector('.select-area');
    if (select.childElementCount === 0) return;
    if (!select.classList.contains('is-open')) {
      this.selectAreaPositioning(element);
    }
    select.classList.toggle('is-open');
```
[Element.closest()](https://developer.mozilla.org/ja/docs/Web/API/Element/closest)

# 実装時のポイント③ クリックイベントの伝播の抑止
セレクトボックスはクリックイベントを監視し、選択肢の表示/非表示を行います。
また、セレクトボックス内の選択肢の×ボタンをクリックした際にはその要素を選択肢に戻します。
この時何も意識しないと、選択肢の×ボタンをクリックした際に、親要素のセレクトボックスにイベントが伝播してしまい、
選択肢が閉じたり開いたりしてしまいます。
そうならないように、×ボタンクリック時にはEvent.stopPropagation()を利用しています。

```javascript:handleDeleteButtonClick
    const buttons = document.querySelectorAll('.delete-button') || [];
    [...buttons].forEach((button) => button.addEventListener('click', (e) => {
      e.stopPropagation();
      this.switching(e.currentTarget);
    }));
```

[Event.stopPropagation()](https://developer.mozilla.org/ja/docs/Web/API/Event/stopPropagation)
【Rails】日時の指定方法について【Time】【Date】【ActiveSupport】

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

## 今回の疑問点
　今回の疑問点は、

　　_日時の指定方法について_
　
　です。　

 　以前、特定期間の投稿を表示させる機能を実装している際に上記について疑問を持ちました。

## 疑問点についての解説

###結論
　`Time`や`Date`とActiveSupportを使用することで指定することができる。（指定方法は他にもある）

###記述例

```.rb
time = Time.now  # 現在

time + 10  # 10秒後

time + 10.seconds  # 10秒後

time + 10.minutes  # 10分後

time + 10.hours  # 10時間後

time + 10.days  # 10日後

time + 10.weeks  # 10週後

time + 10.months  # 10ヶ月後

time + 10.years  # 10年後

time.tomorrow  # 翌日

time.yesterday  # 昨日

time.next_month  # 翌月

time.prev_month  # 前月

time.next_year  # 翌年

time.prev_year  # 前月

time.ago(2.months)  # 2ヶ月前

time.since(2.months)  # 2ヶ月後

time.ago(2.years)  # 2年前

time.since(2.years)  # 2年後

time.beginning_of_month  # 月初

time.end_of_month  # 月末

time.beginning_of_year  # 年初

time.end_of_year  # 年末

Time.now.end_of_quarter #　四半期末

time.next_occurring(:wednesday)  # 次の水曜日

time.prev_occurring(:wednesday)  # 前の水曜日

time.prev_week(:monday)  # 先週の月曜日

time.next_week(:monday)  # 来週の月曜日

time.year  # 年 を取り出す

time.month  # 月 を取り出す

time.day  # 日 を取り出す

time.hour  # 時 を取り出す

time.min  # 分 を取り出す

time.sec  # 秒 を取り出す

time.wday  # 曜日 を取り出す（0:Sunday, 1:Monday, ……)

time.yday  # 1/1からの経過日数

time.all_day # 00:00:00~23:59:59

time.all_week # 1週間

time.all_month　　# 1ヶ月間

time.all_quarter # 四半期の間

time.all_year # 1年間


(Time.current.at_end_of_day - 6.day).at_beginning_of_day)..(Time.current.at_end_of_day)  # 1週間

```
`Date`は年月日
`Time`は年月日時（分秒含む）


## まとめ
最後にポイントをまとめます。

- `Time`や`Date`とActiveSupportを使用することで指定することができる
- `Date`は年月日、`Time`は年月日時（分秒含む）を取得することができる
文章構造化のすゝめ

以前、オンライン会議で共通認識を取るための方法のひとつとして、[ポンチ絵](https://qiita.com/toru-kadowaki-dmm/items/8614cfde33da0818a614) について投稿しました。
図を描くことによるメリットはたくさんあると考えていますが、それだけでは他者には伝わりません。
当然、**文章** を書く必要があります。

今回は、他者に伝わりやすい文章を書く方法のひとつとして、**文章の構造化** というテーマについて書いてみました。

# 文章の構造化とは？
"文章の構造化" というキーワードで検索すると色々な手法が出てきます。

私が特に意識しているのは、
**特に伝えたい点を考え（トピックの選定）、関連する事柄をまとめて理解しやすいようにトピックを並び替える（アウトラインの設計）こと** です。

ここからは、以下の３点のポイントについてまとめてみます。
・**フォーマット**
・**トピックの選定**
・**アウトラインの設計**

**１．フォーマット**
必須ではないと思いますが、自分なりのフォーマットやルールを決めておいた方が、品質は安定する気がしています。
私は以下のようなフォーマットで書いています。

```abap:フォーマット
* トピック
- 文章
- 文章
  - 文章（サブ）
- 文章

* トピック
- 文章
- 文章
```

**２．トピックの選定**
意識しているのは、**似たトピックは1つにまとめる** という点です。
そこで大事になるのは、**トピックの抽象度** だと考えています。
少し極端ですが、例を上げてみます。テーマは「オンプレからクラウド環境への移行失敗例」とします。

```abap:トピックのまとめ方が甘い
* イニシャルコストの増加
- データ移行にかかる費用の見積もりが甘かった

* ランニングコスト（サーバコスト）の増加
- 適切なアーキテクチャを設計できずオーバスペックとなった

* ランニングコスト（運用人員増強による人件費）の増加
- クラウド環境の経験が乏しく運用人員増加が必要になり人件費が高くなった

* セキュリティ対策への負荷の増加
- オンプレ環境では特に意識しておらず考慮する点が増えた

* 計画メンテナンスへの考慮漏れ
- クラウド事業者によるメンテナンスの考慮が必要になった

* 環境が乱立しサーバ管理ができなくなる
- 簡単にサーバが追加できるようになり、環境が乱立して一元管理が難しくなった
```

```abap:トピック選定の修正後
* コストの増加
- イニシャルコスト
  - データ移行にかかる費用の見積もりが甘い
- ランニングコスト
  - サーバコスト：適切なアーキテクチャを設計できずオーバスペックになったため
  - 人件費：クラウド環境の経験が乏しく運用人員増加が必要になったため

* 運用負荷の増加
- セキュリティ対策
  - オンプレ環境では特に意識しておらず考慮する点が増えた
- 計画メンテナンスへの対応
  - クラウド事業者によるメンテナンスの考慮が必要になった
- サーバの一元管理
  - 簡単にサーバが追加できるようになり、環境が乱立して一元管理が難しくなった
```

いかがでしょうか？

一見すると、そこまで違いがない（もしくは修正前の方が見やすい）ように見えるかもしれません。
しかし、修正前の書き方で書き進めると、トピックがどんどん増えて、結果まとまりの無い形になってしまいます。

これを防ぐために、**トピックは内容が分かりつつも具体的にしすぎない程度のもの** にした方が良いと考えています。

（ただし、会議や会話のメモを取る場合、話がどんどん展開していくため、この方法は難しいかと思います。
そういった場合、いきなり綺麗にまとめようとせずに、後でまとめる形が良いかと思います）。

**３．アウトラインの設計**
アウトライトと書くとやや大げさですが、**トピックの順番** も意識したいです。
単純に、**関連があるトピックは続けて書く** ほうが理解しやすいと思います。

例えとして、会議の議事録を取る場合を例にあげます。
この時、発言の順番を重要視するのではなく、後で理解しやすいように関連するトピックで順番を並べ替えます。
もちろん、誰がいつどんな流れで発言したかを残しておく必要がある場合は別ですが、そうでない場合、発言の順番には余りこだわらないようにしています。

# まとめ
ここまで書いてきましたが、他者に伝わりやすい文章を書くのは本当に難しいなと日々感じています。
変に文章をまとめることで、大事な要素が抜けたり、自分のバイアスにより内容が違ったものになってはいけないので、そこには細心の注意を払っています。

リモートワークにより以前にも増してドキュメントの重要性が高まっていると思います。
今回の記事が少しでもヒントになれば幸いです。


P.S. DMM.comプラットフォーム事業本部のその他の記事もぜひ見ていってください！

https://qiita.com/organizations/dmmcompf
SQL備忘録

## テーブルのバックアップ手順

|項番|手順|コマンド|
|----|----|----|
|0|<b>bk_sample_tbl</b>を作成して、<b>sample_tbl</b>のBKを取る例|--|
|1|BK用のテーブル作成|create table <b>bk_sample_tbl</b>|
|2|BK用のテーブルに挿入|INSERT INTO  <b>bk_sample_tbl</b> SELECT*FROM <b>sample_tbl</b>;|

## 集約関数 と 分析関数

|項目|説明|例|
|----|----|----|
|<b>集約関数</b>|行を一つにまとめてて出力する|GROUP BY|
|<b>分析関数 (ウィンドウ関数)</b>|行ごとに集計結果を出力する|ROW_NUMBER() OVER()|

<b>ポイント</b>
・分析関数にはover()が必要。
・over()の有無で集合関数、分析関数のどちらにもなれる関数がある。

```:集合関数と分析関数をCOUNT()で比較
集合関数
SELECT COUNT(*) FROM test_orders;

        COUNT(*)
----------------
               6

分析関数
SELECT order_id, item, COUNT(*) OVER () FROM test_orders;

  ORDER_ID ITEM       COUNT(*)OVER()
---------- ---------- --------------
      1001 Apple                   6
      1005 Banana                  6
      1010 Banana                  6
      1021 Apple                   6
      1025 Apple                   6
      1026 Apple                   6
```
https://qiita.com/tlokweng/items/fc13dc30cc1aa28231c5

## COUNT(*)でエラーが出た際の実例
https://qiita.com/YuichiTanaka007/items/e00ab09866ed86504b02
## GROUP BY
GROUP BYで指定した列において、重複している行を集約する。
### 挙動
* GROUP BY「name」とした例
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/637823/f6b702b8-c33e-6bb8-2812-39564d1542bd.png)

```sql
SELECT
    name
FROM
    table_sample
GROUP BY
    name
```	
### WHERE区に集計関数を含めない理由
* GROUP BYがまだ呼ばれていないので、WHERE内で集計関数を使えない

### 実行順番
①FROM
②WHERE
③GROUP BY
④HAVING
⑤SELECT
⑥ORDER BY

### HAVING
* GROUP BYの条件を指定

ダブりカードを抽出する例
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/637823/df72d173-983b-11eb-5236-feef47852519.png)


```sql
SELECT
    name,
    count(name)
FROM
    table_sample
GROUP BY
    name
HAVING
    count(name)>1
```
## RANK()
RANK() over(order by ～）

|項目|説明|
|----|----|
|RANK|同率があった場合順位は同じになり、その次は順位を飛ばします。<br>（1位、1位、3位・・・）||
|DENSE_RANK|同率があった場合順位は同じになり、その次は順位を飛ばしません。<br>（1位、1位、2位・・・）||
|ROW_NUMBER|同率があっても同じ順位にはならず、順位をカウントします。<br>同率があった場合の順位は常に同じとは限りません。||

https://www.projectgroup.info/tips/SQLServer/MSSQL_00000044.html

## UNION 
SELECTの結果を結合できる

```sql
SELECT
 a,
 b,
 c
FROM
 sample_db
WHERE a > 0
UNION
SELECT
 a,
 b,
 c
FROM
 sample_db
WHERE b > 0
```
HAVING句にサブクエリを使うことで最頻値を求める方法

## はじめに

HAVING句を使って、サブクエリを使うことで最頻値を求める方法を紹介します。

実行結果はこちらで確認できます。

https://www.db-fiddle.com/

## テーブル

例題として、以下のような、大学の卒業生の初任給が情報（`graduates`テーブル）があるとします。

| id  | name    | income |
| --- | ------- | ------ |
| 1   | sampson | 400000 |
| 2   | mike    | 30000  |
| 3   | white   | 20000  |
| 4   | arnold  | 20000  |
| 5   | smith   | 20000  |
| 6   | rolens  | 15000  |
| 7   | hadson  | 15000  |
| 8   | kent    | 10000  |
| 9   | becker  | 10000  |
| 10  | scot    | 10000  |

データは下記を参考にしています。

https://codezine.jp/article/detail/652

Schema (MySQL v8.0)

```sql
 CREATE TABLE graduates (
        id INT NOT NULL PRIMARY KEY auto_increment,
        name TEXT NOT NULL,
        income INT NOT NULL
    );

    INSERT INTO graduates VALUES
        ( 1, 'sampson', 400000),
        ( 2, 'mike',       30000),
        ( 3, 'white',      20000),
        ( 4, 'arnold',     20000),
        ( 5, 'smith',      20000),
        ( 6, 'rolens',     15000),
        ( 7, 'hadson',   15000),
        ( 8, 'kent',      10000),
        ( 9, 'becker',   10000),
        (10, 'scot',     10000);
```

データを眺めると、`20000`が3回、`15000`が3回とこの2つが最頻値です。

このデータの最頻値を求めるクエリはどう書きますか？

## Query

```sql
SELECT income, COUNT(*) AS cnt
FROM graduates
GROUP BY income
HAVING COUNT(*) >= ALL ( SELECT COUNT(*) AS cnt FROM graduates GROUP BY income )
```

グループ化されたものを、HAVINGでカウントしています。

これだけだと、

```sql
SELECT income, COUNT(*) AS cnt
FROM graduates
GROUP BY income
HAVING COUNT(*)
```

| income | cnt |
| ------ | --- |
| 400000 | 1   |
| 30000  | 1   |
| 20000  | 3   |
| 15000  | 2   |
| 10000  | 3   |

こんなデータになりますよね？

それをALL句でtrueになるものだけ呼び出されて、以下のような結果になってます。

## Results

| income | cnt |
| ------ | --- |
| 20000  | 3   |
| 10000  | 3   |

## 参照

https://www.amazon.co.jp/dp/4798157821/

111 - 113p 

## アウトプット100本ノック実施中

https://qiita.com/ryosuketter/items/713474dbd009870e4103
kintoneの非公開APIの調べ方

# :warning: ATTENTION

ここで紹介するAPIは非推奨なAPIです。API使用による事故、トラブルなど一切の責任は負いません。

# はじめに

kintoneには非公開のAPIがたくさんあります。
（中の人がこの話題に触れていいのかわからないですが、あるのは事実なのでw）

当然、非公開なのでdevnetには記載がありません。

でも調べようと思えばどんなリクエストを投げればよいかとかはわかります。

ということで、BB個人の記事ってことでその調べ方を少しだけご紹介します。


# 手順

手順的にはこんな感じです（BB調べ）

1. 開発者ツールを開く
1. ネットワークタブを開く
1. 検索の絞り込みを **「json」** にする
1. 普通にkintoneを操作する
1. ネットワークタブに表示されるリクエストを読み解く
1. URLやリクエストプロパティがわかる！

ってことで、サンプルで**「ピープルをフォロー/フォロー解除するAPI」**を例に説明します

## 開発者ツールのネットワークタブを開く

ここは簡単なので割愛します。
![スクリーンショット 2021-12-13 0.55.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/153051/d9e0cd65-39d8-07f2-7356-d62f7679ad2a.png)

## 検索の絞り込みを **「json」** にする & 表示をリセットする

上の方に「Filter」があるので、そこに`json`と入力します。そうするとjsonに関係するものだけが表示されるので、リクエストが探しやすくなります。
![スクリーンショット 2021-12-13 0.55.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/153051/ac7f02a2-1ff8-0712-ac50-5c855eb9b13c.png)
そのFilter近くの`ø（○にスラッシュ）マーク`で履歴をリセットすることが可能です。

## 普通にkintoneを操作する

その状態で調べたいリクエストの操作（今回だとピープルをフォロー）すると、ネットワーク上にリクエストが表示されます。
![スクリーンショット 2021-12-13 0.55.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/153051/0cbc41da-d2fe-2bdd-ca23-2020318a5a16.png)
はい、これがお目当ての非公開APIです！

## ネットワークタブに表示されるリクエストを読み解く

表示されたリクエストをクリックすることで、`Header`や`Payload`が確認できるので、URLやリクエストパラメータがわかります。今回のピープルフォローAPIだとこんな感じでした。

```JavaScript
const url = 'https://sample.cybozu.com/k/api/people/user/subscribe.json';
const method = 'POST';
const params = {
  subscribe: true, // trueだとフォロー、falseだとフォロー解除っぽい
  userId: 4, // フォローしたいユーザーのUserID
};
```

# あとは調べた情報でAPIを叩くべし

新しいユーザーを追加して（userIDは5）、関係ないページでAPIを叩く！

```JavaScript
const url = kintone.api.url('/k/api/people/user/subscribe.json');
const method = 'POST';
const params = {
  subscribe: true,
  userId: 5,
};

kintone.api(url, method, params).then(resp => console.log(resp)).catch(err => console.error(err));
```
![スクリーンショット 2021-12-13 1.14.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/153051/3167e654-d66d-fd70-8635-58e6ba735b21.png)

ちゃんとフォローしてる！！
![スクリーンショット 2021-12-13 1.14.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/153051/ccdf0162-0ea7-95e4-3233-0609722e4810.png)

# 他にも
`/k/api/space/thread/post/list.json`で **スレッドの投稿内容が取得** できそうだったり、
`/k/api/space/my.json`で **ログインユーザーの参加中のスペースが取得** できそうだったりいろいろできそうです。

# おわりに

実は前々からこの方法で非公開APIを調べて **スレッドの投稿内容のアーカイブ（レコードへコピー）** や **外部サービスからピープルに投稿** などいろいろやってましたw

いや、ほんと助長しているとかじゃなく、アドベントカレンダーの12日が空いていたので埋める目的で即席で書いたネタ記事ってことでよろしくです。
> :warning: ATTENTION
> ここで紹介するAPIは非推奨なAPIです。API使用による事故、トラブルなど一切の責任は負いません。

（じゃあ記事書くなよなってのはナシでお願いしますw）

それでは！≧(+･` ཀ･´)≦
AWS CLIで Web サイトを構築、管理、運用する(13日目)

13日目！
折り返し地点到達です！！

今回は、Web サイトを公開している方で気にならない方はまずいないと思われる(個人の感想です）、アクセス数(リクエスト数）を取得してみます。

# 13日目の要約
アクセス数（リクエスト数）を取得するよ！

# AWS CLI の準備
このあたりをみて、好きなバージョンとお使いのOSにあった環境設定をしてくださいね。
なんなら、 AWS CloudShell で実行するのも楽でよいと思います。
この記事シリーズは、AWS CloudShell で実行し、実行例を載せています。

バージョン１
https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/install-cliv1.html

バージョン２
https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/install-cliv2.html

# 概要
CloudWatch でアクセス数(リクエスト数)に関するメトリクスを取得、確認するよ！

# さあ、やってみよう！
CloudFront のアクセス数(リクエスト数）は、CloudWatch の「Requests」メトリクスが該当します(とはいえ、リクエスト数なので、ガチアクセス数かと言えば微妙なところですが💦)。

## メトリクスを取得する
さくさくっと取得してみましょう！
アクセス数が少なそうであれば、確認のために、何回かアクセスしてみたり、 while あたりを使って curl コマンドをぶん回しまくればいいとおもいます(雑

cloudwatch get-metric-statistics コマンドを使用します。
以下の例では現在時刻から１時間前までの間の 5分(300秒)ごとのリクエスト数の合計値を取得するようにしています。

```sh
ENDTIME=`date +%Y-%m-%dT%H:%M:00`
STARTTIME=`date --date "${ENDTIME} 1 hours ago" +%Y-%m-%dT%H:%M:00`

aws cloudwatch get-metric-statistics --namespace "AWS/CloudFront" \
--metric-name Requests  \
--dimensions Name=DistributionId,Value=<CloudFront ディストリビューションID> Name=Region,Value=Global  \
--statistics Sum --start-time ${STARTTIME}Z --end-time ${ENDTIME}Z  \
--period 300 --region us-east-1
```

成功すると以下のように、 リクエスト数が出力されます。Timestamp の並び順が時系列順ではないので注意ください。

```json
{
    "Label": "Requests",
    "Datapoints": [
        {
            "Timestamp": "2021-12-12T15:12:00+00:00",
            "Sum": 30.0,
            "Unit": "None"
        },
        {
            "Timestamp": "2021-12-12T15:47:00+00:00",
            "Sum": 29.0,
            "Unit": "None"
        },
        {
            "Timestamp": "2021-12-12T15:22:00+00:00",
            "Sum": 30.0,
            "Unit": "None"
        },
(略)
]
```

もし、Timestamp を時系列順にしたいのであれば、 以下のように jq コマンドと組み合わせると良いでしょう。

```sh
ENDTIME=`date +%Y-%m-%dT%H:%M:00`
STARTTIME=`date --date "${ENDTIME} 1 hours ago" +%Y-%m-%dT%H:%M:00`

aws cloudwatch get-metric-statistics --namespace "AWS/CloudFront" \
--metric-name Requests \
--dimensions Name=DistributionId,Value=<CloudFront ディストリビューションID> Name=Region,Value=Global  \
--statistics Sum --start-time ${STARTTIME}Z --end-time ${ENDTIME}Z  \
--period 60 --region us-east-1 | jq '.Datapoints | sort_by(.Timestamp)'
```

これで、CloudWatch メトリクスで記録されている値を AWS CLI で取得できるようになりました。

# まとめ
コマンドでリクエスト数を出力することで、例えば値を報告すうる際の抽出に使えたりするのではないでしょうか。
CloudFront に限らず、各種メトリクスが取得できますので、運用報告などの場面でも発揮できると思います。

* 今回使ったコマンド
 * cloudwatch get-metric-statistics
[Oracle Cloud] Cloud ShellによるOracle DBCSの接続

#はじめに
Oracle Cloud Infrastructure Cloud ShellでSQL*Plusを使用してOracle Database Cloud Service（DBCS）に接続してみた。この手順を試すための前提条件は、パブリックIPアドレスを持つOracle Database Cloud Service Instanceのプロビジョニングと、基本的なLinuxシェルのスキルです。

Cloud Shellは、Oracle Cloud ConsoleからアクセスできるWebブラウザベースのターミナルで、すべてのOracle Cloud Infrastructureユーザーが利用できます。

# DBCSインスタンスにインターネットからの接続を許可するようにセキュリティリストを変更

- Virtual Cloud Network -> DB InstanceがプロビジョニングされているSubnetに移動
- セキュリティリストを更新し、ポート1521でインターネットからの接続を許可します。(ネットワーク・セキュリティ・グループでも可）


# Database Cloud ServiceからDB接続情報を取得
Bare Metal, VM and Exadataにアクセスし、接続先のDatabaseをクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/108635/15b4111b-7130-200d-159b-76a8a34d25e4.png)

「DB接続」をクリックして、形式「長」の接続文字列をコピーします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/108635/2c4e1574-59f0-2909-96ae-809cec798615.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/108635/4ac25a42-17be-0dab-a2b8-0207b174947d.png)


HOSTパラメータを、プライベートIPアドレスではなくパブリックIPに変更します。


# コンソールから Cloud Shellを起動

Oracle Cloud Infrastructureのテナントにサインインし、Consoleヘッダーのコマンドプロンプトアイコンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/108635/78c96b3c-7215-c1c4-d59a-c676e696b33c.png)

# Cloud ShellでのTNSNAMES.oraファイルの作成
Cloud Shell上でtnsnames.oraファイルを作成します。
<< DB_NAME >> = <<長い接続文字列>> の形式で長い接続文字列を貼り付け、ファイルを保存します。

Cloud Shell上での環境変数TNS_ADMINをtnsames.oraファイル配置のディレクトリに設定します。
export TNS_ADMIN=<<location_for_tnsnames.ora_file>>

# Cloud Shellを使用してSQL*PlusでDBに接続
sqlplus system/{パスワード}@{接続文字列}を実行します。

```
sqlplus system/Welcome1#Welcome1#@db19c

SQL*Plus: Release 21.0.0.0.0 - Production on Sun Dec 12 15:15:03 2021
Version 21.4.0.0.0

Copyright (c) 1982, 2021, Oracle.  All rights reserved.

Last Successful login time: Sun Dec 12 2021 15:00:11 +00:00

Connected to:
Oracle Database 19c Standard Edition 2 Release 19.0.0.0.0 - Production
Version 19.12.0.0.0

SQL> 
```

# おわりに
OCI Cloud Shell は、sql*plusでの簡単なアクセスを提供します。
パブリックIPを持たないインスタンスには参考情報のデータベース・ツールやBastionサービスを使用することで接続可能です。

## 参考情報

- [OCI データベース・ツールでオラクルDBへの接続方法](https://qiita.com/liu-wei/items/1d4ed7477a28b794a56c)
- [[Oracle Cloud] Bastionサービスを使ってみた (2021/12/13)](https://qiita.com/kenwatan/items/840ec56153aaefcaefe4)
- [[Oracle Cloud] Bastionサービスを使ってOracle DBCSの接続 (2021/12/13)](https://qiita.com/kenwatan/items/d92d411bb819dc60d52a)
そんなことはない! ==> cbindではNULLとデータフレームを結合できない

cbindではNULLとデータフレームを結合できない
https://qiita.com/kattsu2003/items/7d0395d69fc7ca32556d
とあったが，そもそもそんなことはないし，もっとちゃんとした解決法があるよという話。

# 解決法

根本的には，「データフレームの結合は merge を使いましょう」ということ。

今回は，列方向の結合ということで，元記事は，「それぞれのデータフレームの行は同一サンプルのもの」ということが前提のようだ。

まあ，そいういうこともあるが，一般的には，それぞれのデータフレームの行は，同じ対象者についてのデータではないかもしれないし，幾つかの対象者のデータは欠損しているかもしれない。というのがよくある話。

そこで，最終的な解決法は「各データフレームに共通する何らかのキー（最もあり得るのは，データの個人番号など）を基準として結合する」ということではないか。

例として，以下のようなデータフレームを考えよう。それぞれのデータフレームは共通する項目 id を含む。それぞれのデータフレームにすべての id のデータが存在する訳ではない。

```R 
> df1= data.frame(id=c(1, 2, 3, 4), x=c(3, 2, 1, 3))
> df2 = data.frame(id=c(1, 2, 4, 5), y=c(1, 3, 2, 4))
> df3 = data.frame(id=c(2, 4, 5), z=c(1.3, 3.5, 1.5))
> df1
  id x
1  1 3
2  2 2
3  3 1
4  4 3
> df2
  id y
1  1 1
2  2 3
3  4 2
4  5 4
> df3
  id   z
1  2 1.3
2  4 3.5
3  5 1.5
```

3つのデータフレームは，共通する id という変数で「マージすべき」ものである。

マージする種（基）となるのは（何でもよいが）最初のデータフレームとしよう。これに，他のデータフレームを付加するわけだ。

```R
> dfall = df1
```

マージされるデータフレームは，リストとして列挙しておく（ここ！だいじ。c() ではなく list()。実際，私も，最初ハマった😂）

```R
> dfs = list(df2, df3)
```

実際の結合（マージ）は，以下のような for ループで行う。マージする種に他のデータフレームを順次マージしていくわけである。

```R
> for (i in dfs){
+     dfall = merge(dfall, i, all=TRUE)
+ }
```

出来上がるデータフレームは以下のごとし。
それぞれのデータフレームに存在しない id の変数には NA が割り当てられている。

```R
> dfall
  id  x  y   z
1  1  3  1  NA
2  2  2  3 1.3
3  3  1 NA  NA
4  4  3  2 3.5
5  5 NA  4 1.5
```

merge のオプションは all=TRUE 以外もありうるだろう。

# なぜこのような記事を書いたか

この qiita は開かれた掲示板（古!今はなんていうのかな）のはずなのに，記事に対してコメントしても，元記事投稿者がなんのコメントも返さないということがよくある（実によくある）。

なんでだろう。公衆の面前で恥をかかされたと憤っているのだろうか。

こちらとしては，少しでもいい情報（より正しい情報）を発信できるようにと思っているだけなんだけどなあ。
駄文：久しぶりのC++

# 久しぶりに

大昔いじっていたソフトのエンハンスをしてみた。
最初出したのが2003年だったらしい。
そのころ、Windows CEなるOSのものがあってWindows APIでコーディングをしていた。
Win32APIをクラスラップしたり、http周りもWinSockをやったりして実装してた。
2017年あたりまで修正したりしていたものだけど、

- httpsまでは実装してられない。
- CE自体も死んでるんで使っている人もおらんだろう

ってことで、放置してた。

ふと最近同じようのものをAndroid向けにKotlin+WebView+React実装してみた。
その際、画面デザインとか機能を調べるために古いソースを引っ張り出してビルドしてみる気になった。

今更https周りをたたくのはやってられないので、C++のhttpsクライアントを探してみたところ、[libcurl](https://curl.se/libcurl/)がVisual Studio 2019でもビルドできるらしいということなので早速入手してみた。
プロジェクトファイルでちょっと警告されたような気もするが、サクッとビルドできた。

後は、元のソースのhttp周りのクラスをWinSockからlibcurlにサクッと置き換えて完成。
＃途中、置換するたびにSJISのコードがUTF-8に変換されてビルドが通らんとかいろいろあったが。

完成したってことで興味本位で行数を調べてみたら、こんな感じ。
結構書いているもんだ。
今だとできないだろうね。

| |行数|ファイル数
|--|--:|--:
|C++|22,225|101
|C|726|9
|H|7,554|104

libcurlをざっくり眺めたらWINDOWS_WCEとか使っていてWindows CEでもビルドができなくはないかも。
ただ、ビルド環境が手元にないしで未着手。どうしたもんかね。

これだけだとあれなので、libcurlのざっくり使うものを書いておこう。

## libcurl関数概要

|関数名|概要|
|--|--|
|curl_global_init|初期化
|curl_easy_init|CURLを取得
|curl_easy_setopt|パラメターを指定
|curl_easy_perform|http処理を実行
|curl_easy_cleanup|CURLを解放

## curl_easy_setoptパラメーター概要

|パラメーター|概要|
|--|--|
|CURLOPT_CUSTOMREQUEST|httpメソッドを指定
|CURLOPT_URL|URLを指定
|CURLOPT_HTTPHEADER|httpヘッダーを指定
|CURLOPT_POSTFIELDS|request-bodyを指定
|CURLOPT_WRITEFUNCTION|受信関数
|CURLOPT_WRITEDATA|受信関数のユーザーデータ
|CURLOPT_HEADER|httpヘッダーも受信関数で受け取る

## 例

```cpp
// 呼び出し
Writer writer;
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &writer);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeToUserData);
```

```cpp
//受信関数
static size_t writeToHandle(char* ptr, size_t size, size_t nmemb, void* userdata)
{
    // NOTE: CURLOPT_WRITEDATAで渡した情報をキャストで取得
    Writer* writer = (Writer*)userdata;
    // NOTE: メンバー関数を呼び出して......
    return handle->write(ptr, size * nmemb);
}
```
驚異のFANZA女優検索のTOPページのメイン部分のこだわり

この記事は [驚異のFANZA女優検索](https://fanza-actress-bust-search.azurewebsites.net/) Advent Calendar 2021 の 13 日目の記事です。

# メイン
ナビゲーションバーの次はTOPページのメイン部分について。先ず検索対象はテキストボックスに入力された値ではなく、実際の検索しているものの説明を書くことにした。つまりテキストボックスに100と入力した時は、検索対象は「100cm」と表示されるし、100+と入力した時は「100cm以上」と表示される。まあ、こうしたところで「あー。+は以上なんだ」と気づく人はいないと思うが、それでも表示に機能の説明を書いておくことは大事かなと思ってそうしてみた。また対象女優は、女優データの数によって変わることなので最新のデータ取得日を書くようにした。一応毎日更新しているはずだが、データ取得日を見ればいつ更新したか分かる。

#表示数
次が表示の設定。見ているデバイスの画面サイズにもよるので表示件数の最適な数は難しい。というか基準がないと決めようがないので、自分が開発に使っているのメインパソコンで一覧が画面内で収まる25をデフォルトにしている。50だと相当大きなディスプレイを使っていない限りスクロールバーなしで50人出ることはないし、10は大体のデバイスで少ないと思うのでまあ妥当な数字かと思う。

#並べ替え（名前順、バスト順）
並べ替えは一番最初は深く考えず名前順で作った。まあ、あいうえお順は学校などでもなじみ深いので、これ何順だ？？と思う人は少なく悪くないと思う。次に、自分が欲しかったバスト順を作ってみた。が、実際にバスト順でならべてみると、なんというかがっかり感があった。自分が見たいのはバストの大きい人ではあってもバストの大きい順に並べた結果を見たかったのではなかったことに作ってみて気づいた。実際にバスト順で並べてみると意味が分かるかもしれない（世の中には色々なリビドーを持った人がいるので、むしろテンションが上がる人も居るかもしれない。それは単純に僕とは合わないというだけで何ら否定するものではないし、むしろ誰かの役にたつのであればハッピーだと言える）。

#並べ替え（作品順、新作順、デビュー順）
次は単に自分が開発で便利なので作った作品順だ。正直これほど沢山の作品を出している人がいることに驚いた。80+で検索して作品順で並べ替えるとTOPはなんと2700作品を超えている。。そして、最後に作ったのが新作順とデビュー順だ。内部的にはその女優が単体で出た作品の発売日が一番古い物をデビュー、一番新しいものを新作と定義している。厳密にはちょっと違うが、並べ替えの所に正しく「単体作品発売日」と書いても分かりづらいし、まあ大体はそれっぽい値になっているのでこれで良しとしている。ソートを新作順にすると、新作が出る度にトップにくる女優が変わるのでこのサービスによく来る人にはオススメの並べ替えだ。（そう思って、最近ソートの横にヘルプ機能を足してこの内容を書いておいた。これは完全にこのAdvent Calendar を書いたことが良い方向に繋がっている）

#表示サイズ
開発当初は、表示件数、並べ替えの隣に、さらに表示サイズのコンボボックスが作っていたが、現在は無くした。最初に作った時はサイズ小だけで画像も小さかった。というのも最初は画面に沢山の情報を並べられることを重要視して作ったので、画像は顔認識するよりはほぼアイコンだった。ある時ふと思って画像大（つまり現状のサイズ）を作ったらそれが凄く良くて、その時に初めてこのサービスでは顔はかなり重要な要素だと感じた。暫くは表示サイズ大小を選べるようにしていたが、現在のサイズこそが最低限で、もう小は意味がないと思った無くした。

#プロフィール
写真以外に出す女優のプロフィールについて。最初からサイズとカップと名前はマストだと思っていた。それと外部リンクを作りたかったので、作品数も出したいと思っていた。レイアウト的に名前と作品数は並べたかったのだが、この2つは横サイズが動的に変わる上、それぞれ結構文字数が多いケースもある。作品数も殆ど3桁だが、実は4桁まで考慮しなくてはならないし、名前は「源すず（月奈リカ、綾瀬ゆうか、北島りな、三須みずほ）」のように改名を繰り返してそれが全部表記されているような人がいたりする。ただ5文字以内の人が95%以上なので、5文字までの名前は切らずに出すようにしたかった。つまり1行で名前は5文字までは普通に表示、6文字以上なら…にする。作品数は4桁であってもはみ出さないようにするためにレイアウトを工夫した。それと新作がある女優が分かるようにしたかったので、まだ発売していない作品がある場合は作品数の背景を黄色にした。

#デビュー日と新作日
プロフィールにデビュー日と新作日を入れようしたが結局止めた。まず日付は色々やってみたが、8桁表記(xxxx/xx/xx)だと文字として結構長い。だけど6桁表記(xx/xx/xx)はなんか嫌だった。あんまりうまく説明出来ないが、6桁はなんか弱く感じた。情報として目が行きにくい感じがする。出すなら8桁が良い。が、8桁は長い。ましてや出したい日付情報は2つある。これを2行に分けると今度はプロフィール情報が4行になり縦に長すぎる。どう表示しても望む形にならなかったので、最終的には出さないとした。言い訳程度にマウスカーソルを合わせた時はツールチップで表示するが、スマホでは使えないしイマイチな方法だ。
BurpのIntruderを遅延開始する方法

これはシーエー・アドバンス Advent Calendar 2021 13日目の記事です。

シーエー・アドバンス（CAAD）技術統括本部セキュリティチームの @syk_nakayamaです。
社内（グループ会社含む）サービスの脆弱性診断に関わる業務を行っています。
業務ではBurpSuiteという診断ツールを使用しているのですが、BurpSuiteの機能の一つであるIntruderを使用する際に「○○時間後に開始させたい」というようなことがあります。
その方法について解説していきます。

まず使用するBurpですが、v2021.4.3を使用してください。
v2021.5.1からIntruderの仕様が変わり、機能が削除されてしまっているため、古いVerのBurpでしか該当する機能が使用できません。
古いVerのBurpは以下からダウンロードできます。
https://portswigger.net/burp/releases/professional-community-2021-4-3

次に、v2021.4.3Burpを起動し、Intruder > Optionのタブを開きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/666267/13aa0dbe-73d8-d55b-a81d-9b4af24f58f4.png)

Start Time: in xxx minutesという設定に、開始時間を分単位で設定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/666267/0df3ef79-22e2-23d3-d111-6a7187571017.png)


後はStartボタンを押下するだけで、設定した時間を待ってIntruderが開始されます。

この記事が迷えるBurp使用者の助けになれば。
それでは良いクリスマスを。
書籍「15Stepで習得 Dockerから入るKubernetes」の Private Registry の立ち上げ記録


## 概要

書籍「[15Stepで習得 Dockerから入るKubernetes](https://www.ric.co.jp/book/development/detail/253)」で Private Registry の立ち上げ手順が紹介されている.

今回 Private Registry を立ち上げたものの、
WEB-UI によって `http://<IP>:5043/repositories/` にアクセスすると、
Private Registry に登録しておいた Docker Image が一切表示されなかった.

この原因の調査記録と対策の記録を (無断転載になるといけないので) 大まかに書き残しておく.

　

## 状況

Private Registry を立てたマシンの名前を `my_server` とする.


## 調査記録

`docker-compose logs front-end` から切り分けを開始した.

次に、front-end コンテナにログインして以下を実行することで原因が判明した.

具体的にはコンテナの中ではマシン名 `my_server` の名前解決ができていないだけであった.

```bash
切り分けに必要なツールをインストールする
# apt update
# apt install curl
# apt install iputils-ping

curl を使って Private Registry API を実行する (前者は成功、後者は失敗した)
# curl --insecure -u <ユーザ名>:<パスワード> https://<IP>:5043/v2/_catalog
# curl --insecure -u <ユーザ名>:<パスワード> https://my_server:5043/v2/_catalog
```

## 対策

今回は物理ホストの /etc/hosts を docker コンテナ側にも ReadOnly で見せるようにした

```diff
略
   front-end:
     image: "konradkleine/docker-registry-frontend:v2"
     container_name: myfrontend
     ports:
       - 9080:80
     volumes:
+      - /etc/hosts:/etc/hosts:ro
     environment:
       ENV_DOCKER_REGISTRY_HOST: my_server
       ENV_DOCKER_REGISTRY_PORT: 5043
       ENV_DOCKER_REGISTRY_USE_SSL: 1

```

　

以上.
Rustでlessクローンを作りたい その5

https://qiita.com/hhatto/items/c415db93a1201509d0fc

lessクローン実装の続き

今日はあまりtipsとか無し。
ひたすら実装。

検索と検索後のジャンプがバグってたので修正した。

<a href="https://www.loom.com/share/eb621d375d6e4743a45ca60e3e1a5cc7">
    <img style="max-width:300px;" src="https://cdn.loom.com/sessions/thumbnails/eb621d375d6e4743a45ca60e3e1a5cc7-with-play.gif">
  </a>

## 今わかってるバグ
* 順方向への検索ジャンプはうまくいってるが、その後の表示がおかしい。
* 検索結果が1つの場合？にジャンプできない。
* 検索ワードがBSキーで入力し直せない。

明日はこの辺りを修正する予定。
メインループが肥大化してきたので、リファクタリングもしたいところ。

今日の成果
https://github.com/hhatto/advent-2021/compare/7d2a0c13da261ee6115d4bd1c0f35f151caf6361..bdfa30aa516e6ec97ef2971486bc761923f4ded6
コンストラクトインジェクションの多用は禁物

# はじめに
Laravelにはサービスコンテナと呼ばれるクラスの依存関係を管理し、依存注入を実行するための強力な仕組みが備わっています。
この仕組みを使うことによってクラスの見通しが良くなったり、テストが書きやすくなったりするという利点がありますが、注入の仕方によって負荷が違ってくるので記事にしたいと思います。

# 動作確認環境
- Windows10 Home
- Laravel8
- PHP8.0
 
# 計測結果
|  番号  |  内容  |  結果  |
| ---- | ---- | ---- |
| 1 |  クラスを生成する  |  0.69594383239746 ms  |
| 2 |  makeでクラスを生成する  |  86.152076721191 ms  |
| 3 |  コンストラクトインジェクションでクラスを生成する  |  222.5170135498 ms  |

# サンプル
```sample1.php
public function sample1()
{
    for ($i = 0; $i <= 10000; $i++) {
        new HogeApplication();
    }
}
```

```sample2.php
public function sample2()
{
    for ($i = 0; $i <= 10000; $i++) {
        app()->make(HogeApplication::class);
    }
}
```

```sample3.php
public function sample3()
{
    for ($i = 0; $i <= 10000; $i++) {
        app()->make(HogeApplication::class);
    }
}

class HogeApplication
{
    public function __construct(protected Hoge2Application $hoge2Application)
    {
    }
}
```

|  番号  |  内容  |  結果  |
| ---- | ---- | ---- |
| 4 |  コンストラクトインジェクションでサービスプロバイダ経由で依存注入  |  174.88718032837 ms  |
| 5 |  使用タイミングでサービスプロバイダ経由で依存注入  |  40.194988250732 ms  |
| 6 |  外でサービスプロバイダ経由で依存解決したものを引数で渡す  ​|  40.879011154175 ms  |
※5については呼び出す度に依存解決処理が行われるため、一度呼び出したものは変数に保持しておくなどの対応が必要

```sample4.php
public function sample4()
{
    for ($i = 0; $i <= 10000; $i++) {
        app()->make(HogeApplication::class);
    }
}
class HogeApplication
{
    public function __construct(
        protected TesterInterface $testerInterface
    ) {
    }
}
```

```sample5.php
public function sample5()
{
    for ($i = 0; $i <= 10000; $i++) {
        $hogeApplication = new HogeApplication();
        $hogeApplication->hoge();
    }
}

class HogeApplication
{
    public function hoge()
    {
        app()->make(TesterInterface::class);
    }
}
```

```sample6.php
public function sample6()
{
    for ($i = 0; $i <= 10000; $i++) {
        $test = app()->make(TesterInterface::class);
        $hogeApplication = new HogeApplication($test);
    }
}

class HogeApplication
{
    public function __construct(protected $tester)
    {
    }
}
```


# 問題点
- コンストラクトインジェクションはapp()での依存解決するよりも格段に処理が重いです
- コンストラクトインジェクションの場合、依存解決必要のない場合であっても該当のクラスを生成したタイミングで依存解決の処理がかかってしまうことになります

# まとめ
1つの依存解決程度では然程問題になることはないと思いますが、依存関係が複雑になってくると必要ないタイミングで依存解決処理が入ってしまいボトルネックとなるといったことがあるので、用法に気をつけて依存解決するようにしましょう。
この結果を見る限りでは、5番を上手く利用するのがベターなように思います。

# おまけ
```sample.php
// 以下は同じです。
app()->make(HogeService::class);
app()->makeWith(HogeService::class);

// 以下は同じです。
app()->make(HogeService::class, ['id' => 1]);
app()->makeWith(HogeService::class, ['id' => 1]);
```
公式ドキュメントを読むと違いがあるように書かれていますが、中の処理はまったく同じです。
引数を渡す場合であってもmakeを使えるので、makeWithと使い分ける必要はないでしょう。

# 参考ページ
https://readouble.com/laravel/8.x/ja/container.html
NestJSで入門するnpmライブラリ公開生活


今回、既存のJSライブラリをNestJS用にラッピングするライブラリを作成することにして、OSS活動アピ（大したことない）をやってみようと思いました。

以下、作ったライブラリとその概要

## 1. [nestjs-ip-middleware](https://www.npmjs.com/package/nestjs-ip-middleware)

- 動機：NestJSをいじってて、なにかライブラリっぽいものを作ってみたいという思いつき。
- 経緯
	- 案件でIP取得する機能をやっていて、偶然[nestjs-real-ip](https://www.npmjs.com/package/nestjs-real-ip)というものを見つけた。
	- これはただのラッパーライブラリーで、実装を見ると簡単だったから自分でもこういうの作れるのでは？という経緯。
	- ちなみに、アクセス元のIPを取得する[request-ip](https://github.com/pbojinov/request-ip)のラッパー
- 実装
	- 完全に真似て作りました。こちらのライブラリではデコレータとしてIPを取得するようにしているようでしたが、ミドルウェアとしてreqオブジェクトにattachするようなものを作りました。（詳細はソースなど参照）

- 結果
	- 創作負荷なく簡単にできてテスト、CI/CD環境（Github Actions)も色々勉強になったので良かった。
   - ダウンロード数も100を超えて、なんか嬉しい
   - 自作ライブラリ公開デビューできたので良かっら。

## 2. [nestjs-notarealdb](https://www.npmjs.com/package/nestjs-notarealdb)
 - 経緯：1のライブラリは簡単なものだったので、もっと複雑なものをと思い、@nestjs/typeormを参考にして作ることに！！
	 - 機能は簡単なものにしたいで、DBを扱わないJSON形式でファイル保存する[notarealdb](https://github.com/mirkonasato/notarealdb)をラップすることにした。
 - 実装
    - 結構難しかった。NestJSのDynamicMuduleを理解しなければなかった。
    - 実装・テストは@nestjs/typeormを参考にできたので良かった。普段使ってない機能をたくさん使っていたので勉強になった。
　- 結果　
    - 公開したばかりなのでまだダウンロード数は少ないが、改善点も色々あると思うので今後もメンテ等していこうと思う。
    - 今後はラッパーでないライブラリを作りたいがデビューとしては良い2個が作れたと思います。







### みなさんも小さなところから良いライブラリライフを！！
### 「ライブラリ〇〇をNestJSで使えるようにする」ってだけで色々作れそうなのでやりやすいです。
もしも美術館に自分の作品があったらを体験するクソアプリ

[クソアプリアドベントカレンダー](https://qiita.com/advent-calendar/2021/kuso-app)13日目担当、初参加の @umiremix です。
普段は主にフリーランスでUIデザインをしています。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">クソアプリのプロ化がすごいんだよな近年・・・。なんかよくわかんない日本語だけどそうとしか言いようがないんだよ</p>&mdash; あんど (@ampersand_xyz) <a href="https://twitter.com/ampersand_xyz/status/1468616405923020801?ref_src=twsrc%5Etfw">December 8, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

いやほんとプロ化が進んでクソアプリなのにハードル上がるという謎の現象が起きてますね…？

# 作ったクソアプリ
![unsplash_hQ3WZnY3yZ0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62182/1f48d784-ddb9-d213-5a10-ccf9c2d95515.png)

https://museum.umiremix.com/

あなたの作品が"もしも美術館にあったら"を体験できるWebアプリ「もしも美術館」　です。
クソアプリか否かですが、

> 今年も役に立たない、世の中に貢献しないアプリとかサービス

の部分が当てはまるので胸を張ってクソアプリだと言えますが、ネタ系ではないので面白さは求めないでください🥺
面白路線も考えはしたものの、自分が作りたいものを優先しました。
命名は私が一番好きなドラえもんのひみつ道具、「もしもボックス」を参考にさせてもらいました。
オードリー・タン氏も、もしもボックスが一番好きらしいです。

# 作った経緯

美術館が好きという理由ももちろんあるんですが、昔絵画や写真を入れる額縁を制作する仕事（額装師・フレーマー）の存在を知り、素敵な職業だな〜と感動したことを思い出して作成しました。
主役の作品をいっそう美しく見せるために、作品をよく理解し、リスペクトして縁取る作業って尊いですね。
もともとは主催したプチハッカソンで作りかけだったアプリなのですが、アドベントカレンダーでなんとか完成させることができました。

# アプリ内容

画像をアップロードするだけで額縁をつけて、勝手に作品名をつけ、自由に展示することができます。

![museum.umiremix.com_works_G4aXwjcj4UvmRKvF9aqN.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62182/7134c6ed-2d99-1360-88f8-26ca37208048.png)

https://museum.umiremix.com/works/G4aXwjcj4UvmRKvF9aqN

アンバーってなに…？⛩
と思ったら土壌由来の顔料アンバーを由緒とする色名のことらしいです。
こういうヘンテコな変換とかも意外と楽しめるポイントかなと思ってます。

# 主に使った技術

- Nuxt
- TypeScript
- Vuetify…ローディング・アイコンなど
- Firebase
    - Hosting
    - Storage…画像の保管
    - Firestore…アップロードした日付・画像サイズなど保持
    - Functions…OGP画像の作成・HTMLを返す
- Pixi.js…額縁をつける（この程度なら普通のcanvasでも十分ですが今後アップデートで機能追加できるように）
- Google App Script…英→日翻訳
- Cloud Vision API…画像認識

上記の技術は全員に推しているというわけではなく、今回は短期での開発だったので個人的に慣れている技術に偏っています。
NuxtもFirebaseもわけわからないエラー出るときあるので意外なところで苦戦しましたね…。
Composition APIとか使ってみたかったんですけど開発スピード落ちそうなのでやめました。
次なにか作るときは名前が似ていて親近感湧くので[Remix](https://remix.run/)使ってみたいなーと思ってます。

# 最後に

[ここから作品アップロード](https://museum.umiremix.com/new/)できるのでぜひ使ってみてくださいね。
ではよいお年を〜〜〜🎅
Route53ドメイン設定

#１．やったこと
実際触ってなかったのでRoute53の設定を試してみました。
同じように初めて設定する方の手助けになればと思います。

* Route53でホストゾーン作成＆ドメイン設定

#２．前提条件
* AWSアカウント取得済
* 宛先を用意済（筆者は構築済ALBを設定）
* お名前ドットコムにてドメイン取得済

#３．ドメイン取得
前提条件の通り、今回は取得済みドメインを利用します。（お名前ドットコム）

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688371/fc23e380-d5d4-28d8-ac22-6d65b8a2f066.png)|
|:--|

#４．Route53でホストゾーン作成＆ドメイン設定（ドメインA）
#４．１．Route53ホストゾーン作成
１．Route53　＞　ホストゾーン　＞　ホストゾーンの作成　をクリックする。

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688371/8e882cae-bbd7-f1ed-33ef-1a7cc0d921fd.png)|
|:--|

２．ドメイン名に取得したドメインを設定してホストゾーンの作成をクリックする。

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688371/df37b0c4-3679-b7a6-5b9c-2e37b758b04d.png)|
|:--|

３．ホストゾーンが作成されます。この画面のNSレコードを控える。

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688371/ffe67b6e-acb6-f909-c7c7-3a5c8f77ff6a.png)|
|:--|

## ４．２．Aレコード追加
１．「レコードを作成」をクリックする。
２．Aレコードを作成する。（筆者は構築済のALBを設定）

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688371/6a69e0bc-ba63-0499-75fa-c7bf66b10740.png)|
|:--|

３．Aレコードが追加されたことを確認します。

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688371/437c20d2-36a2-f5b5-1b48-df5517f3f07d.png)|
|:--|

## ４．３．お名前ドットコム設定
お名前ドットコムに控えたNSレコードを設定します。
１．お名前ドットコムコンソールからネームサーバー設定を開く。
２．「その他のネームサーバーを使う」から控えたレコードを登録する。

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688371/f422f35a-67e1-6b33-ef9c-68b1fa8004b3.png)|
|:--|



## ４．４．テスト
設定したドメインにアクセスして確認します。

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688371/2b2c6cea-d439-c6d4-b3d7-dc189b893901.png)|
|:--|
【Rails】ArgumentError (Unrecognized status code :ng):

<h2>症状</h2>
RailsAPIモードでアクションを呼び出したときに、下記エラーが発生しました。
翻訳すると、「ArgumentError（認識されないステータスコード：ng）：」


```terminal:error
ArgumentError (Unrecognized status code :ng):
```

該当のソースです。

```HogeController.rb
  def hoge
    user = User.find_by(id: params[:userId])

    if(true)
      render json: {}, status: :ok
    else
      render json: {}, status: :ng
    end
  end
```

<h2>解決方法</h2>
statusコードを普段使用されるステータスコードにすることで解決しました。
コードの一覧はこちらを参考にしました↓

https://qiita.com/terufumi1122/items/997e24dde87f807e3944

```HogeController.rb
  def hoge
    user = User.find_by(id: params[:userId])

    if(true)
      render json: {user: user}, status: :ok
    else
      render json: {}, status: :unauthorized
    end
  end
```

<h2>参考</h2>

https://qiita.com/terufumi1122/items/997e24dde87f807e3944
gemとはなにか？

## gemとは
１つのgem形式のファイルにパッケージングされたRubyの外部ライブラリのこと。
よく使われるRailsもgemの1つ。Rails自体が多くのgemで構成されている。

##### Rubyのライブラリの種類
- 標準ライブラリ
    - 組み込みライブラリ：Ruby本体に組み込まれるライブラリ（（Array,Stringなど）
    - 組み込みでないライブラリ：JSON,CSVなど
- 外部ライブラリ：device,rails,rspecなど


## どうやってインストールしているのか？
Rubyインストール時に標準ライブラリとしてインストールされる[RubyGems](https://rubygems.org/)により`$ gem install [gem名]`が使え、gemのインストールが可能になっている。


## どこにインストールされるのか？
以下のコマンドで保管場所が表示される。

```:ターミナル
$ gem environment gemdir
/usr/local/bundle　　　　 #実行結果（筆者docker環境)

/usr/local/bundle $ ls
bin  build_info  cache  doc  extensions  gems  specifications

/usr/local/bundle $ cat gems
ctioncable-6.1.4.1              capybara-3.35.3          parser-3.0.2.0              rspec-support-3.10.2
actionmailbox-6.1.4.1            childprocess-3.0.0       pg-1.2.3                    rubocop-1.22.3
actionmailer-6.1.4.1             coderay-1.1.3   　　　　　　　　　　　　　　　　　　　　　などなど
```

## 最後に
今まで詳しく知ろうとせず「便利だな〜」くらいにしか思っていなかったgem。ちゃんと調べることで、いつも近くで支えてくれていたことに気づき、感謝の気持ちが溢れました。
勉強大切。。
【React】react-router-domでハマった話

#初めに
我、最近Reactを学んでいるので、勝手に議事録として記載する。
といっても大体ここら辺の記事は出ていると思うので、そのほかの方の記事を参考にしたほうがいいであろう。
完全に自己満である。

#ハマったコード
```react
import { BrowserRouter, Link, Switch, Route } from "react-router-dom"

/**省略**/

<BrowserRouter>
        <Link to="/">Home</Link><br/>
        <Link to="/page01">page01</Link><br/>
        <Link to="/page02">page02</Link><br/>

        <Switch>
          <Route exact path="/"><Home /></Route>

          <Route 
            path="/page01" 
            render={({match: {url}}) => (
              <Switch>
                <Route exact path={url}>
                  <Page01/>
                </Route>

                <Route exact path={`${url}/detailA`}>
                  <Page01DetailA/>
                </Route>

                <Route exact path={`${url}/detailB`}>
                  <Page01DetailB />
                </Route>
              </Switch>
          )} />

          <Route path="/page02"><Page02 /></Route>
        </Switch>
</BrowserRouter>

/**省略**/
```
まぁこういったrouter-domのパッケージを使ってReactでのルーティング勉強していたわけである。
ただ動いてくれない！！

ほなら大体「書き方が違う！！`Routes`とか`Navigate`使え」とか「`Route`は`Routes`の中に絶対に入れなさい！」などそういった類のエラーが起きると思う。
大体そういったエラーが起きて`History`や`Switch`が使えないのである。

困ったもんだ。

#原因
上記のコードのエラーを調べると、`react-router-dom`のβ版である___v6___が原因で、ハマったコードのような書き方は一新されて使えないらしい。。。
うーん、安易にNPMでパッケージをインストールしたのが裏目に出たなーといったところだ。

#解決策
###その１（v6の記法に慣れる）
今のうちからこのv6に親しんでおけば、後々やりやすくなるのではなかろうか？
v5では`exact`を指定しなければならなかったりするので、いろいろ改善されているようである。
普通にこっちで対応したほうが有意義な気がする。
※単純に見やすいしね。

https://coders-shelf.com/react-router-v6-intro/

※上記参考にしてみてほしい。

###その２（v5へバージョンを下げる）
npmでパッケージバージョン下げればいいのだ。
以下コマンドである。

```shell
npm i -S react-router-dom@5
```

#まとめ
React学び始めの方は、ちょっと古い教材やサイトで学ぶとこのエラーに陥りやすいと思う。
我もその一人であったので、まぁ備忘録で書いておく。

※本業はフロントではござらん。。
VMware 製品に回避策のない脆弱性発生中 (CVE-2021-44228)  VMSA-2021-0028.1 の情報は

# VMware 製品を使用している人は要注意！！
現在 VMware のVMware Security Solutions のページに、[VMSA-2021-0028](https://www.vmware.com/security/advisories/VMSA-2021-0028.html)としてのセキュリティアラートが出ています。現時点では回避策がない事案なので、VMware 製品を使っている人は要注意です。詳細は[VMSA-2021-0028](https://www.vmware.com/security/advisories/VMSA-2021-0028.html)を参照ください。（現在は、[VMSA-2021-0028.1](https://www.vmware.com/security/advisories/VMSA-2021-0028.html)になっています。)
当該情報の表の部分だけを日本語にして、キャッチアップしてる[ブログ](https://imaisato.hatenablog.jp/entry/2021/12/11/155350)も書いていますので、そちらもご参照ください。
Amazon Inspectorを使ってEC2の脆弱性診断をしてみる

先日開催されたAWS ReinventのUpdateでAmazonInspectorによるEC2の脆弱性診断について気になったので検証してみました。
[Amazon Inspectorの刷新](https://image.slidesharecdn.com/awsblackbelt2021reinvent2021digest-211203102222/95/20211203-aws-black-belt-online-seminar-aws-reinvent-2021-131-638.jpg?cb=1638527250)

# Amazon Inspectorとは
Amazon InspectorはEC2やECRの脆弱性およびを診断することができます。
CommonVulnerness and Exposures（共通脆弱性識別子）CVEで始まるソフトウェアの脆弱性情報を提供します。
この機能を使用することで導入しているソフトウェアにどんな脆弱性があるか知ることができます。


Amazon Inspectorには通常版とClassic版の2種類があり、通常版では今のところWindowsOSをサポートしていません。
[通常版](https://docs.aws.amazon.com/inspector/latest/user/supported.html)
[Classic版](https://docs.aws.amazon.com/inspector/latest/userguide/inspector_supported_os_regions.html)

通常版とClassicの大きな違いとしては、
EC2の脆弱性診断をする際に必要なAgentが異なり、それぞれ以下のAgentが必要です。

通常版:SSMAgent（最近のAMIにはほとんどデフォルトでインストールされています）
Classic版:Inspector Agent

SSMAgentを使用する際はssmのapiに接続できる必要がありますので、
プライベートサブネットから接続する際はNATゲートウェイからインターネットゲートウェイ経由で抜けるか、SSMエンドポイントを設置して抜けるか、ネットワーク側の考慮も必要ですのでご注意ください。


## Amazon Inspector（通常版）で診断してみた。

さっそく診断してみましょう。Amazon Inspectorを使用する際は最初に有効にする必要があります。
またリージョナルサービスなので、複数のリージョンでEC2を利用している場合などは各リージョンで有効化する必要があります。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/398873/f1c147ea-8b4c-853d-33c4-5da0bb4c2695.png)

有効化するとサマリー画面が出てきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/398873/c49e5cb1-5f87-6533-48e1-2995bca52ff1.png)

評価した後のサマリー画面がこちらになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/398873/4cba6267-b01b-a2b0-d61c-7354a1e0ea94.png)

今回はRedHatとWindowsをそれぞれ1台用意して診断していますが、
通常版ではWindowsをサポートしていないため、statusがUnsupported OSと表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/398873/8c4100e7-a872-b1f8-faab-68b1c210f507.png)

続いて脆弱性ごとの一覧です。CVEごとにCritical、Highで分類されています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/398873/023f37ce-b323-d2f7-6ceb-db293a67474a.png)

一番上の脆弱性をクリックしてみるとCVEの内容と対応方法が記載されています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/398873/e88606b8-838d-5777-1e17-e8c4ecca16ca.png)

## まとめ
思っていたよりも簡単にEC2内部の脆弱性も診断できることがわかりました。セキュリティグループでフルオープンなルールがないかやS3のパブリック公開がオフになっているかなどAWS側のセキュリティ設定も重要ですが、アプリケーションそのものが動いているのはEC2内部ですのでクラウド側の設定とOS内部の設定両方に注意していく必要があります。本サービスはクラウドサービス利用のお供として導入すべきサービスだと感じました。
dlibのimglabを使ってアノテーションしてみた

物体検出のための学習データを準備するために、dlibのimglabを使ってみた。

## 環境構築

| software | version |
|:-:|:-:|
| mac | BigSur 11.6|
| python | 3.8.2 |
| pip | 21.3.1 |

作業用の仮想環境はpipenvで構築しました。

```sh
mkdir dliblab
cd dliblab
pipenv --python 3.8
```

## アノテーションする画像の準備
適当な顔写真を準備して`faces`というディレクトリに保存しました。


## 導入作業

homebrewでcmakeをインストール。

```sh
brew install cmake
```

続いて[dlib](http://dlib.net/)をダウンロード。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235642/d231496b-6d8b-951a-b27e-9f58d6238169.png)

ダウンロードしたdlibを作業用ディレクトリ(dliblab)に置いてimglabをbuildします。

```sh
cd dlib-19.22/tools/imglab
mkdir build
cd build
cmake ..
cmake --build . --config Release
```

準備した画像ディレクトリを指定してxmlリストを作成。

```sh
./imglab -c ~/work/tests/dliblab/faces/all.xml ~/work/tests/dliblab/faces
```

作成したall.xmlを指定してimglabを起動。

```sh
./imglab ~/work/tests/dliblab/faces/all.xml
```

[xquartz](https://ja.wikipedia.org/wiki/X11.app)が起動してimglabのGUIが表示される。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235642/e623b0ad-7fc3-ba83-73bc-16cea864c689.png)

shiftを押しながらドラッグするとマークすることができる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235642/463ad6db-1c2e-5743-bb5a-db4e599476cd.png)

マークし終わったら[File]-[Save As]を選択し、ファイル名を入力して保存。  
(traning.xmlで保存しました)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235642/01d818fb-c12a-3fbf-03bd-67f194fba462.png)

traning.xmlを見るとマークの座標情報が記録されていました。

```xml
<?xml version='1.0' encoding='ISO-8859-1'?>
<?xml-stylesheet type='text/xsl' href='image_metadata_stylesheet.xsl'?>
<dataset>
<name>imglab dataset</name>
<comment>Created by imglab tool.</comment>
<images>
  <image file='Balloon.jpg'>
    <box top='16' left='68' width='48' height='65'/>
  </image>
  <image file='Girl.jpg'>
    <box top='6' left='40' width='75' height='94'/>
  </image>
  <image file='Lenna.jpg'>
    <box top='13' left='36' width='75' height='102'/>
  </image>
</images>
</dataset>
```

こうやってアノテーションするのかー、と学びになりました。
作成したxmlファイルを使って学習器を作成するのは次のステップとして今回はここまでにしたいと思います。



↓続き書きました。

https://qiita.com/dafukui/items/c8f36a512d3104e2381d

## 参考

http://ishi.main.jp/ros/ros_obj_detection.html
Qt の動的言語切り替え

# はじめに

この記事は [Qt Advent Calendar 2021](https://qiita.com/advent-calendar/2021/qt) の13日目の記事です。

QWidget ベースのアプリケーション および Qt Quick アプリケーションで、動的に言語を切り替える実装について書きました。
今年も時間ギリギリになってしまったのでソースコードとポイントの箇条書きだけですゴメンナサイ。
なるべく簡単に対応言語が増やせるように意識して書いてみました。

Qt Creator 5.0.3 (MSVC), Qt 6.2.1, Windows 10 で動作確認しています。

# Qt多言語対応の概要

## 特徴

1. 実行時に読み込む言語ファイルはバイナリ形式で軽量、高速。
1. 翻訳ファイルは、 Qt Linguist というツールを使い、フォームやソースコードを確認しながら編集できる。
       ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/11596/42f86aa9-f95c-92fb-0e1f-b5e8396adcf9.png)
       ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/11596/0d06e5a4-139e-adc9-e5cc-8cd3dafd0c29.png)

## 基本的な多言語対応の実装手順

1. ソースコードの翻訳したい文字列を、C++ なら QObject::tr で、QML なら qsTr で囲む。
1. lupdate コマンドを実行し、ソースコードから ts ファイルを作る。
1. Qt Linguist で ts ファイルを開いて編集する。
1. lrelease コマンドを実行し、ts ファイルから qm ファイルを作る。
1. アプリケーション実行時に QTranslator::load で(システムのロケールに対応した) qm ファイルを読む。
1. 読み込んだ QTranslator を QCoreApplication::installTranslator する。

# 動的な言語切り替えの実装例

## QWidgetアプリケーション

https://github.com/Atsushi4/multi_language_widgets

### ポイント

- pro ファイルで `TRANSLATIONS += translations/multi_language_widgets_ja_JP.ts` しておくと、lupdate コマンドの引数にこの pro ファイルを指定して ts ファイルを生成できる。Qt Creator からは Tools － External － Linguist － Update Translations(lupdate) メニューで lupdate コマンドを実行できる。
    - ただし ts ファイル出力先のフォルダが無いとコマンドの実行が失敗するので注意。
    - 対応言語を増やす場合は TRANSLATIONS に追加して lupdate を再実行する。 
- ts ファイルを編集する時は Qt Creator で ts ファイルを右クリックして Open With － Qt Linguist メニューを選ぶ。
    - 他にまとめて編集したい ts ファイルがあれば、Qt Creator から Qt Linguist へドラッグ＆ドロップする。
- pro ファイルで `CONFIG += lrelease` しておくとビルド時に lrelease コマンドで TRANSLATIONS の ts ファイルから qm ファイルを出力ディレクトリに生成してくれる。
    - QM_FILES_INSTALL_PATH で出力先ディレクトリを変えられると書いてあったけど効かなかった。。。
- pro ファイルで `CONFIG += lrelease embed_translations` しておくと生成した qm ファイルをリソースとしてアプリケーションに取り込んでおいてくれる。
    - QM_FILES_RESOURCE_PREFIX でリソースの prefix を指定できる。デフォルトは `/i18n/`。
- 先に installTranslator しておいた QTranslator で別ファイルを load してもうまく動いた。
- ui ファイルでフォーム作成したクラスでは、changeEvent で LanguageChange のイベントを受けて retranslateUi する。
- Qt 用の翻訳ファイルも installTranslator してあげると、QLineEdit の右クリックメニュー等々も翻訳される。
    - Qt がインストールされていない環境で実行する場合には Qt の qm ファイルも配布する必要がある。

## Qt Quick アプリケーション

https://github.com/Atsushi4/multi_language_qml

### ポイント

- QWidget アプリケーションと大体同じ。
- 言語が切り替わったら QQmlApplicationEngine::retranslate を呼ぶ。
- その気になれば選択言語に合わせて画像の切り替えもできる。
    - 多分推奨はされていない。

# 終わりに

`CONFIG += lrelease` とか `embed_translations` とか全然知らなかったので、調べてみて良かったなーとおもいました。(小並感)
明日は @hoshianaaa さんの [PyQt5のチュートリアルを動かす ➇ QMessageBox](https://qiita.com/hoshianaaa/items/c72852f76adf7f160335) です。お楽しみに！

https://qiita.com/hoshianaaa/items/c72852f76adf7f160335