command-set 0.10.1 → 0.10.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -626,12 +626,38 @@ Action:: utility functions within the command action block
626
626
  raise CommandException, "#{@name} cannot be undone"
627
627
  end
628
628
 
629
- #This method is deprecated but remains as a nicety. As it stands, any
630
- #command can be interrupted at the command line with Ctrl-C, and
631
- #return to the prompt.
632
- def interruptable
633
- yield
629
+ #For big jobs - splitting them into subthreads and
630
+ #such. But they need to be debugged, and IIRC there's a deadlock
631
+ #condition
632
+ def action_thread(&block)
633
+ collector = sub_collector
634
+ return Thread.new do
635
+ $stdout.set_thread_collector(collector)
636
+ block.call
637
+ $stdout.remove_thread_collector(collector)
638
+ end
634
639
  end
640
+
641
+ def fan_out(threads_at_a_time, array, &block)
642
+ require 'thwait'
643
+
644
+ array = array.to_a
645
+ first_batch = (array[0...threads_at_a_time]||[]).map do |item|
646
+ action_thread { block.call(item) }
647
+ end
648
+
649
+ rest = (array[threads_at_a_time..-1] || [])
650
+
651
+ waiter = ThreadsWait.new(*first_batch)
652
+
653
+ rest.each do |item|
654
+ waiter.next_wait
655
+ waiter.join_nowait(action_thread{block.call(item)})
656
+ end
657
+
658
+ waiter.join
659
+ end
660
+
635
661
  end
636
662
  end
637
663
  end
@@ -109,9 +109,7 @@ module Command::Results
109
109
  end
110
110
  end
111
111
 
112
- def initialize(out = nil, err = nil)
113
- @out_to = out || ::Command::raw_stdout
114
- @err_to = err || ::Command::raw_stderr
112
+ def initialize()
115
113
  @advisor = FormatAdvisor.new(self)
116
114
  @advice = {:list => [], :item => [], :output => []}
117
115
  end
@@ -141,15 +139,6 @@ module Command::Results
141
139
  {}
142
140
  end
143
141
 
144
- def_delegators :@out_to, :p, :puts, :print, :printf, :putc, :write, :write_nonblock, :flush
145
-
146
- def self.inherited(sub)
147
- sub.extend Forwardable
148
- sub.class_eval do
149
- def_delegators :@out_to, :p, :puts, :print, :printf, :putc, :write, :write_nonblock, :flush
150
- end
151
- end
152
-
153
142
  #Presenter callback: output is beginning
154
143
  def start; end
155
144
 
@@ -175,10 +164,26 @@ module Command::Results
175
164
  def finish; end
176
165
  end
177
166
 
178
- #The simplest useful Formatter: it outputs the value of every item in tree order. Think of
179
- #it as what would happen if you just let puts and p go directly to the screen, without the
180
- #annoying consequences of threading, etc.
167
+ #The simplest useful Formatter: it outputs the value of every item in tree
168
+ #order. Think of it as what would happen if you just let puts and p go
169
+ #directly to the screen, without the annoying consequences of threading,
170
+ #etc.
181
171
  class TextFormatter < Formatter
172
+ def initialize(out = nil, err = nil)
173
+ @out_to = out || ::Command::raw_stdout
174
+ @err_to = err || ::Command::raw_stderr
175
+ super()
176
+ end
177
+
178
+ def_delegators :@out_to, :p, :puts, :print, :printf, :putc, :write, :write_nonblock, :flush
179
+
180
+ def self.inherited(sub)
181
+ sub.extend Forwardable
182
+ sub.class_eval do
183
+ def_delegators :@out_to, :p, :puts, :print, :printf, :putc, :write, :write_nonblock, :flush
184
+ end
185
+ end
186
+
182
187
  def closed_item(value)
183
188
  puts value
184
189
  end
@@ -0,0 +1,41 @@
1
+ require 'command-set/results'
2
+
3
+ module Command::Results
4
+ class HashArrayFormatter < Formatter
5
+ def initialize
6
+ @hash_stack = [{:array => []}]
7
+ super
8
+ end
9
+
10
+ def hash
11
+ @hash_stack.last
12
+ end
13
+
14
+ def array
15
+ hash[:array]
16
+ end
17
+
18
+ def closed_begin_list(list)
19
+ list_array = []
20
+ list_hash = {:array => list_array}
21
+ array.push(list_array)
22
+ hash[array().length.to_s] = list_hash
23
+ hash[list.name] = list_hash
24
+ @hash_stack.push(list_hash)
25
+ end
26
+
27
+ def closed_item(item)
28
+ thing = item.value
29
+ array().push(thing)
30
+ hash()[array().length.to_s] = thing
31
+ end
32
+
33
+ def closed_end_list(list)
34
+ @hash_stack.pop
35
+ end
36
+
37
+ def structure
38
+ @hash_stack.first
39
+ end
40
+ end
41
+ end
@@ -1,7 +1,7 @@
1
1
  require 'command-set/formatter/base'
