OmniAICreator/Qiita-1.07M huggingface dataset:
【Rails】バリデーションのuniqueness: trueについて

# はじめに

お疲れさまです！
おおくまです！

今回は、``Ruby on Rails``のバリデーションの``uniqueness: true``についてです！
基礎的なことなのですが、備忘録として残したいと思います！

# 注意点

:::note warn
内容に誤りがある場合があります！
コメント等で教えていただけると幸甚です！
:::

# 環境

:::note
Ruby 3.2.2
Rails 7.0.6
:::

# バリデーションとは

https://railsguides.jp/active_record_validations.html

``バリデーション``とは、``Ruby on Rails``の``Active Record``に搭載されている**正しいデータだけをデータベースに保存するための機能**です！

一般に利用可能な``ビルトインヘルパー``が用意されており、
- ``presnce``：指定された属性が空でないことをバリデーション
- ``numericality``：指定された属性に数値のみが使われていることをバリデーション
- ``length``：指定された属性の値の長さをバリデーション

などがあります！

また、**独自のバリデーションメソッドも作成できる**ようになっています！

``バリデーションヘルパー``や``カスタムバリデーション``については、こちらの記事が分かりやすかったです！

https://qiita.com/h1kita/items/772b81a1cc066e67930e#%E3%82%AB%E3%82%B9%E3%82%BF%E3%83%A0%E3%83%90%E3%83%AA%E3%83%87%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3

# uniqueness: trueについて

様々な種類がある``バリデーションヘルパー``の中から、今回は``uniqueness: true``に注目していきたいと思います！

<img width=800 src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3348444/351cbe61-9e4d-3ccf-0278-a7b80faf9e73.jpeg">

このような``データベース設計``を例に、``バリデーション``を考えていきたいと思います！

## uniqueness: trueとは

この``バリデーションヘルパー``は、``オブジェクト``が保存される直前に、**属性の値が一意であり、重複していないこと**を``バリデーション``します！

```diff_ruby:app/models/user.rb
class User < ApplicationRecord
  validates :email, uniqueness: true
end
```

このように書くことで、``バリデーション``が機能し、同じメールアドレスが``データベース``に登録されなくなります！
この``バリデーションヘルパー``のデフォルトの``エラーメッセージ``は「**has already been taken**」ですが、カスタマイズすることもできます！

```diff_ruby:app/models/user.rb
class User < ApplicationRecord
  validates :email, uniqueness: { message: "同じメールアドレスは使えませ〜ん" }
end
```

このように書くことで、入力フォームなどに任意の``エラーメッセージ``を表示させることができます！

## 応用的な使い方

最近学んだ、個人的に**目から鱗だった使い方**をご紹介します！

```diff_ruby:app/models/post.rb
class Post < ApplicationRecord
  validates :user_id, uniqueness: { message: '1人1つしか投稿できませ〜ん' }
end
```

``外部キー``である``user_id``に``uniquenessヘルパー``をつけることによって、1人1つしか投稿できないように制限することができます！

使うことは少ないかもしれませんが、私は先日実装する機会がありました！
すぐにこのような使い方が浮かばなかったので、頭の片隅に入れておくと、役に立つかもしれません！

# さいごに

本記事が何かのお役に立てれば幸甚です！
最後まで読んでいただきありがとうございました！
NutanixならOpenShiftもシュッとデプロイできるよという話

この記事は[Openshiftアドベントカレンダー](https://qiita.com/advent-calendar/2023/openshift)の16日目の記事です。恵比寿方面からのやんわりとした圧を感じてうっかりエントリーした@jyoshiseです。今年の7月からNutanixで働いているGAFA社長です。

# 仮想マシンとクラウドネイティブ

## 好きだぜKubeVirt
令和も５年が終わりつつあり人類はコロナ禍から立ち直ろうとしていますが、クラウドネイティブ＝コンテナ、みたいな認識が根強く残っている今日この頃です。皆様におかれましてはいかがお過ごしでしょうか。
常々思うのですが、クラウドネイティブってべつにコンテナ関係ないですよね。クラウドネイティブを実現しようとしたらコンテナが便利だから使うだけで。
また、Kubernetesっていうのも、コンテナオーケストレーションと呼ばれたりもしますけど、本質的にはリソースオーケストレーションであり、究極的にはあらゆるリソースをAPIオブジェクトとしてコントロールしてやるぜ、というものだと思っています。
というわけで、仮想マシンもKubernetesのリソースになってしまえ。というのがKubeVirtであり、それを利用したものがOpenShift Virtualizationです。[（赤帽エンジニアブログの関連記事）](https://rheb.hatenablog.com/archive/category/Red%20Hat%20OpenShift%20Virtualization)

私は、「おれとおれの猫以外世界の全てはKubernetesリソースになってしまえ、おれがkubectlしてやるぜ」という思想の持ち主なので（うそです）、KubeVirtは大好きで、OpenShift Virtualizationも応援しています。
## とはいえ全人類がKube APIを喋れるようになる日は遠い
とうぶん人類には仮想マシンが必要なのですが、寝言でKube APIを喋るような人間はまだまだ少数派です。世界のコンピュート、とくにエンタープライズワークロードの大部分は、従来型の仮想マシンの管理（ハイパーバイザーのクラスタとそれを管理するインターフェイス）の下に稼働しています。EC2とか。vSphereとか。その運用は、多くの場合「手続き的な運用」と言っていいと思います。
なんだかんだ、すべての運用をクラウドネイティブというかKubernetes Nativeにするのはハードルが高いわけです。なので、クラウドネイティブな運用（必ずしもコンテナというわけではありません）と、従来型の、手続き的な運用（必ずしも非コンテナというわけではありません）が並立する必要があるわけです。少なくともまだ当分の間は。

## いうてもベアメタルにKubernetesをデプロイするのはめんどくさい
めんどくさいですよね。めんどくさいですよ。

## というわけで現実世界では
下回りとして仮想マシン基盤があって、OpenShift Container Platformを含むKubernetesディストリビューションはその上にデプロイされているケースが多いと思います。

## あとストレージ問題な
パブリッククラウドのマネージドなKubernetesであればブロックストレージはデフォルトのStorageClassがあって使うだけだし、オブジェクトストレージもファイルサーバーもあるものを使うだけです。が、プライベート環境だとそれらも用意して管理しなければならない。めんどくさい。ODF？そんな子もいましたっけねえ・・・

## VMwareたん・・・
Broadcomさんの（以下自粛

# そこでOpenShift on Nutanixですよ
Nutanixなら、OpenShiftもシュッとデプロイできてしまいます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103746/bddbf95a-9a6e-d9d1-905f-746c5e93baad.png)

ハイパーバイザーはKVMベースのAHVというものを使っていて、なかなか良いものです。https://smzklab.net/2023/12/ahv-in-5min.html

あとNutanixの強みとしてはストレージ。コンピュートのクラスタと分散ストレージのクラスタが一緒になっているので、ブロックストレージ・オブジェクトストレージ・ファイルサーバーがいい感じに使えます。スナップショットやレプリケーションもばっちりです。
さらにいうとDBaaSもあって、KubernetesのOperatorからMySQL, Postgres, MongoDB, Oracle, MSSQLが利用できるので、OpenShift上で動くクラウドネイティブなアプリケーションがサクッとデータベースをコンシュームできます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103746/7ca67407-a366-79b1-8f30-93d3774c234d.png)

他にもいろいろ良いことがあるのですが、日付が変わりそうなのでこのへんで。OpenShift on Nutanixは良いですぞ！
便利なChrome拡張機能を2つ紹介します!!

### 最初に
今回紹介するツールはどちらも使い始めるまでの工程は一瞬です。1分あったら出来ちゃいます!!

# 1つ目：Search Result Previews
### どんなツール?
WEB検索の結果画面に、下記の赤い枠のような遷移先の画像も一緒に出せるツール
※表示される画像は **「最新のトップページのもの」** とは限らないのでご注意ください!!

![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1578844/bc79c72b-ed29-65ea-4e60-4b9d5cbd4c96.png)

