Specifying template filenames for template.ParseFiles - go

My current directory structure looks like the following:
App
- Template
- foo.go
- foo.tmpl
- Model
- bar.go
- Another
- Directory
- baz.go
The file foo.go uses ParseFiles to read in the template file during init.
import "text/template"
var qTemplate *template.Template
func init() {
qTemplate = template.Must(template.New("temp").ParseFiles("foo.tmpl"))
}
...
Unit tests for foo.go work as expected. However, I am now trying to run unit tests for bar.go and baz.go which both import foo.go and I get a panic on trying to open foo.tmpl.
/App/Model$ go test
panic: open foo.tmpl: no such file or directory
/App/Another/Directory$ go test
panic: open foo.tmpl: no such file or directory
I've tried specifying the template name as a relative directory ("./foo.tmpl"), a full directory ("~/go/src/github.com/App/Template/foo.tmpl"), an App relative directory ("/App/Template/foo.tmpl"), and others but nothing seems to work for both cases. The unit tests fail for either bar.go or baz.go (or both).
Where should my template file be placed and how should I call ParseFiles so that it can always find the template file regardless of which directory I call go test from?

Helpful tip:
Use os.Getwd() and filepath.Join() to find the absolute path of a relative file path.
Example
// File: showPath.go
package main
import (
"fmt"
"path/filepath"
"os"
)
func main(){
cwd, _ := os.Getwd()
fmt.Println( filepath.Join( cwd, "./template/index.gtpl" ) )
}
First off, I recommend that the template folder only contain templates for presentation and not go files.
Next, to make life easier, only run files from the root project directory. This will help make the path to an file consistent throughout go files nested within sub directories. Relative file paths start from where the current working directory, which is where the program was called from.
Example to show the change in current working directory
user#user:~/go/src/test$ go run showPath.go
/home/user/go/src/test/template/index.gtpl
user#user:~/go/src/test$ cd newFolder/
user#user:~/go/src/test/newFolder$ go run ../showPath.go
/home/user/go/src/test/newFolder/template/index.gtpl
As for test files, you can run individual test files by supplying the file name.
go test foo/foo_test.go
Lastly, use a base path and the path/filepath package to form file paths.
Example:
var (
basePath = "./public"
templatePath = filepath.Join(basePath, "template")
indexFile = filepath.Join(templatePath, "index.gtpl")
)

Related

Google cloud Function - Golag no matching function found with name

I'm trying to run my function on GCP via gitlab CI, but it returns the following error:
Function failed to start: no matching function found with name: "RunUsers"
path: test/function.go
file name "function.go"
deploy
gcloud functions deploy test
--runtime=go116
--entry-point=RunUsers
--region=us-east1
--source=.
--trigger-http
package function
import (
"io"
"net/http"
"github.com/GoogleCloudPlatform/functions-framework-go/functions"
_ "github.com/GoogleCloudPlatform/functions-framework-go/funcframework"
)
func init() {
functions.HTTP("RunUsers", RunUsers)
}
func RunUsers(w http.ResponseWriter, r *http.Request) {
io.WriteString(w, "OK")
}
Should I remove .mod in
.gcloudignore file?
gcloudignore:
# This file specifies files that are *not* uploaded to Google Cloud
# using gcloud. It follows the same syntax as .gitignore, with the addition of
# "#!include" directives (which insert the entries of the given .gitignore-style
# file at that point).
#
# For more information, run:
# $ gcloud topic gcloudignore
#
.gcloudignore
# If you would like to upload your .git directory, .gitignore file or files
# from your .gitignore file, remove the corresponding line
# below:
.git
.gitignore
tests/
README.md
Makefile
env/
*.json
sonar-project.properties
.gitlab-ci.yml
cmd/
*.mod
Has anyone gone through this problem?
You are specifying the entrypoint as:
--entry-point=RunUsers
The function declaration is:
func runUsers(w http.ResponseWriter, r *http.Request)
Notice the difference in case for the function names: runUsers versus RunUsers.
The problem occurred in the deploy, the code in was correct and the function working
When trying to run the code locally I encountered the same error:
Serving function: "Execute"2023/02/06 11:57:36 funcframework.Start: no matching function found with name: "Execute"
The solution required including a func init() in my code (along with the cmd/main.go file), see documentation:
https://github.com/GoogleCloudPlatform/functions-framework-go#quickstart-hello-world-on-your-local-machine

How to get the root path of the project

I run the following code workingDir, _ := os.Getwd()
in server.go file which is under the root project and get the root path,
Now I need to move the server.go file to the following structure
myapp
src
server.go
and I want to ge the path of go/src/myapp
currently after I move the server.go under myapp->src>server.go I got path of go/src/myapp/src/server.go and I want to get the previews path ,
How should I do it in go automatically? or should I put ../ explicit in get path ?
os.Getwd() does not return your source file path. It returns program current working directory (Usually the directory that you executed your program).
Example:
Assume I have a main.go in /Users/Arman/go/src/github.com/rmaan/project/main.go that just outputs os.Getwd()
$ cd /Users/Arman/go/src/github.com/rmaan/project/
$ go run main.go
/Users/Arman/go/src/github.com/rmaan/project <nil>
If I change to other directory and execute there, result will change.
$ cd /Users/Arman/
$ go run ./go/src/github.com/rmaan/project/main.go
/Users/Arman <nil>
IMO you should explicitly pass the path you want to your program instead of trying to infer it from context (As it may change specially in production environment). Here is an example with flag package.
package main
import (
"fmt"
"flag"
)
func main() {
var myPath string
flag.StringVar(&myPath, "my-path", "/", "Provide project path as an absolute path")
flag.Parse()
fmt.Printf("provided path was %s\n", myPath)
}
then execute your program as follows:
$ cd /Users/Arman/go/src/github.com/rmaan/project/
$ go run main.go --my-path /Users/Arman/go/src/github.com/rmaan/project/
provided path was /Users/Arman/go/src/github.com/rmaan/project/
$ # or more easily
$ go run main.go --my-path `pwd`
provided path was /Users/Arman/go/src/github.com/rmaan/project/
If your project is a git repository you can also use the git rev-parse command with the --show-toplevel:
cmdOut, err := exec.Command("git", "rev-parse", "--show-toplevel").Output()
if err != nil {
assert.Fail(t, fmt.Sprintf(`Error on getting the go-kit base path: %s - %s`, err.Error(), string(cmdOut)))
return
}
fmt.Println(strings.TrimSpace(string(cmdOut)))
It'll print out the git project home path wherever in the sub path you are

