Viper, cannot get the value initialized outside the functions - go

I have a problem using a viper, I have assigned variable by viper method, so when I try to get value inside any function I have a null value. Does anybody have any idea why does it happen so? Any other variables initialization works fine, but not viper GetString method.
Structure:
main.go
package main
import (
"project/Model"
"github.com/spf13/viper"
...
)
func main() {
//Config handling
viper.SetConfigName("main")
viper.AddConfigPath("/config/")
err = viper.ReadInConfig()
...
}
Package Model
package Model
import ("github.com/spf13/viper"
...
)
var sqlhost = viper.GetString("db.host")
func foo() {
log.Println(sqlhost)
}

I suspect that doing package variable initialization as you show will lead to the viper configuration not being complete before performing the GetString(), and therefore the zero (empty) string value is what is returned. Without seeing how you initialize your viper config, I'm guessing that it's not in an init() function, but is in a main() function that hasn't run yet when the package variable is assigned.
You should probably retrieve the viper.GetString("db.host") inside the highest level function that needs it rather than during package initialization.

Related

How to initialize global variables of a package in a lazy manner, from the user input?

There're multiple packages in my projects: the main one and secondary ones. The main one accepts input from the users and initializes some variables in itself.
The secondary packages contain some global variables that can only be initialized once the one of the "main" package have been, and not earlier.
I'm aware of the function "init()" of a package but this won't work in my case because it's not capable on initializing data in a lazy way, and with some arguments or data from the outside. Whereas this is what I want.
//main package
var (
a1 int
a2 float
a3 string
a4 MyStruct
a5 MyStruct2
)
func main() {
//process user input
//...........
//some other stuff
// I want that by this point, the global variables of package2 to have been initialized
// **based on** the processed user input from above
//namely, before the first access to the package
package2.Func1()
// I want that by this point, the global variables of package3 to have been initialized
// **based on** the processed user input from above
//namely, before the first access to the package
package3.Func11()
}
Is there a solution for this?
I could've a function "initData()" in the packages package2 and package3, but how would I ensure that it won't be forgotten to be called as the very first function, in a mandatory way, before calling any other functions of a package?
you should probably use an init function in main.go
func init(){
// get user input and set variables
// call package 2 and 3 after setting the variables in main
package2.Func1()
package3.Func1()
}
func main(){
// everything is setup properly when code in this main function is launched
}

Go package Global Variable usage within imports

I am creating a package that will be called from other services that I have in Go. If a service imports this package, and this package has global variables, will the component store the global variables in its' own memory?
So if my package looks something like this,
var global1 = ""
var global2 = ""
var global3 = ""
var global4 = ""
func setAllGlobalValues() error {
// some logic that checks if globals are nil
// if not setting it to a value after some computation.
// returns nil or an actual error.
}
func DoesSomethingUsingGlobalVars() (bool, error) {
// sets and uses the global vars.
// Does some sort of computation and returns a bool, nil or nil,error
}
Then in the service I would import this package and use the doesSomethingUsingGlobalVars function.
Will the component using this package store the global variables in its own memory? I can't really test it now with my services with the way things are set up so I'm just curious if anyone knows.
Essentially, will this work or will the global vars be nil each and every-time anything is called from a service that imports this package?
Thanks in advance all!
When your program imports this package, the globals will be set when you call SetAllGlobalValues(), and later, when you call DoesSomethingUsingGlobalVars(), those values will already be set. Note that the first letter of those function names must be capitalized so that they are exported and available for use by other packages. If the variables are not exported, as shown in your code snippet, you will not be able to access them directly from the caller.
It seems as if you are trying to reinvent objects. Instead of your code, do
something like this:
package some
import "fmt"
type Thing struct {
One string
Two string
Three string
Four string
}
func NewThing() Thing {
return Thing{"One", "Two", "Three", "Four"}
}
func (t Thing) Print() {
fmt.Println(t.One, t.Two, t.Three, t.Four)
}
Then, the "global variables" are only calculated once, when you call NewThing.

