OmniAICreator/Qiita-1.07M huggingface dataset:
[PenTestSecJP] 10分で「知る」ペンテストの世界

[PenTestSecJP](https://pentestsecjp.connpass.com/) で発表された内容の簡単なまとめ
[@r00tapple](https://twitter.com/r00tapple) （村島正浩さん）

# 目的
学生の8割がペンテストをイメージできることが目的です。

# ざっくりとしたペネトレーションテストの分類
日本国内におけるペネトレーションテストの種類
1. IPベースペンテスト
    * ペンテスターと担当者の間でスコープを定義して、対象サーバにのみ攻撃して脆弱性を確認する
    * 脆弱性診断の延長
1. TLPT
    * シナリオに沿って TTP ベース攻撃を行う
    * 実際の脅威シナリオでテストを行う（金融機関で行われる）
1. RedTeam
    * Red Team が社内ネットワークや社員端末に攻撃・侵入を試みて、Blue Team が異常なアクティビティを検知・隔離する
    * SOC や BlueTeam の体制ができているところが実施する
    * 企業がサイバー攻撃を受けた際にどう対応していくのか

## 代表的な Attack Map
* 第三者の攻撃
    * インターネット情報の公開情報を調査
    * 情報探索や脆弱性を悪用
        * 公開サーバにアクセス
            * データベースから顧客情報の奪取
            * VPN・DMZ経由で社内NWに侵入
    * フィッシング
        * マルウェアに感染させて社員端末やSaaSを遠隔操作
        * 中間車攻撃によるセッション奪取して社員端末やSaaSを遠隔操作
            * 社内NWに侵入
    * 物理的アクセス
        * 有線LAN/無線LAN/ゲスト・紛失端末・内部不正を利用して分離環境を不正操作
* アクセス権を有するユーザの攻撃（内部犯）
    * 正規経路でクラウド環境のOAサーバにアクセス
    * 専用経路で社内NWに侵入
* 社内NWに侵入後
    * セキュアルームやイントラネットワークなどの分離環境への侵入
    * ファイルサーバから情報を奪取
    * 管理者アカウント権限を奪取して管理コンソールへ侵入

# 超ざっくりとしたペネトレーションテストの実情
## 分離環境における外部通信制限の設計不備

### 実施目的
インターネット通信を制限している環境などにおいて、マルウェアによる遠隔通信確立や内部不正などによる**外部への情報持ち出しなどが実現しないか検証**を行いたい

### 評価起点
* Web無害化ソリューションを導入した業務環境
* 論理分離環境

### シナリオ
行員端末にマルウェアが感染したことなどを想定

### ゴール
外部への行員端末の情報を持ち出し

### 理想の形
Web無害化ソリューションを提供している製品で攻撃者の攻撃によって奪取される情報を良い感じに制限してくれる

### 現実
* Web無害化ソリューション自体の設定不備
    * 端末内の任意のアプリケーションがHTTPS通信できるような設定不備
    * 通信先CDNの設定不備
* Web無害化ソリューション自体の設計不備
    * Web無害化ソリューションを攻撃側も契約した場合に宛先を攻撃者側にした場合の問題

## オンプレ系OA環境侵害に関する攻撃シナリオ例
親会社が監査ベースのペネトレーションテストを実施していて、子会社がシナリオベース(TLPT/RedTeam)をやっている場合に頻出するシナリオ
外部通信がほぼできない環境でも、外部通信が発生する場所はあったりする
社員端末がマルウェアに感染するとAD経由で権限昇格をして情報を抜き出されることもある
親会社、子会社でADが設定されている場合、親会社の権限で子会社が侵害されることがある

### 実施目的
社員がフィッシングメールなどを起点にマルウェアを実行した場合、遠隔通信の確立が発生するかどうかについて検証を行。さらに、遠隔通信が確立されたあと、社内ネットワークにおいてどの程度の深さまで侵入が可能かを評価する。
具体的には、ファイルサーバから機微な情報を持ち出し、その後暗号化を行うことができるかどうかを確認する

### 評価起点
社員想定の端末に疑似マルウェアを添付してメールを送付し、端末上で擬似**マルウェアを実行しかんせんを模した状態を評価**の起点とする

### シナリオ
社員端末にマルウェアが感染したことを想定

### ゴール
1. Active Directory のドメイン管理者権限の奪取
1. ファイルサーバから機微な情報の奪取

### 理想の形
社内ネットワークにある、管理者サーバなどの本来アクセスができない情報には触れることができない

### 現実
Active Directory の Enterprise Admins グループを悪用して親ドメインから子ドメインにアクセスして土間員管理者を奪取することができ、その結果ファイルサーバの情報を奪取・暗号化できる

# 本当にざっくりしたサイバー攻撃被害後の問題定義
* 脅迫
    * ランサムウェア
    * ダークウェブ
* 被害
    * Webブラウザのアクセス履歴や保存されたログイン情報
    * 契約関係上管理している顧客のサーバ認証情報
    * 自社製品のソースコードや設計資料・管理者アカウント情報
* 二次被害
    * proxy侵入
    * 顧客サーバへの侵入

## 二重脅迫でリークデータを公開された場合の将来的リスク
* ダークウェブに情報がリークされた情報に、どのように二次被害があるかを想定できている企業が少ないイメージ
* リークデータがパブリックになるケースがあったが、**二次的なサイバー攻撃発生リスクの高まりも示している**
* DMZに配置される製品ソースコードの漏洩した場合の深刻度はあまり議論されない

## サイバーインシデント事後対応内容の不足
EDR安全神話の前提が多数
「異常アクティビティ発見して即隔離すれば、あとは社内手順に沿って対応」本当にできてる？
* 攻撃者の遮断フェーズは足りてる？
* 有効セッションの無効化の有効性確認は行っている？

# 備考
* TLPT(Threat Led Penetration Test): 攻撃シナリオに基づいて疑似的な攻撃を行うことで、セキュリティ対策状況を評価する手法のこと
* TTP: 戦術/技術/手順
* EDR: エンドポイントでの検知と対応ソリューション
Railsで、グルーピングしたDBレコードを1クエリで取得するscope

# 動機
特定カラムが同じレコードを、そのどれでもいい一つのみを代表して重複を一つにまとめ、レコードと対応するインスタンスとして取得する...というクエリを組むとき苦労したので備忘録です。
当初distinctメソッドを利用してみようとしていましたが、primary_keyが取得できず(selectメソッドにidを組み込むと、idが異なることで重複していないとみなされるため)、レコードと対応するインスタンス化できませんでした。
特定カラムのみをselectしてdistinctすると、idがnil、カラムがまとめた値、という形になります。

distinctの参考：

https://qiita.com/toda-axiaworks/items/ad5a0e2322ac6a2ea0f4

上記記事の`distinctはselectしなくても問題ないか？`のような操作がやりたかったイメージです。

# グルーピングという解決策
以下の記事を参考に、解決に一歩近づくことができました。
`parent_hoge_id`カラムでグルーピングして、グループごとに最大のIDの1レコードを取得しています。

https://qiita.com/ttiger55/items/4e15ccdccadedf25c83a

```
HogeModel.where(id: { HogeModel.group(:parent_hoge_id).select('max(id)') })
```

# scope化
これを毎回書くのはわかりにくいし面倒なのでscope化しようと思いました。
まず以下のようにしました。

```
scope :uniq_by_parent_hoge_id, -> { where(id: HogeClass.group(:parent_hoge_id).select('max(id)')) }
```

しかし後日気付いたのですが、当然これを利用するシーンでは以下のように他の条件を重ねることになります。

```
HogeModel.where(ex_condition).uniq_by_parent_hoge_id
```

この場合、グルーピングのための`HogeClass.group(:parent_hoge_id).select('max(id)')`での絞り込みは、前者のex_conditionを満たすとは限りません。
where条件で絞り込んだものをグルーピングしたいので、以下のようなチェーンにしたいのですが、上のscopeではそのようにはできません。

```
HogeModel.where(id: { HogeModel.where(ex_condition).group(:parent_hoge_id).select('max(id)') })
```

ということで、修正したscopeが以下のようになりました。

```
scope :uniq_by_parent_hoge_id, -> { HogeModel.where(id: group(:parent_hoge_id).select('max(id)')) }
```

groupメソッドは、selfから呼ばれています。
scope内でのselfは、このscopeメソッドを呼んだ、クラスやwhereチェーンなどになっています。つまり今回の例では`HogeModel.where(ex_condition)`です。
これにより、目標のクエリの発行が可能になりました。

# まとめと注意
SQLクエリから考える、いい勉強になりました。
注意点としては、このscopeを呼んだ際は呼び出し元のクエリに基づいてグルーピングを行うので、やりたい絞り込みをチェーンでつないだ最後に呼ぶ必要があるということになります。
以上、何かの役に立てば幸いです。
私にとっての必須AIツール５選：ChatGPT、Perplexity、AskTube、Cursor、Phind

この記事では、私の日々のワークフローを大幅に向上させている5つのお気に入りのAIツールを紹介します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3846673/7e7bc123-c0c7-2594-bd9d-1ae2c2ad4e84.png)


### 1. ChatGPT：一般的なトピックの説明に最適

ChatGPTは、一般的なトピックについて質問したり、複雑な概念を理解したりするために使用しています。Proバージョンも試しましたが、現在のニーズを満たしているので無料版に戻しました。

**メリット:** ChatGPTは、質の高い、詳細な、わかりやすい回答を提供してくれます。もし満足いかなかった場合は、フォローアップの質問をしてさらに明確にすることができます。

**デメリット:** ChatGPTの知識ベースはリアルタイムで更新されず、インターネットアクセスもありません。正確な回答を得るには、十分なコンテキストを提供する必要があります。また、回答は一般論に終始し、コーディングやオンラインリサーチのような特定のタスクには適していません。

### 2. Perplexity：Googleに取って代わる、インテリジェント検索エンジン

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3846673/6c7c0efb-1b7f-d126-5dd1-b05d310099a7.png)

