OmniAICreator/Qiita-1.07M huggingface dataset:
Javaでgzファイルを解凍するサンプル

サーバ上のgzの中身を確認する必要があるにも関わらず、手元のへっぽこWindows7 PCには7zipのようなgzに対応したソフトウェアがインストールされていない。サーバ上で解凍すると、元ファイルが巨大すぎて、手元のPCに持ってこれない恐れがある。手元で使えそうなものはJavaとEclipseだけ--ということで、Javaでgzファイルを解凍するという機会がありましたので、そのサンプルコードをQiitaに供養しておきたいと思います。

```java
import java.io.IOException;
import java.io.OutputStream;
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.zip.GZIPInputStream;

public class GZIPUtils {
    public static void unzip(Path src, Path dst) {
        try (GZIPInputStream in = new GZIPInputStream(Files.newInputStream(src));
             OutputStream out = Files.newOutputStream(dst)) {
            int len;
            byte[] b = new byte[1024 * 4];
            while ((len = in.read(b)) != -1) {
                out.write(b, 0, len);
            }
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    }
}
```

稼働確認をしてみましょう。まず適当なテキストファイルを作成します。ここでは1MB, 10MB, 100MBのファイルを作るとして、名前をそれぞれ001mb.txt, 010mb.txt, 100mb.txtとしておきましょう。

```
$ cat /dev/urandom | tr -cd [:print:] | head -c 1m   > 001mb.txt
$ cat /dev/urandom | tr -cd [:print:] | head -c 10m  > 010mb.txt
$ cat /dev/urandom | tr -cd [:print:] | head -c 100m > 100mb.txt
```

この3つのファイルについて、メッセージダイジェストを取得しておき、正しく解凍できているかどうかの検証に使うことにします。

```
$ sha256sum *.txt
f2cddb9f0bb64033f7a9d8979a47d925af06271b2e7d79b99bd9e41ad504616d  001mb.txt
0282119ac4c00a1c14370b65e72f3283d988264fb3240f46b679ada117db3979  010mb.txt
1104a28dd5a4033c5dd495289b590526b60c1f285357dd26af53ed0f5c4898c0  100mb.txt
```

メッセージダイジェストも取得できたということで、この3つのファイルをGZIP形式で圧縮します。

```
$ gzip *.txt
$ ls -lh *.gz
-rwxrwxrwx 1 dev dev 852K Nov  7 20:33 001mb.txt.gz
-rwxrwxrwx 1 dev dev 8.4M Nov  7 20:33 010mb.txt.gz
-rwxrwxrwx 1 dev dev  84M Nov  7 20:34 100mb.txt.gz
```

ここで圧縮したファイルを自作のJavaプログラムで解凍してみます。以下の`Main.java`は`GZIPUtils::unzip`を利用して001mb.txt.gz, 010mb.txt.gz, 100mb.txt.gzをそれぞれunzipped_001mb.txt, unzipped_010mb.txt, unzipped_100mb.txtという名前に解凍します。

```java:Main.java
import java.nio.file.Paths;

public class Main {
    public static void main(String[] args) {
        GZIPUtils.unzip(Paths.get("001mb.txt.gz"), Paths.get("unzipped_001mb.txt"));
        GZIPUtils.unzip(Paths.get("010mb.txt.gz"), Paths.get("unzipped_010mb.txt"));
        GZIPUtils.unzip(Paths.get("100mb.txt.gz"), Paths.get("unzipped_100mb.txt"));
    }
}
```

あとは`Main.java`をコンパイルしたあとに実行、その実行結果を確認すると、想定通りunzipped_001mb.txt, unzipped_010mb.txt, unzipped_100mb.txtという3つのファイルが作成されていることがわかります。

```
$ javac Main.java GZIPUtils.java
$ java -cp . Main
$ ls -hl unzipped*
-rwxrwxrwx 1 dev dev 1.0M Nov  7 23:09 unzipped_001mb.txt
-rwxrwxrwx 1 dev dev  10M Nov  7 23:09 unzipped_010mb.txt
-rwxrwxrwx 1 dev dev 100M Nov  7 23:09 unzipped_100mb.txt
```

この3つのファイルについてメッセージダイジェストを確認すると、圧縮前のtxtファイルと全く同じことがわかります。つまり`GZIPUtils::unzip`は想定通り動作していると考えられます。

```
$ sha256sum unzipped*
f2cddb9f0bb64033f7a9d8979a47d925af06271b2e7d79b99bd9e41ad504616d  unzipped_001mb.txt
0282119ac4c00a1c14370b65e72f3283d988264fb3240f46b679ada117db3979  unzipped_010mb.txt
1104a28dd5a4033c5dd495289b590526b60c1f285357dd26af53ed0f5c4898c0  unzipped_100mb.txt
```
Vue.jsコンポーネント間の通信【クラス属性、id属性の受け渡し】

