OmniAICreator/Qiita-1.07M huggingface dataset:
Pythonで始める音楽制作: pydubライブラリ入門

## はじめに
音楽制作や音声編集をPythonで行いたいと思ったことはありませんか？pydubは、そんな願いを叶えてくれる強力なライブラリです。この記事では、pydubの基本的な使い方を紹介し、音声処理の世界への扉を開きます。

## pydubとは？
pydubは、Pythonで音声ファイルを簡単に操作できるライブラリです。波形の生成、編集、エフェクト適用など、様々な音声処理が可能です。

## インストール
pydubのインストールは非常に簡単です。以下のコマンドをターミナルで実行するだけです。

```
pip install pydub
```

注意：pydubは内部でFFmpegを使用しています。FFmpegがインストールされていない場合は、別途インストールが必要です。

## 基本的な使い方

### 1. 音声ファイルの読み込み
```python
from pydub import AudioSegment

# WAVファイルの読み込み
sound = AudioSegment.from_wav("example.wav")

# MP3ファイルの読み込み
sound = AudioSegment.from_mp3("example.mp3")
```

### 2. 音量の調整
```python
# 音量を6dB上げる
louder_sound = sound + 6

# 音量を3dB下げる
quieter_sound = sound - 3
```

### 3. 音声の切り取り
```python
# 最初の5秒を切り取る
first_5_seconds = sound[:5000]

# 2秒から7秒までの部分を切り取る
slice = sound[2000:7000]
```

### 4. ファイルの書き出し
```python
# WAVファイルとして書き出し
sound.export("output.wav", format="wav")

# MP3ファイルとして書き出し
sound.export("output.mp3", format="mp3")
```

## 簡単なプロジェクト: 音声ファイルの前後に無音を追加する

以下は、音声ファイルの前後に1秒間の無音を追加するシンプルなスクリプトです。

```python
from pydub import AudioSegment

# 音声ファイルを読み込む
sound = AudioSegment.from_wav("input.wav")

# 1秒間の無音を生成
one_second_silence = AudioSegment.silent(duration=1000)  # durationはミリ秒単位

# 音声の前後に無音を追加
padded_sound = one_second_silence + sound + one_second_silence

# 結果を保存
padded_sound.export("padded_output.wav", format="wav")
```

## まとめ
この記事では、pydubの基本的な使い方を紹介しました。音声ファイルの読み込み、音量調整、切り取り、書き出しなど、基本的な操作方法を学びました。これらの基本を押さえれば、より複雑な音声処理も可能になります。

