goa_model_gen 0.7.1 → 0.8.0

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
  SHA256:
3
- metadata.gz: 7688fbd0deceb1a0e7c2bcf7db894bae0a8fcb02c391e00c34dda9760f525601
4
- data.tar.gz: 646ff959fe0cce1b7256d7bf7de046aebed82a4e030168429a005b8e32c48658
3
+ metadata.gz: 128844e0701ac2c0ccbb8931770e8778ce28527c7c4dca2948d3fb647b33073d
4
+ data.tar.gz: 6b03045e22675b21b93762927c3551e0c43e5711ce9aa9f48e64a92bcbffa145
5
5
  SHA512:
6
- metadata.gz: 47eafcee348fa1a47b3aada4540f49bdcca3f6a8e6ccd45ecee2613dceeddb676981dbebf31b8656da30a08592962df53514d726258d02e898e19af10fa72d55
7
- data.tar.gz: 99ebfd800233dadca64e4d292efd15a6947c6a6bbf59f2a379ea01ad51df59da77d37dce3f0d24bf5d0cc1395338face2ca987fc893ab7619662135447872302
6
+ metadata.gz: d1a948144492a1ea82cae9bd2afb709a89c29b8cf2ed7286385eeef43e4e7dd5259ed3b153656d14f39429ea7cb53552b0fe4477a26c2731aaf5ed007a549acd
7
+ data.tar.gz: 5044445888128712fd8419cb4487308d86d6d1e4abfe77d1cdc342c9048877fa20d2160dd4cb71041ea2965b3786a1927fcb8218546d4f577204fe142cf0cc56
@@ -1,11 +1,16 @@
1
+ # coding: utf-8
1
2
  require "goa_model_gen"
2
3
 
4
+ require 'json'
5
+
6
+ require "active_support/core_ext/string"
3
7
  require "thor"
4
8
 
5
9
  require "goa_model_gen/logger"
6
10
  require "goa_model_gen/config"
7
11
  require "goa_model_gen/loader"
8
12
  require "goa_model_gen/generator"
13
+ require "goa_model_gen/go_struct"
9
14
 
10
15
  module GoaModelGen
11
16
  class Cli < Thor
@@ -61,17 +66,32 @@ module GoaModelGen
61
66
  end
62
67
  end
63
68
 
64
- desc "converter FILE1...", "Generate converter files from definition files and swagger.yaml"
65
- def converter(*paths)
69
+ desc "structs_gen", "Generate go source files to generate structs.json"
70
+ def structs_gen
71
+ setup
72
+ new_generator.process({
73
+ "templates/structs_base.go.erb" => File.join(cfg.structs_gen_dir, "structs.go"),
74
+ "templates/structs_main.go.erb" => File.join(cfg.structs_gen_dir, "main.go"),
75
+ })
76
+ end
77
+
78
+ desc "converter FILE", "Generate converter files from definition file structs.json"
79
+ def converter(path)
66
80
  setup
67
81
  new_generator.process({
68
82
  "templates/converter_base.go.erb" => File.join(cfg.converter_dir, "base.go"),
69
83
  })
70
- load_types_for(paths) do |source_file|
71
- next if source_file.types.all?{|t| !t.payload && !t.media_type}
72
- new_generator.tap{|g| g.source_file = source_file }.process({
73
- 'templates/converter.go.erb' => File.join(cfg.converter_dir, source_file.basename, "conv.go"),
74
- })
84
+ structs = JSON.parse(File.read(path))
85
+ (structs['model'] || []).each do |mt|
86
+ m = GoStruct.new(mt)
87
+ pt = (structs['payload'] || []).detect{|t| t["Name"] == "#{m.name}Payload" }
88
+ rt = (structs['result'] || []).detect{|t| t["Name"] == m.name }
89
+ variables = {
90
+ model: m,
91
+ payload: pt ? GoStruct.new(pt) : nil,
92
+ result: rt ? GoStruct.new(rt) : nil
93
+ }
94
+ new_generator.run('templates/converter.go.erb', File.join(cfg.converter_dir, m.name.underscore, "conv.go"), variables)
75
95
  end
76
96
  end
