In short, why do *YOU* like Ruby?It's a fair question. I'm currently developing exclusively in Ruby and writing almost only about Ruby. The short answer is Ruby requires less to get the job done. I'll elaborate, but remember, this is why *I* like Ruby, not an attempt to compare Ruby to any other language.
#1. It's easy to add behavior to objects. You can extract patterns and put them in modules or add them to existing classes. Once you have added the behavior you can quickly add responsibilities to your objects with much less code. Since I've been developing in Ruby I've found myself using Attribute Initializer and Initialization Chain quite often. By having the patterns for both of these behaviors extracted out to class methods I can reduce the code necessary which also reduces the potential for errors.
#2. Ruby's duck typing allows me to easily create dependencies. I strongly believe in the Unit Testing Guidelines I previously posted. The guideline I suggest in that entry is: Only one concrete class (with behavior) should be used per test. Because of duck typing I can follow this rule by creating stub dependencies without worrying about interfaces. Consider the following class:
class ConsultantConsultant has a dependency on Boss, Phone, and Laptop. Additionally, when a Consultant instance is created it calls the
def initialize(boss, phone, laptop)
@boss, @phone, @laptop = boss, phone, laptop
@phone.on if @phone.off?
@laptop.on if @laptop.off?
onmethod of both the phone and laptop. Because the Consultant instance calls methods on phone and laptop in the constructor, nil cannot be passed as either argument.
Now, I want to test the Consultant class in isolation so I need to create stubs for Phone, Laptop, and Boss. In a statically typed language I would need a Phone, Boss and Laptop interface and each stub would need to implement the full behavior of the interface. However, in Ruby I can create simple stubs that only define behavior I'm interested in:
class StubLaptop; def on; end; def send_email(e); end; endNow I can use these stubs and define mocks that allow me test only the isolated Consultant class and it's interactions. Also, these stubs are really only in the test for support, thus making them essentially noise. The less noise I am required to include in a test the more readable (and maintainable) the test becomes.
class StubPhone; def on; end; def text(msg, num); end; end
class StubBoss; def number; end; end;
#3. The dynamic features of the language allow me to more easily write Domain Specific Languages (DSL). At my current project we've provided the client with a domain specific language that allows definition of variables and reuse of those variables later in the script.
In the script a variable is defined similar to
define :jay => thoughtworks.developer. Obviously, we are really using Ruby to call the method
definewith a hash where
:jayis the key and
thoughtworks.developeris the value. When the define method is executed it defines a method dynamically by the key and sets the return value to the value of the hash. Later in the script the variables are used similar to
work jay, mike, charles. Again, no magic here, the
workmethod is being called with the return values from the
charlesmethods. However, the end result is a very readable DSL which empowers my business users to code part of the system.
The same functionality could be achieved by changing the
workline to read
work :jay, :mike, :charlesand storing the values in a hash. But, this is a step down from the above syntax in most business users eyes.
Similarly, I recently had a requirement build an ad-hoc sentence using dot notation. For example,
this.was.the.syntax.but.the.sentence.needs.to.be.free.form. By using method_missing I was able to support this style with very little code required other than protecting keywords.