## 参考リンク
- [pydub公式ドキュメント](https://github.com/jiaaro/pydub#installation)
- [FFmpegのインストール方法](https://ffmpeg.org/download.html)
ReferenceError: React is not defined 

# 問題
ReactとViteの環境下でjestとreact-testing-libraryを使用してtestしたときに下記のエラーがずっと出続けていました。

```
ReferenceError: React is not defined
```

# やったこと

エラー内容からReactのimportが必要なのかと思ったので、関係するファイルへ明示的に```import React from "react";```を記載して再度テストを実施。

エラーは解消されませんでした。

# 解決方法

.babelrcファイルへ"runtime": "automatic"を追加したところ、うまくいきました。

```
{
  "presets": [
    "@babel/preset-env",
    ["@babel/preset-react", {
      "runtime": "automatic"
    }]
  ]
}
```

### 解決理由
React17以降に新しいJSX変換方式が導入されたようですが、新しい方を使用するにはBabelの設定を変更する必要があるようです。
新しいJSX変換方式のサポートによって```import React from "react";```が不要になっていましたが、Babelの設定がデフォルトの古い変換方式になっていて、Reactインポートエラーが出ていたようでした。
"runtime": "automatic"を入れることで、新しい変換方式を有効にすることができるようです。



# 参考記事

https://zenn.dev/nbstsh/scraps/97ded6d64561e5

https://legacy.reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html#manual-babel-setup

# おわりに
何をやってもずっと同じエラーが出続けてしまったので、解消に少し時間がかかりました。
エラーの理由は意外とシンプルだったので、同じエラーの方の参考になれば幸いです！
import.meta.env cannot read properties of undefined


# 問題
ReactとViteの環境下でjestとreact-testing-libraryを使用してtestをしたときにエラーが発生していました。
```
import.meta.env cannot read properties of undefined
```

# 解決方法

Viteを使用してsupabaseの環境変数設定をする場合、import.meta.envを使用する必要があります。

ただ、このままだと上記エラーが出てsupabaseUrlの読み込みができないため、vite.config.jsを以下のように変更しました。

```jsx
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import env from "vite-plugin-env-compatible";

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react(), env({ prefix: "VITE", mountedPath: "process.env" })],
});
```

また、supabaseの環境変数設定を行っているところをimport.meta.envからprocess.envに変更しました。
```jsx
import { createClient } from "@supabase/supabase-js";

export const supabase = createClient(
  process.env.VITE_SUPABASE_URL,
  process.env.VITE_SUPABASE_ANON_KEY
);
```

vite.config.jsにplugins: [react(), env({ prefix: "VITE", mountedPath: "process.env" })]とすることで、VITEでimport.meta.envが必要なところをprocess.envに読み替えてくれるようになります。


# おわりに
エラーが出た時は解決策は複雑かもと心配しましたが、意外とシンプルになおせました。
【Ruby】ちゃんと動かないcase/when

## はじめに：突然ですが、問題です
こんなメソッドがあります。

```ruby
def role_name(role)
  case role
  when 'Admin'
    'Administrator'
  when
    'Member'
  else
    'Guest'
  end
end
```

このとき、以下のコードの実行結果はどうなるでしょうか？

```ruby
puts role_name('Admin')
puts role_name('Member')
puts role_name('Guest')
```

正解はCMのあとで！！

＜CM＞

Rubyを知れば、Railsはもっと楽しくなる。
「プロを目指す人のためのRuby入門 改訂2版」好評発売中！🍒

https://ruby-book.jnito.com/

＜CMおわり＞

では正解です。先ほどのコードを実行するとこうなります。
（おや、"Member"が出力されませんね :thinking: ）

```
Administrator

Guest
```

あなたは正解しましたか！？

## 先ほどのコードの何がおかしいのか

もう一度`role_name`メソッドの実装を見てみましょう。

```ruby
def role_name(role)
  case role
  when 'Admin'
    'Administrator'
  when
    'Member'
  else
    'Guest'
  end
end
```

どこがおかしいかわかりましたか？

はい、ここですね。

```ruby 
  when
    'Member'
```

これ、whenに対応する条件式が`'Member'`になってしまっています。
そしてこの条件式が真になったときの処理がありません。

問題点がわかりやすいように書き直すならこうです。

```ruby
def role_name(role)
  case role
  when 'Admin'
    'Administrator'
  when 'Member'
    # 何もしていない（nilを返す）
  else
    'Guest'
  end
end
```

いちおうRubyの文法的には正しいので、構文エラーが発生することはありません。
しかし、実装としては明らかに間違いです。

## 正しく書き直すならこう
roleが"Member"なら"Member"を表示する、という要件なら、以下のように書くのが正解です。

```ruby
def role_name(role)
  case role
  when 'Admin'
    'Administrator'
  when 'Member'
    'Member'
  else
    'Guest'
  end
end
```

こうすればroleが"Member"の場合でも名称が返ってきます。

```ruby 
puts role_name('Member')
#=> Member
```

## RuboCopで検知する

もしRuboCopを使っているなら、このミスはLint/EmptyWhenというcop（ルール）で検知可能です。

https://docs.rubocop.org/rubocop/cops_lint.html#lintemptywhen

```
app/models/user.rb:65:5: W: Lint/EmptyWhen: Avoid when branches without a body.
    when ...
    ^^^^
```

## RBS + Steepで検知する
もし事前に `role_name` メソッドの型定義をRBSで書いていれば、`steep check` で型エラーが発生するので実装ミスを検知できます。

```rbs 
# user.rbs
def role_name: (String role) -> String
```

```
$ steep check
# Type checking files:

...........................................................................................F

app/models/user.rb:65:5: [error] Cannot allow method body have type `(::String | nil)` because declared as type `::String`
│   (::String | nil) <: ::String
│     nil <: ::String
│
│ Diagnostic ID: Ruby::MethodBodyTypeMismatch
│
└     def role_name(role)
          ~~~~~~~~~

Detected 1 problem from 1 file
```

## まとめ
今回紹介したコードは、僕がコードレビューの最中に実際に遭遇した事例です。

構文エラーのようで構文エラーではない（ふつうに実行できてしまう）ので、テストが不十分だとバグを抱えたまま本番リリースされかねません。
Ruby初心者の方はこういったコードを書いてしまわないよう注意しましょう。

また、「コードを書いた本人が気を付ける」だけではうっかりミスは避けにくいので、

- 必ずコードレビューを受ける
- RSpecやMinitestのテストコードで条件分岐を全網羅する
- RuboCopを導入する
- RBS/Steepで型検査する

といった工夫を取り入れましょう。
認証方法・用語まとめ

## 認証と認可

- 認証：アクセスしようとしている人が本当に本人かを確認する
    - ex. Basic認証、OpenID Connect
- 認可：（認証された）ユーザーが、どの情報や機能にアクセスできるかを決定する
    - ex. OAuth 2.0

## 認可・認証方式の比較

### Basic認証

ユーザー名とパスワードで認証を行う方式

Authorizationヘッダに `Authorization: Basic <BASE64エンコードしたユーザ名:パスワード>`をセットしてRequestを送る

※暗号化ではないため、HTTPSの利用が必須

※ログアウトの仕組みはない

### Digest認証

ユーザー名とパスワードで認証を行う方式

サーバーで生成したランダムな文字列と組み合わせてハッシュ化することで、Basic認証よりセキュリティを強化している

※ログアウトの仕組みはない

### Bearer認証

クライアントは事前に入手したアクセストークンをAuthorizationヘッダにセットして、リクエストを送信する

アクセストークンは主にOAuthで取得することが多い（Bearer認証はOAuthの規格の一部）

アクセストークンが盗まれると攻撃されるので、HTTPSの利用が必須

アクセストークンはフロントエンドで保管（CookieにHttpOnlyおよびSecureな値として保存するのが良い）し、HTTPヘッダーやメッセージボディで渡す

### OAuth

クライアントが、アプリケーションへのログインなどでリソースオーナーの同意を得ると、認証サーバーは期限付きのアクセストークンを生成し、クライアントに送信する。
なお、この認可フローにはいくつかの種類が存在するが、一般的には、認可コードと交換でアクセストークンを取得するAuthorization Code Grantが用いられる。

クライアントとWebサーバー間は上記のBearer認証などを用いる

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/678160/d86a6070-24e6-a727-1928-94e5478f1afd.png)
画像は[今さら聞けない暗号技術＆認証・認可](https://gihyo.jp/book/2023/978-4-297-13354-2)より

### OpenID Connect

OAuth 2.0を拡張したプロトコルで、認証結果を含むアイデンティティ情報をIDトークンと呼ばれるJSON Web Token（JWT）に入れて受け渡す

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/678160/c7619070-355e-c3c3-ad67-f68b0bb315d5.png)
画像は[今さら聞けない暗号技術＆認証・認可](https://gihyo.jp/book/2023/978-4-297-13354-2)より

### IP制限

特定のIPアドレスまたはIPアドレス範囲からのみアクセスを許可する方法

なお、IPスプーフィング（IPアドレスのなりすまし）や内部からの攻撃を防ぐことは難しい

### その他の用語

SSO（シングルサインオン）：1度のユーザー認証で複数のシステムの利用が可能になる仕組み。SSOを実現するプロトコルとして、OpenID ConnectやSAMLが存在する

フェデレーション認証：シングルサインオンを実現する方式の一つ。複数の組織やサービス間でユーザー認証を共有する仕組み。SAMLやOpenID Connectといったプロトコルにより実現される

SAML：SSOのフェデレーション認証方式を実現するプロトコルの一つ。OpenID Connectとは異なり、XML SAMLフォーマットを用いる

多要素認証：認証の3要素である「知識情報」、「所持情報」、「生体情報」のうち、2つ以上を組み合わせて認証すること

2段階認証：IDパスワードとは別に、第2の認証方法を設定する仕組み（要素は同じでも構わない）

## 認証情報の利用方法

### 認証/セッション情報として何を利用するか

OpenID Connectなどで、認証を行った後、ステートレスを実現するには2つの方法が考えられる

- サーバーサイドでSession IDを発行し、リクエストの際には、DBに問い合わせることでユーザ ID等を取得する
- OpenID Connectで得られたJWTをそのまま利用する、または認証後にサーバーサイドでJWTを発行する。ユーザ ID等はJWTに含まれる

JWTを利用するのはDBアクセスしなくて良いというメリットはあるが、サーバ側でのセッション無効化が難しく、Session IDのようなものを再発明する必要があるため、セッション管理という意味ではSession IDを利用することが望ましい

### 認証/セッション情報の保存場所

認証/セッション情報を保存する場所としては、CookieとLocal Storageの2つが考えられる。それぞれの主にセキュリティ面での比較は以下のようになる

| 項目 | Local Storage | Cookie |
|-----|--------|---------------|
| XSS | JavaScriptでアクセス可能なため脆弱 | HttpOnly属性をつけることで緩和策となる。ただし、Cookieを利用した秘密情報の盗み出しやなりすましによる攻撃は防止できない |
| CSRF | HTTPリクエストに自動的に付与されないので問題ない | SameSite属性でStrictまたはLax（POSTメソッドのリクエストのみを受け付ける処理の場合）を指定することで防止することが可能 |

すなわち、適切な設定を行ったCookieを利用した上で、XSSはあくまで緩和策であることに留意し、他の対策と組み合わせることが望ましい。

**適切なCookieの設定**

- Secure属性をつける
- 不適切なDomain指定を行わない（指定しなければCookieを発行したドメインのみ許可される。指定するとサブドメインまで許可されるので、指定しないのが安全）
- SameSite属性をStrictとする（POSTメソッドのリクエストのみを受け付ける処理の場合はLaxでも可）
- HttpOnly属性をつける

なお、Domain属性は送信先、SameSite属性は送信元を指定する属性である。

## 参考

- [今さら聞けない暗号技術＆認証・認可](https://gihyo.jp/book/2023/978-4-297-13354-2)
- [Basic認証、Digest認証、Bearer認証、OAuth認証方式について](https://architecting.hateblo.jp/entry/2020/03/27/130535)
- [クロスサイトスクリプティング(XSS)対策としてCookieのHttpOnly属性でどこまで安全になるのか](https://www.youtube.com/watch?v=4JREwhSC2dQ)
Windows IIS + Perl 2024

## Windows 11 Pro Internet Information Service で Perlを動かす

標準ではperlが入っていないので、Strawberry Perlを使う。ただし、最新版を入れると`Locale 'Japanese_Japan.932' is unsupported, and may crash the interpreter.`と言われてしまう。

## 古いバージョンのStrawberry Perlをインストール

[strawberry-perl-5.28.2.1-64bit.msi](https://strawberryperl.com/releases.html)をインストールするとよいようだ。 参考([X:自作とゲームと趣味の日々](https://x.com/jisakuhibi/status/1727629470285365678))

## winget upgrade --all対策

古いバージョンをいれても、`winget upgrade --all`コマンドで、すべてのソフトをアップデートしてしまうと、Strawberry Perlも新しいバージョンにされてしまって、エラーが出るようになってしまう。

```ps
>winget pin add StrawberryPerl.StrawberryPerl
見つかりました Strawberry Perl [StrawberryPerl.StrawberryPerl]
ピンが正常に追加されました
```

を行っておくと、StrawberryPerlが自動でアップデートされなくなる。

## jcode.plや、jacode.plが読まれない

```
HTTP エラー 502.2 - Bad Gateway
指定された CGI アプリケーションは、正しく動作しませんでした。HTTP ヘッダーの完全なセットが返されませんでした。返されたヘッダーは "Can't locate jacode.pl in @INC (@INC contains: C:/Strawberry/perl/site/lib C:/Strawberry/perl/vendor/lib C:/Strawberry/perl/lib)
```

cgiと同じディレクトリ内に置いても読まれないようなので、`C:/Strawberry/perl/site/lib`に移動しておく。
pythonでのエラー発生時のログ出力方法

# 背景
pythonでのデータ分析において、for分で処理を行うが一旦エラー発生も込みで全てに処理を実行し、後でエラーデータ案件に絞って処理を行いたい。

# 対処方法（chat gptを使用）
tracebackのモジュールを使用して例外処理を行う。
下記chat gpt提案のコードで解決。

~~~python
import traceback

def process_item(item):
    # ここにエラーが発生する可能性のある処理を記述
    if item == 0:
        raise ValueError("ゼロの項目は処理できません")  # ゼロの項目に対してエラーを発生させる
    return 10 / item

items = [10, 0, 5, 0, 2]  # サンプルのアイテムリスト

for item in items:
    try:
        result = process_item(item)
        print(f"アイテム {item} の処理結果: {result}")
    except Exception as e:
        # エラーが発生した場合にエラーメッセージとスタックトレースを出力し、次のアイテムに進む
        tb = traceback.format_exc()
        print(f"アイテム {item} の処理中にエラーが発生しました: {e}")
        print(f"スタックトレース:\n{tb}")
        continue

~~~

ログをlinux環境で残したい場合は、linuxのリダイレクト機能を使用して下記で標準出力の内容をtxtに保存可能。
~~~
xxx.py > yyy.txt
~~~

※
String変数のイコール比較を説明するのに苦労した

# 経緯
テレワーク中に新人プログラマーより、if文内にて==比較して同値の値が入っているのに、if文の中に入らないという悩みを相談された。
# 解決法方法
==で数値比較であれば問題ないが、String変数をイコールにて比較しようとしていた。
対処方法としてequalsメソッドを使用してというだけでは何の成長にもならないかなとは思いつつ、ポインターのアドレスの紹介をするのもなんだなと思った。
# 即座に回答した内容
コンピューターって数値でしか処理できないから、文字も文字コード表を元に数値があてはめられてるんだよね。
数値と文字はあくまで文字コード表を元にしか変換できないわけで、、、、、、。
↑
この辺から説明が苦しくなる。

# 結局行きつく先
=はしょせん数値比較でString変数みたいな文字列に関してはここに文字を保存したよってアドレスで保管してるんだよね。同じアドレスだと同じ文字が入ることになるんだよね。
今回の場合にはデータベースからは同じ値を取り出しているんだけど、別々に保管してるんだよね。
だから、別のアドレスが当てはまっている。最初のほうをtest1という変数、test2の変数とすると、test1を編集してもtest2は編集されない。つまりこれって別のアドレスに保管されてるってことになるんだよね。
なんで、今回イコール比較しようとすると、Stringの保管先のアドレスを比較しようとすることになるんで、=比較はできないんだよね。（たとえ、同じ文字列としても、別のアドレスに保管されている値のため）。

これって結局ポインターの概念説明してるんじゃね。

# なんでといわれたら
Stringなんかはequalsメソッドで比較しなきゃだめ。(これが一番楽な説明方法）

# 意図しない編集
編集が画面上で発生しているかどうかを調査するのに、画面上に表示するための変数と別途データベースにもともとある値の比較などという事態が発生して場合にも編集対象の画面のDTOとデータベースのDTOをイコールでコピーみたいなことをすると、同じアドレスになってしまい、イコール比較ができなくなるパターンが存在する。
なので、独自メソッドのクローン関数などを使用して、コピーをしないといけない場合もある。


# 参考文献

https://ti-tomo-knowledge.hatenablog.com/entry/2018/10/16/004625
Salesforce Dev Toolsを使ってみた【Google拡張機能】

本橋孝昭です

今回はGoogle拡張機能「Salesforce DevTools」を使ってみましたので使い方などを共有します


今回の内容は動画でも共有しています

https://www.youtube.com/watch?v=vhYjTtVNe7I

<iframe width="560" height="315" src="https://www.youtube.com/embed/vhYjTtVNe7I?si=z8N7j0cjMjO5nhYu" 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>

## Salesforce DevTools とは？
Salesforce DevToolsとはGoogle Chrome拡張機能です
「xgeek.net」より提供されています

https://www.xgeek.net/

後述しますがこのDevToolsはSalesforce開発をするうえでとても便利な機能がありますので興味がある方はこちらからインストールしてみてはいかがでしょうか

https://chromewebstore.google.com/detail/salesforce-devtools/ehgmhinnhggigkogkbhnbodhbfjgncjf

インストール成功するとGoogle Chromeからこのような画面になります

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531631/e3e901b8-bb39-d356-251b-110e06e71a04.png)



## Salesforce DevTools使える機能1 オブジェクトごとの項目をエクスポート

よく使う機能の1つとしてオブジェクトの項目のエクスポートです
オブジェクトごとに項目一覧をエクスポートすることが可能です


エクスポート画面はこのようになります

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531631/bf47710f-621f-ea77-25c7-b361480eb287.png)


ここで「Object Fields Definitition」をクリックすればこのように取引先オブジェクトの項目一覧が表示されます
選択リスト項目は選択リスト値が。数式項目は数式が表示されるのでとても便利ですね

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531631/dabccc10-6f5d-c6ff-594c-33d3067143d2.png)


## Salesforce DevTools使える機能2 ER図の取得

Salesforceで初めて見る環境の場合「どのオブジェクトとどのオブジェクトがリレーションがあるか（つながりがあるか）」が分かりにくい場合があります

その場合こちらで「ERDs」をクリックすることで

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531631/bf47710f-621f-ea77-25c7-b361480eb287.png)

このような図を作成することが可能です

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531631/a8ce99a5-8ba0-bf2e-29e3-1403123934be.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531631/df49e732-113c-f66a-b0f1-30d528f7f1a1.png)

これは便利ですね

## Salesforce DevTools使える機能3 ページレイアウト上の項目のAPI参照名を確認


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531631/31a79e5f-42a3-f49a-aad5-661dbcf04fd1.png)

DevToolsの機能の1つでページレイアウト上に表示している項目のAPI参照名を確認する方法があります
それは任意のレコード表示の際に「Hide Object API Name」をクリックすることです

Salesforceの仕様上「API参照名は全く同じものは無いが表示ラベルは同じ項目の作成が可能」となっています
そのため、ページをぱっと見るだけでは（特に表示ラベルが同じ場合に）どの項目が表示されているか分かりにくいですが、これであれば（API参照名を手掛かりに）どの項目がページレイアウト上に表示されているか容易に分かりますね

## Salesforce DevTools使える機能4 項目の使用状況が分かる

Salesforce管理者のよくある悩みとして「どの項目がちゃんと使われてるんねん（どの項目が不要なのか）」という悩みがありますね
手掛かりの一つとして「項目の入力状況」により項目が使われているか否かの判断をするかと思いますが、そのような項目入力状況もDevToolsなら確認可能です


Object Fields　をクリックして任意の項目の「集計」をクリックすれば任意の項目の使用状況が分かります
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531631/496d1a59-8279-3b67-bb4c-2932e9890b9b.png)



