OmniAICreator/Qiita-1.07M huggingface dataset:
npm / node.js – package.json の scripts ( npm run xxx ) の挙動が謎 - コマンド引数とかサブコマンドとか

# 結論

scripts は「 npm run xxx 」 より後に指定したコマンド引数を受け取って、コマンド末尾で処理しているように見える

# 標準出力させる例 (1)

package.json に標準出力するだけの scripts を書いておく

```json
{
  "scripts": {
    "echo": "echo"
  }
}

```

## コマンド実行例

- npm コマンド例 `npm run echo ABC`
- scriptとして実行されるコマンド `echo ABC`


`npm run echo` に渡した引数 ( `ABC` ) を echo コマンドがそのまま受け取るようだ

# 標準出力する例 (2)

package.json を以下のようにして、コマンド実行を明示的に終わらせるとどうなるだろう
( コマンド末尾に `;` を追加  )

```json
{
  "scripts": {
    "echo": "echo;"
  }
}
```

### 引数を渡して実行する

npm run に渡した引数 ( `ABC` ) が次のコマンドとして評価され、実行されるようだ

- npm コマンド例 `npm run echo ABC`
- scriptとして実行されるコマンド `echo; ABC`

実行結果:

`sh: ABC: command not found`

### 引数にコマンドを渡してみる

- npm コマンド例 `npm run echo ls some_dir`
- scriptとして実行されるコマンド `echo; ls some_dir`

ls コマンドと引数 ( 対象ディレクトリ ) を渡して、 ls コマンドが実行できてしまった

### フラグを渡してみる

- npm コマンド例 `npm run echo ls -la`
- scriptとして実行されるコマンド `echo; ls`

フラグは渡せないようだ

# 環境

- node v20.11.0



# チャットメンバー募集


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

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


# プロフィール・経歴

https://github.com/YumaInaura/YumaInaura
MacでTerminalコマンドが終了したら、通知センターに”終了”を知らせる方法

# 概要
- コマンドが終了した際に視認性の高い状態変化を表示する方法として、通知センターに表示する方法を思いつきました。
- それはAppleScriptを叩くコマンドを用意することでした。
# 目的
- OS X（macOS）のTerminalで長い計算時間のコマンドを走らせている時、終了したかどうかを知りたい。

## 方法
- 通知センターにAppleScriptで表示するコマンドを用意します(例：showend)。
- 終了時にこのコマンドが発行されるようにします。


## code
```bash
$ cat showend 
#!/usr/bin/bash
osascript -e 'display notification "コマンドが終了しました" with title "終了通知"'
$ chmod +x showend
```

## 実行方法
```bash
$ your_command; showend
```

## 応用
- コマンドが正常終了か、エラー終了かで切り替えることはできるでしょう。`stderr`の値で切り替えればいいでしょうから簡単です。
- 例えば、フラグを読み込んで表示内容を切り替えればいいです。具体的には生成AIにでも聞いて下さい。


```bash
# 例えばフラグで切り替え
$ your_command && showend -T || showend -F
```


アイディアだけですが、どなたかのお役に立てれば嬉しいです。
Vimを覚える【vimtutor Lesson 1】

# はじめに

今までは[VIMATE](https://vimate.jp/)でコマンドを覚えてきましたが、実践できないのが難点でした。

<font color="LightGreen">「実際に動いているところ」を見たい！</font>ということで

Vimの公式チュートリアル「<font color="LightGreen">vimtutor</font>」をやってみます！

Vimに憧れがあるけど触れないそこのキミ！簡単だから一緒にやってみないか！？

# 始め方

## Vimのインストール

Macであればインストールは不要だったはずです。

Windowsであればvimをインストールする必要があります。今回は割愛。

## ターミナルで起動

ターミナルにて、`vimtutor`と入力し、実行するだけです！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3457770/6c47c9c3-13bd-d36a-1303-1b3e7c4b5297.png)
# Lesson 1.1

最初に教わるのは、「カーソルの動かし方」です。

ターミナルのウィンドウサイズによっては指示が見切れてるかも？

<font color="LightGreen">`hjkl`でカーソルが動きます。</font>

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3457770/e422b137-2262-d387-d0c0-b6dc1bec0f64.png)

<font color="LightGreen">実際に動いてる！これがやりたかった！！</font>テンションが上がります。

# Lesson 1.2

<font color="LightGreen">`q!` を押すと終了するよ！！`vimtutor`でまた起動できるよ！</font>

だけです！！

# Lesson 1.3

<font color="LightGreen">`x`で文字を消せるよ！</font>ですね。`Backspace`の操作にあたります。

例文で練習もできます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3457770/5a4c4660-a078-f8fb-621a-073555c40ecb.png)
# Lesson 1.4

<font color="LightGreen">`i`で「insert（挿入）モード」！</font>ですね。

insert（挿入）モードというのは、文字の入力ができるモードのことです。

こちらも例文があります。上の文に文字を挿入して、下の文にしようということですね。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3457770/c42284d7-ec3b-399a-a4bf-bd9a66cade0c.png)


# Lesson 1.5

`a`で「append（追加）モード」です。

append（追加）モードというのは、ほぼ insert（挿入）モードと同じです。

違うのは、「カーソルの後ろに文字を入力する（追加する）」ということです。
細かいですね。

特に最初は、どちらかだけ覚えれば十分そうです。

# Lesson 1.6

`:wq`で「保存して終了」ですね。

終了系コマンドをまとめておきましょう。

| コマンド | 機能 |
| ---- | ---- |
| `:q` | （変更がなければ）終了 |
| `:q!` | （強制的に）終了<br>`!`は`強制`の意味 |
| `:wq` | （変更を保存して）終了 |
# Lesson 1 修了！

おつかれさまでした！！

# おわりに

簡単な内容でしたが、実際に動かしたら一気に理解できた感じ！

Lesson 7まであるようなので、引き続き記事にしていきます～！

次回の記事

https://qiita.com/kohki_takatama/items/5d7e24debd014df7241b
今更だが、Gemini APIを無料で発行して使ってみよう

右も左もわからずの自分がやってみて面白かったとか、備忘録とかにした方がいいと思った物を記事にしたものなります。もし、必要な方にいらっしゃったら、この記事が役に立てたら嬉しいと思います。
* * *

## なんだと？Gemini APIが無料？
OpenAI社のChatGPTのAPIは初回限定かつ、期限ありで２０ドルの枠まで使えたことに対し、GoogleのGeminiはいくつかの制限はあるが無料で使えると発表していた。これを用いれば自分のおもちゃ用ボットとかも作れるわけなので、早速APIしないと思った。

## 無料の枠でできること
以下のイメージを参照
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3714030/a27c8968-22f8-db2d-891b-b779986c1e9b.png)


## まず、Gemini APIのkeyを発行しよう
以下のページからGemini ProのAPI Keyの申し込みができる
[Gemini Pro APIの申請ページ](https://aistudio.google.com/app/prompts/new_chat "Gemini Pro API申請")

「Get API key」を押下し、API発行まで進めば完了です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3714030/ed99e60b-4d3e-bf5b-2c7d-0a48201923cb.png)

## pythonでやり取り開始
API keyが発行完了したら、すぐpythonを用いて会話ができる。
GenerativeModelに「gemini-pro」を指定したら、テキストベースのみに、「gemini-pro-vision」にすると、イメージなどがインプットができるようになる
→ 「gemini-pro-vision」の場合、model.generate_content()を使用

model.start_chat()からチャットセッションを取得したら会話が続けることができる

チャットプロンプト用はこちら。
```python:main.py
import vertexai
from vertexai.preview.generative_models import GenerativeModel, ChatSession

# TODO(developer): Update and un-comment below lines
# project_id = "PROJECT_ID"
# location = "us-central1"
vertexai.init(project=project_id, location=location)

model = GenerativeModel("gemini-pro")
chat = model.start_chat()

def get_chat_response(chat: ChatSession, prompt: str) -> str:
    response = chat.send_message(prompt)
    return response.text

prompt = "Hello."
print(get_chat_response(chat, prompt))

prompt = "What are all the colors in a rainbow?"
print(get_chat_response(chat, prompt))

prompt = "Why does it appear when it rains?"
print(get_chat_response(chat, prompt))
```