Google検索の代わりにPerplexityを使用しています。Focus (Social)機能を使うことで、Redditからの検索結果を表示させることができ、私にとって最も関連性の高い情報が得られます。複雑なクエリの場合は、「Pro Search」を使用して、さまざまなソースからのより深い分析と情報統合を行います。

**メリット:** Perplexityは、インターネットアクセスにより、複数のソースから最新の情報を提供します。

**デメリット:** Perplexityは、時折、不正確な情報を提供することがあります。そのため、情報源の信頼性を二重にチェックしています。安全のために、最適な検索結果を得るために、PerplexityとGoogleの両方を使用しています。

### 3. AskTube：動画の要約に

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3846673/156bda0b-150a-c239-9011-25ecd7ed1726.png)

私は多くの動画、特に長いポッドキャスト、ディスカッション、ニュース、教育コンテンツを見ます。AskTubeは、以下のような点で私を助けてくれます。

* **コンテンツを素早く把握する:** 自動生成された字幕付きの外国語の動画に特に便利です。
* **動画を見るかどうか、どの部分が関連するのかを判断する。**
* **チャット機能を使ってさらに質問を深掘りする。**
* **知識保持のために要約を見直す。**

**デメリット:** エンターテイメント系の動画には向いていません。

### 4. コーディング：CursorとPhind

コーディングにはCursorとPhindを使用しています。8ヶ月前にGithub Copilotを試しましたが、これらのツールの方が好みです（ただし、最近Copilotは再評価していません）。

これらのツールを使った私のコーディング経験は以下の通りです。

* **反復的なタスクをこなし、複雑な側面に集中させてくれる。**
* **私が創造的なコントロールを維持しながら、アイデアの実装を加速させる。**
* **ジュニアデベロッパーのように振る舞い、私の効率性を高めてくれる。**
(Arduino)(c++)(c)ループの高速化?(for)


x arduinoの予定は未定

目的
ちょつとでもコンピュータグラフィックスで大量のデータを扱う為に
ループを高速化したい。

理論
比較を0または、0以外で終わらせるとZフラグまたは、NZフラグが
立つので早く終わる時があるらいし。
ひと手間かけるといい場合もあるが面倒なのであまり使っていない
どうしても、用の究極奥義である。

例1から10 指は、何本だーーーー10本だーーーー

結果
(int nをchar nに書き換えるといいことがあるらしい)

![o_coq230.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/2a30ef65-7230-ad99-117e-52a19a59a787.jpeg)



オンラインコンパイラ

プログラム


```c++


#include <iostream>
using namespace std;
int main(void){
    // Your code here!
    int i=0;
    for(int n = 10;n != 0;n--){
        i++;
        
        printf("%d,",i);//デバッグ
        
        //なんちゃら、かんちゃらの処理
        
    }//for n
}//main



```
【SSR】サーバーサイドレンダリング 初心者必見！ゼロから学べる実践入門

サーバーサイドレンダリング（SSR）とは？
---
サーバーサイドレンダリング（SSR）とは、WebページのHTMLをサーバー側で生成し、クライアントに送信する手法です。
これにより、初回ロード時のパフォーマンスが向上し、SEO（検索エンジン最適化）にも有利になります。

SSRのメリット
---
`SEOの向上`: 検索エンジンが完全なHTMLをインデックスしやすくなる。
`初回ロードの高速化`: クライアント側でのJavaScriptの実行を待たずに表示が可能。
`セキュリティ`: サーバー側でデータを処理するため、クライアント側でのセキュリティリスクが減少。

Node.jsインストール
---
https://nodejs.jp/

``` .Next.jsのセットアップ
npx create-next-app@latest my-ssr-app
cd my-ssr-app
npm run dev
```

このコマンドでNext.jsの新しいプロジェクトが作成され、ローカルサーバーが起動します。`http://localhost:3000`にアクセスして、デフォルトのページが表示されることを確認してください。

基本的なSSRの実装
---
Next.jsでは、ページコンポーネント内でgetServerSideProps関数を使うことで、SSRを実現できます。
``` .pages/index.js
// pages/index.js
export default function Home({ data }) {
  return (
    <div>
      <h1>サーバーサイドレンダリングの例</h1>
      <p>{data.message}</p>
    </div>
  );
}

export async function getServerSideProps() {
  // サーバー側でのデータフェッチング
  const res = await fetch('https://api.example.com/data');
  const data = await res.json();

  return {
    props: {
      data,
    },
  };
}

```
getServerSideProps関数は、ページがリクエストされるたびに実行され、サーバー側でデータをフェッチしてページコンポーネントに渡します。

データフェッチングの実装
---
外部APIからデータを取得し、それをSSRで表示する例を見てみましょう。上記の例では、サンプルAPI `https://api.example.com/data` からデータを取得しています。

APIが返すデータが以下のようなJSONであると仮定します。
```
{
  "message": "Hello from SSR!"
}
```
このデータをページに表示するには、getServerSideProps関数内でfetchを使ってデータを取得し、propsとしてコンポーネントに渡します。

パフォーマンス最適化のポイント
SSRを実装する際に、パフォーマンスを最適化するためのいくつかのポイントがあります。

`キャッシュの活用`: APIリクエストの結果をキャッシュして、サーバーの負荷を軽減します。
`最小限のデータフェッチ`: 必要なデータだけをフェッチして、レスポンスを軽量化します。
`非同期処理`: 複数のデータフェッチを非同期に行い、ページのロード時間を短縮します。

```
export async function getServerSideProps() {
  const [res1, res2] = await Promise.all([
    fetch('https://api.example.com/data1'),
    fetch('https://api.example.com/data2'),
  ]);
  const data1 = await res1.json();
  const data2 = await res2.json();

  return {
    props: {
      data1,
      data2,
    },
  };
}
```

まとめ
---
サーバーサイドレンダリング（SSR）は、サーバー側でHTMLを生成してクライアントに送信する手法です。これにより、初回ロードのパフォーマンスが向上し、SEOにも有利になります。Next.jsを使用することで、簡単にSSRを実装できます。
CI/CD（継続的インテグレーション / 継続的デリバリー）【初心者向け】

継続的インテグレーション（CI）
---
開発者が頻繁にコードをリポジトリに統合（マージ）し、そのたびに自動でビルドおよびテストを行うプロセス。

メリット
- 早期のバグ検出と修正
- 一貫性のあるビルドプロセス
- 新機能の迅速な統合

継続的デリバリー（CD）
---
コードを自動的にテスト環境やステージング環境にデプロイし、本番環境へのデプロイを手動で承認するプロセスです。

メリット
- ステージング環境での準備が整ったコードを迅速に本番環境にリリースできる準備が整う
- 自動化されたデプロイメントにより、人為的ミスが減少する
- デプロイプロセスの自動化により手動作業が削減される

CI/CDのワークフロー
---
`コードのコミット`:
開発者が変更をローカルリポジトリにコミット。
`ビルドプロセスのトリガー`:
コードがリポジトリにプッシュされると、CIツールがビルドプロセスを開始。
`自動テスト`:
単体テスト、統合テストが自動で実行。
`デプロイ`:
テストに合格したビルドがステージング環境、本番環境に自動でデプロイ。

GitHub Actionsを使ったCI/CD
---
1.リポジトリのセットアップ:
- GitHubでリポジトリを作成。

2.ワークフローファイルの作成:
- .github/workflows/ci.ymlという名前でワークフローファイルを作成。
```
name: CI

on: [push, pull_request]

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v2
    - name: Set up Node.js
      uses: actions/setup-node@v2
      with:
        node-version: '14'
    - name: Install dependencies
      run: npm install
    - name: Run tests
      run: npm test
```
`name`: ワークフローの名前を指定します。
ここでは「CI」として、継続的インテグレーション（CI）プロセスであることを示しています。

`on`: ワークフローがトリガーされるイベントを指定します。

`push`: コードがリポジトリにプッシュされたときにこのワークフローが実行されます。

`pull_request`: プルリクエストが作成または更新されたときにもこのワークフローが実行されます。

`jobs`: ワークフロー内で実行するジョブを定義します。
ここでは「build」という名前のジョブを設定しています。

`runs-on`: ジョブが実行される環境を指定します。
ここでは「ubuntu-latest」、つまり最新のUbuntuの環境で実行されることを意味します。

`steps`: ジョブ内で実行されるステップのリストです。
ここでは一連のステップが定義されています。

`uses`: GitHub Actionsのアクションを利用して特定の操作を実行します。
ここでは actions/checkout@v2 を使用して、リポジトリのコードをクローン（チェックアウト）しています。

`name`: ステップの名前を指定します。
「Set up Node.js」という名前がついています。

`uses`: actions/setup-node@v2 アクションを使用してNode.jsの設定を行います。

`with`: アクションに渡すパラメーターです。
ここでは node-version: '14' によってNode.jsのバージョン14を指定しています。

`name`: ステップの名前を指定します。
「Install dependencies」という名前がついています。

`run`: 指定したコマンドを実行します。
ここでは npm install コマンドが実行され、プロジェクトの依存関係がインストールされます。

`name`: ステップの名前を指定します。
「Run tests」という名前がついています。

`run`: 指定したコマンドを実行します。
ここでは npm test コマンドが実行され、プロジェクトのテストが実行されます。

