Debounce saving and loading of config

This commit is contained in:
Max Brunsfeld
2014-12-29 23:52:41 -08:00
parent 32275b27a3
commit 339f331776
3 changed files with 57 additions and 28 deletions

View File

@@ -102,18 +102,20 @@ describe "Config", ->
expect(atom.config.set("foo.bar.baz", 42)).toBe true
expect(atom.config.get("foo.bar.baz")).toBe 42
it "updates observers and saves when a key path is set", ->
observeHandler = jasmine.createSpy "observeHandler"
atom.config.observe "foo.bar.baz", observeHandler
observeHandler.reset()
it "saves the user's config to disk after it stops changing", ->
atom.config.set("foo.bar.baz", 42)
advanceClock(50)
expect(atom.config.save).not.toHaveBeenCalled()
atom.config.set("foo.bar.baz", 43)
advanceClock(50)
expect(atom.config.save).not.toHaveBeenCalled()
atom.config.set("foo.bar.baz", 44)
advanceClock(150)
expect(atom.config.save).toHaveBeenCalled()
expect(observeHandler).toHaveBeenCalledWith 42
it "does not save when a non-default 'source' is given", ->
atom.config.set("foo.bar.baz", 42, source: 'some-other-source', scopeSelector: '.a')
advanceClock(500)
expect(atom.config.save).not.toHaveBeenCalled()
it "does not allow a 'source' option without a 'scopeSelector'", ->
@@ -197,6 +199,7 @@ describe "Config", ->
atom.config.save.reset()
atom.config.unset('a.c')
advanceClock(500)
expect(atom.config.save.callCount).toBe 1
describe "when a 'source' and no 'scopeSelector' is given", ->
@@ -234,6 +237,7 @@ describe "Config", ->
atom.config.save.reset()
atom.config.unset('foo.bar.baz', scopeSelector: '.source.coffee')
advanceClock(150)
expect(atom.config.save.callCount).toBe 1
it "allows removing settings for a specific source and scope selector", ->
@@ -267,26 +271,32 @@ describe "Config", ->
it "removes the scoped value when it was the only set value on the object", ->
spyOn(CSON, 'writeFileSync')
jasmine.unspy atom.config, 'save'
atom.config.save.andCallThrough()
atom.config.setDefaults("foo", bar: baz: 10)
atom.config.set('foo.bar.baz', 55, scopeSelector: '.source.coffee')
atom.config.set('foo.bar.ok', 20, scopeSelector: '.source.coffee')
CSON.writeFileSync.reset()
expect(atom.config.get('foo.bar.baz', scope: ['.source.coffee'])).toBe 55
advanceClock(150)
CSON.writeFileSync.reset()
atom.config.unset('foo.bar.baz', scopeSelector: '.source.coffee')
expect(atom.config.get('foo.bar.baz', scope: ['.source.coffee'])).toBe 10
expect(atom.config.get('foo.bar.ok', scope: ['.source.coffee'])).toBe 20
advanceClock(150)
expect(CSON.writeFileSync).toHaveBeenCalled()
properties = CSON.writeFileSync.mostRecentCall.args[1]
expect(properties['.coffee.source']).toEqual
foo:
bar:
ok: 20
CSON.writeFileSync.reset()
atom.config.unset('foo.bar.ok', scopeSelector: '.source.coffee')
advanceClock(150)
expect(CSON.writeFileSync).toHaveBeenCalled()
properties = CSON.writeFileSync.mostRecentCall.args[1]
expect(properties['.coffee.source']).toBeUndefined()
@@ -692,6 +702,15 @@ describe "Config", ->
describe ".observeUserConfig()", ->
updatedHandler = null
writeConfigFile = (data) ->
previousSetTimeoutCallCount = setTimeout.callCount
runs ->
fs.writeFileSync(atom.config.configFilePath, data)
waitsFor "debounced config file load", ->
setTimeout.callCount > previousSetTimeoutCallCount
runs ->
advanceClock(1000)
beforeEach ->
atom.config.setSchema 'foo',
type: 'object'
@@ -728,7 +747,7 @@ describe "Config", ->
describe "when the config file changes to contain valid cson", ->
it "updates the config data", ->
fs.writeFileSync(atom.config.configFilePath, "foo: { bar: 'quux', baz: 'bar'}")
writeConfigFile("foo: { bar: 'quux', baz: 'bar'}")
waitsFor 'update event', -> updatedHandler.callCount > 0
runs ->
expect(atom.config.get('foo.bar')).toBe 'quux'
@@ -737,8 +756,9 @@ describe "Config", ->
it "does not fire a change event for paths that did not change", ->
atom.config.onDidChange 'foo.bar', noChangeSpy = jasmine.createSpy()
fs.writeFileSync(atom.config.configFilePath, "foo: { bar: 'baz', baz: 'ok'}")
writeConfigFile("foo: { bar: 'baz', baz: 'ok'}")
waitsFor 'update event', -> updatedHandler.callCount > 0
runs ->
expect(noChangeSpy).not.toHaveBeenCalled()
expect(atom.config.get('foo.bar')).toBe 'baz'
@@ -750,7 +770,8 @@ describe "Config", ->
type: 'array'
items:
type: 'string'
fs.writeFileSync(atom.config.configFilePath, "foo: { bar: ['baz', 'ok']}")
writeConfigFile("foo: { bar: ['baz', 'ok']}")
waitsFor 'update event', -> updatedHandler.callCount > 0
runs -> updatedHandler.reset()
@@ -758,8 +779,9 @@ describe "Config", ->
noChangeSpy = jasmine.createSpy()
atom.config.onDidChange('foo.bar', noChangeSpy)
fs.writeFileSync(atom.config.configFilePath, "foo: { bar: ['baz', 'ok'], baz: 'another'}")
writeConfigFile("foo: { bar: ['baz', 'ok'], baz: 'another'}")
waitsFor 'update event', -> updatedHandler.callCount > 0
runs ->
expect(noChangeSpy).not.toHaveBeenCalled()
expect(atom.config.get('foo.bar')).toEqual ['baz', 'ok']
@@ -770,12 +792,13 @@ describe "Config", ->
scopedSpy = jasmine.createSpy()
atom.config.onDidChange('foo.scoped', scope: ['.source.ruby'], scopedSpy)
fs.writeFileSync atom.config.configFilePath, """
writeConfigFile """
global:
foo:
scoped: false
"""
waitsFor 'update event', -> updatedHandler.callCount > 0
runs ->
expect(scopedSpy).toHaveBeenCalled()
expect(atom.config.get('foo.scoped', scope: ['.source.ruby'])).toBe false
@@ -784,7 +807,7 @@ describe "Config", ->
noChangeSpy = jasmine.createSpy()
atom.config.onDidChange('foo.scoped', scope: ['.source.ruby'], noChangeSpy)
fs.writeFileSync atom.config.configFilePath, """
writeConfigFile """
global:
foo:
bar: 'baz'
@@ -793,6 +816,7 @@ describe "Config", ->
scoped: true
"""
waitsFor 'update event', -> updatedHandler.callCount > 0
runs ->
expect(noChangeSpy).not.toHaveBeenCalled()
expect(atom.config.get('foo.bar', scope: ['.source.ruby'])).toBe 'baz'
@@ -800,7 +824,7 @@ describe "Config", ->
describe "when the config file changes to omit a setting with a default", ->
it "resets the setting back to the default", ->
fs.writeFileSync(atom.config.configFilePath, "foo: { baz: 'new'}")
writeConfigFile("foo: { baz: 'new'}")
waitsFor 'update event', -> updatedHandler.callCount > 0
runs ->
expect(atom.config.get('foo.bar')).toBe 'def'
@@ -808,19 +832,20 @@ describe "Config", ->
describe "when the config file changes to be empty", ->
beforeEach ->
fs.writeFileSync(atom.config.configFilePath, "")
writeConfigFile("")
waitsFor 'update event', -> updatedHandler.callCount > 0
it "resets all settings back to the defaults", ->
expect(updatedHandler.callCount).toBe 1
expect(atom.config.get('foo.bar')).toBe 'def'
atom.config.set("hair", "blonde") # trigger a save
advanceClock(500)
expect(atom.config.save).toHaveBeenCalled()
describe "when the config file subsequently changes again to contain configuration", ->
beforeEach ->
updatedHandler.reset()
fs.writeFileSync(atom.config.configFilePath, "foo: bar: 'newVal'")
writeConfigFile("foo: bar: 'newVal'")
waitsFor 'update event', -> updatedHandler.callCount > 0
it "sets the setting to the value specified in the config file", ->
@@ -829,7 +854,7 @@ describe "Config", ->
describe "when the config file changes to contain invalid cson", ->
beforeEach ->
spyOn(console, 'error')
fs.writeFileSync(atom.config.configFilePath, "}}}")
writeConfigFile("}}}")
waitsFor "error to be logged", -> console.error.callCount > 0
it "logs a warning and does not update config data", ->
@@ -840,11 +865,12 @@ describe "Config", ->
describe "when the config file subsequently changes again to contain valid cson", ->
beforeEach ->
fs.writeFileSync(atom.config.configFilePath, "foo: bar: 'newVal'")
writeConfigFile("foo: bar: 'newVal'")
waitsFor 'update event', -> updatedHandler.callCount > 0
it "updates the config data and resumes saving", ->
atom.config.set("hair", "blonde")
advanceClock(500)
expect(atom.config.save).toHaveBeenCalled()
describe ".initializeConfigDirectory()", ->

