GASはいいぞ！

どうも、ブログがなかなか手につかず、本家のブログの[blog.mecaota.work](blog.mecaota.work)を1年放置している怠け者です。メカヲタ。です。

さて、どうやら誕生日である12月5日に学生LTアドベントカレンダーの登録をしていたにも関わらず放置したまま、LT会でLTしていたようで、大変に申し訳ない限りです。

仕方ないのでタイムリープしたいと思います。12月5日のメカヲタ。さんお願いします。

---

どうも、12月5日学生LT Advent Calender担当のメカヲタ。です。

さて、私からはGASはいいぞ！ということでつい先日12月8日(あれ、時空が歪んでる？)に開催された学生LT第7回にて発表した話について、もう少し噛み砕いて説明したいと思います。

# GASとは

うちの地域の都市ガスは北ガスが担当しています。違うか。

GASというのはGoogleAppsScriptのことです。MicrosoftOfficeで言うマクロ機能を作るVBAみたいなもんのGoogle版です。GoogleDrive上に簡単に作成でき、言語もほぼJavascriptと同等なのでちょっとJSかじったことがある人もそうでない人でも手軽に実装できます。

現に私もこのGASを使ったウェブアプリ的なものを初めて作ったのは、大学でプログラミング演習が始まって1ヶ月もしない辺りで作ってます。そんな素人でも簡単に作れてしまいます。ね？簡単でしょ？

## 2種類に別れるスクリプト

微妙な違いで、ContainerBoundScriptと通常のScriptに別れます。

### Container Bound Script

スプレッドシート、またはGoogleフォーム上でスクリプトエディタからスクリプトファイルを作成すると、自動でそのスプレッドシートと紐付けされたプロジェクトファイルが生成されます。

#### 利点

- スプレッドシートやフォーム等に関するマクロ処理を作る時に、操作したいファイルを特定する為のファイルID等を使わなくても、直接呼び出せる。こんな感じで

```javascript:
var ss = SpreadsheetApp.getActiveSheet();
```

#### 欠点

- ドライブ上から直接スクリプトファイルが参照出来ない上に、共有時の権限管理が非常に煩雑になる

### 通常のScript

Google Driveから直接、新規作成して生成できます。

#### 利点

- 個別のファイルで管理出来るため、PCと同期していたり、他のユーザーと共有する際に楽

#### 欠点

- 各種ファイルの呼び出しに毎回各ファイルのファイルIDを取得する必要がある。

まあ、マクロ作るなら前者、純粋なスクリプトファイルやGASでwebサーバー的に使うとかなら後者といった使い分けでしょうか。

# 始め方

通常のスクリプトであれば、グーグルドライブの新規作成ボタンから、「Google Apps Script」を選択します。

