OmniAICreator/Qiita-1.07M huggingface dataset:
webpackでファイルの出力先を複数指定する

webpackでファイルをbundleする際に、サーバー側のファイルとクライアント側のファイルを別々に指定したくて、色々模索したのですが、ある方法に落ち着いたので、備忘録として残します。

以下のディレクトリ構造で

```
src/
    - client.js
    - server.js
package.json
webpack.config.js
```

以下のように出力したい

```
dist/
  - server.bundle.js
public/
  - client.bundle.js
src/
  - client.js
  - server.js
package.json
webpack.config.js
```

この場合のwebpack.config.jsについて、以下のように解決いたしました。

```js

const path = require('path')
const nodeExternals = require('webpack-node-externals')

const env = process.env.NODE_ENV || 'development'
const isDevelopment = env === 'development'

module.exports = {
  mode: env,
  target: 'node',
  devtool: isDevelopment ? 'source-map' : false,
  entry: {
    'public/client': './src/client.ts',
    'dist/server': './src/server.ts',
  },
  output: {
    path: __dirname,
    filename: '[name].bundle.js',
  },
  // 以下省略
}
```

色々模索してみましたが、上記の方法が一番手軽なような気がしました。

もしもっと簡単な方法があったり、より良い方法があったら知りたい。。。
中の人がいないYoutuberを作ってみた

まずは以下の動画をご覧ください。

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">YoutubeLiveのコメントに反応して返答するたぶん世界初のコミュニケーション系中の人がいないAI？Youtuber（モデルは結月ゆかり）作ってみた。<a href="https://twitter.com/hashtag/Youtuber?src=hash&amp;ref_src=twsrc%5Etfw">#Youtuber</a><a href="https://twitter.com/hashtag/%E3%83%A6%E3%83%BC%E3%83%81%E3%83%A5%E3%83%BC%E3%83%90%E3%83%BC?src=hash&amp;ref_src=twsrc%5Etfw">#ユーチューバー</a><a href="https://twitter.com/hashtag/%E7%B5%90%E6%9C%88%E3%82%86%E3%81%8B%E3%82%8A?src=hash&amp;ref_src=twsrc%5Etfw">#結月ゆかり</a><a href="https://twitter.com/hashtag/unity3d?src=hash&amp;ref_src=twsrc%5Etfw">#unity3d</a> <a href="https://t.co/MyKXHoagFX">pic.twitter.com/MyKXHoagFX</a></p>&mdash; カナタ (@kanatano_mirai) <a href="https://twitter.com/kanatano_mirai/status/1046382187430309888?ref_src=twsrc%5Etfw">2018年9月30日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

この動画は（たぶん世界初の）中の人がいないYoutuberです。[^1]

[^1]: 他に中の人がいないYoutuberを知っている方はご連絡していただけると嬉しいです。

僕もVTuberに興味はあるのですが・・・。
外見を女の子にしたところで中は変わらないんですよね。特に声。
ボイスチェンジャーアプリも試してみたんですけど僕の場合はどうしてもボイスチェンジャーを使った感が出てしまっていてダメでした。
今の技術では完全な女声変換はできなさそうです。
さらに僕は喋ることが得意ではないのです。

じゃあもう**中の人なくしちゃえばいいじゃん！**と思いました。
そうすれば声も喋りも動きもプログラムやAIの力で自動にすることで解決できますよね！？