Read file from relative path with different callers

I'm trying to read from a file in my project's directory.
My problem is, that depending on the caller, the path changes. The caller changes, because I want to unit test this code and the caller is not Main.go anymore.
This is what my project structure looks like:
The code where I try to access specialChars.txt from looks like this:
func RemoveSpecialChars(word string) string {
file, err := ioutil.ReadFile("wordlists/specialChars.txt")
[...]
}
This code works for the start from Main.go but not for the start from CleanupUtil_test.go. To get it working from the test I would need file, err := ioutil.ReadFile("../wordlists/specialChars.txt")
I found answers like this one: https://stackoverflow.com/a/32163888/2837489
_, filename, _, ok := runtime.Caller(0) which is obviously also dependent on the caller.
Is it possible to get the projects root path independent of the calling function?
Or is my code design wrong? Should I pass the file path into the function?
Starting from Go 1.16, you can use the embed package. This allows you to embed the files in the running go program. It comes with the caveat that the referenced directory needs to exist at or below the embedding file. In your case, the structure would look as follows:
-- main.go
-- cleanup
-- wordlist
\- specialChars.txt
CleanupUtil.go
CleanupUtil_test.go
You can reference the file using a go directive
// CleanupUtil.go
package cleanup
import (
"embed"
)
//go:embed wordlists/specialChars.txt
var content embed.FS
func RemoveSpecialChars(word string) string {
file, err := content.ReadFile("wordlists/specialChars.txt")
[...]
}
This program will run successfully regardless of where the program is executed. You should be able to reference this code in both your main.go file and your CleanupUtil_test.go file.
Pass in the filepath as a parameter to the function (as indicated in your last question).
More details:
The relative path "wordlists/specialChars.txt" is in fact not dependent on where the source file is located (such as Main.go or CleanupUtil_test.go), but where you execute it from. So you could run your tests from your root directory and then it would actually work. In short, the current working directory is relevant.
Still, I'd recommend specifying the path, because that makes your function more reusable and universal.
Maybe you don't even need to put this information into a file, but can simply have a string containing those chars. In this case you could also check if https://golang.org/pkg/regexp/#Regexp.ReplaceAll already covers your use case.

File path in golang

I have a project with next structure:
|_main.go
|_config
|_config.go
|_config_test.go
|_config.json
I'm having next code line in config.go:
file, _ := os.Open("config/config.json")
When I'm executing method contained this code line from main.go all is working. But when I'm trying to execute this method from config_test.go it produces error:
open config/config.json: no such file or directory
As I understood it is a working directory issue because I'm launching same code with relative path from different directories. How can I fix this problem without using full path in config.go?
Relative paths are always resolved basis your current directory. Hence it's better to avoid relative paths.
Use command line flags or a configuration management tool (better approach) like Viper
Also according to The Twelve-Factor App your config files should be outside your project.
Eg usage with Viper:
import "github.com/spf13/viper"
func init() {
viper.SetConfigName("config")
// Config files are stored here; multiple locations can be added
viper.AddConfigPath("$HOME/configs")
errViper := viper.ReadInConfig()
if errViper != nil {
panic(errViper)
}
// Get values from config.json
val := viper.GetString("some_key")
// Use the value
}

How to get the directory of the package the file is in, not the current working directory

I'm making a package to make API calls to a service.
I have a test package that I use just to test the API calls and test the functions of the main package which I just include the other package into.
In my main package that I'm working on I have
ioutil.ReadFile(filepath.Abs("Filename.pub"))
Which is ok, but when I call it from my test package e.g.
/Users/####/gocode/src/github.com/testfolder go run main.go
it tells me
panic: open /Users/####/gocode/src/github.com/testfolder/public.pub: no such file or directory
The problem is, is it is looking for public.pub inside of testfolder instead of github.com/apipackage/ which is where it is.
Just to clarify this mess of words:
The API Package has a function that reads from the same directory
But because I'm including the API package and Testfolder is the CWD when I go run main.go it is instead trying to get it from the testfolder instead even though the main.go doesn't have the function and is just including it.
runtime.Caller is what you want I believe.
Here is a demonstration :
package main
import (
"fmt"
"runtime"
"path"
)
func main() {
_, filename, _, ok := runtime.Caller(0)
if !ok {
panic("No caller information")
}
fmt.Printf("Filename : %q, Dir : %q\n", filename, path.Dir(filename))
}
https://play.golang.org/p/vVa2q-Er6D
Starting from Go 1.16, you can use the embed package. This allows you to embed the files in the running go program. The referenced file needs to be at or below the embedding file. In your case, the structure would look as follows:
-- apipackage
\- public.pub
\- apipackage.go
-- testfolder
\- main.go
You can reference the file using a go directive
// apipackage.go
package apipackage
import (
"embed"
)
//go:embed public.pub
var content embed.FS
func GetText() string {
text, _ := content.ReadFile("public.pub")
return text
}
This program will run successfully regardless of where the program is executed.

Resources