goon_model_gen 0.1.1 → 0.1.2

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 (45) hide show
  1. checksums.yaml +4 -4
  2. data/lib/goon_model_gen/builder/abstract_builder.rb +104 -0
  3. data/lib/goon_model_gen/builder/model_builder.rb +74 -0
  4. data/lib/goon_model_gen/builder/store_builder.rb +45 -0
  5. data/lib/goon_model_gen/builder/validation_builder.rb +32 -0
  6. data/lib/goon_model_gen/cli.rb +38 -11
  7. data/lib/goon_model_gen/config.rb +4 -2
  8. data/lib/goon_model_gen/generator.rb +6 -0
  9. data/lib/goon_model_gen/golang/field.rb +1 -0
  10. data/lib/goon_model_gen/golang/packages.rb +5 -0
  11. data/lib/goon_model_gen/golang/struct.rb +2 -2
  12. data/lib/goon_model_gen/golang/type.rb +5 -0
  13. data/lib/goon_model_gen/source/field.rb +1 -2
  14. data/lib/goon_model_gen/source/file.rb +1 -0
  15. data/lib/goon_model_gen/source/loader.rb +19 -1
  16. data/lib/goon_model_gen/source/type.rb +1 -1
  17. data/lib/goon_model_gen/templates/model/enum/02_Map.go.erb +1 -1
  18. data/lib/goon_model_gen/templates/store/goon/01_Binder.go.erb +10 -0
  19. data/lib/goon_model_gen/templates/store/goon/02_Hook.go.erb +10 -0
  20. data/lib/goon_model_gen/templates/store/goon/03_struct.go.erb +4 -0
  21. data/lib/goon_model_gen/templates/store/goon/04_KindName.go.erb +3 -0
  22. data/lib/goon_model_gen/templates/store/goon/05_Query.go.erb +11 -0
  23. data/lib/goon_model_gen/templates/store/goon/06_Run.go.erb +10 -0
  24. data/lib/goon_model_gen/templates/store/goon/07_All.go.erb +8 -0
  25. data/lib/goon_model_gen/templates/store/goon/08_AllBy.go.erb +24 -0
  26. data/lib/goon_model_gen/templates/store/goon/09_Select.go.erb +9 -0
  27. data/lib/goon_model_gen/templates/store/goon/10_FirstBy.go.erb +17 -0
  28. data/lib/goon_model_gen/templates/store/goon/11_CountBy.go.erb +16 -0
  29. data/lib/goon_model_gen/templates/store/goon/12_ByID.go.erb +20 -0
  30. data/lib/goon_model_gen/templates/store/goon/13_ByKey.go.erb +15 -0
  31. data/lib/goon_model_gen/templates/store/goon/14_Get.go.erb +22 -0
  32. data/lib/goon_model_gen/templates/store/goon/15_IsValidKey.go.erb +20 -0
  33. data/lib/goon_model_gen/templates/store/goon/16_Exist.go.erb +28 -0
  34. data/lib/goon_model_gen/templates/store/goon/17_Create.go.erb +24 -0
  35. data/lib/goon_model_gen/templates/store/goon/18_Update.go.erb +24 -0
  36. data/lib/goon_model_gen/templates/store/goon/19_PutWith.go.erb +28 -0
  37. data/lib/goon_model_gen/templates/store/goon/20_Put.go.erb +17 -0
  38. data/lib/goon_model_gen/templates/store/goon/21_Delete.go.erb +20 -0
  39. data/lib/goon_model_gen/templates/store/goon/22_ValidateUniqueness.go.erb +35 -0
  40. data/lib/goon_model_gen/templates/store/goon/23_Validate.go.erb +14 -0
  41. data/lib/goon_model_gen/templates/validation/error/01_struct.go.erb +4 -0
  42. data/lib/goon_model_gen/templates/validation/error/02_Error.go.erb +4 -0
  43. data/lib/goon_model_gen/version.rb +1 -1
  44. metadata +30 -2
  45. data/lib/goon_model_gen/model_builder.rb +0 -137
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f1bc7f66fb836ee7b30fd1aa1d53853c51fd2de8bf18d3a0675ed769b4e09c35
4
- data.tar.gz: 384a9ed52b7f63d4170f06eb6f3f02652178519e09fad4c2b6985909786ecbef
3
+ metadata.gz: 316458bf5f2033fb2e803fc8cb74b8a8ef80a44eb2adf8bec57862a9283b19ad
4
+ data.tar.gz: 57100fe6b8c389a3f0eb7faa8ea4e7ac063e386c9866d9eb24eeb0c0ea82ef55
5
5
  SHA512:
