go-on-rails 0.3.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+ }