OmniAICreator/Qiita-1.07M huggingface dataset:
Youtubeデスクトップ版もどきをElectronで(Windows用)

# ない・・・ない!

ないんですよ!デスクトップ版ようつべが!!!!

てんわけでElectronでYotubeのサイトを表示するだけのソフトを作りました。

## electronインスコ
### 1.package.json生成&electronインスコ
```
npm init -y

npm i electron
```

### 2. 設定ファイル記述

```js
const { app, BrowserWindow } = require('electron')
const path = require('path')

function createWindow () {
    const win = new BrowserWindow({
        width: 800,
        height: 600,
    })
    win.loadURL('https://youtube.com/')

    app.whenReady().then(() => {
        createWindow()

        app.on('activate', () => {
            if (BrowserWindow.getAllWindows().length === 0) {
                createWindow()
            }
        })
    })

    app.on('window-all-closed', () => {
        if (process.platform !== 'darwin') {
            app.quit()
        }
    })
}

```

### 3.デバック

```
npx electron .
```

### 4.ビルド
electron-builderもありますが、今回は簡易的なソフトを作るだけなのでelectron-packagerを使います。

```
npm i electron-packager

(windows only)
npx electron-packager . youtube-desktop-luncher --x64 --win32
```

## 追記
ちなみにnpm scriptsを使う場合は

```json
...
"scripts" : {
    "electron" : "electron ."
}
...

```

で行けます