6
- metadata.gz: 64638b0fdf6cc508399c632ce39e20e34536926f5a02df1999bfdd891c15ba1f5cdea474b772e471ddc80775a19f1fc69f64279406dd26a68e076783b46c5de7
7
- data.tar.gz: 27cca1dcb36984d1b276e6b614dd6acce691138d33e85fe39c79f46d15dfe6c0974877f58022b1169986e4b6305ead73c094aead6ed0a5a10d08cfb85fbf0515
6
+ metadata.gz: 9f52397b1dbb695e7f65826cccb701b9cb171bc861b03c50274fd00d94a3a53b5312022f096bedc2fca6d956c0d18748daa2344c673b47c84cfd0b7bbe7ff582
7
+ data.tar.gz: 8226739cfb3a3404a70eb5814e6d0b7ba8169216850ad509f11493bad632bc3ed0392e4b40da811c06f56cdc5378e10a26b7dd8bbbb8cc04557937e4988b2c58
@@ -0,0 +1,104 @@
1
+ require "goon_model_gen"
2
+
3
+ require "goon_model_gen/golang/packages"
4
+
5
+ module GoonModelGen
6
+ module Builder
7
+ class AbstractBuilder
8
+ attr_reader :base_package_path
9
+
10
+ # @param base_package_path [String]
11
+ def initialize(base_package_path)
12
+ @base_package_path = base_package_path
13
+ end
14
+
15
+ # @param context [Source::Context]
16
+ # @return [Golang::Packages]
17
+ def build(context)
18
+ Golang::Packages.new.tap do |r|
19
+ build_sentences = []
20
+ context.files.each do |f|
21
+ package_path = File.join(base_package_path, f.basename)
22
+ pkg, procs = build_package(package_path, f.types)
23
+ r << pkg
24
+ build_sentences.concat(procs)
25
+ end
26
+ resolve_type_names(r)
27
+ build_sentences.each(&:call)
28
+ end
29
+ end
30
+
31
+ # @param package_path [String]
32
+ # @param types [Array<Source::Type>]
33
+ # @return [Golang::Package, Array<Proc>]
34
+ def build_package(package_path, types)
35
+ raise NotImplementedError, "#{self.type.name} doesn't implement build_package method"
36
+ end
37
+
38
+ # @param pkgs [Golang::Packages]
39
+ def resolve_type_names(pkgs)
40
+ raise NotImplementedError, "#{self.type.name} doesn't implement resolve_type_names method"
41
+ end
42
+
43
+ # @param action [string] directory name under templates directory. ex. model, store...
44
+ # @param kind [string] directory name under the directory specified by action. ex. goon, struct, enum, slice
45
+ # @param t [Source::Struct]
46
+ # @param go_type [Golang::Type]
47
+ def build_sentences(action, kind, t, go_type)
48
+ template_base = File.join(action, kind)
49
+ build_sentences_with(template_base, go_type, t.generators[action])
50
+ end
51
+
52
+ # @param template_base [string] template directory path from templates directory. ex. model/enum, store/goon...
53
+ # @param generators [Hash<string,Object>]
54
+ # @param go_type [Golang::Type]
55
+ def build_sentences_with(template_base, go_type, generators)
56
+ m2t = method_to_template_for(template_base)
57
+ generators ||= default_generators_for(template_base)
58
+ generators.each do |name, suffix|
59
+ next if !suffix
60
+ template = m2t[name]
61
+ raise "No template found for #{name.inspect}" unless template
62
+ parts = [go_type.name.underscore]
63
+ custom_suffix = false
64
+ if suffix.is_a?(String)
65
+ parts << suffix
66
+ custom_suffix = true
67
+ end
68
+ filename = parts.join('_') << '.go'
69
+ go_type.package.find_or_new_file(filename).tap do |file|
70
+ file.custom_suffix = custom_suffix
71
+ file.new_sentence(File.join(template_base, template), go_type)
72
+ end
73
+ end
74
+ end
75
+
76
+ def default_generators_for(template_base)
77
+ method_to_template_for(template_base).keys.
78
+ each_with_object({}){|name, d| d[name] = true }
79
+ end
80
+
81
+ def method_to_template_map
82
+ @method_to_template_map ||= {}
83
+ end
84
+
85
+ def method_to_template_for(template_base)
86
+ method_to_template_map[template_base] ||=
87
+ begin
88
+ templates_for(template_base).each_with_object({}) do |filename, d|
89
+ m = filename.sub(/\A\d+\_/, '').sub(/\.go\.erb\z/, '')
90
+ d[m] = filename
91
+ end
92
+ end
93
+ end
94
+
95
+ def templates_for(template_base)
96
+ base_dir = File.join(File.expand_path('../../templates', __FILE__), template_base)
97
+ Dir.chdir(base_dir) do
98
+ Dir.glob('*.go.erb')
99
+ end
100
+ end
101
+
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,74 @@
1
+ require "goon_model_gen"
2
+
3
+ require "goon_model_gen/builder/abstract_builder"
4
+
5
+ require "goon_model_gen/source/struct"
6
+ require "goon_model_gen/source/enum"
7
+ require "goon_model_gen/source/named_slice"
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
+ module Builder
17
+ class ModelBuilder < AbstractBuilder
18
+
19
+ # @param package_path [String]
20
+ # @param types [Array<Source::Type>]
21
+ # @return [Golang::Package, Array<Proc>]
22
+ def build_package(package_path, types)
23
+ procs = []
24
+ pkg = Golang::Package.new(package_path).tap do |pkg|
25
+ types.each do |t|
26
+ case t
27
+ when Source::Struct then
28
+ go_type = build_struct(t, pkg)
29
+ kind = (t.id_name && t.id_type) ? 'goon' : 'struct'
30
+ procs << Proc.new{ build_sentences('model', kind, t, go_type) }
31
+ when Source::Enum then
32
+ go_type = pkg.new_enum(t.name, t.base_type, t.map)
33
+ procs << Proc.new{ build_sentences('model', 'enum', t, go_type) }
34
+ when Source::NamedSlice then
35
+ go_type = pkg.new_named_slice(t.name, t.base_type_name)
36
+ procs << Proc.new{ build_sentences('model', 'slice', t, go_type) }
37
+ else
38
+ raise "Unsupported type #{t.class.name} #{t.inspect}"
39
+ end
40
+ end
41
+ end
42
+ return pkg, procs
43
+ end
44
+
45
+ # @param pkgs [Golang::Packages]
46
+ def resolve_type_names(pkgs)
47
+ pkgs.resolve_type_names(Golang::DatastoreSupported.packages)
48
+ end
49
+
50
+ # @param t [Source::Struct]
51
+ # @param pkg [Golang::Package]
52
+ # @return [Golang::Struct]
53
+ def build_struct(t, pkg)
54
+ pkg.new_struct(t.name).tap do |s|
55
+ s.ref_name = t.ref_name
56
+ if t.id_name && t.id_type
57
+ tags = {
58
+ 'goon' => ['id'],
59
+ 'datastore' => ['-'],
60
+ 'json' => [t.id_name.underscore],
61
+ }
62
+ s.new_field(t.id_name, t.id_type, tags, goon_id: true)
63
+ end
64
+ t.fields.each do |f|
65
+ s.new_field(f.name, f.type_name, f.build_tags).tap do |field|
66
+ field.unique = f.unique
67
+ end
68
+ end
69
+ end
70
+ end
71
+
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,45 @@
1
+ require "goon_model_gen"
2
+
3
+ require "goon_model_gen/builder/abstract_builder"
4
+
5
+ require "goon_model_gen/source/struct"
6
+
7
+ require "goon_model_gen/golang/package"
8
+ require "goon_model_gen/golang/datastore_supported"
9
+
10
+
11
+ module GoonModelGen
12
+ module Builder
13
+ class StoreBuilder < AbstractBuilder
14
+ attr_reader :model_packages
15
+
16
+ # @param base_package_path [String]
17
+ # @param model_packages [Golang::Packages]
18
+ def initialize(base_package_path, model_packages)
19
+ super(base_package_path)
20
+ @model_packages = model_packages
21
+ end
22
+
23
+ # @param package_path [String]
24
+ # @param types [Array<Source::Type>]
25
+ # @return [Golang::Package, Array<Proc>]
26
+ def build_package(package_path, types)
27
+ procs = []
28
+ pkg = Golang::Package.new(package_path).tap do |pkg|
29
+ types.select{|t| t.is_a?(Source::Struct) && t.id_name && t.id_type }.each do |t|
30
+ store_type = pkg.new_struct(t.name + "Store")
31
+ procs << Proc.new{ build_sentences('store', 'goon', t, store_type) }
32
+ store_type.memo[:model] = model_packages.detect_by(t.file.basename).lookup(t.name)
33
+ end
34
+ end
35
+ return pkg, procs
36
+ end
37
+
38
+ # @param pkgs [Golang::Packages]
39
+ def resolve_type_names(pkgs)
40
+ pkgs.resolve_type_names(Golang::DatastoreSupported.packages.dup.add(model_packages))
41
+ end
42
+
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,32 @@
1
+ require "goon_model_gen"
2
+
3
+ require "goon_model_gen/builder/abstract_builder"
4
+
5
+ require "goon_model_gen/golang/packages"
6
+ require "goon_model_gen/golang/datastore_supported"
7
+
8
+
9
+ module GoonModelGen
10
+ module Builder
11
+ class ValidationBuilder < AbstractBuilder
12
+
13
+ # @return [Golang::Packages]
14
+ def build(*)
15
+ Golang::Packages.new.tap do |r|
16
+ r.new_package(base_package_path).tap do |pkg|
17
+ t = pkg.new_struct('ValidationError')
18
+ pkg.new_file('validation_error.go').tap do |f|
19
+ build_sentences_with('validation/error', t, nil)
20
+ end
21
+ end
22
+ end
23
+ end
24
+
25
+ # @param pkgs [Golang::Packages]
26
+ def resolve_type_names(pkgs)
27
+ pkgs.resolve_type_names(Golang::DatastoreSupported.packages)
28
+ end
29
+
30
+ end
31
+ end
32
+ end
@@ -5,7 +5,9 @@ require "yaml"
5
5
  require "thor"
