afkortingen voor methoden namen
Commandline utilities hebben vaak korte namen; ls, rm, grep, svn etc. De subversion client svn heeft (sub-)commando’s welke allemaal afkortingen hebben; svn status kan je schrijven als svn st e.d. Toen ik een ruby wrapper class had geschreven om het svn commando, miste ik metteen de svn st variant. Natuurlijk kan je gewoon wat aliases aanmaken voor commando’s maar ik zag een kans voor een experimentje in een verloren uurtje.
require 'abbrev'
require 'pp'
pp %w{bla die foo bar}.abbrev
levert:
{"die"=>"die",
"bla"=>"bla",
"d"=>"die",
"di"=>"die",
"foo"=>"foo",
"f"=>"foo",
"bar"=>"bar",
"ba"=>"bar",
"fo"=>"foo",
"bl"=>"bla"}
Mooi om automagische afkortingen te leveren. We kunnen aan de slag!
Als we een methode op een object aanroepen die niet bestaat wordt demethod_missing methode aangeroepen. Hier kunnen we kijken of het object met een afgekorte methode naam wordt aangeroepen en hiernaar doorspringen:
require 'abbrev'
module Shorthand
alias :method_missing_orig :method_missing
def method_missing(method, *args)
original = methods.abbrev[method.to_s]
if original
send(original, *args)
else
method_missing_orig(method, *args)
end
end
end
En dan nu toepassen:
class String; include Shorthand; end
Maar wat heeft dit eigenlijk voor afkortingen opgeleverd? De methods methode aanroepen op een instance van String heeft geen zin omdat onze afkortingen via method_missing worden aangeroepen. Eens kijken wat abbrev oplevert, gesorteerd op lengte:
m = "".methods.abbrev
m.keys.sort{|a,b|a.size<=>b.size}.each{|k|puts "#{k.inspect} => #{m[k].inspect}"}
"%" => "%"
"b" => "between?"
"k" => "kind_of?"
">" => ">"
"<" => "<"
"z" => "zip"
"*" => "*"
"+" => "+"
"ty" => "type"
"cr" => "crypt"
"sp" => "split"
"pu" => "public_methods"
"rj" => "rjust"
"be" => "between?"
"ri" => "rindex"
"le" => "length"
"mi" => "min"
"ni" => "nil?"
"id" => "id"
"lj" => "ljust"
"[]" => "[]"
"pa" => "partition"
"is" => "is_a?"
">=" => ">="
"gr" => "grep"
..
Eigenlijk niet veel soeps. We kunnen nu "hallo wereld".le gebruiken in plaats van "hallo wereld".length, big deal.. Waarom krijgt upcase bijvoorbeeld geen mooie afkorting? Er is een upcase en upcase! daar valt dus niet aan af te korten, tenzij we deze suffix er eerst afhalen en er later weer aanplakken. Behouden we ook nog eens de punktuatie!
module Shorthand
alias :method_missing_orig :method_missing
def method_missing(method, *args)
shorthands = methods.reject{|t|t =~ /[!?]$/}.abbrev
shorthands.merge! Hash[*methods.select{|t|t =~ /!$/}.map do |t|
t.chop
end.abbrev.map{|a,b|[a + '!', b + '!']}.flatten]
shorthands.merge! Hash[*methods.select{|t|t =~ /\?$/}.map do |t|
t.chop
end.abbrev.map{|a,b|[a + '?', b + '?']}.flatten]
original = shorthands[method.to_s]
if original
send(original, *args)
else
method_missing_orig(method, *args)
end
end
end
Met als top 15 hoog rendement afkortingen:
"n" => "next" "z" => "zip" "ne" => "next" "lj" => "ljust" "sq" => "squeeze" "n?" => "nil?" "sp" => "split" "sl" => "slice" "le" => "length" "gr" => "grep" "st" => "strip" "gs" => "gsub" "rs" => "rstrip" "m?" => "member?" "u!" => "upcase!"Nu hebben we zelfs
u! voor upcase!, dit begint ergens op te lijken! Wat wel jammer is dat de methods methode op een object met deze module je afkortingen niet levert. Hoewel je er (bijna) niets aan hebt omdat je niet weet waar het een afkorting voor is, werkt het voor een implementatie die aliases gebruikt wel:
module Shorthand
def self.included(klass)
methods = klass.public_instance_methods
shorthands = methods.reject{|t|t =~ /[!?]$/}.abbrev
shorthands.merge! Hash[*methods.select{|t|t =~ /!$/}.map do |t|
t.chop
end.abbrev.map{|a,b|[a + '!', b + '!']}.flatten]
shorthands.merge! Hash[*methods.select{|t|t =~ /\?$/}.map do |t|
t.chop
end.abbrev.map{|a,b|[a + '?', b + '?']}.flatten]
shorthands.each{|k,v| klass.class_eval "alias #{k} #{v}"}
end
end
Het was een leuk experimentje in een verloren uurtje maar de afkortingen waar abbrev meekomt zijn voor m’n Svn class waardeloos. De commit methode zou afgekort worden naar co wat voor de commandline tool gelijk is aan checkout, niet echt wenselijk. Daarbij kan je je afvragen of een beter afkort algoritme veel uit zal halen. Dergelijk automagische afkortingen zullen zeer instabiele interfaces opleveren. De kans is te groot dat de introductie van een nieuwe methode een belangrijk deel van de afkortingen die je ingebruik hebt omzeep zal helpen.
Geen goed idee, ik maak wel aliases met de hand, wel een interessant uurtje waarin ik anders op een collega’s had zitten wachten.