How to pass magickWand reference to functions - go

May I know how to pass imagick.MagickWand struct to functions and apply it methods on it? It seems that the imagick.NewMagickWand return the type of *imagick.MagickWand, isn't it?
I can't get it done, keep getting the error message: ERROR_WAND: ContainsNoImages `MagickWand-0'.
How to pass proper struct reference to the function so that the mw can be continuously used in those functions?
func generateImage() error {
// skip error handling
var err error
mw := imagick.NewMagickWand()
defer mw.Destroy()
err = createCanvas(mw, "red") // create canvas
err = compositeIcon(mw, "c:/icon.png") // add icon
err = addText(mw, "Hello world") // add text
err = mw.WriteImage("c:/output") // get the output
}
func createCanvas(mw *imagick.MagickWand, color string) error {
// skip error handling
var err error
pw := imagick.NewPixelWand()
defer pw.Destroy()
pw.SetColor("blue")
err = mw.NewImage(200, 100, pw)
return nil
}
May please help? Newbie here. :)
Update:
The example that I gave is correct. Passing by reference is done correctly in the example above, I received the error because I overlook my code and debug the wrong lines. Sorry for confusion.
Thanks.

If mw has type *imagick.MagickWand then *mw has type imagick.MagickWand.
That is to say, mw is a pointer to that type and the * operator dereferences the pointer to the type itself.
mw := imagick.NewMagickWand() // Now mw is a *imagick.MagickWand
*mw // Is a imagick.MagickWand

Related

Always a new variable when multiple returns

I'm facing a wierd problem with Golang.
On init() function, i want to assign a value to my variable that was declared outside this function.
But to assign the value to this var, i need to get
error
to check if everything is ok.
Here is the code:
var retryValue time.Duration
func init() {
retryValue, err := time.ParseDuration(retries)
if err != nil {
log.Fatal("retries value is invalid", err)
}
}
func a(){
fmt.Println(retryValue)
}
And i get the compiling error:
retryValue declared and not used
I need to change init() to this:
func init() {
var err error
retryValue, err = time.ParseDuration(retries)
if err != nil {
log.Fatal("retries value is invalid", err)
}
}
There is another way to solve this problem?
:=
always create a new variable if one of the variables are already declared? It's about variable golang's sope?
Thanks!
There are two basic ways to do this. You found one:
var retryValue time.Duration
func init() {
var err error
retryValue, err = time.ParseDuration(retries)
if err != nil {
log.Fatal("retries value is invalid", err)
}
}
A slightly shorter method would be:
var retryValue = func() time.Duration {
rv, err := time.ParseDuration(retries)
if err != nil {
log.Fatal("retries value is invalid", err)
}
return rv
}()
But better than either of these, would be to just specify the value directly, rather than parsing it:
var retryValue = 15 * time.Minute // or whatever value you want
Your fix is indeed the shortest way to do this, and no there is not another way to solve the issue.
This problem is rare enough that it is not really an issue in practice, after all it is only one more (short) line.

How do I use olivere's elastic client.Update() service to change array fields, generic string fields and possibly nested struct fields?