6
6
 
7
7
  require "goon_model_gen/config"
8
- require "goon_model_gen/model_builder"
8
+ require "goon_model_gen/builder/model_builder"
9
+ require "goon_model_gen/builder/store_builder"
10
+ require "goon_model_gen/builder/validation_builder"
9
11
  require "goon_model_gen/source/loader"
10
12
  require "goon_model_gen/generator"
11
13
 
@@ -30,17 +32,29 @@ module GoonModelGen
30
32
  puts YAML.dump(context)
31
33
  end
32
34
 
33
- desc "build FILE1...", "Build golang objects from source YAML files"
34
- def build(*paths)
35
- packages = build_model_objects(paths)
36
- puts YAML.dump(packages)
37
- end
38
-
39
35
  desc "model FILE1...", "Generate model files from source YAML files"
36
+ option :inspect, type: :boolean, desc: "Don't generate any file and show package objects if given"
40
37
  def model(*paths)
41
38
  packages = build_model_objects(paths)
42
- packages.map(&:files).flatten.each do |f|
43
- new_generator(f, packages).run
39
+ if options[:inspect]
40
+ puts YAML.dump(packages)
41
+ else
42
+ packages.map(&:files).flatten.each do |f|
43
+ new_generator(f, packages).run
44
+ end
45
+ end
46
+ end
47
+
48
+ desc "store FILE1...", "Generate store files from source YAML files"
49
+ option :inspect, type: :boolean, desc: "Don't generate any file and show package objects if given"
50
+ def store(*paths)
51
+ packages = build_store_packages(paths).add(*validation_packages)
52
+ if options[:inspect]
53
+ puts YAML.dump(packages)
54
+ else
55
+ packages.map(&:files).flatten.each do |f|
56
+ new_generator(f, packages).run
57
+ end
44
58
  end
