OmniAICreator/Qiita-1.07M huggingface dataset:
Google Apps Scriptの日付操作もろもろ

ある案件で日付の取得、比較を行う必要があり、今まで自分が使用していた開発言語とは使い方が違っていたので苦労したことをまとめておこうと思う。

## さっそくコーディング

``` javascript
// 現在日時を取得
var today = new Date();

// 翌日を取得
var tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1);

// 特定の時間をセット
var 6oclock = newDate();
6oclock.setHours(06);
6oclock.setMinutes(00);
6oclock.setSeconds(00);

// 日付の大小を比較（比較結果の差異を「時間」に換算）
var jadgeRange = (today.getTime() - tomorrow.getTime()) / (1000 * 60 * 60);
```

* 日付形式の指定方法、とりあえずよくつかうものだけ

``` javascript
// 現在日時を24時間表記で取得 ※「M」は月、「m」は分
var today = new Date();
Logger.log(Utilities.formatDate(today, 'Asia/Tokyo', 'yyyy/MM/dd HH:mm:ss'));
```

日付や時間を直接比較するときは日付形式を指定してから判定をした方がよい。

``` javascript
var today = new Date();
if (Utilities.formatDate(today, 'Asia/Tokyo', 'HH') > 12) {
  Logger.log('午後です');
} else {
  Logger.log('午前です');
}
```

このあたりをおさえておいて、細かいところは必要に応じて調べていく感じです。参考になれば。
GAS：SpreadSheetに登録したデータをFirestoreに送信する