77
97
 
@@ -16,6 +16,7 @@ module GoaModelGen
16
16
  model_dir
17
17
  store_dir
18
18
  converter_dir
19
+ structs_gen_dir
19
20
  validator_path
20
21
  generator_version_comment
21
22
  ].freeze
@@ -29,6 +30,7 @@ module GoaModelGen
29
30
  @model_dir ||= "./model"
30
31
  @store_dir ||= "./stores"
31
32
  @converter_dir ||= "./converters"
33
+ @structs_gen_dir ||= "./cmd/structs"
32
34
  @validator_path ||= "gopkg.in/go-playground/validator.v9"
33
35
  @generator_version_comment ||= false
34
36
  self
@@ -65,13 +65,18 @@ module GoaModelGen
65
65
  "datastore" => "google.golang.org/appengine/datastore",
66
66
  }
67
67
 
68
- def generate(template_path)
68
+ def generate(template_path, variables = {})
69
69
  clear_dependencies
70
70
  user_editable(value: false)
71
71
 
72
72
  abs_path = File.expand_path('../' + template_path, __FILE__)
73
73
  erb = ERB.new(File.read(abs_path), nil, "-")
74
74
  erb.filename = abs_path
75
+
76
+ variables.each do |key, val|
77
+ define_singleton_method(key){ val }
78
+ end
79
+
75
80
  body = erb.result(binding).strip
76
81
 
77
82
  raise "No package given in #{abs_path}" if package.blank?
@@ -97,10 +102,10 @@ module GoaModelGen
97
102
  clear: "\e[0m",
98
103
  }
99
104
 
100
- def run(template_path, output_path)
101
- content = generate(template_path)
105
+ def run(template_path, output_path, variables = {}, &block)
106
+ content = generate(template_path, variables, &block)
102
107
 
103
- if user_editable? && keep_editable
108
+ if user_editable? && keep_editable && File.exist?(output_path)
104
109
  $stderr.puts("%sKEEP%s %s" % [COLORS[:blue], COLORS[:clear], output_path])
105
110
  return
106
111
  end
@@ -0,0 +1,68 @@
1
+ # coding: utf-8
2
+ require 'goa_model_gen'
3
+
4
+ require "active_support/core_ext/string"
5
+
6
+ module GoaModelGen
7
+ class GoStruct
8
+ attr_reader :name, :pkg_path, :size, :fields
9
+ def initialize(d)
10
+ @name = d['Name']
11
+ @pkg_path = d['PkgPath']
12
+ @size = d['Size']
13
+ @fields = (d['Fields'] || []).map do |f|
14
+ GoStructField.new(f)
15
+ end
16
+ end
17
+ end
18
+
19
+ class GoStructField
20
+ attr_reader :name, :type, :anonymous, :tags
21
+ def initialize(d)
22
+ @name = d['Name']
23
+ @anonymous = d['Anonymous']
24
+ @type = GoStructFieldType.new(d['Type'])
25
+ @tags = d['Tag'] || {}
26
+ end
27
+ end
28
+
29
+ class GoStructFieldType
30
+ attr_reader :name, :kinds, :pkg_path, :representation
31
+ def initialize(d)
32
+ @name = d['Name']
33
+ @kinds = d['Kinds']
34
+ @pkg_path = d['PkgPath']
35
+ @representation = d['Representation']
36
+ end
37
+
38
+ def ==(other)
39
+ (pkg_path == other.pkg_path) &&
40
+ (name == other.name) &&
41
+ (kinds == other.kinds) &&
42
+ (representation == other.representation)
43
+ end
44
+ alias_method :assignable_with?, :==
45
+
46
+ def pointer_of?(other)
47
+ (pkg_path == other.pkg_path) &&
48
+ (name == other.name) &&
49
+ (kinds == (other.kinds + ['ptr']))
50
+ end
51
+ def pointee_of?(other)
52
+ other.pointer_of?(self)
53
+ end
54
+
55
+ def needs_error_to_convert_to?(other)
56
+ return false if other.name == 'string'
57
+ return true if name == 'string'
58
+ return false
59
+ end
60
+
61
+ def method_part_name
62
+ parts =
63
+ kinds.first == 'struct' ?
64
+ [name] + kinds[1..-1] : kinds
65
+ parts.map(&:camelize).join
66
+ end
67
+ end
68
+ end
@@ -7,96 +7,110 @@ def method_calling_exp(m, argument)
7
7
  end