マルチモーダル用はこちら。
```python:main.py
import http.client
import typing
import urllib.request
from vertexai.preview.generative_models import GenerativeModel, Image

# create helper function
def load_image_from_url(image_url: str) -> Image:
    with urllib.request.urlopen(image_url) as response:
        response = typing.cast(http.client.HTTPResponse, response)
        image_bytes = response.read()
    return Image.from_bytes(image_bytes)

# Load images from Cloud Storage URI
landmark1 = load_image_from_url(
    "https://storage.googleapis.com/cloud-samples-data/vertex-ai/llm/prompts/landmark1.png"
)
landmark2 = load_image_from_url(
    "https://storage.googleapis.com/cloud-samples-data/vertex-ai/llm/prompts/landmark2.png"
)
landmark3 = load_image_from_url(
    "https://storage.googleapis.com/cloud-samples-data/vertex-ai/llm/prompts/landmark3.png"
)

# Pass multimodal prompt
model = GenerativeModel("gemini-pro-vision")
response = model.generate_content(
    [
        landmark1,
        "city: Rome, Landmark: the Colosseum",
        landmark2,
        "city: Beijing, Landmark: Forbidden City",
        landmark3,
    ]
)
print(response)
```

無料の期限（2024年の頭までかな）があると思うが、お金を払わず、トイプロジェクトができることはGoogleに感謝の気持ちでいっぱい。
有料になっても、
テキスト入力　0.00025ドル/1,000文字
テキスト出力　0.0005ドル/1,000文字
になるようだ。
OCI Generative AI Service の Stream 出力を REST API から実施する

# はじめに

OCI Generative AI Service が GA となり、おおよその機能は SDK から使うことができるようになっています。チャット等のインターフェースなどからこれを扱う場合には、ユーザー体験を悪くしないために生成されたテキストを順次クライアントに返却し、その内容をレンダリングするような工夫を一般的にはすると思いますが、SDK では現時点（※2024/02/19）でサポートされていません。しかし、REST API ではサポートされているようなので今回はこれを簡単に試してみます。

# 実装例

```py
import requests
import json
from oci.auth.signers import InstancePrincipalsSecurityTokenSigner

compartment_id = "<your-compartment-id>"
model_ocid = "<model-ocid>"
api_endpoint = "https://inference.generativeai.us-chicago-1.oci.oraclecloud.com"

signer = InstancePrincipalsSecurityTokenSigner()

prompt = "Tell me a joke."
body = {
    "compartmentId": compartment_id,
    "inferenceRequest": {
        "runtimeType": "COHERE",
        "isStream": True,
        "maxTokens": 500,
        "numGenerations": 1,
        "prompt": prompt
    },
    "servingMode": {
        "servingType": "ON_DEMAND",
        "modelId": model_ocid
    }
}

response = requests.post(
    url=api_endpoint + "/20231130/actions/generateText",
    json=body,
    auth=signer,
    stream=True
)

for chunk in response.iter_lines():
    if chunk.strip():
        content = json.loads(chunk.decode('utf-8').split(': ', 1)[1])
        print(content)
```

`inferenceRequest.isStream` に `True` を設定することと、`requests.post` で `stream=True` にするのがポイントです。上記のコードを実行すると、以下のように結果を得ることができます。

```sh
{'id': 'aaa6fc53-c156-4491-bccf-3629e26f6e5b', 'text': ' Why'}
{'id': 'aaa6fc53-c156-4491-bccf-3629e26f6e5b', 'text': ' was'}
{'id': 'aaa6fc53-c156-4491-bccf-3629e26f6e5b', 'text': ' the'}
{'id': 'aaa6fc53-c156-4491-bccf-3629e26f6e5b', 'text': ' computer'}
{'id': 'aaa6fc53-c156-4491-bccf-3629e26f6e5b', 'text': ' cold'}
{'id': 'aaa6fc53-c156-4491-bccf-3629e26f6e5b', 'text': '?'}
{'id': 'aaa6fc53-c156-4491-bccf-3629e26f6e5b', 'text': '\n'}
{'id': 'aaa6fc53-c156-4491-bccf-3629e26f6e5b', 'text': '\n'}
{'id': 'aaa6fc53-c156-4491-bccf-3629e26f6e5b', 'text': 'Because'}
{'id': 'aaa6fc53-c156-4491-bccf-3629e26f6e5b', 'text': ' it'}
{'id': 'aaa6fc53-c156-4491-bccf-3629e26f6e5b', 'text': ' left'}
{'id': 'aaa6fc53-c156-4491-bccf-3629e26f6e5b', 'text': ' its'}
{'id': 'aaa6fc53-c156-4491-bccf-3629e26f6e5b', 'text': ' Windows'}
{'id': 'aaa6fc53-c156-4491-bccf-3629e26f6e5b', 'text': ' open'}
# ...
```

後は、使う側で煮るなり焼くなりできるはずです！

## e.g. Jupyter Notebook 上で Stream 出力する

```py
for chunk in response.iter_lines():
    if chunk.strip():
        content = json.loads(chunk.decode('utf-8').split(': ', 1)[1])
        print(content.get('text', ''), end='')
```

# おわりに

SDK が Stream 出力に対応するまでの暫定対処ですが、ご参考までに。

# 参考

https://docs.oracle.com/en-us/iaas/api/#/en/generative-ai-inference/20231130/GenerateTextResult/GenerateText
throw / throws　

"Throw"の和訳は「投げる」
javaにおいて、
1. `throw`は、意図的に例外を発生させたい場合などに使う
1. `throws`は、メソッドの呼び出した側に例外処理を投げる

参考にしたサイト：
[【Java初心者向け】throwとthrowsの違いとその使い方](https://eng-entrance.com/java-throw-and-throws)

# throw
コード上、入力された値に問題はないが、意図して例外処理を発生させたい場面に使う。
以下は、整数の値である`19`を渡しているので問題はないけれど、if文で20未満の値が入力された場合は例外処理を発生させている。
この際の例外クラスは、「メソッドの引数に不正な引数が渡された場合」に例外を発生させる`IllegalArgumentException`を使用する。
```Java:Throw 実装例
public class Exception_Test5 {

	public static void main(String[] args) {
		// 年齢を表す変数age
		int age = 19;
        // 20未満の整数の場合は例外を発生
		if (age < 20) {
			throw new IllegalArgumentException ( age + "歳の方には非対応です。");
		}
				
	}

}
```

```java:実行結果

Exception in thread "main" java.lang.IllegalArgumentException: 19歳の方には非対応です。
```
ちなみに文字列でもいける。
```java:特定の文字列ABCに対して入力には例外処理
public class Exception_Test5 {

	public static void main(String[] args) {
		// 文字列
		String words = "ABC";
		// ABCという文字列は例外処理
		if (words == "ABC") {
			throw new IllegalArgumentException ( words + "は非対応な文字列です。");
		}
				
	}

}
```
実行結果
![スクリーンショット 2024-02-19 14.23.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3570384/3c98c0e4-fbbb-144f-0264-1f75bd2992e4.png)

# throws
メソッドの呼び出し元で例外処理を行う
>今回の「throws」キーワードをメソッドの定義に追加することにより、例外処理を「そのメソッドの中ではなく、そのメソッドを呼び出した別の箇所」に任せるということが可能になります。
すなわち、「throws」キーワードをつけて定義されたメソッドを呼び出す際には、呼び出す処理を書く部分で例外処理のコードを書かなければならない

引用元[【はじめてのJava】例外を逃れる【例外処理編】](https://tech.pjin.jp/blog/developer/java_13_05_exception_handling_5#subseciton1-2)



**tyr-catch文はその場で(メソッド内で)例外処理を行い、**
**throwsはそのメソッドを呼び出した先で例外処理を行う。**

《実践してみる》
下記通り通常通りメソッドを作成する。
下記で指定しているファイルは存在していないのでエラーになるはずです。
しかし、例外処理は含まれていません。
```Java:ファイルを読み込むメソッド method1を作成する
	static void method1() {
		// Scannerオブジェクトを作成してファイルを読み込むメソッド
		Scanner scanner = new Scanner(new File("test.txt"));
		
		//ファイルの読み込みが続く場合、処理を続ける
		while (scanner.hasNext()) {
			//改行までの入力を受け取る
			String input = scanner.nextLine();
			System.out.println(input);
		}
	}
 ```
 次に、mainメソッドにて、この`method1`を呼び出して使いたいとします。
 その際にはまず、`throws 例外処理のクラス名`を以下画像のようにメソッド名の後ろに追記。
 ![スクリーンショット 2024-02-19 17.15.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3570384/61111690-0972-8024-09e3-6a4983fca68f.png)
 そしてmainメソッドではtry-catch文にて、method1をtryし、例外処理をcatchする。
 ```Java: mainメソッド
 public static void main(String[] args) {
		try {
			method1();
		} catch (FileNotFoundException e) {
                  //ファイルが見つからない場合に行う処理
			System.out.println("ファイルが見つかりません");
		}
	}
```
以下、実行結果
![スクリーンショット 2024-02-19 17.21.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3570384/d78ea6da-b709-0925-f7b9-25e5557eb2ee.png)
きちんと、例外処理が行なわれている。

throw文を追記しない場合は、FileNotFoundExceptionのエラーとなる。
![スクリーンショット 2024-02-19 17.26.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3570384/7b000d10-8d7b-86f0-3822-b75a8c322709.png)
MultiIndexの解除と設定方法（pandas）

# `MultiIndex`って？
MultiIndexとはこんな感じのやつです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3655949/fa2433f5-45ba-9e34-4ea2-a6cbbf72c7d4.png)

これがうっとうしくて、解除方法を調べたので。

# 【解除方法】

`df`の`列名`あるいは`行名`に対して上書きする。以下に2通り示す。
```python
#Indexの場合
df.index = df.index.get_level_values(0)
df.index = df.index.droplevel(1)


#Columnの場合
df.columns = df.columns.get_level_values(0)
df.columns = df.columns.droplevel(1)
```



`.get_level_values( )`の括弧の中の数字は、MultiIndexの残したい階層を指定する。一番上の階層が`0`、下の階層が順に`1, 2, 3, ...`になる。

`.droplevel( )`の括弧の中の数字は、MultiIndexの落としたい階層を指定する。`[0, 1]`のような形で複数落とせる。

## 実行例

1番目の階層を残す。
```python
df.columns = df.columns.get_level_values(0)
#df.columns = df.columns.droplevel(1)

df
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3655949/025307fe-ee03-bdbb-9b28-674e55c8922b.png)

2番目の階層を残す。
```python
df.columns = df.columns.get_level_values(1)
#df.columns = df.columns.droplevel(0)
df
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3655949/eca0f7d5-12de-8c5f-f860-c4c292f04096.png)