https://github.com/olivere/elastic
Version 5.x
The wiki documentation isn't really clear on how client.Update() works. It's needed to completely change a field and to modify arrays. i.e. in the example in the wiki documentation, how would one go about appending and removing tags to a tweet or changing a tweet's content? Also if a tweet was represented in go as a struct and I added a nested struct called "echo" which contains a foo of type int, content of type string and another type string array, how would one go about changing any of these fields using client.Update() if it's even possible?
In my personal example I have this function:
func UpdateEntryContent(eclient *elastic.Client, entryID string, newContent []rune) error{
ctx:=context.Background()
exists, err := eclient.IndexExists(ENTRY_INDEX).Do(ctx)
if err != nil {return err}
if !exists {return errors.New("Index does not exist")}
_, err = eclient.Update().Index(ENTRY_INDEX).Type(ENTRY_TYPE).Id(entryID).
Script("ctx._source.Content = newCont").
ScriptParams(map[string]interface{}{"newCont": newContent}).
Do(ctx)
if err != nil {return err}
return nil
}
But I get this following error when I try to compile:
cannot use "ctx._source.Content = newCont" (type string) as type *elastic.Script in argument to eclient.Update().Index(ENTRY_INDEX).Type(ENTRY_TYPE).Id(entryID).Script
eclient.Update().Index(ENTRY_INDEX).Type(ENTRY_TYPE).Id(entryID).Script("ctx._source.Content = newCont").ScriptParams undefined (type *elastic.UpdateService has no field or method ScriptParams)
The Script method accepts a *elastic.Script, not a string. The ScriptParams method is also found on *elastic.Script as Params instead of being on *elastic.UpdateService.
func UpdateEntryContent(eclient *elastic.Client, entryID string, newContent []rune) error{
ctx:=context.Background()
exists, err := eclient.IndexExists(ENTRY_INDEX).Do(ctx)
if err != nil {return err}
if !exists {return errors.New("Index does not exist")}
script := elastic.NewScript("ctx._source.Content = newCont").Params(map[string]interface{}{"newCont": newContent})
_, err = eclient.Update().Index(ENTRY_INDEX).Type(ENTRY_TYPE).Id(entryID).
Script(script).
Do(ctx)
if err != nil {return err}
return nil
}
You can see more information about the package with GoDoc or by looking through the source code.
The following code should resolve the issue
_, err = eclient.Update().Index(INDEX).
Type(TYPE).
Id(ID).
Doc(map[string]interface{}{field: message}).
Do(ctx)
Credit where it's due Gavin's answer put me on the right track. This is for another .Index but the full function that acts as a generic single field update is as follows:
func UpdateUser(eclient *elastic.Client, userID string, field string, newContent interface{})error {
//CHANGES A SINGLE FIELD OF ES USER DOCUMENT(requires an elastic client pointer,
// the user DocID, the feild you wish to modify as a string,
// and what you want to change that field to as any type necessary)
//RETURN AN error IF SUCESSFUL error = nil
ctx := context.Background()
exists, err := eclient.IndexExists(USER_INDEX).Do(ctx)
if err != nil {return err}
if !exists {return errors.New("Index does not exist")}
_, err = eclient.Update().
Index(USER_INDEX).
Type(USER_TYPE).
Id(userID).
Doc(map[string]interface{}{field: newContent}).
Do(ctx)
return nil
}
You can change the .Index, .Type, and .Id and it works with all fields and types as far as I can tell

Error handling of executed slice of functions in Go

