Skip to content

Commit

Permalink
- registering method receiver directly for the driver
Browse files Browse the repository at this point in the history
- currently each driver contains only a single method receiver:
   - enforcing method receiver pre-registration on go methods driver initialization
   - Method receiver name can be removed from files format
- passing the DbName parameter inside the method receiver for the go methods driver
  • Loading branch information
dimag-jfrog committed Aug 9, 2016
1 parent 570e4b4 commit 866ff82
Show file tree
Hide file tree
Showing 6 changed files with 116 additions and 102 deletions.
53 changes: 18 additions & 35 deletions driver/gomethods/gomethods_migrator.go
Original file line number Diff line number Diff line change
@@ -1,22 +1,15 @@
package gomethods

import (
//"bytes"
"bufio"
"database/sql/driver"
"fmt"
"github.com/dimag-jfrog/migrate/driver"
"github.com/dimag-jfrog/migrate/file"
"os"
"path"
"strings"
)

type UnregisteredMethodsReceiverError string

func (e UnregisteredMethodsReceiverError) Error() string {
return "Unregistered methods receiver: " + string(e)
}

type MissingMethodError string

func (e MissingMethodError) Error() string { return "Non existing migrate method: " + string(e) }
Expand All @@ -37,13 +30,16 @@ func (e *MethodInvocationFailedError) Error() string {
}

type MigrationMethodInvoker interface {
IsValid(methodName string, methodReceiver interface{}) bool
Invoke(methodName string, methodReceiver interface{}) error
IsValid(methodName string) bool
Invoke(methodName string) error
}

type GoMethodsDriver interface {
driver.Driver

MigrationMethodInvoker
MethodsReceiver() interface{}
SetMethodsReceiver(r interface{}) error
}

