Skip to content

Latest commit

 

History

History
301 lines (247 loc) · 7.61 KB

chapter-templates.adoc

File metadata and controls

301 lines (247 loc) · 7.61 KB

Templates

Once you have been working with Rails for a while, you will always make the same changes after calling rails generate scaffold or rails generate model. You are going to adapt the scaffold to your requirements. Fortunately, you can replace the Rails templates for creating the controller or model files with your own custom templates. This saves a lot of time.

I am going to show you the basic principle by using the controller and model template as an example.

Tip
15 minutes spent optimizing a template in accordance with your requirements will save you many hours if not days of work later in every Rails project!

Scaffold Controller Template

Let’s assume you want to create a scaffold User:

$ rails generate scaffold User first_name last_name login
      [...]
      invoke  scaffold_controller
      create    app/controllers/users_controller.rb
      [...]
$

The controller app/controllers/users_controller.rb generated by default then looks like this in Rails 5.0:

app/controllers/users_controller.rb
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(:login)
    end
end

But if we only need HTML, no JSON and no comments then the file could also look like this:

app/controllers/users_controller.rb
class UsersController < ApplicationController
  before_action :set_user, only: [:show, :edit, :update, :destroy]

  def index
    @users = User.all
  end

  def show
  end

  def new
    @user = User.new
  end

  def edit
  end

  def create
    @user = User.new(user_params)

    if @user.save
      redirect_to @user, notice: 'User was successfully created.'
    else
      render :new
    end
  end

  def update
    if @user.update(user_params)
      redirect_to @user, notice: 'User was successfully updated.'
    else
      render :edit
    end
  end

  def destroy
    @user.destroy
    redirect_to users_url, notice: 'User was successfully destroyed.'
  end

  private
    def set_user
      @user = User.find(params[:id])
    end

    def user_params
      params.require(:user).permit(:login)
    end
end

The original template used by rails generate scaffold for generating the controller can be found in the Rails Github repository at https://github.com/rails/rails/blob/5-0-stable/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb

It is a normal ERB file that you can download and then save as new file lib/templates/rails/scaffold_controller/controller.rb (you may need to create the corresponding directories manually). To get the above desired result, you need to change the template as follows:

lib/templates/rails/scaffold_controller/controller.rb
<% if namespaced? -%>
require_dependency "<%= namespaced_file_path %>/application_controller"

<% end -%>
<% module_namespacing do -%>
class <%= controller_class_name %>Controller < ApplicationController
  before_action :set_<%= singular_table_name %>, only: [:show, :edit, :update, :destroy]

  def index
    @<%= plural_table_name %> = <%= orm_class.all(class_name) %>
  end

  def show
  end

  def new
    @<%= singular_table_name %> = <%= orm_class.build(class_name) %>
  end

  def edit
  end

  def create
    @<%= singular_table_name %> = <%= orm_class.build(class_name, "#{singular_table_name}_params") %>

    if @<%= orm_instance.save %>
      redirect_to @<%= singular_table_name %>, notice: <%= "'#{human_name} was successfully created.'" %>
    else
      render action: 'new'
    end
  end

  def update
    if @<%= orm_instance.update("#{singular_table_name}_params") %>
      redirect_to @<%= singular_table_name %>, notice: <%= "'#{human_name} was successfully updated.'" %>
    else
      render action: 'edit'
    end
  end

  def destroy
    @<%= orm_instance.destroy %>
    redirect_to <%= index_helper %>_url, notice: <%= "'#{human_name} was successfully destroyed.'" %>
  end

  private
    def set_<%= singular_table_name %>
      @<%= singular_table_name %> = <%= orm_class.find(class_name, "params[:id]") %>
    end

    def <%= "#{singular_table_name}_params" %>
      <%- if attributes_names.empty? -%>
      params[<%= ":#{singular_table_name}" %>]
      <%- else -%>
      params.require(<%= ":#{singular_table_name}" %>).permit(<%= attributes_names.map { |name| ":#{name}" }.join(', ') %>)
      <%- end -%>
    end
end
<% end -%>

Each time you now use rails generate scaffold, you get the controller in the variation you want.

Model Template

The basic idea is the same as with the controller in section "Scaffold Controller Template": it’s all about adapting the model created by the Rails generator to your own needs.

The model template used by rails generate model and therefore also by rails generate scaffold can be found in the Rails Github repository at https://github.com/rails/rails/blob/5-0-stable/activerecord/lib/rails/generators/active_record/model/templates/model.rb

Save this file in your Rails project under lib/templates/active_record/model/model.rb. If you want to edit the method to_s per default, your model.rb could for example look like this:

lib/templates/active_record/model/model.rb
<% module_namespacing do -%>
class <%= class_name %> < <%= parent_class_name.classify %>
<% attributes.select(&:reference?).each do |attribute| -%>
  belongs_to :<%= attribute.name %><%= ', polymorphic: true' if attribute.polymorphic? %>
<% end -%>
<% if attributes.any?(&:password_digest?) -%>
  has_secure_password
<% end -%>
end

  def to_s
    <%- if attributes.map{ |a| a.name }.include?('name') -%>
    name
    <%- else -%>
    "<%= class_name %> #{id}"
    <%- end -%>
  end

<% end -%>

If you now create a new model with rails generate model Book name number_of_pages:integer, the file app/models/book.rb will look like this:

app/models/book.rb
class Book < ActiveRecord::Base
  def to_s
    name
  end
end