qed 2.4.0 → 2.5.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/History.rdoc CHANGED
@@ -1,5 +1,21 @@
1
1
  = RELEASE HISTORY
2
2
 
3
+ == 2.5.0 / 2010-11-04
4
+
5
+ The latest release of QED improves on applique loading, such that each
6
+ demonstrandum gets it's own localized set. The CLI has also been modified
7
+ so that there is no longer a defualt location, the directory or files to run
8
+ must be specified.
9
+
10
+ Changes:
11
+
12
+ * Better handling of Applique.
13
+ * Remove Advice class --advice is now stored in Applique.
14
+ * Each applique file is it's own module.
15
+ * Advice from each applique is applied.
16
+ * CLI requires files be specified.
17
+
18
+
3
19
  == 2.4.0 / 2010-09-02
4
20
 
5
21
  All engines go! QED has not been tested against 1.8.6, 1.8.7 and 1.9.2.
@@ -141,7 +157,7 @@ Changes:
141
157
  * New system of version numbers.
142
158
 
143
159
 
144
- == 1.2 / 2009-12-07
160
+ == 1.2.0 / 2009-12-07
145
161
 
146
162
  This release adds a significant new feature, Comment Matchers.
147
163
  These work like Cucumber allowing for background code to
@@ -161,7 +177,7 @@ Changes:
161
177
  * Verbatim reporter is literally verbatim.
162
178
 
163
179
 
164
- == 1.1 / 2009-09-05
180
+ == 1.1.1 / 2009-09-05
165
181
 
166
182
  This release
167
183
 
@@ -178,7 +194,7 @@ Changes:
178
194
  * Use latest RDoc version.
179
195
 
180
196
 
181
- == 1.0 / 2009-06-30
197
+ == 1.0.0 / 2009-06-30
182
198
 
183
199
  QED has found itself. It took some time to really figure out
184
200
  what this project "was" and how it should best be utilized.
data/README.rdoc CHANGED
@@ -1,9 +1,5 @@
1
1
  = Ruby Q.E.D.
2
2
 
3
- homepage: http://proutils.github.com/qed
4
- mailing list: http://groups.google.com/group/proutils
5
- development: http://github.com/proutils/qed
6
-
7
3
 
8
4
  == Introduction
9
5
 
@@ -30,6 +26,13 @@ of abstract, from unit test to systems tests.
30
26
  * Documentation tool provides nice output with jQuery-based TOC.
31
27
 
32
28
 
29
+ == Resources
30
+
31
+ * homepage: http://rubyworks.github.com/qed
32
+ * mailing list: http://groups.google.com/group/rubyworks
33
+ * development: http://github.com/rubyworks/qed
34
+
35
+
33
36
  == Synopsis
34
37
 
35
38
  === Assertion Syntax
@@ -43,7 +46,7 @@ In this example, because 4 != 5, this expression will raise an Assertion
43
46
  exception. QED's Runner class is thus just a means of running and capturing
44
47
  code blocks containing these assertions.
45
48
 
46
- You can learn more about AE at http://proutils.github.com/ae.
49
+ You can learn more about AE at http://rubyworks.github.com/ae.
47
50
 
48
51
  === Document Structure
49
52
 
data/lib/qed/advice.rb CHANGED
@@ -1,13 +1,14 @@
1
- require 'qed/core_ext/instance_exec'
1
+ raise "no needed any more"
2
+
3
+ require 'qed/core_ext'
2
4
 
3
5
  module QED
4
6
 
5
7
  # = Advice
6
8
  #
7
- # This class tracks advice defined by demonstrandum
8
- # and applique. It is instantiated in Scope, so that
9
- # the advice methods will have access to the same
10
- # local binding as the scripts themselves.
9
+ # This class tracks advice defined by demonstrandum and applique.
10
+ # Advice are evaluated in Scope, so that they will have access
11
+ # to the same local binding as the scripts themselves.
11
12
  #
12
13
  # There are two types of advice: *pattern matchers*