3.パイプラインの実行:
- コードをプッシュすると、ワークフローが自動で実行され、ビルドおよびテストが行われる。

まとめ
---
CI/CDは、コードの変更を自動的にビルド、テスト、デプロイするプロセスです。
継続的インテグレーション（CI）はコードの統合を頻繁に行い、継続的デプロイメント（CD）は変更を自動的に本番環境にリリースします。
これにより、開発の効率と品質が向上します。
Gitのコマンドまとめ

### <font color="#1e90ff">基本的なコマンド</font>
- **<font color="green">git init</font>**: 新しいGitリポジトリを初期化
  - **使用例**: `git init`
  - **解説**: 現在のディレクトリを新しいGitリポジトリとして初期化します。

  <br>

- **<font color="green">git clone [URL]</font>**: リポジトリをクローン
  - **使用例**: `git clone https://github.com/user/repo.git`
  - **解説**: 指定したURLのリポジトリをローカルにクローンします。

  <br>

- **<font color="green">git status</font>**: 現在のリポジトリの状態を表示
  - **使用例**: `git status`
  - **解説**: 変更されたファイルやコミットされていないファイルの状態を表示します。

  <br>

- **<font color="green">git add [ファイル]</font>**: ファイルをステージングエリアに追加
  - **使用例**: `git add file.txt`
  - **解説**: 指定したファイルを次のコミットに含めるためにステージングエリアに追加します。

  <br>

- **<font color="green">git commit -m "[メッセージ]"</font>**: ステージングエリアの変更をコミット
  - **使用例**: `git commit -m "初回コミット"`
  - **解説**: ステージングエリアの変更をリポジトリにコミットし、メッセージを付けます。

  <br>

- **<font color="green">git push [リモート] [ブランチ]</font>**: ローカルのコミットをリモートリポジトリにプッシュ
  - **使用例**: `git push origin main`
  - **解説**: ローカルのコミットをリモートリポジトリの指定したブランチにプッシュします。

  <br>

- **<font color="green">git pull [リモート] [ブランチ]</font>**: リモートリポジトリから変更をプル
  - **使用例**: `git pull origin main`
  - **解説**: リモートリポジトリの指定したブランチから最新の変更をプルします。

  <br>

### <font color="#1e90ff">ブランチ操作</font>
- **<font color="green">git branch</font>**: ブランチの一覧を表示
  - **使用例**: `git branch`
  - **解説**: 現在のリポジトリのブランチ一覧を表示します。

  <br>

- **<font color="green">git branch [ブランチ名]</font>**: 新しいブランチを作成
  - **使用例**: `git branch feature-branch`
  - **解説**: 新しいブランチを作成します。

  <br>

- **<font color="green">git checkout [ブランチ名]</font>**: 指定したブランチに切り替え
  - **使用例**: `git checkout feature-branch`
  - **解説**: 指定したブランチに切り替えます。

  <br>

- **<font color="green">git checkout -b [ブランチ名]</font>**: 新しいブランチを作成して切り替え
  - **使用例**: `git checkout -b new-branch`
  - **解説**: 新しいブランチを作成し、そのブランチに切り替えます。

  <br>

- **<font color="green">git merge [ブランチ名]</font>**: 指定したブランチを現在のブランチにマージ
  - **使用例**: `git merge feature-branch`
  - **解説**: 指定したブランチの変更を現在のブランチにマージします。

  <br>

### <font color="#1e90ff">リモートリポジトリ操作</font>
- **<font color="green">git remote -v</font>**: リモートリポジトリのURLを表示
  - **使用例**: `git remote -v`
  - **解説**: リモートリポジトリのURLを表示します。

  <br>

- **<font color="green">git remote add [名前] [URL]</font>**: 新しいリモートリポジトリを追加
  - **使用例**: `git remote add origin https://github.com/user/repo.git`
  - **解説**: 新しいリモートリポジトリを追加します。

  <br>

- **<font color="green">git fetch [リモート]</font>**: リモートリポジトリから変更を取得
  - **使用例**: `git fetch origin`
  - **解説**: リモートリポジトリから最新の変更を取得します。

  <br>

### <font color="#1e90ff">その他の便利なコマンド</font>
- **<font color="green">git log</font>**: コミット履歴を表示
  - **使用例**: `git log`
  - **解説**: リポジトリのコミット履歴を表示します。

  <br>

- **<font color="green">git diff</font>**: 変更点を表示
  - **使用例**: `git diff`
  - **解説**: ワーキングディレクトリとステージングエリアの変更点を表示します。

  <br>

- **<font color="green">git stash</font>**: 変更を一時的に保存
  - **使用例**: `git stash`
  - **解説**: ワーキングディレクトリの変更を一時的に保存し、クリーンな状態に戻します。

  <br>

- **<font color="green">git stash pop</font>**: 一時的に保存した変更を適用
  - **使用例**: `git stash pop`
  - **解説**: 一時的に保存した変更を適用します。

  <br>

- **<font color="green">git reset --hard [コミットID]</font>**: 指定したコミットにリセット
  - **使用例**: `git reset --hard a1b2c3d`
  - **解説**: 指定したコミットにリセットし、すべての変更を破棄します。
【備忘録】NestJSで発生するDelete `␍`eslint(prettier/prettier)を解決する

# はじめに
NestJSで発生するDelete `␍`eslint(prettier/prettier)を解決した**備忘録**です。
下記画像のようなエラーについての解決策です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3348923/7a7223ff-eb39-31b5-9e4c-ff2bc0fd1d02.png)

# 解決策
このエラーは`ESLint`と`Prettier`の競合によるものらしいので
`ESLint`のルールに下記のルールを足します。

```js:.eslintrc.js
'prettier/prettier': ['error', { endOfLine: 'auto' }]
```

足した後のルールはこんな感じです。
```js:.eslintrc.js
rules: {
    '@typescript-eslint/interface-name-prefix': 'off',
    '@typescript-eslint/explicit-function-return-type': 'off',
    '@typescript-eslint/explicit-module-boundary-types': 'off',
    '@typescript-eslint/no-explicit-any': 'off',
    'prettier/prettier': ['error', { endOfLine: 'auto' }],
}
```
メモ シェルスクリプト - 引数を受け取る/計算結果を変数に代入する

```shell:calculate.sh
#!/bin/bash

#1
SUM=$(($1+$2))

#2
DOUBLE_PERCENT=$(($SUM/100))

#3
SUM=$(($SUM+$DOUBLE_PERCENT))

#4
RESULT=$(($SUM+800+99))

#5
echo "合計は"$RESULT"です"

#6
PER_PERSON=$(($RESULT/2))

#7
echo "一人当たり"$PER_PERSON"です"
```

```shell:実行
$ chmod +x calculate.sh
$ ./calculate.sh 120000 6000
合計は126899です
一人当たり63449です
```

#1
・`$1`、`$2`で引数を受け取る
・(変数と数字を含む計算結果を変数に代入する際には?) `変数名=$[計算式]`とする
自己紹介ページのフッターを編集

お疲れ様です。

今日は自己紹介文のフッターに貼り付けている画像に動きをつけました。
内容は以前紹介した　**「1冊ですべて身につくJavaScript入門講座」**　を進んだり戻ったりしながら書きました。

**css**　
```diff_css
.other {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  max-width: 1020px;
  gap: 30px;
  margin: auto;  
  padding: 30px;
}

.item{
  width: 300px;
  height: 300px;
  }
  ```
  **JavaScript**
  ```diff_javascript
  // footer画像のアニメーション
// .itemの配列要素を個別に取得
const imgItem = document.querySelectorAll('.item');

for(let i = 0; i < imgItem.length; i++){
  const keyframes = {
    // 定数keyframesで画像の透明度を指定する
    opacity: [0, 1],
  };

// 再生時間の指定
  const option = {
    duration: 800,
  // fill: 'forwards'はキーフレームの状態を保持するという意味
    fill: 'forwards',
  };
imgItem[i].animate(keyframes, option);
}
```
  ![261.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3810013/83cad45f-952a-2273-bff9-92d76cc1736e.png)
画面撮影した動画の埋め込みがアプリの種類の関係でできないため画像での紹介になります。
今回は opacityとduration を使って、0.8秒後に4枚の画像がすべて同時に表示されるようにコードを書きました。
参考本を読んでいる中で、direction というアニメーションを実行する方向の指定や、easing というアニメーションが変化する速度、タイミング、fill というアニメーション再生前後の状態を指定できるプロパティがあると知り、その値を見に行ったり来たりしました。あとは、jsのページでコードの意味をコメントアウトで書いたり…

以前にも書きましたが、慣れるまで日本語で整理してからコードを書くなどした方が、今何をしているか理解できるような気がします。または、コードを読んで、日本語で何をしているコードなのかを書いても勉強になるなと思います。

慣れるまでとは言いますが、コードを書きまくった方が指も動かせるし、覚えられるのでしょうか…？:thinking:

明日は、この画像を数秒遅れで表示されるようにそれぞれ指定し、順番に表示されるようにしていきたいと思います。
(SSD1306とM5StampS3)「デカ文字」まとめ(リンク集)




------------------------------------------

![o_coq221.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/918430b2-d606-edb8-3fcf-54b5366c413a.jpeg)


------------------------------------------


●　ノー仮想VRAM1024バイト


https://qiita.com/caa45040/items/89ebc26f1a4809a139d0


https://qiita.com/caa45040/items/3d1e261af565e7d6f6e1


https://qiita.com/caa45040/items/4b009b283b0ddbf8c4a2


https://qiita.com/caa45040/items/d4bf91be7be3f7fed93b


