OmniAICreator/Qiita-1.07M huggingface dataset:
Laravel カスタムキャストを用いてパスワードのHash化を効率よく行う

# はじめに
パスワードをDBに保存する前にコントローラーでいちいちHash化の処理を書くのも面倒かと思います。
毎回はHash化の処理を書かなくて済む様に、requestのフィールド名に'password'が入っていた場合、自動でHash化され、その値でDBに保存される様にする方法を紹介します。

ただ、正直ベストな方法ではないかと思うので、他にもっと良い方法があればぜひ教えて頂きたいです！！

# 目次
<!-- タイトルとアンカー名を編集 -->
1. [カスタムキャストファイルを作成](#カスタムキャストファイルを作成)
2. [モデルにカスタムキャストを適用する](#モデルにカスタムキャストを適用する)
3. [カスタムキャストにハッシュ化の処理を記述](#カスタムキャストにハッシュ化の処理を記述)
4. [最後に](#最後に)

<!-- 各チャプター -->
# カスタムキャストファイルを作成
app/Casts/HashMake.phpをコマンドではなく、普通にエディタで作成します。
ファイル名は任意に設定してください。
作成後は以下の様に記述してください。(コピペしてクラス名を変更してください)

```
<?php

namespace App\Casts;

use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
use Illuminate\Support\Facades\Hash;

// HashMakeの部分は任意に命名、作成したファイル名と同じ文字にする
class HashMake implements CastsAttributes
{
    public function get($model, $key, $value, $attributes)
    {
    　　　　// ここはDBからデータを取得する際の処理を記載
    }

    public function set($model, $key, $value, $attributes)
    {
　　　　　　　　　　　　// ここはDBにデータを保存・更新する前の処理を記載
    }
}
```

# モデルにカスタムキャストを適用する
今回はパスワードをHash化する為、'password'のフィールドにカスタムキャストを指定します。

```
// User.php

// モデルでHashMakeをuse宣言する。
use App\Casts\HashMake;

class User extends Model
{
    // 追記
    protected $casts = [
        // カスタムキャストの対象となるフィールドにHashMakeカスタムキャストを記述する
        'password' => HashMake::class,
    ];
```


# カスタムキャストにハッシュ化の処理を記述
この例では、setメソッドでパスワードのHash化を行い、getメソッドでは特に何も処理をせずにDBの値をそのまま返しています。

```
?php

namespace App\Casts;

use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
use Illuminate\Support\Facades\Hash;

class HashMake implements CastsAttributes
{
    // コントローラーからパスワードを取得する際に実行する処理
    public function get($model, string $key, $value, array $attributes)
    {
　　　　　　　　　　　　　　　　// 特に処理をせずに値を返す
        return $value;
    }

    // コントローラーにパスワードを登録、更新する前の処理
    public function set($model, string $key, $value, array $attributes)
    {
        // passwordのフィールドがnullではない & 
        if ($value !== null && Hash::needsRehash($value)){
            $value = Hash::make($value);
        }
        // Hash化した後に値を返す
        return $value;
    }
}
```

<br>
__【各変数で取得できるデータ】__
$model ・・・　アクセスするモデル本体のデータが入っています。　

```
// dd($model);　出力結果
// ※取得できるデータは省略して表示しています。

App\Models\User {#1563 ▼
  #fillable: array:4 [▶]
  #hidden: array:2 [▶]
  #casts: array:4 [▶]
  〜　省略　〜
  #attributes: array:5 [▼
    "id" => 1
    "email" => "example@gmail.com"
    "password" => "$2y$10$fS751izgOoe6qPwQOsYssOBsp8a6QSTrRKK2HpPpFjO1YqASyBSdO"
    "created_at" => "2021-11-23 12:27:01"
    "updated_at" => "2021-11-23 12:27:01"
  ]
  #original: array:5 [▶]
  〜　以降も省略　〜
```

<br>
$key ・・・　User.phpのモデルで設定したフィールド名(キー)を取得できます。　

```
// dd($key);　出力結果

"password"
```

<br>
$value ・・・　User.phpのモデルで設定したフィールドの値(passwordの入力フォームで入力した値)を取得で来ます。※このパスワードがHash化されます。

```
// dd($value);　出力結果

"test1234"
```

<br>
$attributes ・・・　フィールドの値を配列で取得しています。

```
// dd($attributes);　出力結果

array:5 [▼
  "id" => 1
  "email" => "example@gmail.com"
  "password" => "$2y$10$fS751izgOoe6qPwQOsYssOBsp8a6QSTrRKK2HpPpFjO1YqASyBSdO"
  "created_at" => "2021-11-23 12:27:01"
  "updated_at" => "2021-11-23 12:27:01"
]
```




# 最後に

あとはHash化されたパスワードをDBに登録及び更新すれば、処理完了となります。
暗号化ではなく、Hash化の為、DBからパスワードを取得する際は復元ができません。
Stripe Checkout / Payment Linksで定期課金プランの上位プラン提案（アップセル）をノーコードで実装する

定期課金では、月額から四半期や年額など、より長期のサブスクリプションへのアップセルを行うことがよくあります。

Stripe Checkoutを利用している場合、下の画像のように、より長期間のプランへの提案（アップセル）を、ノーコードで実装することができます。

![スクリーンショット 0003-12-10 21.58.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/f215828b-c9f5-bfa6-4384-7bdb1a73004a.png)

## Stripe Checkoutでサブスクリプションのアップセルができる条件

アップセルの設定ができる料金プランには以下の条件を満たす必要があります。

- 同じ商品に関連づけられている料金プランであること
- 通貨が同一であること
- 定期(継続)課金の料金であること
- 年換算での合計金額が、元の料金プランより安くなること

料金体系モデルが[標準の料金体系]以外の場合は、追加の条件が発生することがあります。
詳細は、[Stripe Docs](https://stripe.com/docs/payments/checkout/upsells)をご確認ください。

## アップセル可能な料金プランを作成する

### Step1: 複数の料金プランを持つ商品を作成する

アップセルを提案できるように、１つの商品に複数の料金を登録しましょう。

今回は、以下のように３つの料金を登録しました。

![スクリーンショット 0003-12-10 20.38.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/4bb5a067-6690-ceae-273d-1576924a04cd.png)

### Step2: アップセルを提案したい料金を編集する

アップセルは、料金の詳細ページで設定できます。
期間の短い料金を選択し、詳細画面に移動しましょう。
![スクリーンショット 0003-12-10 20.40.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/05f9619b-bc6b-20a4-5461-cf75ad9e6ce7.png)

[アップセル]でプランを設定すると、CheckoutやPayment Linksの支払い画面でアップセル提案が可能になります。

入力欄をクリックすると、編集中の料金で選択可能な料金プランの候補が表示されます。

![スクリーンショット 0003-12-10 20.41.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/8dc1ea2b-8526-c051-7a5b-5c10fcb97d6e.png)

表示された候補から、提案したい料金プランを選択すると、設定完了です。

![スクリーンショット 0003-12-10 20.42.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/89c30c80-0e42-129b-c730-24444b5fd80c.png)

## アップセルの表示を確認する

設定したアップセルは、Payment Linksを使った支払いリンクやCheckoutなどで確認できます。

![スクリーンショット 0003-12-10 21.58.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/f215828b-c9f5-bfa6-4384-7bdb1a73004a.png)

トグルをオンにすると、料金プランが切り替わります。

![スクリーンショット 0003-12-10 21.59.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/ae1d7688-e50f-5740-43fb-5c579c0b29b8.png)

# おわりに

定期課金のアップセル機能を利用すると、「同じ契約期間に換算すると、いくら割引になるか」の計算も自動で表示されます。

決済ページでも提案を行うことで、より長期のサブスクリプションの獲得を目指しましょう。

# [PR] Stripe開発者向け情報をQiitaにて配信中！
2021年12月よりQiitaにて、Stripe開発者のためのブログ記事更新を開始しました。

- [Stripe Updates]：開発者向けStripeアップデート紹介・解説
- ユースケース別のStripe製品や実装サンプルの紹介
- Stripeと外部サービス・OSSとの連携方法やTipsの紹介
- 初心者向けのチュートリアル(予定)

など、Stripeを利用してオンラインビジネスを始める方法について随時更新してまいります。

-> [Stripe Organizationsをフォローして最新情報をQiitaで受け取る](https://qiita.com/organizations/stripe)
PowerShellで指定したフォルダのファイル名一覧をフルパスで取得する

## 結論
```powershell
# カレントフォルダのファイル名のみを取得
ls -file -name

# カレントフォルダのファイル名をフルパスで取得
(ls -file).fullname

# 指定したフォルダのファイル名をフルパスで取得
(ls [指定フォルダのパス] -file).fullname

# 取得結果をテキストファイルに出力する
(ls -file).fullname Out-File list.txt
```

## 説明

### ファイル一覧表示コマンドレット
PowerShellではカレントフォルダのファイル一覧を取得するときに、
`Get-ChildItem`コマンドレットを使用する。


### エイリアスを使う
コマンドレットのヘルプを参照すると、
エイリアスとして`ls`が定義されているので使うことにする。
（Get-ChildItemといちいち入力するのは効率が悪いため）

```\
> help Get-ChildItem

名前
    Get-ChildItem

構文
    Get-ChildItem [[-Path] <string[]>] [[-Filter] <string>]  [<CommonParameters>]

    Get-ChildItem [[-Filter] <string>]  [<CommonParameters>]


エイリアス
    gci
    ls
    dir


注釈
    Get-Help を実行しましたが、このコンピューターにこのコマンドレットのヘルプ ファイルは見つかりませんでした。ヘルプの一部だけが表示されています。
        -- このコマンドレットを含むモジュールのヘルプ ファイルをダウンロードしてインストールするには、Update-Help を使用してください。
        -- このコマンドレットのヘルプ トピックをオンラインで確認するには、「Get-Help Get-ChildItem -Online」と入力するか、
           https://go.microsoft.com/fwlink/?LinkID=113308 を参照してください。
```

### Get-ChildItemコマンドレットの主なオプション
Get-ChildItemコマンドレットには主に下記のオプションを使用することができる。
オプションはGet-Option

|オプション|説明|
|-|-|
|-file|ファイルを表示する（フォルダは表示しない）|
|-name|ファイル名のみを表示する|
|-filter|一致するファイル名のみを表示する|
|-resourse|サブフォルダの配下も表示する|
|-depth|サブフォルダの階層を制限して表示する|

### 取得結果をファイルに出力する
コマンドレットで取得した情報をファイルに出力したい場合は、
下記のオプションでファイルに出力できる。

```powershell
# ファイルに出力（上書き）
ls -file -n > list.txt

# ファイルに出力（追記）
ls -file -n >> list.txt
```

## 参考
- [PowerShell 使い方メモ](https://qiita.com/opengl-8080/items/bb0f5e4f1c7ce045cc57)
- [ファイルの一覧を表示する（Get-ChildItem）](http://suyamasoft.blue.coocan.jp/PowerShell/Tips/File/GetChildItem/index.html)
- [【PowerShell】ファイルにリダイレクト（出力・書き込み）](https://algorithm.joho.info/powershell/file-redirect-ps1/)
STM32G031とI2Cでテストデータを出力(Arduino)


x 過去ログを見よ->


目的
I2Cのテスト



x 受信は、F303


![o_con159.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/49d3f0fb-8eb7-5235-e4ae-44b79c52003c.jpeg)




```c++


//I2C_1__40__80_031

#include <Arduino.h>
#include <Wire.h>

//STM32G031J6M6 i2cピンの定義
#define sdaPin PA12    // ArduinoA4
#define sclPin PA11    // ArduinoA5

#define en      PB7  // 1pin

#define ADDR1  0x40

//初期化
void setup() {

  delay(3000); //not Delete

  //GPIOの初期化
  pinMode(en,OUTPUT);

  //i2cの初期化
  Wire.begin(sdaPin,sclPin); //STM32G031J6M6

}//setup


//              12345678   12345678   12345678   12345678
char b8[8] = {0b10000000,0b01000000,0b00100000,0b00010000,
              0b00001000,0b00000100,0b00000010,0b00000001 };

//メインループ
void loop() {

  for(int ii=0;ii<8;ii++){

    digitalWrite(en,1); //LED ON debug

    //I2Cに送信
    Wire.beginTransmission(ADDR1);
    Wire.write(b8[ii]);
    Wire.endTransmission();

    delay(500);    
    digitalWrite(en,0); //LED OFF debug
    delay(500);

  }//for

}//loop




```
AVX/AVX2による論理演算

# はじめに
YMMレジスタに対して，256ビットの論理演算（and, or, xor, andnot）を行います．
整数だけでなく，浮動小数点のfloat/doubleに対しても論理演算が用意されています．

not演算は用意されておらず，否定が必要な場合には，andnot演算が代わりにあります．
一見，見間違えますが，andnotはnandのことではありません．
以下に，対応するC言語（C++）のコードを示します．

| ope    | c-code |
|:------:|:------:|
| and    |   a&b  |
| or     |   a｜b |
| xor    |   a^b  |
| andnot | (~a)&b |
|--------|--------|
| not    | ~a　　 |
| nand   | ~(a&b) |

また，not演算は，比較演算とxorを使って作ることもできます．
しかしながら，比較の条件を入れ替えるなどをすれば，notがそもそも必要なくなる場合も多く，この4つの論理演算だけで計算が閉じるようにコードを最適化することも重要です．

なお，AVX512からvpternlogd命令で任意の論理演算が可能です．

## _mm256_and|or|xor|andnot_ps|pd (AVX/AVX2)
```cpp
__m256i _mm256_and_ps (__m256 a, __m256 b)
asm: vandps ymm, ymm, ymm //and
__m256i _mm256_and_pd (__m256d a, __m256d b)
asm: vandpd ymm, ymm, ymm //and

__m256 _mm256_or_ps (__m256 a, __m256 b)
asm: vorps ymm, ymm, ymm //or
__m256 _mm256_or_pd (__m256d a, __m256d b)
asm: vorpd ymm, ymm, ymm //or

__m256 _mm256_xor_ps (__m256 a, __m256 b)
asm: vxorps ymm, ymm, ymm //xor
__m256 _mm256_xor_pd (__m256d a, __m256d b)
asm: vxorpd ymm, ymm, ymm //xor

__m256i _mm256_andnot_ps (__m256 a, __m256 b)
asm: vandnps ymm, ymm, ymm //andnot
__m256i _mm256_andnot_pd (__m256d a, __m256d b)
asm: vandnpd ymm, ymm, ymm //andnot
```
**動作**  
作成中

**CPI, Uops**  

| Architecture | Latency | Throughput |Uops|
|--------------|:-------:|:----------:|:--:|
| Alderlake    |    1    |    0.33    |  - |
| Icelake      |    1    |    0.33    |  1 |
| Skylake      |    1    |    0.33    |  1 |
| Broadwell    |    1    |     1      |  1 |
| Haswell      |    1    |     1      |  1 |
| Ivy Bridge   |    1    |     1      |  1 |
| Sandy Bridge |    1    |     1      |  1 |
| Zen3         |    1    |    0.25    |  1 |
| Zen2         |    1    |    0.25    |  1 |
| Zen          |    1    |    0.5     |  2 |

**説明**  
浮動小数点に対する256ビット演算を行います．

## _mm256_and|or|xor|andnot_si256 (AVX2)
```cpp
__m256i _mm256_and_si256 (__m256i a, __m256i b)
asm: vpand ymm, ymm, ymm //and

__m256i _mm256_or_si256 (__m256i a, __m256i b)
asm: vpor ymm, ymm, ymm //or

__m256i _mm256_xor_si256 (__m256i a, __m256i b)
asm: vpxor ymm, ymm, ymm //xor

__m256i _mm256_andnot_si256 (__m256i a, __m256i b)
asm: vpandn ymm, ymm, ymm //andnot
```
**動作**  
作成中

**CPI, Uops**  

| Architecture | Latency | Throughput |Uops|
|--------------|:-------:|:----------:|:--:|
| Alderlake    |    1    |    0.33    |  - |
| Icelake      |    1    |    0.33    |  1 |
| Skylake      |    1    |    0.33    |  1 |
| Broadwell    |    1    |    0.33    |  1 |
| Haswell      |    1    |    0.33    |  1 |
| Zen3         |    1    |    0.25    |  1 |
| Zen2         |    1    |    0.25    |  1 |
| Zen          |    1    |    0.5     |  2 |

* Haswell, Broadwellは浮動小数点のビット演算よりもスループットが高いです．

**説明**  
整数に対するビット演算を行います．
どのサイズに対する命令であっても1ビット単位の処理であるため型によらず共通です．
Go製のCGIからSlackに投稿する

## はじめに

やったことはタイトルの通りですが、ひさびさにGoを触ってみようと思ったので軽く試してみました。
参考にあげてる記事を適当にそのままくっつけただけです。

## 環境

- go: 1.17.5
- Apache: 2.4

## Slack投稿準備

[Slack API を使用してメッセージを投稿する](https://zenn.dev/kou_pg_0131/articles/slack-api-post-message) を参考に、ボット用トークンを取得する。

- workspaceにアプリを作成
- `Bot Token Scopes`に`chat:write`を設定(ユーザーは今回使わないのでパス)
- ワークスペースにインストール
- `Bot User OAuth Token`取得

## コーディング準備

標準パッケージ以外に`slack-go`パッケージも利用するので準備する。

```bash
cd your-dir
go mod init example.com/m
go get -u github.com/slack-go/slack
```

## 必要な要素

- CGIとして動作させるため、パッケージ`net/http/cgi`を使う
- リクエストの読み込みに使う`ReadAll`が`ioutil`から`io`に移動になった

## コード全体

```go:main.go
package main

import (
	"github.com/slack-go/slack"
	"io"
	"net/http"
	"net/http/cgi"
	"net/url"
	"strings"
)

const TOKEN = "xoxb-your-token"

// メイン関数
func main() {
	cgi.Serve(http.HandlerFunc(handler))
}

// CGIの処理
func handler(w http.ResponseWriter, r *http.Request) {
	// POSTメソッドでなければエラーで終了
	if r.Method != http.MethodPost {
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	// リクエストボディの内容を取得(byte[])
	body, err := io.ReadAll(r.Body)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	// urlencodeされているのでmapに分解(デコードも自動でしてくれる)
	values, err := url.ParseQuery(string(body))
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	// フィールド`message`の内容をリクエストから取得(string[]からstringにも変換)
	message := values["message"]
	data := strings.Join(message , "")

	// slackクライアント生成して投稿
	c := slack.New(TOKEN)
	_, _, err = c.PostMessage("#チャンネル名", slack.MsgOptionText(data, true))
	if err != nil {
		w.WriteHeader(http.StatusConflict)
		return
	}

	w.WriteHeader(http.StatusOK)
}
```

## 使い方

- ビルドしてApacheのcgi-bin直下に置く
- ブラウザからとりあえずアクセスする(GETメソッドになるので400エラーの画面が出る)
- Webインスペクターからfetchする

```js
await fetch('main.cgi', {
  method: "POST",
  body: `message=${encodeURIComponent('めっせーじ内容！！！！')}`
});
```

- Slackに届く

## 参考

- [GoでCGIしてみる](https://qiita.com/mackee_w/items/9bdfc4ccd2936127561e)
- [Go1.16で追加されたio#ReadAll関数から読むストリーミング中のバッファ拡張の仕方](https://budougumi0617.github.io/2021/02/22/update_capacity/)
- [Slack API を使用してメッセージを投稿する](https://zenn.dev/kou_pg_0131/articles/slack-api-post-message)
- [【Go】slack-go を使用して Slack にメッセージを投稿する](https://zenn.dev/kou_pg_0131/articles/go-slack-go-usage)
ConoHa WingでAPI公開

#経緯
「やっとレンタルサーバー借りれた！1発目はAPI作ろう！！！」
と思ったは良いもののConoHa Wingが比較的新しいこともあり情報が少なく苦戦した。
同じ事をやろうと思っている人向けに書く。

#環境
**端末:** MacBook Pro M1
**サーバー:** ConoHa Wing
**言語:** Python3.6
**フレームワーク:** Flask

#1, SSH接続
https://enginyaa.com/conoha-wing-ssh/

ここの記事がわかりやすかったです

#2, Pythonを使えるようにする

「ConoHa Wing Python」と検索するとpyenvをインストールして使用する方法が紹介されてます。
自分の環境ではなぜかうまくいかなかったのでサーバーに標準で入っているPython3.6を使用できるようにしました。

Pythonのパスを通す

```
[xxxxxxx@web0000 ~]$ export PATH=/opt/alt/python36/bin:$PATH
```

Pythonのパスが通った事を確認

```
[xxxxxxx@web0000 ~]$ which python
/usr/local/bin/python
```

.bashrcのパスを通す

```
[xxxxxxx@web0000 ~]$ echo 'export PATH=/opt/alt/python36/bin:$PATH' >> $HOME/.bashrc
```

パスが通った事を確認

```
[xxxxxxx@web0000 ~]$ which pip3
/opt/alt/python36/bin/pip3
```

これでPythonとpip3が使えるようになりました。

#3, Flaskをサーバー上で機能させる
用意するファイルは3つです

- .htaccess
- app.py
- index.cgi

```file:.htaccess
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ /index.cgi/$1 [QSA,L]
<Files ~ "\.py$">
  deny from all
</Files>
```

```app.py
#!/opt/alt/python36/bin/python3.6

from flask import Flask, jsonify
 
app = Flask(__name__)

@app.route('/', methods=['GET'])
def hello_world():
    return jsonify({'message': 'Hello world'})
 
if __name__ == "__main__":
    app.run()
```

```index.cgi
#!/opt/alt/python36/bin/python3.6

import cgitb
cgitb.enable()

from wsgiref.handlers import CGIHandler
from app import app
from sys import path

path.insert(0, '/public_html/')# index.cgiのパス
from app import app
class ProxyFix(object):
  def __init__(self, app):
      self.app = app
  def __call__(self, environ, start_response):
      environ['SERVER_NAME'] = "1111.conoha.ne.jp"# ← ホスト名
      environ['SERVER_PORT'] = "0000"# ← ポート番号
      #GETとPOSTの両方がある場合は消す↓
      environ['REQUEST_METHOD'] = "GET"
      environ['SERVER_PROTOCOL'] = "HTTP/1.1"
      return self.app(environ, start_response)
if __name__ == '__main__':
   app.wsgi_app = ProxyFix(app.wsgi_app)
   CGIHandler().run(app)
```

![スクリーンショット 2021-12-12 13.31.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/b252c739-31f9-2295-d10e-fe72cb7ce3db.png)

ホスト名とポート番号はSSHで確認できます

最後に「index.cgi」を選択して右クリックで属性変更を押します。
パーミッションを「７５５」に変更すれば無事API公開成功です。

![スクリーンショット 2021-12-12 16.52.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/1c6c1381-1d70-c208-fda3-7be120b48894.png)


#おわり
質問があったら[@_SNQ](https://twitter.com/_SNQ?t=TS8J1OCmvZshvSkYnqQLjw&s=09)にDMください。

作ったAPIでTwitterBot作りました。
TwitterのShadowBanをチェックできるBotです。
DMに反応します。
[@Shadow_Ban_Bot](https://twitter.com/Shadow_Ban_Bot?t=TS8J1OCmvZshvSkYnqQLjw&s=09)
Windows PowerShellとPowerShellのリダイレクト処理の違い

# 結論

Windows PowerShellとPowerShellとで、リダイレクトした際に出力ファイルのエンコーディングが違っていることを知ったので、記事として記録を残しておきたいと思います。詳細は下記の通りです。ついでにコマンドプロンプトも調べました。

|シェル|出力ファイルのエンコーディング|
|------|------------------------------|
|PowerShell(pwsh)|UTF-8(BOMなし)|
|Windows PowerShell(powershell)|UTF-16(LE)|
|コマンドプロンプト(cmd)|Shift_JIS|

改行コードはすべてCR+LFです。

# 経緯
筆者は普段、Windows PCではPowerShell 7.2を利用しています。ご存知とは思いますが、PowerShell(旧称: PowerShell Core)は、オープンソースかつマルチプラットフォームなシェルで、Windowsに付属している、Windows PowerShell 5.1とは別物となっています。Windows PowerShellはその名の通りWindows専用で、もはや機能追加はされずにメンテナンスのみの状態となっています。

たまたま、Windows PowerShellしかない環境で作業をする機会があり、以下のようなエラーに遭遇しました
(一部の情報は省略しています)

```powershell
PS C:\tmp> echo '{ ... }' > package.json
PS C:\tmp> npm install
npm ERR! code EJSONPARSE
npm ERR! path C:\tmp/package.json
npm ERR! JSON.parse Unexpected token "�" (0xFFFD) in JSON at position 0 while parsing "..."
npm ERR! JSON.parse Failed to parse JSON data.
npm ERR! JSON.parse Note: package.json must be actual JSON, not just JavaScript.
```

package.jsonを開いたところ、エンコーディングがUTF-16(LE)だったのが分かりました。

その後気になったので、Windows系のシェルでリダイレクト自のエンコーディングを調べ、先述の通りの結果が得られました。調査中、バイナリはhexdump for VSCodeで見ました。わざわざバイナリエディタをインストールしなくてもよいので便利でした。

Windows PowerShellでUTF-8出力するには、以下のようなコマンドを利用することも可能です。1行目のコマンドは上書きで、2行目のコマンドは追記です。

```powershell
echo '{ ... }' | Out-File -Encoding utf8 package.json
echo '{ ... }' | Out-File -Encoding utf8 -Append package.json
```

しかし出力結果は、BOM付きUTF-8となってしまうので、素直にPowerShell 7.2を利用することをお勧めします。ちなみに本来UTF-8にBOMは必要ありません。MicrosoftがASCIIとUTF-8とを区別するために、オレオレ仕様で付けるようにしたようです。
[baserCMS]データベース内に専用のテーブルを持たないラクして作るプラグイン


昨年の [baserCMS Advent Calendar 2020](https://adventar.org/calendars/5393 "baserCMS Advent Calendar 2020")に続き、今年も[baserCMS Advent Calendar 2021](https://qiita.com/advent-calendar/2021/basercms)が始まってます。
さてその12日担当です。=[ 'ω' ] ﾄｳﾌｰ

## baserCMS4のご紹介

baserCMSとは、CMS（Contents Management System）です。
世の中には、WordpressとかMovableTypeとかSHIRASAGIなどいろんなCMSがありまして、その中で、PHPのフレームワークであるCakePHPを用いてできてるCMSです。

```
Webサイトを自由にカスタマイズできるCMS（コンテンツ・マネジメント・システム）です。
```

です。
■ <a href="https://basercms.net/">baserCMS - 国産オープンソース！フリー（無料）でコンテンツ管理に強いCMS</a>
</p>

<p>
プラグインとかテーマの配布・販売やってます。<br />
■ <a href="https://market.basercms.net/">baserマーケット</a>
</p>

<p>
導入事例いっぱいあります。着々と増加中です。<br />
■ <a href="https://basercms.net/works/">baserCMS導入事例 | baser CMS』</a>
</p>

<p>
それから、記事をブロック単位で作ることができる、ハンバーガみたいに積み上げていく感覚で簡単に記事作成ができるBurgerEditorというプラグインがあります。<br />
■ <a href="https://market.basercms.net/products/detail.php?product_id=66">BurgerEditor（baserCMS３対応）（baserCMS４対応） ダウンロード | baserマーケット</a>
</p>


## この記事で紹介すること

プラグインを作るときに、データベース内に専用のテーブルまでもないなぁ、というときに、既存のsite_configsを使って設定値を保存しておくとラクですよ、というサンプルの紹介です。

## 想定している対象者
- baserCMSでウェブサイトを制作をしている方


### 環境
- baserCMS 4.5.4以下


### やること

以下の4つのファイルを作ります。

- app/Plugin/ExamplePlugin/Controller/ExamplePluginsController.php
    - https://github.com/materializing/example-bc-advent-2021/blob/main/Controller/ExamplePluginsController.php
- app/Plugin/ExamplePlugin/Model/ExamplePlugin.php
	- https://github.com/materializing/example-bc-advent-2021/blob/main/Model/ExamplePlugin.php
- app/Plugin/ExamplePlugin/View/ExamplePlugins/admin/form.php
	- https://github.com/materializing/example-bc-advent-2021/blob/main/View/ExamplePlugins/admin/form.php
- app/Plugin/ExamplePlugin/VERSION.txt
	- https://github.com/materializing/example-bc-advent-2021/blob/main/VERSION.txt

ExamplePluginを有効化します。
おわり。


### できあがるサンプルの画面

![sc_admin_example_plugins.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26110/617006ad-de3a-f1ec-244c-d63d0bb69a89.png "sc_admin_example_plugins.png")

### 保存された値の状態

![sc_db_example_plugins.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26110/7e10bc18-b437-c9df-f949-d3d85da3ece3.png "sc_db_example_plugins.png")

#### ExamplePluginsController.php

1つのフィールド内に収めるため、json化してまとめちゃってるのがポイントです。
まとめちゃったあとは、site_configsテーブル内にキー（name）を指定して1レコード内に入れ込みます。
SiteConfigモデルからは、findByNameでキーを指定して保存値を取ってきます。
取ってきたときはjsonなので、decodeで連想配列化します。

#### ExamplePlugin.php

専用テーブルは持たないので $useTable = false 指定がポイントです。
バリデーション（入力チェック）情報はbaser流儀でそのまま書いて使うことができます。

#### admin/form.php

専用テーブルを利用しないからといって、特別なことは何もなく、通常の管理側フォーム画面を作るだけです。
みなさん、admin-thirdには慣れましたか？

#### VERSION.txt

せっかくなので置く。

#### サンプルファイル一覧

こっちにまとめて置いてあります。
https://github.com/materializing/example-bc-advent-2021
フォルダ名を「ExamplePlugin」としてプラグインとして設置すると動作できます。


### 特徴

- 専用のテーブルを準備するまでもないのでラク
- 入力したいフィールドを増やすときはビューファイルで入力欄を増やすだけなのでラク
- 入力フィールドが減るときはビューファイルで入力欄を減らすだけなのでラク
- site_configs内に持っているので、使いたいところでの呼び出しがラク

欠点としては、site_configs内のvalueフィールドはTEXT（or MEDIUMTEXT）なので、無制限じゃないので、大量のデータをあまりにも入れすぎないようにしないと事故ることですかね。
ご利用は計画的に。


### あとがき

次の方は [rabbitchangTESTさん](https://qiita.com/rabbitchangTEST)です。
どんなお話が出るのか楽しみです。よろしくお願いしまーす。
[TypeScript] Array.isArrayでReadonlyArrayの型ガード

書こうと思ったらなんかあいてたのでついでに埋めときます。

```ts
function test(a: string[] | readonly number[] | {bbb: string})
```

こんな関数があったと思ってください。なんだこの引数aの型は、とかは思っても気にしない。

とりあえず、bbbプロパティを使った処理をしたいので、`Array.isArray`で配列型を除外しようと思います。

```ts
  if (!Array.isArray(a)) {
    // これでこの中ではaを`{bbb:string}`型として扱えるはず
  }
```

意気揚々とbbbにアクセスしようとしたら

```ts
  if (!Array.isArray(a)) {
    a.bbb.slice(0); // Error!!! あれっ!?bbbにアクセスできない?
  }
```

bbbにアクセスするところでエラーになりました。なんで?

## TL; DR

標準のArray.isArrayの宣言ではreadonlyな配列に対応していないようです。

```ts
declare global {
  interface ArrayConstructor {
    isArray(arg: unknown): arg is readonly unknown[];
  }
}
```

を追加すると期待通りになりました。

## 解決の経緯

a.bbbのところのaにカーソルを合わせて型を確認すると…

```ts
(parameter) a: readonly number[] | {
    bbb: string;
}
```

`readonly number[]`が消えてません。

Array.isArrayの宣言を確認すると

```ts
interface ArrayConstructor {
    // ...
    isArray(arg: any): arg is any[];
    // ...
}
```

となっていました。

any[]はreadonlyではないのでreadonly number[]は除外してくれないようです。

仕方ないので以下を追加しました。

```ts
declare global {
  interface ArrayConstructor {
    isArray(arg: unknown): arg is readonly unknown[];
  }
}
```

こうすると

```ts
function test(a: string[] | readonly number[] | {bbb: string}) {
  if (!Array.isArray(a)) {
    a;
    // ここでaは
    // {bbb: string;}
  }else{
    a;
    // ここでaは
    // string[] | readonly number[]
  }
}
```

となって期待通りになります。

`unknown`もなかった頃の宣言がそのまま残ってるとかなんですかね?
アイデア発散　-９つの発想観点-

![Frame 1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1550168/ce0bd39f-5752-1b4a-6e3c-3a4f4ec2ebd2.png)

こんにちは！
私はGeekSalonのUIUXdesignコースメンターをやっています！今日はプログラミングとは少し離れた内容になりますが、ものづくりにおいて非常に重要なアイデア発散について、すぐに使える<font color="DeepPink">９つの発想観点</font>を紹介したいと思います:point_up:

すでに知っている方も、初めて出会った方も実践を意識して使ってみてほしいです！

ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー

<H3>①アイデア出しの重要性<H3>

プロダクトを制作するに当たって、まずぶち当たる壁がアイデア出しではないでしょうか？
ものづくりにおいて切っても切れない関係性であり、かつプロダクトの質を大きく左右するものがアイデアだと思います。

あらゆる課題がすでに解決され、非常に便利な世の中になっています。
そんな中で新しいアイデアを思いつくのは大変ですよね。

しかし、既存のサービスを元に今から紹介する９つの発想観点を意識すれば、新しいアイデアを生み出しやすくなると思います。
新規のサービスは<font color="DeepPink">既存のサービスの掛け合わせ</font>だと言われています。発想の転換でたくさんのアイデアの組み合わせを生んでいきましょう！

早速ご紹介していこうと思います。

<H3>②９つの発想観点<H3>

![Slide 16_9 - 15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1550168/dbfd2e49-4b8c-7a51-919a-6d3f0ba0dce1.png)

これらが意識してほしい９つの発想観点になります。

パッとみてもイメージが湧きにくいと思うので、実際に今あるサービスを例に出して、考えてみましょう。

<H3>③実践<H3>

今回は、オンライン英会話サービスを例に出し、考えてみました。
※あくまで私のアイデアなので、参考までに。

![Slide 16_9 - 10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1550168/4af3e898-a2c3-b415-ff77-ed4f562f224e.png)


このように書き出してみると、既存のサービスも他のサービスから派生したものだと言えます。
すでにあるサービスもいくつかありますね！
アイデアを発散させ、その中からピンとくるものを選び、深ぼっていけると新規のサービスが生まれるのではないかと思います。

ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー

<H3>④最後に<H3>

よく、ペルソナを設定しなさい。と言われるのですが、本当にその通りで、ターゲットを明確に絞ってプロダクト制作に取りかかれると良いかと思います！なぜなら万人受けするものを作るのはかなり難しいからです。まずは一人の人のニーズに応えられるようなサービスを作り信頼を得たのちに、どんどん広がっていくのではないかと思います。

このように発想の転換を使用して、新規のサービスを立案し、ワンランク上のものづくりができればいいなと思います！
少しでも、参考になれば嬉しいです。
【Rundeck】RundeckのGithub連携を使ったバックアップで挫折した話


# はじめに
前回の続きとしてウッキウキでGithubをつかったRundeckのバックアップ(リストア)を作ろうとしたら挫折したその話を書く。
もともと書きたかったのはあとがきだけなのにやることがどんどん増えていく。

# 挫折した話
まず綺麗な手順を作ろうとおもってまっさらなRundeckを立ててGit Importをしようとした。

以下の記事を応用してまっさらなRundeckを立てた。

https://qiita.com/vidil/items/1acedc72b5c0a9fa16e3

### SCM設定
前回設定したRundeckからプロジェクト・秘密鍵・対象プロジェクトを移設し「Setup SCM」から「Git Import」を設定する。

設定が必要な項目は「Git Export」より少なく2か所だけ

- Git URLは作成したリポジトリを指定する。

- SSH Key Storage Pathに先ほどkeystorageに設定したものを指定する。
![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1092362/ce0bfab6-f4c7-2b6c-3df8-c1ec274ebf0e.png)



「SCM Plugin Setup Complete」がでれば設定完了。
![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1092362/3103e51b-2f97-0b3d-9a07-2d402becf3a6.png)


例のごとくJob画面になにか増えているので「Import Remote Changes」を選択する。
![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1092362/aafc68d8-3bea-a430-cb51-d7774263fa9b.png)


実際にimportしようとしたらこうなった。pluginが足りていなかった。私は挫折した。
![4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1092362/7068f34f-99f6-98be-dfaf-d782d01d747c.png)


というかここまでくるまでに別な場所でもいくつかハマっていて実用的じゃなかった。

### じゃあどうするか
まず「Git Export」「Git Import」設定済みのRundeckを用意してJobを全部消してきれいにする。(しなくてもいい)
![5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1092362/4ea44309-8b13-b455-e338-e3b5e11daf49.png)


おもむろにAWS Consoleへ行き、対象Rundeckのイメージ作成を行う。
イメージ名は適当で構わない
![6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1092362/8d4cd8fd-7b18-d064-f9d4-b4194e04e33f.png)


イメージができたら該当のAMIから新しいインスタンスを起動する。
![7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1092362/6b12cbda-c141-8d59-1797-8056bea34e23.png)


あたらしく立ったRundeckで改めて「Import Remote Changes」を実行する。
![8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1092362/acfcab99-8764-796c-3317-ad7eb047ab6c.png)


Githubが勝手に差分吸収して最新の状態にJobをimportしてくれる。とてもらく。
![9.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1092362/b2ceb456-aec9-38d3-f2b7-28e44fcf8673.png)


この形であれば秘密鍵やプラグインなど移行せずとも全て同じものを使ってくれるため最小限の工数でRundeckのバックアップ(リストア)ができる。
またこのケースだとAMIをから立ち上げるのに期間が空いてもGithubが差分吸収してくれるのでAMI側のバージョン管理が不要。
これならAMI運用でもつらくないのでは。(多少プラグイン追加でバージョン差分がでても)
デメリットとしてはActivityを保持してくれないところ。(でもActivityを監査に使うところなら外だししてるだろうし問題ないだろう)

# あとがき
もともと前回の記事を書いているときにこのRundeckをAMIで無限増殖させられたら面白いのではと考えていた。
たとえばリポジトリは共通で本番用のRundeckから流すと本番用のJobとして動き、開発用なら開発用のJobとして動く構成とか、
Job作成用のDev-RundeckでJobを作り、Job実行権限のみのops-Rundeckで非エンジニアがセルフサービスで実行するとか,
(ops-rundeckはACLが厄介だが)
とても夢が広がった。

なお、今回気付いたがrundeck.orgの仕様が変わっており公式では最新版しかインストールできない？感じに変わっていた。
過去バージョンは[packeagecloud](https://packagecloud.io/pagerduty/rundeck)を使うようなので記事を更新しておく。
Rustで使いそうな出力フォーマットをまとめてみました

# はじめに
先行ゼロのためにこんなコードを書いてしまったのでフォーマットについて調べようと思いました。

```rust
if n < 10 {
	println!("00{}", n);
} else if n < 100 {
	println!("0{}", n);
} else {
	println!("{}", n);
}
```

[公式ドキュメント](https://doc.rust-lang.org/std/fmt/)の一部を日本語にした感じです。

# フォーマット文字列の形式

## 基本
変数をそのまま出力する場合は`{}`と記述します。

```rust
let x = 5;
println!("x = {}", x); // x = 5
```

`{}`と出力したい場合は、`{{}}`と記述します。

```rust
println!("{{}}"); // {}
```

出力する変数には`std::fmt::Display`トレイトを実装している必要があります。

```rust
// `std::vec::Vec<{integer}>` doesn't implement `std::fmt::Display`
println!("{}", vec![0, 1, 2]);
```

その場合でも`std::fmt::Debug`トレイトを実装していれば、`{:?}`と記述することでデバッグ用の形式で出力できます。`{:#?}`と記述すると、改行されて見やすくなります。

```rust
println!("{:?}", vec![0, 1, 2]); // [0, 1, 2]
println!("{:#?}", vec![0, 1, 2]);
/*
[
    0,
    1,
    2,
]
*/
```

## 右揃え、左揃え、中央揃え

`{:(埋める文字)(方向)(文字数)}`の形式で指定します。

* 埋める文字：文字数で足りない部分を埋める文字です。デフォルトはスペースです。
* 方向：以下の形式で指定します。
  * `>`: 右揃え
  * `^`: 中央揃え
  * `<`: 左揃え

```rust
println!("{:<5}", "x"); // x
println!("{:^5}", "x"); //   x
println!("{:>5}", "x"); //     x
```

## 符号を表示する

`+`をつけると、正の値の時にも符号が出力されます。0のときには+になるようです。

```rust
println!("{:+}", 5); // +5
println!("{:+}", 0); // +0
println!("{:+}", -0); // +0
println!("{:+}", -5); // -5
```

# ゼロ埋め

`{:0(文字数)}`とすることで、指定した文字数になるように0で埋めることができます。

```rust
println!("{:03}", 1); // 001
println!("{:03}", -1); // -01
println!("{:03}", 1000); // 1000
```

## 2進、8進、16進、指数表記で出力する

`b`とつけることで2進数表記になります。前述の符号付けやゼロ埋めと併用できます。

```rust
println!("{:04b}", 7); // 0111
println!("{:+b}", 7); // +111
```

`o`で8進、`x`で16進、`e`で指数表記になります。それぞれ[対応するトレイト](https://doc.rust-lang.org/std/fmt/#formatting-traits)を実装している必要があります。

## 自作の構造体を表示させる
`#[derive(Debug)]`と記述することで、構造体に`Debug`トレイトを実装できるので、`{:?}`でデバッグ用の出力ができます。`std::fmt::Display`トレイトを実装することで、独自の形式で出力することができます。

```rust
use std::fmt;

#[derive(Debug)]
struct MyBool {
    value: bool
}

impl fmt::Display for MyBool {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", if self.value { "Yes" } else { "No" })
    }
}

fn main() {
    let my_bool = MyBool {
        value: true
    };

    println!("{:?}", my_bool);
    println!("{}", my_bool);
}

```

# おわりに

もう冒頭のようなコードは書かなくて良くなりました。
目指せ、Autifyマスター！NotionでAutifyの実力診断テストを作ってみた！

#はじめに
はじめに謝ってきます。めちゃめちゃラフな内容です。


#目次
- Autifyを導入したお話
- 導入した後の課題のお話
- Autify診断テストのお話
- さいごに

#Autifyを導入したお話
自社でUI自動テストツールのAutfiyを導入したお話は、こちらをご覧ください。

https://techblog.gaudiy.com/entry/2021/07/30/171115

#導入した後の課題のお話
Autifyを導入してから、作成したAutifyのテストシナリオはめちゃめちゃ活躍しているのですが、実はちょっとした悩みがあります。

それはAutifyのテストシナリオを作成できる人材育成です。

Autifyを触れたことのある人なら、ほんとに簡単にテストシナリオを作成できるのはご存知だと思いますが、画面をポチポチ押してレコーディングするだけ以外にも色々な機能があり、その機能を使うことで複雑なリグレッションテストを作成することができます。

例えば、
- メールを受信して、メール内のリンクを押下する。
- JSでAPIをコールして結果の値を他のステップの入力値として使用する。
- メールを受信して、メール内の値を取得して、他のステップで利用する。
なども作成できます。

少し難しそうなテストも、Autifyの機能とちょっとした工夫で実現できますが、そのようなナレッジを活用してテストシナリオを作成できる人材（= Autifyマスター）を増やしていくことが課題となっています。

#Autify診断テストのお話
そんな『Autifyマスター』を遊び心をもって育成できないかと考えて作ってみたのが、今回ご紹介する『Autify診断テスト』です。

ここでも最初に謝っておきますが、現状で『Autify診断テスト』では課題解決はできてません。なんだったら運用に乗ってもいませんので「こんなん作ってみたよー！」「あ、おもしろそー！！」の段階です。

ただせっかく作ったので、Autfiy好きな人や興味のある人に使ってもらえたらなと思い、この場を借りて紹介します。

まず『Autify診断テスト』は、Notionで作成しています。

こんな感じですね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/322517/0d54c263-64a4-7989-5cd8-2afd8108370a.png)

つい先日に遊郭編がはじまった『鬼滅の刃』の流行りに完全に乗っかっています。

診断テストの内容を一部ご紹介すると、こんな感じになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/322517/c00825cd-3b59-0e2e-1335-a0dd95813e85.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/322517/e9e42a42-b671-ea58-a59a-c7d7154cc933.png)

鬼殺隊の階級に合わせて課題が設定されています。
ご存知でない方にも紹介すると、階級はこんな感じで11階級に分かれています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/322517/edd28da0-7b5d-300f-90cc-ca04436e6075.png)

診断結果は、Notionのデータベース（隊員一覧）を使い、診断テストをテンプレートとして登録しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/322517/f0bbc91f-7122-b2ea-3670-ea2099798499.png)

隊員一覧から「診断する」でテストを実施する流れです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/322517/68d3b160-1634-52cd-e2e5-928e8f855336.png)

#さいごに
最後まで読んでいただきありがとうございます。

最後まで読んでいただいたお礼といってはなんですが、今回ご紹介した『Autify診断テスト』をご自身で使用されているNotionでもご利用いただけるようにシェアいたしました。

https://bit.ly/3dPe8Z7

ヘッダーの「複製」からご自身で使用されているNotionにコピーを作成することができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/322517/8d332fbe-0ee3-4572-bf35-75ecdbfc682f.png)
ぜひご利用ください。
requireの中身を変数を含んだ文字列にする【webpack】

`require`の中身に変数が入っているとエラーになる。
例えば画像ファイルのsrcを取得して`Image`インスタンスを作りたい時に、次のように書くと怒られる。


```index.ts
/*ダメなパターン*/
const path = './images/';
const filename = 'image.png';
const src = require(path + filename);
const image = new Image();
image.src = src;
```

怒られ方はこう。
>WARNING in ./src/index.ts 43:12-34
Critical dependency: the request of a dependency is an expression

ちなみに怒られるだけでなく、require自体もできない。

怒られないためには、ルートからのパスをハードコードして、その中に変数を入れる必要がある。

```index.ts
/*OKなパターン*/
const path = 'images/';
const filename = 'image.png';
const src = require('/src/' + path + filename);
const image = new Image();
image.src = src;

```

ただしフォルダ構成は次の通り。

```
root/
  ┣ index.html
  ┣ dist/
  ┣ src/
      ┠ index.ts
      ┗images/
          └ image.png
  ┣ webpack.config.js
  ┣ tsconfig.json
  ┣ package.json
  ┣ package-lock.json
  ┗ node_modules/
```


webpackのconfigは次の通り。
webpack5を使っているため、output.assetModuleFilenameとmodule.rulesをこんな感じにすると`dist/images/`に画像ファイルを入れてくれる。

```webpack.config.js
const path = require('path');
module.exports{
  entry: './src/index.ts',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist'),
    assetModuleFilename: 'images/[name][ext]'
  },
  module: {
    rules: [
      /*
　　　　　　tsとかcssとかのローダー設定略
　　　*/
      {
        test: /\.(png|jpg|gif)$/i,
        type: 'asset/resource'
      }

    ]
  },
  resolve: {
      extensions: ['.ts', '.js']
  }
}
```

なんらかのインジェクションを怖がっているが故にWarningを出してるのかなあと思う。

参考：https://coderedirect.com/questions/199802/critical-dependencies-the-request-of-a-dependency-is-an-expression-webpack
error: src refspec master does not match any エラーへの対応

# git push 時のエラー

```shell
$ git push origin master
error: src refspec master does not match any
error: failed to push some refs to 'https://****/***.git'
```

# 原因

2020年10月以降、新規にリポジトリを作成する場合、デフォルトブランチが、「main」になったとのこと。

注意点として、既存のリポジトリは「master」のままであること。
※年内にも既存のリポジトリでもデフォルトブランチ名の変更を可能にする予定らしい

https://www.publickey1.jp/blog/20/githubmainmastermain.html

変更の背景には、人権問題が絡んでいたようです。

>今回の変更は、米国ミネソタ州ミネアポリスで5月25日に発生した、警官が黒人男性を拘束し窒息死に至らしめた事件と、その動画がソーシャルメディアなどで拡散したことをきっかけに広がった人権運動を背景にしたものです。
IT業界でこれまでよく使われてきた「Master/Slave」や「Blacklist/Whitelist」など、センシティブと見なされる用語を見直そうという動きが広がりました。

#解決方法

```
$git branch
*main

$ git push origin main
```

何でもかんでもネットで調べる前に、`git branch`して確認しとけよと自分を叱りたい。
深層学習で名寄せ（Entity Matching）を行う「DeepMatcher」を試してみた

# Entity Matchingとは

ざっくりとした説明をすると、あるデータ（商品情報や会社名、人名などなど）に含まれるレコードと別のレコードが同一のものかどうかを判定するタスクです。今年2021の3月~5月に開催されていたKaggleコンペのShopeeがEntity Matchingに該当するかと思います。

https://www.kaggle.com/c/shopee-product-matching/

（Shopeeはテキスト×画像のマルチモーダルな内容なので今回はお話しませんが）

Entity Matchingの詳細に関しては、非常に丁寧にまとめてくださっている方がいるので、そちらを参照していただければと思います。（私もお世話になりました）

https://qiita.com/daimonji-bucket/items/47f806624a5924f2d47b


### どういったときに役立つのか？

例えば、ECサイト/フリマアプリの掲載商品が同一のものかどうかを判定する際に活用されます。

- 同じ商品が別の出品者ではいくらで出品されているかを提示するケース  
     （上記で紹介したShopeeコンペは同一商品の最低価格保証を目的としたもの）
- 類似商品のレコメンドのために使われるケース

商品情報だけでなく、人名や社名情報の同一判定などでも活用されているようです。

- 名刺アプリなどにおける会社名・人名の同一判定に利用するケース
- サイトでユーザー登録する際に会社名をサジェストする機能に利用されるケース
  - 少し毛色が違いますが、こちらもEntity Matchingの一種と呼べそうですね

また、別システムのデータ同士の紐付けを行うという活用ケースもあります。

- 例えば、紐づけに必要な主キー（外部キー）が用意されておらずデータのJOINができないため、レコードの中身を見て同一のEntityであるかどうかを判定するケースなど


### Entity Matching処理の流れ

Entity Matchingにはざっくりと以下のようなタスクが含まれています。

- レコードの各要素の対応付け
  - テーブルA（レコードa）のどの列がテーブルB（レコードb）のどの列に対応するか判定
  - 単に列・項目の種別を判定するだけの場合もあります
- 各要素の類似度の算出（以下、簡易的に **名寄せ** と呼びます）
  - 数字的な近さ（単位を揃えるなど）
  - 文字列的な近さ（商品名・人名・会社名など）
  - 文章的な近さ（フリーテキストの意味的な近さなど）
- Blocking / Filtering
  - 多対多のマッチングを行おうとすると件数が膨大になるので、マッチングする可能性が低いものを除外する（Blocking）、マッチングする可能性が高いものに絞り込む（Filtering）という処理が必要になってきます。


# 今回の検証について

今回は、上記のEntity Matchingを深層学習で行う手法である「DeepMatcher」について、会社名の名寄せ判定にフォーカスして検証した内容について触れます。

### DeepMatcherとは

https://github.com/anhaidgroup/deepmatcher

深層学習によるエンティティマッチングを行うOSSです。

- レコードの各要素の類似度に基づきレコードが一致するかどうかの判定を行う
- 文字列の類似度を単語レベル・文字レベルの埋め込み学習により判定する

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/181219/913eca85-da29-c273-e8cd-66050be455c8.png)


### 名寄せが必要なパターン

会社名の名寄せにおいては、以下のような表記ゆれが含まれることが想定されます。

|パターン|表記ゆれの例  |
|---|---|
|正式名|A & B Technology Solution ltd|
|スペースの有無|A&B TechnologySolution ltd|
|記号の読み|A **and** B Technology Solution ltd|
|記号の有無|A & B Technology Solution **,** ltd **.**|
|会社形態の間違い|A & B Technology Solution **Inc**|
|単語のタイプミス|A & B Tech **on**logy Solution ltd|
|単語の省略形|A & B **Tech** Solution ltd|
|単数形・複数形|A & B Technology Solution **s** ltd|
|単語の追加|A & B Technology Solutions **America** ltd|
|略称（頭文字）|ABTS|

これが全てではないですが、今回は上記の表内のパターンについて検証してみます。
（日本語表記/英語表記/他言語表記などの揺れについては触れていません。）


### 検証

英語の会社名の名寄せデータセットとして公開されているものがあったので、こちらを利用しています。(nama自体も名寄せ用のツールとして作られているもののようです）

https://github.com/bradhackinen/nama

query_stringとcandidate_stringおよび正誤がセットで用意されています。

このデータに含まれれている会社名を表記揺れパターン毎に分類しどれくらいの精度で判定ができるかを試しています。以下、20%の評価データに対しての判定精度です。

|パターン|表記ゆれの例|
|---|---|
|スペースの有無| **77.8%**|
|記号の読み|100.0%|
|記号の有無|99.1%|
|会社形態|98.8%|
|単語のタイプミス|80.8%|
|単語の省略形|97.3%|
|単数形・複数形|100%|
|単語の追加|91.2%|
|略称（頭文字）| **47.2%**|

スペースの有無と略称（頭文字）への対処が難しい傾向にありました。
スペースの有無によって単語の区切り方が異なり別の単語として扱われてしまうことや、略称になることで文字列が大幅に変わることが精度悪化につながるようです。

# まとめ

今回は、深層学習によりEntity Matchingを行うDeepMatcherの文字列類似度の評価検証を行いました。

会社名を例に評価を行い、様々な表記ゆれパターンである程度の精度を出せている一方、スペースの有無や略称（頭文字）レベルでの表記ゆれがあった場合には同一判定が難しいという課題が見えてきました。

学習させるデータセットのAugmentation方法の調査や、利用しているネットワークの変更・改善など引き続き検証していこうと思います。また、今回の検証は英語のみのため日本語や他の言語への対応可否についても検証していきたいです。
firestoreでdocumentがあってもcollecction.sizeがずっと0だった原因

こんにちわ、CROSSER開発者です:relaxed:
今回は初歩的（？）なミスで3時間悩まされた（検索しても記事もなく。。。）ことについて
みなさんに共有していきたいと思います:fist:
どなたかの参考になれば嬉しいです:sparkles:

#事象
この様なDB構造で、documentにフィールドがない時にいくらcollection getしても
collectionのsizeが0でした。
collection/document/collection/

例えばこの様な構造です。
user/userId/userDetail/
userIdドキュメントにはフィールドなし

この時にCollection('user').get()をしてもドキュメントが複数あってもフィールドがないので、get()されるドュメントの数は0になってしまいます。


#解決方法
フィールドを追加する。

ただ、単純にフィールドは追加できません。
なぜなら、Collection('user').get()で取得できるドキュメントがないからです。

今回、私たちはドキュメントIDを別のCollectionに保存していたため、
そのIDで直接ドキュメントを指定し、フィールドを追加していきました。

例）
set(userCollection.doc(userId),{セットしたいフィールド})


#まとめ
- documentがあってもフィールドがなければCollectionのSizeが0になる

運用・保守の観点からもフィールドが不要なdocumentでも何かしらフィールドをつけていきたいと思います。
もしかしたら、公式サイトに書かれているかもしれませんが見つけきれなかったです:sob:



#その他
語学マッチングアプリ『CROSSER』もよろしくお願いします。:two_hearts:
評価もいただけると嬉しいです！
iOS/Androidモバイル対応
https://instabio.cc/crosser
neosでlogix その24

# 概要

niosでlogixやってみる。
neos ui > button オブジェクト
Button Eventsノードを使ってみた。

# 写真

![2021-12-12 02.44.17.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18104/187444b3-ccfd-70f3-e58c-62a654fab0df.jpeg)

# Button Events

```
Inputs
 	IButton	Button
Outputs
 	Impulse	Pressed
 	Impulse	Pressing
 	Impulse	Released
 	Impulse	HoverEnter
 	Impulse	HoverStay
 	Impulse	HoverLeave
 	Component	Source
 	Float3	GlobalPoint
 	Float2	LocalPoint
 	Float2	NormalizedPoint
```

# 手順


- devtooltipを装着
- 新規->オブジェクト->neos ui->buttonをスポーン
- インスペクターを表示
- logixtooltipを装着
- neosbuttonをグラブしてrで、インターフェース表示
- ノードブラウザを表示
- Interaction->button eventsをスポーン
- インターフェースをつなぐ


以上。
フルオンチェーンNFTの考察と実装。

##ちょっと書きたくなったので殴り書き

フルオンチェーンNFT関連の投稿が多く驚いたのでサービス化にあたって工夫した点なんかを殴りガキしておきます。
といっても違法アップロードの観点から一般公開しておらず。
どうやってNFT作ってるのか？謎に包まれています。

NFTDriveの仕様としては、
Geminiというモデルをつくりました。

自動販売機でNFTDriveテストネット祭りの時と同じですね。
自動で手数料を受け取ってあまったら返却を行いNFTを発行する。
自動販売というのは、仮想通貨を預かることを回避するためにすべてクライアントサイドでの実装です。
ツールとしてユーザーがダウンロードして自己責任で利用するという流れです。
それをやらないとNEMBERARTのデポジットのような事にね。
なので使い捨てのアカウントが利用者とのやり取りを自動でします。
手数料を受け取り、データを作成して。。。。
ちょっとフロー載せておきます。

![aa.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/356924/3b2f8cd4-2fc3-3b4f-ee9f-137d7788e6b2.png)


このようになっているわけですね。
基本的には自己責任のツールという枠で実装しました。
とはいえ不安の声もあったので現在は非公開でチェックしてアップロードしているという現実ですｗ

技術的な側面で言うと、Ｓｙｍｂｏｌスパムフィルターとの対峙で、
インターバル多めの４秒とっていますのでゆっくり書き込んでいます。
詰めて送信するとコアの方も言っていましたがキャッシュあふれてエラー返さず消滅するので、400ミリ秒だったかな？
アグリの場合は通常の250ミリ秒以上ではダメみたいです。400は開けろということですね。
それはほかのＴＸがないというのが前提なので、その十倍は開けるのがまぁ良いのかな？と思います。

あと実装で注意するのが、連続で投げるので途中で手数料が変わることがあるので、不足した場合の処理を加える必要があります。
とくにＭＯＳＡＩＣ　を発行する時は注意です、５０の定数はだめですね。
ちゃんとＭＯＳＡＩＣの発行手数料とれるので取ってきてから利用者に請求するようにですね。

最大１７００Ｘｙｍ？もっとかな？ぐらいは見たので不足したら追加で請求する処理を入れました。

あと検証ですがwebsocketが不安定になることがあるので、ちゃんと確認するために４つのノードでリスニングしています。
送信したハッシュリストと送信後にちゃんとあるか？とwebsocketのリアルタイムモニターで送信作業を見守ります。
もし、ハッシュが存在しないと再送信配列に保存しておきます。
そして最後にもう一度送信します。

｛ダブルプレッション方式｝と勝手に呼んでいます。

このダブルプレッション方式は少し強力に機能するようになっていまして、
TXが不足というのは１アグリゲート（最大１００内包）なので
不足するよりは｛重複しても送信すべき｝ということで、検証に少しでも異常があればもくはレスポンスが悪くなったら再送信します。

デコーダ側でこのダブルプレッション方式で重複したTXを処理しています。
といっても配列回すときにサイズが大きいほうに上書きすることで実装できます。


アップローダーのGeminiは３台同時運用にしています。
同時に３台しか動作しない設計です。
それぞれが使用するノードのリストを共有してかぶらないようにしています。
負荷テストの時は一人で５０タブとか開いているかたもおられましたが。。。

サービスで一般開放という条件では同時に３台ほどがせいぜい良いところかな？と考えました。
技術的にも黎明期なので様子見というところですね。

基本的に秘密鍵はクライアントサイドにというのがサービス側としては責任の観点からも重要になってくるので、そこは
各サービス方針に基づいてどっちが責任取るか？ということになるかと思います。

あとアドレス制限を入れるのでこれがあるか？ないか？でデコーダーの反応を変えています。
アドレス制限が確認できなければデコードしても不完全なデータと表示されます。
オンチェーンNFT開発は実装としてはアップローダーの部分より。
デコーダーに開発リソースがとられます。

たとえばMIDI対応をしたり、色々なファイル形式をブラウザーで再生できるようにしていないと利用者的には意味がないですよね。
実装コストを考えるとBase64が良いですね。　サーバーサイドのコンテンツヘッダーもそのまま返せばいいですし。

参考として実際のNFTDriveのプレビューを張っておきます。
https://nft-drive-data-explorer.tk/data.html?id=16F881DE78ED5810&w=90vh

あとこちらはデータエクスプローラーです。（アドレスが持っているMOSAICを上記のプレビューにかけて表示しています。）
https://nft-drive-data-explorer.tk/?address=NCYRHFYAW7W6MNOPNUC3B2BBCXP3KHO3TPTONCA

あとはサーバーサイドのPHPデコーダーです。
※ちなみにこのNFTは参照型なのでオンチェーンに参照先URLが書いていてそれをデコーダーが引っ張て来ています。
変更可能なNFT添付ファイル型とでも言いましょうか。。。普通のNFTですね。

https://nft-drive-data-explorer.tk/download.php?address=NADSSFFQGXPUVNTQ3LH7YBI6ST5Q6QZNPH46WKA

サーバーサイドではコンテンツヘッダーをBase64でそのまま取ってきてるのでリクエストすると適時そのファイルとして返信されてきます。
なのでいろいろＮＦＴを外部アプリケーションで利用したいならこの方式が良いと思います。

プレビュー機能に疑似的なオークション機能が備わっています。
パーシャル状態のTXを入札状態に見立てて参考価格として表示。
URLワンクリックでIchIGowalletとの交換請求連携実装など詳しくは動画をご覧ください。
https://www.youtube.com/watch?v=mDPyrU4JiHw

デコードの方法は公開していますので個人でなんやかんやするのは全然OKです。
デコーダーソフトとして一般に公開する場合には申し訳ないのですが、
公式HPの規約通りにフィルターの実装と、こちらと意思疎通できるように提携してください。
https://nft-drive.localinfo.jp/posts/23874701

理由は違法アップロードとりわけ【児童ポルノ】です。
実際に被害のことを考えるとこうやらざる負えないのはご理解ください。。。
私なりに健全にフルオンチェーンを構築しないといけないと思います。


いや、ちょっと長くなりましたが、
Symbolを使ってとても素晴らしいフルオンチェーンの世界が構築されていくことを願っています。
消えないことはほんとにロマンだし。嬉しいし。楽しい。そういう明るいイメージにしていければと思います。
Qiitaでハードウェア開発の記事は希望しない？という件に関して

こんにちは、
やましょうです。

本年このQiitaでちょっと話題になった件を書いておきたいと思います。


１．経緯
2021年4月16日発表
"Qiitaにおけるコミュニティガイドラインおよび利用規約を改定"
が事の発端です。
詳細の内容は
https://blog.qiita.com/notice-community-guideline-update/


2.改訂内容要約
2-1.プログラミング、プログラマ→エンジニアに変更し、エンジニアの定義を追加
また、プログラミングしていないくてもエンジニアでOK

3.いろいろな解釈
ハード系の記事は希望しないと理解した人が多いが
参考：https://power-of-tech.hatenablog.com/entry/2021/04/19/223740
参考：https://mobile.twitter.com/ichirowo/status/1382982437362298881
結局、直接的に書いていないので意味わからん？ってところだと思います。


4.結局？
コミニュティガイドラインから引用しますが、
Qiitaで投稿することが望まれる記事

```自分以外のエンジニアにとっても価値のある情報
　　   プログラミング
       インフラ（OSや各種ミドルウェア、クラウドインフラ含む）技術
       エンジニアマネジメント
       エンジニアのキャリア論
       デザイン
       数学
       開発手法
       設計
```

iotやマイコン(mbed,ardunio,etc..)を扱う場合には__開発手法__、__設計__ってハード部分含んでいると思っています。

ってことで
書いて消されたってことも私は聞いていないので
書いていいのかな？と思っています。

問題があったらQiitaさん連絡ください。

以上
やましょうでした。
Next.jsとStripeで決済機能を作ってみる。【Let's ハンズオン！】

エンジニアたるもの、ものを作るだけではなく、作ったもので儲けることこそ真の技術者やで。

そんなみんなの為にNext.jsとStripeを使って決済機能を実装したやで。

ほな、いくやで。

# Next.js環境構築

まずはなにがともあれNext.jsのプロジェクトを立ち上げてみる。

```bash
npx create-next-app next-stripe-app --typescript
```

プロジェクトの作成が完了したら、とりあえず動かしてみるやで。

```bash
cd next-stripe-app
npm run dev
```

この画面になったかな？

![スクリーンショット 2021-12-12 11.16.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326114/aa5b07f6-0109-ec5a-1169-0d862176bc1c.png)

# プロジェクトの概要

pages/api以下にcheckout_sessions.tsというファイルを作成してください。そうすると、プロジェクトの構成はこんな感じ。
やはりしんぷるいずべすと。

![スクリーンショット 2021-12-12 11.22.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326114/5f470d7e-afd2-b1f0-8e15-a2b35d7761d0.png)

#Stripeインストール

標準の雛形にpages/api/checkout_sessions.tsを追加しただけやで。

次は、stripeを使うからインストール。

```bash
npm install --save stripe @stripe/stripe-js
```

# Let's ハンズオン！

ほな、実装にうつるやで。
pages/api/checkout_sessions.tsに以下をコーディング。

```typescript:pages/api/checkout_sessions.ts
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);

export default async function handler(req: any, res: any) {
  if (req.method === 'POST') {
    try {
      // Create Checkout Sessions from body params.
      const session = await stripe.checkout.sessions.create({
        line_items: [
          {
            // Provide the exact Price ID (for example, pr_1234) of the product you want to sell
            price: process.env.NEXT_PUBLIC_STRIPE_PRODUCT_KEY,
            quantity: 1,
          },
        ],
        mode: 'payment',
        success_url: `${req.headers.origin}/?success=true`,
        cancel_url: `${req.headers.origin}/?canceled=true`,
      });
      res.redirect(303, session.url);
    } catch (err: any) {
      res.status(500).json(err.message);
    }
  } else {
    res.setHeader('Allow', 'POST');
    res.status(405).end('Method Not Allowed');
  }
}

```

pages/index.tsxのreturnの中に、以下のコーディングを追加しておくれ。

```typescript:pages/index.tsx
<form action="/api/checkout_sessions" method="POST">
        <section>
          <button type="submit" role="link">
            Checkout
          </button>
        </section>
</form>
```


そして、.env.localファイルを作成して、以下を書いておくれ。

```
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
NEXT_PUBLIC_STRIPE_PRODUCT_KEY="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
STRIPE_SECRET_KEY="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
```


アカウントの設定やら、商品の登録はこちらのサイトが分かりやすかったので、掲載。
https://concrete5.co.jp/blog/stripe-subsctiption

そして、アプリの画面からbuttonをクリックすると、こんな画面がでてくればOK

![スクリーンショット 2021-12-12 11.05.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326114/face7ce5-8dd4-7727-2d7a-efec128bb46a.png)

#最後に

これでこれからバシバシ商売ができるようになるやで

みんなが大金持ちになることを祈って終わりにします。

fin
WordPressでサイトを作る方法

WordPressでサイトを制作する方法についてまとめます。WordPressでサイトを作る際に必要になるものは次の3点です。

1. レンタルサーバー
2. ドメイン
3. WordPressのデータ

これら3点をわかりやすく例えると、レンタルサーバーは家、ドメインは住民票、WordPressのデータは家具です。

![ドメインとサーバー.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2380977/2a065a78-e6dc-36a9-6c7f-1289013489c5.jpeg)

WordPressなどのサイトのデータは、データを作っただけではだれからもみてもらうことができません。そのため、レンタルサーバーなどのサーバーにデータをアップロードして誰でも見られる状態にします。

# WordPressでサイトを作る流れ
WordPressでサイトを制作する流れは次です。

1. レンタルサーバー会社を決める
2. レンタルサーバー会社の案内に従ってサーバーにWordPressをインストールする
3. WordPressの設定を行う
4. WordPressでサイトを制作する

## レンタルサーバー会社の決め方
レンタルサーバー会社は、シェアの多い会社を選びましょう。
日本のレンタルサーバーのシェアトップ3は次のレンタルサーバー会社です。
なお、2021年の統計データになります。

1. エックスサーバー　14.77％
2. ロリポップ　14.11％
3. さくらのレンタルサーバ　13.37％

参考：https://ja.hostadvice.com/marketshare/jp/

上記3社のレンタルサーバー会社は、日本のレンタルサーバーのシェアのおよそ半分を占めます。

今回紹介した内容はnoteでも紹介しています。

https://note.com/website_factory/n/n99448d0a5a9b

https://www.websitefactory.jp/
【Git】２つ以上前のコミットを修正する。

### はじめに
commitを修正するときに直前のコミットを修正するには
「git commit --amend」を使用することで容易に修正することができます。
しかし、2つ以上前のコミットを修正するのは初心者にとっては少しハードルが高いです。
今回は２つ以上前のコミットを修正する方法を記載していきます。

### 事前準備
今回は、以下のような手順を踏んだ状態からコミットを修正していきます。

①index.htmlの作成
index.htmlが下記の状態でコミットメッセージ：「最初のコミットです。」でコミットしていきます。
<img width="400" alt="スクリーンショット 2021-12-12 10.47.14.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/918033/e2b850ef-1183-d473-567f-9e7e9cd73dfe.png">

②style.cssの作成
style.cssを作成し、コミットメッセージ：「２番目のコミットです。」でコミットしていきます。
<img width="400" alt="スクリーンショット 2021-12-12 10.50.31.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/918033/deef542d-5547-a8c1-f126-41c23d6ebe63.png">

②home.htmlの作成
コミットメッセージ：「3番目のコミットです。」でコミットしていきます。
<img width="400" alt="スクリーンショット 2021-12-12 10.55.16.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/918033/3ed64922-9b7e-52e6-b743-a247d9e881e9.png">

### コミットの修正
今回は2番目のコミットを修正していきたいと思います。
現在のコミット履歴（git log --oneline）は以下のようになっています。
<img width="500" alt="スクリーンショット 2021-12-12 10.57.57.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/918033/07c3f638-368a-7449-3d21-6847842eedfc.png">

①「git rebase -i HEAD〜ｎ」コマンドを打つ
HEAD~2は新しいものから2つのコミットを表示します。

```
git rebase -i HEAD~2
```
②修正するコミットを「pick」→「edit」に修正
pickのコミットは現在のコミットをそのまま適用することになります。
修正したい２番めのコミットを「pick」→「edit」に修正したら、保存しエディタを終了します。
<img width="400" alt="スクリーンショット 2021-12-12 11.05.10.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/918033/4b857ed7-3286-8bcd-3f3e-331cbfac959f.png">

※現在のコミット履歴を確認すると以下の状態になります。
<img width="500" alt="スクリーンショット 2021-12-12 11.09.59.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/918033/3fb305f1-6259-c103-87de-51ca1404a5aa.png">

③ファイルを修正する。
ファイルを修正、ステージングし、「git commit --amend」コマンドを打ちます。
今回は下記のように「style.css」に色の指定をしました。
また、コミットメッセージを「2番目のコミットです(修正済み)。」に修正しました。
<img width="400" alt="スクリーンショット 2021-12-12 11.15.07.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/918033/11a9b8d4-4cdb-e3a9-5c66-3ef7d04e7af7.png">

④3番目のコミットを反映する。
下記コマンドを打つことで、3番目のコミットが反映されます。

```
git rebase --continue
```

無事2番目のコミットを修正することができました。
<img width="500" alt="スクリーンショット 2021-12-12 11.19.45.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/918033/a1adb609-a9be-e1c6-638b-8714055233f7.png">