kxi 1.0.0 → 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|