いかがでしょうか
Salesforce DevToolsは新機能が結構な頻度でリリースされていますので、また目新しい機能があれば追記します
また「私はSalesforce DevToolsでこんな使い方をしている」という情報を知っている方がおりましたら情報お待ちしております！
コードレビューチェックリスト

# 背景
私は３年目エンジニアです。３年目になると、コードレビューをする側に回ることもあり、
コードレビューをする側とされる側、両方経験して、思うことがあります。

コードレビューをする側
- 体裁面や命名規則などケアレスミスを指摘したり、そこまで細かく全てのコードをレビューすることは工数がかかる
- まだ私自身経験が浅いため、チェックリストのようなものがないと、指摘漏れ、観点もれがある

コードレビューされる側
- ケアレスミスを指摘されると申し訳ない

このような経験があり、
- 実際に私が受けた指摘
- 良いコード／悪いコードで学ぶ設計入門
- ネット上の有益な記事
から得た知見をもとに、自分なりにレビューチェックリストを作成しました。


# 共通

- [ ]  仕様書通りに実装できているか

## 体裁面

- [ ]  警告やエラーが出ていないか
- [ ]  デバッグ用のコードが紛れていないか
- [ ]  インデントがずれていないか
- [ ]  フォーマッターを実行したか

## 命名規則
- メソッド名
- ファイル名
- 変数名
に対して確認する。

### 体裁が整っているか、規約に反していないか

- [ ]  スペルは正しいか
- [ ]  コーディング規則に従った形か

### より良い名前はないか

- [ ]  具体的で意味範囲の狭い名前か
- [ ]  限定的なスコープ以外でtmp,result などの汎用的な命名をしていないか
- [ ]  名前から予想される処理と実際の処理が異なっていないか
- [ ]  略語の使用は適切か、必要以上に省略していないか



# 定数

- [ ]  マジックナンバーが書かれていないか
- [ ]  定数とグローバル変数以外でfinalを使っていないか

# クラス

- [ ]  コンストラクタで初期化されているか
- [ ]  不要な値はガード節で取り除いているか
- [ ]  （ドメイン駆動であれば）データと処理ロジックを同じクラスに記述して高凝集にしているか
- [ ]  単一責任原則を守っているか
- [ ]  継承と委譲を使い分けているか
- [ ]  例外を握りつぶしていないか
- [ ]  他のクラスと疎結合になっているか

# メソッド

## 共通

- [ ]  関数内で早めに処理が抜けられるものはreturnで適切にガード節が作られているか
- [ ]  ひとつの関数・メソッドが大きすぎないか、複数のタスクを処理していないか
- [ ]  関数名を逸脱した複数のタスクがひとつの関数に書かれていないか
- [ ]  変数が適切なスコープで宣言されているか
- [ ]  行数が長すぎないか（30行以内）
- [ ]  すでに実装している処理は、標準APIで実装できない処理か
- [ ]  処理件数が多い処理ではパフォーマンスを考慮した実装ができているか

## 引数

- [ ]  引数にnullを渡していないか
- [ ]  フラグ引数を使っていないか
- [ ]  プリミティブ型以外を渡すことができないか
- [ ]  引数の数が多すぎないか（4つ以下が理想的）

## 戻り値

- [ ]  nullを返していないか
- [ ]  プリミティブ型以外を返すことができないか

## 条件分岐

- [ ]  早期リターンをしているか
- [ ]  ガード節を有効に使えているか
- [ ]  複雑な分岐の場合はinterfaceの利用を考慮しているか
- [ ]  三項演算子によってコードが理解しづらいものになっていないか
- [ ]  switch文やif-elseの連鎖が長すぎないか

## コレクション

- [ ]  自前のコレクション処理を実装していないか、標準APIで同じ処理ができないか
- [ ]  配列以外にもタプル、ハッシュ（ディクショナリ）など、他のデータ型を検討したか
- [ ]  コレクションの操作は効率的か

## 実装漏れや二重実装などの観点

- [ ]  チーム内で横展開されていることが反映されているか
- [ ]  すでに存在する共通処理を自分で実装していないか
- [ ]  エッジケースや例外的なシナリオが考慮されているか

# ファイル

- [ ]  絶対パスが使われていないか
- [ ]  ファイルはクローズされているか

# コメント

- [ ]  コメントはコードの意図を明確に説明しているか
- [ ]  複雑なロジックには適切な説明が付けられているか
- [ ]  コードから読み取れる事をコメントにそのまま書いていないか
- [ ]  定数にはコメントが書かれているか

# セキュリティ

- [ ]  センシティブな情報（パスワード、APIキーなど）がハードコードされていないか
- [ ]  入力値のバリデーションが適切に行われているか
- [ ]  SQLインジェクションやXSSなどを考慮した実装ができているか
- [ ]  出力値は正しいエンコード処理がされているか

# パフォーマンス
特に一括〇〇など扱うデータ件数が多い場合には計算量を意識した実装を行う

- [ ]  不必要なループや処理がないか
- [ ]  大量のデータを扱う場合、メモリ使用量は適切か
- [ ]  データベースクエリは最適化されているか
- [ ]  N+1を発生させる実装はないか

# 参考資料

