B-Teck!

お仕事からゲームまで幅広く

【GAS】SpreadSheetの表をjsonに変換し、ZIPで固めてGoogleDriveに配置する

静的なjsonを作成して配信する必要があり、メンテの楽なスプレッドシートの表から作れないかと思ってやってみたメモです。

JSONを作る

GASはほぼJavaScriptなので、 JSON.stringify が使えます。

JSON.stringify({ x: 5, y: 6 });
// expected output: "{"x":5,"y":6}"

そのため、まずstringifyできるobjectを作ります。
実装はちょっと変えたけど基本この記事ママです。
コピペでスプレッドシートをJSON形式のAPIにする方法 - Qiita

/**
 * 与えられたシート名からシートを取得し、表からjsonに変換可能なオブジェクトを生成して返却する
 * @param sheetName 
 */
function getData(sheetName) {
    const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName);
    const rows = sheet.getDataRange().getValues();
    const keys = rows.splice(0, 1)[0];

    return rows.map(row => {
        var obj = {}
        row.forEach((item, index) => { obj[keys[index]] = item; });
        return obj;
    });
}

この処理で返ってきたオブジェクトをJSON.stringifyに通すと

id name
001 hoge
002 fuga

みたいな表がこういうオブジェクトになります。

{
    [
        {
            "id": "001",
            "name": "hoge"
        },
        {
            "id": "002",
            "name": "fuga"
        }
    ]
}

keyをつけてstringifyしてあげると、トップレベルにkey名がついて取り回しがよくなります

const json = JSON.stringify({ jsonKey: getData(sheetName) });
  • keyのついたjson
{
    "jsonKey": [
        {
            "id": "001",
            "name": "hoge"
        },
        {
            "id": "002",
            "name": "fuga"
        }
    ]
}

Zipに圧縮する

GASには Utilities.zip があるのでこれを使います。

blobsをつくる

Utilities.zipはblobsを渡してやる必要があるので、blobの配列を作成します。
Utilities.newBlobで作成できます。
json以外を作成するときはcontent-typeを適切なものにしてください。
また、このときファイル名を / 区切りにすると、Zip圧縮時にそのディレクトリごと作成されます。

const blobs = new Array();
blobs.push(Utilities.newBlob(json, 'application/json', 'hoge/fuga.json'));

ZIPをつくる

作成したblobsをUtilities.zipに渡してやれば完成です。

const fileName = 'archive.zip';
const zip = Utilities.zip(blobs, fileName);

Driveに配置する

何も考えずにファイルを作成するなら DriveApp.createFile すればよいです。
ただ、これで作ると別IDの新しいファイルが作成されるので、実行するたびに同名の新しいファイルが作られます。
なので今回は、
* 特定のフォルダに出力する。なければ作成する。
* 常に同じファイル名で出力する。すでに存在すれば削除する。 という方針で実装しました。

フォルダを取得する

DriveApp.getFoldersByName を使うと同名フォルダの一覧が取れるので、同じフォルダ名は存在しないという強い意志で、最初に取れたやつを返します。
前述したように、同名のフォルダ・ファイルは内部的なIDが異なるだけで普通に存在できてしまうので、諦めましょう。 なかったら新しく作ります。

/**
 * 指定したフォルダ名がすでに存在していればそのフォルダを、
 * 存在しなければ作成したフォルダのオブジェクトを返す
 * @param folderName 
 */
function getOrCreateTempFolder(folderName) {
    // 未作成なら作成してフォルダを取得する
    const folders = DriveApp.getFoldersByName(folderName);
    if (folders.hasNext()) return folders.next()
    else return DriveApp.createFolder(folderName);
}

ファイルを作成する

getOrCreateTempFolder で取得したFolderのオブジェクトと、作成したZIPファイルを渡して配置します。
フォルダと同様に同名ファイルが存在しないという強い意志をもって取得してremoveしています。

/**
 * 与えられたオブジェクトをファイル化して指定したフォルダ内に配置する
 * 
 * @param folder 
 * @param fileName 
 */
function createFile(folder, file) {
    const files = folder.getFilesByName(file.getName());
    if (files.hasNext()) folder.removeFile(files.next());
    return folder.createFile(file);
}

まとめ

これらの処理を一通りつなげて書いたものがこちらです。

function doCreateZip() {
    const sheetName = 'sample';
    const jsonKey = 'sample_json';
    const json = JSON.stringify({ jsonKey: getData(sheetName) });

    // 圧縮用のblob作成
    const blobs = new Array();
    blobs.push(Utilities.newBlob(json, 'application/json', 'hoge/fuga.json'));
  
    const fileName = 'archive.zip';
    const zip = Utilities.zip(blobs, fileName);
  
  
    const folderName = 'temporary';
    const folder = getOrCreateTempFolder(folderName);
    const file = createFile(folder, zip);
}

/**
 * 与えられたシート名からシートを取得し、表からjsonに変換可能なオブジェクトを生成して返却する
 * @param sheetName 
 */
function getData(sheetName) {
    const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName);
    const rows = sheet.getDataRange().getValues();
    const keys = rows.splice(0, 1)[0];

    return rows.map(row => {
        const obj = {}
        row.forEach((item, index) => { obj[keys[index]] = item; });
        return obj;
    });
}

/**
 * 指定したフォルダ名がすでに存在していればそのフォルダを、
 * 存在しなければ作成したフォルダのオブジェクトを返す
 * @param folderName 
 */
