qed 2.1.1 → 2.2.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/COPYING +622 -344
- data/DIARY.rdoc +117 -0
- data/HISTORY +36 -0
- data/PROFILE +16 -0
- data/README.rdoc +69 -36
- data/REQUIRE +9 -0
- data/ROADMAP +12 -0
- data/VERSION +5 -0
- data/demo/01_demos.rdoc +56 -0
- data/demo/02_advice.rdoc +158 -0
- data/demo/03_helpers.rdoc +42 -0
- data/demo/04_fixtures.rdoc +29 -0
- data/demo/05_quote.rdoc +24 -0
- data/demo/07_toplevel.rdoc +42 -0
- data/demo/08_cross_script.rdoc +27 -0
- data/demo/09_cross_script.rdoc +27 -0
- data/demo/10_constant_lookup.rdoc +16 -0
- data/demo/applique/constant.rb +2 -0
- data/demo/applique/env.rb +5 -0
- data/demo/applique/fileutils.rb +1 -0
- data/demo/applique/markup.rb +10 -0
- data/demo/applique/quote.rb +4 -0
- data/demo/applique/toplevel.rb +15 -0
- data/demo/fixtures/data.txt +1 -0
- data/demo/fixtures/table.yml +5 -0
- data/demo/helpers/advice.rb +40 -0
- data/demo/helpers/sample.rb +4 -0
- data/demo/helpers/toplevel.rb +6 -0
- data/eg/hello_world.rdoc +15 -0
- data/{demo/error.rdoc → eg/view_error.rdoc} +0 -0
- data/{demo → eg}/website.rdoc +0 -0
- data/lib/qed.rb +20 -1
- data/lib/qed/advice.rb +4 -30
- data/lib/qed/advice/events.rb +6 -3
- data/lib/qed/advice/patterns.rb +37 -19
- data/lib/qed/applique.rb +85 -0
- data/lib/qed/command.rb +3 -5
- data/lib/qed/evaluator.rb +52 -56
- data/lib/qed/package.yml +5 -0
- data/lib/qed/parser.rb +149 -0
- data/lib/qed/profile.yml +16 -0
- data/lib/qed/reporter/{base.rb → abstract.rb} +17 -19
- data/lib/qed/reporter/bullet.rb +14 -16
- data/lib/qed/reporter/dotprogress.rb +7 -6
- data/lib/qed/reporter/html.rb +21 -3
- data/lib/qed/reporter/verbatim.rb +28 -26
- data/lib/qed/scope.rb +98 -82
- data/lib/qed/script.rb +21 -69
- data/lib/qed/session.rb +44 -3
- data/script/qedoc +2 -0
- data/script/test +2 -0
- metadata +74 -28
- data/doc/qedoc/index.html +0 -515
- data/doc/qedoc/jquery.js +0 -19
- data/meta/authors +0 -1
- data/meta/created +0 -1
- data/meta/description +0 -2
- data/meta/homepage +0 -1
- data/meta/name +0 -1
- data/meta/released +0 -1
- data/meta/repository +0 -1
- data/meta/requires +0 -5
- data/meta/ruby +0 -2
- data/meta/suite +0 -1
- data/meta/summary +0 -1
- data/meta/title +0 -1
- data/meta/version +0 -1
data/lib/qed/advice/patterns.rb
CHANGED
@@ -1,11 +1,12 @@
|
|
1
1
|
module QED
|
2
2
|
|
3
|
-
# =
|
3
|
+
# = Pattern Advice (When)
|
4
4
|
#
|
5
|
-
# This class
|
6
|
-
#
|
7
|
-
#
|
8
|
-
#
|
5
|
+
# This class encapsulates "When" advice on plain text.
|
6
|
+
#
|
7
|
+
# Matches are evaluated in Scope context, via #instance_exec,
|
8
|
+
# so that the advice methods will have access to the same
|
9
|
+
# scope as the demonstrandum themselves.
|
9
10
|
#
|
10
11
|
class Patterns
|
11
12
|
|
@@ -16,33 +17,50 @@ module QED
|
|
16
17
|
end
|
17
18
|
|
18
19
|
#
|
19
|
-
def add(
|
20
|
-
@when << [
|
20
|
+
def add(patterns, &procedure)
|
21
|
+
@when << [patterns, procedure]
|
21
22
|
end
|
22
23
|
|
23
24
|
#
|
24
|
-
def call(
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
25
|
+
def call(scope, section)
|
26
|
+
match = section.text
|
27
|
+
args = section.args
|
28
|
+
|
29
|
+
@when.each do |(patterns, proc)|
|
30
|
+
compare = match
|
31
|
+
matched = true
|
32
|
+
params = []
|
33
|
+
patterns.each do |pattern|
|
34
|
+
case pattern
|
35
|
+
when Regexp
|
36
|
+
regex = pattern
|
37
|
+
else
|
38
|
+
regex = when_string_to_regexp(pattern)
|
39
|
+
end
|
40
|
+
if md = regex.match(compare)
|
41
|
+
params.concat(md[1..-1])
|
42
|
+
compare = md.post_match
|
43
|
+
else
|
44
|
+
matched = false
|
45
|
+
break
|
46
|
+
end
|
31
47
|
end
|
32
|
-
if
|
33
|
-
|
48
|
+
if matched
|
49
|
+
params += args
|
50
|
+
#proc.call(*params)
|
51
|
+
scope.instance_exec(*params, &proc)
|
34
52
|
end
|
35
53
|
end
|
36
54
|
end
|
37
55
|
|
38
56
|
private
|
39
57
|
|
40
|
-
#
|
58
|
+
# TODO: Now that we can use multi-patterns, we might not need this any more.
|
41
59
|
def when_string_to_regexp(str)
|
42
60
|
str = str.split(/(\(\(.*?\)\))(?!\))/).map{ |x|
|
43
|
-
x =~ /\A\(\((.*)\)\)\
|
61
|
+
x =~ /\A\(\((.*)\)\)\Z/ ? $1 : Regexp.escape(x)
|
44
62
|
}.join
|
45
|
-
str = str.gsub(
|
63
|
+
str = str.gsub(/\\\s+/, '\s+')
|
46
64
|
Regexp.new(str, Regexp::IGNORECASE)
|
47
65
|
|
48
66
|
#rexps = []
|
data/lib/qed/applique.rb
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'qed/advice'
|
2
|
+
|
3
|
+
module QED
|
4
|
+
|
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
|
+
|
16
|
+
class Applique < Module
|
17
|
+
|
18
|
+
#
|
19
|
+
def initialize
|
20
|
+
super()
|
21
|
+
extend self
|
22
|
+
@__advice__ = Advice.new
|
23
|
+
end
|
24
|
+
|
25
|
+
#
|
26
|
+
def initialize_copy(other)
|
27
|
+
@__advice__ = other.__advice__.dup
|
28
|
+
end
|
29
|
+
|
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
|
37
|
+
|
38
|
+
#
|
39
|
+
def __advice__
|
40
|
+
@__advice__
|
41
|
+
end
|
42
|
+
|
43
|
+
# Because patterns are mathced against HTML documents
|
44
|
+
# HTML special charaters +<+, +>+ and +&+ should not be
|
45
|
+
# used.
|
46
|
+
def When(*patterns, &procedure)
|
47
|
+
if patterns.size == 1 && Symbol === patterns.first
|
48
|
+
__advice__.events.add(:"#{patterns.first}", &procedure)
|
49
|
+
else
|
50
|
+
__advice__.patterns.add(patterns, &procedure)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Before advice.
|
55
|
+
def Before(type=:code, &procedure)
|
56
|
+
__advice__.events.add(:"before_#{type}", &procedure)
|
57
|
+
end
|
58
|
+
|
59
|
+
# After advice.
|
60
|
+
def After(type=:code, &procedure)
|
61
|
+
__advice__.events.add(:"after_#{type}", &procedure)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Code match-and-transform procedure.
|
65
|
+
#
|
66
|
+
# This is useful to transform human readable code examples
|
67
|
+
# into proper exectuable code. For example, say you want to
|
68
|
+
# run shell code, but want to make if look like typical
|
69
|
+
# shelle examples:
|
70
|
+
#
|
71
|
+
# $ cp fixture/a.rb fixture/b.rb
|
72
|
+
#
|
73
|
+
# You can use a transform to convert lines starting with '$'
|
74
|
+
# into executable Ruby using #system.
|
75
|
+
#
|
76
|
+
# system('cp fixture/a.rb fixture/b.rb')
|
77
|
+
#
|
78
|
+
#def Transform(pattern=nil, &procedure)
|
79
|
+
#
|
80
|
+
#end
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
|
data/lib/qed/command.rb
CHANGED
@@ -3,7 +3,6 @@
|
|
3
3
|
require 'qed'
|
4
4
|
require 'optparse'
|
5
5
|
require 'shellwords'
|
6
|
-
require 'tilt'
|
7
6
|
|
8
7
|
module QED
|
9
8
|
|
@@ -17,7 +16,7 @@ module QED
|
|
17
16
|
# Default location of demonstrations if no
|
18
17
|
# specific files or locations given. This
|
19
18
|
# is use in Dir.glob.
|
20
|
-
|
19
|
+
DEFAULT_DEMO_LOCATION = '{demo,demos}'
|
21
20
|
|
22
21
|
# Initialize and execute.
|
23
22
|
def self.execute
|
@@ -156,12 +155,11 @@ module QED
|
|
156
155
|
end
|
157
156
|
|
158
157
|
#
|
159
|
-
|
160
158
|
def demos
|
161
159
|
files = self.files
|
162
|
-
types = Tilt.mappings.keys
|
160
|
+
types = %w{qed rdoc md markdown} #Tilt.mappings.keys
|
163
161
|
if files.empty?
|
164
|
-
files <<
|
162
|
+
files << DEFAULT_DEMO_LOCATION
|
165
163
|
end
|
166
164
|
files = files.map do |pattern|
|
167
165
|
Dir[pattern]
|
data/lib/qed/evaluator.rb
CHANGED
@@ -1,27 +1,15 @@
|
|
1
1
|
module QED
|
2
2
|
|
3
|
-
require 'tilt'
|
4
|
-
require 'nokogiri'
|
5
3
|
require 'qed/scope'
|
6
4
|
|
7
|
-
# =
|
8
|
-
#
|
9
|
-
#--
|
10
|
-
# TODO: Currently the Evaluator class uses #traverse to work
|
11
|
-
# thru the HTML document and trigger events accordingly. This
|
12
|
-
# works well enough for simple HTML documents --the kind produced
|
13
|
-
# by typical wiki-markup formats. However, for complex HTML it
|
14
|
-
# it will not produce ideal output (although the code segements
|
15
|
-
# should still run just fine). To counter this weakness, we will
|
16
|
-
# have to swtich to a more complex SAX parser in the future.
|
17
|
-
#--
|
5
|
+
# = Demonstrandum Evaluator
|
18
6
|
class Evaluator
|
19
7
|
|
20
8
|
#
|
21
9
|
def initialize(script, *observers)
|
22
10
|
@script = script
|
23
11
|
@file = script.file
|
24
|
-
@
|
12
|
+
@ast = script.parse
|
25
13
|
@scope = script.scope
|
26
14
|
@binding = script.binding
|
27
15
|
@advice = script.advice
|
@@ -31,75 +19,78 @@ module QED
|
|
31
19
|
|
32
20
|
#
|
33
21
|
def run
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
call_tag(element)
|
38
|
-
end
|
39
|
-
advise!(:after_document, @script)
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
#
|
44
|
-
def call_tag(element)
|
45
|
-
advise!(:tag, element)
|
46
|
-
__send__("tag_#{element.name}", element)
|
22
|
+
advise!(:before_document, @script)
|
23
|
+
process
|
24
|
+
advise!(:after_document, @script)
|
47
25
|
end
|
48
26
|
|
49
|
-
# T A G S
|
50
|
-
|
51
27
|
#
|
52
|
-
def
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
when
|
58
|
-
|
59
|
-
else
|
60
|
-
Script.new(file, scope).run
|
28
|
+
def process
|
29
|
+
@ast.each do |section|
|
30
|
+
case section.type
|
31
|
+
when :code
|
32
|
+
evaluate_code(section)
|
33
|
+
when :text
|
34
|
+
evaluate_text(section)
|
61
35
|
end
|
62
36
|
end
|
63
37
|
end
|
64
38
|
|
65
39
|
#
|
66
|
-
def
|
67
|
-
advise!(:before_code,
|
40
|
+
def evaluate_code(section)
|
41
|
+
advise!(:before_code, section, @file)
|
68
42
|
begin
|
69
|
-
|
70
|
-
|
43
|
+
advise!(:code, section)
|
44
|
+
eval(section.text, @binding, @file, section.line)
|
45
|
+
#@scope.module_eval(section.text, @file, section.line)
|
46
|
+
pass!(section)
|
71
47
|
rescue Assertion => exception
|
72
|
-
fail!(
|
48
|
+
fail!(section, exception)
|
73
49
|
rescue Exception => exception
|
74
|
-
error!(
|
50
|
+
error!(section, exception)
|
75
51
|
end
|
76
|
-
advise!(:after_code,
|
52
|
+
advise!(:after_code, section, @file)
|
77
53
|
end
|
78
54
|
|
79
55
|
#
|
80
|
-
def
|
81
|
-
advise!(:
|
56
|
+
def evaluate_text(section)
|
57
|
+
advise!(:text, section)
|
58
|
+
evaluate_links(section)
|
59
|
+
advise!(:when, section)
|
82
60
|
end
|
83
61
|
|
84
62
|
#
|
85
|
-
def
|
86
|
-
|
63
|
+
def evaluate_links(section)
|
64
|
+
section.text.scan(/\[qed:\/\/(.*?)\]/) do |match|
|
65
|
+
file = $1
|
66
|
+
# relative to demo script
|
67
|
+
if File.exist?(File.join(@script.directory,file))
|
68
|
+
file = File.join(@script.directory,file)
|
69
|
+
end
|
70
|
+
# ruby or another demo
|
71
|
+
case File.extname(file)
|
72
|
+
when '.rb'
|
73
|
+
import!(file)
|
74
|
+
else
|
75
|
+
Script.new(@script.applique, file, @script.scope).run
|
76
|
+
end
|
77
|
+
end
|
87
78
|
end
|
88
79
|
|
89
80
|
#
|
90
|
-
def pass!(
|
91
|
-
advise!(:pass,
|
81
|
+
def pass!(section)
|
82
|
+
advise!(:pass, section)
|
92
83
|
end
|
93
84
|
|
94
85
|
#
|
95
|
-
def fail!(
|
96
|
-
advise!(:fail,
|
86
|
+
def fail!(section, exception)
|
87
|
+
advise!(:fail, section, exception)
|
97
88
|
#raise exception
|
98
89
|
end
|
99
90
|
|
100
91
|
#
|
101
|
-
def error!(
|
102
|
-
advise!(:error,
|
92
|
+
def error!(section, exception)
|
93
|
+
advise!(:error, section, exception)
|
103
94
|
#raise exception
|
104
95
|
end
|
105
96
|
|
@@ -114,7 +105,7 @@ module QED
|
|
114
105
|
def advise!(signal, *args)
|
115
106
|
@observers.each{ |o| o.update(signal, *args) }
|
116
107
|
#@scope.__advice__.call(signal, *args)
|
117
|
-
@advice.call(signal, *args)
|
108
|
+
@advice.call(@scope, signal, *args)
|
118
109
|
end
|
119
110
|
|
120
111
|
#
|
@@ -122,6 +113,11 @@ module QED
|
|
122
113
|
# @advice.call_when(match)
|
123
114
|
#end
|
124
115
|
|
116
|
+
##
|
117
|
+
#def method_missing(s, *a)
|
118
|
+
# super(s, *a) unless /^tag/ =~ s.to_s
|
119
|
+
#end
|
120
|
+
|
125
121
|
end
|
126
122
|
|
127
123
|
end
|
data/lib/qed/package.yml
ADDED
data/lib/qed/parser.rb
ADDED
@@ -0,0 +1,149 @@
|
|
1
|
+
module QED
|
2
|
+
|
3
|
+
# The parser breaks down a demonstandum into
|
4
|
+
# structured object to passed thru the script
|
5
|
+
# evaluator.
|
6
|
+
#
|
7
|
+
# Technically is defines it's own markup language
|
8
|
+
# but for interoperability sake it ...
|
9
|
+
class Parser
|
10
|
+
|
11
|
+
#
|
12
|
+
def initialize(file)
|
13
|
+
@lines = File.readlines(file).to_a
|
14
|
+
@ast = []
|
15
|
+
end
|
16
|
+
|
17
|
+
#
|
18
|
+
attr :ast
|
19
|
+
|
20
|
+
#
|
21
|
+
def parse
|
22
|
+
state = :text
|
23
|
+
linein = 0
|
24
|
+
|
25
|
+
text = ''
|
26
|
+
|
27
|
+
@lines.each_with_index do |line, lineno|
|
28
|
+
if /^\S/ =~ line
|
29
|
+
if state == :code
|
30
|
+
add_section(:code, text, linein)
|
31
|
+
linein = lineno
|
32
|
+
text = ''
|
33
|
+
end
|
34
|
+
state = :text
|
35
|
+
text << line
|
36
|
+
else
|
37
|
+
if state == :text
|
38
|
+
next if text.strip.empty?
|
39
|
+
add_section(:text, text, linein)
|
40
|
+
linein = lineno
|
41
|
+
text = ''
|
42
|
+
end
|
43
|
+
state = :code
|
44
|
+
text << line
|
45
|
+
end
|
46
|
+
end
|
47
|
+
add_section(state, text, linein)
|
48
|
+
@ast.reject!{ |sect| sect.type == :code && sect.text.strip.empty? }
|
49
|
+
return @ast
|
50
|
+
end
|
51
|
+
|
52
|
+
#
|
53
|
+
def add_section(state, text, lineno)
|
54
|
+
case state
|
55
|
+
when :code
|
56
|
+
if ast.last.raw?
|
57
|
+
@ast.last << text #clean_quote(text)
|
58
|
+
else
|
59
|
+
@ast << CodeSection.new(text, lineno)
|
60
|
+
end
|
61
|
+
else
|
62
|
+
@ast << TextSection.new(text, lineno)
|
63
|
+
#cont = (/\.\.\.\s*^/ =~ text ? true : false)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# TODO: We need to preserve the indentation for the verbatim reporter.
|
68
|
+
#def clean_quote(text)
|
69
|
+
# text = text.tabto(0).chomp.sub(/\A\n/,'')
|
70
|
+
# if md = /\A["]{3,}(.*?)["]{3,}\Z/.match(text)
|
71
|
+
# text = md[1]
|
72
|
+
# end
|
73
|
+
# text.rstrip
|
74
|
+
#end
|
75
|
+
|
76
|
+
#
|
77
|
+
class Section
|
78
|
+
attr :text
|
79
|
+
attr :line
|
80
|
+
def initialize(text, line)
|
81
|
+
@text = text
|
82
|
+
@line = line
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
#
|
87
|
+
class TextSection < Section
|
88
|
+
attr :args
|
89
|
+
attr :cont
|
90
|
+
def initialize(text, line, *args)
|
91
|
+
@text = text
|
92
|
+
@line = line
|
93
|
+
@args = args
|
94
|
+
@cont = []
|
95
|
+
end
|
96
|
+
def <<(text)
|
97
|
+
@cont << clean_continuation(text)
|
98
|
+
@args << block_continuation(text)
|
99
|
+
end
|
100
|
+
def type
|
101
|
+
:text
|
102
|
+
end
|
103
|
+
# TODO: Use ':' or '...' ?
|
104
|
+
def raw?
|
105
|
+
#/\:\s*\Z/m =~ text
|
106
|
+
/\.\.\.\s*\Z/m =~ text
|
107
|
+
end
|
108
|
+
|
109
|
+
# Clean up the text, removing unccesseary white lines and triple
|
110
|
+
# quote brackets, but keep indention intact.
|
111
|
+
def clean_continuation(text)
|
112
|
+
text = text.chomp.sub(/\A\n/,'')
|
113
|
+
if md = /\A["]{3,}(.*?)["]{3,}\Z/.match(text)
|
114
|
+
text = md[1]
|
115
|
+
end
|
116
|
+
text.rstrip
|
117
|
+
end
|
118
|
+
|
119
|
+
# Block the text, removing white lines, triple quote brackets
|
120
|
+
# and indention.
|
121
|
+
def block_continuation(text)
|
122
|
+
text = text.tabto(0).chomp.sub(/\A\n/,'')
|
123
|
+
if md = /\A["]{3,}(.*?)["]{3,}\Z/.match(text)
|
124
|
+
text = md[1]
|
125
|
+
end
|
126
|
+
text.rstrip
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
#
|
131
|
+
class CodeSection < Section
|
132
|
+
#attr :args
|
133
|
+
def intialize(text, line) #, *args)
|
134
|
+
@text = text
|
135
|
+
@line = line
|
136
|
+
#@args = args
|
137
|
+
end
|
138
|
+
#def <<(arg)
|
139
|
+
# @args << arg
|
140
|
+
#end
|
141
|
+
def type
|
142
|
+
:code
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
end
|
147
|
+
|
148
|
+
end
|
149
|
+
|