magister_cli 1.0.0 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/magister_cli/ext/string.rb +9 -0
- data/lib/magister_cli/parser.rb +119 -0
- data/lib/magister_cli/runtime.rb +73 -0
- data/lib/magister_cli/tokenizer.rb +86 -0
- data/lib/magister_cli.rb +59 -4
- metadata +37 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fc4e88673fb16d2fa82e0e23bd3312a684584a7eea909d101c49c7e96dd0c3c1
|
4
|
+
data.tar.gz: ffbb5497cc609cbdb4d775fdb92160f9d6ad559955cde2644c451f687795a005
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 489fb98b433f60be95f0c444a8006dc014f677798b489d4341debd299c63d77d21fde38cc5805bffb3e3d45ab4fefcfb06cccf5decb34fecbe0c0de8bdcb5614
|
7
|
+
data.tar.gz: bd1e443124ecb18d1906fdd0ac7aa9c0db6e51c2bb7e309347b99f02ebbece9d01d4f66a92a8f9441b2c90fd58f3897c354501df4185f3338a9415317ff73c35
|
@@ -0,0 +1,9 @@
|
|
1
|
+
# This Source Code Form is subject to the terms of the Mozilla Public
|
2
|
+
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
3
|
+
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
4
|
+
|
5
|
+
class String
|
6
|
+
def is_integer?
|
7
|
+
self.to_i.to_s == self
|
8
|
+
end
|
9
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
# This Source Code Form is subject to the terms of the Mozilla Public
|
2
|
+
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
3
|
+
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
4
|
+
require 'magister_cli/tokenizer'
|
5
|
+
|
6
|
+
class Parser
|
7
|
+
@@actions = [
|
8
|
+
['login', ['username', 'password', 'school']],
|
9
|
+
['auth', ['token', 'school']],
|
10
|
+
['quit', []],
|
11
|
+
['exit', []],
|
12
|
+
['get_classes', ['date_from', 'date_to']]
|
13
|
+
]
|
14
|
+
|
15
|
+
def initialize(string)
|
16
|
+
@tokenizer = Tokenizer.new(string)
|
17
|
+
@string = string
|
18
|
+
end
|
19
|
+
|
20
|
+
def is_action?(actionName)
|
21
|
+
@@actions.each do |action|
|
22
|
+
if action[0] == actionName
|
23
|
+
return true
|
24
|
+
end
|
25
|
+
end
|
26
|
+
return false
|
27
|
+
end
|
28
|
+
|
29
|
+
def string=(string)
|
30
|
+
@string = string
|
31
|
+
@tokenizer = Tokenizer.new(string)
|
32
|
+
end
|
33
|
+
|
34
|
+
def get_args(actionName)
|
35
|
+
@@actions.each do |action|
|
36
|
+
if action[0] == actionName
|
37
|
+
return action[1].dup
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def parse()
|
43
|
+
tokens = @tokenizer.tokenize
|
44
|
+
|
45
|
+
i = 0
|
46
|
+
|
47
|
+
ast = Array.new
|
48
|
+
while i < tokens.length
|
49
|
+
tmp = 0
|
50
|
+
props = false
|
51
|
+
opts = false
|
52
|
+
altOpts = false # true if opts are in the place props would be
|
53
|
+
|
54
|
+
if tokens[0 + i]["type"] != "KEYWORD"
|
55
|
+
puts "Current index: #{i.to_s}"
|
56
|
+
raise SyntaxError.new "Expected #{tokens[0 + i]["value"]} to be a KEYWORD, is a #{tokens[0 + i]["type"]}"
|
57
|
+
end
|
58
|
+
|
59
|
+
if tokens[1 + i] != nil && tokens[1 + i]["type"] == "PARAMS"
|
60
|
+
tmp += 1
|
61
|
+
props = true
|
62
|
+
end
|
63
|
+
|
64
|
+
if tokens[2 + i] != nil && tokens[2 + i]["type"] == "OPTIONS"
|
65
|
+
tmp += 1
|
66
|
+
opts = true
|
67
|
+
end
|
68
|
+
|
69
|
+
if tokens[1 + i] != nil && tokens[1 + i]["type"] == "OPTIONS"
|
70
|
+
if opts || props
|
71
|
+
raise SyntaxError.new "Cant have type OPTIONS here, must be PARAMS"
|
72
|
+
end
|
73
|
+
opts = true
|
74
|
+
altOpts = true
|
75
|
+
tmp += 1
|
76
|
+
end
|
77
|
+
|
78
|
+
ast_node = {"action" => tokens[0 + i]["value"]}
|
79
|
+
|
80
|
+
if props
|
81
|
+
ast_node["parameters"] = tokens[1 + i]["value"]
|
82
|
+
end
|
83
|
+
|
84
|
+
if opts
|
85
|
+
if altOpts
|
86
|
+
ast_node["options"] = tokens[1 + i]["value"]
|
87
|
+
else
|
88
|
+
ast_node["options"] = tokens[2 + i]["value"]
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
if !is_action? ast_node["action"]
|
93
|
+
throw SyntaxError.new "#{ast_node["action"]} is not a valid action!"
|
94
|
+
end
|
95
|
+
|
96
|
+
lacking = Array.new
|
97
|
+
lacking = get_args(ast_node["action"])
|
98
|
+
|
99
|
+
if props
|
100
|
+
ast_node["parameters"].each do |prop|
|
101
|
+
if !lacking.include? prop["key"]
|
102
|
+
raise SyntaxError.new "#{prop["key"]} is is already defined or is not a valid parameter for #{ast_node["action"]}!"
|
103
|
+
end
|
104
|
+
lacking.delete prop["key"]
|
105
|
+
end
|
106
|
+
else
|
107
|
+
ast_node["parameters"] = Array.new
|
108
|
+
end
|
109
|
+
|
110
|
+
ast_node["lacking_params"] = lacking
|
111
|
+
|
112
|
+
ast.append(ast_node)
|
113
|
+
|
114
|
+
i += tmp + 1 # + 1 for the keyword itself
|
115
|
+
end
|
116
|
+
|
117
|
+
ast
|
118
|
+
end
|
119
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# This Source Code Form is subject to the terms of the Mozilla Public
|
2
|
+
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
3
|
+
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
4
|
+
require 'magister'
|
5
|
+
|
6
|
+
def zulu_to_ams(string)
|
7
|
+
("%02d" % (string[0..1].to_i + 1)).to_s + string[2..-1]
|
8
|
+
end
|
9
|
+
|
10
|
+
module Runners
|
11
|
+
def login(username, password, school)
|
12
|
+
$magcli_runtime_variables["magister"].login(school, username, password)
|
13
|
+
end
|
14
|
+
|
15
|
+
def auth(token, school)
|
16
|
+
$magcli_runtime_variables["magister"].authenticate(school, token)
|
17
|
+
end
|
18
|
+
|
19
|
+
def get_classes(date_from, date_to)
|
20
|
+
classes = $magcli_runtime_variables["magister"].get_classes(date_from, date_to)
|
21
|
+
if classes == nil
|
22
|
+
raise "Couldn't fetch classes!"
|
23
|
+
end
|
24
|
+
if @opts["outputMode"]
|
25
|
+
classes.each do |c|
|
26
|
+
puts "|#{zulu_to_ams c.classStart.to_s[11..-13]} - #{zulu_to_ams c.classEndInclusive.to_s[11..-13]}| #{c.description} [#{c.location}]"
|
27
|
+
end
|
28
|
+
else
|
29
|
+
puts classes.to_json
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class Runtime
|
35
|
+
include Runners
|
36
|
+
|
37
|
+
@@spec = {
|
38
|
+
"login" => :login,
|
39
|
+
"auth" => :auth,
|
40
|
+
"quit" => :exit,
|
41
|
+
"exit" => :exit,
|
42
|
+
"get_classes" => :get_classes
|
43
|
+
}
|
44
|
+
def initialize(action)
|
45
|
+
if action["options"] != nil
|
46
|
+
@opts = action["options"]
|
47
|
+
else
|
48
|
+
@opts = {"outputMode" => "formatted"}
|
49
|
+
end
|
50
|
+
|
51
|
+
if @opts["noCache"] != nil && @opts["noCache"] == "true"
|
52
|
+
$magister_useCache = false
|
53
|
+
else
|
54
|
+
$magister_useCache = true
|
55
|
+
end
|
56
|
+
|
57
|
+
requiredArgs = Parser.new("").get_args(action["action"])
|
58
|
+
args = Array.new
|
59
|
+
|
60
|
+
if action["parameters"] == nil
|
61
|
+
raise "Not a single parameter defined!! not even an empty array!"
|
62
|
+
end
|
63
|
+
|
64
|
+
requiredArgs.each do |arg|
|
65
|
+
action["parameters"].each do |param|
|
66
|
+
if arg == param["key"]
|
67
|
+
args.append(param["value"])
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
send(@@spec[action["action"]], *args)
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
# This Source Code Form is subject to the terms of the Mozilla Public
|
2
|
+
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
3
|
+
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
4
|
+
|
5
|
+
class Tokenizer
|
6
|
+
@@spec = [
|
7
|
+
[/(\w+)/, "KEYWORD"],
|
8
|
+
[/(?:\w+\s?){(.*)}/, "PARAMS"],
|
9
|
+
[/(?:\w+\s?(?:{.*}\s?)?)\[(.*)\]/, "OPTIONS"],
|
10
|
+
[/(\s+)/, "EMPTY"]
|
11
|
+
]
|
12
|
+
|
13
|
+
def initialize(string)
|
14
|
+
@string = string
|
15
|
+
end
|
16
|
+
|
17
|
+
def parse_options(optionsString)
|
18
|
+
cursor = 0
|
19
|
+
found = Array.new
|
20
|
+
while cursor < optionsString.length
|
21
|
+
scope = optionsString[cursor..-1]
|
22
|
+
|
23
|
+
if scope[0] == "{" || scope[0] == "}" || scope[0] == "[" || scope[0] == "]"
|
24
|
+
cursor += 1
|
25
|
+
next
|
26
|
+
end
|
27
|
+
|
28
|
+
whiteSpaceMatch = scope.match /^\s+/
|
29
|
+
if whiteSpaceMatch != nil
|
30
|
+
cursor += whiteSpaceMatch[0].length
|
31
|
+
next
|
32
|
+
end
|
33
|
+
|
34
|
+
keywordMatch = scope.match /^\w+\s?:/
|
35
|
+
if keywordMatch != nil
|
36
|
+
cursor += keywordMatch[0].length
|
37
|
+
found.append({"type" => "KEYWORD", "value" => keywordMatch[0][0..-2]})
|
38
|
+
next
|
39
|
+
end
|
40
|
+
|
41
|
+
explicitString = scope.match /^'([^']*)',*/
|
42
|
+
if explicitString != nil
|
43
|
+
cursor += explicitString[0].length
|
44
|
+
found.append({"type" => "STRING", "value" => explicitString.captures[0]})
|
45
|
+
next
|
46
|
+
end
|
47
|
+
|
48
|
+
impliedString = scope.match /^(.*?),*/
|
49
|
+
if impliedString != nil
|
50
|
+
cursor += impliedString[0].length
|
51
|
+
found.append({"type" => "STRING", "value" => impliedString.captures[0]})
|
52
|
+
next
|
53
|
+
end
|
54
|
+
end
|
55
|
+
parsed = Array.new
|
56
|
+
i = 0
|
57
|
+
while i < found.length
|
58
|
+
if found[i]["type"] != "KEYWORD" || found[i + 1]["type"] != "STRING"
|
59
|
+
raise TypeError.new "Expected #{found[i]["value"]} to be a KEYWORD and #{found[i + 1]["value"]} to be a STRING"
|
60
|
+
end
|
61
|
+
|
62
|
+
parsed.append({"type" => "PROPERTY", "key" => found[i]["value"], "value" => found[i + 1]["value"]})
|
63
|
+
|
64
|
+
i += 2
|
65
|
+
end
|
66
|
+
parsed
|
67
|
+
end
|
68
|
+
|
69
|
+
def tokenize
|
70
|
+
@tokens = Array.new
|
71
|
+
@@spec.each do |item|
|
72
|
+
matched = @string.match item[0]
|
73
|
+
if matched != nil
|
74
|
+
matched.captures.each do |match|
|
75
|
+
if item[1] == "PARAMS" || item[1] == "OPTIONS"
|
76
|
+
@tokens.append({"type" => item[1], "value" => parse_options(match)})
|
77
|
+
elsif item[1] == "EMPTY"
|
78
|
+
else
|
79
|
+
@tokens.append({"type" => item[1], "value" => match})
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
@tokens
|
85
|
+
end
|
86
|
+
end
|
data/lib/magister_cli.rb
CHANGED
@@ -2,13 +2,24 @@
|
|
2
2
|
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
3
3
|
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
4
4
|
require 'magister'
|
5
|
+
require 'magister_cli/ext/string'
|
6
|
+
require 'magister_cli/parser'
|
7
|
+
require 'magister_cli/tokenizer'
|
8
|
+
require 'magister_cli/runtime'
|
9
|
+
require 'io/console'
|
10
|
+
|
11
|
+
$magister_cachingDirectory = Dir.home + "/.cache/magister"
|
5
12
|
|
6
13
|
$magcli_runmode = "normal"
|
14
|
+
$magcli_runtime_variables = {}
|
7
15
|
|
8
16
|
module MagisterCLI
|
9
17
|
extend self
|
10
18
|
|
11
19
|
def main
|
20
|
+
$magcli_runtime_variables["magister"] = Magister.new
|
21
|
+
|
22
|
+
begin
|
12
23
|
if ARGV.include?("-r") || ARGV.include?("--run")
|
13
24
|
$magcli_runmode = "inline"
|
14
25
|
args = ARGV
|
@@ -27,9 +38,53 @@ module MagisterCLI
|
|
27
38
|
return
|
28
39
|
end
|
29
40
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
41
|
+
parser = Parser.new("")
|
42
|
+
while true
|
43
|
+
print "> "
|
44
|
+
input = gets.chomp
|
45
|
+
parser.string = input
|
46
|
+
output = parser.parse
|
47
|
+
|
48
|
+
# loop over each action that was outputted
|
49
|
+
output.each do |action|
|
50
|
+
props = action["parameters"]
|
51
|
+
doneParams = Array.new
|
52
|
+
# get any parametres that werent set and ask then set them
|
53
|
+
action["lacking_params"].each do |param|
|
54
|
+
print "#{param}: "
|
55
|
+
if param == "password"
|
56
|
+
ans = STDIN.noecho(&:gets).chomp
|
57
|
+
print "\n"
|
58
|
+
else
|
59
|
+
ans = gets.chomp
|
60
|
+
end
|
61
|
+
props.append({"type" => "PROPERTY", "key" => param, "value" => ans})
|
62
|
+
doneParams.append param
|
63
|
+
end
|
64
|
+
|
65
|
+
# update lacking_params value, done here as to not mess with the order while looping over them
|
66
|
+
doneParams.each do |p|
|
67
|
+
action["lacking_params"].delete(p)
|
68
|
+
end
|
69
|
+
action["parameters"] = props
|
70
|
+
props = Array.new
|
71
|
+
|
72
|
+
runtime = Runtime.new(action)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
rescue SignalException
|
76
|
+
$magcli_runtime_variables = Array.new
|
77
|
+
puts "[CTRL + C]\nExiting..."
|
78
|
+
exit
|
79
|
+
rescue UncaughtThrowError => e
|
80
|
+
if e.message.include? "#<SyntaxError: "
|
81
|
+
message = e.message.split("#<SyntaxError: ").last[0..-2]
|
82
|
+
puts "Invalid syntax: #{message}"
|
83
|
+
else
|
84
|
+
puts "Something went wrong: #{e.message}"
|
85
|
+
end
|
86
|
+
rescue => e
|
87
|
+
puts "Something went wrong: #{e.message}"
|
88
|
+
end
|
34
89
|
end
|
35
90
|
end
|
metadata
CHANGED
@@ -1,15 +1,43 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: magister_cli
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Riley0122
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-02-
|
12
|
-
dependencies:
|
11
|
+
date: 2024-02-29 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: json
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: magister
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
13
41
|
description: The CLI for the 'magister' gem
|
14
42
|
email: contact@riley0122.dev
|
15
43
|
executables:
|
@@ -19,11 +47,15 @@ extra_rdoc_files: []
|
|
19
47
|
files:
|
20
48
|
- bin/magister
|
21
49
|
- lib/magister_cli.rb
|
22
|
-
|
50
|
+
- lib/magister_cli/ext/string.rb
|
51
|
+
- lib/magister_cli/parser.rb
|
52
|
+
- lib/magister_cli/runtime.rb
|
53
|
+
- lib/magister_cli/tokenizer.rb
|
54
|
+
homepage: https://github.com/riley0122/rubymag/tree/main/cli#readme
|
23
55
|
licenses:
|
24
56
|
- MPL-2.0
|
25
57
|
metadata:
|
26
|
-
source_code_uri: https://github.com/riley0122/rubymag/cli
|
58
|
+
source_code_uri: https://github.com/riley0122/rubymag/tree/main/cli
|
27
59
|
post_install_message:
|
28
60
|
rdoc_options: []
|
29
61
|
require_paths:
|