Rails! Here I come (Day #3)

saral

Saral Karki

Posted on January 30, 2019

Rails! Here I come (Day #3)

Starting from where I left off yesterday. I had manually entered data to the database and fetched the data for viewing. Today, my goal was to take it up a notch and enter data on a form and fetch this data for viewing.

I could have used the resources available on rails, but because I wanted to create my first posts page from scratch I chose to manually create all the controllers and routes.

1. Building a mental picture

I started off by building a mental picture of what was required. I would need a route with get verbs(get method) for redirecting to post pages, and I would need a post verb(post method) for saving the user input data to my database. I would be using the same database Post that I created yesterday. With a quick Post.delete_all in my rails console, I wiped my database clean.

Next up, I made the routes ready in route.rb. I would need a /post/index route to view all the posts from the database (just the titles), posts/new would route to the form, and posts/:id would route to a show page that would display the texts of the post in its entirety. I also used an as command to change the prefix of the of posts#new to new_post and posts#show to post. These will be very helpful later on. I also changed the homepage for this exercise. My posts/index would be my homepage. I also would need a post method to fetch user inputs from the forms.

Rails.application.routes.draw do
get 'posts/index'
root 'posts#index'
post 'posts' => 'posts#create'
get 'posts/new'  => 'posts#new' as: 'new_post'
get 'posts/failure' => 'posts#failure` , as: 'failure'
get 'posts/:id' => 'posts#show', as: 'post'

end

TIP: Remember to get all the methods for posts before posts/id else what rails will do is start treating the actions such as new, failure as id, and when such id cannot be found (because they do not exist), rails throws an error.

2. Setting up Controllers

I had already generated the posts controller yesterday via $rails g controller posts. Therefore, I set forth on making updates to the controller.
First I defined the index function where all the contents of my database would be displayed as per the need. Next I defined the actionsnew, and show in the posts controller as per my route. Finally, I had to set up action for create.

class PostsController < ApplicationController

    def index  
        @posts = Post.all   
    end

    def show
        @post = Post.find(params[:id])
    end

    def new
        @post = Post.new
    end

    def create
        post = Post.new(post_params)
        if post.save
            redirect_to post_path(post)           
        else
            redirect_to new_post_path          
        end
    end

    private 
        def post_params
            params.require(:post).permit(:title,:blob,:author)
        end
end

  • Breaking down each action

    • The index action as described yesterday gets all posts from the Post database, and stores it in the instance @post , so that it can be later used in the views.
    • The show action searches and finds from the database the :id. This `param[:id] is generated from the routes.
    • the new action (here I am merely guessing) fetches data from the form, and stores it in the instance @post . This data is now ready to be saved to the database. It has not yet been written to the database, however, we have received the data from the form in the new page. So, I did a quick check in my console after I commented the code on the create action.

      Processing by PostsController#create as HTML
      Parameters: {"utf8"=>"✓",             
        "authenticity_token"=>"z7vZT7ReRs6LKg5bJuGgnMTuEzYwbDbdDvZmi+HAf2CCNICj2CEm/uxPX6Z            WqqEy0GPMLLGLL//Bl+BT/D38kQ==", "post"=>{"title"=>"Introduction",     "blob"=>"Hello World. How are you?", "author"=>"Saral"}, "commit"=>"Submit"}
      No template found for PostsController#create, rendering head     :no_content
      Completed 204 No Content in 56ms (ActiveRecord:          0.0ms)
      

      Let me try and break this down, I can see that the title, blob(or body) and author is being committed to submit. However, then it throws an error saying No templated found for PostsController#Create. This gives me an indication that I need to render a template in PostsController#create.

    • Finally, in the create action is where the magic happens. Here I declared a private function that defined the params for posts as post_params (need to understand this better). The params use the method .require and says it needs a post from the database, and only permits input from the title, blob(body) and author from the forms and saves only these into the database. One reason this is used is for security measures. With this in place, SQL injection can be prevented, and malicious users cannot post useless and harmful stuff to the database.

      After this is done, the create action fetches the post_params as defined on private. It then checks the condition, if the post was saved successfully it redirects to the post_path. If you recall the post_path is the posts/:id page. Therefore, we get redirected to a page posts/1. The '1' is a dummy id in this case. The id will keep changing as the number of items on our database grows. Finally, if the data is unable to be written to our database we redirect to a failure page.

      Once all this was done, and my view set up accordingly, I was able to send my data to my view page from the forms.

But, wait how do we set up the views? How do we make a connection between the backend and frontend?

3. The Views

  • Setting up the new page

    <div class="container">
    <div class = "row">
    <div class="col-md-12 post_heading">
        <h1> Your blog post goes here </h1>
    </div>
    </div>
    <div class="row">
    <div class="col-md-12">
        <%= form_for(@post) do |f|%>
            <div class="form-group">
                <%= f.label :title %><br>
                <%= f.text_field :title , class: "form-control" , placeholder: "Title" , :required => true %>
            </div>
            <div class="form-group">
                <%= f.label :blob, 'Story' %><br>
                <%= f.text_area :blob , class: "form-control blog_body" , rows: 20, :required => true %>                  
            </div>
            <div class="form-group text_size">
                <%= f.label :author %><br>
                <%= f.text_field :author , class: "form-control"%>
            </div>
            <div id = "btn">
                <%= f.submit "Submit", class: "btn btn-primary" %>
            </div>
         <% end %>           
    </div>
    

Noteable observation here is the embedded ruby code for generating the form alongside your regular html/css. We are using form_for method to get the variable @post . Recall, how we set up in our controller @post = Post.new. We are using that very variable to save the title, blod(body) and name of the author. Also, notice how I have used bootstrap classes to the ruby code. The final page looked like this

Alt New Post View

Finally, as using a similar html view as yesterday's index.html I was able to fetch and view the data.

    <div class="container">

    <h1> Post List </h1>
    <table class="table">
    <thead class="thead-dark">
    <tr>
        <th scope="col"> Title </th>
    </tr>
    </thead>
    <% @posts.each do |post| %>
    <tr>
        <td> <%= link_to post.title, post_path(post) %> </td>
    <% end %>  
    </table>
    </div>

Alt Final Image

Final words, there's a lot to unpack from what I did today,but overall I think this post sums it up. I could write a lot more about views and controller,but I feel I will be able to explain things better as I go forward in this coding and writing journey.

Tomorrow, we tacklet updating and deleting posts. Also, if time permits we look into user database and registration.

💖 💪 🙅 🚩
saral
Saral Karki

Posted on January 30, 2019

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

Rails! Here I come (Day #3)
rails Rails! Here I come (Day #3)

January 30, 2019