bauk-gen 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +13 -0
- data/.rspec +3 -0
- data/.travis.yml +7 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +57 -0
- data/LICENSE.txt +21 -0
- data/README.md +78 -0
- data/Rakefile +29 -0
- data/bauk-gen.gemspec +44 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/exe/bauk-gen +7 -0
- data/lib/bauk/gen.rb +100 -0
- data/lib/bauk/gen/config_utils.rb +67 -0
- data/lib/bauk/gen/configs/base.rb +52 -0
- data/lib/bauk/gen/configs/error.rb +10 -0
- data/lib/bauk/gen/configs/files.rb +81 -0
- data/lib/bauk/gen/contents/base_content.rb +29 -0
- data/lib/bauk/gen/contents/erb_content.rb +104 -0
- data/lib/bauk/gen/contents/file_content.rb +22 -0
- data/lib/bauk/gen/contents/string_content.rb +19 -0
- data/lib/bauk/gen/generator.rb +166 -0
- data/lib/bauk/gen/init.rb +57 -0
- data/lib/bauk/gen/inputs/base.rb +19 -0
- data/lib/bauk/gen/inputs/error.rb +10 -0
- data/lib/bauk/gen/inputs/templates.rb +197 -0
- data/lib/bauk/gen/module.rb +87 -0
- data/lib/bauk/gen/outputs/base.rb +21 -0
- data/lib/bauk/gen/outputs/error.rb +10 -0
- data/lib/bauk/gen/outputs/filesystem.rb +85 -0
- data/lib/bauk/gen/version.rb +7 -0
- metadata +180 -0
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bauk/utils/log'
|
4
|
+
require 'bauk/gen/configs/files'
|
5
|
+
require 'fileutils'
|
6
|
+
|
7
|
+
module Bauk
|
8
|
+
module Gen
|
9
|
+
# Class purely used for init
|
10
|
+
module Init
|
11
|
+
include Bauk::Utils::Log
|
12
|
+
def init
|
13
|
+
FileUtils.mkdir_p '.bauk/generator'
|
14
|
+
init_templates.each do |file,content|
|
15
|
+
file = ".bauk/generator/templates/#{name}/#{file}"
|
16
|
+
FileUtils.mkdir_p File.dirname(file.to_s)
|
17
|
+
File.write(file.to_s, content)
|
18
|
+
end
|
19
|
+
init_files.each do |file,content|
|
20
|
+
FileUtils.mkdir_p File.dirname(file.to_s)
|
21
|
+
File.write(file.to_s, content)
|
22
|
+
end
|
23
|
+
init_config_file
|
24
|
+
end
|
25
|
+
|
26
|
+
def init_files
|
27
|
+
{}
|
28
|
+
end
|
29
|
+
|
30
|
+
def init_templates
|
31
|
+
{
|
32
|
+
'exampleGenerated/example..erb': <<~FILE
|
33
|
+
<%= name %>
|
34
|
+
FILE,
|
35
|
+
'example_generated..erb': <<~FILE
|
36
|
+
Name: <%= name %>
|
37
|
+
Description: <%= description %>
|
38
|
+
FILE
|
39
|
+
}
|
40
|
+
end
|
41
|
+
|
42
|
+
def init_config_file
|
43
|
+
file = Bauk::Gen::Configs::Files.new.default_files[0]
|
44
|
+
if File.exist? file
|
45
|
+
log.error "File already exists: #{file}"
|
46
|
+
else
|
47
|
+
init_config = default_config
|
48
|
+
unless init_config[:generators]
|
49
|
+
init_config[:generators] = {}
|
50
|
+
init_config[:generators][name.to_sym] = {}
|
51
|
+
end
|
52
|
+
File.write(file, YAML.dump(init_config))
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bauk/utils/log'
|
4
|
+
|
5
|
+
module Bauk
|
6
|
+
module Gen
|
7
|
+
module Inputs
|
8
|
+
# Base class for inputs
|
9
|
+
# Each input needs to overwrite the following methods:
|
10
|
+
# - input_items(items)
|
11
|
+
# -> Takes a hash of items and adds
|
12
|
+
class Base
|
13
|
+
def input_items(_items)
|
14
|
+
raise NotImplementedError
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,197 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bauk/utils/log'
|
4
|
+
require 'bauk/gen/inputs/base'
|
5
|
+
require 'bauk/gen/contents/erb_content'
|
6
|
+
require 'bauk/gen/contents/file_content'
|
7
|
+
require 'ostruct'
|
8
|
+
require 'deep_merge'
|
9
|
+
|
10
|
+
module Bauk
|
11
|
+
module Gen
|
12
|
+
module Inputs
|
13
|
+
class Templates < Base
|
14
|
+
require 'bauk/utils/array_utils'
|
15
|
+
include Bauk::Utils::Log
|
16
|
+
|
17
|
+
Contents = Bauk::Gen::Contents
|
18
|
+
TEMPLATE_KEYS = %i[name dirs extra_dirs].freeze
|
19
|
+
ATTRIBUTE_VALUE_MAPPINGS = {
|
20
|
+
/^(yes|true)$/ => true,
|
21
|
+
/^(no|false|)$/ => false # Empty string is also false
|
22
|
+
}.freeze
|
23
|
+
|
24
|
+
def initialize(generator, config = {})
|
25
|
+
@generator = generator
|
26
|
+
config[:inputs] ||= {}
|
27
|
+
config[:inputs][:templates] ||= {}
|
28
|
+
template_config = config[:inputs][:templates]
|
29
|
+
@template_name = template_config[:template_name] || generator.name
|
30
|
+
setup_dirs template_config
|
31
|
+
invalid_keys = template_config.keys.reject { |key| TEMPLATE_KEYS.include? key }
|
32
|
+
unless invalid_keys.empty?
|
33
|
+
raise "Invalid keys passed to Bauk::Gen::Inputs::Templates: #{invalid_keys.join ', '}"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def setup_dirs(template_config)
|
38
|
+
template_dirs = template_config[:dirs] || default_template_dirs
|
39
|
+
template_dirs.unshift(*template_config[:extra_dirs]) if template_config.include?(:extra_dirs)
|
40
|
+
log.debug "Checking template dirs: #{template_dirs.join(', ')} (#{@template_name})"
|
41
|
+
@template_dirs = sanitise_dirs template_dirs
|
42
|
+
if @template_dirs.empty?
|
43
|
+
log.error "No template directories found for generator: '#{@generator.name}'. Searched dirs: #{template_dirs.join_custom}"
|
44
|
+
else
|
45
|
+
log.info "Using template dirs: #{@template_dirs}"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def default_template_dirs
|
50
|
+
[
|
51
|
+
'.bauk/generator/templates',
|
52
|
+
'~/.bauk/generator/templates',
|
53
|
+
File.expand_path('../../../../templates', __dir__)
|
54
|
+
]
|
55
|
+
end
|
56
|
+
|
57
|
+
# This function returns the absolute paths of only the provided dirs that exist
|
58
|
+
def sanitise_dirs(dirs)
|
59
|
+
dirs.map do |dir|
|
60
|
+
File.expand_path("#{dir}/#{@template_name}")
|
61
|
+
end.select do |dir|
|
62
|
+
File.directory? dir
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def input_items(items)
|
67
|
+
@template_dirs.each do |dir|
|
68
|
+
input_items_from_dir(items, dir)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def input_items_from_dir(items, base_dir, sub_dir = '', dir_attributes = {})
|
73
|
+
dir = "#{base_dir}/#{sub_dir}"
|
74
|
+
log.debug "Adding items from dir: #{dir}"
|
75
|
+
Dir.entries(dir).reject { |d| %w[. ..].include?(d) || d =~ /\.\.config$/ }.each do |filename|
|
76
|
+
path = sub_dir.empty? ? filename.clone : "#{sub_dir}/#{filename}"
|
77
|
+
full_path = "#{base_dir}/#{path}"
|
78
|
+
name = path.clone.dup
|
79
|
+
attributes = dir_attributes.merge(strip_attributes_from_name!(name))
|
80
|
+
# Check if there is a config file to read in
|
81
|
+
config_file = "#{base_dir}/#{name}..config"
|
82
|
+
if File.exist? config_file
|
83
|
+
log.debug "Loading in config for #{name} from file: #{config_file}"
|
84
|
+
eval File.read(config_file)
|
85
|
+
end
|
86
|
+
if File.directory? full_path
|
87
|
+
next if attributes.key?(:if) and not check_if(attributes[:if])
|
88
|
+
input_items_from_dir(items, base_dir, path, attributes)
|
89
|
+
else
|
90
|
+
if attributes[:foreach]
|
91
|
+
foreach = @generator.config.dig(*attributes[:foreach].split(':').map{|i| i.to_sym})
|
92
|
+
log.debug "Looping through foreach: #{foreach}"
|
93
|
+
if foreach.is_a? Hash
|
94
|
+
foreach.each do |key,value|
|
95
|
+
add_file_to_items items: items, name: name, attributes: attributes, full_path: full_path, config: {key: key, value: value}
|
96
|
+
end
|
97
|
+
elsif foreach.is_a? ::Array
|
98
|
+
foreach.each do |value|
|
99
|
+
add_file_to_items items: items, name: name, attributes: attributes, full_path: full_path, config: {value: value}
|
100
|
+
end
|
101
|
+
else
|
102
|
+
log.debug "Foreach: #{foreach}"
|
103
|
+
raise "Cannot itterate though foreach(#{attributes[:foreach]})"
|
104
|
+
end
|
105
|
+
else
|
106
|
+
add_file_to_items items: items, name: name, attributes: attributes, full_path: full_path
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# Config variable extra complex (extra_if_config) to ensure it does notclash with a key in @generator.config as this causes issues
|
113
|
+
def check_if(if_value, extra_if_config = {})
|
114
|
+
# TODO: Use Openstruct to remove this self-binding config issue
|
115
|
+
b = binding
|
116
|
+
@generator.config.each do |key,value|
|
117
|
+
b.local_variable_set(key.to_sym, value)
|
118
|
+
end
|
119
|
+
extra_if_config.each do |key,value|
|
120
|
+
b.local_variable_set(key.to_sym, value)
|
121
|
+
end
|
122
|
+
if if_value.is_a? String
|
123
|
+
if eval(if_value, b)
|
124
|
+
return true
|
125
|
+
end
|
126
|
+
elsif if_value
|
127
|
+
return true
|
128
|
+
end
|
129
|
+
false
|
130
|
+
end
|
131
|
+
|
132
|
+
def add_file_to_items(items:, name:, attributes:, full_path:, config: {})
|
133
|
+
return if attributes.key?(:if) and not check_if(attributes[:if], config)
|
134
|
+
merged_config = @generator.config.clone
|
135
|
+
merged_config.deep_merge!(config)
|
136
|
+
|
137
|
+
# Substitute any variables in the name
|
138
|
+
dst_name = name.clone
|
139
|
+
name.scan(/%=([^%]*)%/).flatten.each do |path_item|
|
140
|
+
path_value = merged_config.dig(*path_item.split('.').map{|i| i.to_sym})
|
141
|
+
raise "Could not file '#{path_item}' config for '#{name}'" unless path_value
|
142
|
+
dst_name.sub!("%=#{path_item}%", path_value.to_s)
|
143
|
+
end
|
144
|
+
|
145
|
+
log.debug "Adding item: '#{dst_name}'. Attributes: #{attributes}"
|
146
|
+
if items[dst_name]
|
147
|
+
log.warn "Overwriting #{dst_name} with template: #{full_path}"
|
148
|
+
end
|
149
|
+
items[dst_name] = {
|
150
|
+
name: dst_name,
|
151
|
+
attributes: attributes,
|
152
|
+
content: if attributes[:erb]
|
153
|
+
Contents::ErbContent.new(file: full_path, config: merged_config, attributes: attributes)
|
154
|
+
else
|
155
|
+
Contents::FileContent.new(file: full_path, config: merged_config, attributes: attributes)
|
156
|
+
end
|
157
|
+
}
|
158
|
+
end
|
159
|
+
|
160
|
+
def strip_attributes_from_name!(name)
|
161
|
+
return {} unless name =~ /\.\./
|
162
|
+
log.debug("strip_attributes_from_name!(#{name})")
|
163
|
+
|
164
|
+
# First strip any attributes from parent folders as those are merged in by default
|
165
|
+
# If a folder started with .. then that whole folder should be removed
|
166
|
+
name.gsub!(%r{\.\.([^/\.]+\.?)+/}, '/')
|
167
|
+
name.gsub!(%r{^/*}, '')
|
168
|
+
name.gsub!(%r{//*}, '/')
|
169
|
+
|
170
|
+
parts = name.split('/').last().split('..')
|
171
|
+
attr_string = parts.pop
|
172
|
+
name.sub!("..#{attr_string}", '')
|
173
|
+
log.debug("strip_attributes_from_name!.attr_string = #{attr_string}")
|
174
|
+
|
175
|
+
attributes = {}
|
176
|
+
attr_string.split('.').each do |attribute|
|
177
|
+
if attribute =~ /=/
|
178
|
+
vals = attribute.split('=')
|
179
|
+
key = vals.shift.to_sym
|
180
|
+
value = vals.join('=')
|
181
|
+
ATTRIBUTE_VALUE_MAPPINGS.each do |mapping, val|
|
182
|
+
next unless value =~ mapping
|
183
|
+
|
184
|
+
value = val
|
185
|
+
break
|
186
|
+
end
|
187
|
+
attributes[key] = value
|
188
|
+
else
|
189
|
+
attributes[attribute.to_sym] = true
|
190
|
+
end
|
191
|
+
end
|
192
|
+
attributes
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bauk/utils/log'
|
4
|
+
require 'bauk/gen/inputs/templates'
|
5
|
+
require 'bauk/gen/outputs/filesystem'
|
6
|
+
require 'bauk/gen/inputs/error'
|
7
|
+
require 'bauk/gen/configs/files'
|
8
|
+
require 'bauk/gen/config_utils'
|
9
|
+
require 'deep_merge'
|
10
|
+
|
11
|
+
module Bauk
|
12
|
+
module Gen
|
13
|
+
# This class is the base module that all others should extend from.
|
14
|
+
# It contains methods for generating items and creating default config.
|
15
|
+
# It itself is not a valid module as modules need names
|
16
|
+
# All config for a module needs to be in the config modules->#name
|
17
|
+
# It takes 3 arguments:
|
18
|
+
# - Calling generator
|
19
|
+
# - Config generated by the generator
|
20
|
+
# - Map of options
|
21
|
+
# This map contains the possible keys:
|
22
|
+
# - disable_by_default: Whether to not enable this generator unless the
|
23
|
+
# config explicity sets this value to true or a hash.
|
24
|
+
# e.g {modules:{moduleA:true}
|
25
|
+
class Module
|
26
|
+
include Bauk::Utils::Log
|
27
|
+
include ConfigUtils
|
28
|
+
|
29
|
+
def initialize(generator, config, map = {})
|
30
|
+
@generator_config = config
|
31
|
+
@generator = generator
|
32
|
+
@map = map
|
33
|
+
end
|
34
|
+
|
35
|
+
def config
|
36
|
+
return @config if @config
|
37
|
+
@config = default_config.deep_merge! @generator_config
|
38
|
+
@config = @generator_config
|
39
|
+
@config[:modules] ||= {}
|
40
|
+
@config[:modules][name] ||= {}
|
41
|
+
@config[:modules][name] = {} if config[:modules][name].eql? true
|
42
|
+
# Overwrite values with this module values
|
43
|
+
@config = @config.deep_merge! @config[:modules][name]
|
44
|
+
config
|
45
|
+
end
|
46
|
+
|
47
|
+
# Function to tell the calling generator whether this module is active andshould be used
|
48
|
+
# to generate items. It depends on whether the config privided
|
49
|
+
def active?
|
50
|
+
if @map[:disable_by_default] then @config
|
51
|
+
else !@config.eql?(false)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Method to check validate the config being provided to the generator.
|
56
|
+
# This overrride also takes an argument of the generator config in case
|
57
|
+
# default values want to be derived.
|
58
|
+
def validate_config(_generator_config); end
|
59
|
+
|
60
|
+
# This function contains the default list of inputs
|
61
|
+
# It defaults to using the inputs from the root generator
|
62
|
+
def input_generators
|
63
|
+
@generator.input_generators
|
64
|
+
end
|
65
|
+
|
66
|
+
# This function gets the items from the inputs.
|
67
|
+
# It does so by itterating though each input and passing it
|
68
|
+
# the global list of items to add to.
|
69
|
+
def input_items(items)
|
70
|
+
log.info "Inputting items from module: #{name} (#{self.class.name})"
|
71
|
+
input_generators.each do |i_gen|
|
72
|
+
i_gen.new(self, config).input_items(items)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# Method to get generator name from class
|
77
|
+
def name
|
78
|
+
return @name if @name
|
79
|
+
|
80
|
+
@name = underscore(self.class.name.split('::').join('_').sub(/_*module$/i, '')).to_sym
|
81
|
+
raise "Invalid nameless module provided: #{self.class.name}" if @name.empty?
|
82
|
+
|
83
|
+
@name
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bauk/utils/log'
|
4
|
+
require 'fileutils'
|
5
|
+
|
6
|
+
module Bauk
|
7
|
+
module Gen
|
8
|
+
module Outputs
|
9
|
+
# Base class for all outputters
|
10
|
+
# Each outputter needs to implement the following methods:
|
11
|
+
# - output_items(items)
|
12
|
+
class Base
|
13
|
+
include Bauk::Utils::Log
|
14
|
+
|
15
|
+
def initialize(_generator, config = {})
|
16
|
+
config[:outputs] ||= {}
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bauk/utils/log'
|
4
|
+
require 'fileutils'
|
5
|
+
require 'bauk/gen/contents/file_content'
|
6
|
+
require 'bauk/gen/outputs/base'
|
7
|
+
|
8
|
+
module Bauk
|
9
|
+
module Gen
|
10
|
+
module Outputs
|
11
|
+
class Filesystem < Base
|
12
|
+
include Bauk::Utils::Log
|
13
|
+
FILESYSTEM_KEYS = %s(output_base)
|
14
|
+
Contents = Bauk::Gen::Contents
|
15
|
+
|
16
|
+
def initialize(generator, config = {})
|
17
|
+
super generator, config
|
18
|
+
config[:outputs][:filesystem] ||= {}
|
19
|
+
filesystem_config = config[:outputs][:filesystem]
|
20
|
+
@output_base = File.expand_path(filesystem_config[:output_base] || '.')
|
21
|
+
invalid_keys = filesystem_config.keys.reject { |key| FILESYSTEM_KEYS.include? key }
|
22
|
+
unless invalid_keys.empty?
|
23
|
+
raise "Invalid keys passed to Bauk::Gen::Outputs::Filesystem: #{invalid_keys.join ', '}"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def default_template_dirs
|
28
|
+
[
|
29
|
+
'.bauk/generator/templates',
|
30
|
+
'~/.bauk/generator/templates',
|
31
|
+
File.expand_path('../../../../templates', __dir__)
|
32
|
+
]
|
33
|
+
end
|
34
|
+
|
35
|
+
# This function returns the absoluts path of only the provided dirs that exist
|
36
|
+
def sanitise_dirs(dirs)
|
37
|
+
dirs.map do |dir|
|
38
|
+
File.expand_path("#{dir}/#{@template_name}")
|
39
|
+
end.select do |dir|
|
40
|
+
File.directory? dir
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def output_items(items)
|
45
|
+
Dir.chdir(@output_base) do
|
46
|
+
log.info "Outputting items to dir: #{@output_base}"
|
47
|
+
items.each do |name, item|
|
48
|
+
log.debug "Outputting item: #{name}(#{item})"
|
49
|
+
if name =~ %r{/}
|
50
|
+
parent_dir = name.sub(%r{/[^/]*$}, '')
|
51
|
+
FileUtils.mkdir_p parent_dir
|
52
|
+
end
|
53
|
+
if item[:attributes][:merge] and File.exist?(name)
|
54
|
+
merge_item name, item
|
55
|
+
elsif item[:attributes][:overwrite] == false and File.exist?(name)
|
56
|
+
log.info "Not overwriting file: '#{name}'"
|
57
|
+
else
|
58
|
+
output_item name, item
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def merge_item(name, item)
|
65
|
+
if item[:content].respond_to? :merge
|
66
|
+
File.write(name, item[:content].merge(File.read(name)))
|
67
|
+
else
|
68
|
+
log.warn "Content type for '#{name}' does not respond to merge request: #{item[:content]} (Outputting without merge)"
|
69
|
+
output_item name, item
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def output_item(name, item)
|
74
|
+
if item[:content].is_a? Contents::FileContent
|
75
|
+
FileUtils.cp item[:content].file.path, name
|
76
|
+
elsif item[:content].respond_to? :content
|
77
|
+
File.write(name, item[:content].content)
|
78
|
+
else
|
79
|
+
raise "Invalid content found for #{name}: #{item[:content]}"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|