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.
@@ -1,3 +1,5 @@
1
+ require 'facets/dir/ascend'
2
+
1
3
  class Object
2
4
 
3
5
  unless method_defined?(:instance_exec) # 1.9
data/lib/qed/demo.rb CHANGED
@@ -1,35 +1,31 @@
1
1
  module QED
2
- require 'yaml'
3
2
 
4
- require 'facets/dir/ascend'
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
- # = Demo
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, applique, options={})
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, @script.applique, :scope=>@script.scope).run
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
- @script.advise(signal, *args)
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
- def self.__DIR__
6
- File.dirname(__FILE__)
7
- end
3
+ DIRECTORY = File.dirname(__FILE__)
8
4
 
9
- def self.gemfile
10
- @gemfile ||= (
5
+ def self.package
6
+ @package ||= (
11
7
  require 'yaml'
12
- YAML.load(File.new(__DIR__ + '/gemfile'))
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(__DIR__ + '/profile'))
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
- gemfile[key] || profile[key] || super(name)
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)
@@ -1,10 +1,10 @@
1
1
  name: qed
2
- version: 2.4.0
3
- date: 2010-09-02
2
+ date: 2010-11-07
3
+ version: 2.5.0
4
4
 
5
5
  requires:
6
6
  - ansi
7
7
  - facets
8
- - ae (test)
8
+ - ae
9
9
  - syckle (build)
10
10
 
@@ -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, $assertions-$failures, $assertions] #, @pass.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.to_str
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.to_str #.sub(/for QED::Context.*?$/,'')
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
- def self.new(applique, file)
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.const_missing(name)
17
- @_applique.const_get(name)
18
- end
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