Janice Darikho


Indonesian UX designer and developer, Singapore-based

hario: Fitness Resource Website (React + Rails API)

Last portfolio project. It’s been a long, fruitful ride. In this post, I will outline the steps needed to design, build, and deploy hario. This project feels like the epitome of “if you want to learn how to build it, you have to build it”. I bumped into more obstacles than ever, but ultimately got through them.

If you have more suggestions for fitness tools, comment below and I’ll build them!

1. Discovery

This time, I decided to do something different — rather than building a typical web-app, I made a sort of hybrid. Some parts of it seems to be static (e.g. home and about page), but some parts of it has the functionality of a web-app (e.g. fitness tool forms).

This project will be a basic one, built with the following structures:

  • Stateless components: Header, Home, Tools, Services, About
  • Container components: ToolContainer, ToolsContainer
  • Routes: Home, About, Tools, Services, Results, Community

To make it more interesting, I will make it such that the data are served by the Rails API back-end. That means that in the React JSX code, there are no manually typed data — instead of typing Harrison Oldridge into the render method, I used {this.props.trainer.name} instead (this.props instead of this.state because I used redux and mapStateToProps). The trainer info would have been seeded onto the Rails database, and is accessible via the API GET request. The same applies for services, logs, and tools — with the exception of logs not having any seed data. logs is for the user’s input.

This means that there will be quite a few API calls. However, the maximum calls that exists on a page is 2, which is comparatively lesser compared to some websites out there.

2. Strategy

I have never built any React projects on Rails before. Fortunately, there are several comprehensive resources for React front-end + Rails API apps that I could use as guides. Some of them even guide you through setting up ActiveAdmin for user authentication — but I will keep it simple and not implement this feature for now.

A strategy that I used was to implement the form logic (e.g. macronutrient calculation) in the Rails model rather than to implement it in the reducer. Rails will be calculating the form results, and the reducer will just be in charge of fetching data from Rails.

3. Design

I worked out a basic mockup for the website then improvised as I go along. While the live version looks different than the mockup, I think the live version looks simpler and more minimalistic. I used Sketch to design the mockup, which made the whole process really efficient and quick.

4. Architecture

After setting up the Rails API backbone using rails new hario --api --database=postgresql, we are now free to set up our ActiveRecord relationships and MVC structures. Setting it up with PostgresQL will allow us to easily deploy our app to Heroku later on. So far, everything was pretty straightforward. The most fun I’ve had was rendering each object as JSON and making it available for later API requests.

Now that the back-end is ready, we’ll now start building our React front-end. This is achievable by running cd app && create-react-app client, which creates a client folder in hario/app. Following this, we can see our front-end in the browser by running cd client && npm start.

Since we will be running our front-end on localhost:3000 and our back-end on localhost:3001, we foresee our React app attempting to load resources from the back-end. This would be performing Cross-Origin Resource Sharing (CORS), and by default, browsers prevents these types of requests from scripts for security reasons. There are several workarounds for this — the one that I chose was probably the simplest.

For development purposes, I simply installed and enabled a Chrome Extension that allows us to request from other sources using AJAX. During production, we will use Netlify for the front-end and Heroku for the back-end, and they seem to have taken care of CORS without the need for the browser extension. I will need to look up on this — any insights are welcome.

For the first iteration of the project, I decided not to use Redux first. I decided to use no middleware to get everything working first — then refactor, or convert, using Redux.

5. Development

I thought I would have to watch countless Learn videos to get everything working, as I found the videos really helpful during the Rails+JS project section.

There were so many learning points during development, some of them being:

  • how to send GET request using fetch()
  • how to send POST request using fetch()
  • how to convert React methods to using Redux middleware and make stateful components stateless
  • how to use mapStateToProps
  • how to use rootReducer
  • how to fix JS bugs using debugger and console.log()
  • how to dynamically and conditionally render components

6. Deployment

Deployment was quite painful. My attempts at deployment generally are. There were two parts to deployment: front-end and back-end. There are different ways to deploy a React + Rails API app, and this will be only one of them.

First, I deployed the Rails back-end to Heroku. Prior to deploying, I created a Procfile, that specifies the commands that are executed by the app on startup. Mine looks like this:

web: bundle exec rails s
release: bin/rake db:migrate

I’ve installed the Heroku CLI and completed the login step previously, so this step should be as easy as:

heroku create
git push -u origin master
git push heroku master
heroku run rake db:seed

I needed the db:seed command as I need the seeded data to be served to the front-end.

One persistent challenge that I faced was the You must use Bundler 2 or greater with this lockfile error message from Heroku during the git push heroku master step. After reading through this thread, the solution that worked for me was to update my Ruby version to 2.6.1, and to install a specific bundle version (1.17.3) to be used in bundle. This somehow solved the error message, and the deployment was finally successful. I also made sure that the /api/trainers works.

After the back-end is deployed, we can now focus on deploying the front-end. For this project, I’ve chosen to use Netlify as it has been something I’ve wanted to learn. It was surprisingly simple! I setup my public/_redirects app based on this instruction and also this post, and both of them were very helpful. After the final deployment to Netlify, I checked the link using my mobile device and discovered that I was only getting a white screen, whereas on Chrome it was showing the app perfectly, with the API information shown correctly. I then used my desktop and checked it again using Opera, and discovered that it wasn’t working, either. Looking at the console, I saw that it was throwing this error and after looking through some solutions, I decided to delete the Redux DevTools code in index.js, which got everything working.

A note is to remember running npm run build before trying to deploy to Netlify, and to remember pushing to the git repository before deploying to Heroku, which I think is good practice.


RebelBase: Adding JS Functionality to a Rails Application

Learning JavaScript after spending so much time on Ruby is such a ride! I love spending time solving JS challenges on Codewars, but mini logic challenges are somehow different from implementing JS on a web app. This is my first time working with both Ruby and JS on a project, so I learned a lot!

1. Discovery

Initially, after reading the portfolio assessment requirements, I decided that I’m going to make these changes to my Rails app:

  • Render user’s clubs on Memberships index using JS
  • Allow user to click through the upcoming books list and read books list by clicking on ‘Next’ on the Book show page with AJAX
  • Render Discussion’s list of Comments with AJAX
  • Submit a Comment to a Discussion with AJAX
  • Construct Discussion as JS Model Object
  • Render the username of the user who posted the Comment with AJAX
  • Render Comment creation time with AJAX

As you can tell, a lot of AJAX is going to happen! Essentially, AJAX is a technique used to create asynchronous web-apps. Asynchronous means that while we send a method call to the server, our web page is not blocked / frozen while loading the results. We can even interact with other elements on the page while waiting for the call to finish retrieving our results! But for our small virtual book club app, the time needed to retrieve the results is very short, so we won’t need to do that.

2. Strategy

A strategy that I employed here is to firstly serialize all my models using Active Model Serializer. AMS is a gem that easily lets me convert my data into JSON. I didn’t need to serialize all the models, as I was planning to implement AJAX for only the following models: Memberships, Books, Discussions, and Comments; but I did it anyways, just in case. After serializing, I edited my controllers to allow me to access the views as .json, in addition to .html. This was fun. I use the JSONView Chrome Extension to view my JSON data in a neat format, and to expand or collapse any data I need). AMS also allows me to specify any associations between my models.

I used the same associations as the ones I used for my schema. I realised that later on, I needed to modify this slightly — more on that later! After confirming that the data and associations were rendering perfectly, I went on to coding the JS. This is where the hard part begins.

3. Design

I didn’t focus much on visual design here, as this project mainly serves as a coding challenge for me. I did fix some CSS weirdness, but that was it.

4. Architecture

Rails’ architecture allowed me to separate my JS file according to the views and models that I’m focusing on. To implement AJAX on the discussion#index page, I only need to code my JS in discussion.js (which I changed from discussion.coffee— CoffeeScript is something that I’ll learn in the future, along with many other things).

In terms of the views structure, I separated Discussions views from those of Book‘s. What this means is that previously, you can look at a book’s discussions from the individual book’s book#show page, but I realised that this also means that the AJAX calls for discussion#index will need to be placed in books.js (as the Discussions are rendered via the Books controller). I then decided to move the Discussions views to its own rightful folder, which feels cleaner and more easily manageable.