あとjsが}が抜けてたりしてたので修正しました( ﾉД`)ｼｸｼｸ…


あと私のブログもよろしくです。

https://nknight-official.variussoftware.com/

# 続編(?)

https://qiita.com/NknightA/items/5ea26e24515d58d67e0e
実験管理でHydraを使ってみて便利だったところ

機械学習モデルの実験管理で[Hydra](https://hydra.cc/docs/intro/)を使ってみて便利に感じた点を紹介する。
細かい使い方は他に良い記事がたくさんあるのでそちらを参照。

# 1. デコレータ一つでyamlをスムーズに読み込める
```yaml: conf/config.yaml
model:
  activation: relu
optimizer:
  type: Adam
  learning_rate: 0.01
```

```python: main.py
@hydra.main(config_path="conf", config_name="config")
def main(config):
    print("optimizer type: ", config.optimizer.type)
    print("optimizer type: ", config["optimizer"]["type"])
```
デコレータの中でconfigファイルのパスを指定すると、その内容が[OmegaConf](https://omegaconf.readthedocs.io/en/2.3_branch/)のオブジェクトとして関数に渡される。`config`の中身にはドットでアクセスすることができる（もちろん、通常の辞書のような形でのアクセスも可能）。

# 2. コマンドラインから簡単に設定値を変更できる
```bash
python main.py optimizer.type=momentum
```
このような形でコマンドライン上でyamlの設定値を書き換えて実行することができる。
また、複数の設定値で実験を回したいときは`-m`オプションをつけることでmultirunが実行され、与えた全ての組み合わせに対して処理が行われる。
```bash
python main.py -m optimizer.learning_rate=0.05,0.1
```


# 3. yaml上で補完機能が使える
```yaml
model:
  middle_layer_1: 20
  middle_layer_2: ${model.middle_layer_1}
```
OmegaConfのinterpolation機能によって、yaml上で変数を利用するような書き方ができる。

# 4. yamlを構造化して管理できる
```yaml: conf/config.yaml
defaults:
  - model: nn
optimizer:
  type: Adam
  learning_rate: 0.01
```
```yaml: conf/model/nn.yaml
activation: relu
```
これをHydraで読み込むと、以下のようなyamlとして読み込まれる。
```yaml
model:
  activation: relu
optimizer:
  type: Adam
  learning_rate: 0.01
```
この機能により、設定を分割化し、構造的に管理できる。ただし、あまり多くファイルを分割しすぎると逆に管理が面倒になるのでほどほどに。

# 5. 実行ごとに自動的に出力用のディレクトリが生成される
実行ごとに、自動的に以下にようなディレクトリ構成でファイルが出力される（multirunの場合は`multirun`ディレクトリが生成される）。
```bash
outputs
  └─2023-01-01
      └─12-00-00
         ├─.hydra
         │   ├─config.yaml
         │   ├─hydra.yaml
         │   └─overrides.yaml
         └─main.log
```
それぞれのファイルの内容は以下の通り。
- `config.yaml`：実際に読み込まれたconfig（コマンドラインに与えた設定値で上書きされている）
- `hydra.yaml`：Hydraの実行時の設定
- `overrides.yaml`：コマンドラインから上書きされた設定値
- `main.log`：実行時に出力したlog

これにより、実験の設定をタイムスタンプとともに保存しておくことができる。
また、`main()`内ではワーキングディレクトリが自動的に`output/2023-01-01/12-00-00/`に書き換わる。これにより、関数内で出力したファイルも自動的に同フォルダ内に保存される。

この機能は便利だが、ハマりポイントでもあるので注意。例えば相対パスでファイルを読み込む際、想定通り読み込めないというのはありがち。また、MLflowと併用する際にも`mlruns`がこのディレクトリ内に生成されてしまうため適切に設定を書き換える必要がある。
firebaseエミュレーターにIOSの実機から接続する

Flutterで認証周りの実装をFirebaseで行っていた際に、Firebaseエミュレーター(Auth)へ、実機のiosからなかなか繋がらなくて四苦八苦したので備忘録として残す

環境として、開発に使っているmacとios（実機）は同一ネットワークに存在してる。

firebaseの設定
```json
// firebase.json
{
  "emulators": {
    "auth": {
      "host": "0.0.0.0",
      "port": 9099
    },
    "ui": {
      "enabled": true
    },
    "singleProjectMode": true
  }
}

```

```dart
class Authentication {
  static Future<void> initialize() async {
    WidgetsFlutterBinding.ensureInitialized();
    await Firebase.initializeApp();

    if (kDebugMode) {
      await FirebaseAuth.instance.useAuthEmulator('192.168.1.8', 9099);
    }
  }
}

```

ここで興味深いのはfirebaseでのホストとflutterでのホストの設定が違うところ。
ちなみに私は[この](https://twitter.com/tsuruken/status/1379260447149289473)ツイートに救われた
Vue3×TypeScript×Composition APIで、子コンポーネントにref属性を利用してアクセスする

# はじめに
Vue2では`$refs`を利用して子コンポーネントにアクセスして、気軽にデータを取得したり関数を実行していました。

最近Vue3に移行したのですが、`$refs`と同様の記載方法を把握するのに時間がかかったので、同じように困っている人の手助けになるような記事を残します。

# 環境
- Vue3
- TypeScript
- Composition API
- `<script setup>`構文

## 子コンポーネント
まずは子コンポーネント内の実装を行います。

```Child.vue
<script setup lang="ts">
import { ref } from 'vue';

const count = ref<number>(0);

const countUp = () => {
    count.value++;
}

const callDoubleCount = ():number => {
    return count.value * 2;
}

defineExpose({
    count,
    countUp,
    callDoubleCount,
});
</script>
```

上記のように記載します。重要なのは`defineExpose`の部分です。

### `defineExpose`コンパイラマクロ

`<script setup>`構文ではデフォルトで内部のアクセスを閉じているので、`defineExpose`コンパイラマクロというものを利用して、意図的にプロパティを公開する必要があります。

上記のように`defineExpose`内で宣言した内容は別のコンポーネントから参照出来るようになりました。

## 親コンポーネント
子コンポーネントにアクセスしてデータを参照したり関数を実行するには以下のように実装します。

```Parent.vue
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import Child from './Child.vue';

const child = ref<InstanceType<typeof Child> | null>(null);

onMounted(() => {
  console.log(child.value?.count); // 0
  child.value?.countUp();
  console.log(child.value?.callDoubleCount()); // 2
})
</script>

<template>
  <div>
    <Child ref="child"></Child>
  </div>
</template>
```

ポイントを解説します。

### template内でref属性の付与
まず、template内で呼び出している子コンポーネントに対しref属性を付与します。これはVue2の時と同様です。

```html
<Child ref="child"></Child>
```

### ref属性を付与したコンポーネントへのアクセス
ref属性にchildという命名を付与した場合、以下のように記述することでref属性を付与したコンポーネントにアクセス出来ます。
```js
const child = ref(null);
```

この時重要なのは変数名です。変数名はtemplateに付与したref属性と同じ名前にする必要があります。

例えば以下のようにref属性と別の名前で変数を定義しても、何も取得出来ません。
```js
const child2 = ref(null);

onMounted(() => {
  console.log(child2.value); // undefined
})
```

### コンポーネントを格納した変数の型定義
これが一番情報が少なかったのですが、ref属性を取得して格納した変数の型定義についてです。

```js
const child = ref<InstanceType<typeof Child> | null>(null);
```
まずmountされてDOM要素が生成されるまではアクセス出来ないのでnull型を定義します。

そしてunion型にして、`InstanceType<typeof コンポーネント名>`で型定義することで、コンポーネントの型定義を読み込むことが可能です。

こちらに関しては公式ドキュメントに記載がありました。

[Vue.js公式ドキュメント | テンプレート参照を型定義する](https://v3.ja.vuejs.org/guide/typescript-support.html#%E3%83%86%E3%83%B3%E3%83%95%E3%82%9A%E3%83%AC%E3%83%BC%E3%83%88%E5%8F%82%E7%85%A7%E3%82%92%E5%9E%8B%E5%AE%9A%E7%BE%A9%E3%81%99%E3%82%8B)

### onMounted実行後に子コンポーネントにアクセスする
先ほども触れましたが、まずmountされてDOM要素が生成されるまではアクセス出来ません。仮にアクセスを試みた場合、以下のようにnullを返されます。
```js
const refSample = ref<InstanceType<typeof RefSample> | null>(null);
console.log(refSample.value); // null
```

### `.value`プロパティにアクセスする
onMounted実行後にDOM要素が生成されてコンポーネント内の情報にアクセス出来るようになりました。情報を取得するには、`.value`プロパティにアクセスする必要があります。

また、mounted前は`.value`にアクセスしてもnullなので、`.value`に値が存在する場合の処理として、オプショナルチェーンを利用した書き方をします。

```js
console.log(child.value?.count); // 0
child.value?.countUp();
console.log(child.value?.callDoubleCount()); // 2
```

# おわりに
以上でVue3×TypeScript×Composition APIで、子コンポーネントにrefを利用してアクセスする方法となります。
ネットで検索しても情報が散らばっているので、こちらの記事を見ることで困っている方が問題解決することを願っています。

---
[Twitter](https://twitter.com/manzoku_bukuro)もやってます
令和X年X月X日とDate型の変換【備忘録】

# 概要
今回、日付を令和X年X月X日XX:XXと変換する必要があり、またそこから逆に戻す必要もあり、、、
という点が少し大変で覚えられる気がしないので、メモとして残します！！

::: note warn
令和X年X月X日XX:XXは長いので、本記事では令和フォーマットと呼ぶことにしています。
:::

# 実装
実装としては、JS標準のDate型の`toLocalDateString(locales, option)`を利用しています。
日本語にしたいので、`locales`は`ja-JP-u-ca-japanese`を指定しています。
また、さまざまなオプションを利用した指定になるので、詳細はMDNで確認してもらえると幸いです。

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Date/toLocaleString


## Date→令和フォーマット
```js:DateToFormat
new Date().toLocaleDateString('ja-JP-u-ca-japanese', {
    hour12: false,
    year: 'numeric',
    month: 'narrow',
    day: 'numeric',
    weekday: 'narrow',
    hour: '2-digit',
    minute: '2-digit',
})
```

## 令和フォーマット→日付
```ts:dateToNumber
const HEISEI_TO_REIWA_YEAR = 2018

const dateToNumber = (format: string) => {
    const date = format
      .match('^令和[0-9]*年[0-9]*月[0-9]*日')![0]
      .replace('令和', '')
      .replace('年', '/')
      .replace('月', '/')
      .replace('日', '')
      .split('/')
      .map((str) => parseInt(str, 10))
    const time = format
      .match('[0-9]*:[0-9]*')![0]
      .split(':')
      .map((str) => parseInt(str, 10))
    const newDate = new Date(
      date[0] + HEISEI_TO_REIWA_YEAR,
      date[1],
      date[2],
      time[0],
      time[1]
    )
    return newDate
}
```

令和X年を西暦にするには、令和X年＋年号が変わる前の2018年で求められます。
(令和)5 ＋ (西暦)2018 = 2023
ちょうど2023年になる。

### 正規表現を賢く使った方法

コメントの方で @ss8826さんが参考になる方法で実装されていたので
取り上げさせていただきます🙇‍♂️
```javascript
const reiwaStrToDate = str => {
  const p = str.match(/\d+/g);
  p[0] -= -2018;
  p[1] --;
  return new Date(...p);
};

console.log(reiwaStrToDate('令和5年2月')); // Wed Feb 01 2023 00:00:00 GMT+0900 (日本標準時)
console.log(reiwaStrToDate('令和5年2月4日')); // Sat Feb 04 2023 00:00:00 GMT+0900 (日本標準時)
console.log(reiwaStrToDate('令和5年2月4日12時')); // Sat Feb 04 2023 12:00:00 GMT+0900 (日本標準時)
console.log(reiwaStrToDate('令和5年2月4日12:34')); // Sat Feb 04 2023 12:34:00 GMT+0900 (日本標準時)
console.log(reiwaStrToDate('令和5年2月4日12:34:56')); // Sat Feb 04 2023 12:34:56 GMT+0900 (日本標準時)
```

ポイントは、以下の3点と思われます。
1. `/d+`で数字のみを抽出
    1. 数字`d`を1つ以上`+`抽出する
1. `g`オプションで複数回抽出
    1. 置換したい文字列を指定し、複数含まれている場合に、全てを置換するオプション
    1. `match`は標準では1度しか変換しません。そのため、`g`オプションが必要になります
1. 月に該当する部分は-1
    1. `Date`型ではMonthは0~11で扱われるため

gオプションに関しては、以下の記事が参考になります！

https://qiita.com/yuichir43705457/items/6503f257f1d81e9239e3


@ss8826さん、とても参考になりました！！
ありがとうございます✨
Oracle CloudでRHELを利用するときに気を付けること

# はじめに

2023/1/31に[Oracle Cloud上でRedhat Enterprise Linux(RHEL)が正式にサポートされる、というニュース](https://blogs.oracle.com/post/ja-red-hat-enterprise-linux-supported-oci)が出ました。
正式にサポートされる、というのはRedHat社のCCSP(Cloud Certified Service Provider)に登録されたということになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2313926/93da3cf0-8e03-3b27-7fb0-ac56fcf4290a.png)


2006年のOracle Open Worldでラリーが[Oracle Unbreakable Linux(現在のOracle Linux)を発表](https://www.itmedia.co.jp/enterprise/articles/0610/26/news026.html)して以来、両社はいろいろな場で激しくやりあってきました。
RedHatも[Oracle LinuxのRHELへのマイグレーションをサポート](https://access.redhat.com/ja/articles/5678351)していたりします。

なので、個人的には永遠にOracle CloudにRHELが来ることはないのでは、と思っていましたが、よい意味で裏切られました。

# Oracle Cloud でRHELを利用するときに気を付けること

[発表ページ](https://blogs.oracle.com/post/ja-red-hat-enterprise-linux-supported-oci)にあるとおり、OCIでのRHELの利用は、Bring Your Own Image(BYOI)を行う必要があります。
また、サブスクリプション契約自体もOracleを経由するのではなく、Redhatと直接契約する必要があります。
ライセンスは個別に準備しましょう。

さて、RHELを利用する際にはOCIへBYOIすることになるため、OCIでネイティブのOracle Linuxを利用するときに比べるといくつか制限があります。以下に代表的なものをあげてみますので、利用する際にはご注意ください。

1.　起動モードは準仮想化モードになる
OCIではイメージの起動モードは以下三種類となります。
- ネイティブモード
- 準仮想化モード
- エミュレートモード

準仮想化モード自体の利用は問題ありませんが、[ネイティブモードが最大のパフォーマンスが期待できる](https://files.speakerdeck.com/presentations/cf71c90194e445c4812a671235389dff/slide_28.jpg)とあるため、検証はしてませんが性能的にはネイティブのOracle Linuxに比べると落ちる場合がある可能性があります。

RHELを利用する場合にはそのような差分にご注意ください。

2.　超高速ディスク(UHD)は利用不可
OCIでは[ブロック・ボリュームのパフォーマンスを四つのレベルで選択する](https://docs.oracle.com/ja-jp/iaas/Content/Block/Concepts/overview.htm#perf)ことができます。
- 超高パフォーマンス(Ultra High Performance)
- より高いパフォーマンス(Higher Performance)
- バランス（Balanced）
- より低いコスト（Lower Cost）

この中で 超高パフォーマンス については、[ブロックボリュームのアタッチにマルチパス対応が必要](https://docs.oracle.com/ja-jp/iaas/Content/Block/Tasks/configuringmultipathattachments.htm)となります。
現時点ではRHELはマルチパス対応がされていないため、超高パフォーマンスのブロックボリュームは利用できません。

必要な性能が より高いパフォーマンス のボリュームで対応可能か、ご注意ください。

3.　Oracle Cloud Agentは利用不可
[Oracle Cloud Agent](https://docs.oracle.com/ja-jp/iaas/Content/Compute/Tasks/manage-plugins.htm)はコンピュート・インスタンスで実行されているプラグインを管理する軽量プロセスになります。
Oracle Cloud Agentを利用することで、Bastionによる接続や脆弱性スキャン等を実施することができます。

ただ、[Oracle Cloud Agentもサポート対象のイメージが限定](https://docs.oracle.com/ja-jp/iaas/Content/Compute/Tasks/manage-plugins.htm#supported-images)されており、現時点ではRHELは入っていません。

Oracle Linuxと同様の管理下に置きたい場合には、別の手法が必要になりますので、ご注意ください。

# まとめ
今回は以下三点、性能面・管理面の注意点をあげました。

1. 起動モードは準仮想化モードになる
1. 超高速ディスク(UHD)は利用不可
1. Oracle Cloud Agentは利用不可

RHELがOCIで利用可能になったのは、[両社の協業の第一段階であり、「RHELのOCIに対する統合をより進め、使い勝手を良くしていく」](https://ascii.jp/elem/000/004/123/4123307/)とのことなので、今後の改善に期待します。


# おまけ

AMD, armベースでのRHEL 8の利用は 8.7 以降、となってますが、armベースのサポートインスタンスタイプはRedhatのページでは 8.4 以降となってました。あんまり8.4をわざわざ利用する機会はないと思いますが、時間がある方は試してみてください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2313926/2d4b2652-3ab8-7e56-bfd6-cc29142d8e58.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2313926/bdfbfa2e-2c42-da3a-ca68-451a897fa48b.png)
Go 基本




Goのプログラムは、パッケージ( package )で構成されます。
プログラムは main パッケージから開始されます。




規約で、パッケージ名はインポートパスの最後の要素と同じ名前になります。 例えば、インポートパスが "math/rand" のパッケージは、 package rand ステートメントで始まるファイル群で構成します。


Goでは、最初の文字が大文字で始まる名前は、外部のパッケージから参照できるエクスポート（公開）された名前( exported name )です。

関数は、0個以上の引数を取ることができます。
この例では、 add 関数は、 int 型の２つのパラメータを取ります。
変数名の 後ろ に型名を書くことに注意してください。


x int, y int
を
x, y int  　　へ省略可能


関数は複数の戻り値を返すことができます。

func swap(x, y string) (string, string) {
	return y, x
}



Goの

Named return value

Goでは、関数の戻り値に名前をつけて変数として利用することができます。
この仕組みをNamed return valueといいます。

https://www.wakuwakubank.com/posts/778-go-func/#index_id3

https://zenn.dev/yuyu_hf/articles/c7ab8e435509d2

func func3(x, y int) (add, sub int) {
	add = x + y
	sub = x - y
	return
}

func main() {
	x, y := func3(10, 5)
	fmt.Println(x)
	fmt.Println(y)
}

add sub という名前付き戻り値で値を返しています。



・var ステートメントは変数( variable )を宣言します。 関数の引数リストと同様に、複数の変数の最後に型を書くことで、変数のリストを宣言できます。


・var 宣言では、変数毎に初期化子( initializer )を与えることができます。

・関数の中では、 var 宣言の代わりに、短い := の代入文を使い、暗黙的な型宣言ができます。
なお、関数の外では、キーワードではじまる宣言( var, func, など)が必要で、 := での暗黙的な宣言は利用できません。



・変数に初期値を与えずに宣言すると、ゼロ値( zero value )が与えられます。
ゼロ値は型によって以下のように与えられます:
* 数値型(int,floatなど): 0
* bool型: false
* string型: "" (空文字列( empty string ))

・型変換
変数 v 、型 T があった場合、 T(v) は、変数 v を T 型へ変換します。
ブラウザでの縦書きエディタ

## はじめに
ブラウザでは基本的に縦書きは不可能である。なぜかというと、縦書きを使用している国は日本と台湾しかないが、主要なブラウザは横書きしか存在しない英語圏で開発されているからである。そこで、縦書きも横書きもできるテキストエディタの制作に挑戦した。


### 縦表示
CSSでwriting-modeをvertical-rlにすれば縦表示が可能になる。

```css:css
.tate {
    writing-mode: vertical-rl;
}
```
## アプリの設計
表示部分はReactを使用した。基本的にブラウザで動作するエディタはDomを直接操作して表示するが、Reactを使用することによりシンプルでわかりやすい構造にできた。コードを見なくても構造が直感的に理解できる。

```tsx:EditorのReactコンポーネント
   <EditorViewProvider editor={editor} options={{ writingMode, wordWrap }}>
      <OverflowGuard>
        <Margin>
          <MarginViewOverlays>
            <LineNumbers />
          </MarginViewOverlays>
        </Margin>
        <EditorScrollbar options={editor.getOptions()}>
          <LinesContent>
            <IndentGuidesProvider>
              <Selections />
              <LineDecorations />
              <ViewLines />
              <ViewCursors />
            </IndentGuidesProvider>
          </LinesContent>
        </EditorScrollbar>
        <Minimap />
      </OverflowGuard>
    </EditorViewProvider>
```
## 問題点
IMEの変換候補ウィンドウがFireFox以外だと横のままで、ちょうど入力している文字と重なる。
ブラウザの仕様で解決不可能なので、FireFox以外では横書きのみとした。

## 成果物
FireFoxのみ縦書き・横書きが出来る。それ以外のブラウザは横書きのみ。
https://cerulean-babka-3056e9.netlify.app/

## おわりに
基本的な動作のみであるが、とりあえず完成したので公開する。

## 参考リンク
- [Webブラウザでも縦書きの美しい日本語入力を。](https://qiita.com/oreo/items/da1c0f4ac946c48a8f49)
kubeadm initできない！！！！

# 環境
Ubuntu 20.04

# kubeadm initをしようとすると…
大学やバイトで忙しく全くサーバーを触れていなかったので、久しぶりにK8Sを使って遊ぼうと思い`kubeadm init`でクラスタを初期化しようとしたらエラーが出ました。
```
ubuntu@instance:~$ sudo kubeadm init
[init] Using Kubernetes version: v1.26.1
[preflight] Running pre-flight checks
error execution phase preflight: [preflight] Some fatal errors occurred:
        [ERROR CRI]: container runtime is not running: output: time="2023-02-04T07:30:18Z" level=fatal msg="validate service connection: CRI v1 runtime API is not implemented for endpoint \"unix:///var/run/containerd/containerd.sock\": rpc error: code = Unimplemented desc = unknown service runtime.v1.RuntimeService"
, error: exit status 1
[preflight] If you know what you are doing, you can make a check non-fatal with `--ignore-preflight-errors=...`
To see the stack trace of this error execute with --v=5 or higher
```

せっかく久しぶりに開いたのに最初に迎えてくれたのはエラーでした。最悪です。

# 解決策
ランタイムのバージョンが古すぎることが原因だったようです。
Dockerリポジトリから最新のcontainerdを導入することで解決します。

## リポジトリの導入
[Dockerリポジトリの導入方法に関するドキュメント](https://docs.docker.com/engine/install/ubuntu/#set-up-the-repository)を参照しながらGPGキーを追加し、リポジトリを追加します。
```
$ sudo mkdir -p /etc/apt/keyrings
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
$ echo \"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
  $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
```

## 古いcontainerdの削除
いつも通りにaptを用いて削除します。別のパッケージマネージャを用いてる場合は適宜読み替えてください。
```
$ sudo apt remove containerd
$ sudo mv /etc/containerd/config.toml /etc/containerd/config.toml.bak
```
:::note warn
configファイルをconfig.toml.bakに改名しています。
もしconfigを変更している場合新たに生成されたconfig.tomlを変更してください。
:::
## containerdの導入
```
$ sudo apt install containerd.io
$ sudo systemctl restart containerd
```

# 結果
正常に`kubeadm init`できるようになった！やったね！
```
$ sudo kubeadm init
Your Kubernetes control-plane has initialized successfully!
```
MySQL Workbench 8.0.32 SQL実行できない問題を解決

# 課題
* `brew --cask` でMySQL Workbench 8.0.32にアップデートした際、以下の問題が発生
  * :thumbsup: データベースには接続できる
  * :thumbsup: テーブルは見えている
  * :thumbsdown: SQLで問い合わせすると結果を表示する異常終了する


# 結論
* ダウングレードする

## 手順
1. ダウングレードのバージョンのハッシュ値を探す
    ```
    cd /opt/homebrew/Library/Taps/homebrew/homebrew-cask/Casks
    git log mysqlworkbench.rb
    ```
    または
    https://github.com/Homebrew/homebrew-cask/commits/master/Casks/mysqlworkbench.rb
    ... にて該当のコミット番号をコピー
2. ダウングレードしたいハッシュ値をURLに充ててファイルをダウンロード
    ```
    curl -L https://raw.githubusercontent.com/Homebrew/homebrew-cask/208c89f72974f469e5d8235490c03db138d7ce01/Casks/mysqlworkbench.rb > mysqlworkbench.rb
    ```
    * **:memo:補足** : 他のアプリで行う場合は以下のformat
      ```
      https://raw.githubusercontent.com/Homebrew/homebrew-cask/%{ハッシュ値}/Casks/%{アプリ名}.rb
      ```
3. 不具合が発生するバージョン `8.0.32` を削除
    ```
    brew uninstall --cask mysqlworkbench
    ```

4. `brew install --cask` 実行
    ```
    brew install --cask mysqlworkbench.rb
    ```
5. `2.` のファイルを削除
    ```
    rm mysqlworkbench.rb
    ```


## 参考URL
* https://formulae.brew.sh/cask/mysqlworkbench
* https://github.com/Homebrew/homebrew-cask/commits/master/Casks/mysqlworkbench.rb
* https://ubawaretai.work/mysql_workbench_error/
* https://zenn.dev/hellorusk/articles/7581c305ff389c
* https://qiita.com/toranokopg/items/9c90e1d0710915e42ae9
ACMでワイルドカードを使った証明書を発行する際にハマったところ

## この記事を読んでわかること

ACMでSSL証明書作成時、ワイルドカードを使い、複数のサブドメインをサポートしたい時の注意点を知ることができます。



## 冒頭まとめ

ACMで、ワイルドカードを使ってドメイン名を登録する際は、以下の点に注意が必要です。

**サブドメインのレベルは、一つしかサポートされないこと**

2つ以上のサブドメインレベルを登録したい場合は、登録したいドメインをFQDNで指定する必要があります。



## 背景

以下の構成で、HTTPS通信を実現したい場面がありました。

![221120-ページ2.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1100138/4cca7f8d-e585-890a-86f6-29f78c85777c.png)


外部からのアクセス対象となるの名前（ALBのDNSレコード名)は``elb.test.homma.jp``であり、Route53で登録しています。	
``elb.test.homma.jp``に対し、HTTPSでの通信を実現するため、ACMでSSL証明書を作成しました。
SSL証明書を作成する際に、ワイルドカードを使用してつまづいた部分があったので、備忘も兼ねて記事にします。

## 前提条件

- ドメイン名(Zone Apex)

  ``homma.jp``

- ALBのDNSレコード(Aレコード)

  ``elb.test.homma.jp``

## ハマったところ

- まず、ACMでSSL証明書を作成しました。
  サブドメイン扱いとなる``elb.test.homma.jp``に、HTTPSアクセスをしたかったため、**ワイルドカード(*)**を使った証明書として、``*.homma.jp``を登録しました。

![★AWS_Certificate_Manager-5492673.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1100138/368e674b-1a43-160a-e8c3-93ed550dc2d8.png)





- そして、ALBへの証明書のアタッチなど、諸々の設定を済ませたあと、ブラウザから``elb.test.homma.jp``へHTTPSアクセスしてみると、保護されていない通信として、エラーになります。証明書が無効の状態となっていました。

![添付②-5493822.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1100138/a458cb3b-7c17-f252-0a7b-743a45d4a9a9.png)






## 原因

[公式のドキュメント](https://docs.aws.amazon.com/ja_jp/acm/latest/userguide/acm-certificate.html)に、以下の記述がありました。

> ワイルドカード証明書をリクエストする場合、アスタリスク (`*`) はドメイン名の左側に付ける必要があり、1 つのサブドメインレベルのみを保護できます。たとえば、`*.example.com` は `login.example.com` および `test.example.com` を保護できますが、`test.login.example.com` を保護することはできません。〜〜省略〜〜

ACMにおいて、ワイルドカード(*)は、一つのホストまでしかサポートしない仕様に対し、``elb`` と``test``の2つ入った``elb.test.homma.jp``をサブドメインとして登録していたことが、エラーとなった原因です。

## 正しくは

2つのパターンがあります。

1. 今回のように、``elb.test.homma.jp``などの2つ以上のホストが入っている場合は、ワイルドカードを指定するのではなく、FQDNを指定する

2. ドメイン名のホストを一つとしたアクセス要件にする。（例えば、elb.homma.jpにする）

   →こちらの場合はホストの数に制限はかかりますが、ワイルドカードを使うことで複数のドメイン名をサポートできます。

今回は、２の方法ででHTTPSアクセスを実現してみます



## 再び挑戦

- アクセス対象のドメイン名を、``elb.test.homma.jp``から``elb.homma.jp``に変更

![5495252.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1100138/7cee92c8-fa1e-0772-ffb6-3b1f18a80257.png)



- ふたたび、ブラウザから`elb.homma.jp`へアクセスし、無事HTTPSでのアクセスができました。

![elb.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1100138/2316c47a-f6b4-ebcc-2e55-d9ed832b19c6.png)





## まとめ

ACMでワイルドカードを利用する際は、サポートされるサブドメインの範囲に注意が必要です



## 参考にした記事

以下の記事を参考にさせていただきました。

[AWS Certificate Manager(ACM)でワイルドカード証明書を発行する際の注意事項]( https://blog.serverworks.co.jp/tech/2018/05/31/acm-wildcard-certificate/)

[公式のドキュメント](https://docs.aws.amazon.com/ja_jp/acm/latest/userguide/acm-certificate.html)
JavaScriptでグローバル変数を使わない方が良いみたい

## はじめに
プログラミングにおいて、グローバル変数の使用は時として便利かもしれませんが、それにはリスクが伴います。特にJavaScriptのような言語では、グローバル変数が原因で予期せぬ問題やバグが発生することがあります。この記事では、グローバル変数が引き起こす可能性のある問題点を詳しく解説し、より良いコーディング慣行への移行を促します。

## グローバル変数の問題点
#### 1. 名前の衝突
他のライブラリやフレームワークと同じ名前でグローバル変数を宣言すると、意図しない動作を引き起こす可能性があります。これは、名前空間が汚染されることにより、予期せぬエラーやバグを招く原因となります。

#### 2. 不適切なアクセス
グローバル変数はアプリケーション全体でアクセス可能であるため、予期せぬ場所からの変更が生じることがあります。このような変更はコードのトレーサビリティを低下させ、バグの特定を困難にします。

#### 3. メモリリーク
グローバル変数はガベージコレクションの対象外となることがあり、アプリケーションの終了時にメモリから解放されないことがあります。これがメモリリークを引き起こし、長期間稼働するアプリケーションのパフォーマンスを低下させる可能性があります。

## グローバル変数の代替策
JavaScriptでは、以下の方法を用いてグローバル変数の使用を避けることが推奨されます：

+ **ローカル変数の使用**
関数内で変数を宣言することで、そのスコープを限定します。
```javascript
function exampleFunction() {
    let localVariable = 'I am local';
    console.log(localVariable);  // 正しく 'I am local' が出力されます
}

exampleFunction();
console.log(localVariable);  // エラー: localVariable is not defined
```

+ **モジュールシステムの利用**
ES6モジュールやCommonJSなど、モジュールスコープを活用して変数をカプセル化します。
```javascript:data.js
export const myData = 'Important data';
```

```javascript:main.js
import { myData } from './data.js';

console.log(myData);  // 'Important data' が出力されます
```

## まとめ
JavaScriptでのグローバル変数の使用は、多くのリスクを伴います。安全なコーディング慣行に従い、グローバル変数の使用を避けることで、より保守しやすく、安定したアプリケーションの開発が可能になります。この記事があなたのJavaScriptコーディングスキルの向上に役立つことを願っています。

もし、この記事が有益だと感じたら、ぜひいいねやコメントを残してください。
An error occurred using the connection to database '' on server 'localhost'.の対処

# ASP.NET COREマイグレーションエラーが発生
現在Blazorの学習を兼ねて友人から依頼されたデータベースアクセスのアプリケーションを作成しています。手始めにDBアクセスにEntity Framework Coreを使用してみたのですが、表題のエラーが発生しました。結構あっけなく解決できたのですが、意外なところで見落としがちなことでしたので誰かの解決のヒントになることを期待して記事にします。(備忘録としても)

## 前提

### 開発環境

- MacOS 13.1
- .Net 6.0.405
- Visual Studio Code 1.75.0
- MySQL 8.0.32
- Microsoft.EntityFrameworkCore 7.0.2
- Microsoft.EntityFrameworkCore.Design 7.0.2
- Microsoft.EntityFrameworkCore.SqlServer 7.0.2
- Pomelo.EntityFrameworkCore.MySql 7.0.0

Dockerは使用せずローカルにて環境構築を実施。
また、`dotnet ef migration add`を実行して、マイグレーションフォルダは作成済み。
使用するデータベースも作成済み。(他のMySQLとポートが被らないように __ポートは3307を使用__)

### 著者のスキル
- PHP/Laravel歴が最も長い(3年程度ですが…)
- C#はこの1年間、業務で使用(~~レガシー技術~~ Web Formsで使用)
- キャッチアップも兼ねてBlazorを学習中(~~JS書きたくないから~~)

初心者なので生暖かく読んで頂けると幸い。

## 発生したエラー
マイグレーションフォルダも作れたし、`dotnet ef migration add`でデータベースとテーブルを作っちゃうぞっと…__は？__
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2705585/9c390c1e-49c6-06ba-ee19-0cf9bb3efbc8.png)
どうやらデータベース接続できないと言ってるようだ…(~~なんでや！~~)
接続文字列も設定できていると思うのだが…
``` c#:Program.cs
var connectionString = "server=localhost;user=root;password=12345678;database=hoge";
```

## 原因
エラー文中の`connection to database`を見て思ったのだが、そもそもDBが空ってどういうことなのかと。
ふと思いついたのが別アプリ用のDockerでポート3306を使用しているため、今回のDBは前提条件で書いたように3307を使用している…つまりポート番号を指定してあげれば…

## 顛末
``` c#:Program.cs
var connectionString = "server=localhost;user=root;port=3307;password=12345678;database=hoge";
```
`port=3307`を接続文字列内で指定。これで後はDBアップデートのコマンド`dotnet ef database update`を叩けば…
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2705585/a5a412e6-71e7-324d-fe35-52f390180344.png)
__やったぜ__

## おわりに
改めてエラー文を読むと解決の糸口になりますね。今回の例だと直接原因が書かれている訳ではないのですが、DBに接続できていない→設定が間違っているというひらめき(~~大袈裟~~)に繋がりました。
ローカル環境でMySQLを構築したのでデフォルトポート番号3306が空いていると勘違いしていた結果がこれだよ！
複数のDB環境をローカルで構築する時のポート番号管理ってどうすればよいのか…もっと調査、勉強をしないとなあ…

おしまい。
AWSのリソースが一括削除できる『aws-nuke』の実行手順


# aws-nukeとは
AWSアカウント内の**リソースを削除**することができるツール

# 実行手順

## 1. アカウントIDの確認
マネジメントコンソールの右上にある「アカウント名」をクリックして確認できる

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1719666/1a47681e-2ccf-5670-773c-c034a8e90fff.png)

## 2. CloudShellを開く
シェルアイコンをクリックする

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1719666/f6fba4a5-b1a9-8c42-d779-305bbc92558c.png)

## 3. aws-nukeインストール

### 3-1. Githubで最新のバージョン確認する

https://github.com/rebuy-de/aws-nuke/releases

今回は`v2.21.2`が最新だったので、それを使う

### 3-2. バイナリダウンロード

CloudShellで`$ wget https://github.com/rebuy-de/aws-nuke/releases/download/v2.21.2/aws-nuke-v2.21.2-linux-amd64.tar.gz`を実行

### 3-3. 解凍
CloudShellで`$ tar -zxvf aws-nuke-v2.21.2-linux-amd64.tar.gz`を実行

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1719666/2d3dbc61-ac7d-e8ba-28b1-b0bebc742553.png)

