OmniAICreator/Qiita-1.07M huggingface dataset:
KittehNagExceptionのエラー出力を抑制する（標準エラー出力をオフにする方法）

2023年8月29日追記
以下の記事の内容はKittehの正式なエラーハンドリングをすればとくに悩むことじゃなかったのでそちらを説明しているドキュメントを共有しておきます。

https://kitteh-irc-client-library.readthedocs.io/en/latest/#debugging

---

javaのIRCクライエントライブラリのKittehで、暗号化なしでサーバ接続しようとするとエラーがコンソールに出力されます。

```java:接続処理
Client client = Client.builder()
            .nick("kitteh")
            .server()
            .host("127.0.0.1")
            .password("1234567890")
            .port(6667, SecurityType.INSECURE)
            .then()
            .buildAndConnect();
```

```:コンソールに出力される内容
org.kitteh.irc.client.library.exception.KittehNagException: Nag! This is not a fatal exception! Just a problem requiring your attention: Connection is insecure. If the server does not support TLS, consider enabling STS support to facilitate automatic TLS upgrades when it does.
        at org.kitteh.irc.client.library.defaults.DefaultClient.initialize(DefaultClient.java:310)
        at org.kitteh.irc.client.library.defaults.DefaultBuilder.build(DefaultBuilder.java:456)
        at org.kitteh.irc.client.library.defaults.DefaultBuilder.buildAndConnect(DefaultBuilder.java:475)
以下略
```

このコンソール出力を抑制したかったのですが、接続処理をtry句で囲んでもキャッチしてくれず最初困りました。

