Nov 14
2004
Coming to Ruby from Java
Ruby is a subtle language. If you come to it after mastering another language like Perl or Java, it's easy to write Ruby code that looks a lot like the code you used to write in your previous language. Now, writing code that way won't take any longer than it would've taken in your previous language. But if you decide to become another disciple in the burgeoning Ruby cult, you'll have a lot to gain by absorbing the various Ruby idioms. You'll write clearer code in less time, and you'll write less of it while accomplishing as much as you had before. I came to Ruby from Java, so here are a number of tips—some simple, some advanced—for the serious Java programmer who's starting to become a serious Ruby programmer.
One documentation note: When people are writing about Ruby, they use MyClass.method_name to refer to a class method, and MyClass#method_name to refer to an instance method. You never actually call MyClass#method_name in working Ruby code; it's just a documentation convention.
Naming and formatting
Underscores, not camel-case
In Ruby, class names are camel-case, but method and variable names are underscored instead. So instead of MyClass.doSomething, you have MyClass.do_something. I resisted this at first but these days I prefer it. I think it's slightly easier to read, and there are a lot of text editors and shells that can use the underscore character to know where individual words begin and end.
Boolean methods end with question mark
In Ruby, method names can end with a question mark, which Rubyists use to denote methods that return a boolean value. Use MyClass.valid? instead of MyClass.is_valid.
One-line methods
Classes in a highly factored OO design will often have lots of short methods that override those methods in parent classes, like:
def count @contents.split.size end
But this can actually be laid out on one line, as long as the statements are separated by semicolons:
def count; @contents.split.size; end
This isn't really any less typing, but I personally prefer it because the visual size of the method is more in line with how complex the method is.
Multiple classes per file
Java idiom is to have a bunch of big classes with individual files: VoteCalculator.java, VoteRecord.java, VoteReport.java, etc. In Ruby it's much more common simply to put all those classes in the same file such as vote.rb. This cuts down on the amount of requiring you need to do, and I find that having all those classes together in the same place makes it easier for me to spot points of duplication that could use refactoring. Of course, if you're writing a lot of code, you shouldn't try to cram it all into one mega-file: My rule of thumb is to avoid letting files get longer than 750 lines, but that's just what works for me.
Hiding details
Use class methods to define pseudo-compile directives
Classes and class-level methods are a lot more dynamic in Ruby than in Java, so it allows you to do a lot more to tidy up your class level definitions. Here's an example: In my object-relational mapping library Lafcadio, a given domain class has fields corresponding to the given table in the database. Originally I had the domain class set those fields by overriding DomainObject.getClassFields:
class User < DomainObject
def User.getClassFields
fields = []
fields << TextField.new(self, 'firstName')
fields << TextField.new(self, 'lastName')
fields << TextField.new(self, 'email')
fields << TextField.new(self, 'password')
fields << DateField.new(self, 'birthday')
fields
end
end
But in fact, you can save yourself a lot of noise by defining class level methods:
class User < DomainObject text 'firstName' text 'lastName' text 'email' text 'password' date 'birthday' end
The line text 'firstName' is actually calling the method DomainObject.text. (Remember that class methods inherit like instance methods, which is why you can call a DomainObject class method inside of one of its children.) The actual methods are fairly complex for other reasons, but they could look something like this:
class DomainObject
def DomainObject.date( field_name )
add_field( DateField, field_name )
end
def DomainObject.text( field_name )
add_field( TextField, field_name )
end
end
If you're coming from Java, or indeed any language with a strong distinction between compile-time and runtime, this may be a little strange to look at. Remember that in Ruby, there is no compile-time: class and method definitions are invoked in the runtime like any other line of code. So although methods like DomainObject.text and DomainObject.date end up looking and feeling as if they were more like compile-time statements, all you're doing is dynamically changing the class—at the moment you're defining the class itself. And cleaning up your definition code quite a lot, to boot.
Avoid external utility classes
Let's say you're writing a Ruby app with lots of statistical methods, and you have to write a lot of those methods yourself. The Java-ish way to do this would be with a separate StatsUtil class, with only class methods:
class StatsUtil
def mean( collection )
total = 0.0
collection.each { |i| total += i }
total / collection.size
end
end
But this is Ruby, and you can just add those methods to a pre-defined class:
class Array
def mean
total = 0.0
each { |i| total += i }
total / size
end
end
… which automatically gives you less typing and more cohesion.
Now, if you've got a lot of different methods and would rather keep them somewhat separate, you can create a module and then include it:
module Stats
def mean
total = 0.0
each { |i| total += i }
total / size
end
end
class Array; include Stats; end
You might be wondering about how to avoid name collisions, particularly since the Java community has such strict policies about not stepping into one another's namespaces. In my opinion this question isn't 100% resolved right now, but we don't need to solve it now because the Ruby community is still small. When we arrive at the promised land where Ruby is the next Perl, and there are 10,000 stable libraries floating around as Rubygems, we may have to resolve the namespace problem then. But I can say that for the last two years, I have used Ruby almost exclusively in my programming work, and this has never caused a problem for me. In practice, there aren't many cases where two different libraries will create methods with the same name on the same core class.
You probably don't need Factories.
In Java, you can't pick a class at runtime, which is why you might write Factories for complex apps. But in Ruby, not only can you pick a class at runtime, but you can find that class with nothing more than a string:
def init_class( class_name, *inst_args ) a_class = Kernel.const_get( class_name ) a_class.new( *inst_args ) end require 'date' date = init_class( 'Date', 2004, 11, 14 )
In fact, let's hide centralized utilities whenever possible.
There are times when you need global, centralized classes, but you don't necessarily have to make the user of your code aware of them. As in many other cases, method_missing is your friend here; you can use it to elegantly hide those central, theoretical classes behind the more concrete classes that you're actually more likely to interact with on a day-to-day basis.
Here's an extended example: Because I'm fanatical about testing, Lafcadio offers a functionality that allows you to swap out, at runtime, any global service that is related to an external resource, such as a database, SMTP, file system, etc. (One day this functionality will be pulled out into a small library so people can use it without including all of Lafcadio, but today is not that day.)
The ObjectStore is Lafcadio's centralized service which represents the database, and it uses that functionality so you don't have to write annoying SQL scripts in your test cases. The classes involved are:
- Context: A singleton which tracks classes to instances. If an instance hasn't been created for a given service class, it creates an instance and saves it for future use.
- ContextualService: The abstract class for any class whose instance should be tracked through Context.
- ObjectStore: The concrete class which extends ContextualService.
Now, the most bare-bones way to get a service from the Context is to call
Context.instance.get_resource( 'ObjectStore' )
But nobody's going to remember that, so let's define Context#method_missing to allow us to call:
Context.instance.get_object_store
But why should you have to be reminded of the Context every time you want the ObjectStore? So we define ContextualService.method_missing to dispatch some calls to Context:
ContextualService.get_object_store
… which, since ObjectStore is a child of ContextualService, is the same as:
ObjectStore.get_object_store
That's a lot nicer, isn't it? Whoever's using the ObjectStore probably doesn't want to have to think about all that Context business. They just want to mess around with their domain objects and then move on.
Miscellaneous tips
Enumerable is your friend
This might be an obvious one, but I think some Java programmers, coming out of a language that makes dealing with collections so cumbersome, won't know to look for something nice like Enumerable. (Also, when I was learning Ruby I missed it since it's a module included in Array, meaning that its methods aren't visible when you're looking at the Array RDoc page.) In Enumerable, Ruby provides you with a rich API for manipulating collections. The above Stats.mean method gets shortened with a use of Enumerable.inject:
module Stats
def mean; ( inject( 0.0 ) { |sum, n| sum + n } ) / size; end
end
No external configuration files
Why is it that Rubyists hate storing configuration information in external files, but Java programmers do it so much? Is it because Ruby is such an elegant language that it's an even quicker way to express settings than XML? Is it because Java programmers love writing tiny parsers for anything that might ever need to be configured? Is it because Ruby's design is a more faithful expression of the notion that code is data? Whatever the reason, we don't do it.
Typing is the enemy
Rubyists are always looking for ways to type less, and the best of us come up with beautifully elegant ways to do it. The favorite hack I took home from RubyConf 2004 was Rich Kilmer's suggestion of adding methods like Fixnum#days, Fixnum#hours, and Fixnum#minutes, so you can write code like 7.days + 8.hours and get a value expressing the time in seconds. If you manage to cultivate the same sort of impatience with keystrokes, you may end up coming up with similarly beautiful hacks and sharing them with the Ruby community. (Though you should be warned that cultivating that impatience may make your Java day job that much harder.)
If you're one of those rare souls who enjoys the craft of programming, and are about to spend more time in Ruby and less in Java, you've got a lot of great discoveries ahead of you. You'll find yourself writing code that feels more elegant and expressive—more free, even—and yet still powerful and robust enough to handle whatever work you throw at it. Hopefully the idioms described above will help you get deeper into the language. What are you waiting for? Get coding!