command-set 0.8.2 → 0.8.3
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|