ConfigLMM 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (104) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +3 -0
  3. data/.yardopts +4 -0
  4. data/CHANGELOG.md +5 -0
  5. data/Examples/Android.mm.yaml +8 -0
  6. data/Examples/Apps/Blog.mm.yaml +7 -0
  7. data/Examples/Apps/Jellyfin.mm.yaml +3 -0
  8. data/Examples/Implemented.mm.yaml +155 -0
  9. data/Examples/Keys.ini +7 -0
  10. data/Examples/Linux.mm.yaml +16 -0
  11. data/Examples/Windows.mm.yaml +11 -0
  12. data/Examples/configlmmAuth.sh +26 -0
  13. data/Plugins/Apps/ArchiSteamFarm/ArchiSteamFarm.conf.erb +38 -0
  14. data/Plugins/Apps/ArchiSteamFarm/ArchiSteamFarm.lmm.rb +19 -0
  15. data/Plugins/Apps/IPFS/IPFS.conf.erb +44 -0
  16. data/Plugins/Apps/IPFS/IPFS.lmm.rb +23 -0
  17. data/Plugins/Apps/InfluxDB/InfluxDB.conf.erb +34 -0
  18. data/Plugins/Apps/InfluxDB/InfluxDB.lmm.rb +19 -0
  19. data/Plugins/Apps/Jackett/Jackett.conf.erb +38 -0
  20. data/Plugins/Apps/Jackett/Jackett.lmm.rb +19 -0
  21. data/Plugins/Apps/Jellyfin/Jellyfin.conf.erb +59 -0
  22. data/Plugins/Apps/Jellyfin/Jellyfin.lmm.rb +23 -0
  23. data/Plugins/Apps/Mastodon/Mastodon.conf.erb +81 -0
  24. data/Plugins/Apps/Mastodon/Mastodon.lmm.rb +23 -0
  25. data/Plugins/Apps/Matrix/Matrix.conf.erb +36 -0
  26. data/Plugins/Apps/Matrix/Matrix.lmm.rb +23 -0
  27. data/Plugins/Apps/Netdata/Netdata.conf.erb +37 -0
  28. data/Plugins/Apps/Netdata/Netdata.lmm.rb +23 -0
  29. data/Plugins/Apps/Nextcloud/Nextcloud.conf.erb +165 -0
  30. data/Plugins/Apps/Nextcloud/Nextcloud.lmm.rb +23 -0
  31. data/Plugins/Apps/Nginx/config-lmm/errors.conf +31 -0
  32. data/Plugins/Apps/Nginx/config-lmm/private.conf +6 -0
  33. data/Plugins/Apps/Nginx/config-lmm/proxy.conf +15 -0
  34. data/Plugins/Apps/Nginx/config-lmm/public.conf +3 -0
  35. data/Plugins/Apps/Nginx/config-lmm/ssl.conf +18 -0
  36. data/Plugins/Apps/Nginx/main.conf +30 -0
  37. data/Plugins/Apps/Nginx/nginx.conf +90 -0
  38. data/Plugins/Apps/Nginx/nginx.lmm.rb +62 -0
  39. data/Plugins/Apps/Nginx/proxy.conf.erb +31 -0
  40. data/Plugins/Apps/Odoo/Odoo.conf.erb +44 -0
  41. data/Plugins/Apps/Odoo/Odoo.lmm.rb +23 -0
  42. data/Plugins/Apps/Pterodactyl/Pterodactyl.conf.erb +50 -0
  43. data/Plugins/Apps/Pterodactyl/Pterodactyl.lmm.rb +30 -0
  44. data/Plugins/Apps/Pterodactyl/Wings.conf.erb +38 -0
  45. data/Plugins/Apps/Sunshine/Sunshine.conf.erb +31 -0
  46. data/Plugins/Apps/Sunshine/Sunshine.lmm.rb +21 -0
  47. data/Plugins/Apps/Vaultwarden/Vaultwarden.conf.erb +48 -0
  48. data/Plugins/Apps/Vaultwarden/Vaultwarden.lmm.rb +25 -0
  49. data/Plugins/Apps/bitmagnet/bitmagnet.conf.erb +35 -0
  50. data/Plugins/Apps/bitmagnet/bitmagnet.lmm.rb +19 -0
  51. data/Plugins/Apps/gollum/config.ru +11 -0
  52. data/Plugins/Apps/gollum/gollum.conf.erb +41 -0
  53. data/Plugins/Apps/gollum/gollum.lmm.rb +52 -0
  54. data/Plugins/OS/Linux.lmm.rb +64 -0
  55. data/Plugins/OS/Routers/Aruba/ArubaInstant.lmm.rb +144 -0
  56. data/Plugins/Platforms/GitHub.lmm.rb +57 -0
  57. data/Plugins/Platforms/GoDaddy/GoDaddy.lmm.rb +83 -0
  58. data/Plugins/Platforms/GoDaddy/zone.txt.erb +13 -0
  59. data/Plugins/Platforms/porkbun.lmm.rb +129 -0
  60. data/Plugins/Platforms/porkbun_spec.rb +110 -0
  61. data/Plugins/Services/DNS/AmberBit.lmm.rb +14 -0
  62. data/Plugins/Services/DNS/ArubaItDNS.lmm.rb +14 -0
  63. data/Plugins/Services/DNS/NICLV.lmm.rb +18 -0
  64. data/Plugins/Services/DNS/PowerDNS.lmm.rb +261 -0
  65. data/Plugins/Services/DNS/tonic.lmm.rb +126 -0
  66. data/README.md +337 -0
  67. data/Rakefile +15 -0
  68. data/UNLICENSE +24 -0
  69. data/bin/configlmm +7 -0
  70. data/bin/console +11 -0
  71. data/bin/setup +8 -0
  72. data/lib/ConfigLMM/Framework/plugins/dns.rb +63 -0
  73. data/lib/ConfigLMM/Framework/plugins/errors.rb +23 -0
  74. data/lib/ConfigLMM/Framework/plugins/nginxApp.rb +55 -0
  75. data/lib/ConfigLMM/Framework/plugins/plugin.rb +167 -0
  76. data/lib/ConfigLMM/Framework/plugins/ssh.rb +37 -0
  77. data/lib/ConfigLMM/Framework/plugins/store.rb +57 -0
  78. data/lib/ConfigLMM/Framework/plugins.rb +5 -0
  79. data/lib/ConfigLMM/Framework/registrator.rb +32 -0
  80. data/lib/ConfigLMM/Framework.rb +9 -0
  81. data/lib/ConfigLMM/LMM/plugins.rb +5 -0
  82. data/lib/ConfigLMM/LMM.rb +8 -0
  83. data/lib/ConfigLMM/cli.rb +161 -0
  84. data/lib/ConfigLMM/command.rb +53 -0
  85. data/lib/ConfigLMM/commands/build.rb +41 -0
  86. data/lib/ConfigLMM/commands/cleanup.rb +30 -0
  87. data/lib/ConfigLMM/commands/configsCommand.rb +167 -0
  88. data/lib/ConfigLMM/commands/deploy.rb +39 -0
  89. data/lib/ConfigLMM/commands/diff.rb +45 -0
  90. data/lib/ConfigLMM/commands/list.rb +15 -0
  91. data/lib/ConfigLMM/commands/refresh.rb +46 -0
  92. data/lib/ConfigLMM/commands/types.rb +35 -0
  93. data/lib/ConfigLMM/commands/validate.rb +49 -0
  94. data/lib/ConfigLMM/context.rb +52 -0
  95. data/lib/ConfigLMM/io/configList.rb +98 -0
  96. data/lib/ConfigLMM/io/path.rb +48 -0
  97. data/lib/ConfigLMM/io/source.rb +47 -0
  98. data/lib/ConfigLMM/io.rb +2 -0
  99. data/lib/ConfigLMM/state.rb +78 -0
  100. data/lib/ConfigLMM/utils/filters.rb +126 -0
  101. data/lib/ConfigLMM/version.rb +5 -0
  102. data/lib/ConfigLMM.rb +6 -0
  103. data/sig/ConfigLMM.rbs +4 -0
  104. metadata +485 -0