### 始め方
1. [ココ](https://chromewebstore.google.com/detail/search-result-previews/cedcejfiniojnlhlfhcppenochinijfo)をクリック
1. 赤い枠をクリック
![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1578844/9da1a5d4-69a0-6244-4498-e0c99e5bf628.png)
1. 「Search Result Previews」がインストールされると、設定画面が表示される
1. 赤い枠（「Google.com」「DuckDuckGo.com」）にチェックがある事を確認
![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1578844/db033544-1ad6-5998-b4f2-c099b6476766.png)

### 使い方
1. いつも通りGoogle検索してみる（今回は【qiita】と検索した画面を貼っておきます）
![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1578844/0fdf9973-496e-e0e1-0190-240d2e9e63ca.png)

# 2つ目：ato-ichinen
### どんなツール?
Google検索結果を指定期間内のものに限定できる

### 始め方
1. [ココ](https://chromewebstore.google.com/detail/ato-ichinen/pojaolkbbklmcifckclknpolncdmbaph?hl=ja)をクリック
1. 赤い枠をクリック
![5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1578844/2c4c3eb3-b7a2-261b-7b19-495d435adc65.png)
1. 検索画面にオレンジ色の矢印の項目が増えていることを確認する
![6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1578844/d7f035bf-b0a0-6892-9e15-d23ba0034ec9.png)

### 使い方
1. いつも通りGoogle検索するだけ
1. オレンジ色の矢印で期間を1時間以内、1週間以内など変更もできます
[Godot 4.2] Sprite2D.get_rect() で取得できる座標の原点

:::note info
[Sprite2D.get_rect()](https://docs.godotengine.org/ja/4.x/classes/class_sprite2d.html#class-sprite2d-method-get-rect) で取得した矩形の座標は取得元の `Sprite2D` の `position` が原点になる。
:::

取得した矩形の座標を `Sprite2D` が存在する座標系に変換するには、 [`Node2D.to_global()`](https://docs.godotengine.org/ja/4.x/classes/class_node2d.html#class-node2d-method-to-global) を使いましょう。
```gdscript
sprite_2d.to_global(rect_2d.position)
```
ラズベリーパイでPlaywrightを使い検索のスクリーンショットを取りまくった話



ラズベリーパイだとPlayWrite対応してなさそうだったけど色々やったら動いた

https://github.com/microsoft/playwright/issues/4984


## やり方

### chromiumのインストール


https://raspida.com/install-chromium

```
$ sudo apt install chromium
$ which chromium
/usr/bin/chromium
```

後でNode.jsのコードにいれます。


## Node.js側コード


```json
{
  "name": "piapp",
  "version": "1.0.0",
  "description": "ラズベリーパイに利用するアプリケーション",
  "main": "index.js",
  "scripts": {
    "start": "NODE_ENV=production forever start -vc ts-node src/index.ts",
    "stop": "NODE_ENV=production forever stop -vc ts-node src/index.ts",
    "dev": "nodemon"
  },
  "keywords": [],
  "author": "",
  "license": "MIT",
  "dependencies": {
    "chrome-aws-lambda": "^10.1.0",
    "node-cron": "^3.0.3",
    "playwright-core": "^1.40.1"
  },
  "devDependencies": {
    "@types/node": "^20.10.4",
    "@types/node-cron": "^3.0.11"
  }
}
```


この記事が一番参考になった

https://github.com/microsoft/playwright/issues/2834


```js
const piChrome = '/usr/bin/chromium'; // for Raspberry Pi, after like `$ sudo apt install chromium`
import path from 'path';
import fs from 'fs';
import { chromium, devices, Page } from 'playwright-core';
import { nowSecondString } from './utils';

/** TypeのKeyをStringのUnion型にする */
export type NoStringIndex<T> = { [K in keyof T as string extends K ? never : K]: T[K] };
export type DeviceType = keyof NoStringIndex<typeof devices>;
export type DeviceDescriptor = (typeof devices)['Pixel 5'];

export const playwrightScreenshotOnlyLocal = async (page: Page, text?: string) => {
  if (process.env.NODE_ENV === 'production') return;
  await page.screenshot({
    path: path.resolve(__dirname, `../downloads/${nowSecondString()}.jpg`),
    fullPage: false,
  }).catch(_ => undefined);
};

export const googleCheck = async () => {
  const browser = await createChromeBrowser();

  const page = await browser.newPage();
  await page.goto('https://www.google.com/search?q=新宿の天気');
  console.log('await page.title(): ', await page.title());

  await playwrightScreenshotOnlyLocal(page)

  return await browser.close();
};

const defaultOption = {
  headless: true,
  slowMo: 333,
  timeout: 150000, // 120 sec
  args: [
    '--lang=ja', // 日本語対応
  ],
};

export const createChromeBrowser = async () => {
  let executablePath: string | undefined;
  executablePath = fs.existsSync(piChrome) ? piChrome : undefined;
  if (executablePath) return chromium.launch({ executablePath, ...defaultOption });
  return chromium.launch(defaultOption);
};
```


だいたいこんな感じのコードで動きました。


MacOSで開発しているときは `/usr/bin/chromium` にchromiumがなく `npx install playwrite` でインストールした実体を動かして、ラズベリーパイのときは `/usr/bin/chromium` で動かしました。

これで毎日朝7時に新宿区の天気をスクショして保存するという動作を node-cron で走らせられるようになりました。
![20231216_2226794.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/112929/5d943866-72ed-1d14-6f54-91fec8757e37.jpeg)
Ruby Gold3.1合格後の開発等から感じたこと

# はじめに
Ruby Gold3.1合格した後、先輩とのお話やあるシステムの開発に携わる中で「これはRuby Goldで学んだ知識が生きている！」と感じたことをまとめてみました！

# 感じたこと
### 1.rubyとrailsのメソッドを区別できた
[合格記事](https://qiita.com/Takaharu_01/items/ad583a1c78a6290f7954)にも記載した内容になります。先日先輩からあるメソッドに関して質問を受け、そのメソッドはruby goldの勉強の中では触れていなかったため、もしかしたらrailsのメソッドなのではないかと推測できました。
Ruby Gold合格前まではrubyの知識不足によりrubyとrailsのメソッドが混同してそのような推測はできなかったのですが、Ruby Goldの勉強でrubyのメソッドの知識が増えたことで、知らないメソッドはrailsのメソッドかもしれないという推測ができたと思います。
 
### 2.例外処理周りの実装に強くなった
begin rescueを使用して例外処理を組む機会があり、例外が発生した時にどの例外クラスのrescue節でエラーキャッチしていくのか、StandardErrorのrescue節でまとめてエラーキャッチしてもいいのか等より深く考えられるようになりました。
考えらえるようになった理由は、例外クラスの種類や例外が発生した際のrescue節でのエラーキャッチ方法等の知識をRuby Goldで学んだためだと思います。

### 3.オブジェクト指向の話についていけた
先輩がRubyの標準ライブラリのあるクラスを用いて実験されており、その実験結果が所望の結果がでてなさそうということで、再度実験され今度は所望の結果になったとお話されました。原因はなんだったんですか？と質問すると、「同じインスタンスを使い回していたことが原因だった」と回答されました。
回答された後、私は「新しいインスタンスをnewで作り直してメソッドを呼び出されたのかな」という思考が瞬時にでてきて、オブジェクト指向関係の知識も学んでいてよかったなと思いました。

# おわりに
Ruby Goldで得た知識を実務で生かせていると実感でき、よりRuby開発の面白さ楽しさを感じるようになりました！開発のパフォーマンスを向上させるために、Ruby Gold（Silver）に挑戦されることは個人的にはおすすめかなと思うので、気になる方はぜひ挑戦いただければと思います！
ちなみに私がRuby Gold（Silver）受験しようと思ったきっかけは先輩がRuby Gold（Silver）合格されていて、実務と繋がる部分がありいい試験だよ！とお話を聞き、私も挑戦してみよう！と思ったことがきっかけです。
[Godot 4.2] その場で座標だけ親から独立する

:::note info
以下のコードで実現できます。
```gdscript
	var p = global_position
	top_level = true
	position = p
```
:::

こうすると、ノードがルートにあるかのように振る舞いはじめます。
ここで `global_position` を保存し忘れると、座標がどっかへ行ってしまうことがあるので気をつけてください。

参考: [CanvasItem — Godot Engine (4.x)の日本語のドキュメント](https://docs.godotengine.org/ja/4.x/classes/class_canvasitem.html#class-canvasitem-property-top-level)
rex0220 項目検証プラグイン ドロップダウン選択肢による必須チェック

項目検証プラグインによる、ドロップダウン選択肢による必須チェックの例です。

# 概要
ドロップダウン選択「媒体」の選択肢による必須チェックの例です。
「その他」以外の場合、文字列項目の「その他」を非表示。
「その他」の場合、文字列項目の「その他」を必須にします。

![2023-12-16_22h08_25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/865eb92c-4a04-c0f3-7afc-808c1e09dff5.png)

# 操作例

![2023-12-16_22h17_02.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/e1c1f45e-1613-cd1d-a329-6e2e5d0ff609.gif)


# 項目検証プラグイン設定

「初期化」をチェックすると、項目が「非表示」の場合、レコード保存時に、項目をクリアします。

![2023-12-16_22h10_54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/e4bd2caa-83c4-2a14-86d6-f8191edd4aa2.png)

```.js
// その他 必須条件
媒体="その他"

// その他 非表示
媒体!="その他"
```
JetBrainsのAIアシスタントを使ってみた

## 概要

普段IDEとしてJetBrainsのものを利用しているのですが、
最近AI Assistant機能はリリースされた。

https://www.jetbrains.com/ja-jp/ai/?map=4

完結に説明すると生成AIを使った開発の補助機能となっている。
試しに使ってみたので感想などを書きます。
今回はGolandでのみ利用していますのでご注意ください。

## どんな機能があるのか？

機能を箇条書きすると

- AIとのチャット
  - 英単語の相談ができたり、SQLを書いてくれたりと相談相手として活用できます。
- コード提案
  - メソッドを書いただけでもコード提案を自動で行ってくれます。（数秒のラグはある）
  - また、書かれるのではなく、提案のため内容を読んで拒否することも可能です。
- ドキュメント作成
  - 関数のコメントを作成してくれます。
  - 他にもコミットメッセージもコミット内容から判断して作成してくれます。
- リファクタリング提案
  - 対象個所のリファクタリング案を質問するとコードを提案してくれます。
- 名づけ補助
  - 変数名だったり、メソッド名など、ふさわしそうな単語を提案してくれます。
  
機能ごとに感想やおもったころを記載します。

### AIとのチャット機能

ChatGPTを利用したことがある人なら抵抗なく利用できると思います。
特別な使い方も必要とせずに利用でき、日本語で質問しても日本語で回答が返ってきました。

### コード提案機能

非常に使い勝手がよかったです。提案された内容を採用したことも割合としては多かったです。
特に似たような実装をする場合などには有効でした。
ただ注意しなければいけない点としては、メソッド宣言して中身を提案してくれるという流れで動作するのですが、メソッド宣言が近いのに中身が全然違ったりと命名規則をちゃんとしないと提案される内容が全然意図していない作りになっていることがありそうでした。
個人的には、提案という形式であることが非常にうれしかったです。
実態としてはまったく書かれていない状態なので間違って採用してしまうことがまず起こらないので安心して機能を利用するスタンスになれました。

### ドキュメント作成

関数のメソッドやコミットメッセージを作成してくれるのですが、
多分英語でしかかかれません。（設定に気づいてないだけかも）
OSSをする人にとっては英語で書かれてくれることは便利だと思います。
関数のメソッドに関しては要約コメントだけでなく、使い方例も記載されることもあったが、
あるときとないときがあるのでAIの判断次第といった結果でした。
コミットメッセージに関してはうまく書かれている印象があり、英語以外に出力できると活用の幅が広がりそうだと思いました。

### リファクタリング機能

1度くらいしか使っていないのですが、そのときの提案は理にかなっていると思いました。
使ってみたケースとしては重複したコードがあるのでリファクタリングしようとしたときに使ってみましたが、適切に書き直す提案がされたと感じベース案として採用しました。
リファクタリングは壁打ちしていろいろ考えるフェイズでもあると思うので壁打ち相手として利用できるのでは？と思いました。

### 名づけ補助

今回はあまり使えませんでした。正確には候補を出す前にチェット機能で聞いた単語を採用してしまったりと機能を使う前に解決してしまっていた記事を書くときに機能があることに気づきました。
この機能は今後気づいたことがあれば書き足そうかと思いました。

## 総括

お試し期間が短いのでお試しで利用する方は注意していただきたいですが、
使ってみた感想としては便利だったと思いました。
エンジニアとして生成AIと向き合うことは非常に重要なスキルになると思っています。
ただ、やはり判断はちゃんとしなければいけない要素もまだまだあることが現実でした。
その点、実装が提案をするスタンスをとっていることが非常に好印象でした。
提案だから利用者が採用判断するまではコードに書かれている状態でもないので気づかないうちに採用されていたことはほぼ起きないと感じました。
生成AIとエンジニアの付き合い方という点ではいい関係を気付けるものだと思います。
ここまで進化してくると生成AIの存在とうまく付き合えることもエンジニアの必須スキルになってくるんだと思いました。

聞いてみたいことがありましたらご連絡ください。
すべて答えられるとは限りませんが可能な範囲で対応させていただきます。
str_pad()関数

# str_pad()関数
| 第1引数 | 第2引数 | 第3引数 | 第4引数|
|----|----|----|----|
|入力文字列|桁数|埋める文字列|埋め方の形式|

# 第4引数
- 中寄席
STR_PAD_BOTH

- 右寄せ
STR_PAD_RIGHT

- 左寄せ
STR_PAD_LEFT
:::note info
str_pad()関数
[公式マニュアル](https://www.php.net/manual/ja/function.str-pad.php)
:::

# 実行・結果
$value=Aでそれぞれの場合を試す
```php
//中央寄せ
$value = "A";
$result = str_pad($value, 5, 0, STR_PAD_BOTH);
var_dump($result);
string(5) "00A00" 


//右寄せ
$value = "A";
$result = str_pad($value, 5, 0, STR_PAD_RIGHT);
var_dump($result);
string(5) "0000A"


//左寄せ
$value = "A";
$result = str_pad($value, 5, 0, STR_PAD_LEFT);
var_dump($result);
string(5) "A0000" 
```
[Godot 4.2] 文字列を1文字ずつ表示する

:::note info
[Tween](https://docs.godotengine.org/ja/4.x/classes/class_tween.html) を文字列に対して使います。
:::

```gdscript:sample_code.gd
extends Label


func _ready():
	text = ""
	var tween = get_tree().create_tween()
	tween.tween_property(self, "text", "任意の文字列をTweenで表示する。", 1.0)
 ```

 [アニメーション機能](https://docs.godotengine.org/ja/4.x/tutorials/animation/index.html) で線形補間をすることでも同様のことができます。
 そのときに「この補完は試験的な機能です」みたいなことを言われる(スクショ撮り忘れた)ので、それは覚えておきましょう。
Ubuntu22.04の始め方

# インストールのすぐ後に
```
sudo apt-get update
sudo apt-get upgrade
sudo apt-get install git wget curl ca-certificates gnupg lsb-release
```
# 環境設定
## ホームディレクトリのディレクトリ名を英語にする
```
LANG=C xdg-user-dirs-gtk-update
```
「Don't ask me this again」にチェックを入れて、「Update Names」 をクリックする
## CtrlキーとCapsLockキーを入れ替える　（お好みで）
```
gsettings set org.gnome.desktop.input-sources xkb-options "['ctrl:swapcaps']"
```
2023年 Mac OS – Option + Tab キーで、同じアプリ内のウィンドウを切り替え可能にする

# 方法

設定 > キーボード > キーボードショートカット  > キーボード > 次のウィンドウを操作対象にする 

でOption+Tabを設定すればOK


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/89618/8e546f1f-2cd5-ecd0-5a9b-c3b110db61b5.png)

# キーボードショートカット設定はどこ？

けっこう階層が深くて分かりにくいが

設定 > キーボード > キーボードショートカット > キーボード とたどる

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/89618/3b1a25e9-02ec-9d3a-be8c-6758153bcc4a.png)

# チャットメンバー募集


何か質問、悩み事、相談などあればLINEオープンチャットもご利用ください。

https://line.me/ti/g2/eEPltQ6Tzh3pYAZV8JXKZqc7PJ6L0rpm573dcQ


# Twitter

https://twitter.com/YumaInaura
LangChainからSQLiteデータベースを操作してみた（create_sql_agent）

## ゴール
LangChainとAzureChatOpenAIを使用して、PythonでSQLiteデータベースとのインタラクションを実現するエージェントを実装する方法を説明します。

## スタートライン
- 基本的なPythonの知識があること
- Azureのサブスクリプションを持っていること
- LangChainとOpenAIのライブラリがインストールされていること

## 環境
- Windows 11
- Python 3.11.5
- OpenAI
- LangChain
- AzureChatOpenAI

## ステップ1：環境設定と依存関係のインポート

### 環境変数
```.env
OPENAI_API_KEY = "[Azure OpenAIのAPIキー]"
OPENAI_API_BASE = "[Azure OpenAIのエンドポイント]"
```
　　
### ライブラリのインストール
```
pip install langchain
pip install openai
```
　　
### データベースの準備
https://www.sqlitetutorial.net/sqlite-sample-database/ の「Download SQLite sample database」選択し、サンプルのデータベース「chinook.db」をダウンロードします。使用ディレクトリ内に「notebooks」フォルダを作成し、ダウンロードした「chinook.db」を配置します。
　　
### 必要なライブラリのインポート
必要なライブラリをインポートし、環境変数を読み込みます。

```test.py
from dotenv import load_dotenv
load_dotenv()

from langchain.agents import create_sql_agent
from langchain.agents.agent_toolkits import SQLDatabaseToolkit
from langchain.agents.agent_types import AgentType
from langchain.chat_models import AzureChatOpenAI
from langchain.sql_database import SQLDatabase
```

このコードは、LangChainのSQLエージェントとAzureChatOpenAIを使用するためのライブラリをインポートします。また、`.env`ファイルから環境変数を読み込むためのload_dotenv関数も含まれています。

## ステップ2：データベースとモデルの設定
次に、SQLデータベースとLangChainで使用するチャットモデルを設定します。
`YOUR PATH`部分はchinook.dbを配置したご自身のパスに設定してください。
また、Azure OpenAIの設定もご自身の環境に合わせてください。

```test.py
db = SQLDatabase.from_uri("sqlite:///<YOUR PATH>")

llm = AzureChatOpenAI(
    temperature=0,
    azure_deployment="gpt-35-turbo-16k",
    openai_api_version="2023-07-01-preview",
)

toolkit = SQLDatabaseToolkit(db=db, llm=llm)
```

ここでは、SQLiteデータベースを指定してSQLDatabaseオブジェクトを作成し、AzureChatOpenAIを設定しています。toolkitはデータベースと言語モデルを組み合わせて機能します。


## ステップ3：エージェントの実行
最後に、SQLエージェントを作成し、実行します。

```test.py
agent_executor = create_sql_agent(
    llm=llm,
    toolkit=toolkit,
    verbose=True,
    agent_type=AgentType.OPENAI_FUNCTIONS,
)

agent_executor.run("質問内容")
```

このコードは、SQLデータベースと対話するエージェントを作成し、ユーザーの質問に応じて動作します。agent_executor.runメソッドに質問を渡すことで、エージェントが質問に答えます。

## 結果
質問：いくつのデータが存在しますか？

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3124507/cfc5614c-653d-e96d-09db-56a427698637.png)

　　
質問：albumsのデータの中で、Aから始まるデータを抽出してください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3124507/190fa748-5442-c032-33bb-8223b521f149.png)

　　
質問：「invoices」テーブルから、国別の合計売上を計算してください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3124507/69f99780-dd22-6651-40d5-e15e3858fa21.png)

　　
質問：「invoices」テーブルから最新の10件のインボイスを日付順に取得してください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3124507/16a3eb01-b84b-1354-be12-aff653f7db37.png)


## フィニッシュ
お疲れ様でした！

以下のXで情報発信も行っています！

https://twitter.com/t___tatsuki

## 参考文献
https://python.langchain.com/docs/use_cases/qa_structured/sql/
AWS Route53を利用してドメインの作成

AWS Route53を利用してパブリックドメイン発行の方法を簡単に紹介します。

1. Route53の画面に遷移します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2239717/bf9842d4-daa2-a44a-c57f-36bb8eb84437.png)
2. ドメイン登録をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2239717/482a424a-4ab9-81ed-5b9a-eb4367ab8064.png)
3. 欲しいドメイン名を入力してチェックしましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2239717/da7a078d-466b-e28b-148a-763177076788.png)
利用可能な場合に、チェックアウトに進むをクリックして次のページに進みます。
4. 料金設計画面で最終確認します。自動更新しないように、自動更新のチェックボックスをオフにします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2239717/4c87f07e-53b5-a7d2-0a01-eb4657fee23a.png)
5. 以下の感じで情報入力します。
今回は個人利用のため、プライバシーの保護をオンにします。そうしない場合に、whoisでドメイン所有者の情報を確認するに、自分の情報が丸見えのため。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2239717/1df66453-0617-75de-a0f7-0affe35ad2a9.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2239717/7503be00-59a3-3b11-7d35-e5e46997f4dd.png)
6. 確認画面で最終確認したら、送信ボタンを押下してください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2239717/0749a770-b12d-0372-da18-714fe82b0c5d.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2239717/1005fe87-5a73-1ba8-c694-e02c566ba782.png)
7.作成中の場合に、以下の感じで進行中と表示されています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2239717/74ec6550-895d-7a02-c862-34e253886fe9.png)
８. ホストゾーンに遷移して作成されてことを確認します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2239717/641e0b4e-1b27-d159-2cd6-6d8fa15b486e.png)
9.これでドメインの申請が完了しました。
[Godot 4.2] アニメーションの向きを反転させる(5秒で)

