Featured image of post Twitor #1: Create Post

Twitor #1: Create Post

Let's create Twito!, a Tweet Clone App by using Ruby on Rails !

Prerequisition

You have already installed rbenv and familiar with the command.

This project was made based on Progate

Generate New Project

Create new folder like mkdir new_project and go inside cd new_project. Set local for ruby version 2.7 using rbenv local 2.7. Then generate new project by using command rails _7.0.3_ new kicau.

Go to kicau dir, and run server using rails s

Put this address http://localhost:3000/ on browser and you’ll see Rails welcoming screen if success.

Create Homepage

Create the top page with rails generate controller home top.

A new web page is automatically created by this command, and will allow you to access localhost:3000/home/top. Well, when you run the command, Rails generates all the necessary files for displaying page. To display a page in Rails, you need the three things shown below:

  • View
  • Controller
  • Route

Understanding View

A view is an HTML file that’s used to create the look of a page. When the browser asks for a file, Rails returns a requested view to the browser to display a page. Views are located inside the views folder. The rails generate controller home top command generates the home folder and a file named top.html.erb in the views folder. erb is a unique file format, but you can think of it as a regular HTML file for now.

Because a view (HTML) contains the content displayed in the browser, you can change the content by editing the file. Let’s edit top.html.erb to make it look more like the Top page of TweetApp.

In the view for the Top Page (app/views/home/top.html.erb)

1
2
3
4
5
6
<h2>
  Tweet to the world
  <br>
  connect to the world
</h2>
<p>Share your favourite momments!</p>

Open the browser and check localhost:3000/home/top !

Understanding Controller

When displaying a page, Rails returns views via its controllers. Let’s take a look inside a controller file. The rails g controller home top command creates creates a controller file named home_controller.rb with a top method. A method within a controller is also known as action.

controller

The role of an action in a controller is to find a view from the views folder, then return it to the browser. The action looks for a folder with the same name as controller (Home), then finds a file with the same name as the action (top)

Understanding Route

While we return the view through the controller, the routing is the one reponsible for connecting the browser to the controller. Make sure to understand that the process for displaying a page is in the following order: routes -> controllers -> views.

Routes can be described with a routing table. A route points to a specific action of a controller according to the URL requested. When a URL is entered in the browser, it calls for a matching action in the controller based on the URL.

routing table

Routes are defined in the config/routes.rb file in the following syntax: get "URL" => "controller#action. For instance, as shown in the image below, the URL “localhost:3000/home/top” points to the top action in the Home controller.

Routes file

We can practice to change route of the Top page. Once you modify the route, you can access the Top page via localhost:3000/top by changing get "home/top" => "home#top" to get "top" => "home#top".

Create About Page

Let us create the about page. But now we can use again rails generate controller. The command we inputted when we created the Top page actually contains controller name and action name. The rails generate controller controller_name action_name command generates a controller and the files associated with it. However, you can’t use this command when a controller with the same name exists.

To add about page, we do manually.

  1. In Routes file, routes.rb, Add get "about" => "home#about"
  2. In home_controller.rb file, inside class HomeController, add action for about and left it empty.
  3. In the app/views/home/, add file called about.html.erb, and put some words in html format.

Create Posts

Let’s create an index action for the Posts page. Since the Posts controller doesn’t exists yet, we can use the command like rails g controller posts index. We’re going to write the HTML for the Posts page in views/posts/index.html.erb. First, let’s copy & paste the provided code to quickly create a page.

Feature

These are four common features of post:

  • View all posts -> use action posts#index to view all posts
  • Show detail post -> use action posts#showto view detail post
  • Create post -> use action posts#new and posts#create to view create new post form and sending the form data.
  • Edit post -> use action posts#edit and posts#update to view edit form and sending the updated form data.
  • Delete post -> use action posts#destroy to delete post record

Preparing a Database

A database consists of tables. Each table has rows and columns. In the posts table shown below, each row also known as a record, represents a post while each column represents a specific type of data.

To store data in a database, we need to create a table like the one in the last slide. Let us create the posts table for storing posts. There are two steps we must follow, i.e.

  1. Create a file that directs the changes to the database
  2. Apply the changes in the database

Create a Migration File

We need to create a migration file to make changes to the database. With the rails g model Post content:text command, you can create a migration file which adds the posts table with the content column. The text describe the type of data that will be stored in the column.

Look again rails g model .. command. The Post in the command represent a model name. With this command, the following files are generated:

  1. a file in app/models where a model is defined
  2. a migration file in db/migrate

Create a Table

In this step, we will apply the changes to the database using the migration file we have created. To apply the changes in the database, simply execute the command rails db:migrate. By executing the command, it will create a table according to the code in the migration file.

Adding Data to a Table

Let’s create an instance of Post from the Post model (Post class) in the rails console. To create an instance, we’re going to use the new method. For example, you can create a Post instance with the content, “Hello World”, post = Post.new(content: "Hello World").