![スクリーンショット 2017-12-08 16.38.49.png](https://qiita-image-store.s3.amazonaws.com/0/104876/48277584-cc43-83df-b98e-888e52866b50.png)

もし、無かった場合、「アプリを追加」から追加してください。

あるいは、Container Bound Scriptの場合、紐付けたい任意のスプレッドシートを開いて、

![スクリーンショット 2017-12-08 17.24.42.png](https://qiita-image-store.s3.amazonaws.com/0/104876/9c0c8d8c-6f34-0fd7-a58d-6c1487e5f2e0.png)

「メニュー」→「ツール」→「スクリプトエディタ」からでも開けます。

あとはウェブエディタが現れるので、コードを書いていくだけです。

# 作例

- 学祭用ビットコイン決済受付ページ(購入可能になってても、購入したところで僕にカンパ入るだけなのでご注意)
    - URL: [https://script.google.com/macros/s/AKfycbwS2A7E712y86P6yXf3arI8J7Js_6DaT_0M7fOHmNYEoB5kqAE/exec](https://script.google.com/macros/s/AKfycbwS2A7E712y86P6yXf3arI8J7Js_6DaT_0M7fOHmNYEoB5kqAE/exec)
    - Github: [https://github.com/mecaota/bitwireshop2real](https://github.com/mecaota/bitwireshop2real)

他にも、スクレイピングしたりslackに天気予報してみたりなどいろんな事例あるので、良ければ調べてみてどうぞ。

# 作ってみよう

ここにいい感じの怪しいスプレッドシートがありますねぇ

![キャプチャ.PNG](https://qiita-image-store.s3.amazonaws.com/0/104876/e4330fb1-ccc3-e9e2-cf76-aa43881b2274.png)

このスプレッドシートに書かれてる内容を使えば、なんだか在庫管理システムみたいなものが作れそうですね。作ってみましょう。

今回はスプレッドシートをがりごりばりばり弄るので、Container bound scriptで作ろうと思います。まずは、「メニュー」→「ツール」→「スクリプトエディタ」を開く。

![スクリーンショット 2017-12-08 17.27.42.png](https://qiita-image-store.s3.amazonaws.com/0/104876/c4452ae9-d239-be23-b60f-90d1fbdc66a9.png)

では、、スプレッドシートのデータを参照してみます。

```javascript:
function myFunction() {
  var ss = SpreadsheetApp.getActiveSheet(); //スプレッドシートを指定
  var sheetdata = ss.getRange(1, 1, ss.getLastRow(), ss.getLastColumn()).getValues(); //シートの範囲を指定して内容を取得
  Logger.log(sheetdata);
}
```

これでスプレッドシートの中身を拾ってきます。(myFunction()って名前は別にデフォルト値で使ってるだけなので、好きな関数名に変えてやってください)

```javascript:
SpreadsheetApp.getActiveSheet();
```
で、現在選択されているスプレッドシートのシートを参照します。もし、ContainerBoundScrriptではなく、通常のスクリプトで作るのであれば、シートの参照が出来ないはずなので、

```javascript:
SpreadsheetApp.openById("スプレッドシートのファイルID").getSheets();
```

や

```javascript:
SpreadsheetApp.openByUrl("スプレッドシートのURL").getSheets();
```

を使う必要があります。また、もし複数のシートがある場合は

```javascript:
SpreadsheetApp.getActive().getSheetByName("シート名");
```

で、スプレッドシートファイルのどのシートかを指定してあげてください。

また、拾ってきたssから、どの範囲のセルを参照するかを指定してやります。getRange()でRangeクラスを返してくれるのですが、セルの指定方法が0スタートではなく、1スタートなので注意を。(行,列)、(行始点,列始点,行終点,列終点)など複数指定方法があります。

あとはgetValue()かgetValues()で中身を拾います。Rangeの範囲によって変わります。セル1個だけならgetValue()、複数のセルならgetValues()です。

JSで普通コンソール出力する時はconsole.log()を使うと思いますが、GASでは内部で処理されてしまうので、コンソールが見えません。GASに用意されたコンソールへ出力する際はLogger.log()を使います。よく間違えるので気をつけましょう。

実行はメニュー下の▶(左向きの黒三角)アイコンです。隣の虫マークのさらに隣に実行関数を選ぶプルダウンメニューがあるので、実行したい関数と合致しているか確認してください。

![キャプチャ1.PNG](https://qiita-image-store.s3.amazonaws.com/0/104876/cd453b23-1651-1c9e-1e8c-3d1ef6002e7c.png)

「承認が必要です」と多分表示されるので、まあ「至急俺にスプレッドシート見る権限くれや」と言われてるだけなので、画面のとおりに進んで権限を許可してください。

すると、上部に「実行中」の表示がされて消えるので、無事消えてくれたらエラーなく実行できました。おめでとうございます。さて、コンソールを確認してみましょう。

コンソールは「メニュー」→「表示」→「ログ」から見れます。

![キャプチャ2.PNG](https://qiita-image-store.s3.amazonaws.com/0/104876/7b4d3045-82f5-6cc5-b282-2c012465deda.png)

１行ずつ中身が入ったarrayが詰め込まれたarrayだということがわかりますね。

あとはこれを使いやすくすればいいだけです。1行目だけをヘッダーとして、残りをスライスしてそれぞれにkeyを振ってオブジェクト化すれば管理も楽ですね。

```javascript:
function myFunction() {
  var ss = SpreadsheetApp.getActiveSheet(); //スプレッドシートを指定
  var sheetdata = ss.getRange(1, 1, ss.getLastRow(), ss.getLastColumn()).getValues(); //シートの範囲を指定して内容を取得
  var lines = [];
  var header = sheetdata[0];
  sheetdata = sheetdata.slice(1);
  sheetdata.forEach(function(values){
    var line = {};
    values.forEach(function(value, index){
      line[header[index]] = value;
    });
    lines.push(line);
  });
  Logger.log(lines);
  return lines;
}
```

これで

![キャプチャ3.PNG](https://qiita-image-store.s3.amazonaws.com/0/104876/f351a016-0f10-0874-4882-d52b24619efd.png)

でけた。

ここからは疲れてきたので早足でgetリクエスト来た時にさっきスプレッドシートの中身をJSONで返す機能を実装しましょう。

実装は簡単。

```javascript:
function doGet(request){
  var output = ContentService.createTextOutput();
  output.setContent(JSON.stringify(myFunction())).setMimeType(ContentService.MimeType.JSON);
  Logger.log(output);
  return output;
}
```

これを書き足すだけ。

後述するURLにアクセスすると、自動でdoGet()が走って処理を掛け、返り値がそのままレスポンスとなるというのが多分わかる。分からなかったら、myFunction()って書いてるとこを"{"text":Hello world"}とかに書き換えて見ればいいよ。

さあ、これを公開しましょう。

「メニュー」→「公開」→「ウェブアプリケーションとして導入」を開く。

![キャプチャ56.PNG](https://qiita-image-store.s3.amazonaws.com/0/104876/cdfd664e-9221-c877-3b77-4b16233f9fe7.png)

GASでちゃっかりバージョン管理も出来ます。なのでもしコード書き換えて思うような動きがしなかったらいつでも元に戻せますし、そもそもそのバージョンのコードが公開されて処理が走るので、運用したままコード書き換えもしっかり出来ます。なんだこのSaaSは・・・

で、バージョンの概要(オプション)を入力して、「アプリケーションにアクセスできるユーザー」を｢全員(匿名ユーザーを含む)｣にします。これをしないと他のウェブサイト等からAPIアクセス出来ないです。

※｢全員(匿名ユーザーを含む)｣にしないと場合によってはアクセスできない(CORS周りの問題が出るらしい)ことがわかったので記事修正しました。画像では｢全員｣ですが、選ぶのは｢全員(匿名ユーザーを含む)｣です。

終わったら、「導入」をクリック。

URLが貰えます。「現在のウェブ アプリケーションの URL」のURLをコピーしてアクセスしてみましょう。

![キャプチャ5.PNG](https://qiita-image-store.s3.amazonaws.com/0/104876/36fdc481-03c8-b115-499b-f1a910f53a90.png)


やったぜ。

完成したコード

```javascript:
function myFunction() {
  var ss = SpreadsheetApp.getActiveSheet(); //スプレッドシートを指定
  var sheetdata = ss.getRange(1, 1, ss.getLastRow(), ss.getLastColumn()).getValues(); //シートの範囲を指定して内容を取得
  var lines = [];
  var header = sheetdata[0];
  sheetdata = sheetdata.slice(1);
  sheetdata.forEach(function(values){
    var line = {};
    values.forEach(function(value, index){
      line[header[index]] = value;
    });
    lines.push(line);
  });
  Logger.log(lines);
  return lines;
}

function doGet(request){
  var output = ContentService.createTextOutput();
  output.setContent(JSON.stringify(myFunction())).setMimeType(ContentService.MimeType.JSON);
  Logger.log(output);
  return output;
}
```

# 〆

他にも、HTMLテンプレートを用意すれば、HTML上でGASが走らせることも出来るし、ウェブページをスクレイピングしてたり、slackと連動させてなんかさせたりなんかもできちゃいます。可能性は無限大。さあ、あなたもGASを始めよう。