5. Development

Longest part of the process. I had to watch many Learn videos to do this. I signed up for Rails+JS study groups, but they were conducted during my work hours and unfortunately I couldn’t attend them, so I resorted to the recordings instead. I find Brad’s study session recording really helpful! He even posted the code in his GitHub repository.

One of the bugs that I realised early on was that my AJAX calls were firing multiple times. I found out that this was because I have specified the following in my application.js:

//= require jquery
//= require rails-ujs
//= require jquery-ujs
//= require_tree .
//= require popper
//= require bootstrap
//= require typed

Do you see it? I had called on multiple jQuery files, which resulted in each AJAX request being fired twice. I deleted the jquery-ujs line, and everything was restored beautifully (no more double vision!).

Another bug happened when I had to click on a link twice before I see the results being appended onto my view. This was because I had accidentally nested a click function within another click function, which was a mess. This has been corrected.

One of my biggest learning points for this project was in learning to use $.ajax() and $.getJSON(). I used both for different parts of the project, just to practice more with both styles. Personally, I prefer the latter, as it is a shorthand for $.ajax() — it has already specified the parameter for the output, which is in JSON, and it automatically transforms my JSON output into an Object. Yum. I find $.getJSON() to be more configurable as well, as I can easily specify, configure, and see which URL I’m retrieving my request from.

I also learned to manipulate the serialised JSON data into giving me specific key-value pairs. In the discussion_serializer.rb file, for example, I added a custom method:

class DiscussionSerializer < ActiveModel::Serializer
  attributes :id, :title, :body, :book_quote, :comments_with_user

  # include created_at and updated_at

  belongs_to :book
  belongs_to :user

  # has_many :comments

  def comments_with_user
    array = []
    object.comments.map do |comment|
      hash = {}
      hash['id'] = comment.id
      hash['body'] = comment.body
      hash['created_at'] = comment.created_at
      hash['updated_at'] = comment.updated_at
      hash['discussion_id'] = comment.discussion.id
      hash['username'] = comment.user.username
      array << hash
    end
    array
  end

end

To render each comment’s username, I had to be able to access the comment’s user_id from my Discussion JS Object. What I realised was that my Discussion JS Object didn’t have access to the nested comment’s user_id, discussion_id, as AMS only went one-level nest-deep.

However, I was able to see the user_id when I navigate straight to the Comments.

So I needed to make my discussion_serializer.rb serve me my JSON as I like it, which was what the custom method comments_with_user is doing. I was able to construct a new key-value pair for my Discussions Object, retrieve the information I needed, and access it via discussions.json.

After I have my comments_with_user data, I didn’t need AMS to give me my original comments (without user) list, and so I commented the line has_many :comments (line 9) from discussion_serializer.rb.

6. Deployment

I checked out a new git branch to develop the JS features, and submitted my own pull and merge requests. For the live deployment, I will continue to use Heroku for the time being, but I look forward to learning more about server and hosting, especially with AWS EC2, which I’ve been hearing a lot about!

Thank you for reading!


RebelBase: Virtual Book Club Rails Application

It’s portfolio project time! Whew, this one took quite awhile to build. As usual, I’ll share my approach to building rebelbase from scratch — from designing the user flow up to deployment. I enjoyed every moment of the process–especially during the final refactoring steps–and I learned so much more from building this project.

Go see it, play with it, and send a suggestion my way!

1. Discovery

I love books. Maybe a little too much. During the ideation phase, I was coincidentally looking for an online book club with a few requirements:

  • It allows members to suggest the next reads easily
  • It allows members to browse all existing clubs
  • It allows members to follow / unfollow any club
  • Its UI/UX is intuitive and easy on the eyes

I had not seen any strong contender to date, which is the main reason why I decided to build rebelbase, but if you know any, do let me know! I’d love to check it out.

2. Strategy

Rails is amazing. To build a Rails app from scratch, all it literally takes is rails new rebelbase, and this single-line command creates the entire Rails directory structure required for our project. Coupled with the rails generate resource command, this sets up our models, controllers, migrations, and adds the resources :model to our config/routes.rb file.

