dencli 0.1.0 → 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: c630a25a72718f5826c05cef883db24a1c86fb15e2a6b5cc7ff8dfaf2ddd4097
4
- data.tar.gz: 7de6920626c61aa2982680fd0df38d7f1ff229913d816adcccf3446b7243a7e3
3
+ metadata.gz: 81af9b5ecb0bfa6a5c5d0546bef0424ab3301e5306222a9a0db5050039f20049
4
+ data.tar.gz: cdb5ce86ac9ba4e8c574c5c3b688b3e82874c35a9b6873cf9f294800925c9d31
5
5
  SHA512:
6
- metadata.gz: df74bf563daf0e6c6ddb1863ce364225f05d5a8814857da4370dd002851d67f9d9cab422061d9e4a7bb7df6c4b2c3956a74f76c9be20ff3ad661ff3d234b5dea
7
- data.tar.gz: 99e2f4dc73b837f71a4c167e837b3ca88c6176d85e603bdf0cdd5cd1b21551b8e6c37f8ecf2bd7edfd1345cc65581789caded4a8a3e43d1670ef44f79c12e630
6
+ metadata.gz: b82a4b1d8f179b3b12d481f6bd63b891d70f695987d9b0efc81eb0c40f34d984b14b8714c25b0a1b6ef090b6be97055040e5cc0d967624e1c821112a1e0b8bd5
7
+ data.tar.gz: 64eb45044b043717ad869b3cab606e5b2283c2666fa82df2ffc3b5f13e30de3d3b9672ab72ab64424688fda8c5535542217511b72d13da509cc88dd91f546869
data/.gitignore CHANGED
@@ -6,3 +6,4 @@
6
6
  /pkg/
7
7
  /spec/reports/
8
8
  /tmp/
9
+ *.sw[o-t]
@@ -4,6 +4,11 @@ class DenCli
4
4
  class UnknownCommand < UsageError
5
5
  end
6
6
 
7
+ require_relative 'dencli/cmd'
8
+ require_relative 'dencli/sub'
9
+ require_relative 'dencli/interactive'
10
+ require_relative 'dencli/version'
11
+
7
12
  class <<self
8
13
  # Helper Function for generate Regular Expressions of string,
9
14
  # which matches all strings which has parts fron beginning of the given string.
@@ -47,107 +52,7 @@ class DenCli
47
52
  alias g gen_aliases
48
53
  end
49
54
 