13
14
  # and *event signals*.
@@ -36,13 +37,18 @@ module QED
36
37
  @signals = [{}]
37
38
  end
38
39
 
40
+ #
41
+ #def initialize_copy(other)
42
+ # @matchers = other.matchers.dup
43
+ # @signals = other.signals.dup
44
+ #end
45
+
39
46
  #
40
47
  def call(scope, type, *args)
41
48
  case type
42
49
  when :when
43
50
  call_matchers(scope, *args)
44
51
  else
45
- #@events.call(scope, type, *args)
46
52
  call_signals(scope, type, *args)
47
53
  end
48
54
  end
@@ -122,7 +128,7 @@ module QED
122
128
  # contains double parenthesis, such as ((.*?)), then the text within
123
129
  # them is treated as in regular expression and kept verbatium.
124
130
  #
125
- # TODO: Better way to isolate regexp. Maybe "?:(.*?)".
131
+ # TODO: Better way to isolate regexp. Maybe ?:(.*?) or /(.*?)/.
126
132
  #
127
133
  # TODO: Now that we can use multi-patterns, do we still need this?
128
134
  #
data/lib/qed/applique.rb CHANGED
@@ -1,62 +1,88 @@
1
- require 'qed/advice'
2
-
3
1
  module QED
4
2
 
5
- # The Applique is the environment of libraries required by
6
- # and the rules to apply to demonstrandum. The applique is
7
- # defined by a set of scripts located in the +applique+
8
- # directory of the upper-most test directory relative to
9
- # the tests run and below the root of a project. All
10
- # applique scripts are loaded at the start of a test
11
- # session. Thus all demos belong to one and only one
12
- # applique, and all the scripts in an applique must be
13
- # compatible/consistant. For two demos to have separate
14
- # applique they must be kept in separate directores.
15
-
3
+ # Applique is a module built per-script from the +applique+ dirctory.
4
+ # Applique scripts are loaded at the start of a session.
5
+ #
6
+ # <i>The Applique</i> is whole collection of applique that apply to given
7
+ # demonstrandum. The applique that apply are the scripts located in the
8
+ # directory relative to the demonstrandum script and all such directories
9
+ # above this upto and the project's root directory.
10
+ #
11
+ # All scripts in the Applique must be compatible/consistant. For two demos to
12
+ # have separate applique must be kept in separate directores.
13
+ #
16
14
  class Applique < Module
17
15
 
16
+ # Load cache.
17
+ def self.cache
18
+ @cache ||= {}
19
+ end
20
+
21
+ class << self
22
+ alias_method :_new, :new
23
+ end
24
+
25
+ # New method caches Applique based-on +file+, if given.
26
+ #--
27
+ # TODO: may need to expand file to be absolute path
28
+ #++
29
+ def self.new(file=nil)
30
+ if file
31
+ cache[file] ||= _new(file)
32
+ else
33
+ _new(file)
34
+ end
35
+ end
36
+
18
37
  #
19
- def initialize
38
+ def initialize(file=nil)
20
39
  super()
21
40
  extend self
22
- @__advice__ = Advice.new
41
+
42
+ @__matchers__ = []
43
+ @__signals__ = {}
44
+
45
+ if file
46
+ @file = file
47
+ module_eval(File.read(file), file)
48
+ end
23
49
  end
24
50
 
25
51
  #
26
52
  def initialize_copy(other)
27
- @__advice__ = other.__advice__.dup
53
+ @__matchers__ = other.__matchers__.dup
54
+ @__signals__ = other.__signals__.dup
28
55
  end
29
56
 
30
- # Redirect missing constants to Object class
31
- # to simulate TOPLEVEL.
32
- #
33
- # TODO: Clean backtrace when constant is not found.
34
- def const_missing(name)
35
- Object.const_get(name)
36
- end
57
+ # Array of matchers.
58
+ attr :__matchers__
37
59
 
38
- #
39
- def __advice__
40
- @__advice__
41
- end
60
+ # Hash of signals.
61
+ attr :__signals__
42
62
 
