Friday, April 21, 2017

Rails Scaffold Generators

Today, I finished Michael Hartl's chapter 2: A toy app (https://www.railstutorial.org/book/toy_app#cha-a_toy_app) . This chapter provides an overview to create a rails project with large amount of functionality automatically using the scaffold generator.

1. First thing first, create a new rails project:
$ cd ~
$ rails _5.0.1_ new toy_app
$ cd toy_app/
$ bundle install --without production

2. Initialize Git:
$ git init
$ git add -A
$ git commit -m "Initialize repository"
$ git remote add origin https://github.com/jimmy2046/toy_app.git
$ git push -u origin master

3. The config/routes.rb file re-directs the visitor to specific URL location (http://localhost:3000/users) when the a user is going to (http://localhost:3000/):
Rails.application.routes.draw do
  resources :microposts
  resources :users
  root 'users#index'
end

4. Generate a user data model using the scaffold parameter. A user has a name (string) and an email (string):
$ rails generate scaffold User name:string email:string


5. Database migration is needed each time a data model is created or modified:
$ rails db:migrate

6. Start the Rails server:
$ rails server

7. Browse it by Firefox:

8. The web application allows users to:
  • Add a new user
  • Edit user information
  • Delete a user

9. The users_controller.rb file is sub-class of application_controller.rb file. It is generated automatically by the scaffold parameter. The program explains the details instruction in Ruby when a command received. These include listing all users, showing a user, adding a user, editing a user, and deleting a user:

class UsersController < ApplicationController
  before_action :set_user, only: [:show, :edit, :update, :destroy]

  # GET /users
  # GET /users.json
  def index
    @users = User.all
  end

  # GET /users/1
  # GET /users/1.json
  def show
  end

  # GET /users/new
  def new
    @user = User.new
  end

  # GET /users/1/edit
  def edit
  end

  # POST /users
  # POST /users.json
  def create
    @user = User.new(user_params)

    respond_to do |format|
      if @user.save
        format.html { redirect_to @user, notice: 'User was successfully created.' }
        format.json { render :show, status: :created, location: @user }
      else
        format.html { render :new }
        format.json { render json: @user.errors, status: :unprocessable_entity }
      end
    end
  end

  # PATCH/PUT /users/1
  # PATCH/PUT /users/1.json
  def update
    respond_to do |format|
      if @user.update(user_params)
        format.html { redirect_to @user, notice: 'User was successfully updated.' }
        format.json { render :show, status: :ok, location: @user }
      else
        format.html { render :edit }
        format.json { render json: @user.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /users/1
  # DELETE /users/1.json
  def destroy
    @user.destroy
    respond_to do |format|
      format.html { redirect_to users_url, notice: 'User was successfully destroyed.' }
      format.json { head :no_content }
    end
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_user
      @user = User.find(params[:id])
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def user_params
      params.require(:user).permit(:name, :email)
    end
end



10. The User Model file user.rb specifies the meta data of table user in the database. As we can see in the codes, each user can have many microposts (foreign key). A user has a name and an email. The name and email are required fields that cannot be blank (not null).
class User < ApplicationRecord
  has_many :microposts
  validates :name, presence: true
  validates :email, presence: true
end

11. The app/views/users/index.html.erb file is a Ruby Embedded HTML file (ERB file). This presents the format of the HTML file when a visitor wants to see a list of users. This is a table with 2 columns. There are meta data: Name and Email. And there are text box for user input.
<h1>Listing users</h1>

<table>
  <thead>
    <tr>
      <th>Name</th>
      <th>Email</th>
      <th colspan="3"></th>
    </tr>
  </thead>

<% @users.each do |user| %>
  <tr>
    <td><%= user.name %></td>
    <td><%= user.email %></td>
    <td><%= link_to 'Show', user %></td>
    <td><%= link_to 'Edit', edit_user_path(user) %></td>
    <td><%= link_to 'Destroy', user, method: :delete,
                                     data: { confirm: 'Are you sure?' } %></td>
  </tr>
<% end %>
</table>

<br>

<%= link_to 'New User', new_user_path %>

12. Now, creating a second database table Micropost with meta data content (text) and user_id (integer):
$ rails generate scaffold Micropost content:text user_id:integer

13. Again, don't forget to do database migration:
$ rails db:migrate

14. The micropost controller: app/controllers/microposts_controller.rb is very similar to user controller. It defines the database actions, such as list, show, new, edit, destroy and etc.
class MicropostsController < ApplicationController
before_action :set_micropost, only: [:show, :edit, :update, :destroy]

# GET /microposts
# GET /microposts.json
def index
@microposts = Micropost.all
end

# GET /microposts/1
# GET /microposts/1.json
def show
end

# GET /microposts/new
def new
@micropost = Micropost.new
end

# GET /microposts/1/edit
def edit
end

# POST /microposts
# POST /microposts.json
def create
@micropost = Micropost.new(micropost_params)

respond_to do |format|
if @micropost.save
format.html { redirect_to @micropost, notice: 'Micropost was successfully created.' }
format.json { render :show, status: :created, location: @micropost }
else
format.html { render :new }
format.json { render json: @micropost.errors, status: :unprocessable_entity }
end
end
end

# PATCH/PUT /microposts/1
# PATCH/PUT /microposts/1.json
def update
respond_to do |format|
if @micropost.update(micropost_params)
format.html { redirect_to @micropost, notice: 'Micropost was successfully updated.' }
format.json { render :show, status: :ok, location: @micropost }
else
format.html { render :edit }
format.json { render json: @micropost.errors, status: :unprocessable_entity }
end
end
end

# DELETE /microposts/1
# DELETE /microposts/1.json
def destroy
@micropost.destroy
respond_to do |format|
format.html { redirect_to microposts_url, notice: 'Micropost was successfully destroyed.' }
format.json { head :no_content }
end
end

private
# Use callbacks to share common setup or constraints between actions.
def set_micropost
@micropost = Micropost.find(params[:id])
end

# Never trust parameters from the scary internet, only allow the white list through.
def micropost_params
params.require(:micropost).permit(:content, :user_id)
end
end 

 15. The app/models/micropost.rb data model defines the foreign key relation to user table. And it ensures the content field is not empty and the maximum length is 140 characters:
class Micropost < ApplicationRecord
belongs_to :user
validates :content, length: { maximum: 140 },
presence: true
end

16. Finally, I pushed the project to Github:
$ git status
$ git add -A
$ git commit -m "Finish toy app"
$ git push

17. The URL location on Github is:
18. You can download and try on your Linux computer by typing these in your shell terminal:
$ git clone https://github.com/jimmy2046/toy_app.git
$ cd ~/toy_app
$ rails db:migrate
$ rails server

1 comment:

  1. awesome post presented by you..your writing style is fabulous and keep update with your blogs Ruby on Rails Online Course

    ReplyDelete

How to kill an abandoned process in Linux/Unix

I remembered it, then I forgot, then I remembered it, and then I forgot again. In case of a Linux/Unit process hang, I have to figure out ...