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 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: []