goon_model_gen 0.1.0

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.
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