I must run an unknown number of functions in a for cycle and I want to create meaningful errors when something goes wrong (when error returns from one of them)
Here some code:
package storage
import (
"github.com/attilasatan/ankara/engine/indexer"
)
type NewHandler func(*indexer.Document) error
var NewHandlers []NewHandler
func AppendNewHandler(handler NewHandler) {
NewHandlers = append(NewHandlers, handler)
}
func New(document *indexer.Document) (err error) {
for i, handler := range NewHandlers {
err = handler(document)
if err != nil {
err = errors.New(`New Handler error at index ` + string(i) + `
original:
` + err.Error())
return
}
}
return
}
This is my solution for error handling but i don't feel comfortable with it because I only return the index of the function that I executed.
My question is. Can I collect more information about the function that returned not nil error.
Also any kind of advises would be appreciated.
Use a struct that contains the func and any metadata instead of just a func. Something like this.
type NewHandler struct {
Handler func(*indexer.Document) error
Data string // or whatever data
}
Also make sure your slice holds pointers because go is pass-by-value.
var NewHandlers []*NewHandler
Then when you for loop, it goes like this.
for i, handler := range NewHandlers {
err = handler.Handler(document)
....
And you can include your Data in the error.

How do I add data to an interface that is taken as an argument in Go?

I'm just picking up go, so apologies if my terminology isn't precise. My end goal is to add the name of a cachebusted CSS file to the layout template of my Go application. The CSS file built on the fly when the application starts up so can't be hardcoded. In my template file I have this:
//More html here
<link href="{{.CSSFile}}" rel="stylesheet">
//more html here
I have a Render method on a View type like shown below. It takes data interface{} as an argument and then runs ExecuteTemplate. It is called by every controller in one way or another that sends the data argument and exposes information. I know how to add it as data from the controller that then calls the Render method, but I obviously don't want to add the CSS file in every single controller action, so it makes the most sense to add it in the Render function one time and have it added to the data that gets passed to ExecuteTemplate. My issue is how do I add this information to the data already being passed to Render and then pass that whole of information to ExecuteTemplate. What I have below works for the CSS file, but it obviously doesn't send along the data that was passed to the original Render argument.
type View struct {
Template *template.Template
Layout string
}
func (v *View) Render(w http.ResponseWriter, data interface{}) error {
d := Data{}
d.AddCSSFile()
w.Header().Set("Content-Type", "text/html")
err := v.Template.ExecuteTemplate(w, v.Layout, d)
if err != nil {
log.Println(err)
fmt.Fprintln(w, "<h1>Something went wrong. Please contact us at support")
}
return nil
}
type Data struct {
Alerts []Alert
Yield interface{}
CSSFile interface{}
}
func (d *Data) AddCSSFile() {
ss, _ := filepath.Glob("./assets/site-*.css")
fp := strings.Join(ss, "")
_, d.CSSFile = filepath.Split(fp)
}
I've created a gist which, not entirely complete, is a little more fleshed out of what I'm trying to do:
https://gist.github.com/codelitt/549a68149add0482c6dc2514a46aa580
Not sure I understand exactly what you're asking but if what you want is to combine the data interface{} argument with the d := Data{} value inside Render, then you could do something like this...
// ...
func (v *View) Render(w http.ResponseWriter, data interface{}) error {
p := Page{Data:data}
p.AddCSSFile()
w.Header().Set("Content-Type", "text/html")
err := v.Template.ExecuteTemplate(w, v.Layout, p)
if err != nil {
log.Println(err)
fmt.Fprintln(w, "<h1>Something went wrong. Please contact us at support")
}
return nil
}
type Page struct {
Alerts []Alert
Yield interface{}
CSSFile interface{}
Data interface{}
}
func (p *Page) AddCSSFile() {
// ...
}
Edit: Or you could also just initialize an anonymous struct value and pass it to ExecuteTemplate without having to change you existing Data type.
// ...
err := v.Template.ExecuteTemplate(w, v.Layout, struct{
Data
Args interface{}
}{Data:d, Args:data})
// ...
Edit2: So if I understand your comment correctly the data interface{} argument passed to the Render method could in some or all instances be or contain a value of a type that matches one of the Data fields' types, in which case you would like to set that value to that field so as to pass it together to the ExecuteTemplate method. At least one solution to that, as you've already found out, is to use type assertion. Below is a slightly modified version of your example from the comment in the context of your original example from the question.
func (v *View) Render(w http.ResponseWriter, data interface{}) error {
d := Data{}
d.AddCSSFile()
if alerts, ok := data.([]Alert); ok {
d.Alerts = alerts
}
w.Header().Set("Content-Type", "text/html")
err := v.Template.ExecuteTemplate(w, v.Layout, d)
if err != nil {
log.Println(err)
fmt.Fprintln(w, "<h1>Something went wrong. Please contact us at support")
}
return nil
}

How to get the reflect.Type of an interface?

In order to determine whether a given type implements an interface using the reflect package, you need to pass a reflect.Type to reflect.Type.Implements(). How do you get one of those types?
As an example, trying to get the type of an uninitialized error (interface) type does not work (it panics when you to call Kind() on it)
var err error
fmt.Printf("%#v\n", reflect.TypeOf(err).Kind())
Do it like this:
var err error
t := reflect.TypeOf(&err).Elem()
Or in one line:
t := reflect.TypeOf((*error)(nil)).Elem()
Even Shaws response is correct, but brief. Some more details from the reflect.TypeOf method documentation:
// As interface types are only used for static typing, a common idiom to find
// the reflection Type for an interface type Foo is to use a *Foo value.
writerType := reflect.TypeOf((*io.Writer)(nil)).Elem()
fileType := reflect.TypeOf((*os.File)(nil)).Elem()
fmt.Println(fileType.Implements(writerType))
For googlers out there I just ran into the dreaded scannable dest type interface {} with >1 columns (XX) in result error.
Evan Shaw's answer did not work for me. Here is how I solved it. I am also using the lann/squirrel library, but you could easily take that out.
The solution really isn't that complicated, just knowing the magic combination of reflect calls to make.
The me.GetSqlx() function just returns an instance to *sqlx.DB
func (me *CommonRepo) Get(query sq.SelectBuilder, dest interface{}) error {
sqlst, args, err := query.ToSql()
if err != nil {
return err
}
// Do some reflection magic so that Sqlx doesn't hork on interface{}
v := reflect.ValueOf(dest)
return me.GetSqlx().Get(v.Interface(), sqlst, args...)
}
func (me *CommonRepo) Select(query sq.SelectBuilder, dest interface{}) error {
sqlst, args, err := query.ToSql()
if err != nil {
return err
}
// Do some reflection magic so that Sqlx doesn't hork on interface{}
v := reflect.ValueOf(dest)
return me.GetSqlx().Select(v.Interface(), sqlst, args...)
}
Then to invoke it you can do:
func (me *myCustomerRepo) Get(query sq.SelectBuilder) (rec Customer, err error) {
err = me.CommonRepo.Get(query, &rec)
return
}
func (me *myCustomerRepo) Select(query sq.SelectBuilder) (recs []Customer, err error) {
err = me.CommonRepo.Select(query, &recs)
return
}
This allows you to have strong types all over but have all the common logic in one place (CommonRepo in this example).

Resources