OmniAICreator/Qiita-1.07M huggingface dataset:
（日記）DRPGのようなものを作った→で？

## 話

* 前回の日記：[（日記）DRPGを作ろうとしてみる](https://qiita.com/NyancoRitter/items/3698ed078f5048de1933)

「プログラミング初心者がゲームのようなものを作るのに挑戦」的な話をやっていて，それがなんとなく完成した感．

はい．
……で？　っていう．

### Before I sleep

昨日寝る前にぼんやりと考えたんですよ．
こういうやつって一体どこに行くのだろう？　と．

なんだろう，例えば「プログラミング始めました！　コンソールでじゃんけんゲーム作ってみた！」みたいな話であれば，こういう場所にコード全文貼ったりして→それ見た上級者が「こやつめハハハ」とか言いながらちょっとした指摘とかして……みたいな和やかな話になるのだろう．

ガチな「ゲーム作りました」という話だと，まぁ，どこぞの立派な公開場所に公開したりするのだろう．

でも今回みたいなのって，どっちでもない．
その間のどこか：「じゃんけんゲームよりは何歩か進んでいると思うが，ゲームとして公開されるに足るレベルの物になるには何十歩も足りない」みたいな存在だ．
世の中，いきなり公開レベルのゲームを作れる人ばかりではないハズだと思うから，このような存在が日々生み出されていてもおかしくないと思うのだが，そしたらそれらは一体どこに行ってしまうのだろうか？
作った人の所有する補助記憶装置の中で速やかに眠りにつくのだろうか．なんだかなぁ．

……

で，閃いたわけです．
「立派な場所」に公開できないのならば，「立派じゃない場所」に公開すればいいんじゃね？　と．

そう，「ぼくのホームページ」を作ればよいのである！

### ぼくのホームページ？

……ってどうすれば？

自慢ではないが，当方，そっち系は本当にさっぱりであるので，「ホームページ　無料」とかでググった．マジで．
その結果，以下のことをすればよさそうだということがわかった．

1. 場所を借りる
2. そこに HTML のファイルをアップロードする

#### (1)場所を借りる

ググるとなんかいろいろ出てくるが，正直どこがいいのかさっぱり謎である．

そもそも選択基準を持ち合わせていないのに悩んでいても無駄なので，適当に「忍者ホームページ」というところにした．
理由は　「なんか FTP のソフトの使い方が丁寧に説明されていたから」　である．
（知識０の人の理由って，まぁ，そんな感じだよね．）

#### (2)HTMLを用意する

`index.html` なるファイル名のものを，言われるがままの手順で FTP やらでアップロードすればよいとのことだ．

そしたら当然「じゃあそのHTMLファイルってのはどうやって用意するんだよ？」という問題が出てくるわけだが，そこは私の綿密なる調査の結果，
【とりあえず markdown で書いたのを pandoc というソフトに食わせると html が吐き出される模様】
ということが事前に分かっていたので，これでどうにかすることとした．

で，まぁ，それで html が出てくるんだけども
「見出しっぽいところに下線を引きたいです……」とかいうわがままな考えに捉われてしまい，ググりまくって「`h2` のときには下に線を引きましょうね」みたいな超最低限の css なるものをとりあえず書いた．

あとはゲームのようなものを zip に固めたものを一緒にアップロードし，そいつへのリンクを設ければ良いらしい．
もちろん「html リンク ダウンロード」とかググった．
`<a>` とかいうやつの中に `download` と書いとけばいいらしい．

#### (3)できた

そんなわけで， [現代のものとはとても思えない「ぼくのホームページ」](https://nyancoritter.uunyan.com/) が爆誕した．すげぇ．

---

## そんなわけで

超ヒマな人は「ゲームのようなもの」をプレイしてみてくだされ．

---

## その後

無謀にもフリーゲーム投稿サイトみたいなところに突っ込んでみたら，速攻でコメントにてエラー指摘された．
曰く「ファイル読込エラーが発生して全く動かん」とのことであった．
「うーん，自分の環境ではそんなこと一回も起きてなかったのだが…？」と戸惑ったが，いろいろ試してみると「日本語なパス」になるとダメになるやつがあるという状況に見えた（全てじゃないってのがまた嫌らしい）．

頑張って原因を探ってみると……

`GetModuleFileNameA()` ←これを使ってたんだけど，コレ，文字コードは何なの？っていう……
「文字列の文字コードはUTF-8ってことで」っていう前提で `std::string` が用いられている個所にこのAPI由来の文字列がうっかりそのまま渡っていたのだ．
しかし「日本語を含まない（ASCIIな?）パス」だとギリギリ行けてしまっていた，という．
仕方ないので `GetModuleFileNameW()` 側の結果を `WideCharToMultiByte()` なるやつで UTF-8 に変換するようにして対処した．

あとは，ファイルIOする箇所で `fstream` のctor引数にこれまた「UTF-8だと思っている `std::string`」がそのまま突っ込まれていたり，あるいは `std::filesystem::path()` に突っ込まれたりしていて問題を起こしていた．
ここは `std::filesystem::u8path()` を使うようにして対処．

……というわけで，まぁバグってはいたんだけども……　ちょっと前まではパスに「デスクトップ」を含む場所とかで余裕で動かせてたハズなんだよなぁ．←逆に何故動けていたのか？　不思議．
データクラウドクレジット


https://trailhead.salesforce.com/content/learn/modules/data-cloud-credit-consumption-quick-look/get-started-with-data-cloud-credit-consumption

+ [【ヘルプ】Data Cloud の請求可能な利用状況の種別](https://help.salesforce.com/s/articleView?id=data.c360_a_data_usage_types.htm&type=5)

# データ クラウド クレジットとは何ですか?

Data Cloud クレジットは、Data Cloud サービスの支払いに使用するデジタル通貨です。Data Cloud の料金は使用量に基づいており、使用するサービスと同じクレジット数だけを消費します。使用量を制御することで、コストに直接影響を与えることができます。これにより、Data Cloud の料金はさまざまなユースケースに合わせて柔軟に調整できます。Data Cloud を使い始めたばかりでプロジェクトが単純な小規模な企業は、大規模なデータセットと複雑なプロジェクトを抱える企業に比べて、消費するクレジットがはるかに少なくなります。使用量ベースの料金設定では、誤って予想よりも多くのクレジットを消費しないように、使用量を計画することが重要です。

Connect: ストリーミング データとバッチ データを Data Cloud に接続します。または、データ フェデレーションを使用して、Snowflake、Databricks、BigQuery からデータを接続します。

+ バッチデータパイプライン
+ ストリーミングデータパイプライン
+ データ共有行の共有 (データ出力)


Harmonize:Data Cloud でデータを変換、マッピング、統合します。

+ バッチプロファイルの統合
+ 処理されたセグメント行
+ バッチ計算された洞察
+ ストリーミング計算された洞察

Activate:洞察を作成し、AI 機能を構築し、レポートとダッシュボードをクエリし、データをアプリと統合し、自動化を作成することで、データに基づいて行動します。

+ 高速データクエリ
+ データクエリ
+ データフェデレーションまたはアクセスされた行の共有
+ 推論
+ リアルタイム プロファイル API
+ ストリーミングアクション（ルックアップを含む）
+ バッチアクティベーション

# クレジットを有効活用しましょう
Data Cloud の使用を最適化し、購入したものを正確にニーズに合わせる方法を見つけることが重要です。ここでは、余分な使用量を削減するための戦略を紹介します。

+ ストリーミングよりもバッチを選択してください。ストリーミングはデータをリアルタイムで処理しますが、コストがかかります。ストリーミング CI のコストはバッチ CI の 55 倍です。ユースケースが時間に敏感でない場合は、バッチ データを使用してクレジットを節約します。
+ まだ実行中の古いセグメントなど、使用されていないプロジェクトを非アクティブ化します。
+ ユースケースに関連するデータのみを取り込みます。たとえば、取り込むプロファイルを過去 1 年以内にアクティブだったものに限定します。
+ データの処理頻度を減らします。たとえば、CI のユースケースが時間に敏感でない場合は、1 時間ごとではなく毎日更新するように設定します。
『生成AI×財務経理：PoCで挑むSlack AI Bot開発と現場巻き込みのリアル』でJAWS DAYS2025に登壇しました！

# はじめに

今回は池袋で開催されましたJAWS DAYS 2025に登壇した際のセッション資料を公開いたします！（参加された皆様、お疲れ様でした:relaxed:）

https://jawsdays2025.jaws-ug.jp/sessions/A-9

# セッション概要

>タイトル：生成AI×財務経理：PoCで挑むSlack AI Bot開発と現場巻き込みのリアル
>
> 生成AIの盛り上がりが止まらない昨今、昨年のAWS re:Inventでも生成AIに関連する話題が大きく注目されていました
しかし、実業務で生成AIを活用するにはまだ抵抗やハードルを感じる方も多いのではないでしょうか？
私たちは2023年から生成AIのPoCに取り組み、可能性を模索してきましたが、実務での活用には課題がありました。そんな中、財務経理部門との業務効率化プロジェクトの相談が舞い込んできました
本セッションでは、財務経理部門と進めたPoCを例に、生成AI Slackアプリ開発のプロセスや現場スタッフとのコミュニケーション、工夫、課題を赤裸々にお伝えします！ ぜひお気軽にご参加ください！

# 資料

<script async class="speakerdeck-embed" data-id="5f5fc23737ec4a54bef67136de173e3b" data-ratio="1.77777777777778" src="//speakerdeck.com/assets/embed.js"></script>


詳細解説は後日別記事を公開予定です！

# さいごに

弊社としては初めてのJAWS DAYS登壇で非常にワクワクして当日を迎えました！
セッションでは多くの方に足を運んでいただけて登壇者一同非常に嬉しく、
登壇を終えた今達成感でいっぱいです:sparkles:

今年最初の登壇でしたので、これを皮切りにたアウトプットしていきたいと思います!

# 【おまけ】ランチサポーターとしてノベルティとしてクッキー🍪を配布していました

![IMG_4918.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3550351/d6664424-7bac-4fb1-9285-3820e5eb96c1.jpeg)


受け取ってくださった方、ありがとうございました！

# 弊社では一緒に働く仲間を募集中です！
現在、様々な職種を募集しております。
カジュアル面談も可能ですので、ご連絡お待ちしております！

募集内容等詳細は、是非採用サイトをご確認ください。

https://engineer.po-holdings.co.jp/
ReactにおけるPropsの原則まとめ

最近React/Next.jsの学習をしております。

Reactでコンポーネント間でデータを受け渡す際には
propsを利用します。

しかし、はじめてReactに触れる方は
「propsはどのように使えばいいの？」
「なぜ変更してはいけないの？」などの疑問を持つことが多いです。
私がそうでした。

そこで本記事では、私のような初学者向けに
Reactにおけるpropsの基本的な使い方と原則について簡潔にまとめます。

# 1. Propsとは？

親コンポーネントから子コンポーネントに渡すデータ(仕組み)
コンポーネントは、渡されたpropsを受け取って描画します。

例：親→子へ文字列を渡す場合
```
// 親コンポーネント
function App() {
  return <Hello name="React" />;
}

// 子コンポーネント
function Hello(props) {
  return <h1>Hello, {props.name}!</h1>;
}

export default App;
```

このように、App コンポーネントが Hello コンポーネントへ "React" という値を渡されて"Hello React"という結果となります。

イメージこんな感じ
```
┌─────────────────┐
│ 親コンポーネント │ App
│  (propsを渡す)   │
└─────────────────┘
          |
          |  name="React"
          ▼
┌─────────────────┐
│ 子コンポーネント │ Hello
│  (propsを受け取る)│
└─────────────────┘
          |
          |  return <h1>Hello, {props.name}!</h1>
          ▼
  ブラウザ上に「Hello, React!」と表示
```

# 2. Propsは「読み取り専用」

Reactでは、propsは読み取り専用 (read-only) という原則があります。
これは 「子コンポーネントは受け取ったpropsを変更してはいけない」 というルールです。

```
function Hello(props) {
  // 受け取ったpropsを直接変更
  props.name = "Vue"; // ❌ こういった操作は行わない

  return <h1>Hello, {props.name}!</h1>;
}
```
このようにpropsを直接変更すると、Reactの設計方針に反するため
予期しない挙動を引き起こす可能性があります。
また、厳密モード（Strict Mode）やTypeScriptの監視を入れている場合は警告・エラーに繋がります。

# 3. なぜ読み取り専用なのか？

Reactは **「一方向データフロー(One-way data flow)」** を基本としておりデータは上位（親）から下位（子）へと流れます。
もし子コンポーネントがpropsを自由に変更できると、データの流れが複雑になってしまい、状態管理が難しくなります。

シンプルに管理するためのルールとして、Reactは

親 → 子 へデータを渡す
子はpropsを参照のみ行う
更新が必要な場合は、親が状態を持ち、setStateなどで更新して再描画する
という設計を採用しています。

# 4. 状態を変更したいなら「State」を使う

propsはあくまでも受け取るだけなので、**「値を更新したい場合はどうすれば？」** という疑問が出てきます。

Reactでは、状態を変更したい場合は**useState**などを利用し
**「State（状態）」** として管理します。
Stateとはコンポーネント内部で保持する変更可能なデータのことです。

例：count という状態を管理するコンポーネント
```
import React, { useState } from "react";

function Counter({ init }) {
  // 親から渡された init を元に、コンポーネント内の state を初期化
  const [count, setCount] = useState(init);

  const increment = () => setCount(count + 1);

  return (
    <>
      <p>Count: {count}</p>
      <button onClick={increment}>Increment</button>
    </>
  );
}

export default Counter;
```

このとき、init は単に「初期値」として読み取り
実際に値を更新したい場合はcountというstateを操作します。
init自体は読み取り専用のpropsのため変更しません。

# 5. まとめ

- propsは読み取り専用
- 一方向データフローを守るため、変更したい場合は親コンポーネントがstateを管理し、子コンポーネントに再度渡す
- 値を更新したい場合は「state」を使用する

Reactの基本的なデータフローを理解しておくと
コンポーネント同士のデータの受け渡しがシンプルかつ明確になります。
初心者の方は特に **「propsは読み取り専用」** というルールを守るところから始めると、Reactの開発がスムーズになります。

個人的に見返すために記載しているため
何か違っている点や追加事項などございましたら
コメント欄でご教授いただけますと幸いです。


# 参考資料
[これからはじめるReact実践入門 コンポーネントの基本からNext.jsによるアプリ開発まで](https://www.amazon.co.jp/%E3%81%93%E3%82%8C%E3%81%8B%E3%82%89%E3%81%AF%E3%81%98%E3%82%81%E3%82%8BReact%E5%AE%9F%E8%B7%B5%E5%85%A5%E9%96%80-%E3%82%B3%E3%83%B3%E3%83%9D%E3%83%BC%E3%83%8D%E3%83%B3%E3%83%88%E3%81%AE%E5%9F%BA%E6%9C%AC%E3%81%8B%E3%82%89Next-js%E3%81%AB%E3%82%88%E3%82%8B%E3%82%A2%E3%83%97%E3%83%AA%E9%96%8B%E7%99%BA%E3%81%BE%E3%81%A7-%E5%B1%B1%E7%94%B0-%E7%A5%A5%E5%AF%9B/dp/4815619484)
content-visibility: auto のアクセシビリティツリーについて

## 概要

　MDNの記述とChrome・Firefoxの実装が違う。

## 説明

　MDNによると以下のようになっています。

> オフスクリーンコンテンツのスタイルはレンダリングされないので、display: none や visibility: hidden で意図的に非表示にした要素はアクセシビリティツリーに現れます。 要素をアクセシビリティツリーに現れたくない場合は、 aria-hidden="true" を使用してください。
> [content-visibility - CSS: カスケーディングスタイルシート | MDN](https://developer.mozilla.org/ja/docs/Web/CSS/content-visibility#%E3%82%A2%E3%82%AF%E3%82%BB%E3%82%B7%E3%83%93%E3%83%AA%E3%83%86%E3%82%A3%E3%81%AE%E8%80%83%E6%85%AE)

(英語版)
> Since styles for off-screen content are not rendered, elements intentionally hidden with display: none or visibility: hidden will still appear in the accessibility tree. If you don't want an element to appear in the accessibility tree, use aria-hidden="true".

　最初に読んだときは、『`display: none` や `visibility: hidden` で意図的に非表示にした要素はアクセシビリティツリーに現れる』という意味かと読んで、そんなわけないじゃん！となってました。
　MDNのIssueにも同様の内容がありました。

https://github.com/mdn/content/issues/37894

　つまり、`content-visibility: auto`でオフスクリーンとなっている場合、レンダリングされないから、`display: none`や`visibility: hidden`を含めたCSSが適用されずアクセシビリティツリーから除去されない、という意味なのだそうです。（わかりづらい……）

## 現実

除去されてるじゃん……

![2025-03-01_15h46_01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65745/17c5c854-5474-4fc1-9413-de1d0fd45f0f.png)

```css
.auto {
    content-visibility: auto;
}

.hidden {
    visibility: hidden;
}

.none {
    display: none;
}
```

```html
<div class="auto">content-visibility: auto</div>
<div class="hidden auto">visibility: hidden</div>
<div class="none auto">display: none</div>
<div>display: block</div>
```

```js
document.querySelectorAll('.auto').forEach(x => x.addEventListener("contentvisibilityautostatechange", console.log));
```

<p class="codepen" data-height="300" data-default-tab="result" data-slug-hash="VYwPrxX" data-pen-title="https://github.com/mdn/content/issues/37894" data-user="lhankor_mhy" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/lhankor_mhy/pen/VYwPrxX">
  https://github.com/mdn/content/issues/37894</a> by lhankor_mhy (<a href="https://codepen.io/lhankor_mhy">@lhankor_mhy</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://public.codepenassets.com/embed/index.js"></script>

## 補足

　Chrome 133、Firefox 135 で確認。

　なお、仕様でどうなっているのかは確認未了です。
ローカルにNexusRepositoryを作ってライブラリを管理してみる

NexusRepositoryの勉強のためにローカルにNexusRepositoryを使ってライブラリを管理してみます。


https://www.compiere-distribution-lab.net/nexus-install/
https://qiita.com/kannkyo/items/5195069c65350b60edd9#%E3%83%AA%E3%83%9D%E3%82%B8%E3%83%88%E3%83%AA%E6%A7%8B%E7%AF%89-1
を参考にさせていただきました。


NexusRepositoryをインストールできたら、今回はMavenのproxyリポジトリを作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2920773/673f53f1-9700-4519-bc47-cb48a0ce55ba.png)

javaのプロジェクトのbuild.gradleに以下を書き足します。

```build.gradle
repositories {
    maven {
        url 'http://127.0.0.1:8081/repository/maven-proxy/'
        allowInsecureProtocol = true
    }
}
```

~/.m2フォルダを削除します。

gradleプロジェクトのリフレッシュを行うと、proxyリポジトリにライブラリが保存されました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2920773/fd1bca1c-7dd0-4182-98eb-07d64e716517.png)
AIエンジニアがWebアプリ開発のためにキャッチアップしたこと

# はじめに

こんにちは、AIエンジニアの[せり](https://qiita.com/t_serizawa)です。
近年のLLMの台頭で、モデル開発がメインのAIエンジニアでも簡単なアプリケーション開発が求められるようになってきました。自身も、PoCでStreamlitを用いた簡単なアプリ開発はしたことがありましたが、リリースまで意識したアプリ開発はしたことがなく、一方で業務でもある程度の理解を求められるようになってきたことから、しっかりと学ぶことにしました。
本記事では、Webアプリ開発初心者の自分が学習を始めた昨秋から半年間で学んだことと手順を振り返りながら紹介します。

# 学んだもの

Wbアプリ開発は、さまざまな言語・ツール・フレームワークが利用されるかと思います。今回は、自社で使われていることの多い以下をそれぞれ学びました。

|領域|言語・フレームワーク・ツール|
|----|-------------------------|
|バックエンド|Python (Django REST Framework)|
|フロントエンド|Next.js, TailwindCSS|
|インフラ|Azure|

# 学習順序

## 1. バックエンド (Django REST Framework) のキャッチアップ

Pythonの利用経験は7年ほどあり、それなりに書いてきましたが、それらはすべて機械学習のモデル開発やデータ分析であり、バックエンド開発は初めてでした。
そこで、まずDjango REST Frameworkの公式サイトを見て最低限の知識をインプットしました。
https://www.django-rest-framework.org/

最低限の文法を理解したところで、社内で開発しているWebアプリのコードを読みながら、分からないところを開発者に聞く、という形で知識をつけました。

## 2. フロントエンド (Next.js) のキャッチアップ

フロントエンドは全く知見がなく、未経験の状態からスタートしました。こちらは後述の全体のキャッチアップの場面で実際に実装しながらキャッチアップを進めました。

## 3. インフラ (Azure) のキャッチアップ

インフラ、特にネットワークには苦手意識があり、これまで学習を避けてきましたが、今回学習を行いました。
まず、Azureに登録して実際に触ってみましたが、構成や繋がりなどが分からず、どうすればアプリ構築が出来るのか分かりませんでした。そこで、UdemyにあったAzure講座「[合格への近道！Azure Fundamentals AZ-900 試験対策講座](https://www.udemy.com/course/microsoft-azure-az900-video/?couponCode=25BBPMXACCAGE2)」を受講して、構成や各サービスの繋がりについて理解しました。その上で、実際のアプリデプロイに必要な手順についてはネットで調べながら、不明点は社内のWebエンジニアに聞く、という形でキャッチアップを進めました。

## 4. 全体のキャッチアップ

ここまでで最低限の知識は身に付けたので、実際に実装してみることにしました。
まず、Zennで見つけた「[Django REST Framework + NextJSブログ構築](https://zenn.dev/hathle/books/next-drf-blog-book)」という記事を読みながらブログ構築を行ってみました。ただし、一部省略されていて分かりずらい部分などもあったので、Udemyで見つけた「[Nextjs + Tailwind CSS + Django REST Framework で学ぶモダンReact開発](https://www.udemy.com/course/nextjs-tailwind-css-django-rest-framework-react/?couponCode=25BBPMXACCAGE2)」というものも観ながら実装してみることで、Webアプリ開発について理解を深めました。

## 5. 個人開発

ここまでである程度開発できる自信がついたので、0から個人開発を行ってみることにしました。

まず、今回初めて触ったNext.jsに慣れるために、ポートフォリオサイトの構築を行いました。全体をNext.jsとTailwindCSSで作成し、デプロイはvercelで行っています。デプロイをvercelで行った理由は、Githubと簡単に紐づけることができ、費用も掛からないためです。

次に、いよいよ本格的な開発として音声対話AIアプリを作りました。これは、名前の通りお話ししてくれるAIシステムです。

<iframe width="560" height="315" src="https://www.youtube.com/embed/aEA7aogc-Ik?si=AbAdruxmLskHd93Q" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>

これはバックエンドをDjango REST Framework、フロントエンドをNext.jsとTailwindCSS、インフラをAzureで構築しており、AI周りは音声認識をWhisper、返答生成をGPT-4o mini、音声生成をにじボイスで行っています。フロント周りでボタン操作の関数を作成するところが分からず少し手間取りましたが、ChatGPTに教えてもらうことで構築することが出来ました。
ここまで出来るようになると、Webアプリ開発を自力で行えると言えるようになってきたのではないかと思います！

# おわりに

本記事では、Web開発初心者が半年間で学んだことを紹介しました。
この記事作成時点までに学べなかったこととして、セキュリティがあります。実際にデプロイして誰でも閲覧できるようにするとなると、Azure上のセキュリティ構成を堅牢にする必要がありますが、そこまで理解しきれていません。次のステップとして学ぼうと考えています。
ご紹介した手順がWebアプリ開発をしてみたい人の参考になれば幸いです。
Cursor 0.45で進化した！Project RulesはまるでCursor版GPTs！


Cursorのバージョン0.45で追加された新機能「Project Rules」をご存知でしょうか？この新機能を利用することで、様々なプロジェクト内タスクを自動化し、まるでGPTsのようにプロンプトを設定して効率よく作業を進められるようになりました。今回はその具体的な設定方法と活用術をご紹介します。

## Project Rulesの設定方法

まず、Project Rulesを設定してみましょう。

1. **設定メニュー**から**General**を選択し、次に**Project Rules**に移動します。
2. **「+ Add new rule」**をクリックし、新規ルールを作成します。例えば`explain-code`という名前を付けると、`.cursor/rules/explain-code.mdc`というファイルが生成されます。

### ルール設定の詳細

生成された `.mdc` ファイルを開くと、以下の3つを設定できます。

- **Description**: ルールの概要を記述します。
- **Globs**: 対象ファイルを指定します。
- **Rule**: 実行される処理を記述します。

例えば、以下のように設定します。

#### ルール設定例

```yaml
Description: "複数ファイルの解説"
Globs: ""
Rule: |
  # 与えられたファイルの解説をします
  - 複数ファイル指定された場合は、それぞれのファイルを解説します
```

設定を保存し、**Command + Shift + P**を押して**Reload Window**を選択し、ルールファイルを読み込みます。

## ルールを使用してみよう

次に、Cursor ChatでProject Rulesを使ってみましょう。

- ファイル一覧から複数のファイルを選択してCursor Chatに追加します。
- チャット入力欄で`@`マークを入力し、**Cursor Rules**から`explain-code.mdc`を選択します。
- Enterキーを押すだけで、指定したルールに基づいた解説が始まります。

## 応用例

Project Rulesの応用例もいくつかご紹介します。

### README.mdの要約

README.md内の情報を要約するルールを設定します。

```yaml
Description: "README.mdの解説"
Globs: "README.md"
Rule: |
  # README.mdの内容を要約します
```

このルールを適用することで、プロジェクト内のREADME.mdファイルを探し情報を整理してくれます。

### SQLテーブル設計の解説

`.ddl`ファイル内のSQL文を解析し、テーブル設計を解説することもできます。

```yaml
Description: "SQLのCREATE文を解説する"
Globs: "*.ddl"
Rule: |
  # 与えられたファイルの中でCREATE TABLEしている箇所を探しテーブルの解説をする
  - テーブルの役割を解説して、関連するテーブルを併記する
```

## 集大成としてのComposerのAgentモード

Project RulesはComposerのAgentモードで利用すると効果的です。指示内容と関連するルールをCursorが自動で探してきてAgentを動かします。特定のプロジェクトでよく使う処理を自動化し、効率を最大限に高めることができるでしょう。

Cursor 0.45のProject Rulesを活用して、あなたのプロジェクト管理を更に賢くしてみてはいかがでしょうか？どんどん新しいルールを作成して、さまざまなシナリオに合わせた自動化を実現しましょう！
ESP32: 外部割り込み

## ハード
抵抗内蔵の LED を　緑　２個、赤　２個
タクトスイッチ　2個
抵抗　2個　（タクトスイッチのプルアップに使用）

![esp32_mar0101.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179446/79449780-ab3d-4c67-a1fe-770b448b437f.jpeg)

## プログラム

```text:gpio_test03.ino
// ---------------------------------------------------------------------
/*
	gpio_test03.ino
						Mar/01/2025
*/
// ---------------------------------------------------------------------
#define	PROGRAM	"gpio_test03.ino"
#define	VERSION	"2025-03-01 PM 15:03"

#define LED_12 12
#define LED_13 13
#define LED_14 14
#define LED_26 26
#define LED_27 27

#define SW_A 32
#define SW_B 33

volatile bool request_aa = false;
volatile bool request_bb = false;
// ---------------------------------------------------------------------
void setup()
{
	pinMode(LED_12, OUTPUT);
	pinMode(LED_27, OUTPUT);
	pinMode(LED_13, OUTPUT);
	pinMode(LED_26, OUTPUT);
	digitalWrite (LED_12, LOW) ;
	digitalWrite (LED_27, HIGH) ;
	digitalWrite (LED_13, LOW) ;
	digitalWrite (LED_26, HIGH) ;

	pinMode(SW_A, INPUT_PULLUP);
	attachInterrupt(SW_A, blink_aa_proc, FALLING);
	pinMode(SW_B, INPUT_PULLUP);
	attachInterrupt(SW_B, blink_bb_proc, FALLING);

	delay(1000);
	Serial.println(PROGRAM);
	Serial.println(VERSION);
	Serial.println("*** setup end ***");
}

// ---------------------------------------------------------------------
void blink_aa_proc()
{
	request_aa = true;
}

// ---------------------------------------------------------------------
void blink_bb_proc()
{
	request_bb = true;
}

// ---------------------------------------------------------------------
void loop()
{
	if (request_aa)
		{
		digitalWrite (LED_27, !digitalRead(LED_27)) ;
		digitalWrite (LED_12, !digitalRead(LED_12)) ;
		delay(250);
		request_aa = false;
		}

	if (request_bb)
		{
		digitalWrite (LED_26, !digitalRead(LED_26)) ;
		digitalWrite (LED_13, !digitalRead(LED_13)) ;
		delay(250);
		request_bb = false;
		}

	delay(100);
}

// ---------------------------------------------------------------------
```

## Arduino IDE 2.3.4

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179446/b47c6db4-c119-4524-adcb-192ca45572c3.png)
シリコンハウス共立でJetson買って困っている人へ

# シリコンハウス共立でJetson買って困っている人へ #
大阪日本橋のシリコンハウス共立で
AVerMedia NVIDIA Jetson Xavier NXスタンダードボックスPC(新古品 箱入り)
を買ってセットアップに困っている人へ私がセットアップし他方法を残します。
<font color="HotPink">（あくまで衝動買いして（私を含めて）よくわからない人向けです。そんなことないわアホっていわないで）</font>

（注意）ストレージのフォーマット等それなりに危険な作業がありますので、間違っても本番環境のサーバ等で実施しないでください。また仕事で疲れている時に実施すると失敗する可能性が高いので余裕のある時に実施しましょう。

# 用意するもの #
- UbuntuをインスールしたPC（仮想環境は× ローカルにインストールしてください。）
- avermedia EN715用 BSP(ボード サポート パッケージ)
- SDカード64Gぐらい（sandisk extreme辺りを推奨)

PCは実機を用意してください。USBブートでも何でも良いです。
OSインストールはNvidiaのSDK Managerは使えません。（あれはちゃんと正規品を購入した向けみたい）

# インストール作業 #
<font color="Orange">**UbuntuPC(母艦)での作業**</font>

- BSPの解凍
/home/name/jetson
か何処かにダウンロードしたBSPを解凍してください。（普通にtarで）
以降の作業は解凍したファイル内の
```
/JetPack_xxxxxxxxxxx/Linux_for_Tegra
```

で実施してください。
- SDカードの準備
fdiskでもgpartedでもなんでも良いのでext4でフォーマットしたSDカードを作成してください。（最低32Gぐらいですが128Gや256Gを買っても良いと思います。安いし）

- UUIDの記録
SDカードからの起動の為にUUIDが必要になります。
```
sudo blkid 
```
で使用するSDカードのUUIDを記録してください。
```
 echo {記録したUUID} > bootloader/l4t-rootfs-uuid.txt
```
として l4t-rootfs-uuid.txt を bootloader フォルダに作成してください。

-SDカードへOSの書込み
（sdx1は実際に書込みSDカードの物にしてください）
```
$ sudo mount /dev/sdax /mnt
$ cd rootfs/
$ sudo tar -cpf - * | (cd /mnt/ ; sudo tar -xpf - )
$ sudo umount /mnt
```
要するにBSPのrootfsの内容をSDカードにコピーします。
SDカードに対する作業は以上です。

<font color="LimeGreen">**Jetsonでの作業**</font>
Jetsonをリカバリーモードにして母艦にUSBで接続します。
EN715上にリカバリースイッチがあるので、それを押しながら電源を投入するとリカバリーモードで起動します。
USB接続後に

<font color="Orange">**UbuntuPC(母艦)での作業**</font>
```
dmesg
```
とコマンドを打つとjetsonが母艦に接続されているのが分かると思います。

それではJetson内部のeMMCにファイルを転送します。
```
$ sudo ./flash.sh jetson-xavier-nx-en715 mmcblk1p1
```
SDカードから起動するのにeMMCに書き込むのなんでやねんという感じなのですが、要は起動時はeMMCを読みに行き実際はSDカードから起動するという構成になるようです。（勉強不足）

転送が終わったらJetsonの電源を切って、SDカードを差し込んで起動するとシステムが起動するはずです。
初期状態なので、ID：nvidia PASSWORD:nvidiaになっています。
このままでは素の状態なので
```
sudo apt update
sudo apt install upgrade
sudo apt install nvidia-jetpack
```
としてjetpackをインストールして開発環境等を整えて下さい。
あとはサンプルを動かしてみるステップになるのですが、OpenGL関係のエラーが出たので
```
$ sudo apt install freeglut3-dev libglu1-mesa-dev
```
でライブラリの導入が必要でした。
さあoceanFFTを動かしましょう！！
LinuxサーバーにvsFTPdをセットアップしたときの設定ファイルメモ

# vsFTPdとは
vsFTPd (very secure FTP deamon) とは、セキュリティーに重点を置いたFTPサーバープログラムです。Arch Linuxを含め多くのLinuxディストリビューションでサポートされており、簡単にインストールできます。
ここでは、vsFTPdのインストールから、chrootの設定やTLSの有効化、パッシブモードの設定などをしていきます。

# FTPユーザーの追加
FTPサーバーに接続するための、専用ユーザーを追加しておくことはよいマナーです。ということで、まずはFTP接続専用ユーザーを追加して、ftpグループに加えておきます。
```shell-session
$ sudo useradd -m ftp_user
$ sudo passwd ftp_user
$ sudo usermod -aG ftp ftp_user
```
ここではFTP接続専用ユーザーとして、ftp_userを追加しました。

# vsFTPdをインストール
Arch Linuxでは、vsFTPdは標準リポジトリに入っているので、インストールは以下のコマンド一発で完了します。
```shell-session
$ sudo pacman -S vsftpd
```

# 基本的な設定
まずは、基本的な設定をしていきます。
/etc/vsftpd.confを編集します。
```shell:/etc/vsftpd.conf
annonymous_enable=NO
local_enable=YES
write_enable=YES
use_localtime=YES
```
# アクセスを許可するユーザーを設定
まず、/etc/vsftpd.confに以下を追記します。
```shell:/etc/vsftpd.conf
userlist_enable=YES
userlist_deny=NO
userlist_file=/etc/vsftpd/user_list
```
つぎに、/etc/vsftpd/user_listにアクセスを許可するユーザー名を列挙します。
```shell:/etc/vsftpd/user_list
ftp_user
```
これで、ftp_userがFTPサーバーにアクセスできるようになります。

# chrootの設定
chrootをするためには、まず以下の設定を/etc/vsftpd.confに加えます。
```shell:/etc/vsftpd.conf
chroot_local_user=YES
chroot_list_enable=YES
chroot_list_file=/etc/vsftpd/chroot_list
allow_writeable_chroot=YES
user_config_dir=/etc/vsftpd/user_config.d
```
つぎに、/etc/vsftpd/chroot_listファイルにchrootするユーザー名を列挙します。
```shell:/etc/vsftpd/chroot_list
ftp_user
```
さらに、/etc/vsftpd/user_config.d/の中に、chrootするユーザー名と同じ名前のファイルを作成し、そこにそのユーザー固有の設定を書き加えます。
```shell:/etc/vsftpd/user_config.d/ftp_user
local_root=/srv/http/www.example.com/
write_enable=YES
```
これで、ftp_userは/srv/http/www.example.com/ ディレクトリをローカルルートとして、書き込み可能な状態でchrootされるようになります。

# TLSの有効化
TLSを有効化するためには、以下の設定を/etc/vsftpd.confに加えます。
```shell:/etc/vsftpd.conf
ssl_enable=YES
ssl_tlsv1=YES
ssl_sslv2=NO
ssl_sslv3=NO

require_ssl_reuse=NO
ssl_ciphers=HIGH
rsa_cert_file=/etc/letsencrypt/live/www.example.com/fullchain.pem
rsa_private_key_file=/etc/letsencrypt/live/www.example.com/privkey.pem
```
ここでは、認証にLet's Encryptを使用する場合の例を挙げています。

# パッシブモードの有効化
## アクティブモードとパッシブモード
FTP接続にはアクティブモードとパッシブモードの二種類があります。
アクティブモードでは、FTPサーバーは標準で21番ポートを使用して通信を行います。このとき、FTPクライアント側も21番ポートを使ってデータのやり取りをするため、クライアント側も21番ポートを解放してある必要があります。
一方でパッシブモードでは、データのやり取りにはサーバー側であらかじめ定められた仮想ポートを使います。クライアント側は21番ポートを解放する必要はありません。
現在では、セキュリティー上の観点からもパッシブモードが主に使われています。
# パッシブモードの有効化
```shell:/etc/vsftpd.conf
pasv_enable=YES
pasv_addr_resolve=YES
pasv_address=myhost.example.com
pasv_min_port=60000
pasv_max_port=60100
```
*myhost.example.com*は自身のサーバーのドメイン名に読み替えてください。
ここでは、60000番から60100番までのポートをパッシブモードの通信用のポートとして使用するように設定しています。

# ポートの解放
vsFTPdが外部と通信できるように、ファイアーウォールのポートを解放します。
```shell-session:
$ sudo ufw allow 21/tcp
$ sudo ufw allow 60000:60100/tcp
```
ここでは、先に指定したものと同じ60000番から60100番までのポートを解放します。
上記では、ファイアーウォールはUFWを使用していることを想定しています。他のファイアーウォールを使っている場合も、基本的にやることは同じです。

# vsFTPdの開始
```shell-session:
$ sudo systemctl start vsftpd
$ sudo systemctl enable vsftpd
```
これで、vsFTPdのセットアップは完了です。

# まとめ
今回は、vsFTPdを使ってFTPサーバーを構築する手順をまとめてみました。参考にしていただければ幸いです。
ローカル5Gで余計な仕事を増やして本末転倒だか、なんだかカッコイイからヨシッ！

CIMCなどて、リモートから状態を見れるようにサーバは作られているので、わざわざローカル5Gロボットがデータセンター内を巡回する必要はありません。

データセンター作業に行ったことがありますか、データセンターのサーバ室内に構築作業の人間はいても、監視員はいません。なぜなら、リモート監視できるようにネットワークとサーバは設計されているからです。

LEDの状態をロボットがカメラで映し出さなくても、商用グレードのサーバはリモートログインして、各種LEDの点灯状態を画面に表示することができます。

総務省などローカル5Gを推進している団体や業者は、先人の作り込んだシステムを軽視して、デタラメ5G活用を押し付けてるようにもみえます。

※本記事は[いるか速報](https://momokokko.blog.fc2.com)より転載された記事です。

参考情報：
[データセンターにおけるローカル5Gを活用した 運用省人化および安定運営の実現](https://go5g.go.jp/sitemanager/wp-content/uploads/2023/06/%E4%BB%A4%E5%92%8C%EF%BC%94%E5%B9%B4%E5%BA%A6%E3%83%AD%E3%83%BC%E3%82%AB%E3%83%AB%EF%BC%95%EF%BC%A7%E9%96%8B%E7%99%BA%E5%AE%9F%E8%A8%BC%E7%AD%89_%E6%88%90%E6%9E%9C%E5%A0%B1%E5%91%8A%E6%9B%B8_%E9%96%8B07_%E6%A6%82%E8%A6%81%E7%89%88.pdf)

[ローカル5G業者の運用管理ツールの導入する目的が無能な件](https://momokokko.blog.fc2.com/blog-entry-9022.html)
【備忘録】【文法読解】Goの「`ctx, cancel := context.WithTimeout(ctx, c.ctxTimeout)`」

Go言語でよく見かける以下のコードは、**コンテキストを使ってタイムアウトを設定する処理**です。

```go
ctx, cancel := context.WithTimeout(ctx, c.ctxTimeout)
```

この行の役割と動作を順を追って説明します。

---

## 1. **コンテキスト（`context.Context`）とは？**

コンテキストは、**処理の実行に関連する情報や制御を管理する仕組み**です。主に以下の用途で使われます：
- **タイムアウト**: 指定時間内に処理が終わらない場合、自動的にキャンセルする。
- **キャンセル**: 処理を途中で中断する。
- **値の伝達**: 処理中に特定の値を共有する。

Goでは並行処理（ゴルーチン）を多用するため、コンテキストを使ってリソースの無駄遣いを防ぎ、処理を効率的に制御します。

---

## 2. **`context.WithTimeout` の役割**

`context.WithTimeout` は、指定された時間内に処理が終わらない場合に自動的にキャンセルされるコンテキストを作成します。

- **引数**:
  - `ctx`: 親コンテキスト。既存のコンテキストを指定する。
  - `timeout`: タイムアウト時間。`time.Duration`型で指定する（例: `5 * time.Second`）。
- **戻り値**:
  - `ctx`: タイムアウトが設定された新しいコンテキスト。
  - `cancel`: タイムアウトを手動でキャンセルする関数。

---

## 3. **`ctx, cancel :=` の意味**

Goの**多重代入**を使い、`context.WithTimeout` の戻り値（`ctx` と `cancel`）を同時に受け取ります。

- **`ctx`**: タイムアウトが設定された新しいコンテキスト。
- **`cancel`**: タイムアウトを手動でキャンセルする関数。

---

## 4. **具体的な動作**

この行の動作をステップバイステップで説明します。

#### ステップ 1: タイムアウト付きのコンテキストを作成
```go
ctx, cancel := context.WithTimeout(ctx, c.ctxTimeout)
```
- `context.WithTimeout` を呼び出し、指定されたタイムアウト時間（`c.ctxTimeout`）が設定された新しいコンテキスト（`ctx`）を作成する。
- 同時に、このコンテキストを手動でキャンセルする関数（`cancel`）も返される。

#### ステップ 2: キャンセル関数を遅延実行
```go
defer cancel()
```
- `defer` は、関数の終了時に指定された処理を実行するキーワード。
- ここでは、`cancel` 関数を遅延実行し、関数の終了時に必ずコンテキストがキャンセルされるようにする。

---

## 5. **なぜこれが必要か？**

この処理は、以下の問題を解決します。

#### タイムアウトの必要性
- 長時間かかる処理（例: データベース接続、外部APIリクエスト）が終わらない場合、リソースの無駄遣いを防ぐ。

#### キャンセルの必要性
- タイムアウトが発生した場合や処理が不要になった場合、コンテキストをキャンセルして関連する処理を中断する。
- リソースの解放やクリーンアップを確実に行う。

---

## 6. **具体的な例**

以下は、この処理を使った例です。

```go
func (c createAccountInteractor) Execute(ctx context.Context, input CreateAccountInput) (CreateAccountOutput, error) {
    // タイムアウトを設定（例: 5秒）
    ctx, cancel := context.WithTimeout(ctx, c.ctxTimeout)
    defer cancel() // 関数の終了時にキャンセルを実行

    // 新しいユーザーを作成
    user := domain.NewUser(
        domain.UserID(domain.NewUUID()),
        input.Name,
        input.Email,
        time.Now(),
    )

    // ユーザーをデータベースに保存
    createdUser, err := c.repo.Create(ctx, user)
    if err != nil {
        // エラーが発生した場合、エラーを返す
        return c.presenter.Output(domain.User{}), err
    }

    // 正常に処理が完了した場合、結果を返す
    return c.presenter.Output(createdUser), nil
}
```

#### 動作の流れ
1. `context.WithTimeout` で5秒のタイムアウトを設定。
2. `defer cancel()` で、関数の終了時にキャンセルを実行。
3. データベースへの保存処理（`c.repo.Create`）が5秒以内に終わらない場合、コンテキストがキャンセルされ、処理が中断される。
4. エラーが発生した場合、エラーを返す。
5. 正常に処理が完了した場合、結果を返す。

---

## 7. **まとめ**

- **`context.WithTimeout`**: 指定時間内に処理が終わらない場合、自動的にキャンセルするコンテキストを作成。
- **`ctx, cancel :=`**: 多重代入で新しいコンテキストとキャンセル関数を受け取る。
- **`defer cancel()`**: 関数の終了時に必ずキャンセルを実行。

この仕組みを使うことで、長時間かかる処理を制御し、システムの安定性とパフォーマンスを維持できます。
vagrant複数起動時、Vagrantfileを共通化させる（副題：フォルダ名を参照してVagrantfileの設定に使う）

# 概要
vagrant仮想マシンを複数同時に起動するとき、
Vagrantfileの格納フォルダ名をVagrantfileの設定に使うことで、
仮想マシンごとにVagrantfileを編集しなくていいようにした

# 解決したかった問題点

プライベートネットワーク内でVagrant仮想マシンを複数同時に起動するときは、
各マシンごとの設定で、

```ruby:例：
  config.vm.network "private_network", ip: "192.168.33.15"
  config.vm.network "forwarded_port", guest: 22, host: 2015
```

この2つはマシンごとに異なる値としなければいけない。

面倒なので設定しないでいいようにしたい。

# 解決方法

フォルダ名から情報を取得するようにした。
（注釈：仮想マシンを複数扱う場合、Vagrantfileは個別のフォルダに置くはずという前提です。）

```ruby:Vagrantfile
  # フォルダ名の中で最初に出てくる数字を、IPアドレス第4オクテットとして用いる
  # フォルダ名の中で最初のアンダースコアまでを、ホスト名として用いる
  # IPアドレス第4オクテット＋2000をポート番号とする
  dirname = File.basename(Dir.pwd)
  confOctet4 = dirname.gsub(/^[^1-9]*([1-9][0-9]*).+$/, '\1')
  confHostName = 'vm' + dirname.gsub(/^([^_]+).+$/, '\1')
  confPort = 2000 + confOctet4.to_i
  puts 'folder: ' + dirname
  puts 'octet4: ' + confOctet4
  puts 'hostName: ' + confHostName
  puts 'port: ' + confPort.to_s

  # ゲストOSにはプライベートネットワーク接続とする
  config.vm.hostname = confHostName
  config.vm.network "private_network", ip: "192.168.33." + confOctet4
  config.vm.network "forwarded_port", guest: 22, host: confPort
```

必須ではないがホスト名を識別できた方が便利なので、ホスト名も指定した。

# 結果

vagrant up したとき、putsの結果が出力されるので、これで設定値を確認できる。

```:例：フォルダ名が012_testのときの出力
folder: 012_test
octet4: 12
hostName: vm012
port: 2012
```

構築もうまくいった。

# puts は実は邪魔かも

putsは結果確認目的で記述したが、
vagrant ssh するたびにputs結果が出力されるので、
安定稼働に入ったら消していいかも。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/282159/b4bc697e-4eaf-4b18-af95-8274e0bc14cb.png)
エックスサーバーのCLI版PHPを最新バージョンに切り替える方法

### **エックスサーバのCLI版PHPを最新バージョンに切り替える方法**

#### **はじめに**
エックスサーバでは、ウェブサイトのPHPバージョンは管理画面から簡単に変更できますが、SSHでPHPを実行すると **古いバージョンのPHPが使われる** ことがあります。

例えば、SSHで `php -v` を実行すると、以下のように **PHP 5.4** などの古いバージョンが表示されることがあります。

```bash
$ php -v
PHP 5.4.16 (cli) (built: Nov  1 2019 16:04:20)
```

このままでは、最新のWordPress管理ツール **WP-CLI** などのツールが正しく動作しません。  
WP-CLIは **PHP 7.4以上** を推奨しており、古いPHP環境ではエラーが発生します。

また、PHP 5.4はすでに **サポート終了** しており、セキュリティリスクも高いため、**PHP 8.3** に切り替えることをおすすめします。（※一部サーバでは8.4を利用できるようなので下記の手順で確認してください）

---

### **1. 現在のPHPバージョンを確認**
まず、現在のCLI版PHPのバージョンを確認します。

```bash
$ php -v
```

もし古いバージョン（例：5.4や7.1など）が表示されたら、次の手順で最新のバージョンへ変更します。

---

### **2. 利用可能なPHPバージョンを確認**
エックスサーバには複数のPHPバージョンがインストールされているため、まず利用可能なバージョンを確認します。

```bash
$ ls /usr/bin/php*
```

出力例：
```bash
/usr/bin/php          /usr/bin/php7.4      /usr/bin/php8.0      /usr/bin/php8.3
/usr/bin/php5.4       /usr/bin/php7.4-cgi  /usr/bin/php8.0-cgi  /usr/bin/php8.3-cgi
```

この中で、 **最新のPHP 8.3** を選びます。

---

### **3. デフォルトのPHPバージョンをPHP 8.3に変更**
デフォルトで `php` コマンドを実行したときに **PHP 8.3** が使われるように、`~/.bashrc` にエイリアスを設定します。

```bash
$ echo "alias php='/usr/bin/php8.3'" >> ~/.bashrc
$ source ~/.bashrc
```

**ポイント**
- `echo` コマンドで `.bashrc` に設定を追加  
- `source ~/.bashrc` を実行して、すぐに設定を反映  

---

### **4. 設定変更を確認**
正しく変更されたかを `php -v` で確認します。

```bash
$ php -v
```

出力例：
```bash
PHP 8.3.1 (cli) (built: Jan 10 2024 15:32:10) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.3.0, Copyright (c) Zend Technologies
```

無事に **PHP 8.3** に変更されました！

---

### **5. 注意点**
- **SSH再ログインが必要な場合**  
  `.bashrc` の変更がすぐに反映されない場合は、一度 **ログアウトして再ログイン** してください。
- **PHPのバージョンを変更したい場合**  
  他のバージョン（例：8.2）を使いたい場合は、`alias php='/usr/bin/php8.2'` に変更してください。
- **CGI版との違いに注意**  
  `php8.3-cgi` ではなく、通常の `php8.3` を指定してください。

---

### **まとめ**
エックスサーバのCLI版PHPを最新バージョンに切り替える手順は以下の通りです。

1. `php -v` で現在のバージョンを確認  
2. `ls /usr/bin/php*` で利用可能なPHPバージョンを調査  
3. `.bashrc` に `alias php='/usr/bin/php8.3'` を追加  
4. `source ~/.bashrc` を実行して設定を反映  
5. `php -v` で変更を確認  

この方法で、エックスサーバのCLI版PHPを最新の **PHP 8.3** に変更できます。ぜひ試してみてください！

※追記
コメントで指摘いただきました。**PHP 8.4**に変更できるサーバもあるようです！
rex0220 印刷屋プラグイン kintoneグラフの埋め込みタグ表示

[rex0220 印刷屋プラグイン](https://qiita.com/rex0220/items/9be2d9b20a3a1f016c76)で、複数の kintone グラフをまとめて表示してみます。
埋め込みタグのグラフを img 化して表示します。

# 概要

印刷屋プラグインは、埋め込みタグのグラフを画像化して img タグで表示します。
他アプリのグラフも表示できます。

![2025-03-01_14h28_38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/0f4ede0b-3e60-425c-b838-faf29afcca6c.png)


- プレビュー画面

![2025-03-01_14h21_45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/5cb67fb1-abe3-4ec5-95fa-9f04013a481b.png)

# 印刷屋プラグイン設定

埋め込みタグの iframe は、印刷屋プラグインで img に置き換えられます。
css は、img タグに対して指定してください。

![2025-03-01_14h22_56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/87aa26da-9dff-4710-b820-e01d07a784c5.png)

![2025-03-01_14h23_31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/0558a0c8-d348-45ce-a6a3-494eb4a23742.png)

```css:
.pcraft-graph-header {
  font-size: 24px;
  margin: 30px;
}
.pcraft-graph-container {
  display: flex;
  gap: 20px; /* グラフ間の間隔 */
  margin-bottom: 30px; /* 下のコンテナとの間隔 */
}

.graph1 {
  display: flex;
  flex-direction: column; /* ラベルと画像を縦並びに */
  align-items: center; /* 中央寄せ */
}

.graph1 img {
  width: 100%;
}
```

kintone 埋め込みタグ（iframe）をコピペして貼り付けます。
iframe は、印刷屋プラグインで img タグに置き換えられます。

```html:
<div class="rex0220-pcraft-page">
  <div class="pcraft-graph-header">案件まとめグラフ ${DATE_FORMAT("TODAY","YYYY年M月D日")}</div>
  <div class="pcraft-graph-container">
    <div class="graph1">
      <span>見込み金額</span>
      <iframe width="800" height="600" frameborder="0"
        src="https://devenxyfi.cybozu.com/k/381/report/portlet?report=2063&amp;chartType=column&amp;stackType=clustered"></iframe>
    </div>
    <div class="graph1">
      <span>顧客別合計金額</span>
      <iframe width="800" height="600" frameborder="0"
        src="https://devenxyfi.cybozu.com/k/381/report/portlet?report=11252053&amp;chartType=pie"></iframe>
    </div>
  </div>
  <div class="pcraft-graph-container">
    <div class="graph1">
      <span>オプション費用</span>
      <iframe width="800" height="600" frameborder="0"
        src="https://devenxyfi.cybozu.com/k/381/report/portlet?report=11252057&amp;chartType=bar&amp;stackType=clustered"></iframe>
    </div>
  </div>
</div>
```
WindowsにNginxをインストール&パスを通す方法

## Nginxとは
OSSのWebサーバソフトウェア

## Nginxのインストール方法
1. nginxをダウンロード
2. ダウンロードファイルを展開する
3. nginxフォルダをnginxを利用する場所に移動させる
4. nginxの実行

### nginxのダウンロード
[nginxの公式サイト](https://nginx.org/en/download.html)から「Stable version」の「nginx/Windows-1.26.3」をダウンロードします。
※ nginx/Windows-1.26.3でなくても、ダウンロード時の最新のStable versionであれば大丈夫です。

### ダウンロードファイルの展開
ダウンロードファイルを展開します

### nginxフォルダをnginxを利用する場所に移動させる
今回はCドライブ直下に移動しました

### nginxの実行
nginxがあるディレクトリに移動してコマンドを実行します。(Powershellで行いました。)
cd部分はnginxフォルダがある場所のパスを指定します。
```powershell
cd /nginx-1.26.3
start nginx
```

`http://localhost:80`にアクセスするとnginxの初期画面が表示できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4021963/48bf6c50-c120-42c3-916c-3ad12acbfb7a.png)

## 環境変数にnginxのパスを追加する
### nginxのパスを通す理由
パスを通さない場合、Powershellなどで直接nginxと入力しても「nginxは認識されません」と表示されるからです。
実際、nginxを停止させるコマンドをパスを通す前に行ったところ、エラーが起きましたが、パスを通してからは問題なく停止しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4021963/15e03438-f641-4d7a-83c2-f887f6f9d180.png)

### 環境変数にnginxのパスを追加する
1. 「スタート」メニューを右クリックし、システムを選択
2. 「システムの詳細設定」をクリックし、「環境変数」を選択
3. 「システム環境変数」の「Path」を選択し、「編集」をクリック
4. 「新規」をクリックし、nginxのインストールパス(例: C:\nginx-1.26.3)を追加
5. 変更を保存して閉じ、Powershellを再起動


## nginxを停止する
```powershell
nginx -s quit
```

## まとめ
これで、WindowsにNginxを導入して、ローカルホストでアクセスできる環境が整いました。
Substance 3D PainterではPython APIとJavaScript APIのどちらを使用したほうがよいか？

## 環境
* Windows
* Adobe Substance 3D Painter 10.1.2

## PythonとJavaScriptのどちらを選択するべきか？

Substance 3D Painter APIはJavaScriptとPythonの2言語に対応しています。
ただし、完全に互換性があるわけではありません。
ではどちらを選択するべきかという話ですが、 下記の観点からPythonをおすすめします。


### デバッグのしやすさ

Substance 3D PainterにはPythonに対応したコンソールはありますが、JavaScriptに対応したコンソールはありません。
ただし、10.1.2現在ではPythonに関しても1行ずつしか実行できないため扱いやすさについてはやや難があります。


### API の更新状況

Python APIに関しては更新が続いていますが、JavaScript APIに関しては8.3.0以降 更新が止まっています。今後、更新が入る可能性はもちろんありますが、10.1.2時点では目立った更新がありません。


### 別言語のコード評価

Substance 3D Painter APIの厄介な点として、JavaScript APIにしか存在しないAPI (shaderなど) とPython API にしか存在しないAPI (layerstackなど) があります。

ただし、Python API については js モジュールがあり、JavaScript コードを評価して実行できるためこの問題に対処できます。
一方、JavaScript APIではPython APIのコードを評価する手段がないため、Python API に比べてできることに制限があります。

### 返り値

JavaScript APIでは戻り値が文字列のケースが多く取得できる情報も限定的ですが、 Python APIではオブジェクトを返します。
例えば サンプルデータの ```PreviewSphere``` に対して環境マップのリソース情報を取得する処理では下記のような結果になります。

```javascript:JavScript
alg.display.getEnvironmentResource();

// 戻り値は文字列
// resource://starter_assets/Glazed Patio?version=19588866ffc0ba91e0e23edf27a0abae89691e87.image
```

```python:Python
substance_painter.display.get_environment_resource()

# 戻り値はResourceID
# ResourceID(context='starter_assets', name='Glazed Patio', version='19588866ffc0ba91e0e23edf27a0abae89691e87.image')
```
【セッションレポート】AI導入のヒント！Amazon QとBedrockの実践事例を解説

## はじめに

こんにちは！アキキー（[@akikii__](https://x.com/akikii__))です
生成AIアプリケーションに興味があり自分でもAmazon Bedrockを利用しています。
生成AIアプリケーションがどのように生まれているのか興味があったので、セッションを拝聴いたしました！

## セッション

### 一人目の発表者

株式会社ベンジャミン
木村さん

* お客さんから「AWSのAI興味ある」と相談をうけた
* チャットツールを触ってみるだけだと面白くないので「付加価値」をつけたい

### Bedrock KnowlageBaseを扱ってみる

*  外部情報の検索を組み合わせることで解答制度を向上させる技術
*  Knowledge Baseが出た時から取り入れている
*  コストをかけにように考えている
    *  OpenSearch Serviceは高い！Aurora Serverlessを利用することにした
    *  PineconeはAWS外なので除外した（お客様要望）
*  プルダウンでRAGとLLMの解答を切り替えっれるようにした

### コードのサジェスト機能（正式な名前は忘れちゃいました！すみません）

*  Amazon QにもRAG機能がある
*  お客様のコードを入れることにより、お客様のコーディングスタイルに合ったコードを提案できる
*  コードをAIに読み込ませて大丈夫？の質問にしては、お客様のデータと分離できているからOK
*  Q Developer Proを使うことにより、IAM Identity Centerを用いてアクセス性d減できる

### 2人目の発表者

株式会社ベンジャミン
松延さん

### BedrockとTranscribeを組み合わせた議事録生成ツール

#### 開発したきっかけ

* 業務効率性のため、社内生産性を2倍にする活動をしている
* 議事録に対しての熱い要望が多かった
* 会議の録画データが残っている
* 録画データをAmazon Transcribeを使って文字起こし→ Bedrockで要約

#### 概要
* オンライン会議ツールから出力した録画ファイルをWeb画面からアップロードすることで、文字起こししている
* 出力はNotionページへ出力→Slackチャンネルに通知している

#### 利用イメージ
* ① 録画ファイルをアップロード
* ② 録画ファイルの音声データから文字起こし、議事録作成
* ③ 議事録をNotionページへ出力
* ④ Slackチャンネルにて利用者へつうt

#### 使ったAWSサービス

* Bedrock
* Amplify Gen2
* Cognito
* S3
* EventBridge
* Step Functions
* Transcribe
* DynamoDB
* Lambda

#### AWS構成
* S3アップロードしたらEventBridgeにてStep Functionsワークフローを起動
    * Transcribeで文字起こしして
    * Lambdaでプロンプト生成
    * Bedrockで議事録生成
    * Lambdaで議事録出力
* プロンプトはDynamoDBに登録している

#### 工夫した点
* IaC化
    * 利点を活かしてAWSリソースを管理している
    * フロントエンド: Amplify Gen2 (CDK)
    * バックエンド: CDK for Python
* ローコード開発
    * Step Functionsでワークフローを組んでいる
    * 失敗箇所をグラフビューで見ることができるのでデバッグがやりやすい
* プロンプトチューニング
    * プロンプト用のテンプレートをDynamoDBテーブルで管理している
    * Lambdaの中でハードコーディングすると後々回収が大変なので、外に出している
    * コンソール上からプロンプトチューニングできる
    * モデルもDynamoDBに登録しているので、モデルも容易に変更可能

#### 今後実装予定の機能
* 話者分離機能
    * 誰がどのように発言したのかを反映
    * Transcribeでは最大10名まで
* 議事録の制度をスコアリング

#### 補足
* 生成AIスマート議事録サービスの記事も出ている
    * https://benjamin.co.jp/service/genai-minutes/

## 感想

生成AIを利用した製品はどんどん増えていますが、製品の具体的な中身を知ることができる貴重な機会でした
自分も個人で生成AIアプリケーションを開発しているので、参考になる部分が多々ありました！！
【書評】アーキテクトの教科書

## 読んだ本

https://www.shoeisha.co.jp/book/detail/9784798184777

## 感想

アーキテクトって何をするんだろうというざっくりとした疑問から読み始めた。内容としては、開発工程を上流から順に、それぞれの工程でどのようなことを担当するのか、すべきなのかを説明してくれる。自分の今までの経験だとアーキテクトという役割の人は見たことがなかったが、本書を読む限りだと、いなくても誰かが対応しなくてはならない項目に思えたし、実際自分が対応していたことも含まれていた。そのため、設計など上流工程に少しでも関わる人は身につけておくべき内容で、アーキテクトの有無にかかわらず実行できるようにしておくのが理想だなと感じた。

## おすすめの人

- アーキテクトはどんな役割なのか興味がある人
- 設計など上流工程に携わる人

## 次に読むなら

- 個人的に開発工程周りをもう少し体系的に学びたく、PM周りの本を読んでみようと思っている
HPLT v2.0 BERT for Japaneseは『雪国』の冒頭をどうトークナイズするのか

[HPLT](https://hplt-project.org/)から「[HPLT v2.0 BERT for Japanese](https://huggingface.co/HPLT/hplt_bert_base_2_0_jpn-Jpan)」がリリースされたので、とりあえずトークナイザを試してみることにした。

```
>>> from transformers import AutoTokenizer
>>> tkz=AutoTokenizer.from_pretrained("HPLT/hplt_bert_base_2_0_jpn-Jpan")
>>> print(tkz.convert_ids_to_tokens(tkz("国境の長いトンネルを抜けると雪国であった。","夜の底が白くなった。")["input_ids"]))
['[CLS]', 'âĸģåĽ½å¢ĥ', 'ãģ®', 'éķ·', 'ãģĦ', 'ãĥĪãĥ³', 'ãĥįãĥ«', 'ãĤ', 'Ĵ', 'æĬľãģĳãĤĭ', 'ãģ¨', 'éĽª', 'åĽ½', 'ãģ§', 'ãģĤãģ£', 'ãģŁ', 'ãĢĤ', '[SEP]', 'âĸģå¤ľ', 'ãģ®', 'åºķ', 'ãģĮ', 'çĻ½', 'ãģı', 'ãģªãģ£', 'ãģŁ', 'ãĢĤ', '[SEP]']
```

うーん、かなりマズイ気がする。

```
>>> text="国境の長いトンネルを抜けると雪国であった。夜の底が白くなった。"
>>> x=tkz(text,return_offsets_mapping=True)
>>> for i,(s,e) in zip(x["input_ids"],x["offset_mapping"]):
...   print(text[s:e],text[s:e].encode("utf-8"),ascii(tkz.convert_ids_to_tokens(i)))
...
 b'' '[CLS]'
国境 b'\xe5\x9b\xbd\xe5\xa2\x83' '\xe2\u0138\u0123\xe5\u013d\xbd\xe5\xa2\u0125'
の b'\xe3\x81\xae' '\xe3\u0123\xae'
長 b'\xe9\x95\xb7' '\xe9\u0137\xb7'
い b'\xe3\x81\x84' '\xe3\u0123\u0126'
トン b'\xe3\x83\x88\xe3\x83\xb3' '\xe3\u0125\u012a\xe3\u0125\xb3'
ネル b'\xe3\x83\x8d\xe3\x83\xab' '\xe3\u0125\u012f\xe3\u0125\xab'
を b'\xe3\x82\x92' '\xe3\u0124'
を b'\xe3\x82\x92' '\u0134'
抜ける b'\xe6\x8a\x9c\xe3\x81\x91\xe3\x82\x8b' '\xe6\u012c\u013e\xe3\u0123\u0133\xe3\u0124\u012d'
と b'\xe3\x81\xa8' '\xe3\u0123\xa8'
雪 b'\xe9\x9b\xaa' '\xe9\u013d\xaa'
国 b'\xe5\x9b\xbd' '\xe5\u013d\xbd'
で b'\xe3\x81\xa7' '\xe3\u0123\xa7'
あっ b'\xe3\x81\x82\xe3\x81\xa3' '\xe3\u0123\u0124\xe3\u0123\xa3'
た b'\xe3\x81\x9f' '\xe3\u0123\u0141'
。 b'\xe3\x80\x82' '\xe3\u0122\u0124'
夜 b'\xe5\xa4\x9c' '\xe5\xa4\u013e'
の b'\xe3\x81\xae' '\xe3\u0123\xae'
底 b'\xe5\xba\x95' '\xe5\xba\u0137'
が b'\xe3\x81\x8c' '\xe3\u0123\u012e'
白 b'\xe7\x99\xbd' '\xe7\u013b\xbd'
く b'\xe3\x81\x8f' '\xe3\u0123\u0131'
なっ b'\xe3\x81\xaa\xe3\x81\xa3' '\xe3\u0123\xaa\xe3\u0123\xa3'
た b'\xe3\x81\x9f' '\xe3\u0123\u0141'
。 b'\xe3\x80\x82' '\xe3\u0122\u0124'
 b'' '[SEP]'
```

やはり、UTF-8の[Byte-Pair Encoding](https://huggingface.co/learn/nlp-course/chapter6/5)がナマで使われてる(ただし0x00～0x20・0x7f～0x9fはU+0100～U+0120・U+0121～U+0141へマッピング)。文頭のmetaspace「âĸģ」は何とか捌くとしても、助詞の「を」が2バイト・1バイトで泣き別れになってて、すこぶる使いにくい。内蔵の`model_ltgbert.py`には[バグが残ってるみたい](https://huggingface.co/HPLT/hplt_bert_base_2_0_jpn-Jpan/discussions/1)だし、日本語モデルとして使うのは無理があるかな。
Jetson Xavier NX のセットアップに苦労した話

# Jetson Xavier NX 今更買った理由 #
安かったからだよ
大阪日本橋のシリコンハウス共立で新古品が３万円以下で売ってたのを衝動買い。
色々言い訳してJetsonを敬遠したいたのだけど、そろそろヤバいと思い始めることに。

## 純正の開発者キットを買え ##
ubuntu初心者とかとりあえずエッジAIやりたい人は
純正のJetson 開発者キットを買いましょう。

:::note info
nvidiaのドキュメントは純正開発環境向けにかかれているのでサードパーティーのキャリアボードのサポートはガン無視されます。
:::
ちなみに今回苦労したのはサードパーティーのキャリアボードだったのと、衝動買いで情報が無かったから。
詳細は次項

# Jetson Xavier NXはSDカードから起動できない #
何言ってんだこいつってなると思いますが、サードパーティーのキャリアボードではSDブートが出来ません。（でした。凄い人は出来るかもしれない。）
殆どの人が純正品のnanoを買って始めるので当たり前のようにブートイメージSDを用意して始めている記事を見ますが、サードパーティーのキャリアボードはそもそも起動時に認識しません。（でした。凄い人は出来るかもしれない。）
（実際はいったん内蔵eMMCでブートしてSDに移行でするセットアップは出来る）

## でどうするか ##
これを読む人はLinuxとか普通に出来て、組込みも出来るし、安い純正外のJetsonセット買ったけど、どないやねんって人だと思います。
セットアップに苦労しないために以下の準備をしてください。

1.  実機のUbuntuPC
1台2台余っていると思うので、Ubuntuは実環境にインストールしてください。
<font color="red">仮想環境はダメ絶対：最初メンドクサイのでvirtualBOXでやったんですが失敗します。</font>

2. キャリアボードの把握
・キャリアボード毎にBSP(ボードサポートパッケージ)が各メーカから用意されています。そこでSDカードのドライバやらが含めた形でOSが配信されるのでそれを使いましょう。(上級者はNvidiaの純正でもいけると思うけど、BSPを使うのが吉)
3. SanDiskのmicroSDカード 64GB以上ぐらい
 SDカードは良い物を使いましょう。SanDiskのExtremeで良いっしょ。相性で失敗報告もあるので。

なんか簡単な用意なんですが、これを用意するに至るまで1週間かかりました。
実際のセットアップ事例は別記事に記載します
rex0220 印刷屋プラグイン 一覧画面をカード形式で表示

[rex0220 印刷屋プラグイン](https://qiita.com/rex0220/items/9be2d9b20a3a1f016c76)で、一覧画面をカード形式で表示してみます。

# 概要

一覧画面のレコードをカード形式で PDF に出力します。
一覧画面で表示されている並び順で作成されます。

- プレビュー画面

![2025-03-01_11h48_44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/e9e29364-7844-45b3-addf-23ac1c0b4162.png)

- 案件情報のカード形式表示

![2025-03-01_11h49_24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/f98d9930-9817-4aca-b859-35c622a79b60.png)

- 一覧画面で選択したレコードを表示

![2025-03-01_11h48_20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/e3a635f1-ca55-4f00-be43-37746262607f.png)

# 印刷屋プラグイン設定

![2025-03-01_11h53_38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/d9cdea85-74ad-4408-8b5c-2733ffa71641.png)

- CSS, HTML, 計算式 の設定

![2025-03-01_11h54_25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/3fe01eac-3a0e-4ebe-874e-ad330b7a557b.png)


- CSS
    - 金額欄を右寄りに表示
    - 総合計欄の背景色

```css:
.pcraft-card-list {
    width: 320px;
}
.pcraft-card-item {
  position: relative;
  height: 24px;
}
.pcraft-card-value {
    width: auto;
}
.value-amount {
  position: absolute;
  right: 30px;
}
.pcraft-card-footer {
  background-color: lightblue;
}
```

- 計算式： 金額のクラス指定が少し面倒くさい
    - "type","card"： カード形式で表示
    - 金額欄を右寄りに表示するため、value-amount クラス指定
    - DQUOTE 関数は、"" が入れ子になるのを回避するため指定

```js:
RECS_HTML(
    OPT("title","案件情報 " & TODAY() ,
       "type","card",
       "hideLabels","Y",
       "pagination","Y",
       "perPage","8",
       "footer","Y"
    ),
    ARRAY("#","#"&($rseq+1)),
    顧客名,部署名, 案件名,
    ARRAY("確度",確度,"value-center"),
    ARRAY("プラン費用", "<span>プラン費用: </span><span class='value-amount'>"&YEN(プラン費用)&"</span>", "value-amount","'<span>総プラン費用: </span><span class='&DQUOTE('value-amount')&'>'&YEN(RECS_SUM(プラン費用))&'</span>'"),
    ARRAY("オプション費用", "<span>オプション費用: </span><span class='value-amount'>"&YEN(オプション費用)&"</span>", "value-amount","'<span>総オプション費用: </span><span class='&DQUOTE('value-amount')&'>'&YEN(RECS_SUM(オプション費用))&'</span>'"),
    ARRAY("合計費用", "<span>合計費用: </span><span class='value-amount'>"&YEN(合計費用)&"</span>", "value-amount","'<span>総合計費用: </span><span class='&DQUOTE('value-amount')&'>'&FIXED(RECS_SUM(合計費用))&'</span>'")
)
```
RailsのScope vs クラスメソッド：返り値とSQLクエリの違いを徹底解説

## Scopeについて
モデルには、Scopeというものを定義できる。
Scopeとは、よく利用する検索条件に名前をつけて、ひとまとめにしたもの。
高価なワインだけを取得するためのScope、costlyを定義↓
```ruby
class Wine < ApplicationRecord
    scope :costly, -> { where("price > ?", 5000) }
end
```

```ruby
Wine.costly
```
↑この記述だけで、↓と対価となります。
```ruby
Wine.where("price > ?", 5000)
```

メリットは2つ
1.繰り返し利用するクエリの再利用性が上がる
2.クエリに名前をつけることで、可読性が向上

## クエリ(Query)とは？
クエリとは、データベースに対して「データを取得したり、追加・更新・削除するための指示（命令）」のこと。
今回の例だと、データベースの winesテーブルから価格が 5000 円以上のワインを取得したい場合、SQLだと次のようなクエリになります。

```sql
SELECT "wines".* FROM "wines" WHERE (price > 5000) 
```

Rails では、ActiveRecord を使ってこのクエリを Ruby のコードとして表現できる。
Scopeを使えば、以下のコードだけで表現できます。
```ruby
Wine.costly
```
データベースとアプリケーションの橋渡しをするのがクエリの役割で、アプリケーションはクエリを使ってデータを取得したり、更新したりして、それを画面に表示する。

## Scopeとクラスメソッドの違い
大きく分けて3つあります。

1.返り値
2.生成されるSQLクエリ
3.チェーン（連結）できるかどうか

### Scope
Scopeを利用してWine.costlyを実行すると
必ずActiveRecord::Relationを返します。
sqlクエリにはLIMIT が付かないため、条件に合致するすべてのレコードが取得できます。
```sql
SELECT "wines".* FROM "wines" WHERE (price > 5000)
```
必ずActiveRecord::Relationを返すため、チェーンしてさらに条件を追記できる
```ruby
Wine.costly.where("name LIKE ?", "%Chateau%")
```
発行されるSQLクエリ↓
```sql
SELECT "wines".* FROM "wines" WHERE (price > 5000) AND (name LIKE '%Chateau%')
```

### クラスメソッド
```ruby
def self.find_by_price(price)
  find_by(price: price)
end
```
find_byを利用するため、返り値は単一のレコードか、nilになります。（レコードが見つからなければnil）そのため、チェーンで他の条件を追加することはできません。
```ruby
Wine.find_by_price(5000) 
```
上記を実行すると、
発行されるSQLクエリ↓
```sql
SELECT "wines".* FROM "wines" WHERE "wines"."price" = 5000 LIMIT 1
```
LIMIT 1 が自動的に付加されるため、最初の1件だけが返されるようになっています。

### まとめ
Scopeは複数のレコードを対象に条件をチェーンで追加でき、SQLクエリもその条件が全て反映されます。結果がnilとなった場合も該当Scopeの検索条件を除外したクエリを発行し、必ずActiveRecord::Relationを返すという動作をします。

一方、クラスメソッドは単一レコードの取得に特化していて、SQLクエリには自動的に LIMIT 1 が付与され、チェーンもできません。

Scopeで定義する場合とクラスメソッドで定義する場合はこのような違いがあるため、nilを返す必要がある場合はクラスメソッドとして定義する必要があります。

#### 参考文献
https://www.amazon.co.jp/%E3%83%91%E3%83%BC%E3%83%95%E3%82%A7%E3%82%AF%E3%83%88-Ruby-Rails-%E3%80%90%E5%A2%97%E8%A3%9C%E6%94%B9%E8%A8%82%E7%89%88%E3%80%91-Perfect/dp/4297114623/ref=sr_1_1?adgrpid=52707338829&dib=eyJ2IjoiMSJ9.NT1QGDUNPiIVAgPvfl1gPFbeM_WRAo6y-jFRsD9ME-WMO3BblrO0oqAczqesWDcbTkgWvcUZGmo29oXrL6WNKWg8q-rcKKpLxH1SU3U4_36Cg1jIjB4IVZumbVko1ZiV4pKf_yqc3o-Ufd5mmJXNLzrUBOZu-Z0zAppnVgKA1xKiqO8FEvmX7QTr5I2W60toc4HKd7x5rKYkuN1EVFYRDNkww_4o25Pbb_Kybz6suOKGv3G94Hkez5-YMkJk3ggMyyZyB_uDXZE1jecHvd4KxRy65iFKXSTxhuoAlkWqYsH9sS508JCHISX4dDn-o8gw0CPXzWZ8XJF3bdPYU5LSoQvR4zOSPyu4P6twWQMWsgnLpdJHuC9C9oAVI8APWsn0Ekaa2ASAxY5mqK7lG-0Pw8mrMZ_EuhHG2IafUHP-Mb67kPO-aCSUPsTTlIEfVQH8.vy0dqJQPf2UqPXEKln8TYtlXT13Lu6q-1Kvq9WNduH4&dib_tag=se&hvadid=678984843978&hvdev=c&hvlocphy=9197747&hvnetw=g&hvqmt=e&hvrand=2654740006560884045&hvtargid=kwd-387895450927&hydadcr=27294_14738600&jp-ad-ap=0&keywords=%E3%83%91%E3%83%BC%E3%83%95%E3%82%A7%E3%82%AF%E3%83%88+ruby+on+rails&mcid=4b1c4374de763d29b7b499a31ee89b53&qid=1740798008&sr=8-1

https://railsguides.jp/active_record_querying.html