OmniAICreator/Qiita-1.07M huggingface dataset:
初心者でも、これだけやればなんとかなるハッカソン！！

サポーターズ主催の
『【オンライン開発合宿vol.12】思わずニヤッとするアプリケーションを作ろう！』 
に参加しました。そこで、ハッカソンに参加しようか迷っている人がいるのではないかと思い、この記事を執筆するに至った。

# 0.はじめに
### こんな方に読んでほしい
<ul>
<li>プログラミングを授業で勉強しているが、ハッカソンに参加することはしていない方</li>
<li>趣味でプログラミングを勉強しているが、ハッカソンは無理やろwwwと思っている方</li>
<li>ハッカソンに参加する学生の裏話に興味のある方</li>
</ul>

### 自己紹介
私立文系の1回生
今年からプログラミングを始め、JavaScript Reactといったフロントを中心に勉強している学生です。

# 1.ハッカソンに参加した動機とその時点での技術力
### 動機
動機は正直ありませんでしたが、何かを得れだろうと判断したであろう自分が、数カ月前に<strong>勢い</strong>で申し込んでいたので、参加することになりました（笑）
なので、ハッカソンの参加slack通知が来たときは焦りました。

### 技術力
<ul>
<li>HTML CSS</li>
<li>Figma</li>
デザインカンプを作るときに必要なツール
<li>JavaScriptの浅い知識</li>
最近までオブジェクトの取り出し方とかを分かっていなかったレベルでハッカソンに参加する前に基本文法を学んでいた真っ最中だった。
<li>Reactの浅すぎる知識</li>
JSを分かってないのに、これに手を出したので苦しんでいる状態。
<li>Git GitHub</li>
エンジニアの方たち8人ほどとReactでチーム開発をしたことがあり、add commit push pull prなどは出来た。
</ul>
これらを見てもわかる通り、アプリケーションを作るには絶妙に厳しいラインにいた自分ですが、ハッカソンに参加することになりました。

# 2.メンバーと成果物

### メンバー
ハッカソンはチーム開発か個人開発か選べるのですが、今回はチーム開発を選び、ランダムで生成されたチームに所属することになりました。で、僕と組ませて頂いた方々がこちら↓

* 1-インターンの先でvue.jsを使っている同じ大学の先輩&フロントマン
* 2-情報系を専攻しており、プログラミング好きで陽気な先輩
* 3-開発経験はあまりないものの、情報系の学部で修士という猛者

で、
* 僕

という4人でした。
つまり、自分がプログラミングの技術的には一番劣るが、フロントだけに特化してみればまだマシという立ち位置。


### 成果物と各人の担当
#### [アドバスター](https://t.co/js1nvlvSRW)という広告撃退アプリケーション
vue.jsを使ったWEBアプリケーションです。
一度遊んでみてください。

* 大学の先輩→vue.jsの技術リーダー
* 陽気な先輩→ゲームの機能開発
* 猛者→機能開発とスタイル
* 僕→デザインカンプの作成、多少の機能開発とスタイル

# 3.僕がチームに貢献したこと（貢献できるよう意識したこと）
技術的に大きく貢献することは難しいと最初から想定していたので、以下の3つで貢献するようにした。
<ol>
 <li>会議の際に積極的に進行を務め、毎会議後にdiscordで決めたことと次の課題などをまとめたことを投稿する</li>
こんな感じ　↓
<img width="70%" alt="8583159A-8312-412B-941C-1FAF0AD0D3C7_1_201_a.jpeg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1242042/527d468f-1e05-65b5-e3e9-5997e6acd7d7.jpeg">
 <li>Figmaでデザインカンプを作る</li>
そこまで凝ったもの、凄いものは作れませんが、デザインツールが使えてこれから作るものを具現化できるのは、開発をスムーズに進めるためには必要な要素の一つだと思います。

実際に作ったものはこちら　↓
<img width="70%" alt="スクリーンショット (206).png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1242042/8144fc2f-75b5-2f03-092a-a9cd095fdbd8.png"/>
 <li>機能実装の際に、質問するときは、「ここまではわかるけどそれを実装する仕方が分かりません」みたいにする。</li>

discordで画面共有してこの質問をすれば、拙い言葉であっても伝わります！
</ol>

# 4.これだけはやっとけばなんとかなるハッカソン

* Git GitHub

これを一から教えるとなるとチーム開発のスタートに立つのが遅れてしまい、ハッカソンをやり切ることすら危ぶまれてしまう。
（個人開発であれば、少し変わりますが、リポジトリのurlは提出しないといけないのでどっちにしろ触ると思います。）

* プログラミング言語何か一つの知識

web系を作るにしても何を作るにしても、ある程度知っている言語があれば、なんとかなる。はず

* 人に教えを乞うときに忘れてはいけない謙虚な気持ちと姿勢

# 最後に
ハッカソンは本当に勉強になります。
ビビらず、「自分なんか」なんて思わず、参加しましょう！！
困ったらメンター陣に相談すれば何とかなります。
開発経験をハッカソンで積んで強強エンジニアを目指しましょう！！
僕も精進します（笑）

##### 最後まで読んでいただきありがとうございました。
次回は【ハッカソンで「強強エンジニア」が言っていた有用な情報】を投稿します！　ではまた。
テスト技法ツールGIHOZのデシジョンテーブルをテストケースに変換するツール

# はじめに
[テスト技法ツールGIHOZ] (https://www.veriserve.co.jp/gihoz)で作成したデシジョンテーブルをテストケースに変換するツールを作ってみました。この記事ではそのツールを紹介します。
デシジョンテーブルは、条件の組み合わせとその結果を整理できる表です。デシジョンテーブルを作って、テストケースを作るテスト技法のことを、デシジョンテーブルテストと呼びます。

デシジョンテーブルテストの詳細は以下のページに解説がありますので、そちらをご覧ください。

https://www.veriserve.co.jp/gihoz/decision-table.html

# ツールの概要
デシジョンテーブルをテストケースに変換するツールです。テストケースには色々なフォーマットがあると思いますが、この記事ではとりあえず、デシジョンテーブルで指定した条件の組み合わせと動作結果が横1行に並んだものを指すことにします。

具体的には、以下のようなデシジョンテーブルがあったとします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/188690/af88a298-9fa2-7fc2-273b-6fefc91a6859.png)

このテーブルを以下のように変換します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/188690/6bd9ea3d-efb6-88d1-e664-d945806a8d43.png)

# ソースコード・動作確認環境
ソースコードは[こちらのGitHubのリポジトリ] (https://github.com/ktanizaki/gihoz-utilities)で公開しています。
以下の環境で動作することを確認しました。

* OS: Windows10
* 言語: Python 3.9.5

# 使い方

まずPython3をインストールし、次にpandasというライブラリをインストールします。pandasはpipコマンドが使える方は以下のコマンドでインストールできます。Anacondaなどをご利用の方はご自分でコマンドを調べてください。

```
$ pip install pandas
```

ここまでで環境の準備は完了です。
GIHOZのデシジョンテーブル画面で適当にデシジョンテーブルを作成してcsvファイルとしてダウンロードします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/188690/a5442998-393f-72b8-b309-4b0e29e3de25.png)

上記のGitHubのリポジトリからソースコードをダウンロードして、コマンドプロンプトやPowerShellでソースコードの保存先を開いて、以下のコマンドを打てば実行できます。

```
$ python transform_decisiontable.py [デシジョンテーブルのCSVファイル名]
```

[デシジョンテーブルのCSVファイル名]は、GIHOZからダウンロードした直後は`Decision-table_yyyy-mm-dd.csv`といったファイル名になっています。（`yyyy-mm-dd`の部分はダウンロードした日付です）

実行に成功するとtransformed.csvという名前のファイルが生成されます。transformed.csvをExcelなどで開くと、以下のようになっており、デシジョンテーブルからテストケースの形に変換できたことが分かります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/188690/7f3a4303-bdab-ff9e-3033-c7c17db79839.png)

変換のルールは以下の通りです。

- サブ条件（条件の子階層）が存在する場合、「Y」が指定されたサブ条件の名称をテストケースに反映します
- サブ条件がない場合、「Y」が指定された条件は名称をそのままテストケースに反映し、「N」が指定された条件は「NOT 名称」をテストケースに反映します
- 動作に複数の「X」を指定していた場合、動作の名称をカンマ区切りの文字列に結合してテストケースに反映します

GIHOZは条件記述部に子階層を作ることができるので、その記法に対応したものにしています。上図の例でいうと、「会員」という条件の子階層に「通常会員」と「ゴールド会員」が記載されている部分が該当します。

変換後のテストケースは、Excelのテストケースフォーマットに入れ込んで管理するなり、テスト管理ツールに投入するなり、テスト自動化ツールの入力データとして使うなり、自由に使うことができます。

# おわりに
GIHOZで作成したデシジョンテーブルをテストケースに変換するツールを作ってみた、という記事でした。GIHOZを使わないでExcelなどでデシジョンテーブルを作った場合でも、GIHOZからダウンロードしたcsvと同じ形式でcsvとして保存してやれば、このツールで同じようにテストケースに変換できます。テストケース作成の一助になればと思います。

今回はcsvファイルをGIHOZからダウンロードしてツールの入力として使いましたが、GIHOZはAPIも公開しているため、APIを呼び出してGIHOZから直接データを読み取って、変換するようにしても面白いかもしれません。

