Building shape based on user input The purpose of the below code is to build a shape based on the user input, specifically:


Prompt the user to enter a shape - either a triangle or a square
Determine which "texture" to use for the shape - currently set at #'s for squares, *'s for triangles
Build the shape based on the selected texture


Sample output for "Square":

#####
#####
#####


Sample output for "Triangle":

*
**
***
****
*****


How can I completely redo the structure and flow of the program to better follow convention, especially in regards to the way the classes are structured? I still want use multiple classes however, as the main thing i'm trying to do here is work out how to use classes properly. Any other general refactoring welcome too. 

Also sorry for the tab spacing. It's set to 2, but is a lot more when I paste it here.

# Shape Builder v0.1


class BuildShape
  def buildShapeCheck
        if @text == "square"
            self.buildSquare
        else
            self.buildTriangle
        end
    end

    def buildSquare
        (1..3).each do
            puts @texture*5
        end
    end

    def buildTriangle
        j = 1
        (1..5).each do
            puts @texture*j
            j += 1
        end
    end
end


class Textures &lt; BuildShape
    def shapeCheck
        @texture = ""
        if @text == "square"
            @texture = "#"
            buildShapeCheck
        else
            @texture = "*"
            buildShapeCheck
        end
    end
end


class UserInput &lt; Textures
    def initialize(text)
        @text = text
    end

    def printInput
        print "you entered: " 
        puts @text
        self.testIfValid
    end

    def testIfValid
        if @text == "square" || @text == "triangle"
            puts "#{@text} is a valid shape."
            shapeCheck
        else
            puts "#{@text} is not a valid shape, try again"
            newUserInput = UserInput.new(gets.chomp.downcase)
            newUserInput.testIfValid
        end
    end
end


puts "Enter either \"triangle\" or \"square\""
user_input = gets.chomp
newUserInput = UserInput.new(user_input.downcase)

newUserInput.printInput
Ok, so... there's a lot to talk about here. I've simply gone through it line by line, and added comments. So prepare for a pretty long review.

As for the indentation, the reason it's different when you paste it here is probably that you're still using tabs. It should be soft tabs, i.e. just 2 space characters. Not 1 tab character set to be 2 characters wide, but actual spaces.

Anyway, long review incoming.

# BuildShape is a poor name for a class. Classes should generally be nouns,
# but "Build shape" is an imperative. It's true that the class does build a
# shape, but the class in itself is not "the act of building".
class BuildShape

  # This class has no "initialize" method, but because its parent
  # class (which is Object, when nothing else is specified) has one,
  # I can still call "BuildShape.new.buildSquare" (for instance). But
  # if I do, things will be weird, because @text and @texture haven't
  # been defined. And without an initialize method (or attribute
  # setters), there's no way to define them.


  # Don't duplicate the name of the class in the names of its methods.
  # There's simply no need to do that; you don't need to name a file
  # on your computer after the folder it's in.
  # Also, Ruby uses underscored method names, so if anything, it should
  # be called "build_shape_check"
  def buildShapeCheck
    if @text == "square"
      self.buildSquare   # no need for "self." here
    else
      self.buildTriangle # or here
    end
  end

  # This produces a 5x3 rectangle - not a square!
  # It looks like a square because of the font you use to
  # show it, but that's coincidental and may not hold true
  # in all situations. So either make the method produce a
  # real NxN square, or make it very clear in the comments
  # that "by square, I do not actually mean a square" (i.e.
  # admit the deceit and its reasons)
  def buildSquare
    (1..3).each do # you could also just use "3.times do"
      puts @texture*5
    end
  end

  # again: Underscore naming style
  def buildTriangle
    j = 1  # This is completely unnecessary...
    (1..5).each do # ...if you just add |j| as a block argument
      puts @texture*j
      j += 1 # And then this can go too
    end
  end
end


