I am new to Go and don't understand one thing. Let's take one code which works:
package main
import "fmt"
type User struct {
Name string
Email string
}
type Admin struct {
User
Level string
}
type Notifier interface {
notify()
}
func (u *User) notify() {
fmt.Println("Notified", u.Name)
}
func SendNotification(notify Notifier) {
notify.notify()
}
func main() {
admin := Admin{
User: User{
Name: "john smith",
Email: "john#email.com",
},
Level: "super",
}
SendNotification(&admin)
admin.User.notify()
admin.notify()
}
Here function SendNotification recognises admin struct as Notifier, because admin struct has access to embedded user struct which implements the interface via pointer receiver. Ok.
Why then the code below doesn't work. Why norgateMathError needs to implement the interface and not use implementation from err error (for me it is the same situation):
package main
import (
"fmt"
"log"
)
type norgateMathError struct {
lat string
long string
err error
}
// func (n norgateMathError) Error() string {
// return fmt.Sprintf("a norgate math error occured: %v %v %v", n.lat, n.long, n.err)
// }
func main() {
_, err := sqrt(-10.23)
if err != nil {
log.Println(err)
}
}
func sqrt(f float64) (float64, error) {
if f < 0 {
nme := fmt.Errorf("norgate math redux: square root of negative number: %v", f)
return 0, &norgateMathError{"50.2289 N", "99.4656 W", nme}
}
return 42, nil
}
.\custom_error.go:28:13: cannot use &norgateMathError{...} (type *norgateMathError) as type error in return argument:
*norgateMathError does not implement error (missing Error method)
In the first case User is embedded inside Admin, thus Admin gain access to all methods defined on the User type.
In the second case, norgateMathError has a field err of type Error, thus do not automatically gain access to it's methods.
If you want norgateMathError to have an Error() method you have to define it manually
func (n norgateMathError) Error() string {
return n.err.Error()
}
There is a different between embedding a field and just having a field. More information can be found in the reference
Related
trying to create auth service that is abstract by using generic type. I wanted to create package Auth that will have New() method for initialization but currently facing the issue that I can resolve. Anyone have idea if this is even possible and if this is even go way of solving the issue?
type Register[T any] interface {
SignUp(T) (*AuthEntity, error)
}
type AuthEntity struct {
ID string
UpdatedAt time.Time
CreatedAt time.Time
}
type AuthService[T any] struct {
Register[T]
}
type PasswordStrategy struct{}
type SignUpViaPassword struct {
Email string
Password string
}
func (s PasswordStrategy) SignUp(signUpData SignUpViaPassword) (*AuthEntity, error) {
return &AuthEntity{ID: "email#gmail.com"}, nil
}
func New[T any](register Register[T]) AuthService[T] {
return AuthService[T]{
Register: register,
}
}
type GoogleStrategy struct{}
type SignUpViaGoogle struct {
Token string
}
func (s GoogleStrategy) SignUp(signUpData SignUpViaGoogle) (*AuthEntity, error) {
return &AuthEntity{}, nil
}
func main() {
// This works as expected
authService := AuthService[SignUpViaPassword]{
Register: PasswordStrategy{},
}
entity, err := authService.SignUp(SignUpViaPassword{Password: "Kako ide", Email: "email#gmail.com"})
if err != nil {
log.Println(err)
}
log.Println(entity)
// This also works as expected
googleAuthService := AuthService[SignUpViaGoogle]{
Register: GoogleStrategy{},
}
entity2, err := authService.SignUp(SignUpViaPassword{Password: "Kako ide", Email: "email#gmail.com"})
if err != nil {
log.Println(err)
}
log.Println(entity2)
// this doesn't work and returns error type PasswordStrategy of PasswordStrategy{} does not match Register[T] (cannot infer T)
New(PasswordStrategy{})
New(GoogleStrategy{})
}
It cannot infer what T should be, so you can explicitly specify it:
New[SignUpViaPassword](PasswordStrategy{})
Let's say we have a library provide a function Double to double the integer, we use pointer i to get the result value not by return:
package api
type Action interface {
Double(i *int) error
}
type NUM struct{}
func (n NUM) Double(i *int) error {
*i *= 2
return nil
}
in our main function we use this library to do our task. like this:
package app
import (
"fmt"
"github.com/hotsnow/api"
)
func main() {
j := job{a: &api.NUM{}}
d := j.task(3)
fmt.Println(3, d)
}
type job struct {
a api.Action
}
// double me
func (j job) task(i int) int {
j.a.Double(&i)
return i
}
Now we need to test the task() function, how can we get the pointer return bye mock the Double function?
Here is the test:
package app
import (
"github.com/golang/mock/gomock"
"github.com/hotsnow/mocks"
"testing"
)
func TestReq(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
m := mocks.NewMockAction(ctrl)
m.EXPECT().Double(gomock.Any()).Return(nil)
j := job{a: m}
got := j.task(3)
if got != 6 {
t.Errorf("got = %#v; want 6", got)
}
}
The code here: https://github.com/hotsnow/mock.git (stackoverflow branch)
you can use gomock setarg function for this
yourPackage.EXPECT().insert(&pointer).SetArg(0, newPointer)
You can achieve this with the provided Eq() matcher, which internally calls reflect.DeepEqual() on the expected and actual values; as per the documentation for this method:
Pointer values are deeply equal if they are equal using Go's == operator or if they point to deeply equal values.
Say we have a function that depends upon an interface method that takes a pointer parameter:
package resource
type ServiceRequest struct {
Name string
Owner *string // this is a pointer so it can be omitted with `nil`
}
type Model struct {
// resource model...
}
type ResourceService interface {
Fetch(req *ServiceRequest) (Model, error)
}
type getResourceHandler struct {
resourceService ResourceService
}
type GetResourceEvent struct {
Resource string
Owner *string
}
func NewResourceHandler(resourceService ResourceService) *getResourceHandler {
return &getResourceHandler{resourceService}
}
func (h *getResourceHandler) Handle(event GetResourceEvent) (Model, error) {
return h.resourceService.Fetch(&ServiceRequest{event.Resource, event.Owner})
}
We can use the Eq() matcher when setting up the expectation against our generated mock of the ResourceService interface:
package test
import (
"testing"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert"
"github.com/org/repo/internal/mock"
"github.com/org/repo/internal/resource"
)
func optionalString(str string) *string {
return &str
}
func Test_GetResourceHandler_ReturnsResultFromService(t *testing.T) {
resourceName := "my-resource"
owner := optionalString("Joe Bloggs")
resourceReq := &resource.ServiceRequest{resourceName, owner}
event := resource.GetResourceEvent{resourceName, owner}
model := resource.Model{ /* fields here... */ }
ctrl := gomock.NewController(t)
mockResourceService := mock.NewMockResourceService(ctrl)
handler := resource.NewResourceHandler(mockResourceService)
mockResourceService.EXPECT().Fetch(gomock.Eq(resourceReq)).Return(model, nil)
res, err := handler.Handle(event)
assert.Nil(t, err)
assert.Equal(t, model, res)
}
If you change the contents of the service request in either the test or the unit under test, you'll see that the test no longer passes. Otherwise, it will pass in spite of the test and the unit under test having their own respective pointers to separate ServiceRequest{} values.
It seems you don't have to use gomock to test the task method.
Since you have an interface, why not just create a mock implementation of the interface, for example:
type dummy struct{
callCount int
}
func (d *dummy) Double(i *int) error {
d.callCount++
return nil
}
d := dummy{}
j := job{a: &d}
got := j.task(3)
if d.callCount != 1 {
// XXX
}
I have a dilemma to store some data in an unstructured field.
Let's say I have these to store:
package main
type FoodType string
const (
BeefFood FoodType = "beef"
ChickenFood FoodType = "chicken"
)
type FoodI interface {
Prepare()
GetType() FoodType
}
type FoodContainer struct {
FoodI
}
type Chicken struct {
Way string
}
func (c *Chicken) Prepare() {}
func (c *Chicken) GetType() FoodType {
return ChickenFood
}
type Beef struct {
Way string
}
func (b *Beef) Prepare() {}
func (b *Beef) GetType() FoodType {
return BeefFood
}
Let's say I choose json as a serializer (I will probably go for msgpack, but that's another story).
Which way do you think is the most appropriate:
Solution 1
one container that contains all the message types, and simply use the default marshal/unmarshal:
package main
type FoodContainer struct {
Chicken *Chicken `json:"chicken"`
Beef *Beef `json:"beef"`
}
And then, I just need to check which of the field is nil to know what to do with it.
Solution 2
A more "typed" solution, with interfaces and FoodType
package main
import (
"encoding/json"
"fmt"
)
type FoodContainer struct {
FoodType
FoodI
}
func (fc *FoodContainer) MarshalJSON() ([]byte, error) {
return json.Marshal(&FoodContainerT{
FoodType: fc.FoodI.GetType(),
FoodI: fc.FoodI,
})
}
func (fc *FoodContainer) UnmarshalJSON(b []byte) error {
var fct struct {
FoodType
}
if err := json.Unmarshal(b, &fct); err != nil {
return fmt.Errorf("fc UnmarshalJSON: %v", err)
}
switch fct.FoodType {
case ChickenFood:
fmt.Printf("Yeah, Chickenfood: %v\n", fct.FoodType)
var fi struct {
FoodI Chicken
}
if err := json.Unmarshal(b, &fi); err != nil {
return fmt.Errorf("chicken UnmarshalJSON: %v", err)
}
fmt.Printf("%#v", fi.FoodI)
default:
return fmt.Errorf("no known FoodType found")
}
return nil
}
I benchmarked both, they have around the same processing time and same mem usage, but I'm wondering which of them could be more idiomatic?
I have the following code in a golang plugin module:
plug.go
package main
import "fmt"
var (
Thing = New("first thing")
ThingFactory = thingFactory{}
)
type thing struct {
i int
s string
}
func New(s string) thing {
return thing{s: s}
}
func (t *thing) Say() string {
t.i++
return fmt.Sprintf("%s - %d", t.s, t.i)
}
type thingFactory struct{}
func (t thingFactory) Make(s string) thing {
return New(s)
}
it is compiled as a .so object and used in another program:
main.go
package main
import (
"fmt"
"plugin"
)
func main() {
p, err := plugin.Open("../plug/plug.so")
if err != nil {
panic(err)
}
symbol, err := p.Lookup("Thing")
if err != nil {
panic(err)
}
thing := symbol.(Sayer)
fmt.Println(thing.Say())
symbol, err = p.Lookup("ThingFactory") // <-problems start here
if err != nil {
panic(err)
}
factory := symbol.(GetSayer)
madeThing := factory.Make("how about me?")
fmt.Println(madeThing.Say())
fmt.Println(madeThing.Say())
}
type Sayer interface {
Say() string
}
type GetSayer interface {
Make(string) Sayer
}
I'm able to lookup the Thing, and call Say() on it, but the second interface conversion panics:
first thing - 1
panic: interface conversion: *main.thingFactory is not main.GetSayer: missing method Make
even though the runtime recognizes the first symbol as a Sayer it doesn't recognize that thingFactory obviously has a Make() method, which should return something that is also a Sayer.
Am I missing something obvious here?
The first problem is that in your plugin thingFactory (more precicely *thingfactory) does not have a method described in your main app's GetSayer interface:
Make(string) Sayer
You have:
Make(string) thing
So (first) you have to change thingFactory.Make() to this:
type Sayer interface {
Say() string
}
func (t thingFactory) Make(s string) Sayer {
th := New(s)
return &th
}
After this it still won't work. And the reason for this is because the plugin's Sayer type is not identical to your main app's Sayer type. But they must be the same in order to implement your main app's GetSayer interface.
One solution is to "outsource" the Sayer interface to its own package, and use this common, shared package both in the plugin and in the main app.
Let's create a new package, call it subplay:
package subplay
type Sayer interface {
Say() string
}
Import this package and use it in the plugin:
package main
import (
"fmt"
"path/to/subplay"
)
var (
Thing = New("first thing")
ThingFactory = thingFactory{}
)
type thing struct {
i int
s string
}
func New(s string) thing {
return thing{s: s}
}
func (t *thing) Say() string {
t.i++
return fmt.Sprintf("%s - %d", t.s, t.i)
}
type thingFactory struct{}
func (t thingFactory) Make(s string) subplay.Sayer {
th := New(s)
return &th
}
And also import and use it in the main app:
package main
import (
"fmt"
"path/to/subplay"
"plugin"
)
func main() {
p, err := plugin.Open("../plug/plug.so")
if err != nil {
panic(err)
}
symbol, err := p.Lookup("Thing")
if err != nil {
panic(err)
}
thing := symbol.(subplay.Sayer)
fmt.Println(thing.Say())
symbol, err = p.Lookup("ThingFactory")
if err != nil {
panic(err)
}
factory := symbol.(GetSayer)
madeThing := factory.Make("how about me?")
fmt.Println(madeThing.Say())
fmt.Println(madeThing.Say())
}
type GetSayer interface {
Make(string) subplay.Sayer
}
Now it will work, and output will be:
first thing - 1
how about me? - 1
how about me? - 2
See related questions:
go 1.8 plugin use custom interface
How do Go plugin dependencies work?
Your plugin Make method should return a Sayer object not thing
type Sayer interface {
Say() string
}
func (t *thingFactory) Make(s string) Sayer {
return New(s)
}
I'm porting an app from Play (Scala) to Go and wondering how to implement dependency injection. In Scala I used the cake pattern, while in Go I implemented a DAO interface along with an implementation for Mongo.
Here below is how I tried to implement a pattern that let me change the DAO implementation as needed (e.g. test, different DB, etc.):
1. entity.go
package models
import (
"time"
"gopkg.in/mgo.v2/bson"
)
type (
Entity struct {
Id bson.ObjectId `json:"id,omitempty" bson:"_id,omitempty"`
CreatedAt time.Time `json:"createdAt,omitempty" bson:"createdAt,omitempty"`
LastUpdate time.Time `json:"lastUpdate,omitempty" bson:"lastUpdate,omitempty"`
}
)
2. user.go
package models
import (
"time"
)
type (
User struct {
Entity `bson:",inline"`
Name string `json:"name,omitempty" bson:"name,omitempty"`
BirthDate time.Time `json:"birthDate,omitempty" bson:"birthDate,omitempty"`
}
)
3. dao.go
package persistence
type (
DAO interface {
Insert(entity interface{}) error
List(result interface{}, sort string) error
Find(id string, result interface{}) error
Update(id string, update interface{}) error
Remove(id string) error
Close()
}
daoFactory func() DAO
)
var (
New daoFactory
)
4. mongoDao.go (DB info and collection name are hard-coded since it's just an example)
package persistence
import (
"fmt"
"time"
"errors"
"gopkg.in/mgo.v2"
"gopkg.in/mgo.v2/bson"
"github.com/fatih/structs"
"cmd/server/models"
)
type (
mongoDAO struct{
session *mgo.Session
}
)
func NewMongoDAO() DAO {
dialInfo := &mgo.DialInfo{
Addrs: []string{"localhost:27017"},
Timeout: 60 * time.Second,
Database: "test",
}
session, err := mgo.DialWithInfo(dialInfo)
if err != nil {
panic(err)
}
session.SetMode(mgo.Monotonic, true)
return &mongoDAO{session}
}
func (dao *mongoDAO) Insert(entity interface{}) error {
doc := entity.(*models.User)
doc.Id = bson.NewObjectId()
doc.CreatedAt = time.Now().UTC()
doc.LastUpdate = time.Now().UTC()
return dao.session.DB("test").C("users").Insert(doc)
}
func (dao *mongoDAO) List(result interface{}, sort string) error {
return dao.session.DB("test").C("users").Find(nil).Sort(sort).All(result)
}
func (dao *mongoDAO) Find(id string, result interface{}) error {
if !bson.IsObjectIdHex(id) {
return errors.New(fmt.Sprintf("%s is not a valid hex id", id))
}
oid := bson.ObjectIdHex(id)
return dao.session.DB("test").C("users").FindId(oid).One(result)
}
func (dao *mongoDAO) Update(id string, update interface{}) error {
if !bson.IsObjectIdHex(id) {
return errors.New(fmt.Sprintf("%s is not a valid hex id", id))
}
oid := bson.ObjectIdHex(id)
doc := update.(*models.User)
doc.LastUpdate = time.Now().UTC()
return dao.session.DB("test").C("users").Update(oid, bson.M{"$set": structs.Map(update)})
}
func (dao *mongoDAO) Remove(id string) error {
if !bson.IsObjectIdHex(id) {
return errors.New(fmt.Sprintf("%s is not a valid hex id", id))
}
oid := bson.ObjectIdHex(id)
return dao.session.DB("test").C("users").RemoveId(oid)
}
func (dao *mongoDAO) Close() {
dao.session.Close()
}
func init() {
New = NewMongoDAO
}
Finally, here is how I use the types above:
5. userController.go
package controllers
import (
"net/http"
"github.com/labstack/echo"
"cmd/server/models"
"cmd/server/persistence"
)
type (
UserController struct {
dao persistence.DAO
}
)
func NewUserController(dao persistence.DAO) *UserController {
return &UserController{dao}
}
func (userController *UserController) CreateUser() echo.HandlerFunc {
return func(context echo.Context) error {
user := &models.User{}
if err := context.Bind(user); err != nil {
return err
}
if err := userController.dao.Insert(user); err != nil {
return err
}
return context.JSON(http.StatusCreated, user)
}
}
func (userController *UserController) UpdateUser() echo.HandlerFunc {
return func(context echo.Context) error {
user := &models.User{}
if err := context.Bind(user); err != nil {
return err
}
id := context.Param("id")
if err := userController.dao.Update(id, user); err != nil {
return err
}
return context.JSON(http.StatusOK, user)
}
}
....
The code above is 90% fine... I've just a problem in mongoDao.go with methods Insert and Update where the compiler forces me to cast input entity to a specific type (*models.User), but this prevents me from having a generic DAO component that works for all types. How do I fix this issue?
How about creating an interface that you implement for the Entity struct?
type Entitier interface {
GetEntity() *Entity
}
The implementation would simply return a pointer to itself that you can now use in the Insert and Update methods of your DAO. This would also have the added benefit of letting you be more specific in the declarations of your DAO methods. Instead of simply stating that they take an arbitrary interface{} as argument you could now say that they take an Entitier.
Like so:
func (dao *mongoDAO) Update(id string, update Entitier) error
Here's a minimal complete example of what I mean:
http://play.golang.org/p/lpVs_61mfM
Hope this gives you some ideas! You might want to adjust naming of Entity/Entitier/GetEntity for style and clarity once you've settled on the pattern to use.
This generalization
DAO interface {
Insert(entity interface{}) error
looks over-helming
You both assert to *models.User for mongo
doc := entity.(*models.User)
and do
user := &models.User{}
userController.dao.Insert(user)
when use your generic DAO interface.
Why don't you just define interface more precisely?
DAO interface {
Insert(entity *models.User) error