よくよく調べてみるとこのエラー出力は、生成された`KittehNagException`インスタンスを`throw`せずに、ただ`Throwable::printStackTrace`を呼んでいるだけでした。
(詳しい調査内容は省きますが、例外の処理方法を指定しているのは[ここ](https://github.com/KittehOrg/KittehIRCClientLib/blob/f7317e61d7ebd48a4293c5b10c67d576a95943de/src/main/java/org/kitteh/irc/client/library/defaults/DefaultBuilder.java#L358)です。)

```java:エラー出力処理を大雑把に書き下すとこんな感じ
new KittehNagException("だめです").printStackTrace();
```

ですので、標準エラーの出力をオフにすればこの出力を抑制できると思われます。

次のようにすればエラーが出力されないようにできました。念のため出力先を元に戻すコードもまとめてます。

```java:
PrintStream errDf = System.err; //　デフォルトの出力先をバックアップ
System.setErr(new PrintStream(OutputStream.nullOutputStream())); // 標準エラーの出力先を何もしないOutputStreamに変更
new KittehNagException("だめです").printStackTrace();
System.setErr(errDf); // 出力先をデフォルトのものに戻す。
```




参考
https://cyzennt.co.jp/blog/2020/10/09/java%EF%BC%9A%E6%A8%99%E6%BA%96%E5%85%A5%E5%87%BA%E5%8A%9B%E3%81%AE%E5%85%A5%E5%8A%9B%E5%85%83%E3%83%BB%E5%87%BA%E5%8A%9B%E5%85%88%E3%82%92%E5%A4%89%E6%9B%B4%E3%81%99%E3%82%8B/

https://docs.oracle.com/en/java/javase/12/docs/api/java.base/java/io/OutputStream.html#nullOutputStream()
LeetCode No.2

## 136. Single Number
Given a non-empty array of integers nums, every element appears twice except for one. Find that single one.

You must implement a solution with a linear runtime complexity and use only constant extra space.


使用言語：PHP
こちらはfor文やif文で試行錯誤したのですがうまくいきませんでした。。。
関数を探したところ
array_searchとarray_count_valuesを組み合わせて解けました！

```
class Solution {

    /**
     * @param Integer[] $nums
     * @return Integer
     */
    function singleNumber($nums) {
    $result = array_count_values($nums);
    return array_search('1', $result);
    }
}
```

ただ上記の解き方は、処理速度がかなり遅いらしく、
処理速度が速い他の解答も見つけたのでご参考までに

```
例１）
class Solution {

    /**
     * @param Integer[] $nums
     * @return Integer
     */
    function singleNumber($nums) {
        return array_flip(array_count_values($nums))[1];
    }
}

例２）
class Solution {

    /**
     * @param Integer[] $nums
     * @return Integer
     */
    function singleNumber($nums) {
        $result=0;
        for($i=0; $i<count($nums);$i++){
            $result ^=$nums[$i];
        }
        return $result;
    }
}

例３）
class Solution {

    /**
     * @param Integer[] $nums
     * @return Integer
     */
    function singleNumber($nums) {
        $a = 0;
        foreach ($nums as $num) {
            $a = $a ^ $num;
        }

        return $a;
        // Time complexity: O(n)
        // Space complexity: O(1)
    }
}

```


### 関数とその他
- array_count_values(array $array): array
    - 配列の値の数を数える
    - https://www.php.net/manual/ja/function.array-count-values
- array_search(mixed &#36;needle, array haystack, bool strict = false): int|string|false
    - 指定した値を配列で検索し、見つかった場合に対応する最初のキーを返す
    - https://www.php.net/manual/ja/function.array-search.php
- array_flip(array $array): array
    - 配列のキーと値を反転する
    - https://www.php.net/manual/ja/function.array-flip

*ドルマークを連続で書くと、他の引数が数式として表現されてしまうので、割愛しています。

- ビットとは
    - コンピュータさんの世界における「0か1が入る箱」
    - コンピュータが処理する最小単位
- ビット演算とは
    - ビットに着目して、そのon/offを操作すること
- ^ (^=)
    - サーカムフレックスと呼ばれ、「XOR」の演算子として使うことが多いです。
    -  排他的論理和と日本語で表現される
    - ビット演算（XOR エックスオア）で使う演算子
    - 2つのビットが同じだったら0、違ったら1になるよ
- ビット演算子
    - AND（&）: 2つのビットがともに1の場合に1を返し、それ以外の場合には0を返します。
    - OR（|）: 2つのビットのうち、いずれかが1の場合に1を返し、どちらも0の場合には0を返します。
    - XOR（^）: 2つのビットのうち、一方が1で他方が0の場合に1を返し、どちらも1または0の場合には0を返します。
    - NOT（~）: ビットを反転させます。つまり、0は1に、1は0に変換します。
*参考
https://www.sejuku.net/blog/78679

### なんでXORを使うやり方で重複している値を取得できるのか分からなかったので理由を書いておきます！
コードで重複しない値を見つけるためには、XOR演算子が持つ特性を利用しています。
XOR演算子は、同じ値同士をXORすると常に0になり、異なる値同士をXORすると1になるため、以下のような性質があります。
X ^ X = 0
X ^ 0 = X

つまり、同じ値を2回XORすると0になるため、重複している値同士のXORは結果に影響を与えません。逆に、異なる値同士のXORは必ず1になるため、重複していない値同士のXORは結果に反映されます。

そのため、このコードでは、配列内のすべての値を順番にXORしていくことで、重複している値は0になり、重複していない値だけが残ります。最終的には、配列内で重複していない値が1つだけ残るため、それが取得されます。

---------
2進法から10進法の変換方法をすぐ忘れそうなのでついでにULR貼っておきます
https://medium-company.com/2%E9%80%B2%E6%95%B0/
BVE 鉄道シミュレーターで自宅運転台を動かそう～電車でGoコントローラーを使う～2

__BVE 鉄道シミュレーターで自宅運転台を動かそう～電車でGoコントローラーを使う～__ の続きとして今回は __電車でGoコントローラー__ を改造しないで使えるようにします。

https://qiita.com/usashirou/items/ac358a8828f54f97bc2b

前回は、 __電車でGoコントローラー__ を分解し、基板に配線をつないで使えるようにしました。
今回は、__ArduinoNano__ に __PS/PS2コントローラー__ つないで直接使えるようにします。
# プログラム
__電車でGO!コントローラでTSマスコンをエミュレートしてBVEで遊ぶ。__ をそのまま使用しました。

https://github.com/7m4mon/dengo_ts_emu
# 基板の製作
__ArduinoNano__ と __PS/PS2コントローラー__ をつなぐ基板を作りました。
この基板は、__PS/PS2コントローラー__ 以外のピンが使用できるようになっているので、スイッチやペダルなどの追加も可能です。
![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285344/3cc7fca8-166f-a133-27d1-8cccd41029a3.jpeg)
# ケースの製作
3Dプリンタでケースを作成しました。
基板を取り付けるためにインサートナットをはんだゴテで挿入しておきます。
![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285344/6768b8a4-1b30-7b62-5afc-b0d0343077da.jpeg)
# 組立
__ArduinoNano__ と蓋を被せて完成です。
![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285344/48db8ac9-9b11-c2e9-7fe4-4527da200494.jpeg)
# 参考・引用
### 7m4monさん 電車でGO!コントローラでTSマスコンをエミュレートしてBVEで遊ぶ。
https://github.com/7m4mon/dengo_ts_emu
webview_flutterのWebViewでタップイベントを取得する

# はじめに
Flutter公式のWebViewライブラリである[webview_flutter](https://pub.dev/packages/webview_flutter)はiOS/AndroidネイティブのWebViewと比べると機能が足りず、痒い所に手が届かないことが多くあります。
この記事では、webview_flutterを使用してい、WebView内のタップイベントを取得する方法を共有します。

`WebViewWidget`や`WebViewController`のメソッドに用意されていないイベントリスナーを登録するためには、ネイティブのWebViewにイベントリスナーを登録して、MethodChannel経由でデータを渡す必要があります。
`Android`のWebViewを拡張して**WebViewにイベントリスナーを登録するところまで**を扱います。MethodChannelの使い方については、　公式リファレンスや他記事をご参照ください。

# 環境
```
Flutter 3.3.9
Android Studio Dolphin | 2021.3.1
```
pubspec.yaml
```yaml
webview_flutter: ^4.0.1
```

# 目次
-  [WebViewHostApiImplをオーバーライドして、タップイベント等を取得できるようにする](#WebViewHostApiImplをオーバーライドして、タップイベント等を取得できるようにする)


# WebViewHostApiImplをオーバーライドして、タップイベント等を取得できるようにする
`GeneratedAndroidWebView.WebViewHostApi.setup()`で、カスタムした`WebViewHostApiImpl`をsetすることで、ネイティブのWebViewのイベントを取得できるようになります。
コレを応用することで、ネイティブのWebViewの機能を使うことができるようになります。
## コード例
```kotlin
import android.webkit.WebView
import io.flutter.Log
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugins.webviewflutter.*

class MainActivity : FlutterActivity() {
    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)
        val webViewPlugin =
            flutterEngine.plugins[WebViewFlutterPlugin::class.java] as WebViewFlutterPlugin?
        val instanceManager = webViewPlugin?.instanceManager ?: return

        val apiImpl: WebViewHostApiImpl = object : WebViewHostApiImpl(
            instanceManager,
            flutterEngine.dartExecutor.binaryMessenger,
            WebViewProxy(),
            this,
        ) {
            override fun create(instanceId: Long) {
                super.create(instanceId)
                val webView = instanceManager.getInstance(instanceId) as WebView?
                webView?.apply {
                    settings.apply {
                        // 取得したいイベントのリスナーを追加する
                        setOnLongClickListener {
                            Log.d("webView", "onLongClick")
                            return@setOnLongClickListener false
                        }
                    }
                }
            }
        }

        GeneratedAndroidWebView.WebViewHostApi.setup(
            flutterEngine.dartExecutor,
            apiImpl
        )
    }
}

```


# 参考URL
https://github.com/googleads/googleads-mobile-flutter/issues/618#issuecomment-1295306073

# おわりに
今回は、`GeneratedAndroidWebView.WebViewHostApi.setup（）`を使用して、WebViewのタップイベント等を取得する方法を紹介しました。
他にも、`WebChromeClientHostApi.setup()` を同じように使用することで、カスタムした`WebViewClient`を使うことができるようになります。
こちらは、後ほど記事化しようと思います。
ビジネスメールを代筆してくれるChatGPTプロンプト

# Poeがなかなか良い。

https://poe.com/

ChatGPTを組み込んだChatBotを簡単に作れるPoeにハマっている。
ぼくみたいな非エンジニアはこれがとても便利だと思う。

# ここが便利だよ
1. かわいいUIでチャットができる
    1. かわいいUIで使えるのでプレイグラウンドよりも楽しい。
1. プロンプトを組み込んだBotが作れる
    1. 良いプロンプトができたら、それを事前に読み込んだBotを作って保管できる。
    1. プレイグラウンドだとスレッドが乱立してわけ変わらなくなるのを防げます。
1. 人に配布できる
    1. 良いBotができたら人に配布できます。
    1. これが非エンジニアにとって便利です。

# ビジネスメールを書いてくれるBot
ビジネスメールをかいてくれるBotを作りました。

https://poe.com/BizMailer

どうぞお役立てください。
なんと書いてあるでしょう？

下のQRコードの中に隠されているメッセージを解読して下さい。
4/27 現在、私は仕事中もこれのことばかり考えています。

![QR_522113.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2960096/1f1a5527-3e00-5bac-d4c5-3d3f7eb45ceb.png)

CTF 的な問題ですが難易度は低そう。
ジャンルは misc ですかね。

ヒントはありません。
他の私の投稿をみても無駄です。
ロジカルシンキング入門

# ロジカルシンキングとは？
物事を考えるための手段や道具を適切に用い、論理的に思考すること。

# なぜロジカルシンキングが必要か？
世の中に転がっている問題を解決するために最短で最適な解決策を発見できたら最高に効率的ではないでしょうか？きっとその答えは Yes だと思います。

# ロジカルシンキングを取り上げる理由は？
社会人として働く身としては、一人でも多くの人が効率的に思考し、最大の成果をあげて欲しいと考えたため。
かつて、PMBOK のようなプロジェクトマネジメントで利用する各種フレームワークだったり、開発で活用する UML のようなフレームワークだったり、それを知っているだけで使っているだけ、というロボット状態に陥っている人を見てきた。きっとその人がロジカルシンキングできていたなら、最適なアイテムを選択し、最適なプロジェクトマネジメントができていたのでは？と思いを馳せている。

# どうすれば身につくか？
個人差はあるが、適切なフレームワークを用いてトレーニングを繰り返すことで身に付くと信じている。

# そのフレームワークにはどのようなものがあるか？
知っている範囲では、5W思考、目的思考（指向？）、といったものがある。

## 5W思考とは？
What/Who/When/Which/Where を用いて思考すること。

## 目的思考とは？
要求事項（Requirement）や正当性（Justification）を元に、目的（Purpose）を達成するための手段を思考すること。大まかなイメージとしては 5W 思考に Why を追加して考える。

# どんなシーンで使われるのか？
## 5W 思考
例えば、UML のユースケース図やビジネスフロー図では人や企業間等のワークフローを扱い、シーケンス図ではシステムやクラスのプロセスフローを扱い、データフロー図ではその名の通りデータフローを扱う。
これらを利用する意味を理解するためには 5W 思考が非常にしっくりとくるので、そのフレームワークの例をまとめようと思う。

### ワークフロー
ユースケース図では、
* 誰が(Who)/何をするか？(What Do)
* 誰が(Who)/何を使って(What Use)/何をするか？(What Do)

のようなことを記述する。

例として、
* 社員全員が/勤怠システムを使って/就業時間を登録する
* 社員全員が/勤怠システムを使って/年休を登録する

といった作業の流れを絵や吹き出しで表現することになる。
実際のプロジェクトでは、この内容を実現するためにはどのような勤怠システムを構築する必要があるか？ということをさらに深掘りしたり、実際にどのようなプロセスフローを組みこみべきか？どのようなデータフローにすべきか？といったことを考えることになるだろう。

### プロセスフロー
シーケンス図では、
* どの処理が(Which Process)/何をするか？(What Do)
* どの処理が(Which Process)/どの処理に(To Which Process)/何を送信するか？(What Send)
* どの処理が(Which Process)/どの処理に(To Which Process)/何を受信するか？(What Recieve)

のようなことを記述する。


例として、
* 就業時間登録処理が/就業時間用データベース登録処理に/就業開始時間を送信する
* 就業時間用データベース登録処理が/就業開始時間をデータベースに登録する

といった処理の流れを絵で表現することになる。

### データフロー
データフロー図では、
* 何が起きた時に(What Happen)/どのデータが(Which data)/どこに送信されるか？(To Where Send)
* 誰が(Who)/何をしたときに(What Action)/どのデータが(Which Data)/どこに登録されるか？(To Where Be Stored)
* どの操作をしたときに(What Actiohn)/どの画面に(Which Page)/どのデータを表示するか？(Which Data Be Shown)

のようなことを記述する。

例として、
* 社員が/就業開始時間を送信したときに/就業開始時間が/データベースに登録される

といった処理の流れを絵で表現することになる。

# 目的思考
こちらは PMBOK のようなプロジェクトマネジメント側で効果を発揮すると考えられる。

例えば、カスタマーから「ユニバーサルデザインを意識した Web サイトに刷新して欲しい」というような要求を受けたとする。しかし、これを実現するためには何かが足りない。何かがモヤッとする。そう、目的が無い。このままでは、誰をターゲットにするのかはっきりしないため、中途半端なものが完成しそうである。そこで重要な役割を果たす要素が Why である。
なぜ、ユニバーサルデザインを意識した Web サイトに刷新したいのか？とそのまま問いかけるだけでも効果はある。さらに何をターゲットにするのか？という問いかけを追加すれば、要求事項が拡充され、真の目的に到達するだろう。
その考えを元にあなたはカスタマーに熱心にヒアリングし、「老若男女や障がい者、パソコンスキルの高低関係なく直感的に使いやすいものにしたい」という明確な目的をようやく導き出せた。この成果によって、さらに問題をブレイクダウンすることができ、視力問題、色覚問題、スキル問題、といった解決すべき課題を抽出できるようになった。
きっとこの後、この目的や要求、課題をどのレベルまで解決し実現するかをカスタマーと話し合い、プロジェクトスコープを作成、そして開発に着手していくことになるだろう。

# 練習問題
## その１
あなたはカレーを作るように依頼を受けた。5W 思考を参考に、ワークフロー、プロセスフロー、データフローの観点からカレーを完成させる道筋を考えて欲しい。
なお、ワークフロー = 人の動き、プロセスフロー = 人の作業、データフロー = 人や材料や道具の流れ、として考えてみよう。
※より複雑にしたい場合は設定を大人数の料理を大人数で調理するように変更してみよう。

## その２
学校の勉強が社会に出て役に立つのか？そんなことを考えたり、人から言われたことはないだろうか？ちょうど良いので、目的思考を用いて説明してみて欲しい。（要求事項は社会生活上で必要とされたり求められたりすることとして考えてみよう。）

## その３
ロジカルシンキングのトレーニングの一環として[ここ](https://qiita.com/WaterIsland/items/0214adb38cdcda4bdfe8)の課題３について考えてみよう。

## その４
なんでも良いので、今まで知った理論や公式、定義等について、それを使うと便利なことやわかることを考えてみよう。
【VS Code】Amazon CodeWhispererでLambda関数をコーディングしてみる

# Amazon CodeWhispererとは
AWSのコーディング支援サービスです。
コメントにやりたいことを書くだけで、それを実現するコードを自動生成します。

機械学習の仕組みが裏で動いているようです。
以下は公式サイトの一部抜粋です。
> CodeWhisperer は数十億行のコードでトレーニングされており、コメントや既存のコードに基づいて、スニペットから完全な関数まで、さまざまなコードの提案をリアルタイムで生成できます。

というわけで実際に触ってみました。

# やること
CodeWhispererを使ってLambda関数を作成します。
Lambda関数の内容は以下です。
　S3にバケットを作成

# 環境情報
OS：Windows 10
IDE：VS Code
言語：Python

# 事前準備
以下記事を参考にCode Whispererの実行に必要な準備をします。
https://qiita.com/masakinihirota/items/52d3fad3aea8300d391f

# Code WhispererでLambda関数をコーディング
ここから実際にコードを書いていきます。
コードを書く前に、VS Code上でCodeWhispererが実行状態であることを確認します。
VS CodeのAWSアイコンをクリックし、CodeWhispererが動いていることを確認します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2485934/0b20a2d6-d02f-e6f6-3e2d-fc02256a324c.png)

VS Codeで適当なpythonファイルを開きます。
ファイルには何も書いてない状態です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2485934/ac842e97-2421-17b9-b7c9-91393f260e55.png)

実装内容をコメントに記載します。
今回はS3のバケットを作成したいので、「Create Lambda Function creating S3 create Bucket」と書いて、
Alt+Cを押します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2485934/6300c75e-9af4-5e3d-938f-b7c0e9a50f7a.png)



以下のようにサジェスト（import boto3）が出てきました。
Tabキーを押すとサジェストが反映されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2485934/20db3f52-b0c4-da3e-c3af-ceeece9e003c.png)

サジェストが反映されました。
この状態で改行してみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2485934/bc84e81d-d534-f830-f360-9bcf50c4a3c1.png)

すると続きのサジェストが表示されました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2485934/3d9f8cf6-e140-5f2b-663f-c65180b3e296.png)

