Skip to content

Commit

Permalink
add a demo
Browse files Browse the repository at this point in the history
  • Loading branch information
mylxsw committed May 25, 2020
1 parent 1cb2ed5 commit 6bb5873
Show file tree
Hide file tree
Showing 9 changed files with 321 additions and 95 deletions.
189 changes: 96 additions & 93 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,13 @@

> 并不是说对性能要求苛刻的环境中就不能使用了,你可以把 **Container** 作为一个对象依赖管理工具,在你的业务初始化时获取依赖的对象。
使用方式

go get github.com/mylxsw/container

要创建一个 **Container** 实例,使用 `containier.New` 方法

cc := container.New()
cc := container.New()

此时就创建了一个空的容器。

Expand Down Expand Up @@ -56,38 +60,38 @@

- 对象创建函数 `func(deps...) 对象返回值`

比如
比如

cc.Singleton(func() UserRepo { return &userRepoImpl{} })
cc.Singleton(func() (*sql.DB, error) {
return sql.Open("mysql", "user:pwd@tcp(ip:3306)/dbname")
})
cc.Singleton(func(db *sql.DB) UserRepo {
// 这里我们创建的 userRepoImpl 对象,依赖 sql.DB 对象,只需要在函数
// 参数中,将依赖列举出来,容器会自动完成这些对象的创建
return &userRepoImpl{db: db}
})
cc.Singleton(func() UserRepo { return &userRepoImpl{} })
cc.Singleton(func() (*sql.DB, error) {
return sql.Open("mysql", "user:pwd@tcp(ip:3306)/dbname")
})
cc.Singleton(func(db *sql.DB) UserRepo {
// 这里我们创建的 userRepoImpl 对象,依赖 sql.DB 对象,只需要在函数
// 参数中,将依赖列举出来,容器会自动完成这些对象的创建
return &userRepoImpl{db: db}
})

- 带错误返回值的对象创建函数 `func(deps...) (对象返回值, error)`

对象创建函数最多支持两个返回值,且要求第一个返回值为期望创建的对象,第二个返回值为 error 对象。
对象创建函数最多支持两个返回值,且要求第一个返回值为期望创建的对象,第二个返回值为 error 对象。

cc.Singleton(func() (Config, error) {
// 假设我们要创建配置对象,该对象的初始化时从文件读取配置
content, err := ioutil.ReadFile("test.conf")
if err != nil {
return nil, err
}
cc.Singleton(func() (Config, error) {
// 假设我们要创建配置对象,该对象的初始化时从文件读取配置
content, err := ioutil.ReadFile("test.conf")
if err != nil {
return nil, err
}

return config.Load(content), nil
})
return config.Load(content), nil
})

- 直接绑定对象

如果对象已经创建好了,想要让 **Container** 来管理,可以直接将对象传递 `Singleton` 方法
如果对象已经创建好了,想要让 **Container** 来管理,可以直接将对象传递 `Singleton` 方法

userRepo := repo.NewUserRepo()
cc.Singleton(userRepo)
userRepo := repo.NewUserRepo()
cc.Singleton(userRepo)


> 当对象第一次被使用时,**Container** 会将对象创建函数的执行结果缓存起来,从而实现任何时候后访问都是获取到的同一个对象。
Expand All @@ -106,10 +110,9 @@

常用的绑定方法为 `BindValue(key string, value interface{})`

cc.BindValue("version", "1.0.1")
cc.MustBindValue("startTs", time.Now())
cc.BindValue("int_val", 123)

cc.BindValue("version", "1.0.1")
cc.MustBindValue("startTs", time.Now())
cc.BindValue("int_val", 123)

## 依赖注入

Expand All @@ -121,96 +124,94 @@

比如,我们需要获取某个用户的信息和其角色信息,使用 Resolve 方法

cc.MustResolve(func(userRepo repo.UserRepo, roleRepo repo.RoleRepo) {
// 查询 id=123 的用户,查询失败直接panic
user, err := userRepo.GetUser(123)
if err != nil {
panic(err)
}
// 查询用户角色,查询失败时,我们忽略了返回的错误
role, _ := roleRepo.GetRole(user.RoleID)
cc.MustResolve(func(userRepo repo.UserRepo, roleRepo repo.RoleRepo) {
// 查询 id=123 的用户,查询失败直接panic
user, err := userRepo.GetUser(123)
if err != nil {
panic(err)
}
// 查询用户角色,查询失败时,我们忽略了返回的错误
role, _ := roleRepo.GetRole(user.RoleID)

// do something you want with user/role
})
// do something you want with user/role
})

直接使用 `Resolve` 方法可能并不太满足我们的日常业务需求,因为在执行查询的时候,总是会遇到各种 `error`,直接丢弃会产生很多隐藏的 Bug,但是我们也不倾向于使用 `Panic` 这种暴力的方式来解决。

