github.com/gocaveman/caveman@v0.0.0-20191211162744-0ddf99dbdf6e/gen/store.go (about)

     1  package gen
     2  
     3  import (
     4  	"flag"
     5  )
     6  
     7  func init() {
     8  	globalMapGenerator["store"] = GeneratorFunc(func(s *Settings, name string, args ...string) error {
     9  
    10  		fset := flag.NewFlagSet("gen", flag.ContinueOnError)
    11  		storeName := fset.String("name", "Store", "The name of the store struct to define")
    12  		// tableMarshal := fset.Bool("table-marshal", false, "Adds functionality for marshaling tables to/from a flat files.")
    13  		targetFile, data, err := ParseFlagsAndOneFile(s, fset, args)
    14  		if err != nil {
    15  			return err
    16  		}
    17  
    18  		data["StoreName"] = *storeName
    19  
    20  		// FIXME: we should probably add a test file and make it define the environment
    21  		// in which this store (and the other store-crud stuif) is tested.  For example,
    22  		// some people will want to run the tests against their MySQL database, others
    23  		// might be fine with the default sqlite stuff, but either way you definitely don't
    24  		// want to have to set it up separately for each model object, make more sense
    25  		// at the store level.
    26  
    27  		return OutputGoSrcTemplate(s, data, targetFile, `
    28  package {{.PackageName}}
    29  
    30  import (
    31  	"github.com/gocaveman/caveman/migrate"
    32  	"github.com/gocaveman/tmeta"
    33  )
    34  
    35  {{/*
    36  // Default{{.StoreName}} is the default instance that is registered with autowire.
    37  var Default{{.StoreName}} *{{.StoreName}} = New{{.StoreName}}("sqlite3", nil)
    38  
    39  // Default{{.StoreName}}Migrations is all of our migrations for this store.
    40  var Default{{.StoreName}}Migrations migrate.MigrationList
    41  
    42  func init() {
    43  	autowire.ProvideAndPopulate("", Default{{.StoreName}})
    44  }
    45  
    46  func New{{.StoreName}}(driverName string, db *sql.DB) *{{.StoreName}} {
    47  	return &{{.StoreName}}{
    48  		DriverName: driverName,
    49  		DB: db,
    50  		Meta: tmeta.NewMeta(),
    51  	}
    52  }
    53  */}}
    54  
    55  // {{.StoreName}} provides database storage methods.
    56  type {{.StoreName}} struct {
    57  	DriverName string {{bq "autowire:\"driver_name\""}}
    58  	*sql.DB {{bq "autowire:\"\""}}
    59  	*tmeta.Meta {{bq "autowire:\"\""}}
    60  	dbrc *dbr.Connection
    61  	EventReceiver dbr.EventReceiver {{bq "autowire:\"\""}} // if non-nil will be used to log the SQL that gets executed
    62  }
    63  
    64  {{/*
    65  func (s *{{.StoreName}}) dbrConn() *dbr.Connection {
    66  	s.dbrmu.RLock()
    67  	dbrc := s.dbrc
    68  	s.dbrmu.RUnlock()
    69  
    70  	if dbrc == nil {
    71  		return dbrc
    72  	}
    73  
    74  	s.dbrmu.Lock()
    75  	defer s.dbrmu.Unlock()
    76  
    77  	dbrc = &dbr.Connection{
    78  		DB: s.DB,
    79      	EventReceiver: &dbr.NullEventReceiver{},
    80  	}
    81  	switch s.DriverName {
    82  	case "sqlite3":
    83  		dbrc.Dialect = dialect.SQLite3
    84  	case "mysql":
    85  		dbrc.Dialect = dialect.MySQL
    86  	case "postgres":
    87  		dbrc.Dialect = dialect.PostgreSQL
    88  	default:
    89  		panic("unknown driver: "+s.DriverName)
    90  	}
    91  
    92  	s.dbrc = dbrc
    93  
    94  	return dbrc
    95  }
    96  */}}
    97  
    98  // AfterWire is called by autowire or invoked manually and is needed to complete store setup before use.
    99  func (s *{{.StoreName}}) AfterWire() error {
   100  
   101  	if s.Meta == nil {
   102  		return fmt.Errorf("Meta is nil, please provide one")
   103  	}
   104  
   105  	// begin meta type init
   106  
   107  	// end meta type init
   108  
   109  	s.dbrc = &dbr.Connection{
   110  		DB: s.DB,
   111      	EventReceiver: &dbr.NullEventReceiver{},
   112  	}
   113  	switch s.DriverName {
   114  	case "sqlite3":
   115  		s.dbrc.Dialect = dialect.SQLite3
   116  	case "mysql":
   117  		s.dbrc.Dialect = dialect.MySQL
   118  	case "postgres":
   119  		s.dbrc.Dialect = dialect.PostgreSQL
   120  	default:
   121  		return fmt.Errorf("unknown driver: %q", s.DriverName)
   122  	}
   123  
   124  	return nil
   125  }
   126  
   127  {{/* HOLD ON THIS FOR NOW - LET'S GET A REAL USE CASE AND SEE HOW IMPORTANT
   128       IT IS TO INCLUDE THIS IN THE TEMPLATE AS AN OPTION.
   129  
   130  func (s *{{.StoreName}}) loadFlatFile() error {
   131  	// If enabled...
   132  }
   133  
   134  // TODO: we also need the cmdline flag and option(s) to put in the calls 
   135  // to loadFlatFile and storeFlatFile in the individual store-crud stuff,
   136  // at the appropriate points.
   137  
   138  // TODO: what happens if we externalize this and make it an interface, similar
   139  // to how controllers have callbacks for things, we could make a callback mechanism
   140  // that gets plugged into in order to implement the flat file persistence.
   141  // FlatFileLoader, FlatFileStorer, FlatFileLoadStorer - hm, but something needs
   142  // to know which table(s) and the connection, etc.  Seems like the coupling may be too
   143  // tight for this approach to make sense.  Possibly better to just provide a utility
   144  // function to do the dirty work with one method call, and thus making it easy to
   145  // implement loadFlatFile and storeFlatFile.  Think through more...
   146  
   147  // TODO: we also should have a mode which is read-only, storeFlatFile() doesn't get
   148  // called (or is a nop).  Possibly having a flatFileReadOnly flag would be applicable
   149  // in these cases.
   150  
   151  func (s *{{.StoreName}}) storeFlatFile() error {
   152  	// If enabled, write the specified types (from command line flags, checked for in TableInfoMap)
   153  	// out to whatever mechanism we have.
   154  
   155  	// what about: (both being present enables)
   156  	// flatFileFS FileSystem
   157  	// flatFilePath string
   158  
   159  }
   160  
   161  */}}
   162  
   163  `, false)
   164  
   165  	})
   166  }