OmniAICreator/Qiita-1.07M huggingface dataset:
mackerelでRailsのログを監視する

# 参考
[エージェントをインストールする \- Mackerel ヘルプ](https://mackerel.io/ja/docs/entry/howto/install-agent)
[チェック監視に公式チェックプラグイン集を使う \- Mackerel ヘルプ](https://mackerel.io/ja/docs/entry/howto/mackerel-check-plugins)
[ログ監視をおこなう \- Mackerel ヘルプ](https://mackerel.io/ja/docs/entry/howto/check/log)

# エージェントをインストール

mackerelの管理画面で新規ホスト登録用のコマンドを取得して、監視対象のホストで実行します。

![mackerel1.png](https://qiita-image-store.s3.amazonaws.com/0/141934/15660f23-de32-0a1e-3127-2e85d942d347.png)

# プラグインをインストール

ログ監視のためにはプラグインが必要になるため、インストールします。

rpmパッケージの場合

```shell-session
$ yum install mackerel-check-plugins
```

debパッケージの場合

```shell-session
$ apt-get install mackerel-check-plugins
```

# ログの監視設定を追加

mackerelの設定ファイルに監視ルールの名称、監視対象ファイルのパス、検出したいパターンを記述します。

```shell-session
$ sudo vi /etc/mackerel-agent/mackerel-agent.conf
```

以下のように記述すると、ログファイル`/my-app/log/production.log`に`ERROR`という文字列が出現すると`my-app_error`アラートが通知されるようになります。

```text:/etc/mackerel-agent/mackerel-agent.conf
[plugin.checks.my-app_error]
command = "check-log --file /my-app/log/production.log --pattern ERROR"
```

設定が完了したらエージェントを再起動します。

```shell-session
$ sudo service mackerel-agent restart
```

# アラートを確認

アラートはmackerelの管理画面で確認できます。

![mackerel2.png](https://qiita-image-store.s3.amazonaws.com/0/141934/69b62ca0-4654-78e2-e2e1-a37390f87c19.png)

通知を有効にすることでアラートをメールやSlackで受信できるようになります。

![mackerel3.png](https://qiita-image-store.s3.amazonaws.com/0/141934/44ac2858-a2a8-0ee0-a0b0-b916883dbca5.png)

# 補足

設定ファイルに記述した`check-log`コマンドにオプションを追加することで、検出したエラーの内容をmackerelで確認できるようにするなど、より細かい設定ができるようです。

参考：
[ログ監視をおこなう \- Mackerel ヘルプ](https://mackerel.io/ja/docs/entry/howto/check/log)

# 基本コマンド

```shell-session
# 起動・停止
$ sudo service mackerel-agent start
$ sudo service mackerel-agent stop
$ sudo service mackerel-agent restart
$ sudo service mackerel-agent reload

# 動作確認
$ sudo journalctl -u mackerel-agent.service

# 設定ファイル編集
$ sudo vi /etc/mackerel-agent/mackerel-agent.conf
```
phpbrewでmysqlndをインストールする

## 問題

phpbrewは，php拡張をインストール・管理する機能を提供しており，以下のようにphp拡張を追加することができます．

```bash
phpbrew ext install mysqlnd
```

しかし，macOS Sierra(10.12.6)にて，以下のようなエラーが出てしまい，インストールできない問題に遭遇しました．

```
configure: error: Cannot find OpenSSL's <evp.h>
```

この解決方法を書きます．

## opensslをインストールする
まず，mysqlndの依存ライブラリであるOpenSSLをインストールしておいてください．

```
brew install openssl
```

Mac OS XからOpenSSLのヘッダを提供しなくなったようです．
詳しい話はこちらを．

[Mac OS X El CapitanからOpenSSLのヘッダファイルが消えた話
](http://qiita.com/hnw/items/ac83e3ac8f9aa930b934)

## pkg-configのパスを設定する
以下のように，mysqlndをビルドする時に，brew経由でインストールしたOpenSSLを見つけられるように，`PKG_CONFIG_PATH`を指定しておいてください．

```
export PKG_CONFIG_PATH=/usr/local/opt/openssl/lib/pkgconfig
```

## mysqlndをインストールする

以下のコマンドで，mysqlndをインストールしてください．

```
PHP_OPENSSL_DIR=yes phpbrew ext install mysqlnd
```

`PHP_OPENSSL_DIR=yes`について，
[PHP :: Bug #72363 :: Cannot find OpenSSL evp.h](https://bugs.php.net/bug.php?id=72363&edit=3)

によれば，

> The cause of this is because after running the command 'phpize' the configure script has a variable (PHP_OPENSSL_DIR) that is not set "yes"

> 訳) この問題の原因は，`phpize`コマンドが走った後に，configureスクリプトが持つ`PHP_OPENSSL_DIR`が"yes"に設定されていないことだ．

とあります．
`PHP_OPENSSL_DIR`が"yes"に設定されていないと，pkg-configを利用した探索が行われません．

このバグを回避するために，`PHP_OPENSSL_DIR`を自分で設定しています．

なお，場当たり的な対処であるため，何かもっと良い解決法をご存じの方がいらっしゃいましたらご教授いただければと思います．
ESP8266 の起動

## モジュールに電源を入れる前に
- GND RXD TXD（I02) はPCとの通信に使います。PCとのコミュニケーションのために、USB端子とTTLシリアルケーブル (例えば[FT232](http://akizukidenshi.com/catalog/g/gK-01977/))  などを使ってつなぎます。

## プログラムをFlashメモリーに焼く方法
- 最初Flashメモリーの中身は空です。
- プログラムを焼くためには、モジュールをUART Download modeにします。
- IO15とIO0をlow-voltage levelにします。
- IO2は浮かせておきます。
- [flash downloading tools](http://espressif.com/en/support/download/other-tools)を使ってプログラムをFlashメモリーに焼きます。
- プログラムを焼いた後、 IO15をlow-voltage level, IO2 は浮かせたまま、IO0をhigh-voltage levelにします。
- リセットします。
- モジュールはUART Download modeからFlash Boot modeになります。


| | Flash Burn | Flash Boot |
|:---:|:---:|:---:|
| IO0 | Low | High |
| IO2 | NC | NC |
| IO15 | Low | Low |



 ![IMG_2864.JPG](https://qiita-image-store.s3.amazonaws.com/0/58417/d9925a7f-0a19-40ae-2c6d-39ac17bfddae.jpeg)

### プログラム転送エラー

Connecting........_____....._____....._____....._____....._____....._____....._____
A fatal esptool.py error occurred: Failed to connect to ESP8266: Timed out waiting for packet header

エラーが出る場合は大概、リセット時のピンの状態が決めらた値になっていないもとの思われます。シリアルが通っているかどうかは、シリアルモニターで確認できます。Baud Rate は初期状態で74880です。

```
xets Jan  8 2013,rst cause:2, boot mode:(1,6)
```

それでもプログラム転送失敗する場合は、プログラムの転送レートを遅めの57600とするとうまくいく場合があります。
VMnet1、VMnet8のネットワークアドレス変更メモ

# Windows

VMWare Workstation Playerなので、VMWare Workstationと勝手が違うかも知れませんが．．．


## 設定ファイル

* VMnet1を変更する場合、```C:\ProgramData\VMware\vmnetdhcp.conf```
* VMnet8を変更する場合、下記の2つのファイル
    * ```C:\ProgramData\VMware\vmnetdhcp.conf```
    * ```C:\ProgramData\VMware\vmnetnat.conf```

でネットワークアドレスを編集します。VMnet8はNAT機能がある分、ちょっとだけ面倒です。


## レジストリキーの変更

Linux、Macは設定ファイルの変更で終了ですが、Windowsでは、下記のレジストリキーの値を変更する必要があります。  

```
// VMnet1(HostOnly): 192.168.255.1(0xc0a8ff01(big) -> 0x01ffa8c0(little))
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\VMnetDHCP\Parameters\VirtualEthernetSegments\1]
"HostIpAddress"=dword:01ffa8c0

// VMnet8(NAT): 192.168.241.1(0xc0a8f101(big) -> 0x01f1a8c0(little))
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\VMnetDHCP\Parameters\VirtualEthernetSegments\8]
"HostIpAddress"=dword:01f1a8c0
```

両者で設定するIPアドレスが一致していないとVMNetDHCPとVMWare NAT Serviceが起動できないので、VMnetでDHCPとNATが使えなくなります。  
上記の例では、VMnet1の母艦PCのIPアドレスを192.168.255.1、VMnet8を192.168.241.1に設定しています。IPアドレスをリトルエンディアン（ホストバイトオーダー）で設定するところがポイントです。
このレジストリキーはVMnetDHCPとVMware NAT Serviceが起動時に参照するだけのようなので、レジストリキー変更後、OSを再起動する必要はないみたいです。


## NIC（VMnet1、VMnet8）再起動

VMnetの設定を変更し、各サービスを再起動しても、NICがアップしたままだとNICのIPアドレスが変更されません。コントロールパネルパネル等から```コントロール パネル\ネットワークとインターネット\ネットワーク接続```を開いてVMnet1とVMnet8を再起動させると設定ファイルとレジストリで設定したIPアドレスに変更されます。



# Mac

Mac版はVMWare Fusionですが、変更方法はだいたい同じです。

## 設定ファイル

* VMnet1を変更する場合、```/Library/Preferences/VMware Fusion/vmnet1/dhcpd.conf```
* VMnet8を変更する場合、下記の2つのファイル
    * ```/Library/Preferences/VMware Fusion/vmnet8/dhcpd.conf```
    * ```/Library/Preferences/VMware Fusion/vmnet8/nat.conf```

書式はWindows版VMWare Workstation Playerと同じで、ネットワークアドレスをキーに一括変換でいけると思います。


## /Library/Preferences/VMware Fusion/networking

VMWare FusionではmacOSのレジストリに当たるものの設定はありませんが、/Library/Preferences/VMware Fusion/networkingのネットワークアドレスを変更したアドレスに変更します。


## 設定の反映

VMWare Fusionを停止させ、再度起動すると、設定変更のネットワークアドレスでvmnetが起動すると思います。


# Linux

VMWare Fusionとだいたい同じ感じです。

## 設定ファイル

* VMnet1を変更する場合、```/etc/vmware/vmnet1/dhcpd/dhcpd.conf```
* VMnet8を変更する場合、下記の2つのファイル
    * ```/etc/vmware/vmnet8/dhcpd/dhcpd.conf```
    * ```/etc/vmware/vmnet8/nat/nat.conf```

vmnet[18]ディレクトリの下にサブディレクトリがあることと、dhcpd.leasesもconfと同じディレクトリに出力されるところが違います。


## /etc/vmware/networking

VMWare Fusionと同じで、/etc/vmware/networkingのネットワークアドレスを変更したアドレスに変更します。


## 設定の反映

たぶん、```$ sudo /etc/init.d/vmware restart```でいけると思いますが、未検証です。
vmnetを頻繁に設定変更することがないので、次に設定するときに検証しようかと思ってます。
Matplotlib | numpy > データの標準化(standardization)と結果の図示 | Error:AttributeError while adding colorbar in matplotlib

```txt:動作環境
GeForce GTX 1070 (8GB)
ASRock Z170M Pro4S [Intel Z170chipset]
Ubuntu 16.04 LTS desktop amd64
TensorFlow v1.1.0
cuDNN v5.1 for Linux
CUDA v8.0
Python 3.5.2
IPython 6.0.0 -- An enhanced Interactive Python.
gcc (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609
GNU bash, version 4.3.48(1)-release (x86_64-pc-linux-gnu)
```

@ Python機械学習プログラミング by Sebestian Raschkaら
p.38

> 本書に登場する機械学習のさまざまなアルゴリズムは、最適なパフォーマンスを実現するにあたって何らかの特徴量のスケーリングを必要とする。(中略) ここでは、標準化(standardization)というスケーリング手法を用いる。このスケーリング手法はデータに標準正規分布の特性を与える。その特性とは、各特徴量の平均を０として、標準偏差を１にするというものだ。(後略)

式 (2.5.6)

```math
x'_j = \frac{x_j - \mu_j}{\sigma_j}
```

### 試してみた

関連: [Matplotlib > Link > youtube動画 > Introduction to NumPy | SciPy 2015 Tutorial | Eric Jones](http://qiita.com/7of9/items/4ca5a0865ce7391007df)

上記でscatter()の例があったので、それを元にする。

Jupyter code.

```py:DataStandardization_170812.ipynb
%matplotlib inline

import numpy as np
import matplotlib.pyplot as plt


def normalize_data(x,y):
    print('x mean:%f' % x.mean())
    print('x std:%f' % x.std())
    print('y mean:%f' % y.mean())
    print('y std:%f' % y.std())
    x = (x - x.mean()) / x.std()
    y = (y - y.mean()) / y.std()
    return x, y

xorg = np.random.rand(200)
yorg = np.random.rand(200)
size = np.random.rand(200) * 30
color = np.random.rand(200)

xnrm, ynrm = normalize_data(xorg, yorg)

fig = plt.figure(figsize=(15,5))
fig.clf()

ax1 = fig.add_subplot(1, 2, 1)
cax1 = ax1.scatter(xorg, yorg, size, color)
plt.colorbar(cax1)

ax2 = fig.add_subplot(1, 2, 2)
cax2 = ax2.scatter(xnrm, ynrm, size, color)
plt.colorbar(cax2)

```

![qiita.png](https://qiita-image-store.s3.amazonaws.com/0/32870/af8b8c2f-74df-be6c-efe8-bc0c297b2b54.png)

### 補足 > Error:AttributeError while adding colorbar in matplotlib

ax1.colorbar()とするとエラーがでた。

https://stackoverflow.com/questions/2643953/attributeerror-while-adding-colorbar-in-matplotlib
に記載のコードを参照して、上記のように実装した。
dateのformat

# この記事について

本当は
http://php.net/manual/ja/datetime.createfromformat.php
を修正したかったけど、修正のしかるべき手順が全くわからず、修正しようとしたら変なエディタが開いて、セーブしたけどレビューへの回し方とか何もわからなかったのでイラッとして作ったチラシの裏

# 本題

http://php.net/manual/ja/datetime.createfromformat.php
date_create_formatについて

"fooおよびbar"という形式で、「先頭のゼロを含むものと含まないもの」という表記があるのだが、この順番がバラバラ
具体的には、

|format 文字|説明|取りうる値の例|
|:--|:--|:--|
|d および j|2桁の日付。先頭のゼロを含むものと含まないもの|01 から 31 あるいは 1 から 31|
|m および n|月を表す数値。先頭のゼロを含むものと含まないもの|01 から 12 あるいは 1 から 12|
|g および h|12 時間制での時間。先頭のゼロを含むものと含まないもの|1 から 12 あるいは 01 から 12|
|G and H|24時間制での時間。先頭のゼロを含むものと含まないもの|0 から 23 あるいは 00 から 23|

こういう表記は、巡り巡ってどこかでバグを生む可能性がある（もちろん **"悪い"** のは不注意な人だけど…一般論としてはA and Bに対してfooとbarという説明があったら、AがfooでBがbarと思う人は一定数いる）ので、修正した方がいいかなーと思ったけど、修正方法がわからず挫折して萎えた。そんなチラ裏。
DockerでGemfileの更新時のビルド時間を短縮する

# 前提

* すでにRailsプロジェクトは存在済みであることとします。
* バッドノウハウだと思いますので、ご注意ください。
* 余計なファイルを管理したくない場合はこの記事を見ない方がいいです。
* [高速に開発できる Docker + Rails開発環境のテンプレートを作った](http://qiita.com/kawasin73/items/2253523be18e5afd994f) がいい記事ですので、こちらを利用して開発を進めるのもいいと思います。
    * Entrykitについては、 [Entrykitのすすめ](http://qiita.com/spesnova/items/bae6406bf69d2dc6f88b) がわかりやすくていいです。


## はじめに

みなさん、Railsの開発をDocker上で開発する際に、Gemfileの更新をしますよね？

その場合Gemfileのcopyからやり直しになるので、bundle installが始めから走ってしまい、結構長い時間のロスになってしまうこともあるかと思います。

ここでは、決して褒められた方法ではないですが、Gemfile更新時のDockerimageの再ビルドの短縮方法について記載させていただきます。

## なぜ Gemfile を更新すると再buildに時間がかかってしまうのか？

例えば、以下のようなDockerfile[参考]があったとします

```zsh:Dockerfile
FROM ruby:2.3

RUN apt-get install hoge

# 中略

WORKDIR /app

# Gemfileのコピー
COPY Gemfile /app
COPY Gemfile.lock /app

RUN bundle install

# アプリのcopy
COPY . .

CMD bin/rails s
```

上記のようなDockerfileの場合、1回image化するとGemfile及びGemfile.lockの変更がない間は、「RUN bundle install」までがimageのcache化され、imageの再作成が早くなります。

しかし、GemfileやGemfile.lockが編集されてしまうと、「COPY Gemfile /app」や「COPY Gemfile.lock /app」も再作成のbuild対象となってしまうためそれより下の 「RUN bundle install」もcacheされていないこととなってしまい、再実行されてしまうため、時間がかかってしまうのです。

Gemfileの修正があまり発生しない場合はいいですが、比較的多めにGemfileの修正がある場合にはこの時間のロスは大きくなってしまいます。

## じゃあどうするか？

色々方法はあるかと思うのですが、以下のような方法でGemfileの更新の時間を短縮できます。

1. 現在時点のGemfile及びGemfile.lockを別名で保存する

    ```zsh
    % cp Gemfile .Gemfile.cache
    % cp Gemfile .Gemfile.lock.cache
    ```

2. Gemfileでbundle installを2回実行するように修正する

    ```zsh:Dockerfile
    FROM ruby:2.3
    
    RUN apt-get install hoge
    
    # 中略
    
    WORKDIR /app
    
    # Gemfileのキャッシュ化
    COPY .Gemfile.cache /app
    COPY .Gemfile.lock.cache /app
    
    RUN bundle install
    
    # Gemfileのコピー
    COPY Gemfile /app
    COPY Gemfile.lock /app
    
    RUN bundle install
    
    # アプリのcopy
    COPY . .
    
    CMD bin/rails s
    ```

以上


## なぜ短縮されるのか？

単純な話なのですが、.Gemfile.cache、.Gemfile.lock.cacheはGemfile及びGemfile.lockが修正されたとしてもcacheされたままですので、1回目の「RUN bundle install」もcache化されたままとなります。

GemfileとGemfile.lockの修正はcache化されませんが、1回目の「RUN bundle install」がcache化されているので、2回目の「RUN bundle install」は差分のinstallとなりますから、imageの再生成の速度がだいぶ短縮されます。

## 最後に

Dockerは便利ですよね。
いいDockerライフを過ごせますように。
Firebase Hostingを用いてHTMLホームページをインターネットに完全無料で公開する

## まえがき

FirebaseというサービスにはHTMLで作ったホームページをインターネット上に公開できる機能があります。しかも、Googleアカウントさえあれば、現在のところ誰でも無料で使えます。クレジットカードの登録も要りませんので、理論上は、子供でも使えます。

より正確には、Firebaseというサービスには、色々と機能があるわけですが、今回はそのひとつ、静的サイトのホスティング(Hosting)を利用しようというわけです。





## 対象読者

* Googleアカウントを持っている
* HTMLの初歩がわかる
* ターミナル(コマンドプロンプト)を触ったことがある

## この記事が扱うトピック

* Firebaseのはじめかた
* Firebase Hostingで静的サイトを公開する方法

## 1. Firebaseのプロジェクトをつくる

https://console.firebase.google.com/

に行けば、すぐにプロジェクトを作れます。
Googleアカウントにログインされている状態であれば、アカウント作成すら不要です。

![](https://i.imgur.com/oasGa6I.png)


## 2. CLIツールを入れ、ログインする

Node.js が入っていない人はここからインストールします。
https://nodejs.org/ja/

Firebaseの操作は、ターミナル(コマンドプロンプト)のコマンドでやるのが基本です。
インストールも、コマンドです。

```
npm install -g firebase-tools
```

ログインします。

```
firebase login
```


## 3. 公開するHTMLを用意する
公開したい index.html はこうなっているとします。

```
<h1>Hello Firebase!</h1>
```

このようなフォルダ構成であるとします。

```
├── firebase.json
└── public_dir
    └── index.html
```

firebase.json の中身は次のようにします。

```
{
  "hosting": {
    "public": "public_dir"
  }
}
```


## 4. デプロイする
さっき作ったプロジェクトの、IDを確認します。

```
$ firebase list
┌──────┬───────────────────────┬─────────────┐
│ Name │ Project ID / Instance │ Permissions │
├──────┼───────────────────────┼─────────────┤
│ neko │ neko-dcaa9            │ Owner       │
└──────┴───────────────────────┴─────────────┘
```

IDを指定して、デプロイします。

```
$ firebase deploy --project neko-dcaa9

=== Deploying to 'neko-dcaa9'...

i  deploying hosting
i  hosting: preparing public directory for upload...
✔  hosting: 1 files uploaded successfully
i  starting release process (may take several minutes)...

✔  Deploy complete!

Project Console: https://console.firebase.google.com/project/neko-dcaa9/overview
Hosting URL: https://neko-dcaa9.firebaseapp.com
```

これで、

https://neko-dcaa9.firebaseapp.com

に公開されました。

## 5. ホームページ内容の更新

かわいい写真をつけたくなったとします。
写真のjpgファイルをimgフォルダの中に置きます。
それから、写真のためのタグを追記します。

```
<h1>Hello Firebase!</h1>
<img src='img/neko.jpg'>
```

フォルダ構成が、このようになりました。

```
├── firebase.json
└── public
    ├── img
    │   └── neko.jpg
    └── index.html
```

いよいよ更新です。といっても、同じコマンドです。

```
firebase deploy --project neko-dcaa9
```

![](https://i.imgur.com/rrIKGuQ.jpg)
Azureの仮想マシンの外部からのアクセスを制限する方法(ネットワークセキュリティグループ)

## AzureVMのセキュリティについて
Azureの仮想マシンを管理・運用していると、
セキュリティの観点で社内からのアクセスに限定したい要望が出て来るかと思います。
他にもsshでのアクセスは禁止にしたいときや特定のポートからのアクセスを許可・拒否したい場合も出てくるかと思います。
そんな時に簡単にポートやプロトコルレベルの制限を付け加えることができる**NSG(ネットワークセキュリティグループ)**というものをご紹介します。

## NSG(ネットワークセキュリティグループ)とは？
仮想マシン(v1)や仮想マシンに紐付いたNIC(v2)やサブネットに紐付けることでプロトコルやIPなどで受信・送信の制限を設定できるものです。
仮想マシン内でポートを開け閉めすることなく、セキュリティ規則をAzureのリソースでモジュール化できるので複数台への設定の適用も簡易になります。

## 利用方法
1. 例えば仮想マシンsampleというものを作成するときに
NSGの設定も付与されます。
AzurePortal左のタブから盾のアイコンを探してみましょう
![nsg1.png](https://qiita-image-store.s3.amazonaws.com/0/123580/fcee7260-c8d1-fd62-f3b6-6cee4f0dc414.png)

sample-nsgをクリックしてみると以下のような画面になります。
![nsg2.png](https://qiita-image-store.s3.amazonaws.com/0/123580/9515b099-5c8f-ddf6-2ba7-c0dc80dfe394.png)
仮想マシンsampleはLinuxのマシンでしたので、デフォルトでsshのポート22は開放されています。
これがWindowsServerの場合だとPowerShellとRDPについてデフォルトでポート開放されています。
2. プロトコル(ポート)の送信・受信レベルで許可と制限ができます。ここではhttpの通信の送信(仮想マシンsampleから外部)を禁止に設定してみます
3. 「送信セキュリティ規則」をクリックした後に、「新規」をクリック
4. ルールの名前を決め、設定していく 
・「優先度」...数字が小さいほどそのルールが優先されます。例えばあるIPアドレスからのhttp受信を許可するルールの優先度が100ですべてのIPアドレスからのhttp受信を拒否するルールの優先度が200の場合、前者のルールが優先されます。
・「宛先」...送信する先。「CIDR block」でIPアドレス空間を指定します。「Tag」はAzureのリソースに割り当てることができるものですが、利用しなくてもいい。
・「サービス」...以下参照
http=80,ssh=22など標準のポートが紐付けられている。
sshのポートを変更している場合などは、「カスタム」を選択することでポートを特定し制限できる
![nsg5.PNG](https://qiita-image-store.s3.amazonaws.com/0/123580/d73596c4-18b6-8ee1-fd9c-0ccbefda41cc.png)

設定が完了し以下のような画面で「OK」を押す
![nsg6.png](https://qiita-image-store.s3.amazonaws.com/0/123580/aa807b06-55a2-dbe5-3adf-b163e49a3442.png)

1分ほどで以下のような送信規則が作成されたら終了
![nsg7.png](https://qiita-image-store.s3.amazonaws.com/0/123580/6a04dac9-d238-214b-3bf4-2385b31234a7.png)

ポートやアクセス制限はたいていどこの会社もきっちりとやっているし、
Azureのサービスにロックインされることを考えると積極的に利用するかどうかは判断が別れるでしょうが、
開発環境などであれば知っていることで簡単にセキュリティレベルを上げることができるのでおすすめのサービスです。
CircleCI + bitbucketの連携でハマった

# 概要

privateリポジトリに対してCIを行いたくて、CircleCI + bitbucketの連携を試みた。CI対象のbitbucketリポジトリはsubmoduleによって、他のbitbucketリポジトリに依存している。この場合、プロジェクト作成時のデフォルト設定だとうまく動作しない。

# 結論

秘密鍵A/公開鍵Aのペアを用意し、CircleCI, bitbucketでそれぞれ以下の設定を行えばよい。deploy keyが残っていたり、user keyが無かったりするとうまくいかない。

- CircleCI
   - プロジェクト設定 
      - PERMISSIONS
         - Checkout SSH keys
             - **deploy key 削除**
             - **user key 追加**
         - SSH Permissions
             - **秘密鍵A追加**
- bitbucket
   - Bitbucket settings
      - セキュリティ
         - SSH 鍵
            - **公開鍵A追加**

# 参考リンク


- [Using user key for git submodule but still getting access denied](https://discuss.circleci.com/t/using-user-key-for-git-submodule-but-still-getting-access-denied/10486)
rails plugin new 躓いたところ


https://railsguides.jp/plugins.html

これを参考にpluginを作ってみようと思ったが、全くチュートリアル通りにいかないので、困った。
railsにプルリクを送りたいが、ルールがめんどいし、英語できないから誰かお願いします。

- rails 5.1.3
- bundler 1.15.3
- ruby 2.4.1

## > 1.1 gem形式のプラグインを生成する
```:console
$ rails plugin new yaffle
```
動いた。
## > 2 新しく生成したプラグインをテストする
```:console
$ cd yaffle
$ bundle install
You have one or more invalid gemspecs that need to be fixed.
The gemspec at ~/yaffle/yaffle.gemspec is not valid. Please fix this gemspec.
The validation error was '"FIXME" or "TODO" is not a description'

```

いきなりドキュメントの通りにいかない。ドキュメントにプルリク出したい。
以下対策。

```:yaffle.gemspec
@@ -9,9 +9,9 @@ Gem::Specification.new do |s|
   s.version     = Yaffle::VERSION
   s.authors     = ["user"]
   s.email       = ["xxx@example.com"]
-  s.homepage    = "TODO"
-  s.summary     = "TODO: Summary of Yaffle."
-  s.description = "TODO: Description of Yaffle."
+  s.homepage    = ""
+  s.summary     = "Summary of Yaffle."
+  s.description = "Description of Yaffle."
   s.license     = "MIT"

   s.files = Dir["{app,config,db,lib}/**/*", "MIT-LICENSE", "Rakefile", "README.md"]
```

```:console
$ bundle install
```

成功

## > 自動生成されたテストをrakeコマンドで実行します
```:console
$ bundle exec rake test
~/yaffle/test/dummy/db/schema.rb doesn't exist yet. Run `rails db:migrate` to create it, then try again. If you do not intend to use a database, you should instead alter ~/yaffle/test/dummy/config/application.rb to limit the frameworks that will be loaded.
# ...略...
```

よくわからないけどdummyプロジェクトのdbを作ってくればいいのかな。

```:console
$ cd test/dummy
$ bin rails db:migrate
$ cd -
```

リトライ

```:console
$ bundle exec rake test
~/yaffle/test/test_helper.rb:9:in `<top (required)>': uninitialized constant Rails::TestUnitReporter (NameError)
```

なんなんだよぉ〜これもぉー。github行って該当クラス調べたら[定義はされている](https://github.com/rails/rails/blob/master/railties/lib/rails/test_unit/reporter.rb)けど[requireされてない](https://github.com/rails/rails/blob/master/railties/lib/rails/application.rb)みたいなので、以下のように修正してみた。
ただし、railtiesの構造がよくわかっていないので、これで良いのか自信がない。

```:test_helper.rb
@@ -6,6 +6,7 @@ require "rails/test_help"
 # to be shown.
 Minitest.backtrace_filter = Minitest::BacktraceFilter.new

+require 'rails/test_unit/reporter'
 Rails::TestUnitReporter.executable = 'bin/test'

 # Load fixtures from the engine
```

リトライ

```
$ bundle exec rake test
Run options: --seed 10841

# Running:

.

Finished in 0.003075s, 325.2033 runs/s, 325.2033 assertions/s.
1 runs, 1 assertions, 0 failures, 0 errors, 0 skips
```

やったー！

## とりあえずここまで

何も始まっていないのに疲れてしまった！！
flaskでpostされた画像をopencvで扱えるように読み込む

postされた`StrageFile`状態の画像データを読み込んで、npのarrayの形式に変換します。
変換されたものをopencvでカラー画像として読み込んで、opencvで扱える形式になります。
僕の例だとflaskを使っていますが、おそらく他のフレームワークでも同様の方法が使えると思います。

```python
@app.route('/upload', methods=['POST'])
def upload():
    if request.files['file'].filename != u'':
        file_data = request.files['file'].read()
        nparr = np.fromstring(file_data, np.uint8)
        img = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
```

参考：https://gist.github.com/kylehounslow/767fb72fde2ebdd010a0bf4242371594
うんこAIドリルを作りました。夏休みの宿題にどうぞ。

昨日非難受けたらどうしようと、

ドキドキしながら"うんこAIドリル"の投稿したのですが、

何と友人のLouiseが自発的にデザイン起こしてくれました！

という事で、皆さんの夏休みの宿題にどうぞ。

Thank you Louise!


![20664833_10211935006203563_974669737508835220_n.jpg](https://qiita-image-store.s3.amazonaws.com/0/76304/c672934e-0102-518a-d7dc-2c638716e8e9.jpeg)

＊答えは今度投稿します。
pyenv環境下のpython3でsmbusを使う

pyenv環境下のpython3で

```
sudo apt-get install python-smbus
```
を実行してもsmbusがないとエラーが出た．

# 解決法

```
pip install smbus2
```

でsmbusを入れて

```
import smbus2 as sambas
```

と使えば，従来通りに実行できる．
Reproでプッシュ通知を受け取る際にSmall Iconが表示されない場合の対処方法

# 概要

[Repro](https://repro.io/jp/)でプッシュ通知を受け取る際に、`NotificationCompat.Builder`の`setSmallIcon`でiconのリソースを指定していても通知に表示されない場合の対処方法について。

# 対処方法

AndroidManifestのapplicationタグに以下のようなmeta-dataを追加します。

```xml
<meta-data
    android:name="io.repro.android.PushNotification.SmallIcon"
    android:resource="@drawable/YOUR_ICON_ID">
</meta-data>
```

これで表示されるはずです。

# ドキュメントはちゃんと読もう

30分ぐらい悩んでたんですが、Reproの[ドキュメント](http://docs.repro.io/ja/dev/sdk/push-notification/android.html#androidmanifest-xml)を読んだらあっさり解決しました。ドキュメントはちゃんと読もう・・。


# 参考

http://docs.repro.io/ja/dev/sdk/push-notification/android.html#android
軽量ORMの Idiorm でwhere_inした順に並べ替えする（任意順でorder_by）

# なにがしたい？

たとえばランキングや閲覧履歴などを表示しようとDBからレコード一覧を取得するとき、任意のレコードが、**任意の順番で**出てきて欲しいというケース。どうやって order_by するのか？ というのを、SQLではなくてORMのIdiormでやってみます。

# 結論

## order_by_expr で生SQLを書きます

Idiormにそんな便利機能が備わっていないだけでなく、SQLとしても全SQL共通の標準機能で備わっていないので、利用しているDBに応じて生 ORDER BY を書いて上げる必要があります。
 
### MySQLの場合

FIELD関数を使用して `ORDER BY FIELD( column, data1, data2 .... )` と書きます。dataは単純にimplodeすれば大丈夫なので、下記のようになります。

```php
$items = MyModel::where_in('id',$dataArray)
  ->order_by_expr( 'field( id, '.implode(',',$dataArray).')')
  ->find_many();
```

### PostgreSQLの場合

CASEを使用して `ORDER BY CASE WHEN column=data1 THEN 0 WHEN column=data2 THEN 1 ... ELSE N END`と書きます。長いのでヘルパー関数を用意してFIELDのように簡潔に書くのがオススメ。 

```php
$items = MyModel::where_in('id',$dataArray)
  ->order_by_expr( postgres_order_by_array('id',$dataArray) )
  ->find_many();

function postgres_order_by_array( $column, $array ){
	$expr = 'CASE ';
	foreach( $array as $idx => $val ) $expr .= "WHEN $column='$val' THEN $idx ";
	$expr .= 'ELSE '.($idx+1).' END';
	return $expr;
}
```

ELSE N という部分は、where_in と併用している場合は ELSE に該当するレコードは出てこないので適当でも大丈夫のはずですが、念のため付ける場合、 0 としている文献もありますが、想定外のレコードは後ろに並んでほしいので最大値を振ったほうが良いと思います。

# 余談

Laravel の場合は `orderByRaw` というメソッドがあるので、それで同じようにします。最近、プロジェクトAではLaravel＋MySQLを使い、プロジェクトBでは環境の制約でLaravelが使えないので「IdiormをLaravel風に使う」（しかもPostgreSQL）という、個人的には二度手間でしか無い車輪の再発明的なことを積極的にやっています…。これはその奮闘の記録です(笑)。

つづく……。
Djangoで実行されるSQLを確認する（SELECT編）

# はじめに

[前にINSERTのSQLを表示してみました](http://qiita.com/junjis0203/items/8ce6fcfe857e45dc8561)が今回はより使い道がありそうなSELECT文を表示してみます。

# SELECT文を表示する方法

わかってしまうと非常に簡単でした。

```py:sql_dumper.py
def show_select_sql(queryset):
    compiler = queryset.query.get_compiler(using=queryset.db)
    return compiler.as_sql()
```

これを導くまでの話は[こっちの読解記事](http://www.dzeta.jp/~junjis/code_reading/index.php?Django%2F%E3%83%A2%E3%83%87%E3%83%AB%E6%A4%9C%E7%B4%A2%E6%99%82%E3%81%AE%E5%87%A6%E7%90%86%E3%82%92%E8%AA%AD%E3%82%80%EF%BC%88all%EF%BC%89)を参照してください。

# 生成されるSQLの確認

[Djangoチュートリアル その2](https://docs.djangoproject.com/ja/1.11/intro/tutorial02/#playing-with-the-api)に載っている検索がどういうSQLになるのか試してみましょう。

## all

```py
>>> from sql_dumper import show_select_sql
>>> from polls.models import Question
>>> show_select_sql(Question.objects.all())
('SELECT "polls_question"."id", "polls_question"."question_text", "polls_question"."pub_date" FROM "polls_question"', ())
```

## id指定

```py
>>> show_select_sql(Question.objects.filter(id=1))
('SELECT "polls_question"."id", "polls_question"."question_text", "polls_question"."pub_date" FROM "polls_question" WHERE "polls_question"."id" = %s', (1,))
```

## CharFieldの開始文字列指定

```py
>>> show_select_sql(Question.objects.filter(question_text__startswith='What'))
('SELECT "polls_question"."id", "polls_question"."question_text", "polls_question"."pub_date" FROM "polls_question" WHERE "polls_question"."question_text" LIKE %s ESCAPE \'\\\'', ('What%',))
```

## DateTimeFieldの年指定

```py
>>> from django.utils import timezone
>>> current_year = timezone.now().year
>>> show_select_sql(Question.objects.filter(pub_date__year=current_year))
('SELECT "polls_question"."id", "polls_question"."question_text", "polls_question"."pub_date" FROM "polls_question" WHERE "polls_question"."pub_date" BETWEEN %s AND %s', ('2016-12-31 15:00:00', '2017-12-31 14:59:59.999999'))
```

問題なく出てますね。WHEREの部分についてはまだコード読んでないのでどう頑張ってこのようなSQLを構築しているのかを見るのもおもそしろそうです。
Redmineでプラグインを簡単に試す

# Redmineのプラグインを気軽に試す手順

1. 仮想環境にRedmineを入れる
手順は以下に記載しています。
http://qiita.com/yukiyoshimura/items/3317a6af7cf1ba5430e1

2. プラグインを入れる
今回はredmine_backlogs(https://github.com/backlogs/redmine_backlogs/)  を入れてみます。

```bash:terminal
# 仮想環境につなぐ
vagrant ssh 
# apacheユーザに切り替え
sudo su -s /bin/bash apache
# パスをセット
export PATH=$PATH:/usr/local/bin
# プラグイン配置先フォルダに移動
cd /var/lib/redmine/plugins
git clone https://github.com/backlogs/redmine_backlogs.git
## redmine3系の場合のみ以下作業を実施 ##
cd redmine_backlogs
git checkout feature/redmine3 #　→redmin3.x用のブランチ
cd ../
####################################
cd /var/lib/redmine
/usr/local/bin/bundle install
/usr/local/bin/bundle exec rake redmine:plugins:migrate RAILS_ENV=production
# apache再起動
sudo apachectl restart
```

apache再起動後、redmineにアクセスしてみるとプラグインのところで```redmine_backlogs```が追加されていることが確認できます。

※```bundle exec rake redmine:plugins:migrate RAILS_ENV=production```で以下メッセージが出る場合

```bash:
There was an error parsing `Gemfile`: You cannot specify the same gem twice with different version requirements.
```
地道に```/var/lib/redmine/plugins/redmine_backlogs/Gemfile```の該当gemのバーションを合わせていきます。または該当gemを削除してもOK。
OpenAI GymのCopy-v0を解く

## 課題

- コピーしたい文字列がテープに書かれているので，移動と書き込みを使ってコピーする．
- 成功するたびにコピーする文字列は長くなる．

https://gym.openai.com/envs/Copy-v0

![image.png](https://qiita-image-store.s3.amazonaws.com/0/107399/528cc435-a039-52c2-fdef-9c2df4934293.png)



### クリア条件

- 直近100回の試行で25以上の報酬を得ること．

### 報酬

- 正しくコピーできたら1.0, 間違えたら-0.5

### データ構成

コードを読んでみると，

https://github.com/openai/gym/blob/master/gym/envs/algorithmic/copy_.py


行動空間は

```
Tuple(Discrete(2), Discrete(2), Discrete(5))
```

- タプル1つめ: テープの右に行きたいなら1, 左なら0
- タプル2つめ: 書き込みなら1
- タプル3つめ: 書き込む文字を数値に変換した値(1〜5の数字で表現されている)

### 状態空間

- A〜Eの5文字(1〜5の数字で表現されている)

```
Discrete(6)
```

## 解法

- そのまま前の状態を次の行動に突っ込んであげたら良いが，強化学習使ってないので多分違う．

### コード

```python
import numpy as np
import gym
from gym import wrappers

def run():
    env = gym.make('Copy-v0')
    env = wrappers.Monitor(env, '/tmp/copy-v0', force=True)
    Gs = []
    for episode in range(1000):
        x = env.reset()
        G = 0
        for t in range(100):
            a = (1,1, x)
            x, r, done, _ = env.step(a)
            G += r
            if done:
                Gs.append(G)
                break
        score = np.mean(Gs[-100:])
        print("Episode: %3d, Score: %.3f" % (episode, score))
        if score > 25:
            break


if __name__ == "__main__":
    run()
```

## References

1. [LEARNING SIMPLE ALGORITHMS FROM EXAMPLES](http://arxiv.org/abs/1511.07275), Zaremba et al., 2016.
2. [OpenAI Gym](https://github.com/openai/gym/blob/master/gym/envs/algorithmic/copy_.py), Brockman et al., 2016.
fileinput — 標準入力でデータ件数が分からない複数データの入力

**import fileinput**
for line in **fileinput.input():** #3,4,5
    >>**if line == "\n":
        >>>>break**
    >>else:
        >>>>a, b, c = map(int, line.split(","))
print(a, b, c) #3 4 5
PythonでWeb framework を作ろう！ （１）

折角のお盆休みですので、PythonでWeb Frameworkを作成することにしました。
以前にも、PHPでRoRのデッドコピーなFrameworkを作ったことがあるので、今回はPythonで作ってみようというのが今回の趣旨です。

#Hellow world!
まず、WorldにHellowしないと始まりません。
挨拶はとても重要ですからね。（謎）
下記のサイトを参考にさせていただきました。

EC2にnginx+uwsgi+python でHello World
http://qiita.com/chunkof/items/6c9d4b01f0057a9a8de0

ただ、このサイトの記述には抜けがあるようです。
まず、nginxの設定です。下記のとおりVirtualHostの設定をします。
nginxのインストールとファイル位置の記述は省きます。

```strangerpy.conf
server{
    listen 80;
    server_name    strangerpy.example.com;

    location / {
        include uwsgi_params;
        uwsgi_pass 127.0.0.1:3031;
    }

    location = /favicon.ico{
      empty_gif;
    }
}
```

次にuwsgiの設定ファイル

```uwsgi.ini
[uwsgi]
master = True
socket = 127.0.0.1:3031
wsgi-file = index.py
stats = 127.0.0.1:9191
logto = uwsgi.log
pidfile = uwsgi.pid

```

プログラムのソースファイル

```index.py
# index.py
# coding:utf-8
import logging
import datetime
import uuid

handler = logging.StreamHandler()
handler.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s'))
logger = logging.getLogger()
logger.addHandler(handler)
logger.setLevel(logging.INFO)

def main():
    # log
    logger.info('test-log-dayo')

def application(env, start_response):
    main()
    start_response('200 OK', [('Content-type', 'text/html')])
    str = "<html><head><meta charset='utf-8'><title>Home</title></head><body>はろーわーるど！</body></head>".encode("utf-8")
    return [str]
```

一通り記述したら、index.pyやuwsgi.iniをおいてあるディレクトリに移動しあ上で
下記の呪文を唱えてuwsgiを起動してください。

```
uwsgi --ini uwsgi.ini
```

/etc/hostsなどにサーバの名前解決の記述など書かないといけないのですが、それも省きました。
本論とは関係ないので。

http://strangerpy.example.com/ のURLを用いてブラウザで「はろーわーるど！」と出たら成功です。多分。
【Java】コメントの種類と書き方

:beginner:新人研修のソースレビューで毎回指摘してるので、新人さん向けに整理してみました:beginner:

# Javaのコメントの種類
* １行コメント
* 複数行コメント
* JavaDoc

## １行コメントの書き方
```
// コメント
```
eclipseだと` ctrl + /`で入れたり消したりできる。

## 複数行コメントの書き方
```
/*
* 先頭のアスタリスクは１つ
* comment
*/
```

## JavaDoc
```
/**
 * クラスやメソッドの説明
 * 先頭はアスタリスク２つです。
 */
```
前述した２つとは性質の違う特別なコメント。
JavaDocコメントはjavaのコマンドを使ってドキュメント化できる=リファレンスマニュアルになる。
そのため、いろんなタグが使える。
マニュアルなので、ソースコード内でちょっとした説明を書くときには使ってはいけない。

### タグの例
| タグ | 説明 |
|:--|:--|
| `@param` | パラメータ（引数）についての説明。 |
| `@return` | 戻り値についての説明。 |
| `@author` | プログラムの作成者についての説明。 |
| `@throw` | 投げられる例外についての説明。 |
EdgeRouter X でホスト名の静的マッピングを行う

# 背景
外部からドメインでサーバへアクセスできるが、LAN内からはローカルIPアドレスを指定する必要がある。
これを解決するためにはLAN内にBINDなどのDNSをサーバを立てる必要がある。
しかし、BINDの脆弱性なども盛んに議論になっていて奥深そうでめんどくさそうだ。
そこで、EdgeRouter X のstatic-host-mappingで解決を行う。

# 行った設定
[hostname]を192.168.0.2に割り当てる。

```
# set system static-host-mapping host-name [hostname] inet 192.168.0.2
```
javascript3分クッキング「不変オブジェクト」

参考：[パーフェクトjavascript](https://www.amazon.co.jp/dp/B00P2EG5LC/ref=dp-kindle-redirect?_encoding=UTF8&btkr=1)

#不変オブジェクト

##定義

不変オブジェクトとは精製後に状態が変更されないオブジェクトのことである。
広義には、状態を変更しないオブジェクトを不変オブジェクトと呼ぶ。
狭義には、変更しようにも変更できないオブジェクト、つまり変更禁止のために細工をしたオブジェクトを指します。

##作り方

javascriptオブジェクトを不変オブジェクトにするには次のような手がある。

-　プロパティ（状態）を隠し、変更操作を提供しない（jsにはプロパティを隠すために使える技法のひとつにクロージャがある）
-　ECMAScript第５版の関数を活用する
-　writable属性、configurable属性、セッター、ゲッターを活用する

##関数で不変オブジェクト化

メソッド名|プロパティ追加|プロパティ削除|プロパティ値変更|確認メソッド
---|:---:|:---:|:---:|---
preventExtensions|x|o|o|Object.isExtensible
seal|x|x|o|Object.isSealed
freeze|x|x|x|Object.isFrozen

内部的に、sealはプロパティのconfigureable属性を偽に、
freezeはwritable属性を偽にしている。
以下のコードのように、
オブジェクト生成時にこれらの属性を明示的に指定することでも同じ効果を得られます。


```js
var x = { x:2, y:3}
var y = Object.create(
            Object.prototype,
            {
               x: {value:2, writable:true, enumerable:true, configurable:true},
               y: {value:3, writable:true, enumerable:true, configurable:true}
            }
      　　 );
```

#具体例

##Object.preventExtensions

http://qiita.com/namitop/items/78aea01125876a214887

##Object.seal

http://qiita.com/namitop/items/d3d1c463958c0f83e762

##Object.freeze

http://qiita.com/namitop/items/674f70b64646117be73b

#関数で不変オブジェクト化するときの注意

- 変更すると元に戻せない

http://qiita.com/namitop/items/0d6afa8711ccb89b26ec

- プロトタイプ継承元も不変にしたければ、継承元に対して明示的に行う必要がある

http://qiita.com/namitop/items/6b035102585a59f65e5e

#パーフェクトjavascriptにあった不変オブジェクトに対する「助言」

```math
「不変オブジェクトをあきらめる」
```

堅牢で安全なプログラムを作るのはコストとのトレードオフ。
安全さのために時間がかかりすぎるかもしれない。
javascriptのようにコードサイズの制約の厳しい環境では堅牢さのために書いたコードが利用者の快適な体験を妨げるかもしれない。

堅牢さとコストのバランスを考えて使うようにすること。
EdgeRouter X で外部側のポートをLAN内サーバのポートへ転送する設定

# 背景
WAN側に来たポート80,443のパケットをLAN内のサーバに転送する設定。
例のごとくCLIで設定を行う。
[前回までの設定](http://qiita.com/maiani/items/08dbfbd9e6663da86079)を前提としている。
サーバのIPアドレスは192.168.0.2/24。

# Firewallの設定

```
# set firewall name WAN_IN rule 30 action accept
# set firewall name WAN_IN rule 30 destination address 192.168.0.2
# set firewall name WAN_IN rule 30 destination port 80,443
# set firewall name WAN_IN rule 30 protocol tcp
```

上記設定、ご指摘いただいたので修正した。
```
- # set firewall name WAN_IN rule 30 destination address 192.168.0.2/24
+ # set firewall name WAN_IN rule 30 destination address 192.168.0.2
```
特定のホストのネットマスクなんてルータからしたらどうでもよく、指定しなくてもよいそう。
ホスト単体を指定するなら、192.168.0.2/32が正しい。
ネットワーク範囲なら192.168.0.0/24が正しい。
今回の場合は、ホスト単体を指定しているがなしでもいけるので上記のようにした。

# NATの設定
「vyOSと同じだろう」と思って試したが、NATがなかったので焦った。
よく見たらservice内にあったので解決。
ポート80と443の記述が冗長な気がする。
あと、ポートの指定も。

```
# set service nat rule 10 description "port forward to Server (http)"
# set service nat rule 10 destination port 80
# set service nat rule 10 inbound-interface pppoe0
# set service nat rule 10 inside-address address 192.168.0.2
# set service nat rule 10 inside-address port 80
# set service nat rule 10 protocol tcp
# set service nat rule 10 type destination
# set service nat rule 20 description "port forward to Server (https)"
# set service nat rule 20 destination port 443
# set service nat rule 20 inbound-interface pppoe0
# set service nat rule 20 inside-address address 192.168.0.2
# set service nat rule 20 inside-address port 443
# set service nat rule 20 protocol tcp
# set service nat rule 20 type destination
```

# 気になること
~~逆向きの設定（SNAT）はいらないのかな？
なんとなくで設定して、これでSSHやhttp使えてるけど。~~
ステートフルなNATなため要らない、とご指摘いただきました。
今回の設定で入ってきたパケットから始まるTCPセッションを追いかけていくので、戻りもTCPセッションが閉じるまで変換され続けるそう。

# 参考
- [[VyOS] IP/ポートフォワーディングを行う: いろんなことに手を出す](http://light3389.sblo.jp/article/175516204.html)
- [VyOSでホスト名の静的マッピングを行う | 俺的備忘録 〜なんかいろいろ〜](orebibou.com/2016/01/vyosでホスト名の静的マッピングを行う/)
JavaScript バーロー！変数定義、varとletの違いをまとめてみた


## JavaScriptの変数定義、varとletの違いをまとめてみた：イントロ

　

JavaScriptを使う上で変数定義がvarとletがある。
その違いとどちらを使うべきを書いていこうと思います。
ちなみにバーローについては、某探偵少年の名セリフとvar、let(ばー、れっと）をかけてるだけです。。。。むりやりですが（笑)

## まず変数を宣言するとは
　

・ざっくりですが値を格納するための領域を確保する
・変数を利用するための宣言

といったところです。
下記の様に宣言します。

　

```js
var hensuu1 = 1;       // varの変数定義
let hensuu2 = "変数";　// letの変数定義
```

## 本題！varとletの違い
　


上記のように宣言できるのを見ると違いがない様に見えますが、
大きな違いとしては、letの方がES2015という最近追加された機能で、
letの方がコードの保守性が良いように思います。

違いを下記にまとめていきます。

### ■　相違点 　■###

**①変数の重複許容　var => 許す　let => 許さない**

varは変数の重複を許容しますが、letは許容しません。

```js

var hensuu = "変数1";
var hensuu = "変数2";

console.log(hensuu); // 変数2と表示　最初の宣言は上書きされる

let hennsuu = "へんすう";
let hennsuu = "へんすう２";
console.log(hennsuu); // Idemtifer 'hennsuu' has already been declardとエラーになる
　　　　　　　　　　　 // 変数'hennsuu'はすでに宣言済みです
```
　

なので
letを使った方が同じ名前の変数をうっかり使ってしまい、変数が予想外のものになったり、バグが発生しにくいと言えます。
　

**②ブロックスコープの認識　var=>認識しない　let=>認識する**

letの方がより細かく変数の有効範囲を管理できます。

なんとJavaScriptには、ブロックレベルのスコープが存在しません!!
(JavaやPHPなどの言語からすると驚きですよね。。。)

たとえばif文など

```js

if (true) {
    var i = 1;
}

console.log(i); // 1と出力
```

この様に
ブロックを抜けたあとでも、変数iが有効になります。
 
これがletだとちゃんとスコープを認識します

```js

if (true) {
    let i = 2;
}

console.log(i); // 変数iはスコープ外なのでエラーとなる
```

　

これは、良いですね！違和感がなくなります！

##　まとめ
　
varとletであればletを使ったほうが安全！！

しかし、letはES2015から利用できるものなので、環境を確かめてから使いましょう。
良いコードを書くためにlet's let!


。。。苦笑
ゼロから始めるvim

#vimとは

コマンドライン上で呼び出すことのできるエディターです。
CUI(コマンド・ユーザー・インターフェース)で使われることを前提に設計されたエディターです。

Unix/Linux系のOSを使っている人は、vimに触れる機会があると思います。それで、Vimでコンフィグを書き換えなければ行けないなんてことも多々あります。
さらに、サーバ上で直接的にソースや設定ファイルに変更を加えるときも、vimを使います。
なので覚えておいて損はないと思います。

#vimの使うメリット
- キーボードだけで操作できるから慣れればとても生産性が上がる。
- sshでリモート開発などですごい役に立つ
- 何にしろかっこい(個人的な意見w)

#vimを触れる前に基本だけ抑えよう
vimは、 6つの基本モードに加えて、6つのモードがあります。

そのうちの「NormalMode」、「InsertMode」、「VisualMode」の３つのモードについて今回は紹介していきたいと思います。

開いた時の状態を、NormalModeといい、NormalModeでは文字を打っても受け付けてくれません。
NormalModeで主にできることは、カーソル移動、コピー、削除などです。  ※詳しくは下のコマンド一覧で!

InsertMode　とは、その文字どおり、文字を打ち込むときに使います。
InsertMode　も起動方法は、基本的に i で起動させます。起動したら下に[– INSERT –]と表示されるはずです。
(他にも入力モードの起動方法はあります。※下のコマンド一覧に書いておきます。)

入力モードの終了方法は「esc」キーです。

VisualModeについてはあとで説明します。

#vimを起動させよう

ターミナルからの軌道になると思います。
####一応Linuxコマンドの最低限の復習をしましょう。

|コマンド　|内容|
|:----:|:----:|
|cd|ディレクトリの移動|
|ls|ディレクトリの一覧表示|
|pwd|カウントディレクトリーの表示|
|mkdir|フォルダーの作成|
|touch|ファイルの作成|
|cd ~|ホームディレクトリーに移動|

####実際にvimを起動させていきます。


```
vim
```

と打つだけです。
もし、sample.textというファイルをvimで起動したいのなら

```
vim sample.text
```

と打てばsample.textを開いてvimが起動します。
※同じディレクトリにいる必要があります。

###vimに触れよう
####まずはカーソルの移動から覚えましょう。

|コマンド|内容|
|:----:|:----:|
|h|カーソルを左に移動|
|j|カーソルを下に移動|
|k|カーソルを上に移動|
|l|カーソルを右に移動|

とても基本的な操作です。必ず使うので覚えておきましょう。
十字キーでも操作は可能ですが。それではVimの意味がないので h,j,k,l を使いましょう。]

####入力と保存、終了をやってみよう!


|コマンド|内容|
|:---:|:---:|
|i|カーソルの位置から入力モードを起動|
|a|カーソルの位置から１つ進めて入力モードを起動|
|o|カーソルの位置から改行して入力モードを起動|
|:w|保存|
|:q|実行中のタブを閉じる|
|:wq|保存して終了|
|:q!|保存せずに終了|
|ZZ|上書き保存し、viを終了|
|:xa|変更があるタブを保存してすべてのタブを閉じて、ターミナルに戻ります|

###3つもモードについて少し説明します。
|コマンド|内容|起動方法|
|:---:|:---:|:---:|
|NormalMode|カーソルの移動。他のモードへの移行。テキストのコピー、カット、ペースト。|Esc|
|InsertMode|テキストの入力、編集。|a/i/o|
|VisualMode|テキストの選択。|v|

VisualModeは、vを打ってそのあとに選択したいところをカーソルを移動して選択するだけです。
そのあとは、削除なり、コピーなど行いたい操作を行ってください。


#コマンドを覚えよう

全部覚える必要はないですが、ここにあるのは一部ですので自分が必要としているものから覚えていきましょう。

####コマンド一覧
　　　　***※文末に ! をつけると強制っという意味になる。***

|キー|動き|
|:-----------------:|:------------------:|
||------------------移動系------------------|
|^|現在行の先頭に移動|
|$|現在行の末尾に移動|
|ctr + f|ページ送り|
|ctr + b|ページ戻し|
|gg|先頭に移動|
|G|最終行に移動|
|w|次の単語の先頭に移動|
|b|前の単語の先頭に移動|
|e|次の単語の末尾に移動|
||------------------削除系------------------|
|x|一文字削除|
|c|VisualModeで選択した範囲を消してInsertModeで終わる|
|ci|括弧の中身だけを消す|
|ciw|カーソルの下にある１単語を消してInsertModeに入る|
|cit|htmlのタグに囲まれている中を消します|
|dw|カーソルから右側にある単語を消す|
|daw|カーソルの下にある単語を消す|
|D|カーソルから右側にある文を全て消す|
|S|カーソルのある１行を全て消してInsertMode起動|
||------------------その他------------------|
|dd|行をカット|
|yy|行をコピー|
|p|ペースト|
|u|戻る (ctr + z)|
|/[文字]+return|検索（n:下方向に検索 N:上方向に検索）|
||-----------------↓おすすめ↓-----------------|
|:!{shellコマンド}|vim内でshellのコマンドが使えます|
|Control+v|元のカーソル位置から動いたところまでを箱型で選択|
|ggVG|全選択|


#Touch Bar搭載で[esc]がと思った方へ

ctr + [　で同じ動きをしてくれますし、こっちの方が指の動きが小さく済む気がします。
自分的におすすめなので、もしよかったら試してみてください。
UnityのPlayerPrefs保存場所はEditorとビルドで異なる

# 前提
Windows 10 + Unity 5.6.1f1 環境での挙動なので、他のOSやバージョンでは異なる可能性があります。

# PlayerPrefsの保存場所
UnityEngine.PlayerPrefs の保存場所は、Windows環境の場合レジストリです。
[ドキュメント](https://docs.unity3d.com/560/Documentation/ScriptReference/PlayerPrefs.html)には以下の記載があります。

> On Windows, PlayerPrefs are stored in the registry under HKCU\\Software\\[company name]\
\[product name] key, where company and product names are the names set up in Project Settings.

しかし実際には、上記はビルドしたもの（Standalone)の保存場所です。
**Editor上で実行した場合は以下の場所に保存されます** 。

HKCU\\Software **\\Unity\\UnityEditor** \\[company name]\\[product name]

ドキュメントにわざわざ「Editor/Standalone」と書いてあるのに実際には異なっている、という罠でした。
YQL API と jQuery による RSS 表示：各ブログ対応版

表題の処理を行おうとしたところ、詳細な解説つきの[「Google Feed API から YQL API へ移行したお話」](http://qiita.com/nyatto/items/94c3f7cac14e8e8ef50f)が非常に参考になったのですが、ブログサービスによって RSS1.0 と 2.0 が混在していたり、サイトによって画像の取得に工夫が必要だったりしたため、そのあたりに手を加えたものです。

FC2、Ameba、livedoor、Seesaa、Hatena、So-net、goo、Yahoo!、excite、JUGEM 各ブログの RSS で確認済みですが、画像取得は色々見落としがありそうなので「reg」を適宜修正してください。

- goo ブログの RSS1.0 は description の文字数が少なく画像を含まない場合がありますので、画像を取得したい場合は RSS2.0 を使用してください
- excite ブログは RSS に画像タグを含まない仕様らしく、画像は取得できません

```html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>YQL API RSS 表示</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
</head>
<body>
<script type="text/javascript">
$(function() {
	$.getJSON("http://query.yahooapis.com/v1/public/yql?callback=?", {
		// rss(取得件数)：但し1件の場合は for と json.query.results.item[i] の [i] を消す
		q: "select * from rss(5) where url in ('フィードのアドレス')",
		format: "json"
	}, function (json) {
		for(var i in json.query.results.item){
			var entry = json.query.results.item[i];
			var link = entry.link;
			var title = entry.title;

			if(entry.pubDate) {
				var date = new Date(entry.pubDate);
			} else {
				var date = new Date(entry.date);
			}

			var yyyy = date.getFullYear();
			var mm = ('0' + (date.getMonth() + 1)).slice(-2);
			var dd = ('0' + date.getDate()).slice(-2);
			var dispDate = yyyy + '/' + mm + '/' + dd;

			// newDate：NEW 扱いにする日数
			var newDate = 3;
			var dateNow = new Date();
			var checkDate =(dateNow - date) / (1000 * 24 * 3600);
			var dispNew = '';
			if(checkDate <= newDate) {
				dispNew = '<strong>NEW!!</strong>';
			}

			// ameba の絵文字除外、Yahoo! の画像 URL への対応など
			var reg = 'src="((?!https:\/\/stat100.ameba).)(.*?)(\.jpg|\.jpeg|\.gif|\.png|\/img_)(.*?)"';
			if(entry.encoded) {
				var imgsrc = entry.encoded.match(reg);
			} else {
				var imgsrc = entry.description.match(reg);
			}
			var dispImg = '';
			if(imgsrc) {
				var dispImg = '<img ' + imgsrc + ' alt="">';
			}

			var dispElm = '<li><a href="' + link + '" target="_blank">' + dispImg + title + '</a>（' + dispDate + '）' + dispNew + '</li>';
			$("#feed ul").append(dispElm);
		}
	});
});
</script>
<div id="feed">
<ul></ul>
</div>
</body>
</html>
```
##参考サイト
- [Google Feed API から YQL API へ移行したお話](http://qiita.com/nyatto/items/94c3f7cac14e8e8ef50f)
- [YQL API と jQuery を用いて、ココログ等の RSS を取得・表示させる方法（さよなら Google Feed API）](http://qiita.com/KIASMA/items/634b2c5315ebeb2165a8)
リポジトリの最初の作業をプルリクエストにする

# やりたいこと

書いたコードをプルリクエストでレビューしてもらうとコード差分にインラインで指摘を書いてもらえて捗るが、そのためには

* デフォルトブランチから分岐した feature ブランチに作業内容をコミット
* feature ブランチからデフォルトブランチへのプルリクエストをつくる

とする必要があり普段はそうしていました。しかし新規リポジトリを作ったときに気を抜いていたため

* リポジトリを新規作成して最初のコミットからコードを入れてしまった
  * 親コミットがないので差分として見れない
* 最初の作業もプルリクエストでレビューしてもらいたい
  * featureブランチで作業してなかった

となってその対処を考えました。実際には「空コミット上にリベース」の手順を採用。

# 手順の前提

* デフォルトブランチ名: master
* プルリクエストのマージ元ブランチ名: feat-XX
* リモート名: origin
* master ブランチを checkout している

# (案1) 空コミット上にリベース

## 手順

1. `git checkout -b feat-XX` (変更を feat-XX ブランチに退避)
2. `git branch -d master`　（master ブランチを削除）
3. `git checkout --orphan master` (親コミットなしのブランチを作成してチェックアウト)
4. `git reset --hard` (インデックスをクリア)
5. `git commit --allow-empty` (空コミットを作成)
6. `git checkout feat-XX` (変更を退避させていたブランチをチェックアウト)
7. `git rebase master` (空コミット上にリベース)
8. `git push origin feat-XX` (リベースしたものをプッシュ)
9. `git checkout master` (空コミットのみの master ブランチをチェックアウト)
10. `git push origin master --force` (プッシュ)
11. feat-XX から master へのプルリクエストを作る

## Pros

* レビュー対象のコミット単位を維持できる

## Cons

* master の rewrite が必要

# (案2) 全消し

## 手順

1. ソースツリー上のファイルを全て消す
2. `git commit -a` (空のソースツリーをコミット)
3. `git push origin master` (空になったものをプッシュ)
4. `git checkout -b feat-XX` (ブランチ作成)
5. `git revert HEAD` (空にしたものを巻き戻してコミット)
6. `git push origin feat-XX` (プッシュ)
7. feat-XX から master へのプルリクエストを作る

## Pros

* master の rewrite が不要

## Cons

* レビュー対象が１コミットになってしまう
* 後から見たときに何のために revert したのかわかりにくい
localstackをdockerを使ってセットアップする

AWSを使って開発するときに、開発環境はローカルに配置して気軽に実行したいので、[LocalStack](https://github.com/atlassian/localstack)を構築してみました。起動方法はいくつかるようですが、すでにデータベースなどをdockerで立ち上げているので、dockerを使ってLocalStackを動かします。

## インストール

ドキュメントにはgitのリポジトリをローカルにcloneするように書いてありますが、リポジトリ全体が必要はなく、docker-compose.ymlさえあればいいので、それをローカルにコピーします。本当に必要なのは、そのファイルの中のservicesのlocalstackのところだけですね。

## 起動

```shell-session
docker-compose up -d
```

で起動します。Macの場合は

```shell-session
TMPDIR=/private$TMPDIR docker-compose up -d
```
とします。

## AWSのコマンドのインストール

ついでにawsのコマンドをインストールします。pyenvとpyenv-vertialenvがインストールされている前提で話します。

```shell-session
pyenv virtualenv 3.6.2 aws
pyenv shell aws
```

次にawsの設定を行います。

```shell-session¥
aws configure
```

これで４つの情報を聞いてくるので、こんな感じで入力します。

```
AWS Access Key ID [None]: AKIAIOSFODNN7EXAMPLE
AWS Secret Access Key [None]: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
Default region name [None]: us-east-1
Default output format [None]: json
```

これを設定しておかないと動かないので、適当に入力しておけばいいみたいです。

## 動作確認

あとは、「[LocalStackをつかってローカルでLambdaを実行してみた](http://dev.classmethod.jp/cloud/aws/localstack-lambda/)」に書いてあるように、Lambdaを動かして動作確認します。

## おわり

これで動作確認が終わりです。でも、ダッシュボードが8080のポートを使っていますが、dockerのイメージをそのまま使っている限りだと、このポートを変更することはできないようです。自分の開発中のものが8080を使っていると悲しいですね。
neovim始めました

## はじめに
macにneovimをインストールした時のメモです。
vim初心者なので内容に間違い等あればご指摘くださいm(_ _)m

## やったこと
* macにneovimをbrew経由でインストール
* vimコマンドでneovimが起動するようにalias設定
* python3を使用しているプラグイン用にpython3インストール

## 環境
* mac os el capitan
* hombrew 1.3.0


## neovimって何？
公式：https://neovim.io/

vimからフォークされた、vimの課題を解決しよう！って始まったプロジェクトです。vimに比べて速いと言われていたり機能面でかなりモダンになっているみたいです。元々のvimを使っていたわけではないので比較は今後していきたいと思います。

vimから乗り換えてそのままプラグイン使いたい場合、もしかして動かなくなるプラグインもあるかもしれません。どうしても手放せないプラグインであれば予めググるなどして対策した方がいいと思います。


## インストール
```bash
brew install neovim/neovim/neovim
```
これだけです！

```
# nvimで起動してください
nvim
```


## vimコマンドでneovimが起動するようalias設定
```~/.bash_profile
vim ~/.bash_profile

# 以下の1行を追加
alias vim='nvim'

# 設定を反映
source ~/.bash_profile
```

これでvimコマンドでneovimが起動されるようになっていると思います。


## python3を使用しているプラグイン対応
```bash
# python入れるところは直接関係はないので入っている場合はスルーしてください
brew update && brew upgrade
brew install pyenv
pyenv install 3.6.1
pyenv global 3.6.1
# 本題はここ
pip3 install --upgrade neovim
```

まだプラグイン入れていないので動作確認はできていません。
python対応については公式にも書かれています。
https://neovim.io/doc/user/provider.html#provider-python

neovim立ち上げて:helpコマンドでも確認できるみたいです。

```bash
# nvim起動して以下のコマンドを実行してみてください
:help provider-python
```

以上になります！

## おわりに
これでneovimを使うことはできるようになりました。
次はプラグインマネージャーを導入してみようと思います。

プラグインやvimの各種設定については、開発時困ったり、あったらいいなって思った時に都度入れていきたいと思います。すごい方たちの設定などググればすぐ出てきますが、単にコピペするだけでなく自分に残るようなやり方をしていこうと思います。

ありがとうございました。
youtube-dlを操作するrubyプログラム

#Qiitaとrubyとyoutube初心者です!!

凄い！何が凄いってQiitaのコード部分に
https://www.ユーチューブ.com/watch?v=***********
(カタカナ部分をアルファベットで)
って書くと投稿が弾かれる！みんな試してみてくれ！！最初見出しに●arezって入れてたから弾かれてたのかと思ってyoutube初心者に変えたりして試してた(笑)他のサニタイズ用のURLもあると思うけど、どこかに書いてないだろうか。書いてないだろうな。

環境
os X(10.12.6)
ruby 2.4.1

[youtube-dlのインストールはこれの通り](https://rg3.github.io/youtube-dl/download.html)
>sudo curl -L https://yt-dl.org/downloads/latest/youtube-dl -o /usr/local/bin/youtube-dl
>sudo chmod a+rx /usr/local/bin/youtube-dl

mp4からmp3へのエンコードはffmpegでやってる。
[ffmpegのインストールはこれの通り]
(https://trac.ffmpeg.org/wiki/CompilationGuide/macOS)
>brew install ffmpeg

brew入ってないって人は[これ見てやってください](https://yuichon.com/2016/02/ffmpeg-install/)


コード自体はクラスにするほどじゃないはずだけど、オブジェクト志向をやってみたかった書いてみた
もっと綺麗に書けるはずだから機能追加+リファクタリングするかも

download_urls.txtにURL入れて、mediadl.rb動かせば音声だけのが作られる
Mediadl#encode_mp4_to_mp3とMediadl#remove_mp4をコメントアウトすれば普通にmp4形式で下載される

download_urls.txtの中身をサーバ上に置いてブラウザのエクステンションでブックマーク感覚でURLを追加して、定期でmediadl.rbを実行したら、もっと良い感じになるっぽい。そのうちやるかも

```download_urls.txt
https://www.ユーチューブ.com/watch?v=***********
https://www.ユーチューブ.com/watch?v=***********
```


```ruby:mediadl.rb
#mediadl.rb

require 'open3'


class Mediadl
  def initialize(file_name="./download_urls.txt")
    @file = file_name
  end

  def line_counter
    File.open(@file) do |f|
      while f.gets
        @total_lines = f.lineno
      end
    end
    return @total_lines
  end

  def dl
    counter = 0
    total_lines = self.line_counter
    File.open(@file,'r').each do |url|
      counter += 1
      puts "start file:#{counter}/#{total_lines}"
      Open3.capture3('youtube-dl -f mp4 ' + "#{url}")
      puts  "finish file:#{counter}/#{total_lines}"
    end
    Process.waitall
  end

  def encode_mp4_to_mp3
    puts "start encoding"
    Open3.capture3('for f in *.mp4; do ffmpeg -y -i "$f" "`echo $f | sed -e "s/.mp4/.mp3/"`"; done ')
    puts "finish encoding"#
  end

  def remove_mp4
    puts "start remove mp4"
    Open3.capture3('rm *.mp4') 
    puts "finish remove mp4"
  end
end

download = Mediadl.new
download.dl
download.encode_mp4_to_mp3
download.remove_mp4
```
PhoneGap/Cordovaでの外部サイトリンク　内部表示/アプリ内ブラウザ/標準のブラウザ

#概要
PhoneGap/Cordovaアプリで外部サイトへリンクしたときの挙動が、どこかのバージョンから変わったため、現バージョンでの挙動メモです。

#環境
Cordova 7.0.1
記事が古めですが、[この記事](http://qiita.com/tsunet111/items/2ab7a1bfdf58a08af38f)で構築した環境を使っています。
※ ずっとPhoneGap環境をつかっていたのですが、最新版で `phonegap create` が失敗することがあるので、Cordova に乗り換えました。

#検証マシン
Sony Xperia Z5 Premium (Android7.0)
iPhone6Plus (iOS )

#Androidの挙動
##デフォルト=標準のブラウザ（Chrome）で開く

```html
<a href="https://www.google.co.jp/">LINK</a>
```

単に外部URLリンクをを書くだけだと、**Chromeに切り替わってページが表示されます**。
![Screenshot_20170811-192631.png](https://qiita-image-store.s3.amazonaws.com/0/24420/eae407e1-f76c-3858-2dd6-443762d149b4.png)


##アプリ内ブラウザで開くようにする

```
cordova plugin add cordova-inappbrowser
```

プラグイン「InAPpBrowser」をインストール

```html
<a hred="#" onclick="window.open('https://www.google.co.jp/', '_blank', 'location=yes')">LINK</a>
```

`window.open`で`_blank`指定すると、**アプリ内ブラウザで開きます**。
（しかし・・・このUIのダサさはどうにかならないものか…）
![Screenshot_20170811-192647.png](https://qiita-image-store.s3.amazonaws.com/0/24420/256ca88a-3090-02e8-f8fa-950443036752.png)


【注意】
`cordova create` で生成される初期テンプレに以下のmetaタグが入っています。
この１行を削除しないと JavaScriptが利きません。これにハマって、この記事書くのいったんやめかけました...

```html
<meta http-equiv="Content-Security-Policy" content="default-src 'self' data: gap: https://ssl.gstatic.com 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src *; img-src 'self' data: content:;">
```

##アプリ内部で表示するようにします。

かつではこの挙動がデフォルトだったのですが、どのバージョンからか、ホワイトリストに記載する形式に変わりました。
プラグインが必要ですが、Cordovaの初期テンプレに最初から入っています。

```xml:config.xml
<allow-navigation href="https://www.google.co.jp/*" />
```

config.xml にアプリ内表示を許可するアドレスを記載します。

```html
<a href="https://www.google.co.jp/">LINK</a>
```

リンクは、素の a タグでOKです。
これで、Webサーバー上のページと意識されず、**アプリ内部でページが表示されます**。
![Screenshot_20170811-193756.png](https://qiita-image-store.s3.amazonaws.com/0/24420/10371353-bd42-6e0a-1d4f-8195555b9988.png)

なお、ホワイトリストで指定したURL以外は表示できないため、Googleの検索から別サイトに飛ぼうとするような場合は、別URLになった瞬間に、標準のブラウザ（Chrome）が開きます。

参考
https://cordova.apache.org/docs/en/latest/reference/cordova-plugin-whitelist/


#iOSの挙動

標準のブラウザが Safari に変わるだけで、挙動は同じです。
気が向いたら、キャプチャ追記します。。。