qed 2.5.0 → 2.5.1

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/.ruby ADDED
@@ -0,0 +1,47 @@
1
+ ---
2
+ spec_version: 1.0.0
3
+ replaces: []
4
+
5
+ loadpath:
6
+ - lib
7
+ name: qed
8
+ repositories:
9
+ public: git://github.com/proutils/qed.git
10
+ conflicts: []
11
+
12
+ engine_check: []
13
+
14
+ title: QED
15
+ resources:
16
+ home: http://proutils.github.com/qed
17
+ work: http://github.com/proutils/qed
18
+ maintainers: []
19
+
20
+ requires:
21
+ - group: []
22
+
23
+ name: ansi
24
+ version: 0+
25
+ - group: []
26
+
27
+ name: facets
28
+ version: 2.8+
29
+ - group: []
30
+
31
+ name: ae
32
+ version: 0+
33
+ - group:
34
+ - build
35
+ name: syckle
36
+ version: 0+
37
+ manifest: Manifest
38
+ version: 2.5.1
39
+ licenses: []
40
+
41
+ copyright: Copyright (c) 2006 Thomas Sawyer
42
+ authors:
43
+ - Thomas Sawyer <transfire@gmail.com>
44
+ organization: RubyWorks
45
+ description: QED (Quality Ensured Demonstrations) is a TDD/BDD framework utilizing Literate Programming techniques.
46
+ summary: Quod Erat Demonstrandum
47
+ created: 2006-12-16
data/bin/qed CHANGED
@@ -1,4 +1,4 @@
1
1
  #!/usr/bin/env ruby
2
2
  require 'qed'
3
- QED.main(*ARGV)
3
+ QED.cli(*ARGV)
4
4
 
data/lib/qed/demo.rb CHANGED
@@ -17,19 +17,25 @@ module QED
17
17
  # Parser mode.
18
18
  attr :mode
19
19
 
20
+ # Working directory.
21
+ attr :cwd
22
+
20
23
  # Scope to run demonstration within. (Known as a "World" in Cucumber.)
21
24
  attr :scope
22
25
 
23
26
  # New Script
24
27
  def initialize(file, options={})
25
- @file = file
26
- @scope = options[:scope] || Scope.new(applique, file)
27
- @mode = options[:mode]
28
- @binding = @scope.__binding__
28
+ @file = file
29
+ @mode = options[:mode]
30
+ @cwd = options[:at] || fallback_cwd
31
+
32
+ @scope = options[:scope] || Scope.new(applique, cwd, file)
33
+
34
+ @binding = @scope.__binding__
29
35
  #apply_environment
30
36
  end
31
37
 
32
- # One binding per script.
38
+ # One binding per demo.
33
39
  def binding
34
40
  @binding #||= @scope.__binding__
35
41
  end
@@ -46,8 +52,8 @@ module QED
46
52
 
47
53
  #
48
54
  def evaluate(code, line)
49
- eval(code, @binding, @file, line)
50
- #@scope.module_eval(section.text, @file, section.line)
55
+ #eval(code, @binding, @file, line)
56
+ @scope.eval(code, @file, line)
51
57
  end
52
58
 
53
59
  # Returns a cached Array of Applique modules.
@@ -105,6 +111,11 @@ module QED
105
111
  # )
106
112
  #end
107
113
 
114
+ # This shouldn't be needed, but is here as a precaution.
115
+ def fallback_cwd
116
+ @dir ||= File.join(Dir.tmpdir, 'qed', File.filename(Dir.pwd), Time.new.strftime("%Y%m%d%H%M%S"))
117
+ end
118
+
108
119
  end
109
120
 
110
121
  end
data/lib/qed/evaluator.rb CHANGED
@@ -67,10 +67,14 @@ module QED
67
67
  advise!(:when, step) # triggers matchers
68
68
  rescue SystemExit
69
69
  pass!(step)
70
- rescue Assertion => exception
71
- fail!(step, exception)
70
+ #rescue Assertion => exception
71
+ # fail!(step, exception)
72
72
  rescue Exception => exception
73
- error!(step, exception)
73
+ if exception.assertion?
74
+ fail!(step, exception)
75
+ else
76
+ error!(step, exception)
77
+ end
74
78
  else
