Learning the Ruby Array via Specifications with RSpec
Now that I am having fun messing with executable specs let me review how RSpec helped me better learn and use the Ruby Array. First, I walked through the tutorial on the RSpec RubyForge site, adding a pop method to my initial meanderings. See the code and the outcome of running the executable spec. First, the stack:
class Stack	
	def empty?
		@item.nil?
	end
	
	def push item
		@item = item
	end
	
	def top
		@item
	end
	
	def pop
		@topItem = @item
		@item = nil
		return @topItem
	end
end
Then, the completed executable spec:
require 'stack.rb'

context "A new Stack" do
	setup do
		@stack = Stack.new
	end
	
	specify "should be empty" do
		@stack.should_be_empty
	end

end

context "A stack with one item" do
	setup do
		@stack = Stack.new
		@stack.push "one item"
	end

	specify "should not be empty" do
		@stack.should_not_be_empty
	end
	
	specify "should return the top item when you send it 'top'" do
		@stack.top.should_equal "one item"
	end
	
	specify "should return the top item and be empty when you send it 'pop'" do
		@stack.pop.should_equal "one item"
		@stack.should_be_empty
	end
end
Here is the outcome of running the spec:

image of rspec executable

Next, I thought why not use RSpec to see if I could get the Stack object to act like a classic stack? So here I go.

First, I added a new context to the spec that would work with more than one item on the stack. Up to this point the stack was either empty or had only one item.
context "A Stack with more than one item" do

end
Next, I added specs to the context that should check that the stack is not empty, that the top item is returned when you send "top", that the stack should not be empty after you send "pop", that the stack should return the bottom item when you send "bottom", and finally, that you should be able to account for each item in the stack.
specify "should not be empty" do
end	

specify "should return the top item when you send it 'top'" do
end

specify "should not be empty after you send it 'pop'" do
end

specify "should return the bottom item when you sent it 'bottom'" do
end

specify "should be able to account for each item in the stack" do
end
Then, to populate the stack in this new context with more than one item, I added a setup method.
setup do
	@stack = Stack.new
	@stack.push "first item"
	@stack.push "second item"
	@stack.push "third item"
end
After executing the spec with the new context, all looks good.

image of rspec executable

Next, I populate the empty specs of the new context with the content of the specs that we had previously completed in the "A stack with one item" context (see the completed executable spec above) and execute the spec:

image of rspec executable

As expected, we have a problem. Note that the failure was at the "should not be empty after you send it 'pop'" spec. Upon investigation we see that although we setup the stack with three items, the failing spec revealed that there was only one item on the stack. The reason for this is the stack object only holds one item via the @item instance variable. To fix this we need a construct that can hold more than one item. Enter the Ruby Array.
I first create a constructor and instantiate the stack as an array and then modify the pop method commenting out the old code and invoking the array pop method, that I just learned, acts as a classic stack "pop":
def initialize
	@stack = Array.new
end
....
def pop
	#@topItem = @item
	#@item = nil
	#return @topItem
	
	@stack.pop
end
Then, I execute the spec:

image of rspec executable

Hmmmm. We got rid of the "A Stack with more than one item" pop spec error but now have a "A stack with one item should return the top item and be empty when you send it 'pop'" error. Upon investigation we see that the empty? method is still looking at the @item instance variable which was commented out of the pop method in favor of the @stack instance variable. The fix is to add the @stack instance variable and comment the @item variable:
def empty?
	#@item.nil?
	@stack.length == 0
end
So now let's run the spec:

image of rspec executable

Oh boy! Apparently, we need to remove the @item variable throughout the class and replace it with @stack. So to successfully run the spec, I comment the @item variables and then modify the methods with the stack instance variable.
class Stack
	def initialize
		@stack = Array.new
	end
	
	def empty?
		#@item.nil?
		@stack.length == 0
	end
	
	def push item
		#@item = item
		@stack.push item
	end
	
	def top
		#@item
		@stack[@stack.length - 1]
	end
	
	def pop
		#@topItem = @item
		#@item = nil
		#return @topItem
		
		@stack.pop
	end
end
Note the stack object above goes to length of the stack minus 1. This is because a stack operates on a "first in, last out" or a "last in, first out" (LIFO) principle. Since the "third item" placed in last it is at the top of the stack. Also, we subtract the length value of the stack by one since an array starts at zero. Next, we update the top method of the stack class as such:
specify "should return the top item when you send it 'top'" do
	@stack.top.should_equal "third item"
end
We execute the spec at see the following:

image of rspec executable

Okay. Now we are set to modify the "A Stack with more than one item should return the bottom item when you sent it 'bottom'" spec.
specify "should return the bottom item when you sent it 'bottom'" do
	@stack.bottom.should_equal "first item"
