From Comics to the Command Line…
By: Robert Shilcof
Tags: Ruby, API, Cli, Application, Education

Superhero Battles ruby CLI application

My first project with the Flatiron school was to develop Superhero Battles, a simple ruby command line interface (CLI) application that created superhero cards from data pulled from a freely available API, and allowed the user to play a game versus a computer player.

Whilst writing the logic for the game was an interesting exercise, the functionality of the CLI is what this blog post will cover.

API requesting

To get the data required, I wrote an API class, that utilised the in-built ruby libraries of net/http and open-uri to send a get request to the chosen website’s URL. However, the format in which the information was returned was not readily interpretable with ruby methods. Therefore, the JSON gem was utilised to convert this information into an array of superhero hashes that could be easily manipulated. The utilisation of these methods is shown below.

class API

def get_heroes uri = URI("https://raw.githubusercontent.com/akabab/superhero- api/0.3.0/api/all.json") JSON.parse(Net::HTTP.get(uri)) end

end

To minimise the number of API requests the numerous hashes of superheroes obtained were made into instances of the newly written Card class, that instead of having key, value pairs, had attribute accessors that could be called to retrieve their information.

Not only did the individual Cards now contain information about themselves, but the class itself kept a record of all the Cards that it had built through the class method .all as shown.

class Card

attr_accessor :bio def initialize(hero_hash)

self.bio = hero_hash

hero_hash.each do |key, value|

self.class.attr_accessor(key)

self.send("#{key}=", value)

end

end def self.create_from_array(array)

@@all = array.collect{|hero_hash| Card.new(hero_hash)}

end def self.all

@@all

end

end

Application navigation

With the application now containing a fully initialised class of cards, it was necessary to enhance the CLI class with the functionality to display them to the user.

An initial approach I used was to iterate through through the collection of cards and print their names to the terminal. However, as there were in excess of 500 cards, I thought it best to write methods that would display them in pages that the user could navigate through.

This was the beginning of the divergence from a linear application and the first place where the user was going to have to input options of how it should proceed. This allowed me to explore and build the basis for how the application would display user options, and how the user’s inputs would be validated.

Aiming to write robust methods that could handle the addition of new features to the application, I considered how to present the user with options. At each stage there would be a certain number of choices, and these choices would all lead to different responses from the application.

Every time the user must make a choice I decided that they would be presented with a numbered list of predefined options. These options were stored as array constants that varied based on where they were in the application.

For all of the options in each of the option arrays, there was a corresponding message to give to the user stored in a hash constant.

Example of some of the finalised menus and options within the Superhero Battles application.

An advantage of taking the input in the form of a number, was that it could be easily validated by checking that it did not exceed the size of the options array that was being presented, and if it did, the user would be prompted for another input.

With the input validated, it was a matter of selecting the corresponding option from the array and calling the associated method. To allow the application to call the methods with the “send” method, all of the options were given the same name as the function that they would call.

Send

I chose ruby’s send method to implement the functionality of my navigation methods because it eliminated the need for if or case statements to determine how the application should proceed.

Send functions by taking a symbol as an input, if a string is given, it is converted to a symbol. The send method then operates similarly to the dot notation, by calling the method named the same as the string onto whatever object it is being called on. Send can also be called with additional arguments, which will themselves act as the arguments for the method being sent.

In the example below, both lines of code are functionally synonymous, and will return 3, the size of the presented array.

["a", "b", "c"].size ["a", "b", "c"].send(:size)

Send can be called implicitly, a feature common to other methods, and the beauty of this is that when the user enters a number for their decision and the corresponding method has been looked up from the options array, it can be sent directly to the CLI class instance. Marvellous!

Final code example

The reusability of send is highlighted below in a code snippet, where it can be seen that by calling options_and_do with an options argument, all that is required is a valid input from the user and the application will call the necessary function.

This means that if further options are added to the application no extra code needs to be written to handle user inputs or for decision logic. All that is required to do is to add the new method to the desired options arrays, and these methods will alter the menus that the user sees and accept an additional input as the size of the options array will have increased.

def options_and_do(options)

put_options(options)

send(get_and_validate(options))

end def put_options(options)

puts "

Please enter the number for the option that you would like:



" + options.collect.with_index(1){|option, i| "#{i} #{OPTIONS_HASH[option]}

"}.join

end def get_and_validate(options)

input = gets.strip.to_i

while !(1..options.size).include?(input)

puts "

Sorry, that input is invalid.

"

input = gets.strip.to_i

end

options[input - 1]

end

The rest of this code can be found on my Github page. I challenge you to download the application, play a game of Superhero Battles vs the computer and let me know how you get on!