command-set 0.9.2 → 0.10.0
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/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'
|