Basic scoped settings in Config works

This commit is contained in:
Ben Ogle
2014-10-01 16:31:22 -07:00
parent 8533286114
commit 356f4bec7c
2 changed files with 117 additions and 14 deletions

View File

@@ -862,3 +862,49 @@ describe "Config", ->
expect(atom.config.set('foo.bar.arr', ['two', 'three'])).toBe true
expect(atom.config.get('foo.bar.arr')).toEqual ['two', 'three']
describe "scoped settings", ->
describe ".get(scopeDescriptor, keyPath)", ->
it "returns the property with the most specific scope selector", ->
atom.config.addScopedDefaults(".source.coffee .string.quoted.double.coffee", foo: bar: baz: 42)
atom.config.addScopedDefaults(".source .string.quoted.double", foo: bar: baz: 22)
atom.config.addScopedDefaults(".source", foo: bar: baz: 11)
expect(atom.config.get([".source.coffee", ".string.quoted.double.coffee"], "foo.bar.baz")).toBe 42
expect(atom.config.get([".source.js", ".string.quoted.double.js"], "foo.bar.baz")).toBe 22
expect(atom.config.get([".source.js", ".variable.assignment.js"], "foo.bar.baz")).toBe 11
expect(atom.config.get([".text"], "foo.bar.baz")).toBeUndefined()
it "favors the most recently added properties in the event of a specificity tie", ->
atom.config.addScopedDefaults(".source.coffee .string.quoted.single", foo: bar: baz: 42)
atom.config.addScopedDefaults(".source.coffee .string.quoted.double", foo: bar: baz: 22)
expect(atom.config.get([".source.coffee", ".string.quoted.single"], "foo.bar.baz")).toBe 42
expect(atom.config.get([".source.coffee", ".string.quoted.single.double"], "foo.bar.baz")).toBe 22
describe 'when there are global defaults', ->
beforeEach ->
atom.config.setDefaults("foo", hasDefault: 'ok')
it 'falls back to the global when there is no scoped property specified', ->
expect(atom.config.get([".source.coffee", ".string.quoted.single"], "foo.hasDefault")).toBe 'ok'
describe ".set(scope, keyPath, value)", ->
it "sets the value and overrides the others", ->
atom.config.addScopedDefaults(".source.coffee .string.quoted.double.coffee", foo: bar: baz: 42)
atom.config.addScopedDefaults(".source .string.quoted.double", foo: bar: baz: 22)
atom.config.addScopedDefaults(".source", foo: bar: baz: 11)
expect(atom.config.get([".source.coffee", ".string.quoted.double.coffee"], "foo.bar.baz")).toBe 42
expect(atom.config.set(".source.coffee .string.quoted.double.coffee", "foo.bar.baz", 100)).toBe true
expect(atom.config.get([".source.coffee", ".string.quoted.double.coffee"], "foo.bar.baz")).toBe 100
describe ".removeScopedSettingsForName(name)", ->
it "allows properties to be removed by name", ->
atom.config.addScopedDefaults("a", ".source.coffee .string.quoted.double.coffee", foo: bar: baz: 42)
atom.config.addScopedDefaults("b", ".source .string.quoted.double", foo: bar: baz: 22)
atom.config.removeScopedSettingsForName("b")
expect(atom.config.get([".source.js", ".string.quoted.double.js"], "foo.bar.baz")).toBeUndefined()
expect(atom.config.get([".source.coffee", ".string.quoted.double.coffee"], "foo.bar.baz")).toBe 42

View File