45
59
  end
46
60
 
@@ -59,8 +73,16 @@ module GoonModelGen
59
73
  # @return [Array<Golang::Package>]
60
74
  def build_model_objects(paths)
61
75
  context = Source::Loader.new.process(paths)
62
- b = ModelBuilder.new(cfg.model_package_path)
63
- b.build(context)
76
+ Builder::ModelBuilder.new(cfg.model_package_path).build(context)
77
+ end
78
+
79
+ # @param paths [Array<String>] Source::Context
80
+ # @return [Array<Golang::Package>]
81
+ def build_store_packages(paths)
82
+ context = Source::Loader.new.process(paths)
83
+ model_packages = Builder::ModelBuilder.new(cfg.model_package_path).build(context)
84
+ store_packages = Builder::StoreBuilder.new(cfg.store_package_path, model_packages).build(context)
85
+ return store_packages
64
86
  end
65
87
 
66
88
  # @param f [Golang::File]
@@ -74,6 +96,11 @@ module GoonModelGen
74
96
  g.overwrite_custom_file = options[:overwrite_custom_file]
75
97
  return g
76
98
  end
99
+
100
+ # @return [Golang::Package]
101
+ def validation_packages
102
+ Builder::ValidationBuilder.new(cfg.validation_package_path).build
103
+ end
77
104
  end
78
105
  end
79
106
  end
@@ -14,8 +14,8 @@ module GoonModelGen
14
14
  base_package_path
15
15
  model_dir
16
16
  model_package_path
17
- validator_dir
18
- validator_package_path
17
+ validation_dir
18
+ validation_package_path
19
19
  store_dir
20
20
  store_package_path
21
21
  converter_dir
@@ -31,11 +31,13 @@ module GoonModelGen
31
31
  @gofmt_disabled ||= false
32
32
  @model_dir ||= "./model"
33
33
  @store_dir ||= "./stores"
34
+ @validation_dir ||= "./validation"
34
35
  @converter_dir ||= "./converters"
35
36
  @goa_gen_dir ||= "./gen"
36
37
  @base_package_path ||= default_go_package
37
38
  @model_package_path ||= join_paths(@base_package_path, @model_dir)
38
39
  @store_package_path ||= join_paths(@base_package_path, @store_dir)
40
+ @validation_package_path ||= join_paths(@base_package_path, @validation_dir)
39
41
  @converter_package_path ||= join_paths(@base_package_path, @converter_dir)
40
42
  @goa_gen_package_path ||= join_paths(@base_package_path, @goa_gen_dir)
41
43
  @structs_gen_dir ||= "./cmd/structs"
@@ -41,12 +41,15 @@ module GoonModelGen
41
41
  end
42
42
 
43
43
  content = execute(variables)
44
+ return unless content
44
45
 
45
46
  options = {skip: skip, force: force}
46
47
  thor.create_file(output_path, content, options)
47
48
  end
48
49
 
49
50
  def execute(variables = {})
51
+ return nil if file.sentences.empty?
52
+
50
53
  variables.each do |key, val|
51
54
  define_singleton_method(key){ val }
52
55
  end
@@ -57,6 +60,9 @@ module GoonModelGen
57
60
  # local variables used in tempaltes
58
61
  type = sentence.type
59
62
  package = type.package
63
+ type.memo.each do |key, val|
64
+ define_singleton_method(key){ val }
65
+ end
60
66
 
61
67
  erb = ERB.new(File.read(template_path), nil, "-")
62
68
  erb.filename = template_path
@@ -9,6 +9,7 @@ module GoonModelGen
9
9
  attr_reader :type
10
10
  attr_accessor :struct
11
11
  attr_accessor :prepare_method
12
+ attr_accessor :unique
12
13
 
13
14
  def initialize(name, type_name, tags, goon_id: false)
14
15
  @name, @type_name = name, type_name
@@ -1,5 +1,6 @@
1
1
  require "goon_model_gen"
2
2
 
3
+ require "goon_model_gen/golang/package"
3
4
  require "goon_model_gen/golang/modifier"
4
5
 
5
6
  require "active_support/core_ext/string"
@@ -25,6 +26,10 @@ module GoonModelGen
25
26
  self
26
27
  end
27
28
 
29
+ def new_package(path)
30
+ Package.new(path).tap{|pkg| add(pkg)}
31
+ end
32
+
28
33
  def detect_by(basename)
29
34
  detect{|pkg| pkg.basename == basename}
30
35
  end
@@ -12,8 +12,8 @@ module GoonModelGen
12
12
  @fields ||= []
13
13
  end
14
14
 
15
- # @param name [string]
16
- # @param t [Type]
15
+ # @param name [String]
16
+ # @param t [String]
17
17
  # @param tags [Hash<String,Array<String>>]
18
18
  def new_field(name, t, tags, options = {})
19
19
  Field.new(name, t, tags, options).tap do |f|
@@ -24,6 +24,11 @@ module GoonModelGen
24
24
  name
25
25
  end
26
26
  end
27
+
28
+ def memo
29
+ @memo ||= {}
30
+ end
31
+
27
32
  end
28
33
  end
29
34
  end
@@ -29,8 +29,7 @@ module GoonModelGen
29
29
  end
30
30
  r['json'] ||= [name.underscore]
31
31
  if required
32
- r['validate'] ||= []
33
- r['validate'] << 'required'
32
+ r['validate'] = ['required'] + (r['validate'] || [])
34
33
  else
