command-set 0.8.2 → 0.8.3
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.
- data/doc/argumentDSL +2 -0
- data/lib/command-set/arguments.rb +63 -12
- data/lib/command-set/command-set.rb +2 -18
- data/lib/command-set/command.rb +11 -9
- data/lib/command-set/dsl.rb +14 -36
- data/lib/command-set/interpreter.rb +7 -1
- data/lib/command-set/results.rb +2 -2
- data/lib/command-set/text-interpreter.rb +48 -37
- metadata +3 -3
data/doc/argumentDSL
CHANGED
@@ -153,7 +153,7 @@ module Command
|
|
153
153
|
register "regex"
|
154
154
|
|
155
155
|
def initialize(name, regex)
|
156
|
-
|
156
|
+
super(name)
|
157
157
|
@regex = regex
|
158
158
|
end
|
159
159
|
|
@@ -203,26 +203,30 @@ module Command
|
|
203
203
|
end
|
204
204
|
end
|
205
205
|
|
206
|
+
def fs
|
207
|
+
File::Separator
|
208
|
+
end
|
209
|
+
|
206
210
|
def complete(prefix, subject)
|
207
211
|
list = []
|
208
212
|
search_path = @options[:dirs].dup
|
209
213
|
match = %r{^#{Regexp.escape(prefix)}.*}
|
210
214
|
infix = ""
|
211
215
|
|
212
|
-
if( not (m = %r{
|
213
|
-
infix =
|
214
|
-
search_path = [
|
215
|
-
match =
|
216
|
-
elsif( not (m = %r{
|
216
|
+
if( not (m = %r{^#{fs}([^#{fs}]*)$}.match(prefix)).nil? )
|
217
|
+
infix = fs
|
218
|
+
search_path = [fs]
|
219
|
+
match = %r{^#{m[1]}.*}
|
220
|
+
elsif( not (m = %r{^#{fs}(.*#{fs})([^#{fs}]*)$}.match(prefix)).nil? )
|
217
221
|
infix = "/#{m[1]}"
|
218
222
|
match = /^#{m[2]}.*/
|
219
223
|
search_path = [infix]
|
220
|
-
elsif( %r{
|
224
|
+
elsif( %r{#{fs}$} =~ prefix )
|
221
225
|
infix = prefix
|
222
226
|
search_path.map! {|path| File.join(path, prefix)}
|
223
227
|
match = /.*/
|
224
|
-
elsif( %r{
|
225
|
-
infix = File.dirname(prefix) + "
|
228
|
+
elsif( %r{#{fs}} =~ prefix )
|
229
|
+
infix = File.dirname(prefix) + "#{fs}"
|
226
230
|
search_path.map! {|path| File.join(path, infix)}
|
227
231
|
match = %r{^#{Regexp.escape(File.basename(prefix))}.*}
|
228
232
|
end
|
@@ -245,7 +249,7 @@ module Command
|
|
245
249
|
end
|
246
250
|
|
247
251
|
if(File.directory?(candidate))
|
248
|
-
path +=
|
252
|
+
path += fs
|
249
253
|
end
|
250
254
|
|
251
255
|
list << infix + path
|
@@ -256,7 +260,7 @@ module Command
|
|
256
260
|
end
|
257
261
|
end
|
258
262
|
|
259
|
-
if(list.length == 1 && list[0] =~
|
263
|
+
if(list.length == 1 && list[0] =~ %r{#{fs}$})
|
260
264
|
list << list[0] + "."
|
261
265
|
end
|
262
266
|
|
@@ -264,7 +268,7 @@ module Command
|
|
264
268
|
end
|
265
269
|
|
266
270
|
def validate(term, subject)
|
267
|
-
if(%r{
|
271
|
+
if(%r{^#{fs}} =~ term)
|
268
272
|
return @acceptor[term]
|
269
273
|
end
|
270
274
|
|
@@ -493,6 +497,53 @@ module Command
|
|
493
497
|
end
|
494
498
|
end
|
495
499
|
|
500
|
+
#Consumes several positions of the decorated argument.
|
501
|
+
# repeating.file_argument :some_files
|
502
|
+
#
|
503
|
+
#Will collect an array into +some_files+ of validated files.
|
504
|
+
class Repeating < ArgumentDecorator
|
505
|
+
register "repeating"
|
506
|
+
register "many"
|
507
|
+
|
508
|
+
decoration do
|
509
|
+
def consume_hash(subject, hash)
|
510
|
+
term = hash[@name]
|
511
|
+
return {} if term.nil?
|
512
|
+
|
513
|
+
term = [*term]
|
514
|
+
if term.find{|it| not validate(it, subject)}
|
515
|
+
raise ArgumentInvalidException, {@name => term}
|
516
|
+
else
|
517
|
+
return {@name => term.map{|it| parse(subject,it)}}
|
518
|
+
end
|
519
|
+
end
|
520
|
+
|
521
|
+
def consume(subject, arguments)
|
522
|
+
value = []
|
523
|
+
until arguments.empty? do
|
524
|
+
trying = arguments.shift
|
525
|
+
if(validate(trying, subject))
|
526
|
+
value << parse(subject,trying)
|
527
|
+
else
|
528
|
+
arguments.unshift(trying)
|
529
|
+
break
|
530
|
+
end
|
531
|
+
end
|
532
|
+
return {@name => value}
|
533
|
+
end
|
534
|
+
|
535
|
+
def match_terms(subject, terms, arguments)
|
536
|
+
validated = validate(terms.first, subject)
|
537
|
+
if(validated)
|
538
|
+
terms.shift
|
539
|
+
else
|
540
|
+
arguments.shift
|
541
|
+
end
|
542
|
+
return true
|
543
|
+
end
|
544
|
+
end
|
545
|
+
end
|
546
|
+
|
496
547
|
#Allows several arguments to share a position. Pass a block to the
|
497
548
|
#"decorator" method with the argument declarations inside. The first
|
498
549
|
#argument that can parse the input will be assigned - others will get nil.
|
@@ -97,7 +97,7 @@ Command::wrap_stdout
|
|
97
97
|
#:include: GUIDED_TOUR
|
98
98
|
module Command
|
99
99
|
class CommandError < ScriptError; end
|
100
|
-
class CommandException <
|
100
|
+
class CommandException < StandardError
|
101
101
|
def initialize(msg=nil)
|
102
102
|
super
|
103
103
|
@raw_input = nil
|
@@ -165,7 +165,7 @@ module Command
|
|
165
165
|
#creating the command set. You can even call
|
166
166
|
#DSL::CommandSetDefinition#define_commands on the set that's returned
|
167
167
|
#in order to add one-off commands or fold in other command sets.
|
168
|
-
def require_commands(mod, file, cmd_path=[])
|
168
|
+
def require_commands(mod, file=nil, cmd_path=[])
|
169
169
|
set = self.new
|
170
170
|
set.require_commands(mod, file, cmd_path)
|
171
171
|
return set
|
@@ -177,14 +177,6 @@ module Command
|
|
177
177
|
#:section: Workhorse methods - not usually used by client code
|
178
178
|
#
|
179
179
|
|
180
|
-
def paths_update(command, name)
|
181
|
-
command.add_path(self, [], name)
|
182
|
-
|
183
|
-
@paths.each do |set, path|
|
184
|
-
command.add_path(set, path, name)
|
185
|
-
end
|
186
|
-
end
|
187
|
-
|
188
180
|
def get_root
|
189
181
|
command = @command_list[nil]
|
190
182
|
end
|
@@ -283,14 +275,6 @@ module Command
|
|
283
275
|
|
284
276
|
alias short_docs documentation
|
285
277
|
|
286
|
-
def instance(subject)
|
287
|
-
if @command_list.has_key?(nil)
|
288
|
-
return @command_list[nil].new(subject)
|
289
|
-
else
|
290
|
-
raise CommandException, "incomplete command"
|
291
|
-
end
|
292
|
-
end
|
293
|
-
|
294
278
|
def command_list
|
295
279
|
return @command_list.dup
|
296
280
|
end
|
data/lib/command-set/command.rb
CHANGED
@@ -88,7 +88,7 @@ module Command
|
|
88
88
|
#An overworked exception class. It captures details about the command
|
89
89
|
#being interrupted as it propagates up the stack.
|
90
90
|
class ResumeFrom < ::Exception;
|
91
|
-
def initialize(pause_deck
|
91
|
+
def initialize(pause_deck, msg = "")
|
92
92
|
super(msg)
|
93
93
|
@setup = CommandSetup.new
|
94
94
|
@pause_deck = pause_deck
|
@@ -161,12 +161,9 @@ module Command
|
|
161
161
|
end
|
162
162
|
|
163
163
|
def inspect
|
164
|
-
arguments =
|
165
|
-
|
166
|
-
|
167
|
-
list.each do |argument|
|
168
|
-
arguments << argument.name
|
169
|
-
end
|
164
|
+
arguments = argument_list.map{|arg| arg.name}
|
165
|
+
argument_list.each do |argument|
|
166
|
+
arguments << argument.name
|
170
167
|
end
|
171
168
|
return "#<Class:#{self.object_id} - Command:#{path().join(" ")}(#{arguments.join(", ")})>"
|
172
169
|
end
|
@@ -272,6 +269,8 @@ module Command
|
|
272
269
|
extend DSL::Argument
|
273
270
|
include DSL::Action
|
274
271
|
|
272
|
+
class DontResume; end
|
273
|
+
|
275
274
|
def initialize(subject, resume=nil)
|
276
275
|
raise CommandException, "#{@name}: unrecognized command" unless self.class.defined?
|
277
276
|
fields = subject_requirements || []
|
@@ -281,7 +280,7 @@ module Command
|
|
281
280
|
@arg_hash = {}
|
282
281
|
@should_undo = true
|
283
282
|
@validation_problem = CommandException.new("No arguments provided!")
|
284
|
-
@last_completed_task =
|
283
|
+
@last_completed_task = DontResume
|
285
284
|
@resume_from = resume
|
286
285
|
@main_collector = collector
|
287
286
|
end
|
@@ -314,7 +313,7 @@ module Command
|
|
314
313
|
|
315
314
|
def inspect
|
316
315
|
name = self.class.name
|
317
|
-
return "#<Command:#{name}>:#{self.object_id} #{@arg_hash.inspect}"
|
316
|
+
return "#<Command:#{name}>:#{self.object_id} #{@arg_hash.keys.inspect}"
|
318
317
|
end
|
319
318
|
|
320
319
|
def parent
|
@@ -322,6 +321,7 @@ module Command
|
|
322
321
|
end
|
323
322
|
|
324
323
|
def go(collector)
|
324
|
+
return if @resume_from == DontResume
|
325
325
|
unless self.respond_to? :execute
|
326
326
|
raise CommandException, "#{@name}: command declared but no action defined"
|
327
327
|
end
|
@@ -336,6 +336,8 @@ module Command
|
|
336
336
|
puts "Command cancelled"
|
337
337
|
rescue ResumeFrom => rf
|
338
338
|
rf.setup.task_id = @last_completed_task
|
339
|
+
rf.setup.command = self.class
|
340
|
+
rf.setup.args_hash = arg_hash.dup
|
339
341
|
raise rf
|
340
342
|
end
|
341
343
|
end
|
data/lib/command-set/dsl.rb
CHANGED
@@ -47,14 +47,23 @@ Action:: utility functions within the command action block
|
|
47
47
|
#from it.
|
48
48
|
def require_commands(module_name, file = nil, path = [], *commands)
|
49
49
|
require file rescue nil
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
50
|
+
|
51
|
+
if Module === module_name
|
52
|
+
mod = module_name
|
53
|
+
else
|
54
|
+
module_path = module_name.to_s.split("::")
|
55
|
+
mod = Object
|
56
|
+
module_path.each do |part|
|
57
|
+
mod = mod.const_get(part)
|
58
|
+
end
|
54
59
|
end
|
55
60
|
|
56
61
|
set = mod.define_commands
|
57
|
-
|
62
|
+
unless CommandSet === set
|
63
|
+
raise RuntimeError,"#{set.inspect} isn't a CommandSet"
|
64
|
+
end
|
65
|
+
|
66
|
+
set = set.find_command(*path).first
|
58
67
|
|
59
68
|
if CommandSet === set
|
60
69
|
include_commands(set, *commands)
|
@@ -520,37 +529,6 @@ Action:: utility functions within the command action block
|
|
520
529
|
def interruptable
|
521
530
|
yield
|
522
531
|
end
|
523
|
-
|
524
|
-
#These methods are nodoc'd because at present, they don't work.
|
525
|
-
#They'd be awesome for big jobs - splitting them into subthreads and
|
526
|
-
#such. But they need to be debugged, and IIRC there's a deadlock
|
527
|
-
#condition
|
528
|
-
def action_thread(&block) #:nodoc:
|
529
|
-
return Thread.new do
|
530
|
-
collector = sub_collector
|
531
|
-
$stdout.set_thread_collector(collector)
|
532
|
-
block.call
|
533
|
-
$stdout.remove_thread_collector(collector)
|
534
|
-
end
|
535
|
-
end
|
536
|
-
|
537
|
-
require 'thwait'
|
538
|
-
def farm_out(array, thread_count, &block) #:nodoc:
|
539
|
-
first_batch = (array[0...thread_count]||[]).map do |item|
|
540
|
-
action_thread { block.call(item) }
|
541
|
-
end
|
542
|
-
|
543
|
-
rest = (array[thread_count..-1] || [])
|
544
|
-
|
545
|
-
waiter = ThreadsWait.new(*first_batch)
|
546
|
-
|
547
|
-
rest.each do |item|
|
548
|
-
waiter.next_wait
|
549
|
-
waiter.join_nowait(action_thread{block.call(item)})
|
550
|
-
end
|
551
|
-
|
552
|
-
waiter.join
|
553
|
-
end
|
554
532
|
end
|
555
533
|
end
|
556
534
|
end
|
@@ -11,7 +11,7 @@ module Command
|
|
11
11
|
#instance, must implement #cook_input(raw) that converts raw input (of
|
12
12
|
#whatever form is appropriate to the interpreter) into CommandSetup
|
13
13
|
#objects. Other methods can be overridden - especially consider
|
14
|
-
# #get_formatter,
|
14
|
+
# #get_formatter, and #prompt_user
|
15
15
|
class BaseInterpreter
|
16
16
|
attr_accessor :command_set, :out_io, :logger
|
17
17
|
attr_reader :subject
|
@@ -41,6 +41,12 @@ module Command
|
|
41
41
|
@subject = subject
|
42
42
|
end
|
43
43
|
|
44
|
+
def fill_subject
|
45
|
+
template = subject_template
|
46
|
+
yield template
|
47
|
+
self.subject=(template)
|
48
|
+
end
|
49
|
+
|
44
50
|
#Any options that the interpreter might have can be set by passing a
|
45
51
|
#hash to behavior to be merged with the defaults
|
46
52
|
def behavior(hash)
|
data/lib/command-set/results.rb
CHANGED
@@ -622,8 +622,8 @@ module Command
|
|
622
622
|
@strategy_stack = [@strategies[:default]]
|
623
623
|
end
|
624
624
|
|
625
|
-
def_delegators :current_strategy, :saw_begin_list, :saw_item,
|
626
|
-
:closed_begin_list, :closed_item, :closed_end_list
|
625
|
+
def_delegators :current_strategy, :saw_begin_list, :saw_item,
|
626
|
+
:saw_end_list, :closed_begin_list, :closed_item, :closed_end_list
|
627
627
|
|
628
628
|
def current_strategy
|
629
629
|
@strategy_stack.last
|
@@ -71,46 +71,43 @@ module Command
|
|
71
71
|
raise "command_set or subject unset!"
|
72
72
|
end
|
73
73
|
|
74
|
-
Readline.completion_proc = lambda do |prefix|
|
75
|
-
parsed_input = Readline.line_buffer.split(" ")
|
76
|
-
unless prefix.empty?
|
77
|
-
parsed_input.pop
|
78
|
-
end
|
79
|
-
|
80
|
-
list = current_command_set.completion_list(parsed_input,
|
81
|
-
prefix, build_subject)
|
82
|
-
list
|
83
|
-
end
|
84
|
-
|
85
74
|
@stop = false
|
86
75
|
|
87
76
|
begin
|
88
|
-
|
77
|
+
old_proc = set_readline_completion do |prefix|
|
78
|
+
readline_complete(Readline.line_buffer, prefix)
|
79
|
+
end
|
80
|
+
|
81
|
+
line = readline(get_prompt, true)
|
89
82
|
next if line.empty?
|
90
83
|
process_line(line)
|
91
84
|
rescue Interrupt
|
92
|
-
puts "Interrupt: please use \"quit\""
|
85
|
+
@out_io.puts "Interrupt: please use \"quit\""
|
93
86
|
rescue CommandException => ce
|
94
|
-
|
95
|
-
@out_io.puts ce.backtrace if @behavior[:debug_commands]
|
96
|
-
logger.warn ce.message
|
97
|
-
ce.backtrace.each do |line|
|
98
|
-
logger.debug line
|
99
|
-
end
|
100
|
-
|
87
|
+
output_exception("Error", ce)
|
101
88
|
rescue Exception => e
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
logger.debug line
|
107
|
-
end
|
108
|
-
puts "Waiting for return"
|
109
|
-
$stdin.gets
|
110
|
-
@stop = true
|
89
|
+
output_exception("Exception", e)
|
90
|
+
pause_before_dying
|
91
|
+
ensure
|
92
|
+
set_readline_completion(&old_proc)
|
111
93
|
end until @stop
|
112
94
|
end
|
113
95
|
|
96
|
+
def output_exception(label, ex)
|
97
|
+
@out_io.puts label + ": " + ex.message
|
98
|
+
@out_io.puts ex.backtrace.join("\n") if @behavior[:debug_commands]
|
99
|
+
logger.warn ex.message
|
100
|
+
ex.backtrace.each do |line|
|
101
|
+
logger.debug line
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def pause_before_dying
|
106
|
+
puts "Waiting for return"
|
107
|
+
$stdin.gets
|
108
|
+
stop
|
109
|
+
end
|
110
|
+
|
114
111
|
def cook_input(line)
|
115
112
|
line = split_line(line)
|
116
113
|
if(@complete_line)
|
@@ -124,13 +121,6 @@ module Command
|
|
124
121
|
return CommandSetup.new(line)
|
125
122
|
end
|
126
123
|
|
127
|
-
def assign_terms(cmd)
|
128
|
-
terms = super
|
129
|
-
unless terms.empty?
|
130
|
-
puts "(Discarding extraneous: #{terms.join(" ")}"
|
131
|
-
end
|
132
|
-
end
|
133
|
-
|
134
124
|
alias single_command process_input
|
135
125
|
alias process_line process_input
|
136
126
|
|
@@ -150,6 +140,16 @@ module Command
|
|
150
140
|
end
|
151
141
|
end
|
152
142
|
|
143
|
+
def readline_complete(buffer, prefix)
|
144
|
+
parsed_input = split_line(buffer)
|
145
|
+
unless prefix.empty?
|
146
|
+
parsed_input.pop
|
147
|
+
end
|
148
|
+
|
149
|
+
return current_command_set.completion_list(parsed_input,
|
150
|
+
prefix, build_subject)
|
151
|
+
end
|
152
|
+
|
153
153
|
def split_line(line)
|
154
154
|
line_array = []
|
155
155
|
scanner = StringScanner.new(line)
|
@@ -198,7 +198,18 @@ module Command
|
|
198
198
|
end
|
199
199
|
|
200
200
|
def prompt_user(message)
|
201
|
-
|
201
|
+
readline(message)
|
202
|
+
end
|
203
|
+
|
204
|
+
protected
|
205
|
+
def set_readline_completion(&block)
|
206
|
+
old_proc = Readline.completion_proc
|
207
|
+
Readline.completion_proc = block
|
208
|
+
return old_proc
|
209
|
+
end
|
210
|
+
|
211
|
+
def readline(prompt, something=false)
|
212
|
+
return Readline.readline(prompt, something)
|
202
213
|
end
|
203
214
|
end
|
204
215
|
end
|
metadata
CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.4
|
|
3
3
|
specification_version: 1
|
4
4
|
name: command-set
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.8.
|
7
|
-
date: 2007-12-
|
6
|
+
version: 0.8.3
|
7
|
+
date: 2007-12-19 00:00:00 -08:00
|
8
8
|
summary: Framework for interactive programs focused around a DSL for commands
|
9
9
|
require_paths:
|
10
10
|
- lib
|
@@ -55,7 +55,7 @@ rdoc_options:
|
|
55
55
|
- --main
|
56
56
|
- Command
|
57
57
|
- --title
|
58
|
-
- command-set-0.8.
|
58
|
+
- command-set-0.8.3 RDoc
|
59
59
|
extra_rdoc_files:
|
60
60
|
- doc/argumentDSL
|
61
61
|
executables: []
|