Quotes. Data. Models.

in order from most to least sexy.

Image for post
Image for post
^^This^^ or “An eye for eye makes the whole world blind.” You be the judge.

Let’s leave the modeling to the data.

The book that I will be using to source quotes also provides a category and sub-category (I’ll call them “topics”) for each quote.

For any one category, there are many topics. For any one topic, there are many quotes. For any one quote, there is only one topic and category.

In object relational terms, category:topics are one:many, topics:quotes are one:many. A quote will be able to tell its category based on its topic.

Image for post
Image for post

A title on a category will be something like “making dreams come true”, and some of its topics will be “instinct” and “luck”.

Image for post
Image for post
Category: Health. Topic: FAKE NEWS

Since there are two new resources, I generate two new models.

$ rails g model topic title:string category_id:integer
$ rails g model category title:string

Add null: false to the title attribute in both migration files.

Next, I add the topic_id attribute to the quotes table.

$ rails g migration AddTopicIdToQuotes topic_id:integer

Then!:

$ rake db:migrate

Next, I’ll add a mirror of the db restrictions to the two new models:

# topic.rb
validates_presence_of :title
# category.rb
validates_presence_of :title

Associations

# quote.rb
belongs_to :topic
# topic.rb
has_many :quotes
belongs_to :category
# category.rb
has_many :topics
has_many :quotes, through: :topics

Lifecycle

# quote.rb
after_save :ensure_topic
def ensure_topic
if !self.topic || !self.category
self.topic = Topic.uncategorizedTopic
end
end

# topic.rb
def self.uncategorizedTopic
topic = self.find_by(title: 'uncategorized')
topic ? topic : create_uncategorized_topic
end
def create_uncategorized_topic
category = Category.find_or_create_by(title: 'uncategorized')
Topic.create(title: 'uncategorized', category: category)
end

I’ll also add a before_save to both Topics and Categories that downcases their titles:

# topic.rb
before_save :downcase_title
# category.rb
before_save :downcase_title
# both individually
def downcase_title
self.title = self.title.downcase
end
Image for post
Image for post
“downcase”. When Ruby devs stopped giving a f*ck

I’ll hop into the rails console to make sure everything is working:

$ rails c
$ > quote = Quote.create(content: 'yolo', attribution: 'Alexa')
$ > quote.topic
$ => #<Topic id: 3, title: "uncategorized", ...
$ > quote.topic.category
$ => #<Category id: 4, title: "uncategorized">

Uncategorized defaults… ✔

$ category = Category.create(title: 'ALL CAPS')
$ => #<Category id: 5, title: "all caps">
$ topic = Topic.create(title: 'MiXeD cAsE', category: category)
$ => #<Topic id: 4, title: "mixed case", category_id: 5...

Lowercased titles… ✔

Great.

That’s the extent of the resources I plan to have in the quotes API phase one. As you’ll see, I’ll actually be exposing only one of the three resources. Check out the next article where I make a dead simple user interface for creating and querying for quotes.

Until then, check out the repository branch for this post. Oh…

Image for post
Image for post
One more thing…

Writing quote.topic.category every time I need to access a quote’s category is a bummer. Here’s a quick helper getter that solves that:

# quote.rb
def category
topic.category
end

Smell ya later!

Written by

Fullstack Developer and Code Yogi

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store