## 4. configファイル作成
CloudShellで`$ vi nuke-config.yml`を実行

::: note warn
configファイルの中身は編集が必要
:::

```yaml:nuke-config.yml（例）
regions: #削除するリージョン
- ap-northeast-1
- global
account-blocklist:
- 000000000　# 削除対象外アカウントのアカウントID(指定しないとエラーになる)
resource-types:
  excludes:
  - IAMRole
  - IAMRolePolicyAttachment
  - IAMSAMLProvider
  - IAMRolePolicy
accounts:
  1234567890: {} #削除対象アカウントのアカウントID 要書き換え
```

## 5. アカウントエイリアス作成

適当にエイリアス名を設定する

::: note info
エイリアス名は世界中でユニークである必要がある
:::

CloudShellで`$ aws iam create-account-alias --account-alias {エイリアス名}`を実行

## 6. 実行する
> **dry runモード**
> 実行時に削除されるリソースを一度確認したい場合は、dry runモード（`--no-dry-run`を付けない）で実行する


CloudShellで`$ ./aws-nuke-v2.21.2-linux-amd64 -c ./nuke-config.yml --no-dry-run`を実行

::: note warn
自分がインストールしたaws-nukeのバージョンにコマンドを変更する必要がある
:::

2回アカウントエイリアス名の入力が求められるので、入力する

