shashi 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/CODE_OF_CONDUCT.md +49 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +67 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/exe/shashi +5 -0
- data/exe/shashi__db.json.sample +49 -0
- data/gpl-3.0.txt +674 -0
- data/lib/shashi.rb +7 -0
- data/lib/shashi/cli.rb +121 -0
- data/lib/shashi/command.rb +83 -0
- data/lib/shashi/command/create_list.rb +39 -0
- data/lib/shashi/command/create_set.rb +39 -0
- data/lib/shashi/command/list_delete.rb +33 -0
- data/lib/shashi/command/list_push.rb +27 -0
- data/lib/shashi/command/list_show.rb +45 -0
- data/lib/shashi/command/set_delete.rb +57 -0
- data/lib/shashi/command/set_set.rb +50 -0
- data/lib/shashi/command/setup.rb +30 -0
- data/lib/shashi/command/show_keys.rb +39 -0
- data/lib/shashi/utils.rb +48 -0
- data/lib/shashi/version.rb +3 -0
- data/shashi.gemspec +23 -0
- metadata +115 -0
data/lib/shashi.rb
ADDED
data/lib/shashi/cli.rb
ADDED
@@ -0,0 +1,121 @@
|
|
1
|
+
require "optparse"
|
2
|
+
|
3
|
+
module Shashi
|
4
|
+
module CLI
|
5
|
+
|
6
|
+
def self.perform(options:)
|
7
|
+
arguments = {
|
8
|
+
file: "./shashi__db.json",
|
9
|
+
path: [],
|
10
|
+
force: false,
|
11
|
+
echo: true,
|
12
|
+
deep: false,
|
13
|
+
index: nil,
|
14
|
+
count: 1,
|
15
|
+
}
|
16
|
+
|
17
|
+
opt_parser = OptionParser.new do |opts|
|
18
|
+
opts.banner = "Usage: shashi [options]"
|
19
|
+
|
20
|
+
opts.on("--setup", "Creates the storage file with an empty set.") do
|
21
|
+
arguments[:command] = :setup
|
22
|
+
end
|
23
|
+
|
24
|
+
opts.on("--file FILE", "Uses FILE as the desired file path for the storage. Defaults to `./shashi__db.json`.") do |file|
|
25
|
+
arguments[:file] = file
|
26
|
+
end
|
27
|
+
|
28
|
+
opts.on("--path PATH", "`PATH := List<KEY>[separator:.]`. Address the document's item by chaining a list of keys. For example: `key1.key2.key3`.") do |path|
|
29
|
+
arguments[:path] = path.split(".")
|
30
|
+
end
|
31
|
+
|
32
|
+
opts.on("--create-set NAME", "Creates an empty set. Asks for confirmation if the key NAME already exists.") do |name|
|
33
|
+
arguments[:command] = :create_set
|
34
|
+
arguments[:set_name] = name
|
35
|
+
end
|
36
|
+
|
37
|
+
opts.on("--set KEY:VALUE", "Sets the KEY to VALUE. Asks for confirmation if the key NAME already exists. If no VALUE is given, prompts for it.") do |pair|
|
38
|
+
arguments[:command] = :set_set
|
39
|
+
arguments[:key], arguments[:value] = pair.split(":")
|
40
|
+
end
|
41
|
+
|
42
|
+
opts.on("--show KEYS", "`KEYS := List<KEY>[separator:,]`. Shows the values associated with keys but not the content of sets/lists. For example: `name,e-mail`.") do |keys|
|
43
|
+
arguments[:command] = :show_keys
|
44
|
+
arguments[:keys] = keys.split(",")
|
45
|
+
end
|
46
|
+
|
47
|
+
opts.on("--delete KEY", "Deletes the item. Asks for confirmation if it's a non-empty set or list.") do |key|
|
48
|
+
arguments[:command] = :delete
|
49
|
+
arguments[:key] = key
|
50
|
+
end
|
51
|
+
|
52
|
+
opts.on("--create-list NAME", "Creates an empty list. Asks for confirmation if the key NAME already exists.") do |name|
|
53
|
+
arguments[:command] = :create_list
|
54
|
+
arguments[:list_name] = name
|
55
|
+
end
|
56
|
+
|
57
|
+
opts.on("--index INDEX", Integer, "`INDEX := (Integer >= 0)`. References the n-th element of a list. Defaults to the size of the list (ie: the last element).") do |index|
|
58
|
+
arguments[:index] = index
|
59
|
+
end
|
60
|
+
|
61
|
+
opts.on("--count COUNT", Integer, "`COUNT := (Integer > 0), defaults to 1`.") do |count|
|
62
|
+
arguments[:count] = count
|
63
|
+
end
|
64
|
+
|
65
|
+
opts.on("--list-push VALUES", "`VALUES := List<VALUE>[separator:,]`. Push VALUES into a list.") do |values|
|
66
|
+
arguments[:command] = :list_push
|
67
|
+
arguments[:values] = values.split(",")
|
68
|
+
end
|
69
|
+
|
70
|
+
opts.on("--list-show", "Shows `COUNT` elements of a list, starting from `INDEX`.") do
|
71
|
+
arguments[:command] = :list_show
|
72
|
+
end
|
73
|
+
|
74
|
+
opts.on("--list-delete", "Deletes `COUNT` elements from a list, starting from `INDEX`.") do
|
75
|
+
arguments[:command] = :list_delete
|
76
|
+
end
|
77
|
+
|
78
|
+
opts.on("--deep", "Shows the values associated with KEYS (recursively) even if they contain sets or lists.") do
|
79
|
+
arguments[:deep] = true
|
80
|
+
end
|
81
|
+
|
82
|
+
opts.on("--no-echo", "When prompted for a value, doesn't show it while typing.") do
|
83
|
+
arguments[:echo] = false
|
84
|
+
end
|
85
|
+
|
86
|
+
opts.on("--force", "Doesn't ask for confirmation.") do
|
87
|
+
arguments[:force] = true
|
88
|
+
end
|
89
|
+
|
90
|
+
opts.on("-h", "--help", "Prints this help") do
|
91
|
+
puts opts
|
92
|
+
exit
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
opt_parser.parse!(options)
|
97
|
+
|
98
|
+
if Utils.blank?(arguments[:command])
|
99
|
+
puts opt_parser
|
100
|
+
exit 1
|
101
|
+
end
|
102
|
+
|
103
|
+
if arguments[:index] && arguments[:index] < 0
|
104
|
+
puts "`INDEX` must be greater than or equal to 0."
|
105
|
+
puts opt_parser
|
106
|
+
exit 1
|
107
|
+
end
|
108
|
+
|
109
|
+
if arguments[:count] <= 0
|
110
|
+
puts "`COUNT` must be greater than 0."
|
111
|
+
puts opt_parser
|
112
|
+
exit 1
|
113
|
+
end
|
114
|
+
|
115
|
+
arguments[:file] = File.expand_path(arguments[:file])
|
116
|
+
|
117
|
+
Command.perform(arguments: arguments)
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require_relative "command/setup"
|
2
|
+
require_relative "command/create_set"
|
3
|
+
require_relative "command/set_set"
|
4
|
+
require_relative "command/show_keys"
|
5
|
+
require_relative "command/set_delete"
|
6
|
+
require_relative "command/create_list"
|
7
|
+
require_relative "command/list_push"
|
8
|
+
require_relative "command/list_show"
|
9
|
+
require_relative "command/list_delete"
|
10
|
+
|
11
|
+
module Shashi
|
12
|
+
module Command
|
13
|
+
|
14
|
+
def self.perform(arguments:)
|
15
|
+
case arguments[:command]
|
16
|
+
when :setup
|
17
|
+
Setup.perform(file: arguments[:file])
|
18
|
+
when :create_set
|
19
|
+
CreateSet.perform(
|
20
|
+
file: arguments[:file],
|
21
|
+
path: arguments[:path],
|
22
|
+
set_name: arguments[:set_name],
|
23
|
+
force: arguments[:force]
|
24
|
+
)
|
25
|
+
when :set_set
|
26
|
+
SetSet.perform(
|
27
|
+
file: arguments[:file],
|
28
|
+
path: arguments[:path],
|
29
|
+
key: arguments[:key],
|
30
|
+
value: arguments[:value],
|
31
|
+
force: arguments[:force],
|
32
|
+
echo: arguments[:echo]
|
33
|
+
)
|
34
|
+
when :show_keys
|
35
|
+
ShowKeys.perform(
|
36
|
+
file: arguments[:file],
|
37
|
+
path: arguments[:path],
|
38
|
+
keys: arguments[:keys],
|
39
|
+
deep: arguments[:deep],
|
40
|
+
)
|
41
|
+
when :delete
|
42
|
+
SetDelete.perform(
|
43
|
+
file: arguments[:file],
|
44
|
+
path: arguments[:path],
|
45
|
+
key: arguments[:key],
|
46
|
+
force: arguments[:force],
|
47
|
+
)
|
48
|
+
when :create_list
|
49
|
+
CreateList.perform(
|
50
|
+
file: arguments[:file],
|
51
|
+
path: arguments[:path],
|
52
|
+
list_name: arguments[:list_name],
|
53
|
+
force: arguments[:force]
|
54
|
+
)
|
55
|
+
when :list_push
|
56
|
+
ListPush.perform(
|
57
|
+
file: arguments[:file],
|
58
|
+
path: arguments[:path],
|
59
|
+
values: arguments[:values],
|
60
|
+
index: arguments[:index]
|
61
|
+
)
|
62
|
+
when :list_show
|
63
|
+
ListShow.perform(
|
64
|
+
file: arguments[:file],
|
65
|
+
path: arguments[:path],
|
66
|
+
index: arguments[:index],
|
67
|
+
count: arguments[:count]
|
68
|
+
)
|
69
|
+
when :list_delete
|
70
|
+
ListDelete.perform(
|
71
|
+
file: arguments[:file],
|
72
|
+
path: arguments[:path],
|
73
|
+
index: arguments[:index],
|
74
|
+
count: arguments[:count]
|
75
|
+
)
|
76
|
+
else
|
77
|
+
puts "Command `#{arguments[:command]}` is not recognized."
|
78
|
+
exit 1
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Shashi
|
2
|
+
module Command
|
3
|
+
module CreateList
|
4
|
+
|
5
|
+
def self.perform(file:, path:, list_name:, force:)
|
6
|
+
data = Utils::Database.read_database(file: file)
|
7
|
+
data_reference = Utils::Data.walk(data: data, path: path)
|
8
|
+
if data_reference.has_key?(list_name)
|
9
|
+
if force
|
10
|
+
update_data(file: file, data: data, data_reference: data_reference, list_name: list_name)
|
11
|
+
else
|
12
|
+
print "The key #{list_name} already exists at #{path.join(".")}, overwrite? [y/N] "
|
13
|
+
choice = gets
|
14
|
+
case choice.strip
|
15
|
+
when "y"
|
16
|
+
update_data(file: file, data: data, data_reference: data_reference, list_name: list_name)
|
17
|
+
when ""
|
18
|
+
# do nothing
|
19
|
+
when "N"
|
20
|
+
# do nothing
|
21
|
+
else
|
22
|
+
puts "Choice not understood, ignoring."
|
23
|
+
end
|
24
|
+
end
|
25
|
+
else
|
26
|
+
update_data(file: file, data: data, data_reference: data_reference, list_name: list_name)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def self.update_data(file:, data:, data_reference:, list_name:)
|
33
|
+
data_reference[list_name] = []
|
34
|
+
Utils::Database.write_database(file: file, data: data)
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Shashi
|
2
|
+
module Command
|
3
|
+
module CreateSet
|
4
|
+
|
5
|
+
def self.perform(file:, path:, set_name:, force:)
|
6
|
+
data = Utils::Database.read_database(file: file)
|
7
|
+
data_reference = Utils::Data.walk(data: data, path: path)
|
8
|
+
if data_reference.has_key?(set_name)
|
9
|
+
if force
|
10
|
+
update_data(file: file, data: data, data_reference: data_reference, set_name: set_name)
|
11
|
+
else
|
12
|
+
print "The key #{set_name} already exists at #{path.join(".")}, overwrite? [y/N] "
|
13
|
+
choice = gets
|
14
|
+
case choice.strip
|
15
|
+
when "y"
|
16
|
+
update_data(file: file, data: data, data_reference: data_reference, set_name: set_name)
|
17
|
+
when ""
|
18
|
+
# do nothing
|
19
|
+
when "N"
|
20
|
+
# do nothing
|
21
|
+
else
|
22
|
+
puts "Choice not understood, ignoring."
|
23
|
+
end
|
24
|
+
end
|
25
|
+
else
|
26
|
+
update_data(file: file, data: data, data_reference: data_reference, set_name: set_name)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def self.update_data(file:, data:, data_reference:, set_name:)
|
33
|
+
data_reference[set_name] = {}
|
34
|
+
Utils::Database.write_database(file: file, data: data)
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Shashi
|
2
|
+
module Command
|
3
|
+
module ListDelete
|
4
|
+
|
5
|
+
def self.perform(file:, path:, index: 0, count: 1)
|
6
|
+
data = Utils::Database.read_database(file: file)
|
7
|
+
data_reference = Utils::Data.walk(data: data, path: path)
|
8
|
+
if Utils::Data.is_list?(data_reference)
|
9
|
+
if index
|
10
|
+
if data_reference.count <= index
|
11
|
+
puts "The index `#{index}` is too high, the highest possible index for this list is currently `#{data_reference.count - 1}`."
|
12
|
+
exit 1
|
13
|
+
end
|
14
|
+
if data_reference.count < index + count
|
15
|
+
puts "Starting from index `#{index}` you can delete to a maximum of `#{data_reference.count - index}`."
|
16
|
+
exit 1
|
17
|
+
end
|
18
|
+
else
|
19
|
+
index = 0;
|
20
|
+
end
|
21
|
+
data_reference.reject!.with_index do |value, i|
|
22
|
+
i >= index && i < index + count
|
23
|
+
end
|
24
|
+
Utils::Database.write_database(file: file, data: data)
|
25
|
+
else
|
26
|
+
puts "Path `#{path.join(".")}` doesn't lead to a list."
|
27
|
+
exit 1
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Shashi
|
2
|
+
module Command
|
3
|
+
module ListPush
|
4
|
+
|
5
|
+
def self.perform(file:, path:, values: [], index: -1)
|
6
|
+
data = Utils::Database.read_database(file: file)
|
7
|
+
data_reference = Utils::Data.walk(data: data, path: path)
|
8
|
+
if Utils::Data.is_list?(data_reference)
|
9
|
+
if index
|
10
|
+
if data_reference.count <= index
|
11
|
+
puts "The index `#{index}` is too high, the highest possible index for this list is currently `#{data_reference.count - 1}`."
|
12
|
+
exit 1
|
13
|
+
end
|
14
|
+
else
|
15
|
+
index = -1;
|
16
|
+
end
|
17
|
+
data_reference.insert(index, *values)
|
18
|
+
Utils::Database.write_database(file: file, data: data)
|
19
|
+
else
|
20
|
+
puts "Path `#{path.join(".")}` doesn't lead to a list."
|
21
|
+
exit 1
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Shashi
|
2
|
+
module Command
|
3
|
+
module ListShow
|
4
|
+
|
5
|
+
def self.perform(file:, path:, index: 0, count: 1)
|
6
|
+
data = Utils::Database.read_database(file: file)
|
7
|
+
data_reference = Utils::Data.walk(data: data, path: path)
|
8
|
+
if Utils::Data.is_list?(data_reference)
|
9
|
+
if index
|
10
|
+
if data_reference.count <= index
|
11
|
+
puts "The index `#{index}` is too high, the highest possible index for this list is currently `#{data_reference.count - 1}`."
|
12
|
+
exit 1
|
13
|
+
end
|
14
|
+
if data_reference.count < index + count
|
15
|
+
puts "Starting from index `#{index}` you can access to a maximum of `#{data_reference.count - index}`."
|
16
|
+
exit 1
|
17
|
+
end
|
18
|
+
else
|
19
|
+
index = 0;
|
20
|
+
end
|
21
|
+
range_string = if index == index + count - 1
|
22
|
+
index
|
23
|
+
else
|
24
|
+
"#{index}-#{index + count}"
|
25
|
+
end
|
26
|
+
puts "`#{path.join(".")}`[#{range_string}]:"
|
27
|
+
data_reference[index..(index + count - 1)].each do |item|
|
28
|
+
content = if Utils::Data.is_list?(item)
|
29
|
+
"#List#"
|
30
|
+
elsif Utils::Data.is_set?(item)
|
31
|
+
"#Set#"
|
32
|
+
else
|
33
|
+
item
|
34
|
+
end
|
35
|
+
puts "- #{content}"
|
36
|
+
end
|
37
|
+
else
|
38
|
+
puts "Path `#{path.join(".")}` doesn't lead to a list."
|
39
|
+
exit 1
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Shashi
|
2
|
+
module Command
|
3
|
+
module SetDelete
|
4
|
+
|
5
|
+
def self.perform(file:, path:, key:, force:)
|
6
|
+
data = Utils::Database.read_database(file: file)
|
7
|
+
data_reference = Utils::Data.walk(data: data, path: path)
|
8
|
+
if data_reference.has_key?(key)
|
9
|
+
if force
|
10
|
+
update_data(file: file, data: data, data_reference: data_reference, key: key)
|
11
|
+
else
|
12
|
+
value = data_reference[key]
|
13
|
+
if Utils::Data.is_list?(value)
|
14
|
+
if confirms_deletion?(key: key, path: path, value_type: "#List#")
|
15
|
+
update_data(file: file, data: data, data_reference: data_reference, key: key)
|
16
|
+
end
|
17
|
+
elsif Utils::Data.is_set?(value)
|
18
|
+
if confirms_deletion?(key: key, path: path, value_type: "#Set#")
|
19
|
+
update_data(file: file, data: data, data_reference: data_reference, key: key)
|
20
|
+
end
|
21
|
+
else
|
22
|
+
update_data(file: file, data: data, data_reference: data_reference, key: key)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
else
|
26
|
+
puts "There is no key `#{key}` at `#{path.join(".")}`"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def self.confirms_deletion?(key:, path:, value_type:)
|
33
|
+
print "The key `#{key}` at `#{path.join(".")}` contains a `#{value_type}`, confirm deletion? [y/N] "
|
34
|
+
choice = gets.strip
|
35
|
+
case choice
|
36
|
+
when "y"
|
37
|
+
true
|
38
|
+
when ""
|
39
|
+
# do nothing
|
40
|
+
false
|
41
|
+
when "N"
|
42
|
+
# do nothing
|
43
|
+
false
|
44
|
+
else
|
45
|
+
puts "Choice not understood, ignoring."
|
46
|
+
false
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.update_data(file:, data:, data_reference:, key:)
|
51
|
+
data_reference.delete(key)
|
52
|
+
Utils::Database.write_database(file: file, data: data)
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|