:::note info
アニメーションで動かす対象ノードの親の `Scale` を `-1` にします。
:::

## サンプル

まず下記画像の通りのシーン構成を作ります。
その後、画像中の `Sprite2D` を左右に往復させるアニメーションを作ったとします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/699119/5c707a47-6c29-600c-a3cc-2a6e0bd10def.png)

`Sprite2D` の親である `Node2D` の `Scale.x` を `-1` にします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/699119/94c6eb95-e9bd-968c-517e-b2b51aea5ac7.png)

これで同じアニメーションを使いつつ、アニメーションの向きを反転させることができます。
ただし、 `Sprite2D` も反転しているので、適宜 `flip_h` で反転させてください。
pnpm create next-appしたら文句言われた

dockerでnextのプロジェクトを作ろうと`pnpm create next-app`したら
```bash
$ pnpm create next-app
.local/share/pnpm/store/v3/tmp/dlx-14128 |   +1 +
.local/share/pnpm/store/v3/tmp/dlx-14128 | Progress: resolved 1, reused 1, downloaded 0, added 1, done

Aborting installation.
Unexpected error. Please report it as a bug:
 Error: EACCES: permission denied, mkdir '/home/user/.config/create-next-app-nodejs'
    at Object.mkdirSync (node:fs:1380:26)
    at Conf._ensureDirectory (/home/user/.local/share/pnpm/store/v3/tmp/dlx-14128/node_modules/.pnpm/create-next-app@14.0.4/node_modules/create-next-app/dist/index.js:8:81462)
    at get store [as store] (/home/user/.local/share/pnpm/store/v3/tmp/dlx-14128/node_modules/.pnpm/create-next-app@14.0.4/node_modules/create-next-app/dist/index.js:8:80100)
    at new Conf (/home/user/.local/share/pnpm/store/v3/tmp/dlx-14128/node_modules/.pnpm/create-next-app@14.0.4/node_modules/create-next-app/dist/index.js:8:77755)
    at run (/home/user/.local/share/pnpm/store/v3/tmp/dlx-14128/node_modules/.pnpm/create-next-app@14.0.4/node_modules/create-next-app/dist/index.js:74:46748)
    at 4282 (/home/user/.local/share/pnpm/store/v3/tmp/dlx-14128/node_modules/.pnpm/create-next-app@14.0.4/node_modules/create-next-app/dist/index.js:74:52233)
    at __nccwpck_require__ (/home/user/.local/share/pnpm/store/v3/tmp/dlx-14128/node_modules/.pnpm/create-next-app@14.0.4/node_modules/create-next-app/dist/index.js:74:59961)
    at /home/user/.local/share/pnpm/store/v3/tmp/dlx-14128/node_modules/.pnpm/create-next-app@14.0.4/node_modules/create-next-app/dist/index.js:74:60825
    at Object.<anonymous> (/home/user/.local/share/pnpm/store/v3/tmp/dlx-14128/node_modules/.pnpm/create-next-app@14.0.4/node_modules/create-next-app/dist/index.js:74:60869)
    at Module._compile (node:internal/modules/cjs/loader:1376:14) {
  errno: -13,
  code: 'EACCES',
  syscall: 'mkdir',
  path: '/home/user/.config/create-next-app-nodejs'
}
```
みたいな文句言いだした。