How to call unexported function within package?

I'm trying to write a package to work with. Here is sample code:
package redis
import (
"fmt"
"github.com/gomodule/redigo/redis"
"log"
"os"
)
var conn redis.Conn
func init() {
// code to set conn variable
}
func do(command string, args ...interface{}) (interface{}, error) {
init()
return conn.Do(command, args)
}
This code does not compile and compilator says undefined: init. When I change init() to Init() it works, but I don't want it to be available outside of package.
Wherever I read about this problem, it says about calling unexported function from another package, but here I'm calling it from same one.
Also, Goland IDE marks function call as unresolved reference and suggests to create it. But when I do (by IDE itself), it still doesn't see it.
init() is a special function. From the language spec:
func init() { … }
Multiple such functions may be defined per package, even within a
single source file. In the package block, the init identifier can
be used only to declare init functions, yet the identifier itself
is not declared. Thus init functions cannot be referred to from
anywhere in a program.
Use init() for package level initializations.
In Go, init is reserved for initializing work that needs to be done in a package, eg. adding some implementation to some registry.
To solve this problem, you need to use another name.
Take a look at this question to learn more about init if you are interested.

Value assigned inside init doesnt maintain the value

I'm working on Golang and am a little confused about how the func init() works. Lets's say that I have 2 packages called main and pkg2
inside main I am trying to call a variable that is inside pkg2 but its giving me nil. Basically this is the structure:
Main Package:
import (
...
"github.com/myproject/config/pkg2"
)
func main () {
if pkg2.Myvariable == nil {
//it's nil. And it's entering in this conditional don't know why
}
}
PKG2 Package:
package pkg2
import (
...some imports...
)
var MyVariable
func init () {
MyVariable := "something"
//Here I assign a value to MyVariable
//I set an if here to check if it's executed
//and MyVariable get a value correctly
}
I also noticed that the init function is executed before I even call pkg2.Myvariable. So, briefly: inside main package it's given nil, but inside init the value is assigned correctly, why then it return to nil?
What Am I missing? Thank you!
I believe you should change := to =, because that way you are introducing a new var.

Getting a use of package without selector error

I'm using this config library called Viper
In my main I have this:
viper.SetConfigName("development")
viper.AddConfigPath("config/settings/")
err := viper.ReadInConfig()
if err != nil {
fmt.Println("viper config read error %v", err)
}
I then have a struct that takes a viper as parameter:
type MyConfig struct {
v *viper.Viper
}
In my main I have a function that returns this MyConfig like:
func NewMyConfig(v *viper.Viper) *MyConfig {
return &MyConfig{v: v}
}
I am getting this error:
./main.go:55: use of package viper without selector
Not sure what I should be doing?
When you import a package like
import "github.com/spf13/viper"
the package name (which is viper in this case) will be available to you as a new identifier. You may use this identifier to construct qualified identifiers to refer to exported identifiers of the package (identifiers that start wtih an uppercase letter).
The package name itself cannot be used by itself. The line that gives you error:
myConfig = NewMyConfig(&viper)
You used package name viper without specifying what exported identifier you want to refer to from the package.
You want to use your NewMyConfig() function to obtain a pointer to a new value of your MyConfig struct. Your NewMyConfig() function expects a value of *viper.Viper. Since the viper.Viper struct contains unexported fields, you can just create it like &viper.Viper{}, but the viper package exports a function viper.New() which can be used to obtain a pointer to a new, initialized viper.Viper value. You may use it like:
vp := viper.New()
myConfig = NewMyConfig(vp)
Note that the viper package declares an internal, global, unexported viper.Viper "instance". There are many exported functions that match methods of the viper.Viper type. These "matching" functions work on the global, unexported viper.Viper instance. So you may choose to use all the exported global functions of the viper package, or create your own Viper instance and then keep using its methods afterwards.

Resources