●　仮想VRAM


https://qiita.com/caa45040/items/31e16533153d4dca5e5f


https://qiita.com/caa45040/items/5de39ab606e708a62ccc
backdrop-filter が iOS だと効かない問題

## 概要

`backdrop-filter` プロパティは、背景のぼかしやカラーシフトなどの視覚効果を適用するために用いられます。今回は会員登録しているユーザーにのみコンテンツを表示し、その他のユーザーにはそのコンテンツをぼかして表示するという機能を実装するのに用いました。しかし、iOS で確認したときにぼかしが効かないという問題が発生しました。

## 原因

iOS では、`backdrop-filter` プロパティがサポートされているバージョンとされていないバージョンがあります。特に、古いバージョンの iOS では、このプロパティが正しく機能しません。

## 対処法

こちらの問題に対処するには、ベンダープレフィックスが必要でした。`-webkit-` プレフィックスを追加すると、iOS でもぼかしが効くようになりました。

```css
.box {
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
}
```

## 参照

https://developer.mozilla.org/ja/docs/Web/CSS/backdrop-filter
【TypeScript】nullやundefinedの可能性があってもエラーが発生しないようにする


非nullアサーション演算子(`!`)を使用すると`null`または`undefined`でないことをTypeScriptに伝えることができます。

```ts
let sample: string | null = fetchSomeNullableString()
let test: string = sample! // 非nullアサーション演算子を使用
```

上記の例では、`sample`の型は`string | null`ですが、非nullアサーション演算子を使用することで、`string`型である`test`に`sample`を代入できています。
読書メモ『世界一流エンジニアの思考法』

## この記事は何
以下のような状況を改善するためにメモがてら書いてみる
- 本を読んでも内容をあまり覚えていない
- 大事な所だけは見返せるようにしたい
- 文章力が落ちてきている

## Amazonリンク
https://www.amazon.co.jp/%E4%B8%96%E7%95%8C%E4%B8%80%E6%B5%81%E3%82%A8%E3%83%B3%E3%82%B8%E3%83%8B%E3%82%A2%E3%81%AE%E6%80%9D%E8%80%83%E6%B3%95-%E7%89%9B%E5%B0%BE-%E5%89%9B/dp/4163917683/ref=sr_1_1?crid=Z1NDJX2VLH53&dib=eyJ2IjoiMSJ9.1kFEfziypsHC81I2z_UvD9AJGXdhQTlVkRu2qq7V2cjg-oPl1jsHWse5LigPUlPEE0RTn7Ipdnfw-d7_YP7gW38Lt8-216vR_1n2uGaXGBblmezf1XilCdxGghRPuMKk9F2DcUnUiT7CiI90LwPgZclml2IEcDOWN5YfUrW80f7xwfAhMkzp2Cpg1IDmCrHzVgN17h3tXQkqqwXO45RHQhR9VvXLsPI_zOSJTFE14bOb8XKx48I_66iexoJDj8gCHtf8eQx6u4Dsp2g_5ALM9ZK2xVb4bQomlhRJBLmEsWg.uBt9jZAZ_SbGSNgP0OhUadiAN2agCHMczS7Wluw3r5I&dib_tag=se&keywords=%E4%B8%96%E7%95%8C%E4%B8%80%E6%B5%81%E3%82%A8%E3%83%B3%E3%82%B8%E3%83%8B%E3%82%A2%E3%81%AE%E6%80%9D%E8%80%83%E6%B3%95&qid=1722004870&sprefix=%E3%81%9B%E3%81%8B%E3%81%84%2Caps%2C169&sr=8-1

## 結論
自分の人生や幸せに責任をもって, 自分でコントロールする


## 1章：世界一流エンジニアは何が違うのだろう？
### 試行錯誤は悪
むやみに手を動かしても時間を喰うだけ
仮説を立てて, その仮説を証明するために手を動かす

### どんな人でも理解に時間かかる
「人に説明できる・いつでも取り出して使える・応用できる」レベルで理解をする
基礎は大事

### 脳内イメージを構築する
考えが整理され, 問題解決しやすくなる

## 2章:アメリカで見つけたマインドセット
### 少ない時間で価値を最大化する
最低限の努力をする
優先順位を決めてやることを減らす
時間は有限

### リスクや間違いを受け入れる
まずはやって, 早くFBを受ける, 早く失敗する
検討している時間がリスク

## 3章：脳に余裕を生む情報整理・記憶術
### 脳の負荷を下げる
コードは正しい前提で極力読まない
マルチタスクはしない
自分だけの時間を確保する
アウトプットも大事だが, しっかり理解してlevel1を増やした方が長期的にいい
- level1: 何も調べず実装できる
- level2: 調べれば実装できる
- level3: 大まかなプログラムがあれば実装できる
- level4: 自分では難しい, ものすごく時間がかかる

## 4章：コミュニケーションの極意
### 情報量を減らす
付加情報は聞かれてからでいい

### 相手が求めている情報を考える
人に伝えることを前提としてメモする
読んだ人を考えながらコードを書く

### 時には直接話した方が早い
違和感を感じた時点でオフラインで話す
詰まったら10分程度でも画面共有して一緒にやると効率がいい

### 相手を受け入れる
ディスカッションで正誤は関係なく相手のことを理解して認める
相手を否定しない, 尊重する

## 5章：生産性を高めるチームビルディング
### メンバー主体, マネージャーはサポート
タスクの割り振りもメンバーが行う
自分で自分の考えや人生に責任を持つ
メンバーが仕事を楽しめているかが重要
マネージャーは障害を取り除く

## 6章：仕事と人生の質を高める生活習慣術
### 
定時で上がって, その後は学習にあてる
タイムボックス(時間でやることを区切る)を導入する
疲れたら別のことをする
何かをしたら完了までやる

### 脳を休ませる
瞑想する
ディスプレイから離れる
睡眠時間を確保する

### 整理する
ファイルを整理する
ブックマークを整理する
アウトプットして脳内を整理する

### 運動する
筋トレする
有酸素運動をする

## 7章：AI時代をどう生き残るか
専門性を追求する, 高める
ActiveRecord::Type::Boolean.new.castの利用方法

こんばんは。

今日は「ActiveRecord::Type::Boolean.new.castの利用方法」について説明します。

ActiveRecord::Type::Boolean.new.cast メソッドは、入力された値をブール値に変換する際に注意が必要です。
なぜなら、異なるタイプの入力を許容し、それをブール値に変換する際に予想外の結果を引き起こす可能性があるからです。

# どういうことか

`ActiveRecord::Type::Boolean.new.cast`は引数をtrueあるいはfalseに変換してくれるのだが、変換の基準は以下のものに引っかかるものがfalseになる。

クラスとしてはこちらに定義がある。

```
FALSE_VALUES = [
  false, 0,
  "0", :"0",
  "f", :f,
  "F", :F,
  "false", :false,
  "FALSE", :FALSE,
  "off", :off,
  "OFF", :OFF,
].to_set.freeze
```

また、`""` と `nil` は `nil` になる

```
def cast_value(value)
  if value == ""
    nil
  else
    !FALSE_VALUES.include?(value)
  end
end
```

つまり以下のものはすべて false に変換される

- false
- 0
- "0"
- :0
- "false"
- :false
- "FALSE"
- :FALSE
- "off"
- :off
- "OFF"
- :OFF

また、 以下のものは nil になる

- nil
- ""
そしてそれ以外のものは true になる。

false になるシリーズは以下

```
target = "f"
ActiveRecord::Type::Boolean.new.cast(target)
# => false

target = 0
ActiveRecord::Type::Boolean.new.cast(target)
# => false

target = "false"
ActiveRecord::Type::Boolean.new.cast(target)
# => false

target = false
ActiveRecord::Type::Boolean.new.cast(target)
# => false

target = "FALSE"
ActiveRecord::Type::Boolean.new.cast(target)
# => false

target = "off"
ActiveRecord::Type::Boolean.new.cast(target)
# => false

target = :off
ActiveRecord::Type::Boolean.new.cast(target)
# => false

target = :false
ActiveRecord::Type::Boolean.new.cast(target)
# => false

target = :FALSE
ActiveRecord::Type::Boolean.new.cast(target)
# => false

target = 0
ActiveRecord::Type::Boolean.new.cast(target)
# => false

target = 1
ActiveRecord::Type::Boolean.new.cast(target)
# => true
```

ただし `False` (いわゆるパスカルケース)は設定値にないので `true` になる

```
target = :False
ActiveRecord::Type::Boolean.new.cast(target)
# => true

target = "False"
ActiveRecord::Type::Boolean.new.cast(target)
# => true
```

そして空文字とnilはnilになる
```
target = nil
ActiveRecord::Type::Boolean.new.cast(target)
# => nil

target = ""
ActiveRecord::Type::Boolean.new.cast(target)
# => nil
```

# 注意点

- 明示的な変換: 可能であれば、入力を明示的にブール値に変換することを検討してください。
- ユーザー入力の検証: ユーザーからの入力を直接ブール値に変換する前に、検証や前処理を行うことが重要です。
- 予期せぬ変換: 例えば、文字列 "some text" が true に変換されることは、多くの人にとって直感的ではないかもしれません。

ActiveRecord::Type::Boolean.new.cast を使用する際は、これらの点を考慮に入れて、慎重に扱うようにしてください。

今日は以上です。

ありがとうございました。
よろしくお願いいたします。
paizaの問題をPowerShellで解いてみた【文字の一致 (三項演算子をつかう)】

この記事はpaizaのプログラミング問題をPowerShellで解いてみようという試みの記事です。