https://veriserve.github.io/gihoz-api/
operator+していないのに演算できてしまう

演算子のオーバーロードの動作確認で以下のコードを実行してみた。
operator+(int) は if 0 しておく。

``` operator.cpp
#include<iostream>
class myInteger {
    int value;
public:
    int operator + ( myInteger obj ) {
        return this->value + obj.value;
    }
#if 0
    int operator + ( int num ) {
        return this->value + num;
    }
#endif
    myInteger(int value) { this->value = value; }
};

int main() {
    myInteger obj1(10), obj2(50);
    int num = 5;

    std::cout << obj1 + obj2 << std::endl;
    std::cout << obj1 + num  << std::endl;

    return 0;
}
```

結果は、
>root@2af9a6fb2cb6:/cpp/operatpr# g++ -g -O0 operator.cpp 
root@2af9a6fb2cb6:/cpp/operatpr# ./a.out 
60
15
root@2af9a6fb2cb6:/cpp/operatpr# 

ん？実行できちゃった。
`obj1 + num ` はコンパイル時か実行時にエラーになると思ったのだけれど。

gdb でも確認してみる。

>(gdb) p obj1 + obj2
$1 = 60
(gdb) p obj1 + num
Cannot resolve function operator+ to any overloaded instance
(gdb) p obj1 + 5
Cannot resolve function operator+ to any overloaded instance
(gdb) 

こちらは予想通り。
ちなみに、operator.cpp の if 0 を有効にした場合は、

>(gdb) p obj1 + obj2
$1 = 60
(gdb) p obj1 + num
$2 = 15
(gdb) p obj1 + 5
$3 = 15
(gdb) 

となり、これは想定通りの動作。
もうちょっと調べてみる。


-----
コメントで丁寧に教えていただきました。
ありがとうございます。

### 原因：暗黙の型変換によるもの

#### 暗黙の型変換で使われているコンストラクタをecplicit 修飾で修飾する

```
    explicit myInteger(int value) { this->value = value; }
```

>operator.cpp: In function 'int main()':
operator.cpp:21:23: error: no match for 'operator+' (operand types are 'myInteger' and 'int')
     std::cout << obj1 + num  << std::endl;
                  ~~~~~^~~~~