type Migrator struct {
Expand All @@ -52,15 +48,15 @@ type Migrator struct {
}

func (m *Migrator) Migrate(f file.File, pipe chan interface{}) error {
methods, methodsReceiver, err := m.getMigrationMethods(f)
methods, err := m.getMigrationMethods(f)
if err != nil {
pipe <- err
return err
}

for i, methodName := range methods {
pipe <- methodName
err := m.MethodInvoker.Invoke(methodName, methodsReceiver)
err := m.MethodInvoker.Invoke(methodName)
if err != nil {
pipe <- err
if !m.RollbackOnFailure {
Expand All @@ -71,12 +67,12 @@ func (m *Migrator) Migrate(f file.File, pipe chan interface{}) error {
for j := i - 1; j >= 0; j-- {
rollbackToMethodName := getRollbackToMethod(methods[j])
if rollbackToMethodName == "" ||
!m.MethodInvoker.IsValid(rollbackToMethodName, methodsReceiver) {
!m.MethodInvoker.IsValid(rollbackToMethodName) {
continue
}

pipe <- rollbackToMethodName
err = m.MethodInvoker.Invoke(rollbackToMethodName, methodsReceiver)
err = m.MethodInvoker.Invoke(rollbackToMethodName)
if err != nil {
pipe <- err
break
Expand Down Expand Up @@ -122,20 +118,17 @@ func getFileLines(file file.File) ([]string, error) {
}
return lines, nil
} else {
//n := bytes.IndexByte(file.Content, 0)
//n := bytes.Index(file.Content, []byte{0})
//s := string(file.Content[:n])
s := string(file.Content)
return strings.Split(s, "\n"), nil
}
}

func (m *Migrator) getMigrationMethods(f file.File) (methods []string, methodsReceiver interface{}, err error) {
func (m *Migrator) getMigrationMethods(f file.File) (methods []string, err error) {
var lines []string

lines, err = getFileLines(f)
if err != nil {
return nil, nil, err
return nil, err
}

for _, line := range lines {
Expand All @@ -146,24 +139,14 @@ func (m *Migrator) getMigrationMethods(f file.File) (methods []string, methodsRe
continue
}

if methodsReceiver == nil {
receiverName := line
methodsReceiver = GetMethodsReceiver(receiverName)
if methodsReceiver == nil {
return nil, nil, UnregisteredMethodsReceiverError(receiverName)
}
continue

} else {
methodName := line
if !m.MethodInvoker.IsValid(methodName, methodsReceiver) {
return nil, nil, MissingMethodError(methodName)
}

methods = append(methods, methodName)
methodName := line
if !m.MethodInvoker.IsValid(methodName) {
return nil, MissingMethodError(methodName)
}

methods = append(methods, methodName)
}

return methods, methodsReceiver, nil
return methods, nil

}
13 changes: 2 additions & 11 deletions driver/gomethods/gomethods_migrator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@ type FakeGoMethodsInvoker struct {
InvokedMethods []string
}

func (invoker *FakeGoMethodsInvoker) IsValid(methodName string, methodReceiver interface{}) bool {
func (invoker *FakeGoMethodsInvoker) IsValid(methodName string) bool {
if methodName == "V001_some_non_existing_method_up" {
return false
}

return true
}

func (invoker *FakeGoMethodsInvoker) Invoke(methodName string, methodReceiver interface{}) error {
func (invoker *FakeGoMethodsInvoker) Invoke(methodName string) error {
invoker.InvokedMethods = append(invoker.InvokedMethods, methodName)

if methodName == "V001_some_failing_method_up" || methodName == "V001_some_failing_method_down" {
Expand Down Expand Up @@ -56,7 +56,6 @@ func TestMigrate(t *testing.T) {
Name: "foobar",
Direction: direction.Up,
Content: []byte(`
FakeMethodsReceiver
V001_init_organizations_up
V001_init_users_up
`),
Expand All @@ -73,7 +72,6 @@ func TestMigrate(t *testing.T) {
Name: "foobar",
Direction: direction.Down,
Content: []byte(`
FakeMethodsReceiver
V001_init_users_down
V001_init_organizations_down
`),
Expand All @@ -90,7 +88,6 @@ func TestMigrate(t *testing.T) {
Name: "foobar",
Direction: direction.Up,
Content: []byte(`
FakeMethodsReceiver
V001_init_organizations_up
V001_init_users_up
V001_some_non_existing_method_up
Expand All @@ -108,7 +105,6 @@ func TestMigrate(t *testing.T) {
Name: "foobar",
Direction: direction.Up,
Content: []byte(`
FakeMethodsReceiver
V001_init_organizations_up
V001_some_failing_method_up
V001_init_users_up
Expand All @@ -132,7 +128,6 @@ func TestMigrate(t *testing.T) {
Name: "foobar",
Direction: direction.Down,
Content: []byte(`
FakeMethodsReceiver
V001_init_users_down
V001_some_failing_method_down
V001_init_organizations_down
Expand All @@ -157,7 +152,6 @@ func TestMigrate(t *testing.T) {
Name: "foobar",
Direction: direction.Up,
Content: []byte(`
FakeMethodsReceiver
V001_init_organizations_up
V001_init_users_up
V001_some_failing_method_up
Expand Down Expand Up @@ -185,7 +179,6 @@ func TestMigrate(t *testing.T) {
Name: "foobar",
Direction: direction.Down,
Content: []byte(`
FakeMethodsReceiver
V001_init_users_down
V001_some_failing_method_down
V001_init_organizations_down
Expand All @@ -203,8 +196,6 @@ func TestMigrate(t *testing.T) {
},
}

RegisterMethodsReceiver("FakeMethodsReceiver", "")

for _, c := range cases {
migrator := Migrator{}
fakeInvoker := &FakeGoMethodsInvoker{InvokedMethods: []string{}}
Expand Down
37 changes: 23 additions & 14 deletions driver/gomethods/gomethods_registry.go
Original file line number Diff line number Diff line change
@@ -1,30 +1,39 @@
package gomethods

import (
"fmt"
"github.com/dimag-jfrog/migrate/driver"
"sync"
)

var methodsReceiversMu sync.Mutex
var methodsReceivers = make(map[string]interface{})

// Registers a methods receiver object so it can be created from its name. Users of gomethods migration drivers should
// call this method to register objects with their migration methods before executing the migration
func RegisterMethodsReceiver(name string, receiver interface{}) {
// Registers a methods receiver for go methods driver
// Users of gomethods migration drivers should call this method
// to register objects with their migration methods before executing the migration
func RegisterMethodsReceiverForDriver(driverName string, receiver interface{}) {
methodsReceiversMu.Lock()
defer methodsReceiversMu.Unlock()
if receiver == nil {
panic("Go methods: Register receiver object is nil")
}
if _, dup := methodsReceivers[name]; dup {
panic("Go methods: Register called twice for receiver " + name)

driver := driver.GetDriver(driverName)
if driver == nil {
panic("Go methods: Trying to register receiver for not registered driver " + driverName)
}
methodsReceivers[name] = receiver
}

// Retrieves a registered driver by name
func GetMethodsReceiver(name string) interface{} {
methodsReceiversMu.Lock()
defer methodsReceiversMu.Unlock()
receiver := methodsReceivers[name]
return receiver
methodsDriver, ok := driver.(GoMethodsDriver)
if !ok {
panic("Go methods: Trying to register receiver for non go methods driver " + driverName)
}

if methodsDriver.MethodsReceiver() != nil {
panic("Go methods: Methods receiver already registered for driver " + driverName)
}

if err := methodsDriver.SetMethodsReceiver(receiver); err != nil {
panic(fmt.Sprintf("Go methods: Failed to set methods receiver for driver %s\nError: %v",
driverName, err))
}
}
55 changes: 45 additions & 10 deletions driver/gomethods/mongodb/mongodb.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,46 @@ import (
"strings"
)

const MIGRATE_DB = "db_migrations"
type UnregisteredMethodsReceiverError string

func (e UnregisteredMethodsReceiverError) Error() string {
return "Unregistered methods receiver for driver: " + string(e)
}

type WrongMethodsReceiverTypeError string

func (e WrongMethodsReceiverTypeError) Error() string {
return "Wrong methods receiver type for driver: " + string(e)
}

const MIGRATE_C = "db_migrations"
const DRIVER_NAME = "gomethods.mongodb"

type MongoDbGoMethodsDriver struct {
Session *mgo.Session
DbName string

migrator gomethods.Migrator
methodsReceiver MethodsReceiver
migrator gomethods.Migrator
}

var _ gomethods.GoMethodsDriver = (*MongoDbGoMethodsDriver)(nil)

type MethodsReceiver interface {
DbName() string
}

func (d *MongoDbGoMethodsDriver) MethodsReceiver() interface{} {
return d.methodsReceiver
}

func (d *MongoDbGoMethodsDriver) SetMethodsReceiver(r interface{}) error {
r1, ok := r.(MethodsReceiver)
if !ok {
return WrongMethodsReceiverTypeError(DRIVER_NAME)
}

d.methodsReceiver = r1
return nil
}

func init() {
Expand All @@ -32,6 +64,10 @@ type DbMigration struct {
}

func (driver *MongoDbGoMethodsDriver) Initialize(url string) error {
if driver.methodsReceiver == nil {
return UnregisteredMethodsReceiverError(DRIVER_NAME)
}

urlWithoutScheme := strings.SplitN(url, "mongodb://", 2)
if len(urlWithoutScheme) != 2 {
return errors.New("invalid mongodb:// scheme")
Expand All @@ -44,7 +80,6 @@ func (driver *MongoDbGoMethodsDriver) Initialize(url string) error {
session.SetMode(mgo.Monotonic, true)

driver.Session = session
driver.DbName = MIGRATE_DB
driver.migrator = gomethods.Migrator{MethodInvoker: driver}

return nil
Expand All @@ -63,7 +98,7 @@ func (driver *MongoDbGoMethodsDriver) FilenameExtension() string {

func (driver *MongoDbGoMethodsDriver) Version() (uint64, error) {
var latestMigration DbMigration
c := driver.Session.DB(driver.DbName).C(MIGRATE_C)
c := driver.Session.DB(driver.methodsReceiver.DbName()).C(MIGRATE_C)

err := c.Find(bson.M{}).Sort("-version").One(&latestMigration)

Expand All @@ -85,7 +120,7 @@ func (driver *MongoDbGoMethodsDriver) Migrate(f file.File, pipe chan interface{}
return
}

migrate_c := driver.Session.DB(driver.DbName).C(MIGRATE_C)
migrate_c := driver.Session.DB(driver.methodsReceiver.DbName()).C(MIGRATE_C)

if f.Direction == direction.Up {
id := bson.NewObjectId()
Expand All @@ -106,13 +141,13 @@ func (driver *MongoDbGoMethodsDriver) Migrate(f file.File, pipe chan interface{}
}
}

func (driver *MongoDbGoMethodsDriver) IsValid(methodName string, methodsReceiver interface{}) bool {
return reflect.ValueOf(methodsReceiver).MethodByName(methodName).IsValid()
func (driver *MongoDbGoMethodsDriver) IsValid(methodName string) bool {
return reflect.ValueOf(driver.methodsReceiver).MethodByName(methodName).IsValid()
}

func (driver *MongoDbGoMethodsDriver) Invoke(methodName string, methodsReceiver interface{}) error {
func (driver *MongoDbGoMethodsDriver) Invoke(methodName string) error {
name := methodName
migrateMethod := reflect.ValueOf(methodsReceiver).MethodByName(name)
migrateMethod := reflect.ValueOf(driver.methodsReceiver).MethodByName(name)
if !migrateMethod.IsValid() {
return gomethods.MissingMethodError(methodName)
}
Expand Down
Loading

0 comments on commit 866ff82

Please sign in to comment.