qed 2.1.1 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. data/COPYING +622 -344
  2. data/DIARY.rdoc +117 -0
  3. data/HISTORY +36 -0
  4. data/PROFILE +16 -0
  5. data/README.rdoc +69 -36
  6. data/REQUIRE +9 -0
  7. data/ROADMAP +12 -0
  8. data/VERSION +5 -0
  9. data/demo/01_demos.rdoc +56 -0
  10. data/demo/02_advice.rdoc +158 -0
  11. data/demo/03_helpers.rdoc +42 -0
  12. data/demo/04_fixtures.rdoc +29 -0
  13. data/demo/05_quote.rdoc +24 -0
  14. data/demo/07_toplevel.rdoc +42 -0
  15. data/demo/08_cross_script.rdoc +27 -0
  16. data/demo/09_cross_script.rdoc +27 -0
  17. data/demo/10_constant_lookup.rdoc +16 -0
  18. data/demo/applique/constant.rb +2 -0
  19. data/demo/applique/env.rb +5 -0
  20. data/demo/applique/fileutils.rb +1 -0
  21. data/demo/applique/markup.rb +10 -0
  22. data/demo/applique/quote.rb +4 -0
  23. data/demo/applique/toplevel.rb +15 -0
  24. data/demo/fixtures/data.txt +1 -0
  25. data/demo/fixtures/table.yml +5 -0
  26. data/demo/helpers/advice.rb +40 -0
  27. data/demo/helpers/sample.rb +4 -0
  28. data/demo/helpers/toplevel.rb +6 -0
  29. data/eg/hello_world.rdoc +15 -0
  30. data/{demo/error.rdoc → eg/view_error.rdoc} +0 -0
  31. data/{demo → eg}/website.rdoc +0 -0
  32. data/lib/qed.rb +20 -1
  33. data/lib/qed/advice.rb +4 -30
  34. data/lib/qed/advice/events.rb +6 -3
  35. data/lib/qed/advice/patterns.rb +37 -19
  36. data/lib/qed/applique.rb +85 -0
  37. data/lib/qed/command.rb +3 -5
  38. data/lib/qed/evaluator.rb +52 -56
  39. data/lib/qed/package.yml +5 -0
  40. data/lib/qed/parser.rb +149 -0
  41. data/lib/qed/profile.yml +16 -0
  42. data/lib/qed/reporter/{base.rb → abstract.rb} +17 -19
  43. data/lib/qed/reporter/bullet.rb +14 -16
  44. data/lib/qed/reporter/dotprogress.rb +7 -6
  45. data/lib/qed/reporter/html.rb +21 -3
  46. data/lib/qed/reporter/verbatim.rb +28 -26
  47. data/lib/qed/scope.rb +98 -82
  48. data/lib/qed/script.rb +21 -69
  49. data/lib/qed/session.rb +44 -3
  50. data/script/qedoc +2 -0
  51. data/script/test +2 -0
  52. metadata +74 -28
  53. data/doc/qedoc/index.html +0 -515
  54. data/doc/qedoc/jquery.js +0 -19
  55. data/meta/authors +0 -1
  56. data/meta/created +0 -1
  57. data/meta/description +0 -2
  58. data/meta/homepage +0 -1
  59. data/meta/name +0 -1
  60. data/meta/released +0 -1
  61. data/meta/repository +0 -1
  62. data/meta/requires +0 -5
  63. data/meta/ruby +0 -2
  64. data/meta/suite +0 -1
  65. data/meta/summary +0 -1
  66. data/meta/title +0 -1
  67. data/meta/version +0 -1
@@ -1,11 +1,12 @@
1
1
  module QED
2
2
 
3
- # = Patter Advice (When)
3
+ # = Pattern Advice (When)
4
4
  #
5
- # This class tracks When advice defined by demo scripts
6
- # and helpers. It is instantiated in Scope, so that
7
- # the advice methods will have access to the same
8
- # local binding and the demo scripts themselves.
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(pattern, &procedure)
20
- @when << [pattern, procedure]
20
+ def add(patterns, &procedure)
21
+ @when << [patterns, procedure]
21
22
  end
22
23
 
23
24
  #
24
- def call(match, *args)
25
- @when.each do |(pattern, proc)|
26
- case pattern
27
- when Regexp
28
- regex = pattern
29
- else
30
- regex = when_string_to_regexp(pattern)
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 md = regex.match(match)
33
- proc.call(*md[1..-1])
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\(\((.*)\)\)\z/ ? $1 : Regexp.escape(x)
61
+ x =~ /\A\(\((.*)\)\)\Z/ ? $1 : Regexp.escape(x)
44
62
  }.join
45
- str = str.gsub(/(\\\ )+/, '\s+')
63
+ str = str.gsub(/\\\s+/, '\s+')
46
64
  Regexp.new(str, Regexp::IGNORECASE)
47
65
 
48
66
  #rexps = []
@@ -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
- DEFAULT_DEMOS_LOCATION = '{qed}'
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 << DEFAULT_DEMOS_LOCATION
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
- # = Demo Script Evaluator
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
- @root = script.root
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
- Dir.chdir(File.dirname(@file)) do
35
- advise!(:before_document, @script)
36
- @root.traverse do |element|
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 tag_a(element)
53
- case element['href']
54
- when /qed:\/\/(.*?)$/
55
- file = $1
56
- case File.extname(file)
57
- when '.rb'
58
- import!(file)
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 tag_pre(element)
67
- advise!(:before_code, element, @file)
40
+ def evaluate_code(section)
41
+ advise!(:before_code, section, @file)
68
42
  begin
69
- eval(element.text, @binding, @file, element.line)
70
- pass!(element)
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!(element, exception)
48
+ fail!(section, exception)
73
49
  rescue Exception => exception
74
- error!(element, exception)
50
+ error!(section, exception)
75
51
  end
76
- advise!(:after_code, element, @file)
52
+ advise!(:after_code, section, @file)
77
53
  end
78
54
 
79
55
  #
80
- def tag_p(element)
81
- advise!(:when, element.text)
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 method_missing(s, *a)
86
- super(s, *a) unless /^tag/ =~ s.to_s
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!(element)
91
- advise!(:pass, element)
81
+ def pass!(section)
82
+ advise!(:pass, section)
92
83
  end
93
84
 
94
85
  #
95
- def fail!(element, exception)
96
- advise!(:fail, element, exception)
86
+ def fail!(section, exception)
87
+ advise!(:fail, section, exception)
97
88
  #raise exception
98
89
  end
99
90
 
100
91
  #
101
- def error!(element, exception)
102
- advise!(:error, element, exception)
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
@@ -0,0 +1,5 @@
1
+ name : qed
2
+ major: 2
3
+ minor: 2
4
+ patch: 0
5
+ date : 2010-06-20
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
+