35
34
  r['json'] << 'omitempty'
36
35
  end
@@ -28,6 +28,7 @@ module GoonModelGen
28
28
  def new_struct(name)
29
29
  Struct.new(name).tap do |s|
30
30
  s.context = self.context
31
+ s.file = self
31
32
  types.push(s)
32
33
  end
33
34
  end
@@ -48,7 +48,25 @@ module GoonModelGen
48
48
  else
49
49
  raise "Unsupported type definition named '#{name}': #{t.inspect}"
50
50
  end
51
- source_type.generators = t['generates']
51
+
52
+ source_type.generators =
53
+ case t['generates']
54
+ when false then Hash.new({}) # Return empty Hash not to generate any file
55
+ when nil, true then Hash.new(nil) # Fulfill by default methods
56
+ when Hash then
57
+ t['generates'].each_with_object({}) do |(k,v), d|
58
+ d[k] =
59
+ case v
60
+ when false then {}
61
+ when nil, true then nil
62
+ when Array then v.each_with_object({}){|i,d| d[i] = true}
63
+ when Hash then v
64
+ else raise "Invalid generates value in #{k.inspect}: #{v.inspect}"
65
+ end
66
+ end
67
+ when Array then t['generates'].each_with_object({}){|i,d| d[i] = true}
68
+ else raise "Invalid generates: #{t['generates'].inspect}"
69
+ end
52
70
  end
53
71
  end
54
72
 
@@ -8,7 +8,7 @@ module GoonModelGen
8
8
  include Contextual
9
9
 
10
10
  attr_reader :name
11
- attr_accessor :package
11
+ attr_accessor :file
12
12
  attr_accessor :generators # key: method_name, value: true|false|string(file_suffix)
13
13
 
14
14
  # @param name [string]
@@ -1,5 +1,5 @@
1
1
  var <%= type.name %>Map = map[<%= type.name %>]string{
2
2
  <%- type.map.each do |key, name| -%>
3
- <%= key.inspect %>: <%= name.inspect %>,
3
+ <%= name.camelize %>: <%= name.inspect %>,
4
4
  <%- end -%>
5
5
  }
