qed 2.5.1 → 2.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -13,57 +13,86 @@ module QED
13
13
 
14
14
  DEFAULT_TITLE = "Demonstration"
15
15
  DEFAULT_CSS = nil #"../assets/styles/spec.css"
16
- DEFAULT_OUTPUT = "doc/qedoc"
17
- DEFAULT_PATH = "test/demos"
16
+ DEFAULT_OUTPUT = "qed.html"
17
+ DEFAULT_PATH = "qed"
18
18
 
19
19
  attr_accessor :title
20
+
20
21
  attr_accessor :css
21
- attr_accessor :paths
22
+
22
23
  attr_accessor :dryrun
24
+
23
25
  attr_accessor :quiet
24
26
 
25
27
  # Ouput file.
26
28
  attr_accessor :output
27
29
 
30
+ # Either html or plain.
31
+ # Defaults to extension of output file.
32
+ attr_accessor :format
33
+
34
+ #
35
+ attr_reader :paths
36
+
37
+ #
38
+ def paths=(paths)
39
+ @paths = [paths].flatten
40
+ end
41
+
28
42
  # New Spec Document object.
29
43
  def initialize(options={})
30
44
  options.each do |k,v|
31
45
  __send__("#{k}=", v)
32
46
  end
33
47
 
48
+ @paths ||= []
49
+
50
+ @output ||= DEFAULT_OUTPUT
34
51
  @title ||= DEFAULT_TITLE
35
52
  @css ||= DEFAULT_CSS
36
- @output ||= DEFAULT_OUTPUT
37
- @paths ||= []
53
+
54
+ if File.directory?(@output)
55
+ @output = File.join(@output, 'qed.html')
56
+ end
57
+
58
+ @format ||= File.extname(@output).sub('.','')
38
59
 
39
60
  if @paths.empty?
40
- dir = Dir['{test/demos,demos,demo}'].first || DEFAULT_PATH
41
- @paths = File.join(dir, '**', '*')
61
+ #dir = Dir['{test/demos,demos,demo}'].first || DEFAULT_PATH
62
+ #@paths = File.join(dir, '**', '*')
63
+ abort "No files to document."
42
64
  end
43
65
  end
44
66
 
45
67
  # Demo files.
46
68
  def demo_files