```bash
mkdir ~/.config/create-next-app-nodejs
sudo chown -R $(whoami) ~/.config/create-next-app-nodejs
```

これで黙らせましょう。
rex0220 項目検証プラグイン ユーザー選択項目間の重複チェック

[項目検証プラグイン](https://qiita.com/rex0220/items/be20fd879f5e1bc9e035)で、ユーザー選択項目間の重複チェックを行います。

# 概要
ユーザー選択1 に、ユーザー選択2 で選択したユーザーがあったらエラーにします。

![2023-12-16_21h32_47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/71883a51-0ded-8b55-eb66-a88b3bca9050.png)

# 操作例

![2023-12-16_21h42_17.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/68d99bad-9d85-a4d1-a292-efff6759af3d.gif)

# 項目検証プラグインの設定
ユーザー選択1とユーザー選択2 の必須チェックと、ユーザー選択2 のカスタムバリデーションチェックをセット

![2023-12-16_21h34_23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/92f0def2-c0d9-14fc-019b-de93af8057ad.png)

- ユーザー選択2 のカスタムバリデーションチェック
ユーザー選択1 に、ユーザー選択2 で選択したユーザーがあったらエラーにします。

![2023-12-16_21h35_20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/7b9f2ffe-7394-7eda-b0bc-9597c87d6cbb.png)

- ARRAY_SOME: 配列内の少なくとも1つの要素が指定された条件を満たしているか判定します

```ユーザー選択2.js
// 条件
ARRAY_SOME(
  SEL_CODE(ユーザー選択2,"array"), x, 
  IN(SEL_CODE(ユーザー選択1,"array"),x)
)

// メッセージ
"ユーザー選択1と重複"
```
for と if を書かないFizzBuzz ≪Python≫

とある社会人4年目になり、後輩を１人もつ先輩社員となった時のおはなし...

# 記事を書いた経緯
現在進行形で社会人4年目で後輩１人連れて現場にいるような形でプログラマーとして働いています。
その後輩が入る前に、どのレベルなのか知るために軽い問題を解いてもらいました。
その時、解答を作ったのですが、後々考えると見やすく処理的にもよいはずがつまらない解答を作ったなと思い、今更もう一度作って見せるのも気が引けたのでここで珍解答を紹介します！

# 問題
その時出した問題の１つの「**FizzBuzz**」問題のはなしです。
知らない人のために **FizzBuzz** 問題とは簡単に「1~100の数字で3で割り切れる値ならFizz、5で割り切れる値ならBuzz、3と5どちらでも割り切れる値はFizzBuzzと表示して残りはそのまま数字を出力する」問題です。

# 回答
※ 考える問題なのでざっくりと書きますが詳細は書きません。
Pythonで書いてもらったのですが、後輩くんが書いてもらった解答はよくある形。
```Python
for i in range(100):
    # 3と5で割り切れるケース
    # 3で割り切れるケース
    # 5で割り切れるケース
    # そのまま出力するケース
```

私のほうでこう書けたらいいねのケースが以下の形。
```Python
for i in range(100):
    # A or B and C
```
上記を再度見て「面白みがないな！！」と思い、for文 と if文が邪魔なんだという思考に行き着いたので書いてみました。

その解答がこんな形！！
```Python
def put(val):
    next(val, True) or put(val)
put(map(lambda i: print((not i % 3)*'Fizz'+(not i % 5)*'Buzz' or i), range(1,101)))
```

# 最後に
これでFizzBuzz問題からfor文とif文が抜けた！！(^^)v
正直、関数を書いてしまっているので負けた気分...

## 追記
コメントの内容を元に自分が書いた改良版を作ってみました。
print(a,b,c)のように書いて改行区切りにすればループする必要がないは盲点でした！
ほんと色々な書き方があるんですな！
コメントと修正リクエスト頂いた方々ありがとうございます。大変勉強になりました！
```Python
print(*map(lambda i: (not i%3)*'Fizz' + (not i%5)*'Buzz' or i, range(1,101)),sep='\n')
```
varchar型　自動採番について

# 目的
"S0001"などの文字列を含んだIDを生成するfunction
# フロー
[str_pad](https://www.php.net/manual/ja/function.str-pad.php)を使って文字列にデータ件数+1の数値を結合する
# テーブル
Userテーブル
| id  |     user_num      | name |
| --- | ----------- | ------- |
| 1    | S001 |      山田 |
| 2    | S002 |      伊藤|
| 3    | S003 |     鈴木|


# 書いてみた

```php
function get_user_num($dbh) {
        $sql = "SELECT * FROM User";
        $stm = $dbh->query($sql);
        $rows=$stm->fetchAll(PDO::FETCH_ASSOC);
        $count = count($rows);
        if ($count == 0) {
            $user_num = "S0001";
        } else {
            $user_num = "S" . str_pad($count + 1, 4, 0, STR_PAD_LEFT);
        }
    return $user_num;
}
```
:::note warn
[rowCount](https://www.php.net/manual/ja/pdostatement.rowcount.php)dで件数を取得しようとしたがSELECT文では非推奨
:::
>PDOStatement::rowCount() は 相当する PDOStatement オブジェクトによって実行された 直近の DELETE, INSERT, UPDATE 文によって作用した行数を返します。
SELECT文のような、結果セットを生成するSQLステートメントについては動作が未定義です。 動作はドライバによって異なる可能性があります。 データベース(例: MySQL のバッファリングモード)によっては、文によって返された行数を返すかもしれません。しかしながら、 この振る舞いは全てのデータベースで保証されていません。 さまざまな場所で使用するアプリケーションでは、 これに頼ってはいけません。
JavaScript基礎学習⑤コンストラクター関数、プロトタイプ

学習の備忘録まとめ。


参考：Udemy　【JS】ガチで学びたい人のためのJavaScriptメカニズム


## コンストラクター関数とは？

新しくオブジェクトを作成するためのひな型となる関数の事。
new演算子を付けてインスタンス化（オブジェクトを生成する事）する。

```
function Person(name, age) {
    this.name = name;
    this.age = age;
}

const bob = new Person("Bob", 18);
```


## プロトタイプとは？
オブジェクトに存在する特別なプロパティーであり、コンストラクターと併せて使用する。
関数オブジェクトであるコンストラクターはprototypeというプロパティを保持しており、作成済のコンストラクターに後からメソッドを追加できる。
インスタンス化した際にはprototypeの参照が__proto__にコピーされる。
参照元をコピーして一致させる事で余分なメモリを消費せずにすむ。

```
コンストラクター名.prototype.メソッド名 = function() {
     処理内容
}
```
IaCをAWS CDKから別のものに変更してみたい。

## モチベーション

普段のAWS開発ではサーバーレス構成の管理のためのIaC(=Infrasctacture as Code)ツールとしてはAWS CDKを主に使っています。AWSによる説明は以下のリンク。

https://aws.amazon.com/jp/what-is/iac/

https://aws.amazon.com/jp/cdk/

AWS CDKはAWSの中でも好きなサービスなので、別のIaCツールを積極的に利用する気はないです。ただAWS CDKの何が良いのか聞かれた時に明確に答えられなかったので、他のIaCで同じ構成のデプロイを経験しておくことで、今後AWS CDKの何が良いかを説明できるようになることを目的にしています。

## 構成図

![構成図](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/825263/7bf61386-368f-8d48-4b4a-0febb2859c87.png)

Lambdaは共通で、ハンドラーが受け取るイベントをDynamoDBに保存する機能のみ。

## GitHub

（コミット整理中）

## やってみたいIaCツール一覧

- AWS CDK
- AWS CloudFormation
- AWS SAM
- AWS Amplify
- Terraform
- Serverless

## AWS CDK

https://docs.aws.amazon.com/ja_jp/cdk/v2/guide/home.html

## AWS CloudFormation

https://aws.amazon.com/jp/cloudformation/

### 事前の印象

AWS CDKがAWS CloudFormationの欠点を解消しているという認識なので、使いにくそう。

### 触った感想

AWS CDKの正しい理解のために細かい部分まで確認するべき。実際にはAWS CDKが使えるならAWS CDKを利用すればいいと思う。
YAMLなのでレビューが大変そう。
同じようなリソースにデフォルト値を設定したいケースでは、AWS CDKでTypeScriptを使って共通化できることはAWS CDKの利点。（Lambda Layerを設定する場合など。）
Lambdaの綺麗な管理方法が公式のドキュメントのみではわからなかった（特にRoleとCode。今後の課題）。
API Gatewayについては、今までAWS CDKのデフォルトでやっていたので記述量が多いと感じた。
デプロイ方法としてAWS CLIを直接利用したので、細かく指定する必要があった。AWS CDKだと`cdk deploy`だけで実行できる。

## AWS SAM

https://aws.amazon.com/jp/serverless/sam/

### 事前の印象

Lambdaをデプロイする方法として使えるらしい。

### 触った感想

LambdaのデプロイができるAWS CloudFormationという印象。
API Gatewayについては、AWS CDKと同じような共通化がされていて記述量が抑えられそう。
デプロイについても、`sam deploy`でデプロイできるのでAWS CDKと同じ感覚で実行できる。

## まとめ

他の候補を実際に触ることができた。プログラミング言語なので慣れている・共通化がわかりやすいという２点でAWS CDKが良いと感じました。利用するLambdaやAPI数が少なく、共通化をする必要がない場合は、AWS CloudFormationの方が必要最低限の設定を正確にできるという利点があるかもしれないです。

次の目標は、Terraformをやることと、AWS CloudFormationを通してAWS CDKを改めて正しく理解すること。

去年のアドベントカレンダーの時に候補として考えていた内容でしたが、結局今年のアドベントカレンダーでの着手となりました。
来年の目標として、やったことを定期的にまとめておきたいです。
【SwiftUI】Please adopt containerBackground APIでウィジェットが表示されない（iOS17）

# はじめに
iOS17でウィジェットを実行したら以下のような文がウィジェットに表示されました。

`Please adopt containerBackground API`

![IMG_4889.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/f6322600-de97-0d72-ae73-fd100394082b.png)

日本語にすると「containerBackground APIを採用してください。」らしいです。

全く表示されないのはまずいです。高速で対応します。

# 実装
```swift
extension View {
    // ウィジェットのbackgroundを設定する
    @ViewBuilder
    func widgetBackground(_ style: some ShapeStyle) -> some View {
        if #available(iOSApplicationExtension 17.0, *) {
            self.containerBackground(for: .widget) {
                ContainerRelativeShape().foregroundStyle(AnyShapeStyle(style))
            }
        } else {
            self.background(AnyShapeStyle(style))
        }
    }
}
```

# 使い方
```diff_swift
struct DemoWidgetEntryView : View {
    var entry: Provider.Entry

    var body: some View {
        VStack {
            Text("Time:")
            Text(entry.date, style: .time)

            Text("Emoji:")
            Text(entry.emoji)
        }
+       .widgetBackground(.red)
    }
}
```

:::note warn
**注意**
Xcode15で新たにプロジェクトを作成した場合は、初期の段階で`containerBackground`のための分岐が入っています。
新規プロジェクトで試される方はこのコードを削除して使用してください。
```swift
if #available(iOS 17.0, *) {
    DemoWidgetEntryView(entry: entry)
        .containerBackground(.fill.tertiary, for: .widget)
} else {
    DemoWidgetEntryView(entry: entry)
        .padding()
        .background()
}
```
:::

# おわり
iOS17でウィジェット関連めっちゃ変更入りましたね

公式ドキュメント
https://developer.apple.com/documentation/SwiftUI/View/containerBackground(_:for:)
リスク評価とALARP原則

# はじめに
　リスク評価は様々な分野で行われており、その評価結果を用いて何かをする技術や仕組みのことを、よく「Risk Based ～～」という名前で呼びます。ソフトウェアテストの分野だとRisk Based Test/Testingがあり、プラント分野だとRisk Based Inspection/Maintenanceと呼ばれるものがあります。分野によってその観点ややり方が微妙に異なっていて、規格類も別のものが用意されています。今回は、私がリスク評価に関わってきた中で、分野によってはあまり知られていないのかな、と感じたALARP原則について私見も含めて超簡単にお話したいと思います。
 　なお、この記事では、起きたら困ることが起きる可能性や不確実性のことをざっくりと「リスク」と言っています。

# 使える資源の有限性
 　リスク評価では、評価対象に潜むリスクを何らかの方法で明確化します。たいていの場合、関係者ならだれもが知っている、というリスクから、そう言われてみれば、とか、そんなのあるの？といったものまで、数多く列挙されることになります。またリスク量は、分野によって多少の違いはあるものの、基本的には、発生確率と影響の大きさを掛け合わせて計算します。つまり、リスクを小さくするには発生確率や影響の大きさを小さくできればいいわけです。
  　しかし残念なことに、リスクを減らすために使える資源（予算、時間、人員等）は有限であり、すべてのリスクを相手にリスク量を減らすことは困難なことが多いです。

# ALARP原則
　この問題に対する姿勢を概念的に示したものがALARP（アラープ）です。ALARP原則とかALARP原理と言うこともあります。ALARPは"as low as reasonably practicable"の略で、「リスクは合理的に実行可能な限り出来るだけ低くしなければならない」という意味です[1]。ここで重要なのは、「合理的に実行可能な」という部分です。この言葉で資源の有限性に触れています。
 　ALARPの考え方を図として示しているのがキャロット・ダイアグラムです。ニンジンのような逆三角形で示され、リスクを「受容不可能な領域」、「許容可能な領域」、「広く受入れ可能な領域」の3つに区分しています。中央の許容可能な領域をALARP領域と呼びます。2つの領域に分けなおしたものもありますが、今でも3分割バージョンが広く使われています。
  　「許容可能な領域」はちょっと言葉足らずな表現になっています。あくまでも、合理的にリスクを低減し切れない場合にその（残存）リスクの存在を許容してもよい、という意味です。「広く受入れ可能な領域」とは意味が異なります。「受容不可能な領域」にあるリスクは、全力でリスク量を下げていかなければなりません。

# 許容可能な領域とは
　では、ALARP原則で言う合理的に実行可能な領域や、キャロット・ダイアグラムで言う許容可能な領域の閾値はなんなのでしょうか。これについては、分野によっては参考となる基準値が示されていることもある、というのが実際です。
　比較的よく目にするのが、キャロット・ダイアグラムの縦軸を発生確率として、基準値を設定するものです。例えば労働災害分野では、英国安全衛生庁（HSE）が労働災害による死亡に関するALARP領域の範囲を、10の-3乗～10の-6乗/年としています[2]。
　そもそも、発生確率を求めること自体が困難な場合も多いと思います。理想を言えば実績で証明したいところですが、実際には統計学や信頼性工学などの技術を用いて予測・推定して見せることになります。

# おわりに
　リスクをどこまで減らせばいいのかは、リスク評価を行う際に常についてまわる問題です。ALARP原則は概念であり、具体化のための絶対的な基準を示すものはありませんが、リスク沼にはまりそうな人やすでにはまっている人（私含む）にとって、背中を支えてくれる存在であることは確かだと思います。

# 参考
[1]https://ja.wikipedia.org/wiki/ALARP
[2]https://www.meti.go.jp/shingikai/sankoshin/hoan_shohi/sangyo_hoan_kihon/pdf/004_01_00.pdf
Pybricks のマルチタスク

# はじめに

Pybricks にはサブプロセスやスレッドはありませんが、v3.3よりコルーチンを利用した協調的マルチタスクをサポートしています。

CPython や 通常の MicroPython では asyncio モジュールを使うところでしょうが、Pybricksでは、APIの後方互換性やリソース削減の都合から、独自のマルチタスク機能を提供しています。

# マルチタスク用のAPI

マルチタスクのための API は主に次の２つです。

```python
from pybricks.tools import multitask, run_task
```

* `multitask(coroutine1, coroutine2, ...)`<br>複数のコルーチンを実行するコルーチンを作成します。これによりマルチタスクが実現されます。
* `run_task(coroutine)`<br>コルーチンを実行します。これはメインコルーチンの実行に利用します。


# コルーチン化可能なAPI

Pybricks v3.3以降のドキュメントで `await` プレフィックスのついた API はコルーチン化可能です。

たとえば Motor の API にある次のようなものです。

```python
await run_time(speed, time, then=Stop.HOLD, wait=True)
```

このような API を await つきで呼び出すとコルーチンになり、await なしで呼び出すと従来どおりの呼び出しになります。

# 簡単な例

「きらきら星」を鳴らしながら、モーターをメトロノーム風に左右に反復させるデモです（あくまでメトロノーム風で厳密な動きはしていません）。スピーカ鳴らすのと、モーター動かすのを、それぞれ独立にコルーチンにし、マルチタスク実行しています。

![IMG_7219.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/106817/c0b1643d-cac7-526a-a22b-68dfe0ff7a59.jpeg)


```python
from pybricks.hubs import PrimeHub
from pybricks.pupdevices import Motor
from pybricks.parameters import Port
from pybricks.tools import multitask, run_task

hub = PrimeHub()

notes = [
    "C4/4", "C4/4", "G4/4", "G4/4", "A4/4", "A4/4", "G4/2",
    "F4/4", "F4/4", "E4/4", "E4/4", "D4/4", "D4/4", "C4/2"
]

tempo = 120
motor_speed = tempo

motor = Motor(Port.A)
motor.reset_angle()

# きらきら星を演奏するコルーチン
async def kirakira():
    await hub.speaker.play_notes(notes, tempo=tempo)

# モーターをメトロノーム風に左右に反復させるコルーチン
async def metronome():
    for _ in range(len(notes)//2):
        await motor.run_angle(motor_speed, 60)
        await motor.run_angle(motor_speed, -60)

# メイン
async def main():
    await motor.run_angle(motor_speed, -30)
    await multitask(kirakira(), metronome())
    await motor.run_angle(motor_speed, 30)

# 開始から終了までメインプログラムを走らせる。
run_task(main())
```
M5Stack/ｽﾀｯｸﾁｬﾝでキッズと遊びたい→センサで遊ぶ装置をいろいろと。

# 電子工作で遊びたい
別の企画でも書いたことなのですが、小学生くらいのお子さんが電子工作を始めたい、と考えたとき、大きな障害の一つとして立ちはだかるのが、親が「難しい」と考えてしまうことです。それなりの知識が必要なのは事実ではありますが、それ以前にセンサーや配線、基板が露出していると、それだけで拒絶してしまう親御さんが少なからずいらっしゃいます。すなわち、電子工作が電子工作たる部分が見えていることは、電子工作を始めるための障害になってしまう可能性があります。

# M5Stack/ｽﾀｯｸﾁｬﾝならいけるのでは・・？
M5StackののCore、Core2、Stickといったボディでは、画面とスイッチ類、ポート類が一体となっており、マイコンっぽい構造は隠れています。またメーカー純正のセンサーやユニット、モジュール類も、組み立ててしまえば基板やセンサー類がむき出しになることはありません。Groveケーブルを用いてポン付けができます。またユニット類は統一されたすっきりデザインで、レゴ互換の穴があることで何かに取り付ける工作も比較的容易です。この外見なら、親御さんからの拒否反応も起こりにくいでしょう。さらに、M5Stack本体を、「ｽﾀｯｸﾁｬﾝ」のボディに取り付けてしまえば、もうこれは電子工作的な外見ではなくなります。

というわけで、ｽﾀｯｸﾁｬﾝのボディにM5Stack社純正のセンサー類を取り付けて、Uiflowを使ったシンプルなプログラムで動かすおもちゃのような装置をつくってみました。いままでバラバラとTwitter/Xにばらまいてしまったので、YouTube動画としてあったものをまとめてみました。

ｽﾀｯｸﾁｬﾝボディを使わなければ、基本センサーとCore/Core Basic、100均、通販で購入できるモノを使って製作可能となるよう考えています。興味を持って頂いたものだけつまんで見て頂けると幸いです。

## カラーセンサーで遊んじゃう
* カラーセンサーで読み取った色を、画面のｽﾀｯｸﾁｬﾝ顔のほっぺの色として再現します。

https://youtu.be/t8-9GFt6jok

## 圧力センサーで遊んでみる
* Tube Pressureセンサーを使って圧力を読み取り、頭を持ち上げたりプルプルしたりする単純なし掛けです。
* ポンプはダ○ソーで売っている加圧式のスプレーを使用しています。

https://youtu.be/yh93KE2xVgA

## 環境センサーで遊んで、ちょっと実験も。
* 動作はTube Pressureセンサーと似ていますが、読み取りには環境センサーを使用しています。
* ○イソーで売っている加圧式スプレーにちょっとだけ加工して、Groveケーブルを通しています。ペットボトル内にはEnvセンサーを入れてあります。ケースから外して基板だけにすると、ペットボトルの口を通ります。この方法はAirpocketさん（X:＠AirpocketRobot）さんに教えて頂きました。
* ポンプで加圧すると容器内の圧力が上昇し、これに伴って温度が上昇する様子が数値で観察できます。
* ペットボトル内で雲を発生させる実験に使えます。
* イベントに持っていくと、子供たちから一番人気となります。

https://youtu.be/b4Ai5m6-w5M

## 距離センサーでを無駄に使って遊ぶ
* さいとてつやさん（X:＠saitotetsuya）よりフレーズを拝借してます
* 距離センサーの前フラップ状の構造部を取り付け、輪ゴム銃の的にしました。
* 輪ゴムが的に当たって距離センサーとフラップの距離が変化すると「あたり」と判定する単純な仕掛けです。

https://youtu.be/LfoG6FdUJNU

## ジェスチャーセンサーでも遊べる
* ジェスチャーセンサで読み取った動きをつかって、あっち向いてホイ的な動作をします。
* ただ動かすだけでは寂しかったので、回転を検出するとネコミミが光るようにしました。
* イベントに持っていくと、大きなお友達からの一番人気となります。

https://youtu.be/RRqXYVDpUdE

## 心拍センサで飛ばして遊んだ
* 心拍センサで読み取った心拍をｽﾀｯｸﾁｬﾝ顔のほっぺに表現するとともに、ネコミミを光らせます。
* 検出した心拍はをBluetoothをつかって別のM5Stackボディに飛ばします。この動画では、飛ばした先のCore Basicにサーボモータを取り付け、あの高名な先生の工作で有名なエリーちゃの胸がドキドキするようにしました。
* つまり、あなたの胸の高鳴りを誰かに伝えることができるエモーショナルな装置を個人で作ることができます。「己心電信」（仮）として、主にエリーちゃんの服を整える開発を続けてます。

https://youtu.be/O4g885-4t8o

https://protopedia.net/prototype/private/2c23afb9-ff1b-47a5-86c1-ca0e23141af1

以上です！
@KantenNamako
PowerShellでコマンドのパスを確認したいときはwhereに.exeをつけること

タイトルで終わってるから読む必要なし

```
PS C:\> where.exe python
C:\Users\xxxxx\AppData\Local\Programs\Python\Python312\python.exe
C:\Users\xxxxx\AppData\Local\Microsoft\WindowsApps\python.exe
```

----

linuxでは`which`で確認できる

```
$ which python3
/usr/bin/python3
```

windowsでは(紛らわしいことに)`where`を使う
が、存在するはずのpythonのパスが表示されない...

```
PS C:\> where python
PS C:\> python --version
Python 3.12.0
```

コマンドプロンプトではwhereで表示できる

```
c:\>where python
C:\Users\xxxxx\AppData\Local\Programs\Python\Python312\python.exe
C:\Users\xxxxx\AppData\Local\Microsoft\WindowsApps\python.exe
```

PowerShellでは`where.exe`にしないといけないらしい

```
PS C:\> where.exe python
C:\Users\xxxxx\AppData\Local\Programs\Python\Python312\python.exe
C:\Users\xxxxx\AppData\Local\Microsoft\WindowsApps\python.exe
```

なんでかというと、PowerShellの`where`は`Where-Object`のエイリアスだからなんだと
もうﾏﾁﾞ無理。。。

`code $profile`してエイリアス足しとこ

```
Set-Alias which where.exe
```

# 追記

Get-Commandでもいい

```
PS C:\> get-command python

CommandType     Name                                               Version    Source
-----------     ----                                               -------    ------
Application     python.exe                                         3.12.150.… C:\Users\inarb\AppData\Local\Programs\Py…
```

```
PS C:\> (get-command python).Source
C:\Users\inarb\AppData\Local\Programs\Python\Python312\python.exe
```

where.exeのほうは複数渡せる

```
PS C:\> where.exe flutter dart
C:\flutter\bin\flutter
C:\flutter\bin\flutter.bat
C:\flutter\bin\dart
C:\flutter\bin\dart.bat
```

# 参考

[Windows install | Flutter](https://docs.flutter.dev/get-started/install/windows#update-your-path)

> However, if you are using PowerShell, in it where is an alias of Where-Object command, so you need to use where.exe instead.
SSH でポートフォワーディング

# ローカルホストにフォワード

作業PCから、HOST1 に ssh できる状態で、HOST2のポート80 を転送する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/deed6404-303b-3e9f-54e2-992a90ba1d0d.png)



作業PCで、
```
$ ssh -L 10080:192.168.0.102:80 user@XXX.XXX.XXX.XXX
```

とした後、

作業PC で `http://localhost:10080` を開く

なお、作業PCと同じLAN内にある別のPCに、作業PCの10080を公開するには 上記コマンドに -g オプションを追加する必要がある。

# リモートホストにフォワード

作業PCからローカルLAN内のポート80アクセスをリモートホストのポート10080 に転送する

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/5c5b020b-c5cf-8c73-a25c-596edd22a18f.png)
作業PCで、
```
$ ssh -g -R 10080:192.168.0.102:80 user@YYY.YYY.YYY.YYY
```
とした後、Host3 から  localhost:10080 にアクセスすると、HOST2のポート80が見えるようになる。

とした後、YYY.YYY.YYY.YYY のポート10080 にアクセスすると、HOST2のポート80が見えるようになる。
なお、図のようにインターネットサーバとして公開するには、Host3の /etc/ssh/sshd_config に

```
GatewayPorts=yes
```

の設定を入れて sshd を再起動する必要がある。

# ポートフォワードできないもの

TCP で指定ポート以外も使うネットワーク通信はこれだとうまくいきません。

- rtsp など UDPを使うもの
- FTP のアクティブモードなど、フォワードを行ったポート以外のポートを使うもの

# 覚え方
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/950c7dfb-8884-f20d-3fb2-d84540c1933a.png)
文章中に<br>タグ入れるのやめてください

## はじめに
**文章中に改行で`<br>`入れるのやめてください**

これは各種CMSにお願いしたいことです。テキスト投稿サイトでも同じです。

スクリーンリーダーで読み上げた時にめちゃくちゃ読みづらくなるからです。

## なぜか

スクリーンリーダーは基本はキーボードで操作します。タグ内に文字が書いてあれば、それを読み上げてくれます。しかし、`<br>`タグがあると、そこで一旦文章が途切れます。MacのVoiceOverの場合、「空のグループ」と読まれます。もしそのまま`<p>`タグ続きであれば、段落続きで連続で読み続けることができます。しかし、`<br>`が改行目的で使われていると、その度に「空のグループ」と読まれます。これがブログサイトや小説投稿サイトなどの文章メインのサイトだと、スクリーンリーダーを使うユーザーにとってはUXが最悪です。


>プログラミングは、コンピュータに特定のタスクを実行させるための手段です。
コンピュータは人間の言語を理解することができないので、プログラミング言語を使って人間がコンピュータに指示を与えます。
プログラミングによって、ソフトウェアやアプリケーションを作成したり、コンピュータに作業を自動化させたり、データを処理したりすることができます。
プログラミング言語にはさまざまな種類があり、それぞれ異なる目的や特性を持っています。

上のような文章を、もし改行の度に`<br>`タグを使っていたらどうでしょうか？


>プログラミングは、コンピュータに特定のタスクを実行させるための手段です。
空のグループ
コンピュータは人間の言語を理解することができないので、プログラミング言語を使って人間がコンピュータに指示を与えます。
空のグループ
プログラミングによって、ソフトウェアやアプリケーションを作成したり、コンピュータに作業を自動化させたり、データを処理したりすることができます。
空のグループ
プログラミング言語にはさまざまな種類があり、それぞれ異なる目的や特性を持っています。

このように、改行の度に「空のグループ」と読まれます。こんな感じで文章書かれていたらすごく読みづらくないですか？スクリーンリーダーを使っているユーザーは、そんな気持ちになります。

ぜひ、適切に`<p>`タグを使ってちゃんと段落を作りましょう。もしくはここ最近、新しく出てきた、[auto-phrase](https://developer.chrome.com/blog/css-i18n-features?hl=ja)を使いましょう。まだ使えるブラウザは限られますが。
【CSS】Web Platform Baselineってなにもの？

## はじめに
みなさんは、**Web Platform Baseline** ってご存知ですか？

最近 [mdn](https://developer.mozilla.org/ja/docs/Web/CSS/grid) を見ていると、以下のようなものを見るようになったかと思います。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390720/996930d8-5012-aa76-8c1e-e21ec3cac111.png)
![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390720/7720b05c-4d39-a8c0-25f5-3abd848f5637.png)

この部分が、**Web Platform Baseline**です。

私がこの **Web Platform Baseline** を見た時は、ただCSSプロパティのブラウザ互換性について、教えてくれる項目が増えただけと思っていた。

しかし、思い込みで情報を流し見するのも憚られるので、もう少し詳しく調べてみました。

今回はそんな **Web Platform Baseline** について紹介します！

## Web Platform Baseline

https://web.dev/baseline

### ⚪︎ Web Platform Baseline とは？

Web Platform Baselineは、ブラウザがサポートするウェブプラットフォーム機能に関する情報を明確にするツールです。

Web Platform Baselineを使用すると、安全に使用できるウェブプラットフォーム機能についての正確な情報がわかります。

記事を読んでいる時やライブラリを選ぶ時などに、使用されている機能のすべてがBaselineに従っていれば、ブラウザの互換性を心配しなくてよくなります。
また、Baselineに沿っていれば、Webサイトをテストする時に予期しないBugは発生しません。

このBaselineは、[MDN](https://developer.mozilla.org/ja/)、[Can I Use](https://caniuse.com/)、[web.dev](https://web.dev/)で導入されており、記事やライブラリで説明されている機能がすべてBaselineに含まれていることを示すためのツールを提供しています。

![スクリーンショット 2023-12-16 19.26.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390720/9edd1b1a-fad9-b9e0-cedd-5af5e0ce895e.png)

このBaselineを使うことで以下のようなことが簡単にできるようになります。

+ 対応ブラウザ・バージョンを列挙する代わりに、Baselineに含まれている機能を使用することが要件として設定できるようになる
+ ライブラリを公開する時に、使用されている機能がどのブラウザ・バージョンでサポートされているかを理解するために、Baselineへのサポートを宣言することで、潜在的なユーザーを助けることができる
+ チュートリアルを書くときに、説明されているすべてがBaselineに含まれていることを読者に伝えることができる

### ⚪︎ Baselineが更新されるプロセス

1. **新しく利用できるようになる**
    + 機能がコアブラウザの最後のものがサポートされ、相互利用可能になる
1. **広く利用できるようになる**
    + 新しく利用できるようになった日から30ヶ月（2年半）が経過すると、ほとんどのサイトで機能を使用できる

*<font color="gray">※ コアブラウザ：Safari(macOS/iOS)・Firefox(Desktop/Android)・Chrome(Desktop/Android)・Edge(Desktop)</font>*

## Ref
https://web.dev/blog/introducing-baseline

https://web.dev/articles/baseline-features

https://developer.mozilla.org/en-US/blog/baseline-unified-view-stable-web-features/

https://web.dev/blog/baseline2023


## まとめ

この記事では、**Web Platform Baseline** について紹介しました。
Web Platform Baselineについて調べてみると、ますます開発者体験が良くなって行きそうな感じがしました。

---

最後まで読んでくださってありがとうございます！

普段はデザインやフロントエンドを中心にQiitaに記事を投稿しているので、ぜひQiitaのフォローとX(Twitter)のフォローをお願いします。

https://twitter.com/degudegu2510
JointJSで描画した図形に手のひらツールを実装する

図形をたくさん置くと描画に必要な領域が広くなり、スクロールが必要になります。
横スクロールは操作し辛いので、手のひらツールがあるとちょっと便利になります。

## 実際の動作

![tmp.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/129526/a56c0524-1534-3304-1225-2c98d475728b.gif)

全体のソースはこちらにあります

https://codepen.io/acnaman/pen/dyaBOmv


## 実装内容のポイント説明

### ドラッグ＆ドロップの挙動を作る

ドラッグ＆ドロップをしたときの動作を定義するために、Paperの`blank:pointerdown`イベントと`blank:pointermove`イベントを利用します。

`pointerdown`時の座標を取っておき、`pointermove`後の座標との差分をスクロールさせる、という処理内容になります。
また、今回は`blank:`のプレフィックスをつけることで、描画済みのElement上ではスクロールの処理が流れないようにしています。描画した要素の移動をさせたい場合はこのような方法で不要なスクロールを回避させます。

```js
// カーソルクリック時の座標を記録する
paper.on('blank:pointerdown', (evt) => {
  evt.data = { clientX: evt.clientX, clientY: evt.clientY };
});

// カーソル位置の移動時にスクロールする
paper.on('blank:pointermove', (evt) => {
  window.scrollBy( (evt.data.clientX - evt.clientX), (evt.data.clientY - evt.clientY));

  // 差分を取る基準をスクロール後の位置で更新
  evt.data.clientX = evt.clientX;
  evt.data.clientY = evt.clientY;
});
```

### Link上でもドラッグアンドドロップできるようにする

Elementをドラッグ＆ドロップしたときはElementの移動を行いますが、Linkについては特に移動せず、他の背景と同様にドラッグ＆ドロップでスクロールさせたいとします。そういった場合は、Linkのマウスイベントをスルーさせる`pointer-events="none"`を設定すると回避させることができます。

今回のサンプルではLinkが1つしかないため、 `link.attr('root/pointerEvents', 'none');`と書くことでも実現できますが、Linkがたくさんある場合に全てのLinkに対して`attr`メソッドを実行していくのは効率が悪いです。
こういう場合、`pointer-events="none"`を設定済みのカスタム要素を予め作っておき、それをLinkとして使っていくのが効率的です。

```js
// クリックイベントをスルーするカスタムLink
joint.shapes.standard.Link.define(
  'example.SimpleLink', 
  {
    attrs: {
      root: {
        pointerEvents: 'none'
      }
    }
  }
);

const link = new joint.shapes.example.SimpleLink();
link.source(rectangle1);
link.target(rectangle2);
```

### CSSでカーソルを手のひらにする

手のひらツール感を出すためにはカーソルの表示が重要です。
JointJSではなくCSSの設定になりますが、以下の設定を行ってカーソルの表示を制御します。

```css
/*　通常は手のひらを開いた状態　*/
#myholder{
  cursor: grab;
}

/* ドラッグ中は手のひらを掴んだ状態 */
#myholder:active {
  cursor: grabbing;
}
```

## まとめ

今回は、今までに紹介した機能を応用して手のひらツールを実装する方法を紹介しました。
図形描画系のツールではおなじみの機能となっていますので、JointJSで作成する図形に必要になってくることもあると思います（少なくとも私の現場では必要になりました）。

※この記事は [JointJS Advent Calendar 2023](https://qiita.com/advent-calendar/2023/jointjs) の記事です。他の記事を読む場合はカレンダーのページを参照してください。

https://qiita.com/advent-calendar/2023/jointjs
第一世代　手足の接続端子の信号の調査

「aibo ers-11x/210で遊ぶ Advent Calendar 2023」の最終日です。

aiboの魔改造を考えると本体回路をそのまま使うのは難しいことが多いです；記録メディアが8か16 **MBのメモリースティック**であることが特にネックです。

将来的に本体回路をすげ替えることを想定し、手足などの端子の信号について調査しましたので、メモを残します。

:::note alert
警告
この記事について作者はいかなる責任も負いません。
:::

:::note
業務外の個人的な検討活動です。
:::

#### 端子
手足などへ接続されている端子を調べました。10個の端子がありました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103446/07918092-d6f0-d35a-dd4b-a57d23bfde6c.png)

テスターで導通状態や電圧を調べたり簡易的なオシロで波形を調べたところ、下記のような構成だと考えられました。
| ピン番号 | 内容 |
| ---- | ---- |
| 1 | CLK |
| 2 | ? |
| 3 | 3.3V |
| 4, 5 | 5 V |
| 6, 7, 8, 10 | GND |
| 9 | ? |

信号に2番と9番の2本が使われているようだったので最初SPIかと思いましたが、オシロスコープで波形を見るとお互いが反転していましたので、USBの可能性が出てきました。

#### PulseViewで調査
こちらの簡易ロジカルアナライザを入手してPulseViewを使いました。
https://www.amazon.co.jp/gp/product/B078P8GQSQ

PulseViewは最初キャプチャーができなかったのですが、最新版ではなく0.4.2をつかうとキャプチャーできたとの情報を見つけることができました。
https://www.eevblog.com/forum/beginners/cheap-logic-analyzer-to-use-with-pulseview/msg5080951/#msg5080951

コマンドの内容は調査中ですが、USBpacketで信号がやり取りされている可能性が高そうです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103446/82aa33e1-1d15-8a34-bdc3-4cf8738860e6.png)

#### 最後に
発売から四半世紀近く経ってからも動かせて遊べますし、かわいいアイコンとしてもすぐれた機体だと思います。
実際に動かして楽しめてよかったと思いました。

このAdvent Calenderにお付き合いいただきありがとうございました。
今年もよいお年を。
クラウドメモアプリ再考 (Gitでmdファイル管理)

## はじめに
今年、私にとってメモアプリ界隈で非常に残念な出来事が起こった。
* <b>wri.peの閉鎖</b>
markdown対応、シンプル、軽い、スマホ含む複数端末から利用可、無料
という、クラウドメモアプリ最強のサービスだった、、、
とりあえずエンジニア技術メモ的なものを沢山ぶち込んでいた。
閉鎖後、GitHubとFacebookアカウント経由のバックアップサービスが提供されたが、Facebookアカウントでは不具合があるらしく、その認証を使っていた私はメモを全損失する事になった、、、

* <b>Evernoteフリー利用ほぼ不可</b>
Evernoteはwri.pe利用以前のメモや、業務以外のメモとして利用してきた。
これまでもアプリの複数利用制限、ログイン不具合など数多くの不満点があったが、今月に入り無料版は以下の様な制限がかかる事になった。
ノート数：100,000個 → 50個
ノートブック数：25個 → 1個
もう無料では使わせんぞ、という無言の圧力を感じる。
<br>
Evernoteに関してはお金を払いたくない、という事ではなく、wri.pe同様サービス自体無くなってしまうのではないかという懸念があり、これ以上使いたくない、という思いが強い。<br>
そこでクラウドメモアプリを再考する事となった。

## 選定する
まずメモアプリで何を実現したいか考えたい
### クラウドメモアプリに求める条件
* サービス終了しない
* markdownが使える
* 複数端末利用可
* シンプル
* カテゴリ分けしたい
* シークレット情報も扱いたい
* 絵も入れたい
* オフラインでも使える
* できれば無料

、、、おそらくこれらを満たすものは存在しない。
Notion、Dropbox paper、Google Keep、、、、
このサービス滅びない、というものは存在しないと思う。だから条件の`サービス終了しない`というのは考え直した方がいいのかも知れない。
* バックアップが確保されている

これを条件とする。

いきなりだが、<b>[Gitによるmdファイル管理]</b>を選定した。
次に運用について挙げていく。

## Gitによるmdファイル管理

### ・バックアップが確保されている
Gitによるローカルでの管理、そしてGitHubによるクラウド管理も行う。
ローカルでメモ作成したらコミットしてプッシュ。
これによりローカルが死んでもGitHubから復旧、GitHubが死んだら他のGitリモートツールに変更すれば良い。

### ・markdownが使える
拡張子を`md`とすればGitHub上ではマークダウンファイルとしてプレビュー表示される
またクライアントではVSCodeなどを用い、markdownとして記載すれば別タブで同時プレビューが見れて便利。

### ・複数端末利用可
GitHub上でマークダウンファイルとしてプレビュー表示できるので、PCでのWebブラウザは勿論、スマホでもGitHubアプリやブラウザなどで見る事が可能。

### ・シンプル
余計な機能はない。mdファイルを作成するのみ。

### ・カテゴリ分けしたい
これは単純にディレクトリを作ってその中にファイルをどんどん突っ込んでいけば良い。

### ・シークレット情報も扱いたい
`private`等のディレクトリを作成し、[git-crypt](https://qiita.com/batch9703/items/f6959ba51bb9bb32ef93)を用いてその中に存在するファイルは暗号化して管理する。
GitHubにpushするならprivateリポジトリにする。
注意点としては暗号化してバイナリファイルとなるのでGitHubのWebやアプリではブレビューを見る事ができない。

### ・絵も入れたい
`images`等のディレクトリを作成して画像ファイルを入れておき、mdファイルからは以下の様に記載すると参照できる。
```md
![画像1](images/image1.png)
```
### ・オフラインでも使える
クライアントでエディタを使って自由に編集できる。
markdownプレビュー機能があるエディタがお勧め。

### ・できれば無料
Git運用自体はクライアントで管理できるので無料。
GitHubフリープランでもprivateリポジトリを制限なく作れるので無料。
(有料化や不便になっても移行できるGitリモートサービスは他にも沢山ある)

## 活用例
私の活用例を挙げておく
### ・ディレクトリ構成
暗号化したいものとそうでないもの、業務用と私用に分けて活用している
```shell
/
┣━ private/ # 暗号化ファイル格納ディレクトリ
┃  ┣━ images/ # 画像ファイルディレクトリ
┃  ┃  ┣━ 〇〇.png
┃  ┃  ...
┃  ┣━ business/ # 業務用ファイルディレクトリ
┃  ┃  ┣━ 〇〇.md
┃  ┃  ...
┃  ┗━ personal/ # 私用ファイルディレクトリ
┃     ┣━ 〇〇.md
┃     ...
┣━ public/ # 通常ディレクトリ
┃  ┣━ images/ # 画像ファイルディレクトリ
┃  ┃  ┣━ 〇〇.png
┃  ┃  ...
┃  ┣━ business/ # 業務用ファイルディレクトリ
┃  ┃  ┣━ 〇〇.md
┃  ┃  ...
┃  ┗━ personal/ # 私用ファイルディレクトリ
┃     ┣━ 〇〇.md
┃     ...
┣━ .gitattributes # git-crypt設定ファイル
┣━ .gitignore
┗━ README.md
```

### ・git-crypt設定ファイル
```.gitattributes
private/* filter=git-crypt diff=git-crypt
```
### ・VSCode活用
VSCodeで左エディタ、右プレビューと配置すると使い易い。
![スクリーンショット 2023-12-16 21.10.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152189/56b92621-6e03-8cd1-0c9e-26973e51d935.png)

## 終わりに
これでもう未来永劫(？)迷う事はない。
GitHub Copilot契約したらChatでリポジトリ内の自然言語検索ができるんじゃないかと思っている、、、(試していない)