about summary refs log tree commit homepage
path: root/lib/wrongdoc
diff options
context:
space:
mode:
authorEric Wong <normalperson@yhbt.net>2010-12-24 00:24:59 +0000
committerEric Wong <normalperson@yhbt.net>2010-12-24 01:06:37 +0000
commit24415920ff7ee31b294d785e776a2a7dd2ad6367 (patch)
tree30e0bac3f2c6944c6705b73ddc4c668944b4c3f8 /lib/wrongdoc
downloadwrongdoc-24415920ff7ee31b294d785e776a2a7dd2ad6367.tar.gz
wrongdoc 1.0.0 - initial release v1.0.0
Welcome to hell
Diffstat (limited to 'lib/wrongdoc')
-rw-r--r--lib/wrongdoc/changelog.rb25
-rw-r--r--lib/wrongdoc/final.rb105
-rw-r--r--lib/wrongdoc/gemspec.rb18
-rw-r--r--lib/wrongdoc/history.rb50
-rw-r--r--lib/wrongdoc/merge.rb20
-rw-r--r--lib/wrongdoc/news_atom.rb46
-rw-r--r--lib/wrongdoc/news_rdoc.rb30
-rw-r--r--lib/wrongdoc/parse_xml.rb11
-rw-r--r--lib/wrongdoc/prepare.rb17
-rw-r--r--lib/wrongdoc/rdoc.rb68
-rw-r--r--lib/wrongdoc/rdoc_options.rb11
-rw-r--r--lib/wrongdoc/readme.rb20
-rw-r--r--lib/wrongdoc/release.rb44
13 files changed, 465 insertions, 0 deletions
diff --git a/lib/wrongdoc/changelog.rb b/lib/wrongdoc/changelog.rb
new file mode 100644
index 0000000..f16bfbf
--- /dev/null
+++ b/lib/wrongdoc/changelog.rb
@@ -0,0 +1,25 @@
+# helper method for generating the ChangeLog in RDoc format atomically
+module Wrongdoc::Changelog
+  include Wrongdoc::History
+
+  def changelog
+    fp = Tempfile.new('ChangeLog', '.')
+    fp.write "ChangeLog from #@cgit_uri"
+    cmd = %w(git log)
+    if @changelog_start && tags[0]
+      range = "#@changelog_start..#{tags[0][:tag]}"
+      fp.write(" (#{range})")
+      cmd << range
+    end
+    fp.write("\n\n")
+    prefix = "   "
+    IO.popen(cmd.join(' ')) do |io|
+      io.each { |line|
+        fp.write prefix
+        fp.write line
+      }
+    end
+    File.rename(fp.path, 'ChangeLog')
+    fp.close!
+  end
+end
diff --git a/lib/wrongdoc/final.rb b/lib/wrongdoc/final.rb
new file mode 100644
index 0000000..7070de8
--- /dev/null
+++ b/lib/wrongdoc/final.rb
@@ -0,0 +1,105 @@
+require 'find'
+require 'fileutils'
+
+class Wrongdoc::Final
+  include Wrongdoc::ParseXML
+  include Wrongdoc::NewsAtom
+
+  def run
+    Find.find('doc') { |path| /\.html\z/ =~ path and fix(path) }
+    FileUtils.rm_rf('doc/js')
+    news_atom
+  end
+
+  def initialize(opts, git_tag = nil)
+    @cgit_uri = URI.parse(opts[:cgit_url])
+    @rdoc_uri = URI.parse(opts[:rdoc_url])
+    @git_tag = git_tag
+  end
+
+  # returns a cgit URI for the given +path+ and +lineno+
+  def path_uri(path, lineno)
+    uri = @cgit_uri.dup
+    uri.path += "/tree/#{URI.escape(path)}"
+    uri.fragment = "n#{lineno}"
+    uri.query = "id=#{URI.escape(@git_tag)}" if @git_tag
+    uri
+  end
+
+  # delete all the stuff that offends us
+  def killkillkill!(doc)
+    unlink = proc { |node| node.unlink }
+
+    # JavaScript is dangerous
+    doc.search("script").each(&unlink)
+
+    # if your project's big enough to need JS search, it's too bloated
+    doc.search('span.search-toggle').each(&unlink)
+    doc.search('form').each(&unlink)
+
+    # remove W3C validator link, we use tidy instead
+    doc.search('div#validator-badges p').each { |x|
+      /Validate/i =~ x.content and x.unlink
+    }
+  end
+
+  # since we killed off JavaScript, viewing source isn't possible with
+  # RDoc anymore, so link people to the web source viewer
+  def source_linkify!(doc)
+    doc.search('div.method-detail').each { |mdetail|
+      path = lineno = nil
+      mdetail.search('div.method-source-code').each { |src|
+        src.search('span.ruby-comment').each { |x|
+          if x.content =~ /File\s+(\S+),\s+line\s+(\d+)/s
+            path, lineno = $1, $2
+          end
+        }
+        src.unlink if path && lineno
+      }
+      if path && lineno
+        mdetail.search('span.method-click-advice').each { |x|
+          x.content = ''
+          a = Nokogiri::XML::Node.new('a', doc)
+          a['href'] = path_uri(path, lineno).to_s
+          a.content = 'view method source'
+          x.add_child(a)
+        }
+      end
+    }
+  end
+
+  # Don't give the original Darkfish a bad name, and advertise ourselves :)
+  def advertise!(doc)
+    doc.search('div#validator-badges p small').each { |x|
+      if /\AGenerated/ =~ x.content
+        first = x.children.first
+        first.content = first.content.gsub /\AG/, "Originally g"
+        last = x.children.last
+        last.content = "#{last.content}, modified by "
+
+        a = Nokogiri::XML::Node.new('a', doc)
+        a["href"] = "http://bogomips.org/wrongdoc/"
+        a.content = "wrongdoc"
+        last.add_next_sibling(a)
+      end
+    }
+  end
+
+  def fix(file)
+    File.open(file, "a+") do |fp|
+      buf = process(fp.read)
+      fp.truncate 0
+      fp.write buf
+    end
+  end
+
+  # the main entry point, this does all the require processing on any
+  # given String buffer.
+  def process(str)
+    doc = parse_xml(str)
+    killkillkill!(doc)
+    source_linkify!(doc)
+    advertise!(doc)
+    doc.to_xhtml(:indent => 0)
+  end
+end
diff --git a/lib/wrongdoc/gemspec.rb b/lib/wrongdoc/gemspec.rb
new file mode 100644
index 0000000..0d31ffd
--- /dev/null
+++ b/lib/wrongdoc/gemspec.rb
@@ -0,0 +1,18 @@
+# helper methods for gemspecs
+module Wrongdoc::Gemspec
+  include Wrongdoc::Readme
+  include Wrongdoc::RdocOptions
+
+  def extra_rdoc_files(manifest)
+    File.readlines('.document').map! do |x|
+      x.chomp!
+      if File.directory?(x)
+        manifest.grep(%r{\A#{x}/})
+      elsif File.file?(x)
+        x
+      else
+        nil
+      end
+    end.flatten.compact
+  end
+end
diff --git a/lib/wrongdoc/history.rb b/lib/wrongdoc/history.rb
new file mode 100644
index 0000000..3443ca3
--- /dev/null
+++ b/lib/wrongdoc/history.rb
@@ -0,0 +1,50 @@
+module Wrongdoc::History
+  def initialize_history
+    @tags = @old_summaries = nil
+  end
+
+  # returns a cgit URI for a given +tag_name+
+  def tag_uri(tag_name)
+    uri = @cgit_uri.dup
+    uri.path = "/tag/"
+    uri.query = "id=#{tag_name}"
+    uri
+  end
+
+  # TODO: investigate Ruby git libraries
+  def tags
+    timefmt = '%Y-%m-%dT%H:%M:%SZ'
+    @tags ||= `git tag -l`.split(/\n/).map do |tag|
+      next if tag == "v0.0.0"
+      if %r{\Av[\d\.]+} =~ tag
+        header, subject, body = `git cat-file tag #{tag}`.split(/\n\n/, 3)
+        header = header.split(/\n/)
+        tagger = header.grep(/\Atagger /).first
+        body ||= "initial"
+        time = Time.at(tagger.split(/ /)[-2].to_i).utc
+        {
+          :time => time.strftime(timefmt),
+          :ruby_time => time,
+          :tagger_name => %r{^tagger ([^<]+)}.match(tagger)[1].strip,
+          :tagger_email => %r{<([^>]+)>}.match(tagger)[1].strip,
+          :id => `git rev-parse refs/tags/#{tag}`.chomp!,
+          :tag => tag,
+          :subject => subject,
+          :body => (old = old_summaries[tag]) ? "#{old}\n#{body}" : body,
+        }
+      end
+    end.compact.sort { |a,b| b[:time] <=> a[:time] }
+  end
+
+  def old_summaries
+    @old_summaries ||= if File.exist?(".CHANGELOG.old")
+      File.readlines(".CHANGELOG.old").inject({}) do |hash, line|
+        version, summary = line.split(/ - /, 2)
+        hash[version] = summary
+        hash
+      end
+    else
+      {}
+    end
+  end
+end
diff --git a/lib/wrongdoc/merge.rb b/lib/wrongdoc/merge.rb
new file mode 100644
index 0000000..b51ee5a
--- /dev/null
+++ b/lib/wrongdoc/merge.rb
@@ -0,0 +1,20 @@
+class Wrongdoc::Merge
+  include Wrongdoc::ParseXML
+
+  def initialize(opts)
+    @merge_html = opts[:merge_html]
+  end
+
+  def run
+    @merge_html.each do |file, source|
+      rdoc_html = "doc/#{file}.html"
+      src = Nokogiri::XML(File.read(source))
+      File.open(rdoc_html, "a+") { |fp|
+        doc = parse_xml(fp.read)
+        doc.search("div#documentation")[0].add_child(src.root)
+        fp.truncate 0
+        fp.write doc.to_xhtml
+      }
+    end
+  end
+end
diff --git a/lib/wrongdoc/news_atom.rb b/lib/wrongdoc/news_atom.rb
new file mode 100644
index 0000000..1e7a781
--- /dev/null
+++ b/lib/wrongdoc/news_atom.rb
@@ -0,0 +1,46 @@
+module Wrongdoc::NewsAtom
+  include Wrongdoc::History
+  include Wrongdoc::Readme
+
+  # generates an Atom feed based on git tags in the document directory
+  def news_atom
+    project_name, short_desc, _ = x = readme_metadata
+    new_tags = tags[0,10]
+    atom_uri = @rdoc_uri.dup
+    atom_uri.path += "NEWS.atom.xml"
+    news_uri = @rdoc_uri.dup
+    news_uri.path += "NEWS.html"
+    doc = Nokogiri::XML::Builder.new {
+      feed :xmlns => "http://www.w3.org/2005/Atom" do
+        id! atom_uri.to_s
+        title "#{project_name} news"
+        subtitle short_desc
+        link! :rel => 'alternate', :type => 'text/html', :href => news_uri.to_s
+        updated new_tags.empty? ? '1970-01-01:00:00:00Z' : new_tags[0][:time]
+        new_tags.each do |tag|
+          entry {
+            title tag[:subject]
+            updated tag[:time]
+            published tag[:time]
+            author {
+              name tag[:tagger_name]
+              email tag[:tagger_email]
+            }
+            uri = tag_uri(tag[:tag]).to_s
+            link! :rel => "alternate", :type => "text/html", :href => uri
+            id! uri
+            message_only = tag[:body].split(/\n.+\(\d+\):\n {6}/s)[0].strip
+            content({:type =>:text}, message_only)
+            content(:type =>:xhtml) { pre tag[:body] }
+          }
+        end
+      end
+    }
+    fpath = "doc/NEWS.atom.xml"
+    File.open(fpath, "w") { |fp| fp.write doc.to_xml(:indent => 0) }
+    unless new_tags.empty?
+      time = new_tags[0][:ruby_time]
+      File.utime(time, time, fpath)
+    end
+  end
+end
diff --git a/lib/wrongdoc/news_rdoc.rb b/lib/wrongdoc/news_rdoc.rb
new file mode 100644
index 0000000..ee21d6b
--- /dev/null
+++ b/lib/wrongdoc/news_rdoc.rb
@@ -0,0 +1,30 @@
+# -*- encoding: utf-8 -*-
+module Wrongdoc::NewsRdoc
+  include Wrongdoc::History
+
+  def puts_tag(fp, tag)
+    time = tag[:time].tr!('T', ' ').gsub!(/:\d\dZ/, ' UTC')
+    fp.puts "=== #{tag[:subject]} / #{time}"
+    fp.puts ""
+
+    body = tag[:body]
+    fp.puts tag[:body].gsub(/^/smu, "  ").gsub(/[ \t]+$/smu, "")
+    fp.puts ""
+  end
+
+  # generates a NEWS file in the top-level directory based on git tags
+  def news_rdoc
+    news = Tempfile.new('NEWS', '.')
+    tags.each { |tag| puts_tag(news, tag) }
+    File.open("LATEST", "wb") { |latest|
+      if tags.empty?
+        latest.puts "Currently unreleased"
+        news.puts "No news yet."
+      else
+        puts_tag(latest, tags[0])
+      end
+    }
+    File.rename(news.path, 'NEWS')
+    news.close!
+  end
+end
diff --git a/lib/wrongdoc/parse_xml.rb b/lib/wrongdoc/parse_xml.rb
new file mode 100644
index 0000000..6d7abe2
--- /dev/null
+++ b/lib/wrongdoc/parse_xml.rb
@@ -0,0 +1,11 @@
+module Wrongdoc::ParseXML
+  def parse_xml(str)
+    opts = {
+      :input_encoding => 'utf8',
+      :output_encoding => 'utf8',
+      :wrap => 0,
+      :tidy_mark => false,
+    }
+    Nokogiri::XML(TidyFFI::Tidy.new(str, opts).clean)
+  end
+end
diff --git a/lib/wrongdoc/prepare.rb b/lib/wrongdoc/prepare.rb
new file mode 100644
index 0000000..85922ad
--- /dev/null
+++ b/lib/wrongdoc/prepare.rb
@@ -0,0 +1,17 @@
+class Wrongdoc::Prepare
+  include Wrongdoc::NewsRdoc
+  include Wrongdoc::Changelog
+  include Wrongdoc::Readme
+
+  def initialize(opts)
+    @rdoc_uri = URI.parse(opts[:rdoc_url])
+    @cgit_uri = URI.parse(opts[:cgit_url])
+    @changelog_start = opts[:changelog_start]
+    @name, @short_desc = readme_metadata
+  end
+
+  def run
+    news_rdoc
+    changelog
+  end
+end
diff --git a/lib/wrongdoc/rdoc.rb b/lib/wrongdoc/rdoc.rb
new file mode 100644
index 0000000..0b54aef
--- /dev/null
+++ b/lib/wrongdoc/rdoc.rb
@@ -0,0 +1,68 @@
+# we won't deal with the non-Darkfish RDoc in older rubies, it has frames :<
+if (!defined?(RUBY_ENGINE) || RUBY_ENGINE == "ruby") && \
+   RUBY_VERSION.to_f <= 1.8
+  require 'rubygems'
+  gem 'rdoc', '~> 3.0.1'
+end
+require 'rdoc/rdoc'
+
+class Wrongdoc::Rdoc
+  include Wrongdoc::RdocOptions
+  include Wrongdoc::ParseXML
+
+  def initialize(opts)
+    @rdoc_uri = URI.parse(opts[:rdoc_url])
+    @cgit_uri = URI.parse(opts[:cgit_url])
+  end
+
+  def run(argv = [])
+    rdoc(argv)
+    add_atom("doc/ChangeLog.html", cgit_atom_uri)
+    add_atom("doc/NEWS.html", news_atom_uri)
+    add_atom("doc/README.html", news_atom_uri)
+
+    # the stock RDoc index page layout lacks a vertical sidebar full of links
+    rdoc_index = "doc/rdoc_index.html"
+    File.exist?(rdoc_index) and File.unlink(rdoc_index)
+    File.rename("doc/index.html", rdoc_index)
+    File.link("doc/README.html", "doc/index.html")
+  end
+
+  def rdoc(argv)
+    r = RDoc::RDoc.new
+    r.document(rdoc_options.concat(argv))
+  end
+
+  def add_atom(path, atom_uri)
+    File.open(path, "a+") do |fp|
+      doc = parse_xml(fp.read)
+      doc.search("title").each { |t|
+        t.add_next_sibling(atom_node(doc, atom_uri))
+      }
+      fp.truncate 0
+      fp.write doc.to_xhtml
+    end
+  end
+
+  def cgit_atom_uri
+    uri = @cgit_uri.dup
+    uri.path += "/atom/"
+    uri.query = "h=master"
+    uri
+  end
+
+  def news_atom_uri
+    uri = @rdoc_uri.dup
+    uri.path += "NEWS.atom.xml"
+    uri
+  end
+
+  def atom_node(doc, uri, title = 'Atom feed')
+    link = Nokogiri::XML::Node.new('link', doc)
+    link['rel'] = 'alternate'
+    link['title'] = title
+    link['href'] = uri.to_s
+    link['type'] = 'application/atom+xml'
+    link
+  end
+end
diff --git a/lib/wrongdoc/rdoc_options.rb b/lib/wrongdoc/rdoc_options.rb
new file mode 100644
index 0000000..15474f4
--- /dev/null
+++ b/lib/wrongdoc/rdoc_options.rb
@@ -0,0 +1,11 @@
+module Wrongdoc::RdocOptions
+  include Wrongdoc::Readme
+
+  def rdoc_options
+    webcvs = URI.parse(Wrongdoc.config[:cgit_url])
+    webcvs.path += "/tree"
+    webcvs = "#{webcvs}/%s"
+    _, _, title = readme_metadata
+    [ '-t', title, '-W', webcvs ]
+  end
+end
diff --git a/lib/wrongdoc/readme.rb b/lib/wrongdoc/readme.rb
new file mode 100644
index 0000000..8c5f5b6
--- /dev/null
+++ b/lib/wrongdoc/readme.rb
@@ -0,0 +1,20 @@
+# helpers for parsing the top-level README file (no suffix support :P)
+module Wrongdoc::Readme
+
+  # returns a one-paragraph summary from the README
+  def readme_description
+    File.read("README").split(/\n\n/)[1]
+  end
+
+  # parses the README file in the top-level directory for project metadata
+  def readme_metadata
+    l = File.readlines("README")[0].strip!
+    l.gsub!(/^=\s+/, '') or abort "#{l.inspect} doesn't start with '='"
+    title = l.dup
+    if l.gsub!(/^(\w+\!)\s+/, '') # Rainbows!
+      return $1, l, title
+    else
+      return (l.split(/\s*[:-]\s*/, 2)).push(title)
+    end
+  end
+end
diff --git a/lib/wrongdoc/release.rb b/lib/wrongdoc/release.rb
new file mode 100644
index 0000000..f6c72e5
--- /dev/null
+++ b/lib/wrongdoc/release.rb
@@ -0,0 +1,44 @@
+module Wrongdoc::Release
+  self.extend Wrongdoc::History
+
+  def self.changes(io)
+    vtags = tags.map { |tag| tag[:tag] =~ /\Av/ and tag[:tag] }.sort
+    cmds = []
+    if vtags.empty?
+      cmds << %w(git log)
+    else
+      version = vtags[0]
+      prev = vtags[vtags.index(version) - 1]
+      if prev
+        cmds << [ 'git', 'diff', '--stat', prev, version ]
+        cmds << [ 'git', 'log', "#{prev}..#{version}" ]
+      else
+        cmds << [ 'git', 'log', version ]
+      end
+    end
+
+    io.sync = true
+    cmds.each_with_index do |cmd,i|
+      i > 0 and io.puts
+      pid, status = Process.waitpid2(fork do
+        $stdout.reopen(io)
+        io.close
+        exec *cmd
+      end)
+      status.success? or abort status.inspect
+    end
+  end
+
+  def self.notes(io, opts)
+    spec = Gem::Specification.load(Dir['*.gemspec'][0])
+    io.puts spec.description.strip
+    io.puts
+    io.puts "* #{spec.homepage}"
+    io.puts "* #{spec.email}"
+    io.puts "* #{opts[:git_url] || opts[:cgit_url]}"
+
+    _, _, body = `git cat-file tag v#{spec.version}`.split(/\n\n/, 3)
+    io.print "\nChanges:\n\n"
+    io.puts body
+  end
+end