GASのライブラリを使って楽したい③ 自分でライブラリを作ってみる(:3っ)っ -=三[布団]﻿

Google Apps Script Advent Calendar六日目です。

一昨日から三日連続でGASのライブラリ機能の話をしていってます。
今日はライブラリ話の最後で自分でライブラリを作ってみたいと思います。

# ライブラリの作り方
[昨日](http://qiita.com/items/5a7e050a2a20f3e3938a)の時点でほぼ一度作っているのですが、
今回は自分で一から作ってみようと思います。

今回はSitesAppに存在しない
「名前を指定してテンプレートページを取得する」メソッド
を持ったSitesUtilというライブラリを作ってみたいと思います。

## 自作ライブラリ

ライブラリを作るのは普通にGASのコードを書く時とやることは代わりません。
GAS Editor を開いて以下のようなコードを作成してください。
なお、上の方に書いてあるtest関連のメソッドや変数は不要なので、
コメントだけ読んでおいてください。

```javascript:インスタンスを作らない.gs
//グローバル変数は補完には表示されない。ただし値を取得したり設定することは可能
var test;

//グローバル領域に宣言したメソッドは補完に表示される。
function getTest(){
  return test;
}

//_(アンダースコア)が末尾にあるメソッドも補完には表示されない また呼び出し自体が不可能
function test_(){}

/**
 * ・・・・・・・・・①
 * テンプレートの名称よりテンプレートメージを取得します。<br/>
 * テンプレートが見つからない場合は <code>null</code>を返却します。<br/>
 *
 * <h3>利用例</h3>
 * <pre>
 * var template = SitesUtil.getTemplateByName(SitesApp.getActiveSite() , "毎月作るページのテンプレート");
 * </pre>
 * @param {Site} site テンプレートを持っているSite
 * @param {string} templateName テンプレートの名称
 * @return {Page} 見つけたテンプレートページ
 */ 
function getTemplateByName(site , templateName) {
  //var site = SitesApp.getActiveSite(); ・・・・・・・・・②
  
  if(!site) {
    throw new Error("siteは必須です。");
  }
  
  var templates= site.getTemplates().filter( //・・・・・・・・・③
    function(template){ 
      //var template = site.getTemplates()[0]; ・・・・・・・・・②
      return template.getName() == templateName;
  });
  
  if(templates.length == 0) {
    return null;
  }
  
  if(templates.length >= 1) {
    return templates[0];
  }
}


```

### 解説

ちょっと大切な部分というかTips的な部分だけ説明します。

#### ①JsDoc

ライブラリを作成すると自動でWebドキュメントが作成されます。
上のコードだとこの様な[Webドキュメント](https://script.google.com/macros/library/d/Mez5YCsOqf58v4mwvVZwJUeMffa6w-w2J/7)になります。

このドキュメントを見て頂き、`getTest`メソッドと、`getTemplateByName`を見ていただくとわかるのですが、
JsDocスタイル(/** */)でメソッドにコメントを書くと、
メソッドの説明文が記載されます。

JsDoc中では各種のHTMLタグが利用できますが、あまり高度なことはできません。
僕の感覚では例を書く場合は
`<code>`タグや`<pre>`タグを利用すると多少見た目が良くなります。

ただし**シンタックスハイライト**は出来ないようです....

またJsDocスタイルで書きはするのですが、
利用できるJsDocタグは`@param`と`@return`に限られます。
この@paramや@returnはライブラリを使用する際に
補完の候補としても利用されるのでちゃんと書いておいたほうが良いです。

ただし上記のようにPageやSiteを書いてもそれ以降の補完は出来ない。(白目

```javascript
var template = SitesUtil.getTemplateByName(SitesApp.getActiveSite() , "毎月作るページのテンプレート");
template. //とやってもPageクラスのメソッドを補完できません...
```

じゃあ役に立たないかというと、メソッドチェーンをする場合は有効です。

以下のように@return {プロジェクト名}とするとメソッドチェーンができます。
またSitesAppや、SpreadsheetsAppの様な物、違うライブラリの識別子を書いた場合も補完が可能です。


```javascript:ライブラリ側

/**
 * @return SitseUtil
 */
function setHoge(hoge){
  this.hoge = hoge;
  return this;
}

/**
 * @return SitesUtil
 */
function setFuga(fuga){
  this.fuga = fuga;
  return this;
}
```

```javascript:ライブラリを使う側
Sitseutil.setHoge("hoge").setFuga("fuga"); //メソッドを連続して補完できる。
```

#### ② 謎のコメント

謎のコメントが書いてありますね。
これはGAS Editorのハックで、
`site`変数のように「Siteクラスのオブジェクトが渡ってきているはず」という場合、
通常何もしないとこの`site`変数は補完ができません。

ただ`//var {補完したい変数名} = {そのオブジェクトが取れる操作}`の様に記述しておいてあげると
それ以降この変数を補完することが可能になります。

自分でライブラリを作る場合や、
単純にメソッドを切る場合でも非常に有用なので覚えておくと得です。

#### ③ 補完に出てきてないメソッド

GAS Editorで配列(`[]`)を宣言し、補完機能を利用してもfilterメソッドは表示されません。

しかしGAS Editorの補完は
そのオブジェクトに定義されている全てのメソッドを表示しているわけではないので、
filterの様なECMAScript5相当のメソッドはだいたい使えます。

※ちなみにJSONや、setter、getterなんかも実は使えます。

## ライブラリ化する

あとは昨日と同じように バージョンを作成し、プロジェクトキーを取得してライブラリ化してください。

なおバージョンを作成する際に入力するテキストボックスに入れたコメントなどは、
ライブラリのドキュメントの一番上に記載されます。

ここにはHTMLタグも使えるようなのでどのようなライブラリか、
このバージョンで何が変わったかを書いておくといいかもしれません。

#補足

ライブラリを作る際に「クラスっぽく作りたい」と言うことはよくあると思います。
例えば上記の例だと`site`変数は渡さないでインスタンス内に持ちたいよなーとかあると思います。
こういった場合は以下のように`create()`等のメソッドを作って、ライブラリ側でインスタンスを作ってあげたほうが、
補完などもできて嬉しい人が多いかもしれません。

僕が作っている[GSSDB](https://github.com/soundTricker/SpreadsheetDB-For-Google_Apps_Script)というライブラリでは、
「コードやインスタンス化を行うメソッドを持つライブラリ」と「補完用のライブラリ」があり
上記の//var ハックでインスタンス側も補完できるようにしています。
ライブラリを作る際に参考にしてください。

## インスタンス作る版の例
```javascript:SitesUtil.gs

(function(global){
  var SitesUtil = (function() {

    function SitesUtil(site){
      this.site = site;
    }
    
    SitesUtil.prototype.getTemplateByName =  function(templateName) {
      //var this.site = SitesApp.getActiveSite();
      var site = this.site;
      
      var templates= site.getTemplates().filter(
        function(template){ 
          //var template = site.getTemplates()[0];
          return template.getName() == templateName;
        });
      
      if(templates.length == 0) {
        return null;
      }
      
      if(templates.length >= 1) {
        return templates[0];
      }
    };
    
    return SitesUtil;
  })();
  
  global.SitesUtil = SitesUtil;
})(this);

/**
 * SitesUtilインスタンスを作成します。
 * @param {Site} site Siteインスタンス 未指定の場合はSitesApp.getActiveSite()を設定します。
 * @return SitesUtilInstance
 */
function create(site) {
  return new SitesUtil(site || SitesApp.getActiveSite());
}

```

## 補完用のライブラリ
```javascript:SitesUtilInstance.gs
//プロジェクト名をSitesUtilのcreateの@returnと同じにしておく
/**
 * テンプレートの名称よりテンプレートメージを取得します。<br/>
 * テンプレートが見つからない場合は <code>null</code>を返却します。<br/>
 *
 * <h3>利用例</h3>
 * <pre>
 * var template = SitesUtil.getTemplateByName(SitesApp.getActiveSite() , "毎月作るページのテンプレート")
 * </pre>
 * @param {string} templateName テンプレートの名称
 * @return {Page} 見つけたテンプレートページ
 */ 
function getTemplateByName(site){
  throw new Error("it's a mock method for content assist");
}
```

## 使うとき
```javascript:ライブラリ使う側.gs
//SitesUtilとSitesUtilInstanceをライブラリとして取り込んでおく


var siteUtil = SitesUtil.create(SitesApp.getActiveSite());
//var siteUtil = SitesUtilInstance;
var template = siteUtil.getTemplateByName("hogehoge");
```