50
- class CMD
51
- attr_reader :parent, :name, :desc, :exe
52
- def initialize parent, name, desc, exe
53
- raise "Proc expected, instead of: #{exe.inspect}" unless Proc === exe
54
- @parent, @name, @desc, @exe = parent, name, desc, exe
55
- end
56
-
57
- def _full_cmd post
58
- parent._full_cmd [@name]+post
59
- end
60
-
61
- def full_cmd
62
- _full_cmd []
63
- end
64
-
65
- def call *a
66
- @exe.call *a
67
- end
68
-
69
- def help
70
- "#{parent.full_cmd.join ' '} #{name}\n#{ desc}"
71
- end
72
-
73
- def inspect
74
- "#<%s:0x%x %s @name=%p @desc=%p @parent=<%s:0x%x %s> @exe=<arity=%d>>" % [
75
- self.class.name, self.object_id, self.full_cmd,
76
- @name, @desc, @parent.class.name, @parent.class.object_id, @parent.full_cmd,
77
- @exe.arity
78
- ]
79
- end
80
- end
81
-
82
- class Sub
83
- attr_reader :parent, :name, :desc, :subs
84
- def initialize parent, name, desc
85
- @parent, @name, @desc, @subs, @aliases = parent, name, desc, {}, {}
86
- end
87
-
88
- def _full_cmd post
89
- parent._full_cmd [@name]+post
90
- end
91
-
92
- def full_cmd
93
- _full_cmd []
94
- end
95
-
96
- def [] k
97
- @aliases[k]
98
- end
99
-
100
- def help n = nil, *a
101
- if n.nil?
102
- r = "#{full_cmd.join ' '}: #{desc}\n\n"
103
- m = @subs.map {|k,_| k.length }.max
104
- @subs.each do |k, c|
105
- r += " % -#{m}s %s\n" % [k, c.desc] unless k.nil?
106
- end
107
- r
108
- elsif @aliases.has_key? n
109
- @aliases[n].help *a
110
- else
111
- raise UnknownCommand, "unknown command: #{_full_cmd( [n])[1..-1].join ' '}, available for #{full_cmd[1..-1].join' '}: #{@subs.keys.inspect}"
112
- end
113
- end
114
-
115
- def call *a
116
- n, *a = *a
117
- if @aliases.has_key? n
118
- @aliases[n].call *a
119
- else
120
- raise UnknownCommand, "unknown command: #{_full_cmd( [n])[1..-1].join ' '}, available for #{full_cmd[1..-1].join' '}: #{@subs.keys.inspect}"
121
- end
122
- end
123
-
124
- def _add name, min, obj, aliases
125
- name = name.to_s unless name.nil?
126
- @subs[name] = obj
127
- DenCli.gen_aliases( name, min) {|a| @aliases[a] = obj }
128
- if aliases
129
- [*aliases].each {|a| @aliases[a] = obj }
130
- end
131
- obj
132
- end
133
- private :_add
134
-
135
- def sub name, desc, min: nil, aliases: nil, &exe
136
- r = _add name, min, Sub.new( self, name, desc), aliases
137
- block_given? ? yield( r) : r
138
- end
139
-
140
- def cmd name, desc, min: nil, aliases: nil, &exe
141
- _add name, min, CMD.new( self, name, desc, exe), aliases
142
- end
143
-
144
- def inspect
145
- "#<%s:0x%x %s @name=%p @desc=%p @subs={%s} @aliases={%s} @parent=<%s:0x%x %s>>" % [
146
- self.class.name, self.object_id, self.full_cmd,
147
- @name, @desc, @subs.keys.join(', '), @aliases.keys.join(', '), @parent.class.name, @parent.class.object_id, @parent.full_cmd
148
- ]
149
- end
150
- end
55
+ attr_reader :subs
151
56
 
152
57
  def initialize progname, desc
153
58
  @subs = Sub.new self, progname, desc
@@ -180,4 +85,8 @@ class DenCli
180
85
  def [] k
181
86
  @subs[k]
182
87
  end
88
+
89
+ def interactive *args, **opts
90
+ Interactive.new self, *args, **opts
91
+ end
183
92
  end