**Container** 提供了 `ResolveWithError(callback interface{}) error` 方法,使用该方法时,我们的 callback 可以接受一个 `error` 返回值,来告诉调用者这里出现问题了。

err := cc.ResolveWithError(func(userRepo repo.UserRepo, roleRepo repo.RoleRepoo) error {
user, err := userRepo.GetUser(123)
if err != nil {
return err
}
err := cc.ResolveWithError(func(userRepo repo.UserRepo, roleRepo repo.RoleRepoo) error {
user, err := userRepo.GetUser(123)
if err != nil {
return err
}

role, err := roleRepo.GetRole(user.RoleID)
if err != nil {
return err
}
role, err := roleRepo.GetRole(user.RoleID)
if err != nil {
return err
}

// do something you want with user/role

return nil
})
if err != nil {
// 自定义错误处理
}
// do something you want with user/role

return nil
})
if err != nil {
// 自定义错误处理
}

### Call


`Call(callback interface{}) ([]interface{}, error)` 方法不仅完成对象的依赖注入,还会返回 `callback` 的返回值,返回值为数组结构。

比如

results, err := cc.Call(func(userRepo repo.UserRepo) ([]repo.User, error) {
users, err := userRepo.AllUsers()
return users, err
})
if err != nil {
// 这里的 err 是依赖注入过程中的错误,比如依赖对象创建失败
}
results, err := cc.Call(func(userRepo repo.UserRepo) ([]repo.User, error) {
users, err := userRepo.AllUsers()
return users, err
})
if err != nil {
// 这里的 err 是依赖注入过程中的错误,比如依赖对象创建失败
}

// results 是一个类型为 []interface{} 的数组,数组中按次序包含了 callback 函数的返回值
// results[0] - []repo.User
// results[1] - error
// 由于每个返回值都是 interface{} 类型,因此在使用时需要执行类型断言,将其转换为具体的类型再使用
users := results[0].([]repo.User)
err := results[0].(error)
// results 是一个类型为 []interface{} 的数组,数组中按次序包含了 callback 函数的返回值
// results[0] - []repo.User
// results[1] - error
// 由于每个返回值都是 interface{} 类型,因此在使用时需要执行类型断言,将其转换为具体的类型再使用
users := results[0].([]repo.User)
err := results[0].(error)


### Provider

有时我们希望为不同的功能模块绑定不同的对象实现,比如在 Web 服务器中,每个请求的 handler 函数需要访问与本次请求有关的 request/response 对象,请求结束之后,**Container** 中的 request/response 对象也就没有用了,不同的请求获取到的也不是同一个对象。我们可以使用 `CallWithProvider(callback interface{}, provider func() []*Entity) ([]interface{}, error)` 配合 `Provider(initializes ...interface{}) (func() []*Entity, error)` 方法实现该功能。

ctxFunc := func() Context { return ctx }
requestFunc := func() Request { return ctx.request }
provider, _ := cc.Provider(ctxFunc, requestFunc)
results, err := cc.CallWithProvider(func(userRepo repo.UserRepo, req Request) ([]repo.User, error) {
// 这里我们注入的 Request 对象,只对当前 callback 有效
userId := req.Input("user_id")
users, err := userRepo.GetUser(userId)
return users, err
}, provider)
ctxFunc := func() Context { return ctx }
requestFunc := func() Request { return ctx.request }
provider, _ := cc.Provider(ctxFunc, requestFunc)
results, err := cc.CallWithProvider(func(userRepo repo.UserRepo, req Request) ([]repo.User, error) {
// 这里我们注入的 Request 对象,只对当前 callback 有效
userId := req.Input("user_id")
users, err := userRepo.GetUser(userId)
return users, err
}, provider)

### AutoWire 结构体属性注入

使用 `AutoWire` 方法可以为结构体的属性注入其绑定的对象,要使用该特性,我们需要在需要依赖注入的结构体对象上添加 `autowire` 标签。

type UserManager struct {
UserRepo *UserRepo `autowire:"@" json:"-"`
field1 string `autowire:"version"`
Field2 string `json:"field2"`
}
type UserManager struct {
UserRepo *UserRepo `autowire:"@" json:"-"`
field1 string `autowire:"version"`
Field2 string `json:"field2"`
}

manager := UserManager{}
// 对 manager 执行 AutoWire 之后,会自动注入 UserRepo 和 field1 的值
if err := c.AutoWire(&manager); err != nil {
t.Error("test failed")
}
manager := UserManager{}
// 对 manager 执行 AutoWire 之后,会自动注入 UserRepo 和 field1 的值
if err := c.AutoWire(&manager); err != nil {
t.Error("test failed")
}

结构体属性注入支持公开和私有字段的注入。如果对象是通过类型来注入的,使用 `autowire:"@"` 来标记属性;如果使用的是 `BindValue` 绑定的字符串为key的对象,则使用 `autowire:"Key名称"` 来标记属性。

