名库讲解
goconfig
go 语言针对 windows 下常见的 ini 格式的配置文件解析器,该解析器在涵盖了所有 ini 文件操作的基础上,又针对 go 语言实际开发过程中遇到的一些需求进行了扩展。该解析器最大的优势在于对注释的极佳支持,除此之外,支持多个配置文件覆盖加载也是非常特别但好用的功能。
- 提供与 windows api 一模一样的操作
- 支持递归读取分区
- 支持自增键名
- 支持对注释的读与写操作
- 支持直接返回指定类型的键值
- 支持多个文件覆盖加载
安装
- gopm
gopm get github.com/Unknwon/goconfig- go get
go get github.com/Unknwon/goconfig- doc
基本使用方法
; conf.ini
; Google
google = www.google.com
search = http://%(google)s
; Here are Comments
; Second line
[Demo]
# This symbol can also make this line to be comments
key1 = Let's us goconfig!!!
key2 = test data
key3 = this is based on key2:%(key2)s
quote = "special case for quote
"key:1" = This is the value of "key:1"
"key:2=key:1" = this is based on "key:2=key:1" => %(key:1)s
中国 = China
chinese-var = hello %(中国)s!
array_key = 1,2,3,4,5
[What's this?]
; Not Enough Comments!!
name = try one more value ^-^
empty_value =
[url]
google_fake = www.google.fake
google_url = http://%(google_fake)s
[parent]
name = john
relation = father
sex = male
age = 32
money = 1.25
[parent.child]
age = 3
married = true
[parent.child.child]
; Auto increment by setting key to "-"
[auto increment]
- = hello
- = go
- = config
package main
import (
"github.com/Unknwon/goconfig"
)
// 加载配置文件
cfg,err:=goconfig.LoadConfigFile("conf.ini")
if err!=nil {
log.Fatalf("无法加载配置文件:%s",err)
}
// 基本读写操作
value,err:=cfg.GetValue(goconfig.DEFAULT_SECTION,"key_default")
isInsert:=cfg.SetValue(goconfig.DEFAULT_SECTION,"key_devault","这是新的值")
package main
import (
"fmt"
"github.com/Unknwon/goconfig"
"log"
)
func main() {
cfg,err := goconfig.LoadConfigFile("conf.ini")
if err!=nil {
log.Fatalf("无法加载配置文件:%s",err)
}
value,err:=cfg.GetValue("Demo","key1")
if err!=nil {
log.Fatalf("无法获取值{%s}:%s","Demo",err)
}
fmt.Printf("%s -> %s : %s","Demo","key1",value)
}
// 注释 分区注释 键注释
comment:=cfg.GetSectionComments("super") //获取分区注释
log.Printf("分区 %s 的注释:%s","super",comment)
v:=cfg.SetSectionComments("super","# 这是新的分区注释") // 井号保留
- 类型转换读取
vInt,err:=cfg.Int("must","int")//分区 键值- Must 系列方法
vBool:=cfg.MustBool("must","bool")- 删除指定键值
ok:=cfg.DeleteKey("must","string")- 保存配置文件
err=goconfig.SaveConfigFile(cfg,"conf_save.ini")
高级使用
- 多文件覆盖加载
cfg,err:=goconfig.LoadConfigFile("conf.ini","conf2.ini")err = cfg.AppendFiles("conf3.ini")- 配置文件重载
err = cfg.Reload()- 它会按之前的多个顺序来加载
- 为 must 系统方法设置缺省值
vBool:=cfg.MustBool("must","bool404",true)- 递归读取键值
- 子分区只设置值不同的相同的不用设置(子分区用
.来分隔) - 自动键名获取,获取整个分区 (自增键名用
-来设置 sec,err:=cfg.GetSection("auto increment")
; 键值必须是先声明过的,使用%(键名)s
google=www.google.com
search=http://%(goole)s
xorm 基本用法
- 结构与数据表的双向映射与增量同步
- 方法调用链式操作
- session 事务与回滚
- ORM 方法与纯 SQL 混合事务
- 基于 LRU 规则的缓存器
- 数据表乐观锁
gopm get github.com/go-xorm/xorm
# 或使用go get安装
go get github.com/go-xorm/xorm
go 语言中的 ORM 习惯直接在定义结构的基础上配合 tag 来定义模型
type Account struct {
Id int64
Name string `xorm:"unique"` // tag
Balance float64
Version int `xorm:"version"` //乐观锁
}
新建两个文件main.go和models.go文件
// main.go
/* 1. 导入数据库驱动包 这里以sqlite为例
a. go get github.com/go-xorm/xorm
b. go get github.com/mattn/go-sqlite3
c. 导入驱动包 import(
_ "github.com/mattn/go-sqlite3" // 因为导入的包必须使用所以要用个占位,只执行这个包的初始化函数
)
*/
package main
import (
"fmt"
"xorm"
)
const prompt = `Please enter number of operation:
1. Create new account
2. Show detail of account
3. Deposit
4. Withdraw
5. Make transfer
6. List account by Id
7. List account by balance
8. Delete account
9. Exit`
func main() {
fmt.Println("Welcome bank of xorm!")
Exit:
for {
fmt.Println(prompt)
var num int
fmt.Scanf("%dn", &num) //回车
switch num {
case 1:
fmt.Println("Place enter <name> <balance>:")
var name string
var balance float64
fmt.Scanf("%s %fn", &name, &balance)
if err := xorm.NewAccount(name, balance); err != nil {
fmt.Println(err)
}
case 2:
fmt.Println("Place enter <id>:")
var id int64
fmt.Scanf("%dn", &id)
a, err := xorm.GetAccount(id)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("%#v", a)
}
case 3:
fmt.Printf("Please enter <id> <deposit>: ")
var id int64
var deposit float64
fmt.Scanf("%d %fn", &id, &deposit)
a, err := xorm.MakeDeposit(id, deposit)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("%#v", a)
}
case 4:
fmt.Printf("Please enter <id> <withdraw>: ")
var id int64
var withdraw float64
fmt.Scanf("%d %fn", &id, &withdraw)
a, err := xorm.MakeWithdraw(id, withdraw)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("%#v", a)
}
case 5:
fmt.Printf("Please enter <id> <balance> <id>")
var id1, id2 int64
var balance float64
fmt.Scanf("%d %f %d", &id1, &balance, &id2)
if err := xorm.MackTransfer(id1, id2, balance); err != nil {
fmt.Println(err)
}
case 6:
as, err := xorm.GetAccountAscId("id")
if err != nil {
fmt.Println(err)
} else {
for i, a := range as {
fmt.Printf("%d %#vn", i, a)
}
}
case 7:
as, err := xorm.GetAccountAscId("balance")
if err != nil {
fmt.Println(err)
} else {
for i, a := range as {
fmt.Printf("%d %#vn", i, a)
}
}
case 8:
fmt.Println("Place enter <id>:")
var id int64
fmt.Scanf("%dn", &id)
if err := xorm.DeleteAccount(id);err!=nil {
fmt.Println(err)
}
case 9:
break Exit
}
}
}
// models.go
/*
注意:每个golang源文件中都可以定义一个init函数。golang系统中,所有的源文件都有自己所属的目录,每一个目录都有对应的包名。在包的引用中,一旦某一个包被使用,则这个包下边的init函数将会被执行,且只执行一次。
1. 新建
_,err:=x.Insert(&Account{Name:name,Balance:balance})
2. 删除
_,err:=
3. 获取并修改
a:=&Account{}
has,err:=x.Id(id).Get(a)
a.Balance+=deposit
_,err = x.Update(a)
4. 原始的事务操作 通过version判断。
*/
package xorm
import (
"errors"
"github.com/go-xorm/xorm"
_ "github.com/mattn/go-sqlite3"
"log"
)
type Account struct {
Id int64 //默认主键 如果指定其它为主键则要加tag `xorm:"pk"`
Name string //`xorm:"unique"` // tag
Balance float64
Version int `xorm:"version"` //乐观锁
}
var x *xorm.Engine
func init() {
var err error
x, err = xorm.NewEngine("sqlite3", "./bank.db") // xorm.NewEngine{"sqlite3","./bank.db"}
if err != nil {
log.Fatalf("Fail to create engine %v", err)
}
if err = x.Sync(new(Account)); err != nil {
log.Fatalf("Fal to sync database: %v", err)
}
}
func NewAccount(name string, balance float64) error {
//新增记录
_, err := x.Insert(&Account{Name: name, Balance: balance})
return err
}
// 获取记录
func GetAccount(id int64) (*Account, error) {
a := &Account{} //
has, err := x.Id(id).Get(a)
if err != nil {
return nil, err
} else if !has {
return nil, errors.New("Account not found")
}
return a, nil
}
//存款
func MakeDeposit(id int64, deposit float64) (*Account, error) {
//a := &Account{}
a, err := GetAccount(id)
if err != nil {
return nil, err
}
a.Balance += deposit
_, err = x.Update(a)
return a, err
}
//取款
func MakeWithdraw(id int64, withDraw float64) (*Account, error) {
//a := &Account{}
a, err := GetAccount(id)
if err != nil {
return nil, err
}
if a.Balance <= withDraw {
return nil, errors.New("Not enough balance")
}
a.Balance -= withDraw
_, err = x.Update(a)
return a, err
}
//转帐
func MackTransfer(id1, id2 int64, balance float64) error {
a1, err := GetAccount(id1)
if err != nil {
return err
}
a2, err := GetAccount(id2)
if err != nil {
return err
}
if a1.Balance < balance {
return errors.New("Not enough balance")
}
a1.Balance -= balance
a2.Balance += balance
if _, err = x.Update(a1); err != nil {
return err
} else if _, err = x.Update(a2); err != nil {
return err
}
return nil
}
//批量获取
/*
err = x.Desc("balance").Find(&as) //入一个slice的地址
*/
func GetAccountAscId(orderBy string) (as []*Account, err error) {
err = x.Asc(orderBy).Find(&as)
return as, err
}
//delete
func DeleteAccount(id int64) error {
_, err := x.Delete(&Account{Id: id})
return err
}
xorm 高级用法
事务及回滚
- 创建 session 对象
- 启动事务
- 通过 session 操作数据库
- 发生错误时进行回滚
- 提交事务
// 1.
sess:=x.NewSession()
// 2.
sess.Begin()
// 3.
sess.Update()
// 4.
sess.RollBack()
// 5.
sess.Commit()
修改上例中的转帐操作
//转帐
func MackTransfer(id1, id2 int64, balance float64) error {
a1, err := GetAccount(id1)
if err != nil {
return err
}
a2, err := GetAccount(id2)
if err != nil {
return err
}
if a1.Balance < balance {
return errors.New("Not enough balance")
}
a1.Balance -= balance
a2.Balance += balance
//if _, err = x.Update(a1); err != nil {
// return err
//} else if _, err = x.Update(a2); err != nil {
// return err
//}
//return nil
// 创建session
sess:=x.NewSession()
defer sess.Close() //当函数执行完或出错要释放资源
// 开始事务
if err=sess.Begin();err!=nil {
return err
}
// 开始事务的更新操作
if _, err = sess.Update(a1); err != nil {
sess.Rollback()
return err
} else if _, err = sess.Update(a2); err != nil {
sess.Rollback()
return err
}
// 提交事务
return sess.Commit()
}
统计记录条数
x.Count(new(Accout))
// 配合链式操作
x.Where("id>10").Count(new(Account))
迭代查询
- 迭代查询某个表中符合条件的所有记录
- 使用 Rows 对象
// 迭代查询
x.Iterate(new(Account),func(idx int,bean interface{}){
fmt.Printf("%d,%#vn",idx,bean.(*Account))
})
// Rows
func GetRows() {
a := new(Account)
rows, err := x.Rows(new(Account))
if err != nil {
log.Fatalf("Fail to get row:%vn", err)
}
defer rows.Close()
for rows.Next() {
if err = rows.Scan(a); err != nil {
log.Fatalf("Fail to get row:%vn", err)
}
fmt.Printf("%#vn", a)
}
}
// main.go
package main
import (
"fmt"
"log"
"xorm"
)
type Account xorm.Account
func main() {
fmt.Println("Welcome to bank of xorm2!")
count, err := xorm.GetNum()
if err != nil {
log.Fatalf("Fail to get account count: %vn", err)
}
fmt.Println("Account count: ", count)
for i := count; i < 10; i++ {
if err = xorm.NewAccount(fmt.Sprintf("joe%d", i), float64(i)*100); err != nil {
log.Fatalf("Fail to create account %vn", err)
}
}
// 迭代查询
fmt.Println("Query all recores:")
xorm.It()
fmt.Println("==== Query all recores by Rows:====")
xorm.GetRows()
}
// models.go /src/xorm/models.go 新增这几个方法
// Iterator 迭代
func It() {
x.Iterate(new(Account), printFn)
}
var printFn = func(idx int, bean interface{}) error {
fmt.Printf("%d:%vn", idx, bean.(*Account))
return nil
}
func GetRows() {
a := new(Account)
rows, err := x.Rows(new(Account))
if err != nil {
log.Fatalf("Fail to get row:%vn", err)
}
defer rows.Close()
for rows.Next() {
if err = rows.Scan(a); err != nil {
log.Fatalf("Fail to get row:%vn", err)
}
fmt.Printf("%#vn", a)
}
}
常用查询方法
- 当只需要结构中的某个字段的值对你有价值时
x.Cols("name").Iterate(new(Account),...)- 当希望刻意忽略某个字段的查询结构时
x.Omit("name").Iterate(new(Account)...)- 查询结果偏移,这在分页应用中最为常见
x.Limit(3,2).Iterate(new(Account),...)
func GetNameCol() {
x.Cols("name").Iterate(new(Account),printFn)
}
func ExcludeCol() {
x.Omit("name").Iterate(new(Account),printFn)
}
日志功能
- 开启日志
x.ShowSQL = true- 将日志保存到文件
f,err:=os.Create("sql.log")
if err!=nil {
log.Fatalf("Fail to create log file:%vn",err)
return
}
xorm.NewSimpleLogger(f)
logger:= xorm.NewSimpleLogger(f)
logger.ShowSQL(true) // 和教程不一样,这块需求注意
x.SetLogger(logger)
LRU 缓存
设置默认 LRU 缓存
cacher:=xorm.NewLRUCacher(xorm.NewMemoryStore(),1000)
x.SetDefaultCacher(cacher)
事件钩子
官方提供了 6 类
- BeforeInsert
- AfterInsert
- BeforeUpdate
- AfterUpdate
- BeforeDelete
- AfterDelete
// 作为方法调用
func (a *Account) BeforeInsert(){
log.Printf("before insert %s", a.Name)
}
func (a *Account) AfterInsert(){
log.Printf("after insert %s", a.Name)
}
著名案例
GogsGo WalkerGoBuild.IOSudo China
GoConvery
- 测试代码优雅简洁
- 直接集成 Go 原生测试
- 全自动编译测试
- 详细展示测试结构及覆盖率
- 高可读性的命令行输出结果
- 半自动化书写测试
GoConvery 安装
- gopm 安装
gopm get github.com/smartystreets/goconvey- go get
go get github.com/smartystreets/goconvey
通过简单的示例来实现四则运算的 4 个函数,来使用测试
// main.go 一般不会命名为main包,可做单元测试的包一般都是可做重复利用的包,一般情况下main包不太可能会做重复利用
package goconvey
import "errors"
func Add(a, b int) int {
return a + b
}
func Substract(a, b int) int {
return a - b
}
func Multiply(a, b int) int {
return a * b
}
func Division(a, b int) (int, error) {
if b == 0 {
return 0, errors.New("被除数为0!")
}
return a / b, nil
}
编写测试
//引入两个包 testing和github.com/smartystreets/goconvey/convey
package goconvey
import (
. "github.com/smartystreets/goconvey/convey"
"testing"
)
func TestAdd(t *testing.T) {
Convey("将两数相加", t, func() {
So(Add(1, 2), ShouldEqual, 3)
})
}
func TestSubstract(t *testing.T) {
Convey("将两数相减", t, func() {
So(Substract(1, 2), ShouldEqual, -1)
})
}
func TestMultiply(t *testing.T) {
Convey("将两数相乘", t, func() {
So(Multiply(3, 2), ShouldEqual, 6)
})
}
func TestDivision(t *testing.T) {
Convey("将两数相除", t, func() {
// 嵌套Convery 除0和不除0情况
//注意嵌套的不用传t参数
Convey("被除数为0", func() {
_, err := Division(10, 0)
So(err, ShouldNotBeNil)
})
Convey("除数不为0", func() {
num, err := Division(10, 2)
So(err, ShouldBeNil)
So(num, ShouldEqual, 5)
})
})
}
测试方法有两种,
一种是使用 go test 如下图
Convey有几个就几个点,也可以使用go test -v 内容会更丰富一下些
另外一种安装测试覆盖工具
go get code.google.com/p/go.tools/cmd/cover- 测试目录下输入
goconvey注意 gopath 的设置。
Macaron Web 框架
- 支持灵活多变的路由规则和组合模式
- 支持无限路由缓和无限嵌套
- 支持直接集成现有的服务
- 支持运行时动态设置需要渲染的模板集
- 支持对模块的轻松接入与解除
- 提供便利的依赖注入服务
-
采用更好的路由层和更少的反射来提升执行速度
-
gopm 安装
gopm get github.com/Unknwon/macaron- 通过 go get 安装
go get github.com/Unknwon/macaron
Macaron 框架是从 Gogs 项目中提炼发展而来的,因为在开发 Gogs 的过程中产生了大量辅助模块可以被重复利用到其它项目中,因此才萌发了创建一个新的框架来提高代码的利用率的想法
主题测试文章,只做测试使用。发布者:Walker,转转请注明出处:https://walker-learn.xyz/archives/6715
