command_tree 0.1.1 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 669d97d138e5a9b26b34f51561538f6a40e306f3f22682c854f74d8d1a6e76a6
4
- data.tar.gz: bdf8eea29094e6ec232bd32b9ed2b5f0b619b851c48b6e1049325c91dd6610a4
3
+ metadata.gz: 8aed81a9c05e2f2e5610157af9f29faa54e699caddd21f4c5bc6bdf021b9b56a
4
+ data.tar.gz: 54245dba9ee4085224117ca3891bfcd4f1eda142deae333844fa74787a91ad9f
5
5
  SHA512:
6
- metadata.gz: b169b9c1795f6d82cf335292a53c624b0ecf8cda0c99582753ef8beee229b43833a569bb4698d6d6c3e05aadf83152ffa25b8bb0727843777d19383dee22e4fb
7
- data.tar.gz: b2cc19380bff9655d04e0847624f042781a4359fd548bea49eced3d6e898a485b0313c0f0971630a5e310380b16c391404c7216209b016627ab110d3c6b55513
6
+ metadata.gz: fa3f0b10340221ed303c805529652ea0ce40d2e4a58b26eaeaea14afc03c372c271fe217eb56cc55507bfc44183265a984f237a212bf8f36520b3eb3279840a0
7
+ data.tar.gz: f48773a682ce667a3a285eade3cd75b10112acf6922016aa4cc8c0a05261be56c4006d0ea942962bfdcb4856ca87f27fbac5e84fcef98ee55b9cafcacc56bb79
@@ -1,24 +1,23 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- command_tree (0.1.1)
5
- colorize (~> 0.8)
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.11.3)
12
- rake (10.5.0)
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 (~> 5.0)
21
- rake (~> 10.0)
19
+ minitest
20
+ rake
22
21
 
23
22
  BUNDLED WITH
24
- 1.16.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 can register a command category (a node that contains commands)
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.
@@ -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", "~> 0.8"
24
+ spec.add_dependency "colorize"
25
25
 
26
- spec.add_development_dependency "bundler", "~> 1.16"
27
- spec.add_development_dependency "rake", "~> 10.0"
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
@@ -1,47 +1,78 @@
1
1
  require 'io/console'
2
- require 'colorize'
3
- require 'colorized_string'
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
- calls[path] = { name: name, options: options, block: block } if block_given?
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
- private
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] = { name: name, options: options } unless 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
- children = calls.keys.select { |key| key.start_with?(path) && key.length == (path.length + 1) }
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
- table_content = []
90
+ menu = TextMenu.new(40)
54
91
 
55
92
  children.each do |child|
56
- child_node = calls[child]
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
- table(table_content, 40)
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
@@ -1,3 +1,3 @@
1
1
  module CommandTree
2
- VERSION = "0.1.1"
2
+ VERSION = '0.2.0'
3
3
  end
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.1.1
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: 2018-09-28 00:00:00.000000000 Z
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.8'
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.8'
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: '10.0'
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: '10.0'
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: '5.0'
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: '5.0'
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
- rubyforge_project:
111
- rubygems_version: 2.7.3
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: []