OmniAICreator/Qiita-1.07M huggingface dataset:
【ShellScript】記述方法ついて

インフラエンジニアのため、構築や運用等で使用するシェルスクリプトを作成するようになりました。
シェルスクリプトの作成は初めてのため、業務中に学習したことを記載します。

## シェルスクリプトとは
Linux OSのコマンドを上から順に実施するスクリプトです。
普段ならMacのターミナルやWindowsのTeraTermでLinuxのコマンドを手動で実行しますが、
.shファイルにコマンドを記載し、そのファイルを実行することで.shファイルに記載されていることが実行されます。
bash, sh, csh, zshで書き方が少し異なります。

## 使用シェルを定義する
シェルスクリプトでは、ファイルの頭に以下のような記載をします
```
#!/bin/bash
```
こちらの意味は使用するシェルの場所を指定してます。
使用できるシェルは```cat /etc/shells```で確認できます
```
$ cat /etc/shells
/bin/bash
/bin/csh
/bin/dash
/bin/ksh
/bin/sh
/bin/tcsh
/bin/zsh
/usr/local/bin/zsh
```
私のMacbookでは上記シェルが使用できるようです

## 実行方法
まずファイルに実行権限を付与します
```
$ chmod 755 test.sh
```
実行方法は以下になります
```
$ ./test.sh
```
権限が足らないと以下のようにはじかれます。
```
$ ./test.sh
zsh: permission denied: ./test.sh
```
## confファイル内の変数を読み込む
サーバ名はドメイン名などの固有情報を.shファイルに記載したくない場合、confファイルに
記載し、.shファイル内で読み込むことでconfファイルに記載している変数を読み込むことができます。
例として以下のように記載します
```test.conf
USER="testuser"
SERVER="xxx.xxx.xx.xx"
```
```test.sh
#!/bin/bash
. test.conf
echo ${USER}
echo ${SERVER}
```
以下で実行結果です。
```
$ ./test.sh 
testuser
xxx.xxx.xx.xx
```
confファイルに定義した内容がechoで表示されました。

## if文
条件で処理を分岐させます。

```
if [ 条件式 ]; then
  処理内容
fi
```
if文は、ifで始まり、fiで終わります。
比較演算子では以下のようになります。
* 数値の比較
数値の比較は以下になります（）の部分は個人的な覚え方です(あっているかどうかは)

| 比較演算子  | 内容 |
|-----------|------|
| 数値1 -eq 数値2  | 数値１と数値2が等しい(equal)場合True |
| 数値1 -ne 数値2  | 数値１と数値2が等しくない(not equal)場合True |
| 数値1 -gt 数値2  | 数値１が数値２より大きい(greater than)場合True |
| 数値1 -ge 数値2  | 数値1が数値２以上(greater equal)の場合True |
| 数値1 -lt 数値2  | 数値1が数値2より小さい(little than)の場合True |
| 数値1 -le 数値2  | 数値1が数値2以下(little equal)の場合True |

* 文字列の比較
文字列の比較は以下になります。

| 比較演算子  | 内容 |
|-----------|------|
| 文字列1 = 文字列2  | 文字列1と文字列2が等しい場合True |
| 文字列1 != 文字列2  | 数値１と数値2が等しくない場合True |
| -n 文字列  | 文字列の長さが1以上の場合True |
| -z 文字列 | 文字列の長さが0の場合True |

また、if文のつなげたい場合はelifで繋げることができる
```
if [ 条件式1 ];than
  内容1
elif [ 条件式2 ]; than
  内容2
else
  内容3
if
```
上記の場合、条件式1を満たす場合、内容１、
条件式2を満たす場合、内容2、
どれも満たさない場合、内容３が実行されます。

## for文
繰り返し実行するものはfor文を使用します
```
for 変数名 in リスト名
do
  処理内容
done
```
以下実行例です
```test.sh
#!/bin/bash
for FRUIT in apple banana cherry
do
  echo ${FRUIT}
done
```
実行結果は以下
```
$ ./test.sh
apple
banana
cherry
```
## 最後に
シェルスクリプトで運用スクリプトを作成するときは、
対象ログや標準出力等に表示するようにリダイレクトや、
awkなどを使用してこのファイルのこの部分を取得して変数に入れるなどの処理が多いと思います。
Linuxコマンドに詳しくなると幅広いシェルスクリプトが書けるようになると思うので、
特にインフラエンジニアでコードを書いたことがない人は、
シェルスクリプトとLinuxコマンドの学習が必要と感じました。

## 参考文献
https://shellscript.sunone.me/for.html

https://www.pasonatech.co.jp/workstyle/column/detail.html?p=7450
Bokeh　ホバーで表示されるデータが多いときの対処法

Bokehで、ホバーツールつきでローソク足チャートをプロットするとき、
ホバーで表示されるデータが多くなりすぎて、ダサくなることがあります。