##はじめに
[先日の記事](https://qiita.com/noytdm1021/items/b0b5f44ba3e147464639)に引き続き、
コンポーネント間の通信についてまとめます。今回はコンポーネントを呼び出す際にid属性とクラス属性を付与するとどんな挙動になるのか調べました。

##完成コード
*見やすさ考慮してbodyタグの中しか書きません。

```ruby:index.html
<div id="app" class='box'>
        <p>my-componentの呼び出し</p><my-component></my-component>
        <hr>    
        <p>comp-childの呼び出し</p><comp-child id="parent" class="parent"></comp-child>
    </div>
```
次にJSです。

```ruby:main.js
Vue.component('my-component',{
    template:'<p><comp-child id="parent" class="parent" v-bind:val="message"></comp-child></p>',
    data:function(){
        return{
            message:'my-component'
        }
    },
    props:['val']
})
Vue.component('comp-child',{
    template:'<p id="child" class="child">{{message}}/{{val}}</p>',
    data:function(){
        return{
            message:'child-comp'
        }
    },
    props:['val']
})

  new Vue({
    el:'#app',
    data: {

    }
  })
```

##実行結果
<img width="854" alt="スクリーンショット 2019-11-07 22.48.17.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496032/c65fb6f2-dc11-bf40-8b03-1cc70d9aa81c.png">

child-comp/my-componentとchild-compを出力しているPタグ内のid属性とclass属性に注目しておいてください。

##解説
my-componentの呼び出しから辿っていきましょう。

```ruby:index.html
<p>my-componentの呼び出し</p><my-component></my-component>

```

html内で普通に記述しています。そして呼び出されたコンポーネントの中身を見ます。

```ruby:main.js
Vue.component('my-component',{
    template:'<p><comp-child id="parent" class="parent" v-bind:val="message"></comp-child></p>',
    data:function(){
        return{
            message:'my-component'
        }
    },
    props:['val']
})
```
```template:'<p><comp-child id="parent" class="parent" v-bind:val="message"></comp-child></p>'```に注目です。
comp-childを呼び出す際に```id="parent" class="parent" ```を付与しています。
そこで今度はcomp-childを見ていきましょう。

```ruby:main.js
Vue.component('comp-child',{
    template:'<p id="child" class="child">{{message}}/{{val}}</p>',
    data:function(){
        return{
            message:'child-comp'
        }
    },
    props:['val']
})
```
今度はtemplate内のPタグ内で```<p id="child" class="child">{{message}}/{{val}}</p>'```と別のid属性とクラス属性を指定しています。

すると結果、、、
<img width="524" alt="スクリーンショット 2019-11-07 23.01.42.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496032/68b29d7f-3400-211e-5e27-b47d86ae47c0.png">

id属性はコンポーネント内でchildとしているにも関わらず親コンポーネント内で付与したparentに上書きされ、クラス属性についてはどちらも適用されました。

comp-childについては、html内で同様に定義すると処理内容はmy-componentのtemplateと同じ動きをするため、説明は省略します。

<strong>最後まで読んでいただきありがとうございました。

追記：id属性とクラス属性の引き渡しについてはpropsは使っていません。
amazon forecastやってみた - 3. amazon forecastの設定

# はじめに
今日イベントでAmazon forecastで時系列予測の事例紹介を見たところ
とてもお手軽にできそうな感じがつかめたのでその日のうちにやってみた。
参考：https://dev.classmethod.jp/cloud/aws/amazon-forecast-getting-start/

大きな流れはコチラ。
　1. 学習データを取得する
　2. 学習データのインポート
　3. amazon forecastの設定　←今回はココ！
　4. 学習結果の確認

# 3. amazon forecastの設定
ここからがamazon forecastの本領。
まずはawsからamazon forecastのサービスを探してアクセス
ＴＯＰ画面がコチラ。[Create dataset group]クリック
![キャプチャ20.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/416263/182f9cba-4e27-fd85-1904-3a86c1ce7948.png)

dataset groupの名前を決める。なんでもいい。
forecast domainはどういう性質のデータを取り扱うのか？を訪ねている。
売上予測とかシフト計画とか色々選べるけど、
残念ながら今回の気温の推移に関する選択肢はなかったので
Customを選択して[Next]クリック
![キャプチャ21.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/416263/ab49dd08-9d5d-b17d-84bb-080bd6f4d610.png)

続いて、データセットに読み込ませる要素（カラム列）の指定
最低限必要な要素はデフォルトの画面にも表示しているように

- item_id      ←アイテムＩＤ（今回の場合だと地名）
- timestamp　　←日付
- target_value ←目的変数（今回の場合だと気温）

上記3種が最低限必要。
なお、AttributeNameはS3に保管したcsvファイルのカラム名に相当するのでそこの名前を合わせるのがポイント。
ちなみに今回の記事ではforecastのデフォルトの名前に合うように自分が用意したcsvのカラム名を合わせました。
Attributetypeは値の型。ほんとはもっと多彩なtypeが選べるとは思うけど、今回はたまたまうまいこと型を変えずに済んだのでこのままでいく。

![キャプチャ22.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/416263/21250bfc-49ae-d0e0-1ab6-1b9a95e78473.png)

![キャプチャ23.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/416263/db2b892e-4698-597f-5638-f89bd6d9a58c.png)

次の画面はデータセットの詳細設定画面。
タイムスタンプの書式もと工夫できるっぽいけど今回はデフォルトの型と合わせているので対応不要。

DataLocationでは、S3に格納したcsvファイルのパスを指定すればよいようだ。さっきパスコピーした値をセット。
![キャプチャ24.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/416263/7cfc531c-f1c3-d732-cbb6-8590ace44d40.png)

いよいよ、データのインポート～学習結果の確認といった
学習フェイズの画面。
まずはdatasetのインポートから。
画面左側の[target type your data]のところの[Start]クリック。意外と時間かかる。今回のデータだと5分程度かかった。

![キャプチャ25.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/416263/ef3c1f94-422e-a017-eb8b-69d7ad0e7bf1.png)

![キャプチャ26.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/416263/5b1bab9a-21a3-4b62-0715-2c09476ec0e3.png)

インポート完了したこんな感じ。
続いていよいよ学習の開始。[Predictor Training]の[start]クリック
![キャプチャ27.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/416263/ccaa6149-bc0d-e179-b7fe-b9858b0bdd09.png)

学習モデルの設定画面。
forecastHorizonはどんだけ未来まで予測するかを指定できる。
Algorithm Selectionは学習モデルを選択できる。
ここでCustomを選んで癖のあるモデルにしたいところだけど
今回はお手軽なAutomatic[AutoML]を選択。で、[Train Predictor]クリック
![キャプチャ28.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/416263/51d2e9ae-c02b-c81b-b88e-81a31ab001e0.png)

![キャプチャ29.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/416263/8eca6028-95b1-572b-3fcf-8c9d1fb70d33.png)

学習が始まった。ここからが長い。。。1時間超待った。
![キャプチャ30.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/416263/7c1fc4b0-182d-b79c-9883-e4e768f0000a.png)

学習完了！いよいよ結果発表！というところで次の記事に続く～。
![キャプチャ31.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/416263/002efcc6-8610-b0ef-b694-2fd3995930be.png)

# おわりに
時系列予測プログラムを作るときの要となる部分が
Automatic[AutoML]を選択するだけで完了する、
驚異的なお手軽さ。これがamazon forecast。まじやばい。

アルゴリズムのチョイスもさることながら
モデル評価も本来は大変なはずなんだけど
それもまるっとお任せできてしまう。それがamazon forecast、まじやばい。大事なので2度言いました。

～次回へ続く～
(変数)?true:falseは良いのかどうか？(javascript)

# (変数)?true:falseは良いのかどうか？
## 結論
個人の見解の部分が多いですが
`(変数)?true:false`は可読性は高く保てますが、ベストではありません。
二重否定の`!! 変数`が最高です。
短いことが可読性向上につながります。

## 具体例
〇〇〇〇には何を入るべきか？
1から順番に良いと考えています。
1. `!! is_true`
2. `Boolean(is_true)`
3. `is_true ? true : false`
4. `is_true || false`
5. `is_true`

```javascript

function isTrue(){
  let is_true;
  // 処理
  return 〇〇〇〇
}
```

## なぜ二重否定がベストで、そのまま返すのが悪いのか？
1か2、３でないと、Booleanではない値が返ってくる場合があります。

#### 代入し忘れ
例えば、処理部でis_trueへの代入を忘れた場合、以下の結果になります。
1. `!! is_true`=>`false`
2. `Boolean(is_true)`=>`false`
3. `is_true ? true : false`=>`false`
4. `is_true || false`=>`false`
5. `is_true`=>`undefined`

```javascript
const i = 0
const result = isTrue()
console.log(result)
function isTrue(){
  let is_true;
  if(i>100)
    is_true=true
  return 〇〇〇〇
}
```

#### 代入する型間違え
例えば、処理部でis_trueへboolean以外を代入した場合、以下の結果になります。
1. `!! is_true`=>`true`
2. `Boolean(is_true)`=>`true`
3. `is_true ? true : false`=>`true`
4. `is_true || false`=>`{}`
5. `is_true`=>`{}`

```javascript
const result = isTrue()
console.log(result)
function isTrue(){
  let is_true;
  is_true={}
  return 〇〇〇〇
}
```
関数定義で使用される"_"について

## "_"は何のため？
swiftのプログラムを見ていると、たまに関数の引数名の左に"_"(アンダースコア)が書いてあるときがあります。

```swift:howToUseUnderscore
func howToUseUnderscore(_ number1: Int, _ number2: Int) {
    print(number1 + number2)
}
```

引数名number1とnumber2の左に書かれている"_"は何を意味するのでしょうか、、、


## "_"で引数名を省略できる！

関数定義のとき、"_"を引数名の左に書くことで、関数を呼び出す際に引数名の記載を省略することができます。
 
- "_"を書かないとき

```swift:howToUseUnderscore
func howToUseUnderscore(number1: Int, number2: Int) {
    print(number1 + number2)
}

howToUseUnderscore(number1: 1, number2: 2)    // -> 3
```

- "_"を書いたとき
 
 ```swift:howToUseUnderscore
func howToUseUnderscore(_ number1: Int, _ number2: Int) {
    print(number1 + number2)
}

howToUseUnderscore(1, 2)    // -> 3
```

##ちなみに、、、
"\_"を書いているのに、関数を呼び出す際に引数名を記載するとエラーが起こります。
"\_"は関数を呼び出す際、「引数名を書かなくて良い」では無く、「引数名を書かない（書いてはいけない）」という意味のようです。

 ```swift:howToUseUnderscore
func howToUseUnderscore(_ number1: Int, _ number2: Int) {
    print(number1 + number2)
}

howToUseUnderscore(number1: 1, number2: 2)    // -> エラー！！
```

##### 参考サイト
- https://thinkit.co.jp/article/14301
文字化け怖くないVSCode

# VisualStudioCodeの文字コード設定
## 文字化けしたとき
開いているファイルをデコードする文字コードは手軽に変更できます。
![encode01.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26413/e62f114b-fac1-6b3d-ef15-28fafe92151a.gif)

1. 右下のエンコードボタンを押す
2. 「エンコード付きで再度開く」
3. 正しいエンコードを選択
簡単です。標準状態ではUTF-8で読み書きするようになっています。

## エンコードを固定したいとき
shift-jisしか使わない みたいな方はエディタの標準エンコードをへんこうします。
![set_encode.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26413/b7c8dda1-9ecd-26fa-ce21-15ec06918be2.gif)
1. Ctrl+, (カンマ)で設定画面を開く
2. 設定検索窓に"encode"と入力しエンコード設定を出す
3. 望みのエンコードをセット
4. 設定を閉じる
簡単です。これでファイルを開くときは指定文字コードでデコードします。
Woocommerce 各決済方法への アイコン追加

一発で済んだのでメモ。

アイコン画像がWoocommerceに同梱されているので
大体のクレカはコード追加のみでOK。
カード会社・画像ファイルは以下の通り。
カード会社/ファイル
Visa/visa
Master/mastercard
Diners/diners
JCB/jcb
Discover/discover
Maestro/maestro
laser/laser
Amex/amex

```php
function my_add_icon( $icons, $gateway_id ) {
  if( $gateway_id == '｛決済ID}' ){//該当の決済ID
 $icons .= '<img src="' . WC_HTTPS::force_https_url( WC()->plugin_url() . '/assets/images/icons/credit-cards/{画像ファイル}.png' ) . '" alt="｛カード会社｝" style="width:40px;" />';//複数のカードを追加するときはこの行をコピーして追加
 }
 return $icons;
}
add_filter( 'woocommerce_gateway_icon', 'my_add_icon', 10 ,2);
```
【Swift】CocoaPodsでRealmをインストールする方法 & importできないときの対処法。

# はじめに
iOSの勉強中に、データの管理をRealmにしようと思い、自分のプロジェクトにインストールしました。そのとき困った部分があったので、この記事を投稿します。
同じ境遇の人の助けになりますように、と🙌

#### 起こったこと
* `pod`で`RealmSwift`をインストール
* プロジェクト内でRealmをインポート(```import RealmSwift```を追記)
* エラーメッセージ「**No such module 'RealmSwift'**」
→あれれ

#### 結論…そのままビルドしたらエラー消えた、、！笑

CocoaPodsやRealmのインストール方法を知りたい方は、一緒に下に進んでいきましょう！↓↓↓

# やりたいこと
1. CocoaPodsを使う
2. RealmSwiftをインストールする
3. プロジェクトの中でRealmSwiftをimportする
4. 実行！

## 実行環境
|||
|---|---|
|macOS Catalina|10.15.1|
|Xcode|11.2|

## CocoaPodsを使うための準備(環境構築)
コマンドラインツールで以下のように打ちます。

```
sudo gem install cocoapods
```
これがうまくいかない方は以下のように打ってください。
```
sudo gem install -n /usr/local/bin cocoapods
```
何やらOSX El Capitanの方はこのようにする必要があるそうです。
詳しくは↓
[MacOSX El Capitanでcocoapodsインストールが出来ない時の対処法](https://qiita.com/tasaiii725/items/4bb4708de03e6ee14a4a)

インストールが終わったら、以下のように打っておしまいです！
```
pod setup
```

## RealmSwiftをプロジェクトにインストールする
プロジェクトのディレクトリに移動して以下を打ちます。
```
pod init
```
するとそのプロジェクトと同じディレクトリ内にPodfileが作られます。このファイルを開いて、pod 'RealmSwift'と追加します。(「ここ」の部分に追加↓)

```:Podfile
use_frameworks!
target 'プロジェクト名' do
**ここ**
end
```

↓

```:Podfile
use_frameworks!
target 'プロジェクト名' do
pod 'RealmSwift'
end
```

追加したら先ほどのディレクトリ(Podfileがあるディレクトリ)で以下のように打ちます。
```
pod install
```
するといくつかのファイルが追加されます。その中に```[プロジェクト名].workspace```があるので今後プロジェクトを進めていくときはこのファイルを使います(白いやつです)。

##### 注…元々使っていた青い```[プロジェクト名].xcodeproj```ではありません！

## プロジェクトの中でRealmSwiftをimportする
Realmを使いたいファイルを開いて、上部に```import RealmSwift```を追記する。
<img width="449" alt="スクリーンショット 2019-11-07 21.56.08.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/520690/08a9d6a7-324d-6b26-b5f3-3f4cf536101b.png">
これでおしまいです。が、**エラーメッセージ出るんですけど…**という方は、、、
<img width="176" alt="スクリーンショット 2019-11-07 22.00.08.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/520690/80551b4e-b0f3-d028-8c81-ae60f8bffa04.png">
**そのままビルドしましょう！解決します!笑**

　ここが、私が詰まって無駄な時間を取られてしまったところです。「とりあえず走らせてみるかー」って人は解決すると思うのですが、私のように「え？なんでできないんだろう。調べてもう一度インストールしてみようかな？」なんて人は無駄な時間を取られます。同じような人への手助けとなると嬉しいです。

また、記事の中で間違っているところ、おかしいところなどがございましたら、ご指摘お願いします!!
Azure DevOps と Application Insight を連携させてみる

今回は、Azure DevOps と Application Insight を連携してみます。

#前提条件
Azure DevOps で 二つの WebApp を管理/ビルドしていることを前提とします。今回は以下の記事で実施したものをそのまま使用します。
Azure DevOps で Release Line をカスタマイズし手動承認ができるようにする
https://qiita.com/komiyasa/items/505bf7c4c176b00f7e7c

上記に加えて Application Insight をデプロイしておきます。これを WebApp の Application Insight の Connection String に入力します。WebApp のポータルページから Application Insight を有効化する方法もありますが、今回は勉強も兼ねて Application Insight を別リソースとして作成します。以下記事が参考になります。
Azure App Service のパフォーマンスの監視
https://docs.microsoft.com/ja-jp/azure/azure-monitor/app/azure-web-apps

#チームのダッシュボードを更新する
Azure DevOps の　ダッシュボードに「Release Pipeline Overview」 Widget を追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149921/ea614b11-3cb1-3179-6239-97a0e10fb36d.png)
デフォルトの値で入力すると、リリースの状況の Widget が追加されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149921/dc2da244-d678-0010-87ae-824089dcc57e.png)

