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 CHANGED
@@ -14,6 +14,8 @@ For instance
14
14
 
15
15
  Decorator methods, and the classes they add:
16
16
 
17
+ +repeating+:: Repeating
18
+ +many+:: Repeating
17
19
  +multi+:: AlternatingArgument
18
20
  +named+:: Named
19
21
  +optional+:: Optional
@@ -153,7 +153,7 @@ module Command
153
153
  register "regex"
154
154
 
155
155
  def initialize(name, regex)
156
- @name = name
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{^/([^/]*)$}.match(prefix)).nil? )
213
- infix = "/"
214
- search_path = ["/"]
215
- match = /^#{m[1]}.*/
216
- elsif( not (m = %r{^/(.*/)([^/]*)$}.match(prefix)).nil? )
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{/$} =~ prefix )
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{/} =~ prefix )
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{^/} =~ term)
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 < RuntimeError
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
@@ -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 = nil, msg = "")
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
- [required_argument_list, optional_argument_list].each do |list|
166
- next if list.nil?
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 = nil
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
@@ -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
- module_path = module_name.to_s.split("::")
51
- mod = Object
52
- module_path.each do |part|
53
- mod = mod.const_get(part)
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
- set = set.find_command(*path)
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, #prompt_user, and #assign_terms
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)
@@ -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, :saw_end_list,
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
- line = Readline.readline(get_prompt, true)
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
- @out_io.puts "Error: " + ce.message
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
- @out_io.puts "Exception: " + e.message
103
- @out_io.puts e.backtrace.join("\n") if @behavior[:debug_commands]
104
- logger.warn e.message
105
- e.backtrace.each do |line|
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
- Readline.readline(message)
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.2
7
- date: 2007-12-13 00:00:00 -08:00
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.2 RDoc
58
+ - command-set-0.8.3 RDoc
59
59
  extra_rdoc_files:
60
60
  - doc/argumentDSL
61
61
  executables: []