[良いコード／悪いコードで学ぶ設計入門 ―保守しやすい 成長し続けるコードの書き方](https://gihyo.jp/book/2022/978-4-297-12783-1)

[コードレビュー チェックリスト - Qiita](https://qiita.com/yuyakato/items/f3f22f5ec366a650aa51)

[プログラマー歴20数年の私が考えた、コードレビュー依頼前のセルフチェックリスト - Qiita](https://qiita.com/y-some/items/d9bd43d683e429fb3afb)

[コードレビューチェックリスト - doc.dev1x.org](http://doc.dev1x.org/sweng/10/)

[ソースコードレビューのポイント](https://gist.github.com/s-yoshiki/9e446d69cf388703a4711f7e69cba173)
Cloudfrontのキャッシュを削除する方法

## 概要
- 表題の通りなのです。備忘のためと、私みたいに時間を無駄に溶かす人が一人でも少なくなれば良いと思うため記載する。

## 前提
- 記事内ではCloudfrontを用いて、S3に格納してあるindex.htmlを配信する例を用いて書いてある
- 署名付きURL配信やAPI Gatewayなどの異なるオリジンのを配信する場合は、本記事記載の内容が適切でない可能性がある

## Cloudfrontのおさらい
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3531653/30469a7d-b69d-d01e-db95-0b12c44dc4f8.png)

#### キャッシュがヒットしない場合
- 下キャプチャの通り、キャッシュがヒットしない場合は、エッジサーバーを経由してS3があるオリジンサーバーまでリクエストが送信される。
  - ヒットしない例
    - 初回リクエスト時
    - キャッシュの条件に一致していないなど

#### キャッシュがヒットする場合
- キャッシュがヒットする場合は、エッジサーバーから応答が返る仕組み。（レイテンシーの低減が期待できる）

#### キャッシュがヒットしているかしていないか判断する方法
- ブラウザのネットワークタブを見ればわかる
  - `X-Cache` の値が `Miss from cloudfront` になっていたら、Cloudfrontがキャッシュとして保持していない（つまりキャッシュがヒットしていない）状態
  -`X-Cache` の値が `Hit from cloudfront` になっていたら、Cloudfrontがキャッシュとして保持している（つまりキャッシュがヒットしていいる）状態
- ヒットしていない
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3531653/04293923-df4b-89fc-3c7f-c9be78ff4e30.png)
- ヒットしている
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3531653/50b491f5-96b2-5115-b80e-959a2fc06dd8.png)

## 本題
- Cloudfrontは オリジンの内容が更新されたからといって、ホスト側かリクエストされた内容が同じだった場合、`X-Cache` の値が `Hit from cloudfront` となり、更新後のオリジンの内容がレスポンスされない可能性がある(Cloudfront側のキャッシュ設定とかにもよるかもしれない)
- ただ、細かい話は一旦置いておき、もしオリジンの内容を更新してもCloudfrontで`X-Cache` の値が `Hit from cloudfront` となってしまい、キャッシュされた内容が返され続ける状況を抜けるための方法を記載する

## Cloudfrontにてキャッシュを削除する
- 手順
  - 該当のCloudfrontのディストリビューション画面を開く
  - 「キャッシュ削除」タブを開く
   ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3531653/9c8e1d53-a9e2-265c-85a1-b738f8fe3ebf.png)
  - 「キャッシュ削除を作成」タブを開く
  ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3531653/ad4f4520-cdd4-f52c-0f84-a631ec00de97.png)
  - キャッシュを削除したいパスパターンを入力して「キャッシュ削除を作成」ボタンを押下する
   ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3531653/1096e369-dd88-f197-fd6c-717ddbc2e2c7.png)
- 以上、ブラウザを更新してネットワークタブを確認して、`X-Cache` の値が `Miss from cloudfront`になっていることを確認する


## まとめ
- まとめてみると簡単なことだが、知らないと一生ブラウザのスーパーリロードをやり続けるハメになる。ブラウザのキャッシュとCloudfrontのキャッシュが別物ということを頭で理解しておく必要があるため、今回書いてみました。誰かの参考になれば幸いです。
- Cloudfrontのキャッシュについてもう少し詳しく知りたいという方は、[こちら](https://www.k-friendly.com/19302)のサイトが非常によくまとまっていてわかりやすいので、一読いただけると良いかと思います。
モチベ

昨日 久しぶりに目標としている会社の求人募集を確認してみたら、以前より幅広い求人募集サイトで募集ページを展開していました。
本当に働いていけるかとか様々な大きな不安はあるにしろ、会社の求人募集を見ただけで勉強を始めた頃のモチベがちょっとだけ戻った気がします。

現在学んでいる教材も終わりが見えてきているので、それもまたモチベにつながっているのかもしれません。

そういえば昨日7月12日、26歳になりました。
26年も生きてきたんですね。26歳なんでまだまだ若造だとは思いますが、途中で死んだりせずよく生きてこれたと思います。
それもこれも恵まれたこの環境おかげですね。感謝して生きていきます！

***

使っているコマンドを忘れないようにメモ✍️
- `sudo /usr/local/mysql/support-files/mysql.server start`
MySQLの起動

- `sudo /usr/local/mysql/support-files/mysql.server stop`
MySQLの停止

- `php artisan serve`
Laravelのローカルサーバーの起動

- `php artisan queue:work`
キューを実行

- `npm run build`
JavaScriptなどを本番用に整理したりする。（よくわかっていない）
JavaScriptをいろいろ構ったらやったほうがいいのかな？

- `npm run dev`
開発中にブラウザを自動更新してくれる。
Go Development Dashboardとは？

# Go Development Dashboard

https://dev.golang.org/

Goの開発者が見るダッシュボード。
進捗の確認やCLの情報を見ることができる。CLとはChange Listの略で、Pull Requestのようなもの。

* [Release](#release)
* [CodeReview](#codereview)
* [Owners](#owners)
* [Stats](#stats)

## Release

https://dev.golang.org/release

リリース情報を見ることができる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/156096/5501ed48-fbe3-f5c4-d97c-48df7e3637fa.png)


## CodeReview

https://dev.golang.org/reviews

サブリポジトリに関するCLの一覧。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/156096/f3028682-5b03-1eee-1674-50eb9b485a8f.png)

## Owners

それぞれのリポジトリのオーナーを調べることができる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/156096/7117ba80-b044-103a-7386-3f56e46f2f38.png)

ちなみにGoのGerritは以下で確認できる。

https://go-review.googlesource.com/q/status:open+-is:wip

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/156096/3751b456-2557-67d2-e652-ad797c597576.png)

## Stats

https://dev.golang.org/stats

統計情報を見ることができる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/156096/55d2bb54-a77f-6685-63d3-9e97670e8d7a.png)

# Go Build Coordinater

https://farmer.golang.org/

以下の3つから構成される。