@@ -0,0 +1,167 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../command'
4
+ require_relative '../utils/filters'
5
+ require_relative '../io/configList'
6
+ require_relative '../context'
7
+ require_relative '../state'
8
+ require_relative '../Framework'
9
+ require_relative '../LMM'
10
+ require 'xdg'
11
+ require 'tmpdir'
12
+
13
+ module ConfigLMM
14
+ module Commands
15
+ class ConfigsCommand < ConfigLMM::Command
16
+
17
+ def initialize(configPaths, options)
18
+ @ConfigPaths = configPaths
19
+ @Options = options
20
+ @Plugins = {}
21
+
22
+ logger do |config|
23
+ config.level = @Options[:level]
24
+ end
25
+
26
+ @Context = Context.new(logger, prompt, ::XDG.new, @Options[:context])
27
+ @State = State.new(logger, prompt)
28
+ @Diff = {}
29
+
30
+ # Load all Plugin files
31
+ Framework::Registrator.registerAll(logger)
32
+
33
+ # Create Plugin instances
34
+ Framework::Store.boot(logger, prompt, @Plugins)
35
+ end
36
+
37
+ def execute
38
+ raise ConfigLMM::CLI::MissingArgument.new("ERROR: No configs specified!\n\n") if @ConfigPaths.empty?
39
+
40
+ options = @Options.dup
41
+ options.delete(:locations)
42
+ options.delete(:things)
43
+ #options[:locationFilter] = Utils::Filters.parseLocationsOption(@Options[:locations], logger)
44
+ #options[:thingFilter] = Utils::Filters.parseThingsOption(@Options[:things], logger)
45
+
46
+ configList = IO::ConfigList.create(@ConfigPaths, logger)
47
+ configList.expand!(options[:locationFilter])
48
+
49
+ @State.load!(configList, options)
50
+
51
+ self.processConfig(configList.toConfig(@Context), options)
52
+ end
53
+
54
+ def plugins
55
+ @Plugins
56
+ end
57
+
58
+ def state
59
+ @State
60
+ end
61
+
62
+ def context
63
+ @Context
64
+ end
65
+
66
+ def findBestProvider(plugins)
67
+ raise 'No providers!' if plugins.empty?
68
+ # TODO FIXME
69
+ # In case of multiple providers that match
70
+ # We should chose best one and save it in state
71
+ plugins.first
72
+ end
73
+
74
+ protected
75
+
76
+ def invokeValidateAction(id, plugin, singleTarget, options)
77
+ actionMethod = plugin.class.actionMethod(singleTarget['Type'], 'Validate')
78
+ plugin.send(actionMethod, id, singleTarget, state, context, options)
79
+ end
80
+
81
+ def invokeRefreshAction(id, plugin, singleTarget, options)
82
+ state.create!
83
+ activeState = state.item(id)
84
+ if activeState[:Type].nil?
85
+ activeState[:Type] = singleTarget['Type'].to_s
86
+ elsif activeState[:Type] != singleTarget['Type'].to_s
87
+ raise Framework::PluginError.new("Unexpected Type #{activeState[:Type].inspect}! Wanted #{singleTarget['Type']}")
88
+ end
89
+ actionMethod = plugin.class.actionMethod(singleTarget['Type'], 'Refresh')
90
+ if plugin.methods.include?(:authenticate)
91
+ result = plugin.authenticate(actionMethod, singleTarget, state, context, options)
92
+ raise Framework::PluginAuthError.new('Failed to authenticate!') unless result
93
+ end
94
+ plugin.send(actionMethod, id, singleTarget, activeState, context, options)
95
+ state.save
96
+ end
97
+
98
+ def invokeDiffAction(id, plugin, singleTarget, options)
99
+ state.create!
100
+ activeState = state.item(id)
101
+ if activeState[:Type].nil?
102
+ activeState[:Type] = singleTarget['Type'].to_s
103
+ elsif activeState[:Type] != singleTarget['Type'].to_s
104
+ raise Framework::PluginError.new("Unexpected Type #{activeState[:Type].inspect}! Wanted #{singleTarget['Type']}")
105
+ end
106
+ actionMethod = plugin.class.actionMethod(singleTarget['Type'], 'Diff')
107
+ plugin.send(actionMethod, id, singleTarget, activeState, context, options)
108
+ end
109
+
110
+ def invokeBuildAction(id, plugin, singleTarget, options)
111
+ actionMethod = plugin.class.actionMethod(singleTarget['Type'], 'Build')
112
+ plugin.send(actionMethod, id, singleTarget, state, context, options)
113
+ end
114
+
115
+ def invokeDeployAction(id, plugin, singleTarget, options)
116
+ prompt.warn("Deploying #{singleTarget['ID']}: #{singleTarget['Type'].to_s}")
117
+ state.create!
118
+ activeState = state.item(id)
119
+ if activeState[:Type].nil?
120
+ activeState[:Type] = singleTarget['Type'].to_s
121
+ elsif activeState[:Type] != singleTarget['Type'].to_s
122
+ raise Framework::PluginError.new("Unexpected Type #{activeState[:Type].inspect}! Wanted #{singleTarget['Type']}")
123
+ end
124
+ actionMethod = plugin.class.actionMethod(singleTarget['Type'], 'Deploy')
125
+ if plugin.methods.include?(:authenticate)
126
+ result = plugin.authenticate(actionMethod, singleTarget, state, context, options)
127
+ raise Framework::PluginAuthError.new('Failed to authenticate!') unless result
128
+ end
129
+
130
+ Dir.mktmpdir do |outputDir|
131
+
132
+ if plugin.class.persistBuildDir?
133
+ if options['output'] == '/tmp or ./build'
134
+ options = options.dup
135
+ options['output'] = './build'
136
+ if !options['dry']
137
+ FileUtils.mkdir_p(options['output'])
138
+ end
139
+ end
140
+ else
141
+ options = options.dup
142
+ options['output'] = outputDir
143
+ end
144
+
145
+ if !options['dry']
146
+ # Prevent others accessing it
147
+ FileUtils.chmod(0750, options['output'])
148
+ end
149
+
150
+ if plugin.hasAction?(singleTarget['Type'], :build)
151
+ invokeBuildAction(id, plugin, singleTarget, options)
152
+ end
153
+
154
+ plugin.send(actionMethod, id, singleTarget, activeState, context, options)
155
+ end
156
+ state.save
157
+ end
158
+
159
+
160
+ def checkDiff(id, targetName, stateName, target, activeState)
161
+ if target[targetName] != activeState[stateName]
162
+ @Diff[id] = target
163
+ end
164
+ end
165
+ end
166
+ end
167
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'configsCommand'
4
+
5
+ module ConfigLMM
6
+ module Commands
7
+ class Deploy < ConfigsCommand
8
+ def processConfig(config, options)
9
+ config.each do |id, target|
10
+
11
+ target['Resources'].to_h.each do |id, config|
12
+ IO::ConfigList.processConfig(id, config, target[:Parent])
13
+ processDeploy(IO::ConfigList.normalizeId(id), config, options)
14
+ end
15
+
16
+ processDeploy(id, target, options)
17
+ end
18
+ prompt.ok('Deploy successful!')
19
+ end
20
+
21
+ def processDeploy(id, target, options)
22
+ providers = []
23
+ self.plugins.each do |pluginId, plugin|
24
+ if plugin.hasAction?(target['Type'], :deploy)
25
+ providers << plugin
26
+ end
27
+ end
28
+ message = "Couldn't find action Build for #{target['Type']}"
29
+ if providers.empty?
30
+ logger.debug(message)
31
+ return
32
+ end
33
+
34
+ bestProvider = self.findBestProvider(providers)
35
+ invokeDeployAction(id, bestProvider, target, options)
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'configsCommand'
4
+
5
+ module ConfigLMM
6
+ module Commands
7
+ class Diff < ConfigsCommand
8
+ def processConfig(config, options)
9
+ configDiffs = {}
10
+ config.each do |id, data|
11
+ found = false
12
+ plugins.each do |pluginId, plugin|
13
+ if plugin.hasAction?(data['Type'], :diff)
14
+ invokeDiffAction(id, plugin, data, options)
15
+ configDiffs[id] = plugin.diff unless plugin.diff.empty?
16
+ found = true
17
+ end
18
+ end
19
+ logger.debug("Couldn't find action Diff for type #{data['Type']}") unless found
20
+ end
21
+ showDiff(configDiffs)
22
+ end
23
+
24
+ def showDiff(configDiffs)
25
+ configDiffs.each do |id, diffs|
26
+ prompt.say(' ' + id + ':')
27
+ diffs.each do |name, diff|
28
+ if diff.first.is_a?(Hash)
29
+ prompt.say(' ' + name + ':')
30
+ diff.first.each do |name, value|
31
+ prompt.say('- ' + name + ': ' + value, :color => :red)
32
+ end
33
+ diff.last.each do |name, value|
34
+ prompt.say('+ ' + name + ': ' + value, :color => :green)
35
+ end
36
+ else
37
+ prompt.say('- ' + name + ': ' + diff.first, :color => :red)
38
+ prompt.say('+ ' + name + ': ' + diff.last, :color => :green)
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'configsCommand'
4
+
5
+ module ConfigLMM
6
+ module Commands
7
+ class List < ConfigsCommand
8
+ def processConfig(config, options)
9
+ config.each do |id, data|
10
+ prompt.say("#{data['Name']}: #{data['Type']}")
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'configsCommand'
4
+
5
+ module ConfigLMM
6
+ module Commands
7
+ class Refresh < ConfigsCommand
8
+ def processConfig(config, options)
9
+ errors = 0
10
+ config.each do |id, target|
11
+
12
+ target['Resources'].to_h.each do |id, target|
13
+ IO::ConfigList.processConfig(id, target, target[:Parent])
14
+ processRefresh(IO::ConfigList.normalizeId(id), target, options)
15
+ end
16
+
17
+ errors += processRefresh(id, target, options)
18
+ end
19
+ if errors.zero?
20
+ prompt.ok('Refresh successful!')
21
+ else
22
+ prompt.error('Encountered issue while refreshing state!')
23
+ end
24
+ end
25
+
26
+ def processRefresh(id, target, options)
27
+ errors = 0
28
+ found = false
29
+ self.plugins.each do |pluginId, plugin|
30
+ if plugin.hasAction?(target['Type'], :refresh)
31
+ begin
32
+ invokeRefreshAction(id, plugin, target, options)
33
+ rescue Framework::PluginError => e
34
+ logger.error(e)
35
+ errors += 1
36
+ end
37
+ found = true
38
+ end
39
+ end
40
+ # We allow plugins without refresh action
41
+ logger.debug("Couldn't find action Refresh for type #{target['Type']}") unless found
42
+ errors
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../command'
4
+ require_relative '../Framework'
5
+ require 'set'
6
+ require 'yaml'
7
+
8
+ module ConfigLMM
9
+ module Commands
10
+ class Types < ConfigLMM::Command
11
+ def initialize(options)
12
+ logger do |config|
13
+ config.level = options[:level]
14
+ end
15
+
16
+ # Load all Plugin files
17
+ Framework::Registrator.registerAll(logger)
18
+ end
19
+
20
+ def execute
21
+ types = {}
22
+ Framework::Store.plugins.each do |plugin|
23
+ plugin.instance_methods.each do |method|
24
+ if match = method.match('^action(\w+)(Validate|Build|Refresh|Diff|Deploy)$')
25
+ type = match[1]
26
+ types[type] ||= []
27
+ types[type] << match[2]
28
+ end
29
+ end
30
+ end
31
+ prompt.say(YAML.dump({ 'Types' => types.sort.to_h }))
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'configsCommand'
4
+
5
+ module ConfigLMM
6
+ module Commands
7
+ class Validate < ConfigsCommand
8
+ def processConfig(config, options)
9
+
10
+ errors = 0
11
+ config.each do |id, target|
12
+ target['Resources'].to_h.each do |id, target|
13
+ IO::ConfigList.processConfig(id, target, target[:Parent])
14
+ processValidate(IO::ConfigList.normalizeId(id), target, options)
15
+ end
16
+
17
+ errors += 1 unless processValidate(id, target, options)
18
+ end
19
+ if errors.zero?
20
+ prompt.ok('Validation successful, no issues were found!')
21
+ else
22
+ prompt.error('Validation failed, we found some issues!')
23
+ end
24
+ end
25
+
26
+ def processValidate(id, target, options)
27
+ providers = []
28
+ self.plugins.each do |pluginId, plugin|
29
+ if plugin.hasAction?(target['Type'], :validate)
30
+ providers << plugin
31
+ end
32
+ end
33
+ if providers.empty?
34
+ # We allow for validation function to not exist
35
+ true
36
+ else
37
+ errors = []
38
+ providers.each do |provider|
39
+ errors += invokeValidateAction(id, provider, target, options)
40
+ end
41
+ errors.each do |error|
42
+ prompt.error(error)
43
+ end
44
+ errors.empty?
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,52 @@
1
+
2
+ # encoding: UTF-8
3
+ # frozen_string_literal: true
4
+
5
+ require 'yaml'
6
+
7
+ module ConfigLMM
8
+ class Context
9
+ CONTEXT_FILE = 'configlmm/context.yaml'
10
+
11
+ def initialize(logger, prompt, xdg, contextFile)
12
+ @Logger = logger
13
+ @Prompt = prompt
14
+ load!(xdg.config_home, contextFile)
15
+ end
16
+
17
+ def likes?(name)
18
+ @Context['Likes'].include?(name)
19
+ end
20
+
21
+ def dislikes?(name)
22
+ @Context['Dislikes'].include?(name)
23
+ end
24
+
25
+ def add(context)
26
+ return unless context
27
+ context['Likes'] ||= []
28
+ context['Dislikes'] ||= []
29
+ @Context['Likes'] += context['Likes']
30
+ @Context['Dislikes'] += context['Dislikes']
31
+ end
32
+
33
+ private
34
+
35
+ def load!(configHome, contextFile)
36
+ @Context = {}
37
+ if (contextFile && !File.exist?(contextFile))
38
+ @Logger.error("Provided Context file doesn't exist: #{contextFile}")
39
+ raise 'Missing Context!'
40
+ end
41
+ if !contextFile
42
+ contextFile = configHome / CONTEXT_FILE
43
+ end
44
+ if (File.exist?(contextFile))
45
+ @Context = YAML.safe_load_file(contextFile, permitted_classes: [Symbol])
46
+ end
47
+ @Context['Likes'] ||= []
48
+ @Context['Dislikes'] ||= []
49
+ end
50
+
51
+ end
52
+ end
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'path'
4
+ require 'find'
5
+ require 'yaml'
6
+
7
+ module ConfigLMM
8
+ module IO
9
+ class ConfigList
10
+ ConfigError = Class.new(RuntimeError)
11
+
12
+ def self.create(targets, logger)
13
+ targets = targets.uniq.select do |target|
14
+ exist = File.exist?(target)
15
+ logger.warn("'#{path}' doesn't exist, ignoring!") unless exist
16
+ exist
17
+ end
18
+ self.new(targets)
19
+ end
20
+
21
+ def initialize(targets)
22
+ @Sources = targets.map do |target|
23
+ Path.new(target)
24
+ end
25
+ end
26
+
27
+ def expand!(locationFilter)
28
+ sources = []
29
+ @Sources.each do |source|
30
+ basePath = source.to_s
31
+ if File.file?(basePath)
32
+ parent = Path.new(File.dirname(File.expand_path(basePath)))
33
+ sources << Path.new(basePath, parent)
34
+ else
35
+ parent = source
36
+ ::Find.find(basePath) do |path|
37
+ next unless Path.isConfig?(path)
38
+ parent = parent.lookupParent(path)
39
+ if File.directory?(path)
40
+ parent = Path.new(path, parent)
41
+ next
42
+ end
43
+ path = Path.new(path, parent)
44
+ sources << path if Utils::Filters.includePath?(path, locationFilter)
45
+ end
46
+ end
47
+ end
48
+ @Sources = sources
49
+ end
50
+
51
+ def self.normalizeId(id)
52
+ # Remove all non-letters but allow Unicode
53
+ id.gsub(/[[:space:]]/, '').upcase
54
+ end
55
+
56
+ def self.processConfig(id, data, parent)
57
+ data['ID'] = id
58
+ if data['Type'].nil?
59
+ raise ConfigError.new("Missing 'Type' field: #{id}!")
60
+ end
61
+ data['Name'] = id unless data.has_key?('Name')
62
+ data['Type'] = data['Type'].to_sym
63
+ data[:Parent] = parent
64
+ data
65
+ end
66
+
67
+ def toConfig(context)
68
+ config = {}
69
+ @Sources.each do |source|
70
+ YAML.safe_load_file(source.to_s, permitted_classes: [Symbol]).each do |id, data|
71
+ normalizedId = self.class.normalizeId(id)
72
+ if id == '_CONTEXT_'
73
+ context.add(data)
74
+ next
75
+ end
76
+
77
+ self.class.processConfig(id, data, source.parent)
78
+
79
+ # TODO FIXME we should deep merge them instead
80
+ raise ConfigError.new("Duplicate ID: #{id} (#{normalizedId}) - #{source}") if config.has_key?(normalizedId)
81
+ config[normalizedId] = data
82
+ end
83
+ #rescue YAML::SyntaxError => error
84
+ # raise ConfigError.new(error)
85
+ end
86
+ config
87
+ end
88
+
89
+ def count
90
+ @Sources.length
91
+ end
92
+
93
+ def to_a
94
+ @Sources
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'pathname'
4
+ require_relative 'source'
5
+
6
+ module ConfigLMM
7
+ module IO
8
+ class Path < Source
9
+ def initialize(path, parent = nil)
10
+ @Path = path.is_a?(Pathname) ? path : Pathname.new(path)
11
+ super(parent)
12
+ end
13
+
14
+ def name
15
+ @Path.basename.to_s
16
+ end
17
+
18
+ def basename
19
+ @Path.basename(@Path.extname).to_s
20
+ end
21
+
22
+ def dirname
23
+ parent.basename unless parent.nil?
24
+ end
25
+
26
+ def self.isConfig?(path)
27
+ return false unless File.extname(path) == '.yaml'
28
+ File.basename(path, '.yaml').end_with?('.mm')
29
+ end
30
+
31
+ def to_s
32
+ @Path.to_s
33
+ end
34
+
35
+ def ==(other)
36
+ self.to_s == other.to_s
37
+ end
38
+
39
+ def lookupParent(path)
40
+ if path.start_with?(self.to_s)
41
+ self
42
+ else
43
+ parent&.lookupParent(path) or raise "Didn't find parent for #{path}"
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ConfigLMM
4
+ module IO
5
+ class Source
6
+
7
+ def initialize(parent = nil)
8
+ unless parent.nil? || parent.is_a?(Source)
9
+ raise "Invalid parent! Must be instance of Source! Got #{parent.inspect}"
10
+ end
11
+ @ID = false
12
+ @Parent = parent
13
+ @Childs = {}
14
+ @Parent.addChild(self) if @Parent.is_a?(Source)
15
+ end
16
+
17
+ def id
18
+ return @ID unless @ID == false
19
+ if @Parent.is_a?(Source)
20
+ @ID = self.name
21
+ @ID = @Parent.id + '/' + @ID if @Parent.id
22
+ else
23
+ @ID = nil
24
+ end
25
+ @ID
26
+ end
27
+
28
+ def parent
29
+ @Parent
30
+ end
31
+
32
+ def [](name)
33
+ raise "Didn't find #{name} in #{self.inspect}" unless @Childs.key?(name)
34
+ @Childs[name]
35
+ end
36
+
37
+ protected
38
+
39
+ def addChild(child)
40
+ raise "Invalid child! Must be instance of Source! Got #{child.inspect}" unless child.is_a?(Source)
41
+ return if @Childs.key?(child.name)
42
+ @Childs[child.name] = child
43
+ end
44
+
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,2 @@
1
+
2
+ require_relative 'io/configList'