ちなみに、`MultiIndex`を取得するとこんな感じで出力される。
```python
df.index.get_level_values
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3655949/3147399c-ebea-05fb-ee94-f7d7827fefba.png)



# 【MultiIndexを指定する方法】

冒頭で示したMultiIndexをもつDataFrameの作成方法を記す。

1. DataFrameの`index`/`columns`に対して`MultiIndex`オブジェクトを適用する
1. DataFrameの`index`/`columns`に対して`配列`を適用する

## 1. `MultiIndex`オブジェクトを適用する

`MultiIndex`オブジェクトは、`pd.MultiIndex.from_tuples()`で生成する。

```python
#配列の生成
arrays = [
    ["bar", "bar", "baz", "baz", "foo", "foo", "qux", "qux"],
    ["one", "two", "one", "two", "one", "two", "one", "two"],
]

#MultiIndexオブジェクトの生成
tuples = list(zip(*arrays))
index = pd.MultiIndex.from_tuples(tuples, names=["first", "second"])
index
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3655949/6929a73a-6835-9c32-f758-d2edcda2e143.png)

`MultiIndex`オブジェクトの場合、階層に名前を指定できる。上記の例では、`first`, `second`が指定されている。


これを用いて、DataFrameを作成すると以下のようになる。
```python
df = pd.DataFrame(np.random.randn(8, 4), index=index)
df
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3655949/352155dc-2c1f-1474-5726-b9b7da8bbc24.png)

`columns`に指定するとこんな感じ。
```python
df2 = pd.DataFrame(np.random.randn(3, 8), index=["A", "B", "C"], columns=index)
df2
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3655949/a9604936-5a87-c588-7e82-5762a58e03b3.png)

## 2. `配列`を適用する

配列をそのまま適用することもできる。この場合、階層に名前を指定できない。

```python
df = pd.DataFrame(np.random.randn(8, 4), index=arrays)
df
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3655949/c935d84b-1711-2239-c738-cafdbba0fad6.png)



```python
df2 = pd.DataFrame(np.random.randn(3, 8), index=["A", "B", "C"], columns=arrays)
df2
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3655949/71ffde69-4ef4-89f7-d15f-85b3f9c02145.png)
【まとめ】2月に読んだ技術記事・学んだこと【随時更新】

# はじめに


# 学んだこと一覧
## 技術系
### 19日：PostgreSQL上でテーブルを一括削除する方法
```
drop schema public cascade;
create schema public;
```
スキーマを削除することでスキーマに紐づくテーブルを再帰的に削除する、という考え方。
削除したスキーマと同名で再度スキーマを作成しなおすことで、空のスキーマ=テーブル削除済みのスキーマが出来上がる。
トランザクション内の処理ではないため、ロールバックできないので使用には注意を払うこと。

https://qiita.com/a-suenami/items/e231adc2e083ef9449f6

## ビジネススキル系
### 16日：タスク細分化
私はタスク細分化がヘッタクソなので、耳が痛い記事だった。
仕事も家事も、タスクを細分化したうえで作業を進められるよう日々意識してスケジュールを立てたいと思った。

https://qiita.com/haihaikazuma/items/cadbf00a1f848a8e20a6
【備忘録】Homebrewを使ってRubyをバージョンアップする手順

## はじめに
**自己紹介**
　未経験からWEB系エンジニアへの転職を目指している者です。現在はRuby on Railsを学習中です。

**この記事を書いたきっかけ**
　Homebrewを使ってRubyをバージョンアップした際にRubyのバージョンがうまく反映されなくて苦戦したため、備忘録としてまとめました。

## 動作環境
- mac OS Sonoma 14.2.1
- Homebrew 4.2.4

## 準備
Homebrewをインストールしてしていない場合は、下記のURLを見てインストールしてください。
分からない場合は、「Homebrew インストール」とかで検索すれば、良い記事がたくさんあると思います。

https://brew.sh/ja/

## rbenvをインストールする
以下のコマンドを入力して、rbenvをインストールします。

```:terminal
$ brew install rbenv ruby-build
```

<details>
<summary>rbenvとは</summary>
rbenv は、Unix 系システム上の Ruby プログラミング言語のバージョン管理ツールです。同じマシン上で複数の Ruby バージョンを切り替えたり、作業中のプロジェクトが常に正しい Ruby バージョンで実行されるようにする場合に役立ちます。
</details>

インストールが成功しているか確かめるため、バージョンを確認します。
```:terminal
$ rbenv -v
rbenv 1.2.0
```

## Rubyをインストールする
最新の安定版のRubyバージョンを確認します。
```:terminal
$ rbenv install -l 
```
入れたいバージョンを指定してインストールします。
```:terminal
$ rbenv install 3.3
```
使用するrubyバージョンを変更します。
```:terminal
# macで使うデフォルトのrubyバージョンを変更する場合
$ rbenv global 3.3

# カレントディレクトリで使うrubyバージョンを変更する場合
$ rbenv local 3.3
```
Ruby Gemをインストールします。
```:terminal
$ gem install bundler
```
Rubyのバージョンを確認して変更されていればOKです!
変更されていない場合は、もう少し設定が必要です。
```:terminal
$ ruby -v
```

## パスを通す
Rubyのバージョンが変わっていない場合はパスを通す必要があります。
自分の場合はパzshを使用していたにも関わらず、`.bashrc`にパスを通していたことがつまづきの原因でした。

<details>
<summary>パスを通すとは</summary>
コンピューターのシステムにおいて、特定のフォルダーに含まれる実行可能ファイルを、コマンドプロンプトやターミナルから直接実行できるようにすること。
</details>

### .bashrcまたは.zshrcを設定する
以下のコマンドを入力して、現在使用しているシェルを確認します。
```:terminal
$ echo $SHELL
```
**＜bashの場合＞**
以下のコマンドを入力してvim画面を開きます。
```:terminal
$ vim ~/.bashrc
```
`i`を入力して、挿入モードにします。（ターミナルの一番下に「-- INSERT --」と表示さていればOK）
rbenvの初期化スクリプトを実行するコマンドを行末に追加します。
```
eval "$(rbenv init -)"
```
`esc`キーで挿入モードを抜けて、`:wq`を入力し、変更を保存してvim画面を閉じます。
（保存せずにVim画面を閉じる場合は`:q!`を入力します。）

下記コマンドを実行して、パスの追加を反映させます。
```:terminal
$ source ~/.bashrc
```
**＜zshの場合＞**
以下のコマンドを入力してvim画面を開きます。
```:terminal
$ vim ~/.zshrc
```
`eval "$(rbenv init -)"`を行末に追加します。
下記コマンドを実行して、パスの追加を反映させます。
```:terminal
$ source ~/.zshrc
```

## 参考

https://github.com/rbenv/rbenv

https://learningbox.co.jp/2021/11/30/blog_vim-command/

https://qiita.com/sta/items/63e1048025d1830d12fd

https://qiita.com/magicant/items/d3bb7ea1192e63fba850
PHPStormでプロジェクト一覧にsrc以下が表示されなくなった

# 起こっていること
PHPStormを開くと、
プロジェクトにsrcディレクトリ以下が表示されなくなった

