Scaffolding means simply that a basic scaffold for an application is created with a generator. This scaffold not only contains the model but also a simple web GUI (views) and of course a controller. The programming paradigm used for this is Representational State Transfer (REST) .
You can find a definition of REST at wikipedia.org/wiki/Representational_state_transfer . My short and a bit oversimplified version is this: the inventor Roy Fielding described in 2000 how you can access data with a simple set of rules within the concept of CRUD and the specification of the Hypertext Transfer Protocol (HTTP). CRUD is the abbreviation for Create (SQL: INSERT), Read (SQL: SELECT), Update (SQL: UPDATE), and Delete (SQL: DELETE) . This created URLs that are easy to read for humans and have a certain logic. In this chapter, you will see examples showing the individual paths for the different CRUD functions.
I think the greatest frustration with Rails arises regularly from the fact that many beginners use scaffolding to get quick results without having the proper basic knowledge of Ruby and without knowing what ActiveRecord is. They don’t know what to do next. Fortunately, you have worked your way through Chapters 1–3, so you will be able to understand and use scaffolding straightaway.
Redirects and Flash Messages
Scaffolding uses redirects and flash messages . So, you have to make a little detour first to understand scaffolding.
Redirects
The name says it all, really. Redirects are commands that you can use within the controller to skip (i.e., redirect) to other web pages.
A redirect returns to the browser the response 302 Moved with the new target. So, each redirect does a round-trip to the browser and back.
Let’s create a new Rails project for a suitable example.
Before you can redirect, you need a controller with at least two different methods. Here is a ping-pong example:
The controller app/controllers/game_controller.rb has the content shown in Listing 4-1.
app/controllers/game_controller.rb
Now for the redirect: how can you set it up so you get immediately redirected to the method pong when you go to http://localhost:3000/game/ping? Easy, you say—you just change the route in config/routes.rb. And you are right. So, you don’t necessarily need a redirect. But if you want to process something else in the method ping before redirecting, then this is only possible by using a redirect_to in the controller app/controllers/game_controller.rb, as shown in Listing 4-2.
app/controllers/game_controller.rb
But what is game_pong_path? Let’s take a look at the routes generated for this Rails application:
As you can see, the route to the action ping of the controller GameController now gets the name game_ping (see the beginning of the line). You could also write the redirect like this:
I will explain the details and the individual options of the redirect later in the context of each specific case. For now, you just need to know that you can redirect not just to another method but also to another controller or an entirely different web page.
When you try to go to http://localhost:3000/game/ping, you are automatically redirected to http://localhost:3000/game/pong, and in the log output you see this:
redirect_to :back
If you want to redirect the user of your web application to the page the user was just on, you can use redirect_to : back . This is useful in a scenario where your user first has to log in to get access to a specific page.
Flash Messages
In my eyes, the term flash messages is somewhat misleading. Almost anyone would associate the term flash with more or less colorful web pages that were implemented with the Adobe Shockwave Flash plug-in. But in Ruby on Rails, flash messages are something completely different. They are messages that are displayed on the new page after a redirect, for example (see the section “Redirects”).
Flash messages are good friends with redirects. The two often work together in a team to give the user feedback on an action just carried out. A typical example of a flash message is the system feedback when a user has logged in. Often the user is redirected back to the original page and gets the message “You are now logged in.”
As an example, here again is the ping-pong scenario from the section “Redirects”:
You fill app/controllers/game_controller.rb with the content shown in Listing 4-3.
app/controllers/game_controller.rb
Now you start the Rails web server with rails server and use the browser to go to http://localhost:3000/game/ping. You are redirected from ping to pong. But the flash message “Ping-Pong!” is nowhere to be seen. You first need to expand app/views/layouts/application.html.erb, as shown in Listing 4-4.
app/views/layouts/application.html.erb
If you go to http://localhost:3000/game/pong, you still see the normal Pong page . But if you go to http://localhost:3000/game/ping, you are redirected to the Pong page, and then the flash message is displayed at the top.
If you do not see a flash message that you were expecting, first check in the view to see whether the flash message is output there.
Different Types of Flash Messages
Flash messages are “automagically” passed to the view in a hash. By default, there are three different types : error, warning, and notice. You can also invent your own category and then get it in the view later.
You can set a flash message by writing the hash directly too, as shown here:
Please take a look at the official documentation at http://guides.rubyonrails.org/action_controller_overview.html#the-flash for more information.
Why Are There Flash Messages at All?
You may wonder why there are flash messages in the first place. Couldn’t you just build them yourself if you need them? Yes, indeed. But flash messages have the advantage that they offer a defined approach that is the same for any programmer. So, you don’t need to start from scratch every single time you need one.
Generating a Scaffold
Let’s first use scaffolding to create a list of products for an online shop . First, you need to create a new Rails application.
Let’s look at the scaffolding options.
I’ll keep it short: for the current state of knowledge, you can use rails generate scaffold just like rails generate model. Let’s create the scaffold for the products.
As you can see, rails generate scaffold has already created the model. So, you can directly call rails db:migrate.
Let’s create the first six products in db/seeds.rb.
Populate with the example data.
The Routes
rails generate scaffold has created a route (more on this later in Chapter 5), a controller, and several views for you.
You could also have done all of this manually. Scaffolding is merely an automatism that does the work for you for some basic things. This is assuming that you always want to view, create, and delete records.
Without diving too deeply into the topic of routes, let’s just take a quick look at the available routes for the example. You need to run rails routes.
These are all the routes and consequently URLs available in this Rails application. All routes invoke actions (in other words, methods) in the ProductsController.
The Controller
Now it’s about time you had a look at the file app/controllers/products_controller.rb. The scaffolding automatically creates the methods index, show, new, create, update, and destroy. These methods or actions are called by the routes.
Listing 4-5 shows the content of app/controllers/products_controller.rb.
app/controllers/products_controller.rb
Let’s take a moment and go through this controller.
set_product
An action called before_action calls a private method to set an instance variable called @product for the actions :show, :edit, :update, and :destroy. That DRYs it up nicely.
index
The index method sets the instance variable @products. It contains the result of Product.all.
show
The show method doesn’t do anything. set_product before_action already set the instance variable @ product . So, there is not more to do.
new
The new method creates a new instance of Product and saves it in the instance variable @product.
edit
The edit method doesn’t do anything. The action called set_product before_action already set the instance variable @product. So, there is nothing more to do.
create
The create method uses Product.new to create a new instance of Product and store it in @product. The private method product_params is used to filter the trusted parameters with a white list. When @product is successfully saved, a redirect to the show action is initiated for HTML requests. If a validation error occurs, the new action will be rendered.
update
The update method tries to update @product with product_params. The private method product_params is used to filter the trusted parameters with a white list. When @product is successfully updated, a redirect to the show action is initiated for HTML requests. If a validation error occurs, the edit action will be rendered.
destroy
The destroy method destroys @product and redirects an HTML request to the index action.
The Views
Now you start the Rails web server.
If you click Destroy on the index page, you can delete a record after confirming the message that pops up. Isn’t that cool? Within less than ten minutes, you have written a web application that allows you to create, read/retrieve, update, and delete/destroy records. That is the scaffolding magic . You can save a lot of time.
Where Are the Views?
You can probably guess where the views are, but let’s take a look at the directory app/views/products anyway.
There are two different file extensions. The html.erb file is for HTML requests, and the json.jbuilder file is for JSON requests.
For index, edit, new, and show, the corresponding views are located there. As new and edit both require a form for editing the data, this is stored in the partial _form.html.erb in accordance with the principle of DRY and is integrated into new.html.erb and edit.html.erb with a <%= render 'form' %>.
Let’s open the file app/views/products/index.html.erb, as shown in Listing 4-6.
app/views/products/index.html.erb
You are now an old hand when it comes to ERB , so you’ll be able to read and understand the code without any problems.
link_to
In the views generated by the scaffold generator, you first came across the helper link_to. This creates <a hre ...> links. You can of course also enter a link manually via <a href="..."> in erb, but for links within a Rails project, link_to is more practical because you can use the names of the routes as a target. The code becomes much easier to read. In the previous example, there are the following routes:
The first part of this route is the name of the route . With a new call, this is new_product. A link to new_product looks like the following in the erb code (you can see it at the end of the file app/views/products/index.html.erb):
In the HTML code of the generated page (http://localhost:3000/products), you can see the result.
With link_to you can also link to resources within a RESTful resource. Again, you can find examples for this in app/views/products/index.html.erb. In the table, a show link, an edit link, and a destroy link are rendered for each product.
From the resource and the selected route, Rails automatically determines the required URL and the required HTTP verb (in other words, whether it is a POST, GET, PUT, or DELETE). For index and show calls, you need to observe the difference between singular and plural. link_to 'Show', product links to a single record, and link_to 'Show', products_path links to the index view.
Whether the name of the route is used with or without the suffix _path in link_to depends on whether Rails can “derive” the route from the other specified information. If only one object is specified (in this example, the variable product), then Rails automatically assumes that it is a show route.
ERD Code | Explanation |
link_to 'Show', Product.first | Link to the first product |
link_to 'New Product', new_product_path | Link to the web interface where a new product can be created |
link_to 'Edit', edit_product_path(Product.first) | Link to the form where the first product can be edited |
link_to 'Destroy', Product.first, method: :delete | Link to deleting the first product |
form_for
In the partial used by new and edit, called app/views/products/_form.html.erb, you will find the code shown in Listing 4-7 for the product form.
app/views/products/_form.html.erb
In a block, the helper form_for takes care of creating the HTML form via which the user can enter the data for the record or edit it. If you delete a complete <div class="field"> element here, this can no longer be used for input in the web interface. I am not going to comment on all possible form field variations at this point. The most frequently used ones will appear in examples later and be explained then (if they are not self-explanatory).
You can find an overview of all form helpers at http://guides.rubyonrails.org/form_helpers.html .
When using validations in the model, any validation errors that occur are displayed in the following code at the head of the form:
Let’s add a small validation to the app/models/product.rb model , as shown in Listing 4-8.
app/models/product.rb
Access via JSON
By default, Rails’ scaffolding generates not just access via HTML for human users but also a direct interface for machines. The same methods index, show, new, create, update, and destroy can be called via this interface, but in a format that is easier to read for machines. As an example, you will see the index action via which all data can be read in one go. With the same idea, data can be removed (destroy) or edited (update).
JSON (see http://wikipedia.org/wiki/Json ) seems to be the new cool kid. So, let’s use JSON.
If you do not require machine-readable access to data, you can remove the lines shown in Listing 4-9 from the file Gemfile (followed by the command bundle).
Gemfile
Of course, you can delete the format.json lines manually too. But please don’t forget to delete the JSON view files too.
JSON As Default
Right at the beginning of app/controllers/products_controller.rb you will find the entry for the index action, as shown in Listing 4-10.
app/controllers/products_controller.rb
The code is straightforward. In the instance variable @products, all the products are saved. The view app/views/products/index.json.jbuilder contains the code shown in Listing 4-11 to render the JSON.
app/views/products/index.json.jbuilder
It renders the partial named _product.json.jbuilder, as shown in Listing 4-12.
app/views/products/_product.json.jbuilder
If you do not want the JSON output, you need to delete the json.jbuilder files.
JSON and XML Together
If you ever need a JSON and XML interface in a Rails application, you just need to specify both variants in the controller in the block respond_to. Listing 4-13 shows an example with app/controllers/products_controller.rb in the index action.
app/controllers/products_controller.rb
When Should You Use Scaffolding?
You should never use scaffolding just for the sake of it. There are Rails developers who never use scaffolding and always build everything manually. I find scaffolding quite useful for quickly getting into a new project. But it is always just the beginning.
Example for a Minimal Project
Let’s assume you need a web page quickly with which you can list products and represent them individually. But you do not require an editing or deleting function. In that case, a large part of the code created via scaffolding would be useless and have to be deleted. Let’s try it as follows:
Now create db/seeds.rb with some demo products , as shown in Listing 4-14.
db/seeds.rb
Populate it with this data :
Because you need only index and show, you should delete the views that not required.
The json.jbuilder views are not needed either.
The file app/controllers/products_controller.rb can be simplified with an editor. It should look like Listing 4-15.
app/controllers/products_controller.rb
You only need the routes for index and show . Please open the file config/routes.rb and edit it as shown in Listing 4-16.
config/routes.rb
A rails routes command shows you that really only index and show are routed now.
The same message will be displayed in the log.
The error message states that you call the undefined method edit_product_path in the view app/views/products/index.html.erb. Because you route only index and show now, there are no more edit, destroy, or new methods anymore. So, you need to adapt the file app/views/products/index.html.erb in the editor as shown in Listing 4-17.
app/views/products/index.html.erb
While you are at it, you can also edit app/views/products/show.html.erb accordingly; see Listing 4-18.
app/views/products/show.html.erb
In this example, I am not commenting on the required changes in the tests, as this is not an exercise for test-driven development (TDD) but is meant to demonstrate a way of working with scaffolding. TDD developers will quickly be able to adapt the tests.
Conclusion
Try working with scaffolds one time and without them the next. Then you will soon get a feel for whether they fit into your workflow. I find that scaffolding makes my work much easier for standard applications.