end
Notice that we expect the "first item" to be the value of the bottom item of the stack. Again, this is because a stack operates on a "first in, last out" or a "last in, first out" (LIFO) principle. Since the "first item" was added to the stack first, it is at the bottom of the stack. When we run the spec we get a error as expected as we have not added the bottom method of the stack class.

image of rspec executable

After adding the bottom method:
def bottom
	@stack[0]
end
Then we run the spec:

image of rspec executable

Next, why not spec "A Stack with more than one item should return the top item when you send it 'pop'". Since we already have a pop method for the stack object, let's add an executable spec instead of an empty one.
specify "should return the top item when you send it 'pop'" do
	@stack.pop.should_equal "third item"
end
When we execute the spec we see:

image of rspec executable

Now, we will modify the "A Stack with more than one item should be able to account for each item in the stack" spec.
specify "should be able to account for each item in the stack" do
	@stack.each {|item| @stack[item].should_equal item } 
end
And, as you guessed it, when executing the spec we get a NoMethodError since we have to each method.

image of rspec executable

Let's add the each method and execute the spec again. Note that we are taking advantage of the Ruby Array each iterator.

def each
	@stack.each{|item| item}
end

image of rspec executable

A specification would not be complete without specs for exceptions. Let's look at the new stack context. Obviously, since it is empty, we should get an exception if you invoke the pop method with an argument.
specify "should raise an ArgumentError exception if sent a 'pop' with an argument" do
end

image of rspec executable

With the pop method already in the stack object, here is the completed ArgumentError spec:
specify "should raise an ArgumentError exception if sent a 'pop' with an argument" do
	lambda { @stack.pop "an item" }.should_raise ArgumentError
end


image of rspec executable

Good. Let's do one more exception spec. How about "A stack with one item should raise an ExpectationNotMetError exception if sent a 'pop' with an expected return of an item". Here is the spec and the outcome of executing the spec:
specify "should raise an Spec::Api::ExpectationNotMetError exception if sent a 'pop' with an expected return of an item" do
end

image of rspec executable

Now let's modify the spec and execute it:
specify "should raise an Spec::Api::ExpectationNotMetError exception if sent a 'pop' with an expected return of an item" do
	lambda { @stack.pop.should_equal "second item" }.should_raise ExpectationNotMetError
end

image of rspec executable

There you go. Each step of the way was a learning experience. Moreover, the ability to approach this exercise in a step-by-step or rather spec-by-spec manner was provided by RSpec. Also, thanks to Ruby Central's Online Library Reference and Ruby Central's Programming Ruby book for assistance with better understanding the Ruby Array.
Finally, here is all the code for the stack:
class Stack	
	def initialize
		@stack = Array.new
	end
	
	def empty?
		@stack.length == 0
	end
	
	def push item
		@stack.push item
	end
	
	def top
		@stack[@stack.length - 1]
	end
	
	def pop
		@stack.pop
	end
	
	def bottom
		@stack[0]
	end
	
	def each
		@stack.each{|item| item}
	end
end
Here is all the code for the spec:
require 'stack.rb'

context "A new Stack" do
	setup do
		@stack = Stack.new
	end
	
	specify "should be empty" do
		@stack.should_be_empty
	end
	
	specify "should raise an ArgumentError exception if sent a 'pop' with an argument" do
		lambda { @stack.pop "an item" }.should_raise ArgumentError
	end
end

context "A stack with one item" do
	setup do
		@stack = Stack.new
		@stack.push "one item"
	end

	specify "should not be empty" do
		@stack.should_not_be_empty
	end
	
	specify "should return the top item when you send it 'top'" do
		@stack.top.should_equal "one item"
	end
	
	specify "should return the top item and be empty when you send it 'pop'" do
		@stack.pop.should_equal "one item"
		@stack.should_be_empty
	end
	
	specify "should raise an Spec::Api::ExpectationNotMetError exception if sent a 'pop' with an expected return of an item" do
		lambda { @stack.pop.should_equal "second item" }.should_raise Spec::Api::ExpectationNotMetError
	end
end

context "A Stack with more than one item" do
	setup do
		@stack = Stack.new
		@stack.push "first item"
		@stack.push "second item"
		@stack.push "third item"
	end
	
	specify "should not be empty" do
		@stack.should_not_be_empty
	end	
	
	specify "should return the top item when you send it 'top'" do
		@stack.top.should_equal "third item"
	end
	
	specify "should not be empty after you send it 'pop'" do
		@stack.pop
		@stack.should_not_be_empty
	end
	
	specify "should return the top item when you send it 'pop'" do
		@stack.pop.should_equal "third item"
	end
	
	specify "should return the bottom item when you sent it 'bottom'" do
		@stack.bottom.should_equal "first item"
	end
	
	specify "should be able to account for each item in the stack" do
		@stack.each {|item| @stack[item].should_equal item } 
	end
end


Valid XHTML 1.0 Transitional