OmniAICreator/Qiita-1.07M huggingface dataset:
Git初心者がCommitの粒度とかを考えた話

##はじまり
###ある日
「…今日も作業が終わったし、進んだ分をCommitしよか」
「お、**ランキング**に面白い記事があがっとるし、休憩がてら見てみよか」

[私が（iOS エンジニアの）採用でコードチェックする時何を見ているのか](https://qiita.com/lovee/items/d76c68341ec3e7beb611)

「ほえー」
「gitのcommit粒度もチームで開発していく上で重要なんやな…」


##リポジトリを見てみると…
「せや、さっきcommitしたgitの履歴見てみよか」
「gitていうたけどgithubの履歴なんは勘弁な」

![58ebce9b4d5b4e97376c4a5f7cfa1d2e.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/257615/c6faa558-9128-6322-6a6f-59f8e65598ba.png)
…
……
………
「**なんやこれ、ざっくりしすぎや**」
「何してるかは辛うじて読み取れるけども、内容に関して直感的に読み取ることができないやないか…」

「このままじゃあかん…なんとかせな…」
##ルールを考える
「とはいえどうしたらええんやろか…」
「ワイには**ハスケル子ちゃん**はおらんしな…自分で考えるしか道はないんや…」
「ただ単にルールを決めても、**区別**がつかんとあかんきがする…」

「せや、変数みたいに**接頭詞**をつけることで意味を固定化できるかもしれん」
「慣れたら不要かもしれんが、慣れんうちは**意識付ける目的**でやってみよか」

「ついでに、実際に作業したときのことを考えてみよか」
「ちなみに今回は**Vue.js**を使ってやってみるで」
###New
「Newはファイルの新規作成時に使う用やな」
「ライブラリとか参照とかは記述しておくし、最低限の様式は整えるけど、コーディングは一切されてないんやな」
####例
- NEW - 検索フォーム
- NEW - TABウィンドウコンポーネント

```vuejs:sample.vue
<template>
</template>

<script>
export default { 
}
</script>

<style scoped>
</style>
```

```javascript:sample.spec.js
import { mount } from '@vue/test-utils'
import Sample from '@/components/sample.vue'

describe('sample.vue',()=>{
    
})
```
「コードとしては最低限の状態なんやな」

###ADD
「こっちは作ったファイルになにか変更を加えた時に使うんやな」
「commitするときは、機能ごとや関数毎とか変更箇所を1個に絞るのが肝やな」
「あんまりごっちゃ混ぜでcommitすると、何の目的で変更したかわかりにくいしな」

####例
- Add - NewMethodを追加
- Add - NewMethodに非同期処理を追加
- Add - HogeHage変数名をHageyarouに変更

「ボタンとクリックしたときのイベントを追加したいときはこうやるとええんやろか」

```javascript:sample.vue
<template>
    <button>
    </button>
</template>
```

「まずはButtonタグだけの追加やな」
「次にイベントの追加やな」

```vuejs:sample.vue
<template>
    <button @click='onClick>
    </button>
</template>

<script>
export default { 
    methods:{
        onClick:()=>{
            //処理
        }
    }
}
</script>
```
「最後にイベントとメソッドを作ってコミットなんやな」
「今回は短い処理を想定してるからこれでええんやろうかな」
「長くなったりした場合にはここから更に処理とかを分割してわけていくべきやろな」

###REMOVE
「逆に削除するときのやな」
「これもADDと同じで、削除するときは一気にやるんじゃなくて、機能毎に順番に消していく方針やな」
「ただ、ファイル規模だと一気にやってもええんやろうけど」

####例
- Remove - NewMethodを使わなくなったため削除

###BUG_FIX
「不具合対応用やな」
「変更箇所は他のと同じ感覚でやっていくんやな」
「ブランチ側でBUG対応のを作る運用もあるし、もしかしたらいらんかもやな」

####例
- BUG_FIX - InputでEnterキーを押してもイベントが発生しない

##ボツ
「色々考えてた結果、途中でボツになったやつやな」
「なぜボツにしたかも残しておくことで、途中どういうふうに考えたかの思考を残しておこか」
###WORKING
「NEWのあとの作業で使う予定やったやつやな」
「そもそもWORKINGて作業工程が何やねんなのと、ADDとどう違うねんてなったんやな」
「結局ADDでええやんてなった結果ボツやな」
###UPDATE
「当初は既存箇所変更をUPDATEに、新規追加をADDにするようにしようと思ってたんやな」
「分割するほどの意味があるかと言われればあやしかったからボツなんやな」
「ただ、後々復活もありかなとは書いた時は思ってるんやな」
###FIX
「作業が終わったときに、終わったことをCommitでもわかるようにしようと思ってたんやな」
「ただし、それならプルリクで知らせるほうがいいやないか！てことでボツになったんやな」

##終わり
「自分なりにやけど考えてみたけど、ええのができたな」
「**ADD**あたりはまだ不安やけど、ここからさきはやってみて感覚を掴んでいきたいんやな」
「ワイ記法も初めてやけど、なんとか記事にできたのもよかったんやな」

「許していただいたやめ太郎さんには感謝しつくしてもたりひんで…」
React Router から渡されるプロパティ(Route props)を使用する3通りの方法

React Router から渡される `match`, `location`, `history` プロパティ(Route props)を使用する3通りの方法です。（3番目の方法はあまり知られていないはず）


## 1. Route コンポーネントでレンダリングする

Route コンポーネントのレンダリング方法は `componentプロパティ`, `renderプロパティ`, `childrenプロパティ` の３つがありますが、いずれの方法でも Route props が渡されます。

以下のHogeコンポーネントは、いずれも Route props を使用できます。

```react
const Hoge = props => {
  const { match, location, history } = props;
  ...
}

<Route path="/hoge" component={Hoge} />;
<Route path="/hoge" render={routeProps => <Hoge {...routeProps} />} />;
<Route path="/hoge" children={routeProps => <Hoge {...routeProps} />} />;
```

## 2. withRouter を使う

`withRouter` HOC を使えば、Route コンポーネントでレンダリングしたコンポーネントではなくとも任意の場所で Router props を使用できます。
（ただし、 `withRouter` は location の変更等を関知しないので、注意が必要です。 ）

```react
import { withRouter } from "react-router";

const Hoge = withRouter(props => {
  const { match, location, history } = props;
  ...
})

const Container = () => (
  <div>
    <Hoge />
  </div>
)
```

## 3. RouteContext を使う

1,2 はおさらいで、ここからが本題です。
`react-router` には `__RouterContext` というものがあり、これにより Route props を取得することが出来ます。
ちなみに、下記のようにカスタム hook にしておくと便利だと思います。この hook を使えば下層のコンポーネントでも手軽にRoute props を取得できます。
ただし、 `__RouterContext` という名前から感じるように本来は触らないほうがいいものかもしれませんので、使用は自己責任で。

```typescript
import {
  __RouterContext as RouterContext,
  RouteComponentProps,
} from 'react-router'

const useRouter = (): RouteComponentProps =>
  React.useContext(RouterContext) as RouteComponentProps

const Hoge = () => {
  const { match, location, history } = useRouter()
  ...
}
```

### 2019-09-27 追記

React Router の v5.1 がリリースされ、 `useHistory`, `useLocation`, `useParams`, `useRouteMatch` の hooks が正式に提供されました。詳細は下記ページが詳しいです。

https://reacttraining.com/blog/react-router-v5-1/
Apacheの設定を見直してSSL Server TestでA+がでるようにしたときにやったこと

基本的にはここ([SSL Server Test (Powered by Qualys SSL Labs)](https://www.ssllabs.com/ssltest/))で言われた通りに修正していっただけ。

## やったこと

### SSLプロトコルをTLS1.2のみにした

```
SSLProtocol -all +TLSv1.2
```

TLS 1.2未満のサポートはもういいかなということで自分のサイトもサポートしないことにした。

### 暗号スイートを設定した

```
SSLCipherSuite "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA RC4 !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS !RC4"
```

暗号化スイートについてよく知らなかったから下記サイトを読んだ。

- [[SSL] SSL/TLS暗号化設定を見直そう！！（その1） | マリンロード](https://www.marineroad.com/staff-blog/15526.html)
- [ApacheとNginxでのForward Secrecy設定方法](https://rms-digicert.ne.jp/howto/basis/Forward_Secrecy_Apache_Ngix.html)


### HSTSを設定した

```
Header set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
```

max-ageを短く設定したらSSLServerTestに設定値が小さすぎると怒られたから365日に設定した。
`mod_headers`の有効化も必要。


### HSTS preloadの登録

プリロードオプションを使うのであれば、Googleのプリロードサービスに登録を行う。

- [https://hstspreload.org/](https://hstspreload.org/)


## 参考

- [HTTP Strict Transport Security - ウェブセキュリティ | MDN](https://developer.mozilla.org/ja/docs/Web/Security/HTTP_Strict_Transport_Security)
- [SSL判定のA+を取るために安易にHSTSをONにしてはいけません。 – Webディレクターのお仕事ブログ](https://blog.seamonkey-delivery.com/ssl/1507/)
"apt upgrade"と"apt-get upgrade"の違い


`apt-get upgrade`だと保留がでるパッケージが`apt upgrade`だと保留にならずにアップグレードされる。

``` console
$ apt-get -s upgrade
...
以下のパッケージは保留されます:
  firefox-esr linux-image-amd64
...
```

``` console
$ apt list --upgradable
一覧表示... 完了
firefox-esr/oldstable 52.7.3esr-1~deb8u1 amd64 [45.9.0esr-1~deb8u1 からアップグレード可]
libsdl-image1.2/oldstable 1.2.12-5+deb8u1 amd64 [1.2.12-5+b5 からアップグレード可]
...
```

## `apt upgrade`と`apt-get upgrade`の違い

[Debian管理者ハンドブック (6.2.3. システムのアップグレード)](https://debian-handbook.info/browse/ja-JP/stable/sect.apt-get.html)を読んだら解決した。要約すると、

`apt upgrade`、`apt-get upgrade`、`aptitude safe-upgrade`は他のパッケージを削除せずにアップグレードできるインストール済みパッケージだけをアップグレードする。
しかし、`apt-get` のアップグレードパッケージの選択規則は少し条件が厳しく、現在のパッケージ構成を変えないようになっている。つまりパッケージの新バージョンで導入された新しい依存関係により現在インストールされていないパッケージをインストールする必要が生じた場合はそのパッケージのアップグレードを保留する。

## 参考

- [Debian管理者ハンドブック](https://debian-handbook.info/browse/ja-JP/stable/index.html)
便利なオンラインAPIリクエストビルダー、Postwomanの紹介

ついにAPI界にもジェンダーの波が！

というわけでも特になく、単にPostmanフォロアーだからだと思われます。


# Postwomanってなに？

[Liyas Thomas](https://twitter.com/liyasthomas)によって開発された、APIリクエストビルダーです。
平たく言うとオープンソース簡易版[Postman](https://www.getpostman.com/)です。


# Postmanってなに？

[Postman](https://www.getpostman.com/)とは、Qiitaにも[幾つか記事があります](https://qiita.com/tags/postman)が、開発中のAPIを叩いてテストしたりするのに便利な開発ツールです。
ただソースがクローズドだったり一部有料だったり、あとはなんといってもインストール形式なので導入が面倒です。
昔はChromeエクステンションもあったらしいのですが、現在はもう見つかりません。

本格的に重厚長大なアプリを作る際のテストには適しているかもしれませんが、ちょっと数個のAPIを試したいだけなんだ、なんてときにはもっと気軽に使いたいですね。
そんなときはPostwomanの出番です。


# Postwomanを使ってみる

[Webサイト](https://liyasthomas.github.io/postwoman/)にアクセス。
Method、URL、Pathを設定してSendボタン。
それだけでリクエストを送って、返ってきたレスポンスが表示されます。

![01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26088/64aef954-c57f-a975-ed93-127db0b906ab.png)

すごい簡単。

リクエストメソッドはGET/HEAD/POST/PUT/DELETE/OPTIONS/PATCHと各種対応。
BASIC認証とAuthorization: Bearerによる認証にも対応しているので、開発中で閉じてるサイト内にもアクセス可能。
非常に簡単にテストが行えますね。


## WebSocket

なぜかWebSocketも使えます。
デフォルトではあらゆるメッセージを鸚鵡返しにする`wss://echo.websocket.org`が指定されているので簡単にお試しができます。
WebSocketサーバ実装する人がこのWebアプリを使う場面というのはあんまり想像できませんが。


## 見た目を変えよう

デフォルトだと昔のハッカー気取りサイトみたいな黒緑です。
というかなんでアイコンがこんな可愛くないグレイなんですかね。

右上の歯車ボタンをクリックすると、背景色と一部文字色を変更可能です。

![02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26088/1f8b0e81-8e24-e510-5d22-2ad36bf309a9.png)

残念ながら任意の文字色を入れたりはできないようですが、まあそこまでもカスタマイズが必要なサービスでもないでしょう。


## 何度も同じリクエストを送りたい

Historyに履歴が残ります。
鉛筆みたいなボタンを押すとその履歴が反映されるので、再送も簡単です。


## オンラインだと機密情報が…

[GitHub](https://github.com/liyasthomas/postwoman)を`clone`して`npm install`して`npm run dev`して`localhost:3000`にアクセスするんだよ。


## PWAもあるぞ

一番右下のボタンからインストールできます。

![03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26088/df52b326-3267-a2e9-b7e6-afc0bb44d6d4.png)

宗教上の理由でインストールしていないので詳細は不明。


# 感想

本格的にテストを書くならやはり専用テストツールの導入が必要になると思いますが、とりあえずちょっとAPIの動作を試してみたいだけなんだ的な用途には最適だと思います。
使い方も見たらわかるレベルですからね。

[GitHub](https://github.com/liyasthomas/postwoman)では開発の手伝いを募集しているようです。
また[コーヒー](https://www.buymeacoffee.com/liyasthomas)も求めているので、使って気に入った人はぜひ投げ銭を送ってあげましょう。
git の hook script を書くときは GIT_DIR が設定されていることに注意

git のpost-updateスクリプトのなかで、git pullさせようとして、bashスクリプトの中で以下のコマンドを使った。

``` bash
git -C /path/to/git/dir pull
```

この post-update スクリプトをサーバー側で実行しても何もエラーはでなかったが、いざクライアントからサーバーへ push してみるとエラーが出た。

``` console
$ git push
Counting objects: 2, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (2/2), 266 bytes | 266.00 KiB/s, done.
Total 2 (delta 1), reused 0 (delta 0)
remote: fatal: Not a git repository: '.'
To hogehoge.fuga.jp:/path/to/sample.git
   61c35e6..6a9db99  master -> master
```

[GIT_DIR](https://git-scm.com/book/ja/v2/Git%E3%81%AE%E5%86%85%E5%81%B4-%E7%92%B0%E5%A2%83%E5%A4%89%E6%95%B0) という環境変数が定義されていて、それが優先されているらしい。 post-updateスクリプトに、


``` bash
unset GIT_DIR
```

を追加したら期待通りの動作になった。

## 参考

- [Pro Git 2nd Edition (10.8 Gitの内側 - 環境変数)](https://git-scm.com/book/ja/v2/Git%E3%81%AE%E5%86%85%E5%81%B4-%E7%92%B0%E5%A2%83%E5%A4%89%E6%95%B0)
Sidekiqのベストプラクティス [翻訳]

# Sidekiqのベストプラクティス

最終更新 2019/04/27 編集者 Madogiwa

Sidekiqのパフォーマンスを改善するには、次のルールに従ってください。

## 1. ジョブ引数を小さくシンプルにする

Sidekiqは、`perform_async` の引数をJSONとしてRedisに保存します。私はこのようなコードをたくさん見かけます。

```ruby
quote = Quote.find(quote_id)
SomeWorker.perform_async(quote)
```

複雑なRubyオブジェクトはJSONに変換することができません。デフォルトでは `to_s` を使って変換することになり、その結果は `#<Quote:0x0000000006e57288>` のようになります。

たとえシリアライズができたとしても、キューに保存されている間に quoteオブジェクトの値が変更されたらどうなるでしょうか？ **状態をSidekiqに保存するのではなく、シンプルなIDを保存するようにしてください。** そして、performメソッド内で実際に必要になったタイミングで、オブジェクトをfindするようにしてください。

```ruby
SomeWorker.perform_async(quote_id)
```

`perform_async` メソッドに渡す引数は、 **文字列、整数、浮動小数点、真偽値、null(nil)、配列、ハッシュ** といったシンプルなJSONデータ型で **構成する必要があります** 。これは、Rubyのシンボルを引数に **使用してはいけない** ということを意味します。

SidekiqクライアントAPIはRedisへデータを送信するために `JSON.dump` を使用します。Sidekiqサーバーは、そのJSONデータをRedisからプルし、 `JSON.load` を使ってRubyオブジェクトに戻し、performメソッドに渡します。

**シンボル、名前付きパラメーター、複雑なRubyオブジェクト（DateやTimeなど！）を `perform_async` メソッドに渡さないでください。** これらの値またはオブジェクトは、dump/loadの変換で正しく渡される保証がありません。

## 2. ジョブを冪等にし、トランザクションを利用する

[冪等性](https://en.wikipedia.org/wiki/Idempotence)とは、ジョブを複数回安全に実行できる性質のことです。例えば、エラーが起きた際のリトライの仕組みが用意されているとき、ジョブが半分だけ実行されて、そこで例外が起きて、正常に完了するまで何度も再実行されることがあります。

さらに具体的な例として、クレジットカードの取引を無効にし、請求金額の払い戻しがあることをユーザーにemailで知らせるジョブがあるとします。

```ruby
def perform(card_charge_id)
  charge = CardCharge.find(card_charge_id)
  charge.void_transaction
  Emailer.charge_refunded(charge).deliver
end
```

emailの送信がバグで失敗した時に何が起きるでしょうか？ `void_transaction` メソッドは、請求金額が既に返金済みのケースを正しく処理できるでしょうか？ このような疑問を解消するためには、次の2つの方法が利用できます。

- 1つめは、エラーが起きたときにデータベースへの変更を正しくロールバックするようトランザクションを使うという方法。
- 2つめは、エラーが起きたときにそのエラーを適切に処理するような弾力性のあるコードを書くという方法です。

Sidekiqは、ジョブを **必ず1回** ではなく、 **少なくとも1回** 実行するという点を忘れないでください。

## 3. 並行性を取り入れる

Sidekiqは並列実行向けにデザインされています。そのため、ジョブを並列実行可能に設計すれば、Sidekiqのパフォーマンス上の恩恵を最大限受けることができるようになります。

Sidekiqは並行性を調整するための基本的な機能を持っています。例えば、1つのキューを処理するスレッドの数を事前に定義する機能などです。but your system architecture is much simpler if you don't have such specialization.

Sidekiqプロセスからのトラフィックが他のサーバーを圧迫しているのであれば、全体の接続数を制限するために[コネクションプール](https://github.com/mperham/sidekiq/wiki/Advanced-Options#concurrency)を利用することができます。

Sidekiqはジョブの中で並行性を調整するような機能は提供していません。
Automation Anywhere★基本入門「Insert Key Stroke」★

「Insert Key Stroke」です。
キーボード操作しましょう:relaxed:

使用頻度も高く、意外と便利なコマンドです。

前回、作成をしたExcelファイルのB1に文字を入力して、保存をせずに閉じるまでを行います。


いつものようにコマンドを検索し、開きます。

まず、Excelファイルを開きます。
今回は、取得ではないので「Get　Cells]ではなく「Go To Cell」
セル指定があるので「Specific　Cell：B1」を設定。


次に「Insert Key Stroke」を開きます。

操作対象のウィンドウを設定します。

画像の右上「Refresh」をクリックします。
PC上に表示されている「操作対象となる全てのウィンドウ名」がプルダウン表示されます。
その中から、作成をしたExcelファイルを選択します。

<img width="351" alt="11無題.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/489732/2625b287-1fbb-93a9-c783-7a4c173c3101.png">


操作対象を設定したら、入力値を設定。
文字はExcelファイルに入力をしたい文字を入れてください。
そのまま続けて、下のボタンより「Enter」をクリックして追加します。

そして入力欄の下にある「Deray」これが地味に重要です。
文字列を入力する速度。。。と申しますか、ここの設定をしないとするでは、結果が変わります。

なんとなく300msを入力し「Save」

<img width="660" alt="1無題.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/489732/f892b0db-db00-2c26-8c8f-99d6a627af75.png">

現在のコードは、３行のはずです。

AAをSaveして、Runしてみて下さい。

・Excelが開き
・B1セルがアクティブになる
・Key Strokeに入力した文字が流し込まれる
・Enterを押して確定される


この動作となればOKです。


では、このままExcel開いたままでいいの？となるので、現在のファイルは保存せずに閉じます。


最後に「Excelを閉じる」コマンドを追加します。

「Excel」コマンド「close spreadsheet」を選択。
これで「保存をして閉じます」

が、保存をせずに閉じたい時もありますよね。
「Do not save changes」にチェックを入れておけば、保存がされずに閉じられます。
※保存しますか？とは聞いてきません。

<img width="332" alt="44無題.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/489732/ee0efa2f-d125-0a90-77bc-4755943310ef.png">

最終的に、下記の画像のようになっていれば完成です！

<img width="580" alt="55無題.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/489732/9e5403bf-6f8a-9132-746f-8b4afe51df3a.png">




Automation Anywhere　★基本入門★
「Insert Key Stroke」これにて終了！
お読みいただき、ありがとうございました:v:

次回は「Loop」を使用して、Excelファイルに連続入力をします。
社内勉強会でおじさんSEについて勉強した話

# はじめに
2019/8から始まったAWS勉強会のはじめに、「おじさんSE」にならないように「おじさんSE」の勉強をしました。
勉強をしてほしいという思いを込めての動画です。

# 見た動画
https://youtu.be/fwRFm7WCex8

# おじさんSEの定義・特徴
・30歳以上
・モダンな技術を知らない
・レガシーな技術しか知らない
・主に設計書などのドキュメント類を弄っており、コーディングをしない
・現状から変わる気がない（キャリアアップに対し具体的なアクションがない）
・AWS、GCP、Azureなど名前すら聞いたことがない人も存在する
・Excelが好きなのにVBAを書ける人は多くない。
・VBAを使いこなすおじさんSEは生産性が高いと評価される。
・おじさんSEはあまりテクノロジーの勉強をしない。
・おじさんSEの中にも、具体的な行動は起こしていないものの、PM→管理職とマネジメント系で出世できたらいいなと漠然と思っている人は存在する。そういう人は、自己啓発本やマネジメント本を読んだりして、テクノロジー系でなくても、勉強する場合もある。
・なんとなく今生活できてるからいいや、と思っている人は、本当に一切勉強しない。
・勤怠は特筆すべきスキルがないので、勤怠くらいはちゃんとする。

# 参加者の声
「あ～」
「う～ん」
「・・・」
「めっちゃディスってる」
「うちの会社の管理職がでる会議で見せようか？」
「勉強しましょう」
「勉強しないと」

# 個人の感想
まず、あるあるだなぁと感じました。
SEになる前は、技術の進歩が早いと聞いていたのでみんな勉強してるのかと思っていましたが、入ってみるとそうでもなかったです。(めっちゃ勉強する人もいます)
常駐先のマナー/しきたりに命を懸ける人もいるなぁとしみじみ思いをはせました。
人間ついついサボりたくなるので、気持ちがわかるのでどうにか勉強しておじさんSEにならないようにしないとと焦りました。
ただ、おじさんSEも現在のSierの仕事には必要ではあるので全くの否定はできないです。。(なりたくはないです)

# 参照(魚拓)
https://megalodon.jp/2019-0911-1119-59/https://qiita.com:443/kyojinogasawara/items/fbc63f1f66b7b7a929bd
Vagrant上のWindows10にWSL+Docker for Windowsを導入してみる

自分のMacOS上で、仮想環境のWindows環境を立ち上げていろいろ実験。

今回は、Dockerを手軽に試したいと思ったので、
WSLとDocker for Windowsを導入してみます。

結論として、仮想環境上のWindowsは、Enterpise版なんですが、Hyper-Vは使えない。
よって、Docker for Windowsは動作不可でした。。

WSLの導入手順がメインの内容となります。

# 環境
- MacOSX
- Windows10 Enterprise(Vagrant上のWindows)

# WSLの導入

## WSLのインストール
「アプリと機能」 → 「プログラムと機能」 → 「Windowsの機能の有効化または無効化」 → 「Windows Subsystem for Linux」

## WSLを使用可能とする(Fall Creators Updateでは不要)
「設定」→「更新とセキュリティ」→ 「開発者向け」→ 「開発者モード」にチェック →　注意書が出ますが、そのまま進めます。

# Ubuntuのインストール

- Microsoft StoreでUbuntu(バージョン記載ない最新版)を検索して、インストール
- 任意のuser、passを設定

# Ubuntu設定、環境整備

## リポジトリの変更

通信速度が遅くなる海外サーバーから日本サーバーへ変更します。 

```
sudo sed -i -e 's%http://.*.ubuntu.com%http://ftp.jaist.ac.jp/pub/Linux%g' /etc/apt/sources.list
```

## パッケージのアップデート

各種ライブラリをインストールするための準備として、updateとupgradeをします。

```
sudo apt update
sudo apt upgrade
```

## よく使う各種ツールをUbuntuにインストールする
```
sudo apt install vim-nox
sudo apt install git
sudo apt install zsh zsh-syntax-highlighting
chsh -s /bin/zsh
sudo apt install openjdk-11-jdk
```

# Docker for Windows入れてみた話を少し
Windows10 EnterpriseでもHyper-Vが使えそうだったので、Hyper-Vを有効化しようとしましたが。。
Hypervisor機能が無効になっている。。

![スクリーンショット 2019-09-12 21.22.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/84228/0ee6cb9d-b88a-d6b4-6859-193791fcfaf6.png)



>現在PCに搭載されているCPUのVirtualization Technology(VTx)がサポートされているかを確認してみましょう。

以下URLで上記記載があったので、CPU-Zをダウンロードして確認したところ、「VTx」はなし。。だめか。
https://www.billionwallet.com/windows10/hyper-v-error-solution.html

Docker for Windowsは諦めて、WSL上でコンテナ1つでの動作確認だけしてみようと思います。。

# 参考にさせて頂いたURL
- [Windows Subsystem for Linuxをインストールしてみよう！](https://qiita.com/Aruneko/items/c79810b0b015bebf30bb)
選択したリンクによってformの初期値を変更する


# やりたいこと
リンクをクリックしたら、それに応じてセレクトボックスの初期値を変更したい
# 実装
今回はそのリンク先に`data-form-type`を書いて値を設定。その値がセレクトボックスのvalueとなるようにした。

`data-form-type`で設定しているがjsでvalueをセットするときは`selecteSchedule.val(formType)`になるので注意。

```html
<div class="link">
  <a href="#form" data-form-type="a">AAA</a>
  <a href="#form" data-form-type="b">BBB</a>
  <a href="#form" data-form-type="c">CCC</a>
</div>

<form id="form">
  <select name="参加希望日" size="1" value="" class="selector">			
    <option value="">選択してください</option>
    <option value="a">AAA</option>	
    <option value="b">BBB</option>	
    <option value="c">CCC</option>							
  </select>
</form>

```

```javascript
(function($){
    const links = $(".link > a");
    links.each(function() {
        $(this).on("click", function(e) {
            const formType = $(this).data("formType");
            const selecteSchedule = $(".selector");
            selecteSchedule.val(formType);
        })
    })
})(jQuery)

```
Project Euler 013を解いてみる。「大きな数の足し算」

# Project Euler 013
[013](http://odz.sakura.ne.jp/projecteuler/index.php?cmd=read&page=Problem%2013)
>以下の50桁の数字100個の合計の上から10桁を求めなさい。
37107287533902102798797998220837590246510135740250
46376937677490009712648124896970078050417018260538
~~~~~~~~~~~~~~~~~~~ 長いので省略 ~~~~~~~~~~~~~~~~~~~
20849603980134001723930671666823555245252804609722
53503534226472524250874054075591789781264330331690

->[次の問題](https://qiita.com/radiol/items/e2cf4c4ad63626ce9f46)

## 考え方
シンプルな問題ですね。Pythonなら大きな数字もそのまま扱えますし、
50桁100個程度ならそのまま計算してしまっても大して時間はかかりませんでした。

```Python:euler013.py
def main():
    n = 10
    # 長かったので問題の50桁の数字はテキストファイルにしました
    with open('euler013.txt') as file:
        number_lines = file.readlines()
    all_number_lines = [int(number.strip()) for number in number_lines]  # strからintへ変換
    all_sum_numbers = sum(all_number_lines)  # 合計
    print(all_sum_numbers)  # 全部表示
    print(str(all_sum_numbers)[:n])  # 先頭10桁だけ表示

if __name__ == '__main__':
    main()
```
これが1000桁とかになって来たら先頭の10桁だけで計算して、あとは大きい桁から逆順に繰り上がりの影響がなくなる場所までさかのぼって行く感じになるんでしょうか。
今回の場合、先頭から11桁を足すだけで`5537376230342`が得られ、ここより後ろの数字がなんであれ後半3桁の`342`に繰り上がりが吸収されるので同じ結果になります。
AWK を使って JSON の値を抽出する

jq 便利ですよね。ただインストールされていない環境も多く、jq なしで JSON の値を抽出したいとき AWK が使えるかもしれません。

# 単純なケース

たとえば、以下のような JSON があったとして、

```
$ cat users.json
{
  "Alice": "alice@example.com",
  "Bob": "bob@example.com",
  "Carol": "carol@example.com"
}
```

`Bob` の値 `bob@example.com` が欲しいとき、次のコマンドで抽出できます。

```
$ awk -F\" '/"Bob"/{print $4}' users.json
bob@example.com
```

# JSON が minify されているケース

```
$ cat users.min.json
{"Alice":"alice@example.com","Bob":"bob@example.com","Carol":"carol@example.com"}
```

組み込み変数 `RS` を定義して、レコードの区切り文字を変更します。

```
$ awk -F\" -v RS=, '/"Bob"/{print $4}' users.min.json
bob@example.com
```

# `"` がエスケープされているケース

上記の方法では `\"` のようにエスケープされた `"` がキーや値に含まれるとうまく抽出できません。

```
$ awk -F: -v 'RS=[,\n]' '/"Bob"/{ print gensub(/^ *"(.*)",? *}?$/, "\\1", 1, $2) }' users.json
```

gawk に限りますが、組み込み関数の `gensub` を使って抽出できました。上で挙げた全てのケースに対応しているので、これひとつ使えば OK かもしれません。

さて、ここまでして AWK で頑張る必要があるのでしょうか。私にはわかりません。

文字列以外が入ってくることは想定してないので、複雑なものは jq を使うなり他の高級言語を使うなりするのが良いと思います。
【PostgreSQL】紐付いたテーブルの UPDATE 文の書き方

PostgreSQL は MySQL と異なり、 `UPDATE` でテーブル結合をするときは `JOIN` が使えないことを最近知ったので記事に残しておきます。


## 結論
以下のような、2つのテーブルがあるとする
飼い主のテーブル（owners）はの名前とメガネの有無の情報を持っている。
ネコのテーブル（cats）は名前と首輪の有無と飼い主IDの情報を持っている。

 owners テーブル

| id | owner_name | has_glasses|
|:--:|:----------:|:----:|
| 1  |    一郎     | true
| 2  |    二郎     |   false
| 3  |    三郎     |  true
| 4  |    四郎     | false

cats テーブル


| id | cat_name | has_collar | owner_id |
|:--:|:----------:|:-----:|:------:|
| 1  |    タマ     |false|  3       |
| 2  |    トロイ     | true| 3 |
| 3  |    ビヨーク   | true|  2    |
| ...  |    ... | ...| ...   |
| 1000  |  ティグ|false|  4   |

メガネをかけている人が飼っているのネコ全てに首輪を付けたいとき、以下のようにする。

```sql
UPDATE pets 
SET has_collar=true
FROM owners
WHERE pets.owner_id = owners.id
  AND has_glasses=true
```

## やりがちなミス
「メガネをかけている人が飼っているのネコ」を調べるとき、

```sql
SELECT * 
FROM pets
JOIN owners
  ON pets.owner_id = owners.id
WHERE has_glasses=true
```

みたいな SELECT 文を書くから、下記のような UPDATE を書いてしまいがち。

```sql

UPDATE pets 
SET has_collar=true
FROM pets
JOIN owners
  ON pets.owner_id = owners.id
WHERE has_glasses=true
```

MySQL だと上記の方法でできるかもしれないけど、PostgreSQL だと

```
table name "pets" specified more than once
```

というエラーになる。
#rails + shoryuken gem で #AWS SQS にキューを作成する

# Gemfile

```
gem 'shoryuken'
gem 'aws-sdk-sqs'
```

# install

```
bundle install
```

# shoryuken.html

```
aws:
  access_key_id:      <%= ENV['AWS_ACCESS_KEY_ID'] %>
  secret_access_key:  <%= ENV['AWS_SECRET_ACCESS_KEY'] %>
  region:             <%= ENV['AWS_REASION'] %>
```

# env

```

export AWS_ACCESS_KEY_ID=xxxxxxxxxxxx
export AWS_SECRET_ACCESS_KEY=yyyyyyyyyyyyyyyyyyyyy
export AWS_REGION=us-east-2
```

# command

```
bundle exec shoryuken sqs create example1
```

result

```
Queue example1 was successfully created. Queue URL https://sqs.us-east-2.amazonaws.com/842696858454/example1
```

# AWSで確認

![image](https://user-images.githubusercontent.com/13635059/64765394-07353080-d57f-11e9-86e2-e3491f0cd633.png)


# Original by Github issue

https://github.com/YumaInaura/YumaInaura/issues/2417








<!-- Update From Qiita API -->

# チャットメンバー募集


何か質問、悩み事、相談などあればLINEオープンチャットもご利用ください。

https://line.me/ti/g2/eEPltQ6Tzh3pYAZV8JXKZqc7PJ6L0rpm573dcQ





# Twitter


https://twitter.com/YumaInaura


<!-- Update From Qiita API -->
最下部までスクロールしないと次のDOMが現れないパターンのあれ

終点が決まっている場合は以下のように書くのがベストらしい。

```python:test.py
current_source = ''
while current_source != driver.page_source:
    current_source = driver.page_source
    driver.execute_script('scrollTo(100, document.body.scrollHeight)')
    sleep(1.5)
```
[#Unity] BlendShapeを列挙してインデックスを保存できるエディタ拡張

誰もが実装するであろう、BlendShapeのインデックスを登録しておくエディタ拡張です。
![2019-09-12_191745.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/127402/9c272e77-7f08-24b7-6455-f26f143019c8.png)

# MonoBehaviour側
```
using UnityEngine;
using System.Linq;

public class SetBlendshape : MonoBehaviour
{
    public SkinnedMeshRenderer skin = null;

    public int[] selectedIndexes = Enumerable.Range(0, 7).ToArray();

    private void Start()
    {
    }

    private void Update()
    {
    }
}
```

# エディタ側
```
using UnityEngine;
using UnityEditor;

[CustomEditor(typeof(SetBlendshape))]
public class SetBlendshapeEditor : Editor
{

    SerializedProperty skinProperty;
    SerializedProperty selectedIndexesProperty;

    private static string[] settings = new string[]
    {
        "A", "B", "C", "D", "E", "F", "G"
    };

    private void OnEnable()
    {
        skinProperty = serializedObject.FindProperty("skin");
        selectedIndexesProperty = serializedObject.FindProperty("selectedIndexes");
    }
    
    public override void OnInspectorGUI()
    {

        var blendShapeNames = GetMeshBlendshapeNames();

        serializedObject.Update();

        EditorGUILayout.PropertyField(skinProperty);

        if (EditorGUILayout.PropertyField(selectedIndexesProperty))
        {
            EditorGUI.indentLevel++;

            for(int i = 0; i < settings.Length; i++)
            {
                BlendNameProperty(selectedIndexesProperty.GetArrayElementAtIndex(i), settings[i], blendShapeNames);
            }

            EditorGUI.indentLevel--;
        }

        serializedObject.ApplyModifiedProperties();
    }


    private string[] GetMeshBlendshapeNames()
    {
        var target = (SetBlendshape)serializedObject.targetObject;

        if (target == null || target.skin == null)
        {
            return null;
        }

        var mesh = target.skin.sharedMesh;
        var blendshapeCount = mesh.blendShapeCount;
        var blendNames = new string[blendshapeCount];

        for (int i = 0; i < mesh.blendShapeCount; ++i)
        {
            blendNames[i] = mesh.GetBlendShapeName(i);
        }

        return blendNames;
    }


    private void BlendNameProperty(SerializedProperty prop, string name, string[] blendShapeNames = null)
    {
        if (blendShapeNames == null)
        {
            EditorGUILayout.PropertyField(prop, new GUIContent(name));
            return;
        }

        var values = new int[blendShapeNames.Length];
        var options = new GUIContent[blendShapeNames.Length];

        for (int i = 0; i < blendShapeNames.Length; ++i)
        {
            values[i] = i;
            options[i] = new GUIContent(blendShapeNames[i]);
        }

        EditorGUILayout.IntPopup(prop, options, values, new GUIContent(name));
    }
}

```
VSCode拡張機能開発・Intellisenseのアイコンマークまとめ

# VSCode拡張機 Intellisense
言わずもがな、いつも助けていただいている補完機能。
<img width="685" alt="intellisense.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189041/7dad8f41-9761-1140-0b33-cffe35f21f9e.png">

最近、個人的にこの補完の機能を作っている中で補完時にでるアイコンについて調べたのでメモ

# アイコンの決め方
アイコンはvscode.CompletionItemのvscode.CompletionItemKindで決めます

```ts
const completion = new vscode.CompletionItem("app", vscode.CompletionItemKind.Variable);
```
この```vscode.CompletionItemKind```が25種類ありますが、そのどれでどのアイコンがでるかがいまいちわからなかったので、一度全て出してみました。
![CompletionItem_icon.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189041/f9bca80f-3166-d388-8e86-62a5513cad13.png)

# 具体的なアイコンの使い分け
上のアイコンの中でじゃあ具体的にどれを使えばいいの！っていうのがむずかしく、vscodeの補完とにらめっこしながら考えていたのですが、ちゃんと記載がありました。

https://code.visualstudio.com/docs/editor/intellisense
一部スクショで抜粋。
<img width="763" alt="スクリーンショット 2019-09-11 7.34.49.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189041/d75586a6-312a-23b3-5c5f-1a43f7226860.png">

改めて表とにらめっこしながらKindを決めていく必要がありそうです。
Qiita 記事投稿時のテンプレート

記事投稿時のフォーマットのまとめ。
良さそうな型が見つかれば追記します。

```
# 前提/概要
## 想定/対象
## 環境

# TL;DR(要約)
- 
- 

# 実行手順／再現手順／詳細
1.
2.
3.

# 参考文献
```
[Ruby] SJIS (CP932) のはずなのにエンコーディングが UTF-8 の文字列を UTF-8 に変換する

# やりたいこと

本当は SJIS (CP932) のはずなのにエンコーディングが UTF-8 と解釈されている文字列を、SJIS と判断した上で UTF-8 に変換したい。

```rb
str = '米津玄師'.encode(Encoding::SJIS).force_encoding(Encoding::UTF_8)
#=> "\x95ĒÌ\xBA\x8Et"
str.encoding
#=> #<Encoding:UTF-8>
str.encode(Encoding::UTF_8)
#=> "\x95ĒÌ\xBA\x8Et"
# ???
#=> "米津玄師"
```

# 方法

[NKF.guess](https://docs.ruby-lang.org/ja/latest/method/NKF/m/guess.html) と [String#encode](https://docs.ruby-lang.org/ja/latest/method/String/i/encode.html) (第 2 引数に変換元のエンコーディングを指定する) を使う。

```rb
require 'nkf'

str = '米津玄師'.encode(Encoding::SJIS).force_encoding(Encoding::UTF_8)
str.encode(Encoding::UTF_8, NKF.guess(str)) # NKF.guess(str) #=> #<Encoding:Shift_JIS>
#=> "米津玄師"

str = '米津玄師'
str.encode(Encoding::UTF_8, NKF.guess(str))
#=> "米津玄師"
```
GCE環境のredashにてSystemStatusページからアクセスするCeleryStatus内のQueriesが消えてくれない場合のkill方法

#### ※自分用のメモです。
# 環境
- GCE内でdocker運用
- redash Version: 7.0.0+b18042

# redash内のこの画面
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96662/debcaa3e-fd69-c88a-7fab-aa6be6d8cd9b.png)
## ここで時たま問題が発生する
- 上の画像はNo Dataになってますがここにスケジュールで実行されたQueryやExecuteしたQueryが基本的に表示されるようになってるが、実行が失敗したかなにかで無限にここに残り続けるやつ（画像無）がいる。。。。

### そして、それを消さないと次のスケジュールされてるQueryも無限に増え続ける。。。
- 複数の事業部でがっつり使ってるので更新されないとクレームの嵐・・・

### 手動で見にいく必要はあるが以下手順で解消できる（Mac）
※この手順はローカルアクセスする場合（GCE内からSSHログインでもほぼ変わらない手順で可能）

- 1.ターミナルでGCPにログイン`gcloud beta compute --project "プロジェクト名" ssh --zone "ゾーン名" “インスタンス名”`
    - GCEの`gcloudコマンドを表示`すればコピ貼りでいける
    - パスワードを求められるのでパスワード入力
- 2．sshログインが完了したら`sudo docker exec -it redash_scheduled_worker_1 /bin/sh`を入力
    - worker名（redash_scheduled_worker_1）は環境によって異なるので注意
- 3.次に`top`コマンドを入力しプロセス一覧を念の為確認
    - redash内の画面と同じPIDの処理があれば正解
- 4．プロセス一覧を終了し`kill -9 PID`を行って無事に消えてれば成功

### これからの課題
- 自動で止まってるqueryが気づける必要があるのと止まる原因の把握が必要
    - いつも普通に動いてるQueryがなぜか急に詰まったりする
Subversionにおけるマージ手順 ~ Eclipseを使って ~

Eclipseを使ってSubversionでマージを行う手順です。

## 使用ツール
Eclipse (Neon 4.8)

## 手順
### 1. eclipseにて、trunkをチェックアウトする
①「ファイル」 > 「インポート」 > 「SVNからプロジェクト」（SVNフォルダ配下） > 「次へ」を押下
②「新しいリポジトリー・ロケーションを作成」を選択し、「次へ」を押下
③「URL」にtrunkのURLを入力し、認証欄の「ユーザー」と「パスワード」欄に自身のアカウントのものを入力し、「次へ」
④改訂欄が「HEAD改訂」となっていることを確認し、「完了」を押下（※必要に応じて変更）
⑤別名チェックアウト小ウィンドウが立ち上がるので、「名前を指定してプロジェクトとしてチェックアウト」を選択し、任意の名称を付け、「完了」を押下

### 2. 開発用ブランチとtrunkの比較と変更の反映
①1でチェックアウトしたtrunkの変更対象ファイルを右クリックし、「比較」 > 「改訂またはURL」を押下
②リポジトリー・リソースが表示されるので、「branches」配下より、開発用ブランチを探し、対象ファイルを選択
③差分を反映し、手順1-⑤で作成したプロジェクトフォルダ上で右クリックし、「チーム」 > 「コミット」で変更内容をコミットする
ARFoundation version2.0以降での注意点

#経緯
下のテラシュールブログさんの記事を参考にAR Foundationを使用しARアプリを作ろうとした時に記事のバージョンより上の
AR Foundation Version 2.0.2を使用したところいくつか変更点があり、記事の通りにやってもうまくいかなかったため。
[テラシュールブログさんの記事](http://tsubakit1.hateblo.jp/entry/2018/11/21/211400)
#内容
AR Foundation Version 2.0.2を使用しての簡単なARアプリの作り方
なおアプリの仕様に関しては前述のテラシュールブログさんの内容に同じです。
#環境
Unity 2019.2.5f1
AR Foundation 2.0.2
AR Core XR Plugin
AR Kit XR Plugin
ビルド先
Android端末
#開発の流れ
1. Package Managerから上部3つをインストールします。
2. Main Cameraを削除しシーン上にAR Session, AR Session Origin,AR Default Planeを設置します。
3. AR Default PlaneをPrefabにしてシーンから削除します。
4. AR Session OriginにAR Plane Manager, **AR Raycast Manager**の二つのコンポーネントを追加します。Version 1.0.0では   
  AR Session OriginにRaycastの定義が含まれていましたが2.0.0以降では新しくAR Raycast Managerというコンポーネントで    
  AR CameraからのRaycastを管理するようになりました。
5. AR Plane ManagerにPrefabにしたPlaneを登録します。
6. 以下のスクリプトを参考にしてAR Session Originにアタッチしてください。

```c#

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR.ARFoundation;

public class SpawnScript : MonoBehaviour
{
    private List<ARRaycastHit> hitResults = new List<ARRaycastHit>();
    private ARRaycastManager rayManage;

    [SerializeField] GameObject prefab;

    private void Awake() {
        rayManage = this.GetComponent<ARRaycastManager>();
    }
    private void Update() {
        if (Input.GetMouseButtonDown(0)) {
            if (rayManage.Raycast(Input.GetTouch(0).position, hitResults)) {
                Instantiate(prefab, hitResults[0].pose.position, Quaternion.identity);
            }
        }
    }
}

```

7.Prefabに出現させたいオブジェクトを登録してください
8.Build Settingで各種設定をしてもらいます。この時にAR Core Supportにチェックを入れるとAR Core XR Pluginと競合するのでチェックを入れないようにしてください。Settingの仕方に関してはAndroidの場合以下を参考にしてください(iosに関してはすみませんが調べてください)
[Android AR Setting](https://developers.google.com/ar/develop/unity/quickstart-android)
Buildするとうまくいくと思います。
初めての執筆だったのでわかりづらい点があるかもしれませんが参考になればうれしいです。
#解決策になったUnity Forum
https://answers.unity.com/questions/1632653/arfoundation-arsessionoriginraycast-no-longer-exis.html
【超便利なのに知らなかった】 Apache バーチャルホスト設定された一覧を一括表示

# httpd -S の存在知りませんでした。

```conf
[your@server ~]$ sudo /usr/sbin/httpd -S

VirtualHost configuration:
wildcard NameVirtualHosts and _default_ servers:

*:80                   is a NameVirtualHost
         default server 100.10.100.10 (/etc/httpd/conf/httpd.conf:1005)
         port 80 namevhost 100.10.100.10 (/etc/httpd/conf/httpd.conf:1005)
         port 80 namevhost aaa.com (/etc/httpd/conf.d/vhost_1.conf:2)
         port 80 namevhost bbb.com (/etc/httpd/conf.d/vhost_1.conf:9)
         port 80 namevhost ccc.com (/etc/httpd/conf.d/vhost_1.conf:17)
                 alias hoge.ccc.com
                 alias fuga.ccc.com
         port 80 namevhost ddd.jp (/etc/httpd/conf.d/vhost_2.conf:31)
         port 80 namevhost eee.jp (/etc/httpd/conf.d/vhost_2.conf:66)
         port 80 namevhost fff.jp (/etc/httpd/conf.d/vhost_2.conf:87)
*:443                  is a NameVirtualHost
         default server ssl.aaa.com (/etc/httpd/conf.d/vhost_ssl.conf:24)
         port 443 namevhost ssl.aaa.jp (/etc/httpd/conf.d/vhost_ssl.conf:24)
                 alias ssl.sample.com
                 alias ssl.example.com

*:8000                 is a NameVirtualHost
         default server pma.aaa.com (/etc/httpd/conf.d/phpMyAdmin.conf:11)
         port 8000 namevhost pma.aaa.com (/etc/httpd/conf.d/phpMyAdmin.conf:11)
```

全てのバーチャルホスト設定を一覧表示。

上記だと`80`、`443`、`8000ポート` ３つに対してのそれぞれのバーチャルホストが（しかもエイリアス）まで
一発でどんなサービスがそのサーバに乗っかっているか一目瞭然や。。。（稼働しているかは別として）

本当今まで無知だったのが恥ずかしい。（ちなみに Apache 2.x 以上）
[Rails]本番環境でWe're sorry, but something went wrongのエラーが出る。

#はじめに
本番環境にて投稿したデータを削除しようとした際このようなエラーが出ました。
![error.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/435517/246afb4c-1e64-a2de-43f1-ede750333598.jpeg)
ローカルではどのようなエラーが出るか確認したところ、<img width="1440" alt="スクリーンショット 2019-09-12 18.25.22.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/435517/08e7f6c1-9621-17e7-3f00-5ad71dd8f0f3.png">
このようなエラーが出た為DBの問題か？と考えましたがどうやらいいね機能に原因があるらしく、いいねがされてる投稿を削除をする事が出来なくなっていました。

#なぜこのようなエラーが出るか？
この記事を参考にした所
https://qiita.com/Ushinji/items/650fa295a3054d2fe582
どうやら外部キー制約のついたカラムを削除する必要があるらしく、`dependent: :destroy`を設定する必要があるみたいです。今回は`video.rb`に記述漏れがあったのでその記述を追加しました。

#記述例 
```ruby:video.rb
class Video < ApplicationRecord
  mount_uploader :image, VideoUploader
  belongs_to :user
  has_many :likes, dependent: :destroy
  has_many :liked_users, through: :likes, source: :user
  has_many :comments
end
```

```ruby:user.rb
class User < ApplicationRecord
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable
  has_many :videos, dependent: :destroy
  has_many :likes, dependent: :destroy
  has_many :liked_videos, through: :likes, source: :video
　以下略
```

#まとめ
あまり見慣れないエラーだったので、少々焦りました。機能実装の際はコピペだけでなく、なぜこの記述が必要なのか理解する事も大事ですね。
ローカルでの動作確認も怠っていたので、次からはしっかり確認するように気をつけていこうと思います。
AWS認定ビッグデータ - 専門知識 勉強記~DynamoDB~

今回は DynamoDB の大容量データに関しての勉強記録となる.なお,記事は適宜更新していく.
また,DynamoDBの雑多なまとめ記事は[こちら](https://qiita.com/tada_infra/items/eca6455309a61c1db0e7)に書いている.

# ビッグデータ関連でのDynamoDB
DynamoDB は、分析、データウェアハウス、データのインポート/エクスポート、バックアップ、およびアーカイブの目的でEMR、Redshift、Data Pipeline、S3などの他のサービスと統合される

## 大きな項目と属性を格納するベストプラクティス
1つ以上の大きな属性を圧縮するか：圧縮するとコストが削減される。GZIPやLZOなどの圧縮はバイナリで、バイナリ属性タイプに格納できる
S3にオブジェクトとして格納して、メタデータをDynamoにいれるか

## 大容量ストリームのイベントの設計
・単一のWCUで捌ける各イベント項目のサイズ(1kb以下)を決める。
・1つの物理パーティション(10GB)に治るイベント項目の数を予測する
・毎秒書き込まれる受信イベント数を予測する
・期間ごとに1つのテーブルを作り直す
・古いデータは、S3やGlacierにアーカイブしてテーブルを削除する

## ユースケース
読み書きのレイテンシーが低く、コードの変更やダウンタイムを伴わずにストレージとスループットを必要に応じてスケールアップ/ダウンできる柔軟なNoSQLデータベースを必要とするアプリケーションに最適

* モバイルアプリ
* ゲーム
* デジタル広告サービス
* ライブ投票
* ライブイベントへのオーディエンス参加
* センサーネットワーク
* ログ処理
* ウェブベースコンテンツへのアクセスコントロール
* S3 オブジェクトのメタデータストレージ
* e コマースショッピングカート
* ウェブセッション管理
開発用メールサーバーMailSlurperをmacにいれる

# 開発用メールサーバーMailSlurperをmacにいれる

Macには最初からpostfixが入ってますが開発時に本当にメール送られても困るので、メールが送ったことにされるけど実際のメールアドレスには送られない開発用のSMTPサーバーをいれたいと思います。
今回は適当に検索したら一番上にでてきたGo製の *MailSlurper* というのを入れてみます。
Windows、Mac、Linux対応でライセンスはMITとなっています。

感謝感謝 :pray: :pray: :pray:


**追記**
*メールの本文にマルチバイト文字が入っているとうまく表示されないっぽいです。。。
ツライ。*

## ダウンロード

こちらが公式サイト。

[MailSlurper](https://mailslurper.com/)

こちらからダウンロードします。

[ダウンロード](https://github.com/mailslurper/mailslurper/releases)

今回はMacに入れますのでMac用をダウンロード。
自分がダウンロードしたversionは *1.14.1* でした。

## 解凍

zipなので解凍します。

それだけ:relaxed:


## 設定の編集

readmeを読むとconfig.jsonを編集しろと書いてあるので開きます。

```
{
	"wwwAddress": "localhost",
	"wwwPort": 8080, // WEBページで送信されたメールを確認するためのポート
	"serviceAddress": "localhost",
	"servicePort": 8085,
	"smtpAddress": "localhost",
	"smtpPort": 2500, // メール送信に使うポート。普通は25だけど初期値は大きな値になってます
	"dbEngine": "SQLite",
	"dbHost": "",
	"dbPort": 0,
	"dbDatabase": "./mailslurper.db",
	"dbUserName": "",
	"dbPassword": "",
	"maxWorkers": 1000,
	"autoStartBrowser": false,
	"keyFile": "",
	"certFile": "",
	"adminKeyFile": "",
	"adminCertFile": "",
	"authenticationScheme": "",
	"authSecret": "",
	"authSalt": "",
	"authTimeoutInMinutes": 120,
	"credentials": {}
}
```

今回は特にいじる必要ないのでスルー。
ローカルで何かをあげていて、ポートがぶつかる場合は適宜ずらしてくださいね！

## 起動

ターミナルで起動します。

```
$ cd /path/to/解凍したディレクトリ
$ ./mailslurper
```

これで起動します。
ターミナルが開きっぱなしになり、ログなどが流れていきます。
終了したい場合は `control + C` で普通に落とせます。

## 確認ページをひらいてみる

ではmailsplurperを起動した状態で画面を確認してみましょう。
ブラウザに下記のような感じでアドレスを入力して開きます。

```
http://<wwwAddress>:<wwwPort>/
```

config.jsonを何も変更してなければ下記のアドレスでアクセスできるはずです。

```
http://localhost:8080/
```

ひらけましたかね？

## メールの送信テスト

開発しているアプリでSmtpサーバーの設定をconfig.jsonに設定した感じにして送信してみてください。うまくいけばブラウザの確認画面をリフレッシュすると送ったメールが表示されているはずです。また、その際に本当の宛先にはメールは送信されてないことが確認できると思います。

もし何度リフレッシュしてもメールが確認できない場合はメールの送信に失敗している可能性があります。
起動時に使用したターミナルにエラーログが出ています。
ターミナルになんのログも出てない場合はそもそもmailsplurperのSmtpに到達できてないので開発しているアプリのSmtp設定を確認してください。

#### エラーのサンプル

fromを *kotolingo_test* にしたらメールアドレスの形式じゃないと怒られてしまいました。
（おなじプログラムでもpostfixだと送信できちゃうんだけども。。。）

```
ERRO[26798] Problem executing command                     command=SEND error="The provided email address, '<kotolingo_test>', is invalid" input="MAIL FROM:<kotolingo_test>" who="SMTP Worker 2"
ERRO[26798] Error executing command                       error="The provided email address, '<kotolingo_test>', is invalid" who="SMTP Worker 2"
```

こんな感じにエラーが出るのでもしうまくいかない場合はエラーログをチェックしてみましょう！

## おわり

これで開発時に間違ってメールボムしなくてすみますね！
開発の最後には実際のSMTPサーバーで送信テストはしましょう〜:runner:


:beer: :beer: :beer:
MacBook Proの2018で画面がちらつく

# 現象
こんな感じで、一瞬なんか四角のようなものがフラッシュする。
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">こんな感じでMacBook Pro 2018の画面に謎のフラッシュが入る…<br><br>ロジックボード交換しても直らない。 <a href="https://t.co/bmGXoz6CjU">pic.twitter.com/bmGXoz6CjU</a></p>&mdash; 長岡 英史 - 趣味が🎻なプログラマー (@hid9hiro) <a href="https://twitter.com/hid9hiro/status/1172079679504703488?ref_src=twsrc%5Etfw">September 12, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

# 出るタイミング
- 外付けモニターを繋いだ時？
- 再起動するとなくなる？
- SMCリセットでなくなったような？

# 試したこと
- ロジックボード交換
- OS入れ直し
- SMCリセット
- PRAMリセット
- 再起動

# 未だ直らず。直ったら報告します

# 参考
https://forums.macrumors.com/threads/macbook-pro-2018-screen-flicker-rf-interference.2160654/
https://support.apple.com/en-qa/HT202824#targetText=Put%20hard%20disks%20to%20sleep,only%20SSDs%20to%20store%20data.
#ruby #AWS |  aws-sdk-sqs のエラー |  Aws::SQS::Errors::IncompleteSignature: Credential must have exactly 5 slash-delimited elements

- 単にAWS_ACCESS_KEY_ID、AWS_SECRET_ACCESS_KEYの指定が間違っていたっぽい
- アクセスキーにシークレットを、シークレットにアクセスキーを書いていてしまっていたかも

# Original by Github issue

https://github.com/YumaInaura/YumaInaura/issues/2416








<!-- Update From Qiita API -->

# チャットメンバー募集


何か質問、悩み事、相談などあればLINEオープンチャットもご利用ください。

https://line.me/ti/g2/eEPltQ6Tzh3pYAZV8JXKZqc7PJ6L0rpm573dcQ





# Twitter


https://twitter.com/YumaInaura


<!-- Update From Qiita API -->
Herokuデプロイ後に出たエラーを解決するためにやったこと

以前作ったアプリが開発環境に問題が出てしまい、(AWSのターミナルが使えなくなりました）
https://qiita.com/acro_y/items/84f4d759ed01cee4bf52

そのためgit cloneをしてローカルで作り直すもとにかくエラーを吐き続けていましたが
やっとこの度エラーが解決できたのでまとめてみたいと思います。
#herokuでデプロイ後、開発環境のエラー勃発
この時点でherokuは使えるものの、２つのherokuのurlを有していました。
一つは最初に用意していた分、もう一つはエラーが止まらなかったので作り直した分。
そしてheroku自体は使える状態ではありましたので、エラーで止まっている方のurlを削除したところ
→互換性が何かあったのか、もう一方のherokuのurlのappも動かなくなってしまいました。
![スクリーンショット 2019-08-29 20.39.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139887/2e860399-ab7b-dfe1-3af8-063ab26fe9e0.png)

なんとか、herokuのappの復旧(この部分は色々やりすぎて思い出せない）
AWSのcloud9を使用していましたが、free spaceが無いということでターミナルが使えなくなりました。そのためAWSで新たなワークスペースを作る必要がありましたが、AWSのエラーは本当にこりごりだったので今度はローカルで作ることにしました。
#ローカルで作成の際Rubyのバージョンを上げた
最初、Ruby2.4.0で開発していました。
git cloneした時点でエラーが発生していましたので
単純にアップデートした方が何かと良いのではという、初心者ならではの根拠のあまり無い考えで最新の2.6.3にアップデート。
そうすることで今度はさらに多くエラーが出てしまいました。rails sしてもエラーを吐き続ける状態です。主にbootsnap関連のエラーです。
どうやらローカルであげたバージョンと他のgemと、何かしらのコンフリクトを起こしているらしい、、、！?
railsのバージョンもあげたり、とにかくエラーをググってみたりしてみましたがそのエラーは消えず。
すでに使ってくださってるユーザーさんもいたためherokuのコマンドが使えるまでは復旧できたので
herokuはロールバックしました。
（この時点でgithubにあげてる分と差異があります）
#git clone後に環境変数の設定を忘れていた。
herokuにそもそも環境変数の設定をするのを忘れていました。
git cloneし、新たにherokuで新しいURLになったため、herokuに環境変数がsetされていない状況でした。それらをセットし直しました。

```
$ heroku config:set AWS_ACCESS_KEY_ID=[自分のID]
$ heroku config:set AWS_SECRET_ACCESS_KEY=[自分のシークレットキー]
```
#githubをエラーが出てなかったところまで思い切ってロールバックする
何度git cloneをした事でしょうか。。。
これはgithubにあげてるcommitの本番環境も開発環境もエラーが出ていなかったところまで思い切って戻すことにしました。この時点でrails sコマンドは使えるようになりました。
#git cloneし、ローカルでrubyバージョンを初期状態に戻す
しかし、開発環境自体は使えるようになったものの、herokuにデプロイしてもアプリケーションエラーが止まらない。
Mentaというサービスでメンターさんに相談したところ、rubyのバージョンを戻してしまえば直るかもしれない。というアドバイスで、2.6.3にあげていたRubyをローカルのファイルではすでに2.4.0に戻し、
ローカルでも同じように戻しました。
そしてrubyのバージョンを戻したのにおかしい、、

```
 $ ruby -v
ruby 2.4.0p0 (2016-12-24 revision 57164) [x86_64-darwin17]
```
だけどまだエラーがでる。。ということでgemfile.lockのファイルに

```gemfile.lock
RUBY VERSION
   ruby 2.6.3p62

BUNDLED WITH
   2.0.2
```

これが悪さをしていたみたいです、、、！
これを決して、bundle installします。
bundle installをするとエラーが出ましたので、今度は

```
$bundle update
```

そしてgithubにpushした後

```
$ git push heroku master
```

で無事heroku にデプロイ後のアプリケーションエラーが消えました！

#やったことまとめ
- herokuに環境変数設定し直す
- gitのcommitを確認して、エラーが出てなかったところまでロールバックする（注意必要）
- rubyのバージョンを下げる
- gemfileだけでなくruby.rbファイルをruby下げ直したバージョンに書き直す、gemfile.lockの元のバージョンの記載があれば削除

ものすごく遠回りしましたが、エラーを読んでもググってもなかなか意味がわからなかった部分が多く
「なんだかわからないけどこうなった、、、」ってところがまだまだたくさんあります。
課題は残りますがなんとか開発が続けられるようになりました。
新しいURLはこちらです。
↓
[Poletricks](https://poletricks.herokuapp.com)
Google Maps Javascript APIメモ

踏み抜いたものを記録していく用

# Directions API

## 軌跡にそってMarkerを動かす

Directions APIのResultに細かいLatLngの配列がある。

たとえばlodashだと以下のようにして得られる

```ts
const stepPoints = _(result.routes[0].legs)
      .map(leg => leg.steps)
      .flatten()
      .map((step: any) => step.lat_lngs)
      .flatten()
      .map(latLng => ({ lat: latLng.lat(), lng: latLng.lng() }))
      .value()
```

`@types/googlemaps` の `DirectionsStep` にlat_lngsの定義が抜けていたのでとりあえずanyで逃げている。
factorybotでassociationつくりかた

### 要約
- 作り方メモ
  ([オフィシャルドキュメント](https://github.com/thoughtbot/factory_bot/blob/master/GETTING_STARTED.md#associations)のとおりなのでそちらを見たほうが良いです)
- でもあまり作らないほうがいい理由

### 作り方
- belongs_toの場合

```ruby
factory :post do
  # ...
  author #belongs_to名
end
```

- has_manyの場合

```ruby
after(:create) do |user, evaluator|
  create_list(:post, evaluator.posts_count, user: user)
end
```

### でもあまり作らないほうがいい理由
- テストが遅くなる<br>
あらゆるところでnestしてモデルが作られるので、遅くなります。factorybot基本的には遅いらしい。(これひとつとりあげてブログ記事になるくらい)<br>
[英語記事](https://evilmartians.com/chronicles/testprof-2-factory-therapy-for-your-ruby-tests-rspec-minitest)
[日本語記事]
(https://techracho.bpsinc.jp/hachi8833/2017_10_10/46290)
aws-vaultのmacOSのキーチェーンのタイムアウトを伸ばす

# はじめに
[aws-vault](https://github.com/99designs/aws-vault) はAWSのアクセスキーをキーチェーンに保存しています。

しかしながらmacOSのキーチェーンのデフォルトのタイムアウトが短すぎて頻繁にパスワードを求められて、普段遣いするにはちょっとうざいです。これ伸ばせないのかなと思ったらできたのでメモ。

# 環境

- macOS 10.14 (Mojave)
- aws-vault v4.6.4

# 解決方法

調べたところ、aws-vaultでmacOSのキーチェーンのパスワードが求められるのは、ちょっとわかりにくいんんですが、実は3種類あるようです。

[99designs/aws-vault #219: Constantly have to re-enter keychain password, part 2](https://github.com/99designs/aws-vault/issues/219#issuecomment-398243572)

## Keychain locked
=> タイムアウトを伸ばすことで解決可能

キーチェーンの設定を開きます。

```
$ open ~/Library/Keychains/aws-vault.keychain-db
```

「aws-vault」のキーチェーンを右クリックして、設定変更を選択。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61152/fa344195-90cb-9bb6-eeae-0e8b4dc5d4c0.png)

デフォルトのタイムアウトが5分になってるので、これを伸ばすか、

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61152/983b111a-2832-140b-f68b-6845a6d91e05.png)

もしくは、操作しない場合のロックのチェックを外して、スリープ時のロックだけにします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61152/dfa4b236-6f3a-70c5-cabb-d97186bfd7e5.png)

このへんは好みの分かれるところかもですが、個人的には、
基本的にノートPC開きっぱなしで離席することはない＆離席するときはノートPCを閉じてスリープさせればよい
という気もしていて、操作しない場合の自動ロックはなくてもよいかなというお気持ちです。
普段からAWSいじりまくってると思考を中断されるのもストレスフルだし。
まぁ利便性とのバランスなので、好みでどうぞ。
※ちなみにスリープ時のロックは、スクリーンセーバや画面ロックでは発動しないようなのでご注意下さい。

## Application access to a specific keychain
=> 「常に許可」を選択するとaws-vaultのバイナリが変わるまでは聞かれない。逆にいうと、バイナリが変わったら聞かれるようなので、「常に許可」にしてもリスクはない認識。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61152/aac10dd3-623d-04a1-638f-229e71641ea9.png)

## Application access to a specific item in a keychain
=> これが毎回聞かれるのは、aws-vaultのバグだったようで、v4.5.0以降で修正されているそうなので、aws-vaultのバージョンが古い場合はバージョンアップしてみて下さい。

# おまけ
aws-vaultのAWSレベルのAssumeRoleのセッションタイムアウトはデフォルトで15分になってますが、ちょっとなにか試行錯誤してるとすぐにタイムアウトして、微妙に短いと思うんですけど、どうなんでしょう。厳しすぎない？？そんなもん？？
この設定は、実行時の引数 `--assume-role-ttl` もしくは、環境変数 `AWS_ASSUME_ROLE_TTL` で上書き可能です。たとえば1hにする場合は以下のように設定します。

```bash
$ export AWS_ASSUME_ROLE_TTL=1h
```

1h以上にしたい場合は、Assume Roleの設定で、12hまでは伸ばせそうに見えます。
https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/id_roles_use.html

が、role chainingしていると1h以上には設定できないようです。

https://github.com/99designs/aws-vault/blob/6ef054e6e714994fbd110d30f118bddceee65aa1/USAGE.md#assuming-a-role-for-more-than-1h
PowerShellで「Firebase Functions + VSCode」をデバッグ実行する

# 概要

[こちらの記事](https://qiita.com/amay077/items/829630273721fb7a615e)を参考にして、Firebase FunctionsをVSCodeで開発している時に、
デバッグできるようにしたのですが、デバッグに行き着くまでのコマンドが多いので、
PowerShell一発実行でできるようにしてみたという話です。

**※ Windows限定です。**

# コード

```powershell:functions/debug.ps1
$ErrorActionPreference = "silentlycontinue"

Add-Type -AssemblyName Microsoft.VisualBasic
Add-Type -AssemblyName System.Windows.Forms

# 起動中のエミュレータを破棄
functions delete helloWorld
functions kill

# ビルド実行
npm run build

# エミュレータとデバッガ起動
functions start
functions deploy helloWorld --trigger-http
functions inspect helloWorld

# VSCodeのウィンドウをアクティブにする。
$codeProcesses = [System.Diagnostics.Process]::GetProcessesByName('code')
foreach ($process in $codeProcesses) {
  [Microsoft.VisualBasic.Interaction]::AppActivate($process.Id) > $null
}

# ここでF5キーを送信してVSCodeのデバッガと接続
[void][System.Windows.Forms.SendKeys]::SendWait('{F5}')

# callしたいfunctionを呼出す。
functions call helloWorld
```

# ポイント

VSCodeを開いた状態で、F5キー送信してデバッグ開始するとこまでをPowerShellで実現したかったんですが、
以下が実行されたタイミングでnode.jsのプロセスが立ち上がり、VSCodeのウィンドウは非アクティブになってしまいます。

その状態でF5キーを送信しても、node.jsのプロセスに対してF5キーを押す事になるのでデバッガが起動しません。

```powershell
functions inspect helloWorld
```

そこで、以下のコードで無理やりVSCodeのウィンドウをアクティブ化します。
**`Microsoft.VisualBasic`とか使ってるあたりに闇を感じますね。**

```powershell
# VSCodeのウィンドウをアクティブにする。
$codeProcesses = [System.Diagnostics.Process]::GetProcessesByName('code')
foreach ($process in $codeProcesses) {
  [Microsoft.VisualBasic.Interaction]::AppActivate($process.Id) > $null
}
```

# 補足

ちなみに、F5キー送信時は`[void]`不要なんですが、
QiitaのMarkdownの記法のせいなのか、付けないと行ごと表示されなかったです。
なんか`::`がダメっぽいですね。

```powershell
# ここでF5キーを送信してVSCodeのデバッガと接続
[void][System.Windows.Forms.SendKeys]::SendWait('{F5}')
```

# 最後に

VSCode複数起動してても行けるのかな？
グローバルホットキーアプリの小さなサンプル

## 概要

- Mac でも Windows でも
- Python 3.6
- wxPython 4.0.6

グローバルホットキーというものを知りました。どんなアプリがアクティブのときでも、ホットキー押下でプログラムを実行できるのです。 wxPython が持っているこの機能を使って、小さなサンプルを作ってみました。サンプルとして作ってみた機能は以下ふたつ。

- Alt+Space でデスクトップに空ファイル作成。
- Ctrl+Space で Qiita を開く。


## 小さなサンプル

![HotKeyApp.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/270842/ad712ad2-a787-801c-caa7-f5c2decb9075.jpeg)

```python
import wx
import os
import webbrowser


class Frame(wx.Frame):

    def __init__(self):
        super(Frame, self).__init__(None)

        # ここで Alt+Space のホットキーを登録しています。
        new_id = wx.NewIdRef(count=1)
        self.RegisterHotKey(new_id, wx.MOD_ALT, wx.WXK_SPACE)
        self.Bind(wx.EVT_HOTKEY, self.Foo, id=new_id)

        # ここで Ctrl+Space のホットキーを登録しています。
        new_id = wx.NewIdRef(count=1)
        self.RegisterHotKey(new_id, wx.MOD_CONTROL, wx.WXK_SPACE)
        self.Bind(wx.EVT_HOTKEY, self.Bar, id=new_id)


    def Foo(self, event):
        """新しいファイルをデスクトップに作ります。"""
        filepath = os.path.join(
            os.path.expanduser('~'),
            'Desktop',
            'NewFile.txt',
        )
        with open(filepath, 'w') as f:
            f.write('')


    def Bar(self, event):
        """Qiita を開きます。"""
        webbrowser.open('https://qiita.com/midoriiro')


app = wx.App(False)
Frame()
app.MainLoop()
```


## ちょっとした所感

### ウィンドウを表示しないくせに Frame を使っている件

`wx.Frame` はウィンドウを担当するクラスです。けれど今回は `frame.Show()` していないため、ウィンドウはどこにも表示されません。ならば `wx.Frame` を使う必要はないのでは? と思うのですが、そうもいきません。ホットキーを登録する `RegisterHotKey` は `wx.Frame` が持っているからです。正確には `wx.Frame` の上位クラスである `wx.Window`  が持っています。

ドキュメント: [https://docs.wxpython.org/wx.Window.html#wx.Window.RegisterHotKey](https://docs.wxpython.org/wx.Window.html#wx.Window.RegisterHotKey)

### 採用しているホットキーが微妙な件

なんで Alt+Space と Ctrl+Space やねんという話です。それは Windows で機能するホットキーがあんまりないからです。 Alt+Up Alt+Down とかにしたかったのだけれど、 Windows では反応しません。 Mac では反応するのに。

### ドキュメント読みづらい件

`RegisterHotKey` の項目に `virtualKeyCode` 一覧へのリンクはあっていいはず! むしろ未発見。どこにあるんですか? [wx.KeyCode](https://wxpython.org/Phoenix/docs/html/wx.KeyCode.enumeration.html) がそうかなと思ったけれど、 `RegisterHotKey` に使えないコードも含まれているし、そもそも存在しない定数も記載してある。(wxPython 4.0.6 にて)

このドキュメントの具合や、ネット上の記事の少なさから、開発終了したライブラリかと思ったのですがバリバリ現役ですね。

GitHub: [https://github.com/wxWidgets/Phoenix](https://github.com/wxWidgets/Phoenix)