kxi 1.0.0 → 1.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.
- checksums.yaml +4 -4
- data/lib/kxi/application/config.rb +178 -0
- data/lib/kxi/application/config_reader.rb +17 -0
- data/lib/kxi/application/event.rb +36 -0
- data/lib/kxi/application/logger.rb +156 -0
- data/lib/kxi/application/version.rb +75 -0
- data/lib/kxi/application/version_expression.rb +70 -0
- data/lib/kxi/cli/argument_values.rb +4 -4
- data/lib/kxi/cli/property_list.rb +49 -0
- data/lib/kxi/cli/table.rb +63 -0
- data/lib/kxi/cli/verb.rb +8 -8
- data/lib/kxi/exceptions/configuration_exception.rb +26 -0
- data/lib/kxi/exceptions/invalid_operation_exception.rb +11 -0
- data/lib/kxi/exceptions/parse_exception.rb +21 -0
- data/lib/kxi/math/polynomial.rb +102 -0
- data/lib/kxi/platform.rb +58 -0
- data/lib/kxi/reflection/stack_frame.rb +1 -1
- data/lib/kxi/version.rb +4 -4
- data/lib/kxi.rb +17 -0
- metadata +15 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 78e859a6e89e40dbab305b79851a257dfd2ddf68
|
4
|
+
data.tar.gz: aedb9bc1385418141e79d90d64fe688c00e529d4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 856340dde06a8e744284dfb3060eb69a053f90868338ce3b7034163750c566f8c0f60cbc67c59b73ee0c9666b18cbe1e808afcb22d84cba58a0f49593c5517ce
|
7
|
+
data.tar.gz: 10ee0e919f19ea968f65220714bc2a5be7a42cc4f2d59999c40b5c02ab0300da8974c54b814b977b9e7ff7cd86c2df0a7137e9110066169c499f618cf64b3702
|
@@ -0,0 +1,178 @@
|
|
1
|
+
# Created by Matyáš Pokorný on 2018-01-24.
|
2
|
+
|
3
|
+
module KXI
|
4
|
+
module Application
|
5
|
+
# Represents configuration file
|
6
|
+
class Config
|
7
|
+
# Gets configuration file
|
8
|
+
# @return [String] Path to configuration file
|
9
|
+
def file
|
10
|
+
@file
|
11
|
+
end
|
12
|
+
|
13
|
+
# Instantiates the {KXI::Application::Config} file
|
14
|
+
# @param file [String] Configuration file to read
|
15
|
+
# @param reader [KXI::Application::ConfigReader] Readers used to parse configuration
|
16
|
+
def initialize(file, reader)
|
17
|
+
@file = file
|
18
|
+
@obj = reader.parse(File.read(file))
|
19
|
+
@pth = ''
|
20
|
+
@def = false
|
21
|
+
end
|
22
|
+
|
23
|
+
# Gets current context
|
24
|
+
# @return [Object] Current context
|
25
|
+
def context
|
26
|
+
@obj
|
27
|
+
end
|
28
|
+
|
29
|
+
# Gets path of current context
|
30
|
+
# @return [String] Path of current context
|
31
|
+
def path
|
32
|
+
@pth
|
33
|
+
end
|
34
|
+
|
35
|
+
# Gets value or default from current context
|
36
|
+
# @param key [String] Name of value
|
37
|
+
# @param default [Object, nil] Default value
|
38
|
+
# @yield [val] Validator function
|
39
|
+
# @yieldparam val [Object, nil] Value to validate
|
40
|
+
# @return [Object, nil] Value or default
|
41
|
+
def get(key, default = nil, &validator)
|
42
|
+
if not @def and @obj.key?(key)
|
43
|
+
ctx = @obj[key]
|
44
|
+
return ctx if validator == nil
|
45
|
+
return validate(key, ctx, &validator)
|
46
|
+
else
|
47
|
+
return default
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Gets value from current context
|
52
|
+
# @param key [String] Name of value
|
53
|
+
# @yield [val] Validator function
|
54
|
+
# @yieldparam val [Object, nil] Value to validate
|
55
|
+
# @return [Object, nil] Value
|
56
|
+
# @raise [KXI::Exceptions::ConfigurationException] Raised when field is not found
|
57
|
+
def get!(key, &validator)
|
58
|
+
if not @def and @obj.key?(key)
|
59
|
+
ctx = @obj[key]
|
60
|
+
return ctx if validator == nil
|
61
|
+
return validate(key, ctx, &validator)
|
62
|
+
else
|
63
|
+
raise(KXI::Exceptions::ConfigurationException.new(@file, "#{@pth}/#{key}", 'Field is mandatory!'))
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Enters named section of configuration file
|
68
|
+
# @param key [String] Name of section
|
69
|
+
# @param man [Bool] Determines whether this section is mandatory
|
70
|
+
# @yield [] Block of actions for entered context
|
71
|
+
def inside(key, man = false, &block)
|
72
|
+
if not @def and @obj.key?(key)
|
73
|
+
ctx = @obj[key]
|
74
|
+
raise(KXI::Exceptions::ConfigurationException.new(@file, "#{@pth}/#{key}", 'Field must be object!')) unless ctx.is_a?(Hash)
|
75
|
+
enter(key, ctx) do
|
76
|
+
block.call
|
77
|
+
end
|
78
|
+
elsif man
|
79
|
+
raise(KXI::Exceptions::ConfigurationException.new(@file, "#{@pth}/#{key}", 'Object is mandatory!'))
|
80
|
+
else
|
81
|
+
enter(key, nil) do
|
82
|
+
if @def
|
83
|
+
block.call
|
84
|
+
else
|
85
|
+
@def = true
|
86
|
+
block.call
|
87
|
+
@def = false
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# Enters mandatory named section of configuration file
|
94
|
+
# @param key [String] Name of section
|
95
|
+
# @yield [] Block of actions for entered context
|
96
|
+
def inside!(key, &block)
|
97
|
+
inside(key, true) { block.call }
|
98
|
+
end
|
99
|
+
|
100
|
+
# Iterates through collection
|
101
|
+
# @param key [String] Name of collection
|
102
|
+
# @param man [Bool] Determines whether this section is mandatory
|
103
|
+
# @yield [] Block of actions for entered context
|
104
|
+
def each_index(key, man = false, &block)
|
105
|
+
if not @def and @obj.key?(key)
|
106
|
+
ctx = @obj[key]
|
107
|
+
raise(KXI::Exceptions::ConfigurationException.new(@file, "#{@pth}/#{key}", 'Field must be array!')) unless ctx.is_a?(Array)
|
108
|
+
ctx.each_with_index do |itm, idx|
|
109
|
+
enter("#{key}[#{idx}]", itm) do
|
110
|
+
block.call
|
111
|
+
end
|
112
|
+
end
|
113
|
+
elsif man
|
114
|
+
raise(KXI::Exceptions::ConfigurationException.new(@file, "#{@pth}/#{key}", 'Array is mandatory!'))
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# Iterates through mandatory collection
|
119
|
+
# @param key [String] Name of collection
|
120
|
+
# @yield [] Block of actions for entered context
|
121
|
+
def each_index!(key, &block)
|
122
|
+
each_index(key, true) { block.call }
|
123
|
+
end
|
124
|
+
|
125
|
+
# Iterates through fields of object
|
126
|
+
# @param key [String] Name of object
|
127
|
+
# @param man [Bool] Determines whether this section is mandatory
|
128
|
+
# @yield [idx] Block of actions for entered context
|
129
|
+
# @yieldparam idx [String] Name of field
|
130
|
+
def each_field(key, man = false, &block)
|
131
|
+
if not @def and @obj.key?(key)
|
132
|
+
ctx = @obj[key]
|
133
|
+
raise(KXI::Exceptions::ConfigurationException.new(@file, "#{@pth}/#{key}", 'Field must be object!')) unless ctx.is_a?(Hash)
|
134
|
+
ctx.each_pair do |idx, itm|
|
135
|
+
enter("#{key}/#{idx}", itm) do
|
136
|
+
block.call(idx)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
elsif man
|
140
|
+
raise(KXI::Exceptions::ConfigurationException.new(@file, "#{@pth}/#{key}", 'Object is mandatory!'))
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
# Iterates through fields of mandatory object
|
145
|
+
# @param key [String] Name of object
|
146
|
+
# @yield [idx] Block of actions for entered context
|
147
|
+
# @yieldparam idx [String] Name of field
|
148
|
+
def each_field!(key, &block)
|
149
|
+
each_field(key, true) { |itm| block.call(itm) }
|
150
|
+
end
|
151
|
+
|
152
|
+
def enter(key, ctx, &block)
|
153
|
+
obj = @obj
|
154
|
+
pth = @pth
|
155
|
+
@obj = ctx
|
156
|
+
@pth = "#{@pth}/#{key}"
|
157
|
+
block.call
|
158
|
+
ensure
|
159
|
+
@pth = pth
|
160
|
+
@obj = obj
|
161
|
+
end
|
162
|
+
|
163
|
+
def validate(key, ctx, &validator)
|
164
|
+
begin
|
165
|
+
enter(key, ctx) do
|
166
|
+
validator.call(ctx)
|
167
|
+
end
|
168
|
+
return ctx
|
169
|
+
rescue Exception => ex
|
170
|
+
raise(KXI::Exceptions::ConfigurationException.new(@file, "#{@pth}/#{key}", ex.message))
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
protected :context, :path, :get, :get!, :inside, :inside!, :each_index, :each_index!, :each_field, :each_field!
|
175
|
+
private :enter, :validate
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# Created by Matyáš Pokorný on 2018-01-24.
|
2
|
+
|
3
|
+
module KXI
|
4
|
+
module Application
|
5
|
+
# Parses configuration
|
6
|
+
# @abstract
|
7
|
+
class ConfigReader
|
8
|
+
# Parses configuration from file
|
9
|
+
# @param str [String] Data to parse
|
10
|
+
# @return [Hash] Parsed configuration
|
11
|
+
# @abstract
|
12
|
+
def parse(str)
|
13
|
+
raise(KXI::Exceptions::AbstractException.new(KXI::Application::ConfigReader))
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# Created by Matyáš Pokorný on 2018-01-26.
|
2
|
+
|
3
|
+
module KXI
|
4
|
+
module Application
|
5
|
+
# Event handler
|
6
|
+
class Event
|
7
|
+
# Adds hook to handler
|
8
|
+
# @return [Number] Id of hook
|
9
|
+
# @yield [sender, *args] Action block of hook
|
10
|
+
# @yieldparam sender [Object] Invoker of event
|
11
|
+
# @yieldparam args [Array<Object, nil>] Arguments of invocation
|
12
|
+
def add(&block)
|
13
|
+
@hooks.push(block)
|
14
|
+
return @hooks.length - 1
|
15
|
+
end
|
16
|
+
|
17
|
+
# Removes hook from handler
|
18
|
+
# @param id [Number] Id of hook to remove
|
19
|
+
def remove(id)
|
20
|
+
@hooks.delete_at(id)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Instantiates the {KXI::Application::Event} class
|
24
|
+
def initialize
|
25
|
+
@hooks = []
|
26
|
+
end
|
27
|
+
|
28
|
+
# Invokes all hooks
|
29
|
+
# @param sender [Object] Invoker of event
|
30
|
+
# @param args [Array<Object, nil>] Arguments of invocation
|
31
|
+
def fire(sender, *args)
|
32
|
+
@hooks.each { |h| h.call(sender, *args) }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,156 @@
|
|
1
|
+
# Created by Matyáš Pokorný on 2018-01-26.
|
2
|
+
|
3
|
+
module KXI
|
4
|
+
module Application
|
5
|
+
# Allows simple unstructured (time-independent) logging
|
6
|
+
class Logger
|
7
|
+
# Gets the message event
|
8
|
+
# @return [KXI::Application::Event] Message event
|
9
|
+
def on_message
|
10
|
+
@event
|
11
|
+
end
|
12
|
+
|
13
|
+
# Attaches action to message event
|
14
|
+
# @param min [KXI::Application::Logger::Message::Severity] Minimal severity of message
|
15
|
+
# @yield [msg] Event action
|
16
|
+
# @yieldparam msg [KXI::Application::Logger::Message] Logger message
|
17
|
+
# @return [Number] Id of event hook
|
18
|
+
def attach(min = Message::Severity::COMMAND, &block)
|
19
|
+
return @event.add { |s, msg| block.call(msg) if msg.severity.level >= min.level }
|
20
|
+
end
|
21
|
+
|
22
|
+
# Instantiates the {KXI::Application::Logger} class
|
23
|
+
def initialize
|
24
|
+
@event = KXI::Application::Event.new
|
25
|
+
@start = DateTime.now
|
26
|
+
end
|
27
|
+
|
28
|
+
# Logs message with the {KXI::Application::Logger::Message::Severity::COMMAND} severity
|
29
|
+
# @param cmd [String] Executed command
|
30
|
+
def command(cmd)
|
31
|
+
log(Message::Severity::COMMAND, cmd)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Logs message with the {KXI::Application::Logger::Message::Severity::TRACE} severity
|
35
|
+
# @param msg [String] Text of message
|
36
|
+
def trace(msg)
|
37
|
+
log(Message::Severity::TRACE, msg)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Logs message with the {KXI::Application::Logger::Message::Severity::DEBUG} severity
|
41
|
+
# @param msg [String] Text of message
|
42
|
+
def debug(msg)
|
43
|
+
log(Message::Severity::DEBUG, msg)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Logs message with the {KXI::Application::Logger::Message::Severity::INFO} severity
|
47
|
+
# @param msg [String] Text of message
|
48
|
+
def info(msg)
|
49
|
+
log(Message::Severity::INFO, msg)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Logs message with the {KXI::Application::Logger::Message::Severity::WARNING} severity
|
53
|
+
# @param msg [String] Text of message
|
54
|
+
def warning(msg)
|
55
|
+
log(Message::Severity::WARNING, msg)
|
56
|
+
end
|
57
|
+
|
58
|
+
# Logs message with the {KXI::Application::Logger::Message::Severity::ERROR} severity
|
59
|
+
# @param msg [String] Text of message
|
60
|
+
def error(msg)
|
61
|
+
log(Message::Severity::ERROR, msg)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Logs message with the {KXI::Application::Logger::Message::Severity::FATAL} severity
|
65
|
+
# @param msg [String] Text of message
|
66
|
+
def fatal(msg)
|
67
|
+
log(Message::Severity::FATAL, msg)
|
68
|
+
end
|
69
|
+
|
70
|
+
def log(severity, msg)
|
71
|
+
now = DateTime.now
|
72
|
+
@event.fire(self, Message.new(severity, now - @start, now, msg))
|
73
|
+
end
|
74
|
+
|
75
|
+
private :log
|
76
|
+
|
77
|
+
# Represents a log message
|
78
|
+
class Message
|
79
|
+
# Represents severity of message
|
80
|
+
class Severity
|
81
|
+
# Gets the name of severity
|
82
|
+
# @return [String] Name of severity
|
83
|
+
def name
|
84
|
+
@name
|
85
|
+
end
|
86
|
+
|
87
|
+
# Gets the level of severity
|
88
|
+
# @return [Number] Level of severity
|
89
|
+
def level
|
90
|
+
@lvl
|
91
|
+
end
|
92
|
+
|
93
|
+
# Instantiates the [KXI::Application::Logger::Message::Severity] class
|
94
|
+
# @param nm [String] Name of severity
|
95
|
+
# @param lvl [Number] Level of severity
|
96
|
+
def initialize(nm, lvl)
|
97
|
+
@name = nm
|
98
|
+
@lvl = lvl
|
99
|
+
end
|
100
|
+
|
101
|
+
|
102
|
+
# Severity of messages that log execution of external commands
|
103
|
+
COMMAND = Severity.new('command', 0)
|
104
|
+
# Severity of messages that allow tracing of execution paths
|
105
|
+
TRACE = Severity.new('trace', 1)
|
106
|
+
# Severity of messages used for debugging
|
107
|
+
DEBUG = Severity.new('debug', 2)
|
108
|
+
# Severity of messages that user should know about
|
109
|
+
INFO = Severity.new('info', 3)
|
110
|
+
# Severity of messages that notify about something that might lead in future to problems
|
111
|
+
WARNING = Severity.new('warning', 4)
|
112
|
+
# Severity of messages that notify about problems that application can recover from
|
113
|
+
ERROR = Severity.new('error', 5)
|
114
|
+
# Severity of messages that notify about problems that application cannot recover from
|
115
|
+
FATAL = Severity.new('fatal', 6)
|
116
|
+
end
|
117
|
+
|
118
|
+
# Gets the severity of message
|
119
|
+
# @return [KXI::Application::Logger::Message:Severity] Severity of message
|
120
|
+
def severity
|
121
|
+
@sev
|
122
|
+
end
|
123
|
+
|
124
|
+
# Gets time at which the message was created
|
125
|
+
# @return [DateTime] Time of creation
|
126
|
+
def absolute_time
|
127
|
+
@ttime
|
128
|
+
end
|
129
|
+
|
130
|
+
# Gets time, relative to creation of logger, at which the message was created
|
131
|
+
# @return [DateTime] Relative time of creation
|
132
|
+
def relative_time
|
133
|
+
@rtime
|
134
|
+
end
|
135
|
+
|
136
|
+
# Gets the text message
|
137
|
+
# @return [String] Message
|
138
|
+
def message
|
139
|
+
@msg
|
140
|
+
end
|
141
|
+
|
142
|
+
# Instantiates the {KXI::Application::Logger::Message} class
|
143
|
+
# @param severity [KXI::Application::Logger::Message::Severity] Severity of message
|
144
|
+
# @param rtime [DateTime] Relative time of message
|
145
|
+
# @param ttime [DateTime] Absolute time of message
|
146
|
+
# @param message [String] Text of message
|
147
|
+
def initialize(severity, rtime, ttime, message)
|
148
|
+
@sev = severity
|
149
|
+
@rtime = rtime
|
150
|
+
@ttime = ttime
|
151
|
+
@msg = message
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# Created by Matyáš Pokorný on 2018-01-27.
|
2
|
+
|
3
|
+
module KXI
|
4
|
+
module Application
|
5
|
+
class Version
|
6
|
+
def major
|
7
|
+
@major
|
8
|
+
end
|
9
|
+
|
10
|
+
def minor
|
11
|
+
@minor
|
12
|
+
end
|
13
|
+
|
14
|
+
def patch
|
15
|
+
@patch
|
16
|
+
end
|
17
|
+
|
18
|
+
def identifier
|
19
|
+
@id
|
20
|
+
end
|
21
|
+
|
22
|
+
def metadata
|
23
|
+
@meta
|
24
|
+
end
|
25
|
+
|
26
|
+
def initialize(major, minor = 0, patch = 0, id = nil, meta = nil)
|
27
|
+
@major = major
|
28
|
+
@minor = minor
|
29
|
+
@patch = patch
|
30
|
+
@id = id
|
31
|
+
@meta = meta
|
32
|
+
end
|
33
|
+
|
34
|
+
def to_s
|
35
|
+
"#{@major}.#{@minor}.#{@patch}#{@id == nil ? '' : "-#{@id}"}#{@meta == nil ? '' : "+#{@meta}"}"
|
36
|
+
end
|
37
|
+
|
38
|
+
def ==(other)
|
39
|
+
return false unless other.is_a?(KXI::Application::Version)
|
40
|
+
return (@major == other.major and @minor == other.minor and @patch == other.patch and @id == other.identifier)
|
41
|
+
end
|
42
|
+
|
43
|
+
def <(other)
|
44
|
+
raise(KXI::Exceptions::InvalidOperationException.new("Can't compare version to #{other.class.name}!")) unless other.is_a?(KXI::Application::Version)
|
45
|
+
return true if @major < other.major or @minor < other.minor or @patch < other.patch
|
46
|
+
return true if @id != nil and other.identifier == nil
|
47
|
+
return true if @id != nil and other.identifier != nil and @id < other.identifier
|
48
|
+
return false
|
49
|
+
end
|
50
|
+
|
51
|
+
def <=(other)
|
52
|
+
raise(KXI::Exceptions::InvalidOperationException.new("Can't compare version to #{other.class.name}!")) unless other.is_a?(KXI::Application::Version)
|
53
|
+
return true if self == other
|
54
|
+
return true if @major < other.major or @minor < other.minor or @patch < other.patch
|
55
|
+
return true if @id != nil and other.identifier == nil
|
56
|
+
return true if @id != nil and other.identifier != nil and @id < other.identifier
|
57
|
+
return false
|
58
|
+
end
|
59
|
+
|
60
|
+
def >(other)
|
61
|
+
return (not self <= other)
|
62
|
+
end
|
63
|
+
|
64
|
+
def >=(other)
|
65
|
+
return (not self < other)
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.parse(str)
|
69
|
+
m = /^\s*(?'ma'\d+)\s*(\.\s*(?'mi'\d+))?\s*(\.\s*(?'pt'\d+))?\s*(-\s*(?'id'[0-9A-Z\-.]+))?\s*(\+\s*(?'mt'[0-9A-Z\-.]+))?\s*$/mi.match(str)
|
70
|
+
raise(KXI::Exceptions::ParseException.new('version', str)) if m == nil
|
71
|
+
return Version.new(m['ma'].to_i, m['mi'] != nil ? m['mi'].to_i : 0, m['pt'] != nil ? m['pt'].to_i : 0, m['id'], m['mt'])
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# Created by Matyáš Pokorný on 2018-01-27.
|
2
|
+
|
3
|
+
module KXI
|
4
|
+
module Application
|
5
|
+
class VersionExpression
|
6
|
+
COMP_EQ = '='
|
7
|
+
COMP_LEQ = '<='
|
8
|
+
COMP_LT = '<'
|
9
|
+
COMP_GEQ = '>='
|
10
|
+
COMP_GT = '>'
|
11
|
+
COMP_AT = '~>'
|
12
|
+
|
13
|
+
def comparison
|
14
|
+
@comp
|
15
|
+
end
|
16
|
+
|
17
|
+
def major
|
18
|
+
@major
|
19
|
+
end
|
20
|
+
|
21
|
+
def minor
|
22
|
+
@minor
|
23
|
+
end
|
24
|
+
|
25
|
+
def patch
|
26
|
+
@patch
|
27
|
+
end
|
28
|
+
|
29
|
+
def initialize(comp, major, minor = nil, patch = nil)
|
30
|
+
@comp = comp
|
31
|
+
@major = major
|
32
|
+
@minor = minor
|
33
|
+
@patch = patch
|
34
|
+
@target = KXI::Application::Version.new(major, minor == nil ? 0 : minor, patch == nil ? 0 : patch)
|
35
|
+
end
|
36
|
+
|
37
|
+
# @param ver [KXI::Application::Version]
|
38
|
+
def matches?(ver)
|
39
|
+
case @comp
|
40
|
+
when COMP_EQ
|
41
|
+
return (
|
42
|
+
ver.major == @major and
|
43
|
+
ver.minor == (@minor == nil ? 0 : @minor) and
|
44
|
+
ver.patch == (@patch == nil ? 0 : @patch)
|
45
|
+
)
|
46
|
+
when COMP_AT
|
47
|
+
return (
|
48
|
+
ver.major == @major and
|
49
|
+
(@minor == nil or ver.minor == @minor) and
|
50
|
+
(@patch == nil or ver.patch == @patch)
|
51
|
+
)
|
52
|
+
when COMP_GEQ
|
53
|
+
return ver >= @target
|
54
|
+
when COMP_LEQ
|
55
|
+
return ver <= @target
|
56
|
+
when COMP_GT
|
57
|
+
return ver > @target
|
58
|
+
when COMP_LT
|
59
|
+
return ver < @target
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.parse(str)
|
64
|
+
m = /^\s*(?'cm'>|<|>=|<=|=|~>)\s*(?'mj'\d+)\s*(\.\s*(?'mi'\d+))?\s*(\.\s*(?'pt'\d+))?\s*?$/m.match(str)
|
65
|
+
raise(KXI::Exceptions::ParseException.new('version expression', str)) if m == nil
|
66
|
+
return VersionExpression.new(m['cm'], m['mj'].to_i, m['mi']&.to_i, m['pt']&.to_i)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -42,13 +42,13 @@ module KXI
|
|
42
42
|
# Gets value of argument
|
43
43
|
# @param index [String, Symbol, KXI::CLI::Argument] Name of argument
|
44
44
|
# @return [String] Value of argument
|
45
|
-
def []
|
45
|
+
def [](index)
|
46
46
|
if index.is_a?(Symbol)
|
47
47
|
index = index.to_s
|
48
48
|
elsif index.is_a?(KXI::CLI::Argument)
|
49
49
|
index = index.name
|
50
50
|
end
|
51
|
-
raise(Exception.new("Undefined argument #{index}!"))
|
51
|
+
raise(Exception.new("Undefined argument '#{index}'!")) unless @vals.include?(index)
|
52
52
|
return @vals[index][:value]
|
53
53
|
end
|
54
54
|
|
@@ -65,7 +65,7 @@ module KXI
|
|
65
65
|
# Validates variadic arguments and checks for minimal argument requirements
|
66
66
|
def finish
|
67
67
|
@args.each do |arg|
|
68
|
-
if @vals
|
68
|
+
if @vals.include?(arg.name)
|
69
69
|
unless arg.is_a?(KXI::CLI::FlagArgument)
|
70
70
|
begin
|
71
71
|
arg.validate(@vals[arg.name][:value])
|
@@ -75,7 +75,7 @@ module KXI
|
|
75
75
|
end
|
76
76
|
else
|
77
77
|
raise(KXI::Exceptions::ArgumentException.new(arg.name, 'Argument is mandatory!')) if arg.required?
|
78
|
-
@vals[arg.name] = arg.default
|
78
|
+
@vals[arg.name] = { :argument => arg, :value => arg.default }
|
79
79
|
end
|
80
80
|
end
|
81
81
|
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# Created by Matyáš Pokorný on 2018-01-24.
|
2
|
+
|
3
|
+
module KXI
|
4
|
+
module CLI
|
5
|
+
class PropertyList
|
6
|
+
def initialize
|
7
|
+
@align = 0
|
8
|
+
@data = {}
|
9
|
+
end
|
10
|
+
|
11
|
+
def field(name, value)
|
12
|
+
@data[name] = value
|
13
|
+
@align = name.length if name.length > @align
|
14
|
+
end
|
15
|
+
|
16
|
+
def render(cols = 4)
|
17
|
+
@data.each_pair do |k, v|
|
18
|
+
if v.kind_of?(Array)
|
19
|
+
print("#{pad(k)}: ")
|
20
|
+
v.each_index do |idx|
|
21
|
+
if idx > 0 and idx % cols == 0
|
22
|
+
puts('')
|
23
|
+
print(' ' * (@align + 2))
|
24
|
+
elsif idx > 0
|
25
|
+
print(', ')
|
26
|
+
end
|
27
|
+
print(v[idx])
|
28
|
+
end
|
29
|
+
puts('')
|
30
|
+
else
|
31
|
+
puts("#{pad(k)}: #{v}")
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def pad(key)
|
37
|
+
d = false
|
38
|
+
p = ''
|
39
|
+
(@align - key.length).times do |i|
|
40
|
+
p = (d ? '.' : ' ') + p
|
41
|
+
d = (not d)
|
42
|
+
end
|
43
|
+
return key + p
|
44
|
+
end
|
45
|
+
|
46
|
+
private :pad
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# Created by Matyáš Pokorný on 2018-01-24.
|
2
|
+
|
3
|
+
module KXI
|
4
|
+
module CLI
|
5
|
+
class Table
|
6
|
+
def initialize
|
7
|
+
@cols = KXI::Collections::ArrayCollection.new
|
8
|
+
@rows = 0
|
9
|
+
yield(self) if block_given?
|
10
|
+
end
|
11
|
+
|
12
|
+
def column(name)
|
13
|
+
col = Column.new(name)
|
14
|
+
@cols.add(col)
|
15
|
+
return col
|
16
|
+
end
|
17
|
+
|
18
|
+
def row(e)
|
19
|
+
e.each_key { |c| @cols.first { |i| i.name == c }&.row(e[c].to_s.strip.chomp) }
|
20
|
+
@rows += 1
|
21
|
+
end
|
22
|
+
|
23
|
+
def render
|
24
|
+
@rows.times do |row|
|
25
|
+
f = true
|
26
|
+
@cols.foreach do |col|
|
27
|
+
print(' ') unless f
|
28
|
+
f = false
|
29
|
+
col.write(row)
|
30
|
+
end
|
31
|
+
puts
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class Column
|
36
|
+
def align
|
37
|
+
@align
|
38
|
+
end
|
39
|
+
|
40
|
+
def name
|
41
|
+
@name
|
42
|
+
end
|
43
|
+
|
44
|
+
def initialize(name)
|
45
|
+
@name = name
|
46
|
+
@align = 0
|
47
|
+
@data = []
|
48
|
+
end
|
49
|
+
|
50
|
+
def row(str)
|
51
|
+
@align = str.length if @align < str.length
|
52
|
+
@data.push(str)
|
53
|
+
end
|
54
|
+
|
55
|
+
def write(row)
|
56
|
+
str = @data[row]
|
57
|
+
str = '' if str == nil
|
58
|
+
print(str.ljust(@align))
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
data/lib/kxi/cli/verb.rb
CHANGED
@@ -129,9 +129,9 @@ module KXI
|
|
129
129
|
|
130
130
|
# Parses and executes action of verb
|
131
131
|
# @param args [Array<String>] Given arguments
|
132
|
-
def act(
|
133
|
-
args = ARGV if args
|
134
|
-
hf
|
132
|
+
def act(args = nil)
|
133
|
+
args = ARGV if args == nil
|
134
|
+
hf = (args.include?('-h') or args.include?('--help') or args.include?('-?'))
|
135
135
|
if @verbs.count > 0
|
136
136
|
v = args.shift
|
137
137
|
if @help and (v == 'help' or hf)
|
@@ -142,14 +142,14 @@ module KXI
|
|
142
142
|
if verb == nil
|
143
143
|
help
|
144
144
|
else
|
145
|
-
verb.act(
|
145
|
+
verb.act(args)
|
146
146
|
end
|
147
147
|
end
|
148
148
|
else
|
149
149
|
raise(KXI::Exceptions::VerbExpectedException.new(@verbs.select { |i| i.name }.to_array)) if v == nil
|
150
150
|
verb = @verbs.first { |i| i.name == v.downcase }
|
151
151
|
raise(KXI::Exceptions::VerbExpectedException.new(@verbs.select { |i| i.name }.to_array)) if verb == nil
|
152
|
-
verb.act(
|
152
|
+
verb.act(args)
|
153
153
|
end
|
154
154
|
else
|
155
155
|
if @help and hf
|
@@ -178,7 +178,7 @@ module KXI
|
|
178
178
|
else
|
179
179
|
raise(KXI::Exceptions::ArgumentException.new(val, 'Expected short argument name!')) if val.length == 1
|
180
180
|
nm = val[1]
|
181
|
-
arg = @args.first { |i| i.shortcut == nm }
|
181
|
+
arg = @args.of_type(KXI::CLI::ExplicitArgument).first { |i| i.shortcut == nm }
|
182
182
|
raise(KXI::Exceptions::ArgumentException.new(nm, 'No such argument exists!')) if arg == nil
|
183
183
|
if arg.is_a?(KXI::CLI::FlagArgument)
|
184
184
|
vals.set(arg, true)
|
@@ -198,14 +198,14 @@ module KXI
|
|
198
198
|
an = anon.first
|
199
199
|
raise(KXI::Exceptions::NoArgumentException.new(val)) if an == nil
|
200
200
|
vals.set(an, val)
|
201
|
-
anon.remove_at(0)
|
201
|
+
anon.remove_at(0) unless an.variadic?
|
202
202
|
end
|
203
203
|
end
|
204
204
|
end
|
205
|
+
vals.finish
|
205
206
|
if @action == nil
|
206
207
|
raise(KXI::Exceptions::NotImplementedException.new)
|
207
208
|
else
|
208
|
-
vals.finish
|
209
209
|
@action.call(vals)
|
210
210
|
end
|
211
211
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# Created by Matyáš Pokorný on 2018-01-24.
|
2
|
+
|
3
|
+
module KXI
|
4
|
+
module Exceptions
|
5
|
+
class ConfigurationException < Exception
|
6
|
+
def file
|
7
|
+
@file
|
8
|
+
end
|
9
|
+
|
10
|
+
def path
|
11
|
+
@path
|
12
|
+
end
|
13
|
+
|
14
|
+
def error_message
|
15
|
+
@msg
|
16
|
+
end
|
17
|
+
|
18
|
+
def initialize(file, path, msg = nil)
|
19
|
+
super("Invalid configuration <#{path}> in file '#{file}'!#{msg != nil ? ": #{msg}" : ''}")
|
20
|
+
@file = file
|
21
|
+
@path = path
|
22
|
+
@msg = msg
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# Created by Matyáš Pokorný on 2018-01-27.
|
2
|
+
|
3
|
+
module KXI
|
4
|
+
module Exceptions
|
5
|
+
class ParseException < Exception
|
6
|
+
def value
|
7
|
+
@val
|
8
|
+
end
|
9
|
+
|
10
|
+
def parser
|
11
|
+
@par
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(parser, val)
|
15
|
+
super("Failed to parse value '#{val.to_s}' as #{parser}!")
|
16
|
+
@par = parser
|
17
|
+
@val = val
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
# Created by Matyáš Pokorný on 2018-01-26.
|
2
|
+
|
3
|
+
module KXI
|
4
|
+
module Math
|
5
|
+
class Polynomial
|
6
|
+
def degree
|
7
|
+
@cfs.length - 1
|
8
|
+
end
|
9
|
+
|
10
|
+
def coefficients
|
11
|
+
@cfs
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(*cfs)
|
15
|
+
cfs.shift while cfs[0] == 0
|
16
|
+
@cfs = cfs.collect { |i| i.to_f }
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_s
|
20
|
+
ret = nil
|
21
|
+
foreach do |k, d|
|
22
|
+
if d > 0
|
23
|
+
if k != 0
|
24
|
+
if ret == nil
|
25
|
+
ret = k >= 0 ? '' : '-'
|
26
|
+
else
|
27
|
+
ret += k >= 0 ? ' + ' : ' - '
|
28
|
+
end
|
29
|
+
ret += (k >= 0 ? k : -k).to_s if k != 1 and k != -1
|
30
|
+
ret += "x#{d > 1 ? "^#{d.to_s}" : ''}"
|
31
|
+
end
|
32
|
+
else
|
33
|
+
if ret == nil
|
34
|
+
ret = k >= 0 ? '' : '-'
|
35
|
+
else
|
36
|
+
ret += k >= 0 ? ' + ' : ' - '
|
37
|
+
end
|
38
|
+
ret += (k >= 0 ? k : -k).to_s
|
39
|
+
end
|
40
|
+
end
|
41
|
+
return ret
|
42
|
+
end
|
43
|
+
|
44
|
+
def derivative
|
45
|
+
cfs = []
|
46
|
+
foreach do |k, d|
|
47
|
+
if d > 0
|
48
|
+
cfs.push(k * d)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
return Polynomial.new(*cfs)
|
52
|
+
end
|
53
|
+
|
54
|
+
def integral
|
55
|
+
cfs = []
|
56
|
+
foreach do |k, d|
|
57
|
+
cfs.push(k / (d + 1))
|
58
|
+
end
|
59
|
+
cfs.push(0)
|
60
|
+
return Polynomial.new(*cfs)
|
61
|
+
end
|
62
|
+
|
63
|
+
def integrate(x1, x2)
|
64
|
+
min = x1 > x2 ? x2 : x1
|
65
|
+
max = x1 > x2 ? x1 : x2
|
66
|
+
i = integral
|
67
|
+
return i.at(max) - i.at(min)
|
68
|
+
end
|
69
|
+
|
70
|
+
def at(x)
|
71
|
+
sum = 0
|
72
|
+
foreach do |k, d|
|
73
|
+
sum += k * (x ** d)
|
74
|
+
end
|
75
|
+
return sum
|
76
|
+
end
|
77
|
+
|
78
|
+
def foreach
|
79
|
+
d = degree
|
80
|
+
@cfs.each do |k|
|
81
|
+
yield(k, d)
|
82
|
+
d -= 1
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def *(other)
|
87
|
+
if other.is_a?(Polynomial)
|
88
|
+
cfs = [0] * (degree + other.degree)
|
89
|
+
foreach do |k1, d1|
|
90
|
+
other.foreach do |k2, d2|
|
91
|
+
cfs[d1 + d2] += k1 * k2
|
92
|
+
end
|
93
|
+
end
|
94
|
+
return Polynomial.new(*cfs)
|
95
|
+
elsif other.is_a?(Numeric)
|
96
|
+
return @cfs.collect { |k| k * other }
|
97
|
+
end
|
98
|
+
raise(Exception.new('Polynomial multiplication error!'))
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
data/lib/kxi/platform.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
# Created by Matyáš Pokorný on 2018-01-28.
|
2
|
+
|
3
|
+
module KXI
|
4
|
+
class Platform
|
5
|
+
def name
|
6
|
+
@name
|
7
|
+
end
|
8
|
+
|
9
|
+
def version
|
10
|
+
@version
|
11
|
+
end
|
12
|
+
|
13
|
+
def library_extensions
|
14
|
+
@libs
|
15
|
+
end
|
16
|
+
|
17
|
+
def path_separator
|
18
|
+
@ps
|
19
|
+
end
|
20
|
+
|
21
|
+
def initialize(nm, ver, ps, *libs)
|
22
|
+
@name = nm
|
23
|
+
@version = KXI::Application::Version.parse(ver)
|
24
|
+
@ps = ps
|
25
|
+
@libs = libs
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.this
|
29
|
+
if /cygwin|mswin|mingw|bccwin|wince|emx/ =~ RUBY_PLATFORM
|
30
|
+
return Platform.new('windows', `wmic os get Version /value`.split('=').last.strip.chomp, ';', 'dll')
|
31
|
+
elsif /darwin/ =~ RUBY_PLATFORM
|
32
|
+
return Platform.new('osx', `defaults read loginwindow SystemVersionStampAsString`, ':', 'dylib')
|
33
|
+
else
|
34
|
+
return Platform.new('linux', `uname -r`.split('-')[0], ':', 'so')
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def exec(name, *dir)
|
39
|
+
dir = Platform.env('path').split(@ps) if dir.length == 0
|
40
|
+
ext = Platform.env('pathext')
|
41
|
+
(ext != nil ? ext.split(@ps) + [''] : ['']).each do |e|
|
42
|
+
dir.each do |d|
|
43
|
+
f = File.join(d, "#{name}#{e}")
|
44
|
+
return f if File.exists?(f)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
return nil
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.env(key)
|
51
|
+
key = key.downcase
|
52
|
+
ENV.each_pair do |k, v|
|
53
|
+
return v if k.downcase == key
|
54
|
+
end
|
55
|
+
return nil
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -32,7 +32,7 @@ module KXI
|
|
32
32
|
def method
|
33
33
|
return nil if @block != nil and @block[0] == '<' and @block[@block.length - 1] == '>'
|
34
34
|
return @block unless @block == nil
|
35
|
-
return nil if @context[0] == '<' and @context[@context.length - 1] == '>'
|
35
|
+
return nil if @context != nil and @context[0] == '<' and @context[@context.length - 1] == '>'
|
36
36
|
@context
|
37
37
|
end
|
38
38
|
|
data/lib/kxi/version.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
module KXI
|
2
|
-
# Version of KXI
|
3
|
-
VERSION = '1.0.
|
4
|
-
end
|
1
|
+
module KXI
|
2
|
+
# Version of KXI
|
3
|
+
VERSION = '1.0.1'
|
4
|
+
end
|
data/lib/kxi.rb
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
# Ruby
|
2
|
+
require 'date'
|
3
|
+
|
4
|
+
# KXI
|
1
5
|
require 'kxi/version'
|
2
6
|
require 'kxi/exceptions/abstract_exception'
|
3
7
|
require 'kxi/exceptions/collection_exception'
|
@@ -7,6 +11,9 @@ require 'kxi/exceptions/argument_exception'
|
|
7
11
|
require 'kxi/exceptions/no_argument_exception'
|
8
12
|
require 'kxi/exceptions/verb_expected_exception'
|
9
13
|
require 'kxi/exceptions/invalid_type_exception'
|
14
|
+
require 'kxi/exceptions/configuration_exception'
|
15
|
+
require 'kxi/exceptions/parse_exception'
|
16
|
+
require 'kxi/exceptions/invalid_operation_exception'
|
10
17
|
require 'kxi/reflection/stack_frame'
|
11
18
|
require 'kxi/collections/enumerator'
|
12
19
|
require 'kxi/collections/enumerable'
|
@@ -20,3 +27,13 @@ require 'kxi/cli/flag_argument'
|
|
20
27
|
require 'kxi/cli/named_argument'
|
21
28
|
require 'kxi/cli/argument_values'
|
22
29
|
require 'kxi/cli/verb'
|
30
|
+
require 'kxi/cli/table'
|
31
|
+
require 'kxi/cli/property_list'
|
32
|
+
require 'kxi/application/config_reader'
|
33
|
+
require 'kxi/application/config'
|
34
|
+
require 'kxi/application/event'
|
35
|
+
require 'kxi/application/logger'
|
36
|
+
require 'kxi/application/version'
|
37
|
+
require 'kxi/application/version_expression'
|
38
|
+
require 'kxi/math/polynomial'
|
39
|
+
require 'kxi/platform'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kxi
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matyáš Pokorný
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-02-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -116,12 +116,20 @@ extensions: []
|
|
116
116
|
extra_rdoc_files: []
|
117
117
|
files:
|
118
118
|
- lib/kxi.rb
|
119
|
+
- lib/kxi/application/config.rb
|
120
|
+
- lib/kxi/application/config_reader.rb
|
121
|
+
- lib/kxi/application/event.rb
|
122
|
+
- lib/kxi/application/logger.rb
|
123
|
+
- lib/kxi/application/version.rb
|
124
|
+
- lib/kxi/application/version_expression.rb
|
119
125
|
- lib/kxi/cli/anonymous_argument.rb
|
120
126
|
- lib/kxi/cli/argument.rb
|
121
127
|
- lib/kxi/cli/argument_values.rb
|
122
128
|
- lib/kxi/cli/explicit_argument.rb
|
123
129
|
- lib/kxi/cli/flag_argument.rb
|
124
130
|
- lib/kxi/cli/named_argument.rb
|
131
|
+
- lib/kxi/cli/property_list.rb
|
132
|
+
- lib/kxi/cli/table.rb
|
125
133
|
- lib/kxi/cli/verb.rb
|
126
134
|
- lib/kxi/collections/array_collection.rb
|
127
135
|
- lib/kxi/collections/enumerable.rb
|
@@ -131,11 +139,16 @@ files:
|
|
131
139
|
- lib/kxi/exceptions/abstract_exception.rb
|
132
140
|
- lib/kxi/exceptions/argument_exception.rb
|
133
141
|
- lib/kxi/exceptions/collection_exception.rb
|
142
|
+
- lib/kxi/exceptions/configuration_exception.rb
|
143
|
+
- lib/kxi/exceptions/invalid_operation_exception.rb
|
134
144
|
- lib/kxi/exceptions/invalid_type_exception.rb
|
135
145
|
- lib/kxi/exceptions/no_argument_exception.rb
|
136
146
|
- lib/kxi/exceptions/not_implemented_exception.rb
|
137
147
|
- lib/kxi/exceptions/out_of_range_exception.rb
|
148
|
+
- lib/kxi/exceptions/parse_exception.rb
|
138
149
|
- lib/kxi/exceptions/verb_expected_exception.rb
|
150
|
+
- lib/kxi/math/polynomial.rb
|
151
|
+
- lib/kxi/platform.rb
|
139
152
|
- lib/kxi/reflection/stack_frame.rb
|
140
153
|
- lib/kxi/version.rb
|
141
154
|
homepage: https://github.com/cryocz/kxi
|