balmora 0.0.1

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.
@@ -0,0 +1,17 @@
1
+ class Balmora::Command::Stop < Balmora::Command
2
+
3
+ class Error < StandardError; end
4
+
5
+ def options()
6
+ return super().concat([:status])
7
+ end
8
+
9
+ def verify()
10
+ return true
11
+ end
12
+
13
+ def run()
14
+ raise Balmora::Stop.new(nil, @status)
15
+ end
16
+
17
+ end
@@ -0,0 +1,8 @@
1
+ class Balmora::Command::Yaourt < Balmora::Command::Pacman
2
+
3
+ def initialize(state, command)
4
+ super(state, command)
5
+ @bin = ['yaourt']
6
+ end
7
+
8
+ end
@@ -0,0 +1,190 @@
1
+ require 'json'
2
+
3
+ class Balmora::Config
4
+
5
+ class Error < StandardError; end
6
+
7
+ attr_accessor :file, :dir, :require, :variables
8
+ attr_reader :old, :config
9
+
10
+ def self.factory(state)
11
+ return self.create(state.options[:config], state.variables)
12
+ end
13
+
14
+ def self.create(config = nil, variables = nil)
15
+ if config.nil?()
16
+ config = File.join(Dir.home(), '.config/balmora/balmora.conf')
17
+ end
18
+
19
+ if !File.exist?(config)
20
+ config = File.join('/etc/balmora/balmora.conf')
21
+ end
22
+
23
+ if !File.exist?(config)
24
+ raise Error.new('Config not found in ~/.config/balmora/balmora.conf ' +
25
+ 'and /etc/balmora/balmora.conf; you should create config to ' +
26
+ 'continue or use --config switch')
27
+ end
28
+
29
+ return self.new(config || '~/.config/balmora/balmora.conf', variables)
30
+ end
31
+
32
+ def initialize(path, variables = nil)
33
+ @file = File
34
+ @dir = Dir
35
+
36
+ @variables = variables
37
+ @path = path
38
+ @require = Object.method(:require)
39
+
40
+ @config = nil
41
+ end
42
+
43
+ def get(key = [], options = {})
44
+ if !key.instance_of?(::Array)
45
+ key = [key]
46
+ end
47
+
48
+ result =
49
+ key.
50
+ inject(@config) { |current, part|
51
+ begin
52
+ current = current.fetch(part)
53
+ rescue KeyError
54
+ if options[:default]
55
+ return options[:default]
56
+ end
57
+
58
+ raise "Config value #{key.inspect()} should be defined in " +
59
+ "#{@path.inspect()}"
60
+ end
61
+
62
+ next current
63
+ }
64
+
65
+ result = _deep_clone(result)
66
+ if options[:variables] != false
67
+ result = @variables.inject(result, false)
68
+ end
69
+
70
+ return result
71
+ end
72
+
73
+ def load()
74
+ @old = _deep_clone(@config)
75
+
76
+ Dir.chdir(File.dirname(@path)) {
77
+ @config = _load(@path)
78
+ }
79
+
80
+ @config[:config_dir] = File.dirname(@path)
81
+ @config[:config_path] = @path
82
+
83
+ get(:require, default: [], variables: false).each() { |file|
84
+ @require.call(file)
85
+ }
86
+ end
87
+
88
+ def _load(path)
89
+ contents = @file.read(@file.expand_path(path))
90
+ value = JSON.parse(contents, {symbolize_names: true})
91
+ value = _process(value)
92
+ return value
93
+ rescue => error
94
+ raise Error.new("Failed to read config file #{path}: #{error.to_s()}")
95
+ end
96
+
97
+ def _process(value)
98
+ result =
99
+ if value.instance_of?(::Array)
100
+ _process_array(value)
101
+ elsif value.instance_of?(::Hash)
102
+ _process_hash(value)
103
+ else
104
+ value
105
+ end
106
+
107
+ return result
108
+ end
109
+
110
+ def _process_array(value)
111
+ result = []
112
+ value.each() { |item|
113
+ if item.instance_of?(::Hash) && item.keys() == [:'extend-file']
114
+ files = item[:'extend-file']
115
+ if !files.instance_of?(::Array)
116
+ files = [files]
117
+ end
118
+
119
+ files.each() { |path|
120
+ _glob(path).each() { |file|
121
+ result.concat(_load(file))
122
+ }
123
+ }
124
+ else
125
+ result.push(_process(item))
126
+ end
127
+ }
128
+
129
+ return result
130
+ end
131
+
132
+ def _process_hash(value)
133
+ result = {}
134
+ value.each() { |key, item|
135
+ if key == :'include-file'
136
+ files = item
137
+ if !files.instance_of?(::Array)
138
+ files = [files]
139
+ end
140
+
141
+ files.each() { |path|
142
+ _glob(path).each() { |file|
143
+ result.merge!(_load(file))
144
+ }
145
+ }
146
+ else
147
+ result[key] = _process(item)
148
+ end
149
+ }
150
+
151
+ return result
152
+ end
153
+
154
+ def _glob(file)
155
+ if !file.include?('*')
156
+ return [file]
157
+ end
158
+
159
+ return @dir.glob(file)
160
+ end
161
+
162
+ def _deep_clone(value)
163
+ if value.instance_of?(::Array)
164
+ value = value.collect() { |item|
165
+ _deep_clone(item)
166
+ }
167
+ elsif value.instance_of?(::Hash)
168
+ value = Hash[
169
+ value.collect() { |key, item|
170
+ [_deep_clone(key), _deep_clone(item)]
171
+ }
172
+ ]
173
+ else
174
+ is_clonable = (
175
+ !value.is_a?(::Numeric) &&
176
+ !value.is_a?(::TrueClass) &&
177
+ !value.is_a?(::FalseClass) &&
178
+ !value.is_a?(::NilClass) &&
179
+ !value.is_a?(::Symbol)
180
+ )
181
+
182
+ if is_clonable
183
+ value = value.clone()
184
+ end
185
+ end
186
+
187
+ return value
188
+ end
189
+
190
+ end
@@ -0,0 +1,98 @@
1
+ class Balmora::Context < Balmora::Command
2
+
3
+ def initialize(state, context)
4
+ @state = state
5
+
6
+ @logger = state.logger
7
+ @shell = state.shell
8
+ @config = state.config
9
+ @variables = state.variables
10
+ @balmora = state.balmora
11
+
12
+ @context = context
13
+ end
14
+
15
+ def init()
16
+ if (@context.keys() - [:context] - options()).length != 0
17
+ raise Error.new("Unknown options #{(@context.keys() - [:context] -
18
+ options()).inspect()}")
19
+ end
20
+
21
+ options().each() { |key|
22
+ if self.instance_variable_defined?(:"@#{key}")
23
+ raise Error.new("Can not use #{key} as option")
24
+ end
25
+
26
+ option = @context.fetch(key, nil)
27
+ self.instance_variable_set(:"@#{key}", option)
28
+ }
29
+
30
+ verify()
31
+ end
32
+
33
+ def options()
34
+ return [:operator, :value]
35
+ end
36
+
37
+ def verify()
38
+ if @operator.nil?()
39
+ raise Error.new('"operator" should be defined')
40
+ end
41
+
42
+ if @value.nil?()
43
+ raise Error.new('"value" should be defined')
44
+ end
45
+ end
46
+
47
+ def execute()
48
+ result = run()
49
+
50
+ operator = @operator
51
+
52
+ is_not = false
53
+ if operator.start_with?('not-')
54
+ is_not = true
55
+ operator = operator[4..-1]
56
+ end
57
+
58
+ case operator
59
+ when 'match'
60
+ return _not(is_not, result.match(@value) != nil)
61
+ when 'equal'
62
+ return _not(is_not, result == @value)
63
+ when 'greater'
64
+ return _not(is_not, result > @value)
65
+ when 'greater-or-equal'
66
+ return _not(is_not, result >= @value)
67
+ when 'lesser'
68
+ return _not(is_not, result < @value)
69
+ when 'lesser-or-equal'
70
+ return _not(is_not, result <= @value)
71
+ end
72
+
73
+ raise Error.new("Unknown operator #{operator}")
74
+ rescue => error
75
+ @logger.error("#{error.inspect()}; failed to run " +
76
+ "context: #{@context.inspect()}")
77
+
78
+ raise error
79
+ end
80
+
81
+ def _not(is_not, result)
82
+ if is_not
83
+ return !result
84
+ end
85
+
86
+ return result
87
+ end
88
+
89
+ def run()
90
+ raise Error.new("run should be implemented in subclass")
91
+ end
92
+
93
+ def option(option)
94
+ return @variables.inject(self.instance_variable_get(:"@#{option}"))
95
+ end
96
+
97
+
98
+ end
@@ -0,0 +1,11 @@
1
+ class Balmora::Context::ConfigChanged < Balmora::Context
2
+
3
+ def run()
4
+ if @config.old.nil?() || @config.config.nil?()
5
+ return @config.config == @config.old
6
+ end
7
+
8
+ return JSON.generate(@config.config) != JSON.generate(@config.old)
9
+ end
10
+
11
+ end
@@ -0,0 +1,26 @@
1
+ class Balmora::Context::Exec < Balmora::Context
2
+
3
+ def options()
4
+ return super().concat([:exec])
5
+ end
6
+
7
+ def verify()
8
+ if !@exec
9
+ raise Error.new('"exec" should be defined')
10
+ end
11
+
12
+ super()
13
+ end
14
+
15
+ def run()
16
+ return @shell.run!(_exec(), verbose: false).rstrip()
17
+ end
18
+
19
+ def _exec()
20
+ exec = option(:exec)
21
+ if exec.instance_of?(::String)
22
+ exec = [@shell.expression(exec)]
23
+ end
24
+ end
25
+
26
+ end
@@ -0,0 +1,7 @@
1
+ class Balmora::Context::ExecResult < Balmora::Context::Exec
2
+
3
+ def run()
4
+ return @shell.run(_exec(), verbose: false)[0]
5
+ end
6
+
7
+ end
@@ -0,0 +1,76 @@
1
+ class Balmora::Contexts
2
+
3
+ def self.factory(state)
4
+ return self.new(state.logger, state.extension, state.variables, state)
5
+ end
6
+
7
+ def initialize(logger, extension, variables, state)
8
+ @state = state
9
+ @logger = logger
10
+ @extension = extension
11
+ @variables = variables
12
+ end
13
+
14
+ def check(context)
15
+ if context.nil?()
16
+ return true
17
+ end
18
+
19
+ if context.instance_of?(::Array)
20
+ return _check_array(context)
21
+ end
22
+
23
+ if context.instance_of?(::String)
24
+ context = {
25
+ context: 'exec-result',
26
+ exec: context,
27
+ operator: 'equal',
28
+ value: 0,
29
+ }
30
+ end
31
+
32
+
33
+ context_class = @extension.get(Balmora::Context, context[:context])
34
+ context_instance = context_class.new(@state, context)
35
+ context_instance.init()
36
+
37
+ if !(context_class < Balmora::Context)
38
+ raise Error.new("Context #{context_class} should be subclass of context")
39
+ end
40
+
41
+ context_instance.verify()
42
+ result = context_instance.execute()
43
+ return result
44
+ end
45
+
46
+ def _check_array(contexts)
47
+ operator = :and
48
+ result = true
49
+ contexts.each() { |context|
50
+ if context == 'or'
51
+ operator = :or
52
+ next
53
+ end
54
+
55
+ if operator == :or
56
+ result = result || check(context)
57
+ operator = :and
58
+ else
59
+ if !result
60
+ next
61
+ end
62
+
63
+ result = result && check(context)
64
+ end
65
+ }
66
+
67
+ return result
68
+ end
69
+
70
+ end
71
+
72
+ # [
73
+ # {"context": "test -e folder/file1; echo $?", "operator": "equal", "value": "0"},
74
+ # "or",
75
+ # {"context": "test -e folder/file2; echo $?", "operator": "equal", "value": "0"},
76
+ # ]
@@ -0,0 +1,58 @@
1
+ class Balmora::Extension
2
+
3
+ class Error < StandardError; end
4
+
5
+ attr_accessor :command_constant, :extension_constant
6
+
7
+ def self.factory(state)
8
+ return self.new(state)
9
+ end
10
+
11
+ def initialize(state)
12
+ @command_constant = ::Balmora::Command
13
+ @extension_constant = ::Balmora::Extension
14
+ @state = state
15
+ end
16
+
17
+ def get(namespace, extension)
18
+ class_name =
19
+ extension.
20
+ to_s().
21
+ gsub(/(-|^)(\w)/) { |char | (char[1] || char[0]).upcase() }.
22
+ to_sym()
23
+
24
+ if !namespace.const_defined?(class_name, false)
25
+ raise Error.new("Extension #{class_name.inspect()} not found in " +
26
+ "namespace " + namespace.inspect())
27
+ end
28
+
29
+ return namespace.const_get(class_name, false)
30
+ end
31
+
32
+ def create_command(state, command)
33
+ if command.instance_of?(::String)
34
+ command = {command: 'exec', exec: command}
35
+ end
36
+
37
+ if command[:command].nil?()
38
+ raise Error.new("\"command\" should be defined in command " +
39
+ "#{command.inspect()}")
40
+ end
41
+
42
+ command_name = @state.variables.inject(command[:command])
43
+ command_class = get(@command_constant, command_name)
44
+ if !(command_class < @command_constant)
45
+ raise Error.new("Command should be instance of #{@command_constant}")
46
+ end
47
+
48
+ command_instance = command_class.new(state, command)
49
+
50
+ (command[:extensions] || []).each() { |extension|
51
+ extension_class = get(@extension_constant, extension)
52
+ command_instance.extend(extension_class)
53
+ }
54
+
55
+ return command_instance
56
+ end
57
+
58
+ end