43
63
  # Pattern matchers and "upon" events.
44
64
  def When(*patterns, &procedure)
45
65
  if patterns.size == 1 && Symbol === patterns.first
46
- __advice__.add_event(patterns.first, &procedure)
66
+ type = "#{patterns.first}".to_sym
67
+ @__signals__[type] = procedure
68
+ #define_method(type, &procedure)
47
69
  else
48
- __advice__.add_match(patterns, &procedure)
70
+ @__matchers__ << [patterns, procedure]
49
71
  end
50
72
  end
51
73
 
52
74
  # Before advice.
53
75
  def Before(type=:code, &procedure)
54
- __advice__.add_event(:"before_#{type}", &procedure)
76
+ type = "before_#{type}".to_sym
77
+ @__signals__[type] = procedure
78
+ #define_method(type, &procedure)
55
79
  end
56
80
 
57
81
  # After advice.
58
82
  def After(type=:code, &procedure)
59
- __advice__.add_event(:"after_#{type}", &procedure)
83
+ type = "after_#{type}".to_sym
84
+ @__signals__[type] = procedure
85
+ #define_method(type, &procedure)
60
86
  end
61
87
 
62
88
  # Code match-and-transform procedure.
@@ -77,6 +103,14 @@ module QED
77
103
  #
78
104
  #end
79
105
 
106
+ # Redirect missing constants to Object class
107
+ # to simulate TOPLEVEL.
108
+ #
109
+ # TODO: Clean backtrace when constant is not found.
110
+ def const_missing(name)
111
+ Object.const_get(name)
112
+ end
113
+
80
114
  end
81
115
 
82
116
  end
data/lib/qed/command.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require 'optparse'
2
2
  require 'shellwords'
3
+ require 'fileutils'
3
4
 
4
5
  module QED
5
6
 
@@ -7,7 +8,7 @@ module QED
7
8
  Command.main(*argv)
8
9
  end
9
10
 
10
- # = QED Commandline Tool
11
+ # QED Command-line tool.
11
12
  #
12
13
  # TODO: Merge Command with Session ?
13
14
  class Command
@@ -19,18 +20,30 @@ module QED
19
20
  # scenarios.
20
21
  CONFIG_PATTERN = "{.,.config/,config/}qed"
21
22
 
22
- # Default location of demonstrations if no specific files
23
- # or locations given. This is use in Dir.glob. The default
24
- # locations are qed/, demo/ or demos/, searched for in that
25
- # order relative to the root directory.
26
- DEMO_LOCATION = '{qed,demo,demos}'
23
+ ## Default location of demonstrations if no specific files
24
+ ## or locations given. This is use in Dir.glob. The default
25
+ ## locations are qed/, demo/ or demos/, searched for in that
26
+ ## order relative to the root directory.
27
+ ##--
28
+ ## TODO: deprecate this
29
+ ##++
30
+ ##DEMO_LOCATION = '{qed,demo,demos}'
27
31
 
28
32
  # Glob pattern used to search for project's root directory.
29
- ROOT_PATTERN = '{.root,.git,.hg,_darcs}/'
33
+ ROOT_PATTERN = '{.ruby,.git/,.hg/,_darcs/,.qed/,.config/qed/,config/qed/}'
34
+
35
+ # Directory names to omit from automatic selection.
36
+ OMIT_PATHS = %w{applique helpers support sample samples fixture fixtures}
30
37
 
31
38
  # Home directory.
32
39
  HOME = File.expand_path('~')
33
40
 
41
+ # Default recognized demos file types.
42
+ DEMO_TYPES = %w{qed rdoc md markdown}
43
+
44
+ #
45
+ CODE_TYPES = %w{rb}
46
+
34
47
  # Instantiate a new Command object and call #execute.
35
48
  def self.main(*argv)
36
49
  new.execute(argv)
@@ -51,10 +64,10 @@ module QED
51
64
  attr :profile
52
65
 
53
66
  # Command-line options.
