株式会社スマレジの開発部でスマレジのサーバサイドを作っています

Redmine APIをGASで実行して、ガントチャートを同期する(4: claspとTypeScriptを導入する)

こんばんは!株式会社スマレジ、開発部のmasaです。 コロナの影響で、スマレジでもテレワークを導入しておりますが、 皆様の会社はいかがでしょうか?

顔を合わせないで仕事をするのは難しい部分もありますが、 感染拡大防止のため、協力していきましょうー!

というわけで、今回は「お家で一緒にやってみよう」ではありませんが、 GASの開発を本格化するため、claspを導入します。

claspを導入する。

claspはgoogleが提供している、GASをローカルで開発するための認証基盤と命令セットです。 公式GitHub github.com

ローカルで開発するメリットとしては以下のようなものがあります。

  1. TypeScriptのような、AltJS系を利用できる。
  2. webpackを用いれば、MVC用途ごとにフォルダを切って整理することができる。(現状、ブラウザの開発環境では階層を作ることができないっぽい)
  3. Lintを使うことができる。

で、すでに導入方法はいろんなところで説明しています。masaは↓を見て導入しました。 AltJSは学生時代にCoffee Scriptを触って以来、ふれてこなかったので、TypeScriptも勉強の意味で導入しました。

medium.com

イメージのわきにくい方は、下記の構成イメージをご参照ください。

f:id:masa2019:20200419201031p:plain
clasp,TypeScript,webpackの関係

TypeAcriptでコーディングして、webpackでjsに変換して、claspで認証してソースをサーバにあげるという流れです。

セルの値を相対参照で取得する

さて、環境はできたので、リファクタリングとclaspの動作確認もかねて少しだけ基本部品を作成します。 VBAやGASを触っていてよくあるのが、「シートの構成が変わったから参照するセルの位置がずれた」というやつです。 特にGASでツール化するときは社内の多くの人が使うことが多いので、利用用途に応じてフォーマットもちょいちょい変わりがち。 「フォーマット?考えながら作ってよ。ちょいちょい変えてくれればいいからさー」という要望にすぐに対応できるときとできないときが、 構成によっては発生します。

そういう手戻りを回避するために、いわゆる"A3"とか"B5:C3"のような決め打ちのセル指定ではなく、 「○○からみて、右に3つ、下に2つ」のような相対参照をする機能を考えます。

そして、実際に作成したものがこちら。(3分コーディング?)

まずは、相対参照・絶対参照の値を保持するオブジェクト。

/**
 * 位置種別クラス
 * relativeかabsoluteの二値のみ保持する。
 * Cellクラスで位置判定をするためのValue object
 * @class PositionType
 */
export class PositionType {
  public position: string;
  public static readonly RELATIVE: string = "relative";
  public static readonly ABSOLUTE: string = "absolute";

  constructor(position?: string) {
    if (
      position === PositionType.RELATIVE ||
      position === PositionType.ABSOLUTE
    ) {
      this.position = position;
    } else {
      this.position = PositionType.ABSOLUTE;
    }
  }
  isRelative() {
    return this.position === PositionType.RELATIVE;
  }
  isAbsolute() {
    return this.position === PositionType.ABSOLUTE;
  }
}

次にセルの位置情報を持つオブジェクト。 ここに参照区分を持たせる。

import { PositionType } from "./PositionType";

/**
 * シートのセルの基本クラス
 * 位置情報として、相対参照と絶対参照を持つ
 * 相対参照の場合は、相対元となる値が必須.
 * 相対元の値がシート状で複数回現れる場合は、何番目に現れるのかをindexに指定.
 * 相対元の値の検索は行ごとに1行目から行う.
 * @class Cell
 */
export class Cell {
  public x: number;
  public y: number;
  public positionType: PositionType;
  public label: string;
  public index: number;
  constructor(
    x: number,
    y: number,
    position?: string,
    label?: string,
    index?: number
  ) {
    this.x = x;
    this.y = y;
    this.positionType = new PositionType(position);
    if (this.positionType.isAbsolute() === true) {
      this.label = "CAN'T USE";
      this.index = -1;
    } else {
      if (label === undefined || label.length === 0) {
        throw new Error(
          "You must set the label of this value if you want to define the position relatively."
        );
      } else {
        this.label = label;
        this.index = index === undefined ? 0 : index;
      }
    }
  }
}

でシート一枚を表すクラス(現在作成中)に下記のセルの値を 取得するメソッドを足してあげる。 相対と絶対で参照するセルを分ける。

  read(cell: Cell) {
    if (cell.positionType.isAbsolute()) {
      return this.dataList[cell.y][cell.x] + cell.index.toString();
    }
    let index = 0;
    let currentY = 0;
    let currentX = 0;
    for (const line of this.dataList) {
      for (const value of line) {
        if (value === cell.label) {
          if (index === cell.index) {
            return (
              this.dataList[currentY + cell.y][currentX + cell.x] +
              cell.index.toString()
            );
          }
          index++;
        }
        currentX++;
      }
      currentX = 0;
      currentY++;
    }
    throw new Error(cell.label + "is not found.");
  }

とまあこんな感じにしておいて、相対参照を組み合わせてロジックを組めば、少しは変化に強くなっている・・・はず。