operator.cpp:5:9: note: candidate: int myInteger::operator+(myInteger)
     int operator + ( myInteger obj ) {
         ^~~~~~~~
operator.cpp:5:9: note:   no known conversion for argument 1 from 'int' to 'myInteger'

コンパイルで弾かれた。当初期待していた動作となった。


#### 左辺も受け取りたい
メンバ関数での演算子のオーバーロードでは引数は１つしか受け取れない。
メンバ関数ではprivateメンバにアクセスできるようにしておいて、
非メンバ関数で2項を受け取る関数を定義する

```
class myInteger {
    int value;
public:
    myInteger &operator +=(const myInteger &r) {
        value += r.value;
        return *this;
    }
    myInteger &operator +=(int r) {
        value += r;
        return *this;
    }
    explicit myInteger(int value) { this->value = value; }
};

// クラス同士
const myInteger operator+(const myInteger& l, const myInteger& r) {
    return myInteger(l) += r;
}
// 左辺がint
const myInteger operator+(int l, const myInteger& r) {
    return myInteger(r) += l;
}
// 右辺がint
const myInteger operator+(const myInteger& l, int r) {
    return myInteger(l) += r;
}
```
Webシステムのセキュリティについて

Webシステムがどんどん発達していくのに伴ってセキュリティ対策の重要性も高まっています。
情報セキュリティの基礎となる用語をまとめました。

#情報セキュリティ

`機密性`・・・第三者がアクセスできないような状態を確保すること
`完全性`・・・情報が改ざんされていない状態を確保すること
`可用性`・・・情報のアクセスを認められたものがいつでも情報にアクセスできる状態を確保すること

上記三つの要素を維持すること


#悪意を持った第三者による攻撃例
###パスワードクラッキング　
####1. 辞書攻撃
よくパスワードとして使われる単語をまとめたリストを用いて順にログインしてパスワードを当てる手法
####2. ブルートフォース攻撃
パスワードに組み合わせられる文字列を全てのパターンを試してパスワードを当てる手法

---

###Dos（Denial of service）攻撃
####1. SYN Flood攻撃
大量のSYNパケットをWebサーバーに送り付け、Webサーバーが新しいユーザーの接続に対応できなくする攻撃

####2. F5攻撃
大量のWebページの再読み込みリクエストをWebサーバーに送り付け、Webサーバーが高負荷の状態になり、他のユーザーのリクエストに反応できなくなるようにする攻撃

---

###セッションハイジャック

悪意のある第三者がCookieの中身を盗み取り、そのCookieのデータを利用してあたかもログイン済みのユーザーとして振る舞い、個人情報を取得すること

---

###ディレクトリトラバーサル
URLを改変することでWebで公開されていないファイルに接続し、Webサーバーへのログイン情報を盗み出し、Webサーバーへ不正なログインなどをする攻撃

---

###SQLインジェクション
ログイン情報などDBサーバーと連携して処理を行う必要のある情報に、DBサーバーが解釈できるデータを一緒に送信することで、DBに意図しない操作をさせる攻撃


#参考
[イラスト図解式 この一冊で全部わかるWeb技術の基本](https://www.amazon.co.jp/%E3%82%A4%E3%83%A9%E3%82%B9%E3%83%88%E5%9B%B3%E8%A7%A3%E5%BC%8F-%E3%81%93%E3%81%AE%E4%B8%80%E5%86%8A%E3%81%A7%E5%85%A8%E9%83%A8%E3%82%8F%E3%81%8B%E3%82%8BWeb%E6%8A%80%E8%A1%93%E3%81%AE%E5%9F%BA%E6%9C%AC-%E5%B0%8F%E6%9E%97-%E6%81%AD%E5%B9%B3/dp/4797388811/ref=asc_df_4797388811/?tag=jpgo-22&linkCode=df0&hvadid=295704876452&hvpos=&hvnetw=g&hvrand=13165480539858370255&hvpone=&hvptwo=&hvqmt=&hvdev=c&hvdvcmdl=&hvlocint=&hvlocphy=1009333&hvtargid=pla-526274317363&psc=1&th=1&psc=1)
Djangoのon_commitをテストする

overview
---
Djangoで開発していると、トランザクションを張る部分が出てきます。
そして、「このトランザクションがコミットされたらアクションを起こしたいな…」というニーズもあるはずです。
そんな時役に立つのがtransaction.on_commit。

```py
from django.db import transaction

def do_something():
    pass

transaction.on_commit(do_something)
```
※ lambdaでラップすれば変数を渡すこともできる。 e.g. `transaction.on_commit(lambda: do_something(arg))`


通常はメール送信やcelery taskなど、投げっぱなしのactionで使用することが多いためテストを実装するというパターンは少ないかもしれない。
しかし、実際にテストするとなると簡単にはいかないのだ…

訪れるAssertionError
---

(検証用だからゴミコード許して)

```py:views.py
from django.db import transaction
from .models import Animal


def do_something(instance):
    instance.has_changed = True
    instance.save()


@transaction.atomic
def animal_create(name):
    animal = Animal()
    animal.name = name
    animal.save()
    transaction.on_commit(lambda: do_something(animal))
```

Animalを生成するコードがあったとします

```py:tests.py
from django.test import TestCase
from .views import animal_create
from .models import Animal


class AnimalCreateTestCase(TestCase):
    def test_animal_create(self):
        animal_create('mouse')
        self.assertEqual(1, Animal.objects.count())
        animal = Animal.objects.first()
        self.assertEqual('mouse', animal.name)
        self.assertEqual(True, animal.has_changed)
```

それをテストします。

```console
$ ./manage.py test

======================================================================
FAIL: test_animal_create (app.tests.AnimalCreateTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/takino/PycharmProjects/workshop/django_4_0/app/tests.py", line 12, in test_animal_create
    self.assertEqual(True, animal.has_changed)
AssertionError: True != False
```

こけます :innocent: 


なぜ？
---

* on_commitはcommit後に実行される
* TestCaseはtransactionレベルで実行され、テスト終了後にrollbackされるためtransactionはcommitされない
* commitされないということはon_commitが走らない :innocent: 


じゃあどうするの
---

### TransactionTestCaseを使用する

```py:tests.py
from django.test import TransactionTestCase
from .views import animal_create
from .models import Animal


class AnimalCreateTestCase(TransactionTestCase):
    def test_animal_create(self):
        animal_create('mouse')
        self.assertEqual(1, Animal.objects.count())
        animal = Animal.objects.first()
        self.assertEqual('mouse', animal.name)
        self.assertEqual(True, animal.has_changed)
```

テストは成功します :thumbsup: 

```console
$ ./manage.py test

Found 1 test(s).
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
.
----------------------------------------------------------------------
Ran 1 test in 0.029s

OK
```

TransactionTestCase vs TestCase
---

### TestCase

* テストはトランザクション内にラップされる
* テスト実行後はロールバックされる
* コミットされないのでon_commitは走らない

### TransactionTestCase

* トランザクションは作成しない
* テスト実行後はDBをフラッシュする
* コミットをするのでon_commitが走る
* テスト間でDBのフラッシュが行われるため、TestCaseより低速

最後に
---
あたりまえのようにTestCaseを使っていると意外と気づかないものだった。
別スレッドの処理でもTransactionTestCaseを使う必要があるため、
実装内容によってどのテストクラスを使用するべきかなどの検討はちゃんとしなければならない。

まあ、本記事はDjangerにとっては当たり前の内容だと思うので、備忘録的に記事を残しつつキーボードを止めることにする。
メモ: チェリー本の気づいたこと、印象に残ったことメモ 12/28


[プロを目指す人のためのRuby入門（通称チェリー本） 改訂2版](https://www.amazon.co.jp/%E3%83%97%E3%83%AD%E3%82%92%E7%9B%AE%E6%8C%87%E3%81%99%E4%BA%BA%E3%81%AE%E3%81%9F%E3%82%81%E3%81%AERuby%E5%85%A5%E9%96%80-%E6%94%B9%E8%A8%822%E7%89%88-%E8%A8%80%E8%AA%9E%E4%BB%95%E6%A7%98%E3%81%8B%E3%82%89%E3%83%86%E3%82%B9%E3%83%88%E9%A7%86%E5%8B%95%E9%96%8B%E7%99%BA%E3%83%BB%E3%83%87%E3%83%90%E3%83%83%E3%82%B0%E6%8A%80%E6%B3%95%E3%81%BE%E3%81%A7-Software-Design/dp/4297124378/ref=asc_df_4297124378/?tag=jpgo-22&linkCode=df0&hvadid=529520598444&hvpos=&hvnetw=g&hvrand=15532457502370004850&hvpone=&hvptwo=&hvqmt=&hvdev=c&hvdvcmdl=&hvlocint=&hvlocphy=9053344&hvtargid=pla-1436120556850&psc=1&th=1&psc=1)


をやっていきます


```
オブジェクト.メソッド(引数1,引数2,引数３)
```


引数のカッコは省略することができる

```
オブジェクト.メソッド 引数1,引数2,引数３
```

業務でみたことあったな、こんなコード。

```ruby

10.to_s(16)
# => "a"
```

数値を16進数の文字列に変換する

10.to_s(19)

これも実行結果は、"a"になった。
なんで。。。
一旦チェリー本を読み進めたいので、次。


```

=begin

こめんとだからエラーにならない？

=end
```

と複数行でコメントアウトできるっぽい。
ちなみに手元のルビーファイル(file.rb)
に上記のコードを書いて、ruby　file.rbと実行したら

```
cherry.rb:1: syntax error, unexpected '='
=bigin
cherry.rb:5: syntax error, unexpected '=', expecting end-of-input
```

とバリバリエラーが起きた。


追記、biginではんくbeginでした。。。。

biginとendは、コード中にRDocというドキュメントを埋め込むために用意されている構文らしい。

>RDocは、Rubyプログラミング言語用の埋め込みドキュメントジェネレーターです。 Rubyのソースコードを分析し、Rubyのオブジェクトとメソッドのページの構造化されたコレクションを生成します。

wikipedia引用。


Yardの元から組み込まれてる版的な感じですかね。


# 識別子と予約後

漢字や、ひらがなも識別子として使える

Ruby 3.0では _1 _2 _3 _4 _5 .. _9
が予約語になってる

# 空白文字

空白文字はいくつ続いてもいい。

しかり、メソッド呼び出しの（）の手前にスペースを開けたら構文エラーが発生する場合もある。

# リテラル

数値の123,文字列の"Hello"など、ソースコードに直接埋め込むことができる値のことをリテラルといいます。

数値、文字列

# 変数

数値から始まる変数名は使えない、エラーになる

```
c,d = 10

```

右辺の数が少ない場合はnilになる

```
c #=> 10
d #=> nil
```


右辺の数が多い場合ははみ出した値が切り捨てられる

```

e,f = 100, 200 ,300
e => 100
f => 200

```

アンダースコアで始まる変数名はあまり使わない。が
**「宣言するが使わない」という意味をもたせてあえてアンダースコア始まりの変数名が使われるケースもあります。**

```ruby

# splitメソッドをつかって人名をスペースで二分割するが、使うのはファーストネームだけ
# ラストネームは使わないので、変数名をアンダースコア始まりにする

first_name , _last_name = 'Scott Tiger'.split(' ')
p first_name #=> scott

# このバリエーションで、変数名をアンダースコア一文字にしてしまうケースもある

first_name , _ = 'Scott Tiger'.split(' ')
p first_name #=> scott

```

```ruby

# 両方にhelloを代入する
 a = b = 'hello'

a.upcase!

# aだけを大文字にしたはずなのにbも大文字に変わってしまった

a #=> "HELLO"
b #=> "HELLO"

#別々に代入した場合は、この問題はおきない

c = "hello"
d = "hello"

c.upcase!

c #=> "HELLO"
d #=> "hello"


```


 a = b = 'hello'は同じハッシュ値であるhelloを代入してしまった。


c = "hello"
d = "hello"

見かけ上はどっちもhelloだが、ハッシュ値が違う。的な感じなのかな。

というような予想をしつつ次



# 文字列

シングルクォートのなかで、シングルクォートを使いたいとき
ダブルクォートのなかで、ダブルクォートを使いたいときは、バックスラッシュでエスケープ

```ruby

puts 'He said, "Dont\'t speak"'
#=> He said, "Don't speak"


```


# 文字列の比較

```ruby

"a" < "b" #=> true

文字列を構成するバイト値が、大小比較の基準となる


```


# 数値

```ruby

数値には_を含めることができる

1_000_000_000 #=> 1000000000


```


```ruby

変数の手前に - をつけると数値の正と負を反転できる

n = 1

-n #=> -1
```


```ruby

1/2 #=> 0.5ではなく0になる


1.0 / 2 #=> 0.5

1 / 2.0 #=> 0.5

小数点以下の値が必要な場合はどちらかの値に小数点の.0を付けます

```

```ruby

変数に整数が入ってる場合は to_fメソッドを呼ぶことで整数から少数に変更することができます。
*浮動小数点数 Float 

n = 1
n.to_f #=> 　1.0
n.tof /2 #=> 0.5

```

```ruby

**はべき乗を求める演算子

2**3 #=> 8

```
【Rails】定数の定義方法【上書きトラブル注意】freeze

皆さんは、定数ってよく使いますか？

「Rails」も別の言語と同様で、
どこか専用の場所に定義して使うんだろうなー...ってフワッとした認識だったのですが、調べてみると色々注意点が出てきました。

Railsの定数っていうのは「定数＝定まった変数」と言いながら、
実はプログラム内で値を書き換えることが可能なのですね。

チーム開発においては、その辺特にバグの原因にもなり得る箇所かなーなんて思うので、
ぜひ皆さんの今後の開発トラブル回避に繋がるなら幸いだなーと思います！

注意点の前に、
まず、Railsの定数の定義方法について３つ共有します！

# 定数を定義する方法①【カンタン】

一番カンタンな方法がこちら。
「config/initializers/constants.rb」ファイルに定数を定義する方法です。これが、アプリケーションのどこからでも定数を呼び出せる一番カンタンな方法でした。

```config/initializers/constants.rb
module Hoge
  FUGA = ["これは定数です"]
end

p Hoge::FUGA
# => "これは定数です"
```
基本的に大文字で記述します。

今回、目的ごとに定数を分けたかったので、module内に記述しておりますが、
直接定義しても良いと思います↓
ただ、私個人的には後から紹介する「freeze」というメソッドを便利に使うためにも
「module」単位で定義した方が良いかな、とは思いました。

```config/initializers/constants.rb
FUGA = ["これは定数です"]

p FUGA
# => "これは定数です"
```

# 定数を定義する方法②：環境設定に追記

続いては、アプリケーションの初期設定用のファイルに追記する方法です。


```config/application.rb
module Hoge
  class Application < Rails::Application
    config.time_zone = 'Tokyo'
    config.i18n.default_locale = :ja

    # 追記する
    config.x.fuga = ["これは定数です"]
    config.x.y.fuga = ["これは2つ目の定数です"]
  end
end

p Rails.configuration.x.fuga
# => "これは定数です"

p Rails.configuration.x.y.fuga
# => "これは2つ目の定数です"
```

ただ、このファイルは滅多に変わらないような初期設定などを記述するような箇所なので、後から変更があり得るような定数を記述するのは、あまり良い方法ではないかなと私は個人的に思いました。

# 定数を定義する方法③：ymlに追記

ymlファイルに定義する方法は、若干ハードルが高いのですが、なぜおすすめなのかというと、環境ごとの定数を定義できるからです。つまりは「本番環境」「dev環境」「テスト環境」など、環境に応じて複数の定数が定義できるのです。なんとも便利ですよね。



```app/config/application.yml
  defaults: &defaults
    hoge: 200
    fuga: "これは定数です"

  development:
    <<: *defaults
    hoge: 100
  test:
    <<: *defaults
  production:
    <<: *defaults
```

# 定数定義の注意点

続いて、定数定義の注意点についてまとめてみました。実は、上記で紹介したような記述方法だと定数がプログラム内で書き換えられてしまう場合があるのです。

```config/environments/development.rb
module Hoge
  FUGA = ["これは定数です"]
end

p Hoge::FUGA
# => "これは定数です"

# 書き換えてみる
Hoge::FUGA = ["書き換えたよ!!!!"]
warning: already initialized constant Hoge::FUGA 

p Hoge::FUGA
# => "書き換えたよ!!!!"
```
上記のようにプログラム内で、定数を書き換えられてしまう場合があるのです。

特にチーム開発となると、全員が全ての定数を把握するのが難しいですし、
どうしても自分の手元ではうまく動作したのに、結合すると別の人が定義した定数を書き換えてしまっていた...なんてトラブルも起き得ますよね。

注意しても起きてしまう場合があるのは仕方のないことかもしれません。
でも、できることなら、事前に自分が定義した変数を他の人が書き換えられないよう、
ロックできたらベストだとは思いませんか？

# 解決）freezeで定数を凍結！

解決方法はこちら、[【Rails】定数の定義方法【上書きトラブル注意】freeze](https://takeda-no-nao.net/programming/ruby/how-to-define-constants/)にもあるのですが、書き換えたくない定数に「freeze」を使うことで解決できます！

```config/initializers/constants.rb
FUGA = ["これは定数です"].freeze
```

なお、molude単位でロックをかける＝つまり書き換えを阻止することも
可能です！



# 参考

[【Rails】定数の定義方法【上書きトラブル注意】freeze](https://takeda-no-nao.net/programming/ruby/how-to-define-constants/)
三級知的財産管理技能士（管理業務）を取得したので振り返る

#はじめに
現在知的財産を扱う方々とお仕事をしているので、
せっかくなら…と思い知的財産のことを勉強するついでに資格を取ってみました。

エンジニアの方々がIT系以外の資格を勉強する際の参考の一助となれば幸いです。

#参考文献

本記事で使用している画像などは、すべてこちらから引用しています。

http://www.kentei-info-ip-edu.org/about.html


#そもそも知的財産管理技能士ってなに？
私も正直なところ、最近までこの資格のことを知りませんでした。

超一言でお話しすると

* 知的財産(本に書かれた内容や、新しい技術的なアイディアなど…)を保有する企業などの『新たな競争力』をマネジメントする資格 

です。

その「知的財産をマネジメントする力」を公的に証明する国家資格が『知的財産管理技能士』という資格になります。

##もうすこしくわしく

合格者には「知的財産管理技能士」という国家資格が与えられます。
今回私が取得したのが3級で、「三級知的財産管理技能士（管理業務）」となります。

＜各級の内訳と付与される資格＞
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1846796/f8c48292-5541-0ab3-e00e-d00b49824d77.png)


3級には受験資格はありませんが、上位の級を受験するためには、

* 下位の級の合格
* 実務経験

等のいずれかが必要となります。
詳しい受験資格については、参考文献として公式サイトのリンクを貼付していますので、そちらをご確認ください。

受験費用については以下のようになっています。資格取得となるためには学科、実技のいずれにも合格しなければなりません。

##受験費用

受験費用についても、公式サイトに表がありましたので、こちらに掲載しておきます。
＜受験費用＞
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1846796/aaf7e636-c4ad-23e4-c3d6-564f09dfdf96.png)


#勉強方法

受験期間としては約二か月、平日は1日1時間、休日に時間取れた場合はその分纏めて
みたいな感じで勉強を進めました。

##使用テキスト

https://www.amazon.co.jp/1%E5%86%8A%E5%90%88%E6%A0%BC-%E7%9F%A5%E7%9A%84%E8%B2%A1%E7%94%A3%E7%AE%A1%E7%90%86%E6%8A%80%E8%83%BD%E6%A4%9C%E5%AE%9A%C2%AE2%E7%B4%9A%E3%83%BB3%E7%B4%9A-%E3%82%A2%E3%82%A4%E3%83%94%E3%83%BC%E3%82%B7%E3%83%BC%E6%96%B0%E6%A9%8B%E3%82%BC%E3%83%9F/dp/4820727915/ref=sr_1_28?adgrpid=54017031740&gclid=CjwKCAiAzrWOBhBjEiwAq85QZ-4s5YFVpx0ct5A6A21gItac8i99FG2uOYtz47Tj3uXj83dXZfh3uRoChdQQAvD_BwE&hvadid=553832918301&hvdev=c&hvlocphy=1009304&hvnetw=g&hvqmt=b&hvrand=5589689431784736271&hvtargid=kwd-304443695269&hydadcr=4074_13159886&jp-ad-ap=0&keywords=%E7%9F%A5%E7%9A%84%E8%B2%A1%E7%94%A3%E7%AE%A1%E7%90%86%E6%8A%80%E8%83%BD%E6%A4%9C%E5%AE%9A2%E7%B4%9A&qid=1640859602&sr=8-28

https://www.amazon.co.jp/%E7%9F%A5%E7%9A%84%E8%B2%A1%E7%94%A3%E7%AE%A1%E7%90%86%E6%8A%80%E8%83%BD%E6%A4%9C%E5%AE%9A3%E7%B4%9A%E5%8E%B3%E9%81%B8%E9%81%8E%E5%8E%BB%E5%95%8F%E9%A1%8C%E9%9B%86-2022%E5%B9%B4%E5%BA%A6%E7%89%88-%E3%82%A2%E3%83%83%E3%83%97%E3%83%AD%E3%83%BC%E3%83%89%E7%9F%A5%E8%B2%A1%E6%95%99%E8%82%B2%E7%B7%8F%E5%90%88%E7%A0%94%E7%A9%B6%E6%89%80/dp/4909189351/ref=pd_vtp_3/356-4956660-5612742?pd_rd_w=t6Yqf&pf_rd_p=ae64b7f5-458b-4e9a-9b07-1feecb909091&pf_rd_r=5HW5XWV8273Y1AP5SSRD&pd_rd_r=c74d61dd-ed55-4e50-bc3f-80c3dc3389b3&pd_rd_wg=hTAiy&pd_rd_i=4909189351&psc=1

一問一答を電車の中で読みながら知識を抑えて、
過去問1冊を解いたというシンプルな方法です。

#試験の振り返り

【GOOD】

* 自分の学習スタイルが確立できた

恥ずかしい話ですが、学生の頃よりあまり勉強をしたことがなく、自分に合った勉強方法が確立できていませんでした。
　そのため、毎回試験勉強やら資格試験やらでひぃひぃ言いながら勉強していたのですが、今回の資格試験ではやり方から（といってもやったことはシンプルですが…）考え、自分が一番集中して勉強できる時間帯、インプットできる教材などをいろいろと試行錯誤しながら
考えて実践し、結果として合格することができたのは自分の中でも大きな出来事でした。

ちなみに私に合った勉強方法は、

* 一問一答をインプットできるまで周回（テキストは読んでいる途中で飽きてしまうことが分かったため、今回は購入しませんでした）
* 過去問でひたすら問題演習し、間違えたところは解説が暗記できるレベルにまで読み込み、異なる選択肢はなぜそれが異なっているのか理解できるまでやりこむ

が自分の中での勉強方法としてとてもマッチしていました。
今後もしテキストが必要になってきた場合は最初から読破しようとするのではなく、
解説を読んでもわからなかった場合の辞書として活用しようと思います。

【BAD】

* 過去問のやりこみが甘く、得点は合格ぎりぎりだった

これは言い訳になってしまいますが、正直学習の時間はあまり多く取れませんでした。そのため、過去問のやりこみを完璧にすることができず、9割程度での試験本番となりました。
次回はよりスピーディーにやりこむ方法を考えて学習の効率を上げようと思います。

#おわりに

今回は3級に受験して無事に合格できました。
知的財産について学ぶのはすごくおもしろかったので、次の2級にもチャレンジしようと思います。合格したらまた記事を書こうと思います。
AWS Certifed DevOps Engineer Professional 合格記

# 背景

[前回の記事](https://qiita.com/silverbox/items/a994b09dca01bc32010c)でも書きましたが、AWS DevDayの視聴特典でゲットしたDevOps Engineer Professional受験半額バウチャーを無駄にするのが忍びなかったです。受験期限が2021年12月31日と準備期間が２ヶ月弱しかない状況でしたが、頑張って勉強して受ける事にしました。

# 受験前スペック

CLF、SAA、DVA、SOA、SAP、MLS、DASは取得済み。
業務でAWSを使っているけど、すべてのサービスを使った事がある訳ではないです。今回は業務で使っている部分と出題範囲が被る所があってその点助かりました。

# 使用した教材

## AWS公式

### [公式模擬試験](https://explore.skillbuilder.aws/learn/course/external/view/elearning/9159/aws-certification-official-practice-question-sets-japanese)

昔は2000円（だったと思う）払って受験していましたが（別の試験に合格していれば（受けていれば？）無料クーポンあったのでそんなに気になってはいませんでしたが）、最近になって無料公開されました。20問です。公式ですので一番本番に近い問題だと思います。
BenchPrepというオンライン学習システムでクーポンを使って受ける形です。昔は点数だけ出て正誤は不明でしたが、各問題で正誤が解ると同時に、何故その選択肢が間違っているかor正解かをちゃんと解説してくれます。ぜひ逃さずに受験しましょう。
解説にあるリンクも必見です。もちろんこの問題がそのまま本題に出る事は多分無いと思いますが、リンク先にある関連情報とその問題の内容を組み合わせた問題が出るという可能性は高かった気がします。

### [公式サンプル試験](https://aws.amazon.com/jp/certification/certified-devops-engineer-professional/)

以前はこれだけが無料の公式問題でしたが、それでも10問あり、本番の問題の雰囲気や傾向をつかむ貴重な資料です。押さえておいて損は無いです。

## Udemy

### [AWS Certified DevOps Engineer Professional 2022 - Hands On!](https://www.udemy.com/course/aws-certified-devops-engineer-professional-hands-on/)

2021年12月現在ですが、既に2022年版になってますｗ。始めた時はまだ2021年だったと思います。
英語の教材ですが、聞き取りやすい英語ですし、字幕もあります。スライドやAWSコンソールの画面を見ながら進めるので大体何言っているか雰囲気で解ると思います。
目的はHandsOnなので、自分のAWSアカウントで色々やって体で覚える（？）事は結構出来ると思います。
もちろん、英語の勉強を兼ねて聞き取りしてみるのも良いかと思います。

### [AWS Certified DevOps Engineer Professional Practice Exams](https://www.udemy.com/course/aws-certified-devops-engineer-professional-practice-exams-amazon/)

問題集です。75問セットが２つあります。こちらも英語です。DOPは問題文が長いのでまともに英語読んでいたら時間切れ必須です。自分はスクリーンショット取って一時停止するというズルをして時間をかけて読んで解いていました。
本番でも母国語版が用意されてない人は30分の延長出来るっぽいので、それぐらいは許されると思いますｗ
ここでやった問題がそのままでたケースもありましたがそんなに数は多きなかったと思います。ここで出た問題の技術要素を組み合わせた問題が出た、という感じが妥当と思います。問題集の丸暗記では難しいと思います。
この講座も製作元のTUTORIAL Dojoの講座の期間限定クーポンももらえます。Udemyページに届いているメッセージは見逃さず確認しましょう（自分はやる余裕が無かったです）。

### [AWS認定 DevOpsエンジニア – プロフェッショナル (DOP-C01) 模擬試験 (2021年試験対応版)](https://www.udemy.com/course/aws-devops-dop-c01-2020/)

日本語の問題集です。前述英語問題集をやっている途中にこの日本語教材が存在する情報を得ました。はい、自分はやっていないです。75問セットが４つある様です。落ちたらこちらを購入して追加勉強する予定でしたが、無事受かったので未購入です。

# まとめ

DOPも特別な事はしていなく、王道の講座と問題集を繰り返し行うという形です。ここで重要なのは問題集の丸暗記では本番の試験では難しくなるという事です。ちゃんと技術要素を把握しておかないと本番の問題に応えるのは難しいと思います。
３時間の長丁場です。最初の1/5ぐらい解いた時点で結構時間が過ぎている事に気づき、それ以降は悩んだ問題はチェック付けて一旦適当に選択して先に進む形にしました。答え終えた時は40分ぐらい残っていたのでそれから見直す形でした。見直しで何問か修正しました。全部は見直せず時間切れでしたが、あと少しで最後まで見直しできるぐらいでした。時間配分作戦は練っておきましょう。ちなみに845点でした。準備不足だったとは思いますが結構良い点が取れたと思います。

# 今後

2021年は会社が報奨金を出してくれたのでAWS試験の勉強ばっかりでした。勉強ばっかりだと頭でっかちになるし、実際に手を動かして使って見て自分のものになると思うので、2020年は実地の方に力を入れたいです。
MAMP環境のPHPにGMPをインストールする方法

下記の機能を実装するにあたりGMPをPHPにインストールする必要がありました。
MAMP環境を使っていたため少しインストールが難しかったですがなんとかできました。

https://blog.capilano-fw.com/?p=5404#env_error

##参考にした記事
https://gist.github.com/aurelioluiz/63813b6af780725a67ba4fb535b3bed5


##手順

###まずは下記のコマンドを入力(Homebrewが必要)

```
brew install autoconf gmp
```

Homebrewは下記を参照
https://qiita.com/omega999/items/6f65217b81ad3fffe7e6

###PHPの公式サイトから自分がMAMPで使っているPHPのバージョンをダウンロード
公式サイト
https://www.php.net/downloads.php

自分は8.0.0を使っていたので、下記をダウンロードしました
https://www.php.net/distributions/php-8.0.14.tar.gz.asc

ダウンロードをすると　php-8.0.14/ext　内にgmpファイルがあるのでそれをMAMPの
/Applications/MAMP/bin/php/php8.0.0/include/php/ext 内にコピーしてあげます。

下記のようなディレクトリ構成になります
/Applications/MAMP/bin/php/php8.0.0/include/php/ext/gmp

###MAMP内の下記のgmp内に移動

```
$ cd /Applications/MAMP/bin/php/php8.0.0/ext/gmp
```

###gmp内で下記コマンドを実行

```
$ /Applications/MAMP/bin/php/php8.0.00/bin/phpize
$ ./configure --with-php-config=/Applications/MAMP/bin/php/php8.0.0/bin/php-config
$ make
$ make install
```

###php.iniに追記
下記のディレクトリにあるphp.iniを開いて
/Applications/MAMP/bin/php/php8.0.0/conf/php.ini

下記の文言を好きな場所に追記

```
extension=gmp.so
```

以上でインストールができました。

##感想
これで一日とかしました。
【RockyLinux】CIFSを使ってWindows10とファイル共有したーい！！

# Windows10上にあるファイルをLinuxで操作したーい！！
Linuxのファイルサーバーへアクセスしたいとかそういう人は今回対象外です。

- Windowsマシンにプロジェクトファイルとか保存しててLinuxマシンに持って来たい。
- サーバーがWindowsでワークステーションがLinuxで...

本記事ではそんなニッチな？方を対象にしてます。

# 実行環境
- Linux (私はRockyLinux8.5をインスコした実機)
- Windows10 (私はOSビルド19043(21H1)の実機)

CentOSとかAlmaLinuxとかRHEL系なら今回の手順でいけると思います。
Ubuntu等Debian系は`dnf`コマンドを`apt`へ置き換えればいけるかも。

# Windows側を設定しよう
Linuxへ共有したいフォルダを作成または選択する。
今回私は、

- WindowsのUserディレクトリを丸々共有したい。
- しかもLinuxからフルアクセスできる。

そんな感じの設定でいきます。

1. 共有したいフォルダの`共有`設定をする。
![2021-12-28 15_31_38-NaoyaOguraのプロパティ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1396975/f1652953-e4a1-b2c5-6f9b-7117ce46b333.png)

2. `セキュリティ`タブを開いてユーザーを選択し、`アクセス許可`を設定する。
![2021-12-28 16_53_21-NaoyaOgura のアクセス許可.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1396975/a9f49fa1-2164-5d1d-df06-689f78c2c04e.png)

これでWindows側の設定はOK

# Linux側を設定しよう

1. `cifs-utils`をインストールする。

    ```Bash
    $ sudo dnf install cifs-utils -y
    ```

2. マウントポイントを作成する。
   (設定が完了するとマウントポイント内にWindows共有フォルダの中身が表示される。)

    ```Bash
    # 今回はユーザーディレクトリ直下に作成
    # Mountディレクトリ作成
    $ mkdir Mount
    # Mountディレクトリ内にWindowsディレクトリ作成
    $ mkdir Mount/Windows
    ```

3. Windowsの共有フォルダをマウントする。
   **ここの設定が非常に重要なポイント。**
   先程Windows側の`セキュリティ`でアクセス許可設定を実施したがLinux側でもPermissionの設定を行う必要がある。
   下記コマンド内の`file_mode=0777`及び`dir_mode=0777`オプション設定をしない場合マウント時に`読み込み専用`権限になる。
   今回はLinux側からフルアクセスを設定するので以下の通り。

    ```Bash
    # Windows共有フォルダをマウント
    $ sudo mount.cifs -o username=${Windowsのユーザー名},password=${Windowsのユーザーパスワード},file_mode=0777,dir_mode=0777 //${WindwsプライベートIP}/Users/${Windws共有フォルダ(ユーザーディレクトリ)} /home/${Linuxユーザーディレクトリ}/Mount/Windows
    ```

4. Linux起動時に自動でマウントする設定を行う。

    ```Bash
    # 起動時マウント設定
    # /etc/fstab へ設定を書き込む
    $ sudo sh -c "echo '//${WindwsプライベートIP}/Users/${Windws共有フォルダ(ユーザーディレクトリ)} /home/${Linuxユーザーディレクトリ}/Mount/Windows cifs username=${Windowsのユーザー名},password=${Windowsのユーザーパスワード},file_mode=0777,dir_mode=0777 0 0' >> /etc/fstab"
    
    # /etc/fstab へ設定が正常に書き込まれているか確認する
    $ cat /etc/fstab
    ```

5. 再起動後、正常にWindows共有フォルダがマウントされている事を確認する。


以上で設定完了です。
Windows側とLinux側で権限の設定がそれぞれ必要というのがポイントです。
それでは良いLinuxライフを！！
three.jsのGLTFLoaderでgltfファイルを読み込み、生webglで描画したい

# 概要
gltfの読み込みだけthree.jsでして、他は生webgl使いたい人向けです。

今回は、頂点の座標の配列とインデックス（どの頂点で三角形を描画するかを示したもの）の取得まで説明します。
そこからの描画はwebglの以下の入門サイト等で説明があるので、本記事では割愛します。
https://wgld.org/d/webgl/w018.html

# コード
とりあえずコードはこんな感じです。
GLTFLoaderのload関数の第一引数は"./box.gltf"のようなgltfファイルへのパスです。
第二引数はコールバックでgltfファイルの読み込みが終わった時に呼ばれる関数が指定できます。
引数のdataには第一引数で指定したgltfファイルがjsのobject型に変換されたものが入っており、頂点の座標の配列等も入っています。

```js
        const loader = new THREE.GLTFLoader();
        loader.load(path, (data) => { 
                const returnData = {
                    //頂点の座標の配列
                    positionData: data.scene.children[0].geometry.attributes.position.array,
                    //頂点の個数（drawElementsならelementCount使うので不要かも？）
                    vertexCount: data.scene.children[0].geometry.attributes.position.count,
                    //indexの配列
                    elementData: data.scene.children[0].geometry.index.array,
                    //indexの個数
                    elmentCount: data.scene.children[0].geometry.index.count,
                }; 
            }
```
# promise使って同期処理に
GLTFLoaderのload関数の第二引数で指定したコールバックは、gltfファイルが読み込まれてから非同期的に呼ばれるぽいので少し使いずらいです。
のでjsのpromiseという機能を使い、gltfファイルが読み終わりコールバックが呼ばれるまで他の処理を止めておくコードも書いておきます。

```js
async function getGeometory(path) {
    return new Promise((resolve => {
        const loader = new THREE.GLTFLoader();
        loader.load(path, (data) => {
                const returnData = {
                    //頂点の配列
                    positionData: data.scene.children[0].geometry.attributes.position.array,
                    //頂点の個数（drawElementsならelementCount使うので不要かも？）
                    vertexCount: data.scene.children[0].geometry.attributes.position.count,
                    //indexの配列
                    elementData: data.scene.children[0].geometry.index.array,
                    //indexの個数
                    elmentCount: data.scene.children[0].geometry.index.count,
                    // uvData: data.scene.children[0].geometry.attributes.uv.array,
                };
                resolve(returnData);
            }
        );
    }));
}
```
このgetGeometory関数を定義したら、以下の様に呼び出せば、resolveの引数のreturnData が左辺値geometorydataに入ります。
awitを付けることでgetGeometory関数の処理が終わるまで、次の行にはいかない普通の同期処理になります。

```js
 const geometorydata = await getGeometory("./../../model/sphere.gltf");
```
あとは、この`geometorydata `から頂点座標のバッファとインデックスバッファを作りdrawElements関数を呼べばよいですね。
contextmanagerを使ったSFTPのconnection管理 (Paramiko)

# はじめに

[Zenn](https://zenn.dev/nslender/articles/586fc6979cbfdc)とダブルポストです。

[contextmanagerの実践例](https://qiita.com/n_slender/items/c08a0611449cfd4e802c) の続きです。今回はSFTPが題材です。SSH/SFTPのクライアントライブラリとしてよく使われるParamikoを使います。

# Paramikoでの実践例

パスフレーズ付きの秘密鍵を使った、公開鍵認証での例です。

```python

from paramiko import Transport
from paramiko import RSAKey
from paramiko import SFTPClient

class SFTPService:

   def __init__(self, host, port, user, private_key, pass_phrase):
       self.host = host
       self.port = port
       self.private_key = private_key
       self.pass_phrase = pass_phrase       

   @contextlib.contextmanager
   def connect(self):
       try:
         transport = Transport((self.host, self.port))
         pkey = RSAKey.from_private_key_file(self.private_key, password=self.pass_phrase)
         transport.connect(self.user, pkey=pkey)
         self.client = SFTPClient.from_transport(transport)  
         yield self
       finally:
         self.client.close()
         
         

   def put(local_file: Path, remote_file: Path):
       self.client.put(local_file.as_posix(), remote_file.as_posix()) 
         

```

複数回のsftpコマンドを実行している間は、ひとつのコネクションでやることを想定して、この形に落ち着きました。
このクラスのインスタンス生成を、with構文を使わないとまともにできないような形も検討したのですが、テストコードが書きづらくなるので、コネクションをつくるメソッド + contextmanager という形にしました。

SFTPClient#closeは、インスタンス生成時に渡されたTransportのcloseを呼び出しています。
なので、clientのcloseだけ呼び出せば十分です。

```python
    def __init__(self, sock):
        """
        Create an SFTP client from an existing `.Channel`.  The channel
        should already have requested the ``"sftp"`` subsystem.
        An alternate way to create an SFTP client context is by using
        `from_transport`.
        :param .Channel sock: an open `.Channel` using the ``"sftp"`` subsystem
        :raises:
            `.SSHException` -- if there's an exception while negotiating sftp
        """
        BaseSFTP.__init__(self)
        self.sock = sock
        ...


    def close(self):
        """
        Close the SFTP session and its underlying channel.
        .. versionadded:: 1.4
        """
        self._log(INFO, "sftp session closed.")
        self.sock.close()
```

https://github.com/paramiko/paramiko/blob/main/paramiko/sftp_client.py#L100

https://github.com/paramiko/paramiko/blob/main/paramiko/sftp_client.py#L188


使い方としては、このようになります。　

```python

sftp_service = SFTPService(...)
src_dir = Path('tmp', 'src')
with sftp_service.connect() as sftp:
     for file in src_dir.iterdir():
         remote_file = Path('dst').joinpath(file.name)
         sftp.put(file, remote_file) 

```

これで、コネクション周りのコードを、ビジネスロジックを書くところから隔離しやすくなります。


# 参考

https://docs.paramiko.org/en/stable/api/keys.html#paramiko.pkey.PKey.from_private_key

https://docs.paramiko.org/en/stable/api/transport.html

https://docs.paramiko.org/en/stable/api/sftp.html#paramiko.sftp_client.SFTPClient.from_transport

https://stackoverflow.com/questions/25399635/how-to-connect-to-sftp-through-paramiko-with-ssh-key-pageant

https://gist.github.com/batok/2352501
【Mac】MySQLが起動しない （ERROR! The server quit without updating PID file）

## 症状

開発マシンの調子が悪いので再起動したが、それからMacのMySQLが起動しない。


```shell
$ sudo mysql.server start
Starting MySQL
. ERROR! The server quit without updating PID file (/usr/local/var/mysql/my-host.pid).

$ sudo mysql.server restart                                                                                                                                                              127 ↵
 ERROR! MySQL server PID file could not be found!
Starting MySQL
.. ERROR! The server quit without updating PID file (/usr/local/var/mysql/my-host.pid).
```

## 試行錯誤

とりあえずMySQLのプロセスが動いているか見て、動いていればプロセスを止める。

```shell
ps aux | grep mysql
kill -9 mysqlのPID / mysqld_safeのPID
```

MySQLのエラーログを見る。

```shell
$ sudo less /usr/local/var/mysql/my-host.err

中略

[ERROR] [MY-010274] [Server] Could not open unix socket lock file /tmp/mysql.sock.lock.
[ERROR] [MY-010268] [Server] Unable to setup unix socket lock file.
[ERROR] [MY-010119] [Server] Aborting
```

mysql.sock.lock の読み書きで問題があるらしい。
移動（削除）してあげよう。

```shell
sudo mv /tmp/mysql.sock.lock /tmp/mysql.sock.lock.old
```

再挑戦。

```shell
$ sudo mysql.server start
Starting MySQL
. SUCCESS!
```

起動成功！

結構悩んだのに、そんなんでいいんかい！🧐
Debian10にOpenJDK 17をインストール（Microsoft Build）

# はじめに

Microsoft Build of OpenJDKを利用してDebian10にOpenJDK17をインストール
親記事：[OpenJDKの各種インストール方法とEOLまとめ](https://qiita.com/witchcraze/items/b179202216aab50b7090)
参考：[Install the Microsoft Build of OpenJDK \| Microsoft Docs](https://docs.microsoft.com/en-us/java/openjdk/install)

## サポート

本手法で導入した場合、[Support roadmap for the Microsoft Build of OpenJDK \| Microsoft Docs](https://docs.microsoft.com/en-us/java/openjdk/support#release-and-servicing-roadmap)より、2027-09がEOLになると思われる。
それ以降に報告された脆弱性や不具合への対応は実施されない可能性がある。

## LOG

### インストール

```shell-session
# cat /etc/os-release
PRETTY_NAME="Debian GNU/Linux 10 (buster)"
NAME="Debian GNU/Linux"
VERSION_ID="10"
VERSION="10 (buster)"
VERSION_CODENAME=buster
ID=debian
HOME_URL="https://www.debian.org/"
SUPPORT_URL="https://www.debian.org/support"
BUG_REPORT_URL="https://bugs.debian.org/"

# apt-get update; apt-get -y install wget
# wget https://packages.microsoft.com/config/debian/10/packages-microsoft-prod.deb -O packages-microsoft-prod.deb
# dpkg -i packages-microsoft-prod.deb

# apt-get update
# apt-get install -y msopenjdk-17
... 略
```

### 各種確認

```shell-session
# which java
/usr/bin/java

# java -version
openjdk version "17.0.1" 2021-10-19 LTS
OpenJDK Runtime Environment Microsoft-28056 (build 17.0.1+12-LTS)
OpenJDK 64-Bit Server VM Microsoft-28056 (build 17.0.1+12-LTS, mixed mode, sharing)
```
Power Query で正規表現を使って取得したい Match/Matches

# 背景

URL パラメーターの分解取得しようとしたら・・
前回の記事では Replace しか使わなかったので、その Match 版を記録しておく

これ使って Forms の URL から FormsId 取得して、FormsAPI を利用したカスタムコネクターが作ろう！

Replace 版は以下

https://qiita.com/baku2san/items/8041d0259fe0db680e7e

# Matches/Match の実装例

```powershell:Matches/Matchの実装例
(expression as text, optional original as text, optional indexBeginWith1 as number ) as text =>
let
   originalText = Text.Combine({original, ""}),
   expressionText = Text.Replace(expression, "\", "\\"),
   replaceResult =  Web.Page("<script>var originalText="&"'"&originalText&"'"&";var expression=new RegExp('"&expressionText&"','g');var result=originalText.match(expression);document.write(result);</script>")
      [Data]{0}[Children]{0}[Children],

   result = if (indexBeginWith1 = null) then 
      {
         replaceResult{1}[Text]{0} as text 
      } else {
         let
            matches = Text.Split(replaceResult{1}[Text]{0}, ","),
            safeIndex = if (indexBeginWith1 <= 0 or indexBeginWith1 > List.Count(matches)) then 0 else indexBeginWith1 - 1 
         in 
            matches{safeIndex} as text
      }
in 
  result{0}
```

見た目

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/120072/3d0dbd41-8aa8-a2f7-f096-4a5618a8dd97.png)

# パラメータ説明

- expression
    - 正規表現のパターン
- original
    - 正規表現適用対象のテキスト
- indexBeginWith1
    - 取得対象の位置指定
        - 省略(null): 一致したもの全部
        - 1 ～ 取得結果内の長さ: 見つかったものの中の指定位置取得
        - 0 以下 or 長さ超えた値: index 1 として取得

# 変換テスト例

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/120072/73ec018a-7efd-caeb-40d3-f96a7935cc51.png)


```powershell:上記のコード
let
    ソース = Table.FromRows(Json.Document(Binary.Decompress(Binary.FromText("i45WyigpKSi20tcvzMwsSdRLzs/VTylKTCsp1rc0MzG0MDY2tzC1SEs2MTQ2STMwU9JRiolJ0VaK1SFHY3m1iY5ZLZmaU6oNTYF6YwE=", BinaryEncoding.Base64), Compression.Deflate)), let _t = ((type nullable text) meta [Serialized.Text = true]) in type table [Original = _t, Expression = _t]),
    変更された型 = Table.TransformColumnTypes(ソース,{{"Original", type text}, {"Expression", type text}}),
    呼び出されたカスタム関数2 = Table.AddColumn(変更された型, "Index 0", each Matches([Expression], [Original], 0)),
    呼び出されたカスタム関数 = Table.AddColumn(呼び出されたカスタム関数2, "Index 1", each Matches([Expression], [Original], 1)),
    呼び出されたカスタム関数1 = Table.AddColumn(呼び出されたカスタム関数, "Index 2", each Matches([Expression], [Original], 2)),
    呼び出されたカスタム関数3 = Table.AddColumn(呼び出されたカスタム関数1, "index 3", each Matches([Expression], [Original], 3)),
    呼び出されたカスタム関数4 = Table.AddColumn(呼び出されたカスタム関数3, "Index 4", each Matches([Expression], [Original], 4)),
    呼び出されたカスタム関数5 = Table.AddColumn(呼び出されたカスタム関数4, "index null", each Matches([Expression], [Original], null))
in
    呼び出されたカスタム関数5
```

# あとがき
さぁ準備は出来た！
あとは、カスタムコネクターへ

# keyword
how to use match regular expression in powerquery
Debian10にOpenJDK 11をインストール（Microsoft Build）

# はじめに

Microsoft Build of OpenJDKを利用してDebian10にOpenJDK11をインストール
親記事：[OpenJDKの各種インストール方法とEOLまとめ](https://qiita.com/witchcraze/items/b179202216aab50b7090)
参考：[Install the Microsoft Build of OpenJDK \| Microsoft Docs](https://docs.microsoft.com/en-us/java/openjdk/install)

## サポート

本手法で導入した場合、[Support roadmap for the Microsoft Build of OpenJDK \| Microsoft Docs](https://docs.microsoft.com/en-us/java/openjdk/support#release-and-servicing-roadmap)より、2024-09がEOLになると思われる。
それ以降に報告された脆弱性や不具合への対応は実施されない可能性がある。

## LOG

### インストール

```shell-session
# cat /etc/os-release
PRETTY_NAME="Debian GNU/Linux 10 (buster)"
NAME="Debian GNU/Linux"
VERSION_ID="10"
VERSION="10 (buster)"
VERSION_CODENAME=buster
ID=debian
HOME_URL="https://www.debian.org/"
SUPPORT_URL="https://www.debian.org/support"
BUG_REPORT_URL="https://bugs.debian.org/"

# apt-get update; apt-get -y install wget
# wget https://packages.microsoft.com/config/debian/10/packages-microsoft-prod.deb -O packages-microsoft-prod.deb
# dpkg -i packages-microsoft-prod.deb

# apt-get update
# apt-get install -y msopenjdk-11
... 略
```

### 各種確認

```shell-session
# which java
/usr/bin/java

# java -version
openjdk version "11.0.13" 2021-10-19 LTS
OpenJDK Runtime Environment Microsoft-27990 (build 11.0.13+8-LTS)
OpenJDK 64-Bit Server VM Microsoft-27990 (build 11.0.13+8-LTS, mixed mode)
```
PHP URLから構成要素を返す

# 目的

- URLの文字列から構成要素を返す`parse_url()`関数の使い方をメモ的にまとめる

# 謝辞

- 本知見は筆者が記載した記事[PHP ファイルパスやURLからファイル名や拡張子等を取得する](https://qiita.com/miriwo/items/35e76cc255788b618ad0)にコメントを下さった @s9er7 様のご知見です。
- この場を借りて改めて感謝致します！コメントを下さり本当にありがとうございます！

# 情報

- 筆者は下記サービスを用いて今回の記事内容の動作確認を行った。
    - [https://paiza.io/ja](https://paiza.io/ja)

# 方法

1. PHPのURL関数の一種である`parse_url()`を用いる。
1. 下記の様に記載することでURLの構成要素の情報を配列で返してくれる。

    ```php
    parse_url(URL);
    ```

1. 例えば[https://qiita.com/miriwo](https://qiita.com/miriwo)というURLの文字列を渡すと下記の様に「scheme」「host」「path」を配列で返してくれる。

    ```php
    $path = 'https://qiita.com/miriwo';
    var_dump(parse_url($path));
    //array(3) {
    //  ["scheme"]=>
    //  string(5) "https"
    //  ["host"]=>
    //  string(9) "qiita.com"
    //  ["path"]=>
    //  string(7) "/miriwo"
    //}
    ```

1. 当該の関数の第二引数にコンポーネントを渡すことで任意の値のみ返すこともできる。コンポーネントを指定しないと存在する情報のみを配列で返す。

    ```php
    parse_url(URL, コンポーネント);
    ```

1. 例えば[https://qiita.com/miriwo](https://qiita.com/miriwo)のhost部分だけを返してほしい場合は下記の様に指定する。

    ```php
    $path = 'https://qiita.com/miriwo';
    var_dump(parse_url($path, PHP_URL_HOST));
    // string(9) "qiita.com"と出力される
    ```

1. 渡したURLの文字列にコンポーネントで指定した情報がなかった場合、nullを変えす。

    ```php
    $path = 'https://qiita.com/miriwo';
    var_dump(parse_url($path, PHP_URL_PORT));
    // NULLが返される
    ```

1. ポート情報やクエリパラメータの情報も含むURL文字列を当該の関数にわたすと返してくれる。渡すと返してくれる値は下記である。
    - scheme - 例: http
    - host
    - port
    - user
    - pass
    - path
    - query - クエスチョンマーク ? 以降
    - fragment - ハッシュマーク # 以降
【Project Euler】Problem 65: ネイピア数eに収束する分数

* 本記事は[ProjectEuler](https://projecteuler.net/about)の「100番以下の問題の説明は記載可能」という規定に基づいて回答のヒントが書かれていますので、自分である程度考えてみてから読まれることをお勧めします。

### 問題： ネイピア数eに収束する分数

原文 [Problem 65:Convergents of e](https://projecteuler.net/problem=65)

**問題の要約：　ネイピア数eの連分数展開の100番目の項まで収束した分数の分子の各桁の和を求めよ**

連分数に関しては[連分数(Wikipedia)](https://ja.wikipedia.org/wiki/%E9%80%A3%E5%88%86%E6%95%B0)に説明があるので参考にしてください。</br>

まずネイピア数eの連分数展開ですが、とても不思議なことに規則性を持っているようです。その辺を確認するためにpython sympyのcontinued_fraction_iteratorを使ってみます。すると問題の中でも提示されているように$[\dots,1,2k,1,\dots]$のパターンが確認できました。

```python
from sympy.ntheory.continued_fraction import continued_fraction_iterator
from sympy import E, Rational, fraction

cf = continued_fraction_iterator(E)
print([next(cf) for i in range(22)])
#[2, 1, 2, 1, 1, 4, 1, 1, 6, 1, 1, 8, 1, 1, 10, 1, 1, 12, 1, 1, 14, 1]
```
問題を解くだけであればsympyにcontinued_fraction_convergentsという連分数から収束する分数を求める関数があるので、これを使えば以下のようにN番目の分数を求めることが出来ます。

```python
from sympy.ntheory.continued_fraction import continued_fraction_iterator, continued_fraction_convergents 
from sympy import E, Rational, fraction

it = continued_fraction_convergents(continued_fraction_iterator(E))
conv = [fraction(next(it)) for i in range(10)]
print(conv[-1])
#(1457, 536)
```



これだけだと味気ないのでConvergentの部分を自分で書いてみます。アルゴリズムは前出のWikipediaの[連分数の性質](https://ja.wikipedia.org/wiki/%E9%80%A3%E5%88%86%E6%95%B0#%E9%80%A3%E5%88%86%E6%95%B0%E3%81%AE%E6%80%A7%E8%B3%AA)という章を参考にしました。

 **連分数の性質**
 いま、$a0$は整数、それ以外の$a_n$は正の整数であるような数列

```math
 $\ \ \ a_0,a_1,a_2,a_3,\dots$
```
 があるとき、数列$p_n,q_n$を以下のように定める

```math
\left\{
\begin{array}{ll}
p_0=1  \\
p_1=a_0 \\
p_n=a_{n-1}p_{n-1}+p_{n-2} & (n\ge2)
\end{array}
\right.\\

\left\{
\begin{array}{ll}
q_0=0  \\
q_1=1 \\
q_n=a_{n-1}q_{n-1}+q_{n-2} & (n\ge2)
\end{array}
\right.
```
このとき、連分数は

```math
[a_0;a_1,a_2,\dots,a_{n-1}]=\frac{a_{n-1}p_{n-1}+p_{n-2}}
{a_{n-1}q_{n-1}+q_{n-2}}=\frac{p_n}{q_n}
```

これをそのままPythonのコードにして、連分数のリストから収束する分数を求める関数**cf2frac**書いてみました。

```python
import itertools
def cf2frac(cf):
  if len(cf) == 1: return (cf[0],1)
  pn_2, pn_1, qn_2, qn_1, an_1 = 1, cf[0], 0, 1, cf[1]
  for n in itertools.count(2):
    pn, qn = an_1 * pn_1 + pn_2, an_1 * qn_1 + qn_2
    if n >= len(cf): break
    an_1, pn_1, pn_2, qn_1, qn_2 = cf[n], pn, pn_1, qn, qn_1
  return (pn, qn)

N = 10
a = [2]+[2 * (i // 3 ) if (i % 3) == 0 else 1 for i in range(2,1+N) ]
print(a)

(num, den) = cf2frac(a)
print(len(a),(num, den))
#[2, 1, 2, 1, 1, 4, 1, 1, 6, 1]
#10 (1457, 536)
```
化石がマイナスからKaggleを始める～レベル最弱の「タイタニック号を救え！」2 ～

#前提
数学力・統計力・技術力がほぼすべて「ゼロより低い、マイナス」の化石エンジニア・アンモナイトが生き残りを目指して~~賞金が欲しくて~~、Kaggleに挑戦する。
たぶん、Kaggleに挑戦する記事の中で、もっともレベルが低いと思う。
現状は、タイタニック号を救うべく、「先生」（個人的な教本）のNotebookを「Submit」して、0.7ちょいのスコアをもらい、
スコア1.0の方が複数存在することに驚愕、「へえぇ、スゴ！」と思ったところです。

#1：で、どうやってタイタニック号を救うの？
「Submit」したのは模範解答（「先生」のNotebookをデバッグしたもの）なので、
なにがどう評価されて、0.7ちょいのスコアになったのか、わからない。
そもそも、機械学習でどうやってタイタニック号を救うのか、脈絡がわからない。

アンモナイトがタイタニック号の救出方法を考えるに、
　1：氷山を削る←直感的に考えた
　2：船員がすごく頑張る←現実的に考えた
　3：タイタニック号的なデカい客船を急遽派遣する←方向性を変えてみた
　4：事前に予約した人の予約を全キャンセルする←方向性を（以下略）
等が浮かんだが、どれも機械学習でなんとかなる問題ではない。

4は機械学習でなんとかなるのか？　とも考えたが、
でも当時って、受付システム、仮にタイプライターあったとしても、紙ベースだよね？
データがないから、全キャンセルするなら、申込書を全部誤って暖炉に入れた、とか、
申し込み担当者の首が飛ぶようなアクシデントがない限り、無理じゃない？
←完全に機械学習の範疇ではない＆タイタニック号の航海が違う意味で炎上する

完璧に行き先を見失って迷いまくっているアンモナイトに「先生」が優しく手を差し伸べる。
そう、P51から「模範解答」の解説が始まるのだ。

「先生」＝『PythonではじめるKaggleスタートブック』
https://www.amazon.co.jp/dp/4065190061
![sensei.jpg](https://images-fe.ssl-images-amazon.com/images/I/51WQEgoiD4L._SX218_BO1,204,203,200_QL40_ML2_.jpg)

※毎回、「先生」のリンクや書影を出していますが、関係者ではありません。

#2：機械学習の流れ
P54まで読み進む。importからデータ取り込み、前もって提示された結果データに合うデータを作成、Submitする、までは理解した（たぶん）。
ここまで読んだアンモナイトの根本的な素朴な疑問：「タイタニック号を救わないの？」
そう、アンモナイトはずっとこのチュートリアルコンペ「タイタニック号を救え！」（超訳）のミッションは「タイタニック号を救う」ことだと思っていた。
あれ？　なんか違うぞ？　女性のみが生存するリスト作成経過＆提出でいいの？
え、機械学習じゃ、タイタニック号を救えないの？　氷山、削れないの？

……いまこの部分を読んで「はい？？（某ドラマの●京さん風）」と驚愕している方がいるかもしれませんが（いや、確実にいる）、ご安心ください、アンモナイト自身も驚愕しています。
機械学習＝超スゴい・不可能を可能にする技術、富岳的なやつと思っていたアンモナイト（Lv3）は、
このチュートリアルコンペの目的は「タイタニック号を救う」だと考えてました。
だから、タイトルも「タイタニック号を救え！」だったんだよ。←わかるひとには見えてた出オチ
どうやって機械学習で救うんだろう？　ケースごとのシミュレーションとかやるのか？　と
漠然と考えてました。
まさか、リストの提出が最終目的だったなんて。
そんなん、タイタニック号は沈んでしまうじゃないですか！←当たり前
あの2人の運命を変えられないじゃないですか！←フィクション
助けられると思ったから、頑張ってデバッグしたのに！←関係ない
なんという悲劇……（本当に悲劇的な頭である。もはや「お大事に」レベル）

#3：とりあえず先へ
愕然としつつも（レオナ●ドとケイ●には悪いが）先に進まないと話にならないので、
「先生」の続きを読む。
仮説を立て、可視化、検証する。うむ、この流れはアジャイルに通じるものがあるので、するっと納得した。
以降、「先生」は「タイタニック号」のデータの詳細について説明してくれるのだが、
もう全然頭に入らない。
いろいろ衝撃的過ぎて。
どんだけあの2人に感情移入、ではなく、
「過去の災害を現在の技術で回避できるのではないか」と考えていたところを、
「そういう意味じゃない」と否定されたので、ガックリきてしまったのです。
そうか、そうなのかぁ……今の技術でも、災害回避のシミュレーションはできないのか……。
※アンモナイトが読んでるP60までは、です。もしかしたら、あとでできるようになるのかも。

「技術」って誰かの、何かのヘルプになるものだ、という概念を持っていたので、
「助けられない」「別のルートの想定ができない」に「＝ヘルプにならない」と思い、
落ち込んだんです。
テンションがダダ下がってしまったため、本日の投稿はここで終わります……。
ごめん、レ●ナルドとケ●ト。助けられなかった。

長文にお付き合いいただき、ありがとうございました！
Ubuntu 20.04にOpenJDK 17をインストール（Microsoft Build）

# はじめに

Microsoft Build of OpenJDKを利用してUbuntu 20.04にOpenJDK17をインストール
親記事：[OpenJDKの各種インストール方法とEOLまとめ](https://qiita.com/witchcraze/items/b179202216aab50b7090)
参考：[Install the Microsoft Build of OpenJDK \| Microsoft Docs](https://docs.microsoft.com/en-us/java/openjdk/install)

## サポート

本手法で導入した場合、[Support roadmap for the Microsoft Build of OpenJDK \| Microsoft Docs](https://docs.microsoft.com/en-us/java/openjdk/support#release-and-servicing-roadmap)より、2027-09がEOLになると思われる。
それ以降に報告された脆弱性や不具合への対応は実施されない可能性がある。

## LOG

### インストール

```shell-session
# cat /etc/os-release
NAME="Ubuntu"
VERSION="20.04.3 LTS (Focal Fossa)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 20.04.3 LTS"
VERSION_ID="20.04"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=focal
UBUNTU_CODENAME=focal

# apt-get update; apt-get -y install wget lsb-release
# ubuntu_release=`lsb_release -rs`
# wget https://packages.microsoft.com/config/ubuntu/${ubuntu_release}/packages-microsoft-prod.deb -O packages-microsoft-prod.deb
# dpkg -i packages-microsoft-prod.deb

# apt-get install apt-transport-https
# apt-get update
# apt-get install -y msopenjdk-17
... 略
```

### 各種確認

```shell-session
# which java
/usr/bin/java

# java -version
openjdk version "17.0.1" 2021-10-19 LTS
OpenJDK Runtime Environment Microsoft-28056 (build 17.0.1+12-LTS)
OpenJDK 64-Bit Server VM Microsoft-28056 (build 17.0.1+12-LTS, mixed mode, sharing)
```