Expand All @@ -222,39 +223,41 @@

方法签名

HasBound(key interface{}) bool
HasBoundValue(key string) bool
HasBound(key interface{}) bool
HasBoundValue(key string) bool

用于判断指定的 Key 是否已经绑定过了。

### Keys

方法签名

Keys() []interface{}
Keys() []interface{}

获取所有绑定到 **Container** 中的对象信息。

### CanOverride

方法签名

CanOverride(key interface{}) (bool, error)
CanOverride(key interface{}) (bool, error)

判断指定的 Key 是否可以覆盖,重新绑定创建函数。

### Extend

`Extend` 并不是 **Container** 实例上的一个方法,而是一个独立的函数,用于从已有的 Container 生成一个新的 Container,新的 Container 继承已有 Container 所有的对象绑定。

Extend(c Container) Container
Extend(c Container) Container

容器继承之后,在依赖注入对象查找时,会优先从当前 Container 中查找,当找不到对象时,再从父对象查找。

> 在 Container 实例上个,有一个名为 `ExtendFrom(parent Container)` 的方法,该方法用于指定当前 Container 从 parent 继承。
## 示例项目

简单的示例可以参考项目的 [example](https://github.com/mylxsw/container/example) 目录。

以下项目中使用了 `Container` 作为依赖注入管理库,感兴趣的可以参考一下。

- [Glacier](https://github.com/mylxsw/glacier) 一个应用管理框架,目前还没有写使用文档,该框架集成了 **Container**,用来管理框架的对象实例化。
Expand Down
2 changes: 1 addition & 1 deletion container.go
Original file line number Diff line number Diff line change
Expand Up @@ -389,7 +389,7 @@ func (c *containerImpl) get(key interface{}, provider func() []*Entity) (interfa
return c.parent.Get(key)
}

return nil, buildObjectNotFoundError(fmt.Sprintf("key=%#v not found", key))
return nil, buildObjectNotFoundError(fmt.Sprintf("key=%v not found", key))
}

// buildKeyLookupFunc 构建用于查询 key 是否存在的函数
Expand Down
3 changes: 3 additions & 0 deletions example/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Example

这是一个使用 **Container** 的示例程序。
12 changes: 12 additions & 0 deletions example/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
module github.com/mylxsw/container/example

go 1.14

require (
github.com/go-sql-driver/mysql v1.5.0 // indirect
github.com/mattn/go-sqlite3 v1.13.0 // indirect
github.com/mylxsw/container v0.0.0-20200524155918-1cb2ed5ec854
github.com/onsi/ginkgo v1.12.2 // indirect
github.com/proullon/ramsql v0.0.0-20181213202341-817cee58a244
github.com/ziutek/mymysql v1.5.4 // indirect
)
73 changes: 73 additions & 0 deletions example/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/go-gorp/gorp v2.0.0+incompatible h1:dIQPsBtl6/H1MjVseWuWPXa7ET4p6Dve4j3Hg+UjqYw=
github.com/go-gorp/gorp v2.0.0+incompatible/go.mod h1:7IfkAQnO7jfT/9IQ3R9wL1dFhukN6aQxzKTHnkxzA/E=
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/mattn/go-sqlite3 v1.13.0 h1:LnJI81JidiW9r7pS/hXe6cFeO5EXNq7KbfvoJLRI69c=
github.com/mattn/go-sqlite3 v1.13.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mylxsw/container v0.0.0-20200524155918-1cb2ed5ec854 h1:IXKmp5hpj5yYZ7Va0m2D9fjYBxlyyx2ZUBigu8ZL3GM=
github.com/mylxsw/container v0.0.0-20200524155918-1cb2ed5ec854/go.mod h1:N7QSVqaHggsEM4ckqO35RqEb1/wGXQfYgWF3ho9vRTY=
github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.12.2 h1:Ke9m3h2Hu0wsZ45yewCqhYr3Z+emcNTuLY2nMWCkrSI=
github.com/onsi/ginkgo v1.12.2/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/proullon/ramsql v0.0.0-20181213202341-817cee58a244 h1:fdX2U+a2Rmc4BjRYcOKzjYXtYTE4ga1B2lb8i7BlefU=
github.com/proullon/ramsql v0.0.0-20181213202341-817cee58a244/go.mod h1:jG8oAQG0ZPHPyxg5QlMERS31airDC+ZuqiAe8DUvFVo=
github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs=
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7 h1:AeiKBIuRw3UomYXSbLy0Mc2dDLfdtbT/IVn4keq83P0=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299 h1:DYfZAGf2WMFjMxbgTjaC+2HC7NkNAQs+6Q8b9WEB/F4=
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
Loading

0 comments on commit 6bb5873

Please sign in to comment.