├── .gitignore ├── .jshintrc ├── .npmignore ├── .travis.yml ├── LICENSE ├── Makefile ├── README.md ├── TODO.md ├── bin └── google-docs-cms ├── lib └── main.js ├── package.json ├── planning ├── google-spreadsheets-feeds-sample-json.md ├── gsheets-api-sample-json │ ├── cms.xlsx │ ├── googleapis.com.json │ └── spreadsheets.api.json └── test-tabletop.js ├── test ├── fixtures │ └── src │ │ ├── combined.json │ │ ├── index.json │ │ └── tabs │ │ ├── achievements.json │ │ ├── biography.json │ │ ├── clients.json │ │ ├── contact.json │ │ ├── icons.json │ │ ├── main.json │ │ └── projects.json ├── helpers │ └── helpers.js ├── main.test.js └── mocks │ └── tabletop.js └── testem.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | temp.js 3 | .sauce_connect.log* 4 | *.sublime* 5 | *.log* 6 | res.json 7 | expected.json -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "strict": false, 3 | "laxcomma": true, 4 | "expr": true, 5 | "maxlen": 400, 6 | "eqnull": true, 7 | "sub": true, 8 | "multistr": true, 9 | "white": false, 10 | "quotmark": false, 11 | "smarttabs": true, 12 | "camelcase": false, 13 | "plusplus": false, 14 | "curly": true, 15 | "indent": false, 16 | "nomen": false, 17 | "latedef": true, 18 | "newcap": false, 19 | "undef": true, 20 | "node":true, 21 | "browser":true, 22 | "predef": { 23 | "d3": true, 24 | "_": true, 25 | "$": true, 26 | "sjcl": true, 27 | "Cookies": true, 28 | "CONFIG": true, 29 | "MyError": true, 30 | "google": true, 31 | "spyOn": true, 32 | "AsyncSpec": true, 33 | "topojson": true, 34 | "inject": true, 35 | "expect": true, 36 | "Globals": true, 37 | "angular": true, 38 | "Modernizr": true, 39 | "Highcharts": true, 40 | "describe": true, 41 | "it": true, 42 | "before": true, 43 | "beforeEach": true, 44 | "afterEach": true, 45 | "after": true, 46 | "suite": true, 47 | "test": true, 48 | "module": true, 49 | "iScroll": true, 50 | "setTimeout": true, 51 | "document": true, 52 | "WebKitCSSMatrix": true, 53 | "Backbone": true, 54 | "console": true, 55 | "window": true, 56 | "define": true, 57 | "require": true, 58 | "backbone": true 59 | } 60 | } -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | planning 2 | TODO.md -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - 0.8 5 | 6 | script: npm test 7 | 8 | before_script: 9 | - npm install -g gulp 10 | - npm install 11 | 12 | matrix: 13 | allow_failures: 14 | - node_js: 0.6 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Andrew Griffiths 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | publish: clean test lint 2 | 3 | lint: 4 | @./node_modules/.bin/jshint lib 5 | 6 | test: clean node_modules 7 | @mkdir -p test/fixtures/tmp 8 | @NODE_ENV=test ./node_modules/.bin/mocha --reporter tap --timeout 2000 test/*.test.js 9 | 10 | node_modules: package.json 11 | @npm install 12 | 13 | clean: 14 | @rm -f *.log* 15 | @rm -f expected.json 16 | @rm -f res.json 17 | @rm -rf test/fixtures/tmp/* 18 | 19 | .PHONY: clean test lint -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # google-docs-cms 2 | 3 | Google docs is your CMS! 4 | 5 | - Creates a JSON object of your google spreadsheet. 6 | - Combines all tabs together with the main sheet into a single json object with the tab names as keys. 7 | - Writes data to disc (optional). 8 | 9 | 10 | ## Install 11 | 12 | ```Shell 13 | npm install -g google-docs-cms 14 | ``` 15 | 16 | ## Usage 17 | 18 | 19 | ### CLI 20 | 21 | ```Shell 22 | google-docs-cms 'spreadsheet-id' 'path-to-write-data.json' 23 | ``` 24 | 25 | ### JavaScript 26 | 27 | ```JavaScript 28 | var googleDocsCms = require('google-docs-cms'); 29 | 30 | googleDocsCms({ 31 | id: 'your-google-spreadsheet-id', 32 | outPath: path.join('where', 'to', 'write', 'data') 33 | }, function(err, res) { 34 | console.log(res); // {tabNameOne: [{...}], tabNameTwo: [{...}]} 35 | console.log(require(path.join('where', 'to', 'write', 'data'))); // {tabNameOne: [{...}], tabNameTwo: [{...}]} 36 | }); 37 | 38 | // also has a promise API 39 | googleDocsCms({...}).then(function(res) {}, function(err), {}); 40 | ``` 41 | 42 | ## API 43 | 44 | ### Options {object} 45 | 46 | ### Options.id {string} (Required) 47 | Google spreadsheet id 48 | 49 | ### Options.outPath {string} (Optional) 50 | Path to the file that you want the data to be written to. 51 | 52 | 53 | 54 | -------------------------------------------------------------------- 55 | 56 | ## Tests 57 | 58 | ```Shell 59 | npm install && npm test 60 | ``` -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | ### CLI API 2 | 3 | ```Shell 4 | google-docs-cms input.json output.json 5 | 6 | google-docs-cms -h 7 | Argments 8 | -1 Path to json file containing urls of google docs spreadsheets 9 | -2 Path to file to output results 10 | ``` 11 | 12 | 13 | 14 | ================================================================================================= 15 | ================================================================================================= 16 | 17 | 18 | https://www.googleapis.com/drive/v2/files/fileId 19 | https://www.googleapis.com/drive/v2/files/1HvEMxPVMfUml-tGgaaD0xjyKnpKykGY3gxGgBxSlaM8 20 | 21 | GET https://www.googleapis.com/drive/v2/files/1HvEMxPVMfUml-tGgaaD0xjyKnpKykGY3gxGgBxSlaM8?projection=BASIC&key={YOUR_API_KEY} 22 | 23 | X-JavaScript-User-Agent: Google APIs Explorer 24 | 25 | https://docs.google.com/spreadsheets/d/1HvEMxPVMfUml-tGgaaD0xjyKnpKykGY3gxGgBxSlaM8/pubhtml 26 | 27 | ## https://spreadsheets.google.com/feeds/ 28 | - node-google-spreadsheets 29 | - node-google-spreadsheet 30 | - g-spreadsheet 31 | - google-sheets 32 | - https://spreadsheets.google.com/feeds/spreadsheets/private/full 33 | 34 | Shell 35 | ``` 36 | curl https://spreadsheets.google.com/feeds/list/0Aqglj65pqAwmdEh4a1otT3lmYnN0TGV1Q2JkdndVUnc/od6/public/basic?hl=en_US&alt=json 37 | ``` 38 | 39 | 40 | 41 | 42 | ##### MINE 43 | https://spreadsheets.google.com/feeds/list/1HvEMxPVMfUml-tGgaaD0xjyKnpKykGY3gxGgBxSlaM8/od6/public/basic?hl=en_US&alt=json 44 | 45 | 46 | 47 | ================================================================================================= 48 | ================================================================================================= 49 | ================================================================================================= 50 | ================================================================================================= 51 | 52 | 53 | https://www.googleapis.com/drive/v2/files/fileId 54 | https://www.googleapis.com/drive/v2/files/1HvEMxPVMfUml-tGgaaD0xjyKnpKykGY3gxGgBxSlaM8 55 | 56 | GET https://www.googleapis.com/drive/v2/files/1HvEMxPVMfUml-tGgaaD0xjyKnpKykGY3gxGgBxSlaM8?projection=BASIC&key={YOUR_API_KEY} 57 | 58 | X-JavaScript-User-Agent: Google APIs Explorer 59 | 60 | https://docs.google.com/spreadsheets/d/1HvEMxPVMfUml-tGgaaD0xjyKnpKykGY3gxGgBxSlaM8/pubhtml 61 | 62 | https://spreadsheets.google.com/feeds/list/0Aqglj65pqAwmdEh4a1otT3lmYnN0TGV1Q2JkdndVUnc/od6/public/basic?hl=en_US&alt=json 63 | 64 | 65 | 66 | ##### MINE 67 | https://spreadsheets.google.com/feeds/list/1HvEMxPVMfUml-tGgaaD0xjyKnpKykGY3gxGgBxSlaM8/od6/public/basic?hl=en_US&alt=json 68 | 69 | 70 | 71 | ---------------------------------------------------------------------------------------------------------------------------------- 72 | APIs 73 | ---------------------------------------------------------------------------------------------------------------------------------- 74 | 75 | https://www.googleapis.com/drive/v2/files/fileId 76 | https://www.googleapis.com/drive/v2/files/1HvEMxPVMfUml-tGgaaD0xjyKnpKykGY3gxGgBxSlaM8 77 | 78 | GET https://www.googleapis.com/drive/v2/files/1HvEMxPVMfUml-tGgaaD0xjyKnpKykGY3gxGgBxSlaM8?projection=BASIC&key={YOUR_API_KEY} 79 | https://docs.google.com/spreadsheets/d/1HvEMxPVMfUml-tGgaaD0xjyKnpKykGY3gxGgBxSlaM8/pubhtml 80 | 81 | X-JavaScript-User-Agent: Google APIs Explorer 82 | 83 | 84 | 85 | 86 | ---------------------------------------------------------------------------------------------------------------------------------- 87 | FEED 88 | ---------------------------------------------------------------------------------------------------------------------------------- 89 | 90 | ##### MINE ##### 91 | https://spreadsheets.google.com/feeds/list/1HvEMxPVMfUml-tGgaaD0xjyKnpKykGY3gxGgBxSlaM8/od6/public/values?alt=json 92 | https://spreadsheets.google.com/feeds/list/1HvEMxPVMfUml-tGgaaD0xjyKnpKykGY3gxGgBxSlaM8/od6/public/basic?hl=en_US&alt=json -------------------------------------------------------------------------------- /bin/google-docs-cms: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /* 4 | USAGE 5 | 6 | -> echo data to STDOUT 7 | google-docs-cms spreadsheet-id 8 | 9 | -> write data to file 10 | google-docs-cms spreadsheet-id path-to-output-file.json 11 | */ 12 | 13 | // deps 14 | var fs = require('fs'); 15 | var clc = require('cli-color'); 16 | 17 | // parse args 18 | var args = process.argv.slice(2); 19 | var spreadSheetId = args[0]; 20 | var outputFilePath = args[1]; 21 | 22 | // local lib 23 | var main = require('./../lib/main.js'); 24 | 25 | main({ 26 | id: spreadSheetId, 27 | outPath: outputFilePath 28 | }).then(function (data) { 29 | 30 | console.log((outputFilePath) ? clc.green("[OK]: spreadsheet downloaded " + 'and written to ' + outputFilePath) : JSON.stringify(data)); 31 | 32 | }).error(function (err) { 33 | 34 | console.error(clc.red("[FAIL]: " + err.msg)); 35 | 36 | }); -------------------------------------------------------------------------------- /lib/main.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | var _ = require('underscore'); 3 | var Tabletop = require('tabletop'); 4 | var Promise = require('bluebird'); 5 | 6 | /** 7 | * downloads google sheets data and writes to disk in JSON format 8 | * 9 | * @param {Object} options options object 10 | * @param {Array} options.id id of google spreadsheet to download (visible in the URL or share link) 11 | * @param {Array} options.outPath location of file to write spreadsheet data to 12 | * @param {Function} cb node style cb(null, jsonPayloadIfSuccessful) 13 | * @return {Promise} resolves with combined JSON payload of all tabs combined into a single object with tabnames as keys for each tab data 14 | */ 15 | var Main = function (options, cb) { 16 | 17 | if (!(this instanceof Main)) return new Main(options, cb); 18 | 19 | return this.downloadSpreadsheet(options.id) 20 | .bind(this) 21 | .spread(this.extractTabsJson) 22 | .spread(this.combine) 23 | .then(function (res) { return (this.result = res); }) 24 | // .then(JSON.stringify).then(Promise.promisify(fs.writeFile).bind(fs, options.outPath)) 25 | .then(function (res) { 26 | return (options.outPath == null) ? res : Promise.promisify(fs.writeFile).bind(fs, options.outPath)(JSON.stringify(res)); 27 | }) 28 | .then(function () { return this.result; }) 29 | .nodeify(cb); 30 | }; 31 | 32 | /** 33 | * download spreadhseet from google docs with toptable npm module 34 | * 35 | * @param {string} sheetId id of google spreadsheet (visible in URL or share link) 36 | * @return {Promise} resolves with result [mainJson, tabletop] */ 37 | Main.prototype.downloadSpreadsheet = function (sheetId) { 38 | return new Promise(function (resolve, reject) { 39 | Tabletop.init({ 40 | key: this.generateGoogleSheetUrl(sheetId), 41 | callback: function () { 42 | resolve(Promise.all(Array.prototype.slice.call(arguments))); 43 | }, 44 | parseNumbers: true, 45 | simpleSheet: true 46 | // wanted: true, 47 | // debug: true, 48 | }); 49 | }.bind(this)); 50 | }; 51 | 52 | Main.prototype.extractTabsJson = function (data, tableTop) { 53 | var tabs = tableTop.model_names.sort(); 54 | return [tabs, tabs.map(tableTop.sheets.bind(tableTop))]; 55 | }; 56 | 57 | /** 58 | * combines array of sheets JSON from tabletop into a single JSON object 59 | * 60 | * @param {array} tabNames array of google sheet names from manifest file 61 | * @param {array} tabsJson array of JSON payload from toptable taken from google docs 62 | * @return {Object} single JSON object with all sheets merged into a single object - each sheet is a property on the object 63 | */ 64 | Main.prototype.combine = function (tabNames, tabsJson) { 65 | return tabsJson.reduce(function (memo, row, rowIndex) { 66 | var section = _(row.elements).map(this.filterRowName, this); // remove rowNumber props added by toptable 67 | memo[tabNames[rowIndex]] = section.length === 1 ? section[0] : section; // if only one object in array then take it out of array 68 | return memo; 69 | }.bind(this), {}); 70 | }; 71 | 72 | /*-------------------------------------- 73 | helper methods 74 | ---------------------------------------*/ 75 | Main.prototype.filterKeyName = function (badKeyName, objectDirty) { 76 | return _(objectDirty).reduce(function (mem, val, key) { 77 | (key !== badKeyName) && (mem[key] = val); 78 | return mem; 79 | }, {}); 80 | }; 81 | Main.prototype.filterRowName = Main.prototype.filterKeyName.bind(null, 'rowNumber'); 82 | 83 | Main.prototype.generateGoogleSheetUrl = function (sheetId) { 84 | return 'https://docs.google.com/spreadsheet/pub?hl=en_US&hl=en_US&key=' + sheetId + '&output=html'; 85 | }; 86 | 87 | module.exports = Main; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "google-docs-cms", 3 | "version": "0.0.5", 4 | "license": "MIT", 5 | "description": "Create a JSON payload of data from a google spreadsheet that includes all tabs' data", 6 | "keywords": [ 7 | "google docs", 8 | "google", 9 | "cms" 10 | ], 11 | "author": { 12 | "name": "Andrew Griffiths", 13 | "email": "mail@andrewgriffithsonline.com" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "git@github.com:techjacker/google-docs-cms.git" 18 | }, 19 | "engines": [ 20 | "node >= 0.8.0" 21 | ], 22 | "main": "lib/main.js", 23 | "bin": { 24 | "google-docs-cms": "./bin/google-docs-cms" 25 | }, 26 | "scripts": { 27 | "test": "make test", 28 | "dev": "nodemon lib" 29 | }, 30 | "dependencies": { 31 | "bluebird": "^2.1.3", 32 | "cli-color": "^0.3.2", 33 | "tabletop": "^1.3.5", 34 | "underscore": "~1.5.2" 35 | }, 36 | "devDependencies": { 37 | "bud": "^1.0.4", 38 | "glob": "~3.2.6", 39 | "mocha": "~1.17.1", 40 | "rewire": "^2.0.1", 41 | "should": "~3.1.3" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /planning/google-spreadsheets-feeds-sample-json.md: -------------------------------------------------------------------------------- 1 | https://www.googleapis.com/drive/v2/files/fileId 2 | https://www.googleapis.com/drive/v2/files/1HvEMxPVMfUml-tGgaaD0xjyKnpKykGY3gxGgBxSlaM8 3 | 4 | GET https://www.googleapis.com/drive/v2/files/1HvEMxPVMfUml-tGgaaD0xjyKnpKykGY3gxGgBxSlaM8?projection=BASIC&key={YOUR_API_KEY} 5 | 6 | X-JavaScript-User-Agent: Google APIs Explorer 7 | 8 | https://docs.google.com/spreadsheets/d/1HvEMxPVMfUml-tGgaaD0xjyKnpKykGY3gxGgBxSlaM8/pubhtml 9 | 10 | ## https://spreadsheets.google.com/feeds/ 11 | - node-google-spreadsheets 12 | - node-google-spreadsheet 13 | - g-spreadsheet 14 | - google-sheets 15 | - https://spreadsheets.google.com/feeds/spreadsheets/private/full 16 | 17 | Shell 18 | ``` 19 | curl https://spreadsheets.google.com/feeds/list/0Aqglj65pqAwmdEh4a1otT3lmYnN0TGV1Q2JkdndVUnc/od6/public/basic?hl=en_US&alt=json 20 | ``` 21 | 22 | 23 | 24 | 25 | ##### MINE 26 | https://spreadsheets.google.com/feeds/list/1HvEMxPVMfUml-tGgaaD0xjyKnpKykGY3gxGgBxSlaM8/od6/public/basic?hl=en_US&alt=json 27 | 28 | 29 | 30 | ================================================================================================= 31 | ================================================================================================= 32 | ================================================================================================= 33 | ================================================================================================= 34 | 35 | 36 | https://www.googleapis.com/drive/v2/files/fileId 37 | https://www.googleapis.com/drive/v2/files/1HvEMxPVMfUml-tGgaaD0xjyKnpKykGY3gxGgBxSlaM8 38 | 39 | GET https://www.googleapis.com/drive/v2/files/1HvEMxPVMfUml-tGgaaD0xjyKnpKykGY3gxGgBxSlaM8?projection=BASIC&key={YOUR_API_KEY} 40 | 41 | X-JavaScript-User-Agent: Google APIs Explorer 42 | 43 | https://docs.google.com/spreadsheets/d/1HvEMxPVMfUml-tGgaaD0xjyKnpKykGY3gxGgBxSlaM8/pubhtml 44 | 45 | https://spreadsheets.google.com/feeds/list/0Aqglj65pqAwmdEh4a1otT3lmYnN0TGV1Q2JkdndVUnc/od6/public/basic?hl=en_US&alt=json 46 | 47 | 48 | 49 | ##### MINE 50 | https://spreadsheets.google.com/feeds/list/1HvEMxPVMfUml-tGgaaD0xjyKnpKykGY3gxGgBxSlaM8/od6/public/basic?hl=en_US&alt=json 51 | 52 | 53 | 54 | ---------------------------------------------------------------------------------------------------------------------------------- 55 | APIs 56 | ---------------------------------------------------------------------------------------------------------------------------------- 57 | 58 | https://www.googleapis.com/drive/v2/files/fileId 59 | https://www.googleapis.com/drive/v2/files/1HvEMxPVMfUml-tGgaaD0xjyKnpKykGY3gxGgBxSlaM8 60 | 61 | GET https://www.googleapis.com/drive/v2/files/1HvEMxPVMfUml-tGgaaD0xjyKnpKykGY3gxGgBxSlaM8?projection=BASIC&key={YOUR_API_KEY} 62 | https://docs.google.com/spreadsheets/d/1HvEMxPVMfUml-tGgaaD0xjyKnpKykGY3gxGgBxSlaM8/pubhtml 63 | 64 | X-JavaScript-User-Agent: Google APIs Explorer 65 | 66 | 67 | 68 | 69 | ---------------------------------------------------------------------------------------------------------------------------------- 70 | FEED 71 | ---------------------------------------------------------------------------------------------------------------------------------- 72 | 73 | ##### MINE ##### 74 | https://spreadsheets.google.com/feeds/list/1HvEMxPVMfUml-tGgaaD0xjyKnpKykGY3gxGgBxSlaM8/od6/public/values?alt=json 75 | https://spreadsheets.google.com/feeds/list/1HvEMxPVMfUml-tGgaaD0xjyKnpKykGY3gxGgBxSlaM8/od6/public/basic?hl=en_US&alt=json -------------------------------------------------------------------------------- /planning/gsheets-api-sample-json/cms.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techjacker/google-docs-cms/e2239b333a9f7bdd805cd223995ceea1a039ffea/planning/gsheets-api-sample-json/cms.xlsx -------------------------------------------------------------------------------- /planning/gsheets-api-sample-json/googleapis.com.json: -------------------------------------------------------------------------------- 1 | https://www.googleapis.com/drive/v2/files/1HvEMxPVMfUml-tGgaaD0xjyKnpKykGY3gxGgBxSlaM8?projection=BASIC&key={YOUR_API_KEY} 2 | https://docs.google.com/spreadsheets/d/1HvEMxPVMfUml-tGgaaD0xjyKnpKykGY3gxGgBxSlaM8/pubhtml 3 | 4 | { 5 | "kind": "drive#file", 6 | "id": "1HvEMxPVMfUml-tGgaaD0xjyKnpKykGY3gxGgBxSlaM8", 7 | "etag": "\"sIP8ArR2PAy9qIx8FYkTpbHmKik/MTQwMTQ0Mjk3Njg2Ng\"", 8 | "selfLink": "https://content.googleapis.com/drive/v2/files/1HvEMxPVMfUml-tGgaaD0xjyKnpKykGY3gxGgBxSlaM8", 9 | "alternateLink": "https://docs.google.com/spreadsheets/d/1HvEMxPVMfUml-tGgaaD0xjyKnpKykGY3gxGgBxSlaM8/edit?usp=drivesdk", 10 | "embedLink": "https://docs.google.com/spreadsheets/d/1HvEMxPVMfUml-tGgaaD0xjyKnpKykGY3gxGgBxSlaM8/htmlembed", 11 | "iconLink": "https://ssl.gstatic.com/docs/doclist/images/icon_11_spreadsheet_list.png", 12 | "thumbnailLink": "https://lh5.googleusercontent.com/FdTGbFHvBoZjkHXec8KOAkMAGgDDg9T5pF6wZOrUTBaQjvKmsqWgBR9PwBWTWPVQdHuabH4XKFcHwOH573waXw=s220", 13 | "title": "cms", 14 | "mimeType": "application/vnd.google-apps.spreadsheet", 15 | "labels": { 16 | "starred": false, 17 | "hidden": false, 18 | "trashed": false, 19 | "restricted": false, 20 | "viewed": false 21 | }, 22 | "createdDate": "2014-05-28T18:23:21.490Z", 23 | "modifiedDate": "2014-05-30T09:42:56.866Z", 24 | "version": "15579", 25 | "parents": [{ 26 | "kind": "drive#parentReference", 27 | "id": "0ACPfUp1fLihSUk9PVA", 28 | "selfLink": "https://content.googleapis.com/drive/v2/files/1HvEMxPVMfUml-tGgaaD0xjyKnpKykGY3gxGgBxSlaM8/parents/0ACPfUp1fLihSUk9PVA", 29 | "parentLink": "https://content.googleapis.com/drive/v2/files/0ACPfUp1fLihSUk9PVA", 30 | "isRoot": false 31 | }], 32 | "exportLinks": { 33 | "application/pdf": "https://docs.google.com/spreadsheets/export?id=1HvEMxPVMfUml-tGgaaD0xjyKnpKykGY3gxGgBxSlaM8&exportFormat=pdf", 34 | "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "https://docs.google.com/spreadsheets/export?id=1HvEMxPVMfUml-tGgaaD0xjyKnpKykGY3gxGgBxSlaM8&exportFormat=xlsx" 35 | }, 36 | "userPermission": { 37 | "kind": "drive#permission", 38 | "etag": "\"sIP8ArR2PAy9qIx8FYkTpbHmKik/6E3cHvdaH_dVUYhO-YzJqX07EjI\"", 39 | "id": "me", 40 | "selfLink": "https://content.googleapis.com/drive/v2/files/1HvEMxPVMfUml-tGgaaD0xjyKnpKykGY3gxGgBxSlaM8/permissions/me", 41 | "role": "reader", 42 | "type": "user" 43 | }, 44 | "quotaBytesUsed": "0", 45 | "ownerNames": [ 46 | "Andrew Griffiths" 47 | ], 48 | "owners": [{ 49 | "kind": "drive#user", 50 | "displayName": "Andrew Griffiths", 51 | "isAuthenticatedUser": false, 52 | "permissionId": "03053244954543198106" 53 | }], 54 | "lastModifyingUserName": "Andrew Griffiths", 55 | "lastModifyingUser": { 56 | "kind": "drive#user", 57 | "displayName": "Andrew Griffiths", 58 | "isAuthenticatedUser": false, 59 | "permissionId": "03053244954543198106" 60 | }, 61 | "editable": false, 62 | "copyable": true, 63 | "writersCanShare": true, 64 | "shared": true, 65 | "appDataContents": false 66 | } -------------------------------------------------------------------------------- /planning/gsheets-api-sample-json/spreadsheets.api.json: -------------------------------------------------------------------------------- 1 | https://spreadsheets.google.com/feeds/list/1HvEMxPVMfUml-tGgaaD0xjyKnpKykGY3gxGgBxSlaM8/od6/public/basic?hl=en_US&alt=json 2 | 3 | 4 | { 5 | "version": "1.0", 6 | "encoding": "UTF-8", 7 | "feed": { 8 | "xmlns": "http://www.w3.org/2005/Atom", 9 | "xmlns$openSearch": "http://a9.com/-/spec/opensearchrss/1.0/", 10 | "xmlns$gsx": "http://schemas.google.com/spreadsheets/2006/extended", 11 | "id": { 12 | "$t": "https://spreadsheets.google.com/feeds/list/1HvEMxPVMfUml-tGgaaD0xjyKnpKykGY3gxGgBxSlaM8/od6/public/values" 13 | }, 14 | "updated": { 15 | "$t": "2014-05-30T09:42:56.809Z" 16 | }, 17 | "category": [{ 18 | "scheme": "http://schemas.google.com/spreadsheets/2006", 19 | "term": "http://schemas.google.com/spreadsheets/2006#list" 20 | }], 21 | "title": { 22 | "type": "text", 23 | "$t": "main" 24 | }, 25 | "link": [{ 26 | "rel": "alternate", 27 | "type": "application/atom+xml", 28 | "href": "https://docs.google.com/spreadsheets/d/1HvEMxPVMfUml-tGgaaD0xjyKnpKykGY3gxGgBxSlaM8/pubhtml" 29 | }, { 30 | "rel": "http://schemas.google.com/g/2005#feed", 31 | "type": "application/atom+xml", 32 | "href": "https://spreadsheets.google.com/feeds/list/1HvEMxPVMfUml-tGgaaD0xjyKnpKykGY3gxGgBxSlaM8/od6/public/values" 33 | }, { 34 | "rel": "http://schemas.google.com/g/2005#post", 35 | "type": "application/atom+xml", 36 | "href": "https://spreadsheets.google.com/feeds/list/1HvEMxPVMfUml-tGgaaD0xjyKnpKykGY3gxGgBxSlaM8/od6/public/values" 37 | }, { 38 | "rel": "self", 39 | "type": "application/atom+xml", 40 | "href": "https://spreadsheets.google.com/feeds/list/1HvEMxPVMfUml-tGgaaD0xjyKnpKykGY3gxGgBxSlaM8/od6/public/values?alt\u003djson" 41 | }], 42 | "author": [{ 43 | "name": { 44 | "$t": "andrewgriffithsonline" 45 | }, 46 | "email": { 47 | "$t": "andrewgriffithsonline@gmail.com" 48 | } 49 | }], 50 | "openSearch$totalResults": { 51 | "$t": "1" 52 | }, 53 | "openSearch$startIndex": { 54 | "$t": "1" 55 | }, 56 | "entry": [{ 57 | "id": { 58 | "$t": "https://spreadsheets.google.com/feeds/list/1HvEMxPVMfUml-tGgaaD0xjyKnpKykGY3gxGgBxSlaM8/od6/public/values/cokwr" 59 | }, 60 | "updated": { 61 | "$t": "2014-05-30T09:42:56.809Z" 62 | }, 63 | "category": [{ 64 | "scheme": "http://schemas.google.com/spreadsheets/2006", 65 | "term": "http://schemas.google.com/spreadsheets/2006#list" 66 | }], 67 | "title": { 68 | "type": "text", 69 | "$t": "Andrew Griffiths" 70 | }, 71 | "content": { 72 | "type": "text", 73 | "$t": "sections: home, biography, projects, clients, achievements, contact, showcase: 3, 2, 0" 74 | }, 75 | "link": [{ 76 | "rel": "self", 77 | "type": "application/atom+xml", 78 | "href": "https://spreadsheets.google.com/feeds/list/1HvEMxPVMfUml-tGgaaD0xjyKnpKykGY3gxGgBxSlaM8/od6/public/values/cokwr" 79 | }], 80 | "gsx$title": { 81 | "$t": "Andrew Griffiths" 82 | }, 83 | "gsx$sections": { 84 | "$t": "home, biography, projects, clients, achievements, contact" 85 | }, 86 | "gsx$showcase": { 87 | "$t": "3, 2, 0" 88 | } 89 | }] 90 | } 91 | } -------------------------------------------------------------------------------- /planning/test-tabletop.js: -------------------------------------------------------------------------------- 1 | // 1HvEMxPVMfUml-tGgaaD0xjyKnpKykGY3gxGgBxSlaM8 2 | var public_spreadsheet_url = 'https://docs.google.com/spreadsheet/pub?hl=en_US&hl=en_US&key=1HvEMxPVMfUml-tGgaaD0xjyKnpKykGY3gxGgBxSlaM8&output=html'; 3 | var Tabletop = require('tabletop'); 4 | 5 | function init() { 6 | Tabletop.init( { key: public_spreadsheet_url, 7 | callback: showInfo, 8 | simpleSheet: true } ) 9 | } 10 | 11 | function showInfo(data, tabletop) { 12 | console.log(data); 13 | // [ { title: 'Andrew Griffiths', 14 | // sections: 'home, biography, projects, clients, achievements, contact', 15 | // showcase: '3, 2, 0', 16 | // rowNumber: 1 } ] 17 | 18 | 19 | 20 | 21 | var icons = tabletop.sheets('icons'); 22 | console.log("icons", icons); 23 | // icons { column_names: [ 'name', 'link', 'photourl' ], 24 | // name: 'icons', 25 | // elements: 26 | // [ { name: 'email', 27 | // link: 'mailto:mail@andrewgriffithsonline.com', 28 | // photourl: '', 29 | // rowNumber: 1 }, 30 | // { name: 'twitter', 31 | // link: 'http://twitter.com/techjacker', 32 | // photourl: '', 33 | // rowNumber: 2 } ], 34 | // raw: 35 | // { version: '1.0', 36 | // encoding: 'UTF-8', 37 | // feed: 38 | // { xmlns: 'http://www.w3.org/2005/Atom', 39 | // 'xmlns$openSearch': 'http://a9.com/-/spec/opensearchrss/1.0/', 40 | // 'xmlns$gsx': 'http://schemas.google.com/spreadsheets/2006/extended', 41 | // id: [Object], 42 | // updated: [Object], 43 | // category: [Object], 44 | // title: [Object], 45 | // link: [Object], 46 | // author: [Object], 47 | // 'openSearch$totalResults': [Object], 48 | // 'openSearch$startIndex': [Object], 49 | // entry: [Object] } } } 50 | 51 | 52 | 53 | // } 54 | 55 | 56 | 57 | 58 | init(); -------------------------------------------------------------------------------- /test/fixtures/src/combined.json: -------------------------------------------------------------------------------- 1 | { 2 | "main": { 3 | "title": "Andrew Griffiths", 4 | "sections": "home, biography, projects, clients, achievements, contact", 5 | "showcase": "3, 2, 0" 6 | }, 7 | "icons": [ 8 | { 9 | "name": "email", 10 | "link": "mailto:mail@andrewgriffithsonline.com", 11 | "photo_url": "img/img_placeholder.jpg" 12 | }, 13 | { 14 | "name": "twitter", 15 | "link": "http://twitter.com/techjacker", 16 | "photo_url": "img/img_placeholder2.jpg" 17 | } 18 | ], 19 | "biography": { 20 | "body": "Lorem ipsum Ut nostrud aliqua ad in enim sed qui culpa enim minim minim id consectetur sed dolore nulla aute ex amet id deserunt eiusmod deserunt nisi quis nulla id elit consectetur.\n\nLorem ipsum Sunt ut exercitation sed sunt ea in non velit exercitation aliquip.\n\nLorem ipsum Excepteur ut eu est id consequat veniam qui sit anim dolore.", 21 | "photo_url": "img/img_placeholder_portrait.jpg", 22 | "attachment_text": "download CV", 23 | "attachment_url": "docs/resume.pdf", 24 | "attachment_after": "(100kb pdf)" 25 | }, 26 | "projects": [ 27 | { 28 | "client": "Samsung", 29 | "project": "Project A", 30 | "release": "08/2012", 31 | "side_heading": "My role in this project", 32 | "side_bullets": ["ate lots of donuts", "drank lots of tea", "burped"], 33 | "description": "Lorem ipsum Ea aliqua est dolor dolor commodo veniam nulla sit do voluptate in amet ut nostrud officia cupidatat ut esse Excepteur.", 34 | "description_sub": "Launch Project", 35 | "description_sub_link": "http://themummyofmulberryavenue.com", 36 | "photo_url": "img/img_placeholder.jpg, img/img_placeholder_2.jpg, img/img_placeholder_3.jpg" 37 | }, 38 | { 39 | "client": "Samsung", 40 | "project": "Project A", 41 | "release": "08/2012", 42 | "side_heading": "My role in this project", 43 | "side_bullets": ["ate lots of donuts", "drank lots of tea", "burped"], 44 | "description": "Lorem ipsum Exercitation occaecat dolore laborum eiusmod Excepteur ullamco adipisicing enim esse ut sit Ut id id velit dolore in ut id pariatur aliqua reprehenderit aliqua occaecat ut nostrud pariatur exercitation esse.", 45 | "photo_url": "img/img_placeholder.jpg, img/img_placeholder_2.jpg, img/img_placeholder_3.jpg" 46 | }, 47 | { 48 | "client": "Samsung", 49 | "project": "Project A", 50 | "release": "08/2012", 51 | "side_heading": "My role in this project", 52 | "side_bullets": ["ate lots of donuts", "drank lots of tea", "burped"], 53 | "description": "Lorem ipsum Nulla in esse ex quis pariatur non incididunt pariatur velit et adipisicing sint in in qui proident culpa esse fugiat qui anim ullamco.", 54 | "description_sub": "Launch Project", 55 | "description_sub_link": "http://themummyofmulberryavenue.com", 56 | "photo_url": "img/img_placeholder.jpg, img/img_placeholder_2.jpg, img/img_placeholder_3.jpg" 57 | }, 58 | { 59 | "client": "Samsung", 60 | "project": "Project A", 61 | "release": "08/2012", 62 | "side_heading": "My role in this project", 63 | "side_bullets": ["ate lots of donuts", "drank lots of tea", "burped"], 64 | "description": "Lorem ipsum Ea in consectetur non laboris officia eiusmod aliqua eiusmod cillum in ad incididunt anim voluptate magna proident dolore ut culpa tempor velit in dolore veniam dolore anim Excepteur aliqua voluptate in dolor proident do minim pariatur.", 65 | "photo_url": "img/img_placeholder.jpg, img/img_placeholder_2.jpg, img/img_placeholder_3.jpg" 66 | }, 67 | { 68 | "client": "Samsung", 69 | "project": "Project A", 70 | "release": "08/2012", 71 | "side_heading": "My role in this project", 72 | "side_bullets": ["ate lots of donuts", "drank lots of tea", "burped"], 73 | "description": "Lorem ipsum Enim pariatur cillum laborum esse in ea cupidatat quis ut do commodo sint veniam sed dolor officia officia velit sed sit occaecat minim do cupidatat laborum in qui nisi ex ullamco proident cupidatat.", 74 | "photo_url": "img/img_placeholder.jpg, img/img_placeholder_2.jpg, img/img_placeholder_3.jpg" 75 | }, 76 | { 77 | "client": "Samsung", 78 | "project": "Project A", 79 | "release": "08/2012", 80 | "side_heading": "My role in this project", 81 | "side_bullets": ["ate lots of donuts", "drank lots of tea", "burped"], 82 | "description": "Lorem ipsum Consectetur eiusmod ex amet minim ad fugiat esse mollit id velit nisi ut deserunt eiusmod proident labore mollit do consequat minim minim.", 83 | "photo_url": "img/img_placeholder.jpg, img/img_placeholder_2.jpg, img/img_placeholder_3.jpg" 84 | }, 85 | { 86 | "client": "Samsung", 87 | "project": "Project A", 88 | "release": "08/2012", 89 | "side_heading": "My role in this project", 90 | "side_bullets": ["ate lots of donuts", "drank lots of tea", "burped"], 91 | "description": "Lorem ipsum Consequat veniam voluptate culpa laborum quis non elit est eu nisi magna quis esse reprehenderit aliquip mollit laboris nisi non elit sit proident id aute sed quis proident est adipisicing.", 92 | "photo_url": "img/img_placeholder.jpg, img/img_placeholder_2.jpg, img/img_placeholder_3.jpg" 93 | }, 94 | { 95 | "client": "Samsung", 96 | "project": "Project A", 97 | "release": "08/2012", 98 | "side_heading": "My role in this project", 99 | "side_bullets": ["ate lots of donuts", "drank lots of tea", "burped"], 100 | "description": "Lorem ipsum Enim dolore eu mollit sint nostrud ea aute in dolore enim voluptate voluptate dolore dolore ut in commodo nulla tempor fugiat ut aliqua sit Excepteur deserunt non aute est aute laboris.", 101 | "photo_url": "img/img_placeholder.jpg, img/img_placeholder_2.jpg, img/img_placeholder_3.jpg" 102 | } 103 | ], 104 | "clients": [ 105 | { 106 | "name": "Samsung", 107 | "photo_url": "img/img_placeholder.jpg" 108 | }, 109 | { 110 | "name": "Samsung", 111 | "photo_url": "img/img_placeholder.jpg" 112 | }, 113 | { 114 | "name": "Samsung", 115 | "photo_url": "img/img_placeholder.jpg" 116 | }, 117 | { 118 | "name": "Samsung", 119 | "photo_url": "img/img_placeholder.jpg" 120 | }, 121 | { 122 | "name": "Samsung", 123 | "photo_url": "img/img_placeholder.jpg" 124 | }, 125 | { 126 | "name": "Samsung", 127 | "photo_url": "img/img_placeholder.jpg" 128 | }, 129 | { 130 | "name": "Samsung", 131 | "photo_url": "img/img_placeholder.jpg" 132 | }, 133 | { 134 | "name": "Samsung", 135 | "photo_url": "img/img_placeholder.jpg" 136 | } 137 | ], 138 | "achievements": [ 139 | { 140 | "name": "project A", 141 | "date": "08/2012", 142 | "description": "Lorem ipsum Eu esse est Duis Ut sint fugiat ut laboris voluptate consectetur et ullamco esse eu nisi proident magna quis incididunt id cupidatat adipisicing reprehenderit consectetur aliquip officia in pariatur." 143 | }, 144 | { 145 | "name": "project B", 146 | "date": "08/2012", 147 | "description": "Lorem ipsum Eu esse est Duis Ut sint fugiat ut laboris voluptate consectetur et ullamco esse eu nisi proident magna quis incididunt id cupidatat adipisicing reprehenderit consectetur aliquip officia in pariatur." 148 | }, 149 | { 150 | "name": "project C", 151 | "date": "08/2012", 152 | "description": "Lorem ipsum Eu esse est Duis Ut sint fugiat ut laboris voluptate consectetur et ullamco esse eu nisi proident magna quis incididunt id cupidatat adipisicing reprehenderit consectetur aliquip officia in pariatur." 153 | }, 154 | { 155 | "name": "project D", 156 | "date": "08/2012", 157 | "description": "Lorem ipsum Eu esse est Duis Ut sint fugiat ut laboris voluptate consectetur et ullamco esse eu nisi proident magna quis incididunt id cupidatat adipisicing reprehenderit consectetur aliquip officia in pariatur." 158 | } 159 | ], 160 | "contact": { 161 | "heading": "Keep in touch, fill the form below", 162 | "submit_text": "send message" 163 | } 164 | } -------------------------------------------------------------------------------- /test/fixtures/src/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "1HvEMxPVMfUml-tGgaaD0xjyKnpKykGY3gxGgBxSlaM8", 3 | "tabs": ["icons","biography","projects","clients","achievements","contact", "main"] 4 | } -------------------------------------------------------------------------------- /test/fixtures/src/tabs/achievements.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "project A", 4 | "date": "08/2012", 5 | "description": "Lorem ipsum Eu esse est Duis Ut sint fugiat ut laboris voluptate consectetur et ullamco esse eu nisi proident magna quis incididunt id cupidatat adipisicing reprehenderit consectetur aliquip officia in pariatur.", 6 | "rowNumber": 1 7 | }, 8 | { 9 | "name": "project B", 10 | "date": "08/2012", 11 | "description": "Lorem ipsum Eu esse est Duis Ut sint fugiat ut laboris voluptate consectetur et ullamco esse eu nisi proident magna quis incididunt id cupidatat adipisicing reprehenderit consectetur aliquip officia in pariatur.", 12 | "rowNumber": 2 13 | }, 14 | { 15 | "name": "project C", 16 | "date": "08/2012", 17 | "description": "Lorem ipsum Eu esse est Duis Ut sint fugiat ut laboris voluptate consectetur et ullamco esse eu nisi proident magna quis incididunt id cupidatat adipisicing reprehenderit consectetur aliquip officia in pariatur.", 18 | "rowNumber": 3 19 | }, 20 | { 21 | "name": "project D", 22 | "date": "08/2012", 23 | "description": "Lorem ipsum Eu esse est Duis Ut sint fugiat ut laboris voluptate consectetur et ullamco esse eu nisi proident magna quis incididunt id cupidatat adipisicing reprehenderit consectetur aliquip officia in pariatur.", 24 | "rowNumber": 4 25 | } 26 | ] -------------------------------------------------------------------------------- /test/fixtures/src/tabs/biography.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "body": "Lorem ipsum Ut nostrud aliqua ad in enim sed qui culpa enim minim minim id consectetur sed dolore nulla aute ex amet id deserunt eiusmod deserunt nisi quis nulla id elit consectetur.\n\nLorem ipsum Sunt ut exercitation sed sunt ea in non velit exercitation aliquip.\n\nLorem ipsum Excepteur ut eu est id consequat veniam qui sit anim dolore.", 3 | "photo_url": "img/img_placeholder_portrait.jpg", 4 | "attachment_text": "download CV", 5 | "attachment_url": "docs/resume.pdf", 6 | "attachment_after": "(100kb pdf)", 7 | "rowNumber": 1 8 | }] -------------------------------------------------------------------------------- /test/fixtures/src/tabs/clients.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "Samsung", 4 | "photo_url": "img/img_placeholder.jpg", 5 | "rowNumber": 1 6 | }, 7 | { 8 | "name": "Samsung", 9 | "photo_url": "img/img_placeholder.jpg", 10 | "rowNumber": 1 11 | }, 12 | { 13 | "name": "Samsung", 14 | "photo_url": "img/img_placeholder.jpg", 15 | "rowNumber": 1 16 | }, 17 | { 18 | "name": "Samsung", 19 | "photo_url": "img/img_placeholder.jpg", 20 | "rowNumber": 1 21 | }, 22 | { 23 | "name": "Samsung", 24 | "photo_url": "img/img_placeholder.jpg", 25 | "rowNumber": 1 26 | }, 27 | { 28 | "name": "Samsung", 29 | "photo_url": "img/img_placeholder.jpg", 30 | "rowNumber": 1 31 | }, 32 | { 33 | "name": "Samsung", 34 | "photo_url": "img/img_placeholder.jpg", 35 | "rowNumber": 1 36 | }, 37 | { 38 | "name": "Samsung", 39 | "photo_url": "img/img_placeholder.jpg", 40 | "rowNumber": 1 41 | } 42 | ] -------------------------------------------------------------------------------- /test/fixtures/src/tabs/contact.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "heading": "Keep in touch, fill the form below", 3 | "submit_text": "send message", 4 | "rowNumber": 1 5 | }] -------------------------------------------------------------------------------- /test/fixtures/src/tabs/icons.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "email", 4 | "link": "mailto:mail@andrewgriffithsonline.com", 5 | "photo_url": "img/img_placeholder.jpg", 6 | "rowNumber": 1 7 | }, 8 | { 9 | "name": "twitter", 10 | "link": "http://twitter.com/techjacker", 11 | "photo_url": "img/img_placeholder2.jpg", 12 | "rowNumber": 2 13 | } 14 | ] -------------------------------------------------------------------------------- /test/fixtures/src/tabs/main.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "title": "Andrew Griffiths", 3 | "sections": "home, biography, projects, clients, achievements, contact", 4 | "showcase": "3, 2, 0", 5 | "rowNumber": 1 6 | }] -------------------------------------------------------------------------------- /test/fixtures/src/tabs/projects.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "client": "Samsung", 4 | "project": "Project A", 5 | "release": "08/2012", 6 | "side_heading": "My role in this project", 7 | "side_bullets": ["ate lots of donuts", "drank lots of tea", "burped"], 8 | "description": "Lorem ipsum Ea aliqua est dolor dolor commodo veniam nulla sit do voluptate in amet ut nostrud officia cupidatat ut esse Excepteur.", 9 | "description_sub": "Launch Project", 10 | "description_sub_link": "http://themummyofmulberryavenue.com", 11 | "photo_url": "img/img_placeholder.jpg, img/img_placeholder_2.jpg, img/img_placeholder_3.jpg", 12 | "rowNumber": 1 13 | }, 14 | { 15 | "client": "Samsung", 16 | "project": "Project A", 17 | "release": "08/2012", 18 | "side_heading": "My role in this project", 19 | "side_bullets": ["ate lots of donuts", "drank lots of tea", "burped"], 20 | "description": "Lorem ipsum Exercitation occaecat dolore laborum eiusmod Excepteur ullamco adipisicing enim esse ut sit Ut id id velit dolore in ut id pariatur aliqua reprehenderit aliqua occaecat ut nostrud pariatur exercitation esse.", 21 | "photo_url": "img/img_placeholder.jpg, img/img_placeholder_2.jpg, img/img_placeholder_3.jpg", 22 | "rowNumber": 1 23 | }, 24 | { 25 | "client": "Samsung", 26 | "project": "Project A", 27 | "release": "08/2012", 28 | "side_heading": "My role in this project", 29 | "side_bullets": ["ate lots of donuts", "drank lots of tea", "burped"], 30 | "description": "Lorem ipsum Nulla in esse ex quis pariatur non incididunt pariatur velit et adipisicing sint in in qui proident culpa esse fugiat qui anim ullamco.", 31 | "description_sub": "Launch Project", 32 | "description_sub_link": "http://themummyofmulberryavenue.com", 33 | "photo_url": "img/img_placeholder.jpg, img/img_placeholder_2.jpg, img/img_placeholder_3.jpg", 34 | "rowNumber": 1 35 | }, 36 | { 37 | "client": "Samsung", 38 | "project": "Project A", 39 | "release": "08/2012", 40 | "side_heading": "My role in this project", 41 | "side_bullets": ["ate lots of donuts", "drank lots of tea", "burped"], 42 | "description": "Lorem ipsum Ea in consectetur non laboris officia eiusmod aliqua eiusmod cillum in ad incididunt anim voluptate magna proident dolore ut culpa tempor velit in dolore veniam dolore anim Excepteur aliqua voluptate in dolor proident do minim pariatur.", 43 | "photo_url": "img/img_placeholder.jpg, img/img_placeholder_2.jpg, img/img_placeholder_3.jpg", 44 | "rowNumber": 1 45 | }, 46 | { 47 | "client": "Samsung", 48 | "project": "Project A", 49 | "release": "08/2012", 50 | "side_heading": "My role in this project", 51 | "side_bullets": ["ate lots of donuts", "drank lots of tea", "burped"], 52 | "description": "Lorem ipsum Enim pariatur cillum laborum esse in ea cupidatat quis ut do commodo sint veniam sed dolor officia officia velit sed sit occaecat minim do cupidatat laborum in qui nisi ex ullamco proident cupidatat.", 53 | "photo_url": "img/img_placeholder.jpg, img/img_placeholder_2.jpg, img/img_placeholder_3.jpg", 54 | "rowNumber": 1 55 | }, 56 | { 57 | "client": "Samsung", 58 | "project": "Project A", 59 | "release": "08/2012", 60 | "side_heading": "My role in this project", 61 | "side_bullets": ["ate lots of donuts", "drank lots of tea", "burped"], 62 | "description": "Lorem ipsum Consectetur eiusmod ex amet minim ad fugiat esse mollit id velit nisi ut deserunt eiusmod proident labore mollit do consequat minim minim.", 63 | "photo_url": "img/img_placeholder.jpg, img/img_placeholder_2.jpg, img/img_placeholder_3.jpg", 64 | "rowNumber": 1 65 | }, 66 | { 67 | "client": "Samsung", 68 | "project": "Project A", 69 | "release": "08/2012", 70 | "side_heading": "My role in this project", 71 | "side_bullets": ["ate lots of donuts", "drank lots of tea", "burped"], 72 | "description": "Lorem ipsum Consequat veniam voluptate culpa laborum quis non elit est eu nisi magna quis esse reprehenderit aliquip mollit laboris nisi non elit sit proident id aute sed quis proident est adipisicing.", 73 | "photo_url": "img/img_placeholder.jpg, img/img_placeholder_2.jpg, img/img_placeholder_3.jpg", 74 | "rowNumber": 1 75 | }, 76 | { 77 | "client": "Samsung", 78 | "project": "Project A", 79 | "release": "08/2012", 80 | "side_heading": "My role in this project", 81 | "side_bullets": ["ate lots of donuts", "drank lots of tea", "burped"], 82 | "description": "Lorem ipsum Enim dolore eu mollit sint nostrud ea aute in dolore enim voluptate voluptate dolore dolore ut in commodo nulla tempor fugiat ut aliqua sit Excepteur deserunt non aute est aute laboris.", 83 | "photo_url": "img/img_placeholder.jpg, img/img_placeholder_2.jpg, img/img_placeholder_3.jpg", 84 | "rowNumber": 1 85 | } 86 | ] -------------------------------------------------------------------------------- /test/helpers/helpers.js: -------------------------------------------------------------------------------- 1 | function sortObject(o) { 2 | var sorted = {}, 3 | key, a = []; 4 | 5 | for (key in o) { 6 | if (o.hasOwnProperty(key)) { 7 | a.push(key); 8 | } 9 | } 10 | 11 | a.sort(); 12 | 13 | for (key = 0; key < a.length; key++) { 14 | sorted[a[key]] = o[a[key]]; 15 | } 16 | return sorted; 17 | }; 18 | 19 | module.exports = { 20 | sortObject: sortObject 21 | }; -------------------------------------------------------------------------------- /test/main.test.js: -------------------------------------------------------------------------------- 1 | describe('main', function () { 2 | var sortObject = require('./helpers/helpers.js').sortObject; 3 | var fs = require('fs'); 4 | var path = require('path'); 5 | var should = require('should'); 6 | var glob = require('glob'); 7 | var rewire = require("rewire"); 8 | 9 | // file to test 10 | var main = rewire('../lib/main.js'); 11 | 12 | // mocks 13 | var tableTopMock = require('./mocks/tabletop.js'); 14 | main.__set__("Tabletop", tableTopMock); 15 | 16 | // fixtures 17 | var targetOutputFile = path.join(__dirname, 'fixtures', 'tmp', 'index.json'); 18 | var indexManifest = require('./fixtures/src/index.json'); // ["icons","biography","projects","clients","achievements","contact"] 19 | var expected = sortObject(require('./fixtures/src/combined.json')); 20 | var tableTopMock = require('./mocks/tabletop.js'); 21 | var arrTabsJson; 22 | 23 | before(function(done) { 24 | glob(__dirname + '/fixtures/src/tabs/*', function (err, arrFileNames) { 25 | arrTabsJson = arrFileNames.sort().map(function(name) { 26 | return {elements: require(name)}; 27 | }); 28 | done(); 29 | }) 30 | }); 31 | 32 | beforeEach(function () { 33 | if (fs.existsSync(targetOutputFile)) fs.unlinkSync(targetOutputFile); 34 | }); 35 | 36 | afterEach(function () { 37 | if (fs.existsSync(targetOutputFile)) fs.unlinkSync(targetOutputFile); 38 | }); 39 | 40 | it('should export all needed API methods', function () { 41 | main.should.be.a.function; 42 | }); 43 | 44 | it('should generate the google sheet url for tabletop', function () { 45 | var sheetId = 'sdfdsf2234'; 46 | var expectedUrl = 'https://docs.google.com/spreadsheet/pub?hl=en_US&hl=en_US&key=' + sheetId + '&output=html'; 47 | main.prototype.generateGoogleSheetUrl.call(main.prototype, sheetId).should.equal(expectedUrl); 48 | }); 49 | 50 | var opts = { 51 | id: '1HvEMxPVMfUml-tGgaaD0xjyKnpKykGY3gxGgBxSlaM8', 52 | outPath: targetOutputFile 53 | }; 54 | 55 | it('should combine all the tabs from a google spreadseet into a single json object with the tab names as keys and write it to disk', function (done) { 56 | main(opts, function(err, res) { 57 | // fs.writeFileSync('res.json', JSON.stringify(sortObject(res))); 58 | // fs.writeFileSync('expected.json', JSON.stringify(expected)); 59 | // console.log("sortObject(res)", sortObject(res)); 60 | sortObject(res).should.eql(expected, 'it should write the results to disk'); 61 | // sortObject(require(opts.outPath)).should.eql(expected, 'it should write the results to disk'); 62 | done(err); 63 | }); 64 | }); 65 | 66 | it('should have a promise API', function (done) { 67 | main(opts).then(function(res) { 68 | sortObject(res).should.eql(expected, 'it should write the results to disk'); 69 | sortObject(require(opts.outPath)).should.eql(expected, 'it should write the results to disk'); 70 | done(); 71 | }, done); 72 | }); 73 | 74 | 75 | 76 | it('should be optional to write data to disk', function () { 77 | main.bind(null, {id: 'sdfdsf'}).should.not.throw(); 78 | }); 79 | 80 | 81 | it('should combine all the tabs JSON a single object with the tab names as keys', function () { 82 | // var res = sortObject(main.prototype.combine.call(main.prototype, indexManifest.tabs.sort(), arrTabsJson)); 83 | var res = sortObject(main.prototype.combine.call(main.prototype, indexManifest.tabs.sort(), arrTabsJson)); 84 | // res.should.eql(expected, 'it should combine the tabs from the spreadsheet into a single json file'); 85 | // console.log("res.main", res.main); 86 | // console.log("expected.main", expected.main); 87 | // res.achievements.should.eql(expected.achievements, 'it should combine the tabs from the spreadsheet into a single json file'); 88 | // res.biography.should.eql(expected.biography, 'it should combine the tabs from the spreadsheet into a single json file'); 89 | // res.clients.should.eql(expected.clients, 'it should combine the tabs from the spreadsheet into a single json file'); 90 | // res.contact.should.eql(expected.contact, 'it should combine the tabs from the spreadsheet into a single json file'); 91 | // res.icons.should.eql(expected.icons, 'it should combine the tabs from the spreadsheet into a single json file'); 92 | // res.projects.should.eql(expected.projects, 'it should combine the tabs from the spreadsheet into a single json file'); 93 | // fs.writeFileSync('res.json', JSON.stringify(res)); 94 | // fs.writeFileSync('expected.json', JSON.stringify(expected)); 95 | res.main.should.eql(expected.main, 'it should combine the tabs from the spreadsheet into a single json file'); 96 | }); 97 | 98 | 99 | 100 | }); -------------------------------------------------------------------------------- /test/mocks/tabletop.js: -------------------------------------------------------------------------------- 1 | var indexManifest = require('./../fixtures/src/index.json'); // ["icons","biography","projects","clients","achievements","contact"] 2 | 3 | var tableTop = {}; 4 | 5 | tableTop.init = function (options) { 6 | options.callback({}, tableTop); 7 | }; 8 | 9 | tableTop.sheets = function (tabName) { 10 | return {elements: require('./../fixtures/src/tabs/' + tabName + '.json')}; 11 | }; 12 | 13 | tableTop.model_names = indexManifest.tabs; 14 | 15 | module.exports = tableTop; -------------------------------------------------------------------------------- /testem.json: -------------------------------------------------------------------------------- 1 | { 2 | "framework": "mocha", 3 | "src_files": [ 4 | "lib/**/*.js", 5 | "lib/*.js", 6 | "test/*.test.js" 7 | ], 8 | "launchers": { 9 | "Node": { 10 | "command": "npm test", 11 | "protocol": "tap" 12 | } 13 | }, 14 | "launch_in_dev": [ "Node"], 15 | "launch_in_ci": [ "Node" ] 16 | } --------------------------------------------------------------------------------