# When I said "classes should be nouns" I meant *singular* nouns.
# The String class is not called "Strings" for instance.
# If anything, this should be called "Texture"
# But more importantly, this class doesn't make sense. You use
# class inheritance because you have a generic class and want to
# make a specialization of that class. E.g. you have a class called
# "Vehicle", and you make a class called "Car" or "Boat". Those are
# specializations of a common thing: a vehicle.
# But here, you seem to be making a new class for a completely
# different reason - a reason I can't quite figure out.
class Textures &lt; BuildShape

  # Again, no initialize method...

  # Again you're kinda-sorta repeating the name of the class
  # in the method name, because - due to inheritance - this class
  # is a BuildShape. It'd be enough to call the method "check".
  # ... buuut, this method isn't checking anything, so it'd
  # still be a terrible name.
  def shapeCheck
    # This is unnecessary; @texture *will* be set to either "#"
    # or to "*" - there's no reason to set it to anything else
    # beforehand.
    @texture = ""

    # Another thing is that this method is pointless in many ways.
    # You can't set the @texture variable anywhere else, which means
    # @texture is 100% dependent on what @text is. Which, in turn,
    # means you can just figure out the right texture when you're
    # drawing the shape. You don't even need the @texture variable;
    # the methods that print the shape are already different depending
    # on whether you want a triangle or a square; they can just print
    # the correct character themselves.
    if @text == "square"
      @texture = "#"

      # Don't call this here, *and* in the else-block: Call it *after*
      # the if-else instead, since you want to call independent of
      # what the @text is
      buildShapeCheck
    else
      @texture = "*"
      buildShapeCheck # Again: Delete this
    end
  end
end


# Ok, this is a better name of a class. UserInput sounds like a
# class name. But of course, your inheritance chain is saying that
# "UserInput is a kind of Textures, which is a kind of BuildShape"
# Do you see how that doesn't make sense?
# It also highlights why the other classes are problematic: You
# can't use them on their own. Only after 2 levels of inheritance
# do you get the class that actually solves the task. The preceding
# classes don't solve anything by themselves, you're simply
# treating them as stepping stones to the class you actually want.
# They're dependent on you extending them, which, to reuse the
# analogy from earlier, is like saying that vehicles don't work
# until someone builds a boat; it's backwards.
class UserInput &lt; Textures

  # Yay! An initialize method!
  def initialize(text)
    @text = text
  end

  # Ok, it prints the input - but it also checks the input
  # and that's not at all obvious. Your methods should only
  # do what it says on the tin.
  def printInput
    print "you entered: " 
    puts @text
    self.testIfValid
  end

  # Uh, no. The name of this method is again pretty wrong.
  # Does it mean "it only does its test, if things are valid"?
  # The Ruby-like name for this thing would be "valid?"
  # And again, the method isn't just testing; it's also
  # the method that actually produces the output! AND it
  # completely duplicating code from *outside* the class.
  # What's worse, it creates a instance of itself, within
  # itself, and... honestly, I don't know how to best explain
  # how weird this is. Sorry, but that the case.
  def testIfValid
    if @text == "square" || @text == "triangle"
      puts "#{@text} is a valid shape."
      shapeCheck
    else
      puts "#{@text} is not a valid shape, try again"
      newUserInput = UserInput.new(gets.chomp.downcase)
      newUserInput.testIfValid
    end
  end
end

# If you just used single quotes, you wouldn't have to escape
# the inner double quotes...
puts "Enter either \"triangle\" or \"square\""

# Terrible naming: "user_input" is *not* a UserInput object;
# that's "newUserInput"! Which, again, shouldn't be CamelCased
# and just plain shouldn't be called that.
user_input = gets.chomp
newUserInput = UserInput.new(user_input.downcase)

# So... when you say printInput, you actually mean
# "do everything and print the *output*"?
newUserInput.printInput


Here's a different way of doing things:

def print_triangle(size = 5)
  size.times { |count| puts "*" * count }
end

def print_square(size = 5)
  size.times { puts "#" * size }
end

while true # loop until further notice
  puts 'Please type either "triangle" or "square"'
  type = gets.chomp.downcase
  if %w(triangle square).include?(type) # did the user enter something valid?
    send("print_#{type}")               # call the proper method
    break                               # stop looping
  else
    puts "Sorry, that didn't make sense. Try again."
  end
end


And done. I know this doesn't use any class hierarchy or anything like that, but that's also to illustrate that it isn't always necessary. Just because you can doesn't mean you should.