@@ -0,0 +1,31 @@
1
+ require_relative '../dencli'
2
+
3
+ class DenCli::CMD
4
+ attr_reader :parent, :name, :desc, :exe, :completion
5
+
6
+ def initialize parent, name, desc, exe
7
+ raise "Proc expected, instead of: #{exe.inspect}" unless Proc === exe
8
+ @parent, @name, @desc, @exe = parent, name, desc, exe
9
+ completion {|*a| [] }
10
+ end
11
+
12
+ def _full_cmd( post) parent._full_cmd [@name]+post end
13
+ def full_cmd() _full_cmd [] end
14
+ def call( *a) @exe.call *a end
15
+ def help() "#{parent.full_cmd.join ' '} #{name}\n#{ desc}" end
16
+
17
+ def complete( *pre, str) @completion.call *pre, str end
18
+
19
+ def completion &exe
20
+ @completion = exe
21
+ self
22
+ end
23
+
24
+ def inspect
25
+ "#<%s:0x%x %s @name=%p @desc=%p @parent=<%s:0x%x %s> @exe=<arity=%d>>" % [
26
+ self.class.name, self.object_id, self.full_cmd,
27
+ @name, @desc, @parent.class.name, @parent.class.object_id, @parent.full_cmd,
28
+ @exe.arity
29
+ ]
30
+ end
31
+ end
@@ -0,0 +1,133 @@
1
+ require_relative '../dencli'
2
+
3
+ class DenCli::Interactive
4
+ attr_reader :cl, :prompt, :cur, :histfile
5
+
6
+ def initialize cl, prompt, histfile: nil
7
+ require 'readline'
8
+ @cl, self.prompt = cl, prompt
9
+ @cur = cl.subs
10
+ cur.instance_variable_set :@name, ''
11
+
12
+ @histfile =
13
+ case histfile
14
+ when nil then nil
15
+ when Pathname then histfile
16
+ else Pathname.new histfile.to_s
17
+ end
18
+ read_history if @histfile
19
+
20
+ Readline.vi_editing_mode rescue NotImplementedError
21
+ Readline.completion_append_character = " "
22
+ Readline.completion_proc = method :complete
23
+
24
+ prepare_sub cl.subs
25
+ cl.cmd :exit, "exit", min: 2 do
26
+ exit 0
27
+ end
28
+ cl.subs.aliases['?'] = cl.subs.subs['help']
29
+ cl.subs.subs.delete 'cli'
30
+
31
+ stty_save = %x`stty -g`.chomp
32
+ trap "INT" do
33
+ system "stty", stty_save
34
+ Readline.refresh_line
35
+ end
36
+ end
37
+
38
+ def history_file file = nil
39
+ file =
40
+ case file
41
+ when Pathname then file
42
+ when nil then @histfile
43
+ else Pathname.new file.to_s
44
+ end
45
+ end
46
+
47
+ def read_history file = nil
48
+ file = history_file file
49
+ return unless file and file.exist?
50
+ file.each_line do |line|
51
+ Readline::HISTORY.push line.chomp
52
+ end
53
+ end
54
+
55
+ def write_history file = nil
56
+ file = history_file file
57
+ return unless file
58
+ file.open 'w+' do |f|
59
+ Readline::HISTORY.each do |line|
60
+ f.puts line
61
+ end
62
+ end
63
+ end
64
+
65
+ def prompt= s
66
+ @prompt = s.to_s
67
+ end
68
+
69
+ def complete s
70
+ ws = words Readline.line_buffer
71
+ @cur.complete *ws[0...-1], s
72
+ end
73
+
74
+ def sub *args, cur: nil
75
+ args.inject( cur || @cur) do |r, a|
76
+ return nil unless r.is_a? DenCli::Sub
77
+ r[a]
78
+ end
79
+ end
80
+ private :sub
81
+
82
+ def words line = nil
83
+ r = line.split " "
84
+ r.push '' if ' ' == line[-1]
85
+ r
86
+ end
87
+ private :words
88
+
89
+ def prepare_sub c
90
+ c.subs.values.each do |n|
91
+ case n
92
+ when DenCli::Sub
93
+ n.cmd :exit, "<- #{n.parent.full_cmd.join ' '} - #{n.parent.desc[3..-1]}", min: 2 do
94
+ @cur = n.parent
95
+ end
96
+ n.cmd '', "", min: 2, aliases: [nil] do
97
+ @cur = n
98
+ end
99
+ n.subs.delete ''
100
+ n.aliases['?'] = n.subs['help']
101
+ prepare_sub n
102
+ when DenCli::CMD
103
+ else raise "Unsupported sub-type: #{x}"
104
+ end
105
+ end
106
+ end
107
+
108
+ def read
109
+ line = Readline.readline( "#{prompt}#{cur.full_cmd.join ' '}> ", true)
110
+ return nil if line.nil?
111
+ Readline::HISTORY.pop if /^\s*$/ =~ line
112
+ if 0 < Readline::HISTORY.length-2 and Readline::HISTORY[Readline::HISTORY.length-2] == line
113
+ Readline::HISTORY.pop
114
+ end
115
+ line.split " "
116
+ end
117
+
118
+ def step
119
+ line = read
120
+ return nil if line.nil?
121
+ begin
122
+ cur.call *line
123
+ rescue ::UsageError
124
+ STDERR.puts "! #$!"
125
+ end
126
+ true
127
+ end
128
+
129
+ def run
130
+ while step
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,74 @@
1
+ require_relative '../dencli'
2
+
3
+ class DenCli::Sub
4
+ attr_reader :parent, :name, :desc, :subs, :aliases
5
+
6
+ def initialize parent, name, desc
7
+ @parent, @name, @desc, @subs, @aliases = parent, name, "-> #{desc}", {}, {}
8
+ end
9
+
10
+ def _full_cmd( post) parent._full_cmd [@name]+post end
11
+ def full_cmd() _full_cmd [] end
12
+ def []( k) @aliases[k] end
13
+
14
+ def help n = nil, *a
15
+ if n.nil?
16
+ r = "#{full_cmd.join ' '}: #{desc}\n\n"
17
+ m = @subs.map {|k,_| k.length }.max
18
+ @subs.each do |k, c|
19
+ r += " % -#{m}s %s\n" % [k, c.desc] unless k.nil?
20
+ end
21
+ r
22
+ elsif @aliases.has_key? n
23
+ @aliases[n].help *a
24
+ else
25
+ raise UnknownCommand, "unknown command: #{_full_cmd( [n])[1..-1].join ' '}, available for #{full_cmd[1..-1].join' '}: #{@subs.keys.join ' '}"
26
+ end
27
+ end
28
+
29
+ def call *a
30
+ n, *a = *a
31
+ if @aliases.has_key? n
32
+ @aliases[n].call *a
33
+ else
34
+ raise UnknownCommand, "unknown command: #{_full_cmd( [n])[1..-1].join ' '}, available for #{full_cmd[1..-1].join' '}: #{@subs.keys.join ' '}"
35
+ end
36
+ end
37
+
38
+ def _add name, min, obj, aliases
39
+ name = name.to_s unless name.nil?
40
+ @subs[name] = obj
41
+ DenCli.gen_aliases( name, min) {|a| @aliases[a] = obj }
42
+ if aliases
43
+ [*aliases].each {|a| @aliases[a] = obj }
44
+ end
45
+ obj
46
+ end
47
+ private :_add
48
+
49
+ def sub name, desc, min: nil, aliases: nil, &exe
50
+ r = _add name, min, DenCli::Sub.new( self, name, desc), aliases
51
+ block_given? ? yield( r) : r
52
+ end
53
+
54
+ def cmd name, desc, min: nil, aliases: nil, &exe
55
+ _add name, min, DenCli::CMD.new( self, name, desc, exe), aliases
56
+ end
57
+
58
+ def complete *pre, str
59
+ if pre.empty?
60
+ @subs.keys.grep /\A#{Regexp.escape str}/
61
+ elsif sub = @subs[pre[0]]
62
+ sub.complete *pre[1..-1], str
63
+ else
64
+ STDOUT.print "\a"
65
+ end
66
+ end
67
+
68
+ def inspect
69
+ "#<%s:0x%x %s @name=%p @desc=%p @subs={%s} @aliases={%s} @parent=<%s:0x%x %s>>" % [
70
+ self.class.name, self.object_id, self.full_cmd,
71
+ @name, @desc, @subs.keys.join(', '), @aliases.keys.join(', '), @parent.class.name, @parent.class.object_id, @parent.full_cmd
72
+ ]
73
+ end
74
+ end
@@ -1,3 +1,3 @@
1
1
  class DenCli
2
- VERSION = '0.1.0'
2
+ VERSION = '0.2.0'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dencli
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Denis Knauf
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-12-13 00:00:00.000000000 Z
11
+ date: 2020-12-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -41,6 +41,9 @@ files:
41
41
  - bin/example.rb
42
42
  - dencli.gemspec
43
43
  - lib/dencli.rb
44
+ - lib/dencli/cmd.rb
45
+ - lib/dencli/interactive.rb
46
+ - lib/dencli/sub.rb
44
47
  - lib/dencli/version.rb
45
48
  homepage: https://git.denkn.at/deac/dencli
46
49
  licenses: