OmniAICreator/Qiita-1.07M huggingface dataset:
"Web"と付く様々な名称説明2

【概要】
----------------------------------------
<b>1."webサーバー"と"アプリケーションサーバー"の違い</b>

<b>2."web"と"インターネット"の違い</b>



1."webサーバー"と"アプリケーションサーバー"の違い
----------------------------------------
大まかに言うと２点あります
❶静的コンテンツを扱うか、動的コンテンスを扱うか
❷データーベースサーバーとやりとりを行うか
です。

<b>”webサーバー”</b>とは、webクライアント(私たちユーザー)に対しての窓口を果たすプログラムです。webクライアントからリクエストを受け取り静的コンテンツを表示する命令を出します。動的コンテンツが必要であればアプリケーションサーバーに命令を出し、その結果を表示します。

<b>”アプリケーションサーバー”</b>とは、webアプリケーションの中核を担うプログラムです。webサーバーから送られてきた命令を受け取り動的コンテンツやデーターベースとやり取りをします。データベースで検索、加工したあとにwebサーバーにレスポンスします。



<br>

2."web"と"インターネット"の違い
----------------------------------------
違いは１つです。
❶その物自体か、繋がりか、
です。


”web"とは、通信プロトコルである"http"を利用し、文章や画像音声動画などのデータを送るシステムです。インターネット上で使用する1つのシステムになります。他にも電子メール、オンラインゲーム等々の様々なものがあります。

”インターネット”とは、”http”を利用し網目上(net)の繋がりをさします。

例えると鉄道になります。
貨物＝webになります。
線路＝インターネット
であり、線路(=インターネット)を辿り、電車によって運ばれる貨物(=web)があらゆる届けられます。ここで言う電車はインフラ設備(ネット回線)になると思います。



<br>

今回は自分がよくわからず似ていた言葉をまとめてみました！
下記の自分の記事では表面上の言葉でしか理解していなかった"web"と付く名称の説明をしています！
<a href="https://qiita.com/taka_no_okapi/items/2105a0a0f9692fbea78c">"Web"と付く様々な名称説明</a>
jQueryを使ってselectから任意の場所のoptionを拾う方法


以下のようなセレクタがあったとします。

```
<select id="sample_selector">
  <option value="0">選択肢1</option>
  <option value="1">選択肢2</option>
  <option value="2">選択肢3</option>
  <option value="3">選択肢4</option>
  <option value="4">選択肢5</option>
</select>
```

ここから「最初の選択肢」や「最後の選択肢」などを引っ張ってきたい時に使えるjQuery構文の紹介。

■最初の選択肢：
```
$('#sample_selector').children('option:first-child').val()
$('#sample_selector').children('option:first-child').text()
```

■最後の選択肢：
```
$('#sample_selector').children('option:last-child').val()
$('#sample_selector').children('option:last-child').text()
```

■N番目の選択肢：
```
$('#sample_selector').children('option:nth-child(3)').val()
$('#sample_selector').children('option:nth-child(3)').text()
```
※この例なら選択肢3が対象となります。

今回は`id`を使いましたが、`name`ならセレクタを`$('select[name=sample_selector]')`に変えることで実現可能。

これを応用すると、select要素の最後のオプションを削除したりすることもできます。
klassってなんやねんってお話

pythonやrubyで、ライブラリのソースコードとか眺めてるとちょいちょいでてくる、
`klass`というキーワード。

感覚的に「英語でこんな単語はないよな・・・」と思いながらも一昔前までスルーしてたのですが、最近またとあるrubyのライブラリの中身をハックじみたことしてたらまたでてきたので調べたのです。


## classって名前がつけられないのでklass

何言ってるかわからないかもしれませんが、例えば

```ruby
def inspect_class(class)
  puts class.class
end

inspect_class('a')
```

というコードを書きたいとき。
これはシンタックスエラーになってしまいます。
なぜなら`class`というキーワードはrubyでは予約語としてすでに定義されているため。

そういうときに、

```ruby
def inspect_class(klass)
  puts klass.class
end

inspect_class('a')
```

こうかくとちゃんと通ります。

イメージ的に、

* クラス名の文字列を受け取る
* クラスそのものを受け取る

といったメソッドの引数名として書くことが多い印象です。

特にrubyのActiveRecordとかでは頻繁にクラス（モデル）を引数に渡したりします。（というかほとんどのフレームワークではそういうことを行いますね）
そんなこんなでruby業界では`klass`っていうキーワードはよく出てくるんだろうなと。

で、それにインスパイアを受けてpythonとかでも`klass`っていうのを使ったりって感じなんですかね。

※こういったケースでは必ず`klass`としなければならないというわけではなく、あくまで **「クラスを渡してるんだな」というのがわかりやすいよう** に書いてるだけです。
人（プロジェクトとか）によっては、`clazz`というのもあるでしょうし、コメントでもいただきましたがpythonでは`cls`と書いたりもします。

## 以上です

ソースの流れを見る限りうっすらとあるいは無意識に「きっとそういうことなんだろう」と思いながらもこうやって疑問に感じたことに興味をもって調べてみるのがスキルを磨く上ではとても大事なんだろうと信じてます。


## 参考文献
https://stackoverflow.com/questions/4299289/what-is-the-difference-between-class-and-klass-in-ruby
[Unity]SDK Managerを手動で入れる

## なにをするのか?

UnityのAndroid SDKがないときに手動でいれる．

## 環境

- Windows 10 Pro
- Unity 2019.4.8f1

## なんでやったか？

2020年11月2日以降にGoogle Playで公開するアプリは Android 10(API 29以上)にする必要があったため．

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/487681/20c2b028-c151-79d6-56f9-66247c9a87c9.png)

## APIレベルを指定する方法

Unityでは，ビルドするときにAPIレベルを指定することができます．

`Build Settings > Player > Identification > Minimum API Level`ここを変えることで変更することができます．

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/487681/af71bee5-d062-8264-185a-a502cf7027ee.png)

ここを指定してビルドしたときに，ビルドが成功すればAPIレベルを指定したものでアプリが作られます．

しかし，次の画像のように，`Android SDK が見つからない`というエラーが出た場合は手動でSDKを入れて対処することができます．

## 手動でAndroid SDKを入れる

### Android SDKを手に入れる

1. Android　Studioを起動し，`Configure > SDKManager` を選択します．([Android Studioを入れていない方]( https://developer.android.com/studio?hl=ja))
2. 手に入れたいAPIレベルを選択します．(今回はAndroid10.0 API Level29)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/487681/47612caf-8fda-5f11-d8e0-3df820b99a77.png)

3. Applyを押して待ちます．
4. インストールが終わったら，Andorid SDK Locationのパスをコピーして，エクスプローラなどで開きます．
5. 開いたフォルダの`platforms`を選択するとandroid-29などインストールしたデータがあると思います．

### UnityにSDKを追加する

まずUnity内のAndroid SDKのパスを調べます．
`Preferences > External Tools > Android`にパスが書いてあるのでコピーしてみてみましょう.

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/487681/48c82af7-5e5e-ca8a-1045-c0d92c0d37ba.png)

コピーしたフォルダの中に`platforms`があるのでその中に行きます．

するとandroid-28(自分の場合)などのフォルダがあると思います．ここにandroid-29がないので，Unityで見つけられていません．

ここにAndroid Studioからインストールしたandroid-29(入れたいフォルダ)を追加するとUnityがそれを使ってビルドすることができるようになります．

## ビルドする

ここまでくると更新したSDKでビルドを行うことができると思います!
Robolectric 4.4 にバージョンアップした時に見るメモ