To save a Post instance to the posts table, you need to run the save method, post.save. Because the Post model inherits from ApplicationRecord, it can use the methods defined in ApplicationRecord, like the save method.

We can get the first record in the posts table using post = Post.first. Then with post.content, we can get the content of the first post. To get all the data from the posts table, use post = Post.all which returns you all the data saved in the table in the form of an array. Then we can get an element using an index number like Post.all[0]. The data we get with Post.all[0] is the same as data you get with Post.first. So with Post.all[0].content, you can get the content of the first post.

Displaying Error Message

Rails provides a special flash variable. By assigning a string to flash[:notice] in an action, you can use flash[:notice] in the views. Flashes are automatically removed after being displayed once. We’ll add flash[:notice] to application.html.erb because it’ll be used in many places.

We put these lines after closing tag </header>

1
2
3
4
5
<% if flash[:notice] %>
  <div class="flash">
    <%= flash[:notice] %>
  </div>
<% end %>

Adding Routes

By writing posts/:id with a colon : in the URL of the route, you can map URLs like /posts/1 and /posts/2 to the show action. This applies to every URL written as /posts/.... In routes.rb, write

1
2
3
4
5
6
7
  get "posts/index" => "posts#index"
  get "posts/new" => "posts#new"
  get "posts/:id" => "posts#show"
  post "posts/create" => "posts#create"
  get "posts/:id/edit" => "posts#edit"
  post "posts/:id/update" => "posts#update"
  post "posts/:id/destroy" => "posts#destroy"

It’s important that you need to write the route posts/:id below posts/index and posts/new. This is because the routing looks for URLs from the top. Writing it above posts/index or posts/new would cause the URL localhost:3000/posts/index or localhost:3000/posts/new to match the route posts/:id.

Generally, when you press the submit button, the post data will be sent to the server-side (Rails). Let’s prepare a create action to save the receive post data to the database. The URL of the create action should be /posts/create. We use post method on the create when receiving data from a form.

Since the update action receives the value from the form, we also need to change the routing to post instead of get. It is also updates the post with a specific id, so make sure to include id in the URL.

Adding Action

Let’s adding some actions in the Post Controller. In the index action of the Post controller, We catch the data from the database using Post.all, then assign them to @posts variable. In posts/index.html.erb, we can use the each method to loop through the @posts array to print each post.

