OmniAICreator/Qiita-1.07M huggingface dataset:
MacのWordで、⌘Pで変更・コメントを含まない文書を印刷する(Ventura対応版)

Microsoft Word for Macの「校閲」機能で変更履歴機能やコメント機能を使っている場合、表示が「変更履歴／コメントなし」以外だと、デフォルトの印刷対象が「変更とコメントの内容を含む文書」になります。![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192970/4fdb2199-0224-80ee-06de-e3bc9c502e91.png)
私は依然、プリントダイアログを開いて印刷対象を「文書」にするという一連の操作をAppleScriptで自動化するサービスに⌘Pを割り当てる方法で、一発で文書本体の印刷を行えるようにしました。
→ [MacのWordで、⌘Pで変更・コメントを含まない文書を印刷する - Qiita](https://qiita.com/kasanma3104/items/e0721da4ea4104f62e46)
ただ、このスクリプトはmacOS 13 Venturaではプリントダイアログの形式が変わってしまい、うまく動作しません。なんかWord用の設定部分をうまいこと展開できないのです。
![PrintDialogOnVentura.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192970/fab58d8d-f830-6f87-8f84-00b9b307dd2a.png)
で、前回のサービスを作っていて最後に気づいたのが、編集画面で変更履歴・コメントを表示していなければ印刷対象も「文書」になる、ということ。 
そんで、変更履歴・コメントの表示・非表示は、メニューバーの「表示＞コメントと注釈」のON/OFFで切り替えられます。 
![CommandsOnMenuBar.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192970/f41a3d27-98cc-5ad0-df76-ab0f1b5f283e.png)
てことは、印刷開始時にメニューバーの「コメントと注釈」をOFFにして、もし元々これがONだったならプリントダイアログが閉じた時に再度ONにすれば良さそうです。 
たとえばこんな感じのスクリプトで。 
```AppleScript
(* Wordで印刷する際、デフォルトの印刷対象は編集画面での変更履歴等表示状況に依存するので、変更履歴等を非表示にしてからプリントダイアログを開く *)
set isShowing to false

tell application "Microsoft Word"
	activate
end tell

tell application "System Events"
	tell process "Microsoft Word"
		tell menu item "コメントと注釈" of menu 1 of menu bar item "表示" of menu bar 1
			if value of attribute "AXMenuItemMarkChar" is "✓" then (* コメント表示中なら *)
				set isShowing to true
				pick (* "コメントと注釈"を選択し、コメントを非表示にする。 *)
			end if
		end tell
		
		(* プリントダイアログを表示 *)
		pick menu item "プリント..." of menu "ファイル" of menu bar item "ファイル" of menu bar 1
		
		(* もし元々コメント表示してたら、プリントダイアログを閉じた時にコメント表示に戻す *)
		if isShowing then
			repeat while (exists window "プリント")
				delay 1
			end repeat
			pick menu item "コメントと注釈" of menu 1 of menu bar item "表示" of menu bar 1
		end if
		
	end tell
end tell
```
余談ですが、どうもON/OFFのあるメニュー項目の現在のステータスは、`menu item`の`property`では取得できないらしく、`value of attribute "AXMenuItemMarkChar" of menu item`の値が`"✓"`か否かで判定する力技を使うようです。
（参考：https://stackoverflow.com/questions/69030/in-applescript-how-can-i-find-out-if-a-menu-item-is-selected-focused )
で、このスクリプトを内容とする「サービス」をAutomatorで作ります。 
![AutomatorService.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192970/4a5985ff-0de6-dcc4-eab2-e62c040d8081.png)
あとはこの「サービス」のキーボードショートカットを⌘Pに、Word標準の「プリント...」のキーボードショートカットを適当に⌥⌘Pあたりに設定すればOKです。ちなみにMontrey以前ではどちらも「システム設定＞キーボード＞ショートカット＞アプリケーション」から設定していたのですが、Venturaでは「サービス」へのキーボードショートカットは、「システム設定＞キーボード＞キーボードショートカット＞サービス＞一般」からに変わったようです。（Word標準の「プリント...」は従来通り） 
![SettingServiceShortcutOnVentura.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192970/d4390c89-4b5c-7d99-4e31-584686e723cc.png)
なお、実際に動作させるには「システム設定＞プライバシーとセキュリティ＞アクセシビリティ」でMicrosoft Wordに「コンピュータの制御を許可」する必要があるのは従来通り。 
ということで。プリントダイアログが閉じるまでServiceが無限ループして待つというちょっとした強引さはあるものの、プリントダイアログを開いてからぽちぽちクリックするよりもスマートな方法で、文書のみの印刷が可能になりました。たぶん古いバージョンのmacOSでも動くと思います。Enjoy!
TypeError: value too large (max 65536 bytes)

65537以上は保存できませんのでよろしくです。回避するにはデータ圧縮をお試しを

https://qiita.com/7mpy/items/b88b01bf73ade445517f

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/435735/1e641307-3bb1-1431-c08a-6720af11fa06.png)



```typescript
kv.set([""], crypto.getRandomValues(new Uint8Array(65537)));
```


https://gist.github.com/alexdiliberto/39a4ad0453310d0a69ce
Python3: SQL Server のデータを削除 (Delete)

## プログラム

```py:mssql_delete.py
#! /usr/bin/python
#
#	mssql_delete.py
#
#					May/08/2023
#
#
# --------------------------------------------------------
import	sys
import	pymssql
#
# --------------------------------------------------------
def	sql_delete_proc(cursor_aa,key_in):
	print("*** sql_delete_proc ***")
#
	sql_str = sql_delete_string_gen_proc(key_in)
	cursor_aa.execute(sql_str)
# --------------------------------------------------------
def	sql_delete_string_gen_proc(key_in):
	sql_str=u"delete from cities where id = '%s'" % key_in
	print(sql_str)
#
	return	sql_str
# --------------------------------------------------------
sys.stderr.write("*** 開始 ***\n")
key_in = sys.argv[1]
print ("%s" % key_in)
#
#server_mssql = 'localhost'
server_mssql = '172.17.0.2'
user_mssql = 'sa'
password_mssql = 'Password1'
#
conn = pymssql.connect \
	(host=server_mssql,user=user_mssql,password=password_mssql,database='city')
#
cursor = conn.cursor()
#
sql_delete_proc(cursor,key_in)
conn.commit()
#
cursor.close()
conn.close()
sys.stderr.write("*** 終了 ***\n")
#
# --------------------------------------------------------
```

## 実行コマンド

```bash
./mssql_delete.py t1071
```

## 確認したバージョン

```text
$ python --version
Python 3.11.3
```
Python3: SQL Server のデータを更新 (Update)

## プログラム

```py:mssql_update.py
#! /usr/bin/python
#
#	mssql_update.py
#
#					May/08/2023
#
#
# --------------------------------------------------------
import	sys
import	string
import pymssql
from time import localtime,strftime
#
#
# --------------------------------------------------------
def	sql_update_proc(cursor_aa,id_in,ipop_in):
#
	sql_str = sql_update_string_gen_proc(id_in,ipop_in)
	cursor_aa.execute(sql_str)
# --------------------------------------------------------
def	sql_update_string_gen_proc(id_in,ipop_in):
	date_mod = strftime ("%Y-%m-%d",localtime())
	ft_aa="update cities set population = %d, " % ipop_in
	ft_bb="date_mod='%s' " % date_mod
	ft_cc="where id = '%s'" % id_in
	sql_str=ft_aa + ft_bb + ft_cc
	print(sql_str)
#
	return	sql_str
# --------------------------------------------------------
sys.stderr.write("*** 開始 ***\n")
key_in = sys.argv[1]
population_in = int(sys.argv[2])
print("%s\t%d" % (key_in, population_in))
#
#server_mssql = 'localhost'
server_mssql = '172.17.0.2'
user_mssql = 'sa'
password_mssql = 'Password1'
#
conn = pymssql.connect \
	(host=server_mssql,user=user_mssql,password=password_mssql,database='city')
#
cursor = conn.cursor()
#
sql_update_proc(cursor,key_in,population_in)
conn.commit()
#
cursor.close()
conn.close()
sys.stderr.write("*** 終了 ***\n")
#
# --------------------------------------------------------
```

## 実行コマンド

```bash
./mssql_update.py t1075 81254300
```

## 確認したバージョン

```text
$ python --version
Python 3.11.3
```
OpenLDAPでadminのアカウントを確認する

OpenLDAPの初期設定で詰まった部分の備忘録です。

## 前置き
LDAPの初期設定を行う最中Adminのアカウントってどこにいるんだ...?となりました。
様々なサイトを参照するとLDAPのアカウントの検索にはldapsearchを用いると出てきます。そこでは
```
ldapsearch -x -H ldaps://example.com -D "cn=admin,dc=example,dc=com" -w "password" -b "dc=example,dc=com" "(objectClass=*)"
```
という形で-Dの部分でバインドDNを指定するのですが、いざ 
```
ldapsearch -x -H ldaps://example.com -D "cn=admin,dc=example,dc=com" -w "password" -b "dc=example,dc=com" "(cn=admin)"
```
とするとadminユーザーは出てこないです。　

##  adminユーザーが出てこない理由 
adminが出てこない理由はそもそもこのバインドDNにadminが格納されていないからです。