2
2
 
3
3
  module Command::Results
4
- class StrategyFormatter < Formatter
4
+ class StrategyFormatter < TextFormatter
5
5
  class FormatStrategy
6
6
  extend Forwardable
7
7
  include Formatter::Styler
@@ -4,7 +4,7 @@ module Command::Results
4
4
  #A trivial and obvious Formatter: produces well-formed XML fragments based on events. It even
5
5
  #indents. Might be handy for more complicated output processing, since you could feed the document
6
6
  #to a XSLT processor.
7
- class XMLFormatter < Formatter
7
+ class XMLFormatter < TextFormatter
8
8
  def initialize(out = nil, err = nil, indent=" ", newline="\n")
9
9
  super(out, err)
10
10
  @indent = indent
@@ -54,7 +54,7 @@ module Command
54
54
  end
55
55
 
56
56
  def initialize
57
- @formatter_factory = proc {Results::Formatter.new(::Command::raw_stdout)}
57
+ @formatter_factory = proc {Results::TextFormatter.new(::Command::raw_stdout)}
58
58
  super
59
59
  end
60
60
 
@@ -1,66 +1,7 @@
1
- require 'readline'
2
1
  require 'command-set'
3
- require 'dl/import'
4
2
  require 'command-set/interpreter/base'
5
- require 'command-set/formatter/strategy'
6
-
7
- #This really locks text-interpreter down to Linux, maybe Unix-like
8
- #platforms, I'm thinking. A more flexible way of doing this would rock,
9
- #regardless of length.
10
- module Readline
11
- begin
12
- extend DL::Importable
13
- found_libreadline = false
14
- ls_so_dirs = [
15
- %w{lib},
16
- %w{usr lib},
17
- %w{usr local lib}
18
- ].each{|path| path.unshift("")}
19
-
20
- begin
21
- File::open("/etc/ld.so.conf", "r") do |ld_so_conf|
22
- ld_so_conf.each do |line|
23
- unless /^#/ =~ line or /^%s*$/ =~ line
24
- ls_so_dirs << line.chomp.split(File::Separator)
25
- end
26
- end
27
- end
28
- rescue Exception
29
- end
30
-
31
- libreadline_names = %w{libreadline.so libreadline.dylib libreadline.dll}
32
-
33
- libreadline_paths = ls_so_dirs.inject([]) do |list, dir|
34
- list + libreadline_names.map do |name|
35
- File::join(*(dir + [name]))
36
- end
37
- end
38
-
39
- libreadline_paths.each do |path|
40
- begin
41
- dlload path
42
- RLLB = symbol("rl_line_buffer")
43
- found_libreadline = true
44
- break
45
- rescue RuntimeError
46
- end
47
- end
48
-
49
- raise RuntimeError,"couldn't find libreadline.so" unless found_libreadline
50
-
51
- def self.line_buffer
52
- p = RLLB.ptr
53
- if p.nil?
54
- return p
55
- else
56
- return p.to_s
57
- end
58
- end
59
- rescue RuntimeError => rte
60
- warn "Couldn't find libreadline - tab-completion will be unpredictable at best."
61
- warn "The problem was: " + rte.message
62
- end
63
- end
3
+ require 'command-set/formatter/base'
4
+ require 'command-set/readline'
64
5
 
65
6
  module Command
66
7
  class TextInterpreter < BaseInterpreter
@@ -0,0 +1,60 @@
1
+ require 'readline'
2
+ require 'dl/import'
3
+
4
+ #This really locks text-interpreter down to Linux, maybe Unix-like
5
+ #platforms, I'm thinking. A more flexible way of doing this would rock,
6
+ #regardless of length.
7
+ module Readline
8
+ begin
9
+ extend DL::Importable
10
+ found_libreadline = false
11
+ ls_so_dirs = [
12
+ %w{lib},
13
+ %w{usr lib},
14
+ %w{usr local lib}
15
+ ].each{|path| path.unshift("")}
16
+
17
+ begin
18
+ File::open("/etc/ld.so.conf", "r") do |ld_so_conf|
19
+ ld_so_conf.each do |line|
20
+ unless /^#/ =~ line or /^%s*$/ =~ line
21
+ ls_so_dirs << line.chomp.split(File::Separator)
22
+ end
23
+ end
24
+ end
25
+ rescue Exception
26
+ end
27
+
28
+ libreadline_names = %w{libreadline.so libreadline.dylib libreadline.dll}
29
+
30
+ libreadline_paths = ls_so_dirs.inject([]) do |list, dir|
31
+ list + libreadline_names.map do |name|
32
+ File::join(*(dir + [name]))
33
+ end
34
+ end
35
+
36
+ libreadline_paths.each do |path|
37
+ begin
38
+ dlload path
39
+ RLLB = symbol("rl_line_buffer")
40
+ found_libreadline = true
41
+ break
42
+ rescue RuntimeError
43
+ end
44
+ end
45
+
46
+ raise RuntimeError,"couldn't find libreadline" unless found_libreadline
47
+
48
+ def self.line_buffer
49
+ p = RLLB.ptr
50
+ if p.nil?
51
+ return p
52
+ else
53
+ return p.to_s
54
+ end
55
+ end
56
+ rescue RuntimeError => rte
57
+ warn "Couldn't find libreadline - tab-completion will be unpredictable at best."
58
+ warn "The problem was: " + rte.message
59
+ end
60
+ end
@@ -13,7 +13,7 @@ module Command
13
13
  @order = nil
