Rewriting of find(), which now checks for in-depth values,
and ignores shadowed ones.
* When looking for "foo.bar":
- before: if "foo" was defined in some map, "foo.*" was looked for
only in this map
- now: "foo.*" is looked for in all maps, the first one with a value is
used (even if "foo" was defined elsewhere, in a higher-priority map).
* Also, find() tests that the requested key is not shadowed somewhere
on its path.
e.g., {"foo.bar": 1} in the config map shadows "foo.bar.baz" in
the defaults map
* Lastly, if in the config map, config["foo"]["bar"] and config["foo.bar"]
coexist, the latter value is used.
(this should not be necessary for other maps, since by construction
their keys should not contain dots)
=> Get() and IsSet() corrected and simplified, since all the logic
about value retrieval has been put in find()
+ README.md modified accordingly:
In Section “Accessing nested keys”, to reflect the corrected behavior of find():
- paths searched for at all levels, not only the one of the first sub-key;
- paths may be shadowed by a shorter, higher priority, path.
+ tests modified accordingly
If a property name contains a ".", it is split and generates sub-maps in
the "config" map, as for the other configuration sources.
Tests updated accordingly, to reflect these nested values.
"clothing.jacket.price" exists in defaults map, but "clothing.jacket" exists
and is a value in config map
=> should remain undefined (“shadowed” by the config)
overrides (elements added with Set()) should have highest priority,
checked first (was after PFlags).
From README.md:
> Viper uses the following precedence order. Each item takes precedence
> over the item below it:
> • explicit call to Set
> • flag
> • env
> • config
> • key/value store
> • default
+ fix TestBindPFlags() to use a new Viper instance (otherwise, "port" was
defined in an earlier test function with Set() and took precedence)
Also avoid doing a strings.Split in the Get common case.
Any TRACE statements in these hot paths must be totally turned off when not testing.
```
benchmark old ns/op new ns/op delta
BenchmarkGetBool-4 4090 409 -90.00%
BenchmarkGetBoolFromMap-4 6.33 6.28 -0.79%
benchmark old allocs new allocs delta
BenchmarkGetBool-4 6 3 -50.00%
BenchmarkGetBoolFromMap-4 0 0 +0.00%
benchmark old bytes new bytes delta
BenchmarkGetBool-4 129 33 -74.42%
BenchmarkGetBoolFromMap-4 0 0 +0.00%
```
Fixes#242
* Fix typo in description of UnmarshalExact
* Omit 2nd values from range loops
* Delete findCWD method from util (was unused)
* Edit documentation according to golint
* Fix documentation in util
* Use RemoteProvider interface instead of defaultRemoteProvider
* Fix err variable in BindFlagValues
I stumbled over this when trying to merge multiple configs.
```
viper.SetConfigName("default")
err := viper.MergeInConfig()
```
which caches file path resolvemenet in `v.configFile`
and then
```
viper.SetConfigName("prod")
err := viper.MergeInConfig()
```
which reuses `v.configFile` without updating it accordingly to the new name.
See c1ccc378a0/viper.go (L1240)
This patch adds the `MergeConfig` and `MergeInConfig` functions to
enable reading new configuration files via a merge strategy rather
than replace. For example, take the following as the base YAML for a
configuration:
hello:
pop: 37890
world:
- us
- uk
- fr
- de
Now imagine we want to read the following, new configuration data:
hello:
pop: 45000
universe:
- mw
- ad
fu: bar
Using the standard `ReadConfig` function the value returned by the
nested key `hello.world` would no longer be present after the second
configuration is read. This is because the `ReadConfig` function and
its relatives replace nested structures entirely.
The new `MergeConfig` function would produce the following config
after the second YAML snippet was merged with the first:
hello:
pop: 45000
world:
- us
- uk
- fr
- de
universe:
- mw
- ad
fu: bar
Examples showing how this works can be found in the two unit tests
named `TestMergeConfig` and `TestMergeConfigNoMerge`.
This patch refactors the IsSet function to examine the keys in order
to see if a key is set instead of simply checking if a value is nil.
This change is necessary due to the fact that default values via
flag bindings will result in the old logic always being true for
the IsSet function due to a type's default value such as 0 for an
integer or an empty string for a string. While a type's default
value may be preferable when getting the value for a key, it
results in a false positive when determining if a key is actually
set. This change enables users to detect whether a key is set by
only returning a flag's value if it has changed.
This reverts commit 8d9577a72e.
The commit is reasonable enough, but this is a major breaking change for Hugo.
We have to figure out how to handle this before we introduce this one.
See https://github.com/spf13/hugo/issues/1129