An important strategy I’d like to keep in mind for future Rails project is to gather all the potential gems that would be used in the course of the project. This would give me a clearer foresight, and would allow me to configure the gems with my project early in the development stage, rather than after having written lengthy codes, which might increase the likelihood of errors.

3. Design

After my previous project (Flatiron Hall), I learned the value in focusing on the user flow diagram when designing. This is essential as this project is larger in scope than Flatiron Hall, and I did not want to waste time in determining where the user is at any point in time, and where to redirect the user to after a method is run.

4. Architecture

Defining the relationships between model is no easy task, either. But after this project, I have a better idea about has_many :through, as well as about setting up references in the database.

One thing I wish I had done is to set up my Rails app with a PostgreSQL database from the start, rather than after development and before deployment to Heroku. I’d also check my Ruby version and make sure that it fulfils Heroku’s minimum acceptable version.

5. Development

The longest stage. But also the most enjoyable. I learned to use many gems, such as:

6. Deployment (or Publishing)

Deployment to Heroku was relatively smooth-sailing, but I couldn’t have done it without Stack Overflow. Some errors I faced include:

  • Incompatible Ruby version
  • Missing gem that was listed as a dependency by other gems led to LoadError
  • Forgetting to run heroku run rake db:drop db:create db:migrate after pushing to GitHub master
  • Incorrect migration sequence that had a table column referencing other table, while this ‘other table’ was still not created.
  • Forgetting to update heroku config used in Facebook OAuth led to parameter_id error.
  • Other than that, it was a successful learning project!

Thank you for reading!


Flatiron Hall: Sinatra Application for Web Development Inspiration

On this second portfolio project post, I’ll share my process-based approach on writing Flatiron Hall from scratch, putting the CRUD functions together, designing and adding user-friendly features, and finally, deployment to Heroku. Needless to say — I faced many roadblocks during the development phase, but nothing insurmountable, and I will share them later on. Most importantly, first things first: Go visit my baby here! It needs a lot of love.

Project time is always going to be my favourite part of being in Flatiron. It allows me to be creative, expressive, and to be scrappy—don’t know how to do it? Read the docs. Read other people’s answers to the same problem. Revisit the code, talk to it (yes), and iterate. I learned so much more from building a project than from passing lab specs, and I’m thankful for that.

1. Discovery

Flatiron Hall was originally Dakota’s idea. During one of my 1:1 sessions with Dakota, we were discussing about potential ideas, and he mentioned that he’s always wanted to do a web-app that puts everyone’s projects in one place. I then enthusiastically volunteered to build it, as I didn’t have any strong pull towards any particular idea at that moment. (I had wanted to make something similar to 80,000 Hours’ Problem Profiles, but I decided that the idea hasn’t had much concreteness yet, so I’m letting it incubate for a while more—I may revisit it for my upcoming Rails project.) Even so, I knew that I wanted to create something that other people would use, and would enjoy using, and Flatiron Hall’s idea seems extremely exciting!

2. Strategy

To build a Sinatra app from scratch, the Corneal app generator is available on the open-source. This gem will take care of the skeletal MVC structures (with all the scaffold directory), and everything you need to load your basic development environment, including shotgun. This gem was made by a fellow Flatiron Student!

Using the knowledge I learned from Learn.co’s previous labs (especially Fwitter), programming Flatiron Hall and making it fully functional isn’t at all the challenging part. The challenge would be the designing of the front-end, streamlining the user on-boarding and flow throughout the app, as well as ensuring that all user interactions with the app is meaningful and purposeful—we want to minimise user frustration as much as possible.

3. Design

Naturally, along that line of thought, I chose to first engage in the design aspect of the project. I didn’t focus on making any prototype—rather, I first built a design system that will guide my overall thinking process.

For this project, the design system only serves as a visualisation guide, as I only got to the wire-framing stage and didn’t proceed with the prototyping. After considering the time/effort/efficiency of prototyping vs. developing, I jumped straight into the front-end development right away, as I already had mental pictures of how the frames will interact.

