Skip to content

Commit

Permalink
Merge pull request gobuffalo#6 from markbates/fizz
Browse files Browse the repository at this point in the history
Fizz
  • Loading branch information
markbates authored Aug 15, 2016
2 parents 4507b81 + ba3416e commit ae9d5aa
Show file tree
Hide file tree
Showing 63 changed files with 2,568 additions and 618 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,4 @@ bin/*
gin-bin
*.sqlite
vendor/
tsoda
17 changes: 9 additions & 8 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,16 @@ install:
- go get -t -v ./...

before_script:
- mysql -e 'create database pop_test;'
- mysql -u travis --password= pop_test < sql_scripts/mysql-pop_test.sql
- psql -c 'create database pop_test;' -U postgres
- psql pop_test < sql_scripts/postgres-pop_test.sql
- go build -o tsoda ./soda
# travis hangs when trying to create mysql db using soda. not sure why
- mysql -e 'create database pop_test;'
- ./tsoda create -e "postgres"
- ./tsoda create -e "sqlite"
- ./tsoda migrate -e "mysql_travis"
- ./tsoda migrate -e "postgres"
- ./tsoda migrate -e "sqlite"

# script: ./test.sh
# script: ./travis.sh
script: go test ./...
script: go test ./... -v

global_env:
- MYSQL_USER="travis"
Expand All @@ -36,4 +38,3 @@ addons:
matrix:
allow_failures:
- go: 'tip'

35 changes: 28 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## A Tasty Treat For All Your Database Needs

So what does Pop do exactly? Well, it wraps the absolutely amazing [https://github.com/jmoiron/sqlx](https://github.com/jmoiron/sqlx) and [https://github.com/mattes/migrate](https://github.com/mattes/migrate) libraries. It cleans up some of the common patterns and workflows usually associated with dealing with databases in Go.
So what does Pop do exactly? Well, it wraps the absolutely amazing [https://github.com/jmoiron/sqlx](https://github.com/jmoiron/sqlx) library. It cleans up some of the common patterns and workflows usually associated with dealing with databases in Go.

Pop makes it easy to do CRUD operations, run migrations, and build/execute queries. Is Pop an ORM? I'll leave that up to you, the reader, to decide.

Expand All @@ -13,6 +13,12 @@ Pop, by default, follows conventions that were defined by the ActiveRecord Ruby
* If there is a timestamp column named "updated_at", "UpdatedAt" on the `struct`, it will be set with the current time when the record is updated.
* Default databases are lowercase, underscored versions of the `struct` name. Examples: User{} is "users", FooBar{} is "foo_bars", etc...

## Supported Databases

* PostgreSQL (>= 9.3)
* MySQL (>= 5.7)
* SQLite (>= 3.x)

## Connecting to Databases

Pop is easily configured using a YAML file. The configuration file should be stored in `config/database.yml` or `database.yml`.
Expand Down Expand Up @@ -74,7 +80,7 @@ Pop features CLI support via the `soda` for the following operations:
### Installing CLI Support

```bash
$ go get -d -t -u github.com/markbates/pop/...
$ go get github.com/markbates/pop/...
$ go install github.com/markbates/pop/soda
```

Expand All @@ -85,7 +91,7 @@ Assuming you defined a configuration file like that described in the above secti
#### Create All Databases

```bash
$ soda create -all
$ soda create -a
```

#### Create a Specific Database
Expand All @@ -101,7 +107,7 @@ Assuming you defined a configuration file like that described in the above secti
#### Drop All Databases

```bash
$ soda drop -all
$ soda drop -a
```

#### Drop a Specific Database
Expand Down Expand Up @@ -131,11 +137,26 @@ $ soda migrate create name_of_migration
Running this command with generate the following files:

```text
./migrations/0001_name_of_migration.up.sql
./migrations/0001_name_of_migration.down.sql
./migrations/20160815134952_name_of_migration.up.fizz
./migrations/20160815134952_name_of_migration.down.fizz
```

The generated files are `fizz` files. [Fizz](./fizz/README.md) lets you use a common DSL for generating migrations. This means the same `.fizz` file can be run against any of the supported dialects of Pop! Find out more about [Fizz](./fizz/README.md)

If you want to generate old fashion `.sql` files you can use the `-t` flag for that:

```bash
$ soda migrate create name_of_migration -t sql
```

Running this command with generate the following files:

```text
./migrations/20160815134952_name_of_migration.up.sql
./migrations/20160815134952_name_of_migration.down.sql
```

It is up to you to fill these files with the appropriate SQL to do whatever it is you need done.
The `soda migrate` command supports both `.fizz` and `.sql` files, so you can mix and match them to suit your needs.

#### Running Migrations

Expand Down
13 changes: 11 additions & 2 deletions connection.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,14 +63,23 @@ func Connect(e string) (*Connection, error) {
if c == nil {
return c, fmt.Errorf("Could not find connection named %s!", e)
}
err := c.Open()
return c, err
}

func (c *Connection) Open() error {
if c.Store != nil {
return c, nil
return nil
}
db, err := sqlx.Open(c.Dialect.Details().Dialect, c.Dialect.URL())
if err == nil {
c.Store = &dB{db}
}
return c, err
return nil
}

func (c *Connection) Close() error {
return c.Store.Close()
}

// Transaction will start a new transaction on the connection. If the inner function
Expand Down
2 changes: 1 addition & 1 deletion database.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ mysql_travis:
host: {{ envOr "MYSQL_HOST" "localhost" }}
port: {{ envOr "MYSQL_PORT" "3306" }}
user: {{ envOr "MYSQL_USER" "root" }}
password: ""
password: {{ envOr "MYSQL_PASSWORD" "" }}

postgres:
dialect: "postgres"
Expand Down
14 changes: 5 additions & 9 deletions dialect.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"

. "github.com/markbates/pop/columns"
"github.com/markbates/pop/fizz"
)

type Dialect interface {
Expand All @@ -18,15 +19,14 @@ type Dialect interface {
SelectMany(store Store, models *Model, query Query) error
CreateDB() error
DropDB() error
FizzTranslator() fizz.Translator
}

func genericCreate(store Store, model *Model, cols Columns) error {
var id int64
w := cols.Writeable()
query := fmt.Sprintf("INSERT INTO %s (%s) VALUES (%s)", model.TableName(), w.String(), w.SymbolizedString())
if Debug {
Log(query)
}
Log(query)
res, err := store.NamedExec(query, model.Value)
if err != nil {
return err
Expand All @@ -40,9 +40,7 @@ func genericCreate(store Store, model *Model, cols Columns) error {

func genericUpdate(store Store, model *Model, cols Columns) error {
stmt := fmt.Sprintf("UPDATE %s SET %s where id = %d", model.TableName(), cols.Writeable().UpdateString(), model.ID())
if Debug {
Log(stmt)
}
Log(stmt)
_, err := store.NamedExec(stmt, model.Value)
return err
}
Expand All @@ -53,9 +51,7 @@ func genericDestroy(store Store, model *Model) error {
}

func genericExec(store Store, stmt string) error {
if Debug {
Log(stmt)
}
Log(stmt)
_, err := store.Exec(stmt)
return err
}
Expand Down
2 changes: 1 addition & 1 deletion doc.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
So what does Pop do exactly? Well, it wraps the absolutely amazing https://github.com/jmoiron/sqlx and https://github.com/mattes/migrate libraries. It cleans up some of the common patterns and workflows usually associated with dealing with databases in Go.
So what does Pop do exactly? Well, it wraps the absolutely amazing https://github.com/jmoiron/sqlx library. It cleans up some of the common patterns and workflows usually associated with dealing with databases in Go.
Pop makes it easy to do CRUD operations, run migrations, and build/execute queries. Is Pop an ORM? I'll leave that up to you, the reader, to decide.
Expand Down
1 change: 1 addition & 0 deletions executors.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ func (c *Connection) Reload(model interface{}) error {
func (q *Query) Exec() error {
return q.Connection.timeFunc("Exec", func() error {
sql, args := q.ToSQL(nil)
Log(sql, args...)
_, err := q.Connection.Store.Exec(sql, args...)
return err
})
Expand Down
1 change: 1 addition & 0 deletions finders.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ func (q Query) Count(model interface{}) (int, error) {
q.Paginator = nil
col := "count(*) as row_count"
query, args := q.ToSQL(&Model{Value: model}, col)
Log(query, args...)
return q.Connection.Store.Get(res, query, args...)
})
return res.Count, err
Expand Down
120 changes: 120 additions & 0 deletions fizz/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
# Fizz

## A Common DSL for Migrating Databases

## Create a Table

``` javascript
create_table("users", func(t) {
t.Column("email", "string", {})
t.Column("twitter_handle", "string", {"size": 50})
t.Column("age", "integer", {"default": 0})
t.Column("admin", "boolean", {"default": false})
t.Column("bio", "text", {"null": true})
t.Column("joined_at", "timestamp", {})
})
```

The `create_table` function will also generate an `id` column of type `integer` that will auto-increment. It will also generate two `timestamp` columns; `created_at` and `updated_at`.

Columns all have the same syntax. First is the name of the column. Second is the type of the field. Third is any options you want to set on that column.

#### <a name="column-info"></a> "Common" Types:

* `string`
* `text`
* `timestamp`
* `integer`
* `boolean`

Any other type passed it will be be passed straight through to the underlying database. For example for PostgreSQL you could pass `jsonb`and it will be supported, however, SQLite will yell very loudly at you if you do the same thing!

#### Supported Options:

* `size` - The size of the column. For example if you wanted a `varchar(50)` in Postgres you would do: `t.Column("column_name", "string", {"size": 50})`
* `null` - By default columns are not allowed to be `null`.
`default` - The default value you want for this column. By default this is `null`.

## Drop a Table

``` javascript
drop_table("table_name")
```

## Rename a Table

``` javascript
rename_table("old_table_name", "new_table_name")
```

## Add a Column

``` javascript
add_column("table_name", "column_name", "string", {})
```

See [above](#column-info) for more details on column types and options.

## Rename a Column

``` javascript
rename_column("table_name", "old_column_name", "new_column_name")
```

## Drop a Column

``` javascript
drop_column("table_name", "column_name")
```

## Add an Index

#### Supported Options:

* `name` - This defaults to `table_name_column_name_idx`
* `unique`

### Simple Index:

``` javascript
add_index("table_name", "column_name", {})
```

### Multi-Column Index:

``` javascript
add_index("table_name", ["column_1", "column_2"], {})
```

### Unique Index:

``` javascript
add_index("table_name", "column_name", {"unique": true})
```

### Index Names:

``` javascript
add_index("table_name", "column_name", {}) # name => table_name_column_name_idx
add_index("table_name", "column_name", {"name": "custom_index_name"})
```

## Rename an Index

``` javascript
rename_index("table_name", "old_index_name", "new_index_name")
```

## Drop an Index

``` javascript
drop_index("table_name", "index_name")
```

## Raw SQL

``` javascript
raw("select * from users;")
```

*All calls to `raw` must end with a `;`!*
57 changes: 57 additions & 0 deletions fizz/bubbler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package fizz

import (
"fmt"
"strings"

"github.com/mattn/anko/vm"

core "github.com/mattn/anko/builtins"
)

type BubbleType int

type Bubbler struct {
Translator
data []string
}

func NewBubbler(t Translator) *Bubbler {
return &Bubbler{
Translator: t,
data: []string{},
}
}

func (b *Bubbler) String() string {
return strings.Join(b.data, "\n")
}

func (b *Bubbler) Bubble(s string) (string, error) {
env := core.Import(vm.NewEnv())

f := fizzer{b}

// columns:
env.Define("add_column", f.AddColumn())
env.Define("drop_column", f.DropColumn())
env.Define("rename_column", f.RenameColumn())

env.Define("raw", f.RawSql())

// indexes:
env.Define("add_index", f.AddIndex())
env.Define("drop_index", f.DropIndex())
env.Define("rename_index", f.RenameIndex())

// tables:
env.Define("create_table", f.CreateTable())
env.Define("drop_table", f.DropTable())
env.Define("rename_table", f.RenameTable())

_, err := env.Execute(s)
if err != nil {
fmt.Printf("ParseError: err -> %#v\n", err)
}
return b.String(), err
}
1 change: 1 addition & 0 deletions fizz/bubbler_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package fizz_test
Loading

0 comments on commit ae9d5aa

Please sign in to comment.