こちらの[サイト](https://int128.hatenablog.com/entry/20130726/1374851911)を参照すると
`
dc=example,dc=com の管理者アカウントは olcDatabase={1}hdb,cn=config の olcRootDN 属性と olcRootPW 属性に格納されている。これは cn=config ツリーに入っているので、通常のアカウントからは見えない。
`
となっています。

## 確認方法
ということでcn=configを見に行きたいわけですが、これはOpenLDAPをホストしているマシンからじゃないと見ることができません。ですので、ホストしているマシンのcli(今回筆者の環境では全てDocker上でホスティングを行っていたので`docker exec -it hogehoge bash`でcliに侵入した)を利用して、
```
ldapsearch -Y EXTERNAL -H ldapi:/// -b cn=config
```
と入力する(ldapi:///は自分でホスティングしてるやつのことを指す)とどこかに 
```
# {1}mdb, config
dn: olcDatabase={1}mdb,cn=config
objectClass: olcDatabaseConfig
objectClass: olcMdbConfig
olcDatabase: {1}mdb
olcDbDirectory: /var/lib/ldap
olcSuffix: dc=example,dc=com
olcAccess: {0}to * by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external
 ,cn=auth manage by * break
olcAccess: {1}to attrs=userPassword,shadowLastChange by self write by dn="cn=a
 dmin,dc=example,dc=com" write by anonymous auth by * none
olcAccess: {2}to * by self read by dn="cn=admin,dc=example,dc=com" write by * none
olcLastMod: TRUE
olcRootDN: cn=admin,dc=example,dc=com
olcRootPW: {SSHA}hogehoge
olcDbCheckpoint: 512 30
olcDbIndex: uid eq
olcDbIndex: mail eq
olcDbIndex: memberOf eq
olcDbIndex: entryCSN eq
olcDbIndex: entryUUID eq
olcDbIndex: objectClass eq
olcDbMaxSize: 1073741824
```
という形でadminユーザーの存在を確認することができました。
EmscriptenでES6_MODULE=1ビルドしたモジュールを、Workerで使うとViteでリリースビルドに失敗する

Emscripten + Vite で Worker を使ったサービスを作ると、リリースビルドできない問題が発生することがあります。大変分かりにくいので、原因と対策のメモです。

確認したバージョン: Vite:4.3.4, Emscripten:3.1.34

# 背景
Viteから参照するnpmパッケージが commonjs 形式だと、開発中にパッケージをローカル参照（file:..やnpm link）した際に[正しく動きません](https://github.com/vitejs/vite/issues/11657)。

オプションで解決する方法もあるのですが副作用があるため、できれば参照するライブラリは module 形式にしたいです。

Emscriptenでも module 形式のライブラリを生成することは可能で、[EXPORT_ES6=1オプション](https://github.com/emscripten-core/emscripten/issues/14383)を付けてビルドします。こうして作ったライブラリは Vite での開発中に npm run dev している限りは、メインでも Worker でもまったく問題なく利用できます。

# 問題
しかし、Emscripten で作ったライブラリを Worker から使った場合、npm run build ができません。
具体的には、まず、次のような警告が出ます。

```
[plugin:vite:resolve] Module "module" has been externalized for browser compatibility, imported by "/Users/username/dev/myapp/node_modules/mylib/dist/mymodule.js"
```

さらに、次のエラーでビルドが失敗します。

```
RollupError: Invalid value "iife" for option "output.format" - UMD and IIFE output formats are not supported for code-splitting builds.
```

# 原因
まず ES6_MODULE=1 付きでビルドした場合、生成されるコードに `await import` が含まれていることが要因です。

```my-emscripten-module.js
... if(ENVIRONMENT_IS_NODE){const{createRequire:createRequire}=await import("module"); ...
```

実はこれ、node で実行した場合に通るパスなので、ブラウザではデッドコードであり、実行されません。しかし、そんなことは Vite のバンドラ（Rollup）には分かりません。Rollup はここで発見した `await import` 先のパッケージをコードスプリット対象にして、警告を出力しています。

これだけなら警告だけで済むのですが、問題になるのは Worker で import していることです。
Vite は Worker のビルド形式をデフォルトで [iife](https://qiita.com/snamiki1212/items/37152d0066c57eb60796) にしていますが、iife 形式はコードスプリットに対応していません。むしろコードスプリットしないためのビルド形式です。

このため、コードスプリットしたい Rollup と、そうさせたくない Vite 本体の設定が矛盾して、ビルドエラーとなります。

# 対策
## 対策1: 最も簡単な対策（FireFox非対応）
Worker で import が[使えるブラウザ](https://caniuse.com/mdn-api_worker_worker_ecmascript_modules)であれば、Vite のコンフィグで Worker ビルド時のモジュール形式を es module にすることで解決できます。設定値としては次のように `es` を指定します。

```vite.config.js
export default defineConfig({
  plugins: [react()],
  worker: {
    format: 'es', // ←これを追加
  }
});
```

この対策は簡単ですが、現時点では FireFox が Worker で import をサポートしていないため、FireFox では動作しません。

## 対策2: 諦めて CommonJS 形式に戻す
諦めて Emscripten のビルド形式を commonjs に戻します。
冒頭で説明した、commonjs のパッケージをローカル参照した場合のエラーは Vite の設定で、[resolve.preserveSymlinks](https://vitejs.dev/config/shared-options.html#resolve-preservesymlinks) オプションを有効にすれば、回避できます。

ただし、ローカル参照したパッケージのソースの動的変更に追従できなくなります。
コードを変更した場合は、キャッシュを消してビルド（npm run dev -- --force）が必要になります。

上記以外のトラブルもあるかもしれませんが、手元では今のところ遭遇していません。

## 対策3 Emscripten 側で ENVIRONMENT=web オプションを付けてビルドする

Emscriptenでのビルド時に、-sENVIRONMENT=web オプションを付けると、上記の await import を含むコードが削除されるため、問題が発生しなくなります。同じコードを node.jsでも使いたい場合は、node 用のビルドが必要になってしまう可能性はありますが、node 専用の機能（NODEFSとか）を使っていなければ、web 用にビルドしたものも普通に node で動かせるので、悪くない対策だと思います。

# おわりに

Vite + Emscripten + Worker + AudioWorklet の組み合わせで使っています。開発中は簡単に動くなぁという感じだったのですが、リリースビルドするとこのようにまだ一癖ある感じです。FireFox もそろそろ正式に Worker での import をサポートしてほしいところですね。
Ktor で JWT 認証を利用する。

Ktor では JWT を利用した認証をすることが出来ますが、公式ドキュメントだと情報が少なくて苦労したので記事に残しておきます。

なお本記事では JWS に含まれる範囲も含めて JWT と表現しています。

## HS256 か RS256 か

Ktor の公式ドキュメントには HS256 と RS256 の 2 つの署名方式について書いてあります。

HS256 は秘密鍵を共有して署名と検証を行います。
RS256 は公開鍵と秘密鍵のペアを使用して、署名と検証を行います。

RS256 は秘密鍵の所有者のみがトークンに署名でき、秘密鍵が漏れた際も公開鍵を更新するだけで署名鍵をローテーション出来ます。

:::note info
公開鍵の更新は jwks という方式を使うため、ソフトウェアを更新する必要がありません。
:::

特に理由がなければ RS256 を選ぶべきでしょう。

https://auth0.com/blog/rs256-vs-hs256-whats-the-difference/

## 謎の UUID

https://ktor.io/docs/jwt.html#generate

ここにある UUID `6f8856ed-9189-488f-9011-0ff4b6c08edc`、最初は意味がわかりませんでした。これは jwk で使われる kid の値です。つまり鍵の ID です。

後で出てくる jwk のところで使用します。

## どのクレームが要るのか

まず JWT 自体に必須のクレームはありません。
とりあえず kid があればどの鍵を使用すればいいかがわかるため kid があるといいです。iss や aud は検証側の実装によりますが、ライブラリによっては必須かもしれません。(特に iss は)

Ktor の場合いかのようになります。

```kotlin
JWT.create()
            .withAudience("トークンの利用者 aud")
            .withIssuer("トークンの発行者 iss")
            .withKeyId("kid") //jwkで使用したkidの値を入力
            .withExpiresAt(now.plus(30, ChronoUnit.MINUTES)) //30分後に期限切れ
            .sign(Algorithm.RSA256(publicKey, privateKey)) //RS256を使用して署名
```

## jwk って何

これは Ktor のドキュメントでは完全にスルーされています。何故

jwk は JWT で使用する鍵を配布するための取り決めだと思ってください。多分詳しく読んだら違うのだと思いますがとりあえず動いたので良しとします。

あまり良くわかっていないのですがとりあえず https://example.com/.well-known/jwks.json に jwk の json を置いておけばいいです。

## jwk の作り方

### jwks.json の形式

形式によりますがこうなります。

```json
{
	"keys": [
		{
			"e": 鍵のpublicExponentをゴニョゴニョしてBase64URLエンコードしたもの,
			"n": 鍵のmodulusをゴニョゴニョしてBase64URLエンコードしたもの,
			"use": "sig",
			"kid": 鍵のID (謎のUUID),
			"kty": "RSA"
		}
	]
}

```

### Java の RSAPublicKey を JWK の形式にする方法

上でゴニョゴニョしている部分を実装する必要があります。

長さが 8 の倍数かつ 1 バイト目が 0 で、かつ長さが 1bit 以上のとき 1 バイト目を削除した上で、Base64URL エンコードする必要があります。

```kotlin

val bigInteger = publicKey.publicExponent

var bytes = bigInteger.toByteArray()
        if (bigInteger.bitLength() % 8 == 0 && (bytes[0] == 0.toByte()) && bytes.size > 1) {
            bytes = Arrays.copyOfRange(bytes, 1, bytes.size)
        }
Base64.getUrlEncoder().withoutPadding().encodeToString(bytes) //← これ
```

~~なんでこんなことやっているのか全くわかりません。~~

### kid は結局何?

鍵の ID なので被らなければ何でもいいです。
署名するときの kid と jwks.json で公開する kid は同じにする必要があります。つまり署名するたびに kid を変えたりしてはいけないということです。

https://stackoverflow.com/questions/75998370/validating-jwt-with-rsa256-with-ktor

## JWT の期限

JWT はステートレスなので一々 DB にアクセスせずともトークンに情報を持たせることが出来ます。逆に DB にアクセスせずに色々済ませるということはトークンが漏れて、それに気づいてもトークンを使用不可能にする方法が秘密鍵の変更しかありません。だって使用不可能なトークンかの判定を行えないんですもの。[^1 ] そこで JWT の期限を短くしてリフレッシュトークンを使用します。

### リフレッシュトークン

JWT の期限を 30 分だとしましょう。30 分間は利便性と不正利用のリスクとのトレードオフです。リフレッシュトークンは 2 週間に設定します。こっちも利便性と不正利用のリスクのトレードオフですが、JWT よりは長くしておきます。

API の利用者は、JWT の期限切れに気づいたときにリフレッシュトークンを使用して JWT を発行してもらいます。リフレッシュトークンの期限も切れていたときは再度パスワードなどを使用して認証を行った後、JWT を発行してもらいます。

リフレッシュトークンは DB などで保存し、いつでも破棄できるようにします。

リフレッシュトークンの実装については本題ではないので書きません。Ktor の他の認証プロバイダーを使って制限されたエンドポイントを作成すればいいと思います。

## 結局何をすれば使えるようになるのか

1. /.well-known/jwks.json を作成する。
1. 公式ドキュメント通り JWT 認証を追加する。

[^1 ]: https://qiita.com/hakaicode/items/1d504a728156cf54b3f8
C言語の変換指定子で％02とか書くやつ(リーディングゼロの指定)

# 注意
・AtCoderの[abc001_B - 視程の通報](https://atcoder.jp/contests/abc001/tasks/abc001_2)ネタバレあり。
・間違っていたら教えてください！

# 学んだこと
・リーディングゼロの指定を理解してACできた。[参考にした記事](https://www.k-cube.co.jp/wakaba/server/format.html)
![スクリーンショット 2023-05-08 2.11.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2823837/cf4838ec-478a-50d9-7a4b-8909b0257f51.png)


【問題文抜粋】
0.1km 以上 5km 以下：距離 (km) を 10 倍した値とする。1 桁の場合は上位に 0 を付す。
例えば、2,000m =2.0km ならば、VVは 20 である。同じく、200mの場合VVは 02 である。
【解説】
・1 桁の場合は上位に 0 を付す。（要するに十の位に0をつける）
という指示なのでリーディングゼロの指定を使って2桁になるように表示する。
・標準入力が100〜900までを1000で割り算すると1桁になるのでリーディングゼロの指定が適応される。標準入力の値が1000以上だと1000で割った答えが2桁以上になるのでリーディングゼロの指定を使っていても適応されないから以下の条件分岐ACできる。


# ACしたコード
```c
#include <stdio.h>
int	main(void)
{
	int m;
	scanf("%d",&m);
	// printf("%d\n",m);

	if(m < 100){
		printf("00\n");
	} else if(100 <= m && m <= 5000){
    //ここでリーディングゼロの指定を使ってる。
		printf("%02d\n",m / 100);
	} else if(6000 <= m && m <= 30000){
		printf("%d\n",m / 1000 + 50);
	} else if(35000 <= m && m <= 70000){
		printf("%d\n",((m / 1000) - 30) / 5 + 80);
	} else if(70000 < m){
		printf("89\n");
	}
	return (0);
}

```
QGISを使って描いた山林マップを、QGIS Cloudを使って他の人に共有する

# 実施する内容
QGISで作成したマップを、『QGIS Cloud』 というサービスを使って共有します。
自分が作ったマップを他の人と一緒に見ることが出来るようになります。

また、その過程でクラウド上にデータをアップロードしてWEBブラウザで見ることになるので、自分自身も、スマートフォンのGPS位置情報を使いながら、自分が作成したマップのどこ辺りにいるのかを確認出来るようになります。
※ただし、GPSの位置精度には注意した方が良いと思います。

取り組んでいる背景などはこちらの記事も見てもらえたらと思います。

https://qiita.com/yukima77/items/a2284cdca072dee7375c

# 完成イメージ
スマートフォンでこのような感じで、描画したエリアを確認することが出来ます。
GPSを有効にしたら、自分の居場所も表示されます。
![MicrosoftTeams-image (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21408/7aa15fe0-8b1d-9cd6-4171-d1e12886710f.png)

# 使用するツール／サービス
**QGIS**

https://qgis.org/ja/site/

**QGIS Cloud**

https://qgiscloud.com/

# 手順
1. QGISでマップを作成する（[こちら](https://qiita.com/yukima77/items/a2284cdca072dee7375c)の記事を参考に）

1. 『[QGIS Cloud](https://qgiscloud.com/)』のアカウントを作成する（詳細は割愛）

1. QGISでプラグインを追加する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21408/853ace47-812b-1d4e-fa53-311c81439b30.png)
『QGIS Cloud Plugin』をインストールします。
※キャプチャは既にインストール済みの環境なので、新規の場合はインストールをクリックして進めて下さい。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21408/7c0063c2-c678-836a-756e-a4e42d61fd2a.png)

1. QGIS上に、『QGIS Cloud』が表示される
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21408/3c4fff24-2204-5bf4-9646-a4a8b5012e86.png)

1. QGIS Cloud にログインする

1. データベースがない場合は、データベースを作成する
データベースがない場合は、こちらのメッセージが表示されて、自動的に生成されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21408/aacca2d1-78eb-a7cd-106d-0b9dcb89db98.png)

1. データベースが生成される
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21408/d1bef5d4-1e60-865a-f41b-772ea74a9e3c.png)

1. データをアップロードする
「Upload Data」からデータをアップする。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21408/5ea64fc1-8b4e-b037-4e15-c7e51680039d.png)

1. プロジェクトのSaveが求められたら保存する

1. マップを公開する
「Publish Map」のボタンを押す
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21408/b7a60ee4-679d-754b-1b64-7b3be769f159.png)

1. マップの公開完了
「Webmap」の URL を参照
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21408/4fd049e6-f5dd-2932-ad03-f328f8380a04.png)

1. ブラウザで表示
「Webmap」の URL をブラウザで開くとこんな感じで表示されます。
スマートフォンでも見ることができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21408/5a71bc17-7931-96ed-9e02-8eb2387e522e.png)

# おまけ
今回は、マップとしては Google Map を使用しています。
その場合は、QGIS のサイト上で「Google Maps API Key」を追加する必要があります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21408/d4b34461-a801-1f4c-96da-8771d75d6bff.png)



# 参考
『QGIS Cloud』については、日本語の情報が少ないのですが、もしかしたらこちらのサイトも参考になるかもしれません。

https://support.pacificspatial.com/494-pss-knowledge-base-site/976-gis-cloud/3286-qgis-gis-cloud
Azuruのお値段について


会社のオンプレミスのWindowsサーバ１台の環境をAzuruに移動した場合の費用概要です。

VPN　3500円/一か月

鯖　B2ms　月額8000円/2のはずですが、5000円くらい
* 鯖は8時～20時まで起動します。それ以外の時間は停止

HDD
　容量は40GB程度

　ディスクの状態　プライマリ: 利用可能、セカンダリ: 利用可能

　パフォーマンス:Standard
　　パフォーマンス面で制約はありますがコスト効率に優れているのが特徴です。
　　使用量（ディスクサイズ）、トランザクション量、送信データ転送回数などに応じて
　　課金が行われます。

　レプリケーション:読み取りアクセス geo 冗長ストレージ (RA-GRS)
　　geo 冗長ストレージ (GRS) は、LRS を使用して、プライマリ リージョンの
　　1つの物理的な場所内で、データを同期的に 3 回コピーします。
　　その後、プライマリ リージョンから何百キロも離れたセカンダリ リージョン内の
　　1つの物理的な場所にデータが非同期にレプリケートされます。

　アカウントの種類:StorageV2 (汎用 v2)
　　最新機能のサポートも行う基本的なBlob・Files・Queue・Table・Disk用アカウント、
　　パフォーマンスレベルStandard・Premiumをサポートする。

　これだけの機能がそろって、月額4000円くらい？

追加で

バーチャルネットワーク 1000円
Azure DNS　100円
などなど

合計13,600円/月

くらい・・・
HDDがあらかじめ冗長化されているものを選べばバックアップは、ほぼ不要
サーバのバックアップも、手順書を作成すれば、いらないかな？
と思います。
USB-GPSレシーバーによるNMEAフォーマットログ取得方法

Amazon等で¥2000程度で比較的安価にUSB‐GPSレシーバーが入手できるので、自動車のサーキット走行分析に活用するべく、NMEAフォーマットのログの取得方法について共有します。

NMEAフォーマットのログをもとに筑波サーキットのラップタイムを計測するスクリプトもpythonで作成してみたので、ご興味あれば、
[https://github.com/qoosen/MakeKmlGpsLog](https://github.com/qoosen/MakeKmlGpsLog)
も覗いてみてください。

**1.USB-GPSレシーバー設定ソフトのダウンロードとインストール**
ubloxのホームページから、”u-center”をダウンロードしてインストール。
[https://www.u-blox.com/en/product/u-center](https://www.u-blox.com/en/product/u-center)
この時、u-center2ではなく"u-center,v22.07"('23/2時点)の方を選択すること。

**2.USB-GPSレシーバーのデバイス設定**
デバイスマネージャーを開きポート(COMとLPT)を確認したのち、USB-GPSレシーバーをPCにUSB接続する。
デバイス認識されたのち増えたデバイスを確認する(この場合COM6)。
![DeviceManager.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/521846/467a5922-657f-5771-f57e-5a94dec6f475.png)

**3.GPSレシーバーをu-centerに接続する**
u-centerを開く。うまくすると自動的に接続されるが、(下記、赤矢印部が緑になる。)
![connect.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/521846/6e1197b6-8c82-b154-09ab-38c202e3e39c.png)
うまく接続できない場合は、[Receiver] - [Connection] から、2.で確認したポートを選択する。(この場合はCOM6)
![connect2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/521846/00ff5fb8-305c-41c5-e219-81acb4d1853a.png)

**4.GPSレシーバーの設定**
メニューバー [View] ‐ [Configration View]で設定ウィンドウを開く。
 ![Config.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/521846/49737ff3-e7f8-b2ab-2276-5ff8a4725b62.png)

**4‐1.ポート設定**
転送速度を設定する。4‐2.受信周波数設定で10HzとするとBoudrate:9600では間に合わなくなるため
①PRT(ports)を選択
②Target : 1 ‐ UART1 を選択
③Protcol out : 1 ‐ NMEA を選択
④Boudrate : 115200 を選択
⑤Sendを押下して送信する(、、、忘れがち)
![RATE0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/521846/3de28c64-7907-a3bf-26dc-56237784db95.png)

さらに、USBからNMEAだけを出力するようにする。
①PRT(ports) を選択、、、すでに選択されてますよね。
②Target : 3 ‐ USB を選択
③Protcol out : 1 ‐ NMEAを選択
④Sendを押下して送信する(、、、忘れがち)
![PRT.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/521846/5ed21b83-3e80-da44-6ef1-949e0b95bdbd.png)

**4-2.受信周波数設定**
デフォルトでは1Hz(1000ms/回)だと、サーキット走行用途では遅すぎるので10Hz(100ms/回)とする。
①RATE(Rates)を選択
②100[ms]を入力
③Sendを押下して送信する(、、、忘れがち)
![RATE.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/521846/c98de8a3-5a59-6e39-fb36-26f4e9118551.png)

**4-3.設定の保存**
4-1. ~ 4-2.の内容をUSB-GPSレシーバーに保存するのだが、なぜか私のレシーバは保存ができないので、毎回 4-1. ~ 4-2.の内容を繰り返すことになる。
①CFG(Configration)を選択
②Save current configrationを選択
③0‐EBR ⁺ 1‐FLASHを選択 もし、1つしか選択されていない場合は"Shift"+左クリックで2つ選択できる。
④Sendを押下して送信する(、、、忘れがち)
![CFG.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/521846/cdd58d94-4361-8e30-3113-fdb7cbef3088.png)

これで、サーキットにおける、NMEAフォーマットログ取得の準備は完了です。

**5.ログの取得**
ログの取得方法には、主に下記2つの方法がある。
a.そのままu-centerを使う
b.ターミナルソフト(TeraTerm等)を使う
私は、GPSデータ以外にもUSBを通じてGセンサーやCANデータの取得しているため、軽量なb.の方法を使用しているが、ここでは簡単なa.の方法を紹介する。

①を押下すると、ログファイルをどこにどんな名前で保存するかのウィンドウが表示されるので、適当な名前を付けてログの取得を開始する。
～サーキットを走行する～
②を押下すると、ログファイルの記録を終了する。
![REC.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/521846/bf90536e-7915-f98d-5ef8-6cdd693e9f10.png)

以上、Enjoy your driving by qoosen 2023/05/08
AWSCLI で CERTIFICATE_VERIFY_FAILED エラーが出た時の対処（Zscaler/Netskope環境）

すぐに解決したい場合は`--no-verify-ssl`オプションを使えば警告は出るものの一応使えるようになります。
しかしここでは証明書を指定する方法で解決します。

## 原因

典型的にはセキュリティソフトが通信に介入していることで発生します。
通常はOS側にセキュリティソフト用の証明書がインストールされているためエラーにはならないのですが、
AWSCLIなど独自に証明書ストアを持っているアプリケーションでは、OS側の証明書を参照せずにタイトルのエラーが出ることになります。

## 解決策

OS側から証明書を取り出しAWSCLIに渡します。

Windowsボタンでメニューが開いた状態で「証明書」と日本語入力すれば「コンピューター証明書の管理」が出てきますので起動します。
信頼されたルート証明機関＞証明書とツリーを開きます。
今回はZscalerやNetskopeが怪しいので「Zscaler」や「Netsokpe」と記載のある証明書を探します。
見つけたら右クリック＞すべてのタスク＞エクスポートと選択します。
途中で聞かれる形式は「Base 64 encoded X.509 (.CER)」を選択し保存します。

あとは`--ca-bundle "保存した証明書のパス"`としてAWSコマンドのオプションを足せば解決すると思います。

なおコマンドラインオプションだと普段使いには不便なので、
`AWS_CA_BUNDLE`という環境変数に証明書のパスを設定するか、
`aws configure set default.ca_bundle "保存した証明書のパス"`で設定値として保存できます。

## 続き

自分の環境ではここでは終わらなくて、サブコマンド（接続先ホスト）によってNetskopeが介入するときとしないときがあるようでした。
つまりNetskopeの証明書のみを指定することによって逆に動かなくなるコマンドも出てきました。
そこで証明書を追加することにします。

今度は「Starfield Class 2」と記載のある証明書をエクスポートします。
エクスポートした証明書はテキストファイルで複数格納可能な形式ですので、以下のように並べたテキストファイルを作成します。


```awscli.pem
-----BEGIN CERTIFICATE-----
AAAAAAA...
AAAAAAA...
(略)
AAAAAAA...
AAAAAAA...
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
AAAAAAA...
AAAAAAA...
(略)
AAAAAAA...
AAAAAAA...
-----END CERTIFICATE-----
```

今度はこちらの証明書を指定することで問題が解決しました。
Google Cloud DLP の変換メソッドの特徴メモ

# 概要

Google Cloud Security Engineer の試験で、 DLP 変換の特徴を覚える必要があるので学習がてらメモします。


# TL;DR

本家のサイトに一覧が有るからそこを見れば良い。
https://cloud.google.com/dlp/docs/transformations-reference?hl=ja#transformation_methods

# メモ

頻出するメソッドと特徴

試験では、主に `仮名化 (Pseudonymization)` について問われることが多い。
特に、仮名化は、HMAC / FPE-FFX / AES-SIV で特徴があるため注意が必要。

技術的には知っていることなので、名称と対応付けること。

## データが消える変換

データが消えたり、置換されたり、マスクされたり、元に戻したり、比較したり出来ない変換は以下の通り。

| 日本語 | 英語 | オブジェクト | 説明 |
| :--- | :--- | :--- | :--- |
| 削除 | Redaction | RedactConfig | PII を削除する | | |
| 置換 | Replacement | ReplaceValueConfig | PII を同一の値 (e.g. fake@example.com) に変換する |
| 辞書 | Replace with dictionary | ReplaceDictionaryConfig | 『置換』の値が単一でなく、複数になったバージョン　|
| マスキング | Mask with character | CharacterMaskConfig | PII を特定の文字 (e.g. "*") で埋め尽くす |

## データが残る変換

データを元に戻すことが出来たり、比較したりできる変換は以下の通り。

| 日本語 | 英語 | オブジェクト | 説明 | 文字セットと長さを保持 | 復元可能 | 参照整合性 |
| :--- | :--- | :--- | :--- | :--- | :--- | :--- |
| 仮名化（入力値を暗号ハッシュで置換） | Pseudonymization by replacing input value with cryptographic hash | CryptoHashConfig | いわゆる HMAC | | | ✔ |
| 仮名化（暗号形式を維持したトークンに置換） | Pseudonymization by replacing with cryptographic format preserving token | CryptoReplaceFfxFpeConfig　| FPE-FFX で元データに戻すことができる | ✔ | ✔ | ✔ |
| 仮名化（暗号トークンに置換） | Pseudonymization by replacing with cryptographic token | CryptoDeterministicConfig　| AES-SIV で元に戻すことができる | | ✔ | ✔ |

FPE の特徴として、変換前の値が少ない場合、リバースエンジニアリングが容易となる。
セキュリティが重要な場合は、 AES-SIV を用いた方法を利用する。

以上
Mule4 JMS Connector について

# JMSの概念

Java Message Service（JMS）は、Javaアプリケーション間でメッセージを送信および受信するためのJava APIです。JMSは、メッセージングシステムを構築するための標準的な手段を提供します。Mule4 JMSコネクタを使用することにより、MuleアプリケーションでJMSリソースを簡単に利用できます。

# JMSに関するコンポーネント
Mule4は、JMSを使用するためのさまざまなコンポーネントを提供しています。例えば、JMSリスナーやJMS送信者などがあります。これらのコンポーネントを使用することで、JMSを介してメッセージを送受信するためのフローを簡単に構築できます。また、Mule4 JMSコネクタは、トランザクション管理、メッセージの読み取り、書き込み、処理などのさまざまな機能を提供します。

## publish
publishコンポーネントを使用すると、メッセージをジェネレートしてJMSキューまたはトピックに送信できます。

## consumer 
consumerコンポーネントは、JMSキューまたはトピックからメッセージを読み取ります。

## publish-consumer
publish-consumerコンポーネントは、メッセージを生成し、JMSキューまたはトピックに送信し、同時にメッセージを読み取ります。

## ack
ackコンポーネントは、メッセージの受信を確認するために使用されます。

## recover-session 
recover-sessionコンポーネントは、JMSセッションを回復するために使用されます。

## on-new-message
on-new-messageは、JMSキューまたはトピックからの新しいメッセージを受信し、Muleフロー内の他のコンポーネントにルーティングするために使用されます。

# JMS,VM,Anypoint MQの違い
JMS、VM、Anypoint MQは、それぞれ異なるメッセージングプロトコルを使用しています。

JMSはJavaアプリケーションでのメッセージングに使用され、

VMは同じMuleフロー内のコンポーネント間でメッセージングを可能にします。

Anypoint MQは、クラウドベースのメッセージングサービスであり、高度なセキュリティ機能を備えています。
DIを利用してAWS Lambdaのコーディングする

コードの可読性と保守性を向上させるために、DI（Dependency　Injection）を利用することができます。
この記事では、Lambda関数の中でDIパターンを適用して、モジュールやサービスの依存関係を管理する方法を解説します。

目次
1. Dependency Injectionとは
1. AWS LambdaとDIの関連性
1. 例：簡単なLambda関数でDIを利用する方法
1. 実践：サードパーティのDIライブラリを使用する
1. まとめ

## Dependency Injectionとは
Dependency Injectionは、オブジェクト指向プログラミングにおいて、コンポーネント間の依存関係を外部から注入するデザインパターンです。DIを利用することで、以下の利点があります。

- コードの可読性向上
- テストの容易性向上
- モジュール間の疎結合

## AWS LambdaとDIの関連性

AWS Lambdaは、サーバーレスアーキテクチャを実現するためのFaaS（Function as a Service）サービスです。Lambda関数は短期間実行されるため、関数内の状態管理やリソースの管理が重要です。DIを利用することで、コードの品質を向上させ、保守性や拡張性を確保できます。

## 例：簡単なLambda関数でDIを利用する方法

今回はpython3.10で例を提示します。

```python
class UserService:
    def __init__(self, repository):
        self.repository = repository

    def find_user(self, user_id):
        return self.repository.get_user(user_id)

class UserRepository:
    def get_user(self, user_id):
        # データソースからユーザー情報を取得する処理
        # 取得元はDBや外部API等
        pass

def handler(event, context):
    user_repository = UserRepository()
    user_service = UserService(user_repository)

    user_id = event["user_id"]
    user = user_service.find_user(user_id)
    return user

```

この例では、UserServiceとUserRepositoryの依存関係をコンストラクタで注入しています。これにより、依存関係の管理が容易になり、テストも行いやすくなります。
今回は実装していませんが、通常サービスクラスにロジックを記述し、リポジトリクラスにデータの取得や保存のロジックを記述します。

## 実践：サードパーティのDIライブラリを使用する

さきほどはDIがどのようなデザインなのかを説明するために、インスタンスを生成するコードを記述しました。
しかしこれでは、クラスの依存関係を意識しながらコーディングする必要があります。
それらを解決するサードパーティライブラリがいくつか存在します。ここでは、injectorライブラリを使用してDIを実装します。

まず、injectorライブラリをインストールします。

```bash
pip install injector
```

次に、injectorを使用してDIを実装する方法を見ていきましょう。

```python
from injector import Injector, inject, Module, singleton , provider

class UserService:
    # このデコレータによって依存関係がinjectorによって解決されます
    @inject
    def __init__(self, repository: UserRepository):
        self.repository = repository

    def find_user(self, user_id):
        return self.repository.get_user(user_id)

class UserRepository:
    def get_user(self, user_id):
        # データソースからユーザー情報を取得する処理
        pass

def handler(event, context):
    injector = Injector()
    # injectorがクラスの依存関係を解決したうえで、インスタンスを提供する
    user_service = injector.get(UserService)

    user_id = event["user_id"]
    user = user_service.find_user(user_id)
    return user

```

この例では、injectorライブラリを使用して、UserServiceとUserRepositoryの依存関係を解決しています。handler関数内でインジェクタを作成し、UserServiceインスタンスを取得しています。

## まとめ
この記事では、AWS LambdaでDIを利用してコーディングする方法を紹介しました。DIを適用することで、Lambda関数のコードの可読性や保守性が向上し、より品質の高いコードを実現できます。

今後、以下のようなコンテンツを予定しています。

- [インタフェースによる抽象化](https://qiita.com/odxdoge/items/2d1924e7d4fbe92e9969)
- injector(DIコンテナ)の隠蔽
- 単体テストの実装、実行
InvalidAuthenticationTokenTenant

エラー対策です。

  "error": {
    "code": "InvalidAuthenticationTokenTenant",
    "message": "アクセス トークンが、間違った発行元 'https://sts.windows.net/xxxxx/' からのものです。これは、このサブスクリプションに関連付けられているテナント 'https://sts.windows.net/xxxxx/' と一致している必要があります。トークンを取得するには、機関 (URL) 'https://login.windows.net/xxxxx' を使用してください。サブスクリプションが別のテナントに移転された場合、サービスに影響はありませんが、新しいテナントに関する情報が反映されるまでに時間がかかる場合があるのでご注意ください (最大 1 時間)。サブスクリプションを移転したばかりでこのエラー メッセージが表示された場合は、後でもう一度お試しください。"
  }

というエラーが出たら、どうしましょ。。。
 
ユーザーIDを使ってSVに対してログインしていると、うまくログイン出来ないようでした。
これは、バグ？
なので、別の方法でログインしました。
＊　ユーザーIDじゃなくって、違うアカウントでログインするように変更したらいいと考えました。

＝＝＝＝　下記まとめ　＝＝＝＝
そこで対策です。

１．普通にやると、エラーが出ます。

２．『ロジックアプリ』の既に作成しているアカウントから
左メニューのIDで「システム割り当てマネージド ID」を作成します。
アクセス許可は、所有者にしました。
＊オブジェクト IDをコピペしておきます。
xxxx-xxxx-xxxx-xxxx-xxxx


３．「ロジック アプリ」の作成したアプリを選択して「ロジック アプリ デザイナー」を選択します。
そこでこの画面で
![999.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/257082/99a98391-3967-45da-d8f9-4dc74e2775d4.png)
接続方法を修正します。
マネージド ID を使用して接続で、
xxxx-xxxx-xxxx-xxxx-xxxx
を入力して接続をかけます。


もう一個『ロジックアプリ』を作成する場合は、２と３を再度繰り返す。
つまり、『ロジックアプリ』毎に、管理権限のあるアカウントが必要って事です・・・

うーんまぢで意味が不明でしたが、何とか動きました。
killしてもkillしても復活するプロセスに遭遇した

## 経緯
Dockerでmailコンテナを立てようとした際に下記のエラーが発生した。
```
ERROR: for mail  Cannot start service mail: Ports are not available: listen tcp 0.0.0.0:8025: bind: address already in use
```

8025ポートは既に使われているとのこと。
8025がどのプロセスで使われているのかを確認する。

```
$ lsof -i:8025
```

実行結果は下記。
```
COMMAND PID     USER   FD   TYPE             DEVICE SIZE/OFF NODE NAME
MailHog 688 UserName    8u  IPv6 0x0x0x0x0x0x0x0x0x      0t0  TCP *:ca-audit-da (LISTEN)
```

PIDが判明したので、対象のプロセスをキル。
```
$ kill -9 688
```

8025ポートはもう使われていないはずなので、再度確認。
するが、なぜのPIDでプロセスで復活している・・
```
COMMAND   PID     USER   FD   TYPE             DEVICE SIZE/OFF NODE NAME
MailHog 50336 UserName   8u   IPv6 0x0x0x0x0x0x0x0x0x      0t0  TCP *:ca-audit-da (LISTEN)
```
という流れを3回繰り返したので、本格的に解決していくことに。

## 解決

psコマンドで、起動中のプロセスを一覧表示する。

```
$ ps -axf
```

lsofコマンドでヒットしたPIDのプロセスを探してみつける。

```
 UID   PID  PPID   C STIME   TTY     TIME CMD
 502 50336     1   0 3:16PM  ??   0:00.01 /usr/local/opt/mailhog/bin/MailHog
```

どうやらHomeBrewでinstallしていたmailhogのプロセスが8025ポートを使用していたということらしい？
具体的に何が起きていていてこの事象が発生したのかは、調べ中なのでまた後日。
brew uninstallでアンインストールして、無事解消。コンテナを立ち上げることができました。

```
$ brew uninstall mailhog
```
Godot4.0　ローカル座標系を親の座標系に変換する

# 目的
　Godotではノードを親子関係にして、子は親の座標系内のローカル位置を保持しています。
　また子はglobal座標系の位置も取得することができます。
　今回はGlobalと子の中間ノード上の座標系で子の位置を取得する方法を調査しました。
　結論から言うと、親のtransform(Transform3D) を子の位置(Vector3)に「*」で計算すると親の座標系上の位置を計算することができました。

# 参考
>GODOT DOCS : Matrices and transforms
>https://docs.godotengine.org/en/latest/tutorials/math/matrices_and_transforms.html#

# シーン
　下記のようなシーンを作成します。

| ノード名:ノードクラス名 |インスペクタ変更値|
|:-|:-|
|Global : Node3D|Rotation : (0°, 90°, 0°)|
|　┗┳Parent : MeshInstance3D|新規PlaneMesh3D、Position : (2m, 0m, 1m)|　　
|　　┗━Child : MeshInstance3D|新規SphereMesh3D|　　

# 座標の確認

## Childの位置
 Childは初期値なので、(x,y,z)=(0,0,0)になります。
![スクリーンショット (750).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/74f52348-4b37-8b39-93da-390ac449d66e.png)
## Parent/Childの位置
 ParentはPositionを(x,y,z)=(2m, 0m, 1m)に設定していますので、Parent座標系の中ではChildの球の位置も(2m, 0m, 1m)になります。この位置を計算で取得するのが本記事の目的です。
　下図は上面図です。右側がXプラス、下がZプラスになります。
![スクリーンショット (751).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/a00298fe-e04a-48d0-7514-9b7c8ad6486c.png)
## Global/Parent/Childの位置
　GlobalはY軸周りに90°回転しています。子の位置は(x,y,z)=(1m, 0m, -2m)になります。
　トップノードなので、global_positionで位置を取得することができますが、計算でも求めたいと思います。
![スクリーンショット (754).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691050/7c1517a0-822a-eab5-3629-012c3431e206.png)

# スクリプトと実行結果
 シーン内のGlobalノードを右クリックして、スクリプトをアタッチして下記のように修正します。
```GDScript
extends Node3D

func _ready():
	var child  : MeshInstance3D = $Parent/Child
	var parent : MeshInstance3D = $Parent
	var global : Node3D         = $"."
	
	print("child pos(x,y,z)")
	print("  local             :", child.position)
	print("  parent coordinate :", parent.transform * child.position)
	print("  global coordinate :", global.transform * parent.transform * child.position)
	print("  global_position   :", child.global_position)
```

　実行結果は下記のように出力されます。
```
child pos(x,y,z)
  local             :(0, 0, 0)
  parent coordinate :(2, 0, 1)
  global coordinate :(1, 0, -2)
  global_position   :(1, 0, -2)
```

　child.position(Vector3)にparent.transform(Transform3D)を掛け合わせると、parent座標系上の位置に変換できました。
　同様にglobal.transform(Transform3D) * parent.transform(Transform3D) * child.position(Vector3)でglobal_positionと同じ値になることを確認できました。


以上
ubuntuでnginx hello world

# nginx（エンジンエックス）とは
* Webサーバの一つ

# nginx インストール
* ubuntuから下記コマンド実行
```
sudo apt update
sudo apt install nginx
```

* 確認
```
nginx -v
```

# nginx 起動
* ubuntuから下記コマンド実行
```
sudo nginx
```

* ブラウザから
http://localhost
にアクセス
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/281492/fbd09d7b-e371-4c96-bf2f-7c331a823d87.png)


# nginxの環境について
* デフォルトのrootフォルダは/var/www/html
* /var/www/html に移動
```
cd /var/www/html
```
* ファイルを表示してみる
```
user01@DESKTOP-5FA6FSN:/var/www/html$ ls
index.nginx-debian.html
```
* http://localhost で表示されていた「Welcome ～」は、
  index.nginx-debian.html が表示されていた

# hello world
* /var/www/html/hello.html を作成
```
sudo vi hello.html
```
* hello.htmlに以下のように記述し保存
```
hello world
```
* 下記にアクセス
http://localhost/hello.html
* 「hello world」と表示されることを確認する

# nginxの設定ファイルを見てみよう
```
cd /etc/nginx
view nginx.conf
```
※linuxの設定ファイルはだいたい```/etc```にある
## なぜ/var/www/htmlにあるファイルを参照するのか
* viewコマンドで参照すると下記のような設定がある
```
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
```
includeで、さらに他のファイルを読み込んでいる

* includeされている```/etc/nginx/sites-enabled/default```を参照してみる
```
view sites-enabled/default
```
* defaultに
```
root /var/www/html;
```
の定義があり、ここで設定されたディレクトリにあるファイルを参照できる
各国の名目GDPの推移（動的グラフ）

# はじめに
各国の名目GDP推移をplotlyを使ってインタラクティブなグラフにした。
データはWorldBankデータベースから取得。

```Python
import requests
import json
from datetime import datetime
import pandas as pd
import plotly.graph_objs as go
from plotly.subplots import make_subplots

def fetch_gdp_data(country_code):
    gdp_data = {}
    for page in range(1, 3):  # ページを指定してデータを取得
        url = f"https://api.worldbank.org/v2/country/{country_code}/indicator/NY.GDP.MKTP.CD?format=json&page={page}"
        response = requests.get(url)
        data = json.loads(response.text)
        
        for entry in data[1]:
            date = datetime.strptime(entry["date"], "%Y")
            value = entry["value"]
            if value is not None:
                gdp_data[date] = value
                
    return pd.Series(gdp_data)

# 国のISOコードを設定（G7＋中印）
countries = {
    'USA': 'United States',
    'CHN': 'China',
    'JPN': 'Japan',
    'DEU': 'Germany',
    'IND': 'India',
    'GBR': 'United Kingdom',
    'FRA': 'France',
    'ITA': 'Italy',
    'CAN': 'Canada',
}

# データフレームの作成
gdp_data = pd.DataFrame({code: fetch_gdp_data(code) for code in countries.keys()})

# インデックスを datetime 型に変換
gdp_data.index = pd.to_datetime(gdp_data.index)

# 欠損値の処理
gdp_data_filled = gdp_data.fillna(method='ffill').fillna(method='bfill')

# データの最初の年と最後の年を取得
start_year = gdp_data_filled.index.min().year
end_year = gdp_data_filled.index.max().year

# 最新年のデータを取得して並べ替え
latest_year = gdp_data_filled.index.max()
latest_data = gdp_data_filled.loc[latest_year].sort_values(ascending=False)

# 並べ替えた順に国名を取得
sorted_countries = latest_data.index.tolist()

# インタラクティブなグラフを作成
fig = make_subplots(specs=[[{'secondary_y': True}]])
for country in sorted_countries:
    fig.add_trace(go.Scatter(x=gdp_data_filled.index, y=gdp_data_filled[country], mode='lines', name=countries[country]))

fig.update_layout(
    title=f'Nominal GDP (current USD) of G7 countries, China, and India ({start_year}-{end_year})',
    xaxis_title='Year',
    yaxis_title='GDP (current USD)',
    hovermode='x unified',
    legend_title_text='Country',
    legend=dict(x=0.01, y=0.99, xanchor='left', yanchor='top', orientation='v'),
    xaxis=dict(
        tickformat='%Y'  # 年のみを表示
    )
)

fig.show()
```

　
描かれたグラフをGoogleDriveに保存する。

```Python
from google.colab import drive
import plotly.io as pio
import os

# Google ドライブをマウント
drive.mount('/content/drive')

# 保存先ディレクトリの設定
save_directory = '/content/drive/MyDrive/'

# HTML ファイル名の設定
html_filename = 'gdp_chart.html'

# グラフを HTML ファイルとしてエクスポートし、Google ドライブに保存
pio.write_html(fig, file=os.path.join(save_directory, html_filename), auto_open=False)
```

出力結果をここに貼り付けたいが、やり方がよくわからない（あるいは、無理なのか）ので、今後の課題。
Laravelでのユーザーの編集と更新と退会機能の実装

# 概要
LaravelでTwitterクローンのようなアプリを作りました。
その時に登録したユーザー情報の編集と更新機能を実装した時のことと、
ユーザーが退会するための機能を実装した時のことを書いています。
ユーザが何かを投稿してそれを編集したり削除したりできるアプリを開発していました。
後完全な削除はしません。


# ユーザー編集機能
### まずはルーティングから
***全部は公開できませんので１部公開***します。

```routes/web.php
//ユーザー詳細、編集、更新
Route::prefix('users')->group(function () {
    #省略
    Route::get('{id}/edit', 'UsersController@edit')->name('users.edit');
    Route::put('{id}', 'UsersController@update')->name('users.update');
});
```
ルーティングでeditとupdateの設定をします。

# コントローラー
まずはuseで下の２つを読み込みました。
そしてユーザーeditとupdateを作ります。

```UsersController.php
#省略
use App\Http\Requests\UserRequest;
use App\Http\Controllers\Controller;

#省略

public function edit($id)
    {
        if ($id == \Auth::id()) {
            #省略
        }
        abort(404);
    }


public function update(UserRequest $request, $id)
    {
        #省略
        $user->password = bcrypt($request->password);
        $user->save();
       #省略
    }

```

# ユーザーリクエスト
ここで***バリデーションの設定***をします。***編集や更新ではバリデーションは必須***です。


```UserRequest.php
<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
use Illuminate\Support\Facades\Auth;

class UserRequest extends FormRequest
{
    #省略
    public function authorize()
    {
        return true;//ここはtrueにする
    }

    #省略

    public function rules()
    {
        return [
            #省略　ここはバリデーションの設定です
        ];
    }
}

```

# Viewの実装
edit.blade.phpでフォームの画面を作成します。


```edit.blade.php
@extends('layouts.app')
@section('content')
    <h2 class="mt-5 mb-3">ユーザを編集</h2>
    <form method="POST" action="{{ route('users.update', $user->id) }}">
    @csrf
    @method('PUT')
    @include('commons.error_messages')
#省略　ここにはフォームの実装がきます。


</form>
@endsection
```

# 編集画面に遷移
編集画面に遷移するコードは以下のように実装しました。
ボタンのデザインは今回は公開しないです。
ユーザー編集画面にしたいviewに記述をすると遷移できました。

```php
<a href = "{{route('users.edit',['id' => $user->id])}}"　class = "省略" >ユーザーの編集</a>
```

# ユーザー退会
### まずはルーティングから
ルーティングはまずは基本は下のようにします。
```routes/web.php
//ユーザー詳細、編集、更新,削除
Route::prefix('users')->group(function () {
    #省略
    Route::delete('{id}', 'UsersController@destroy')->name('users.delete');
});
```

# ユーザー退会と同時に投稿も削除
***ユーザーが退会したと同時にユーザーが投稿した投稿も削除しなければいけません***。
そのためにこのような実装をしました。思い切ってChatGptに聞きました。

```app/User.php
protected $dates = [
        'deleted_at'
    ];

#省略
public static function boot()
    {
        parent::boot();

        static::deleting(function ($user) {
            $user->posts()->delete();
        });
    }
```

# コントローラー作成
コントローラーは都合により今回は公開できません。
申し訳ありません。

# viewの実装
viewは以下のようにすると退会ボタンを作成できました。

```resources/views/users/edit.blade.php
#省略
<div class="d-flex justify-content-between">
            <a href="#" data-toggle="modal" data-target="#delete-modal" class="btn btn-danger align-self-end">退会する</a>
            #省略
        </div>
```
こうすると赤い退会ボタンができました。
</div>の下はモーダルのソースコードを書きましたが、今回は公開しません。

# まとめ
公開できない部分もありましたが、実装の参考になると嬉しいです。

# 他に参考になる記事
省略の部分などは以下の記事を参考にしてください。

https://qiita.com/Hashimoto-Noriaki/items/bba6991683135a4a72f5
【勉強のやる気が出ないを即解決】行動を起こすことでやる気が高まる「行動先行仮説」とは？

# 行動先行仮説

やる気は待っているだけでは高まらない、行動を起こすことが鍵となる「行動先行仮説」について解説します。この現象は、脳科学や心理学の研究によって明らかになっており、私たちが仕事や勉強などのタスクに取り組む際に重要な意味を持っています。

従来、やる気は自然に湧いてくるものだと考えられがちでした。しかし、現代の脳科学研究では、実際には行動を起こすことでやる気が後からついてくることが示されています。

まず行動を起こすことが重要である理由は、自分がそのタスクに取り組むことができるという自己効力感が高まるからです。自己効力感が高まることで、やる気が継続して高まりやすくなります。また、行動を起こすことで脳内のドーパミンなどの報酬物質が放出されることがあり、これがやる気を引き出す役割を果たしています。

# どのようにすれば良いか
例えば、勉強を始める際には、まず環境を整えたり、短時間だけ集中してみるなど、小さな行動を起こすことが効果的です。
これによって、やる気が徐々に高まり、タスクに対する意欲や集中力が向上することが期待できます。

椅子に座ってノートを広げるだけでも構いません。
そうすると「作業興奮」と呼ばれるところまで行きます。
これが俗にいう「やる気スイッチ」です。

行動先行仮説を理解し、実践することで、待っているだけでは解決しないやる気の問題に対処できるようになります。

# さいごに
私はバーチャルコミュニケーションアプリのピクセルトークを開発しています。
よければどのようなアプリかホームページを見てみてください。
どうぞよろしくお願いします。

https://www.pixeltalk.app/
ATTiny202にCH340Kで書き込む

# TL;DR
- 素のavrdudeで行ける
- CH340Kなら抵抗もダイオードも要らない

# ?
## ATTiny85
2005年発表
一世を風靡したDigiSparkの正体はこれ
- 内蔵RC8MHzクロック
- 8bitタイマー*2
- 8kBフラッシュ
- PLL(これで64MHzクロックが作れる)
    - これがすごい
    - 主クロックに使えば16MHzで走る
    - PWMに使えば250kHz出せる
- ISPで書き込み
    - AVRマイコンにプログラムを書き込む手段として昔から用いられているインターフェース
    - 電源含めて6本の線を用いる

## ATTiny202
2018年発表
tinyAVR0シリーズ最廉価マイコン
- 内蔵RC20MHzクロック
- 16bitタイマー*2
    - 8bitに分割できるがピンが少ないのであまり恩恵はない
- 2kBフラッシュ
    - 流石に小さい
- 乗算器
    - 今までは加算で頑張っていた
- UPDIで書き込み
    - できることはISPと同じ
    - 電源含めて3本の線を用いる
    - 最近のAVRはみんなこれ

## CH340K
USBシリアル変換チップ
電流制限のダイオードを内蔵している
異なる電圧で動くチップとそのまま接続可能な優れもの
5V, 3.3V, 1.8Vは行けるとデータシートに書いてある
ESP32の書き込みによく使われる

## pyupdi
python製のUPDIドライバ
USBシリアル変換をUPDI書き込み機として振る舞わせることができる
元ネタは多分これ


# `avrdude -p serialupdi`
avrdudeの書き込み機のオプション`-p`にserialupdiがある
pyupdiの導入やavrdudeに細工などせずともこれで問題なく書き込める

以下に結線を示す
| USB | ch340k | 0.1uF | 0.1uF | t202 |
|:-:|:-:|:-:|:-:|:-:|
|VBUS| Vcc| @ |   | Vcc |
| D- | D- |   |   |     |
| D+ | D+ |   |   |     |
|    | TXD|   |   | UPDI|
|    | RXD|   |   | UPDI|
|    | V3 |   | @ |     |
| GND| GND| @ | @ | GND |

前述の通りCH340Kには電流制限の素子も入っているのでTXDRXDをそのまま繋げてしまって問題ない
CH340Kの注意点として
- 2つある0.1uFを付けないと動かない
- windowsの場合はzadig等を用いてusbドライバのインストールが必要

https://qiita.com/McbeEringi/items/d72c793d7fb079dc6997

を作った場合はジャンパ線のみあればそのまま流用できる
【Road to Cassandra Day】ドライバーからの接続とコネクションプールとの関係

# はじめに

今年、2023年６月１日に、[Cassandra Day](https://www.datastax.com/events/cassandra-day-japan)が日本でも開催されます。
Cassandra Dayは、昨年、ベルリン、ロンドン、アムステルダム、ハノイ、ジャカルタ、ヒューストン、サンタクララ、シアトル、シンガポールでも開催されました。

今回の東京での開催に向けて、Apache Cassandraに関する記事を発表していきます。

[![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176567/6a879803-d135-348b-f815-08be011607fa.png)](https://www.datastax.com/events/cassandra-day-japan)

# Apache Cassandraについて

Apache Cassandraとは、一言でいうなら、オープンソースの分散データベース管理システムです。

他の分散データベース管理システム同様、複数の汎用サーバーを用いて、ひとつのデータベースを構築します（開発などの目的のため、一つのサーバーのみで構成することも可能です）。

ここでは、詳しい説明は割愛し、興味のある方へのご紹介の役割は、公式サイトやWikipediaに譲ります。

https://cassandra.apache.org/_/index.html

https://en.wikipedia.org/wiki/Apache_Cassandra

## ドライバーからの接続とコネクションプールとの関係

今回は、ニッチな話題になりますが、Cassandraにおいては、**コネクションプールは（基本的に）不要**、という話をします。

急いで付け加えると、コネクションプールの概念・機能はCassandraに含まれています。
では、どういうことか？上記は、コネクションプールのデフォルト値は「１」になっており、この設定を変更することは、基本的に不要、という趣旨になります。

以下では、

 * なぜコネクションプールを必要がないのか？つまり、どんな点がRDBのコネクションプールの考え方と違うのか？
 * 「基本的には」と注釈した理由である例外が発生するケース

について、解説を加えます。


本稿の論拠となっている情報は以下のドキュメントです、適宜、詳細について、こちらを参照ください。

https://docs.datastax.com/en/developer/java-driver/4.14/manual/core/pooling/

## なぜコネクションプールが必要ないのか、あるいはRDBとの違い

（JDBC）コネクションプールは、データベースとの接続を維持する数のスレッドを作って管理する仕組み、といえます。これは、いわば同期通信に基づいています。

これに対して、Cassandraのクライアント・サーバー通信は、非同期です。クライアント（この場合、典型的にはアプリケーションサーバーを想定すると良いでしょう）とサーバー（クラスターを構成するノード）の間では、一つの接続上の非同期通信として、複数の（アプリケーションサーバー上の）データ通信（つまり、エンドユーザーのリクエストに対応するDB接続）が行われます。それぞれの通信の同一性・素性はストリームIDで管理されます。

コネクションプールの場合のスレッド数に相当する、同時接続の上限は、このストリームIDの上限に従うことになります。この上限はプロトコルのバージョンによって、以下のようになっています。

 - protocol v2 以前: コネクション毎に128のストリームID stream ids per connection.
 - protocol v3 以降: コネクション毎に最大32768のストリームID

ここから、特にv３以降については、同時接続数の条件は、およそ気にする必要がないと分かるでしょう。

また、ドライバーは、クラスターを認識し、標準的な環境では、クライアントからのコネクションは、複数ノードへ分散されます。

以下は、クライアントからのリクエストとクラスターとの間の１対多環境を示す図になります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176567/12f6326e-a7d0-f0d3-0e9e-b67191487ded.png)

## コネクションプールを使う（増やす）ケース

コネクションプールを増やす必要がありうるケースの典型は、１ノードクラスターです。
ドキュメントから、該当部分の記載を引用します。


>前述のように、プロトコル v3 のデフォルトのプール サイズは core = max = 1 です。これは、特定のノードへのすべてのリクエストが単一の接続を共有することを意味し、したがって単一の Netty I/O スレッドを共有します。

>この I/O スレッドが CPU コアを使い果たし、ドライバーのボトルネックになるまれなケースがあります。私たちのベンチマークでは、これは単一ノード クラスタと高スループット (約 80K リクエスト/秒) で発生しました。

>この問題が発生する可能性はほとんどありません。ほとんどの実際の展開では、ドライバーが複数のノードに接続するため、負荷がより多くの I/O スレッドに分散されます。ただし、問題が発生していると思われる場合は、次の点に注意してください。


上記のコーナーケースへの対応もドキュメントに記載されています。

とはいえ、ほとんどこのケースを機にする必要があることはないだろうと思います。

# 最後に

この記事では、Cassandraのドライバーからの接続に関連した、ありうべき誤解、特にコネクションプールに親しんでいる開発者が抱くかもしれないもの、に対して、あらかじめ注釈することでした。

ご参考になれば幸いです。
jetpack composeで見切れてるリストアイテムをちゃんと表示する

# 初めに
`jetpack compose`で`horizontalPager`を使ってるとヘッダー等でリストアイテムが見切れてしまうことがあると思います
それをちゃんと表示するためのロジックを今日は紹介していきます
### 本文
```kotlin
val headerState = rememberLazyListState()
fun adjustScroll(selectedPage: Int, forSwiped: Boolean = false) {
        scope.launch {
            if (!forSwiped) {
                pagerState.scrollToPage(selectedPage)
            }
            val headerVisibleList = headerState.layoutInfo.visibleItemsInfo.filter {
                val cutStart =
                    0.coerceAtLeast(headerState.layoutInfo.viewportStartOffset - it.offset)
                val cutEnd =
                    0.coerceAtLeast(it.offset + it.size - headerState.layoutInfo.viewportEndOffset)
                val visibilityPercent =
                    0f.coerceAtLeast(100f - (cutStart + cutEnd) * 100f / it.size)
                visibilityPercent >= 100f
            }
            if (pagerState.currentPage > headerVisibleList.last().index || pagerState.currentPage < headerVisibleList.first().index) {
                headerState.scrollToItem(pagerState.currentPage)
            }
        }
    }
```
まず、下記の部分はページングがスワイプでされたかボタンでされたかをみています
ボタンでされた場合はpageを進めないといけないのでその処理を書いています
```kotlin
if (!forSwiped) {
    pagerState.scrollToPage(selectedPage)
}
```
次に下記の部分でヘッダーで表示されているアイテムを取得し、その中で見切れずに表示されてるものをフィルタリングしています
```kotlin
val headerVisibleList = headerState.layoutInfo.visibleItemsInfo.filter {
                val cutStart =
                    0.coerceAtLeast(headerState.layoutInfo.viewportStartOffset - it.offset)
                val cutEnd =
                    0.coerceAtLeast(it.offset + it.size - headerState.layoutInfo.viewportEndOffset)
                // ここの100は表示されてる%で50にすれば半分見切れてるかどうか等を判定できます
                val visibilityPercent =
                    0f.coerceAtLeast(100f - (cutStart + cutEnd) * 100f / it.size)
                visibilityPercent >= 100f
            }
```
最後に表示している`horizontalPager`のページがヘッダーで見切れずに表示されているかどうかをみて表示されていなかった場合ヘッダーを該当箇所までスクロールするようにしています
```kotlin
if (pagerState.currentPage > headerVisibleList.last().index || 
    pagerState.currentPage < headerVisibleList.first().index) {
    headerState.scrollToItem(pagerState.currentPage)
}
```
### 最後に
自分用の備忘録程度に記事にしておきました
参考にしたのは下記のサイトです
https://stackoverflow.com/a/69267808
Vim 操作まとめと便利な使い方

<h1>Vimの操作をまとめる</h1>

最近、とある理由でテキストエディタをVimにしたので、操作方法などを備忘録的にまとめておく。便利な操作方法があったら付け足していくし、多分付け足すことを忘れるので、ここで一気にまとめたい。

<h1>モードと基本操作について</h1>

![スクリーンショット 2023-05-08 16.11.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/354334/9e17c7d8-d400-3104-6fac-f22c1ac0902c.png)
<h3>ノーマルモード</h3>
デフォルトのモード。Vim開いたらこのモードになっている。すべてのモードへの起点となるモード。

|コマンド　|操作  |
|------|---|
|`h`   |カーソルを左に移動 |
|`j`   |カーソルを下に移動 |
|`k`   |カーソルを上に移動 |
|`l`   |カーソルを右に移動 |
|`gg`  |ファイルの先頭に移動 |
|`G`   |ファイルの最後の行に移動 |
|`y`   |選択範囲ヤンク　|
|`yy`  |行ヤンク |
|`d`   |選択範囲削除　|
|`dd`  |行削除　|

<h3>インサートモード</h3>
文字を挿入するモード。コードか何か書きたいときはこのモード。

|コマンド　|操作  |
|------|---|
|`i`   |現在のカーソル位置でインサートモード移行 |
|`j`   |現在のカーソル位置の一つ後ろでインサートモード移行 |
|`k`   |行を一行追加してインサートモード移行 |
|`>`   |選択した範囲を1タブ右に移動 |
|`<`   |選択した範囲を1タグ右に移動|

<h3>ビジュアルモード</h3>
選択モード。選択の方法によってパターンがある。

|コマンド　|操作  |
|------|---|
|`v`         |一文字ずつの選択モード移行。 |
|`Shift+v`   |行選択モード。一行ずつの選択モード |
|`Ctrl+v`    |矩形選択モード。ブロックでの選択モード |

<h3>コマンドモード</h3>
別記事編集中。

<h1>便利な使い方</h1>
<h3>コメントアウト</h3>

1. 行の先頭にカーソルを合わせ**Ctrl+v**で矩形選択モードへ。
2. **Shift+i**でカーソルを行の先頭にもどして挿入モードへ。
3. //なり＃なりを行の先頭に挿入する。
3. Escで抜けると、選択したすべての行にコメントが入る。 

<h3>コメント削除</h3>

1. **Ctrl+v**で矩形選択モードへ。
2. 矩形選択モードで、コメントの部分のブロックを選択。
3. 選択のち、**d**で一括削除 

<h3>一斉インデント</h3>

1. 行の先頭にカーソルを合わせ**Ctrl+v**で矩形選択モードへ。
3. そのまま\>で一斉にTabが挿入される。反対に<で一斉にTabが削除される。
クラスメソッド

# クラスにクラスメソッドを定義する方法
クラスにメソッドを定義する方法は様々あります。
ただし、クラスメソッドを使わずに、モジュールをエクステンドした方が良い場合もあるそうなので、状況に応じて使っていくと良いみたいです。
## 1
これは見ることが多いですね。
```
class Foo
  def self.bar
    :baz
  end
end
```
## 2
なんか周りくどい、、、
```
class Foo 
end

def Foo.bar 
  :baz
end
```
## 3
あまり見ないですねー。
```
def (Foo = Class.new).bar 
  :baz
end
```
## 4
これもよく見る書き方だと思います。
```
class Foo 
  class << self
    def bar 
      :baz
    end 
  end
end
```
## 5
あまりこんな書き方をしませんが、以下のように書くこともできます。
```
class Foo 
end

class << Foo 
  def bar 
    :baz
  end 
end
```
## 6
instance_evalを使った書き方もあるみたいですが、あまり使わないようです。
```
Foo.instance_eval do 
  def bar
    :baz
  end 
end
```

## 7
こんな書き方もできますね。
```
class Foo 
  define_singleton_method(:bar) do
    :baz
  end 
end
```