command-set 0.9.2 → 0.10.0
Sign up to get free protection for your applications and to get access to all the features.
- data/doc/Specifications +64 -17
- data/lib/command-set.rb +1 -1
- data/lib/command-set/arguments.rb +40 -36
- data/lib/command-set/command-set.rb +68 -31
- data/lib/command-set/command.rb +123 -53
- data/lib/command-set/dsl.rb +54 -45
- data/lib/command-set/formatter/base.rb +186 -0
- data/lib/command-set/formatter/strategy.rb +243 -0
- data/lib/command-set/formatter/xml.rb +56 -0
- data/lib/command-set/{interpreter.rb → interpreter/base.rb} +43 -36
- data/lib/command-set/{batch-interpreter.rb → interpreter/batch.rb} +0 -0
- data/lib/command-set/{quick-interpreter.rb → interpreter/quick.rb} +4 -4
- data/lib/command-set/{text-interpreter.rb → interpreter/text.rb} +29 -25
- data/lib/command-set/results.rb +4 -478
- data/lib/command-set/standard-commands.rb +2 -2
- data/lib/command-set/structural.rb +230 -0
- data/lib/command-set/subject.rb +153 -39
- metadata +13 -8
- data/lib/command-set/command-common.rb +0 -110
@@ -13,24 +13,34 @@ module Command
|
|
13
13
|
#objects. Other methods can be overridden - especially consider
|
14
14
|
# #get_formatter, and #prompt_user
|
15
15
|
class BaseInterpreter
|
16
|
-
attr_accessor :
|
17
|
-
attr_reader :subject
|
16
|
+
attr_accessor :out_io, :logger
|
17
|
+
attr_reader :subject, :command_set
|
18
18
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
19
|
+
def initialize
|
20
|
+
@command_set=nil
|
21
|
+
@sub_modes = []
|
22
|
+
@behavior = {
|
23
|
+
:screen_width => 76,
|
24
|
+
:warn_no_undo => true
|
25
|
+
}
|
26
|
+
@out_io = $stdout
|
27
|
+
@stop = false
|
28
|
+
@subject = nil
|
29
|
+
@logger = Logger.new($stderr)
|
30
|
+
@logger.level=Logger::FATAL
|
31
|
+
@undo_stack = UndoStack.new
|
32
|
+
@commands_pending = []
|
33
|
+
@pause_decks = Hash.new {|h,k| h[k]=[]}
|
28
34
|
end
|
29
35
|
|
36
|
+
#:section: Client app methods
|
37
|
+
alias subject_template subject
|
38
|
+
|
30
39
|
#Before running an interpreter on input, you must set the subject.
|
31
40
|
#Get a subject object by calling subject_template, assign it's fields,
|
32
41
|
#and then pass it into subject=
|
33
42
|
def subject= (subject)
|
43
|
+
subject
|
34
44
|
begin
|
35
45
|
subject.get_image(subject_requirements())
|
36
46
|
rescue CommandException
|
@@ -41,8 +51,14 @@ module Command
|
|
41
51
|
@subject = subject
|
42
52
|
end
|
43
53
|
|
54
|
+
|
55
|
+
def command_set=(set)
|
56
|
+
@command_set = set
|
57
|
+
@subject = prep_subject(get_subject)
|
58
|
+
end
|
59
|
+
|
44
60
|
def fill_subject
|
45
|
-
template =
|
61
|
+
template = self.subject
|
46
62
|
yield template
|
47
63
|
self.subject=(template)
|
48
64
|
end
|
@@ -53,33 +69,17 @@ module Command
|
|
53
69
|
@behavior.merge!(hash)
|
54
70
|
end
|
55
71
|
|
56
|
-
def initialize
|
57
|
-
@command_set=nil
|
58
|
-
@sub_modes = []
|
59
|
-
@behavior = {
|
60
|
-
:screen_width => 76,
|
61
|
-
:warn_no_undo => true
|
62
|
-
}
|
63
|
-
@out_io = $stdout
|
64
|
-
@stop = false
|
65
|
-
@subject = nil
|
66
|
-
@logger = Logger.new($stderr)
|
67
|
-
@logger.level=Logger::FATAL
|
68
|
-
@undo_stack = UndoStack.new
|
69
|
-
@commands_pending = []
|
70
|
-
@pause_decks = Hash.new {|h,k| h[k]=[]}
|
71
|
-
end
|
72
|
-
|
73
72
|
#:section: Command behavior related method
|
74
73
|
|
75
74
|
# Puts a CommandSet ahead of the current one for processing. Useful for command
|
76
75
|
# modes, like Cisco's IOS with configure modes, et al.
|
77
|
-
def push_mode(
|
76
|
+
def push_mode(nesting)
|
77
|
+
mode = nesting.pop
|
78
78
|
unless CommandSet === mode
|
79
79
|
raise RuntimeError, "Sub-modes must be CommandSets!"
|
80
80
|
end
|
81
81
|
|
82
|
-
@sub_modes.push([mode, mode.most_recent_args])
|
82
|
+
@sub_modes.push([mode, mode.most_recent_args, nesting])
|
83
83
|
return nil
|
84
84
|
end
|
85
85
|
|
@@ -106,7 +106,8 @@ module Command
|
|
106
106
|
#interpreter.
|
107
107
|
def prep_subject(subject)
|
108
108
|
@command_set.add_requirements(subject)
|
109
|
-
|
109
|
+
@command_set.add_defaults(subject)
|
110
|
+
subject.required_fields(subject_requirements())
|
110
111
|
subject.undo_stack = @undo_stack
|
111
112
|
subject.interpreter_behavior = @behavior
|
112
113
|
subject.chain_of_command = @commands_pending
|
@@ -118,7 +119,8 @@ module Command
|
|
118
119
|
#You'll almost never want to override this method.
|
119
120
|
def next_command
|
120
121
|
setup = @commands_pending.shift
|
121
|
-
setup.
|
122
|
+
setup.arg_hash = default_arg_hash.merge(setup.arg_hash)
|
123
|
+
setup.set_nesting = current_nesting + setup.set_nesting
|
122
124
|
return setup.command_instance(current_command_set, build_subject)
|
123
125
|
end
|
124
126
|
|
@@ -193,17 +195,22 @@ module Command
|
|
193
195
|
|
194
196
|
def default_arg_hash
|
195
197
|
return {} if @sub_modes.empty?
|
196
|
-
return @sub_modes.last
|
198
|
+
return @sub_modes.last[1]
|
197
199
|
end
|
198
200
|
|
199
201
|
def current_command_set
|
200
202
|
return @command_set if @sub_modes.empty?
|
201
|
-
return @sub_modes.last
|
203
|
+
return @sub_modes.last[0]
|
204
|
+
end
|
205
|
+
|
206
|
+
def current_nesting
|
207
|
+
return [] if @sub_modes.empty?
|
208
|
+
return @sub_modes.last[2]
|
202
209
|
end
|
203
210
|
|
204
211
|
def build_subject
|
205
212
|
if @subject.nil?
|
206
|
-
subject=(subject_template())
|
213
|
+
self.subject=(subject_template())
|
207
214
|
end
|
208
215
|
return @subject
|
209
216
|
end
|
File without changes
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require 'command-set/interpreter'
|
1
|
+
require 'command-set/interpreter/base'
|
2
2
|
|
3
3
|
module Command
|
4
4
|
#A looser Subject, used by QuickInterpreter for testing purposes.
|
@@ -7,11 +7,11 @@ module Command
|
|
7
7
|
#easier.
|
8
8
|
class PermissiveSubject < Subject
|
9
9
|
protected
|
10
|
-
def
|
10
|
+
def add_field_writer(name)
|
11
11
|
super
|
12
12
|
(class << self; self; end).instance_eval do
|
13
13
|
define_method(name) do
|
14
|
-
return
|
14
|
+
return @fields[name]
|
15
15
|
end
|
16
16
|
end
|
17
17
|
end
|
@@ -84,7 +84,7 @@ module Command
|
|
84
84
|
|
85
85
|
#Passes the arguments to process_input directly to CommandSetup
|
86
86
|
def cook_input(words)
|
87
|
-
|
87
|
+
current_command_set.process_terms(words, subject)
|
88
88
|
end
|
89
89
|
|
90
90
|
def complete_input(terms, word)
|
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'readline'
|
2
2
|
require 'command-set'
|
3
3
|
require 'dl/import'
|
4
|
-
require 'command-set/interpreter'
|
4
|
+
require 'command-set/interpreter/base'
|
5
5
|
|
6
6
|
#This really locks text-interpreter down to Linux, maybe Unix-like
|
7
7
|
#platforms, I'm thinking. A more flexible way of doing this would rock,
|
@@ -97,8 +97,7 @@ module Command
|
|
97
97
|
rescue CommandException => ce
|
98
98
|
output_exception("Error", ce)
|
99
99
|
rescue Exception => e
|
100
|
-
|
101
|
-
pause_before_dying
|
100
|
+
self.pause_before_dying(e)
|
102
101
|
ensure
|
103
102
|
unless old_proc.nil?
|
104
103
|
set_readline_completion(&old_proc)
|
@@ -115,7 +114,8 @@ module Command
|
|
115
114
|
end
|
116
115
|
end
|
117
116
|
|
118
|
-
def pause_before_dying
|
117
|
+
def pause_before_dying(exception)
|
118
|
+
output_exception("Exception", exception)
|
119
119
|
puts "Waiting for return"
|
120
120
|
$stdin.gets
|
121
121
|
stop
|
@@ -132,7 +132,7 @@ module Command
|
|
132
132
|
line = new_line
|
133
133
|
end
|
134
134
|
line.pop if line.last.empty?
|
135
|
-
return
|
135
|
+
return self.current_command_set.process_terms(line, self.subject)
|
136
136
|
end
|
137
137
|
|
138
138
|
alias single_command process_input
|
@@ -155,29 +155,33 @@ module Command
|
|
155
155
|
end
|
156
156
|
|
157
157
|
def readline_complete(buffer, rl_prefix)
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
158
|
+
begin
|
159
|
+
parsed_input = split_line(buffer)
|
160
|
+
prefix = parsed_input.pop
|
161
|
+
|
162
|
+
#Lest this kill coverage: hide exists to fix readline's irritating
|
163
|
+
#word splitting
|
164
|
+
hide = prefix.sub(%r{#{rl_prefix}$}, "")
|
165
|
+
|
166
|
+
if /"#{prefix}$/ =~ buffer
|
167
|
+
hide = '"' + hide
|
168
|
+
end
|
169
|
+
|
170
|
+
completes = current_command_set.completion_list(parsed_input,
|
171
|
+
prefix, build_subject)
|
172
|
+
completes.map! do |complete|
|
173
|
+
if split_line(complete).length > 1
|
174
|
+
('"' + complete + '"').sub(/^#{hide}/, "")
|
172
175
|
else
|
173
|
-
complete
|
176
|
+
complete.sub(/^#{hide}/, "")
|
174
177
|
end
|
175
|
-
else
|
176
|
-
complete
|
177
178
|
end
|
178
|
-
end
|
179
179
|
|
180
|
-
|
180
|
+
return completes
|
181
|
+
rescue Object => ex
|
182
|
+
#It's really irritating for an app to crap out in completion
|
183
|
+
return ["#{ex.class}: #{ex.message}", ""]
|
184
|
+
end
|
181
185
|
end
|
182
186
|
|
183
187
|
def split_line(line)
|
@@ -209,7 +213,7 @@ module Command
|
|
209
213
|
|
210
214
|
prompt.sub!(*(@command_set.prompt))
|
211
215
|
@sub_modes.each do |mode|
|
212
|
-
prompt.sub!(*(mode.prompt))
|
216
|
+
prompt.sub!(*(mode[0].prompt))
|
213
217
|
end
|
214
218
|
prompt.sub!(*(@behavior[:prompt]))
|
215
219
|
end
|
data/lib/command-set/results.rb
CHANGED
@@ -391,484 +391,10 @@ module Command
|
|
391
391
|
return list
|
392
392
|
end
|
393
393
|
end
|
394
|
-
|
395
|
-
#The end of the Results train. Formatter objects are supposed to output to the user events that they
|
396
|
-
#receive from their presenters. To simplify this process, a number of common IO functions are delegated
|
397
|
-
#to an IO object - usually Command::raw_stdout.
|
398
|
-
#
|
399
|
-
#This class in particular is pretty quiet - probably not helpful for everyday use.
|
400
|
-
#Of course, for some purposes, singleton methods might be very useful
|
401
|
-
class Formatter
|
402
|
-
module Styler
|
403
|
-
Foregrounds = {
|
404
|
-
'black' => 30,
|
405
|
-
'red' => 31,
|
406
|
-
'green' => 32,
|
407
|
-
'yellow' => 33,
|
408
|
-
'blue' => 34,
|
409
|
-
'magenta' => 35,
|
410
|
-
'cyan' => 36,
|
411
|
-
'white' => 37
|
412
|
-
}
|
413
|
-
|
414
|
-
Backgrounds = {}
|
415
|
-
|
416
|
-
Foregrounds.each() do |name, value|
|
417
|
-
Backgrounds[name] = value + 10
|
418
|
-
end
|
419
|
-
|
420
|
-
Extras = {
|
421
|
-
'clear' => 0,
|
422
|
-
'bold' => 1,
|
423
|
-
'underline' => 4,
|
424
|
-
'reversed' => 7
|
425
|
-
}
|
426
|
-
|
427
|
-
def style(text, options)
|
428
|
-
options ||= {}
|
429
|
-
if options.key? :format_advice
|
430
|
-
options = options.merge(options[:format_advice])
|
431
|
-
end
|
432
|
-
aliased = {
|
433
|
-
:foreground => options[:color],
|
434
|
-
:extra => options[:text_style]
|
435
|
-
}
|
436
|
-
options = aliased.merge(options)
|
437
|
-
markup = code_for(Foregrounds, options[:foreground]) +
|
438
|
-
code_for(Backgrounds, options[:background]) +
|
439
|
-
code_for(Extras, options[:extra])
|
440
|
-
return text if markup.empty?
|
441
|
-
return markup + text + code_for(Extras, "clear")
|
442
|
-
end
|
443
|
-
|
444
|
-
def code_for(kind, name)
|
445
|
-
if kind.has_key?(name.to_s)
|
446
|
-
"\e[#{kind[name.to_s]}m"
|
447
|
-
else
|
448
|
-
""
|
449
|
-
end
|
450
|
-
end
|
451
|
-
end
|
452
|
-
extend Forwardable
|
453
|
-
|
454
|
-
class FormatAdvisor
|
455
|
-
def initialize(formatter)
|
456
|
-
@advisee = formatter
|
457
|
-
end
|
458
|
-
|
459
|
-
def list(&block)
|
460
|
-
@advisee.advice[:list] << proc(&block)
|
461
|
-
end
|
462
|
-
|
463
|
-
def item(&block)
|
464
|
-
@advisee.advice[:item] << proc(&block)
|
465
|
-
end
|
466
|
-
|
467
|
-
def output(&block)
|
468
|
-
@advisee.advice[:output] << proc(&block)
|
469
|
-
end
|
470
|
-
end
|
471
|
-
|
472
|
-
def notify(msg, item)
|
473
|
-
if msg == :start
|
474
|
-
start
|
475
|
-
return
|
476
|
-
end
|
477
|
-
if msg == :done
|
478
|
-
finish
|
479
|
-
return
|
480
|
-
end
|
481
|
-
|
482
|
-
apply_advice(item)
|
483
|
-
|
484
|
-
if List === item
|
485
|
-
case msg
|
486
|
-
when :saw_begin
|
487
|
-
saw_begin_list(item)
|
488
|
-
when :saw_end
|
489
|
-
saw_end_list(item)
|
490
|
-
when :arrive
|
491
|
-
closed_begin_list(item)
|
492
|
-
when :leave
|
493
|
-
closed_end_list(item)
|
494
|
-
end
|
495
|
-
else
|
496
|
-
case msg
|
497
|
-
when :arrive
|
498
|
-
closed_item(item)
|
499
|
-
when :saw
|
500
|
-
saw_item(item)
|
501
|
-
end
|
502
|
-
end
|
503
|
-
end
|
504
|
-
|
505
|
-
def initialize(out = nil, err = nil)
|
506
|
-
@out_to = out || ::Command::raw_stdout
|
507
|
-
@err_to = err || ::Command::raw_stderr
|
508
|
-
@advisor = FormatAdvisor.new(self)
|
509
|
-
@advice = {:list => [], :item => [], :output => []}
|
510
|
-
end
|
511
|
-
|
512
|
-
def apply_advice(item)
|
513
|
-
type = List === item ? :list : :item
|
514
|
-
|
515
|
-
item.options[:format_advice] =
|
516
|
-
@advice[type].inject(default_advice(type)) do |advice, advisor|
|
517
|
-
result = advisor[item]
|
518
|
-
break if result == :DONE
|
519
|
-
if Hash === result
|
520
|
-
advice.merge(result)
|
521
|
-
else
|
522
|
-
advice
|
523
|
-
end
|
524
|
-
end
|
525
|
-
end
|
526
|
-
|
527
|
-
attr_reader :advice
|
528
|
-
|
529
|
-
def receive_advice(&block)
|
530
|
-
@advisor.instance_eval(&block)
|
531
|
-
end
|
532
|
-
|
533
|
-
def default_advice(type)
|
534
|
-
{}
|
535
|
-
end
|
536
|
-
|
537
|
-
def_delegators :@out_to, :p, :puts, :print, :printf, :putc, :write, :write_nonblock, :flush
|
538
|
-
|
539
|
-
def self.inherited(sub)
|
540
|
-
sub.extend Forwardable
|
541
|
-
sub.class_eval do
|
542
|
-
def_delegators :@out_to, :p, :puts, :print, :printf, :putc, :write, :write_nonblock, :flush
|
543
|
-
end
|
544
|
-
end
|
545
|
-
|
546
|
-
#Presenter callback: output is beginning
|
547
|
-
def start; end
|
548
|
-
|
549
|
-
#Presenter callback: a list has just started
|
550
|
-
def saw_begin_list(list); end
|
551
|
-
|
552
|
-
#Presenter callback: an item has just been added
|
553
|
-
def saw_item(item); end
|
554
|
-
|
555
|
-
#Presenter callback: a list has just ended
|
556
|
-
def saw_end_list(list); end
|
557
|
-
|
558
|
-
#Presenter callback: a list opened, tree order
|
559
|
-
def closed_begin_list(list); end
|
560
|
-
|
561
|
-
#Presenter callback: an item added, tree order
|
562
|
-
def closed_item(item); end
|
563
|
-
|
564
|
-
#Presenter callback: an list closed, tree order
|
565
|
-
def closed_end_list(list); end
|
566
|
-
|
567
|
-
#Presenter callback: output is done
|
568
|
-
def finish; end
|
569
|
-
end
|
570
|
-
|
571
|
-
#The simplest useful Formatter: it outputs the value of every item in tree order. Think of
|
572
|
-
#it as what would happen if you just let puts and p go directly to the screen, without the
|
573
|
-
#annoying consequences of threading, etc.
|
574
|
-
class TextFormatter < Formatter
|
575
|
-
def closed_item(value)
|
576
|
-
puts value
|
577
|
-
end
|
578
|
-
end
|
579
|
-
|
580
|
-
class StrategyFormatter < Formatter
|
581
|
-
class FormatStrategy
|
582
|
-
extend Forwardable
|
583
|
-
include Formatter::Styler
|
584
|
-
|
585
|
-
def initialize(name, formatter)
|
586
|
-
@name = name
|
587
|
-
@formatter = formatter
|
588
|
-
setup
|
589
|
-
end
|
590
|
-
|
591
|
-
def setup; end
|
592
|
-
|
593
|
-
def_delegators :@formatter, :p, :puts, :print, :printf, :putc, :write, :write_nonblock, :flush
|
594
|
-
|
595
|
-
attr_reader :name
|
596
|
-
|
597
|
-
def switch_to(name)
|
598
|
-
unless name == self.name
|
599
|
-
return true
|
600
|
-
end
|
601
|
-
return false
|
602
|
-
end
|
603
|
-
|
604
|
-
def finish
|
605
|
-
@formatter.pop_strategy(self.name)
|
606
|
-
end
|
607
|
-
|
608
|
-
#Presenter callback: a list has just started
|
609
|
-
def saw_begin_list(list); end
|
610
|
-
|
611
|
-
#Presenter callback: an item has just been added
|
612
|
-
def saw_item(item); end
|
613
|
-
|
614
|
-
#Presenter callback: a list has just ended
|
615
|
-
def saw_end_list(list); end
|
616
|
-
|
617
|
-
#Presenter callback: a list opened, tree order
|
618
|
-
def closed_begin_list(list);
|
619
|
-
end
|
620
|
-
|
621
|
-
#Presenter callback: an item added, tree order
|
622
|
-
def closed_item(item); end
|
623
|
-
|
624
|
-
#Presenter callback: an list closed, tree order
|
625
|
-
def closed_end_list(list);
|
626
|
-
if list.options[:strategy_start] == self
|
627
|
-
finish
|
628
|
-
end
|
629
|
-
end
|
630
|
-
|
631
|
-
private
|
632
|
-
def out
|
633
|
-
@formatter.out_to
|
634
|
-
end
|
635
|
-
|
636
|
-
def err
|
637
|
-
@formatter.err_to
|
638
|
-
end
|
639
|
-
end
|
640
|
-
|
641
|
-
@strategies = {:default => FormatStrategy}
|
642
|
-
|
643
|
-
class << self
|
644
|
-
def strategy(name, base_klass = FormatStrategy, &def_block)
|
645
|
-
@strategies[name.to_sym] = Class.new(base_klass, &def_block)
|
646
|
-
end
|
647
|
-
|
648
|
-
def inherited(sub)
|
649
|
-
self.instance_variables.each do |var|
|
650
|
-
value = self.instance_variable_get(var)
|
651
|
-
if value.nil?
|
652
|
-
sub.instance_variable_set(var, nil)
|
653
|
-
else
|
654
|
-
sub.instance_variable_set(var, value.dup)
|
655
|
-
end
|
656
|
-
end
|
657
|
-
end
|
658
|
-
|
659
|
-
def strategy_set(formatter)
|
660
|
-
set = {}
|
661
|
-
@strategies.each_pair do |name, klass|
|
662
|
-
set[name] = klass.new(name, formatter)
|
663
|
-
end
|
664
|
-
return set
|
665
|
-
end
|
666
|
-
end
|
667
|
-
|
668
|
-
def initialize(out = nil, err = nil)
|
669
|
-
super(out, err)
|
670
|
-
@strategies = self.class.strategy_set(self)
|
671
|
-
@strategy_stack = [@strategies[:default]]
|
672
|
-
end
|
673
|
-
|
674
|
-
attr_reader :out_to, :err_to
|
675
|
-
|
676
|
-
def_delegators :current_strategy, :saw_begin_list, :saw_item,
|
677
|
-
:saw_end_list
|
678
|
-
|
679
|
-
def current_strategy
|
680
|
-
@strategy_stack.last
|
681
|
-
end
|
682
|
-
|
683
|
-
def push_strategy(name)
|
684
|
-
if @strategies.has_key?(name)
|
685
|
-
@strategy_stack.push(@strategies[name])
|
686
|
-
end
|
687
|
-
end
|
688
|
-
|
689
|
-
def pop_strategy(name)
|
690
|
-
if current_strategy.name == name
|
691
|
-
@strategy_stack.pop
|
692
|
-
end
|
693
|
-
end
|
694
|
-
|
695
|
-
def closed_begin_list(list)
|
696
|
-
going_to = current_strategy.name
|
697
|
-
unless list.options[:format_advice].nil? or
|
698
|
-
(next_strategy = list.options[:format_advice][:type]).nil? or
|
699
|
-
@strategies[next_strategy].nil? or
|
700
|
-
not current_strategy.switch_to(next_strategy)
|
701
|
-
going_to = next_strategy
|
702
|
-
end
|
703
|
-
push_strategy(going_to)
|
704
|
-
current_strategy.closed_begin_list(list)
|
705
|
-
end
|
706
|
-
|
707
|
-
def closed_end_list(list)
|
708
|
-
current_strategy.closed_end_list(list)
|
709
|
-
current_strategy.finish
|
710
|
-
end
|
711
|
-
|
712
|
-
def closed_item(item)
|
713
|
-
unless item.options[:format_advice].nil? or
|
714
|
-
(once = item.options[:format_advice][:type]).nil? or
|
715
|
-
@strategies[once].nil? or
|
716
|
-
not current_strategy.switch_to(once)
|
717
|
-
@strategies[once].closed_item(item)
|
718
|
-
else
|
719
|
-
current_strategy.closed_item(item)
|
720
|
-
end
|
721
|
-
end
|
722
|
-
|
723
|
-
strategy :default do
|
724
|
-
def closed_item(value)
|
725
|
-
puts value
|
726
|
-
end
|
727
|
-
end
|
728
|
-
|
729
|
-
strategy :progress do
|
730
|
-
def switch_to(name); false; end
|
731
|
-
def closed_begin_list(list)
|
732
|
-
puts unless list.depth == 0
|
733
|
-
justify = 0 || list[:format_advice][:justify]
|
734
|
-
print style(list.to_s.ljust(justify), list.options)
|
735
|
-
end
|
736
|
-
|
737
|
-
def closed_item(item)
|
738
|
-
print style(".", item.options)
|
739
|
-
flush
|
740
|
-
end
|
741
|
-
|
742
|
-
def closed_end_list(list)
|
743
|
-
puts
|
744
|
-
super
|
745
|
-
end
|
746
|
-
end
|
747
|
-
|
748
|
-
strategy :indent do
|
749
|
-
def setup
|
750
|
-
@indent_level = 0
|
751
|
-
end
|
752
|
-
|
753
|
-
def indent
|
754
|
-
return " " * @indent_level
|
755
|
-
end
|
756
|
-
|
757
|
-
def closed_begin_list(list)
|
758
|
-
super
|
759
|
-
puts indent + style(list.to_s, list.options)
|
760
|
-
@indent_level += 1
|
761
|
-
@indent_level
|
762
|
-
end
|
763
|
-
|
764
|
-
def closed_item(item)
|
765
|
-
item.to_s.split(/\s*\n\s*/).each do |line|
|
766
|
-
puts indent + style(line, item.options)
|
767
|
-
end
|
768
|
-
super
|
769
|
-
end
|
770
|
-
|
771
|
-
def closed_end_list(list)
|
772
|
-
@indent_level -= 1
|
773
|
-
@indent_level
|
774
|
-
super
|
775
|
-
end
|
776
|
-
end
|
777
|
-
|
778
|
-
strategy :invisible do
|
779
|
-
def closed_item(value); end
|
780
|
-
end
|
781
|
-
|
782
|
-
strategy :skip do
|
783
|
-
def closed_begin_list(list)
|
784
|
-
finish
|
785
|
-
end
|
786
|
-
end
|
787
|
-
|
788
|
-
strategy :chatty do
|
789
|
-
def switch_to(name); false; end
|
790
|
-
|
791
|
-
def saw_begin_list(list)
|
792
|
-
err.print style("B", list.options)
|
793
|
-
end
|
794
|
-
|
795
|
-
def saw_item(list)
|
796
|
-
err.print style(".", list.options)
|
797
|
-
end
|
798
|
-
|
799
|
-
def saw_end_list(list)
|
800
|
-
err.print style("E", list.options)
|
801
|
-
end
|
802
|
-
|
803
|
-
def closed_begin_list(list);
|
804
|
-
clean_options = list.options.dup
|
805
|
-
clean_options.delete(:strategy_start)
|
806
|
-
puts "> #{list.to_s} (depth=#{list.depth} #{clean_options.inspect})"
|
807
|
-
end
|
808
|
-
def closed_item(list)
|
809
|
-
puts " " + list.to_s +
|
810
|
-
unless(list.options.empty?)
|
811
|
-
" " + list.options.inspect
|
812
|
-
else
|
813
|
-
""
|
814
|
-
end
|
815
|
-
end
|
816
|
-
def closed_end_list(list); puts "< " + list.to_s; end
|
817
|
-
end
|
818
|
-
end
|
819
|
-
|
820
|
-
#A trivial and obvious Formatter: produces well-formed XML fragments based on events. It even
|
821
|
-
#indents. Might be handy for more complicated output processing, since you could feed the document
|
822
|
-
#to a XSLT processor.
|
823
|
-
class XMLFormatter < Formatter
|
824
|
-
def initialize(out = nil, err = nil, indent=" ", newline="\n")
|
825
|
-
super(out, err)
|
826
|
-
@indent = indent
|
827
|
-
@newline = newline
|
828
|
-
@indent_level=0
|
829
|
-
end
|
830
|
-
|
831
|
-
def line(string)
|
832
|
-
print "#{@indent * @indent_level}#{string}#@newline"
|
833
|
-
end
|
834
|
-
|
835
|
-
def closed_begin_list(name)
|
836
|
-
line "<#{name}#{xmlize_options(name)}>"
|
837
|
-
@indent_level += 1
|
838
|
-
end
|
839
|
-
|
840
|
-
def closed_item(value)
|
841
|
-
line "<item value=\"#{value}\"#{xmlize_options(value)} />"
|
842
|
-
end
|
843
|
-
|
844
|
-
def closed_end_list(name)
|
845
|
-
@indent_level -= 1
|
846
|
-
if @indent_level < 0
|
847
|
-
@indent_level = 0
|
848
|
-
return
|
849
|
-
end
|
850
|
-
line "</#{name}>"
|
851
|
-
end
|
852
|
-
|
853
|
-
private
|
854
|
-
|
855
|
-
def flatten_value(value)
|
856
|
-
case value
|
857
|
-
when Hash
|
858
|
-
return value.map do |name, value|
|
859
|
-
"#{name}: #{value}"
|
860
|
-
end.join("; ")
|
861
|
-
else
|
862
|
-
return value.to_s
|
863
|
-
end
|
864
|
-
end
|
865
|
-
|
866
|
-
def xmlize_options(item)
|
867
|
-
item.options.inject("") do |string, (name, value)|
|
868
|
-
string + " #{name}=\"#{flatten_value(value)}\""
|
869
|
-
end
|
870
|
-
end
|
871
|
-
end
|
872
394
|
end
|
873
395
|
end
|
874
396
|
|
397
|
+
#TODO migrate these requires to only where they're needed
|
398
|
+
require 'command-set/formatter/base'
|
399
|
+
require 'command-set/formatter/strategy'
|
400
|
+
require 'command-set/formatter/xml'
|