Dive deep into the Ruby on Rails tutorial, covering topics such as making the form work by defining the create method, making an edit form, optimizing your code, and more.
This exercise is excerpted from Noble Desktop’s past web development training materials. Noble Desktop now teaches JavaScript and the MERN Stack in our Full Stack Development Certificate. To learn current skills in web development, check out our coding bootcamps in NYC and live online.
Note: These materials are provided to give prospective students a sense of how we structure our class exercises and supplementary materials. During the course, you will get access to the accompanying class files, live instructor demonstrations, and hands-on instruction.
Topics covered in this Ruby on Rails tutorial:
Making the form work: defining a create method, Making an edit form, Optional bonus: DRYing up the code even more
Exercise Preview
Exercise Overview
In the previous exercise we created a form, but it can’t submit the data yet. Something in the controller needs to be ready to receive the data from the form, process it, and create a new movie object based on that information. In Resourceful Routing that method is called Create.
-
If you completed the previous exercises, you can skip the following sidebar. We recommend you finish the previous exercises before starting this one. If you haven’t finished them, do the following sidebar.
If You Did Not Do the Previous Exercises (3A–4A)
- Close any files you may have open.
- Open the Finder and navigate to Class Files > yourname-Rails Class
- Open Terminal.
- Type
cd
and a single space (do NOT press Return yet). - Drag the yourname-Rails Class folder from the Finder to the Terminal window and press ENTER.
- Run
rm -rf flix
to delete your copy of the Flix site. - Run
git clone https://bitbucket.org/noble-desktop/flix.git
to copy the Flix git repository. - Type
cd flix
to enter the new directory. - Type
git checkout 4A
to bring the site up to the end of the previous exercise. - Run
bundle
to install any necessary gems. - Run
yarn install --check-files
to install JavaScript dependencies.
Getting Started
Open the Finder and navigate to Class Files > yourname-Rails Class
Open Terminal.
Type
cd
and a single space (do NOT press Return yet).Drag the flix folder from the Finder to the Terminal window.
Make sure you’re in Terminal and hit Return to change into the new folder.
-
Type the following to launch the server:
rails server
Open a browser and navigate to localhost:3000 just to check that everything is up and running. You should see the Flix website.
We suggest opening the entire flix folder in your code editor if it allows you to (like Sublime Text does).
Making the Form Work: Defining a Create Method
In your code editor open flix > app > controllers > movies_controller.rb
-
Create a new method right before the final
end
in the document as follows:def create end end
Inside this create method we will receive and process what are called the parameters from this form. Rails receives and processes form permissions in a hash called
params
. An important security feature in Rails called strong parameters requires us to white-list those parameters we expect to receive through each form. This feature is a safeguard against the injection of additional parameters which might allow unauthorized users to modify content in an inappropriate way.The upshot of this security method is that controllers are responsible for declaring which parameters they want to receive with a function called
params.permit
followed by a list of expected parameters. -
Add the following bold code:
def create movie_params = params.require(:movie).permit() end
-
Complete the list of expected parameters by adding the following bold code (on a single line):
movie_params
=
params.require(:movie).permit(
:title,
:description,
:has_subtitles,
:placement,
:mpaa_rating,
:release_date,
:ticket_price,
:runtime,
:poster_image
)
Take a moment to double-check your work. That was tedious to type out, but it will provide additional security. Now let’s create the new movie object.
-
After the parameters line (some code omitted to save space), add the following bold code to create the new movie object:
def create movie_params = params.require(:movie).permit(...) @movie = Movie.new(movie_params) end
In order to program the creation of the new movie object from information sent from the form, all we had to do is set instance variable
@movie
equal toMovie.new
and send themovie_params
object directly intoMovie.new
, and we can count on Rails to do the rest for us. -
If we stopped at this point, a new movie would be created, but temporarily—we need to save the information in the database! Add the following bold code to the
create
method:@movie = Movie.new(movie_params) if @movie.save redirect_to @movie else render action: 'new' end end
We want the user to know whether or not their movie was created successfully. This
if/else
statement you just wrote will either send them to the completed movie page or, if the movie creation fails, send them back to thenew
screen with the data they’ve already entered. If it succeeds, it will display a new movie page, but it would be nice to have a message on that page saying the movie was successfully created. Let’s add that. Save the file.
Open flix > app > views > layouts > application.html.erb
-
Around line 52 notice this piece of code:
<% if flash[:alert] %> <div id="flash" class="alert"><%= flash[:alert] %></div> <% elsif flash[:notice] %> <div id="flash" class="notice"><%= flash[:notice] %></div> <% end %>
We have coded this alert system in advance for you to save a little time. Thanks to some CSS classes, the
notice
will display a message with a green background, and thealert
will display a message with a red background. Now we have to tell Rails to put a message into this notice. Switch back to movies_controller.rb
-
Add the following bold code:
if @movie.save redirect_to @movie, notice: 'Movie was created successfully.' else
Save the file.
Switch to the browser and go to localhost:3000/movies/new
Let’s try adding a real movie through the form! We have a text file with the description, etc. typed up to save you some work. Switch to the Finder.
Navigate into Class Files > yourname-Rails Class > flix snippets and double–click on gone_with_the_windows.txt to open it (it will probably open in TextEdit).
Arrange your windows so gone_with_the_windows.txt and the browser window are side-by-side.
Copy and paste all of the information from gone_with_the_windows.txt into the proper form fields. For the multiple-choice fields, set the Placement to In Theaters, MPAA Rating to PG, and Release Date to any future date as indicated in the text file.
-
When done, click Create Movie.
You should be directed to localhost:3000/movies/4 where you can see the new movie page for Gone With the Windows and a green banner at the top of the page alerting you to the successful creation of a new movie.
Making an Edit Form
The form is working great—but what if we need to edit an existing movie? The PR Department is demanding that we make two changes to the Text M for Murder page: the movie is rated R, not PG, and the description is inaccurate too—it’s about an ex-tennis star, not an ex-golfer.
Let’s make an edit form. We can use the existing create form as a starting point.
Switch to movies_controller.rb in your code editor.
-
Add the following
edit
method directly above the finalend
of the document, around line 36:def edit @movie = Movie.find(params[:id]) end end
Save the file.
Open flix > app > views > movies > new.html.erb in your code editor.
Choose File > Save As.
Name the file edit.html.erb and make sure it’s in: flix > app > views > movies (it should be in that folder by default, but it’s always good to double-check).
-
Find the header on the first line of the document:
<h1>Add Movie</h1>
-
Edit the line to read:
<h1>Edit Movie</h1>
We’ll also need to make a corresponding
update
method. (Just as thecreate
method went with the new page, theupdate
method goes with the edit page.) Save the file.
Switch to movies_controller.rb
-
Around line 40 add the following bold code:
def update end end
-
Copy the contents of the
edit
method above and paste the code in theupdate
method as shown below:def update @movie = Movie.find(params[:id]) end
-
Find the following piece of code a little further up in the file (around line 27):
movie_params
=
params.require(:movie).permit(:title,
:description,
:has_subtitles,
:placement,
:mpaa_rating,
:release_date,
:ticket_price,
:runtime,
:poster_image)
Copy the code.
-
Paste the code inside the
update
method:def update @movie = Movie.find(params[:id]) movie_params = params.require(:movie).permit(...) end
-
Add the following bold code to the
update
method:@movie = Movie.find(params[:id]) movie_params = params.require(:movie).permit(...) if @movie.update(movie_params) redirect_to @movie, notice: 'Movie was successfully updated.' else render action: 'edit' end end
Save the file.
In a browser navigate to localhost:3000/movies/1/edit
The edit form should be looking pretty good! In the Description text area, change ex-golfer to ex-tennis star.
Change the MPAA Rating to R. (You may notice that the menu is currently empty, instead of showing the current rating. We’ll fix that in a moment.)
-
Click Update Movie.
You should be redirected to localhost:3000/movies/1 with a green banner at the top of the page: Movie was successfully updated.
So all we had to do was copy and paste the existing form, tweak the heading, and thanks to Rails 4 form helper and the
@movie
instance variable, all the fields were automatically pre-populated for us with the existing movie’s information.
DRYing Up the Code with Partials
Although we got this done this quickly, the end result is kind of sloppy. We have the same form code in two different files, the new and edit pages. As new fields get added to the movies form (and we’ll be adding several!) we’ll have to remember to update both forms identically—no good!
A key principle in Rails is DRY (Don’t Repeat Yourself). Having code routines that do the same thing in multiple places—like we’re doing here with two identical forms—creates a lot of problems, maintenace issues, and bugs. Fortunately, Rails provides tons of helpful tools to keep your code DRY, and we’re going to use one called partials here.
A partial is like a sub-view that can be rendered from inside of other views. It’s not usually called by a controller directly. You can recognize the files easily because partials start with an underscore, like so: _
partial.
Switch to edit.html.erb in your code editor.
Do a File > Save As.
Save the new file as
_
form.html.erb (don’t forget the underscore indicating it’s a partial!) and make sure it’s saved in flix > app > views > movies (it should be there by default).Delete the heading on the first line. (This will need to change depending on whether you are editing or creating a new movie.)
Save the file.
Re-open edit.html.erb (it’s in flix > app > views > movies).
Delete everything except the first line (the heading).
-
Add the following bold code:
<h1>Edit Movie</h1> <%= render 'form' %>
Save the file.
Open new.html.erb (it’s in flix > app > views > movies).
Delete everything except the first line (the heading).
-
Add the following bold code:
<h1>Add Movie</h1> <%= render 'form' %>
Save the file.
Both of these files now point to the same form so we’re reusing the code instead of repeating it. In a browser go to localhost:3000/movies/new and the form for creating movies should still be working and looking the same, even though the form is now being rendered as a partial.
Navigate to localhost:3000/movies/4/edit/ and notice that the form is looking good on that page, too. Our code is DRY and the form is still working; all is good in the world. Except… if you look at the MPAA Rating field on the edit page, it is blank. It’s not remembering the info for this movie because we forgot to tell the
options_for_select
helper to do that. Let’s fix it now.Switch to
_
form.html.erb in your code editor.-
Find the following line of code, around line 24:
<% mpaa_ratings = options_for_select ["G", "PG", "R", "NR"] %>
-
Add the following bold code and don’t miss the comma!
<% mpaa_ratings = options_for_select ["G", "PG", "R", "NR"], selected: @movie.mpaa_rating %>
Save the file.
Switch to the browser, reload localhost:3000/movies/4/edit and check to see that the bug has been fixed!
Back in Terminal hit Ctrl–C to shut down the server.
Optional Bonus: DRYing Up the Code Even More
There’s a little more work we could do to DRY up the controller, because there are a few repeating pieces of code.
Switch to movies_controller.rb in your code editor.
-
Directly before the final
end
at the bottom, add the following bold private method:end end private def movie_params end end
Private methods can only be called from within the controller itself. The methods we are going to put here are non-action (or helper) methods. It’s a best practice in Rails to make non-action methods private, to differentiate them from methods accessible via routes.
-
A few lines up (in the
update
method around line 42) find this line of code:movie_params
=
params.require(:movie).permit(:title,
:description,
:has_subtitles,
:placement,
:mpaa_rating,
:release_date,
:ticket_price,
:runtime,
:poster_image)
Select the code and cut it (Cmd–X).
-
Paste it as follows in the
private movie_params
method:private def movie_params movie_params = params.require(:movie).permit(...) end end
-
Remove the movie_params = part so the complete line looks like this:
private def movie_params params.require(:movie).permit(...) end end
Find the
create
method around line 26 and delete the identical line of code formovie_params
. From now on, whenmovie_params
is invoked, it will be fetched from the private method at the bottom of the file.-
Another reason our code is not terribly DRY is that we’re calling
@movie = Movie.find(params[:id])
several times. That’s unnecessary repetition, so let’s create another private method. Above themovie_params
method create another private method:private def set_movie end def movie_params
-
Find the
edit
method around line 35:def edit @movie = Movie.find(params[:id]) end
-
Cut (Cmd–X) the
@movie = Movie.find(params[:id])
line so it looks like this:def edit end
-
Paste the code into the private
set_movie
method as follows:private def set_movie @movie = Movie.find(params[:id]) end
-
Delete the
@movie = Movie.find(params[:id])
line in the following methods:- The
update
method (around line 39) - The
show
method (around line 13)
- The
-
At the very top of the file add the following just beneath the class definition:
class MoviesController < ApplicationController before_action :set_movie, only: [:show, :edit, :update] def about end
This code we just wrote is going to be called at the beginning of each specified method. We don’t need to explicitly set the movie instance variable in every method, just three of them:
show
,edit
, andupdate
. We can use theonly
parameter and an array of those method names to specify exactly which ones. Save the file.
Switch to Terminal and type the following to launch the server:
rails server
Switch to the browser and reload localhost:3000/movies/4/edit to make sure everything is still working. Click around the Flix site. Everything should be looking good, and our code is much DRYer too!
Back in Terminal hit Ctrl–C to shut down the server.