qed 2.5.1 → 2.6.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.
@@ -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