@@ -0,0 +1,10 @@
1
+ <%-
2
+ import 'google.golang.org/appengine/datastore'
3
+ import model.package
4
+ -%>
5
+
6
+ type <%= type.name %>Binder interface {
7
+ Query(q *datastore.Query) *datastore.Query
8
+ Visible(m *<%= model.qualified_name %>) bool
9
+ Prepare(m *<%= model.qualified_name %>)
10
+ }
@@ -0,0 +1,10 @@
1
+ <%-
2
+ import 'context'
3
+ import model.package
4
+ -%>
5
+
6
+ type <%= type.name %>Hook interface {
7
+ AfterQuery(ctx context.Context, m *[]*<%= model.qualified_name %>) error
8
+ AfterGet(ctx context.Context, m *<%= model.qualified_name %>) error
9
+ BeforeValidation(ctx context.Context, m *<%= model.qualified_name %>) error
10
+ }
@@ -0,0 +1,4 @@
1
+ type <%= type.name %> struct {
2
+ Binder <%= type.name %>Binder
3
+ Hook <%= type.name %>Hook
4
+ }
@@ -0,0 +1,3 @@
1
+ func (s *<%= type.name %>) KindName() string {
2
+ return "<%= model.name %>"
3
+ }
@@ -0,0 +1,11 @@
1
+ <%-
2
+ import 'google.golang.org/appengine/datastore'
3
+ -%>
4
+
5
+ func (s *<%= type.name %>) Query() *datastore.Query {
6
+ q := datastore.NewQuery(s.KindName())
7
+ if s.Binder != nil {
8
+ q = s.Binder.Query(q)
9
+ }
10
+ return q
11
+ }
@@ -0,0 +1,10 @@
1
+ <%-
2
+ import 'context'
3
+ import 'google.golang.org/appengine/datastore'
4
+ import 'github.com/mjibson/goon'
5
+ -%>
6
+
7
+ func (s *<%= type.name %>) Run(ctx context.Context, q *datastore.Query) *goon.Iterator {
8
+ g := goon.FromContext(ctx)
9
+ return g.Run(q)
10
+ }
@@ -0,0 +1,8 @@
1
+ <%-
2
+ import 'context'
3
+ import model.package
4
+ -%>
5
+
6
+ func (s *<%= type.name %>) All(ctx context.Context) ([]*<%= model.qualified_name %>, error) {
7
+ return s.AllBy(ctx, s.Query())
8
+ }
@@ -0,0 +1,24 @@
1
+ <%-
2
+ import 'context'
3
+ import model.package
4
+ import 'google.golang.org/appengine/datastore'
5
+ import 'google.golang.org/appengine/log'
6
+ -%>
7
+
8
+ func (s *<%= type.name %>) AllBy(ctx context.Context, q *datastore.Query) ([]*<%= model.qualified_name %>, error) {
9
+ g := goon.FromContext(ctx)
10
+ r := []*<%= model.qualified_name %>{}
11
+ _, err := g.GetAll(q.EventualConsistency(), &r)
12
+ if err != nil {
13
+ log.Errorf(ctx, "Failed to AllBy <%= model.qualified_name %> because of %v\n", err)
14
+ return nil, err
15
+ }
16
+
17
+ if s.Hook != nil {
18
+ if err := s.Hook.AfterQuery(ctx, &r); err != nil {
19
+ return nil, err
20
+ }
21
+ }
22
+
23
+ return r, nil
24
+ }
@@ -0,0 +1,9 @@
1
+ <%-
2
+ import 'context'
3
+ import model.package
4
+ import 'google.golang.org/appengine/datastore'
5
+ -%>
6
+
7
+ func (s *<%= type.name %>) Select(ctx context.Context, q *datastore.Query) ([]*<%= model.qualified_name %>, error) {
8
+ return s.AllBy(ctx, q)
9
+ }
@@ -0,0 +1,17 @@
1
+ <%-
2
+ import 'context'
3
+ import model.package
4
+ import 'google.golang.org/appengine/datastore'
5
+ -%>
6
+
7
+ func (s *<%= type.name %>) FirstBy(ctx context.Context, q *datastore.Query) (*<%= model.qualified_name %>, error) {
8
+ r, err := s.AllBy(ctx, q.Limit(1))
9
+ if err != nil {
10
+ return nil, err
11
+ }
12
+ if len(r) > 0 {
13
+ return r[0], nil
14
+ } else {
15
+ return nil, nil
16
+ }
17
+ }
@@ -0,0 +1,16 @@
1
+ <%-
2
+ import 'context'
3
+ import 'google.golang.org/appengine/datastore'
4
+ import 'google.golang.org/appengine/log'
5
+ import 'github.com/mjibson/goon'
6
+ -%>
7
+
8
+ func (s *<%= type.name %>) CountBy(ctx context.Context, q *datastore.Query) (int, error) {
9
+ g := goon.FromContext(ctx)
10
+ c, err := g.Count(q)
11
+ if err != nil {
12
+ log.Errorf(ctx, "Failed to count <%= model.qualified_name %> with %v because of %v\n", q, err)
13
+ return 0, err
14
+ }
15
+ return c, nil
16
+ }
@@ -0,0 +1,20 @@
1
+ <%-
2
+ import 'context'
3
+ import model.package
4
+ -%>
5
+
6
+ func (s *<%= type.name %>) ByID(ctx context.Context, iD int64) (*<%= model.qualified_name %>, error) {
7
+ r := <%= model.qualified_name %>{ID: iD}
8
+ err := s.Get(ctx, &r)
9
+ if err != nil {
10
+ return nil, err
11
+ }
12
+
13
+ if s.Hook != nil {
14
+ if err := s.Hook.AfterGet(ctx, &r); err != nil {
15
+ return nil, err
16
+ }
17
+ }
18
+
19
+ return &r, nil
20
+ }
@@ -0,0 +1,15 @@
1
+ <%-
2
+ import 'context'
3
+ import model.package
4
+ import 'google.golang.org/appengine/datastore'
5
+ import 'google.golang.org/appengine/log'
6
+ -%>
7
+
8
+ func (s *<%= type.name %>) ByKey(ctx context.Context, key *datastore.Key) (*<%= model.qualified_name %>, error) {
9
+ if err := s.IsValidKey(ctx, key); err != nil {
10
+ log.Errorf(ctx, "<%= type.name %>.ByKey got Invalid key: %v because of %v\n", key, err)
11
+ return nil, err
12
+ }
13
+
14
+ return s.ByID(ctx, key.IntID())
15
+ }
@@ -0,0 +1,22 @@
1
+ <%-
2
+ import 'context'
3
+ import model.package
4
+ import 'google.golang.org/appengine/datastore'
5
+ import 'google.golang.org/appengine/log'
6
+ import 'github.com/mjibson/goon'
7
+ -%>
8
+
9
+ func (s *<%= type.name %>) Get(ctx context.Context, m *<%= model.qualified_name %>) error {
10
+ g := goon.FromContext(ctx)
11
+ err := g.Get(m)
12
+ if err != nil {
13
+ log.Errorf(ctx, "Failed to Get <%= model.qualified_name %> because of %v\n", err)
14
+ return err
15
+ }
16
+
17
+ if s.Binder != nil && !s.Binder.Visible(m) {
18
+ return datastore.ErrNoSuchEntity
19
+ }
20
+
21
+ return nil
22
+ }
@@ -0,0 +1,20 @@
1
+ <%-
2
+ import 'context'
3
+ import 'fmt'
4
+ import model.package
5
+ import 'google.golang.org/appengine/datastore'
6
+ import 'google.golang.org/appengine/log'
7
+ import 'github.com/mjibson/goon'
8
+ -%>
9
+
10
+ func (s *<%= type.name %>) IsValidKey(ctx context.Context, key *datastore.Key) error {
11
+ if key == nil {
12
+ return fmt.Errorf("key is nil")
13
+ }
14
+ g := goon.FromContext(ctx)
15
+ expected := g.Kind(&<%= model.qualified_name %>{})
16
+ if key.Kind() != expected {
17
+ return fmt.Errorf("key kind must be %s but was %s", expected, key.Kind())
18
+ }
19
+ return nil
20
+ }
@@ -0,0 +1,28 @@
1
+ <%-
2
+ import 'context'
3
+ import model.package
4
+ import 'google.golang.org/appengine/datastore'
5
+ import 'google.golang.org/appengine/log'
6
+ import 'github.com/mjibson/goon'
7
+ -%>
8
+
9
+ func (s *<%= type.name %>) Exist(ctx context.Context, m *<%= model.qualified_name %>) (bool, error) {
10
+ if m.ID == 0 {
11
+ return false, nil
12
+ }
13
+ g := goon.FromContext(ctx)
14
+ key, err := g.KeyError(m)
15
+ if err != nil {
16
+ log.Errorf(ctx, "Failed to Get Key of %v because of %v\n", m, err)
17
+ return false, err
18
+ }
19
+ _, err = s.ByKey(ctx, key)
20
+ if err == datastore.ErrNoSuchEntity {
21
+ return false, nil
22
+ } else if err != nil {
23
+ log.Errorf(ctx, "Failed to get existance of %v because of %v\n", m, err)
24
+ return false, err
25
+ } else {
26
+ return true, nil
27
+ }
28
+ }
@@ -0,0 +1,24 @@
1
+ <%-
2
+ import 'context'
3
+ import 'fmt'
4
+ import model.package
5
+ import 'google.golang.org/appengine/datastore'
6
+ import 'google.golang.org/appengine/log'
7
+ -%>
8
+
9
+ func (s *<%= type.name %>) Create(ctx context.Context, m *<%= model.qualified_name %>) (*datastore.Key, error) {
10
+ if err := m.PrepareToCreate(); err != nil {
11
+ return nil, err
12
+ }
13
+ return s.PutWith(ctx, m, func() error {
14
+ exist, err := s.Exist(ctx, m)
15
+ if err != nil {
16
+ return err
17
+ }
18
+ if exist {
19
+ log.Errorf(ctx, "Failed to create %v because of another entity has same key\n", m)
20
+ return fmt.Errorf("Duplicate ID error: %q of %v\n", m.ID, m)
21
+ }
22
+ return nil
23
+ })
24
+ }
@@ -0,0 +1,24 @@
1
+ <%-
2
+ import 'context'
3
+ import 'fmt'
4
+ import model.package
5
+ import 'google.golang.org/appengine/datastore'
6
+ import 'google.golang.org/appengine/log'
7
+ -%>
8
+
9
+ func (s *<%= type.name %>) Update(ctx context.Context, m *<%= model.qualified_name %>) (*datastore.Key, error) {
10
+ if err := m.PrepareToUpdate(); err != nil {
11
+ return nil, err
12
+ }
13
+ return s.PutWith(ctx, m, func() error {
14
+ exist, err := s.Exist(ctx, m)
15
+ if err != nil {
16
+ return err
17
+ }
18
+ if !exist {
19
+ log.Errorf(ctx, "Failed to update %v because it doesn't exist\n", m)
20
+ return fmt.Errorf("No data to update %q of %v\n", m.ID, m)
21
+ }
22
+ return nil
23
+ })
24
+ }
@@ -0,0 +1,28 @@
1
+ <%-
2
+ import 'context'
3
+ import model.package
4
+ import 'google.golang.org/appengine/datastore'
5
+ -%>
6
+
7
+ func (s *<%= type.name %>) PutWith(ctx context.Context, m *<%= model.qualified_name %>, f func() error) (*datastore.Key, error) {
8
+ if s.Binder != nil {
9
+ s.Binder.Prepare(m)
10
+ }
11
+
12
+ if s.Hook != nil {
13
+ if err := s.Hook.BeforeValidation(ctx, m); err != nil {
14
+ return nil, err
15
+ }
16
+ }
17
+
18
+ if err := s.Validate(ctx, m); err != nil {
19
+ return nil, err
20
+ }
21
+ if f != nil {
22
+ if err := f(); err != nil {
23
+ return nil, err
24
+ }
25
+ }
26
+
27
+ return s.Put(ctx, m)
28
+ }
@@ -0,0 +1,17 @@
1
+ <%-
2
+ import 'context'
3
+ import model.package
4
+ import 'google.golang.org/appengine/datastore'
5
+ import 'google.golang.org/appengine/log'
6
+ import 'github.com/mjibson/goon'
7
+ -%>
8
+
9
+ func (s *<%= type.name %>) Put(ctx context.Context, m *<%= model.qualified_name %>) (*datastore.Key, error) {
10
+ g := goon.FromContext(ctx)
11
+ key, err := g.Put(m)
12
+ if err != nil {
13
+ log.Errorf(ctx, "Failed to Put %v because of %v\n", m, err)
14
+ return nil, err
15
+ }
16
+ return key, nil
17
+ }
@@ -0,0 +1,20 @@
1
+ <%-
2
+ import 'context'
3
+ import model.package
4
+ import 'google.golang.org/appengine/log'
5
+ import 'github.com/mjibson/goon'
6
+ -%>
7
+
8
+ func (s *<%= type.name %>) Delete(ctx context.Context, m *<%= model.qualified_name %>) error {
9
+ g := goon.FromContext(ctx)
10
+ key, err := g.KeyError(m)
11
+ if err != nil {
12
+ log.Errorf(ctx, "Failed to Get key of %v because of %v\n", m, err)
13
+ return err
14
+ }
15
+ if err := g.Delete(key); err != nil {
16
+ log.Errorf(ctx, "Failed to Delete %v because of %v\n", m, err)
17
+ return err
18
+ }
19
+ return nil
20
+ }
@@ -0,0 +1,35 @@
1
+ <%-
2
+ import 'context'
3
+ import 'fmt'
4
+ import model.package
5
+
6
+ validation_error = packages.type_for('ValidationError')
7
+ raise "ValidationError not found" unless validation_error
8
+ import validation_error.package
9
+ -%>
10
+
11
+ func (s *<%= type.name %>) ValidateUniqueness(ctx context.Context, m *<%= model.qualified_name %>) error {
12
+ conditions := map[string]interface{}{
13
+ <%- model.fields.select(&:unique).each do |field| -%>
14
+ "<%= field.name %>": m.<%= field.name %>,
15
+ <%- end -%>
16
+ }
17
+ for field, value := range conditions {
18
+ q := s.Query().Filter(field+" =", value)
19
+ c, err := s.CountBy(ctx, q)
20
+ if err != nil {
21
+ return err
22
+ }
23
+ b := 0
24
+ if m.IsPersisted() {
25
+ b = 1
26
+ }
27
+ if c > b {
28
+ return &<%= validation_error.qualified_name %>{
29
+ Field: field,
30
+ Message: fmt.Sprintf("%v has already been taken", value),
31
+ }
32
+ }
33
+ }
34
+ return nil
35
+ }
@@ -0,0 +1,14 @@
1
+ <%-
2
+ import 'context'
3
+ import model.package
4
+ -%>
5
+
6
+ func (s *<%= type.name %>) Validate(ctx context.Context, m *<%= model.qualified_name %>) error {
7
+ if err := m.Validate(ctx); err != nil {
8
+ return err
9
+ }
10
+ if err := s.ValidateUniqueness(ctx, m); err != nil {
11
+ return err
12
+ }
13
+ return nil
14
+ }
@@ -0,0 +1,4 @@
1
+ type <%= type.name %> struct {
2
+ Field string
3
+ Message string
4
+ }
@@ -0,0 +1,4 @@
1
+ <%- import 'fmt' -%>
2
+ func (err *<%= type.name %>) Error() string {
3
+ return fmt.Sprintf("Invalid %s: %s", err.Field, err.Message)
4
+ }
@@ -1,3 +1,3 @@
1
1
  module GoonModelGen
