OmniAICreator/Qiita-1.07M huggingface dataset:
ChatGPT APIをお試しで使ってみる。

## 背景
入力したキーワードからクイズを生成してくれるWebアプリを作りたく、ChatGPT APIを利用するにあたり、準備作業を記事にしてみました。

## 準備

以下のサイトを参考に実施しました。
https://aismiley.co.jp/ai_news/what-is-the-chatgpt-api/

まずはOpenAIのサイトにアクセスしてログインします。
アカウントがない場合は、アカウントを作成します。
https://openai.com/product

ログイン後、以下のサイトからAPI Keysを発行します。
https://platform.openai.com/account/api-keys

こんな感じの画面
![](https://storage.googleapis.com/zenn-user-upload/be923b2f4ec0-20230720.png)

「Create new secret key」を押下して、適当なAPIKey名を入力、
「Create secret key」をクリックしたら、一度だけキーが表示されるので控えておきます。

## テスト実装

APIキーが取れたので、APIを叩いてみます。
過去に作成したアプリのバックエンド資材をコピーしてきてTMDBAPI部分をChatGPTに置き換えてみます。言語はPythonです。

まずは必要なライブラリを落としてきます。
```
pip install openai
```
インストールした結果、0.27.8バージョンが入ってきました。
（openai以外のライブラリは過去に作成したアプリ開発時に入れたものです。）
![](https://storage.googleapis.com/zenn-user-upload/65d19a7dc9cf-20230720.png)

とりあえず最小構成で構築してみます。
![](https://storage.googleapis.com/zenn-user-upload/3d4cf9f8b17f-20230720.png)

test.py
```
import pandas as pd
from flask_cors import CORS
from flask import Flask, request, jsonify
from deep_translator import GoogleTranslator
import openai

app = Flask(__name__)
CORS(app)

@app.route("/")
def test():

  openai.api_key = "{OpenAIから取得したAPIKey}"

  prompt = "こんにちは、私の名前は"
  model = "text-davinci-002"
  response = openai.Completion.create(
    engine=model,
    prompt=prompt,
    max_tokens=5
  )

  return response.choices[0].text

if __name__ == "__main__":
  app.run(debug=True)

```

test.http
```
###
GET http://localhost:5000/
```

test.pyを実行します。
![](https://storage.googleapis.com/zenn-user-upload/96c4c70e27b5-20230720.png)

test.httpから叩いてみたらなんかエラーが出ました。
![](https://storage.googleapis.com/zenn-user-upload/e9b8e5d242f0-20230720.png)

エラーレスポンスがHTMLになっていたので、見てみます。
プラン、料金云々言っています。
![](https://storage.googleapis.com/zenn-user-upload/26f9fecf83cb-20230720.png)

クレジットカードの設定、上限金額の設定を行う必要があるみたいですね。
https://qiita.com/kotattsu3/items/d6533adc785ee8509e2c

Qiitaの通り実施したらAPI叩けました。（アンジェラ Goldって何...？）
![](https://storage.googleapis.com/zenn-user-upload/0d4bb52d6354-20230724.png)

以下のサイトを真似てコードを修正したらそれっぽいものを返却してくれるようになりました。
https://dev.classmethod.jp/articles/openai-api-chat-python-first-step/
```
import pandas as pd
from flask_cors import CORS
from flask import Flask, request, jsonify
from deep_translator import GoogleTranslator
import openai

app = Flask(__name__)
CORS(app)

@app.route("/")
def test():
  openai.api_key = "{OpenAIから取得したAPIKey}"

  response = openai.ChatCompletion.create(
      model="gpt-3.5-turbo",
      messages=[
          {"role": "user", "content": "大谷翔平について教えて"},
      ],
  )

  return response.choices[0]["message"]["content"].strip()

if __name__ == "__main__":
  app.run(debug=True)

```
得られた結果
![](https://storage.googleapis.com/zenn-user-upload/3238da76c24c-20230724.png)

## おまけ

サイトによって少し書き方が違う？
```
const completion = await openAiApi.createChatCompletion({
    model: "gpt-3.5-turbo",
```
```
response = openai.Completion.create(
  engine="davinci",
```
createChatCompletionの方がChatAPIっぽい回答が得られるらしいです。
SESの契約が終わり仕事が無い状態です。

SESは客先常駐なので契約期間が切れたら仕事がなくなります。
今は次の仕事を探すまでの空いた期間なので勉強しています。
なので、ブログも結構更新する時間があります。

勉強といっても何を勉強するか悩みました。
私の経験的には、C++が●年程、C#が2年で、
最近はC#のお仕事をしていたので、もっとC#勉強しなければと思いました。

まず、書籍を読もうと思ってKindleで本を買いました。
①AdaptiveCode
②良いコード悪いコードで学ぶ設計入門
③.Netのクラスライブラリ設計
はっきり言って、どれも難しく最後まで読めそうにないです。

これらは、素晴らしい設計で、これが出来る人は凄いと思うのです。が！


SESで色々な現場を回っていると、コーディング方法はバラバラです。
デザインパターンなんて知ったこっちゃない現場も多いです。
というか、ちゃんと考えている現場なんて殆ど見た事ないです。

もし、ここでデザインパターンマスターした人が入ってきて、
自身の担当箇所だけデザインパターンをバリバリ駆使して作るとどうなるか。
（又はC#の最新バージョンでサポートされているような文法を使って）

恐らく、他の人が全く読めないコードになり、独りよがりな分かりにくいコードを書く人。
という扱いをされると思います。現にそういう現場を経験した事があります。

なぜこういう事が起こるのか理由は色々あると思うのですが、
私の経験では、大抵の現場は既にあるシステムの改修だとか、機能追加が多いです。
１から作るというケースはほぼ無いです。

しかも、20年以上前に作られたコードを使い続けているというケースも良くあります。
そのようなコードは言語のバージョンも古い為、新しい機能は当然使っていませんし、
もし使ったら、使わないようにレビューで指摘を受ける事もあります。

新しい機能や設計を使う事で、よいコードが出来るのかもしれませんが、
他の人がついていけないというデメリットもあります。

つまり、何が言いたかったかというと、
こういう設計手法とか勉強した所で、それを現場で生かす事はあるのだろうか？
と思ってしまいます。

これって分かって貰えるかな。。。
シェルスクリプト　初心者　備忘録


## シェルスクリプト　if文

````
#!/bin/bash

name="${1}"
age="${2}"

if [[ "${name}" == "Taro" ]]; then
        echo "あなたの名前は太郎です"
elif [[ "${name}" != "Hanako" ]]; then
        echo "あなたの名前はHanakoではないです"
else
        echo "あなたの名前はHanakoです"
fi
````

[[スペース"${name}" == "Taro"スペース]]を入れる。
スペースを入れておらずエラーになったため些細なことだが投稿。
Hexabase TypeScript SDKを使ったデモ（画像アップロード）

[Hexabase（ヘキサベース）](https://www.hexabase.com/)は企業においても安心して利用できるBaaS（Backend as a Service）を提供しています。多くのBaaSがそうであるように、主にフロントエンド開発者に利用してもらいたいと考えています。そこで現在、TypeScript SDKの開発が進められています。

この記事ではv2系になって新しくなったHexabase TypeScript SDKのインストールと、画像アップロードの方法を紹介します。

## デモ

以下はデモアプリです。データの登録と、チェックをクリックした際にデータの削除を行っています。

https://stackblitz.com/edit/stackblitz-starters-1g7sny?file=pages%2Findex.tsx,.env,pages%2Fhexabase.ts

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/86fc3738-9e5f-4514-28a1-c15913386a99.png)


## インストール

インストールはnpmやyarnを使って行います。

```
# npmの場合
npm install @hexabase/hexabase-js

# yarnの場合
yarn add @hexabase/hexabase-js
```

## インポート

インポートすると、 `HexabaseClient` というオブジェクトが取得できます。

```jsx
import { HexabaseClient, User } from "@hexabase/hexabase-js";
```

## 初期化

HexabaseClientを初期化します。

```tsx
// 本番閑居向け
const client = new HexabaseClient();

// 開発環境向け
const client = new HexabaseClient('dev');
```

なお、すでに認証済みだった場合に備えて、2番目の引数にトークンを指定できます。

```tsx
const client = new HexabaseClient('dev', 'YOUR_TOKEN');
```

## データストアの準備

データストアでは、添付ファイルのフィールドを持ったテーブルを用意します。今回はフィールド名を `photos` としています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/5dc519c4-d268-98dc-6f71-32fc54ec6f1d.png)


## コード

### 認証

Hexabaseでは業務利用を想定しているため、利用する際に認証情報が必須になります。最初はメールアドレスとパスワードで認証し、その後はトークンを使ってGraphQLにアクセスします。 `client` を使って処理します。

初回の認証は次のようになります。emailとパスワード、またはトークンが必須です。

```tsx
const bol = await client.login({email, password, token});
```

レスポンスは認証成功すれば true 、失敗すれば false が返ってきます。

### 画像アップロード

Hexabaseでは添付ファイルのフィールドに対して、複数のファイルを設定できます。ファイルをアップロードする際には `item.file()` を使ってFileObjectインスタンスを呼び出します。そして、 `name` （ファイル名）と `data` ファイルデータを設定します。

```ts
const upload = async (e) => {
	// ファイルがなければ終了
	const { files } = e.target;
	if (!files[0]) return;
	// 対象プロジェクト
	const project = await client.currentWorkspace.project(
		process.env.NEXT_PUBLIC_PROJECT_ID
	);
	// 対象データストア
	const datastore = await project.datastore(
		process.env.NEXT_PUBLIC_DATASTORE_ID
	);
	// 新規レコード
	const item = await datastore.item();
	// タイトルは1つ目のファイル名を利用
	item.set('Title', files[0].name);
	const ary = [];
	for (const file of files) {
		// ファイルオブジェクトを作成
		const f = item.file();
		f.set('name', file.name).set('data', file);
		ary.push(f);
	}
	// photosフィールドにファイルオブジェクトの配列を指定
	item.set('photos', ary);
	// 保存
	await item.save();
	setItem(item);
};
```

### ファイルダウンロード

ファイルのダウンロードは、取得したFileObjectの配列に対して `download` メソッドを実行します。受け取った結果を `Blob[]` とします。

```ts
const download = async (e) => {
	e.preventDefault();
	const files = item.get<FileObject[]>('photos');
	// ファイルダウンロード
	const promises = files.map((f) => f.download());
	const data = (await Promise.all(promises)) as unknown;
	setPhotos(data as Blob[]);
};
```

画像は `URL.createObjectURL` を使えば表示できます。

```tsx
{photos.length > 0 ? (
	<>
		{photos.map((photo) => (
			<img key={photo.name} src={URL.createObjectURL(photo)} />
		))}
	</>
) : (
	''
)}
```

## まとめ

SDKを使うことで、Hexabaseのファイルアップロードは簡単に利用できます。ぜひWebアプリケーション開発に活用してください。

Hexabaseには他にもスクリプトやAuth0を使った認証などの機能があります。それらの機能も利用してください。

[Hexabase | 新規事業向け開発・競争領域でのDX実現をサポート](https://www.hexabase.com/)
runasというコマンドは惜しい

https://atmarkit.itmedia.co.jp/ait/articles/0606/17/news015.html

管理者権限でコマンドプロンプトで実行したいのですが
Linuxでいう所の
```
su -
```
みたいなのはWindowsには無いのかな(T_T)
SAP BTPへログオンする方法

SAPのチュートリアルは結構英語ベースなので、日本語の勉強メモを残したいと思います。

# やりたいこと
・ローカルからコマンドでSAP BTPへログオン

# 前提
・SAPのアカウント作成済み
・SAP BTPでSubaccountsと作成済み
・Cloud Foundry CLIインストール済み

# やり方

### ①SAP BTP Cockpit＞Account Explore＞Subaccountsをクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/1183a3a5-135e-c2ec-9911-504fcbe10e92.png)

### ②Overview＞General＞Cloud Foundry Environment＞API Endpoint、エンドポイントのURLをコピー


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/b95c9190-7408-09a2-114f-973069663d88.png)

### ③ローカルでコマンドを打つ

```
cf api https://api.xxxxxxxxxx.ondemand.com
cf login
```
すると、メールとパスワードが聞かれ、SAP BTPへログインするときのやつを入れる

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/4a517e11-197b-dd0a-3745-cf78eaae9e1b.png)

以下が出たら成功！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/2ed8523f-0700-ff09-2c61-0e00c3a6f40f.png)



# 参考サイト
https://developers.sap.com/tutorials/btp-cf-buildpacks-node-create.html

https://developers.sap.com/tutorials/btp-app-prepare-btp.html
Jmeterで選択した一部の Thread Groupだけ実行(再生)する

Thread Group を右クリック -> Start で特定の Thread Group だけ実行できるようだ

<img width="437" alt="image" src="https://github.com/YumaInaura/YumaInaura/assets/13635059/244820b2-fbdc-4592-82c8-cf46e1e971c2">



# チャットメンバー募集


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

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


# Twitter

https://twitter.com/YumaInaura
【タスク管理アプリを作る】④タスク詳細表示

タスク一覧で選択したタスクの詳細を表示します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545817/9746fef5-2a40-a4d4-7a62-4e97e7825f62.png)

■仕様
左側のリストボックスにあるタスクを選択すると、
右側にあるグリッドにタスク詳細を表示します。

詳細表示にはPropertyGridというコントロールを使っています。
これが簡単で超便利でした。

■その他
・タスク一覧に同じ名前のタスクがある場合、
　まとめて削除されてしまうバグを直しました。
　タスクIDをキーに削除するようにしました。
・タスクIDの型をクラスにしました。
　今まではintで持っていたのをTaskIDというクラスにしました。
　これは、値オブジェクトという物と考えています。
　（本で読んでまだ完全に理解していませんが。）

■コード
特に特別なコードはないので記載しませんが、
TaskIDクラスというかレコードはこんな感じです。

```cs:TaskID.cs
public record TaskID
{
    const int InvalidId = -1;
    public int ID { get; set; }

    // これは、データが無いとか不正な場合にnullとせず、この値を返す事でnullチェック不要になります。
    // 最近覚えました。
    public static TaskID Invalid = new(InvalidId);

    public TaskID() { }
    public TaskID(int id)
    {
        ID = id;
    }

    // NumericUpDownで使うのでDecimalに変換します。
    public decimal ToDecimal()
    {
        return Convert.ToDecimal(ID);
    }

    // これがないと、PropertyGridでTaskID表示した時に数値が表示されません。
    public override string ToString()
    {
        return ID.ToString();
    }
}
```
ディレクトリ操作・マニュアル確認

## 現在のディレクトリを確認

`pwd`

## ルートディレクトリに移動
`cd /`

## ホームディレクトリに移動
`cd ~`

## 一つ上のディレクトリに移動
`cd ..`

## ディレクトリの内容を表示
`ls`

## 隠しファイルを含む全てのファイルを表示
`ls -a`

## 内容を、詳細なリスト形式で表示
`ls -l`

## ディレクトリの作成
`mkdir`

## ディレクトリの削除

`rm`

`rm -r ディレクトリ名`　　
ディレクトリの時は `-r` をつける

## man コマンド

`man コマンド名`

コマンドのレファレンスマニュアルを表示
「コア業務」、「ノンコア業務」

# 「コア業務」、「ノンコア業務」の特徴
戦略的な業務改革手法「BPR（ビジネスプロセス改革）」に基づく業務効率化を考えるうえで、「コア業務」、「ノンコア業務」を明確にすることは、非常に重要です。

<table>
<tbody>
    <tr>
        <th>コア業務</th>
        <th>ノンコア業務</th>
    </tr>
<tr>
    <td>経営の土台となる業務<br>
        経営の中心的な役割を果たす業務
    </td>
    <td>経営のサポートをする業務<br>
        （業務自体で利益は生まない）
    </td>
</tr>
<tr>
    <td>非定型な業務<br>
    主業務とも言い換えられ、企業存続のために欠かすことができない業務</td>
    <td>副業務とも言い換えられ、会社の利益に直結しないサブ的な位置<br>
    定型、もしくは定型化出来る業務</td>
</tr>
<tr>
    <td>専門的な判断が必要</td>
    <td>高度な判断は不要</td>
</tr>
<tr>
    <td>業務に慣れるまで一定期間が必要</td>
    <td>業務に慣れるまでの時間は不要</td>
</tr>
<tr>
    <td>難易度が高い</td>
    <td>難易度が低い</td>
</tr>
</tbody>
</table>

# BPOのアウトソーシングスタイル
BPOとは、「Business　Process　Outsourcing（ビジネス・プロセス・アウトソーシング）」の略

# アウトソーシング
アウトソーシングとは、業務の一部、またはすべてを外部企業や個人に委託することをいいます。アウトソーシングには、３つの種類があります。

<table>
  <tbody>
    <tr>
      <th> 種類</th>
      <th> 概要</th>
      <th> 特徴</th>
    </tr>
    <tr>
      <th> PUSH OUT（プッシュアウト）型</th>
      <td> 単一業務の外部への委託や一時的な外部への委託する方法</td>
      <td> スポット的な依頼に向いている方法<br>組織から特定のタスクを切り離すことで業務にかかる工数やコストの削減を実現するスタイル</td>
    </tr>
    <tr>
      <th> ADD ON（アドオン）型</th>
      <td> 業務委託だけでなく、専門家に依頼することでプラスアルファの効果を見込む方法</td>
      <td> 納期の短縮や機能の向上を目指したい時に向いている方法</td>
    </tr>
    <tr>
      <th> BUY IN（バイイン）型</th>
      <td> 委託先に企画・戦略・実行を任せる方法</td>
      <td> 委託先のノウハウを活用し運用体制を強化したい時に向いている方法</td>
    </tr>
  </tbody>
</table>

# 引用、参考サイト
https://liskul.com/core-business-113479

https://smartcompany.jp/column/what-is-core-business/
宣言的UIとViewModelの関係

宣言的UIは

ユーザーインターフェースが宣言的に書けるUI

です。

手続き的UIは

ユーザーインターフェースを手続き的に書けるUI

です。


ViewModelはUIから状態とロジックを分離したものです。

Viewを表示レイアウトと入力のみとするためにViewModelを分離します。



UIのViewクラスとViewのデータロジックを分離したものがViewModelクラスです。

UIの種類とViewModelは関係ありません。


宣言的UIでもViewからViewのデータロジックを分離するならばViewModelが必要です。

手続き的UIでもViewからViewのデータロジックを分離するならばViewModelが必要です。

UIの種類とViewModelの要不要は関係ありません。
【Rails】CarrierWaveとMiniMagickで画像を.wepbに変換する

# 画像を.webpに変換したい

と思って記事を探したんですが、無さそうだったので投稿します。

CarrierWaveとMiniMagickの導入方法については多くの記事があるため、今回は割愛させて頂きます。

https://qiita.com/wonder_boooy/items/1510f8750d1282693148

### .webpに変換する方法

具体的な方法ですが、CarrierWaveのUploaderクラスに記載するだけです。

https://github.com/carrierwaveuploader/carrierwave#getting-started

```＄ rails generate uploader 〇〇〇```コマンドで作られたファイルにコードを書きます。🔥マークのコメントが該当部分

```app/uploaders/stamp_image_uploader.rb
class BoozeImageUploader < CarrierWave::Uploader::Base
  # Include RMagick or MiniMagick support:
  # include CarrierWave::RMagick
  include CarrierWave::MiniMagick  #🔥CarrierWaveでMiniMagickが使えるようにする

  # Choose what kind of storage to use for this uploader:
  # storage :file
  # productionでは、fogで外部ストレージにアップロードする
  if Rails.env.development? || Rails.env.test?
    storage :file
  else
    storage :fog
  end

  # Override the directory where uploaded files will be stored.
  # This is a sensible default for uploaders that are meant to be mounted:
  def store_dir
    "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
  end

  # Provide a default URL as a default if there hasn't been a file uploaded:
  def default_url
    'no_image_gray.png'
  end

  # Process files as they are uploaded:　
  process resize_to_fit: [1000, 667]

  # def scale(width, height)
  #   # do something
  # end

  # Create different versions of your uploaded files:
  # version :thumb do
  #   process resize_to_fit: [50, 50]
  # end

  # Add an allowlist of extensions which are allowed to be uploaded.
  # For images you might use something like this:
  def extension_allowlist
    %w[jpg jpeg gif png heic webp]
  end

  # Override the filename of the uploaded files:
  # Avoid using model.id or version_name here, see uploader/store.rb for details.

  #🔥WebPに変換
  process :convert_to_webp

  def convert_to_webp
    manipulate! do |img|
      img.format 'webp'
      img
    end
  end
    #🔥拡張子を.webpで保存
  def filename
    super.chomp(File.extname(super)) + '.webp' if original_filename.present?
  end

```

| 1 | オリジナル | WebP |
|:-:|:-:|:-:|
|5472x3648px |3.0 MB <img width="400" src="https://boozepedia.s3.ap-northeast-3.amazonaws.com/webp/inu.jpg">   | 2.1 MB <img width="400" src="https://boozepedia.s3.ap-northeast-3.amazonaws.com/webp/inu-1.webp">  |
|1000x667 px|302.7 KB<img width="400" src="https://boozepedia.s3.ap-northeast-3.amazonaws.com/webp/inu1000.jpg">   | 207.0 KB<img width="400" src="https://boozepedia.s3.ap-northeast-3.amazonaws.com/webp/inu1000.webp">  |


横幅500pxにしてるので分かりづらいでずが、画質の差にもそれほど違いがないことがわかります。
ただ、WebPに変換するだけでは10~30%程度の圧縮にしかならないので、画像のリサイズは必須かなと思います。

WEBページであれば横幅1000pxあれば充分なようです。

https://flex-box.net/image-optimization/#co-index-3


https://developers.google.com/speed/webp?hl=ja

### 最後になりましたが、うぇぶぴーじゃなくてウェッピーって読むのかわいいです。
[Django] クライアントのIPアドレスを取得してみよう！



## この記事の目的
DjangoでクライアントのIPアドレスを取得する関数の実装方法がわかる

## 使用技術

```
python = 3.11.2
Django = 4.2.3
```

## では早速該当の関数を見てみよう
```sample.py
def get_client_ip(request):
    """クライアントのIPアドレスを取得"""
    x_forwarded_for = request.META.get("HTTP_X_FORWARDED_FOR")
    if x_forwarded_for:
        ip = x_forwarded_for.split(",")[0]
    else:
        ip = request.META.get("REMOTE_ADDR")
    return ip
```

クライアントのIPアドレスを取得することはセキュリティやトラッキングの目的で役立つことがあります

## 取得方法はクライアントがプロキシサーバーを経由しているか否かで決まる
1. プロキシサーバーを経由している場合
    1. `HTTP_X_FORWARDED_FOR`から取得する
1. プロキシサーバーを経由していない場合
    1. `REMOTE_ADDR`から取得する

プロキシサーバー(NginxやApache等)が使用されている場合、`HTTP_X_FORWARDED_FOR`ヘッダーを参照します
このヘッダーが存在する場合、その最初の要素がクライアントの実際のIPアドレスになります


## 参考
- [How do I get user IP address in Django?](https://stackoverflow.com/questions/4581789/how-do-i-get-user-ip-address-in-django)
- [Django Request and response objects](https://docs.djangoproject.com/en/4.2/ref/request-response/)
ものぐさなのでDockerを使わずWiindows版XAMPPでlaravel開発する土台を作る（認証もあるよ）

# 序
筆者はものぐさである。
よって、PHP開発ではDocker＋WSL2ではなく専らWindows版XAMPPを使っているのだが、laravelではDocker＋WSL2が推奨となっている。
フ〇ック。

という訳で筆者なりにXAMPPでlaravelのインストールと認証周りの設定を行う手順を記すことにした。

# 準備
まずあなたのwindowsなりmacなりにXAMPPを入れる。
XAMPPはここで最新版が[ダウンロード](https://www.apachefriends.org/jp/index.html)出来るのでダウンロードとインストールをする事。
もしあなたの会社のセキュリティポリシーが厳しくてインストーラーを使えないのであればシステム管理者にインストール許可を貰うのを忘れずに。

次にcomposerのインストールとパスを通す作業がある。
この手順は[こちらのページ](https://www.sejuku.net/blog/82454)を見ておけば良い

# 開始
コマンドプロンプトを立ち上げる。
そして 
```
cd xamppをインストールしたフォルダ\htdocs 
```
と入力しhtdocsフォルダに移動する。
次に
```
composer create-project laravel/laravel プロジェクトのフォルダ名 "8.*"
```
と入力しエンターキーを押してインストールする。
ここではlaravelのバージョンを8.x系で決め打ちしているが、筆者環境では10.x系の動作が不安定だったので8.xにしている。

# 設定
インストールが終わったら
```
"C:\Program Files\nodejs\npm.cmd" install
```
で依存モジュールをインストールする。
その後
```
xamppをインストールしたフォルダ\htdocs\プロジェクトのフォルダ名
```
を開いてインストールしたフォルダに入る。
直下の.envファイルを開き、xamppデフォルトのデータベース設定を見る。
```
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=root
DB_PASSWORD=
```
DB_HOST、DB_PORTは通常そのままでよい。
もし何かの都合でポートを変えて運用しているPCならポートを書き換える。
DB_DATABASEはこのプロジェクト用にxampp同封のphpmyadminで新規作成してその名前に差し替える。
私はcrmというデータベースを作成したのでこのようになる。
```
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=crm
DB_USERNAME=root
DB_PASSWORD=
```
ここで、xamppからapacheとmysqlを起動し、ブラウザで次のURLを打ち込む。
```
http://127.0.0.1/プロジェクトのフォルダ名/public/
```
そうするとlaravel8のルート画面が出てくるのでまずはこれで良しとする。

# 認証の実装
認証に使うテーブルは最初にインストール済みのマイグレーションファイルから生成できる。
```
cd xamppをインストールしたフォルダ\htdocs\プロジェクトのフォルダ名
php artisan migrate
```
と入力すればマイグレーションファイルからテーブルが生成される。
そのまま
```
composer require laravel/breeze --dev
php artisan breeze:install
npm install
npm run dev
php artisan migrate
```
を実行する。
phpstorm等のIDEに入っているコマンドラインパネルで実行するとphp artisan breeze:install以降のコマンド入力は自動的に行ってくれる。便利。

# 検証
認証アカウントの登録をしたい場合はブラウザで
http://127.0.0.1/プロジェクトのフォルダ名/public/register
登録したアカウントでログインしたい場合はブラウザで
http://127.0.0.1/プロジェクトのフォルダ名/public/login
を参照すればOK
後は公式ドキュメントに沿って必要なコントローラーとアクションに認証をかければOK。

# 微調整
詳細は[こちらの記事](https://qiita.com/aoaoao/items/7278683545b3f5be2bef)を参照。
xamppをインストールしたフォルダ\htdocs\プロジェクトのフォルダ名\resources/views/layouts/app.blade.php
の記述が間違っているので書き換える。
```
//コメントアウト
{{-- @vite(['resources/css/app.css', 'resources/js/app.js'])--}}

//代わりに次の2行追加
    <link rel="stylesheet" href="{{ mix('css/app.css') }}">
    <script src="{{ mix('js/app.js') }}" defer></script>

```
xamppをインストールしたフォルダ\htdocs\プロジェクトのフォルダ名\resources/views/layouts/guest.blade.php
の記述が間違っているので書き換える。
```
//コメントアウト
{{-- @vite(['resources/css/app.css', 'resources/js/app.js'])--}}

//2行追加
    <link rel="stylesheet" href="{{ mix('css/app.css') }}">
    <script src="{{ mix('js/app.js') }}" defer></script
```
次に以下のコマンドを実行
```
cd xamppをインストールしたフォルダ\htdocs\プロジェクトのフォルダ名
npm ci
npm run dev
```
# 編集後記
laravel10で同じ手順を踏むとnpm run devでこけてしまう事象が発生した。
同様の現象に遭遇した人の記事も散見されたが解決したパターンと解決しなかったパターンとであるので、おま環多発地帯の模様。
（識者の方調査求む）
laravel10は2023年現在ではちょっと様子見したほうがいいっぽい？
request という変数について

# **`request`**

**`request`** は、JavaのサーブレットやJSPなどのウェブアプリケーション開発で利用される特別なオブジェクトであり、HTTPリクエストに関連する情報へのアクセスができます。

これは、「暗黙オブジェクト」とよばれ、開発者が明示的に宣言する必要はありません。

具体的には、**`request`** オブジェクトは 「**`HttpServletRequest`** インターフェースのインスタンス」を指し示し、HTTPリクエストに含まれる情報へのアクセスを可能にします。
サーブレットコンテナやJSPコンテナが自動的に **`HttpServletRequest`** の「インスタンス」を生成し、それを **`request`** という変数に自動的にバインドします。

開発者は、この **`request`** 変数を利用して、HTTPリクエストからクエリパラメータやセッション情報、ヘッダなどの情報に簡単にアクセスでき、**`request.getParameter()`** などのメソッドを用いてクエリパラメータを取得することができます。

```java

String parameter = request.getParameter("paramName");
```

このように、**`request`** オブジェクトは、HTTPリクエストを処理する際に必要な情報に簡単にアクセスできます。
MVVMのViewModelはバインドのために必要なわけではありません。

MVVMはソフトウェアを

View
ViewModel
Model

に分ける事です。

Viewから表示データを分離してViewModelに保持します。
理由はViewには状態とロジックを置かないためです。

ViewとViewModelのデータのやり取りにバインドを使用しています。

バインドのためにViewModelがあるわけではありません。
ViewとViewModelと分けて、表示と表示データを繋ぐためにバインドを使用しています。

バインドの代わりにRxを使用する環境もあります。

# バインドのためにViewModelが必要なのではありません。

# ViewModelはViewからデータとロジックを分離するために必要なのです。
衛星画像.tifに標高データを1つのバンドとして加える

作業方法を忘れないための自分用メモの観点が強いです。

# 材料
- 国土地理院からダウンロードしたDEMデータ(.xml)
  - https://fgd.gsi.go.jp/download/mapGis.php?tab=dem
  - 10mメッシュ
- planet Explorerからダウンロードしたplanet doveの衛星画像
  - https://www.planet.com/explorer/
  - 座標系はEPSG:32651 **(使用するデータにより異なります！！！)** 
- QGIS 3.28 "フィレンツェ"


# 手順
## 1.xmlデータをベクタデータ(points)に変換する
基盤地図情報ビューアを使用します。説明書は↓

https://fgd.gsi.go.jp/otherdata/tool/fgdv_manual.pdf

特にこれのpp16「標高メッシュをシェープファイルへ出力」が参考になると思います。
この作業で、標高の情報を持つポイントデータを得られれば成功です。

## 2.ポイントデータをラスタへと変換
ここからはQGISでの作業です。
プロセシングツールボックスより「ベクタをラスタ化(rasterize)」を選択。
パラメータは以下の通り
- 入力レイヤ:先ほど作成したポイントデータ.shp
- 出力ラスタサイズの単位:地理単位
- 水平、鉛直方向の解像度:15(=ポイントデータの間隔の1.5倍を目安に)
- ラスタ化:出力したいファイルへのパス

これでラスタ型のDEMが作成されました。


## 3.DEMラスタ、衛星画像ラスタの座標系とセルサイズを揃える

DEMラスタのセルサイズは15m、座標系は2458
衛星画像ラスタのセルサイズは3m、座標系は32651
これだと解析も何もないですね。
衛星画像のデータの座標系等はあまりいじりたくないので、DEMラスタを衛星画像ラスタに合わせていきます。

**作業前に、衛星画像ラスタのCRS、セルサイズ(=解像度)と「幅、高さ」をメモっておきましょう**
QGISメイン画面左のレイヤをダブルクリック→ⓘ情報を選択すれば表示されます
余裕があれば領域もメモ

上のメニューから「ラスタ(R)」→「ラスタを揃える」を選択。なぜかプロセシングツールボックスにはありません。
「サイズ、範囲、CRSなどを揃えるラスタ」の右にある＋ボタンをクリックし、
- 入力ラスタ：2で作成したラスタ型DEMを指定
- 出力ファイル名：「ブラウズ」から自分が出力ファイルを保存したいパスを指定
- リサンプリング方法：こだわりがなければ多分最近傍でいいと思います
- セルサイズに応じてスケール：チェックを入れる（自分は入れない場合で試せていません。コメント欄で教えて）

その後、OKを押した後のパラメータは以下の通り
-　座標参照系：チェックを入れ、衛星画像ラスタのCRSを選択する
-　セルサイズ：チェックを入れ、衛星画像のセルサイズを入力。単位はおそらくメートルです
-　グリッドオフセット：チェックを入れ、どちらにも0を入力
-　切り抜く範囲：チェックを入れ、「次をもとに計算」の「レイヤ」から衛星画像ラスタを選択。

すべてしっかりと入力できていれば、「出力サイズ」に表示される数字が事前にメモっておいた衛星画像ラスタの幅×高さと一致するはずです。
座標系、セルサイズ、範囲が同じなので当然と言えば当然
これが異なる場合はここからの作業もうまくいかないはずなのでどこかを見直そう


## 4.結合(GDAL_merge)

ここまでくればあとは簡単なはず。

プロセシングツールボックスから結合(gdal_merge)を選択
- 入力レイヤ：作成した2個のラスタだけにチェック。「衛星画像→DEMデータ」の順にチェックすると、完成したラスタの最後のバンドにDEMデータが入るのでわかりやすいです
- 各ファイルを別のバンドに格納する：チェックを入れる
- 出力データ型：細かい方に合わせた方がいいと思います

出力レイヤにパスを指定し、OKを押す。
できたレイヤが表示されるので、これを確認して終了です。


# 気になる点

作業1の後、DEMのポイントデータの属性名が「標高」という2バイト文字を使って記されているのが気になった。
まあ意外と大丈夫かもしれないけど私は念のためelevationという1バイト文字に変更しておきました。
変更は「プロパティ→属性を管理→名前」をダブルクリックでできます
[Maya] Error: Cannot find procedure "CgAbBlastPanelOptChangeCallback"の脱し方

Mayaで出てくる地獄のメッセージの対処法をメモ。
皆さんは公式の対策を読み解けたと思うんですが私は読み解けずはまりました…。

https://www.autodesk.co.jp/support/technical/article/caas/sfdcarticles/sfdcarticles/kA93g0000000TnW.html

【方法１】
手順：
（追加）Mayaを起動する。起動後に「1～5」の作業を行う。
　　　　※問題のファイルを開いてしまったらMaya再起動。
　　　　（もしかしたら開いててもいいかもだけど）
1.メインメニューから ウィンドウ > 設定/プリファレンス > プリファレンス を選択する。
2.UI 要素 カテゴリのパネルの構成で [保存するとき:] [開くとき:]の両項目のチェックボックスをオフにする。
3.プリファレンスを保存する。
4.エラーが発生した scene file を読み込み、エラーメッセージが表示されるか確認する。
5.エラーメッセージが表示されないのを確認したら scene file を保存する。
（追加※1）Maya再起動して問題のファイルを開く。
（追加）エラーが消えてたら[保存するとき:] [開くとき:]の両項目の設定戻したかったら戻す。

※1）うまく行かなかったらここもやってみてください。

再起動必要かわからないところあってそこまで検証してないけど、最初だけなので手間だけどやっちゃうといいと思います。

追記：問題ファイルを開いた後に問題ないファイルを開いて保存したら伝染するので、問題起きたら保存せずに閉じましょう。
計測機器でよく使われる TCP と UDP のポート番号

TCP と UDP では宛先アプリケーションを識別するため 1 番から 65535 番までのポート番号が使われます。１つのアプリケーションで複数のポート番号を使う事もできます。いくつかのポート番号は「一般的に知られているポート番号」（ウェルノウン・ポート、well - known port number）として特定のアプリケーション・サービスに割り当てられています。

この記事では制御機器や計測機器でよく使われるウェルノウン・ポートを示します。
IANA > [Service Name and Transport Protocol Port Number Registry](https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml)

|サービス名|ポート番号|プロトコル|説明|
|---|---|---|---|
|FTP-data|20|tcp|File Transfer（Default Data）|
|FTP|21|tcp|File Transfer（Control）|
|SSH|22|tcp|SSH Remote Login Protocol|
|Telnet|23|tcp|TELNET|
|SMTP|25|tcp|Simple Mail Transfer|
|DNS|53|udp|Domain Name Server|
|DHCPv4-server|67|udp|Bootstrap Protocol Server(DHCP)|
|DHCPv4-client|68|udp|Bootstrap Protocol Client(DHCP)|
|HTTP|80|tcp|World Wide Web HTTP|
|POP3|110|tcp|Post Office Protocol-Version 3|
|VXI-11|111|tcp/udp|[VXI-11の特徴](https://qiita.com/mmake/items/ee837fd1b8f43e464856#vxi-11-%E3%81%AE%E7%89%B9%E5%BE%B4)|
|SNTP|123|udp|Network Time Protocol|
|NetBIOS-ns|137|tcp/udp|NetBIOS Name Service|
|NetBIOS-dgm|137|udp|NetBIOS Datagram Service|
|NetBIOS-ssn|139|tcp|NetBIOS Session Service|
|PTP-event|319|udp|PTP Event|
|PTP-general|320|udp|PTP General|
|HTTPS|443|tcp|http protocol over TLS/SSL|
|Modbus|502|tcp|Modbus Application Protocol|
|DHCPv6-client|546|udp|DHCPv6 Client|
|DHCPv6-server|547|udp|DHCPv6 Server|
|FTPS-data|989|tcp|file data over TLS/SSL|
|FTPS|990|tcp|file control over TLS/SSL|
|POP3S|995|tcp|POP3 over TLS protocol|
|HiSLIP|4880|tcp|IVI High-Speed LAN Instrument Protocol|
|Scpi-raw|5025|tcp|IEEE 488.2 (SCPI) Socket|
|Scpi-telnet|5024|tcp|IEEE 488.2 (SCPI) Telnet|
|LXI-eventsvc|5044|udp|LXI Event Service|
|mDNS|5353|udp|Multicast DNS|
[002] RC回路のステップ応答~ラプラス変換(微分形)~

tex記法とMarkdown記法の練習を兼ねて，図1のRC回路のステップ応答を求めてみる．

```plantuml
@startuml
<style>
root {
  Padding 0
  Margin 0
}
title {
  FontStyle normal
  FontColor black
  FontSize 20
}
</style>
title
<img:https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/615014/6cb7dffb-d65f-cb9d-4fea-8eec094afcff.png>
図１ RC回路
end title
@enduml
```


コンデンサ$C$に流れる電流$i(t)$は，
$$i(t)=C\frac{dv_o(t)}{dt}$$

と表される．
この式を使って，キルヒホッフの電圧則を表すと，

$$E=RC\frac{dv_o(t)}{dt}+v_o(t)$$

$\mathcal{L}[v_o(t)]=V_o(s)$として，両辺をラプラス変換すると，

$$\frac{E}{s}=RC[sV_o(s)-v_o(0)]+V_o(s)$$

コンデンサの初期電荷はないものとして（すなわち$v(0)=q(0)/C=0$として），$V_o(s)$について式を整理すると，

```math
\begin{align}
V_o(s)&=\frac{E}{s} \cdot \frac{1}{1+sRC}\\
&=E\left( \frac{1}{s} - \frac{RC}{1+sRC}\right)\\
&=E\left( \frac{1}{s} - \frac{1}{s+\frac{1}{RC}}\right)
\end{align}
```

逆ラプラス変換で$v_o(t)$を求めると，
$$\therefore v_o(t)=E\left(1-e^{-\frac{1}{RC}t}\right)$$

$i(t)$は$v_o(t)$の時間微分より，
$$\therefore i(t)=C\frac{dv_o(t)}{dt}=\frac{E}{R}e^{-\frac{1}{RC}t}$$


電圧$v_o(t)$と電流$i(t)$を再掲すると，

```math
\begin{align}
v_o(t)&=E\left(1-e^{-\frac{1}{RC}t}\right)\\
i(t)&=\frac{E}{R}e^{-\frac{1}{RC}t} 

\end{align}
```
【HTML】Tableタグ：HTML編

🌻【Tableタグ】HTML編

💡上から1行ずつ、
💡左から右に表を埋めていくイメージ

・1つ目の「tr」の中は
　　「項目名、見出し」＝「th」を使う

・2つ目以降の「tr」の中は
　　「内容、情報」＝「td」を使う

スタイリングはまた明日！

👇Codepen
https://codepen.io/tomok7/pen/abPYdLQ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2525847/3c5e2a37-7bed-0eb3-b5ad-ed5d920b48b1.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2525847/d6faea06-25d0-bddf-0f23-31c7220b4d19.png)
Azure Portalで使えるショートカットキー

### はじめに
Azure Portalにはショートカットキーが用意されているのをご存知でしたか？よく使えるだろうおすすめのショートカットキーをご紹介します。

### よく使えるショートカットキー一覧

| No. | アクション | ショートカットキー |
|:-:|:-:|:-:|
| 1 | サイドバーのアイテムのページに遷移 | G+number |
| 2 | ダッシュボードへ遷移 | G+D |
| 3 | すべてのリソースへ遷移 | G＋A |
| 4 | リソースグループ一覧へ遷移 | G＋R |
| 5 | 検索ボックスを選択 | G+/ |

特にご紹介したいのは1. サイドバーのアイテムのページに遷移です。お気に入りに登録しているAzureサービスはサイドバーからアクセスできるのですが、実はこれにショートカットキーが対応しています。例えば私の場合Azure Automanageのページを開きたい場合にG+2を押下することで該当のページを開くことができます。よく使うサービスをお気に入りに登録、ショートカットを利用してAzure Portalを便利にしていきましょう。

![Shortcut.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3322105/ed91b886-f37d-2825-34ad-321c829cbd50.png)

### 参考
Azure Portalで利用できるショートカットキー一覧はMicrosoftドキュメントから確認することが可能です。

https://learn.microsoft.com/Ja-jp/azure/azure-portal/azure-portal-keyboard-shortcuts?WT.mc_id=docs-azuredevtipsvideo-azureappsdev
UnrealEngineで...を左から順に表示していくようなマテリアルを作ってみた

UnrealEngine5.2.0で以下のように段階的に表示するUI用のマテリアルを作成したので内容をメモしておきます。
![dot_tenmetu.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16768/c037346a-0c1a-89cd-fdf4-a0626f79d044.gif)

マテリアルの中身は以下のようになります。

![スクリーンショット 2023-09-22 080945.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16768/e5f4673e-4670-90ab-5385-c57ff186a218.png)
ようつべ体験記11

[前回](https://qiita.com/matsukatsu/items/d80f8274b4ac1d0e4fd7)

右肩上がりに視聴回数が上がり、コンスタントに100回/日を超えはじめ、そのまま一気に200回/日をうかがおうかという矢先、突如として12時間ほど無風になりました。

![詳細.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/423506/766deb16-b167-1061-f5a8-ce42aa50e2f6.png)

チャンネル全ての動画が無風…
おわた…

上り調子だっただけに、突然の無風に恐怖しました。
チャンネルそのものが何か悪いことをしたのか、全ての動画が悪いことをしたのか。

考えてもすぐに答えは浮かびません。

視聴回数はピークよりは減っているものの、公開した動画の良し悪しによっても上下するので違和感はあまりありません。

![コンテンツ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/423506/82a4e23f-aa11-0892-e9a5-f420566ae355.png)

インプレッション数は右肩上がりで、逆に好調に見えます。

![コンテンツ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/423506/b2286477-c663-b987-ac75-e5bfe0c7db85.png)

インプレッションのクリック率は、4%とか5%を維持していたのに突如2.8%まで下がり、そこから下落し続けています。
これは、なんだか良くない臭いがプンプンします。

![コンテンツ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/423506/9f7d54d6-0415-3777-88e9-b25007505bad.png)

突如下落した日、全ての動画に及ぼす悪影響…
思い出しました。
作業記録を見返すと、まさにその日にやった事がありました。

最近、YouTubeのチャンネル運営に関する動画を見てノウハウを学んでおり、その中で関連するキーワードを設定しておくと良いよという事を学びました。
また同様の動画で「できる事をやれば必ずチャンネル登録者1000人達成できます！」みたいなのを見て素直に「だよね、やらなくちゃ」みたいな心境になってしまいました。

そういう心境で、各動画と関連が強そうなキーワードを調べて、ハッシュタグに追加していったのでした。

![コンテンツ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/423506/ba031cdc-753e-35ca-37de-e13c7658daca.png)

これは推測ですが、ハッシュタグが増えた事により従来より広範にインプレッションするようになり（インプレッションは右肩上がり）、しかし新しい客層には受けずにクリックされずクリック率が下がり続け、その中でより新しい客層に比重を置いてインプレッションした結果、無風の状態に陥ったのではないか。

確かめようがないですが、あり得そうです。
すぐに全ての動画の編集画面を開いて元のハッシュタグに戻してまわりました。
戻すとすぐに視聴回数が上がりはじめました。

どうやら、自分が設定したハッシュタグが原因だったようです。
右肩上がりだった視聴回数に5日間も自分自身でブレーキを掛けてしまっていたようです。

ハッシュタグを戻してすぐに、直近で公開した動画の視聴回数が戻りました。
1日経ち、このチャンネルで1番見られている動画の視聴回数が戻りました。

今回の無風事件が起こる直前まで、1番見られている動画と、西ヨーロッパ1、2が継続して視聴回数を稼ぎ続け、それに加えて直近で公開した2つの動画が入れ替わりで短期的に視聴回数を稼ぐという5本体制ができていました。
無風事件から2日経ちましたが、西ヨーロッパ1、2の視聴回数は戻りません。

[次回](https://qiita.com/matsukatsu/items/8034d639340dbf5346d3)
Mac版teemsアプリで"アカウント切り替えが検出されました"と表示され現アカウントでログインできない不具合の対処法

※以前使用していたアカウントで勝手に組織のページに飛ばされてしまい、そこでログインIDを変えてログインしようとすると"アカウント切り替えが検出されました"と表示され、再起動するもまた同じ繰り返しになる不具合の対処法になります。



対処法
-
1.Finderを開きMac左上のタブで移動を選択
2.移動タブのフォルダに移動で以下のコマンドを入力して移動。
[ /Users/saharakaiya/Library/Application Support/Microsoft ]
3.移動したフォルダ内にあるteemsフォルダをゴミ箱へ。
4.再びteemsを開くと不具合が消えているはずです。

参考にしたWebページ
[teemsのキャッシュクリア](https://www.uvm.edu/it/kb/article/clearing-teams-cache/)
【Go】環境ごとの設定値管理

Go で web server を立てる際に、環境（staging とか production とか）ごとの設定値をどういうふうに管理し、アクセス可能にするかのメモ。

外からの値の受け取り方としては環境変数から取るか、コマンドラインフラグで渡すかの2択だけど、どちらを取ったとしてもアプリケーションコードのロジック中からその値をどのように参照するかという話がある。
毎回 `os.Getenv` とか書きたくないし、このアプリケーションにどのような設定を入れられるのかがわかりづらいので、どこかで一元管理したい。

そこで、以下のように設定管理用の `myconfig` というパッケージを作っておいて、そこに集約させてみる。
設定値を持った構造体をパッケージグローバル変数に突っ込んでおいて、その構造体経由で設定値を参照できるようにする。

```myconfig.go
package myconfig

import (
    "github.com/caarlos0/env/v6"
    /*...以下省略*/
)

var cfg config

type config struct {
	DBName       string `env:"DB_NAME" envDefault:"hoge_test"`
	DBUser       string `env:"DB_USER" envDefault:"root"`
	DBPass       string `env:"DB_PASS" envDefault:"password"`
	DBHost       string `env:"DB_HOST" envDefault:"localhost"`
	DBPort       int    `env:"DB_PORT" envDefault:"3306"`
}

// パッケージ初期化時に一度だけ環境変数をロードし、cfg 変数にセットします
func init() {
    if err := env.Parse(&cfg); err != nil {
		panic(fmt.Sprintf("failed to setup config, error: %s", err))
	}
}

func DBName() string {
	return cfg.DBName
}

func DBUser() string {
	return cfg.DBUser
}

func DBPass() string {
	return cfg.DBPass
}

func DBHost() string {
	return cfg.DBHost
}

func DBPort() int {
	return cfg.DBPort
}
```

値を使う側はこんな感じで、`myconfig` パッケージに用意されたゲッター経由でアプリケーションのどこからでもアクセスできる。

```main.go
func main() {
    fmt.Println(myconfig.DBName())
}
```

値は全て環境変数から取る前提である。
一応ポイントというか解説というか

- 環境変数から値を取り出す処理は [ライブラリ](https://github.com/caarlos0/env)を使って楽している
    - ライブラリに依存したくない場合は、自分で書いても全然良い
    - フィールドを export しているのはライブラリから参照させるためなので、自分で書く場合はフィールドも unexported にして良い
- init 関数で初期化し、環境変数から取得した値をセットした構造体を作って、グローバル変数に入れておく
    - init 関数ではなく自前の initialize 関数を用意して明示的に呼ぶ形でも良い
        - その場合、シングルトンパターンで `config` 構造体の初期化は一度のみに制限するなどの工夫をした方がいいかも
- ゲッターのみを提供することで、不用意に設定値を変えてしまうリスクをなくす
- `config` 構造体のフィールドで設定値を一元管理しているので、どのような値を設定できるのかの確認がしやすい

## ローカル開発用に .env ファイルなどを使いたい場合

こんな感じで、.env と併用させることもできる。
.env ファイルがあるときはそれを読み込んで値をオーバーライドするようにしておく。
（.env ファイルへのパスは適宜合わせる）

```myconfig.go
package myconfig

import (
    "github.com/joho/godotenv"
    /*...以下省略*/
)

var cfg config

type config struct {/*...省略*/}

// パッケージ初期化時に一度だけ環境変数をロードし、cfg 変数にセットします
func init() {
	// 開発環境以外では .env を使用しないのでここのエラーは無視します
	_, b, _, _ := runtime.Caller(0)
	_ = godotenv.Load(filepath.Join(filepath.Dir(b), "../.env_dev"))

    if err := env.Parse(&cfg); err != nil {
		panic(fmt.Sprintf("failed to setup config, error: %s", err))
	}
}
```

## flag と環境変数を併存させたい場合
あまりないかもだが、flag と環境変数を併存させてみる。この場合、両方設定されているときにどちらの値を優先的に評価するかを決めておく必要がある。
この例では、flag の値を優先している。


```myconfig.go
type config struct {
	WriterDBName       *string
}

func init() {
    cfg = config {
        WriterDBName: flag.String("DB_NAME", getEnvString("DB_NAME", "hoge_test"), "db name")
        /*...省略*/
    }
}

func getEnvString(envKey string, defaultVal string) string {
	if envVal, isSet := os.LookupEnv(envKey); isSet {
		return envVal
	}
	return defaultVal
}
```

- `flag.String` の第二引数が flag が設定されていない時のデフォルト値
    - その値を環境変数から値が取れればその値を、取れなければ引数に与えたデフォルト値を返すようにしている
- flag > 環境変数 > デフォルト値、という順で評価されるようにしている
【タスク管理アプリを作る】③タスクデータを保存、読出する。

タスク管理アプリに以下の機能を追加してみました。
・追加したタスク情報を保存、読み出し。
これで、いちいち再起動後に再登録する必要がなくなりました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545817/9369fbb4-257f-2fac-eaa4-fad50af45f0f.png)

※もっとこうしたら良いととか、ここ問題あるとか軽くでも頂ければ喜びます。
　GITHUB公開については、やり方調べ中。
　publicにするだけでよいのかどうかが分かっていません。

■コードの説明
・XML形式で保存するので、XMLSerialierを使う。
・タスクデータクラスを作る。
・マスタデータクラスを作り、タスクデータクラスのリストを持たせる。
・マスタデータクラスをシリアライズする。

■躓いた点
・マスタデータクラスをシリアライズすると、以下の例外になりました。
　原因はタスクデータクラスにデフォルトコンストラクタがない為でした。
　例外の内容からは分からないので解決に時間が掛かりました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545817/480d7b94-7e35-66d3-290d-6b7ebfeac220.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545817/dfaa6c75-a4de-2424-c8d4-10880c00014c.png)

■実装
```cs:TaskData.cs
public class TaskData
{
    public enum TaskType
    {
        Active,
        Deactive,
        None,
    }

    [XmlIgnore]
    const int InvalidTaskId = -1;

    // シリアライズしたいプロパティ
    public int Id { get; set; } = InvalidTaskId;
    public string Name { get; set; } = string.Empty;
    public string Description { get; set; } = string.Empty;
    public TaskType Type { get; set; } = TaskType.None;

    public static TaskData InvalidData()
    {
        return new TaskData(InvalidTaskId, string.Empty, string.Empty, TaskType.None);
    }
    public TaskData() { } // これがないと例外になる。
    public TaskData(int id, string name, string description, TaskType type)
    {
        Id = id;
        Name = name;
        Description = description;
        Type = type;
    }
}
```

```cs:MasterData.cs
public class MasterData
{
    public List<TaskData> TaskData { get; set; } = new();
}
```

```cs:FileManager
public static class FileManager
{
    public static bool XmlSerialize(string fileName, MasterData writeData)
    {
        var xmlSerializer = new XmlSerializer(typeof(MasterData));
        using (var streamWriter = new StreamWriter(fileName, false, Encoding.UTF8))
        {
            xmlSerializer.Serialize(streamWriter, writeData);
            streamWriter.Flush();
        }

        return true;
    }
}
```

```xml:シリアライズしたXMLファイル
<?xml version="1.0" encoding="utf-8"?>
<MasterData xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <TaskData>
    <TaskData>
      <Id>0</Id>
      <Name>タスク①</Name>
      <Description>タスク①説明</Description>
      <Type>Active</Type>
    </TaskData>
    <TaskData>
      <Id>1</Id>
      <Name>タスク②</Name>
      <Description>タスク②説明</Description>
      <Type>Active</Type>
    </TaskData>
  </TaskData>
</MasterData>
```
VPCエンドポイント経由でS3にアクセスしてみる

## 今回やりたいこと
-　VPCエンドポイント経由でS3にアクセス
-　VPCエンドポイントにポリシーを設定
-　S3にバケットポリシーを設定
-　セッションマネージャーを使用してS3のオブジェクトをダウンロード


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/636718/0d93105f-3d2d-d595-33aa-35edabc839d3.png)

## S3用のエンドポイントの作成

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/636718/914885cb-f686-8a82-5ef8-50f4fb608640.png)


ポリシーも作成

```json:json
{
	"Version": "2012-10-17",
	"Statement": [
		{
			"Effect": "Allow",
			"Principal": "*",
			"Action": "s3:GetObject",
			"Resource": "arn:aws:s3:::vpc-endpoint-test-bucket2/*",
			"Condition": {
				"IpAddress": {
					"aws:SourceIp": "10.0.0.13"
				}
			}
		}
	]
}
```
Resourceで指定したバケットに対してGetObjectを許可しています。
Conditionでこのエンドポイント経由するEC2のプライベートIPを指定しています。

## ルートテーブルにエンドポイントを追加

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/636718/bf826b79-3e36-1892-72c7-436ea7520dda.png)

エンドポイントと追加方法とIAMのロール、ポリシーの設定方法については解説しません。
申し訳ございませんが、他の方の記事を参考にしてください。

## S3のバケットポリシーの作成

```json:json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:*",
            "Resource": "arn:aws:s3:::vpc-endpoint-test-bucket2/*",
            "Condition": {
                "StringEquals": {
                    "aws:sourceVpce": "vpce-004cca88eeb224ebf"
                }
            }
        }
    ]
}
```

Conditionで先ほど作成したVPCエンドポイントの通信を許可するように設定しています。

## S3のオブジェクトをダウンロードできるか試してみる

```
sh-5.2$ aws s3 cp s3://vpc-endpoint-test-bucket2/test.txt /home/ssm-user/test
fatal error: An error occurred (403) when calling the HeadObject operation: Forbidden
```

403エラーなので権限がない
たぶんS3のACL関係？？(調査中)

とりあえずVPCエンドポイントのポリシーのコンディションを削除したらできた
```json:json
{
	"Version": "2012-10-17",
	"Statement": [
		{
			"Effect": "Allow",
			"Principal": "*",
			"Action": "s3:GetObject",
			"Resource": "arn:aws:s3:::vpc-endpoint-test-bucket2/*"
		}
	]
}
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/636718/c40b98f0-2695-63cb-8e83-243f41384aea.png)
Node.jsのutilモジュールを利用する

## utilモジュールとは
公式の説明では、`このnode:utilモジュールは、Node.js内部APIのニーズをサポートします。`と書かれているが、私にはよく意味がわからなかった。
→ 要は、ユーティリティ関数 (よく使う処理を簡単に呼び出せるように作成される小さな関数) を提供しているモジュールですということで良さそう。


## できること
主な関数。シンプルな機能が提供されている。

| Method | Description | 
|:-----------|------------:|
| debuglog()     | デバッグメッセージを出力する  | 
| deprecate()       | 指定した関数が呼び出されたとき、非推奨としてwarningを出す | 
| format()         | 指定した引数に応じて文字列をフォーマットする          | 
| inherits()       | 継承をutil.inherits(Child, Parent);の形で手軽に行える |
| inspect()    | オブジェクトを文字列化する  | 

### その他の使えそうな機能
###  util.types 
様々な種類の組み込みオブジェクトの型チェックを提供する
```
(例)
util.types.isArgumentsObject(value): 引数がオブジェクトであればtrueを返却
util.types.isAsyncFunction(value): 引数が非同期関数であればtrueを返却
```
他にも[様々な型チェック](https://nodejs.org/api/util.html#utiltypes)が可能。

## できることの補足
- inherits()では下記のようなことが行われて継承される。
`Child.prototype  = {constructor: Child, __proto__: Parent.prototype}`
- util.inspectは%oフォーマッターを使った時に内部で呼ばれている関数、らしい。
この内容は[こちらの記事](https://qiita.com/yosuke_furukawa/items/0019e34b2c43fa9e76eb)が大変分かりやすい。
転職活動で失敗したことと、その対応について

## 概要
私は転職活動を4回経験しています。
4回中2回転職に至りましたが、残りの2回は途中で転職活動を止めています。
止めた理由を追求すると、失敗したな、と感じる点がいくつかありました。この記事では何が失敗だったかや反省点、どのように対応したかをまとめました。
記事にした意図は、次回転職する際の自分向けの備忘と、読者の方が反面教師にしてもらえれば、という思いからです。

## 略歴
30代のシステムエンジニア。新卒から継続してインフラ屋。
転職活動自体は4回経験。転職まで至らなかった活動が2回あり。


## 転職目的が何かを転職活動前に突き詰めておくこと
### 失敗したなと思うこと
とある転職活動のタイミングでは、有難いことに複数社から内定をいただきましたが、オファー条件を拝見・比較した際に、しっくりこないと思う自分がいました。今となっては、心のどこかで内定がゴールのように感じ、内定さえ出れば自分の期待を超えるオファーが来ると思っていたと感じます。

### 反省ポイント
転職を決断するポイントは主に以下だと感じました。

**転職の目的が達成できそうかがしっかり腹落ちするか**

表面的な転職動機で取り繕うのではなく、自身の内面を認識する必要があると思いました。
特に不満に思うことは、その時の瞬間的な気持ちに左右されやすいものがあると思っています。瞬間的に感じることは、解消も刹那的であるため、転職を決めるまでの数か月で立ち消える可能性があります。つまり、内定を得る頃には「何のために転職してるんだっけ？」となってしまう恐れがあると思います。だからこそ、不満に思うことは明文化し把握することが大事だと感じます。

転職の目的は、転職活動を始めてから考えるのでは遅いと思っています。ヒントは日常の業務の中にありますで、普段感じたことなどを細目にメモしておくのが良いと思いました。

### 反省を踏まえやってみたこと
具体的に私は以下段取りでメモを取っています。
　1.嫌だ・不満だと感じることを思ったときに記載
　2.分類を記載(ex.業務内容、人間関係、環境など)　
　3.どの程度続いているかの期間を記載

上記のメモを2,3か月ごとに見直し、今もそのように思うかを確認するようにしました。結果的に不満に感じていることを文書化し、その不満が慢性的かを知ることができました。
このメモは転職する際の一つの軸になったと思います。

## エージェントの方との最初の打合せは受身にならないこと
### 失敗したなと思うこと
転職サイトに登録すると「一度お会いして話しませんか？」とのお誘いがありました。特に何の準備もせず軽い気持ちで打ち合わせに臨んだところ、明確な意思無く複数社の面接を受けることとなりました。その後、選考は進みましたが気持ちが追い付かず、結局曖昧な形で辞退、という形になりました。

### 反省ポイント
転職エージェントの方とのファーストコンタクトで受け身の態勢を取ってしまうと、**曖昧な形で話が進んでしまう**と感じました。この結果、転職の判断が出来ずに途中辞退、勢いで転職を決断するものの長くは続かない、という事態になる可能性があると思います。これは、転職エージェントの方だけではなく企業側の方にも迷惑をかけてしまう不幸なことだと感じます。
転職エージェントの方との会話の中で転職の解を見つけられる方もいらっしゃいますが、私の場合はそうではありませんでした。

よくよく考えると、転職エージェントの方はボランティアで会ってくれるわけでは無いという結論に至りました。だからこそ、事前準備を行い対等に話が出来るようになることが大切だと感じました。

### 反省を踏まえやってみたこと
具体的には以下の点を準備し、転職エージェントの方とのファーストコンタクトに臨むようにしました。
　・転職活動に対して現時点でどの程度、真剣に考えているか
　・何を悩んでいて転職を考えており、その原因は何にあるか
　・転職エージェントの方に何をして欲しいか

ここら辺をまとめてから打ち合わせに臨むことで、お互いの期待値がすり合ったように感じ、前向きに転職活動に取り組むことが出来たと感じます。

## 他者との比較はしないこと
### 失敗したなと思うこと
退職エントリーの記事などで同年代の方の待遇や業務内容を羨ましいと感じ、私もこれぐらいならイケるか、と感じながら転職活動を進めていました。ただ結果的に、年収の水準が低かったり、業務内容がしっくりこないと感じるものがあるなど、一喜一憂しながら本質を見失って転職活動をしていました。結果的に「何のために転職活動してるんだっけ？」と疑問を抱き、転職活動からフェードアウトしてしまいました。

### 反省ポイント
他者の方の羨ましいと感じる内容は、結局は他者の方の一面のみということを自覚すべきだと思いました。
よく「自分の敵は自分」のようなキャッチコピーを目にしますが、これは自然体だと他者比較をしてしまうからこその標語だと思い至りました。月並みですが、他人と比較したから何なの？であり、意味は無いと感じます。

### 反省を踏まえやってみたこと
将来の自分の年表を作成し、そこを目指すようにするようにしました。例えば5年後は年収1000万を超える、10年後は管理職になる、などの目標を立て明文化するようにしました。
私の場合は文書化したことで、自然と他者ではなく自分と向き合うことが出来ようになったと思います。

## 終わりに
私の転職活動は、ここまでに記載したような失敗と反省を繰り返しながら前に進んできたと思っています。その中で、ご支援いただいたエージェントの方や面談にて親身になってくださった企業の方には申し訳ないと思う反面、非常に感謝しております。
転職活動は一人ではなく様々な方とのコミュニケーションを通して実現するものであるからこそ、事前準備を入念に行い真摯に取り組む必要があると感じます。
この記事が少しでも読者の方の糧になることができれば幸いです。
iOS UI/UX試験 (ゆめみ社内認定資格)に合格した

https://www.notion.so/yumemi/iOS-UI-UX-e8c0ebbed1e1472bb82ea5f3b49dec74

## 感想
以下のことがわかってためになった。
- iOSアプリにおけるUIの名称
- それらUIの特性や意味
- それらUIの推奨事項や禁止事項

試験範囲となるpdf資料はスライド形式だった。それぞれスライド作成者が違うため見づらい資料もあった。しかし、各資料に対応する小テストがあり資料内容に沿っているため内容を見落とすことはなさそう。
最終試験も小テストに出てきた内容のみだった。
問題文の表記揺れがあったため勘違いを起こしやすいと思ったので注意が必要だと思う。

例：
「〜のうち正しいものを選べ」
「〜のうち正しくないものを選べ」
「〜のうち誤っているものを選べ」
「〜のうち誤っていないものを選べ」
Cloudflare Workers を使って Google Cloud Storage から R2 にプログレッシブ移行する

# 目的

以下の記事を参考にしながら、Google Cloud Storage (GCS) から Cloudflare R2 にプログレッシブに移行します。

S3 と書いてあるところには、GCS も使えるため、試してみます。
（もちろん AWS S3 にも使えます）

R2 にオブジェクトがなければ、GCS から取ってきて次回以降アクセス用に R2 に保存する流れになります。

> - [Progressive S3 to Cloudflare R2 migration using Workers](https://kian.org.uk/progressive-s3-to-cloudflare-r2-migration-using-workers/)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/a68d8cae-c440-471d-3db4-663e8611dcfc.png)


# 今回利用するコード

- [gcs-to-r2/wrangler.toml at main · kyouheicf/gcs-to-r2](https://github.com/kyouheicf/gcs-to-r2/blob/main/wrangler.toml)

こちらにあるものを利用します。



# GCS バケット準備

- [WebP Gallery  |  Google for Developers](https://developers.google.com/speed/webp/gallery1#sample_image_files_in_jpeg_and_webp_and_the_png_source)

移行元となる GCS バケットを作成し、サンプルイメージをアップロードします。

```shell:
gcloud storage buckets create gs://example-r2-migration --location=asia-northeast1
gsutil cp img/*.jpg gs://example-r2-migration
gsutil ls gs://example-r2-migration 
```

```shell:
gs://example-r2-migration/1.jpg
gs://example-r2-migration/2.jpg
gs://example-r2-migration/3.jpg
gs://example-r2-migration/4.jpg
gs://example-r2-migration/5.jpg
```

# R2 バケット準備
移行先となる Cloudflare R2 バケットを作成します。

```shell:
wrangler r2 bucket create example-gcs-migration  
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/f555723e-e682-0d60-9785-88093c70c8df.png)


# シークレット設定
GCS のアクセスに必要な認証情報をシークレットとして設定します。

```shell:
wrangler secret put AWS_ACCESS_KEY_ID
wrangler secret put AWS_SECRET_ACCESS_KEY
```

# Workers デプロイ
Cloudflare Workers をデプロイします。

```shell
% wrangler deploy
 ⛅️ wrangler 3.8.0 (update available 3.9.0)
-----------------------------------------------------
Your worker has access to the following bindings:
- R2 Buckets:
  - R2: example-gcs-migration
- Vars:
  - AWS_DEFAULT_REGION: "asia-northeast1"
  - AWS_SERVICE: "s3"
  - AWS_S3_BUCKET_SCHEME: "https:"
  - AWS_S3_BUCKET: "example-r2-migration.storage.googleap..."
Total Upload: 12.24 KiB / gzip: 3.87 KiB
Uploaded gcs-to-r2 (1.44 sec)
Published gcs-to-r2 (3.72 sec)
  https://gcs-to-r2.example.workers.dev
Current Deployment ID: a6bcb442-d219-4061-95b9-81ca32663726
```

# プログレッシブ移行の確認
以下のように画像にアクセスしてページを表示します。

```
open https://gcs-to-r2.example.workers.dev/1.jpg
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/063e7fd0-79f9-1131-b3cb-2752bb4867bf.png)

すると、R2 にアクセスした画像が保存されたことが確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/86ba920b-ae7b-8e46-ebe9-5ca6a6644b23.png)

残りの画像も問題なくプログレッシブ移行できました。

```
open https://gcs-to-r2.example.workers.dev/2.jpg
open https://gcs-to-r2.example.workers.dev/3.jpg
open https://gcs-to-r2.example.workers.dev/4.jpg
open https://gcs-to-r2.example.workers.dev/5.jpg
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/852e0f5f-f847-3c6b-47b8-927112a19d14.png)

# まとめ

ワンタイム移行では、一度に諸々を切り替える必要がありますが、

プログレッシブ移行では、既存の運用はそのままにしつつ、徐々に移行することが可能です。

Cloudflare Workers も手軽に使えるので、R2 への移行の1つの選択肢となりえるでしょう。
HistoryAPIのreplaceStateの第2引数'unused'について

## 概要
HistoryAPIを使う必要があったため、replaceStateメソッドのドキュメントを見ていたら以下のような構文が書かれていました。

```javascript
replaceState(state, unused)
replaceState(state, unused, url)
```

https://developer.mozilla.org/ja/docs/Web/API/History/replaceState

第2引数を見てみると`unused`と書かれています。なんだこれは・・・。
引数の説明には以下のように書かれていました。

> この引数は歴史的な理由のために存在しており、省略することはできません。空文字を渡すことが、将来このメソッドに変更が加えられたときに安全です。

どうやら元々使う予定だった引数だったが、何かしらの理由で利用されなくなってしまったようです。
「歴史的な理由」という言葉にがぜん好奇心がそそられたため、少し調べてみることにしました。

## replaceStateの第2引数は何者か？
まず第2引数にはもともと何が渡される想定だったのかを見ていきましょう。
mdnの以下ドキュメントに記載がありました。
（以下はpushStateの説明です。pushStateもreplaceState同様、第2引数はunusedとなっています）

> title
Safari 以外のすべてのブラウザーは、現在この引数を無視しています。ただし、将来的には使用する可能性があります。ここで空文字列を渡せば、将来このメソッドが変更されても安全です。あるいは、移動先の状態を表す短いタイトルを渡すこともできます。

https://developer.mozilla.org/ja/docs/Web/API/History_API/Working_with_the_History_API

これをみる限りだと第2引数にはtitleという引数が渡される想定だったようです。
第2引数に文字列を渡すことで、履歴の追加・更新と併せてページのタイトルを指定した文字列に変更することができたようです。

## なぜ第2引数titleは使われなくなったのか？
引数の説明を見る限りだとSafari以外のブラウザはtitleに引数が渡されていたとしても無視しているようです。
そのためtitleが使われなくなったのは **「各ブラウザベンダーが不要な機能だと判断したから」** であると言えます。

ではなぜブラウザベンダー達は、titleを不要な機能だと判断したのか？
これについては関連する情報が見つけられず真相はわからず仕舞いでした。（もしご存知の方がいたら教えて頂きたいです）
代わりと言ってはなんですがGPT-4の回答を貼っておきます。

![スクリーンショット 2023-09-24 15.52.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/567079/1062d5ac-711c-c54e-8a97-93cf22506ce9.png)

個人的には3つ目の理由がしっくりきていて、「普通に`document.title`で変更できるんだから`pushState`や`replaceState`の引数でtitleを変える必要は無いよね」となったのではないかと推測しています。

## まとめ
`pushState`や`replaceState`の第2引数が`unused`とされている理由について調査してみました。
「`unused`として空文字列を渡すくらいなら第2引数を無くせばいいじゃん」とも思いましたが、Safariでは機能するようなので既に使用しているユーザーのことを考えた結果、今の仕様に落ち着いたという感じなのかもしれません。

HTML5の仕様に携わった方のインタビューでも`pushState`に関しては以下のように語られていました。
実際に運用してみなきゃ善し悪しが分からないのに、運用後の仕様変更は難しいという点はある種の開発者共通のジレンマかもしれませんね・・・。

> パブリックに開発され多くの人にレビューされた機能であっても、実際にはそのプロセスが終わる前に実装され使われ始めてしまう。そしていったん使われ始めると、それは変更できなくなってしまう。これが出来の悪いAPIにつながってしまうのだ。例えばpushState()みたいな。引数が要求されているのに無視されてしまう。
> 難しいのは、仕様の善し悪しは現実世界のWebで実装してみなければ分からないのに、その実装によるテストが適切に行われたあとではもう仕様を変更するには遅すぎる、という点だ。pushState()に起きたのはこういうことだ。

https://www.publickey1.jp/blog/13/hixie_html5hixie.html
スッキリわかるJava入門 実践編 第３版 まとめ - part4 -JVM制御とリフレクション

# はじめに
こんにちは　◤●ㅅ●◥ﾉ

今回はJVM自体の制御に関わるAPIについて学びました！

## 第４章　JVM制御とリフレクション

### 1.JVM自体の制御
そもそもJVMとは...?
　Java Virtual Machineの略で、Javaのプログラムを動かすために必要なソフトウェアのこと

Javaには開発者がJVM自体の内部機構にアクセスして情報を得たり、
直接制御するためのAPIが存在する
JVM自体を制御するAPIについて学んでいく

### 2.JVMの終了
System.exit()を呼ぶとその場でプログラムを終了する
System.exitにはint型の引数を与えることができ、その数値が終了コードとしてOSに報告される
→多くのOSでは0は正常終了、0以外は異常終了を意味する

どのように使うのか
エラーの原因ごとに異なる終了コードを返してプログラムを終了するようにする
→バッチ処理が異常終了した場合の原因究明に使うことができる

### 3.外部プログラムの実行
java.lang.ProcessBuilderクラスを利用する
→自分が開発しているJavaプログラムから他のプログラムを起動できる

### 4.システムプロパティの利用
 JVMは、プログラム起動直後に地震やOSに関する情報を自動的に収集し、
システムプロパティとして保持する
システムプロパティはキーと値のペアとなっている

※システムプロパティとは...
JVMに関する情報のこと「属性」だったり「特性」などのある対象物の特徴に関する情報を表す
　
システムプロパティはどのようなクラスからも直接読み書きできるため便利
ただ、不具合の原因にもなりやすいため乱用は禁物

### 5.ロケールと国際化
JVMがOSからどのような「場所」でどのような「言語」を使うユーザーを前提として動作するかという情報を受けとっている
この情報は様々な局面で利用するために動作中はJVMが保持し続ける
これらの情報を総称して __ロケール__　と呼ばれ、java.util.Localeクラスを通して参照や変更される

またJVMは「時間帯」も管理しており、これをタイムゾーンと呼び、プログラムが動作している
PCの置かれた地域で使われている標準時と世界標準時との時差で表される
タイムゾーンはjava.util.TimeZoneクラスを用いて操作する

ロケールやタイムゾーンなどの情報はアプリケーションの国際化の基盤として重要な要素になる
SNSや貿易に関するシステムなどの世界各国から同時に利用されるシステムの構築には欠かせない技術

### 6.メモリに関する状態の取得
 JVMのメモリ状態の取得にはjava.lang.RuntimeクラスのgetRuntime()を呼び出し、Runtimeインスタンスを取得する必要がある

### 7.リフレクション
リフレクションAPI
クラスやインタフェースに関するさまざまな型情報の調査をJVMに依頼することができる
リフレクションAPIを用いて、クラスやメンバに対してどのような修飾がされているかを調べることができる

:pencil2: Classインスタンスの基本的な取得方法
1.`Class<?> cinfo = Class.forName(FQCN文字列);`
2.`Class<?> cinfo = クラス名.class;`
3.`Class<?> cinfo = 変数名.getClass();`

これらはjava.lang.Classクラスのインスタンスを戻り値として返す



## まとめ
System.exit()を呼ぶとその場でプログラムを終了する、OSに終了コードを返す
java.lang.ProcessBuilderクラスを利用することで外部プログラムを起動できる
タイムゾーンはjava.util.TimeZoneクラスを用いて操作する
リフレクションではClassクラスを用いて、型に関するさまざまな情報を取得できる