├── .gitignore ├── Listing_1.1.txt ├── Listing_1.10.rb ├── Listing_1.11.txt ├── Listing_1.12.rb ├── Listing_1.13.rb ├── Listing_1.14.rb ├── Listing_1.15.txt ├── Listing_1.16.txt ├── Listing_1.17.txt ├── Listing_1.18.txt ├── Listing_1.19.txt ├── Listing_1.2.txt ├── Listing_1.20.txt ├── Listing_1.21.txt ├── Listing_1.22.rb ├── Listing_1.23.txt ├── Listing_1.24.txt ├── Listing_1.25.txt ├── Listing_1.3.txt ├── Listing_1.4.txt ├── Listing_1.5.txt ├── Listing_1.6.txt ├── Listing_1.7.rb ├── Listing_1.8.rb ├── Listing_1.9.txt ├── Listing_10.1.rb ├── Listing_10.10.txt ├── Listing_10.11.rb ├── Listing_10.12.rb ├── Listing_10.13.rb ├── Listing_10.14.txt ├── Listing_10.15.rb ├── Listing_10.16.txt ├── Listing_10.17.rb ├── Listing_10.18.txt ├── Listing_10.19.rb ├── Listing_10.2.html.erb ├── Listing_10.20.rb ├── Listing_10.21.rb ├── Listing_10.22.txt ├── Listing_10.23.yaml ├── Listing_10.24.rb ├── Listing_10.25.rb ├── Listing_10.26.txt ├── Listing_10.27.rb ├── Listing_10.28.rb ├── Listing_10.29.rb ├── Listing_10.3.html ├── Listing_10.30.rb ├── Listing_10.31.rb ├── Listing_10.32.rb ├── Listing_10.33.rb ├── Listing_10.34.txt ├── Listing_10.35.rb ├── Listing_10.36.rb ├── Listing_10.37.rb ├── Listing_10.38.html.erb ├── Listing_10.39.rb ├── Listing_10.4.html.erb ├── Listing_10.40.scss ├── Listing_10.41.html.erb ├── Listing_10.42.txt ├── Listing_10.43.rb ├── Listing_10.44.rb ├── Listing_10.45.rb ├── Listing_10.46.html.erb ├── Listing_10.47.rb ├── Listing_10.48.txt ├── Listing_10.49.rb ├── Listing_10.5.html.erb ├── Listing_10.50.txt ├── Listing_10.51.html.erb ├── Listing_10.52.html.erb ├── Listing_10.53.html.erb ├── Listing_10.54.txt ├── Listing_10.55.rb ├── Listing_10.56.rb ├── Listing_10.57.rb ├── Listing_10.58.html.erb ├── Listing_10.59.rb ├── Listing_10.6.html.erb ├── Listing_10.60.rb ├── Listing_10.61.txt ├── Listing_10.62.rb ├── Listing_10.63.rb ├── Listing_10.64.txt ├── Listing_10.7.html.erb ├── Listing_10.8.rb ├── Listing_10.9.rb ├── Listing_11.1.rb ├── Listing_11.10.rb ├── Listing_11.11.rb ├── Listing_11.12.rb ├── Listing_11.13.html.erb ├── Listing_11.14.html.erb ├── Listing_11.15.rb ├── Listing_11.16.rb ├── Listing_11.17.rb ├── Listing_11.18.rb ├── Listing_11.19.rb ├── Listing_11.2.rb ├── Listing_11.20.rb ├── Listing_11.21.rb ├── Listing_11.22.txt ├── Listing_11.23.rb ├── Listing_11.24.rb ├── Listing_11.25.txt ├── Listing_11.26.rb ├── Listing_11.27.txt ├── Listing_11.28.rb ├── Listing_11.29.rb ├── Listing_11.3.rb ├── Listing_11.30.txt ├── Listing_11.31.rb ├── Listing_11.32.rb ├── Listing_11.33.rb ├── Listing_11.34.rb ├── Listing_11.35.txt ├── Listing_11.36.rb ├── Listing_11.37.rb ├── Listing_11.38.rb ├── Listing_11.39.txt ├── Listing_11.4.rb ├── Listing_11.40.rb ├── Listing_11.41.rb ├── Listing_11.42.rb ├── Listing_11.43.yaml ├── Listing_11.44.rb ├── Listing_11.45.rb ├── Listing_11.46.rb ├── Listing_11.5.yaml ├── Listing_11.6.txt ├── Listing_11.7.html.erb ├── Listing_11.8.html.erb ├── Listing_11.9.rb ├── Listing_12.1.rb ├── Listing_12.10.rb ├── Listing_12.11.txt ├── Listing_12.12.rb ├── Listing_12.13.txt ├── Listing_12.14.html.erb ├── Listing_12.15.rb ├── Listing_12.16.rb ├── Listing_12.17.rb ├── Listing_12.18.rb ├── Listing_12.19.rb ├── Listing_12.2.html.erb ├── Listing_12.20.txt ├── Listing_12.21.rb ├── Listing_12.22.rb ├── Listing_12.23.rb ├── Listing_12.24.txt ├── Listing_12.3.html.erb ├── Listing_12.4.html.erb ├── Listing_12.5.rb ├── Listing_12.6.rb ├── Listing_12.7.rb ├── Listing_12.8.html.erb ├── Listing_12.9.html.erb ├── Listing_13.1.txt ├── Listing_13.10.txt ├── Listing_13.11.rb ├── Listing_13.12.rb ├── Listing_13.13.rb ├── Listing_13.14.txt ├── Listing_13.15.rb ├── Listing_13.16.yaml ├── Listing_13.17.txt ├── Listing_13.18.rb ├── Listing_13.19.txt ├── Listing_13.2.rb ├── Listing_13.20.rb ├── Listing_13.21.rb ├── Listing_13.22.txt ├── Listing_13.23.html.erb ├── Listing_13.24.rb ├── Listing_13.25.html.erb ├── Listing_13.26.rb ├── Listing_13.27.scss ├── Listing_13.28.yaml ├── Listing_13.29.rb ├── Listing_13.3.rb ├── Listing_13.30.txt ├── Listing_13.31.rb ├── Listing_13.32.rb ├── Listing_13.33.rb ├── Listing_13.34.rb ├── Listing_13.35.rb ├── Listing_13.36.txt ├── Listing_13.37.rb ├── Listing_13.38.html.erb ├── Listing_13.39.html.erb ├── Listing_13.4.yaml ├── Listing_13.40.html.erb ├── Listing_13.41.rb ├── Listing_13.42.html.erb ├── Listing_13.43.txt ├── Listing_13.44.html.erb ├── Listing_13.45.html.erb ├── Listing_13.46.html.erb ├── Listing_13.47.rb ├── Listing_13.48.rb ├── Listing_13.49.html.erb ├── Listing_13.5.rb ├── Listing_13.50.html.erb ├── Listing_13.51.rb ├── Listing_13.52.rb ├── Listing_13.53.html.erb ├── Listing_13.54.rb ├── Listing_13.55.rb ├── Listing_13.56.yaml ├── Listing_13.57.rb ├── Listing_13.58.rb ├── Listing_13.59.txt ├── Listing_13.6.rb ├── Listing_13.60.rb ├── Listing_13.61.rb ├── Listing_13.62.html.erb ├── Listing_13.63.rb ├── Listing_13.64.html.erb ├── Listing_13.65.txt ├── Listing_13.66.rb ├── Listing_13.67.rb ├── Listing_13.68.rb ├── Listing_13.69.js ├── Listing_13.7.txt ├── Listing_13.70.js ├── Listing_13.71.html.erb ├── Listing_13.72.rb ├── Listing_13.73.rb ├── Listing_13.74.rb ├── Listing_13.75.html.erb ├── Listing_13.76.rb ├── Listing_13.77.yaml ├── Listing_13.78.rb ├── Listing_13.79.rb ├── Listing_13.8.rb ├── Listing_13.9.rb ├── Listing_14.1.rb ├── Listing_14.10.rb ├── Listing_14.11.txt ├── Listing_14.12.rb ├── Listing_14.13.rb ├── Listing_14.14.rb ├── Listing_14.15.rb ├── Listing_14.16.html.erb ├── Listing_14.17.html.erb ├── Listing_14.18.scss ├── Listing_14.19.html.erb ├── Listing_14.2.rb ├── Listing_14.20.rb ├── Listing_14.21.html.erb ├── Listing_14.22.html.erb ├── Listing_14.23.html.erb ├── Listing_14.24.rb ├── Listing_14.25.rb ├── Listing_14.26.html.erb ├── Listing_14.27.txt ├── Listing_14.28.yaml ├── Listing_14.29.rb ├── Listing_14.3.rb ├── Listing_14.30.txt ├── Listing_14.31.rb ├── Listing_14.32.rb ├── Listing_14.33.rb ├── Listing_14.34.rb ├── Listing_14.35.html.erb ├── Listing_14.36.html.erb ├── Listing_14.37.rb ├── Listing_14.38.txt ├── Listing_14.39.rb ├── Listing_14.4.rb ├── Listing_14.40.txt ├── Listing_14.41.rb ├── Listing_14.42.txt ├── Listing_14.43.rb ├── Listing_14.44.rb ├── Listing_14.45.txt ├── Listing_14.46.rb ├── Listing_14.47.txt ├── Listing_14.48.rb ├── Listing_14.49.rb ├── Listing_14.5.rb ├── Listing_14.50.rb ├── Listing_14.51.rb ├── Listing_14.6.yaml ├── Listing_14.7.txt ├── Listing_14.8.rb ├── Listing_14.9.rb ├── Listing_2.1.rb ├── Listing_2.10.rb ├── Listing_2.11.html.erb ├── Listing_2.12.rb ├── Listing_2.13.rb ├── Listing_2.14.rb ├── Listing_2.15.rb ├── Listing_2.16.rb ├── Listing_2.17.rb ├── Listing_2.18.rb ├── Listing_2.19.rb ├── Listing_2.2.rb ├── Listing_2.20.rb ├── Listing_2.21.rb ├── Listing_2.22.rb ├── Listing_2.23.rb ├── Listing_2.24.rb ├── Listing_2.3.rb ├── Listing_2.4.rb ├── Listing_2.5.txt ├── Listing_2.6.rb ├── Listing_2.7.rb ├── Listing_2.8.rb ├── Listing_2.9.rb ├── Listing_3.1.txt ├── Listing_3.10.html.erb ├── Listing_3.11.html.erb ├── Listing_3.12.html ├── Listing_3.13.html ├── Listing_3.14.rb ├── Listing_3.15.txt ├── Listing_3.16.txt ├── Listing_3.17.rb ├── Listing_3.18.txt ├── Listing_3.19.txt ├── Listing_3.2.rb ├── Listing_3.20.rb ├── Listing_3.21.txt ├── Listing_3.22.rb ├── Listing_3.23.html.erb ├── Listing_3.24.txt ├── Listing_3.25.html ├── Listing_3.26.rb ├── Listing_3.27.txt ├── Listing_3.28.html.erb ├── Listing_3.29.html.erb ├── Listing_3.3.txt ├── Listing_3.30.html.erb ├── Listing_3.31.txt ├── Listing_3.32.rb ├── Listing_3.33.html.erb ├── Listing_3.34.txt ├── Listing_3.35.html.erb ├── Listing_3.36.html.erb ├── Listing_3.37.html.erb ├── Listing_3.38.html.erb ├── Listing_3.39.html.erb ├── Listing_3.4.rb ├── Listing_3.40.html.erb ├── Listing_3.41.txt ├── Listing_3.42.html.erb ├── Listing_3.43.rb ├── Listing_3.44.rb ├── Listing_3.45.rb ├── Listing_3.46.rb ├── Listing_3.47.rb ├── Listing_3.5.rb ├── Listing_3.6.rb ├── Listing_3.7.txt ├── Listing_3.8.rb ├── Listing_3.9.rb ├── Listing_4.1.html.erb ├── Listing_4.10.rb ├── Listing_4.11.rb ├── Listing_4.12.rb ├── Listing_4.13.rb ├── Listing_4.14.html.erb ├── Listing_4.15.rb ├── Listing_4.16.rb ├── Listing_4.17.rb ├── Listing_4.2.rb ├── Listing_4.3.html.erb ├── Listing_4.4.rb ├── Listing_4.5.txt ├── Listing_4.6.html.erb ├── Listing_4.7.txt ├── Listing_4.8.rb ├── Listing_4.9.rb ├── Listing_5.1.html.erb ├── Listing_5.10.rb ├── Listing_5.11.css ├── Listing_5.12.html.erb ├── Listing_5.13.html.erb ├── Listing_5.14.html.erb ├── Listing_5.15.html.erb ├── Listing_5.16.html.erb ├── Listing_5.17.scss ├── Listing_5.18.html.erb ├── Listing_5.19.scss ├── Listing_5.2.html.erb ├── Listing_5.20.scss ├── Listing_5.21.rb ├── Listing_5.22.txt ├── Listing_5.23.rb ├── Listing_5.24.rb ├── Listing_5.25.html.erb ├── Listing_5.26.txt ├── Listing_5.27.rb ├── Listing_5.28.rb ├── Listing_5.29.rb ├── Listing_5.3.txt ├── Listing_5.30.html.erb ├── Listing_5.31.html.erb ├── Listing_5.32.rb ├── Listing_5.33.txt ├── Listing_5.34.txt ├── Listing_5.35.rb ├── Listing_5.36.rb ├── Listing_5.37.rb ├── Listing_5.38.txt ├── Listing_5.39.rb ├── Listing_5.4.txt ├── Listing_5.40.html.erb ├── Listing_5.41.rb ├── Listing_5.42.txt ├── Listing_5.43.rb ├── Listing_5.44.rb ├── Listing_5.45.html.erb ├── Listing_5.46.html.erb ├── Listing_5.5.rb ├── Listing_5.6.scss ├── Listing_5.7.scss ├── Listing_5.8.scss ├── Listing_5.9.scss ├── Listing_6.1.txt ├── Listing_6.10.txt ├── Listing_6.11.rb ├── Listing_6.12.rb ├── Listing_6.13.txt ├── Listing_6.14.rb ├── Listing_6.15.txt ├── Listing_6.16.rb ├── Listing_6.17.txt ├── Listing_6.18.rb ├── Listing_6.19.rb ├── Listing_6.2.rb ├── Listing_6.20.txt ├── Listing_6.21.rb ├── Listing_6.22.txt ├── Listing_6.23.rb ├── Listing_6.24.rb ├── Listing_6.25.rb ├── Listing_6.26.rb ├── Listing_6.27.rb ├── Listing_6.28.txt ├── Listing_6.29.rb ├── Listing_6.3.rb ├── Listing_6.30.yaml ├── Listing_6.31.yaml ├── Listing_6.32.rb ├── Listing_6.33.rb ├── Listing_6.34.rb ├── Listing_6.35.rb ├── Listing_6.36.rb ├── Listing_6.37.rb ├── Listing_6.38.rb ├── Listing_6.39.txt ├── Listing_6.4.rb ├── Listing_6.40.rb ├── Listing_6.41.txt ├── Listing_6.42.rb ├── Listing_6.43.rb ├── Listing_6.44.txt ├── Listing_6.5.rb ├── Listing_6.6.txt ├── Listing_6.7.rb ├── Listing_6.8.txt ├── Listing_6.9.rb ├── Listing_7.1.html.erb ├── Listing_7.10.html.erb ├── Listing_7.11.scss ├── Listing_7.12.rb ├── Listing_7.13.rb ├── Listing_7.14.rb ├── Listing_7.15.html.erb ├── Listing_7.16.scss ├── Listing_7.17.html ├── Listing_7.18.rb ├── Listing_7.19.rb ├── Listing_7.2.scss ├── Listing_7.20.html.erb ├── Listing_7.21.html.erb ├── Listing_7.22.scss ├── Listing_7.23.rb ├── Listing_7.24.txt ├── Listing_7.25.rb ├── Listing_7.26.rb ├── Listing_7.27.rb ├── Listing_7.28.rb ├── Listing_7.29.html.erb ├── Listing_7.3.rb ├── Listing_7.30.rb ├── Listing_7.31.rb ├── Listing_7.32.rb ├── Listing_7.33.html.erb ├── Listing_7.34.rb ├── Listing_7.35.rb ├── Listing_7.36.rb ├── Listing_7.37.sh ├── Listing_7.38.yaml ├── Listing_7.4.html.erb ├── Listing_7.5.rb ├── Listing_7.6.rb ├── Listing_7.7.rb ├── Listing_7.8.html.erb ├── Listing_7.9.rb ├── Listing_8.1.txt ├── Listing_8.10.txt ├── Listing_8.11.rb ├── Listing_8.12.txt ├── Listing_8.13.rb ├── Listing_8.14.rb ├── Listing_8.15.rb ├── Listing_8.16.rb ├── Listing_8.17.rb ├── Listing_8.18.rb ├── Listing_8.19.html.erb ├── Listing_8.2.rb ├── Listing_8.20.txt ├── Listing_8.21.js ├── Listing_8.22.html.erb ├── Listing_8.23.scss ├── Listing_8.24.txt ├── Listing_8.25.js ├── Listing_8.26.rb ├── Listing_8.27.js ├── Listing_8.28.html.erb ├── Listing_8.29.html.erb ├── Listing_8.3.rb ├── Listing_8.30.js ├── Listing_8.31.scss ├── Listing_8.32.js ├── Listing_8.33.rb ├── Listing_8.34.yaml ├── Listing_8.35.rb ├── Listing_8.36.txt ├── Listing_8.37.rb ├── Listing_8.38.rb ├── Listing_8.39.rb ├── Listing_8.4.html.erb ├── Listing_8.40.rb ├── Listing_8.41.rb ├── Listing_8.42.rb ├── Listing_8.43.txt ├── Listing_8.44.rb ├── Listing_8.45.rb ├── Listing_8.46.rb ├── Listing_8.47.txt ├── Listing_8.48.rb ├── Listing_8.5.html ├── Listing_8.6.rb ├── Listing_8.7.rb ├── Listing_8.8.rb ├── Listing_8.9.rb ├── Listing_9.1.rb ├── Listing_9.10.txt ├── Listing_9.11.rb ├── Listing_9.12.rb ├── Listing_9.13.txt ├── Listing_9.14.rb ├── Listing_9.15.rb ├── Listing_9.16.txt ├── Listing_9.17.rb ├── Listing_9.18.rb ├── Listing_9.19.txt ├── Listing_9.2.rb ├── Listing_9.20.rb ├── Listing_9.21.txt ├── Listing_9.22.html.erb ├── Listing_9.23.scss ├── Listing_9.24.rb ├── Listing_9.25.rb ├── Listing_9.26.rb ├── Listing_9.27.rb ├── Listing_9.28.txt ├── Listing_9.29.rb ├── Listing_9.3.rb ├── Listing_9.30.rb ├── Listing_9.31.rb ├── Listing_9.32.txt ├── Listing_9.33.rb ├── Listing_9.34.txt ├── Listing_9.35.rb ├── Listing_9.36.txt ├── Listing_9.37.rb ├── Listing_9.38.rb ├── Listing_9.4.rb ├── Listing_9.5.rb ├── Listing_9.6.rb ├── Listing_9.7.rb ├── Listing_9.8.rb ├── Listing_9.9.rb └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /Listing_1.1.txt: -------------------------------------------------------------------------------- 1 | $ echo "gem: --no-document" >> ~/.gemrc 2 | -------------------------------------------------------------------------------- /Listing_1.10.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | . 3 | . 4 | . 5 | # Allow connections to local server on cloud IDE. 6 | config.hosts.clear 7 | end 8 | -------------------------------------------------------------------------------- /Listing_1.11.txt: -------------------------------------------------------------------------------- 1 | $ cd ~/environment/hello_app/ 2 | $ rails server 3 | => Booting Puma 4 | => Ctrl-C to shutdown server 5 | -------------------------------------------------------------------------------- /Listing_1.12.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::Base 2 | 3 | def hello 4 | render html: "hello, world!" 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /Listing_1.13.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | # Define your application routes per the DSL in 3 | # https://guides.rubyonrails.org/routing.html 4 | 5 | # Defines the root path route ("/") 6 | # root "articles#index" 7 | end 8 | -------------------------------------------------------------------------------- /Listing_1.14.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | root "application#hello" 3 | end 4 | -------------------------------------------------------------------------------- /Listing_1.15.txt: -------------------------------------------------------------------------------- 1 | $ source <(curl -sL https://cdn.learnenough.com/upgrade_git) 2 | -------------------------------------------------------------------------------- /Listing_1.16.txt: -------------------------------------------------------------------------------- 1 | $ git config --global user.name "Your Name" 2 | $ git config --global user.email your.email@example.com 3 | -------------------------------------------------------------------------------- /Listing_1.17.txt: -------------------------------------------------------------------------------- 1 | $ git config --global init.defaultBranch main 2 | -------------------------------------------------------------------------------- /Listing_1.18.txt: -------------------------------------------------------------------------------- 1 | $ git config --global alias.co checkout 2 | -------------------------------------------------------------------------------- /Listing_1.19.txt: -------------------------------------------------------------------------------- 1 | $ git config --global credential.helper "cache --timeout=86400" 2 | -------------------------------------------------------------------------------- /Listing_1.2.txt: -------------------------------------------------------------------------------- 1 | $ gem install rails -v 7.0.3 2 | -------------------------------------------------------------------------------- /Listing_1.20.txt: -------------------------------------------------------------------------------- 1 | [website (main)]$ git remote add origin https://github.com//hello_app.git 2 | [website (main)]$ git branch -M main 3 | [website (main)]$ git push -u origin main 4 | -------------------------------------------------------------------------------- /Listing_1.21.txt: -------------------------------------------------------------------------------- 1 | # Ruby on Rails Tutorial 2 | 3 | ## "hello, world!" 4 | 5 | This is the first application for the 6 | [*Ruby on Rails Tutorial*](https://www.railstutorial.org/) 7 | by [Michael Hartl](https://www.michaelhartl.com/). Hello, world! 8 | -------------------------------------------------------------------------------- /Listing_1.22.rb: -------------------------------------------------------------------------------- 1 | 2 | To prepare the system for deployment to production, we first run a special \kode{bundle config} command to prevent the local installation of any production gems (which in this case consists of the \texttt{pg}~gem), as shown in Listing~\ref{code:bundle_without_production}. We've also included a line to add the Linux deployment platform, which is required on some systems. 3 | 4 | \begin{codelisting} 5 | \label{code:bundle_without_production} 6 | \codecaption{Bundling without production gems.} 7 | %= lang:console 8 | \begin{code} 9 | $ bundle _2.3.14_ config set --local without 'production' 10 | $ bundle _2.3.14_ install 11 | $ bundle _2.3.14_ lock --add-platform x86_64-linux 12 | -------------------------------------------------------------------------------- /Listing_1.23.txt: -------------------------------------------------------------------------------- 1 | $ source <(curl -sL https://cdn.learnenough.com/heroku_install) 2 | -------------------------------------------------------------------------------- /Listing_1.24.txt: -------------------------------------------------------------------------------- 1 | $ heroku create 2 | Creating app... done, ⬢ thawing-refuge-35095 3 | https://thawing-refuge-35095.herokuapp.com/ | 4 | https://git.heroku.com/thawing-refuge-35095.git 5 | -------------------------------------------------------------------------------- /Listing_1.25.txt: -------------------------------------------------------------------------------- 1 | $ heroku apps:info 2 | === mysterious-atoll-47182 3 | . 4 | . 5 | . 6 | Web URL: https://mysterious-atoll-47182.herokuapp.com/ 7 | -------------------------------------------------------------------------------- /Listing_1.3.txt: -------------------------------------------------------------------------------- 1 | $ gem install bundler -v 2.3.14 2 | -------------------------------------------------------------------------------- /Listing_1.4.txt: -------------------------------------------------------------------------------- 1 | $ source <(curl -sL https://cdn.learnenough.com/resize) 2 | -------------------------------------------------------------------------------- /Listing_1.5.txt: -------------------------------------------------------------------------------- 1 | # These steps are not needed on the cloud IDE. 2 | $ cd # Change to the home directory. 3 | $ mkdir environment # Make an environment directory. 4 | $ cd environment/ # Change into the environment directory. 5 | -------------------------------------------------------------------------------- /Listing_1.9.txt: -------------------------------------------------------------------------------- 1 | $ cd hello_app/ 2 | $ bundle _2.3.14_ install 3 | Fetching source index for https://rubygems.org/ 4 | . 5 | . 6 | . 7 | -------------------------------------------------------------------------------- /Listing_10.10.txt: -------------------------------------------------------------------------------- 1 | $ rails test 2 | -------------------------------------------------------------------------------- /Listing_10.12.rb: -------------------------------------------------------------------------------- 1 | class UsersController < ApplicationController 2 | . 3 | . 4 | . 5 | def update 6 | @user = User.find(params[:id]) 7 | if @user.update(user_params) 8 | flash[:success] = "Profile updated" 9 | redirect_to @user 10 | else 11 | render 'edit', status: :unprocessable_entity 12 | end 13 | end 14 | . 15 | . 16 | . 17 | end 18 | -------------------------------------------------------------------------------- /Listing_10.13.rb: -------------------------------------------------------------------------------- 1 | class User < ApplicationRecord 2 | attr_accessor :remember_token 3 | before_save { self.email = email.downcase } 4 | validates :name, presence: true, length: { maximum: 50 } 5 | VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i 6 | validates :email, presence: true, length: { maximum: 255 }, 7 | format: { with: VALID_EMAIL_REGEX }, 8 | uniqueness: true 9 | has_secure_password 10 | validates :password, presence: true, length: { minimum: 6 }, allow_nil: true 11 | . 12 | . 13 | . 14 | end 15 | -------------------------------------------------------------------------------- /Listing_10.14.txt: -------------------------------------------------------------------------------- 1 | $ rails test 2 | -------------------------------------------------------------------------------- /Listing_10.15.rb: -------------------------------------------------------------------------------- 1 | class UsersController < ApplicationController 2 | before_action :logged_in_user, only: [:edit, :update] 3 | . 4 | . 5 | . 6 | private 7 | 8 | def user_params 9 | params.require(:user).permit(:name, :email, :password, 10 | :password_confirmation) 11 | end 12 | 13 | # Before filters 14 | 15 | # Confirms a logged-in user. 16 | def logged_in_user 17 | unless logged_in? 18 | flash[:danger] = "Please log in." 19 | redirect_to login_url, status: :see_other 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /Listing_10.16.txt: -------------------------------------------------------------------------------- 1 | $ rails test 2 | -------------------------------------------------------------------------------- /Listing_10.17.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class UsersEditTest < ActionDispatch::IntegrationTest 4 | 5 | def setup 6 | @user = users(:michael) 7 | end 8 | 9 | test "unsuccessful edit" do 10 | log_in_as(@user) 11 | get edit_user_path(@user) 12 | . 13 | . 14 | . 15 | end 16 | 17 | test "successful edit" do 18 | log_in_as(@user) 19 | get edit_user_path(@user) 20 | . 21 | . 22 | . 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /Listing_10.18.txt: -------------------------------------------------------------------------------- 1 | $ rails test 2 | -------------------------------------------------------------------------------- /Listing_10.19.rb: -------------------------------------------------------------------------------- 1 | class UsersController < ApplicationController 2 | # before_action :logged_in_user, only: [:edit, :update] 3 | . 4 | . 5 | . 6 | end 7 | -------------------------------------------------------------------------------- /Listing_10.20.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class UsersControllerTest < ActionDispatch::IntegrationTest 4 | 5 | def setup 6 | @user = users(:michael) 7 | end 8 | . 9 | . 10 | . 11 | test "should redirect edit when not logged in" do 12 | get edit_user_path(@user) 13 | assert_not flash.empty? 14 | assert_redirected_to login_url 15 | end 16 | 17 | test "should redirect update when not logged in" do 18 | patch user_path(@user), params: { user: { name: @user.name, 19 | email: @user.email } } 20 | assert_not flash.empty? 21 | assert_redirected_to login_url 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /Listing_10.21.rb: -------------------------------------------------------------------------------- 1 | class UsersController < ApplicationController 2 | before_action :logged_in_user, only: [:edit, :update] 3 | . 4 | . 5 | . 6 | end 7 | -------------------------------------------------------------------------------- /Listing_10.22.txt: -------------------------------------------------------------------------------- 1 | $ rails test 2 | -------------------------------------------------------------------------------- /Listing_10.23.yaml: -------------------------------------------------------------------------------- 1 | michael: 2 | name: Michael Example 3 | email: michael@example.com 4 | password_digest: <%= User.digest('password') %> 5 | 6 | archer: 7 | name: Sterling Archer 8 | email: duchess@example.gov 9 | password_digest: <%= User.digest('password') %> 10 | -------------------------------------------------------------------------------- /Listing_10.26.txt: -------------------------------------------------------------------------------- 1 | $ rails test 2 | -------------------------------------------------------------------------------- /Listing_10.28.rb: -------------------------------------------------------------------------------- 1 | class User < ApplicationRecord 2 | . 3 | . 4 | . 5 | # Remembers a user in the database for use in persistent sessions. 6 | def remember 7 | self.remember_token = User.new_token 8 | update_attribute(:remember_digest, User.digest(remember_token)) 9 | remember_digest 10 | end 11 | 12 | # Returns a session token to prevent session hijacking. 13 | # We reuse the remember digest for convenience. 14 | def session_token 15 | remember_digest || remember 16 | end 17 | . 18 | . 19 | . 20 | end 21 | -------------------------------------------------------------------------------- /Listing_10.3.html: -------------------------------------------------------------------------------- 1 |
3 | 4 | . 5 | . 6 | . 7 |
8 | -------------------------------------------------------------------------------- /Listing_10.31.rb: -------------------------------------------------------------------------------- 1 | module SessionsHelper 2 | . 3 | . 4 | . 5 | # Stores the URL trying to be accessed. 6 | def store_location 7 | session[:forwarding_url] = request.original_url if request.get? 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /Listing_10.33.rb: -------------------------------------------------------------------------------- 1 | class SessionsController < ApplicationController 2 | . 3 | . 4 | . 5 | def create 6 | user = User.find_by(email: params[:session][:email].downcase) 7 | if user && user.authenticate(params[:session][:password]) 8 | forwarding_url = session[:forwarding_url] 9 | reset_session 10 | params[:session][:remember_me] == '1' ? remember(user) : forget(user) 11 | log_in user 12 | redirect_to forwarding_url || user 13 | else 14 | flash.now[:danger] = 'Invalid email/password combination' 15 | render 'new', status: :unprocessable_entity 16 | end 17 | end 18 | . 19 | . 20 | . 21 | end 22 | -------------------------------------------------------------------------------- /Listing_10.34.txt: -------------------------------------------------------------------------------- 1 | $ rails test 2 | -------------------------------------------------------------------------------- /Listing_10.35.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class UsersControllerTest < ActionDispatch::IntegrationTest 4 | 5 | def setup 6 | @user = users(:michael) 7 | @other_user = users(:archer) 8 | end 9 | 10 | test "should get new" do 11 | get signup_path 12 | assert_response :success 13 | end 14 | 15 | test "should redirect index when not logged in" do 16 | get users_path 17 | assert_redirected_to login_url 18 | end 19 | . 20 | . 21 | . 22 | end 23 | 24 | -------------------------------------------------------------------------------- /Listing_10.36.rb: -------------------------------------------------------------------------------- 1 | class UsersController < ApplicationController 2 | before_action :logged_in_user, only: [:index, :edit, :update] 3 | before_action :correct_user, only: [:edit, :update] 4 | 5 | def index 6 | end 7 | 8 | def show 9 | @user = User.find(params[:id]) 10 | end 11 | . 12 | . 13 | . 14 | end 15 | -------------------------------------------------------------------------------- /Listing_10.37.rb: -------------------------------------------------------------------------------- 1 | class UsersController < ApplicationController 2 | before_action :logged_in_user, only: [:index, :edit, :update] 3 | . 4 | . 5 | . 6 | def index 7 | @users = User.all 8 | end 9 | . 10 | . 11 | . 12 | end 13 | -------------------------------------------------------------------------------- /Listing_10.38.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:title, 'All users') %> 2 |

