[GAS][slack]営業日カウントダウンbotを作っている途中 その8 ペアプロして芝刈りされた。ペアプロは精神と時の部屋。

ペアプロすげ〜ってのと、コードのビフォアフを見ていく。このnoteは私がとっても楽しい内容だか、他人にとってどうなのかは知らない。クラスを理解し始めた人、クラス化していきたい人は参考になる、かもしれない。

コードだけさくっと見たい人は下記の目次からコードの箇所に飛ぶか、github見てください。

で、肝心の営業日カウントダウンの作り方は、また今度、改めてまとめようかな。コード見ればわかる人はそれでいいけど、もちっと、まとめて整理したい気もする。

GitHub



前回

ペアプロした

ペアプロは精神と時の部屋、ペアプロは知の高速道路。

天才こと @etau さんにお相手いただきましたっ感謝!
本当にありがとうございます。
「むかつく」はetauさんへの褒め言葉であり、自身への戒めです。

咀嚼中

ちょっとまだ全部を咀嚼しきれてないので、ちょっとずつnoteに書いて整理していきたい、自分のために。

は〜、ほんとに、これは確かに便利で再利用も効きそうだが、初見殺しでもある。
めちゃくちゃ勉強になった。けど、それが自分の中の血肉になるには、まだもう少しウンウン悩んだりエラー出したりしていく必要がある。

いやしかし、わしプログラマでないのに、ここまで勉強して偉いな??今どきこれぐらい普通なのか??世の中がわからない。自分で自分を褒めていくぞっ!!えらい!!!

