goals {
div(style: 'margin: 0 auto; width: 250px; height: 120px;') do
model_diagram header: 'Topics', fields: %w(id title description), style: "float: left;"
div(style: 'float: left; position: relative; width: 60px; height: 100px;') do
div(class: 'arrow-left', style: 'left: 0; top: 30px;')
div(class: 'horiz-line', style: 'left: 5px; top: 37px; width: 25px;')
div(class: 'vert-line', style: 'left: 30px; top: 38px; height: 25px;')
div(class: 'horiz-line', style: 'right: 0; top: 62px; width: 30px;')
end
model_diagram header: 'Votes', fields: %w(id topic_id), style: "float: left;"
end
message "Because there is an explicit relationship between a topic and its
votes, we need to specify that. In this step, we'll explicitly
declare the relationship between votes and topics."
}
steps {
step "Teach the Topic model about Votes" do
message "Edit `app/models/topic.rb` so that it looks like this:"
source_code :ruby, <<-RUBY
class Topic < ApplicationRecord
has_many :votes, dependent: :destroy
end
RUBY
end
step "Teach the Vote model about Topics" do
message "Look into `app/models/vote.rb` and confirm that it looks like this:"
source_code :ruby, <<-RUBY
class Vote < ApplicationRecord
belongs_to :topic
end
RUBY
end
step "Play around with Topics and Votes in the Rails console" do
message "First, make sure you've made at least one topic on the site."
console_with_message "Next, open a Rails console in the shell window:", "rails console"
fuzzy_result <<-CONSOLE
{FUZZY}~/RandomWittyName{/FUZZY}$ rails console
Loading development environment (Rails 7{FUZZY}.2.1.1{/FUZZY})
suggestotron(dev)>
CONSOLE
tip "Are you typing into the shell or are you typing into the rails console? This makes a big difference. You can always tell the two apart by the prompt"
message "At the console, try the following things"
rails_console_with_message "See how many topics exist:", "Topic.count"
fuzzy_result <<-CONSOLE
suggestotron(dev)> Topic.count
Topic Count (0.2ms) SELECT COUNT(*) FROM "topics"
=> {FUZZY}2{/FUZZY}
CONSOLE
message <<-MARKDOWN
* On the first line you see
* the rails console prompt ending in with a greater-than sign, and after that
* the code you typed in
* on the second line, you can see (from right to left)
* the SQL Query that was sent to the database **SELECT COUNT(*) FROM "topics"**
* that it took 0.2ms to run that Query
* on the third line you see
* a "fat arrow" =>
* and after that the result: there is are two topics in this database
Most of the time you are only intrested in the last line with the result.
MARKDOWN
rails_console_with_message "Save the first topic into a variable:", "my_topic = Topic.first"
tip "`my_topic` here could have been any variable name, but we'll stick with `my_topic` for consistency."
rails_console_with_message "Change the title of that topic to something else:", "my_topic.update(title: 'Edited in the console')"
message <<-MARKDOWN
Reload the page in the browser and you will see the change:

MARKDOWN
rails_console_with_message "Add a vote to that topic:", "my_topic.votes.create"
rails_console_with_message "See how many votes that topic has:", "my_topic.votes.count"
rails_console_with_message "Remove a vote from that topic:", "my_topic.votes.first.destroy"
message "Note that the things you can do to **Model classes** (like **Topic** and **Vote**), differ from the things you can do to **Model instances** (like my\\_topic, here). **my\\_topic.votes** is an **association**, and here behaves mostly like a model class."
div do
half_width "Model class / association methods" do
ul class: 'no-style-type' do
li "Topic.first"
li "Topic.last"
li "Topic.all"
li "Topic.count"
li "Topic.find_by_id(5)"
li "Topic.destroy_all"
li "my_topic.votes.count"
li "my_topic.votes.create"
li "my_topic.votes.destroy_all"
end
end
half_width "Model instance methods" do
ul class: 'no-style-type' do
li "my_topic.title"
li "my_topic.title = 'New title'"
li "my_topic.update_attributes(title: 'New title')"
li "my_topic.save"
li "my_topic.save!"
li "my_topic.destroy"
li "my_topic.votes.first.destroy"
end
end
end
message <<-TEXT
An exhaustive list of things you can do to models and associations can be found in the <a href="http://guides.rubyonrails.org/active_record_querying.html">Active Record Query Interface RailsGuide</a>.
TEXT
end
}
explanation {
message <<-MARKDOWN
`has_many` and `belongs_to`:
* In Rails, relationships between models are called associations.
* Associations (usually) come in pairs.
* A topic will have many votes so we put `has_many :votes` in the
topic model.
* When you ask a topic for its votes, you get an array of votes
for that topic.
* A vote is for a particular topic, so we put `belongs_to :topic`
in the vote model.
* When you ask a vote for its topic, you get the topic for that
vote.
It can still be important to clean up after yourself! `dependent: :destroy`
on `has_many :votes` means when a **Topic** gets destroyed, all
the **votes** that correspond to it will be destroyed, too. Without
`dependent :destroy`, those votes would live on the database forever.
MARKDOWN
}
commitnow do
message "find a good commit message and commit your changes"
end
next_step "allow_people_to_vote"