Gamer

I recently had an experience which resulted in a friend giving me some great feedback on how to write more readable and idiomatic ruby. Inspired by this, I decided to dig up an old simple ruby program I was playing around with when I first started to teach myself. I wanted to polish it up, and so I spent a weekend refactoring and going through it. The program basically takes user input through a text file, processes the commands given and displays the result. Being that I’m a fan of videogames, I thought that this would be fun to use this as the topic. To begin, I wrote up some tests before each functionality I wanted to create:

describe Gamer do
  describe '#add' do
    it 'adds a new game' do
      input_file = File.open("input.txt", "r")
      gamer = Gamer.new(input_file)
      gamer.parse_input
      expect(gamer.games.first).to be_a(Game)
    end
  end

  describe '#update' do
    it 'updates an existing game' do
      input_file = File.open("input.txt", "r")
      gamer = Gamer.new(input_file)
      gamer.parse_input
      expect(gamer.games.first.year).to eq('1995')
    end
  end

  describe '#delete' do
    it 'deletes an existing game' do
      input_file = File.open("input.txt", "r")
      gamer = Gamer.new(input_file)
      gamer.parse_input
      expect(gamer.get_game('Shaq-fu')).to be_nil
    end
  end
end

In my tests, the Gamer class is being initialized with the input_file which contains the following commands:

Add Earthbound 1996 SNES
Update Earthbound 1995 SNES
Add Shaq-fu 1994 SNES
Delete Shaq-fu

This input gets pushed through to Gamer#parse_input which will split the string and interpret the first index as the command. From there it pushes it to one of the methods I plan to set up:

def parse_input
  @input.readlines.each do |line|
    command, *args = line.split

    case command.downcase
    when "add"
      add(args)
    when "update"
      update(args)
    when "delete"
      delete(args)
    when "show"
      show
    else
      raise "Error: invalid command #{ command }."
    end
  end
end

For Gamer#add I use the additional arguments to initialize a Game class and push this to an array to be used later:

def add(args)
  name, year, system = args
  @games << Game.new(name, year, system)
end

From here, I can use my array of games to find a specific title that I want to work with. This is is useful for Gamer#update and Gamer#delete. To accomplish this I use Gamer#get_game which will find a game in my array based on the name parameter:

def get_game(name)
  @games.find { |game| game.name == name }
end

Using this the Gamer#update and Gamer#delete are pretty straightforward:

def update(args)
  name, year, system = args
  @game = get_game(name)
  @game.year = year
end

def delete(args)
  name, year, system = args
  @game = get_game(name)
  @games.delete @game
end

Finally, Gamer#show will display all of our games, which I print out in this format:

def show
  puts "Your List of games:"
  @games.each_with_index do  |game, id|
    id += 1  
    puts "#{id}.) #{game.name} | #{game.year} | #{game.system}"
    puts "...thank you!"
  end
end

Some doubts I still have/Things I want to work on:

  • Not sure why I need to have all arguments pass through Gamer#delete in order for it to actually remove the game. #delete(name) alone doesn’t seem to do it.
  • I would like to add a rating system and perhaps a System class so that I can reference games belonging to a system or add more functionality from there.
  • Any additions or suggestions from others (checkout the github repo)

Rewriting this program has been a great joy and very relaxing. It was very nice just sitting on a weekend and writing pure ruby. Currently I am continuing work on a recipe app in rails. I am considering writing a similar program in pure Ruby first just meditate on what I want the app to do, relax and get more more more practice!.

Written on January 22, 2015