* [Build Dashboard](#build-dashboard)
* [Performance Dashboard](#performance-dashboard)
* [Builders](#builders)

## Build Dashboard

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/156096/2c2f777c-029b-dbf9-5945-a3f6f0f5e2e3.png)

GoのCIの結果を確認できる。

実装自体は[x/build](https://pkg.go.dev/golang.org/x/build)にある。

## Performance Dashboard

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/156096/6000ca93-b264-33f4-3002-36d05b5320ce.png)

ブランチごとのベンチマーク結果を確認することができる。

## Builders

buildした人を確認できる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/156096/dafdfe95-e1a7-bf89-d754-1b476cb51a69.png)


# Goのリポジトリ

https://go.googlesource.com/

Goのリポジトリの一覧。Gitilesを使って作成されている。

ちなみにサブリポジトリは以下に載っている。

https://pkg.go.dev/golang.org/x

# Contribute Guide

https://go.dev/doc/contribute

コントリビュートのガイドライン
gradle version catalog を使って exclude を書きたい

使用しているライブラリの依存関係に問題があり、excludeの指定が必要にある場合があります。

```build.gradle.kts
implementation("com.example:foo:1.2.3") {
    exclude(group = "com.example", module = "bar")
}
```

しかし、gradle version catalogを使っている場合、excludeの指定もgradle version catalogの値を使いたいなと思って調べました。

まあ、当然、以下のような指定はそのままではできません。

```build.gradle.kts
implementation(libs.foo) {
    exclude(libs.bar)
}
```

gradle version catalogで定義した、`libs.foo` の型は `Provider<MinimalExternalModuleDependency>` となっていて、ライブラリの情報へアクセスするには、Providerの`get()` メソッドを使って `MinimalExternalModuleDependency` を取得し、`MinimalExternalModuleDependency`のメソッドを呼び出します。
要するに以下のように記述することで　`group` と `module` の値を指定することができます。

```build.gradle.kts
implementation(libs.foo) {
    exclude(group = libs.bar.get().group, module = libs.bar.get().name)
}
```

ただ、すこし冗長なので、`let` を使って以下などどうでしょう？

```build.gradle.kts
implementation(libs.foo) {
    libs.bar.get().let { exclude(it.group, it.name) }
}
```

もしくは、以下のような拡張関数を定義しておけば

```build.gradle.kts
fun <T : ModuleDependency> T.exclude(dependency: Provider<MinimalExternalModuleDependency>): T =
    dependency.get().let { exclude(it.group, it.name) }
```

以下のような記述が可能です。

```build.gradle.kts
implementation(libs.foo) {
    exclude(libs.bar)
}
```

gradle **version** catalogなのにバージョン情報使ってないやんというツッコミはなしでお願いします。
プロトコルとは？約束事って結局何？

# 約束事って？
よくHTTPやTCP/IPの説明で「約束事」って使われてますよね。
- 「約束事」だけ言われても何が約束されいるんだろう？
- 単語は分かるけど説明がいまいちはっきり出来ない
こんな経験はないでしょうか？

ここでは、HTTPにおける具体的な「約束事」について詳しく説明します。

## HTTPの約束事

### HTTPとは
ご存知の方は多いと思いますが、一旦HTTPの説明から行きます。
HTTP (Hypertext Transfer Protocol) は、Webページを取得するためのプロトコル（約束事）です。
ブラウザ（クライアント）とWebサーバー（サーバー）が通信する際に使います。

### HTTPの約束事とは？
リクエストとレスポンスでそれぞれ違うのでリクエストから説明していきます。

リクエストの約束事
- メソッド: どのような操作を行うかを指定します。これが約束事の一部です
例: GET（データを取得する）、POST（データを送信する）
- URL: アクセスしたいリソースの場所を指定します
例: /index.html
- ヘッダー: 通信に関する追加情報を含みます
例: 
User-Agent: Mozilla/5.0（ブラウザの種類）
Cookie

上記の情報を含めてリクエストを送る。というのが約束事です。
これを共通認識の約束としておくことで、サーバとクライアントの間で正常に処理できるということです。
上記のいずれかが含まれてないと、サーバはどうやって処理したらいいのか分からず400番台のエラーをレスポンスとして返すことになります。


続いてレスポンスの約束事です。
### レスポンスの約束事
- **ステータスコード**: リクエストの結果を示します。これが約束事の一部です
  - 例: `200 OK`（成功）、`404 Not Found`（ページが見つからない）、`500 Internal Server Error`（サーバー内部のエラー）
- **ヘッダー**: 通信に関する追加情報を含みます
  - 例: `Content-Type: text/html; charset=UTF-8`（返されるデータの種類）
- **ボディ**: 実際のデータ（例: HTMLの内容）

上記の情報を含めてレスポンスを返す、というのが約束事です。これもまた共通認識の約束としておくことで、クライアントとサーバーの間で正常に通信が成功します。
もしこれらの約束事が守られなければ、クライアントは適切に動作しないかもしれません。

## まとめ
プロトコルの約束事を理解し守ることは、ネットワークやインターネット上でのデータのやり取りが正確かつ安全に行われるために非常に重要です。HTTPやその他の各プロトコルの約束事を守ることで、クライアントとサーバーが正しく通信し、エラーを防ぐことができます。エンジニアとして大切な基礎知識ですので、これらの約束事を理解しておきましょう。

おかしな点がある場合はご指摘お願いします。
ここまで読んでくださり、ありがとうございました。
ハート押してくださるとモチベになります。
No, you don't want a dash

Characters have names.

I happen to have some experience with text normalization of Western texts, crawled from the World Wide Web, having seen the better part of the zodiac of esoteric symbols.  Also, as a traumatic carryover from my relationship with LaTex, I also have some by-product-level of interest in typesetting. One of the recurring topics being the hyphen. 

Since poeople have a tendency to assign any word to any meaning (hello arbitrariness of the sign, hello Saussure!), I don't expect anyone to be aware of the nomenclature concerning the horizontal bar-like symbols. Say, when German people read out text loud for others, they say "minus" for the hyphen. But anyone who did LaTeX - and math - knows that minus and hyphen symbols are not interchangeable. You are butchering it. Well, historically, hyphen used to be a replacement for minus in environments where only ASCII was available. Like your grandmom's typewriter. Hence the official name `HYPHEN-MINUS`. 

Without aiming to be comprehensive, here is a list of hyphen-like symbols.

|symbol | unicode  | name   |   notes |
|---|--------|----------|----|
| - | 0x002d |plain hyphen    | the only one in **ASCII** plane | 
| ‐ |0x2010 |  unicode hyphen | | 
| ‑ |0x2011 | non-breaking hyphen | | 
| – | 0x2012 |figure dash   |  |
| — | 0x2013 | en-dash   | as wide as 'n'  |
| — | 0x2014 |em-dash   | as wide as 'm'  |
| ― | 0x2015 |dash or em-dash   | as wide as 'm'  |
| − | 0x2212 |minus     | minus is not hyphen | 
| ― | 0x2e12 |CJK minus  |minus in Japanese texts  | 
| ⸺ | 0x2e14 |double em-dash   | twice 'm'  | 
| ―| 0x2e15 |horizontal bar |   | 
|﹘|0xfe58|small em dash   |   |
|﹣|0xfe63|small hyphen minus  ||
|－|0xff0d | CJK full width hyphen-minus |  |
|ｰ|0xff70 | CJK length mark | half-width  |
|ー|0x30fc | CJK full width length mark  | 'the' kana length mark | 
|一|0x4e00 | numerical one  |   | 
|─|0x2500 | box drawings light horizontal  | may look different | 
|━|0x2501 | box drawings heavy horizontal  | may look different | 


Feel free to check the code point for any of these symbols in Python:
```Python
hex(ord("―"))
```
So, who cares?

Normally no one cares. I don't care. But when a site is _explicitly_ asking for a **dash** in the input, most probably it is not what they want. They want an ASCII hyphen, not a dash. They just don't know. Perhaps dash sounds cooler than hyphen, or easier to spell. I have never come accross any site that accepted dash - code point ` 0x2013` - in the input.

![qiita-dash.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3837456/72286168-d99c-f07f-5adb-088068c22103.png)

Yeah, the failed attempt captured in the screenshot was due to the requested dash.
OSC信号を受信してTouchDesignerで映像を出す3

前回に続き、TouchDesignerでOSCを受信して映像を出す記事の最終回です。

![TD_all.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/848771/e5a4e5c1-aeeb-e71b-453c-7dc4b34160e1.png)

今回は右側の出力系の内容になります。

まずSwitch TOPを作ります。
## Switch TOP
文字通り入力されたTOPをスイッチするものです。
Indexの値を変えることで、どの入力を出力するか決められます。
![スクリーンショット 2024-07-13 16.21.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/848771/e326a54a-3848-bf87-972e-6d3f259024a7.png)
### Null TOPを繋ぐ
全てのNull TOPからSwitch　TOPに接続します。
パラメータウィンドウの下側にInput OPとありますが、左から２番めの数字の順番で再生されます。順番を変えるときは青い矢印で変更できます。

### Indexの値の代入
ここにOSCで受信したMo1（映像選択）の信号を入れたいです。
そこで、Mo1の値の入っているMath CHOPからSwitch ＴＯＰのIndexの欄にドラッグ&ドロップします。
![スクリーンショット 2024-07-13 16.31.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/848771/72ff1d57-a7d2-a5e5-4134-0383f52e9790.png)
その前にMath CHOPの右下の＋ボタンをクリックすると、周りの枠が表示されない状態になります。
その状態でMath CHOPからSwitch ＴＯＰのIndexドラッグ&ドロップします。
![スクリーンショット 2024-07-13 16.35.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/848771/729118b9-d6a5-34d2-cae8-e287aeb899ac.png)
そうするとメニューが出てくるので、Export CHOPを選びます。

これで、OSCを介してDoctorMXなりTouchOSCなりのフェーダーから映像を選択できるようになるはずです。

## Cross TOP
続いて、Cross TOPとRectangle TOPを作ります。
Cross TOPはクロスフェードさせるためのTOPです。
![スクリーンショット 2024-07-13 16.52.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/848771/026d8997-db60-7722-a365-5dc6580e2539.png)
Rectangle　TOPはまた黒い画面用です。
Rectangle TOPとSwitch TOPをCross TOPに繋ぎます。
そしてこのCrossもフェーダーで操作できるようにしたいので、今度はCross（フェードアウト用）を繋いでいるMath CHOPの値をこのCrossに入力します。
![スクリーンショット 2024-07-13 17.00.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/848771/17e001e2-fa67-94ea-97b4-e0e23f1aeec0.png)
先ほどと同じように＋をクリックして選択できるようにした後、Cross TOPのCross欄にドラッグ&ドロップします。出てきたメニューでExport CHOPを選ぶと、こちらもOSCで操作できるようになります。
あとはOut TOPに繋いでkantanMapperに繋いで出力します。

## kantanMapper

kantanMapperの記事はいくつかあったのでそちらを参照していただいた方がわかりやすいと思います。
私も参考にさせていただきました。ありがとうございました。

https://qiita.com/aokey/items/d6852c11fea25888c206
https://zenn.dev/miwa_maroon/articles/2f35259090b916
https://note.com/bunkeidatte/n/nf27d0c08448e

## 最後に

ひとまずこれで、OSC信号を受信してTDで映像を出すシリーズを終えたいと思います。
おそらく経験者の方にとっては「記事にする必要ある？」という感じだと思いますし、初めて触る方には「説明が足りない！」という感じだと思いますが、個人的には色々学びがありました。
今後複数台で出力できるのか等、やってみたいです。
テストケースの優先順位付けの方法

# なぜテストケースの優先順位付けができると嬉しいか
- テストはテストケースを用いて実施する。テストケースには、入力と出力を定義するだろう。
- 例として、入力に10種類の変数があり、それぞれは2値(TRUE または FALSEのみ)をとるとする。
このとき、全ての変数の値の組み合わせは2の10乗すなわち1024通りをとる。
- テスト自動化などの工夫が困難な場面では、全ての変数の値の組み合わせをテストするのは難しくなる。そのため、優先度の高いテストケースを選択することが必要となる。
- それでは、どのようにしてテストケースの優先度付けをするのか？

方法は2つある。
1. リスクベースドテスト
2. 要件ベースドテスト

1.リスクベースドテストの方法は、拙文だが以下にて紹介済みなので、ここでは割愛させていただく。
ここでは、2.を主に紹介する。

https://qiita.com/MaiTana/items/d8142a0507a5f7fa1cc6

# 要件ベースドテスト

- 要件ベースドテストとは、要件をどのぐらい網羅しているかに基づきテストケースを作成し、優先度付けするテスト。すなわち、テストケースや条件、データはシステムの要件から抽出される。そのもとで、品質リスクの高い要件やビジネス上重要な要件と紐づいたテストケースに高い優先順位を割り当てる、などを行う。

## 方法
方法は1つではないが、一例をあげる。
以下の3ステップを行う。
1.要件の妥当性を確認する
2.テスト条件を分析する
3.原因結果グラフからテストケースを作成する


### 1.要件の妥当性を確認する
- 前提として、要件の妥当性を確認しておくことが必要。つまり、曖昧でないか、プロジェクトの目的と整合しているかなどを確認する
- そのための手法が**曖昧性レビュー**
これは、よくある要件の欠陥をまとめたチェックリストを使うことで、要件に潜む曖昧な記述を発見し、取り除くという手法。
- 曖昧な記述の例を以下に挙げる。このような点が見つかったら、まずは修正が必要。
  - プロジェクトの目標が定義されていない	
  - プロジェクトの目標にとって不要な要件がある/必要な要件が漏れている			
  - 重要なステークホルダが要件の定義に参加していない			
  - 非機能要件が抜けている	
  - 要件の記述に使われる言葉の意味が曖昧である。記述に対して解釈が複数通りありうる。	
  - 要件の詳細が書かれていない	
  - 要件同士が矛盾している	
  
### 2.テスト条件を分析する
- 要件の定義されたドキュメントを読み、テスト条件を決定する。
- すでに要件に優先度がついているならば、それに沿ってテスト条件とそこから作成されるテストケースの優先度を定義する。		
- 優先度がついていない場合は、要件の重要度をつける。この時にリスクベースドテストを併用するとよい。

### 3.原因結果グラフからテストケースを作成する

- **原因結果グラフ**とは、**デシジョンテーブル**を作成する補助的に使うテスト技法。デシジョンテーブルとは、お互いに作用する変数同士の値の組み合わせを網羅するための表である。デシジョンテーブルでは変数の値の組み合わせの数が膨大になるため、テストケースの意図が分からなくなる場合がある。その時、原因結果グラフを作成し、それをもとにデシジョンテーブルを生成することで組み合わせの数を減らすことができる。
 
- 原因結果グラフの構成要素
    - 	ノードとリンクからなる。ノードには、原因ノード、結果ノードがある。
さらに、制約という要素もある。
以下の画像は筆者が作成した原因結果グラフ。電子マネーしか使えない自動販売機で、飲料を買うという要件を表現してみた。(非現実的な設定だが..)

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1081780/125d1d14-d723-f27b-6f7f-eaae88a5c3bd.png)