75
79
  pass!(step)
76
80
  end
@@ -78,7 +82,22 @@ module QED
78
82
 
79
83
  #
80
84
  def evaluate_data(step)
81
- advise!(:data, step)
85
+ #advise!(:data, step)
86
+ begin
87
+ advise!(:data, step)
88
+ rescue SystemExit
89
+ pass!(step)
90
+ #rescue Assertion => exception
91
+ # fail!(step, exception)
92
+ rescue Exception => exception
93
+ if exception.assertion?
94
+ fail!(step, exception)
95
+ else
96
+ error!(step, exception)
97
+ end
98
+ else
99
+ pass!(step)
100
+ end
82
101
  end
83
102
 
84
103
  # Evaluate a demo step.
@@ -87,11 +106,15 @@ module QED
87
106
  advise!(:code, step)
88
107
  @script.evaluate(step.code, step.lineno)
89
108
  rescue SystemExit
90
- pass!(step)
91
- rescue Assertion => exception
92
- fail!(step, exception)
109
+ pass!(step) # TODO: skip!(step)
110
+ #rescue Assertion => exception
111
+ # fail!(step, exception)
93
112
  rescue Exception => exception
94
- error!(step, exception)
113
+ if exception.assertion?
114
+ fail!(step, exception)
115
+ else
116
+ error!(step, exception)
117
+ end
95
118
  else
96
119
  pass!(step)
97
120
  end
@@ -189,7 +189,7 @@ module Reporter
189
189
 
190
190
  # After running a step that raised an error.
191
191
  def error(step, exception)
192
- raise exception if $DEBUG
192
+ raise exception if $DEBUG # TODO: do we really want to do it like this?
193
193
  #@error << [step, exception]
194
194
  end
195
195
 
@@ -237,8 +237,8 @@ module Reporter
237
237
  end
238
238
 
239
239
  def print_tally
240
- assert_count = Assertion.count
241
- assert_fails = Assertion.fails
240
+ assert_count = AE::Assertor.counts[:total]
241
+ assert_fails = AE::Assertor.counts[:fail]
242
242
  assert_delta = assert_count - assert_fails
243
243
 
244
244
  mask = "%s demos, %s steps: %s failures, %s errors (%s/%s assertions)"
@@ -253,7 +253,7 @@ module Reporter
253
253
  end
254
254
 
255
255
  #
256
- INTERNALS = /(lib|bin)[\\\/]qed/
256
+ INTERNALS = /(lib|bin)[\\\/](qed|ae)/
257
257
 
258
258
  =begin
259
259
  # Clean the backtrace of any reference to ko/ paths and code.
@@ -23,8 +23,8 @@ module Reporter #:nodoc:
23
23
 
24
24
  #
25
25
  def data(step)
26
- io.puts step.clean_text.ansi(:blue)
27
- io.puts
26
+ #io.puts step.clean_text.ansi(:blue)
27
+ #io.puts
28
28
  end
29
29
 
30
30
  #
@@ -34,6 +34,8 @@ module Reporter #:nodoc:
34
34
  io.print "#{step.text}".ansi(:green)
35
35
  elsif step.header?
36
36
  io.print "#{step.text}".ansi(:bold)
37
+ elsif step.data?
38
+ io.print "#{step.text}".ansi(:blue)
37
39
  else
38
40
  io.print "#{step.text}"
39
41
  end
@@ -48,7 +50,7 @@ module Reporter #:nodoc:
48
50
  msg = []
49
51
  #msg << ANSI::Code.bold(ANSI::Code.red("FAIL: ")) + error.message
50
52
  #msg << ANSI::Code.bold(clean_backtrace(error.backtrace[0]))
51
- msg << "FAIL: ".ansi(:bold, :red) + error.message #to_str
53
+ msg << "FAIL: ".ansi(:bold, :red) + error.message.to_s #to_str
52
54
  msg << clean_backtrace(error.backtrace[0]).ansi(:bold)
53
55
  io.puts msg.join("\n").tabto(tab||2)
54
56
  io.puts
data/lib/qed/scope.rb CHANGED
@@ -21,9 +21,10 @@ module QED
21
21
  # end
22
22
 
23
23
  #
