command-set 0.10.1 → 0.10.2

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.
@@ -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