goon_model_gen 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +13 -0
  3. data/.rspec +3 -0
  4. data/.travis.yml +7 -0
  5. data/CODE_OF_CONDUCT.md +74 -0
  6. data/Gemfile +10 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +44 -0
  9. data/Rakefile +6 -0
  10. data/bin/console +14 -0
  11. data/bin/setup +8 -0
  12. data/exe/goon_model_gen +10 -0
  13. data/goon_model_gen.gemspec +41 -0
  14. data/lib/goon_model_gen.rb +6 -0
  15. data/lib/goon_model_gen/cli.rb +79 -0
  16. data/lib/goon_model_gen/config.rb +83 -0
  17. data/lib/goon_model_gen/generator.rb +111 -0
  18. data/lib/goon_model_gen/golang.rb +22 -0
  19. data/lib/goon_model_gen/golang/builtin.rb +31 -0
  20. data/lib/goon_model_gen/golang/datastore_supported.rb +48 -0
  21. data/lib/goon_model_gen/golang/enum.rb +26 -0
  22. data/lib/goon_model_gen/golang/field.rb +41 -0
  23. data/lib/goon_model_gen/golang/file.rb +27 -0
  24. data/lib/goon_model_gen/golang/modifier.rb +65 -0
  25. data/lib/goon_model_gen/golang/named_slice.rb +24 -0
  26. data/lib/goon_model_gen/golang/package.rb +83 -0
  27. data/lib/goon_model_gen/golang/packages.rb +64 -0
  28. data/lib/goon_model_gen/golang/predeclared_type.rb +16 -0
  29. data/lib/goon_model_gen/golang/sentence.rb +17 -0
  30. data/lib/goon_model_gen/golang/struct.rb +38 -0
  31. data/lib/goon_model_gen/golang/type.rb +29 -0
  32. data/lib/goon_model_gen/model_builder.rb +137 -0
  33. data/lib/goon_model_gen/source/context.rb +17 -0
  34. data/lib/goon_model_gen/source/contextual.rb +9 -0
  35. data/lib/goon_model_gen/source/enum.rb +20 -0
  36. data/lib/goon_model_gen/source/field.rb +41 -0
  37. data/lib/goon_model_gen/source/file.rb +58 -0
  38. data/lib/goon_model_gen/source/loader.rb +71 -0
  39. data/lib/goon_model_gen/source/named_slice.rb +18 -0
  40. data/lib/goon_model_gen/source/struct.rb +26 -0
  41. data/lib/goon_model_gen/source/type.rb +21 -0
  42. data/lib/goon_model_gen/templates/dsl.rb +60 -0
  43. data/lib/goon_model_gen/templates/model/enum/01_base.go.erb +7 -0
  44. data/lib/goon_model_gen/templates/model/enum/02_All.go.erb +5 -0
  45. data/lib/goon_model_gen/templates/model/enum/02_Map.go.erb +5 -0
  46. data/lib/goon_model_gen/templates/model/enum/03_String.go.erb +3 -0
  47. data/lib/goon_model_gen/templates/model/enum/04_NameToValue.go.erb +5 -0
  48. data/lib/goon_model_gen/templates/model/enum/05_StringTo.go.erb +19 -0
  49. data/lib/goon_model_gen/templates/model/enum/06_ToString.go.erb +3 -0
  50. data/lib/goon_model_gen/templates/model/enum/07_Valid.go.erb +4 -0
  51. data/lib/goon_model_gen/templates/model/goon/01_struct.go.erb +6 -0
  52. data/lib/goon_model_gen/templates/model/goon/02_Assign.go.erb +16 -0
  53. data/lib/goon_model_gen/templates/model/goon/03_PrepareToCreate.go.erb +16 -0
  54. data/lib/goon_model_gen/templates/model/goon/04_PrepareToUpdate.go.erb +8 -0
  55. data/lib/goon_model_gen/templates/model/goon/05_PrepareFields.go.erb +8 -0
  56. data/lib/goon_model_gen/templates/model/goon/06_IsPersisted.go.erb +3 -0
  57. data/lib/goon_model_gen/templates/model/slice/01_base.go.erb +1 -0
  58. data/lib/goon_model_gen/templates/model/struct/01_struct.go.erb +6 -0
  59. data/lib/goon_model_gen/version.rb +3 -0
  60. 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