Quantcast
Channel: Ruby – Simple Thread
Viewing all articles
Browse latest Browse all 15

What Is So Great About Ruby?

$
0
0

I had a bit of a meltdown on Twitter the other day… well, it wasn’t really a meltdown, it was just frustration. Frustration with C# and desperately wanting a language which allows me to work without a lot of the ceremony that comes with most strongly typed languages. For a while I thought IronRuby was going to help me achieve the goal of moving off C# at least for my web development. I thought that once I got full IronRuby support inside of ASP.NET MVC, bam! That would have been it, I would have used it, no question about it. Alternatively, if I had gotten full Ruby support, I would have also had the option of moving to Rails which makes for interesting conspiracy theories about the downsizing of the IronRuby team.

I got myself pretty upset, I even stayed up until about 2 in the morning on Thursday night writing a long blog post. I’m glad I slept on it for a few days and talked to half a dozen people about it. It made me realize that I didn’t really have any good goals for the post other than to burn off some steam. I thought for a while about what my goals should be, and I thought back to the conversation I had on Twitter. Many of the comments that I received were along the lines of "stop complaining about C#, show me why Ruby is better!" I was actually surprised that I got this comment several times. I mean, everyone knows Ruby,right? (Was that elitist enough for you?)

So, I realized that my goals were really to explain why I am frustrated with C#, and why I think that Ruby (or a Ruby-like language) would make my life easier. I also don’t want you to think that I am saying that C# sucks, I’m not saying that at all. C# is a great language with goals that were different from the highly testable, abstracted, dependency injected world in which we live now. Developers (or at least many developers) on the Microsoft platform *need* a more malleable and flexible language, one which is a first class citizen in the ecosystem. If Microsoft developers don’t get this, then the string of defections to other platforms won’t end any time soon. So, before you fall asleep, let’s get on with the show…

Yes, I Can Invoke Methods On Classes!

No, no you can’t. You invoke methods on objects. Remember, objects and classes aren’t the same thing. You create methods in a class, and then you create an object from that class, then you call methods on that object. But what if you could call methods on classes? What if a class was an object? Would that mean that a class had state? Why yes, yes it would.

Sound useful? No? Well, do you use attributes in C#? What if I told you that you could get some of that exact same functionality in Ruby without tacking on a language feature? What about properties? Ruby does the same thing, without a separate language feature. Check out the following class:

class MyClass
  include ActiveModel::Validations

  validates_presence_of :name

  attr_accessor :name
end

A lot going on there. For now we will ignore the "include" line and we will just say that it is a library which allows you to perform validations. Next we see the "validates_presence_of :name", which as you can probably guess validations the name "property" which is defined below it. (It isn’t really a property, but we will pretend it is for right now) The interesting thing is that both of those are just method calls on the current class. "attr_accessor" is just a method class which creates a getter and setter method called "name". The method "validates_presence_of" is executed on the class and saves a piece of state which will check to make sure that "name" property has a valid when "valid?" is called on the object.

Pretty powerful stuff… think about how annoying it is in order to create a System.ComponentModel.DataAnnotations validator in .NET which compares two properties? No problem here, we would just pass both properties to a validator method. The flexibility is pretty amazing. Think of all of the uses that you have for attributes, and then think about what it would be like if they could store state, add methods, be combined, etc… It truly is attributes on steroids.

If Only This Class Had A <blank> Method!

We’ve all seen it a hundred times. There is this class you have to use, but it just doesn’t provide the functionality that you want. In C# 3.0 extension methods were introduced that help with this problem immensely, but there are still a few key limitations to their uses. Mainly the fact that they have to be implemented as a static method, which means that they are limited in the scope of what they can accomplish, mainly because they can neither affect the internal state of a type, and because they are unable to apply additional state to an instance. In other words, they can’t add additional "stuff" to a class, they can only interact with its public methods and properties.

So, why not just subclass a type? Well, unfortunately for many types (especially those within the .NET framework) this is impossible. There are tons of sealed types, and even if the types aren’t sealed, subclassing a type doesn’t buy you a ton if the types are being produced by the framework. In Ruby, types are "open" which means that any type can be modified at virtually any time.

While it isn’t something you should do often, there are instances where this can be useful. For example, Rails extends the object class to add a "blank?" method which allows the developer to avoid checking a variable against null and then checking if it is empty. A useful method to have when developing web applications and checking input! If we wanted to open up the Object class and add our own method, we would only need to do this:

class Object
  def my_method
    "hello!"
  end
end

Simple, we just declare the class again and add methods. This will modify the type everywhere within our process! This same behavior even allows us to replace methods in classes if we needed to. While this practice is generally frowned upon for most uses, having the ability to simply open up and modify types can really come in handy. We are all consenting adults, I believe that languages should give us the power and flexibility to do the things that we need to do, while accepting the risks of doing so.

Also, if you’ve dealt with .NET for a while, and had to use any of the frameworks which generate classes for you, if you think back to the last section, then you know that you could say goodbye to "buddy classes" forever! (Well, hopefully you have already said goodbye to them)

I Want Behavior Across Type Hierarchies

When you start talking about AOP and cross cutting concerns, most people just start rolling their eyes. Well, if you are a C# developer and you enjoy using extension methods and frameworks like Linq, then you need to stop rolling your eyes because you are experiencing a limited version of the power you can get with mixins. You get behavior across a bunch of types that don’t share the same type heirarchy. The result can be incredibly powerful.

What we saw in the first example was a use of mixins in order to add to a class the ability to support validation. Instead of creating separate classes which perform validation, we simply "include" ActiveModel::Validations. The include keyword allows us to take a Ruby module, and add its functionality to a class:

module MyModule
  def my_module_method
    "hello"
  end
end

class MyClass
  include MyModule

end

obj = MyClass.new
obj.my_module_method

Powerful stuff. Like I said, if you are using Linq and extension methods then you know how powerful it can be to be able to put a set of methods across types that don’t share a heirarchy. In C# you are limited by the fact that extension methods are just static methods that just look like they are part of the object. In reality though, they are limited because they don’t have the ability to actually add variables or store any data.

As we saw with the ActiveModel validators, it is quite useful to be able to mixin a set of methods and variables so that our newly added methods can interact with each other! Think about all of the great things you could do with extension methods if they were able to store extra state on the instance that is passing by?

I Sure Wish I Could Test All Of These Framework Classes

How many times have you written a wrapper around HttpContext, the caching api, file IO, network IO, etc…? Too many times? Yeah, me too. This little gem falls right in place with the first example we had of Ruby’s "open" classes, but also touches on another one of Ruby’s core features… duck typing.

In .NET if I want to test a framework type I often have no choice but to write a full wrapper around it. I can’t modify its behavior, I can’t subclass it, and even if I could its methods most likely aren’t virtual. What if I want to test some file io? A wrapper to the rescue! I’ll create a wrapper, slap an interface on it, and then I’ll inject it somewhere.

Let’s start off with that first piece, creating the wrapper. Why would I need to create a wrapper in Ruby? I probably wouldn’t, if I was using Ruby I could use open classes to modify the type and change a method. It depends on what I am doing though. If I wanted to actually modify the type within my process, I could simply do something like this:

"abcdef".reverse # outputs "fedcba"

class String
  def reverse
    "tricked you!"
  end
end

"abcdef".reverse # outputs "tricked you!"

Here we have changed the String class’ reverse method. Keep in mind that this would change that method for all instances of that class everywhere within our process, so we have to be careful. But with the power of Ruby, we can even get around this problem by using a chunk of code like the one found here. There is a lot going on in that code, but what it allow us to do is something like this:

def my_test
  MyClass.use_class(:MyDependency, StubDependency) do
    # the Stub will be used until this block ends!
  end
end

So in the context of that block (the do .. end section) any instance of MyDependency used will be replaced by StubDependency. This is a way of manually replacing a class instance, but there are also frameworks available for Ruby which do things like mocking, but not always in the way that C# developers think of mocks. For instance, in C# we create a mock and we then set behavior on that instance. Ruby mocking frameworks have the ability to say things like, "all new instances should do x" which allows them to setup behavior without having to inject an instance… but more on that later.

If you are a C# developer, then you might be wondering, "well, what if MyDependency and StubDependency" don’t have the same interface? What if MyDependency does’t even implement an interface? Well, in Ruby, the concept of an interface is very fluid. Which leads us to the next section…

What Do You Mean I Have To Create Another Interface?

C# developers (and Java developers) love some interfaces! I might as well be creating header files because I feel like I have an interface for almost everything I write. If it wasn’t for Resharper, I’d probably jump off something very tall. Ruby doesn’t really have the same problems though, because while a Ruby class still has an "interface", it isn’t necessarily a static contract in the way that C# developers think of it.

In Ruby, something implements my "interface" if it implements it. Huh? Okay, lets say that I want to create a method that operates on an interface which allows me to add points to an object. Who cares what points are, and who cares what the classes are, I just want to add points. In C# we would declare an IAddPoints interface and then we would have to implement this interface on each class that we wanted to use. In Ruby we would do this:

class MyClass1
  def add_points(num_points)

  end
end

class MyClass2
  def add_points(num_points)

  end
end

So, each of these classes support my interface simply because they do. They implement it, so they support it. This is usually referred to as duck typing. If it walks like a duck, and it quacks like a duck, then it might as well be a duck. If I wanted to operate on these classes I could define a method somewhere that did this:

def add_five_points(target)
  target.add_points(5)
end

And now each time I call this method, 5 points is added to anything that can support "add_points". We could ask Ruby to check whether or not the type supports that method, but in reality we don’t just pass types around willy-nilly, we generally know what a method is for and what is expected of our types. Besides, tests will catch silly errors like that.

So how else would this duck typing be useful? Honestly, how isn’t it useful?! Let’s look at something that is essentially impossible to do in C# currently, and that is to define a generic class which adds any two numeric types together. Think about it, we have generics, and we have generic constraints, but something as simple as creating a generic class which can add two numbers together is essentially impossible unless we start using the "dynamic" keyword. Aha! It took the dynamic keyword to save us :-)

In Ruby we could just do this:

def add(val1, val2)
  val1 + val2
end

It just works. Using the + operator invokes the "+" method on object val1 and passes object val2. Yep, you heard me right, the method. In Ruby, almost everything is a method call. By relaxing requirements on parameters and the "." operator, some pretty amazing things are possible. The only downside is that you have to implement order of operations on methods, which is a bit bizarre, but workable nonetheless.

In C# 4.0, we can finally do this, in limited circumstances where we control the code being called:

public dynamic Add(dynamic val1, dynamic val2){
  return val1 + val2;
}

The dynamic keyword gets us close, but not all the way.

You Want Me To Inject Something?

This is very closely related to the section above where I talked about testing framework classes. In that section we looked at the ways we could replace classes for testing without having to inject instances. Well, you might have been thinking, "but I inject instances because I want dependency inversion, not because I want to test!"

Well, first of all, dependency inversion doesn’t mean as much in a dynamic language as it does in a static language. This is mainly because in Ruby you are almost always depending on abstractions, every variable can hold any type so when writing your code you need to only worry about how you interact with an object, not which object you are interacting with.

Secondly, we generally inject dependencies because the service locator pattern can be a bit too rigid for our needs, and it makes it hard to switch out dependencies later. Well, in Ruby we really don’t have this problem. Service location is really the name of the game if you want to be able to pull in dependencies. Because Ruby is so flexible, we can simply modify what we are returning at any time. And the usual arguments for external configuration (via XML or whatever) go out the window when you are writing in a language which doesn’t have a compile step.

Another thing to take note of is the ability to use Modules to apply behavior and dependencies on a type. For example, let’s say we need to have a connection on a class which goes to the database. We don’t really know what kind of connection that we need, and we might need to change this, so we have a few different implementations. We could declare a module like this:

module SqlConnection
  def connection
    @connection ||= SqlServerConnection.new "connection string"
  end
end

Now if we have a class which needs to depend on a connection, then we can create the class like this:

class MyRepository
  include SqlConnection

  def save(instance)
    @connection.save(instance)
  end
end

Now the repository class can have its dependency on SQL Server switched out by changing the mixin that it uses. This can also be done dynamically by using the extend method. So instead of using "include SqlConnection" my repository would look like this:

class MyRepository
  def save(instance)
    @connection.save(instance)
  end
end

Then when I wanted to create an instance I could do this:

repository = MyRepository.new
repository.extend SqlConnection

Excellent, this would put the mixin into this instance, and not at the class level. I hope you are starting to see the kind of flexibility that mixins give you, and the power of this kind of programming. Mixins aren’t something that are only applicable to dynamic languages though, Scala has mixins and it has a very interesting way of implementing them that gets you most of the power while still maintaining type safety.

That Ain’t All Folks!

While I have tried to show the many different areas where Ruby is powerful and lets us do thing that we have some trouble doing in C#, I really didn’t touch on many of the other cool features of Ruby and its Ecosystem. I’ll just throw out a few examples here:

method_missing – In Ruby all method calls are really just messages being sent to a class. If the class has a method which can respond to the message, then it is invoked and the result is returned. If there isn’t a method which can respond to the method then the message is sent to the "method_missing" method. This allows some really neat things to be done, such as Rails finders which parse the method name in order to determine what attributes you are trying to query. So a method called "find_by_first_name" means that you are looking for a class by a first_name attribute. We get some of this with the dynamic keyword in C#, but the real power in Ruby comes from the fact that this ability is available everywhere, not just when we specify types as dynamic.

blocks – If you understand the concept of lambdas or anonymous delegates in C#, then you essentially already understand what blocks are in Ruby. The interesting thing about blocks in Ruby is their beautiful integration with method parameters, like this example of File IO in Ruby:

File.open("file.txt", "r") do |file|
  puts file.readline
end

The open method actually takes three parameters, the first two are the file name and mode, the third is a block that lets you perform file IO and then automatically closes the file. It looks like a native language construct, but it is just syntactic sugar on a block parameter. Pretty awesome!

Singleton methods – Singleton methods might not be exactly what you think. They are the ability to add a method to a single instance of a class, similar to the way that JavaScript works with its prototype based object system. You can take a class and just assign methods to it until it does what you want, a very powerful way to compose functionality.

Gems – Folks on the .NET stack are finally getting a gems style solution with NuPack, and before that Nu existed and was starting to gain some steam. It is a tool which gives you the ability to easily pull in other open source projects, do upgrades, maintain and check versions, etc… The concept is a very powerful one and will make starting up and maintaining projects much easier going forward. This is one tool that I hope really catches on in the .NET space.

Simple reflection – Reflection in .NET is a pain. Especially when you start involving generics, method invocation, etc… What to get the methods on a Ruby class or object? Do this:

MyClass.methods

MyClass.new.methods

Want to dynamically invoke a method on a class? Just call the "send" method:

"hello".send :reverse

This calls the "reverse" method on the string class. If we had parameters, we would just pass them after the method name.

Ruby is full of tons more little surprises, and every time you see a new library like EventMachine, Treetop, Capistrano, cucumber, mocha, etc… you’ll realize even more the power and flexibility of Ruby.

Ruby Isn’t Perfect

All of this talk about how awesome Ruby is might have you confused into thinking that I believe it is the "One True Language". That is clearly not the case. Ruby isn’t perfect, and as well all know perfect is relative. What is perfect for writing an OS is not perfect for writing business apps. What is perfect to me, may not be perfect to you. And what is perfect now, won’t be perfect a year from now. Things change. What I think is that right now Ruby is much closer to what I am looking for in a language.

Will C# Get There?

No, because they don’t want to. C# is a different type of language with different goals. C#’s main goals from the beginning has been to create a flexible type safe language. Many of the abilities in Ruby probably directly conflict with the design goals of C#, and many of them would be impossible within the statically typed environment of C#.

What Is The Solution?

If you are a developer on the Microsoft platform and you really want a flexible dynamic language then you really have three options.

1) You like C# and it fits your needs. Stick with it. Or you just accept that you don’t have a dynamic language with full support of the ecosystem, and you just move on with your life. Honestly, you can do pretty much whatever you need to do in C#, it might just be a bit harder.

2) Whine a bit and complain, all the while waiting for Microsoft to deliver a solution. Personally I think that Microsoft is working on a homegrown dynamic language, but how it will fit in with the rest of the .NET ecosystem is left to be seen. I think it would be great if Microsoft tried to out-Ruby Ruby. Everything needs good competition, even Ruby. Microsoft has a lot of smart people and a ton of good language designers, I’d love to see them take a shot at it.

3) Switch platforms and move over to a platform that has what you want. If you want to do Ruby on Rails development then realistically this means switching off Windows. You’ll learn a new language, new frameworks, new tools, new operating system, etc… For those of us who love to learn, that can actually be quite the appealing proposition.

Summary

So, what is my point of all this? Well, I wanted to provide a solid post which points out why I love the Ruby language and why I think it is better for writing web applications than C#. I hope to add one more voice to the choir of people calling for Microsoft to release a dynamic language with full support from the ecosystem. While I know that my voice isn’t going to realistically change anything, my frustration had gotten to the point where I felt like I needed to get my opinions out in public. I hope you enjoyed the post, and I’d love to hear your opinions on the topic.


Viewing all articles
Browse latest Browse all 15

Trending Articles