Rails: Custom view scaffold

12 Aug 2014

Recently I thought of setting up custom view scaffold for our project. In order to setup the default views like index, edit, show, new & _form are easy, just need to put our custom html in lib/template/erb/scaffold directory.

Here is an example of edit.html.erb custom HTML.

<!-- lib/template/erb/scaffold/edit.html.erb -->
<%% provide(:title, '<%= class_name %>') %>

<%% content_for :breadcrumb do %>
    <%%= link_to('Home', root_path) %>
    <%%= link_to('<%= plural_table_name %>', <%= plural_table_name %>_path) %>
    <a class="current"><%%= @<%= singular_table_name %>.name %></a>
<%% end %>

<%%= render 'form' %>

Note that I have used <%% & <%%= which will prevent the evaluation of expression. The class_name, plural_table_name, singular_table_name are the methods provided by rails to create scaffold. Since I used <%= around class_name, the expression will be evaluated while generating the scaffold.

But things got complicated when I need to generate index.js along. In rails the view names are hardcoded in railties/lib/rails/generators/erb/scaffold/scaffold_generator.rb#L26-L28

Custom generator

After some googling, as usual a stackoverflow answer showed a way generate custom view files. In that answer, the author actually defined a new template engine. But It doesn’t support generating index.js. So I tweaked some and made it possible.

# lib/generators/custom/scaffold_generator.rb
require 'rails/generators/named_base'
require 'rails/generators/resource_helpers'
require 'rails/generators/named_base'

module Custom # :nodoc:
  module Generators # :nodoc:
    class Base < Rails::Generators::NamedBase #:nodoc:
      protected

      def format
        :html
      end

      def handler
        :erb
      end

      def filename_with_extensions(name)
        cformat = name[/\.js/] ? nil : format
        [name, cformat, handler].compact.join(".")
      end
    end

    class ScaffoldGenerator < Base # :nodoc:
      include Rails::Generators::ResourceHelpers

      source_root File.join(Rails.root, 'lib', 'templates', 'erb', 'scaffold', File::SEPARATOR)

      argument :attributes, :type => :array, :default => [], :banner => "field:type field:type"

      def create_root_folder
        empty_directory File.join("app/views", controller_file_path)
      end

      def copy_view_files
        available_views.each do |view|
          filename = filename_with_extensions(view)
          template filename, File.join("app/views", controller_file_path, filename)
        end
      end

    protected
      def available_views
        %w(index edit show new _form _search_form _table index.js)
      end
    end
  end
end

Now configure new Custom template engine,

In Rails 3

# config/application.rb
config.generators do |g|
  # ...
  g.template_engine :custom
  g.fallbacks[:custom] = :erb # or haml/slim etc
end

Done, Now rails g scaffold <name> command will generate index, edit, show, new, _form, _search_form, _table & index.js.

If you find my work helpful, You can buy me a coffee.