command_tree 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +7 -8
- data/README.md +14 -1
- data/command_tree.gemspec +3 -4
- data/lib/command_tree/command.rb +33 -0
- data/lib/command_tree/group.rb +31 -0
- data/lib/command_tree/text_menu.rb +55 -0
- data/lib/command_tree/tree.rb +55 -37
- data/lib/command_tree/version.rb +1 -1
- metadata +21 -33
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8aed81a9c05e2f2e5610157af9f29faa54e699caddd21f4c5bc6bdf021b9b56a
|
4
|
+
data.tar.gz: 54245dba9ee4085224117ca3891bfcd4f1eda142deae333844fa74787a91ad9f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fa3f0b10340221ed303c805529652ea0ce40d2e4a58b26eaeaea14afc03c372c271fe217eb56cc55507bfc44183265a984f237a212bf8f36520b3eb3279840a0
|
7
|
+
data.tar.gz: f48773a682ce667a3a285eade3cd75b10112acf6922016aa4cc8c0a05261be56c4006d0ea942962bfdcb4856ca87f27fbac5e84fcef98ee55b9cafcacc56bb79
|
data/Gemfile.lock
CHANGED
@@ -1,24 +1,23 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
command_tree (0.
|
5
|
-
colorize
|
4
|
+
command_tree (0.2.0)
|
5
|
+
colorize
|
6
6
|
|
7
7
|
GEM
|
8
8
|
remote: https://rubygems.org/
|
9
9
|
specs:
|
10
10
|
colorize (0.8.1)
|
11
|
-
minitest (5.
|
12
|
-
rake (
|
11
|
+
minitest (5.14.1)
|
12
|
+
rake (13.0.1)
|
13
13
|
|
14
14
|
PLATFORMS
|
15
15
|
ruby
|
16
16
|
|
17
17
|
DEPENDENCIES
|
18
|
-
bundler (~> 1.16)
|
19
18
|
command_tree!
|
20
|
-
minitest
|
21
|
-
rake
|
19
|
+
minitest
|
20
|
+
rake
|
22
21
|
|
23
22
|
BUNDLED WITH
|
24
|
-
1.
|
23
|
+
2.1.4
|
data/README.md
CHANGED
@@ -29,7 +29,7 @@ You start by creating a new tree
|
|
29
29
|
t = CommandTree::Tree.new
|
30
30
|
```
|
31
31
|
|
32
|
-
Then you
|
32
|
+
Then you register a command category (a node that contains a group of commands)
|
33
33
|
|
34
34
|
```ruby
|
35
35
|
t.register 'a', 'Applications' # associate the character 'a' to a category called 'applications'
|
@@ -52,6 +52,19 @@ it will print the toplevel categories and commands and wait for you to press a c
|
|
52
52
|
|
53
53
|
when the tree reachs a leaf it'll exit, if a command is the leaf it will execute it and exit the tree giving your code the control again.
|
54
54
|
|
55
|
+
|
56
|
+
There is another way to define a group of commands in a nested way using `Tree#group` method as follows
|
57
|
+
|
58
|
+
```ruby
|
59
|
+
t = CommandTree::Tree.new
|
60
|
+
t.group 'a', 'Applications' do |g|
|
61
|
+
g.register 'g','Google Chrome' do
|
62
|
+
system 'google-chrome-stable'
|
63
|
+
end
|
64
|
+
end
|
65
|
+
t.show
|
66
|
+
```
|
67
|
+
|
55
68
|
## Development
|
56
69
|
|
57
70
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
data/command_tree.gemspec
CHANGED
@@ -21,9 +21,8 @@ Gem::Specification.new do |spec|
|
|
21
21
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
22
22
|
spec.require_paths = ["lib"]
|
23
23
|
|
24
|
-
spec.add_dependency "colorize"
|
24
|
+
spec.add_dependency "colorize"
|
25
25
|
|
26
|
-
spec.add_development_dependency "
|
27
|
-
spec.add_development_dependency "
|
28
|
-
spec.add_development_dependency "minitest", "~> 5.0"
|
26
|
+
spec.add_development_dependency "rake"
|
27
|
+
spec.add_development_dependency "minitest"
|
29
28
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module CommandTree
|
2
|
+
# A command class
|
3
|
+
class Command
|
4
|
+
attr_reader :name, :desc, :prefix, :block
|
5
|
+
|
6
|
+
def initialize(prefix, name, options = {}, &block)
|
7
|
+
@prefix = prefix
|
8
|
+
@name = name
|
9
|
+
@desc = options[:desc]
|
10
|
+
@block = block
|
11
|
+
end
|
12
|
+
|
13
|
+
def execute
|
14
|
+
print_banner
|
15
|
+
block.call
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def print_banner
|
21
|
+
puts pretty_name if name
|
22
|
+
puts pretty_description if desc
|
23
|
+
end
|
24
|
+
|
25
|
+
def pretty_name
|
26
|
+
name.light_magenta.bold if name
|
27
|
+
end
|
28
|
+
|
29
|
+
def pretty_description
|
30
|
+
desc.to_s.light_black if desc
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module CommandTree
|
2
|
+
# A group of commands
|
3
|
+
class Group
|
4
|
+
attr_reader :name, :desc, :prefix
|
5
|
+
|
6
|
+
def initialize(prefix, name, options = {})
|
7
|
+
@prefix = prefix
|
8
|
+
@name = name
|
9
|
+
@desc = options[:desc]
|
10
|
+
end
|
11
|
+
|
12
|
+
def execute
|
13
|
+
print_banner
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def print_banner
|
19
|
+
puts pretty_name if name
|
20
|
+
puts pretty_description if desc
|
21
|
+
end
|
22
|
+
|
23
|
+
def pretty_name
|
24
|
+
name.light_magenta.bold if name
|
25
|
+
end
|
26
|
+
|
27
|
+
def pretty_description
|
28
|
+
desc.to_s.light_black if desc
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'colorize'
|
2
|
+
require 'colorized_string'
|
3
|
+
|
4
|
+
module CommandTree
|
5
|
+
# responsible for showing the menu in terminal
|
6
|
+
class TextMenu
|
7
|
+
def initialize(item_width)
|
8
|
+
@item_width = item_width
|
9
|
+
@items = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def render
|
13
|
+
_, screen_width = IO.console.winsize
|
14
|
+
items_per_row = screen_width / item_width
|
15
|
+
|
16
|
+
names = items.dup.map! { |item| item_name(item) }
|
17
|
+
descs = items.dup.map! { |item| item_desc(item) }
|
18
|
+
|
19
|
+
until names.empty?
|
20
|
+
puts names.shift(items_per_row).join
|
21
|
+
row_descs = descs.shift(items_per_row).join
|
22
|
+
puts row_descs unless row_descs.strip.empty?
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def add(item)
|
27
|
+
items << item
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
attr_reader :items, :item_width
|
33
|
+
|
34
|
+
def item_name(item)
|
35
|
+
prefix = item.prefix
|
36
|
+
arrow = ' → '
|
37
|
+
|
38
|
+
name_width = item_width - prefix.length - arrow.length
|
39
|
+
name = (item.is_a?(Group) ? '+' : '') + item.name
|
40
|
+
name = name.ljust(name_width)
|
41
|
+
|
42
|
+
colored_name = item.is_a?(Group) ? name.light_magenta.bold : name.cyan
|
43
|
+
|
44
|
+
(prefix.green + arrow.light_black + colored_name)
|
45
|
+
end
|
46
|
+
|
47
|
+
def item_desc(item)
|
48
|
+
desc = item.desc.to_s
|
49
|
+
return desc.ljust(item_width) if desc.strip.empty?
|
50
|
+
|
51
|
+
return (desc[0...item_width - 3] + '...').light_black if desc.length >= item_width
|
52
|
+
return desc.ljust(item_width).light_black
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
data/lib/command_tree/tree.rb
CHANGED
@@ -1,47 +1,78 @@
|
|
1
1
|
require 'io/console'
|
2
|
-
|
3
|
-
|
2
|
+
|
3
|
+
require_relative 'group'
|
4
|
+
require_relative 'command'
|
5
|
+
require_relative 'text_menu'
|
4
6
|
|
5
7
|
module CommandTree
|
8
|
+
# A tree of commands and associated keys for every node
|
6
9
|
class Tree
|
7
10
|
def initialize
|
8
|
-
@calls = { '' =>
|
11
|
+
@calls = { '' => nil }
|
9
12
|
end
|
10
13
|
|
14
|
+
# register a `path` to a `name` with a block of code if
|
15
|
+
# you wish it to be a command, the following `options` are
|
16
|
+
# supported:
|
17
|
+
# desc: a description of the item, as a help text for the user
|
11
18
|
def register(path, name, options = {}, &block)
|
19
|
+
path = path.to_s
|
20
|
+
name = name.to_s
|
21
|
+
prefix = path[-1]
|
22
|
+
|
12
23
|
insure_path(path, name, options)
|
13
|
-
|
24
|
+
return unless block_given?
|
25
|
+
|
26
|
+
calls[path] = Command.new(prefix, name, options, &block)
|
27
|
+
end
|
28
|
+
|
29
|
+
# define a group of commands (subtree)
|
30
|
+
# the method will create a subtree and pass it to
|
31
|
+
# the given block of code if you passed a block
|
32
|
+
# otherwise it works in a similar way to register
|
33
|
+
def group(prefix, name, options = {})
|
34
|
+
subtree = self.class.new
|
35
|
+
yield(subtree) if block_given?
|
36
|
+
|
37
|
+
merge(subtree, prefix, name, options)
|
14
38
|
end
|
15
39
|
|
40
|
+
# Start the tree, prints the first level and walk
|
41
|
+
# the user through the tree with keystroks
|
16
42
|
def show
|
17
43
|
execute_path('')
|
18
44
|
end
|
19
45
|
|
20
|
-
|
46
|
+
# merge a subtree with a prefix and a name
|
47
|
+
def merge(subtree, prefix, name, options = {})
|
48
|
+
register(prefix, name, options)
|
49
|
+
subtree.calls.each do |key, command|
|
50
|
+
next unless command
|
51
|
+
|
52
|
+
calls["#{prefix}#{key}"] = command
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
protected
|
21
57
|
|
22
58
|
attr_accessor :calls
|
23
59
|
|
60
|
+
private
|
61
|
+
|
24
62
|
def insure_path(path, name, options = {})
|
25
63
|
return if path.empty?
|
26
64
|
|
27
65
|
insure_path(path[0...-1], name, options)
|
28
|
-
calls[path] =
|
66
|
+
calls[path] = Group.new(path[-1], name, options) unless calls[path]
|
29
67
|
end
|
30
68
|
|
31
69
|
def execute_path(path)
|
32
70
|
return puts "#{path} couldn't be found..." unless calls.key?(path)
|
33
71
|
|
34
72
|
node = calls[path]
|
35
|
-
|
36
|
-
children.sort!
|
37
|
-
|
38
|
-
puts "#{node[:name]}:".light_magenta.bold if node.key?(:name)
|
39
|
-
|
40
|
-
description = node.dig(:options, :desc)
|
41
|
-
puts description.to_s.light_black if description
|
42
|
-
|
43
|
-
node[:block].call if node.key?(:block)
|
73
|
+
node.execute if node
|
44
74
|
|
75
|
+
children = get_children(path)
|
45
76
|
return if children.empty?
|
46
77
|
|
47
78
|
print_children(children)
|
@@ -49,34 +80,21 @@ module CommandTree
|
|
49
80
|
execute_path(path + choice)
|
50
81
|
end
|
51
82
|
|
83
|
+
def get_children(path)
|
84
|
+
calls.keys.select do |key|
|
85
|
+
key.start_with?(path) && key.length == (path.length + 1)
|
86
|
+
end.sort
|
87
|
+
end
|
88
|
+
|
52
89
|
def print_children(children)
|
53
|
-
|
90
|
+
menu = TextMenu.new(40)
|
54
91
|
|
55
92
|
children.each do |child|
|
56
|
-
|
57
|
-
|
58
|
-
output = child[-1].green
|
59
|
-
output << ' → '.light_black
|
60
|
-
|
61
|
-
output << if child_node.key?(:block)
|
62
|
-
" #{child_node[:name].ljust(40)}".cyan
|
63
|
-
else
|
64
|
-
"+#{child_node[:name].ljust(40)}".light_magenta.bold
|
65
|
-
end
|
66
|
-
|
67
|
-
table_content << output
|
93
|
+
menu.add calls[child]
|
68
94
|
end
|
69
95
|
|
70
|
-
|
96
|
+
menu.render
|
71
97
|
print "\n"
|
72
98
|
end
|
73
|
-
|
74
|
-
def table(items, item_width)
|
75
|
-
_, screen_width = IO.console.winsize
|
76
|
-
items_per_row = screen_width / item_width
|
77
|
-
items_dup = items.dup
|
78
|
-
|
79
|
-
puts items_dup.shift(items_per_row).join until items_dup.empty?
|
80
|
-
end
|
81
99
|
end
|
82
100
|
end
|
data/lib/command_tree/version.rb
CHANGED
metadata
CHANGED
@@ -1,71 +1,57 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: command_tree
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Emad Elsaid
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-08-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: colorize
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '0
|
19
|
+
version: '0'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - "
|
24
|
+
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '0
|
27
|
-
- !ruby/object:Gem::Dependency
|
28
|
-
name: bundler
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - "~>"
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: '1.16'
|
34
|
-
type: :development
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - "~>"
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: '1.16'
|
26
|
+
version: '0'
|
41
27
|
- !ruby/object:Gem::Dependency
|
42
28
|
name: rake
|
43
29
|
requirement: !ruby/object:Gem::Requirement
|
44
30
|
requirements:
|
45
|
-
- - "
|
31
|
+
- - ">="
|
46
32
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
33
|
+
version: '0'
|
48
34
|
type: :development
|
49
35
|
prerelease: false
|
50
36
|
version_requirements: !ruby/object:Gem::Requirement
|
51
37
|
requirements:
|
52
|
-
- - "
|
38
|
+
- - ">="
|
53
39
|
- !ruby/object:Gem::Version
|
54
|
-
version: '
|
40
|
+
version: '0'
|
55
41
|
- !ruby/object:Gem::Dependency
|
56
42
|
name: minitest
|
57
43
|
requirement: !ruby/object:Gem::Requirement
|
58
44
|
requirements:
|
59
|
-
- - "
|
45
|
+
- - ">="
|
60
46
|
- !ruby/object:Gem::Version
|
61
|
-
version: '
|
47
|
+
version: '0'
|
62
48
|
type: :development
|
63
49
|
prerelease: false
|
64
50
|
version_requirements: !ruby/object:Gem::Requirement
|
65
51
|
requirements:
|
66
|
-
- - "
|
52
|
+
- - ">="
|
67
53
|
- !ruby/object:Gem::Version
|
68
|
-
version: '
|
54
|
+
version: '0'
|
69
55
|
description: Builds trees of commands for the terminal, each node is either a group
|
70
56
|
of commands or the command itself, every node is associated with a character to
|
71
57
|
access it.
|
@@ -86,13 +72,16 @@ files:
|
|
86
72
|
- bin/setup
|
87
73
|
- command_tree.gemspec
|
88
74
|
- lib/command_tree.rb
|
75
|
+
- lib/command_tree/command.rb
|
76
|
+
- lib/command_tree/group.rb
|
77
|
+
- lib/command_tree/text_menu.rb
|
89
78
|
- lib/command_tree/tree.rb
|
90
79
|
- lib/command_tree/version.rb
|
91
80
|
homepage: https://www.github.com/emad-elsaid/command_tree
|
92
81
|
licenses:
|
93
82
|
- MIT
|
94
83
|
metadata: {}
|
95
|
-
post_install_message:
|
84
|
+
post_install_message:
|
96
85
|
rdoc_options: []
|
97
86
|
require_paths:
|
98
87
|
- lib
|
@@ -107,9 +96,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
107
96
|
- !ruby/object:Gem::Version
|
108
97
|
version: '0'
|
109
98
|
requirements: []
|
110
|
-
|
111
|
-
|
112
|
-
signing_key:
|
99
|
+
rubygems_version: 3.1.2
|
100
|
+
signing_key:
|
113
101
|
specification_version: 4
|
114
102
|
summary: Command trees for the terminal
|
115
103
|
test_files: []
|