Wednesday, 1 April 2009
Ruby idioms in ASP.Net MVC
In Rails there is a mechanism by which you can mimic variadic methods (Ruby also supports true variadic methods) by treating the last parameter as a hash. I can write a function definition like this
def pass_a_hash(s, h)
puts s
h.each { |key, value| puts "#{key} => #{value}"} if h.class == Hash
end
and call it either like
pass_a_hash "kevin", {:a => "b", :c => "d"}
or
pass_a_hash "kevin", :a => "b", :c => "d"either way this prints
kevin a => b c => d
The second style is much neater, it hides the use of the hash and instead makes it seem like we are using named parameters. This is used all over Rails, especially on web pages where you need to specify things such as controllers and actions. You will often see code like:
<%= link_to "Add", :action => :add, :controller => :students %>
Here we are calling the 'link_to' method passing a string and a hash of options, just like above
Microsoft (bless 'em) have tried to mimic this in ASP.Net MVC by letting you can write code similar to the 'link_to' Rails method. On an ASP.Net view you can do something like:
<%= Html.RouteLink("Add", new {action = "add", controller = "students"}) %>
And this adds a link to the page with the text "Add" and a url something like http://server.com/students/add. This is similar to ActionLink and they both eventually call into a method called GenerateRouteLink. In the ASP.Net case the second parameter is not a hash (oh for first class hashes in C#) but an instance of an anonymous class. Using Reflector we can see what this method does.
Internally RouteLink creates a RoutedDictionary passing the anonymous instance as a parameter to its constructor
return htmlHelper.RouteLink(linkText, new RouteValueDictionary(routeValues));
The RouteValueDictionary constructor looks like this
public RouteValueDictionary(object values)
{
this._dictionary = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
this.AddValues(values);
}
and AddValues does this:
private void AddValues(object values)
{
if (values != null)
{
foreach (PropertyDescriptor descriptor in TypeDescriptor.GetProperties(values))
{
object obj2 = descriptor.GetValue(values);
this.Add(descriptor.Name, obj2);
}
}
}
public void Add(string key, object value)
{
this._dictionary.Add(key, value);
}
So AddValues uses reflection to build a dictionary of name/value pairs
Just like in Rails, when the element is rendered this dictionary is interrogated and the values used to build the URL.
Neat, but still more ceremony than in Ruby!
Posted by at 5:45 PM in Ruby
[Trackback URL for this entry]