[公式サイトのローソク足の例](https://docs.bokeh.org/en/latest/docs/gallery/candlestick.html)でも、MSFTデータをフルに使って、TOOLSにhoverをいれると、ホバーで表示されるデータが10点分くらいになったりします。あまり実害はないんですが、、、

しかしまあそんなときは、ホバーで表示するデータを、renderersに適切に指定することで対処できます。
以下は公式サイトのローソク足の例に追記するところだけ記載しています。

```python
from bokeh.models import HoverTool

・・・

r1 = p.vbar(
    df.date[inc], w, df.open[inc], df.close[inc],
    fill_color="#D5E1DD", line_color="black"
)
r2 = p.vbar(
    df.date[dec], w, df.open[dec], df.close[dec],
    fill_color="#F2583E", line_color="black"
)
p.add_tools(Hovertool(renderers=[r1, r2], mode='vline'))

・・・
```

これでデータが多くなっても、ホバー時に表示されるデータが一点分になって、スマートです。
LocalからGitHubにUploadしてみた

PCからGitHubにファイルやフォルダのアップロード(最初のステップ）

1．GitHubからレポジトリを作成
2．Git Bash
　　フォルダアップロードの場合はアップロードしたいフォルダの親フォルダに入る
　　ファイルアップロードの場合はファイルが入っているフォルダに入る
3．git init　//ローカルレポジトリを作成
4．git config --global user.email "メールアドレス"　//GitHubに加入したメールアドレス
5．git config --global user.name "ユーザー名"　//GitHubのユーザー名
6．git status
7. git remote add origin https://github.com/GitHubID/レポジトリ名　//アップされるレポジトリを指定
8. git remote -v
9. git add フォルダ名orファイル名
10. git status
11. git commit -m "コメント" //  入れたいコメントを入力
12. git add フォルダ名orファイル名 // 9番の繰り返し
13. git push --set-upstream origin master //この命令は初回のみで, 2回目からはgit pushだけ入力
14. レポジトリにアップされたか確認

同じフォルダ内・同じレポジトリでの作業なら9番からで良い
Navigation2で半永久機関を作りたい。

## はじめに

Navigation2すごく便利で賢い。誰でも簡単に自動走行ができる。
ただ、じゃあいざ、商品化しようとすると、実際はなんの操作もせずに、ずっと動き続ける
ロボットのほうが喜ばれることが多い。そこでアイディアを形にするため、半永久的に動き続けるTurtlebot3ができないかと考えた。

## やりたいこと
1. 設定は簡単であること
1. 一度設定するとずっと動き続ける


## 方針
Goalにたどり着く前に、Pathを書き換え、Startに戻る。
上により、半永久的にGoalにたどり着かなければ、行ったり来たりするロボットができる

## 参考にしたページ
Pathのページ
https://navigation.ros.org/plugin_tutorials/docs/writing_new_nav2planner_plugin.html


## DEMO
ゴールを設定すると、半永久的にStartとGoalの間を行き来するTurtlebotができた。
![Peek 2022-05-18 23-22.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/93168/810986f7-f528-b2fd-8a30-9d2b3b29af20.gif)
【Swift】週初めの日付を取得する

## はじめに
ある日付からその日が含まれる週初めの日付を取得したい場面ってありませんか？
僕なりに考えて実装してみたので備忘録として残します。

## 環境
- Swift: version 5.6
- Xcode: Version 13.3.1 (13E500a)
- macOS: 12.3.1 (21E258)

## 手順

やり方はたくさんあると思いますが、今回は下記の手順で処理をします。

1. 当日の0:00の日時を取得
2. 曜日番号を取得
3. 曜日番号から日時の演算を行う
4. 週初めの日付を取得

## 実装

`extension`で`Calendar`のメソッドとして実装してみました。
```swift
import Foundation

extension Calendar {
    //月曜始まりor日曜始まり
    enum startOfWeek {
        case sunday
        case monday
    }

    func initialDayOfWeek(_ date: Date = Date(), start: startOfWeek = .sunday) -> Date {
        //当日0:00の日時を取得
        let am = self.startOfDay(for: date)
        //曜日番号を取得
        let weekNumber = self.component(.weekday, from: am)

        if start == .sunday { //日曜始まりの場合
            return self.date(byAdding: .day, value: -(weekNumber - 1), to: am)!
        } else { //月曜始まりの場合
            if weekNumber == 1 {
                return self.date(byAdding: .day, value: -6, to: am)!
            } else {
                return self.date(byAdding: .day, value: -(weekNumber - 2), to: am)!
            }
        }
    }
}
```

## 動作確認

注意点は1点。
そのまま出力すると9時間遅れになってしまうので、実際に出力する時は`DateFormatter`を使用して下さい。
```swift:
let calendar = Calendar.current

//今日
let a = calendar.initialDayOfWeek()
let b = calendar.initialDayOfWeek(start: .monday)
//5月6日
let date = calendar.date(from: DateComponents(year: 2022, month: 5, day: 6))!
let c = calendar.initialDayOfWeek(date)
let d = calendar.initialDayOfWeek(date, start: .monday)

//そのまま出力（9時間遅れ）
print(a)
print(b)
print(c)
print(d)

print("----------------------------------")

var dateFormater: DateFormatter {
    let df = DateFormatter()
    df.calendar = Calendar(identifier: .gregorian)
    df.locale = Locale(identifier: "ja_JP")
    df.timeZone = TimeZone(identifier: "Asia/Tokyo")
    df.dateStyle = .full
    df.timeStyle = .long
    return df
}
//DateFormatterで調整して出力
print(dateFormater.string(from: a))
print(dateFormater.string(from: b))
print(dateFormater.string(from: c))
print(dateFormater.string(from: d))

```

### 実行結果

```
2022-05-14 15:00:00 +0000
2022-05-15 15:00:00 +0000
2022-04-30 15:00:00 +0000
2022-05-01 15:00:00 +0000
----------------------------------
2022年5月15日 日曜日 0:00:00 JST
2022年5月16日 月曜日 0:00:00 JST
2022年5月1日 日曜日 0:00:00 JST
2022年5月2日 月曜日 0:00:00 JST
```

期待通りに出力されています。

## さいごに
週初めの日付を取得する方法をまとめてみました。
さいごまで見ていただきありがとうございました。

## 参考記事

https://capibara1969.com/2100/#toc3

https://www.choge-blog.com/programming/swiftdateandtimeweekday/
macOS(Monterey)のisoイメージをmacで作成する

作成日: 2022-05-18

## この記事は何？
macOS(Monterey)のisoイメージをmacで作成した備忘録です。基本的には参考文献の欄に載せた記事の手順のままです(とても良い記事です)。より詳細は是非そちらを参照するようにしてください。

#### 実行環境
- macbook Air(2019)
- macOS Monterey ver12.3.1

## 参考文献
https://souiunogaii.hatenablog.com/entry/macOS-BigSur-createISO

https://souiunogaii.hatenablog.com/entry/macOS-Catalina-createISO

## インストーラのダウンロード
App storeからmacOS Monterey インストール.appをダウンロードしておきます。
以下ではこれは「アプリケーション」フォルダに入っていることを前提としています。

## step.1 イメージファイルの作成
hdiutilコマンドを使ってイメージファイルをスパースファイルで作成します。初め10gbで作成すると3.68gb足りないと言われたので余裕をみて15GBで作成し直しました。
```
hdiutil create -o /tmp/monterey -size 15g -layout SPUD -fs HFS+J -type SPARSE
```

## step.2 イメージのマウント
上記で作成したイメージファイルをマウントします。

```
hdiutil attach /tmp/monterey.sparseimage -noverify -mountpoint /Volumes/installMonterey 
```

## step.3 インストーラの作成
マウントしたイメージファイルにインストーラを作成します。

```
sudo /Applications/Install\ macOS\ Monterey.app/Contents/Resources/createinstallmedia --volume /Volumes/installMonterey --nointeraction
```
## step.4 イメージファイルをアンマウントする
```
hdiutil detach /Volumes/Install\ macOS\ Monterey/
```

## step.5 cdrファイルの生成
sparseファイルをcdrファイルに変換します(デスクトップに生成しています)。
``` 
hdiutil convert /tmp/monterey.sparseimage  -format UDTO -o ~/Desktop/installMonterey.cdr
```
## step.6 リネーム
cdrファイルの拡張子をisoに変更します(右クリックメニューでそのまま変更した)。

## step.7(optional)
sparseイメージは用済みなので削除します。
```
rm /tmp/monterey.sparseimage
```
------
この記事は以上です。
403 Forbidden

１時間に３０から５０くらい編集すると、

403 Forbidden

が出て、何もできなくなることがある。

機械アクセスという判定をしているのかもしれない。

特定の利用者で、人手で作業をしていることがわかっているIDは、除外して欲しいかもしれない。

たしかに、乗っ取られて編集が活発になったかどうかの判定ができないのかもしれない。

それなら、質問ページに飛ばして、人が答える問題を出せばいいような気がする。

どこまで管理を学習で自動判定するかは、
実際に自動判定ではまずい例外を組み込むとよいかもしれない。
REST API Design Best Practices を用いたAPI開発 #2 (APIのVersion管理)


本記事は[REST API Design Best Practices を用いたAPI開発 #1](https://qiita.com/ryumura/items/9ee28d43ec27edc36e2e)の続きです。



## 動機
[REST API Design Best Practices を用いたAPI開発 #1](https://qiita.com/ryumura/items/9ee28d43ec27edc36e2e)と同様、以下の記事をFollowしながら手を動かすことで、少しでも昨今の流れ・感覚をCatch Upをすること、また自分自身のメモとして残すことを目的として記事に起こす。

https://www.freecodecamp.org/news/rest-api-design-best-practices-build-a-rest-api/

## 本記事で実施すること
* APIのVersion管理
* URLを用いたVersion管理の実装
* サーバの起動とアクセス確認

## 環境
[REST API Design Best Practices を用いたAPI開発 #1](https://qiita.com/ryumura/items/9ee28d43ec27edc36e2e)と同様

## APIのVersion管理

本記事に記載するAPIのVersion管理は、
Best Practiceの1つとして**URLを用いたVersion管理**を行う。

```
// Version 1 
"/api/v1/workouts" 

// Version 2 
"/api/v2/workouts" 
```
URLを用いたVersion管理のメリットは以下の通り。

* 新しいバージョンのリリースは、既存バージョンのAPIに影響を与えない
* 新しいバージョンへの切り替えをクライアント側に強制する必要がない。切り替えタイミングはクライアント側で判断・コントロールが可能
* 複数のバージョンのAPIをお互い影響を与えることなく並行で稼働させることが可能


### v1フォルダの作成
v1フォルダをsrc配下に作成する。
```
mkdir src/v1
```

続いて、src/v1の中にsrc/routesフォルダの移動。
```
# current directoryを取得 
pwd 

# "routes"を"v1"配下に移動 
mv {pwd}/src/routes {pwd}/src/v1
```
これにより、/src/v1/routesにはVersion1のroutesを格納していくことになる。 
例えばindex.jsを作成してみる。
```
# In /src/v1/routes 
touch index.js
```

src/index.js → /src/v1/routes/index.jsへRoutingさせる。

```javascript:src/index.js
// In src/index.js
const express = require("express");
// *** ADD ***
const v1Router = require("./v1/routes");

const app = express();
const PORT = process.env.PORT || 3000;

// *** REMOVE ***
app.get("/", (req, res) => {
  res.send("<h2>It's Working!</h2>");
});

// *** ADD ***
app.use("/api/v1", v1Router);

app.listen(PORT, () => {
  console.log(`API is listening on port ${PORT}`);
});
```
これでURLを用いたVersion管理の実装完了。以下のようなフォルダ階層になっているはず。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2664640/6ef61190-d336-5dcf-99b0-9fec707f47e6.png)


## サーバ起動
以下のコマンドでサーバを起動させる。
```
npm run dev
```
正常に起動していれば、以下のメッセージが出力される。
```
API is listening on port 3000
```

:::note info
インフォメーション
nodemonを利用し、コードの変更を監視して自動でサーバーは再起動されるため、サーバが既に起動済であれば不要。
:::

## アクセス
ブラウザから、**localhost:3000/api/v1**にアクセスし正常に起動していることを確認する。 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2664640/d6ff4c38-bd47-7918-f31a-399700e296dd.png)

上記より/api/v1に対するリクエストを、/src/v1/routes/index.jsにRoutingし処理させることに成功。

なお、Routesフォルダ同様、services、controllerフォルダも同様にv1配下に配置させることで同じ要領でVersion管理が可能。
今回の例のような小規模なAPI開発であれば、すべてをv1配下でバージョン管理する必要はないが、大規模なAPI開発になってくるとservices、controllerフォルダなど他のコンポーネントについても、v1配下でバージョン管理を行う方が良い。
そうすることでメリットに記載した**複数のバージョンのAPIをお互い影響を与えることなく並行で稼働させることが可能**というコンセプトを遵守できる。


## まとめ
本記事では以下を実施した。
* APIのVersion管理
* URLを用いたVersion管理の実装
* サーバの起動とアクセス確認

次回も引き続き以下Followしながら続けていく。
https://www.freecodecamp.org/news/rest-api-design-best-practices-build-a-rest-api/
Javascriptでクリックでclassを変更

##　方法
[参考記事](https://www.delftstack.com/ja/howto/javascript/javascript-toggle-class/)


```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>Document</title>
</head>
<body>
 <style>
 .toggleClass {
  font-size: 30px;
  color: blue;
}
 </style>

<p id="text">
  クリックするとスタイルが変わります。
</p>
<button onclick="toggleFunc()">Button</button>


<script>
function toggleFunc() {
    let text = document.getElementById("text");
        text.classList.toggle("toggleClass");
}
</script>
</body>
</html>
```

上記で実装可能
【Rails】ERROR: Can't install RMagick 4.2.5. Can't find the ImageMagick library or one of the dependent libraries.の対処法

mac OS バージョン11.6
エディタ…VScode
Rails 6.0.4

# やりたいこと
bundle installした時にエラーが出ないようにしたい

# エラー内容
```
% bundle install
（省略）
Gem::Ext::BuildError: ERROR: Failed to build gem
native extension.

（省略)
ERROR: Can't install RMagick 4.2.5.
Can't find the ImageMagick library or one of the
dependent libraries.
Check the mkmf.log file for more detailed　information.

（省略)
An error occurred while installing
rmagick (4.2.5), and Bundler cannot continue.
```

だいぶ端折りましたが、エラー文で文字が赤くなっている部分だけ抜粋しました。


# 原因
```
Can't install RMagick 4.2.5.
Can't find the ImageMagick library or one of the
dependent libraries.
```
↓
（和訳）
RMagick4.2.5をインストールできません。
ImageMagickライブラリまたは依存ライブラリの1つが見つかりません。


<br>
よって原因は、「必要なライブラリがインストールされていない為」ということになります。



# 解決策
必要なライブラリを導入した後、再度gem installするだけです。
（↓こちらの記事が大変参考になりました。）
https://qiita.com/yukiweaver/items/52c45f442b9d9d59367e


順に実行していきます。


１.Homebrew で ImageMagick 6 と pkg-config をインストールする
```
$ brew install imagemagick@6
$ brew install pkg-config
```

<br>
2.環境変数 PKG_CONFIG_PATH を指定する

PATHの指定ですが、上記の ```% brew install imagemagick@6``` コマンドの入力に成功した場合、下の方に以下のような説明が表示されるかと思います。

```
If you need to have imagemagick@6 first in your PATH, run:
  echo 'export PATH="/usr/local/opt/imagemagick@6/bin:$PATH"' >> ~/.zshrc

For compilers to find imagemagick@6 you may need to set:
  export LDFLAGS="-L/usr/local/opt/imagemagick@6/lib"
  export CPPFLAGS="-I/usr/local/opt/imagemagick@6/include"

For pkg-config to find imagemagick@6 you may need to set:
  export PKG_CONFIG_PATH="/usr/local/opt/imagemagick@6/lib/pkgconfig"
```

これの一番最後の部分をそのまま実行してPATHを通します。以下を実行。

```
export PKG_CONFIG_PATH="/usr/local/opt/imagemagick@6/lib/pkgconfig"
```

3.gemコマンドで rmagick をインストールする

```
$ gem install rmagick
```
これで準備が整いましたので、Gemfileを以下のようにして、```bundle install```実行。


```.Gemfile
gem 'carrierwave'
gem 'rmagick'
```

```
# bundle install
```
以上です。
k8s command memo


my memo

## kubectl

[cheat sheet](https://kubernetes.io/docs/reference/kubectl/cheatsheet/)

```bash
# view
$ kubectl config view
$ kubectl cluster-info
$ kubectl get namespace

# edit
$ kubectl create namespace <ns>
$ kubectl apply -n <ns> -f xxx

# config
$ kubectl config get-contexts
$ kubectl config use-context <context name>
$ kubectl config current-context

```

## kind

[Getting Start](https://kind.sigs.k8s.io/docs/user/quick-start/)

```bash
$ kind get clusters
$ kind create cluster --name <cluster name> --config <yaml>
$ kind delete cluster --name <cluster name>>

# M1 mac の罠
$ kind create cluster --name xxxx --config xxx --image rossgeorgiev/kind-node-arm64:v1.21
```

## k9s

[command doc](https://k9scli.io/topics/commands/)

```bash
$ k9s --readonly
$ k9s -n <namespace>

# k9s command
:ctx  context list
:ns   namespace list
:pod  pod list
:portfoword

l       log
y       yaml
ctrl-d  delete
```

## docker container registory

api : [Registry API](https://docs.docker.com/registry/spec/api/)

```
$ curl <host>:<port>/v2/_catalog
$ curl <host>:<port>/v2/<name>/tags/list
$ curl <host>:<port>/v2/<name>/manifest/<reference>
```

## GKE

TODO

```bash
# add context
$ gcloud container clusters get-credentials



# mcs
$ kubectl describe serviceimport --context xxxx
$ kubectl describe serviceexport --context xxxx
$ kubectl get endpoints -n xxxx
$ kubectl describe endpoints -n xxxx <endpoint name>
```

## minukube

TODO
pip freezeコマンドが叩けない場合にモジュールから呼び出して実行する

requirements.txtを作成したいが、Windows + PyCharm環境でpip freezeしようとすると権限の関係で実行できなかったときに対処した方法

コマンドがダメならモジュールを呼べば良い。

```a.py
import pip

pip.main(['freeze'])
```

```
> python3 a.py > requirements.txt
```
Compose Multipreview Annotationsで複数のバリエーションのPreviewを表示させる

Jetpack Compose 1.2 と Android Studio Dolphin 以降の組み合わせで Compose Multipreview Annotations の仕組みを使い、複数の `@Preview` を一つの Annotation で表現することができます。

https://developer.android.com/studio/preview/features#compose_multipreview_annotations

## 実装

これまで、例えば Light Theme と Dark Theme の Compose のプレビューを表示したい場合は、以下のように複数の `@Preview` を使って実装していました。

```kotlin
@Preview
@Preview(
    uiMode = Configuration.UI_MODE_NIGHT_YES
)
@Composable
fun PreviewMilestoneItem() {
    MilestoneItem()
}
```

<img width="200px" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/126298/8b371393-45fd-bde8-397b-40ea2ebe37d1.png" />


これが Compose Multipreview Annotations を使って複数の `@Preview` をまとめることができます。
上の例をまとめるとこのようになります。  

```Kotlin
@Preview(
    name = "Light theme",
    group = "Themes",
)
@Preview(
    name = "Dark theme",
    group = "Themes",
    uiMode = Configuration.UI_MODE_NIGHT_YES
)
annotation class ThemePreviews

// usage
@ThemePreviews
@Composable
fun PreviewMilestoneItem() {
    MilestoneItem()
}
```

<img width="200px" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/126298/bb378422-2cb8-d685-e9a4-e0bdb70c67b3.png" />

## ユースケース
まとめた Annotation を他の Preview で使いまわせるので、プロジェクトで一つ用意しておけばあらゆるところで共通の Preview の設定にすることができます。  
また、まとめた Annotation を複数同時に指定したり、まとめた Annotation に別のまとめた Annotation をつけることもできます。

```kotlin
@Preview(
    name = "Small font",
    group = "Font",
    fontScale = 0.5f
)
@Preview(
    name = "Normal font",
    group = "Font",
    fontScale = 1.0f
)
annotation class FontScalePreviews

@Preview(
    name = "Pixel 4",
    group = "Device",
    device = Devices.PIXEL_4,
    showSystemUi = true
)
@Preview(
    name = "Foldable",
    group = "Device",
    device = Devices.FOLDABLE,
    showSystemUi = true
)
annotation class DevicePreviews


@Preview(
    name = "Light theme",
    group = "Theme",
)
@Preview(
    name = "Dark theme",
    group = "Theme",
    uiMode = Configuration.UI_MODE_NIGHT_YES
)
annotation class ThemePreviews

// usage
@FontScalePreviews
@DevicePreviews
@ThemePreviews
@Composable
fun PreviewMilestoneItem() {
    MilestoneItem()
}

or

@FontScalePreviews
@DevicePreviews
@ThemePreviews
annotation class CompletePreviews

@CompletePreviews
@Composable
fun PreviewMilestoneItem() {
    MilestoneItem()
}
```

<img width="500px" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/126298/e57eedc3-b0f8-0dd8-c4f1-6a3373b83383.png" />

`@Preview` に `group` を指定しておくことで、プレビューから指定した `group` だけの表示も可能になります。

<img width="500px" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/126298/84b2cc7d-553d-2a46-fbd0-eace55452986.png" />
docker in docker 2022年版

# やりたいこと
Dockerコンテナでdockerコマンドを利用したい(docker in docker)
昔からよくやっているが古い記事が多いので2022年の情報としてメモを残す

# 1.[公式イメージ](https://hub.docker.com/_/docker)を使う
[公式イメージ](https://hub.docker.com/_/docker) をdocker pullして使う。
公式ドキュメントがわかりやすいのでdockerコマンドだけ使いたいならこちらがよい。

# 2.[binファイルのみ配備](https://docs.docker.com/engine/install/binaries/)する
[binファイルをダウンロード](https://docs.docker.com/engine/install/binaries/) 然るべき場所に配備する。
大抵の場合jenkinsやその他タスクランナーコンテナでdockerコマンドを使いたいだけだろうから以下のようにdockerクライアントだけバイナリインストールする。

```Dockerfile
ENV DOCKER_CLIENT_VERSION=20.10.16
ENV DOCKER_API_VERSION=1.４１
RUN wget -O - https://download.docker.com/linux/static/stable/x86_64/docker-${DOCKER_CLIENT_VERSION}.tgz \
    | tar -xzC /usr/local/bin --strip=1 docker/docker
```

# 以下のような古いバイナリインストールはやめましょう🙅
2016年くらいから残っている古いbinファイルなのでやめたほうがいいと思われる。

```Dockerfile
ENV DOCKER_CLIENT_VERSION=1.12.3
ENV DOCKER_API_VERSION=1.24
RUN wget -O - https://get.docker.com/builds/Linux/x86_64/docker-${DOCKER_CLIENT_VERSION}.tgz \
    | tar -xzC /usr/local/bin --strip=1 docker/docker
```
Linux版OperaでH.264コーディックの動画を再生する 

# はじめに
Linuxを最近使い始めた．
もともとはWindowsでOperaブラウザを普段遣いしていたのでLinuxでもOperaを使おうとSnapでダウンロードしたところ，起動が長い...ログイン後の初回の起動は実に1分以上かかる...

調べたところOperaに限らずSnapのデスクトップアプリは起動が長いのがよく問題になっているそう．そこでdeb版のOperaを利用してみたところ起動の遅さは改善された

しかし，それとは別にdeb版ではH.264コーディックの動画(TwitterやYoutube Liveなど)が再生できないという問題が発生したので，H.264コーディックの動画を再生するための対処を残しておく

# 本題
対処としてはffmpegを使って動画を再生できるようにする
## ffmpegパッケージをダウンロード
検索欄に"opera ffmpeg"と入力し`opera-ffmpeg-codecs`を探す

https://archlinux.org/packages/?sort=&q=opera+ffmpeg&maintainer=&flagged=

この際，パッケージのバージョンが`opera://about`のブラウザIDのChromeのバージョンと同じであることを確認する

![Screenshot_20220518_221001.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/519419/3f18aef1-8869-439c-e6d8-9f99e09ccc32.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/519419/feb58cf4-73e0-73bc-3082-a7928d05d0e5.png)

## ffmpegをOperaに反映
ダウンロードしたディレクトリを展開し，中のファイルを`/usr/lib/opera/lib_extra`以下に配置する
```sh
$ sudo mkdir /usr/lib/x86_64-linux-gnu/opera/lib_extra
$ sudo mv opera-ffmpeg-codecs-[バージョン]-x86_64.pkg/usr/lib/opera/lib_extra/libffmpeg.so /usr/lib/x86_64-linux-gnu/opera/lib_extra/
```

## Operaを再起動
これでffmpegを利用できるようになったはずなので再起動すれば動画が再生できるようになっている
html5test.comなどでH.264コーディックが対応しているか確認することもできる

https://html5test.com

![Screenshot_20220518_222928.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/519419/d9b0aaed-ae7f-2d43-a081-1fcd0060e74f.png)

# おわり
これでひとまずはLinuxでも快適にOperaが使えるようになった
でも，Snap版のほうがインストールが楽なのでSnap版のOperaの起動が早くなってほしいなあとも思ったり
古いLaravel + tailwind で npm run watch が反映されない問題

# はじめに
殴り書きですが、他に発生した方の参考になればと思い残しておきます。

# 環境
```
Laravel: v5.8
TailwindCSS: v3.0.23
Webpack: v4.44.2

MacOS Monterey 12.4
```

# 発生した現象
現在の設定では、`tailwind.config.json` の `content` 内にあるファイルのみを監視し、不要なクラスを生成しないようになっています。
`npm run development` では正常にクラス生成が行われているのですが、`npm run watch` では2回目以降の差分を書き出してくれません。

```json
"scripts": {
  "development": "node node_modules/cross-env/dist/bin/cross-env.js NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --config=node_modules/laravel-mix/setup/webpack.config.js",
  "watch": "npm run development --watch",
}
```


# 試したこと

## 1: `TAILWIND_MODE=watch` を設定してみる

Tailwind公式ドキュメントの [#Styles don't update when saving content files](https://v2.tailwindcss.com/docs/just-in-time-mode#styles-don-t-update-when-saving-content-files) セクションにある、CSSファイルが rebuild されない場合はこちらを試してください というものを発見。

残念ながら解決はできなかった。

## 2: Laravel Mix のキャッシュを疑ってみる

Laravel Mix のキャッシュが原因かと思い、ハッシュによるキャッシュの更新がされるように試みた。
`.version()` をつけ、Laravelのグローバルmix関数で読み込むことにした。

```js
mix.js('resources/js/app.js', 'public/js')
  .sass('resources/sass/app.scss', 'public/css')
  .options({
    processCssUrls: false,
    postCss: [ tailwindcss('./tailwind.config.js') ],
  }).version();
```

しかし、これもダメだった。


# 解決方法
`tailwind.config.js` 内の `content` が正常に取得できていないのではないかと考え、それ関係で解決方法を模索してみた。
すると、こちらの [リビルドが無限ループする際の対応ドキュメント](https://tailwindcss.com/docs/content-configuration#styles-rebuild-in-an-infinite-loop) を見つけた。

今回の問題とは異なるが、このドキュメントに「`glob` オプションがサポートされていないことが原因であり、`webpack` のようなビルドツールではこれがサポートされていないためこのような現象が発生する」 と書かれていた。

そのため、`tailwind.config.js` ファイルを以下のようにすることで解決した。
```js
  content: require('fast-glob').sync([
    './storage/framework/views/*.php',
    './resources/**/*.blade.php',
    './resources/**/*.js',
    './resources/**/*.vue'
  ]),
```

# 参考文献
https://stackoverflow.com/questions/71160737/tailwind-laravelmix-infinity-loop-on-watch
Python で変数の使い回し (再代入の繰り返し) をすると、型ヒントは容易く無力になる。

## 要約

- Python を書く時、変数は再代入をしないほうが良い。 (この記事内容に関わらず)
- 変数の使い回し (再代入の繰り返し) をすると、ときに型ヒントは無力になる。
- この現象を VSCode + Pylance の環境で確認した。


## イントロ

業務や趣味などで実装を行っていると、気づけばコード量は 3 桁、時には 4 桁に差し掛かるような python ファイルが出来てしまう。
こういったファイルにおいて処理の追加やバグ修正など行っている時に、ふと VSCode + Pylance 上で補完がうまくできないケースが発生していた。

例えば `a: Foo = Foo()` といった型ヒントを書いているものの、次の行あたりで VSCode 上では `a:Bar()` と認識されている。
一見すると、変数 `a` では Foo クラスに実装したメソッド `Foo.hoge()` のようなものが使えないように見えてしまっていた。

直前に `a:Foo = Foo()` と型ヒントを明示的に書いているのに、次の行では VSCode 上ではエラーの表示。
明らかに意図していない状況ではあるが、ざっと同ファイルを読むと以下のような状況になっていることがわかった。

1. 同変数 `a` を for 文や if 文などを使いながら、何度も再代入をしていた。
2. 変数 `a` の型ヒントの誤認識は複数箇所で起きている。
3. 1. と同じように何度も再代入をしている変数、例えば `b` も同じく型ヒントの誤認識が発生している。

そのとき作業していた環境では、「for 文によって似たような処理を繰り返すので、可読性を考えて似た処理については同変数名で統一しておこう」といった考えを過去に持っていたので、このような使い回し (再代入の繰り返し) が発生していた。

とはいえ、この現象は単にファイルの記述量が多すぎるがゆえに、適切な処理ができなかったのでは？と考えられる部分もある。
もし行数が起因するなら、適切なファイル分割をすれば回避できる問題かもしれない。
そうであれば、どうせリファクタリングも予定しているし、Pylance の型ヒントがどれほど壊れやすいか検証してみよう。

## 実験

ひとまず、条件分岐を使うことで変数 `a` に何度も違う型ヒントを与えつつ、再代入を繰り返すとどうなるか検証

### 環境


- Python 3.7.12
- VSCode
    - 拡張機能: [Python](https://marketplace.visualstudio.com/items?itemName=ms-python.python) 
    - LSP: [Pylance](https://marketplace.visualstudio.com/items?itemName=ms-python.vscode-pylance)

### 簡単なコードによる検証

コード自体は無意味だが、やりたいことの最小限の再現は以下。

```python
condition: bool = False

a:int = 0

if condition:
    a:str = "0"

a

if condition:
    a: list = [0]

a

```


### 結果

上記コードにおいて、それぞれの型が何かを VSCode 上で確認してみる。


![Screenshot from 2022-05-18 15-32-02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132293/e98192c7-0e97-d800-0428-d1db1d65df85.png)
![Screenshot from 2022-05-18 15-32-07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132293/11a27037-371c-72b9-6133-ac71145f294f.png)
![Screenshot from 2022-05-18 15-32-11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132293/5f331d90-927b-b8fd-93dd-d3edf3fa3e20.png)

思っていたより、型ヒントはすぐ無意味になってしまうんだな…

#### 追加検証

前述のスニペットでは、最初の変数 `a:int = 0` の時点で `a:list` と判定されてしまっている。
型ヒントがすぐ無意味になってしまうのはわかったが、なぜ `a:list` なのだろうか？

人間の目から判断すると、直近で `int` 以外の変更をしている `a:str = "0"` のほうが影響しそうな気もするが…
Pylance はどのような parse をしているか具体的には不明であるが、コードの最下部の方から処理をしているんだろうか？

ということで、最後の行に追加で `a` に dict 型を入れてみる

```python
condition: bool = False

a:int = 0

if condition:
    a:str = "0"

a

if condition:
    a: list = [0]


a
a:dict = {"0": 0}
```

#### 結果

スクリーンショットでは一部の変数を見ているが、実際にはすべての `a` が `a:dict` として判定されていた。

![Screenshot from 2022-05-18 15-38-54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132293/736944c3-9eee-4391-276a-8009ed484ffc.png)



## まとめ

そもそもとして、「変数の再代入を行うのは参照透過性が壊れる」といったような話は既に多くの場所でされており、この行為自体は良くないものである。
とはいえ、今回の自分のケースでは

- 条件分岐などで、そのブロック内でしか使わない変数であった。
- 毎回、そのブロックに入った時点で最初に宣言するので、異なるデータが入ってこないという状況にはしていた。

といった点で、「可読性を上げたいしなぁ」ととりあえず書いていた。

しかし、型ヒントが意味を成さなくなる状況は、前述したスニペットの更に一部の中で発生していた。
思ったより容易に、この現象に似たような再現はできるのである。
(今回の検証が、完全に同一の現象を再現しているかは置いておいて)

可読性の高さを求めることも大事だが、手の抜きすぎも思わぬトラブルを引き起こすものである(自戒)
【M1Mac】nodebrewを使ってNode.jsをインストール

# はじめに
はじめてNode.jsをインストールするとなったときにパスが通らなくて手間取ってしまったので記事を作りました。

# Homebrewをインストールする
1. [Homebrew](https://brew.sh/index_ja)のサイトのインストール欄に書かれているコマンドをコピーしてターミナル上で実行
2. 処理が終わった後に、最後らへんの行に指示が書かれているのでそれに従って実行する
3. Homebrewが正常に使えることを確認

    ```bash
    % brew -v
    % brew doctor
    Your system is ready to brew.
    ```
        
# nodebrewをインストールする
1. Homebrewを使ってインストール

    ```bash
    % brew install nodebrew
    ```

1. 以下の行を`~/.zshrc`または`~/.zprofile`に追記する
    - 多分初期状態のままだと、`~/.zshrc`は存在してなくて、`~/.zprofile`は存在しているはず
    - `~/.zprofile`には既にHomebrewのインストール時に追加された行が存在しているはず。その下にコピペ
    
    ```bash
    export PATH=$HOME/.nodebrew/current/bin:$PATH
    ```
    
    [zshrcとzprofileについて - gallardo diary](https://gallard316.hatenablog.com/entry/2020/11/24/185634)
    
2. 変更を反映する
    
    ```bash
    % source  ~/.zprofile  # または ~/.zshrc
    ```
    
3. 処理が終わったら、最後らへんの行に以下のような指示が書かれているのでそれに従って実行する

    ```bash
    Caveats
    You need to manually run setup_dirs to create directories required by nodebrew:
      /opt/homebrew/opt/nodebrew/bin/nodebrew setup_dirs
    
    Add path:
      export PATH=$HOME/.nodebrew/current/bin:$PATH
    ```

4. nodebrewコマンドが使えるようになったことを確認
まだNode.js自体はインストールしていないので以下のような表示がされると思います。

    ```bash
    % nodebrew ls
    not installed
    
    current: none
    ```
        
# Node.jsをインストール
## Stable版をインストール

```bash
% nodebrew install stable
% nodebrew ls
v18.2.0

current: none
```

## 最新版をインストール

```bash
% nodebrew install latest
% nodebrew ls
v18.2.0

current: none
```

## Node.jsのバージョンを指定してインストール
### v16.0.0以上の場合

```bash
% nodebrew ls-remote  // 指定可能なバージョンの一覧を表示
% nodebrew install v18.2.0
% nodebrew ls
v18.2.0

current: none
```

### v16.0.0未満の場合
M1macのようなarm64に対応していないバージョンのNode.jsはそのままだとインストールできないです。以下のようになってしまいます。
```bash
% nodebrew install v14.8.0
v14.8.0 is not found

Can not fetch: https://nodejs.org/dist/v14.8.0/node-v14.8.0-darwin-arm64.tar.gz
```
    
なのでcompileを使ってインストールする必要があります。ちなみにインストールには30分~1時間ほどかかります。
    
```bash
% nodebrew compile v14.8.0
```
                    
https://kotsukotsu.work/tech/2021-11-13-node-js-m1-mac-%E3%81%A7-nodebrew-%E3%81%8B%E3%82%89-node-v-16-0-0-%E6%9C%AA%E6%BA%80%E3%82%92%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB%E3%81%99%E3%82%8B/

# Node.jsにパスを通す
6. nodebrewのcurrentに任意のバージョンのNode.jsを指定する
パスはcurrentに通しているので、currentに何らかのNode.jsを指定しないとnodeコマンドが使えません。

    ```bash
    % nodebrew use v18.2.0
    % nodebrew use stable
    % nodebrew use latest
    ```
    
7. nodeコマンドとnpmコマンドが使えることを確認
    
    ```bash
    % node -v
    % npm -v
    ```

# おわりに
うまくいかない点があればコメントで教えてください🙇‍♂️

# 参考

https://original-game.com/how-to-install-node-js-on-mac/
Topに戻るボタンの実装

## 上に戻るボタン実装
* @mediaについて
[参考記事print, screen](https://developer.mozilla.org/ja/docs/Web/CSS/@media#%E3%83%A1%E3%83%87%E3%82%A3%E3%82%A2%E7%A8%AE%E5%88%A5)

* スマホのhover有効について
[参考記事](https://web-geek-site.com/%E3%82%B9%E3%83%9E%E3%83%9B%E3%81%A7hover%E3%82%92%E6%9C%89%E5%8A%B9%E3%81%AB%E3%81%99%E3%82%8B%E6%96%B9%E6%B3%95/)

* hoverの画像切り替え
[参考記事](https://hajimete.org/css-hover-image-change)
 	注意点
 	overflow:hidden がついていると重ねた画像が乱れた。
 	overflow:hidden を外すと戻った。

* スムーズスクロール
https://codepen.io/tonkotsuboy/pen/NWyNMOo
```
html {
  scroll-behavior: smooth;
}
```
これだけでスムーズにスクロールする。


* フェードスクロールボタン
[参考記事](https://cotodama.co/pagetop/#:~:text=%7D)%3B-,%E3%83%95%E3%82%A7%E3%82%A4%E3%83%89%E3%81%97%E3%81%A6%E7%8F%BE%E3%82%8C%E3%82%8B%E3%83%9C%E3%82%BF%E3%83%B3,-jQuery(function)
これで簡単に実装！
Xcodeのアップデートが進まないと思った時

XcodeのPlaygroundで全然実行されないと思って原因を探していたら、Xcodeのアップデートを発見しました。
これが原因かなと思いアップデートを押したところ、98%付近からアップデートが進まなくって、あれ？固まったのかな？
と思っていたら、しっかり動いていたという話です。
疑ってごめんよ、Macbookair.

# 解説記事

https://softantenna.com/blog/how-to-check-slow-xcode-install/

↑この方の記事通りでした。
ありがとうございました。

# 備忘録
せっかくなので、このコードについてメモを残します。
```
tail -f /var/log/install.log
```

tailコマンドは、対象ファイルの下から10行を表示してくれます。
今回はそれにオプションの、-fをつけます。
これはファイルが更新された際に、それに合わせて10行を表示してくれます。
ログ監視にピッタリということですね。

# 感想
Xcodeのインストール、アップデートには根気も必要。
[Swift]RxSwift×MoyaでAPI通信してみる

## はじめに
API通信でおなじみのMoyaですがRxMoyaも備わっているのでRxSwiftと絡めた実装をやってみます。

シンプルに、Qiitaで「iOS」というタグでAPIを叩きます。

## ソースコード

まずはAPI
### API
```ruby:QiitaAPI.swift
import Foundation
import Moya

enum QiitaError: Error {
    case error
}

enum QiitaAPI {
    case user
}

extension QiitaAPI: TargetType {
    var baseURL: URL {
        guard let url = URL(string: "https://qiita.com/api/v2/") else {
            fatalError("base URL error")
        }
        return url
    }

    var path: String {
        switch self {
        case .user:
            return "tags/\(QiitaSearchTag.tag)/items"
        }
    }

    var method: Moya.Method {
        return Moya.Method.get
    }

    var sampleData: Data {
        return Data()
    }

    var task: Task {
        return .requestPlain
    }

    var headers: [String: String]? {
        return nil
    }
}

```


QiitaSearchTag.tagを作成して外部で定数化しました。

### Model

```ruby:QiitaModel.swift
import Foundation

struct QiitaModel: Decodable {
    var title: String?
    var user: QiitaUser
    let url: String?
}

struct QiitaUser: Decodable {
    var profileImageUrl: String?

    enum CodingKeys: String, CodingKey {
        case profileImageUrl = "profile_image_url"
    }
}
 
```

### ViewModel

```ruby:QiitaViewModel.swift
import Foundation
import Moya
import RxCocoa
import RxMoya
import RxSwift

final class QiitaViewModel {

    var models = BehaviorRelay<[QiitaModel]>(value: [])
    private var disposeBag = DisposeBag()
    let provider = MoyaProvider<QiitaAPI>()

    func requestDataSource() {
        provider.rx.request(.user)
            .filterSuccessfulStatusCodes()
            .map([QiitaModel].self)
            .subscribe(onSuccess: { (response) in
                self.models.accept(response)
            }) { (error) in
                print(error)
            }
            .disposed(by: disposeBag)
    }
}
```

### View(ViewController)

```ruby: QiitaClientViewController

import RxSwift
import UIKit

final class QiitaClientViewController: UIViewController {

　　　　　　　　#//tableViewのセットアップは省略

    private let viewModel = QiitaViewModel()
    private var disposeBag = DisposeBag()

    override func viewDidLoad() {
        super.viewDidLoad()
        bind()
        viewModel.requestDataSource()
    }

    private func bind() {
        viewModel.models
            .bind(to: tableView.rx.items(cellIdentifier: tableViewCell, cellType: TableViewCell.self)) { _, element, cell in cell.configure(model: element) }.disposed(by: disposeBag)

        tableView.rx.modelSelected(QiitaModel.self)
            .subscribe(onNext: { response in
                guard let url = response.url,
                      let _url = URL(string: url) else {
                    return
                }
                UIApplication.shared.open(_url, options: [:], completionHandler: nil)
            })
            .disposed(by: disposeBag)
    }

}

```


tableView箇所の記述が劇的にスッキリしましたね。

使い方は徐々になれるしかないと思いました。

間違っている箇所やより良い書き方があればご指摘お願いします。
基本情報（n進数）

最も基本的な進数を飛ばしてやってしまったので、ちゃんとやります。
赤ペン先生に怒られます。

情報処理でよく使われるのが**２進数**と**8進数**と**10進数**と**16進数**らしいです。

正直、コーディングで意識したことはないですが、アルゴリズム問題でたまに出ますね。

```
2進数 0と１の2個の数字を使って表す
0→1→10→11→100→１０１→111....

8進数 8個の数字で表し、7の次で桁があがる
0→1→2→3→4→5→6→7→10→11..... 

１０進数 普段の生活で使っていますね
0→1→2→3→4→5→6→7→8→9→10....

16進数 10個のアルファベットと6個のアルファベットで表す16進数
0→1→2→3→4→5→6→7→8→9→A→B→C→D→E→F→10→11...
```

その中でも２進数、８進数、１６進数に関してまとめます。

## 2進数
```
     0
     1
    10
    11
   100
   111
  1000
```
のように増えていきます。

## 8進数
```
0→1→2→3→4→5→6→7
10→11→12→13→14→15→16→17
20→21→22→略→75→76→77→100

// ７の次がないので桁が上がっていく
```
## 16進数
```
0→1→2→3→4→5→6→7→8→9→A→B→C→D→E→F→
10→11→12→13→略→19→1A→1B→略→1E→1F→
20→21→22→略→F1→F2→略→FE→FF→100

// Fの次がないので桁が上がる
```

8進数は一つの桁で0~7の数値を表現することができ、２進数にすると0~111の範囲に相当します。
16進数は一つの桁で0~15の数値を表現することができ、２進数にすると0~1111の範囲に相当します。

8進数は3ビット。2進数3桁分だからですね。昔のコンピュータは3の倍数ビットで動かすことが多く相性が良かったようです。
16進数は4ビット。2進数4桁分。今時のコンピュータは4の倍数ビットで処理するのが普通だからですね。

## 基数と桁の重み
n進数には**基数**という概念があるようです。意味としては「基本となる数」のことを示します。
2進数なら2で8進数なら8のn進数のnにあたる部分です。

```
    128        64      32       16      8    4   2  1
1000000    100000  100000    10000   1000  100  10  1
```
上記を２進数がもつ各桁の**重み**といいます。
基数を累乗していく数値です。

例えば332.5という数字は10進数で10進数の重みは
```
1000 100 10 1 . 1/10 1/100 ←重み
       3  3 2      5
```
3 * 100 + 3 * 10 + 2 * 1 + 5 * 0.1 = 332.5

となるわけで、どの進数でも同じですね。

## まとめ
毎日、書くのはなかなかパワーいりますね。重みじゃなくてつらみですね。
業務ですぐに直結はしないですが、基本情報の勉強は考え方としては役に立ちそうなので
どれもしっかりとやっていきます。
【TypeScript】型定義用のファイルを作成して型定義

# プログラミング勉強日記
2022/05/18
TypeScript の型定義時、ファイルを分けて定義する方法を知ったのでまとめる。

 # 型(タイプ)定義専用のファイルを用意して記述する

src 配下に`types`フォルダを作成
types配下に`UserProfile.ts`を作成
`UserProfile.ts`に以下を記述
```types/UserProfile.ts
export type UserProfile = () => {
  id: number;
  name: string;
  mail: string;
  address: string;
};
```

あとは、使いたいファイルでimport して使用する。
型定義しておくと、下記画像のようにエディタの保管機能でタイポを失くせるので便利。

![スクリーンショット 2022-05-18 21.47.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1209814/ece96308-6f2b-0518-df8b-20a6aacc4a0d.png)
traefikのdashbordを有効にする

https://qiita.com/error484/items/d5ff9da8385ea2ee720a

:::note info
上記の設定が出来ていることが前提となります。
:::

## traefik.yml

dashboard に true を設定します

```diff_yaml
# Providers config
providers:
  docker: {} # Dockerとの連携を有効

# API/Dashboard config
api:
  insecure: true # WebUI にアクセスできるように設定
+  dashboard: true # dashboard
```

## docker-compose.yml

dashboard の設定を labels に追加します

```diff_yaml
version: '3.7'

services:
  traefik:
    image: traefik
    container_name: traefik
    restart: "always"
    ports:
      - "80:80"
      - "443:443"
      - "8080:8080"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - /tmp/traefik.yml:/etc/traefik/traefik.yml:ro
+    labels:
+      - traefik.enable=true
+      - traefik.http.routers.dashboard.service=api@internal
+      - traefik.http.routers.dashboard.rule=Host(`www.localhost`)
+      # HTTPSの設定をする場合
+      - traefik.http.routers.dashboard.entrypoints=secure
+      - traefik.http.routers.dashboard.tls=true
+      - traefik.http.routers.dashboard.tls.certresolver=default
```

## ダッシュボードへのアクセス

`https://www.localhost/` 
上記URLにアクセスをすれば、ダッシュボードにリダイレクトが行われて表示されます。

## ■関連記事

https://qiita.com/error484/items/d5ff9da8385ea2ee720a

https://qiita.com/error484/items/356087c8e7b6826e0dee

https://qiita.com/error484/items/5c538d4f0d39fd0df5d0
Ruby symbol

```ruby
a = {"a":1,"c":2}
p a

unless a[:a]
  p "unless a"
end

unless a[:cc]
  p "unless cc"
end

if a[:a]
  p "a ok"
end
if a[:cc]
  p "cc ok"
end
```

```
{:a=>1, :c=>2}
"unless cc"
"a ok"
```
AWS SAM API、Preflightリクエストエラーに対応しよう！( Python )

## Prefilghtリクエストとは？
Preflightはサイバー攻撃への対策として登場した技術らしいです。
ブラウザ側で本当のリクエストを投げる前に送られるリクエストです。
PreflightのHTTPメソッドは常にOPTIONSです。
（たとえばPOSTをしてもPreflightリクエストはOPTIONSとなります。）

## 解決方法
OPTIONSのメソッドに返すレスポンスを作成します。
bodyは空でいいです。ヘッダーはちゃんと書きましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2481192/df3f46b8-419a-9bde-22aa-7da87592ee49.png)

Preflightリクエストには`access-control-request-headers`と`origin`が入っています。
ApiGatewayのtemplateのAllowHeadersに以下の赤い枠の中の内容を追記します。
（この手順を省略しても通る場合がありました。）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2481192/7213ad35-202f-69e5-26e3-efd0915585a3.png)


## わかったこと
- OPTIONSではApiキーの指定ができなさそうです。
- Access-Control-Allow-OriginがないとPrefilghtが通らないです。
- ~~LambdaではなくてDjangoのAPIは、自動でOPTIONSメソッドに対応してくれました。~~ 「[DjangoのAPI、CORSエラーを解決](https://qiita.com/opentone_parkheedong/items/a64ff7c5f883141acd8e)」を参照
- Credentialsの設定と関連して、Access-Control-Allow-Originを*にすると通らない場合があります。
Rails 命名規則について

# Rails 命名規則について

## コントローラを作成する（小文字で複数形）
```ruby:controller.rb
$ rails g controller homes
```

コントローラ名	複数形、先頭は英小文字	homes
コントローラのクラス名	複数形、先頭は英大文字	HomesController
ファイル名	複数形、先頭は英小文字	homes_controller.rb

## モデルを作成する(先頭大文字で単数形)
```ruby:model.rb
 $ rails g model List
```
モデル名	単数形、先頭は英大文字	List
モデルのファイル名	単数形、先頭は英小文字	list.rb
テーブル名	複数形、先頭は英小文字	lists

https://www.wakuwakubank.com/posts/804-it-naming-convention/


https://designsupply-web.com/media/development/4052/
Excelアウトプット【条件付き書式】【スパークライン】

初めまして“うに”と申します。
今回はExcelのアウトプットに挑戦します。
私はExcel自体は何回も使っていた(表を作る程度ですが)のですが、資格試験向けの勉強となると出てくる機能も増えますね。
今回使ったのは【条件付き書式】と【スパークライン】という機能。
やっている人からすると『なんだそんなことか』と思うようなことも、初心者の私からするとびっくり超機能でした。
【条件付き書式】とはExcelのホームタブの真ん中のほうにあるボタンを押すと使える機能で、書式で指定した範囲を検索して条件に合ったセルに書式を当てはめる機能、名前通り【条件付き】の【書式】なんですね。
【スパークライン】は挿入タブの右のほうにあるボタンを使うと使える機能で、グラフを簡単に作る事ができますしセルと同じ幅だから収納に便利です。
【スパークライン】は最大値と最小値の設定ができるらしく、テキストでは毎回最大値をほかのグラフと共通に、最小値を０にしていました。
どちらも非常に有益な機能で、これからのエクセルの活用の幅が広がりそうですね。
今回は以上です、お目汚し失礼しました。
JS/TSでバイナリ⇔BASE64はライブラリを使わなくても簡単に出来る

JS/TSは、バイナリデータをBASE64エンコードするビルトイン機能が無いため、大人しくライブラリを使うか自前でロジックを用意する必要がありました。

結局はライブラリを使用する場合が多いのですが、機密性の高いデータを扱う場面もあるため、ブラックボックスになるのも精神衛生的に良くないなぁと、ずっと思っていました。

とはいえ、アルファベットテーブルを拵えて6ビット区切になるようビットシフトして文字列加算して...を毎回用意するのも気が引けていました。

しかしある時「実はビルトイン機能を組み合わせるだけで簡単に出来るのでは？」と単純な思い付きで書いたコードがすんなり動いてしまったので、本当に大丈夫なのか有識者の意見も欲しく、こうして記事にしてみました。

# 本体コード
```ts
function base64encode(data:Uint8Array){
    return btoa([...data].map(n => String.fromCharCode(n)).join(""));
}

function base64decode(data:string){
    return new Uint8Array([...atob(data)].map(s => s.charCodeAt(0)));
}
```

エンコードは以下のような手順です。

1. バイト配列を配列リテラルへ展開
2. 1バイト(1要素)ずつASCII文字列(`\0`~`ÿ`)へ変換
3. 全要素を結合しBASE64エンコード

デコードは以下の手順です。

1. BASE64デコードし配列リテラルへ展開
2. 1文字(1要素)ずつASCII値(`0x00`~`0xFF`)へ変換
3. 配列リテラルをバイト配列へ変換

# 検証コード
```ts
const sample = new Uint8Array(new Float32Array(0x00400000).map(() => Math.random()).buffer);

async function hash(data:Uint8Array){
    return [...new Uint8Array(await crypto.subtle.digest("SHA-256", data))].map(n => n.toString(16).toUpperCase().padStart(2, "0")).join("");
}

const result = base64decode(base64encode(sample));

console.log(hash(sample));
console.log(hash(result));
```

上記のように、何回かランダムデータを流し込んでハッシュを取りましたが、全て一致していたので、制御文字を含む文字列へ変換することによるデータ欠損...なんてことは無さそうでした。

また、個人的に気になった「`atob()` / `btoa()` がそんな大量の入出力に耐えれるのかよ」問題も、とりあえず100MBくらいなら動作したので、実用には耐えれそうな感じでした。

ただ、これらは同期関数なので、大容量データを変換したいなら、本体関数はWebWorkerなどの別プロセスへ置いて、データはTransferableオブジェクトとして転送する方法が賢いかも知れません。

これは使えるのでは...!?
railsチュートリアルをなるべく嚙み砕く　2章

#railsチュートリアル第2章ですね
この章はscaffoldを使って簡単なアプリを作り出すようです

### 2.1.1
ユーザーのモデル設計です。ユーザーを必要な要素を簡単に書き出すと
| user | 型 |
| --- | --- |
|id|integer|
|name|string|
|email|string|

ユーザーを識別するための一意なidと名前、メールアドレスですね。
一意というのは他に無い、唯一ということです。

### 2.1.2
マイクロポストのモデル設計です。　マイクロポストというのはTwitterのツイートのことですね。つまりはユーザーがする投稿です。

| microposts | 型 |
| --- | --- |
|id|integer|
|content|string|
|user_id|integer|

マイクロポストのid これはユーザーのidと同じで一意であることを表しています。そして文章のcontentと、どの ユーザーが投稿したかを表すuser_idです

### 2.2
UsersリソースとHTTPプロトコルという言葉が出てきましたね。
Usersリソースとは　Userを操作する対象として見たものです。つまりはUserをこれからデータとして扱いますよ～って意味ですね
https://qiita.com/katsutakashima/items/47977b9e7d18cf816734
こちらの記事がわかりやすいと思います。

HTTPプロトコル
プロトコルとは約束ごと、決まり事
HTTPはHyperText Transfer Protocol の略です。クライアントとサーバーの間でハイパーテキストを転送するためのプロトコルです。色々調べれば出てくると思いますがサイトを貼っておきますね。

https://www.web-knowledge-info.com/wp/web_basic_knowledge4/#1HTTP%E3%81%A8%E3%81%AF
https://wa3.i-3-i.info/word165.html
https://atmarkit.itmedia.co.jp/ait/articles/1703/29/news045.html

次に rails generate scaffold User name:string email:string

generateは生み出すという意味ですね
scaffoldを使って 2.1.1のUserモデルを作り出しています
idは勝手につくので書かなくて大丈夫です。

その後　
rails db:migrate をします。
これはデータベースに先ほど作った　scaffoldのUserモデルを反映させるということです。migrateは移行、移動という意味で
私はデータベースを新しい方へ移動させるイメージを勝手に持っています

最後に　rails server でサーバーを実行します。 rails sでも同じ意味です。

### 2.2.1 

index show new edit　など様々な単語が出てきますね
index　は索引。私は全体を見るイメージを持っています
show 　は見る、表す。これは自分自身を表すイメージです
new    は新しい。　　新しく何かを作ります
edit   は編集。　自分のプロフィールを編集します
destroy は破壊。　削除ですね。

rails s の後に色々と文字が出てきますが、 http://127.0.0.1:3000をコピーしてブラウザへ貼り付けてください。その後ろに　/users を加えるとindexアクションが表示されます。
http://127.0.0.1:3000/index

上記のようにしてください。


### 2.2.2

ここはチュートリアルの説明で十分だと思います

その後の
root 'users#index'
は　最初に来るページをusers#index アクションに割り当てるという意味です。
つまりは　http://127.0.0.1:3000のurlをクリックしたら最初にusers#indexのページが表示されます。

### 2.3

やっていることはuserの時と同じですね。


### 2.3.2
  validates :content, length: {maximum: 140}

validates は検証という意味です　
contentの長さが最大で140字までなのか検証しています

### 2.3.3
   ```app/models/user.rb
class User < ApplicationRecord
  has_many :microposts
end
```

  ```app/models/micropost.rb
class Micropost < ApplicationRecord
  belongs_to :user
  validates :content, length: { maximum: 140 }
end
```
has_many はたくさん持っている　つまり投稿をたくさんする
belogns_toは所属している　つまりuserがしているもの
ということです。
関連付けることによって

first_user.microposts
最初のユーザーのマイクロポストを表示する
というコマンドが動作します
関連付けていなければ
first_userまでは動作しますが　micropostsはエラーが出てしまいますね

###演習

1
\<%= @user.microposts.first.content %>

2
presenceは存在ですね
割愛します


3

```app/models/user.rb
class User < ApplicationRecord
  has_many :microposts
  validates :name, presence: true
  validates :email, presence: true
end
```

他はチュートリアルをしっかりと読めば大丈夫だと思います。
継承は他のクラスの物も使えるよ　という機能です


ではお疲れさまでした！
【Java 】javaコマンド実行でNoClassDefFoundError が出る理由

コマンドプロンプトでjavaを実行する場合に、NoClassDefFoundErrorとなり最初つまづきました。原因はクラスパスやクラスの完全名を間違えていたためでした。この記事でNoClassDefFoundErrorの時の対処法をまとめました。
*****
### 環境
Windows10
Java version 17.0.3.1

## NoClassDefFoundError とは
**Java仮想マシン**または**ClassLoaderインスタンス**がクラス定義をロードしようとしたが、**クラス定義**が見からない場合にスローされます。（公式ドキュメントより）
この説明を理解してくと、**クラスパス**や**クラスの完全名**について理解が進みました。

### Java仮想マシンとは
Javaプログラムを実行するためのソフトウェア。 Javaバイトコードで記述されたコンピュータプログラムを解釈し、そのコンピュータで実行可能な形式のコードに変換して実行する。

### ClassLoaderインスタンスとは
クラスのロードやリソース（ファイル）の検索を担当するオブジェクト

### クラス定義とは
変数を宣言し、オブジェクトが持つメソッドやプロパティが呼び出されたときに実行されるコードを記述した一連のコードを「クラス」あるいは「クラスの定義」と呼びます。

## つまり
**NoClassDefFoundError** は、「Javaバイトコード（中間コード。JVMによって実行可能な機械語になる前に、コンパイルされてできたバイナリデータ）をJVMが解釈しようとするが、**実行するべきクラスをロードできていない**」というエラーです。

## ここでクラスパスが出てくる
なぜJVMがクラスをロードできないのでしょうか。
それは**クラスがどこにあるか=クラスパスをJVMに明確に教えてあげる**必要があるからです。

### クラスパスとは
**クラスパス**は、Java 実行環境がクラスおよび他のソースファイルを検索するパスです。

つまり、**クラスが存在するパス**を指定して実行する必要があります。

### クラスパスの指定のしかた
`java` コマンドの後に `-classpath` + **クラスパス**を指定します。

```
> java -classpath <クラスパス>
```

重要なことは、**クラスパス=クラスが含まれるパッケージ（フォルダ）の親フォルダ**だということです。
大体は、`OO.java`ファイルが入ったフォルダがパッケージとなります。コマンドプロンプトでコンパイルしたら、そのパッケージにクラス（`OO.classファイル`）も入ります。そのパッケージの親フォルダをクラスパスに指定します。

例えば、`C:\...\hoge\fuga\foo.java` のパッケージは `fuga` とします。その親フォルダは `hoge` なので、
クラスパスは `C:\...\hoge` になります。

```
> java -classpath C:\...\hoge
```

ここでまだ完成ではありません。最後にクラス名を指定します。

### クラスパスを指定したら(完全)クラス名を指定する
さっきの例の `C:\...\hoge\fuga\` パッケージに `foo.class` が出来ていたとします。`java` コマンドで指定するクラスは、完全クラス名です。**完全クラス名**とは、「**パッケージのフォルダ名＋ .（ドット） ＋クラスファイル名**」です。

```
> java -classpath C:\...\hoge fuga.foo
```

### 参考にしたサイト
[クラスNoClassDefFoundError](https://docs.oracle.com/javase/jp/8/docs/api/java/lang/NoClassDefFoundError.html)
[クラスの検索方法](https://docs.oracle.com/javase/jp/1.5.0/tooldocs/findingclasses.html)
[クラスパスの設定](https://docs.oracle.com/javase/jp/1.5.0/tooldocs/windows/classpath.html)
AWS CLIでS3上の全メンバーのjsonを削除するコマンドが欲しい時（）

VSCodeで正規表現の置換ってついつい忘れがちなのでメモ
※ショートカットはMacの場合です、ご了承ください

## 量産したいコマンド
「`M000`」の部分を変えたい
```command.txt
aws s3 rm s3://example-environment/M000/json/detail.json
```

## 代入したい文字列
連番のように見えて実はところどころ歯抜け（悲しい過去が透けて見える…）
```members.txt
M001
M002
M004
M005
M006
M008
M009
M010
M011
M012
M013
M014
M015
```

## コマンド量産手順
- VSCodeで`members.txt`を開く
- 検索と置換ウィンドウを開く。ショートカットは`⌘ + F`
- 右側の`.*`のアイコンをクリック
- 上の欄に入力して検索
※文字列を`()`で囲むのがポイント
```上の欄.txt
(M[0-9]{3})
```
- 下の欄（表示されていない場合、入力欄左側の`>`をクリックして表示する）に置換後の文字列を入力
※`$1`でマッチした文字列をそのまま使用できる
```下の欄.txt
aws s3 rm s3://example-environment/$1/json/detail.json
```

## 実行
- 入力欄右側の`Replace all`アイコンをクリック
- またはドキュメント内の任意の箇所にカーソルを移動して`⌘+option+Enter`
- または下の欄にカーソルをフォーカスして`⌘+Enter`で実行

## 感想
連番のようで歯抜けな一連の番号が割り振られているので一行ずつ書くのが一番安心
DBの基礎的な事柄2(演算・トランザクション・ロック)


以下の記事の続きです

https://qiita.com/akitika/items/981674e7dc2954355738


## 関係演算と集合演算

- 演算 = データベースから必要なデータを取り出すこと
- 関係演算 = テーブルから目的のデータを取り出すこと
  - 選択
    - 目的とするテーブルから選択条件を満たすレコードだけを取り出す
  - 射影
    - カラム名とその並びを指定することで、目的とするテーブルからその条件の通りの並びでレコードを取り出す
  - 結合
    - 指定される複数のテーブルの中で、共通のカラムを介してテーブルを結びつけ、新たな結果を取り出す
- 集合演算 = ２つのテーブルから集合の考えを利用してデータを取り出すこと
  - 和集合
    - ２つのテーブル全て
  - 差集合
    - ２つのテーブルの集合のうち、１つのテーブルのみに存在するもの
  - 積集合
    - ２つのテーブルに共通しているものだけ

## トランザクション

- 相互依存の関係にある複数の処理を１つに纏めることで、矛盾なく処理すること。
- またはdbを更新する単位。
- commitはトランザクション内の全ての変更を確定する処理
  - commitされるまでdbに値は反映されない
  - 一度commitしたトランザクションはrollbackできない
- rollbackはトランザクション内で行った更新の取り消しを行う
  - 取り消した後、トランザクションの開始前の状態に戻す

## 排他制御

- 複数のユーザーがデータを読み書きしても、矛盾が生じないようにする仕組み
- ロックを利用して実現する
  - 同一のデータに対して複数のトランザクションが同時に書き込むことを防止する仕組み

### ロック

#### ロックの種類

- 排他ロック
  - 他のトランザクションに対する読み書きを一切禁止する
- 共有ロック
  - 読みだけを許可する

#### ロックの仕組み

- 楽観的ロック(optimistic)
  - 複数のユーザーは参照や更新が可能
    - ただ最初に更新したユーザの処理が反映される
    - ほかのユーザーの更新は失敗or待ち状態
- 悲観的ロック(pessimistic)
  - 更新対象のデータを取得してから更新が完了するまでロックがかかる
    - 他のアクセスからは参照も更新もできない
  - 整合性は保ちやすいがパフォーマンスが低下する可能性がある
  - SQLで対応(SELECT ~ FOR UPDATE)

#### デッドロック

- 2人以上のユーザがお互いにロックを掛け合い、ロックの解除待ちになってしまう状態のこと
- エンジニアがdbに対して操作を行わないと、この状態を解消することはできない

#### デッドロックの回避策

- トランザクションを短くして、ロックをかける時間を短くする
- トランザクション内の処理の順番を決める

## 続き

https://qiita.com/akitika/items/4e863c9443bfcfafae3f
【備忘録】Golangで日付の操作を実施する。

## 日付の操作
```golang:main.go
package main

import (
	"fmt"
	"time"
)

func main() {
	n := time.Now()

	// fmt.Println(n.Format("2006-01-02 15:04:05"))
	year := n.Format("2006")
	month := n.Format("2006-01")
	day := n.Format("2006-01-02")
	hour := n.Format("2006-01-02 15")
	min := n.Format("2006-01-02 15:04")
	sec := n.Format("2006-01-02 15:04:05")
	fmt.Println("year: ", year)
	fmt.Println("month: ", month)
	fmt.Println("day: ", day)
	fmt.Println("hour: ", hour)
	fmt.Println("min: ", min)
	fmt.Println("sec: ", sec)
}
```

```実行結果
~/go/src/array_slice $ go run main.go
year:  2022
month:  2022-05
day:  2022-05-18
hour:  2022-05-18 20
~/go/src/array_slice $ go run main.go
year:  2022
month:  2022-05
day:  2022-05-18
hour:  2022-05-18 20
min:  2022-05-18 20:11
sec:  2022-05-18 20:11:21
```

## コラム(Formatの日付はなんでも良いの？？)
`2006-01-02 15:04:05`と指定しているが、何か意味があるの？？
→あります。この値を変更すると値を正しく取得できなくなります。

試しに`2007`とし、年を取得してみます。
```golang
package main

import (
	"fmt"
	"time"
)

func main() {
	n := time.Now()
	year := n.Format("2007")
	fmt.Println("year: ", year)
}
```

```実行結果
~/go/src/array_slice $ go run main.go
year:  18007
```
なんか、バグっていますね。内部的に意味を持つ値なのでしょう。

ちなみに、`2006-01-02 15:04:05`はアメリカの日付表記にした場合、「1月2日 3時4分5秒 6年(2006年)」となるようです。単に「123456」で綺麗じゃんってだけの話みたいです。
へー、って感じですよね。別に覚える必要は特にないと思いますが、なんか面白いですね。
vbsの作法 その52

# 概要
vbsの作法、調べてみた。
htaでX-UA-Compatibleでedgeを召喚出来ると紹介あるが、userAgent、調べてみた。


# 環境

windows 10 64bit

# ie=9の場合

```
Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 10.0; WOW64; Trident/7.0; .NET4.0C; .NET4.0E; .NET CLR 2.0.50727; .NET CLR 3.0.30729; .NET CLR 3.5.30729)
```

# ie=edgeの場合

```
Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; .NET4.0C; .NET4.0E; .NET CLR 2.0.50727; .NET CLR 3.0.30729; .NET CLR 3.5.30729; rv:11.0) like Gecko
```

# サンプルコード


```
<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="X-UA-Compatible" content="IE=9" />
    <title>Compatibility Test</title>
    <HTA:APPLICATION ID="testApp" APPLICATIONNAME="Compatibility Test" >
</head>
<body onload="document.write(window.navigator.userAgent);">
</body>
</html>
```

以上。