### 以上！

# 参考
https://dev.classmethod.jp/articles/aws_nuke_intro/
Google Cloud Compute Engine なLinux VM が認識するユーザーID にご注意を

皆さん、こんにちは。
ゼロバンク・デザインファクトリー株式会社(以下、ZDF )にてTechnical Lead をさせて頂いている家壽田(けすだ)です。
今回は、Google Cloud Compute Engine をお使いの方々にちょっとした注意事項をお届けできればと思い、筆をとりました。

# はじめに
以下条件にて実施しています。
* Google Cloud Compute Engine 上のLinux VM をGoogle Cloud コンソール(GUI )から作成しています。
* OS Login は有効にしていません。
* アクセス経路はIdentity Aware Proxy (以下、IAP )経由になります。
* ログインアカウントはm.kesuda@{domain} です。(Google Cloud コンソール / gcloud コマンド共)
* gcloud コマンドはWindows のコマンドラインにて実行しています。
# Google Cloud コンソールから、ブラウザ経由でSSH してみる
```
m_kesuda@{vm name}:~$ 
```
ユーザー名はm_kesuda (アンダースコア)になっています。

# gcloud コマンドでscp してみる
Linux VM 上のユーザーIDがアンダースコア付きなので、`/home/m_kesuda`にscp してみます。
※事前にtest.txtの準備を忘れずに。(異なるエラー`test.txt: No such file or directory`が出ますので)
```
gcloud compute scp test.txt {vm name}:/home/m_kesuda --zone {zone} --tunnel-through-iap --project {project name}
```
と
```
pscp: unable to open /home/m_kesuda/test.txt: permission denied
ERROR: (gcloud.compute.scp) [{省略}\Google\Cloud SDK\google-cloud-sdk\bin\sdk\pscp.exe] exited with return code [1].
```
権限がないと怒られます。
**ログインアカウントはブラウザ経由のSSH と同じなのになぜ？！**
# gcloud コマンドでSSH してみる
```
gcloud compute ssh {vm name} --zone {zone} --tunnel-through-iap --project {project name}
```
と
```
m.kesuda@{vm name}:~$ 
```
ユーザー名がm.keusda (ピリオド)になっているのがわかります。
# scp 先を変更してみる
gcloud コマンド経由でSSH した際にユーザー名がピリオドになっていたので`/home/m_kesuda`を`/home/m.kesuda`に変更して実行してみます。
```
gcloud compute scp test.txt {vm name}:/home/m.kesuda --zone {zone} --tunnel-through-iap --project {project name}
```
と
```
test.txt                  | 0 kB |   0.0 kB/s | ETA: 00:00:00 | 100%
```
成功。
# ブラウザ経由でSSH した際のユーザーID のホームに転送するには？！
gcloud コマンドの場合、Linux VM 上のユーザーID はピリオド付きで認識されるので、VM 名にブラウザ経由でSSH した際のユーザーID を認識させる形でscp してみます。
```
gcloud compute scp test.txt m_kesuda@{vm name}:/home/m_kesuda --zone {zone} --tunnel-through-iap --project {project name}
```
と
```
test.txt                  | 0 kB |   0.0 kB/s | ETA: 00:00:00 | 100%
```
成功。
# 実際、Linux VM 上ではどうなってるかと言えば。。。
```
$ ls -al /home
total 16
drwxr-xr-x  4 root     root     4096 Feb  2 06:24 .
drwxr-xr-x 18 root     root     4096 Feb  2 06:23 ..
drwxr-xr-x  3 m.kesuda m.kesuda 4096 Feb  2 06:31 m.kesuda
drwxr-xr-x  3 m_kesuda m_kesuda 4096 Feb  2 06:35 m_kesuda
```
当たり前と言えば当たり前ですが、別のアカウントとして認識されています。
# まとめ
Google アカウント名にピリオドが含まれている場合において、ブラウザ経由のSSH と gcloud コマンド経由のSSH / scp ではLinux VM 上のユーザーID が変わってしまうケースがあるようです。
全てを洗い出した訳ではありませんが、
* ブラウザ経由のSSH ＝ アンダースコア (ピリオドからの変換あり)
* gcloud コマンド経由のSSH / scp ＝ ピリオド (変換なし)

