From e77336f160a16b2ae2992ebea7ec15afddf85072 Mon Sep 17 00:00:00 2001
From: Loic Nageleisen <loic.nageleisen@gmail.com>
Date: Wed, 9 Dec 2015 15:24:48 +0100
Subject: [PATCH] tiered configuration for embeddability on build

- configuration is embeddable
- use OS conventions (via Electron) to store state

be consistent: embedded config.json -> state.json
---
 .gitignore                                    |   2 +
 src/main.js                                   |   4 +-
 src/settings.js                               |  32 +++--
 test/fake-app/state.json                      |  12 ++
 .../config.json                               |   0
 .../.matterfront => fake-userdata}/state.json |   0
 test/settings-spec.js                         | 119 ++++++++++++++----
 7 files changed, 125 insertions(+), 44 deletions(-)
 create mode 100644 test/fake-app/state.json
 rename test/{fake-home-dir/.matterfront => fake-userdata}/config.json (100%)
 rename test/{fake-home-dir/.matterfront => fake-userdata}/state.json (100%)

diff --git a/.gitignore b/.gitignore
index ac17892..5a7e5de 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,6 +2,8 @@
 /dist
 /config.json
 /src/config.json
+/state.json
+/src/state.json
 .tag*
 *npm-debug.log
 /.vagrant
diff --git a/src/main.js b/src/main.js
index 7270d64..043713f 100644
--- a/src/main.js
+++ b/src/main.js
@@ -7,7 +7,7 @@ var settings = require('./settings.js');
 var teams = require("./teams.js");
 var tray = require("./tray.js");
 
-settings.load();
+settings.load(app.getAppPath(), app.getPath('userData'));
 chromeArgs.apply(settings);
 teams.listen();
 
