Today we'll be looking at one of the major strengths of the Ruby: classes!
Start by copying the file NameTester.rb from the course directory into your directory for this lab. Using your favorite text editor, open it up and take a few minutes to look over its contents. Then run it using the command:
ruby NameTester.rband verify that it runs correctly.
The Name class. Uncomment the line:
# name = Name.new("John", "Paul", "Jones")
Then re-run your program.
What happens?
To fix the problem, we need to define a Ruby class named Name. Here is the basic BNF:
ClassDec ::= class identifier SectionList end SectionList ::= ∅ | Specifier DeclarationList SectionList Specifier ::= public | private | protected DeclarationList ::= ∅ | Declaration DeclarationList
The identifier for Ruby classes must always begin with a capital letter. This is because Ruby has a semantic rule that constants must begin with a capital letter. If you think about what a class is, it is a blueprint -- a static structure that doesn't change -- and so this semantic rule makes a lot of sense.
Using the BNF above, we might create this skeleton to work with:
class Name endAdd this to NameTester.rb. Now, we just have to fill in the operations!
Initialization. To initialize the members of a class in Ruby, we define a method named (surprise!) initialize():
class Name
def initialize(first, middle, last)
@first, @middle, @last = first, middle, last
end
end
The initialize() method serves as the class constructor.
It gets called when you send the class the
new message, as in Name.new.
Looking at the definition of initialize() for Name, we can see an interesting bit of Ruby syntax. The technique here is called parallel assignment, and it allows us to chain together pairs of assignment statements so that we can assign @first, @middle, and @last all on one line.
Add this to your Name class. Then test what you have done so far. If you've not made any mistakes, your program should now pass the test!
@ttributes. You'll notice that the variables on the left side of the assignment statement are prepended by @ symbols. That is because identifiers that begin with @ are instance variables or attributes in Ruby. Unlike other languages, Ruby does not require instance variables to be declared, aside from using their names in a method.
Accessors. Uncomment the first assert line in the driver. Re-run your program. What happens?
To fix this, we could write separate accessor methods for each of our instance variables as we have done in our other languages. However Ruby provides a way for us to write all of our accessor methods in a single line of code:
class Name
def initialize(first, middle, last)
@first, @middle, @last = first, middle, last
end
attr_reader :first, :middle, :last
end
Take a moment to add this line to your class.
To better understand what this line does, consider that Ruby provides the following special "shortcut" commands:
| Shortcut | Is A Shortcut For |
|---|---|
| attr_reader :member | def member; @member; end |
| attr_writer :member | def member=(newMember); @member = newMember; end |
| attr_accessor :member | attr_reader :member; attr_writer :member |
The attr_reader command thus lets us conveniently define "getters", the attr_writer command lets us define "setters", and the attr_accessor lets us define both!
We have used attr_reader because all we need is the reader-type accessor methods. As indicated, one attr_reader can handle multiple members if you separate them with commas.
In order to use any of these shortcuts, you'll need to give them the name of the attributes to set up. As we've shown above, Ruby provides a special format used for labeling and identifying called the symbol. A symbol is like a lightweight string, and it's used extensively in Ruby. The syntax is simple: just prepend a colon to a string of characters. Examples of symbols include :name, :id, and :hello.
In the example above, we pass attr_reader a list of our attributes as symbols. It then uses those symbols to generate reader-methods for us. The one line:
attr_reader :first, :middle, :lastsaves us from having to write:
def first
@first
end
def middle
@middle
end
def last
@last
end
We don't need it (so don't do it), but if we were to write
class Name
attr_accessor :first
end
it would be the same as if we had
written the following code:
class Name
def first
@first
end
def first=(new_first)
@first = (new_first)
end
end
If we wanted to write readers and writers that had special functionality (e.g., writers that validated their parameters), we could use this approach to write them. But since all we need is to retrieve our instance variable values, we will stick to attr_reader.
If you have not already done so, use this information to create accessors for the three instance variables; then make certain that the assertions for all three "getters" are uncommented, re-run your program, and verify that your class passes all three of those tests before continuing.
The fullName Method. Uncomment the next-to-last assertion, run your program, and verify that the assertion fails.
To pass the test, we must define a fullName method that converts a Name into a string. To do so, we need a way to concatenate strings. In Ruby, we can use the + operator to perform string concatenation.
Using this information, define method fullName. Then re-run your program and verify that your method now passes the test. Continue when it does.
The print Method. Uncomment the final assertion in the program. Re-run your program and verify that we fail the test.
To pass this test, we need to write a method that prints the full name to the screen and then returns that name to the caller. Since we have defined the fullName method, this is simple. The only "gotcha" is that you'll need to use puts instead of print to do the actual output, because calling print within a method called print can cause a problem...
Note that the test expects this method to return the name being printed, so be sure to make it do so. (Remember, Ruby methods return the last expression they evaluate.) Aside from that, the method should be easy to write.
When your are done, make sure that all of the test-code is uncommented and test your class. When everything works correctly, take a few minutes to document each of your methods.
Then use script to make a recording named script.ruby in which you list and then run NameTester.rb.
That concludes the Ruby part of this lab.
If this was your last exercise, return to the lab 08 page and follow the "Turn In" instructions there.
Calvin > CS > 214 > Labs > 08 > Ruby