14
14
  @parent = nil
15
15
  @options = {}
16
- @depth = nil
16
+ @depth = 0
17
17
  end
18
18
 
19
19
  attr_reader :value, :sequence
@@ -1,5 +1,6 @@
1
1
  require 'command-set/result-list'
2
2
  require 'Win32/Console/ANSI' if PLATFORM =~ /win32/
3
+ require 'thread'
3
4
 
4
5
  module Kernel
5
6
  def puts(*args)
@@ -172,9 +173,9 @@ module Command
172
173
  #understand that other Collectors could be running at the same time - that's the
173
174
  #Presenter's job.
174
175
  class Collector
175
- def initialize(presenter)
176
+ def initialize(presenter, list_root)
176
177
  @presenter = presenter
177
- @nesting = []
178
+ @nesting = [list_root]
178
179
  end
179
180
 
180
181
  def initialize_copy(original)
@@ -194,23 +195,11 @@ module Command
194
195
  end
195
196
 
196
197
  def item( obj, options={} )
197
- @presenter.item(@nesting.dup + [obj], options)
198
+ @presenter.item(@nesting.last, obj, options)
198
199
  end
199
200
 
200
201
  def begin_list( name, options={} )
201
- @nesting.push(name)
202
- @presenter.begin_list(@nesting.dup, options)
203
- if block_given?
204
- yield
205
- end_list
206
- end
207
- end
208
-
209
- def open_list(name, options={})
210
- @nesting.push(name)
211
- unless @presenter.list_open?(@nesting)
212
- @presenter.begin_list(@nesting.dup, options)
213
- end
202
+ @nesting << @presenter.begin_list(@nesting.last, name, options)
214
203
  if block_given?
215
204
  yield
216
205
  end_list
@@ -218,8 +207,7 @@ module Command
218
207
  end
219
208
 
220
209
  def end_list
221
- @presenter.end_list(@nesting.dup)
222
- @nesting.pop
210
+ @presenter.end_list(@nesting.pop)
223
211
  end
224
212
 
225
213
  @dispatches = {}
@@ -236,8 +224,8 @@ module Command
236
224
  self.class.dispatches
237
225
  end
238
226
 
239
- #Use to register an IO +method+ to handle. The block will be passed a Collector and the
240
- #arguments passed to +method+.
227
+ #Use to register an IO +method+ to handle. The block will be passed a
228
+ #Collector and the arguments passed to +method+.
241
229
  def self.dispatch(method, &block)
242
230
  @dispatches[method] = true
243
231
  define_method(method, &block)
@@ -263,7 +251,7 @@ module Command
263
251
  end
264
252
 
265
253
  #Gets item and list events from Collectors, and emits two kinds of
266
- #events to Formatters:
254
+ #events to Formatters:
267
255
  #[+saw+ events] occur in chronological order, with no guarantee regarding timing.
268
256
  #[+closed+ events] occur in tree order.
269
257
  #
@@ -272,14 +260,15 @@ module Command
272
260
  #soon as the relevant output element enters the system.
273
261
  #
274
262
  #On the other hand, +closed+ events will be generated in the natural
275
- #order you'd expect the output to appear in. Most Formatter subclasses use
276
- #+closed+ events.
263
+ #order you'd expect the output to appear in. Most Formatter subclasses
264
+ #use +closed+ events.
277
265
  #
278
266
  #A list which has not received a "list_end" event from upstream will
279
267
  #block lists later in tree order until it closes. A Formatter that
280
268
  #listens only to +closed+ events can present them to the user in a way
281
269
  #that should be reasonable, although output might be halting for any
282
270
  #process that takes noticeable time.
271
+ #
283
272
  class Presenter
284
273
  class Exception < ::Exception; end
285
274
 
@@ -287,57 +276,48 @@ module Command
287
276
  @results = List.new("")
288
277
  @leading_edge = @results
289
278
  @formatters = []
279
+ @list_lock = Mutex.new
290
280
  end
291
281
 
292
282
  def create_collector
