Building my first CLI application

An adventure in project management, user journeys, testing, and ActiveRecord validation

Erin
My Own Utopia
6 min readAug 26, 2019

--

Creating an application from the ground-up certainly gives you a deep appreciation for how much consideration goes into the design and development of the tech products we use on a daily basis. Even the most basic of features we may take for-granted have to be detailed (e.g. all of our CRUD actions — create, read, update, delete) must be built.

Multi-coloured pens and paper FTW

As you start with nothing, the first thing that my coding partner, Will, and I did was sat down and talked about our ideas for what we thought would be an interesting project, what requirements it needed to meet the brief, and some additional features that meant it could be useful.

Once we started to write out what it needed to do by way of user stories (written in “As a [type of user], I want to [do something] because it [helps me meet a goal for using this product]” format), we could start to see what information we’d need to take in, and how, in order to achieve these goals. We had our models and could connect them.

What we decided to make was a simple wish-list app that would meet the objectives of all basic CRUD actions. Create a wish, see their wishes, update a wish, or delete it. We decided that our domains would be a User class, a Product class, and a Wish class that joins the two together.

Still before we even started coding, we mapped out how the user would interact with the application, including when and how they would see the functionality required by their user stories. Will found a great resource, FlowMapp, that allowed us to do this quickly without needing to faff around too much with actually drawing the diagrams, as connectors are just pulled out of each flowchart symbols and snapped onto the next step.

Where do we go from here? Now we know.

Finally, we put together a schedule. This was a rundown of everything we needed to build each day so it was ready to ship by project showcase day, when students from other modules could swing by our desks and test it out.

Why did we spend so long on the planning phase? Sitting down to think about what we actually needed to do helped next steps start to fall into place. We knew precisely what needed to be done, and by when.

And now, we code

This isn’t to say that sitting down to a blank screen isn’t still slightly daunting, or that you will avoid any and all problems that may arise. But, spending the time to do this will help you considerably as you go forward with the project.

We began to get our folders set up, including the run.rb file, config/environment.rb file and import our gems, database set up and classes declared. We wrote our seed data and imported it.

We had put into our schedule that we wanted to refactor the code that we wrote. No one will ever get it completely right the first time, so this allowed us to look at each piece of the user journey, step by step, and just get it to a state where it was working. After a few simple methods, we began to test. And test again.

If it worked, this didn’t mean it was the best way to write it. Thinking back on the project, we spent at least one full day reading and re-reading what we had written to see what could have been abstracted into helper methods rather than a single massive method, and made sure our methods interacting with the database rather than the user weren’t all sitting on our CommandLineInterface class.

Testing it helped to realise that we had missed some steps in our user flow, and we could try to search for other features that would help improve user experience. Screen too messy from all the stacked commands? Enter system ‘clear’. Should they have to type in the name of the command or can we create a clickable list of all their options? Enter the gem tty-prompt. And for a bit of retro-fun, titles with tty-font.

Each iteration made the app more streamlined, more user-friendly. After the basics were covered, we could start to look at our stretch goals for enhanced filter options.

What I learned — ActiveRecord Validations

Testing the project ourselves and giving to other people on our cohort made us realise we hadn’t fully considered all the edge cases of how users might enter data, so we looked towards improving both the instructions given to users (so they were less likely to make a mistake) and introduced some validation features where feasible.

I was most proud of the validation to ensure that Product instances were entered correctly before recording them to the database. After a bit of searching and reading the guide on ActiveRecord validations, below is a walk-through of what I did.

See the GIST of the code snippet

Lines 1–3 show the set-up of the class. As we used ActiveRecord, it inherits from ActiveRecord::Base and maps to the other appropriate tables in our database.

Set up our Product class and inherit ActiveRecord, then map tables

Then in lines 5–9, each of these fields has a validation applied. We didn’t need to validate very much, only that users had entered something (and not skipped through), and that the fields we would later use in our filtering functionality were entered as numbers.

Validating the presence of fields, plus ‘numericality of’ the fields where we required numbers

Lines 16–22 shows the beginning of our validation in use. If we called our Class method make_new_product from the CLI class, it would attempt to save these fields to the database via the self.create method (passing in what is required to be mapped to their correct database fields).

Try to make a new Product and save it to the database, with create

I had some fun deep-diving into the return values with pry, which is where we get this:

if new_product.errors.messages.keys.nil? == false

ActiveRecord stores errors and the return messages in a hash, so by looking for an absence of keys, we can run the method (still to come!) show_error_method on this instance of Product (what is now our ‘self’, passing in the variable local to our method, new_product — declared when we needed to store the details that would go into the database. Here’s the full line:

self.show_error_message(new_product)

As the helper method show_error_message is used in this method, I wrote this above the method to put it into the database, on lines 11–14.

Show the ActiveRecord error message to the User

Calling the ruby enumerable .map on the messages hash returns the key, field interpolating alongside it’s value — an array I’ve just called error (and since we only needed the text inside the array, I used [0] to puts this to the console).

new_product.errors.messages.map { |field, error| puts “#{field.upcase}: #{error[0]}.” }

At the end, I felt proud that I learned quite a bit about what to look for when creating a new project and how to structure the time required to put it together. Although it’s still far from perfect (the above could certainly be refactored again with more time, and I would have liked to enter custom error messages), it was a great project to get the practice needed for creating software. If you have constructive criticism, tips or suggestions, I’d love for you to leave a comment below!

Check out the GitHub repository of the final project.

--

--