24
- def initialize(applique, file=nil)
24
+ def initialize(applique, cwd, file=nil)
25
25
  super()
26
26
  @_applique = applique
27
+ @_cwd = cwd
27
28
  @_file = file
28
29
  #@loadlist = []
29
30
 
@@ -61,12 +62,12 @@ module QED
61
62
  end
62
63
 
63
64
  # Evaluate code in the context of the scope's special binding.
64
- def eval(code, binding=nil, file=nil)
65
- super(code, binding || __binding__, @_file)
65
+ # The return result of the evaluation is stored in `@_`.
66
+ def eval(code, file=nil, line=nil)
67
+ @_ = super(code, __binding__, @_file, line)
66
68
  end
67
69
 
68
70
 
69
-
70
71
  # Utilize is like #require, but will evaluate the script in the context
71
72
  # of the current scope.
72
73
  #--
@@ -89,7 +90,6 @@ module QED
89
90
  end
90
91
 
91
92
 
92
-
93
93
  # Define "when" advice.
94
94
  def When(*patterns, &procedure)
95
95
  patterns = patterns.map{ |pat| pat == :text ? :desc : pat }
@@ -113,7 +113,6 @@ module QED
113
113
  end
114
114
 
115
115
 
116
-
117
116
  # TODO: Should Table and Data be extensions that can be loaded if desired?
118
117
 
119
118
  # Use sample table to run steps. The table file will be
@@ -165,6 +164,23 @@ module QED
165
164
  #end
166
165
  end
167
166
 
167
+ # Clear temporary work directory.
168
+ def clear_working_directory!
169
+ dir = @_cwd
170
+ dir = File.expand_path(dir)
171
+
172
+ if dir == '/' or dir == File.expand_path('~')
173
+ abort "DANGER! Trying to use home or root as a temporary directory!"
174
+ end
175
+
176
+ entries = Dir.glob(File.join(dir, '**/*'))
177
+
178
+ dirs, files = entries.partition{ |f| File.directory?(f) }
179
+
180
+ files.each { |file| FileUtils.rm(file) }
181
+ dirs.each { |dir| FileUtils.rmdir(dir) }
182
+ end
183
+
168
184
  #
169
185
  def const_missing(const)
170
186
  Object.const_get(const)
data/lib/qed/session.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  module QED
2
2
 
3
- #require 'qed/config'
3
+ require 'qed/settings'
4
4
  require 'qed/demo'
5
5
 
6
6
  # The Session class encapsulates a set of demonstrations
@@ -9,27 +9,66 @@ module QED
9
9
  #
10
10
  class Session
11
11
 
12
- # Demonstration files.
13
- attr :files
12
+ # Default recognized demos file types.
13
+ DEMO_TYPES = %w{qed rdoc md markdown}
14
+
15
+ #
16
+ CODE_TYPES = %w{rb}
17
+
18
+ # Directory names to omit from automatic selection.
19
+ OMIT_PATHS = %w{applique helpers support sample samples fixture fixtures}
20
+
21
+ # Demonstration files (or globs).
22
+ attr_reader :files
23
+
24
+ # File patterns to omit.
25
+ attr_accessor :omit
14
26
 
15
27
  # Output format.
16
28
  attr_accessor :format
17
29
 
18
- # Trace mode
30
+ # Trace execution?
19
31
  attr_accessor :trace
20
32
 
21
- #
33
+ # Parse mode.
22
34
  attr_accessor :mode
23
35
 
36
+ # Paths to be added to $LOAD_PATH.
37
+ attr_reader :loadpath
38
+
39
+ # Libraries to be required.
40
+ attr_reader :requires
41
+
42
+ # Do not operate from project root?
43
+ attr_accessor :rootless
44
+
45
+ # Selected profile.
46
+ attr_accessor :profile
47
+
48
+ # Returns instance of Settings class.
49
+ attr :settings
50
+
24
51
  # New Session
25
52
  def initialize(files, options={})
26
53
  require_reporters
27
54
 
28
- @files = [files].flatten
55
+ @files = [files].flatten
56
+
57
+ @format = options[:format] || :dotprogress
58
+ @trace = options[:trace] || false
59
+ @mode = options[:mode] || nil
60
+ @profile = options[:profile] || :default
61
+ @loadpath = options[:loadpath] || []
62
+ @requires = options[:requires] || []
29
63
 
