-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
274 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
ActiveRecord Example | ||
============= | ||
|
||
This directory contains an exemplary `Movie` class, a migration script and seeds to experiment with ActiveRecord. The migration script and the seeds are also used by the book's Rails introductory lab (also covered in this class). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
# replace 6.1 with your Rails version if you are not | ||
# using the RVM 2.7.2 Ruby bundle on the BIAR VM | ||
class CreateMovies < ActiveRecord::Migration[6.1] | ||
def change | ||
create_table 'movies' do |t| | ||
t.string 'title' | ||
t.string 'rating' | ||
t.text 'description' | ||
t.datetime 'release_date' | ||
# Add fields that let Rails automatically keep track | ||
# of when movies are added or modified: | ||
t.timestamps | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
class Movie < ActiveRecord::Base | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
# Seed the RottenPotatoes DB with some movies. | ||
more_movies = [ | ||
{:title => 'Aladdin', :rating => 'G', | ||
:release_date => '25-Nov-1992'}, | ||
{:title => 'When Harry Met Sally', :rating => 'R', | ||
:release_date => '21-Jul-1989'}, | ||
{:title => 'The Help', :rating => 'PG-13', | ||
:release_date => '10-Aug-2011'}, | ||
{:title => 'Raiders of the Lost Ark', :rating => 'PG', | ||
:release_date => '12-Jun-1981'} | ||
] | ||
|
||
more_movies.each do |movie| | ||
Movie.create(movie) | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
# Part 1: Set up the new app | ||
|
||
## Create a new Rails app | ||
|
||
You may find the Rails [v6 online | ||
documentation ](https://guides.rubyonrails.org/v6.0/) useful during this assignment. | ||
If you are on the **BIAR VM**, make sure tp have followed the steps from the guide | ||
provided by the instructors to set up NodeJS and other dependencies for Rails. | ||
|
||
|
||
Create a new, empty Rails app with the command: | ||
```sh | ||
rails new rottenpotatoes --skip-test-unit --skip-turbolinks --skip-spring | ||
``` | ||
|
||
The options tell Rails to omit three aspects of the new app: | ||
|
||
* Rather than Ruby's `Test::Unit` framework (we will deal with tests in a future assignment). | ||
|
||
* Turbolinks is a piece of trickery that uses AJAX behind-the-scenes to speed | ||
up page loads in your app. However, it causes so many problems with JavaScript | ||
event bindings and scoping that we strongly recommend against using it. A well | ||
tuned Rails app should not benefit much from the type of speedup Turbolinks provides. | ||
|
||
* Spring is a gem that keeps your application "preloaded" making it | ||
faster to restart once stopped. However, this sometimes causes | ||
unexpected effects when you make changes to your app, stop and | ||
restart, and the changes don't seem to take effect. On modern | ||
hardware, the time saved by Spring is minimal, so we recommend against | ||
using it. | ||
|
||
If you're interested, `rails new --help` shows more options available | ||
when creating a new app. | ||
|
||
|
||
If all goes well, you'll see several messages about files being created, | ||
ending with `run bundle install`, which may take a couple of minutes to complete. | ||
You can now `cd` to the | ||
newly-created `rottenpotatoes` directory, called the **app root** | ||
directory for your new app. From now on, unless we say otherwise, all | ||
file names will be relative to the app root. Before going further, | ||
spend a few minutes examining the contents of the new app directory | ||
`rottenpotatoes` to remind yourself with some of | ||
the directories common to all Rails apps. | ||
|
||
What about that message `run bundle install`? | ||
Recall that Ruby libraries are packaged as "gems", and the tool | ||
`bundler` (itself a gem) tracks which versions of which libraries a | ||
particular app depends on. | ||
Open the file called `Gemfile` --it might surprise you that there are | ||
already gem names in this file even though you haven't written any | ||
app code, but that's because Rails itself is a gem and also depends on | ||
several other gems, so the Rails app creation process creates a | ||
default `Gemfile` for you. For example, | ||
you can see that `sqlite3` is listed, because the default | ||
Rails development environment expects to use the SQLite3 database. | ||
|
||
OK, now change into the directory of the app name you just created | ||
(probably `rottenpotatoes`) to continue... | ||
|
||
## Check your work | ||
|
||
To make sure everything works, run the app locally. (It doesn't do | ||
anything yet, but we can verify that it is running and reachable!) | ||
|
||
```sh | ||
cd rottenpotatoes | ||
bin/rails server | ||
``` | ||
Open http://localhost:3000/ on your browser | ||
|
||
When you visit the app's home page, you should see the generic Ruby on Rails landing page, | ||
which is actually being served by your app. Later we will define our own routes | ||
so that the "top level" page does not default to this banner. | ||
|
||
<div align="center"> | ||
<b><a href="Part2.md">Next: Part 2 →</a></b> | ||
</div> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
# Part 2: Create the database and initial migration | ||
You wouldn't want to develop or test your app against the production database, as bugs in your code might accidentally damage valuable customer data. So Rails defines three _environments_ -- `production`, `development`, and `test`---each of which manages its own separate database. These environments, and the means for connecting to the database associated with each, are stored by Rails in `config/database.yml`. | ||
|
||
The `test` database is entirely managed by the testing tools and should never be modified manually: it is wiped clean and repopulated at the beginning of every testing run. | ||
|
||
A brand-new Rails app has no database, so we need to create one. The default `config/database.yml`, which we will use, specifies that the development database will be stored in the file `db/development.sqlite3`. We could use the [SQLite3 command-line tool](http://www.sqlite.org/cli.html) or a SQLite GUI tool to create it manually, but how would we later create the database and table in our production database when we deploy? | ||
|
||
Typing the same commands a second time isn't DRY, and the exact commands might be hard to remember. Further, if the production database is something other than SQLite3 (as is almost certainly the case), the specific commands might be different. And in the future, if we add more tables or make other changes to the database, we'll face the same problem. | ||
|
||
A better alternative is a _migration_---a portable script for changing the database schema (layout of tables and columns) in a consistent and repeatable way, just as Bundler uses the Gemfile to identify and install necessary gems (libraries) in a consistent and repeatable way. Changing the schema using migrations is a four-step process: | ||
|
||
1. Create a migration describing what changes to make. As with `rails new`, Rails provides a migration _generator_ that gives you the boilerplate code, plus various helper methods to describe the migration. | ||
1. Apply the migration to the development database. Rails defines a `rake` task for this. | ||
1. Assuming the migration succeeded, update the test database's schema by running `rake db:test:prepare`. | ||
1. Run your tests, and if all is well, apply the migration to the production database and deploy the new code to production. The process for applying migrations in production depends on the deployment environment. | ||
|
||
We'll use the first 3 steps of this process to add a new table that stores each movie's title, rating, description, and release date. Each migration needs a name, and since this migration will create the movies table, we choose the name CreateMovies. Run the command | ||
```sh | ||
bin/rails generate migration create_movies | ||
``` | ||
and if successful, you will find a new file under `db/migrate` whose name begins with the creation time and date and ends with the name you supplied, for example, `20111201180638_create_movies.rb`. (This naming scheme lets Rails apply migrations in the order they were created, since the file names will sort in date order.) | ||
|
||
Edit this file to make it look like the code below. As you can see, migrations illustrate an idiomatic use of blocks: the `ActiveRecord::Migration#create_table` method takes a block of 1 argument and yields to that block an object representing the table being created. The methods `string`, `datetime`, and so on are provided by this table object, and calling them results in creating columns in the newly-created database table; for example, `t.string 'title'` creates a column named `title` that can hold a string, which for most databases means up to 255 characters. The documentation for the `ActiveRecord::Migration` class (from which all migrations inherit) is part of the [Rails documentation](http://api.rubyonrails.org/), and gives more details and other migration options. | ||
|
||
Paste [this code](../activerecord/initial_migration.rb) into the file, save it, and type | ||
``` | ||
bin/rake db:migrate | ||
``` | ||
to actually apply the migration and create this table. Note that this housekeeping task also stores the migration number itself in the database, and by default it only applies migrations that haven't already been applied. (Type `rake db:migrate` again to verify that it does nothing the second time.) | ||
|
||
## Create the initial model, and seed the database | ||
|
||
Although the database now contains a `movies` table, Rails has no idea that it exists. Since Movies are a model, the next step is therefore to create the ActiveRecord model that uses this table. That is as simple as creating a file `app/models/movie.rb` with these two lines: | ||
|
||
```ruby | ||
class Movie < ActiveRecord::Base | ||
end | ||
``` | ||
|
||
Recall that convention over configuration says that Rails expects the model named `Thing` to be defined in `app/models/thing.rb` and to correspond to a database table `things`. (Yes, Rails does the right thing pluralizing irregular nouns, but to keep things simple, we recommend not using them!) | ||
|
||
You can now verify that `Movie` is defined as a model by running `bin/rails console`, which loads up your entire app into a REPL (read-eval-print loop) environment, and saying `Movie.new`. You will get a brand new instance of `Movie` whose attribute values are all `nil`, and importantly, whose database primary key `id` is `nil` because it hasn't been saved to the database yet. You can confirm the latter by saying `Movie.first`, which tries to fetch the very first movie in the database; you should get back `nil`. | ||
|
||
As a last step before continuing, you can now _seed_ the database with some movies to make the rest of the chapter more interesting. Copy [this code](../activerecord/seeds.rb) into `db/seeds.rb` and once you have a sense of what it does, run `bin/rake db:seed` to run it. Now once again try `Movie.first` and verify that there are movies in the database. | ||
|
||
*Optionally*, using the [ActiveRecord Basics CHIPS](https://github.com/saasbook/hw-activerecord-practice) from the book as inspiration, try a few simple queries on movies from the Rails console. | ||
|
||
## Summary | ||
|
||
* Rails defines three environments---development, production and | ||
test---each with its own copy of the database. | ||
|
||
* A migration is a script describing a specific set of changes to | ||
the database. As apps evolve and add features, migrations are added | ||
to express the database changes required to support those new features. | ||
|
||
* Changing a database using a migration takes three steps: create | ||
the migration, | ||
apply the migration to your development database, and (if | ||
applicable) after testing your code apply the migration to your | ||
production database. | ||
|
||
* The `rails generate migration` | ||
generator fills in the boilerplate for a new | ||
migration, and the `ActiveRecord::Migration` class | ||
contains helpful methods for defining it. | ||
|
||
* `rake db:migrate` applies only those migrations not already applied to the development database. The method for applying migrations to a production database depends on the deployment environment. | ||
|
||
* `rake db:seed` runs the `db/seeds.rb` file, which can optionally contain some initial data to put into the database. | ||
|
||
|
||
<div align="center"> | ||
<b><a href="Part3.md">Next: Part 3 →</a></b> | ||
</div> | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
# Part 3: Create CRUD routes, actions, and views for Movies | ||
Try running the app again, and this time instead of visiting the home page try visiting `/movies`. (That is: `/movies` should be the URI path, the part that immediately follows the hostname and/or port number in the URI.) | ||
|
||
Or, for that matter, visit `/turtle` or `/foobar` or any URI path at all; Rails will complain that your URI doesn't match any route, because we haven’t specified any routes mapping URIs to app methods. Try `rails routes` and verify that it informs us that there are no routes in our brand-new app. (You may want to open multiple Terminal windows or tabs so that the app can keep running while you try other commands.) | ||
|
||
More importantly, use an editor to open the file `log/development.log` and observe that the error message is logged there; this is where you look to find detailed error information when something goes wrong. We'll show other problem-finding and debugging techniques later. | ||
|
||
To fix this error we need to add some routes. Since our initial goal is to store movie information in a database, we can take advantage of a Rails shortcut that creates RESTful routes for the four basic CRUD actions (Create, Read, Update, Delete) on a model. (Recall that RESTful routes specify self-contained requests of what operation to perform and what entity, or resource, to perform it on.) | ||
|
||
Edit `config/routes.rb`, which was auto-generated by the `rails new` command and is heavily commented. Replace the contents of the file with this code (the file is mostly comments, so you’re not actually deleting much). | ||
``` | ||
Rails.application.routes.draw do | ||
resources :movies | ||
root :to => redirect('/movies') | ||
end | ||
``` | ||
|
||
|
||
Save the `routes.rb` file and run `rails routes` again, and observe that because of our change to `routes.rb`, the first line of output says that the URI `GET /movies` will try to call the `index` action of the `MoviesController`; this and most of the other routes in the table are the result of the line resources `:movies`, as we’ll soon see. (As with many Rails methods, `resources 'movies'` would also work, but a symbol usually indicates one of a fixed set of choices rather than an arbitrary string.) The root route `'/'`, RottenPotatoes’ “home page,” will take us to the main Movie listings page by a mechanism we’ll soon see called a `URL redirection`. | ||
|
||
(*Optional:* If you want more practice with how the routes.rb contents get parsed into routes, play around with the [Rails Routing Practice app](https://rails-routing-practice.herokuapp.com/) brought to you by the SaaS book.) | ||
|
||
Using convention over configuration, Rails will expect this controller’s actions to be defined in the class `MoviesController`, and if that class isn’t defined at application start time, Rails will try to load it from the file `app/controllers/movies_controller.rb`. Sure enough, if you now reload the page `https://localhost:3000/movies` in your browser, you should see a different error: `uninitialized constant MoviesController`. This is good news: Rails is essentially complaining that it can’t find the `MoviesController` class, but the fact that it’s even looking for that class tells us that our route is working correctly! As before, this error message and additional information are captured in the log file `log/development.log`. | ||
|
||
To create both the controller file and associated views, we run the following command: | ||
``` | ||
rails generate scaffold_controller Movie title rating description release_date --skip-test | ||
``` | ||
|
||
Notice the command above lists the model name (i.e., `Movie`) followed by the fields in movie records (ie title, rating, description and release date). | ||
|
||
The `rails g` / `rails generate` commands provide a number of useful methods that can auto-generate scaffolding for basic `CRUD+I` operations. You can check the list of all available commands on using `rails g --help`. Now you should have a working application even though the styling does not look too appealing. To make the page more appealing, you need to incorporate CSS. You will see this in later assignments. | ||
|
||
## Summary | ||
|
||
You’ve used the following commands to set up a new Rails app: | ||
|
||
1. `rails new` sets up the new app; the `rails` command also has subcommands to run the app locally (`rails server`) and other management tasks. | ||
|
||
2. Rails and the other gems your app depends on are listed in the app’s `Gemfile`, which Bundler uses to automate the process of creating a consistent environment for your app whether in development or production mode. | ||
|
||
3. You created a database by running an initial migration to create the first table, then adding seed data in `seeds.rb` to populate the first few entries. (In a real production app, you might or might not need seed data; usually it's used for things like creating the initial Admin account so that someone can login.) | ||
|
||
4. To add routes in `config/routes.rb`, the one-line `resources` method provided by the Rails routing system allowed us to set up a group of related routes for `CRUD+Index` actions on a RESTful resource (REST = Representational State Transfer). The log files in the log directory collect error information when something goes wrong. You may have noticed that after changing routes.rb, you didn’t have to stop and restart the app in order for the changes to take effect. In development mode, Rails reloads all of the app’s classes on every new request, so that your changes take effect immediately. In production this would cause serious performance problems, so Rails provides ways to change various app behaviors between development and production mode. | ||
|
||
5. Then, we took advantage of Rails' **scaffolding** to automatically generate (extremely simple) code for a controller and views that actually implement the CRUD+I RESTful actions. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
# Hello Rails! | ||
|
||
**Contents take and adapted from the SaaS book course material.** | ||
*Thanks also to Giovanni Farina for shortening the docs.* | ||
|
||
In this assignment you will create your first Rails app from scratch: | ||
a simple app called RottenPotatoes (inspired by the real web site | ||
RottenTomatoes) for cataloging movies. | ||
|
||
RottenPotatoes lets users interactively (via a Web browser) create | ||
database entries for new movies, view or modify the content of movie | ||
records (movie title, rating, description, and so on), and delete | ||
movie records. | ||
|
||
In later assignments, you'll add features to the app, such as the | ||
ability to filter the list of movies or associate reviews with movies. | ||
|
||
## Learning Goals | ||
|
||
After this assignment, you should know how to: | ||
|
||
1. Create a new Rails app from scratch | ||
|
||
2. Set up a database to store the Rails models, by writing and | ||
executing an initial migration describing the database's schema | ||
|
||
3. Create a basic set of controller actions and views to | ||
support the CRUD actions (create, read, update, delete) on a resource | ||
in a Rails app, and specify HTTP routes that map to the controller actions | ||
|
||
## Lab Parts | ||
|
||
1. [Part 1: Set up to create a new app](Part1.md) | ||
|
||
2. [Part 2: Create the database and initial migration](Part2.md) | ||
|
||
3. [Part 3: Create CRUD routes, actions, and views for Movies](Part3.md) | ||
|