54
- attr :options
67
+ #attr :options
55
68
 
56
69
  # Files to be run.
57
- attr :files
70
+ attr_reader :files
58
71
 
59
72
  # Ensure files are in a flat list.
60
73
  def files=(globs)
@@ -67,83 +80,84 @@ module QED
67
80
  # Libraries to be required.
68
81
  attr_accessor :requires
69
82
 
70
- # ?
71
- attr_accessor :extension
72
-
73
83
  # Move to root directory?
74
84
  attr_accessor :root
75
85
 
76
86
  # Parse mode.
77
87
  attr_accessor :mode
78
88
 
89
+ #
90
+ attr_accessor :omit
91
+
79
92
  #
80
93
  # TODO: Should extension and profile have a common reference?
81
94
  def initialize
82
95
  @format = :dotprogress
83
- @extension = :default
96
+ #@extension = :default
84
97
  @profile = :default
85
98
  @requires = []
86
99
  @loadpath = []
87
100
  @files = []
88
- @options = {}
101
+ #@options = {}
102
+
103
+ @omit = OMIT_PATHS
89
104
  end
90
105
 
91
106
  # Instance of OptionParser
92
107
  def opts
93
108
  @opts ||= OptionParser.new do |opt|
109
+ opt.banner = "Usage: qed [options] <files...>"
94
110
 
95
111
  opt.separator("Custom Profiles:") unless profiles.empty?
96
112
 
97
113
  profiles.each do |name, value|
98
114
  o = "--#{name}"
99
115
  opt.on(o, "#{name} custom profile") do
100
- @profile = name
116
+ self.profile = name
101
117
  end
102
118
  end
103
119
 
104
120
  opt.separator("Report Formats (pick one):")
105
121
  opt.on('--dotprogress', '-d', "use dot-progress reporter [default]") do
106
- @options[:format] = :dotprogress
122
+ self.format = :dotprogress
107
123
  end
108
124
  opt.on('--verbatim', '-v', "use verbatim reporter") do
109
- @options[:format] = :verbatim
125
+ self.format = :verbatim
110
126
  end
111
127
  opt.on('--bullet', '-b', "use bullet-point reporter") do
112
- @options[:format] = :bullet
128
+ self.format = :bullet
113
129
  end
114
130
  opt.on('--html', '-h', "use underlying HTML reporter") do
115
- @options[:format] = :html
116
- end
117
- opt.on('--format', '-f FORMAT', "use custom reporter") do |format|
118
- @options[:format] = format
131
+ self.format = :html
119
132
  end
120
133
  #opt.on('--script', "psuedo-reporter") do
121
- # @options[:format] = :script # psuedo-reporter
134
+ # self.format = :script # psuedo-reporter
122
135
  #end
136
+ opt.on('--format', '-f FORMAT', "use custom reporter") do |format|
137
+ self.format = format
138
+ end
123
139
  opt.separator("Control Options:")
124
- opt.on('--root', '-R', "run command from project's root directory") do
125
- @options[:root] = true
140
+ opt.on('--root', '-R', "run from alternate directory") do |path|
141
+ self.root = path
126
142
  end
127
143
  opt.on('--comment', '-c', "Run comment code.") do
128
- @options[:mode] = :comment
144
+ self.mode = :comment
129
145
  end
130
- opt.on('--ext', '-e NAME', "runtime extension [default]") do |name|
131
- @options[:extension] = name
146
+ opt.on('--profile', '-p NAME', "load runtime profile") do |name|
147
+ self.profile = name
132
148
  end
133
- opt.on('--loadpath', "-I PATH", "add paths to $LOAD_PATH") do |arg|
134
- @options[:loadpath] ||= []
135
- @options[:loadpath].concat(arg.split(/[:;]/).map{ |dir| File.expand_path(dir) })
149
+ opt.on('--loadpath', "-I PATH", "add paths to $LOAD_PATH") do |paths|
150
+ self.loadpath = paths.split(/[:;]/).map{|d| File.expand_path(d)}
136
151
  end
