bauk-gen 0.0.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.
- 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
|