@@ -8,6 +8,8 @@ async = require 'async'
pathWatcher = require 'pathwatcher'
{deprecate} = require 'grim'
ScopedPropertyStore = require 'scoped-property-store'
# Essential: Used to access all of Atom's configuration details.
#
# An instance of this class is always available as the `atom.config` global.
@@ -307,6 +309,7 @@ class Config
properties: {}
@defaultSettings = {}
@settings = {}
@scopedSettingsStore = new ScopedPropertyStore
@configFileHasErrors = false
@configFilePath = fs.resolve(@configDirPath, 'config', ['json', 'cson'])
@configFilePath ?= path.join(@configDirPath, 'config.cson')
@@ -368,20 +371,16 @@ class Config
#
# Returns the value from Atom's default settings, the user's configuration
# file in the type specified by the configuration schema.
get: (keyPath) ->
value = _.valueForKeyPath(@settings, keyPath)
defaultValue = _.valueForKeyPath(@defaultSettings, keyPath)
get: (scopeDescriptor, keyPath) ->
if arguments.length == 1
keyPath = scopeDescriptor
scopeDescriptor = undefined
if value?
value = _.deepClone(value)
valueIsObject = _.isObject(value) and not _.isArray(value)
defaultValueIsObject = _.isObject(defaultValue) and not _.isArray(defaultValue)
if valueIsObject and defaultValueIsObject
_.defaults(value, defaultValue)
else
value = _.deepClone(defaultValue)
if scopeDescriptor?
value = @getRawScopedValue(scopeDescriptor, keyPath)
return value if value?
value
@getRawValue(keyPath)
# Essential: Sets the value for a configuration setting.
#
@@ -394,14 +393,23 @@ class Config
# Returns a {Boolean}
# * `true` if the value was set.
# * `false` if the value was not able to be coerced to the type specified in the setting's schema.
set: (keyPath, value) ->
set: (scope, keyPath, value) ->
unless value == undefined
try
value = @makeValueConformToSchema(keyPath, value)
catch e
return false
@setRawValue(keyPath, value)
if arguments.length < 3
value = keyPath
keyPath = scope
scope = undefined
if scope?
@addRawScopedValue(scope, keyPath, value)
else
@setRawValue(keyPath, value)
@save() unless @configFileHasErrors
true
@@ -578,6 +586,18 @@ class Config
save: ->
CSON.writeFileSync(@configFilePath, @settings)
getRawValue: (keyPath) ->
value = _.valueForKeyPath(@settings, keyPath)
defaultValue = _.valueForKeyPath(@defaultSettings, keyPath)
if value?
value = _.deepClone(value)
_.defaults(value, defaultValue) if isPlainObject(defaultValue) and isPlainObject(defaultValue)
else
value = _.deepClone(defaultValue)
value
setRawValue: (keyPath, value) ->
defaultValue = _.valueForKeyPath(@defaultSettings, keyPath)
value = undefined if _.isEqual(defaultValue, value)
@@ -651,6 +671,43 @@ class Config
value = @constructor.executeSchemaEnforcers(keyPath, value, schema) if schema = @getSchema(keyPath)
value
###
Section: Private Scoped Settings
###
addScopedDefaults: (name, selector, value) ->
if arguments.length < 3
value = selector
selector = name
name = null
name = "#{name ? ''}+default"
settingsBySelector = {}
settingsBySelector[selector] = value
@scopedSettingsStore.addProperties(name, settingsBySelector)
addRawScopedValue: (selector, keyPath, value) ->
if keyPath?
newValue = {}
_.setValueForKeyPath(newValue, keyPath, value)
value = newValue
settingsBySelector = {}
settingsBySelector[selector] = value
@scopedSettingsStore.addProperties(name, settingsBySelector)
getRawScopedValue: (scopeDescriptor, keyPath) ->
scopeChain = scopeDescriptor
.map (scope) ->
scope = ".#{scope}" unless scope[0] is '.'
scope
.join(' ')
@scopedSettingsStore.getPropertyValue(scopeChain, keyPath)
removeScopedSettingsForName: (name) ->
@scopedSettingsStore.removeProperties(name)
@scopedSettingsStore.removeProperties("#{name}+default")
# Base schema enforcers. These will coerce raw input into the specified type,
# and will throw an error when the value cannot be coerced. Throwing the error
# will indicate that the value should not be set.