How to persist "package" states in Go? - go

My goal is to encapsulate in one module/package.
Main package:
package main
import (
"github.com/zenazn/goji"
"./routes"
)
func main(){
routes.Setup()
goji.Serve()
}
And another package:
package routes
import "github.com/zenazn/goji"
func Setup() {
goji.Get("/static", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "static!")
})
}
How can I do this?

goji, in your example, is a package. Not a variable.
You cannot pass packages around like this.
If you look at the example on the goji github page
You simply just call goji.Get from your Init function, and goji.Serve from your main
route.go
package route
import "route"
import "github.com/zenazn/goji"
func Init(){
goji.Get("/hello/:name", hello)
}
main.go
package main
import "github.com/zenazn/goji"
func main(){
route.Init()
goji.Serve()
}

Packages in go export constants, variables, types and functions that have uppercase letters as their name. The package itself is not something directly manipulatable by the program.
The package goji should be exporting a variable named something like goji.Goji if you want to directly access it from other packages. A better solution is to provide some functions in the package that allow you to register your functions/helpers.
You could also export a function from goji like:
func Set(s string, func(w http.ResponseWriter, r *http.Request)) { ... }
that could be used by other packages:
goji.Set("/static", myFunc)
The error you had "use of package goji without selector" is saying you can't use the name of the package without specifying which exported value you want from the package. It's expecting goji.something not goji by itself.
The function init() inside go files has special properties: see http://golang.org/ref/spec#Program_initialization_and_execution

Related

Doesn't see public functions of a struct Golang

I don't see the public method of the struct that I defined.
Can someone please let me understand why?
Here is the code:
// DataSaver.go:
package DataStorage
import (
"fmt"
"os"
)
type DataSaver struct {
// doesn't relevant to my question
fileName string
file *os.File
}
func PrintStr() {
fmt.Println("hello")
}
Then, I have a main method in other class. I initialized the struct and I wanted to call to PrintStr() function. However, I can't call to this method. Why?
Thank you!
That is not a method, it is just a function.
Import DataStorage (you should make it lower case) then you can call DataStorage.PrintStr()
I had a play around myself with this, and there are a few things to be careful of:
Make sure you import the package properly with respect to your GOPATH, e.g.
import "github.com/some-repo/datastorage"
And ensure that your packages/repos are in your GOPATH and in the correct directories (go is very fussy about how you do this)
Refer to your function using the package name like so:
func main(){
DataStorage.PrintStr()
}
The other poster is correct in that go conventions are to keep package names in lowercase.
Seems you dont have the concept of method receiver yet. Your method for the struct should be defined as below :
func (this DataSaver)PrintStr() {
fmt.Println("hello")
}
or
func (this *DataSaver)PrintStr() {
fmt.Println("hello")
}
which one you choose depends on you want to call the method on a pointer or not;

How golang's init() works. I am confused