改行を続けてサジェストを受け入れ続けると、以下のようなコードが出来ました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2485934/6fdcd075-fb37-3abc-e253-e368f120e2c0.png)

バケット名を、他バケットと被らない名前にします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2485934/a62b122d-a682-de5c-4422-695132b42548.png)

リージョンを指定する必要があるので、下図赤枠部分を追記します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2485934/bf8dc988-fc51-aaec-8967-687d0d6e51ce.png)



このコードをLambdaにデプロイしてテストします。
※LambdaのIAMロールには、S3のフル操作権限を付与しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2485934/17de6310-013f-5b74-9c31-18ad187d9fdf.png)




処理が正常に完了しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2485934/a6799663-671f-7989-c687-9e3ed13cedcc.png)

S3にアクセスするとバケットが作成されています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2485934/1cc83aa0-b4fc-d1e1-732c-14413c6e70a6.png)


# 所感
コードをある程度書いてくれるので、自分で調べてコードを書く手間が省けるのはよいなと思いました。

一方で、コメントの書き方次第では、今回のように一部追記をする必要があります。
今回のケースではバケット作成先リージョンを追記しましたが、リージョンをコメントで明示すれば、そこを考慮したコードをCodeWhispererが書いてくれます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2485934/ad2658f9-9039-d3e1-0d84-a5eeeacbb50b.png)

あとほんとに動くかどうか検証する必要もあります（当然ですよね）。

# 参考資料
https://qiita.com/masakinihirota/items/52d3fad3aea8300d391f
モジュールの理解

    

 モジュールとは
        
モジュールは、プログラムを構成するパーツの一つで、部品箱の中のパーツのようなものです。この部品箱には、数学計算をするためのモジュールや、日付や時間を扱うためのモジュールなど、様々な種類のパーツがあります。プログラムを作る時、この部品箱から必要なパーツを選んで取り出し、組み合わせてプログラムを作ることができます。
例えば、数学の問題を解くプログラムを作るときには、数学計算をするためのモジュールを使います。

モジュールは、プログラムを構成するための独立した要素の1つであり、他のプログラムからインポートして使用することができます。また、Pythonにおいては、標準ライブラリに含まれるモジュールや、サードパーティ製のモジュールを利用することができます。
        
これをしないと変数名や関数名の名前が別ファイルで被ってバグる。名前空間の問題が発生。1人で開発する場合だと、管理できるかもしれないが、大人数で開発すると名前が被る管理をできない

モジュールはコードを分割することによって影響範囲を狭めるという利点がありますが、その他にも再利用性や保守性の向上があるといった利点もある。
Git,GitHubでタグを付けてリリースポイント毎にバージョンを管理する

