Scaffold Templates

Episode #508 by Teacher's Avatar David Kimura

Summary

In this episode, we explore using the scaffold generator and modifying the templates to simplify the creation of consistent, Tailwind CSS-styled views and controllers in a Rails application, saving time when prototyping and building new features.
rails view template 13:42

Chapters

  • Introduction (0:00)
  • Locating the templates (1:44)
  • Creating the template folders (2:12)
  • Downloading the templates (2:25)
  • Generating with the templates (2:48)
  • Downloading the Tailwind templates (3:23)
  • Updating the index template (4:16)
  • Discussing when this is a good fit (10:30)
  • Using the controller generator (10:52)
  • Using the scaffold controller template (11:35)
  • Final thoughts (12:25)

Resources

bin/rails app:templates:copy
This will download the controller view, mail layouts/view, and scaffold views.
Download Source Code

Summary

# Terminal
rails g scaffold users first_name last_name email --force
wget https://raw.githubusercontent.com/rails/rails/main/railties/lib/rails/generators/erb/scaffold/templates/show.html.erb.tt
wget https://raw.githubusercontent.com/rails/rails/main/railties/lib/rails/generators/erb/scaffold/templates/partial.html.erb.tt
wget https://raw.githubusercontent.com/rails/rails/main/railties/lib/rails/generators/erb/scaffold/templates/index.html.erb.tt
wget https://raw.githubusercontent.com/rails/rails/main/railties/lib/rails/generators/erb/scaffold/templates/edit.html.erb.tt
wget https://raw.githubusercontent.com/rails/rails/main/railties/lib/rails/generators/erb/scaffold/templates/new.html.erb.tt
wget https://raw.githubusercontent.com/rails/rails/main/railties/lib/rails/generators/erb/scaffold/templates/_form.html.erb.tt

# lib/templates/erb/scaffold/index.html.erb.tt
<%% content_for :title, "<%= human_name.pluralize %>" %>

<div class="w-full">
  <%% if notice.present? %>
    <p class="py-2 px-3 bg-green-50 mb-5 text-green-500 font-medium rounded-md inline-block" id="notice"><%%= notice %></p>
  <%% end %>

  <div class="flex justify-between items-center">
    <h1 class="font-bold text-4xl"><%= human_name.pluralize %></h1>
    <%%= link_to "New <%= human_name.downcase %>", new_<%= singular_route_name %>_path, class: "rounded-md px-3.5 py-2.5 bg-blue-600 hover:bg-blue-500 text-white block font-medium" %>
  </div>

  <div id="<%= plural_table_name %>" class="min-w-full divide-y divide-gray-200 space-y-5">
    <%% if @<%= plural_table_name %>.any? %>
      <div class="orverlow-x-auto">
        <table class="min-w-full divide-y divide-gray-200">
          <thead class="bg-gray-50">
            <tr>
              <%- attributes.reject(&:password_digest?).each do |attribute| -%>
              <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"><%= attribute.human_name %></th>
              <%- end -%>
              <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Actions</th>
            </tr>
          </thead>
          <tbody class="bg-white divide-y divide-gray-200">
            <%% @<%= plural_table_name %>.each do |<%= singular_table_name %>| %>
              <tr class="hover:bg-gray-50">
                <%- attributes.reject(&:password_digest?).each do |attribute| -%>
                <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-700">
                  <%- if attribute.attachment? -%>
                  <%% if <%= singular_name %>.<%= attribute.column_name %>.attached? %>
                    <%%= link_to <%= singular_name %>.<%= attribute.column_name %>.filename, <%= singular_name %>.<%= attribute.column_name %>, class: "text-blue-600 hover:text-blue-900" %>
                  <%% end %>
                  <% elsif attribute.attachments? -%>
                  <%% <%= singular_name %>.<%= attribute.column_name %>.each do |<%= attribute.singular_name %>| %>
                    <div class="mb-1"><%%= link_to <%= attribute.singular_name %>.filename, <%= attribute.singular_name %>, class: "text-blue-600 hover:text-blue-900" %></div>
                  <%% end %>
                  <% else -%>
                  <%%= <%= singular_name %>.<%= attribute.column_name %> -%>
                  <%- end -%>
                </td>
                <%- end -%>
                <td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
                  <%%= link_to "View", <%= singular_name %>, class: "text-blue-600 hover:text-blue-900 mr-3" %>
                  <%%= link_to "Edit", <%= edit_helper(singular_name, type: :path) %>, class: "text-indigo-600 hover:text-indigo-900" %>
                </td>
              </tr>
            <%% end %>
          </tbody>
        </table>
      </div>
    <%% else %>
      <div class="px-6 py-4 text-center text-gray-500">
        No <%= human_name.pluralize.downcase %> found. Create your first one now.
      </div>
    <%% end %>
  </div>
</div>

# lib/templates/rails/scaffold_controller/controller.rb.tt
<% module_namespacing do -%>
class <%= controller_class_name %>Controller < ApplicationController
  before_action :set_<%= singular_table_name %>, only: %i[ show edit update destroy ]
  before_action :user_signed_in?

  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 <%= redirect_resource_name %>, notice: <%= %("#{human_name} was successfully created.") %>
    else
      render :new, status: :unprocessable_entity
    end
  end

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

  def destroy
    @<%= orm_instance.destroy %>
    redirect_to <%= index_helper %>_path, notice: <%= %("#{human_name} was successfully destroyed.") %>, status: :see_other
  end

  private

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

  def <%= "#{singular_table_name}_params" %>
    <%- if attributes_names.empty? -%>
    params.fetch(:<%= singular_table_name %>, {})
    <%- else -%>
    params.expect(<%= singular_table_name %>: [ <%= permitted_params %> ])
    <%- end -%>
  end
end
<% end -%>