OmniAICreator/Qiita-1.07M huggingface dataset:
Microsoft Forms + PowerAutoimateでOutlookのカレンダーに予定を書く(時刻を含む)

今回は標題の通り、MS Forms と PowerAutomate とOutlookを連携させます。目的はFormsに入れた時刻付きの予定をOutlookに記載することです。

# こんな回りくどいことをなぜやるのか

さて、なぜこんな回りくどいことをするのか。と言うことですが、こういうシーンを踏まえて作りました。
1. 誰かがFormsを使って施設の解錠予約をする
1. 管理者が承認する
1. すると、承認した時刻に予定が入る
1. 管理者はその予定時刻になったら鍵を開けに行く
1. 管理者は終了時刻に施錠しに行く

この3番4番5番をきっちりおこなうために作ろうとしました。

# ･･･というわけで試行錯誤しました。

まずは、カレンダーにイベントを作成するアクションを出します。今回は時刻だけに絞っていますので他のことは必要ならば他で調べてください。
で、時刻の指定のしかたを見てみるとこんなことが書いてありますね。
![Screenshot 2023-01-07 at 05-59-51 フローの編集 Power Automate.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/494348/790eb8b6-19b2-f400-9e87-71e5b0e58381.png)

例:'2017-08-29T04:00:00'

ってことはこのフォーマットにすれば良いのかと思います。実際にやってみましょう。

## Formsで必要事項を取得しよう

まずはFormsから値を取得します。方法は簡単

1. 日付の入力
1. 範囲0から23までのテキスト入力
1. 範囲0から59までのテキスト入力

を設けましょう。

では、それらを使ってアクションに入れる数値を作りましょう。今回はわかりやすいように2段構えにしています。

## 数値を日付にするのにこんなに苦労するとは思わなかった

パーツごとに簡単に説明します。

1. 日付はそのまま使う
1. 時刻は0でパディングすることがある
1. そしてそれらを文字列として結合する

つまり、こういう関数になります。

concat(日付,'T',時,':',分,':00')

やっかいなのは時と分で、こいつらは例えば分なら0分から9分の間は0でパディングしないとエラーになります。
そこで時と分はこういう手を使いました。

* string(formatNumber(int(時,'00')))
* string(formatNumber(int(分,'00')))

これらをバラバラに説明する必要はあるかわかりませんがこういうスタンスで作りました。

1. 時または分のテキストを整数にする
1. 整数を00でフォーマットする
1. それを文字列に再変換する

これを組み合わせてまずは日時の文字列を完成させるとこうなります。

```
concat(日付,'T',string(formatNumber(int(時,'00'))),':',string(formatNumber(int(分,'00'))),':00')
```

これだけでも長いので、これを変数***dateTimeString***として格納しあとはこれを使って

parseDateTime(***dateTimeString***,'ja-JP')

とします、この変数を使ってさっきのカレンダーの日時にこの関数もしくはもう一段変数を設けてこの値を入れます。

そうしたら、何とかカレンダーに日時付きの予定が入りました。

これで終了です。お疲れ様でした。
Virtual BoxにUbuntu20.04をインストールしました

Virtual Box 上にUbuntuをインストールしました。

# 環境
Windows10 Home 20H2
VirtualBox6.1（VirtualBox-6.1.40-154048-Win.exeをインストール）

# インストールしたUbuntuのインストールファイル
ubuntu-ja-20.04.1-desktop-amd64.iso

# インストールの流れ
1.Virtual Boxで新規作成
(仮想マシン →) 新規
・タイプ：Linux
・バージョン：Ubuntu (64-bit)
・メモリサイズ：4096MB（4GBのこと）
・ハードディスク：仮想ハードディスクを作成する
・ハードディスクのファイルタイプ：VDI（Virtual Box Disk Image）
・物理ハードディスクにあるストレージ：可変サイズ
・ファイルの場所とサイズ：サイズ　20GB

2.Ubuntuのインストールファイル(ISOイメージ)をダウンロード
・日本語 Remix イメージ　ubuntu-ja-20.04.1-desktop-amd64.iso

3.VirtualBox で　ダウンロードした Ubuntu のイメージファイルを使って Ubuntu を起動
(仮想マシン →) 起動
・起動ハードディスクを選択：仮想光学ディスクファイルの選択 → 追加　ダウンロードしたファイルを指定

4.Ubuntuをインストール
キーボードレイアウト：Japanese
通常のインストール
ディスクを削除してUbuntuをインストール
どこに住んでいますか：Tokyo

# Ubuntuのアップデート
1.アップグレード可能なパッケージリストの取得
`sudo apt update`

2.アップグレード可能なパッケージのアップグレード
`sudo apt upgrade` 
以下のパッケージが自動でインストールされましたがもう必要とされていません。
と表示されたので
`sudo apt autoremove`

3.dkmsパッケージのインストール
`sudo apt install dkms` 

# 解像度の変更を可能にする
デバイス → Guest Additions CDイメージの挿入

マウントできないというエラー（仮想光ディスク C:\Program Files\Oracle\Virtualbox\VBoxGuestAdditions.isoをマシンUbuntu　20.04に挿入できません。）になってしまったので、いったん、Ubuntuを電源オフ

VirtualBoxで 設定
ストレージ → ストレージデバイス → コントローラーIDE → 光学ドライブの追加 → 空のままにする

Ubuntuを起動

デバイス → Guest Additions CDイメージの挿入

# 初期設定からCPU数の変更
VirtualBoxで 設定
システム → プロセッサー数 2 に変更

#ホストOSとゲストOSで相互でコピペできるように設定変更
VirtualBox 仮想マシン → 設定 → 一般 → 高度
クリップボード共有の項目を双方向に変更

# バックアップしておく
VirtualBoxで ファイル → 仮想アプライアンスのエクスポート

# 参考にしたサイト
・https://www.ci.seikei.ac.jp/advpro/
上記のサイトの「VirtualBox＋Ubuntuの環境準備方法」のリンク先の「VirtualBox＆Ubuntuインストールマニュアル.pdf」

・VirtualBoxにUbuntu20.04 ＆ VBox Guest Additionsをインストールする
https://hiiragi-works.sakura.ne.jp/virtualbox-ubuntu-001/

・VirtualBoxで作った仮想マシンをエクスポートしていつでも元に戻せるようにする方法
https://selifelog.com/blog-entry-1579.html
テストで考えるべきこと

<h4>・はじめに</h4>
この記事では、テストで考えるべき基本的なことについて書いていく。

<h4>・テストの必要性</h4>
テストが必要な理由は、作ったソフトウェアがきちんと動作するかを確認しなければならないためである。たとえば、電卓を使う際足し算をしているのに掛け算がされたり、閉じるボタンを押しても閉じず毎回毎回タスクマネージャーからアプリを落とさなければらならないなど、不具合や誤作動を起こさないソフトウェアを作らなければならないためである。<br><br>
そしてテストが必要な理由は、もう１つ理由があり、それはユーザーが求めているものをしっかりとソフトウェアに落とし込めているかを確認する必要があるためである。たとえば、スマホのカメラアプリで写真を撮って保存する際、毎回保存をするか、削除（保存しない）かを選ばなければならない仕様になっていたり、タイマー機能を使う際、わざわざパスコードを入力しなければならない仕様になっているような、ユーザーのニーズとはまったく合わないようなソフトウェアになっていないかということを確認するためである。
<br><br>
このようにテストとは「不具合や誤作動がないかというまずソフトウェアがきちんと動くかを確認すること、さらにソフトウェアがユーザーのニーズにきちんと合致しているかを確認するため」に行うものである。

<h4>・ユーザのニーズとは</h4>
次にユーザーのニーズについて考えていく。<br>ソフトウェアにおける欠陥とは、わかりやすいもので一言でいえばソフトウェアが機能しなければ、そのソフトウェアに欠陥があるということができる。しかし、ユーザーのニーズにあっているかどうかというのはとても抽象的なもので、具体的にどうすればユーザーのニーズにあったソフトウェアになるのかということは難しい。<br><br>

<h5>・狩野モデルとは</h5>

そこで考えたいのが「ユーザー満足」と「品質」の関係を示した「狩野モデル」である。
狩野モデルは、品質モデルを「当たり前品質」、「一元的品質」、「魅力的品質」に分けて考える。<br>
「当たり前品質」とは、あって当たり前の機能がある品質のことである。キーボードにおける当たり前品質とは、きちんと文字が入力されることである。当たり前品質は無ければ商品、ソフトウェアとしてありえないレベルの品質を「当たり前品質」という。

次に「一元的品質」とは、なければ不満が出る、あれば評価があがるような機能である。電子書籍リーダーを例に挙げると、しおり機能などである。もし電子書籍リーダーにしおり機能が無ければ、不便に感じ、もししおり機能があれば便利だと感じる。このようなあれば評価があがり、なければ不満が出るような品質のことを「一元的品質」という。

最後に「魅力的品質」とは、あればすごいと評価が上がり、なくても仕方がない、なくて当然と感じさせるような機能である。例としては、動かすことによって電気を作り出す機能があるマウスなどです。このような人が期待していないような品質のことを「魅力的品質」という。

このように「狩野モデル」はユーザーのニーズに合っているかどうかをこの基準に考えることに役に立つのである。

<h4>・テストをする際に大切な目線</h4>
次に、テストをする際に担当者が持つべき視点、大切な視点について２点考えていく。