137
- opt.on('--require', "-r LIB", "require library") do |arg|
138
- @options[:requires] ||= []
139
- @options[:requires].concat(arg.split(/[:;]/)) #.map{ |dir| File.expand_path(dir) })
152
+ opt.on('--require', "-r LIB", "require library") do |paths|
153
+ self.requires = paths.split(/[:;]/)
140
154
  end
141
155
  opt.on('--trace', '-t', "show full backtraces for exceptions") do
142
- @options[:trace] = true
156
+ self.trace = true
143
157
  end
144
158
  opt.on('--debug', "exit immediately upon raised exception") do
145
159
  $VERBOSE = true # wish this were called $WARN
146
- $DEBUG = true
160
+ $DEBUG = true
147
161
  end
148
162
  opt.separator("Optional Commands:")
149
163
  opt.on_tail('--version', "display version") do
@@ -151,7 +165,7 @@ module QED
151
165
  exit
152
166
  end
153
167
  opt.on_tail('--copyright', "display copyrights") do
154
- puts "Copyright (c) 2008, 2009 Thomas Sawyer, GPL License"
168
+ puts "Copyright (c) 2008 Thomas Sawyer, Apache 2.0 License"
155
169
  exit
156
170
  end
157
171
  opt.on_tail('--help', '-h', "display this help message") do
@@ -161,10 +175,6 @@ module QED
161
175
  end
162
176
  end
163
177
 
164
- # Default recognized demos file types.
165
- DEMO_TYPES = %w{qed rdoc md markdown}
166
- CODE_TYPES = %w{rb}
167
-
168
178
  # Returns a list of demo files. The files returned depends on the
169
179
  # +files+ attribute and if none given, then the current run mode.
170
180
  def demos
@@ -179,25 +189,23 @@ module QED
179
189
 
180
190
  # Collect default files to process in normal demo mode.
181
191
  def demos_in_normal_mode
182
- demos_gather(DEMO_LOCATION, DEMO_TYPES)
192
+ demos_gather(DEMO_TYPES)
183
193
  end
184
194
 
185
195
  # Collect default files to process in code comment mode.
186
196
  #
187
- # TODO: Sure removing applique files is the best approach?
188
- #
189
- # TODO: Either add environment alond with applique or deprecate environment
190
- # as an alternate name.
197
+ # TODO: Sure removing applique files is the best approach here?
191
198
  def demos_in_comment_mode
192
- files = demos_gather('lib', CODE_TYPES)
199
+ files = demos_gather(CODE_TYPES)
193
200
  files = files.reject{ |f| f.index('applique/') } # don't include applique files ???
194
201
  files
195
202
  end
196
203
 
197
- #
198
- def demos_gather(default_location, extensions=DEMO_TYPES)
204
+ # Gather list of demo files. Uses +omit+ to remove certain files
205
+ # based on the name of their parent directory.
206
+ def demos_gather(extensions=DEMO_TYPES)
199
207
  files = self.files
200
- files << default_location if files.empty?
208
+ #files << default_location if files.empty?
201
209
  files = files.map{|pattern| Dir[pattern]}.flatten.uniq
202
210
  files = files.map do |file|
203
211
  if File.directory?(file)
@@ -207,6 +215,7 @@ module QED
207
215
  end
208
216
  end
209
217
  files = files.flatten.uniq
218
+ files = files.reject{ |f| f =~ Regexp.new('\/'+omit.join('|')+'\/') }
210
219
  files.map{|f| File.expand_path(f) }.uniq.sort
211
220
  end
212
221
 
@@ -217,33 +226,39 @@ module QED
217
226
  #@files.concat(argv)
218
227
  @files = argv
219
228
 
220
- #if profile
221
- if args = profiles[profile]
222
- argv = Shellwords.shellwords(args)
223
- opts.parse!(argv)
224
- @files.concat(argv)
229
+ if @files.empty?
230
+ puts "No files."
231
+ puts opts
232
+ exit
225
233
  end