- 作成方法	
簡単に説明する。もっと具体的な方法を知りたい方は[こちら](https://www.jasst.jp/archives/jasst10t/pdf/s2.pdf	
)が参考になる。
1. 要件書などのドキュメントから、動作の原因と結果の因果関係を抽出する。
2. 複数の原因を論理式で結び付けた結果を結果ノードに記載する。(論理式とは、AND, OR, など)
3. さらに、それらに制約を付加する。制約の例としては、REQという前提条件を定義する制約やONEという排他的論理和の制約がある。
4. 最後に、デシジョンテーブルを作成する。
このデシジョンテーブルから、テストケースを抽出する。
原因結果グラフをデシジョンテーブルに簡単に変換するために、原因結果グラフの作成には[**CEGTest**](https://softest.jp/tools/CEGTest/)という無料で公開されているツールを使うのがよい。(手動でやるのは困難)	

  以下の画像は筆者がCEGTestを用いて原因結果グラフとデシジョンテーブルを作成した例。
    
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1081780/a67508f2-7bcd-715f-403f-e8a9fc559c27.png)		


 ※原因結果グラフは、「世界で最も難しいテスト技法」らしい。
『ソフトウェアテスト技法ドリル【第2版】テスト設計の考え方と実際』より


# 参考資料
曖昧性レビュー

https://artelogic.net/blog/7-common-mistakes-in-software-requirements-specification/

https://qatestlab.com/resources/knowledge-center/3-types-of-frequent-errors-in-software-requirements-specifications/

https://enfocussolutions.com/5-common-requirement-mistakes/

『ソフトウェアテスト技法ドリル【第2版】テスト設計の考え方と実際』

https://www.juse-p.co.jp/products/view/934
allow(Kernel).to receive(:sleep)でsleepメソッドをmockできない場合の対処法

# 解決したいこと

RSpecでテストを実行するとき、実装に含まれている`sleep`メソッドで待機時間が発生し、テストの実行時間が増大してしまうので対処したい。

## 具体例

`Sample#test`についてテストする例。


```lib/sample.rb
class Sample
  def test
    sleep 10
  end
end
```

sleepメソッドはKerenlクラスのメソッドなので、Kernelクラスでmockしようとしてみる。

https://docs.ruby-lang.org/ja/latest/method/Kernel/m/sleep.html

```spec/lib/sample_spec.rb
require_relative '../../lib/sample'

describe Sample do
  it 'not to sleep' do
    allow(Kernel).to receive(:sleep)

    expect { sample.test }.to_not raise_error
  end
end
```

しかし、テストを実行してみると、`Finished in 10.01 seconds`と時間がかかる。

```rb
$ bundle exec rspec spec/lib/sample_spec.rb
.

Finished in 10.01 seconds (files took 0.06606 seconds to load)
1 example, 0 failures
```

## 解決方法

Sampleのインスタンスに対して`allow`で振る舞いを教える。

```spec/lib/sample_spec.rb
require_relative '../../lib/sample'

describe Sample do
  it 'not to sleep' do
    sample = Sample.new
    allow(sample).to receive(:sleep)

    expect { sample.test }.to_not raise_error
  end
end
```

今度はテストを実行してみると、`inished in 0.00965 seconds`とすぐに完了した。

```sh
$ bundle exec rspec spec/lib/sample_spec.rb
.

Finished in 0.00965 seconds (files took 0.05652 seconds to load)
1 example, 0 failures
```

## sleepメソッドの振る舞いを上書きする

完全にはsleepさせたくない場合に、`and_wrap_original`で振る舞いを変更できる。

https://www.rubydoc.info/github/rspec/rspec-mocks/RSpec%2FMocks%2FMessageExpectation:and_wrap_original

```spec/lib/sample_spec.rb

require_relative '../../lib/sample'

describe Object do
  it 'not to sleep' do
    sample = Sample.new
    allow(sample).to receive(:sleep).and_wrap_original { |method, _| method.call(0.5) }

    expect { sample.test }.to_not raise_error
  end
end
```

上記の例では、sleepメソッドに渡す引数が何であっても0.5秒待つようになる。実行してみると、約0.5秒かかっていることがわかる。

```sh
% bundle exec rspec spec/lib/sample_spec.rb
.

Finished in 0.50638 seconds (files took 0.05306 seconds to load)
1 example, 0 failures
```

# なぜこれでmockできるのか？（調査中）

調査次第追記します。

# 参考元

こちらの記事を参考にさせていただきました！

https://qiita.com/k-yamada-github/items/f60d15aece1ca7c6b246
zio-httpの@@について

# はじめに

`zio-http`は、ZIOベースのHTTPライブラリです。

ルートにミドルウェア設定を適用するためには、`@@`演算子を使います。
この記事では、以下のサンプルコードを使って、この`@@`演算子の使い方を解説します。

# サンプルコード

```scala
import zio._
import zio.http._

object Main extends ZIOAppDefault {
  val app: Routes[Any, Response] = Routes(
    Method.GET / "text" -> handler {
      for {
        _ <- ZIO.sleep(Duration.fromSeconds(1))
      } yield Response.text("Hello World!")
    }
  )

  val middlewares = Middleware.timeout(Duration.fromSeconds(2))

  val run = Server.serve(app @@ middlewares).provide(Server.default)
}
```

# コード解説

### ルートの定義

まず、シンプルなルートを定義しています。このルートは、`/text`への`GET`リクエストに対して1秒待ってから"Hello World!"というテキストを返します。

```scala
val app: Routes[Any, Response] = Routes(
  Method.GET / "text" -> handler {
    for {
      _ <- ZIO.sleep(Duration.fromSeconds(1))
    } yield Response.text("Hello World!")
  }
)
```

### ミドルウェアの定義

次に、タイムアウトのミドルウェアを定義しています。このミドルウェアは、リクエストが2秒以内に完了しない場合にタイムアウトエラーを返します。

```scala
val middlewares = Middleware.timeout(Duration.fromSeconds(2))
```

### `@@`演算子の使用

最後に、`@@`演算子を使って、定義したルートにミドルウェアを適用しています。これにより、`app`のすべてのルートがミドルウェアの影響を受けることになります。

```scala
val run = Server.serve(app @@ middlewares).provide(Server.default)
```

### `@@`演算子の詳細

`@@`演算子は一見すると何をしているのか分かりにくいかもしれませんが、実態はシンプルで、
`Routes`クラスの`@@`メソッドを見てみると、ミドルウェアの`apply`メソッドを呼び出しているだけです。

### `Routes`の実装

`Routes`クラスは、複数のルートを含むデータ構造です。`@@`演算子は、ミドルウェアをルートに適用するために使われます。

```scala
// ZIOの実装から抜粋
final case class Routes[-Env, +Err](routes: Chunk[zio.http.Route[Env, Err]]) { self =>
  private var _tree: Routes.Tree[_] = null.asInstanceOf[Routes.Tree[_]]

  def @@[Env1 <: Env](aspect: Middleware[Env1]): Routes[Env1, Err] =
    aspect(self)
}
```

### `Middleware`の実装

`Middleware`は、リクエストを処理する前後に追加のロジックを挟むための抽象クラスです。例えば、`timeout`は、指定した時間内にリクエストが完了しなければタイムアウトエラーを返します。

```scala
// ZIOの実装から抜粋
object Middleware extends HandlerAspects {
  def timeout(duration: Duration)(implicit trace: Trace): Middleware[Any] =
    new Middleware[Any] {
      def apply[Env1 <: Any, Err](routes: Routes[Env1, Err]): Routes[Env1, Err] =
        routes.transform[Env1] { handler =>
          handler.timeoutFail(Response(status = Status.RequestTimeout))(duration)
        }
    }
}
```

