{"id":13413679,"url":"https://github.com/marlow/marlow","last_synced_at":"2025-03-14T19:33:00.353Z","repository":{"id":144217613,"uuid":"286753181","full_name":"marlow/marlow","owner":"marlow","description":"persistence layer code generation for golang","archived":false,"fork":false,"pushed_at":"2020-08-18T14:06:35.000Z","size":42,"stargazers_count":14,"open_issues_count":0,"forks_count":2,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-04-22T13:32:14.113Z","etag":null,"topics":["golang"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/marlow.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2020-08-11T13:34:00.000Z","updated_at":"2024-04-22T13:32:14.114Z","dependencies_parsed_at":"2024-01-07T21:54:08.285Z","dependency_job_id":"6c867f92-91e8-4174-a585-b9f3be12da85","html_url":"https://github.com/marlow/marlow","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marlow%2Fmarlow","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marlow%2Fmarlow/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marlow%2Fmarlow/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marlow%2Fmarlow/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/marlow","download_url":"https://codeload.github.com/marlow/marlow/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243635699,"owners_count":20322979,"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":["golang"],"created_at":"2024-07-30T20:01:46.271Z","updated_at":"2025-03-14T19:33:00.052Z","avatar_url":"https://github.com/marlow.png","language":"Go","readme":"\u003cdiv style=\"text-align: center\"\u003e\n  \u003cimg src=\"https://s3.amazonaws.com/coverage.marlow.sizethree.cc/media/marlow.svg\" width=\"65%\" align=\"center\"/\u003e\n\u003c/div\u003e\n\n---\n\nMarlow is a code generation tool written in [golang] designed to create useful constructs that provide an ergonomic API\nfor interacting with a project's data persistence layer while maintaining strong compile time type safety assurance.\n\n---- \n\n[![report.img]][report.url]\n[![godoc.img]][godoc.url]\n![build](https://github.com/marlow/marlow/workflows/build/badge.svg)\n\n---- \n\n### Objective \u0026amp; Inspiration\n\nMarlow was created to improve developer velocity on projects written in [golang] that interact with a data persistence\nlayer, like [mysql] or [postgres]. In other web application backend environments, these interfaces are usually\nprovided by an application framework's [ORM], like the [ActiveRecord] library used by [Rails].\n\nFor web applications leveraging the benefits of using [golang], it can be difficult to construct the equivalent\nabstraction of their database that provides [crud] operations - especially one that is type-safe and [dry]. There are\nseveral open source projects in the golang ecosystem who's goal is exactly that; [`gorm`], [`beego`], and  [`gorp`] to\nname a [few][awesome-go]. Marlow differs from these other projects in its philosophy; rather than attempt to provide an\neloquent orm for your project at runtime, it generates a tailored solution at compile time.\n\n### Usage\n\nAt its core, marlow simply reads a package's [field tags] and generates valid [golang] code. The `marlowc` executable \ncan be installed \u0026 used directly via:\n\n```\ngo get -u github.com/marlow/marlowc\nmarlowc -input=./examples/library/models -stdout=true\n```\n\nFor a full list of options supported by the compiler refer to `marlowc -help`. The command line tool can also be used\nas the executable target for golang's [`go generate`] command using `//go:generate` comment syntax:\n\n```go\npackage models\n\n//go:generate marlowc -input=book.go\n\ntype Book struct {\n  ID       string `marlow=\"column=id\"`\n  AuthorID string `marlow=\"column=author_id\"`\n}\n```\n\nThe generated files will live in the same directory as their source counterparts, with an optional suffix to \ndistinguish them (useful if a project is using [`make`] to manage the build pipeline). In general it is encouraged that\nthe generated files **are not committed to your project's revision control**; the source should *always* be generated\nimmediately before the rest of the package's source code is compiled.\n\n### Generated Code \u0026amp; Field Tag Configuration\n\nThe compiler parses the `marlow` field tag value using the `net/url` package's [`parseQuery`] function. This means that\neach configuration option supported by marlow would end up in delimited by the ampersand (`\u0026`) character where the key \nand value are separated by an equal sign (`=`). For example, a user record may look like:\n\n```go\npackage models\n\ntype User struct {\n\ttable        string `marlow:\"tableName=users\"`\n\tID           uint   `marlow:\"column=id\"`\n\tName         string `marlow:\"column=name\"`\n\tEmail        string `marlow:\"column=email\"`\n\tSettingsMask uint8  `marlow:\"column=settings\u0026bitmask\"`\n}\n```\n\nIn this example, marlow would create the following `UserStore` interface in the `models` package:\n\n```go\ntype UserStore interface {\n\tUpdateUserID(uint, *UserBlueprint) (int64, error)\n\tFindUsers(*UserBlueprint) ([]*User, error)\n\tSelectUserEmails(*UserBlueprint) ([]string, error)\n\tCreateUsers(...User) (int64, error)\n\tUpdateUserEmail(string, *UserBlueprint) (int64, error)\n\tDropUserSettingsMask(uint8, *UserBlueprint) (int64, error)\n\tUpdateUserName(string, *UserBlueprint) (int64, error)\n\tSelectUserNames(*UserBlueprint) ([]string, error)\n\tAddUserSettingsMask(uint8, *UserBlueprint) (int64, error)\n\tCountUsers(*UserBlueprint) (int, error)\n\tSelectUserIDs(*UserBlueprint) ([]uint, error)\n\tUpdateUserSettingsMask(uint8, *UserBlueprint) (int64, error)\n\tDeleteUsers(*UserBlueprint) (int64, error)\n\tSelectUserSettingsMasks(*UserBlueprint) ([]uint8, error)\n}\n```\n\nFor every store that is generated, marlow will create a \"blueprint\" struct that defines a set of fields to be used for\nquerying against the database. In this example, the `UserBlueprint` generated for the store above would look like:\n\n```go\ntype UserBlueprint struct {\n\tIDRange           []uint\n\tID                []uint\n\tNameLike          []string\n\tName              []string\n\tEmailLike         []string\n\tEmail             []string\n\tSettingsMaskRange []uint8\n\tSettingsMask      []uint8\n\tInclusive         bool\n\tLimit             int\n\tOffset            int\n\tOrderBy           string\n\tOrderDirection    string\n}\n```\n\n**Special `table` field**\n\nIf present, marlow will recognize the `table` field's `marlow` tag value as a container for developer specified \noverrides for default marlow assumptions about the table.\n\n| Option | Description |\n| :--- | :--- |\n| `tableName` | The name of the table (marlow will assume a lowercased \u0026amp; pluralized version of the struct name). |\n| `dialect` | Specifying the `dialect` option determines the syntax used by marlow during db queries. See [supported-drivers](#supported-drivers) for more info. |\n| `primaryKey` | Some dialects require that marlow knows of the table's primary key during transactions. If provided, this value will be used as the _column name_ by marlow. |\n| `storeName` | The name of the store type that will be generated, defaults to `%sStore`, where `%s` is the name of the struct. |\n| `blueprintName` | The name of the blueprint type that will be generated, defaults to `%sBlueprint`, where `%s` is the name of the struct. |\n| `defaultLimit` | When using the queryable feature, this will be the default maximum number of records to load. |\n| `blueprintRangeFieldSuffix` | A string that is added to numerical blueprint fields for range selections. Defults to `%sRange` where `%s` is the name of the field (e.g: `AuthorIDRange`). |\n| `blueprintLikeFieldSuffix` | A string that is added to string/text blueprint fields for like selections. Defaults to `%sLike` where `%s` is the name of the field (e.g: `FirstNameLike`). |\n\n**All other fields**\n\nFor every other field found on the source `struct`, marlow will use the following field tag values:\n\n| Option | Description |\n| :--- | :--- |\n| `column` | This is the column that any raw sql generated will target when scanning/selecting/querying this field. |\n| `autoIncrement` | If `true`, this flag will prevent marlow from generating sql during creation that would attempt to insert the value of the field for the column. |\n| `bitmask` | If present, the compiler will generate `AddRecordFieldMask` and `DropRecordFieldMask` methods which will perform native bitwise operations as `UPDATE` queries to the datbase. |\n\n#### Generated Coverage \u0026 Documentation\n\nWhile everyone's generated marlow code will likely be unique, the [`examples/library`] application includes a\ncomprehensive test suite that demonstrates the features of marlow using 3 models - [`Author`], [`Book`] and [`Genre`].\n\n#### Supported Drivers\n\nThe follow is a list of officially support [`driver.Driver`] implementations supported by the generated marlow code. To request an additional driver, feel free to open up an [issue][issues].\n\n| Driver Name | `dialect` Value |\n| :--- | :--- |\n| [`github.com/lib/pq`] | `postgres` | \n| [`github.com/mattn/go-sqlite3`] | none or `sql` | \n| [`github.com/go-sql-driver/mysql`] | none or `sql` | \n\n\n----\n\n![logo][logo.img]\n\n----\n\n[preview]: https://s3.amazonaws.com/marlow-go/media/marlow.gif\n[`ParseQuery`]: https://golang.org/pkg/net/url/#ParseQuery\n[`make`]: https://www.gnu.org/software/make/\n[`go generate`]: https://blog.golang.org/generate\n[awesome-go]: https://github.com/avelino/awesome-go#orm\n[`gorm`]: https://github.com/jinzhu/gorm\n[`gorp`]: https://github.com/go-gorp/gorp\n[`beego`]: https://github.com/astaxie/beego/tree/master/orm\n[crud]: https://en.wikipedia.org/wiki/Create,_read,_update_and_delete\n[dry]: https://en.wikipedia.org/wiki/Don%27t_repeat_yourself\n[Rails]: http://rubyonrails.org\n[issues]: https://github.com/marlow/marlow/issues\n[ActiveRecord]: http://guides.rubyonrails.org/active_record_basics.html\n[ORM]: https://en.wikipedia.org/wiki/Object-relational_mapping\n[mysql]: https://www.mysql.com\n[postgres]: https://www.postgresql.org\n[struct field tags]: https://golang.org/ref/spec#Tag\n[golang]: https://golang.org\n[report.img]: https://goreportcard.com/badge/github.com/marlow/marlow?style=flat-square\n[report.url]: https://goreportcard.com/report/github.com/marlow/marlow\n[godoc.img]: http://img.shields.io/badge/godoc-reference-5272B4.svg?style=flat-square\n[godoc.url]: https://godoc.org/github.com/marlow/marlow\n[field tags]: https://golang.org/pkg/reflect/#StructTag\n[logo.img]: https://s3.amazonaws.com/coverage.marlow.sizethree.cc/media/marlow.svg\n[`github.com/lib/pq`]: https://github.com/lib/pq\n[`github.com/mattn/go-sqlite3`]: https://github.com/mattn/go-sqlite3\n[`github.com/go-sql-driver/mysql`]: https://github.com/go-sql-driver/mysql\n[`driver.Driver`]: https://golang.org/pkg/database/sql/driver/#Driver\n[`examples/library`]: https://github.com/marlow/library-demo\n[`Author`]: https://github.com/marlow/library-demo/blob/master/models/author_test.go\n[`Book`]: https://github.com/marlow/library-demo/blob/master/models/book_test.go\n[`Genre`]: https://github.com/marlow/library-demo/blob/master/models/genre_test.go\n[`GH-67`]: https://github.com/dadleyy/marlow/issues/67\n","funding_links":[],"categories":["ORM","网络相关库","Relational Databases"],"sub_categories":["HTTP Clients","HTTP客户端","ORM","OpenGL"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmarlow%2Fmarlow","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmarlow%2Fmarlow","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmarlow%2Fmarlow/lists"}