Es ist zwar noch eine ganze Weile bis Weihnachten, aber eins ist sicher: Ruby 1.9.2 kommt. Und mit ihm ein Herzenswunsch Yehuda Katz's, der es wegen später Einreichung nicht mehr in 1.9.1 geschafft hat, aber im trunk vorhanden ist: Proc#parameters. Einen kleinen Spass, den man sich damit leisten kann, findet ihr unter dem Link.
Proc#parameters tut das, was der Name sagt: es gibt einem die Parameter eines Blocks zurück:
a =->(a,b=0){}.parameters #=> [[:req, :a], [:opt, :b]] |
Spassig, braucht mans? Aber klar! Wie wärs mit Named Captures bei regulären Ausdrücken?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
def named_match(string, regexp, &block) match = string.match(regexp) if match arguments = [] block.parameters.each do |required, name| name = name.to_s #name ist ein symbol, match will strings if match.names.include? name arguments << match[name] elsif required == :req raise Exception.new("Required arg missing in regexp: #{name}") end end block.(*arguments) end end named_match "123abc", /(?<number>\d+)(?<string>\w*)/ do |number, string| puts number puts string end named_match "123abc", /(?<number>\d+)(?<string>\w*)/ do |string, number| puts number puts string end |
Natürlich lässt sich das weiter ausweiten. Wie wärs mit Hash#with_values ?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
class Hash def with_values(&block) arguments = [] block.parameters.each do |required, name| if !(v = self[name]) && required == :req raise Exception.new("Required key missing: #{name}") end arguments << v end block.(*arguments) end end {a: "b", c: "d", e: "f"}.with_values do |a,e| puts a,e end |
Dummerweise kriegt man default-Parameter nicht mitgeliefert (angeblich schwierig zu implementieren, gerade weil der default code sein kann), sonst hätte ich sofort sowas geschrieben:
1 2 3 |
xml_document.css do |name="person > name", street="person > street"| #schade... *schnief*... geht nicht end |
Für Nutzer von Ruby 1.9.1 gibt es das Feature als gem:
gem19 install methopara |
Kommentar schreiben
Kommentare
Nette Neuerung. Mal sehen, was man damit noch alles anstellen kann - Dein Beispiel führt ja schon in eine bestimmte Richtung. Wegen der Match-Geschichte kann man aber schont jetzt was machen, weil es ein "Magisches Variablenverhalten" in Ruby 1.9 gibt... -> /(?.)/=~"XYZ";otto ...liefert "X".
Die Highlighting-Schwachstelle für block.(*arguments) wird in der nächsten Version gefixt. Ich bevorzuge aber die [...]-Schreibweise...
Das praktische an .() ist, dass es ein alias für #call ist und damit [] nicht verbraucht. Ich verwende das gerne für Objekte, die #call implementieren, aber nicht das #[]-Verhalten von Blocks haben. Allerdings ist es auch der schnelle Weg zu Ruby-1.9 only Code. Aber hey - ich verwend ja auch das picky lamdba.