@@ -55,7 +55,7 @@ app.on('ready', function() {
       settings.set('window:width', bounds.width);
       settings.set('window:height', bounds.height);
     }
-    settings.saveState();
+    settings.saveState(app.getPath('userData'));
 
     // Quit when the window is closed if not on OS X or if the tray icon is disabled.
     if (process.platform != 'darwin' && !tray.isEnabled()) {
diff --git a/src/settings.js b/src/settings.js
index 74f8098..7358401 100644
--- a/src/settings.js
+++ b/src/settings.js
@@ -5,19 +5,16 @@ var path = require('path-extra');
 
 var settings = {};
 
-var getSettingsDir = function(homedir){
-  homedir = homedir || path.homedir();
-  return path.join(homedir, '.matterfront');
+var getStatePath = function(userDataPath){
+  return path.join(userDataPath, 'state.json');
 };
 
-var getStatePath = function(homedir){
-  var settingsDir = getSettingsDir(homedir);
-  return path.join(settingsDir, 'state.json');
+var getAppStatePath = function(appPath){
+  return path.join(appPath, 'state.json');
 };
 
-var getConfigPath = function(homedir){
-  var settingsDir = getSettingsDir(homedir);
-  return path.join(settingsDir, 'config.json');
+var getConfigPath = function(userDataPath){
+  return path.join(userDataPath, 'config.json');
 };
 
 var defaults = {
@@ -29,12 +26,14 @@ var defaults = {
   "showTrayIcon": false
 };
 
-settings.load = function(homedir){
-  var statePath = getStatePath(homedir);
-  var configPath = getConfigPath(homedir);
+settings.load = function(appPath, userDataPath){
+  var statePath = getStatePath(userDataPath);
+  var appStatePath = getAppStatePath(appPath);
+  var configPath = getConfigPath(userDataPath);
 
   nconf.argv();
-  nconf.file("state", statePath);
+  nconf.file('state', statePath);
+  nconf.file('appstate', appStatePath);
   nconf.file("config", configPath);
   nconf.defaults(defaults);
 };
@@ -54,11 +53,10 @@ settings.append = function(key, value){
   return settings._current;
 };
 
-settings.saveState = function(homedir){
-  var settingsDir = getSettingsDir(homedir);
-  mkdirp(settingsDir);
+settings.saveState = function(userDataPath){
+  mkdirp(userDataPath);
 
-  var statePath = getStatePath(homedir);
+  var statePath = getStatePath(userDataPath);
   var state = {
     teams: nconf.get("teams"),
     window: nconf.get("window")
diff --git a/test/fake-app/state.json b/test/fake-app/state.json
new file mode 100644
index 0000000..a1bc515
--- /dev/null
+++ b/test/fake-app/state.json
@@ -0,0 +1,12 @@
+{
+  "teams": [{
+    "name": "teamA",
+    "url": "http://some.server.com/teamA"
+  }, {
+    "name": "teamB",
+    "url": "http://some.server.com/teamB"
+  }],
+  "window": {
+    "height": 1024
+  }
+}
diff --git a/test/fake-home-dir/.matterfront/config.json b/test/fake-userdata/config.json
similarity index 100%
rename from test/fake-home-dir/.matterfront/config.json
rename to test/fake-userdata/config.json
diff --git a/test/fake-home-dir/.matterfront/state.json b/test/fake-userdata/state.json
similarity index 100%
rename from test/fake-home-dir/.matterfront/state.json
rename to test/fake-userdata/state.json
diff --git a/test/settings-spec.js b/test/settings-spec.js
index 6b1102e..4d2e8d1 100644
--- a/test/settings-spec.js
+++ b/test/settings-spec.js
@@ -2,40 +2,109 @@ var path = require('path');
 var settings = require('../src/settings.js');
 
 describe('settings', function(){
+  describe('without config but state', function(){
+    before(function(){
+      var fakeApp = path.join(__dirname, '.');
+      var fakeUserData = path.join(__dirname, './fake-userdata');
+      settings.load(fakeApp, fakeUserData);
+    });
 
-  before(function(){
-    var fakeHomeDir = path.join(__dirname, './fake-home-dir');
-    settings.load(fakeHomeDir);
-  });
+    it('reads command-line args', function(){
+      //this arg is passed into the specs by mocha
+      expect(settings.get('reporter')).to.eql('spec');
+    });
 
-  it('reads command-line args', function(){
-    //this arg is passed into the specs by mocha
-    expect(settings.get('reporter')).to.eql('spec');
-  });
+    it('reads array types', function(){
+      expect(settings.get('teams')).to.have.length(2);
+    });
 
-  it('reads array types', function(){
-    expect(settings.get('teams')).to.have.length(2);
-  });
+    it('reads nested objects', function(){
+      expect(settings.get('window:width')).to.eql(800);
+    });
 
-  it('reads nested objects', function(){
-    expect(settings.get('window:width')).to.eql(800);
-  });
+    it('sets config values', function(){
+      settings.set('window:width', 1920);
+      expect(settings.get('window:width')).to.eql(1920);
+    });
 
-  it('sets config values', function(){
-    settings.set('window:width', 1920);
-    expect(settings.get('window:width')).to.eql(1920);
-  });
+    it('has default values', function(){
+      expect(settings.get('window:height')).to.eql(600);
+    });
 
-  it('has default values', function(){
-    expect(settings.get('window:height')).to.eql(600);
+    it('appends config values', function(){
+      settings.append('teams', 'http://localhost/team3');
+      expect(settings.get('teams')).to.have.length(3);
+    });
   });
 
-  it('appends config values', function(){
-    settings.append('teams', 'http://localhost/team3');
-    expect(settings.get('teams')).to.have.length(3);
+  describe('with config but no state', function(){
+    before(function(){
+      var fakeApp = path.join(__dirname, './fake-app');
+      var fakeUserData = path.join(__dirname, './');
+      settings.load(fakeApp, fakeUserData);
+    });
+
+    it('reads command-line args', function(){
+      //this arg is passed into the specs by mocha
+      expect(settings.get('reporter')).to.eql('spec');
+    });
+
+    it('reads array types', function(){
+      expect(settings.get('teams')).to.have.length(2);
+    });
+
+    it('reads nested objects', function(){
+      expect(settings.get('window:height')).to.eql(1024);
+    });
+
+    it('sets config values', function(){
+      settings.set('window:height', 1920);
+      expect(settings.get('window:height')).to.eql(1920);
+    });
+
+    it('has default values', function(){
+      expect(settings.get('window:width')).to.eql(1024);
+    });
+
+    it('appends config values', function(){
+      settings.append('teams', 'http://localhost/team3');
+      expect(settings.get('teams')).to.have.length(3);
+    });
   });
 
-  it('reads non-state settings from `config.json`', function(){
-    expect(settings.get('chrome-args')).to.have.property("some-arg-name", "some-arg-value");
+  describe('with config and state', function(){
+    before(function(){
+      var fakeApp = path.join(__dirname, './fake-app');
+      var fakeUserData = path.join(__dirname, './fake-userdata');
+      settings.load(fakeApp, fakeUserData);
+    });
+
+    it('reads command-line args', function(){
+      //this arg is passed into the specs by mocha
+      expect(settings.get('reporter')).to.eql('spec');
+    });
+
+    it('reads array types', function(){
+      expect(settings.get('teams')).to.have.length(2);
+    });
+
+    it('reads nested objects', function(){
+      expect(settings.get('window:width')).to.eql(800);
+      expect(settings.get('window:height')).to.eql(1024);
+    });
+
+    it('sets config values', function(){
+      settings.set('window:width', 1920);
+      expect(settings.get('window:width')).to.eql(1920);
+    });
+
+    it('appends config values', function(){
+      settings.append('teams', 'http://localhost/team3');
+      expect(settings.get('teams')).to.have.length(3);
+    });
+
+    it('reads non-state settings from `config.json`', function(){
+      expect(settings.get('chrome-args')).to.have.property("some-arg-name", "some-arg-value");
+    });
   });
 });