shashi 0.1.0
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 +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
|