8
8
  end
9
9
  -%>
10
- <%- package source_file.basename -%>
10
+ <%- package model.name.underscore -%>
11
11
 
12
- <%- source_file.types.select(&:gen_converter?).each do |type| -%>
13
- <%- if type.payload -%>
12
+ <%- if payload-%>
14
13
  <%-
15
- import "#{config.go_package}/converters"
16
- import :gen, "#{config.go_package}/gen/#{type.name.underscore}"
17
- import "#{config.go_package}/model"
14
+ import "#{config.go_package}/converters"
15
+ import :gen, "#{config.go_package}/gen/#{model.name.underscore}"
16
+ import "#{config.go_package}/model"
18
17
  -%>
19
- func <%= type.name %>PayloadToModel(payload *gen.<%= type.payload_name %>) (*model.<%= type.name %>, error) {
20
- model := &model.<%= type.name %>{}
21
- if err := CopyFrom<%= type.name %>PayloadToModel(payload, model); err != nil {
18
+ func <%= model.name %>PayloadToModel(payload *gen.<%= payload.name %>) (*model.<%= model.name %>, error) {
19
+ m := &model.<%= model.name %>{}
20
+ if err := CopyFrom<%= model.name %>PayloadToModel(payload, m); err != nil {
22
21
  return nil, err
23
22
  }
24
- return model, nil
23
+ return m, nil
25
24
  }
26
25
 
27
- func CopyFrom<%= type.name %>PayloadToModel(payload *gen.<%= type.payload_name %>, model *model.<%= type.name %>) error {
26
+ func CopyFrom<%= model.name %>PayloadToModel(payload *gen.<%= payload.name %>, m *model.<%= model.name %>) error {
28
27
  if payload == nil {
29
28
  return converters.NoPayloadGiven
30
29
  }
31
- if model == nil {
30
+ if m == nil {
32
31
  return converters.NoModelGiven
33
32
  }
34
33
 
35
34
  <%-
36
- type.fields_including_id.each do |f|
37
- pf = type.payload.fields.detect{|pf| pf.name == f.swagger_name }
38
- if pf.nil?
35
+ assigned_field_names = []
36
+ model.fields.each do |mf|
37
+ pf_name = (mf.tags['payload'] || mf.name).downcase
38
+ pf = payload.fields.detect{|pf| pf.name.downcase == pf_name }
39
+ assigned_field_names.push(pf.name) if pf
40
+ if pf.nil?
39
41
  -%>
40
- // <%= f.name %> not found in payload fields
41
- <%-
42
- else
43
- simple, with_error, method_name = f.payload_assignment_options(pf)
44
- -%>
45
- <%- if simple -%>
46
- model.<%= f.name %> = payload.<%= f.goa_name %>
47
- <%- elsif !with_error -%>
48
- model.<%= f.name %> = converters.<%= method_calling_exp(method_name, "payload.#{f.goa_name}") %>
42
+ // <%= mf.name %> not found in <%= payload.name %> fields
43
+ <%- elsif mf.type.assignable_with?(pf.type) -%>
44
+ m.<%= mf.name %> = payload.<%= pf.name %>
45
+ <%- elsif mf.type.pointee_of?(pf.type) -%>
46
+ m.<%= mf.name %> = *payload.<%= pf.name %>
47
+ <%- elsif !pf.type.needs_error_to_convert_to?(mf.type) -%>
48
+ m.<%= mf.name %> = converters.<%= pf.type.method_part_name %>To<%= mf.type.method_part_name %>(payload.<%= pf.name %>)
49
49
  <%- else -%>
50
- if v, err := converters.<%= method_calling_exp(method_name, "payload.#{f.goa_name}") %>; err != nil {
50
+ if v, err := converters.<%= pf.type.method_part_name %>To<%= mf.type.method_part_name %>(payload.<%= pf.name %>); err != nil {
51
51
  return err
52
52
  } else {
53
- model.<%= f.name %> = v
53
+ m.<%= mf.name %> = v
54
54
  }
55
55
  <%- end -%>
56
56
  <%- end -%>
57
- <%- end -%>
58
- <%- type.payload.field_diffs(type.fields_including_id.map{|f| f.swagger_name}).each do |pf| -%>
59
- // No model field for payload field "<%= pf.name %>"
60
- <%- end -%>
57
+ <%-
58
+ payload.fields.each do |pf|
59
+ unless assigned_field_names.include?(pf.name)
60
+ -%>
61
+ // No model field in <%= model.name %> for payload field "<%= pf.name %>"
62
+ <%-
63
+ end
64
+ end
65
+ -%>
61
66
  return nil
62
67
  }
68
+ <%- end -%>
63
69
 
64
- <%- end -%>
65
- <%- if type.media_type -%>
66
- func <%= type.name %>ModelToMediaType(model *model.<%= type.name %>) (*gen.<%= type.media_type_name_for_go %>, error) {
67
- if model == nil {
70
+ <%- if result -%>
71
+ <%-
72
+ import "#{config.go_package}/converters"
73
+ import :gen, "#{config.go_package}/gen/#{model.name.underscore}"
74
+ import "#{config.go_package}/model"
75
+ -%>
76
+ func <%= model.name %>ModelToResult(m *model.<%= model.name %>) (*gen.<%= result.name %>, error) {
77
+ if m == nil {
68
78
  return nil, converters.NoModelGiven
69
79
  }
70
- r := &gen.<%= type.media_type_name_for_go %>{}
80
+ r := &gen.<%= result.name %>{}
71
81
 
72
82
  <%-
73
- type.fields_including_id.each do |f|
74
- mf = type.media_type.fields.detect{|mf| f.swagger_name == mf.name }
75
- if mf.nil?
83
+ assigned_field_names = []
84
+ model.fields.each do |mf|
85
+ rf_name = (mf.tags['result'] || mf.name).downcase
86
+ rf = result.fields.detect{|rf| rf.name.downcase == rf_name }
87
+ assigned_field_names.push(rf.name) if rf
88
+ if rf.nil?
76
89
  -%>
77
- // <%= f.name %> not found for media type field
78
- <%-
79
- else
80
- simple, with_error, method_name = f.media_type_assignment_options(mf)
81
- -%>
82
- <%- if simple -%>
83
- r.<%= f.goa_name %> = model.<%= f.name %>
84
- <%- elsif !with_error -%>
85
- r.<%= f.goa_name %> = converters.<%= method_calling_exp(method_name, "model.#{f.name}") %>
86
- <%- else -%>
87
- if val, err := converters.<%= method_calling_exp(method_name, "model.#{f.name}") %>; err != nil {
88
- return nil, err
90
+ // <%= mf.name %> not found in <%= result.name %> fields
91
+ <%- elsif rf.type.assignable_with?(mf.type) -%>
92
+ r.<%= rf.name %> = m.<%= mf.name %>
93
+ <%- elsif rf.type.pointer_of?(mf.type) -%>
94
+ r.<%= rf.name %> = &m.<%= mf.name %>
95
+ <%- elsif !mf.type.needs_error_to_convert_to?(rf.type) -%>
96
+ r.<%= rf.name %> = converters.<%= mf.type.method_part_name %>To<%= rf.type.method_part_name %>(m.<%= mf.name %>)
97
+ <%- else -%>
98
+ if v, err := converters.<%= mf.type.method_part_name %>To<%= rf.type.method_part_name %>(m.<%= mf.name %>); err != nil {
99
+ return err
89
100
  } else {
90
- r.<%= f.goa_name %> = val
101
+ r.<%= rf.name %> = v
91
102
  }
92
- <%- end
93
- end -%>
94
- <%- end -%>
95
- <%- type.media_type.field_diffs(type.fields_including_id.map{|f| f.swagger_name}).each do |mf| -%>
96
- // No model field for media type field "<%= mf.name %>"
103
+ <%- end -%>
97
104
  <%- end -%>
105
+ <%-
106
+ result.fields.each do |rf|
107
+ unless assigned_field_names.include?(rf.name)
108
+ -%>
109
+ // No model field in <%= model.name %> for result field "<%= rf.name %>"
110
+ <%-
111
+ end
112
+ end
113
+ -%>
98
114
  return r, nil
99
115
  }
100
-
101
- <%- end -%>
102
116
  <%- end -%>
@@ -0,0 +1,165 @@
1
+ <%- package 'main' -%>
2
+
3
+ import (
4
+ "encoding/json"
5
+ "fmt"
6
+ "os"
7
+ "reflect"
8
+ "regexp"
9
+ )
10
+
11
+ func process(objectMap map[string][]interface{}, ptn *regexp.Regexp) {
12
+ res := map[string][]*DataType{}
13
+
14
+ for key, objects := range objectMap {
15
+ types := []reflect.Type{}
16
+ for _, obj := range objects {
17
+ types = append(types, reflect.TypeOf(obj))
18
+ }
19
+
20
+ types = digTypes(types)
21
+
22
+ dataTypes := []*DataType{}
23
+ for _, t := range types {
24
+ dt := newDataType(t)
25
+ if ptn.MatchString(dt.PkgPath) {
26
+ dataTypes = append(dataTypes, dt)
27
+ }
28
+ }
29
+
30
+ res[key] = dataTypes
31
+ }
32
+
33
+ b, err := json.MarshalIndent(res, "", " ")
34
+ if err != nil {
35
+ fmt.Fprintf(os.Stderr, "Failed to json.MarshalIndent because of %v\n", err)
36
+ return
37
+ }
38
+ _, err = os.Stdout.Write(b)
39
+ if err != nil {
40
+ fmt.Fprintf(os.Stderr, "Failed to write because of %v\n", err)
41
+ return
42
+ }
43
+ }
44
+
45
+ type DataFieldType struct {
46
+ PkgPath string `json:"PkgPath,omitempty"`
47
+ Name string `json:"Name,omitempty"`
48
+ Kinds []string `json:"Kinds,omitempty"`
49
+ Representation string
50
+ }
51
+
52
+ type DataField struct {
53
+ Name string
54
+ Type *DataFieldType
55
+ RawTag string `json:"RawTag,omitempty"`
56
+ Tag map[string]string `json:"Tag,omitempty"`
57
+ Anonymous bool
58
+ }
59
+
60
+ type DataType struct {
61
+ Name string
62
+ PkgPath string
63
+ Size uintptr
64
+ Fields []*DataField
65
+ }
66
+
67
+ func digTypes(types []reflect.Type) []reflect.Type {
68
+ m := map[string]reflect.Type{}
69
+ for _, t := range types {
70
+ key := t.PkgPath() + "." + t.Name()
71
+ m[key] = t
72
+ }
73
+
74
+ for _, t := range types {
75
+ digType(m, t)
76
+ }
77
+
78
+ r := []reflect.Type{}
79
+ for _, t := range m {
80
+ r = append(r, t)
81
+ }
82
+ return r
83
+ }
84
+
85
+ func digType(m map[string]reflect.Type, t reflect.Type) {
86
+ switch t.Kind() {
87
+ case reflect.Struct:
88
+ digStruct(m, t)
89
+ case reflect.Array, reflect.Chan, reflect.Map, reflect.Ptr, reflect.Slice:
90
+ digType(m, t.Elem())
91
+ }
92
+ }
93
+
94
+ func digStruct(m map[string]reflect.Type, t reflect.Type) {
95
+ numField := t.NumField()
96
+ for i := 0; i < numField; i++ {
97
+ f := t.Field(i)
98
+ ft := f.Type
99
+ if ft.PkgPath() != "" {
100
+ key := ft.PkgPath() + "." + ft.Name()
101
+ _, ok := m[key]
102
+ if !ok {
103
+ m[key] = ft
104
+ switch ft.Kind() {
105
+ case reflect.Struct:
106
+ digType(m, ft)
107
+ }
108
+ }
109
+ }
110
+ }
111
+ }
112
+
113
+ func newDataType(t reflect.Type) *DataType {
114
+ r := &DataType{
115
+ Name: t.Name(),
116
+ PkgPath: t.PkgPath(),
117
+ Size: t.Size(),
118
+ }
119
+ if t.Kind() != reflect.Struct {
120
+ return r
121
+ }
122
+
123
+ fields := []*DataField{}
124
+ numField := t.NumField()
125
+ for i := 0; i < numField; i++ {
126
+ f := t.Field(i)
127
+ ft := f.Type
128
+ fields = append(fields, &DataField{
129
+ Name: f.Name,
130
+ Anonymous: f.Anonymous,
131
+ Type: DataFieldTypeFromType(ft),
132
+ Tag: parseTag(string(f.Tag)),
133
+ RawTag: string(f.Tag),
134
+ })
135
+ }
136
+ r.Fields = fields
137
+ return r
138
+ }
139
+
140
+ func DataFieldTypeFromType(t reflect.Type) *DataFieldType {
141
+ switch t.Kind() {
142
+ case reflect.Array, reflect.Chan, reflect.Map, reflect.Ptr, reflect.Slice:
143
+ r := DataFieldTypeFromType(t.Elem())
144
+ r.Kinds = append(r.Kinds, t.Kind().String())
145
+ return r
146
+ default:
147
+ return &DataFieldType{
148
+ PkgPath: t.PkgPath(),
149
+ Name: t.Name(),
150
+ Kinds: []string{t.Kind().String()},
151
+ Representation: t.String(),
152
+ }
153
+ }
154
+ }
155
+
156
+ var TagParserRE = regexp.MustCompile(`\s*([^:\s]+?):"(.+?)"`)
157
+
158
+ func parseTag(src string) map[string]string {
159
+ parsed := TagParserRE.FindAllStringSubmatch(src, -1)
160
+ r := map[string]string{}
161
+ for _, parts := range parsed {
162
+ r[parts[1]] = parts[2]
163
+ }
164
+ return r
165
+ }
@@ -0,0 +1,28 @@
1
+ <%-
2
+ package 'main'
3
+
4
+ user_editable
5
+
6
+ import "regexp"
7
+ -%>
8
+
9
+ // Usage
10
+ // $ go run <%= config.structs_gen_dir %>/*.go
11
+
12
+ func main() {
13
+ ptn := regexp.MustCompile(`\A<%= config.go_package %>`)
14
+
15
+ objectMap := map[string][]interface{}{
16
+ "model": []interface{}{
17
+ // model.User{},
18
+ },
19
+ "payload": []interface{}{
20
+ // user.UserPayload{},
21
+ },
22
+ "result": []interface{}{
23
+ // user.User{},
24
+ },
25
+ }
26
+
27
+ process(objectMap, ptn)
28
+ }
@@ -1,3 +1,3 @@
1
1
  module GoaModelGen
2
- VERSION = "0.7.1"
2
+ VERSION = "0.8.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: goa_model_gen
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.1
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - akm
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-01-08 00:00:00.000000000 Z
11
+ date: 2019-01-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thor
@@ -147,6 +147,7 @@ files:
147
147
  - lib/goa_model_gen/config.rb
148
148
  - lib/goa_model_gen/field.rb
149
149
  - lib/goa_model_gen/generator.rb
150
+ - lib/goa_model_gen/go_struct.rb
150
151
  - lib/goa_model_gen/goa.rb
151
152
  - lib/goa_model_gen/golang_helper.rb
152
153
  - lib/goa_model_gen/loader.rb
@@ -160,6 +161,8 @@ files:
160
161
  - lib/goa_model_gen/templates/model_validation.go.erb
161
162
  - lib/goa_model_gen/templates/store.go.erb
162
163
  - lib/goa_model_gen/templates/store_validation.go.erb
164
+ - lib/goa_model_gen/templates/structs_base.go.erb
165
+ - lib/goa_model_gen/templates/structs_main.go.erb
163
166
  - lib/goa_model_gen/templates/validator.go.erb
164
167
  - lib/goa_model_gen/type.rb
165
168
  - lib/goa_model_gen/version.rb