Ruby on Rails basics [4d]

Rails is our web framework.

Goals

  • Be able to write a simple Rails application.
  • Understand how Rails talks to the database (ActiveRecord)
    • What is a model?
    • How are records retrieved and saved?
    • Validations
    • Migrations
    • belongs_to, has_many, has_many :through
  • Gain an understanding of the structure of a basic Rails app
    • Routes
    • Controllers
      • Generate a controller using Rails scaffolding
      • Write your own controller
    • Views
      • using ERB
    • Models
    • Helpers
  • Learn how to do CRUD ("Create", "Read", "Update", "Destroy")
    • How to show a list of existing database records
    • How to build a user interface to show and update database records

Resources

Exercises

Rails tutorial

Find The Ruby on Rails Tutorial in our library. It should be the 6th edition that works with Rails 6.1.

Work through the following chapters and skip the rest. Also skip deployment to Heroku everywhere.

1 From zero to deploy
  Introduction
  1.1 Up and running
  1.2 The first application
2 A toy app
  All sections
3 Mostly static pages
  All sections
5 Filling in the layout
  All sections
6 Modeling users
  All sections
7 Sign up
  Introduction
  7.1 Showing users
  7.2 Signup form
  7.3 Unsuccessful signups
  7.4 Successful signups
  7.6 Conclusion
8 Basic login
  All chapters
13 User microposts
  All chapters

For each chapter:

  • You should do all the programming exercises.
  • You can skip the testing exercises. We are going to learn testing in a later card. We're also using different testing frameworks and different types of tests than the tutorial.

We will also be using a different development environment thatn the book suggests. Deviate from the following instructions:

  • We are managing Ruby versions using rbenv, not RVM. rbenv is already installed on your PC.
  • The Ruby version used by the tutorial (2.7) is already installed on your PC. You can check with ruby -v.
  • Node.js is already installed on your PC. Check with node -v.
  • Yarn is already installed on your PC. Check with yarn -v.
  • We will not use a cloud-based editor like "Cloud9". Instead we will develop on our local machine and use a local IDE like RubyMine. Skip any instructions for cloud IDEs.
  • We will not upload our code to GitHub. Push your code to our private GitLab instance instead.
  • We are developing on Linux. Skip all sections specific to MacOS or Windows.

Movie database

We want to take what learned from the tutorial and apply it to our own app. In this exercise we will write a Rails app that manages a list of movies and a list of actors. We will call this app "MovieDB" in subsequent cards.

Your MovieDB should deliver the following requirements:

  • There should be a full CRUD Archive interface for movies and actors.
  • Movies have a year and name.
  • Actors just have a name.
  • Movies can have multiple actors (and hence an actor can star in multiple movies).
  • The user should be able to create a new movie/actor association from a movie's show view.
  • You only need very basic styling of the UI. We will learn CSS in a later card.
  • Your application layout should have a navigation bar to switch between sections (movies, actors).

For our MovieDB we will use Rails 6.1 without Turbolinks. Create your new application like this:

gem install rails --version="~>6.1"
rails new movie-db --skip-turbolinks

Hint

  • Use a join model Archive like Role or Casting to associate actors with the movies they star in. A movie's show view should link to associated actors. An actor's show view should link to associated movies.
  • We recommend to build the views from scratch, without scaffolding. If you do use scaffolding, you must understand every line of the generated code. Also delete any generated code that is not required for your application.

Beautiful controller pattern

Refactor your MovieDB controllers to the pattern in our book Growing Rails Applications in Practice Archive (chapter "Beautiful Controllers").

The book is in our library.

Understand how forms pass values to your model

In this exercise we take a closer look at how the HTML forms produced by Rails helpers, and how user input in those forms is sent over the wire and saved to your database.

Tip

We are not going to keep changes from this exercise. Before you start, make sure all changes from previous exercises and commited and pushed. We will discard any changes at the end of this exercise.

Take a look at your view to create a new movie, app/views/movies/new.erb. It will look something like this:

<%= form_for @movie do |form| %>
  <%= form.text_field :title %>
  <%= form.text_field :year %>
  <%= form.submit %>
<% end %>  

Your actual view will be longer. For the sake of this example we only look at a minimal subset.

Now go to http://localhost:3000/movies/new to access that view. Right-click into your form and select Inspect. You should see the HTML generated by your view. It will look something like this:

<form action="/movies">
  <input type="text" name="movie[title]">
  <input type="text" name="movie[year]">
  <input type="submit" name="commit" value="Save movie"></button>
</form>

Again your actual HTML will be longer.

Note the peculiar name attributes of the inputs, e.g. movie[title]. This name was chosen automatically by the form.text_field helper, and it will be helpful later.

How form data is transferred and parsed

Now open the Network tab in your developer console (CTRL+Shift+I in Chrome).

Fill out the movie. Use the title Sunshine and the year 2007. Submit the form.

In the network tab you will see the HTTP request to save the movie. Select the request from the list and go to the sub-tab Payload. You should see your request's payload as a list of key/value pairs:

movie[title]    Sunshine
movie[year]     2007
commit          Save movie

Now change your MoviesController#create method so it prints out the params that Rails sees. For this we comment out the code that was creating the movie and render the params object:

def create
  # @movie = Movie.new
  # @movie.attributes = params[:movie]
  # if @movie.save
  #   redirect_to @movie
  # else
  #   render 'new'
  # end
  render plain: params.inspect
end  

Note that #inspect returns a human-readable representation of an object. All Ruby objects implement this.

Submit the new movie form again and you should see something like this:

{ 
  'controller' => 'movies',
  'action_name' => 'create',
  'movie' => { 'title' => 'Sunshine', 'year' => '2007' },
  'commit' => 'Save movie'
}

Note how Rails has used the square brackets in the payload keys to group all movie attributes into one sub-hash, params['movie'].

Let's take a look at only the movie-related attributes:

def create
  # @movie = Movie.new
  # @movie.attributes = params[:movie]
  # if @movie.save
  #   redirect_to @movie
  # else
  #   render 'new'
  # end
  render plain: params[:movie].inspect
end

Note that we can use either params[:movie] (symbol key) or params['movie'] (string key) and get the same value. This is a peculiarity of the params hash, other Ruby hashes don't share this behavior.

Submit the new movie form again and you should see something like this:

{ 'title' => 'Sunshine', 'year' => '2007' }

How parsed params are assigned to a record

Restore the original implementation of MoviesController#create. It probably has a line like this somewhere:

@movie.attributes = params[:movie]

We now know that this expands to the following:

@movie.attributes = { 'title' => 'Sunshine', 'year' => '2007' }

And this is a shortcut to set individiual attributes in separate lines:

@movie.title = 'Sunshine'
@movie.year = '2007'

Hopefully this clarifies how form values are assigned to your model records.

Clean-up

When you're done with this exercises, discard all changes:

git reset --hard
Henning Koch almost 7 years ago
This website uses short-lived cookies to improve usability.
Accept or learn more