In the show action, the value of the :id is stored in a hash variable known as params. You can get that value using params[:id]. To display the post, we declare the variable @post in the show action and assign it the posts whose id is equal to params[:id] by retrieving them from the database. Then we’ll display the details of the @post in ‘show.html.erb`.

Since new.html.erb will also be displayed via new action (localhost:3000/posts/new), an error will occur unless we also define the @post variable there. We can solve this by assigning Post.new (a blank post instance), to @post as code shown above.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
class PostsController < ApplicationController
  
  def index
    @posts = Post.all.order(created_at: :desc)
  end
  
  def show
    @post = Post.find_by(id: params[:id])
  end
  
  def new
    @post = Post.new
  end
  
  def create
    @post = Post.new(content: params[:content])
    if @post.save
      flash[:notice] = "Post successfully created"
      redirect_to("/posts/index")
    else
      render("posts/new")
    end
  end
  
  def edit
    @post = Post.find_by(id: params[:id])
  end
  
  def update
    @post = Post.find_by(id: params[:id])
    @post.content = params[:content]
    if @post.save
      flash[:notice] = "Post successfully edited"
      redirect_to("/posts/index")
    else
      render("posts/edit")
    end
  end
  
  def destroy
    @post = Post.find_by(id: params[:id])
    @post.destroy
    flash[:notice] = "Post successfully deleted"
    redirect_to("/posts/index")
  end
  
end

There are two problems for creating action:

  1. No matching view for the create action.
  2. Can’t save any posts.

For solving this problem, we can redirect to a different URL instead of adding a view. For our create action, let us redirect the request to the Posts page.

1
2
3
4
5
6
7
class PostController < ApplicationController
...
  def create
    redirect_to("/posts/index")
  end
...
end

To solve second problem, we have go to two steps:

  1. Send post data to the create action
  2. Receive the sent data in create action and save it

To send data to the create action, We need to specify the name attribute of the <textarea> tag. This way, the data in the <textarea> tag will be sent to the Rails side as a hash with the name attributes as the key.

Once you specify the name attributes of a form element, the action of the controller can receive the form data. Since the params variable is a hash with the name attibutes as the key, we can get content with params[:content].

To receive the sent data in the create action, we can create a @post with the data received from the form by using params[:content] as the argument.

1
2
3
4
5
6
7
8
9
class PostController < ApplicationController
...
  def create
    @post = Post.new(content: params[:content])
    @post.save
    redirect_to("/posts/index")
  end
...
end

We’ve used params when receiving input data and also to get the id value from the URL. Remember there are two ways of using params:

  1. Get the value from the URL of the route
  2. Get the form input data with the name attribute

Adding Views

In the views/posts folder, we need to create

  • new.html.erb is html page to display create-post form
  • index.html.erb is to view all posts
  • show.html.erb is to show detail post
  • edit.html.erb is to display post-edit form

New Post

You can send the data entered in the form using the form_tag method. The destination URL is specified like: <%= form_tag(destination URL) do %>. This allows <input type="submit" ...>, also known as the submit button in the form, when clicked, for the data to be sent to the specified URL.

Put these lines to posts/new.html.erb,

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
<div class="main posts-new">
  <div class="container">
    <h1 class="form-heading">Create a new post</h1>
    <%= form_tag("/posts/create") do %>
      <div class="form">
        <div class="form-body">
          <% @post.errors.full_messages.each do |message| %>
            <div class="form-error">
              <%= message %>
            </div>
          <% end %>
          
          <textarea name="content"><%= @post.content %></textarea>
          </div>
        <input type="submit" value="Post">
        
      </div>
    <% end %>
  </div>
</div>

Displaying All Post

In posts/index.html.erb, we can use the each method to loop through the @posts array to print each post.

1
2
3
4
5
6
7
8
9
<div class="main posts-index">
  <div class="container">
    <% @posts.each do |post| %>
      <div class="posts-index-item">
        <%= link_to(post.content, "/posts/#{post.id}") %>
      </div>
    <% end %>
  </div>
</div>

Displaying Detail Post

Then in the /posts/show.html.erb, put these

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
<div class="main posts-show">
  <div class="container">
    <div class="posts-show-item">
      <p>
        <%= @post.content %>
      </p>
      <div class="post-time">
        <%= @post.created_at %>
      </div>
      <div class="post-menus">
        <%= link_to("Edit", "/posts/#{@post.id}/edit") %>
        <%= link_to("Delete", "/posts/#{@post.id}/destroy", {method: "post"}) %>
      </div>
    </div>
  </div>
</div>

In ’erb’ (or Embedded Ruby) file format like ‘index.html.erb’, you can embed Ruby code in HTML using <% %> brackets. Let’s try to assign and display a variable called ‘post1’. Put this code in an ‘html.erb’ file format.

1
2
3
<% post1 = "Learning Rails" %>

<%= post1 %>

We’ve learned that we can <% %> and <%= %> to embed Ruby code. <% %> is used in cases like defining a variables as it won’t be displayed. <%= %> on the other hand, is used for cases like printing the content of a variable as it will be displayed.

Edit Form

We’ll able to edit and delete posts from the Post details page as shown in the left image. We’ll also create an Edit post page to edit posts as shown in the right image.

Let’s add an Edit and Delete links in show.html.erb as shown below

1
2
3
4
...
  <%= link_to("Edit", "/posts/#{@post.id}/edit") %>
  <%= link_to("Delete", "/posts/#{@post.id}/destroy", {method: "post"}) %>
...

You can send a post request with link_to method by adding {method: "post"} as the third argument.

The data of the form needs to be sent to the update action in order to be saved to database. Just like with the New post page, you can specify the destination using the form_tag method. In the posts/edit.html.erb, put these lines

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
<div class="main posts-new">
  <div class="container">
    <h1 class="form-heading">Edit a post</h1>
    <%= form_tag("/posts/#{@post.id}/update") do %>
      <div class="form">
        <div class="form-body">

          <!-- This code is to get the error messages and put them in variables with an each loop -->
          <% @post.errors.full_messages.each do |message| %>
            <div class="form-error">
              <%= message %>
            </div>
          <% end %>

          <textarea name="content"><%= @post.content %></textarea>
          <input type="submit" value="Save">
        </div>
      </div>
    <% end %>
  </div>
</div>

where <%= @post.content %> in line 15 is the initial value.

Post Validation

Validation is set in the models/post.rb as shown in codes below.

1
2
3
class Post < ApplicationRecord
  validates :column_name, {details of the validation}
end

You can use the validates method, and specify the column name and the details of the validation as arguments. By using {presence: true} as shown below.

1
2
3
class Post < ApplicationRecord
  validates :content, {presence: true}
end

Validation can be used to check not only the existence of a value but the number of characters as well. As shown in the image, We can set the maximum number of characters using

1
2
3
class Post < ApplicationRecord
  validates :content, {length: {maximum: 140}}
end

The list of validation rules, known as validators, is actually a hash. You can specify more than one by separating them with a comma , as shown below

1
2
3
class Post < ApplicationRecord
  validates :content, {presence: true, length: {maximum: 140}}
end
comments powered by Disqus
Built with Hugo
Theme Stack designed by Jimmy