CS 240 Homework 2 - Ruby

Due Fri Sep 19 Sun Sep 21 at 11:59pm

Overview

In this assignment you will do some simple programming exercises to get familiar and comfortable with the Ruby language. You will also practice using the testing tool RSpec to automatically test and grade your code.

Objectives

Necessary Files

You will want to get the starter code and instructions from the GitHub homework repository. Be sure and pull the latest version of the homework upstream repo, per the instructions in Hwk1. The starter code you're looking for is in the hwk2 folder.

Assignment Details

If you have time, it may be helpful to go through a tutorial on using Ruby. I recommend either Ruby in Twenty Minutes or the Codecademy Ruby course.

The goal of this multi-part assignment is to get you accustomed to basic Ruby coding and introduce you to RSpec, the unit testing tool we will be using heavily.

RSpec

RSpec is a tool testing Ruby code (see Sections 8.1 and 8.2 of the text). It lets you define a series of tests (a "spec") which can be run to tell you if your code does what it is supposed to. While I will provide explanations of what your code should do, you should get accustomed to the idea that with Test-Driven Development, the true specification for code is in these test files!

I have provided test files for all of the code you need to write in this assignment: you can thus check your work by running the appopriate spec file.

Appetizer: Warmup

Specs: spec/warmup_spec.rb

In the first part of the assignment you'll write some simple Ruby methods. Check the documentation for Array, Hash, Enumerable, and String, as they could help tremendously with these exercises.

Entree: Fun With Strings

Specs: spec/fun_with_strings_spec.rb

In the second part of the assignment, you'll implement some functions that perform basic string processing. You should start from the provided fun_with_strings.rb template. This arranges to mix your methods into the String class, so that methods can be called like

  "redivider".palindrome?    # => should return true
  "adam".palindrome?         # => should return false or nil

Dessert: Basic Object-Oriented Programming

Specs: spec/dessert_spec.rb

In the third part of this assignment, you'll develop a simple class and do some basic object-oriented programming.

Coffee: Ruby Metaprogramming

Specs: spec/attr_accessor_with_history_spec.rb

In the last part of the assignment you will perform some in-depth metaprogramming to extend Ruby classes. Note that this part is conceptually challenging but not programmatically complex.

The textbook discusses how attr_accessor uses metaprogramming to create getters and setters for object attributes on the fly. Define a method attr_accessor_with_history that provides the same functionality as attr_accessor but also tracks every value the attribute has ever had:

  class Foo 
    attr_accessor_with_history :bar
  end

  f = Foo.new        
  f.bar = 3          # => 3
  f.bar = :wowzo     # => :wowzo
  f.bar = 'boo!'     # => 'boo!'
  f.bar_history      # => [nil, 3, :wowzo]

Calling bar_history before bar's setter is ever called should return nil.

History of instance variables should be maintained separately for each object instance. that is:

  f = Foo.new
  f.bar = 1 ; f.bar = 2
  g = Foo.new
  g.bar = 3 ; g.bar = 4
  g.bar_history

then the last line should just return [nil,3], rather than [nil,1,3].

If you're interested in how the template works, the first thing to notice is that if we define attr_accessor_with_history in class Class, we can use it as in the snippet above. This is because a Ruby class like Foo or String is actually just an object of class Class. (If that makes your brain hurt, just don't worry about it for now. It'll come.)

The second thing to notice is that Ruby provides a method class_eval that takes a string and evaluates it in the context of the current class, that is, the class from which you're calling attr_accessor_with_history. This string will need to contain a method definition that implements a setter-with-history for the desired attribute attr_name. In short: this method will contain inside it the code to define an accessor for the arbitrary variable with attr_name, where the name of the variable is itself a variable. Wow. Much meta.

If you think of Generics in Java, where the "type" of the generic is itself a variable, you can get a feel for what is going on here.

Hints:

Submitting

Make sure that your code passes all tests, and then push your code out to your personal homework repo (which you set you in Homework 1).

Grading

Each part of this assignment will be graded out of 10 points, based on the number of tests that pass.