Hallo Leute,
nachdem ich gerade wieder an meinem
Papyrus-Projekt gearbeitet habe, stand ich gerade vor der netten Aufgabe, Methodennamen zu sortieren (Papyrus ist ein RDoc-Template). Das soll natürlich kurz und bündig vonstatten gehen, und ist damit eine prima Aufgabe für Code-Golf

Folgendes: Gegeben ist ein unsortiertes Array mit
RDoc::ClassModule-Objekten. Damit ihr die Doku nicht quälen müsst: Diese Klasse bietet eine Methode #method_list (und falls interessant auch #each_method) an, die ihrerseits ein unsortiertes Array von
RDoc::AnyMethod-Objekten zurückgibt. Diese wiederum bieten die Methode #pretty_name, welche den Methodennamen je nach Typ als "::foo" oder "#foo" zurückgibt. Außerdem bieten sie die Methode #full_name, welche den voll qualifizierten Methodennamen à la "Foo::Bar#foo" oder "Foo::Bar::foo" zurückgibt und #name_prefix für das Zeichen "::" oder "#" je nach Typ.
Ziel ist es nun, das ganze in der für Ruby-Dokumentation üblichen Ordnung in ein Array, das nur noch die RDoc::AnyMethod-Objekte enthält, zu sortieren. Das heißt: Auf oberster Ebene wird nach Klassen/Modulnamen geordnet (Klasse oder Modul ist gleichwertig). Innerhalb einer Klasse/Modul werden dann zunächst die Klassenmethoden gelistet, danach folgen die Instanzmethoden. Diese wiederum sollen jeweils alphabetisch sortiert sein.
Konkret also soll eine solche Ordnung entstehen:
Bar::class_method
Bar::this_is_another_class_method
Bar#instance_method
Bar#instance_method2
Foo::a_class_meth
Foo::Bar#an_inst_meth
Ich hab mich schon dran versucht, aber mit dem Ergebnis bin ich nicht zufrieden. ary.map{|mod| mod.method_list.sort{|m1, m2| tm1, tm2 = [m1, m2].map{|m|m.pretty_name.tr(":#", "ab")}; tm1 <=> tm2}}.flatten
Oder im zweiten Anlauf: ary.map{|mod| mod.method_list}.flatten.sort{|m1, m2| m1.full_name.tr(":#", "ab") <=> m2.full_name.tr(":#", "ab")}
Das geht kürzer

Und weil die bloße Theorie langweilig ist, hier eine vollständige Datei, mit der ihr euren Code testen könnt: # -*- coding: utf-8 -*-
require "rdoc/rdoc"
require "pathname"
# Dateien und Verzeichnisse, die dokumentiert werden sollen
DOC_TARGETS = [Pathname.pwd + "lib"]
# Ausgabeverzeichnis
OUTPUT_DIR = Pathname.pwd + "doc"
class Sorter
def initialize(options)
@output_dir = Pathname.pwd.expand_path + options.op_dir
end
def generate(top_levels)
# Klassen und Module abgreifen (ary enthält dann die RDoc::ClassModule-Objekte)
ary = RDoc::TopLevel.all_classes + RDoc::TopLevel.all_modules
# ↓↓↓ Hier meinen Sortiercode durch euren ersetzen ↓↓↓
sorted = ary.map{|mod| mod.method_list}.flatten.sort{|m1, m2| m1.full_name.tr(":#", "ab") <=> m2.full_name.tr(":#", "ab")}
# Und hier die Ausgabe
File.open(@output_dir + "sorted_method_list.txt", "w") do |file|
sorted.each{|meth| file.puts meth.full_name}
end
end
end
if __FILE__ == $0
options = RDoc::Options.new
options.template = "" # Für den Minimalgenerator wird kein Template benötigt
options.generator = Sorter
options.files = DOC_TARGETS
options.op_dir = OUTPUT_DIR
RDoc::RDoc.new.document(options)
end
Einfach an der entsprechend markierten Stelle den Sortiercode einsetzen und bei DOC_TARGETS Verzeichnisse oder Ruby-Dateien auflisten, die RDoc parsen soll. Das Ergebnis liegt dann in doc/sorted_method_list.txt.
Valete,
Quintus
EDIT: Ich sehe gerade, die zweite Lösung sortiert nichts ganz richtig, der vergleicht Klassenmethoden direkt mit Klassennamen. Also bitte nicht als Referenz nehmen (aber die erste sollte tun).