OmniAICreator/Qiita-1.07M huggingface dataset:
本日の基礎練習問題(22/3/19)

**問題**
以下の配列に任意の値が存在するかどうか、そして何番目に存在するのか、検索するコードを作成しましょう。
添字が0の要素、つまり以下の配列における「1」は「配列の0番目に存在する」と表現します。
```
array=[1,3,5,6,9,10,13,20,26,31]
```
任意の値が配列内に存在しない場合は、「値は配列内に存在しません」と表示し、
存在する場合は、配列の何番目にあるかを表示してください。
※配列の上限である32以上の値による検索は想定しないものとします。
検索はバイナリーサーチ（2分割検索）を使用して行います。

ヒント
まず.lengthメソッドを使用して配列の要素数を取得し、変数number_of_elementsとします。
binary_searchメソッド内で配列の要素数を半分にしたものを変数centerとしましょう。
while文を使用して、当てはまるまで計算を繰り返します。

出力例：
```
検索したい数字を入力してください
5
5は配列の2番目に存在します 
```
```
検索したい数字を入力してください
7
7は配列内に存在しません
```

# 私の回答
```
　# 処理内容書けずタイムアップ

array=[1,3,5,6,9,10,13,20,26,31]

puts "検索したい数字を入力してください"
target = gets.to_i
number_of_elements = array.length

```
# 模範回答
```
def binary_search(array, right, target)
  left = 0
  while left <= right
    center = (left + right) / 2
    if array[center] == target
      return center
    elsif array[center] < target
      left = center + 1
    else
      right = center - 1
    end
  end
  return -1 
end

array=[1,3,5,6,9,10,13,20,26,31]

puts "検索したい数字を入力してください"
target = gets.to_i
number_of_elements = array.length

result = binary_search(array, number_of_elements, target)

if result == -1
  puts "#{target}は配列内に存在しません"
else
  puts "#{target}は配列の#{result}番目に存在します "
end
```

# 模範解説
1〜14行目で中央の値を見て、検索したい値との大小関係を用いて、検索したい値が中央の値の右にあるか、左にあるかを判断して、片側には存在しないことを確かめながら検索を行う処理を行っています。13行目のreturn -1は何も当てはまるときがないときに、最終的な返り値になります。

たとえば、targetとして31を入力した場合を考えてみましょう。そうすると2〜12行目で以下のように処理が進みます。
```
#1回目のループ
left=0
right=10
center=5
array[center]=10

#2回目のループ
left=6
right=10
center=8
array[center]=26

#3回目のループ
left=9
right=10
center=9
array[center]=31
```
そして6行目のreturn centerによって返り値が9となり、24〜28行目の処理で31は配列の9番目に存在しますと出力されます。

# 感じたこと
ネットで調べながら解いたが時間をかけすぎて処理内容を書けなかった。
かなり難しいと感じた。正直答えを見ても変数rightとarray[center]の動きがいまいちよくわからない。
1から整理して理解できるようにしたい。
またバイナリーサーチは聞いたことがなかったので学習し使用できるようにしたい。
※実際に開発現場で使用しているかはわからないが‥‥

プログラミング未経験なので初見ではなかなか厳しい問題が多くなってきましたが
逆に吸収できることが多いので良い意味で捉え日々成長していきたい。

まだまだruby初心者ですのでアドバイスやご指摘等ございましたらぜひお願いします！！！

以上
Better ErrorsとBinding_of_callerを導入する(Rails+Docker環境)

## Better ErrorsとBinding_of_callerとは？

Railsのgemの一つでBetter_Errors gemを使うと便利で高機能なエラー画面が表示されます。

Binding_of_callerはブラウザ上でirbを使えるようになるgemです。

### Better Errorsとbinding_of_callerの導入

Gemfileに以下を記述します↓

```Gemfile
group :development do
  gem 'better_errors'　     #追加
  gem 'binding_of_caller'　 #追加
end
```

次にdocker-compose buildでコンテナをビルドします。
```
$ docker-compose build
```

**/config/environments/development.rb**に以下を追加します↓
```
if Rails.env.development?
  BetterErrors::Middleware.allow_ip! "0.0.0.0/0"
end
```

docker-compose up などでコンテナを起動すると↓

いつもならこのような表示が(画像は一例です)

![スクリーンショット 2022-03-19 14.33.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/584500/9daa1785-fac2-8245-781a-7cc04ba82100.png)


このような表示になります！

![スクリーンショット 2022-03-19 14.24.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/584500/dabeb396-f4ec-2a30-560a-b8d8fc0196b0.png)

左のペインで処理の流れがわかりやすくなります。

Application framesは自分が書いた部分が表示されます。

All framesリンクをクリックすると、gemやフレームワークのスタックトレース（バックトレース）が表示されます。

スタックトレースは下から上にメソッドが呼び出された順番が表示されます。

スタックトレースを読むことで、エラーが発生するまでにどのファイルのどの行が呼ばれてきたのかを確認できます。

右にある>>はirbの部分で、ここでコマンドすることで対話形式でデバッグの手がかりを探す事もできます。

### その他デバッグに関しての心得

開発環境であれば画面にエラーの発生箇所が表示されます。

本番環境であればログを読みます。ログにもエラーの発生箇所が表示されます。

エラー画面やログを見て、エラーが起きたファイルと行番号を確認します。

実際にエラーが起きたのは自分が書いたコードではなく、gemやフレームワークのコードであることも多いようです。
Celeryのchainで複数の引数をタスクに渡す

## Celeryのタスクchainの引数について

- celeryのタスクをchainさせると、1つ目のタスクの戻り値が次のタスクの引数に入る仕組みになっています。
- したがって、チェーンするタスク同士のシグネチャはうまく整合させる必要があります。
- ここで、複数の引数を受け入れるタスクにはどのようにして値を渡すのか疑問です。

```python
chains = chain(task_a.s(), task_b.s())


@shared_task
def task_a():
    # 処理...
    return ???


@shared_task
def task_b(?? a,b):
    # 処理...
    return None
```

- シグネチャを合わせるといわれて一番最初に思いつく方法は以下のようなものでしょうか。しかし、うまくいきません。

```python
@shared_task
def task_a():
    # 処理...
    return a, b


@shared_task
def task_b(a, b):
    # 処理...
    return None

> TypeError("task_b() missing 1 required positional argument: 'b'"):
```



## 対策１

- pythonで複数の戻り値をカンマ区切りでreturnすると自動的にタプルとして返され、結果をタプルのアンパッキングを使って受け取れます。

```python
def method():
	return "a", "b"
	
a, b = method()
```

- したがって、複数の引数ではなくタプルを受け取るようにして、メソッド内でアンパッキングさせればよいことがわかります。

```python
chains = chain(task_a.s(), task_b.s())


@shared_task
def task_a():
    # 処理...
    return 'a', 'b'


@shared_task
# 注)*argsではない
# Tuple([a, b])となってしまってunpackしづらい
def task_b(args): 
    # 処理...
    a, b = args
    logger.info("args={a} , {b}")
    return None
```

- この方法の注意点としては可変長引数『*args』で定義していないことです。
- 好みの問題かもしれませんがceleryのタスクchainでは可変長引数はListとして扱われるようなのでargs[0]に対してアンパッキングすることになり、何をしているのかわかりづらいです。



## 対策2

- 辞書でreturnして引数で受け取る方法も動作します。

```python
chains = chain(task_a.s(), task_b.s())


@shared_task
def task_a():
    # 処理...
    return {'a': 'a', 'b': 'b'}


@shared_task
def task_b(args_dict): # 注)**kwargsではない
    # 処理...
    a, b = args_dict.values()
    logger.info("args={a}, {b}")
    return None

```

- この方法の注意点は『**kwargs』のように可変長なキーワード引数としてtask_bを定義するとうまくいきません。
- メソッドのチェーンで『**kwargs』と書くと、task_bは位置引数を１つも期待していないのに、task_aからreturnされたdict型の位置引数を１つ渡したことになり例外を投げます。



- もっと良い方法や、間違いがあれば修正いたしますのでコメントお願いします。
クリックすると特定名のexeファイルを「Widnows Terminal」上で実行するだけのbatファイル

# 序文
　Windows10では、既定ターミナルが「コマンドプロンプト」になっており、「exeファイル」をクリックすると「コマンドプロンプト」で実行されてしまう。
「exeファイル」をクリックすると初動から「Widnows Terminal」で実行して欲しいが出来ない。「Widnows Terminal」を起動して、「exeファイル」のファイル名をキーボード入力すれば「Widnows Terminal」上で実行できるが、それは求めてない。

　一方、Windows11からは既定ターミナルを「Widnows Terminal」に変更できるらしいが、Myパソコンは古くて更新ができない。当面は買い替える予定もない。
　そこで、クリックすると特定名のexeファイルを「Widnows Terminal」上で実行するbatファイルを作成した。

## 環境
- ＯＳ　　　　　　　　：windows10
- バッチファイル　　　：xyz.bat
- 実行したいファイル　：ABC.exe

# バッチファイルの中身

```powershell xyz.bat
wt -p "Windows PowerShell" powershell.exe -noexit ./ABC.exe
```

