Skip to content

Commit

Permalink
feat: initial
Browse files Browse the repository at this point in the history
  • Loading branch information
aldy505 committed Jun 24, 2021
0 parents commit 82c04c6
Show file tree
Hide file tree
Showing 15 changed files with 454 additions and 0 deletions.
100 changes: 100 additions & 0 deletions .github/COMMIT_CONVENTION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
> [conventional-changelog](https://github.com/ajoslin/conventional-changelog) [angular](https://github.com/angular/angular) preset
## Angular Convention

Angular's [commit message guidelines](https://github.com/angular/angular/blob/master/CONTRIBUTING.md#commit).

### Examples

Appears under "Features" header, pencil subheader:

```
feat(pencil): add 'graphiteWidth' option
```

Appears under "Bug Fixes" header, graphite subheader, with a link to issue #28:

```
fix(graphite): stop graphite breaking when width < 0.1
Closes #28
```

Appears under "Performance Improvements" header, and under "Breaking Changes" with the breaking change explanation:

```
perf(pencil): remove graphiteWidth option
BREAKING CHANGE: The graphiteWidth option has been removed. The default graphite width of 10mm is always used for performance reason.
```

The following commit and commit `667ecc1` do not appear in the changelog if they are under the same release. If not, the revert commit appears under the "Reverts" header.

```
revert: feat(pencil): add 'graphiteWidth' option
This reverts commit 667ecc1654a317a13331b17617d973392f415f02.
```

### Commit Message Format

A commit message consists of a **header**, **body** and **footer**. The header has a **type**, **scope** and **subject**:

```
<type>(<scope>): <subject>
<BLANK LINE>
<body>
<BLANK LINE>
<footer>
```

The **header** is mandatory and the **scope** of the header is optional.

### Revert

If the commit reverts a previous commit, it should begin with `revert: `, followed by the header of the reverted commit. In the body it should say: `This reverts commit <hash>.`, where the hash is the SHA of the commit being reverted.

### Type

If the prefix is `feat`, `fix` or `perf`, it will appear in the changelog. However if there is any [BREAKING CHANGE](#footer), the commit will always appear in the changelog.

Other prefixes are up to your discretion. Suggested prefixes are `build`, `ci`, `docs` ,`style`, `refactor`, and `test` for non-changelog related tasks.

Details regarding these types can be found in the official [Angular Contributing Guidelines](https://github.com/angular/angular/blob/master/CONTRIBUTING.md#type).

### Scope

The scope could be anything specifying place of the commit change. For example `$location`,
`$browser`, `$compile`, `$rootScope`, `ngHref`, `ngClick`, `ngView`, etc...

### Subject

The subject contains succinct description of the change:

* use the imperative, present tense: "change" not "changed" nor "changes"
* don't capitalize first letter
* no dot (.) at the end

### Body

Just as in the **subject**, use the imperative, present tense: "change" not "changed" nor "changes".
The body should include the motivation for the change and contrast this with previous behavior.

### Footer

The footer should contain any information about **Breaking Changes** and is also the place to
reference GitHub issues that this commit **Closes**.

**Breaking Changes** should start with the word `BREAKING CHANGE:` with a space or two newlines. The rest of the commit message is then used for this.

A detailed explanation can be found in this [document](#commit-message-format).

[npm-image]: https://badge.fury.io/js/conventional-changelog-angular.svg
[npm-url]: https://npmjs.org/package/conventional-changelog-angular
[travis-image]: https://travis-ci.org/conventional-changelog/conventional-changelog-angular.svg?branch=master
[travis-url]: https://travis-ci.org/conventional-changelog/conventional-changelog-angular
[daviddm-image]: https://david-dm.org/conventional-changelog/conventional-changelog-angular.svg?theme=shields.io
[daviddm-url]: https://david-dm.org/conventional-changelog/conventional-changelog-angular
[coveralls-image]: https://coveralls.io/repos/conventional-changelog/conventional-changelog-angular/badge.svg
[coveralls-url]: https://coveralls.io/r/conventional-changelog/conventional-changelog-angular
[commit-message-format]: https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit#
23 changes: 23 additions & 0 deletions .github/workflow/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: Build test

on: [pull_request, push]

jobs:
build-test:
name: Build test
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
go-version: [1.15.x, 1.16.x]
steps:
- name: Checkout code
uses: actions/checkout@v2

- name: Install Go
uses: actions/setup-go@v2
with:
go-version: ${{ matrix.go-version }}

- name: Build
run: go build ./
23 changes: 23 additions & 0 deletions .github/workflow/coverage.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: Test and coverage

on: [push, pull_request]

jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
with:
fetch-depth: 2

- name: Install Go
uses: actions/setup-go@v2
with:
go-version: 1.16.x

- name: Run coverage
run: go test -v -race -coverprofile=coverage.out -covermode=atomic -failfast

- name: Upload coverage to Codecov
uses: codecov/codecov-action@v1
15 changes: 15 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib

# Test binary, built with `go test -c`
*.test

# Output of the go coverage tool, specifically when used with LiteIDE
*.out

# Dependency directories (remove the comment below to include it)
# vendor/
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2021-present Reinaldy Rafli and Bob collaborators

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
27 changes: 27 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Bob - SQL Query Builder

I really need a create table SQL builder, and I can't find one. So, like everything else, I made one. Heavily inspired by [Squirrel](https://github.com/Masterminds/squirrel) and [Knex](https://knexjs.org/).

Oh, and of course, heavily inspired by Bob the Builder.

## Usage

It's not ready for production (yet). But, the API is probably close to how you'd do things on Squirrel.

```go
import "github.com/aldy505/bob"

func main() {
sql, _, err := bob.CreateTable("users").
Columns("id", "email", "name", "password", "date").
Types("varchar(36)", "varchar(255)", "varchar(255)", "text", "date").
Primary("id").
Unique("email")
ToSql()
if err != nil {
log.Fatal(err)
}

// process SQL with whatever you like it
}
```
28 changes: 28 additions & 0 deletions append.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package bob

import "io"

func AppendToSql(parts []BobBuilder, w io.Writer, sep string, args []interface{}) ([]interface{}, error) {
for i, p := range parts {
partSql, partArgs, err := p.ToSql()
if err != nil {
return nil, err
} else if len(partSql) == 0 {
continue
}

if i > 0 {
_, err := io.WriteString(w, sep)
if err != nil {
return nil, err
}
}

_, err = io.WriteString(w, partSql)
if err != nil {
return nil, err
}
args = append(args, partArgs...)
}
return args, nil
}
19 changes: 19 additions & 0 deletions bob.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package bob

import "github.com/lann/builder"

type BobBuilderType builder.Builder

type BobBuilder interface {
ToSql() (string, []interface{}, error)
}

func (b BobBuilderType) CreateTable(table string) CreateBuilder {
return CreateBuilder(b).Name(table)
}

var BobStmtBuilder = BobBuilderType(builder.EmptyBuilder)

func CreateTable(table string) CreateBuilder {
return BobStmtBuilder.CreateTable(table)
}
99 changes: 99 additions & 0 deletions create.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package bob

import (
"bytes"
"errors"
"strings"

"github.com/aldy505/bob/util"
"github.com/lann/builder"
)

type CreateBuilder builder.Builder

type createData struct {
TableName string
Schema string
Columns []string
Types []string
Primary string
Unique string
NotNull []string
}

func init() {
builder.Register(CreateBuilder{}, createData{})
}

func (b CreateBuilder) Name(name string) CreateBuilder {
return builder.Set(b, "TableName", name).(CreateBuilder)
}

func (b CreateBuilder) WithSchema(name string) CreateBuilder {
return builder.Set(b, "Schema", name).(CreateBuilder)
}

func (b CreateBuilder) Columns(cols ...string) CreateBuilder {
return builder.Set(b, "Columns", cols).(CreateBuilder)
}

func (b CreateBuilder) Types(types ...string) CreateBuilder {
return builder.Set(b, "Types", types).(CreateBuilder)
}

func (b CreateBuilder) Primary(column string) CreateBuilder {
return builder.Set(b, "Primary", column).(CreateBuilder)
}

func (b CreateBuilder) Unique(column string) CreateBuilder {
return builder.Set(b, "Unique", column).(CreateBuilder)
}

func (b CreateBuilder) ToSql() (string, []interface{}, error) {
data := builder.GetStruct(b).(createData)
return data.ToSql()
}

func (d *createData) ToSql() (sqlStr string, args []interface{}, err error) {
if len(d.TableName) == 0 {
err = errors.New("create statements must specify a table")
return
}

if (len(d.Columns) != len(d.Types)) && len(d.Columns) > 0 {
err = errors.New("columns and types should have equal length")
return
}

sql := &bytes.Buffer{}

sql.WriteString("CREATE TABLE ")

if d.Schema != "" {
sql.WriteString("`" + d.Schema + "`.")
}

sql.WriteString("`" + d.TableName + "`")
sql.WriteString(" ")

var columnTypes []string
for i := 0; i < len(d.Columns); i++ {
columnTypes = append(columnTypes, "`"+d.Columns[i]+"` "+d.Types[i])
}

sql.WriteString("(")
sql.WriteString(strings.Join(columnTypes, ", "))
sql.WriteString(");")

if d.Primary != "" && util.IsIn(d.Columns, d.Primary) {
sql.WriteString(" ")
sql.WriteString("ALTER TABLE `" + d.TableName + "` ADD PRIMARY KEY (`" + d.Primary + "`);")
}

if d.Unique != "" && util.IsIn(d.Columns, d.Unique) {
sql.WriteString(" ")
sql.WriteString("ALTER TABLE `" + d.TableName + "` ADD UNIQUE (`" + d.Unique + "`);")
}
sqlStr = sql.String()
return
}

0 comments on commit 82c04c6

Please sign in to comment.