All users

3 | 4 | 12 | -------------------------------------------------------------------------------- /Listing_10.39.rb: -------------------------------------------------------------------------------- 1 | module UsersHelper 2 | 3 | # Returns the Gravatar for the given user. 4 | def gravatar_for(user, options = { size: 80 }) 5 | size = options[:size] 6 | gravatar_id = Digest::MD5::hexdigest(user.email.downcase) 7 | gravatar_url = "https://secure.gravatar.com/avatar/#{gravatar_id}?s=#{size}" 8 | image_tag(gravatar_url, alt: user.name, class: "gravatar") 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /Listing_10.40.scss: -------------------------------------------------------------------------------- 1 | . 2 | . 3 | . 4 | /* Users index */ 5 | 6 | .users { 7 | list-style: none; 8 | margin: 0; 9 | li { 10 | overflow: auto; 11 | padding: 10px 0; 12 | border-bottom: 1px solid $gray-lighter; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Listing_10.42.txt: -------------------------------------------------------------------------------- 1 | $ rails test 2 | -------------------------------------------------------------------------------- /Listing_10.43.rb: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | git_source(:github) { |repo| "https://github.com/#{repo}.git" } 3 | 4 | ruby "3.1.2" 5 | 6 | gem "rails", "7.0.3" 7 | gem "bcrypt", "3.1.13" 8 | gem "faker", "2.21.0" 9 | gem "bootstrap-sass", "3.4.1" 10 | . 11 | . 12 | . 13 | -------------------------------------------------------------------------------- /Listing_10.44.rb: -------------------------------------------------------------------------------- 1 | # Create a main sample user. 2 | User.create!(name: "Example User", 3 | email: "example@railstutorial.org", 4 | password: "foobar", 5 | password_confirmation: "foobar") 6 | 7 | # Generate a bunch of additional users. 8 | 99.times do |n| 9 | name = Faker::Name.name 10 | email = "example-#{n+1}@railstutorial.org" 11 | password = "password" 12 | User.create!(name: name, 13 | email: email, 14 | password: password, 15 | password_confirmation: password) 16 | end 17 | -------------------------------------------------------------------------------- /Listing_10.45.rb: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | git_source(:github) { |repo| "https://github.com/#{repo}.git" } 3 | 4 | gem "rails", "7.0.3" 5 | gem "bcrypt", "3.1.13" 6 | gem "faker", "2.21.0" 7 | gem "will_paginate", "3.3.1" 8 | gem "bootstrap-will_paginate", "1.0.0" 9 | . 10 | . 11 | . 12 | -------------------------------------------------------------------------------- /Listing_10.46.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:title, 'All users') %> 2 |

All users

3 | 4 | <%= will_paginate %> 5 | 6 | 14 | 15 | <%= will_paginate %> 16 | -------------------------------------------------------------------------------- /Listing_10.47.rb: -------------------------------------------------------------------------------- 1 | class UsersController < ApplicationController 2 | before_action :logged_in_user, only: [:index, :edit, :update] 3 | . 4 | . 5 | . 6 | def index 7 | @users = User.paginate(page: params[:page]) 8 | end 9 | . 10 | . 11 | . 12 | end 13 | -------------------------------------------------------------------------------- /Listing_10.48.txt: -------------------------------------------------------------------------------- 1 | michael: 2 | name: Michael Example 3 | email: michael@example.com 4 | password_digest: <%= User.digest('password') %> 5 | 6 | archer: 7 | name: Sterling Archer 8 | email: duchess@example.gov 9 | password_digest: <%= User.digest('password') %> 10 | 11 | lana: 12 | name: Lana Kane 13 | email: hands@example.gov 14 | password_digest: <%= User.digest('password') %> 15 | 16 | malory: 17 | name: Malory Archer 18 | email: boss@example.gov 19 | password_digest: <%= User.digest('password') %> 20 | 21 | <% 30.times do |n| %> 22 | user_<%= n %>: 23 | name: <%= "User #{n}" %> 24 | email: <%= "user-#{n}@example.com" %> 25 | password_digest: <%= User.digest('password') %> 26 | <% end %> 27 | -------------------------------------------------------------------------------- /Listing_10.49.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class UsersIndexTest < ActionDispatch::IntegrationTest 4 | 5 | def setup 6 | @user = users(:michael) 7 | end 8 | 9 | test "index including pagination" do 10 | log_in_as(@user) 11 | get users_path 12 | assert_template 'users/index' 13 | assert_select 'div.pagination' 14 | User.paginate(page: 1).each do |user| 15 | assert_select 'a[href=?]', user_path(user), text: user.name 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /Listing_10.5.html.erb: -------------------------------------------------------------------------------- 1 | <%= form_with(model: @user) do |f| %> 2 | <%= render 'shared/error_messages', object: @user %> 3 | 4 | <%= f.label :name %> 5 | <%= f.text_field :name, class: 'form-control' %> 6 | 7 | <%= f.label :email %> 8 | <%= f.email_field :email, class: 'form-control' %> 9 | 10 | <%= f.label :password %> 11 | <%= f.password_field :password, class: 'form-control' %> 12 | 13 | <%= f.label :password_confirmation %> 14 | <%= f.password_field :password_confirmation, class: 'form-control' %> 15 | 16 | <%= f.submit yield(:button_text), class: "btn btn-primary" %> 17 | <% end %> 18 | -------------------------------------------------------------------------------- /Listing_10.50.txt: -------------------------------------------------------------------------------- 1 | $ rails test 2 | -------------------------------------------------------------------------------- /Listing_10.51.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:title, 'All users') %> 2 |

All users