<h5>・Ｖ＆Ｖ</h5>
Ｖ＆Ｖとは、「Verification & Validation(検証と妥当性確認)」の略である。<br>
「<b>Verification</b>」の視点とは、ソフトウェアの設計図通りに作られているかどうかを確認することである。このVerificationを行う際は実際にソフトウェアを動かして確認する。ソフトウェアの設計図は開発工程ごとに作成されるため、Verificationも開発工程ごとに行われる。
<br><br>
「<b>Validation</b>」の視点とは、ユーザーの要望通りにソフトウェアが作られているかどうかを確認売ることである。Validationは、ソフトウェアを実際に動かして確認する。そしてそれに加えて実際に動かしてユーザーの要望通りに作られているかを確認する。
<br><br>
すべての欠陥はこの「V&V」のどちらかに分けることができる。<br>

<h4>・Never・Must・Wantの視点</h4>
Never・Must・Wantの視点とは、<br>
<b>Never</b>:あってはならないこと<br>
<b>Must</b>:できればなければならないこと<br>
<b>Want</b>:できたらよいこと
<br>
この３つの視点からテストを考えることである。<br><br>

具体的には
Neverとは、<b>生命や財産など</b>に悪影響を及ぼすような事態がないかどうか
Mustとは、<b>ユーザーのニーズ</b>にあっているかどうか
Wantとは、<b>改善するべき点</b>や<b>使いやすさを向上させる</b>ような点がないかどうか
このような視点でテストをすることである。


【参考】
この1冊でよくわかるソフトウェアテストの教科書[増補改訂 第2版] 著: 布施昌弘ら
https://www.amazon.co.jp/dp/481560875X
素数一般項による素数の生成 Python3

素数の一般項という数式を見つけたので、python3 で、mpmathモジュールを使って、任意精度演算で素数を求めるプログラムを書いてみました。
但し、一回の演算の回数が多すぎるため、小さい素数（２９とか）でも、遅くて、使い物にはなりません。
`chmod +x prime.py`として、実行権限を付けて動かして下さい。
Usage: prime.py

# 素数の一般項
説明などが欲しい方は、googleで、「素数　一般項」等として検索して下さい。

<img width=800 src="https://i.imgur.com/WOqzlPA.jpeg">

# 実行

mpmathモジュール は、
`$ python3 -m pip install mpmath`としてインストールして下さい。

ここでは、演算精度は200にしてあります。

# プログラム１

```prime.py
#!/usr/bin/python3
import mpmath
import sys
argv=sys.argv
n=int(argv[1])
mpmath.mp.dps = 200

s1=1
for m in range(1,int(mpmath.power(2,n)+1)):

 s2=0
 for k in range(1,m+1):
   s2+=mpmath.floor(mpmath.power(mpmath.cos((mpmath.factorial(k-1)+1)/k*mpmath.pi),2))

 s1=s1+mpmath.floor(mpmath.power(n/s2,1/n))
print(int(s1))
```

# プログラム２

こちらは、同じアルゴリズムで、次々と素数を表示していきます。

```prime4.py
#!/usr/bin/python3
import mpmath
import sys
argv=sys.argv
mpmath.mp.dps = 200

def nthprime(n):
  s1=1
  for m in range(1,int(mpmath.power(2,n)+1)):
    s2=0
    for k in range(1,m+1):
      s2+=mpmath.floor(mpmath.power(mpmath.cos((mpmath.factorial(k-1)+1)/k*mpmath.pi),2))
    s1=s1+mpmath.floor(mpmath.power(n/s2,1/n))
  return(s1)

for i in range(1,100):
  print(int(nthprime(i)))
```
typescriptでvsce packageとF5で実行結果が違って困った話（vscodeで使うtsのバージョンを確認しよう）

数時間持ってかれたので備忘録代わりに。

## TL;DR
vscodeでnodejsプロジェクトを扱うときはちゃんとワークスペースのtypescriptのバージョンを使うこと。

## 経緯

vscode拡張機能を作成中、以下に遭遇。
前提として、package.jsonを弄ってパッケージの整理をしていた。
1.`vsce package`で以下のエラーが出る。
```
tsconfig.json:13:3 - error TS5023: Unknown compiler option 'ignoreDeprecations'.
13   "ignoreDeprecations": "5.0",
     ~~~~~~~~~~~~~~~~~~~~
```
そんなオプションないよって言われてる。

2.tsconfig.jsonでは以下の画像のようなエラー。`ignoreDeprecations:5.0`を追加しろと言われる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/728667/3ebb9d5a-5db8-59c0-f903-d31b68b0b278.png)

## 何があったのか？
どうやら、`vsce package`に使っているtypescriptのバージョンとvscodeで使っているtypescriptのバージョンが異なっていた。
vscodeのtsバージョンは画像の手順で確認可能。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/728667/23a48afa-2eb0-8c2e-2944-02a0ecbd018e.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/728667/8b7c2efe-5504-b97a-cc3b-4629016e3aec.png)

おそらくnode_moduleの削除などをやっているうちにvscodeで使うtsのバージョンがワークスペースのものからvscodeのバージョンへと変わってしまった。
ts5.0以降ではsuppressImplicitAnyIndexErrorsでエラーがでるが、4.9.4だと警告が出ない（そのためignoreDeprecationsも存在しない）ため、エラーが出たっぽい。
vscodeでnodejsプロジェクトを扱うときはちゃんとワークスペースのtypescriptのバージョンを使うこと。
DynamoDBで1:nのデータ構造を表現する

こんにちは太郎です。
最近、DynamoDBを触る機会があり1:nのデータ構造を表現しようとしました。
しかし、DynamoDB特有の設計方法に戸惑ったのでメモを残します。

## 前提条件
この先は例を挙げながら進めます。
ひとつのチームに複数の選手が所属しているという想定です。
チームの一覧を表示する画面と属する選手の一覧を表示する画面があります。
DynamoDBではRDBとは違い、どのようにデータにアクセスするのか次第で構造がかなり変わるため、最初にユースケースをリストアップすることが重要になってきます。

### テーブル設計
RDBで設計するなら２つテーブルを作って、一方が片方を参照しているという設計が一般的かと思います。
しかし、DynamoDBではテーブルの数は極力減らすという方針があります。
それぞれの一覧が取得できるように以下のような設計にしました。

| ID(PK) | dataType(GSI) | teamName | memberName | teamID |
| - | - | - | - | - |
| 1 | team | teamA |
| 2 | team | teamB |
| 3 | member | | memberA | 1 |
| 4 | member | | memberB | 1 |
| 5 | member | | memberC | 2 |

### チーム一覧を取得する
上で設計したテーブルからチームの一覧を取得しようと思います。
グローバルセカンダリインデックス(GSI)にdataTypeを登録しています。
このGSIを使ってチームの一覧を取得しようと思います。
AWSのコンソールを使うと画像のようになるのですが、GoのSDKを使うと下のようになります。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/551042/13185d91-c0a2-af28-c5ed-e3ed61c69627.png" width="500" />

```go:main.go
params := &dynamodb.QueryInput{
  TableName: aws.String("sample"),
  ExpressionAttributeNames: map[string]*string{
    "#dataType": aws.String("dataType"),
  },
  ExpressionAttributeValues: map[string]*dynamodb.AttributeValue{
    ":dataType": {
      S: aws.String("team"),
    },
  },
  KeyConditionExpression: aws.String("#dataType = :dataType"),
  IndexName:              aws.String("dataType-index"),
}
```

### teamAに所属する選手の一覧を取得する
dataTypeがmemberかつ、teamIDが１の選手を取得します。
コンソールで操作すると画像のような感じです。
Goのコードも載せます。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/551042/69a16e7a-4888-27b0-82a8-a3e94b0ac374.png" width="500" />

```go:main.go
params := &dynamodb.QueryInput{
  TableName: aws.String("sample"),
  ExpressionAttributeNames: map[string]*string{
    "#dataType": aws.String("dataType"),
    "#teamID":   aws.String("teamID"),
  },
  ExpressionAttributeValues: map[string]*dynamodb.AttributeValue{
    ":dataType": {
      S: aws.String("member"),
    },
    ":teamID": {
      S: aws.String("1"),
    },
  },
  KeyConditionExpression: aws.String("#dataType = :dataType"),
  FilterExpression:       aws.String("#teamID = :teamID"),
  IndexName:              aws.String("dataType-index"),
}
```

## まとめ
DynamoDBの操作は独特でSDKの扱いにも手こずりました。
下にGolangの全ソースコードを載せます。

