goon_model_gen 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +13 -0
- data/.rspec +3 -0
- data/.travis.yml +7 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +10 -0
- data/LICENSE.txt +21 -0
- data/README.md +44 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/exe/goon_model_gen +10 -0
- data/goon_model_gen.gemspec +41 -0
- data/lib/goon_model_gen.rb +6 -0
- data/lib/goon_model_gen/cli.rb +79 -0
- data/lib/goon_model_gen/config.rb +83 -0
- data/lib/goon_model_gen/generator.rb +111 -0
- data/lib/goon_model_gen/golang.rb +22 -0
- data/lib/goon_model_gen/golang/builtin.rb +31 -0
- data/lib/goon_model_gen/golang/datastore_supported.rb +48 -0
- data/lib/goon_model_gen/golang/enum.rb +26 -0
- data/lib/goon_model_gen/golang/field.rb +41 -0
- data/lib/goon_model_gen/golang/file.rb +27 -0
- data/lib/goon_model_gen/golang/modifier.rb +65 -0
- data/lib/goon_model_gen/golang/named_slice.rb +24 -0
- data/lib/goon_model_gen/golang/package.rb +83 -0
- data/lib/goon_model_gen/golang/packages.rb +64 -0
- data/lib/goon_model_gen/golang/predeclared_type.rb +16 -0
- data/lib/goon_model_gen/golang/sentence.rb +17 -0
- data/lib/goon_model_gen/golang/struct.rb +38 -0
- data/lib/goon_model_gen/golang/type.rb +29 -0
- data/lib/goon_model_gen/model_builder.rb +137 -0
- data/lib/goon_model_gen/source/context.rb +17 -0
- data/lib/goon_model_gen/source/contextual.rb +9 -0
- data/lib/goon_model_gen/source/enum.rb +20 -0
- data/lib/goon_model_gen/source/field.rb +41 -0
- data/lib/goon_model_gen/source/file.rb +58 -0
- data/lib/goon_model_gen/source/loader.rb +71 -0
- data/lib/goon_model_gen/source/named_slice.rb +18 -0
- data/lib/goon_model_gen/source/struct.rb +26 -0
- data/lib/goon_model_gen/source/type.rb +21 -0
- data/lib/goon_model_gen/templates/dsl.rb +60 -0
- data/lib/goon_model_gen/templates/model/enum/01_base.go.erb +7 -0
- data/lib/goon_model_gen/templates/model/enum/02_All.go.erb +5 -0
- data/lib/goon_model_gen/templates/model/enum/02_Map.go.erb +5 -0
- data/lib/goon_model_gen/templates/model/enum/03_String.go.erb +3 -0
- data/lib/goon_model_gen/templates/model/enum/04_NameToValue.go.erb +5 -0
- data/lib/goon_model_gen/templates/model/enum/05_StringTo.go.erb +19 -0
- data/lib/goon_model_gen/templates/model/enum/06_ToString.go.erb +3 -0
- data/lib/goon_model_gen/templates/model/enum/07_Valid.go.erb +4 -0
- data/lib/goon_model_gen/templates/model/goon/01_struct.go.erb +6 -0
- data/lib/goon_model_gen/templates/model/goon/02_Assign.go.erb +16 -0
- data/lib/goon_model_gen/templates/model/goon/03_PrepareToCreate.go.erb +16 -0
- data/lib/goon_model_gen/templates/model/goon/04_PrepareToUpdate.go.erb +8 -0
- data/lib/goon_model_gen/templates/model/goon/05_PrepareFields.go.erb +8 -0
- data/lib/goon_model_gen/templates/model/goon/06_IsPersisted.go.erb +3 -0
- data/lib/goon_model_gen/templates/model/slice/01_base.go.erb +1 -0
- data/lib/goon_model_gen/templates/model/struct/01_struct.go.erb +6 -0
- data/lib/goon_model_gen/version.rb +3 -0
- metadata +189 -0
@@ -0,0 +1,64 @@
|
|
1
|
+
require "goon_model_gen"
|
2
|
+
|
3
|
+
require "goon_model_gen/golang/modifier"
|
4
|
+
|
5
|
+
require "active_support/core_ext/string"
|
6
|
+
|
7
|
+
module GoonModelGen
|
8
|
+
module Golang
|
9
|
+
class Packages < Array
|
10
|
+
|
11
|
+
def name_to_type_map
|
12
|
+
each_with_object({}) do |pkg, d|
|
13
|
+
d.update(pkg.name_to_type_map)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def dup
|
18
|
+
Packages.new.add(*self)
|
19
|
+
end
|
20
|
+
|
21
|
+
def add(*packages)
|
22
|
+
packages.each do |i|
|
23
|
+
self << i
|
24
|
+
end
|
25
|
+
self
|
26
|
+
end
|
27
|
+
|
28
|
+
def detect_by(basename)
|
29
|
+
detect{|pkg| pkg.basename == basename}
|
30
|
+
end
|
31
|
+
|
32
|
+
def resolve_type_names(extra_packages = [])
|
33
|
+
candidates = dup.add(*extra_packages)
|
34
|
+
map(&:types).flatten.each do |t|
|
35
|
+
t.resolve(candidates)
|
36
|
+
end
|
37
|
+
return self
|
38
|
+
end
|
39
|
+
|
40
|
+
# @param type_name [string]
|
41
|
+
# @param [Type]
|
42
|
+
def lookup(type_name)
|
43
|
+
if type_name.include?('.')
|
44
|
+
pkg_name, name = type_name.split('.', 2)
|
45
|
+
pkg = detect_by(pkg_name)
|
46
|
+
return pkg ? pkg.lookup(name) : nil
|
47
|
+
else
|
48
|
+
each do |pkg|
|
49
|
+
t = pkg.lookup(type_name)
|
50
|
+
return t if t
|
51
|
+
end
|
52
|
+
return nil
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def type_for(expression)
|
57
|
+
return nil if expression.blank?
|
58
|
+
Modifier.parse(expression) do |name|
|
59
|
+
lookup(name)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require "goon_model_gen"
|
2
|
+
|
3
|
+
module GoonModelGen
|
4
|
+
module Golang
|
5
|
+
class Sentence
|
6
|
+
attr_reader :template_path
|
7
|
+
attr_reader :type
|
8
|
+
|
9
|
+
# @param template_path [string]
|
10
|
+
# @param type [Type]
|
11
|
+
def initialize(template_path, type)
|
12
|
+
@template_path = template_path
|
13
|
+
@type = type
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require "goon_model_gen"
|
2
|
+
|
3
|
+
require "goon_model_gen/golang/type"
|
4
|
+
require "goon_model_gen/golang/field"
|
5
|
+
|
6
|
+
module GoonModelGen
|
7
|
+
module Golang
|
8
|
+
class Struct < Type
|
9
|
+
attr_accessor :ref_name
|
10
|
+
|
11
|
+
def fields
|
12
|
+
@fields ||= []
|
13
|
+
end
|
14
|
+
|
15
|
+
# @param name [string]
|
16
|
+
# @param t [Type]
|
17
|
+
# @param tags [Hash<String,Array<String>>]
|
18
|
+
def new_field(name, t, tags, options = {})
|
19
|
+
Field.new(name, t, tags, options).tap do |f|
|
20
|
+
f.struct = self
|
21
|
+
fields.push(f)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def id_field
|
26
|
+
fields.detect(&:goon_id)
|
27
|
+
end
|
28
|
+
|
29
|
+
# @param pkgs [Packages]
|
30
|
+
def resolve(pkgs)
|
31
|
+
fields.each do |f|
|
32
|
+
f.resolve(pkgs)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require "goon_model_gen"
|
2
|
+
|
3
|
+
module GoonModelGen
|
4
|
+
module Golang
|
5
|
+
class Type
|
6
|
+
attr_reader :name
|
7
|
+
attr_accessor :package
|
8
|
+
|
9
|
+
# @param name [string]
|
10
|
+
def initialize(name)
|
11
|
+
@name = name
|
12
|
+
end
|
13
|
+
|
14
|
+
# @param pkgs [Packages]
|
15
|
+
def resolve(pkgs)
|
16
|
+
raise NotImplementedError, "#{self.type.name} doesn't implement resolve method"
|
17
|
+
end
|
18
|
+
|
19
|
+
# @return [string]
|
20
|
+
def qualified_name
|
21
|
+
if package && package.name
|
22
|
+
"#{package.name}.#{name}"
|
23
|
+
else
|
24
|
+
name
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
require "goon_model_gen"
|
2
|
+
|
3
|
+
require "goon_model_gen/source/context"
|
4
|
+
require "goon_model_gen/source/struct"
|
5
|
+
require "goon_model_gen/source/enum"
|
6
|
+
require "goon_model_gen/source/named_slice"
|
7
|
+
|
8
|
+
|
9
|
+
require "goon_model_gen/golang/package"
|
10
|
+
require "goon_model_gen/golang/packages"
|
11
|
+
require "goon_model_gen/golang/datastore_supported"
|
12
|
+
|
13
|
+
require "active_support/core_ext/string"
|
14
|
+
|
15
|
+
module GoonModelGen
|
16
|
+
class ModelBuilder
|
17
|
+
attr_reader :base_package_path
|
18
|
+
|
19
|
+
def initialize(base_package_path)
|
20
|
+
@base_package_path = base_package_path
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
# @param context [Source::Context]
|
25
|
+
# @return [Golang::Packages]
|
26
|
+
def build(context)
|
27
|
+
Golang::Packages.new.tap do |r|
|
28
|
+
build_sentences = []
|
29
|
+
context.files.each do |f|
|
30
|
+
package_path = File.join(base_package_path, f.basename)
|
31
|
+
pkg, procs = build_package(package_path, f.types)
|
32
|
+
r << pkg
|
33
|
+
build_sentences.concat(procs)
|
34
|
+
end
|
35
|
+
r.resolve_type_names(Golang::DatastoreSupported.packages)
|
36
|
+
build_sentences.each(&:call)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# @param package_path [String]
|
41
|
+
# @param types [Array<Source::Type>]
|
42
|
+
# @return [Golang::Package, Array<Proc>]
|
43
|
+
def build_package(package_path, types)
|
44
|
+
procs = []
|
45
|
+
pkg = Golang::Package.new(package_path).tap do |pkg|
|
46
|
+
types.each do |t|
|
47
|
+
case t
|
48
|
+
when Source::Struct then
|
49
|
+
go_type = build_struct(t, pkg)
|
50
|
+
template = (t.id_name && t.id_type) ? "model/goon" : "model/struct"
|
51
|
+
procs << Proc.new{ build_sentences(template, t, go_type) }
|
52
|
+
when Source::Enum then
|
53
|
+
go_type = pkg.new_enum(t.name, t.base_type, t.map)
|
54
|
+
procs << Proc.new{ build_sentences("model/enum", t, go_type) }
|
55
|
+
when Source::NamedSlice then
|
56
|
+
go_type = pkg.new_named_slice(t.name, t.base_type_name)
|
57
|
+
procs << Proc.new{ build_sentences("model/slice", t, go_type) }
|
58
|
+
else
|
59
|
+
raise "Unsupported type #{t.class.name} #{t.inspect}"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
return pkg, procs
|
64
|
+
end
|
65
|
+
|
66
|
+
# @param t [Source::Struct]
|
67
|
+
# @param pkg [Golang::Package]
|
68
|
+
# @return [Golang::Struct]
|
69
|
+
def build_struct(t, pkg)
|
70
|
+
pkg.new_struct(t.name).tap do |s|
|
71
|
+
s.ref_name = t.ref_name
|
72
|
+
if t.id_name && t.id_type
|
73
|
+
tags = {
|
74
|
+
'goon' => ['id'],
|
75
|
+
'datastore' => ['-'],
|
76
|
+
'json' => [t.id_name.underscore],
|
77
|
+
}
|
78
|
+
s.new_field(t.id_name, t.id_type, tags, goon_id: true)
|
79
|
+
end
|
80
|
+
t.fields.each do |f|
|
81
|
+
s.new_field(f.name, f.type_name, f.build_tags)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
|
87
|
+
# @param template_base [string]
|
88
|
+
# @param t [Source::Struct]
|
89
|
+
# @param go_type [Golang::Type]
|
90
|
+
def build_sentences(template_base, t, go_type)
|
91
|
+
m2t = method_to_template_for(template_base)
|
92
|
+
t.generators ||= default_generators_for(template_base)
|
93
|
+
t.generators.each do |name, suffix|
|
94
|
+
next if !suffix
|
95
|
+
template = m2t[name]
|
96
|
+
parts = [t.name.underscore]
|
97
|
+
custom_suffix = false
|
98
|
+
if suffix.is_a?(String)
|
99
|
+
parts << suffix
|
100
|
+
custom_suffix = true
|
101
|
+
end
|
102
|
+
filename = parts.join('_') << '.go'
|
103
|
+
go_type.package.find_or_new_file(filename).tap do |file|
|
104
|
+
file.custom_suffix = custom_suffix
|
105
|
+
file.new_sentence(File.join(template_base, template), go_type)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def default_generators_for(template_base)
|
111
|
+
method_to_template_for(template_base).keys.
|
112
|
+
each_with_object({}){|name, d| d[name] = true }
|
113
|
+
end
|
114
|
+
|
115
|
+
def method_to_template_map
|
116
|
+
@method_to_template_map ||= {}
|
117
|
+
end
|
118
|
+
|
119
|
+
def method_to_template_for(template_base)
|
120
|
+
method_to_template_map[template_base] ||=
|
121
|
+
begin
|
122
|
+
templates_for(template_base).each_with_object({}) do |filename, d|
|
123
|
+
m = filename.sub(/\A\d+\_/, '').sub(/\.go\.erb\z/, '')
|
124
|
+
d[m] = filename
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def templates_for(template_base)
|
130
|
+
base_dir = File.join(File.expand_path('../templates', __FILE__), template_base)
|
131
|
+
Dir.chdir(base_dir) do
|
132
|
+
Dir.glob('*.go.erb')
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
end
|
137
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require "goon_model_gen"
|
2
|
+
|
3
|
+
require "goon_model_gen/source/type"
|
4
|
+
|
5
|
+
module GoonModelGen
|
6
|
+
module Source
|
7
|
+
class Enum < Type
|
8
|
+
attr_reader :base_type, :map
|
9
|
+
|
10
|
+
# @param name [String]
|
11
|
+
# @param base_type [String]
|
12
|
+
# @param map [Hash<Object,String>]
|
13
|
+
def initialize(name, base_type, map)
|
14
|
+
super(name)
|
15
|
+
@base_type = base_type
|
16
|
+
@map = map
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require "goon_model_gen"
|
2
|
+
|
3
|
+
require "goon_model_gen/source/contextual"
|
4
|
+
|
5
|
+
module GoonModelGen
|
6
|
+
module Source
|
7
|
+
class Field
|
8
|
+
include Contextual
|
9
|
+
|
10
|
+
attr_reader :name, :type_name
|
11
|
+
attr_reader :required
|
12
|
+
attr_reader :unique
|
13
|
+
attr_reader :tags # Hash<string,Array[string]> ex. for datastore, validate, json, etc...
|
14
|
+
|
15
|
+
# @param name [String]
|
16
|
+
# @param attrs [Hash<string,Object>]
|
17
|
+
def initialize(name, attrs)
|
18
|
+
@name = name
|
19
|
+
@type_name = attrs['type']
|
20
|
+
@required = attrs['required']
|
21
|
+
@unique = attrs['unique']
|
22
|
+
@tags = attrs['tags']
|
23
|
+
end
|
24
|
+
|
25
|
+
def build_tags
|
26
|
+
r = {}
|
27
|
+
(tags || {}).each do |key, val|
|
28
|
+
r[key] = val.is_a?(Array) ? val.dup : [val]
|
29
|
+
end
|
30
|
+
r['json'] ||= [name.underscore]
|
31
|
+
if required
|
32
|
+
r['validate'] ||= []
|
33
|
+
r['validate'] << 'required'
|
34
|
+
else
|
35
|
+
r['json'] << 'omitempty'
|
36
|
+
end
|
37
|
+
return r
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require "goon_model_gen"
|
2
|
+
|
3
|
+
require "goon_model_gen/source/contextual"
|
4
|
+
require "goon_model_gen/source/struct"
|
5
|
+
require "goon_model_gen/source/enum"
|
6
|
+
require "goon_model_gen/source/named_slice"
|
7
|
+
|
8
|
+
module GoonModelGen
|
9
|
+
module Source
|
10
|
+
class File
|
11
|
+
include Contextual
|
12
|
+
|
13
|
+
attr_reader :path
|
14
|
+
attr_reader :types
|
15
|
+
|
16
|
+
# @param path [string]
|
17
|
+
def initialize(path)
|
18
|
+
@path = path
|
19
|
+
@types = []
|
20
|
+
end
|
21
|
+
|
22
|
+
def basename
|
23
|
+
::File.basename(path, '.*')
|
24
|
+
end
|
25
|
+
|
26
|
+
# @param name [string]
|
27
|
+
# @return [Struct]
|
28
|
+
def new_struct(name)
|
29
|
+
Struct.new(name).tap do |s|
|
30
|
+
s.context = self.context
|
31
|
+
types.push(s)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# @param name [string]
|
36
|
+
# @param base_type [String]
|
37
|
+
# @param map [Hash<Object,Hash>] elements of enum from YAML
|
38
|
+
# @return [Enum]
|
39
|
+
def new_enum(name, base_type, map)
|
40
|
+
Enum.new(name, base_type, map).tap do |t|
|
41
|
+
t.context = self.context
|
42
|
+
types.push(t)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# @param name [string]
|
47
|
+
# @param base_type_name [string]
|
48
|
+
# @return [Slice]
|
49
|
+
def new_named_slice(name, base_type_name)
|
50
|
+
NamedSlice.new(name, base_type_name).tap do |s|
|
51
|
+
s.context = self.context
|
52
|
+
types.push(s)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|