# 今回使う機能
解説する機能はtagです。
tagを付けることで簡単にリポジトリのバージョンを変更することができます。
そうすることで簡単に振り返りができるようになります。
基本的なGitとGitHubの機能を使うことができる前提で話を進めます。
add, commit, push, branch, status等が分からない場合はそれらを先に勉強することを推奨します。
[もう怖くないGit!](https://www.udemy.com/course/unscared_git/)　Udemyの教材です。体系的にGitとGitHubについて学ぶことができます。
# 具体的に行うこと
- コミットにタグを付ける
- GitHubにプッシュする
- GitHubでバージョンを切り替えてみる
# コマンドとその解説
最新のコミットにタグを付ける
```
git tag <タグ名>
```  

注釈付きで最新のコミットにタグを付ける
注釈にこのバージョンの説明を書いたりできます。
```
git tag -a <タグ名> -m <注釈>
```
タグの一覧を表示する
```
git tag
```
タグをGitHubに送信する
```
git push origin <タグ名>
```
タグを削除する
```
git tag -d <タグ名>
```
# バージョンの切り替え方
ブランチを切り替えるところでtagsを選択すると好きなバージョンが選択できます。
![スクリーンショット 2023-04-27 0.11.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3340022/550b2a26-d2f0-797c-6951-96162f4cb5b1.png)

# 参考
[もう怖くないGit!](https://www.udemy.com/course/unscared_git/)
コマンドプロンプトのログ出力

# コマンドをリダイレクトで実行した場合
コマンドプロンプトでコマンドをリダイレクトした場合、結果しかリダイレクトされないのでどんなオプションでコマンドを実行したか分からない。
```:コマンド
nslookup www.yahoo.co.jp 8.8.8.8 > before.log 2>&1
```
```:before.log　※実行したコマンドが表示されない
権限のない回答:

サーバー:  dns.google
Address:  8.8.8.8

名前:    edge12.g.yimg.jp
Address:  183.79.217.124
Aliases:  www.yahoo.co.jp
```
# バッチファイルをリダイレクトで実行した場合
バッチファイルにしてバッチ自身をリダイレクトすればログファイルにコマンドも表示される。
```:test.bat
nslookup www.yahoo.co.jp 8.8.8.8
nslookup -type=ns yahoo.co.jp 8.8.8.8
```
```:コマンド　※バッチファイルをリダイレクトで実行
test.bat > after.log 2>&1
```
```:after.log　※ログファイルにコマンドも表示される。
M:\temp>nslookup www.yahoo.co.jp 8.8.8.8 
権限のない回答:

サーバー:  dns.google
Address:  8.8.8.8

名前:    edge12.g.yimg.jp
Address:  183.79.250.123
Aliases:  www.yahoo.co.jp


M:\temp>nslookup -type=ns yahoo.co.jp 8.8.8.8 
権限のない回答:

サーバー:  dns.google
Address:  8.8.8.8

yahoo.co.jp	nameserver = ns01.yahoo.co.jp
yahoo.co.jp	nameserver = ns11.yahoo.co.jp
yahoo.co.jp	nameserver = ns02.yahoo.co.jp
yahoo.co.jp	nameserver = ns12.yahoo.co.jp
```
g-trendsというGoogleトレンドのパッケージを使って、キーワードの出力。


# はじめに

g-trendsというGoogleトレンドのパッケージを使って、キーワードの出力してみました。

# 最終更新日
2023年4月26日

# 環境の情報
OS
Ubuntu2004

# 注意
最新の状態を確認して下さい。

# 参考さまはこちら
https://www.npmjs.com/package/g-trends

# g-trends

g-trendsというGoogleトレンドのパッケージを使って、キーワードの出力してみました。


# 環境を作っていきます

### nodeを確認します

```
$ node -v
v14.21.3
```

```
$ npm -v
9.6.0
```

バージョンが出てくれば問題ありません。


### インストールします

```
$ npm install g-trends
```


### 作成します。

jsファイルを作りましょう。

```main.js
const { ExploreTrendRequest } = require('g-trends')
​
const explorer = new ExploreTrendRequest()
​
explorer
.past30Days()
.addKeyword('なんかキーワード')
        .download().then( csv => {
            console.log('OK!!!!')
            console.log(csv)
        }).catch( error => {
            console.log('エラー',error)
        })
 ```



### 実行します。

```
$ node main.js
```

以下のようになれば問題ありません🎊
![E2CA31F8-B603-458A-AFDC-68C61509A511.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148394/2f5ac5d5-32d6-6859-65a4-bac84193472a.png)
Cronジョブの使い方

今回初めてCronジョブに挑戦しました。備忘録です。

<h1> Cronジョブとは
<p>Cron（クーロン）ジョブとは、設定された時間になるとコンピュータが自動でスクリプトを走らせ仕事をしてくれるものです。Windowsのタクスケジューラーに当たります。例として、定期的に行うデータのバックアップ、データのアップロード／ダウンロードなどを、利用が少ない夜間のタイミングにスケジューリングしておくことができる。


<h1>ジョブをスケジュールする
<h3>Crontabでスケジューリング
<p>ターミナルで次のコマンドを実行し、クーロンタブを開く。「-e」はedit（編集）の意。

```
crontab -e
```
クーロンタブには下記の要領で入力。
```
*     *     *     *     *  （コマンド／スクリプト）
-     -     -     -     -
|     |     |     |     |
|     |     |     |     +----- 曜日を0から6で入力（日曜日（=0）から始まる）
|     |     |     +------- 月を1 から 12で入力
|     |     +--------- 日にちを1 から 31で入力
|     +----------- 時間を 0から23で入力
+------------- 分 を0 から 59で入力

```

:::note info
* クーロンタブが編集できない時は、aキー（append）またはiキー（insert）を押す。
* 編集後は、Escキー、コロン（；）キー、wキー（Writeの意）、qキー（Quitの意）を押すとクーロンタブを抜けられる。
:::

アスタリスク5つ及び実行したいコマンド／スクリプトだ。この5つのアスタリスク及びコマンド／スクリプトの間にはそれぞれに半角スペースを入れる。
アスタリスクは全てを表す。例えば、毎週日曜日、午前0時10分にジョブを走らせたい場合は、下記のようになる。
```
10 0 * * 0 （コマンド／スクリプト）
```
スケジューリングはもっと細かく設定できるので、必要に応じて調べればOK。

<h3>簡単なテストをしてみる
<p>いきなりスクリプトを走らせる前に、echoでメッセージを表示させてみる。
下記は、23時55分に"Hello"と表示させるスクリプト。

```
55 23 * * * echo "Hello" 
```

:::note info
* クーロンタブを抜けると、ターミナルからアドミンなんとかのメッセージが出るので、「OK」で進める。
:::
:::note warn
* スクリプトを保存したら、ターミナルに "crontab: installing new crontab"（クーロンタブ：新しいクーロンタブをインストールしています）のメッセージが出ていることを確認。出ていない場合は、権限に問題がある。
私は[こちらの記事](https://atoriekohaku.com/2021/01/24/%E3%80%90%E8%A7%A3%E6%B1%BA%E3%80%91macos-catalina-crontab%E3%81%A7sh%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%8C%E5%AE%9F%E8%A1%8C%E3%81%A7%E3%81%8D%E3%81%AA%E3%81%84/)を参考に権限設定した。
:::

ジョブ実行時刻である23時55分を過ぎたところで、ターミナルを起動すると、"You have mail"のメッセージが。メールを確認する。
```terminal
mail
```
23時55分に実行されたメールを開くと、「Hello」のメッセージがプリントされている。

<h3> Python ファイルをクーロンジョブで実行する</h3>
上記では、クーロンジョブで実行するコマンドを直接入力したが、今度は、.pyファイルを実行する。
クーロンジョブでPythonコードを走らせるには、.pyファイルのトップに次の1行を追加する必要がある。これにより「このファイルはPythonで実行してね」と実行プログラムを指定する。

```code.py
#!/usr/bin/env python
```
また、クーロンタブでジョブを設定する際に、Pythonのパスを渡す必要がある。
今回、私はpyenv環境を使ったため、pyenvコマンドにてPythonパスを確認する。
```
pyenv which python
```
ここで確認したPythonのパスをクーロンタブで使用する。
```
* * * * * （Pythonパス） （実行するスクリプトのパス）
```
下記は実行例
```
23 55 * * * /usr/bin/python3 /path/to/myscript.py
```
私は今回、.pyファイルの中で、ファイルをエクスポートするコマンドを記載しておいたのだが、このエクスポート先も絶対パスで記載する。そうしないと、HOMEディレクトリにファイルが出来てしまった。
また、.pyファイル内に、printを入れておけば、コードがどこまで進んだかを、先述したしたmailから確認できる。あるいは、更新時刻から、コードの実行状況を確認出来る。


<h1> 最後に</h1>
今回初めてクーロンジョブを使った。頻繁にデータ収集、データ更新するデータベースの管理などがラクになりそうだ。次のプロジェクトでデータベースとともに使ってみたい。
ボードゲームでよくある六角形の戦場マップをFlutterで実装する

![スクリーンショット 2023-04-26 23.35.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/478047/20513fba-bc8c-4881-0e3a-e21bdc335769.png)

以前、Flutterでボードゲームと対戦用AIを実装した体験を紹介しました。

https://qiita.com/MilosNaniwa/items/63845d79bf11f4b656dc

https://github.com/MilosNaniwa/warchest-dojo

本記事では、Flutterでボードゲームにおける六角形の戦場マップを実装する方法を解説します。以下に示すソースコードをベースに説明を進めます。

他にも ChatGPT の API を使って AI と話しながらゲームができる機能や、アルファベータ法を Dart で実装して Firebase Cloud Functions で探索処理を動かすようにしたりと色々やっていたのですが、気が向いたら順次記事にしていこうかと思っています。

ぶっちゃけChatGPTに書かせれば一発で出てきそうですが、一昨年の年末年始に頑張って書いたものを供養する意味でコード解説を書きたいと思います。解説にはもちろんChatGPTを使っています。


```dart
(import 文略)

class HexagonTile extends StatelessWidget {
  (プロパティ略)
  const HexagonTile({
    (パラメータ略)
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    (build処理略)
  }
}

class _HexagonClipper extends CustomClipper<Path> {
  (プロパティ略)
  _HexagonClipper({
    (パラメータ略)
  });

  @override
  Path getClip(Size size) {
    (getClip処理略)
  }

  @override
  bool shouldReclip(covariant CustomClipper<Path> oldClipper) => false;
}

class _HexagonPainter extends CustomPainter {
  (プロパティ略)
  _HexagonPainter({
    (パラメータ略)
  });

  @override
  void paint(Canvas canvas, Size size) {
    (paint処理略)
  }

  (描画メソッド略)

  @override
  bool shouldRepaint(CustomPainter oldDelegate) => false;
}

Path createHexagonPath({
  required double radius,
  required Offset center,
}) {
  (createHexagonPath処理略)
}

class PaintComputer {
  (定数定義略)

  double computeRadius({
    required double screenWidth,
    required double screenHeight,
  }) {
    (computeRadius処理略)
  }

  (関数略)
}
```

* 実際に私が書いたものは以下のコードを参考にしてください。

[六角形のタイル](https://github.com/MilosNaniwa/warchest-dojo/blob/main/lib/widget/hexagon_tile.dart)
[タイルを組み合わせたボード](https://github.com/MilosNaniwa/warchest-dojo/blob/main/lib/widget/hexagon_grid.dart)

## HexagonTile クラス

HexagonTile クラスは、六角形のマップタイルを表現する StatelessWidget です。以下に示すプロパティを持ちます。

- tileName: タイルの名前
- coordinateX, coordinateY: タイルの座標
- centerOffset: タイルの中心位置
- radius: タイルの半径
- onTap, onHover: タイルのタップやホバー時のイベント
- isFocused, isHovered: タイルのフォーカスやホバー状態
- deployedUnitClass: 配置されているユニットの種類
- 他、タイルの状態に関するプロパティ

build メソッドでは、Stack ウィジェットを用いて六角形のタイルとその上に配置されるユニットを描画しています。

## _HexagonClipper クラス

_HexagonClipper クラスは、CustomClipper<Path> を継承し、getClip メソッドで六角形の Path を作成します。

## _HexagonPainter クラス

`_HexagonPainter` クラスは、CustomPainter を継承し、`paint` メソッドで六角形のタイルを描画します。タイルの状態によって、フォーカス状態、ホバー状態、移動可能状態、攻撃可能状態、配置可能状態、指示可能状態などの色を付与します。

また、_drawTileId メソッドを用いてタイルの名前を描画します。

## createHexagonPath 関数

createHexagonPath 関数は、六角形の Path を生成します。引数として、半径 radius と中心位置 center を受け取ります。この関数では、六角形の各頂点を計算し、それらをつなぐ Path を作成して返します。

## PaintComputer クラス

PaintComputer クラスは、六角形のタイルの半径や高さを計算するためのクラスです。computeRadius メソッドでは、画面の幅と高さに応じて適切なタイルの半径を計算します。また、computeHeight メソッドでは、半径からタイルの高さを計算します。

これらのクラスと関数を組み合わせることで、六角形の戦場マップを実装することができます。HexagonTile クラスを利用して、任意の座標に六角形のタイルを配置し、マップ上でのユニットの操作や状態の更新を行うことが可能です。

コード全文

https://github.com/MilosNaniwa/warchest-dojo
vagrantコマンドを実行したら、「"vagrant"を開くとコンピュータが破損します。」というアラートウインドウが発生

vagrantコマンドを実行したら、急に「"vagrant"を開くとコンピュータが破損します。」というアラートウインドウが出て、vagrantコマンドが一切使えなくなったので、その時のエラーの処方法についてです。
（vagrant up,vagrant statusとか全部ダメでアラートウインドウが出る）

#### 環境
macOS Big Sur、バージョン11.7.6

### やった事
ネットで調べたら、以下のページを発見。

・[mac（Big Sur）で、vagrant起動時に『"vagrant"を開くとコンピュータが破損します。』と表示され起動できなくなりました。対処法を教えてもらえませんか？](https://teratail.com/questions/hf5vzec3ws22wy)

基本的にこのページを参考にして解決しました。

#### Vagrantをインストールし直す

まずは[Vagrant](https://developer.hashicorp.com/vagrant/downloads)のサイトで、Vagrantのインストーラをダウンロードします。

自分の場合はmacOSなのでBinary download for macOSのAMD64 Version2.3.4を選択してダウンロードしました。

vagrant_2.3.4_darwin_amd64.dmgがダウンロードしたら実行する。Vagrantのインストーラが起動するので実行してVagrantをインストールする。

インストールできたら、何かvagrantコマンドを実行してみます。

試しにvagrant statusコマンドを実行したら、問題のアラートウインドウは出なくなったけど、以下のエラーメッセージが出て実行できない、、

```
$ vagrant status

Vagrant failed to initialize at a very early stage:



The plugins failed to initialize correctly. This may be due to manual

modifications made within the Vagrant home directory. Vagrant can

attempt to automatically correct this issue by running:



vagrant plugin repair



If Vagrant was recently updated, this error may be due to incompatible

versions of dependencies. To fix this problem please remove and re-install

all plugins. Vagrant can attempt to do this automatically by running:



vagrant plugin expunge --reinstall



Or you may want to try updating the installed plugins to their latest

versions:



vagrant plugin update



Error message given during initialization: Unable to resolve dependency: user requested 'sahara (= 0.0.17)'
```

ここで、vagrant plugin updateコマンドを実行してみる。

```
$ vagrant plugin update

Updating installed plugins...

Fetching open4-1.3.4.gem

Fetching Platform-0.4.2.gem

Fetching popen4-0.1.2.gem

Fetching sahara-0.0.17.gem

All plugins are up to date.
```

再度vagrant statusコマンドを実行したら、問題なく実行できるようになりました！
他のvagrant up,vagrant ssh,vagrant haltなども実行できたし、インストールしていたvagrantのプラグインも残っていて問題なく使えたので、大丈夫そう。よかったよかった。
AWS組織アカウント下でHands-On用の子アカウントを大量に作る

社内でAWSトレーニングやるから、AWS組織アカウント下で参加者が使える
子アカウントを大量に作れるようにしとけ、というお達しをいただきましたので
備忘録ついでに記事にしました。
以下手順を淡々と..
## 親(Payer)アカウントにて下ごしらえ

① Payerアカウントにスクリプト実行用のIAMuserアカウント/アクセスキーを作成します。コンソールは使わないので利用権限OFFでOK。

:::note info
PayerアカウントのEC2とかでスクリプト実行するならアクセスキーなんぞ使わずRoleアタッチとかでOKですし
Cloudshell で後述のスクリプトを実行でももちろんOK
:::

② ①で作成したアクセスキーをスクリプト実行PCに新規プロファイル設定
```
$ cat ~/.aws/config
[profile demoaccount]
region = ap-northeast-1

$ cat ~/.aws/credentials
[demoaccount]
aws_access_key_id = AKIxxxxxxxxxxxxJO2
aws_secret_access_key = jOoxxxxxxxxxxxxxxxxxxxxxt4xBU0
```
## 子アカウント作成してみる

③スクリプト作成

アカウント作成用スクリプトはこちら。お好みでbash でも pythonでも修正してどうぞ。
```
$ cat CreateDemoAccount.sh
#!/bin/csh
set PROFILE=demoaccount
set NUMBER=001
set REGION=ap-northeast-1
set EMAIL_ADDRESS=aws_demo+${NUMBER}@xxx.co.jp
set ACCOUNT_NAME="aws_demo_${NUMBER}"
set ROLE_NAME="DemoAccountRole"
 organizations create-account \
   --profile ${PROFILE}\
   --region ${REGION}\
   --email ${EMAIL_ADDRESS} \
   --account-name ${ACCOUNT_NAME} \
   --role-name ${ROLE_NAME}
```
メールアドレスは実際使わないのでダミーでもいいですけど、本作業用のものを用意するのをお薦めします。


④ 実行

実行すると、IN_PROGRESS ステータスで新規アカウントが発行されているのが確認できます。

```
$ ./CreateDemoAccount.sh
{
   "CreateAccountStatus": {
       "Id": "car-3d6431xxxxxxxxxxxxxxd66e24cb",
       "AccountName": "aws_demo_001",
       "State": "IN_PROGRESS",
       "RequestedTimestamp": "2023-04-26T16:09:09.781000+09:00"
   }
}
```
出来上がり確認。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223740/fddee0b7-b86e-56ae-ca43-c2a447a14dec.png)


たくさん作る時には③のスクリプトで $NUMBER を通番ループで回してやればOK

## 親アカウントにSwitchRole用のIAMuser(参加者用ID)を作ろう

作成したアカウントにはIAMuserはいないのでSwitchRoleするためのIAMuserを
親アカウント側に用意しなければなりません。

⑤IAMグループHandsOnUsersを作成します
さらにInlineRule にてAssumeRole をアタッチします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223740/149e0115-f04c-767b-8491-e6e5af11f085.png)

⑥ IAMuser demouser01を作成, グループ HandsOnUsers に追加
※これも大量に作る際はスクリプトで行えますがここでは説明を割愛

⑦　demouser01 で改めてAWSコンソールにログインし直し、④で確認したAWS account 番号でSwitchRole 実行
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223740/06d5ae92-3ff3-1b53-b833-0759f2f66812.png)

⑧ SwitchRole成功を確認
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223740/9c6e257b-118f-aa4a-e49c-f64575972264.png)
これで参加者にHands-on用Sandboxを提供できます！

## 補足など

アカウント削除をCLIでやるなら以下の通り
```
% aws organizations close-account --profile demoaccount --account-id <対象アカウント>
```
実行すると何も応答無しで終了しますが、コンソール上で見ると
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223740/7241506d-b225-43c1-4818-5c38a8bd1dc5.png)
うーん怖いw

誤操作の可能性を考えるとかなり怖いので、都度アカウント閉じるなら
可能な限り本番利用とは別にトレーニング用Payerアカウントを用意して運用すべきでしょう。

ただ、コマンドラインヘルプ見ると気になる記載がある。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223740/a09c0418-4b48-fd1b-34eb-6daa118bdad1.png)
毎月2-30アカウント作ってHands-on後にごっそり削除ってのは通常ではできないのかもしれない。
こっちはサポートに相談かな..

追記:AWSサポートに確認したところ最低10アカウント/月までは削除できるそうです
【シェルスクリプト】$*と$@について

## `$*`と`$@`の違い

クォートしない場合には`$*`と`$@`は同じ挙動になります。  

```test.sh
outputarg() {
  echo "引数の数は $# 個です。"
  echo "第一引数は $1 です。"
  echo "第二引数は ${2:-なし} です。"
}

echo 'クォートなし'
echo '$@'
outputarg $@
echo '--------------------------'
echo '$*'
outputarg $*

```

```terminal
$ bash test.sh 'arg1 arg2' 'arg3 arg4'
$@
引数の数は 4 個です。
第一引数は arg1 です。
第二引数は arg2 です。
--------------------------
$*
引数の数は 4 個です。
第一引数は arg1 です。
第二引数は arg2 です。
```

## `"$*"`と`"$@"`の違い

クォートした場合には`"$*"`は引数が結合され、`"$@"`は引数が渡したとおりに分割されます。

```test.sh
outputarg() {
  echo "引数の数は $# 個です。"
  echo "第一引数は $1 です。"
  echo "第二引数は ${2:-なし} です。"
}

echo '"$@"'
outputarg "$@"
echo '--------------------------'
echo '"$*"'
outputarg "$*"

```

```terminal
$ bash test.sh 'arg1 arg2' 'arg3 arg4'
"$@"
引数の数は 2 個です。
第一引数は arg1 arg2 です。
第二引数は arg3 arg4 です。
--------------------------
"$*"
引数の数は 1 個です。
第一引数は arg1 arg2 arg3 arg4 です。
第二引数は なし です。

```
AWSで開発環境構築 #2（プライベートサブネットの構築）

# 目的

https://qiita.com/kanerin1004/items/1b0749163036558af78f

これを参照
続きをしていきます！


# 今回やること

今回の目標は以下の3つ。
* プライベートサブネットを作成する
* プライベートサブネットにEC2インスタンスを作成する
* EC2インスタンスからSSH接続する

ここまでやって、こんな感じの構成で環境構築します！
![private_subnet.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/693325/01d90908-14ad-4657-f3ef-377ccddb058e.png)


# 環境
* windows 10


# いざ出陣

## サブネットの作成

1. VPCダッシュボードの左のメニューから、「サブネット」を選択します。
1. 「サブネットを作成」をクリックします。
1. 以下の項目を設定します。

<dl>
  <dt>VPC</dt>
  <dd>前回作成した「test-vpc-01」を選択</dd>
  <dt>サブネット名</dt>
  <dd>test-subnet01</dd>
  <dt>アベイラビリティーゾーン（AZ）</dt>
  <dd>「アジアパシフィック (東京) / ap-northeast-1a」を選択（プライベートサブネットと同じ）</dd>
  <dt>IPv4 CIDR ブロック</dt>
  <dd>10.0.2.0/24</dd>
</dl>

他は、デフォルトでサブネットを作成します。

## EC2インスタンスの作成

1. AWSコンソールにログインし、EC2を選択します。
1. EC2のダッシュボードで、「インスタンスを起動」をクリックします。
1. 以下の項目を設定します。

<dl>
  <dt>Name and tags</dt>
  <dd>test-private-server</dd>
  <dt>アプリケーションおよび OS イメージ</dt>
  <dd>「Amazon Linux 2 AMI (HVM) - Kernel 5.10, SSD Volume Type」を選択（無料枠対象）</dd>
  <dt>インスタンスタイプ</dt>
  <dd>「t2.micro」にします（無料枠対象）</dd>
  <dt>キーペア (ログイン)</dt>
  <dd>前回作成した「testkey01」を選択</dd>
  <dt>VPC - 必須</dt>
  <dd>上で作成した「test-vpc-01」を選択</dd>
  <dt>サブネット</dt>
  <dd>上で作成した「test-subnet01」を選択</dd>
  <dt>パブリック IP の自動割り当て</dt>
  <dd>「無効化」を選択（プライベートサブネットのため）</dd>
  <dt>ファイアウォール (セキュリティグループ) </dt>
  <dd>「セキュリティグループを作成する」を選択</dd>
  <dt>　・セキュリティグループ名</dt>
  <dd>　　test-sg02</dd>
  <dt>　・説明</dt>
  <dd>　　任意に設定</dd>
  <dt>　・インバウンドルール</dt>
  <dd>　　タイプに「SSH」、ソースタイプに「任意の場所」を選</dd>
</dl>
</dl>

他は、デフォルトでインスタンスを起動します。

## プライベートサブネットのEC2にSSH接続する

### 踏み台サーバーに秘密鍵をコピーする
まず、以下のコマンドで踏み台サーバーに秘密鍵を置きます。
（自分的には）分かりやすいので、testkey01.pem があるフォルダには先に入っています。
```cmd:コマンドプロンプト ( testkey01.pem があるフォルダ)
scp -i .\testkey01.pem .\testkey01.pem ec2-user@${Elastic IPアドレス}:/tmp/
```

scpコマンドはSecure Copyの略で、リモートホスト間でファイルを転送するためのコマンドです。
```cmd:
scp [オプション] [送信ファイル] [宛先ホスト]:[宛先ディレクトリ]
```
オプションには、以下のようなものがあります。

| オプション | 説明 |
| --- | --- |
| `-r` | ディレクトリを再帰的にコピーするためのオプション |
| `-p` | ファイルのパーミッションを保持するためのオプション |
| `-q` | 転送中の進捗を表示しないためのオプション |
| `-i` | SSHの秘密鍵を指定するためのオプション |
| `-C` | 圧縮して転送するためのオプション |

### 踏み台サーバーにアクセスする
次に、以下のコマンドで踏み台サーバーにアクセスします。
```cmd:コマンドプロンプト ( testkey01.pem があるフォルダ)
ssh -i testkey01.pem ec2-user@${Elastic IPアドレス}
```

### プライベートサブネットのEC2インスタンスにアクセスする
最後に、以下のコマンドでプライベートサブネットのEC2インスタンスにアクセスします。
```cmd:コマンドプロンプト
ssh -i /tmp/testkey01.pem ec2-user@${プライベート IPv4 アドレス}
```

# 余談
最近、夜起きるのだるくて全然触れていなかった。

もうすぐGWですね。
GW終わるまでに、今作っているところまでで良いからこの環境でサイトをリリースしてみたい。

次はNATゲートウェイをつけようか。

#### 続き
追記（2023/04/29）

https://qiita.com/kanerin1004/items/cdb1b8354efe98d6aa15
初心者なら読んでほしい！「アプレンティスシップ・パターン」

# 表示
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2350392/264da428-6813-39a8-b83f-d72e559c5535.png)