2
- VERSION = "0.1.1"
2
+ VERSION = "0.1.2"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: goon_model_gen
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - akm
@@ -143,6 +143,10 @@ files:
143
143
  - exe/goon_model_gen
144
144
  - goon_model_gen.gemspec
145
145
  - lib/goon_model_gen.rb
146
+ - lib/goon_model_gen/builder/abstract_builder.rb
147
+ - lib/goon_model_gen/builder/model_builder.rb
148
+ - lib/goon_model_gen/builder/store_builder.rb
149
+ - lib/goon_model_gen/builder/validation_builder.rb
146
150
  - lib/goon_model_gen/cli.rb
147
151
  - lib/goon_model_gen/config.rb
148
152
  - lib/goon_model_gen/generator.rb
@@ -160,7 +164,6 @@ files:
160
164
  - lib/goon_model_gen/golang/sentence.rb
161
165
  - lib/goon_model_gen/golang/struct.rb
162
166
  - lib/goon_model_gen/golang/type.rb
163
- - lib/goon_model_gen/model_builder.rb
164
167
  - lib/goon_model_gen/source/context.rb
165
168
  - lib/goon_model_gen/source/contextual.rb
166
169
  - lib/goon_model_gen/source/enum.rb
@@ -187,6 +190,31 @@ files:
187
190
  - lib/goon_model_gen/templates/model/goon/06_IsPersisted.go.erb
