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