ということになります。
**ご注意を！！**

尚、Google アカウント名にピリオドなど文字以外がない`name@{domain}`の場合は変換される要素がないので、どちらでも同じアカウントとして認識されます。
# 終わりに
紛らわしいので、どちらかに統一頂きたいものですね。(＾＾；
簡単なWebサイト制作ツールとは！？

## 概要
Webサイトは集客やサービスの紹介など、さまざまな目的で使用されます。Webサイトを活用しない企業は非常に少ないため、所有していない人は準備するのがおすすめです。

しかし、Webサイトの構築は複雑なので、どのように作ればいいのかわからない方も多いでしょう。

そこで、今回はWebサイト制作を簡単にするおすすめのツールを3つご紹介しますので、制作を検討している人はぜひ参考にしてください。
![ヘッダー女性画像.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1180499/e7d24308-bb1b-8af3-2356-1aca9250f071.jpeg)


## Webサイト制作を簡単にする3つのツール

### Webサイトの制作を簡単にするのは、下記の3つです。

WordPress

WiX

ペライチ

STUDIO

それぞれのツールについて詳しく解説します。

### WordPress

WordPressは、コンテンツマネジメントシステム(CMS)の代表格です。世界中のWebサイトの約42.3％がWordPressを使用しています。また、CMSに限定するとシェアは約65.2％です。

CMSは、簡単に言えば、Web開発の知識が少ない人でもホームページやブログを作れるシステムのことになります。簡単につくれる理由は、テーマとプラグインがあるからです。

テーマはサイトのデザインを決める骨格の部分で、インストールするだけでホームページの大部分を作れてしまいます。そのほかにもプラグインという機能があり、それを利用することで容易にサイト機能を追加することが可能です。

### WiX

WiXは、本格的なホームページを作成することができる制作ツールです。

WiXには、デフォルトで500以上のテンプレートが準備されており、好みのデザインから選択することができます。また、Webに関する専門知識は一切不要となっており、誰でも簡単に制作することが可能です。そのほかにも下記のようなメリットがあります。

SEOツール内蔵で集客効果あり

マルチデバイスに対応

機能拡張

独自ドメインの取得もできるため、WiXを利用すればWebサイトに必要なさまざまな点をカバーしてもらえるでしょう。

### ペライチ

ペライチは、クオリティの高いホームページが作れるツールです。

スタートプランを利用すれば、無料でホームページの運用をはじめることができます。また、オンライン決済や予約システム機能もあるため、商品の売り場としてWebサイトを活用したり、お店の予約を受け付けたりすることも可能です。

しかし、ペライチはプランの選択によって使用できる機能などが異なります。スタートプランでは利用したい機能を実現できないこともあるため、注意しましょう。

## Webサイト制作はツール活用がおすすめ！

オリジナルのサイトを制作したいと考えている方もいるのではないでしょうか。

Webサイト制作ツールは、簡単に作ることができますが、テーマやテンプレートでデザインを決めるケースが多いので、ほかの企業やお店とデザインがかぶることもあります。

もし、オリジナルの制作を依頼したいなら、当社のWebサイト制作がおすすめです。当社を利用すれば、ビジネスシーンに合わせてノーコードからCMS活用まで様々な集客効果の高いWebサイトをプラニングして制作が実現できます。

また、実際にリリースした後のマーケティング分析に強い当社では運用面から逆算した開発が可能です。

特に当社が最近導入を進めており実績を積み上げているのがSTUDIOとなります。費用対効果が圧倒的に高いという点で本ツールはとてもオススメです。

## ツールを活用して集客に役立つWebサイトを制作しよう

今回は、Webサイトを簡単に制作できるツールをメインとして3種類ご紹介しました。利用するツールによって料金や機能が異なりますので、比較して十分に検討しましょう。また、オリジナル制作を依頼したい人は、ゼロベースから制作を受け付けてくれる開発企業に依頼するのがおすすめです。
文字列、数値を連結する時の注意

JavaScriptは文字を連結する際に文字列を必要とする場面ではどのような値でも文字列に使用とし、数値が必要となる場面では数値に変換しようとします。（変換できない場合はNaNに変換します）。今回はその例をメモします。

まず文字列同士は連結し、数値同士は計算します
~~~ javascript
'こんばんは' + ' おはよう' // => こんばんは おはよう
'こんばんは' * ' おはよう' // => NaN 数値変換できないのでNaNが返る
4 + 8                 // => 12
4 * 8                 // => 32
~~~
次に数値と文字列の場合、文字列と**数値の文字列**は文字列に変換します
また、文字列と数値で計算しようとするとNaNを返します
~~~ javascript
4 + ' おはよう'     // => 4 おはよう
'こんばんは' + '8'  // => こんばんは8
'こんばんは' * 32   // => NaN
'こんばんは' - '32' // => NaN
~~~
**文字列の数値**同士では**加算**では**文字列で連結**それ以外は数値に変換され、計算されます
注意したいです
~~~ javascript
'4' + '8'        // => 48 加算のみ連結する
'5' - '10'       // => -5
'7' * '3'        // => 21
'24' / '6'       // => 4
'7' % '4'        // => 3
~~~
加算は左から順に処理が行われるのでケースによって違う結果を変えします。
~~~ javascript
1200 + 1400 + '歳です'           // =>　2600歳です
'年齢は' + 1200 + 1200 + '歳です' // =>　年齢は12001600歳です
~~~
もし加算で数値と文字列を混在させる場合は（)で演算する順序を変更させましょう
~~~ javascript
'年齢は' + (1200 + 1200) + '歳です' // =>　年齢は2600歳です
~~~