## paizaのプログラミング問題とは？
ブラウザ上でコードを書いて実行できる、無料プログラミング練習問題集です。
1. 難易度別に問題を選ぶ
2. 問題文を読んで、解答コードを書く
3. 提出するとテストケースが実行されて結果をわかる
正解・不正解だけではなく、解答コード例や解説も見ることができるので、プログラミングの習得から復習まで活用できます

## こんな人におすすめ
- プログラミングスキルの腕試しをしたい方
- プログラミングのスキルアップしたい方
- 新しい言語にチャレンジをしたい方

###

通常は問題をc言語やJavaなどで解くのですがPowerShellで解いてみます。

## 問題
文字の一致 (paizaランク D 相当)

https://paiza.jp/works/mondai/d_rank_skillcheck_sample/diff_str


### 問題: 2 つの文字列 a, b が入力されます。文字列が一致していれば "OK" 、異なっていれば "NG" と出力してください。

入力: 文字列aと文字列b
出力: aとb が一致していれば "OK" 、異なっていれば "NG" 


### 解き方:
1. 標準入力から2行の文字列を読み込む
1. 文字列を比較一致していれば "OK" 、異なっていれば "NG"を出力



### コード:

#### PowerShell バージョン7.4以降
~~~
# PowerShell バージョン7.4以降
((read-host) -eq (read-host)) ? "OK" : "NG"
~~~

##### PowerShell バージョン7.4以降コードの説明:
PowerShellはバージョン7.4から三項演算子が使えるようになりました。
ワンライナーも書けます。

#### PowerShell V5系
 下はいつものバージョン5系
~~~
# PowerShell V5系
$(if((read-host) -eq (read-host)) { "OK" } else { "NG" })
~~~
V5系でも＄()を使うと三項演算子風の書き方ができます。



