# encoding: utf-8 module Watir module HTML class SpecExtractor class InterfaceNotFound < StandardError end def initialize(uri) @uri = uri end def process download_and_parse extract_idl_parts extract_interface_map build_result rescue p errors raise end def errors @errors ||= [] end # # returns a topoligically sorted array of WebIDL::Ast::Interface objects # def sorted_interfaces process if @interfaces.nil? sorter.sort.map { |name| @interfaces.find { |i| i.name == name } or puts "ignoring interface: #{name}" }.compact end def print_hierarchy process if @interfaces.nil? sorter.print end def fetch_interface(interface) @interfaces_by_name[interface] or raise InterfaceNotFound, "#{interface} not found in IDL" end private def download_and_parse open(@uri) { |io| @doc = Nokogiri.HTML(io) } end def extract_idl_parts parsed = @doc.search("//pre[@class='idl']").map { |e| parse_idl(e.inner_text) }.compact implements = [] @interfaces = [] parsed.flatten.each do |element| case element when WebIDL::Ast::Interface @interfaces << element when WebIDL::Ast::ImplementsStatement implements << element end end @interfaces_by_name = @interfaces.group_by(&:name) apply_implements(implements) merge_interfaces end def extract_interface_map # http://www.whatwg.org/specs/web-apps/current-work/#elements-1 table = @doc.search("//h3[@id='elements-3']/following-sibling::table[1]").first table or raise "could not find elements-3 table" @interface_map = {} parse_table(table).each do |row| row['Element'].split(", ").each { |tag| @interface_map[tag] = row['Interface'] } end end def build_result # tag name => Interface instance(s) result = {} @interface_map.each do |tag, interface| result[tag] = fetch_interface(interface) end # missing from the elements-1 table result['frameset'] = fetch_interface('HTMLFrameSetElement') result end def parse_table(table) headers = table.css("thead th").map { |e| e.inner_text.strip } table.css("tbody tr").map do |row| result = {} row.css("th, td").each_with_index do |node, idx| result[headers[idx]] = node.inner_text.strip end result end end def parse_idl(str) result = idl_parser.parse(str) if result result.build else errors << idl_parser.failure_reason nil end end def apply_implements(implements) implements.each do |is| implementor_name = is.implementor.gsub(/^::/, '') implementee_name = is.implementee.gsub(/^::/, '') begin intf = fetch_interface(implementor_name).first intf.implements << fetch_interface(implementee_name).first rescue InterfaceNotFound => ex puts ex.message end end end def merge_interfaces non_duplicates = @interfaces.uniq(&:name) duplicates = @interfaces - non_duplicates duplicates.each do |intf| final = non_duplicates.find { |i| i.name == intf.name } final.inherits += intf.inherits final.members += intf.members final.extended_attributes += intf.extended_attributes end @interfaces = non_duplicates end def idl_parser @idl_parser ||= WebIDL::Parser::IDLParser.new end def sorter @idl_sorter ||= IDLSorter.new(@interfaces) end end # SpecExtractor end # HTML end # Watir