[チーム一覧を取得する](https://github.com/h-taro/dynamodb-one-to-many-get-teams)

[チームに所属する選手一覧を取得する](https://github.com/h-taro/dynamodb-one-to-many-get-members)

## 参考
https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/best-practices.html
https://qiita.com/_kensh/items/2351096e6c3bf431ff6f
【Vue.js + golang】ログイン機能をdockerで構築

## はじめに
フロントの描画をvue、バックエンドをgolangで実装し、ログイン機能を実現したものをgithubで公開しました。
https://github.com/rirma/vue_golang_framework

Vue.jsとgolangで構築している記事は何個か見かけましたが、そのまま使えるログイン・ログアウトの機能を実装しているものや、dockerで開発できる環境をgitで公開してくれているものが存在しなかったため構築しました。

今回は、公開した機能一式の説明をします。

## 環境
+ PC: MacBook Pro (15-inch, 2019)
+ OS: Monterey
+ golang: ver1.17
+ Vue: ver3.2.13

goのフレームワーク
Webフレームワーク: gin
DB接続関連: gorm

LaravelライクなMVCモデルを目指して構築しています。

試される方いらっしゃいましたらまずはgithub記載の手順に沿ってVueのコンパイル、及びgolangサーバの立ち上げを行なってください。

## 画面
### ログイン画面
ログインボタンを押すと、localhost:8082のgolangサーバにアクセスし、セッションを確立します。

![スクリーンショット 2023-01-07 2.15.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/767221/e75852c7-dfd3-38bf-a46a-c9a12b394aa0.png)

### ログイン後の画面
ログインをするとまずこの画面に遷移します。
未ログインの状態でこのページにアクセスしようとすると、ログイン画面にリダイレクトします。
ログアウトボタンを押すと、セッション情報を破棄します。

![スクリーンショット 2023-01-07 2.16.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/767221/bb2817a1-80a3-a20b-eb6d-28e9470e0213.png)

## 最後に
今回、Vueもgolangも実装するのは初めてで、configとしてまとめきれていないものやaxiosの非同期通信周りで粗い部分がまだまだあります。ベストプラクティスを教えていただけると嬉しいです。
[Python] 仮想環境を構築する

# 使用技術

- [Python 3](https://www.python.org/)
- [venv](https://packaging.python.org/ja/latest/guides/installing-using-pip-and-virtual-environments/)

※この記事はPython 3を使用しています。Python 2では手順が異なります。

# 仮想環境について
Pythonには仮想環境を構築するためのライブラリがいくつかあります。
これらを使用することで各プロジェクトに対して それぞれ別のパッケージをインストールして管理することができます。

もし共通の環境で開発を進めると、モジュールを追加・削除したりバージョンアップしたことによって他のプロジェクトに影響を与える場合があります。
仮想環境を使用することで、これを解決することができます。

# 構築方法

## 1. コンソールを開く
Windowsでは特に事情がない限り、PowerShellを使用することを推奨します。

## 2. pipを最新版にする
pipコマンドを使用する前に、最新にする必要があります。
最新をインストールするには以下のコマンドを実行します。
```
$ python -m pip install --upgrade pip
```
:::note info
ヒント
先頭の `$` はコマンドであることを意味するものです。実際にコンソールに入力するのは `$` を省いた文字列です。
:::

## 3. (venvをインストールする)
:::note info
venvは通常、Pythonの標準ライブラリに含まれているため**この手順は飛ばして構いません。**
念のためインストール方法を記載しておきます。
:::
仮想環境を構築するためのツール 'venv' をインストールします。
```
$ python -m pip install venv
```

## 4. 仮想環境を作成する
cd (chdir) を用いて、仮想環境を作成したいディレクトリに移動します。
```
$ cd (任意のパス)
```
次に仮想環境を作成します
```
$ python -m venv env
```
これで仮想環境を作成できました。

## 5. 仮想環境を有効化する
作成した仮想環境を有効化すると、その環境に入ることができます。

UNIX系では以下のコマンドを実行します
```
$ source env/bin/activate
```

Windows系では以下のコマンドを実行します
```
> .\env\Scripts\activate
```
これで仮想環境の構築は完了しました。

:::note warn
Windowsではコマンド実行時に下記のエラーが発生する場合があります。
```
このシステムではスクリプトの実行が無効になっているため、ファイルを読み込むことができません。
```
その際は以下のコマンドを実行して、ファイルの実行ポリシーを変更する必要があります。
```
> Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope Process
```
その後、先程のコマンドをもう一度実行してください。
:::

## 6. 仮想環境内で作業開始
仮想環境内でpipコマンドを行えば、ローカルとは別に、その環境内でパッケージを管理できます。
試しに `$ pip list` とするとローカルにインストールされているものとは別であることが確認できます。

プログラムを実行するには `$ python (ファイルパス)` とすることで、仮想環境内で実行できます。

特定のバージョンをインストールするには以下のように入力します
```
$ pip install (パッケージ名)==(バージョン)
```


## 7. 仮想環境を無効化する
仮想環境を無効化すると、その環境から出ることができます。
```
$ deactive
```
:::note info
もう一度仮想環境に入るには有効化を行うだけです。
:::

# さいごに
今回はPythonの仮想環境を構築する方法について説明しました。
参考になれば幸いです。

# 参考
- [ pip と仮想環境を使ってパッケージをインストールする |  Python Packaging User Guide](https://packaging.python.org/ja/latest/guides/installing-using-pip-and-virtual-environments/)
Raycast 3周年記念の思い出話を翻訳してみる

# Raycast
Raycast創業者のひとり、Thomas Paul Mannが3周年記念でちょうど今、Twitterで思い出話を呟いていたので翻訳しようと思う。
Raycast3周年おめでとう！

## 1/12スレッド
https://twitter.com/thomaspaulmann/status/1611381287700529157
### 訳
3年前１つのアイデアとして始まったRaycastは今や何万人もの生産性を加速させている。
思い出を振り返り原点に立ち返ってみよう。

## 2/12スレッド
https://twitter.com/thomaspaulmann/status/1611381318591602690
### 訳
僕らはYコンビネータのYC Winter 2020 Batchに加わり、Demo Dayまでに3ヶ月の猶予があった。
そこで、僕らはサンフランシスコに向かう飛行機の中で、クリップボードヒストリーのような機能を開発した。

もちろん、象徴的なYコンビネータの看板の前でお約束の写真も撮っている。

## 3/12スレッド
https://twitter.com/thomaspaulmann/status/1611381387411722241
### 訳
Yコンビネータのネットワークが手に入ったので、Airbnb、Lyft、Uberといった企業のOBと話をした。
みんなツールに遅いという不満があった。

なので僕らはJiraエクステンションを作って彼らの工数が短縮されるようにした。
(issueを立ち上げる、誰かをそれにアサインする、それを編集する。)

## 4/12スレッド
https://twitter.com/thomaspaulmann/status/1611381393237610496
### 訳
僕らはHacker NewsでRaycastを発表しDemo Dayの前に注目を集めることができた。
開発者がウェイティングリストにサインし僕はビデオ通話でオンボーディングをした。
Petrはその間に問題を修正していた。

## 5/12スレッド
https://twitter.com/thomaspaulmann/status/1611381396014260230
### 訳
Accelからシードラウンドで資金調達をし、チームビルディングを開始した。
この時誰も僕らの会社を知らなかった。
だから僕らの人脈を頼った。
僕らは初期メンバーを採用し、
その中でも@nichlaswaはRaycastのウェイティングリストに一番初めにサインした人だった。

## 6/12スレッド
https://twitter.com/thomaspaulmann/status/1611381398803451904
### 訳
ここで僕らは一からアプリを作り直した。
高速に改善でき、モダンなデザインで新しい仲間が活躍できるような強固な基盤を求めていたのだ。
この基盤のおかげで大きな機能をリリースできている。

## 7/12スレッド
https://twitter.com/thomaspaulmann/status/1611381409125654528
### 訳
僕らは新しいエクステンションをリリースした。
@asana, @github, @GoogleWorkspace,@linear, @Zoom
を一週間ごとにだ。
ユーザの声を反映しApple CalenderやReminderも統合した。
1期目の締めくくりとして、@ProductHuntをリリースした。

## 8/12スレッド
https://twitter.com/thomaspaulmann/status/1611381413026353152
### 訳
これまでエクステンションはネイティブで開発していた。
当初からAPIが必要だと思っていたけどUXを設計する必要があった。
2期目は開発者を支援するためにAPIの開発に専念した。
もっと詳しく知りたい場合は以下を聞いてほしい。
@DevtoolsFM
: http://devtools.fm/episode/38

## 9/12スレッド
https://twitter.com/thomaspaulmann/status/1611381415798788096
### 訳
僕らはAPIを構築しただけではなくプラットフォーム全体を構築した。
開発者はエクステンションを開発して僕らのストアで配布できる。
ユーザーはワンクリックで何百ものソリューションをインストールできるのだ。

再び僕らはより早く前進するための基盤を構築した。
今回はコミュニティの助けを借りた。

## 10/12スレッド
https://twitter.com/thomaspaulmann/status/1611381419049385984
### 訳
3期目はプロダクトを成熟させる年だ。
組織に対してプライベートなエクステンションを共有できるようにした。
またAPIを使ってネイティブのエクステンションをオープンソースのものに差し替えた。

一貫して新機能をリリースすることで品質に対してのブランドを立ち上げることができた。

## 11/12スレッド
https://twitter.com/thomaspaulmann/status/1611381422018940930
### 訳
プロダクトを作る一方で職人のための会社を作った。
フィードバックに傾聴しコードレビューを求ない。平等な給料を支払い、毎週金曜日はハックを行う。

覚えておいてほしい。会社を始めるときは世間に流されず君が働きたい場所を作れ。

## 12/12スレッド
https://twitter.com/thomaspaulmann/status/1611381424820740097
### 訳
この3年間に感謝しこれからもさまざまな機会を楽しみにしたい。
自分で何かを作ればアイデアは尽きることがない。
同じ問題に情熱を燃やす仲間と集まり走り出せば勢いは遠くまで君を連れていく。

### 最後に
プロダクトを作ること、会社を立ち上げるということ、情熱を持って取り組むこと、すごく素晴らしいなと思った。
胸が熱くなった。
さあ、作りたいプロダクトを作ろう。
【C++】基底クラスに virtual デストラクタを用意しないと、派生先のメンバクラスが解放されない

#
C++ 初心者なのでかなりハマってしまった……。指摘等あればください、お願いします。
使用ツールは Visual Studio 2017 です。

:::note warn
注意
この記事の内容はあくまで推測なので、間違いの可能性があります
:::

:::note
**ご指摘いただきました**
コメントに詳しい理由が書かれています。また、より実践的な例も指摘いただいております。
:::

## まとめ
- **ポリモーフィックに運用する基底クラスで、派生クラスがオブジェクトを保持する場合、基底クラスのデストラクタを virtual にする。**
→ 派生先の保持するメンバクラスのデストラクタが呼ばれないため。
### 参考にさせて頂いたサイト
[C++のメモリリーク検出！Visual Studio系](https://tech.walkit.net/memory-leak-vsc)
[【c++】デストラクタにvirtualを付ける場合、付けない場合。](https://qiita.com/ashdik/items/3cb3ee76137d176982f7)
## 問題のコード
メモリリークするコードを提示します。
```cpp:main.cpp
// メモリリーク検出用
#define _CRTDBG_MAP_ALLOC
#include <cstdlib>
#include <crtdbg.h>

#include <string>

// 基底クラス
class Base {
};

// 派生クラス
class Derived : public Base {
private:
	std::string str;
};

int main() {
    // メモリリークを検出する
	_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF|_CRTDBG_LEAK_CHECK_DF);

	Base* polymorphic = new Derived;

	delete polymorphic;
}
```
上記のコードでは、派生クラスの`std::string str`が解放されず、メモリリークします。

~~派生クラスではデストラクタを使用しないため、基底クラスには virtual デストラクタを実装していません。**が、これはおそらく誤りです**~~

**コメントで指摘いただきました**
この場合、どうやら未定義動作となるようです。詳しくはコメントをご覧ください。

## 修正方法
修正済みのコードを提示します。
```diff_cpp:main.cpp
// メモリリーク検出用
#define _CRTDBG_MAP_ALLOC
#include <cstdlib>
#include <crtdbg.h>

#include <string>

// 基底クラス
class Base {
+ public:
+     virtual ~Base(){};
};

// 派生クラス
class Derived : public Base {
private:
	std::string str;
};

int main() {
    // メモリリークを検出する
	_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF|_CRTDBG_LEAK_CHECK_DF);

	Base* polymorphic = new Derived;

	delete polymorphic;
}
```
上記のコードでは、基底クラスに **virtual なデストラクタを実装しています**。これにより、メモリリークが発生しません。
~~「基底クラスに virtual なデストラクタを実装しないと、派生クラスのデストラクタが呼ばれない」
と覚えていたのがダメだった。どうやら、**派生クラスの持つメンバクラスのデストラクタも呼ばれない**ようです。~~

**コメントで指摘いただきました**
問題のコードの場合、どうやら未定義動作となるようです。詳しくはコメントをご覧ください。
Media Session APIについての所感

本記事は自分のブログからの流用です。[Media Session APIについての所感](https://mkw-blog.herokuapp.com/article/157PERT2DAhvR36vyEr2uh)

Media Session API はメディアについての通知をカスタマイズする機能を提供しています。これによってメディア再生時に表示されるデバイス毎に違う通知画面の情報をある程度統一したり操作性が良くなることでUX向上に繋げられたりすることができます。ただこの機能はまだまだ扱いにくく不安定？なところも多いので触っていて思ったことを備忘録としてまとめました。
仕様はこちら: [Media Session API](https://developer.mozilla.org/en-US/docs/Web/API/Media_Session_API)

# Media Session API概要
これについてはGoogleが提供しているデモを見ていただくのが一番わかりやすいと思います。[Media Session Samples](https://googlechrome.github.io/samples/media-session/)
基本的には`navigator.mediaSession.metadata = new MediaMetadata()` にtitleやartistなどを渡すことでメディア通知に反映することができます。基本的に渡す値は文字列ですが、artworkは配列でsrc, sizes, typeからなるオブジェクトを渡すことができます。定義していない状態だと基本的にページのfaviconとタイトルを表示するのみになります
適用前:

published
![Screen Shot 2022-09-23 at 22.24.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/336928/90bfe475-7d49-0fe7-677d-60ca0a65aafa.png)

適用後:
![Screen Shot 2022-09-20 at 17.23.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/336928/4cec11fd-5593-aef8-f053-47d52737f8a8.png)


metaデータを渡しただけの状態ではブラウザやデバイスの種類にもよりますが、chromeでは再生・停止ボタンのみです。（ちなみに後述の再生・停止の関数を指定していなくてもブラウザやデバイスによっては押下によって再生しているメディアを直接操作するので再生状態をステートの状態でのみ管理している場合は注意が必要です）

再生・停止やシークなどの機能を追加する場合は`navigator.mediaSession.setActionHandler(action:string, handler:function)`で追加することができます。

またシークや10秒スキップのような再生時間を変更する仕様については

`navigator.mediaSession.setPositionState()`にduration, playbackRate, positionからなるオブジェクトを渡すことでページ内での再生位置の変更の反映などをおこなうことができます。これは仕様にも記載されていることではありますが、positionがdurationを超えるとエラーを吐いてnavigator.mediaSessionのmetaデータ等が再定義されるまで適用前のブランクな状態になります。
ここで特に仕様として記載されていない部分ではありますが、例えば再生中の楽曲がアルバムの最後であるから「次に」ボタンを無効化したかったり、動画に関しては次・前ボタンの代わりに10秒スキップボタンにしたかったりする場合があります。既存の機能を無効化しボタンの表示を消すにはactionに無効化したい機能を指定し、handlerにnullを渡すことで無効化することができます。これが仕様に記載のなかった点で実務的に重要な点だと思っています。

ここから少しぼやきに近いことではるのですが、あまりにブラウザやデバイス間で動作に違いがありすぎて、導入しようとすることでコードベースが膨れてしまうのが率直でネガティブな感想です。たとえばiOSではそもそも15.4以前では適用されません。（しかしxcodeシミュレーターでは15.6からしか動かないという謎）また、Android WebViewではそもそもMediaSessionAPI自体一切対応しておらず、WebView内でのメディア再生が一切再生されません。macやwindowsの通知では情報が反映されますが、Firefoxではどうも情報の反映が安定しませんし、Edgeは表示してくれなかったり、なんだかある程度統一されるまでの過渡期だからか、まだしばらく実装したほうがブラウザやデバイス間の統一性が失われてしまう結果に陥りかねなくなっている現状でした。。。

現状ではMediaSessionAPIの恩恵を一番受けているのはWebブラウザで楽曲・動画を再生しているネイティブデバイスだと思います。ブラウザ右上のメディアボタンやOSのメディアコントローラーよりも、ネイティブデバイスのロック画面などで見ることがほとんどだと思います。（自分が携わっているプロジェクトでMediaSessionAPIからのイベントを計測していないのでなんともいえませんが）音楽・動画・ラジオなどを提供するサービスのほとんどがネイティブ版アプリを配信しているだろう状態でMediaSessionAPIへ対応するメリットはUI/UX向上以上のものはありません。UI/UXは重要です。しかしSafariやAndroidのIn App Browserへの順応が不十分な現状では微に入り細を穿っているだけでしかないのが残念なところです。

みんなで使ってブラウザ按分減らしましょう。
UDLD( UniDirectional Link Detection )

UDLDとは単一方向リンクを検出することができるL2プロトコル。

単一方向リンクとは光ケーブルやUTPケーブルに接続された機器がトラフィックを送信できるが受信できないorトラフィックを受信できるが送信できない状況を差す。

送信（TX）ケーブルと、受信（RX）ケーブルがピンポイントに障害が発生しやすい光ファイバーケーブルが接続されるCatalystスイッチのポート上でこの機能を有効化すると効果的

UDLDを有効にすると機器はケーブルが繋がっているかモニタするので単一方向リンクを検出してポートをerror disableにする。

※cisco スイッチだとデフォルトで無効化されている

分かりやすいサイト↓

https://www.infraexpert.com/study/l2control01.html


設定方法

~~~MD:ポート全体の場合
　(config)# udld aggressive | enable | message time message-timer-interval
~~~

~~~MD:特定のポートだけの場合
　(config)# interface interface-id
　(config-if)# udld port [ aggressive ]
~~~

基本的にafressiveモード推奨。
PAMとPAMの設定ファイルについて

●PAM(Pluggable Authentication Modules)
プログラムが使用する様々な認証方式を一元的に管理するためのシステム。
PAMのモジュールは、32ビット版は「/lib/security」ディレクトリ配下に、64ビット版は「/lib64/security」ディレクトリ配下に格納。

通常、PAMの設定ファイルは「/etc/pam.d」ディレクトリにプログラムごとに用意されています。「/etc/pam.d」ディレクトリが存在しない場合は「/etc/pam.conf」ファイルにまとめて記述。

/etc/pam.dディレクトリと/etc/pam.confファイルの両方が存在する場合は、/etc/pam.dディレクトリ内の設定ファイルが利用（/etc/pam.confファイルは無視）。




●PAMの設定ファイル(/etc/pam.d/以下にある個々の設定ファイル）の書式と、それぞれの項目

PAMの設定ファイルの書式
モジュールタイプ コントロール モジュール [引数]

■モジュールタイプ
その行に記述されているモジュールに何をさせるのか、またはどのようなタイプの認証を行うのかを指定。
モジュールタイプには以下の4つ。

・auth パスワードを使用してユーザの認証を行う
・account ユーザのアカウントが有効かどうかをチェック
・password パスワードの設定および変更を行う
・session 認証前後に実行させたい処理を指定する(ログの記録など)


■コントロール
モジュールの実行に成功または失敗した際の処理方法を指定。
以下は主なコントロール。

・requisite モジュールの実行に失敗した場合は、すぐに認証を拒否し、以降の行にあるモジュールを実行しない
・required モジュールの実行に失敗した場合、以降の行にある同じタイプのモジュールを実行してから認証を拒否する
・sufficient モジュールの実行に成功した場合、以前の行のrequiredのモジュールの実行が全て成功していれば、その時点で認証を許可する。失敗した場合は、引き続き移行の行にあるモジュールの実行を続ける。
・optional モジュールの実行に成功または失敗した場合でも、移行の行にあるモジュールの実行を続ける。
・include 指定した設定ファイルを読み込み実行する。


■モジュール [引数]
使用するモジュールを指定。引数がある場合は引数を指定。
PAMのモジュールは、32ビット版は「/lib/security」ディレクトリ配下に、64ビット版は「/lib64/security」ディレクトリ配下に格納。モジュールを絶対パスで記述しない場合は、これらのディレクトリが参照。
以下は主なPAMのモジュール。

・pam_console.so 端末(コンソール)から一般ユーザでログインした際、管理コマンドやデバイスなどの所有権を一時的に一般ユーザ権限に変更
・pam_clacklib.so(RHEL7,CentOS7からはpam_pwquality.so) パスワードの書式を制限し、安全性を向上。
・pam_deny.so 認証を全て拒否
・pam_env.so 環境変数を設定
・pam_ldap.so LDAPを使った認証を行う
・pam_limits.so ユーザが利用するリソースを制限
・pam_listfile.so 任意のファイルを用いてプログラムのアクセス制御を行う
・pam_nologin.so /etc/nologinファイルがある場合、一般ユーザのログインを拒否
・pam_securetty.so rootでのログインを「/etc/securetty」ファイルに記述された端末にのみ許可。
・pam_sss.so SSSDを使った認証を行う。
・pam_unix.so 通常のパスワード認証を行う
・pam_warm.so 認証時やパスワードの変更時にログを記録
・pam_wheel.so root権限のアクセスをwheelグループのメンバーに制限

それぞれのモジュールで指定できるモジュールタイプや引数などは、manコマンドもしくは「/usr/share/doc/pam-バージョン番号/texts」ディレクトリに格納されているドキュメンテーションで確認することが出来ます。
【ルール説明・shared】meta-no-tags

こちらの記事は[Ansible lint Advent Calendar 2022 カレンダー2](https://qiita.com/advent-calendar/2022/ansible-lint) 17日目の記事になります。


今回はルール [meta-no-tags](https://ansible-lint.readthedocs.io/rules/meta-no-tags/) について説明します。

### meta-no-tags

[meta-no-tags](https://ansible-lint.readthedocs.io/rules/meta-no-tags/) は galaxy role のメタデータの`galaxy_tags`でタグに無効な文字列が使用されていないか検証します。タグに利用できる文字列は`小文字のアルファベット`・`数字`になります。

#### 問題のあるコード

```yaml:meta/main.yml
---
# タグに無効な文字列が含まれている
galaxy_info:
  galaxy_tags: [MyTag#1, MyTag&^-]
```

#### 修正されたコード

```yaml:meta/main.yml
---
# タグで使用できる文字列は「小文字のアルファベット」・「数字」のみ
galaxy_info:
  galaxy_tags: [mytag1, mytag2]
```

### 参考サイト

* [meta-no-tags — Ansible Lint Documentation](https://ansible-lint.readthedocs.io/rules/meta-no-tags/)
【Azure DevOps】Azure Pipelinesに設定したymlから環境変数的に値を参照したい

# はじめに

CICDのために欠かせないパイプライン
シークレットとして扱いたい文字列や、可変にしたい設定値などがよくある
ローカルマシンやサーバであれば環境変数として設定できるが、CIを動かしているAgentではどのように可変でプライベートな値を設定するのかを記載する

## 前提

- Pipelines/Libraryを使用した方法を記載する
    - `Variable groups`を使用
    - `Secure files`はここでは取り上げない
- KeyVaultを使用する方法もあるが、やっとことないのでここでは取り上げない
    - 参考
        - [Azure Pipelines で Azure Key Vault シークレットを使用する](https://learn.microsoft.com/ja-jp/azure/devops/pipelines/release/azure-key-vault?view=azure-devops&tabs=yaml)
        - [パイプラインで Azure Key Vault シークレットを使用する](https://learn.microsoft.com/ja-jp/azure/devops/pipelines/release/key-vault-in-own-project?view=azure-devops&tabs=portal)
- 使用するAgentはMicrosoftHosted
- Pipelineは既に作成済みとする

# LibraryにVariable groupsを作成

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647946/4272668c-2281-fd56-9f77-55dae908681a.png)

「+ Variable group」をクリック

## Variablesを定義

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647946/7d23abd8-a107-0ce2-d45a-653484321d54.png)

1. groupに名前をつける
2. 環境変数として使用したい値をkey valueの形式で追加する

# yamlに値を参照するための記述を追加

```ci.yml
# variable groupから環境設定を読み込み
variables:
  - group: qiita-env

steps:
  - bash: echo $(Build.SourceBranch)
    displayName: 'echo working branch name'

  - script: |
      npm test
    displayName: 'unit test'
    env:
      SECRET: $(SECRET) # $(variable)の形式でLibraryに定義した値にアクセスできる
```

# 参照

- [変数グループ内のシークレット変数を参照する](https://learn.microsoft.com/ja-jp/azure/devops/pipelines/process/variables?view=azure-devops&tabs=yaml%2Cbatch#reference-secret-variables-in-variable-groups)

# 余談

## LibraryにアクセスできるPipelineを制限する

デフォルトだと、OrganizationかProjectか分からないが、ほぼ全てのPipelineがLibrary/Variable groupにアクセスできてしまう
セキュリティ向上のためにも、アクセスできるPipelineを限定しておくと良い

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647946/178dca0c-f66d-af60-f8b1-a788c6d45ccb.png)
【ルール説明・shared】meta-no-info

こちらの記事は[Ansible lint Advent Calendar 2022 カレンダー2](https://qiita.com/advent-calendar/2022/ansible-lint) 16日目の記事になります。


今回はルール [meta-no-info](https://ansible-lint.readthedocs.io/rules/meta-no-info/) について説明します。

### meta-no-info

[meta-no-info](https://ansible-lint.readthedocs.io/rules/meta-no-info/) は galaxy role のメタデータ`meta/main.yml`で次の項目が設定されているか検証します。

* author
* description
* license
* min_ansible_version
* platforms

#### 問題のあるコード

メタデータに何も記述されていない

```yaml:meta/main.yml
---
```

#### 修正されたコード

必要な項目を記述する


```yaml:meta/main.yml
---
dependencies: []
galaxy_info:
  author: mamono210
  description: Cowsay installation for Linux.
  license: "license (BSD, MIT)"
  min_ansible_version: 6.0.0
  role_name: cowsay
  platforms:
    - name: EL
      release:
        - 7
        - 8
```

### 参考サイト

* [meta-no-info — Ansible Lint Documentation](https://ansible-lint.readthedocs.io/rules/meta-no-info/)
【react】gsapのScrollTriggerのpinを使うと、別ページ移動時にエラーが出るの解決方法

## エラー内容
```
Unhandled Runtime Error
NotFoundError: Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node.
```


### Before
```tsx
  useEffect(() => {
    const pagesEl = pagesRef?.current
    if (!pagesEl) return
    const pagesWrapperEl = pagesWrapperRef?.current
    if (!pagesWrapperEl) return
    const verticalTextEl = verticalTextRef?.current
    if (!verticalTextEl) return

    const tl = gsap.timeline({
      scrollTrigger: {
        trigger: '#horizontal-scroll',
        start: 'top -40rem',
        end: () => `+=${pagesEl.clientWidth - pagesWrapperEl.clientWidth}`,
        scrub: 0.3,
        pin: true,
        anticipatePin: 1,
        invalidateOnRefresh: true,
      },
    })

    tl.to(pagesEl, {
      x: () => -(pagesEl.clientWidth - pagesWrapperEl.clientWidth),
    })
  }, [])

```

### After
```tsx
  useLayoutEffect(() => {
    const pagesEl = pagesRef?.current
    if (!pagesEl) return
    const pagesWrapperEl = pagesWrapperRef?.current
    if (!pagesWrapperEl) return
    const verticalTextEl = verticalTextRef?.current
    if (!verticalTextEl) return

    const tl = gsap.timeline({
      scrollTrigger: {
        trigger: '#horizontal-scroll',
        start: 'top -40rem',
        end: () => `+=${pagesEl.clientWidth - pagesWrapperEl.clientWidth}`,
        scrub: 0.3,
        pin: true,
        anticipatePin: 1,
        invalidateOnRefresh: true,
      },
    })

    tl.to(pagesEl, {
      x: () => -(pagesEl.clientWidth - pagesWrapperEl.clientWidth),
    })

    return () => {
      ScrollTrigger.killAll()
    }
  }, [])

```

載せるためにちょっとコードを省いたので、へんなところがあったらごめんなさい。
みそは、`useLayoutEffect`に変わってるところ。
その中の最後のクリーンアップ関数で、アンマウント時に`ScrollTrigger`をkillしてます。
これで別ページ遷移時に消えるので、エラーがなくなります。

[参考]
https://greensock.com/forums/topic/28327-scrolltrigger-breaks-react-router/
SublimeText3でbladeファイルのシンタックスハイライトをPHPにする

`hoge.blade.php`というファイル名なのにPHPのファイルシンタックスが反映されず、Plain Text扱いになるのでその対策。

1. 適当なphpファイルをSublimeText3で開く
1. メニュー`基本設定→設定-シンタックス指定`で`PHP.sublime-settings`が開いたのを確認する
1. 以下のコードを貼り付けて保存

```json:PHP.sublime-settings
{
    "extensions":
    [
        "blade.php",
        "php"
    ],
    "tab_size": 4,
    "translate_tabs_to_spaces": true
}
```
【Mac】Operation not permitted 解消法

## はじめに

`ls ~/.Trash`などのコマンドを実行すると以下のようにエラーが発生します。  

```terminal
$ ls ~/.Trash
ls: /Users/username/.Trash: Operation not permitted
```

このエラーの解消方法について記載します。  

## 解消法

ターミナルのアプリにフルディスクアクセスを許可することで解消できます。  

「システム設定」→「プライバシーとセキュリティ」→「フルディスクアクセス」と移動します。  
ターミナルアプリの左側のトグルをクリックして許可します。  

![スクリーンショット 2023-01-06 23.44.13のコピー.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2342443/c264c61f-b5d5-d8ab-2283-7330fe704897.png)
【Docker】Dockerのメリット



## Dockerのメリット

Dockerのメリットは、開発環境を簡単に共有することができることです。
複数人で開発を行うときでもDockerfileなどのファイルを共有するだけで、すぐに共通の環境を構築することができます。（仮想環境の構築手順をファイルで管理することができるため、誰が実行しても同じ結果が得られる。）

個々のコンピュータに直接ソフトウェアをインストールすることもなく、必要なときに必要な分だけコンテナを立てて開発環境を構築できます。


## コンテナとは

>Simply put, a container is another process on your machine that has been isolated from all other processes on the host machine. That isolation leverages kernel namespaces and cgroups, features that have been in Linux for a long time. Docker has worked to make these capabilities approachable and easy to use.

コンテナは、「OS上で実行されている隔離されたプロセス」です。
プロセスとは実行されているプログラムのことです。
（仮想マシンではなく、特殊なプロセス。ゲストOSを使用することもなく、コンテナはホストのOSを使用する。）

Dockerのメリットとして「開発環境を簡単に共有できる」と書きましたが、コンテナはプロセスなので、imageを共有することでプロセスを共有できることになります。つまり、コマンド1つで完成したアプリケーションを個々のPC上で実行できることになります。（プログラムを開発する場合は、コードを共有してDockerfile等からコンテナを立ち上げた上でコードを編集していくことになる。しかし、アプリケーションの共有だけできればいいのであればimageをpullしてくるだけで可能であり、これはコマンド1つで実現できる。）


コンテナ作成に必要な作業をソフトウェア（コマンドラインツール）としてまとめてくれたのが
Dockerです。

原則として１つのコンテナで１つのソフトウェアが動くようにします。
１つ１つコンテナを立てるのは手間なため、多くの場合`docker-compose`を使って一気に複数のコンテナを立てて環境構築します。

## まとめ

Dockerのメリットは、開発環境を簡単に共有できることです。
自分のコンピュータにあれこれ直接ソフトウェアをインストールする必要もないので、自分のPC環境が綺麗な状態で色々なコンテナを立てて試すこともできます。


コンテナは、隔離されたプロセスです。

コンテナを作成するためのソフトウェア（コマンドラインツール）がDockerです。
Reactで動的にSVGの色を変える方法

Reactで動的にSVGの色を変える方法を紹介する。

# SVGのテキストを返す関数を作る
色を入力したら、SVGのテキストを返す関数を作る。
SVGのファイルを開いて、コピペし、色が設定してあるところを変えられるようにする。

```ts
type Color = {
    title: string;
    hex: string;
};
const getSvgText =  (colors: Color[]) =>
{
  return `<svg ~~省略~~ text-rendering="geometricPrecision" style="background-color:${colors[0].hex}">
<style>~~省略~~</style>
<defs><linearGradient id="eALyGup1cVD2-fill" x1="0" y1="0.5" x2="1" y2="0.5" spreadMethod="pad" gradientUnits="objectBoundingBox" gradientTransform="translate(0 0)"><stop id="eALyGup1cVD2-fill-0" offset="0%" stop-color="${colors[1].hex}"/><stop id="eALyGup1cVD2-fill-1" offset="100%" stop-color="${colors[2].hex}"/></linearGradient></defs><g id="eALyGup1cVD2_to" transform="translate(640,360)"><rect width="2492.099859" height="2223.719874" rx="0" ry="0" transform="scale(0.820545,1) translate(-1246.049942,-1111.859937)" fill="url(#eALyGup1cVD2-fill)" stroke-width="0"/></g></svg>
`;
}
```

# useStateで色を管理する

```ts
  const [ colors, setColors ] = useState<Color[]>( [] );
```

# 色を入力できるようにする
react-colorfulというライブラリを用いて色入力できるようにした。スタイルが最初からいい感じになっているので楽だ。

![スクリーンショット 2023-01-06 16.27.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/188304/a2e413ff-bc62-0479-aef1-45b7ab1b452e.png)

https://omgovich.github.io/react-colorful/


# SVGを表示する

最初に作った`getSvgText`を利用することで動的にSVGを表示できる。
```tsx
<Box
    borderRadius="50px"
    bgColor="hsla(0,0%,0%,0)"
    bgImage={`url("data:image/svg+xml;base64,${Buffer.from(
      getSvgText(colors)
    ).toString("base64")}")`}
    bgRepeat="no-repeat"
    bgSize="cover"
    bgPosition="center"
    pos="relative"
></Box>
```

※ Chakra UIを使用

# まとめ

この4ステップでSVGの色を動的に変えられるようになる。



# SVGグラデーションアニメーターの開発

SVGでグラデーションのアニメーションを動かした経験を生かして、SVGグラデーションアニメーターを作りました。
色を変えるだけで、簡単にSVGグラデーションアニメーションを作れます。
ぜひ、ご利用ください！

https://technologies.anharu.me/tools/gradient-svg

作れるアニメーション例↓
![wave-gradiation (1).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/188304/12970cc0-fbf1-f0cb-d568-1279b0ba9e9a.gif)
Amazon Linux 2 に npm をインストールするときに libuv パッケージのバージョンエラーが出た


## 状況
[公式の Node インストール手順](https://docs.aws.amazon.com/ja_jp/sdk-for-javascript/v2/developer-guide/setting-up-node-on-ec2-instance.html)に従えば npm も同時にインストールされるはずだが、nvm で別の node バージョンに切り替えたら node のみ入っていて npm が入っていない状況になった。どうやってこういう状況になったのかは忘れた。

```bash
$ nvm --version
0.34.0

$ node -v
v17.8.0

$ npm -v
bash: npm: command not found
```

## npm インストール時のエラー

```sh
$ sudo yum install npm

...

--> Finished Dependency Resolution
Error: Package: 1:nodejs-16.18.1-3.el7.x86_64 (epel)
           Requires: libuv >= 1:1.43.0
           Installed: 1:libuv-1.39.0-1.amzn2.x86_64 (@amzn2-core)
               libuv = 1:1.39.0-1.amzn2
           Available: 1:libuv-1.23.2-1.amzn2.0.2.i686 (amzn2-core)
               libuv = 1:1.23.2-1.amzn2.0.2
 You could try using --skip-broken to work around the problem
 You could try running: rpm -Va --nofiles --nodigest
```

## 原因と対処法
`sudo yum list libuv` でみると、epel にも libuv はあるが amzn2-core の方が優先度が高いらしい。

[stackoverflowのお兄さんの発言](https://stackoverflow.com/questions/71043534/nodejs-16-dependency-issue-with-libuv)を試したら npm がインストールできた。

>sudo yum remove libuv -y    
sudo yum install libuv --disableplugin=priorities

`--disableplugin=priorities` がないとエラーが出ていても優先度高いリポジトリから持ってきてしまうらしい。
デフォルトでインストールされているパッケージのバージョンを無理やり変えることになるので他に影響がないとは言い切れないが。

## おまけ (Node バージョンが古い理由)
Node バージョン 18, 19 を入れようとすると別のエラーになったため 17 にしている。

```sh
$ nvm install --lts
Installing latest LTS version.
v18.13.0 is already installed.
node: /lib64/libm.so.6: version `GLIBC_2.27' not found (required by node)
node: /lib64/libc.so.6: version `GLIBC_2.28' not found (required by node)
nvm is not compatible with the npm config "prefix" option: currently set to ""
```

document に以下の記載がある。現時点で document の nvm のインストールコマンドのバージョンは 0.34.0 だが、リンクからレポジトリ見ると最新は 0.39.3 だったので、この辺の問題な気がする。

> Warning
AWS does not control the following code. Before you run it, be sure to verify its authenticity and integrity. More information about this code can be found in the nvm GitHub repository.

> curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.34.0/install.sh | bash

しかし実際にnvmを0.39.3にしても結局 node 18以降はうまくいかなかった(npmが上記のエラーで動かない)

```sh
$ npm -v
node: /lib64/libm.so.6: version `GLIBC_2.27' not found (required by node)
node: /lib64/libc.so.6: version `GLIBC_2.28' not found (required by node)
```
GLIBC は Amazon Linux 2 には 2.26 までしか入っていないからだそう。
気付けば後継の Amazon Linux 2022 (AL2022) が出ており、こちらなら新しい GLIBC が入っていそうな雰囲気を感じたので、node 18以降を入れるならこちらを試すのがいいと思う。

[Amazon Linux 2022 に関するよくある質問](https://aws.amazon.com/jp/linux/amazon-linux-2022/faqs/)
> Q: Amazon Linux 2022 とは何ですか?
A: Amazon Linux 2022 (AL2022) は、rpmベースの汎用Linuxディストリビューションで、Amazon Linux 2の後継にあたります。
Arweaveに200年残るファイルをデプロイしてみる

# Arweaveとは？

https://arwiki.wiki/#/en/main

> Arweave is a protocol that allows you to store data permanently, sustainably, with a single upfront fee.

>Arweaveは半永久的・持続的なストレージの提供を可能としたプロトコル。
保存時にのみ一回だけトークンを前払いすればよい。

最低200年保存できるということがうたわれている。
IPFSと違い、前払い一回でよく、永続化に対して回答を持っているのがイケてる感じです。
今回は手始めに、お試しでウォレット&トークンの取得とファイルのデプロイを試してみます。

#### cf. IPFS & Filecoin
https://ipfs-book.decentralized-web.jp/what_is_ipfs/


## ウォレット作成&トークンの取得

#### ひつようなもの
- Twitterアカウント(bot認定されないもの)&ツイート一回
    - Twitter連携で一回だけ無料のトークンがもらえます。
    - bot判定の基準はよくわからない。取得後はツイ消ししてよいので、一番使ってるものがよいかも。
- 保存したいファイル


#### Twitter連携&認証
下記のページからArweaveのトークン(AR)と新しいウォレットが取得できます。

https://faucet.arweave.net/

プライバシーポリシーに同意のチェックを入れて次へ。
もらえる0.02ARは0.13USDの価値があるとか書いてあります。(2023/1/3)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317088/616ac999-37a4-8589-71b6-012df74b1137.png)


新しいウォレットが生成されるので、ダウンロードしておきます。
Twitter連携されるとこのウォレットにトークンが送信されます。
後ほど使用しますので、ファイルは控えておきます。
※紛失すると二度と復元できないので取扱注意！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317088/98ac49b3-39ed-ea13-c3c9-b7988b175744.png)

Twitterアカウントからツイート後、botチェックが終了すれば完了です。

## ウォレットのインポート&ファイルアップロード

ブラウザウォレットのページに遷移します。
https://arweave.app/welcome

して、左下の+を押す
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317088/2860e21b-9366-53bd-b4c1-fbebcdfc1338.png)

先ほどダウンロードしておいたkeyファイルからウォレットをインポートします。
真ん中のアイコンをクリックしてダウンロードしておいたファイルを読み込み。
インポートが正常に完了したら、左上にアイコンが出るのでクリック。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317088/16a791fc-8d4f-bb96-695d-3acf566eb631.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317088/fc40ac90-d196-569e-693b-0e0717a5ab95.png)
Transactionsの履歴を見てみるとちゃんと0.02AR受け取れています！
次に、このトークンを使ってファイルをデプロイしてみます。

今回は試しに私が撮影したラーメン二郎の写真をデプロイすることにします。
同じくウォレットのメニュー画面からsendをクリックし、
トークン・データの送受信画面を出します。
Dataの部分にアップロードしたい画像をドラック。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317088/1742eefd-53ce-232b-27cb-861ab861ebbb.png)
左下にサイズと手数料が計算されます。(安い！？)
Submitをクリック。

TransactionのSendタブからデプロイ進捗が確認できます。
しばらくすれば完了！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317088/2cc7f241-6749-7629-70a8-99481fb8fec6.png)

↓アップロードした最低200年のこるらしいラーメン二郎の画像。
https://arweave.net/OLgxJ9RhrXh6PR5DvD6hau7Ib0-7VWuQJVR8o8JuTYs

## さいごに&次の学習に向けて
思ったより手軽に分散ストレージにファイルをアップロードできた。
NFTファイルの保存先としての利用によさそう。

技術的な深堀りについては下記のyellow paperを参考。

https://www.arweave.org/yellow-paper.pdf

ファイルを分散して保持できるのはわかったけどどうやってホスティングしてんねん！
ということが気になってwikiを読んでいたが、Meson NetworkとAmplifyという技術があるもよう。

https://meson.network/

https://www.amplify.host/

これらを駆使して
permawebと呼ばれている分散webの仕組みがあるとのこと、Arweaveのwikiもデプロイされている。

https://arwiki.wiki/#/en/the-permaweb

https://www.arweave.org/technology#permaweb

no more 404。web3すばらしい。
[Python]四捨五入

毎回調べちゃうよね、この処理。
めんどくさいのでまとめました。

# 四捨五入
**Decimal.quantize()** を使います。

## Decimalってなに？
小数を表現するための型の一つです。
ふだんは`float`でも構わないのですが、この子は小数部の桁があまりにも膨れてしまった場合、正確に表現できなくなってしまうという短所を持ちます。

これを解決するため、**一般的な手計算と同じ数値を返すことのできる小数型**としてデザインされたのがDecimalになります。

## 使ってみる
Decimal型はモジュールなので、まずはインポートします。
```py
>>> from decimal import *
```

そしたら試しに四捨五入させてみましょう。
今回は`1.2345`を小数点第4位で四捨五入してみます。

```py
>>> d = Decimal("1.2345") # 必ず文字列で渡す
>>> d = d.quantize(Decimal(".001"), rounding=ROUND_HALF_UP)
>>> print(d)
1.235
```

こんな感じです。
ちゃんと小数点第4位が四捨五入され、切り上げられて`1.235`になっているのが確認できます。

手順としては、
1. `Decimal(number)`でDecimal型のオブジェクトを作る
1. `Decimal.quantize(有効桁、rounding=ROUND_HULF_UP)`で四捨五入

となります。

:::note warn
似たような処理に`round`関数がありますが、こちらは**偶数丸め**を行うものです。
一般的には四捨五入と同じふるまいを見せますが、`1.5`や`1.25`のように **最終桁が5である（切り上げと切り捨ての結果が同様に近い）** 場合、**結果の最終桁が偶数となる**ような処理を行います。

```py
>>> round(0.5)
0

>>> round(1.25, 1)
1.2
```

コメントにて修正いただきました。ありがとうございます:clap:
:::

### 有効桁を指定する
すこしわかりにくいですが、有効桁の指定には**Decimal型のオブジェクトを渡します**。
有効桁の最後の桁に`1`を置き、それ以外の桁を`0`で埋めることで有効桁を表現します。

### 丸め形式について
第二引数に`rounding`というプロパティを指定していますが、これは**丸め形式**というものです。

今回用いた`ROUND_HALF_UP`は、`0.5`のような閾値の場合**切り上げる**という指定になります。
先述した`round()`関数と同じ挙動をしたい場合は`ROUNDING_HALF_EVEN`を指定してあげましょう。
条件演算子(PHP)

条件演算子のサンプルプログラムです。
全体プログラムです

```HTML
    <!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>関数テスト(引数とデフォルト)</title>
  <style>
      .a {
        font-size:18px;
        color:#2F7;
        font-weight: bold;
      }
     
  </style>  
</head>
<body>
  <?php
    function calc($a,$b,$c) {
      $total = $a + $b + $c;
      // 条件演算子($total>=0ならそのまま、$total<0なら0を返す)
      return $total >= 0 ? $total : 0;
    }
    function show($num){
      echo "<p class=a>$num</p>";
    }
    $a = calc(40,200,70);
    show($a);
    $b = calc(-400,200,70);
    show($b);

  ?>

</body>
</html>
```

PHP部分です
```PHP
    <?php
    function calc($a,$b,$c) {
      $total = $a + $b + $c;
      // 条件演算子($total>=0ならそのまま、$total<0なら0を返す)
      return $total >= 0 ? $total : 0;
    }
    function show($num){
      echo "<p class=a>$num</p>";
    }
    $a = calc(40,200,70);
    show($a);
    $b = calc(-400,200,70);
    show($b);

  ?>
```

条件演算子の部分です。

```PHP
return $total >= 0 ? $total : 0;
```
条件演算子(三項演算子)は
```PHP
条件式 ? True : False
```
で書きます。可読性の問題があるので使う場合を考えるとよいでしょう。
[Node.js] Expressのタイムアウト時間を設定した上で、408レスポンスを返す

# はじめに

https://qiita.com/YOS0602/items/5a9d23037d04aacf8db6

↑自分が過去に書いた記事では、`Express`でのタイムアウト時間設定を記載しました
しかし、この設定だけだとHTTPレスポンスが返らないので、ちゃんと相手がいるシステムだと困るなあと思い、レスポンスを返せるように実装してみました

## `curl`やPostmanでtimeout設定だけしたExpressサーバのAPIを叩いてみた

```terminal
% curl http://localhost:3000/time
curl: (52) Empty reply from server
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647946/bd4476bf-1f1c-1056-ed59-266120bd69ac.png)

レスポンスがクライアントに返っていないことがわかります
では、`408`ステータスが返却されるようにしてみましょう↓

# 実装

タイムアウトを設定し、`408`をレスポンスする関数を作成

```timeoutSeetting.ts
import { Response } from 'express'

const TIMEOUT_MS = 1000 * 60 // 60sec

const setResponseTimeOut = (res: Response) => {
  res.setTimeout(TIMEOUT_MS, () => {
    // 408 Request Timeoutの定義に従いConnectionをcloseする
    res.setHeader('Connection', 'close')
    res.sendStatus(408)
  })
}
```

expressのルーティング定義から作成した関数を最初にコールしておく

```app.ts
app.get('/time', async (req: Request, res: Response) => {
  setResponseTimeOut(res)

  // do something...

  // 既に408を返している場合はレスポンスしない
  if (!res.headersSent) res.sendStatus(200)
})
```

# 試してみる

めんどくさいので1ファイルにどかっと書きました

```app.ts
// タイムアウト時間は1秒と設定
const TIMEOUT_MS = 1000
const setResponseTimeOut = (res: Response, next: NextFunction) => {
  res.setTimeout(TIMEOUT_MS, () => {
    // 408 Request Timeoutの定義に従いConnectionをcloseする
    res.setHeader('Connection', 'close')
    console.log(`${TIMEOUT_MS}ms経過 Timeoutレスポンスを返却します`)
    console.timeEnd('/time')
    res.sendStatus(408)
  })
}

app.get('/time', async (req: Request, res: Response, next: NextFunction) => {
  console.time('/time')
  setResponseTimeOut(res, next)
  // 3秒かかる処理
  await new Promise<void>((resolve) => {
    setTimeout(() => {
      resolve()
    }, 3000)
  })
  await new Promise<void>((resolve) => {
    setTimeout(() => {
      console.log('408を返した後も処理は続くので注意!')
      resolve()
    }, 1000)
  })
  if (!res.headersSent) res.sendStatus(200)
})
// サーバリッスンなど省略
```

## APIを叩くと...

```terminal
1000ms経過 Timeoutレスポンスを返却します
/time: 1.003s
408を返した後も処理は続くので注意!
```

設定したタイムアウト時間である1秒経過後に`408`がレスポンスされています

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647946/bbd677d8-8681-0352-5a84-5c0523813734.png)

# 注意点

ターミナルにも出力していますが、`408`が返った後も処理自体は継続して動いているので注意が必要です
`408`を受け取ったクライアント側が`500`エラーなんかと同じように扱って、リトライをしまくると、冪等性のないAPIだった場合データのダブりがたくさん出ます
Rstudio内でraw CELファイルを入手する。遺伝子発現データを入手する。

ー課題１ー
・下記のBioconductorパッケージをインストールし、vignetteに目を通したのち、着目したプロジェクトに関するメタデータやraw CELファイルをゲットせよ。　
（私は今回"GSE9311"を用いている。 Rstudioで実行している。）

```{r}
#入手したいGEO IDを指定
>practice <- "GSE9311"
```
```{r}
#パッケージの読み込み
>library(GEOquery)
>library(Biobase)
>library(data.table)
```
```{r}
#指定したGEO IDのサプリメントファイルをダウンロード
>getGEOSuppFiles(practice)
```
```{r}
#GSE9311では以下の8つのサンプルのデータファイルが存在していたので、それらの解凍を行う。
（GSM237280,GSM237281,GSM237282,GSM237283,GSM237292,GSM237293,GSM237294,GSM237295)
>gunzip("GSM237280.CEL.gz", remove=FALSE)
```
・以上の手順で、使っているパソコン内の作業フォルダに目的としているデータが保存できたと思う。
＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿
”不明な点”
・今回の課題で問われている"メタデータ"と"raw CELファイル"の意味
__________________________________________________________________________
・見よう見まね(独学？)で行っているので、よろしければ間違いのご指摘よろしくお願いします。
初めてのAWS-備忘録-

# AWSを初めてつかってみたので、便利な情報を備忘録でまとめる

- 随時更新中
- GUIの説明は少なめなので、AWSコンソールに付いての情報は、あまりありません。
- Python(Boto3）を使用して、イジイジするための情報を主にまとめています。
- 実施環境
    - Windows11

## ルートユーザとIAMユーザの違い
ルートユーザとIAMユーザの使い分けは、必須と聞いたが、違いがよくわかってなかったので、調べてざっくりと図示してみた。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407942/46ad5e9f-14b9-78d2-b24e-fc4c209501a5.png)

## リンク集

- ### [リージョン一覧](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Concepts.RegionsAndAvailabilityZones.html)
　みんな使ってる東京は、**ap-northeast-1**
- ### [AWS環境の設定 入門ガイド](https://aws.amazon.com/jp/getting-started/guides/setup-environment/)
    Cloud9の作成コマンドを適当に叩いてみたら、AWSのコンソールでも作成ちゃんと作成されてた。
    ```aws:bash
    aws cloud9 create-environment-ec2 --name getting-started --description "Getting started with AWS Cloud9." --instance-type t3.micro --automatic-stop-time-minutes 60
    ```

    また、載ってたCloud9の削除コマンドを実行してみたら、AWSのコンソール画面でも早速、削除が始まった。

    ```aws:bash
    aws cloud9 delete-environment --region MY-REGION --environment-id MY-ENVIRONMENT-ID
    ```
    （MY-REGIONとMY-ENVIRONMENT-IDは、自身の環境に置き換えてください。）
- ### [AWS SQS](https://www.fenet.jp/aws/column/aws-beginner/299/#%E3%80%8C%E3%83%A1%E3%83%83%E3%82%BB%E3%83%BC%E3%82%B8%E3%80%8D%E3%81%A8%E3%81%AF%EF%BC%9F)
    - Simple Queue Serviceの略。
    - 「Full managed」の「メッセージキューイング（Message Queueing（MQ））」サービスを指す。
    - [Boto3におけるSQS公式リファレンス](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/sqs.html#SQS.Client.delete_queue)
        とりあえず、SQS消したいとき（※当然だが、メッセージも消えるので、消すときは注意）
        ```python:delete_sqs
        import boto3
        client = boto3.client('sqs')
        client.delete_queue(QueueUrl='消したいSQSのURL')
        ```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407942/164c8917-5b51-47ef-7de9-55736f7e6f05.png)

- ### [venv](https://qiita.com/fiftystorm36/items/b2fd47cf32c7694adc2e)
    venvは、あまり使ってこなかったので、初歩的な使い方Qiita記事を残しておく。

- ### CDK
    CDKを利用して、色々やってみたので、よく使うコマンドをメモ。
    ```bash:synth
    cdk synth
    ```
    ```bash:bootstrap
    cdk bootstrap
    # cdk bootstrap aws://USERID/REGION　も使う
    ```
    ```bash:deploy
    cdk deploy
    ```

## 次に試してみたいこと

- [AWS CDK の使用を開始する 入門ガイド](https://aws.amazon.com/jp/getting-started/guides/setup-cdk/)
- [AWS での Python](https://aws.amazon.com/jp/developer/language/python/?nc1=f_dr)
- [Amazon CloudWatch examples](https://boto3.amazonaws.com/v1/documentation/api/latest/guide/cw-examples.html)
環境変数とは


## 環境変数とは

環境変数とはOSがもつ変数です。

OSがもっているので、そのコンピュータ上で動くその他のアプリ（ソフトウェア）から参照することができます。


環境変数を設定しておくことで、秘密にしておくべき情報をより安全にアプリケーションから共通で参照することができるわけです。



## まとめ

環境変数とは、OSがもつ変数です。

そのコンピュータ上で動く全てのソフトウェアから参照することができます。
Org-mode (Emacs) で #+begin_src や #+end_src などを楽に入力できるようにする方法



# はじめに

Emacs の Org-mode を利用する際に `#+begin_src ...  #+end_src` みたいなブロックを書くことがよくあるのですが、これを楽に入力する設定などを書きます。


# `#+begin_src ...  #+end_src` みたいなやつの名前

`#+begin_src ...  #+end_src` みたいなものをorg-mode 的には **"structure-template"** と呼ぶらしいです。
こういう名前がわからないとコマンド名などを探すのに苦労します(した)。


# structure-template の入力方法


## org-insert-structure-template

structure-template を手打ちするのはとても厳しいので当然これらを入力する方法として、 `org-insert-structure-template` というコマンドが用意されています。
このコマンドは、デフォルトでは `C-c C-,` に割り当てられています。

ただ、GUI ではなくターミナルエミュレーター内で Emacs を動かしている場合などは、ターミナルエミュレータが `C-,` をアプリケーションに送信できないということがあるらしく、このキーバインドが使えなかったりします(これは、ASCII に C-, に相当する制御文字が存在しないため、ターミナルエミュレータがキーイベントを無視するか `,` だけ送信してしまうためのようです)。
あと若干タイプ数が多くて面倒です。


## org-tempo

今回の記事の本命です。
org-tempo というモジュールを有効にすると、もっと楽に入力できます。

手順 (`#+begin_src ...  #+end_src` の場合)

1.  バッファ上に `<s` と入力 (コマンドではない)
2.  `<s` の右側にカーソルがある状態で、TAB (or C-i) を入力

これで `<s` が `#+begin_src ...  #+end_src` に変換される形で入力されます。
他にも `<e` は `#+begin_example ... #+end_example` に変換されます。


### org-tempo の設定方法

org-tempo はデフォルトでは有効になっていないですが、以下の手順で有効にできます。

1.  M-x customize-option org-modules を実行。 org-tempo を選択
2.  init.el など設定ファイルに `(require 'org-tempo)` を追記


# 終わりに

org-mode を使うときに設定しておくと便利な org-tempo の話でした。
カスタマイズ方法などは調べてみてください(丸投げ)。


# 余談

僕の記憶が正しければ、 昔は org-tempo の入力方法はデフォルトで有効になっていたと思うのですが、いつの間にか使えなくなっていて大変困ったので調べって設定したという経緯があります。
<https://twitter.com/deep_tkkn/status/1578728491025334273>