188
191
  - lib/goon_model_gen/templates/model/slice/01_base.go.erb
189
192
  - lib/goon_model_gen/templates/model/struct/01_struct.go.erb
193
+ - lib/goon_model_gen/templates/store/goon/01_Binder.go.erb
194
+ - lib/goon_model_gen/templates/store/goon/02_Hook.go.erb
195
+ - lib/goon_model_gen/templates/store/goon/03_struct.go.erb
196
+ - lib/goon_model_gen/templates/store/goon/04_KindName.go.erb
197
+ - lib/goon_model_gen/templates/store/goon/05_Query.go.erb
198
+ - lib/goon_model_gen/templates/store/goon/06_Run.go.erb
199
+ - lib/goon_model_gen/templates/store/goon/07_All.go.erb
200
+ - lib/goon_model_gen/templates/store/goon/08_AllBy.go.erb
201
+ - lib/goon_model_gen/templates/store/goon/09_Select.go.erb
202
+ - lib/goon_model_gen/templates/store/goon/10_FirstBy.go.erb
203
+ - lib/goon_model_gen/templates/store/goon/11_CountBy.go.erb
204
+ - lib/goon_model_gen/templates/store/goon/12_ByID.go.erb
205
+ - lib/goon_model_gen/templates/store/goon/13_ByKey.go.erb
206
+ - lib/goon_model_gen/templates/store/goon/14_Get.go.erb
207
+ - lib/goon_model_gen/templates/store/goon/15_IsValidKey.go.erb
208
+ - lib/goon_model_gen/templates/store/goon/16_Exist.go.erb
209
+ - lib/goon_model_gen/templates/store/goon/17_Create.go.erb
210
+ - lib/goon_model_gen/templates/store/goon/18_Update.go.erb
211
+ - lib/goon_model_gen/templates/store/goon/19_PutWith.go.erb
212
+ - lib/goon_model_gen/templates/store/goon/20_Put.go.erb
213
+ - lib/goon_model_gen/templates/store/goon/21_Delete.go.erb
214
+ - lib/goon_model_gen/templates/store/goon/22_ValidateUniqueness.go.erb
215
+ - lib/goon_model_gen/templates/store/goon/23_Validate.go.erb
216
+ - lib/goon_model_gen/templates/validation/error/01_struct.go.erb
217
+ - lib/goon_model_gen/templates/validation/error/02_Error.go.erb
190
218
  - lib/goon_model_gen/version.rb
191
219
  homepage: https://github.com/akm/goon_model_gen
192
220
  licenses:
@@ -1,137 +0,0 @@
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