僕は以前[Unityちゃんと会話できるアプリ](https://qiita.com/kanatano_mirai/items/ecb826b06c658bf350a8)を作成しました。
これはユーザーからの入力に応じてUnityちゃんが返答をするというものです。
そこで僕は考えました。

**このアプリの「入力」をYoutubeLiveのコメントに置き換えれば、あたかもキャラクターが生放送で意思を持ったようにリスナーのコメントを読んで会話できるのでは！？**
と思いました。
そして構想から2~3ヶ月ほどで動画で紹介した試作レベルのアプリが完成しました。

仕組みは上記のUnityちゃんと会話できるアプリとほぼ変わっていません。
入力をYoutubeLiveからのコメント、返答出力時にTextMeshProを使用したぐらいです。
以前作った仕組み説明の画像の一部を変えたものを載せておきます。
画像中のAPIはすべてdocomo提供のAPIです。

- [自然対話API](https://dev.smt.docomo.ne.jp/?p=docs.api.page&api_name=natural_dialogue&p_name=api_usage_scenario)
- [音声合成API](https://dev.smt.docomo.ne.jp/?p=docs.api.page&api_name=text_to_speech&p_name=api_usage_scenario)


![AIYoutuber.001.jpeg](https://qiita-image-store.s3.amazonaws.com/0/154305/f72c26e3-4c80-b8c1-bb61-e562f25fac97.jpeg)


まあこれをAIと名乗っていいのか、Youtuberと名乗っていいのかはわかりませんが。
たぶん大事なのは、**こういうことができるんだとわかる**ということだと思います。
これからどんどんAIが進化して、いつかは本物のAIがゲーム実況したりライブしたりする時代も来るかもしれませんね。

# お世話になった記事🙇
[YouTubeLiveの配信コメントをUnityで利用する](https://qiita.com/n0mimono/items/0c2921c9b964cd305fe3)

[UnityでYouTubeLiveのAPIを叩いてUIに変数を表示する](https://qiita.com/platoronical/items/cd793e54a50274f2ff1f)

上記2つの記事がなければアプリは作れませんでした。本当にありがとうございました！

[TextMeshProの使い方]
(https://qiita.com/hinagawa/items/b606c6a2fd56d559a375)

TextMeshProを使う際に大変参考になりました！
Progateでいろんな言語の勉強を始めてみた

プログラミングの勉強をしたいと思い無料サイトを検索。

そもそもはrubyを習得したいと思って「効果的な独習方法」で検索していた。
初級～中級であれば「Progate」の独習サイトがオススメとあったので
早速　使ってみた。

まず最初に思ったこと
「・・か、かわいい　キャラクターがかわいいｶﾜ(・∀・)ｲｲ!!」
ペパーミントグリーンがCPカラーらしく優しい。

そして「にんじゃわんこ」というキャラクター！
![wanko.jpg](https://qiita-image-store.s3.amazonaws.com/0/297938/f391e21b-e026-のd502-1960-1b3289b6908e.jpeg)


ダ、ダメだ！　かわいいもの好きのおばちゃんは、もうこのサイトから離れられない！
学習状況はLVで確認できるし、数値化されるとモチベーションも上がる！(・∀・)ｲｲﾈ!!

目標はrubyの独習だったのですが、あまりの手軽さにHTML、CSS、PHP　
あとコマンドもついでに学習！

課題は簡単に感じてしまうのですが、HTMLの「道場」は初級編から
なかなかの難問！

だけど、ここを乗り越えると自分一人でなかなかのサイトは作れそうです。
（仕様書をもとに画面レイアウトを行う、これが難しいです！）

目標はrubyでwebサイトを作れるようになることです。
ruby on Railsの学習もできるし、何よりも統合開発環境を体験できるのはうれしい。
独学だと、最初に環境を構築する時点で時間がかかったり、挫折したりするので
いきなりソースを書くところから開始できるのも初心者には嬉しい！

私のように「他言語の経験はあるけど、WEBプログラミングをマスターしたい！」
という人にもオススメの学習サイトだと思いました。
★注意！★　ただし月980円の有料会員でないと深くは勉強できません。

さあ！HTML道場の続きをやろうっと！
f2: sms - imessage 連携テスト

.
### **[ オーダー ]**

iphone A が 機内モード (オフライン) 時、
iphone B + wifi のみ (sim なし) に対し、
imessage ( sms ) を自動転送してくれるか検証

### **[ 仕込 ]**

A - B にて 同 apple ID でログイン
A -> B の自動転送 設定

### **[ 調理 ]**

0. android C から A に sms 発信

### **[ 食感1 ( 結果1 ) ]**

1. A 電話回線 ON + データ通信(LTE) ON

-> B も同 sms の同時受信を確認

### **[ 食感2 ( 結果2 ) ]**

2. A 電話回線 OFF + データ通信(LTE) OFF

-> B 無反応

### **[ 食感3 ( 結果3 ) ]**

3. A 電話回線 ON + データ通信(LTE) OFF

-> A sms 受信 -> B 無反応

### **[ 食感4 ( 結果4 ) ]**

4. A 電話回線 ON + データ通信(LTE) ON

-> B 同 sms 受信 (結果1 と同じ)

### **[ デザート ( まとめ ) ]**

結果的に、まぁ当たり前という感じではあるけれど
A オフライン時 は A が SMS を受信しないので 
B には 転送するものがない

改めて、以下の再認識ができた感じ

SMS: 電話回線がオンラインにならないと受信とされない仕組み

icloud: データ回線オンライン条件下で機能する
(自動転送というより紐付け 両ID に同時発信的イメージ)

A を家に置きっ放しで、B のみでお出かけするなら
A を 常時 電話回線 ON + データ通信(LTE) ON
にしないと、B にも sms 自動転送してあげないよ、
A の電池が切れて困るなら A の充電もしっぱなしでね！

という感じに

ローカル依存自動転送はちょっと厳しいところがあるなぁ

.
CentOS7 に VirtualBox をインストールする

少々紛らわしいですがメモ

# 手順

1. あらかじめ依存ライブラリをインストールする

    ```
    # yum -y install libGL libICE libSM libX11-xcb libX11 libXcursor libXext libXinerama libXmu libXrender libXt libvpx libxcb SDL SDL-devel alsa-lib alsa-lib-devel fontconfig fontconfig-devel opus opus-devel libpng libpng-devel kernel-devel
    ```

2. VirtualBox の RPM パッケージをダウンロードする

    ```
   # curl -LkvOf https://download.virtualbox.org/virtualbox/5.2.18/VirtualBox-5.2-5.2.18_124319_el7-1.x86_64.rpm 
    ```

3. VirtualBox の公開鍵をダウンロードする

    ```
    # curl -LkvOf https://www.virtualbox.org/download/oracle_vbox.asc
    ```

4. 公開鍵をインポートする

    ```
    # rpm --import oracle_vbox.asc
    ```

5. インポートした公開鍵でチェックする

    ```
    # rpm --checksig VirtualBox-5.2-5.2.18_124319_el7-1.x86_64.rpm
    ```

6. ダウンロードした RPM パッケージをインストールする

    ```
    # rpm -ivh VirtualBox-5.2-5.2.18_124319_el7-1.x86_64.rpm
    ```

6. VirtualBox を使用するユーザを vboxusers グループに追加する

    ```
    # usermod -a -G vboxusers <VirtualBoxを使用するユーザ名>
    ```

# 参考

- [VirtualBox 公式サイト - Download VirtualBox for Linux Hosts](https://www.virtualbox.org/wiki/Linux_Downloads)
- [CentOS 公式サイト - Installing and using VirtualBox on CentOS](https://wiki.centos.org/HowTos/Virtualization/VirtualBox)
pomera DM200のLinuxでリモートデスクトップ

#概要
pomera DM200にインストールしたLinuxでリモートデスクトップ(RDP)を使うためのメモ

Windowsマシンをリモート操作できるようになるとpomeraできることの範囲が更に広がります、また、往年のVaio P(Type P)を使っているような気分が味わえます(謎

#インストール
Linuxで現在よく使われているRDPクライアントはRemminaのようですが、pomeraにインストールするのは面倒そうなので代わりにrdesktopをインストールします。

```
$sudo apt install rdesktop
```

このソフトですが、インストールしてもアプリケーションメニューには追加されません、しかも設定用のGUIすらありません。じゃあどうするのかというとコマンドラインからオプション付きで起動します。

```
$rdesktop -u [ユーザー名] -f [ホスト]
```

-uでサーバーにログインするためのユーザー名を設定できます。設定しないとクライアント側でログインしているユーザー名になるようです。-fはフルスクリーンで起動するオプションです。pomeraは画面が狭いので素直にフルスクリーンにしましょう。

マウスを接続していない場合、keynav等でマウスエミュレーションしているかと思いますが、rdesktop起動中はキーを横取りされて使えないので、サーバー側にも同等のソフトをインストールする必要があります。

#サーバーにマウスエミュレーションソフトをインストール(Windows)
Windowsには標準でマウスキーという機能がありますが、これはテンキーをマウス代わりにする機能なのでテンキーのないpomeraでは(多分)使えません。

同等のソフトはあまり選択肢がないのですが、自分は今のところ「[KBMouse](https://www.vector.co.jp/soft/winnt/util/se459162.html)」を使っています。

このソフトはキー設定はカスタマイズできるのですが設定画面のGUIの出来がイマイチ(設定画面ではエミュレーションは無効、キーボードのみで設定できる作りになっていない等々…)なのでpomera単体で設定するのはかなり難しい感じです。予め別のPCやサーバーを直接操作して設定しておく必要があると思ってください。
また、keynavとキーバインドを合わせたくなると思いますが、KBMouseは単一キーでオンオフを切り替える構造でkeynavはどうもオンオフは個別に割り当てる構造になっているため完全に一致させることは出来ません。

他に良さげなソフトがあれば切り替えたいのですが…

#気になるところ
KBMouseの問題なのかrdesktopの問題なのかわからないのですが、マウス移動ボタンを押しっぱなしで移動させようとするとマウスポインタが行ったり来たりする現象が起こります、実際の描画とポインタの移動が一致してない感じみえますが原因不明です、移動方向はちゃんとあっているので操作自体に問題はないのですが、気になる人はすごく気になると思います。

#終了方法
キーを横取りされているためキーボードでrdesktop本体を閉じる手段が限られています。

ひとつはターミナル切り替え(X上ではAlt+Ctrl+F1～)は横取りされず有効なので切り替えたターミナル上でkillできます、これはWindowsが操作不可能な場合の最後の手段にしたほういいと思います。

もうひとつはリモート操作中はWindowsの終了メニューに「切断」というのが追加されているのでWindowsメニューから終了→切断とするとリモートデスクトップが終了(本体のWindowsは起動したまま)し、rdesktopも閉じられます。通常はこちらを使うといいと思います。
[JavaScript] Dependency Injectionコンテナどれにしよう？

Azure FunctionsをJavaScriptで書いていました。
テストを書くにあたり、やっぱり依存先クラスを自前でnewしてるのは辛いなと思い、DIコンテナを探すことにしました。

# awilix

https://www.npmjs.com/package/awilix

こちら [nodejs, ES6 で dependency injection with awilix](https://qiita.com/sugiyasu-qr/items/3679d5041ee547ca28eb) で紹介されていました。


コンテナを作り

```js:
const container = awilix.createContainer({
  injectionMode: awilix.InjectionMode.PROXY
})
```

コンテナにクラスを登録し

```js:
container.register({
  userController: awilix.asClass(UserController)
})
```

コンテナからインスタンスを取り出して使います。

```js:
var userController = container.resolve('userController');
```

簡単です。

依存関係を直接書かなくても解決してくれるのかな？いいですね。


# InversifyJS

https://github.com/inversify/InversifyJS

こちら [InversifyJSで学ぶDI(Dependency Injection)](https://qiita.com/Quramy/items/e65ee58cf1fba589c81b) で紹介されていました。



クラスを修飾し

```js:
inversify.decorate(inversify.injectable(), Katana);
inversify.decorate(inversify.injectable(), Shuriken);
inversify.decorate(inversify.injectable(), Ninja);
```

依存関係を登録し（引数の0や1は、多分コンストラクタ引数の順番）

```js:
inversify.decorate(inversify.inject("Katana"), Ninja, 0);
inversify.decorate(inversify.inject("Shuriken"), Ninja, 1);
```

コンテナを作成し

```js:
var container = new inversify.Container();
```

型名とクラスを紐付け

```js:
container.bind("Ninja").to(Ninja);
container.bind("Katana").to(Katana);
container.bind("Shuriken").to(Shuriken);
```

コンテナからインスタンスを取り出して使います。


```js:
var ninja = container.get("Ninja");
```

だいぶ冗長ですね。安全のために仕方ないみたいです。
TypeScriptだとデコレータがあるのでよい感じです。

# bottlejs

https://github.com/young-steveo/bottlejs

こちら [bottlejsでDIを試す](https://qiita.com/jgs/items/551c6c992d538c1d35fa) で紹介されていました。

コンテナを作成し

```js:
var bottle = new Bottle();
```

コンテナに型名とクラスを登録し

```js:
bottle.service('Barley', Barley);
bottle.service('Hops', Hops);
bottle.service('Water', Water);
```

コンテナに依存関係を登録し

```js:
bottle.service('Beer', Beer, 'Barley', 'Hops', 'Water');
```

コンテナからインスタンスを取り出して使います。

```js:
var beer = bottle.container.Beer
```

シンプルですね。
コードサンプルがいい（笑）


# その他

npmjs探すといっぱいありますね。

* [Aurelia](https://aurelia.io/) のDIコンテナ部分
* [Deep Framework](https://github.com/MitocGroup/deep-framework) のDIコンテナ部分

# 結論

DIといいながら、単にクラスへの直接依存からコンテナへの依存に切り替わっているだけなのは、JavaScript故に仕方がないことかしらん。

それなりのチームであればinversifyJS、少人数and/of小規模ならawilixかbottlejsがよさそうです。

ところで、探してはみたけれど、Azure FunctionsでDIコンテナ使うのはごしたい感じ。
Eclipseでctrl + のショートカットが利かない場合welcomeをdisableにすると治る場合がある

タイトルで終わってるんだけど……

Eclipse 4.9で```Ctrl + ```のショートカットキーが利かなくなった。で、下のリンクに書いてあるようにwelocomをdisable（右下にあるチェックボックスを外す）にしたら治った。

https://stackoverflow.com/questions/18341928/eclipse-ctrlright-does-nothing
BigQueryで「日付別テーブル」や「テーブルパーティショニング」を活用して大規模データを扱う

# 概要
BigQueryで大量のデータを扱う場合、[日付別テーブル](https://cloud.google.com/bigquery/docs/partitioned-tables?authuser=0&hl=ja#partitioning_versus_sharding)もしくは[パーティショニングされたテーブル](https://cloud.google.com/bigquery/docs/partitioned-tables?authuser=0&hl=ja)を利用すると便利です。

これらのテーブルの種類についてはドキュメントにもありますが、いくつかのページにまたがっていたりして見つけるのに苦労しているので、ここにまとめておきます。

# ３つの選択肢
**「日付別テーブル」**、**「取り込み時間で分割されたテーブル」**、**「分割テーブル」**の３つの選択肢があります。

後ろの２つがパーティショニングされたテーブルです。テーブルとしては１つですが内部的に分割（パーティションニング）されていて、実行するクエリで読み込むパーティションを指定することでスキャン対象のデータサイズを減らすことができ、パフォーマンスとコストを向上させることができます。

「日付別テーブル」は、その名の通り日付ごとに作成した複数のテーブルです。命名規則に従ったテーブル名をつけることで、Webコンソール上でも複数テーブルが集約して表示されます。また、BigQueryには[ワイルドカードテーブル](https://cloud.google.com/bigquery/docs/reference/standard-sql/wildcard-table-reference)という機能があり、複数のテーブルをクエリ上でまとめて扱うことができます。

それぞれのタイプについて説明していきます。

## テーブルパーティショニングを使わない
### 日付別テーブル
テーブル名に"_yyyymmdd"のサフィックスを付けるだけです。この方法は、「シャーディング」とも呼ばれます。

例として`foo_yyyymmdd`のテーブルを作りました。各テーブルのフィールド定義は一致している必要はありません。
<img width="1066" alt="スクリーンショット 2018-10-07 17.26.05.png" src="https://qiita-image-store.s3.amazonaws.com/0/115524/ae3b6230-0c1f-ab04-2327-3de0bb7355b5.png">

このテーブルタイプのメリットは、単純さです。例えば日次バッジでテーブルの作成を行なっているとき、エラーなどでデータが中途半端な状態になったとしても、テーブルを削除してバッジ処理をやり直すことができます。
テーブル同士のフィールドの一貫性を気にしなくてもいいことも、時には便利でしょう。

デメリットとしては、パーティションされたテーブルに比べて、[クエリのパフォーマンスが低くなります。](https://cloud.google.com/bigquery/docs/partitioned-tables?authuser=0&hl=ja#partitioning_versus_sharding)

日付別テーブルは、後で紹介する[取り込み時間で分割されたテーブルへの変換](https://cloud.google.com/bigquery/docs/creating-partitioned-tables?authuser=0&hl=ja#converting_date-sharded_tables_into_ingestion-time_partitioned_tables)も可能です。

## パーティショニングを使う
「取り込み時間で分割されたテーブル」と「分割テーブル」の２種類があります。後者の方が新しい機能で便利だと思うのですが、一応いずれも紹介します。

### 取り込み時間で分割されたテーブル
BigQueryがデータを取り込む日にちでパーティションが作成されます。（１日１パーティション）　テーブル作成時に、「取り込む時間により分割」を指定します。（Webコンソールの場合）


クエリでパーティションを限定することでクエリのパフォーマンスが向上し、読み込むデータサイズも減少することからコスト（お金）も下がります。

例です。

```sql
SELECT * 
  FROM `project.dataset.table` 
 WHERE _PARTITIONTIME >= TIMESTAMP("2018-10-07")
```

`_PARTITIONTIME`は自動で生成される擬似列で、テーブルに読み込まれたデータの日付ベースのタイムスタンプ（UTC）がこの列に格納されています。これを使ってデータを読み込むパーティションを指定します。

デフォルトではデータが入れられた日のパーティションにデータが入りますが、パーティションを指定することもできます。`テーブル名$yyyymmdd`を指定してデータをロードします。

### 分割テーブル

「取り込み時間で分割されたテーブル」ではデータを格納するパーティションの選択に`_PARTITIONTIME`列が使われましたが、分割テーブルではそれに変わるフィールドをユーザが指定します。

例えば、Webサーバのログを保存するテーブルがあり、ユーザがアクセスした時刻が入る`access_time`というフィールドあったとします。
このテーブルに対して実行されるクエリは、`access_time`で条件を指定することが多いはずです。（例えば、2018年10月1日以降のログを確認する）

　テーブル作成時に、このフィールドをパーティションに使うフィールドとして指定します。そうすることで、WHERE句に`access_time`を指定したクエリは、必要なパーティションからのみデータが読み込まれます。

```sql
SELECT * 
  FROM `project.dataset.table` 
 WHERE access_time >= TIMESTAMP("2018-10-01")
```

パーティションに使うフィールドの型は、TIMESTAMP型もしくはDATE型である必要があります。


# まとめ
３つのタイプのテーブルについて紹介しました。これらの比較は[ドキュメント](https://cloud.google.com/bigquery/docs/partitioned-tables?authuser=0&hl=ja#comparing_partitioning_options)にもありますので、ご参照ください。

以上です。
GCSの更新情報をCloud Pub/Subに通知するメモ

Google Cloud Storage (GCS)の各種更新情報をCloud Pub/Subにpublishするよう設定したのですが、意外と手順が多かったので備忘録の意味で記事にしておきます。

## gsutilコマンドでGCSからPub/Subへの通知を設定する

まず次のものを用意します。

- 更新情報を知りたいGCSのバケット
- 更新情報を流すCloud Pub/Subのtopic

次にgsutilコマンドで次のようにタイプします。

```
$ gsutil notification create -f json -t projects/[PROJECT_NAME]/topics/example-topic gs://example-storage
```

これでGCSからPub/Subへの通知設定ができました。

ちなみにGCPコンソールの「Storage」「ブラウザ」から各バケットのオプション「Cloud Pub/Subにエクスポート」というメニューでDataflowを使ったエクスポートが選べますが、これは別物です。

> <img src="https://qiita-image-store.s3.amazonaws.com/0/46856/1488e071-c105-9f34-dfd0-5de38fd7c35b.png" alt="Cloud Dataflowによるエクスポートの実行" width="66%">


## サブスクライバの設定

さて、これでGCSバケットにファイルを置くとPub/Subに情報がパブリッシュされるようになりました。

送られた情報を受け取るためにCloud Functionsでサブスクライバを設定してみましょう。

「Cloud Functions」「関数を作成」から「トリガー」「Cloud Pub/Sub」を選べば、サブスクライバの設定とfunctionの設定と両方を一気に行うことができます。

> <img src="https://qiita-image-store.s3.amazonaws.com/0/46856/8ef1d72e-c437-8fe6-ec88-5c726c4afc9c.png" alt="Cloud Functionsの設定例" width="66%">

デフォルトで定義される関数はペイロードの中身をログに出すだけの処理になっています。アトリビュートは`event['attributes']`でアクセスできます（Pythonの場合）。

## 通知の中身

GCSからPub/Subにパブリッシュされるデータは次のようなものです。

```:payload
{
   "kind": "storage#object",
   "id": "example-storage-0/example.txt/1538910622649455",
   "selfLink": "https://www.googleapis.com/storage/v1/b/example-storage-0/o/example.txt",
   "name": "example.txt",
   "bucket": "example-storage-0",
   "generation": "1538910622649455",
   "metageneration": "1",
   "contentType": "text/plain",
   "timeCreated": "2018-10-07T11:10:22.649Z",
   "updated": "2018-10-07T11:10:22.649Z",
   "storageClass": "REGIONAL",
   "timeStorageClassUpdated": "2018-10-07T11:10:22.649Z",
   "size": "8",
   "md5Hash": "H/hCXuUOBquznL0JpP4ApA==",
   "mediaLink": "https://www.googleapis.com/download/storage/v1/b/example-storage-0/o/example.txt?generation=1538910622649455&alt=media",
   "crc32c": "u9BeSg==",
   "etag": "CO+YhOiY9N0CEAE="
}
```

```:attributes
{
  'bucketId': 'example-storage-0',
  'eventTime': '2018-10-07T11:10:22.649192Z',
  'eventType': 'OBJECT_FINALIZE',
  'notificationConfig': 'projects/_/buckets/example-storage-0/notificationConfigs/1',
  'objectGeneration': '1538910622649455',
  'objectId': 'example.txt',
  'payloadFormat': 'JSON_API_V1'
}
```

私の理解ではペイロードが通知の本文、アトリビュートが付属情報のようなものです。GCSからの通知の場合、ペイロードに対象のファイルの情報がJSON形式で格納されており、アトリビュートにはそれ以外の情報が格納されるようです。

### eventType

アトリビュートのうち、eventTypeはファイル操作の種類を示すものです。現在、次の4つが定義されています。

- OBJECT_FINALIZE ファイル作成
- OBJECT_METADATA_UPDATE メタデータ変更
- OBJECT_DELETE ファイル削除
- OBJECT_ARCHIVE アーカイブ

全ての操作は上記の操作の１つ以上の組み合わせで実現されています。たとえば、ファイル名の変更はDELETEとFINALIZEの2操作を連続して行っています。

## 補足

GCSのファイルの生成だけをフックしたい場合はCloud Functionsで「トリガー」「Cloud Storage」を選んだ方が楽かもしれません。

## 参考URL

- [Google Cloud Platform Japan 公式ブログ: Cloud Storage のオブジェクト イベントが Cloud Pub/Sub サブスクリプションの対象に](https://cloudplatform-jp.googleblog.com/2017/04/Cloud-Storage-introduces-Cloud-Pub-Sub-notifications.html)
- [Cloud Pub/Sub Notifications for Cloud Storage  |  Cloud Storage  |  Google Cloud](https://cloud.google.com/storage/docs/pubsub-notifications)
EC-CUBE 3 のストアフロントで取得できるユーザ情報

EC-CUBE3のtwigテンプレート上`{{app.user.xxx}}`で取得できるログインユーザ（ショップ会員）の項目名と値のサンプル。

|    Key    |    Sample   |
|:-----------------|:-----------------|
|id|7|
|name01|"佐々木"|
|name02|"多生"|
|kana01|"ササキ"|
|kana02|"タヲ"|
|company_name|"株式会社クロスキューブ"|
|zip01|"173"|
|zip02|"0004"|
|zipcode|null|
|addr01|"板橋区板橋"|
|addr02|"1234"|
|tel01|"03"|
|tel02|"1234"|
|tel03|"5678"|
|fax01|null|
|fax02|null|
|fax03|null|
|birth|null|
|password|"42yeuwidfjkfherjk"|
|salt|"43tr9eufi"|
|secret_key|"4trwgueifvodjklna"|
|first_buy_date|DateTime|
|last_buy_date|DateTime|
|buy_times|"3"|
|note|null|
|reset_key|null|
|reset_expire|null|
|Status|CustomerStatus|
|create_date|DateTime|
|update_date|DateTime|
|del_flg|0|
|Sex|Sex|
|Job|null|
|Country|null|
|Pref|Pref|
|CustomerFavoriteProducts|PersistentCollection|
|CustomerAddresses|PersistentCollection|
|Orders|PersistentCollection|

デバッグモードにして、twigテンプレートの適当なとこに`{{dump(app.user)}}`と書くと詳しく見れます。

### [EC-CUBEのカスタマイズはこちら](https://www.xross-cube.com/service/ec-cube%E3%82%AB%E3%82%B9%E3%82%BF%E3%83%9E%E3%82%A4%E3%82%BA)
Go 1.10にMIPS32 softfloat対応が来たよー

## Go 1.10から32bit MIPSの「実用的な」バイナリが生成できるようになった

表題の通りですが、Go 1.10でついに32bit MIPSアーキテクチャで動くバイナリが吐けるようになりました。

正確に言うとGo 1.8から32bit MIPSのバイナリを出力することはできていたのですが、必ず浮動小数点数演算命令を含んだバイナリ（＝hardfloat）を出力していたため、浮動小数点数演算ユニット（FPU）なしの環境では動かないという問題がありました。一方で、32bit MIPSはFPUなしのことが多いため、Go 1.8が吐いた32bit MIPSバイナリが動く環境はかなり少ない状況でした（参考：「[Go 1\.8のMIPS32バイナリをルータ上で動かしてみた（成功編） \- Qiita](https://qiita.com/hnw/items/8703a0e776f8b6a60b45)」）

その後FPUなしの環境向けにFPU命令を含まないバイナリも吐けるような開発が進みましたが、Go 1.9リリースには間に合いませんでした（参考： 「[GoのMIPS32 soft\-float対応が来たぞー（1\.9に入るとは言っていない） \- Qiita](https://qiita.com/hnw/items/1cf89069b0d3e4118831)」）。これが遂に Go 1.10で正式採用されたというわけです。

## 32bit MIPSバイナリのビルド方法

MIPS32のsoftfloatバイナリのビルド方法はGo 1.8.3用パッチと同じで、`GOARCH=mips　GOMIPS=softfloat`を付けてビルドすれば手近に転がっているMIPSマシンで動くバイナリが得られます。

```
$ GOOS=linux GOARCH=mips GOMIPS=softfloat go build hello.go
```

わざわざパッチを当てなくても、デフォルトインストールされているGoでMIPSバイナリが生成できるのは素晴らしいですね。

## 補足

すでに時期を逃した感のある話題なんですが、下書きから発掘したので記事にしておきます。
Docker 環境構築 備忘録

# はじめに
dockerでLAMP環境を作ったので、忘れないようにここに書いておく

# Dockerコンテナの準備
Mysqlコンテナを立てる

```
docker run --name mysql -e MYSQL_ROOT_PASSWORD=**** -d mysql:5.7
```
centosコンテナを立てる

```
docker run -it --name centos -p 80:80 -v /data:/data --link mysql:mysql centos:6.7
```
# OSコンテナにいろいろインストールする。
yumをupdateする

```
yum update
```
vimをインストールする

```
yum install vim
```
gitをインストールする

```
yum install git
```
apacheをインストールする

```
yum insatall apache
```
php72をインストールする

```
yum -y install epel-release
yum -y install http://rpms.famillecollet.com/enterprise/remi-release-6.rpm
yum install --enablerepo=remi,remi-php72 php php-mysql php-mbstring php-pdo php-common
```

# apacheを起動する
`/etc/httpd/conf.d/hoge.conf`
ここに新しくアクセス先の情報を記述する。

```
<VirtualHost *:80>
  DocumentRoot "/var/www/html"
  ServerName hoge
  ErrorLog logs/hoge-error_log
  CustomLog logs/hoge-access_log common
</VirtualHost>
```
apacheを起動する

```
/etc/init.d/httpd start
```
#localのhostsに接続情報を記述する

```
127.0.0.1 localhost hoge.jp
```
あとはブラウザからhoge.jpでアクセスできる
Django ListViewについて（template_name, context_object_name）

## 概要
Djangoの汎用ビューの

- template_name
- context_object_name

について、学んだことを備忘としてまとめました。

## 背景
さりげなく出現してくるこれら二つの変数ですが、ListViewの場合はviewにModelを指定してあげればWeb上に良さげに表示してくれます(テンプレートとかurlの設定はもちろん必要ですが)。
指定する場合と指定しない場合の違いについて頭から抜けていかないように...。

## 汎用ビューとは
Djangoの方で「だいたいWebページってこんな画面必要だよね」というページの設定をあらかじめ用意してくれています。例えば、ListViewを使うと1ページに表示させるアイテムの数を「paginate_by」という変数を使って指定することができたり。チュートリアルには汎用ビューを使うことでコードを少なくすることができるとありましたが、ソースをみてみたらなかなか面白かったです。
備忘も兼ねてまた今度まとめたいと思います。

## ListViewにおける`template_name`と`context_object_name`の役割
これら二つの変数は特に指定しなくてもデフォルト値が設定されるため、必要なければ設定しなくて問題ないです。
例えば、`User`モデルを作成してそのリストを表示させたいとします。

```python:models.py
from django.db import models


class User(models.Model):
    first_name = models.CharField(primary_key=True, max_length=100)
    last_name = models.CharField(max_length=100)
```

```python:vews.py
from django.views.generic import ListView

from .models import User

class UserListView(ListView):
    model = User
```

```python:user_list.html
user_list.html
<ul>
  {% for data in user_list %}
  <li>{{ data.first_name }} : {{ data.last_name }}</li>
  {% endfor %}
</ul>
```
あとはよしなにurls.pyに設定を書いてあげれば`User`モデルがリスト表示されると思います。指定したモデルに対してテンプレートとテンプレート内のコンテキスト変数（例だとuser_list）がそれぞれ下記のように対応していれば特に指定する必要なしです。

- テンプレート：`<アプリ名>/<モデル名>_list.html`
- コンテキスト変数：`<モデル名>_list `

親切なことに、特にテンプレートとコンテキスト変数の指定が無い場合は上記の名前のものを設定して使ってくれるんですね。
試しに`user_list.html`を`data_list.html`に変えてみると、Djangoさんから「user_list.htmlが無いんすけど」と怒られると思います。

共通的なリスト表示テンプレートを作成してそれを使いまわしたい時とかに利用すると良いのだろうなぁと思います。
【AWSとか】デプロイ時のエラー出たときに使いそうなコマンド集

## 構成とか
この記事でのインフラ構成はAWS EC2 + Ruby on Rails + nginx + unicorn + capistrano です。その時に使いそうなコマンドをまとめています。
DBはEC2内にMySQLを立てて管理しています。

AWSを使ってデプロイするときにエラーにはまってしまった初学者の私は
logの見かたすら分からなかった。同じようなエラーにハマった時に使ってもらえたら良いですね。

## 共通
```
$ ps ax -H    ワーキングプロセス(pid)を確認できる
$ sudo reboot EC2再起動
```

## 環境変数関連
```
vim ~/.bash_profile　　　　　　　　環境変数書き込み
source ~/.bash_profile　　環境変数適用
printenv               設定されている環境変数を確認
```
sourceコマンド打ってもなぜか入力内容が反映されていないことがありました。
printenvでちゃんと設定内容を確認するようにしています。

## MySQL関連
```
$ sudo service mysqld status 状態を確認する
$ sudo service mysqld start 　　起動コマンド
$ mysql -u root -p 　　　　　　　　　　　　　　　　　　　　PW入力してMySQLへ接続する
```

EC2を一度落とすとMySQLは落ちますのでmysql.sockがないというエラーが出ます。
起動させましょう。

## unicorn関連
```
$ ps auxwww | grep unicorn 状態を確認する
$ kill -9 ●●●●● プロセス終了

Appディレクトリで
$ cat log/unicorn.stderr.log ログを見れる
または
$ cd /var/www/app/log
$ less unicorn.staderr.log ログが見れる
$ tail unicorn.staderr.log 最新10行ログが見れる
```
S3を設定して、
Capistranoで自動デプロイを行っても、なぜかS3に画像が保存されないことがあります。
そんな時はunicornの起動時刻を確認しましょう。
今さっきunicornをリスタートしたのに４時間前のプロセスのままだったりしていることがあります。
そういう場合はkillしてデプロイし直してみると直る。

## nginx関連
```
$ sudo service nginx restart 再起動

本番環境のホームディレクトリで
$ sudo less /var/log/nginx/error.log ログが見れる
```

## capistrano関連
```
ローカル環境、Appディレクトリで
$ bundle exec cap production deploy デプロイを行う
$ less log/capistrano.log 　　　　　　　　　　　　　　　　　　　　　ログファイルの内容全て表示
$ tail log/capistrano.log 　　　　　　　　　　　　　　　　　　　　　最新10行を表示
```
bootstrapのクラスについて調べたのでまとめてみた

bootstrapのクラスについて調べたのでまとめてみました。

1. navbar
　ナビゲーションバーの設置。
　
2. navbar-fixed-top
　サイトをスクロールしても上部に固定する。

3. navbar-right
　ナビゲーションバーのボタンを右揃えにする。

3. navbar-inverse
　ナビゲーションバーの色を黒にする。
　
4. navbar-nav
　ナビゲーションバーを使用しますという指定。

5. container
 中身を画面の中央に幅を固定する。

6. btn
　ボタンを設置する。

7. btn-lg
　ボタンのサイズを変更することができる。
　lgは、largeサイズのボタン。
　btn-smは、smalサイズのボタン。l

8. btn-primary
　ボタンの種類の1つ。
    その他に、default,success,information,warning,dangerがある。
Django2をMySQLに接続する Windows10版

# 前提条件
Windows10
Python 3.6.0
Django 2.1.1

# MySQLのインストール
以下のページを参考にWindows版MySQLのインストール
https://webkaru.net/mysql/install-windows/

参考先でページでは、MySQL 5.6のインストール手順を示されている。
最新版はMySQL 8.0であるので、読み替えてインストールを行った。

# PyMySQLのインストール
`>pip install PyMySQL`
`>pip list PyMySQL`

# データベースの作成
事前に接続先データベースを用意する。
`mysql> CREATE DATABASE データベース名;`

# manage.pyの編集
PyMySQLを利用するために、manage.pyに以下のように追記をする。

```python:manage.py
import os
import sys
import pymysql  # 追記

pymysql.install_as_MySQLdb()  # 追記

if __name__ == '__main__':
```

# settings.pyの編集
デフォルトでは、SQLite3の設定が記載されているので、MySQL用に編集する。

(変更前)

```python:settings.py
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}
```

(変更後)

```python:settings.py
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'データベース名',
        'USER': 'ユーザー名',
        'PASSWORD': 'パスワード',
        'HOST': 'localhost',
        'PORT': '3306',
    }
}
```
# マイグレーションを実行
DjangoとMySQLを接続する。

```
> python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying admin.0003_logentry_add_action_flag_choices... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying auth.0009_alter_user_last_name_max_length... OK
  Applying news.0001_initial... OK
  Applying news.0002_auto_20181003_1820... OK
  Applying sessions.0001_initial... OK
```

# テーブルの確認
models.pyに記載したモデルがテーブルに反映されているか確認する。

`mysql> USE データベース名;`
`mysql> SHOW tables;`

# おわりに
以上は開発環境での接続方法なので、本番環境での設定もメモに残したい。
Lighthouseを自動化する 〜その３〜

# Lighthouse大事
LighthouseはGoogleが出しているWebサイトのパフォーマンス測定ツール。
今後はMFIが進んでいくこともあり、Lighthouseの数値を日頃からチェックしておくことは大事。
Webサイトに改修を加えた結果、スコアが落ちてSEOの評価が下がってしまったら目も当てられない。

<img width="500" alt="screenshots_001.png" src="https://qiita-image-store.s3.amazonaws.com/0/164059/233ceca7-1651-21b8-7ffa-fdef5096b081.png">

# Lighthouseは面倒
Lighthouseの実行は[Chrome拡張機能](https://chrome.google.com/webstore/detail/lighthouse/blipmdconlkpinefehnmjammfjpmpbjk?hl=JA)やChromeのデベロッパーツールから実行することができる。
けど、毎回対象のページをブラウザで開いてツールを実行するのは結構面倒。
あと、スコアの共有も面倒。

なので今回は、以下の3つの処理をJavaScript上から自動で行う。
1. [スプレッドシートから対象のURLを取得](https://qiita.com/wtotw/items/cd24037b8ba5649c323e)
2. [Lighthouseのスコアを取得](https://qiita.com/wtotw/items/651680f47fe6c81ae797)
3. Slackへの通知まで（今回）

# SlackへLighthouseスコアを通知

## Slack Webhookを使えるようにする
[このあたり](https://qiita.com/kakiuchis/items/1d9ade2ef83709209dc4)とかを参考にWebhookのURLを取得する。

## LighthouseスコアをSlackへ通知
```javascript
exports.send = (params) => {
  const https = require('https');

  const channel = '<投稿したいチャンネル>';
  const text = params.message;
  const method = 'post';
  const username   = 'Lighthouse通知';

  // 絵文字か画像URLのどちらか
  const icon = 'https://raw.githubusercontent.com/GoogleChrome/lighthouse/master/assets/lh_logo_icon.png';

  const payload = {
    "channel": channel,
    "username": username,
    "text": text,
    "icon_url": icon,
  };

  const data = JSON.stringify(payload);

  const options = {
    hostname: 'hooks.slack.com',
    port: 443,
    path: '<webhookのURL>',
    method: method,
    headers: {
      'Content-Type': 'application/json',
      'Content-Length': Buffer.byteLength(data)
    }
  };

  const req = https.request(options, (res) => {
    console.log('status code : ' + res.statusCode);
    res.setEncoding('utf8');
    res.on('data', (d) => {
      console.log(d)
    });
  });

  req.on('error', (e) => {
    console.error(e)
  ;});

  req.write(data);
  req.end();
}
```

## 最終的にこんな感じでSlackに通知される
<img width="425" alt="screenshots_004.png" src="https://qiita-image-store.s3.amazonaws.com/0/164059/8c38d5d0-2a98-a809-fc59-e6b3e589c24b.png">
Lighthouseを自動化する 〜その２〜

# Lighthouse大事
LighthouseはGoogleが出しているWebサイトのパフォーマンス測定ツール。
今後はMFIが進んでいくこともあり、Lighthouseの数値を日頃からチェックしておくことは大事。
Webサイトに改修を加えた結果、スコアが落ちてSEOの評価が下がってしまったら目も当てられない。

<img width="500" alt="screenshots_001.png" src="https://qiita-image-store.s3.amazonaws.com/0/164059/233ceca7-1651-21b8-7ffa-fdef5096b081.png">

# Lighthouseは面倒
Lighthouseの実行は[Chrome拡張機能](https://chrome.google.com/webstore/detail/lighthouse/blipmdconlkpinefehnmjammfjpmpbjk?hl=JA)やChromeのデベロッパーツールから実行することができる。
けど、毎回対象のページをブラウザで開いてツールを実行するのは結構面倒。
あと、スコアの共有も面倒。

なので今回は、以下の3つの処理をJavaScript上から自動で行う。
1. [スプレッドシートから対象のURLを取得](https://qiita.com/wtotw/items/cd24037b8ba5649c323e)
2. Lighthouseのスコアを取得（今回）
3. [Slackへの通知まで](https://qiita.com/wtotw/items/425bcc3833aceea9a4b8)

# Lighthouseのスコアを取得

## Lighthouse CLIのインストール
[LighthouseはCLIが公開されている](https://github.com/GoogleChrome/lighthouse)のでインストールする。

```bash
npm i -g lighthouse
```

## JavaScriptでLighthouse実行
CLIを呼び出すため、execSync()を使用している。
※execSyncのプロセスがkillされないときがあるので、対応方法知ってる方がいたら教えてほしい…

```javascript
const execSync = require('child_process').execSync;

exports.getLighthouseScore = (url) => {
  return new Promise((resolve, reject) => {
    try {
      console.log(`${url}`);

      // Lighthouse CLIを実行
      const result = execSync(`lighthouse "${url}" --output json --quiet`, {timeout: 60000}).toString();
      const stats = JSON.parse(result);

      const scoreMap = Object.entries(stats.categories).reduce((acc, [key, val]) => {
        return Object.assign({}, acc, {[val.title]: val.score ? parseInt(val.score * 100) : 0 });
      }, {});

      resolve(scoreMap);
    } catch(e) {
      reject({});
      execSync
    } 
  });
}
```
cakephp3 hasmany 関連モデルの合計及び、最新いいね順でソート

・OjisansTable 単
・IinesTable 複 hasmany

の関係になっている。
joinして累計したい。

```

        $query = $this->Ojisans->find();

        $query->select(['total_result'=> $query->func()->count('Iines.id')])
        ->autoFields(true)
        ->contain('Iines')
        ->leftJoinWith('Iines')
        ->group(['Ojisans.id'])
        ->order(['total_result' => 'desc']);


```

これで hasmany 先の 合計のレコード数でソートできる。


#最新いいね順でソートしたい


```

        $query = $this->Ojisans->find();

        $query->select(
            [
                'total_result'=> $query->func()->count('Iines.id'),
                'max_id' => $query->func()->max('Iines.id')//Iinesテーブルの中で最大のIDを取得
            ])
            ->autoFields(true)
            ->contain([
                'Iines' => function ($q) {
                    return $q->order(['Iines.id' => 'asc']);
                }
            ])
            ->leftJoinWith('Iines')
            ->order(['max_id' => 'desc'])
            ->group(['Ojisans.id']);




    $res = $query->all();

```


これで最新いいね順で取得できる。
Lighthouseを自動化する 〜その１〜

# Lighthouse大事
LighthouseはGoogleが出しているWebサイトのパフォーマンス測定ツール。
今後はMFIが進んでいくこともあり、Lighthouseの数値を日頃からチェックしておくことは大事。
Webサイトに改修を加えた結果、スコアが落ちてSEOの評価が下がってしまったら目も当てられない。

<img width="500" alt="screenshots_001.png" src="https://qiita-image-store.s3.amazonaws.com/0/164059/233ceca7-1651-21b8-7ffa-fdef5096b081.png">

# Lighthouseは面倒
Lighthouseの実行は[Chrome拡張機能](https://chrome.google.com/webstore/detail/lighthouse/blipmdconlkpinefehnmjammfjpmpbjk?hl=JA)やChromeのデベロッパーツールから実行することができる。
けど、毎回対象のページをブラウザで開いてツールを実行するのは結構面倒。
あと、スコアの共有も面倒。

なので今回は、以下の3つの処理をJavaScript上から自動で行う。
1. スプレッドシートから対象のURLを取得（今回）
2. [Lighthouseのスコアを取得](https://qiita.com/wtotw/items/651680f47fe6c81ae797)
3. [Slackへの通知まで](https://qiita.com/wtotw/items/425bcc3833aceea9a4b8)

# スプレッドシートからURLを取得
## スプレッドシートを準備
LighthouseにかけたいURLの一覧を用意する。
<img width="359" alt="screenshots_002.png" src="https://qiita-image-store.s3.amazonaws.com/0/164059/0de9bb9c-327a-8c2a-f00d-7a7477aa5823.png">


## スプレッドシートにアクセスするための準備
1. [Google API Console](https://console.cloud.google.com/apis/library?hl=ja&pli=1)にアクセスし、Google Drive APIを有効にする。
2. [IAMと管理](https://console.cloud.google.com/iam-admin/iam?hl=ja)からサービスアカウントを作る。
3. サービスアカウント作成時に秘密鍵の生成を聞かれるのでJSON形式でDLする。
4. 作成したサービスアカウントをスプレッドシートの共有者に追加する。
<img width="400" alt="screenshots_003.png" src="https://qiita-image-store.s3.amazonaws.com/0/164059/0635b8a2-f6fe-dfb6-f40b-a389f267a70f.png">

## JavaScriptでデータ取得
```javascript
const { promisify } = require('util');
const GoogleSpreadsheet = require('google-spreadsheet');

// サービスアカウント作成時にDLしたJSONファイル
const credentials = require('../config/client.json');

const mySheet = new GoogleSpreadsheet('<スプレッドシートのID>');

// URLが記載されているカラム
const COL_URL = 1;

exports.getReleasePath = async() => {
  const urls = [];

  // スプレッドシートからデータ取得
  await promisify(mySheet.useServiceAccountAuth)(credentials);
  const data = await promisify(mySheet.getInfo)();

  const option = {
    'min-row': 1,
    'max-row': 25,
    'return-empty': false,
  };

  // スプレッドシートデータの中からURL情報を抽出
  for(const worksheet of data.worksheets) {
    if(worksheet.title === '10/07') {
      const cells = await promisify(worksheet.getCells)(option);

      for(const cell of cells) {
        if(cell.col === COL_URL && cell.value) {
          urls.push(`${cell.value}`);
        }
      }
    }
  }

  return urls;
};
```

次は取得したURLを使ってLighthouseを実行する。
Android Studio 環境構築(1)  実機で"Hello World" 

#目的
Android Studio のインストール、Hello world表示アプリの作成、実機での実行
環境：

 * Android Studio 3.1.4
 * Windows10 Home 64bit
 * 実機: arrowsM04 / Nexus9 (Android 7.1.1)

## 【1】Android Studio インストール
1. インストーラをダウンロード
<details>https://developer.android.com/studio/?hl=ja</details>
2. ダウンロードしたexeを実行
<details>[インストール手順]( https://developer.android.com/studio/install?hl=ja ) の通りに問題なく成功 </details>

## 【2】アプリの作成
[developer.android.com - 初めてのアプリの作成](https://developer.android.com/training/basics/firstapp/)の手順を実施

1. Android Studioを起動
2. [Welcome to Android Studio] → [Start a new Android Studio project] 
3. [New Project]→Application NameとCompany Domainに入力
4. [Target Android Devices]：デフォルト
(実機のAndroidバージョンより minimum SDKバージョンが同じか小さいことを確認)
5. [Add an Activity to Mobile]→[Empty Activity] 

★プロジェクトが生成された
![01_project1.PNG](https://qiita-image-store.s3.amazonaws.com/0/298713/fe3cfb55-ac1c-4a31-31b4-fbc1e02b94fe.png)

## 【3】アプリを実行する(実機)

1. USBケーブルでPCと実機を接続
2. 実機の開発者オプションを有効
  <details>Settings アプリ→[System]→[About phone]→[Build number]を7回タップ</details>
  <details>１つ上に戻って、[開発者向けオプション]→ON、[USBデバック]→有効</details>
3. Android Studioで[Project] ウィンドウの [app] モジュールをクリックしてから[Run]
![03_run2.png](https://qiita-image-store.s3.amazonaws.com/0/298713/e096182d-f094-20bb-a6e4-bc0bdc129f83.png)

4. [Select Deployment Target]ウィンドウで端末を選択
![03_run.PNG](https://qiita-image-store.s3.amazonaws.com/0/298713/b4bf6246-4276-da12-ab85-e317768072e6.png)

★実機(Nexus9)に"Hello World!" が表示された
　![screen.png](https://qiita-image-store.s3.amazonaws.com/0/298713/5e0aa580-9b5a-cd0b-8dff-aa0683c1e9c1.png)


## 【その他の参考サイト】
- [adb 経由の screenshot の取り方](https://qiita.com/TNaruto/items/b2407f5668e15e42bedd)

- [Windows10にADBドライバをインストールする](https://qiita.com/VTRyo/items/fc34c55cf10f563cf85d)

## 【MEMO】
<dl>
<dt>app > java > com.example.myfirstapp > MainActivity.java</dt>
<dd>アプリのエントリ ポイント</dd>
<dt>app > res > layout > activity_main.xml</dt>
 <dd>UI のレイアウト、 "Hello world!"テキストも含まれる</dd>
<dt>app > manifests > AndroidManifest.xml</dt>
<dd>各コンポーネントの定義</dd>
<dt>Gradle Scripts > build.gradle</dt>
<dd>アプリをビルドする方法を設定(https://developer.android.com/studio/build/ )</dd>
</dl>
Laravel + vue-routerでハッシュ記号(シャープ記号)をなくしてちゃんと動くようにする方法

## ハッシュ記号をなくす方法

vue-routerはデフォルトでは、ルーティングに「#」記号がつくようになっています。
これを消す方法として公式が紹介しているのは、インスタンスを作るとき


```javascript
  const router = new VueRouter({
    mode:'history',
    routes:[
        { path: '/foo', component: Foo },
        { path: '/bar', component: Bar }
    ]
  })
```

のように、mode:'history'にしろ、とのこと。

## でも動かない

それをやってもなぜか私の環境では動きませんでした。
環境は以下の通り
・windows10
・Xampp
・Laravel 5.7
・vue-router
・vue

いずれも2018年10月現在最新版を使用しています。
WindowsのXampp環境ではしばしばルーティングに問題が起きるようです。

## 解決方法？

ネットで調べる限り、Laravel側の問題として紹介されることが多いようです。
おおむねLaravelのルート設定を変える方法が出てきましたが、私の環境ではうまく解決できませんでした。

```php
Route::get('/{vue?}', function () {
    return view('index');
 })->where('vue', '[\/\w\.-]*');
```


## 解決できた方法
問題はベースとするURLがうまく設定されていなかったことでした。
これはインスタンス・オプションの一つであるbaseオプションを変えればいいようです。
仮にLaravelで`app`というディレクトリをルートとしていると、以下の通り

```javascript
  const router = new VueRouter({
    mode:'history',
    base:'/app/public/',
    routes:[
        { path: '/foo', component: Foo },
        { path: '/bar', component: Bar }
    ]
  })
```

## 感想
とりあえずこの辺りでコケてしまっているLaravel + vue + vue-routerユーザーの皆様の問題解決に役立てれば幸いです。
pipenv installが上手く行かなかった際にやったこと

既存のPipfileを用いてpipenv installを実行した際、以下のようなエラーが発生した。

実行環境は以下の通りである。
- python, 3.6.5(pyenv)
- pipenv, version 2018.7.1

```
> pipenv install
Pipfile.lock (ca72e7) out of date, updating to (c2d5f0)...
Locking [dev-packages] dependencies...
Locking [packages] dependencies...
python3.6/site-packages/pipenv/utils.py", line 402, in resolve_deps
    req_dir=req_dir
  File "/Users/kosuke-f/.pyenv/versions/3.6.5/lib/python3.6/site-packages/pipenv/utils.py", line 250, in actually_resolve_deps
    req = Requirement.from_line(dep)
  File "/Users/kosuke-f/.pyenv/versions/3.6.5/lib/python3.6/site-packages/pipenv/vendor/requirementslib/models/requirements.py", line 704, in from_line
    line, extras = _strip_extras(line) TypeError: 'module' object is not callable 
```

pipenvの公式repositoryの[issue](https://github.com/pypa/pipenv/issues/2871)によると、どうやらpipの最新versionである18.1が未対応らしい。

pipのバージョンを確認してみる。

```
pip --version  
pip 18.1 from /Users/kosuke-f/.pyenv/versions/3.6.5/lib/python3.6/site-packages/pip (python 3.6)
```
pipのversionが18.1だった。
version18.0まで対応しているようなので、pipのversionを18.0にdowngradeする。

```
pip install `pip==18.0`
```

もう一度pipenv installを試してみる。


```
> pipenv install
Pipfile.lock (ca72e7) out of date, updating to (c2d5f0)...
Locking [dev-packages] dependencies...
Locking [packages] dependencies...
Installing dependencies from Pipfile.lock (c2d5f0)...
  🐍   ▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉ 58/58 — 00:02:05
```

上手くいきました。
pipenvがpip==18.1に対応するまでは、pip==18.0を用いるしか無いようです。
GitLensのBlameが邪魔な日々だった

VSCodeのGitLensを使っているとBlame行がたくさん入って邪魔だったので消そうとしたら手こずったのでメモ

[How to disable inline author link?](https://github.com/eamodio/vscode-gitlens/issues/54) より

```json
{
  "gitlens.codeLens.recentChange.enabled": false,
  "gitlens.codeLens.authors.enabled": false,
  "gitlens.currentLine.enabled": false
}
```
forをパイプの中で使用する…のはオススメで無い

[fukuoka.ex](https://fukuokaex.fun/)代表の[piacere](https://twitter.com/piacere_ex)です
今回もご覧いただいて、ありがとうございます:bow:

[前回のコラム](https://qiita.com/piacere_ex/items/98ed0d9ac6fd533902f4)で、forが、可変長引数を取る関数であることを解明しました

このことを利用して、パイプの中で、forを使う方法について解説します

# パイプでforの第1引数を渡す

forは、以下のように、第1引数にリストの分解、第2引数以降に「リストの分解」「条件判定」を複数取り、最後に「do:」を取ります

```elixir
iex> for x <- [ 1, 3, 5 ], do: x + 2
[3, 5, 7]
iex> for x <- [ 1, 3, 5 ], x < 4, do: x
[1, 3]
iex> for x <- [ 1, 3, 5 ], y <- [ 2, 4, 6 ], x < 4, y < 5, do: x + y
[3, 5, 5, 7]
iex> for x <- [ 1, 3, 5 ], x < 4, y <- [ 2, 4, 6 ], y < 5, do: x + y
[3, 5, 5, 7]
```

これをパイプで書き直すと、こうなります

```elixir
iex> ( x <- [ 1, 3, 5 ] ) |> for( do: x + 2 )
[3, 5, 7]
```

なお、if同様、パイプで渡す部分をカッコで囲まないと、うまく動きませんので、注意してください

# forをパイプの中で使えるか？

リスト分解する部分は、単独での実行はできません

```elixir
iex> x <- [ 1, 3, 5 ]
** (CompileError) iex:76: undefined function <-/2
iex> ( x <- [ 1, 3, 5 ] )
** (CompileError) iex:76: undefined function <-/2
```

Kernelモジュールや、Kernel.SpecialFormsモジュール等のどこにも定義されておらず、for特有の記述のようです（withでも使えるので、正確には、forとwithに特有、ですが）

リファレンスでの「<-」の検索結果
https://hexdocs.pm/elixir/search.html?q=%3C-

アリティも存在しません

```elixir
iex> &<-/0
** (UndefinedFunctionError) function :erl_eval.<-/0 is undefined or private
iex> &<-/1
** (UndefinedFunctionError) function :erl_eval.<-/1 is undefined or private
iex> &<-/2
** (UndefinedFunctionError) function :erl_eval.<-/2 is undefined or private
iex> &<-/3
** (UndefinedFunctionError) function :erl_eval.<-/3 is undefined or private
```

そのため、パイプの後続として、forを使うことはできません

これは以前、[「Excelから関数型言語マスター2回目：『列の抽出』と『Web表示』」の「forはforでは無い」](https://qiita.com/piacere_ex/items/b7787580fce5f148242f#for%E3%81%AFfor%E3%81%A7%E3%81%AF%E7%84%A1%E3%81%84%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0%E7%B5%8C%E9%A8%93%E8%80%85%E3%81%AE%E3%81%BF%E3%81%94%E8%A6%A7%E3%82%92)の②で挙げた通りで、その背景が、上記「<-」の仕様によるものです

# 参考程度：パイプの先頭でならforは使える ※非推奨

[前回コラムの冒頭](https://qiita.com/piacere_ex/items/98ed0d9ac6fd533902f4#elixir%E3%81%AEfor%E3%81%8C%E9%96%A2%E6%95%B0%E3%81%A7%E3%81%82%E3%82%8B%E3%81%93%E3%81%A8%E3%82%92%E7%A2%BA%E8%AA%8D%E3%81%99%E3%82%8B)にある通り、forはリストを返すことができるため、以下のように、パイプの先頭で使うことは可能です（ま、この例だと、forの中に条件判定書いちゃえるんですが）

```elixir
iex> ( for x <- [ 1, 3, 5 ], do: x + 2 ) |> Enum.filter( &( &1 < 6 ) )
[3, 5]
```

しかし、こんなforをカッコで囲むような書き方をする位なら、以下のように、Enum.mapで書いた方が、ナチュラルです

```elixir
iex> Enum.map( [ 1, 3, 5 ], &( &1 + 2 ) ) |> Enum.filter( &( &1 < 6 ) )
[3, 5]
```

更に、渡すリストをパイプで繋げ、可読性を良くできるのも、forではできない、Enum.mapらしい書き方でしょう

```elixir
iex> [ 1, 3, 5 ] |> Enum.map( &( &1 + 2 ) ) |> Enum.filter( &( &1 < 6 ) )
[3, 5]
```

こうしたことから、パイプの中ではforを使わず、より汎用性高いEnum.mapを使うことが得策でしょう

# p.s.「いいね」よろしくお願いします

よろしければ、ページ左上の ![image.png](https://qiita-image-store.s3.amazonaws.com/0/155423/4d515047-cc48-382e-c2b1-3ad0cc50dbbf.png) や ![image.png](https://qiita-image-store.s3.amazonaws.com/0/155423/a4e3da58-70a3-4197-95a2-6a6906650d01.png) のクリックをお願いしますー:bow:
AtCoderの過去問をPythonで解く(解けるとは言っていない)#ABC100

#まえおき
勉強の為にやっているので、ググったり時には解説読みながらやっております。
ご了承ください。

#A問題：[Happy Birthday!](https://beta.atcoder.jp/contests/abc100/tasks/abc100_a)
問題文からして、AとBが共に8以下である必要がある。

```python:Birthday.py
a,b = map(int,input().split())
if (a <= 8) and (b <= 8):
    print('Yay!')
else:
    print(':(')
```

#B問題：[Ringo's Favorite Numbers](https://beta.atcoder.jp/contests/abc100/tasks/abc100_b)
Nを100^D倍するだけだな！

```python:Numbers.py
D,N = map(int,input().split())
print(N*(100**D))
```
当然のようにWAした。
`N = 100` の時に割れる回数が一回増えてしまうので、条件を満たさなくなってしまうことを見落としていた。
本当は一般化した方が良いのだろうけど、今回は問題の制約が`N ≦ 100`なので

```python:Numbers2.py
D,N = map(int,input().split())
if N == 100: N += 1
print(N*(100**D))
```
これでよかろう。

#C問題：[*3 or /2](https://beta.atcoder.jp/contests/abc100/tasks/abc100_c)
少し考えて、3倍は気にする必要がなく、何回2で割れるかを数えるだけと気づいた。

```python:/2.py
num = int(input())
lis = list(map(int,input().split()))
 
ans = 0
for i in range(num):
    while lis[i]%2 == 0:
        lis[i] = lis[i]/2
        ans += 1
print(ans)
```
もっと良い書き方あるかなー。

##他の方の回答を見てみる
###似てるやつ

```python:/2'.py
num = int(input())
lis = map(int,input().split())
 
ans = 0
for number in lis:
    while number%2 == 0:
        number /= 2
        ans += 1
```

・一旦リストにしてインデックスでアクセスするより直接イテレータとして使ったほうが良い。
・なのでmapオブジェクトを`list()`する必要もない。
・あれ？最初の`input()`も必要なくね？

###全然違うやつ

```/2''.py
input();print(sum(bin(i)[::-1].index('1') for i in map(int,input().split())))
```

ワンライナーで書けるのかー。
内包表記がとてもPythonっぽい。
初見だと意味がわからなかったので調べてみた。

・`input();`で標準入力の1行目をスキップしている。
・`map(int,input().split())`の部分がイテレータ。
・`sum()`の引数が内包表記になっている。
・`bin(i)[::-1]`で要素を2進数にして逆順にしている。
・`.index('1')`で何文字目に1が最初に出てくるか＝何回2で割れるか。

とても勉強になりますね。
ビット演算はそこそこ頻出らしいので押さえておきたい。

#D問題：[Patisserie ABC](https://beta.atcoder.jp/contests/abc100/tasks/abc100_d)
うーん、わからん。方針すらわからん。
D問題はまだまだ手が付けられそうにないので、解説と解答例を見ていくことにする。

```python:Patisserie.py
from itertools import product
n,m = map(int,input().split())
cake = [list(map(int,input().split())) for i in range(n)]
 
ans = 0
for x,y,z in product([1,-1], repeat = 3):
    tmp = []
    for a,b,c in cake:
        tmp.append(a * x + b * y + c * z)
    tmp.sort(reverse= True)
    ans = max(ans,sum(tmp[:m]))
print(ans)
```

他にもっと早いのも短いのもあるけども、可読性はこれが一番な気がした。

・`itertools.product()`でデカルト積を生成
　詳しい説明は[ここ](https://note.nkmk.me/python-itertools-product/)を参考にしました。

```python:product
product([1,-1], repeat = 3)
<itertools.product at 0x1ffc0b60708>

list(product([1,-1], repeat = 3))
[( 1,  1,  1),
 ( 1,  1, -1),
 ( 1, -1,  1),
 ( 1, -1, -1),
 (-1,  1,  1),
 (-1,  1, -1),
 (-1, -1,  1),
 (-1, -1, -1)]
```

こんなリストを生成しているようです。

```python:cake
#標準入力
5 3
1 -2 3
-4 5 -6
7 -8 -9
-10 11 -12
13 -14 15

n,m = map(int,input().split())
cake = [list(map(int,input().split())) for i in range(n)]
print(cake)
[[1, -2, 3],
 [-4, 5, -6],
 [7, -8, 9],
 [-10, 11, -12],
 [13, -14, 15]]
```
こちらでは各ケーキのプロパティを二次元配列にしている。
これらを掛け合わせて8通りの全探索をする。
2重for文で同じインデックスの要素を掛けて足して`tmp`に`append()`して降順ソートしてm個足す。

`sum(tmp[:m])`はとてもオシャレでいいですね。
`ans = max(ans, x)`も便利そうです。
`if ans < x:ans = x`みたいなことせずに済みそう。

やはり他人のコード読むのが一番勉強になる。
P○izaは課金しないと模範解答見れないのでやはりAtCoderが良いのかな。
ディレクティブとは

ディレクティブの意味がわからずググったのですが、いまいちパッとしない説明が多かったのでわかりやすくメモっておきます。

ディレクティブ・・・コンパイル時にコンパイラーに与える補足情報。

大抵は、プログラムの最初の行に記述する。

このファイルは
・UTF-8だよ　とか
・HTMLファイルだよ　とか
・＊＊クラスをインポートしてね　とか

一番わかりやすかった参考ページは以下です。
[ディレクティブとは]
(https://www.javadrive.jp/servlet/jsp_directive/index1.html)
pwnable.kr random ~疑似乱数シード~

#問題文
`ssh random@pwnable.kr -p2222`　にアクセスして、flagを入手する
#解法
##ファイル構成
- -r--r----- 1 random_pwn  root     flag
- -r-sr-x--- 1 random_pwn  random   random
- -rw-r--r-- 1 root        root     random.c

例によってflag はrootでしか読み取れず、random.cも買い替えは不能。よって./random　という実行ファイルに対して何らかの攻撃をすればflagはゲットできると予想する。
##random.c
```random.c
#include <stdio.h>

int main(){
	unsigned int random;
	random = rand();	// random value!

	unsigned int key=0;
	scanf("%d", &key);

	if( (key ^ random) == 0xdeadbeef ){
		printf("Good!\n");
		system("/bin/cat flag");
		return 0;
	}

	printf("Wrong, maybe you should try 2^32 cases.\n");
	return 0;
}
```
ユーザーの入力keyとrand()で生成したrandomの排他的論理和が0xdeadbeefであればcatコマンドでflagを表示してくれる。
#方針
間違ったときに出てくるメッセージによると、2^32 のブルートフォースアタックをすればいいようにも見えるが、（unsunged intなので32乗）約42億となってしまうので少しやりたくない。（やったらどれくらい時間がかかるかどうかはわかりません）
というわけでどうにかして、randomを特定してそれに対応したkeyを入力してやることによるflag入手を目指す。
#rand()関数の脆弱性
さて、randomはrand()関数を使用して乱数を生成しているわけだが、この関数は通常これだけでは使ってはいけない。コンピュータが乱数を生成すると言っても、人間のように思いつきで生成しているわけではなく、擬似乱数というものを計算で生成しており、そのために乱数のシード（seed:種）を使う。
今回のrand()関数が使う方法では、シードが同じなら、必ず同じ値が生成されてしまうという特徴があり、コード中にはシードを設定している箇所はない。
つまり、これを実行しても、randomは乱数ではなく、「定数」となってしまうのだ。
以上から、randomを特定してやり、それを入力してやることでflagゲットを目指す。
#デバッガによるrandom特定
random.cは書き換えできないので、何らかの手段で実行中の変数の内容を観測してやる必要がある。
そのためにGDBというデバッガを使う。
main関数のディスアセンブリ結果の抜粋（下）

- rand関数周辺

` 0x0000000000400601 <+13>:	call   0x400500 <rand@plt> `
   ` 0x0000000000400606 <+18>:	mov    DWORD PTR [rbp-0x4],eax `

- if文周辺

` 0x0000000000400629 <+53>:	mov    eax,DWORD PTR [rbp-0x8] `
   ` 0x000000000040062c <+56>:	xor    eax,DWORD PTR [rbp-0x4] `
  ` 0x000000000040062f <+59>:	cmp    eax,0xdeadbeef `

以上から、[rbp-0x8]にkeyが、[rbp-0x4]にrandomがあるということが推測される。
よってmain+18直後の[rbp-0x8]を調べればrandomがわかることになる。
##答え
(私の環境では)randomは0x6b8b4567となり、これと0xdeadbeefの排他的論理和である3039230856を入力してやることでflagが表示された。

#rand()の正しい（?）利用法
rand()関数を使ってやる際には前述のように毎回違うシードを使ってやる必要があり、慣習的に時間をシードとする方法が知られている。
srand（）関数でシードを指定する際にtime()関数で現在時間を表示するなどがある。
CODER.comを使って、ブラウザだけでGoogle Cloud Functionsの開発する

ここ数年、[Cloud9](https://aws.amazon.com/cloud9/)や[Codenvy](https://codenvy.com)を始めとしてブラウザだけでフロントやサーバサイドの開発を行えるCloudIDEが複数リリースされています。

そもそもAtomやVSCodeなどのエディタがElectronなどを使ったWeb技術の上で作られていることもあり、ここ数年で凄まじい進化を遂げ、いくつかの環境では本当にブラウザだけで開発できる様になってきました。

いま個人的なプロジェクトのフロントエンド(React)は[CodeSandbox](https://codesandbox.io)で開発していますが、MacbookでもiPadでも同じように開発ができ、非常に便利です。

次にサーバサイドとしてCloud Functionsを使いたく、この開発環境もブラウザだけで構築するのを試してみました。

まずは直接Cloud Functionsとの連携をサポートしているCloudIDEを探したけど見つからず、コンソールをサポートしていて使いやすいものを探してみました。コンソールにDockerをサポートしているのはCodenvyなんだけど、ちょっと複雑で手軽に使うには面倒。

ちょうど2018/8にリリースされたばかりの、[CODER](https://coder.com)を見つけたので試してみました。

# CODERへの登録

まず、[CODER](https://coder.com/)にアクセスして"Sign Up"します。私はGoogle accountで登録しました。SMSによる認証があるので適当な番号で設定してください。

登録が完了すると、Dashboardへ来るので「Create Project」で新しいプロジェクトを作り、「Open IDE」で開発環境を開きます。

<img width="944" alt="Screen Shot 2018-10-07 at 16.43.39.png" src="https://qiita-image-store.s3.amazonaws.com/0/4471/9c0e2b4c-4c75-5f22-2d4e-8e43b3c7fda9.png">

# IDEの起動

空の環境ができたので、まずは適当な`index.js`を作ります。
<img width="950" alt="Screen Shot 2018-10-07 at 16.51.50.png" src="https://qiita-image-store.s3.amazonaws.com/0/4471/28d74d4e-78ae-bff0-410c-03ee4e82ccb8.png">

```javascript

exports.index = (req, res) => {
    res.send(`Hello World`);
}
```

# CloudSDKのインストール

次に、これをGoogle Cloud Functionsにdeployするために、[Google Cloud SDK](https://cloud.google.com/sdk/)をCODERにインストールします。

簡単にインストールできるコマンドを用意したので、左のターミナルのアイコンをクリックして、下記の行をコピペして実行してください。

```
curl -o- -L https://gist.githubusercontent.com/masuidrive/676c571d81f02d569eaa29921382f0a0/raw/3b8631ee4642f29c22e616452b409ae7dc300fac/install.sh | sh
```

実行されるコマンドは[Gistにアップロード](https://gist.github.com/masuidrive/676c571d81f02d569eaa29921382f0a0)してあります

CODERに標準でインストールされているPythonは3系統なのですが、CloudSDKでは2.7を要求しているので、`apt`で`python2.7`をインストールしています。これは公式FAQにも書いている手法なのですが、なぜか一度terminalを落とすと`/usr/bin/python2.7`が勝手に3系統に上書きされるので、下記のコマンド内では回避しています。

次にGoogle Cloudへの認証です。先にGoogle Cloudのアカウントやプロジェクトを作成し、ターミナルから下記のコマンドを入力してください。

```
. ~/.bashrc; gcloud init
```

途中で、URLをコピーするところがありますが、そのままコピペすると改行コードが入ってしまうのでペーストした後に取り除いてください。

認証が終わると`.config`フォルダができています。これはGitなどにあげると問題なので、必ず`.gitignore`に`/.config`を追加しておきましょう。

# deploy

さて、最後にdeployです。

```
gcloud functions deploy index --runtime nodejs8 --trigger-http
```

これでCloud Functionsにdeployされたはずです。terminalに表示されているURLを開けば動作が確認できるはずです。

<img width="996" alt="Screen Shot 2018-10-07 at 17.12.16.png" src="https://qiita-image-store.s3.amazonaws.com/0/4471/56457022-6f35-12b6-a172-bc00ea45e777.png">

# 最後に

今のところ、iPadのSafariではカーソルキーやCmd+Sによる保存が効かないのでiPadではちょっと厳しい感じです。

大体月1ぐらいでアップデートされているようなので、これからの進化が楽しみです。