#Application Insights の Widget を追加する
Azure DevOps の Dashboard から Application Insights を検索して追加します。追加されていない場合は、拡張機能ポータル(Visuatu Studio の MarketPlace)から Application Insights を Install します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149921/59fa2297-742d-6215-ff6f-0d0503013566.png)

Install が出来たら、Application Insights 側に API Key を作成します。Azure Portal から 該当の Application Insight にアクセスし、 Access Key から API Key の発行を選択します。すると、API Key が発行されるので、それをWidget の中に挿入するだけです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149921/86d63a47-b56a-7e8b-45ae-bba97eb3fb1e.png)
これでダッシュボードにアプリケーション Insight から抽出した値を入力することができました。(時間がかかるので下の画像にはまだ数字が流し込まれていないです)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149921/33e30b6d-11b2-a423-7bf1-2b0ae060d373.png)

#参考
Azure App Service のパフォーマンスの監視
https://docs.microsoft.com/ja-jp/azure/azure-monitor/app/azure-web-apps
Macホームディレクトリの変更で発生したAnypointStudioのエラーで手間取った話

# はじめに
Mac初期設定時に作成したホームディレクトリ名が気に入らず。ホームディレクトリを変更したところ、Anypoint Studioが動かなくなり解決するのにかなり手間取った話です。Macの知識があれば簡単に克服できる話なのですが、まだまだ修行が足りないと痛感したお話です。。。

# ポイント
 1. 『An error has occurred See the log file null』の場合、最終的に再インストールで対応
 2. Javaのバージョンを８に設定する場合、iniファイルにJVMの絶対パス指定で対応

##  『An error has occurred See the log file null』の場合、最終的に再インストールで対応
ホームディレクトリの名前が、気になる方いませんか？、Mac初期設定時にユーザ名を漢字で入力すると、ホームディレクトリ名は入力時のローマ字入力で登録されるため、ターミナルで作業しているとかなり気になります。

ホームディレクトリの変更をインターネットで検索すると、ユーザ名を作成し直す方法が紹介されています。この方法で変更すると、AnypointStudio内部のパスが正しく読まれないためか、起動時にエラーが発生します。

海外でも数件同様のエラーメッセージで投稿がありましたが、エラーログの出力先を見つけることができず。原因の分析ができない状態だったので、いろいろ考えましたが、再インストールする方法で対応しました。

再インストール後に、実行すると初回起動だけパスの検索？検証？処理というメッセージでプログレスバーが表示されます。初期設定ファイルの変更が必要なのでしょうか？

## Javaのバージョンを８に設定する場合、iniファイルにJDKの絶対パス指定する事で対応
AnypointStudioを実行するには、Javaのバージョンを８に設定する必要があり、ホームディレクトリが変更されたことで環境変数が初期の状態に戻り、もう一度設定する必要があります。
設定せずに起動すると、Workspace選択ランチャーまで起動しますが、その後に下記のエラーが発生します。

<img width="439" alt="スクリーンショット 2019-11-07 21.25.16.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/76270/5670015f-37d2-87b4-1042-addbf90fbeb8.png">

Workspaceの[.metadata]フォルダに起動時のログが格納されており、エラー内容を確認することができます。
エラーのログを見たところ、エラーの原因はJavaのバージョンとわかり。
まずは、ユーザ毎の環境変数定義ファイルにJDKのパスを設定する方法を対応しました。

