Other Pages

hooking_up_votes_and_topics.step

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" =&gt;
        * 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:

      ![screenshot of the web app in the browser showing new title](img/edited-at-the-console.png)

    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"