47
69
  @demo_files ||= (
48
- glob = paths.map do |f|
49
- File.directory?(f) ? Dir[File.join(f,'**/*')] : Dir[f]
50
- end
51
- glob = glob.flatten.select do |f|
52
- File.file?(f) && f !~ /fixtures\/|helpers\// && f !~ /\.rb$/
70
+ files = []
71
+ paths.each do |f|
72
+ if File.directory?(f)
73
+ files.concat Dir[File.join(f,'**','*')]
74
+ else
75
+ files.concat Dir[f]
76
+ end
53
77
  end
54
- glob.sort
78
+ files = files.reject{ |f| File.directory?(f) }
79
+ files = files.reject{ |f| File.extname(f) == '.rb' }
80
+ files = files.reject{ |f| /(fixtures|helpers)\// =~ f }
81
+ files.sort
55
82
  )
56
83
  end
57
84
 
58
85
  # Supress output.
59
- def quiet? ; @quiet ; end
86
+ def quiet?
87
+ @quiet
88
+ end
60
89
 
61
90
  # Generate specification document.
62
91
  def generate
63
- copy_support_files
92
+ #copy_support_files
64
93
 
65
- output = ''
66
- files = []
94
+ out = ''
95
+ files = []
67
96
 
68
97
  #paths.each do |path|
69
98
  # files.concat(Dir.glob(path).select{ |f| File.file?(f) })
@@ -102,33 +131,49 @@ module QED
102
131
  when '.rd', '.rdoc'
103
132
  require_rdoc
104
133
  require_qedoc
105
- markup = Markup.new(txt)
106
- text << markup.to_html
107
- #text << markup.convert(iotext, formatter)
134
+ if html?
135
+ markup = Markup.new(txt)
136
+ text << markup.to_html
137
+ #text << markup.convert(iotext, formatter)
138
+ else
139
+ text << txt
140
+ end
108
141
  when '.md', '.markdown'
109
142
  require_rdiscount
110
- markdown = RDiscount.new(txt)
111
- text << markdown.to_html
143
+ if html?
144
+ markdown = RDiscount.new(txt)
145
+ text << markdown.to_html
146
+ else
147
+ text << txt
148
+ end
112
149
  end
113
150
 
114
- output << "#{text}\n"
151
+ out << "#{text}\n"
115
152
  end
116
153
 
117
- temp = Template.new(template, output, title, css)
118
- html = temp.parse_template
119
-
120
- save(html)
154
+ if html?
155
+ temp = Template.new(template, output, title, css)
156
+ html = temp.parse_template
157
+ save(html)
158
+ else
159
+ save(out)
160
+ end
121
161
  end
122
162
 
123
163
  #
124
- def copy_support_files
125
- make_output_directory
126
- %w{jquery.js}.each do |fname|
127
- file = File.join(File.dirname(__FILE__), 'document', fname)
128
- FileUtils.cp(file, output)
129
- end
164
+ def html?
165
+ format == 'html'
130
166
  end
131
167
 
168
+ #
169
+ #def copy_support_files
170
+ # make_output_directory
171
+ # %w{jquery.js}.each do |fname|
172
+ # file = File.join(File.dirname(__FILE__), 'document', fname)
173
+ # FileUtils.cp(file, output)
174
+ # end
175
+ #end
176
+
132
177
  # Load specification HTML template.
133
178
  def template
134
179
  @template ||= (
@@ -143,14 +188,15 @@ module QED
143
188
  puts "\nwrite #{output}"
144
189
  else
145
190
  make_output_directory
146
- File.open(output + '/index.html', 'wb') do |f|
191
+ File.open(output, 'wb') do |f|
147
192
  f << text
148
193
  end
149
194
  end
150
195
  end
151
196
 
152
197
  def make_output_directory
153
- FileUtils.mkdir_p(output)
198
+ dir = File.dirname(output)
199
+ FileUtils.mkdir_p(dir) unless File.directory?(dir)
154
200
  end
155
201
 
156
202
  private
@@ -173,7 +219,7 @@ module QED
173
219
  #
174
220
  def require_qedoc
175
221
  @require_qedoc ||= (
176
- require 'qedoc/document/markup'
222
+ require 'qed/document/markup'
177
223
  true
178
224
  )
179
225
  end
File without changes
File without changes
@@ -103,17 +103,12 @@
103
103
  .title { font-size: 2em; }
104
104
  </style>
105
105
 
106
- <!-- TODO: only include if these files exists -->
107
- <link href="../assets/styles/spec.css" type="text/css" rel="stylesheet">
108
- <!-- spec.css might be a problem with clobber -->
109
- <link href="spec.css" type="text/css" rel="stylesheet">
110
-
111
106
  <% if css %>
112
107
  <link rel="stylesheet" href="<%= css %>" type="text/css">
113
108
  <% end %>
114
109
 
115
110
  <!-- JQuery is needed -->
116
- <script src="jquery.js" type="text/javascript" language="javascript"></script>
111
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/1.6.1/jquery.min.js" type="text/javascript" language="javascript"></script>
117
112
 
118
113
  </head>
119
114
 
@@ -122,7 +117,7 @@
122
117
  <!-- Side Table of Contents -->
123
118
  <div id="sidebar" style="position: fixed; top: 10; right: 10; background: white;">
124
119
  <a href="javascript: toc_toggle();">
125
- <img src="img/icon/book.jpg" height="30px;" style="border: none;" alt="TOC" align="right"/>
120
+ <img src="http://www.cdnjs.com/images/poweredbycloudflare.png" style="border: none;" alt="TOC" align="right"/>
126
121
  </a>
127
122
 
128
123
  <div id="toc_side" class="toc">
@@ -131,7 +126,7 @@
131
126
 
132
127
  <div id="container">
133
128
  <div id="header">
134
- <img src="img/icon/book.jpg" align="left" style="padding-right: 10px;" alt=""/>
129
+ <img src="http://www.cdnjs.com/images/poweredbycloudflare.png" align="left" style="padding-right: 10px;" alt=""/>
135
130
 
136
131
  <div class="title"><%= title %></div>
137
132
 
@@ -8,7 +8,7 @@ module QED
8
8
  #
9
9
  def initialize(script, *observers)
10
10
  @script = script
11
- @steps = script.parse
11
+ @steps = script.steps
12
12
 
13
13
  #@file = script.file
14
14
  #@scope = script.scope
@@ -1,5 +1,10 @@
1
1
  module QED
2
2
 
3
+ #
4
+ def self.all_steps
5
+ @all_steps ||= []
6
+ end
7
+
3
8
  # The parser breaks down a demonstandum into
4
9
  # structured object to passed thru the script
5
10
  # evaluator.
@@ -198,6 +203,8 @@ module QED
198
203
 
199
204
  #
200
205
  def initialize(file)
206
+ QED.all_steps << self
207
+
201
208
  @file = file
202
209
  @raw = []
203
210
  @type = :description
@@ -2,16 +2,25 @@ module QED
2
2
  module Reporter
3
3
 
4
4
  require 'facets/string'
5
- require 'ansi/code'
5
+
6
+ begin
7
+ require 'ansi/core'
8
+ rescue LoadError
9
+ require 'ansi/code'
10
+ end
6
11
 
7
12
  # = Reporter Absract Base Class
8
13
  #
9
14
  # Serves as the base class for all other output formats.
10
15
  class Abstract
11
16
 
17
+ attr :session
18
+
12
19
  attr :io
20
+
13
21
  attr :record
14
22
 
23
+ # TODO: pass session into initialize
15
24
  def initialize(options={})
16
25
  @io = options[:io] || STDOUT
17
26
  @trace = options[:trace]
@@ -110,6 +119,7 @@ module Reporter
110
119
 
111
120
  # At the start of a session, before running any demonstrations.
112
121
  def before_session(session)
122
+ @session = session
113
123
  @start_time = Time.now
114
124
  end
115
125
 
@@ -248,12 +258,24 @@ module Reporter
248
258
  end
249
259
 
250
260
  #
251
- def clean_backtrace(btrace)
252
- btrace.chomp(":in \`__binding__'")
261
+ INTERNALS = /(lib|bin)[\\\/](qed|ae)/
262
+
263
+ #
264
+ def sane_backtrace(exception)
265
+ if trace_count
266
+ clean_backtrace(*exception.backtrace[0, trace_count])
267
+ else
268
+ clean_backtrace(*exception.backtrace)
269
+ end
253
270
  end
254
271
 
255
272
  #
256
- INTERNALS = /(lib|bin)[\\\/](qed|ae)/
273
+ def clean_backtrace(*btrace)
274
+ stack = btrace.reject{ |bt| bt =~ INTERNALS } unless $DEBUG
275
+ stack.map do |bt|
276
+ bt.chomp(":in \`__binding__'")
277
+ end
278
+ end
257
279
 
258
280
  =begin
259
281
  # Clean the backtrace of any reference to ko/ paths and code.
@@ -269,44 +291,140 @@ module Reporter
269
291
  end
270
292
  =end
271
293
 
294
+ # Produce a pretty code snippet given an exception.
272
295
  #
273
- def code_snippet(exception, bredth=3)
274
- backtrace = exception.backtrace.reject{ |bt| bt =~ INTERNALS }
275
- backtrace.first =~ /(.+?):(\d+(?=:|\z))/ or return ""
276
- source_file, source_line = $1, $2.to_i
296
+ # @param exception [Exception, String]
297
+ # An exception or backtrace.
298
+ #
299
+ # @param radius [Integer]
300
+ # The number of surrounding lines to show.
301
+ #
302
+ # @return [String] pretty code snippet
303
+ def code_snippet(exception, radius=2)
304
+ radius = radius.to_i
305
+
306
+ file, lineno = file_and_line(exception)
277
307
 
278
- source = source(source_file)
308
+ return nil if file.empty?
309
+
310
+ source = source(file)
279
311
 
280
- radius = bredth # number of surrounding lines to show
281
- region = [source_line - radius, 1].max ..
282
- [source_line + radius, source.length].min
312
+ region = [lineno - radius, 1].max ..
313
+ [lineno + radius, source.length].min
283
314
 
284
315
  # ensure proper alignment by zero-padding line numbers
285
316
  format = " %2s %0#{region.last.to_s.length}d %s"
286
317
 
287
318
  pretty = region.map do |n|
288
- format % [('=>' if n == source_line), n, source[n-1].chomp]
319
+ format % [('=>' if n == lineno), n, source[n-1].chomp]
289
320
  end #.unshift "[#{region.inspect}] in #{source_file}"
290
321
 
291
322
  pretty
292
323
  end
293
324
 
325
+ # Return a structure code snippet in an array of lineno=>line
326
+ # hash elements.
327
+ #
328
+ # @param exception [Exception, String]
329
+ # An exception or backtrace.
330
+ #
331
+ # @param radius [Integer]
332
+ # The number of surrounding lines to show.
333
+ #
334
+ # @return [Hash] structured code snippet
335
+ def structured_code_snippet(exception, radius=2)
336
+ radius = radius.to_i
337
+
338
+ file, lineno = file_and_line(exception)
339
+
340
+ return {} if file.empty?
341
+
342
+ source = source(file)
343
+
344
+ region = [lineno - radius, 1].max ..
345
+ [lineno + radius, source.length].min
346
+
347
+ region.map do |n|
348
+ {n => source[n-1].chomp}
349
+ end
350
+ end
351
+
352
+ # Cache the source code of a file.
353
+ #
354
+ # @param file [String] full pathname to file
294
355
  #
356
+ # @return [String] source code
295
357
  def source(file)
296
358
  @source[file] ||= (
297
359
  File.readlines(file)
298
360
  )
299
361
  end
300
362
 
363
+ # @param exception [Exception,Array,String]
364
+ # An exception or backtrace.
365
+ #
366
+ #--
301
367
  # TODO: Show more of the file name than just the basename.
368
+ #++
302
369
  def file_and_line(exception)
303
- line = exception.backtrace[0]
304
- return "" unless line
305
- i = line.rindex(':in')
306
- line = i ? line[0...i] : line
307
- File.basename(line)
370
+ backtrace = case exception
371
+ when Exception
372
+ exception.backtrace.reject{ |bt| bt =~ INTERNALS }.first
373
+ when Array
374
+ exception.first
375
+ else
376
+ exception
377
+ end
378
+
379
+ backtrace =~ /(.+?):(\d+(?=:|\z))/ or return ""
380
+
381
+ file, lineno = $1, $2.to_i
382
+
383
+ return file, lineno
384
+
385
+ #i = backtrace.rindex(':in')
386
+ #line = i ? line[0...i] : line
387
+ #relative_file(line)
308
388
  end
309
389
 
390
+ # Same as file_and_line, exception return file path is relative.
391
+ def file_line(exception)
392
+ file, lineno = file_and_line(exception)
393
+ return relative_file(file), lineno
394
+ end
395
+
396
+ # Default trace count. This is the number of backtrace lines that
397
+ # will be provided on errors and failed assertions, unless otherwise
398
+ # overridden with ENV['trace'].
399
+ DEFAULT_TRACE_COUNT = 3
400
+
401
+ # Looks at ENV['trace'] to determine how much trace output to provide.
402
+ # If it is not set, or set to`false` or `off`, then the default trace count
403
+ # is used. If set to `0`, `true`, 'on' or 'all' then aa complete trace dump
404
+ # is provided. Otherwise the value is converted to an integer and that many
405
+ # line of trace is given.
406
+ #
407
+ # @return [Integer, nil] trace count
408
+ def trace_count
409
+ cnt = ENV['trace']
410
+ case cnt
411
+ when nil, 'false', 'off'
412
+ DEFAULT_TRACE_COUNT
413
+ when 0, 'all', 'true', 'on'
414
+ nil
415
+ else
416
+ Integer(cnt)
417
+ end
418
+ end
419
+
420
+ #
421
+ def relative_file(file)
422
+ pwd = Dir.pwd
423
+ idx = (0...pwd.size).find do |i|
424
+ file[i,1] != pwd[i,1]
425
+ end
426
+ file[(idx || 0)..-1]
427
+ end
310
428
  end
311
429
 
312
430
  end