結構ルール細いね。
数値はなるべくNumber（数値型)で扱ったほうがエラー少なくていいかも( ◠‿◠ )

#### 参考
https://www.javadrive.jp/javascript/ope/index16.html
Ruby Feedback

## むやみにattr_accessorをpublicに定義するのは好ましくない

外部から書き換えられても良いものには `attr_accessor`を利用する。
それ以外は`attr_reader`を使用する

## 同じ処理は共通化する

```diff_ruby: vending_machine.rb
- # ペプシを購入するリクエスト
- def request_purchase_pepsi(suica)
-    if suica.deposit >= @pepsi.price
-      @earning += @pepsi.price
-      suica.purchased(@pepsi.price)
-      @pepsi.stock -= 1
-    else
-      puts 'カードの残高が足りません。'
-    end
-  end
-  # モンスターを購入するリクエスト
-  def request_purchase_monster(suica)
-    if suica.deposit >= @monster.price
-      @earning += @monster.price
-      suica.purchased(@monster.price)
-      @monster.stock -= 1
-    else
-      puts 'カードの残高が足りません。'
-    end
-  end
-  # いろはすを購入するリクエスト
-  def request_purchase_irohas(suica)
-    if suica.deposit >= @irohas.price
-      @earning += @irohas.price
-      suica.purchased(@irohas.price)
-      @irohas.stock -= 1
-    else
-      puts 'カードの残高が足りません。'
-    end
-   end

+ # juice_type
+  # 1. ペプシ 2. モンスター 3. いろはす
+  def request_purchase(suica, juice_type)
+    juice_list = [@pepsi, @monster, @irohas]
+    return puts 'カードの残高が足りません。' if suica.deposit < juice_list[juice_type - 1].price
+
+    if juice_list[juice_type - 1].stock.positive?
+      @earning += juice_list[juice_type - 1].price
+      suica.purchased(juice_list[juice_type - 1].price)
+      juice_list[juice_type - 1].stock -= 1
+    else
+      puts "#{juice_list[juice_type - 1].name}の在庫は0です。"
+    end
+  end

```

## 早期リターンでif文のネストを少なくする

```diff_ruby
- if suica.deposit >= juice_list[juice_type - 1].price
-      if juice_list[juice_type - 1].stock.positive?
-        @earning += juice_list[juice_type - 1].price
-        suica.purchased(juice_list[juice_type - 1].price)
-        juice_list[juice_type - 1].stock -= 1
-      else
-        puts "#{juice_list[juice_type - 1].name}の在庫は0です。"
-      end
-    else
-      puts 'カードの残高が足りません。'
-    end

+ return puts "カードの残高が足りません。 " if suica.deposit < juice_list[juice_type - 1].price
+
+ if juice_list[juice_type - 1].stock.positive?
+    @earning += juice_list[juice_type - 1].price
+     suica.purchased(juice_list[juice_type - 1].price)
+     juice_list[juice_type - 1].stock -= 1 
+ else
+    puts "#{juice_list[juice_type - 1].name}の在庫は0です。"
+ end
```

## Rubyのファイル名は小文字で命名する(スネークケース)
勾配ブースティング木のDARTについて

Kaggleとかでよく使われている（らしい）DARTについて論文を読んでまとめた。
DART: Dropouts meet Multiple Additive Regression Trees (2015)
https://arxiv.org/abs/1505.01866

# 概要
通常のGBDT[^1]では、イテレーションの回数が増えるほど、新しく追加される決定木が僅かなサンプルにしか寄与しなくなるover-specializationという問題が知られており、これが汎化性能を下げる要因となる。この問題に対し、これまではshrinkage（各木に微小な定数をかけるという正則化法）を行うことで対処がなされていた。DART（Dropouts meet Multiple Additive Regression Trees）は、このover-specializationの問題を、ニューラルネットにおける正則化法の一つであるドロップアウトを用いることで解消し、モデルの性能向上を実現している。

[^1]: 論文中ではMART（Multiple Additive Regression Trees）という言葉で説明されているが、GBDTとほぼ同義だと思うので、今回はこちらの言葉を使った（実際、カステラ本10章では同様のアルゴリズムが「勾配ブースティング木」という名前で解説されている）。