`@@`演算子の背後では、`routes.transform`メソッドが呼び出され、各ハンドラにミドルウェアが適用されているだけです。
このシンプルな仕組みにより、`@@`演算子は直感的かつ柔軟にミドルウェアを適用することができます。

# まとめ

`zio-http`の`@@`演算子を使うことで、ルートに対して簡単にミドルウェアを適用できます。
内部的には、単に`routes.transform`を呼び出しているだけでした。
C_TS452_2021 - SAP Certified Application Associate - SAP S/4HANA Sourcing and Procurement対策（11/100）

【11日目】
トピック1：企業構造とマスタデータ（11/11）

≪問題11≫
企業構造のカスタマイジングで実行できる購買組織割当はどれですか
（３つ選択）
□１つの購買組織を複数の会社コードに割り当てる。
□１つの購買組織を１つの会社コードに割り当てる。
□複数の購買組織を１つの会社コードに割り当てる。
□複数の購買組織を１つの購買グループに割り当てる。
□１つの購買組織を複数の会社コードにわたる複数のプラントに割り当てる。

≪解答≫
□１つの購買組織を複数の会社コードに割り当てる。
■１つの購買組織を１つの会社コードに割り当てる。
■複数の購買組織を１つの会社コードに割り当てる。
□複数の購買組織を１つの購買グループに割り当てる。
■１つの購買組織を複数の会社コードにわたる複数のプラントに割り当てる。

≪解説≫
◆購買グループは、特定の購買活動に関する責任を負う購買担当者または購買担当グループを表しており、企業構造の他の組織には割り当てません。
◆購買組織は、購買要件に応じて企業を細分化するロジスティクス内の組織ユニットであり、品目やサービスの調達、および仕入先との購買条件の交渉を担当します。会社構造に対する購買の組織的統合は、購買組織を以下に割り当てることで実現されます。
（1）1つの会社コード（任意）
（2）1つまたは複数のプラント（必須）
◆適切な割当を使用すると、購買が会社内で集中型または分散型のどちらで編成されているかを表現できます。購買組織が複数の会社コードにわたって活動する場合（分散型/戦略的購買）、会社コードと購買組織の間に割当は存在せず、すべての会社コードのプラントを購買組織からプラントへの割当で使用することができます。
◆購買組織が会社コードに割り当てられている場合、選択した会社コードのプラントのみを購買組織からプラントへの割当で使用することができます。また、複数の購買組織を1つの会社コードに割り当てることも可能です。
◆購買組織とプラントは、多対多の関係になります。つまり、複数のプラントを1つの購買組織に割り当てることも、1つのプラントを複数の購買組織に割り当てることも可能です。また、購買組織とプラント間の割当は必要な設定であり、省略することはできません。
◆会社コード、プラント、購買組織の間のさまざまな割当オプションにより、購買組織に以下のようなカテゴリが生じます。
（1）プラント購買組織：1つの購買組織が1つのプラントだけの品目調達を担当します。
	-購買組織を1つの会社コードに割り当てます。-購買組織に対して1つのプラントだけを割り当てます。
（2）プラント共通購買組織：1つの購買組織が1つの会社コードに属する複数のプラントに対する品目調達を担当します。
	-購買組織を1つの会社コードに割り当てます。-購買組織に対して1つの会社コードに属する複数のプラントを割り当てます。
（3）会社間共通購買組織：1つの購買組織が複数の会社コードに属する複数のプラントに対する品目調達を担当します。
	-購買組織に対する会社コードの割り当ては行いません。
	-購買組織に対して複数の会社コードに属する複数のプラントを割り当てます。
MacにGitをインストールする


以下に、前提条件を含めたGitのインストール手順をまとめました。

# 前提条件
・macOS: Sonoma 14.5（実施環境）
・Gitのバージョン: 最新版をインストールすることを推奨します。
・Homebrew: インストール済み


# 1.Gitをインストールします。

```bash
brew install git
```
# 2. Gitのインストールを確認
インストールが完了したら、以下のコマンドでGitのバージョンを確認できます。

```bash
git --version
```
# 3. Gitの初期設定
初めてGitを使用する場合、以下のコマンドでユーザー名とメールアドレスを設定してください。

```bash
git config --global user.name "Your Name"
git config --global user.email "your.email@example.com"
```
これでMacにGitがインストールされ、使用できる状態になりました！
作成したプロジェクトをGitHubにアップロードする方法


# 前提条件
・Gitがインストールされていること
・GitHubアカウントがあること
・GitHub上でリポジトリを作成済みであること

# 主な手順
・1. プロジェクトディレクトリに移動
・2. Gitリポジトリを初期化
・3. プロジェクトファイルの追加
・4. 最初のコミットを作成
・5. リモートリポジトリを追加
・6. 変更をプッシュ

# 1. プロジェクトディレクトリに移動
まず、プロジェクトディレクトリに移動する。

```bash
cd 作成したプロジェクト
```

# 2. Gitリポジトリを初期化
プロジェクトディレクトリでGitリポジトリを初期化する。（最初のみ）
```bash
git init
```


# 3. プロジェクトファイルの追加
すべてのプロジェクトファイルの追加する。
```bash
git add .
```

# 4. 最初のコミットを作成
変更をコミットします。
```bash
git commit -m "Initial commit"
```

# 5. リモートリポジトリを追加
GitHubで作成したリポジトリのURLを使用して、リモートリポジトリを追加する。

```bash
git remote add origin URL.git
```

（URLは実際のリポジトリのURLに置き換えてください。）

# 6. 変更をプッシュ
リモートリポジトリに変更をプッシュする。

```
git push -u origin master
```

（Gitのバージョンによっては、masterの代わりにmainを使用することがあるので注意してください。）

# まとめ
この手順で、作成したプロジェクトを簡単にGitHubにアップロードすることができる。
C_TS452_2021 - SAP Certified Application Associate - SAP S/4HANA Sourcing and Procurement対策（10/100）

【10日目】
トピック1：企業構造とマスタデータ（10/11）

≪問題10≫
仕入先の取引先マスタレコードを更新する必要がある組織レベルはどれですか。
（３つ選択）
□会社コード
□クライアント
□プラント
□購買組織
□購買グループ

≪解答≫
■会社コード
■クライアント
□プラント
■購買組織
□購買グループ

≪解説≫
◆仕入先（ビジネスパートナーロールである"仕入先"と"FI仕入先"）に関連するビジネスパートナーデータは、以下のカテゴリに分類されます。
（1）一般データ（クライアントレベル）：一般データは、単一のクライアントで有効です。これには、仕入先の住所、管理データ、銀行情報、担当者などのデータが含まれます。
（2）会計データ（会社コードレベル）：会計データは会社コードレベルで管理されます。このデータには、統制勘定コード、連絡文書、源泉徴収税、自動支払処理で使用する支払方法などが含まれます。
（3）購買データ（購買組織レベル）：購買データは購買組織レベルで管理されます。このデータには、購買発注通貨、インコタームズ、取引先機能、購買データ、仕入先に関連する各種制御などが含まれます。特定のプラントや仕入先部門ごとに異なる購買データを保持することも可能です。
Macでetcdをローカルで動かしてCRUD操作を行う

# 概要

この記事では、macOSでetcdをローカル環境で動かし、
etcdctlコマンドを使用してレコードのCRUD（作成、読み取り、更新、削除）操作を行う方法を紹介します。

# ステップ1: etcdのインストール

まず、Homebrewを使用してetcdをインストールします。ターミナルを開いて以下のコマンドを実行します。

```bash
brew install etcd
```

# ステップ2: etcdの起動

次に、etcdをローカルで起動します。デフォルトの設定を使用して、以下のコマンドを実行します。

```bash
etcd
```

以下のような出力が表示され、etcdが正常に起動していることを確認します。

```text
{"level":"info","ts":"2024-07-13T16:21:29.728007+0900","caller":"etcdmain/etcd.go:116","msg":"server has been already initialized","data-dir":"default.etcd","dir-type":"member"}
```

# ステップ3: etcdctlを使用してレコードをCRUDする

#### レコードの作成（Create）

以下のコマンドを実行して、`example.com`のレコードを作成します。

```bash
$ etcdctl put /skydns/com/example/www '{"host": "192.168.1.100"}'
OK
```

#### レコードの読み取り（Read）

以下のコマンドを実行して、`example.com`のレコードを読み取ります。

```bash
$ etcdctl get /skydns/com/example/www
/skydns/com/example/www
{"host": "192.168.1.100"}
```

#### レコードの更新（Update）

以下のコマンドを実行して、`example.com`のレコードを更新します。

```bash
$ etcdctl put /skydns/com/example/www '{"host": "192.168.1.101"}'
OK
```

#### レコードの削除（Delete）

以下のコマンドを実行して、`example.com`のレコードを削除します。

```bash
$ etcdctl del /skydns/com/example/www
1
```

# ステップ4: レコードの確認

CRUD操作が正しく行われたことを確認するために、各操作後に以下のコマンドを実行して結果を確認します。

```bash
$ etcdctl get /skydns/com/example/www
```

結果は何も標準出力されないはずです。

# まとめ

以上の手順で、macOSでetcdをローカル環境で動かし、etcdctlコマンドを使用してレコードのCRUD操作を行うことができました。

