atom/atom — pull request #4567 by nathansobo (2014-12-12 03:45:01+00:00)
Title: Improve Config API with regard to scoped settings

This may be able to supplant #4558.

/cc @maxbrunsfeld: I got the get/set API looking good.

### Problems with Config API

The Config API still has some awkwardness leftover from when scoped settings were merged in. It would be nice to make the API more coherent considering the expanded role of settings.

* We're using a private method, `::addScopedSettings` in packages. We need a public equivalent. The main difference is that `::addScopedSettings` takes a `source` argument. The `source` is used to distinguish between user settings and package settings internally, so it's important.

* Methods related to defaults don't make sense in the presence of scoped settings. The methods `::isDefault`, `::restoreDefault`, and `::getDefault` don't actually deal with true *default* values from the schemas. Instead they consider a value "default" if its source isn't the user's `config.cson`

### Proposed Solution

We can eliminate `::addScopedSettings` and the methods that deal with defaults from the API. Instead, we replace them with the ability to talk about the *source* of a given setting when getting and setting it. Then the settings view can make decisions based on the source being the user's `config.cson`, rather than exposing a murky conception of "default" in settings.

We propose to replace the following methods:

* getDefault
* isDefault
* restoreDefault
* addScopedSettings

With these new signatures for get and set

* set(keyPath, value, [{scopeSelector, source}])
* get(keyPath, [{scope, sources, excludeSources}])

Plus the new `unset` method... which can be used to remove a single key path or all key paths from a given scope.

* unset([keyPath], [{source}])

If we need to know if a value is assigned in the user's settings, we specify the desired *source* in our getter. If we get no value back, we can assume we're using some other value (maybe a schema default, mabye from a package). If we want to determine the non-user-assigned value, we exclude it as a source when getting the setting.

### Steps

* [x] Make `Config::get` and `::set` take trailing options hashes
* [ ] Store global settings in the scoped property store under `*` so they're associated with a source
* [ ] Add a `source` option to `Config::set`
* [ ] Use the user's settings file path as the default source in `::set`
* [ ] Add `sources` and `excludeSources` options to `Config::get`
* [ ] Add `Config::unset`
* [ ] Make `Config::observe` and `::onDidChange` consistent with `::get` by taking a scope as an option instead of the first argument.
* [ ] Bugfix: Ensure the most recently assigned setting is honored when there is a specificity tie.