Water of Life - A Scotch Whisky API
Tags: ruby, sql, programming
Published: 2022-02-03 15:12:47

Hello! I'm back with my first full-stack web application: "Water of Life," a Scotch whisky API using a React JS frontend and Object-Oriented Ruby, Active Record, and Sinatra on the backend.

For those unfamiliar with Scotch, it is essentially just whisky distilled and bottled in Scotland. Scotland itself is divided into regions, each region has many distilleries, and each distillery has many bottles. Sounds like a perfect dynamic to take advantage of Active Record macros!

The API is comprised of three tables: regions, distilleries, and bottles, each with a corresponding model that inherits from ActiveRecord::Base.

As for the associations between the models, a region instance has many distilleries, and a distillery instance has many bottles. The distillery model acts as the join class so that a region instance has many bottles through distilleries, and a bottle instance has one region through the distillery to which it belongs:
```ruby
class Region < ActiveRecord::Base
    has_many :distilleries
    has_many :bottles, through: :distilleries
end
```
```ruby
class Bottle < ActiveRecord::Base
    belongs_to :distillery
    has_one :region, through: :distillery
end
```
With these associations established, "Water of Life" harnesses the power of Active Record to provide useful, organized data the client requests through the routes established with Sinatra.

Yes, I did it Sinatra's way. 

Not "My Way."

But trust me, "The Best is Yet to Come..."

The first and most comprehensive endpoint in the application controller is the `get "/all"` route:
```ruby
get "/all" do
    regions = Region.all
    regions.to_json(include: { distilleries: { include: :bottles } })
end
```
Simply, this returns all of the API's data, hierarchically structured by region, distillery, and bottle.

More dynamic routes, like `get "/bottles/:id"`, take advantage of the params hash to locate and return the data for a specific bottle:
```ruby
get "/bottles/:id" do
    bottle = Bottle.find(params[:id])
    bottle.to_json(include: { distillery: { include: :region } })
end
```
Or even delete it from the database entirely:
```ruby
delete "/bottles/:id" do
    bottle = Bottle.find(params[:id])
    bottle.destroy
    bottle.to_json(include: { distillery: { include: :region } })
end
```
In both of these methods, the routes are not only returning information about the specific bottle instance but are "including" the data from its associated distillery and region as well:
```ruby
bottle.to_json(include: { distillery: { include: :region }
```
Such functionality is possible because of the associations established earlier with Active Record.

Finally, for one of the post routes on the API...

As a quasi well-adapted perfectionist, I want my API to be efficiently created and properly maintained, which includes new bottles being added to the database in the correct format.  Many scotches have a multiple-word name: "Lagavulin 16 Year Old," "macallan double cask 12 year old," etc. I prefer the former, not the latter. 

Of course, I could have imposed a regex validator on the frontend form, but users shouldn't inherit my problems (after all, I'm not their parent class!). There are Ruby gems that provide a method to "titleize" a string, but I wanted to write my own method to better understand the process involved:
```ruby
def titleize(string)
      title_cased = string.split.map do |word|
        letters = word.split("")
        letters[0] = letters[0].upcase
        letters.join
      end
      title_cased.join(" ")
end
```
And that, as they say, is that. My first API and full-stack web application. How far we've come...

Feeling thirsty? Check out "Water of Life" for details on your scotch of choice. And, if it's missing, please add it!