作成方針

  • メンテしやすいように後から変わりそうなものはglobalにまとめる

  • if/else は書かなくてもよい、Booleanで返ってくるからそのまま一行で書ける(今回の場合

  • function内にコメント書くならコードの後、コードを見やすく

  • ifの後は一行空けたい派

  • 残営業日の計算はスプレッドシートでやって、そこから値をとってくる形にする

  • globalの構成、Enumが肝


それぞれのコードを見てみよう

Beforeのコードはこちらのnoteこちらのnoteを参照した。このnoteの後も自分でチマチマ直したところもあるが割愛する。

main

Befor1

初期。

function main({
  //平日のみslackに投稿する用の日付判定用の別関数呼び出し、土日祝ならここで処理終了
  if (isWorkday(today) == false) {
    return;
  }

  //土日祝でないならslacに投稿
  else {
    postSlackbot()
  }

}


Befor2

一旦クラス化した後。

function main({

//クラスから呼び出し
  const newDay = new Day();

  //平日のみslackに投稿する用の日付判定用の別関数呼び出し、土日祝ならここで処理終了
  if (newDay.isWorkday() === false) {
    console.log(`今日は 営業日じゃないのでSlackには投稿しませんでした。`);
    return;
  }

  //土日祝でないならslacに投稿投稿
  else {
    postSlackbot();
    console.log(`今日は 営業日なのでSlackに投稿しました。`);
  }

}

After

'use strict'
function main({
  const newDay = new Day('2022/04/22');
  if (newDay.isWorkday() === falsereturn console.log(`今日は 営業日じゃないのでSlackには投稿しませんでした。`);

  postSlackbot();
  console.log(`今日は 営業日なのでSlackに投稿しました。`);
}

なんということでしょう。
匠の技により、if文もスッキリと一行に整理されました。

厳密等価===もバッチリです。
ifが成立すればretunが返るし、不成立ならpostSlackbot()が実行されます。

use strictによりエラーを発見しやすくします。new Dayに検証用の引数を入れても動作するよう、クラスの方にも手を入れました(後述)。

//のコメントはコード読めばわかるから要らないそうです……うっ、説明欲しいよう。

※厳密等価のあたりは、そーちゃんにコメントいただいた。

global

Before

/**
 * グローバル定数宣言
 */
/** @type {SpreadsheetApp.Spreadsheet} */
const holidaySheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('祝日リスト');
const dateMasterSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('日付マスタ');

うーん、とりあえずシート置いとくか……?から始まった。

After

'use strict'

/**
 * グローバル定数宣言
 */
/** @type {SpreadsheetApp.Spreadsheet} */
const SS = SpreadsheetApp.getActiveSpreadsheet();

/** @enum {number|string}  */
const SHEET_INFO = {
  HOLIDAY: {
    NAME: '祝日リスト',
  },
  DATEMASTER: {
    NAME: '日付マスタ',
    COLUMN_NAME: {
      TYPE: {
        NAME: '種別',
        VALUE: {
          WINTER_HOLIDAY: '今年の冬期休暇',
          BUSINESSDAYS_REMAINING_THIS_MONTH: '今月の残り営業日数',
          BUSINESSDAYS_REMAINING_THIS_YEAR: '年内の残り営業日数',
          BUSINESSDAYS_REMAINING_THIS_FISCAL_YEAR: '今年度の残り営業日数',
        },
      },
      RESULT: {
        NAME: '計算結果',
      },
    },
  },
};
const MESSAGE = {
  WORKDAY: '今日は stringifiedToday です。\n' +
    '今日を含めて、\n' +
    '今月の残り営業日数は、あと businessDaysRemainingThisMonth 日です。\n' +
    '今年の残り営業日数は、あと businessDaysRemainingInThisYear 日です。\n' +
    '今年度の残り営業日数は、あと businessDaysRemainingInThisFiscalYear 日です。',
};

シートの名前、見出し名称、投稿するメッセージなどをここでバチっと書いています。ここで定義しておくことで、コードで呼び出せて便利。何か変更があった時も基本的にはまずここを見れば良いという形。階層を間違えるとアウト。

入れ替えた時にエラーを防ぐためにも、各行末に,を忍ばせておくという心憎い演出。

const SHEET_INFO でオブジェクトとしてシートの各情報を格納し、それを必要な時に取り出している。

const MESSAGE でslackに投稿する際の文言もここで定型文を用意。

postSlackbot

Before

function postSlackbot({

  //SlackAPIで登録したボットのトークンを設定する
  const token = PropertiesService.getScriptProperties().getProperty('SLACK_TOKEN');

  //ライブラリから導入したSlackAppを定義し、トークンを設定する
  const slackApp = SlackApp.create(token);

  //Slackボットがメッセージを投稿するチャンネルを定義する
  const channelId = "#general";

  //メッセージ内で使用する日付、残営業日数を定義する
  const todayFromSheet = dateMasterSheet.getRange('D2').getDisplayValue();
  const businessDaysRemainingThisMonth = dateMasterSheet.getRange('D28').getValue();
  const businessDaysRemainingInThisYear = dateMasterSheet.getRange('D29').getValue();
  const businessDaysRemainingInThisFiscalYear = dateMasterSheet.getRange('D30').getValue();

  //Slackボットが投稿するメッセージを定義する
  const message = `
今日は ${todayFromSheet} です。
今月の残り営業日数は  あと ${businessDaysRemainingThisMonth} 日です。
今年の残り営業日数は  あと ${businessDaysRemainingInThisYear} 日です。
今年度の残り営業日数は あと ${businessDaysRemainingInThisFiscalYear} 日です。
`

  const iconImage = "http://flat-icon-design.com/f/f_business_23/s256_f_business_23_0bg.png";

  const options = {
    // channelId: slackUserID, //チャンネル名
    botUserName: "GASdeName"//投稿するbotの名前
    // message: statusMessage, //投稿するメッセージ
    bot_icon: iconImage //投稿時に表示されるアイコン
  };

  // console.log(options.botUserName);
  //SlackAppオブジェクトのpostMessageメソッドでボット投稿を行う
  slackApp.postMessage(channelId, message, { username: options.botUserName, icon_url: options.bot_icon });
}

After

'use strict'
function postSlackbot({

  const sheet = new Sheet(SpreadsheetApp.getActiveSpreadsheet().getSheetByName(SHEET_INFO.DATEMASTER.NAME));
  const businessDaysRemainingThisMonth = sheet.getResultsByDicts(SHEET_INFO.DATEMASTER.COLUMN_NAME.TYPE.VALUE.BUSINESSDAYS_REMAINING_THIS_MONTH)[0];
  const businessDaysRemainingInThisYear = sheet.getResultsByDicts(SHEET_INFO.DATEMASTER.COLUMN_NAME.TYPE.VALUE.BUSINESSDAYS_REMAINING_THIS_YEAR)[0];
  const businessDaysRemainingInThisFiscalYear = sheet.getResultsByDicts(SHEET_INFO.DATEMASTER.COLUMN_NAME.TYPE.VALUE.BUSINESSDAYS_REMAINING_THIS_FISCAL_YEAR)[0];
  const strDate = Day.format();
  const replacementLists = [
    [/stringifiedToday/g, strDate],
    [/businessDaysRemainingThisMonth/g, businessDaysRemainingThisMonth],
    [/businessDaysRemainingInThisYear/g, businessDaysRemainingInThisYear],
    [/businessDaysRemainingInThisFiscalYear/g, businessDaysRemainingInThisFiscalYear],
  ];
  const message = StringEx.replaceWithLists(MESSAGE.WORKDAY, replacementLists);
  const sm = new SlackMessage();
  sm.send(message);
}

ライブラリを使わず、新たにクラスを用意しました。
また、globalで色々と書いてます。Enumの整理はまだ試行中だそうです。
メッセージも後から更新する物としてglobaにまとめました。
globalとclassのコードも見て、全体として理解できますね。
メッセージを置き換えるクラスも用意してあるんだぜ、うへ〜。

const sheet = new Sheet で残営業日を計算しているスプレッドシートを取得。getSheetByNameの引数はglobalで定数宣言しているものをドット記法で呼び出し。

const businessDay~ の3行で使われているgetResultsByDicts の引数と配列0 これにより、残営業日の値を取得。

Day.format()は静的メソッドなのでnewせず使える。

const replacementLists で、globalの定型文から、必要部分を置き換えリストを作成。それをStringEx(これも静的メソッド)で置き換えて、最後にSlackMessageで投稿。

class Day

Before

class Day {

  /**
   *  @param {Date} targetDate - 日付
   *  @param {object} dateMasterSheet - シート
   */
  constructor(targetDate = new Date('2022/12/31'), dateMasterSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('日付マスタ')) {
    this.targetDate = targetDate;
    this.dateMasterSheet = dateMasterSheet;
  }

  /**
   * 最終更新日 2022/4/15
   * @param {Date} targetDate = 今日の日付
   * @return {Boolean} 土日祝+指定の休日かどうかを判定 true=営業日
   * 参考
   * https://dev.classmethod.jp/articles/202001-workday-only-gas/
   */

  isWorkday(targetDate = this.targetDate) {

    // console.log({ targetDate });

    // targetDate の曜日を確認、週末は休む (false)
    if (this.isRestDay(targetDate) === true) { return false; }

    // 祝日カレンダーを確認する
    if (this.isJapaneaseHoliday(targetDate) === true) { return false; }

    //会社指定の休日(上記の祝日カレンダには入っていない休日、休暇)スプレッドシートから取得
    if (this.isCompanyHoliday(targetDate) === true) { return false; }

    // 全て当てはまらなければ営業日 (True)
    console.log(`今日は 営業日ですよ`);
    return true;
  }


  /**  targetDate の曜日を確認、週末は休む (true)
  * @param {Date} Date -  今日の日付
  * @return {boolean} boolean - 今日が土日なら true を返す
  */
  isRestDay(targetDate = this.targetDate) {
    const rest_or_work = ["REST""mon""tue""wed""thu""fri""REST"]; // 日〜土
    if (rest_or_work[targetDate.getDay()] == "REST") {
      console.log(`今日は 土日ですよ`);
      return true;
    } else {
      console.log(`今日は 土日じゃないですよ`);
      return false;
    }
  }


  /**
  *  targetDate の曜日を確認、祝祭日は休む (true)
  * @param {Date} Date -  今日の日付
  * @return {boolean} boolean - 今日が祝祭日なら true を返す
  */
  isJapaneaseHoliday(targetDate = this.targetDate) {
    const calJpHolidayUrl = "ja.japanese#holiday@group.v.calendar.google.com";
    const calJpHoliday = CalendarApp.getCalendarById(calJpHolidayUrl);
    if (calJpHoliday.getEventsForDay(targetDate).length != 0) {
      // その日に予定がなにか入っている = 祝祭日 = 営業日じゃない (false)
      console.log(`今日は 祝日ですよ`);
      return true;
    } else {
      console.log(`今日は 祝日じゃないですよ`);
      return false;
    }
  }

  /**
  *  targetDate の曜日を確認、会社の指定休日は休む (true)
  * @param {targetDate} Date -  今日の日付
  * @return {boolean} boolean - 今日が会社の指定休日なら true を返す
  */
  isCompanyHoliday(targetDate = this.targetDate) {
    //会社指定の休日をスプレッドシートから取得
    const winterHoliday1 = dateMasterSheet.getRange('D31').getValue().getTime();
    const winterHoliday2 = dateMasterSheet.getRange('D32').getValue().getTime();
    const winterHoliday3 = dateMasterSheet.getRange('D33').getValue().getTime();

    //会社指定の休日を配列に格納
    const winterHolidays = [winterHoliday1, winterHoliday2, winterHoliday3];

    //winterHolidays に targetDate が含まれていたら true
    if (winterHolidays.includes(targetDate.getTime()) == true) {
      //targetDate が冬季休暇日だったらfalse
      console.log(`今日は 冬季休日ですよ`);
      return true;
    } else {
      console.log(`今日は 冬季休日じゃないですよ`);
      return false;
    }
  }


}

After


'use strict'

/**
 * 日付に関するクラス
 * 
 * method
 * 1 isWorkday
 * 2 isRestDay
 * 3 isJapaneseHoliday
 * 4 isCompanyHoliday
 * 
 */
class Day {

  /**
   * @param {Date|string|number|...number} date - Date オブジェクトでインスタンス生成可能な引数
   */
  constructor(...args) {
    this.date = new Date(...args);
  }

  /**
   * @param {Date} date = 今日の日付
   * @return {Boolean} 土日祝+指定の休日かどうかを判定 true=営業日
   * NOTE: 参考 URL https://dev.classmethod.jp/articles/202001-workday-only-gas/
   */
  isWorkday(date = this.date) {
    if (this.isRestDay(date)) return false;
    if (this.isJapaneaseHoliday(date)) return false;
    return this.isCompanyHoliday(date) === false;
  }

  /**
   * date の曜日を確認、週末は休む (true)
   * @param {Date} Date -  今日の日付
   * @return {boolean} boolean - 今日が土日なら true を返す
   */
  isRestDay(date = this.date) {
    return date.getDay() % 6 === 0;  // HACK: 土曜 6, 日曜 0
  }

  /**
   * date の曜日を確認、祝祭日は休む (true)
   * @param {Date} Date -  今日の日付
   * @return {boolean} boolean - 今日が祝祭日なら true を返す
   */
  isJapaneaseHoliday(date = this.date) {
    const calJpHolidayUrl = "ja.japanese#holiday@group.v.calendar.google.com";
    const calJpHoliday = CalendarApp.getCalendarById(calJpHolidayUrl);
    return calJpHoliday.getEventsForDay(date).length !== 0;
  }

  /**
   * date の曜日を確認、会社の指定休日は休む (true)
   * @param {date} Date -  今日の日付
   * @return {boolean} boolean - 今日が会社の指定休日なら true を返す
   */
  isCompanyHoliday(date = this.date) {
    const sheet = new Sheet(SpreadsheetApp.getActiveSpreadsheet().getSheetByName(SHEET_INFO.DATEMASTER.NAME));
    const results = sheet.getResultsByDicts(SHEET_INFO.DATEMASTER.COLUMN_NAME.TYPE.VALUE.WINTER_HOLIDAY);
    const winterHolidayTimes = results.map(date => date.getTime());
    return winterHolidayTimes.includes(date.getTime()) === true;
  }

  /**
   * 指定のフォーマットで日時を文字列化する静的メソッド
   * @param {Date} d - Date オブジェクト 文字列型も可
   * @param {string} format - フォーマットする形式
   * @param {boolean} hasJDay - 日本語表記の曜日を追加するかどうか
   * @return {string} フォーマットされた文字列型の日時
   */
  static format(d = new Date(), format = 'yyyy/MM/dd', hasJDay = true) {
    const date = new Date(d);
    const strDate = Utilities.formatDate(date, 'JST', format) + (hasJDay ? '(' + '日月火水木金土日'[date.getDay()] + ')' : '');
    return strDate;
  }

}

constructor

まず、constructorである。

Before

  constructor(targetDate = new Date('2022/12/31'), dateMasterSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('日付マスタ')) {
    this.targetDate = targetDate;
    this.dateMasterSheet = dateMasterSheet;
  }

After

  /**
   * @param {Date|string|number|...number} date - Date オブジェクトでインスタンス生成可能な引数
   */
  constructor(...args) {
    this.date = new Date(...args);
  }

当初、シートの情報を呼び出そうと思ってconstructorに入れていたが、この情報を使っているのはこのクラス内の特定のメソッドだけだった。なのでconstructorにそれを持たせる必要はなく、そのメソッドで

const sheet = new Sheet(SpreadsheetApp.getActiveSpreadsheet().getSheetByName(SHEET_INFO.DATEMASTER.NAME));

のようにすれば良い。

次に当初の引数に持たせていた new Date()これ、えーと、どう説明したらいいんだ。

まずはこのリファレンス
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Date


文字列や数値を引数に持たせてもnew Dateできるよねっていうことだと雑に理解。

あ〜残余引数とか可変長引数はまだよくわかってないな〜〜〜

isWorkday

Before

 isWorkday(targetDate = this.targetDate) {

    // console.log({ targetDate });

    // targetDate の曜日を確認、週末は休む (false)
    if (this.isRestDay(targetDate) === true) { return false; }

    // 祝日カレンダーを確認する
    if (this.isJapaneaseHoliday(targetDate) === true) { return false; }

    //会社指定の休日(上記の祝日カレンダには入っていない休日、休暇)スプレッドシートから取得
    if (this.isCompanyHoliday(targetDate) === true) { return false; }

    // 全て当てはまらなければ営業日 (True)
    console.log(`今日は 営業日ですよ`);
    return true;
  }


After

 /**
   * @param {Date} date = 今日の日付
   * @return {Boolean} 土日祝+指定の休日かどうかを判定 true=営業日
   * NOTE: 参考 URL https://dev.classmethod.jp/articles/202001-workday-only-gas/
   */
  isWorkday(date = this.date) {
    if (this.isRestDay(date)) return false;
    if (this.isJapaneaseHoliday(date)) return false;
    return this.isCompanyHoliday(date) === false;
  }

あ〜らスッキリ3行に。
確かに、コード以外のものは、ドキュメンテーションコメントに書くか、書くとしてもコードの後に置いた方が見通しがいい。

そして、ifとreturn の使い方、書き方。
this.isRestDayはBooleanを返すので、暗黙の型変換としてifの中にそのまま書いちゃっていい。isHogehogeはBooleanを返すメソッド名。
return { false; }の波括弧{}も省略して良い。
最後は、二つのifを抜けた先なのでこの形、あれ、あってるこれ?


isRestDay

Befo


  isRestDay(targetDate = this.targetDate) {
    const rest_or_work = ["REST""mon""tue""wed""thu""fri""REST"]; // 日〜土
    if (rest_or_work[targetDate.getDay()] == "REST") {
      console.log(`今日は 土日ですよ`);
      return true;
    } else {
      console.log(`今日は 土日じゃないですよ`);
      return false;
    }
  }

After


  /**
   * date の曜日を確認、週末は休む (true)
   * @param {Date} Date -  今日の日付
   * @return {boolean} boolean - 今日が土日なら true を返す
   */
  isRestDay(date = this.date) {
    return date.getDay() % 6 === 0;  // HACK: 土曜 6, 日曜 0
  }

これ、すごくない?1行になった。
0=日曜 6=土曜だから、getDayでその日の数字取れたらそれを判定、6で割り切れたら0、0を6で割っても0だから、この一文で書けるって寸法よ。
こういうちょっとクセのあるものは//HACK とコメントを書くそうです。


isJapaneaseHoliday

Before

/**
 *  targetDate の曜日を確認、祝祭日は休む (true)
 * @param {Date} Date -  今日の日付
 * @return {boolean} boolean - 今日が祝祭日なら true を返す
 */
function isJapaneaseHoliday(targetDate) {
  const calJpHolidayUrl = "ja.japanese#holiday@group.v.calendar.google.com";
  const calJpHoliday = CalendarApp.getCalendarById(calJpHolidayUrl);
  if (calJpHoliday.getEventsForDay(targetDate).length != 0) {
    // その日に予定がなにか入っている = 祝祭日 = 営業日じゃない (false)
    console.log(`今日は 祝日ですよ`);
    return true;
  };
}

After

  /**
   * date の曜日を確認、祝祭日は休む (true)
   * @param {Date} Date -  今日の日付
   * @return {boolean} boolean - 今日が祝祭日なら true を返す
   */
  isJapaneaseHoliday(date = this.date) {
    const calJpHolidayUrl = "ja.japanese#holiday@group.v.calendar.google.com";
    const calJpHoliday = CalendarApp.getCalendarById(calJpHolidayUrl);
    return calJpHoliday.getEventsForDay(date).length !== 0;
  }

taegetDAteをdateに変更、統一して、
this.dateにして、
returnの判定を != から !== にしたくらいかな。


isCompanyHoliday

Before

/**
*  targetDate の曜日を確認、会社の指定休日は休む (true)
* @param {Date} Date -  今日の日付
* @return {boolean} boolean - 今日が会社の指定休日なら true を返す
*/
function isCompanyHoliday(targetDate) {
  //会社指定の休日をスプレッドシートから取得
  const winterHoliday1 = dateMasterSheet.getRange('D31').getValue().getTime();
  const winterHoliday2 = dateMasterSheet.getRange('D32').getValue().getTime();
  const winterHoliday3 = dateMasterSheet.getRange('D33').getValue().getTime();

  //会社指定の休日を配列に格納
  const winterHolidays = [winterHoliday1, winterHoliday2, winterHoliday3];

  //winterHolidays に targetDate が含まれていたら true
  if (winterHolidays.includes(targetDate.getTime()) == true) {
    //targetDate が冬季休暇日だったらfalse
    console.log(`今日は 冬季休日ですよ`);
    return true;
  }
}

After

  /**
   * date の曜日を確認、会社の指定休日は休む (true)
   * @param {date} Date -  今日の日付
   * @return {boolean} boolean - 今日が会社の指定休日なら true を返す
   */
  isCompanyHoliday(date = this.date) {
    const sheet = new Sheet(SpreadsheetApp.getActiveSpreadsheet().getSheetByName(SHEET_INFO.DATEMASTER.NAME));
    const results = sheet.getResultsByDicts(SHEET_INFO.DATEMASTER.COLUMN_NAME.TYPE.VALUE.WINTER_HOLIDAY);
    const winterHolidayTimes = results.map(date => date.getTime());
    return winterHolidayTimes.includes(date.getTime()) === true;
  }

いわゆるマジックナンバーとも呼ばれる、シートの固有の番地はここで制御しないでglobalで扱うようにした。

static format

Before

なし

After

  /**
   * 指定のフォーマットで日時を文字列化する静的メソッド
   * @param {Date} d - Date オブジェクト 文字列型も可
   * @param {string} format - フォーマットする形式
   * @param {boolean} hasJDay - 日本語表記の曜日を追加するかどうか
   * @return {string} フォーマットされた文字列型の日時
   */
  static format(d = new Date(), format = 'yyyy/MM/dd', hasJDay = true) {
    const date = new Date(d);
    const strDate = Utilities.formatDate(date, 'JST', format) + (hasJDay ? '(' + '日月火水木金土日'[date.getDay()] + ')' : '');
    return strDate;
  }

Utilities.formatDateしたときに、何もしないと曜日表示が英語になるので、日本語になるようにしている、こういうのもさらっと書いちゃうんだから、etauさんすごいわ。悔しい。

class Sheet

ちょっといっぱいありすぎるので割愛する。すまん、疲れた。


class SlackAPI改め class SlackMessage

Before

( -﹏- ).。இ 

After

'use strict'

/**
 * slack メッセージ送信に関するクラス
 */
class SlackMessage {

  /**
   * slack のメッセージ送信に関するコンストラクタ
   * @constructor
   */
  constructor() {
    /** @type {string} */
    this.webhookUrl = PropertiesService.getScriptProperties().getProperty('WEBHOOK_URL');
  }

  /**
   * slack にメッセージを送信する
   * @param {string} message - slack に投稿するメッセージ
   * @param {boolean} isChannelMention - チャンネルメンションをつけるかどうか。デフォルト引数は「false」
   */
  send(message, isChannelMention = false) {
    const options = {
      method: 'POST',
      contentType: 'application/json',
      payload: JSON.stringify({
        text: isChannelMention ? '<!channel>\n' + message : message
      })
    };
    UrlFetchApp.fetch(this.webhookUrl, options);
  }

  /**
   * Webhook URL をセットする静的メソッド
   * NOTE: class properties がある場合は不要
   */
  static setWebhookUrl(webhookUrl) {
    PropertiesService.getScriptProperties().setProperty('WEBHOOK_URL', webhookUrl);
  }

}


結局、ライブラリは使わず、こっちで。

class StringEx

Before

なし

After

'use strict'

class StringEx {

  /**
   * 置換リストにしたがって置換する静的メソッド
   * @param {string} string - 置換対象の文字列
   * @param {Array.<Array.<RegExp|string>>} replacementLists - 置換リスト
   * @return {string} 置換後の文字列
   * NOTE: replacementLists は [[/hoge/g, 'HOGE']] のようなもの
   */
  static replaceWithLists(string, replacementLists) {
    const replaced = replacementLists.reduce((pre, list) => pre.replace(...list), string);
    return replaced;
  }

}

postslackのファンクション内でglobalで設定したメッセージに置き換えて投稿するときに使っています。
ドキュメンテーションコメントも見事。
このへんの反復メソッドの理解、まだ甘いんだよな〜…

静的メソッド

classにおいて、基本的には、methodでthisが使われていれば、クラスに関係したmethodということで適したものと見ることができる。
しかし、例外的に、

考えてみると、SpreadsheetApp.getActiveSpreadsheet() なんかも内部的な静的メソッドなのかもしれない。
うーん、この辺、まだもうちょいなんかこう、理解と実践が必要っぽい。


まとめ

etauさんのクラスは理解して使うと便利だけど悔しい。
ペアプロ超勉強になるけど、脳味噌の容量と処理速度が追いつかない。
実際に自分で作りたいものでうんうん悩むと身に付くような気がする。

https://github.com/etau

残余引数、可長変引数、静的メソッドのあたり、もうちょい色々やってみる必要があるな。
そもそもif文とかreturnも自信無くなってきた。

疲れたから風呂入ろっと。


は〜辛いけどたのし〜

#GAS
#Slack
#ノンプロ研
#ペアプロ
#etau


いただいたサポートで、書籍代や勉強費用にしたり、美味しいもの食べたりします!