30
- @mode = options[:mode]
31
- @trace = options[:trace] || false
32
- @format = options[:format] || :dotprogress
64
+ @omit = OMIT_PATHS # TODO: eventually make configurable
65
+
66
+ @settings = Settings.new(options)
67
+ end
68
+
69
+ #
70
+ def directory
71
+ settings.tmpdir
33
72
  end
34
73
 
35
74
  # Top-level configuration.
@@ -53,8 +92,11 @@ module QED
53
92
  end
54
93
 
55
94
  # Returns an Array of Demo instances.
95
+ #--
96
+ # TODO: Pass settings to demo, so we can get temporary_dirctory.
97
+ #++
56
98
  def demos
57
- @demos ||= files.map{ |file| Demo.new(file, :mode=>mode) }
99
+ @demos ||= demo_files.map{ |file| Demo.new(file, :mode=>mode, :at=>directory) }
58
100
  end
59
101
 
60
102
  #
@@ -63,16 +105,94 @@ module QED
63
105
  end
64
106
 
65
107
  # Run session.
108
+ #--
109
+ # TODO: remove loadpath additions when done
110
+ #++
66
111
  def run
67
- #profile.before_session(self)
68
- reporter.before_session(self)
69
- demos.each do |demo|
70
- demo.run(*observers)
71
- #pid = fork { demo.run(*observers) }
72
- #Process.detach(pid)
112
+ abort "No documents." if demo_files.empty?
113
+
114
+ clear_directory
115
+
116
+ prepare_loadpath
117
+ require_libraries
118
+
119
+ require_profile # TODO: here or in chdir?
120
+
121
+ Dir.chdir(directory) do
122
+ #profile.before_session(self)
123
+ reporter.before_session(self)
124
+ demos.each do |demo|
125
+ demo.run(*observers)
126
+ #pid = fork { demo.run(*observers) }
127
+ #Process.detach(pid)
128
+ end
129
+ reporter.after_session(self)
130
+ #profile.after_session(self)
131
+ end
132
+ end
133
+
134
+ #
135
+ def clear_directory
136
+ settings.clear_directory
137
+ end
138
+
139
+ # Add to load path (from -I option).
140
+ def prepare_loadpath
141
+ loadpath.each{ |dir| $LOAD_PATH.unshift(dir) }
142
+ end
143
+
144
+ # Require libraries (from -r option).
145
+ def require_libraries
146
+ requires.each{ |file| require(file) }
147
+ end
148
+
149
+ #
150
+ def require_profile
151
+ settings.require_profile(profile)
152
+ end
153
+
154
+ # Returns a list of demo files. The files returned depends on the
155
+ # +files+ attribute and if none given, then the current run mode.
156
+ def demo_files
157
+ @demo_files ||= (
158
+ if mode == :comment
159
+ demos_in_comment_mode
160
+ else
161
+ demos_in_normal_mode
162
+ end
163
+ )
164
+ end
165
+
166
+ # Collect default files to process in normal demo mode.
167
+ def demos_in_normal_mode
168
+ demos_gather(DEMO_TYPES)
169
+ end
170
+
171
+ # Collect default files to process in code comment mode.
172
+ #
173
+ # TODO: Sure removing applique files is the best approach here?
174
+ def demos_in_comment_mode
175
+ files = demos_gather(CODE_TYPES)
176
+ files = files.reject{ |f| f.index('applique/') } # don't include applique files ???
177
+ files
178
+ end
179
+
180
+ # Gather list of demo files. Uses +omit+ to remove certain files
181
+ # based on the name of their parent directory.
182
+ def demos_gather(extensions=DEMO_TYPES)
183
+ files = self.files
184
+ #files << default_location if files.empty?
185
+ files = files.map{|pattern| Dir[pattern]}.flatten.uniq
186
+ files = files.map do |file|
187
+ if File.directory?(file)
188
+ Dir[File.join(file,'**','*.{' + extensions.join(',') + '}')]
189
+ else
190
+ file
191
+ end
73
192
  end
74
- reporter.after_session(self)
75
- #profile.after_session(self)
193
+ files = files.flatten.uniq
194
+ files = files.reject{ |f| f =~ Regexp.new('\/'+omit.join('|')+'\/') }
195
+ files.map{|f| File.expand_path(f) }.uniq.sort
76
196
  end