# Robolectric 4.4 がリリース
2020年8月末に`v4.4`がリリースされたようです。[リリースノート](https://github.com/robolectric/robolectric/releases/tag/robolectric-4.4)
`v4.4`では大きな変更があり、バージョンアップしたらテストの大半が失敗するなんてこともあります。

この記事では、`v4.4`での変更点と、テストが失敗する時の対処方法をまとめていきます。

# Robolectric 4.4 の大きな変更点
[リリースノート](https://github.com/robolectric/robolectric/releases/tag/robolectric-4.4)にある通り、`PAUSED looper mode`がデフォルトになったことが大きな変更点です。
これによって、大半のテストが失敗するようになるかも知れません。

`PAUSED looper mode` の説明は、公式のドキュメントに詳しく書いています。
[PAUSED looper mode の説明](http://robolectric.org/blog/2019/06/04/paused-looper/)
公式ドキュメントを要約すると、以下のようなことが書いています。

* `PAUSED looper mode` では、メインルーパーのキューにあるタスクは明示的に実行する必要がある。
* メインルーパーのキューに未実行のタスクがあってテストが失敗した場合は、エラーメッセージを表示してくれる。

# エラーメッセージが出た時の対処方法
メインルーパーのキューに未実行のタスクがあってテストが失敗した場合は、次のようなエラーメッセージを表示されます。

```
Main looper has queued unexecuted runnables. 
This might be the cause of the test failure. You might need a shadowOf(getMainLooper()).idle() call.
```

このエラーメッセージを解決する方法は2つあります。

* エラーメッセージに従って、適切なタイミングで`shadowOf(getMainLooper()).idle()` を実行する。
* テストクラスに`@LooperMode(LooperMode.Mode.LEGACY)` をつけて、`v4.4`以前と同じモードでテストする。

#### 適切なタイミングで`shadowOf(getMainLooper()).idle()` を実行する
適切なタイミングと言っても、テストコードによってそのタイミングはバラバラです。
基本的には`assertThat(...)`や`verify(...)`など、テストの結果を出す直前に実行するといいと思います。

#### テストクラスに`@LooperMode(LooperMode.Mode.LEGACY)` をつけて、`v4.4`以前と同じモードでテストする
`shadowOf(getMainLooper()).idle()`を実行するタイミングがよくわからず、調査に時間がかかりそうな場合は、この方法で一時的に回避するのも一つの方法だと思います。

# 調査が必要なところ
`Robolectric 4.3.1` 以降では、`Android API 29` でテストする時に次のようなメッセージが表示されます。

```
[Robolectric] WARN: Android SDK 29 requires Java 9 (have Java 8). 
Tests won't be run on SDK 29 unless explicitly requested.
```

つまりは、`Android API 29`でテストするには`Java 9`が必要になるそうです。
[Robolecric 4.3.1のリリースノート](https://github.com/robolectric/robolectric/releases/tag/robolectric-4.3.1)
ですが、AndroidStudioに組み込まれているJDKは`1.8`なので、Java8で実行している場合が多いと思います。
その時に、`Android SDK 28`でテストが動くのか、`Android SDK 29`で動くのかはっきりわかりませんでした。
【備忘録】AmazonLinux 2 にNGINXを入れてHTTPS化する！

#はじめに
Amazon Linux 2(minimal)にNGINXを導入し、SSL化するまでの手順です。
備忘録なので内容は適当です。
(いつもなんか忘れちゃったりするのでね...)
#AMIや設定
ami-05a91a726af26a821 (minimal)
セキュリティグループ 80 443 22とSSH変更後のポート
EIPをとって関連付けて自分のドメインに浸透させておいてね！
#手順
###SSHポート変更
`sudo yum install nano` //初期ではnano入ってない！
`sudo nano /etc/ssh/sshd_config`
コメントアウトし`PORT=XXXX`
`sudo systemctl restart sshd`
一回SSH切断して確認する

###各パッケージ導入
`sudo amazon-linux-extras install nginx1`
`sudo systemctl start nginx`
`sudo systemctl enable nginx`

この時点で外からHTTPでつながるか確認
「うえるかむ とぅ えんじんえっくす おん あまぞんりなっくす！」

`sudo amazon-linux-extras install epel`
`sudo yum install certbot python2-certbot-nginx`

###Certbot起動
Certbotを立ち上げるときはnginxを**立ち上げたままで**
`sudo certbot --nginx`
この後は適当に答える

###定期的に証明書が更新されるようにする
cronなどに`sudo certbot renew`を入れておく
普通に実行すると、実行した日から3か月ぐらい延びる

おつかれさまでした！
Python（axis = 0, axis =1）

今回はPython（numpy ならびに pandas）での axis についてお話していきます。



## ■ axis の基本ルール

axis = 0：行方向（行01から02へ向かう方向）→ 縦方向
axis = 1：列方向（列AからBへ向かう方向）→ 横方向
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/725082/b40fdde9-15cb-dfeb-f41f-85882ee6ae1a.png)

## ■ 実際に確かめてみる
pandas における DataFrame を2つ用意してみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/725082/2e73b698-96cb-f78d-dfa6-f7c7a4d8f4a8.png)
ここで concat（データフレームの連結）を行います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/725082/bb9ab72e-075b-b5a9-d77d-9e6446a770ab.png)
以上のことから、axis の機能について確認することができました。

参考文献：Pythonによるあたらしいデータ分析の教科書
（Python 3 エンジニア認定データ分析試験　の公式テキスト）
URL：https://cbt.odyssey-com.co.jp/pythonic-exam.html
## ■ 例外もある
axis に関しては、numpy の基本演算から pandas のデータフレーム連結まで
上記の基本ルールをもとに処理することができます。

ただし、中には下記のようなものもあります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/725082/0c07f738-d402-605c-2c66-5f21cf75683a.png)
普通に考えると

行（02）であれば、横に削除するから axis = 1
列（B）であれば、縦に削除するから axis = 0

となってしまうかもしれません。

こちらの場合には、あくまでニュアンスにはなりますが

・行方向（縦）に移動して、削除したい行を選択する　→　axis = 0
・列方向（横）に移動して、削除したい列を選択する　→　axis = 1

と考えると良いです。
【Ruby on Rails】Active Recordコールバックについて

# コールバックとは
Railsガイドより
>コールバックとは、オブジェクトのライフサイクル期間における特定の瞬間に呼び出されるメソッドのことです。
>コールバックを利用することで、Active Recordオブジェクトが作成/保存/更新/削除/検証/データベースからの読み込み、などのイベント発生時に常に実行されるコードを書くことができます。

Active Recordのコールバックは、ModelなどのActive Recordオブジェクトが更新されたり削除されたりして状態が変わるときに、その前後でイベントが発生する。
そのイベントで任意の処理を行うことができる。

# コールバックの登録方法
例）会員データが削除された時にログを出力する。

```ruby:app/models/member.rb
class Member < ApplicationRecord
  略
  after_destroy do
    Rails.logger.info "Member is deleted: #{self.attributes}"
  end
end
```

Memberが削除された時に`after_destroy`内の処理が、実行される。

# 種類
以下はActive Recordで利用可能なコールバック例。
before_はトリガーの前、around_はトリガーの前後、after_はトリガーの後の処理を定義できる。
before_validationなら、createとupdateで行われる検証処理の前に発生する。

|コールバックポイント    |create |update |destroy |
|------------------|-------|-------|--------|
|before_validation |○      |○      |×       |
|after_validation  |○      |○      |×       |
|before_save       |○      |○      |×       |
|around_save       |○      |○      |×       |
|after_save        |○      |○      |×       |
|before_create     |○      |×      |×       |
|around_create     |○      |×      |×       |
|after_create      |○      |×      |×       |
|before_update     |○      |○      |×       |
|around_update     |○      |○      |×       |
|after_update      |○      |○      |×       |
|before_destroy    |×      |×      |○       |
|around_destroy    |×      |×      |○       |
|after_destroy     |×      |×      |○       |

# その他の使ってみた例
開発中に削除処理が失敗した時の挙動を確認したい場合があったので、以下のようにすると失敗させることができた。

```ruby:app/models/member.rb
class Member < ApplicationRecord
  略
  before_destroy { throw(:abort) }
end
```

`throw(:abort)`を書くことでコールバックの処理を意図的に停止させることができる。

# コールバックで気をつけるべきこと

- コールバックはmodelで設定されるので、controllerからロジックが見えず、開発者が予期しない処理が実行されることになりかねない。
- コールバックを多用したり複雑な処理を行うとファットモデルになる。

