OmniAICreator/Qiita-1.07M huggingface dataset:
firestoreの考察

最近firestoreの使い方について調べてたので考察です。
元の記事は[こちら](https://snow-cat.net/?p=1348)です。
# 考察のまとめ
考察は以下のような感じです

+ データベースの拡張性について
+ データを小分けにする
+ 重複するデータを活用する
+ リレーションデータを作成する
+ 検索が弱い
+ 注意すべきところ

## データベースの拡張性について
まずデータベースのスペックに関してですが、Google側で自動的にスケールアウトしてるので特に意識する必要がないです。アクセスがスパイクしても難なく対応できますが、読み込めば読み込むほど料金はかかるので、効率的にデータへアクセスすることを考える必要があります。（googleに寄付したい場合は別ですが）

## データを小分けにする
1ドキュメントで持つことのできるデータ量は1MBが上限らしいです。レストランの住所や電話番号のようにそのデータの属性ならそのドキュメントで持つことがいいですが、訪問したレストランの履歴など増えていくデータを持ちたいとか考え出すと話は異なります。その場合はそのドキュメントにサブコレクションを作成してデータ量の少ないドキュメントを作成した方が安全だと思います。

## 重複するデータを活用する
firestoreはRDSと違ってデータを重複させて持つことがあるらしいです。ほんまかいな。ということは、ユースケースを考えてトップレベルのコレクションにするかサブコレクションにするか吟味する必要がありそうだと思いました。また、更新処理がめんどくさくなりそうなので、関連するサブコレクションで持っておいた方が処理が楽になるかと思います。例えば、ユーザがレビューしたレストランの情報をサブコレクションとして持つと更新時に必要な箇所を取得することができて全てのレストランの全てのレビューを見て対象箇所を見つけて更新する手間が省けるかと思う。

## リレーションデータを作成する
firestoreにはjoinはありません。なので、重複するという特徴を使ってドキュメントにリレーション用のサブコレクションを作成すると効率的にデータにアクセスできるかと思います。全てのコレクションから対象のデータを検索して、その中身を取得するよりいいのではないでしょうか？

## 検索が弱い
条件指定の方法はこちらのリンクを見てもらうとわかりやすいです。

https://firebase.google.com/docs/firestore/query-data/queries?hl=ja

https://zenn.dev/tentel/articles/ea7d5c03e68e6d142d98

firestoreは以上、以下などの条件で絞ることはできますがlikeのような条件で絞ることができません。

## 注意すべきところ
スキーマレスで拡張性が高いfirestoreはデータ構造を変更する際に簡単に変更することができます。要件が変わりやすいベンチャー企業などのシステムには最適ですが、仕様書などにどのような項目があるか残していかないと後々問題になっていくと思います。

# サンプルデータ
### チャット機能
チャット機能について考えてみます。
まず、ユースケースについて考えてみます。
LINEやマッチングアプリでは会話の一覧があり、その中にメッセージ投稿するので、以下のユースケースが考えられます。

+ チャットの一覧を見る
+ チャットの詳細を見る
+ メッセージを投稿する

まず、ChatRoomCollectionを作成してそこにチャットを作成します。
/ChatRoomCollection/{chatRoomDocument}
```
{
    "id" : "testId",
    "chat_name" : "テストのチャット",
    "latest_message" : "最新のメッセージです。",
}
```
そこにチャットのドキュメントを作成して、そのサブコレクションに会話データを入れていきます。
これが実際の投稿するメッセージのデータになります。
/ChatRoomCollection/{chatRoomDocument}/ChatCollection/{chatDocument}
```
{
    "id" : "testChatId-0001",
    "post_user_id" : "userId001",
    "text" : "こんにちわ、私はテストです。",
    "create_datetime" : "2022-01-01 00:00:00",
}
```
このようにすることで、コレクションから対象の会話データを絞り込む必要がなくなります。

ChatCollectionと同じ階層にこんな感じでユーザデータを登録すると参加しているユーザが取得しやすいですね。
/ChatRoomCollection/{chatRoomDocument}/UserCollection/{userDocument}
```
{
    "id":"user1",
    "enable":"false",
    "auth":"admin",
}
```
次に各ユーザのサブコレクションにリレーションデータを登録します。
/UserCollection/{UserDocument}/ChatRelationCollection/{ChatRelationDocument}
UserDocumentはとりあえずこんな感じですかね
```
{
    "id":"user1",
    "name":"テスト太郎",
    "email":"taro.test@gmail.com",
}
```

ChatRelationDocumentはこんな感じです。
このデータはユーザがチャット一覧を見る際に使うユースケースで使う想定です。
```
{
    "id":"chatRelation-001",
    "chat_id":"testChatId-0001",
    "show_flg":false,
}
```
とりあえず今回はこんな感じです。
違う機能も気が向いたら記載していこうと思います。

まだ開発で使ったことがないので、意見をいただけると助かります。
webdriver.Chrome() でChromedriverを開こうとしたら、chromedriverは開発元を検証できないため開けませんと表示された

# はじめに
スクレイピングでChromedriverを使用したく、環境構築時にエラーが発生したため、備忘録としてメモしておきます。

# 開発環境
Mac
Monterey (ver. 12.2.1)
Chrome(バージョン: 99.0.4844.51（Official Build） （x86_64）)

# Chromedriverの開発環境構築時に発生したエラー、経緯は以下の通り

### 1.
 ```brew install chromedriver --cask``` でchromedriverをインストール
### 2.
```!pip install selenium```でseleniumインストール
### 3.
以下の通り記述

```python
from selenium import webdriver
import time
import pandas as pd

USER = "test_user"
PASS = "test_pw"

#Chrome起動
browser = webdriver.Chrome()
browser.implicitly_wait(3)

```
このとき、エラーが発生。

# 解決方法手順

### 開発元を検証出来ないと表示される際、キャンセルを選択
「chromedriverは開発元を検証出来ないため、開けません」っと表示され、
 ゴミ箱に入れる
 キャンセル
の2択が出ますが、

2のキャンセルを選択。


### システム環境設定のセキュリティとプライバシーでそのまま許可をクリック

下の方に「このまま許可」と表示されてるはずです。

### chromedriverの開発元を検証出来ません。開いてもよろしいですか？

以上のように表示されるので、**開く**を選択。

これでChromedriverが使えるようになりました。


# 参照
https://qiita.com/apukasukabian/items/77832dd42e85ab7aa568
Raspberry PiでgRPCが動かない

Raspbian GNU/Linux 11 (bullseye)にてgRPCを使ったPythonプログラムを実行しようとしたら次のエラーが出た。

```py
Traceback (most recent call last):
  File "/usr/local/lib/python3.9/dist-packages/etcd3/init.py", line 3, in <module>
    import etcd3.etcdrpc as etcdrpc
  File "/usr/local/lib/python3.9/dist-packages/etcd3/etcdrpc/init.py", line 2, in <module>
    from .rpc_pb2_grpc import *
  File "/usr/local/lib/python3.9/dist-packages/etcd3/etcdrpc/rpc_pb2_grpc.py", line 2, in <module>
    import grpc
  File "/usr/local/lib/python3.9/dist-packages/grpc/init.py", line 22, in <module>
    from grpc import _compression
  File "/usr/local/lib/python3.9/dist-packages/grpc/_compression.py", line 15, in <module>
    from grpc._cython import cygrpc
ImportError: /usr/local/lib/python3.9/dist-packages/grpc/_cython/cygrpc.cpython-39-arm-linux-gnueabihf.so: undefined symbol: __atomic_exchange_8
```


pipでインストールされるgRPCライブラリに問題があるようなので、代わりにapt経由でインストールすれば解決した。　

```shell-session
$ pip uninstall -y grpcio grpcio-tools
$ sudo apt install -y python3-grpcio python3-grpc-tools
```


## 参考
- [python 3.x - undefined symbol: __atomic_exchange_8 - Stack Overflow](https://stackoverflow.com/questions/70084134/undefined-symbol-atomic-exchange-8)
DDLとDMLとDCL

## ■DDL
・データ定義言語、Data Definition Languageの略である。
・テーブルに関する命令文(SQL文)のこと。
&nbsp;&nbsp;&nbsp;&nbsp;\- CREATE文：テーブル/ビューを新規作成する。
&nbsp;&nbsp;&nbsp;&nbsp;\- DROP文：既存テーブル/ビューを削除する。
&nbsp;&nbsp;&nbsp;&nbsp;\- ALTER文：既存テーブル/ビューをのカラム定義を変更する。
&nbsp;&nbsp;&nbsp;&nbsp;\- TRUNCATE文：既存テーブル/ビューを再作成する。(テーブル/ビュー内のデータを全削除する)

## ■DML
・データ操作言語、Data Manipulation Languageの略である。
・テーブルのレコードに関する命令文(SQL文)のこと。
&nbsp;&nbsp;&nbsp;&nbsp;\- SELECT文：テーブル/ビューからレコードを抽出する。
&nbsp;&nbsp;&nbsp;&nbsp;\- INSERT文：テーブル/ビューにレコードを新規登録する。
&nbsp;&nbsp;&nbsp;&nbsp;\- UPDATE文：テーブル/ビューのレコードを更新する。
&nbsp;&nbsp;&nbsp;&nbsp;\- DELETE文：テーブル/ビューのレコードを削除する。

## ■DCL
・データ制御言語、Data Control Languageの略である。
・テーブルに対する権限に関する命令文(SQL文)のこと。
&nbsp;&nbsp;&nbsp;&nbsp;\- GRANT文：テーブルに対する権限を与える。
&nbsp;&nbsp;&nbsp;&nbsp;\- REVOKE文：テーブルに対する権限を取り消す。
  
## ■ひとこと
今までの自分の経験上、DDLとDMLをまとめてDDLと呼んでいたり、逆にDDLとDMLをまとめてDMLと呼んでいたりするプロジェクトがあったので、郷に入っては郷に従え的な感じなところもある定義なのかなと。(正しく使って欲しい、、)
Ubuntuでユーザ追加したらsudo使えなかった。

昔も同じ躓きをしたと思うので、忘れないように投稿します。
知っている方にとっては常識で、しょうもない内容だったらサーセン。
# 内容
古いPCにUbuntuをインストールしました。（メモリ2GB！オンボだから増設できない！）
rootユーザとは別に新規にユーザを作成して、そのユーザで作業をしようとしました。

その中で sudo apt install など sudoを使用しようとしたところ、
`[ユーザ名] は sudoers ファイル内にありません。この事象は記録・報告されます。`
という警告が出て、sudoが使えませんでした。
sudoersというグループに新規に作成したユーザを追加する必要があるようです。

# 対応
以下のブログを参考に対応しました。
https://tech.unifa-e.com/entry/2018/05/18/112810

`/etc/sudoers.d/`の下に設定ファイルを作ります。
今回はuser-initというファイル名で設定しました。
```sh
# rootユーザに切り替える
test@pc:~$ su
# /etc/sudoers.d の下に user-init という名前のファイルを作る
# user-initの内容は★
root@pc:/home/test$ sudo vi /etc/sudoers.d/user-init
root@pc:/home/test$ exit
# 以降sudoが使えるようになっているはず
```

★user-initの内容は下記になります
```
[sudo使えなかったユーザのユーザ名] ALL=(ALL) ALL
```

# 対応 その2
以下のコマンドでもsudoを使えるようにできるみたいです。
```
sudo usermod -G sudo [sudo使えなかったユーザのユーザ名]
```
@noradoggさんがコメントで教えてくださいました。ありがとうございます。

# 終わり
これでsudoが使えるようになりました。
もし、この設定の仕方に問題があったら教えてほしいです。
「こんな容赦なくALLとか使って大丈夫なんか。。？」とか思っています。
AWSでもそうだけど、権限の設定が苦手です。
【メモ１００％】vSphereClient上でこのvShereのVMを立てていたがそういったのが可能なのか？可能ならば実装方法は何という語句でググれば良いのか伺ってよろしいでしょうか？

お世話になっております。

件名の通りなのですが
vSphereClient上で　esx-e41　にアクセスすると

```
esx-e41
+ HA
+esx-e40
```
となっていたと思うのですがesx-e40の方は
```
esx-e40
+HA
  esx-e41
＋
```
となっていたと思います。

【思います】と表記したのは
現在vSphereにアクセス出来ないので確認出来ないため

それでブラウザから
https://esx-e41/
にはwmwareでアクセス出来るのですが
https://esx-e40/
は開きません。

どうやったらesx-e40のvPhereClientが起動出来るのか相談に乗って頂けないでしょうか？

漠然とした質問で申し訳無いのですが
何か情報がございましたら

----------------
とここまで書いて
https://esx-e41/
にwmwareでアクセス出来た所に
esx-e40があった(^_^;)

ちょっと焦った記録を残させて頂きます。
Pythonで三角関数を自作してみた

# ライブラリを使うと
Pythonでは標準ライブラリで三角関数の計算ができます。

```
$ python3
Python 3.10.2 (main, Mar  9 2022, 20:22:18) [GCC 7.5.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import math
>>> math.sin(0.1)
0.09983341664682815
```

これを使わずに三角関数を計算してみます。
理由は特にありません。暇つぶしです。

# マクローリン展開
マクローリン展開を使うと、三角関数を多項式に置き換えることが出来ます。

```math
f(x) = \sum_{k=0}^{\infty}f^{(k)}(0)\frac{x^k}{k!}
```

$\sin(x)$のマクローリン展開は以下の通りです。

```math
\sin(x) = \sum_{k=0}^{\infty}(-1)^k\frac{x^{2k+1}}{(2k+1)!}
```

これを実装します。

```python:sin.py
# -*- coding:utf-8 -*-

def fact(x):
    if x == 0: return 1
    else: return x * fact(x-1)

def sin(x):
    ans = 0.0
    for k in range(30):
        ans += (-1 if k%2 else 1) * (x**(2*k+1)) / fact(2*k+1)
    return ans

if __name__ == "__main__":
    import math
    print("自作", sin(0.1))
    print("math", math.sin(0.1))
    print("差分", abs(sin(0.1) - math.sin(0.1)))

"""
$ python3 sin.py
自作 0.09983341664682817
math 0.09983341664682815
差分 1.3877787807814457e-17
"""
```

それっぽい値がでました。

# 高階関数
Pythonでは関数も第一級オブジェクトですので、関数$g(n)=f^{(n)}(0)$を受け取って関数$f$を返すような関数を定義できます。引数または返り値に関数を含む関数を高階関数と言います。ここで、$f^{(n)}(x)$は$f(x)$のn次導関数とします。$f(x)=\sin(x)$とすると、$f^{(n)}(0)$は次のように表すことが出来ます。

```math
f^{(n)}(0) = \left\{
\begin{array}{ll}
0 & (n \equiv 0 \pmod 4) \\
1 & (n \equiv 1 \pmod 4) \\
0 & (n \equiv 2 \pmod 4) \\
-1 & (n \equiv 3 \pmod 4) \\
\end{array}
\right.
```

$f(x)=\cos(x)$のときも同様に定義できます。

```math
f^{(n)}(0) = \left\{
\begin{array}{ll}
1 & (n \equiv 0 \pmod 4) \\
0 & (n \equiv 1 \pmod 4) \\
-1 & (n \equiv 2 \pmod 4) \\
0 & (n \equiv 3 \pmod 4) \\
\end{array}
\right.
```

これをコードにします。

```python:trigonometric_function.py
# -*- coding:utf-8 -*-

def fact(x):
    if x == 0: return 1
    else: return x * fact(x-1)

# マクローリン展開
def maclaurin(f):
    def g(x):
        ans = 0.0
        for k in range(30):
            ans += f(k) * (x**k) / fact(k)
        return ans
    return g

# sin(x)のn次導関数
def diff_sin(n):
    return (0, 1, 0, -1)[n % 4]

# cos(x)のn次導関数
def diff_cos(n):
    return (1, 0, -1, 0)[n % 4]

sin = maclaurin(diff_sin)
cos = maclaurin(diff_cos)

if __name__ == "__main__":
    import math
    print("---sin---")
    print("自作", sin(0.1))
    print("math", math.sin(0.1))
    print("差分", abs(sin(0.1) - math.sin(0.1)))

    print("---cos---")
    print("自作", cos(0.1))
    print("math", math.cos(0.1))
    print("差分", abs(cos(0.1) - math.cos(0.1)))

"""
$ python3 trigonometric_function.py 
---sin---
自作 0.09983341664682817
math 0.09983341664682815
差分 1.3877787807814457e-17
---cos---
自作 0.9950041652780258
math 0.9950041652780258
差分 0.0
"""
```

このmaclaurin関数を用いれば、n次導関数が分かっている関数を定義できます。例えば、$f(x)=e^x$のとき、任意のnについて$f^{(n)}(0)=1$なので

```python
f = maclaurin(lambda _: 1)
print(f(1)) # 2.7182818284590455
```

と定義できます。


# 参考文献・サイト
[マクローリン展開｜高校数学の美しい物語](https://manabitimes.jp/math/570)
Kotlin KoansでKotlin入門　第１４回：Comparison

# はじめに
公式の問題集[「Kotlin Koans」](https://play.kotlinlang.org/koans/overview)を解きながらKotlinを学習します。

過去記事はこちら
- Introduction
  - [第０回：Kotlin Koansについて](https://qiita.com/kosuke1/items/41d192af63485bfc8cb8)
  - [第１回：Simple Functions](https://qiita.com/kosuke1/items/5862a3ece96082a62e67)
  - [第２回：Named arguments](https://qiita.com/kosuke1/items/2af11262852ee41b5da7)
  - [第３回：Default arguments](https://qiita.com/kosuke1/items/16cb8ac780df58dc8054)
  - [第４回：Triple-quoted strings](https://qiita.com/kosuke1/items/52cfeed2c49b0252d40e)
  - [第５回：String templates](https://qiita.com/kosuke1/items/0221bbbc5bc70847be6f)
  - [第６回：Nullable types](https://qiita.com/kosuke1/items/aa96e422c5313b6961bc)
  - [第７回：Nothing type](https://qiita.com/kosuke1/items/1ca0e48820d58c9dfe16)
  - [第８回：Lambdas](https://qiita.com/kosuke1/items/8de74f9491e003127f53)
- Classes
  - [第９回：Data classes](https://qiita.com/kosuke1/items/1be2de1b4406b8d914bf) 
  - [第１０回：Smart casts](https://qiita.com/kosuke1/items/50bdecaa0740eb0e1a17)
  - [第１１回：Sealed classes](https://qiita.com/kosuke1/items/71a4da02fe1e99da5d63)
  - [第１２回：Rename on import](https://qiita.com/kosuke1/items/f11465cc15a94e7589a3)
  - [第１３回：Extension functions](https://qiita.com/kosuke1/items/d4a1da00bc142c297221)


# 問題
[Comparison](https://play.kotlinlang.org/koans/Conventions/Comparison/Task.kt)

[演算子のオーバーロード](https://kotlinlang.org/docs/operator-overloading.html?_ga=2.178430244.1801786368.1647520462-1976109019.1644486873)について、また、==、<、+といった演算の異なる規則がKotlinでどのように機能するかについて学びます。
クラスMyDateに関数compareToを追加して、比較できるようにします。
この後、以下の date1 < date2 のコードがコンパイルされ始めるはずです。
Kotlinでメンバをオーバーライドする場合、override修飾子が必須であることに注意してください。

修正前のコード
```Kotlin
data class MyDate(val year: Int, val month: Int, val dayOfMonth: Int) : Comparable<MyDate> {
    /* TODO */
}

fun test(date1: MyDate, date2: MyDate) {
    // this code should compile:
    println(date1 < date2)
}
```


# 問題のポイント
Kotlinでは、型に対してあらかじめ定義された演算子のセットに対して、カスタム実装を提供することができます。これらの演算子は、あらかじめ定義された記号表現（+や*など）と優先順位を持ちます。

例として、単項のマイナス演算子をオーバーロードする方法を紹介します。
```Kotlin
data class Point(val x: Int, val y: Int)

operator fun Point.unaryMinus() = Point(-x, -y)

val point = Point(10, 20)

fun main() {
   println(-point)  // prints "Point(x=-10, y=-20)"
}
```

# 解答例
すべての比較はcompareToの呼び出しに変換され、Intを返すようにします。

```Kotlin
data class MyDate(val year: Int, val month: Int, val dayOfMonth: Int) : Comparable<MyDate> {
    override fun compareTo(other: MyDate) = when {
        year != other.year -> year - other.year
        month != other.month -> month - other.month
        else -> dayOfMonth - other.dayOfMonth
    }
}

fun test(date1: MyDate, date2: MyDate) {
    // this code should compile:
    println(date1 < date2)
}
```
Mac で Vue CLI を利用するまで

<p>※ mac で行っています。うまく解決できるとは限らないのでご注意ください</p>
<p>※ 当方初学者ですのでもっといい方法はあると思います。</p>
<br>
<h2>下準備</h2>
<ul>
<li>よく分からんが Vue CLI を使うといいらしい</li>
<li>Vue CLI を使うためには npm が必要</li>
<li>npm は Node.js のパッケージ管理ツールであるため、 Node.js のインストールが必要</li>
<li>Node.js のインストールに限らず Mac で行うなら homebrew で行うといいらしい</li>
</ul>
<p>つまり開発環境を作成するためには上記を逆順で行う必要がある！</p>

<h2>homebrew のインストール</h2>
<code>/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"</code>
<h3>homebrew のバーションを確認</h3>
<code>$ brew --version</code>

<h2>node.js のインストール</h2>
<code>brew install node</code>
<h3>nodeのバーションを確認</h3>
<code>$ node -v</code>

<h2>Vue CLI のインストールする</h2>
<code>npm install -g @vue/cli</code>
<h3>Vue CLI のバーションを確認</h3>
<code>$ vue --version</code>

<h2>「bash: vue: command not found」と出て上手くいかないよ！</h2>
<p>こちらの記事を参考にさせていただきました</p>
<blockquote><a href="https://qiita.com/kinokon/items/f8801d764b875115c350">Vue CLIをインストール後vue: command not foundになる</a></blockquote>

<p>うまくいったと思ったら確認する</p>
<code>$ vue --version</code>

<br>
<p>今回は以上です。ここまで読んでいただきありがとうございました</p>
初心者ブロックチェーンに挑戦する　16

# 人生初NFTをゲット

## 私はホモ・サピエンス

　コードを読みに読んで、でも、わからないものはわからない、できないことはできない、でも、今できなくても、いつかできるようになろう、という決断は突然降ってきた。ともかく、第一課題を完成させよう。そして次に進もう。

　なので、最後の関門は、

- プロジェクトをGitHubに上げる。
- vercelにアップロードして公開。
　
　実はGitHub、苦手。特に今回、UNCHAINからフォークでもらってきて、localにcloneという、そのcloneに失敗して、Gipでダウンロードしたコードを書き換えてつくってきた。最後の最後に、これを繋ぐ、ができない。cloneしてないのでアクセス権がないのだ。何ちゃらkeyをつくるのがどうの、というのにも、毎回失敗する。

　のでまた、獣道を行く。
　まずclone。親切に誘導されている方法がいつも失敗する。なんでだからわからないまま放置しているから、いつまでも改善しない。ともかく

- terminalで　git clone の後、コピーしなさいね、というURLの代わりに、サイトの一番上の、ふつうのURLを取ってきて貼る。Finderでみると、けっこう上のほうにフォルダができている。
- 今まで編集していたフォルダをどけて、そこにcloneしてきたのをつっこむ。
- よけてある編集済みのファイルを、cloneにペタペタ貼って更新する。

　実はこれで、いけてしまうから不思議だ。一応deployして確認。npmが上手く紐付いてなかったので、再インストール。よしよし。ここ数日の苦闘の跡を全部消すかどうか考えて、結局、後日のために残したので、「使ってないコードがある」とやたら注意されるけど、無視。

- git add .
- git commit -m "〜"
- git push

 行け〜！。

　次、Vercelにホスト。アカウントつくって、projectつくって、リポジトリ引っぱって、、、、deploy、いけ〜！

　あら･･･・エラーだ。さっさとDiscordで質問。あ、deployボタンのすぐ上にある、Addボタンも押さなきゃいけなかったのね。はい、やり直し。

　行け〜！

　行きましたよ。完成です。提出リポートを書いて、修了書として

- 人生初NFTいただきました〜。

　現在41種類あるUNCHAIN Passportの中の　"Spientia"です。私はホモ・サピエンス。やった〜。
新人がよくやる間違い(MVCの領域侵犯編)

Railsを使ったアプリケーション制作を始めて2ヶ月ちょっと...。

こんな↓コードを書いていたら、先輩から「それはやばいよ」と指摘を受けました。

```erb
<% @users.each do |user| %>
	user_type = UserType.find(user.type_id)
	<div>
	<p><%= user %></p>
	<p><%= user_type.type_name %></p>
	</div>
<% end %>
```

なにがやばいのかわかりますか?

まあ、タイトルでネタバレしているのですぐわかってしまうと思うんですけど、**ViewでSQL文を書いている**んですね。端的に云うと、「それコントローラーでやれよ」ってことです。(さら言うとN+1問題も発生しているんですが、今回は割愛)

サンプルではリレーションのあるわかりやすい2つのモデルなので、コントローラーでらくらく書けるんですけど、

例えば直接的なリレーションのない2つのモデルを扱わないといけなかったり...

複雑な関係をもつ3~4つのモデルを同時に扱わないといけなかったり...

ぱっとSQLが書けないときに、「なんか違うな...」と思いつつも、「eachのループ中にSQL書いちゃえ」と安易な方法をとってしまっていました。

他にも、、、

```ruby
def index
	@current_date = Date.today.strftime("%Y年%-m月%-d日")
end
```

→コントローラーで、今の日付の表示のさせかたまで書いてしまっていたり...

```ruby
def index
	@all_users_names = User.all.pluck(:user_name).join('さん、') + 'さん'
end
```

→名前を連結させて、さん付けさせるところまで書いてしまっていたり...

```ruby
class User < ActiveRecord::Base

	scope :all_users_names, -> {.all.pluck(:user_name).join('さん、')}

end
```

→スコープなのにカラムを配列化してさらにjoinまでしてる...

**・コントローラーでやるべきこと(=モデルとビューの橋渡し)をビューやモデルでやってしまっている**

**・ビューでやるべきこと(コントローラーから渡されたデータの表示)をコントローラーでやってしまっている**

ということをやっちゃってました...。より抽象的に云うなら、MVCそれぞれの責任領域を逸脱したコードを存在させてしまっていたんですね。

### 何が問題なのか?

先輩からは、なんでやばいのかを聞いてなかったんですが、理由の1つはおそらく**他の人が手を加えるときにどこを見ればいいかわからないから**だと思います。

例えばこの部分で↓

```ruby
def index
	@current_date = Date.today.strftime("%Y年%-m月%-d日")
end
```

日付の表示の仕方を変えたいなってなったとき、普通なら該当するビューのhtmlを覗きにいきますよね。でも、ビューには日付の表示に関わる記述がないわけです。

もちろん、これは簡単なコードなので容易にコントローラーが怪しいってことはわかるんですけど、アプリケーションが複雑になればなるほど、どこを参照していいかがわからなくなる...。

動くプログラムではあるけれども、良いプログラムでない。

経験が浅いうちはとくに、書くコードがどの役割を持っていて、どこに書くのが適切なのか意識しておきたいですね!
【AWS】AWS ConfigでS3バケットの暗号化をチェックしてみました

# はじめに
AWSを利用するときに、構築したサービスがセキュリティガイドラインに準拠しているかどうかを図れるサービスとして、AWS Configがよく使われています。
本記事は、S3側でバケットの暗号化を有効化していないことを、AWS Configにて検知してもらう設定を試しました。

# S3側
S3側であるバケットを作成し、作成時にデフォルトの暗号化を無効と選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/865613/a9dee3e1-ddc3-9bfb-e736-35b2c4d02e05.png)

# Config側
ルールを追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/865613/7d385947-514f-9ecc-7078-3076bb3cb1b2.png)

``s3-bucket-server-side-encryption-enabled``を検索し、ルールを作成します。
当ルールの説明は、詳しく下記の公式ドキュメントをご覧ください。

https://docs.aws.amazon.com/ja_jp/config/latest/developerguide/s3-bucket-server-side-encryption-enabled.html

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/865613/faefd7f5-9946-e7dc-6c7d-aba00a114266.png)

作成したルールに、再評価のボタンを押下すると、
自動的にS3バケットに暗号化設定を行っているかどうかのチェックを走ります。
ボタン押下から反映するまで1分ほどかかる模様です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/865613/aad5923a-0fcd-14de-7190-be2fd45cc6c3.png)

ルール実行した結果、暗号化設定をしていないS3にを、
非準拠リソースとしてスキャンされました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/865613/602b8a0f-1642-f1ea-89b5-7f73d4cc2364.png)

# 思考
AWS Configを作成した時に、私はワンクリックで作ったため、
AWSが自動的にConfigを作ってくださいました。

個人的には上記のルールを実行した結果、
なぜかAWS Config用のバケットもスキャンされており、
非準拠リソースと認識されております。

おそらくワンクリックで作成時に、
Config用のS3バケットは、デフォルトで暗号化を有効化していない模様です。
Lichee RVでPythonを使ってみる


# Lichee RV Tina環境用のopkg

Lichee RV Tina環境用のopkgを公開してもらっているので、試してみた

customfeeds.confでリポジトリのURLを指定する。distfeeds.confは削除するかリネームして見ないようにする。

```
cd /etc/opkg
mv distfeeds.conf distfeeds.conf.bak
```

```
vi customfeeds.conf
```

customfeeds.confの最後に以下の行を追加する

```
src/gz snapshots http://opkg.gloomyghost.com/snapshots
```

以下のコマンドでpythonをインストール

```
opkg update
```

```
opkg install python3-lite
```

バージョンを表示してみる

```
python3 --version
```


ついでにnanoもインストール

```
opkg install nano
```

# 問題

Tina Linuxの環境は電源を入れ直すと初期化されてしまう。overlayfsでrootfsを更新できるようになっているが、永続化されない模様。WiFiのドライバーも同様。何とかせねば。


# 参考

https://bbs.aw-ol.com/topic/1046/搭建了一个-tina-的-opkg-仓库-软件包逐渐完善中/2
wailsにEchoを導入したら「blocked by CORS policy」が発生したのでCORS対応する

![go-1.17](https://img.shields.io/badge/go-1.17-brightgreen) ![wails-v2 beta](https://img.shields.io/badge/wails-v2.0.0(beta)-brightgreen) ![echo-v4.7.2](https://img.shields.io/badge/echo-v4.7.2-brightgreen)

## 前提
- wailsに別のweb frameworkを用いたい
- wailsにReactとEcho( https://echo.labstack.com/ )を用いてアプリケーションを作ってみた
- CORSのエラーが発生した

## 結論
- EchoにCORSの設定を行う
- 勉強目的でWebフレームワークを入れたがwailsでいろいろやってくれているので煩わしい気がする

## 事象
wailsにEchoを導入してfetchして見たところ下記エラーが発生しました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114149/b06b168b-1da6-b972-8bfa-e894ae5b326b.png)

> wails.localhost/:1 
> Access to fetch at 'http://localhost:8080/users/1' from origin 'https://wails.localhost' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

よく見かけるやつですね。
それよりもwailsの内部ではHTTPSになっているんですね。

## 対象方法
EchoのmiddlewareでCORS対策をする
https://echo.labstack.com/middleware/cors/

AllowOriginsに https://wails.localhost と設定します。

```diff_go:main.go
func runServer() {
  e := echo.New()
  e.Use(middleware.Logger())
  e.Use(middleware.Recover())
+  e.Use(middleware.CORSWithConfig(middleware.CORSConfig{
+    AllowOrigins: []string{"https://wails.localhost"},
+    AllowHeaders: []string{echo.HeaderOrigin, echo.HeaderContentType, echo.HeaderAccept},
+  }))

  e.GET("/users/:id", getUser)
  e.Logger.Fatal(e.Start(":8080"))
}
```

https://gist.github.com/mr-hana/4739163f70327b12e330243d139978d7#file-main-go-L46-L49

呼び出し側はfetchで行っています。

```tsx:App.tsx
  const handleEnterUserId = async function() {
    var apiUrl = `http://localhost:8080/users/${userId}`

    try {
      const response = await fetch(apiUrl , {method: 'GET', mode: 'cors'});
      const json = await response.json();
      setUser(json)
    } catch (error) {
      console.error(error);
    }
  }
```

https://gist.github.com/mr-hana/4739163f70327b12e330243d139978d7#file-app-tsx-L42-L52
GitLabサーバーの構築

# 概要
公式に記載されているオールインワンパッケージでインストールします。

https://www.gitlab.jp/install/#ubuntu

# 検証環境
Microsoft Hyper-V Serverにて、GitLabサーバーとなる仮想マシンを構築しました。
## GitLab用サーバー

<table>
   <tr>
     <td>OS</td>
     <td>Ubuntu 20.04.3 LTS</td>
   </tr>
   <tr>
     <td>IP</td>
     <td>192.168.137.61</td>
   </tr>
   <tr>
     <td>hostname</td>
     <td>gitlab</td>
   </tr>
   <tr>
     <td>GitLab</td>
     <td>Version 14.7.3-ee.0</td>
   </tr>
</table>

# 構築手順
## GitLabのインストール
1. [GitLabサーバー](#gitlab用サーバー)に接続します。

1. 以下コマンドを実行し、gitlabユーザーを作成し、sudoグループに追加します。

    ```Bash:gitlab
    $ sudo useradd -m -s /bin/bash gitlab
    $ sudo passwd gitlab
      "gitlabpass"と入力
    $ sudo usermod -G sudo gitlab
    ```

1. [GitLabサーバー](#gitlab用サーバー)にgitlabユーザーとして接続します。

1. 以下コマンドを実行し、依存パッケージのcurl、openssh-server、ca-certificatesをインストールします。

    ```Bash:gitlab
    $ sudo apt-get update
    $ sudo apt-get install -y curl openssh-server ca-certificates
    ```

1. 以下コマンドを実行し、依存パッケージのpostfixをインストールします。

    ```Bash:gitlab
    $ sudo apt-get install -y postfix
    ```

1. 「Ok」を選択します。
![Postfix Configuration_1](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1564963/5141432f-0cc3-03a2-157e-db79288d9c64.png)

1. 今回はメールサーバーの設定は実施しないので、「設定なし」を選択します。
![Postfix Configuration_2](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1564963/a9f0a347-b9b6-dd0a-96b4-63d6fb97fa17.png)

1. 以下コマンドを実行し、GitLabパッケージをリポジトリへ追加します。

    ```Bash:gitlab
    $ curl https://packages.gitlab.com/install/repositories/gitlab/gitlab-ee/script.deb.sh | sudo bash
    ```

1. 以下コマンドを実行し、GitLabパッケージをインストールします。
（GITLAB_ROOT_PASSWORDにパスワード"hogehoge"を渡し、パスワードの初期値を設定しています。）

    ```Bash:gitlab
    $ sudo GITLAB_ROOT_PASSWORD="hogehoge" EXTERNAL_URL="http://192.168.137.61" apt install -y gitlab-ee
    ```
## 動作確認

1. ブラウザにて[GitLabサーバー](#gitlab用サーバー)に接続します。
（502エラーが出た場合、まだ起動中の可能性があるので、少し待ってから再度アクセスしてみてください。）
![GitLabログイン画面](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1564963/7c79078d-93b1-e6a3-60b5-857f837f6ba8.png)

1. Usernameに"root"、Passwordに"hogehoge"と入力し、サインインします。
![GitLabログイン画面_root](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1564963/bd481eb6-b693-bf57-e2d2-dc03b6a64ba3.png)

1. 以下のようにログイン出来たら完成です。
![GitLab画面](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1564963/6f1230bc-9475-1d19-bf5a-48c738f0128f.png)
commitを整理したい

# やること
コミットの履歴を直したい場合に使用するコマンド等の解説をします。

# コミットを整理する際に使用するコマンド
## git commit --amend
`git commit --ammend`を使用すると、現在のステージングの内容を直前のコミットに含めることができます。

## git stash
現在の変更を一時的に退避するコマンドです。
直接コミットの履歴を改変するものではありませんが、例えば`git reset`（後述）などでコミットを作業ツリーに戻した際に使用したりします。

## git rebase -i
過去のコミットを対話形式で修正するためのコマンドです。

## git reset
コミットを取り消す（作業ディレクトリへ戻す）ために使用します。

# 実践
例えば以下のようなコミット履歴があるとします。

```
f4a348b class B をさらに修正
770be22 class B を一部修正
064dd2a class C を作成
55af461 class B を作成
2ef7ce5 class A を作成
```
修正分のコミットはコミットの歴史としてはノイズになりやすいので、可能であれば以下のような状態が望ましいとします。

```
064dd2a class C を作成
55af461 class B を作成
2ef7ce5 class A を作成
```

こちらの方がコミット履歴としては自然ですし、プルリクエストなどのレビューに際してレビュアーもレビューがしやすくなるでしょう。
そこで、過去のコミットを改変（整理）してみようと思います。

#はじめに手順概要

```
git reset HEAD~2      -- 直近のコミット2つを取り消す
git stash             -- 上記で取り消した変更を退避させる（後に取り込む）
git rebase -i HEAD~2  -- 過去のコミットを改変する状態とする
git stash pop         -- 2つ目の手順で退避した変更を作業ツリーに戻す
git add .             -- 上記で作業ツリーに戻した分をステージングに移動
git commit --amend    -- 過去のコミットに変更分を含める
git rebase --continue -- rebaseを終了
```

# 手順詳細
## git reset 
まずは`git reset`コマンドを使用して、コミットした変更を作業ツリーに戻します。
`git reset HEAD~2`
この時点で、コミットログは以下のようになります。

```
064dd2a class C を作成
55af461 class B を作成
2ef7ce5 class A を作成
```
最新のコミットが2つなくなり、変更箇所が作業ツリーに戻っています。
## git stash
`git stash`を使用して作業ツリーに残っている変更を退避させます。


## git rebase -i
次に、`git rebase -i`を使用して過去のコミットを改変します。
今回は、classBの変更分（現在`git stash`により退避中)をclassBを作成した際のコミットに含めるようにします。
`git rebase HEAD~2`を実行します。
すると以下のような編集画面が現れます。今回、コミットを改変したいのはClassBの作成に関わる箇所のため、一番上の行の左端の文言を`edit`に修正します。

```
pick 55af461 Class B を作成
pick 064dd2a Class C を作成
```
↓

```
edit 55af461 Class B を作成
pick 064dd2a Class C を作成
```
編集したら`:wq`で編集画面を抜けます。

この状態になると、ClassBを作成した時のコミットを修正状態となります。

## git stash pop ~ git commit --amend
先ほど`git stash`したClassBの変更点を`git stash pop`で再度取り込みます。
修正内容が作業ツリーに戻ってきているため、`git add`でステージングに追加します。
最後に`git commit --amend`を実行すると、Class Bを作成したコミットが、元々修正としてコミットされた変更分も合わせた状態となります。

# 余談
私も以前そうだったのですが、`Git`のコミット履歴が汚くなってしまう部分については往々にして作業の分割がうまくいっていないように思います。最初からコミット履歴が綺麗であればコミットの整理という作業も発生しないので、自分がこれからする作業についてどういった順序や粒度で進めていくかを一旦手を止めて考えてみるといいかもしれません。（予定を立ててもその通りにいかないというのは世の常ですが・・。）

# 終わりに
素敵な`Git`ライフを。
デプロイするDjangoアプリ（メモアプリ）にCSSを反映させる（2022.3.19）

<h1>背景</h1>
前回はDjangoアプリをGithub経由でHerokuにアップしてインターネットから見れるようにしました。せっかくインターネットで使えるなら、使いやすい様にUIもカスタマイズ出来る様にしたいです。


前回の投稿
[GitHubとHerokuを連携してDjangoアプリをデプロイする（2022.3.15）](https://qiita.com/tan0ry0shiny/items/149179d3d6a3f6c5aa73)

なお、説明や用語の使い方が間違っている事がありましたら、優しく教えて頂けると嬉しいです。その割に説明はいろいろ飛ばしているので、初心者には向かないものになっています。ただ、大雑把にどのようにするかは分かると思いますので取っ掛かりになれば幸いです。
<h1>目的</h1>
CSSを使って、見た目をカスタマイズしたいです。
<h1>方法</h1>
簡単な手順は

1. Djangoアプリを作る
1. サーバー環境で動くように調整する
1. デプロイする

になります。ほとんど前回と同じですので、今回は詳細な説明を省いている所があります。詳細は前回の投稿「[GitHubとHerokuを連携してDjangoアプリをデプロイする（2022.3.15）](https://qiita.com/tan0ry0shiny/items/149179d3d6a3f6c5aa73)」をご参照下さい。

<h2>準備</h2>
GitHub,Heroku,ローカルの環境を整えます。
詳細は

「[GitHubとHerokuを連携してDjangoアプリをデプロイする（2022.3.15）](https://qiita.com/tan0ry0shiny/items/149179d3d6a3f6c5aa73)」
をご参照下さい。
<h2>Djangoアプリを作る</h2>

[DjangoBoys](https://djangobrothers.com/tutorials/memo_app/introduction/)を参考にメモアプリを作ります。出来上がりは以下の様になります。以下の画像はローカルからブラウザに表示した物です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2496939/6d30b5be-b16f-09e4-66f5-a98f4eb308c0.png)

<h2>本番環境でCSSが反映される様にする</h2>
先程は開発環境でCSSが反映される様にしました。次は本番環境でCSSが反映される様にします。具体的にはwhitenoiseを使えるようにします。settings.pyにおいて、次の様に追記します。

詳細は「[Django と静的アセット](https://devcenter.heroku.com/ja/articles/django-assets)」をご参照下さい。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2496939/1250f73a-a385-3920-5d6f-846cb129c842.png)

最後に設定ファイルを作成していきます。下の画像はコマンドラインからファイルを作っていますが、どのように作っても構いません。詳細は「[とりあえず秒速でDjangoでホームページを作ってHerokuで公開するやつ（Windows対応）](https://qiita.com/hibit/items/d9b2ed158ac3b0ff7b64)」をご参照下さい。「Procfile」を作る際はアプリ名をご自身のアプリ名に変えるのを忘れないで下さい。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2496939/67a75aa7-4935-4e97-9f86-7291597e9734.png)

<h2>GitHub経由でHerokuにデプロイする</h2>
GitHubにpushした時点でHerokuにも反映されます。ただ、デプロイはされないので、手動でデプロイする必要があります。詳細は

「[GitHubとHerokuを連携してDjangoアプリをデプロイする（2022.3.15）](https://qiita.com/tan0ry0shiny/items/149179d3d6a3f6c5aa73)」
をご参照下さい。

<h1>結果</h1>
本番環境でもCSSが反映される様に出来ました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2496939/d91a77db-1d11-39bb-73ec-3d24a8fb15ad.png)

<h1>考察</h1>
公式の情報を見るのが一番良いとは思うものの、公式の情報を理解して実装するまでの実力が無い為、必然的にいろんなサイトを見る事になったりします。実力を付けるために記事を書いたりしますが、記事を書くためには更に多くの時間が必要になります。「情報を見つける＝＞理解する＝＞実装する＝＞記事にする」のサイクルを効率良く回せるようにしたいです。

<h1>課題</h1>
データベースとしてMySQLやPostgreを使いたい。

次の記事
[「.env」を使ってGitHubで安全なアプリ管理をする（2022.4.2）](https://qiita.com/tan0ry0shiny/items/f692d9f5ba6d85810030)
Laravelのレイアウトの継承とセクション【個人的なお勉強アウトプット】

## 参考図書
https://www.amazon.co.jp/dp/B08625YD7H/ref=sspa_dk_detail_1?psc=1p13NParams&spLa=ZW5jcnlwdGVkUXVhbGlmaWVyPUEyNlpDQ0JRWDc1RDdOJmVuY3J5cHRlZElkPUEwMDc4ODYzM0UxR0hGQk9QSkk0OCZlbmNyeXB0ZWRBZElkPUEyUTA4TzVGQ1MzNkNPJndpZGdldE5hbWU9c3BfZGV0YWlsJmFjdGlvbj1jbGlja1JlZGlyZWN0JmRvTm90TG9nQ2xpY2s9dHJ1ZQ==

## 継承
継承を使うことで各ページで共通するベースとなるレイアウトを用意して、展開できる。

### 継承とは
親の要素を子に受け継がせる

#### わかりやすい記事
https://wa3.i-3-i.info/word1202.html

## セクション
継承でページをデザインするときに、ページ内の要素として活用されるのが「セクション」。
セクションは、レイアウト用に用意される区画。
レイアウト用テンプレートにセクション区画を用意しておく。
そうすると子のファイルでセクションをはめ込んで表示することができる。
この継承とセクションによって、ベースとなるレイアウトから同じレイアウトのページを作っていける。

## @sectionと@yield
### @section
レイアウトで様々な区画を定義するために用いられる。
```
@section(名前)
hogehoge表示内容
@endsection
```
このセクションは同じ名前の@yieldにはめ込まれ表示される

### @yield
セクションの内容をはめ込んで表示するためのもの。
```
@yield(名前)
```
@yieldで指定した名前の@sectionがあると、yieldのところにはめ込まれる。

## 実例
### 継承元のレイアウトテンプレート
```layouts/layout.blade.php
<body>
<h1>@yield('title')</h1>
@section('meubar')
<h2>メニュー</h2>
<ul>
<li>@show</li>
</ul>
<div class="content">
@yield('content')
</div>
<div class="footer">
@yield('footer')
</div>
</body>
```
はめ込み先を@yieldで名前を指定していくつか用意。
土台となるテンプレート側で@sectionを用意する場合は、終わりは@showとする。
@showの中に継承を受けるファイルに記述した同名前の@sectionの内容が展開される。（ここ難しい）

### 継承を受けるファイル
```index.blade.php
@extends('layouts.layout')

@section('title', 'インデックス')

@section('menubar')
@parent
インデックスページ
@endsection

@section('content')
<p>本文のコンテンツ</p>

@section('footer')
copyright 2020 tuyano
@endsection
```
@extendsで継承元のファイルを指定
@sectionとすることで、テンプレート側の同名前の@yieldにはめ込まれる。
両方のファイルに同名前の@sectionが合った場合は、継承を受けるファイルに記載されてるものが優先される。
ただし、継承元の@sectionの内容を引き継ぎたい場合は@parentを書く。

## 展開の流れ
テンプレート側の内容をコピーして、継承を受けるファイルの中で展開しているようだ。
展開したあと(もしくは同時)に、@sectionで設定した第二引数や内容がyield内で展開されている、んだとおもう。
ローカルリポジトリのマージ済みブランチを一括削除する

# やること
ローカルブランチのうち、`develop`ブランチへマージ済みのブランチを一括で削除する。

# コマンド
```
$ git branch --merged | grep -i -v -E 'main|develop' | xargs git branch -d
```
## .gitconfig
私は上記のコマンドを`.gitconfig`に記載することで、`cleanup`で実行できるようになっています。具体的には`.gitconfig`に以下のように記載しています。

```
[alias]
        cleanup = "!git branch --merged | grep -i -v -E 'main|develop' | xargs git branch -d"
```
これで`git cleanup`が使えるようになります。実際に使ってみると、以下のようになります。

```
# 現在のブランチ一覧。branchAとbranchBは既にマージ済みだとする
$ git branch
  branchA
  branchB
* develop
  main

# cleanupの実行
$ git cleanup
Deleted branch branchA (was 0eebe36).
Deleted branch branchB (was 6549eaf).
```

ここまででこの記事の目的は達成しているのであとは蛇足ですが、せっかくなのでワンライナーの解説もしようと思います。

# 解説
`git`というよりもどちらかというと`unix`系のコマンドの知識になります。
## | (パイプ)
はじめに、`|`（パイプ）から説明します。
こちらは、前のコマンドの実行結果を後のコマンドに引き渡す役割を持ちます。
つまり、上記のコマンド`git branch --merged | grep -i -v -E 'main|develop'`の場合、`git branch --merged`の結果の中から`grep`による検索が行われることになります。

例えば、`git branch --merged`の結果が

```
* develop
  main
  branchA
  branchB
```
の場合、`grep -i -v -E 'main|develop'`は上記の結果に対して行われるため、

```
  branchA
  branchB
```
となります。

## git branch --merged
`git branch`コマンドは、branchの一覧を表示します。`--merged`オプションを付けることでカレンとブランチにマージ済みのブランチのみが表示されます。

## grep -i -v -E 'main|develop'
`grep`コマンドは文字列検索のコマンドになります。`grep hoge`とすることで文字列`hoge`を検索することができます。
以下、使用しているオプションを見ていきます。

### -i
`i`オプションは、大文字と小文字を区別せずに検索をします。上記の例で言えば、`hoge`も`HOGE`も検索に引っかかるということです。

### -v
`v`オプションは、**一致しない**文字列を検索します。

### -E 
-Eは正規表現を用いた検索を可能にします。[正規表現](https://ja.wikipedia.org/wiki/%E6%AD%A3%E8%A6%8F%E8%A1%A8%E7%8F%BE)については解説すると長くなるので割愛しますが、平たく言うと「特定の文字列パターンを表す文字列」のことです。
今回の`'main|develop'`という正規表現は、`main`という文字列もしくは`develop`という文字列のいずれかを指します。

まとめると、`grep -i -v -E 'main|develop'`は、`main`でも`develop`でもないブランチ名を検索していることになります。

## xargs git branch -d
`xargs`コマンドは前のコマンドの実行結果を受け取り、`xargs`の後に続くコマンド（この場合`git branch -d`）の実行パラメータとします。
つまり、`grep`コマンドの結果ひとつずつが`git branch -d`のパラメータとして渡されることで、`main`と`develop`を除くマージ済みのブランチが削除されることとなります。

# 終わりに
素敵なGitライフを！
GORMでデータベースのレコードが削除されない時の対処法

# はじめに
Gormを使用してデータベースを操作していましたが、レコードを削除した際にアプリ上で見ると消えているもののデータベース上には残っていることに気が付きました。
筆者の場合は、ユーザーのメールアドレスを一意な値にしたことで、「新規登録」→「ユーザー削除」→「同じアドレスで再登録」した際にデータベースでエラーが起きていました。

# 対処法
対処前に書いていたユーザー削除のコードは以下になります。

```go
func DbDeleteUser(id int) {
	d := db.GormConnect()
	d.Delete(&User{}, id)
	defer d.Close()
}
```

この書き方だと、論理削除といってレコードがデータベースから物理的に削除されるわけではなく、通常のクエリ系のメソッドで検索できなくなるだけのようです。

https://gorm.io/ja_JP/docs/delete.html


対処後のコードは以下になります。

```golang
func DbDeleteUser(id int) {
	d := db.GormConnect()
	d.Unscoped().Delete(&User{}, id) //ここを変更
	defer d.Close()
}
```

``Unscoped()``を追加することでレコードをデータベースから物理的に削除することができます。
Puppeteer + Node.js + Typescriptでサイトのデータを取得してみる

### 下記のようにサイトに存在する文字列をpuppeteerを用いて取得することを目的としたコードを書いたのでアウトプットします。
取得したかどうかはターミナルにlogとして出力しました。

:::note info
 puppeteerのライブラリを使用すること事態は調べながら感覚を掴むことができました。
 その中でも、メソッドの中でlogが出力できなかったため、なぜ出力できないのかを理解するのに時間がかかりました。evaluateメソッドで記述するコールバック関数は、ブラウザ内で実行されるため意図してターミナルにlogを出力することができないということが原因だったのかなと考えています。
 そのため、取得したいデータの取得に時間がかかりましたが実装はできました。
 Documentオブジェクトを用いてデータを取得する際はTypeScriptで型アサーションの方法をとらなくてはいけないので注意。
:::

サイトに存在する文字列
![](https://storage.googleapis.com/zenn-user-upload/08b216a80b33-20220319.png)


取得した文字列
![](https://storage.googleapis.com/zenn-user-upload/d0d148ed3d06-20220319.png)

:::note warn
puppeteerのevaluateメソッドでconsole.logが出力できない
解決のために参考にしたサイト
https://qiita.com/nobu17/items/5d073b7ec2df950e64f5
:::

```
import puppeteer from "puppeteer";
const getSiteData = () => {
 // ブラウザを開く
 const browser = await puppeteer.launch();
      // ページを作成する
      const page = await browser.newPage();
      // URLを準備
      const url = "サイトのURL";

      // サイトに進む
      await page.goto(url);
      // evaluateメソッドでサイト内のデータを取得する関数を取得する
      const datas = await page.evaluate(() => {
        // 配列を用意する(今回はanyの配列にする)
        const dataList: any[] = [];
	
	// Web APIであるDocumentオブジェクトのメソッドを利用してnodeのデータを取得
        const nodeListData = document
          .querySelector(".recruit-info")
          ?.querySelectorAll("h2") as NodeListOf<HTMLHeadingElement>;
	  
	// 標準組み込みオブジェクトのArrayクラスを用いてArrayインスタンスを作成
        const nodeList = Array.from(nodeListData);
	// Arrayを回して必要なプロパティを定義した配列に格納
        nodeList.forEach((_node) => {
          dataList.push(_node.innerText);
        });
	
	// データが格納された配列を返す。
        return dataList;
      });
      // 取得したデータの表示
      console.log("datas: " + datas);

      // ブラウザを閉じる
      await browser.close();

      return "成功";
    } catch (error) {
      console.log(error);

      return "失敗";
    }
 }
getSiteData()
```

参考にした記事やサイト
https://github.com/CircleAround/pgonline/tree/master/src/20210528_web_scraping
https://developer.mozilla.org/ja/docs/Web/API/Document
Blazor WebAssembly アプリの発行時に、"ランタイムの再リンク" を抑止する

# 背景

「.NET WebAssemblyビルドツール」をインストールすると、Blazor WebAssemblyアプリを Release 構成で発行する際に、"ランタイムの再リンク" が自動的に実行されます。

![".NET WebAssembly build tools" のインストールオプション](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8a2tl9eana7czoz8g24q.png)

**ランタイムの再リンクは、未使用のランタイムコードを切り捨てることで、"dotnet.wasm" のサイズを縮小**します。 
このサイズ削減は、Blazor WebAssemblyアプリのダウンロード速度に貢献します。

![.NET WebAssemblyビルドツール導入前後の「dotnet.wasm」サイズ](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kh1kphi27vpokz9mf7nn.png)
 
詳しくは、以下のリンクもご参照ください。

- [_".NET WebAssembly ビルドツール｜Microsoft Docs"_](https://docs.microsoft.com/ja-jp/aspnet/core/blazor/webassembly-native-dependencies#net-webassembly-build-tools)
- [_"ランタイムの再リンク｜Microsoft Docs"_](https://docs.microsoft.com/ja-jp/aspnet/core/blazor/host-and-deploy/webassembly#runtime-relinking)

ただし、**ランタイムの再リンクは発行に少し時間がかかります**。

もちろん、ほとんどの開発者にとって、これは問題ではありません。 
しかし、稀にランタイムの再リンクに時間がかかると、特に発行処理に関わる問題を調査する際に、開発者を悩ませることがあります。

# 解決策

幸いなことに、**手動でランタイムの再リンクを抑制することができます**。

必要なのは、**MSBuildプロパティの "UsingBrowserRuntimeWorkload "を "false"に指定**することだけです。

例えば、`dotnet publish`コマンドのコマンドライン引数でこれを行うことができます。

```shell
dotnet publish -c:Release -p:UsingBrowserRuntimeWorkload=false
```

このコマンドは 「.NET WebAssemblyビルドツール」がインストールされていないときと同じように動作するため、ランタイムの再リンクを行う場合よりも早く発行処理が終了します。

!["-p:UsingBrowserRuntimeWorkload=false" を指定した場合としなかった場合の発行処理速度の比較](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kaq4wt5y3ku0ev1ub4yt.png)

# 制限事項

"UsingBrowserRuntimeWorkload" MSBuild プロパティを "false" に設定することは、Blazor WebAssembly プロジェクトが "WebAssembly のネイティブの依存関係" 機能を使用していない場合のみ有効です。

Blazor WebAssembly プロジェクトが "WebAssembly のネイティブの依存関係" 機能に依存している場合、ビルドと公開処理は失敗するはずです。
Ipad Pro Liderで3DスキャンしたものをUnityにインポートする（オブジェクト編）

# はじめに
実験で、現実の環境をUnity上に落とし込み疑似AR状態にして実験をする必要が出てきた．3DCADを勉強し始めたが、いつまでたっても実験に入れない．そこで、IpadのLiderでスキャンしたデータで疑似ARができるのか試してみた．

# オブジェクト編とは？
現在、Liderスキャンの用途として主に2種類ある．一つ目が部屋まるごとスキャンする用途、二つ目がオブジェクト単体をスキャンする用途である．それぞれLiderデータのサンプリングの細かさを変えることでデータが大きくなりすぎないようにであったり、精度が粗すぎないように調整しているらしい．
今回は、オブジェクトをスキャンした結果について記述する．

# 試したアプリ
* [3d Scanner App™](https://apps.apple.com/jp/app/3d-scanner-app/id1419913995)
無料iphoneLiderアプリ．古くからあるそうでネット上に情報も多い．主な使い方は[【iPad Pro】3D Scanner App（LiDARスキャナ）で3Dモデルを簡単に作成する](https://styly.cc/ja/tips/ipad-lidar-3dscan/)を参照．
* [Polycam](https://apps.apple.com/jp/app/polycam-lidar-3d-%E3%82%B9%E3%82%AD%E3%83%A3%E3%83%8A%E3%83%BC/id1532482376)
課金型iPhoneLiderアプリ．最近のLiderアプリの中では評判が一番高い．値段も高い．主な使い方は[【iPad / iPhone Proシリーズ】LiDARアプリ「Polycam」で3Dスキャンをする](https://styly.cc/ja/tips/nimi-lidar-polycam-3dscan/)を参照．

# 撮影状況と撮影対象
## 撮影状況
iPadLiderは撮影環境によって色味(RGMセンサ)であったり、深度センシング（Liderセンサ）精度に差が出るらしい．そこで一応記述しておくと、大学のだだっ広い部屋でブラインドをして屋外の日光が入ってこないようにした．また、蛍光灯を全部点け特定の方向のみ光が強くならないようにした．
撮影機器はiPadPro(2020, 11inch)．

## 撮影対象
今回撮影するのは、研究室で実験に使っているスピーカーである．以下に写真を示す．
表
<img width=400 alt=DSC_0902.JPG src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2142992/01bc7690-234c-2f05-4273-5bcd6a11a456.jpeg>
裏
<img width=400 alt=DSC_0904.JPG src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2142992/0b4842d7-1c12-d965-0fd9-68063dea0ec0.jpeg>
スピーカーは立方体で、そのスピーカーの底辺面積と近い大きさの台があったのでその上に乗せた．

# 結果
## 3d Scanner App™
このアプリで撮影してOBJで出力した．それをWindows3D Viewerで見ると以下のようになる．
<img width=400 alt=image.png src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2142992/c2eb88ba-a609-531b-6dd4-91d000dd2d51.png>
凹凸感は再現しようと頑張っているのが見て取れる．実際に、スピーカーのコーン紙部分は若干再現されていた．しかし、全体的に歪んでしまっているのと、色の位置が合っていない場所があることがリアル感を失わせる原因になっていると感じる．

## Polycam
このアプリで撮影したものを無料で出力しようとすると、glbという形式になる．これはCADなどで読み込めず加工がしにくそう．出力結果を以下に示す．
<img width=400 alt=image.png src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2142992/7650b0ef-f8ad-77ba-08d0-d1c9f21d92b8.png>

若干バリがあるが、きれいにできている．コーン紙の凹凸感も良く再現できていた．エンクロージャー表面が滑らかでなく凸凹してしまっているが、もうこれはしょうがないんだと思う．

# UnityとOculusLinkで見てみる．
Polycamで撮ったデータをUnityとOculusLink,OculusQuest2を使ってみてみた．まず、Unityに入れた後のスクショを提示する．
Unityへのインポート方法は[KhronosGroup/UnityGLTFでglbファイルの読み込みを試す](https://bluebirdofoz.hatenablog.com/entry/2020/04/10/160721)を参照．
<img width=400 alt=image.png src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2142992/c3759ea7-af57-91bd-7898-16b786caa114.png>
色味が本物より、ちょっと濃く感じる．自分がUnity強々ではないのでどうやればいいのかわからないが、標準設定だと色が暗くなるみたいだ．

次にOculusQuset2で見てみる．オブジェクトの条件は、Unity上でのスピーカースケールが(1, 1, 1)で、Positionが(0, 0, -2)である．つまり2 m地点に頭部と水平に提示したという条件で出している．
<img width=400 alt=OculusScreenshot1647668409 (2).jpeg src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2142992/e85dcc36-13f9-408f-01b1-efdfc2d421d6.jpeg>
~~違いが分かりにくいというか、ほぼ同じようにOculusでも見ることができた．~~
~~一方サイズ感は小さく感じる．これはHMDの感じ方なのか、Lider側の問題なのか、Unity側の問題なのかはわからない．~~

スピーカーの高さを，目線でなく現実のスピーカーと同じ高さにして見比べてみると同じ大きさに見えた

# おわりに
今回、実際にiPadProを用いてLiderスキャナを用いたオブジェクトの3Dスキャンを行いUnityに取り込んでみた．結果としては、悪くない精度で撮れたのではないかと思う．用途によっては精度が足りないかもしれないが、自分がやる実験には十分であった．次は部屋のスキャンをしてみたいが、それはQiitaに載せられるのだろうか？
【AWS】Linux EC2のCPU使用率を上げる方法

# 結論
下記のコマンドを実行すれば、EC2を無限な循環グループに陥らせることができます。
よって、CPU使用率も当然上げることになります。
```
while true; do echo; done
```
# 実行結果
実行してから間もなく、CPU使用率が100％になっていることを観測できました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/865613/4a039e3e-47c0-6b77-ade0-a331c047334d.png)
Python 関数名を取得する

pythonのオブジェクト

```python
def my_func():
    return 

print(my_func.__name__)
```

```実行結果
'my_func'
```

# 使用例

## マルチスレッドの実行時にタスク名(関数名)をスレッド名にする

```python
from threading import Thread

def task1():
    while True:
        break

def task2():
    while True:
        break

task_list = [task1,task2]
for task in task_list:
    th = Thread(target=task, args=(), daemon=True, name=task.__name__)
    th.start()
```

## wrapperにして使う
@shiracamusさんにコメントいただきました。

```python
def greet(callback):
    print("Hello", callback.__name__)
    callback()

def taro():
    print("Hi, I'm Taro.")

def hanako():
    print("Hello, this is Hanako.")

greet(taro)
greet(hanako)
```

```:実行結果
Hello taro
Hi, I'm Taro.
Hello hanako
Hello, this is Hanako.
```
HTC Viveでトラッカーを超高精度トラッキング 

# はじめに
- HTC viveを使って誤差数mm単位でトラッカーをトラッキングする手法を紹介します。
- ロボットの位置推定など、実空間上の座標を扱う分野で活躍できると思います。

# 必要な機材
- HTC VIVE Tracker (2.0) もしくは (3.0) 
- HTC Base Station (1.0) もしくは (2.0) を2つ
- Windows PC 1台 （普通のノートPCでも動く）
- 三脚（ベースステーションの設置用、最後に後述してます）

# 必要なソフト
- SteamVR（Steamから検索してインストールする）
- Python3が動く環境

# 手順
## 機材のセットアップ
- ベースステーション2台を設置
- SteamからSteamVRをインストール

## SteamVRの設定を変更
- 以下の設定ファイルを開く
```
C:\Program Files (x86)\Steam\steamapps\common\SteamVR\resources\settings\default.vrsettings
``` 
- requireHmd を false に、activateMultipleDriversをtrueにして保存する。

## ドングルをPCに接続
- トラッカーに同梱されていたUSBドングルをWindows PCに接続し、SteamVRを再起動する。

## トラッカーの認識
- トラッカーの電源を入れ、2つのベースステーションから見通しが良い位置に設置する。
- 認識に成功するとSteamVRのアプリ内のトラッカーとベースステーションアイコンが青くなる。

## triad_openvrのダウンロード
- 作成者に感謝してgithubから[triad_openvr](https://github.com/TriadSemi/triad_openvr)をダウンロードして解凍する。

## 実行してみる
- ダウンロードしたフォルダ内の```tracker_test.py```を実行してみる。
- openvrが入っていない場合はエラーが出るのでインストールする。```pip install openvr```
- 成功するとトラッカーの6自由度が表示される。
- このコードをヒントにcsv等に書き込めるようにプログラムを書き換えることもできる。

# 補足
- 座標の原点はどちらかのベースステーションとなる
- サンプリングレートは最大250fps？要調査が必要
- 測定範囲は7m×7mあたりが限界
- 誤差はxとy軸で0.3cm程度、y軸で1cm程度
- 最低1台のベースステーションから見えていれば6自由度が取得できる
- 需要があれば書き足していきます

# 三脚について
広範囲で見通し内となるようにするため、ベースステーションは可能な限り高い位置から見下ろす形にすると良いらしい。そこで高い三脚が役に立つ。しかし、調べてみると分かるが2mを超えるような三脚は非常に高価である。安価かつ2mを超えるような三脚を調べ尽くした結果、[このようなものを見つけた](https://www.amazon.co.jp/Concept-%E3%82%AB%E3%83%A1%E3%83%A9%E4%B8%89%E8%84%9A%E3%83%96%E3%83%BC%E3%83%A0-%E3%83%9E%E3%82%AF%E3%83%AD%E6%92%AE%E5%BD%B1%EF%BC%94%E6%AE%B5%E4%BC%B8%E7%B8%AE-360%E5%BA%A6%E3%83%91%E3%83%8E%E3%83%A9%E3%83%9E%E9%9B%B2%E5%8F%B0-%E3%82%AF%E3%82%A4%E3%83%83%E3%82%AF%E3%82%B7%E3%83%A5%E3%83%BC%E5%BC%8F/dp/B0953G2H1H)。最大231cmまで伸ばせられる上に、使い勝手が良かったのでオススメである。


# 参考にした記事・文献
- [HMDなしでViveトラッカーを使いたい](https://ymremio.hatenablog.com/entry/2018/09/07/001155)
- [【Unity】ViveのHMDなしにTrackerを使う](https://shop-0761.hatenablog.com/entry/2018/01/08/034418)
- [HTC Vive as a Ground-Truth System for Anchor-Based Indoor Localization](https://ieeexplore.ieee.org/document/9222439)
- [HTC Vive: Analysis and Accuracy Improvement](https://ieeexplore.ieee.org/document/8593707)
【Flutter】contextresult::kfatalfailure: too few vertex attributes supported (0, should be 8).の解決法

いつものようにFlutterでWebViewを使おうとしたところ、WebViewに何も表示されなくなってしまった。

## エラー
```contextresult::kfatalfailure: too few vertex attributes supported (0, should be 8).```

## 原因
詳しいことはわからなかったのですが、どうやらエミュレータが原因っぽかったので、初期化してみたところ復旧。
それ以降は問題なくWebViewを使用することができています。

## 参考
https://stackoverflow.com/questions/51927200/android-log-cat-error-contextresultkfatalfailure
N番煎じですみませんが、SeleniumとC#でEdge操作する際の、初手メモ



:::note info
C#をスクラッチから書いて、コマンドラインでコンパイルするときのやり方です
:::


## １．webdriverを入手

手元のEdgeとバージョンと同じものを以下から取得

https://developer.microsoft.com/ja-jp/microsoft-edge/tools/webdriver/


## ２．Seleniumを入手

https://www.nuget.org/packages/Selenium.WebDriver

の、右側

Download package　から

https://www.nuget.org/api/v2/package/Selenium.WebDriver/4.1.0
selenium.webdriver.4.1.0.nupkg

を取得

（１）
得たファイルの拡張子をnupkgからzipにして中を開く
（２）
selenium.webdriver.4.1.0\lib　の中から、対応するバージョンの
WebDriver.dll
を得る。

## ３．サンプルソースを、以下から得る
https://docs.microsoft.com/ja-jp/microsoft-edge/webdriver-chromium/?tabs=c-sharp

## ４．以下を同階層において、コンパイル

EdgeDriver_1.cs
msedgedriver.exe
WebDriver.dll

```
c:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe /reference:WebDriver.dll /target:exe EdgeDriver_1.cs
```

## ５．ソースは以下

（４．のMSサイトのサンプルをほぼママ使用してます）
（注：EdgeOptionsで、エラーを出なくしている）


```csharp:EdgeDriver_1.cs
//c:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe /reference:WebDriver.dll /target:exe EdgeDriver_1.cs

using OpenQA.Selenium;
using OpenQA.Selenium.Edge;
using System.Threading;

namespace EdgeDriver_1
{
	class Program
	{
		static void Main(string[] args)
		{
			var options = new EdgeOptions();
			options.AddExcludedArgument("enable-logging");
			var driver = new EdgeDriver(options);
			try
			{
				driver.Url = "https://bing.com";
				var element = driver.FindElement(By.Id("sb_form_q"));
				element.SendKeys("WebDriver");
				element.Submit();
				Thread.Sleep(1000);
			}
			finally
			{
				//driver.Quit();
			}
		}
	}
}
```
【Java】アルゴ式 標準出力 1-5（一日は何秒か）

アルゴ式の「[標準出力 1-5](https://algo-method.com/tasks/73)」を、
Java の Time API を使って解いてみた。

```Java
import java.time.Duration;

public class Main {
    public static void main(String... args) {
        var dayInSeconds = Duration.ofDays(1).toSeconds();
        System.out.println(dayInSeconds);
    }
}
```

／以上
STM32L010とI2Cを使い0x80に「hello...」と出力する。(Keil)



x Mbed2

1.SCLとSDAを接続、プルアップも忘れずに
2.電源の接続
3.下記のソースコードを書き込む
4.コンパイル実行で表示されたら終了
5.おわり


![o_con270.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/1295d7aa-c474-fe42-2fe3-58baa4a5f867.jpeg)


![o_con271.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/70d4c76e-c9eb-c267-74e0-e354beb9f33d.jpeg)





STM32G031とVSCode+STM32CubeでI2Cスレーブの受信文字をシリアル出力(受信)(STM32-I2C-USART)(CH340N)




https://qiita.com/caa45040/items/5032e6905c9425f41495






なぜかわからないがmbed.bldのファイルを変更したら動いた

mbed.bld


```


https://os.mbed.com/users/mbed_official/code/mbed/builds/2e9cc70d1897



```



プログラム




```c++



//I2C_TEST_STRING_R1
// build https://os.mbed.com/users/mbed_official/code/mbed/builds/2e9cc70d1897

#include "mbed.h"

DigitalOut myled(PA_4);

#define ADDR (0x80)  //I2Cのアドレス

I2C i2c(PA_10, PA_9); //010

//10の割り算 0から1028までは、正しい。主に0から999
#define DVI10(n) ((n*205)>>11)

char ch_hex_a_b[16];
char *ch_hex_a(int x)
{

        int x_bk;

        ch_hex_a_b[4] = 0;

        if     ( x >= 9000 ) { x = x - 9000;   ch_hex_a_b[0] = '9';    }
        else if( x >= 8000 ) { x = x - 8000;   ch_hex_a_b[0] = '8';    }
        else if( x >= 7000 ) { x = x - 7000;   ch_hex_a_b[0] = '7';    }
        else if( x >= 6000 ) { x = x - 6000;   ch_hex_a_b[0] = '6';    }
        else if( x >= 5000 ) { x = x - 5000;   ch_hex_a_b[0] = '5';    }
        else if( x >= 4000 ) { x = x - 4000;   ch_hex_a_b[0] = '4';    }
        else if( x >= 3000 ) { x = x - 3000;   ch_hex_a_b[0] = '3';    }
        else if( x >= 2000 ) { x = x - 2000;   ch_hex_a_b[0] = '2';    }
        else if( x >= 1000 ) { x = x - 1000;   ch_hex_a_b[0] = '1';    }
        else                 {                 ch_hex_a_b[0] = '0';    }

        x_bk = x;

        x     =  DVI10(x);                           // 12 <- 123 / 10
        ch_hex_a_b[3] = '0' +  (  x_bk - (x*10)  );  // 3  <- 120 - 123
        x_bk  =  x;

        x     =  DVI10(x);                           // 1  <- 12 / 10
        ch_hex_a_b[2] = '0' +   ( x_bk - (x*10) );   // 2  <- 12 - 10

        ch_hex_a_b[1] = '0' + x;

        return(ch_hex_a_b);
	
} //ch_hex_a

//一文字出力
int pc_putc(char ch)
{
        
    i2c.write(ADDR, &ch , 1); 
    wait_ms(1);//1mS待つ　連続送信防止用　受信落ち対策

    return(0);

} //pc_putc

//文字列の表示 nana_seg
int pc_printf(char *str1)
{

    //文字の中身がゼロか
    while(*str1) {

    	pc_putc( *str1 ++); 

    } //while

    //戻り値
    return(0);

}//ns_printf

//メインプログラム
int main() {

    //無限ループ
    while(1){

        myled = 1;//LEDをオン

        //I2Cに文字列を出力
        pc_printf("hello world\r\n");

        //I2Cに文字列を出力
        pc_printf(ch_hex_a(1234));

        //I2Cに文字列を出力
        pc_printf("\r\n");

        myled = 0;//LEDをオフ

        wait_ms(1000);//1秒待つ

    }//while

} // main

//容量削減 約-3kバイト
void error(const char* format, ...){}



```
Docker で OOM の再現方法

メモです。

以下のコンテナを使いつつ、`docker run` の際に `-m` オプションでメモリのハードリミットを指定すると簡単に再現出来る。

[polinux/stress](https://hub.docker.com/r/polinux/stress)

```
# メモリの負荷を掛けるように起動
$docker run -m "10m" -d polinux/stress stress --vm-bytes 128M --vm 1
a6386b5adc9b444a0abc83fe747f444a41cd8372409140187b158e740dfb3e4a

# すぐに終了している
$docker ps -a |grep a6386b5ad
a6386b5adc9b   polinux/stress                   "stress --vm-bytes 1…"   3 minutes ago    Exited (1) 3 minutes ago             strange_kalam

# OOM により kill されたことが分かる
$docker inspect a6386b5ad |grep OOM
            "OOMKilled": true,
```