## 参考1 [-noexitの説明]
[PowerShellスクリプトへのショートカットを作成する](https://atmarkit.itmedia.co.jp/fwin2k/win2ktips/1024psshrtct/psshrtct.html)
[右クリックメニューの「PowerShell で実行」を拡張する](http://www.vwnet.jp/windows/w10/2015121601/ExtendRightClickMenu.htm)

## 参考2
[ターミナル(WindowsTerminal)で効率アップ(2021年睦月)](https://qiita.com/CinTAKE/items/61def2faee7bea4fb908)
[Windows 11では標準コンソールの変更が可能になったのでWindows Terminalを設定する](https://ascii.jp/elem/000/004/079/4079062/)
PythonからImageJを利用するPyImageJライブラリを使ってみる

imageJは科学的な画像解析のためのソフトで、PythonからimageJを利用するための**PyimageJ**というパッケージが用意されている。

**PyImageJ**はImageJ2とPythonを統合するためのラッパー関数のセットを提供している。

今回はAnacondaで仮想環境を作成し、pyimagejを利用する環境構築を行い、サンプル画像を表示するところまでやってみる。

[参考URL1](https://github.com/imagej/pyimagej)
[参考URL2](https://github.com/imagej/pyimagej#pyimagej-python-wrapper-for-imagej2)

# 1. Anacondaで仮想環境を作成する

Anacondaで新しい仮想環境を作成する際は、`$ conda create -n 新しい環境名`を実行する。今回は`imagej`という名前の仮想環境を作成する。

2022年3月現在ではPythonのバージョンは3.6, 3.7, 3.8が推奨されているため、3.8を使用する。

```terminal
$ conda create -n imagej python=3.8
```

続いて作成した仮想環境に入る。

```terminal
$ conda activate imagej
```

# 2. 必要なパッケージをインストールする

**pyimagej**と**openjdk**をインストールする。

```terminal
$ conda install -c conda-forge pyimagej
```

```terminal
$ conda install -c conda-forge openjdk=8
```

# 3. 仮想環境でJupyterを使えるようにする

仮想環境をJupyter Notebook上で使えるようにするために、コマンドプロンプト上でipykernelをインストールする。

```terminal
$ pip install ipykernel
```

Jupyterのカーネルとして仮想環境を追加するためのコマンド`$ ipython kernel install --user --name=環境名`を実行する。


```terminal
$ ipython kernel install --user --name=imagej
```

# サンプル画像を表示してみる

`conda activate imagej`で仮想環境下に移動し、`$ jupyter lab`でJupyter Labを開く。

新しいノートブックを作成し、以下のコードを実行する。

PyImageJを使用する際の最初のステップは、ImageJ2ゲートウェイを作成すること。ゲートウェイを使用すると、ImageJ2 API に完全にアクセスできるようになる


```python
# ライブラリのインポート
import imagej

# ゲートウェイを作成する
ij = imagej.init()

# 画像を読み込む
image_url = 'https://imagej.net/images/FluorescentCells.jpg'
jimage = ij.io().open(image_url)

# 配列に変換
image = ij.py.from_java(jimage)

# 画像を表示する
ij.py.show(image)
```


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/471633/3b24b79c-d1d1-e736-a26f-2603ff58f1a0.png)

JupyterでImageJを使うことができました！
【WordPress】WordPress管理画面の404アクセスエラー

# 概要

エックスサーバーで管理しているサイトの WordPress 管理画面にアクセスできなくなりました。
その際に試したこと、解決した手順を備忘録として残しておきます。

# エラー内容

WordPress 管理画面へのログイン画面で、アカウント及びパスワードを入力し、「ログイン」ボタンをクリックすると、404 ページに飛ばされる

# 考えられた要因

- WordPress 管理画面の「一般設定」にある、「WordPress アドレス（URL）」欄または「サイトアドレス（URL）」欄の URL が誤っている
- セキュリティ系のプラグイン

# 試したこと

- wp-config.php ファイルの編集<br>
  1. サーバー上のファイルマネージャーの[public_html]フォルダにある、「wp-config.php」ファイルをダウンロードする
  2. 本ファイルの最終行に、`define( ‘RELOCATE’, true );`を追記
  3. 本ファイルを同じフォルダにアップロード
- .htaccess の編集<br>
  1. エックスサーバー管理画面にある「.htaccess 編集」ボタンを開く
     ![エックスサーバー管理画面](https://raw.githubusercontent.com/yamashin01/qiita_tech_report/main/image/20220319_xserver_control.png "エックスサーバー管理画面")
  2. `404-siteguard [L]`の文言が入った文章をコメントアウト<br>

上記 2 つを実施しても解消せず。

# 解決した方法

セキュリティプラグイン「SiteGuard」により強制的に 404 画面に飛ばされていたことが原因でした。解決方法を教えてくださったエックスサーバーのサポートスタッフに感謝です。

1. サーバ上の[/XXXXX.co.jp/public_html/]にある【.htaccess】ファイルの、以下の記述をコメントアウトする

```sh:.htaccess
#SITEGUARD_PLUGIN_SETTINGS_START
#==== SITEGUARD_RENAME_LOGIN_SETTINGS_START
<IfModule mod_rewrite.c>
RewriteEngine on
RewriteBase /
RewriteRule ^wp-signup\.php 404-siteguard [L]
RewriteRule ^wp-activate\.php 404-siteguard [L]
RewriteRule ^login_06737(.*)$ wp-login.php$1 [L]
</IfModule>
#==== SITEGUARD_RENAME_LOGIN_SETTINGS_END
#SITEGUARD_PLUGIN_SETTINGS_END
```

2. ファイルマネージャーを用いてサーバー上の下記フォルダ内にある　【siteguard】フォルダを、別の名称へ変更する<br>
   /XXXXX.co.jp/public_html/wp-content/plugins

# 参考サイト

- [WordPress のログイン方法とログインできない場合のトラブルシューティング【5 選】](https://web-kanji.com/posts/wordpress-how-to-login)
- [HAYAMIZ BLOG](https://hayamizbg.net/management-portal-wordpress-404/)
- [FTP ソフトの設定](https://www.xserver.ne.jp/manual/man_ftp_setting.php)
- [ファイルマネージャー](https://www.xserver.ne.jp/manual/man_tool_file.php)
1対多なリレーションがUUIDなモデルに対して向いているときのFixtureの書き方

## どんなFixtureを生成させたいか

- モデル定義部分を見ていただくのが早いかと思います。
- AとBは1対多の関係でBモデルはAクラスへの参照フィールドを保持しています。

```python
class A(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    a = models.CharField(max_length=100)

class B(models.Model):
    b = models.CharField(max_length=200)
    a = models.ForeignKey(A, on_delete=models.CASCADE)
```

- ポイントは、A.idはUUIDで定義されているところです。デフォルトのintであれば以下のように簡単にfixtureを定義できます。

```json
{
    "model": "A",
    "pk": 1,
    "fields": {
        "a": "aモデルです"
    }
},
{
    "model": "B",
    "pk": 1,
    "fields": {
        "b": "bモデルです",
        "a": 1 # pk=1のAモデルへのリレーション
    }
}
```

- ただ、同じようなことをUUIDなidで行うとうまくいきません

```json
{
    "model": "A",
    "id": "c5ecbde1-cbf4-11e5-a759-6096cb89d9a0",
    "fields": {
        "a": "aモデルです"
    }
},
{
    "model": "B",
    "pk": 1,
    "fields": {
        "b": "bモデルです",
        "a": "c5ecbde1-cbf4-11e5-a759-6096cb89d9a0"
    }
}
```

```
django.db.utils.IntegrityError: ...
DETAIL:  Key (a_id)=(c5ecbde1-cbf4-11e5-a759-6096cb89d9a0) is not present in table "a".
```



## 解決法

- pkというフィールドなんかモデルに定義していないのに！と思っていましたが、pkというフィールド名はそもそもdjangoでは予約語であり、primary_key=Trueなフィールドへのエイリアスとしてふるまうみたいです。

```json
{
    "model": "A",
    // idではなくpkにする
    "pk": "c5ecbde1-cbf4-11e5-a759-6096cb89d9a0", 　
    "fields": {
        "a": "aモデルです"
    }
},
{
    "model": "B",
    "pk": 1,
    "fields": {
        "b": "bモデルです",
        "a": "c5ecbde1-cbf4-11e5-a759-6096cb89d9a0"
    }
}
```

- 納得しました。



## 参考にした記事

https://stackoverflow.com/questions/60899558/django-onetoone-relations-using-fixtures-and-uuids

https://developpaper.com/question/the-orm-reserved-word-problem-of-django/
Ruby 3.1.1 インストール（Windows）+ AtomでRubyを実行する方法

Rubyの現時点（2022/3/19）での最新版を入れる機会があったのでメモ。
Atomの拡張機能「atom-runner」のインストール手順も記載。

# 端末とかRubyのバージョンとか

- Windows 10 64bit
- Ruby 3.1.1
- 因みに、WindowsでRails開発を行うならWSL上で構築したほうがよい（らしい）

# Rubyインストール

### ダウンロードページ

[Downloads (rubyinstaller.org)](https://rubyinstaller.org/downloads/)

64bit版をダウンロード
※ 純粋にRubyだけを実行する場合は、**WITHOUT DEVKIT** の方でよかったらしい。
![ruby0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99895/1200d6e3-89b2-0339-0300-8a624cabc6cf.png)

### rubyinstaller-devkit-3.1.1-1-x64.exe を実行

基本的にはそのまま次へでOK
![ruby4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99895/abd8a70f-35a6-072a-0d70-695740328200.png)
![ruby5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99895/31a6ef40-85ae-f2e4-532b-631492366e7d.png)
![ruby1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99895/9d078cd6-5084-9742-4637-dd84be41f1f5.png)
![ruby2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99895/fc02fcea-252e-d66f-718a-f98698a0897d.png)
![ruby3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99895/ebb453c4-d3a5-922d-1e98-c24e6c46c9a5.png)

インストール完了

※上記、ダイアログを閉じると、RubyInstaller2 が起動する。
　Cのライブラリを利用するgem（Nokogiriやsqlite3等）をインストールする場合は必須。
　私の場合は不要だったため、Ctrl+Cで閉じました。
![Untitled.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99895/02251270-35cd-e3d4-27f1-3c7321d2aca6.png)

### インストール確認

cmd などで `ruby -v` で確認する
![ruby6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99895/3f004ca7-cae2-0cd3-95c4-c9c1d10c8507.png)

# Atomの拡張機能でRubyを動かす

### atom-runner
コードを実行するためのパッケージ。
実行できる言語は **JavaScript, CoffeeScript, Ruby, Python, Go, Bash and PowerShell** 

### Atomのインストール

下記のページからダウンロードしてインストール。
特に難しい点はないため詳細は割愛。
[Atom](https://atom.io/)

### atom-runnerのインストール
File → settings を選択
![atom1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99895/6f0b7a64-5b8a-f833-daa3-c9e1ef0299da.png)

install をクリックして 「atom-runner」を入力。
Installボタンをクリックしてインストール完了。
![atom2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99895/19b467da-56c3-0602-c826-31775862adfa.png)

### 動作確認

Atomを開き、適当な名前でRBファイルを作成（今回はtest.rbで作成）
中身は `print "Hello Ruby!"` としました。
![atom3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99895/54c09ea4-a1a5-6771-520b-598f35c1909a.png)

Alt ＋ R を入力すると、記載したRubyが実行されます。
![atom4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99895/2a48ea28-78f5-0e0c-1197-538476c3276a.png)

以上です。
【Kotlin】アルゴ式 標準出力 1-5（一日は何秒か）

アルゴ式の「[標準出力 1-5](https://algo-method.com/tasks/73)」を、
Kotlin の [`kotlin.time`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.time/) パッケージを使って解いてみた。

```Kotlin
import kotlin.time.ExperimentalTime
import kotlin.time.days

@OptIn(ExperimentalTime::class)
fun main() {
    1.days.inWholeSeconds
        .also { println(it) }
}
```

アルゴ式の Kotlin はまだバージョン1.5のようだ。（正確なバージョンを確認する方法はあるのかな？）

Kotlin 1.6 だと次のように書ける。

```Kotlin:Kotlin1.6
import kotlin.time.Duration.Companion.days

fun main() {
    1.days.inWholeSeconds
        .also { println(it) }
}
```

／以上
unittestのmockでハマった３つのこと

### 外部ライブラリをpatchするとき

- nltkなど外部ライブラリのメソッドを呼び出す関数があったとする。

```python
# ./nltkclient.py
from nltk.tag import pos_tag

def normalize():
    morph = [...]
    pos_tag(morph)
    ...
```

- この関数をテストするときにnltk.tag.pos_tagメソッドについてはモックをパッチさせてテストを実行したい。

```python
# ./test_nltkclient.py
from unittest import TestCase
from unittest import mock

class NltkClient(unittest.TestCase):
    # ↓パッチした。
    @mock.patch('nltk.tag.pos_tag')             
	def test_normalize(self, pos_tag):
    	pos_tag.return_value = [('word', 'tag')]
```

- しかし、この書き方だとpos_tagのモックはただしくパッチされませんでした。
- 外部ライブラリをモックとして扱うには以下のように書く必要があった。

```python
# ./test_nltkclient.py
class NltkClient(unittest.TestCase
	× @mock.patch('nltk.pos_tag')
    @mock.patch('nltkclient.pos_tag')             
	def test_normalize(self, pos_tag):
    	pos_tag.return_value = [('word', 'tag')]
    	
```



## return_valueの値がおかしい??

- 以下のように複数のオブジェクトをパッチするとき

```python
@mock.patch('tasks.pos_tag')
@mock.patch('tasks.tokenize')
@mock.patch('tasks.normalize')
def test_normalize(self, pos_tag, tokenize, normalize):
	pos_tag.return_value = [('mocked', 'VERB'), ('text', 'NOUN')]
    tokenize.return_value = ['mocked' ,'text']
    normalize.return_value = None
```

- return_valueの値が正しくならない。

- ここで注意したいのは、デコレータは内部から引数に入っていることです。したがって、引数はデコレータを書いた順と逆にする必要があることです。

```python
@mock.patch('tasks.normalize')
@mock.patch('tasks.tokenize_text')
@mock.patch('tasks.pos_tag')
def test_normalize(self, pos_tag, tokenize_text, normalize):
	pos_tag.return_value = [('mocked', 'VERB'), ('text', 'NOUN')]
    tokenize_text.return_value = ['mocked' ,'text']
    normalize.return_value = None
```



### クラスのインスタンスをモックさせる

- 例えば、Celeryというタスクキューライブラリを使っていてAsyncResultはタスクの状態を監視できるクラスである。
- テストでは、実際にタスクを走らせずに実行されているかどうかのみをテストしたいとする。
- AsyncResultはtask.idをイニシャライザ引数として受け取りインスタンス化させる。属性としてstatusとresultを持ちそれぞれが返す値をモックさせる。

```python
from unittest import mock

from rest_framework import status
from rest_framework.test import APITestCase

from apiv1.views import AsyncResult


class resultTrackViewTest(APITestCase):
    
    # ↓ @mock.patch.object(クラス, メンバ, 返す値(メンバがメソッドならcallableである必要がある)
    @mock.patch.object(AsyncResult, '__init__', lambda self, task_id: None)
    @mock.patch.object(AsyncResult, 'status', 'SUCCESS')
    @mock.patch.object(AsyncResult, 'result', 'write_path')
    def test_get_response(self):
        response = self.client.get(f'/api/v1/tasks/{uuid.uuid4()}/')
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(response.data, 'write_path')
```

- 特に\_\_init\_\_メソッドについては、selfとtask_idを引数に受け取りNoneを返すラムダを指定することによってうまく動作しました。
- ふたつ前の章同様にAsyncResultはcelery.result.AsyncResultではなく、apiv1.views.AsyncResultなどそのライブラリを呼び出しているパスに対してモックを適用するように記述することに注意する。





### 参考にした記事

https://docs.python.org/ja/3.8/library/unittest.mock-examples.html

https://leben.mobi/blog/django_mock/python/

https://note.crohaco.net/2015/python-mock/#%E3%83%87%E3%82%B3%E3%83%AC%E3%83%BC%E3%82%BF%E3%82%92%E3%83%8D%E3%82%B9%E3%83%88%E3%81%99%E3%82%8B

https://medium.com/@george.shuklin/mocking-complicated-init-in-python-6ef9850dd202
Redshiftチートシート

## 背景・目的
忘れぽいため、RedshiftのコマンドやSQL等をまとめておく。

## 内容

### psqlで接続する。
```
psql -h {エンドポイント名} -p {ポート番号} -U {ユーザ名} -d {データベース名}
```

### psqlを終了する。
```
# \q
```

### データベース作成
```
CREATE DATABASE {DB名} WITH OWNER = {ユーザ名}
```

### [COPY](https://qiita.com/zumastee/items/a71b1f6e3266c76f596a)
```
copy users from 's3://{バケット名}/tickit/allusers_pipe.txt' 
iam_role 'arn:aws:iam::{アカウントID}:role/{ロール名}';
delimiter '|' region 'ap-northeast-1';
```

### [UNLOAD](https://qiita.com/zumastee/items/84534883f8ba8ffd0a41)
```
unload ('select * from venue')
to 's3://{バケット名}/unload/venue/' 
iam_role 'arn:aws:iam::{アカウントID}:role/{ロール名}';
```

### Viewを参照しているテーブルを確認する
```
SELECT *
(
select distinct c_p.oid as tbloid,
n_p.nspname as schemaname, c_p.relname as name,
n_c.nspname as refbyschemaname, c_c.relname as refbyname,
c_c.oid as viewoid
from pg_catalog.pg_class c_p
join pg_catalog.pg_depend d_p
on c_p.relfilenode = d_p.refobjid
join pg_catalog.pg_depend d_c
on d_p.objid = d_c.objid
join pg_catalog.pg_class c_c
on d_c.refobjid = c_c.relfilenode
left outer join pg_namespace n_p
on c_p.relnamespace = n_p.oid
left outer join pg_namespace n_c
on c_c.relnamespace = n_c.oid
where d_c.deptype = 'i'::"char"
and c_c.relkind = 'v'::"char";
) WHERE refbyschemaname ={テーブル名}

```
- [参考:Redshift > DROP TABLE](https://docs.aws.amazon.com/ja_jp/redshift/latest/dg/r_DROP_TABLE.html)

## その他
- 随時更新する予定です。
Google AppSheetでノーコード開発の環境構築をしてみた

# はじめに
Googleの提供するノーコードツールAppSheetを使い、ノーコードの環境構築をしてみました

# 前提
Googleのアカウントを作成する

https://accounts.google.com/signup/v2/webcreateaccount?flowName=GlifWebSignIn&flowEntry=SignUp

# 手順
1.AppSheet( https://www.appsheet.com/ )のページを開く
↓バナーをクリックすると飛べます

https://www.appsheet.com/

2.「Googleスプレッドシートとフォーム」をクリックします
![スクリーンショット 2022-03-19 13.55.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/603047/ea4b1e10-1978-ec77-3515-49986425d9cc.png)

3.ログインするGoogleアカウントを選択します
![スクリーンショット 2022-03-19 13.55.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/603047/a745046d-d69e-476a-69f5-863c12218b38.png)

4.「許可」をクリックします
![スクリーンショット 2022-03-19 13.58.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/603047/9e3704a5-474f-8c87-e11d-9ef32f97c0be.png)

このページが開けば環境構築完了です！
![スクリーンショット 2022-03-19 14.08.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/603047/147cc57c-380d-b762-31a6-1b30e1246765.png)

Google AppSheetは無料で使用できますが
デプロイをすると料金が発生してしまいます。
開発をして動かす際には、誤ってデプロイしないように注意しましょう

AppSheet 料金表

https://solutions.appsheet.com/pricing
よく使うgitコマンド

# GitLab/GitHubからクローンしてくる

```
git clone 【URL】 【フォルダ名】
```

フォルダ名を指定すると自分の好きなフォルダ名でクローンできる

# 自分のブランチを切って開発する

```
git checkout -b dev
```

devブランチが作成され、さらに作ったブランチに切り替えてくれる。

# 自分のブランチをpushする

```
git push -u origin dev
```

これでGitLabやGitHubにpushできる。
一度このコマンドを使えば、次からはgit pushとするだけでpushできるようになる。

# いらないブランチを削除する

```
git branch -d dev
```

# 今の作業をなかったことにしたい！

```
git reset --hard HEAD
```

前回のコミットしたところまで戻してくれる。
【Kotlin】アルゴ式 標準入力 2-10（文字を入れ替える）

「[集まれKotlin好き！Kotlin愛好会 vol.35 @オンライン](https://love-kotlin.connpass.com/event/241581/)」 における
談義「Kotlinでアルゴ式に入門してみた」（jollyjoester 氏）にて
「[アルゴ式](https://algo-method.com)」というものを紹介していただいて興味を持ったのでやってみた。

アルゴ式とは、競技プログラミングのような形式で、基礎的なプログラミングの練習ができるサイトである。

談義の中で紹介された問題「[標準入力 2-10](https://algo-method.com/tasks/33)」は Kotlin だとさまざまな解答がありそうなので、自分の解答を記しておく。

```Kotlin
fun main() {
    // 文字列 S。
    val s = readLine()
        .let { checkNotNull(it) }
    // 正の整数 N, M を 0 始まりにしたもの。
    val (n, m) = readLine()
        .let { checkNotNull(it) }
        .split(" ")
        .map { it.toInt() }
        .map { it - 1 } // 1 始まりの値を 0 始まりにする。

    StringBuilder(s).also {
        it[n] = s[m]
        it[m] = s[n]
    }.also {
        println(it)
    }
}
```

前半では標準入力から文字列 S と正の整数 N, M を取り出している。
ここは概ねこれ以上の解答はできないだろう。

後半は文字を入れ替えて結果を出力している。
Kotlin の標準ライブラリーには文字を入れ替える関数はないので、
文字を入れ替える方法はいくつも考えられる。
例えば次のようなものだ。

- [`String.replaceRange`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.text/replace-range.html) 関数を使う
- 1文字ずつに分解して文字を入れ替え、文字列に戻す
    - 分解したものは配列にいれる？　`MutableList` にいれる？　分解に使う関数は？
    - 配列や `MutableList` にも要素を入れ替える関数はなかったはずなので、どう書く？

前者は、文字を入れ替えたいのに範囲を指定するため、今ひとつ美しくなかった。

後者は、文字列を配列や `MutableList` にするというのがなんとなく気に食わない。
文字列を編集するのだから、と `StringBuilder` にしてみた。
分解した文字を文字列に結合しなおす必要がなくなり、シンプルになった。

／以上
WordPressでプラグイン新規追加ボタンがない時

今回は、私がmixhostでWordPressで作られたウェブサイトの復旧作業を行なったときに、プラグインの新規追加ボタンが見つからず苦労しましたので備忘録を兼ねて記録します。

ネット検索で探しましたが、どれも当てはまりませんでした。

強いて言えば、こちらの記事に興味を持ちました。

https://nobe-nobe.blogspot.com/2017/09/wordpress-plugin.html

念の為 phpMyAdminで確認しましたが私の場合は、
s:15:"install_plugins";
がありました。

また、WordPressをダウンロードしてmixhostにアップロードする際に、wp-config.phpを復旧するウェブサイトの内容でアップロードしました。
それでも新規追加ボタンが表示されないので、wp-config.phpを確認しました。
すると復旧するウェブサイトのwp-config.phpに、下記のコードが一番最後に書かれていることに気がつきました。



```php
/** Sets up WordPress vars and included files. */
require_once ABSPATH . 'wp-settings.php';

define('DISALLOW_FILE_EDIT', true);
define('DISALLOW_FILE_MODS', true);
```

WordPressからダウンロードしたwp-confing.phpに、データベースの接続情報のみ修正して、サーバーにアップロードしました。
そうしたら、プラグイン新規追加のボタンが表示されました。
docker logsを削除する方法

# docker logs
コンテナ内のログを確認したいときに使える。
-fとオプションをつければ、リアルタイムでログを確認できる。

```
docker logs -f コンテナ名
```

# ログが溜まりすぎて削除したいとき

## 1. ログのファイルパスを確認する

```
docker inspect コンテナ名 | grep -i log
```

これを実行すると、ログが格納されているファイルが表示される。

## 2. ログファイルを削除

```
sudo truncate -s 0 /var/lib/docker/containers/xxxxx/yyyyy-json.log
```

これを実行すれば、ログが削除される。
Nethack 操作 簡易まとめ

### 移動

|y|k|u|
|-|-|-|
|h|.|l|
|b|j|n|

* `.` はその場で足踏みする
* [Shiftキー] `.` 以外はShiftキーを押すと高速移動(ぶつかるまで進む)

|キー|コマンド名|内容|
|-|-|-|
|<|up|階段を昇る．|
|>|down|階段を降りる．|
|.|rest|休む．|
|_|travel|マップで示された点に最小経路アルゴリズムで移動する．|
|o|open|扉を開ける．|
|^D|kick|蹴る．(鍵がかかった扉に使用)|
|:|look|何があるかを見る．|
|;|farlook|他に何があるか調べる．(地図上の記号を選択する)|
|M-l|loot|床の上の箱(宝箱など)の中身を強奪する．|

### システム

|キー|コマンド名|内容|
|-|-|-|
|?|help|ヘルプ文章の表示|
|#quit|quit|現在のゲームを保存せずにゲームをやめる|
|S|save|ゲームを(後で続けるために)保存して終了する[復元するには，再びプレイして同じキャラクター名を使う]|
|O|options|オプションを設定する．|
|/|what-is|画面の記号が何かを知る．|
|^A|again|1つ前のコマンドを再実行する．(^Aはコントロール-A)|
|^R|redraw|画面を書きなおす．|
|^P|prevmsg|前のメッセージを見る．(^Pは繰り返し使える)|

### 忘備

|キー|コマンド名|内容|
|-|-|-|
|M-A|annotate|現在の階に1行メモを追加する(M-O参照)|
|M-O|overview|訪れた階に関する情報とメモを表示する|
|M-n|name|特定の怪物，個々のアイテム，アイテムの種類に名前をつける．|

### 装備

|キー|コマンド名|内容|
|-|-|-|
|*||装備しているものを表示|
|w|wield|武器を装備する．(w- 何も装備しない)|
|x|xchange 左右の武器を交換する．|
|X|twoweapon 二刀流を切り替える．|
|W|wear|鎧を着る．|
|T|takeoff 鎧を脱ぐ．|
|A|armor|全ての鎧を脱ぐ．|
|P|puton|アクセサリを身につける．(指輪，魔除けなど ...タオルで目隠しすることも)|
|R|remove|アクセサリをはずす．(指輪，魔除けなど)|

### アイテム

|キー|コマンド名|内容|
|-|-|-|
|d|drop|物を置く．d7a：aなるものを7個置く．|
|D|Drop|種類を選んで物を置く．|
|i|invent|あなたの持ち物一覧．(あなたが持っているもの全て)|
|I|Invent|種類を選択して持ち物一覧．|
|||Iu - 未払の道具一覧．|
|||Ix - 未払だが使いはたした道具一覧．|
|||I$ - お金を数える．|

### 神

|キー|コマンド名|内容|
|-|-|-|
|M-p|pray|神に祈る．|
|M-o|offer|神に供物を捧げる．|

### 未整理

|キー|コマンド名|内容|
|-|-|-|
|\\ |known|今までに発見した道具の一覧を表示する．|
|v|version|バージョンを表示する．|
|V|history|ゲームの歴史を表示する．|
|#||拡張コマンド(#?でヘルプ)|
|&|what-does|キーを押して起動するコマンドを説明する．|
|^T|Teleport|瞬間移動．(可能なら)|
|^X|show|属性を表示する|
|a|apply|道具を使う．(つるはし，鍵，カメラ等)|
|c|close|扉を閉める．|
|C|call|特定の怪物，個々のアイテム，アイテムの種類に名前をつける．|
|e|eat|何かを食べる．|
|E|engrave 床のほこりに文字を書く．(E- 指を使って)|
|f|fire|装填されているものを射る．|
|F|fight|方向を指定し戦う．|
|p|pay|勘定を払う．(店の中で)|
|q|quaff|何かを飲む(水薬, 水, など)|
|Q|quiver|装填するものを選択する．(終了は'#quit')|
|r|read|巻物や魔法の本を読む．|
|s|search|秘密の扉，隠れた罠，怪物を探す．|
|t|throw|武器を投げる，撃つ，射る．|
|z|zap|杖を振りかざす．(number_pad が -1 なら z ではなく y)|
|Z|Zap|呪文を唱える．(number_pad が -1 なら Z ではなく Y)|
|^|trap_id 罠を調べる．|
|+|spells|唱えることのできる魔法の一覧．|
|`|classkn|一つの種類の中で発見した物を表示する|
|,|pickup|物を拾う．|
|@||pickup(自動pickup)のオプションを切り替える．|
|M-?||拡張コマンドヘルプの表示 (プラットフォームが許せば)|
|M-2|twoweapon|二刀流の切り替え (number_pad が無効なら)|
|M-a|adjust|持ち物一覧の調整|
|M-c|chat|誰かと話す|
|M-C|conduct|自発的挑戦を表示する|
|M-d|dip|何かに物を浸す．|
|M-e|enhance|武器の技量を高めるまたは見る|
|M-f|force|鍵をこじあける．|
|M-i|invoke|物の特別な力を使う．|
|M-j|jump|他の位置に飛びうつる．|
|M-m|monster|怪物の特別能力を使う．|
|M-r|rub|ランプをこする．|
|M-R|ride|騎乗するまたは騎乗をやめる|
|M-s|sit|座る．|
|M-t|turn|アンデットを土に返す．|
|M-T|tip|入れ物をひっくり返す|
|M-u|untrap|罠をはずす．|
|M-v|version|コンパイル時のオプションを表示する．|
|M-w|wipe|顔を拭う．|
[Flutter] アプリからCloudFunctionsの関数に引数を渡す、返り値を受け取る

Flutterのアプリ作成で実装したことです。

何回も使いそうなので忘れないように記事にしておこうと思いました。

`onRequest` と `onCall` で引数の渡し方、返り値の受け取り方が少し違います。

一度まとめておくと、次から調べる時間が短縮できるので。

このやり方以外にもあると思いますが、少しでも誰かのお役に立てれば、幸いに思います。

## onRequestの場合

CloudFunctionsの関数

```typescript
import * as functions from "firebase-functions";

export const testFunction = functions
  .region("asia-northeast1")
  .https.onRequest(async (request, response) => {
    const testVar = request.body.data.testVar;  // requestに格納されている引数を受け取る

    ...省略

    response.status(200).send({
      data: { resultVar: resultVar }, // responseに格納して、アプリに返り値を返す
    });
});
```

アプリ側

```dart
import 'package:cloud_functions/cloud_functions.dart';

final callable = FirebaseFunctions.instanceFor(region: 'asia-northeast1')
        .httpsCallable('testFunction');

final response = await callable.call({ 'testVar': testVar });  // 変数を渡す

final resultVar = response.data['resultVar'];  // 返り値を取り出す
```


## onCallの場合

CloudFunctionsの関数

```typescript
import * as functions from "firebase-functions";

export const testFunction = functions
  .region("asia-northeast1")
  .https.onCall(async (data, context) => {
    const testVar = data.testVar;  // dataに格納されている引数を受け取る

    ...省略
  
    return { resultVar };  // アプリ側に返り値を返す
  });
```

アプリ側

```dart
import 'package:cloud_functions/cloud_functions.dart';
import 'package:firebase_core/firebase_core.dart';

final functions = FirebaseFunctions.instanceFor(
    app: Firebase.app(), region: 'asia-northeast1');

final callable = functions.httpsCallable('testFunction');
final result = await callable({'testVar': testVar});  // 変数を渡す

final resultVar = result.data['resultVar'];  // 返り値を取り出す
```

## 参考にしたサイト

https://firebase.google.com/docs/functions/callable?hl=ja#write_and_deploy_the_callable_function

https://kenchan0130.github.io/post/2021-10-29-1
SSH 免密登陆服务器

## 0x01 客户端生成公私钥

```shell
$ ssh-keygen
```

一直按回车即可，生成后会在用户目录下有个 .ssh 的目录，其中包含公钥和私钥



确保客户端，也就是本机已经生成对应密钥，如果没生成，需要先生成



## 0x02 上传公钥到服务器

```shell
$ ssh-copy-id -i ~/.ssh/id_rsa.pub root@192.55.37.187
```



运行后输入密码，然后提示一下信息代表成功

```
Number of key(s) added:        1

Now try logging into the machine, with:   "ssh 'root@192.55.37.187'"
and check to make sure that only the key(s) you wanted were added.
```



此时就可以在服务器下看到对应的公钥

```shell
[root@VM]$ cat ~/.ssh/authorized_keys   
```





## 0x03 测试

```shell
$ ssh root@192.55.37.187
```

运行后成功免密登陆
【Stylus】crayon-syntaxをVSCode風にするcssコード(メモ用)

#### crayon-syntaxのcssがあんま気に入らないので変えてみました

```css

l.crayon-syntax{
    margin-top : 0px;
    border-style : none !important;
}

.crayon-theme-ssms2012{
    background-color:#383838 !important;
}

.crayon-theme-ssms2012 .crayon-marked-line{
    background-color:#383838 !important;
    border-color:yellow !important;
}

.crayon-nums-content{
    display: none !important;
}

.crayon-theme-classic .crayon-pre .crayon-r{
    color: #07f !important;
}

.crayon-theme-classic .crayon-pre .crayon-e{
    color: #2f90ff !important;
}

.crayon-theme-classic .crayon-pre .crayon-s{
    color: #e16023 !important;
}

.crayon-theme-ssms2012 .crayon-toolbar{
    border-bottom-width: 0px !important;
    border-bottom-style: none !important;
}

.crayon-theme-classic .crayon-toolbar{
    display: none !important;
}

.crayon-tools{
    display: none !important;
}

.crayon-main{
    margin-left: auto;
    background-color:#383838 !important;
}

.crayon-theme-classic .crayon-striped-line{
    background-color:#383838 !important;
    color: white !important;
}

.crayon-theme-classic .crayon-pre .crayon-i{
    color: white !important;
}

.crayon-striped-line{
    background-color:#383838 !important;
    color:#383838 !important;
}

.crayon-theme-ssms2012 .crayon-pre .crayon-sy{
    color: #dcdcdc !important;
}

.crayon-theme-ssms2012 .crayon-pre .crayon-v{
    color: rgb(58, 183, 191) !important;
}

.crayon-theme-ssms2012 .crayon-pre .crayon-t{
    color: rgb(199, 199, 31) !important;
}

.crayon-theme-ssms2012 .crayon-pre .crayon-s{
    color: #e16023 !important;
}

.crayon-theme-ssms2012 .crayon-pre .crayon-cn{
    color: #00c101 !important;
}

.crayon-syntax .crayon-pre, .crayon-syntax pre{
    color: #dcdcdc !important;
}

.crayon-theme-ssms2012 .crayon-pre .crayon-st{
    color: #e600e6 !important;
}

.crayon-theme-ssms2012 .crayon-pre .crayon-k{
    color: #e600e6 !important;
}

.crayon-theme-ssms2012 .crayon-pre .crayon-r{
    color: #4b4bff !important;
}

.crayon-theme-ssms2012 .crayon-pre .crayon-m{
    color: #4b4bff !important;
}

.crayon-theme-ssms2012 .crayon-pre .crayon-e{
    color: #dcdcdc !important;
}

.crayon-theme-ssms2012 .crayon-pre .crayon-c{
    color: #00d100 !important;
}

.crayon-syntax .crayon-pre, .crayon-syntax pre{
    color: white !important;
}

```

### 各種設定


Crayon Syntax
適応先：正規表現に一致するURL URL:(https|http)://*.*

### プレビュー

これが
![screenshot-000002.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1086026/adfb521a-7c30-dc96-e7c3-cc7eb94df54c.jpeg)


こうなる
![screenshot-000000.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1086026/7fcdb9eb-d538-07ee-5bff-74c2dbe4489a.jpeg)
[firebase] emulators:startでportエラーが出る

```firebase emulators:start```を打った時に出たエラー

# エラー内容
```bash
⚠  hosting: Port 5000 is not open on localhost, could not start Hosting Emulator.
⚠  hosting: To select a different host/port, specify that host/port in a firebase.json config file:
      {
        // ...
        "emulators": {
          "hosting": {
            "host": "HOST",
            "port": "PORT"
          }
        }
      }
i  emulators: Shutting down emulators.
```

portの5000番が使われているらしい。
記憶にないけどチェック。

確かに使われているけどcommplex-mainってなんだ？
```bash
$ lsof -i :5000

# ---------------------------------------->
COMMAND   PID   FD   TYPE  SIZE/OFF NODE NAME
ControlCe 397   21u  IPv4  0t0  TCP *:commplex-main (LISTEN)
ControlCe 397   22u  IPv6  0t0  TCP *:commplex-main (LISTEN)
```

# 解決
MacをMontereyにバージョンアップした時にAirPlayなる機能が5000番ポートを使っているらしい。
参考：[MacをMontereyにアップデートしたらFlaskが5000番ポートで起動できなくなった](https://www.keisuke69.net/entry/2021/10/29/012608)

何かと5000番使うこと多いので、出くわすことが多そう？

firebaseのエミュレータ自体は5002番にして使えるようになりました！
```firebase.json
"emulators": {
    "hosting": {
        "port": 5002
    },
}

```
C#のgetアクセサーのみのプロパティを使用する際に気をつけるべきこと

# TL;DR
getアクセサーのみ持つプロパティに対して、コンストラクターで値を設定するか、getterで値を設定するかによって取得する値が変わる場合があります。

```cs
// プロパティに対して
public string Foo { get; }

// コンストラクターで値を設定
public ClassName()
{
    Ticks = Datetime.Now.Ticks.ToString();
}

// getterで値を設定
public string Ticks => Datetime.Now.Ticks.ToString();
```

# どういうことか？
プロパティをインターフェースや基底クラスで定義して、派生クラスで具体的な値を設定したいという場合があるかと思います。

```cs
interface SampleInterface
{
    public string Ticks { get; }
}
```
これを継承して実装します。

## getterで設定
```cs
// SampleInterfaceを派生クラスで実装する
class DerivedClass : SampleInterface
{
    public string Ticks => DateTime.Now.Ticks.ToString();
}
```

これを使って値を取得してみます。

```cs
static void Main(string[] args)
{
    SampleInterface instance = new DerivedClass();
    Console.WriteLine(instance.Ticks); // 637832891548362530
    Console.WriteLine(instance.Ticks); // 637832891548915630：上と値が違う
}
```

`get => 処理`のように実装した場合は、値を取得しようとするごとにメソッドのように実行されます。
そのため、毎回処理が実行され、DateTimeのTicksを取得しようとした場合には別の値になったり、
`=> new Class()`のようにインスタンスを返す場合は、毎回違うインスタンスが返ってくることになります。

## コンストラクターで設定
今度はコンストラクターで値を設定します。

```cs
class DerivedClass : SampleInterface
{
    public string Ticks { get; }

    public DerivedClass()
    {
        Ticks = DateTime.Now.Ticks.ToString();
    }
}
```

```cs
static void Main(string[] args)
{
    SampleInterface instance = new DerivedClass();
    Console.WriteLine(instance.Ticks); // 637832896477867260
    Console.WriteLine(instance.Ticks); // 637832896477867260：上と値が同じ
}
```

コンストラクターで値を設定した場合は、値が一意に定まります。
そのため、この場合はコンストラクターで設定した時点でのTicksが表示されますし、
`Value = new Class();`などのように、参照型の値をコンストラクターで入れていた場合は毎回同じ参照のインスタンスが返ってきます。

つまり、同じgetterのみのプロパティだとしても、実装の方法によって返ってくる値が異なる場合が出てきます。

---

@Zuishin さんから補足情報いただきました。
下記の方法ではコンストラクターでの初期化よりも前に実行されるため、この方法でも値が一意に定まります。

```cs
public string Ticks { get; } = Datetime.Now.Ticks.ToString();
```
コンストラクターの引数で処理を変える必要がない場合はこちらも有効ですね。


# パフォーマンスへの影響
getter処理に実装した場合、毎回その処理が実行されて値が返ってきます。
何かしらのクラスをnewして返す場合では、newは比較的コストが高いため、
getter内でnewして返す実装は、コンストラクターで初期値としてnewしている場合に比べてパフォーマンスが落ちます。

# おわりに
自分用メモとして書きました。
当たり前といえば当たり前なのですが、2つの実装の違いを理解しておかないと不具合に繋がりかねないので気をつけましょう！
AWS公式資料で挑むSCS認定(18)-模擬テスト

##### [前回] [AWS公式資料で挑むSCS認定(17)-Macie](https://qiita.com/mingchun_zhao/items/6941cc760f546c1e76d4)

## はじめに

AWS公式の無料トレーニングコース
[Exam Readiness: AWS Certified Security - Specialty (Japanese)](https://explore.skillbuilder.aws/learn/course/762/play/41659/exam-readiness-aws-certified-security-specialty-japanese) 
の最後にある模擬テスト(24問)を解いてみました。
結果見事に不合格、準備不足と判断し認定試験延期する羽目に(24時間前まで変更・キャンセル可)。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2523098/56e02444-5fcd-c10e-afcf-71795ba1b20f.png)


ちなみに、上記コースの出題分野別解説と例題、とても分かりやすかったです。

今回は、模擬テストの振り返り、自己分析となります。
不正解の原因をカテゴライズしてみると、

- 知識不足
- 解き方
- その他

1つずつ、紐解いてみます。

## 知識不足

- 似たようなサービスにおける守備範囲の差異
    - DDoS対策に使用できるサービス複数あるが、守れる範囲は異なる
        - Amazon GuardDutyはEC2を守れてもアプリケーション全体は守れない
    - セキュリティグループに無制限アクセス設定がないかの確認は、AWS ConfigよりTrusted Advisorに軍配が
- サービスの「できる・できない」
    - DynamoDB がサポートするのはIDベースポリシー(IAMポリシー)のみ、リソースベースポリシーはサポートしない
    - S3パケットACLで設定できるのは、許可のみで拒否はできない
    - CloudTrail はAPI使用をモニタリングできても、アクセス許可はチェックできない
- 連携できないサービス
    - Trusted Advisor のチェックに基づき CloudWatch Events を設定することはできない
    - Amazon Inspector で EC2インスタンスの AMI が正しいかはチェックできない

## 解き方

- 問題文を正しく理解できず齟齬や漏れ
    - だれ(主語)が、だれから、何を、どう守りたいか
        - アクセス許可を付与すべきは「管理者」「所有者」「利用者」「リソース」どっち?
        - 守りたいのは、S3バケットなのかオブジェクト?
        - S3バケットへのアクセスは、同じAWSアカウントから?パートナーアカウントから?
        - セキュリティパッチ当てたいのは、EC2インスタンスのみか、オンプレミスサーバもか
- 消去法をもっと積極的に使うべき
    - 時間の節約になる
    - 余計な選択肢を排除し、集中力高められる
- 実現できても手順が複雑な選択肢ははずれ
    - AWS様のマネージドサービスが王道、サードパーティーはやむを得ないケースのみ
    - CLIでスクリプト組んだりするソリューションはほぼはずれ
- お作法的に適切か否か
    - アプリケーションでIAMユーザーのアクセスキーを使用できなくもないが、不適切

## その他

- 問題文の日本語訳にくせがあり、すんなり理解できなかった(言い訳?)
    - これは慣れでしょうか

## おわりに

以上、模擬テストの反省回でした。
上記ポイントを意識しながら、引き続き試験準備進めようと思います。
次回をお楽しみに。

##### [次回] [AWS公式資料で挑むSCS認定(19)-こんな時どうする](https://qiita.com/mingchun_zhao/items/62c7d4a69dafb3f46e66)
Laravelのフォーム利用【個人的なお勉強アウトプット】

## 参考図書
https://www.amazon.co.jp/dp/B08625YD7H/ref=sspa_dk_detail_1?psc=1p13NParams&spLa=ZW5jcnlwdGVkUXVhbGlmaWVyPUEyNlpDQ0JRWDc1RDdOJmVuY3J5cHRlZElkPUEwMDc4ODYzM0UxR0hGQk9QSkk0OCZlbmNyeXB0ZWRBZElkPUEyUTA4TzVGQ1MzNkNPJndpZGdldE5hbWU9c3BfZGV0YWlsJmFjdGlvbj1jbGlja1JlZGlyZWN0JmRvTm90TG9nQ2xpY2s9dHJ1ZQ==

## Bladeテンプレートにフォームを用意
```
<form method="POST" action="/hello">
@csrf
<input type="text" name="msg">
<input type="submit">
</form>
```

### CSRF対策
BladeディレクティブでCSRF対策ができる
CSRFとは、表示しているフォームとは別サーバーから悪意のあるプログラムをリクエストすること。
CSRF対策はブラウザ側とサーバー側でトークンを発行し、フォームから送信された時にそのトークンが一致しているかどうか確かめることで当該フォームからのリクエストかどうか確認している。


## コントローラ側にアクションを用意する

```
class HelloController extends Controller{

public function index(){
$data = ['msg'=>'お名前を入力してください。'];
return view('hello.index', $data)
}

public function post(Request $request){
$msg = $request->msg;
return view('hello.index', $msg);
}

}

```
アクションの引数でRequestクラスをインスタンス化している。
当該インスタンスにはフォームからのPOST情報が含まれている
input nameでmsgとしたものも動的メソッドとして取り出すことが可能。

## ルートでPOSTのアクションの設定
`Route::post('hello', 'App\Http\Controllers\HelloController@post')`
マーケティング会社で得た、webサイト制作をするときのメモ

どの層にアピールするためのwebサイトか？

潜在顧客（興味、関心から検索している層）
見込み顧客（すでに行動を起こしたい層）

ファネルを想定しよう
モバイルサイトにはカスタマージャーニーを想定しよう
Socket 通信の基本をメモる

Rubyの非同期タスクスケジューラーを自作するにあたり、ネットワーク通信、特にソケットについて理解を深めておきたいと考えた。

経験上まずは手を動かしてみるのがベストだと考えている。以下の素晴らしい記事が見つかったので自分でもやってみる。

https://qiita.com/MikuriyaHiroshi/items/b0a40f5e7b7be1ef327c

手を動かした中で個人的にメモっておきたいところを残す。


## Socketとは

Socketは、ネットワークはプログラムが外部のネットワークと通信する際のインターフェイスとして使われるもの。ファイルとして抽象化される。

## Socket通信の種類の例

- インターネット通信
  - UDP (dgram型)
  - TCP (stream型)
- プロセス間通信(UNIX domain socket通信)
  - UNIX domain socket dgram (dgram型)
  - UNIX domain socket stream (stream型)


<details>
<summary>dgramとstreamの違い</summary>

関連記事を貼る。
- https://docs.oracle.com/cd/E19253-01/819-0392/sockets-4/index.html

memo. sreamの方が信頼性という面では優れている。

</details>

## サーバーとなるプロセスがデータを受信する手順

以下インターネット通信の場合。unix domain socket通信の場合は、「ポート」が「ファイル」になる。

1. ソケットを生成する(複数可能)
   - ソケットを初期化する
   - そのソケットを一つのポートにバインドする
   - [stream型のソケットの場合のみ]ソケットを通してポートをリッスンする。
2. クライアントからのデータ受信を無限ループする
   - IO::select で、ソケットに張り付き、データが到着したソケットを検知する。
   - [ストリーム型のソケットの場合のみ] `Socket#accept` で、そのソケットのクライアントとのconnectionをアクセプトする
   - そのソケットに入っているデータを読み出す。
   - [ストリーム型のソケットの場合のみ] `Socket#close` で、connectionをclose する。

終了後は、ソケットをcloseすることを忘れずに。


## クライアントとなるプロセスがデータを送信する手順

1. ソケットを生成する
1. ソケット経由でデータを書き込む。
    - ストリーム通信のソケットの場合には、`Socket#connect(送信先)`でコネクションを確立してから、`Socket#write(データ)`を使用する。
    - データグラム通信のソケットの場合には、`Socket#send(データ, 送信先)`を使用する。






## Rubyでの実装

https://qiita.com/MikuriyaHiroshi/items/b0a40f5e7b7be1ef327c

を参照すると良い。以下の通信が実装されている。


- internet❎dgram
    - `Socket.new(:INET, :DGRAM)`でソケット生成
    - `UDPSocket.new`でもソケット生成可能
- internet❎stream
    - `Socket.new(:INET, :DGRAM)`でソケット生成
    - `TCPSocket.new`でもソケット生成可能
    - `TCPServer.new`でもソケット生成+listenが可能
- unix_domain_socket❎dgram
    - `Socket.new(:UNIX, :DGRAM)`でソケット生成
- unix_domain_socket❎stream
    - `Socket.new(:UNIX, :DGRAM)`でソケット生成
    - `UNIXSocket.new`でもソケット生成が可能
    - `UNIXServer.new`でもソケット生成+listenが可能


## Ref.

https://qiita.com/kudojp/items/60beb428b3efef852c7e


<details>
<summary>メモ</summary>

- ストリーム型のsocketには、`Socket#write`

</details>


<details>
<summary>追記</summary>

https://docs.ruby-lang.org/ja/latest/class/Socket.html#I_ACCEPT_NONBLOCK に書いてあった。

> ソケットオブジェクトを生成する汎用的な方法として Socket.new がありますが、以下のようなより便利なメソッドがあります。
>
> - TCP のクライアントソケット Socket.tcp TCPSocket.open
> - TCP のサーバソケット Socket.tcp_server_loop, Socket.tcp_server_sockets, TCPServer.open
> - UNIX socket のクライアントソケット Socket.unix UNIXSocket.open
> - UNIX socket のサーバソケット Socket.unix_server_loop, Socket.unix_server_socket, UNIXServer.open
>
> また、クライアントソケットは Addrinfo#connect で、サーバソケットを Addrinfo#bind や Addrinfo#listen で作ることもできます。



</details>
【java】ヒープメモリエラー

javaで下記エラーが出ました。
よくあるエラー。
**java.lang.OutOfMemoryError: Java heap space**

コードは下記

```sample.java

//初期化
long n = 沢山;

//日付ループ
for(long i=0;i<n;i++){
    //SimpleDateFormatオブジェクト
    SimpleDateFormat sdf = new SimpleDateFormat(format);  //error
    //フォーマット指定あり
    System.out.println(sdf.format(date));
}
```

この通りに書いているわけではないですが、あるクラスの中で**SimpleDateFormat**を使用していて、サンプルデータ作成時にそのクラスをループで呼び出していたというものでした。
解決策としては、下記の様にクラスを開放して問題なく動作しました。

```sample.java

//初期化
long n = 沢山;

//日付ループ
for(long i=0;i<n;i++){
    //SimpleDateFormatオブジェクト
    SimpleDateFormat sdf = new SimpleDateFormat(format);  //ok
    //フォーマット指定あり
    System.out.println(sdf.format(date));
    //クリア
    sdf = null;  //追記
}
```
システム系の論文調査方法のメモ

システム系の論文の情報取集と論文の読み方についての記事

- [システム系論文の情報収集方法](https://mmi.hatenablog.com/entry/2020/12/01/034833)
- [矢谷流論文の読み方](https://iis-lab.org/misc/paperreading/)

# 国際会議
### USENIXの会議全般
- OSDI
- ATC
- NSDI
- FAST
- SEC
### IEEE
- SOSP
### ACM
-  ASPLOS
-  VEE
-  EuroSys
terraform init を実行し `.terraform.lock.hcl` に差分が生じた際に疑うこと


## はじめに

今までと異なる CPU アーキテクチャのマシンで terraform を触る機会があり、 `terraform init` を実行した際に `.terraform.lock.hcl` に差分が生じ、その理由について調べたのでまとめました

## 対象バージョンや環境

- Terraform バージョン
  - 0.14 以上
- 実行環境
  - 普段 `darwin_amd64` から `terraform` を実行していたが、新たに `darwin_arm64` 環境で実行

## 差分の理由と対応方法

### 対応方法

以下のように `h1` のみ差分が生じた場合は `.terraform.lock.hcl` をコミットしましょう
※ 今後も継続して今までと異なる環境で `terraform`実行が予定されている場合

```diff_hcl:.terraform.lock.hcl
provider "registry.terraform.io/xxx/xxx" {
  version     = "x.xx.x"
  constraints = "~> x.xx.x"
  hashes = [
+    "h1:xxxxxx",
     "h1:yyyyyy",
```

## 差分の理由

Terraform の v0.4 から `.terraform.lock.hcl` を生成するようになりました

`.terraform.lock.hcl` については以下記事にまとめられていたのでそちらを参照ください

https://qiita.com/asdasda/items/b8becb672ad572897c25

`z1` は 該当の terraform provider パッケージの zip ハッシュ `h1` は 該当の terraform provider の内容物の ハッシュ値を指します
また、`z1` `h1` の様々な理由から、ハッシュ値の取得元も異なるようで実行環境が増えるとハッシュ値が追加されます
様々な理由については、以下 Isuue のコメントを参照していただくとスムーズです

https://github.com/hashicorp/terraform/issues/27264#issuecomment-743389837

## 実行環境の追加

私は `terraform init` 実行時に差分が生じ今回の事象に気づきましたが、実行環境の追加が予め予定されている場合、以下コマンドで実行予定のプラットフォームを追加することができます

下記は、 `darwin_arm64` 環境での実行を予定している場合の例

```sh
terraform providers lock -platform=darwin_arm64
```

`terraform providers lock` コマンドについて詳しく知りたい方は以下 URL から確認できます

https://www.terraform.io/cli/commands/providers/lock

## Reference

https://www.terraform.io/language/files/dependency-lock
Laravelのリクエストとレスポンス【個人的なお勉強アウトプット】

# 参考図書
https://www.amazon.co.jp/dp/B08625YD7H/ref=sspa_dk_detail_1?psc=1p13NParams&spLa=ZW5jcnlwdGVkUXVhbGlmaWVyPUEyNlpDQ0JRWDc1RDdOJmVuY3J5cHRlZElkPUEwMDc4ODYzM0UxR0hGQk9QSkk0OCZlbmNyeXB0ZWRBZElkPUEyUTA4TzVGQ1MzNkNPJndpZGdldE5hbWU9c3BfZGV0YWlsJmFjdGlvbj1jbGlja1JlZGlyZWN0JmRvTm90TG9nQ2xpY2s9dHJ1ZQ==

リクエストとレンスポンスの情報はLaravelでも利用可能。
Illuminate\Http名前空間に用意されている`Request` `Response`クラスとして用意されている。
これらのオブジェクトには、リクエストまたはレスポンスに関する情報を保管するプロパティやそれらを操作するためのメソッドが用意されている。

コントローラー側でuse分で記載する

```
use Illuminate\Http\Request;
use Illuminate\Http\Response;

class HelloController extends Controller{
public function index(Request $request, Response $response){

}
}

```
アクションの引数としてそれぞれのクラスをインスタンス化している。
上記のインスタンス化した変数を表示するとRequestとResponseが全部表示される。

# Requestの主なメソッド
```
$request->url();
$request->fullurl();
$request->path();
```
## Responseの主なメソッド
```
$request->content();
$request->setContent(値);
```
Laravel、URLのパラメータの受け渡し方【個人的なお勉強アウトプット】

# 参考図書
https://www.amazon.co.jp/dp/B08625YD7H/ref=sspa_dk_detail_1?psc=1p13NParams&spLa=ZW5jcnlwdGVkUXVhbGlmaWVyPUEyNlpDQ0JRWDc1RDdOJmVuY3J5cHRlZElkPUEwMDc4ODYzM0UxR0hGQk9QSkk0OCZlbmNyeXB0ZWRBZElkPUEyUTA4TzVGQ1MzNkNPJndpZGdldE5hbWU9c3BfZGV0YWlsJmFjdGlvbj1jbGlja1JlZGlyZWN0JmRvTm90TG9nQ2xpY2s9dHJ1ZQ==

Route::getは、アクセスする際にパラーメータを設定し値を渡すことができる。
`Route::get('/hogehoge/{パラメータ}'), function($受け取る引数){hogehoge});`
第一引数に設定したパスの{パラメータ}に当たる部分が取り出せるようになる。
第二引数の関数では、パラメータの値を受け取る引数を用意しておく。

例）http://hogehoge/hello/sampleにアクセス
```
Route::get('hello/{msg}' function($msg){
　$msgに'sample'が代入される
});
```

任意パラメータにはパラメータの末尾に`?`をつける。
任意パラメータには値が渡される仮引数にデフォルト値を指定して、引数が渡されなくても処理できるようにしておく。
`Route::get('hello/{msg?},function($msg='no message.')){・・・`
【Rails】異なる階層のファイルをrender (部分テンプレート)



## やりたいこと

似たデザインのページを使い回したい。
以下のように、処理が完了すると「完了しました」と通知するようなページが複数存在している状況で
「完了ページ」をテンプレート化したい。

ログアウト完了→ログアウトしました！
ユーザーの削除完了→ユーザーを削除しました！

## 方法

```ruby
render template: 'ファイル名', locals: { ローカル変数の定義 }
```

今回は`views/shared/after_xxx.html.erb`を呼び出します。
以下のようにタイトルやメッセージ、path を柔軟に変更できるファイルをイメージします。

```_after_xxx.html.erb
<div>
  <div class="">
　　　　　　　<h1><%= title %></h1>　　　#ここ
　　　　</div> 
  <div class="">
    <p><%= message %></p>　　　#ここ
  </div>
  <div class="">
    <%= link_to path_message, path_detail %>　　#ここ
  </div>
</div>
```

以下コントローラ、今回のテンプレートは`views/shared/`にあります。
`views/sessions/`とは異なる階層にテンプレートがあるのでディレクトリの指定をする必要があります。
ログアウト完了後に部分テンプレートを呼び出す処理を想定しています。

```controllers/sessions_controller.rb

class SessionsController < ApplicationController
//

  def destroy
    reset_session
    title = "ログアウトしました"
    message = "お疲れ様でした"
    path_message = "ログイン画面に戻る"
    path_detail = login_path
    render partial: 'shared/after_xxx',
           locals: { title: title, message: message, path_message: path_message, path_detail: path_detail }
  end

//
end
```

ログアウト完了後、以下のようにrenderできました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2508607/fefc2b64-75d0-09e8-85c8-77bcce3311ac.png)


## 参考にした記事

https://railsguides.jp/layouts_and_rendering.html#%E5%88%A5%E3%81%AE%E3%82%B3%E3%83%B3%E3%83%88%E3%83%AD%E3%83%BC%E3%83%A9%E3%81%8B%E3%82%89%E3%82%A2%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3%E3%83%86%E3%83%B3%E3%83%97%E3%83%AC%E3%83%BC%E3%83%88%E3%82%92%E3%83%AC%E3%83%B3%E3%83%80%E3%83%AA%E3%83%B3%E3%82%B0%E3%81%99%E3%82%8B
Dockerコンテナ上にあるファイルを確認したい

# はじめに
最近Dockerを使ってYOLOv5を使ってみたのだが，結果の画像を確認するのにいちいちDockerコンテナ内からホスト側(Dockerコンテナ外)にコピーして確認していた．
今後，物体認識をしていくのにいちいちこんなことをやっていたら気が狂ってしまう．

そこで今回はDocker外のファイルをDockerに**マウント**することでこの問題を解決した．
ぜひ参考にしてほしい．

ちなみにDockerやYOLOv5を使う方法はこちらの記事↓で紹介している．

https://qiita.com/liker59/items/0da75f66542b2e5082f2

## マウント とは
そもそもマウントとは，**ホスト側（Dockerコンテナ外）にあるディレクトリやファイルをDockerコンテナ内で利用できるようにする機能**だ．
詳しいことは以下の記事を参考にした．

https://www.public.ne.jp/2019/02/08/docker-5/

## ディレクトリをマウントする
では実際にマウントをしてみる．

まず，マウントしたいディレクトリ test を用意する．
その中に例えば
``` hello.txt
Hello world!
```
というテキストファイルを用意する．
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2525698/cf6ac8f3-ef20-cb26-12ca-4b89639aab6c.png)

この **testディレクトリ** を今回はマウントする．
マウントはコンテナを作る際に行う．runコマンドを打つときに以下のように打つ．
```
$ docker run -it -d --gpus all --name [name] -v /home/[ユーザディレクトリ]/[マウントしたいディレクトリ]:/usr/[マウント先のディレクトリ] [イメージ] bash
```

コンテナを作成がよく分からない人は[こちらの記事](https://qiita.com/liker59/items/0da75f66542b2e5082f2)に書いているので確認する．

マウント部分は **-v /home/[ユーザディレクトリ]/[マウントしたいディレクトリ]:/[マウント先のディレクトリ]** である．

まず，マウントしたいディレクトリは必ず**絶対パス**で指定すること．
自分はここで非常に手間取った．
```
$ pwd
```
で現在の絶対パスを確認できると思うので，それを入力する．
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2525698/80f8ccc9-96d5-007b-ecff-0f086ace8caa.png)
マウント先のディレクトリも絶対パスで書く必要がある．
今回はqiitaというディレクトリにマウントしたいと思う．

以下が私の環境でうったコマンドだ．
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2525698/73e4be2d-cef2-8933-8558-f68b82484643.png)
これで testディレクトリがマウントされた **mount** というコンテナができる．
ではコンテナ内に入って確認してみよう．

```
$ docker exec -it [name] bash
```

```
# ls
```
listをするとマウント先のディレクトリ qiita が作成されているのが確認できる．
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2525698/5e2e8292-dc14-38c0-778b-907bb396f6c5.png)
そこに移動してみるとhello.txtが確認できる．
```
# cat hello.txt
```
で中身を確認できる．
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2525698/3ce16de0-bd38-974d-83de-3adac817b476.png)


次に，例えばtest ディレクトリ内で新しいファイルを作成してみる．
```
# touch docker.txt
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2525698/ffcfdec0-6d1c-d430-ce84-e258ddadb9eb.png)

ホスト側のディレクトリで確認すると，
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2525698/5469a9d1-71e8-8d30-c008-45dd5f4a90f9.png)
docker.txtが作成されていることが確認できた．
ホスト側の操作もコンテナ内に反映され，コンテナ内の操作もホスト側に反映された．

これでDockerコンテナ内からホスト側のディレクトリを操作できるようになり，画像の確認やテキストの編集を行えるようになる．

# おわりに
今回は，Dockerコンテナ内のファイルを確認できる方法を紹介した．
PHPDocについての個人メモ

# 概要

- PHPDocについてまとめられているリンクなどをここにメモ的に記載しておく。

# わかりやすく説明されていたリンク

- PHPDocそのものについて
    - [https://zonuexe.github.io/phpDocumentor2-ja/references/phpdoc/basic-syntax.html#id1](https://zonuexe.github.io/phpDocumentor2-ja/references/phpdoc/basic-syntax.html#id1)
- PHPDocのタグについて
    - [https://zonuexe.github.io/phpDocumentor2-ja/references/phpdoc/tags/index.html](https://zonuexe.github.io/phpDocumentor2-ja/references/phpdoc/tags/index.html)