├── .browserslistrc ├── .gitignore ├── .ruby-version ├── Gemfile ├── Gemfile.lock ├── Guardfile ├── HELP.md ├── LICENSE.md ├── Procfile ├── README.md ├── Rakefile ├── app ├── assets │ ├── config │ │ └── manifest.js │ ├── images │ │ ├── .keep │ │ └── rails.svg │ └── stylesheets │ │ ├── account_activations.scss │ │ ├── application.css │ │ ├── custom.scss │ │ ├── microposts.scss │ │ ├── password_resets.scss │ │ ├── relationships.scss │ │ ├── sessions.scss │ │ ├── static_pages.scss │ │ └── users.scss ├── channels │ └── application_cable │ │ ├── channel.rb │ │ └── connection.rb ├── controllers │ ├── account_activations_controller.rb │ ├── application_controller.rb │ ├── concerns │ │ └── .keep │ ├── microposts_controller.rb │ ├── password_resets_controller.rb │ ├── relationships_controller.rb │ ├── sessions_controller.rb │ ├── static_pages_controller.rb │ └── users_controller.rb ├── helpers │ ├── account_activations_helper.rb │ ├── application_helper.rb │ ├── microposts_helper.rb │ ├── password_resets_helper.rb │ ├── relationships_helper.rb │ ├── sessions_helper.rb │ ├── static_pages_helper.rb │ └── users_helper.rb ├── javascript │ ├── channels │ │ ├── consumer.js │ │ └── index.js │ └── packs │ │ └── application.js ├── jobs │ └── application_job.rb ├── mailers │ ├── application_mailer.rb │ └── user_mailer.rb ├── models │ ├── application_record.rb │ ├── concerns │ │ └── .keep │ ├── micropost.rb │ ├── relationship.rb │ └── user.rb └── views │ ├── layouts │ ├── _footer.html.erb │ ├── _header.html.erb │ ├── _shim.html.erb │ ├── application.html.erb │ ├── mailer.html.erb │ └── mailer.text.erb │ ├── microposts │ └── _micropost.html.erb │ ├── password_resets │ ├── edit.html.erb │ └── new.html.erb │ ├── relationships │ ├── create.js.erb │ └── destroy.js.erb │ ├── sessions │ └── new.html.erb │ ├── shared │ ├── _error_messages.html.erb │ ├── _feed.html.erb │ ├── _micropost_form.html.erb │ ├── _stats.html.erb │ └── _user_info.html.erb │ ├── static_pages │ ├── about.html.erb │ ├── contact.html.erb │ ├── help.html.erb │ └── home.html.erb │ ├── user_mailer │ ├── account_activation.html.erb │ ├── account_activation.text.erb │ ├── password_reset.html.erb │ └── password_reset.text.erb │ └── users │ ├── _follow.html.erb │ ├── _follow_form.html.erb │ ├── _unfollow.html.erb │ ├── _user.html.erb │ ├── edit.html.erb │ ├── index.html.erb │ ├── new.html.erb │ ├── show.html.erb │ └── show_follow.html.erb ├── babel.config.js ├── bin ├── bundle ├── rails ├── rake ├── setup ├── webpack ├── webpack-dev-server └── yarn ├── config.ru ├── config ├── application.rb ├── boot.rb ├── cable.yml ├── credentials.yml.enc ├── database.yml ├── environment.rb ├── environments │ ├── development.rb │ ├── production.rb │ └── test.rb ├── initializers │ ├── application_controller_renderer.rb │ ├── assets.rb │ ├── backtrace_silencers.rb │ ├── content_security_policy.rb │ ├── cookies_serializer.rb │ ├── filter_parameter_logging.rb │ ├── inflections.rb │ ├── mime_types.rb │ └── wrap_parameters.rb ├── locales │ └── en.yml ├── puma.rb ├── routes.rb ├── spring.rb ├── storage.yml ├── webpack │ ├── development.js │ ├── environment.js │ ├── production.js │ └── test.js └── webpacker.yml ├── db ├── migrate │ ├── 20190822013911_create_users.rb │ ├── 20190822021835_add_index_to_users_email.rb │ ├── 20190822031056_add_password_digest_to_users.rb │ ├── 20190823000019_add_remember_digest_to_users.rb │ ├── 20190823171209_add_admin_to_users.rb │ ├── 20190823175841_add_activation_to_users.rb │ ├── 20190824013003_add_reset_to_users.rb │ ├── 20190824113338_create_microposts.rb │ ├── 20190827011913_create_active_storage_tables.active_storage.rb │ └── 20190827030205_create_relationships.rb ├── schema.rb └── seeds.rb ├── lib ├── assets │ └── .keep └── tasks │ └── .keep ├── log └── .keep ├── package.json ├── postcss.config.js ├── public ├── 404.html ├── 422.html ├── 500.html ├── apple-touch-icon-precomposed.png ├── apple-touch-icon.png ├── favicon.ico └── robots.txt ├── storage └── .keep ├── test ├── application_system_test_case.rb ├── channels │ └── application_cable │ │ └── connection_test.rb ├── controllers │ ├── .keep │ ├── account_activations_controller_test.rb │ ├── microposts_controller_test.rb │ ├── relationships_controller_test.rb │ ├── sessions_controller_test.rb │ ├── static_pages_controller_test.rb │ └── users_controller_test.rb ├── fixtures │ ├── .keep │ ├── files │ │ ├── .keep │ │ └── kitten.jpg │ ├── microposts.yml │ ├── relationships.yml │ └── users.yml ├── helpers │ ├── .keep │ └── sessions_helper_test.rb ├── integration │ ├── .keep │ ├── following_test.rb │ ├── microposts_interface_test.rb │ ├── password_resets_test.rb │ ├── site_layout_test.rb │ ├── users_edit_test.rb │ ├── users_index_test.rb │ ├── users_login_test.rb │ ├── users_profile_test.rb │ └── users_signup_test.rb ├── mailers │ ├── .keep │ ├── previews │ │ └── user_mailer_preview.rb │ └── user_mailer_test.rb ├── models │ ├── .keep │ ├── micropost_test.rb │ ├── relationship_test.rb │ └── user_test.rb ├── system │ └── .keep └── test_helper.rb ├── tmp └── .keep ├── vendor └── .keep └── yarn.lock /.browserslistrc: -------------------------------------------------------------------------------- 1 | defaults 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files for more about ignoring files. 2 | # 3 | # If you find yourself ignoring temporary files generated by your text editor 4 | # or operating system, you probably want to add a global ignore instead: 5 | # git config --global core.excludesfile '~/.gitignore_global' 6 | 7 | # Ignore bundler config. 8 | /.bundle 9 | 10 | # Ignore the default SQLite database. 11 | /db/*.sqlite3 12 | /db/*.sqlite3-journal 13 | 14 | # Ignore all logfiles and tempfiles. 15 | /log/* 16 | /tmp/* 17 | !/log/.keep 18 | !/tmp/.keep 19 | 20 | # Ignore uploaded files in development. 21 | /storage/* 22 | !/storage/.keep 23 | 24 | /public/assets 25 | .byebug_history 26 | 27 | # Ignore master key for decrypting credentials and more. 28 | /config/master.key 29 | 30 | /public/packs 31 | /public/packs-test 32 | /node_modules 33 | /yarn-error.log 34 | yarn-debug.log* 35 | .yarn-integrity 36 | 37 | # Ignore macOS DS_Store. 38 | .DS_Store 39 | 40 | # Ignore db test files. 41 | db/test.* 42 | -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | 2.7.5 2 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | git_source(:github) { |repo| "https://github.com/#{repo}.git" } 3 | 4 | ruby '2.7.5' 5 | 6 | gem 'rails', '6.1.4.6' 7 | gem 'image_processing', '1.9.3' 8 | gem 'mini_magick', '4.9.5' 9 | gem 'active_storage_validations', '0.8.9' 10 | gem 'bcrypt', '3.1.13' 11 | gem 'faker', '2.11.0' 12 | gem 'will_paginate', '3.3.0' 13 | gem 'bootstrap-will_paginate', '1.0.0' 14 | gem 'bootstrap-sass', '3.4.1' 15 | gem 'puma', '5.3.1' 16 | gem 'sass-rails', '6.0.0' 17 | gem 'webpacker', '5.4.0' 18 | gem 'turbolinks', '5.2.1' 19 | gem 'jbuilder', '2.10.0' 20 | gem 'bootsnap', '1.7.2', require: false 21 | 22 | group :development, :test do 23 | gem 'sqlite3', '1.4.2' 24 | gem 'byebug', '11.1.3', platforms: [:mri, :mingw, :x64_mingw] 25 | end 26 | 27 | group :development do 28 | gem 'web-console', '4.1.0' 29 | gem 'rack-mini-profiler', '2.3.1' 30 | gem 'listen', '3.4.1' 31 | gem 'spring', '2.1.1' 32 | end 33 | 34 | group :test do 35 | gem 'capybara', '3.35.3' 36 | gem 'selenium-webdriver', '3.142.7' 37 | gem 'webdrivers', '4.6.0' 38 | gem 'rails-controller-testing', '1.0.5' 39 | gem 'minitest', '5.11.3' 40 | gem 'minitest-reporters', '1.3.8' 41 | gem 'guard', '2.16.2' 42 | gem 'guard-minitest', '2.4.6' 43 | end 44 | 45 | group :production do 46 | gem 'pg', '1.2.3' 47 | gem 'aws-sdk-s3', '1.87.0', require: false 48 | end 49 | 50 | # Windows does not include zoneinfo files, so bundle the tzinfo-data gem 51 | # Uncomment the following line if you're running Rails 52 | # on a native Windows system: 53 | # gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] 54 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | actioncable (6.1.4.6) 5 | actionpack (= 6.1.4.6) 6 | activesupport (= 6.1.4.6) 7 | nio4r (~> 2.0) 8 | websocket-driver (>= 0.6.1) 9 | actionmailbox (6.1.4.6) 10 | actionpack (= 6.1.4.6) 11 | activejob (= 6.1.4.6) 12 | activerecord (= 6.1.4.6) 13 | activestorage (= 6.1.4.6) 14 | activesupport (= 6.1.4.6) 15 | mail (>= 2.7.1) 16 | actionmailer (6.1.4.6) 17 | actionpack (= 6.1.4.6) 18 | actionview (= 6.1.4.6) 19 | activejob (= 6.1.4.6) 20 | activesupport (= 6.1.4.6) 21 | mail (~> 2.5, >= 2.5.4) 22 | rails-dom-testing (~> 2.0) 23 | actionpack (6.1.4.6) 24 | actionview (= 6.1.4.6) 25 | activesupport (= 6.1.4.6) 26 | rack (~> 2.0, >= 2.0.9) 27 | rack-test (>= 0.6.3) 28 | rails-dom-testing (~> 2.0) 29 | rails-html-sanitizer (~> 1.0, >= 1.2.0) 30 | actiontext (6.1.4.6) 31 | actionpack (= 6.1.4.6) 32 | activerecord (= 6.1.4.6) 33 | activestorage (= 6.1.4.6) 34 | activesupport (= 6.1.4.6) 35 | nokogiri (>= 1.8.5) 36 | actionview (6.1.4.6) 37 | activesupport (= 6.1.4.6) 38 | builder (~> 3.1) 39 | erubi (~> 1.4) 40 | rails-dom-testing (~> 2.0) 41 | rails-html-sanitizer (~> 1.1, >= 1.2.0) 42 | active_storage_validations (0.8.9) 43 | rails (>= 5.2.0) 44 | activejob (6.1.4.6) 45 | activesupport (= 6.1.4.6) 46 | globalid (>= 0.3.6) 47 | activemodel (6.1.4.6) 48 | activesupport (= 6.1.4.6) 49 | activerecord (6.1.4.6) 50 | activemodel (= 6.1.4.6) 51 | activesupport (= 6.1.4.6) 52 | activestorage (6.1.4.6) 53 | actionpack (= 6.1.4.6) 54 | activejob (= 6.1.4.6) 55 | activerecord (= 6.1.4.6) 56 | activesupport (= 6.1.4.6) 57 | marcel (~> 1.0.0) 58 | mini_mime (>= 1.1.0) 59 | activesupport (6.1.4.6) 60 | concurrent-ruby (~> 1.0, >= 1.0.2) 61 | i18n (>= 1.6, < 2) 62 | minitest (>= 5.1) 63 | tzinfo (~> 2.0) 64 | zeitwerk (~> 2.3) 65 | addressable (2.8.0) 66 | public_suffix (>= 2.0.2, < 5.0) 67 | ansi (1.5.0) 68 | autoprefixer-rails (10.4.7.0) 69 | execjs (~> 2) 70 | aws-eventstream (1.2.0) 71 | aws-partitions (1.593.0) 72 | aws-sdk-core (3.131.1) 73 | aws-eventstream (~> 1, >= 1.0.2) 74 | aws-partitions (~> 1, >= 1.525.0) 75 | aws-sigv4 (~> 1.1) 76 | jmespath (~> 1, >= 1.6.1) 77 | aws-sdk-kms (1.57.0) 78 | aws-sdk-core (~> 3, >= 3.127.0) 79 | aws-sigv4 (~> 1.1) 80 | aws-sdk-s3 (1.87.0) 81 | aws-sdk-core (~> 3, >= 3.109.0) 82 | aws-sdk-kms (~> 1) 83 | aws-sigv4 (~> 1.1) 84 | aws-sigv4 (1.5.0) 85 | aws-eventstream (~> 1, >= 1.0.2) 86 | bcrypt (3.1.13) 87 | bindex (0.8.1) 88 | bootsnap (1.7.2) 89 | msgpack (~> 1.0) 90 | bootstrap-sass (3.4.1) 91 | autoprefixer-rails (>= 5.2.1) 92 | sassc (>= 2.0.0) 93 | bootstrap-will_paginate (1.0.0) 94 | will_paginate 95 | builder (3.2.4) 96 | byebug (11.1.3) 97 | capybara (3.35.3) 98 | addressable 99 | mini_mime (>= 0.1.3) 100 | nokogiri (~> 1.8) 101 | rack (>= 1.6.0) 102 | rack-test (>= 0.6.3) 103 | regexp_parser (>= 1.5, < 3.0) 104 | xpath (~> 3.2) 105 | childprocess (3.0.0) 106 | coderay (1.1.3) 107 | concurrent-ruby (1.1.10) 108 | crass (1.0.6) 109 | erubi (1.10.0) 110 | execjs (2.8.1) 111 | faker (2.11.0) 112 | i18n (>= 1.6, < 2) 113 | ffi (1.15.5) 114 | formatador (1.1.0) 115 | globalid (1.0.0) 116 | activesupport (>= 5.0) 117 | guard (2.16.2) 118 | formatador (>= 0.2.4) 119 | listen (>= 2.7, < 4.0) 120 | lumberjack (>= 1.0.12, < 2.0) 121 | nenv (~> 0.1) 122 | notiffany (~> 0.0) 123 | pry (>= 0.9.12) 124 | shellany (~> 0.0) 125 | thor (>= 0.18.1) 126 | guard-compat (1.2.1) 127 | guard-minitest (2.4.6) 128 | guard-compat (~> 1.2) 129 | minitest (>= 3.0) 130 | i18n (1.10.0) 131 | concurrent-ruby (~> 1.0) 132 | image_processing (1.9.3) 133 | mini_magick (>= 4.9.5, < 5) 134 | ruby-vips (>= 2.0.13, < 3) 135 | jbuilder (2.10.0) 136 | activesupport (>= 5.0.0) 137 | jmespath (1.6.1) 138 | listen (3.4.1) 139 | rb-fsevent (~> 0.10, >= 0.10.3) 140 | rb-inotify (~> 0.9, >= 0.9.10) 141 | loofah (2.18.0) 142 | crass (~> 1.0.2) 143 | nokogiri (>= 1.5.9) 144 | lumberjack (1.2.8) 145 | mail (2.7.1) 146 | mini_mime (>= 0.1.1) 147 | marcel (1.0.2) 148 | method_source (1.0.0) 149 | mini_magick (4.9.5) 150 | mini_mime (1.1.2) 151 | minitest (5.11.3) 152 | minitest-reporters (1.3.8) 153 | ansi 154 | builder 155 | minitest (>= 5.0) 156 | ruby-progressbar 157 | msgpack (1.5.1) 158 | nenv (0.3.0) 159 | nio4r (2.5.8) 160 | nokogiri (1.13.6-x86_64-darwin) 161 | racc (~> 1.4) 162 | notiffany (0.1.3) 163 | nenv (~> 0.1) 164 | shellany (~> 0.0) 165 | pg (1.2.3) 166 | pry (0.14.1) 167 | coderay (~> 1.1) 168 | method_source (~> 1.0) 169 | public_suffix (4.0.7) 170 | puma (5.3.1) 171 | nio4r (~> 2.0) 172 | racc (1.6.0) 173 | rack (2.2.3) 174 | rack-mini-profiler (2.3.1) 175 | rack (>= 1.2.0) 176 | rack-proxy (0.7.2) 177 | rack 178 | rack-test (1.1.0) 179 | rack (>= 1.0, < 3) 180 | rails (6.1.4.6) 181 | actioncable (= 6.1.4.6) 182 | actionmailbox (= 6.1.4.6) 183 | actionmailer (= 6.1.4.6) 184 | actionpack (= 6.1.4.6) 185 | actiontext (= 6.1.4.6) 186 | actionview (= 6.1.4.6) 187 | activejob (= 6.1.4.6) 188 | activemodel (= 6.1.4.6) 189 | activerecord (= 6.1.4.6) 190 | activestorage (= 6.1.4.6) 191 | activesupport (= 6.1.4.6) 192 | bundler (>= 1.15.0) 193 | railties (= 6.1.4.6) 194 | sprockets-rails (>= 2.0.0) 195 | rails-controller-testing (1.0.5) 196 | actionpack (>= 5.0.1.rc1) 197 | actionview (>= 5.0.1.rc1) 198 | activesupport (>= 5.0.1.rc1) 199 | rails-dom-testing (2.0.3) 200 | activesupport (>= 4.2.0) 201 | nokogiri (>= 1.6) 202 | rails-html-sanitizer (1.4.2) 203 | loofah (~> 2.3) 204 | railties (6.1.4.6) 205 | actionpack (= 6.1.4.6) 206 | activesupport (= 6.1.4.6) 207 | method_source 208 | rake (>= 0.13) 209 | thor (~> 1.0) 210 | rake (13.0.6) 211 | rb-fsevent (0.11.1) 212 | rb-inotify (0.10.1) 213 | ffi (~> 1.0) 214 | regexp_parser (2.4.0) 215 | ruby-progressbar (1.11.0) 216 | ruby-vips (2.1.4) 217 | ffi (~> 1.12) 218 | rubyzip (2.3.2) 219 | sass-rails (6.0.0) 220 | sassc-rails (~> 2.1, >= 2.1.1) 221 | sassc (2.4.0) 222 | ffi (~> 1.9) 223 | sassc-rails (2.1.2) 224 | railties (>= 4.0.0) 225 | sassc (>= 2.0) 226 | sprockets (> 3.0) 227 | sprockets-rails 228 | tilt 229 | selenium-webdriver (3.142.7) 230 | childprocess (>= 0.5, < 4.0) 231 | rubyzip (>= 1.2.2) 232 | semantic_range (3.0.0) 233 | shellany (0.0.1) 234 | spring (2.1.1) 235 | sprockets (4.0.3) 236 | concurrent-ruby (~> 1.0) 237 | rack (> 1, < 3) 238 | sprockets-rails (3.4.2) 239 | actionpack (>= 5.2) 240 | activesupport (>= 5.2) 241 | sprockets (>= 3.0.0) 242 | sqlite3 (1.4.2) 243 | thor (1.2.1) 244 | tilt (2.0.10) 245 | turbolinks (5.2.1) 246 | turbolinks-source (~> 5.2) 247 | turbolinks-source (5.2.0) 248 | tzinfo (2.0.4) 249 | concurrent-ruby (~> 1.0) 250 | web-console (4.1.0) 251 | actionview (>= 6.0.0) 252 | activemodel (>= 6.0.0) 253 | bindex (>= 0.4.0) 254 | railties (>= 6.0.0) 255 | webdrivers (4.6.0) 256 | nokogiri (~> 1.6) 257 | rubyzip (>= 1.3.0) 258 | selenium-webdriver (>= 3.0, < 4.0) 259 | webpacker (5.4.0) 260 | activesupport (>= 5.2) 261 | rack-proxy (>= 0.6.1) 262 | railties (>= 5.2) 263 | semantic_range (>= 2.3.0) 264 | websocket-driver (0.7.5) 265 | websocket-extensions (>= 0.1.0) 266 | websocket-extensions (0.1.5) 267 | will_paginate (3.3.0) 268 | xpath (3.2.0) 269 | nokogiri (~> 1.8) 270 | zeitwerk (2.5.4) 271 | 272 | PLATFORMS 273 | x86_64-darwin-19 274 | 275 | DEPENDENCIES 276 | active_storage_validations (= 0.8.9) 277 | aws-sdk-s3 (= 1.87.0) 278 | bcrypt (= 3.1.13) 279 | bootsnap (= 1.7.2) 280 | bootstrap-sass (= 3.4.1) 281 | bootstrap-will_paginate (= 1.0.0) 282 | byebug (= 11.1.3) 283 | capybara (= 3.35.3) 284 | faker (= 2.11.0) 285 | guard (= 2.16.2) 286 | guard-minitest (= 2.4.6) 287 | image_processing (= 1.9.3) 288 | jbuilder (= 2.10.0) 289 | listen (= 3.4.1) 290 | mini_magick (= 4.9.5) 291 | minitest (= 5.11.3) 292 | minitest-reporters (= 1.3.8) 293 | pg (= 1.2.3) 294 | puma (= 5.3.1) 295 | rack-mini-profiler (= 2.3.1) 296 | rails (= 6.1.4.6) 297 | rails-controller-testing (= 1.0.5) 298 | sass-rails (= 6.0.0) 299 | selenium-webdriver (= 3.142.7) 300 | spring (= 2.1.1) 301 | sqlite3 (= 1.4.2) 302 | turbolinks (= 5.2.1) 303 | web-console (= 4.1.0) 304 | webdrivers (= 4.6.0) 305 | webpacker (= 5.4.0) 306 | will_paginate (= 3.3.0) 307 | 308 | RUBY VERSION 309 | ruby 2.7.5p203 310 | 311 | BUNDLED WITH 312 | 2.2.31 313 | -------------------------------------------------------------------------------- /Guardfile: -------------------------------------------------------------------------------- 1 | # This is the Guardfile for the 6th edition of the Rails Tutorial. 2 | # For the 7th edition, see here: 3 | # https://github.com/learnenough/rails_tutorial_sample_app_7th_ed/blob/main/Guardfile 4 | 5 | require 'active_support/core_ext/string' 6 | # Defines the matching rules for Guard. 7 | guard :minitest, spring: "bin/rails test", all_on_start: false do 8 | watch(%r{^test/(.*)/?(.*)_test\.rb$}) 9 | watch('test/test_helper.rb') { 'test' } 10 | watch('config/routes.rb') { interface_tests } 11 | watch(%r{app/views/layouts/*}) { interface_tests } 12 | watch(%r{^app/models/(.*?)\.rb$}) do |matches| 13 | ["test/models/#{matches[1]}_test.rb", 14 | "test/integration/microposts_interface_test.rb"] 15 | end 16 | watch(%r{^test/fixtures/(.*?)\.yml$}) do |matches| 17 | "test/models/#{matches[1].singularize}_test.rb" 18 | end 19 | watch(%r{^app/mailers/(.*?)\.rb$}) do |matches| 20 | "test/mailers/#{matches[1]}_test.rb" 21 | end 22 | watch(%r{^app/views/(.*)_mailer/.*$}) do |matches| 23 | "test/mailers/#{matches[1]}_mailer_test.rb" 24 | end 25 | watch(%r{^app/controllers/(.*?)_controller\.rb$}) do |matches| 26 | resource_tests(matches[1]) 27 | end 28 | watch(%r{^app/views/([^/]*?)/.*\.html\.erb$}) do |matches| 29 | ["test/controllers/#{matches[1]}_controller_test.rb"] + 30 | integration_tests(matches[1]) 31 | end 32 | watch(%r{^app/helpers/(.*?)_helper\.rb$}) do |matches| 33 | integration_tests(matches[1]) 34 | end 35 | watch('app/views/layouts/application.html.erb') do 36 | 'test/integration/site_layout_test.rb' 37 | end 38 | watch('app/helpers/sessions_helper.rb') do 39 | integration_tests << 'test/helpers/sessions_helper_test.rb' 40 | end 41 | watch('app/controllers/sessions_controller.rb') do 42 | ['test/controllers/sessions_controller_test.rb', 43 | 'test/integration/users_login_test.rb'] 44 | end 45 | watch('app/controllers/account_activations_controller.rb') do 46 | 'test/integration/users_signup_test.rb' 47 | end 48 | watch(%r{app/views/users/*}) do 49 | resource_tests('users') + 50 | ['test/integration/microposts_interface_test.rb'] 51 | end 52 | end 53 | 54 | # Returns the integration tests corresponding to the given resource. 55 | def integration_tests(resource = :all) 56 | if resource == :all 57 | Dir["test/integration/*"] 58 | else 59 | Dir["test/integration/#{resource}_*.rb"] 60 | end 61 | end 62 | 63 | # Returns all tests that hit the interface. 64 | def interface_tests 65 | integration_tests << "test/controllers" 66 | end 67 | 68 | # Returns the controller tests corresponding to the given resource. 69 | def controller_test(resource) 70 | "test/controllers/#{resource}_controller_test.rb" 71 | end 72 | 73 | # Returns all tests for the given resource. 74 | def resource_tests(resource) 75 | integration_tests(resource) << controller_test(resource) 76 | end 77 | -------------------------------------------------------------------------------- /HELP.md: -------------------------------------------------------------------------------- 1 | # Rails Tutorial Help 2 | 3 | This is the Help page for the [*Ruby on Rails Tutorial*](https://www.railstutorial.org/) (6th Edition) by [Michael Hartl](http://www.michaelhartl.com/). 4 | 5 | See also the [7th edition Help page](https://github.com/learnenough/rails_tutorial_sample_app_7th_ed/blob/main/HELP.md). 6 | 7 | 8 | ## General suggestions 9 | 10 | Web development is a tricky business, and despite our collective best efforts it’s likely that you’ll run into trouble at some point. If you do, I suggest comparing your code to the [reference implementation of the sample app](https://github.com/learnenough/sample_app_6th_ed) to track down any discrepancies. You can also post your question at [Stack Overflow](https://stackoverflow.com/), but I suggest you do so after trying all the debugging tips listed below. 11 | 12 | ## Debugging tips 13 | 14 | While it’s impossible to anticipate every potential problem, here are 15 | some debugging tips that might help: 16 | 17 | - Have you compared your code to the [reference implementation of the sample app](https://github.com/learnenough/sample_app_6th_ed)? 18 | - Are you using the [exact gem versions](https://gemfiles-6th-ed.railstutorial.org) (including Rails) used in the 19 | tutorial? 20 | - Did you try Googling the error message? 21 | - Did you stop the Rails web server (with Ctrl-C) and restart? 22 | - Did you try stopping Spring using `bin/spring stop`? 23 | - Did you copy-and-paste from the book’s code? (Experience shows that typing in code, while a better learning technique in general, is error-prone, so when in doubt be sure to copy all code exactly.) 24 | - Did you try Googling the error message? 25 | - Did you re-run `bundle install`? 26 | - Did you try running `bundle update`? 27 | - Did you examine the Heroku logs using `heroku logs` or `heroku logs --tail`? 28 | - Did you make sure the `sqlite3` gem is listed only in the `development` and `test` environments in the `Gemfile`? 29 | - Did you look for the answer at Stack Overflow? 30 | - Did you try Googling the error message? 31 | 32 | If your problem is of a general nature, such as having issues installing 33 | Rails or configuring your system, I suggest posting to [Stack 34 | Overflow](http://stackoverflow.com/). This will allow other 35 | people running into your issue (and not just those following the Rails 36 | Tutorial) to benefit from the discussion. For issues 37 | deploying to [Heroku](http://heroku.com/), please contact [Heroku 38 | technical support](http://support.heroku.com/). 39 | 40 | When asking your question on any mailing list or forum, be sure to 41 | include as much relevant information as possible. To maximize your chances 42 | of a helpful reply, I especially recommend the article [How To Ask Questions The Smart 43 | Way](http://www.catb.org/esr/faqs/smart-questions.html) by [Eric 44 | Raymond](http://www.catb.org/esr/). 45 | 46 | ## Error reports 47 | 48 | Suspected errors, typos, and bugs can be emailed to . All such reports are gratefully received, but please double-check with the [online version of the tutorial](https://www.railstutorial.org/book) and the [reference implementation](https://github.com/learnenough/sample_app_6th_ed) before submitting. 49 | 50 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | All source code in the [Ruby on Rails Tutorial](http://railstutorial.org/) is available jointly under the MIT License and the Beerware License. 2 | 3 | ``` 4 | The MIT License 5 | 6 | Copyright (c) 2016 Michael Hartl 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in 16 | all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | THE SOFTWARE. 25 | ``` 26 | 27 | ``` 28 | THE BEERWARE LICENSE (Revision 42) 29 | 30 | Michael Hartl wrote this code. As long as you retain this notice you can do 31 | whatever you want with this stuff. If we meet some day, and you think this 32 | stuff is worth it, you can buy me a beer in return. 33 | ``` 34 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: bundle exec puma -C config/puma.rb -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ruby on Rails Tutorial sample application 2 | 3 | ## Reference implementation 4 | 5 | This is the reference implementation of the sample application from 6 | [*Ruby on Rails Tutorial: 7 | Learn Web Development with Rails*](https://www.railstutorial.org/) 8 | (6th Edition) 9 | by [Michael Hartl](http://www.michaelhartl.com/). 10 | 11 | See also the [7th edition README](https://github.com/learnenough/rails_tutorial_sample_app_7th_ed#readme). 12 | 13 | ## License 14 | 15 | All source code in the [Ruby on Rails Tutorial](https://www.railstutorial.org/) 16 | is available jointly under the MIT License and the Beerware License. See 17 | [LICENSE.md](LICENSE.md) for details. 18 | 19 | ## Getting started 20 | 21 | To get started with the app, first follow the setup steps in [Section 1.1 Up and running](https://www.railstutorial.org/book#sec-up_and_running). 22 | 23 | Next, clone the repo and `cd` into the directory: 24 | 25 | ``` 26 | $ git clone https://github.com/mhartl/sample_app_6th_ed.git 27 | $ cd sample_app_6th_ed 28 | ``` 29 | 30 | Also make sure you’re using a compatible version of Node.js: 31 | 32 | ``` 33 | $ nvm install 16.13.0 34 | $ node -v 35 | v16.13.0 36 | ``` 37 | 38 | Then install the needed packages (while skipping any Ruby gems needed only in production): 39 | 40 | ``` 41 | $ yarn add jquery@3.5.1 bootstrap@3.4.1 42 | $ gem install bundler -v 2.2.17 43 | $ bundle _2.2.17_ config set --local without 'production' 44 | $ bundle _2.2.17_ install 45 | ``` 46 | 47 | Next, migrate the database: 48 | 49 | ``` 50 | $ rails db:migrate 51 | ``` 52 | 53 | Finally, run the test suite to verify that everything is working correctly: 54 | 55 | ``` 56 | $ rails test 57 | ``` 58 | 59 | If the test suite passes, you’ll be ready to seed the database with sample users and run the app in a local server: 60 | 61 | ``` 62 | $ rails db:seed 63 | $ rails server 64 | ``` 65 | 66 | Follow the instructions in [Section 1.2.2 `rails server`](https://www.railstutorial.org/book#sec-rails_server) to view the app. You can then register a new user or log in as the sample administrative user with the email `example@railstutorial.org` and password `foobar`. 67 | 68 | ## Deploying 69 | 70 | To deploy the sample app to production, you’ll need a Heroku account as discussed [Section 1.4 Deploying](https://www.railstutorial.org/book/beginning#sec-deploying). 71 | 72 | The full production app includes several advanced features, including sending email with [SendGrid](https://sendgrid.com/) and storing uploaded images with [AWS S3](https://aws.amazon.com/s3/). As a result, deploying the full sample app can be rather challenging. The suggested method for testing a deployment is to use the branch for Chapter 10 (“Updating users”), which doesn’t require more advanced settings but still includes sample users. 73 | 74 | To deploy this version of the app, you’ll need to create a new Heroku application, switch to the right branch, push up the source, run the migrations, and seed the database with sample users: 75 | 76 | ``` 77 | $ heroku create 78 | $ git checkout updating-users 79 | $ git push heroku updating-users:main 80 | $ heroku run rails db:migrate 81 | $ heroku run rails db:seed 82 | ``` 83 | 84 | Visiting the URL returned by the original `heroku create` should now show you the sample app running in production. As with the local version, you can then register a new user or log in as the sample administrative user with the email `example@railstutorial.org` and password `foobar`. 85 | 86 | ## Branches 87 | 88 | The reference app repository includes a separate branch for each chapter in the tutorial (Chapters 3–14). To examine the code as it appears at the end of a particular chapter (with some slight variations, such as occasional exercise answers), simply check out the corresponding branch using `git checkout`: 89 | 90 | ``` 91 | $ git checkout 92 | ``` 93 | 94 | A full list of branch names appears as follows (preceded the number of the corresponding chapter in the book): 95 | 96 | ``` 97 | 3. static-pages 98 | 4. rails-flavored-ruby 99 | 5. filling-in-layout 100 | 6. modeling-users 101 | 7. sign-up 102 | 8. basic-login 103 | 9. advanced-login 104 | 10. updating-users 105 | 11. account-activation 106 | 12. password-reset 107 | 13. user-microposts 108 | 14. following-users 109 | ``` 110 | 111 | For example, to check out the branch for Chapter 7, you would run this at the command line: 112 | 113 | ``` 114 | $ git checkout sign-up 115 | ``` 116 | 117 | ## Help with the Rails Tutoiral 118 | 119 | Experience shows that comparing code with the reference app is often helpful for debugging errors and tracking down discrepancies. For additional assistance with any issues in the tutorial, please consult the [Rails Tutorial Help page](https://www.railstutorial.org/help). 120 | 121 | Suspected errors, typos, and bugs can be emailed to . All such reports are gratefully received, but please double-check with the [online version of the tutorial](https://www.railstutorial.org/book) and this reference app before submitting. 122 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # Add your own tasks in files placed in lib/tasks ending in .rake, 2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 3 | 4 | require_relative 'config/application' 5 | 6 | Rails.application.load_tasks 7 | -------------------------------------------------------------------------------- /app/assets/config/manifest.js: -------------------------------------------------------------------------------- 1 | //= link_tree ../images 2 | //= link_directory ../stylesheets .css 3 | -------------------------------------------------------------------------------- /app/assets/images/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/learnenough/sample_app_6th_ed/4e8ff53aa89e55258b79053a7711f7374ef0c4f9/app/assets/images/.keep -------------------------------------------------------------------------------- /app/assets/images/rails.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | rails-logo 6 | 7 | 10 | 12 | 15 | 16 | 18 | 20 | 22 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /app/assets/stylesheets/account_activations.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the AccountActivations controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /app/assets/stylesheets/application.css: -------------------------------------------------------------------------------- 1 | /* 2 | * This is a manifest file that'll be compiled into application.css, which will include all the files 3 | * listed below. 4 | * 5 | * Any CSS and SCSS file within this directory, lib/assets/stylesheets, or any plugin's 6 | * vendor/assets/stylesheets directory can be referenced here using a relative path. 7 | * 8 | * You're free to add application-wide styles to this file and they'll appear at the bottom of the 9 | * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS 10 | * files in this directory. Styles in this file should be added after the last require_* statement. 11 | * It is generally better to create a new file per style scope. 12 | * 13 | *= require_tree . 14 | *= require_self 15 | */ 16 | -------------------------------------------------------------------------------- /app/assets/stylesheets/custom.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 | /* universal */ 15 | 16 | body { 17 | padding-top: 60px; 18 | } 19 | 20 | section { 21 | overflow: auto; 22 | } 23 | 24 | textarea { 25 | resize: vertical; 26 | } 27 | 28 | .center { 29 | text-align: center; 30 | h1 { 31 | margin-bottom: 10px; 32 | } 33 | } 34 | 35 | /* typography */ 36 | 37 | h1, h2, h3, h4, h5, h6 { 38 | line-height: 1; 39 | } 40 | 41 | h1 { 42 | font-size: 3em; 43 | letter-spacing: -2px; 44 | margin-bottom: 30px; 45 | text-align: center; 46 | } 47 | 48 | h2 { 49 | font-size: 1.2em; 50 | letter-spacing: -1px; 51 | margin-bottom: 30px; 52 | text-align: center; 53 | font-weight: normal; 54 | color: $gray-light; 55 | } 56 | 57 | p { 58 | font-size: 1.1em; 59 | line-height: 1.7em; 60 | } 61 | 62 | 63 | /* header */ 64 | 65 | #logo { 66 | float: left; 67 | margin-right: 10px; 68 | font-size: 1.7em; 69 | color: white; 70 | text-transform: uppercase; 71 | letter-spacing: -1px; 72 | padding-top: 9px; 73 | font-weight: bold; 74 | &:hover { 75 | color: white; 76 | text-decoration: none; 77 | } 78 | } 79 | 80 | /* footer */ 81 | 82 | footer { 83 | margin-top: 45px; 84 | padding-top: 5px; 85 | border-top: 1px solid $gray-medium-light; 86 | color: $gray-light; 87 | a { 88 | color: $gray; 89 | &:hover { 90 | color: $gray-darker; 91 | } 92 | } 93 | small { 94 | float: left; 95 | } 96 | ul { 97 | float: right; 98 | list-style: none; 99 | li { 100 | float: left; 101 | margin-left: 15px; 102 | } 103 | } 104 | } 105 | @media (max-width: 800px) { 106 | footer { 107 | small { 108 | display: block; 109 | float: none; 110 | margin-bottom: 1em; 111 | } 112 | ul { 113 | float: none; 114 | padding: 0; 115 | li { 116 | float: none; 117 | margin-left: 0; 118 | } 119 | } 120 | } 121 | } 122 | 123 | /* miscellaneous */ 124 | 125 | .debug_dump { 126 | clear: both; 127 | float: left; 128 | width: 100%; 129 | margin-top: 45px; 130 | @include box_sizing; 131 | } 132 | 133 | /* sidebar */ 134 | 135 | aside { 136 | section.user_info { 137 | margin-top: 20px; 138 | } 139 | section { 140 | padding: 10px 0; 141 | margin-top: 20px; 142 | &:first-child { 143 | border: 0; 144 | padding-top: 0; 145 | } 146 | span { 147 | display: block; 148 | margin-bottom: 3px; 149 | line-height: 1; 150 | } 151 | h1 { 152 | font-size: 1.4em; 153 | text-align: left; 154 | letter-spacing: -1px; 155 | margin-bottom: 3px; 156 | margin-top: 0px; 157 | } 158 | } 159 | } 160 | 161 | .gravatar { 162 | float: left; 163 | margin-right: 10px; 164 | } 165 | 166 | .gravatar_edit { 167 | margin-top: 15px; 168 | } 169 | 170 | .stats { 171 | overflow: auto; 172 | margin-top: 0; 173 | padding: 0; 174 | a { 175 | float: left; 176 | padding: 0 10px; 177 | border-left: 1px solid $gray-lighter; 178 | color: gray; 179 | &:first-child { 180 | padding-left: 0; 181 | border: 0; 182 | } 183 | &:hover { 184 | text-decoration: none; 185 | color: blue; 186 | } 187 | } 188 | strong { 189 | display: block; 190 | } 191 | } 192 | 193 | .user_avatars { 194 | overflow: auto; 195 | margin-top: 10px; 196 | .gravatar { 197 | margin: 1px 1px; 198 | } 199 | a { 200 | padding: 0; 201 | } 202 | } 203 | 204 | .users.follow { 205 | padding: 0; 206 | } 207 | 208 | /* forms */ 209 | 210 | input, textarea, select, .uneditable-input { 211 | border: 1px solid #bbb; 212 | width: 100%; 213 | margin-bottom: 15px; 214 | @include box_sizing; 215 | } 216 | 217 | input { 218 | height: auto !important; 219 | } 220 | 221 | #error_explanation { 222 | color: red; 223 | ul { 224 | color: red; 225 | margin: 0 0 30px 0; 226 | } 227 | } 228 | 229 | .field_with_errors { 230 | @extend .has-error; 231 | .form-control { 232 | color: $state-danger-text; 233 | } 234 | } 235 | 236 | .checkbox { 237 | margin-top: -10px; 238 | margin-bottom: 10px; 239 | span { 240 | margin-left: 20px; 241 | font-weight: normal; 242 | } 243 | } 244 | 245 | #session_remember_me { 246 | width: auto; 247 | margin-left: 0; 248 | } 249 | 250 | /* Users index */ 251 | 252 | .users { 253 | list-style: none; 254 | margin: 0; 255 | li { 256 | overflow: auto; 257 | padding: 10px 0; 258 | border-bottom: 1px solid $gray-lighter; 259 | } 260 | } 261 | 262 | /* microposts */ 263 | 264 | .microposts { 265 | list-style: none; 266 | padding: 0; 267 | li { 268 | padding: 10px 0; 269 | border-top: 1px solid #e8e8e8; 270 | } 271 | .user { 272 | margin-top: 5em; 273 | padding-top: 0; 274 | } 275 | .content { 276 | display: block; 277 | margin-left: 60px; 278 | img { 279 | display: block; 280 | padding: 5px 0; 281 | } 282 | } 283 | .timestamp { 284 | color: $gray-light; 285 | display: block; 286 | margin-left: 60px; 287 | } 288 | .gravatar { 289 | float: left; 290 | margin-right: 10px; 291 | margin-top: 5px; 292 | } 293 | } 294 | 295 | aside { 296 | textarea { 297 | height: 100px; 298 | margin-bottom: 5px; 299 | } 300 | } 301 | 302 | span.image { 303 | margin-top: 10px; 304 | input { 305 | border: 0; 306 | } 307 | } 308 | -------------------------------------------------------------------------------- /app/assets/stylesheets/microposts.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the Microposts controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /app/assets/stylesheets/password_resets.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the PasswordResets controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /app/assets/stylesheets/relationships.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the Relationships controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /app/assets/stylesheets/sessions.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the Sessions controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /app/assets/stylesheets/static_pages.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the StaticPages controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /app/assets/stylesheets/users.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the Users controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /app/channels/application_cable/channel.rb: -------------------------------------------------------------------------------- 1 | module ApplicationCable 2 | class Channel < ActionCable::Channel::Base 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /app/channels/application_cable/connection.rb: -------------------------------------------------------------------------------- 1 | module ApplicationCable 2 | class Connection < ActionCable::Connection::Base 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /app/controllers/account_activations_controller.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 | -------------------------------------------------------------------------------- /app/controllers/application_controller.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 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /app/controllers/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/learnenough/sample_app_6th_ed/4e8ff53aa89e55258b79053a7711f7374ef0c4f9/app/controllers/concerns/.keep -------------------------------------------------------------------------------- /app/controllers/microposts_controller.rb: -------------------------------------------------------------------------------- 1 | class MicropostsController < ApplicationController 2 | before_action :logged_in_user, only: [:create, :destroy] 3 | before_action :correct_user, only: :destroy 4 | 5 | def create 6 | @micropost = current_user.microposts.build(micropost_params) 7 | @micropost.image.attach(params[:micropost][:image]) 8 | if @micropost.save 9 | flash[:success] = "Micropost created!" 10 | redirect_to root_url 11 | else 12 | @feed_items = current_user.feed.paginate(page: params[:page]) 13 | render 'static_pages/home' 14 | end 15 | end 16 | 17 | def destroy 18 | @micropost.destroy 19 | flash[:success] = "Micropost deleted" 20 | if request.referrer.nil? || request.referrer == microposts_url 21 | redirect_to root_url 22 | else 23 | redirect_to request.referrer 24 | end 25 | end 26 | 27 | private 28 | 29 | def micropost_params 30 | params.require(:micropost).permit(:content, :image) 31 | end 32 | 33 | def correct_user 34 | @micropost = current_user.microposts.find_by(id: params[:id]) 35 | redirect_to root_url if @micropost.nil? 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /app/controllers/password_resets_controller.rb: -------------------------------------------------------------------------------- 1 | class PasswordResetsController < ApplicationController 2 | before_action :get_user, only: [:edit, :update] 3 | before_action :valid_user, only: [:edit, :update] 4 | before_action :check_expiration, only: [:edit, :update] # Case (1) 5 | 6 | def new 7 | end 8 | 9 | def create 10 | @user = User.find_by(email: params[:password_reset][:email].downcase) 11 | if @user 12 | @user.create_reset_digest 13 | @user.send_password_reset_email 14 | flash[:info] = "Email sent with password reset instructions" 15 | redirect_to root_url 16 | else 17 | flash.now[:danger] = "Email address not found" 18 | render 'new' 19 | end 20 | end 21 | 22 | def edit 23 | end 24 | 25 | def update 26 | if params[:user][:password].empty? # Case (3) 27 | @user.errors.add(:password, "can't be empty") 28 | render 'edit' 29 | elsif @user.update(user_params) # Case (4) 30 | reset_session 31 | log_in @user 32 | flash[:success] = "Password has been reset." 33 | redirect_to @user 34 | else 35 | render 'edit' # Case (2) 36 | end 37 | end 38 | 39 | private 40 | 41 | def user_params 42 | params.require(:user).permit(:password, :password_confirmation) 43 | end 44 | 45 | # Before filters 46 | 47 | def get_user 48 | @user = User.find_by(email: params[:email]) 49 | end 50 | 51 | # Confirms a valid user. 52 | def valid_user 53 | unless (@user && @user.activated? && 54 | @user.authenticated?(:reset, params[:id])) 55 | redirect_to root_url 56 | end 57 | end 58 | 59 | # Checks expiration of reset token. 60 | def check_expiration 61 | if @user.password_reset_expired? 62 | flash[:danger] = "Password reset has expired." 63 | redirect_to new_password_reset_url 64 | end 65 | end 66 | end 67 | -------------------------------------------------------------------------------- /app/controllers/relationships_controller.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.js 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 } 18 | format.js 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /app/controllers/sessions_controller.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 | if user.activated? 10 | forwarding_url = session[:forwarding_url] 11 | reset_session 12 | params[:session][:remember_me] == '1' ? remember(user) : forget(user) 13 | log_in user 14 | redirect_to forwarding_url || user 15 | else 16 | message = "Account not activated. " 17 | message += "Check your email for the activation link." 18 | flash[:warning] = message 19 | redirect_to root_url 20 | end 21 | else 22 | flash.now[:danger] = 'Invalid email/password combination' 23 | render 'new' 24 | end 25 | end 26 | 27 | def destroy 28 | log_out if logged_in? 29 | redirect_to root_url 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /app/controllers/static_pages_controller.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 | -------------------------------------------------------------------------------- /app/controllers/users_controller.rb: -------------------------------------------------------------------------------- 1 | class UsersController < ApplicationController 2 | before_action :logged_in_user, only: [:index, :edit, :update, :destroy, 3 | :following, :followers] 4 | before_action :correct_user, only: [:edit, :update] 5 | before_action :admin_user, only: :destroy 6 | 7 | def index 8 | @users = User.paginate(page: params[:page]) 9 | end 10 | 11 | def show 12 | @user = User.find(params[:id]) 13 | @microposts = @user.microposts.paginate(page: params[:page]) 14 | end 15 | 16 | def new 17 | @user = User.new 18 | end 19 | 20 | def create 21 | @user = User.new(user_params) 22 | if @user.save 23 | @user.send_activation_email 24 | flash[:info] = "Please check your email to activate your account." 25 | redirect_to root_url 26 | else 27 | render 'new' 28 | end 29 | end 30 | 31 | def edit 32 | @user = User.find(params[:id]) 33 | end 34 | 35 | def update 36 | @user = User.find(params[:id]) 37 | if @user.update(user_params) 38 | flash[:success] = "Profile updated" 39 | redirect_to @user 40 | else 41 | render 'edit' 42 | end 43 | end 44 | 45 | def destroy 46 | User.find(params[:id]).destroy 47 | flash[:success] = "User deleted" 48 | redirect_to users_url 49 | end 50 | 51 | def following 52 | @title = "Following" 53 | @user = User.find(params[:id]) 54 | @users = @user.following.paginate(page: params[:page]) 55 | render 'show_follow' 56 | end 57 | 58 | def followers 59 | @title = "Followers" 60 | @user = User.find(params[:id]) 61 | @users = @user.followers.paginate(page: params[:page]) 62 | render 'show_follow' 63 | end 64 | 65 | private 66 | 67 | def user_params 68 | params.require(:user).permit(:name, :email, :password, 69 | :password_confirmation) 70 | end 71 | 72 | # Before filters 73 | 74 | # Confirms the correct user. 75 | def correct_user 76 | @user = User.find(params[:id]) 77 | redirect_to(root_url) unless current_user?(@user) 78 | end 79 | 80 | # Confirms an admin user. 81 | def admin_user 82 | redirect_to(root_url) unless current_user.admin? 83 | end 84 | end 85 | -------------------------------------------------------------------------------- /app/helpers/account_activations_helper.rb: -------------------------------------------------------------------------------- 1 | module AccountActivationsHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/application_helper.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 | -------------------------------------------------------------------------------- /app/helpers/microposts_helper.rb: -------------------------------------------------------------------------------- 1 | module MicropostsHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/password_resets_helper.rb: -------------------------------------------------------------------------------- 1 | module PasswordResetsHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/relationships_helper.rb: -------------------------------------------------------------------------------- 1 | module RelationshipsHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/sessions_helper.rb: -------------------------------------------------------------------------------- 1 | module SessionsHelper 2 | 3 | # Logs in the given user. 4 | def log_in(user) 5 | session[:user_id] = user.id 6 | # Guard against session replay attacks. 7 | # See https://bit.ly/33UvK0w for more. 8 | session[:session_token] = user.session_token 9 | end 10 | 11 | # Remembers a user in a persistent session. 12 | def remember(user) 13 | user.remember 14 | cookies.permanent.encrypted[:user_id] = user.id 15 | cookies.permanent[:remember_token] = user.remember_token 16 | end 17 | 18 | # Returns the user corresponding to the remember token cookie. 19 | def current_user 20 | if (user_id = session[:user_id]) 21 | user = User.find_by(id: user_id) 22 | if user && session[:session_token] == user.session_token 23 | @current_user = user 24 | end 25 | elsif (user_id = cookies.encrypted[:user_id]) 26 | user = User.find_by(id: user_id) 27 | if user && user.authenticated?(:remember, cookies[:remember_token]) 28 | log_in user 29 | @current_user = user 30 | end 31 | end 32 | end 33 | 34 | # Returns true if the given user is the current user. 35 | def current_user?(user) 36 | user == current_user 37 | end 38 | 39 | # Returns true if the user is logged in, false otherwise. 40 | def logged_in? 41 | !current_user.nil? 42 | end 43 | 44 | # Forgets a persistent session. 45 | def forget(user) 46 | user.forget 47 | cookies.delete(:user_id) 48 | cookies.delete(:remember_token) 49 | end 50 | 51 | # Logs out the current user. 52 | def log_out 53 | forget(current_user) 54 | reset_session 55 | @current_user = nil 56 | end 57 | 58 | # Stores the URL trying to be accessed. 59 | def store_location 60 | session[:forwarding_url] = request.original_url if request.get? 61 | end 62 | end 63 | -------------------------------------------------------------------------------- /app/helpers/static_pages_helper.rb: -------------------------------------------------------------------------------- 1 | module StaticPagesHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/users_helper.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 | -------------------------------------------------------------------------------- /app/javascript/channels/consumer.js: -------------------------------------------------------------------------------- 1 | // Action Cable provides the framework to deal with WebSockets in Rails. 2 | // You can generate new channels where WebSocket features live using the `rails generate channel` command. 3 | 4 | import { createConsumer } from "@rails/actioncable" 5 | 6 | export default createConsumer() 7 | -------------------------------------------------------------------------------- /app/javascript/channels/index.js: -------------------------------------------------------------------------------- 1 | // Load all the channels within this directory and all subdirectories. 2 | // Channel files must be named *_channel.js. 3 | 4 | const channels = require.context('.', true, /_channel\.js$/) 5 | channels.keys().forEach(channels) 6 | -------------------------------------------------------------------------------- /app/javascript/packs/application.js: -------------------------------------------------------------------------------- 1 | // This file is automatically compiled by Webpack, along with any other files 2 | // present in this directory. You're encouraged to place your actual application logic in 3 | // a relevant structure within app/javascript and only use these pack files to reference 4 | // that code so it'll be compiled. 5 | 6 | import Rails from "@rails/ujs" 7 | import Turbolinks from "turbolinks" 8 | import * as ActiveStorage from "@rails/activestorage" 9 | import "channels" 10 | import "jquery" 11 | import "bootstrap" 12 | 13 | Rails.start() 14 | Turbolinks.start() 15 | ActiveStorage.start() 16 | -------------------------------------------------------------------------------- /app/jobs/application_job.rb: -------------------------------------------------------------------------------- 1 | class ApplicationJob < ActiveJob::Base 2 | # Automatically retry jobs that encountered a deadlock 3 | # retry_on ActiveRecord::Deadlocked 4 | 5 | # Most jobs are safe to ignore if the underlying records are no longer available 6 | # discard_on ActiveJob::DeserializationError 7 | end 8 | -------------------------------------------------------------------------------- /app/mailers/application_mailer.rb: -------------------------------------------------------------------------------- 1 | class ApplicationMailer < ActionMailer::Base 2 | default from: "noreply@example.com" 3 | layout 'mailer' 4 | end 5 | -------------------------------------------------------------------------------- /app/mailers/user_mailer.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 | -------------------------------------------------------------------------------- /app/models/application_record.rb: -------------------------------------------------------------------------------- 1 | class ApplicationRecord < ActiveRecord::Base 2 | self.abstract_class = true 3 | end 4 | -------------------------------------------------------------------------------- /app/models/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/learnenough/sample_app_6th_ed/4e8ff53aa89e55258b79053a7711f7374ef0c4f9/app/models/concerns/.keep -------------------------------------------------------------------------------- /app/models/micropost.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 | 12 | # Returns a resized image for display. 13 | def display_image 14 | image.variant(resize_to_limit: [500, 500]) 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /app/models/relationship.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 | -------------------------------------------------------------------------------- /app/models/user.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 | attr_accessor :remember_token, :activation_token, :reset_token 12 | before_save :downcase_email 13 | before_create :create_activation_digest 14 | validates :name, presence: true, length: { maximum: 50 } 15 | VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i 16 | validates :email, presence: true, length: { maximum: 255 }, 17 | format: { with: VALID_EMAIL_REGEX }, 18 | uniqueness: true 19 | has_secure_password 20 | validates :password, presence: true, length: { minimum: 6 }, allow_nil: true 21 | 22 | # Returns the hash digest of the given string. 23 | def User.digest(string) 24 | cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST : 25 | BCrypt::Engine.cost 26 | BCrypt::Password.create(string, cost: cost) 27 | end 28 | 29 | # Returns a random token. 30 | def User.new_token 31 | SecureRandom.urlsafe_base64 32 | end 33 | 34 | # Remembers a user in the database for use in persistent sessions. 35 | def remember 36 | self.remember_token = User.new_token 37 | update_attribute(:remember_digest, User.digest(remember_token)) 38 | remember_digest 39 | end 40 | 41 | # Returns a session token to prevent session hijacking. 42 | # We reuse the remember digest for convenience. 43 | def session_token 44 | remember_digest || remember 45 | end 46 | 47 | # Returns true if the given token matches the digest. 48 | def authenticated?(attribute, token) 49 | digest = send("#{attribute}_digest") 50 | return false if digest.nil? 51 | BCrypt::Password.new(digest).is_password?(token) 52 | end 53 | 54 | # Forgets a user. 55 | def forget 56 | update_attribute(:remember_digest, nil) 57 | end 58 | 59 | # Activates an account. 60 | def activate 61 | update_attribute(:activated, true) 62 | update_attribute(:activated_at, Time.zone.now) 63 | end 64 | 65 | # Sends activation email. 66 | def send_activation_email 67 | UserMailer.account_activation(self).deliver_now 68 | end 69 | 70 | # Sets the password reset attributes. 71 | def create_reset_digest 72 | self.reset_token = User.new_token 73 | update_attribute(:reset_digest, User.digest(reset_token)) 74 | update_attribute(:reset_sent_at, Time.zone.now) 75 | end 76 | 77 | # Sends password reset email. 78 | def send_password_reset_email 79 | UserMailer.password_reset(self).deliver_now 80 | end 81 | 82 | # Returns true if a password reset has expired. 83 | def password_reset_expired? 84 | reset_sent_at < 2.hours.ago 85 | end 86 | 87 | # Returns a user's status feed. 88 | def feed 89 | following_ids = "SELECT followed_id FROM relationships 90 | WHERE follower_id = :user_id" 91 | Micropost.where("user_id IN (#{following_ids}) 92 | OR user_id = :user_id", user_id: id) 93 | end 94 | 95 | # Follows a user. 96 | def follow(other_user) 97 | following << other_user unless self == other_user 98 | end 99 | 100 | # Unfollows a user. 101 | def unfollow(other_user) 102 | following.delete(other_user) 103 | end 104 | 105 | # Returns true if the current user is following the other user. 106 | def following?(other_user) 107 | following.include?(other_user) 108 | end 109 | 110 | private 111 | 112 | # Converts email to all lowercase. 113 | def downcase_email 114 | self.email = email.downcase 115 | end 116 | 117 | # Creates and assigns the activation token and digest. 118 | def create_activation_digest 119 | self.activation_token = User.new_token 120 | self.activation_digest = User.digest(activation_token) 121 | end 122 | end 123 | -------------------------------------------------------------------------------- /app/views/layouts/_footer.html.erb: -------------------------------------------------------------------------------- 1 | 14 | -------------------------------------------------------------------------------- /app/views/layouts/_header.html.erb: -------------------------------------------------------------------------------- 1 | 42 | -------------------------------------------------------------------------------- /app/views/layouts/_shim.html.erb: -------------------------------------------------------------------------------- 1 | 5 | -------------------------------------------------------------------------------- /app/views/layouts/application.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', media: 'all', 11 | 'data-turbolinks-track': 'reload' %> 12 | <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %> 13 | <%= render 'layouts/shim' %> 14 | 15 | 16 | <%= render 'layouts/header' %> 17 |
18 | <% flash.each do |message_type, message| %> 19 |
<%= message %>
20 | <% end %> 21 | <%= yield %> 22 | <%= render 'layouts/footer' %> 23 | <%= debug(params) if Rails.env.development? %> 24 |
25 | 26 | 27 | -------------------------------------------------------------------------------- /app/views/layouts/mailer.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | 10 | 11 | <%= yield %> 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/views/layouts/mailer.text.erb: -------------------------------------------------------------------------------- 1 | <%= yield %> 2 | -------------------------------------------------------------------------------- /app/views/microposts/_micropost.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.display_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, method: :delete, 12 | data: { confirm: "You sure?" } %> 13 | <% end %> 14 | 15 |
  • 16 | -------------------------------------------------------------------------------- /app/views/password_resets/edit.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:title, 'Reset password') %> 2 |

    Reset password

    3 | 4 |
    5 |
    6 | <%= form_with(model: @user, url: password_reset_path(params[:id]), 7 | local: true) do |f| %> 8 | <%= render 'shared/error_messages', object: f.object %> 9 | 10 | <%= hidden_field_tag :email, @user.email %> 11 | 12 | <%= f.label :password %> 13 | <%= f.password_field :password, class: 'form-control' %> 14 | 15 | <%= f.label :password_confirmation, "Confirmation" %> 16 | <%= f.password_field :password_confirmation, class: 'form-control' %> 17 | 18 | <%= f.submit "Update password", class: "btn btn-primary" %> 19 | <% end %> 20 |
    21 |
    22 | -------------------------------------------------------------------------------- /app/views/password_resets/new.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:title, "Forgot password") %> 2 |

    Forgot password

    3 | 4 |
    5 |
    6 | <%= form_with(url: password_resets_path, scope: :password_reset, 7 | local: true) do |f| %> 8 | <%= f.label :email %> 9 | <%= f.email_field :email, class: 'form-control' %> 10 | 11 | <%= f.submit "Submit", class: "btn btn-primary" %> 12 | <% end %> 13 |
    14 |
    15 | -------------------------------------------------------------------------------- /app/views/relationships/create.js.erb: -------------------------------------------------------------------------------- 1 | $("#follow_form").html("<%= escape_javascript(render('users/unfollow')) %>"); 2 | $("#followers").html('<%= @user.followers.count %>'); 3 | -------------------------------------------------------------------------------- /app/views/relationships/destroy.js.erb: -------------------------------------------------------------------------------- 1 | $("#follow_form").html("<%= escape_javascript(render('users/follow')) %>"); 2 | $("#followers").html('<%= @user.followers.count %>'); 3 | -------------------------------------------------------------------------------- /app/views/sessions/new.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:title, "Log in") %> 2 |

    Log in

    3 | 4 |
    5 |
    6 | <%= form_with(url: login_path, scope: :session, local: true) do |f| %> 7 | 8 | <%= f.label :email %> 9 | <%= f.email_field :email, class: 'form-control' %> 10 | 11 | <%= f.label :password %> 12 | <%= link_to "(forgot password)", new_password_reset_path %> 13 | <%= f.password_field :password, class: 'form-control' %> 14 | 15 | <%= f.label :remember_me, class: "checkbox inline" do %> 16 | <%= f.check_box :remember_me %> 17 | Remember me on this computer 18 | <% end %> 19 | 20 | <%= f.submit "Log in", class: "btn btn-primary" %> 21 | <% end %> 22 | 23 |

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

    24 |
    25 |
    26 | -------------------------------------------------------------------------------- /app/views/shared/_error_messages.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 | -------------------------------------------------------------------------------- /app/views/shared/_feed.html.erb: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /app/views/shared/_micropost_form.html.erb: -------------------------------------------------------------------------------- 1 | <%= form_with(model: @micropost, local: true) 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 | 12 | 21 | -------------------------------------------------------------------------------- /app/views/shared/_stats.html.erb: -------------------------------------------------------------------------------- 1 | <% @user ||= current_user %> 2 | 16 | -------------------------------------------------------------------------------- /app/views/shared/_user_info.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 | -------------------------------------------------------------------------------- /app/views/static_pages/about.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 | -------------------------------------------------------------------------------- /app/views/static_pages/contact.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 | -------------------------------------------------------------------------------- /app/views/static_pages/help.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 | -------------------------------------------------------------------------------- /app/views/static_pages/home.html.erb: -------------------------------------------------------------------------------- 1 | <% if logged_in? %> 2 |
    3 | 14 |
    15 |

    Micropost Feed

    16 | <%= render 'shared/feed' %> 17 |
    18 |
    19 | <% else %> 20 |
    21 |

    Welcome to the Sample App

    22 | 23 |

    24 | This is the home page for the 25 | Ruby on Rails Tutorial 26 | sample application. 27 |

    28 | 29 | <%= link_to "Sign up now!", signup_path, class: "btn btn-lg btn-primary" %> 30 |
    31 | 32 | <%= link_to image_tag("rails.svg", alt: "Rails logo", width: "200"), 33 | "https://rubyonrails.org/" %> 34 | <% end %> 35 | -------------------------------------------------------------------------------- /app/views/user_mailer/account_activation.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 | -------------------------------------------------------------------------------- /app/views/user_mailer/account_activation.text.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 | -------------------------------------------------------------------------------- /app/views/user_mailer/password_reset.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 | -------------------------------------------------------------------------------- /app/views/user_mailer/password_reset.text.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 | -------------------------------------------------------------------------------- /app/views/users/_follow.html.erb: -------------------------------------------------------------------------------- 1 | <%= form_with(model: current_user.active_relationships.build, local: false) do |f| %> 2 |
    <%= hidden_field_tag :followed_id, @user.id %>
    3 | <%= f.submit "Follow", class: "btn btn-primary" %> 4 | <% end %> 5 | -------------------------------------------------------------------------------- /app/views/users/_follow_form.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 | -------------------------------------------------------------------------------- /app/views/users/_unfollow.html.erb: -------------------------------------------------------------------------------- 1 | <%= form_with(model: current_user.active_relationships.find_by(followed_id: @user.id), 2 | html: { method: :delete }, 3 | remote: true) do |f| %> 4 | <%= f.submit "Unfollow", class: "btn" %> 5 | <% end %> 6 | -------------------------------------------------------------------------------- /app/views/users/_user.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, method: :delete, 6 | data: { confirm: "You sure?" } %> 7 | <% end %> 8 |
  • 9 | -------------------------------------------------------------------------------- /app/views/users/edit.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:title, "Edit user") %> 2 |

    Update your profile

    3 | 4 |
    5 |
    6 | <%= form_with(model: @user, local: true) do |f| %> 7 | <%= render 'shared/error_messages', object: f.object %> 8 | 9 | <%= f.label :name %> 10 | <%= f.text_field :name, class: 'form-control' %> 11 | 12 | <%= f.label :email %> 13 | <%= f.email_field :email, class: 'form-control' %> 14 | 15 | <%= f.label :password %> 16 | <%= f.password_field :password, class: 'form-control' %> 17 | 18 | <%= f.label :password_confirmation, "Confirmation" %> 19 | <%= f.password_field :password_confirmation, class: 'form-control' %> 20 | 21 | <%= f.submit "Save changes", class: "btn btn-primary" %> 22 | <% end %> 23 | 24 |
    25 | <%= gravatar_for @user %> 26 | change 27 |
    28 |
    29 |
    30 | -------------------------------------------------------------------------------- /app/views/users/index.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:title, 'All users') %> 2 |

    All users

    3 | 4 | <%= will_paginate %> 5 | 6 |
      7 | <%= render @users %> 8 |
    9 | 10 | <%= will_paginate %> 11 | -------------------------------------------------------------------------------- /app/views/users/new.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:title, 'Sign up') %> 2 |

    Sign up

    3 | 4 |
    5 |
    6 | <%= form_with(model: @user, local: true) do |f| %> 7 | <%= render 'shared/error_messages', object: f.object %> 8 | 9 | <%= f.label :name %> 10 | <%= f.text_field :name, class: 'form-control' %> 11 | 12 | <%= f.label :email %> 13 | <%= f.email_field :email, class: 'form-control' %> 14 | 15 | <%= f.label :password %> 16 | <%= f.password_field :password, class: 'form-control' %> 17 | 18 | <%= f.label :password_confirmation, "Confirmation" %> 19 | <%= f.password_field :password_confirmation, class: 'form-control' %> 20 | 21 | <%= f.submit "Create my account", class: "btn btn-primary" %> 22 | <% end %> 23 |
    24 |
    25 | -------------------------------------------------------------------------------- /app/views/users/show.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 |
    -------------------------------------------------------------------------------- /app/views/users/show_follow.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:title, @title) %> 2 |
    3 | 21 |
    22 |

    <%= @title %>

    23 | <% if @users.any? %> 24 | 27 | <%= will_paginate %> 28 | <% end %> 29 |
    30 |
    31 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function(api) { 2 | var validEnv = ['development', 'test', 'production'] 3 | var currentEnv = api.env() 4 | var isDevelopmentEnv = api.env('development') 5 | var isProductionEnv = api.env('production') 6 | var isTestEnv = api.env('test') 7 | 8 | if (!validEnv.includes(currentEnv)) { 9 | throw new Error( 10 | 'Please specify a valid `NODE_ENV` or ' + 11 | '`BABEL_ENV` environment variables. Valid values are "development", ' + 12 | '"test", and "production". Instead, received: ' + 13 | JSON.stringify(currentEnv) + 14 | '.' 15 | ) 16 | } 17 | 18 | return { 19 | presets: [ 20 | isTestEnv && [ 21 | require('@babel/preset-env').default, 22 | { 23 | targets: { 24 | node: 'current' 25 | } 26 | } 27 | ], 28 | (isProductionEnv || isDevelopmentEnv) && [ 29 | require('@babel/preset-env').default, 30 | { 31 | forceAllTransforms: true, 32 | useBuiltIns: 'entry', 33 | corejs: 3, 34 | modules: false, 35 | exclude: ['transform-typeof-symbol'] 36 | } 37 | ] 38 | ].filter(Boolean), 39 | plugins: [ 40 | require('babel-plugin-macros'), 41 | require('@babel/plugin-syntax-dynamic-import').default, 42 | isTestEnv && require('babel-plugin-dynamic-import-node'), 43 | require('@babel/plugin-transform-destructuring').default, 44 | [ 45 | require('@babel/plugin-proposal-class-properties').default, 46 | { 47 | loose: true 48 | } 49 | ], 50 | [ 51 | require('@babel/plugin-proposal-object-rest-spread').default, 52 | { 53 | useBuiltIns: true 54 | } 55 | ], 56 | [ 57 | require('@babel/plugin-transform-runtime').default, 58 | { 59 | helpers: false, 60 | regenerator: true, 61 | corejs: false 62 | } 63 | ], 64 | [ 65 | require('@babel/plugin-transform-regenerator').default, 66 | { 67 | async: false 68 | } 69 | ] 70 | ].filter(Boolean) 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /bin/bundle: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # 5 | # This file was generated by Bundler. 6 | # 7 | # The application 'bundle' is installed as part of a gem, and 8 | # this file is here to facilitate running it. 9 | # 10 | 11 | require "rubygems" 12 | 13 | m = Module.new do 14 | module_function 15 | 16 | def invoked_as_script? 17 | File.expand_path($0) == File.expand_path(__FILE__) 18 | end 19 | 20 | def env_var_version 21 | ENV["BUNDLER_VERSION"] 22 | end 23 | 24 | def cli_arg_version 25 | return unless invoked_as_script? # don't want to hijack other binstubs 26 | return unless "update".start_with?(ARGV.first || " ") # must be running `bundle update` 27 | bundler_version = nil 28 | update_index = nil 29 | ARGV.each_with_index do |a, i| 30 | if update_index && update_index.succ == i && a =~ Gem::Version::ANCHORED_VERSION_PATTERN 31 | bundler_version = a 32 | end 33 | next unless a =~ /\A--bundler(?:[= ](#{Gem::Version::VERSION_PATTERN}))?\z/ 34 | bundler_version = $1 || ">= 0.a" 35 | update_index = i 36 | end 37 | bundler_version 38 | end 39 | 40 | def gemfile 41 | gemfile = ENV["BUNDLE_GEMFILE"] 42 | return gemfile if gemfile && !gemfile.empty? 43 | 44 | File.expand_path("../../Gemfile", __FILE__) 45 | end 46 | 47 | def lockfile 48 | lockfile = 49 | case File.basename(gemfile) 50 | when "gems.rb" then gemfile.sub(/\.rb$/, gemfile) 51 | else "#{gemfile}.lock" 52 | end 53 | File.expand_path(lockfile) 54 | end 55 | 56 | def lockfile_version 57 | return unless File.file?(lockfile) 58 | lockfile_contents = File.read(lockfile) 59 | return unless lockfile_contents =~ /\n\nBUNDLED WITH\n\s{2,}(#{Gem::Version::VERSION_PATTERN})\n/ 60 | Regexp.last_match(1) 61 | end 62 | 63 | def bundler_version 64 | @bundler_version ||= begin 65 | env_var_version || cli_arg_version || 66 | lockfile_version || "#{Gem::Requirement.default}.a" 67 | end 68 | end 69 | 70 | def load_bundler! 71 | ENV["BUNDLE_GEMFILE"] ||= gemfile 72 | 73 | # must dup string for RG < 1.8 compatibility 74 | activate_bundler(bundler_version.dup) 75 | end 76 | 77 | def activate_bundler(bundler_version) 78 | if Gem::Version.correct?(bundler_version) && Gem::Version.new(bundler_version).release < Gem::Version.new("2.0") 79 | bundler_version = "< 2" 80 | end 81 | gem_error = activation_error_handling do 82 | gem "bundler", bundler_version 83 | end 84 | return if gem_error.nil? 85 | require_error = activation_error_handling do 86 | require "bundler/version" 87 | end 88 | return if require_error.nil? && Gem::Requirement.new(bundler_version).satisfied_by?(Gem::Version.new(Bundler::VERSION)) 89 | warn "Activating bundler (#{bundler_version}) failed:\n#{gem_error.message}\n\nTo install the version of bundler this project requires, run `gem install bundler -v '#{bundler_version}'`" 90 | exit 42 91 | end 92 | 93 | def activation_error_handling 94 | yield 95 | nil 96 | rescue StandardError, LoadError => e 97 | e 98 | end 99 | end 100 | 101 | m.load_bundler! 102 | 103 | if m.invoked_as_script? 104 | load Gem.bin_path("bundler", "bundle") 105 | end 106 | -------------------------------------------------------------------------------- /bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | APP_PATH = File.expand_path('../config/application', __dir__) 3 | require_relative "../config/boot" 4 | require "rails/commands" 5 | -------------------------------------------------------------------------------- /bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require_relative "../config/boot" 3 | require "rake" 4 | Rake.application.run 5 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require "fileutils" 3 | 4 | # path to your application root. 5 | APP_ROOT = File.expand_path('..', __dir__) 6 | 7 | def system!(*args) 8 | system(*args) || abort("\n== Command #{args} failed ==") 9 | end 10 | 11 | FileUtils.chdir APP_ROOT do 12 | # This script is a way to set up or update your development environment automatically. 13 | # This script is idempotent, so that you can run it at any time and get an expectable outcome. 14 | # Add necessary setup steps to this file. 15 | 16 | puts '== Installing dependencies ==' 17 | system! 'gem install bundler --conservative' 18 | system('bundle check') || system!('bundle install') 19 | 20 | # Install JavaScript dependencies 21 | system! 'bin/yarn' 22 | 23 | # puts "\n== Copying sample files ==" 24 | # unless File.exist?('config/database.yml') 25 | # FileUtils.cp 'config/database.yml.sample', 'config/database.yml' 26 | # end 27 | 28 | puts "\n== Preparing database ==" 29 | system! 'bin/rails db:prepare' 30 | 31 | puts "\n== Removing old logs and tempfiles ==" 32 | system! 'bin/rails log:clear tmp:clear' 33 | 34 | puts "\n== Restarting application server ==" 35 | system! 'bin/rails restart' 36 | end 37 | -------------------------------------------------------------------------------- /bin/webpack: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | ENV["RAILS_ENV"] ||= ENV["RACK_ENV"] || "development" 4 | ENV["NODE_ENV"] ||= "development" 5 | 6 | require "pathname" 7 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", 8 | Pathname.new(__FILE__).realpath) 9 | 10 | require "rubygems" 11 | require "bundler/setup" 12 | 13 | require "webpacker" 14 | require "webpacker/webpack_runner" 15 | 16 | APP_ROOT = File.expand_path("..", __dir__) 17 | Dir.chdir(APP_ROOT) do 18 | Webpacker::WebpackRunner.run(ARGV) 19 | end 20 | -------------------------------------------------------------------------------- /bin/webpack-dev-server: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | ENV["RAILS_ENV"] ||= ENV["RACK_ENV"] || "development" 4 | ENV["NODE_ENV"] ||= "development" 5 | 6 | require "pathname" 7 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", 8 | Pathname.new(__FILE__).realpath) 9 | 10 | require "rubygems" 11 | require "bundler/setup" 12 | 13 | require "webpacker" 14 | require "webpacker/dev_server_runner" 15 | 16 | APP_ROOT = File.expand_path("..", __dir__) 17 | Dir.chdir(APP_ROOT) do 18 | Webpacker::DevServerRunner.run(ARGV) 19 | end 20 | -------------------------------------------------------------------------------- /bin/yarn: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | APP_ROOT = File.expand_path('..', __dir__) 3 | Dir.chdir(APP_ROOT) do 4 | yarn = ENV["PATH"].split(File::PATH_SEPARATOR). 5 | select { |dir| File.expand_path(dir) != __dir__ }. 6 | product(["yarn", "yarn.cmd", "yarn.ps1"]). 7 | map { |dir, file| File.expand_path(file, dir) }. 8 | find { |file| File.executable?(file) } 9 | 10 | if yarn 11 | exec yarn, *ARGV 12 | else 13 | $stderr.puts "Yarn executable was not detected in the system." 14 | $stderr.puts "Download Yarn at https://yarnpkg.com/en/docs/install" 15 | exit 1 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /config.ru: -------------------------------------------------------------------------------- 1 | # This file is used by Rack-based servers to start the application. 2 | 3 | require_relative 'config/environment' 4 | 5 | run Rails.application 6 | -------------------------------------------------------------------------------- /config/application.rb: -------------------------------------------------------------------------------- 1 | require_relative 'boot' 2 | 3 | require 'rails/all' 4 | 5 | # Require the gems listed in Gemfile, including any gems 6 | # you've limited to :test, :development, or :production. 7 | Bundler.require(*Rails.groups) 8 | 9 | module SampleApp 10 | class Application < Rails::Application 11 | # Initialize configuration defaults for originally generated Rails version. 12 | config.load_defaults 6.0 13 | 14 | # Include the authenticity token in remote forms. 15 | config.action_view.embed_authenticity_token_in_remote_forms = true 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /config/boot.rb: -------------------------------------------------------------------------------- 1 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) 2 | 3 | require 'bundler/setup' # Set up gems listed in the Gemfile. 4 | require 'bootsnap/setup' # Speed up boot time by caching expensive operations. 5 | -------------------------------------------------------------------------------- /config/cable.yml: -------------------------------------------------------------------------------- 1 | development: 2 | adapter: async 3 | 4 | test: 5 | adapter: test 6 | 7 | production: 8 | adapter: redis 9 | url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %> 10 | channel_prefix: sample_app_production 11 | -------------------------------------------------------------------------------- /config/credentials.yml.enc: -------------------------------------------------------------------------------- 1 | IoVnBvbodBwAxQAOFrmxNjYXjIsWlGVtI0it7QoXGS0JveTffUR1qxK7Lq7pmKlZgxW8+1giF8ai1bCOhR4Lni5HucUMhuQUOgEI2kdlxG/Yvtp4jQbaW65TFR90/Nu0HlDUiM9ydk2Y4XpDpRpv3YenwwQsm9GcVyw97jD8XVvfv+rtD448urVTpnybfLu8GrgJicaxKcFb/XoZ7x6rhdLqtpMX7Oc4g9rqpUxJp8VFtQwxRGYAoIzxmD8yiKdM+t3UolP3FWXJwUonJwaMRQDfA8GXYpRdOXc99yKbmM5gDbT+goQSIpIh8eD++ouo0sTLz2UuqJ/8FJp49pdy9nk0xM1iE7KUHApkn191oepAXKey0XwFuk9Xb9eaG3zNiwoPnrVk1/rnjevKxHjBbdXHrkoGmdFcN5vU--j9rbuRFRl52el2hz--MLSNdvjWDJL7XkB5kgIxlQ== 2 | -------------------------------------------------------------------------------- /config/database.yml: -------------------------------------------------------------------------------- 1 | # SQLite. Versions 3.8.0 and up are supported. 2 | # gem install sqlite3 3 | # 4 | # Ensure the SQLite 3 gem is defined in your Gemfile 5 | # gem 'sqlite3' 6 | # 7 | default: &default 8 | adapter: sqlite3 9 | pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> 10 | timeout: 5000 11 | 12 | development: 13 | <<: *default 14 | database: db/development.sqlite3 15 | 16 | # Warning: The database defined as "test" will be erased and 17 | # re-generated from your development database when you run "rake". 18 | # Do not set this db to the same as development or production. 19 | test: 20 | <<: *default 21 | database: db/test.sqlite3 22 | 23 | production: 24 | adapter: postgresql 25 | encoding: unicode 26 | # For details on connection pooling, see Rails configuration guide 27 | # https://guides.rubyonrails.org/configuring.html#database-pooling 28 | pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> 29 | database: sample_app_production 30 | username: sample_app 31 | password: <%= ENV['SAMPLE_APP_DATABASE_PASSWORD'] %> 32 | -------------------------------------------------------------------------------- /config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the Rails application. 2 | require_relative 'application' 3 | 4 | # Initialize the Rails application. 5 | Rails.application.initialize! 6 | -------------------------------------------------------------------------------- /config/environments/development.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # In the development environment your application's code is reloaded on 5 | # every request. This slows down response time but is perfect for development 6 | # since you don't have to restart the web server when you make code changes. 7 | config.cache_classes = false 8 | 9 | # Do not eager load code on boot. 10 | config.eager_load = false 11 | 12 | # Show full error reports. 13 | config.consider_all_requests_local = true 14 | 15 | # Enable/disable caching. By default caching is disabled. 16 | # Run rails dev:cache to toggle caching. 17 | if Rails.root.join('tmp', 'caching-dev.txt').exist? 18 | config.action_controller.perform_caching = true 19 | config.action_controller.enable_fragment_cache_logging = true 20 | 21 | config.cache_store = :memory_store 22 | config.public_file_server.headers = { 23 | 'Cache-Control' => "public, max-age=#{2.days.to_i}" 24 | } 25 | else 26 | config.action_controller.perform_caching = false 27 | 28 | config.cache_store = :null_store 29 | end 30 | 31 | # Store uploaded files on the local file system (see config/storage.yml for options). 32 | config.active_storage.service = :local 33 | 34 | # Don't care if the mailer can't send. 35 | config.action_mailer.raise_delivery_errors = false 36 | 37 | host = '0ebe1dc6d40e4a4bb06e0ca7fe138127.vfs.cloud9.us-east-2.amazonaws.com' 38 | config.action_mailer.default_url_options = { host: host, protocol: 'https' } 39 | 40 | 41 | config.action_mailer.perform_caching = false 42 | 43 | # Print deprecation notices to the Rails logger. 44 | config.active_support.deprecation = :log 45 | 46 | # Raise an error on page load if there are pending migrations. 47 | config.active_record.migration_error = :page_load 48 | 49 | # Highlight code that triggered database queries in logs. 50 | config.active_record.verbose_query_logs = true 51 | 52 | # Debug mode disables concatenation and preprocessing of assets. 53 | # This option may cause significant delays in view rendering with a large 54 | # number of complex assets. 55 | config.assets.debug = true 56 | 57 | # Suppress logger output for asset requests. 58 | config.assets.quiet = true 59 | 60 | # Raises error for missing translations. 61 | # config.action_view.raise_on_missing_translations = true 62 | 63 | # Use an evented file watcher to asynchronously detect changes in source code, 64 | # routes, locales, etc. This feature depends on the listen gem. 65 | config.file_watcher = ActiveSupport::EventedFileUpdateChecker 66 | 67 | # Allow connections to local server. 68 | config.hosts.clear 69 | end 70 | -------------------------------------------------------------------------------- /config/environments/production.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # Code is not reloaded between requests. 5 | config.cache_classes = true 6 | 7 | # Eager load code on boot. This eager loads most of Rails and 8 | # your application in memory, allowing both threaded web servers 9 | # and those relying on copy on write to perform better. 10 | # Rake tasks automatically ignore this option for performance. 11 | config.eager_load = true 12 | 13 | # Full error reports are disabled and caching is turned on. 14 | config.consider_all_requests_local = false 15 | config.action_controller.perform_caching = true 16 | 17 | # Ensures that a master key has been made available in either ENV["RAILS_MASTER_KEY"] 18 | # or in config/master.key. This key is used to decrypt credentials (and other encrypted files). 19 | # config.require_master_key = true 20 | 21 | # Disable serving static files from the `/public` folder by default since 22 | # Apache or NGINX already handles this. 23 | config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present? 24 | 25 | # Compress CSS using a preprocessor. 26 | # config.assets.css_compressor = :sass 27 | 28 | # Do not fallback to assets pipeline if a precompiled asset is missed. 29 | config.assets.compile = false 30 | 31 | # Enable serving of images, stylesheets, and JavaScripts from an asset server. 32 | # config.action_controller.asset_host = 'http://assets.example.com' 33 | 34 | # Specifies the header that your server uses for sending files. 35 | # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache 36 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX 37 | 38 | # Store uploaded files on Amazon AWS. 39 | config.active_storage.service = :amazon 40 | 41 | # Mount Action Cable outside main process or domain. 42 | # config.action_cable.mount_path = nil 43 | # config.action_cable.url = 'wss://example.com/cable' 44 | # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ] 45 | 46 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. 47 | config.force_ssl = true 48 | 49 | # Use the lowest log level to ensure availability of diagnostic information 50 | # when problems arise. 51 | config.log_level = :debug 52 | 53 | # Prepend all log lines with the following tags. 54 | config.log_tags = [ :request_id ] 55 | 56 | # Use a different cache store in production. 57 | # config.cache_store = :mem_cache_store 58 | 59 | # Use a real queuing backend for Active Job (and separate queues per environment). 60 | # config.active_job.queue_adapter = :resque 61 | # config.active_job.queue_name_prefix = "sample_app_production" 62 | 63 | config.action_mailer.perform_caching = false 64 | 65 | # Ignore bad email addresses and do not raise email delivery errors. 66 | # Set this to true and configure the email server for immediate delivery to raise delivery errors. 67 | config.action_mailer.raise_delivery_errors = true 68 | config.action_mailer.delivery_method = :smtp 69 | host = 'https://mysterious-atoll-47182.herokuapp.com' 70 | config.action_mailer.default_url_options = { host: host } 71 | ActionMailer::Base.smtp_settings = { 72 | :address => 'smtp.sendgrid.net', 73 | :port => '587', 74 | :authentication => :plain, 75 | :user_name => 'apikey', 76 | :password => ENV['SENDGRID_API_KEY'], 77 | :domain => 'heroku.com', 78 | :enable_starttls_auto => true 79 | } 80 | 81 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to 82 | # the I18n.default_locale when a translation cannot be found). 83 | config.i18n.fallbacks = true 84 | 85 | # Send deprecation notices to registered listeners. 86 | config.active_support.deprecation = :notify 87 | 88 | # Use default logging formatter so that PID and timestamp are not suppressed. 89 | config.log_formatter = ::Logger::Formatter.new 90 | 91 | # Use a different logger for distributed setups. 92 | # require 'syslog/logger' 93 | # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name') 94 | 95 | if ENV["RAILS_LOG_TO_STDOUT"].present? 96 | logger = ActiveSupport::Logger.new(STDOUT) 97 | logger.formatter = config.log_formatter 98 | config.logger = ActiveSupport::TaggedLogging.new(logger) 99 | end 100 | 101 | # Do not dump schema after migrations. 102 | config.active_record.dump_schema_after_migration = false 103 | 104 | # Inserts middleware to perform automatic connection switching. 105 | # The `database_selector` hash is used to pass options to the DatabaseSelector 106 | # middleware. The `delay` is used to determine how long to wait after a write 107 | # to send a subsequent read to the primary. 108 | # 109 | # The `database_resolver` class is used by the middleware to determine which 110 | # database is appropriate to use based on the time delay. 111 | # 112 | # The `database_resolver_context` class is used by the middleware to set 113 | # timestamps for the last write to the primary. The resolver uses the context 114 | # class timestamps to determine how long to wait before reading from the 115 | # replica. 116 | # 117 | # By default Rails will store a last write timestamp in the session. The 118 | # DatabaseSelector middleware is designed as such you can define your own 119 | # strategy for connection switching and pass that into the middleware through 120 | # these configuration options. 121 | # config.active_record.database_selector = { delay: 2.seconds } 122 | # config.active_record.database_resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver 123 | # config.active_record.database_resolver_context = ActiveRecord::Middleware::DatabaseSelector::Resolver::Session 124 | end 125 | -------------------------------------------------------------------------------- /config/environments/test.rb: -------------------------------------------------------------------------------- 1 | # The test environment is used exclusively to run your application's 2 | # test suite. You never need to work with it otherwise. Remember that 3 | # your test database is "scratch space" for the test suite and is wiped 4 | # and recreated between test runs. Don't rely on the data there! 5 | 6 | Rails.application.configure do 7 | # Settings specified here will take precedence over those in config/application.rb. 8 | 9 | config.cache_classes = false 10 | 11 | # Do not eager load code on boot. This avoids loading your whole application 12 | # just for the purpose of running a single test. If you are using a tool that 13 | # preloads Rails for running tests, you may have to set it to true. 14 | config.eager_load = false 15 | 16 | # Configure public file server for tests with Cache-Control for performance. 17 | config.public_file_server.enabled = true 18 | config.public_file_server.headers = { 19 | 'Cache-Control' => "public, max-age=#{1.hour.to_i}" 20 | } 21 | 22 | # Show full error reports and disable caching. 23 | config.consider_all_requests_local = true 24 | config.action_controller.perform_caching = false 25 | config.cache_store = :null_store 26 | 27 | # Raise exceptions instead of rendering exception templates. 28 | config.action_dispatch.show_exceptions = false 29 | 30 | # Disable request forgery protection in test environment. 31 | config.action_controller.allow_forgery_protection = false 32 | 33 | # Store uploaded files on the local file system in a temporary directory. 34 | config.active_storage.service = :test 35 | 36 | config.action_mailer.perform_caching = false 37 | 38 | # Tell Action Mailer not to deliver emails to the real world. 39 | # The :test delivery method accumulates sent emails in the 40 | # ActionMailer::Base.deliveries array. 41 | config.action_mailer.delivery_method = :test 42 | config.action_mailer.default_url_options = { host: 'example.com' } 43 | 44 | # Print deprecation notices to the stderr. 45 | config.active_support.deprecation = :stderr 46 | 47 | # Raises error for missing translations. 48 | # config.action_view.raise_on_missing_translations = true 49 | end 50 | -------------------------------------------------------------------------------- /config/initializers/application_controller_renderer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # ActiveSupport::Reloader.to_prepare do 4 | # ApplicationController.renderer.defaults.merge!( 5 | # http_host: 'example.org', 6 | # https: false 7 | # ) 8 | # end 9 | -------------------------------------------------------------------------------- /config/initializers/assets.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Version of your assets, change this if you want to expire all your assets. 4 | Rails.application.config.assets.version = '1.0' 5 | 6 | # Add additional assets to the asset load path. 7 | # Rails.application.config.assets.paths << Emoji.images_path 8 | # Add Yarn node_modules folder to the asset load path. 9 | Rails.application.config.assets.paths << Rails.root.join('node_modules') 10 | 11 | # Precompile additional assets. 12 | # application.js, application.css, and all non-JS/CSS in the app/assets 13 | # folder are already added. 14 | # Rails.application.config.assets.precompile += %w( admin.js admin.css ) 15 | -------------------------------------------------------------------------------- /config/initializers/backtrace_silencers.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. 4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } 5 | 6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. 7 | # Rails.backtrace_cleaner.remove_silencers! 8 | -------------------------------------------------------------------------------- /config/initializers/content_security_policy.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Define an application-wide content security policy 4 | # For further information see the following documentation 5 | # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy 6 | 7 | # Rails.application.config.content_security_policy do |policy| 8 | # policy.default_src :self, :https 9 | # policy.font_src :self, :https, :data 10 | # policy.img_src :self, :https, :data 11 | # policy.object_src :none 12 | # policy.script_src :self, :https 13 | # policy.style_src :self, :https 14 | # # If you are using webpack-dev-server then specify webpack-dev-server host 15 | # policy.connect_src :self, :https, "http://localhost:3035", "ws://localhost:3035" if Rails.env.development? 16 | 17 | # # Specify URI for violation reports 18 | # # policy.report_uri "/csp-violation-report-endpoint" 19 | # end 20 | 21 | # If you are using UJS then enable automatic nonce generation 22 | # Rails.application.config.content_security_policy_nonce_generator = -> request { SecureRandom.base64(16) } 23 | 24 | # Set the nonce only to specific directives 25 | # Rails.application.config.content_security_policy_nonce_directives = %w(script-src) 26 | 27 | # Report CSP violations to a specified URI 28 | # For further information see the following documentation: 29 | # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only 30 | # Rails.application.config.content_security_policy_report_only = true 31 | -------------------------------------------------------------------------------- /config/initializers/cookies_serializer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Specify a serializer for the signed and encrypted cookie jars. 4 | # Valid options are :json, :marshal, and :hybrid. 5 | Rails.application.config.action_dispatch.cookies_serializer = :json 6 | -------------------------------------------------------------------------------- /config/initializers/filter_parameter_logging.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Configure sensitive parameters which will be filtered from the log file. 4 | Rails.application.config.filter_parameters += [:password] 5 | -------------------------------------------------------------------------------- /config/initializers/inflections.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new inflection rules using the following format. Inflections 4 | # are locale specific, and you may define rules for as many different 5 | # locales as you wish. All of these examples are active by default: 6 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 7 | # inflect.plural /^(ox)$/i, '\1en' 8 | # inflect.singular /^(ox)en/i, '\1' 9 | # inflect.irregular 'person', 'people' 10 | # inflect.uncountable %w( fish sheep ) 11 | # end 12 | 13 | # These inflection rules are supported but not enabled by default: 14 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 15 | # inflect.acronym 'RESTful' 16 | # end 17 | -------------------------------------------------------------------------------- /config/initializers/mime_types.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new mime types for use in respond_to blocks: 4 | # Mime::Type.register "text/richtext", :rtf 5 | -------------------------------------------------------------------------------- /config/initializers/wrap_parameters.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # This file contains settings for ActionController::ParamsWrapper which 4 | # is enabled by default. 5 | 6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. 7 | ActiveSupport.on_load(:action_controller) do 8 | wrap_parameters format: [:json] 9 | end 10 | 11 | # To enable root element in JSON for ActiveRecord objects. 12 | # ActiveSupport.on_load(:active_record) do 13 | # self.include_root_in_json = true 14 | # end 15 | -------------------------------------------------------------------------------- /config/locales/en.yml: -------------------------------------------------------------------------------- 1 | # Files in the config/locales directory are used for internationalization 2 | # and are automatically loaded by Rails. If you want to use locales other 3 | # than English, add the necessary files in this directory. 4 | # 5 | # To use the locales, use `I18n.t`: 6 | # 7 | # I18n.t 'hello' 8 | # 9 | # In views, this is aliased to just `t`: 10 | # 11 | # <%= t('hello') %> 12 | # 13 | # To use a different locale, set it with `I18n.locale`: 14 | # 15 | # I18n.locale = :es 16 | # 17 | # This would use the information in config/locales/es.yml. 18 | # 19 | # The following keys must be escaped otherwise they will not be retrieved by 20 | # the default I18n backend: 21 | # 22 | # true, false, on, off, yes, no 23 | # 24 | # Instead, surround them with single quotes. 25 | # 26 | # en: 27 | # 'true': 'foo' 28 | # 29 | # To learn more, please read the Rails Internationalization guide 30 | # available at https://guides.rubyonrails.org/i18n.html. 31 | 32 | en: 33 | hello: "Hello world" 34 | -------------------------------------------------------------------------------- /config/puma.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 | -------------------------------------------------------------------------------- /config/routes.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 | resources :relationships, only: [:create, :destroy] 19 | get '/microposts', to: 'static_pages#home' 20 | end 21 | -------------------------------------------------------------------------------- /config/spring.rb: -------------------------------------------------------------------------------- 1 | Spring.watch( 2 | ".ruby-version", 3 | ".rbenv-vars", 4 | "tmp/restart.txt", 5 | "tmp/caching-dev.txt" 6 | ) 7 | -------------------------------------------------------------------------------- /config/storage.yml: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /config/webpack/development.js: -------------------------------------------------------------------------------- 1 | process.env.NODE_ENV = process.env.NODE_ENV || 'development' 2 | 3 | const environment = require('./environment') 4 | 5 | module.exports = environment.toWebpackConfig() 6 | -------------------------------------------------------------------------------- /config/webpack/environment.js: -------------------------------------------------------------------------------- 1 | const { environment } = require('@rails/webpacker') 2 | 3 | const webpack = require('webpack') 4 | environment.plugins.prepend('Provide', 5 | new webpack.ProvidePlugin({ 6 | $: 'jquery/src/jquery', 7 | jQuery: 'jquery/src/jquery' 8 | }) 9 | ) 10 | 11 | module.exports = environment 12 | -------------------------------------------------------------------------------- /config/webpack/production.js: -------------------------------------------------------------------------------- 1 | process.env.NODE_ENV = process.env.NODE_ENV || 'production' 2 | 3 | const environment = require('./environment') 4 | 5 | module.exports = environment.toWebpackConfig() 6 | -------------------------------------------------------------------------------- /config/webpack/test.js: -------------------------------------------------------------------------------- 1 | process.env.NODE_ENV = process.env.NODE_ENV || 'development' 2 | 3 | const environment = require('./environment') 4 | 5 | module.exports = environment.toWebpackConfig() 6 | -------------------------------------------------------------------------------- /config/webpacker.yml: -------------------------------------------------------------------------------- 1 | # Note: You must restart bin/webpack-dev-server for changes to take effect 2 | 3 | default: &default 4 | source_path: app/javascript 5 | source_entry_path: packs 6 | public_root_path: public 7 | public_output_path: packs 8 | cache_path: tmp/cache/webpacker 9 | check_yarn_integrity: false 10 | webpack_compile_output: false 11 | 12 | # Additional paths webpack should lookup modules 13 | # ['app/assets', 'engine/foo/app/assets'] 14 | resolved_paths: [] 15 | 16 | # Reload manifest.json on all requests so we reload latest compiled packs 17 | cache_manifest: false 18 | 19 | # Extract and emit a css file 20 | extract_css: false 21 | 22 | static_assets_extensions: 23 | - .jpg 24 | - .jpeg 25 | - .png 26 | - .gif 27 | - .tiff 28 | - .ico 29 | - .svg 30 | - .eot 31 | - .otf 32 | - .ttf 33 | - .woff 34 | - .woff2 35 | 36 | extensions: 37 | - .mjs 38 | - .js 39 | - .sass 40 | - .scss 41 | - .css 42 | - .module.sass 43 | - .module.scss 44 | - .module.css 45 | - .png 46 | - .svg 47 | - .gif 48 | - .jpeg 49 | - .jpg 50 | 51 | development: 52 | <<: *default 53 | compile: true 54 | 55 | # Verifies that correct packages and versions are installed by inspecting package.json, yarn.lock, and node_modules 56 | check_yarn_integrity: true 57 | 58 | # Reference: https://webpack.js.org/configuration/dev-server/ 59 | dev_server: 60 | https: false 61 | host: localhost 62 | port: 3035 63 | public: localhost:3035 64 | hmr: false 65 | # Inline should be set to true if using HMR 66 | inline: true 67 | overlay: true 68 | compress: true 69 | disable_host_check: true 70 | use_local_ip: false 71 | quiet: false 72 | headers: 73 | 'Access-Control-Allow-Origin': '*' 74 | watch_options: 75 | ignored: '**/node_modules/**' 76 | 77 | 78 | test: 79 | <<: *default 80 | compile: true 81 | 82 | # Compile test packs to a separate directory 83 | public_output_path: packs-test 84 | 85 | production: 86 | <<: *default 87 | 88 | # Production depends on precompilation of packs prior to booting for performance. 89 | compile: false 90 | 91 | # Extract and emit a css file 92 | extract_css: true 93 | 94 | # Cache manifest.json for performance 95 | cache_manifest: true 96 | -------------------------------------------------------------------------------- /db/migrate/20190822013911_create_users.rb: -------------------------------------------------------------------------------- 1 | class CreateUsers < ActiveRecord::Migration[6.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 | -------------------------------------------------------------------------------- /db/migrate/20190822021835_add_index_to_users_email.rb: -------------------------------------------------------------------------------- 1 | class AddIndexToUsersEmail < ActiveRecord::Migration[6.0] 2 | def change 3 | add_index :users, :email, unique: true 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20190822031056_add_password_digest_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddPasswordDigestToUsers < ActiveRecord::Migration[6.0] 2 | def change 3 | add_column :users, :password_digest, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20190823000019_add_remember_digest_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddRememberDigestToUsers < ActiveRecord::Migration[6.0] 2 | def change 3 | add_column :users, :remember_digest, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20190823171209_add_admin_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddAdminToUsers < ActiveRecord::Migration[6.0] 2 | def change 3 | add_column :users, :admin, :boolean, default: false 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20190823175841_add_activation_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddActivationToUsers < ActiveRecord::Migration[6.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 | -------------------------------------------------------------------------------- /db/migrate/20190824013003_add_reset_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddResetToUsers < ActiveRecord::Migration[6.0] 2 | def change 3 | add_column :users, :reset_digest, :string 4 | add_column :users, :reset_sent_at, :datetime 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20190824113338_create_microposts.rb: -------------------------------------------------------------------------------- 1 | class CreateMicroposts < ActiveRecord::Migration[6.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 | -------------------------------------------------------------------------------- /db/migrate/20190827011913_create_active_storage_tables.active_storage.rb: -------------------------------------------------------------------------------- 1 | # This migration comes from active_storage (originally 20170806125915) 2 | class CreateActiveStorageTables < ActiveRecord::Migration[5.2] 3 | def change 4 | create_table :active_storage_blobs do |t| 5 | t.string :key, null: false 6 | t.string :filename, null: false 7 | t.string :content_type 8 | t.text :metadata 9 | t.string :service_name, null: false 10 | t.bigint :byte_size, null: false 11 | t.string :checksum, null: false 12 | t.datetime :created_at, null: false 13 | 14 | t.index [ :key ], unique: true 15 | end 16 | 17 | create_table :active_storage_attachments do |t| 18 | t.string :name, null: false 19 | t.references :record, null: false, polymorphic: true, index: false 20 | t.references :blob, null: false 21 | 22 | t.datetime :created_at, null: false 23 | 24 | t.index [ :record_type, :record_id, :name, :blob_id ], name: "index_active_storage_attachments_uniqueness", unique: true 25 | t.foreign_key :active_storage_blobs, column: :blob_id 26 | end 27 | 28 | create_table :active_storage_variant_records do |t| 29 | t.belongs_to :blob, null: false, index: false 30 | t.string :variation_digest, null: false 31 | 32 | t.index %i[ blob_id variation_digest ], name: "index_active_storage_variant_records_uniqueness", unique: true 33 | t.foreign_key :active_storage_blobs, column: :blob_id 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /db/migrate/20190827030205_create_relationships.rb: -------------------------------------------------------------------------------- 1 | class CreateRelationships < ActiveRecord::Migration[6.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 | -------------------------------------------------------------------------------- /db/schema.rb: -------------------------------------------------------------------------------- 1 | # This file is auto-generated from the current state of the database. Instead 2 | # of editing this file, please use the migrations feature of Active Record to 3 | # incrementally modify your database, and then regenerate this schema definition. 4 | # 5 | # This file is the source Rails uses to define your schema when running `bin/rails 6 | # db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to 7 | # be faster and is potentially less error prone than running all of your 8 | # migrations from scratch. Old migrations may fail to apply correctly if those 9 | # migrations use external dependencies or application code. 10 | # 11 | # It's strongly recommended that you check this file into your version control system. 12 | 13 | ActiveRecord::Schema.define(version: 2019_08_27_030205) do 14 | 15 | create_table "active_storage_attachments", force: :cascade do |t| 16 | t.string "name", null: false 17 | t.string "record_type", null: false 18 | t.integer "record_id", null: false 19 | t.integer "blob_id", null: false 20 | t.datetime "created_at", null: false 21 | t.index ["blob_id"], name: "index_active_storage_attachments_on_blob_id" 22 | t.index ["record_type", "record_id", "name", "blob_id"], name: "index_active_storage_attachments_uniqueness", unique: true 23 | end 24 | 25 | create_table "active_storage_blobs", force: :cascade do |t| 26 | t.string "key", null: false 27 | t.string "filename", null: false 28 | t.string "content_type" 29 | t.text "metadata" 30 | t.string "service_name", null: false 31 | t.bigint "byte_size", null: false 32 | t.string "checksum", null: false 33 | t.datetime "created_at", null: false 34 | t.index ["key"], name: "index_active_storage_blobs_on_key", unique: true 35 | end 36 | 37 | create_table "active_storage_variant_records", force: :cascade do |t| 38 | t.integer "blob_id", null: false 39 | t.string "variation_digest", null: false 40 | t.index ["blob_id", "variation_digest"], name: "index_active_storage_variant_records_uniqueness", unique: true 41 | end 42 | 43 | create_table "microposts", force: :cascade do |t| 44 | t.text "content" 45 | t.bigint "user_id", null: false 46 | t.datetime "created_at", precision: 6, null: false 47 | t.datetime "updated_at", precision: 6, null: false 48 | t.index ["user_id", "created_at"], name: "index_microposts_on_user_id_and_created_at" 49 | t.index ["user_id"], name: "index_microposts_on_user_id" 50 | end 51 | 52 | create_table "relationships", force: :cascade do |t| 53 | t.integer "follower_id" 54 | t.integer "followed_id" 55 | t.datetime "created_at", precision: 6, null: false 56 | t.datetime "updated_at", precision: 6, null: false 57 | t.index ["followed_id"], name: "index_relationships_on_followed_id" 58 | t.index ["follower_id", "followed_id"], name: "index_relationships_on_follower_id_and_followed_id", unique: true 59 | t.index ["follower_id"], name: "index_relationships_on_follower_id" 60 | end 61 | 62 | create_table "users", force: :cascade do |t| 63 | t.string "name" 64 | t.string "email" 65 | t.datetime "created_at", precision: 6, null: false 66 | t.datetime "updated_at", precision: 6, null: false 67 | t.string "password_digest" 68 | t.string "remember_digest" 69 | t.boolean "admin", default: false 70 | t.string "activation_digest" 71 | t.boolean "activated", default: false 72 | t.datetime "activated_at" 73 | t.string "reset_digest" 74 | t.datetime "reset_sent_at" 75 | t.index ["email"], name: "index_users_on_email", unique: true 76 | end 77 | 78 | add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id" 79 | add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id" 80 | add_foreign_key "microposts", "users" 81 | end 82 | -------------------------------------------------------------------------------- /db/seeds.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 | activated: true, 8 | activated_at: Time.zone.now) 9 | 10 | # Generate a bunch of additional users. 11 | 99.times do |n| 12 | name = Faker::Name.name 13 | email = "example-#{n+1}@railstutorial.org" 14 | password = "password" 15 | User.create!(name: name, 16 | email: email, 17 | password: password, 18 | password_confirmation: password, 19 | activated: true, 20 | activated_at: Time.zone.now) 21 | end 22 | 23 | # Generate microposts for a subset of users. 24 | users = User.order(:created_at).take(6) 25 | 50.times do 26 | content = Faker::Lorem.sentence(word_count: 5) 27 | users.each { |user| user.microposts.create!(content: content) } 28 | end 29 | 30 | # Create following relationships. 31 | users = User.all 32 | user = users.first 33 | following = users[2..50] 34 | followers = users[3..40] 35 | following.each { |followed| user.follow(followed) } 36 | followers.each { |follower| follower.follow(user) } 37 | -------------------------------------------------------------------------------- /lib/assets/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/learnenough/sample_app_6th_ed/4e8ff53aa89e55258b79053a7711f7374ef0c4f9/lib/assets/.keep -------------------------------------------------------------------------------- /lib/tasks/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/learnenough/sample_app_6th_ed/4e8ff53aa89e55258b79053a7711f7374ef0c4f9/lib/tasks/.keep -------------------------------------------------------------------------------- /log/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/learnenough/sample_app_6th_ed/4e8ff53aa89e55258b79053a7711f7374ef0c4f9/log/.keep -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sample_app", 3 | "private": true, 4 | "dependencies": { 5 | "@rails/actioncable": "^6.0.0", 6 | "@rails/activestorage": "^6.0.0", 7 | "@rails/ujs": "^6.0.0", 8 | "@rails/webpacker": "5.4.2", 9 | "turbolinks": "^5.2.0", 10 | "webpack": "^4.46.0", 11 | "webpack-cli": "^3.3.12" 12 | }, 13 | "version": "0.1.0", 14 | "devDependencies": { 15 | "webpack-dev-server": "^4.0.0" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: [ 3 | require('postcss-import'), 4 | require('postcss-flexbugs-fixes'), 5 | require('postcss-preset-env')({ 6 | autoprefixer: { 7 | flexbox: 'no-2009' 8 | }, 9 | stage: 3 10 | }) 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /public/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The page you were looking for doesn't exist (404) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
    60 |
    61 |

    The page you were looking for doesn't exist.

    62 |

    You may have mistyped the address or the page may have moved.

    63 |
    64 |

    If you are the application owner check the logs for more information.

    65 |
    66 | 67 | 68 | -------------------------------------------------------------------------------- /public/422.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The change you wanted was rejected (422) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
    60 |
    61 |

    The change you wanted was rejected.

    62 |

    Maybe you tried to change something you didn't have access to.

    63 |
    64 |

    If you are the application owner check the logs for more information.

    65 |
    66 | 67 | 68 | -------------------------------------------------------------------------------- /public/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | We're sorry, but something went wrong (500) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
    60 |
    61 |

    We're sorry, but something went wrong.

    62 |
    63 |

    If you are the application owner check the logs for more information.

    64 |
    65 | 66 | 67 | -------------------------------------------------------------------------------- /public/apple-touch-icon-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/learnenough/sample_app_6th_ed/4e8ff53aa89e55258b79053a7711f7374ef0c4f9/public/apple-touch-icon-precomposed.png -------------------------------------------------------------------------------- /public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/learnenough/sample_app_6th_ed/4e8ff53aa89e55258b79053a7711f7374ef0c4f9/public/apple-touch-icon.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/learnenough/sample_app_6th_ed/4e8ff53aa89e55258b79053a7711f7374ef0c4f9/public/favicon.ico -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # See https://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file 2 | -------------------------------------------------------------------------------- /storage/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/learnenough/sample_app_6th_ed/4e8ff53aa89e55258b79053a7711f7374ef0c4f9/storage/.keep -------------------------------------------------------------------------------- /test/application_system_test_case.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class ApplicationSystemTestCase < ActionDispatch::SystemTestCase 4 | driven_by :selenium, using: :chrome, screen_size: [1400, 1400] 5 | end 6 | -------------------------------------------------------------------------------- /test/channels/application_cable/connection_test.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class ApplicationCable::ConnectionTest < ActionCable::Connection::TestCase 4 | # test "connects with cookies" do 5 | # cookies.signed[:user_id] = 42 6 | # 7 | # connect 8 | # 9 | # assert_equal connection.user_id, "42" 10 | # end 11 | end 12 | -------------------------------------------------------------------------------- /test/controllers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/learnenough/sample_app_6th_ed/4e8ff53aa89e55258b79053a7711f7374ef0c4f9/test/controllers/.keep -------------------------------------------------------------------------------- /test/controllers/account_activations_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class AccountActivationsControllerTest < ActionDispatch::IntegrationTest 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /test/controllers/microposts_controller_test.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_redirected_to login_url 21 | end 22 | 23 | test "should redirect destroy for wrong micropost" do 24 | log_in_as(users(:michael)) 25 | micropost = microposts(:ants) 26 | assert_no_difference 'Micropost.count' do 27 | delete micropost_path(micropost) 28 | end 29 | assert_redirected_to root_url 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /test/controllers/relationships_controller_test.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 | -------------------------------------------------------------------------------- /test/controllers/sessions_controller_test.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 | -------------------------------------------------------------------------------- /test/controllers/static_pages_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class StaticPagesControllerTest < ActionDispatch::IntegrationTest 4 | 5 | test "should get home" do 6 | get root_path 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 help_path 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 about_path 19 | assert_response :success 20 | assert_select "title", "About | Ruby on Rails Tutorial Sample App" 21 | end 22 | 23 | test "should get contact" do 24 | get contact_path 25 | assert_response :success 26 | assert_select "title", "Contact | Ruby on Rails Tutorial Sample App" 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /test/controllers/users_controller_test.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 | test "should redirect edit when not logged in" do 21 | get edit_user_path(@user) 22 | assert_not flash.empty? 23 | assert_redirected_to login_url 24 | end 25 | 26 | test "should redirect update when not logged in" do 27 | patch user_path(@user), params: { user: { name: @user.name, 28 | email: @user.email } } 29 | assert_not flash.empty? 30 | assert_redirected_to login_url 31 | end 32 | 33 | test "should redirect edit when logged in as wrong user" do 34 | log_in_as(@other_user) 35 | get edit_user_path(@user) 36 | assert flash.empty? 37 | assert_redirected_to root_url 38 | end 39 | 40 | test "should redirect update when logged in as wrong user" do 41 | log_in_as(@other_user) 42 | patch user_path(@user), params: { user: { name: @user.name, 43 | email: @user.email } } 44 | assert flash.empty? 45 | assert_redirected_to root_url 46 | end 47 | 48 | test "should redirect destroy when not logged in" do 49 | assert_no_difference 'User.count' do 50 | delete user_path(@user) 51 | end 52 | assert_redirected_to login_url 53 | end 54 | 55 | test "should redirect destroy when logged in as a non-admin" do 56 | log_in_as(@other_user) 57 | assert_no_difference 'User.count' do 58 | delete user_path(@user) 59 | end 60 | assert_redirected_to root_url 61 | end 62 | 63 | test "should redirect following when not logged in" do 64 | get following_user_path(@user) 65 | assert_redirected_to login_url 66 | end 67 | 68 | test "should redirect followers when not logged in" do 69 | get followers_user_path(@user) 70 | assert_redirected_to login_url 71 | end 72 | end 73 | -------------------------------------------------------------------------------- /test/fixtures/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/learnenough/sample_app_6th_ed/4e8ff53aa89e55258b79053a7711f7374ef0c4f9/test/fixtures/.keep -------------------------------------------------------------------------------- /test/fixtures/files/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/learnenough/sample_app_6th_ed/4e8ff53aa89e55258b79053a7711f7374ef0c4f9/test/fixtures/files/.keep -------------------------------------------------------------------------------- /test/fixtures/files/kitten.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/learnenough/sample_app_6th_ed/4e8ff53aa89e55258b79053a7711f7374ef0c4f9/test/fixtures/files/kitten.jpg -------------------------------------------------------------------------------- /test/fixtures/microposts.yml: -------------------------------------------------------------------------------- 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: http://tauday.com" 8 | created_at: <%= 3.years.ago %> 9 | user: michael 10 | 11 | cat_video: 12 | content: "Sad cats are sad: http://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 | 28 | ants: 29 | content: "Oh, is that what you want? Because that's how you get ants!" 30 | created_at: <%= 2.years.ago %> 31 | user: archer 32 | 33 | zone: 34 | content: "Danger zone!" 35 | created_at: <%= 3.days.ago %> 36 | user: archer 37 | 38 | tone: 39 | content: "I'm sorry. Your words made sense, but your sarcastic tone did not." 40 | created_at: <%= 10.minutes.ago %> 41 | user: lana 42 | 43 | van: 44 | content: "Dude, this van's, like, rolling probable cause." 45 | created_at: <%= 4.hours.ago %> 46 | user: lana 47 | -------------------------------------------------------------------------------- /test/fixtures/relationships.yml: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /test/fixtures/users.yml: -------------------------------------------------------------------------------- 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 | archer: 10 | name: Sterling Archer 11 | email: duchess@example.gov 12 | password_digest: <%= User.digest('password') %> 13 | activated: true 14 | activated_at: <%= Time.zone.now %> 15 | 16 | lana: 17 | name: Lana Kane 18 | email: hands@example.gov 19 | password_digest: <%= User.digest('password') %> 20 | activated: true 21 | activated_at: <%= Time.zone.now %> 22 | 23 | malory: 24 | name: Malory Archer 25 | email: boss@example.gov 26 | password_digest: <%= User.digest('password') %> 27 | activated: true 28 | activated_at: <%= Time.zone.now %> 29 | 30 | <% 30.times do |n| %> 31 | user_<%= n %>: 32 | name: <%= "User #{n}" %> 33 | email: <%= "user-#{n}@example.com" %> 34 | password_digest: <%= User.digest('password') %> 35 | activated: true 36 | activated_at: <%= Time.zone.now %> 37 | <% end %> 38 | -------------------------------------------------------------------------------- /test/helpers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/learnenough/sample_app_6th_ed/4e8ff53aa89e55258b79053a7711f7374ef0c4f9/test/helpers/.keep -------------------------------------------------------------------------------- /test/helpers/sessions_helper_test.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 | -------------------------------------------------------------------------------- /test/integration/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/learnenough/sample_app_6th_ed/4e8ff53aa89e55258b79053a7711f7374ef0c4f9/test/integration/.keep -------------------------------------------------------------------------------- /test/integration/following_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class FollowingTest < ActionDispatch::IntegrationTest 4 | 5 | def setup 6 | @user = users(:michael) 7 | @other = users(:archer) 8 | log_in_as(@user) 9 | end 10 | 11 | test "following page" do 12 | get following_user_path(@user) 13 | assert_not @user.following.empty? 14 | assert_match @user.following.count.to_s, response.body 15 | @user.following.each do |user| 16 | assert_select "a[href=?]", user_path(user) 17 | end 18 | end 19 | 20 | test "followers page" do 21 | get followers_user_path(@user) 22 | assert_not @user.followers.empty? 23 | assert_match @user.followers.count.to_s, response.body 24 | @user.followers.each do |user| 25 | assert_select "a[href=?]", user_path(user) 26 | end 27 | end 28 | 29 | test "should follow a user the standard way" do 30 | assert_difference '@user.following.count', 1 do 31 | post relationships_path, params: { followed_id: @other.id } 32 | end 33 | end 34 | 35 | test "should follow a user with Ajax" do 36 | assert_difference '@user.following.count', 1 do 37 | post relationships_path, xhr: true, params: { followed_id: @other.id } 38 | end 39 | end 40 | 41 | test "should unfollow a user the standard way" do 42 | @user.follow(@other) 43 | relationship = @user.active_relationships.find_by(followed_id: @other.id) 44 | assert_difference '@user.following.count', -1 do 45 | delete relationship_path(relationship) 46 | end 47 | end 48 | 49 | test "should unfollow a user with Ajax" do 50 | @user.follow(@other) 51 | relationship = @user.active_relationships.find_by(followed_id: @other.id) 52 | assert_difference '@user.following.count', -1 do 53 | delete relationship_path(relationship), xhr: true 54 | end 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /test/integration/microposts_interface_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class MicropostsInterfaceTest < ActionDispatch::IntegrationTest 4 | 5 | def setup 6 | @user = users(:michael) 7 | end 8 | 9 | test "micropost interface" do 10 | log_in_as(@user) 11 | get root_path 12 | assert_select 'div.pagination' 13 | assert_select 'input[type=file]' 14 | # Invalid submission 15 | post microposts_path, params: { micropost: { content: "" } } 16 | assert_select 'div#error_explanation' 17 | assert_select 'a[href=?]', '/?page=2' # Correct pagination link 18 | # Valid submission 19 | content = "This micropost really ties the room together" 20 | image = fixture_file_upload('kitten.jpg', 'image/jpeg') 21 | assert_difference 'Micropost.count', 1 do 22 | post microposts_path, params: { micropost: { content: content, 23 | image: image } } 24 | end 25 | assert assigns(:micropost).image.attached? 26 | follow_redirect! 27 | assert_match content, response.body 28 | # Delete a post. 29 | assert_select 'a', 'delete' 30 | first_micropost = @user.microposts.paginate(page: 1).first 31 | assert_difference 'Micropost.count', -1 do 32 | delete micropost_path(first_micropost) 33 | end 34 | # Visit a different user (no delete links). 35 | get user_path(users(:archer)) 36 | assert_select 'a', { text: 'delete', count: 0 } 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /test/integration/password_resets_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class PasswordResetsTest < ActionDispatch::IntegrationTest 4 | 5 | def setup 6 | ActionMailer::Base.deliveries.clear 7 | @user = users(:michael) 8 | end 9 | 10 | test "password resets" do 11 | get new_password_reset_path 12 | assert_template 'password_resets/new' 13 | assert_select 'input[name=?]', 'password_reset[email]' 14 | # Invalid email 15 | post password_resets_path, params: { password_reset: { email: "" } } 16 | assert_not flash.empty? 17 | assert_template 'password_resets/new' 18 | # Valid email 19 | post password_resets_path, 20 | params: { password_reset: { email: @user.email } } 21 | assert_not_equal @user.reset_digest, @user.reload.reset_digest 22 | assert_equal 1, ActionMailer::Base.deliveries.size 23 | assert_not flash.empty? 24 | assert_redirected_to root_url 25 | # Password reset form 26 | user = assigns(:user) 27 | # Wrong email 28 | get edit_password_reset_path(user.reset_token, email: "") 29 | assert_redirected_to root_url 30 | # Inactive user 31 | user.toggle!(:activated) 32 | get edit_password_reset_path(user.reset_token, email: user.email) 33 | assert_redirected_to root_url 34 | user.toggle!(:activated) 35 | # Right email, wrong token 36 | get edit_password_reset_path('wrong token', email: user.email) 37 | assert_redirected_to root_url 38 | # Right email, right token 39 | get edit_password_reset_path(user.reset_token, email: user.email) 40 | assert_template 'password_resets/edit' 41 | assert_select "input[name=email][type=hidden][value=?]", user.email 42 | # Invalid password & confirmation 43 | patch password_reset_path(user.reset_token), 44 | params: { email: user.email, 45 | user: { password: "foobaz", 46 | password_confirmation: "barquux" } } 47 | assert_select 'div#error_explanation' 48 | # Empty password 49 | patch password_reset_path(user.reset_token), 50 | params: { email: user.email, 51 | user: { password: "", 52 | password_confirmation: "" } } 53 | assert_select 'div#error_explanation' 54 | # Valid password & confirmation 55 | patch password_reset_path(user.reset_token), 56 | params: { email: user.email, 57 | user: { password: "foobaz", 58 | password_confirmation: "foobaz" } } 59 | assert is_logged_in? 60 | assert_not flash.empty? 61 | assert_redirected_to user 62 | end 63 | end 64 | -------------------------------------------------------------------------------- /test/integration/site_layout_test.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 | -------------------------------------------------------------------------------- /test/integration/users_edit_test.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 | assert_template 'users/edit' 13 | patch user_path(@user), params: { user: { name: "", 14 | email: "foo@invalid", 15 | password: "foo", 16 | password_confirmation: "bar" } } 17 | 18 | assert_template 'users/edit' 19 | end 20 | 21 | test "successful edit with friendly forwarding" do 22 | get edit_user_path(@user) 23 | log_in_as(@user) 24 | assert_redirected_to edit_user_url(@user) 25 | name = "Foo Bar" 26 | email = "foo@bar.com" 27 | patch user_path(@user), params: { user: { name: name, 28 | email: email, 29 | password: "", 30 | password_confirmation: "" } } 31 | assert_not flash.empty? 32 | assert_redirected_to @user 33 | @user.reload 34 | assert_equal name, @user.name 35 | assert_equal email, @user.email 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /test/integration/users_index_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class UsersIndexTest < ActionDispatch::IntegrationTest 4 | 5 | def setup 6 | @admin = users(:michael) 7 | @non_admin = users(:archer) 8 | end 9 | 10 | test "index as admin including pagination and delete links" do 11 | log_in_as(@admin) 12 | get users_path 13 | assert_template 'users/index' 14 | assert_select 'div.pagination' 15 | first_page_of_users = User.paginate(page: 1) 16 | first_page_of_users.each do |user| 17 | assert_select 'a[href=?]', user_path(user), text: user.name 18 | unless user == @admin 19 | assert_select 'a[href=?]', user_path(user), text: 'delete' 20 | end 21 | end 22 | assert_difference 'User.count', -1 do 23 | delete user_path(@non_admin) 24 | end 25 | end 26 | 27 | test "index as non-admin" do 28 | log_in_as(@non_admin) 29 | get users_path 30 | assert_select 'a', text: 'delete', count: 0 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /test/integration/users_login_test.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: @user.email, 13 | password: "invalid" } } 14 | assert_not is_logged_in? 15 | assert_template 'sessions/new' 16 | assert_not flash.empty? 17 | get root_path 18 | assert flash.empty? 19 | end 20 | 21 | test "login with valid information followed by logout" do 22 | get login_path 23 | post login_path, params: { session: { email: @user.email, 24 | password: 'password' } } 25 | assert is_logged_in? 26 | assert_redirected_to @user 27 | follow_redirect! 28 | assert_template 'users/show' 29 | assert_select "a[href=?]", login_path, count: 0 30 | assert_select "a[href=?]", logout_path 31 | assert_select "a[href=?]", user_path(@user) 32 | delete logout_path 33 | assert_not is_logged_in? 34 | assert_redirected_to root_url 35 | # Simulate a user clicking logout in a second window. 36 | delete logout_path 37 | follow_redirect! 38 | assert_select "a[href=?]", login_path 39 | assert_select "a[href=?]", logout_path, count: 0 40 | assert_select "a[href=?]", user_path(@user), count: 0 41 | end 42 | 43 | test "login with remembering" do 44 | log_in_as(@user, remember_me: '1') 45 | assert_not cookies[:remember_token].blank? 46 | end 47 | 48 | test "login without remembering" do 49 | # Log in to set the cookie. 50 | log_in_as(@user, remember_me: '1') 51 | # Log in again and verify that the cookie is deleted. 52 | log_in_as(@user, remember_me: '0') 53 | assert cookies[:remember_token].blank? 54 | end 55 | end 56 | -------------------------------------------------------------------------------- /test/integration/users_profile_test.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 | -------------------------------------------------------------------------------- /test/integration/users_signup_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class UsersSignupTest < ActionDispatch::IntegrationTest 4 | 5 | def setup 6 | ActionMailer::Base.deliveries.clear 7 | end 8 | 9 | test "invalid signup information" do 10 | get signup_path 11 | assert_no_difference 'User.count' do 12 | post users_path, params: { user: { name: "", 13 | email: "user@invalid", 14 | password: "foo", 15 | password_confirmation: "bar" } } 16 | end 17 | assert_template 'users/new' 18 | assert_select 'div#error_explanation' 19 | assert_select 'div.field_with_errors' 20 | end 21 | 22 | test "valid signup information with account activation" do 23 | get signup_path 24 | assert_difference 'User.count', 1 do 25 | post users_path, params: { user: { name: "Example User", 26 | email: "user@example.com", 27 | password: "password", 28 | password_confirmation: "password" } } 29 | end 30 | assert_equal 1, ActionMailer::Base.deliveries.size 31 | user = assigns(:user) 32 | assert_not user.activated? 33 | # Try to log in before activation. 34 | log_in_as(user) 35 | assert_not is_logged_in? 36 | # Invalid activation token 37 | get edit_account_activation_path("invalid token", email: user.email) 38 | assert_not is_logged_in? 39 | # Valid token, wrong email 40 | get edit_account_activation_path(user.activation_token, email: 'wrong') 41 | assert_not is_logged_in? 42 | # Valid activation token 43 | get edit_account_activation_path(user.activation_token, email: user.email) 44 | assert user.reload.activated? 45 | follow_redirect! 46 | assert_template 'users/show' 47 | assert is_logged_in? 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /test/mailers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/learnenough/sample_app_6th_ed/4e8ff53aa89e55258b79053a7711f7374ef0c4f9/test/mailers/.keep -------------------------------------------------------------------------------- /test/mailers/previews/user_mailer_preview.rb: -------------------------------------------------------------------------------- 1 | # Preview all emails at http://localhost:3000/rails/mailers/user_mailer 2 | class UserMailerPreview < ActionMailer::Preview 3 | 4 | # Preview this email at http://localhost:3000/rails/mailers/user_mailer/account_activation 5 | def account_activation 6 | user = User.first 7 | user.activation_token = User.new_token 8 | UserMailer.account_activation(user) 9 | end 10 | 11 | # Preview this email at http://localhost:3000/rails/mailers/user_mailer/password_reset 12 | def password_reset 13 | user = User.first 14 | user.reset_token = User.new_token 15 | UserMailer.password_reset(user) 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /test/mailers/user_mailer_test.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 ["noreply@example.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 | 17 | test "password_reset" do 18 | user = users(:michael) 19 | user.reset_token = User.new_token 20 | mail = UserMailer.password_reset(user) 21 | assert_equal "Password reset", mail.subject 22 | assert_equal [user.email], mail.to 23 | assert_equal ["noreply@example.com"], mail.from 24 | assert_match user.reset_token, mail.body.encoded 25 | assert_match CGI.escape(user.email), mail.body.encoded 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /test/models/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/learnenough/sample_app_6th_ed/4e8ff53aa89e55258b79053a7711f7374ef0c4f9/test/models/.keep -------------------------------------------------------------------------------- /test/models/micropost_test.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 | test "content should be present" do 20 | @micropost.content = " " 21 | assert_not @micropost.valid? 22 | end 23 | 24 | test "content should be at most 140 characters" do 25 | @micropost.content = "a" * 141 26 | assert_not @micropost.valid? 27 | end 28 | 29 | test "order should be most recent first" do 30 | assert_equal microposts(:most_recent), Micropost.first 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /test/models/relationship_test.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 | -------------------------------------------------------------------------------- /test/models/user_test.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 | test "should be valid" do 11 | assert @user.valid? 12 | end 13 | 14 | test "name should be present" do 15 | @user.name = "" 16 | assert_not @user.valid? 17 | end 18 | 19 | test "email should be present" do 20 | @user.email = " " 21 | assert_not @user.valid? 22 | end 23 | 24 | test "name should not be too long" do 25 | @user.name = "a" * 51 26 | assert_not @user.valid? 27 | end 28 | 29 | test "email should not be too long" do 30 | @user.email = "a" * 244 + "@example.com" 31 | assert_not @user.valid? 32 | end 33 | 34 | test "email validation should accept valid addresses" do 35 | valid_addresses = %w[user@example.com USER@foo.COM A_US-ER@foo.bar.org 36 | first.last@foo.jp alice+bob@baz.cn] 37 | valid_addresses.each do |valid_address| 38 | @user.email = valid_address 39 | assert @user.valid?, "#{valid_address.inspect} should be valid" 40 | end 41 | end 42 | 43 | test "email validation should reject invalid addresses" do 44 | invalid_addresses = %w[user@example,com user_at_foo.org user.name@example. 45 | foo@bar_baz.com foo@bar+baz.com] 46 | invalid_addresses.each do |invalid_address| 47 | @user.email = invalid_address 48 | assert_not @user.valid?, "#{invalid_address.inspect} should be invalid" 49 | end 50 | end 51 | 52 | test "email addresses should be unique" do 53 | duplicate_user = @user.dup 54 | @user.save 55 | assert_not duplicate_user.valid? 56 | end 57 | 58 | test "password should be present (nonblank)" do 59 | @user.password = @user.password_confirmation = " " * 6 60 | assert_not @user.valid? 61 | end 62 | 63 | test "password should have a minimum length" do 64 | @user.password = @user.password_confirmation = "a" * 5 65 | assert_not @user.valid? 66 | end 67 | 68 | test "authenticated? should return false for a user with nil digest" do 69 | assert_not @user.authenticated?(:remember, '') 70 | end 71 | 72 | test "associated microposts should be destroyed" do 73 | @user.save 74 | @user.microposts.create!(content: "Lorem ipsum") 75 | assert_difference 'Micropost.count', -1 do 76 | @user.destroy 77 | end 78 | end 79 | 80 | test "should follow and unfollow a user" do 81 | michael = users(:michael) 82 | archer = users(:archer) 83 | assert_not michael.following?(archer) 84 | michael.follow(archer) 85 | assert michael.following?(archer) 86 | assert archer.followers.include?(michael) 87 | michael.unfollow(archer) 88 | assert_not michael.following?(archer) 89 | # Users can't follow themselves. 90 | michael.follow(michael) 91 | assert_not michael.following?(michael) 92 | end 93 | 94 | test "feed should have the right posts" do 95 | michael = users(:michael) 96 | archer = users(:archer) 97 | lana = users(:lana) 98 | # Posts from followed user 99 | lana.microposts.each do |post_following| 100 | assert michael.feed.include?(post_following) 101 | end 102 | # Self-posts for user with followers 103 | michael.microposts.each do |post_self| 104 | assert michael.feed.include?(post_self) 105 | end 106 | # Self-posts for user with no followers 107 | archer.microposts.each do |post_self| 108 | assert archer.feed.include?(post_self) 109 | end 110 | # Posts from unfollowed user 111 | archer.microposts.each do |post_unfollowed| 112 | assert_not michael.feed.include?(post_unfollowed) 113 | end 114 | end 115 | end 116 | -------------------------------------------------------------------------------- /test/system/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/learnenough/sample_app_6th_ed/4e8ff53aa89e55258b79053a7711f7374ef0c4f9/test/system/.keep -------------------------------------------------------------------------------- /test/test_helper.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 | fixtures :all 9 | 10 | # Returns true if a test user is logged in. 11 | def is_logged_in? 12 | !session[:user_id].nil? 13 | end 14 | 15 | # Log in as a particular user. 16 | def log_in_as(user) 17 | session[:user_id] = user.id 18 | end 19 | end 20 | 21 | class ActionDispatch::IntegrationTest 22 | 23 | # Log in as a particular user. 24 | def log_in_as(user, password: 'password', remember_me: '1') 25 | post login_path, params: { session: { email: user.email, 26 | password: password, 27 | remember_me: remember_me } } 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /tmp/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/learnenough/sample_app_6th_ed/4e8ff53aa89e55258b79053a7711f7374ef0c4f9/tmp/.keep -------------------------------------------------------------------------------- /vendor/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/learnenough/sample_app_6th_ed/4e8ff53aa89e55258b79053a7711f7374ef0c4f9/vendor/.keep --------------------------------------------------------------------------------