desc 'Build all release announcements.' task :ann => %w[ ann:html ann:text ann:feed ] # it has long been a tradition to use an "[ANN]" prefix # when announcing things on the ruby-talk mailing list @ann_subject_prefix = '[ANN] ' task :@ann_subject do unless @ann_subject Rake::Task[:@project].invoke @ann_subject = @ann_subject_prefix + @project_module::PROJECT + ' ' + @project_module::VERSION end end # fetch project description from manual task :@ann_nfo_html_nodes do unless @ann_nfo_html_nodes begin head, body = fetch_nodes_between('h2#ABOUT', 'h1,h2,h3,h4,h5,h6') rescue => error error.message.insert 0, "The manual lacks a

ABOUT heading.\n" raise error end @ann_nfo_html_nodes = body end end task :@ann_nfo_text do unless @ann_nfo_text Rake::Task[:@ann_nfo_html_nodes].invoke @ann_nfo_text = nodes_inner_text(@ann_nfo_html_nodes) end end # fetch release notes from manual task :@ann_rel_html_body_nodes do unless @ann_rel_html_body_nodes begin head, body = fetch_nodes_between('h2#VERSIONS ~ h3', 'h1,h2,h3') rescue => error error.message.insert 0, "The manual lacks a

heading under a

VERSIONS heading.\n" raise error end @ann_rel_html_title_node = head @ann_rel_html_body_nodes = body end end # fetch authors list from manual task :@project_authors_html_nodes do unless @project_authors_html_nodes begin head, body = fetch_nodes_between('h2#AUTHORS,h2#CREDITS', 'h1,h2,h3,h4,h5,h6') rescue => error error.message.insert 0, "The manual lacks content under a

AUTHORS or CREDITS heading.\n" raise error end @project_authors_html_nodes = body end end task :@project_authors_text do unless @project_authors_text Rake::Task[:@project_authors_html_nodes].invoke @project_authors_text = nodes_inner_text(@project_authors_html_nodes) end end # build release announcement task :@ann_html do unless @ann_html Rake::Task[:@ann_nfo_html_nodes].invoke Rake::Task[:@ann_rel_html_body_nodes].invoke @ann_html = %{

#{@project_module::PROJECT}

#{@project_module::TAGLINE}

#{@project_module::WEBSITE}

#{@ann_nfo_html_nodes.join} #{@ann_rel_html_body_nodes.join} }.strip @ann_html = resolve_html_links(@ann_html) end end task :@ann_text do unless @ann_text Rake::Task[:@ann_html].invoke @ann_text = convert_html_to_text(@ann_html) end end #----------------------------------------------------------------------------- # HTML #----------------------------------------------------------------------------- @ann_html_dst = 'ann.html' desc 'Build HTML announcement.' task 'ann:html' => @ann_html_dst file @ann_html_dst => @man_src do Rake::Task[:@ann_html].invoke File.write @ann_html_dst, @ann_html end CLOBBER.include @ann_html_dst #----------------------------------------------------------------------------- # plain text #----------------------------------------------------------------------------- @ann_text_dst = 'ann.txt' desc 'Build plain text announcement.' task 'ann:text' => @ann_text_dst file @ann_text_dst => @man_src do Rake::Task[:@ann_text].invoke File.write @ann_text_dst, @ann_text end CLOBBER.include @ann_text_dst #----------------------------------------------------------------------------- # RSS feed #----------------------------------------------------------------------------- @ann_feed_dst = 'ann.xml' desc 'Build RSS feed announcement.' task 'ann:feed' => @ann_feed_dst file @ann_feed_dst => @man_src do Rake::Task[:@project].invoke Rake::Task[:@ann_nfo_html_nodes].invoke Rake::Task[:@ann_rel_html_body_nodes].invoke require 'rss/maker' rss = RSS::Maker.make('2.0') do |feed| feed.channel.title = @ann_subject_prefix + @project_module::PROJECT feed.channel.link = @project_module::WEBSITE feed.channel.description = @ann_nfo_html_nodes.join item = feed.items.new_item item.link = @project_module::WEBSITE require 'time' item.date = Time.parse(@project_module::RELDATE) item.title = @ann_rel_html_title_node.inner_text item.description = @ann_rel_html_body_nodes.join end File.write @ann_feed_dst, rss end CLOBBER.include @ann_feed_dst #----------------------------------------------------------------------------- # helper logic #----------------------------------------------------------------------------- def nodes_inner_text nodes nodes.map {|n| n.inner_text }.join(' ').gsub(/\n/, ' ').squeeze(' ').strip end ## # Fetches all nodes between those matching the given head and tail selectors. # def fetch_nodes_between head_selector, tail_selector Rake::Task[:@man_html_dom].invoke head = @man_html_dom.at(head_selector) body = [] tail = head while tail = tail.next_sibling and not tail.matches? tail_selector body << tail end [head, body, tail] end ## # Converts the given HTML into plain text. we do this using # lynx because (1) it outputs a list of all hyperlinks used # in the HTML document and (2) it runs on all major platforms # def convert_html_to_text html # lynx's -dump option requires a .html file require 'tempfile' tmp_file = Tempfile.new($$).path + '.html' begin File.write tmp_file, html `lynx -dump #{tmp_file} -width 70`. # # improve readability of list items # by adding a blank line between them # gsub(/(\r?\n)( +\* \S)/, '\1\1\2') ensure File.delete tmp_file end end ## # Converts relative URLs in the given HTML into # absolute URLs bound to the given base URL. # # http://en.wikipedia.org/wiki/URI_scheme#Generic_syntax # def resolve_html_links html, base_url = nil Rake::Task[:@project].invoke base_url ||= @project_module::WEBSITE require 'cgi' " #{html}" end