Beyond Javas: Chapter 6. Ruby in the Rough
Java's leadership run, at least for applications, might be drawing to an end. This book points out one language and two frameworks (one in Ruby and one in Smalltalk) that have something special to offer. One possible alternative language, Ruby. It improves on Java, but that doesn't mean that Ruby will succeed, or that it's the best possible alternative. Dynamic Languages...
Java's leadership run, at least for applications, might be drawing to an end. This book points out one language and two
frameworks (one in Ruby and one in Smalltalk) that have something special to offer. One possible alternative language, Ruby.
It improves on Java, but that doesn't mean that Ruby will succeed, or that it's the best possible alternative.
Ruby is a dynamic, fully object-oriented language that's usually grouped with scripting languages. The scripting term, for
languages like Ruby, Smalltalk, and Python, is a little too limited, so I'll use the term applications language .
Install it and type along. It comes with a primitive IDE, but the command line works well.
From here, you can evaluate Ruby statements. You'll frequently use irb to answer those tiny questions that come up often in
You don't have to worry about primitives or wrappers at all. More importantly, you don't have to deal with those cases in an
API. Ruby's reflection, persistence engines, and XML frameworks are all much simpler, because you don't have to deal with all
the edge cases related to primitives and arrays of primitives.
Try to do an assignment without a declaration:if it works that's a strong hint that Ruby is dynamically typed. The type in
Ruby is bound to the object, but not the thing that contains it. So Ruby is dynamically typed. But Ruby won't break its
typing rules by coercing one type to another. That means Ruby is strongly typed. Actually, strongly typed is an
oversimplification. Since you can change Ruby types indiscriminately, some might consider Ruby to have weaker typing.
You've read that dynamic typing lets you focus on the right part of the problem at the right time. It eases your refactoring
burden, and reduces the amount of code that you have to write and maintain.
Ruby's conditionals will remind you more of C than Java. Ruby also has a few conventions that you should know about. ? and !
are both valid in method names. By convention, methods ending in ? are tests. For example, nil? would test to see if a value
is Nil. Methods ending in ! are potentially dangerous, because they have side effects. Ruby has an if statement. Ruby also
supports an unless statement that works the same way. You can use if or unless in block form, as you do in Java. You can also
tack them onto the end of a line, to conditionally execute a single line of code.
Ruby has two conditional loops. The loop continued until I entered the end-of-file character. Until, the other looping construct.
Ruby supports first-class range support. Ranges introduce = = =, another type of comparison. Also third type of comparison,
called match, which you'll use with regular expressions .
Ruby builds regular expressions into the syntax. Some like regular expressions and others do not. They're a critical part of
dealing with strings. In Ruby, you'll define a regular expression between slashes.
Ruby returns the index of the character at the match. Ruby regular expressions are pattern matching, such as regular
expressions and ranges.
Ruby containers are like Java's collections. Everything in an array is also an object. Ruby uses a Hash. Like Java's HashMaps, a Ruby Hash is an object. Unlike Java's HashMap.
Like Java collections, Ruby containers hold objects, and they need not be homogeneous.
- Since there's no distinction between primitives and other objects, you can put literally anything into any given container, and you can nest them easily.
- Since everything inherits from object, everything has a hash code.
- The language gives you the same syntactic sugar for hashes as for arrays.
- Code blocks make iteration tighter and easier.
Similarly, you can use Hash whenever you need a set, dictionary, or any type of unordered collection. You'll find yourself
doing more with collections, and less customized iteration.
Using Ruby Grep.rb you're effectively using a library that specifies everything on the outside of a control loop that
iterates through a file. Ruby does the repetitive dirty work for you, and you customize the inside of the control loop with a
By now, you should be getting a feel for the power and simplicity of Ruby. You can probably see how the lines of code go down
and the abstraction goes up.
- You still have to understand anything that your tools generate.
- The more code you have, the more bugs it can hide. Unit testing can take you only so far.
- Writing code is not the only cost, training, maintaining, and extending your code factor in also.
- Each code generation technique that you use limits your flexibility.
- Java developers rely increasingly on XML for configuration. Remember, configuration is still code. Developers from other languages often find Java's over-reliance on XML configuration annoying. Meanwhile, configuration in Ruby is usually clean
With Ruby, you wind up building the iteration strategies into your containers and reusing that logic.
In dynamic languages like Ruby and Smalltalk, this programming strategy gives you tremendous intellectual freedom, both in
the frameworks that you use and in the frameworks that you build.
Both Ruby and Java are object-oriented languages. Both support object models with single inheritance. Still, you're going to
see some differences between Ruby and Java:
- In Java, the smallest application is a class. In Ruby, everything is an object, so you can evaluate primitives, expressions, code blocks, and scripts. They all are objects, and all are valid Ruby.
- In Java, class definitions are static. In Ruby, you can modify your classes on the fly. When you see a class definition, if the class already exists, the new definition will modify the class that's already there.
- Ruby supports mixins and Java does not. Think of a mixin as an interface, plus an implementation, that you can attach to a class.
- In Ruby, everything returns some value, and that value is typed dynamically, so you won't see a return in the method definition.
- In Ruby, method parameters and instance variables are not typed; but the instances themselves are typed.
David Heinemeier Hansson (DHH) is the programmer of Base-camp, Backpack, and Ta-da List under the commercial banner of
- Q: What are the three most important features in Ruby that you use in Rails?
- DHH: First, meta-programming. You can manipulate a class while it's being defined. You can create domain-specific
languages, because you've got hooks everywhere into the life cycle of classes and objects... Second, open classes. Active
Record consists of around 10 layers that are all applied to the base class... Third, everything is an object, with
exceptions. You can work procedurally on top of the object orientation, but that's the order of business. It makes for an
incredibly consistent experience that really makes "The Principle of Least Surprise" come true. You can guess the names and
behavior of Ruby classes more often than not.
Ruby is object-oriented. In Ruby, instance variables start with @, class variables start with @@, and global variable start
with $. Ruby developers take advantage of open classes . You can change the class definition of an existing class. That's a
useful capability for debugging, iterative programming, and metaprogramming. Ruby also lets you subclass. To subclass, you
use the < operator. These concepts should look familiar to you. Classes package instance data and methods together. An
instance of a class is an object. All classes have single parents, and eventually inherit from Object, with the exception of
To implement a mixin, Ruby uses a concept called a module. A module lets you group together methods and classes. You can't
instantiate a module, and a module doesn't stand alone. A module isn't a class, but it does have its own namespace. Modules
form the foundation of classes and mixins. Recall that a mixin is an interface with an implementation. That means you can
group together a set of methods that many classes may need to use. You can separate an aspect, or a capability, into a mixin.
What makes mixins so powerful is this: you can also access an objects class methods in your module. In fact, we used an
object propery in the module, before we had even defined the object.
With Ruby, interception is easy. You simply take a method, rename it, and put another method in its place
AOP lets you add services to your POJOs without modifying any code. AOP helps you control the flow of your application, such
as adding custom methods at interesting points—for instance, before or after a method executes. In particular, you'll often
see AOP for:
- Debugging or logging
- Declarative services
A standardized AOP framework has never really taken off in Ruby because the language itself already supports most of the
desirable functionality of AOP. The next version of Ruby will take this a step further by including AOP-like constructs right
in the language with pre, post, and wrap conditions. The meta-programming capabilities of Ruby lie so close to the surface
and are quite accessible to the average Ruby programmer. Most of the problems addressed by AOP are addressed by meta-programming in Ruby.
Of course, AOP is a much broader tool, and if it is successful, the typical use cases obviously will grow in scope and power.
For Ruby developers, AOP is not quite as urgent, because you've already got robust tools to deal with these kinds of concerns:
- You can use interceptors. These let you add services to any object at any time. It's as easy as renaming one method and introducing another.
- You can use mixins, even attaching them at runtime. You could easily make all of the methods on a domain model secure, for example.
- You can use hooks . Ruby provides hooks so that you can inject custom code at certain well-defined locations. The next version of Ruby will support hooks called _ _before, _ _after, and _ _wrap.
In short, Ruby can already solve many AOP-like problems without AOP, and will add AOP-like features in the very near future.
The difference dependency injection in Java and Ruby is a little tougher to understand for Java developers. In Java,
dependency injection is rapidly changing the way that we build applications. It's a relatively simple concept:
A few things come up right off the bat when you look at dependency injection in Ruby. First, Java's not very good at
configuration, but Ruby lets you represent structured data quite well, often with far less invasive syntax than XML. You also
can solve many of the coupling problems by changing the definition of a class on the fly.
Some developers in Ruby seem to think dependency injection is important and that the idea will have a place in the Ruby
mainstream, given time. It should come as no surprise to you that Ruby has an outstanding dependency injection framework
Others tend to think that dependency injection should happen in spots, instead of with a single, general-purpose framework.
Since it's easy to change a class definition on the fly, you can easily inject the behavior that you need without adding
another layer of complexity, across the application. Most of the Ruby programming community seems to be converging on the
idea that Ruby's overall dynamic design makes dependency injection unnecessary for all but the most complex applications
Ruby makes some of the hard things in Java easy. More and more of the top independent consultants are looking for ways to make more money
working in Ruby, or other languages that are more dynamic. The Java community is spending an amazing amount of money and
brainpower on making Java more dynamic. Dependency injection and aspect-oriented programming are groundbreaking ideas for
Java, and they are only now getting serious commercial traction.
As we stretch Java in increasingly unnatural directions,
there's a cost. AOP and dependency injection are near-trivial exercises in Ruby, but they force Java developers to learn new
programming models, deal with XML, and introduce increasingly complex syntax. With each new meta-programming concept that we
bake into Java, it's looking more and more like all of that complexity is trying to drive us somewhere. The net effect is to
push Java further and further into the enterprise niche, and make it less and less accessible to the average Joe. Contrast
that situation with Ruby, where dependency injection and AOP don't consume your focus; you're free to apply those ideas in
spots right where you need them.