* Added method to write into TOML file.
* Added functionality to export configuration based on config type. The feature supports JSON and TOML.
* Added method to write into YAML file.
* Fixed the issue of incorrect defer and error checking order. The error checking must be first otherwise it will cause panic.
* Add WriteConfig methods
* Add support for toml
* Add shared write function and safe methods
* Fix incorrectly modified imports
* Remove extra comments
* Fix spelling
* Make marshal spelling consistent throughout
* Add support for remaining configuration types
This commit moves a significant portion of the code back to viper.go to
facilitate having access to the object when reading the files. The purpose is to
add properties to the viper object at read time, so that we can add the comments
back to the file when writing.
* Add tests for each written file type
* Modify test for updated HCL specification
* Modify to only support HCL write in Go 1.7
* Revert "Modify to only support HCL write in Go 1.7"
This reverts commit 12b34bc4eb92cbf8ebfd56b79519f448607e3e51.
* Need to truncate the file before writing
* Write all settings including overrides
* Use filename variable
* Lint remote.go
* Fix toml return count error
If the user creates a invalid config file while watching for config
changes, the previous, valid config is not retained. This commit only
overwrites the running config if unmarshalling was successful.
* Fixed: values bound with BindEnv added to AllKeys()
Cast was not working, and v.env wasn't used when merging keys.
Rewrote explicit and specific casts for maps storing strings or FlagValues.
* Added: test for BindEnv() and AllKeys()
To make sure AllSettings() and Unmarshal() will consider environment
variables added with BindEnv().
* Fixed: insensitiviseMaps and tests
All keys (even nested ones) are now lower-cased recursively.
On the way, map[interface{}]interface{} are cast to map[string]interface{}
* Changed: simplified find() fast path and increase performance
Removed searchMapForKey(), fast path directly integrated into searchMap() and
searchMapWithPathPrefixes()
=> more generic (searchMapForKey() wasn't called everywhere it should have)
At the same time, significantly speed up searchMap() and searchMapWithPathPrefixes(),
which are still used for nested keys: the assumption that map keys are all
lower-cased allows to perform
val = m[key]
instead of
for k, v := range m {
if strings.ToLower(k) == strings.ToLower(key) {
val = v
}
}
=> i.e., directly access the map instead of enumerate the keys
Fixes#71, #93, #158, #168, #209, #141, #160, #162, #190
* Fixed: indentation in comment
* Fixed: Get() returns nil when nested element not found
* Fixed: insensitiviseMaps() made recursive so that nested keys are lowercased
* Fixed: order of expected<=>actual in assert.Equal() statements
* Fixed: find() looks into "overrides" first
* Fixed: TestBindPFlags() to use a new Viper instance
* Fixed: removed extra aliases from display in Debug()
* Added: test for checking precedence of dot-containing keys.
* Fixed: Set() and SetDefault() insert nested values
* Added: tests for overriding nested values
* Changed: AllKeys() includes all keys / AllSettings() includes overridden nested values
* Added: test for shadowed nested key
* Fixed: properties parsing generates nested maps
* Fixed: Get() and IsSet() work correctly on nested values
* Changed: modifier README.md to reflect changes
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)
Then checking the file name in the event handler. This seems to be the only robust way
of handling changes from a single file on multiple platforms and editors.
See #142
* Only add *the* config file, not all possible folders
* Trigger reload on both write and create events;
the latter is what we get from atomic save editors (like TextMate) once https://github.com/go-fsnotify/fsnotify/pull/111 is merged
See #142
This patch fixes a bug with how Viper handle's key casing when keys are
nested. While Viper is generally case-insensitive, this was not the case
with regards to nested keys. This patch makes nested keys insensitive as
well.
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`.
Using an interface allows people to use their favourite flag system
with viper without being restricted to the semantics of pflag or the
standard library.
This change introduce two new functions `BindFlagValues` and
`BindFlagValue` that behave like `BindFlags` and `BindFlag` but using
the new interface as values.
This change also introduces two internal structures to transform
`*pflag.FlagSet` and `*pflag.Flag` into the new interface. This way,
viper keeps working as expected for people that are currently using the
pflag package without breaking backwards compatibility.
Signed-off-by: David Calavera <david.calavera@gmail.com>
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 patch alters the way flags are handled to coincide with the
documentation on the Viper README. The documentation indicated that flag
bindings were late, when in fact they were very, very early. This patch
changes flag bindings to behave as late bindings.
This patch adds a feature, if enabled, will infer a value's type from
its default value no matter from where else the value is set. This is
particularly important when working with environment variables. For
example:
package main
import (
"fmt"
"os"
"github.com/spf13/viper"
)
func print(name string, val interface{}) {
fmt.Printf("%-15[1]s%-15[2]T%[2]v\n", name, val)
}
func main() {
viper.BindEnv("mykey", "MYPREFIX_MYKEY")
viper.SetDefault("mykey", []string{})
os.Setenv("MYPREFIX_MYKEY", "a b c")
v1 := viper.GetStringSlice("mykey")
v2 := viper.Get("mykey")
print("v1", v1)
print("v2", v2)
}
When this program is executed the following is emitted:
[0]akutz@pax:ex$ ./ex1
v1 []string [a b c]
v2 string a b c
[0]akutz@pax:ex$
You may wonder, why is this important? Just use the GetStringSlice
function. Well, it *becomes* important when dealing with marshaling.
If we update the above program to this:
package main
import (
"fmt"
"os"
"github.com/spf13/viper"
)
type Data struct {
MyKey []string
}
func print(name string, val interface{}) {
fmt.Printf("%-15[1]s%-15[2]T%[2]v\n", name, val)
}
func main() {
viper.BindEnv("mykey", "MYPREFIX_MYKEY")
viper.SetDefault("mykey", []string{})
os.Setenv("MYPREFIX_MYKEY", "a b c")
v1 := viper.GetStringSlice("mykey")
v2 := viper.Get("mykey")
print("v1", v1)
print("v2", v2)
d := &Data{}
viper.Marshal(d)
print("d.MyKey", d.MyKey)
}
Now we can see the issue when we execute the updated program:
[0]akutz@pax:ex$ ./ex2
v1 []string [a b c]
v2 string a b c
d.MyKey []string []
[0]akutz@pax:ex$
The marshalled data structure's field MyKey is empty when in fact it
should have a string slice equal to, in value, []string {"a", "b",
"c"}.
The problem is that viper's Marshal function calls AllSettings which
ultimately uses the Get function. The Get function does try to infer
the value's type, but it does so using the type of the value retrieved
using this logic:
Get has the behavior of returning the value associated with the
first place from where it is set. Viper will check in the
following order:
* override
* flag
* env
* config file
* key/value store
* default
While the above order is the one we want when retrieving the values,
this patch enables users to decide if it's the order they want to be
used when inferring a value's type. To that end the function
SetTypeByDefaultValue is introduced. When SetTypeByDefaultValue(true)
is called, a call to the Get function will now first check a key's
default value, if set, when inferring a value's type. This is
demonstrated using a modified version of the same program above:
package main
import (
"fmt"
"os"
"github.com/spf13/viper"
)
type Data struct {
MyKey []string
}
func print(name string, val interface{}) {
fmt.Printf("%-15[1]s%-15[2]T%[2]v\n", name, val)
}
func main() {
viper.BindEnv("mykey", "MYPREFIX_MYKEY")
viper.SetDefault("mykey", []string{})
os.Setenv("MYPREFIX_MYKEY", "a b c")
v1 := viper.GetStringSlice("mykey")
v2 := viper.Get("mykey")
print("v1", v1)
print("v2", v2)
d1 := &Data{}
viper.Marshal(d1)
print("d1.MyKey", d1.MyKey)
viper.SetTypeByDefaultValue(true)
d2 := &Data{}
viper.Marshal(d2)
print("d2.MyKey", d2.MyKey)
}
Now the following is emitted:
[0]akutz@pax:ex$ ./ex3
v1 []string [a b c]
v2 string a b c
d1.MyKey []string []
d2.MyKey []string [a b c]
[0]akutz@pax:ex$
Viper should not be searching for config.{json,toml,yaml,yml}
in the directory where the `hugo` executable binary is located,
i.e. do not try to look for e.g. $GOPATH/bin/config.toml or
/usr/local/bin/config.toml