lib/qed/script.rb in qed-1.3 vs lib/qed/script.rb in qed-2.0.0
- old
+ new
@@ -1,449 +1,116 @@
module QED
require 'yaml'
+ require 'tilt'
+ require 'nokogiri'
+
require 'facets/dir/ascend'
- require 'ae'
+ require 'qed/evaluator'
- require 'qed/reporter/dotprogress'
- require 'qed/reporter/summary'
- require 'qed/reporter/verbatim'
-
#Assertion = AE::Assertion
- Expectation = Assertor
+# Expectation = Assertor
- # Global Before
- def self.Before(&procedure)
- @_before = procedure if procedure
- @_before
- end
-
- # Global After
- def self.After(&procedure)
- @_after = procedure if procedure
- @_after
- end
-
- # New Specification
- #def initialize(specs, output=nil)
- # @specs = [specs].flatten
- #end
-
# = Script
#
+ # When run current working directory is changed to that of
+ # the demonstration script's. So any relative file references
+ # within a demo must take that into account.
+ #
class Script
- #def self.load(file, output=nil)
- # new(File.read(file), output)
- #end
-
- # Path of demonstration script.
+ # Demonstration file.
attr :file
- # Reporter object to issue output calls.
- attr :output
+ # Expanded dirname of +file+.
+ attr :dir
- # List of helper scripts to require.
- attr :helpers
+ #
+ attr :scope
# New Script
- def initialize(file, output=nil)
- @file = file
- @output = output || Reporter::Verbatim.new #(self)
- parse_document(file)
+ def initialize(file, scope=nil)
+ @file = file
+ @scope = scope || Scope.new
+ apply_environment
end
+ #
+ def dir
+ @dir ||= File.expand_path(File.dirname(file))
+ end
+
# File basename less extension.
def name
@name ||= File.basename(file).chomp(File.extname(file))
end
- #
- def directory
- @directory ||= Dir.pwd #File.dirname(File.expand_path(file))
+ # Nokogiri HTML document.
+ def document
+ @document ||= Nokogiri::HTML(to_html)
end
- #def convert
- # @source.gsub(/^\w/, '# \1')
- #end
-
- # Run the script.
- def run
- @lineno = 0
-
- $LOAD_PATH.unshift(directory)
-
- import_helpers
-
- steps.each do |step|
- output.report_step(step)
- case step
- when /^[=#]/
- output.report_header(step)
- when /^\S/
- output.report_comment(step)
- context.When.each do |(regex, proc)|
- if md = regex.match(step)
- proc.call(*md[1..-1])
- end
- end
- else
- #if context.table
- # run_table(step)
- #else
- run_step(step)
- #end
- end
- @lineno += step.count("\n")
- end
-
- $LOAD_PATH.index(directory){ |i| $LOAD_PATH.delete_at(i) }
+ # Root node of the html document.
+ def root
+ document.root
end
- #--
- # NOTE: The Around code is in place should we decide
- # to use it. I'm not sure yet if it's really neccessary,
- # since we have Before and After.
- #++
- def run_step(step=nil, &blk)
- QED.Before.call if QED.Before
- context.Before.call if context.Before
- begin
- if blk # TODO: Is this still used?
- blk.call #eval(step, context._binding)
- else
- #if context.Around
- # context.Around.call do
- # eval(step, context._binding, @file, @lineno+1)
- # end
- #else
- eval(step, context._binding, @file, @lineno+1)
- #end
- end
- output.report_pass(step) if step
- rescue Assertion => error
- output.report_fail(step, error)
- rescue Exception => error
- output.report_error(step, error)
- ensure
- context.After.call if context.After
- QED.After.call if QED.After
+ # Open file and translate template into HTML text.
+ def to_html
+ #case file
+ #when /^http/
+ # ext = File.extname(file).sub('.','')
+ # Tilt[ext].new{ source }
+ #else
+ #end
+ if File.extname(file) == '.html'
+ File.read(file)
+ else
+ Tilt.new(file).render
end
end
-=begin
- #
- def run_table(step)
- file = context.table
- Dir.ascend(Dir.pwd) do |path|
- f1 = File.join(path, file)
- f2 = File.join(path, 'fixtures', file)
- fr = File.file?(f1) ? f1 : File.exist?(f2) ? f2 : nil
- (file = fr; break) if fr
- end
- output.report_pass(step) #step)
-
- tbl = YAML.load(File.new(file))
- key = tbl.shift
- tbl.each do |set|
- assign = key.zip(set).map{ |k, v| "#{k}=#{v.inspect};" }.join
- run_table_step(assign + step, set)
- #run_step(set.inspect.tabto(4)){ blk.call(set) }
- #@_script.run_step(set.to_yaml.tabto(2)){ blk.call(set) }
- #@_script.output.report_table(set)
- end
- #output.report_pass(step) #step)
- context.table = nil
+ # Open, convert to HTML and cache.
+ def html
+ @html ||= to_html
end
#
- #def run_table_step(step, set)
- def run_table_step(set, &blk)
- context.before.call if context.before
- begin
- #eval(step, context._binding, @file) # TODO: would be nice to know file and lineno here
- blk.call(*set)
- output.report_pass(' ' + set.inspect) #step)
- rescue Assertion => error
- output.report_fail(set.inspect, error)
- rescue Exception => error
- output.report_error(set.inspect, error)
- ensure
- context.after.call if context.after
- end
- end
-=end
-
- # Cut-up script into steps.
- def steps
- @steps ||= (
- code = false
- str = ''
- steps = []
- @source.each_line do |line|
- if /^\s*$/.match line
- str << line
- elsif /^[=]/.match line
- steps << str #.chomp("\n")
- steps << line #.chomp("\n")
- str = ''
- #str << line
- code = false
- elsif /^\S/.match line
- if code
- steps << str #.chomp("\n")
- str = ''
- str << line
- code = false
- else
- str << line
- end
- else
- if code
- str << line
- else
- steps << str
- str = ''
- str << line
- code = true
- end
- end
- end
- steps << str
- #steps.map{ |s| s.chomp("\n") }
- steps
- )
- end
-
- # The run context.
- def context
- @context ||= Context.new(self)
- end
-
- #
- def import(helper)
- code = File.read(helper)
- eval(code, context._binding)
- end
-
- private
-
- # Splits the document into main source and footer
- # and extract the helper document references from
- # the footer.
- #
- def parse_document(file)
- text = File.read(file)
- index = text.rindex('---') || text.size
- source = text[0...index]
- footer = text[index+3..-1].to_s.strip
- helpers = parse_helpers(footer)
- @source = source
- @helpers = helpers
- end
-
- #
- def parse_helpers(footer)
- helpers = []
- footer.split("\n").each do |line|
- next if line.strip == ''
- case line
- when /\[(.*?)\]\((.*?)\)/
- helpers << $2
- when /(.*?)\[(.*?)\]/
- helpers << $2
- end
- end
- helpers
- end
-
-=begin
- # Looks for a master +helper.rb+ file and a special
- # helpers/<name>.rb file. Both of these, when found, will
- # be imported when this script is run.
-
- def collect_helpers
- dir = File.dirname(file)
- list = []
- list << "helper.rb" if File.exist?(File.join(dir, "helper.rb"))
- list << "helpers/#{name}.rb" if File.exist?(File.join(dir, "helpers/#{name}.rb"))
- list
- end
-=end
-
- # TODO: How to determine where to find the env.rb file?
- #def require_environment
- # dir = File.dirname(file)
- # dir = File.expand_path(dir)
- # env = loop do
- # file = File.join(dir, 'env.rb')
- # break file if File.exist?(file)
- # break nil if ['demo', 'demos', 'doc', 'docs', 'test', 'tests'].include? File.basename(dir)
- # break nil if dir == Dir.pwd
- # dir = File.dirname(dir)
- # end
- # require(env) if env
+ #def source
+ # @source ||= (
+ # #case file
+ # #when /^http/
+ # # ext = File.extname(file).sub('.','')
+ # # open(file)
+ # #else
+ # File.read(file)
+ # #end
+ # )
#end
- # FIXME: where to stop looking for helpers.
- def import_helpers
- hlp = []
- dir = Dir.pwd #File.expand_path(dir)
- env = loop do
- helpers.each do |helper|
- file = File.join(dir, 'helpers', helper)
- if File.exist?(file)
- hlp << file
- end
- end
- break if ['qed', 'demo', 'demos', 'doc', 'docs', 'test', 'tests'].include? File.basename(dir)
- dir = File.dirname(dir)
- end
- hlp.each{ |helper| import(helper) }
- end
-
- end
-
- #
- class Context < Module
-
- TABLE = /^TABLE\[(.*?)\]/i
-
- def initialize(script)
- @_script = script
- @_when = []
- @_tables = []
- end
-
- def _binding
- @_binding ||= binding
- end
-
- # Before each step.
- def Before(&procedure)
- @_before = procedure if procedure
- @_before
- end
-
- # After each step.
- def After(&procedure)
- @_after = procedure if procedure
- @_after
- end
-
- # Run code around each step.
#
- # Around procedures must take a block, in which the step is run.
- #
- # Around do |&step|
- # ... do something here ...
- # step.call
- # ... do stiff stuff ...
- # end
- #
- #def Around(&procedure)
- # @_around = procedure if procedure
- # @_around
- #end
-
- # Comment match procedure.
- #
- # This is useful for creating unobtrusive setup and (albeit more
- # limited) teardown code. A pattern is matched against each comment
- # as it is processed. If there is match, the code procedure is
- # triggered, passing in any mathcing expression arguments.
- #
- def When(pattern=nil, &procedure)
- return @_when unless procedure
- raise ArgumentError unless pattern
- unless Regexp === pattern
- pattern = __when_string_to_regexp(pattern)
- end
- @_when << [pattern, procedure]
+ def run(*observers)
+ evaluator = Evaluator.new(self, *observers)
+ evaluator.run
end
- # Code match-and-transform procedure.
#
- # This is useful to transform human readable code examples
- # into proper exectuable code. For example, say you want to
- # run shell code, but want to make if look like typical
- # shelle examples:
- #
- # $ cp fixture/a.rb fixture/b.rb
- #
- # You can use a transform to convert lines starting with '$'
- # into executable Ruby using #system.
- #
- # system('cp fixture/a.rb fixture/b.rb')
- #
- #def Transform(pattern=nil, &procedure)
- #
- #end
-
- # Table-based steps.
- def Table(file=nil, &blk)
- file = file || @_tables.last
- tbl = YAML.load(File.new(file))
- tbl.each do |set|
- blk.call(*set)
- end
- @_tables << file
+ def environment
+ glob = File.join(dir, '{environment,common,shared}', '*')
+ Dir[glob]
end
- # Read/Write a fixture.
- def Data(file, &content)
- raise if File.directory?(file)
- if content
- FileUtils.mkdir_p(File.dirname(fname))
+ #
+ def apply_environment
+ environment.each do |file|
case File.extname(file)
- when '.yml', '.yaml'
- File.open(file, 'w'){ |f| f << content.call.to_yaml }
+ when '.rb'
+ eval(File.read(file), scope.__binding__, file)
else
- File.open(file, 'w'){ |f| f << content.call }
+ Script.new(file, scope).run
end
- else
- #raise LoadError, "no such fixture file -- #{fname}" unless File.exist?(fname)
- case File.extname(file)
- when '.yml', '.yaml'
- YAML.load(File.new(file))
- else
- File.read(file)
- end
end
end
-
- private
-
- def __when_string_to_regexp(str)
- str = str.split(/(\(\(.*?\)\))(?!\))/).map{ |x|
- x =~ /\A\(\((.*)\)\)\z/ ? $1 : Regexp.escape(x)
- }.join
- str = str.gsub(/(\\\ )+/, '\s+')
- Regexp.new(str, Regexp::IGNORECASE)
-
- #rexps = []
- #str = str.gsub(/\(\((.*?)\)\)/) do |m|
- # rexps << '(' + $1 + ')'
- # "\0"
- #end
- #str = Regexp.escape(str)
- #rexps.each do |r|
- # str = str.sub("\0", r)
- #end
- #str = str.gsub(/(\\\ )+/, '\s+')
- #Regexp.new(str, Regexp::IGNORECASE)
- end
-
- #
- # check only local and maybe start paths
- #def __locate_file(file)
- # Dir.ascend(Dir.pwd) do |path|
- # f1 = File.join(path, file)
- # f2 = File.join(path, 'fixtures', file)
- # fr = File.file?(f1) ? f1 : File.exist?(f2) ? f2 : nil
- # (file = fr; break) if fr
- # end
- #end
end
end