77
197
 
78
198
  # Globally applicable advice.
@@ -82,6 +202,108 @@ module QED
82
202
  # end
83
203
  #end
84
204
 
205
+ #
206
+ def self.cli(*argv)
207
+ require 'optparse'
208
+ require 'shellwords'
209
+
210
+ files, options = cli_parse(argv)
211
+
212
+ if files.empty?
213
+ puts "No files."
214
+ puts options_parser
215
+ exit -1
216
+ end
217
+
218
+ session = new(files, options)
219
+ session.run
220
+ end
221
+
222
+ # Instance of OptionParser
223
+ def self.cli_parse(argv)
224
+ options = {}
225
+ options_parser = OptionParser.new do |opt|
226
+ opt.banner = "Usage: qed [options] <files...>"
227
+
228
+ opt.separator("Custom Profiles:") unless settings.profiles.empty?
229
+
230
+ settings.profiles.each do |name, value|
231
+ o = "--#{name}"
232
+ opt.on(o, "#{name} custom profile") do
233
+ options[:profile] = name.to_sym
234
+ end
235
+ end
236
+
237
+ opt.separator("Report Formats (pick one):")
238
+ opt.on('--dotprogress', '-d', "use dot-progress reporter [default]") do
239
+ options[:format] = :dotprogress
240
+ end
241
+ opt.on('--verbatim', '-v', "use verbatim reporter") do
242
+ options[:format] = :verbatim
243
+ end
244
+ opt.on('--bullet', '-b', "use bullet-point reporter") do
245
+ options[:format] = :bullet
246
+ end
247
+ opt.on('--html', '-h', "use underlying HTML reporter") do
248
+ options[:format] = :html
249
+ end
250
+ #opt.on('--script', "psuedo-reporter") do
251
+ # options[:format] = :script # psuedo-reporter
252
+ #end
253
+ opt.on('--format', '-f FORMAT', "use custom reporter") do |format|
254
+ options[:format] = format.to_sym
255
+ end
256
+
257
+ opt.separator("Control Options:")
258
+ opt.on('--comment', '-c', "run comment code") do
259
+ options[:mode] = :comment
260
+ end
261
+ opt.on('--profile', '-p NAME', "load runtime profile") do |name|
262
+ options[:profile] = name
263
+ end
264
+ opt.on('--loadpath', "-I PATH", "add paths to $LOAD_PATH") do |paths|
265
+ options[:loadpath] = paths.split(/[:;]/).map{|d| File.expand_path(d)}
266
+ end
267
+ opt.on('--require', "-r LIB", "require library") do |paths|
268
+ options[:requires] = paths.split(/[:;]/)
269
+ end
270
+ opt.on('--rootless', '-R', "run from system-wide temporary directory") do
271
+ options[:rootless] = true
272
+ end
273
+ opt.on('--trace', '-t', "show full backtraces for exceptions") do
274
+ options[:trace] = true
275
+ end
276
+ opt.on('--warn', "run with warnings turned on") do
277
+ $VERBOSE = true # wish this were called $WARN!
278
+ end
279
+ opt.on('--debug', "exit immediately upon raised exception") do
280
+ $DEBUG = true
281
+ end
282
+
283
+ opt.separator("Optional Commands:")
284
+ opt.on_tail('--version', "display version") do
285
+ puts "QED #{QED::VERSION}"
286
+ exit
287
+ end
288
+ opt.on_tail('--copyright', "display copyrights") do
289
+ puts "Copyright (c) 2008 Thomas Sawyer, Apache 2.0 License"
290
+ exit
291
+ end
292
+ opt.on_tail('--help', '-h', "display this help message") do
293
+ puts opt
294
+ exit
295
+ end
296
+ end
297
+ options_parser.parse!(argv)
298
+ return argv, options
299
+ end
300
+
301
+ # TODO: Pass to Session class, instead of acting global.
302
+ # It is used at the class level to get profiles for the cli.
303
+ def self.settings
304
+ @settings ||= Settings.new
305
+ end
306
+
85
307
  end#class Session
86
308
 
87
309
  end#module QED