A Java Developer’s Guide to Ruby

A Java Developer’s Guide to Ruby

s a Java developer, why should you learn Ruby? Because Ruby’s versatility and flexibility complement Java well, and you will be a more effective and efficient developer if you use both languages. In fact, I use Java, Ruby, and Common Lisp for all my development and Ruby has become a core part of my work life. Specifically, the following reasons make Ruby compelling for Java developers:

  • As a scripting language, Ruby is an effective tool for small projects. When I need to write utilities for data conversion and text processing quickly, I almost always use Ruby.
  • Ruby is a dynamic and terse language.
  • Using Ruby will often offer a different perspective on problem solving.
  • JRuby is still a work-in-progress but I believe it eventually will provide an excellent Ruby deployment platform using the Java VM. Currently, IntelliJ, NetBeans, and Eclipse all provide excellent Ruby support.
  • As the cost of software maintenance is roughly proportional to the number of lines of code, and Ruby programs are short and concise, they tend to be easier to read, understand, and maintain.
  • The Ruby on Rails web development framework is great for small and medium-sized database-backed web applications. You need to know Ruby if you want to use Ruby on Rails.

To demonstrate why Ruby is a good fit for Java developers, this article introduces the language features that will make you more efficient (see Table 1. Ruby and Java Feature Comparison) and then shows short program examples in both languages.

What You Need
To follow along with the rest of the article, you need to install external Ruby libraries. The RubyGems library system makes this easy. Download it from RubyForge and follow the installation instructions for your operating system. (If you already have Ruby set up, you can verify that your setup includes RubyGems?many Ruby install packages do?by typing gem in a command shell to check for installation.) Having a central repository for libraries and a standard tool like RubyGems will save you a lot of time: no searching for the libraries you need, installing them, and using them in multiple projects.

Use the following commands to install the required gems:

 gem query --remote # if you want to see all available remotely installable gemssudo gem install activerecordsudo gem install mysql # if you want to use MySQLsudo gem install postgres-pr # optional: install "pure ruby" PostgreSQL interfacesudo gem install postgres # optional: install native PostgreSQL interfacesudo gem install ferret # a search library like Lucene (same API)sudo gem install stemmer # a word stemming library for demonstrating extending a classgem query # to show gems locally installedgem specification activerecord # info on gem (ActiveRecord in this example)

Under Mac OS X and Linux, you will need to run the gem installs using sudo; if you are a Windows user, remove “sudo” from the previous commands.

This article also assumes that you will open a Ruby irb shell as follows and keep it open while you’re reading:

 markw$ irb>> s = "a b c"=> "a b c">> 

The example programs and code snippets are short enough to copy and paste into an irb interactive session.