# 書籍情報
題名：アプレンティスシップ・パターン――徒弟制度に学ぶ熟練技術者の技と心得
著訳：Dave H. Hoover, Adewale Oshineye　著、柴田 芳樹　訳
発行：2010年07月 発行

https://www.oreilly.co.jp/books/9784873114606/


# 前書き
この本はプログラミングの初心者が職人になるための姿勢について説明した本であって、学習すべき知識が多いプログラミング業界で、生き残る勇気をくれる本だと思います。

### 自己紹介
私は学習時間は1000時間以上、副業としてRails、Reactのエンジニアの仕事をして6ヶ月間、最近、内定をいただいて、6月から本業としてのエンジニアの仕事も始まる。という超駆け出しエンジニアです。
今の私の状況にこの本に出会ったのは本当に幸運です。

本の内容の中で刺さった内容いくつかを紹介します。


#### 成長する方法
成長する方法はとても多い。
- 勉強会に出る
- ブログを書く
- オフラインのコミュニティに参加して仲間を増やす
- カンファレンス参加して、仲間を増やす
- 学びたい言語の職人の記事を読む
- 学びたい言語の職人に連絡して教えてもらう
- 本を読んで作者に連絡してみる
- 今の会社が成長できない環境だと思ったら、成長できる会社に転職する
- いろんな言語を触ってみる
- 職人のコードを読もう
    - 職人のコードを見つけるのは難しくない。職人のコードから成長できる 
