You may have read that title and thought that this was going to just be another one of those “learn a different paradigm to make you think different” posts. Well, it isn’t. Those posts aren’t bad, and I have made a few of them, but this is a much less creative post…
Just to get this out of the way, all of the following code is covered under the MS-PL license.
If you haven’t had the luxury of getting to play around with the Ruby libraries then you are missing out. In fact, if you haven’t ever used them, then go check out some of the classes in the library to see just how powerful it really can be. For example, check out the String class in Ruby and you will see a list of methods that looks like this:
% * + << <=> == =~ [] []= bytes bytesize capitalize capitalize! casecmp center chars chomp chomp! chop chop! concat count crypt delete delete! downcase downcase! dump each each_byte each_char each_line empty? end_with? eql? gsub gsub! hash hex include? index initialize_copy insert inspect intern length lines ljust lstrip lstrip! match new next next! oct partition replace reverse reverse! rindex rjust rpartition rstrip rstrip! scan size slice slice! split squeeze squeeze! start_with? strip strip! sub sub! succ succ! sum swapcase swapcase! to_f to_i to_s to_str to_sym tr tr! tr_s tr_s! unpack upcase upcase! upto
There is a lot of awesome functionality in there which could be really useful in C#. If only we had some code in C# that performed all of these actions! Oh wait, IronRuby is written in C#! We can just look up the IronRuby methods in the source.
So, how do we find these methods? Well, the IronRuby team has two attributes that help us find the method that we are looking for. One is called the “RubyClassAttribute” and the other is called “RubyMethodAttribute”. If we look in the IronRuby source and search for “chomp” we will actually find a few method that look like this:
[RubyMethod("chomp")] public static MutableString/*!*/ Chomp(CodeContext/*!*/ context, MutableString/*!*/ self) { return InternalChomp(context, self, RubyUtils.GetExecutionContext(context).InputSeparator); } [RubyMethod("chomp")] public static MutableString/*!*/ Chomp(CodeContext/*!*/ context, MutableString/*!*/ self, [NotNull]MutableString/*!*/ separator) { return InternalChomp(context, self, separator); } [RubyMethod("chomp")] public static MutableString/*!*/ Chomp(CodeContext/*!*/ context, MutableString/*!*/ self, object separator) { return separator == null ? self : InternalChomp(context, self, Protocols.CastToString(context, separator)); }
So, here you can see that all of these methods are calling out to another method to do their bidding called “InternalChomp”. We were lucky in that the string class is the only class which has a “chomp” method, but if we had multiple type then that is where the “RubyClassAttribute” can help you out. If we look at the top of the class that we found the “Chomp” methods in we will see this attribute declaration:
[RubyClass("String", Extends = typeof(MutableString), Inherits = typeof(Object))] public class MutableStringOps {
This is pretty self explanatory. This class represents methods that are on the Ruby “String” class, which is represented by the CLR type MutableString. If we go back to the “InternalChomp” method that we saw earlier, then we will see this code:
private static MutableString InternalChomp(CodeContext/*!*/ context, MutableString/*!*/ self, MutableString separator) { if (separator == null) return self; // Remove multiple trailing CR/LFs if (separator.Length == 0) return RubyUtils.FlowTaint(context, self, ChompTrailingCarriageReturns(self, false)); // Remove single trailing CR/LFs MutableString result = RubyUtils.FlowTaint(context, self, CreateSubClass(context, self, MutableString.Create(self))); int length = result.Length; if (separator.Length == 1 && separator.GetChar(0) == '\n') { if (length > 1 && result.GetChar(length - 2) == '\r' && result.GetChar(length - 1) == '\n') { result.Remove(length - 2, 2); } else if (length > 0 && (self.GetChar(length - 1) == '\n' || result.GetChar(length - 1) == '\r')) { result.Remove(length - 1, 1); } } else if (EndsWith(result, separator)) { result.Remove(length - separator.Length, separator.Length); } self.Version++; return result; }
While this method may not be particularly interesting there is a lot of code you can go through in the IronRuby codebase to give you plenty of ideas. For example, everyone complains about C#’s lack of a good Range class. So why not go into the IronRuby source and get a few ideas for implementing your own C# Range class? So, go get the source for IronRuby, play around with it, and let me know what you find.
Loved the article? Hated it? Didn’t even read it?
We’d love to hear from you.