下の図はナイーブなGBDT、shrinkageを用いたGDBT、DARTそれぞれに対し、各イテレーション数において作成された木を可視化し、手法の振る舞いの比較を行ったもの。葉の大きさがそこに含まれるサンプル数、葉の色が予測値の大きさを表している（緑に行くほど正、黄色が0、赤に行くほど負）。大きい黄色の葉を持つような木が生成されることが、まさにover-specializationに対応している。ナイーブなGBDTでは100イテレーションほどでそのような木が生成されている一方で、DARTでは1000イテレーションでようやくそのような木が生じており、over-specializationが緩和されていることがわかる。

![dart.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2988692/2ecd6b5d-5869-ed2b-2100-8842179510ca.png)
Rashmi and Gilad-Bachrach (2015) より引用

# DARTのアルゴリズム
ニューラルネットにおけるドロップアウトはユニットを削除することにより行われるが、DARTにおけるドロップアウトは既に作成されている木を削除することにより行われる。すなわち、各イテレーションにおいて、既に作成されているそれぞれの木を独立に確率 で一時的に削除し、残った木のみを用いたモデルによる予測値の残差（より一般には損失関数の勾配）をターゲットとして新しい木を学習する。ただし、学習された新しい木をそのままモデルに追加すると、一時的に削除された木の効果とあわさって予測値が「オーバーシュート」するので、適切にスケーリングを行う（これはニューラルネットのドロップアウトにおいて、推論時にスケーリングを行うことと似ている）。

具体的には、次の手続きを定めたイテレーション数の上限まで繰り返す（細かい部分を少し省いているがだいたいこんな感じ、詳細は論文参照）。

$t$ステップ目における木の集合を$M = \lbrace T_i \rbrace_{i=1, \dots, t-1}$とする。

1. 定めた確率$p_{drop}$にしたがい、ドロップアウトする木の集合$D \subset M$をランダムに選択する。
2. 残った木$\hat{M} = M \setminus D$に基づくモデルの予測値の残差（より一般には損失の勾配）をターゲットとし、新しい木$T_t$を学習する。
3. 新しい木をスケーリングして集合に追加する：$M \leftarrow M \cup \lbrace \frac{T_t}{|D|+1} \rbrace$
4. ドロップアウトされた木もスケーリングする：$T \leftarrow \frac{|D|}{|D|+1} T \quad (T \in D)$

論文では、GBDT、ランダムフォレストがDARTの極端な場合と見なせることが述べられている。ドロップアウトを全く行わない場合が通常のGBDTに対応し、逆に全てドロップアウトする場合がランダムフォレストに対応する。
【Python】【boto3】S3からオブジェクトを分割して読み込む

AWSLambdaの限られたメモリの中で大きなファイルをダウンロードする必要がありそうだったので調査した。
Lambdaの起動時間の最大15分については、一旦考えないことにする。    

```
import boto3


def s3_get_object_chunk(bucket, key, buf_size=4096):
    offset = 0
    s3_client = boto3.client('s3')
    file_size = s3_client.get_object(Bucket=bucket, Key=key)['ContentLength']
    print('file_size: ', int(file_size))
    chunk_cnt = file_size // buf_size
    floor = file_size if file_size<buf_size else file_size%buf_size
    chunk_list = [buf_size] * chunk_cnt
    chunk_list.append(floor)
    for chunk in chunk_list:
        if chunk < 1:
            break
        read_range = 'bytes={start}-{end}'.format(start=offset, end=offset+chunk-1)
        response = s3_client.get_object(Bucket=bucket, Key=key, Range=read_range)
        read_bytes = response['Body'].read()
        offset += chunk
        yield read_bytes


if __name__ == '__main__':
    bucket = 'bucket'
    s3_path = 'test.csv'
    local_output = 'output.txt'

    local_file = open(local_output, 'wb')

    total_size = 0
    for read_bytes in s3_get_object_chunk(bucket, s3_path):
        local_file.write(read_bytes)
        total_size += len(read_bytes)

    local_file.close()
    print('written: ', str(total_size))
```

## 参考
https://stackoverflow.com/questions/51085539/streaming-in-chunking-csvs-from-s3-to-python
C++でタイムスタンプリクエストを作成する

OpenSSLを使って、C++でタイムスタンプリクエストを作成しました。
OpenSSLのmanページにタイムスタンプリクエストに関連する関数が見つからなかったので、OpenSSLのソースコードを見ながら作ってみました。

参照したソースコードです。

https://github.com/openssl/openssl/blob/master/apps/ts.c

## サンプル

```C++

#include <iostream>
#include <string>
#include <vector>
#include <openssl/sha.h>
#include <openssl/ts.h>
#include <openssl/bio.h>

int main()
{
    std::string str("Hello World");

    unsigned char uchash2[32];
    size_t hashSize2 = sizeof(uchash2);

    SHA256((const unsigned char*)str.data(), str.size(), uchash2);

    TS_REQ* ts_req = NULL;
    TS_MSG_IMPRINT* msg_imprint = NULL;
    X509_ALGOR* algo = NULL;
    const EVP_MD* md = NULL;
    BIO* out1 = NULL;
    BIO* out2 = NULL;

    do {
        //タイムスタンプリクエストの作成
        if ((ts_req = TS_REQ_new()) == NULL)
            break;
        if (!TS_REQ_set_version(ts_req, 1))
            break;

        //MessageImprintの作成
        if ((msg_imprint = TS_MSG_IMPRINT_new()) == NULL)
            break;

        if ((algo = X509_ALGOR_new()) == NULL)
            break;

        if ((md = EVP_get_digestbyname("sha256")) == NULL)
            break;

        if ((algo->algorithm = OBJ_nid2obj(EVP_MD_get_type(md))) == NULL)
            break;

        if ((algo->parameter = ASN1_TYPE_new()) == NULL)
            break;
        algo->parameter->type = V_ASN1_NULL;

        if (!TS_MSG_IMPRINT_set_algo(msg_imprint, algo))
            break;

        if (!TS_MSG_IMPRINT_set_msg(msg_imprint, uchash2, hashSize2))
            break;

        if (!TS_REQ_set_msg_imprint(ts_req, msg_imprint))
            break;

        if (!TS_REQ_set_cert_req(ts_req, 1))
            break;

        if ((out1 = BIO_new_file("HelloWorld.tsq.txt", "w")) == NULL)
            break;

        TS_REQ_print_bio(out1, ts_req);

        if ((out2 = BIO_new_file("HelloWorld.tsq", "wb")) == NULL)
            break;

        i2d_TS_REQ_bio(out2, ts_req);

    } while (0);

    if (msg_imprint != NULL) {
        TS_MSG_IMPRINT_free(msg_imprint);
    }
    if (ts_req != NULL) {
        TS_REQ_free(ts_req);
    }
    if (out1 != NULL) {
        BIO_free_all(out1);
    }
    if (out2 != NULL) {
        BIO_free_all(out2);
    }
    
}
```

作成したタイムスタンプリクエストを以下のOpenSSLのコマンドで表示してみました。

```
>openssl ts -query -in HelloWorld.tsq -text
Using configuration from C:\Program Files\Common Files\SSL/openssl.cnf
Version: 1
Hash Algorithm: sha256
Message data:
    0000 - a5 91 a6 d4 0b f4 20 40-4a 01 17 33 cf b7 b1 90   ...... @J..3....
    0010 - d6 2c 65 bf 0b cd a3 2b-57 b2 77 d9 ad 9f 14 6e   .,e....+W.w....n
Policy OID: unspecified
Nonce: unspecified
Certificate required: yes
Extensions:
```
【Flutter】Firestoreへ更新する時にセキュリティルールによるエラーをキャッチする

# セキュリティルールによって弾かれてもそれがわからない
セキュリティルールによってエラーとなった場合、アプリ側で成功したのか失敗したのか分かりません。
エラーキャッチする方法です。
```dart
Firestore.instance.collection("collection").document("document").setData({
    // data
  }).then((_) {
    // 更新成功時の処理
  }).catchError((error) {
    // エラー表示
    ScaffoldMessenger.of(context).showSnackBar(SnackBar(
      content: Text("セキュリティルールによるエラーが発生しました: $error"),
    ));
  });
```
#  エラーキャッチのサンプル
新規作成する場合のサンプルです。
ボタンを押して登録処理を行います。
```dart
child: ElevatedButton(
    onPressed: () {
        FirebaseFirestore.instance
            .collection('items')
            .doc()
            .set({
                'name': name,
                'created_at': timestamp,
                'updated_at': timestamp
            }).then((value) {
                    ScaffoldMessenger.of(context)
                        .showSnackBar(SnackBar(content: Text("登録に成功しました")));
                    }).catchError((error) {
                        ScaffoldMessenger.of(context).showSnackBar(
                            SnackBar(content: Text("セキュリティルールにるエラー： $error")));
                    });
    },
    child: const Text('登録'),
),
```
Error: Could not locate device support files.への対処