Goolgle SpreadSheetに登録したデータをFirestoreに送信するGASを実装してみました。
その記録です。
英語ですが詳しいことは、[grahamearley/FirestoreGoogleAppsScript](https://github.com/grahamearley/FirestoreGoogleAppsScript)に書かれています。

日本語で自分が参考にした記事は、[【書籍管理シリーズ】GASとFireBase(Firestore)を連携させるよ！](https://qiita.com/h_plum/items/f6ba16931f955d33158e)です。
どちらもわかりやすく丁寧に書かれていて、思っていたよりもかなり簡単に実装できました。


SpreadSheetのGASでの扱い方については、今回は触れません。
そちらについてはウェブサイト[【初心者向けGAS】スプレッドシートのシートを取得する２つの方法](https://tonari-it.com/gas-spreadsheet-get-sheet/)がとても参考になるので、読んでみてください。

# Firestoreのライブラリを追加 
まずは、
ツールからスクリプトエディタを起動し、
リソース＞ライブラリ＞ライブラリを追加から、

```
1VUSl4b1r1eoNcRWotZM3e87ygkxvXltOgyDZhixqncz9lQ3MjfT1iKFw
```

を入力し、その最新のバージョンを選択して保存します（※2019年1月現在では22）。
識別子はFirestoreになっているかと思います。


# Firestoreへの認証情報を入力
続いて、GASのファイルに、認証に必要な情報をまとめます。
emailの部分は、設定＞サービスアカウントのところに書かれていて、~~~@sexualhealthmedia-736f9.iam.gserviceaccount.comというアドレスです。
秘密keyはそのページから秘密キーを生成するを押すと作られるjsonファイルの、 "private_key": "~~~"、この~~~の部分です。

```js
// Firestoreへ認証する為のデータ情報を設定
function firestoreCertification() {
  var certification = {
    "email": "<Your Project's Service Account>", 
    "key": "-----BEGIN PRIVATE KEY-----\n<Your Secret Key>\n-----END PRIVATE KEY-----\n",
    "projectId": "<Your Project's ID>"
  }
  return certification;
}
```


# Firestoreへ送信するコードを書く
まずは全体のコードを。

```js
//Firestoreへ送信するためのコード
function setDataToFirestore(){
  var sheet = SpreadsheetApp.getActive().getSheetByName('<シート名>');

  //Firestoreへの認証のための変数を用意
  var certification = firestoreCertification();
  var firestore = FirestoreApp.getFirestore(certification.email, certification.key, certification.projectId);
  
  try {
    //name
    var nameValue = sheet1.getRange('<範囲>').getValue();

    //genre
    var genreValue = sheet1.getRange('<範囲>').getValue();

    var newData = {
          name:nameValue,
          genre:genreValue,
    }

    firestore.updateDocument("<collection名>/<document名>", newData);

  } catch(e) {
    Logger.log(e)
  }

}

```

Realtime Databaseへ送信するときは、pathは/(スラッシュ)で区切ればよいというシンプルな形式でしたが、
Firestoreの場合、collectionとdocumentという新しい概念によって若干pathの書き方に注意しないといけないっぽいです。

上の例だと、

```js
firestore.updateDocument("<collection名>/<document名>", newData);
```
のようにpathを設定していますが、仮に

```js
firestore.updateDocument("<collection名>/<document名>/<ほにゃらら>", newData);
```

というpathを設定した場合、"ほにゃらら"というcollectionが生成されることになります。
では、documentの下に辞書型でデータを保存するためにはどうすればよいかというと、

```js
var newData = {
  name:nameValue,
  genre:genreValue,
}
```
この例のように、辞書型で値を定義した変数を、updateDocumentの引数として渡せばいいようです。


# updateDocument()かcreateDocument()か
Firebaseにデータを追加するメソッドには、updateDocument()とcreateDocument()があるのですが、updateDocumentの方が使いやすそうです。

[grahamearley/FirestoreGoogleAppsScript](https://github.com/grahamearley/FirestoreGoogleAppsScript)には、
```Note: Although you can call updateDocument without using createDocument to create the document, any documents in your path will not be created and thus you can only access the document by using the path explicitly.
```

と書かれていて、updateDocumentでもdocumentは作れるけれど、ちゃんとpathは明記してね、とのこと。
つまり、creatDocument()を使えば、特にdocument名（上でいう/\<document名>の部分）を書かなくても、勝手にdocumentを生成してくれるけれど、
updateDocument()を使うなら、必ず\<collection名>/\<document名>まで書きなさいねってことみたいです。
MacBook Proの個人用環境構築メモ

# はじめに
Macを使うようになり、環境構築について改めて考えてみた

# 目的
環境構築の流れを忘れないようメモします。(更新あり？)
今後、環境構築が必要になる人(特に後輩)向けになれば

# 流れ
1. homebrewの導入
2. Zshの導入
3. ShiftItの導入
4. ⌘英かなの導入

## homebrewの導入
### homebrewとは？
MacOS用のパッケージ管理ツール

[Homebrew のインストールと基本的な使い方](https://yu8mada.com/2018/04/12/homebrew-s-installation-and-basic-usage/#content-3)
このサイトを参考に導入を行いました。
使い方も書いているので個人的におすすめ

大まかな流れ
・Xcodeのインストール...長かった笑
・command line tools のインストール

```
xcode-select --install
```
ターミナル上でこのコマンドを入力するとインストール要求が来ます

・homebrewのインストール
です。

## Zshの導入
続いてZshの導入です。
もともとMacOSにはBashというシェルが既存で使用できます。
しかし、補完性能の高いZshの方が個人的に便利かと思いました。

[Macの環境設定(3) zshを入れる](https://qiita.com/nenokido2000/items/763a4af5c161ff5ede68)
こちらを参考に導入を行いました。

大まかな流れ
・homebrewによるインストール

```
brew install zsh
```


・ログインシェル(bazh->zsh)の変更
1. `/etc/shells`に`/usr/local/bin/zsh`の追記
2. コマンド実行

```
chsh -s /usr/local/bin/zsh
```


です。

## ShiftItの導入
こちらはMacOs用ウィンドウ分割ツールです。
・最大化/最小化
・フルスクリーン
・1/2分割(上下左右)
・1/4分割(右上右下左上左下)
等様々あります。便利。
ショートカットキーの重複があるっぽいので自分好みに変更した方がいいかもですね。

[Macのウィンドウ分割に便利なShiftIt](https://qiita.com/tozaiuser/items/82f496104aa35226a44b)
こちらを参考に導入を行いました。

[こちら](https://github.com/fikovnik/ShiftIt/releases)からダウンロードします。

## ⌘英かなの導入
MacBook ProのUSキーボードではかな入力、英入力などの切り替えがわかりにくい。
このツールは自分で設定したショートカットキーで切り替えが行えるので便利。

[英かな](https://ei-kana.appspot.com/)からダウンロードしてください。
導入簡単です


# 最後に
参考にさせていただいたサイト様ありがとうございます。
【C#】TaskのTimeoutの方法

繰り返し処理のTaskのタイムアウトのより良い方法というのは何だろう？

数パターン書いてみて、一番C#らしいのはCancellationTokenSourceを使うやり方だと思いました。

都合があって.NET4.0 C#4.0です。

# CancellationTokenSourceを使う

```C#:一番それらしい？？
var source = new CancellationTokenSource();
//タイムアウトの設定 3秒
source.CancelAfter(3000);

Task t = Task.Factory.StartNew(() =>
{
    while(true)
    {
        source.Token.ThrowIfCancellationRequested();
        Thread.Sleep(100);//Task.Delay(100);//Delayが使えればDelayが良いな･･･。
    }
}, source.Token);

try
{
    t.Wait(source.Token);//OperationCanceledExceptionが発生します。
    //t.Wait();//AggregateExceptionが発生します。
}
catch(OperationCanceledException)
{
    Console.WriteLine("OperationCanceledExceptionが発生しました。");
}
catch(AggregateException)
{
    Console.WriteLine("AggregateExceptionが発生しました。");
}

```


# CancellationTokenSourceを使う (微妙)
これに至る前はこう書きました。

```C#:微妙
var source = new CancellationTokenSource();

Task t = Task.Factory.StartNew(() =>
{
    while (true)
    {
        source.Token.ThrowIfCancellationRequested();
        Thread.Sleep(100);//Task.Delay(100);//Delayが使えればDelayが良いな･･･。
    }
}, source.Token);

try
{
    t.Wait(3000, source.Token);
    if (!t.IsCompleted)
    {
        source.Cancel();
        t.Wait();
    }
}
catch (OperationCanceledException)
{
    Console.WriteLine("OperationCanceledExceptionが発生しました。");
}
catch (AggregateException)
{
    Console.WriteLine("AggregateExceptionが発生しました。");
}

```

Waitが2つだったりtry～catchが2箇所を目的に包んでいたり。
ちょっと気持ち悪いですね。


# CancellationTokenSourceを使わない

CancellationTokenSourceを使わないと何かしらの手段で自前で時間を測る必要が出てきます。


```C#:DateTimeとTimeSpan
Task t = Task.Factory.StartNew(() =>
{
    var start = DateTime.Now;
    while (true)
    {
        
        if(DateTime.Now - start > TimeSpan.FromMilliseconds(3000))
        {
            break;
        }
        Thread.Sleep(100);
    }
});

t.Wait();

```




```C#:Stopwatch
Task t = Task.Factory.StartNew(() =>
{
    var stopwatch = new System.Diagnostics.Stopwatch();
    stopwatch.Start();
    while (true)
    {
        
        if(stopwatch.ElapsedMilliseconds > 3000)
        {
            stopwatch.Stop();
            break;
        }
        Thread.Sleep(100);
    }
});

t.Wait();
```
anyenvからnodenvをインストールしてyarnをインストールするまで

## anyenvのインストール
```
git clone https://github.com/riywo/anyenv ~/.anyenv
```

bashやzsh勢

```
export PATH="$HOME/.anyenv/bin:$PATH"
eval "$(anyenv init -)"
exec $SHELL -l
```

僕はfish勢なので、`config.fish`に以下を追記

```
set -x PATH $HOME/.anyenv/bin $PATH
```

再読込

```
exec fish
```

確認

```
anyenv -v
```

インストールできるenv系を確認

```
anyenv install -l
```

上のコマンドが出来なかったので、、調べると`init`をしないといけないっぽい

```
anyenv install --init
```

参考：https://medium.com/@riywo/anyenv-all-in-one-for-env-6423d23b0838


## nodenvのインストール
anyenvでnodenvのインストール

```
anyenv install nodenv
```

`nodenv`の確認

```
nodenv -v
```

出来ない。。。涙

fish勢は`config.fish`に設定が必要なようだ。

```
set -x NODENV_ROOT $HOME/.anyenv/envs/nodenv
set -x PATH $HOME/.anyenv/envs/nodenv/bin $PATH
set -gx PATH $NODENV_ROOT/shims $PATH
```

これで再読込

```
exec fish
```

うまくいった！



## yarnのインストール
npm経由でyarnをインストールする

```
npm install -g yarn
```

とやったが、うまくいかない。(一応パスを通せばできるっぽいけどめんどいｗ）

なので、潔くbrewでインストールしちゃうｗ

```
brew install yarn
```

これにて完了！
（icu4cとnodeが依存として勝手にインストールされちゃう）

## あとがき
自分はfishシェルを使っていたため、色んな所でハマってしまった。
結果的にyarnのインストールまで完了することが出来た。
これからのyarnライフを満喫します。

ひろたつ
Pythonで、「漢字一字」のJpg画像を連続で作っていく

プログラミングで、たくさんの漢字画像ファイルを書き出す方法です

※スクラッチの、漢字当てゲームのために、作りました
https://scratch.mit.edu/projects/280853889/
[リンク](https://scratch.mit.edu/projects/280853889/)


```python:OUTIMG.py
#coding: utf-8

from PIL import Image, ImageDraw, ImageFont

font = ImageFont.truetype('/Library/Fonts/ヒラギノ丸ゴ ProN W4.ttc', 256)

with open('./kanji6.txt', 'r') as f:
    for s_line in f:
        s_line = s_line.replace('\n', '')
        s_line = s_line.replace('\r', '')

        im = Image.new("RGB", (256, 256), (255, 255, 255))
        draw = ImageDraw.Draw(im)
        draw.multiline_text((0, 0), unicode(s_line, 'utf-8'), fill=(0, 0, 0), font=font)
        im.save('./' + s_line + '.jpg', quality=95)
f.close()
```

```text:kanji6.txt
異
遺
域
宇
映
延
沿
我
灰
```

漢字の画像ファイルができます

![異.jpg](https://qiita-image-store.s3.amazonaws.com/0/107418/62bf5dc0-691d-014a-e9ca-9a0d4b5f73fe.jpeg)
C言語でファイルの読み書きを行う。

ファイルが与えられて、それをもとに入出力処理を行うときのやりかたを個人的な覚書とともにシェアします。

# 以下覚書。解説あまりないです。
- 下のようなデータ、point.csvが以下のような書式付きで与えられていたとしますね。
    - 左から順に、出席番号、英語、数学、国語、理科、社会の順でカンマ区切りで与えられているとします。
    - 得点で成績をつけて、科目ごとにそれぞれ別々のファイルに出席番号と成績をカンマ区切りで書き込みます。

```csv:point.csv
1001,99,83,94,100,86
1002,42,15,61,44,57
1003,37,75,65,28,49
1004,58,52,51,62,55
1005,83,90,33,27,10
1006,41,27,36,85,86
1007,90,56,83,48,95
1008,12,89,24,78,21
```
- ここからソースコードです。

```c:score.c
#include <stdio.h>

#define seito 8

int main(void){
    //配列は全科目用意してるけど英語の成績のみ書き出すところまで書いてます。
    printf("Start\n");

    int num[seito],eng[seito],math[seito],japa[seito],sci[seito],civ[seito];
    int i;

    //読み込み用ファイルのロード
    FILE *fp;
    if( (fp = fopen( "score.csv", "r" )) == NULL) {
        printf("can not open write file.");
        return -1;
    }

    //ファイルから配列にデータを読み込む
    for( i = 0; i < seito; i++ ) {
        if( fscanf( fp, "%d,%d,%d,%d,%d,%d\n",&num[i],&eng[i],&math[i],&japa[i],&sci[i],&civ[i]) != EOF );
    }//これでファイルの最後まで１行ごとに書式指定で読み込んでくれます。
    fclose(fp);//読み込み用のファイルは不要なので閉じておく。

    //英語の成績を処理するために、まずは書き込み用のファイルをオープンする。
    if( (fp = fopen( "grade_Eng.csv", "w" )) == NULL) {
        printf("can not open write file.");
        return -1;
    }
    for( i = 0; i < seito; i++){//点数に応じてファイルに書き込み。
        if(eng[i] < 0 || eng[i] > 100){
            fprintf(fp,"%d,%s\n",num[i],"X");
        }
        else if(eng[i] < 60){
            fprintf(fp,"%d,%s\n",num[i],"F");
        }
        else if(eng[i] < 70){
            fprintf(fp,"%d,%s\n",num[i],"C");
        }
        else if(eng[i] < 80){
            fprintf(fp,"%d,%s\n",num[i],"B");
        }
        else if(eng[i] < 90){
            fprintf(fp,"%d,%s\n",num[i],"A");
        }
        else {
            fprintf(fp,"%d,%s\n",num[i],"A+");
        }
    }//fpを閉じるまでは上書きではなく追加書き込みしてくれるからこれで大丈夫です。
    fclose(fp);

    printf("End\n");
    return 0;

}
```


画面出力結果

```terminal
Start
End
```
ファイル出力

```csv:grade_Eng.csv
1001,A+
1002,F
1003,F
1004,F
1005,A
1006,F
1007,A+
1008,F
```
# 最後に
今回は英語のみしましたが、成績判定のところとかを関数化するなり、または新しくfpにgrade_math.csvを開いてeng[i]をmath[i]に変えて後ろにおなじようにくっつけて、成績判定して書き込んでクローズを科目数分行えば可能かと思います。
もちろん、２次元配列や構造体を使うほうが汎用性が出て便利にはなりますが、文字列や文字を処理する際にポインタなどを使うことが多く複雑になりやすいです。。
QGIS3.4.4 'Madeira'  セットアップ on Windows10

QGISをWindows10にインストールすることになりました。
GIS初心者が行うQGIS事始めです。

# QGIS3.4をWindows10にインストールする手順

## インストールする環境

- Windows 10 Pro x64

## まずはインストーラを準備

- 下記のサイトから最新のインストーラを入手
- [QGISのダウンロード](https://www.qgis.org/ja/site/forusers/download.html) 
![QGISダウンロードページ](https://qiita-image-store.s3.amazonaws.com/0/347923/085f1bdc-f655-76bb-a4fa-81a967130b94.png "QGISダウンロードページ")

## インストール開始！

### ダウンロードしたインストーラを管理者権限でダブルクリック
　　![image8.png](https://qiita-image-store.s3.amazonaws.com/0/347923/9173e340-3c26-1881-d9d1-6fa4cfa4beea.png)

### ようこそ画面が表示される。「次へ」をクリック
　　![image6.png](https://qiita-image-store.s3.amazonaws.com/0/347923/4706fe0a-f940-cb68-c05a-954dec7053c2.png)

### ライセンス契約書の確認画面が表示される。確認して「同意する」をクリック
　　![image2.png](https://qiita-image-store.s3.amazonaws.com/0/347923/12e38c4d-42ef-6d08-b7f1-63613e7eade4.png)

### インストール先を選んでくださいの画面が表示される。インストール先フォルダを設定して「次へ」をクリック
　　![image7.png](https://qiita-image-store.s3.amazonaws.com/0/347923/ae763962-fd4f-655f-963d-4d43b44692d0.png)

- 私はEドライブをアプリのディスクと決めているので変更しました。

### コンポーネントを選んでくださいの画面が表示される。確認して「インストール」をクリック
　　![image5.png](https://qiita-image-store.s3.amazonaws.com/0/347923/bfd93478-f0c2-1843-c282-e59ea69d0bef.png)

- 試しに全部選択して進めましたが、ダウンロードに時間がかかるため１つ目のData Setのみインストールし、後の２つはキャンセルしました。オプションをインストールするか・しないかを選択する画面が表示されるので、全部選択していたとしても該当オプションだけキャンセルできたので良かったです。

　　![image1.png](https://qiita-image-store.s3.amazonaws.com/0/347923/b950247d-fcab-feea-2b8d-029caf712e46.png)


### インストール画面が表示される。じっと待つ。
　　![image13.png](https://qiita-image-store.s3.amazonaws.com/0/347923/5faf2509-c8ad-7e0c-f1bc-a3a0523a9b6f.png)

- 詳細表示をクリックするとこんな感じの画面が表示される。
　　![image12.png](https://qiita-image-store.s3.amazonaws.com/0/347923/b55874bf-380a-eb6c-fe44-63eed79ea9eb.png)
    
###  セットアップウィザード完了画面が表示さえる。「完了」をクリック
　　![image4.png](https://qiita-image-store.s3.amazonaws.com/0/347923/42e8a032-0dab-9d14-6626-a88fbd7e18f7.png)
- お疲れ様でした。あっさり終わりました。

## スタートメニューを確認
- なんかいろいろと追加されてますね。

　　![image10.png](https://qiita-image-store.s3.amazonaws.com/0/347923/9290911e-c192-5ed8-d8d7-d4b0cbeec421.png)

#最後に
まだ何も設定をしていないのでいよいよこれからです。
次は地図情報等を取り込んでなにか表示できたらと思ってます。
paizaでsympy

#概要

sympyが、魔法なので、paizaで、やってみた。

#練習問題


```
# coding: utf-8
# Your code here!

import sympy as sym

x = sym.symbols('x')
print ("expand - 展開")
print (sym.expand((x + 1) ** 2))
print ("factor - 因数分解")
print (sym.factor(x ** 4 - 3 * x ** 2 + 1))
print (sym.factor(x ** 3 - x ** 2 - 3 * x + 3))
print ("simplify - 簡約")
print (sym.simplify((x ** 3 + x ** 2 - x - 1) / (x ** 2 + 2 * x + 1)))
print (sym.simplify(x ** 2 / (2 * x ** (4 / 3)) ** 3))
print (sym.simplify((2 * x ** (4 / 3)) ** 2 / x ** (2 / 3)))
print (sym.simplify(1 / x + (x * sym.sin(x) - 1) / x))
print ("limit - 極限")
print (sym.limit(x, x, sym.oo))
print (sym.limit(x, x, sym.O))
print ("diff - 微分")
print (sym.diff(sym.cos(x), x))
print (sym.diff(x ** 3 + x ** 2 - x - 1, x))
print (sym.diff(sym.cos(x)))
print (sym.diff(sym.exp(x)))
print (sym.diff(sym.log(x)))
print ("integrate - 積分")
print (sym.integrate(sym.cos(x), x))
print (sym.integrate(x ** 3 + x ** 2 - x - 1, x))
print (sym.integrate(sym.cos(x)))
print (sym.integrate(sym.exp(x)))
print (sym.integrate(sym.log(x)))
print ("Matrix - 行列")
print (sym.Matrix([[1, 2, 3], [-2, 0, 4]]))
print ("solve - 式を解く")
print (sym.solve(x ** 2 - 1, x))
print (sym.solve(x ** 2 - 3 * x + 2))
print (sym.solve(x ** 2 + x + 1))
print (sym.solve(x ** 3 + 2 * x ** 2 + 4 * x + 8, x))

```

#結果


```
expand - 展開
x**2 + 2*x + 1
factor - 因数分解
(x**2 - x - 1)*(x**2 + x - 1)
(x - 1)*(x**2 - 3)
simplify - 簡約
x - 1
x**(-2.0)/8
4*x**2.0
sin(x)
limit - 極限
oo
<class 'sympy.series.order.Order'>
diff - 微分
-sin(x)
3*x**2 + 2*x - 1
-sin(x)
exp(x)
1/x
integrate - 積分
sin(x)
x**4/4 + x**3/3 - x**2/2 - x
sin(x)
exp(x)
x*log(x) - x
Matrix - 行列
Matrix([[1, 2, 3], [-2, 0, 4]])
solve - 式を解く
[-1, 1]
[1, 2]
[-1/2 - sqrt(3)*I/2, -1/2 + sqrt(3)*I/2]
[-2, -2*I, 2*I]

```

#成果物

https://paiza.io/projects/IFZVNRkTTs80EiKzsOyCSg

以上。
init.el に依らない Emacs on Ubuntu でのフォントサイズ設定

いろいろなところで言われていますが、Ubuntu での Emacs って確かにフォントが大きすぎて、Full HD くらいのサイズならちょうどいいのですが、Note PC とかであまり解像度がない環境では作業がしにくいです。

いろいろ調べたところ、init.el にフォント設定を書き込むという物がありました。しかし、X のデフォルトフォントが効いてるんじゃないかと発想を変えてみたら上手く行ったという話です。

## GNOME Tweaks のインストール

Ubuntu のデフォルトのデスクトップ環境は GNOME で、私もこれを使っています。
GNOME の細かい設定を行うには Tweaks というツールを使用します。
もしもインストールされていなければ色々と便利なのでインストールしましょう。

```shell-session
sudo apt-get install gnome-tweaks
```

余談ですが、私は昔ながらの X の人なので、MS-Windows と異なりマウス・オーバーでウィンドウの重なり方を変えずにフォーカスが行くという挙動が好きなので、これも GNOME Tweaks で設定しています。

## GNOME Tweaks の実行

GNOME Tweaks の実行。「アプリケーションの表示」から「Tweaks」で検索しても良いです。

```shell-session
gnome-tweaks
```

GNOME Teaks を実行すると「フォント」という項目があるので、これのフォントサイズを変えていきます。個人的には NotePC での使用には 10pt 程度が快適に感じました。Emacs の設定に一番効くのは「等幅」の設定ですが、全部 10pt に設定してみました。
それで、再ログインしてみると...。
あら不思議 (いや、そう設定したので不思議でも何でもないのだが)、「端末」とか「Emacs」のフォントもちゃんと反映されています。

これで、起動時に自動実行するアプリで Emacs を登録しても、ちゃんと `--geometry 80x52-0+0` みたいなウインドウサイズ・位置指定のオプションが効いてくれました。

めでたし、めでたし。

Ubuntu 18.04 LTS と 18.10、19.04、19.10、20.04、20.10で確認しました。
AWSでCloud9を使えるようにしよう

###はじめに###
Cloud9を使うために、AWSアカウントを作成して、環境構築を進めていましたが、少しつまずきました。
ネットでググっても情報が古かったり、リファレンスが薄い部分があったりと苦労した部分もありました。
なので、備忘録的に一度まとめようと思います。
あくまでも自分はこうしましたという一例なのでご了承ください

####①AWSアカウント取得####
アカウント取得はすみませんが、省略します
Eメールアドレスやパスワード、連絡先、クレジットカード情報等を入力して、日本語自動音声電話によるアカウント認証を行い、サポートプランの選択を行うとで登録を行えます。(ざっくりなしてます)
1年間の無料枠があるので、とても便利です。

####②グループ・ユーザーの作成####
Cloud9を使えるようにする前に、Cloud9を使う用のユーザーを作成します。
①で作成したユーザーはルートユーザーで何でもできてしまうので、最低限の権限しかないユーザーで、操作を行います。
これはAWSのほかの機能を使うときも同様にユーザーを作成して、最低限の権限を付与するのがおすすめです。

AWSにルート権限でログインし、サービスからIAMを選択します。

![iam.png](https://qiita-image-store.s3.amazonaws.com/0/239560/68dee9fd-ce3a-07aa-cb6a-959ef8ca5233.png)

![iam.png](https://qiita-image-store.s3.amazonaws.com/0/239560/ee7561b8-8b58-bb5f-02c9-7a54f81aba70.png)(**イメージ①**)

ユーザーとグループを作成することができます。
ユーザー作成の際に所属するグループを選択するので、今回はグループを先に作成します。

グループをクリックすると、グループ名の入力を求められるので、任意のグループ名を入力し、次のステップをクリックすると、ポリシーのアタッチを選べる画面に遷移します(権限を選べる画面です)

![iam.png](https://qiita-image-store.s3.amazonaws.com/0/239560/3d71c686-5c2d-c9d1-8ec5-72d9bde719df.png)

今回はCloud9を使えるようにするので、**AmazonEC2FullAccess**と**AWSCloud9Administrator**を選択して、確認画面に移動します。
グループの作成をクリックすると一覧に戻り、グループが作成されているはずです。

次にユーザーの作成です
イメージ①のユーザーを押し、ユーザーの追加をクリックする。


![iam.png](https://qiita-image-store.s3.amazonaws.com/0/239560/02630451-ec96-fee5-c0ec-7a301b2da5af.png)

ユーザー名を入力し、AWSマネージメントコンソールへのアクセスを選択、カスタムパスワードを入力し、パスワードのリセットが必要にチェックを入れ、「次のステップ：アクセス権限」ボタンをクリックしました。
ユーザーを追加画面に移動するはずです。
ユーザーを追加画面ではアクセス許可の設定で、ユーザーをグループに追加を選択して選択して、先ほど作成したグループが一覧に表示されているはずなので、チェックを入れて、次のステップに進みます。

タグの追加(オプション)では何も入力せず、確認画面に移動し、ユーザーの作成ボタンをクリックします。
問題がなければ成功のメッセージが表示されるはずです。

![iam.png](https://qiita-image-store.s3.amazonaws.com/0/239560/a08d5e53-64a9-6f72-c3d0-75005971588e.png)


成功のメッセージの中に作成したユーザーのログイン用URLが記載されているため、メモをするか、CSVのダウンロードでユーザー情報を入手しよう。
また、Eメールの送信をクリックすると、ログインの手順をメールで送ってくれるため、活用しよう

####③Cloud9の環境作成####

②で作成したユーザーでログインしよう
ユーザーとパスワードは②で作成した情報を入力しよう
※②のユーザー作成でパスワードのリセットが必要にチェックを入れたら、再度パスワードを設定するため、再設定パスワードで次回以降はログインしてください

サービスをクリックして、Cloud9を選択してください
![iam.png](https://qiita-image-store.s3.amazonaws.com/0/239560/dc9bda78-a8e4-b5b1-f050-d2b294d085c6.png)

Cloud９の画面に移動すると思うので、Create environmentボタンをクリックします。
すると、名前と説明を入力する画面に移動するので、記載します。(説明は任意)


![iam.png](https://qiita-image-store.s3.amazonaws.com/0/239560/826fdcab-a889-4bd4-a85e-051a6d0408cd.png)

環境設定画面に移動するので、下記設定を行います。
Environment type ---->  EC2
instance type ---->  t2.micro(無料なので、とりあえず)
Cost-saving setting ---->  After 30 minutes(30分触らないとインスタンスが停止する設定)


次に行き、確認を行い、設定通りであれば、Create environmentボタンをクリックする
すると、インスタンスが起動し、Cloud9の画面に移動する
次回以降はサービスのCloud9をクリックすると設定した名前のインスタンスが表示されるため、Open IDEをクリックすればCloud9を使うことができます。

##最後に##
今回は自分が行った設定を記載しましたが、**すべてではありません。**
ルートユーザーのセキュリティステータスをすべて成功させる必要があります。
少し詳しく述べると、I AMユーザーやルートユーザーのセキュリティ(2段階認証)の設定やパスワードポリシーの適用などがある意味必須となります。
時間があれば追記したいです。
また、今回の設定がすべてではないので、参考程度にCloud9やAWSに触れるきっかけになればいいなぁと思います!
TypeScript で再帰的な型を定義する

追記

最新の TypeScript （2021/05/12 現在）では、この記事のテクニックをかなりかんたんに記述できるようになっています。

```ts
// tuple を自然に取り扱えるようになった（以前は関数の引数を経由する必要があった）
type Head<T> = T extends [infer U, ...unknown[]] ? U : never;
type Tail<T> = T extends [unknown, ...infer U] ? U : [];

// 再帰を自然に記述できるようになった
// readonly で as const 相当を強制できるようになった
type DeepPath<State, Paths extends readonly unknown[]> = Head<Paths> extends keyof State
  ? Tail<Paths> extends []
    ? State[Head<Paths>]
    : DeepPath<NonNullable<State[Head<Paths>]>, Tail<Paths>>
  : never;
```


# はじめに
この記事では TypeScript で再帰的な型を定義する方法を紹介します。
TypeScript 推奨の方法かどうかは特に調べてないので将来的に壊れたりする可能性はあります。

# サマリ
- TypeScript では素直に再帰的な型を定義できない
- その回避方法を調べていたら TypeScript 本家 issue にやり方が紹介されていた
- 試しに `get(obj, 'key', 'key')` に型をつけてみたらうまくいった
- その方法を簡略化すると下記のような感じだったので紹介する

```ts
type MyType<T> = {
  0: ...;
  1: MyType<...>;
}[T extends ... ? 1 : 0];
```

# お題
あるネストしたオブジェクトの深い部分の型を取り出します。

```ts
type State = {
  key1: {
    key2: {
      key3: { name: string }[];
    };
  };
};

type Key3Value = DeepPath<State, [
  'key1',
  'key2',
  'key3',
  number
]>; // { name: string; };
```

# 実装
```ts
// Tuple の先頭を取り出す
type Head<U> = U extends [any, ...any[]]
  ? ((...args: U) => any) extends (head: infer H, ...args: any) => any
    ? H
    : never
  : never;

// Tuple の先頭以外を Tuple として取り出す
type Tail<U> = U extends [any, any, ...any[]]
  ? ((...args: U) => any) extends (head: any, ...args: infer T) => any
    ? T
    : never
  : never;

// State と Paths を与え、再帰的に型を取り出していく
type DeepPath<State, Paths extends any[]> = Head<Paths> extends keyof State
  ? {
      0: State[Head<Paths>];
      1: DeepPath<State[Head<Paths>], Tail<Paths>>;
    }[Tail<Paths> extends never ? 0 : 1]
  : never;
```

# 説明
一番重要なのは `DeepPath` の実装になりますが、なんだか妙な記述です。
素直に書くとこうなります。

```ts
type DeepPath<State, Paths extends any[]> = Tail<Paths> extends never
  ? State[Head<Paths>]
  : DeepPath<State[Head<Paths>], Tail<Paths>>;
```

でもこれは TypeScript 的には NG です。
`Type alias 'DeepPath' circularly references itself.` と言われます。

そのため、下記のように一度オブジェクトの型を定義した上で、即座にそのオブジェクトのインデックスを選択的に参照することでエラーを回避しています。

```ts
type DeepPath<State, Paths extends any[]> = Head<Paths> extends keyof State
  ? {
      0: State[Head<Paths>];
      1: DeepPath<State[Head<Paths>], Tail<Paths>>;
    }[Tail<Paths> extends never ? 0 : 1]
  : never;
```

やっていることは変わらないのですが書き方で変わるのが面白いです。

この方法は https://github.com/Microsoft/TypeScript/issues/14833 などでも紹介されているようです。

これを利用すると下記のような実装を行っても利用側で型を明示せずに推論されます。

```ts
export function get<State extends any, Paths extends (string | number)[]>(state: State, ...paths: Paths): DeepPath<State, Paths> {
  const [head, ...tail] = paths;

  if (tail.length) {
    return get(state[head], ...tail);
  }

  return state[head];
}

const hrsh7th = get({
  key1: {
    key2: {
      key3: [{ name: 'hrsh7th' }]
    }
  }
}, 'key1', 'key2', 'key3', 0);

console.log(hrsh7th.name); // 推論されているので、エディタなどで補完が効く
```

※ `get(state, ['key1', 'key2', 'key3', 0]);` なシグネチャにするのは paths が `(string | number)[]` に推論されてしまって難しかったです。
※ paths はあくまでも `['key1', 'key2', 'key3', 0]` に推論してもらう必要があります。配列を使ってこのように推論させる方法がわからないので募集中です。

# おわりに
普段のプログラミングで定義することはあまりなさそうですが、ライブラリ作る時とかは便利かもしれません。
[簡単] screenの起動時に表示されるメッセージを消す [ほぼ自分用メモ]

#設定ファイルがない時は作る
```
touch ~/.screenrc
```

#設定を書き込む
```
nano ~/.screenrc
```
.screenrc に以下を書き込む
>\# unable startup message
startup_message off

ついでに、screenを起動中に`ctrl + a`で行頭にカーソルを移動するには .screenrc に以下を書き込む
>\# prefix key
escape ^Jj

#メモ
環境

- Screen version 4.06.02 (GNU) 23-Oct-17
- Ubuntu VERSION="18.04.1 LTS (Bionic Beaver)"

再起動は必要ない。
`ctrl + a`で行頭にカーソルを移動する設定を行った時は、デタッチは`ctrl + j + d`に変更になる。
参考にしたサイトは[こちら](https://bacchi.me/linux/how-2-use-screen/)
Nginxで足し算するときの代替案

## モジュールなしで対応する方法
※コメントにて`lua-nginx-module`モジュールを用いた方法を教えていただいたので、
その方法も末尾に追記しました。

システム移行のときに実際に困ったのでメモ代わりに。

システム移行に伴って旧URLを新URLにリダイレクトさせるようなことがあるかと思います。
自分の例では、`/hoge/1000`というURLを`/piyo/index.html?id=1001000`というように、パスの変更に加えてID部分も旧URLの時から+100万するという変更でした。

nginxでは足し算が出来ないようなので代替案として以下のように設定しました。

```nginx
server {
    # ..(省略)..
    location ~* ^/(hoge)/(\d)(\/*)$ {
        rewrite /(hoge)/(.*) https://$host/piyo/index.html?id=100000$2 permanent;
    }
    location ~* ^/(hoge)/(\d\d)(\/*)$ {
        rewrite /(hoge)/(.*) https://$host/piyo/index.html?id=10000$2 permanent;
    }
    location ~* ^/(hoge)/(\d\d\d)(\/*)$ {
        rewrite /(hoge)/(.*) https://$host/piyo/index.html?id=1000$2 permanent;
    }
    location ~* ^/(hoge)/(\d\d\d\d)(\/*)$ {
        rewrite /(hoge)/(.*) https://$host/piyo/index.html?id=100$2 permanent;
    }
    location ~* ^/(hoge)/(\d\d\d\d\d)(\/*)$ {
        rewrite /(hoge)/(.*) https://$host/piyo/index.html?id=10$2 permanent;
    }
    location ~* ^/(hoge)/(\d\d\d\d\d\d)(\/*)$ {
        rewrite /(hoge)/(.*) https://$host/piyo/index.html?id=1$2 permanent;
    }
    # ..(省略)..
}
```
ようするに1桁であれば100000を先頭に追加して、2桁であれば10000を追加…というようにしています。
この方法は、繰り上がりがない場合しか使えないですが、システム統合などでIDに大きな数字を足す時は使えるかもしれないです。

本来であればプログラム側でリダイレクトさせるほうが楽のハズなんですが、
自分の例ではプログラム側を操作できない状況でリダイレクトさせる必要があったため、この方法を取りました。

※もしnginxで足し算させる方法ご存知でしたら教えて下さい。

## `lua-nginx-module`モジュール使用した場合の方法

コメントにて`lua-nginx-module`を用いると可能というお話を頂いたので試してみました。
[apt-getでDebian 8 + lua nginx moduleの環境構築(nginx-extras)](https://symfoware.blog.fc2.com/blog-entry-1971.html)を見ると、
`nginx-extras`が`lua-nginx-module`が組み込まれているということで、お手軽に確認したかったのでひとまず`nginx-extras`を使って確認。

```nginx
server {
    # ..(省略)..
    location ~* ^/hoge/(\d+)(\/*)$ {
        set $id $1;
        set $new_id "";
        rewrite_by_lua_block {
            ngx.var.new_id = tonumber(ngx.var.id) + 1000000;
            return ngx.redirect("/piyo/index.html?id="..ngx.var.new_id, 301);
        }
    }
    # ..(省略)..
}
```

上記で、`/hoge/123/`にアクセスすると`/piyo/index.html?id=1000123`にリダイレクトするようになりました。
GCEインスタンス/GKEノードにSSH接続するメモ


## 目的

* GCEインスタンスを起動
* GCEインスタンスにSSH接続
* WindowsPCからとりあえずSSH接続できるところまで
* おまけ：GKEノード（Kubernetes）にSSH接続

## 前提

* Googleアカウントがある
* プロジェクトを作成済

## 作業
* 作成を押してVMを作り始める

![image.png](https://qiita-image-store.s3.amazonaws.com/0/57350/ea893951-27e3-a0b5-ed60-c8ab61d9ccae.png)

* リージョンを決める。無料対象はアメリカ西側など限られるので注意
* インスタンス性能を決める。microが無料らしいのでmicroを選択。デフォルトは違うので注意。
* SSDの容量を決める。30Gまで無料らしい。せっかくなので変更しておく。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/57350/e3e81b56-d48e-0c45-86db-80d2a94cd19c.png)


* SSHでつなぐので固定外部IPをアタッチする
* AWSと同じく使わないで放置しておくと料金が発生するらしいので注意

![image.png](https://qiita-image-store.s3.amazonaws.com/0/57350/a2d81bc9-793d-2220-1426-e5b9bebe0044.png)

* 必要事項を入力し生成
* AWSと同じく使わないで放置しておくと料金が発生するらしいので注意

![image.png](https://qiita-image-store.s3.amazonaws.com/0/57350/aca084fa-6ae0-4d7b-4f17-376e171d5d44.png)

* インスタンスが作成されるまで待つ

![image.png](https://qiita-image-store.s3.amazonaws.com/0/57350/0fc40710-1a7f-edee-bb5a-004ad20949af.png)

### GCEインスタンスにSSH接続したい場合

* メタデータメニューからSSH認証鍵タブを選択
* 手持ちのSSH公開鍵を登録

![image.png](https://qiita-image-store.s3.amazonaws.com/0/57350/3d3e8bdb-8f51-75b3-6158-c60c0100a0f4.png)

* SSH公開鍵を貼り付ける

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/57350/3c76aca9-39d2-bd5f-c4c3-ae7c9c1705f8.png)


* SSHクライアントで接続してみる。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/57350/ffcf1fe3-b933-6462-904c-b9b95a9c8612.png)

* コマンドラインの場合

```
//win
ssh -i "C:\Users\appuser\Desktop\work\sshkey\kp-appuser" appuser@35.211.xxx.xxx

//mac
ssh -i appuser-key appuser@35.211.xxx.xxx
```

### GKEノードにSSH接続したい場合

* Cloud ShellからNode名を指定して"gcloud compute ssh"で接続

```
$ kubectl get pod -o wide
NAME                                READY   STATUS             RESTARTS   AGE   IP           NODE                                            NOMINATED NODE   READINESS GATES
sample-app-server-644758d6d-2gvsb   0/1     CrashLoopBackOff   220        18h   10.48.1.10   gke-sample-cluster-default-pool-abcbad49-2w8m   <none>           <none>
sample-app-server-644758d6d-5x8k4   0/1     CrashLoopBackOff   220        18h   10.48.2.6    gke-sample-cluster-default-pool-abcbad49-95gp   <none>           <none>
sample-app-server-644758d6d-67dfh   0/1     CrashLoopBackOff   220        18h   10.48.1.11   gke-sample-cluster-default-pool-abcbad49-2w8m   <none>           <none>


$ gcloud compute ssh gke-sample-cluster-default-pool-abcbad49-2w8m
$ journalctl -n 50 --no-pager
```

### ファイアウォールで拒否される場合

* コネクションタイムアウトなどはファイアウォールが整備されていない可能性が高いです
* ファイアウォールルールを作成してインスタンスに設定します。
* 作成したファイアウォールルールはインスタンスのネットワークタグとして適用し有効化する
* 個人的にココが非常に分かりづらかったので注意ですね

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/57350/97a0eb29-bada-6194-95b2-9e56409781df.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/57350/81f2fb59-f6af-9fb4-716f-811f00d39b5f.png)
Javaの正規表現でつまずきやすいポイント

# はじめに

Javaの正規表現でつまずくポイントをまとめておきます。

他言語の正規表現をよく使う人ほどハマる気がします。



# エスケープ文字

エスケープシーケンスを書く場合はエスケープ文字 `\` を使用します。

が、JavaのString型で『バックスラッシュ』を表現したい場合は、エスケープシーケンスの1種である `\\` を使用する必要があります。

私は丸1日無駄にしました。

```java:コンパイルエラーを吐くコード
void check() {
    if (isFind("hoge.com")) {
        // 実行できない
    }
}

boolean isFind(String text) {
    // コンパイルエラー!!!!!
    Pattern p = Pattern.compile("\.com");
    return p.matcher(text).find();
}
```

```java:コンパイルが通るコード
void check() {
    if (isFind("hoge.com")) {
        // 実行できる
    }
}

boolean isFind(String text) {
    Pattern p = Pattern.compile("\\.com");
    return p.matcher(text).find();
}
```

要は、他言語におけるエスケープシーケンス `\.` を表現したい場合は、 `\\.` と書く必要があります、ということです。
このあたりの言い回しはややこしいため、コメント欄を参考にしてください。



# `Matcher#group(int)` をすぐには利用できない

Matcherクラスの `group(int)` メソッドは、 `find()` メソッドを利用しないと実行時エラーを吐きます。

というのも、 `find()` メソッドで文字列のマッチを確認するためです。
`group(int)` メソッドは、 `find()` メソッドでマッチした文字列のゲッターでしかありません。

```java:実行時エラーを吐くコード
Pattern p = Pattern.compile("(A*).");

void print() {
    System.out.println(extract(AAABBB));
}

String extract(String text) {
    Matcher m = p.matcher(text);
    // 実行時エラー!!!!!
    return m.group(1);
}
```

```java:ちゃんと実行できるコード
Pattern p = Pattern.compile("(A*).");

void print() {
    System.out.println(extract(AAABBB)); // 出力: AAA
}

String extract(String text) {
    Matcher m = p.matcher(text);
    if (m.find()) {
        return m.group(1);
    } else {
        return "";
    }
}
```



# おわりに

正規表現は、言語によってかなり書き方が異なります。
1つの言語で得意になったと思っていると、痛い目にあうので気を付けましょう。

~~Perlはあんなに簡単なのに！~~
PHPでWebスクレイピングを非同期で実行する

## 必要パッケージをインストール
`composer require guzzlehttp/guzzle symfony/dom-crawler`

## ソース
```run.php

<?php
require_once('vendor/autoload.php');

use GuzzleHttp\Client;
use GuzzleHttp\Pool;
use GuzzleHttp\Psr7\Request;
use Symfony\Component\DomCrawler\Crawler;

// URL一覧を用意
$urls = [
	'http://localhost/01.html',
	'http://localhost/02.html',
	'http://localhost/03.html',
	'http://localhost/04.html',
	'http://localhost/05.html',
];

// クライアント
$client = new Client([
	'headers' => [
		'User-Agent' => 'PHP Crawler',
	],
]);

// リクエストの作成
$requests = function() use($urls) {
	foreach ($urls as $url) {
		yield new Request('GET', $url);
	}
};

$pool = new Pool($client, $requests(), [
	// 同時に実行できる数
	'concurrency' => 3,

	// 各処理が成功したときの処理
	'fulfilled' => function($response, $index) {
		// HTMLを取得
		$body = $response->getBody()->getContents();
		// HTMLを解析
		$crawler = new Crawler($body);
		$crawler->filter('a')->each(function($element) {
			echo sprintf("%s\n", $element->text());
		});
	},

	// 各処理が失敗したときの処理
	'rejected' => function($reason, $index) {
		// $reason : GuzzleHttp\Exception\ConnectException
		// $index : integer
		echo sprintf("Error: %s\n", $reason->getMessage());
	},
]);

// 実行開始
$promise = $pool->promise();
$promise->wait();

// 完了
echo "Completed!\n";
```
岡三マンがNEMを使って本人であることを証明する方法

岡三マンのアカウントが凍結されてしまいました。
タイムリーな情報をいち早くツイートしてくれるため重宝していた人も多いのではないでしょうか？

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">正体不明の岡三マン、ツイッターアカウントの表示に一時的な制限 <a href="https://t.co/lzZMaECkws">https://t.co/lzZMaECkws</a> <a href="https://t.co/UQincJJYaP">pic.twitter.com/UQincJJYaP</a></p>&mdash; ブルームバーグニュース日本語版 (@BloombergJapan) <a href="https://twitter.com/BloombergJapan/status/1087942918659280897?ref_src=twsrc%5Etfw">2019年1月23日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


もし、永久凍結されてしまった場合、岡三マンは新しいアカウントを作らなくてはいけなくなります。
同じ考えの人はたくさんいるでしょう。そして「我こそが岡三マンである」と主張して影響力を持とうとするかもしれません。

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">岡三マン凍結<br><br>まぁ代打はいっぱいいる。 <a href="https://t.co/CARblBjJ2e">pic.twitter.com/CARblBjJ2e</a></p>&mdash; ポケット (@pocketcastanets) <a href="https://twitter.com/pocketcastanets/status/1087910924545998849?ref_src=twsrc%5Etfw">2019年1月23日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

既にたくさんの偽アカウントがあるようです。

それでは第三者の証言無しにブロックチェーンNEMを用いて、我こそは岡三マンであると証明する方法を考えてみます。

残念ながら、twitter以外にチャンネルを持たない現状では非常に難しいです。
しかしもし、彼が過去にNEMを使っていてネームスペース取得を公表していれば状況は異なりました。

NEMWalletの署名済みメッセージとアカウントの検索機能を使います。
まず、岡三マンが自分にしか作成できない署名済みメッセージを作成します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/29187/1e49237f-d026-593a-6c71-38b42f5184b0.png)

サービスから「作成」の方をクリック

![image.png](https://qiita-image-store.s3.amazonaws.com/0/29187/3cb06053-0590-ac45-cd35-2d0cc64ac64e.png)

「twitter @okasanman2 は本物です。」というメッセージを岡三マンしか知り得ない秘密鍵で署名しました。
これをインターネットで公開することで、誰でもメッセージの作成者が岡三マンであることが確認できます。


では検証してみましょう。公表されたメッセージを「署名済みメッセージの検証」画面に入力して検証ボタンを押すだけです。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/29187/98725c9b-1999-9e3b-3e54-315a5cf9260a.png)

このように出力されました。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/29187/f6e1239c-5c96-9b2f-f4fe-2b4527f9a73b.png)

つまりアカウントNBZNQL2JDWTGUAW237PXV4SSXSPORY43GUSWGSB7（今回は私です）の持ち主によって、このメッセージが作成されたことをNEMが証明してくれました。ただし、この英数字の羅列だけではフォロワーに覚えておいてもらうのは非常に困難です。なので、twitterアカウントと同じ名前のNamespaceを取得（レンタル）しておくことをお勧めします。そして取得したことをTwitter上で公表するだけです。用心深い人はそのツイートをオープンアポスティーユしておいてもいいかもしれません。

そうしておけば、アカウントエクスプローラーから以下のように確認できます。（今回は私のネームスペースxembookです）

![image.png](https://qiita-image-store.s3.amazonaws.com/0/29187/be2e328d-fcbc-a09d-b62e-16b9e8a7c60a.png)

はい、これでxembookの持ち主がNBZNQL2JDWTGUAW237PXV4SSXSPORY43GUSWGSB7であることがNEMによって証明されました。
このようにすれば、岡三マンは現在凍結されたアカウントを捨てて、次に新しいアカウントを作成しても同一人物であることが簡単に証明できます。

ただ、事前にネームスペースを取って公表しておけば、の話でした。残念。
これは岡三マンに限った話ではありません。多くのフォロワーを持つアカウントの方は是非、NEMのネームスペース所有をご検討ください。

# その他参考
[ブロックチェーンで本人を証明するということ - Qiita](https://qiita.com/nem_takanobu/items/96f989c6bba5d9b1aaac)
CSSのFlexboxでグリッドレイアウトを実現した！

# Flexboxでグリッドレイアウト

カスタムプロパティの`--all-margin`と`--num-column`の値を変えると、それぞれのボックスのmarginと、横に並べるボックスの数を調整できます。

<p class="codepen" data-height="265" data-theme-id="0" data-default-tab="css,result" data-user="omj" data-slug-hash="VgYXzx" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid black; margin: 1em 0; padding: 1em;" data-pen-title="VgYXzx">
  <span>See the Pen <a href="https://codepen.io/omj/pen/VgYXzx/">
  VgYXzx</a> by Ordermade Japan Inc. (<a href="https://codepen.io/omj">@omj</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

# ボックスの幅の指定方法

一個のボックスに注目してみていきましょう。

```css
.grid2x3 > div {
  display: flex;
  justify-content: center;
  align-items: center;
  
  width: calc(100% / var(--num-column) - 2 * var(--all-margin));
  margin: var(--all-margin); 
}
```

ここでのポイントは、1つのボックスのwidthを設定する時に、**100%を横に並べるボックスの数で割る**ことで横方向の均等を実現しています。

さらに、**ボックスの両端のmarginを引く**ことで厳密なwidthを計算しています。

# ボックスをまとめるdiv

次に、ボックスをひとまとめにしているdivのスタイルをみてみます。

```css
.grid2x3 {
  color: white;
  height: 100%;
  display: flex;
  flex-wrap: wrap;
  margin: var(--all-margin);
}
```

`flex-wrap`プロパティを`wrap`にすることで折り返しできるようにします。また、`heigth`を100%にするのを忘れないように。

最後に両端のmarginを揃えます。これがないと、それぞれのボックスは均等配置だけれど、その全体の両端だけmarginが半分になってしまいます。

その問題を解消するため、**ボックスに適用されているmarginと同じ値を外側のdivに適用**させます。


あとはhtmlとbodyに`height: 100%`を適用すれば完成です。

```css
html, body {
  height: 100%;
  margin: 0;
}
```

# 参考

https://codepen.io/fabiobiondi/pen/PqeWGv

# まとめ

Flexboxとても便利です。floatを使っていた頃の自分が嘘みたいです。CSSを考えて設計するのもとても楽しいですね。
PHPのstdClassオブジェクトのプロパティを確認したいが、メモリ不足でエラーになる場合

通常は、`get_object_vars($obj)`でオブジェクトの中身が配列で取得できる。

問題は、これで下記のようなエラーが発生する場合。

```
Allowed memory size of xxxxxxxxx bytes exhausted
```

そんなときは、下記でできるかも。（自分はできた。）

```
json_decode(json_encode($obj), true)
```

参考: https://stackoverflow.com/a/18576902/1646699
上手くいきません。

以下のプログラムを作成しても、２８、４０、４７行にエラーが出ます。
如何すればよいのでしょう。

from tkinter import *

tile_size  = 20

def load_map_data():
     from maze_data import data
     return data

def draw_map(cv, data):
    rows = len(data)
    cols = len(data[0])
    for y in range(rows):
        y1 = y * tile_size
        y2 = y1 + tile_size
    for x in range(cols):
        x1 = x * tile_size
        x2 = x1 + tile_size

        p = data[y][x]

        if p == 0: color = "white"
        if p == 0: color = "#404040"
        if p == 0: color = "red"
        if p == 0: color = "blue"

        cv.create_rectangle(
            x1, y1, x2, y2,
            fill=color,
            outline="black", width=2)

def create_window(map_data, events = []):
        win = Tk()
        win.title("迷路")
        rows = len(map_data)
        cols = len(map_data[0])
        cv = Canvas(win,
                    width=(cols * tile_size),
                    height=(rows * tile_size))
        cv.pack()
        draw_map(cv, map_data)

        for func in events: func(cv, map_data)
        win.mainloop()

if __name__ == "__main__":
    map_data = load_map_data()
    create_window(map_data)
Railsで遷移元のURLを取得する方法

# きっかけ
遷移前のページのURLによってページに表示するリンク先を分岐させる処理がしたかった。
その際に遷移前のページのURL取得方法を調べたのでメモ。

# request.referer
遷移前のURLを取得するにはrequest.refererを使う。

```ruby
<% if request.referer&.include?("/*******/**/") %>
  #前のページが指定したURLだった場合のリンク先
<% else %>
  #その他のリンク先
<% end %>
```

今回はこんな感じで使用しました。

# 参考
http://railsdoc.com/references/request
https://triple-underscore.github.io/RFC7231-ja.html#section-5.5.2
WordPressサイトのCSSをChromeのデベロッパーツールで編集する方法

# やりたいこと
* WordPressサイトの特定箇所の表示を変更したい。
* 具体的には、`記事詳細画面の下部に表示される「関連記事」`。
* これをChromeのデベロッパーツール機能で非表示にする。

# 原因
* CSSの優先度の問題
  ※importantは使わない(なんとなく)

# 参考URL
[Chromeのデベロッパーツールで変更を保存する方法](https://qiita.com/tokeikun/items/14513b019e66c2410296)
[Web開発でよく使う、特に使えるChromeデベロッパー・ツールの機能](https://www.buildinsider.net/web/chromedevtools/01)

## 解決した方法
1. デベロッパーツールを起動
　  該当サイトをChromeで開き、デベロッパーツールを起動します（Windowsは[F12]キー)
2. 虫眼鏡アイコン（□+↖）を押す
デベロッパーツールの上部に表示されているタブメニュー(パネル)の、
　 左上にある`虫眼鏡アイコン`（□+↖）を押す
3. サイト画面の要素を選択できる
　  虫眼鏡アイコンの下に「Select an element in the page to inspect it Ctrl + Shift + C」という
 　メッセージが表示され、CSSを編集したい要素を選択できるようになる
4. CSSを編集したい要素を選択
     ElementsパネルのDOMエレメントツリー内から、該当のエレメント要素が選択
　  選択された要素のCSS情報などがサイドバー(Styles)に示される
5. サイドバー(Styles)からCSSを編集
　  該当箇所のスタイル情報から、CSS優先度の高いコードを無効化(レ点チェックを外す）
6. 編集内容を保存する
　 Sourcesパネルからファイルを右クリックし「save as」を選択。
　元のファイルを選択して上書き保存。

# おわり
CSS入門1~書式~

##CSSとは##
**Cascading Style Sheets**でCSSです.
Webページの見た目を整えるやつです.

##書式##
基本的な書式は以下の通りです

```css

セレクタ{
　　　　　プロパティ: 値
         このカギ括弧の中をスタイルっていいます
}

p {
   background-color: #000000;
}

```

セレクタには次の種類があります

###タイプセレクタ###
html内の,同じタグ名の要素を全て選択します.

```html
<h1>hogehoge</h1>
<p>コンテンツ</p>
<p>コンテンツ2</p>
```

例えばhtmlでこう書いてあるとします.
CSSで`p {スタイル}`って書くと全ての\<p>タグに作用します.

###全称セレクタ###
全ての要素(タグとコンテンツ)が選択される.
`* {スタイル}`こうかくの.

###classセレクタ###
セレクタで指定した｢クラス名｣と同じclass属性を持つ要素全て選択.
書き方は,**ドットに続けて選択したい要素のクラス名をかく**
とりあえず下をご覧ください.

```html
<h1 class="title">コンテンツ1</h1>
<p> コンテンツ</p>
<p> コンテンツ</p>
<h1 class="title">コンテンツ2</h1>
<p>コンテンツ</p>
<p>コンテンツ</p>
```

htmlがこう書いてあって,"title"の部分をしていしたいとします.
そしたらこうです.`.title {スタイル}`

###idセレクタ###
セレクタで指定した｢ID名｣と同じid属性を持つ要素を選択します.
id属性につけるID名は1つのhtml内で一度しか使えないので,idセレクタで選択できる要素は1つだけ,となります.
書き方はこうです.

`#title {スタイル}`↔`<h1 id="title">コンテンツ</h1>`

###疑似クラス###
ある要素が特定の状態にあるときだけ選択するセレクタ.
例えばリンクにマウスポインタが｢乗っている状態｣,｢押された状態｣などです.
下のような感じで書きます.
`a:hover{スタイル}`↔`<a href="http://///"></a>`

###子孫セレクタ###
｢\<ul>~\</ul>のなかにある\<li>要素｣や｢\<table clas="info">\</table>の中にある<td>要素｣など,特定の親要素に含まれる子要素などを指定するのが子孫セレクタです.

```html
<nav class="navi">
  <ul>
    <li>ナビ1</li>
    <li>ナビ2</li>
    <li>ナビ3</li>
    <li>ナビ4</li>
  </ul>
</nav>

```

htmlがこうだったとします."navi"の\<li>タグにだけ適用したときはこうかきます.
`.navi li {スタイル}`

###複数のセレクタを1つのセレクタに割り当てる###
これはカンマで区切るだけ

`th, td{スタイル}`

##色や長さの指定##
| 単位       |説明 |
|:-----------------|:------------------|
|px|ピクセル。画面を構成する点1つ分|
|pt|ポイント。1pt=1/72インチ|
|cm|センチメートル| 
|vw|ビューポイントの幅。画面全体の幅を100とした値|
|vh|ビューポイントの高さ。画面全体の高さを100とした値|
|em|｢m｣のサイズ。フォントの｢m｣の大きさの何倍かで指定|
|% |パーセント。親要素のサイズの何パーセント分かで指定|

##参考サイト##
[マージンとかの参考](https://saruwakakun.com/html-css/basic/margin-padding)
PwC’s サイバーセキュリティ体験イベント

## 概要
サイバー攻撃は、デジタルビジネスの発展に伴い、複雑化・多様化しており、グローバルで社会的な課題となっています。
本イベントは、PwCのコンサルタントがクライアント向けに提供している、サイバー攻撃を疑似体験する対戦型ゲーム「Game of Threats™」を用い、サイバーセキュリティの重要性、企業のリスク感覚を体感するイベントです。是非、ご参加ください！

### 「Game of Threats™」とは・・

PwCが、CEOやCIO、CISO（最高情報セキュリティ責任者）などの経営層に対して
提供している、サイバー攻撃の脅威を疑似体験できる対戦型ゲームです。
本プログラムを通じて、世界の経営者が経営課題の一つとして捉えている
サイバーセキュリティの重要性を体感頂けます。

[![動画を開くことができませんでした。](https://i.ytimg.com/vi/qAfwCEHLxko/mqdefault.jpg)](https://youtu.be/qAfwCEHLxko)


2016年、学生の皆さま向けに開催したイベントでは、「サイバーセキュリティの難しさ、奥深さ、楽しさが分かり、今後も学びたいと感じた。」「学生にとって情報を守ることを意識する機会が少ないので、とても貴重な経験であった。」「企業の限られた予算の中で見えない敵の攻撃を防ぐことの難しさを体感できた。」など非常に高い評判を頂いています。

※株式会社マイナビの運営する大学生向けキャリアサイトに掲載頂いております。
https://mycampus.jp/2017/05/29/pwc2016_report/

### こんな人にぜひ！
- 社会的に重要な課題の解決に取り組みたい人
- サイバーセキュリティに興味がある人
- テクノロジーに興味がある人
- サイバーセキュリティの仕事を体験してみたい人
- ご自身のテクニカルスキルを試してみたい人

## 実施詳細
- 実施日時：2019年2月14日(木)18:00-21:00 
    - 第一部、第二部、ご都合よい方をお選びください。
    - 座談会は自由参加ですので、途中退席、途中参加が可能です。
    - 第一部：
        - 18:00-19:30（Game of Threats体験）
        - 19:30-21:00（先輩社員との座談会）
    - 第二部：
        - 18:00-19:30（先輩社員との座談会）
        - 19:30-21:00（Game of Threats体験）
- 実施場所：PwCコンサルティング合同会社　丸の内パークビルディング21F（※1）
- 服装：自由（※2）
- 定員：第一部、第二部、それぞれ約10名（※3）
- 参加資格：2020年3月卒業/修了見込みかつ2020年4月/10月入社が可能な方
- 応募方法：当社採用ウェブサイトのマイページに登録後、イベントにエントリー
    - 当社採用ウェブサイトURL：http://bit.ly/2GeIvIS
- 応募締切：2019年2月8日(金)23:59

（※1）交通費の支給はございませんので、予めご了承ください。 
（※2）ぜひカジュアルな服装でお越しください。
（※3）応募者多数の場合、抽選となります、予めご了承ください。
CentOS 6 への fail2ban のインストールで大ハマリした件

2019年になってもなお CentOS 6 のインスタンスを立てる必要があり、
昔は denyhosts 大活躍だったが、どうも行方不明らしいので
fail2ban を導入しようと思って数時間格闘する羽目に。

# 結論

**タイムゾーンを変更したら再起動しておきましょう**

いや、俺やったはずだけど?!と思ったものの、
その記憶もなかなかにぁゃιぃのと、
もしかしたら何かインストールした関係で・・?
という気もしつつ、できることなら明示的に再起動しておいたほうが無難。

# そこにたどり着いた経緯

先にCentOS 7 なインスタンスに fail2ban をインストールし、
まぁ普通に設定したら普通に動いたぜーやったー
さぁ次は CentOS 6 だ iptables だぜー
とノリノリで作業を進めるも、
/var/log/secure にもりもり追記されるsshdへの不正ログインに対し
fail2ban が反応してる気配が1ミリもない。

fail2ban-client status sshd
で状況を確認しても、ちゃんと設定は読み込まれてるし
ログファイルへの Path も正しいにも関わらず、である。

そこからネットの海を徘徊すること数時間、
ときには英語圏だけでなく何語なのか自信がないようなフォーラムまでうろうろしながら
あれやこれやを試していて気づいたこと。

fail2ban.conf の loglevel を DEBUG にして tailf してたら、
どうも /var/log/secure への追記のタイミングでいろいろ吐き出してるので、
ログファイルの監視自体はできてるものの、
fail2ban.**datedetector** という処理しか流れてこない。

・・・そういやさっきどっかのフォーラムでタイムゾーンがどうこう言ってるやついたな・・・

と、念のため再起動したところ、それまでのあれこれがなんだったのか、
というくらいサクッと動いてくれていた。

※※※※※※※※※※※※※※※

もっとちゃんとした記事じゃないと
Qiitaには書いても意味ないかなーと思っていたが、
若者や初心者と話すと「あ、こんなこともtipsになるんだ」
ということがちょいちょいあるので、
雑記的なものも書いていくようにする。
LinuxでTCPのListenポートを指定してプロセスをKILLするワンライナー

Linux上でVSCodeでGolangのWebアプリケーションを開発しているが、
デバッグ実行中、goroutine内でPanicが起きた時などに、
VSCode上でデバッグを中止してもListenしているプロセスが死なず、
再度実行しようとした時にポートが塞がっていて実行できない現象が起きる。

killすればいいだけだが、netstatでプロセスIDを調べて、そのIDを渡す…というのが面倒。
もっと直感的な「TCP何番をListenしているプロセスをKILLする」というコマンドを打ちたい。

これでいける。

```bash
lsof -t -i:《ポート番号》 -sTCP:LISTEN | xargs -r kill
```
※対象となるプロセスが無い場合にkillのUsageが出ないように、xargsのオプションに-rを追記しました。
　 @angel_p_57 さんコメントありがとうございます。

~~プロセスが無いとkillコマンドのUsageが出るが気にしないことにする。~~
~~気になる人はこうでもすればいいのでは。~~

```bash
lsof -t -i:《ポート番号》 -sTCP:LISTEN | xargs kill 2> /dev/null
```
Tabpy接続(Tableau+Python)をした日

**簡単な計算から統計処理、機械学習プログラムまでをTableauでビジュアライズする法**

##公式からanacondaインストーラをダウンロード
https://www.anaconda.com/download/#macos

###仮想環境を爆誕させる
<pre>$ conda create -n Tableau-Python-Server python=3.X anaconda</pre>
*Xは適宜

###仮想環境を有効化する
<pre>$ source activate Tableau-Python-Server</pre>

###Tabpyパッケージを入れる
<pre>$ conda install -c pkgs/free tabpy-server</pre>

ここ↓に入ってた
<pre>anaconda3/envs/Tableau-Python-Server/lib/python3.6/site-packages/tabpy_server/</pre>

中にあるファイルstartup.sh(winの方はstartup.bat)を実行

<pre>$ sh startup.sh</pre>

<pre>Web service listening on port 9004</pre>
になってたらOK。

#Tableau側の設定
・Tableau起動
・ヘルプ→設定とパフォーマンス→外部サービス接続の管理 を選択
・ポート:9004に設定

・[テスト接続]をクリック
![スクリーンショット 2019-01-23 14.42.54.png](https://qiita-image-store.s3.amazonaws.com/0/83884/847161af-358d-d3a1-c6d9-1fac1d801eba.png)




###success!



##参考
・[TabPy入門【環境構築】]
(https://tableau-i-ways.com/column/tabpy_construction)
・[alexloth.com]
(http://alexloth.com/2016/11/06/tabpy-tutorial-integrating-python-tableau-advanced-analytics/)
・[外部サービスに式を渡す(公式ヘルプ)]
(https://onlinehelp.tableau.com/current/pro/desktop/ja-jp/r_connection_manage.htm)
・[The endpoint you're trying to query did not respond. Please make sure the endpoint exists and the correct set of arguments are provided.] 
(https://github.com/tableau/TabPy/issues/44)
・[Building advanced analytics applications with TabPy]
(https://www.tableau.com/about/blog/2017/1/building-advanced-analytics-applications-tabpy-64916)
・[TableauとPythonの連携環境(TabPy)構築]
(https://datadesigner.jp/tabpy-setup)
Nuxtのmiddleware上でのredirectには注意

# 現象

Nuxtで認証チェックを行う時に、middlewareを使うことがあると思います。

```js:middleware
export default function ({ store, redirect, route }) {
  let token = sessionStorage.getItem('kintai-token');
    if (token) {
      return Promise.resolve()
    } else {
      redirect('/signin')
    }
}
```

リダイレクトした際、一瞬だけlayoutが適用される前のページが表示されてしまいます。

# 解決方法

リダイレクトの方法を変更します。

```js:middleware
export default function ({ store, redirect, route }) {
  let token = sessionStorage.getItem('kintai-token');
    if (token) {
      return Promise.resolve()
    } else {
      window.location.href = '/signin'
      return new Promise((resolve) => {
　　　　　　　　　　　　　　　　// 完全にレンダリングされたらリダイレクトされる
      })
    }
}
```
Cloud FunctionsでGo言語を使ってCloud Pub/Subから値を取得する

#本記事の内容
Cloud FunctionsでGo言語を使用してCloud Pub/Subへ送信されたデータを受信します。

##動機
2019/1/17にCloud Functions環境での[Go言語のサポートが発表](https://cloud.google.com/blog/products/application-development/cloud-functions-go-1-11-is-now-a-supported-language)された。リンク先にはCloud Pub/Subからデータを拾うサンプルがなかったので、実際に作成してみました。

#開発環境

プロジェクトに付属しているCloud Shellを使用します。
まずはバージョンの確認です。

```console
$ go version
go version go1.11 linux/amd64
```

次はgcloudツールのアップデートとベータ版ツールのインストールをします。

```console
# gcloud components update && gcloud components install beta
```

#実装
##Pub/Subトピックの作成
まずはCloud Pub/Subでトピックを作成します。

```console
$ gcloud pubsub topics create YOUR_TOPIC
Created topic [projects/YOUR_PROJECT/topics/YOUR_TOPIC].
```
「APIを有効にしてください」的なエラーが出た場合はWEBのコンソールより有効にしてください。

##Cloud Functionsへのデプロイ
ソース格納用のディレクトリ作成します。

```console
$ mkdir testpubsub && cd testpubsub
```

Goの実装をします。

```hello_from_pubsub.go
package sample

import (
    "context"
    "log"
    "encoding/json"
)
//Pub/Subからのメッセージは"data"にバイト列で入るらしい。構造体のバインドをもっと上手に
//やる方法を模索中
type PubSubMessage struct {
    Data []byte `json:"data"`
}
type Info struct {
    Name  string `json:"name"`
    Place string `json:"place"`
}
// Pub/Subからメッセージを受診したらこの関数が起動する
func HelloFromPubSub(ctx context.Context, m PubSubMessage) error {
    var i Info

    err := json.Unmarshal(m.Data, &i)

    if err != nil {
        log.Printf("Error:%T message: %v", err, err)
        return nil
    }

    log.Printf("こんにちは、%sさん！%sへCloud Pub/SubからFunctions経由で愛をこめて。", i.Name, i.Place)
    return nil
}
```
main関数をGCPで実行する感じで実装していけます。Pub/SubからはJSON形式のテキストメッセージが来ると想定して実装しています。

実装が終わったらデプロイしていきます。

```console
$ gcloud functions deploy HelloFromPubSub --runtime go111 --trigger-topic YOUR_TOPIC
・・・（省略）
status: ACTIVE
・・・（省略）
```
「APIを有効にしてください」的なエラーが出た場合はWEBのコンソールより有効にしてください。
status: ACTIVEと出て入れば準備オッケーです。だいたい2分くらいでデプロイが完了します。
お次はPub/Subへメッセージを投げてみましょう。

```console
$ gcloud pubsub topics publish YOUR_TOPIC --message '{"name":"YOUR_TOPIC" ,"place":"YOUR_PLACE"}'
messageIds:
- 'XXXXXXXXXXXXXXX'
```

最後にログメッセージを確認します。

```console
$ gcloud functions logs read --limit 50
LEVEL  NAME             EXECUTION_ID     TIME_UTC                 LOG
D      HelloFromPubSub  365626939064089  2019-01-23 07:57:47.764  Function execution started
       HelloFromPubSub  365626939064089  2019-01-23 07:57:47.765  2019/01/23 07:57:47 こんにちは、YOUR_NAMEさん！YOUT_PLACEへCloud Pub/SubからFunctions経由で愛をこめて。
D      HelloFromPubSub  365626939064089  2019-01-23 07:57:47.766  Function execution took 2 ms, finished with status: 'ok'
```
出力できていることが確認できました。

#所感
デプロイがものすごく早くてびっくりしました。思っていたより簡単に作成できます。
ログを見る限りPub/Subからメッセージを拾ってくるだけであれば0.02秒で完了しています。

Goとは関係なくCloud Funtionsの特徴ですが、Pub/Subにメッセージが無い時はリソースが消費されないのが素晴らしいですね。今までは定期的にメッセージが無いか確認をしていたのでどうしてもコンピューティングリソースが発生し課金を行っていました。Dataflowで作っている一部の処理をCloud Functionsへ移管を検討したいと思いました。