Over a million developers have joined DZone.

Ruby - Crafting An Object - The Behavior Of A Ticket

· Web Dev Zone

Start coding today to experience the powerful engine that drives data application’s development, brought to you in partnership with Qlik.

A ticket is a familiar object, with a known set of properties and behaviors. Let’s take a high-level view at what we expect a ticket-like Ruby object to do and to know about itself.

The ticket object, behavior-first

A ticket object should be able to provide data about itself. It should field requests for information about the event it’s for: when, where, name of event; performer; which seat; how much it costs.
When asked, the ticket will provide the following information, based on an imaginary public reading by Mark Twain in 1903:

01/02/03
Town Hall
Author's reading
Mark Twain
Second Balcony, row J, seat 12
$5.50

The goal is to create an object from which we can easily get all this information.

This article is based on chapter 3 from The Well-Grounded Rubyist by David Black, to be published January 2009. It is being reproduced here by permission from Manning Publications. Manning early access books and ebooks are sold exclusively through Manning. Visit the book's page for more information

[img_assist|nid=6662|title=|desc=|link=url|url=http://www.manning.com/black2/|align=right|width=146|height=182]
CREATING THE TICKET OBJECT

A generic object will serve as the basis for the ticket:

ticket = Object.new

Once it exists, you can start to endow the object ticket with properties and data by defining methods, each returning the appropriate value.

def ticket.venue
"Town Hall"
end

def ticket.performer
"Mark Twain"
end

def ticket.event
"Author's reading"
end

def ticket.price
5.50
end

def ticket.seat
"Second Balcony, row J, seat 12"
end

def ticket.date
"01/02/03"
end

Cueball in code and text

The majority of the methods defined here return string values. You can see this at a glance: they hand back a value inside quotation marks. The price method #1 returns a floating-point decimal number: 5.50.

WARNING: THE JOY (OR NOT) OF FLOATS
Floating-point numbers have more complexity and quirkiness than you may think. Some day you’ll probably come across something similar to this frequently-cited example:

This code is part of the callout.

puts 0.5 - 0.4 - 0.1
-2.77555756156289e-17

The problem - or, more accurately, the inevitable consequence of the laws of mathematics and computers - is that decimal floating-point numbers of arbitrary length can’t be stored and operated on in binary form with complete accuracy. Don’t be surprised if your “floats” don’t act as integer-like as you might wish or expect. Now that the ticket object knows a little about itself, let’s ask it to share the information.

Querying the ticket object

Rather than produce a raw list of items, let’s generate a reader-friendly summary of the details of the ticket. The use of print and puts can help get the information into more or less narrative form:

print "This ticket is for: "
print ticket.event + ", at "
print ticket.venue + ", on "
puts ticket.date + "."
print "The performer is "
puts ticket.performer + "."
print "The seat is "
print ticket.seat + ", "
print "and it costs $"
puts "%.2f." % ticket.price

Save all the code, starting with ticket = Object.new, to a file called ticket.rb, and run it. You’ll see the following:

This ticket is for: Author's reading, at Town Hall, on 01/02/03.
The performer is Mark Twain.
The seat is Second Balcony, row J, seat 12, and it costs $5.50.

The code for this example consists of a series of calls to the methods defined earlier: ticket.event, ticket.venue, and so forth. The printing code embeds those calls - in other words, embeds the return values of those methods ("Author’s reading", "Town Hall", and so on) - in a succession of output commands, and adds connectors (", at", ", on", and so forth) to make the text read well and look nice.

The Twain ticket is a simple example, but it encompasses some vital Ruby procedures and principles. The most important lesson is that the knowledge necessary for the program to do anything useful resides in the object. The ticket object has the knowledge; you tap into that knowledge by asking the ticket for it, via method calls. Nothing is more central to Ruby programming than this. It's all about asking objects to do things and tell you things.

The ticket code works, and embodies useful lessons; but it’s wordy. Ruby has a reputation as a powerful, high-level language. You’re supposed to be able to get a lot done with relatively little code. But the ticket example takes nine lines of print and puts instructions to generate three lines of output.
Let's improve that ratio a bit.

Shortening the ticket code via string interpolation

The goal of shortening the output of this little program gives us an excuse to dip into one of the most useful programming techniques available in Ruby: string interpolation. The string interpolation operator gives you a way to drop variables, method-return values, or anything else into a string. This can save you a lot of back-and-forth between print and puts.

Moreover, strings can be concatenated with the plus sign (+). Here’s how the printing code looks, using string interpolation to drop the values you need into place and string "addition" to consolidate multiple puts calls into one:

puts "This ticket is for: #{ticket.event}, at #{ticket.venue}." +
"The performer is #{ticket.performer}." +
"The seat is #{ticket.seat}, " +
"and it costs $#{"%.2f." % ticket.price}"

Whatever’s inside the interpolation operator #{...} gets calculated separately, and the results of the calculation are pasted automatically into the string. When you run these lines, you don’t see the #{...} operator on your screen; instead, you see the results of calculating or evaluating what was inside that operator.

Interpolation helped eliminate six of nine lines of codeand also made the code look a lot more like the eventual format of the output, rather than something that works but doesn’t convey much visual information. So far, we’ve been asking the ticket for information in the form of strings and numbers. Tickets also have some true/false—Boolean—information about themselves.

Ticket availability: expressing Boolean state in a method

By way of Boolean information, consider the matter of whether a ticket has been sold or is still available. One way to endow a ticket with knowledge of its own availability status is this:

def ticket.availability_status
"sold"
end

Another way is to ask the ticket whether it’s available and have it report back true or false:

def ticket.available?
false
end

false is a special term in Ruby, as is the term true. true and false are objects. The reason for their existence is to provide a way to express truth and falsehood—which is helpful when you’re writing conditional (if-based) statements or methods where all you need to know is whether something is true (as opposed to methods where you need a number, a string, or some other object).

You may have noticed that the method name available? ends with a question mark. Ruby lets you do this so you can write methods that evaluate to true or false and make the method calls look like questions:

if ticket.available?
puts "You're in luck!"
else
puts "Sorry--that seat has been sold."
end

Every expression in Ruby evaluates to an object; and every object in Ruby has a truth-value. The truth-value of almost every object in Ruby is true. The only objects whose truth-value (or Boolean value) is false are the objects false and the special non-entity object nil. For the moment, you can think of both false and nil as functionally equivalent indicators of a negative test outcome.

Playing around with if expressions in irb is a good way to get a feel for how conditional logic plays out in Ruby. Try some examples like these:

>> if "abc"
>> puts "Strings are 'true' in Ruby!"
>> end
Strings are 'true' in Ruby!
=> nil
>> if 123
>> puts "So are numbers!"
>> end
So are numbers!
=> nil
>> if 0
>> puts "Even 0 is true, which it isn't in some languages."
>> end
Even 0 is true, which it isn't in some languages.
=> nil
>> if 1 == 2
>> puts "One doesn't equal two, so this won't appear."
>> end
=> nil

(The indentation in this irb session isn't mandatory, but indenting the bodies of if statements, as you would in a program file, makes even an irb transcript easier to follow.)

Notice how irb not only obeys the puts method-calls (when conditions are right) but also, on its own initiative, outputs the value of the entire expression. In the cases where the puts happens, the whole expression evaluates to nil—because the return values of puts is always nil. In the last case, where the string isn’t printed (because the condition fails), the value of the expression is also nil—because an if statement that fails (and has no else branch to salvage it) also evaluates to nil.

Remembering that nil has a Boolean value of false, you can, if you wish, get into acrobatics with irb. A call to puts returns nil and is therefore false, even though the string gets printed. If you put puts in an if clause, the clause will be false. But it will still be evaluated. So...

>> if puts "You'll see this"; puts "but not this"; end
You'll see this
=> nil

The first puts is executed, but the value it returns, namely nil, isn’t true in the Boolean sense—so the second puts isn’t executed.

This is, to use the popular phrase, a contrived example. But it’s a good idea to get used to the fact that everything in Ruby has a Boolean value, and sometimes it’s not what you may expect. As is often the case, irb can be a great help in getting a handle on this concept. You’ve now manually given your ticket object some behaviors.

Create data driven applications in Qlik’s free and easy to use coding environment, brought to you in partnership with Qlik.

Topics:

Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
Subscribe

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}