项目作者: eltorocorp

项目描述 :
Check is a compact error handling framework for go.
高级语言: Go
项目地址: git://github.com/eltorocorp/go-check.git
创建时间: 2019-05-02T23:22:35Z
项目社区:https://github.com/eltorocorp/go-check

开源协议:MIT License

下载


go-check

go get github.com/eltorocorp/go-check

Example

Here’s a block of code with conventional go error handling

  1. func (a *API) CalculateContributorScore(user usercontextiface.UserContextAPI, contributorID int) (float64, error) {
  2. if user == nil {
  3. return 0, inertiaerrors.ErrPermissionDenied
  4. }
  5. admin := &administration.API{DB: a.DB}
  6. settings, err := admin.GetSettings(user)
  7. if err != nil {
  8. return 0, err
  9. }
  10. avgGoalScore, err := a.averageRawScoreForContributor(contributorID)
  11. if err != nil {
  12. return 0, err
  13. }
  14. leadID, err := a.getLeadIDForContributor(contributorID)
  15. if err != nil {
  16. return 0, err
  17. }
  18. avgLeadScore, err := a.averageScoreForLead(leadID)
  19. if err != nil {
  20. return 0, err
  21. }
  22. return calculateBiasedScore(avgGoalScore, avgLeadScore, settings.BaseScore), nil
  23. }

With check.Trap, we can restate as follows:

  1. func (a *API) CalculateContributorScore(user usercontextiface.UserContextAPI, contributorID int) (out float64, err error) {
  2. err = check.Trap(func() {
  3. if user == nil {
  4. panic(ErrPermissionDenied)
  5. }
  6. admin := &administration.API{DB: a.DB}
  7. settings:= check.IFace(admin.GetSettings(user)).(*models.Setting)
  8. avgGoalScore:= check.Float64(a.averageRawScoreForContributor(contributorID))
  9. leadID:= check.Int(a.getLeadIDForContributor(contributorID))
  10. avgLeadScore:= check.Float64(a.averageScoreForLead(leadID))
  11. out = check.Float64(calculateBiasedScore(avgGoalScore, avgLeadScore, settings.BaseScore))
  12. })
  13. return
  14. }

Notice how the overall intent of the code is now much more clear since all of the transaction and error handling noise has been abstracted away.

How it Works

Helper functions (such as check.Float64) are wrapped around functions that return a value and an error. These helper functions panic if the error is not nil, and otherwise return value.
check.Trap in turn, traps the panic caused by the helper function, retrieves the error that caused the panic, and returns the error.

Transaction Handling

check can also be used to simplify database transaction handling in line with err handling.

This is done with the use of check.TrapTx, which is similar to check.Trap, but also manages a transaction within the same context as any errors.

Here is an example of a database transaction and errors being handled conventionally:

  1. func (c *Context) ExpireSessions() error {
  2. if c.SessionToken() == "" {
  3. return nil
  4. }
  5. tx, err := c.db.Begin()
  6. if err != nil {
  7. return err
  8. }
  9. sessions, err := models.GetAllSessions(tx)
  10. if err != nil {
  11. return err
  12. }
  13. for _, session := range sessions {
  14. if session.PersonID == c.UserID() {
  15. err = session.Delete(tx)
  16. if err != nil {
  17. log.Println(err)
  18. err = tx.Rollback()
  19. if err != nil {
  20. return err
  21. }
  22. }
  23. }
  24. }
  25. return tx.Commit()
  26. }

Here is the same code, but rewritten with check.TrapTx:

  1. func (c *Context) ExpireSessions() error {
  2. return check.TrapTx(check.UseDB(c.DB), func(tx check.Tx) {
  3. if c.SessionToken() == "" {
  4. return
  5. }
  6. sessions:= check.IFace(models.GetAllSessions(tx)).([]*models.Session)
  7. for _, session := range sessions {
  8. if session.PersonID == c.UserID() {
  9. check.Err(session.Delete(tx))
  10. }
  11. }
  12. })
  13. }

Just as in the check.Trap example, notice how the overall intent of the code is now much more clear since all of the transaction and error handling noise has been abstracted away.

How it Works

In this case, a database reference is passed into check.TrapTx. Internally, check will create a transaction for the underlaying database. That transaction is then passed into the closure supplied to TrapFx. If any errors occur within the closure, the helper functions (such as check.Err) will panic. check will then recover from the panic, and automatically rollback the transaction. If the closure returns without any panicks, check will automatically commit the transaction.