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,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,16 @@
1
+ require "goon_model_gen"
2
+
3
+ require "goon_model_gen/golang/type"
4
+
5
+ module GoonModelGen
6
+ module Golang
7
+ class PredeclaredType < Type
8
+
9
+ # @param pkgs [Packages]
10
+ def resolve(pkgs)
11
+ # Do nothing
12
+ end
13
+
14
+ end
15
+ end
16
+ 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,17 @@
1
+ require "goon_model_gen"
2
+
3
+ module GoonModelGen
4
+ module Source
5
+ class Context
6
+ attr_reader :files
7
+
8
+ def initialize
9
+ @files = []
10
+ end
11
+
12
+ # @return [nil|Type]
13
+ def lookup(type_name)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,9 @@
1
+ require "goon_model_gen"
2
+
3
+ module GoonModelGen
4
+ module Source
5
+ module Contextual
6
+ attr_accessor :context
7
+ end
8
+ end
9
+ 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