293
- return Collector.new(self)
283
+ return Collector.new(self, @results)
294
284
  end
295
285
 
296
- def item( item_path, options={} )
297
- item = item_path.pop
298
- home = get_collection(item_path)
299
- item = home.add item
300
- item.options = home.options.merge(options)
301
- item.depth = item_path.length
302
- notify(:saw, item)
303
- advance_leading_edge
286
+ def register_formatter(formatter)
287
+ @formatters << formatter
288
+ formatter.notify(:start, nil)
304
289
  end
305
290
 
306
291
  def leading_edge?(list)
307
292
  return list == @leading_edge
308
293
  end
309
294
 
310
- def register_formatter(formatter)
311
- @formatters << formatter
295
+ def item( home, value, options={} )
296
+ item = ListItem.new(value)
312
297
 
313
- formatter.notify(:start, nil)
298
+ add_item(home, item, options)
299
+
300
+ notify(:saw, item)
301
+ return nil
314
302
  end
315
303
 
316
- def begin_list( list_path, options={} )
317
- list = list_path.pop
318
- home = get_collection(list_path)
319
- list = List.new(list)
320
- list.options = home.options.merge(options)
321
- list.depth = list_path.length
304
+ def begin_list( home, name, options={} )
305
+ list = List.new(name)
306
+
307
+ add_item(home, list, options)
308
+
322
309
  notify(:saw_begin, list)
323
- home.add(list)
324
- advance_leading_edge
310
+ return list
325
311
  end
326
312
 
327
- def list_open?(list_path)
328
- begin
329
- get_collection(list_path)
330
- return true
331
- rescue Exception
332
- return false
313
+ def end_list( list )
314
+ @list_lock.synchronize do
315
+ list.close
316
+ advance_leading_edge
333
317
  end
334
- end
335
318
 
336
- def end_list( list_path )
337
- list = get_collection( list_path )
338
319
  notify(:saw_end, list)
339
- list.close
340
- advance_leading_edge
320
+ return nil
341
321
  end
342
322
 
343
323
  def done
@@ -346,8 +326,9 @@ module Command
346
326
  notify(:done, nil)
347
327
  end
348
328
 
349
- #Returns the current list of results. A particularly advanced Formatter might treat +saw_*+ events
350
- #like notifications, and then use the List#filter functionality to discover the specifics about the
329
+ #Returns the current list of results. A particularly advanced
330
+ #Formatter might treat +saw_*+ events like notifications, and then use
331
+ #the List#filter functionality to discover the specifics about the
351
332
  #item or list just closed.
352
333
  def output
353
334
  @results
@@ -372,23 +353,21 @@ module Command
372
353
  end
373
354
  end
374
355
 
375
- def notify(msg, item)
376
- @formatters.each do |f|
377
- f.notify(msg, item)
356
+ def add_item(home, item, options)
357
+ item.depth = home.depth + 1
358
+
359
+ @list_lock.synchronize do
360
+ #home = get_collection(path)
361
+ item.options = home.options.merge(options)
362
+ home.add(item)
363
+ advance_leading_edge
378
364
  end
379
365
  end
380
366
 
381
- def get_collection(path)
382
- thumb = @results.values
383
- list = @results
384
- path.each do |step|
385
- list = thumb.find{|member| List === member && member.open? && member.name == step}
386
- if list.nil?
387
- raise Exception, "#{step} in #{path.inspect} missing from #{thumb}!"
388
- end
389
- thumb = list.values
367
+ def notify(msg, item)
368
+ @formatters.each do |f|
369
+ f.notify(msg, item)
390
370
  end
391
- return list
392
371
  end
393
372
  end
394
373
  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.10.1
7
- date: 2008-02-12 00:00:00 -08:00
6
+ version: 0.10.2
7
+ date: 2008-02-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
@@ -45,11 +45,13 @@ files:
45
45
  - lib/command-set/dsl.rb
46
46
  - lib/command-set/formatter
47
47
  - lib/command-set/formatter/strategy.rb
48
+ - lib/command-set/formatter/hash-array.rb
48
49
  - lib/command-set/formatter/base.rb
49
50
  - lib/command-set/formatter/xml.rb
50
51
  - lib/command-set/command.rb
51
52
  - lib/command-set/standard-commands.rb
52
53
  - lib/command-set/results.rb
54
+ - lib/command-set/readline.rb
53
55
  - doc/README
54
56
  - doc/GUIDED_TOUR
55
57
  - doc/Specifications
@@ -61,7 +63,7 @@ rdoc_options:
61
63
  - --main
62
64
  - Command
63
65
  - --title
64
- - command-set-0.10.1 RDoc
66
+ - command-set-0.10.2 RDoc
65
67
  extra_rdoc_files:
66
68
  - doc/README
67
69
  - doc/GUIDED_TOUR