verbose(true) class DocsetGenerator require 'rubygems' require 'nokogiri' require 'fileutils' def parse_html_docref(node) code = '' code << node.xpath(".//p[@class='abstract']").text code << "\n" code << node.xpath(".//div[@class='api discussion']").text.sub(/^Discussion/, '') code.strip! code.gsub!(/^/m, ' # ') code << "\n" return code end def parse_type(type) type = type.to_s type.strip! star = type.sub!(/\s*\*$/, '') # Remove pointer star. case type when /\*$/ # A double pointer, in MacRuby this becomes a Pointer. 'Pointer' when 'id' 'Object' when 'void' 'nil' when 'SEL' 'Symbol' when 'bool', 'BOOL' 'Boolean' when 'float', 'double', 'CGFloat' 'Float' when 'int', 'short', 'long', 'long long', 'NSInteger', 'NSUInteger' 'Integer' when 'NSString', 'NSMutableString' 'String' when 'NSArray', 'NSMutableArray' 'Array' when 'NSDictionary', 'NSMutableDictionary' 'Hash' else type end end def parse_html_class(name, doc) # Find superclass (mandatory). sclass = nil doc.xpath("//table[@class='specbox']/tr").each do |node| if md = node.text.match(/Inherits from([^ ]+)/) sclass = md[1] break end end return nil unless sclass code = doc.xpath(".//p[@class='abstract']")[0].text code.gsub!(/^/m, '# ') code << "\nclass #{name} < #{sclass}\n\n" # Properties. doc.xpath("//div[@class='api propertyObjC']").each do |node| decl = node.xpath(".//div[@class='declaration']/div[@class='declaration']").text readonly = decl.include?('readonly') decl.sub!(/@property\s*(\([^\)]+\))?/, '') md = decl.match(/(\w+)$/) next unless md title = md[1] type = md.pre_match code << parse_html_docref(node) code << " # @return [#{parse_type(type)}]\n" code << ' ' << (readonly ? "attr_reader" : "attr_accessor") << " :#{title}\n\n" end # Methods. methods = [] methods.concat(doc.xpath("//div[@class='api classMethod']")) methods.concat(doc.xpath("//div[@class='api instanceMethod']")) methods.each do |node| decl = node.xpath(".//div[@class='declaration']").text types = decl.scan(/\(([^)]+)\)/) ret_type = types.shift # Docref. code << parse_html_docref(node) # Parameters and return value. arg_names = node.xpath(".//div[@class='api parameters']//dt") arg_docs = node.xpath(".//div[@class='api parameters']//dd") if arg_names.size == arg_docs.size has_types = types.size == arg_names.size arg_names.each_with_index do |arg_name, i| arg_doc = arg_docs[i] code << " # @param " code << "[#{parse_type(types[i])}] " if has_types code << "#{arg_name.text} #{arg_doc.text}\n" end end retdoc = node.xpath(".//div[@class='return_value']/p").text.strip code << " # @return " code << "[#{parse_type(ret_type)}] " if ret_type code << "#{retdoc}" unless retdoc.empty? code << "\n" is_class_method = decl.match(/^\s*\+/) != nil decl.sub!(/^\s*[\+\-]/, '') # Remove method qualifier. sel_parts = decl.gsub(/\([^)]+\)/, '').split.map { |x| x.split(':') } head = sel_parts.shift code << " def #{is_class_method ? 'self.' : ''}#{head[0]}(" code << "#{head[1]}" if head.size > 1 unless sel_parts.empty? code << ', ' code << sel_parts.map { |part| "#{part[0]}:#{part[1]}" }.join(', ') end code << "); end\n\n" end code << "end" return code end def parse_html_data(data) doc = Nokogiri::HTML(data) title = doc.xpath('/html/head/title') if title and md = title.text.match(/^(.+)Class Reference$/) parse_html_class(md[1].strip, doc) else nil end end def initialize(outpath, paths) @input_paths = [] paths.each do |path| if File.directory?(path) @input_paths.concat(Dir.glob(path + '/**/*.html')) else @input_paths << path end end @outpath = outpath end def run rb_files_dir = '/tmp/rb_docset' FileUtils.rm_rf(rb_files_dir) FileUtils.mkdir_p(rb_files_dir) @input_paths.map { |path| parse_html_data(File.read(path)) }.compact.each_with_index do |code, n| File.open(File.join(rb_files_dir, "t#{n}.rb"), 'w') do |io| io.write(code) end end sh "yard doc #{rb_files_dir}" sh "mv doc \"#{@outpath}\"" end end require 'redcloth' class GuideGenerator < RedCloth def initialize(src, dest, no_header_footer) base = File.basename(src, '.textile') @title = base.gsub(/([a-z])([A-Z])/, '\1 \2') @dest = dest @no_header_footer = no_header_footer data = File.read(src) super(fix_anchors(data)) end def run File.open(@dest, 'w') do |io| unless @no_header_footer io.puts html_header #io.puts toc io.puts '