Icy Truck App

Murat Ogulcan Sahin
5 min readApr 5, 2021

Icy Truck is a web application that is built with Ruby on Rails. It allows user to sign up either as an ice cream truck owner or as a customer. Trucks can create their ice creams, even their unique flavors and customers can place ice cream orders. Nested resources, nested forms, omniauth-facebook, custom validations are used. However, this post will be about callbacks, helper methods and custom Active Record queries which are also used in Icy Truck.

Custom Active Record Queries

Model objects that inherit from ActiveRecord::Base and contain Active Record Associations macros have methods that perform database queries. This makes object oriented programming so much easier and quicker. However these methods trigger multiple queries in order to retrieve the data we are trying to access. For example:

IcyTruck database diagram

Based on this database tables diagram, we can run: Truck.find_by(id:1).icecreams code to access the menu of the ice cream truck that first customer ordered from. This will perform:

SELECT "trucks".* FROM "trucks" WHERE "trucks"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]SELECT "icecreams".* FROM "icecreams" WHERE "icecreams"."truck_id" = ? /* loading for inspect */ LIMIT ?  [["truck_id", 1], ["LIMIT", 11]]

AR queries. However if we want our code to execute more efficiently we can create a custom query that can retrieve same data with performing only one query. In this example above our custom query that gives same result would be Icecream.where("truck_id = ?", 1) and this query would perform:

SELECT "icecreams".* FROM "icecreams" WHERE (truck_id = 1) /* loading for inspect */ LIMIT ?  [["LIMIT", 11]]

As you can see, our custom query performs only one query. The time difference between first and second query methods depends on how many ice creams and trucks our database has. The more data our code looks into, the more time it takes.

What if we want to use a custom query in multiple places throughout our code ? We don’t want to be repetitive. In order to do that, we declare a class method in our model that we want to perform our query on.

For example, in Icy Truck, the trucks in the same zip code area show up for truck owners and customers. We need a custom query method so we can use every time our server gets a request for truck index page. Our custom query method would look like :

Truck model custom query method.

The argument that is passed will be the zip code we want filter our trucks through. This method returns an array of truck objects that we display in index view page.

TrucksController index action method

To make our custom query method neat and easy to spot, we can use #scope method. Scope method is preferred rather than class method because makes our code cleaner, easier to read so we can spot them easily. For the example above, our scope method would be like:

scoping custom query method

:in_the_area_of is our class method name, ->(zipcode) is our argument and { where("zipcode = ?, zipcode) } would be our method block.

Helper Methods

There are different types of helper methods. They can be used for model objects, views and controllers to clean up our code. Sometimes a helper method can be called in controllers and views too. For the sake of keeping our code eloquent and clean, we should keep helper methods in the right places.

Controller Helper Methods

These methods live in our controllers, including ApplicationController. We should keep them in the controller that we use them. Strong params methods are good example of a controller helper method.

IcecreamsController strong params method that permits mass assignment to save the icecream object.

However, sometimes some helper methods are needed in controller and views. Therefore, we place them in ApplicationController and in order to use them in our views as well, they should be passed as an argument of #helper_method macro method.

current_truck method is called in other controllers and views in IcyTruck app

View Helper Methods

View helper methods are the ones we use in only our erb files. They are placed in module ApplicationHelperin app/helpers/application_helper.rb file. They help us to carry our application’s logic apart from view files which makes our code easy for debugging, reading, overriding.

icecream_url helper is used to make right post request for form_for

Model Helper Methods

Model helper methods can be used for building our application logic, custom validations, lifecycle callbacks etc. They are related to the model only.

charge_customer is helper method for our callback, order_valid? and order_total_valid? are used for custom validation

Active Record Callbacks

While the user interacts with our app, application’s objects can be manipulated such as created, deleted, updated. Active Record Callbacks are macro methods that allows us carry our application logic during objects lifecycle.

#cancel_orders_upon_delete method deletes orders of an ice cream after ice cream is deleted

In the photo above, we use after_destroy macro that is executed right after an ice cream is deleted from database and triggers #cancel_orders_upon_delete which deletes all orders that include that ice cream.

--

--