In hindsight, this made the creation process quite efficient, especially because this was such a simple project with only a few views, and it was easy to visualise the user flow without having a prototype to refer to. It saved me some time that would’ve been spent prototyping. Nonetheless, I could’ve spent time building out the user flow diagram, just so that it’d make life easier when determining which path to redirect the user, or considering which flash message to show, and at which point of the user journey. I previously used Draw.io to map out the user journey, however I feel more affinity towards Milanote due to it being lightweight and minimalistic, and I believe that it can also be suitable for the task.

I also noticed that I made a few changes in design decisions along the development, which gave me an inkling of how developers can provide their input and perspective for designers, and vice versa, to improve the overall UX of the product. This developer-designer feedback loop is an aspect in which I will try to deepen my understanding, and this project has been a great stepping stone for me. I’m looking forward to exercising more code-switching between the two disciplines with future projects.

4. Architecture

Back to code. After installing Corneal with gem install corneal, you can cd to the appropriate directory, create your app using corneal new <app-name>, and watch the magic happens! I discovered this gem a little too late, so I created the scaffold and configured everything manually—however, due to VSCode’s capabilities and its integrated terminal, setting up wasn’t a very time-consuming process.

Afterwards, make sure to redefine the scaffold structure according to your needs. I ended up with the following:


├── config.ru
├── Gemfile
├── Gemfile.lock
├── Rakefile
├── README.md
├── app
│   ├── controllers 
│   │   ├── application_controller.rb
│   │   ├── projects_controller.rb
│   │   └── users_controller.rb
│   ├── models
│   │   ├── project.rb
│   │   └── user.rb
│   └── views
│       ├── projects
│       │   ├── create.erb
│       │   ├── edit.erb
│       │   ├── projects.erb
│       │   └── show.erb
│       ├── users
│       │   ├── create.erb
│       │   ├── login.erb
│       │   └── show.erb
│       ├── about.erb
│       ├── error.erb
│       ├── index.erb
│       └── layout.erb
├── config
│   └── environment.rb
├── db
│   └── migrate
├── lib
│   └── .gitkeep
└── public
    ├── css
    │   └── style.css
    ├── fonts
    ├── icons
    └── js
        ├── jquery.js
        └── main.js

I also defined the relationship between the User and Project model. In this case, it’s straightforward enough without the has_many :through association.

Now, our environment is all set up and it’s ready for some action!

5. Development

Again, this stage took the longest—no surprises there. But again, this has proved to be a successful project, and I learned a few more things:

Back-end:

  • Do not use type as an ActiveRecord column names, as type is one of the reserved keywords for AR. I used type originally to indicate which type of project the user has submitted — and ran into errors. I have changed the column name to category instead. Dakota warned me of this earlier—but I forgot about it, and had to learn it the hard way.
  • Try to use params[:slug] as early as possible. I originally used params[:id] to set up relations between the project views and controllers, and changed nearer to the completion of the app. Needless to say, at this point, there are already a few interactions set up based on params[:id] and I missed making a few changes, which broke the edit and delete function of the application. Alternatively, I could have done a mass-replacement of the.id as well, but I believe it’s better to just use .slug earlier.
  • Do not use two similar paths in the controller, i.e. get '/projects/:id' and get '/projects/:slug'. Only one of them will work, and I believe that depends on which comes first in the controller. So if there is a request to http://localhost:9393/projects/<project-name>, it will give you the Sinatra “don’t know this ditty” page, as there are no projects with params[:id] = <project-name>.
  • Used and formatted created_at for viewing date of submission of projects, as well as added a few more columns along the way. It’s probably not realistic to have migrated all the columns I wanted right at the start of the project—I realised there were other columns which I wanted to add, such as the blog_url and timestamps.