# 参考
- [Railsガイド - Active Record コールバック](https://railsguides.jp/active_record_callbacks.html)
- [パーフェクトRuby on Rails【増補改訂版】](https://gihyo.jp/book/2020/978-4-297-11462-6)
WSL2が「リモートプロシージャー・・・」で起動できない。

僕はWSL2でNvidia-container-toolkitを使用した開発を行いたかったのに最新のアップデートでWSL2自体使えなくなり勉強できなくなりました。他の人はアプデを待つみたいですが僕はリスクを負ってでも使いたいので以下の手順でやります！

#　意地でも使う手順
※ここから先のWindowsのバージョンではセキュリティリスクが存在します。またWindowsがクリーンインストール強制ですのでインストール先のドライブに引き継ぎたいデータなどある方どこかに退避させてから行います。
[こちら](https://www.microsoft.com/en-us/software-download/windowsinsiderpreviewadvanced)からISOイメージを取得します。
![スクリーンショット 2020-09-30 091900.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/243161/6f60a7eb-c607-7f1f-040b-54085dacd30d.png)

Build番号は20201の64bitイメージを取得します。


![スクリーンショット 2020-09-30 092934.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/243161/f7df78c0-2f61-fd95-7886-c59dfd7e2abd.png)

「マウント」します。
![スクリーンショット 2020-09-30 093118.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/243161/f8cb7c77-7e73-5ad8-2278-4828445b9bf9.png)

「setup.exe」を起動する。
後は手順に従いインストールしてください。

インストール後はWindowsのアップデートを行われないようにWindows Updateを殺します。
Windowsの「サービス」から以下の項目を「停止」にする。
![スクリーンショット 2020-09-30 095129.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/243161/901ab78f-3c75-d637-a0d4-a2d7ebc8e27c.png)

これでも勝手にWindows Updateの更新が行われる可能性があるみたいなので、以下のように追加で設定しとく
![スクリーンショット 2020-09-30 095558.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/243161/5f31f938-ce3b-4540-62ff-c3b22cad3494.png)

とりあえず、Devチャネルのアップデートを受け取らないようにしとく。

# Windows10のセットアップ後
[前の記事](https://qiita.com/n-jun-k2/items/d46aab62204a63a2156f)の手順に従いWSL2のセットアップをします。
がUbuntuイメージをセットアップしようとすると
以下のようなメッセージが表示されてセットアップできない為 <br>

```bash
・・・
WslRegisterDistribution failed with error: 0x800706be.
・・・
```

[こちら](https://wslstorestorage.blob.core.windows.net/wslblob/wsl_update_x64.msi)からカーネルのアップデートを行います。 (エラーコードだけ検索すると「WSLが機能をONにしろ」とか「wsl --updateで解決」とか記載ありましたが自分は改善されませんでした。)

# 所感
他の人は全然これに引っかかってないみたいですね。。。でもそういう人たちは大抵GPUを使用していないみたいですね。。。なんでそういう人たちばっかりWSL2の環境が使えて僕みたいにGPUを使いたい人にこんな仕打ちをするんですかね。。。
【ruby】each文の中にeach文を入れると？？

#１、最初に#

現在、プログラミング学習を初めて2ヶ月の者です。
今回は、rubyにてeach文の中にeach文を入れるとどうなるのか、ということについて記載しようと思います。

具体的には、以下の通りです。
①each文の中にeach文を入れると、どんな動きをしているのか
②動きを確認した方法

なお、each文の中にeach文とは、以下を想定しています。

```ruby:sample1.rb
#簡略に記載してます。
array.each do |i|
  array.each do |num|
  end
end
```

私と同じ学習をはじめたばかりの方にとって、何かプラスになれば幸いです！
※初心者投稿の記事になります。もっとこうした方がいいよ！といったことがあれば教えていただきたいです！


#２、どんな動きをしているのか#

今回はわかりやすくするために、先のコードに少し記述を加えた
以下のコードを使用します。

```ruby:sample2.rb
array = [1,2,3]

array.each do |i|
  puts i
  array.each do |num|
    puts num
  end
end
```

では、どんな動きをしているのか結論からお伝えすると、
要素を一つ取り出したときに、要素を全て表示させています。

```terminal:sample3.rb
1 #要素を一つ取り出したとき
1 #要素を全て表示
2 #要素を全て表示
3 #要素を全て表示
2
1
2
3
3
1
2
3
```

もう少し、具体的に記載していきます。

eachメソッドの動きは以下のイメージです。
①配列から要素を一つ取り出す。
②取り出した要素をブロック変数として使う。
③ブロック変数を使ったりしながら、記述された処理をする
④配列内の要素の数だけ処理を繰り返す。


今回は、配列から要素を一つ取り出した後に、またeach文が来ています。

ということは、先のイメージにのっとると、
①arrayから要素である「1」を取り出す。
②1をブロック変数「i」とする
③iを表示させ、arrayの配列に対してeachメソッドを実装する。
　arrayから要素である「1」を取り出す。
　1をブロック変数「num」とする
　numを表示する
　1,2,3まで処理を繰り返す。
④1,2,3まで処理を繰り返す。

となります。
要は一個取り出した→全部取り出す→一個取り出した→全部取り出すを繰り返しているんです。


#３、どうやって動きを確認したのか#

結論から言うと、terminalで実行しながら確認しました。
なんやかんや、terminalで実行してみてどんな結果がでるのか確認する方法が
いまのところは確実かつ早いと思ってます。

最近は主に以下の流れでやっています。
①仮説を立てる
②どうやったら検証できるか考え、vsコードで実際に書いてみる
③terminalで実装してみる
④期待した結果と違うなら、また仮説を立てる。

each文の中にeach文がどんな動きをするのかについても、上記の方法で確認しました。
ポイントは②です。
ここはとにかく余計なことはなくして、シンプルに書くことがポイントかなと思っています。

もともとは、学習中に以下の問題がでてきたことがきっかけです。
「配列内の要素同士が重複していないかどうか確認する。」

このとき、each文の中にeach文をいれて、要素を一つずつ照らし合わせる処理をするのですが、
この処理よくわからんなあ、どうしたらわかるかなあといった感じでした。
そのとき、ふとシンプルに記述して実装してみたらいいんじゃないか？と思いつきました。

今のところは、この方法で処理の流れのイメージがつきやすくなりました。
mongodumpとmongorestoreコマンドの使い方を解説 | サーバーからローカルへのダンプを解説します | mongoのダンプとリストアしてみよう

昔書いた自分の記事がわかりづらい。
[herokuのmongoのダンプリストア](https://qiita.com/rh_/items/46f0b7fde532e3eb295a
)

## やりたいこと

herokuなどのサーバーから僕のパソコンのローカルへmongoのデータをダンプしたい。

## ロジック

まずは、ローカル（mac）などからmongodumpを使って、サーバーのデータをダンプします。mongodumpする時、サーバーのmongoのホストipや、ユーザーネームやパスワードを使ってください。
DockerにPython3.6をインストールする

#概要

Ubuntu(20.04)コンテナに、Python3.6をインストールする。

#スクリプト内容

## 前準備
```
apt-get update
apt-get install wget gcc make zlib1g-dev libssl-dev
```

## Pythonインストール
```
wget https://www.python.org/ftp/python/3.6.1/Python-3.6.1.tgz
tar zxvf Python-3.6.1.tgz 
cd Python-3.6.1
./configure --prefix=/usr/local/python
make && make install
```

##リンク作成
```
ln -s /usr/local/python/bin/python3.6 /usr/local/bin/python
ln -s /usr/local/python/bin/pip3.6 /usr/local/bin/pip
```

##Pip更新
```
pip install --upgrade pip
```

##パッケージ導入
```
pip install setuptools==40.3.0
pip install requests fake_useragent lxml python-amazon-simple-product-api google-compute-engine boto-mws google-api-python-client python-amazon-simple-product-api cssselect pymysql
```

#備考
パッケージ導入は自分用メモ
【PHP】コメントを書く際に注意すること

前回は、[「コメントを書くべきか、書かないべきか」](https://qiita.com/hommayade/items/5435eb187908d5083bce)について述べましたが、
今回は、「コメントを書く際の注意点」について、
自分なりの意見を書いてみたいと思います。

##コメントを書く際に一番重要視していること
大事なことは
####抽象性をなくす
####(可能であれば)具体的にする

です。
可能であれば、と書いたのは
具体性を出すことで冗長になってしまったり、
わかりづらくなってしまったりするのであれば、
それを避けた方がいいからです。

例：

```php5

// 注文商品の中に特定のメーカーがないか確認する
if ($order->hasHogeProducts()) {
	return;
}
```

「確認」はソースを理解するための言葉としては、ほとんど何の意味も持ちません。
どのように確認するのか、確認したら何をするのか、といった情報の方が大切です。

また「特定の」ではなく、具体性を持たせられるなら（短くまとめられるなら）
具体的に書いてしまっていいと思います。

```php5

// 注文商品の中にメーカーがHogeのものがあれば処理しない
if ($order->hasHogeProducts()) {
	return;
}
```
抽象性を消すことができました。
でも、もう少し短く、端的にまとめられる気がします。

```php5

// メーカーがHogeの注文商品があれば処理しない
if ($order->hasHogeProducts()) {
	return;
}
```
**できるだけ具体的に、そして短く。**
これを第一に意識してコメントを書くようにしています。

すべてのコメントに対して、細部ひとつひとつを気にしていたらキリがないです。
だからこそ、意識して最初から精錬されたものが書けるようになることが大事。

そのためには、（時間があるときは）ソースを見直しながら推敲することも必要なのかなと思います。
ローカルMacでJenkinsを立てて、1つjobを作成し、成功するまでの手順

# 概要
ローカルmacにjenkinsを立てて、1つjobを実行し、成功するまでの手順を以下に記載する。

## 手順

### Javaをインストール
* Javaのバージョン確認を以下のコマンドで実行した


```Text
java -version
```

* 既にJavaがインストールされていたので以下のようにJavaのバージョンが表示された

```Text
openjdk version "14.0.2" 2020-07-14
OpenJDK Runtime Environment (build 14.0.2+12-46)
OpenJDK 64-Bit Server VM (build 14.0.2+12-46, mixed mode, sharing)
```

### Homebrewのインストール
* Homebrewのバージョンを以下のコマンドで確認した

```Text
brew -v
```

* 既にHomebrewがインストーリされていたので、以下のようにHomebrewのバージョンが表示された

```Text
Homebrew 2.4.13
Homebrew/homebrew-core (git revision b022a; last commit 2020-08-26)
Homebrew/homebrew-cask (git revision 408c61; last commit 2020-08-26)
```
### Jenkinsインストール
* 以下のコマンドを実行し、jenkinsをインストールした

```Text
brew install jenkins
```
### Jenkins起動
* 以下のコマンドを実行してjenkinsを起動した

```Text
brew services start jenkins
```

### Jenkinsの初期設定
* jenkinsのセットアップ画面にアクセスするためにまず以下のコマンドで設定ファイルを開きポート番号を任意の番号に変更した

```Text
vim /usr/local/Cellar/jenkins/2.258/homebrew.mxcl.jenkins.plist
```

* その後以下のURLへアクセスし,セットアップ画面に遷移することを確認した

```Text
http://localhost:20000/
```

 * 最初の画面でパスワードを求められるが、その画面に記載のパスを開き取得した。以下使用したコマンド

```Text
vim /Users/owner/.jenkins/secrets/initialAdminPassword
```
  
### 新規jobを作成
*  `java_compile_and_run`という名前でフリースタイル・プロジェクトのビルドで作成した

### javaファイルを作成
* コードは以下の通り

```Java
public class Main{
   public static void main(String[] args){
     System.out.println("Hello World");
   }
}
```

### シェルスクリプトの作成
* 以下のようなコードで作成

```Markdown
# ソースコードのある階層へ移動
cd /Users/owner/work/study

# Main.javaファイルをコンパイル
javac Main.java

# プログラムの実行
java Main
```

### ビルドの実行
* 作成したjobを実行した
* 成功時のコンソール出力画面は以下
![スクリーンショット 2020-09-29 15.19.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703051/7d40de63-e471-163a-45a4-e1792432fa16.png)


## 参考サイト
* https://it-rev-env.com/mac_in_jenkins_install/
* https://qiita.com/shirasaya0201/items/863a05117c934856e532
* https://qiita.com/enuesutea/items/45527625362e5f3cc17b
* https://qiita.com/fftcy-sttkm/items/d6616d7b6f8a4a58dfe4
* https://qiita.com/makoto_kw/items/cbe93d4ebbc35f3b43fd
* https://qiita.com/beliefarrow/items/6862b4a6726fc3b24944
* https://eng-entrance.com/jenkins-use
* https://qiita.com/yuuman/items/622347f86e444b8218b3
* https://techacademy.jp/magazine/17597
iOS14のおすすめショートカット3選


iOS 14の[ショートカットアプリ（App Store）](https://apps.apple.com/jp/app/%E3%82%B7%E3%83%A7%E3%83%BC%E3%83%88%E3%82%AB%E3%83%83%E3%83%88/id915249334)をインストールすると、
普段の操作の自動化などが簡単に設定可能です。

今回は特におすすめのレシピを3つご紹介します。

## ① 毎晩のアラームセット、音量調整を自動化 ⏰
アラームの音を大きくするのを忘れており聞こえなかった・・・なんて事態を防ぎます！

#### オートメーション内容
- 毎晩0:00に実行
- アラーム1~3をONに変更
- 音量を55%に設定
- デバイスを振動させる（設定完了の合図）

<br>

## ② Kindleを開き、本の読み上げを開始 📕
ランニング・乗車時などに、ワンタップでKindle本の読み上げを開始することが可能です。

#### ショートカット内容
- Kindleで私の本を開く（最後に開いていた本が開く）
- 3秒待機（アプリ起動後、本が開くまでの待ち時間）
- 画面の読み上げを開始

#### 実行タイミング
- ウィジェットにショートカットを表示しておき、タップするのがおすすめです。

<br>

## ③ Bluetoothイヤホン接続時に音楽を自動再生 ♪
Musicアプリなら、音楽の再生まで可能です。
Spotifyでは、プレイリストを開く部分までが自動化できます。

#### A) Musicアプリで音楽を再生 （オートメーションを作成）
- （事前にイベントのトリガーとなるBluetoothデバイスを接続しておく）
- Bluetoothデバイスを選択
- ミュージックアプリで音楽を再生

#### B) Spotifyを開く （オートメーションを作成）
- （事前にイベントのトリガーとなるBluetoothデバイスを接続しておく）
- Bluetoothデバイスを選択
- Spotifyアプリを開き、再生したい曲/プレイリストで共有リンク（URL）を取得
- Safariで上記のURLを開く


<br>

## おまけ: 出勤/通学前にWi-Fiを"完全に" OFFにする

#### 1. オートメーションを作成
- 通勤/通学する予測時刻に実行
- Wi-Fiをオフにする





## おまけ：オートメーションを設定できるタイミング
退社時に音楽を鳴らすことや、日の出とともに音楽を再生することなども可能です。
iPhoneの背面を2~3回ノックするとショートカットを実行させる方法が特におすすめです。

- Siriから呼び出して実行
- NFCタグにタッチしたとき
- 平日の午前８時
- アラームが止まったとき
- 睡眠準備が開始したとき
- ジムに到着したとき
- 勤務先を出発したとき
- 通勤/通学前
- CarPlay接続時
- ヒロからメールが来たとき
- 母からメッセージが来たとき
- 自宅のWi-Fiに接続したとき
- AirPodsに接続したとき
- Appが開いた／閉じたとき
- 機内モードがONになったとき
- おやすみモードがONになったとき
- 定電力モードがオフになったとき
- バッテリー残量が50%を超えたとき
- 電源に接続したとき


みなさまのおすすめレシピがありましたら、是非教えてください！！
UnityのAndroidビルドでGradleエラー

UnityでAndroidをビルドしようとしたらGradleエラーが発生した原因のメモ

# エラー発生時の画面
![001.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/630678/a4b95b50-1bc8-4443-b237-df099466284f.png)
![002.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/630678/fe075625-d8f6-979f-2c52-bfeac2268e7d.png)
![003.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/630678/c6726770-21be-0f23-af9d-557ce7efee0a.png)

# Editor.log

```
CommandInvokationFailure: Gradle build failed. 
C:/Program Files/Unity/Hub/Editor/2019.4.1f1/Editor/Data/PlaybackEngines/AndroidPlayer\OpenJDK\bin\java.exe -classpath "C:\Program Files\Unity\Hub\Editor\2019.4.1f1\Editor\Data\PlaybackEngines\AndroidPlayer\Tools\gradle\lib\gradle-launcher-5.1.1.jar" org.gradle.launcher.GradleMain "-Dorg.gradle.jvmargs=-Xmx4096m" "assembleRelease"

stderr[

FAILURE: Build failed with an exception.

* Where:
Build file 'C:\develop\Unity\���{��t�H���_\BuildTest\Temp\gradleOut\launcher\build.gradle' line: 3

* What went wrong:
A problem occurred evaluating project ':launcher'.
> Failed to apply plugin [id 'com.android.application']
   > Your project path contains non-ASCII characters. This will most likely cause the build to fail on Windows. Please move your project to a different directory. See http://b.android.com/95744 for details. This warning can be disabled by adding the line 'android.overridePathCheck=true' to gradle.properties file in the project directory.

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Get more help at https://help.gradle.org
```

# 原因
プロジェクトの作成フォルダのパスに日本語が含まれていたためでした。
次のようなパスでプロジェクトを作成しています。

```
C:\develop\Unity\日本語フォルダ\BuildTest
```

日本語を含まないパスでビルドすれば成功しました。
[Android] Cloud FireStore使ってみた

[前回](https://qiita.com/karass/items/3269f4929ada967052d2)はRealtime Databaseを使ってみたが、今回はCloud FireStoreを使ってみる。

# Gradle
```gradle
implementation 'com.google.firebase:firebase-firestore:21.2.1'
```
# 実装
公式Documentを見る感じ、ルールとかの特別な設定とか特になしでできるっぽい？（お試しで使う場合）
Cloud FireStoreはRealtime Databeseとは違うデータモデルです。データを1つの大きなJsonツリーとして保存していたRealtime Databaseとは違って、データをドキュメントのコレクションとして保存します。

コレクション：複数のドキュメントを格納しているもの
ドキュメント： データを格納しているもの

## データ書き込み
`FirebaseFirestore.getInstance()`でデータベースを取得します。取得したデータベースにデータを追加してみる。

```kotlin
val db = FirebaseFirestore.getInstance()
val user = hashMap(
    "name" to "hoge",
    "age" to 12
)
db.collection("users").add(user) // 1
db.collection("users").document("user_id").set(user) //2
```

`users`というコレクションに、ユーザ（ドキュメント）ごとのデータを追加しています。`users`や`user_id`がなくても自動生成してくれるみたいです。一つ目のやり方はdocumentのuser_idを自動で割り振ってれるそうです。

新しいデータを使いすると、上書きされるみたいなので、データの更新をしたい時は

```kotlin
// user(name:"hoge", age:30) -> user(name:"hoge", age:30)
val useUpdate = hashMap("age" to 12)
db.collection("users).document("user_id").set(
    "age" to 30,
    SetOptions.merge()
)
// user(name:"hoge", age:30) -> user(age:30)
db.collection("users).document("user_id").set("age" to 30)
```

と思ったら`update()`というメソッドがあるそうだ

```kotlin
db.collection("users").document("user_id").update("age", 30)
```

## データ読み取り
データの読み取りには`get()`を使用する。

```kotlin
val user1 = User("u1", 12) 
val user2 = User("u2", 22)
val user3 = User("u3", 32)
val user4 = User("u4", 42)

db.collection("users").add(user1)
db.collection("users").add(user2)
db.collection("users").add(user3)
db.collection("users").add(user4)

// [u1, u2, u3, u4]
db.collection("users").get().addOnCompleteListener { doc ->
                Log.d(TAG, "data: ${doc.result?.documents?.map { it["name"] }}")
            }
```

簡単なクエリを叩いてみる。

```kotlin
db.collection("users").whereLessThan("age", 30).get().addOnCompleteListener { doc ->
                Log.d(TAG, "data: ${doc.result?.documents?.map { listOf(it["name"], it["age"]) }}")
            }
````

`whereLessThan()`は、指定した値よりも小さい値にヒットする。他にも色々あるので試してみたい。
IKS用のToolchainのカスタマイズ（Ingressの設定をもとにアプリケーションのURLを表示）

## はじめに
・IKS用のToolchainのカスタマイズのメモ

※基本的なToolchainのカスタマイズ方法は以下を参照
[IBM Cloud Open ToolchainテンプレートをカスタマイズしてGitOps的なToolchainを作ってみた！](https://qiita.com/strada/items/2b7d70d5de677692157f)

※今回のカスタマイズ対象のshell
https://github.com/open-toolchain/commons

## やりたいこと
・IKSのToolchainでDEPLOYステージを実行した際に、ログに表示させるURLをカスタマイズする
・Ingressに定義されたhostnameをもとにアプリケーションのURL（APP_URL）を表示させる

## 対応
・ToolchainのSampleとして提供されている「check_and_deploy_kubectl.sh」を編集する
・kubectlコマンドでingressの情報をもとにFQDNを確認しURLとして設定させる

オリジナルの記述

```bash:
  export APP_URL=http://${IP_ADDR}:${PORT} # using 'export', the env var gets passed to next job in stage
  echo -e "VIEW THE APPLICATION AT: ${APP_URL}"

```

修正後の記述

```bash:
  SVC_NAME="$(kubectl get services -o json -n ${CLUSTER_NAMESPACE} | jq -r '.items[] | select(.spec.selector.app == "'"${DEPLOYMENT_NAME}"'") | .metadata.name')"
  HOST_NAME="$(kubectl get ingress -o json  -n ${CLUSTER_NAMESPACE}  | jq -r '.items[].spec.rules[] | select(.http.paths[].backend.serviceName == "'"${SVC_NAME}"'") | .host')"
  export APP_URL=https://"${HOST_NAME}"
  echo "${APP_URL}"
  echo -e "VIEW THE APPLICATION AT: ${APP_URL}"
```

## 注意事項

- APP_URLは「check_health.sh」(Liveness/Readiness Probe)で利用するURLとしても利用される

GET/POSTなどの処理の実装に応じて、「check_health.sh」の内容をカスタマイズする必要が出てくる。
（具体的には下記の処理で、GETの場合は「-X GET」オプションを付けるなどのカスタマイズが必要。）

```bash:
if [ "$(curl -Is ${LIVENESS_PROBE_URL} --connect-timeout 3 --max-time 5 --retry 2 --retry-max-time 30 | head -n 1 | grep 200)" != "" ];
```


以上。
zapierでGoogleカレンダーとTwilioを連携し、スケジュール更新を電話で伝えるシステムを作ったよ



###これからはDXだ！！
と最近よく聞きますが、我が国の多くの会社では、<B>１に対面、２に対面、３、４がなくて５に電話</B>という感じで、DXというものは何か違う世界の話のように思えます。

Slackなどのチャットツールは軽薄、遊んでるように見えます。上司にチャットやメールで用件を済ますのは失礼にあたります。直接会いにいって話すか、どうしても会えない場合は電話をかけるのが社会人のマナーです。
社内イントラネット上の共用の上司のスケジュールを更新しても、チャットやメールで済ますのは言語道断です。

それを解決するソリューションとして、
<B>「スケジュールが更新されたら電話で教えてもらえるシステム」</B>
をご提案いたします。


##使うサービス

[zapier](https://zapier.com/)で、GoogleカレンダーとTwilioを連携します。

##システム構成図
[![Image from Gyazo](https://i.gyazo.com/dd008ea37eb363b644e82d69ce041809.png)](https://gyazo.com/dd008ea37eb363b644e82d69ce041809)


##手順

[zapier](https://zapier.com/)のウェブサイトを開き、
そこで、GoogleカレンダーとTwilioを選択します。

私は今回初めてzapierを使いましたが、使い方はネットで検索しなくても、zapierのサイトのいわれるとおりに進めば、ほぼストレスなく、やろうと思ってたとおりのことができました。

Google先生に頼ったのは、日本語対応だけでした。
それだけ、UI/UXが素晴らしいです。



[![Image from Gyazo](https://i.gyazo.com/ffee6df71e420faf736a55292e4d563f.png)](https://gyazo.com/ffee6df71e420faf736a55292e4d563f)


[![Image from Gyazo](https://i.gyazo.com/f24cc620932b38b72725a519608d8276.png)](https://gyazo.com/f24cc620932b38b72725a519608d8276)

###音声を日本語対応に
英語だけかと思ってたら、こちらのサイトを見ると日本語でのセッティングの方法が掲載されてました。ありがとうございました。

[メールを送ると即時電話通報するZap(Email by Zapier)](https://qiita.com/will_meaning/items/0621e13e7c93728abe41)

音声を日本語にする場合は、zapierのTwillio設定画面で
<B>Voice を　alice</B>
<B>Language ja-JN</B>
にします。
[![Image from Gyazo](https://i.gyazo.com/adf88002a2055870605c1597bbded15f.png)](https://gyazo.com/adf88002a2055870605c1597bbded15f)

##完成
はい、もうこれでGoogleカレンダーを更新すると自動的に上司に電話がかかるようになりました。
デジタルなんか関係ない職場でも、これならなんとかOKもらえるかもしれませんね。
（あ！　そもそもそんな職場がGoogleカレンダーで共有するわけないですが・・・）



##注意点
Twilioは無料ではありません。
最低２０００円をチャージして、そこから電話料を払います。
ゼロ円になったら使用できなくなり追加購入する必要があります。（自動購入することもできます）
このへんはうろ覚えで書いてるので、正式にはご自分でお調べください。
Flutter web のビルドで generated_plugin_regstrant.dart が無いと怒られる

Flutter Web のアプリを `flutter run` すると、「generated_plugin_registrant.dart が存在しない」というエラーが出ることがあります。

```
org-dartlang-app:/web_entrypoint.dart:8:8: Error: Error when reading 'lib/generated_plugin_registrant.dart': No such file or directory
import 'package:my_package_name/generated_plugin_registrant.dart';
       ^                                                                
org-dartlang-app:/web_entrypoint.dart:11:3: Error: Method not found: 'registerPlugins'.
  registerPlugins(webPluginRegistry);                                   
  ^^^^^^^^^^^^^^^         
```

これは Flutter アプリのソースのルートに ./web フォルダが作成されていないと発生するエラーのようです。

./web フォルダと ./web/index.html がないとそもそも Flutter web として実行できないわけですが、フォルダが無いというエラーより前に、 generated_plugin_registrant.dart が無いというエラーが出るので、ちょっと原因が分かりにくいですね。

私は Flutter web 未対応のプラグインを web 対応に更新した際に、example アプリ用の web フォルダの作成を完全に忘れていたので、30分ほどハマってしまいました。
aws設定メモ（インスタンスへのElastic IP アドレスを割り当て）

Amazon Elastic Compute Cloud (Amazon EC2) インスタンスに関連付けられた自動割り当てパブリック IP アドレスが、インスタンスを停止して起動するたびに変わります。このような場合でも変わらない静的なパブリック IP アドレスを、EC2 インスタンスに割り当てるにはどうすればよいですか? 
https://aws.amazon.com/jp/premiumsupport/knowledge-center/ec2-associate-static-public-ip/
下記サイトのElastic IP アドレスを割り当てをクリック
<IMG src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101462/ec56f65d-4e45-27b5-41c6-f214eaa8aa64.png">
更に下記サイトの割り当てをクリック
<IMG src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101462/010de26f-3850-2ae1-c05b-e96c61e4b002.png" width="50%">
下記のように割当られたIPが表示される。
<IMG src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101462/aa32a21b-c44a-83f0-18e5-ed34b60098af.png" width="60%">
次に割り当てられたIPアドレスをクリックする下記画面表示されElastic IP アドレス関連付けをクリック
<IMG src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101462/03a1c0cb-b0b7-4b31-11de-cc5206df6371.png" width="60%">
インスタンにカーソル合わせ登録済みのインスタンスを選択し関連付けるをクリック
<IMG src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101462/566ff012-f875-4a61-0407-5e80313fc91f.png" width="60%">
下記のようにインスタンス一覧のElastic IP欄にIPアドレスが表示され、パブリックIPとElastic IPが同一になる。
![スクリーンショット 2020-09-30 5.54.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101462/38c32058-eb31-c903-b44e-856d86b4cd65.png)
[Android] テスト時のSimpleDateFormatの取り扱いで気をつけること [メモ]

日付を取り扱う時にSimpleDateFormatを使っていたのですが、テストする時にローカルでは通っているのにGitHubActionのUnitTestでエラーを吐いてしまう。

# 原因
テストしたいViewModelとTestコードはこちら

```kotlin
class MainViewModel : ViewModel() {

    fun getDate(): String {
        val sdf = SimpleDateFormat("yyyyMMdd HH:mm", Locale.JAPAN).apply {
            this.timeZone = TimeZone.getTimeZone("Asia/Tokyo")
        }
        return sdf.format(DateUtil.getCurrentDate())
    }
}

object DateUtil {
    fun getCurrentDate() = Date()
}
```

```kotlin
class ExampleUnitTest {

    @Test
    fun getDate() {
        val mainViewModel = MainViewModel()
        mockkObject(DateUtil)

        val sdf = SimpleDateFormat("yyyyMMdd HH:mm", Locale.JAPAN)
        val currentDate = Date()
        every { DateUtil.getCurrentDate() } returns currentDate

        assertEquals(sdf.format(currentDate), mainViewModel.getDate())
    }
}
```

こちらのテストはローカルでは通るのだが、、、
<img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/289190/d8953891-7535-aea8-8d07-abda559d67bb.png  width=150>

GitHub Actionではエラーを吐く。

分かる人はすぐに気づくと思いますが、

```kotlin
val sdf = SimpleDateFormat("yyyyMMdd HH:mm", Locale.JAPAN)
```

こいつが悪さをしていた。`SimpleDateFormat`は`Locale`を指定していてもTimeZoneはデフォルトが使用されるため、テスト対象のMainViewModelでは、明示的にTimeZoneを指定していたが、TestコードにTimeZoneを書き忘れていたのが原因みたいです。GitHub ActionのTimeZoneがUTCなので、

```kotlin
val sdf = SimpleDateFormat("yyyyMMdd HH:mm", Locale.JAPAN).apply {
            this.timeZone = TimeZone.getTimeZone("Asia/Tokyo")
        }
```
しっかりTimeZoneを指定してあげましょう。（ローカルのTimeZoneってどうやって決まってるんだろう）
WSL2 上の neovim + vimtex + SumatraPDF で inverse search

# やること

- WSL2 の neovim + vimtex
- chocolatey でインストールした SumatraPDF

で，inverse search を実現します．WSL2 のディストリビューションは Ubuntu，TeX 環境は，WSL2 で texlive をインストールしています．synctex の設定については他のページを参照してください．


# neovim (vimtex) の設定

inverse-search とは関係ありませんが，vimtex のプレビュアー設定はこのようにしています．

```init.vim
let g:vimtex_view_general_viewer  = '/mnt/c/Users/ユーザ名/AppData/Local/SumatraPDF/SumatraPDF.exe'
let g:vimtex_view_general_options = '-reuse-instance -forward-search @tex @line @pdf'
let g:vimtex_view_general_options_latexmk = '-reuse-instance'
```

SumatraPDF は起動しますが，forward-search ができません．困っていないので，原因は調べていません．


# neovim-remote のインストール

```
$ pip3 install neovim-remote
```

環境変数 NVIM_LISTEN_ADDRESS にソケット用のパスを設定して，nvim を動かすようにします．

``` .zshenv
export NVIM_LISTEN_ADDRESS=/tmp/nvimsocket
```


# vbs スクリプトの作成

wsl コマンドはウィンドウが開いてしまうので，vbs 経由で動くようにします．
servername には，NVIM_LISTEN_ADDRESS で設定パスを指定します．

``` C:\bin\pdfinv.vbs
Set ws = CreateObject("Wscript.Shell")
n = WScript.Arguments(0)
ws.run "wsl /home/ユーザ名/.local/bin/nvr --servername /tmp/nvimsocket --remote-send "":" & n & "<CR>zz""", vbhide
```

# SumatraPDF の設定

SumatraPDF の [☰] > [設定] > [オプション] の逆順検索コマンドラインの設定に，以下のように書きます．

```
wscript C:\bin\pdfinv.vbs %l
```

これで，inverse search ができています．
【Mac / SQLクライアント】Sequel Pro → Sequel Pro Nightly build → Sequel Aceへ

## Sequel Pro
![sequel-pro.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/120764/c7f78d2e-8df2-f17e-3fcf-850d83479100.png)


macOS Catalinaにしてから、クラッシュが相次ぐSequel Proですが、
他に代替SQLクライアントもなく、アプリのアイコンも気に入っていたので、ずっと使っていました。
※ プロジェクト自体は終了しておらず、今後も継続開発は行っていく模様です。（2020/09/30 現在）

公式サイト
https://sequelpro.com/

Github
https://github.com/sequelpro/sequelpro

## Sequel Pro - Nightly build
![sequel-pro-nightly-buildのコピー.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/120764/bcfb7ce8-030f-e0d2-c434-7f13c50f18bd.png)

Sequel Proのテストビルドである、Nightly buildも使っていたこともありました。
ウィンドウを閉じたりした場合に、クラッシュするバグは無くなっていて良かったのですが、
フィルターだったり、外部キーを使用した別テーブルへのリンクが無いなど、メインで使っていくにはという感じでした。

公式ページ
https://sequelpro.com/test-builds

## Sequel Ace
![sequel-aceのコピー.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/120764/cb6c0885-95a4-3743-752f-03e943245cb6.png)

Sequel ProからForkして開発されているのが、**Sequel Ace**です。
フォークされて開発されているので、ほぼSequel Proと同様に使うことができます。

**※ 2020/09/30 現在**
- 日本語未対応
- Export出来ない


Github
https://github.com/Sequel-Ace/Sequel-Ace

### 互換性
**OS X Yosemite以降**であれば使用できるようです。

macOS >= 10.10
MySQL >= 5.6
MariaDB
参照：https://github.com/Sequel-Ace/Sequel-Ace#compatibility

### インストール方法
CLIを使用するか、Mac App Storeにもあるので、そちらからDLできます。

#### MAS CLI
```shell
$ mas install 1518036000 # Sequel Ace
```

### Homebrew
```shell
$ brew cask install sequel-ace
```

### Mac App Store
https://apps.apple.com/us/app/sequel-ace/id1518036000?ls=1

### 外観
データベース接続画面
ほぼ、Sequel Proのままです。
![スクリーンショット 2020-09-30 3.26.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/120764/522c0813-22a9-1d40-9a4a-96e1520877e1.png)

#### ダークモード対応
また、ダークモードに対応しており、環境設定（Preferences）から変更できます。
デフォルトでは、Macの設定に依存する形になっています。
`⌘ + ,`
![スクリーンショット 2020-09-30 3.24.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/120764/017133c4-2668-2658-4a65-6e63fa8376f8.png)

ダークモード
![スクリーンショット 2020-09-30 3.20.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/120764/f4b0b922-d878-61d7-b261-58579c704bdd.png)
【ミライトデザイン社内勉強会#4】DOM・仮想DOMとは

## DOMとは

- Document Object Model
- 「jsからhtmlを操作するためのもの」ってどこかに書いてあったのを読んだことがある
    - これはあんまりよく無い説明かも
- htmlやXML等の文書の中のツリーの構造のこと
    - htmlには限らない
- htmlからDOMを生成して、DOMを元に画面を描画している。
    - jQueryはそのDOMを直接操作している。
    - DOMの値を変更すると表示されている見た目も変更される。

## 仮想DOMとは

- メリットは？
    - jsで直接DOMを操作するのか面倒くさかった　→　jQueyが出てきた
        - でも、jQueryだと複雑なviewを作ろうとしたらコードが破綻しやすかった
        - 仮装DOMを使うと、コードの可読性も上がるし、パフォーマンスも上がる
    - DOMを直接いじらないので、描画したいタイミングで描画できる
        - jQueryだとDOMを触ったタイミングでその都度（リアルタイムで）描画が更新される。
            - 書き方にもよるけど、仮装DOMだとそれが簡単にできる。
            - 仮装DOMなら完成してから描画することができる
         

## Reactのチュートリアルをやってみる
[チュートリアル：React の導入 – React](https://ja.reactjs.org/tutorial/tutorial.html)

## 参考記事 

- [仮想DOMってすげーんだぜ！ \- Qiita](https://qiita.com/Gsann/items/e8ca34f5b7bdab4fde11)
【RHEL8/CentOS8】NTPは死んだ。時代はChrony。

# 茶番
ワイ「OSインスコ完了...」
ワイ「`ntpdate ntp.nict.jp`」

```
bash: ntpdate: コマンドが見つかりませんでした...
```

ワイ「え？」
ワイ「`sudo yum install ntp`(脳死)」

```
エラー: 表示するための一致したパッケージはありません
```

は？「ワイ」

# 結論：Chronyを使おう
RHEL8及びCentOS8から`NTP`がプリインストールされなくなり、同機能は`Chrony`に完全移行しました。
ですので、`ntpdate`コマンドはもう使えません。R.I.P

~~元職場が極めてレガシーなオンプレ環境でしたので知りませんでした。浦島太郎状態です。(言い訳)~~

「linux 時刻同期」とかでggると`ntpdate`コマンドでの設定方法ばかりトップに出てくるので、ちょっとハマりました。

# 使い方
OSインスコ後のプレーンな環境で時刻同期が出来ればそれでいいので、この記事ではそこまでの流れだけ書きます。
所謂、「強制的に時刻同期をする手順」になります。

詳細な使い方は以下が参考になります。または[Google大先生](https://www.google.co.jp/)でお尋ねください。
>[RedHat CustomerPortal - 18.2. Chronyの概要および設定](https://access.redhat.com/documentation/ja-jp/red_hat_enterprise_linux/7/html/system_administrators_guide/sect-understanding_chrony_and-its_configuration)

>[NTPに変わるChronyって何が変わったの？](https://qiita.com/legitwhiz/items/5b2d56ce9a0ee29a24f9)

### ①存在確認
コマンドはchrony"**c**"なので気をつけて。

```
$ chronyc --version
```
ない → ②
ある → ③

### ②インストール
```
# yum -y install chrony
```

### ③起動状態確認
デーモンはchrony"**d**"なので気をつけて。

```
# systemctl status chronyd
```
`Active:`の後ろが、
inactive (dead) → ④
active (running) → ⑤

### ④起動する
```
# systemctl start chronyd
```


### ⑤時間合わせる
```
# chronyc makestep
```
「200 OK」 → ⑥
それ以外 ↓

- 権限を見直す。
- 起動状態を再度確認する。
- 放り投げて寝る

### ⑥時刻を確認する
正しい時刻になっていれば、完了です。

```
$ date
2020年  9月 30日 水曜日 01:47:50 JST
```
TypeScript 4.1 の Template Literal Types がやばい

TS 4.1 の Template Literal Types を使うと、文字列を解釈して型定義ができる。文字列は変数化でき、テンプレートリテラルで埋め込める。

```typescript
type Hello = 'Hello'
type World = 'World'

// Template Literal を埋め込める
type HelloWorld = `${Hello}, ${World}`
// => type 'Hello, World'

// 引数に取ることもできる
type Concat<T extends string, S extends string> = `${T}, ${S}`
type HelloWorld = Concat<'Hello', 'World'> 
// => type 'Hello, World'
```

やばい。革命的だ。

更にやばいのが、 `infer` を使って任意の文字列にマッチ、みたいなことができる！

```typescript
type GetWorld<T extends string> = T extends `Hello, ${infer World}` ? World : never
type World = GetWorld<`Hello, World`>
// => type 'World'
```

`infer` は一つのリテラルで複数回使えるので、簡単な SQL くらいならすぐパースできる。以下は型レベルでSQLがパースできる例。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103885/f10fd773-3dfd-7e2a-3365-ed841a7bfbd7.png)

`infer` と再帰的な型定義を使えば、ツリー構造を型レベルでパースしたり、文字列を分割することも可能。（後で例を書きます）

既に SQL から型定義を作成したり、 GraphQL の Resolver の型定義をするライブラリが作成されつつある。

- https://github.com/codemix/ts-sql
- https://github.com/dotansimha/graphql-typed-ast

２年前、 [typed-graphqlify](https://github.com/acro5piano/typed-graphqlify) という OSS を作ったが、Template Literal Types を使えば、もっとすごいものが作れる。

試しに GraphQL から型定義を自動作成するものを、ちょっと書いてみた。

```typescript
/*
 * TypeScript 4.1
 */

type GetSchema<Schema> =
  Schema extends `type Query { ${infer RootQueryName}: String! }`
    ? { [k in RootQueryName]: string }
    : never

type QueryBuilder<Schema, Query> = Query extends `{ ${infer RootQueryName} }`
  ? RootQueryName extends keyof Schema ? { [k in RootQueryName]: Schema[RootQueryName] } : never
  : never

type Schema = GetSchema<`type Query { hello: String! }`>

type ExpectQueryType = QueryBuilder<Schema, '{ hello }'> // => { hello: string }
type ExpectNever = QueryBuilder<Schema, '{ foo }'> // => never
```

見た目はえげつないけど、ちゃんと動いている。がんばればライブラリ化できるかもしれない。 [typed-graphqlify](https://github.com/acro5piano/typed-graphqlify) の次期バージョンとして作ろう。

ただ、 Fragment とか面倒そうｗ
頑張る。

[追記]
実装のヒントを貰うために、 graphql-typed-ast に Issue を立てた。良い人だ。
https://github.com/dotansimha/graphql-typed-ast/issues/2
[/追記]
【Salesforce】Apexの日付比較メソッドの使い方と注意点

## SalesforceのApexで日付を比較するには
DateクラスのisSameDayメソッドを使用する。

■Date クラス
https://developer.salesforce.com/docs/atlas.ja-jp.226.0.apexcode.meta/apexcode/apex_methods_system_date.htm

## isSameDayメソッドの使い方
Date型の変数を2つ作成し、isSameDayメソッドをコールすれば比較結果が返ってくる。

``` apex
Date date1 = Date.newInstance(2020, 1, 1);
Date date2 = Date.newInstance(2020, 1, 1);
Boolean result = date1.isSameDay(date2);
System.debug('result: ' + result);
```

## isSameDayメソッドの引数にnullを指定してみた
isSameDayメソッドの引数にnullを指定するとどうなるか検証してみた。
結果はfalseになりそうだが、エラーであった。

``` apex
Date date1 = Date.newInstance(2020, 1, 1);
Date date2 = null;
Boolean result = date1.isSameDay(date2);
System.debug('result: ' + result);

/* エラー内容
Line: 3, Column: 1
System.NullPointerException: Argument cannot be null.
*/
```

## まとめ
isSameDayメソッドにnullを指定できないことが分かった。
処理によってはnullが引数になるパターンも発生しうるため、nullの場合はメソッドをコールしないように注意する必要がある。
品質機能更新プログラム（QU）の削除スクリプト

#コマンドベースでWindowsの品質更新プログラム(QU)をアンインストールする方法
品質更新プログラム(以下QU)の削除には、wusaコマンドまたはdismコマンドを用いることが考えられます。

wusaコマンドのほうが簡単にkb番号を指定できて使いやすかったのですが、
この記事を記載した時点で/quietオプションと/uninstallオプションがなぜか両立できなかったため、
dismコマンドオプションを使用してスクリプトを作成します。

以下は自分で作成したスクリプトの備忘録になります。

#スクリプト内容
まずは変数定義部分になります。

削除するQU番号を引数で指定しています。
一例をあげると"18362.1082"といったOSビルド番号を指定します。
ここで指定した値が削除される処理となります。

その他はログファイル用の日付などを定義しています。

```
$OSbuildData_Current=$Args[0]
$ResultPackage_bef1=0
$ResultPackage_aft1=0
$QuPackage_work1=""
$QuPackage=""
$ReturnCheck=1
$ExecuteCheck=$true
$ExecutionDate=Get-Date -Format "yyyyMMddHHmm"
$CheckDate=Get-Date -Format "yyyyMMdd"
```
ログ出力を定義します。

```
Start-Transcript C:\Windows\QU_uninstall_$ExecutionDate.txt
```

引数で渡したOSビルド番号のQUが適用されているかをチェックします。
チェックにもdismコマンドが使用でき、/get-packagesオプションを指定します。/onlineオプションは情報取得先を今のOS領域に定めてます。
このスクリプトでは最初に引数で渡したQUのみを削除したいので、それが存在しない場合には処理を実行しません。

```
$ResultPackage_bef1=@(dism /get-packages /online | Where-Object { $_ -match $OSbuildData_Current -and $_ -match "RollupFix" }).count

if($ResultPackage_bef1 -ne $ReturnCheck){
    echo "実行しない"
    $ExecuteCheck=$false
}
```

dismコマンドで削除パッケージを指定内容は、get-packagesで取得できるパッケージ名をしっかり指定してあげる必要があります。
get-packagesで取得したデータはsplitで不要な内容を削除し、変数にパッケージ名のみが入るようにしてます。

またget-packagesでQUは、"Package_for_RollupFix~~~"と表示されるようです。
なので文字列抽出条件にそちらも入れておきます。

```
if($ExecuteCheck){
    ##dismで指定するパッケージIDを特定
    $QuPackage_work1=dism /get-packages /online /format:table |Select-String $OSbuildData_Current |Select-String "RollupFix"
    ##スペース区切りで不要領域を除く
    $QuPackage_work2=$QuPackage_work1 -split " "

    $QuPackage=$QuPackage_work2[0]
```
ここが実際にQUがアンインストールされる個所になります。
/remove-package・・・パッケージを削除することを定義
/PackageName:・・・削除するパッケージ名を指定する。
この後に続く値は"Package_for_RollupFix~31bf3856ad364e35~amd64~~18362.1082.1.10"のような具体的なパッケージ名となります。
/online・・・現在のOS領域から削除します。
/Quiet・・・プロンプトを出さずに実行します。
/NoRestart・・・勝手に再起動されないようにします。

    echo $QuPackage
    echo "アンインストール処理開始"
    dism /remove-package /PackageName:$QuPackage /online /Quiet /NoRestart
ここまでif文の中で書いているので、elseも記載しておきます。

```
}else{
    "実行条件に合致しないため、実行しない"
}
```
削除後のチェックを行います。
正しくアンインストールされていれば、最初に検索した条件ではHITしないはずなので、その件数で比較を行います。
ちなみにdismコマンドを用いて削除すると、消したRollupFixが表示されなくなり、ひとつ前のRollupFixがインストール保留中で待機しています。

```
$ResultPackage_aft1=@(dism /get-packages /online | Where-Object { $_ -match $OSbuildData_Current -and $_ -match "RollupFix" }).count

if($ResultPackage_aft1 -lt $ReturnCheck){
    echo "実施_削除成功"
}else{
    echo "実施_削除失敗"
}
```

ログの出力を止めます。

```
Stop-Transcript
```

#総括
このスクリプトを作成する前にWEBで調査していたら、
たいていwusaコマンドに/uninstall /quiet /norestartオプションをつけて、コマンドベースの削除を行っておりました。

しかし自分の環境ではなぜか動かず。。最近仕様が変わったのかもしれません。
同じような環境にいる方は参考にしてください。
ReportingAPI + Cloud Functionsで各ページのアクセス数を取得

#目的
ReportingAPI + Cloud Functionsを使用して各ページのアクセス数を取得する方法を記載します。
Google Analyticsのデータ取得にはReporting API v4（google-api-python-client）を使用します。

#環境
google-api-python-client: 1.12.2
oauth2client: 4.1.3

#流れ
1．Google API Consoleの設定
2．Google Analyticsの設定
3．Analyticsデータの出力


##1．Google API Consoleの設定
<a href='https://console.cloud.google.com/projectselector2/iam-admin/serviceaccounts?hl=ja&supportedpurview=project'>Google API Console</a>からReporting API v4を有効にします。
この際に秘密鍵をダウンロードしサービスアカウントのメールアドレスを控えておきます。

```
quickstart@PROJECT-ID.iam.gserviceaccount.com
```

##2．Google Analyticsの設定
<a href="https://analytics.google.com/analytics/web/">Google Analytics</a>の[管理]>[ユーザー管理を表示]>[ユーザー追加]から先ほどのメールアドレスを追加します。


##3．Analyticsデータの出力
GCPのCloud Functionsから関数を作成し、以下のソースを張り付ければ完了
※VIEW_ID、KEY_FILE_LOCATIONの値は自身のものに置き換えてください。

```python:main.py
from apiclient.discovery import build
from oauth2client.service_account import ServiceAccountCredentials

SCOPES = ['https://www.googleapis.com/auth/analytics.readonly']
KEY_FILE_LOCATION = 'Jsonファイルのパス'
VIEW_ID = 'VIEW_ID'

def initialize_analyticsreporting():
    credentials = ServiceAccountCredentials.from_json_keyfile_name(
        KEY_FILE_LOCATION, SCOPES)
    analytics = build('analyticsreporting', 'v4', credentials=credentials)
    return analytics

def get_report(analytics):
    return analytics.reports().batchGet(
        body={
            'reportRequests': [
                {
                    'viewId': VIEW_ID,
                    'dateRanges': [{'startDate': '7daysAgo', 'endDate': 'today'}],
                    'metrics': [{'expression': 'ga:uniquePageviews'}],
                    'dimensions': [{'name': 'ga:pagePath'}]
                }]
        }
    ).execute()

def print_response(response):
    for report in response.get('reports', []):
        columnHeader = report.get('columnHeader', {})
        dimensionHeaders = columnHeader.get('dimensions', [])
        metricHeaders = columnHeader.get(
            'metricHeader', {}).get('metricHeaderEntries', [])

        for row in report.get('data', {}).get('rows', []):
            dimensions = row.get('dimensions', [])
            dateRangeValues = row.get('metrics', [])

            for i, values in enumerate(dateRangeValues):
                for dimension, value in zip(dimensions, values.get('values')):
                    print(dimension + ':' + value)


def main(event, context):
    analytics = initialize_analyticsreporting()
    response = get_report(analytics)
    print_response(response)
```

```txt:requirements.txt
google-api-python-client==1.12.2
oauth2client==4.1.3
```

#感想
PythonもGCPもAnalyticsも初心者だったのでいろいろ勉強になりました
今回はブログにランキング機能が欲しくて作ってみましたがFireStoreにアクセス数カウントしてくのとどれくらい料金に差が出るんですかね:thinking:
ReportingAPIやる前にAnalyticsの知識付けたほうがよさそうですね(笑)
Azure の従量課金でコストオーバーしないためにアラートを設定する

# この記事について

この記事では、**Azure サブスクリプションの課金アラート**を設定する方法について解説します。

Microsoft Azure をはじめ、クラウドの従量課金(Pay As You Go)は、使った分だけ課金というメリットもある反面、`リソースを放置していると右肩上がりに課金が行われてしまう`というデメリットもあります。

サーバレスや FaaS、無償枠があるサービスのみを使用していれば問題ないかもしれませんが、勉強や検証の内容によっては、身銭を切る場合もあるでしょう。
そんな時に、不要となったリソースの削除を忘れてしまい、**1 ヶ月後に請求額がとんでもないことになっていた** となっては大変です。

Azure を従量課金などで使用する場合は、きちんと課金アラートについても設定しておきます。


# アラート設定方法

Azure ポータルにログインし、[コストの管理と請求] を選択します。

![スクリーンショット 2020-09-30 0.34.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/348116/4ec797c4-5df1-0155-e9db-3e137d573798.png)

![スクリーンショット 2020-09-30 0.35.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/348116/67d6d951-cc23-9be1-30da-0944b2263c05.png)

画面左部のメニューから [コスト管理] を選択します。
※画像のように、サブスクリプションの一覧が表示される場合は、課金アラートを設定したいサブスクリプションを選択します。

![スクリーンショット 2020-09-30 0.36.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/348116/6f87bbc2-7d16-8509-ea9a-c62a35471b23.png)

コスト管理の画面が表示されるので、画面左部のメニューから \[コスト管理\] -> \[コストのアラート\] を選択します。

![スクリーンショット 2020-09-30 0.48.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/348116/350d5049-1ca2-eabb-a45e-0937374852d0.png)

\[+追加\] ボタンを選択します。


![スクリーンショット 2020-09-30 0.49.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/348116/305c2728-280b-c8c0-1258-e822f2abe89f.png)

予算を作成する画面が表示されます。

![スクリーンショット 2020-09-30 0.54.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/348116/c49cf8a9-5998-c026-f707-52153047b870.png)

[予算の詳細] 欄にて、以下を設定します。従量課金の利用であれば、リセット期間は **月単位** で設定すると思います。
※[スコープの変更] ボタンにて、課金アラート適用対象とするサブスクリプションを変更することも可能です。

- **名前**: 課金アラートの名前。サブスクリプション内で一意である必要がある。
- **リセット期間**: 課金アラートで監視する総課金額の値をリセットする間隔。
    - 月単位
    - 四半期ごと
    - 毎年
- **作成日**: 課金アラートによる監視の開始日
- **有効期限**: 課金アラートの有効期限。この期限を迎えるまで、課金アラートは有効になります。

\[予算額\]欄にて、予算額の閾値を入力します。(私の場合は大体 10,000 円で設定しています。)

- **金額(￥)**: 課金アラートの閾値。この値は課金アラートを行うための閾値となる予算金額であり、この金額を超えてもサービスを使用し続ける限り課金は発生します。

設定が終わったら、\[次へ\] を選択します。

![スクリーンショット 2020-09-30 1.05.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/348116/6e1edaf3-5993-cd55-0789-fad227fe0d3a.png)

[警告条件]欄にて、予算の割合に対するアラートの警告条件を設定します。
（私の場合は、大体 50 % / 75 % / 90% / 100 % の 4 つで設定しています。）

- **予算の割合**: 0.01 〜 1000 までの間の数値を設定します。※1000 % の課金アラートとか身震いしますね・・・
- **アクショングループ**: アクショングループを設定済みの場合は設定しても OK です。[なし] でも問題ありません。

\[アラートの受信者\(メール\)\] 欄にて、アラートを送信するメールアドレスを指定します。
サブスクリプションにログインしているメールアドレスなどを設定しましょう。

設定が終わったら、\[作成\] を選択し、課金アラートを作成します。
作成が完了したら、画面左部のメニューから \[予算\] を選択します。
先ほど作成したアラート設定が表示されるはずです。
※この画面にある \[+追加\] ボタンからでも、先ほどと同様、課金アラートを設定することが可能です。

![スクリーンショット 2020-09-30 1.20.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/348116/2bb39b3d-ae93-a44f-2314-bb101086eb3c.png)

また、すでに発生したアラートについては、先ほどの [コストのアラート] メニューにて確認が可能となっています。


# まとめ

Azure の利用を始めた結果、`意図しない課金によって発生した巨額の支払請求書`によってあなたが泣き崩れてしまうことが起こらないように、**課金アラートはしっかり設定しましょう**。