Ruby String Handling
The Ruby String class provides a large set of string-processing methods that are more flexible than Java’s string handling capabilities. This section shows a useful subset of Ruby’s string processing. This code snippet shows how to combine strings, take them apart with slices, and then search for substrings (the examples to follow use the # character to make the rest of a line a program comment):

 require 'pp'  # use the "pretty print" library. Defines the function 'pp'# define some strings to use in our examples:s1 = "The dog chased the cat down the street"s2 = "quickly"puts s1puts s1[0..6]  # a substring slice up to and including character at index==6puts s1[0...6] # a substring slice up to (but not including) the character at index==6puts "He is a #{s2} dog #{1 + 6} days a week." # expressions inside #{} are inserted into a double quote stringputs "   test  ".strip  # create a copy of the string: the new copy has white space removedputs s1 + ' ' + s2 # string literals can also be formed with single quotesputs s2 * 4puts s1.index("chased") # find index (zero based) of a substrings1[4..6] = 'giant lizard'  # replace a substring (/dog/ -> /giant lizard/)puts s1s2 = s2 << " now"  # the << operator, which also works for arrays and other collections, copies to then endputs s2puts "All String class methods:"pp s1.methods # the method "methods" returns all methods for any object

The output would be:

 The dog chased the cat down the streetThe dogThe doHe is a quickly dog 7 days a week.testThe dog chased the cat down the street quicklyquicklyquicklyquicklyquickly8The giant lizard chased the cat down the streetquickly nowAll String class methods:["send", "%", "index", "collect", "[]=", "inspect", ......]    # most methods not shown for brevity--try this in irb

The << operator in the above example is really a method call. When evaluating expressions, Ruby translates infix operators into method calls. For example, the << operator in the following code adds the value of the expression on its right side to the value on the left side:

 >> "123" << "456"=> "123456">> "123".<<("456")=> "123456">> 1 + 2=> 3>> 1.+(2)=> 3

In the above example, using the form ".<<" is a standard method call.

Many classes use the << operator to add objects to a class-specific collection. For example, you will later see how the Ferret search library (a Ruby gem you have installed) defines the << operator to add documents to an index.

Modifying an Existing Class
The key to Ruby's versatility is the ability to extend all its classes by adding methods and data. I frequently extend core Ruby classes in my application, not in the original class source code. This likely seems strange to Java or even C++ developers, but this technique lets you keep resources for a project in one place and enables many developers to add application-specific functionality without "bloating" the original class. As a Java programmer, think how the limitations of Java constrain you: if you want to add functionality and data to an existing class, you must subclass.

The following listing shows how to add the method stem to the String class:

 begin  puts "The trips will be longer in the future".downcase.stem # stem is undefined at this pointrescue    puts 'Error:' + $!end  require "rubygems"require_gem 'stemmer'class String # you will extend the String class    include Stemmable # add methods and data defined in module Stemmableendputs "The trips will be longer in the future".downcase.stem

You will also find it useful to add methods and perhaps new class instance variables to existing classes in your application.

The next section looks at "duck typing," another example of the extreme flexibility that Ruby offers.

Ruby Duck Typing
In Java, you can call a method only on an object that is defined (with public, package, etc. visibility) in the object's class hierarchy. Suppose that you have a collection of objects and you want to iterate over each element in the collection, calling one or more methods. In Java, the objects would need to be part of the same class hierarchy or implement interfaces defining the methods that you want to call.

As you have probably already guessed, Ruby is much more flexible. Specific data types and classes are not required in Ruby's runtime method-calling scheme. Suppose you call method foo on an object obj, and then call method bar on the resulting object of this first method call as follows (the example shows two equivalent calls; when there are no method arguments, you can leave off the ()):


The result of calling obj.foo will be some object, and whatever the class of this new object is, you would attempt to call method bar on it.

As another example, suppose you want to call the method name on each object in a collection. One element in this collection happens to be of an instance of class MyClass2 that does not have a method name defined. You will get a runtime error when you first try applying method name to this object. You can fix this by dynamically adding the method as follows:

 class MyClass2    def name       "MyClass2: #{this}"    endend

Developers who are used to a strongly type checked language like Java likely will expect this "unsafe" flexibility to make their programs less reliable because the compiler or interpreter is not statically checking all type uses. However, any program bugs due to runtime type checking will be found quickly in testing, so there is no decrease in software reliability. Yet you get the benefits of a more flexible language: shorter programs and shorter development time.

Dealing with Missing Methods
Still skeptical about duck typing? Hang on, because now you are going to see another Ruby trick: how to handle missing methods for any Ruby class, starting with this simple example that applies two methods to a string object, one that is defined (length) and one that is undefined (foobar):

 markw$ irb>> s = "this is a string"=> "this is a string">> s.length=> 16>> s.foobarNoMethodError: undefined method `foobar' for "this is a string":String        from (irb):3

You'll see an error thrown for the undefined method. So "patch" the String class by writing your own method_missing method:

 >> class String>>   def method_missing(method_name, *arguments)>>     puts "Missing #{method_name} (#{arguments.join(', ')})">>   end>> end=> nil>> s.foobarMissing foobar ()=> nil>> s.foobar(1, "cat")Missing foobar (1, cat)=> nil>> 

Whenever the Ruby runtime system cannot find a method for an object, it calls the method method_missing that is initially inherited and simply raises a NoMethodError exception. This example overrode this inherited method with one that does not throw an error, and it prints out the name and arguments of the method call. Now, redefine this method again, this time checking to see if the method name (after converting it to a string with to_s) is equal to foobar:

 >> class String>>   def method_missing(method_name, *arguments)>>     if method_name.to_s=='foobar'>>       arguments.to_s.reverse  # return a value>>     else?>       raise NoMethodError, "You need to define #{method_name}">>     end>>   end>> end=> nil>> s.foobar(1, "cat")=> "tac1">> s.foobar_it(1, "cat")NoMethodError: You need to define foobar_it        from (irb):38:in `method_missing'        from (irb):43        from :0>> 

If the method name is equal to foobar, this example calculates a return value. Otherwise, it throws an error.

Ruby Code Blocks
Ruby uses code blocks as an additional way to iterate over data. These blocks offer more flexibility and power than the limited iteration functionality built into the Java language. The previous example showing basic string functionality used the stemmer gem to find the word stems of a string containing English words. The following example uses the String split method to tokenize a string using the space character as a word delimiter and then passes a code block defined using the { and } characters to mark the beginning and end of a code block (you also can use begin and end). Local variables in a block are listed between two | characters:

 puts "longs trips study studying banking".split(' ')puts "longs trips study studying banking".split(' ').each {|token| puts "#{token} : #{token.stem}"

This code snippet produces the following:

 longstripsstudystudyingbankinglongs : longtrips : tripstudy : studistudying : studibanking : bank

You can see another good use of code blocks in the following example, which uses the Array collect method. The collect method processes each array element and then passes it to a code block:

 require 'pp'pp ["the", "cat", "ran", "away"].collect {|x| x.upcase}pp ["the", "cat", "ran", "away"].collect {|x| x.upcase}.join(' ')

In this example, the code block assumes that the elements are strings and calls the upcase method on each element. The collect method returns the collected results in a new array. It also uses the method join to combine all the resulting array elements into a string, separating the elements with the space character. This is the output:


Writing Methods That Use Code Blocks
You can use the yield method to call a code block passed to a method or function call. The following example uses the method block_given? to call yield conditionally if a code block is supplied. The method yield returns a value that is printed:

 def cb_test name	puts "Code block test: argument: #{name}"	s = yield(name) if block_given?	puts "After executing an optional code block, =#{s}"end	

This example calls function cb_test, first without a code block and then with one:

 >> puts cb_test("Mark")Code block test: argument: MarkAfter executing an optional code block, =nil=> nil>> puts cb_test("Mark") {|x| x + x}Code block test: argument: MarkAfter executing an optional code block, =MarkMarknil=> nil>> 

The string value Mark is passed as an argument to yield, and inside the code block the local variable x is assigned the value Mark. The return value from the code block is MarkMark.

Ruby Regular Expressions
Ruby has built-in support for handling regular expressions using the class Regexp. Java's java.util.regex APIs offer similar functionality but regular expression support in Ruby definitely has a more native feel to it. You can create a regular expression object by either directly using a method call like Regexp.new("[a-e]og") or enclosing a regular expression between slash characters like /[a-e]og/. You can find good tutorials on both regular expressions and on Ruby's regular expression support on the web; this simple example shows only using the =~ operator:

 => 4>> "the dog ran" =~ /[a-e]og/=> 4>> "the zebra ran" =~ /[a-e]og/=> nil

Ruby Network Programming
Ruby has a great standard library for network programming as well. Please see my previous DevX article on this subject. I frequently use Ruby for collecting data from the Internet, parsing it, and then storing it in XML or a database.

Ruby Document Indexing and Search Using the Ferret Library
By now, you have installed the Ruby gem called ferret. Ferret is the fastest indexing and search library based on Java Lucene (even faster than the Common Lisp version, Montezuma). One interesting fact about the Ferret library is that during development the author David Balmain eventually wrote most of it in C with a Ruby wrapper. The lesson is that if you start to use Ruby and have performance problems, you can always recode the time-critical parts in C or C++. Ferret defines a few classes that you will use in your own applications once you adopt Ruby:

  • Document represents anything that you want to search for: a local file, a web URL, or (as you will see in the next section) text data in a relational database.
  • Field represents data elements stored in a document. Fields can be indexed or non-indexed. Typically, I use a single indexed (and thereby searchable) text field and then several "meta data" fields that are not indexed. Original file paths, web URLs, etc. can be stored in non-indexed fields.
  • Index represents the disk files that store an index.
  • Query provides APIs for search.

Indexing and Searching Microsoft Word Documents
The following is the Ruby class I use for reading Microsoft Word documents and extracting the plain text, which is an example of using external programs in Ruby:

 class ReadWordDoc  attr_reader :text  def initialize file_path    @text = `antiword #{file_path}`   # back quotes to run external program  endend

The "trick" here is that I use the open source antiword utility to actually process Word document files. You can run any external program and capture its output to a string by wrapping the external command in back quotes. Try the following under Linux or OS X (for Windows try `dir`):

 puts `ls -l`

This example prints the result of executing the external ls (Unix list directory) command.

The following Ruby script enters a Word document into an index (plain text files are easier?try that as an exercise):

 require 'rubygems'require 'ferret'include Ferretinclude Ferret::Documentrequire 'read_word_doc' # read_word_doc.rb defines class ReadWordDocindex = Index::Index.new(:path => './my_index_dir')  # any path to a directorydoc_path = 'test.doc'                   # path to a Microsoft Worddoc_text = ReadWord.new(doc_path).text  # get the plain text from the Word file doc = Document.newdoc << Field.new("doc_path", doc_path, Field::Store::YES, Field::Index::NO)doc << Field.new("text", doc_text, Field::Store::YES, Field::Index::TOKENIZED)index << docindex.search_each('text:"Ruby"') do |doc, score|  # a test search  puts "result: #{index[doc]['doc_path']} : #{score}"    # print doc_path meta data  puts "Original text: #{index[doc]['text']}"            # print original textendindex.close  # close the index when you are done with it

Notice how short this example is. In 24 lines (including the class to use antiword for extracting text from Word documents), you have seen an example that extracts text from Word, creates an index, performs a search, and then closes the index when you are done with it. Using Ruby enabled you to get complex tasks done with very few lines of code. Had you coded this example in Java using the very good Lucene library (which I've done!), the Java program would be much longer. Shorter programs are also easier and less expensive to maintain.

This example uses Word documents, but OpenOffice.org documents are simple enough to be read. With about 30 lines of pure Ruby code, you can unzip a document and extract the text from the content.xml element in the unzipped XML data stream. (XML processing is simple in Ruby, but it is beyond the scope of this article.)

Ruby Complements Java
The cost of software development and maintenance is usually the largest expense for a company's IT budget?much more expensive than servers, Internet connectivity, etc. The use of Ruby can greatly reduce the cost of building and maintaining systems, mostly because programs tend to be a lot shorter (For me, the time spent per line of code is similar for most programming languages I use).

OK, so when should you use Java? I have used the Java platform for building systems for my consulting customers for over 10 years, and I certainly will continue using Java. A well-built, Java-based web application will run forever?or at least until servers fail or have to be rebooted for hardware maintenance. My confidence comes from seeing systems run unattended for months on end with no problems. My advice is to continue using Java on the server side for large systems and to start using Ruby for small utility programs. For my work, I view Java and Ruby as complementary, and not as competitors. Use the best tool for each task.



Share the Post:
Apple Tech

Apple’s Search Engine Disruptor Brewing?

As the fourth quarter of 2023 kicks off, the technology sphere is abuzz with assorted news and advancements. Global stocks exhibit mixed results, whereas cryptocurrency

Revolutionary Job Market

AI is Reshaping the Tech Job Market

The tech industry is facing significant layoffs in 2023, with over 224,503 workers in the U.S losing their jobs. However, experts maintain that job security

Foreign Relations

US-China Trade War: Who’s Winning?

The August 2023 visit of Gina Raimondo, the U.S. Secretary of Commerce, to China demonstrated the progress being made in dialogue between the two nations.

Pandemic Recovery

Conquering Pandemic Supply Chain Struggles

The worldwide coronavirus pandemic has underscored supply chain challenges that resulted in billions of dollars in losses for automakers in 2021. Consequently, several firms are

Game Changer

How ChatGPT is Changing the Game

The AI-powered tool ChatGPT has taken the computing world by storm, receiving high praise from experts like Brex design lead, Pietro Schirano. Developed by OpenAI,

Apple Tech

Apple’s Search Engine Disruptor Brewing?

As the fourth quarter of 2023 kicks off, the technology sphere is abuzz with assorted news and advancements. Global stocks exhibit mixed results, whereas cryptocurrency tokens have seen a substantial

GlobalFoundries Titan

GlobalFoundries: Semiconductor Industry Titan

GlobalFoundries, a company that might not be a household name but has managed to make enormous strides in its relatively short 14-year history. As the third-largest semiconductor foundry in the

Revolutionary Job Market

AI is Reshaping the Tech Job Market

The tech industry is facing significant layoffs in 2023, with over 224,503 workers in the U.S losing their jobs. However, experts maintain that job security in the sector remains strong.

Foreign Relations

US-China Trade War: Who’s Winning?

The August 2023 visit of Gina Raimondo, the U.S. Secretary of Commerce, to China demonstrated the progress being made in dialogue between the two nations. However, the United States’ stance

Pandemic Recovery

Conquering Pandemic Supply Chain Struggles

The worldwide coronavirus pandemic has underscored supply chain challenges that resulted in billions of dollars in losses for automakers in 2021. Consequently, several firms are now contemplating constructing domestic manufacturing

Game Changer

How ChatGPT is Changing the Game

The AI-powered tool ChatGPT has taken the computing world by storm, receiving high praise from experts like Brex design lead, Pietro Schirano. Developed by OpenAI, ChatGPT is known for its

Future of Cybersecurity

Cybersecurity Battles: Lapsus$ Era Unfolds

In 2023, the cybersecurity field faces significant challenges due to the continuous transformation of threats and the increasing abilities of hackers. A prime example of this is the group of

Apple's AI Future

Inside Apple’s AI Expansion Plans

Rather than following the widespread pattern of job cuts in the tech sector, Apple’s CEO Tim Cook disclosed plans to increase the company’s UK workforce. The main area of focus

AI Finance

AI Stocks to Watch

As investor interest in artificial intelligence (AI) grows, many companies are highlighting their AI product plans. However, discovering AI stocks that already generate revenue from generative AI, such as OpenAI,

Web App Security

Web Application Supply Chain Security

Today’s web applications depend on a wide array of third-party components and open-source tools to function effectively. This reliance on external resources poses significant security risks, as malicious actors can

Thrilling Battle

Thrilling Battle: Germany Versus Huawei

The German interior ministry has put forward suggestions that would oblige telecommunications operators to decrease their reliance on equipment manufactured by Chinese firms Huawei and ZTE. This development comes after

iPhone 15 Unveiling

The iPhone 15’s Secrets and Surprises

As we dive into the most frequently asked questions and intriguing features, let us reiterate that the iPhone 15 brings substantial advancements in technology and design compared to its predecessors.

Chip Overcoming

iPhone 15 Pro Max: Overcoming Chip Setbacks

Apple recently faced a significant challenge in the development of a key component for its latest iPhone series, the iPhone 15 Pro Max, which was unveiled just a week ago.

Performance Camera

iPhone 15: Performance, Camera, Battery

Apple’s highly anticipated iPhone 15 has finally hit the market, sending ripples of excitement across the tech industry. For those considering upgrading to this new model, three essential features come

Battery Breakthrough

Electric Vehicle Battery Breakthrough

The prices of lithium-ion batteries have seen a considerable reduction, with the cost per kilowatt-hour dipping under $100 for the first occasion in two years, as reported by energy analytics

Economy Act Soars

Virginia’s Clean Economy Act Soars Ahead

Virginia has made significant strides towards achieving its short-term carbon-free objectives as outlined in the Clean Economy Act of 2020. Currently, about 44,000 megawatts (MW) of wind, solar, and energy

Renewable Storage Innovation

Innovative Energy Storage Solutions

The Department of Energy recently revealed a significant investment of $325 million in advanced battery technologies to store excess renewable energy produced by solar and wind sources. This funding will

Renesas Tech Revolution

Revolutionizing India’s Tech Sector with Renesas

Tushar Sharma, a semiconductor engineer at Renesas Electronics, met with Indian Prime Minister Narendra Modi to discuss the company’s support for India’s “Make in India” initiative. This initiative focuses on

Development Project

Thrilling East Windsor Mixed-Use Development

Real estate developer James Cormier, in collaboration with a partnership, has purchased 137 acres of land in Connecticut for $1.15 million with the intention of constructing residential and commercial buildings.

USA Companies

Top Software Development Companies in USA

Navigating the tech landscape to find the right partner is crucial yet challenging. This article offers a comparative glimpse into the top software development companies in the USA. Through a

Software Development

Top Software Development Companies

Looking for the best in software development? Our list of Top Software Development Companies is your gateway to finding the right tech partner. Dive in and explore the leaders in

India Web Development

Top Web Development Companies in India

In the digital race, the right web development partner is your winning edge. Dive into our curated list of top web development companies in India, and kickstart your journey to

USA Web Development

Top Web Development Companies in USA

Looking for the best web development companies in the USA? We’ve got you covered! Check out our top 10 picks to find the right partner for your online project. Your

Clean Energy Adoption

Inside Michigan’s Clean Energy Revolution

Democratic state legislators in Michigan continue to discuss and debate clean energy legislation in the hopes of establishing a comprehensive clean energy strategy for the state. A Senate committee meeting

Chips Act Revolution

European Chips Act: What is it?

In response to the intensifying worldwide technology competition, Europe has unveiled the long-awaited European Chips Act. This daring legislative proposal aims to fortify Europe’s semiconductor supply chain and enhance its