- etcd Documentation: [etcd Documentation](https://etcd.io/docs/)
Core DNSをDockerで立ち上げてレコードを登録する

# 概要

この記事では、CoreDNSをDockerコンテナ上で起動し、digコマンドで登録されたDNSレコードを確認する方法を紹介します。

# ステップ1: CoreDNSの設定ファイルを作成する

まず、CoreDNSの設定ファイル（`Corefile`）を作成します。このファイルには、DNSサーバーの設定とホストするレコードが含まれます。以下は基本的な設定例です。

```text
.:1053 {
    forward . 8.8.8.8
    log
    errors
}
example.com:1053 {
    hosts {
        127.0.0.1 localhost
        192.168.1.100 www.example.com
        fallthrough
    }
    log
}
```

この設定では、`example.com`ドメインのDNSクエリを処理し、指定されたIPアドレスにマッピングします。また、すべての他のクエリはGoogleのパブリックDNS（8.8.8.8）にフォワードされます。

# ステップ2: DockerコンテナでCoreDNSを起動する

設定ファイルが準備できたら、Dockerコンテナを使用してCoreDNSを起動します。以下のコマンドを実行します。

```bash
docker run -d -p 1053:1053/udp -p 1053:1053/tcp -v $(pwd)/Corefile:/Corefile coredns/coredns:latest -conf /Corefile
```

このコマンドは、ローカルの1053番ポートをコンテナの1053番ポートにマッピングし、設定ファイルをコンテナ内の`/Corefile`にマウントします。

# ステップ3: DNSレコードの確認

CoreDNSが正常に起動したかを確認するために、DNSクエリを実行して登録したレコードを確認します。以下のコマンドを実行します。

```bash
dig @127.0.0.1 -p 1053 www.example.com
```

このコマンドの出力が以下のようになれば、設定が正しく反映されています。

```text
; <<>> DiG 9.10.6 <<>> @127.0.0.1 -p 1053 www.example.com
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 453
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;www.example.com.		IN	A

;; ANSWER SECTION:
www.example.com.	3600	IN	A	192.168.1.100

;; Query time: 2 msec
;; SERVER: 127.0.0.1#1053(127.0.0.1)
;; WHEN: Sat Jul 13 15:40:04 JST 2024
;; MSG SIZE  rcvd: 75
```

# まとめ

以上の手順で、CoreDNSをDocker上で起動し、基本的なDNSレコードを登録することができました。`dig`コマンドを使用することで、特定のポートに対してDNSクエリを送信し、設定を確認することができます。

公式ドキュメント: [CoreDNS Documentation](https://coredns.io/manual/toc/)
rbenvでRubyのバージョンが更新されないときの対処法

# はじめに
Rubyのバージョン管理で直面した問題について共有します。

# 問題
問題の背景は、プロジェクトでRubyのバージョン`2.7` , `3.3`を必要としていて、現在使用している Ruby のバージョン `2.6.5` をアップグレードする必要がありました。

このアップグレードで直面した主な問題は、`rbenv` を使用して新しいRubyのバージョンをグローバルに設定しても、期待通りのバージョンがシステムに反映されなかったことです。

```zsh
# Ruby 2.7.2 をインストール
rbenv install 2.7.2

# インストール後の Ruby バージョンを確認
ruby -v
# 出力: ruby 2.6.5
```

# 解決方法
以下の手順で解決していきます。

## 1. 現在のRubyバージョン状況の確認
`rbenv` が管理しているRubyのバージョンと現在使用されているバージョンを確認します。`rbenv versions` を実行して、Rubyバージョンが更新されない原因が分かってきました。

```zsh
# 現在インストールされているRubyのバージョンを表示
rbenv versions
# 出力例:
#   system
# * 2.6.5 (set by /Users/あなたのユーザー名/.ruby-version)
#   2.7.2
```

## 2. .ruby-versionファイルの確認と削除

```zsh
# .ruby-version ファイルの内容を確認
cat ~/.ruby-version
# 出力：2.6.5

# ファイルが不要であれば削除
rm ~/.ruby-version
```

## 3. `rbenv rehash` コマンドの実行
rbenvの設定を更新し、新しい設定が適用されるようにします。

```zsh
rbenv rehash
```

## 4. システム設定の再読込み
zshを使用している場合は、以下のコマンドで全ての変更をシステムに適用します。

```zsh
source ~/.zshrc
```

## 5. 更新後のバージョン確認
全ての変更を適用した後、正しいバージョンの Ruby が使用されていることを確認します。

```zsh
# 現在の Ruby バージョンを確認
ruby -v
# 期待される出力：Ruby 2.7.2
```

# 原因
この問題の根本原因は、**`~/.ruby-version` ファイルによるグローバル設定の上書きです。** `rbenv` はこのファイルの指示に従い、グローバル設定よりも優先して特定の Ruby バージョンを使用します。この挙動は便利な場合もありますが、意図しないバージョンの使用を引き起こすことがあります。

# おわりに
独学初期にrbenvを使用して適当にRubyをインストールしていたのを思い出しました。今回の記事を通じて、rbenvを使ってRubyのバージョン管理について、少し理解できたのでこの問題を解決する過程で得たコマンドなど、知識を活かしていきます。
qiita-cliで記事を投稿する


# qiita-cli で記事を投稿する

## はじめに

Qiita に記事を投稿する際に、ブラウザを使わずにコマンドラインから簡単に記事を投稿できる方法として、`qiita-cli`があります。本記事では、`qiita-cli`を使って記事を投稿する手順を紹介します。

## 前提条件

- Node.js がインストールされていること
- Qiita の API トークンを取得していること

## インストール

まず、`qiita-cli`をインストールします。以下のコマンドを実行してください。

```bash
npm install -g qiita-cli
```
C_TS452_2021 - SAP Certified Application Associate - SAP S/4HANA Sourcing and Procurement対策（9/100）

【9日目】
トピック1：企業構造とマスタデータ（9/11）

≪問題9≫
購買組織の定義にあたって考慮が必要なことは何ですか。
（２つ選択）
□購買組織ごとに独自の購買グループを持つこと。
□購買組織ごとに独自の品目マスタデータを持つこと。
□購買組織ごとに独自の仕入先マスタデータを持つこと。
□購買組織ごとに独自の購買情報を持つこと。

≪解答≫
□購買組織ごとに独自の購買グループを持つこと。
□購買組織ごとに独自の品目マスタデータを持つこと。
■購買組織ごとに独自の仕入先マスタデータを持つこと。
■購買組織ごとに独自の購買情報を持つこと。

≪解説≫
◆購買組織とは、企業の購買要件に応じて細分化されるロジスティクス内の組織単位でございます。購買組織は、品目やサービスの調達および仕入先との購買条件の交渉を担当し、これらの取引に関する責任を負います。
◆購買グループは、特定の購買活動に関する責任を持つ購買担当者または購買担当グループを表しますが、購買組織やプラントなどの組織単位に割り当てられることはございません。購買グループは、プラントレベルで個々の品目に割り当てられます。
◆仕入先マスタデータは、一般データ（クライアントレベル）、会計データ（会社コードレベル）、購買データ（購買組織レベル）で構成されます。
◆購買情報データは、クライアントレベルの一般データと購買組織固有のデータに分類されます。
◆品目マスタデータは、さまざまな組織レベルで管理されていますが、購買管理データはプラントレベルで管理されており、購買組織レベルでは管理されておりません。
pipでコマンドをインストールしようとすると「venvしろって！」怒られてうざい人向けのtips

# TLDR;
単発のコマンドラインツールをpipで入れたい時は`pipx`か`Docker`を使うと良い．

# 何
いつからかpipで公開されている便利コマンドを入れようとすると，「OS標準のPython環境を壊すな！」って言われる．


```
$ pip3 install rfcbibtex
error: externally-managed-environment

× This environment is externally managed
╰─> To install Python packages system-wide, try brew install
    xyz, where xyz is the package you are trying to
    install.

    If you wish to install a Python library that isn't in Homebrew,
    use a virtual environment:

    python3 -m venv path/to/venv
    source path/to/venv/bin/activate
    python3 -m pip install xyz
...
```


ちょっとコマンド一つ入れるために言われた通りにvenvするのも面倒くさい

# 手法

## pipxで入れる
コマンドラインツールを分離環境でインストールするためのツールがあった．

https://pipx.pypa.io/stable/installation/

pipxをbrewでインストール
```
brew install pipx
```

使いたいコマンドをインストール
```
$ pipx install rfcbibtex
  installed package rfcbibtex 0.3.2, installed using Python 3.12.4
  These apps are now globally available
    - rfcbibtex
⚠️  Note: '/Users/username/.local/bin' is not on your PATH environment
    variable. These apps will not be globally accessible until your PATH is
    updated. Run `pipx ensurepath` to automatically add it, or manually modify
    your PATH in your shell's config file (e.g. ~/.bashrc).
done! ✨ 🌟 ✨
```

利用中のshellに`~/.local/bin`のパスを追加するコマンドがある．
```bash
$ pipx ensurepath
```



## Dockerで入れよう
pipxも入れたくない！ / より厳密に実行環境を分離したい人向け．

### 1. 使いたいコマンドだけのためにDocker imageを作る
- Dockerfileを書く
```Dockerfile
FROM python:3.11.9-slim-bookworm

# 作業ディレクトリを設定
WORKDIR /app

# 必要なPythonパッケージをインストール
RUN pip install rfcbibtex

# デフォルトのコマンドを設定
ENTRYPOINT ["rfcbibtex"]

```

buildする．ここではイメージ名はコマンド名とする．
```
docker build -t rfcbibtex .
```

### 2. shellにaliaseを書く
- zsh版/bash版
```zsh:.zshrc
alias rfcbibtex='docker run --rm rfcbibtex'
```
- fish版
```fish:config.fish
alias rfcbibtex 'docker run --rm rfcbibtex'
```


### 3. 実行する
パイプもできるよ
```bash:
$ rfcbibtex rfc9200 | pbcopy
```