go-on-rails 0.3.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9440dee24a0661bf808bf44778dd5d9d95671a9c
4
- data.tar.gz: ca2e1a666a7650cb3dd695cb97ccf1e00797cf2d
3
+ metadata.gz: 1300efab528e68b02247041903cca19315b3dd3d
4
+ data.tar.gz: 64888a3675703678a9c470a993a41296ca7c0535
5
5
  SHA512:
6
- metadata.gz: e69cb0445523bbb0a172b47cdf319f14d72196621b85240479727edfb416a9b2887089ee748521da03d7442ce93e332481145781db25533955a3fd75c45f4a6b
7
- data.tar.gz: fb805efaf9e5ad7b66d523f0c4b303e99f09f1ffa6cea6b6e94b6d5510c521348b2f507d39d1c14bd3660a1726fbbcbf731a8d87e6a1dee4cfe38097e49c8c09
6
+ metadata.gz: ef7e09edb3d98d4f81b70037f4c5c41b0306542ca4e633bd98fd2bc5e7404aabe51b793a06101ad68389329149b183725b4dd11cde4e59fb66714997b04aee5c
7
+ data.tar.gz: 9759b05c07feef1658c603d63a20ad668e520da32af309683b0044a382a56850c936c08ae8fa1ad4a414c778427fbd5147133678920a707936d64dad67b7da30
data/README.md CHANGED
@@ -1,12 +1,16 @@
1
+ <p align="center">
2
+ <img width="260" height="260" src="./go-on-rails.png">
3
+ </p>
4
+
1
5
  [![Gem Version](https://badge.fury.io/rb/go-on-rails.svg)](https://badge.fury.io/rb/go-on-rails)
2
- [![Build Status](https://travis-ci.org/goonr/go-on-rails.svg?branch=dev)](https://travis-ci.org/goonr/go-on-rails)
3
- [![Maintainability](https://api.codeclimate.com/v1/badges/6fba1f226f027a14c19b/maintainability)](https://codeclimate.com/github/goonr/go-on-rails/maintainability)
6
+ [![Build Status](https://travis-ci.org/railstack/go-on-rails.svg?branch=dev)](https://travis-ci.org/railstack/go-on-rails)
7
+ [![Maintainability](https://api.codeclimate.com/v1/badges/6fba1f226f027a14c19b/maintainability)](https://codeclimate.com/github/railstack/go-on-rails/maintainability)
8
+ [![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/railstack/go-on-rails/blob/master/MIT-LICENSE)
4
9
 
5
- <img align="right" width="260" height="260" src="./go-on-rails.png">
6
10
 
7
11
  # Go on Rails
8
12
 
9
- [中文文档](./README_zh.md)
13
+ [用 Rails 开发 Go 应用:中文文档](./README_zh.md)
10
14
 
11
15
  `go-on-rails` is a Rails generator aims to:
12
16
 
@@ -15,9 +19,9 @@
15
19
  3. Convert a *not very complicated* Rails app to Golang equivalent
16
20
 
17
21
  Here's some examples:
18
- * A simple [example(tutorial)](https://github.com/goonr/example_simple) on the basic usage of go-on-rails generator
19
- * [An advanced example](https://github.com/goonr/example_with_admin) shows how to integrate Go APIs in a Rails project
20
- * [Another example](https://github.com/goonr/example_read_rails_session) shows how to handle a Rails session to get an user's info in a go-on-rails generated Go API
22
+ * A simple [example(tutorial)](https://github.com/railstack/example_simple) on the basic usage of go-on-rails generator
23
+ * [An advanced example](https://github.com/railstack/example_with_admin) shows how to integrate Go APIs in a Rails project
24
+ * [Another example](https://github.com/railstack/example_read_rails_session) shows how to handle a Rails session to get an user's info in a go-on-rails generated Go API
21
25
 
22
26
  ## Prerequisites
23
27
 
@@ -48,10 +52,16 @@ You must have an existed Rails app or to create a new one before you try go-on-r
48
52
  After that you can run the command just as other Rails generators:
49
53
 
50
54
  ```bash
51
- bundle exec rails g gor [dev(elopment) | pro(duction) | test] [-m model_a model_b model_c ...]
55
+ rails g gor [dev(elopment) | pro(duction) | test | ...] [-m model_a model_b model_c ...]
52
56
 
53
57
  # OR (on rails version < 5.0)
54
- bundle exec rake g gor ...
58
+ rake g gor ...
59
+ ```
60
+
61
+ here we take generating all models for the `development` environment for example:
62
+
63
+ ```bash
64
+ rails g gor dev
55
65
  ```
56
66
 
57
67
  Then a folder named `go_app` that includes Golang codes will be generated under your Rails app root path.
@@ -59,7 +69,7 @@ Then a folder named `go_app` that includes Golang codes will be generated under
59
69
  Install the dependent Golang packages for this Go project:
60
70
 
61
71
  ```bash
62
- bundle exec rails gor:deps
72
+ rails gor:deps
63
73
  ```
64
74
 
65
75
  Then change to the `go_app` directory and run:
@@ -73,7 +83,7 @@ You can visit the page in http://localhost:4000 by default.
73
83
  More command details about go-on-rails generator:
74
84
 
75
85
  ```bash
76
- bundle exec rails g gor --help
86
+ rails g gor --help
77
87
  ```
78
88
 
79
89
  ## What will be generated?
@@ -92,7 +102,7 @@ You can view the godoc page of all functions in http://localhost:7979/doc/models
92
102
  bundle exec rails gor:doc
93
103
  ```
94
104
 
95
- Besides, there's [a sample project](https://github.com/goonr/gor_models_sample) generated by go-on-rails, you can view its [godoc](https://godoc.org/github.com/goonr/gor_models_sample) at godoc.org just now to get a picture of what functions are generated.
105
+ Besides, there's [a sample project](https://github.com/railstack/gor_models_sample) generated by go-on-rails, you can view its [godoc](https://godoc.org/github.com/railstack/gor_models_sample) at godoc.org just now to get a picture of what functions are generated.
96
106
 
97
107
 
98
108
  ## Known issues and TODOs
@@ -100,7 +110,7 @@ Besides, there's [a sample project](https://github.com/goonr/gor_models_sample)
100
110
  The gem is still under development, so there're some known issues.
101
111
 
102
112
  * databases specific functions between MySQL and Postgres or other databases are not covered yet
103
- * sql.NullType not supported yet, so you'd better in the migrations set those columns "not null" with a default value that's consistent with Golang's zero value specification, such as "" default for string and text typed column, and 0 default for int, etc. And now we have an alternative approch for manipulating the database nullable fields, see the wiki [Working with database nullable fields](https://github.com/goonr/go-on-rails/wiki/Working-with-database-nullable-fields)
113
+ * sql.NullType not supported yet, so you'd better in the migrations set those columns "not null" with a default value that's consistent with Golang's zero value specification, such as "" default for string and text typed column, and 0 default for int, etc. And now we have an alternative approch for manipulating the database nullable fields, see the wiki [Working with database nullable fields](https://github.com/railstack/go-on-rails/wiki/Working-with-database-nullable-fields)
104
114
 
105
115
  - [x] Associations
106
116
  - [x] has_many
@@ -113,21 +123,21 @@ The gem is still under development, so there're some known issues.
113
123
  - [x] format(string only)
114
124
  - [x] numericality(partially)
115
125
  - [ ] other validations
116
- - [x] Pagination(details see [wiki](https://github.com/goonr/go-on-rails/wiki/Pagination))
126
+ - [x] Pagination(details see [wiki](https://github.com/railstack/go-on-rails/wiki/Pagination))
117
127
  - [ ] Callbacks
118
128
  - [ ] Transactions
119
129
 
120
130
  ## Wiki
121
131
 
122
- * [Built-in Pagination](https://github.com/goonr/go-on-rails/wiki/Pagination)
123
- * [Working with database nullable fields](https://github.com/goonr/go-on-rails/wiki/Working-with-database-nullable-fields)
124
- * [Some Make commands](https://github.com/goonr/go-on-rails/wiki/Some-Make-commands)
125
- * [Dockerize a Go-on-Rails application](https://github.com/goonr/go-on-rails/wiki/Dockerize-a-Go-on-Rails-application)
132
+ * [Built-in Pagination](https://github.com/railstack/go-on-rails/wiki/Pagination)
133
+ * [Working with database nullable fields](https://github.com/railstack/go-on-rails/wiki/Working-with-database-nullable-fields)
134
+ * [Some Make commands](https://github.com/railstack/go-on-rails/wiki/Some-Make-commands)
135
+ * [Dockerize a Go-on-Rails application](https://github.com/railstack/go-on-rails/wiki/Dockerize-a-Go-on-Rails-application)
126
136
 
127
137
  ## Golang dependencies by default
128
138
 
129
139
  * [github.com/jmoiron/sqlx](https://github.com/jmoiron/sqlx): an extension on the standard `database/sql` database API library
130
- * [github.com/goonr/go-sqlite3](https://github.com/goonr/go-sqlite3): a SQLite driver(This's a forked version of [mattn/go-sqlite3](https://github.com/mattn/go-sqlite3), and This is [why we use this forked version](https://github.com/mattn/go-sqlite3/pull/468))
140
+ * [github.com/railstack/go-sqlite3](https://github.com/railstack/go-sqlite3): a SQLite driver(This's a forked version of [mattn/go-sqlite3](https://github.com/mattn/go-sqlite3), and This is [why we use this forked version](https://github.com/mattn/go-sqlite3/pull/468))
131
141
  * [github.com/go-sql-driver/mysql](https://github.com/go-sql-driver/mysql): a MySQL driver
132
142
  * [github.com/lib/pq](https://github.com/lib/pq): a PostgreSQL driver
133
143
  * [github.com/asaskevich/govalidator](https://github.com/asaskevich/govalidator): for the struct validation
@@ -162,4 +172,4 @@ Run `rails db:seed` to use the data defined in `db/seeds.rb`. And change to `go_
162
172
 
163
173
  ## License
164
174
 
165
- See the [LICENSE](https://github.com/goonr/go-on-rails/blob/master/MIT-LICENSE) file.
175
+ See the [LICENSE](https://github.com/railstack/go-on-rails/blob/master/MIT-LICENSE) file.
@@ -1,30 +1,32 @@
1
1
  Description:
2
- generate Golang app by Rails models: use Rails as a model and migration tool to build a Golang app, or migrate some of existed Rails APIs to Golang to decouple and high-perform your services.
2
+ Generate Golang app by Rails models: use Rails as a model and migration tool to build a Golang app, or migrate some of existed Rails APIs to Golang to decouple and high-perform your services.
3
3
 
4
- read more: https://github.com/gingerhot/goonrails
4
+ Read more: https://github.com/railstack/go-on-rails
5
5
 
6
6
 
7
7
  Example:
8
8
  rails generate gor development -m user post
9
9
 
10
- Here are valid ENV_NAME arguments: [dev, development, pro, production, test], the default ENV_NAME is "development" if you omit it.
10
+ Here are valid ENV_NAME arguments by default for a Rails generated database configuration by default: [dev, development, pro, production, test]. The default ENV_NAME is "development" if you omit one. Obviously, you can reconfig the config/database.yml to define your environments.
11
11
 
12
12
  So you can also run:
13
13
 
14
14
  rails g gor dev -m user post
15
15
 
16
16
  This will mainly create:
17
+ go_app/Dockerfile
18
+ go_app/Makefile
17
19
  go_app/main.go
18
- go_app/db/conn.go
19
- go_app/models/user/gor_user.go
20
- go_app/models/user/gor_post.go
20
+ go_app/models/doc/models.html
21
+ go_app/models/db.go
22
+ go_app/models/gor_user.go
23
+ go_app/models/gor_post.go
21
24
 
22
25
  Notice: If you omit the -m option, all the models of the Rails app will be generated.
23
26
 
24
- So if you omit all the options, just run:
27
+ If you omit all the options, just run:
25
28
 
26
29
  rails g gor
27
30
 
28
- This will generate all the models and database connection with Rails development env params.
29
-
31
+ This will generate code of all the models and database connection in Rails development environment.
30
32
 
@@ -16,7 +16,7 @@ module GoOnRails
16
16
  }.freeze
17
17
 
18
18
  # COALESCE datetime typed field for different databases
19
- # sqlite3 is dependent on the driver: https://github.com/goonr/go-sqlite3, details see: https://github.com/mattn/go-sqlite3/pull/468
19
+ # sqlite3 is dependent on the driver: https://github.com/railstack/go-sqlite3, details see: https://github.com/mattn/go-sqlite3/pull/468
20
20
  DATETIME_COALESCE_MAP = {
21
21
  "sqlite3" => "CAST(COALESCE(%s, '0001-01-01T00:00:00Z') as text) AS %s",
22
22
  "mysql" => "COALESCE(%s, CONVERT_TZ('0001-01-01 00:00:00','+00:00','UTC')) AS %s",
@@ -50,11 +50,17 @@ class GorGenerator < Rails::Generators::Base
50
50
  # iterate the structs info to generate codes for each model
51
51
  @all_structs_info.each do |k, v|
52
52
  @model_name, @struct_info = k, v
53
- template "gor_model.go.erb", "go_app/models/gor_#{@model_name.underscore}.go"
53
+ if @db_config[:driver_name] == "postgres"
54
+ template "gor_model_postgres.go.erb", "go_app/models/gor_#{@model_name.underscore}.go"
55
+ else
56
+ template "gor_model_mysql.go.erb", "go_app/models/gor_#{@model_name.underscore}.go"
57
+ end
54
58
  end
55
59
 
56
60
  # generate program for database connection
57
61
  template "db.go.erb", "go_app/models/db.go"
62
+ # and utils
63
+ template "utils.go.erb", "go_app/models/utils.go"
58
64
 
59
65
  unless options[:only_models]
60
66
  # generate the main.go
@@ -91,7 +97,7 @@ class GorGenerator < Rails::Generators::Base
91
97
  when "sqlite3"
92
98
  @db_config[:driver_name] = "sqlite3"
93
99
  @db_config[:dsn] = Rails.root.join(@db_config[:database]).to_s
94
- @db_config[:driver_package] = "_ \"github.com/goonr/go-sqlite3\""
100
+ @db_config[:driver_package] = "_ \"github.com/railstack/go-sqlite3\""
95
101
  when "mysql2"
96
102
  @db_config[:driver_name] = "mysql"
97
103
  @db_config[:port] ||= "3306"
@@ -6,7 +6,7 @@ FROM golang:1.9.2 as builder
6
6
  WORKDIR /root/
7
7
  COPY . /root/
8
8
  <%- if %w[127.0.0.1 localhost].include? @db_config[:host] and @db_config[:driver_name] != "sqlite3" -%>
9
- RUN perl -pi -e "s/tcp\(.*?:/tcp\(database:/; s/host=\S+? /host=database /" models/db.go
9
+ RUN perl -pi -e "s/tcp\(.*?:/tcp\(db:/; s/host=\S+? /host=db /" models/db.go
10
10
  <%- end -%>
11
11
  RUN make deps
12
12
  RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o myapp .
@@ -15,13 +15,13 @@ build: $(MYAPP)
15
15
  deps:
16
16
  $(GO) get -u github.com/jmoiron/sqlx \
17
17
  github.com/gin-gonic/gin \
18
- github.com/goonr/go-sqlite3 \
18
+ github.com/railstack/go-sqlite3 \
19
19
  github.com/go-sql-driver/mysql \
20
20
  github.com/lib/pq \
21
21
  github.com/asaskevich/govalidator
22
22
 
23
23
  test:
24
- $(GO) test -v ./models
24
+ $(GO) test -v ./...
25
25
 
26
26
  run: $(MYAPP)
27
27
  ./$(MYAPP)
@@ -3,7 +3,7 @@ version: '3'
3
3
  services:
4
4
  <%- if %w[127.0.0.1 localhost].include? @db_config[:host] and @db_config[:driver_name] != "sqlite3" -%>
5
5
  # generated according to the config/database.yml
6
- database:
6
+ db:
7
7
  image: <%= @db_config[:driver_name] %>
8
8
  ports:
9
9
  - '<%= @db_config[:port] %>:<%= @db_config[:port] %>'
@@ -18,7 +18,7 @@ services:
18
18
  - "3000:3000"
19
19
  <%- if %w[127.0.0.1 localhost].include? @db_config[:host] and @db_config[:driver_name] != "sqlite3" -%>
20
20
  depends_on:
21
- - database
21
+ - db
22
22
  <%- end -%>
23
23
 
24
24
  # golang app part. It depends on the rails_app to initialize the database
@@ -32,6 +32,6 @@ services:
32
32
  - "4000:4000"
33
33
  depends_on:
34
34
  <%- if %w[127.0.0.1 localhost].include? @db_config[:host] and @db_config[:driver_name] != "sqlite3" -%>
35
- - database
35
+ - db
36
36
  <%- end -%>
37
37
  - rails_app
@@ -1,5 +1,5 @@
1
1
  <%#- var_pmn stands for pluralized model name, its format is same as the table name -#%>
2
- <%- var_pmn = table_name = @model_name.underscore.pluralize -%>
2
+ <%- var_pmn = table_name = @model_name.tableize -%>
3
3
  <%#- var_umn stands for underscored model name -#%>
4
4
  <%- var_umn = @model_name.underscore -%>
5
5
  <%- fields = @struct_info[:select_fields] -%>
@@ -57,11 +57,7 @@ func (_p *<%= @model_name %>Page) Current() ([]<%= @model_name %>, error) {
57
57
  _p.buildOrder()
58
58
  }
59
59
  idStr, idParams := _p.buildIdRestrict("current")
60
- <% if @db_config[:driver_name] == "postgres" %>
61
- whereStr := fmt.Sprintf("%s %s %s FETCH NEXT %v ROWS ONLY", _p.WhereString, idStr, _p.orderStr, _p.PerPage)
62
- <% else %>
63
60
  whereStr := fmt.Sprintf("%s %s %s LIMIT %v", _p.WhereString, idStr, _p.orderStr, _p.PerPage)
64
- <% end %>
65
61
  whereParams := []interface{}{}
66
62
  whereParams = append(append(whereParams, _p.WhereParams...), idParams...)
67
63
  <%= var_pmn %>, err := Find<%= @model_name.pluralize %>Where(whereStr, whereParams...)
@@ -90,11 +86,7 @@ func (_p *<%= @model_name %>Page) Previous() ([]<%= @model_name %>, error) {
90
86
  _p.buildOrder()
91
87
  }
92
88
  idStr, idParams := _p.buildIdRestrict("previous")
93
- <% if @db_config[:driver_name] == "postgres" %>
94
- whereStr := fmt.Sprintf("%s %s %s FETCH NEXT %v ROWS ONLY", _p.WhereString, idStr, _p.orderStr, _p.PerPage)
95
- <% else %>
96
89
  whereStr := fmt.Sprintf("%s %s %s LIMIT %v", _p.WhereString, idStr, _p.orderStr, _p.PerPage)
97
- <% end %>
98
90
  whereParams := []interface{}{}
99
91
  whereParams = append(append(whereParams, _p.WhereParams...), idParams...)
100
92
  <%= var_pmn %>, err := Find<%= @model_name.pluralize %>Where(whereStr, whereParams...)
@@ -124,11 +116,7 @@ func (_p *<%= @model_name %>Page) Next() ([]<%= @model_name %>, error) {
124
116
  _p.buildOrder()
125
117
  }
126
118
  idStr, idParams := _p.buildIdRestrict("next")
127
- <% if @db_config[:driver_name] == "postgres" %>
128
- whereStr := fmt.Sprintf("%s %s %s FETCH NEXT %v ROWS ONLY", _p.WhereString, idStr, _p.orderStr, _p.PerPage)
129
- <% else %>
130
119
  whereStr := fmt.Sprintf("%s %s %s LIMIT %v", _p.WhereString, idStr, _p.orderStr, _p.PerPage)
131
- <% end %>
132
120
  whereParams := []interface{}{}
133
121
  whereParams = append(append(whereParams, _p.WhereParams...), idParams...)
134
122
  <%= var_pmn %>, err := Find<%= @model_name.pluralize %>Where(whereStr, whereParams...)
@@ -593,15 +581,10 @@ func Create<%= @model_name %>(am map[string]interface{}) (int64, error) {
593
581
  }
594
582
  }
595
583
  <%- end -%>
596
- keys := make([]string, len(am))
597
- i := 0
598
- for k := range am {
599
- keys[i] = k
600
- i++
601
- }
584
+ keys := allKeys(am)
602
585
  sqlFmt := `INSERT INTO <%= table_name %> (%s) VALUES (%s)`
603
- sqlStr := fmt.Sprintf(sqlFmt, strings.Join(keys, ","), ":"+strings.Join(keys, ",:"))
604
- result, err := DB.NamedExec(sqlStr, am)
586
+ sql := fmt.Sprintf(sqlFmt, strings.Join(keys, ","), ":"+strings.Join(keys, ",:"))
587
+ result, err := DB.NamedExec(sql, am)
605
588
  if err != nil {
606
589
  log.Println(err)
607
590
  return 0, err
@@ -940,12 +923,7 @@ func Update<%= @model_name %>(id int64, am map[string]interface{}) error {
940
923
  <%- if @struct_info[:timestamp_cols].include? "updated_at" -%>
941
924
  am["updated_at"] = time.Now()
942
925
  <%- end -%>
943
- keys := make([]string, len(am))
944
- i := 0
945
- for k := range am {
946
- keys[i] = k
947
- i++
948
- }
926
+ keys := allKeys(am)
949
927
  sqlFmt := `UPDATE <%= table_name %> SET %s WHERE id = %v`
950
928
  setKeysArr := []string{}
951
929
  for _,v := range keys {
@@ -994,10 +972,6 @@ func Update<%= @model_name.pluralize %>BySql(sql string, args ...interface{}) (i
994
972
  if sql == "" {
995
973
  return 0, errors.New("A blank SQL clause")
996
974
  }
997
- <%- if @struct_info[:timestamp_cols].include? "updated_at" -%>
998
- sql = strings.Replace(strings.ToLower(sql), "set", "set updated_at = ?, ", 1)
999
- args = append([]interface{}{time.Now()}, args...)
1000
- <%- end -%>
1001
975
  stmt, err := DB.Preparex(DB.Rebind(sql))
1002
976
  result, err := stmt.Exec(args...)
1003
977
  if err != nil {
@@ -0,0 +1,988 @@
1
+ <%#- var_pmn stands for pluralized model name, its format is same as the table name -#%>
2
+ <%- var_pmn = table_name = @model_name.tableize -%>
3
+ <%#- var_umn stands for underscored model name -#%>
4
+ <%- var_umn = @model_name.underscore -%>
5
+ <%- fields = @struct_info[:select_fields] -%>
6
+ <%- col_names = @struct_info[:col_names] -%>
7
+ <%- has_assoc = !@struct_info[:assoc_info][:has_many].empty? || !@struct_info[:assoc_info][:has_one].empty? -%>
8
+ // Package models includes the functions on the model <%= @model_name %>.
9
+ package models
10
+
11
+ import (
12
+ "errors"
13
+ "fmt"
14
+ "log"
15
+ "math"
16
+ "strings"
17
+ <%- if @struct_info[:has_datetime_type] -%>
18
+ "time"
19
+ <%- end -%>
20
+
21
+ "github.com/asaskevich/govalidator"
22
+ )
23
+
24
+ // set flags to output more detailed log
25
+ func init() {
26
+ log.SetFlags(log.LstdFlags | log.Lshortfile)
27
+ }
28
+
29
+ type <%= @model_name %> struct {
30
+ <%= @struct_info[:struct_body] -%>
31
+ }
32
+
33
+ // DataStruct for the pagination
34
+ type <%= @model_name %>Page struct {
35
+ WhereString string
36
+ WhereParams []interface{}
37
+ Order map[string]string
38
+ FirstId int64
39
+ LastId int64
40
+ PageNum int
41
+ PerPage int
42
+ TotalPages int
43
+ TotalItems int64
44
+ orderStr string
45
+ }
46
+
47
+ // Current get the current page of <%= @model_name %>Page object for pagination.
48
+ func (_p *<%= @model_name %>Page) Current() ([]<%= @model_name %>, error) {
49
+ if _, exist := _p.Order["id"]; !exist {
50
+ return nil, errors.New("No id order specified in Order map")
51
+ }
52
+ err := _p.buildPageCount()
53
+ if err != nil {
54
+ return nil, fmt.Errorf("Calculate page count error: %v", err)
55
+ }
56
+ if _p.orderStr == "" {
57
+ _p.buildOrder()
58
+ }
59
+ idStr, idParams := _p.buildIdRestrict("current")
60
+ whereStr := fmt.Sprintf("%s %s %s FETCH NEXT %v ROWS ONLY", _p.WhereString, idStr, _p.orderStr, _p.PerPage)
61
+ whereParams := []interface{}{}
62
+ whereParams = append(append(whereParams, _p.WhereParams...), idParams...)
63
+ <%= var_pmn %>, err := Find<%= @model_name.pluralize %>Where(whereStr, whereParams...)
64
+ if err != nil {
65
+ return nil, err
66
+ }
67
+ if len(<%= var_pmn %>) != 0 {
68
+ _p.FirstId, _p.LastId = <%= var_pmn %>[0].Id, <%= var_pmn %>[len(<%= var_pmn %>)-1].Id
69
+ }
70
+ return <%= var_pmn %>, nil
71
+ }
72
+
73
+ // Previous get the previous page of <%= @model_name %>Page object for pagination.
74
+ func (_p *<%= @model_name %>Page) Previous() ([]<%= @model_name %>, error) {
75
+ if _p.PageNum == 0 {
76
+ return nil, errors.New("This's the first page, no previous page yet")
77
+ }
78
+ if _, exist := _p.Order["id"]; !exist {
79
+ return nil, errors.New("No id order specified in Order map")
80
+ }
81
+ err := _p.buildPageCount()
82
+ if err != nil {
83
+ return nil, fmt.Errorf("Calculate page count error: %v", err)
84
+ }
85
+ if _p.orderStr == "" {
86
+ _p.buildOrder()
87
+ }
88
+ idStr, idParams := _p.buildIdRestrict("previous")
89
+ whereStr := fmt.Sprintf("%s %s %s FETCH NEXT %v ROWS ONLY", _p.WhereString, idStr, _p.orderStr, _p.PerPage)
90
+ whereParams := []interface{}{}
91
+ whereParams = append(append(whereParams, _p.WhereParams...), idParams...)
92
+ <%= var_pmn %>, err := Find<%= @model_name.pluralize %>Where(whereStr, whereParams...)
93
+ if err != nil {
94
+ return nil, err
95
+ }
96
+ if len(<%= var_pmn %>) != 0 {
97
+ _p.FirstId, _p.LastId = <%= var_pmn %>[0].Id, <%= var_pmn %>[len(<%= var_pmn %>)-1].Id
98
+ }
99
+ _p.PageNum -= 1
100
+ return <%= var_pmn %>, nil
101
+ }
102
+
103
+ // Next get the next page of <%= @model_name %>Page object for pagination.
104
+ func (_p *<%= @model_name %>Page) Next() ([]<%= @model_name %>, error) {
105
+ if _p.PageNum == _p.TotalPages-1 {
106
+ return nil, errors.New("This's the last page, no next page yet")
107
+ }
108
+ if _, exist := _p.Order["id"]; !exist {
109
+ return nil, errors.New("No id order specified in Order map")
110
+ }
111
+ err := _p.buildPageCount()
112
+ if err != nil {
113
+ return nil, fmt.Errorf("Calculate page count error: %v", err)
114
+ }
115
+ if _p.orderStr == "" {
116
+ _p.buildOrder()
117
+ }
118
+ idStr, idParams := _p.buildIdRestrict("next")
119
+ whereStr := fmt.Sprintf("%s %s %s FETCH NEXT %v ROWS ONLY", _p.WhereString, idStr, _p.orderStr, _p.PerPage)
120
+ whereParams := []interface{}{}
121
+ whereParams = append(append(whereParams, _p.WhereParams...), idParams...)
122
+ <%= var_pmn %>, err := Find<%= @model_name.pluralize %>Where(whereStr, whereParams...)
123
+ if err != nil {
124
+ return nil, err
125
+ }
126
+ if len(<%= var_pmn %>) != 0 {
127
+ _p.FirstId, _p.LastId = <%= var_pmn %>[0].Id, <%= var_pmn %>[len(<%= var_pmn %>)-1].Id
128
+ }
129
+ _p.PageNum += 1
130
+ return <%= var_pmn %>, nil
131
+ }
132
+
133
+ // GetPage is a helper function for the <%= @model_name %>Page object to return a corresponding page due to
134
+ // the parameter passed in, i.e. one of "previous, current or next".
135
+ func (_p *<%= @model_name %>Page) GetPage(direction string) (ps []<%= @model_name %>, err error) {
136
+ switch direction {
137
+ case "previous":
138
+ ps, _ = _p.Previous()
139
+ case "next":
140
+ ps, _ = _p.Next()
141
+ case "current":
142
+ ps, _ = _p.Current()
143
+ default:
144
+ return nil, errors.New("Error: wrong dircetion! None of previous, current or next!")
145
+ }
146
+ return
147
+ }
148
+
149
+ // buildOrder is for <%= @model_name %>Page object to build a SQL ORDER BY clause.
150
+ func (_p *<%= @model_name %>Page) buildOrder() {
151
+ tempList := []string{}
152
+ for k, v := range _p.Order {
153
+ tempList = append(tempList, fmt.Sprintf("%v %v", k, v))
154
+ }
155
+ _p.orderStr = " ORDER BY " + strings.Join(tempList, ", ")
156
+ }
157
+
158
+ // buildIdRestrict is for <%= @model_name %>Page object to build a SQL clause for ID restriction,
159
+ // implementing a simple keyset style pagination.
160
+ func (_p *<%= @model_name %>Page) buildIdRestrict(direction string) (idStr string, idParams []interface{}) {
161
+ bindVarNumber := len(_p.WhereParams) + 1
162
+ switch direction {
163
+ case "previous":
164
+ if strings.ToLower(_p.Order["id"]) == "desc" {
165
+ idStr += fmt.Sprintf("id > $%v ", bindVarNumber)
166
+ idParams = append(idParams, _p.FirstId)
167
+ } else {
168
+ idStr += fmt.Sprintf("id < $%v ", bindVarNumber)
169
+ idParams = append(idParams, _p.FirstId)
170
+ }
171
+ case "current":
172
+ // trick to make Where function work
173
+ if _p.PageNum == 0 && _p.FirstId == 0 && _p.LastId == 0 {
174
+ idStr += fmt.Sprintf("id > $%v ", bindVarNumber)
175
+ idParams = append(idParams, 0)
176
+ } else {
177
+ if strings.ToLower(_p.Order["id"]) == "desc" {
178
+ idStr += fmt.Sprintf("id <= $%v AND id >= $%v ", bindVarNumber, bindVarNumber + 1)
179
+ idParams = append(idParams, _p.FirstId, _p.LastId)
180
+ } else {
181
+ idStr += fmt.Sprintf("id >= $%v AND id <= $%v ", bindVarNumber, bindVarNumber + 1)
182
+ idParams = append(idParams, _p.FirstId, _p.LastId)
183
+ }
184
+ }
185
+ case "next":
186
+ if strings.ToLower(_p.Order["id"]) == "desc" {
187
+ idStr += fmt.Sprintf("id < $%v ", bindVarNumber)
188
+ idParams = append(idParams, _p.LastId)
189
+ } else {
190
+ idStr += fmt.Sprintf("id > $%v ", bindVarNumber)
191
+ idParams = append(idParams, _p.LastId)
192
+ }
193
+ }
194
+ if _p.WhereString != "" {
195
+ idStr = " AND " + idStr
196
+ }
197
+ return
198
+ }
199
+
200
+ // buildPageCount calculate the TotalItems/TotalPages for the <%= @model_name %>Page object.
201
+ func (_p *<%= @model_name %>Page) buildPageCount() error {
202
+ count, err := <%= @model_name %>CountWhere(_p.WhereString, _p.WhereParams...)
203
+ if err != nil {
204
+ return err
205
+ }
206
+ _p.TotalItems = count
207
+ if _p.PerPage == 0 {
208
+ _p.PerPage = 10
209
+ }
210
+ _p.TotalPages = int(math.Ceil(float64(_p.TotalItems) / float64(_p.PerPage)))
211
+ return nil
212
+ }
213
+
214
+
215
+ // Find<%= @model_name %> find a single <%= var_umn %> by an ID.
216
+ func Find<%= @model_name %>(id int64) (*<%= @model_name %>, error) {
217
+ if id == 0 {
218
+ return nil, errors.New("Invalid ID: it can't be zero")
219
+ }
220
+ _<%= var_umn %> := <%= @model_name %>{}
221
+ err := DB.Get(&_<%= var_umn %>, DB.Rebind(`SELECT <%= fields %> FROM <%= table_name %> WHERE <%= table_name %>.id = $1 LIMIT 1`), id)
222
+ if err != nil {
223
+ log.Printf("Error: %v\n", err)
224
+ return nil, err
225
+ }
226
+ return &_<%= var_umn %>, nil
227
+ }
228
+
229
+ // First<%= @model_name %> find the first one <%= var_umn %> by ID ASC order.
230
+ func First<%= @model_name %>() (*<%= @model_name %>, error) {
231
+ _<%= var_umn %> := <%= @model_name %>{}
232
+ err := DB.Get(&_<%= var_umn %>, DB.Rebind(`SELECT <%= fields %> FROM <%= table_name %> ORDER BY <%= table_name %>.id ASC LIMIT 1`))
233
+ if err != nil {
234
+ log.Printf("Error: %v\n", err)
235
+ return nil, err
236
+ }
237
+ return &_<%= var_umn %>, nil
238
+ }
239
+
240
+ // First<%= @model_name.pluralize %> find the first N <%= var_umn.pluralize %> by ID ASC order.
241
+ func First<%= @model_name.pluralize %>(n uint32) ([]<%= @model_name %>, error) {
242
+ _<%= var_pmn %> := []<%= @model_name %>{}
243
+ sql := fmt.Sprintf("SELECT <%= fields %> FROM <%= table_name %> ORDER BY <%= table_name %>.id ASC LIMIT %v", n)
244
+ err := DB.Select(&_<%= var_pmn %>, DB.Rebind(sql))
245
+ if err != nil {
246
+ log.Printf("Error: %v\n", err)
247
+ return nil, err
248
+ }
249
+ return _<%= var_pmn %>, nil
250
+ }
251
+
252
+ // Last<%= @model_name %> find the last one <%= var_umn %> by ID DESC order.
253
+ func Last<%= @model_name %>() (*<%= @model_name %>, error) {
254
+ _<%= var_umn %> := <%= @model_name %>{}
255
+ err := DB.Get(&_<%= var_umn %>, DB.Rebind(`SELECT <%= fields %> FROM <%= table_name %> ORDER BY <%= table_name %>.id DESC LIMIT 1`))
256
+ if err != nil {
257
+ log.Printf("Error: %v\n", err)
258
+ return nil, err
259
+ }
260
+ return &_<%= var_umn %>, nil
261
+ }
262
+
263
+ // Last<%= @model_name.pluralize %> find the last N <%= var_umn.pluralize %> by ID DESC order.
264
+ func Last<%= @model_name.pluralize %>(n uint32) ([]<%= @model_name %>, error) {
265
+ _<%= var_pmn %> := []<%= @model_name %>{}
266
+ sql := fmt.Sprintf("SELECT <%= fields %> FROM <%= table_name %> ORDER BY <%= table_name %>.id DESC LIMIT %v", n)
267
+ err := DB.Select(&_<%= var_pmn %>, DB.Rebind(sql))
268
+ if err != nil {
269
+ log.Printf("Error: %v\n", err)
270
+ return nil, err
271
+ }
272
+ return _<%= var_pmn %>, nil
273
+ }
274
+
275
+ // Find<%= @model_name.pluralize %> find one or more <%= var_umn.pluralize %> by the given ID(s).
276
+ func Find<%= @model_name.pluralize %>(ids ...int64) ([]<%= @model_name %>, error) {
277
+ if len(ids) == 0 {
278
+ msg := "At least one or more ids needed"
279
+ log.Println(msg)
280
+ return nil, errors.New(msg)
281
+ }
282
+ _<%= var_pmn %> := []<%= @model_name %>{}
283
+ idsHolder := buildIdsHolder(len(ids))
284
+ sql := DB.Rebind(fmt.Sprintf(`SELECT <%= fields %> FROM <%= table_name %> WHERE <%= table_name %>.id IN (%s)`, idsHolder))
285
+ idsT := []interface{}{}
286
+ for _,id := range ids {
287
+ idsT = append(idsT, interface{}(id))
288
+ }
289
+ err := DB.Select(&_<%= var_pmn %>, sql, idsT...)
290
+ if err != nil {
291
+ log.Printf("Error: %v\n", err)
292
+ return nil, err
293
+ }
294
+ return _<%= var_pmn %>, nil
295
+ }
296
+
297
+ // Find<%= @model_name %>By find a single <%= var_umn %> by a field name and a value.
298
+ func Find<%= @model_name %>By(field string, val interface{}) (*<%= @model_name %>, error) {
299
+ _<%= var_umn %> := <%= @model_name %>{}
300
+ sqlFmt := `SELECT <%= fields %> FROM <%= table_name %> WHERE %s = ? LIMIT 1`
301
+ sqlStr := fmt.Sprintf(sqlFmt, field)
302
+ err := DB.Get(&_<%= var_umn %>, DB.Rebind(sqlStr), val)
303
+ if err != nil {
304
+ log.Printf("Error: %v\n", err)
305
+ return nil, err
306
+ }
307
+ return &_<%= var_umn %>, nil
308
+ }
309
+
310
+ // Find<%= @model_name.pluralize %>By find all <%= var_pmn %> by a field name and a value.
311
+ func Find<%= @model_name.pluralize %>By(field string, val interface{}) (_<%= var_pmn %> []<%= @model_name %>, err error) {
312
+ sqlFmt := `SELECT <%= fields %> FROM <%= table_name %> WHERE %s = ?`
313
+ sqlStr := fmt.Sprintf(sqlFmt, field)
314
+ err = DB.Select(&_<%= var_pmn %>, DB.Rebind(sqlStr), val)
315
+ if err != nil {
316
+ log.Printf("Error: %v\n", err)
317
+ return nil, err
318
+ }
319
+ return _<%= var_pmn %>, nil
320
+ }
321
+
322
+ // All<%= @model_name.pluralize %> get all the <%= @model_name %> records.
323
+ func All<%= @model_name.pluralize %>() (<%= var_pmn %> []<%= @model_name %>, err error) {
324
+ err = DB.Select(&<%= var_pmn %>, "SELECT <%= fields %> FROM <%= table_name %>")
325
+ if err != nil {
326
+ log.Println(err)
327
+ return nil, err
328
+ }
329
+ return <%= var_pmn %>, nil
330
+ }
331
+
332
+ // <%= @model_name %>Count get the count of all the <%= @model_name %> records.
333
+ func <%= @model_name %>Count() (c int64, err error) {
334
+ err = DB.Get(&c, "SELECT count(*) FROM <%= table_name %>")
335
+ if err != nil {
336
+ log.Println(err)
337
+ return 0, err
338
+ }
339
+ return c, nil
340
+ }
341
+
342
+ // <%= @model_name %>CountWhere get the count of all the <%= @model_name %> records with a where clause.
343
+ func <%= @model_name %>CountWhere(where string, args ...interface{}) (c int64, err error) {
344
+ sql := "SELECT count(*) FROM <%= table_name %>"
345
+ if len(where) > 0 {
346
+ sql = sql + " WHERE " + where
347
+ }
348
+ stmt, err := DB.Preparex(DB.Rebind(sql))
349
+ if err != nil {
350
+ log.Println(err)
351
+ return 0, err
352
+ }
353
+ err = stmt.Get(&c, args...)
354
+ if err != nil {
355
+ log.Println(err)
356
+ return 0, err
357
+ }
358
+ return c, nil
359
+ }
360
+
361
+ // <%= @model_name %>IncludesWhere get the <%= @model_name %> associated models records, currently it's not same as the corresponding "includes" function but "preload" instead in Ruby on Rails. It means that the "sql" should be restricted on <%= @model_name %> model.
362
+ func <%= @model_name %>IncludesWhere(assocs []string, sql string, args ...interface{}) (_<%= var_pmn %> []<%= @model_name %>, err error) {
363
+ _<%= var_pmn %>, err = Find<%= @model_name.pluralize %>Where(sql, args...)
364
+ if err != nil {
365
+ log.Println(err)
366
+ return nil, err
367
+ }
368
+ if len(assocs) == 0 {
369
+ log.Println("No associated fields ard specified")
370
+ return _<%= var_pmn %>, err
371
+ }
372
+ if len(_<%= var_pmn %>) <= 0 {
373
+ return nil, errors.New("No results available")
374
+ }
375
+ ids := make([]interface{}, len(_<%= var_pmn %>))
376
+ for _, v := range _<%= var_pmn %> {
377
+ ids = append(ids, interface{}(v.Id))
378
+ }
379
+ <%- if has_assoc -%>
380
+ idsHolder := buildIdsHolder(len(ids))
381
+ for _, assoc := range assocs {
382
+ switch assoc {
383
+ <%- unless @struct_info[:assoc_info][:has_many].empty? -%>
384
+ <%- has_many = @struct_info[:assoc_info][:has_many] -%>
385
+ <%- has_many.each do |k, v| -%>
386
+ case "<%= k.underscore.pluralize %>":
387
+ <%- if v[:through] || v[:as] -%>
388
+ // FIXME: optimize the query
389
+ for i, vvv := range _<%= var_pmn %> {
390
+ _<%= k.underscore.pluralize %>, err := <%= @model_name %>Get<%= k.pluralize %>(vvv.Id)
391
+ if err != nil {
392
+ continue
393
+ }
394
+ vvv.<%= k %> = _<%= k.underscore.pluralize %>
395
+ _<%= var_pmn %>[i] = vvv
396
+ }
397
+ <%- else -%>
398
+ <%- if v[:foreign_key] -%>
399
+ where := fmt.Sprintf("<%= v[:foreign_key] %> IN (%s)", idsHolder)
400
+ <%- else -%>
401
+ where := fmt.Sprintf("<%= var_umn %>_id IN (%s)", idsHolder)
402
+ <%- end -%>
403
+ _<%= v[:class_name].underscore.pluralize %>, err := Find<%= v[:class_name].pluralize %>Where(where, ids...)
404
+ if err != nil {
405
+ log.Printf("Error when query associated objects: %v\n", assoc)
406
+ continue
407
+ }
408
+ for _, vv := range _<%= v[:class_name].underscore.pluralize %> {
409
+ for i, vvv := range _<%= var_pmn %> {
410
+ <%- if v[:foreign_key] -%>
411
+ if vv.<%= v[:foreign_key].to_s.camelize %> == vvv.Id {
412
+ vvv.<%= k %> = append(vvv.<%= k %>, vv)
413
+ }
414
+ <%- else -%>
415
+ if vv.<%= @model_name.camelize %>Id == vvv.Id {
416
+ vvv.<%= k %> = append(vvv.<%= k %>, vv)
417
+ }
418
+ <%- end -%>
419
+ _<%= var_pmn %>[i].<%= k %> = vvv.<%= k %>
420
+ }
421
+ }
422
+ <%- end -%>
423
+ <%- end -%>
424
+ <%- end -%>
425
+ <%- unless @struct_info[:assoc_info][:has_one].empty? -%>
426
+ <%- has_one = @struct_info[:assoc_info][:has_one] -%>
427
+ <%- has_one.each do |k, v| -%>
428
+ case "<%= k.underscore %>":
429
+ <%- if v[:foreign_key] -%>
430
+ where := fmt.Sprintf("<%= v[:foreign_key] %> IN (%s)", idsHolder)
431
+ <%- else -%>
432
+ where := fmt.Sprintf("<%= var_umn %>_id IN (%s)", idsHolder)
433
+ <%- end -%>
434
+ _<%= v[:class_name].underscore.pluralize %>, err := Find<%= v[:class_name].pluralize %>Where(where, ids...)
435
+ if err != nil {
436
+ log.Printf("Error when query associated objects: %v\n", assoc)
437
+ continue
438
+ }
439
+ for _, vv := range _<%= v[:class_name].underscore.pluralize %> {
440
+ for i, vvv := range _<%= var_pmn %> {
441
+ <%- if v[:foreign_key] -%>
442
+ if vv.<%= v[:foreign_key].to_s.camelize %> == vvv.Id {
443
+ vvv.<%= k %> = vv
444
+ }
445
+ <%- else -%>
446
+ if vv.<%= @model_name.camelize %>Id == vvv.Id {
447
+ vvv.<%= k %> = vv
448
+ }
449
+ <%- end -%>
450
+ _<%= var_pmn %>[i].<%= k %> = vvv.<%= k %>
451
+ }
452
+ }
453
+ <%- end -%>
454
+ <%- end -%>
455
+ }
456
+ }
457
+ <%- end -%>
458
+ return _<%= var_pmn %>, nil
459
+ }
460
+
461
+ // <%= @model_name %>Ids get all the IDs of <%= @model_name %> records.
462
+ func <%= @model_name %>Ids() (ids []int64, err error) {
463
+ err = DB.Select(&ids, "SELECT id FROM <%= table_name %>")
464
+ if err != nil {
465
+ log.Println(err)
466
+ return nil, err
467
+ }
468
+ return ids, nil
469
+ }
470
+
471
+ // <%= @model_name %>IdsWhere get all the IDs of <%= @model_name %> records by where restriction.
472
+ func <%= @model_name %>IdsWhere(where string, args ...interface{}) ([]int64, error) {
473
+ ids, err := <%= @model_name %>IntCol("id", where, args...)
474
+ return ids, err
475
+ }
476
+
477
+ // <%= @model_name %>IntCol get some int64 typed column of <%= @model_name %> by where restriction.
478
+ func <%= @model_name %>IntCol(col, where string, args ...interface{}) (intColRecs []int64, err error) {
479
+ sql := "SELECT " + col + " FROM <%= table_name %>"
480
+ if len(where) > 0 {
481
+ sql = sql + " WHERE " + where
482
+ }
483
+ stmt, err := DB.Preparex(DB.Rebind(sql))
484
+ if err != nil {
485
+ log.Println(err)
486
+ return nil, err
487
+ }
488
+ err = stmt.Select(&intColRecs, args...)
489
+ if err != nil {
490
+ log.Println(err)
491
+ return nil, err
492
+ }
493
+ return intColRecs, nil
494
+ }
495
+
496
+ // <%= @model_name %>StrCol get some string typed column of <%= @model_name %> by where restriction.
497
+ func <%= @model_name %>StrCol(col, where string, args ...interface{}) (strColRecs []string, err error) {
498
+ sql := "SELECT " + col + " FROM <%= table_name %>"
499
+ if len(where) > 0 {
500
+ sql = sql + " WHERE " + where
501
+ }
502
+ stmt, err := DB.Preparex(DB.Rebind(sql))
503
+ if err != nil {
504
+ log.Println(err)
505
+ return nil, err
506
+ }
507
+ err = stmt.Select(&strColRecs, args...)
508
+ if err != nil {
509
+ log.Println(err)
510
+ return nil, err
511
+ }
512
+ return strColRecs, nil
513
+ }
514
+
515
+ // Find<%= @model_name.pluralize %>Where query use a partial SQL clause that usually following after WHERE
516
+ // with placeholders, eg: FindUsersWhere("first_name = ? AND age > ?", "John", 18)
517
+ // will return those records in the table "users" whose first_name is "John" and age elder than 18.
518
+ func Find<%= @model_name.pluralize %>Where(where string, args ...interface{}) (<%= var_pmn %> []<%= @model_name %>, err error) {
519
+ sql := "SELECT <%= fields %> FROM <%= table_name %>"
520
+ if len(where) > 0 {
521
+ sql = sql + " WHERE " + where
522
+ }
523
+ stmt, err := DB.Preparex(DB.Rebind(sql))
524
+ if err != nil {
525
+ log.Println(err)
526
+ return nil, err
527
+ }
528
+ err = stmt.Select(&<%= var_pmn %>, args...)
529
+ if err != nil {
530
+ log.Println(err)
531
+ return nil, err
532
+ }
533
+ return <%= var_pmn %>, nil
534
+ }
535
+
536
+ // Find<%= @model_name %>BySql query use a complete SQL clause
537
+ // with placeholders, eg: FindUserBySql("SELECT * FROM users WHERE first_name = ? AND age > ? ORDER BY DESC LIMIT 1", "John", 18)
538
+ // will return only One record in the table "users" whose first_name is "John" and age elder than 18.
539
+ func Find<%= @model_name %>BySql(sql string, args ...interface{}) (*<%= @model_name %>, error) {
540
+ stmt, err := DB.Preparex(DB.Rebind(sql))
541
+ if err != nil {
542
+ log.Println(err)
543
+ return nil, err
544
+ }
545
+ _<%= var_umn %> := &<%= @model_name %>{}
546
+ err = stmt.Get(_<%= var_umn %>, args...)
547
+ if err != nil {
548
+ log.Println(err)
549
+ return nil, err
550
+ }
551
+ return _<%= var_umn %>, nil
552
+ }
553
+
554
+ // Find<%= @model_name.pluralize %>BySql query use a complete SQL clause
555
+ // with placeholders, eg: FindUsersBySql("SELECT * FROM users WHERE first_name = ? AND age > ?", "John", 18)
556
+ // will return those records in the table "users" whose first_name is "John" and age elder than 18.
557
+ func Find<%= @model_name.pluralize %>BySql(sql string, args ...interface{}) (<%= var_pmn %> []<%= @model_name %>, err error) {
558
+ stmt, err := DB.Preparex(DB.Rebind(sql))
559
+ if err != nil {
560
+ log.Println(err)
561
+ return nil, err
562
+ }
563
+ err = stmt.Select(&<%= var_pmn %>, args...)
564
+ if err != nil {
565
+ log.Println(err)
566
+ return nil, err
567
+ }
568
+ return <%= var_pmn %>, nil
569
+ }
570
+
571
+ // Create<%= @model_name %> use a named params to create a single <%= @model_name %> record.
572
+ // A named params is key-value map like map[string]interface{}{"first_name": "John", "age": 23} .
573
+ func Create<%= @model_name %>(am map[string]interface{}) (int64, error) {
574
+ if len(am) == 0 {
575
+ return 0, fmt.Errorf("Zero key in the attributes map!")
576
+ }
577
+ <%- unless @struct_info[:timestamp_cols].empty? -%>
578
+ t := time.Now()
579
+ for _, v := range []string{<%= @struct_info[:timestamp_cols].map(&:inspect).join(", ") %>} {
580
+ if am[v] == nil {
581
+ am[v] = t
582
+ }
583
+ }
584
+ <%- end -%>
585
+ keys := allKeys(am)
586
+ sqlFmt := `INSERT INTO <%= table_name %> (%s) VALUES (%s) RETURNING id`
587
+ sql := fmt.Sprintf(sqlFmt, strings.Join(keys, ","), ":"+strings.Join(keys, ",:"))
588
+ stmt, err := DB.PrepareNamed(sql)
589
+ if err != nil {
590
+ log.Println(err)
591
+ return 0, err
592
+ }
593
+ var lastId int64
594
+ err = stmt.Get(&lastId, am)
595
+ if err != nil {
596
+ log.Println(err)
597
+ return 0, err
598
+ }
599
+ return lastId, nil
600
+ }
601
+
602
+ // Create is a method for <%= @model_name %> to create a record.
603
+ func (_<%= var_umn %> *<%= @model_name %>) Create() (int64, error) {
604
+ ok, err := govalidator.ValidateStruct(_<%= var_umn %>)
605
+ if !ok {
606
+ errMsg := "Validate <%= @model_name %> struct error: Unknown error"
607
+ if err != nil {
608
+ errMsg = "Validate <%= @model_name %> struct error: " + err.Error()
609
+ }
610
+ log.Println(errMsg)
611
+ return 0, errors.New(errMsg)
612
+ }
613
+ <%- unless @struct_info[:timestamp_cols].empty? -%>
614
+ t := time.Now()
615
+ <%- @struct_info[:timestamp_cols].each do |c| -%>
616
+ _<%= var_umn %>.<%= c.camelize %> = t
617
+ <%- end -%>
618
+ <%- end -%>
619
+ sql := `INSERT INTO <%= table_name %> (<%= col_names.join(",") %>) VALUES (:<%= col_names.join(",:") %>) RETURNING id`
620
+ stmt, err := DB.PrepareNamed(sql)
621
+ if err != nil {
622
+ log.Println(err)
623
+ return 0, err
624
+ }
625
+ var lastId int64
626
+ err = stmt.Get(&lastId, _<%= var_umn %>)
627
+ if err != nil {
628
+ log.Println(err)
629
+ return 0, err
630
+ }
631
+ return lastId, nil
632
+ }
633
+
634
+ <%- unless @struct_info[:assoc_info][:has_many].empty? -%>
635
+ <%- has_many = @struct_info[:assoc_info][:has_many] -%>
636
+ <%- has_many.each do |k, v| -%>
637
+ // <%= k.pluralize %>Create is used for <%= @model_name %> to create the associated objects <%= k.pluralize %>
638
+ func (_<%= var_umn %> *<%= @model_name %>) <%= k.pluralize %>Create(am map[string]interface{}) error {
639
+ <%- if v[:through] -%>
640
+ // FIXME: use transaction to create these associated objects
641
+ <%= v[:class_name].underscore %>Id, err := Create<%= v[:class_name] %>(am)
642
+ if err != nil {
643
+ return err
644
+ }
645
+ _, err = Create<%= v[:through].to_s.singularize.camelize %>(map[string]interface{}{"<%= var_umn %>_id": _<%= var_umn %>.Id, "<%= v[:class_name].underscore %>_id": <%= v[:class_name].underscore %>Id})
646
+ <%- elsif v[:as] -%>
647
+ am["<%= v[:as] %>_id"] = _<%= var_umn %>.Id
648
+ am["<%= v[:as] %>_type"] = "<%= @model_name %>"
649
+ _, err := Create<%= v[:class_name] %>(am)
650
+ <%- else -%>
651
+ <%- if v[:foreign_key] -%>
652
+ am["<%= v[:foreign_key] %>"] = _<%= var_umn %>.Id
653
+ <%- else -%>
654
+ am["<%= var_umn %>_id"] = _<%= var_umn %>.Id
655
+ <%- end -%>
656
+ _, err := Create<%= v[:class_name] %>(am)
657
+ <%- end -%>
658
+ return err
659
+ }
660
+
661
+ // Get<%= k.pluralize %> is used for <%= @model_name %> to get associated objects <%= k.pluralize %>
662
+ // Say you have a <%= @model_name %> object named <%= var_umn %>, when you call <%= var_umn %>.Get<%= k.pluralize %>(),
663
+ // the object will get the associated <%= k.pluralize %> attributes evaluated in the struct.
664
+ func (_<%= var_umn %> *<%= @model_name %>) Get<%= k.pluralize %>() error {
665
+ _<%= k.underscore.pluralize %>, err := <%= @model_name %>Get<%= k.pluralize %>(_<%= var_umn %>.Id)
666
+ if err == nil {
667
+ _<%= var_umn %>.<%= k %> = _<%= k.underscore.pluralize %>
668
+ }
669
+ return err
670
+ }
671
+
672
+ // <%= @model_name %>Get<%= k.pluralize %> a helper fuction used to get associated objects for <%= @model_name %>IncludesWhere().
673
+ func <%= @model_name %>Get<%= k.pluralize %>(id int64) ([]<%= v[:class_name] %>, error) {
674
+ <%- if v[:through] -%>
675
+ // FIXME: use transaction to create these associated objects
676
+ <%- target_table = v[:class_name].underscore.pluralize -%>
677
+ <%- through_table = v[:through].to_s.pluralize -%>
678
+ sql := `SELECT <%= @all_structs_info[v[:class_name]][:select_fields] %>
679
+ FROM <%= target_table %>
680
+ INNER JOIN <%= through_table %>
681
+ ON <%= target_table %>.id = <%= through_table %>.<%= v[:class_name].underscore %>_id
682
+ WHERE <%= through_table %>.<%= var_umn %>_id = ?`
683
+ _<%= k.underscore.pluralize %>, err := Find<%= v[:class_name].pluralize %>BySql(sql, id)
684
+ <%- elsif v[:as] -%>
685
+ where := `<%= v[:as] %>_type = "<%= @model_name %>" AND <%= v[:as] %>_id = ?`
686
+ _<%= k.underscore.pluralize %>, err := Find<%= v[:class_name].pluralize %>Where(where, id)
687
+ <%- else -%>
688
+ <%- if v[:foreign_key] -%>
689
+ _<%= k.underscore.pluralize %>, err := Find<%= v[:class_name].pluralize %>By("<%= v[:foreign_key] %>", id)
690
+ <%- else -%>
691
+ _<%= k.underscore.pluralize %>, err := Find<%= v[:class_name].pluralize %>By("<%= var_umn %>_id", id)
692
+ <%- end -%>
693
+ <%- end -%>
694
+ return _<%= k.underscore.pluralize %>, err
695
+ }
696
+
697
+ <%- end -%>
698
+ <%- end -%>
699
+
700
+ <%- unless @struct_info[:assoc_info][:has_one].empty? -%>
701
+ <%- has_one = @struct_info[:assoc_info][:has_one] -%>
702
+ <%- has_one.each do |k, v| -%>
703
+ // Create<%= k %> is a method for the <%= @model_name %> model object to create an associated <%= k %> record.
704
+ func (_<%= var_umn %> *<%= @model_name %>) Create<%= k %>(am map[string]interface{}) error {
705
+ <%- if v[:foreign_key] -%>
706
+ am["<%= v[:foreign_key] %>"] = _<%= var_umn %>.Id
707
+ <%- else -%>
708
+ am["<%= var_umn %>_id"] = _<%= var_umn %>.Id
709
+ <%- end -%>
710
+ _, err := Create<%= v[:class_name] %>(am)
711
+ return err
712
+ }
713
+ <%- end -%>
714
+ <%- end -%>
715
+
716
+ <%- unless @struct_info[:assoc_info][:belongs_to].empty? -%>
717
+ <%- belongs_to = @struct_info[:assoc_info][:belongs_to] -%>
718
+ <%- belongs_to.each do |k, v| -%>
719
+ <%-# don't create virtual table for polymorphic model -%>
720
+ <%- next if v[:polymorphic] -%>
721
+ // Create<%= k %> is a method for a <%= @model_name %> object to create an associated <%= k %> record.
722
+ func (_<%= var_umn %> *<%= @model_name %>) Create<%= k %>(am map[string]interface{}) error {
723
+ <%- if v[:foreign_key] -%>
724
+ am["<%= v[:foreign_key] %>"] = _<%= var_umn %>.Id
725
+ <%- else -%>
726
+ am["<%= var_umn %>_id"] = _<%= var_umn %>.Id
727
+ <%- end -%>
728
+ _, err := Create<%= v[:class_name] %>(am)
729
+ return err
730
+ }
731
+ <%- end -%>
732
+ <%- end -%>
733
+
734
+ // Destroy is method used for a <%= @model_name %> object to be destroyed.
735
+ func (_<%= var_umn %> *<%= @model_name %>) Destroy() error {
736
+ if _<%= var_umn %>.Id == 0 {
737
+ return errors.New("Invalid Id field: it can't be a zero value")
738
+ }
739
+ err := Destroy<%= @model_name %>(_<%= var_umn %>.Id)
740
+ return err
741
+ }
742
+
743
+ // Destroy<%= @model_name %> will destroy a <%= @model_name %> record specified by the id parameter.
744
+ func Destroy<%= @model_name %>(id int64) error {
745
+ <%- if @struct_info[:has_assoc_dependent] -%>
746
+ // Destroy association objects at first
747
+ // Not care if exec properly temporarily
748
+ destroy<%= @model_name %>Associations(id)
749
+ <%- end -%>
750
+ stmt, err := DB.Preparex(DB.Rebind(`DELETE FROM <%= table_name %> WHERE id = ?`))
751
+ _, err = stmt.Exec(id)
752
+ if err != nil {
753
+ return err
754
+ }
755
+ return nil
756
+ }
757
+
758
+ // Destroy<%= @model_name.pluralize %> will destroy <%= @model_name %> records those specified by the ids parameters.
759
+ func Destroy<%= @model_name.pluralize %>(ids ...int64) (int64, error) {
760
+ if len(ids) == 0 {
761
+ msg := "At least one or more ids needed"
762
+ log.Println(msg)
763
+ return 0, errors.New(msg)
764
+ }
765
+ <%- if @struct_info[:has_assoc_dependent] -%>
766
+ // Destroy association objects at first
767
+ // Not care if exec properly temporarily
768
+ destroy<%= @model_name %>Associations(ids...)
769
+ <%- end -%>
770
+ idsHolder := buildIdsHolder(len(ids))
771
+ sql := fmt.Sprintf(`DELETE FROM <%= table_name %> WHERE id IN (%s)`, idsHolder)
772
+ idsT := []interface{}{}
773
+ for _,id := range ids {
774
+ idsT = append(idsT, interface{}(id))
775
+ }
776
+ stmt, err := DB.Preparex(DB.Rebind(sql))
777
+ result, err := stmt.Exec(idsT...)
778
+ if err != nil {
779
+ return 0, err
780
+ }
781
+ cnt, err := result.RowsAffected()
782
+ if err != nil {
783
+ return 0, err
784
+ }
785
+ return cnt, nil
786
+ }
787
+
788
+ // Destroy<%= @model_name.pluralize %>Where delete records by a where clause restriction.
789
+ // e.g. Destroy<%= @model_name.pluralize %>Where("name = ?", "John")
790
+ // And this func will not call the association dependent action
791
+ func Destroy<%= @model_name.pluralize %>Where(where string, args ...interface{}) (int64, error) {
792
+ sql := `DELETE FROM <%= table_name %> WHERE `
793
+ if len(where) > 0 {
794
+ sql = sql + where
795
+ } else {
796
+ return 0, errors.New("No WHERE conditions provided")
797
+ }
798
+ <%- if @struct_info[:has_assoc_dependent] -%>
799
+ ids, x_err := <%= @model_name %>IdsWhere(where, args...)
800
+ if x_err != nil {
801
+ log.Printf("Delete associated objects error: %v\n", x_err)
802
+ } else {
803
+ destroy<%= @model_name %>Associations(ids...)
804
+ }
805
+ <%- end -%>
806
+ stmt, err := DB.Preparex(DB.Rebind(sql))
807
+ result, err := stmt.Exec(args...)
808
+ if err != nil {
809
+ return 0, err
810
+ }
811
+ cnt, err := result.RowsAffected()
812
+ if err != nil {
813
+ return 0, err
814
+ }
815
+ return cnt, nil
816
+ }
817
+
818
+ <%- if @struct_info[:has_assoc_dependent] -%>
819
+ // destroy<%= @model_name %>Associations is a private function used to destroy a <%= @model_name %> record's associated objects.
820
+ // The func not return err temporarily.
821
+ func destroy<%= @model_name %>Associations(ids ...int64) {
822
+ idsHolder := ""
823
+ if len(ids) > 1 {
824
+ idsHolder := buildIdsHolder(len(ids))
825
+ }
826
+ idsT := []interface{}{}
827
+ for _, id := range ids {
828
+ idsT = append(idsT, interface{}(id))
829
+ }
830
+ var err error
831
+ // make sure no declared-and-not-used exception
832
+ _, _, _ = idsHolder, idsT, err
833
+ <%- [:has_many, :has_one].each do |ass| -%>
834
+ <%- ass_cols = @struct_info[:assoc_info][ass] -%>
835
+ <%- unless ass_cols.empty? -%>
836
+ <%- ass_cols.each_value do |opts| -%>
837
+ <%- if opts.key? :dependent -%>
838
+ <%- case opts[:dependent] -%>
839
+ <%- when :destroy, :delete_all -%>
840
+ <%- if opts[:through] -%>
841
+ where := fmt.Sprintf("id IN (SELECT id FROM <%= opts[:through].to_s %> WHERE <%= var_umn %>_id IN (%s))", idsHolder)
842
+ _, err = Destroy<%= opts[:class_name].pluralize %>Where(where, idsT...)
843
+ if err != nil {
844
+ log.Printf("Destroy associated object %s error: %v\n", "<%= opts[:class_name].pluralize %>", err)
845
+ }
846
+ where = fmt.Sprintf("<%= var_umn %>_id IN (%s)", idsHolder)
847
+ _, err = Destroy<%= opts[:through].to_s.pluralize.camelize %>Where(where, idsT...)
848
+ if err != nil {
849
+ log.Printf("Destroy associated object %s error: %v\n", "<%= opts[:through].to_s.singularize.camelize %>", err)
850
+ }
851
+ <%- elsif opts[:as] -%>
852
+ where := fmt.Sprintf(`<%= opts[:as] %>_type = "<%= @model_name %>" AND <%= opts[:as] %>_id IN (%s)`, idsHolder)
853
+ _, err = Destroy<%= opts[:class_name].pluralize %>Where(where, idsT...)
854
+ if err != nil {
855
+ log.Printf("Destroy associated object %s error: %v\n", "<%= opts[:class_name].pluralize %>", err)
856
+ }
857
+ <%- else -%>
858
+ <%- if opts.key? :foreign_key -%>
859
+ where := fmt.Sprintf("<%= opts[:foreign_key] %> IN (%s)", idsHolder)
860
+ <%- else -%>
861
+ where := fmt.Sprintf("<%= var_umn %>_id IN (%s)", idsHolder)
862
+ <%- end -%>
863
+ _, err = Destroy<%= opts[:class_name].pluralize %>Where(where, idsT...)
864
+ if err != nil {
865
+ log.Printf("Destroy associated object %s error: %v\n", "<%= opts[:class_name].pluralize %>", err)
866
+ }
867
+ <%- end -%>
868
+ <%- when :nullify -%>
869
+ // no sql.NULLType supported, just set the associated field to zero value of int64
870
+ <%- if opts[:through] -%>
871
+ sql := fmt.Sprintf("UPDATE <%= opts[:through].to_s %> SET <%= opts[:class_name].underscore %>_id = 0 WHERE <%= opts[:class_name].underscore %>_id IN (%s)", idsHolder)
872
+ _, err = Update<%= opts[:through].to_s.camelize %>BySql(sql, idsT...)
873
+ if err != nil {
874
+ log.Printf("Delete associated object %s error: %v\n", "<%= opts[:class_name].pluralize %>", err)
875
+ }
876
+ <%- else -%>
877
+ <%- if opts.key? :foreign_key -%>
878
+ sql := fmt.Sprintf("UPDATE <%= opts[:class_name].underscore.pluralize %> SET <%= opts[:foreign_key] %> = 0 WHERE <%= opts[:foreign_key] %> IN (%s)", idsHolder)
879
+ <%- else -%>
880
+ sql := fmt.Sprintf("UPDATE <%= opts[:class_name].underscore.pluralize %> SET <%= var_umn %>_id = 0 WHERE <%= var_umn %>_id IN (%s)", idsHolder)
881
+ <%- end -%>
882
+ _, err = Update<%= opts[:class_name].pluralize %>BySql(sql, idsT...)
883
+ if err != nil {
884
+ log.Printf("Delete associated object %s error: %v\n", "<%= opts[:class_name].pluralize %>", err)
885
+ }
886
+ <%- end -%>
887
+ <%- end -%>
888
+ <%- end -%>
889
+ <%- end -%>
890
+ <%- end -%>
891
+ <%- end -%>
892
+ }
893
+ <%- end -%>
894
+
895
+ // Save method is used for a <%= @model_name %> object to update an existed record mainly.
896
+ // If no id provided a new record will be created. FIXME: A UPSERT action will be implemented further.
897
+ func (_<%= var_umn %> *<%= @model_name %>) Save() error {
898
+ ok, err := govalidator.ValidateStruct(_<%= var_umn %>)
899
+ if !ok {
900
+ errMsg := "Validate <%= @model_name %> struct error: Unknown error"
901
+ if err != nil {
902
+ errMsg = "Validate <%= @model_name %> struct error: " + err.Error()
903
+ }
904
+ log.Println(errMsg)
905
+ return errors.New(errMsg)
906
+ }
907
+ if _<%= var_umn %>.Id == 0 {
908
+ _, err = _<%= var_umn %>.Create()
909
+ return err
910
+ }
911
+ <%- if @struct_info[:timestamp_cols].include? "updated_at" -%>
912
+ _<%= var_umn %>.UpdatedAt = time.Now()
913
+ <%- end -%>
914
+ sqlFmt := `UPDATE <%= table_name %> SET %s WHERE id = %v`
915
+ <%- save_col_names = col_names - ["created_at"] -%>
916
+ sqlStr := fmt.Sprintf(sqlFmt, "<%= save_col_names.zip(save_col_names).map{|c| c.join(" = :")}.join(", ") %>", _<%= var_umn %>.Id)
917
+ _, err = DB.NamedExec(sqlStr, _<%= var_umn %>)
918
+ return err
919
+ }
920
+
921
+ // Update<%= @model_name %> is used to update a record with a id and map[string]interface{} typed key-value parameters.
922
+ func Update<%= @model_name %>(id int64, am map[string]interface{}) error {
923
+ if len(am) == 0 {
924
+ return errors.New("Zero key in the attributes map!")
925
+ }
926
+ <%- if @struct_info[:timestamp_cols].include? "updated_at" -%>
927
+ am["updated_at"] = time.Now()
928
+ <%- end -%>
929
+ keys := allKeys(am)
930
+ sqlFmt := `UPDATE <%= table_name %> SET %s WHERE id = %v`
931
+ setKeysArr := []string{}
932
+ for _,v := range keys {
933
+ s := fmt.Sprintf(" %s = :%s", v, v)
934
+ setKeysArr = append(setKeysArr, s)
935
+ }
936
+ sqlStr := fmt.Sprintf(sqlFmt, strings.Join(setKeysArr, ", "), id)
937
+ _, err := DB.NamedExec(sqlStr, am)
938
+ if err != nil {
939
+ log.Println(err)
940
+ return err
941
+ }
942
+ return nil
943
+ }
944
+
945
+ // Update is a method used to update a <%= @model_name %> record with the map[string]interface{} typed key-value parameters.
946
+ func (_<%= var_umn %> *<%= @model_name %>) Update(am map[string]interface{}) error {
947
+ if _<%= var_umn %>.Id == 0 {
948
+ return errors.New("Invalid Id field: it can't be a zero value")
949
+ }
950
+ err := Update<%= @model_name %>(_<%= var_umn %>.Id, am)
951
+ return err
952
+ }
953
+
954
+ // UpdateAttributes method is supposed to be used to update <%= @model_name %> records as corresponding update_attributes in Ruby on Rails.
955
+ func (_<%= var_umn %> *<%= @model_name %>) UpdateAttributes(am map[string]interface{}) error {
956
+ if _<%= var_umn %>.Id == 0 {
957
+ return errors.New("Invalid Id field: it can't be a zero value")
958
+ }
959
+ err := Update<%= @model_name %>(_<%= var_umn %>.Id, am)
960
+ return err
961
+ }
962
+
963
+ // UpdateColumns method is supposed to be used to update <%= @model_name %> records as corresponding update_columns in Ruby on Rails.
964
+ func (_<%= var_umn %> *<%= @model_name %>) UpdateColumns(am map[string]interface{}) error {
965
+ if _<%= var_umn %>.Id == 0 {
966
+ return errors.New("Invalid Id field: it can't be a zero value")
967
+ }
968
+ err := Update<%= @model_name %>(_<%= var_umn %>.Id, am)
969
+ return err
970
+ }
971
+
972
+ // Update<%= @model_name.pluralize %>BySql is used to update <%= @model_name %> records by a SQL clause
973
+ // using the '$1...$n' binding syntax.
974
+ func Update<%= @model_name.pluralize %>BySql(sql string, args ...interface{}) (int64, error) {
975
+ if sql == "" {
976
+ return 0, errors.New("A blank SQL clause")
977
+ }
978
+ stmt, err := DB.Preparex(DB.Rebind(sql))
979
+ result, err := stmt.Exec(args...)
980
+ if err != nil {
981
+ return 0, err
982
+ }
983
+ cnt, err := result.RowsAffected()
984
+ if err != nil {
985
+ return 0, err
986
+ }
987
+ return cnt, nil
988
+ }