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! |
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:
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:
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:
<% 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.
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:
<% 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:
class Book < ActiveRecord::Base
def to_s
name
end
end