I have a init() function defined in "config/config.go"
config.go
package config
import(
log "github.com/sirupsen/logrus"
)
func init() {
log.SetReportCaller(true)
}
I have another go file called auth.go in auth package
package auth
import(
log "github.com/sirupsen/logrus"
)
func auth(username string, pwd string) {
//some auth code
log.Info("Auth success")
}
When log.Info() is called in auth.go the log prints as below
2018-11-09T16:38:27+05:30 auth/auth.go:36 level=info msg="Auth success"
What I am confused here is that, how "log" in auth.go is aware of the settings done in config.go. log.SetReportCaller() is in config.go but even when auth.go is logged it takes settings of log.SetReportCaller() done in config.go
Since log.SetReportCaller() is not set in auth.go expected log should be as below without showing line number of caller method.
2018-11-09T16:38:27+05:30 level=info msg="Auth success"
main.go
package main
import (
"path/to/auth"
log "github.com/sirupsen/logrus"
"net/http"
)
func main() {
r := server.Route()
log.Info("Listening on 8080")
http.Handle("/", r)
log.Fatal(http.ListenAndServe(":8080", nil))
}
auth/router.go
package auth
import (
"github.com/gorilla/mux"
"github.com/rs/cors"
"net/http"
)
func Route() http.Handler {
r := mux.NewRouter()
// UI handlers
r.HandleFunc("/", IndexPageHandler)
r.HandleFunc("/login", LoginHandler).Methods("POST")
handler := cors.Default().Handler(r)
return
}
auth/login.go
package auth
import (
"fmt"
"path/to/config"
log "github.com/sirupsen/logrus"
"net/http"
)
func LoginHandler(w http.ResponseWriter, r *http.Request) {
username := r.FormValue("username")
password := r.FormValue("password")
status, userName, mail, _ := auth(username, password)
//some code goes here
}
Kindly explain how this is happening
Check this diagram to understand how init() work: diagram
Initialization order are as follows,
If a package imports other packages, the imported packages are initialised first.
Package level variables are initialised then.
init function of current package is called next. A package can have multiple init functions (either in a single file or distributed across multiple files) and they are called in the order in which they are presented to the compiler.
You will found an example explaining this here.
I am suspecting in dependency tree, there is a common ancestor file that import both config package and auth package.
Here is official doc on initialization: Package initialization
UPD: As you have added respective codes, let's visualize what is happening here. Look at the picture bellow,
What happening:
Your main package start initialing. But it is has imported auth package. So to complete initialization, auth package must be initialized.
auth package start initializing. But it has imported config package. So to complete initialization, config package must be initialized.
config package complete initialization(log.SetReportCaller(true) is called).
auth package complete initialization.
main package complete initialization.
main() function starts executing...
The behavior you're asking about actually has nothing to do with how init() functions work. SetReportCaller sets a global variable in github.com/sirupsen/logrus, not in your config or auth packages. So it doesn't matter where you call that function or where you call log.Info et al; the setting affects all calls to logrus regardless of call origin.

golang : Custom package and 'undefined'

I've read the doc on creating custom packages, etc but I can't seem to pin down what the problem is.
GOPATH=/Users/lrsmith/GoWorkSpace
|->bin
|->pkg
|->src
|->github.com
|->lrsmith
|-> zaphod
|-> zaphod.go
I've done a 'go get github.com/lrsmith/go-icinga2-api/iapi' and it
dropped it into the same dir as 'zaphod' and created and .a file under pkg.
GOPATH=/Users/lrsmith/GoWorkSpace
|->bin/
|->pkg/
|->..../iapi.a
|->src/
|->github.com/
|->lrsmith/
|-> zaphod/
|-> zaphod.go
|-> go-icinga2-api/
zaphod.go is very simple right now
package main
import (
"github.com/lrsmith/go-icinga2-api/iapi"
)
func main () {
t := iapi.Config("zaphod","beeblebrox","http://localhost",true)
}
When I do a go build in the zaphod directory I get
./zaphod.go:11: undefined: iapi.Config
I've read through the docs, checked cases and tried different structures but I can't seem to get it to load the package and let me call iapi.Config. The iapi code works and if I build something in the go-icinga2-api directory it works fine and the test all pass.
I want to create a separate project/code base that imports the go-icinga2-api and uses it, but can't seem to get it work.
Thanks
Len
Added info. The structure for go-icinga2-api is
go-icinga2-api
|-> iapi
|-> client.go
|-> client_test.go
|-> host.go
.......
client.go is
// Package iapi provides a client for interacting with an Icinga2 Server
package iapi
import (
"bytes"
"crypto/tls"
"encoding/json"
"fmt"
"net/http"
)
// Server ... Use to be ClientConfig
type Server struct {
Username string
Password string
BaseURL string
AllowUnverifiedSSL bool
httpClient *http.Client
}
// func Config ...
func (server *Server) Config(username, password, url string, allowUnverifiedSSL bool) (*Server, error) {
// TODO : Add code to verify parameters
return &Server{username, password, url, allowUnverifiedSSL, nil}, nil
}
I've tried with the .go files up one level, i.e. not nested underneath iapi/ to for the same results.
Updated Answer
In client.go, it looks like you're trying to use Config as a constructor for Server structs. Since Config is defined with a receiver (func (server *Server)), it's a method of Server and can't be called directly. You're code should work if you remove (server *Server).
It's idiomatic to name constructors New[type being returned], or New if the type is the same name as the package.
From the 3rd paragraph of the Package Names section of Idiomatic Go:
the function to make new instances of ring.Ring—which is the definition of a constructor in Go—would normally be called NewRing, but since Ring is the only type exported by the package, and since the package is called ring, it's called just New, which clients of the package see as ring.New
Original Answer
The import path should reference a directory. In your code, you then reference that package by whatever name is used in the package [name] in the .go files in that directory.
For example, if github.com/lrsmith/go-icinga2-api contains a file called api.go with the line package iapi, your importing package should look like this:
package main
import (
"github.com/lrsmith/go-icinga2-api"
)
func main () {
t := iapi.Config("zaphod","beeblebrox","http://localhost",true)
}
Note the declaration of the Config() function:
func (server *Server) Config(username, password, url string, allowUnverifiedSSL bool) (*Server, error)
It's a "method" that should be applied to a Server object:
server.Config(...)
Thus you need to first create a Server object (or you could try with nil):
var server iapi.Server
server, err := server.Config(...)
You're trying to run it as if it had the following declaration:
func Config(username, password, url string, allowUnverifiedSSL bool) (*Server, error)