# 解決
1. PHPStormを終了
2. プロジェクト直下の``.idea``ディレクトリを消す
3. 再度PHPStormを開く

# 参考
https://www.scriptlife.jp/contents/programming/2018/03/09/trouble-intellij-can-not-view-file/

# もっと参考
``.idea``ディレクトリとは？
JetBrainsのIDEで使用されるプロジェクト設定フォルダ。
今回は何かしらの原因で設定フォルダが壊れたみたい。
PL900 Power Platform Fundamentals 受験記

## PL900 Power Platform Fundamentalsとは？
Power Platformを勉強し始めた初心者向けの資格試験となります。

[詳しくはこちら](https://learn.microsoft.com/ja-jp/credentials/certifications/exams/pl-900/)

## 受験方法や受験料は？
- 受験方法
あらかじめ予約した日時にテストセンターにてCBT形式で受験するのが基本的な流れです。
自宅や職場からも受験可能ではありますが、細かな注意点が多く準備も大変なのでテストセンターへ足を運ぶことをお勧めします。

- 受験料
日本円で12,500円（税抜）です。
少し高いですがこちらの受験料は、申込時に会社のアカウントを使用したり紐づけたりすることによりお安く受けることができます。
ちなみに私は、申込時に会社のアカウントを紐づけたら50%引きが適用され、6250円（税抜）で受験することができました。

## 試験対策
① Microsoft Virtual Training Day
② Microsoft Learnを使用する（無料）
③ Udemyで模擬問題集を解く（4回分・2,600円（2024/02時点））

上から順番に、①でpower platformの基礎を学習し、②で具体的な試験対策のアンデマンドセミナーを視聴。最後にアウトプット形式で知識を習得するようなイメージで進めていくと勉強しやすいと思います。

Udemyの問題集は、本番の試験と出題形式が若干異なる点は要注意です。しかし、問題の要素は変わらないため、Udemyの問題の内容を抑えていれば試験対策としては十分だと思われます。

[Udemy模擬問題集](https://www.udemy.com/course/2023pl-900-power-platform-fundamentals/)

## 実際の試験問題は・・？

私が2023/12に受験した試験では、Power AppsとPower Automateが比較的多く出題されていた印象でした。
モデル駆動型アプリとキャンバスアプリの具体的な機能、power automateの各フローでできることなどが特に印象強いです。
問題形式としては、1択を選ぶものより並び替えや複数選択の問題が多かったです。
そのため、事前に解いた問題等をそのまま覚えるより、問題の根本的な知識を覚えることをお勧めします。

## さいごに
PL900は、Power Platformの資格の中でも一番基礎のものとなります。
これからPowerPlatformを活用したい方には必ずと言ってよいほど重要な知識が詰まってる資格試験だと思いますので、是非受験してみることをお勧めします！
個人的な意見にはなりますが、前述した試験対策を試験2か月前あたりから一通り進めればほぼ合格できると思うのでチャレンジしてみてください！

※こちらのURLにアクセスしていただくと簡単にPL900について知ることができますので是非ご活用ください。

https://learn.microsoft.com/ja-jp/credentials/certifications/exams/pl-900/
研修内容を振り返る ~2020.04

## まえがき

2020/03/09からバックエンドエンジニアとして働かせていただいています。ほぼPHP未経験の状態での業務スタートということもあり、弊社の先輩方には今日まで手厚い研修をしていただきました。今回はそんな自分が研修内容と実際に実施した内容・振り返りなどを一か月毎に区切って記事にしたいと思います。

## ご注意

- 本内容は研修を受けた自分が感じた主観であり、弊社の意向と異なる可能性があります。
- ご入社いただいたみなさんに必ず本研修を受けていただくということではありません。
- 研修内容に関してはメモを取っていましたが実際の内容と異なる可能性があります。
- 情報セキュリティの観点から一部実名を伏せて記載することがあります。

## 入社時の自分のレベル

ここで前提情報として2020/03/09時点での自分の知識レベルを簡単に書いてみます。

- Ruby on Railsで簡単なWebアプリ開発ができる。
- herokuを用いたアプリのデプロイをすることができる。
- Webの基礎が理解できている。（リクエスト、レスポンスなど）
- MVC構造を知っている。
- 物理PCにLinuxを導入することができる。
- MacOSを触ったことがある。
- 調べながらならPHPの制御構文を記載することができる。

## 研修のサポート

新型コロナウイルスの蔓延に伴いリモートでの研修となったため、毎日上司の方とzoomを用いて10分~30分程度のミーティングを行いました。また、弊社のSlackにある技術的な内容を質問できるチャンネルで詰まった部分の相談に乗ってもらったりしました。

## 2020.04までの主な研修内容

- Laravelアプリで筆算を出力する計算ドリルアプリを作ってみよう。
- SQLを実行できるようになろう。

### 研修内容をちょっとだけ深堀り

計算ドリルアプリ

まずはLaravelに慣れることを目的に、ランダムな二桁の数字を出力する筆算の計算ドリルの表示アプリの作成を行いました。環境構築から始まり、ルーティング、コントローラ、ビューの記載方法、各ファイルの設置場所など基礎の基礎の基礎くらいの内容を実施しました。ビューに表示されている画面をPDFとして保存するライブラリの導入も行いました。Laravelの動作環境はMacのローカルに直接構築しました。（DockerやAWSは未使用）MacのローカルにLaravelの環境を構築した時の手順はこちら→https://qiita.com/miriwo/items/adaaaeecae69d6392e08

SQLの実行

SQL文単体での実行経験がなかったためselect文などこちらも基礎の基礎の内容を実施しました。MySQLをMacのローカルに環境構築を行いSQLでDBを作成してデータを格納するところから開始しました。「すっきり分かるSQL入門」という本を持っていたのでその本を用いて進めて良いか上司の方に打診し、受け入れていただけたので、本の章ごとに区切ってSQL文の実行経験を積んでゆきました。MacのローカルにMySQLを導入した時の手順はこちら→https://qiita.com/miriwo/items/ef6a064bc545fbea29d9

## 得た知識

- Laravelアプリの作成方法
- ライブラリの導入方法
- LaravelのMVCファイルの場所や記載方法
- 環境構築そのもの
- SQLを用いたデータの操作
- テーブル結合

## 苦労した点

- Ruby on Railsとの差異
- MySQL8.0の環境構築
- JOIN句を用いたテーブル結合

### 苦労した点をちょっとだけ深堀り

Ruby on Railsとの差異

フレームワークの言語が異なるため差異があることは当たり前ですが、その差分に非常に驚きました。そもそもフレームワーク自体Railsしか触れたことがなかったので「ルーティング、コントローラ、ビューファイルの場所」がもう違ってプチパニックになった記憶があります。（今思うと落ち着いて両者を比較すれば焦る必要はなかったのかも・・・）最初想像していた以上にLaravelの馴染むことができず焦りました。「artisan コマンドってなんだよ！」と思っていました。

MySQL8.0の環境構築

先に記載した構築手順にはスマートに記載されておりますが、まあうまく構築できずにヒーヒー言ってた覚えがあります。MySQLは起動しているのにターミナルからログインできず、かつ、バージョンが8.0なのであまり知見がなく困りました。

JOIN句を用いたテーブル結合

SQL文の習得で非常に詰まったのがテーブル結合でした。今でこそ「リレーションを用いて他テーブルの関連するレコードのデータを取得する」ということが理解できていますが、実施すればするほど「結合とは・・？結局・・？」となった覚えがあります。おそらく考えて理解するよりたくさんJOIN句を用いたSQL文を実行して慣れた方が早かった気がします。理解するより真似して場数踏んで慣れるが自分にはあっているのかも？

## さいごに

入社から一か月の主な研修内容を記載してみました。入社当時の自分と同じ様な境遇の方や駆け出しのエンジニアさんの一つの参考と慣れば幸いです。研修終了までこの様に一か月単位で記事にしていこうかと思います。
XCodeでビルド時にModule map file not foundがでた

# やりたいこと
React Nativeのアプリをローカルでビルドしたい。(Xcode)

# ビルドエラー
Xcodeからビルドしようとすると以下のエラー
> module map file '/Users/hoge/Library/Developer/Xcode/DerivedData/application/Build/Products/Debug-iphoneos/FirebaseCoreInternal/FirebaseCoreInternal.modulemap' not found

# 解決
### 原因
間違えて``.xcodeproj`` ファイルを開いていた。
``.xcworkspace``ファイルを開かないとエラーになる。

### どう違う？
``.xcodeproj``
Xcodeプロジェクトの基本ファイル。
単一プロジェクトの管理に使用する。

``.xcworkspace``
Xcodeのワークスペースファイル。
複数プロジェクトや依存関係を含むプロジェクト群の管理に使用する

今回はCocoaPodsを使用してFirebase SDKをプロジェクトに追加しているため、.xcworkspaceを通じてプロジェクトを管理します。

# 参考
https://stackoverflow.com/questions/55675694/how-to-fix-cocoapod-modulemap-file-not-found
【Flutter】CircleAvatarでチャットのアイコンを作る

こんにちは。
今回は、CircleAvatarでチャットのアイコンを作ります。

# 方法
![スクリーンショット 2023-08-13 20.18.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524120/ce57fee2-1687-ea40-8538-b4c8b9db94e0.png)

CircleAvaterのwidgetを使用します。
valueは、bool値を指定します。trueの時にチェックボックスにチェックが入ります。
onChangedは、チェックボックスをタップした際に実行されるイベントハンドラを指定します。

今回は、性別のチェックボックスをマテリアルアイコンと一緒に表示してみました。
[MaterialIcon](https://fonts.google.com/icons?icon.query=gender&icon.platform=android)


# 使用例
```dart

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  bool isCheckedMale = false;
  bool isCheckedFemale = false;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Row(mainAxisAlignment: MainAxisAlignment.center, children: [
          SizedBox(
            child: Row(
              children: [
                Icon(
                  Icons.male,
                  color: isCheckedMale ? Colors.blue : Colors.grey[500],
                  size: 100.0,
                ),
                Checkbox(
                    activeColor: Colors.blue,
                    value: isCheckedMale,
                    onChanged: (value) {
                      setState(() {
                        isCheckedMale = value!;
                      });
                    }),
              ],
            ),
          ),
          SizedBox(
            child: Row(
              children: [
                Icon(
                  Icons.female,
                  color: isCheckedFemale ? Colors.pink : Colors.grey[500],
                  size: 100.0,
                ),
                Checkbox(
                    activeColor: Colors.pink,
                    value: isCheckedFemale,
                    onChanged: (value) {
                      setState(() {
                        isCheckedFemale = value!;
                      });
                    }),
              ],
            ),
          )
        ]),
      ),
    );
  }
}


```

# 実行例
![checkbox.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524120/96289df2-64dc-ac9a-4f57-b07903e224f9.gif)



# 最後に
ここまで読んでいただき、ありがとうございました！
いいねしてくれたら、スキップして喜びます:heartpulse:
mysql メモ

```
◆◆◆ mydqldump

1 mysql をシステムpathに追加する
2 cmd立ち上がる

★tdb全部テーブルをエクスポート
mysqldump -uroot -ptest tdb > e:\db\tbl-and-data.sql
mysqldump --user=root --password=test tdb  > e:\db\tbl-and-data.sql
注：ログインIDはroot  パスワードはtest  上記でわざとパスワード入力不要、便利です。
mysqldump -u root -p tdb  > e:\db\tbl-and-data.sql

〇特定なテーブルの場合
mysqldump -uroot -ptest tdb testtbl > e:\db\testtbl.sql
テーブル名はtesttbl

★test_db全部テーブルをインポート   注意：mysqldump ×  mysql 〇
mysql -uroot -ptest  --default-character-set=binary tdb < e:\db\tbl-and-data.sql
又は
mysql --user=root --password=test --default-character-set=binary tdb  <  e:\db\tbl-and-data.sql

〇特定なテーブルの場合
コマンドは上記のままです、データファイルの特定なテーブルのデータのみです。

厳重注意  日本語インポートエラーないように下記は必須です
--default-character-set=binary

◆◆◆ 外部キーテーブル作成
CREATE TABLE parent (
    id INT NOT NULL,
    PRIMARY KEY (id)
) ENGINE=INNODB;

CREATE TABLE child (
    id INT,
    parent_id INT,
	PRIMARY KEY (id),
    INDEX par_ind (parent_id),
    FOREIGN KEY (parent_id)
        REFERENCES parent(id)
        ON DELETE CASCADE
) 

◆◆◆  Workbenchを日本語化
MySQL Workbenchを日本語化する（2024.3.22月時点）
MySQL Workbench Version8.0

https://ja.osdn.net/projects/oss-ja-jpn/releases/p10742
上記からダウンロード 非常に時間かかる。2時間???
連続解凍する
mysql-workbench-gpl_5.2.34_ja6.tar.gz
↓
mysql-workbench-gpl_5.2.34_ja6.gz
↓
mysql-workbench-gpl_5.2.34-6_i386.deb
↓
data.tar.gz
main_menu.xmlファイルはここ↓
usr/local/share/mysql-workbench/data/main_menu.xml
下記に上書き、再起動。
Application/MySQLWorkbench/Contents/Resources/data/main_menu.xml

★日本語文字化け防止設定
メインメニュー＞MySQL Workbench>Preferences>Modeling>Appearance>Fonts
にアクセスし、「Configure Fonts For」にJapaneseを選択する。

ER図自動生成可能です。ただし、テーブル定義に必ず外部キー定義ある、ないとできない。
DB接続から作成する。sql文から作成できない、DB、テーブル選択できないから。

★7zip 日本語化にする
日本語化する方法は
左のstartメニューにある7-Zip File Managerに
ツール＞オプション＞言語
言語を日本語を選択する
インストール完了後PC再起動だけで右メニューも日本語になる
git –  特定ファイルだけを1個前の状態に戻す

# コマンド例

`git checkout HEAD^ <ファイルパス>`

# gitconfig のエイリアスに設定する例



```
[alias]
  rollback = "!f() { git checkout HEAD^ \"$1\"; }; f"
```

コマンド実行

```
git rollback <ファイルパス>
```


# チャットメンバー募集


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

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


# プロフィール・経歴

https://github.com/YumaInaura/YumaInaura
Azure functionsのバインドとトリガー

# Azure Functionsの構成
Azure functionの各関数は、function.jsonとセットで定義されます。
```bash
 MyFunctionApp/
├── Function1/
│   ├── function.json
│   └── index.js
├── Function2/
│   ├── function.json
│   └── index.js
└── shared/
    └── utils.js

```

## Azure functionsのfunction.json
Azure functionsは、どのタイミングで関数を発火させるかという「トリガー」の設定と、データの入力、処理、出力などをまとめた「バインド」を設定されている必要があります。これは、function.jsonに定義してあります。(JavaScript/PowerShell/Python/TypeScriptの場合)
以下はfunction.jsonの一例です。
```
{
  "bindings": [
    {
      "type": "queueTrigger",
      "direction": "in",
      "name": "order",
      "queueName": "myqueue-items",
      "connection": "MY_STORAGE_ACCT_APP_SETTING"
    },
    {
      "type": "table",
      "direction": "out",
      "name": "$return",
      "tableName": "outTable",
      "connection": "MY_TABLE_STORAGE_ACCT_APP_SETTING"
    }
  ]
}
```
まず、directionはデータの方向を示しています。inの場合は、関数にデータを入力する方向で、outは関数からデータを出力する方向です。つまり入力と出力の方向の情報を記しています。myqueue-itemsにキューメッセージが入力された場合に、queueTriggerという関数が発火します。そして、処理をしてoutTableというテーブルに情報を入れ込むということです。`"name": "$return",`という表現からも返り値がテーブルい直接書き込まれることがわかります。

処理の関数について、javascriptでは以下のような処理を行います。orderはキューのデータです。orderの情報をもとに、orderを処理していることがわかります。

## Azure functionsの処理ファイル

今回はjavascriptで処理を行います。関数のロジック自体は非常にシンプルです。

関数のパラメータ
context: Azure Functions ランタイムから提供されるコンテキストオブジェクトで、関数の実行環境に関する情報や、バインディングへのアクセスなどを提供します。
order: 関数のトリガーとなったキューメッセージの内容です。この場合、order オブジェクトはキューから直接受け取ったデータを表します。

処理の流れ
PartitionKeyとRowKeyの設定: order オブジェクトに PartitionKey と RowKey を設定します。これらは、Azure Table Storageでのデータのパーティショニングと一意性の確保に使用されます。

PartitionKey は、データを分割するためのキーで、関連するデータを同じパーティションに格納することでアクセスの効率を上げることができます。ここでは "Orders" という静的な値が使用されており、すべての注文が同じパーティションに格納されることを意味します。

RowKey は、パーティション内でのデータの一意性を保証するキーです。generateRandomId 関数を呼び出して生成される一意のIDが設定されます。これにより、各注文が識別可能になります。

データのバインディングへの設定: context.bindings.order = order; により、処理された order オブジェクトが指定されたバインディング（このコードからは出力バインディングであることが推測されます）に設定されます。この操作により、order オブジェクトは自動的に指定されたストレージに保存される準備が整います。

```javascript
module.exports = async function (context, order) {
    order.PartitionKey = "Orders";
    order.RowKey = generateRandomId(); 

    context.bindings.order = order;
};

function generateRandomId() {
    return Math.random().toString(36).substring(2, 15) +
        Math.random().toString(36).substring(2, 15);
}
```

そんな感じです。つまり、Azure Functionsではデータの流れと関数の発動タイミングを処理する関数とセットで定義することがミソです。
Yolov8モデルをPyinstallerでexe化するのにハマった点

# Pyinstallerでexeアプリを作成するのに苦労したので備忘録です
pyinstaller = ver 5.6.2
python = ver 3.10.9
ultralytics = ver 8.0.47

yolov8モデルを作成する際に、作業用フォルダを作成し
```pip install ultralytics```
するとフォルダにultralyticsというフォルダができます。このフォルダをpyinstallerに認識させるのに苦労しました。
exe化したいpyファイル（今回はyolov8_myproject.py)まで移動して
最初は
```pyinstaller  --collect-data=ultralytics yolov8_myproject.py --onefile```
オプションを付けて実行したが、ultralyticsあたりのmodule not foundを連発。さらに、コンソールが一瞬で消えるのでエラーの内容がわかりません。
１．コンソールが消えないようにyolov8_mypoject.pyの冒頭に
```import subprocess　subprocess.Popen('PAUSE', shell=True)```
を付け足して、コンソールが消えないようにしておきます。
２．```pyinstaller  --collect-data=ultralytics yolov8_myproject.py ```
onefile オプションを外します。作成すると、distフォルダにyolov8_myprojectが作成され、その中にyolov8_myproject.exeファイルが作成されるのでそれを実行すると、ultralyticsのファイルが存在しないといわれます。作業用フォルダのultaralyticsフォルダが参照できないようです。そこで、作業用フォルダのultralyticsフォルダをyolov8_myproject.exeファイルと同一階層にコピーします。
３．exeファイルを実行すると、別のmodule　not foundがでるので、yolov8_myproject.pyファイルと同一階層に作成されたyolov8_myproject.specファイルをエディタで編集し、hidden importにmodule を書き加えます
```
from PyInstaller.utils.hooks import collect_data_files

datas = []
datas += collect_data_files('ultralytics')


block_cipher = None


a = Analysis(
    ['yolov8_myproject.py'],
    pathex=[],
    binaries=[],
    datas=datas,
    hiddenimports=['charset_normalizer.md__mypyc'],
    hookspath=[],
    hooksconfig={},
    runtime_hooks=[],
    excludes=[],
    win_no_prefer_redirects=False,
    win_private_assemblies=False,
    cipher=block_cipher,
    noarchive=False,
)
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)

exe = EXE(
    pyz,
    a.scripts,
    [],
    exclude_binaries=True,
    name='yolov8_myproject',
    debug=False,
    bootloader_ignore_signals=False,
    strip=False,
    upx=True,
    console=True,
    disable_windowed_traceback=False,
    argv_emulation=False,
    target_arch=None,
    codesign_identity=None,
    entitlements_file=None,
)
coll = COLLECT(
    exe,
    a.binaries,
    a.zipfiles,
    a.datas,
    strip=False,
    upx=True,
    upx_exclude=[],
    name='yolov8_myproject',
)
```
以上がspecファイルの全体になります。
```pyinstaller yolov8_ocd_20240218.spec```
で再度実行すると、distファイルが空でないと怒られるので、distファイルの中身を削除して、再度実行し最後にultralyticsフォルダをもう一度コピーして、、

## 何とか動きました！
onefileではないので、配布するときはフォルダ事渡して、exeファイルの位置を移動させないように注意が必要です。
2024/2/19時点ですので、ultrapyticsの仕様が変わればまた変わるかもしれません。
参考サイト
[pyinstaller + yolo v8](https://qiita.com/kotai2003/items/c1c3193966448bb29af7)
[YoloV8 deploy (EXE/WINDOWS) #1356](https://github.com/orgs/ultralytics/discussions/1356)
[PyInstallerのImportErrorとModuleNotFoundErrorで躓いた話]
(https://qiita.com/__Lily__/items/b2d55965e417f2997324)
AppServiceのWebJobを試してみた

AppServiceにWebJobというのがあって定期実行したりすることができるというのは何となく知っていたけど、いざどうやってやればいいかというのがイマイチわからない。

Azure App Service で Web ジョブを使用してバックグラウンド タスクを実行する
https://learn.microsoft.com/ja-jp/azure/app-service/webjobs-create

上記ドキュメントを読むと様々な実行形式に対応しているということで下記のようなサンプルのbatを作って動かしてみた

```sample.bat
@echo off
echo %date% %time% >> %HOME%\site\wwwroot\webjob-message.txt
```

このバッチファイルを.zipにして、ポータル上でアップロードすると動くことを確認できた。

webjob は下記にアップロードされるらしい
C:\home\site\wwwroot\App_Data\jobs\continuous 
C:\home\site\wwwroot\App_Data\jobs\triggered
Cookieについてのまとめ

## Cookieの概念
- Cookieは、サーバーがWebブラウザに送信する小さなデータの塊です。
- WebブラウザはこのCookieをローカルコンピュータに保存し、指定されたドメインにアクセスする際にこれを送信します。
- HTTPは無状態プロトコルですが、Cookieを用いることで状態を持たせることができます。

## Cookieの用途
- セッション管理：ログイン状態の維持やショッピングカートの情報保存などに使用されます。
- 個人設定：ユーザーの設定やテーマの選択など、個人化された体験を提供するために使われます。
- 追跡：ユーザーの行動や嗜好を追跡し、分析やマーケティングに役立てます。

## Cookieに関連するHTTPリクエスト/レスポンスヘッダー
**HTTPリクエストヘッダー**

```
GET /sample_page.html HTTP/2.0
Host: www.example.org
Cookie: yummy_cookie=choco; tasty_cookie=strawberry
```

**HTTPレスポンスヘッダー**
```
HTTP/2.0 200 OK
Content-Type: text/html
Set-Cookie: yummy_cookie=choco
Set-Cookie: tasty_cookie=strawberry
```


## セキュリティ上の脆弱性
- MITM攻撃（中間者攻撃）
- XSS（クロスサイトスクリプティング）
- CSRF（クロスサイトリクエストフォージェリ）

## セキュリティを強化するための属性
- HttpOnly：JavaScriptを介してCookieにアクセスすることを防ぎます。

    ```
    Set-Cookie: id=q1w2e3; HttpOnly
    ```

- Secure：HTTPSプロトコルを使用する際にのみCookieを送信します。

    ```
    Set-Cookie: id=q1w2e3; Secure
    ```

- SameSite：同一サイト/異なるサイトからのリクエストに対するCookieの送信ルールを定めます。SameSiteの基準は、「登録可能なドメイン」によって決まります。
    - SameSiteの基準
        - ドメインが`https://example.com`の場合
            - ⭕ `https://example.com/en-US/docs/` (ルート不問)
            - ⭕ `https://api.example.com/en-US/` (サブドメインは問いません)
            - ⭕ `https://example.com:8080` (ポートは問いません)
            - ❌ `https://example2.com` (登録可能なドメインが異なる)
            - ❌ `http://example.com` (プロトコルが異なる)
    - SameSiteの値
        - Strict：同一ドメインからのリクエストのみにCookieを送信します。
            ```
            Set-Cookie: id=q1w2e3; SameSite=Strict
            ```
        - Lax：リンクを介したリクエストを含む、同一ドメインからのリクエストに対してCookieを送信します。
            ```
            Set-Cookie: id=q1w2e3; SameSite=Lax
            ```
        - None：任意のドメインからのリクエストに対してCookieを送信しますが、この場合はSecure属性が必須です。
            ```
            Set-Cookie: id=q1w2e3; SameSite=None; Secure
            ```

## 有効範囲を定めるための属性
`api.example.com`でCookieを生成した場合
- Domain：指定されたドメインに対するHTTPリクエストであれば、Cookieを送信する。指定ドメインがなければ、サブドメインは除外されます。
    ```
    Set-Cookie: id=q1w2e3; domain=example.com
    ```
    - ⭕ www.example.com
    - ⭕ api.example.com
    - ⭕ example.com
    ```
    Set-Cookie: id=q1w2e3;
    ```
    - ❌ www.example.com
    - ⭕ api.example.com
    - ❌ example.com
    
- Path：指定したURLパスを含むHTTPリクエストの場合にのみ、Cookieを送信します。
    ```
    Set-Cookie: id=q1w2e3; path=/docs
    ```
    - ⭕ /docs
    - ⭕ /docs/
    - ⭕ /docs/Web/
    - ⭕ /docs/Web/HTTP
    - ❌ /
    - ❌ /docsets
    - ❌ /fr/docs

### 参考文献
- **[Using HTTP cookies - MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies)**
- [Site - MDN](https://developer.mozilla.org/en-US/docs/Glossary/Site)
【MySQL】ver8.0へ切り替わった時のEmbulk接続

## はじめに
Embulkを用いてMySQLDBへ接続を行おうとして以下のようなメッセージが表示されたことはございますでしょうか。

```ruby:メッセージ例
Unable to load authentication plugin 'caching_sha2_password'.
```

本記事では、上記接続エラーメッセージが表示されたときの対応方法について記載しております。

## 記事の対象者
+ Embulkを用いてMySQLDBへ接続したい方
+ 接続先となるMySQLDBのバージョン変更に対応したい方

## 結論
+ 最新版のmysql JDBCドライバーを用意してください
+ Embulk実行用のymlファイルに取得したドライバ情報を記載してください

## 目次
1. MySQLDBver8.0の変更点
1. ymlファイルの設定内容
1. MySQLの設定変更
1. おわりに
 
## MySqlDBver8.0の変更点について
MySQLではver8.04よりユーザーの認証方式のデフォルトが「mysql_native_password」から「caching_sha2_password」に変更されました。
SHA-256を使用したより安全なパスワードの暗号化を提供するとともに、キャッシュを使用して同一ユーザの認証処理を高速化しようという、MySQL推奨の認証プラグインとなります。
詳しくは[公式ドキュメント](https://dev.mysql.com/doc/refman/8.0/ja/caching-sha2-pluggable-authentication.html)を参照してください。

 ### MySQLのバージョン確認
接続先のMySQLのバージョンが不明な場合は、MySQLへクライアント接続した状態で以下のコマンドを実行することで確認することが可能となります。

```ryby:接続コマンド
  $ mysql --version
```

## ymlファイルの設定内容
EmbulkにてMySQLのver8.04に接続するためには、ymlファイル上に公式サイトから取得した最新版のMySQL JDBC ドライバ(.jarファイル)を指定することで、ユーザー認証方式がMySQLのVer8.04のデフォルトに合う形で接続することが可能となります。

MySQL JDBCドライバーのダウンロードは[こちら](https://www.mysql.com/jp/products/connector/)からご参照ください。

```ruby:サンプルymlファイル
in:
  type: mysql
  host: your_mysql_host
  user: your_mysql_user
  password: your_mysql_password
  database: your_mysql_database
  table: your_mysql_table

  # 以下のようにドライバのパスを指定
  driver_path: /.embulk/mysql-connector-java-X.XX.XX.jar
```

## MySQLの設定変更
MySQLの設定ファイル (my.cnfまたはmy.ini) を編集し、デフォルトの認証プラグインをmysql_native_passwordに変更することでEmbulk側の設定を変更せずに対応することが可能となります。

```ruby:cnfまたはiniファイル
default-authentication-plugin=mysql_native_password
```
この変更を行った後にMySQLを再起動して変更が有効になります。

## おわりに
バージョンが変更されるたびに仕様の変更されることがあるため、どんな変化があったのかを整理することでスムーズな開発が出来るのではないかと考えております。
本記事が、どなたかの役に立てたのであれば幸いです。
拝見頂き、ありがとうございました。
act で Github Actions (CIのworkflow) を local 実行する

# レポジトリ

https://github.com/nektos/act

# インストール ( Mac + HomeBrew の場合 )

```
brew install act
```

## バージョン確認

```
act --version
act version 0.2.59
```


# Workflowファイルを準備する

`.github/workflows/main.yml`


```
name: CI
on: push

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-node@v1
      - run: npm install
      - run: echo 'OK!'
```



# 実行してみる


```
act
```

一部だけ実行する場合は、ファイルを選んで実行するのではなく、 Workflow のタグを指定して実行するようだ


```
act push
```


M系のMacの場合は Platform を指定する

```
act --container-architecture linux/amd64
```

# 実行できない？ (コンソールが固まる)

docker pull に時間がかかっているかもしれない
pull で止まっている場合は、そのまま待てば良い
( actでdocker pull  のプログレスバーが表示されない )


以下のように、コンソールのログにpull先のイメージが出力されているはずだ


```
[CI/test]   🐳  docker pull image=catthehacker/ubuntu:act-latest platform=linux/amd64 username= forcePull=true
```

もしくは先に直接 docker コマンドで pull しておくと良いかもしれない

```
docker pull catthehacker/ubuntu:act-latest
```

```
docker pull catthehacker/ubuntu:act-latest --platform=linux/amd64
```

# 成功した結果例


![image](https://github.com/YumaInaura/YumaInaura/assets/13635059/0cd1b22a-c234-4bee-9cef-1e3591bd5caa)


# チャットメンバー募集


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

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


# プロフィール・経歴

https://github.com/YumaInaura/YumaInaura
removeメソッドとfor文を組み合わせるときに注意すること

Pythonでリストの要素を操作する際には、特に`remove()`メソッドをforループと組み合わせる場合には注意が必要です。

## 問題点の説明
例えば以下のようなリストがあるとします。
```python
fruits = ['apple', 'apple', 'banana', 'orange', 'grape']
```

このリストからすべての`'apple'`を削除したい場合、一見以下のようなコードで問題なさそうに思えます。

```python
for fruit in fruits:
    if fruit == 'apple':
        fruits.remove(fruit)
```
しかし、これは期待通りに動作しません。なぜならば、`remove()`メソッドによってリストの要素が変化し、forループがリスト全体を正しく処理できなくなるからです。
```python
結果：['apple', 'banana', 'orange', 'grape']
```

### 何が起きているのか
1．最初の'apple'が削除されると、リストの要素が1つ削除され、**インデックスがずれます**。

2．forループは次の要素に進みますが、**リスト内の要素がシフトされた**ため、本来の順序と異なる要素にアクセスしようとします。

3．その結果、**2つ目の'apple'がスキップされ**、'banana'が次の要素として取り出されます。

このように、要素を直接削除することでリストのインデックスがずれ、予期せぬ動作を引き起こす可能性があるため、この方法は推奨されません。

代わりに、**リストのコピー**を作成するか、**whileループ**を使用して安全に要素を削除しましょう。

## 正しい使い方
### 方法1: リストのコピーを作成して操作する
```python
fruits = ['apple', 'apple', 'banana', 'orange', 'grape']
fruits_copy = fruits[:]  # リストのコピーを作成する
for fruit in fruits_copy:
	if fruit == 'apple':
		fruits.remove(fruit)

print(fruits) # Output: ['banana', 'orange', 'grape']
```

### 方法2: whileループを使用する
```python
fruits = ['apple', 'apple', 'banana', 'orange', 'grape']
while 'apple' in fruits:
	fruits.remove('apple')

print(fruits) # Output: ['banana', 'orange', 'grape']
```

## まとめ
リストの要素を安全に削除するためには、リストのコピーを作成して操作するか、whileループを使用する方法があります。これにより、リストのインデックスがずれる問題を回避しつつ、要素を確実に削除することができます。
コンストラクタ

コンストラクタを用いるとクラス側で初期化を行う事が出来ます。

コンストラクタはプログラムで直接呼び出す事は出来ません。
クラスがnewで作成された後、コンストラクタも自動的に実行されます。

クラスメイト全く同じ名前でメソッドを作成すると、コンストラクタとして扱われます。
ただし、コンストラクタではvoidを含めた戻り値の型を使用する事は出来ません。
DDコマンドで焼く時はsyncする

dd実行後、syncしないでSDカードを抜いたりすると、中途半端に焼かれた状態になる。
従ってsyncして、同期が終わるまで待つ必要がある。

* 例１) dd後、sync実行
```bash
$ sudo dd if=/dev/zero of=/dev/sde bs=1M conv=sync
$ sync
```

conv=syncの意味は以下。I/O同期ではないので注意
>各入力ブロックを「ibs=」で指定したサイズになるまでNULL文字で埋める（blockまたはunblockと一緒に指定した場合、NULL文字ではなくスペースで埋める）

* 例２）ddのiflag,oflagでsync指定
```bash
$ sudo dd if=/dev/zero of=/dev/sde iflag=sync oflag=sync bs=1M conv=sync
```

* 例３）conf=fsyncをつける
```bash
$ sudo dd if=/dev/zero of=/dev/sde bs=1M conv=fsync
```
conv=fsyncの意味は以下。
> ファイルに対してディスクへの書き出しを保証

# 参考
* [【 dd 】コマンド――ブロック単位でファイルをコピー、変換する](https://atmarkit.itmedia.co.jp/ait/articles/1711/30/news027.html)
* [dd - ファイルをコピーする](https://linuxcommand.net/dd/)
[Setup] Cypress + windows

# outline

Cypressをwindows環境で試してみた


# setup

## install Node.js for npm

nodejsのinstallerをもちいてinstallする。
install optionで"必要なライブラリーなどはinstallする"を選択する。

https://nodejs.org/en

その後、command promptを起動し、npmを実行し、動くことを確認する


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/267085/e829ea97-8fb6-34a0-05c9-fc46bd98d871.png)


## install Cypress

```
npm install cypress --save-dev
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/267085/5aa1cb7d-a406-c6cf-fad5-8d9ccd205887.png)


## 起動確認

```
npx cypress open
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/267085/453a1f2a-82ac-8e59-1cfa-6c41a84414d7.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/267085/da3054cd-7ae7-8f4a-6914-ee1fc7c2ad14.png)


# sample

keibaのloginをサンプルとして作ってみた

以下のように、create newすると、最低限のcodeが作成される

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/267085/daa31c10-44d5-c822-816f-b30acf438ed6.png)

シナリオは

1. brower openして https://keiba.rakuten.co.jp にアクセスする
2. マイページログインをクリックする
3. IDを入力して、次へをクリック
4. Passwordを入力して、ログインする


```
context('login', () => {
  it('login', () => {
    cy.visit('https://keiba.rakuten.co.jp')
    cy.get('#introduceArea > div.mypageLogin > a').click()
    
    cy.origin('https://login.account.rakuten.com',() => {
      cy.get('#scrollingLayer > div.hc.p-30-0.spacing-24-24.s.c.wf.ct.cl > h1 > div' , { timeout:10000 }).should('have.text','楽天会員 ログイン')
      // input id
      cy.get('#user_id').clear()
      cy.get('#user_id').type('Login ID')
      cy.get('#cta001 > div > div').click()
      // input password
      cy.get('#password_current').clear()
      cy.get('#password_current').type('Password')
      cy.get('#cta011 > div > div').click()
      
    })

    cy.get('#headline > h1').should('have.text','マイページトップ' , { timeout:5000 })
    cy.get('body > div.frheader > div.glonavmain > div > ul > li:nth-child(1) > a').click()

  })
})
```

Cypressのオブジェクトの特定にはselectorを用いる。

chrome developer toolでselector をcopyして取得する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/267085/8bcc14af-58e6-9e2c-074f-f18fe396d62e.png)


Cypressは複数のdomain間をはさむことは基本的にできない。
たとえば、ログインするとき、別のdomainに遷移するとき、そのままでは以下のようなエラーになる

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/267085/b9cf2bd4-4823-06b0-e470-c65b2586fcff.png)

その解決方法として、 originを用いる。

https://docs.cypress.io/guides/references/error-messages#cyvisit-succeeded-but-commands-are-timing-out





実際にCypressで実行してみた。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/267085/1479fbfa-18f3-c1df-8205-f32b84dcc6f4.png)
IntersectionObserverで動的にscrollMarginを変更したい

スクロールイベントを検知するAPIのIntersection Observerですが、インスタンス生成後にscrollMarginを動的に変更することはできません。
以下の記事でも、同じように悩んでいる人がいました。
"How to dynamically change Intersection Observer's configuration"
https://stackoverflow.com/questions/49511503/how-to-dynamically-change-intersection-observers-configuration

今回は、以下のようにラッパークラスを作り対応しました。
```ts
/**
 * IntersectionObserverを拡張し、rootMarginの動的な更新をサポートするラッパークラス。
 * 標準のIntersectionObserverでは、rootMarginを変更するためには新しいインスタンスの作成が必要だが、
 * このクラスを使用することで、既存のインスタンスでrootMarginを容易に更新できる。
 */
class IntersectionObserverWrapper {
    private _intersectionObserver: IntersectionObserver;
    private _callback: IntersectionObserverCallback;
    private _options?: IntersectionObserverInit;
    private _targets: Element[] = [];

    /**
     * IntersectionObserverWrapperの新しいインスタンスを作成する。
     * @param callback 要素の視認性の変化を通知するコールバック関数。
     * @param options IntersectionObserverのオプション、rootMarginの初期値を含む。
     */
    constructor(callback: IntersectionObserverCallback, options?: IntersectionObserverInit) {
        this._intersectionObserver = new IntersectionObserver(callback, options);
        this._callback = callback;
        this._options = options;
    }

    /**
     * rootMarginを更新する。このメソッドを呼び出すと、監視中の全ての要素に対して、
     * 新しいrootMarginが適用される。
     * 
     * @param rootMargin 新しいrootMarginの値（例: `-80px 0px -1000px 0px`）。
     */
    updateRootMargin(rootMargin: string): void {
        // 指定された値が現状と変わらなければ何もしない
        if (this._intersectionObserver.rootMargin === rootMargin) {
            return;
        }
        this._intersectionObserver.disconnect();
        const newOptions = { ...this._options, rootMargin: rootMargin };
        this._intersectionObserver = new IntersectionObserver(this._callback, newOptions);
        this._targets.forEach(target => this._intersectionObserver.observe(target));
    }

    /**
     * 指定した要素を監視対象に追加する。
     * 
     * @param target 監視対象に追加する要素。
     */
    observe(target: Element): void {
        this._intersectionObserver.observe(target);
        this._targets.push(target);
    }

    /**
     * 監視を終了し、全ての要素の監視を停止する。
     */
    disconnect(): void {
        this._intersectionObserver.disconnect();
    }
}


```
52枚のトランプを5人に配るとき、11枚、11枚、10枚、10枚、10枚と配りたい。

## やりたいこと
52枚のトランプを5人に配るとき、11枚、11枚、10枚、10枚、10枚と配りたい。

## 問題
52を3で割って配ると、1人あたり17枚になり、合計が51枚になってしまう。

```ruby
irb(main):001> total = 52
=> 52
irb(main):002> total / 5
=> 17
```
## 考えたこと
・1から52までの数を配列に入れて、5人に順番に渡していく。
・人数分の配列を作り、そこに順番に入れていく。

## やってみる
each_sliceメソッドを使うと指定した数で区切れることがわかった。
配列を用意して、区切った配列を入れていく。
```ruby
numbers = (1..52).to_a

n = 52 / 5

array = []
numbers.each_slice(n) do |x|
  # 均等に配る
  array << x
end
```
5人に10枚ずつ配ったが、2枚余ってしまった。
余った51と52を1人目と2人目に配りたい。
```ruby
[ruby]$ ruby tmp_cart.rb
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
[21, 22, 23, 24, 25, 26, 27, 28, 29, 30]
[31, 32, 33, 34, 35, 36, 37, 38, 39, 40]
[41, 42, 43, 44, 45, 46, 47, 48, 49, 50]
[51, 52]
```

each_with_indexメソッドを使って、最後の配列から各要素を取り出して、順番に渡していく。（余った枚数を配る）
```ruby
array[-1].each_with_index do |x, i|
  array[i] << x
end

array.pop
```
52枚のトランプを5人に配るとき、11枚、11枚、10枚、10枚、10枚と配ることができた。
```ruby
[ruby]$ ruby tmp_cart.rb
[[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 51],
 [11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 52],
 [21, 22, 23, 24, 25, 26, 27, 28, 29, 30],
 [31, 32, 33, 34, 35, 36, 37, 38, 39, 40],
 [41, 42, 43, 44, 45, 46, 47, 48, 49, 50]]
```
Azure Functions

## Azure Functionsとは
Azure Functionsとは、インフラや環境などを心配することなく関数を実行できるサーバーレスソリューションです。アプリケーションの実行やデプロイ、管理について心配せずに最新のソリューションを使用することができます。

## 特徴
Azure Functiionsの特徴は、イベント駆動型で、「トリガー」を設定して、イベントが発生した際に自動で関数を呼び出す機能があることです。また、データの入出力に関する記述を簡単に行える「バインダ」のも特徴です。いろいろな自動化のサービスが存在し、どのサービスでも入力、アクション、条件、出力を定義できます。


- 開発スタイル：コード中心。開発者は具体的なコードを書いて機能を実装します。

- 接続：約10種類のビルトインバインドが利用可能で、必要に応じてカスタムバインドをコードで作成できます。つまり、Azure Functionsのコードで直接StorageやCosmoDBにアクセス、読み書きができるということです。これは標準で搭載されています（ビルドインバインド）。外部のデータへのアクセスも可能です。標準で搭載されていない場合は、自分でカスタムしてバインドする必要があります（カスタムバインド）。

- アクション：個々のアクティビティをコードで記述します。
- モニタリング：Azure Application Insightsを使用して監視します。
- 管理：REST APIやVisual Studioを通じて管理できます。
- 実行コンテキスト：Azure上またはローカル環境で実行可能。

# 補足
 Azure Logic Apps やWebJobsnについても一部取り上げられていますが、あまり気にする必要はないと思われます。コストやスケーリングなどについては感覚でわかると思います。