Front-end:

  • Using if-else in the .erb files to display certain message, depending on conditions (e.g. logged_in?, user == current_user).
  • Implementing sinatra-flash messages to interact with users. Did the user successfully edit their project? Did they successfully log out? The gem also allows full reign over the styling of the alerts, which I find really neat—different colours can be used to indicate the nature of different alerts.
  • Better understanding of yield. I previously only vaguely understand this concept—thinking that yield is meant to be written in the different views, when it is only meant for the layout view.
  • HTML class names are case-sensitive. I had problems with the filter feature due to this. The way the filter works is by attaching a value to each filter link (say, I click CLI, then the value attached to that link is cli, and so on). Then a JS script tells it to hide all projects, and only show all the project that has the HTML class cli. Pretty simple in concept (learned about this in SuperHi). I dynamically implemented the HTML class names to every project just by adding <%= project.category %> to the project div— such that it becomes <div class=”project <%= project.category.downcase %>”>. The .downcase was to ensure that the HTML class name is now cli instead of CLI. I had that inkling the first time around and everything works perfectly. But during deployment, I deleted the .downcase and everything fell apart. Please don’t ask why I deleted it.
  • Customise radio button look. I had the help of Sam Keddy’s public Codepen creation to achieve this. It’s in the create and edit project view of the application.
  • Keeping aspect ratio of iframe contents using CSS padding. I still don’t quite remember which percentage means which aspect ratio—but we have the Internet for that.

6. Deployment (or Publishing)

Deployment was made incredibly painless due to the help of Heroku doing all the heavy-lifting and Dakota’s step-wise walkthrough. Postgres configuration was surprisingly easy. An issue I faced, however, was the Gemfile specification for the pg gem—the version has to be specified, such that it looks like so: gem 'pg', ‘~> 0.20’. Simply adding gem 'pg' will not work, and the release will break. Otherwise, all is good.

Once again, check out the project here: https://flatiron-hall.herokuapp.com/.


Libri: CLI Scraper RubyGem for Bibliophiles

I’ll share my process-based approach on how I created Libri and published it on RubyGems.org, alongside some technical roadblocks that I faced during the development phase. This project specifically focuses on scraping, which is a term used to describe the act of retrieving HTML-and-CSS-based data from a website page. Here is a walkthrough video of how Libri works:

1. Discovery

After sifting through several scraping ideas—including scraping Noti.st, or 80,000 Hours’ Problem Profiles, or Adafruit’s Raspberry Pi projects—I settled on going back to a theme that can be simple, meaningful, and usable by many: Books. In searching for which website to scrape from, I had several options: the Man Booker website, Goodreads’ Awards section, as well as Penguin’s Award Winners list.

I chose Barnes & Noble’s awards webpage to scrape as it seems to be the most comprehensive and it’s also quite up-to-date.

2. Strategy

To build a gem using Bundler, I started by running bundle gem libri in the terminal at the Libri working directory. This will create file structures (called scaffold directory) for our gem, so we can start coding right away.

I made sure that my computer has also installed the following dependencies:

  • Rake, used to build a local copy of our gem, which we’ll use to push and publish to RubyGems.org
  • OpenURI, used to open a URL as if it is an HTML file
  • Nokogiri, used to parse HTML and XML values from a webpage
  • Pry, used as a local sandbox and a debugging tool
  • Colorize, used to style text in the terminal using different colours

3. Architecture

Now, for Libri, I wanted to make 3 things work on my terminal:

  • Display the various awards
  • Display the books belonging to a chosen award
  • Display the information of a chosen book

To do this, I structured my lib folder in this manner, separating the CLI, scraper, awards, books, and book classes.

Simplified directory structure for Libri

Each class is responsible for different parts of the gem:

  • The CLI class is responsible for the terminal interface that interacts with the user
  • The scraper class scrapes text-based contents off the webpage
  • The awards class creates new instances of Awards object from hash values returned by the Scraper.scrape_barnes_noble method
  • The books class creates new instances of Books object from hash values returned by the Scraper.scrape_award(award) method
  • The book class creates new instances of Books object from hash values returned by the Scraper.scrape_book(book) method

4. Development