How to access flags outside of main package?

We parse flags in main.go which is in main package, of course. Then we have another package where we want to read some flag's value.
flags.Args() work fine, it will return all non-flag values.
But I cannot figure out to how read already parsed value for a flag in a package other than main.
Is it possible?
Thanks
Amer
I had the same requirement recently and I wanted a solution that avoided calling flag.Parse repeatedly in init functions.
Perusing the flag package I found Lookup(name string) which returns a Flag which has a Value. Every built in Value implements the flag.Getter interface. The call chain looks like this:
flag.Lookup("httplog").Value.(flag.Getter).Get().(bool)
If you mistype the flag name or use the wrong type you get a runtime error. I wrapped the lookup in a function that I call directly where needed since the lookup and get methods are fast and the function is not called often. So the main package declares the flag.
// main.go
package main
import "flag"
var httplog = flag.Bool("httplog", false, "Log every HTTP request and response.")
func main() {
flag.Parse()
// ...
}
And the utility package, which is decoupled from main except for the flag name, reads the flag value.
// httpdiag.go
package utility
import "flag"
func logging() bool {
return flag.Lookup("httplog").Value.(flag.Getter).Get().(bool)
}
You can define the var storing the flag in the separate package, as an exported variable, then call the flag parsing in the main package to use that variable, like this:
mypackage/const.go
var (
MyExportedVar string
)
mainpackage/main.go
func init() {
flag.StringVar(&mypackage.MyExportedVar, "flagName", "defaultValue", "usage")
flag.Parse()
}
This way, everybody can access that flag, including that package itself.
Note:
this only works for exported variables.
You can define the flag in that package and call flag.Parse() in func init(), flag.Parse can be called multiple times.
However, if you want to access the flag's value from multiple packages you have to expose it or create an exposed function to check it.
for example:
// pkgA
var A = flag.Bool("a", false, "why a?")
func init() {
flag.Parse()
}
// main package
func main() {
flag.Parse()
if *pkgA.A {
// stuff
}
}
Also you can use FlagSet.Parse(os.Args) if you want to reparse the args.
When you parse your flags, parse them into global variables which start with an initial Capital so they are public, eg
package main
var Species = flag.String("species", "gopher", "the species we are studying")
func main() {
flag.Parse()
}
Then in your other package you can refer to them as
package other
import "/path/to/package/main"
func Whatever() {
fmt.Println(main.Species)
}

Package imports, cannot use struct from imported package

This is probably a noob mistake but I cannot figure this out.
In main.go I am importing a package.
import(
"models/users"
)
// ...
func main() {
r.HandleFunc("/users/list", UsersModel.List())
The package is stored in src/models/users
The users package looks like this:
package users
import (
"gopkg.in/mgo.v2"
)
// ...
/**
* User Model
*/
type UsersModel struct {
}
// Add methods to the UsersModel type.
func (m *UsersModel) List() {
// ...
When I run the code I get the following error.
src\main.go:9: imported and not used: "models/users" src\main.go:20:
undefined: UsersModel
The go code appears to be valid as if I invalidate it throws an error. How do I export the UsersModel type from the users package?
You need to prefix your use of UsersModel with the package name users, like so:
um := users.UserModel{}
UsersModel.List() is also wrong: go doesn't have "static methods" or "class methods".

Resources