require 'net/ftp' require 'net/http' class SdocAll class Ruby < Base def initialize(raw_config) raw_config ||= {} raw_config = {:version => raw_config} unless raw_config.is_a?(Hash) @config = { :update => raw_config.delete(:update) != false, :version => raw_config.delete(:version), :index => raw_config.delete(:index), :stdlib => raw_config.delete(:stdlib), } version = config[:version] unless version.present? raise ConfigError.new("specify version of ruby (place archive to 'sources' directory or it will be download from ftp://ftp.ruby-lang.org/)") end self.class.find_or_download_matching_archive(version) if config[:index] index = Pathname(config[:index]) unless index.directory? && (index + 'index.html').file? raise ConfigError.new("index should be a directory with index.html inside and all related files should be with relative links") end end if config[:stdlib] download_and_get_stdlib_config end raise_unknown_options_if_not_blank!(raw_config) end def add_tasks(options = {}) archive = self.class.find_or_download_matching_archive(config[:version], :update => config[:update] && options[:update]) version = archive.full_version src_path = sources_path + version unless src_path.directory? Base.remove_if_present(src_path) case archive.extension when 'tar.bz2' Base.system('tar', '-xjf', archive.path, '-C', sources_path) when 'tar.gz' Base.system('tar', '-xzf', archive.path, '-C', sources_path) when 'zip' Base.system('unzip', '-q', archive.path, '-d', sources_path) end File.rename(sources_path + "ruby-#{version}", src_path) end self.class.used_sources << src_path task_options = { :src_path => src_path, :doc_path => "ruby-#{version}", :title => "ruby-#{version}" } task_options[:index] = config[:index] if config[:index] Base.add_task(task_options) if config[:stdlib] stdlib_config = download_and_get_stdlib_config(:update => config[:update] && options[:update]) stdlib_tasks = [] Dir.chdir(src_path) do main_files_to_document = get_files_to_document stdlib_config['targets'].each do |target| name = target['target'] paths = FileList.new paths.include("{lib,ext}/#{name}/**/README*") paths.include("{lib,ext}/#{name}.{c,rb}") paths.include("{lib,ext}/#{name}/**/*.{c,rb}") paths.resolve paths.reject! do |path| [%r{/extconf.rb\Z}, %r{/test/(?!unit)}, %r{/tests/}, %r{/sample}, %r{/demo/}].any?{ |pat| pat.match path } end if paths.present? && (paths - main_files_to_document).present? stdlib_tasks << { :src_path => src_path, :doc_path => name.gsub(/[^a-z0-9\-_]/i, '-'), :paths => paths.to_a, :main => target['mainpage'], :title => name } end end end Base.add_merge_task( :doc_path => "ruby-stdlib-#{version}", :title => "ruby-stdlib-#{version}", :tasks_options => stdlib_tasks.sort_by{ |task| task[:title].downcase } ) end end private def get_files_to_document(dir = nil) files = [] dot_document_name = '.document' dot_document_path = dir ? dir + dot_document_name : Pathname(dot_document_name) if dot_document_path.exist? dot_document_path.readlines.map(&:strip).reject(&:blank?).reject{ |line| line[/^\s*#/] } else ['*'] end.each do |glob| Pathname.glob(dir ? dir + glob : glob) do |path| if path.directory? files.concat(get_files_to_document(path)) else files << path.to_s end end end files end def download_and_get_stdlib_config(options = {}) stdlib_config_url = 'http://stdlib-doc.rubyforge.org/svn/trunk/data/gendoc.yaml' if options[:update] || (config = get_stdlib_config).nil? data = Net::HTTP.get(URI.parse(stdlib_config_url)) stdlib_config_path.write(data) if (config = get_stdlib_config).nil? raise ConfigError.new("could not get stdlib config from #{stdlib_config_url}") end end config end def get_stdlib_config YAML.load_file stdlib_config_path if stdlib_config_path.readable? end def stdlib_config_path sources_path.parent + 'stdlib-gendoc.yaml' end ArchiveInfo = Struct.new(:path, :name, :full_version, :extension, :version) module ClassMethods def match_ruby_archive(path) name = File.basename(path) if match = /^ruby-((\d+\.\d+\.\d+)-p(\d+))(?:\.(tar\.(?:gz|bz2)|zip))$/.match(name) ArchiveInfo.new.tap do |i| i.path = path i.name = name i.full_version = match[1] i.extension = match[4] i.version = match[2].split('.').map(&:to_i) << match[3].to_i end end end def last_matching_ruby_archive(version, paths) paths.map do |path| match_ruby_archive(path) end.compact.sort_by(&:version).reverse.find do |tar_info| tar_info.full_version.starts_with?(version) end end def find_matching_archive(version) paths = sources_path.parent.children.select(&:file?) last_matching_ruby_archive(version, paths) end def download_matching_archive(version) Net::FTP.open('ftp.ruby-lang.org') do |ftp| remote_path = '/pub/ruby' ftp.debug_mode = true ftp.passive = true ftp.login ftp.chdir(remote_path) paths = ftp.list('ruby-*.tar.bz2').map{ |line| "#{remote_path}/#{line.split.last}" } if tar = last_matching_ruby_archive(version, paths) dest = sources_path.parent + tar.name unless File.exist?(dest) && File.size(dest) == ftp.size(tar.path) ftp.getbinaryfile(tar.path, dest) end end end end def find_or_download_matching_archive(version, options = {}) if options[:update] || (archive = find_matching_archive(version)).nil? download_matching_archive(version) if (archive = find_matching_archive(version)).nil? raise ConfigError.new("could not find version of ruby matching #{version.inspect}") end end archive end end extend ClassMethods end end