This stage took the longest to complete, but all in all, it was a success, and I have several notes to make:

  • I learned to use a multi-line string via HEREDOC, which in itself has various methods to achieve the same thing (e.g. %{...}, %Q{...}, <<-EOS...EOS)
  • Initially, whenever an exit command was called, the Please try again. message would also be displayed. This was fixed by using a single-level if/else...end conditional rather than while input != 'exit'...end loop.
  • I knew that I wanted to access several levels of information, scraping from various URLs, and being able to pass in different URL based on the user’s input (e.g. if user inputs for the Pulitzer Awards, the Scraper.scrape_award() method must return information based on the Pulitzer Awards URL. If user inputs for Man Booker Prize, the expected return should be from the Man Booker URL). I knew then that I needed to pass in the URL as an argument for the Scraper.scrape_award() method. Knowing this, I included a :url key into the top-level awards hash, whose value will be passed in to Scraper.scrape_award(). Then, the second-level books hash can scrape from and access from the passed in URL—the same concept applies as we scrape from a third-level URL for individual book information. I wasn’t sure if this was workable, as previous labs I worked on hasn’t used a multi-level, real-time updated website, and therefore had no need for this flow. But it was! This was the best revelation I learned while building this project, knowing that versatility can be built into code.
  • I couldn’t access HTML values for attributes which are not href. The rating values on the B&N website was stored within the aria-label attribute, which does not return a value when I attempted to access it. I also couldn’t access the books listed under the Customers Who Bought This Item Also Bought section, which returns nothing as well. I’m still searching for answers.
  • Originally, upon scraping, I realised that I could access hash values and display them from the CLI class using Hash[:key], even without instantiating new objects and assigning them their arguments / attributes. This led to an oversight, where I published the working gem without practicing the Ruby object relationship methods, such as has-many. This was fixed by editing the awards, books, and book classes accordingly. Now we can access hash values, such as book.title and book.author using the attr_accessor.
  • At one point, as the terminal displayed a list of books, then went back to select another award, the list of books displayed was accumulated, resulting in 20–40–60… number of books. This was a disaster, and I had almost given up. However, it was soon realised that the bug was caused by CLI#make_book(award) method being called every time CLI#menu_award was called, and this adds a new array of books onto Books.all. #make_book(award) is needed to instantiate our Book object and to access various attributes of our Book, and we need that. To fix this, a method to clear the previous instantiated object was included before #make_book(award) is called, thus resetting the Books.all return value for each menu call.

All in all, I wouldn’t have been able to overcome these challenges without talking through my code line by line, component by component, flow by flow, as suggested by Dakota.

By talking out my thought process based on this rough flow:

  • What am I trying to do?
  • Is Ruby doing what I’m expecting her to do? (Yn)
  • If no, what’s happening instead, and why do we think it’s happening?
  • If it’s happening because of X—then, if we change Y, we expect Z to happen.
  • We test our hypothesis by changing Y, and we see if Z happens.
  • If Z happens, based on our understanding of X, we should know how to fix it and achieve what we were trying to do.
  • If Z doesn’t happen, don’t give up! Read up and look for help, and test different understandings to find the one that works with Ruby.
  • This is a simple project, however, with several different components interacting with one another, it was soon quite easy to lose track of one of them (e.g. how best to access and display every single piece of information, at which stage have objects been instantiated and at which stage they have not been, etc.), and when I lost that one, I soon lost focus on the big picture and I had to start all over again. So here’s to remember to keep practicing, and to practice it right!

5. Publishing

Lastly, to publish a gem for the first time, I followed these simple steps:

  1. Edit the Gemspec file and update the Summary as well as Description specification. Make sure that all todo on the file has been rewritten to prevent any potential errors when publishing. Next, comment out the entire code block that says Prevent pushing this gem to RubyGems.org, otherwise we won’t be able to push our gem.
  2. Change spec.bindir and spec.executables.
  3. Add dependencies via spec.add_development_dependency and spec.add_dependency.
  4. Update the version.rb file if necessary, following semantic versioning standards. There are many guides out there, including this and this.
  5. Update the README.md file as well. This is to help users have an overview of the gem, as well as how to install and run the gem.
  6. Make sure that your GitHub repo has all its files updated (latest commit and push).
  7. Make sure that rake is installed—so we can run rake build followed by rake release, which will push our latest gem version onto RubyGems.org for others to use! Alternatively, I also tried using gem build and gem push libri-0.x.x.gem to a similar effect. Another alternative is to install the gem-release gem, which provides several methods for helping with gem development that I will explore with further projects.

Hope you enjoyed this post, and I hope that makes sense to you! Drop in any suggestions for the gem and I’ll work on it. Happy coding! Originally published on Medium here.