3 | 4 | <%= will_paginate %> 5 | 6 | 11 | 12 | <%= will_paginate %> 13 | -------------------------------------------------------------------------------- /Listing_10.52.html.erb: -------------------------------------------------------------------------------- 1 |
  • 2 | <%= gravatar_for user, size: 50 %> 3 | <%= link_to user.name, user %> 4 |
  • 5 | -------------------------------------------------------------------------------- /Listing_10.53.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:title, 'All users') %> 2 |

    All users

    3 | 4 | <%= will_paginate %> 5 | 6 | 9 | 10 | <%= will_paginate %> 11 | -------------------------------------------------------------------------------- /Listing_10.54.txt: -------------------------------------------------------------------------------- 1 | $ rails test 2 | -------------------------------------------------------------------------------- /Listing_10.55.rb: -------------------------------------------------------------------------------- 1 | class AddAdminToUsers < ActiveRecord::Migration[7.0] 2 | def change 3 | add_column :users, :admin, :boolean, default: false 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /Listing_10.56.rb: -------------------------------------------------------------------------------- 1 | # Create a main sample user. 2 | User.create!(name: "Example User", 3 | email: "example@railstutorial.org", 4 | password: "foobar", 5 | password_confirmation: "foobar", 6 | admin: true) 7 | 8 | # Generate a bunch of additional users. 9 | 99.times do |n| 10 | name = Faker::Name.name 11 | email = "example-#{n+1}@railstutorial.org" 12 | password = "password" 13 | User.create!(name: name, 14 | email: email, 15 | password: password, 16 | password_confirmation: password) 17 | end 18 | -------------------------------------------------------------------------------- /Listing_10.58.html.erb: -------------------------------------------------------------------------------- 1 |
  • 2 | <%= gravatar_for user, size: 50 %> 3 | <%= link_to user.name, user %> 4 | <% if current_user.admin? && !current_user?(user) %> 5 | | <%= link_to "delete", user, data: { "turbo-method": :delete, 6 | turbo_confirm: "You sure?" } %> 7 | <% end %> 8 |
  • 9 | -------------------------------------------------------------------------------- /Listing_10.59.rb: -------------------------------------------------------------------------------- 1 | class UsersController < ApplicationController 2 | before_action :logged_in_user, only: [:index, :edit, :update, :destroy] 3 | before_action :correct_user, only: [:edit, :update] 4 | . 5 | . 6 | . 7 | def destroy 8 | User.find(params[:id]).destroy 9 | flash[:success] = "User deleted" 10 | redirect_to users_url, status: :see_other 11 | end 12 | 13 | private 14 | . 15 | . 16 | . 17 | end 18 | -------------------------------------------------------------------------------- /Listing_10.6.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:title, 'Sign up') %> 2 | <% provide(:button_text, 'Create my account') %> 3 |

    Sign up

    4 |
    5 |
    6 | <%= render 'form' %> 7 |
    8 |
    9 | -------------------------------------------------------------------------------- /Listing_10.60.rb: -------------------------------------------------------------------------------- 1 | class UsersController < ApplicationController 2 | before_action :logged_in_user, only: [:index, :edit, :update, :destroy] 3 | before_action :correct_user, only: [:edit, :update] 4 | before_action :admin_user, only: :destroy 5 | . 6 | . 7 | . 8 | private 9 | . 10 | . 11 | . 12 | # Confirms an admin user. 13 | def admin_user 14 | redirect_to(root_url, status: :see_other) unless current_user.admin? 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /Listing_10.61.txt: -------------------------------------------------------------------------------- 1 | michael: 2 | name: Michael Example 3 | email: michael@example.com 4 | password_digest: <%= User.digest('password') %> 5 | admin: true 6 | 7 | archer: 8 | name: Sterling Archer 9 | email: duchess@example.gov 10 | password_digest: <%= User.digest('password') %> 11 | 12 | lana: 13 | name: Lana Kane 14 | email: hands@example.gov 15 | password_digest: <%= User.digest('password') %> 16 | 17 | malory: 18 | name: Malory Archer 19 | email: boss@example.gov 20 | password_digest: <%= User.digest('password') %> 21 | 22 | <% 30.times do |n| %> 23 | user_<%= n %>: 24 | name: <%= "User #{n}" %> 25 | email: <%= "user-#{n}@example.com" %> 26 | password_digest: <%= User.digest('password') %> 27 | <% end %> 28 | -------------------------------------------------------------------------------- /Listing_10.64.txt: -------------------------------------------------------------------------------- 1 | $ rails test 2 | -------------------------------------------------------------------------------- /Listing_10.7.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:title, 'Edit user') %> 2 | <% provide(:button_text, 'Save changes') %> 3 |

    Update your profile

    4 |
    5 |
    6 | <%= render 'form' %> 7 |
    8 | <%= gravatar_for @user %> 9 | Change 10 |
    11 |
    12 |
    13 | -------------------------------------------------------------------------------- /Listing_10.9.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class UsersEditTest < ActionDispatch::IntegrationTest 4 | 5 | def setup 6 | @user = users(:michael) 7 | end 8 | 9 | test "unsuccessful edit" do 10 | get edit_user_path(@user) 11 | assert_template 'users/edit' 12 | patch user_path(@user), params: { user: { name: "", 13 | email: "foo@invalid", 14 | password: "foo", 15 | password_confirmation: "bar" } } 16 | 17 | assert_template 'users/edit' 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /Listing_11.1.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | root "static_pages#home" 3 | get "/help", to: "static_pages#help" 4 | get "/about", to: "static_pages#about" 5 | get "/contact", to: "static_pages#contact" 6 | get "/signup", to: "users#new" 7 | get "/login", to: "sessions#new" 8 | post "/login", to: "sessions#create" 9 | delete "/logout", to: "sessions#destroy" 10 | resources :users 11 | resources :account_activations, only: [:edit] 12 | end 13 | -------------------------------------------------------------------------------- /Listing_11.10.rb: -------------------------------------------------------------------------------- 1 | class UserMailer < ApplicationMailer 2 | 3 | # Subject can be set in your I18n file at config/locales/en.yml 4 | # with the following lookup: 5 | # 6 | # en.user_mailer.account_activation.subject 7 | # 8 | def account_activation 9 | @greeting = "Hi" 10 | 11 | mail to: "to@example.org" 12 | end 13 | 14 | # Subject can be set in your I18n file at config/locales/en.yml 15 | # with the following lookup: 16 | # 17 | # en.user_mailer.password_reset.subject 18 | # 19 | def password_reset 20 | @greeting = "Hi" 21 | 22 | mail to: "to@example.org" 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /Listing_11.11.rb: -------------------------------------------------------------------------------- 1 | class ApplicationMailer < ActionMailer::Base 2 | default from: "user@realdomain.com" 3 | layout "mailer" 4 | end 5 | -------------------------------------------------------------------------------- /Listing_11.12.rb: -------------------------------------------------------------------------------- 1 | class UserMailer < ApplicationMailer 2 | 3 | def account_activation(user) 4 | @user = user 5 | mail to: user.email, subject: "Account activation" 6 | end 7 | 8 | def password_reset 9 | @greeting = "Hi" 10 | 11 | mail to: "to@example.org" 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /Listing_11.13.html.erb: -------------------------------------------------------------------------------- 1 | Hi <%= @user.name %>, 2 | 3 | Welcome to the Sample App! Click on the link below to activate your account: 4 | 5 | <%= edit_account_activation_url(@user.activation_token, email: @user.email) %> 6 | -------------------------------------------------------------------------------- /Listing_11.14.html.erb: -------------------------------------------------------------------------------- 1 |

    Sample App

    2 | 3 |

    Hi <%= @user.name %>,

    4 | 5 |

    6 | Welcome to the Sample App! Click on the link below to activate your account: 7 |

    8 | 9 | <%= link_to "Activate", edit_account_activation_url(@user.activation_token, 10 | email: @user.email) %> 11 | -------------------------------------------------------------------------------- /Listing_11.15.rb: -------------------------------------------------------------------------------- 1 | >> CGI.escape('foo@example.com') 2 | => "foo%40example.com" 3 | -------------------------------------------------------------------------------- /Listing_11.16.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | . 3 | . 4 | . 5 | config.action_mailer.raise_delivery_errors = false 6 | 7 | host = 'example.com' # Don't use this literally; use your local dev host instead 8 | # Use this on the cloud IDE. 9 | config.action_mailer.default_url_options = { host: host, protocol: 'https' } 10 | # Use this if developing on localhost. 11 | # config.action_mailer.default_url_options = { host: host, protocol: 'http' } 12 | . 13 | . 14 | . 15 | end 16 | -------------------------------------------------------------------------------- /Listing_11.17.rb: -------------------------------------------------------------------------------- 1 | # Preview all emails at http://localhost:3000/rails/mailers/user_mailer 2 | class UserMailerPreview < ActionMailer::Preview 3 | 4 | # Preview this email at 5 | # http://localhost:3000/rails/mailers/user_mailer/account_activation 6 | def account_activation 7 | UserMailer.account_activation 8 | end 9 | 10 | # Preview this email at 11 | # http://localhost:3000/rails/mailers/user_mailer/password_reset 12 | def password_reset 13 | UserMailer.password_reset 14 | end 15 | 16 | end 17 | -------------------------------------------------------------------------------- /Listing_11.18.rb: -------------------------------------------------------------------------------- 1 | # Preview all emails at http://localhost:3000/rails/mailers/user_mailer 2 | class UserMailerPreview < ActionMailer::Preview 3 | 4 | # Preview this email at 5 | # http://localhost:3000/rails/mailers/user_mailer/account_activation 6 | def account_activation 7 | user = User.first 8 | user.activation_token = User.new_token 9 | UserMailer.account_activation(user) 10 | end 11 | 12 | # Preview this email at 13 | # http://localhost:3000/rails/mailers/user_mailer/password_reset 14 | def password_reset 15 | UserMailer.password_reset 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /Listing_11.19.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class UserMailerTest < ActionMailer::TestCase 4 | test "account_activation" do 5 | mail = UserMailer.account_activation 6 | assert_equal "Account activation", mail.subject 7 | assert_equal ["to@example.org"], mail.to 8 | assert_equal ["from@example.com"], mail.from 9 | assert_match "Hi", mail.body.encoded 10 | end 11 | 12 | test "password_reset" do 13 | mail = UserMailer.password_reset 14 | assert_equal "Password reset", mail.subject 15 | assert_equal ["to@example.org"], mail.to 16 | assert_equal ["from@example.com"], mail.from 17 | assert_match "Hi", mail.body.encoded 18 | end 19 | 20 | end 21 | -------------------------------------------------------------------------------- /Listing_11.2.rb: -------------------------------------------------------------------------------- 1 | class AddActivationToUsers < ActiveRecord::Migration[7.0] 2 | def change 3 | add_column :users, :activation_digest, :string 4 | add_column :users, :activated, :boolean, default: false 5 | add_column :users, :activated_at, :datetime 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /Listing_11.20.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class UserMailerTest < ActionMailer::TestCase 4 | 5 | test "account_activation" do 6 | user = users(:michael) 7 | user.activation_token = User.new_token 8 | mail = UserMailer.account_activation(user) 9 | assert_equal "Account activation", mail.subject 10 | assert_equal [user.email], mail.to 11 | assert_equal ["user@realdomain.com"], mail.from 12 | assert_match user.name, mail.body.encoded 13 | assert_match user.activation_token, mail.body.encoded 14 | assert_match CGI.escape(user.email), mail.body.encoded 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /Listing_11.21.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | . 3 | . 4 | . 5 | config.action_mailer.delivery_method = :test 6 | config.action_mailer.default_url_options = { host: 'example.com' } 7 | . 8 | . 9 | . 10 | end 11 | -------------------------------------------------------------------------------- /Listing_11.22.txt: -------------------------------------------------------------------------------- 1 | $ rails test:mailers 2 | -------------------------------------------------------------------------------- /Listing_11.23.rb: -------------------------------------------------------------------------------- 1 | class UsersController < ApplicationController 2 | . 3 | . 4 | . 5 | def create 6 | @user = User.new(user_params) 7 | if @user.save 8 | UserMailer.account_activation(@user).deliver_now 9 | flash[:info] = "Please check your email to activate your account." 10 | redirect_to root_url 11 | else 12 | render 'new', status: :unprocessable_entity 13 | end 14 | end 15 | . 16 | . 17 | . 18 | end 19 | -------------------------------------------------------------------------------- /Listing_11.26.rb: -------------------------------------------------------------------------------- 1 | class User < ApplicationRecord 2 | . 3 | . 4 | . 5 | # Returns true if the given token matches the digest. 6 | def authenticated?(attribute, token) 7 | digest = send("#{attribute}_digest") 8 | return false if digest.nil? 9 | BCrypt::Password.new(digest).is_password?(token) 10 | end 11 | . 12 | . 13 | . 14 | end 15 | -------------------------------------------------------------------------------- /Listing_11.27.txt: -------------------------------------------------------------------------------- 1 | $ rails test 2 | -------------------------------------------------------------------------------- /Listing_11.28.rb: -------------------------------------------------------------------------------- 1 | module SessionsHelper 2 | . 3 | . 4 | . 5 | # Returns the current logged-in user (if any). 6 | def current_user 7 | if (user_id = session[:user_id]) 8 | user = User.find_by(id: user_id) 9 | @current_user ||= user if session[:session_token] == user.session_token 10 | elsif (user_id = cookies.encrypted[:user_id]) 11 | user = User.find_by(id: user_id) 12 | if user && user.authenticated?(:remember, cookies[:remember_token]) 13 | log_in user 14 | @current_user = user 15 | end 16 | end 17 | end 18 | . 19 | . 20 | . 21 | end 22 | -------------------------------------------------------------------------------- /Listing_11.29.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class UserTest < ActiveSupport::TestCase 4 | 5 | def setup 6 | @user = User.new(name: "Example User", email: "user@realdomain.com", 7 | password: "foobar", password_confirmation: "foobar") 8 | end 9 | . 10 | . 11 | . 12 | test "authenticated? should return false for a user with nil digest" do 13 | assert_not @user.authenticated?(:remember, '') 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /Listing_11.3.rb: -------------------------------------------------------------------------------- 1 | class User < ApplicationRecord 2 | attr_accessor :remember_token, :activation_token 3 | before_save :downcase_email 4 | before_create :create_activation_digest 5 | validates :name, presence: true, length: { maximum: 50 } 6 | . 7 | . 8 | . 9 | private 10 | 11 | # Converts email to all lowercase. 12 | def downcase_email 13 | self.email = email.downcase 14 | end 15 | 16 | # Creates and assigns the activation token and digest. 17 | def create_activation_digest 18 | self.activation_token = User.new_token 19 | self.activation_digest = User.digest(activation_token) 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /Listing_11.30.txt: -------------------------------------------------------------------------------- 1 | $ rails test 2 | -------------------------------------------------------------------------------- /Listing_11.31.rb: -------------------------------------------------------------------------------- 1 | class AccountActivationsController < ApplicationController 2 | 3 | def edit 4 | user = User.find_by(email: params[:email]) 5 | if user && !user.activated? && user.authenticated?(:activation, params[:id]) 6 | user.update_attribute(:activated, true) 7 | user.update_attribute(:activated_at, Time.zone.now) 8 | log_in user 9 | flash[:success] = "Account activated!" 10 | redirect_to user 11 | else 12 | flash[:danger] = "Invalid activation link" 13 | redirect_to root_url 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /Listing_11.35.txt: -------------------------------------------------------------------------------- 1 | $ rails test 2 | -------------------------------------------------------------------------------- /Listing_11.36.rb: -------------------------------------------------------------------------------- 1 | class User < ApplicationRecord 2 | . 3 | . 4 | . 5 | # Activates an account. 6 | def activate 7 | update_attribute(:activated, true) 8 | update_attribute(:activated_at, Time.zone.now) 9 | end 10 | 11 | # Sends activation email. 12 | def send_activation_email 13 | UserMailer.account_activation(self).deliver_now 14 | end 15 | 16 | private 17 | . 18 | . 19 | . 20 | end 21 | -------------------------------------------------------------------------------- /Listing_11.37.rb: -------------------------------------------------------------------------------- 1 | class UsersController < ApplicationController 2 | . 3 | . 4 | . 5 | def create 6 | @user = User.new(user_params) 7 | if @user.save 8 | @user.send_activation_email 9 | flash[:info] = "Please check your email to activate your account." 10 | redirect_to root_url 11 | else 12 | render 'new', status: :unprocessable_entity 13 | end 14 | end 15 | . 16 | . 17 | . 18 | end 19 | -------------------------------------------------------------------------------- /Listing_11.38.rb: -------------------------------------------------------------------------------- 1 | class AccountActivationsController < ApplicationController 2 | 3 | def edit 4 | user = User.find_by(email: params[:email]) 5 | if user && !user.activated? && user.authenticated?(:activation, params[:id]) 6 | user.activate 7 | log_in user 8 | flash[:success] = "Account activated!" 9 | redirect_to user 10 | else 11 | flash[:danger] = "Invalid activation link" 12 | redirect_to root_url 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /Listing_11.39.txt: -------------------------------------------------------------------------------- 1 | $ rails test 2 | -------------------------------------------------------------------------------- /Listing_11.41.rb: -------------------------------------------------------------------------------- 1 | class UsersController < ApplicationController 2 | . 3 | . 4 | . 5 | def index 6 | @users = User.where(activated: FILL_IN).paginate(page: params[:page]) 7 | end 8 | 9 | def show 10 | @user = User.find(params[:id]) 11 | redirect_to root_url and return unless FILL_IN 12 | end 13 | . 14 | . 15 | . 16 | end 17 | -------------------------------------------------------------------------------- /Listing_11.43.yaml: -------------------------------------------------------------------------------- 1 | michael: 2 | name: Michael Example 3 | email: michael@example.com 4 | password_digest: <%= User.digest('password') %> 5 | admin: true 6 | activated: true 7 | activated_at: <%= Time.zone.now %> 8 | 9 | inactive: 10 | name: Inactive User 11 | email: inactive@example.com 12 | password_digest: <%= User.digest('password') %> 13 | admin: false 14 | activated: false 15 | . 16 | . 17 | . 18 | -------------------------------------------------------------------------------- /Listing_11.44.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class UsersShowTest < ActionDispatch::IntegrationTest 4 | 5 | def setup 6 | @inactive_user = users(:inactive) 7 | @activated_user = users(:archer) 8 | end 9 | 10 | test "should redirect when user not activated" do 11 | get user_path(@inactive_user) 12 | assert_response FILL_IN 13 | assert_redirected_to FILL_IN 14 | end 15 | 16 | test "should display user when activated" do 17 | get user_path(@activated_user) 18 | assert_response FILL_IN 19 | assert_template FILL_IN 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /Listing_11.45.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | . 3 | . 4 | . 5 | config.action_mailer.raise_delivery_errors = true 6 | config.action_mailer.delivery_method = :smtp 7 | host = '.herokuapp.com' 8 | config.action_mailer.default_url_options = { host: host } 9 | ActionMailer::Base.smtp_settings = { 10 | :address => 'smtp.sendgrid.net', 11 | :port => '587', 12 | :authentication => :plain, 13 | :user_name => 'apikey', 14 | :password => ENV['SENDGRID_API_KEY'], 15 | :domain => 'heroku.com', 16 | :enable_starttls_auto => true 17 | } 18 | . 19 | . 20 | . 21 | end 22 | -------------------------------------------------------------------------------- /Listing_11.46.rb: -------------------------------------------------------------------------------- 1 | class ApplicationMailer < ActionMailer::Base 2 | default from: "from@example.com" # <- Make sure it's not this example adddress 3 | layout "mailer" 4 | end 5 | -------------------------------------------------------------------------------- /Listing_11.6.txt: -------------------------------------------------------------------------------- 1 | $ rails generate mailer UserMailer account_activation password_reset 2 | -------------------------------------------------------------------------------- /Listing_11.7.html.erb: -------------------------------------------------------------------------------- 1 | User#account_activation 2 | 3 | <%= @greeting %>, find me in app/views/user_mailer/account_activation.text.erb 4 | -------------------------------------------------------------------------------- /Listing_11.8.html.erb: -------------------------------------------------------------------------------- 1 |

    User#account_activation

    2 | 3 |

    4 | <%= @greeting %>, find me in app/views/user_mailer/account_activation.html.erb 5 |

    6 | -------------------------------------------------------------------------------- /Listing_11.9.rb: -------------------------------------------------------------------------------- 1 | class ApplicationMailer < ActionMailer::Base 2 | default from: "from@example.com" 3 | layout "mailer" 4 | end 5 | -------------------------------------------------------------------------------- /Listing_12.1.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | root "static_pages#home" 3 | get "/help", to: "static_pages#help" 4 | get "/about", to: "static_pages#about" 5 | get "/contact", to: "static_pages#contact" 6 | get "/signup", to: "users#new" 7 | get "/login", to: "sessions#new" 8 | post "/login", to: "sessions#create" 9 | delete "/logout", to: "sessions#destroy" 10 | resources :users 11 | resources :account_activations, only: [:edit] 12 | resources :password_resets, only: [:new, :create, :edit, :update] 13 | end 14 | -------------------------------------------------------------------------------- /Listing_12.10.rb: -------------------------------------------------------------------------------- 1 | # Preview all emails at http://localhost:3000/rails/mailers/user_mailer 2 | class UserMailerPreview < ActionMailer::Preview 3 | 4 | # Preview this email at 5 | # http://localhost:3000/rails/mailers/user_mailer/account_activation 6 | def account_activation 7 | user = User.first 8 | user.activation_token = User.new_token 9 | UserMailer.account_activation(user) 10 | end 11 | 12 | # Preview this email at 13 | # http://localhost:3000/rails/mailers/user_mailer/password_reset 14 | def password_reset 15 | user = User.first 16 | user.reset_token = User.new_token 17 | UserMailer.password_reset(user) 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /Listing_12.13.txt: -------------------------------------------------------------------------------- 1 | $ rails test 2 | -------------------------------------------------------------------------------- /Listing_12.14.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:title, 'Reset password') %> 2 |

    Reset password

    3 | 4 |
    5 |
    6 | <%= form_with(model: @user, url: password_reset_path(params[:id])) do |f| %> 7 | <%= render 'shared/error_messages' %> 8 | 9 | <%= hidden_field_tag :email, @user.email %> 10 | 11 | <%= f.label :password %> 12 | <%= f.password_field :password, class: 'form-control' %> 13 | 14 | <%= f.label :password_confirmation, "Confirmation" %> 15 | <%= f.password_field :password_confirmation, class: 'form-control' %> 16 | 17 | <%= f.submit "Update password", class: "btn btn-primary" %> 18 | <% end %> 19 |
    20 |
    21 | -------------------------------------------------------------------------------- /Listing_12.15.rb: -------------------------------------------------------------------------------- 1 | class PasswordResetsController < ApplicationController 2 | before_action :get_user, only: [:edit, :update] 3 | before_action :valid_user, only: [:edit, :update] 4 | . 5 | . 6 | . 7 | def edit 8 | end 9 | 10 | private 11 | 12 | def get_user 13 | @user = User.find_by(email: params[:email]) 14 | end 15 | 16 | # Confirms a valid user. 17 | def valid_user 18 | unless (@user && @user.activated? && 19 | @user.authenticated?(:reset, params[:id])) 20 | redirect_to root_url 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /Listing_12.17.rb: -------------------------------------------------------------------------------- 1 | class User < ApplicationRecord 2 | . 3 | . 4 | . 5 | # Returns true if a password reset has expired. 6 | def password_reset_expired? 7 | reset_sent_at < 2.hours.ago 8 | end 9 | 10 | private 11 | . 12 | . 13 | . 14 | end 15 | -------------------------------------------------------------------------------- /Listing_12.18.rb: -------------------------------------------------------------------------------- 1 | class PasswordResetsController < ApplicationController 2 | . 3 | . 4 | . 5 | def update 6 | if params[:user][:password].empty? 7 | @user.errors.add(:password, "can't be empty") 8 | render 'edit', status: :unprocessable_entity 9 | elsif @user.update(user_params) 10 | FILL_IN 11 | reset_session 12 | log_in @user 13 | flash[:success] = "Password has been reset." 14 | redirect_to @user 15 | else 16 | render 'edit', status: :unprocessable_entity 17 | end 18 | end 19 | . 20 | . 21 | . 22 | end 23 | 24 | -------------------------------------------------------------------------------- /Listing_12.20.txt: -------------------------------------------------------------------------------- 1 | $ rails test 2 | -------------------------------------------------------------------------------- /Listing_12.23.rb: -------------------------------------------------------------------------------- 1 | class PasswordResetsController < ApplicationController 2 | . 3 | . 4 | . 5 | def update 6 | if params[:user][:password].empty? 7 | @user.errors.add(:password, "can't be empty") 8 | render 'edit', status: :unprocessable_entity 9 | elsif @user.update(user_params) 10 | log_in @user 11 | @user.update_attribute(:reset_digest, nil) 12 | flash[:success] = "Password has been reset." 13 | redirect_to @user 14 | else 15 | render 'edit', status: :unprocessable_entity 16 | end 17 | end 18 | . 19 | . 20 | . 21 | end 22 | -------------------------------------------------------------------------------- /Listing_12.24.txt: -------------------------------------------------------------------------------- 1 | $ rails test 2 | $ git add -A 3 | $ git commit -m "Add password reset" 4 | $ git checkout main 5 | $ git merge password-reset 6 | -------------------------------------------------------------------------------- /Listing_12.4.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:title, "Forgot password") %> 2 |

    Forgot password

    3 | 4 |
    5 |
    6 | <%= form_with(url: password_resets_path, scope: :password_reset) do |f| %> 7 | <%= f.label :email %> 8 | <%= f.email_field :email, class: 'form-control' %> 9 | 10 | <%= f.submit "Submit", class: "btn btn-primary" %> 11 | <% end %> 12 |
    13 |
    14 | -------------------------------------------------------------------------------- /Listing_12.5.rb: -------------------------------------------------------------------------------- 1 | class PasswordResetsController < ApplicationController 2 | 3 | def new 4 | end 5 | 6 | def create 7 | @user = User.find_by(email: params[:password_reset][:email].downcase) 8 | if @user 9 | @user.create_reset_digest 10 | @user.send_password_reset_email 11 | flash[:info] = "Email sent with password reset instructions" 12 | redirect_to root_url 13 | else 14 | flash.now[:danger] = "Email address not found" 15 | render 'new', status: :unprocessable_entity 16 | end 17 | end 18 | 19 | def edit 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /Listing_12.7.rb: -------------------------------------------------------------------------------- 1 | class UserMailer < ApplicationMailer 2 | 3 | def account_activation(user) 4 | @user = user 5 | mail to: user.email, subject: "Account activation" 6 | end 7 | 8 | def password_reset(user) 9 | @user = user 10 | mail to: user.email, subject: "Password reset" 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /Listing_12.8.html.erb: -------------------------------------------------------------------------------- 1 | To reset your password click the link below: 2 | 3 | <%= edit_password_reset_url(@user.reset_token, email: @user.email) %> 4 | 5 | This link will expire in two hours. 6 | 7 | If you did not request your password to be reset, please ignore this email and 8 | your password will stay as it is. 9 | -------------------------------------------------------------------------------- /Listing_12.9.html.erb: -------------------------------------------------------------------------------- 1 |

    Password reset

    2 | 3 |

    To reset your password click the link below:

    4 | 5 | <%= link_to "Reset password", edit_password_reset_url(@user.reset_token, 6 | email: @user.email) %> 7 | 8 |

    This link will expire in two hours.

    9 | 10 |

    11 | If you did not request your password to be reset, please ignore this email and 12 | your password will stay as it is. 13 |

    14 | -------------------------------------------------------------------------------- /Listing_13.1.txt: -------------------------------------------------------------------------------- 1 | $ rails generate model Micropost content:text user:references 2 | -------------------------------------------------------------------------------- /Listing_13.10.txt: -------------------------------------------------------------------------------- 1 | $ rails test 2 | -------------------------------------------------------------------------------- /Listing_13.11.rb: -------------------------------------------------------------------------------- 1 | class Micropost < ApplicationRecord 2 | belongs_to :user 3 | validates :user_id, presence: true 4 | validates :content, presence: true, length: { maximum: 140 } 5 | end 6 | -------------------------------------------------------------------------------- /Listing_13.12.rb: -------------------------------------------------------------------------------- 1 | class User < ApplicationRecord 2 | has_many :microposts 3 | . 4 | . 5 | . 6 | end 7 | -------------------------------------------------------------------------------- /Listing_13.13.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class MicropostTest < ActiveSupport::TestCase 4 | 5 | def setup 6 | @user = users(:michael) 7 | @micropost = @user.microposts.build(content: "Lorem ipsum") 8 | end 9 | 10 | test "should be valid" do 11 | assert @micropost.valid? 12 | end 13 | 14 | test "user id should be present" do 15 | @micropost.user_id = nil 16 | assert_not @micropost.valid? 17 | end 18 | . 19 | . 20 | . 21 | end 22 | -------------------------------------------------------------------------------- /Listing_13.14.txt: -------------------------------------------------------------------------------- 1 | $ rails test 2 | -------------------------------------------------------------------------------- /Listing_13.15.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class MicropostTest < ActiveSupport::TestCase 4 | . 5 | . 6 | . 7 | test "order should be most recent first" do 8 | assert_equal microposts(:most_recent), Micropost.first 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /Listing_13.16.yaml: -------------------------------------------------------------------------------- 1 | orange: 2 | content: "I just ate an orange!" 3 | created_at: <%= 10.minutes.ago %> 4 | user: michael 5 | 6 | tau_manifesto: 7 | content: "Check out the @tauday site by @mhartl: https://tauday.com" 8 | created_at: <%= 3.years.ago %> 9 | user: michael 10 | 11 | cat_video: 12 | content: "Sad cats are sad: https://youtu.be/PKffm2uI4dk" 13 | created_at: <%= 2.hours.ago %> 14 | user: michael 15 | 16 | most_recent: 17 | content: "Writing a short test" 18 | created_at: <%= Time.zone.now %> 19 | user: michael 20 | -------------------------------------------------------------------------------- /Listing_13.17.txt: -------------------------------------------------------------------------------- 1 | $ rails test test/models/micropost_test.rb 2 | -------------------------------------------------------------------------------- /Listing_13.18.rb: -------------------------------------------------------------------------------- 1 | class Micropost < ApplicationRecord 2 | belongs_to :user 3 | default_scope -> { order(created_at: :desc) } 4 | validates :user_id, presence: true 5 | validates :content, presence: true, length: { maximum: 140 } 6 | end 7 | -------------------------------------------------------------------------------- /Listing_13.19.txt: -------------------------------------------------------------------------------- 1 | $ rails test 2 | -------------------------------------------------------------------------------- /Listing_13.2.rb: -------------------------------------------------------------------------------- 1 | class Micropost < ApplicationRecord 2 | belongs_to :user 3 | end 4 | -------------------------------------------------------------------------------- /Listing_13.20.rb: -------------------------------------------------------------------------------- 1 | class User < ApplicationRecord 2 | has_many :microposts, dependent: :destroy 3 | . 4 | . 5 | . 6 | end 7 | -------------------------------------------------------------------------------- /Listing_13.21.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class UserTest < ActiveSupport::TestCase 4 | 5 | def setup 6 | @user = User.new(name: "Example User", email: "user@example.com", 7 | password: "foobar", password_confirmation: "foobar") 8 | end 9 | . 10 | . 11 | . 12 | test "associated microposts should be destroyed" do 13 | @user.save 14 | @user.microposts.create!(content: "Lorem ipsum") 15 | assert_difference 'Micropost.count', -1 do 16 | @user.destroy 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /Listing_13.22.txt: -------------------------------------------------------------------------------- 1 | $ rails test 2 | -------------------------------------------------------------------------------- /Listing_13.23.html.erb: -------------------------------------------------------------------------------- 1 |
  • 2 | <%= link_to gravatar_for(micropost.user, size: 50), micropost.user %> 3 | <%= link_to micropost.user.name, micropost.user %> 4 | <%= micropost.content %> 5 | 6 | Posted <%= time_ago_in_words(micropost.created_at) %> ago. 7 | 8 |
  • 9 | -------------------------------------------------------------------------------- /Listing_13.24.rb: -------------------------------------------------------------------------------- 1 | class UsersController < ApplicationController 2 | . 3 | . 4 | . 5 | def show 6 | @user = User.find(params[:id]) 7 | @microposts = @user.microposts.paginate(page: params[:page]) 8 | end 9 | . 10 | . 11 | . 12 | end 13 | -------------------------------------------------------------------------------- /Listing_13.25.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:title, @user.name) %> 2 |
    3 | 11 |
    12 | <% if @user.microposts.any? %> 13 |

    Microposts (<%= @user.microposts.count %>)

    14 |
      15 | <%= render @microposts %> 16 |
    17 | <%= will_paginate @microposts %> 18 | <% end %> 19 |
    20 |
    21 | -------------------------------------------------------------------------------- /Listing_13.26.rb: -------------------------------------------------------------------------------- 1 | . 2 | . 3 | . 4 | # Generate microposts for a subset of users. 5 | users = User.order(:created_at).take(6) 6 | 50.times do 7 | content = Faker::Lorem.sentence(word_count: 5) 8 | users.each { |user| user.microposts.create!(content: content) } 9 | end 10 | -------------------------------------------------------------------------------- /Listing_13.28.yaml: -------------------------------------------------------------------------------- 1 | orange: 2 | content: "I just ate an orange!" 3 | created_at: <%= 10.minutes.ago %> 4 | user: michael 5 | 6 | tau_manifesto: 7 | content: "Check out the @tauday site by @mhartl: https://tauday.com" 8 | created_at: <%= 3.years.ago %> 9 | user: michael 10 | 11 | cat_video: 12 | content: "Sad cats are sad: https://youtu.be/PKffm2uI4dk" 13 | created_at: <%= 2.hours.ago %> 14 | user: michael 15 | 16 | most_recent: 17 | content: "Writing a short test" 18 | created_at: <%= Time.zone.now %> 19 | user: michael 20 | 21 | <% 30.times do |n| %> 22 | micropost_<%= n %>: 23 | content: <%= Faker::Lorem.sentence(word_count: 5) %> 24 | created_at: <%= 42.days.ago %> 25 | user: michael 26 | <% end %> 27 | -------------------------------------------------------------------------------- /Listing_13.29.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class UsersProfileTest < ActionDispatch::IntegrationTest 4 | include ApplicationHelper 5 | 6 | def setup 7 | @user = users(:michael) 8 | end 9 | 10 | test "profile display" do 11 | get user_path(@user) 12 | assert_template 'users/show' 13 | assert_select 'title', full_title(@user.name) 14 | assert_select 'h1', text: @user.name 15 | assert_select 'h1>img.gravatar' 16 | assert_match @user.microposts.count.to_s, response.body 17 | assert_select 'div.pagination' 18 | @user.microposts.paginate(page: 1).each do |micropost| 19 | assert_match micropost.content, response.body 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /Listing_13.3.rb: -------------------------------------------------------------------------------- 1 | class CreateMicroposts < ActiveRecord::Migration[7.0] 2 | def change 3 | create_table :microposts do |t| 4 | t.text :content 5 | t.references :user, null: false, foreign_key: true 6 | 7 | t.timestamps 8 | end 9 | add_index :microposts, [:user_id, :created_at] 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /Listing_13.30.txt: -------------------------------------------------------------------------------- 1 | $ rails test 2 | -------------------------------------------------------------------------------- /Listing_13.31.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | root "static_pages#home" 3 | get "/help", to: "static_pages#help" 4 | get "/about", to: "static_pages#about" 5 | get "/contact", to: "static_pages#contact" 6 | get "/signup", to: "users#new" 7 | get "/login", to: "sessions#new" 8 | post "/login", to: "sessions#create" 9 | delete "/logout", to: "sessions#destroy" 10 | resources :users 11 | resources :account_activations, only: [:edit] 12 | resources :password_resets, only: [:new, :create, :edit, :update] 13 | resources :microposts, only: [:create, :destroy] 14 | end 15 | -------------------------------------------------------------------------------- /Listing_13.32.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class MicropostsControllerTest < ActionDispatch::IntegrationTest 4 | 5 | def setup 6 | @micropost = microposts(:orange) 7 | end 8 | 9 | test "should redirect create when not logged in" do 10 | assert_no_difference 'Micropost.count' do 11 | post microposts_path, params: { micropost: { content: "Lorem ipsum" } } 12 | end 13 | assert_redirected_to login_url 14 | end 15 | 16 | test "should redirect destroy when not logged in" do 17 | assert_no_difference 'Micropost.count' do 18 | delete micropost_path(@micropost) 19 | end 20 | assert_response :see_other 21 | assert_redirected_to login_url 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /Listing_13.33.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::Base 2 | include SessionsHelper 3 | 4 | private 5 | 6 | # Confirms a logged-in user. 7 | def logged_in_user 8 | unless logged_in? 9 | store_location 10 | flash[:danger] = "Please log in." 11 | redirect_to login_url, status: :see_other 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /Listing_13.34.rb: -------------------------------------------------------------------------------- 1 | class UsersController < ApplicationController 2 | before_action :logged_in_user, only: [:index, :edit, :update, :destroy] 3 | . 4 | . 5 | . 6 | private 7 | 8 | def user_params 9 | params.require(:user).permit(:name, :email, :password, 10 | :password_confirmation) 11 | end 12 | 13 | # Before filters 14 | 15 | # Confirms the correct user. 16 | def correct_user 17 | @user = User.find(params[:id]) 18 | redirect_to(root_url), status: :see_other unless current_user?(@user) 19 | end 20 | 21 | # Confirms an admin user. 22 | def admin_user 23 | redirect_to(root_url), status: :see_other unless current_user.admin? 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /Listing_13.35.rb: -------------------------------------------------------------------------------- 1 | class MicropostsController < ApplicationController 2 | before_action :logged_in_user, only: [:create, :destroy] 3 | 4 | def create 5 | end 6 | 7 | def destroy 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /Listing_13.36.txt: -------------------------------------------------------------------------------- 1 | $ rails test 2 | -------------------------------------------------------------------------------- /Listing_13.37.rb: -------------------------------------------------------------------------------- 1 | class MicropostsController < ApplicationController 2 | before_action :logged_in_user, only: [:create, :destroy] 3 | 4 | def create 5 | @micropost = current_user.microposts.build(micropost_params) 6 | if @micropost.save 7 | flash[:success] = "Micropost created!" 8 | redirect_to root_url 9 | else 10 | render 'static_pages/home', status: :unprocessable_entity 11 | end 12 | end 13 | 14 | def destroy 15 | end 16 | 17 | private 18 | 19 | def micropost_params 20 | params.require(:micropost).permit(:content) 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /Listing_13.39.html.erb: -------------------------------------------------------------------------------- 1 | <%= link_to gravatar_for(current_user, size: 50), current_user %> 2 |

    <%= current_user.name %>

    3 | <%= link_to "view my profile", current_user %> 4 | <%= pluralize(current_user.microposts.count, "micropost") %> 5 | -------------------------------------------------------------------------------- /Listing_13.4.yaml: -------------------------------------------------------------------------------- 1 | # empty 2 | -------------------------------------------------------------------------------- /Listing_13.40.html.erb: -------------------------------------------------------------------------------- 1 | <%= form_with(model: @micropost) do |f| %> 2 | <%= render 'shared/error_messages', object: f.object %> 3 |
    4 | <%= f.text_area :content, placeholder: "Compose new micropost..." %> 5 |
    6 | <%= f.submit "Post", class: "btn btn-primary" %> 7 | <% end %> 8 | -------------------------------------------------------------------------------- /Listing_13.41.rb: -------------------------------------------------------------------------------- 1 | class StaticPagesController < ApplicationController 2 | 3 | def home 4 | @micropost = current_user.microposts.build if logged_in? 5 | end 6 | 7 | def help 8 | end 9 | 10 | def about 11 | end 12 | 13 | def contact 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /Listing_13.42.html.erb: -------------------------------------------------------------------------------- 1 | <% if object.errors.any? %> 2 |
    3 |
    4 | The form contains <%= pluralize(object.errors.count, "error") %>. 5 |
    6 |
      7 | <% object.errors.full_messages.each do |msg| %> 8 |
    • <%= msg %>
    • 9 | <% end %> 10 |
    11 |
    12 | <% end %> 13 | -------------------------------------------------------------------------------- /Listing_13.43.txt: -------------------------------------------------------------------------------- 1 | $ rails test 2 | -------------------------------------------------------------------------------- /Listing_13.46.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:title, 'Reset password') %> 2 |

    Reset password

    3 | 4 |
    5 |
    6 | <%= form_with(model: @user, url: password_reset_path(params[:id])) do |f| %> 7 | <%= render 'shared/error_messages', object: f.object %> 8 | 9 | <%= hidden_field_tag :email, @user.email %> 10 | 11 | <%= f.label :password %> 12 | <%= f.password_field :password, class: 'form-control' %> 13 | 14 | <%= f.label :password_confirmation, "Confirmation" %> 15 | <%= f.password_field :password_confirmation, class: 'form-control' %> 16 | 17 | <%= f.submit "Update password", class: "btn btn-primary" %> 18 | <% end %> 19 |
    20 |
    21 | -------------------------------------------------------------------------------- /Listing_13.47.rb: -------------------------------------------------------------------------------- 1 | class User < ApplicationRecord 2 | . 3 | . 4 | . 5 | # Defines a proto-feed. 6 | # See "Following users" for the full implementation. 7 | def feed 8 | Micropost.where("user_id = ?", id) 9 | end 10 | 11 | private 12 | . 13 | . 14 | . 15 | end 16 | -------------------------------------------------------------------------------- /Listing_13.48.rb: -------------------------------------------------------------------------------- 1 | class StaticPagesController < ApplicationController 2 | 3 | def home 4 | if logged_in? 5 | @micropost = current_user.microposts.build 6 | @feed_items = current_user.feed.paginate(page: params[:page]) 7 | end 8 | end 9 | 10 | def help 11 | end 12 | 13 | def about 14 | end 15 | 16 | def contact 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /Listing_13.49.html.erb: -------------------------------------------------------------------------------- 1 | <% if @feed_items.any? %> 2 |
      3 | <%= render @feed_items %> 4 |
    5 | <%= will_paginate @feed_items %> 6 | <% end %> 7 | -------------------------------------------------------------------------------- /Listing_13.5.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class MicropostTest < ActiveSupport::TestCase 4 | 5 | def setup 6 | @user = users(:michael) 7 | # This code is not idiomatically correct. 8 | @micropost = Micropost.new(content: "Lorem ipsum", user_id: @user.id) 9 | end 10 | 11 | test "should be valid" do 12 | assert @micropost.valid? 13 | end 14 | 15 | test "user id should be present" do 16 | @micropost.user_id = nil 17 | assert_not @micropost.valid? 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /Listing_13.50.html.erb: -------------------------------------------------------------------------------- 1 | <% if logged_in? %> 2 |
    3 | 11 |
    12 |

    Micropost Feed

    13 | <%= render 'shared/feed' %> 14 |
    15 |
    16 | <% else %> 17 | . 18 | . 19 | . 20 | <% end %> 21 | -------------------------------------------------------------------------------- /Listing_13.51.rb: -------------------------------------------------------------------------------- 1 | class MicropostsController < ApplicationController 2 | before_action :logged_in_user, only: [:create, :destroy] 3 | 4 | def create 5 | @micropost = current_user.microposts.build(micropost_params) 6 | if @micropost.save 7 | flash[:success] = "Micropost created!" 8 | redirect_to root_url 9 | else 10 | @feed_items = current_user.feed.paginate(page: params[:page]) 11 | render 'static_pages/home', status: :unprocessable_entity 12 | end 13 | end 14 | 15 | def destroy 16 | end 17 | 18 | private 19 | 20 | def micropost_params 21 | params.require(:micropost).permit(:content) 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /Listing_13.52.rb: -------------------------------------------------------------------------------- 1 | <% if @feed_items.any? %> 2 |
      3 | <%= render @feed_items %> 4 |
    5 | <%= will_paginate @feed_items, 6 | params: { controller: :static_pages, action: :home } %> 7 | <% end %> 8 | -------------------------------------------------------------------------------- /Listing_13.53.html.erb: -------------------------------------------------------------------------------- 1 |
  • 2 | <%= link_to gravatar_for(micropost.user, size: 50), micropost.user %> 3 | <%= link_to micropost.user.name, micropost.user %> 4 | <%= micropost.content %> 5 | 6 | Posted <%= time_ago_in_words(micropost.created_at) %> ago. 7 | <% if current_user?(micropost.user) %> 8 | <%= link_to "delete", micropost, data: { "turbo-method": :delete, 9 | turbo_confirm: "You sure?" } %> 10 | <% end %> 11 | 12 |
  • 13 | -------------------------------------------------------------------------------- /Listing_13.54.rb: -------------------------------------------------------------------------------- 1 | class MicropostsController < ApplicationController 2 | before_action :logged_in_user, only: [:create, :destroy] 3 | before_action :correct_user, only: :destroy 4 | . 5 | . 6 | . 7 | private 8 | 9 | def micropost_params 10 | params.require(:micropost).permit(:content) 11 | end 12 | 13 | def correct_user 14 | @micropost = current_user.microposts.find_by(id: params[:id]) 15 | redirect_to root_url, status: :see_other if @micropost.nil? 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /Listing_13.56.yaml: -------------------------------------------------------------------------------- 1 | . 2 | . 3 | . 4 | ants: 5 | content: "Oh, is that what you want? Because that's how you get ants!" 6 | created_at: <%= 2.years.ago %> 7 | user: archer 8 | 9 | zone: 10 | content: "Danger zone!" 11 | created_at: <%= 3.days.ago %> 12 | user: archer 13 | 14 | tone: 15 | content: "I'm sorry. Your words made sense, but your sarcastic tone did not." 16 | created_at: <%= 10.minutes.ago %> 17 | user: lana 18 | -------------------------------------------------------------------------------- /Listing_13.59.txt: -------------------------------------------------------------------------------- 1 | $ rails test 2 | -------------------------------------------------------------------------------- /Listing_13.6.rb: -------------------------------------------------------------------------------- 1 | class Micropost < ApplicationRecord 2 | belongs_to :user 3 | validates :user_id, presence: true 4 | end 5 | -------------------------------------------------------------------------------- /Listing_13.61.rb: -------------------------------------------------------------------------------- 1 | class Micropost < ApplicationRecord 2 | belongs_to :user 3 | has_one_attached :image 4 | default_scope -> { order(created_at: :desc) } 5 | validates :user_id, presence: true 6 | validates :content, presence: true, length: { maximum: 140 } 7 | end 8 | -------------------------------------------------------------------------------- /Listing_13.62.html.erb: -------------------------------------------------------------------------------- 1 | <%= form_with(model: @micropost) do |f| %> 2 | <%= render 'shared/error_messages', object: f.object %> 3 |
    4 | <%= f.text_area :content, placeholder: "Compose new micropost..." %> 5 |
    6 | <%= f.submit "Post", class: "btn btn-primary" %> 7 | 8 | <%= f.file_field :image %> 9 | 10 | <% end %> 11 | -------------------------------------------------------------------------------- /Listing_13.64.html.erb: -------------------------------------------------------------------------------- 1 |
  • 2 | <%= link_to gravatar_for(micropost.user, size: 50), micropost.user %> 3 | <%= link_to micropost.user.name, micropost.user %> 4 | 5 | <%= micropost.content %> 6 | <%= image_tag micropost.image if micropost.image.attached? %> 7 | 8 | 9 | Posted <%= time_ago_in_words(micropost.created_at) %> ago. 10 | <% if current_user?(micropost.user) %> 11 | <%= link_to "delete", micropost, data: { "turbo-method": :delete, 12 | turbo_confirm: "You sure?" } %> 13 | <% end %> 14 | 15 |
  • 16 | -------------------------------------------------------------------------------- /Listing_13.65.txt: -------------------------------------------------------------------------------- 1 | $ curl -o test/fixtures/files/kitten.jpg \ 2 | > -L https://cdn.learnenough.com/kitten.jpg 3 | -------------------------------------------------------------------------------- /Listing_13.66.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class MicropostsInterface < ActionDispatch::IntegrationTest 4 | 5 | def setup 6 | @user = users(:michael) 7 | log_in_as(@user) 8 | end 9 | end 10 | . 11 | . 12 | . 13 | class ImageUploadTest < MicropostsInterface 14 | 15 | test "should have a file input field for images" do 16 | get root_path 17 | assert_select 'input[type=FILL_IN]' 18 | end 19 | 20 | test "should be able to attach an image" do 21 | cont = "This micropost really ties the room together." 22 | img = fixture_file_upload('kitten.jpg', 'image/jpeg') 23 | post microposts_path, params: { micropost: { content: cont, image: img } } 24 | assert FILL_IN.image.attached? 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /Listing_13.67.rb: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | git_source(:github) { |repo| "https://github.com/#{repo}.git" } 3 | 4 | gem "rails", "7.0.3" 5 | gem "active_storage_validations", "0.9.8" 6 | gem "bcrypt", "3.1.18" 7 | . 8 | . 9 | . 10 | -------------------------------------------------------------------------------- /Listing_13.68.rb: -------------------------------------------------------------------------------- 1 | class Micropost < ApplicationRecord 2 | belongs_to :user 3 | has_one_attached :image 4 | default_scope -> { order(created_at: :desc) } 5 | validates :user_id, presence: true 6 | validates :content, presence: true, length: { maximum: 140 } 7 | validates :image, content_type: { in: %w[image/jpeg image/gif image/png], 8 | message: "must be a valid image format" }, 9 | size: { less_than: 5.megabytes, 10 | message: "should be less than 5MB" } 11 | end 12 | -------------------------------------------------------------------------------- /Listing_13.69.js: -------------------------------------------------------------------------------- 1 | // Prevent uploading of big images. 2 | document.addEventListener("turbo:load", function() { 3 | document.addEventListener("change", function(event) { 4 | let image_upload = document.querySelector('#micropost_image'); 5 | const size_in_megabytes = image_upload.files[0].size/1024/1024; 6 | if (size_in_megabytes > 5) { 7 | alert("Maximum file size is 5MB. Please choose a smaller file."); 8 | image_upload.value = ""; 9 | } 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /Listing_13.7.txt: -------------------------------------------------------------------------------- 1 | $ rails test:models 2 | -------------------------------------------------------------------------------- /Listing_13.70.js: -------------------------------------------------------------------------------- 1 | // Configure your import map in config/importmap.rb. 2 | // Read more: https://github.com/rails/importmap-rails 3 | import "@hotwired/turbo-rails" 4 | import "controllers" 5 | import "custom/menu" 6 | import "custom/image_upload" 7 | -------------------------------------------------------------------------------- /Listing_13.71.html.erb: -------------------------------------------------------------------------------- 1 | <%= form_with(model: @micropost) do |f| %> 2 | <%= render 'shared/error_messages', object: f.object %> 3 |
    4 | <%= f.text_area :content, placeholder: "Compose new micropost..." %> 5 |
    6 | <%= f.submit "Post", class: "btn btn-primary" %> 7 | 8 | <%= f.file_field :image, accept: "image/jpeg,image/gif,image/png" %> 9 | 10 | <% end %> 11 | -------------------------------------------------------------------------------- /Listing_13.72.rb: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | git_source(:github) { |repo| "https://github.com/#{repo}.git" } 3 | 4 | gem "rails", "7.0.3" 5 | gem "image_processing", "1.12.2" 6 | gem "active_storage_validations", "0.9.8" 7 | . 8 | . 9 | . 10 | -------------------------------------------------------------------------------- /Listing_13.73.rb: -------------------------------------------------------------------------------- 1 | require_relative "boot" 2 | require "rails/all" 3 | Bundler.require(*Rails.groups) 4 | 5 | module SampleApp 6 | class Application < Rails::Application 7 | config.load_defaults 7.0 8 | config.active_storage.variant_processor = :mini_magick 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /Listing_13.74.rb: -------------------------------------------------------------------------------- 1 | class Micropost < ApplicationRecord 2 | belongs_to :user 3 | has_one_attached :image do |attachable| 4 | attachable.variant :display, resize_to_limit: [500, 500] 5 | end 6 | default_scope -> { order(created_at: :desc) } 7 | validates :user_id, presence: true 8 | validates :content, presence: true, length: { maximum: 140 } 9 | validates :image, content_type: { in: %w[image/jpeg image/gif image/png], 10 | message: "must be a valid image format" }, 11 | size: { less_than: 5.megabytes, 12 | message: "should be less than 5MB" } 13 | end 14 | -------------------------------------------------------------------------------- /Listing_13.76.rb: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | git_source(:github) { |repo| "https://github.com/#{repo}.git" } 3 | 4 | gem "rails", "7.0.3" 5 | gem "image_processing", "1.12.2" 6 | gem "active_storage_validations", "0.9.8" 7 | . 8 | . 9 | . 10 | group :production do 11 | gem "pg", "1.3.5" 12 | gem "aws-sdk-s3", "1.114.0", require: false 13 | end 14 | . 15 | . 16 | . 17 | -------------------------------------------------------------------------------- /Listing_13.77.yaml: -------------------------------------------------------------------------------- 1 | test: 2 | service: Disk 3 | root: <%= Rails.root.join("tmp/storage") %> 4 | 5 | local: 6 | service: Disk 7 | root: <%= Rails.root.join("storage") %> 8 | 9 | amazon: 10 | service: S3 11 | access_key_id: <%= ENV['AWS_ACCESS_KEY_ID'] %> 12 | secret_access_key: <%= ENV['AWS_SECRET_ACCESS_KEY'] %> 13 | region: <%= ENV['AWS_REGION'] %> 14 | bucket: <%= ENV['AWS_BUCKET'] %> 15 | -------------------------------------------------------------------------------- /Listing_13.78.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | . 3 | . 4 | . 5 | # Store uploaded files on Amazon AWS. 6 | config.active_storage.service = :amazon 7 | . 8 | . 9 | . 10 | end 11 | -------------------------------------------------------------------------------- /Listing_13.9.rb: -------------------------------------------------------------------------------- 1 | class Micropost < ApplicationRecord 2 | belongs_to :user 3 | validates :user_id, presence: true 4 | validates :content, presence: true, length: { maximum: 140 } 5 | end 6 | -------------------------------------------------------------------------------- /Listing_14.1.rb: -------------------------------------------------------------------------------- 1 | class CreateRelationships < ActiveRecord::Migration[7.0] 2 | def change 3 | create_table :relationships do |t| 4 | t.integer :follower_id 5 | t.integer :followed_id 6 | 7 | t.timestamps 8 | end 9 | add_index :relationships, :follower_id 10 | add_index :relationships, :followed_id 11 | add_index :relationships, [:follower_id, :followed_id], unique: true 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /Listing_14.10.rb: -------------------------------------------------------------------------------- 1 | class User < ApplicationRecord 2 | . 3 | . 4 | . 5 | # Follows a user. 6 | def follow(other_user) 7 | following << other_user unless self == other_user 8 | end 9 | 10 | # Unfollows a user. 11 | def unfollow(other_user) 12 | following.delete(other_user) 13 | end 14 | 15 | # Returns true if the current user is following the other user. 16 | def following?(other_user) 17 | following.include?(other_user) 18 | end 19 | 20 | private 21 | . 22 | . 23 | . 24 | end 25 | -------------------------------------------------------------------------------- /Listing_14.11.txt: -------------------------------------------------------------------------------- 1 | $ rails test 2 | -------------------------------------------------------------------------------- /Listing_14.12.rb: -------------------------------------------------------------------------------- 1 | class User < ApplicationRecord 2 | has_many :microposts, dependent: :destroy 3 | has_many :active_relationships, class_name: "Relationship", 4 | foreign_key: "follower_id", 5 | dependent: :destroy 6 | has_many :passive_relationships, class_name: "Relationship", 7 | foreign_key: "followed_id", 8 | dependent: :destroy 9 | has_many :following, through: :active_relationships, source: :followed 10 | has_many :followers, through: :passive_relationships, source: :follower 11 | . 12 | . 13 | . 14 | end 15 | -------------------------------------------------------------------------------- /Listing_14.13.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class UserTest < ActiveSupport::TestCase 4 | . 5 | . 6 | . 7 | test "should follow and unfollow a user" do 8 | michael = users(:michael) 9 | archer = users(:archer) 10 | assert_not michael.following?(archer) 11 | michael.follow(archer) 12 | assert michael.following?(archer) 13 | assert archer.followers.include?(michael) 14 | michael.unfollow(archer) 15 | assert_not michael.following?(archer) 16 | # Users can't follow themselves. 17 | michael.follow(michael) 18 | assert_not michael.following?(michael) 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /Listing_14.15.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | root "static_pages#home" 3 | get "/help", to: "static_pages#help" 4 | get "/about", to: "static_pages#about" 5 | get "/contact", to: "static_pages#contact" 6 | get "/signup", to: "users#new" 7 | get "/login", to: "sessions#new" 8 | post "/login", to: "sessions#create" 9 | delete "/logout", to: "sessions#destroy" 10 | resources :users do 11 | member do 12 | get :following, :followers 13 | end 14 | end 15 | resources :account_activations, only: [:edit] 16 | resources :password_resets, only: [:new, :create, :edit, :update] 17 | resources :microposts, only: [:create, :destroy] 18 | end 19 | -------------------------------------------------------------------------------- /Listing_14.16.html.erb: -------------------------------------------------------------------------------- 1 | <% @user ||= current_user %> 2 | 16 | -------------------------------------------------------------------------------- /Listing_14.17.html.erb: -------------------------------------------------------------------------------- 1 | <% if logged_in? %> 2 |
    3 | 14 |
    15 |

    Micropost Feed

    16 | <%= render 'shared/feed' %> 17 |
    18 |
    19 | <% else %> 20 | . 21 | . 22 | . 23 | <% end %> 24 | -------------------------------------------------------------------------------- /Listing_14.19.html.erb: -------------------------------------------------------------------------------- 1 | <% unless current_user?(@user) %> 2 |
    3 | <% if current_user.following?(@user) %> 4 | <%= render 'unfollow' %> 5 | <% else %> 6 | <%= render 'follow' %> 7 | <% end %> 8 |
    9 | <% end %> 10 | -------------------------------------------------------------------------------- /Listing_14.2.rb: -------------------------------------------------------------------------------- 1 | class User < ApplicationRecord 2 | has_many :microposts, dependent: :destroy 3 | has_many :active_relationships, class_name: "Relationship", 4 | foreign_key: "follower_id", 5 | dependent: :destroy 6 | . 7 | . 8 | . 9 | end 10 | -------------------------------------------------------------------------------- /Listing_14.21.html.erb: -------------------------------------------------------------------------------- 1 | <%= form_with(model: current_user.active_relationships.build) do |f| %> 2 |
    <%= hidden_field_tag :followed_id, @user.id %>
    3 | <%= f.submit "Follow", class: "btn btn-primary" %> 4 | <% end %> 5 | -------------------------------------------------------------------------------- /Listing_14.22.html.erb: -------------------------------------------------------------------------------- 1 | <%= form_with(model: current_user.active_relationships.find_by(followed: @user), 2 | html: { method: :delete }) do |f| %> 3 | <%= f.submit "Unfollow", class: "btn" %> 4 | <% end %> 5 | -------------------------------------------------------------------------------- /Listing_14.23.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:title, @user.name) %> 2 |
    3 | 14 |
    15 | <%= render 'follow_form' if logged_in? %> 16 | <% if @user.microposts.any? %> 17 |

    Microposts (<%= @user.microposts.count %>)

    18 |
      19 | <%= render @microposts %> 20 |
    21 | <%= will_paginate @microposts %> 22 | <% end %> 23 |
    24 |
    25 | -------------------------------------------------------------------------------- /Listing_14.24.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class UsersControllerTest < ActionDispatch::IntegrationTest 4 | 5 | def setup 6 | @user = users(:michael) 7 | @other_user = users(:archer) 8 | end 9 | . 10 | . 11 | . 12 | test "should redirect following when not logged in" do 13 | get following_user_path(@user) 14 | assert_redirected_to login_url 15 | end 16 | 17 | test "should redirect followers when not logged in" do 18 | get followers_user_path(@user) 19 | assert_redirected_to login_url 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /Listing_14.25.rb: -------------------------------------------------------------------------------- 1 | class UsersController < ApplicationController 2 | before_action :logged_in_user, only: [:index, :edit, :update, :destroy, 3 | :following, :followers] 4 | . 5 | . 6 | . 7 | def following 8 | @title = "Following" 9 | @user = User.find(params[:id]) 10 | @users = @user.following.paginate(page: params[:page]) 11 | render 'show_follow', status: :unprocessable_entity 12 | end 13 | 14 | def followers 15 | @title = "Followers" 16 | @user = User.find(params[:id]) 17 | @users = @user.followers.paginate(page: params[:page]) 18 | render 'show_follow', status: :unprocessable_entity 19 | end 20 | 21 | private 22 | . 23 | . 24 | . 25 | end 26 | -------------------------------------------------------------------------------- /Listing_14.27.txt: -------------------------------------------------------------------------------- 1 | $ rails test 2 | -------------------------------------------------------------------------------- /Listing_14.28.yaml: -------------------------------------------------------------------------------- 1 | one: 2 | follower: michael 3 | followed: lana 4 | 5 | two: 6 | follower: michael 7 | followed: malory 8 | 9 | three: 10 | follower: lana 11 | followed: michael 12 | 13 | four: 14 | follower: archer 15 | followed: michael 16 | -------------------------------------------------------------------------------- /Listing_14.3.rb: -------------------------------------------------------------------------------- 1 | class Relationship < ApplicationRecord 2 | belongs_to :follower, class_name: "User" 3 | belongs_to :followed, class_name: "User" 4 | end 5 | -------------------------------------------------------------------------------- /Listing_14.30.txt: -------------------------------------------------------------------------------- 1 | $ rails test 2 | -------------------------------------------------------------------------------- /Listing_14.31.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class RelationshipsControllerTest < ActionDispatch::IntegrationTest 4 | 5 | test "create should require logged-in user" do 6 | assert_no_difference 'Relationship.count' do 7 | post relationships_path 8 | end 9 | assert_redirected_to login_url 10 | end 11 | 12 | test "destroy should require logged-in user" do 13 | assert_no_difference 'Relationship.count' do 14 | delete relationship_path(relationships(:one)) 15 | end 16 | assert_redirected_to login_url 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /Listing_14.32.rb: -------------------------------------------------------------------------------- 1 | class RelationshipsController < ApplicationController 2 | before_action :logged_in_user 3 | 4 | def create 5 | end 6 | 7 | def destroy 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /Listing_14.33.rb: -------------------------------------------------------------------------------- 1 | class RelationshipsController < ApplicationController 2 | before_action :logged_in_user 3 | 4 | def create 5 | user = User.find(params[:followed_id]) 6 | current_user.follow(user) 7 | redirect_to user 8 | end 9 | 10 | def destroy 11 | user = Relationship.find(params[:id]).followed 12 | current_user.unfollow(user) 13 | redirect_to user, status: :see_other 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /Listing_14.34.rb: -------------------------------------------------------------------------------- 1 | class RelationshipsController < ApplicationController 2 | before_action :logged_in_user 3 | 4 | def create 5 | @user = User.find(params[:followed_id]) 6 | current_user.follow(@user) 7 | respond_to do |format| 8 | format.html { redirect_to @user } 9 | format.turbo_stream 10 | end 11 | end 12 | 13 | def destroy 14 | @user = Relationship.find(params[:id]).followed 15 | current_user.unfollow(@user) 16 | respond_to do |format| 17 | format.html { redirect_to @user, status: :see_other } 18 | format.turbo_stream 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /Listing_14.35.html.erb: -------------------------------------------------------------------------------- 1 | <%= turbo_stream.update "follow_form" do %> 2 | <%= render partial: "users/unfollow" %> 3 | <% end %> 4 | <%= turbo_stream.update "followers" do %> 5 | <%= @user.followers.count %> 6 | <% end %> 7 | -------------------------------------------------------------------------------- /Listing_14.36.html.erb: -------------------------------------------------------------------------------- 1 | <%= turbo_stream.update "follow_form" do %> 2 | <%= render partial: "users/follow" %> 3 | <% end %> 4 | <%= turbo_stream.update "followers" do %> 5 | <%= @user.followers.count %> 6 | <% end %> 7 | -------------------------------------------------------------------------------- /Listing_14.38.txt: -------------------------------------------------------------------------------- 1 | $ rails test 2 | -------------------------------------------------------------------------------- /Listing_14.4.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class RelationshipTest < ActiveSupport::TestCase 4 | 5 | def setup 6 | @relationship = Relationship.new(follower_id: users(:michael).id, 7 | followed_id: users(:archer).id) 8 | end 9 | 10 | test "should be valid" do 11 | assert @relationship.valid? 12 | end 13 | 14 | test "should require a follower_id" do 15 | @relationship.follower_id = nil 16 | assert_not @relationship.valid? 17 | end 18 | 19 | test "should require a followed_id" do 20 | @relationship.followed_id = nil 21 | assert_not @relationship.valid? 22 | end 23 | end 24 | 25 | -------------------------------------------------------------------------------- /Listing_14.40.txt: -------------------------------------------------------------------------------- 1 | $ rails test 2 | -------------------------------------------------------------------------------- /Listing_14.41.rb: -------------------------------------------------------------------------------- 1 | class User < ApplicationRecord 2 | . 3 | . 4 | . 5 | # Returns true if a password reset has expired. 6 | def password_reset_expired? 7 | reset_sent_at < 2.hours.ago 8 | end 9 | 10 | # Returns a user's status feed. 11 | def feed 12 | Micropost.where("user_id IN (?) OR user_id = ?", following_ids, id) 13 | end 14 | 15 | # Follows a user. 16 | def follow(other_user) 17 | following << other_user unless self == other_user 18 | end 19 | . 20 | . 21 | . 22 | end 23 | -------------------------------------------------------------------------------- /Listing_14.42.txt: -------------------------------------------------------------------------------- 1 | $ rails test 2 | -------------------------------------------------------------------------------- /Listing_14.43.rb: -------------------------------------------------------------------------------- 1 | class User < ApplicationRecord 2 | . 3 | . 4 | . 5 | # Returns a user's status feed. 6 | def feed 7 | Micropost.where("user_id IN (:following_ids) OR user_id = :user_id", 8 | following_ids: following_ids, user_id: id) 9 | end 10 | . 11 | . 12 | . 13 | end 14 | -------------------------------------------------------------------------------- /Listing_14.44.rb: -------------------------------------------------------------------------------- 1 | class User < ApplicationRecord 2 | . 3 | . 4 | . 5 | # Returns a user's status feed. 6 | def feed 7 | following_ids = "SELECT followed_id FROM relationships 8 | WHERE follower_id = :user_id" 9 | Micropost.where("user_id IN (#{following_ids}) 10 | OR user_id = :user_id", user_id: id) 11 | end 12 | . 13 | . 14 | . 15 | end 16 | -------------------------------------------------------------------------------- /Listing_14.45.txt: -------------------------------------------------------------------------------- 1 | $ rails test 2 | -------------------------------------------------------------------------------- /Listing_14.46.rb: -------------------------------------------------------------------------------- 1 | class User < ApplicationRecord 2 | . 3 | . 4 | . 5 | # Returns a user's status feed. 6 | def feed 7 | following_ids = "SELECT followed_id FROM relationships 8 | WHERE follower_id = :user_id" 9 | Micropost.where("user_id IN (#{following_ids}) 10 | OR user_id = :user_id", user_id: id) 11 | .includes(:user, image_attachment: :blob) 12 | end 13 | . 14 | . 15 | . 16 | end 17 | -------------------------------------------------------------------------------- /Listing_14.47.txt: -------------------------------------------------------------------------------- 1 | $ rails test 2 | -------------------------------------------------------------------------------- /Listing_14.48.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class Following < ActionDispatch::IntegrationTest 4 | 5 | def setup 6 | @user = users(:michael) 7 | @other = users(:archer) 8 | log_in_as(@user) 9 | end 10 | end 11 | 12 | class FollowPagesTest < Following 13 | . 14 | . 15 | . 16 | test "feed on Home page" do 17 | get root_path 18 | @user.feed.paginate(page: 1).each do |micropost| 19 | assert_match CGI.escapeHTML(FILL_IN), FILL_IN 20 | end 21 | end 22 | end 23 | . 24 | . 25 | . 26 | -------------------------------------------------------------------------------- /Listing_14.49.rb: -------------------------------------------------------------------------------- 1 | class User < ApplicationRecord 2 | . 3 | . 4 | . 5 | # Returns a user's status feed. 6 | def feed 7 | part_of_feed = "relationships.follower_id = :id or microposts.user_id = :id" 8 | Micropost.left_outer_joins(user: :followers) 9 | .where(part_of_feed, { id: id }) 10 | .includes(:user, image_attachment: :blob) 11 | end 12 | . 13 | . 14 | . 15 | end 16 | -------------------------------------------------------------------------------- /Listing_14.5.rb: -------------------------------------------------------------------------------- 1 | class Relationship < ApplicationRecord 2 | belongs_to :follower, class_name: "User" 3 | belongs_to :followed, class_name: "User" 4 | validates :follower_id, presence: true 5 | validates :followed_id, presence: true 6 | end 7 | -------------------------------------------------------------------------------- /Listing_14.51.rb: -------------------------------------------------------------------------------- 1 | class User < ApplicationRecord 2 | . 3 | . 4 | . 5 | # Returns a user's status feed. 6 | def feed 7 | part_of_feed = "relationships.follower_id = :id or microposts.user_id = :id" 8 | Micropost.left_outer_joins(user: :followers) 9 | .where(part_of_feed, { id: id }).distinct 10 | .includes(:user, image_attachment: :blob) 11 | end 12 | . 13 | . 14 | . 15 | end 16 | -------------------------------------------------------------------------------- /Listing_14.6.yaml: -------------------------------------------------------------------------------- 1 | # empty 2 | -------------------------------------------------------------------------------- /Listing_14.7.txt: -------------------------------------------------------------------------------- 1 | $ rails test 2 | -------------------------------------------------------------------------------- /Listing_14.8.rb: -------------------------------------------------------------------------------- 1 | class User < ApplicationRecord 2 | has_many :microposts, dependent: :destroy 3 | has_many :active_relationships, class_name: "Relationship", 4 | foreign_key: "follower_id", 5 | dependent: :destroy 6 | has_many :following, through: :active_relationships, source: :followed 7 | . 8 | . 9 | . 10 | end 11 | -------------------------------------------------------------------------------- /Listing_14.9.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class UserTest < ActiveSupport::TestCase 4 | . 5 | . 6 | . 7 | test "should follow and unfollow a user" do 8 | michael = users(:michael) 9 | archer = users(:archer) 10 | assert_not michael.following?(archer) 11 | michael.follow(archer) 12 | assert michael.following?(archer) 13 | michael.unfollow(archer) 14 | assert_not michael.following?(archer) 15 | # Users can't follow themselves. 16 | michael.follow(michael) 17 | assert_not michael.following?(michael) 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /Listing_2.10.rb: -------------------------------------------------------------------------------- 1 | class User < ApplicationRecord 2 | end 3 | -------------------------------------------------------------------------------- /Listing_2.11.html.erb: -------------------------------------------------------------------------------- 1 |

    <%= notice %>

    2 | 3 |

    Users

    4 | 5 |
    6 | <% @users.each do |user| %> 7 | <%= render user %> 8 |

    9 | <%= link_to "Show this user", user %> 10 |

    11 | <% end %> 12 |
    13 | 14 | <%= link_to "New user", new_user_path %> 15 | -------------------------------------------------------------------------------- /Listing_2.12.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | resources :microposts 3 | resources :users 4 | root 'users#index' 5 | end 6 | 7 | -------------------------------------------------------------------------------- /Listing_2.13.rb: -------------------------------------------------------------------------------- 1 | class MicropostsController < ApplicationController 2 | . 3 | . 4 | . 5 | def index 6 | . 7 | . 8 | . 9 | end 10 | 11 | def show 12 | . 13 | . 14 | . 15 | end 16 | 17 | def new 18 | . 19 | . 20 | . 21 | end 22 | 23 | def edit 24 | . 25 | . 26 | . 27 | end 28 | 29 | def create 30 | . 31 | . 32 | . 33 | end 34 | 35 | def update 36 | . 37 | . 38 | . 39 | end 40 | 41 | def destroy 42 | . 43 | . 44 | . 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /Listing_2.14.rb: -------------------------------------------------------------------------------- 1 | class Micropost < ApplicationRecord 2 | validates :content, length: { maximum: 140 } 3 | end 4 | -------------------------------------------------------------------------------- /Listing_2.15.rb: -------------------------------------------------------------------------------- 1 | class User < ApplicationRecord 2 | has_many :microposts 3 | end 4 | -------------------------------------------------------------------------------- /Listing_2.16.rb: -------------------------------------------------------------------------------- 1 | class Micropost < ApplicationRecord 2 | belongs_to :user 3 | validates :content, length: { maximum: 140 } 4 | end 5 | -------------------------------------------------------------------------------- /Listing_2.18.rb: -------------------------------------------------------------------------------- 1 | class Micropost < ApplicationRecord 2 | belongs_to :user 3 | validates :content, length: { maximum: 140 }, 4 | presence: true 5 | end 6 | -------------------------------------------------------------------------------- /Listing_2.19.rb: -------------------------------------------------------------------------------- 1 | class User < ApplicationRecord 2 | has_many :microposts 3 | validates FILL_IN, presence: true # Replace FILL_IN with the right code. 4 | validates FILL_IN, presence: true # Replace FILL_IN with the right code. 5 | end 6 | -------------------------------------------------------------------------------- /Listing_2.2.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::Base 2 | 3 | def hello 4 | render html: "hello, world!" 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /Listing_2.20.rb: -------------------------------------------------------------------------------- 1 | class User < ApplicationRecord 2 | . 3 | . 4 | . 5 | end 6 | -------------------------------------------------------------------------------- /Listing_2.21.rb: -------------------------------------------------------------------------------- 1 | class Micropost < ApplicationRecord 2 | . 3 | . 4 | . 5 | end 6 | -------------------------------------------------------------------------------- /Listing_2.22.rb: -------------------------------------------------------------------------------- 1 | class UsersController < ApplicationController 2 | . 3 | . 4 | . 5 | end 6 | -------------------------------------------------------------------------------- /Listing_2.23.rb: -------------------------------------------------------------------------------- 1 | class MicropostsController < ApplicationController 2 | . 3 | . 4 | . 5 | end 6 | -------------------------------------------------------------------------------- /Listing_2.24.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::Base 2 | . 3 | . 4 | . 5 | end 6 | -------------------------------------------------------------------------------- /Listing_2.3.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | root 'application#hello' 3 | end 4 | -------------------------------------------------------------------------------- /Listing_2.4.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | . 3 | . 4 | . 5 | # Allow connections to local server on cloud IDE. 6 | config.hosts.clear 7 | end 8 | -------------------------------------------------------------------------------- /Listing_2.5.txt: -------------------------------------------------------------------------------- 1 | $ rails db:migrate 2 | == CreateUsers: migrating ====================================== 3 | -- create_table(:users) 4 | -> 0.0027s 5 | == CreateUsers: migrated (0.0036s) ============================= 6 | -------------------------------------------------------------------------------- /Listing_2.6.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | resources :users 3 | root 'application#hello' 4 | end 5 | -------------------------------------------------------------------------------- /Listing_2.7.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | resources :users 3 | root 'users#index' 4 | end 5 | -------------------------------------------------------------------------------- /Listing_2.8.rb: -------------------------------------------------------------------------------- 1 | class UsersController < ApplicationController 2 | . 3 | . 4 | . 5 | def index 6 | . 7 | . 8 | . 9 | end 10 | 11 | def show 12 | . 13 | . 14 | . 15 | end 16 | 17 | def new 18 | . 19 | . 20 | . 21 | end 22 | 23 | def edit 24 | . 25 | . 26 | . 27 | end 28 | 29 | def create 30 | . 31 | . 32 | . 33 | end 34 | 35 | def update 36 | . 37 | . 38 | . 39 | end 40 | 41 | def destroy 42 | . 43 | . 44 | . 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /Listing_2.9.rb: -------------------------------------------------------------------------------- 1 | class UsersController < ApplicationController 2 | . 3 | . 4 | . 5 | def index 6 | @users = User.all 7 | end 8 | . 9 | . 10 | . 11 | end 12 | -------------------------------------------------------------------------------- /Listing_3.1.txt: -------------------------------------------------------------------------------- 1 | $ cd ~/environment 2 | $ rails _7.0.3_ new sample_app --skip-bundle 3 | $ cd sample_app/ 4 | -------------------------------------------------------------------------------- /Listing_3.10.html.erb: -------------------------------------------------------------------------------- 1 |

    StaticPages#home

    2 |

    Find me in app/views/static_pages/home.html.erb

    3 | -------------------------------------------------------------------------------- /Listing_3.11.html.erb: -------------------------------------------------------------------------------- 1 |

    StaticPages#help

    2 |

    Find me in app/views/static_pages/help.html.erb

    3 | -------------------------------------------------------------------------------- /Listing_3.12.html: -------------------------------------------------------------------------------- 1 |

    Sample App

    2 |

    3 | This is the home page for the 4 | Ruby on Rails Tutorial 5 | sample application. 6 |

    7 | -------------------------------------------------------------------------------- /Listing_3.13.html: -------------------------------------------------------------------------------- 1 |

    Help

    2 |

    3 | Get help on the Ruby on Rails Tutorial at the 4 | Rails Tutorial Help page. 5 | To get help on this sample app, see the 6 | Ruby on Rails Tutorial 7 | book. 8 |

    9 | -------------------------------------------------------------------------------- /Listing_3.14.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class StaticPagesControllerTest < ActionDispatch::IntegrationTest 4 | 5 | test "should get home" do 6 | get static_pages_home_url 7 | assert_response :success 8 | end 9 | 10 | test "should get help" do 11 | get static_pages_help_url 12 | assert_response :success 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /Listing_3.15.txt: -------------------------------------------------------------------------------- 1 | $ rails db:migrate # Necessary on some systems 2 | $ rails test 3 | 2 runs, 2 assertions, 0 failures, 0 errors, 0 skips 4 | -------------------------------------------------------------------------------- /Listing_3.16.txt: -------------------------------------------------------------------------------- 1 | . 2 | . 3 | . 4 | # Ignore db test files. 5 | db/test.* 6 | -------------------------------------------------------------------------------- /Listing_3.17.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class StaticPagesControllerTest < ActionDispatch::IntegrationTest 4 | 5 | test "should get home" do 6 | get static_pages_home_url 7 | assert_response :success 8 | end 9 | 10 | test "should get help" do 11 | get static_pages_help_url 12 | assert_response :success 13 | end 14 | 15 | test "should get about" do 16 | get static_pages_about_url 17 | assert_response :success 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /Listing_3.18.txt: -------------------------------------------------------------------------------- 1 | $ rails test 2 | 3 runs, 2 assertions, 0 failures, 1 errors, 0 skips 3 | -------------------------------------------------------------------------------- /Listing_3.19.txt: -------------------------------------------------------------------------------- 1 | $ rails test 2 | NameError: undefined local variable or method `static_pages_about_url' 3 | -------------------------------------------------------------------------------- /Listing_3.20.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | get "static_pages/home" 3 | get "static_pages/help" 4 | get "static_pages/about" 5 | root "application#hello" 6 | end 7 | -------------------------------------------------------------------------------- /Listing_3.21.txt: -------------------------------------------------------------------------------- 1 | $ rails test 2 | AbstractController::ActionNotFound: 3 | The action 'about' could not be found for StaticPagesController 4 | -------------------------------------------------------------------------------- /Listing_3.22.rb: -------------------------------------------------------------------------------- 1 | class StaticPagesController < ApplicationController 2 | 3 | def home 4 | end 5 | 6 | def help 7 | end 8 | 9 | def about 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /Listing_3.23.html.erb: -------------------------------------------------------------------------------- 1 |

    About

    2 |

    3 | The Ruby on Rails 4 | Tutorial, part of the 5 | Learn Enough family of 6 | tutorials, is a 7 | book and 8 | screencast series 9 | to teach web development with 10 | Ruby on Rails. 11 | This is the sample app for the tutorial. 12 |

    13 | -------------------------------------------------------------------------------- /Listing_3.24.txt: -------------------------------------------------------------------------------- 1 | $ rails test 2 | 3 runs, 3 assertions, 0 failures, 0 errors, 0 skips 3 | -------------------------------------------------------------------------------- /Listing_3.25.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Greeting 5 | 6 | 7 |

    Hello, world!

    8 | 9 | 10 | -------------------------------------------------------------------------------- /Listing_3.26.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class StaticPagesControllerTest < ActionDispatch::IntegrationTest 4 | 5 | test "should get home" do 6 | get static_pages_home_url 7 | assert_response :success 8 | assert_select "title", "Home | Ruby on Rails Tutorial Sample App" 9 | end 10 | 11 | test "should get help" do 12 | get static_pages_help_url 13 | assert_response :success 14 | assert_select "title", "Help | Ruby on Rails Tutorial Sample App" 15 | end 16 | 17 | test "should get about" do 18 | get static_pages_about_url 19 | assert_response :success 20 | assert_select "title", "About | Ruby on Rails Tutorial Sample App" 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /Listing_3.27.txt: -------------------------------------------------------------------------------- 1 | $ rails test 2 | 3 runs, 6 assertions, 3 failures, 0 errors, 0 skips 3 | -------------------------------------------------------------------------------- /Listing_3.28.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Home | Ruby on Rails Tutorial Sample App 5 | 6 | 7 |

    Sample App

    8 |

    9 | This is the home page for the 10 | Ruby on Rails Tutorial 11 | sample application. 12 |

    13 | 14 | 15 | -------------------------------------------------------------------------------- /Listing_3.29.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Help | Ruby on Rails Tutorial Sample App 5 | 6 | 7 |

    Help

    8 |

    9 | Get help on the Ruby on Rails Tutorial at the 10 | Rails Tutorial help 11 | page. 12 | To get help on this sample app, see the 13 | Ruby on Rails 14 | Tutorial book. 15 |

    16 | 17 | 18 | -------------------------------------------------------------------------------- /Listing_3.31.txt: -------------------------------------------------------------------------------- 1 | $ rails test 2 | 3 runs, 6 assertions, 0 failures, 0 errors, 0 skips 3 | -------------------------------------------------------------------------------- /Listing_3.32.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class StaticPagesControllerTest < ActionDispatch::IntegrationTest 4 | 5 | def setup 6 | @base_title = "Ruby on Rails Tutorial Sample App" 7 | end 8 | 9 | test "should get home" do 10 | get static_pages_home_url 11 | assert_response :success 12 | assert_select "title", "Home | #{@base_title}" 13 | end 14 | 15 | test "should get help" do 16 | get static_pages_help_url 17 | assert_response :success 18 | assert_select "title", "Help | #{@base_title}" 19 | end 20 | 21 | test "should get about" do 22 | get static_pages_about_url 23 | assert_response :success 24 | assert_select "title", "About | #{@base_title}" 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /Listing_3.33.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:title, "Home") %> 2 | 3 | 4 | 5 | <%= yield(:title) %> | Ruby on Rails Tutorial Sample App 6 | 7 | 8 |

    Sample App

    9 |

    10 | This is the home page for the 11 | Ruby on Rails Tutorial 12 | sample application. 13 |

    14 | 15 | 16 | -------------------------------------------------------------------------------- /Listing_3.34.txt: -------------------------------------------------------------------------------- 1 | $ rails test 2 | 3 runs, 6 assertions, 0 failures, 0 errors, 0 skips 3 | -------------------------------------------------------------------------------- /Listing_3.35.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:title, "Help") %> 2 | 3 | 4 | 5 | <%= yield(:title) %> | Ruby on Rails Tutorial Sample App 6 | 7 | 8 |

    Help

    9 |

    10 | Get help on the Ruby on Rails Tutorial at the 11 | Rails Tutorial help 12 | section. 13 | To get help on this sample app, see the 14 | Ruby on Rails 15 | Tutorial book. 16 |

    17 | 18 | 19 | -------------------------------------------------------------------------------- /Listing_3.37.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <%= yield(:title) %> | Ruby on Rails Tutorial Sample App 5 | 6 | 7 | <%= csrf_meta_tags %> 8 | <%= csp_meta_tag %> 9 | 10 | <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %> 11 | 12 | 13 | 14 | <%= yield %> 15 | 16 | 17 | -------------------------------------------------------------------------------- /Listing_3.38.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:title, "Home") %> 2 |

    Sample App

    3 |

    4 | This is the home page for the 5 | Ruby on Rails Tutorial 6 | sample application. 7 |

    8 | -------------------------------------------------------------------------------- /Listing_3.39.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:title, "Help") %> 2 |

    Help

    3 |

    4 | Get help on the Ruby on Rails Tutorial at the 5 | Rails Tutorial Help page. 6 | To get help on this sample app, see the 7 | Ruby on Rails Tutorial 8 | book. 9 |

    10 | -------------------------------------------------------------------------------- /Listing_3.4.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | . 3 | . 4 | . 5 | # Allow connections to local server on cloud IDE. 6 | config.hosts.clear 7 | end 8 | -------------------------------------------------------------------------------- /Listing_3.40.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:title, "About") %> 2 |

    About

    3 |

    4 | The Ruby on Rails 5 | Tutorial, part of the 6 | Learn Enough family of 7 | tutorials, is a 8 | book and 9 | screencast series 10 | to teach web development with 11 | Ruby on Rails. 12 | This is the sample app for the tutorial. 13 |

    14 | -------------------------------------------------------------------------------- /Listing_3.41.txt: -------------------------------------------------------------------------------- 1 | $ rails test 2 | 3 runs, 6 assertions, 0 failures, 0 errors, 0 skips 3 | -------------------------------------------------------------------------------- /Listing_3.42.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:title, "Contact") %> 2 |

    Contact

    3 |

    4 | Contact the Ruby on Rails Tutorial about the sample app at the 5 | contact page. 6 |

    7 | -------------------------------------------------------------------------------- /Listing_3.43.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | root "static_pages#home" 3 | get "static_pages/home" 4 | get "static_pages/help" 5 | get "static_pages/about" 6 | end 7 | -------------------------------------------------------------------------------- /Listing_3.44.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class StaticPagesControllerTest < ActionDispatch::IntegrationTest 4 | 5 | test "should get root" do 6 | get FILL_IN 7 | assert_response FILL_IN 8 | end 9 | 10 | test "should get home" do 11 | get static_pages_home_url 12 | assert_response :success 13 | end 14 | 15 | test "should get help" do 16 | get static_pages_help_url 17 | assert_response :success 18 | end 19 | 20 | test "should get about" do 21 | get static_pages_about_url 22 | assert_response :success 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /Listing_3.45.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | # root "static_pages#home" 3 | get "static_pages/home" 4 | get "static_pages/help" 5 | get "static_pages/about" 6 | end 7 | -------------------------------------------------------------------------------- /Listing_3.46.rb: -------------------------------------------------------------------------------- 1 | ENV["RAILS_ENV"] ||= "test" 2 | require_relative "../config/environment" 3 | require "rails/test_help" 4 | require "minitest/reporters" 5 | Minitest::Reporters.use! 6 | 7 | class ActiveSupport::TestCase 8 | # Run tests in parallel with specified workers 9 | parallelize(workers: :number_of_processors) 10 | 11 | # Set up all fixtures in test/fixtures/*.yml. 12 | fixtures :all 13 | 14 | # Add more helper methods to be used by all tests here... 15 | end 16 | -------------------------------------------------------------------------------- /Listing_3.5.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::Base 2 | 3 | def hello 4 | render html: "hello, world!" 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /Listing_3.6.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | root "application#hello" 3 | end 4 | -------------------------------------------------------------------------------- /Listing_3.7.txt: -------------------------------------------------------------------------------- 1 | $ rails generate controller StaticPages home help 2 | create app/controllers/static_pages_controller.rb 3 | route get "static_pages/home" 4 | get "static_pages/help" 5 | invoke erb 6 | create app/views/static_pages 7 | create app/views/static_pages/home.html.erb 8 | create app/views/static_pages/help.html.erb 9 | invoke test_unit 10 | create test/controllers/static_pages_controller_test.rb 11 | invoke helper 12 | create app/helpers/static_pages_helper.rb 13 | invoke test_unit 14 | -------------------------------------------------------------------------------- /Listing_3.8.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | get "static_pages/home" 3 | get "static_pages/help" 4 | root "application#hello" 5 | end 6 | -------------------------------------------------------------------------------- /Listing_3.9.rb: -------------------------------------------------------------------------------- 1 | class StaticPagesController < ApplicationController 2 | def home 3 | end 4 | 5 | def help 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /Listing_4.1.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <%= yield(:title) %> | Ruby on Rails Tutorial Sample App 5 | 6 | 7 | <%= csrf_meta_tags %> 8 | <%= csp_meta_tag %> 9 | 10 | <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %> 11 | 12 | 13 | 14 | <%= yield %> 15 | 16 | 17 | -------------------------------------------------------------------------------- /Listing_4.10.rb: -------------------------------------------------------------------------------- 1 | >> def palindrome_tester(s) 2 | >> if FILL_IN 3 | >> puts "It's a palindrome!" 4 | >> else 5 | >> puts "It's not a palindrome." 6 | >> end 7 | >> end 8 | -------------------------------------------------------------------------------- /Listing_4.11.rb: -------------------------------------------------------------------------------- 1 | module ApplicationHelper 2 | 3 | # Returns the full title on a per-page basis. # Documentation comment 4 | def full_title(page_title = '') # Method def, optional arg 5 | base_title = "Ruby on Rails Tutorial Sample App" # Variable assignment 6 | if page_title.empty? # Boolean test 7 | base_title # Implicit return 8 | else 9 | page_title + " | " + base_title # String concatenation 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /Listing_4.12.rb: -------------------------------------------------------------------------------- 1 | >> def string_shuffle(s) 2 | >> s.?('').?.? 3 | >> end 4 | >> string_shuffle("foobar") 5 | => "oobfra" 6 | -------------------------------------------------------------------------------- /Listing_4.13.rb: -------------------------------------------------------------------------------- 1 | >> params = {} # Define a hash called 'params' (short for 'parameters'). 2 | => {} 3 | >> params[:user] = { name: "Michael Hartl", email: "mhartl@example.com" } 4 | => {:name=>"Michael Hartl", :email=>"mhartl@example.com"} 5 | >> params 6 | => {:user=>{:name=>"Michael Hartl", :email=>"mhartl@example.com"}} 7 | >> params[:user][:email] 8 | => "mhartl@example.com" 9 | -------------------------------------------------------------------------------- /Listing_4.14.html.erb: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /Listing_4.15.rb: -------------------------------------------------------------------------------- 1 | >> class Word < String # Word inherits from String. 2 | >> # Returns true if the string is its own reverse. 3 | >> def palindrome? 4 | >> self == self.reverse # self is the string itself. 5 | >> end 6 | >> end 7 | => nil 8 | -------------------------------------------------------------------------------- /Listing_4.16.rb: -------------------------------------------------------------------------------- 1 | >> class String 2 | >> def shuffle 3 | >> self.?('').?.? 4 | >> end 5 | >> end 6 | >> "foobar".shuffle 7 | => "borafo" 8 | -------------------------------------------------------------------------------- /Listing_4.17.rb: -------------------------------------------------------------------------------- 1 | class User 2 | attr_accessor :name, :email 3 | 4 | def initialize(attributes = {}) 5 | @name = attributes[:name] 6 | @email = attributes[:email] 7 | end 8 | 9 | def formatted_email 10 | "#{@name} <#{@email}>" 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /Listing_4.2.rb: -------------------------------------------------------------------------------- 1 | module ApplicationHelper 2 | 3 | # Returns the full title on a per-page basis. 4 | def full_title(page_title = '') 5 | base_title = "Ruby on Rails Tutorial Sample App" 6 | if page_title.empty? 7 | base_title 8 | else 9 | page_title + " | " + base_title 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /Listing_4.3.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <%= full_title(yield(:title)) %> 5 | 6 | 7 | <%= csrf_meta_tags %> 8 | <%= csp_meta_tag %> 9 | 10 | <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %> 11 | 12 | 13 | 14 | <%= yield %> 15 | 16 | 17 | -------------------------------------------------------------------------------- /Listing_4.4.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class StaticPagesControllerTest < ActionDispatch::IntegrationTest 4 | 5 | test "should get home" do 6 | get static_pages_home_url 7 | assert_response :success 8 | assert_select "title", "Ruby on Rails Tutorial Sample App" 9 | end 10 | 11 | test "should get help" do 12 | get static_pages_help_url 13 | assert_response :success 14 | assert_select "title", "Help | Ruby on Rails Tutorial Sample App" 15 | end 16 | 17 | test "should get about" do 18 | get static_pages_about_url 19 | assert_response :success 20 | assert_select "title", "About | Ruby on Rails Tutorial Sample App" 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /Listing_4.5.txt: -------------------------------------------------------------------------------- 1 | $ rails test 2 | 3 runs, 6 assertions, 1 failures, 0 errors, 0 skips 3 | -------------------------------------------------------------------------------- /Listing_4.6.html.erb: -------------------------------------------------------------------------------- 1 |

    Sample App

    2 |

    3 | This is the home page for the 4 | Ruby on Rails Tutorial 5 | sample application. 6 |

    7 | -------------------------------------------------------------------------------- /Listing_4.7.txt: -------------------------------------------------------------------------------- 1 | $ rails test 2 | -------------------------------------------------------------------------------- /Listing_4.8.rb: -------------------------------------------------------------------------------- 1 | IRB.conf[:PROMPT_MODE] = :SIMPLE 2 | IRB.conf[:AUTO_INDENT_MODE] = false 3 | -------------------------------------------------------------------------------- /Listing_4.9.rb: -------------------------------------------------------------------------------- 1 | >> puts "It's a palindrome!" if s == s.reverse 2 | -------------------------------------------------------------------------------- /Listing_5.10.rb: -------------------------------------------------------------------------------- 1 | <%#= image_tag("kitten.jpg", alt: "Kitten") %> 2 | -------------------------------------------------------------------------------- /Listing_5.11.css: -------------------------------------------------------------------------------- 1 | img { 2 | display: none; 3 | } 4 | -------------------------------------------------------------------------------- /Listing_5.12.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <%= full_title(yield(:title)) %> 5 | 6 | 7 | <%= csrf_meta_tags %> 8 | <%= csp_meta_tag %> 9 | 10 | <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %> 11 | <%= render 'layouts/shim' %> 12 | 13 | 14 | <%= render 'layouts/header' %> 15 |
    16 | <%= yield %> 17 |
    18 | 19 | 20 | -------------------------------------------------------------------------------- /Listing_5.13.html.erb: -------------------------------------------------------------------------------- 1 | 5 | -------------------------------------------------------------------------------- /Listing_5.14.html.erb: -------------------------------------------------------------------------------- 1 | 13 | -------------------------------------------------------------------------------- /Listing_5.15.html.erb: -------------------------------------------------------------------------------- 1 | 14 | -------------------------------------------------------------------------------- /Listing_5.16.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <%= full_title(yield(:title)) %> 5 | 6 | 7 | <%= csrf_meta_tags %> 8 | <%= csp_meta_tag %> 9 | 10 | <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %> 11 | <%= render 'layouts/shim' %> 12 | 13 | 14 | <%= render 'layouts/header' %> 15 |
    16 | <%= yield %> 17 | <%= render 'layouts/footer' %> 18 |
    19 | 20 | 21 | -------------------------------------------------------------------------------- /Listing_5.17.scss: -------------------------------------------------------------------------------- 1 | . 2 | . 3 | . 4 | /* footer */ 5 | 6 | footer { 7 | margin-top: 45px; 8 | padding-top: 5px; 9 | border-top: 1px solid #eaeaea; 10 | color: #777; 11 | } 12 | 13 | footer a { 14 | color: #555; 15 | } 16 | 17 | footer a:hover { 18 | color: #222; 19 | } 20 | 21 | footer small { 22 | float: left; 23 | } 24 | 25 | footer ul { 26 | float: right; 27 | list-style: none; 28 | } 29 | 30 | footer ul li { 31 | float: left; 32 | margin-left: 15px; 33 | } 34 | -------------------------------------------------------------------------------- /Listing_5.18.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <%= full_title(yield(:title)) %> 5 | 6 | 7 | <%= render 'layouts/rails_default' %> 8 | <%= render 'layouts/shim' %> 9 | 10 | 11 | <%= render 'layouts/header' %> 12 |
    13 | <%= yield %> 14 | <%= render 'layouts/footer' %> 15 |
    16 | 17 | 18 | -------------------------------------------------------------------------------- /Listing_5.2.html.erb: -------------------------------------------------------------------------------- 1 |
    2 |

    Welcome to the Sample App

    3 | 4 |

    5 | This is the home page for the 6 | Ruby on Rails Tutorial 7 | sample application. 8 |

    9 | 10 | <%= link_to "Sign up now!", '#', class: "btn btn-lg btn-primary" %> 11 |
    12 | 13 | <%= link_to image_tag("rails.svg", alt: "Rails logo", width: "200"), 14 | "https://rubyonrails.org/" %> 15 | -------------------------------------------------------------------------------- /Listing_5.22.txt: -------------------------------------------------------------------------------- 1 | $ rails test 2 | -------------------------------------------------------------------------------- /Listing_5.23.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | root "static_pages#home" 3 | get "static_pages/home" 4 | get "static_pages/help" 5 | get "static_pages/about" 6 | get "static_pages/contact" 7 | end 8 | -------------------------------------------------------------------------------- /Listing_5.24.rb: -------------------------------------------------------------------------------- 1 | class StaticPagesController < ApplicationController 2 | . 3 | . 4 | . 5 | def contact 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /Listing_5.25.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:title, 'Contact') %> 2 |

    Contact

    3 |

    4 | Contact the Ruby on Rails Tutorial about the sample app at the 5 | contact page. 6 |

    7 | -------------------------------------------------------------------------------- /Listing_5.26.txt: -------------------------------------------------------------------------------- 1 | $ rails test 2 | -------------------------------------------------------------------------------- /Listing_5.27.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | root "static_pages#home" 3 | get "/help", to: "static_pages#help" 4 | get "/about", to: "static_pages#about" 5 | get "/contact", to: "static_pages#contact" 6 | end 7 | -------------------------------------------------------------------------------- /Listing_5.29.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | root "static_pages#home" 3 | get "/help", to: "static_pages#help", as: 'helf' 4 | get "/about", to: "static_pages#about" 5 | get "/contact", to: "static_pages#contact" 6 | end 7 | -------------------------------------------------------------------------------- /Listing_5.3.txt: -------------------------------------------------------------------------------- 1 | $ curl -o app/assets/images/rails.svg -L https://cdn.learnenough.com/rails.svg 2 | -------------------------------------------------------------------------------- /Listing_5.30.html.erb: -------------------------------------------------------------------------------- 1 | 13 | -------------------------------------------------------------------------------- /Listing_5.31.html.erb: -------------------------------------------------------------------------------- 1 | 14 | -------------------------------------------------------------------------------- /Listing_5.32.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class SiteLayoutTest < ActionDispatch::IntegrationTest 4 | 5 | test "layout links" do 6 | get root_path 7 | assert_template 'static_pages/home' 8 | assert_select "a[href=?]", root_path, count: 2 9 | assert_select "a[href=?]", help_path 10 | assert_select "a[href=?]", about_path 11 | assert_select "a[href=?]", contact_path 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /Listing_5.33.txt: -------------------------------------------------------------------------------- 1 | $ rails test:integration 2 | -------------------------------------------------------------------------------- /Listing_5.34.txt: -------------------------------------------------------------------------------- 1 | $ rails test 2 | -------------------------------------------------------------------------------- /Listing_5.35.rb: -------------------------------------------------------------------------------- 1 | ENV['RAILS_ENV'] ||= 'test' 2 | . 3 | . 4 | . 5 | class ActiveSupport::TestCase 6 | # Run tests in parallel with specified workers 7 | parallelize(workers: :number_of_processors) 8 | # Set up all fixtures in test/fixtures/*.yml. 9 | fixtures :all 10 | include ApplicationHelper 11 | . 12 | . 13 | . 14 | end 15 | -------------------------------------------------------------------------------- /Listing_5.36.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class SiteLayoutTest < ActionDispatch::IntegrationTest 4 | 5 | test "layout links" do 6 | get root_path 7 | assert_template 'static_pages/home' 8 | assert_select "a[href=?]", root_path, count: 2 9 | assert_select "a[href=?]", help_path 10 | assert_select "a[href=?]", about_path 11 | assert_select "a[href=?]", contact_path 12 | get contact_path 13 | assert_select "title", full_title("Contact") 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /Listing_5.37.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class ApplicationHelperTest < ActionView::TestCase 4 | test "full title helper" do 5 | assert_equal FILL_IN, full_title 6 | assert_equal FILL_IN, full_title("Help") 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /Listing_5.38.txt: -------------------------------------------------------------------------------- 1 | $ rails generate controller Users new 2 | create app/controllers/users_controller.rb 3 | route get "users/new" 4 | invoke erb 5 | create app/views/users 6 | create app/views/users/new.html.erb 7 | invoke test_unit 8 | create test/controllers/users_controller_test.rb 9 | invoke helper 10 | create app/helpers/users_helper.rb 11 | invoke test_unit 12 | invoke assets 13 | invoke scss 14 | create app/assets/stylesheets/users.scss 15 | -------------------------------------------------------------------------------- /Listing_5.39.rb: -------------------------------------------------------------------------------- 1 | class UsersController < ApplicationController 2 | 3 | def new 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /Listing_5.4.txt: -------------------------------------------------------------------------------- 1 | $ curl -OL https://cdn.learnenough.com/kitten.jpg 2 | -------------------------------------------------------------------------------- /Listing_5.40.html.erb: -------------------------------------------------------------------------------- 1 |

    Users#new

    2 |

    Find me in app/views/users/new.html.erb

    3 | -------------------------------------------------------------------------------- /Listing_5.41.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class UsersControllerTest < ActionDispatch::IntegrationTest 4 | 5 | test "should get new" do 6 | get users_new_url 7 | assert_response :success 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /Listing_5.42.txt: -------------------------------------------------------------------------------- 1 | $ rails test 2 | -------------------------------------------------------------------------------- /Listing_5.43.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | root "static_pages#home" 3 | get "/help", to: "static_pages#help" 4 | get "/about", to: "static_pages#about" 5 | get "/contact", to: "static_pages#contact" 6 | get "/signup", to: "users#new" 7 | end 8 | -------------------------------------------------------------------------------- /Listing_5.44.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class UsersControllerTest < ActionDispatch::IntegrationTest 4 | 5 | test "should get new" do 6 | get signup_path 7 | assert_response :success 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /Listing_5.45.html.erb: -------------------------------------------------------------------------------- 1 |
    2 |

    Welcome to the Sample App

    3 | 4 |

    5 | This is the home page for the 6 | Ruby on Rails Tutorial 7 | sample application. 8 |

    9 | 10 | <%= link_to "Sign up now!", signup_path, class: "btn btn-lg btn-primary" %> 11 |
    12 | 13 | <%= link_to image_tag("rails.svg", alt: "Rails logo", width: "200"), 14 | "https://rubyonrails.org/" %> 15 | -------------------------------------------------------------------------------- /Listing_5.46.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:title, 'Sign up') %> 2 |

    Sign up

    3 |

    This will be a signup page for new users.

    4 | -------------------------------------------------------------------------------- /Listing_5.5.rb: -------------------------------------------------------------------------------- 1 | . 2 | . 3 | . 4 | gem "rails", "7.0.3" 5 | gem "bootstrap-sass", "3.4.1" 6 | gem "sassc-rails", "2.1.2" 7 | gem "sprockets-rails", "3.4.2" 8 | . 9 | . 10 | . 11 | -------------------------------------------------------------------------------- /Listing_5.6.scss: -------------------------------------------------------------------------------- 1 | @import "bootstrap-sprockets"; 2 | @import "bootstrap"; 3 | -------------------------------------------------------------------------------- /Listing_5.7.scss: -------------------------------------------------------------------------------- 1 | @import "bootstrap-sprockets"; 2 | @import "bootstrap"; 3 | 4 | /* universal */ 5 | 6 | body { 7 | padding-top: 60px; 8 | } 9 | 10 | section { 11 | overflow: auto; 12 | } 13 | 14 | textarea { 15 | resize: vertical; 16 | } 17 | 18 | .center { 19 | text-align: center; 20 | } 21 | 22 | .center h1 { 23 | margin-bottom: 10px; 24 | } 25 | -------------------------------------------------------------------------------- /Listing_5.8.scss: -------------------------------------------------------------------------------- 1 | @import "bootstrap-sprockets"; 2 | @import "bootstrap"; 3 | . 4 | . 5 | . 6 | /* typography */ 7 | 8 | h1, h2, h3, h4, h5, h6 { 9 | line-height: 1; 10 | } 11 | 12 | h1 { 13 | font-size: 3em; 14 | letter-spacing: -2px; 15 | margin-bottom: 30px; 16 | text-align: center; 17 | } 18 | 19 | h2 { 20 | font-size: 1.2em; 21 | letter-spacing: -1px; 22 | margin-bottom: 30px; 23 | text-align: center; 24 | font-weight: normal; 25 | color: #777; 26 | } 27 | 28 | p { 29 | font-size: 1.1em; 30 | line-height: 1.7em; 31 | } 32 | -------------------------------------------------------------------------------- /Listing_5.9.scss: -------------------------------------------------------------------------------- 1 | @import "bootstrap-sprockets"; 2 | @import "bootstrap"; 3 | . 4 | . 5 | . 6 | /* header */ 7 | 8 | #logo { 9 | float: left; 10 | margin-right: 10px; 11 | font-size: 1.7em; 12 | color: #fff; 13 | text-transform: uppercase; 14 | letter-spacing: -1px; 15 | padding-top: 9px; 16 | font-weight: bold; 17 | } 18 | 19 | #logo:hover { 20 | color: #fff; 21 | text-decoration: none; 22 | } 23 | -------------------------------------------------------------------------------- /Listing_6.1.txt: -------------------------------------------------------------------------------- 1 | $ rails generate model User name:string email:string 2 | invoke active_record 3 | create db/migrate/_create_users.rb 4 | create app/models/user.rb 5 | invoke test_unit 6 | create test/models/user_test.rb 7 | create test/fixtures/users.yml 8 | -------------------------------------------------------------------------------- /Listing_6.10.txt: -------------------------------------------------------------------------------- 1 | $ rails test:models 2 | -------------------------------------------------------------------------------- /Listing_6.11.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class UserTest < ActiveSupport::TestCase 4 | 5 | def setup 6 | @user = User.new(name: "Example User", email: "user@example.com") 7 | end 8 | 9 | test "should be valid" do 10 | assert @user.valid? 11 | end 12 | 13 | test "name should be present" do 14 | @user.name = "" 15 | assert_not @user.valid? 16 | end 17 | 18 | test "email should be present" do 19 | @user.email = " " 20 | assert_not @user.valid? 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /Listing_6.12.rb: -------------------------------------------------------------------------------- 1 | class User < ApplicationRecord 2 | validates :name, presence: true 3 | validates :email, presence: true 4 | end 5 | -------------------------------------------------------------------------------- /Listing_6.13.txt: -------------------------------------------------------------------------------- 1 | $ rails test 2 | -------------------------------------------------------------------------------- /Listing_6.14.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class UserTest < ActiveSupport::TestCase 4 | 5 | def setup 6 | @user = User.new(name: "Example User", email: "user@example.com") 7 | end 8 | . 9 | . 10 | . 11 | test "name should not be too long" do 12 | @user.name = "a" * 51 13 | assert_not @user.valid? 14 | end 15 | 16 | test "email should not be too long" do 17 | @user.email = "a" * 244 + "@example.com" 18 | assert_not @user.valid? 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /Listing_6.15.txt: -------------------------------------------------------------------------------- 1 | $ rails test 2 | -------------------------------------------------------------------------------- /Listing_6.16.rb: -------------------------------------------------------------------------------- 1 | class User < ApplicationRecord 2 | validates :name, presence: true, length: { maximum: 50 } 3 | validates :email, presence: true, length: { maximum: 255 } 4 | end 5 | -------------------------------------------------------------------------------- /Listing_6.17.txt: -------------------------------------------------------------------------------- 1 | $ rails test 2 | -------------------------------------------------------------------------------- /Listing_6.18.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class UserTest < ActiveSupport::TestCase 4 | 5 | def setup 6 | @user = User.new(name: "Example User", email: "user@example.com") 7 | end 8 | . 9 | . 10 | . 11 | test "email validation should accept valid addresses" do 12 | valid_addresses = %w[user@example.com USER@foo.COM A_US-ER@foo.bar.org 13 | first.last@foo.jp alice+bob@baz.cn] 14 | valid_addresses.each do |valid_address| 15 | @user.email = valid_address 16 | assert @user.valid?, "#{valid_address.inspect} should be valid" 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /Listing_6.19.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class UserTest < ActiveSupport::TestCase 4 | 5 | def setup 6 | @user = User.new(name: "Example User", email: "user@example.com") 7 | end 8 | . 9 | . 10 | . 11 | test "email validation should reject invalid addresses" do 12 | invalid_addresses = %w[user@example,com user_at_foo.org user.name@example. 13 | foo@bar_baz.com foo@bar+baz.com] 14 | invalid_addresses.each do |invalid_address| 15 | @user.email = invalid_address 16 | assert_not @user.valid?, "#{invalid_address.inspect} should be invalid" 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /Listing_6.2.rb: -------------------------------------------------------------------------------- 1 | class CreateUsers < ActiveRecord::Migration[7.0] 2 | def change 3 | create_table :users do |t| 4 | t.string :name 5 | t.string :email 6 | 7 | t.timestamps 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /Listing_6.20.txt: -------------------------------------------------------------------------------- 1 | $ rails test 2 | -------------------------------------------------------------------------------- /Listing_6.21.rb: -------------------------------------------------------------------------------- 1 | class User < ApplicationRecord 2 | validates :name, presence: true, length: { maximum: 50 } 3 | VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i 4 | validates :email, presence: true, length: { maximum: 255 }, 5 | format: { with: VALID_EMAIL_REGEX } 6 | end 7 | -------------------------------------------------------------------------------- /Listing_6.22.txt: -------------------------------------------------------------------------------- 1 | $ rails test:models 2 | -------------------------------------------------------------------------------- /Listing_6.23.rb: -------------------------------------------------------------------------------- 1 | class User < ApplicationRecord 2 | validates :name, presence: true, length: { maximum: 50 } 3 | VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i 4 | validates :email, presence: true, length: { maximum: 255 }, 5 | format: { with: VALID_EMAIL_REGEX } 6 | end 7 | -------------------------------------------------------------------------------- /Listing_6.24.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class UserTest < ActiveSupport::TestCase 4 | 5 | def setup 6 | @user = User.new(name: "Example User", email: "user@example.com") 7 | end 8 | . 9 | . 10 | . 11 | test "email addresses should be unique" do 12 | duplicate_user = @user.dup 13 | @user.save 14 | assert_not duplicate_user.valid? 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /Listing_6.25.rb: -------------------------------------------------------------------------------- 1 | class User < ApplicationRecord 2 | validates :name, presence: true, length: { maximum: 50 } 3 | VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i 4 | validates :email, presence: true, length: { maximum: 255 }, 5 | format: { with: VALID_EMAIL_REGEX }, 6 | uniqueness: true 7 | end 8 | -------------------------------------------------------------------------------- /Listing_6.26.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class UserTest < ActiveSupport::TestCase 4 | 5 | def setup 6 | @user = User.new(name: "Example User", email: "user@example.com") 7 | end 8 | . 9 | . 10 | . 11 | test "email addresses should be unique" do 12 | duplicate_user = @user.dup 13 | duplicate_user.email = @user.email.upcase 14 | @user.save 15 | assert_not duplicate_user.valid? 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /Listing_6.27.rb: -------------------------------------------------------------------------------- 1 | class User < ApplicationRecord 2 | validates :name, presence: true, length: { maximum: 50 } 3 | VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i 4 | validates :email, presence: true, length: { maximum: 255 }, 5 | format: { with: VALID_EMAIL_REGEX }, 6 | uniqueness: { case_sensitive: false } 7 | end 8 | -------------------------------------------------------------------------------- /Listing_6.28.txt: -------------------------------------------------------------------------------- 1 | $ rails test 2 | -------------------------------------------------------------------------------- /Listing_6.29.rb: -------------------------------------------------------------------------------- 1 | class AddIndexToUsersEmail < ActiveRecord::Migration[7.0] 2 | def change 3 | add_index :users, :email, unique: true 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /Listing_6.3.rb: -------------------------------------------------------------------------------- 1 | class User < ApplicationRecord 2 | end 3 | -------------------------------------------------------------------------------- /Listing_6.30.yaml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/ 2 | # FixtureSet.html 3 | 4 | one: 5 | name: MyString 6 | email: MyString 7 | 8 | two: 9 | name: MyString 10 | email: MyString 11 | -------------------------------------------------------------------------------- /Listing_6.31.yaml: -------------------------------------------------------------------------------- 1 | # empty 2 | -------------------------------------------------------------------------------- /Listing_6.32.rb: -------------------------------------------------------------------------------- 1 | class User < ApplicationRecord 2 | before_save { self.email = email.downcase } 3 | validates :name, presence: true, length: { maximum: 50 } 4 | VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i 5 | validates :email, presence: true, length: { maximum: 255 }, 6 | format: { with: VALID_EMAIL_REGEX }, 7 | uniqueness: true 8 | end 9 | -------------------------------------------------------------------------------- /Listing_6.33.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class UserTest < ActiveSupport::TestCase 4 | 5 | def setup 6 | @user = User.new(name: "Example User", email: "user@example.com") 7 | end 8 | . 9 | . 10 | . 11 | test "email addresses should be unique" do 12 | duplicate_user = @user.dup 13 | @user.save 14 | assert_not duplicate_user.valid? 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /Listing_6.34.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class UserTest < ActiveSupport::TestCase 4 | 5 | def setup 6 | @user = User.new(name: "Example User", email: "user@example.com") 7 | end 8 | . 9 | . 10 | . 11 | test "email addresses should be unique" do 12 | duplicate_user = @user.dup 13 | @user.save 14 | assert_not duplicate_user.valid? 15 | end 16 | 17 | test "email addresses should be saved as lowercase" do 18 | mixed_case_email = "Foo@ExAMPle.CoM" 19 | @user.email = mixed_case_email 20 | @user.save 21 | assert_equal mixed_case_email.downcase, @user.reload.email 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /Listing_6.35.rb: -------------------------------------------------------------------------------- 1 | class User < ApplicationRecord 2 | before_save { email.downcase! } 3 | validates :name, presence: true, length: { maximum: 50 } 4 | VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i 5 | validates :email, presence: true, length: { maximum: 255 }, 6 | format: { with: VALID_EMAIL_REGEX }, 7 | uniqueness: true 8 | end 9 | -------------------------------------------------------------------------------- /Listing_6.36.rb: -------------------------------------------------------------------------------- 1 | class AddPasswordDigestToUsers < ActiveRecord::Migration[7.0] 2 | def change 3 | add_column :users, :password_digest, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /Listing_6.37.rb: -------------------------------------------------------------------------------- 1 | . 2 | . 3 | . 4 | gem "rails", "7.0.3" 5 | gem "bcrypt", "3.1.18" 6 | gem "bootstrap-sass", "3.4.1" 7 | . 8 | . 9 | . 10 | -------------------------------------------------------------------------------- /Listing_6.38.rb: -------------------------------------------------------------------------------- 1 | class User < ApplicationRecord 2 | before_save { self.email = email.downcase } 3 | validates :name, presence: true, length: { maximum: 50 } 4 | VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i 5 | validates :email, presence: true, length: { maximum: 255 }, 6 | format: { with: VALID_EMAIL_REGEX }, 7 | uniqueness: true 8 | has_secure_password 9 | end 10 | -------------------------------------------------------------------------------- /Listing_6.39.txt: -------------------------------------------------------------------------------- 1 | $ rails test 2 | -------------------------------------------------------------------------------- /Listing_6.4.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class UserTest < ActiveSupport::TestCase 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /Listing_6.40.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class UserTest < ActiveSupport::TestCase 4 | 5 | def setup 6 | @user = User.new(name: "Example User", email: "user@example.com", 7 | password: "foobar", password_confirmation: "foobar") 8 | end 9 | . 10 | . 11 | . 12 | end 13 | -------------------------------------------------------------------------------- /Listing_6.41.txt: -------------------------------------------------------------------------------- 1 | $ rails test 2 | -------------------------------------------------------------------------------- /Listing_6.42.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class UserTest < ActiveSupport::TestCase 4 | 5 | def setup 6 | @user = User.new(name: "Example User", email: "user@example.com", 7 | password: "foobar", password_confirmation: "foobar") 8 | end 9 | . 10 | . 11 | . 12 | test "password should be present (nonblank)" do 13 | @user.password = @user.password_confirmation = " " * 6 14 | assert_not @user.valid? 15 | end 16 | 17 | test "password should have a minimum length" do 18 | @user.password = @user.password_confirmation = "a" * 5 19 | assert_not @user.valid? 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /Listing_6.43.rb: -------------------------------------------------------------------------------- 1 | class User < ApplicationRecord 2 | before_save { self.email = email.downcase } 3 | validates :name, presence: true, length: { maximum: 50 } 4 | VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i 5 | validates :email, presence: true, length: { maximum: 255 }, 6 | format: { with: VALID_EMAIL_REGEX }, 7 | uniqueness: true 8 | has_secure_password 9 | validates :password, presence: true, length: { minimum: 6 } 10 | end 11 | -------------------------------------------------------------------------------- /Listing_6.44.txt: -------------------------------------------------------------------------------- 1 | $ rails test:models 2 | -------------------------------------------------------------------------------- /Listing_6.5.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class UserTest < ActiveSupport::TestCase 4 | 5 | def setup 6 | @user = User.new(name: "Example User", email: "user@example.com") 7 | end 8 | 9 | test "should be valid" do 10 | assert @user.valid? 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /Listing_6.6.txt: -------------------------------------------------------------------------------- 1 | $ rails test:models 2 | -------------------------------------------------------------------------------- /Listing_6.7.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class UserTest < ActiveSupport::TestCase 4 | 5 | def setup 6 | @user = User.new(name: "Example User", email: "user@example.com") 7 | end 8 | 9 | test "should be valid" do 10 | assert @user.valid? 11 | end 12 | 13 | test "name should be present" do 14 | @user.name = " " 15 | assert_not @user.valid? 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /Listing_6.8.txt: -------------------------------------------------------------------------------- 1 | $ rails test:models 2 | -------------------------------------------------------------------------------- /Listing_6.9.rb: -------------------------------------------------------------------------------- 1 | class User < ApplicationRecord 2 | validates :name, presence: true 3 | end 4 | -------------------------------------------------------------------------------- /Listing_7.1.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | . 4 | . 5 | . 6 | 7 | <%= render 'layouts/header' %> 8 |
    9 | <%= yield %> 10 | <%= render 'layouts/footer' %> 11 | <%= debug(params) if Rails.env.development? %> 12 |
    13 | 14 | 15 | -------------------------------------------------------------------------------- /Listing_7.10.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:title, @user.name) %> 2 |
    3 | 11 |
    12 | -------------------------------------------------------------------------------- /Listing_7.11.scss: -------------------------------------------------------------------------------- 1 | . 2 | . 3 | . 4 | /* sidebar */ 5 | 6 | aside { 7 | section.user_info { 8 | margin-top: 20px; 9 | } 10 | section { 11 | padding: 10px 0; 12 | margin-top: 20px; 13 | &:first-child { 14 | border: 0; 15 | padding-top: 0; 16 | } 17 | span { 18 | display: block; 19 | margin-bottom: 3px; 20 | line-height: 1; 21 | } 22 | h1 { 23 | font-size: 1.4em; 24 | text-align: left; 25 | letter-spacing: -1px; 26 | margin-bottom: 3px; 27 | margin-top: 0px; 28 | } 29 | } 30 | } 31 | 32 | .gravatar { 33 | float: left; 34 | margin-right: 10px; 35 | } 36 | 37 | .gravatar_edit { 38 | margin-top: 15px; 39 | } 40 | -------------------------------------------------------------------------------- /Listing_7.12.rb: -------------------------------------------------------------------------------- 1 | module UsersHelper 2 | 3 | # Returns the Gravatar for the given user. 4 | def gravatar_for(user, options = { size: 80 }) 5 | size = options[:size] 6 | gravatar_id = Digest::MD5::hexdigest(user.email.downcase) 7 | gravatar_url = "https://secure.gravatar.com/avatar/#{gravatar_id}?s=#{size}" 8 | image_tag(gravatar_url, alt: user.name, class: "gravatar") 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /Listing_7.13.rb: -------------------------------------------------------------------------------- 1 | module UsersHelper 2 | 3 | # Returns the Gravatar for the given user. 4 | def gravatar_for(user, size: 80) 5 | gravatar_id = Digest::MD5::hexdigest(user.email.downcase) 6 | gravatar_url = "https://secure.gravatar.com/avatar/#{gravatar_id}?s=#{size}" 7 | image_tag(gravatar_url, alt: user.name, class: "gravatar") 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /Listing_7.14.rb: -------------------------------------------------------------------------------- 1 | class UsersController < ApplicationController 2 | 3 | def show 4 | @user = User.find(params[:id]) 5 | end 6 | 7 | def new 8 | @user = User.new 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /Listing_7.15.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:title, 'Sign up') %> 2 |

    Sign up

    3 | 4 |
    5 |
    6 | <%= form_with(model: @user) do |f| %> 7 | <%= f.label :name %> 8 | <%= f.text_field :name %> 9 | 10 | <%= f.label :email %> 11 | <%= f.email_field :email %> 12 | 13 | <%= f.label :password %> 14 | <%= f.password_field :password %> 15 | 16 | <%= f.label :password_confirmation, "Confirmation" %> 17 | <%= f.password_field :password_confirmation %> 18 | 19 | <%= f.submit "Create my account", class: "btn btn-primary" %> 20 | <% end %> 21 |
    22 |
    23 | -------------------------------------------------------------------------------- /Listing_7.16.scss: -------------------------------------------------------------------------------- 1 | @import "bootstrap-sprockets"; 2 | @import "bootstrap"; 3 | 4 | /* mixins, variables, etc. */ 5 | 6 | $gray-medium-light: #eaeaea; 7 | 8 | @mixin box_sizing { 9 | -moz-box-sizing: border-box; 10 | -webkit-box-sizing: border-box; 11 | box-sizing: border-box; 12 | } 13 | . 14 | . 15 | . 16 | /* forms */ 17 | 18 | input, textarea, select, .uneditable-input { 19 | border: 1px solid #bbb; 20 | width: 100%; 21 | margin-bottom: 15px; 22 | @include box_sizing; 23 | } 24 | 25 | input { 26 | height: auto !important; 27 | } 28 | -------------------------------------------------------------------------------- /Listing_7.18.rb: -------------------------------------------------------------------------------- 1 | class UsersController < ApplicationController 2 | 3 | def show 4 | @user = User.find(params[:id]) 5 | end 6 | 7 | def new 8 | @user = User.new 9 | end 10 | 11 | def create 12 | @user = User.new(params[:user]) # Not the final implementation! 13 | if @user.save 14 | # Handle a successful save. 15 | else 16 | render 'new', status: :unprocessable_entity 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /Listing_7.19.rb: -------------------------------------------------------------------------------- 1 | class UsersController < ApplicationController 2 | . 3 | . 4 | . 5 | def create 6 | @user = User.new(user_params) 7 | if @user.save 8 | # Handle a successful save. 9 | else 10 | render 'new', status: :unprocessable_entity 11 | end 12 | end 13 | 14 | private 15 | 16 | def user_params 17 | params.require(:user).permit(:name, :email, :password, 18 | :password_confirmation) 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /Listing_7.2.scss: -------------------------------------------------------------------------------- 1 | @import "bootstrap-sprockets"; 2 | @import "bootstrap"; 3 | . 4 | . 5 | . 6 | /* miscellaneous */ 7 | 8 | .debug_dump { 9 | clear: both; 10 | float: left; 11 | width: 100%; 12 | margin-top: 45px; 13 | } 14 | -------------------------------------------------------------------------------- /Listing_7.21.html.erb: -------------------------------------------------------------------------------- 1 | <% if @user.errors.any? %> 2 |
    3 |
    4 | The form contains <%= pluralize(@user.errors.count, "error") %>. 5 |
    6 |
      7 | <% @user.errors.full_messages.each do |msg| %> 8 |
    • <%= msg %>
    • 9 | <% end %> 10 |
    11 |
    12 | <% end %> 13 | -------------------------------------------------------------------------------- /Listing_7.22.scss: -------------------------------------------------------------------------------- 1 | . 2 | . 3 | . 4 | /* forms */ 5 | . 6 | . 7 | . 8 | #error_explanation { 9 | color: red; 10 | ul { 11 | color: red; 12 | margin: 0 0 30px 0; 13 | } 14 | } 15 | 16 | .field_with_errors { 17 | @extend .has-error; 18 | .form-control { 19 | color: $state-danger-text; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Listing_7.23.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class UsersSignupTest < ActionDispatch::IntegrationTest 4 | 5 | test "invalid signup information" do 6 | get signup_path 7 | assert_no_difference 'User.count' do 8 | post users_path, params: { user: { name: "", 9 | email: "user@invalid", 10 | password: "foo", 11 | password_confirmation: "bar" } } 12 | end 13 | assert_response :unprocessable_entity 14 | assert_template 'users/new' 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /Listing_7.24.txt: -------------------------------------------------------------------------------- 1 | $ rails test 2 | -------------------------------------------------------------------------------- /Listing_7.26.rb: -------------------------------------------------------------------------------- 1 | class UsersController < ApplicationController 2 | . 3 | . 4 | . 5 | def create 6 | @user = User.new(user_params) 7 | if @user.save 8 | redirect_to @user 9 | else 10 | render 'new', status: :unprocessable_entity 11 | end 12 | end 13 | 14 | private 15 | 16 | def user_params 17 | params.require(:user).permit(:name, :email, :password, 18 | :password_confirmation) 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /Listing_7.27.rb: -------------------------------------------------------------------------------- 1 | class UsersController < ApplicationController 2 | . 3 | . 4 | . 5 | def create 6 | @user = User.new(user_params) 7 | if @user.save 8 | flash[:success] = "Welcome to the Sample App!" 9 | redirect_to @user 10 | else 11 | render 'new', status: :unprocessable_entity 12 | end 13 | end 14 | 15 | private 16 | 17 | def user_params 18 | params.require(:user).permit(:name, :email, :password, 19 | :password_confirmation) 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /Listing_7.28.rb: -------------------------------------------------------------------------------- 1 | $ rails console 2 | >> flash = { success: "It worked!", danger: "It failed." } 3 | => {:success=>"It worked!", danger: "It failed."} 4 | >> flash.each do |key, value| 5 | ?> puts "#{key}" 6 | ?> puts "#{value}" 7 | >> end 8 | success 9 | It worked! 10 | danger 11 | It failed. 12 | -------------------------------------------------------------------------------- /Listing_7.29.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | . 4 | . 5 | . 6 | 7 | <%= render 'layouts/header' %> 8 |
    9 | <% flash.each do |message_type, message| %> 10 |
    <%= message %>
    11 | <% end %> 12 | <%= yield %> 13 | <%= render 'layouts/footer' %> 14 | <%= debug(params) if Rails.env.development? %> 15 |
    16 | . 17 | . 18 | . 19 | 20 | 21 | -------------------------------------------------------------------------------- /Listing_7.3.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | root "static_pages#home" 3 | get "/help", to: "static_pages#help" 4 | get "/about", to: "static_pages#about" 5 | get "/contact", to: "static_pages#contact" 6 | get "/signup", to: "users#new" 7 | resources :users 8 | end 9 | -------------------------------------------------------------------------------- /Listing_7.30.rb: -------------------------------------------------------------------------------- 1 | $ rails console 2 | >> User.find_by(email: "example@railstutorial.org") 3 | => # 6 | -------------------------------------------------------------------------------- /Listing_7.31.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class UsersSignupTest < ActionDispatch::IntegrationTest 4 | . 5 | . 6 | . 7 | test "valid signup information" do 8 | assert_difference 'User.count', 1 do 9 | post users_path, params: { user: { name: "Example User", 10 | email: "user@example.com", 11 | password: "password", 12 | password_confirmation: "password" } } 13 | end 14 | follow_redirect! 15 | assert_template 'users/show' 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /Listing_7.32.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class UsersSignupTest < ActionDispatch::IntegrationTest 4 | . 5 | . 6 | . 7 | test "valid signup information" do 8 | assert_difference 'User.count', 1 do 9 | post users_path, params: { user: { name: "Example User", 10 | email: "user@example.com", 11 | password: "password", 12 | password_confirmation: "password" } } 13 | end 14 | follow_redirect! 15 | assert_template 'users/show' 16 | assert_not flash.FILL_IN 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /Listing_7.33.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | . 4 | . 5 | . 6 | <% flash.each do |message_type, message| %> 7 | <%= content_tag(:div, message, class: "alert alert-#{message_type}") %> 8 | <% end %> 9 | . 10 | . 11 | . 12 | 13 | -------------------------------------------------------------------------------- /Listing_7.34.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | . 3 | . 4 | . 5 | # Force all access to the app over SSL, use Strict-Transport-Security, 6 | # and use secure cookies. 7 | config.force_ssl = true 8 | . 9 | . 10 | . 11 | end 12 | -------------------------------------------------------------------------------- /Listing_7.35.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | . 3 | . 4 | . 5 | # Force all access to the app over SSL, use Strict-Transport-Security, 6 | # and use secure cookies. 7 | config.force_ssl = false 8 | . 9 | . 10 | . 11 | end 12 | -------------------------------------------------------------------------------- /Listing_7.36.rb: -------------------------------------------------------------------------------- 1 | # Puma configuration file. 2 | max_threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 } 3 | min_threads_count = ENV.fetch("RAILS_MIN_THREADS") { max_threads_count } 4 | threads min_threads_count, max_threads_count 5 | port ENV.fetch("PORT") { 3000 } 6 | environment ENV.fetch("RAILS_ENV") { ENV['RACK_ENV'] || "development" } 7 | pidfile ENV.fetch("PIDFILE") { "tmp/pids/server.pid" } 8 | workers ENV.fetch("WEB_CONCURRENCY") { 2 } 9 | preload_app! 10 | plugin :tmp_restart 11 | -------------------------------------------------------------------------------- /Listing_7.37.sh: -------------------------------------------------------------------------------- 1 | web: bundle exec puma -C config/puma.rb 2 | -------------------------------------------------------------------------------- /Listing_7.4.html.erb: -------------------------------------------------------------------------------- 1 | <%= @user.name %>, <%= @user.email %> 2 | -------------------------------------------------------------------------------- /Listing_7.5.rb: -------------------------------------------------------------------------------- 1 | class UsersController < ApplicationController 2 | 3 | def show 4 | @user = User.find(params[:id]) 5 | end 6 | 7 | def new 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /Listing_7.6.rb: -------------------------------------------------------------------------------- 1 | class UsersController < ApplicationController 2 | 3 | def show 4 | @user = User.find(params[:id]) 5 | debugger 6 | end 7 | 8 | def new 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /Listing_7.7.rb: -------------------------------------------------------------------------------- 1 | class UsersController < ApplicationController 2 | 3 | def show 4 | @user = User.find(params[:id]) 5 | end 6 | 7 | def new 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /Listing_7.8.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:title, @user.name) %> 2 |

    3 | <%= gravatar_for @user %> 4 | <%= @user.name %> 5 |

    6 | -------------------------------------------------------------------------------- /Listing_7.9.rb: -------------------------------------------------------------------------------- 1 | module UsersHelper 2 | 3 | # Returns the Gravatar for the given user. 4 | def gravatar_for(user) 5 | gravatar_id = Digest::MD5::hexdigest(user.email.downcase) 6 | gravatar_url = "https://secure.gravatar.com/avatar/#{gravatar_id}" 7 | image_tag(gravatar_url, alt: user.name, class: "gravatar") 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /Listing_8.1.txt: -------------------------------------------------------------------------------- 1 | $ rails generate controller Sessions new 2 | -------------------------------------------------------------------------------- /Listing_8.10.txt: -------------------------------------------------------------------------------- 1 | $ rails test test/integration/users_login_test.rb 2 | -------------------------------------------------------------------------------- /Listing_8.11.rb: -------------------------------------------------------------------------------- 1 | class SessionsController < ApplicationController 2 | 3 | def new 4 | end 5 | 6 | def create 7 | user = User.find_by(email: params[:session][:email].downcase) 8 | if user && user.authenticate(params[:session][:password]) 9 | # Log the user in and redirect to the user's show page. 10 | else 11 | flash.now[:danger] = 'Invalid email/password combination' 12 | render 'new', status: :unprocessable_entity 13 | end 14 | end 15 | 16 | def destroy 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /Listing_8.12.txt: -------------------------------------------------------------------------------- 1 | $ rails test test/integration/users_login_test.rb 2 | $ rails test 3 | -------------------------------------------------------------------------------- /Listing_8.13.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::Base 2 | include SessionsHelper 3 | end 4 | -------------------------------------------------------------------------------- /Listing_8.14.rb: -------------------------------------------------------------------------------- 1 | module SessionsHelper 2 | 3 | # Logs in the given user. 4 | def log_in(user) 5 | session[:user_id] = user.id 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /Listing_8.15.rb: -------------------------------------------------------------------------------- 1 | class SessionsController < ApplicationController 2 | 3 | def new 4 | end 5 | 6 | def create 7 | user = User.find_by(email: params[:session][:email].downcase) 8 | if user && user.authenticate(params[:session][:password]) 9 | reset_session 10 | log_in user 11 | redirect_to user 12 | else 13 | flash.now[:danger] = 'Invalid email/password combination' 14 | render 'new', status: :unprocessable_entity 15 | end 16 | end 17 | 18 | def destroy 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /Listing_8.16.rb: -------------------------------------------------------------------------------- 1 | module SessionsHelper 2 | 3 | # Logs in the given user. 4 | def log_in(user) 5 | session[:user_id] = user.id 6 | end 7 | 8 | # Returns the current logged-in user (if any). 9 | def current_user 10 | if session[:user_id] 11 | @current_user ||= User.find_by(id: session[:user_id]) 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /Listing_8.17.rb: -------------------------------------------------------------------------------- 1 | >> session = {} 2 | >> session[:user_id] = nil 3 | >> @current_user ||= User.find_by(id: session[:user_id]) 4 | 5 | >> session[:user_id] = User.first.id 6 | >> @current_user ||= User.find_by(id: session[:user_id]) 7 | 8 | >> @current_user ||= User.find_by(id: session[:user_id]) 9 | 10 | -------------------------------------------------------------------------------- /Listing_8.18.rb: -------------------------------------------------------------------------------- 1 | module SessionsHelper 2 | 3 | # Logs in the given user. 4 | def log_in(user) 5 | session[:user_id] = user.id 6 | end 7 | 8 | # Returns the current logged-in user (if any). 9 | def current_user 10 | if session[:user_id] 11 | @current_user ||= User.find_by(id: session[:user_id]) 12 | end 13 | end 14 | 15 | # Returns true if the user is logged in, false otherwise. 16 | def logged_in? 17 | !current_user.nil? 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /Listing_8.2.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | root "static_pages#home" 3 | get "/help", to: "static_pages#help" 4 | get "/about", to: "static_pages#about" 5 | get "/contact", to: "static_pages#contact" 6 | get "/signup", to: "users#new" 7 | get "/login", to: "sessions#new" 8 | post "/login", to: "sessions#create" 9 | delete "/logout", to: "sessions#destroy" 10 | resources :users 11 | end 12 | -------------------------------------------------------------------------------- /Listing_8.20.txt: -------------------------------------------------------------------------------- 1 | $ rails importmap:install turbo:install stimulus:install 2 | -------------------------------------------------------------------------------- /Listing_8.21.js: -------------------------------------------------------------------------------- 1 | //= link_tree ../images 2 | //= link_directory ../stylesheets .css 3 | //= link_tree ../../javascript .js 4 | //= link_tree ../../../vendor/javascript .js 5 | -------------------------------------------------------------------------------- /Listing_8.23.scss: -------------------------------------------------------------------------------- 1 | . 2 | . 3 | . 4 | /* Dropdown menu */ 5 | 6 | .dropdown-menu.active { 7 | display: block; 8 | } 9 | -------------------------------------------------------------------------------- /Listing_8.24.txt: -------------------------------------------------------------------------------- 1 | $ mkdir app/javascript/custom 2 | $ touch app/javascript/custom/menu.js 3 | -------------------------------------------------------------------------------- /Listing_8.25.js: -------------------------------------------------------------------------------- 1 | // Menu manipulation 2 | 3 | // Add toggle listeners to listen for clicks. 4 | document.addEventListener("turbo:load", function() { 5 | let account = document.querySelector("#account"); 6 | account.addEventListener("click", function(event) { 7 | event.preventDefault(); 8 | let menu = document.querySelector("#dropdown-menu"); 9 | menu.classList.toggle("active"); 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /Listing_8.26.rb: -------------------------------------------------------------------------------- 1 | # Pin npm packages by running ./bin/importmap 2 | 3 | pin "application", preload: true 4 | pin "@hotwired/turbo-rails", to: "turbo.min.js", preload: true 5 | pin "@hotwired/stimulus", to: "stimulus.min.js", preload: true 6 | pin "@hotwired/stimulus-loading", to: "stimulus-loading.js", preload: true 7 | pin_all_from "app/javascript/controllers", under: "controllers" 8 | pin_all_from "app/javascript/custom", under: "custom" 9 | -------------------------------------------------------------------------------- /Listing_8.27.js: -------------------------------------------------------------------------------- 1 | // Configure your import map in config/importmap.rb. 2 | // Read more: https://github.com/rails/importmap-rails 3 | import "@hotwired/turbo-rails" 4 | import "controllers" 5 | import "custom/menu" 6 | -------------------------------------------------------------------------------- /Listing_8.28.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <%= full_title(yield(:title)) %> 5 | 6 | 7 | <%= csrf_meta_tags %> 8 | <%= csp_meta_tag %> 9 | . 10 | . 11 | . 12 | -------------------------------------------------------------------------------- /Listing_8.29.html.erb: -------------------------------------------------------------------------------- 1 | 22 | -------------------------------------------------------------------------------- /Listing_8.3.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class SessionsControllerTest < ActionDispatch::IntegrationTest 4 | 5 | test "should get new" do 6 | get login_path 7 | assert_response :success 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /Listing_8.30.js: -------------------------------------------------------------------------------- 1 | // Menu manipulation 2 | 3 | // Add toggle listeners to listen for clicks. 4 | document.addEventListener("turbo:load", function() { 5 | let hamburger = document.querySelector("#hamburger"); 6 | hamburger.addEventListener("click", function(event) { 7 | event.preventDefault(); 8 | let menu = document.querySelector("#navbar-menu"); 9 | menu.classList.toggle("collapse"); 10 | }); 11 | 12 | let account = document.querySelector("#account"); 13 | account.addEventListener("click", function(event) { 14 | event.preventDefault(); 15 | let menu = document.querySelector("#dropdown-menu"); 16 | menu.classList.toggle("active"); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /Listing_8.31.scss: -------------------------------------------------------------------------------- 1 | . 2 | . 3 | . 4 | /* footer */ 5 | 6 | footer { 7 | . 8 | . 9 | . 10 | } 11 | @media (max-width: 800px) { 12 | footer { 13 | small { 14 | display: block; 15 | float: none; 16 | margin-bottom: 1em; 17 | } 18 | ul { 19 | float: none; 20 | padding: 0; 21 | li { 22 | float: none; 23 | margin-left: 0; 24 | } 25 | } 26 | } 27 | } 28 | . 29 | . 30 | . 31 | -------------------------------------------------------------------------------- /Listing_8.32.js: -------------------------------------------------------------------------------- 1 | // Menu manipulation 2 | 3 | // Adds a toggle listener. 4 | function addToggleListener(selected_id, menu_id, toggle_class) { 5 | let selected_element = document.querySelector(`#${selected_id}`); 6 | selected_element.addEventListener("click", function(event) { 7 | event.preventDefault(); 8 | let menu = document.querySelector(`#${menu_id}`) 9 | menu.classList.toggle(toggle_class); 10 | }); 11 | } 12 | 13 | // Add toggle listeners to listen for clicks. 14 | document.addEventListener("turbo:load", function() { 15 | addToggleListener("hamburger", "navbar-menu", "collapse"); 16 | addToggleListener("account", "dropdown-menu", "active"); 17 | }); 18 | -------------------------------------------------------------------------------- /Listing_8.34.yaml: -------------------------------------------------------------------------------- 1 | michael: 2 | name: Michael Example 3 | email: michael@example.com 4 | password_digest: <%= User.digest('password') %> 5 | -------------------------------------------------------------------------------- /Listing_8.35.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class UsersLoginTest < ActionDispatch::IntegrationTest 4 | 5 | def setup 6 | @user = users(:michael) 7 | end 8 | . 9 | . 10 | . 11 | test "login with valid information" do 12 | post login_path, params: { session: { email: @user.email, 13 | password: 'password' } } 14 | assert_redirected_to @user 15 | follow_redirect! 16 | assert_template 'users/show' 17 | assert_select "a[href=?]", login_path, count: 0 18 | assert_select "a[href=?]", logout_path 19 | assert_select "a[href=?]", user_path(@user) 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /Listing_8.36.txt: -------------------------------------------------------------------------------- 1 | $ rails test test/integration/users_login_test.rb 2 | -------------------------------------------------------------------------------- /Listing_8.37.rb: -------------------------------------------------------------------------------- 1 | class SessionsController < ApplicationController 2 | 3 | def new 4 | end 5 | 6 | def create 7 | user = User.find_by(email: params[:session][:email].downcase) 8 | if user # && user.authenticate(params[:session][:password]) 9 | reset_session 10 | log_in user 11 | redirect_to user 12 | else 13 | flash.now[:danger] = 'Invalid email/password combination' 14 | render 'new', status: :unprocessable_entity 15 | end 16 | end 17 | 18 | def destroy 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /Listing_8.38.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class UsersLoginTest < ActionDispatch::IntegrationTest 4 | 5 | def setup 6 | @user = users(:michael) 7 | end 8 | 9 | test "login with valid email/invalid password" do 10 | get login_path 11 | assert_template 'sessions/new' 12 | post login_path, params: { session: { email: FILL_IN, 13 | password: "invalid" } } 14 | assert_response :unprocessable_entity 15 | assert_template 'sessions/new' 16 | assert_not flash.empty? 17 | get root_path 18 | assert flash.empty? 19 | end 20 | . 21 | . 22 | . 23 | end 24 | -------------------------------------------------------------------------------- /Listing_8.39.rb: -------------------------------------------------------------------------------- 1 | class SessionsController < ApplicationController 2 | 3 | def new 4 | end 5 | 6 | def create 7 | user = User.find_by(email: params[:session][:email].downcase) 8 | if user&.authenticate(params[:session][:password]) 9 | reset_session 10 | log_in user 11 | redirect_to user 12 | else 13 | flash.now[:danger] = 'Invalid email/password combination' 14 | render 'new', status: :unprocessable_entity 15 | end 16 | end 17 | 18 | def destroy 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /Listing_8.4.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:title, "Log in") %> 2 |

    Log in

    3 | 4 |
    5 |
    6 | <%= form_with(url: login_path, scope: :session) do |f| %> 7 | 8 | <%= f.label :email %> 9 | <%= f.email_field :email, class: 'form-control' %> 10 | 11 | <%= f.label :password %> 12 | <%= f.password_field :password, class: 'form-control' %> 13 | 14 | <%= f.submit "Log in", class: "btn btn-primary" %> 15 | <% end %> 16 | 17 |

    New user? <%= link_to "Sign up now!", signup_path %>

    18 |
    19 |
    20 | -------------------------------------------------------------------------------- /Listing_8.40.rb: -------------------------------------------------------------------------------- 1 | class UsersController < ApplicationController 2 | 3 | def show 4 | @user = User.find(params[:id]) 5 | end 6 | 7 | def new 8 | @user = User.new 9 | end 10 | 11 | def create 12 | @user = User.new(user_params) 13 | if @user.save 14 | reset_session 15 | log_in @user 16 | flash[:success] = "Welcome to the Sample App!" 17 | redirect_to @user 18 | else 19 | render 'new', status: :unprocessable_entity 20 | end 21 | end 22 | 23 | private 24 | 25 | def user_params 26 | params.require(:user).permit(:name, :email, :password, 27 | :password_confirmation) 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /Listing_8.41.rb: -------------------------------------------------------------------------------- 1 | ENV['RAILS_ENV'] ||= 'test' 2 | . 3 | . 4 | . 5 | class ActiveSupport::TestCase 6 | # Run tests in parallel with specified workers 7 | parallelize(workers: :number_of_processors) 8 | # Set up all fixtures in test/fixtures/*.yml. 9 | fixtures :all 10 | 11 | # Returns true if a test user is logged in. 12 | def is_logged_in? 13 | !session[:user_id].nil? 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /Listing_8.42.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class UsersSignupTest < ActionDispatch::IntegrationTest 4 | . 5 | . 6 | . 7 | test "valid signup information" do 8 | assert_difference 'User.count', 1 do 9 | post users_path, params: { user: { name: "Example User", 10 | email: "user@example.com", 11 | password: "password", 12 | password_confirmation: "password" } } 13 | end 14 | follow_redirect! 15 | assert_template 'users/show' 16 | assert is_logged_in? 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /Listing_8.43.txt: -------------------------------------------------------------------------------- 1 | $ rails test 2 | -------------------------------------------------------------------------------- /Listing_8.44.rb: -------------------------------------------------------------------------------- 1 | module SessionsHelper 2 | 3 | # Logs in the given user. 4 | def log_in(user) 5 | session[:user_id] = user.id 6 | end 7 | . 8 | . 9 | . 10 | # Logs out the current user. 11 | def log_out 12 | reset_session 13 | @current_user = nil 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /Listing_8.45.rb: -------------------------------------------------------------------------------- 1 | class SessionsController < ApplicationController 2 | 3 | def new 4 | end 5 | 6 | def create 7 | user = User.find_by(email: params[:session][:email].downcase) 8 | if user && user.authenticate(params[:session][:password]) 9 | reset_session 10 | log_in user 11 | redirect_to user 12 | else 13 | flash.now[:danger] = 'Invalid email/password combination' 14 | render 'new', status: :unprocessable_entity 15 | end 16 | end 17 | 18 | def destroy 19 | log_out 20 | redirect_to root_url, status: :see_other 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /Listing_8.47.txt: -------------------------------------------------------------------------------- 1 | $ rails test 2 | -------------------------------------------------------------------------------- /Listing_8.5.html: -------------------------------------------------------------------------------- 1 |
    2 | 4 | 5 | 7 | 8 | 10 | 12 |
    13 | -------------------------------------------------------------------------------- /Listing_8.6.rb: -------------------------------------------------------------------------------- 1 | class SessionsController < ApplicationController 2 | 3 | def new 4 | end 5 | 6 | def create 7 | render 'new', status: :unprocessable_entity 8 | end 9 | 10 | def destroy 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /Listing_8.7.rb: -------------------------------------------------------------------------------- 1 | class SessionsController < ApplicationController 2 | 3 | def new 4 | end 5 | 6 | def create 7 | user = User.find_by(email: params[:session][:email].downcase) 8 | if user && user.authenticate(params[:session][:password]) 9 | # Log the user in and redirect to the user's show page. 10 | else 11 | # Create an error message. 12 | render 'new', status: :unprocessable_entity 13 | end 14 | end 15 | 16 | def destroy 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /Listing_8.8.rb: -------------------------------------------------------------------------------- 1 | class SessionsController < ApplicationController 2 | 3 | def new 4 | end 5 | 6 | def create 7 | user = User.find_by(email: params[:session][:email].downcase) 8 | if user && user.authenticate(params[:session][:password]) 9 | # Log the user in and redirect to the user's show page. 10 | else 11 | flash[:danger] = 'Invalid email/password combination' # Not quite right! 12 | render 'new', status: :unprocessable_entity 13 | end 14 | end 15 | 16 | def destroy 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /Listing_8.9.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class UsersLoginTest < ActionDispatch::IntegrationTest 4 | 5 | test "login with invalid information" do 6 | get login_path 7 | assert_template 'sessions/new' 8 | post login_path, params: { session: { email: "", password: "" } } 9 | assert_response :unprocessable_entity 10 | assert_template 'sessions/new' 11 | assert_not flash.empty? 12 | get root_path 13 | assert flash.empty? 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /Listing_9.1.rb: -------------------------------------------------------------------------------- 1 | class AddRememberDigestToUsers < ActiveRecord::Migration[7.0] 2 | def change 3 | add_column :users, :remember_digest, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /Listing_9.10.txt: -------------------------------------------------------------------------------- 1 | $ rails test 2 | -------------------------------------------------------------------------------- /Listing_9.12.rb: -------------------------------------------------------------------------------- 1 | module SessionsHelper 2 | 3 | # Logs in the given user. 4 | def log_in(user) 5 | session[:user_id] = user.id 6 | end 7 | . 8 | . 9 | . 10 | # Forgets a persistent session. 11 | def forget(user) 12 | user.forget 13 | cookies.delete(:user_id) 14 | cookies.delete(:remember_token) 15 | end 16 | 17 | # Logs out the current user. 18 | def log_out 19 | forget(current_user) 20 | reset_session 21 | @current_user = nil 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /Listing_9.13.txt: -------------------------------------------------------------------------------- 1 | $ rails test 2 | -------------------------------------------------------------------------------- /Listing_9.15.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | . 3 | . 4 | . 5 | class LogoutTest < Logout 6 | . 7 | . 8 | . 9 | test "should still work after logout in second window" do 10 | delete logout_path 11 | assert_redirected_to root_url 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /Listing_9.16.txt: -------------------------------------------------------------------------------- 1 | $ rails test 2 | -------------------------------------------------------------------------------- /Listing_9.17.rb: -------------------------------------------------------------------------------- 1 | class SessionsController < ApplicationController 2 | . 3 | . 4 | . 5 | def destroy 6 | log_out if logged_in? 7 | redirect_to root_url, status: :see_other 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /Listing_9.18.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class UserTest < ActiveSupport::TestCase 4 | 5 | def setup 6 | @user = User.new(name: "Example User", email: "user@example.com", 7 | password: "foobar", password_confirmation: "foobar") 8 | end 9 | . 10 | . 11 | . 12 | test "authenticated? should return false for a user with nil digest" do 13 | assert_not @user.authenticated?('') 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /Listing_9.19.txt: -------------------------------------------------------------------------------- 1 | $ rails test 2 | -------------------------------------------------------------------------------- /Listing_9.20.rb: -------------------------------------------------------------------------------- 1 | class User < ApplicationRecord 2 | . 3 | . 4 | . 5 | # Returns true if the given token matches the digest. 6 | def authenticated?(remember_token) 7 | return false if remember_digest.nil? 8 | BCrypt::Password.new(remember_digest).is_password?(remember_token) 9 | end 10 | 11 | # Forgets a user. 12 | def forget 13 | update_attribute(:remember_digest, nil) 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /Listing_9.21.txt: -------------------------------------------------------------------------------- 1 | $ rails test 2 | -------------------------------------------------------------------------------- /Listing_9.23.scss: -------------------------------------------------------------------------------- 1 | . 2 | . 3 | . 4 | /* forms */ 5 | . 6 | . 7 | . 8 | .checkbox { 9 | margin-top: -10px; 10 | margin-bottom: 10px; 11 | span { 12 | margin-left: 20px; 13 | font-weight: normal; 14 | } 15 | } 16 | 17 | #session_remember_me { 18 | width: auto; 19 | margin-left: 0; 20 | } 21 | . 22 | . 23 | . 24 | -------------------------------------------------------------------------------- /Listing_9.24.rb: -------------------------------------------------------------------------------- 1 | class SessionsController < ApplicationController 2 | 3 | def new 4 | end 5 | 6 | def create 7 | user = User.find_by(email: params[:session][:email].downcase) 8 | if user && user.authenticate(params[:session][:password]) 9 | reset_session 10 | params[:session][:remember_me] == '1' ? remember(user) : forget(user) 11 | log_in user 12 | redirect_to user 13 | else 14 | flash.now[:danger] = 'Invalid email/password combination' 15 | render 'new', status: :unprocessable_entity 16 | end 17 | end 18 | 19 | def destroy 20 | log_out if logged_in? 21 | redirect_to root_url, status: :see_other 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /Listing_9.26.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class UsersLoginTest < ActionDispatch::IntegrationTest 4 | 5 | def setup 6 | @user = users(:michael) 7 | end 8 | . 9 | . 10 | . 11 | test "login with remembering" do 12 | log_in_as(@user, remember_me: '1') 13 | assert_not cookies[:remember_token].blank? 14 | end 15 | 16 | test "login without remembering" do 17 | # Log in to set the cookie. 18 | log_in_as(@user, remember_me: '1') 19 | # Log in again and verify that the cookie is deleted. 20 | log_in_as(@user, remember_me: '0') 21 | assert cookies[:remember_token].blank? 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /Listing_9.27.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class UsersLoginTest < ActionDispatch::IntegrationTest 4 | 5 | def setup 6 | @user = users(:michael) 7 | end 8 | end 9 | . 10 | . 11 | . 12 | class RememberingTest < UsersLogin 13 | 14 | test "login with remembering" do 15 | log_in_as(@user, remember_me: '1') 16 | assert_not cookies[:remember_token].blank? 17 | end 18 | 19 | test "login without remembering" do 20 | # Log in to set the cookie. 21 | log_in_as(@user, remember_me: '1') 22 | # Log in again and verify that the cookie is deleted. 23 | log_in_as(@user, remember_me: '0') 24 | assert cookies[:remember_token].blank? 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /Listing_9.28.txt: -------------------------------------------------------------------------------- 1 | $ rails test 2 | -------------------------------------------------------------------------------- /Listing_9.29.rb: -------------------------------------------------------------------------------- 1 | class SessionsController < ApplicationController 2 | 3 | def new 4 | end 5 | 6 | def create 7 | ?user = User.find_by(email: params[:session][:email].downcase) 8 | if ?user && ?user.authenticate(params[:session][:password]) 9 | reset_session 10 | params[:session][:remember_me] == '1' ? remember(?user) : forget(?user) 11 | log_in ?user 12 | redirect_to ?user 13 | else 14 | flash.now[:danger] = 'Invalid email/password combination' 15 | render 'new', status: :unprocessable_entity 16 | end 17 | end 18 | 19 | def destroy 20 | log_out if logged_in? 21 | redirect_to root_url, status: :see_other 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /Listing_9.30.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class UsersLoginTest < ActionDispatch::IntegrationTest 4 | 5 | def setup 6 | @user = users(:michael) 7 | end 8 | . 9 | . 10 | . 11 | test "login with remembering" do 12 | log_in_as(@user, remember_me: '1') 13 | assert_equal FILL_IN, assigns(:user).FILL_IN 14 | end 15 | 16 | test "login without remembering" do 17 | # Log in to set the cookie. 18 | log_in_as(@user, remember_me: '1') 19 | # Log in again and verify that the cookie is deleted. 20 | log_in_as(@user, remember_me: '0') 21 | assert_empty cookies[:remember_token] 22 | end 23 | . 24 | . 25 | . 26 | end 27 | -------------------------------------------------------------------------------- /Listing_9.31.rb: -------------------------------------------------------------------------------- 1 | module SessionsHelper 2 | . 3 | . 4 | . 5 | # Returns the user corresponding to the remember token cookie. 6 | def current_user 7 | if (user_id = session[:user_id]) 8 | @current_user ||= User.find_by(id: user_id) 9 | elsif (user_id = cookies.encrypted[:user_id]) 10 | raise # The tests still pass, so this branch is currently untested. 11 | user = User.find_by(id: user_id) 12 | if user && user.authenticated?(cookies[:remember_token]) 13 | log_in user 14 | @current_user = user 15 | end 16 | end 17 | end 18 | . 19 | . 20 | . 21 | end 22 | -------------------------------------------------------------------------------- /Listing_9.32.txt: -------------------------------------------------------------------------------- 1 | $ rails test 2 | -------------------------------------------------------------------------------- /Listing_9.33.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class SessionsHelperTest < ActionView::TestCase 4 | 5 | def setup 6 | @user = users(:michael) 7 | remember(@user) 8 | end 9 | 10 | test "current_user returns right user when session is nil" do 11 | assert_equal @user, current_user 12 | assert is_logged_in? 13 | end 14 | 15 | test "current_user returns nil when remember digest is wrong" do 16 | @user.update_attribute(:remember_digest, User.digest(User.new_token)) 17 | assert_nil current_user 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /Listing_9.34.txt: -------------------------------------------------------------------------------- 1 | $ rails test test/helpers/sessions_helper_test.rb 2 | -------------------------------------------------------------------------------- /Listing_9.35.rb: -------------------------------------------------------------------------------- 1 | module SessionsHelper 2 | . 3 | . 4 | . 5 | # Returns the user corresponding to the remember token cookie. 6 | def current_user 7 | if (user_id = session[:user_id]) 8 | @current_user ||= User.find_by(id: user_id) 9 | elsif (user_id = cookies.encrypted[:user_id]) 10 | user = User.find_by(id: user_id) 11 | if user && user.authenticated?(cookies[:remember_token]) 12 | log_in user 13 | @current_user = user 14 | end 15 | end 16 | end 17 | . 18 | . 19 | . 20 | end 21 | -------------------------------------------------------------------------------- /Listing_9.36.txt: -------------------------------------------------------------------------------- 1 | $ rails test 2 | -------------------------------------------------------------------------------- /Listing_9.37.rb: -------------------------------------------------------------------------------- 1 | class User < ApplicationRecord 2 | . 3 | . 4 | . 5 | # Remembers a user in the database for use in persistent sessions. 6 | def remember 7 | self.remember_token = User.new_token 8 | update_attribute(:remember_digest, User.digest(remember_token)) 9 | remember_digest 10 | end 11 | 12 | # Returns a session token to prevent session hijacking. 13 | # We reuse the remember digest for convenience. 14 | def session_token 15 | remember_digest || remember 16 | end 17 | . 18 | . 19 | . 20 | end 21 | -------------------------------------------------------------------------------- /Listing_9.4.rb: -------------------------------------------------------------------------------- 1 | class User < ApplicationRecord 2 | . 3 | . 4 | . 5 | # Returns the hash digest of the given string. 6 | def self.digest(string) 7 | cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST : 8 | BCrypt::Engine.cost 9 | BCrypt::Password.create(string, cost: cost) 10 | end 11 | 12 | # Returns a random token. 13 | def self.new_token 14 | SecureRandom.urlsafe_base64 15 | end 16 | . 17 | . 18 | . 19 | end 20 | -------------------------------------------------------------------------------- /Listing_9.5.rb: -------------------------------------------------------------------------------- 1 | class User < ApplicationRecord 2 | . 3 | . 4 | . 5 | class << self 6 | # Returns the hash digest of the given string. 7 | def digest(string) 8 | cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST : 9 | BCrypt::Engine.cost 10 | BCrypt::Password.create(string, cost: cost) 11 | end 12 | 13 | # Returns a random token. 14 | def new_token 15 | SecureRandom.urlsafe_base64 16 | end 17 | end 18 | . 19 | . 20 | . 21 | -------------------------------------------------------------------------------- /Listing_9.7.rb: -------------------------------------------------------------------------------- 1 | class SessionsController < ApplicationController 2 | 3 | def new 4 | end 5 | 6 | def create 7 | user = User.find_by(email: params[:session][:email].downcase) 8 | if user && user.authenticate(params[:session][:password]) 9 | reset_session 10 | remember user 11 | log_in user 12 | redirect_to user 13 | else 14 | flash.now[:danger] = 'Invalid email/password combination' 15 | render 'new', status: :unprocessable_entity 16 | end 17 | end 18 | 19 | def destroy 20 | log_out 21 | redirect_to root_url, status: :see_other 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ruby on Rails Tutorial Code Listings 2 | 3 | This repository contains files for all the code listings in the *Ruby on Rails Tutorial* (7th edition) by Michael Hartl. 4 | --------------------------------------------------------------------------------