234
+
235
+ #if profile
236
+ #if args = profiles[profile]
237
+ # argv = Shellwords.shellwords(args)
238
+ # opts.parse!(argv)
239
+ # @files.concat(argv)
240
+ #end
226
241
  #end
227
242
 
228
- options.each do |k,v|
229
- __send__("#{k}=", v)
230
- end
243
+ #options.each do |k,v|
244
+ # __send__("#{k}=", v)
245
+ #end
231
246
  end
232
247
 
233
248
  # Run demonstrations.
234
249
  def execute(argv)
235
250
  parse(argv)
236
251
 
237
- jump = @options[:root] ? root_directory : Dir.pwd
252
+ abort "No documents." if demos.empty?
238
253
 
239
- Dir.chdir(jump) do
240
- abort "No documents." if demos.empty?
254
+ prepare_loadpath
255
+ require_libraries
241
256
 
242
- prepare_loadpath
257
+ require_profile # TODO: here or in chdir?
243
258
 
244
- require_libraries
245
- require_profile
259
+ jump = root || temporary_directory
246
260
 
261
+ Dir.chdir(jump) do
247
262
  session.run
248
263
  end
249
264
  end
@@ -266,8 +281,10 @@ module QED
266
281
  # Profile configurations.
267
282
  def profiles
268
283
  @profiles ||= (
269
- file = Dir["#{config_directory}/profile{,s}.{yml,yaml}"].first
270
- file ? YAML.load(File.new(file)) : {}
284
+ files = Dir["#{config_directory}/*.rb"]
285
+ files.map do |file|
286
+ File.basename(file).chomp('.rb')
287
+ end
271
288
  )
272
289
  end
273
290
 
@@ -284,19 +301,31 @@ module QED
284
301
  # Require requirement file (from -e option).
285
302
  def require_profile
286
303
  return unless config_directory
287
- if file = Dir["#{config_directory}/#{extension}.rb"].first
304
+ if file = Dir["#{config_directory}/#{profile}.rb"].first
288
305
  require(file)
289
306
  end
290
307
  end
291
308
 
309
+ #
310
+ def temporary_directory
311
+ @temporary_directory ||= (
312
+ dir = File.join(root_directory, 'tmp', 'qed')
313
+ FileUtils.mkdir_p(dir)
314
+ dir
315
+ )
316
+ end
317
+
292
318
  # Locate project's root directory. This is done by searching upward
293
319
  # in the file heirarchy for the existence of one of the following
294
320
  # path names, each group being tried in turn.
295
321
  #
296
- # * .root/
297
322
  # * .git/
298
323
  # * .hg/
299
324
  # * _darcs/
325
+ # * .config/qed/
326
+ # * config/qed/
327
+ # * .qed/
328
+ # * .ruby
300
329
  #
301
330
  # Failing to find any of these locations, resort to the fallback:
302
331
  #
@@ -318,19 +347,26 @@ module QED
318
347
  root = lookup('lib/', path)
319
348
  return root if root
320
349
 
321
- abort "Failed to resolve project's root location. Try adding a .root directory."
350
+ abort "QED failed to resolve project's root location.\n" +
351
+ "QED looks for following entries to identify the root:\n" +
352
+ " .config/qed/\n" +
353
+ " config/qed/\n" +
354
+ " .qed/\n" +
355
+ " .ruby\n" +
356
+ " lib/\n" +
357
+ "Please add one of them to your project to proceed."
322
358
  end
323
359
 
324
360
  # Locate configuration directory by seaching up the
325
361
  # file hierachy relative to the working directory
326
362
  # for one of the following paths:
327
363
  #
328
- # * .qed/
329
364
  # * .config/qed/
330
365
  # * config/qed/
366
+ # * .qed/
331
367
  #
332
368
  def find_config
333
- lookup(CONFIG_PATTERN)
369
+ Dir[File.join(root_directory,CONFIG_PATTERN)].first
334
370
  end
335
371
 
336
372
  # Lookup path +glob+, searching each higher directory