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,111 @@
|
|
1
|
+
require "goon_model_gen"
|
2
|
+
|
3
|
+
require "goon_model_gen/templates/dsl"
|
4
|
+
require "goon_model_gen/golang"
|
5
|
+
|
6
|
+
module GoonModelGen
|
7
|
+
class Generator
|
8
|
+
include Templates::DSL
|
9
|
+
|
10
|
+
attr_reader :file, :packages
|
11
|
+
attr_reader :templates_dir
|
12
|
+
|
13
|
+
attr_accessor :thor
|
14
|
+
attr_accessor :gofmt_disabled # false/true
|
15
|
+
attr_accessor :version_comment # false/true
|
16
|
+
attr_accessor :force, :skip # false/true
|
17
|
+
attr_accessor :overwrite_custom_file # false/true
|
18
|
+
|
19
|
+
DEFAULT_TEMPLATES_DIR = File.expand_path('../templates', __FILE__)
|
20
|
+
|
21
|
+
# @param file [Golang::File]
|
22
|
+
# @param templates_dir [String]
|
23
|
+
def initialize(file, packages, templates_dir: DEFAULT_TEMPLATES_DIR, thor: nil)
|
24
|
+
@file = file
|
25
|
+
@packages = packages
|
26
|
+
@templates_dir = templates_dir
|
27
|
+
@thor = thor
|
28
|
+
end
|
29
|
+
|
30
|
+
COLORS = {
|
31
|
+
blue: "\e[34m",
|
32
|
+
clear: "\e[0m",
|
33
|
+
}
|
34
|
+
|
35
|
+
def run(variables = {})
|
36
|
+
output_path = File.join(Golang.gopath, 'src', file.package.path, file.name)
|
37
|
+
|
38
|
+
if file.custom_suffix && File.exist?(output_path) && !overwrite_custom_file
|
39
|
+
$stderr.puts("%sKEEP%s %s" % [COLORS[:blue], COLORS[:clear], output_path])
|
40
|
+
return
|
41
|
+
end
|
42
|
+
|
43
|
+
content = execute(variables)
|
44
|
+
|
45
|
+
options = {skip: skip, force: force}
|
46
|
+
thor.create_file(output_path, content, options)
|
47
|
+
end
|
48
|
+
|
49
|
+
def execute(variables = {})
|
50
|
+
variables.each do |key, val|
|
51
|
+
define_singleton_method(key){ val }
|
52
|
+
end
|
53
|
+
|
54
|
+
texts = file.sentences.sort_by(&:template_path).map do |sentence|
|
55
|
+
template_path = File.join(templates_dir, sentence.template_path)
|
56
|
+
|
57
|
+
# local variables used in tempaltes
|
58
|
+
type = sentence.type
|
59
|
+
package = type.package
|
60
|
+
|
61
|
+
erb = ERB.new(File.read(template_path), nil, "-")
|
62
|
+
erb.filename = template_path
|
63
|
+
erb.result(binding).strip
|
64
|
+
end
|
65
|
+
|
66
|
+
r = [
|
67
|
+
header_comments,
|
68
|
+
"package %s" % file.package.name,
|
69
|
+
partitioned_imports(except: [file.package.path]),
|
70
|
+
texts.join("\n\n"),
|
71
|
+
].join("\n\n").strip << "\n"
|
72
|
+
|
73
|
+
r = gofmt(r) unless gofmt_disabled
|
74
|
+
r
|
75
|
+
end
|
76
|
+
|
77
|
+
def header_comments
|
78
|
+
r = []
|
79
|
+
if user_editable?
|
80
|
+
r << "You can edit this file. goa_model_gen doesn't overwrite this file."
|
81
|
+
else
|
82
|
+
r << "DO NOT EDIT this file."
|
83
|
+
end
|
84
|
+
|
85
|
+
if version_comment
|
86
|
+
r << "This code generated by goon_model_gen-#{GoonModelGen::VERSION}"
|
87
|
+
end
|
88
|
+
|
89
|
+
return r.map{|s| "// #{s}" }.join("\n")
|
90
|
+
end
|
91
|
+
|
92
|
+
|
93
|
+
def gofmt(content)
|
94
|
+
# https://docs.ruby-lang.org/ja/2.5.0/class/IO.html#S_POPEN
|
95
|
+
r = IO.popen(["gofmt"], "r+", err: :out) do |io|
|
96
|
+
io.puts(content)
|
97
|
+
io.close_write
|
98
|
+
io.read
|
99
|
+
end
|
100
|
+
return r unless r.empty?
|
101
|
+
raise "gofmt returned empty output:\n#{content}"
|
102
|
+
end
|
103
|
+
|
104
|
+
# @param cfg [Config]
|
105
|
+
def load_config(cfg)
|
106
|
+
[:gofmt_disabled, :version_comment].each do |key|
|
107
|
+
self.send("#{key}=", cfg.send(key))
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require "goon_model_gen"
|
2
|
+
|
3
|
+
require "goon_model_gen/golang/package"
|
4
|
+
require "goon_model_gen/golang/builtin"
|
5
|
+
|
6
|
+
module GoonModelGen
|
7
|
+
module Golang
|
8
|
+
class << self
|
9
|
+
|
10
|
+
def gopath
|
11
|
+
# Instead of $GOPATH
|
12
|
+
`go env GOPATH`.strip
|
13
|
+
end
|
14
|
+
|
15
|
+
# @return [Package]
|
16
|
+
def builtin_package
|
17
|
+
Buildin.package
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require "goon_model_gen"
|
2
|
+
|
3
|
+
require "goon_model_gen/golang/predeclared_type"
|
4
|
+
require "goon_model_gen/golang/package"
|
5
|
+
|
6
|
+
module GoonModelGen
|
7
|
+
module Golang
|
8
|
+
class Builtin < PredeclaredType
|
9
|
+
TYPE_NAMES =
|
10
|
+
%w[bool byte complex128 complex64
|
11
|
+
error float32 float64 int int16 int32 int64 int8
|
12
|
+
rune string uint uint16 uint32 uint64 uint8 uintptr]
|
13
|
+
|
14
|
+
class << self
|
15
|
+
def package
|
16
|
+
@package ||= Package.new(nil).tap do |pkg|
|
17
|
+
instances.each do |i|
|
18
|
+
i.package = pkg
|
19
|
+
pkg.types.push(i)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def instances
|
25
|
+
@instances ||= TYPE_NAMES.map{|name| self.new(name) }
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require "goon_model_gen"
|
2
|
+
|
3
|
+
require "goon_model_gen/golang/predeclared_type"
|
4
|
+
require "goon_model_gen/golang/package"
|
5
|
+
require "goon_model_gen/golang/packages"
|
6
|
+
require "goon_model_gen/golang/builtin"
|
7
|
+
|
8
|
+
module GoonModelGen
|
9
|
+
module Golang
|
10
|
+
class DatastoreSupported < PredeclaredType
|
11
|
+
|
12
|
+
class << self
|
13
|
+
|
14
|
+
def datastore
|
15
|
+
@datastore ||= Package.new('google.golang.org/appengine/datastore').tap do |pkg|
|
16
|
+
pkg.add(self.new('ByteString'))
|
17
|
+
pkg.add(self.new('Key'))
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def time
|
22
|
+
@time ||= Package.new('time').tap do |pkg|
|
23
|
+
pkg.add(self.new('Time'))
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def appengine
|
28
|
+
@appengine ||= Package.new('google.golang.org/appengine').tap do |pkg|
|
29
|
+
pkg.add(self.new('BlobKey'))
|
30
|
+
pkg.add(self.new('GeoPoint'))
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
def packages
|
36
|
+
@packages ||= Packages.new.tap do |pkgs|
|
37
|
+
pkgs << Builtin.package
|
38
|
+
pkgs << datastore
|
39
|
+
pkgs << time
|
40
|
+
pkgs << appengine
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require "goon_model_gen"
|
2
|
+
|
3
|
+
require "goon_model_gen/golang/type"
|
4
|
+
|
5
|
+
module GoonModelGen
|
6
|
+
module Golang
|
7
|
+
class Enum < Type
|
8
|
+
attr_reader :base_type_name, :map
|
9
|
+
attr_reader :base_type
|
10
|
+
|
11
|
+
# @param name [String]
|
12
|
+
# @param base_type_name [String]
|
13
|
+
# @param map [Hash<Object,String>]
|
14
|
+
def initialize(name, base_type_name, map)
|
15
|
+
super(name)
|
16
|
+
@base_type_name = base_type_name
|
17
|
+
@map = map
|
18
|
+
end
|
19
|
+
|
20
|
+
# @param pkgs [Packages]
|
21
|
+
def resolve(pkgs)
|
22
|
+
@base_type = pkgs.type_for(base_type_name) || raise("#{base_type_name.inspect} not found")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require "goon_model_gen"
|
2
|
+
|
3
|
+
module GoonModelGen
|
4
|
+
module Golang
|
5
|
+
class Field
|
6
|
+
attr_reader :name, :type_name
|
7
|
+
attr_reader :tags # Hash<string,Array[string]> ex. for datastore, validate, json, etc...
|
8
|
+
attr_reader :goon_id # true/false
|
9
|
+
attr_reader :type
|
10
|
+
attr_accessor :struct
|
11
|
+
attr_accessor :prepare_method
|
12
|
+
|
13
|
+
def initialize(name, type_name, tags, goon_id: false)
|
14
|
+
@name, @type_name = name, type_name
|
15
|
+
@tags = tags || {}
|
16
|
+
@goon_id = goon_id
|
17
|
+
end
|
18
|
+
|
19
|
+
# @param pkgs [Packages]
|
20
|
+
def resolve(pkgs)
|
21
|
+
@type = pkgs.type_for(type_name) || raise("#{type_name.inspect} not found for #{struct.qualified_name}.#{name}")
|
22
|
+
end
|
23
|
+
|
24
|
+
def tags_string
|
25
|
+
tags.keys.sort.map do |key|
|
26
|
+
val = tags[key]
|
27
|
+
vals = val.is_a?(Array) ? val.join(',') : val.to_s
|
28
|
+
vals.empty? ? nil : "#{key}:\"#{vals}\""
|
29
|
+
end.compact.join(' ')
|
30
|
+
end
|
31
|
+
|
32
|
+
# @param pkg [Package]
|
33
|
+
# @return [string]
|
34
|
+
def definition_in(pkg)
|
35
|
+
type_exp =
|
36
|
+
(type.package.path == pkg.path) ? type.name : type.qualified_name
|
37
|
+
"#{ name } #{ type_exp } `#{ tags_string }`"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require "goon_model_gen"
|
2
|
+
|
3
|
+
require "goon_model_gen/golang/sentence"
|
4
|
+
|
5
|
+
module GoonModelGen
|
6
|
+
module Golang
|
7
|
+
class File
|
8
|
+
attr_reader :package, :name
|
9
|
+
attr_reader :sentences
|
10
|
+
attr_accessor :custom_suffix # false/true
|
11
|
+
|
12
|
+
# @param package [Package]
|
13
|
+
# @param name [string]
|
14
|
+
def initialize(package, name)
|
15
|
+
@package = package
|
16
|
+
@name = name
|
17
|
+
@sentences = []
|
18
|
+
end
|
19
|
+
|
20
|
+
def new_sentence(template_path, type)
|
21
|
+
Sentence.new(template_path, type).tap do |s|
|
22
|
+
sentences.push(s)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require "goon_model_gen"
|
2
|
+
|
3
|
+
require "goon_model_gen/golang/type"
|
4
|
+
|
5
|
+
module GoonModelGen
|
6
|
+
module Golang
|
7
|
+
class Modifier < Type
|
8
|
+
attr_reader :prefix, :target_name
|
9
|
+
attr_reader :target
|
10
|
+
def initialize(prefix, target)
|
11
|
+
@prefix = prefix
|
12
|
+
case target
|
13
|
+
when Type
|
14
|
+
@target = target
|
15
|
+
@target_name = target.name
|
16
|
+
else
|
17
|
+
@target = nil
|
18
|
+
@target_name = target.to_s
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def package
|
23
|
+
target.package
|
24
|
+
end
|
25
|
+
|
26
|
+
def name
|
27
|
+
prefix + target.name
|
28
|
+
end
|
29
|
+
|
30
|
+
def qualified_name
|
31
|
+
prefix + target.qualified_name
|
32
|
+
end
|
33
|
+
|
34
|
+
# @param pkgs [Packages]
|
35
|
+
def resolve(pkgs)
|
36
|
+
return if target
|
37
|
+
@target = pkgs.type_for(target_name) || raise("#{target_name.inspect} not found")
|
38
|
+
end
|
39
|
+
|
40
|
+
PATTERN = /\A\*|\A\[\d*\]/
|
41
|
+
|
42
|
+
class << self
|
43
|
+
# @param s [String]
|
44
|
+
# @return [Proc]
|
45
|
+
def parse(s)
|
46
|
+
parts = parse_expression(s)
|
47
|
+
t = yield(parts.shift)
|
48
|
+
return nil unless t
|
49
|
+
parts.each do |part|
|
50
|
+
t = new(part, t)
|
51
|
+
end
|
52
|
+
t
|
53
|
+
end
|
54
|
+
|
55
|
+
# @param s [String]
|
56
|
+
# @return [Array<String>]
|
57
|
+
def parse_expression(s)
|
58
|
+
m = s.match(PATTERN)
|
59
|
+
m.nil? ? [s] : parse_expression(s.sub(PATTERN, '')) + [m[0]]
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require "goon_model_gen"
|
2
|
+
|
3
|
+
require "goon_model_gen/golang/type"
|
4
|
+
|
5
|
+
module GoonModelGen
|
6
|
+
module Golang
|
7
|
+
class NamedSlice < Type
|
8
|
+
attr_reader :base_type_name
|
9
|
+
attr_reader :base_type
|
10
|
+
|
11
|
+
# @param name [String]
|
12
|
+
# @param base_type_name [String]
|
13
|
+
def initialize(name, base_type_name)
|
14
|
+
super(name)
|
15
|
+
@base_type_name = base_type_name
|
16
|
+
end
|
17
|
+
|
18
|
+
# @param pkgs [Packages]
|
19
|
+
def resolve(pkgs)
|
20
|
+
@base_type = pkgs.type_for(base_type_name) || raise("#{base_type_name.inspect} not found")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require "goon_model_gen"
|
2
|
+
|
3
|
+
require "goon_model_gen/golang/struct"
|
4
|
+
require "goon_model_gen/golang/enum"
|
5
|
+
require "goon_model_gen/golang/named_slice"
|
6
|
+
require "goon_model_gen/golang/file"
|
7
|
+
|
8
|
+
module GoonModelGen
|
9
|
+
module Golang
|
10
|
+
class Package
|
11
|
+
attr_reader :path
|
12
|
+
attr_reader :types
|
13
|
+
attr_reader :files
|
14
|
+
|
15
|
+
# @param path [String]
|
16
|
+
def initialize(path)
|
17
|
+
@path = path
|
18
|
+
@types = []
|
19
|
+
@files = []
|
20
|
+
end
|
21
|
+
|
22
|
+
def basename
|
23
|
+
@basename ||= path ? ::File.basename(path, '.*') : nil
|
24
|
+
end
|
25
|
+
|
26
|
+
def name
|
27
|
+
@name ||= basename ? basename.gsub(/[\-\_]/, '').downcase : nil
|
28
|
+
end
|
29
|
+
|
30
|
+
def add(type)
|
31
|
+
types.push(type)
|
32
|
+
type.package = self
|
33
|
+
end
|
34
|
+
|
35
|
+
def new_struct(name)
|
36
|
+
Struct.new(name).tap{|s| add(s) }
|
37
|
+
end
|
38
|
+
|
39
|
+
# @param name [string]
|
40
|
+
# @param base_type [String]
|
41
|
+
# @param map [Hash<Object,Hash>] elements of enum from YAML
|
42
|
+
# @return [Enum]
|
43
|
+
def new_enum(name, base_type, map)
|
44
|
+
Enum.new(name, base_type, map).tap{|s| add(s) }
|
45
|
+
end
|
46
|
+
|
47
|
+
# @param name [string]
|
48
|
+
# @param base_type_name [String]
|
49
|
+
# @return [Slice]
|
50
|
+
def new_named_slice(name, base_type_name)
|
51
|
+
NamedSlice.new(name, "[]#{base_type_name}").tap{|s| add(s) }
|
52
|
+
end
|
53
|
+
|
54
|
+
# @param name [string]
|
55
|
+
# @return [File]
|
56
|
+
def find_or_new_file(name)
|
57
|
+
files.detect{|f| f.name == name} || new_file(name)
|
58
|
+
end
|
59
|
+
|
60
|
+
# @param name [string]
|
61
|
+
# @return [File]
|
62
|
+
def new_file(name)
|
63
|
+
File.new(self, name).tap do |f|
|
64
|
+
files.push(f)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# @return [Hash<String,Type>]
|
69
|
+
def name_to_type_map
|
70
|
+
@name_to_type_map ||= types.each_with_object({}) do |t,d|
|
71
|
+
d[t.name] = t
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# @param name [string]
|
76
|
+
# @param [Type]
|
77
|
+
def lookup(name)
|
78
|
+
name_to_type_map[name]
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|