Read file from relative path with different callers - go

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.

Related

How to call static HTML files

I created a static folder that contains index.html file, and in my go file, I wrote:
package main
import (
"net/http"
)
func main() {
http.Handle("/", http.FileServer(http.Dir("./static")))
http.ListenAndServe(":8482", nil)
}
And it works fine upon exploring http://localhost:8482/
I tried to write the code as:
http.Handle("/static", http.FileServer(http.Dir("./static")))
But it fails upon exploring http://localhost:8482/static with 404 error
http.Handle("/static", http.FileServer(http.Dir("./static"))) simply means, "whenever someone connects to .../static, reroute the entire request to a file server rooted at directory ./static".
However, the url is passed along to the file server as-is. In other words, the file server receives the request from the user, and believes that the user is looking for a file called "static" within the root ("./static") directory.
In fact, if you simply placed a file called "static" in your "./static" directory, going to .../static would serve that file.
So the fix requires two things:
Change the path prefix to "/static/" rather than "/static", so that all files within the static directory can be rerouted to the file server (rather than only the "/static" request)
Strip the "/static/" path prefix from the request before passing it to the file server.
Like so:
http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("./static"))))

Golang beginner, on Windows: System cannot find file specified

Only just now starting with golang, with only a small amount of programming experience before this. I'm trying to create a script that will summarize certain things from a csv file, but I haven't even gotten past testing out file reading yet.
I was having trouble reading the excel files, and kept getting the "System cannot find file specified" error. So I thought I'd see if I could at least get it to read a simple text file, using an example from golangbot, which looks like this:
package main
import (
"fmt"
"io/ioutil"
)
func main() {
data, err := ioutil.ReadFile("test.txt")
if err != nil {
fmt.Println("File reading error", err)
return
}
fmt.Println("Contents of file:", string(data))
}
That simple. The text file is located within the same folder (in %USERPROFILE%/go/src, and /go/ is my GOPATH) as the actual code file I'm attempting to run, and yes, it is called "test.txt". Yet, every attempt to run gives me the same error message, that the system cannot find the specified file (test.txt).
Running any other kind of .go file or building one from this location works just fine. I'm seen others with this error, but it seemed like it was always to do with the GOPATH being set wrong.
I'm frustrated that I even have to ask about something like this, but it's all I could think of right now. Is there something wrong with the locations of my files or the GOPATH itself, or is this something different?
Thank you
Welp, the problem was solved. Turns out, the actual name of the txt file was test.txt.txt. Thanks to notepad and my own lack of awareness.
Bit embarrassing, really. Changing the name worked.
when you are trying this with your notepad. Be cautious while saving it.
The file type should be compatible
File -> save as >
FileName : FileName.go
Save as type : All Files
Now execute as ("folder path">go run FileName.go
Then the result. Kudos
I have been having a similar issue as well. I would try to do this in order to fix it. delete the file and create a new one. That is a simple solution in my opinion. Make sure to copy the code and then paste onto the new file. Mkdir
cd into that directory touch or nano
create a new file. then open that file. then do go run that file name.
it should work.

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.

Specifying template filenames for template.ParseFiles

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")
)

Resources