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.
@@ -0,0 +1,186 @@
1
+ module Command::Results
2
+ #The end of the Results train. Formatter objects are supposed to output to the user events that they
3
+ #receive from their presenters. To simplify this process, a number of common IO functions are delegated
4
+ #to an IO object - usually Command::raw_stdout.
5
+ #
6
+ #This class in particular is pretty quiet - probably not helpful for everyday use.
7
+ #Of course, for some purposes, singleton methods might be very useful
8
+ class Formatter
9
+ module Styler
10
+ Foregrounds = {
11
+ 'black' => 30,
12
+ 'red' => 31,
13
+ 'green' => 32,
14
+ 'yellow' => 33,
15
+ 'blue' => 34,
16
+ 'magenta' => 35,
17
+ 'cyan' => 36,
18
+ 'white' => 37
19
+ }
20
+
21
+ Backgrounds = {}
22
+
23
+ Foregrounds.each() do |name, value|
24
+ Backgrounds[name] = value + 10
25
+ end
26
+
27
+ Extras = {
28
+ 'clear' => 0,
29
+ 'bold' => 1,
30
+ 'underline' => 4,
31
+ 'reversed' => 7
32
+ }
33
+
34
+ def style(text, options)
35
+ options ||= {}
36
+ if options.key? :format_advice
37
+ options = options.merge(options[:format_advice])
38
+ end
39
+ aliased = {
40
+ :foreground => options[:color],
41
+ :extra => options[:text_style]
42
+ }
43
+ options = aliased.merge(options)
44
+ markup = code_for(Foregrounds, options[:foreground]) +
45
+ code_for(Backgrounds, options[:background]) +
46
+ code_for(Extras, options[:extra])
47
+ return text if markup.empty?
48
+ return markup + text + code_for(Extras, "clear")
49
+ end
50
+
51
+ def code_for(kind, name)
52
+ if kind.has_key?(name.to_s)
53
+ "\e[#{kind[name.to_s]}m"
54
+ else
55
+ ""
56
+ end
57
+ end
58
+ end
59
+ extend Forwardable
60
+
61
+ class FormatAdvisor
62
+ def initialize(formatter)
63
+ @advisee = formatter
64
+ end
65
+
66
+ def list(&block)
67
+ @advisee.advice[:list] << proc(&block)
68
+ end
69
+
70
+ def item(&block)
71
+ @advisee.advice[:item] << proc(&block)
72
+ end
73
+
74
+ def output(&block)
75
+ @advisee.advice[:output] << proc(&block)
76
+ end
77
+ end
78
+
79
+ def notify(msg, item)
80
+ if msg == :start
81
+ start
82
+ return
83
+ end
84
+ if msg == :done
85
+ finish
86
+ return
87
+ end
88
+
89
+ apply_advice(item)
90
+
91
+ if List === item
92
+ case msg
93
+ when :saw_begin
94
+ saw_begin_list(item)
95
+ when :saw_end
96
+ saw_end_list(item)
97
+ when :arrive
98
+ closed_begin_list(item)
99
+ when :leave
100
+ closed_end_list(item)
101
+ end
102
+ else
103
+ case msg
104
+ when :arrive
105
+ closed_item(item)
106
+ when :saw
107
+ saw_item(item)
108
+ end
109
+ end
110
+ end
111
+
112
+ def initialize(out = nil, err = nil)
113
+ @out_to = out || ::Command::raw_stdout
114
+ @err_to = err || ::Command::raw_stderr
115
+ @advisor = FormatAdvisor.new(self)
116
+ @advice = {:list => [], :item => [], :output => []}
117
+ end
118
+
119
+ def apply_advice(item)
120
+ type = List === item ? :list : :item
121
+
122
+ item.options[:format_advice] =
123
+ @advice[type].inject(default_advice(type)) do |advice, advisor|
124
+ result = advisor[item]
125
+ break if result == :DONE
126
+ if Hash === result
127
+ advice.merge(result)
128
+ else
129
+ advice
130
+ end
131
+ end
132
+ end
133
+
134
+ attr_reader :advice
135
+
136
+ def receive_advice(&block)
137
+ @advisor.instance_eval(&block)
138
+ end
139
+
140
+ def default_advice(type)
141
+ {}
142
+ end
143
+
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
+ #Presenter callback: output is beginning
154
+ def start; end
155
+
156
+ #Presenter callback: a list has just started
157
+ def saw_begin_list(list); end
158
+
159
+ #Presenter callback: an item has just been added
160
+ def saw_item(item); end
161
+
162
+ #Presenter callback: a list has just ended
163
+ def saw_end_list(list); end
164
+
165
+ #Presenter callback: a list opened, tree order
166
+ def closed_begin_list(list); end
167
+
168
+ #Presenter callback: an item added, tree order
169
+ def closed_item(item); end
170
+
171
+ #Presenter callback: an list closed, tree order
172
+ def closed_end_list(list); end
173
+
174
+ #Presenter callback: output is done
175
+ def finish; end
176
+ end
177
+
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.
181
+ class TextFormatter < Formatter
182
+ def closed_item(value)
183
+ puts value
184
+ end
185
+ end
186
+ end
@@ -0,0 +1,243 @@
1
+ require 'command-set/formatter/base'
2
+
3
+ module Command::Results
4
+ class StrategyFormatter < Formatter
5
+ class FormatStrategy
6
+ extend Forwardable
7
+ include Formatter::Styler
8
+
9
+ def initialize(name, formatter)
10
+ @name = name
11
+ @formatter = formatter
12
+ setup
13
+ end
14
+
15
+ def setup; end
16
+
17
+ def_delegators :@formatter, :p, :puts, :print, :printf, :putc, :write, :write_nonblock, :flush
18
+
19
+ attr_reader :name
20
+
21
+ def switch_to(name)
22
+ unless name == self.name
23
+ return true
24
+ end
25
+ return false
26
+ end
27
+
28
+ def finish
29
+ @formatter.pop_strategy(self.name)
30
+ end
31
+
32
+ #Presenter callback: a list has just started
33
+ def saw_begin_list(list); end
34
+
35
+ #Presenter callback: an item has just been added
36
+ def saw_item(item); end
37
+
38
+ #Presenter callback: a list has just ended
39
+ def saw_end_list(list); end
40
+
41
+ #Presenter callback: a list opened, tree order
42
+ def closed_begin_list(list);
43
+ end
44
+
45
+ #Presenter callback: an item added, tree order
46
+ def closed_item(item); end
47
+
48
+ #Presenter callback: an list closed, tree order
49
+ def closed_end_list(list);
50
+ if list.options[:strategy_start] == self
51
+ finish
52
+ end
53
+ end
54
+
55
+ private
56
+ def out
57
+ @formatter.out_to
58
+ end
59
+
60
+ def err
61
+ @formatter.err_to
62
+ end
63
+ end
64
+
65
+ @strategies = {:default => FormatStrategy}
66
+
67
+ class << self
68
+ def strategy(name, base_klass = FormatStrategy, &def_block)
69
+ @strategies[name.to_sym] = Class.new(base_klass, &def_block)
70
+ end
71
+
72
+ def inherited(sub)
73
+ self.instance_variables.each do |var|
74
+ value = self.instance_variable_get(var)
75
+ if value.nil?
76
+ sub.instance_variable_set(var, nil)
77
+ else
78
+ sub.instance_variable_set(var, value.dup)
79
+ end
80
+ end
81
+ end
82
+
83
+ def strategy_set(formatter)
84
+ set = {}
85
+ @strategies.each_pair do |name, klass|
86
+ set[name] = klass.new(name, formatter)
87
+ end
88
+ return set
89
+ end
90
+ end
91
+
92
+ def initialize(out = nil, err = nil)
93
+ super(out, err)
94
+ @strategies = self.class.strategy_set(self)
95
+ @strategy_stack = [@strategies[:default]]
96
+ end
97
+
98
+ attr_reader :out_to, :err_to
99
+
100
+ def_delegators :current_strategy, :saw_begin_list, :saw_item,
101
+ :saw_end_list
102
+
103
+ def current_strategy
104
+ @strategy_stack.last
105
+ end
106
+
107
+ def push_strategy(name)
108
+ if @strategies.has_key?(name)
109
+ @strategy_stack.push(@strategies[name])
110
+ end
111
+ end
112
+
113
+ def pop_strategy(name)
114
+ if current_strategy.name == name
115
+ @strategy_stack.pop
116
+ end
117
+ end
118
+
119
+ def closed_begin_list(list)
120
+ going_to = current_strategy.name
121
+ unless list.options[:format_advice].nil? or
122
+ (next_strategy = list.options[:format_advice][:type]).nil? or
123
+ @strategies[next_strategy].nil? or
124
+ not current_strategy.switch_to(next_strategy)
125
+ going_to = next_strategy
126
+ end
127
+ push_strategy(going_to)
128
+ current_strategy.closed_begin_list(list)
129
+ end
130
+
131
+ def closed_end_list(list)
132
+ current_strategy.closed_end_list(list)
133
+ current_strategy.finish
134
+ end
135
+
136
+ def closed_item(item)
137
+ unless item.options[:format_advice].nil? or
138
+ (once = item.options[:format_advice][:type]).nil? or
139
+ @strategies[once].nil? or
140
+ not current_strategy.switch_to(once)
141
+ @strategies[once].closed_item(item)
142
+ else
143
+ current_strategy.closed_item(item)
144
+ end
145
+ end
146
+
147
+ strategy :default do
148
+ def closed_item(value)
149
+ puts value
150
+ end
151
+ end
152
+
153
+ strategy :progress do
154
+ def switch_to(name); false; end
155
+ def closed_begin_list(list)
156
+ puts unless list.depth == 0
157
+ justify = 0 || list[:format_advice][:justify]
158
+ print style(list.to_s.ljust(justify), list.options)
159
+ end
160
+
161
+ def closed_item(item)
162
+ print style(".", item.options)
163
+ flush
164
+ end
165
+
166
+ def closed_end_list(list)
167
+ puts
168
+ super
169
+ end
170
+ end
171
+
172
+ strategy :indent do
173
+ def setup
174
+ @indent_level = 0
175
+ end
176
+
177
+ def indent
178
+ return " " * @indent_level
179
+ end
180
+
181
+ def closed_begin_list(list)
182
+ super
183
+ puts indent + style(list.to_s, list.options)
184
+ @indent_level += 1
185
+ @indent_level
186
+ end
187
+
188
+ def closed_item(item)
189
+ item.to_s.split(/\s*\n\s*/).each do |line|
190
+ puts indent + style(line, item.options)
191
+ end
192
+ super
193
+ end
194
+
195
+ def closed_end_list(list)
196
+ @indent_level -= 1
197
+ @indent_level
198
+ super
199
+ end
200
+ end
201
+
202
+ strategy :invisible do
203
+ def closed_item(value); end
204
+ end
205
+
206
+ strategy :skip do
207
+ def closed_begin_list(list)
208
+ finish
209
+ end
210
+ end
211
+
212
+ strategy :chatty do
213
+ def switch_to(name); false; end
214
+
215
+ def saw_begin_list(list)
216
+ err.print style("B", list.options)
217
+ end
218
+
219
+ def saw_item(list)
220
+ err.print style(".", list.options)
221
+ end
222
+
223
+ def saw_end_list(list)
224
+ err.print style("E", list.options)
225
+ end
226
+
227
+ def closed_begin_list(list);
228
+ clean_options = list.options.dup
229
+ clean_options.delete(:strategy_start)
230
+ puts "> #{list.to_s} (depth=#{list.depth} #{clean_options.inspect})"
231
+ end
232
+ def closed_item(list)
233
+ puts " " + list.to_s +
234
+ unless(list.options.empty?)
235
+ " " + list.options.inspect
236
+ else
237
+ ""
238
+ end
239
+ end
240
+ def closed_end_list(list); puts "< " + list.to_s; end
241
+ end
242
+ end
243
+ end
@@ -0,0 +1,56 @@
1
+ require 'command-set/formatter/base'
2
+
3
+ module Command::Results
4
+ #A trivial and obvious Formatter: produces well-formed XML fragments based on events. It even
5
+ #indents. Might be handy for more complicated output processing, since you could feed the document
6
+ #to a XSLT processor.
7
+ class XMLFormatter < Formatter
8
+ def initialize(out = nil, err = nil, indent=" ", newline="\n")
9
+ super(out, err)
10
+ @indent = indent
11
+ @newline = newline
12
+ @indent_level=0
13
+ end
14
+
15
+ def line(string)
16
+ print "#{@indent * @indent_level}#{string}#@newline"
17
+ end
18
+
19
+ def closed_begin_list(name)
20
+ line "<#{name}#{xmlize_options(name)}>"
21
+ @indent_level += 1
22
+ end
23
+
24
+ def closed_item(value)
25
+ line "<item value=\"#{value}\"#{xmlize_options(value)} />"
26
+ end
27
+
28
+ def closed_end_list(name)
29
+ @indent_level -= 1
30
+ if @indent_level < 0
31
+ @indent_level = 0
32
+ return
33
+ end
34
+ line "</#{name}>"
35
+ end
36
+
37
+ private
38
+
39
+ def flatten_value(value)
40
+ case value
41
+ when Hash
42
+ return value.map do |name, value|
43
+ "#{name}: #{value}"
44
+ end.join("; ")
45
+ else
46
+ return value.to_s
47
+ end
48
+ end
49
+
50
+ def xmlize_options(item)
51
+ item.options.inject("") do |string, (name, value)|
52
+ string + " #{name}=\"#{flatten_value(value)}\""
53
+ end
54
+ end
55
+ end
56
+ end