130 Ruby on Rails basics [4d]

Posted Over 8 years ago. Visible to the public.

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 7th edition that works with Rails 7.0.

Work through the following chapters and skip the rest. Also skip deployment to Heroku and upload to AWS S3 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 except 13.4.4 Image upload in production

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 than 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 (3.1.2) is already installed on your PC. You can check with ruby -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.

Please note that the dependencies Hotwire, Turbo and Stimulus are not part of our usual stack. Don't spend too much time trying to understand how they work exactly.

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. Skim the README file of the project if you have not done that already.

Your MovieDB should deliver the following requirements:

  • There should be a full CRUD Show archive.org snapshot interface for movies and actors.
  • Movies have a release 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.
    • Note: This does not imply that the corresponding form fields have to be on the show view, you can also link to a new corresponding form. In each case, add this functionality to a a new controller, not the movies controller.
  • The movie/actor association should include an attribute for the name of the played character
  • 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 7 without Turbolinks. You should already have forked the movie-db-base application in a previous card. Build your MovieDB on top of this fork.

Hint

  • Use a join model Show archive.org snapshot 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.
  • The movie's show view could just render the castings partial. This is sufficient to fulfill the corresponding requirement above (that a user should be able to create a movie/actor association on the movie's show view.)
  • The "release year" field should be stored as an integer column in the database. Don't get confused by the use of text_field below.

Beautiful controller pattern

Refactor your MovieDB controllers to the pattern of our default controller implementation (without caching and pagination).

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 :release_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[release_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 release 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[release_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', 'release_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', 'release_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', 'release_year' => '2007' }

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

@movie.title = 'Sunshine'
@movie.release_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
Last edit
About 2 months ago
Michael Leimstädtner
License
Source code in this card is licensed under the MIT License.
Posted by Henning Koch to makandra Curriculum (2015-07-07 15:06)