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 squareDetermine which texture to use for the shape - currently set at #'s for squares, *'s for trianglesBuild the shape based on the selected textureSample 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.1class 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    endendclass Textures < BuildShape    def shapeCheck        @texture =         if @text == square            @texture = #            buildShapeCheck        else            @texture = *            buildShapeCheck        end    endendclass UserInput < 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    endendputs Enter either \triangle\ or \square\user_input = gets.chompnewUserInput = UserInput.new(user_input.downcase)newUserInput.printInput
Building shape based on user input
beginner;ruby;formatting
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  endend# 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 < 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  endend# 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 < 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  endend# 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.chompnewUserInput = UserInput.new(user_input.downcase)# So... when you say printInput, you actually mean# do everything and print the *output*?newUserInput.printInputHere's a different way of doing things:def print_triangle(size = 5)  size.times { |count| puts * * count }enddef print_square(size = 5)  size.times { puts # * size }endwhile 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.  endendAnd 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.