OmniAICreator/Qiita-1.07M huggingface dataset:
[Cordova] cordova-plugin-inapppurchaseを使ったアプリ内課金

# はじめに

AndroidアプリをCordovaを用いて開発している。
アプリ内課金を実装しようと思ったが、王道プラグインである[cordova-plugin-purchase](https://github.com/j3k0/cordova-plugin-purchase)で、うまく動作しなかったので、別のプラグインを使ってみた。
あまり、日本語で書かれたドキュメントがなかったので参考までに
(ちなみに、iOS版は試していません)


<a id="orgadcdc24"></a>

# cordova-plugin-inapppurchaseプラグイン

今回は、[cordova-plugin-inapppurchase](https://github.com/AlexDisler/cordova-plugin-inapppurchase)を利用する
cordova-plugin-purchaseプラグインよりも、Issueやプルリクが片付けられていないが、まぁ動作するのでそれでよい


<a id="org84cee8f"></a>

## インストール

`cordova plugin add` で普通にインストールできる

    $ cordova plugin add cordova-plugin-inapppurchase

## アプリ側の設定

まず、cordovaプロジェクトの直下の`www`ディレクトリ(アプリのhtml/css/jsファイルがある場所)に`manifest.json`ファイルを作成し、アプリのライセンスキー(Billing Keyとも呼ばれている)を登録する
ライセンスキーは、Google Play Consoleからアプリ内課金を実装したいアプリのダッシュボード画面を開き、左のメニューバーの「開発ツール」>「サービスとAPI」の画面から確認することができる
`manifest.json`は以下のように記載する

    { "play_store_key": "アプリのライセンスキー"}

次に、Cordovaが提供するネイティブ機能をJavascriptから制御したいので、アプリにcordova.jsを読み込ませる
以下をhtmlに追記すればOK

    <script src="cordova.js"></script>

(ブラウザからアプリをテストすると、`cordova.js`がないというエラーが出るが、実機デバッグや仮想マシンで動かせば問題なく読み込まれるのでご心配なく)

これで、利用準備完了

## Google Play Consoleから課金アイテムを登録

本誌の主題ではないが、一応やり方を説明しておく
ライセンスキーを取得したときと同様にGoogle Play Consoleのアプリのダッシュボード画面から、左のメニューバーの「ストアでの表示」>「アプリ内サービス」から課金アイテムを登録する
登録の際に必要な、アイテムIDはアプリ側のJavascriptに必要なのでメモをしておく

ちなみに、公開前のリリースが残っていないと課金アイテムが登録できないっぽいので、登録できない方はα/β版でもいいので適当にAPKをアップロードして試してみてください


<a id="orgb92bb3c"></a>

## 利用できるAPI

以降は、本プラグインが利用できるAPIを紹介する
基本的に、公式のREADMEの劣化版だが、日本語で概要を理解すると言った形で利用してもらいたい

APIの利用はinAppPurchaseオブジェクトのメソッドを呼び出す形で行われる

### Get Products

課金アイテムのアイテムIDのリストを引数として渡し、アイテムの価格や説明文などの情報を取得する
公式には、「購入する前に呼び出す必要がある」とあるが、呼び出さなくても問題なく動作している気がするので謎
一応、ドキュメントに従ってdevicereadyイベントにフックする形で、呼び出すことをおすすめする

それ以外では、単純にアプリとGoogle Playストアとの疎通が取れているかを確認するために利用できる

```javascript
inAppPurchase
  .getProducts([{"課金アイテムのIDのリスト"}])
  .then(function (products) {
    console.log(products);
    /*
    [{ productId: 'com.yourapp.prod1', 'title': '...', description: '...', currency: '...', price: '...', priceAsDecimal: '...' }, ...]
    */
  })
  .catch(function (err) {
     console.log(err);
  });
```

### Buy

課金アイテムのIDを引数として渡し、購入処理を行う

```javascript
inAppPurchase
  .buy({"課金アイテムのID"})
  .then(function (data) {
    console.log(data);
    /*
      {
        transactionId: ...
        receipt: ...
        signature: ...
      }
    */
  })
  .catch(function (err) {
    console.log(err);
  });
```

### Subscribe

定期購入型のアイテムの購入に利用する
BUYメソッドと同じ使い方

### Consume

消費型の課金アイテム(ゲーム内コインなど)の消費に利用する
私は利用していないので動作を保証できない

### Restore Purchase

購入済みのアイテムのリストを返してくれる
具体的には、課金アイテムのproductIdや購入処理ごとに割り当てられるtransactionIdや購入日時などのオブジェクトのリストが返ってくる
購入者が課金アイテムを購入しているかを確かめたり、利用者がアプロを再インストールしても購入情報をアプリに反映できるようにするために利用する

```javascript
inAppPurchase
  .restorePurchases()
  .then(function (data) {
    console.log(data);
    /*
      [{
        transactionId: ...
    	productId: ...
    	state: ...
    	date: ...
      }]
    */
  })
  .catch(function (err) {
    console.log(err);
  });
```
docker、nginx-proxy、letsencryptでとりあえずredmineするまで

メモ

CentOS Linux release 7.5
Docker version 1.13.1
docker-compose version 1.21.2

```shared/docker-compose.yml
version: "2"
services:
  proxy:
    image: jwilder/nginx-proxy
    container_name: proxy
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/tmp/docker.sock:ro
      - ./certs:/etc/nginx/certs:ro
      - /etc/nginx/vhost.d
      - /usr/share/nginx/html
    restart: always

  letsencrypt:
    image: jrcs/letsencrypt-nginx-proxy-companion
    container_name: letsencrypt
    volumes:
      - ./certs:/etc/nginx/certs
      - /var/run/docker.sock:/var/run/docker.sock:ro
    volumes_from:
      - proxy
    restart: always

  mysql:
    image: mysql:5.7
    container_name: mysql
    command: >
      --character-set-server=utf8mb4
      --collation-server=utf8mb4_general_ci
      --max-allowed-packet=128M
    ports:
      - "3306:3306"
    environment:
      MYSQL_ROOT_USER: root
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: mysql
    volumes:
      - ./mysql:/var/lib/mysql
    restart: always

networks:
  default:
    external:
      name: shared
```

```redmine/docker-compose.yml
version: "2"
services:
  redmine:
    image: redmine
    environment:
      VIRTUAL_HOST: 〇〇.com
      LETSENCRYPT_HOST: 〇〇.com
      LETSENCRYPT_EMAIL: 〇〇@〇〇.com
      REDMINE_DB_MYSQL: mysql
      REDMINE_DB_DATABASE: redmine
      REDMINE_DB_PASSWORD: root
    external_links:
      - mysql
    restart: always

networks:
  default:
    external:
      name: shared
```

```
docker network create --driver bridge shared
cd shared
docker-compose up -d
```
必要に応じてDB作成

```
docker-compose exec mysql bash
mysql -u root -p
mysql> CREATE DATABASE redmine;
```
redmineもup

```
cd redmine
docker-compose up -d
```

〇〇.comにアクセス

### 関連
[docker、nginx-proxy、letsencrypt環境でlaravelするまで](https://qiita.com/hrkbyc/items/bf4675b6df4d3e4e0de3)
[docker、nginx-proxy、letsencrypt環境でwordpressするまで](https://qiita.com/hrkbyc/items/d7802b1f8d9fda21c43d)

### 参考
[VPSにdockerで複数サイトをホスティングするには？](https://suin.io/561)
[Docker上でnginx-proxy他を使ってSSL対応マルチドメインサーバ環境の構築](https://hogehuga.net/linux/116/)
Windows 版 OpenJDK インストール手順

Amazon Corretto インストール手順は [Windows 版 Amazon Corretto インストール手順 - Qiita](https://qiita.com/ryo-sato/items/482a58b375fbc448e723) を参照すること。

## 調べ方

- [openjdk - Google Search](https://www.google.com/search?hl=en&q=openjdk)
- [windows ライフサイクル site:microsoft.com - Google 検索](https://www.google.com/search?q=windows+%E3%83%A9%E3%82%A4%E3%83%95%E3%82%B5%E3%82%A4%E3%82%AF%E3%83%AB+site%3Amicrosoft.com)
- [エクスプローラー アドレスバー - Google 検索](https://www.google.com/search?q=%E3%82%A8%E3%82%AF%E3%82%B9%E3%83%97%E3%83%AD%E3%83%BC%E3%83%A9%E3%83%BC+%E3%82%A2%E3%83%89%E3%83%AC%E3%82%B9%E3%83%90%E3%83%BC&tbm=isch)

## 前提

- サポートされている Windows であること。
   - OpenJDK 10 は .tar.gz 形式で配布されていたため, tar コマンドが必要になったが,
     OpenJDK 11 以降は zip 形式で配布されているため, 特に最新である必要は無くなった。

## 備考

- 新しいバージョンの OpenJDK をダウンロード, 展開した場合は, **JAVA_HOME** の変数値を変更するだけで OK.
   - Path は `%JAVA_HOME%\bin` のままとする。

## インストール手順

1. [jdk.java.net](http://jdk.java.net/) にアクセスし, Windows 版の zip ファイルをダウンロードする。
   1. *Ready for use:* と記載されているリンクをクリックする。
      - 今回は **JDK 16** をクリックしたものとする。
   1. *Windows / x64* の **zip** をクリックし, ダウンロードする。
1. ダウンロードしたファイルを右クリックし, **すべて展開** をクリックする。
   - *圧縮 (ZIP 形式) フォルダーの展開* ウィンドウが開く。
1. *ファイルを下のフォルダーに展開する* 欄に, `C:\` と入力し, **展開** ボタンを押下する。
   - 上記は, OS やブラウザーによってはコロン (`:`) の次にバックスラッシュが表示される場合があるが, 日本語キーボードの場合は円記号で OK.
   - C ドライブ直下に, `jdk-16.0.1` というフォルダーが展開される。
1. ダウンロードしたファイルを削除する。
1. 展開したフォルダーに移動し, アドレスバーよりパスをコピーする。
   - 今回のパスは `C:\jdk-16.0.1` となる。
1. [Windows 10 環境変数設定手順](https://qiita.com/ryo-sato/items/e24c81eca6ffe9db3fb0) を参考に, 以下の環境変数を新規に追加する。
   - 変数名: **JAVA_HOME**
   - 変数値: (先ほどコピーしたパスを貼り付ける。)
      - 今回の場合は `C:\jdk-16.0.1` を貼り付けることになる。
1. 同様に, 環境変数 **Path** に `%JAVA_HOME%\bin` を追加する。
1. コマンドプロンプトを開き, `java` や `javac` コマンドが利用できることを確認する。
   - `> java -version`

    ```shell
    openjdk version "16.0.1" 2021-04-20
    OpenJDK Runtime Environment (build 16.0.1+9-24)
    OpenJDK 64-Bit Server VM (build 16.0.1+9-24, mixed mode, sharing)
    ```

   - `> javac -version`

    ```shell
    javac 16.0.1
    ```

どっとはらい。
AnacondaでOpenCVのバージョン確認する方法

###Anaconda Promptよりバージョンを確認したいとき
下記をまず入力する

```python

python

```

pythonが起動したら

```python
import cv2
print(cv2.__version__)
```

```python
3.4.1
```
OpenCVのバージョンは3.4.1だった。
SharePointのFormsが編集できない場合の暫定対応

## Microsoft Formsとは
アンケートとかを簡単に作成できるWEBアプリです。
https://forms.office.com/Pages/DesignPage.aspx
SharePointでも使えます。

## SharePointでForms作成
以下からFormsを作成できます。
<img width="344" alt="" src="https://qiita-image-store.s3.amazonaws.com/0/245365/1c3279c5-e8f1-47ca-c7a3-d36c249a51b5.png">

こんな感じで適当に作成します。
<img width="880" alt="" src="https://qiita-image-store.s3.amazonaws.com/0/245365/43a2382e-05e6-c5b1-ddc1-06b5e4f73e8e.png">

作成できたら右側の「共有」から回答ページのURLをゲッツできるので、それを展開する感じです。
作成が終わるとSharePointのドキュメントにExcel形式でファイルが作成されます。
これを開くと回答が表示されます。

## Formsを編集する
作成したFormsを後から編集することができます。
作成したFormsのExcelを表示して(Excel Onlineで）、下図のようにFormsのボタンを押下すると編集メニューが出てきますが、、、
<img width="271" alt="" src="https://qiita-image-store.s3.amazonaws.com/0/245365/7bec5bcb-6a63-2248-5f2b-7c8f52996458.png">
どういうわけか、グレーダウンされていて「フォームの編集」をクリックできません。以前はできたのですが、なぜか出来なくなりました。編集が出来るものもあるので、なぜ編集できないのかは全く不明。

## Excel OnlineからFormsの編集ができない場合
ここからが本題です。現状分かっている対応策です。
ただ前提条件として、編集したいFormsのFormIdが分からないとこの暫定対応でもできません。
FormsのFormIdは回答ページのURLに付いています。
`https://forms.office.com/Pages/ResponsePage.aspx?id=3ZQ9..(中略)..FCUDlYTiQlQCN0PWcu`
これのid=の部分がFormIdです。

それで以下のURLのQueryパラメータのFormIdに↑のFormIdをつけます。
`https://forms.office.com/Pages/DesignPage.aspx?Action=Create#FormId=[ID]`

これでFormsの編集ページにいけるはずです。
Formsを新規に作成した場合は「共有」から編集用ページのURLをゲッツできます。
日付をフォーマットする

FlutterというかDartのライブラリを使って日付をフォーマットします。


## ライブラリ

ライブラリはこちら

https://pub.dartlang.org/packages/intl

ドキュメントにしたがってInstallしてください。
他のライブラリと手順は変わりません

## 使い方

```dart

var format = DateFormat("y/m/d H:mm", "en_US").format(new DateTime.now());
```

例えば今の時間が 2018/6/27 10:00だったら　`2018/6/27 10:00` というテキストが吐き出されます。
他にもいろいろな設定方法があるのでライブラリのページから確認してください。
How to Install Yarn in Ubuntu 18.04 LTS?

# 1. Configure the repository
```
$ curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
$ echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
```

# 2. Install Yarn
```
$ sudo apt-get update && sudo apt-get install --no-install-recommends yarn

--> Because I used NVM to install NodeJS, I Added "--no-install-recommends".

...
Recommended packages:
  nodejs
The following NEW packages will be installed:
  yarn
0 upgraded, 1 newly installed, 0 to remove and 0 not upgraded.
Need to get 697 kB of archives.
After this operation, 4,436 kB of additional disk space will be used.
Get:1 https://dl.yarnpkg.com/debian stable/main amd64 yarn all 1.7.0-1 [697 kB]
Fetched 697 kB in 1s (1,105 kB/s)
Selecting previously unselected package yarn.
(Reading database ... 166162 files and directories currently installed.)
Preparing to unpack .../archives/yarn_1.7.0-1_all.deb ...
Unpacking yarn (1.7.0-1) ...
Setting up yarn (1.7.0-1) ...
...
```

# 3. Verified Yarn Installtation.
```
$ yarn -v

1.7.0
```

https://yarnpkg.com/lang/en/docs/install/#debian-stable
.DS_Storeをグローバルで無視する（.gitignore_global を作る）


`.DS_Store` のignoreを、グローバルで適応させたいときの方法。

## .gitignore_global をつくる

```
touch .gitignore_global
```

## 無視させたいファイル等をつくったファイルに書く

```
vi .gitignore_global
```

でviエディタを開き、`i` で入力モードにして

```
.DS_Store
```

などと書き、`:wq` で保存して終了。

#### 参考
https://qiita.com/ryo2132/items/b7e312b0eb50fc449841

## .gitignore_global を有効にする

```
git config --global core.excludesfile ~/.gitignore_global
```

#### 参考
https://qiita.com/katsew/items/5cade12fa743a2f31f25

## 設定ができているかの確認

```
git config --global --list
```

で↓のような設定ができていればOK。

```
core.excludesfile=/Users/shono/.gitignore_global
```

#### 参考
http://yag-ays.hateblo.jp/entry/2012/01/18/011734

## おまけ
ターミナルで `.` 始まりのファイルを確認するコマンド

```
ls -a
```
機械学習論文読みメモ_129

[THE (UN)RELIABILITY OF SALIENCY METHODS](https://arxiv.org/abs/1711.00867)
deep learningにおいてsaliency methodはモデルの予測結果を
可視化するための手法として一般的である。
しかしこの手法はモデル予測に寄与しない部分に関しても敏感に
反応してしまう弱点がある。
本論ではこの事実を、単純なconstant shiftをpre-processingに
行うだけで実現できることから示す。
こうした脆弱性を克服するためには入力に関するある種の
invarianceが必要になる。
本論ではその事をsaliency modelが満たすことは難しいことを示した。

[Inverse Reward Design](https://arxiv.org/abs/1711.02827)
エージェントシステムにおいて、それらエージェントは
与えられたreward functionに沿って最適化されていく。
そうしたreward functionをdesignしようとする場合、
具体的な訓練シナリオを考え、それに沿って、望ましい
行動へ導く事が可能なようなrewardを考えるのが一般的である。
しかしそのようにしてデザインされたrewardは
まさに想定されたシナリオに対してのみ有効であって、
今までになりシナリオに遭遇した場合に対応できない。
本論では、デザインされたrewardは実際に何が求められているか
を示す観測データであるとみなす。
すなわち、それら観測の住むコンテクストに沿って考える必要がある。
本論では、そうした観測としてのrewardをもとに真の目的関数を
導出する手法としてinverse reward designを定義し、MDPの文脈で
近似的に求解する手法を提案する。

[convolutional normalize flow](https://arxiv.org/abs/1711.02255)
Bayesian posterior inference一般的なアプローチの一つである。
特にvariational inferenceは事後分布の近似を与える手法である。
しかしその表現力は制限されている。
最近の研究において、deepnetを用いて高精度に事後分布を近似する手法は
提案されてきている。
この近似手法の一つとして、単純な分布から複雑な分布へ可逆的にwarpさせていく
Normalizing flowsがある。
この手法では出力される分布を解析可能である。
しかしながら、normalizing flowのflexibilityとcomputation costの間には
トレードオフがある。
本論ではこのトレードオフを解決するための手法として
ConvFlowを提案する。
ConvFlowではランダム入力に対するconvolutionに基づいた手法である。
convolutionを用いることで可逆な変換でありながらlinear scaleの計算complexity
を維持できる。
文字列を連結するプログラム

##string str1とstr2の文字列を連結して表示するプログラム
今回は二つの文字列を連結して表示するプログラムを作成しました。
##ソースコード
<div class="code-frame" data-lang="text"><div class="highlight">
<pre>
using System;

namespace ConsoleApplication1
{
    class renketu
    {
        static void Main()
        {
            string str1 = "おはよう";
            string str2 = "ございます";

            string result = str1 + str2;

            Console.WriteLine(result);

            Console.ReadKey();
        }
    }
}
</pre></div></div>
##結果
![文字列結合.PNG](https://qiita-image-store.s3.amazonaws.com/0/249276/440bdd86-5508-ec9f-50d7-14b9c3c2acc7.png)
このプログラムは単純にstr1とstr2に格納した文字列を+で合わせて表示するだけのプログラムです。

##Concatメソッドを使用する方法
+を使用する以外にもStringクラスのConcatというメソッドを利用した文字の連結も可能です。
##ソースコード
<div class="code-frame" data-lang="text"><div class="highlight">
<pre>
using System;

namespace ConsoleApplication1
{
    class Sample
    {
        static void Main()
        {
            string str1 = "こん";
            string str2 = "にちは";

            string result = String.Concat(str1, str2);

            Console.WriteLine(result);

            Console.ReadKey();
        }
    }
}
</pre></div></div>
##結果![Concat.PNG](https://qiita-image-store.s3.amazonaws.com/0/249276/83dd6c98-dd51-137d-33e0-ca006efbf2d6.png)

このようにしてConcatメソッドの引数に、連結したい文字列を指定しすることで文字の連結もできました。
##まとめ
二つの方法には特に結果出力の違いもなく双方複雑なものでもないので自分が使いやすいほうの扱い方をすればいいと思いました。
この中にAndroidで開けないリンクがある！

以下は自分のTwitterアカウントへのリンクなんだが、
twitter名の前に@を付けてるリンクがあった。

iOSとPCでは無事着地できるが、Androidで開いてみよう。
「ユーザーが見つかりませんでした」となる。

そう。Androidだけなんだ。

<a href="https://twitter.com/m2wasabi">https://twitter.com/m2wasabi</a>
<a href="https://twitter.com/@m2wasabi">https://twitter.com/@m2wasabi</a>

```html
<a href="https://twitter.com/m2wasabi">https://twitter.com/m2wasabi</a>
<a href="https://twitter.com/@m2wasabi">https://twitter.com/@m2wasabi</a>
```
github.ioを使ってMySiteを作ろう

初めまして。

最近Qiita始めて、自己紹介でも投稿しようと思いました。

ですが、ここは**Qiita**。

どうせなら技術系な事でも書きながら自己紹介でもしようという事で今回の記事に至ります。

※注意事項
`GitHubのアカウントを持っている。
　html,css,JSの知識がある。
`
以上の２点はすでに用意してあるという事で話を進めさせていただきます。

では、さっそく本題に入っていきましょう。

# github.ioとは？？
githubにindex.htmlを送る事で簡単に自分のサイトを作る事ができるといった感じです。

今回自分が作った奴のリンクを貼っときますので、もしよかったら見てください
~~twitterフォローして下さい。なんでもしますから。~~

[MokeeeMokeee.github.io](https://mokeeemokeee.github.io)

# 作り方
まずNewRepositoryを作成しましょう

`[username].github.io`という名前で作りましょう。

[username]の所は自分のGitHubの名前にしましょう。

自分の場合は`MokeeeMokeee.github.io`になります。

![gtihub.io.image.png](https://qiita-image-store.s3.amazonaws.com/0/261479/2f41156f-ffb4-bdb5-5a5b-96f290897ea7.png)


そしたら次はindex.htmlを作りましょう。
terminalでのやり方を載せときます。

```terminal:terminal
$ cd
$ mkdir github.io
$ cd github.io
$ vim index.html
```
今回はホームディレクトリにgithub.ioというディレクトリを作成し、その中にindex.htmlという名前でvimで作成しました。

やり方はなんでもいいのですが、あとで`add`と`commit`したり`css`を使う人はわかりやすいところにディレクトリを作り、その中で作業するとわかりやすいと思います。

```html:index.html
<html>
  <head>
    <meta charset='utf-8'>
    <title>タイトル</title>
  </head>
  <body>
    <!-- ここに書きたい内容を書く -->
  </body>
</html>
```

あとはhtmlの書き方をすれば大丈夫です。

cssを使用する場合も`link`を使って参照すればcssを読み込ませることもできます。

また、動的なWebページを作りたい場合はJSも参照させたりすれば使えます。
※JSに関してはServerに依存する処理を行うものはできない場合があります

```terminal:terminal
$ git add index.html
$ git commit -m "commit Messege"
$ git remote add origin https://github.com/[UserName]/[UserName].github.io.git
$ git push origin master
```

terminalの方で作成した`index.html`をadd,commitを行いました。

詳しいやり方はNewRepositoryを作成した時にコマンドが書いてあるので、それを参照して下さい。

![github.io.image.2.png](https://qiita-image-store.s3.amazonaws.com/0/261479/b4733a58-4fb8-51ee-0150-cf04923b5746.png)

pushできているかを確認しましょう。
※　自分は画像とCSSを使用しているので画像のようになっています。`index.html`のみをadd,commit,pushした場合はindex.htmlが表示されているはずです。

それでは、最後にChrome,firefoxなどを開いてURLを打ってみましょう

URL = **UserName.github.io** と入力
※ 自分の場合は`MokeeeMokeee.github.io`

そしたら表示されましたか？

これで完成です！！

あとは自分のお好みでカスタムなどを行なって自分だけのサイトを作って見て下さい！！！！

# おわりに
いかがだったでしょうか？
自分はgithub.ioを知るまでクラウドでサーバを借りてやってたり...
本当に楽です！！

Qiitaを書くのは初めてでどうすれば見やすいか？
わかりやすい書き方はどんな感じか？
など、まだまだ課題はいっぱいありますが温かく見守ってくれればと思います。

よろしくお願いします:innocent:
How to Install NodeJS via NVM in Ubuntu 18.04 LTS?

# 1. Install NVM
```
$ wget -qO- https://raw.githubusercontent.com/creationix/nvm/v0.33.11/install.sh | bash

=> Downloading nvm from git to '/home/johnnychu/.nvm'
=> Cloning into '/home/johnnychu/.nvm'...
remote: Counting objects: 267, done.
remote: Compressing objects: 100% (242/242), done.
remote: Total 267 (delta 31), reused 86 (delta 15), pack-reused 0
Receiving objects: 100% (267/267), 119.47 KiB | 1.61 MiB/s, done.
Resolving deltas: 100% (31/31), done.
=> Compressing and cleaning up git repository

=> Appending nvm source string to /home/johnnychu/.bashrc
=> Appending bash_completion source string to /home/johnnychu/.bashrc
=> Close and reopen your terminal to start using nvm or run the following to use it now:

export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"  # This loads nvm
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"  # This loads nvm bash_completion
```

# 2. Verify NVM Installation
```
$ source ~/.profile
$ nvm ls-remote
...
         v8.11.3   (Latest LTS: Carbon)
         v9.0.0
         v9.1.0
         v9.2.0
         v9.2.1
         v9.3.0
         v9.4.0
         v9.5.0
         v9.6.0
         v9.6.1
         v9.7.0
         v9.7.1
         v9.8.0
         v9.9.0
        v9.10.0
        v9.10.1
        v9.11.0
        v9.11.1
        v9.11.2
        v10.0.0
        v10.1.0
        v10.2.0
        v10.2.1
        v10.3.0
        v10.4.0
        v10.4.1
        v10.5.0
...
```

# 3. Install NodeJS
```
$ nvm install 10.5.0

Downloading and installing node v10.5.0...
Downloading https://nodejs.org/dist/v10.5.0/node-v10.5.0-linux-x64.tar.xz...
##################################################################################################################################################################################################### 100.0%
Computing checksum with sha256sum
Checksums matched!
Now using node v10.5.0 (npm v6.1.0)
Creating default alias: default -> 10.5.0 (-> v10.5.0)
```
# 4. Verify NodeJS Installation 
```
$ node -v

v10.5.0
```

# 5. Uninstall NodeJS
```
$ nvm current
v10.5.0

$ nvm uninstall v10.5.0
```

https://github.com/creationix/nvm
https://www.digitalocean.com/community/tutorials/how-to-install-node-js-on-ubuntu-18-04
UITextViewでPlaceholderを実装する（Swift4対応版）

UITextFieldにはplaceholderがありますが、UITextViewにはありません。
今回は、UITextViewでもplaceholderを使えるように、新しいクラスを作って見ました。

#実装方法
UITextViewを継承した新しいClass（ファイル）を作成
UITextViewにCustomClassとしてPlaceHolderTextViewを指定
ソースコードでplaceholderの文字と文字色を指定

```swift:PlaceHolderTextView.swift

import UIKit

public class PlaceHolderTextView: UITextView {
    
    lazy var placeHolderLabel:UILabel = UILabel()
    var placeHolderColor:UIColor = UIColor.lightGray
    var placeHolder:NSString = ""
    
    required public init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)!
    }
    
    deinit {
        NotificationCenter.default.removeObserver(self)
    }
    
    override public func awakeFromNib() {
        super.awakeFromNib()
        
        NotificationCenter.default.addObserver(self, selector: #selector(textChanged), name: NSNotification.Name.UITextViewTextDidChange, object: nil)
    }
    
    override public func draw(_ rect: CGRect) {
        if(self.placeHolder.length > 0) {
            self.placeHolderLabel.frame = CGRect(x: 8, y: 8, width: self.bounds.size.width - 16 , height: 0)
            self.placeHolderLabel.lineBreakMode = NSLineBreakMode.byWordWrapping
            self.placeHolderLabel.numberOfLines = 0
            self.placeHolderLabel.font = self.font
            self.placeHolderLabel.backgroundColor = UIColor.clear
            self.placeHolderLabel.textColor = self.placeHolderColor
            self.placeHolderLabel.alpha = 0
            self.placeHolderLabel.tag = 1
            
            self.placeHolderLabel.text = self.placeHolder as String
            self.placeHolderLabel.sizeToFit()
            self.addSubview(placeHolderLabel)
        }
        
        self.sendSubview(toBack: placeHolderLabel)
        
        if(self.text.utf16.count == 0 && self.placeHolder.length > 0){
            self.viewWithTag(1)?.alpha = 1
        }
        
        super.draw(rect)
    }
    
    @objc func textChanged(notification:NSNotification?) -> (Void) {
        if(self.placeHolder.length == 0){
            return
        }
        
        if(self.text.utf16.count == 0) {
            self.viewWithTag(1)?.alpha = 1
        }else{
            self.viewWithTag(1)?.alpha = 0
        }
    }
    
}
```

カスタムビューなのでStoryboardでカスタムクラスを自作のPlaceHolderTextViewに指定しないと関連付けが出来ませんので注意！
Pythonを使いGCPでlinebotを動かす（１）

こんにちは。
GCP上で動作するLINE-botをPython（flask）で作ってみたいと思います。
GCPを使うので、相手の発言を記憶するdatabaseも実装し、簡単なアプリとして恥ずかしくないようにします。databaseの実装によって、ボットの応用範囲がググッと広がります。

今回は、イエス、ノーで答えられる質問を10問出して、イエスの数の集計結果を表示するボットを作ってみます（画像はイメージです）。
![IMG_20180627_114959.png](https://qiita-image-store.s3.amazonaws.com/0/266780/a6950787-f177-e3d5-766b-83475648721f.png)

完成品は、以下のQRコードかボタンで友達追加してお試しください！
↓

![image.png](https://qiita-image-store.s3.amazonaws.com/0/266780/5f2a2d2a-2ef8-61a5-9043-958d831fddb9.png)

<a href="https://line.me/R/ti/p/%40kti5682q"><img height="80" border="0" alt="友だち追加" src="https://scdn.line-apps.com/n/line_add_friends/btn/ja.png"></a>

対象としている方は
●google cloud platformを使ったことがある
●line-bot-sdkを使ったことがある
●pythonに慣れている
方になります。
上の前提知識については、時間があれば、別の記事で説明します。
それでは、さっそくいきましょう。

###プログラムの仕組み
* * *

書くファイルは以下の３つです。

1  main.py　　botの本体を書いたファイル
2  app.yaml　 google-cloud-platformにデプロイする際の条件を書くファイル
3  question.txt イエス、ノーで答えられる質問を記載したファイル


それでは、順番に見ていきましょう。

###main.py
* * *

まずは、必要なモジュールのインポートと、認証部分の記述です。
以下の公式ドキュメントほぼそのままです。
↓
[line-bot-sdk-python](https://github.com/line/line-bot-sdk-python)

```python:main.py

from google.cloud import datastore   
from flask import Flask, redirect, request, render_template,abort,request
from linebot import (
    LineBotApi, WebhookHandler
)
from linebot.exceptions import (
    InvalidSignatureError
)
from linebot.models import (
    MessageEvent, TextMessage,PostbackEvent,JoinEvent, TextSendMessage,TemplateSendMessage, ButtonsTemplate,
    PostbackAction, MessageAction,
    URIAction, DatetimePickerAction,
    ConfirmTemplate, CarouselTemplate, CarouselColumn,
    ImageCarouselTemplate, ImageCarouselColumn
)
app = Flask(__name__)

line_bot_api = LineBotApi('<channel-access-token>')
handler = WebhookHandler('<channel-secret>')


@app.route("/callback", methods=['POST'])
def callback():
    # get X-Line-Signature header value
    signature = request.headers['X-Line-Signature']

    # get request body as text
    body = request.get_data(as_text=True)
    app.logger.info("Request body: " + body)

    # handle webhook body
    try:
        handler.handle(body, signature)
    except InvalidSignatureError:
        abort(400)


    return 'ok'

```
上のコードで出てくる

```python:main.py

line_bot_api = LineBotApi('<channel-access-token>')
handler = WebhookHandler('<channel-secret>')
```

の部分は、Line　Developers　に登録して、アクセストークンとチャンネルシークレットを取得して書き換えます。
↓
[Line Developers](https://developers.line.me/ja/)

1行目の

```python:main.py
from google.cloud import datastore
```
は、GCP上にデプロイしてからでないと使えないので、ご注意を。
それでは、下準備ができたので、次回からは、main.pyの本体を記述していきます。


[（２）へ続く](https://qiita.com/ideki1991/items/ad71de150b00b7677769)
gmailからのアクセスを許可するホワイトリストを作成する。

# メールサーバの防衛対策

　ipsetとiptabableで特定亜細亜権からのアクセスは拒否していたけれど、それ以外からも
　大量にメールが送られてくるようになってきたので、根本対処をするためにホワイトリスト
　に登録したアドレス帯域からのメールしか受信できないように対策した。
　基本設定は、日本国内からアクセスのみをホワイトリストに設定した。

# 改善しないといけないところ

　国内のみで良いかというとそうでもない、特に活用させていただいている。
　下記からのメールは許可しておきたい。

　①　Google mail
　②　マイクロソフト社からのメール
　③　Amazonからのメール

#  　Google mailのからのIPアドレス一覧作成

  Googleで公式に公開されている、下記のページを参照して、自動的に
　ホワイトリストを登録するShellを作成しました。
　必要な方もいそうな気がしますので、添付しておきます。

  　情報参照元：https://support.google.com/a/answer/60764?hl=ja

```shell:GetGmailIPtoIPset.sh
#! /bin/bash

Val=`nslookup -q=txt _spf.google.com 8.8.8.8 | tr ":" "\n" | tr " " "\n"| grep -v text | grep google.com`

(
for i in $Val
do
   nslookup -q=TXT $i 8.8.8.8 | tr " " "\n" | grep ip4 | sed -e "s=ip4:=ipset add WHITE =g"
done
) > /tmp/googleip.sh

sh -x /tmp/googleip.sh
```

#  マイクロソフト社からのメール
 マイクソフトの下記のページに情報がまとまっているようです。
　「Exchange Online Protection の IP アドレス」
　　※EOP の IP アドレスの範囲の完全な一覧 (2018 年 3 月 1 日現在) 
   上記、データを使って力ずくで、Shell作成

```shell:GetOutlookIPtoIPset.sh
#! /bin/bash
ipset add WHITE 23.103.132.0/22
ipset add WHITE 23.103.136.0/21
ipset add WHITE 23.103.144.0/20
ipset add WHITE 23.103.198.0/23
ipset add WHITE 23.103.200.0/22
ipset add WHITE 23.103.212.0/22
ipset add WHITE 40.92.0.0/14
ipset add WHITE 40.107.0.0/17
ipset add WHITE 40.107.128.0/18
ipset add WHITE 52.100.0.0/14
ipset add WHITE 65.55.88.0/24
ipset add WHITE 65.55.169.0/24
ipset add WHITE 94.245.120.64/26
ipset add WHITE 104.47.0.0/17
ipset add WHITE 157.55.234.0/24
ipset add WHITE 157.56.110.0/23
ipset add WHITE 157.56.112.0/24
ipset add WHITE 207.46.100.0/24
ipset add WHITE 207.46.163.0/24
ipset add WHITE 213.199.154.0/24
ipset add WHITE 213.199.180.128/26
ipset add WHITE 216.32.180.0/23
```

#  Amazonからのメール

既に、公開されている素敵な情報がありましたので、活用させていただきます。
※ https://qiita.com/iharakenji/items/81d84bb76a97a54478d7

情報の公開ありがとうございます！　@iharakenji さん

```shell:GetAmazonIPtoIPset.sh
#! /bin/bash

Val=`curl https://ip-ranges.amazonaws.com/ip-ranges.json | jq '.prefixes[] | select(.servi
ce=="AMAZON") | .ip_prefix' | tr -d '\"'`

for i in $Val
do
      ipset add WHITE $i
done
```  　

以上で、目的達成しました。
文字列が同じものかどうか判定するプログラム(全角半角の違いを無視して同じ文字列扱いで判定する)

#全角半角の違いを無視して文字列の一致判定を行うプログラム
前回は大文字小文字、全角半角の違いを判定して結果を表示するプログラムでしたが
今回は**[ｱｲｳ]****[アイウ]**のように半角文字と全角文字の違いは区別せずに同じ文字列として判定するプログラムを制作しました。
##ソースコード
<div class="code-frame" data-lang="text"><div class="highlight">
<pre>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
    class Hikaku
    {
        static void Main()
        {
            System.Globalization.CompareInfo ci =
                System.Globalization.CultureInfo.CurrentCulture.CompareInfo;

            string s1 = "ｱｲｳ";//半角のｱｲｳです
            string s2 = "アイウ";//全角のアイウです
            //全角と半角の区別を無視して、s1とs2が等しいか調べる
            if (ci.Compare(s1, s2, System.Globalization.CompareOptions.IgnoreWidth) == 0)
            {
                Console.WriteLine("等しいです。");
            }
            else
            {
                Console.WriteLine("等しくないです。");
            }
            Console.ReadKey();
        }
    }
}

</pre></div></div>
##結果
![全角半角区別なし.PNG](https://qiita-image-store.s3.amazonaws.com/0/249276/ea94401c-9db7-9cab-df5c-144ca876c4f6.png)
ソースコードにIgnoreWidthのメンバを記述することで半角全角の違いを無視して判定しることができました。
他にも似た形で大文字小文字の区別を無視するIgnoreCase
ひらがなカタカナの違いを無視するIgnoreKanaType等もあるようです。
##まとめ
前回今回と文字が一致しているかどうかの判定を行うプログラムを制作しました。
工夫をすれば比較した文字列の中で一致した文字が何個あるのかの判定もできるようなのでまだまだ勉強の余地があると思いました。
Google reCAPTCHA v3 を動かしてみる

# Google reCAPTCHA v3 を動かしてみる

現時点(2018年6月下旬)ではまだβバージョンらしいですが、reCAPTCHA  v3をドキュメント通りに動かして見ました。

## ドキュメント

* [reCAPTCHA v3](https://developers.google.com/recaptcha/docs/v3)

## v3とは

* ユーザーのアクションなし
* スコアを返す

と書いてあります。なるほど、シコシコと自動車の絵を探す必要はないのですね。

## webページに導入する流れ

手順としては3つです。

> 1. Load the JavaScript api with your sitekey
> 2. Call grecaptcha.execute on an action or when the page loads
> 3. Send the token to your backend with the request to verify(←GoogleのAPI)

### 0. Register(Keyの取得)

reCAPTCHA v3を導入するためにはまずは js 読み込みやトークン取得のためのKeyが必要になります。
上記ドキュメントにリンクがあってそこから辿っていけます。

![recaptcha_v3_01.png](https://qiita-image-store.s3.amazonaws.com/0/10192/75ede8f2-16b9-269d-d012-8520aba4fbe5.png)

Key は2種類あります。

* Site Key : HTMLに貼り付ける時に使う
* Secret Key : Google にスコアを問い合わせる時に使う

どちらかというと、Secret Key の扱いに注意が必要ですね。

ちなみにこの画面、実際の画面で見ると右下に 「reCAPTCHA で保護されています」と出てきます。

![reCAPTCHA_v3_02.png](https://qiita-image-store.s3.amazonaws.com/0/10192/6296eb9a-374a-a889-28d6-1a8189b199bb.png)

つまり、reCAPTCHA の設定画面に reCAPTCHA が導入されている!

### 1. Frontend integration(HTMLに入れてトークン取得)

何かのアクションやwebページをロードしたタイミングでトークンを取得します。

ドキュメントには

* js 読み込み
* トークン取得

の方法が記載されています。

```HTML
  <script src="https://www.google.com/recaptcha/api.js?render=reCAPTCHA_site_key"></script>
  <script>
  grecaptcha.ready(function() {
      grecaptcha.execute('reCAPTCHA_site_key', {action: 'homepage'}).then(function(token) {
         ...
      });
  });
  </script>
```

Keyを取得するところでは実際の site_key が含まれた状態のサンプルが載ってるので便利です。
動作確認なので同じように入れます。

### 2. API Request/Response (スコア問い合わせ)

ドキュメント : [Verifying the user's response](https://developers.google.com/recaptcha/docs/verify)

APIの問い合わせ方法もそれほど難しくありません。

```JSON
{
  "success": true|false,      // whether this request was a valid reCAPTCHA token for your site
  "score": number             // the score for this request (0.0 - 1.0)
  "action": string            // the action name for this request (important to verify)
  "challenge_ts": timestamp,  // timestamp of the challenge load (ISO format yyyy-MM-dd'T'HH:mm:ssZZ)
  "hostname": string,         // the hostname of the site where the reCAPTCHA was solved
  "error-codes": [...]        // optional
}
```

`action` ってところは `grecaptcha.execute` 時に指定できます。
厳密な検証という意味では、このaction, challenge_ts, hostname あたりチェックしておくことも必要でしょう。

### 3. Interpreting the score(スコアに従った処理)

いわゆる「パズルを突破したらおk！」な方法ではないので、実際のプロダクトに入れていくことを考えるとここが一番大事になりますね。
ドキュメントには Use case / Recommendation として、bot判定への使い方みたいなのが書いてます。

## 動作確認用のwebページ

https://recaptcha-sample.appspot.com/ <- お金かかるので止めました。Elixir(Phoenix)なソースコードは https://github.com/ritou/elixir-recaptcha-example 

取得したトークンを自分に投げて検証結果を表示するだけの人です。

よく使うブラウザで試すと高いスコアになりました。

![recaptcha_v3_03.png](https://qiita-image-store.s3.amazonaws.com/0/10192/6cb7bb19-974c-2a8e-004f-18852a1c67d8.png)

シークレットウィンドウだと低めになりました。

![recaptcha_v3_04.png](https://qiita-image-store.s3.amazonaws.com/0/10192/58ce000c-0a4b-a2d7-1b1c-4219fa9b11cd.png)

botでは試してませんがいつかやる(やらなそう)

以上です。
2018年6月25日にFirebaseのBigQueryへのExportスキーマが変更されたので対処する

FirebaseからBigQueryへiOS/AndroidのログをExportしているのですが、6月25日にスキーマが以下に変更されました。

* https://support.google.com/firebase/answer/7029846?visit_id=1-636656596224100576-4160687049&rd=1

移行の仕方は以下にあります（英語版にして`Use this script to migrate existing BigQuery datasets from the old export schema to the new one`を開いてください。そこにかいてある手順を実行すれば大丈夫です。

* https://support.google.com/analytics/answer/7029846?visit_id=1-636656596224100576-4160687049&rd=1&hl=en#migrationscript

移行手順書こうとおもったら手順そのまんまだったので移行していて気づいたことを書きます

# 気づいたところ

* Export先は`analytics_<property_id>`というデータセット名だが、<property_id>は、Firebaseの設定=>データのプライバシー=>アナリティクスの設定 からアクセスできた。ほかの行き方もありそうだがひとまず。
* １イベントで１レコードになった
* app_instance_idはuser_pseudo_idにrename。ほかにもかわっているやつがあるが、migration_script.sqlを見ればよさそう
* migration.shはIOSとANDROIDと２回動かす必要がある
ServiceWorker メモ

初心者のただの勉強メモ

本稿では、**普通の JS = scriptタグで読み込まれる従来型の JavaScript** という意味で使います。あしからず。

# ちゃちゃっと理解したい
- [Service Worker の紹介 - Web Fundamentals](https://developers.google.com/web/fundamentals/primers/service-workers/)
    - Google の記事。日本語。複数記事がぶるさがっているのでまとめて読もう
- [サービスワーカーの使用 - MDN](https://developer.mozilla.org/ja/docs/Web/API/ServiceWorker_API/Using_Service_Workers)
    - 20分くらいで読める日本語ドキュメント
    - デモ: 
        - デモサイト https://mdn.github.io/sw-test/
        - コード https://mdn.github.io/sw-test/

一通り読んだら大体わかると思うので後は Qiita なり Medium なりでノウハウを集めるのがよい。


# 普通の JS との違い
- イベントループが違う
    - 普通のJSはレンダリングと同じイベントループで動作するが、ServiceWorker は独立したイベントループで動作
    - ServiceWorker が他のCPUで動作しうるので、「マルチスレッドな並列処理が可能な技術」といえる
    - レンダリングを邪魔しないで処理をする技術ともいえる
- ライフサイクルが違う
    - 普通のJS はブラウザタブを閉じたら終了
    - Serviceworker はブラウザ上で動作し続ける
    - ServiceWorker は複数のページ(scope)の JS と連携して動作する
- DOM 操作はできない
    - ServiceWorker は DOM をいじれない、じゃあ何するのよ？答えは↓
    - fetch, sync, push のイベントで駆動させる
        - fetchイベント: 普通の JS の HTTPリクエストをインターセプト
        - syncイベント: オフラインからの復帰動作を定義
        - pushイベント: 通知関連
    - ![MDNより拝借](https://mdn.mozillademos.org/files/12632/sw-events.png)

# ServiceWorker を作る単位

アプリごとに一つだけ作るのが基本。例えば、一つのサイトで二つのアプリを提供するなら、 `sw.js` を二つ作って、アプリの機能ごとには作らない。

```
/app1/index.html
/app1/sw.js
...
/app2/index.html
/app2/sw.js
...
```

ServiceWorker の実装を変えたい場合、単純にWebブラウザの `sw.js` を置き換えちゃえばOK。実行中の SeiviceWorker とかどうなるのよ？みたいな疑問はライフサイクルについて、前述の資料で学ぼう。↓のようなライフサイクルとそれに付随するイベントがあるので何かしたい場合は、そのイベントハンドラで実装を書く

![MDNより拝借](https://mdn.mozillademos.org/files/12636/sw-lifecycle.png)

# ServiceWorker使い道

- オフライン対応

- 通知
よく見るHTTPステータスコードをまとめてみた

# HTTPステータスコード  
サーバからブラウザに返してくるコードのことをステータスコードと言います。
これを見れば処理がうまくいっているのか、なにが原因でうまくいっていないかのステータスを把握することができます。
今回はステータスコードの中でもよく見るものをまとめてみました！  

## 目次  
- 100番台 Informatinal  
- 200番台 Success  
- 300番台 Redirection  
- 400番台 Client Error  
- 500番台 Server Error  
<br>

## 100番台 Informatinal  
リクエスト情報処理中 (←あんまりお目にかからない)
「今やってまっせ」  

## 200番台 Success  
リクエスト成功！！
「今日も平和ですね。」
ユーザが一番受け取っているであろうリクエスト。問題なくうまくいっているのでまぁ意識する必要はないですね。  

## 300番台 Redirection  
    301 Moved Permanently  
URL変わりましたよっていうやつ
検索エンジンに「あ、お引越ししました。もう戻ってきません。」って伝えるお役目がある。
サイトを移転したときなどに、移転元のURLに対して301リダイレクトをして移転先のURLに飛ばすといった流れになりますね。  


    302 Found  
URL変わりました。※一時的な変更です。
「そのうちまた使えるようになります」って検索エンジンにお伝えする。
サイトのメンテナンスなどで、一時的に別ページに飛ばす時などに使用されています。  


    304 Not Modified  
未更新 ※エラーではない
「新しくサーバからデータを貰わないで自分が前にダウンロードして表示したデータを使ってね！」
ブラウザ内のキャッシュに残っているコンテンツを使って表示させるときなどにこれが帰ってきています。  

## 400番台 Client Error  
→クライアント側に起因するエラー  

    400 Bad Request  
クライアントのリクエストがおかしい。
ブラウザか端末に起因してることが多いです。他のブラウザを使うなどして試してみるといいかと思います。

    403 Forbidden  
禁止
「このURL使う権限がないからダメ！！」
このリクエストが帰ってきているときはアクセスの権限周りに起因しています。  


    404 Not found  
見つかりませんでした。
「なかったんだけどほんとにあるの？URL間違えてない？」
存在しないURLに対してアクセスしようとしているときに帰ってきます。

404は各サイトで作り込まれていることも多く、例えば下記はGitHubの404ページです。
![GitHub404](https://i.imgur.com/q6LU8Ff.png)
スタウォーズエピソード4のオビワンをオマージュしているようですね！個人的にお気に入りです。  


## 500番台 Server Error  
→PHPだとかRubyだとか、サーバの処理に起因するエラー  


    500 Internal Server Error  
サーバ内部エラー
※コード内にsyntax errorがあった場合など。
バグが原因で見れないときなどはこれですね、急いで対処しましょう。  

    503 Service Unavailable  
サーバが使えません。
サーバがメンテナンス中や大量アクセスなどで使用できないときにこれが返ってくる。
某ジブリ映画の某呪文に合わせた大量にツイートでサーバが落ちたとかいう話聞いたことありますよね。
「バルス！！！」  
<br>
HTTPリクエストコードのなかでもよく目にするところに絞ってまとめてみました。
単にページにアクセスできない時も返ってきているステータスの意味がわかれば原因解明しやすいですよね！
Rails5.2.0のdevelopment環境におけるsecret_key_baseは設定できない

# はじめに
Rails 5.2から、config/secret.ymlは廃止され、基本的にconfig/credentials.yml.encで管理するように変更されています。credentials.yml.enc自体は暗号化されているので、通常通りgit管理下に置いておいて、master.keyという復号キーのみを別管理にするという仕組みに変わりました。
credentials.yml.encにはsecret.ymlで書いていたようなsecret_key_baseを記述します。（他にもAPIキーなども記述できます）

production環境だと上のような形で問題ないのですが、development, test環境ではsecret_key_baseを指定できません＞＜Rails側で適当に作られてしまっているのです！！

# どういうこと？

[Rails 5.2.0ブランチ](https://github.com/rails/rails/blob/5-2-0/railties/lib/rails/application.rb)におけるsecret_key_baseを取得するメソッドは下のようになっています。

```rb
def secret_key_base
  if Rails.env.test? || Rails.env.development?
    Digest::MD5.hexdigest self.class.name
  else
    validate_secret_key_base(
      ENV["SECRET_KEY_BASE"] || credentials.secret_key_base || secrets.secret_key_base
    )
  end
end
```

お分かりいただけただろうか……development, test環境だと適当な文字列が適当にハッシュ化されたもの以外設定できません。
ついでに、productionでの仕様もわかりやすいですね、ENV→credentials→secrets(config/secrets.yml)の順に優先度が低くなっていきます。


これは流石にまずかったのか、[Rails 5.2-stableブランチ](https://github.com/rails/rails/blob/5-2-stable/railties/lib/rails/application.rb)では以下のように修正されました。config/secrets.ymlがあればそちらが優先されます。


```rb
def secret_key_base
  if Rails.env.test? || Rails.env.development?
    secrets.secret_key_base || Digest::MD5.hexdigest(self.class.name)
  else
    validate_secret_key_base(
      ENV["SECRET_KEY_BASE"] || credentials.secret_key_base || secrets.secret_key_base
    )
  end
end
```


# なんでこの問題にハマったか
message_verifierの暗号化と復号化をそれぞれ別Railsプロジェクトで行おうとすると、secret_key_baseを同じ値にする必要が出てきますが、Rails5.2.0のdevelopmentだとどうしてもInvalidSignatureエラーが出てくるので、不審に思いソースまで追ってみました。

そもそもsecret_key_baseを複数プロジェクトで共有するという作りもどうなんだろう？という感じはしています。


参考：[RailsのMessageVerifierの内部実装を追ってみた](https://qiita.com/johro/items/06b146d749946e13ae0d)
文字列が同じものかどうか判定するプログラム(完全一致)

##文字列が同じものかどうか判定するプログラム　
タイトル通りに今回は二つの文字列を比較してそれが同じ文字列であるかどうかの判定を出力するプログラムを書きます

今回はアルファベットの同じAでも大文字のAと小文字のaの様に小文字、大文字の違いのように文字列が完全に等しくないと違う文字列として判断するプログラムを書きました。
##ソースコード
<div class="code-frame" data-lang="text"><div class="highlight">
<pre>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
    class Hikaku
    {
        static void Main()
        {
            string s1 = "ABC";
            string s2 = "abc";

            //s1とs2が等しいなら「等しいです」を、等しくないなら「等しくないです」を出力する
            if (s1 == s2)
            {
                Console.WriteLine("等しいです。");
            }
            else
            {
                Console.WriteLine("等しくないです。");
            }
            Console.ReadKey();
        }
    }
}

</pre></div></div>
##結果
![大小区別比較結果.PNG](https://qiita-image-store.s3.amazonaws.com/0/249276/a916885e-6916-3085-cbce-46c88820551e.png)
今回はstring s1の文字列ABCとstring s2の文字列abcを比較して大文字と小文字の違いがあったので文字列は等しくないものと判定して結果を表示しました。
##まとめ
今回は単純に文字列が完全に一致しているかどうかを判定するためのプログラムを書きました。
次回は他の比較方法を試したいです
BottomNavigationViewの影を表示しない


マテリアルデザインとしては正しくないですが、デザイン上の理由でBottomNavigationViewの影を表示したくないことがあります。
その場合は`app:elevation="0dp"` を指定すれば影を表示しないことが可能です。

```xml
    <android.support.design.widget.BottomNavigationView
        app:elevation="0dp"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:background="?android:attr/windowBackground"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:menu="@menu/navigation" />
```
Google Maps APIのMarker Clusterers libraryを使用した際のcluster iconのclick event

## やりたいこと
cluster iconをクリックしたときの挙動を変えたい（デフォルトはクリックするとズームする）

##コード
```markerclusterer.js
/**
 * Triggers the clusterclick event and zoom's if the option is set.
 */
ClusterIcon.prototype.triggerClusterClick = function() {
  var markerClusterer = this.cluster_.getMarkerClusterer();

  // Trigger the clusterclick event.
  google.maps.event.trigger(markerClusterer, 'clusterclick', this.cluster_);

  if (markerClusterer.isZoomOnClick()) {
    // Zoom into the cluster.
    this.map_.fitBounds(this.cluster_.getBounds());
  }
};
```
ライブラリの上記がcluster iconがクリックされた時のイベント部分です。
`markerClusterer.isZoomOnClick`はmarkerClustererのオプションです。

|Name              |Type               |Description        |
|:-----------------|:------------------|:------------------|
|zoomOnClick       |boolean            |Whether the default behaviour of clicking on a cluster is to zoom into it.|

zoomOnClickがtrueのときにズームされるみたいです。
単純に常にズームしないのであれば`zoomOnClick:false`にすれば良いです。
また、例えばズームレベルが9未満のときにのみズームさせるのであれば、`  if (markerClusterer.isZoomOnClick()) {`の部分に以下を追記すれば良いです。

```markerclusterer.js
  if (markerClusterer.isZoomOnClick()) {
    if(this.map_.getZoom() < 9){
    // Zoom into the cluster.
    this.map_.fitBounds(this.cluster_.getBounds());
    }
  }
```

私の場合は、単にmapをドラッグしたいだけなのに、cluster iconのところでドラッグしてしまうと、ドラッグした後にズームしてしまう:pouting_cat:のをなんとかしたかったので、上記の部分を「ドラッグしていたらズームしない」というように変えました。

参考：[Gmaps Marker Clusterer](https://gmaps-marker-clusterer.github.io/gmaps-marker-clusterer/)
個人用メモ２ - SkyWay

個人的に調べた内容を自分のメモとして書き残します。

---

Web アプリ開発で使用予定の、「**SkyWay**」について をまとめる

---

■ SkyWay とは

- Web会議
- サービスデスク
- オンライン診療 ( 遠隔医療 )
- IoT

などの機会において、
**オンラインリアルタイムコミュニケーション** を提供しているサービス


■ 料金

- **Community Edition**
 - 基本料金 -- ¥0
 - サーバ通信料 -- ¥0
- **Enterprise Edition**
 - 基本料金 -- 初期¥0 + 月額¥100,000
 - サーバ通信料 -- ¥40/GB

の二つがある
どちらも、会員登録が必要

**Community Edition** は一切、料金がかからない
**Enterprise Edition** は、商用サービスや大規模サービスに利用できる


■ **SkyWay Conference**

- インストール **無し**
- 会員登録 **無し**
- ログイン **無し**

で利用できるサービス

ブラウザで URL を共有するだけで、多人数とも通話・画面共有が可能

**[SkyWay Conference を開く](https://conf.webrtc.ecl.ntt.com/)**


■ 実装するには

実装するには、まず **SkyWay への開発者登録**が必須
( Community Edition の新規登録 ⇒ 開発者登録 )

SkyWay 公式サイトより、**[JavaScript SDK チュートリアル](https://webrtc.ecl.ntt.com/js-tutorial.html)** が用意されていたので、割愛させていただきます

---

Web アプリ開発に使用するにあたり、もっと深く掘り下げて調査していく予定です。

最後までお読みいただきありがとうございました。
DocumentRootから別ディレクトリにredirect

#「.htaccess」で実装するやり方

httpd.confで編集するとシステムの再起動が必要になってくるのでこの方法で実装。
※Apacheサーバーでの実装になります。

実際にサーバーで記載する時は下記の「〇〇〇」をディレクトリ名に変更して記載する。

```linux:.htaccess
RewriteEngine On
RewriteRule ^$ 〇〇〇/ [L]
RewriteCond %{REQUEST_FILENAME} !-f
# !-f は存在するファイル名にはルールを適用しない
RewriteCond %{REQUEST_FILENAME} !-d
# !-d は存在するディレクトリ名にはルールを適用しない
RewriteRule ^(.+)$ 〇〇〇/$1 [L]
```

##オプション

| 定義名 | 説明 |
|:-----------|:------------|
| REQUEST_FILENAME|  ドメインの後に入力されたファイル名orディレクトリ名を参照する。 |


サーバー関係の定義関数はまとめていきたい
２語で構成されるコマンドをmanする方法

# 解決したかったこと
2語で構成されるコマンド(git pushとか)
を単純に`man git push`みたいにすると

```
NAME
       git - the stupid content tracker

SYNOPSIS
       git [--version] [--help] [-C <path>] [-c <name>=<value>]
           [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]
           [-p|--paginate|--no-pager] [--no-replace-objects] [--bare]
           [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]
           [--super-prefix=<path>]
           <command> [<args>]

DESCRIPTION
       Git is a fast, scalable, distributed revision control system with an unusually rich command set that provides both high-level operations and
       full access to internals.
```

最初の引数`git`のマニュアルが表示されてしまう

# ハイフン`-`で繋げる

`man git-push`とすると

```
NAME
       git-push - Update remote refs along with associated objects

SYNOPSIS
       git push [--all | --mirror | --tags] [--follow-tags] [--atomic] [-n | --dry-run] [--receive-pack=<git-receive-pack>]
                  [--repo=<repository>] [-f | --force] [-d | --delete] [--prune] [-v | --verbose]
                  [-u | --set-upstream] [--push-option=<string>]
                  [--[no-]signed|--sign=(true|false|if-asked)]
                  [--force-with-lease[=<refname>[:<expect>]]]
                  [--no-verify] [<repository> [<refspec>...]]

DESCRIPTION
       Updates remote refs using local refs, while sending objects necessary to complete the given refs.

       You can make interesting things happen to a repository every time you push into it, by setting up hooks there. See documentation for git-
       receive-pack(1).
```

と無事にマニュアルが見れる
koin + ViewModelでDialogFragmentから値を受け取る

## 背景
[Share data between fragments](https://developer.android.com/topic/libraries/architecture/viewmodel#sharing)
をkoin + DialogFragmentに適用したらこんな感じ？

## 実装
```kotlin:MainActivity.kt
class MainActivity : AppCompatActivity() {
  private val model by viewModel<MainViewModel>()
  private val sharedViewModel by viewModel<MainDialogFragment.SharedViewModel>() // 2

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    val binding = DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
    binding.button.setOnClickListener {
      MainDialogFragment().show(this.supportFragmentManager, MainDialogFragment.TAG)
    }
    this.sharedViewModel.selected.observe(this, Observer { selected -> // 4
      this.model.string.postValue(selected)
    })
  }
}
```

```kotlin:MainDialogFragment.kt
class MainDialogFragment : DialogFragment() {
  private val sharedViewModel by sharedViewModel<SharedViewModel>() // 3

  override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
    val binding = DataBindingUtil.inflate<FragmentMainDialogBinding>(inflater, R.layout.fragment_main_dialog, container, false)
    binding.buttonDone.setOnClickListener {
      this.sharedViewModel.selected.postValue(binding.editText.text.toString()) // 5
      this.dismiss()
    }
    return binding.root
  }

  class SharedViewModel : ViewModel() { // 1
    val selected = MutableLiveData<String>()
  }
}
```

1. SharedViewModelはDialogFragmentの持ち物とする
2. Activity（DialogFragmentを表示する）側：ViewModelをinject
3. DialogFragment側：`sharedViewModel`でActivityと共有されたViewModelをinject
4. Activity側：DialogFragmentでの変更を待ち受ける
5. DialogFragment側：postValueで変更を伝える
バイアスとバリアンス

# はじめに
機械学習で汎用的なモデルを作成するにはバイアスとバリアンスが大切と実感することがあったのでメモします。

# 上手くモデルがつくれないときのパターン分け
モデルは現実の近似。モデルの現実との僅差として2つの誤差要因に分けられる。

## irreducible error（既約誤差）
未知であったり予測不可能な要因や単なる偶然のこと、減少させることはできない。

## reducible error（可約誤差）
モデルの調整によってさらに低減することができる誤差。
「2乗偏りによる誤差」および「分散による誤差」にさらに分解することができる。

## バイアスとバリアンス
バイアス：モデルの偏りの大きさ。
バリアンス：モデルの過学習のしやすさ。

バイアスとバリアンスはトレードオフの関係にある。
モデルがシンプルな場合：高バイアス・低バリアンス
モデルが複雑な場合：低バイアス・高バリアンス
has_secure_passwordを使うとcannot load such file -- bcryptが出る

## 起

https://railstutorial.jp/chapters/modeling_users?version=5.1#sec-a_hashed_password 


> セキュアなパスワードの実装は、has_secure_passwordというRailsのメソッドを呼び出すだけでほとんど終わってしまいます。このメソッドは、Userモデルで次のように呼び出せます。

```ruby:user.rb
class User < ApplicationRecord
  has_secure_password
end
```
> 上のようにモデルにこのメソッドを追加すると、次のような機能が使えるようになります。

加えると以下のエラーが出るようになった。
cannot load such file -- bcrypt

## 承
gemを確認する

gem list
> Using bcrypt 3.1.12 (x86-mingw32)

なんだこの、(x86-mingw32)というのは。違うでしょ、railsで使うんでしょ。

## 転
https://github.com/codahale/bcrypt-ruby/issues/149

> gem uninstall bcrypt
> gem install bcrypt --platform=ruby

上記を実行したうえで再起動したら直った～。

## 結
いろいろありますなあ。
The python_package resource requires pip >= 6.1.0, currently 10.0.0

# 環境
pip 10.0.0
poise_python 1.6.0

# エラー
`STDERR: The python_package resource requires pip >= 6.1.0, currently 10.0.0`

# エラーの原因
poise_pythonの中で
pipの内部APIを叩こうとするが
pip 10から内部APIを外から叩くことができなくなっているので
エラーになる

# 解決法
poise_python 1.7.0から
新しいpip 10の仕様に対応するようになったので
1.7.0~ を使う

# 参考
https://github.com/poise/poise-python/issues/107
WANとLANについて

# はじめに

このブログは授業で受けた内容のまとめや感想などを書いています。

今回は自分で勉強したWANとLANについて簡単にまとめた記事になります('ω')

#項目
**・WAN**
**・LAN**

#WAN
wanとはwide Area Network(ワイドエリアネットワーク)の略称です。
![Image_wideareanetwork1.png](https://qiita-image-store.s3.amazonaws.com/0/249281/ac7ea3d8-713f-a039-629d-62570b75846d.png)
引用元：ウィキペディア

WANは広範囲のネットワークをつなぐもので、これを使うにはISPと契約する必要があります
WAN回線を使ってLANとLANを接続するやり方で、用途としては、東京本部から大阪支部に接続するときなどにも使います。

※ISPはインターネットサービスプロバイダの略称でインターネット接続の電気通信役務を提供する組織のことを指します。

#LAN
LANは Local Area Network(ローカルエリアネットワーク)の略称です。
基本的に家庭で使ってるものや会社の中のネットワーク、簡単に言えば1施設規模のネットワークです。

接続としては無線LANと有線LAN(イーサネット)があります。どちらも一般的に使用するのなら速度は問題ありません。
無線のメリット・デメリットと有線のメリット・デメリットは次の記事で説明させていただきます。

#最後に
簡単にまとめてみましたが、間違いなどがあればご指摘いただければ幸いです(>_<)
次はLANの無線と有線のことまとめて記事を作ろうと思います。
C#で文字列を連結する方法 

(+)演算子を使って文字列の連結ができる。

コード：
<pre>
string str0 = "清風";
string str1 = "情報工科学院";

//(+)演算子
string result = str0 + str1;

Debug.WriteLine(result);

</pre>

結果:
![スケッチ (7).png](https://qiita-image-store.s3.amazonaws.com/0/251603/bb1d1b95-f087-0dac-77a4-4cc1df1b759b.png)
create-react-appでcss modules

`eject`しないで行う方法

- create-react-appはver.2でcss modulesをサポートするため、それまでの繋ぎ
- [Roadmap for react\-scripts@2\.0 · Issue \#3815 · facebook/create\-react\-app](https://github.com/facebook/create-react-app/issues/3815)

### やり方

#### react-app-rewired, react-app-rewire-css-modules 入れる

ejectせずに、webpack, babel, estlintなどの設定を上書きするツールと、css moduleを有効にするプラグインを使って実現

```sh
yarn add --dev react-app-rewired codebandits/react-app-rewire-css-modules
```

#### sass-loader node-sass入れる

プラグインがsass-loaderとnode-sassに依存してるのでこれも追加（入れないとエラー吐く）

```sh
yarn add --dev sass-loader node-sass
```

sass使わなくても入れるのが必要


#### config-overrides.js作って、設定をかく

`package.json`と同じ階層にファイルを置く

```js
//設定例
const rewireCssModules = require('react-app-rewire-css-modules');

module.exports = function override(config, env) {
  return rewireCssModules(config, env);
};
```

#### npm scripts修正

create-react-appが生成した

```sh
yarn start
yarn build
yarn test
yarn eject
```

の値を更新

```diff
-   "start": "react-scripts start",
+   "start": "react-app-rewired start",
-   "build": "react-scripts build",
+   "build": "react-app-rewired build",
-   "test": "react-scripts test --env=jsdom",
+   "test": "react-app-rewired test --env=jsdom"
```

#### css moduleを有効にしたいcssファイルの拡張子を `.css`から `.module.css`に変える

```diff
- import style from './style.css';
+ import style from './style.module.css';
```

`style.css`や`module.css`だとcss modulesが有効にならない
C#で今日の日付を表示する

日付や時刻を扱ういくつかのクラスの中で、主役となるのは「DateTime構造体」である。
現在の日付および時刻を取得するためには、そのスタティックなNowメソッドを使用する。
現在の日付だけがほしい場合は、Todayメソッドを使用する。
<pre>
DateTime today = DateTime.Today;

Debug.WriteLine("今日の日付は：" + today);

DateTime now = DateTime.Now;

Debug.WriteLine("現在の日付は：" + now);
</pre>

結果：
![スケッチ (7).png](https://qiita-image-store.s3.amazonaws.com/0/251603/26b116f4-a623-1192-3acd-9b7a40122286.png)