function getOrCreateTempFolder(folderName) {
    // 未作成なら作成してフォルダを取得する
    const folders = DriveApp.getFoldersByName(folderName);
    if (folders.hasNext()) return folders.next()
    else return DriveApp.createFolder(folderName);
}

/**
 * 与えられたオブジェクトをファイル化して指定したフォルダ内に配置する
 * 
 * @param folder 
 * @param fileName 
 */
function createFile(folder, file) {
    const files = folder.getFilesByName(file.getName());
    if (files.hasNext()) folder.removeFile(files.next());
    return folder.createFile(file);
}

これを実行すると、こういう感じになります。便利ですね。
f:id:beatdjam:20200830060410p:plain:w300
f:id:beatdjam:20200830060514p:plain:w300
f:id:beatdjam:20200830060610p:plain:w300

【JavaScript/jQuery】We don't need jQuery?

昔書いてたメモが出てきたので、今更ですが供養。
正直今でも作り捨てとか、とりあえず作るならjQuery使ってもいいんじゃないかな?って思ってる。

jQueryとはなんだったのか

  • ブラウザ間でJavaScriptの実装が大きく異なっていた時代に、共通の実装が行えるように差異を吸収していたライブラリ。
    • ぶっちゃけ今でも差異はあるけど頑張れる範囲になってきた
  • 簡潔な実装からWebFront界隈で広く利用されており、jQueryに依存するライブラリも多数存在した。

なぜjQueryをやめるのか

不要なライブラリを読み込むことによる動作遅延

  • ただし70〜100KB前後なので、現在の通信環境ではほぼ問題なさそう

動作が遅い

  • jQueryはブラウザ間の差異を吸収するようにできているため、現代のブラウザには不要な分岐がある。
  • jQueryオブジェクト等の生成のコストもあり、現在の環境では不必要な処理が多い。
    • ついでにDOMを直接触るため、最近の仮想DOMを用いるフレームワークとの親和性が低い

jQueryでできていたことが、ES自体の仕様として取り込まれつつある

この辺参考
- jQueryのfindなどをquerySelectorで書く - Qiita
- You Don't Need jQuery - Qiita

jQueryで指定していたアニメーションがCSSで実現できるようになった

Pros/Cons

Pros

  • 不要なファイル(jQuery本体)を読み込まなくて良くなる
  • 本来必要ではない処理コストを削減できる
    • 標準APIに移行することでjQueryへのロックインがなくなり、他のフレームワークなどと組み合わせやすい
    • 近年流行っている仮想DOMとjQueryはだいぶ相性が悪い
  • スクリプトで行うべきこと、CSSで行うべきことの責務が明確になる

Cons

  • なんだかんだいってもjQueryの互換性担保は魅力
    • 現状、IE対応でPolyfillやBabelによるトランスパイルが必要となるケースはまだある
  • jQueryの方が記述が簡潔になるケースも多い
  • 長い間使われてきているプラグインにはjQuery必須のものもある
  • 小規模の開発ではjQuery使ってさっくり作るほうが良いこともある
    • VueやReactを入れるほどではない開発規模とか、標準APIだとまだ冗長になる部分とか

【JavaScript/Vue.js】Vue.jsとVuetifyを使ってTodoリストを作ってみた

f:id:beatdjam:20190629195739p:plain

ちょっと会社でVueを触る機会があったので、勉強がてらTodoリストを作ってみました。
リポジトリと動作サンプルは下記
GitHub - beatdjam/training_vue-cli-todo
サンプルページ

やったこと

vue-cliインストール

$ npm install -g @vue/cli

プロジェクト作成

$ vue create my-project

Vuetify導入

$ vue add vuetify

ファイル整理

Todoを作り始めるにあたっていらないファイルを削除
[remove]Unnecessary files removed · beatdjam/training_vue-cli-todo@3fb851f · GitHub

完成イメージを考える

適当にUIイメージをメモに書き出すなど

必要なComponentをVuetifyのサイトで検索して配置

とりあえず今回はこの辺のドキュメントをサラッと眺めました

  • v-toolbar
  • v-form
  • v-text-field
  • v-btn
  • v-date-picker
  • v-list
  • v-checkbox
  • v-icon

Quick Start — Vuetify.js

追加・削除の処理などを実装

コンポーネントに頼ったので、ロジック的には追加・削除しか書かずに実装できました。

Github Pagesに公開

下記のページを参考に設定
https://www.shookuro.com/entry/2019/02/02/174655

感想

Vue.jsの機能とVuetifyのコンポーネント群を利用することで、
曖昧な理解でも簡単にそれなりのアプリケーションを構築することができてしまいました。
小さめのプロダクトをスモールスタートするときに使うには良いと思います。
ただ、jQuery等と同様に、裏側の処理等を知らないまま負債を積み上げてしまうような気がしました。
最近 Vue.js入門 基礎から実践アプリケーション開発まで を購入したので、
読み進めながらもっと勉強していきたいと思います。

Vue.js入門 基礎から実践アプリケーション開発まで

Vue.js入門 基礎から実践アプリケーション開発まで

  • 作者: 川口和也,喜多啓介,野田陽平,手島拓也,片山真也
  • 出版社/メーカー: 技術評論社
  • 発売日: 2018/09/22
  • メディア: 単行本(ソフトカバー)
  • この商品を含むブログを見る