下記のように、ユーザ毎の環境変数定義ファイルを作成し、設定を反映します。
[ORACLEのパス設定資料](https://docs.oracle.com/javase/tutorial/essential/environment/paths.html)

 - 下記のホームディレクトリ直下に環境変数設定ファイルを作成します。

```
touch ~/.bash_profile
vi ~/.bash_profile
```
 - ファイルにJDKのパスを環境変数に設定します。

```
export JAVA_HOME=`/System/Library/Frameworks/JavaVM.framework/Versions/A/Commands/java_home -v "1.8.0_201"`
export PATH=$JAVA_HOME/bin:$PATH
```
 - 設定したファイルを反映します。

```
source ~/.bash_profile
```

ですが、私の知識不足かログイン時にうまくJavaバージョンが設定できない事象が発生し、最終的にAnypointStudioの初期設定ファイル[AnypointStudio.ini]に実行環境時のVMを指定して起動することができました。

iniファイルへの設定方法は、下記の通りです。

 1. 設定方法は、[アプリケーション]フォルダで[AnypointStudio]ファイルをCntrl+クリックし、メニューの[パッケージ内容を表示]を選択します。
<img width="476" alt="スクリーンショット 2019-11-07 21.52.42.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/76270/8a805fe1-d40a-69c4-acaf-9e34e1cf3bb1.png">

 2. 初期設定情報が記載されている[AnypointStudio.ini]ファイルが格納されているフォルダを辿ります。
<img width="642" alt="スクリーンショット 2019-11-07 22.20.12.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/76270/cbb21bc2-17c1-81d3-d654-b10e7652a369.png">

 3. [AnypointStudio.ini]をダブルクリックすると、エディタで内容を編集することができます。
 -vmから-vmargsまでのVMの絶対パスをしてすることで、エラーが解決できます。
<img width="752" alt="スクリーンショット 2019-11-07 22.05.24.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/76270/f282a018-683c-0f25-8d4a-d8f8511bb737.png">

```
-vm
/Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/home/bin
```

# 最後に
Macの知識があれば、時間をかけずに『スッと』解決できるところですが、知識が不足していることで内部の動きがイメージできず解決に手間取りました。
今回は、Windowと同じ対応方法で解決できたことで、アプリケーションが更に手に馴染んだ気がしました。
では、みなさんもMuleSoftを楽しんでください。では！！
Rubyでトランプの山札からカードを引くアプリを作った(1)

トランプを使ったゲームがたくさんあるのでRubyで書けるよなと思ってます。
よくあるのはブラックジャックとかポーカーとかですかね？

そのベースを作ってみました。

##仕様要件
・ジョーカーを除いて、52枚のカードが重複しないようにする。
・一枚ずつ山札からカードを引く。
・手札の上限枚数までカードを引けるようにする。
・手札を捨てることができる。
・山札を使い切る、もしくはカードを引くことやめると終了する。

##実際に書いてみた
```ruby:playing_card.rb
# カード判定メソッド
def card_judge(i)
  # 記号判定
  if i % 13 == 0
    mark_no = (i / 13) - 1
  else
    mark_no = i / 13
  end
  # マーク番号：0スペード/1ハート/2ダイア/3クラブ
  case mark_no
  when 0
    card_mark = "♠️" #SPADE
  when 1
    card_mark = "♥️" #HEART
  when 2
    card_mark = "♦️" #DIAMOND
  when 3
    card_mark = "♣️" #CRAB
  else
    puts "ERROR"
    exit
  end

  # 数字判定
  card_num = i - ( 13 * mark_no )

  # 表示判定
  disp_list = ["A","2","3","4","5","6","7","8","9","10","J","Q","K"]
  disp_num = disp_list[(card_num - 1)]

  # 戻り値：カードの数字/カードのマーク/表示される文字
  return card_num, card_mark, disp_num
end

# デッキをシャッフルする
puts "DECKをシャッフルします"
deck = [*(1..52)]
deck.shuffle!
puts ""

# 手札の設定
hand = []
hand_limit = 7

# ドローのループ
loop_flg = 0
while loop_flg == 0 do
  # カードをドローする
  puts "DECKからカードをドローします"
  draw_card = deck.shift(1)
  card = card_judge(draw_card[0])

  puts "#{card[1]} #{card[2]}"
  puts "DECKの残り枚数は、#{deck.length}枚"
  puts ""

   # 手札の表示させる
  hand.push(draw_card[0])
  puts "手札一覧（#{hand.length}枚）："
  i = 0
  while i < hand.length do
    puts "#{card_judge(hand[i])[1]} #{card_judge(hand[i])[2]}"
    i += 1
  end

  if deck.length == 0
    # デッキを使い切ったときの挙動
    puts "DECKを使い切りました"
    loop_flg = 1
  elsif hand.length >= hand_limit
    # 手札上限チェック
    puts "上限を超えているのでドローできません"
    puts "手札をすべて捨てますか？[Y/N]"
    response = gets
    case response
    when /^[yY]/
      hand = []
    when /^[nN]/
      loop_flg = 1
    end
  else
    # さらにドローする
    puts "ドローしますか？[Y/N]"
    response = gets
    case response
    when /^[yY]/
      puts ""
    when /^[nN]/
      loop_flg = 1
    end
  end
end

exit
```
##ソースコードで使用したもの
後日まとめます

・商と余剰（トランプを判断するロジック）
・配列、何番目の要素を取得、要素数の取得、要素の追加、シャッフル
・while文
・shiftメソッド
・文字入力
Node.js (2)データ処理とモジュール化


<p>node.jsのデータ
処理はchrome中にconsoleの機能似てる。</p>
<h1>データ処理</h1>
<p>先ずはconsole.logに文字列入れる。</p>

```javascript:app.js
console.log('Hello World');
```
<p>以下はnodeの実行結果。</p>

```terminal:terminal
$ node app.js
Hello World
```
つぎは簡単な変数計算を試しよう。

```app.js
var a = 100;
var b = 200;
var c = a+b;
console.log(c);
```

```terminal:terminal
$ node app.js
300
```

<h1>モジュール化</h1>

<p>複数ファイル間のデータ輸出と輸入につて一般的な方法は三つがある。先ずは二つファイルを作る、app.jsの方はデータ受ける用、data.jsはデータ輸出用。app.js中身のrequire関数は輸出ファイルのバス引数にようるとデータを受け取れる、data.jsはexports函数形、exports変数形とmodule形をまとめる。</p>

```app.js
var content = require('./data');
console.log(content.apple());
```

```data.js
//exports函数形
exports.apple = function(){
    return 'apple!!';
}
```



```terminal:terminal
$ node app.js
apple!!
```
次はexports変数形、変数見たいdataで保存する輸出形だ。

```app.js
var content = require('./data');
console.log(content.data);
```

```data.js
//exports変数形
exports.data = 100;

```

```terminal:terminal
$ node app.js
100
```
最後はmodule形を試しってmodule.exports中身に保存するデータ輸出形だ。

```app.js
var content = require('./data');
console.log(content);
```

```data.js
//module形
module.exports ={
    price:3000
}

```

```terminal:terminal
$ node app.js
3000
```
<h2>注意点</h2>
同時に使えない！！
```app.js
var content = require('./data');

console.log(content);
console.log(content.data);
console.log(content.apple());
```

```data.js
//module形
exports.data = 100;

exports.apple = function(){
    return 'apple!!';
}
module.exports ={
    price:3000
}

```

```terminal:terminal
$ node app.js
{ price: 3000 }
undefined
console.log(content.apple());
                    ^

TypeError: content.apple is not a function
    at Object.<anonymous> 
    at Module._compile (internal/modules/cjs/loader.js:688:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:699:10)
    at Module.load (internal/modules/cjs/loader.js:598:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:537:12)
    at Function.Module._load (internal/modules/cjs/loader.js:529:3)
    at Function.Module.runMain (internal/modules/cjs/loader.js:741:12)
    at startup (internal/bootstrap/node.js:285:19)
    at bootstrapNodeJSCore (internal/bootstrap/node.js:739:3)
```
Macのターミナルコマンド（初歩）

Macのターミナルでの基本操作コマンドを記録します。
#pwd
```sh
pwd #自分の現在位置を表示
```
自分の現在位置は「カレントディレクトリ」とよぶ
pwdは居場所確認なので頻繁に使う
#cd
```sh
cd /home/desktop/  # デスクトップに移動
cd ..  #1つ上のディレクトリに移動
cd ~/  #ホームディレクトリに移動
```
絶対パスと相対パスがある
.はカレントディレクトリを表し、..は一つ上のディレクトリを表す
※入力途中でtabを1回入力すれば残りのコマンドやディレクトリ名が入力補完される
#ls
```sh
ls  #ディレクトリの内容を表示
ls -l  #ファイルの詳細情報を含めて表示
ls -a  #隠しファイルも含めてすべて表示
ls −la  #隠しファイルを含めてファイルの詳細情報をすべて表示
ls -1　#リストを縦に並べる
ls -lr  #逆順に表示
ls -tl #更新時間順に表示
```
listの略
「.」を先頭につけたファイルは隠しファイルとなる
隠しファイルは名前の通り、通常表示されない

#sudo
```sh
sudo 実行したいコマンド#管理者権限を付与
````
「Operation not permitted」など管理者権限がないエラーの場合に
sudoを先頭に付けてコマンドを実行する

#mkdir,cp,mv
```sh
mkdir ~/Desktop/work #デスクトップにworkディレクトリを作成
cp test1.txt ~/Desktop/work/test2.txt #test1.txtをコピーしtest2.txtを作成
mv ~/Desktop/work/test2.txt ~/Desktop #デスクトップに移動
mv test2.txt test3.txt　#リネーム
```
#touch,rm
```sh
touch test.txt #ファイルを作成する
rm test.txt #ファイルを削除する
```

#vim
```sh
vim test.txt #ファイルを編集する
```
編集画面に入るので、以下キーで切り替えをする。
INSERTモード⇀「aキー」or「iキー」を押す
NSERTモードを抜ける⇀「Escキー」を押す
保存⇀「:w」
保存して終了⇀「：wq」
保存しないで終了⇀「：q」


基本はここまで。
数回クリックするだけでどこでもUSBドングルを簡単に共有

![dfOverviewDownloadImage.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/447311/c6fe999f-7619-a0bd-40e6-34d6c8544e9f.png)

[Donglify](https://www.donglify.net/)は、ネットワーク接続を介したUSBセキュリティドングルの共有の問題に対するソフトウェアソリューションです。 これは、使用するためにUSBドングルが物理的に存在する必要性を排除するプロ級のアプリケーションです。 このプログラムは、インターネット上のUSBセキュリティキーのリダイレクトを可能にします。 Donglifyを使用すると、USBドングルのコピーを作成し、任意の場所からリモートで管理できます。
USBスイッチ：USBスイッチとは、USBスイッチの説明| USBスイッチインターフェース

*USBスイッチとは何ですか、どこで使用できますか。複数のコンピューター間でUSBデバイスを共有するためのUSBハードウェアに関する完全なガイド。*

#USBスイッチとは

USBスイッチは、USBデバイスをネットワーク接続せずにコンピューター間で共有するためのUSBハードウェアです。 USBスイッチを使用すると、デバイスを1台のコンピューターに接続することなくデバイスを共有してから、すべてのデバイスで使用するためにデバイスを取り外して別のコンピューターに接続できます。ボタンを押すだけで、接続されているすべてのコンピューターでUSBスイッチをすぐに使用できます。
![Group 1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/447311/63038a4c-7961-11f2-f3ea-3850055308b2.jpeg)


##USBスイッチの種類：

多種多様なUSBスイッチがあります。一部のスイッチは、1台のUSBデバイスを共有するために2台のコンピューター用に開発されています。このようなスイッチは、プリンターまたはスキャナーを共有するホームユーザーを対象としています。一部のスイッチは、最大4台のコンピューターをサポートでき、1つのUSBデバイス用の出力ポートを備えています。また、一部のスイッチでは、4台のコンピューター間で4つのUSBデバイスを共有できます。

##USBハブまたはUSBスイッチ？

USBハブは、USBスイッチの正反対です。ハブのおかげで、1台のコンピューターは、接続されていなくても多くのデバイスにアクセスできます。これは、遅延や中断なしに複数のデバイスで作業する必要がある場合に非常に便利です。

##知ってますか？
[USB Network Gate](https://www.usb-over-ethernet.org/)ソフトウェアは、追加のハードウェアなしで多くのコンピューター間でUSBデバイスを共有するのに役立ちます。
DonkeyCar ジョイスティックのステアリングをエクスポネンシャル化

##ニュートラル付近のスティック操作はステアリングの変化量を小さくしたい
#~/projects/donkeycar/donkeycar/parts/controller.py

JoystickController クラス

＿init__(self,...の引数リストに

      exponent = 1,

を追加
＿init__(self,...の本体に

      self.exponent = exponent

を追加
def set_steering(self, axis_val): に

        if self.exponent > 1:
            if axis_val < 0:
                axis_val = (pow(self.exponent,abs(axis_val))-1) / (pow(self.exponent,1)-1) * -1
            else:
                axis_val = (pow(self.exponent,axis_val)-1) / (pow(self.exponent,1)-1)

を追加
def get_js_controller(cfg):の
    ctr = cont_classの引数リストに

                                exponent=cfg.EXPONENT,

を追加

#~/mycar/myconfig.py

EXPONENT = 100

を追加

EXPONENT の値が1以下の時はエキスポネンシャル無し
y = x

設定する数値が大きいほどスティックの角度に対するステアリング値の0付近のカーブが緩やかになる
y = EXPONENT の x乗

![無題.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/263672/2597381f-f59b-bfd5-e9ad-909be1e653d8.jpeg)
系列１：EXPONENT=1.1
系列２：EXPONENT=10
系列３：EXPONENT=50
系列４：EXPONENT=100
系列５：EXPONENT=500
系列６：EXPONENT=1000
Vimコマンドの備忘録

# 環境
Ubuntu

# 最初に
知ってたけど深く触ってないシリーズ。
今の環境で頻繁に使うことになりそうなので備忘録として残します。
自分がよく使っているコマンドを載せています。
使うコマンドが増えてきたら更新しようと思います。

# Vimをインストールする
Ubuntuになりますが、

```
$ sudo apt-get install vim
```

####バージョン確認

```
dpkg -l vim
```

# インサートモード 開始位置
|     コマンド      | 詳細              |
|:----------------:|:-----------------|
|        a         |カーソルの右側から開始                   |
| i           |カーソルの位置から開始              | 
| A             |行末に移動し開始               | 
| l               |行頭に移動し開始                 |  
| o             |現在の行の下に改行し開始 |   
| cc          |１行を削除して開始            |  

# ビジュアルモード
##### ※編集中のテキストの範囲選択を視覚的に行うモード
|     コマンド      | 詳細              |
|:----------------:|:-----------------|
|        v         |ビジュアルモード開始                   |
| Ctrl + v           |矩形選択のビジュアルモード開始              | 
| V             |カーソルのある行からビジュアルモード               | 

# その他の挙動
|     コマンド      | 詳細              |
|:----------------:|:-----------------|
|       ESC          |コマンドモードに戻る                   |
| Ctrl + v           |コマンドモードに戻る              |

# 移動する
|     コマンド      | 詳細              |
|:----------------:|:-----------------|
|       j          |下に移動                   |
|       k           |上に移動              |
|       h          |左に移動                   |
|       l           |右に移動              |
|       w, W          |次の単語の先頭に前進                   |
|       H          |ウィンドウ内の一番上の行に移動              |
|       M          |ウィンドウ内の真ん中の行に移動              |
|       L          |ウィンドウ内の一番下の行に移動               |
|       gg          |ページの先頭に移動              |
|       G          |ページの最後に移動               |

# 削除する
|     コマンド      | 詳細              |
|:----------------:|:-----------------|
|       dd          |カーソルのある行を削除                   |
|       diw           |カーソル上の単語を削除              |
|       di"          |ダブルクォーテーションの内部の文字を削除                   |
|       dit           |HTMLタグ内部を削除              |
|       x         | 1文字削除                   |
|       D          |カーソル位置から行末まで削除              |

# コピペ、戻る進む
|     コマンド      | 詳細              |
|:----------------:|:-----------------|
|       yy          |今いる行をコピー                   |
|       p           |カーソル下に行をペースト              |
|       u          |戻る(Ctrl + zと一緒)                   |
|       p           |進(Ctrl + yと一緒)              |

# 検索
|     コマンド      | 詳細              |
|:----------------:|:-----------------|
|       *          |カーソル下の単語を検索                  |
|       #           |カーソル下の単語を検索(上方向)              |
|       /"文字"           |"文字"を検索する              |

# Vim終了
|     コマンド      | 詳細              |
|:----------------:|:-----------------|
|       sq          |閉じる                  |
|       :q           |閉じる              |
|       :w           |保存              |
|       :q!           |強制終了              |
【Python】boto3でS3ファイル操作まとめ

## ディレクトリ構成

s3で以下のようにファイルが用意されている前提。

```
line/
└── diagonal/
   └── hoge.csv
```



## 同バケット内でファイルをフォルダ間でコピー

`line/diagonal/hoge.csv`を新たに`line`フォルダ下に`straight`フォルダを作成しそこにコピー

```python
import os
import boto3

BUCKET_NAME = 'your_bucket'  # バケット名
COPY_FROM = 'line/diagonal'  # コピー元ディレクトリパス
COPY_TO = 'line/straight'  # コピー先ディレクトリパス]
FILE_NAME = 'hoge.csv'  # ファイル名

s3 = boto3.client('s3')

copy_from_path = os.path.join(COPY_FROM, FILE_NAME)
copy_to_path = os.path.join(COPY_TO, FILE_NAME)

s3.copy_object(Bucket=BUCKET_NAME, Key=copy_to_path, CopySource={'Bucket': BUCKET_NAME, 'Key': COPY_FROM_PATH})
```

実行すると`line`フォルダ下に`straight`フォルダが作成されそこに`hoge.csv`がコピーされる。

```
line/
├── diagonal/
│   └── hoge.csv
└── straight/
	└── hoge.csv
```



## ディレクトリからファイルを削除

`diagonal`フォルダから`hoge.csv`を削除する。

```python
import os
import boto3

BUCKET_NAME = 'your_bucket'  # バケット名
DELETE_DIR_PATH = 'line/diagonal'  # コピー元ディレクトリパス
FILE_NAME = 'hoge.csv'  # ファイル名

s3 = boto3.client('s3')

delete_file_path = os.path.join(DELETE_DIR_PATH, FILE_NAME)

s3.delete_object(Bucket=BUCKET_NAME, Key=delete_file_path)
```

実行すると`diagonal`フォルダ下の`hoge.csv`がcsvが削除される。

```
line/
├── diagonal/
│   
└── straight/
	└── hoge.csv
```



## S3からローカルにダウンロード

```python
import os
import boto3

BUCKET_NAME = 'your_bucket'  # バケット名
S3_PATH = 'line/straight'  
LOCAL_PATH = 'hogehoge'
FILE_NAME = 'hoge.csv'

s3_resource = boto3.resource('s3')
bucket = s3_resource.Bucket(BUCKET_NAME)

downlod_from_path = os.path.join(S3_PATH, FILE_NAME)
download_to_path = os.path.join(LOCAL_PATH, FILE_NAME)

bucket.download_file(downlod_from_path, download_to_path)
```



## PandasデータフレームをcsvとしてS3に保存

```python
import os
import pandas as pd
from io import StringIO
import boto3

S3_PATH = 'line/diagonal'
FILE_NAME = 'diagonal.csv'

df = pd.DataFrame([[1, 10], [2, 20], [3, 30]])

upload_path = os.path.join(S3_PATH, FILE_NAME)

csv_buffer = StringIO()
df.to_csv(csv_buffer)
s3_resource = boto3.resource('s3')
s3_resource.Object(S3_BUCKET, upload_path.put(Body=csv_buffer.getvalue())
```

実行すると`diagonal`フォルダ下に`diagonl.csv`が作成される

```
line/
├── diagonal/
│   └── diagonal.csv
└── straight/
	└── hoge.csv
```
ローカル変数とインスタンス変数の違い

###ローカル変数とインスタンス変数の違い
`ローカル変数`
その場限りの一時的な変数。
メソッド内で定義したローカル変数は**そのメソッドの中でしか使えない。**

`インスタンス変数`
オブジェクトの保持する変数。
オブジェクトの**どのメソッド内でも使用できる**
##ローカル変数numberを使ってメソッドを呼び出す

```ruby:myclass.rb
class Myclass
 def method_1
  number = 100
 end

 def method_2
  number
 end
end
```

```irb
>object = Myclass.new
>object.method_1
=> 100

>object.method_2
Traceback(most recente call last):
NameError (undifined...)
```
method_2ではエラーが返ってきています。
つまりmethod_2ではmethod_1で使ったnumberは使えない状態だということです。

##インスタンス変数@numberを使う

```ruby:myclass.rb
class Myclass
 def method_1
  @number = 100
 end

 def method_2
  @number
 end
end
```
```irb
>object = Myclass.new
>object.method_1
=> 100

>object.method_2
=> 100
```

無事２つとも１００という値が返っています。
つまりmethod_1とmethod_2では同じ@numberが使えているということです。

##まとめ
ローカル変数は１つのメソッドの中で一時的に使うデータを参照するために使用する。
一方で、インスタンス変数は特定のオブジェクト内で使いまわしたり、オブジェクトに属するデータとして外部からゲッターを利用するために使用する。
PHPでログインパスワードとデータベースに保存している暗号化したパスワードを照合する

こんにちはジャムです。

今回はPHPでログインフォームから、パスワードを入力した際にデータベースに保存しているパスワードと照合する場合の方法を解説します。

##ソースコード

```php5
<?php
//フォーム等に入力したパスワード
$password =  "1234567890";
//パスワードを暗号化
$hash = password_hash($password,PASSWORD_BCRYPT);
//暗号化したパスワードを出力確認（毎回出力が変わるので確認してください）
var_dump($hash);

//password_verify関数を使用して照合する。
if(password_verify($password,$hash)){
  echo "一致しています";
}
else {
  echo "一致していません";
}

?>

```

##使用方法
####1.暗号化された$hashの値をデータベース等に保存しておく。
####2.password_verify関数で入力したパスワードとデータベースに保存したパスワードを照合する。
####3.終わりです。



参考記事は[コチラ](https://qiita.com/MasaKuuuu/items/51552aa0331f3ae90dae)
[Ruby] paiza初級　一行で複数の値入力をする

#目的 ： 一行で複数の値入力をする

```入力例.
1 3 5 7
```
というように，一行で複数の値入力のケースについて解説します．

#使用メソッド
###<font color="blue">gets</font>   :値入力をする
###<font color="blue">split</font>  :引数で区切った文字列を配列に分割
###<font color="blue">map</font>    :配列の各要素を変換

#方法

```
nums = gets.split.map(&:to_i)
p nums  #→[1, 3, 5, 7]
```

###[解説]
１．getsメソッドで値入力を受け取り
２．splitメソッドで文字列として配列に格納
３．mapメソッドを用いて各要素を整数型に変換

#まとめ
paizaのようなプログラミング学習サイトでコードを書くにあたり，受け取った値を用いて後の処理はわかるが，適切な値入力が分からない場合があると思うので参考になればと思います．

##参考文献
[プロを目指すための人のRuby入門](https://ruby-book.jnito.com/)
【PHP】Laravel 学習メモ / 簡易アプリで流れを掴む２（受け取ったデータを計算して完成まで）

##前回まで
前回までで画面を表示させるところまで完成しました。
[【PHP】Laravel学習メモ / 簡易アプリで流れを掴む１（表示まで）](https://qiita.com/kota_sho/items/06c09033bddf682d08a0)

##今回で完成
今回は、入力されたデータ（米ドル）を受け取り、日本円に換算して表示するところまで実装して、完成としたいと思います。為替は、今回１ドル＝110円として計算します。

##完成動画
![66f8e35a38f4ce0d1865297836b5011e.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496614/b186e56b-d74c-1f95-c583-5039b2fbe980.gif)


コードを追ってみましょう。
##ルーティング
ルーティングは前回で終わっているので、そのまま使います。
本来Webサービス、アプリではもっと多くのルーティングを設定しますが、今回のアプリはトップページのみで動かす為、１行のみです。

routes/web.php

```php:routes/web.php
Route::get('/', function () {
    return view('welcome');
});

Route::get('/calculate', 'CalculateController@getcalc');
```

##コントローラー、ビュー

###コントローラー
app/Http/Controllers/CalculateController.php

```php:app/Http/Controllers/CalculateController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class CalculateController extends Controller
{
  public function getCalc(Request $request){

    $data['exchange'] = $request->input('usdoller');
    $data['jpyen'] = ($data['exchange'] * 110);

    return view('calculate.num', $data);
    //
  }
}
```

###ビュー
resources / views /calculate / num.blade.php

```html:resources/views/calculate/num.blade.php

<head>
    <title>計算アプリ</title>
</head>
<body>
    <h1>計算アプリです</h1>
    <h2>米ドルを入力すれば日本円に換算します</h2>
    <div>
        <form>
            <input type="text" name="usdoller" placeholder="$0" style="height: 30px; width: 120px;">
            <button type="submit">円に換算</button>
        </form>
        <p>日本円で{{ number_format($jpyen)}}円です</p>
    </div>
</body>
</html>
```

#####では、１つずつ見ていきます。

###コントローラー
まず、コントローラーを順に見ていくと、viewから'usdoller'というデータを受け取っています。後ほどビューで見ますが、この中にはユーザーが入力した米ドルの金額が入っています。
その受け取ったデータを$data['exchange']に代入しています。

app / Http / Controllers / CalculateController.php

```php:app/Http/Controllers/CalculateController.php

$data['exchange'] = $request->input('usdoller');
```

そして、'jpyen'にはusdollerに110（為替レート)を掛けたものを代入しています。


```php

$data['jpyen'] = ($data['exchange'] * 110);
```

最後に、ビューに$dataを一緒に渡します。

```php

return view('calculate.num', $data);
```

２つのデータを受け取っていますので、$data[　]の中身は２つ入っています。

```php
$data['exchange', 'jpyen']
```
ビューで表示したいのは、計算結果が入っている'jpyen'です。
'jpyen'を表示させるようにしましょう。

では、ビューを見てみます。
###ビュー
まず、入力欄とボタンを実装しています。ここは特に変わったことはしていません。ここで入力された金額が、コントローラーの$request->input()に渡されます。

resources / views /calculate / num.blade.php


```html:resources/views/calculate/num.blade.php


<form>
      <input type="text" name="usdoller" placeholder="$0" style="height: 30px; width: 120px;">
            <button type="submit">円に換算</button>
</form>
```

コントローラーから渡されたデータを表示します。bladeファイルでは、{{ $データ }}で受け取ったデータを表示させることができます。number_formatは、数字の位をカンマで分けてくれる関数です。

```html
<p>日本円で{{ number_format($jpyen)}}円です</p>
```
以上で完成です。
![66f8e35a38f4ce0d1865297836b5011e.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496614/b186e56b-d74c-1f95-c583-5039b2fbe980.gif)

##データの流れまとめ
・ビューのinputで入力されたデータはsubmitでコントローラーへ渡す。

・コントローラーでは$request->input()でデータを受け取る。

・受け取ったデータを、$data[]に代入する。

・為替の計算をし、return view(ビューのファイル, $data);でビューに渡す。

・ビューでは{{ $データ }}でデータを表示する。
Kubernetes Meetup Tokyo #24 Kubernetes上で動かすアプリケーション開発のデバッグとテスト 参加メモ

## はじめに

[Kubernetes Meetup Tokyo #24](https://k8sjp.connpass.com/event/149872/)に参加してきました。
今回のテーマは「Kubernetes上で動かすアプリケーション開発のデバッグとテスト」です

## セッション

### 1. minne の開発環境の変革とこれから

登壇者：後藤 利博 ([@_shiro16](https://twitter.com/_shiro16)), GMOペパボ株式会社

- 開発環境の数が足りない問題をkuberntesによって解決した。
- Telepresenceを使って開発するサービスだけをDocker上で動かすことが出来るようになった。

### 2. Debug application inside Kubernetes using Linux Kernel tools

資料：https://speakerdeck.com/kentatada/debug-application-inside-kubernetes-using-linux-kernel-tools
登壇者：KENTA TADA ([@KentaTada](https://github.com/KentaTada)), ソニー株式会社

oci-ftrace-syscall-analyzerというftraceベースのシステムコールアナライザをkubernetes上で動かす話。

#### どうやってコンテナにアタッチするか

- prestart時にコンテナで動かす予定のPIDをftraceに設定する
- stdinでjson形式でコンテナの状態が書いてあり、そこにPIDが書いてある
- ただし、実際のexecまでのログまで残ってしまう
- コンテナ起動後にkubectl execで実行したプロセスのシステムコールは取れない

#### Kuberentesで使う方法
CRI-Oのprestart時にruncのprestartをフックする

#### eBPFベースのoci-seccomp-bpf-hookと比べると

- 特権が不要
- eBPFの設定がカーネルのバージョンに依存するので、その心配がない

### 3. Kubernetes 1.16 アルファ機能を先取り！エフェメラルコンテナ

資料：https://speakerdeck.com/superbrothers/kubernetes-1-dot-16-ephemeral-containers-alpha-bf6ff69c-410b-4f89-a7e9-e856db91fb7f
登壇者：Kazuki Suda ([@superbrothers](https://twitter.com/superbrothers)), ゼットラボ株式会社

#### 現状のkubectl execでの課題

- コンテナ内に存在するコマンドしか実行できない
- 軽量イメージにはツールが少ない
- 最低限のツールを含めたコンテナをサイドカーにする？
  - すべてのpodに入れるのはリソースの無駄

#### エフェメラルコンテナ

- 実行中のpodに一時的に追加出来るコンテナ
- トラブルシュートやデバッグ時に任意のツールを使うことができる
- 通常のpod操作と権限を分けることが出来る
- あくまでも一時的なコンテナなので機能が制限されている
- まだアルファで、本番で使用できるようになるにはまだまだ掛かりそう
picoCTF 2019 WhitePages - Points: 250

#問題

https://2019game.picoctf.com/problems

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17681/b2d12fc6-8096-c73d-d3e1-cda2e8602c85.png)

#解いてみた

添付ファイルを開いてみる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17681/87742f88-6264-1d0a-c0ad-7eaa2cd04ad5.png)

スペースだらけ。

半角スペースで検索してみるといろいろなスペースが使われていることがわかる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17681/100340a6-1587-39e6-9ecd-cadb5c13943c.png)

スペースだけのプログラミング言語があったよなと思って調べてみる。

Whitespace - Wikipedia
https://ja.wikipedia.org/wiki/Whitespace

これな気がします。
そして確かweb上でコンパイルと実行ができたと思うので探してみます。

Ideone.com
https://ideone.com/

ここで実行してみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17681/9bf4a470-3b8a-eb8b-cc20-ce25938a5d99.png)

違ったみたいです。
半角スペース以外に使用されているスペースを調べます。
これが全角スペースです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17681/8f7585ce-eaad-b673-35b9-9836ed21e599.png)

半角と全角スペースで全部でした。
2種類なので0と1とかでしょうか。
半角0、全角1で置換してみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17681/a5de4a1b-362a-cc8f-4188-6e5d343bab3f.png)

CyberChefで変換してみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17681/c17e7e08-4d4d-150b-56f6-f7b9bb49684e.png)

間違いのようです。
半角1、全角0で置換してみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17681/0179ba44-80fa-0173-cf9f-d0c83af1bcad.png)

これを文字列に変換。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17681/7abbdd06-2301-6e8b-1e0e-b47294fad1b2.png)

正解でした。
サイエンス社の Information & Computing シリーズから Pascal 関連の書籍を読んでみる <番外編>

# はじめに
サイエンス社の Information & Computing シリーズで出ていた Pascal 関連書籍の書評を書きました。

 - [サイエンス社の Information & Computing シリーズから Pascal 関連の書籍を読んでみる (Qiita)](https://qiita.com/ht_deko/items/93899a6fe0c2f46cc945)
 
# NS ライブラリシリーズ
サイエンス社には **NS ライブラリシリーズ** というのがあり、ここに一冊だけ Pascal 関連の書籍があります。折角なのでこれも読んでみようという趣旨の記事です。

## #3 ザ・PASCAL
Turbo Pascal を対象とした書籍です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/18f499c1-7d1d-9071-105a-b2d78d513166.png)

| No | タイトル | 著者 | ISBN-10<br>(Amazon) |出版年|
|:-:|:--|:--|:-:|:-:|
| 3 | [ザ・PASCAL](https://www.saiensu.co.jp/search/?isbn=978-4-7819-0475-7&y=1987) | 戸川 隼人 | [4781904750](https://www.amazon.co.jp/dp/4781904750)  | 1987/4/10 |

### 書評 
評価に困る本。

まず『ザ・PASCAL』と言っておきながら処理系が記載されていない。実行例に `Running` とあり、ソースコードに半角カナが使われており、印刷したものを貼り付けたであろうリストのフォントが PC-PR 系っぽいので**「PC-9801 シリーズ用の Turbo Pascal 3.0 ではないか？」**と推測しながら読み進めていくと、int() / frac() とかが出てくる。しかし `turbo PASCAL 等で使用可能。`[^1] との注釈付きなので決定的な証拠にはならずモヤっとする。さらに読み進めると **5.2 文字列型** の所で、

 **var** 変数名 : **String** [最大文字数] 

と書いてある。やはりそうだった。そして隣のページには `「演算記号 + により二つの文字列を連結してみましょう」` とあるうえ、ここには「Turbo Pascal ～」 の注釈がない...えっとだ。

**6.2 ポインタ型** の所で、New() の説明があって Dispose() の説明がない事から、筆者は `標準 Pascal -> UCSD Pascal -> Turbo Pascal` と使ってきて**記憶がごっちゃになっている**ものと思われる。

 - UCSD Pascal にも Turbo Pascal にも文字列型 **String** が存在する。
 - 古い標準 Pascal と UCSD Pascal には Dispose() がない。

推測だけれど、時期的にみて標準 Pascal を試せる環境がもう手元になかったのではないだろうか？

しかしながら、だ。そんな話よりも **"そもそもの話"** をする必要があるように思う。それはこの NS ライブラリシリーズの **NS** が何を意味しているかなのだが...カバーの袖 (そで) の文を引用してみよう。

> ■計算機科学の急速な細分化・専門家の中で、その精髄をできるだけはやく、的確に把握し身につけたいとする人たちの真摯な要請に応えるべく、各分野の
>
>  **必要にして十分** (**N**ecessary & **S**ufficient)
>
> な内容を厳選し、わかり易く解説したのが、本ライブラリであります。

本書はそのシリーズのうちの一冊なのだ。実際、本書は読みやすいし、順に読み進んでいけばそれなりに Turbo Pascal でプログラムが書けるようになるだろう。NS ライブラリシリーズのコンセプト通りの本だと思う。即席プログラマを量産するにはこれで**必要にして十分**なのだ。

ソースコードは **Borland スタイル** で書かれている [^2]。

```pascal
program Test(Output);
  var
    i: Integer;
  function sub: Boolean;
  begin
    sub := (i = 0);
  end;
begin
  i := 0;
  if sub then
  begin
    writeln('True!');
  end;
end.
```
個人的な評価: ★★★☆☆

# おわりに
評価が低いのは**「Pascal の本としてどうか？」**と考えたからです。考え方によっては良書でもあるのですが。いずれにせよ、最低でも動作環境と登録商標、参考文献くらいは記すべきだと思います。

そういえば[本書の紹介ページ](https://www.saiensu.co.jp/search/?isbn=978-4-7819-0475-7&y=1987)には次のように書かれています...いやこれ、殆ど Turbo Pascal 用でしょう。

> これまでに出版された PASCAL の本の中でおそらく最もやさしいテキスト．なおかつプログラミング言語教育にとって最近とみにその重要性が指摘される斯言語の要所をあます所なく解説する．例題は有限要素法まで扱う．TURBO PASCAL 使用者にも適応．

ちなみに [NS ライブラリシリーズの殆どを戸川氏が手掛けられています](https://www.saiensu.co.jp/search/?book_class_id=2&library_id=5)。これは凄い！

**See also:**

 - [Pascal / Delphi 関連の書籍を読んでみる (まとめ) (Qiita)](https://qiita.com/ht_deko/items/34bbcdea27ce224079e0)


[^1]: 綴りは原文通り。
[^2]: 字下げのスタイルについては[こちらを参照](https://qiita.com/ht_deko/items/50bac0de384d66208944#832--pascal-%E3%81%AE%E5%AD%97%E4%B8%8B%E3%81%92-%E3%82%A4%E3%83%B3%E3%83%87%E3%83%B3%E3%83%88-%E3%82%B9%E3%82%BF%E3%82%A4%E3%83%AB)の事。
ElementUIのLoadingを閉じた後、JavaScriptのアラート開くとLoadingが残ったままになる

# ハマったこと
ElementUIのLoadingを閉じて、JavaScriptのalertを呼ぶと
<b>Loadingが残ったままalertが表示された。</b>
表示されているのはalertだけだと思っていたので、ハマりました。

# 原因
ElementUIのLoadingとJavaScriptのalertの動きが関係していました。

- ElementUIのLoadingを閉じる処理は300ms後に閉じる非同期処理
- JavaScriptのalertは開いてる間スクリプトの実行が停止する

Loadingが閉じるにはタイムラグがあり、その間にalertが起動するとLoadingの非同期処理も止まってしまい、結果としてLoading閉じられずが残ったまま、alertが表示されてしまっていました。。。

コードで表したイメージはこんな感じです。

``` javascript
// ローディング表示するよ
const loading = this.$loading({});

// ローディングを非表示にするよ
// でも300ms後に閉じるよ
loading.close();

// アラートを表示するよ
// アラート表示したら全てのスクリプト処理が止まるよ
alert('アラート消したら、ローディングが消える！');
```

# 対策
- ElementUIのLoadingComponetnを修正する(setTimeoutを0にすればうまくいくはず。。。）
- JavaScriptのalertを非同期にしてLoadingが閉じるのを待って開くようにする

実務では、仕様でJavaScriptのアラートを使うことになり（わけのわからない仕様）、
またElemetUIを修正すると影響範囲が大きいと理由で修正できなっかたので、 
JavaScriptのアラートをLoadingが閉じてから開くような処理で対応しました。
以下、ご参考までに（きっともっといい方法があるはず。。。きっとある）

```javascript
// 指定した時間待って、アラートを表示します
function delayAlert(msg, waitSec) {
  setTimeout(function() {
    alert(msg);
  }, waitSec);
}
// 使い方
delayAlert("時間差でアラート表示するよ", 300);
```

## 最後に
初めて記事を書いてみましたが、記事を書くのって難しいですね！
一つの記事書くのにかなり時間かかっちゃいました:sweat_smile:
記事の書き方や内容などで、ご指摘ありましたらガンガンお願いいたします:bow:
MySQLでよく使うコマンド一覧

備忘録です。

更新日：2019/11/07

##作成（create）

###データベースの作成

```
create database データベース名;
```

###テーブルの作成

```
create table user(id int auto_increment primary key,body varchar(20));
```

##参照

###ユーザーとパスワードの参照

```
select Host, User, Password from mysql.user;
```



###テーブルの中身を確認

```
select * from テーブル名
```

###テーブルの構造を確認

```
mysql> show columns from テーブル名;

出力例）
+-------+-------------+------+-----+---------+----------------+
| Field | Type        | Null | Key | Default | Extra          |
+-------+-------------+------+-----+---------+----------------+
| id    | int(11)     | NO   | PRI | NULL    | auto_increment |
| body  | varchar(20) | YES  |     | NULL    |                |
+-------+-------------+------+-----+---------+----------------+

```

##権限

###テーブルの修正権限の付与

```
GRANT ALL PRIVILEGES ON `DB名`.テーブル TO 'ユーザ名'@'ホスト名';
```

##挿入（INSERT）

###データの挿入

```
insert into テーブル名(カラム名1,カラム名2) values("値1”,"値2");
```


##修正、訂正

##カラム


####途中からカラムを追加する場合


```
alter table テーブル名 add column カラム名 データ型;
```
コーディングの基礎知識（リンク集）

Webサイトのコーディング時によく登場する単語について、わかりやすくまとめられているサイトを集めました。

# 言語説明
**HTMLの基礎知識**
[今さら聞けない！HTMLとは【初心者向け】](https://techacademy.jp/magazine/4843)

**HTMLとCSSの関係性**
[HTMLとCSSの違いとは？初心者でもわかるHTMLとCSSの違いと使い方](https://kukuli-blog.com/difference-between-html-and-css/)

**JSとPHPの違い**
[未経験でもわかる！PHPとJavaScriptの違いとは？](https://www.sejuku.net/blog/4228)

# メタ情報
**SEOとは**
[SEOとは? 定義・目的・方法まで徹底解説!【Web担当者さま必読】](https://wacul-ai.com/blog/seo/seo-basic/what-is-seo/)

**メタ情報とは**
[meta要素とは](https://www.seohacks.net/basic/terms/meta/)

**シェア情報について**
[OGPを設定しよう！SNSでシェアされやすい設定方法とは？](https://digitalidentity.co.jp/blog/seo/ogp-share-setting.html)

# XML・JSON
**XML・JSONとは**
[【超ざっくり解説シリーズ】 データフォーマットとは？XML、JSON、CSVの違いや特徴についてわかりやすく解説するよ](https://note.mu/toyomomo/n/nc4ad516aa32c)

# CMS
**CMSとは**
[CMSとHTMLの違いとは | 最適なWebサイト構築のために知っておきたいこと](https://boxil.jp/mag/a3817/)

# アイコンフォント・SVG
**アイコンフォントとは**
[アイコンデザインをWebフォントで表現出来る「アイコンフォント」の基本知識を解説](https://ferret-plus.com/6248)

**SVGとは**
[WEBデザイナーのはじめての「SVG」](https://webkikaku.co.jp/blog/webdesign/svg_for_webdesigner/)
GitHubにリモートブランチを増やさない方法

##GitHubにリモートブランチを増やさない方法

インターンで企業の方々がGitHubにて作業用ブランチをリモートに
残さないように工夫していたので、その一連の流れをメモしておきます。

まずは作業用branchを作成
branchの作成のコマンド↓

```
git branch [branch_name]
```

branchの移動

```
git checkout [branch_name]
```


作業がひと段落したら以下のコマンドを実行
ワーキングツリーのファイルをインデックスに追加

```
git add [file_name]
```

コミット

```
git commit -a -m '説明'
```


mergeしたいbranchに移動して作業完了したbranchをmerge
ここではmasterを想定

```
git checkout master
git merge --no-ff [作業用ブランチ]
git push origin master
```
これでリモートには反映されずbranchをmergeできます。
localにbranchは残るのでかなりリモートリポジトリがスッキリすると思います。
リモートにbranchが大量にあって困っていたので、非常に助かりました。

***
最後に使えそうなコマンドを羅列しておきます。
自身のメモ用です。

##ローカルリポジトリの新規作成

```
git init
git add *
git commit -m "initial commit"
```

##ファイル更新の基本操作

```
git add [ファイル名] //追加
git commit -a -m "任意のコメント"  //コミット (-aオプションは変更を自動検出してくれる)
git push origin master  //masterを更新
```

##git add のオプション

```
git add . //すべてのファイル・ディレクトリ
git add *.css //すべてのCSSファイル
git add -n //追加されるファイルを調べる
git add -u //変更されたファイルを追加する
git rm --cached //addしてしまったファイルを除外
```

##git commit のオプション

```
git commit -a //変更のあったファイルすべて
git commit --amend //直前のコミットを取り消す
git commit -v //変更点を表示してコミット
```

##commit の取り消し, 修正

```
git reset --soft HEAD~2 // 最新のコミットから2件分をワークディレクトリの内容を保持し取り消す
git reset --hard HEAD~2 // 最新のコミットから2件分のワークディレクトリの内容とコミットを取り消す
git rebase -i HEAD~2 // HEADから2件のコミットメッセージを修正
```

##branch のオプション

```
git branch [branch_name]  //ブランチの作成
git checkout [branch_name]  //ブランチの移動
git branch -d [branch_name]  //ブランチの削除
git branch -m [branch_name]  //現在のブランチ名の変更
git branch // ローカルブランチの一覧
git branch -a //リモートとローカルのブランチの一覧
git branch -r //リモートブランチの一覧
git checkout -b branch_name origin/branch_name //リモートブランチへチェックアウト
```

##mergeの取り消し
```
git merge --abort
```

##差分の確認
```
git diff
git diff HEAD^ //最後のコミットからの差分を表示
git diff --name-only HEAD^ //差分ファイルを表示
git diff file1.txt file2.txt //特定フィイルの差分
git diff commit1 commit2 //コミットの差分
```

##ログの表示
```
git log //コミットのログが見れる
git reflog //いろいろ見れる
git reflog origin/branch_name //pushのログが見れる
```

##作業の退避
```
git stash
git stash pop //戻す場合
git stash list //退避の一覧
git stash clear //退避の消去
```

##タグ
```
git tag // タグの一覧表示
git tag -l 'v1.*' // パターンでタグを検索
git tag -a v0.0.0 -m 'version 0.0.0' // タグの作成
git push origin v0.0.0 // タグの共有
```
***
***

##参考URL
https://qiita.com/konweb/items/621722f67fdd8f86a017