- 知らないことを隠さず現す
    - 隠せば隠すほど、成長できなくなる。知らないことを知らないと言えることで、周りにも教えてくれる
- サイドプロジェクトをやる
    - 新しい技術を壊してもいいおもちゃのように遊んでみることが重要 
- プログラミングを続けることで手に入るものを確認する
    - そうすることで、モチベーションを保つことができる 
- あえて難しいことをやる
    - 慣れてしまうと、成長はない。自ら難しいタスクに挑戦して成長し続ける
- 後30年あることを忘れずに
    - 職人は1日でできるもんではない。今30歳だったらプログラミングは後30年以上続くものなので、焦らず長く見て、学習をしよう
- 経験より、技術水準の向上が重要
    - 良い経験したわ。で終わると、悪い習慣が身についたかもしれない。自分の技術が上達したかをいつも確認する 

# 感想
作者たちはいろんなエンジニアをインタービュして、プログラミングの職人になれる方法、姿勢を全部まとめてこの本1冊に詰め込みました。本当に1枚1枚が貴重で、読むのにすごく時間がかかりました。

この本の問題は本の題名だけですね、、もっといい題名だったらより多くの方々から読まれたと思います。
内容はとても良いので是非とも読んでいただけると嬉しいです。

ありがとうございました。
【VSCode】プロジェクトごとに設定を変える方法

# はじめに
VSCode で、プロジェクトごとにタブのサイズを変更したいと思うことがありました。この記事では、そういった場合に役立つ「フォルダーの設定」という便利な機能を紹介します。

# 方法
1. VSCode で設定を変更したいフォルダーを開きます。
1. サイドバーのエクスプローラーを右クリックし、「フォルダーの設定を開く」を選択します。
![スクリーンショット 2023-04-26 22.57.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366249/1d884deb-9ce9-e558-f011-b80ed45c5720.png)
1. 見慣れた設定画面が出るので、あとは好きなように設定します。
![スクリーンショット 2023-04-26 22.59.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366249/86866b91-0d89-2ebd-d932-f7fdc2ff5cc0.png)

1. 設定を変更すると、「.vscode」というフォルダーに「settings.json」が作成されます。これを編集して設定を変えることもできます。

# まとめ
VSCodeでプロジェクトごとに設定を変更するには、「フォルダーの設定」を使用します。「.vscode/settings.json」を編集して設定を変更することもできます。

---

ご覧いただきありがとうございました。

