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 +19 -3
- data/README.rdoc +8 -5
- data/lib/qed/advice.rb +13 -7
- data/lib/qed/applique.rb +65 -31
- data/lib/qed/command.rb +107 -71
- data/lib/qed/{core_ext/instance_exec.rb → core_ext.rb} +2 -0
- data/lib/qed/demo.rb +49 -34
- data/lib/qed/evaluator.rb +110 -3
- data/lib/qed/helpers/file_fixtures.rb +35 -0
- data/lib/qed/{extensions → helpers}/shell_session.rb +0 -0
- data/lib/qed/meta/data.rb +8 -10
- data/lib/qed/meta/{gemfile → package} +3 -3
- data/lib/qed/reporter/abstract.rb +5 -1
- data/lib/qed/reporter/verbatim.rb +3 -3
- data/lib/qed/scope.rb +56 -14
- data/lib/qed/session.rb +4 -62
- data/lib/qedoc/document.rb +33 -32
- data/lib/qedoc/document/markup.rb +46 -41
- data/lib/qedoc/document/template.rhtml +33 -33
- data/meta/data.rb +8 -10
- data/meta/{gemfile → package} +3 -3
- data/qed/04_samples.rdoc +3 -3
- metadata +10 -12
- data/Diary.rdoc +0 -117
- data/lib/qed/config.rb +0 -60
- data/lib/qed/extensions/filefixtures.rb +0 -27
data/lib/qed/demo.rb
CHANGED
@@ -1,35 +1,31 @@
|
|
1
1
|
module QED
|
2
|
-
require 'yaml'
|
3
2
|
|
4
|
-
require '
|
3
|
+
require 'yaml'
|
5
4
|
|
5
|
+
require 'qed/core_ext'
|
6
6
|
require 'qed/parser'
|
7
7
|
require 'qed/evaluator'
|
8
|
+
require 'qed/applique'
|
8
9
|
|
9
|
-
#
|
10
|
+
# The Demo class ecapsulates a demonstration document.
|
10
11
|
#
|
11
12
|
class Demo
|
12
13
|
|
13
|
-
#
|
14
|
-
attr :applique
|
15
|
-
|
16
14
|
# Demonstrandum file.
|
17
15
|
attr :file
|
18
16
|
|
19
|
-
#
|
17
|
+
# Parser mode.
|
20
18
|
attr :mode
|
21
19
|
|
22
|
-
#
|
20
|
+
# Scope to run demonstration within. (Known as a "World" in Cucumber.)
|
23
21
|
attr :scope
|
24
22
|
|
25
23
|
# New Script
|
26
|
-
def initialize(file,
|
24
|
+
def initialize(file, options={})
|
27
25
|
@file = file
|
28
|
-
@applique = applique.dup # localize copy of applique
|
29
26
|
@scope = options[:scope] || Scope.new(applique, file)
|
30
27
|
@mode = options[:mode]
|
31
28
|
@binding = @scope.__binding__
|
32
|
-
#@loadlist = []
|
33
29
|
#apply_environment
|
34
30
|
end
|
35
31
|
|
@@ -38,17 +34,6 @@ module QED
|
|
38
34
|
@binding #||= @scope.__binding__
|
39
35
|
end
|
40
36
|
|
41
|
-
# TODO: demo advice vs. applique advice
|
42
|
-
def advice
|
43
|
-
#@scope.__advice__
|
44
|
-
@applique.__advice__
|
45
|
-
end
|
46
|
-
|
47
|
-
#
|
48
|
-
def advise(signal, *args)
|
49
|
-
advice.call(@scope, signal, *args)
|
50
|
-
end
|
51
|
-
|
52
37
|
# Expanded dirname of +file+.
|
53
38
|
def directory
|
54
39
|
@directory ||= File.expand_path(File.dirname(file))
|
@@ -65,6 +50,48 @@ module QED
|
|
65
50
|
#@scope.module_eval(section.text, @file, section.line)
|
66
51
|
end
|
67
52
|
|
53
|
+
# Returns a cached Array of Applique modules.
|
54
|
+
def applique
|
55
|
+
@applique ||= (
|
56
|
+
list = [Applique.new]
|
57
|
+
applique_locations.each do |location|
|
58
|
+
Dir[location + '/**/*.rb'].each do |file|
|
59
|
+
list << Applique.new(file)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
list
|
63
|
+
)
|
64
|
+
end
|
65
|
+
|
66
|
+
# Returns a list of applique directories to be used by this
|
67
|
+
# demonstrastion.
|
68
|
+
def applique_locations
|
69
|
+
@applique_locations ||= (
|
70
|
+
locations = []
|
71
|
+
Dir.ascend(File.dirname(file)) do |path|
|
72
|
+
break if path == Dir.pwd
|
73
|
+
dir = File.join(path, 'applique')
|
74
|
+
if File.directory?(dir)
|
75
|
+
locations << dir
|
76
|
+
end
|
77
|
+
end
|
78
|
+
locations
|
79
|
+
)
|
80
|
+
end
|
81
|
+
|
82
|
+
# Parse demonstration script.
|
83
|
+
#
|
84
|
+
# Returns an abstract syntax tree.
|
85
|
+
def parse
|
86
|
+
Parser.new(file, :mode=>mode).parse
|
87
|
+
end
|
88
|
+
|
89
|
+
#
|
90
|
+
def run(*observers)
|
91
|
+
evaluator = Evaluator.new(self, *observers)
|
92
|
+
evaluator.run
|
93
|
+
end
|
94
|
+
|
68
95
|
#
|
69
96
|
#def source
|
70
97
|
# @source ||= (
|
@@ -78,18 +105,6 @@ module QED
|
|
78
105
|
# )
|
79
106
|
#end
|
80
107
|
|
81
|
-
# Parse script.
|
82
|
-
# Retruns an abstract syntax tree.
|
83
|
-
def parse
|
84
|
-
Parser.new(file, :mode=>mode).parse
|
85
|
-
end
|
86
|
-
|
87
|
-
#
|
88
|
-
def run(*observers)
|
89
|
-
evaluator = Evaluator.new(self, *observers)
|
90
|
-
evaluator.run
|
91
|
-
end
|
92
|
-
|
93
108
|
end
|
94
109
|
|
95
110
|
end
|
data/lib/qed/evaluator.rb
CHANGED
@@ -98,6 +98,7 @@ module QED
|
|
98
98
|
end
|
99
99
|
|
100
100
|
# TODO: Not sure how to handle loading links in --comment runner mode.
|
101
|
+
# TODO: Do not think Scope should be reuseud by imported demo.
|
101
102
|
def evaluate_links(step)
|
102
103
|
step.text.scan(/\[qed:\/\/(.*?)\]/) do |match|
|
103
104
|
file = $1
|
@@ -110,7 +111,7 @@ module QED
|
|
110
111
|
when '.rb'
|
111
112
|
import!(file)
|
112
113
|
else
|
113
|
-
Demo.new(file,
|
114
|
+
Demo.new(file, :scope=>@script.scope).run
|
114
115
|
end
|
115
116
|
end
|
116
117
|
end
|
@@ -142,7 +143,14 @@ module QED
|
|
142
143
|
# Dispatch event to observers and advice.
|
143
144
|
def advise!(signal, *args)
|
144
145
|
@observers.each{ |o| o.update(signal, *args) }
|
145
|
-
|
146
|
+
|
147
|
+
#@script.advise(signal, *args)
|
148
|
+
case signal
|
149
|
+
when :when
|
150
|
+
call_matchers(*args)
|
151
|
+
else
|
152
|
+
call_signals(signal, *args)
|
153
|
+
end
|
146
154
|
end
|
147
155
|
|
148
156
|
#
|
@@ -150,6 +158,106 @@ module QED
|
|
150
158
|
# @advice.call_when(match)
|
151
159
|
#end
|
152
160
|
|
161
|
+
# React to an event.
|
162
|
+
#
|
163
|
+
# TODO: Should events short circuit on finding first match?
|
164
|
+
# In other words, should there be only one of each type of signal
|
165
|
+
# ragardless of how many applique layers?
|
166
|
+
def call_signals(type, *args)
|
167
|
+
@script.applique.each do |a|
|
168
|
+
signals = a.__signals__
|
169
|
+
proc = signals[type.to_sym]
|
170
|
+
#signals.each do |set|
|
171
|
+
#proc = set[type.to_sym]
|
172
|
+
#proc.call(*args) if proc
|
173
|
+
@script.scope.instance_exec(*args, &proc) if proc
|
174
|
+
#end
|
175
|
+
end
|
176
|
+
|
177
|
+
#@script.applique.each do |a|
|
178
|
+
# signals = a.__signals__
|
179
|
+
# proc = signals[type.to_sym]
|
180
|
+
# if proc
|
181
|
+
# @script.scope.instance_exec(*args, &proc)
|
182
|
+
# break
|
183
|
+
# end
|
184
|
+
#end
|
185
|
+
|
186
|
+
#meth = "qed_#{type}"
|
187
|
+
#if @script.scope.respond_to?(meth)
|
188
|
+
# meth = @script.scope.method(meth)
|
189
|
+
# if meth.arity == 0
|
190
|
+
# meth.call
|
191
|
+
# else
|
192
|
+
# meth.call(*args)
|
193
|
+
# end
|
194
|
+
#end
|
195
|
+
|
196
|
+
#@script.scope.__send__(meth, *args)
|
197
|
+
end
|
198
|
+
|
199
|
+
#
|
200
|
+
def call_matchers(section)
|
201
|
+
match = section.text
|
202
|
+
args = section.arguments
|
203
|
+
@script.applique.each do |a|
|
204
|
+
matchers = a.__matchers__
|
205
|
+
matchers.each do |(patterns, proc)|
|
206
|
+
compare = match
|
207
|
+
matched = true
|
208
|
+
params = []
|
209
|
+
patterns.each do |pattern|
|
210
|
+
case pattern
|
211
|
+
when Regexp
|
212
|
+
regex = pattern
|
213
|
+
else
|
214
|
+
regex = match_string_to_regexp(pattern)
|
215
|
+
end
|
216
|
+
if md = regex.match(compare)
|
217
|
+
params.concat(md[1..-1])
|
218
|
+
compare = md.post_match
|
219
|
+
else
|
220
|
+
matched = false
|
221
|
+
break
|
222
|
+
end
|
223
|
+
end
|
224
|
+
if matched
|
225
|
+
params += args
|
226
|
+
#proc.call(*params)
|
227
|
+
@script.scope.instance_exec(*params, &proc)
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
# Convert matching string into a regular expression. If the string
|
234
|
+
# contains double parenthesis, such as ((.*?)), then the text within
|
235
|
+
# them is treated as in regular expression and kept verbatium.
|
236
|
+
#
|
237
|
+
# TODO: Better way to isolate regexp. Maybe ?:(.*?) or /(.*?)/.
|
238
|
+
#
|
239
|
+
# TODO: Now that we can use multi-patterns, do we still need this?
|
240
|
+
#
|
241
|
+
def match_string_to_regexp(str)
|
242
|
+
str = str.split(/(\(\(.*?\)\))(?!\))/).map{ |x|
|
243
|
+
x =~ /\A\(\((.*)\)\)\Z/ ? $1 : Regexp.escape(x)
|
244
|
+
}.join
|
245
|
+
str = str.gsub(/\\\s+/, '\s+')
|
246
|
+
Regexp.new(str, Regexp::IGNORECASE)
|
247
|
+
|
248
|
+
#rexps = []
|
249
|
+
#str = str.gsub(/\(\((.*?)\)\)/) do |m|
|
250
|
+
# rexps << '(' + $1 + ')'
|
251
|
+
# "\0"
|
252
|
+
#end
|
253
|
+
#str = Regexp.escape(str)
|
254
|
+
#rexps.each do |r|
|
255
|
+
# str = str.sub("\0", r)
|
256
|
+
#end
|
257
|
+
#str = str.gsub(/(\\\ )+/, '\s+')
|
258
|
+
#Regexp.new(str, Regexp::IGNORECASE)
|
259
|
+
end
|
260
|
+
|
153
261
|
##
|
154
262
|
#def method_missing(s, *a)
|
155
263
|
# super(s, *a) unless /^tag/ =~ s.to_s
|
@@ -158,4 +266,3 @@ module QED
|
|
158
266
|
end
|
159
267
|
|
160
268
|
end
|
161
|
-
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module QED
|
2
|
+
|
3
|
+
# This extension provides a simple means for creatind file-system fixtures.
|
4
|
+
# Include this in your applique, to have a
|
5
|
+
module FileFixtures
|
6
|
+
|
7
|
+
#
|
8
|
+
def self.included(base)
|
9
|
+
require 'erb'
|
10
|
+
end
|
11
|
+
|
12
|
+
#
|
13
|
+
def copy_fixture(name, tmpdir=nil)
|
14
|
+
tmpdir ||= 'tmp' # self.tmpdir
|
15
|
+
FileUtils.mkdir(tmpdir) unless File.directory?(tmpdir)
|
16
|
+
srcdir = File.join(demo_directory, 'fixtures', name)
|
17
|
+
paths = Dir.glob(File.join(srcdir, '**', '*'), File::FNM_DOTMATCH)
|
18
|
+
paths.each do |path|
|
19
|
+
basename = File.basename(path)
|
20
|
+
next if basename == '.'
|
21
|
+
next if basename == '..'
|
22
|
+
dest = File.join(tmpdir, path.sub(srcdir+'/', ''))
|
23
|
+
if File.directory?(path)
|
24
|
+
FileUtils.mkdir(dest)
|
25
|
+
else
|
26
|
+
text = ERB.new(File.read(path)).result
|
27
|
+
File.open(dest, 'w'){ |f| f << text }
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
File without changes
|
data/lib/qed/meta/data.rb
CHANGED
@@ -1,29 +1,27 @@
|
|
1
|
-
Object.__send__(:remove_const, :VERSION) if Object.const_defined?(:VERSION) # becuase Ruby 1.8~ gets in the way
|
2
|
-
|
3
1
|
module QED
|
4
2
|
|
5
|
-
|
6
|
-
File.dirname(__FILE__)
|
7
|
-
end
|
3
|
+
DIRECTORY = File.dirname(__FILE__)
|
8
4
|
|
9
|
-
def self.
|
10
|
-
@
|
5
|
+
def self.package
|
6
|
+
@package ||= (
|
11
7
|
require 'yaml'
|
12
|
-
YAML.load(File.new(
|
8
|
+
YAML.load(File.new(DIRECTORY + '/package'))
|
13
9
|
)
|
14
10
|
end
|
15
11
|
|
16
12
|
def self.profile
|
17
13
|
@profile ||= (
|
18
14
|
require 'yaml'
|
19
|
-
YAML.load(File.new(
|
15
|
+
YAML.load(File.new(DIRECTORY + '/profile'))
|
20
16
|
)
|
21
17
|
end
|
22
18
|
|
23
19
|
def self.const_missing(name)
|
24
20
|
key = name.to_s.downcase
|
25
|
-
|
21
|
+
package[key] || profile[key] || super(name)
|
26
22
|
end
|
27
23
|
|
28
24
|
end
|
29
25
|
|
26
|
+
# becuase Ruby 1.8~ gets in the way
|
27
|
+
Object.__send__(:remove_const, :VERSION) if Object.const_defined?(:VERSION)
|
@@ -237,8 +237,12 @@ module Reporter
|
|
237
237
|
end
|
238
238
|
|
239
239
|
def print_tally
|
240
|
+
assert_count = Assertion.count
|
241
|
+
assert_fails = Assertion.fails
|
242
|
+
assert_delta = assert_count - assert_fails
|
243
|
+
|
240
244
|
mask = "%s demos, %s steps: %s failures, %s errors (%s/%s assertions)"
|
241
|
-
vars = [demos.size, steps.size, fails.size, errors.size,
|
245
|
+
vars = [demos.size, steps.size, fails.size, errors.size, assert_delta, assert_count] #, @pass.size ]
|
242
246
|
|
243
247
|
io.puts mask % vars
|
244
248
|
end
|
@@ -46,9 +46,9 @@ module Reporter #:nodoc:
|
|
46
46
|
tab = step.text.index(/\S/)
|
47
47
|
io.print "#{txt}\n\n".ansi(:red)
|
48
48
|
msg = []
|
49
|
-
#msg << ANSI::Code.bold(ANSI::Code.red("FAIL: ")) + error.
|
49
|
+
#msg << ANSI::Code.bold(ANSI::Code.red("FAIL: ")) + error.message
|
50
50
|
#msg << ANSI::Code.bold(clean_backtrace(error.backtrace[0]))
|
51
|
-
msg << "FAIL: ".ansi(:bold, :red) + error.to_str
|
51
|
+
msg << "FAIL: ".ansi(:bold, :red) + error.message #to_str
|
52
52
|
msg << clean_backtrace(error.backtrace[0]).ansi(:bold)
|
53
53
|
io.puts msg.join("\n").tabto(tab||2)
|
54
54
|
io.puts
|
@@ -62,7 +62,7 @@ module Reporter #:nodoc:
|
|
62
62
|
tab = step.text.index(/\S/)
|
63
63
|
io.print "#{txt}\n\n".ansi(:red)
|
64
64
|
msg = []
|
65
|
-
msg << "ERROR: #{error.class} ".ansi(:bold,:red) + error.
|
65
|
+
msg << "ERROR: #{error.class} ".ansi(:bold,:red) + error.message #.sub(/for QED::Context.*?$/,'')
|
66
66
|
msg << clean_backtrace(error.backtrace[0]).ansi(:bold)
|
67
67
|
#msg = msg.ansi(:red)
|
68
68
|
io.puts msg.join("\n").tabto(tab||2)
|
data/lib/qed/scope.rb
CHANGED
@@ -6,26 +6,30 @@ module QED
|
|
6
6
|
#
|
7
7
|
class Scope < Module
|
8
8
|
|
9
|
-
#
|
10
|
-
|
11
|
-
@_applique = applique
|
12
|
-
super(applique, file)
|
13
|
-
end
|
9
|
+
# Location of `qed/scope.rb`.
|
10
|
+
DIRECTORY = File.dirname(__FILE__)
|
14
11
|
|
15
12
|
#
|
16
|
-
def self.
|
17
|
-
@_applique
|
18
|
-
|
13
|
+
# def self.new(applique, file)
|
14
|
+
# @_applique = applique
|
15
|
+
# super(applique, file)
|
16
|
+
# end
|
17
|
+
|
18
|
+
# #
|
19
|
+
# def self.const_missing(name)
|
20
|
+
# @_applique.const_get(name)
|
21
|
+
# end
|
19
22
|
|
20
23
|
#
|
21
24
|
def initialize(applique, file=nil)
|
22
25
|
super()
|
23
26
|
@_applique = applique
|
24
27
|
@_file = file
|
28
|
+
#@loadlist = []
|
25
29
|
|
30
|
+
include *applique
|
26
31
|
extend self
|
27
|
-
extend applique # TODO: extend or include applique or none ?
|
28
|
-
include applique
|
32
|
+
#extend applique # TODO: extend or include applique or none ?
|
29
33
|
#extend DSLi
|
30
34
|
|
31
35
|
# TODO: custom extends?
|
@@ -45,20 +49,51 @@ module QED
|
|
45
49
|
}
|
46
50
|
end
|
47
51
|
|
52
|
+
#
|
53
|
+
def include(*modules)
|
54
|
+
super(*modules)
|
55
|
+
extend self
|
56
|
+
end
|
57
|
+
|
48
58
|
# Expanded dirname of +file+.
|
49
59
|
def demo_directory
|
50
60
|
@_demo_directory ||= File.expand_path(File.dirname(@_file))
|
51
61
|
end
|
52
62
|
|
53
63
|
# Evaluate code in the context of the scope's special binding.
|
54
|
-
def eval(code, binding=nil)
|
64
|
+
def eval(code, binding=nil, file=nil)
|
55
65
|
super(code, binding || __binding__, @_file)
|
56
66
|
end
|
57
67
|
|
68
|
+
|
69
|
+
|
70
|
+
# Utilize is like #require, but will evaluate the script in the context
|
71
|
+
# of the current scope.
|
72
|
+
#--
|
73
|
+
# TODO: Alternative to Plugin gem?
|
74
|
+
#
|
75
|
+
# TODO: Should work like require so same file isn't loaded twice.
|
76
|
+
#++
|
77
|
+
def utilize(file)
|
78
|
+
file = Dir[DIRECTORY + "/helpers/#{file}"].first
|
79
|
+
if !file
|
80
|
+
require 'plugin'
|
81
|
+
file = Plugin.find("#{file}{,.rb}", :directory=>nil)
|
82
|
+
end
|
83
|
+
if file
|
84
|
+
code = File.read(file)
|
85
|
+
eval(code, nil, file)
|
86
|
+
else
|
87
|
+
raise LoadError, "no such file -- #{file}"
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
|
92
|
+
|
58
93
|
# Define "when" advice.
|
59
94
|
def When(*patterns, &procedure)
|
60
95
|
patterns = patterns.map{ |pat| pat == :text ? :desc : pat }
|
61
|
-
@_applique.When(*patterns, &procedure)
|
96
|
+
@_applique.first.When(*patterns, &procedure)
|
62
97
|
end
|
63
98
|
|
64
99
|
# Define "before" advice. Default type is :each, which
|
@@ -66,7 +101,7 @@ module QED
|
|
66
101
|
def Before(type=:each, &procedure)
|
67
102
|
type = :step if type == :each
|
68
103
|
type = :demo if type == :all
|
69
|
-
@_applique.Before(type, &procedure)
|
104
|
+
@_applique.first.Before(type, &procedure)
|
70
105
|
end
|
71
106
|
|
72
107
|
# Define "after" advice. Default type is :each, which
|
@@ -74,9 +109,11 @@ module QED
|
|
74
109
|
def After(type=:each, &procedure)
|
75
110
|
type = :step if type == :each
|
76
111
|
type = :demo if type == :all
|
77
|
-
@_applique.After(type, &procedure)
|
112
|
+
@_applique.first.After(type, &procedure)
|
78
113
|
end
|
79
114
|
|
115
|
+
|
116
|
+
|
80
117
|
# TODO: Should Table and Data be extensions that can be loaded if desired?
|
81
118
|
|
82
119
|
# Use sample table to run steps. The table file will be
|
@@ -128,6 +165,11 @@ module QED
|
|
128
165
|
#end
|
129
166
|
end
|
130
167
|
|
168
|
+
#
|
169
|
+
def const_missing(const)
|
170
|
+
Object.const_get(const)
|
171
|
+
end
|
172
|
+
|
131
173
|
end#class Scope
|
132
174
|
|
133
175
|
end#module QED
|