View File

@@ -106,9 +106,9 @@ beforeEach ->
spyOn(atom.menu, 'sendToBrowserProcess')
# reset config before each spec; don't load or save from/to `config.json`
spyOn(Config::, 'load')
spyOn(Config::, 'save')
config = new Config({resourcePath, configDirPath: atom.getConfigDirPath()})
spyOn(config, 'load')
spyOn(config, 'save')
atom.config = config
atom.loadConfig()
config.set "core.destroyEmptyPanes", false
@@ -118,6 +118,7 @@ beforeEach ->
config.set "core.disabledPackages", ["package-that-throws-an-exception",
"package-with-broken-package-json", "package-with-broken-keymap"]
config.set "editor.useShadowDOM", true
advanceClock(1000)
config.load.reset()
config.save.reset()

View File

@@ -318,6 +318,9 @@ class Config
@configFilePath ?= path.join(@configDirPath, 'config.cson')
@transactDepth = 0
@debouncedSave = _.debounce(@save, 100)
@debouncedLoad = _.debounce(@loadUserConfig, 100)
###
Section: Config Subscription
###
@@ -564,7 +567,7 @@ class Config
else
@setRawValue(keyPath, value)
@save() if source is @getUserConfigPath() and shouldSave and not @configFileHasErrors
@debouncedSave() if source is @getUserConfigPath() and shouldSave and not @configFileHasErrors
true
# Essential: Restore the setting at `keyPath` to its default value.
@@ -594,16 +597,15 @@ class Config
_.setValueForKeyPath(settings, keyPath, undefined)
settings = withoutEmptyObjects(settings)
@set(null, settings, {scopeSelector, source, priority: @priorityForSource(source)}) if settings?
@save() unless @configFileHasErrors
@debouncedSave()
else
@scopedSettingsStore.removePropertiesForSource(source)
@scopedSettingsStore.removePropertiesForSourceAndSelector(source, scopeSelector)
@emitChangeEvent()
else
@scopedSettingsStore.removePropertiesForSource(source)
if keyPath?
@set(keyPath, _.valueForKeyPath(@defaultSettings, keyPath))
# Extended: Get an {Array} of all of the `source` {String}s with which
# settings have been added via {::set}.
getSources: ->
@@ -797,7 +799,7 @@ class Config
observeUserConfig: ->
try
@watchSubscription ?= pathWatcher.watch @configFilePath, (eventType) =>
@loadUserConfig() if eventType is 'change' and @watchSubscription?
@debouncedLoad() if eventType is 'change' and @watchSubscription?
catch error
@notifyFailure('Failed to watch user config', error)