🐦 [Twitter: @shota_appdev](https://twitter.com/shota_appdev)

📱 [個人アプリ: Symbols Explorer](https://apps.apple.com/jp/app/symbols-explorer/id1612933742)
CKA試験、Kube-dns

# Serviceと、DNS Lookup
https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/

## 問題
- nginx imageを使用する`resolver` podを作成し、`resolver-service`というサービスを構成します
- クラスタ内で、ServiceとPodのあんまえを参照できるのかテストします
- dns 参照に使うPodのImageはbusyboxで、ServiceとPodの名前の参照は`nslookup`です
- Service参照結果は`/var/CKA2023/nginx.svc`に、Pod名を参照した結果は`/var/CKA2023/nginx.pod`記録します

```sh
kubectl run resolver --image=nginx
kubectl expose pod resolver --port=80 --name=resolver-service

kubectl get pods
# PodのIPを記録
# 123.123.123.123

kubectl get service
# ServiceのClusterIPを記録
# 111.111.111.111

kubectl run test --image=busybox -it --rm /bin/bash
```

```sh
#testPod内部

# pod のdnsを確認
nslookup 123-123-123-123.default.pod.cluster.local

# service のdnsを確認
nslookup 111.111.111.111
```
結果をファイルを保存
「カタカナの半角・全角」「ひらがな・カタカナ」を区別せずに検索する

# やりたいこと

- あるテーブルのある VARCHAR 型のカラムで LIKE 検索したい。
- このとき、**検索文字列の「カタカナの半角・全角」「ひらがな・カタカナ」を区別せずに検索したい。**
- せっかくなので色々なテーブルやカラムで使用できるように、特定のモデルのメソッドではなく汎用的なクラスとして実装したい。

# バージョン情報

```shell-session
$ ruby -v
ruby 3.2.2 (2023-03-30 revision e51014f9c0) [arm64-darwin22]

$ bin/rails -v
Rails 7.0.4.3

$ mysql --version
mysql  Ver 14.14 Distrib 5.7.42, for osx10.18 (x86_64) using  EditLine wrapper
```

# 方法

- カタカナの半角・全角を区別せずに検索するために [COLLATE 句](https://dev.mysql.com/doc/refman/5.7/en/charset-collate.html)で `utf8mb4_unicode_ci` を指定する。
  - COLLATE 句は照合順序を指定するための句。
  - 照合順序とはデータベース内で文字を比較したりソートしたりする際のルールのこと。
  - `utf8mb4_unicode_ci` は Unicode のコードポイントに基づいて文字列を比較する照合順序のひとつ。
    - この照合順序では**カタカナの半角・全角を区別しないし、ひらがな・カタカナも区別しない**。
- [quote_table_name](https://api.rubyonrails.org/v7.0.4.2/classes/ActiveRecord/ConnectionAdapters/Quoting.html#method-i-quote_table_name) メソッドでテーブル名を [quote_column_name](https://api.rubyonrails.org/v7.0.4.2/classes/ActiveRecord/ConnectionAdapters/Quoting.html#method-i-quote_column_name) メソッドでカラム名をバックスラッシュでクォートする。
- [sanitize_sql_like](https://api.rubyonrails.org/v7.0.4.2/classes/ActiveRecord/Sanitization/ClassMethods.html#method-i-sanitize_sql_like) メソッドで LIKE 演算子の値をサニタイズする。
  - 具体的には `%` と `_` をエスケープすることで、ユーザの入力値を安全に LIKE 演算子に渡すことができる。

```rb
class PerformCaseInsensitiveSearch
  COLLATION = 'collate utf8mb4_unicode_ci'

  delegate :model, to: :@relation, private: true
  delegate :connection, :sanitize_sql_like, to: :model, private: true
  delegate :quote_column_name, :quote_table_name, to: :connection, private: true

  def self.call(...)
    new(...).call
  end

  def initialize(relation, column_name:, value:)
    @relation = relation
    @column_name = column_name
    @value = value
  end

  def call
    @relation.where(
      "#{quoted_table_name}.#{quoted_column_name} #{COLLATION} LIKE ?",
      "%#{sanitized_value}%"
    )
  end

  private

  def quoted_table_name
    quote_table_name(model.table_name)
  end

  def quoted_column_name
    quote_column_name(@column_name)
  end

  def sanitized_value
    sanitize_sql_like(@value)
  end
end

```

```rb
Monster.pluck(:name)
#=> ["リオレウス", "ﾘｵﾚｲｱ", "銀レウス", "るかりお"]
searched_monsters = PerformCaseInsensitiveSearch.call(Monster.all, column_name: :name, value: 'ﾘｵ')
searched_monsters.to_sql
#=> "SELECT `monsters`.* FROM `monsters` WHERE (`monsters`.`name` collate utf8mb4_unicode_ci LIKE '%ﾘｵ%')"
searched_monsters.pluck(:name)
#=> ["リオレウス", "ﾘｵﾚｲｱ", "るかりお"]
```

# 参考

- [MySQL :: MySQL 5.7 Reference Manual :: 10.8.1 Using COLLATE in SQL Statements](https://dev.mysql.com/doc/refman/5.7/en/charset-collate.html)
- [mysql - What's the difference between utf8_general_ci and utf8_unicode_ci? - Stack Overflow](https://stackoverflow.com/questions/766809/whats-the-difference-between-utf8-general-ci-and-utf8-unicode-ci)
Cloud Firestoreの画面で、条件に応じた結果を表示する（クエリビルダー）

今まで、Cloud Firestoreのデータは、一覧でしか表示できないと思っていたのですが、
デフォルト表示される画面の横に、「クエリビルダー」というタブがあることに気がつきました。
このタブで、where句(indexが無い場合は単一条件のよう）と、order by が指定できることに気がつきましたのでメモ。

手順：「クエリビルダー」→「クエリに追加」をクリックし、where句やorder byを追加　→ <実行>

![クエリビルダー.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/661656/88c9c858-ffc7-c20d-43ce-a6042bc7617c.png)
【GAS】ニュースサイトの記事を定期実行でスクレイピングしてスプレッドシートに保存その２

# はじめに

https://qiita.com/kokogento/items/630847be3a67b9a60e3c

👆の記事に対して少し手を加えて、今回はスプレッドシートへの保存方法を変えたいと思います！


一体どんな変更を施したかというと
**①日毎にシートを作成せずに、１つのシートにずっと追加し続ける**
**②最新を下に追加していくのではなく上に追加していく**
**③同じ記事がダブってもそのまま保存**

ざっくりとですが、これでより簡単なコードになるかと！
※ニュース記事をスクレイピングする方法は同じなので割愛します:raised_hand:


# 取得したデータをシートに保存

```javascript
function setValues() {
  //アクティブなスプレッドシートを取得
  const ss = SpreadsheetApp.getActiveSpreadsheet();

  //名前が「2023」のシートを取得
   const todaySheet = ss.getSheetByName('2023');
   
   //ニュース記事を取得
   const newsList = getValues();

  for (news of newsList) {
    //シートの一番上に行を追加
    todaySheet.insertRowBefore(1);
    const range = todaySheet.getRange('A1:F1');
    let array2D = [];
    array2D.push(news);
    range.setValues(array2D);
  }
}
```


`setValues`は２次元配列で値を渡さないといけないので、`array2D`を用意して追加しています。。

https://auto-worker.com/blog/?p=3709#

１次元配列👇
```console
[ '4/26 21:30',
  'https://jp.news.gree.net/news/entry/4646402?from_ggpnews=media_scoopie_list',
  'キュッとくびれた腰＆ムニっとした胸元が最高…グラビアアイドル・風愛ことりが披露した“天使のビキニショット”',
  'https://ei2bw16vy9.user-space.cdn.idcfcloud.net/public/img/article/64/02/4646402_280_01.jpeg?64491958',
  '5月4日には、都内で開催される「ベルエージェンシーBBQオフ会」への参加を予定している風愛。6月23日には、竹書房からファン待望のイメージ作品がリリース予定だ。',
  '風愛ことり' ]
```


2次元配列👇
```javascript
[ [ '4/26 21:30',
    'https://jp.news.gree.net/news/entry/4646402?from_ggpnews=media_scoopie_list',
    'キュッとくびれた腰＆ムニっとした胸元が最高…グラビアアイドル・風愛ことりが披露した“天使のビキニショット”',
    'https://ei2bw16vy9.user-space.cdn.idcfcloud.net/public/img/article/64/02/4646402_280_01.jpeg?64491958',
    '5月4日には、都内で開催される「ベルエージェンシーBBQオフ会」への参加を予定している風愛。6月23日には、竹書房からファン待望のイメージ作品がリリース予定だ。',
    '風愛ことり' ] ]
```


あとは好きなタイミングで実行されるようにトリガーを設定すればOK！:relieved:

```javascript
 //ニュース記事を取得
   const newsList = getValues();
```
については以前の記事を参考に！

https://qiita.com/kokogento/items/630847be3a67b9a60e3c#取得したデータをシートに保存
typescriptで hoge is possibly 'null'.のエラーは"?"(オプショナルチェーン)で逃がそう

tsを書いている時、
特定の要素に対して処理を行おうとするとtyescriptに下記のエラーで怒られました。

html側

```
<button id="js_btn" type="button">ボタンです</button>
```

ts側
```
const btn = document.querySelector("#js_btn"); //ボタン要素を取得
const click = new window.MouseEvent('click');//クリックイベントを作成

//ボタンに対してクリックイベントを発火。これを書くことで、実際にjs_btnをクリックしなくても発火できる
btn.dispatchEvent(click);
```

こんな感じで記述すると、typescriptコンパイル時にエラーとなりました。
エラー内容
```
'btn' is possibly 'null'.
```
文字通り、btn要素が取れないケースもある。
tsを書いている時点では該当要素があるかどうかtsさんからではわからないので、
そのオブジェクトがnullかどうか、チェックを行うことを求めます。
で、下記のように調整すると正常にコンパイルできました。

```
 btn?.dispatchEvent(click);
```

上記のオプショナルチェーンを利用することで、nullの場合はその先のdispatchEventは行わないぞ、ということになります。

参考
https://typescriptbook.jp/reference/values-types-variables/object/optional-chaining



追記：または、メソッド名の後ろに！をつけることで、これはnullではないとtsに伝えることも可能。
```
const btn = document.querySelector("#js_btn")!;
```
Larave sail mysql で 「service "mysql" is not running container #1」が出た時の対処方法

### Docker Composeを停止する
まず、Docker Composeを停止してください。


```$ sail down```

### Docker Composeのコンテナを削除する
次に、Docker Composeのコンテナを削除します。

```$ docker-compose down --volumes```

### Docker Composeを再起動する
最後に、Docker Composeを再起動してください。

```$ sail up -d```
これで、MySQLサービスが正常に起動し、エラーが解決される
GitHubを使った開発手順

GitHubを使用して開発を行う際の手順を示します。
また、下記に公式リファレンスがありますので確認してみてください。
https://docs.github.com/ja/get-started/quickstart/github-flow
また、今回の開発環境は以下になります
・OS：Windows11
・Ubuntu on WSL2 (Windows Subsystem for Linux 2)

## 1. ローカルにブランチを新規作成、切り替え
新しいブランチを作成し、そのブランチに切り替えます。
```
git checkout -b feature-branch
```
このコマンドで、新しいブランチ（`feature-branch`という名前の例）を作成し、そのブランチに切り替えています。

## 2. 任意のファイルの変更
ファイルをエディタで開いて、必要な変更を加えて保存してください。
## 3. 結果をコミット
変更したファイルをステージングエリアに追加し、コミットメッセージとともにコミットを作成します。
```
git add <変更したファイルのパス>
git commit -m "コミットメッセージ"
```

## 4. GitHubに作成したブランチで変更をプッシュ
ローカルの`feature-branch`ブランチの変更を、リモートリポジトリ（GitHub）の同じブランチにプッシュします。
```
git push origin feature-branch
```

## 5. GitHubを開き、作成したブランチからmainブランチへのプルリクエストを作成
GitHubのリポジトリページを開いて、「Pull Requests」タブをクリックし、「New Pull Request」ボタンを押します。次に、「base: main」と「compare: feature-branch」を選択して、「Create Pull Request」ボタンをクリックします。
## 6. 変更内容を確認し、GitHub上で変更をマージ
プルリクエストページで変更内容を確認し、問題がなければ、「Merge Pull Request」ボタンをクリックしてマージします。
## 7. GitHub上の作成したブランチを削除
マージが完了したら、「Delete branch」ボタンをクリックして、GitHub上の`feature-branch`ブランチを削除します。
## 8. ローカルリポジトリのブランチをmainブランチに切り替え
```
git checkout main
```
## 9. リモートリポジトリのmainブランチの内容をローカルリポジトリのmainブランチに取り込む
```
git pull origin main
```
## 10. ローカルリポジトリの作成したブランチを削除

```
git branch -d feature-branch
```
ただし、もしローカルのブランチがリモートリポジトリにマージされていると認識されていない場合、`-d`オプションでは削除できません。その場合、`-D`オプションを使って強制削除します。
```
git branch -D feature-branch
```
強制削除は注意が必要です。未マージの変更が失われる可能性があるため、本当に削除しても問題ないか確認してから実行してください。

## まとめ
これらの手順を繰り返すことで、開発を効率的に進めることができます。新しい機能や修正を開発する際には、手順1からスタートし、プルリクエストの作成とマージを繰り返してください。これにより、コードの変更がチーム内で共有され、問題が検出されやすくなります。
heroku pg backupのコマンド

```terminal
# 現在存在するバックアップの確認
heroku pg:backups --app app-name

# 今この瞬間のバックアップの作成（手動）
heroku pg:backups:capture --app app-name

# 自動バックアップのスケジューリング
heroku pg:backups:schedule DATABASE_URL --at '02:00 America/Los_Angeles' --app app-name
```

参考
https://devcenter.heroku.com/ja/articles/heroku-postgres-backups#set-up-a-backup-schedule
パスフレーズをハードコーディングすることの危険性と対処法【セキュリティ】

## **はじめに**

パスフレーズやパスワードを扱う際、セキュリティ上のリスクを最小限に抑えるため、ハードコーディングを避けることが非常に重要です。この記事では、パスフレーズのハードコーディングがなぜ危険なのか、そして代替手段としてどのような方法があるのかを解説します。

## **1. パスフレーズのハードコーディングとその問題点**

パスフレーズのハードコーディングとは、直接プログラムやスクリプトにパスフレーズを記述することを指します。このような方法でパスフレーズを扱うと、セキュリティ上のリスクが高まります。以下に、ハードコーディングされたパスフレーズを含むシェルスクリプトの例を示します。

```bash
#!/bin/bash

echo "パスワードマネージャーへようこそ！"

# ハードコーディングされたパスフレーズ
PASSPHRASE="your_passphrase"

# 以降のスクリプトでパスフレーズを使用する...
```

この例では、パスフレーズ **`your_passphrase`** が直接スクリプトに記述されています。この方法には以下のような問題があります。

1. **セキュリティ上のリスク**: パスフレーズがソースコードに直接記述されているため、誰かがソースコードを閲覧すれば簡単にパスフレーズを知ることができます。また、このソースコードがバージョン管理システム（例：Git）にコミットされると、パスフレーズが履歴に残り、後から削除することも困難になります。
2. **メンテナンス性の低下**: ハードコーディングされたパスフレーズは、パスフレーズを変更する際にスクリプト自体を編集しなければならず、管理が煩雑になります。また、同じパスフレーズが複数の場所に記述されている場合、一貫性を保つためにすべての場所を修正する必要があります。

## **2. 環境変数を利用した安全なパスフレーズ管理**

ハードコーディングされたパスフレーズの代わりに、環境変数を利用することで、上記の問題を回避することができます。環境変数を使用することで、パスフレーズを安全に管理し、アプリケーションのメンテナンス性も向上させることができます。

### **2.1 環境変数の利点**

環境変数を使用することで、以下のようなメリットが得られます。

- セキュリティが向上: パスフレーズをソースコードから分離し、環境変数に格納することで、悪意のある第三者によるアクセスが難しくなります。
- 柔軟性が向上: 環境変数を変更するだけでパスフレーズを更新できるため、メンテナンスが容易になります。

### **2.2 Docker環境での環境変数の利用**

Docker環境では、ホスト環境からコンテナ環境へ環境変数を渡すことができます。これにより、コンテナ内のアプリケーションがパスフレーズを安全に参照できるようになります。Docker環境で環境変数を設定・利用する方法は以下の通りです。

### 2.2.1 ホスト環境で環境変数を設定

まず、ホスト環境上でパスフレーズを環境変数として設定します。MacやLinuxの場合、ターミナル上で以下のコマンドを実行します。

```bash
export HOST_PASSPHRASE_ENV_VAR="your_passphrase"
```

### 2.2.2 Dockerコンテナに環境変数を渡す

Dockerコンテナを実行する際に、ホスト環境の環境変数をコンテナに渡すには、****`docker run`コマンドに`--env`****オプションを使用します。

```bash
docker run -it --env PASSPHRASE_ENV_VAR=${HOST_PASSPHRASE_ENV_VAR} your_image_name
```

**2.2.3 コンテナ内のアプリケーションで環境変数を参照する**

コンテナ内のアプリケーションでは、環境変数を参照してパスフレーズを使用できます。例えば、シェルスクリプト内で以下のように環境変数を参照できます。

```bash
PASSPHRASE=${PASSPHRASE_ENV_VAR}
```

## **まとめ**

パスフレーズをハードコーディングしないことは、セキュリティ上非常に重要です。環境変数を活用することで、パスフレーズを安全に管理し、アプリケーションのメンテナンス性も向上させることができます。Docker環境での環境変数の利用方法を理解し、セキュアなアプリケーション開発を行いましょう。
Salesforceの無料アプリ拡張ファイルカードを調査してみた

こんにちは

本橋孝昭です

今回はリゾルバ社が提供している無料App Exchange
「拡張ファイルカード」について解説します
こちらの拡張ファイルカードを使用すれば

・レコードの添付ファイルを探しやすくする

ということが可能です


今回の内容は動画でも紹介しています

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

<iframe width="560" height="315" src="https://www.youtube.com/embed/QC8Xjupybms" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>


インストール場所はこちらです


https://appexchangejp.salesforce.com/appxListingDetail?listingId=a0N3u00000Qu6XGEAZ

※注意１※　本番環境でいきなりインストールするのではなく、
まずはSandboxやplaygroundで試しにインストールして仕様をご確認ください

※注意２※　今回はplaygroundにインストールします。AppExchangeの
Playgroundのインストール方法は割愛しますので下記Trailheadをご参照ください
https://trailhead.salesforce.com/content/learn/modules/trailhead_playground_management


こちらをインストールすれば、通常の添付ファイルの場所だと添付した画像はこのような見え方になるので正直見にくいです

![添付ファイル01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531631/b36f1301-6362-49b7-361e-67d98b79dc11.png)

しかし、拡張ファイルであればこのようにレコードに表示されているファイルがよく見えます

![ファイル02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531631/d47a9e8f-e98f-71dc-4b6d-7a3d012bdf96.png)


例として、１つの商談に見積書などおなじような書類が複数紐づいている場合があるかと思います
その場合、見積書の見た目は似ているので、探したい見積書はどれなのか探すのに苦労したりすることあるかと思います

そんなときのために、この拡張ファイルはお勧めです

これを使えば、探したい画像やファイルが容易に探せますね

設定方法は簡単です

カスタムコンポーネントから
・fileCardList
を選択し表示させるだけです


![設定方法01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531631/bb161b47-66c9-cdfb-a576-c8b24078090e.png)


しいていえば、
列数　が
1
2
3
と選べるので、ここで表示したい添付ファイルの列数を記載してください

これで設定は完了です。


この拡張ファイルカードを使用すれば、レコードにたくさんひもづくファイルについて、クリックしなくてもファイルを探すことが可能ですね

ぜひ添付ファイルを探す際は、この拡張ファイルカードを使ってみてください