- Read Tutorial
- Watch Guide Video
In this deep dive guide we'll examine how generators work in Rails. Additionally, we'll walk through how to create a custom generator in Rails.
We covered a substantial amount of information in a short period of time. If you're still unsure about some of the elements we've just covered, I'd definitely recommend going through the guides again. The more times you view the information the more clear it will become.
In this deep dive section, I want to take an immersive look into what generators are, and how we can customize them.
Generators, whether the scaffold, controller, model or resource, are incredibly powerful, and one of the reasons why Rails became one of the most widely utilized frameworks in the world. Additionally, one of the impressive things about generators, is that they can be customized. That customization is what we're going to investigate in this guide.
Not only is it a benefit to learn how to customization a generator, the process of customization can clarify what generators do because you can see what occurs during every stage of the generation process. With that in mind, let's get started.
Deep Dives and how they Differ
Before we begin this demo, I want to make one clarification. These deep dives are separate from the rest of the course. My goal with these deep dive guides is to give you a laser like focus on a specific topic. We will not work on our main application during these specialized guides. The purpose of them is to take a step back, and really dig into the concepts.
I have seen many tutorials that only focus on one project. That is typically what I do, but you miss out on the ability to take a much deeper look at some of the more advanced components of the Rails framework.
Typically, in each deep dive, we will be creating brand new applications from scratch for the sole purpose of studying an isolated feature. So, in this guide we will be focussing on generators specifically.
Creating a New Application
Let's go to the terminal and create a new application in your Desktop folder. The command is
rails new GeneratorApp -T --database=postgresql
You can call your app whatever you want. In this instance Generator doesn’t have any special meaning. Remember the option -T
means we will build without testing files and the option --database=postgresql
designates that we want to use postgres instead of the default database. We are using these preferences just to follow the same pattern we used before.
Once the app is created, you can clear your terminal screen with the command clear
.
Now, cd into your new app with the imperative cd <AppName>
. If you followed along with my chosen name you would use:
cd GeneratorApp
Next, we'll create our database with the command rails db:create
The other reason I like to create different apps with these deep dives is, if you're new to Rails, and this is your first course, going through the process of creating multiple apps is actually very helpful, because it allows you to get familiar with the process. Soon, you'll be able to run these commands in your sleep!
Back to the Scaffold
Now that we have our application, let's talk about generators, specifically the scaffold.
We'll start by creating a scaffold with the command rails g scaffold Post title:string body:text
This command will generate a massive amount of content for us.
Next, we'll update the database with the code rails db:migrate
Now we want to run the rails server. If you don't have a screen splitting tool that will allow you to split your terminal screen into multiple windows, you can always press COMMAND +T, and this will open up another terminal window. Let's start the rails server rails s
in the new terminal screen, so it won't get in the way of anything we do in the other.
Switch to the browser and go to localhost:3000, and you will see the Yay! Your on Rails site. Go ahead and add /posts
to your url and you should see this screen:
Here, I can do all the CRUD functionality that was created with scaffolds, which is fantastic. However, there are a few things that can be bothersome with this generator.
First of all, notice how it gives us some really weird styles in our view. For example, as soon as you hover over a link, you get a black background.
This styling is generated when the scaffold runs and creates a stylesheet called scaffolds.scss
. It is a file I've never used in a production application, so I don't see a point in having it. Thankfully, our generators are completely customizable, and we will discover how we can run a scaffold without creating this css file.
How to Customize
First we will discuss how to customize the generators by turning things on or off.
Next, we will explore a deeper level of customizing by modifying the content that is generated.
Let's open this code for our generator app in sublime. We have our views and our posts and all these great items we wanted. However, what if I don't want to have the scaffold stylesheet? Technically, I can go to app/stylesheets
, right click on scaffolds.scss
, and delete the file. This will remove those unwanted styles we saw in the browser. If you switch over now, you'll just see the default styles
Now you may think this styling is even more unappealing than the previous one --Yes, it is. What you are seeing now are the default HTML styles. The good news is, these defaults are easier to override and you will run into fewer conflicts using them. Usually, if I am starting an app completely from scratch, I prefer to start with these HTML defaults rather than the underdeveloped styles that scaffolds give you.
While it was easy to just delete that file, if you're building a big app, there's no point in deleting the css file, because every time you run a scaffold, it is going to generate those css files for you.
The Application.rb file
A better way to control the generator is to update to your main application file: config/application.rb
.
require_relative 'boot' require "rails" # Pick the frameworks you want: require "active_model/railtie" require "active_job/railtie" require "active_record/railtie" require "action_controller/railtie" require "action_mailer/railtie" require "action_view/railtie" require "action_cable/engine" require "sprockets/railtie" # require "rails/test_unit/railtie" # Require the gems listed in Gemfile, including any gems # you've limited to :test, :development, or :production. Bundler.require(*Rails.groups) module GeneratorApp class Application < Rails::Application # Setting in the config/environments/* take precedence over those specified here. # Application configuration should go into files in config/initializers # —all .rb files in that directory are automatically loaded. end end
To tailor the generator to our needs, we can paste a set of custom configuration items inside the GeneratorApp
module, like this:
module GeneratorApp class Application < Rails::Application config.generators do |g| g.orm :active_record g.template_engine :erb g.test_framework :test_unit, fixture: false g.stylesheets false g.javascripts false end end end
This configuration will manage all the different generators we have. The code
config.generators do |g|
indicates that for ever every generator that runs the items in the method should be set as follows:
g.orm :active_record
The orm
should be set to active_record
. This means the app should use the standard Rails default connection to the database. It would be a rare occasion that you would change this setting, for example if you wanted to connect to a new SQL database, or something custom.
g.template_engine :erb
Next, is our template_engine. You can see this is set to erb
, which is what we will be using in this course. However if you want to use Slim or HAML, you could change the erb
to slim
or haml
respectively, and this code would use the corresponding templating engine. We're not going to change it for this course, but it's definitely something you should know is available to you.
g.test_framework :test_unit, fixture: false
Moving on, you'll need a test_framework
. Though we skipped the test environment, and we do not have a test directory, in a production environment, you will want to have tests. Scaffolds is going to create some tests for you, and you can define which test framework you want to use. By default it is :test_unit
. You can choose to have a different testing framework such as rspec
, or whichever framework performs best for your application.
g.stylesheets false g.javascripts false
Next, is our stylesheets
parameter. With this parameter, you can tell the generator whether you want to generate stylesheets or not. It is the same case with javascript. Here we’ve obviously chosen not to create them by using false
.
Viewing the Results
Now that we have set these custom settings for our generators, let’s go ahead and save the file and return to the terminal.
We can run another scaffold generator: rails g scaffold Blog title:string
Let's also migrate the database with the command rails db:migrate
Remember my rails server is running in the background, so I don't have to start it up again. I can just switch to the browser and go to localhost:3000/blogs. If you hover over the New Blog link, you can see there is no black background, so those scaffold.css styles were not added.
You can also go to your file tree in sublime and look to app/stylesheets
. You'll see that it did not generate any custom stylesheet for blogs. Likewise, it did not create a custom javascript file for blogs.
There may be times when you don't want stylesheets created, but you do want a javascript file. Simply change the configuration to true
for javascripts.
module GeneratorApp class Application < Rails::Application config.generators do |g| g.orm :active_record g.template_engine :erb g.test_framework :test_unit, fixture: false g.stylesheets false g.javascripts true end end end
If you run the scaffold generator again, rails g scaffold Guide title:string
, then rails db:migrate
You can see in your terminal that it did create a javascript file for us in app/assets/javascripts/guides.coffee
This shows the level of granularity you can get by turning things on and off with your configuration parameters.
Even More Customization
Now, to some more in-depth customization! Let's say you really loathe the way the scaffold sets up your data in tables.
Notice this table format that Rails uses for displaying records? Though tables are great for presenting tabular data, you may sometimes want the data to be displayed side-by-side or in paragraph form. There is a way to fix this!
One way is to go to your blog's index.html.erb
page and make all styling changes there. The problem is you'll have to make the same change every time you run a scaffold. For example, let's say you changed the style to match your website design in your blog's index.html.erb
file. Next, you create another feature, say posts. You'll have to go and make those same exact changes in post's index.html.erb
file too. That would be incredibly redundant!
Instead, you can customize what is generated, and it is relatively straightforward to do. To begin, go to your lib
directory. We'll cover this directory in depth later in the course, but for now, understand that lib
allows you to have an external source of customization for your app. Essentially, we're going to make custom styles that will override the default styles for you. For that, we need to create a new directory called templates
.
(Developers note: to create the new directory in the lib, right click on the lib directory and select New Folder, and give it the name templates)
Inside of templates we will create two more directories. First, we're going to create a directory called erb
. It is titled 'erb' because we are using the ‘erb’ syntax. If you are using Haml, or some other templating engine, you would use that as the directory name.
Next, we will create another directory called scaffold
inside of erb
. This may seem like a lot of directories, which technically it is, but this is the way it needs to be organized for our override to work.
Inside this scaffold
directory, I'm going to create a file called index.html.erb
.
(Developers note: to create a new file right click on the folder and choose new file, then give it a name and hit enter)
The process of creating this file set up will allow us to override our index action. Though we can do this for any action, such as the show, new, form, or any of those, for demonstration purposes, I think the index is the easiest to demonstrate and understand.
So here in our lib/templates/erb/scaffold/index.html.erb file, we can technically override whatever we want. We could add:
<h1>My post</h1>
Then, when we run a scaffold it would simply create this tiny file that displays this one title. This would not be very effective, and it would basically defeat the purpose of having scaffolds because the biggest advantage of scaffolds is that it builds all that functionality for you! Rather, what we will do, is override and customize the default behavior. The easiest way to do that is by copying and pasting the source code from Rails.
Go to https://gist.github.com/jordanhudgens/2023f69b3a16d13c86d57091eec39fe9
Since we're going to override index.html.erb
, copy that code and paste in your new file.
<p id="notice"><%%= notice %></p> <h1><%= plural_table_name.titleize %></h1> <table> <thead> <tr> <% attributes.reject(&:password_digest?).each do |attribute| -%> <th><%= attribute.human_name %></th> <% end -%> <th colspan="3"></th> </tr> </thead> <tbody> <%% @<%= plural_table_name %>.each do |<%= singular_table_name %>| %> <tr> <% attributes.reject(&:password_digest?).each do |attribute| -%> <td><%%= <%= singular_table_name %>.<%= attribute.name %> %></td> <% end -%> <td><%%= link_to 'Show', <%= singular_table_name %> %></td> <td><%%= link_to 'Edit', edit_<%= singular_table_name %>_path(<%= singular_table_name %>) %></td> <td><%%= link_to 'Destroy', <%= singular_table_name %>, method: :delete, data: { confirm: 'Are you sure?' } %></td> </tr> <%% end %> </tbody> </table> <br> <%%= link_to 'New <%= singular_table_name.titleize %>', new_<%= singular_table_name %>_path %>
One of the most advatageous things about open source software is that all this code is freely available to us. These files give us the ability to learn from some of the best Rails developers the world. When you study their code, you can learn a great deal by reverse engineering the processes they have created. I've taught myself quite a bit about Ruby and Rails framework just by looking at the source code.
Going back to our new file, if you run this as it is, it will give us no overrides. It will simply give us exactly what the system already has. There would not really be a point to doing that. So we can customize it to have the changes we want.
For example, I want to change the title size from an <h1>
to an<h2>
sized heading.
<h2><%= plural_table_name.titleize %></h2>
Let’s also make some other changes. We can go to the browser and create another blog to see how the view behaves. There are a few things I think we should change. We’ve already reduced the size of our heading, we can also add a horizontal line and remove the table. While designing content, it's pretty rare I use tables as I prefer to have the items stacked over one another, so let’s look at the best way to do that.
Back in sublime in our file for override changes, let’s add a horizontal line <hr>
under our header.
<p id="notice"><%%= notice %></p> <h1><%= plural_table_name.titleize %></h1> <hr> <table>
You will notice at the top of the file there is a line that references ‘notice’. Remember when you go through the process of creating a new blog in the browser a notice pops up at the top of the screen that says, Blog was successfully created . You do not see this notice a lot on the index action, so you don’t really need to be concerned with it here, but it is good to know that if you were creating another app, where you would use notices, you do have the option of moving it wherever you like on the page. You can rearrange all the items in this view however you want. We will do some of this customization in our Portfolio app. For now you could just move the notice under the heading. You won’t actually see that right now because you will be redirected to the show page.
<h1><%= plural_table_name.titleize %></h1> <p id="notice"><%%= notice %></p> <hr> <table>
So we’ve add the horizontal line with the code <hr>
. It will be displayed under the heading to split the page and give some definition.
Next, I want to get rid of the table completely. Select and remove this code from your file:
<table> <thead> <tr> <% attributes.reject(&:password_digest?).each do |attribute| -%> <th><%= attribute.human_name %></th> <% end -%> <th colspan="3"></th> </tr> </thead>
You will also need to remove the closing </table>
tag
Next, you will need to select the table body section found between the <tbody> </tbody>
tags, and move the whole set of text to the left. Essentially you are removing the current indentation.
Next, we'll change the <tbody>
and </tbody>
to <div>
and </div>
respectively.
The<tr>
and<td>
tags are related to tables, so we need to update those as well. The <tr>
tags we will change to <div>
tags, and the <td>
tags we will change to <p>
paragraph tags.
Sublime Tip
You could hold the command key and double click on each <td>
tag to select and change them, but there is an easier way. Hit COMMAND+F. This will open the search box at the bottom of the screen. In the search box type td
, then click on the "Find all" button. This will select all the instances of td for you. Simply type in p
and it will update all the td tags to paragraph tags.
Another change I like to make is to replace the word "destroy". That language seems a bit odd so I want to change it to "delete".
<td><%%= link_to 'Delete', <%= singular_table_name %>, method: :delete, data: { confirm: 'Are you sure?' } %></td>
Lastly, instead of a link that just reads “new” followed by the item title, I'm going change the link to read "Create a New” item.
<%%= link_to 'Create a New <%= singular_table_name.titleize %>', new_<%= singular_table_name %>_path %>
The customized file should now look like this:
<h2><%= plural_table_name.titleize %></h2> <p id="notice"><%%= notice %></p> <hr> <div> <%% @<%= plural_table_name %>.each do |<%= singular_table_name %>| %> <div> <% attributes.reject(&:password_digest?).each do |attribute| -%> <p><%%= <%= singular_table_name %>.<%= attribute.name %> %></p> <% end -%> <p><%%= link_to 'Show', <%= singular_table_name %> %></p> <p><%%= link_to 'Edit', edit_<%= singular_table_name %>_path(<%= singular_table_name %>) %></p> <p><%%= link_to 'Destroy', <%= singular_table_name %>, method: :delete, data: { confirm: 'Are you sure?' } %></p> </div> <%% end %> </div> <br> <%%= link_to 'Create a New <%= singular_table_name.titleize %>', new_<%= singular_table_name %>_path %>
This is just an example to show you the different kinds of things you can do to override the rails defaults.
Real World Instance
To give you a real-life example, if I am working on a project for a client, I would usually be given a layout created by a designer. I would implement the code for it here in this override file. Most likely I'll work with some pre-existing css styles. So, right away I would add those classes to the corresponding divs here in in the generator.
For example, I would add a css class to a div: <div class=“content”>
or whatever style I am using for the application. So when I run a generator the files would already have those styles built in.
This is Not Retroactive
One thing to note is that these overrides are not retroactive. If you go back to the browser and hit refresh, you will see that nothing changes even though we have made changes to this file. This template is created so that all the future generators and scaffolds I run will use these styles. Usually, I do create the template early on in the life cycle of a new project so this code will be applied to all the generators and scaffolds.
Switching back to the terminal, let’s create a new scaffold: rails g category title:string description:text
You will get an error message, Could not find generator ‘Category’ The command didn't execute because I didn't indicate what type of generator I wanted to use.
So, the correct code is: rails g scaffold Category title:string description:text
That will create everything for us. Now we have to migrate the database with the command: rails db:migrate
. Your rails server should still be running, so when you switch over to your browser, and go to localhost:3000/categories, you should see the updated design.
Notice you have the horizontal line, a different heading and a more understandable link.
If you click on Create a New Category, Add a title and description and hit enter you will be able to see how it is now displayed. The data is no longer in a table form, but is stacked on top of one another.
So, we have overridden all the default layout for the scaffold generator. All that we had to do was customize and adjust the lib/templates/erb/scaffold/index.html.erb
file to reflect what we wanted. So, every time the scaffold runs in the future, it will create a customized index file for us instead of the default one provided by Rails.
Rails Guide to Customization
Before we end, I want to introduce one more item. There are many more customizations you can do with generators. I recommend you go through the Creating and Customizing Rails Generators & Templates guide provided by Rails. You will discover all the different things you can do.
For example, you can create a generator completely from scratch. In other words, if you have a set of settings that you want for your application you can dynamically add them. You can also create your own layouts. Rails gives you so much control building your apps. This guide is a highly valuable tool to explore and run through different options to see what you can build with your own customizations.
Even if you won’t be using a lot of the tools found in this Rails guide, it is beneficial to know what choices exist. Simply by going thru this content, you will learn a lot about how rails works and what generators do, which in turn, is going to help your overall Rails knowledge.
See you in the next section.