iOSの実機ビルドをする際に

```
Error: Could not locate device support files. Taisuke may be running a version of iOS that is not supported by this version of
  Xcode. You can download the latest version of Xcode from the Mac App Store or developer.apple.com. (code 601)
```

というエラーに遭遇しました。


こちらはiOSのバージョンとXcodeのバージョンの整合性が取れていない時に発生するエラーのようでエラーを解消するためには、必要なiOSバージョンをダウンロードして、それをXcode.app内のDeviceSupportに置く必要があるようです。

まずは以下のサイトから最新バージョンをダウンロード。

https://github.com/filsv/iPhoneOSDeviceSupport


サイトには

１：Download version you need listed above;
２：Unzip it;
３：Close Xcode;
４：Copy and paste unziped folder by path:

/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport/

５：Reopen Xcode;

と書かれていたので、その通りにダウンロードしたXcodeのバージョンを解凍して、Xcode閉じて…と進めていったのですが

４：Copy and paste unziped folder by path:で詰まりました。。

Finderを開いてみるとApplications配下にXcode.appはあるものの、それ以降がない…

こちらの解決方法は2種類あるようで

①Finderを開いた状態で、command + Shift + Gを押すとファイルパスを入力できるようになるので、そこに

/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport/

をコピペ。

![スクリーンショット 2023-02-04 15.22.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1248189/213e9071-8164-1938-233e-c2d21f1014e6.png)

②Finderのアプリケーション内にあるXcode.appを右クリックして（おそらくFinderのアプリケーション内にあるはず）、「パッケージ内容を表示」を選択。Contentsが出現するので、そのままDeveloper→Platforms→iPhoneOS.platform→DeviceSupportと進んでいく。

![スクリーンショット 2023-02-04 15.25.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1248189/5ade638a-0ae0-3fb8-ac78-9fe9713db1c1.png)

![スクリーンショット 2023-02-04 15.27.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1248189/2ac8ef60-0198-17d7-ab07-3fe0e2b891c8.png)


上記の方法のいずれかでDeviceSupportまで行って、ダウンロードしたバージョンをコピペして、Xcodeを再度開き直す。


が、

エラーが解消せず。。

最終的には最新のXcodeをダウンロードして、macOSを最新にしたらエラーが解消できました！

XcodeとmacOSを最新にするのに、とても時間がかかったので、時間に余裕がある時に行うといいかもしれません！

同じ現象で困っている方の参考になれば幸いです！

※Xcodeを最新にしたことで、他のプロジェクトでビルドが通らなくなってしまったので、Xcodeを最新にする際は、前のXcodeも残しておいた方がいいかもしれません。


参考サイト
https://god48.com/xcode-could-not-locate-device-support-files

https://qiita.com/luccafort/items/7eefe4dcf50cca9d8c02
【デプロイ】リポジトリからの環境構築②(Laravel)

この記事では以下の環境で行ったものです。
・OS：Windows10 Pro
・IDE(統合開発環境)：Cloud9
・Cloud9に入っているPHPバージョン：7.4.30
・レンタルサーバー：Conoha WING
・Conohaに入っているPHPバージョン：8.1.12
・バージョン管理システム：GitHub

ここでは、<font color="red">「Cloud9(ローカル環境)で作成したLaravelプログラムがGitHubと繋がっている」</font>という前提で、そのプログラムをCloneし、Conoha WINGのサーバーを使って、本番環境に公開(デプロイ)する方法をお伝えします。

＜方針＞
Ⅰ. Conoha WINGでサーバーを作成し、Cloud9からサーバーへログイン
Ⅱ. GithubのプログラムをClone
Ⅲ. 本番環境の設定
Ⅳ. 本番データの投入
Ⅴ. 動作確認、本番環境の更新

長編になるため、方針ごとで記事を分けております。

今回は、「Ⅱ. GithubのプログラムをClone」に関する記事です。

前編の「Ⅰ. Conoha WINGでサーバーを作成し、Cloud9からサーバーへログイン」に関する記事は下記URLからご覧ください。
https://qiita.com/kamitch/items/87030f98e5deb49badf9

#### Ⅱ. GithubのプログラムをClone
・まず、Cloud9のターミナルを使ってConoha WINGのサーバー(本番環境)にログインしましょう。前編の記事で説明している`sshコマンド`を使用します。
![スクリーンショット 2023-02-04 143245.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2835861/d0914670-4c75-d7de-e1a0-b9933dc3b456.jpeg)
これでサーバーにログイン完了。ここから、Cloud9のターミナルを使ってサーバー内を遠隔操作していきます。

##### ①Composerのインストール
・次に、composerをインストールします。composerとは、「PHP向けのソフトウエアおよび必要なライブラリの依存関係を管理するパッケージ管理システム」のことです。Conohaの場合、始めからPHPは入っていますが、composerは入っていないためインストールする必要があります。
`curl -sS https://getcomposer.org/installer | php` をターミナルに入力しましょう。

・下記コマンドでアップデートを確認しましょう。
`./composer.phar self-update`
下図のように、`You are already using the latest…` と出れば、最新バージョンのComposerをインストールできていることを表しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2835861/e1e2f4c1-63d7-9f38-619e-c61bcd87a679.png)

##### ②クローン先のフォルダ作成
・サーバーにクローン先のフォルダを作成しましょう。
`mkdir 任意のフォルダ名` を入力してください。
作成できているかは、Conohaのファイルマネージャー(サーバーのディレクトリ構成が見れる場所)から確認できます。
![Laravel最終課題 備忘録 (10).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2835861/e89de74b-1ae2-e71a-71f0-798ee55bfd11.jpeg)
・作成したら、`cdコマンド` でこのフォルダに移動しましょう。

##### ③GitHubのクローンURLを取得
前述②で、クローンしたプログラムを入れるための箱は用意できました。
次はGitHubからプログラムを持ってきて、作った箱に入れましょう。

・GitHubにログインし、クローンしたいプログラムのページまで移動します。

・移動したら下図の手順でクリックし、URLをコピーしましょう。
![Laravel最終課題 備忘録 (12).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2835861/88832f3f-defe-9de1-ae18-68eda348fd02.jpeg)
・Cloud9のターミナルで、`git clone コピーしたurl` を入力してください。

・最後に、Conohaのファイルマネージャーからクローン先のフォルダ内にプログラムが入っていることを確認しましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2835861/4a4aabf2-588d-581d-5be9-4e3ecfb767a9.png)

##### あとがき
いかがでしたしょうか。

今回は、「GithubのプログラムをClone」する方法をお伝えさせていただきました。

次回は、クローンした Laravel プロジェクトが動作するように各種設定を行っていきたいと思います。

できるだけ細かく書いたつもりですが、分からないことなどありましたら、ぜひコメントなどお願いいたします。
Canvas LMSで成績データをインポートする際の注意事項

# 現象
* 授業担当者として、Canvas LMSで課題の成績データをCSVファイルで一括アップロードする際、ファイルの形式が正しいのに、インポートに失敗することがある。
* エラーは出ず、「すでに登録済のデータと変更ありません」という意味のメッセージが出て正常に終了する。CSVファイルには新しい成績情報が正しく含まれているのに。

# ありえる原因と解決方法

## 禁則事項 `final`
* **原因：** １行目の課題名に文字列 `final` （大文字小文字を問わず）が含まれているとその課題（CSVファイルの列）が読み込まれなくなる。
* **解決方法：** １行目の課題名から文字列 `final` を削除する。結果として課題名がCanvas上の課題名と不一致になっても課題IDが合致していればOK
    * 例：`Final Grade (23456)` だと正しくインポートできないが、`Grade (23456)` などと書きかえれば、課題 Final Grade (23456) を上書きするように正常に読み込まれる。

# おしまい
* 今後も見つけた事象があれば書き足していきます。
【React】<input type='number'>にマウススクロールでの数値入力を無効にする

# サンプル
```typescript
<input type= 'number' />
```
といった数値入力の入力フォームがあるとします。
画面上でこの入力フォームにフォーカスし、マウスをスクロールすると数値が入力されてしまう場合があります。
# 解決策
これを無効にするには、`e.preventDefault();`だけでは無効にできません。
`.addEventListener`の第３引数で`passive: false`が必要になります。
*Chrome上では、デフォルトでtrueになっているそう。
なので、`<input>`に、フォーカス時にこの`passive`をfalseにさせます。
```diff_typescript
<input 
  type= 'number' 
  onFocus={(e) =>
          e.target.addEventListener(
            'wheel',
            (e) => {
              e.preventDefault();
            },
            { passive: false }
          )
        }
/>
```
これで入力フォームがフォーカスされている時のマウススクロールが無効化され、数値が入力されません。
以上が解決方法でした。

# 参考にした記事
https://blog.myntinc.com/2019/10/javascript-3.html

https://qiita.com/kyntk/items/5c16846a9b0e0b36154c