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 }