### 終わりに
noteにPowerShellのサンプルコードの記事を書いています。そっちも見てね
[PowerShellまとめ](https://note.com/mahalo_/m/m421b17a881c8)

https://note.com/mahalo_/m/m421b17a881c8
プリザンターにマスタメンテ機能を委ねる

# マスタメンテ画面とは
システム上、その値が無ければ動作する事ができない設定値等を管理する画面の事で、具体的には商品マスタ、取引先マスタなどが挙げられます。

# マスタメンテ画面の辛い所

システム上、重要な位置付けにあるマスタメンテ画面ですが、見積り、実装になると極端に低い値を叩き出す事で有名です **（金額という数字も含む）**

マスタメンテ画面は必要だが **実装単価が安い（というか0に近い）** というのが実態ではないでしょうか（私だけかもしれませんが）

作らないと怒られるし、作る時間も無いものを作らなければいけない場合、最低限のコストで出来ないか、というのを考えたもが **プリザンターで実現する** という方法になります。

# プリザンターとは
プリザンターは、無料で使えるOSSのノーコード・ローコード開発ツールです。
パッケージソフトには真似できないほど業務にフィットしたWebアプリを、フルスクラッチとは比べ物にはならないスピードで簡単に作成できます。（公式より）

https://pleasanter.org/

# 実装方法（考え方）
実装方法というか考え方に近いニュアンスですが **プリザンターにデータを格納してRestAPIで取ってくる** といものです。

マスタメンテ画面はプリザンターで作成し、RestAPI経由で必要なパラメータを取得する、という流れです。API仕様については本家のドキュメントを参考にしています。

https://pleasanter.org/manual/api

# プリザンターの魅力
プリザンターをマスタメンテ画面として使う魅力を考えてみました。

### オンプレ環境にインストールできる
プリザンターはオンプレに展開可能ですので、ネットワークの閉ざされた環境であっても構築することができます。

```plantuml
@startuml
package プリザンター
circle システム

プリザンター <--> システム : RestAPI
@enduml
```

### クラウド環境対応

プリザンターはAzure等のクラウド環境にも対応しているので、インターネットからデータを更新したいシステム等がある場合、非常に便利です。

```plantuml
@startuml
cloud プリザンター
interface システム

プリザンター <--> システム : RestAPI
@enduml
```

更に、プリザンターに複数のシステムからなる設定値を格納する事も可能です。

```plantuml
@startuml
cloud プリザンター
interface システムA
interface システムB
interface システムC
interface システムD

プリザンター <--> システムA : RestAPI
プリザンター <--> システムB : RestAPI
プリザンター <--> システムC : RestAPI
プリザンター <--> システムD : RestAPI
@enduml
```

# デメリット
素晴らしいプリザンターですが、いくつかデメリットがあります。

### 考えられるデメリット
* マスタメンテ画面となるプリザンターを更新する、というメンテナンス作業が発生する
* プリザンター用にパスワード等を管理する必要がある

# 最後に
規模にもよるかもしれませんが、プリザンターをマスタメンテ画面として利用する事を検討するのは価値があるのではないでしょうか。
ワンランク上のVBAを書いてみた

どうも。
葬送のフリーエンです。

## 皆さんは、VBAをご存じだろうか？
Excelに標準でついてくる言語のひとつで、マクロとも呼ばれていたりする言語です。

VBAのいいところは開発環境の構築が、ExcelやWordなどのOffice製品のみで完結するため、
Office製品がインストールされている環境(PC)であれば、ほぼ使える。
VBAの歴史は長く、古来より情報は公開され、いろいろなコードもまた生成されてきたコンニチ。
そんな無駄遣いにされたコードたちを送る...

それでは、本題に入ります。
まず、はじめにExcelの真骨頂は、データベースを扱いやすく設計されていることでしょう。
方眼紙の線がそれを物語っています。
このVBA関数TableDataFormatは、指定されたExcelのテーブルからデータを配列に変換して返す役割を持っています。
入力パラメータとしてselectedTableを受け取り、これは操作対象のワークシートとテーブル名に使用されます。この関数はString型の2次元配列を返します。

## コードの動作
この関数はselectedTableを引数として受け取ります。
対象のワークシート(ws)とテーブル(tbl)を特定します。
テーブルの行数と列数に基づき、items配列のサイズを再定義します。
二重ループを使用して、テーブルの全てのデータを読み取り、items配列に格納します。
完成した配列を関数の出力として返します。

```VB:Module1
Function TableDataFormat(selectedTable As String) As String()
    Dim items() As String
    Dim ws As Worksheet
    Dim tbl As ListObject
    Dim i As Long, j As Long

    On Error GoTo ErrorHandler
    Set ws = ThisWorkbook.Sheets(selectedTable)
    Set tbl = ws.ListObjects(selectedTable)
    If tbl Is Nothing Then Err.Raise Number:=vbObjectError + 513, Description:="Table not found."

    ReDim items(0 To tbl.ListRows.Count - 1, 0 To tbl.ListColumns.Count - 1)
    For i = 0 To tbl.ListRows.Count - 1
        For j = 0 To tbl.ListColumns.Count - 1
            items(i, j) = tbl.DataBodyRange.Cells(i + 1, j + 1).Value
        Next j
    Next i
    TableDataFormat = items
    Exit Function

ErrorHandler:
    TableDataFormat = Nothing
    MsgBox "Error: " & Err.Description
End Function

```

## 使用方法: TableDataFormat関数
TableDataFormat関数は、引数としてExcelのテーブル名を受け取ります。このテーブル名は、ワークシート上で定義されたテーブルの名前である必要があります。

例
ExcelにSalesDataという名前のテーブルがある場合、次のように関数を呼び出すことができます。

VBAエディター内で新しいサブルーチンを作成するか、即時ウィンドウを使用します。
次のコードを実行して関数をテストします。

```VB
Sub TestTableDataFormat()
    Dim output As Variant
    output = TableDataFormat("SalesData")
    
    ' 出力結果をデバッグウィンドウに表示
    Dim i As Long, j As Long
    For i = LBound(output, 1) To UBound(output, 1)
        For j = LBound(output, 2) To UBound(output, 2)
            Debug.Print output(i, j)
        Next j
    Next i
End Sub


```
Tauri 2.0 でマルチウィンドウが正しく動かない時の対処法〈Windows〉

Tauri 2.0 にはウィンドウを動的に複数作成できるマルチウィンドウの機能があるのですが、Windows環境ではウィンドウ作成時にバグが発生します。

その対処法です。

# 同期の#[tauri::command]下で動かない

新しいウィンドウでexample.comを表示するプログラムを考えます。
まず、以下のコードは正しいですが、動きません。
作成後、空のウィンドウが表示されますが、その後操作を受け付けなくなります。
```rust
#[tauri::command]
fn open_new_window(handle: tauri::AppHandle) {

    let window = WebviewWindowBuilder::new(
        &handle,
        "hoge",
        tauri::WebviewUrl::External("https://example.com/")
    )
    .build().expect("failed to create new window");

    window.show().expect("failed to show window");
    
}
```

それは、この関数がasyncになっていないからです。
以下のようにasyncにしてやると、正しくウィンドウが生成されます。

```rust
#[tauri::command(async)]
async fn open_new_window(handle: tauri::AppHandle) -> Result<(), ()>{

    let window = WebviewWindowBuilder::new(
        &handle,
        "hoge",
        tauri::WebviewUrl::External("https://example.com/")
    )
    .build().expect("failed to create new window");

    window.show().expect("failed to show window");
    
    Ok(())
}
```

非同期関数をcommandに使う場合は、返値をResult型にしなければならないため、そうしています。
なお、このasync下でしかWebviewWindowBuilderが動かないという現象は[バグらしい](https://github.com/tauri-apps/wry/issues/583)です。
今後修正されるのかもしれません。

[Docs.rs](https://docs.rs/tauri/2.0.0-beta.22/tauri/webview/struct.WebviewWindowBuilder.html)に次のように書いてあります。
> Known issues
> 
> On Windows, this function deadlocks when used in a synchronous command, see the Webview2 issue. You should use async commands when creating windows.

ResultはOkが返りますし、panicすらしない不具合なので、調べるのが大変でした。
【GAS覚書き】doPostではリクエストヘッダー情報を取得できない

# 初めに
GASを用いてLINE BOTからのリクエストを処理しようと思っていたのですが、リクエストヘッダー情報が取れずリクエストの検証ができませんでした。

# doPost()とは
`doPost()`は、デプロイしたGASに対してPostのリクエストを投げた際、そのリクエストを処理する関数になります。
同様に、GETのリクエストを処理する`doGet()`もあります。


```js
/*
 * 引数eにはリクエスト情報が入っている
 */
function doPost(e) {

  // レスポンスで
  return ContentService.createTextOutput(
    JSON.stringify({'content': 'post ok'})
  ).setMimeType(ContentService.MimeType.JSON);
}

```

# リクエスト情報には何が入っているのか
ということで、引数の`e`に何が入っているのかを見てみようと思います。
`e`をそのままスプレッドシートに吐き出すようなコードを書いてみました。

```js
function doPost(e) {
  
  // リクエスト情報のeをログ出力してみる
  var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
  var sheet = spreadsheet.getSheetByName('log');
  sheet.appendRow([new Date(), JSON.stringify(e)]);


  return ContentService.createTextOutput(
    JSON.stringify({'content': 'post ok'})
  ).setMimeType(ContentService.MimeType.JSON);
}
```

送るリクエストは以下です。
ヘッダーに付与した値が`e`に含まれているかどうかを見てみましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/430841/35390df2-336e-9159-a02c-0a8890a57773.png)


こちらが実際に`e`に送られてきたデータを整形したものになります。
`postData->type`にリクエストヘッダで指定した`Content-Type`の値が入ってますが、それ以外の値はとれていません。

```json
{
    "contextPath": "",
    "parameter": {},
    "queryString": "",
    "parameters": {},
    "postData": {
        "contents": "{\n  \"test\": \"ttt\"\n}",
        "length": 19,
        "name": "postData",
        "type": "application/json"
    },
    "contentLength": 19
}
```

# 一応公式ドキュメントも見てみる

GASの[公式ドキュメント](https://developers.google.com/apps-script/guides/web?hl=ja#request_parameters)を見ると、`e`にはヘッダー情報は詰められてなさそうです。


# 終わりに
ということで、doPostではリクエストヘッダーを取得することはできませんでした。取得したい場合はGASじゃなくGoogle Cloud Functionsなど使うのがべきなんでしょうね。

LINE Bot自体はヘッダー情報がなくても動かすことができますが、本当にLINEからのリクエストかどうかを検証することはできません。Google Cloud FunctionsやLambdaでの実装を検討しようと思います。

もしGAS上で取得できる方法を知っている方いたら教えてください。
ここまでご覧いただきありがとうございました！
AtCoder 勉強記録[C++] その32

AtCoderで入茶を目指して勉強しています。
勉強を継続するために投稿を始めました。
もともとアカウントを作成していましたが、今年の4月から本格的に勉強を始めました。
一応自分用に解法を書いていますが雑です、自分で読み返して困ったら修正します。
[私のアカウント](https://atcoder.jp/users/nanami_brother)
[解いた問題](https://kenkoooo.com/atcoder/#/table/nanami_brother)
# 本日解いた問題
## B - Modulo Number
[B - Modulo Number](https://atcoder.jp/contests/abc266/tasks/abc266_b)
解答
```c++
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using vec = vector<ll>;
#define rep(i, n) for (int i = 0; i < (int)(n); i++)
#define m 998244353


int main() {
  ll n;
  cin >> n;
  for(int i = 0; i < m; i++){
    if((n-i)%m == 0) cout << i << endl;
  }
}
```
### 解法
0から998244353までをすべて試してもギリギリ実行時間以内に収まると思いこちらのコードで解いた。実際`AC`できたが、解説を読んだらNを998244353で割ったあまりで良いことに気付いた。
## C - Connect 6
[C - Connect 6](https://atcoder.jp/contests/abc241/tasks/abc241_c)
解答
```c++
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using vec = vector<ll>;
#define rep(i, n) for (int i = 0; i < (int)(n); i++)


int main() {
  int n;
  cin >> n;
  vector<string> s(n);
  rep(i, n) cin >> s[i];
  //縦
  bool jud = false;
  for(int i = 0; i < n-5; i++){
    for(int j = 0; j < n; j++){
      ll cnt = 0;
      for(int k = 0; k < 6; k++){
        if(s[i+k][j] == '#') cnt++;
        if(cnt>=4) jud = true;
      }
    }
  }
  //横
  for(int i = 0; i < n; i++){
    for(int j = 0; j < n-5; j++){
      ll cnt = 0;
      for(int k = 0; k < 6; k++){
        if(s[i][j+k] == '#') cnt++;
        if(cnt>=4) jud = true;
      }
    }
  }
  //右上から左下
  for(int i = 0; i < n-5; i++){
    for(int j = 0; j < n-5; j++){
      ll cnt = 0;
      for(int k = 0; k < 6; k++){
        if(s[i+k][j+k] == '#') cnt++;
        if(cnt>=4) jud = true;
      }
    }
  }
  //左上から右下
  for(int i = 0; i < n-5; i++){
    for(int j = n-1; j > 4; j--){
      ll cnt = 0;
      for(int k = 0; k < 6; k++){
        if(s[i+k][j-k] == '#') cnt++;
        if(cnt>=4) jud = true;
      }
    }
  }
  if(jud) cout << "Yes" << endl;
  else cout << "No" << endl;
}
```
### 解法
ある一つのマスから6つのマスを考えた時に、黒が4個以上であれば`Yes`、4個未満であれば`No`を出力する。また、6つのマスを考える際に、配列の要素数に気を付ける。
エラー対処　System.ArgumentNullException: e

以下エラーが出た時の解決方法について

System.ArgumentNullException: e
This Exception was thrown from a job compiled with Burst, which has limited exception support.

まったくもって、不可解なエラーである。
全文読むとエラー箇所が書いてあるが、その場所は間違っている。ECSのエラー解析が難しい原因の代表例みたいなエラー。

恐らくこのエラーは、同じExecute内で同じcompornentsに対して、複数回内容変更した時に発生するものと思われる。

例えば、以下のようにCharactorAttackPropertiesというコンポーネントを作って、中に色々と定義する。
```
    public struct CharactorAttackProperties : IComponentData
    {
        public float3 FirePointOffset;
        public Entity AttackPrefab;
        public float CoolDownTime;
        public float EnemySearchCooldownTime;
        public bool alreadyAttack;
    }
```
その際、alreadyAttackの値を変える場面で、上記エラーが発生するとする。
その時、以下のようにコンポーネントを分離するだけで、まったく同じ流れのコードを記述しても、エラーが出なくなる。
```
    public struct AlreadyAttack : IComponentData
    {
        public bool alreadyAttack;
    }
```

デバックが非常に困難だし、エラー原因も明確にわからないので、
対処方法としては、Compornent内に複数変数を作りすぎず、なるべくCompornentを小分けにしてアタッチすることぐらいと思われる。

釈然としないが、備忘録として残す。
DockerのMicrosoft SQL Server - Ubuntu based images 2022-latest のsqlcmdのパスが/opt/mssql-tools/binから/opt/mssql-tools18/binになっていたメモ

## 概要

[Microsoft SQL Server - Ubuntu based images](https://hub.docker.com/r/microsoft/mssql-server)に入っている`sqlcmd`の場所が変わっていたのでメモ。

## バージョン

確認したところ、切り替わりの境目は13と14だった。

Tags|sqlcmdのパス
--|--
2022-CU14-ubuntu-22.04|/opt/mssql-tools18/bin
2022-CU13-ubuntu-22.04|/opt/mssql-tools/bin

## 18について
18とは何の数字かと調べたところ、[Linux に SQL Server コマンドライン ツール sqlcmd および bcp をインストールする](https://learn.microsoft.com/ja-jp/sql/linux/sql-server-linux-setup-tools?view=sql-server-ver16&tabs=redhat-install)を読むと`Microsoft ODBC 18 packages`とある。[ODBC](https://learn.microsoft.com/en-us/sql/connect/odbc/linux-mac/installing-the-microsoft-odbc-driver-for-sql-server?view=sql-server-ver16&tabs=alpine18-install%2Calpine17-install%2Cdebian8-install%2Credhat7-13-install%2Crhel7-offline)のバージョンだったもよう。

なお、[クイック スタート:Docker を使用して SQL Server Linux コンテナー イメージを実行する](https://learn.microsoft.com/ja-jp/sql/linux/quickstart-install-connect-docker?view=sql-server-ver16&tabs=cli&pivots=cs1-bash)では18のついていないバージョンでの説明になっていた。

## まとめ
`-latest`はツールのパスが変わることもあるリスクがあることは覚えておかねばなと思いました。

## 参考
[MySQL・MSSQL・PostgreSQLで接続するまでの流れまとめ](https://qiita.com/itohdaigo/items/fd7a487911b720901eef)
【PostgreSQL】SELECTの結果を配列形式で取得（ARRAY_AGG）する際に重複を排除したい

## 概要
SELECTの結果を配列として集約する際、`ARRAY_AGG`関数を使用する方法が挙げられます。ARRAY_AGGについては[PostgreSQLで配列を扱う](https://tech.anti-pattern.co.jp/postgresql-array/)の記事を参照ください。
ARRAY_AGGで集約した結果について重複がありうる場合、どのように重複排除するのかというのを今回メモ書きします。

## 前提
* 今回使用したPostgreSQLのバージョンは16.1です。

## 対応方法
[How to remove duplicates, which are generated with array_agg postgres function](https://stackoverflow.com/questions/26363742/how-to-remove-duplicates-which-are-generated-with-array-agg-postgres-function)のstackoverflowの記事で紹介されている通り、ARRAY_AGGの中でDISTINCTを指定できるようですので、この対応を今回は試してみました。

## 実装サンプル

### <前準備>
以下の3テーブルを用意します。

```sql
CREATE TABLE public.sample_place (
	id varchar(100) NOT NULL,
	"name" varchar(100) NOT NULL,
	CONSTRAINT sample_place_pkey PRIMARY KEY (id)
);

CREATE TABLE public.sample_users (
	id varchar(100) NOT NULL,
	"name" varchar(100) NOT NULL,
	CONSTRAINT sample_users_pkey PRIMARY KEY (id)
);

CREATE TABLE public.sample_visit_log (
	user_id varchar(100) NOT NULL,
	place_id varchar(100) NOT NULL,
	visited_at timestamp NOT NULL
);
```

以下の通りテストデータを登録します。
<img width="200" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32098/ad2e2e14-725a-3383-ff92-24cc47692bae.png">
<img width="200" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32098/4685ce5b-3404-0b5e-773c-315a1bfb6c7e.png">
<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32098/75026f02-a67a-365c-8acf-e96029a59c5e.png">

### <クエリ>
取得する内容としては`sample_visit_log`に対して、user毎にplace_idの配列を取得します。
まずはDISTINCTを指定しなかった時の結果です。重複排除されずにSELECTそのままの結果が配列になります。

<img width="350" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32098/2f4f85bb-dde4-6198-f681-df620ba25c3e.png">


DISTINCTを指定した時の結果です。重複排除された結果が配列になっています。

<img width="350" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32098/cb9865d9-d1d5-d630-53f7-32ea384e06e1.png">
Flutterのマテリアル構造とレイアウト ~FlutterAppの基本~ 

### [FlutterAppの基本](https://flutter.ctrnost.com/tutorial/tutorial03/)

コメントアウトで自分の理解を書いてみました。
```dart
// material designを使用するためのパッケージのimport
import 'package:flutter/material.dart';

// 返り値を返さないmain関数にrunApp関数を代入する
// runApp関数は引数にWidgetを取り、それを画面に表示する機能を持つ
// void main()のvoidとは、返り値がないことを示す。
void main() => runApp(MyApp()); 

// class MyAppはStatelessWidgetクラスを継承する
// StatelessWidgetクラスは、状態を持たないWidgetを作成するためのクラス
// 一度描画されたら変更されることがないWidgetを作成するためのクラス
class MyApp extends StatelessWidget {

  // @overrideは、継承元のクラスにあるメソッドを上書きすることを示す
  @override
  
 //Widget buildのbuildは、メソッド名でWidgetは返り値の型。
 // buildメソッドは、Widgetを返すメソッドで、画面に表示する内容を記述する
 // 引数にはBuildContextクラスのcontextという変数を取る
 // contextは、Widgetがどの位置に描画されるかを示す情報を持つ
  Widget build(BuildContext context) { 
    return MaterialApp(
      title: 'Welcome to Flutter',
      home: Scaffold(appBar: AppBar(
        title: Text('Welcome to Flutter'),
      ),
      body: Center(
        child: Text('Hello World'),
      ),
      )
    );
  }
}
```

#### main()関数でrunApp関数を呼び出す理由
main 関数の中で runApp 関数を呼び出し、その引数として渡したウィジェットが、Flutter フレームワークによってルートウィジェットとして認識される。

Flutter アプリケーションは、このルートウィジェットを起点としてウィジェットツリーを構築し、画面上に UI を描画する。
Linuc Level1を取得したので、合格法,実務への影響度をまとめました。

## 0 記事の対象読者

前提知識レベル：
専門知識：簡単なコマンド(cd,grep,find,cp,chmod)、リダイレクト、スクリプト
などは使うから知ってるくらいの人が対象(受ける前の自分がそうでした)
汎用知識：初学者（入門書を１冊読んだ程度）レベルのネットワークとプログラムの知識がある人
試験趣旨、費用,申込み方法,出題範囲やらは調べればわかるので省略してます
完全未経験者にはあまりお役に立てないかも

記事で教えられる事：
なるべく短時間で安上がりで受かる方法
そもそも受ける価値のある資格なのかの説明

## 1 初っ端結論

基本的なコマンドやシェルスクリプトをちゃんと理解していて、
サーバー運用がっつりやらん人にとっては受験料３万の価値は全然無い
その金をudemyの受けたい講座や欲しい有償の開発環境に使う方がよっぽど有意義
受験はしなくても、出題範囲から実務で使うとこだけ勉強するのは大いにありだと思う

逆に取得or勉強した方がいい人は以下の人達

・会社から取得を推奨されてる(費用負担がある)人
・サーバー運用業務やる人
・未経験からサーバーエンジニアを目指している人
・冒頭であげたコマンドも書けない人

自分の受験理由は
Linuxサーバーを運用する業務に長期就業することになったので、知識取得のため。
あと会社から、いつかは受けるように言われてたのと、会社が費用出してくれるから。


## 2 勉強法

Ping-t　https://ping-t.com/
Linucイージス　https://www.infraexpert.com/infra/study10.html
ここで問題集と模試、解説を勉強して、レベル40まで行くだけで確実に合格ボーダーまでいけます。
終わり。いや、本当にそれだけ。
現役のSEなら逆に本気でやって落ちるほうが難しいんじゃないだろうか。
目標は2週間+2週間で1ヶ月以内

以下は自分のスコアと勉強時間
101 580点 勉強日数 14日
102 600点 勉強日数 10日
1日4時間くらい学習



実際にコマンド叩きたい場合は無料のターミナル環境を用意しておく
自分はそれすら面倒だったので
https://bellard.org/jslinux/vm.html?url=alpine-x86.cfg&mem=192
みたいなシミュレーターを使ったり
現場の開発環境を動かして遊んでました。

未経験者でも問題集解いて解説もちゃんと覚えるだけで受かる。
暗記が多すぎてちと苦行ではあるけど

ping-tのコマ問は1単語で済む問題だけに絞ってやるのはあり。
フルコマンド問題は別にやらなくても受かります。

## 3 本番の問題

感触
7割：ping-tに似てるorほぼ同じ
2割：ping-tっぽくは無いけど解説の範囲内
～ここまでがping-tでカバーできる範囲～
1割：何言ってるかよくわからん問題

101のSSHと102のシェルとネットワークはたくさん出てきて、
残りはまんべんなくという感じ

ping-tを真面目にやれば、ping-t範囲から８０％は正解できるはずなので
0.9X0.8=0.72(580点くらい)で7割以上は自分の様に取れる
合格が60％(満点が800で合格が480)なので理論上問題なく受かる、という計算になる。

細かい回答テク(選択問題で迷った場合はあり得ない選択肢から除外する、など)
は皆言われなくてもわかってると思うので割愛

入力問題はポート番号、プロトコル、コマンド、オプション１個を入力する。
自分の時はHTTPSのポート番号、ssh-keygen、オプションの -r やらを入力させられた。

「ip route del 192.168.3.0/26 via 192.168.2.101 dev eth1」
のようなフルオプションコマンドを丸暗記で入力、などという理不尽な問題は出てこない


## 4 実務に活かせるかどうか

全部じゃないけどある程度は役に立つ
ただし後々まで忘れないようにちゃんと憶えてくべき箇所と、忘れて良い箇所がある

具体例を挙げると
tar,grep,ファイル、権限、ユーザー操作,パイプ,リダイレクト,簡単なシェルスクリプト,crontab設定,mail設定,DB設定、ログ調査コマンド、ネットワーク知識、
は常にわかるに越したことは無いので
過去問丸暗記で終わらせないで、大切に憶えて実践的なコマンドを打てるようにするべきでしょう

逆に自分が使わない知識の部分は感情をOFFにして受かるために丸暗記して、試験終わったらさっさと忘れていいです

自分の場合、debian系は一生触るつもりないので、apt-getは3日で忘れましたがtarやviは今もオプションまでちゃんと憶えています

## 5 最後に



もし受けるなら、真面目に勉強するのは当たり前だとしても
受かるのが前提の試験なので、あまり気負わずにやりましょう
なるはやで合格して次の資格やら勉強に行けるよう願っています
人工知能の研究向けの VSCode の拡張機能

# 人工知能の研究向けの VSCode の拡張機能

https://marketplace.visualstudio.com/

## はじめに

私は人工知能の研究を VSCode で行なっています。この記事では、私がインストールしている拡張機能を紹介します。

この記事がみなさんの参考になれば幸いです。

:::note info
随時、情報を更新する予定です。
:::

## インストールした拡張機能

### Data Wrangler

https://marketplace.visualstudio.com/items?itemName=ms-toolsai.datawrangler

### GitHub Copilot

https://marketplace.visualstudio.com/items?itemName=GitHub.copilot

### GitHub Copilot Chat

https://marketplace.visualstudio.com/items?itemName=GitHub.copilot-chat

### IntelliCode

https://marketplace.visualstudio.com/items?itemName=VisualStudioExptTeam.vscodeintellicode

### IntelliCode API Usage Examples

https://marketplace.visualstudio.com/items?itemName=VisualStudioExptTeam.intellicode-api-usage-examples

### isort

https://marketplace.visualstudio.com/items?itemName=ms-python.isort

### Japanese Language Pack for Visual Studio Code

https://marketplace.visualstudio.com/items?itemName=MS-CEINTL.vscode-language-pack-ja

### Jupyter

https://marketplace.visualstudio.com/items?itemName=ms-toolsai.jupyter

### Jupyter Cell Tags

https://marketplace.visualstudio.com/items?itemName=ms-toolsai.vscode-jupyter-cell-tags

### Jupyter Keymap

https://marketplace.visualstudio.com/items?itemName=ms-toolsai.jupyter-keymap

### Jupyter Notebook Renderers

https://marketplace.visualstudio.com/items?itemName=ms-toolsai.jupyter-renderers

### Jupyter Slide Show

https://marketplace.visualstudio.com/items?itemName=ms-toolsai.vscode-jupyter-slideshow

### Path Intellisense

https://marketplace.visualstudio.com/items?itemName=christian-kohler.path-intellisense

### Pylance

https://marketplace.visualstudio.com/items?itemName=ms-python.vscode-pylance

### Python

https://marketplace.visualstudio.com/items?itemName=ms-python.python

### Python Debugger

https://marketplace.visualstudio.com/items?itemName=ms-python.debugpy

### Remote - SSH

https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-ssh

### Remote - SSH: Editing Configuration Files

https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-ssh-edit

### Remote Explorer

https://marketplace.visualstudio.com/items?itemName=ms-vscode.remote-explorer

### Todo Tree

https://marketplace.visualstudio.com/items?itemName=Gruntfuggly.todo-tree

### YAML

https://marketplace.visualstudio.com/items?itemName=redhat.vscode-yaml

## おわりに

この記事では、私がインストールしている VSCode の拡張機能について、書かせていただきました。

この記事が、みなさんに貢献できれば幸いです。
JavaScript　クロージャ

# クロージャとは
[mozilla](https://developer.mozilla.org/ja/docs/Web/JavaScript/Closures)から引用させてもらうと、内側の関数から外側の関数スコープへのアクセスを提供するものだそう。

## どんな時に使うのか
複数回のメソッドコールに渡って変数の状態を管理したいけど、メソッドの外からは変数を触られたくない。といったケースにおいて、変数をメソッドの内側に定義した上で外から操作できるようにするための手法。

## 例
整理券を発行する用のメソッドを例に確認してみる。
名前を引数に渡すことで、番号を採番して名前と番号を返却するメソッドを作るとする。

変数の隠蔽を意識せずに書くと以下のようになる。
この場合、採番値(i)は自由に書き換えることができてしまう。

```javascript
let i = 0;

function numbering(name) {
  return `${++i}番 ${name}`;
}

console.log(numbering("田中"));
console.log(numbering("佐藤"));
```
そんな時にクロージャの出番。
変数iを変数に格納し、プライベートな変数にしてしまうことで外からさわれなくするのである。
外から呼ぶメソッドの戻り値として実際に動くメソッドを返すことで、変数をカプセル化しつつ同一の挙動を行えるようになる。

```javascript
function numberedTicket() {
  let i = 0;
  function numbering(name) {
    return `${++i}番 ${name}`;
  }
  return numbering;
}

const ticket = numberedTicket();

console.log(ticket("田中"));
console.log(ticket("佐藤"));
```
Everyday Rails - RSpecによるRailsテスト入門を読んで

# はじめに
Everyday Rails - RSpecによるRailsテスト入門を読了したので、良かった部分や学んだ部分についてまとめようと思います
# 良かったところ
* 章の組み立てがわかりやすい
    * テストの内容・説明→まとめ→章末問題という流れ
    * 最初、基礎的なテストの仕方を説明して、後の章で今回言うとFactoryBotを使って説明していて理解しやすい
* Rails5ではなくRails7で書かれていること
    * Ruby on Railsの教材の中でRails7の内容を書かれているものはあまり多くない印象です
    * 今後バージョンが上がった際も対応していただけそうな雰囲気を文章から感じました
# 学んだところ
### RSpec のセットアップ
* Gemfile

* ```ruby
    group :development, :test do

    gem 'rspec-rails' #追加
    
    end
    ```
    に記述して ``bundle install``実行
* テストデータベース
config/database.ymlを開き、どのデータベースにアクセスできるか確認

SQLiteなら
* ``` yml
    test:
      <<: *default
      database: db/test.sqlite3
    ```
 MySQL や PostgreSQLなら

* ``` yml
    test:
      <<: *default
      database: projects_test
    ```
こうなっています

そして``bin/rails db:create:all``を実行
すでに作成済みなら、rails タスクはテストデータベースがすでにあることを教えてくれる

RSpecをインストール
``bin/rails generate rspec:install``

### 意味のあるテストデータの作成
##### Factory Bot をインストールする
``` ruby
group :development, :test do
  gem "rspec-rails"
  gem "factory_bot_rails"

  # その他の gem は省略 ...
end
```
gemをインストールして例えば、Userモデルのファクトリを追加するときには

``bin/rails g factory_bot:model user``

このコマンドを実行すると、spec ディレクトリ内に factories という新しいディレクトリが
作られます。その中には users.rb という名前のファイルが作られます。

``` ruby
FactoryBot.define do
  factory :user do
    sequence(:user_name) { |n| "test_user#{n}" }
    sequence(:email) { |n| "test-#{n}@exmaple.com" }
    password { 'password' }
    password_confirmation { 'password' }
    phone { '09012345678' }
    birthdate { '2024-07-03' }
  end
end
```

そしてこのように記述してテスト内で 
``` ruby
FactoryBot.create(:user)
``` 
と書くだけで、
簡単に新しいユーザーの作成ができます

ですが、FactoryBot.create(:user)を繰り返し使用すると
emailなどが同じになってしまいバリデーションエラーを起こします
なのでシーケンスを使ってユニークバリデーションを持つデータを生成して防ぎます
``` ruby
sequence(:user_name) { |n| "test_user#{n}" }
sequence(:email) { |n| "test-#{n}@exmaple.com" }
```
とすることでユニークなメールアドレスを生成することができます

Factory Botはごちゃごちゃしがちなスペックを綺麗にできる点が非常に便利に感じました

### システムスペックで UI をテストする
* 統合テストの一つとしてシステムスペックがある
* 特にUIのテストとして有効
##### Capybara をインストールする
``` ruby
group :test do
  gem 'capybara'

  # その他の gem は省略 ...
end
```

### リクエストスペックで API をテストする

* リクエストスペックとシステムスペックの比較
    * リクエストスペックは高速で軽量なテストに適しており、主にサーバーサイドの動作やAPIのテストに使用されます。
    * システムスペックは現実的なユーザー体験をテストするのに適しており、フロントエンドの動作を含む複雑な操作をテストするのに使用されます。 
### スペックを DRY に保つ
##### let で遅延読み込みする
* let は呼ばれたときに初めてデータを読み込む、遅延読み込みを実現するメソッド
    * beforeブロックの外部で呼ばれるため、セットアップに必要なテストの構造を減らすこともできる


### テスト駆動開発に向けて
1. 最初にテストを書く（Red）
2. テストがパスするように最小限のコードを実装する（Green）
3. 必要に応じてコードをリファクタリングする（Refactor）

RSpec は失敗した内容をわかりやすく表示してくれるため、テストは失敗すべきであるとも言えます。

# 難しかったこと
* モックとスタブに関しての部分の理解
* 実務でのテスト駆動開発をどう進めていくのか

# さいごに
RSpecの知識を深めたい人には、まずはこれを読めば良いと進めたいぐらい本書は丁寧で読みやすかったです
まだまだ理解できていない部分もあるのでテストする際、都度読み返そうと思います。

https://leanpub.com/everydayrailsrspec-jp
【Angular】Materialのmat-inputを共通化してみる

オッス、俺泉（[@izumin_0401](https://twitter.com/izumin_0401)）！！

# Angular Materialのinputコンポーネントを共通化したい
mat-inputをとにかく共通化したいお年頃。

# ソースコード
## 共通inputコンポーネント
```typescript:input.component.ts
import { CommonModule } from '@angular/common';
import { Component, Input } from '@angular/core';
import { FormControl, ReactiveFormsModule } from '@angular/forms';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';

@Component({
  selector: 'app-input',
  standalone: true,
  imports: [CommonModule, MatFormFieldModule, MatInputModule, ReactiveFormsModule],
  template: `
    <mat-form-field [style.width]="width">
      <input matInput [attr.required]="required" [formControl]="control" [placeholder]="placeholder" />
    </mat-form-field>
  `,
})
export class InputComponent {
  @Input() control!: FormControl;
  @Input() placeholder: string;
  @Input() required: boolean = false;
  @Input() width: string;
}
```

## 呼び出し側
```html:app.component.html
<!-- サンプル1 -->
<app-input width="400px" [control]="control"></app-input>

<!-- サンプル2（プレースホルダー付き） -->
<app-input placeholder="うんこ" [control]="control" [required]="true"></app-input>
```

これで毎回「mat-form-field」を書く必要もなくなるのだ！

# まとめ
Angular Materialの共通inputコンポーネントを作ってみた。

mat-form-fieldが色んな箇所に記載されていてキモすぎたのでスッキリ。

https://traveler0401.com/angular-mat-input-common/

# 関連記事
https://qiita.com/izumin0401/items/c48bf6a7a4d03635a831

https://qiita.com/izumin0401/items/bbbaa3e4c70d6c6ebfc7

# 最後に
暇つぶしに[Twitter](https://twitter.com/izumin_0401)と[ブログ](https://traveler0401.com/)もやってるので見てね。