This article summarizes what I learned in Lesson 1 of the Launch School 5301 Rails Course. I used Rails 6 and Ruby 2.5.3.
Create an app called PostIt based on the entity relationship diagram.
Create routes for posts and categories. Prevent the delete route from being accessed.
Create controllers and views to view:
- all posts
- a specific post and its associated categories
- all categories
- a specific category and its associated posts
Change the association name between posts and user to posts and creator, so we have a better idea of the relationship of the association.
rails new postit
- Run
cd postit
- Run
rails server
- Navigate to
http://localhost:3000
in browser and verify that a welcome page is shown.
After creating each migration file and modifying its contents:
- Run
rails db:migrate
. - Run
cat db/schema.rb
to inspect the schema. We want to check if the migration had the intended effects.
rails g migration CreateUsers
def change
create_table :users do |t|
t.string :username
t.timestamps
end
rails g migration CreatePosts
def change
create_table :posts do |t|
t.string :title
t.string :url
t.text :description
t.belongs_to :user
t.timestamps
end
rails g migration CreateComments
def change
create_table :comments do |t|
t.text :body
t.belongs_to :user
t.belongs_to :post
t.timestamps
end
rails generate migration CreateCategories
def change
create_table :categories do |t|
t.string :name
t.timestamps
end
rails generate migration CreatePostCategories
def change
create_table :post_categories do |t|
t.belongs_to :post
t.belongs_to :category
t.timestamps
end
end
- Run
rails console
to open up the rails console. - After creating each model file, run
reload!
to reload the console. - Run
[ModelName].all
to verify that a SQL query is executed to select all rows from the appropriate table. We want to check that each model is hooked up with the appropriate table.
app/models/user.rb
class User < ApplicationRecord
has_many :posts, dependent: :destroy
has_many :comments, dependent: :destroy
end
app/models/post.rb
class Post < ApplicationRecord
belongs_to :user
has_many :comments, dependent: :destroy
has_many :categories, through: :post_categories, dependent: :destroy
end
app/models/comment.rb
class Comment < ApplicationRecord
belongs_to :user
belongs_to :post
end
app/models/category.rb
class Category < ApplicationRecord
has_many :posts , through: :post_categories, dependent: :destroy
end
app/models/post_category.rb
class PostCategory < ApplicationRecord
belongs_to :post
belongs_to :category
end
Run the following commands in the rails console and check that the output is as expected.
If you encounter errors, try restarting the rails console (not just running reload!
).
It is assumed that the commands are run sequentially from one section to the next (e.g., commands in "1:M association between User and Post" are run before "1:M association between User and Comment".
nancy = User.create(username: "Nancy")
victor = User.create(username: "Victor")
chili = Post.create(title: "How to make chili oil", url: "woksoflife.com", description: "great recipe on how to make chili oil", user: nancy)
compost = Post.create(title: "How to make compost", url: "urbangardening.com", description: "compost recipe using coffee grounds", user: nancy)
bok_choy = Post.create(title: "Why bok choy is great for cats", url: "loveyourcats.com", description: "argument for more greens in your cat's diet", user: victor)
chili.user.username # "Nancy"
bok_choy.find(3).user.username # "Victor"
nancy.posts.map {|x| x.title} # ["How to make chili oil", "How to make compost"]
victor.posts.map {|x| x.title} # ["Why bok choy is great for cats"]
Comment.create(body: "I agree!", user: nancy, post: bok_choy)
Comment.create(body: "This looks delicious!", user: victor, post: chili)
Comment.create(body: "I'm commenting on my own post.", user: nancy, post: chili)
nancy.comments.map {|x| x.body} # ["I agree!", "I'm commenting on my own post."]
victor.comments.map {|x| x.body} # ["This looks delicious!"]
chili.comments.map {|x| x.body} # ["This looks delicious!", "I'm commenting on my own post."]
recipes = Category.create(name: "recipes")
food = Category.create(name: "food")
cat = Category.create(name: "cat")
PostCategory.create(post: chili, category: recipes)
PostCategory.create(post: chili, category: food)
PostCategory.create(post: bok_choy, category: food)
PostCategory.create(post: bok_choy, category: cat)
recipes.posts.map {|x| x.title} # ["How to make chili oil"]
food.posts.map {|x| x.title} # ["How to make chili oil", "Why bok choy is great for cats"]
cat.posts.map {|x| x.title} # ["Why bok choy is great for cats"]
chili.categories.map {|x| x.name} # ["recipes", "food"]
bok_choy.categories.map {|x| x.name} # ["food", "cat"]
Create routes for posts and categories. Prevent the delete route (destroy action) from being accessed.
config/routes.db
Rails.application.routes.draw do
resources :posts, :categories, except: :destroy
end
Check that the output for rails routes -g posts
and rails routes -g categories
do not contain a route for the DELETE method.
Create controllers and views to view:
- all posts
- a specific post and its associated categories
- all categories
- a specific category and its associated posts
app/controllers/posts.rb
class PostsController < ApplicationController
def index
@posts = Post.all
end
def show
@post = Post.find(params[:id])
end
end
app/controllers/categories.rb
class CategoriesController < ApplicationController
def index
@categories = Category.all
end
def show
@category = Category.find(params[:id])
end
end
After each view is created, navigate to the appropriate URL in the browser to verify that the response is as expected.
Make sure your server is running before checking the URLs (run rails server
).
app/views/posts/index.html.erb
localhost:3000/posts
<h1>Posts</h1>
<table>
<thead>
<tr>
<th>Title</th>
<th>URL</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<% @posts.each do |post| %>
<tr>
<td><%= post.title %></td>
<td><%= post.url %></td>
<td><%= post.description %></td>
<td><%= link_to "Show", post %></td>
</tr>
<% end %>
</tbody>
</table>
app/views/posts/show.html.erb
localhost:3000/posts/:id
<h1><%= @post.title %></h1>
<p> Tags:
<% @post.categories.each do |category| %>
<%= link_to category.name, category_path %>
<% end %>
</p>
<p>URL: <%= @post.url %></p>
<p>Description: <%= @post.description %></p>
<%= link_to "All Posts", posts_path %>
app/views/categories/index.html.erb
localhost:3000/categories
<h1>Categories</h1>
<ul>
<% @categories.each do |category| %>
<li><%= link_to category.name.capitalize, category %></li>
<% end %>
</ul>
app/views/categories/show.html.erb
localhost:3000/categories/:id
<h1>Posts Tagged "<%= @category.name.capitalize %>"</h1>
<table>
<thead>
<tr>
<th>Title</th>
<th>URL</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<% @category.posts.each do |post| %>
<tr>
<td><%= post.title %></td>
<td><%= post.url %></td>
<td><%= post.description %></td>
<td><%= link_to "Show", post %></td>
</tr>
<% end %>
</tbody>
</table>
<%= link_to "All Categories", categories_path %>
- In
app/models/post.rb
, change the linebelongs_to :user
tobelongs_to :creator, class_name: "User", foreign_key: "user_id"
. - Open rails console and check that
Post.first.creator
returns the first user object, andPost.first.user
now throws aNoMethodError
.