qed 2.4.0 → 2.5.0

Sign up to get free protection for your applications and to get access to all the features.
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