{"id":21377853,"url":"https://github.com/eltorocorp/go-check","last_synced_at":"2025-03-16T09:46:00.125Z","repository":{"id":57582162,"uuid":"184668764","full_name":"eltorocorp/go-check","owner":"eltorocorp","description":"Check is a compact error handling framework for go.","archived":false,"fork":false,"pushed_at":"2019-05-03T17:37:25.000Z","size":14,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-01-22T22:11:16.428Z","etag":null,"topics":["error-handling","go","golang","panic","sql","transactions"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/eltorocorp.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-05-02T23:22:35.000Z","updated_at":"2019-05-03T17:37:09.000Z","dependencies_parsed_at":"2022-09-26T19:32:25.493Z","dependency_job_id":null,"html_url":"https://github.com/eltorocorp/go-check","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eltorocorp%2Fgo-check","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eltorocorp%2Fgo-check/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eltorocorp%2Fgo-check/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eltorocorp%2Fgo-check/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/eltorocorp","download_url":"https://codeload.github.com/eltorocorp/go-check/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243852428,"owners_count":20358270,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["error-handling","go","golang","panic","sql","transactions"],"created_at":"2024-11-22T09:24:00.771Z","updated_at":"2025-03-16T09:46:00.099Z","avatar_url":"https://github.com/eltorocorp.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"go-check\n========\n\n`go get github.com/eltorocorp/go-check`\n\n\nExample\n=======\nHere's a block of code with conventional go error handling\n```go\nfunc (a *API) CalculateContributorScore(user usercontextiface.UserContextAPI, contributorID int) (float64, error) {\n\tif user == nil {\n\t\treturn 0, inertiaerrors.ErrPermissionDenied\n\t}\n\n\tadmin := \u0026administration.API{DB: a.DB}\n\tsettings, err := admin.GetSettings(user)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\tavgGoalScore, err := a.averageRawScoreForContributor(contributorID)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\tleadID, err := a.getLeadIDForContributor(contributorID)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\tavgLeadScore, err := a.averageScoreForLead(leadID)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn calculateBiasedScore(avgGoalScore, avgLeadScore, settings.BaseScore), nil\n}\n```\n\nWith `check.Trap`, we can restate as follows:\n\n```go\nfunc (a *API) CalculateContributorScore(user usercontextiface.UserContextAPI, contributorID int) (out float64, err error) {\n    err = check.Trap(func() {\n        if user == nil {\n            panic(ErrPermissionDenied)\n        }\n\n        admin := \u0026administration.API{DB: a.DB}\n        settings:= check.IFace(admin.GetSettings(user)).(*models.Setting)\n        avgGoalScore:= check.Float64(a.averageRawScoreForContributor(contributorID))\n        leadID:= check.Int(a.getLeadIDForContributor(contributorID))\n        avgLeadScore:= check.Float64(a.averageScoreForLead(leadID))\n        out = check.Float64(calculateBiasedScore(avgGoalScore, avgLeadScore, settings.BaseScore))\n    })\n    return\n}\n```\n\nNotice 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.\n\nHow it Works\n============\nHelper 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.\n`check.Trap` in turn, traps the panic caused by the helper function, retrieves the error that caused the panic, and returns the error.\n\nTransaction Handling\n====================\n`check` can also be used to simplify database transaction handling in line with err handling.\n\nThis 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.\n\nHere is an example of a database transaction and errors being handled conventionally:\n\n```go\nfunc (c *Context) ExpireSessions() error {\n\tif c.SessionToken() == \"\" {\n\t\treturn nil\n\t}\n\n\ttx, err := c.db.Begin()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tsessions, err := models.GetAllSessions(tx)\n\tif err != nil {\n\t\treturn err\n\t}\n\tfor _, session := range sessions {\n\t\tif session.PersonID == c.UserID() {\n\t\t\terr = session.Delete(tx)\n\t\t\tif err != nil {\n\t\t\t\tlog.Println(err)\n\t\t\t\terr = tx.Rollback()\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn tx.Commit()\n}\n```\n\nHere is the same code, but rewritten with `check.TrapTx`:\n\n\n```go\nfunc (c *Context) ExpireSessions() error {\n    return check.TrapTx(check.UseDB(c.DB), func(tx check.Tx) {\n        if c.SessionToken() == \"\" {\n            return\n        }\n\n        sessions:= check.IFace(models.GetAllSessions(tx)).([]*models.Session)\n        for _, session := range sessions {\n            if session.PersonID == c.UserID() {\n                check.Err(session.Delete(tx))\n            }\n        }\n    })\n}\n```\n\nJust 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.\n\nHow it Works\n============\nIn 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.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feltorocorp%2Fgo-check","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Feltorocorp%2Fgo-check","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feltorocorp%2Fgo-check/lists"}